diff --git a/.clang-tidy b/.clang-tidy index 9423f438d68..f7f00391f25 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -42,6 +42,7 @@ Checks: - google-default-arguments # - google-explicit-constructor # triggered for O2 columns - google-global-names-in-headers + - google-readability-casting # same as cpplint:readability/casting # - misc-const-correctness # to be checked - misc-header-include-cycle - misc-include-cleaner @@ -111,8 +112,6 @@ WarningsAsErrors: >- *, -readability-braces-around-statements, -readability-suspicious-call-argument, - -modernize-*, - -readability-*, -bugprone-assignment-in-if-condition, -bugprone-branch-clone, -bugprone-copy-constructor-init, @@ -140,17 +139,20 @@ WarningsAsErrors: >- -cppcoreguidelines-missing-std-forward, -cppcoreguidelines-prefer-member-initializer, -cppcoreguidelines-pro-bounds-array-to-pointer-decay, - -cppcoreguidelines-pro-type-const-cast, - -cppcoreguidelines-pro-type-cstyle-cast, -cppcoreguidelines-pro-type-member-init, - -cppcoreguidelines-pro-type-reinterpret-cast, - -cppcoreguidelines-pro-type-static-cast-downcast, -cppcoreguidelines-pro-type-union-access, -cppcoreguidelines-slicing, -cppcoreguidelines-virtual-class-destructor, + -modernize-*, + -readability-*, + readability-redundant-casting, # Positives for the following checks are currently being fixed and should be reported as errors. # clang-analyzer-* # clang-diagnostic-* +# cppcoreguidelines-pro-type-const-cast +# cppcoreguidelines-pro-type-cstyle-cast +# cppcoreguidelines-pro-type-reinterpret-cast +# cppcoreguidelines-pro-type-static-cast-downcast # google-default-arguments # misc-include-cleaner # misc-redundant-expression diff --git a/.github/workflows/clean-test.yml b/.github/workflows/clean-test.yml index be471ce2287..fcaa7084f8d 100644 --- a/.github/workflows/clean-test.yml +++ b/.github/workflows/clean-test.yml @@ -24,11 +24,6 @@ name: Clean PR checks type: boolean default: true - 'check_build/O2Physics/o2/macOS': - description: build/O2Physics/o2/macOS - type: boolean - default: true - 'check_build/O2Physics/o2': description: build/O2Physics/o2 type: boolean diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f837e1ad5a0..cf0e0a1b341 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,15 +2,47 @@ # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v6.0.0 + rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0 hooks: - - id: trailing-whitespace + - id: check-added-large-files + - id: check-ast + - id: check-case-conflict + - id: check-executables-have-shebangs + - id: check-merge-conflict + - id: check-symlinks + - id: check-yaml - id: end-of-file-fixer + - id: trailing-whitespace + - repo: https://github.com/shellcheck-py/shellcheck-py + rev: a6564f5d57444e123433a5c9af733e3975c77229 # frozen: v0.11.0.1-1 + hooks: + - id: shellcheck + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: f12be1ebaa5351c1fc76472de98db2c3446c8253 # frozen: v0.16.10 + hooks: + - id: ruff-check # linter + args: ["--fix"] + - id: ruff-format # formatter + - repo: local + hooks: + - id: format-includes + name: format includes + language: python + entry: Scripts/format_includes.py + types: [c++] - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v20.1.3 # clang-format version (keep synced with https://github.com/alisw/ali-bot/blob/master/.github/workflows/c++-code-formatting.yml) + # clang-format version (keep synced with https://github.com/alisw/ali-bot/blob/master/.github/workflows/c++-code-formatting.yml) + rev: 7ec07eeb4135dc6ecf410d24ac4ead986a5d413d # frozen: v20.1.3 hooks: - id: clang-format - repo: https://github.com/cpplint/cpplint - rev: 2.0.2 + rev: 350702a4d39395571bd118c9a3cff0a573488d61 # frozen: 2.0.2 hooks: - id: cpplint + - repo: local + hooks: + - id: O2 linter + name: O2 linter + language: python + entry: Scripts/o2_linter.py + types_or: [c++, cmake, python] diff --git a/ALICE3/Core/ALICE3CoreLinkDef.h b/ALICE3/Core/ALICE3CoreLinkDef.h index bdd3c955d51..10706fa14cc 100644 --- a/ALICE3/Core/ALICE3CoreLinkDef.h +++ b/ALICE3/Core/ALICE3CoreLinkDef.h @@ -9,6 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +// NOLINT(build/header_guard) + #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; diff --git a/ALICE3/Core/CMakeLists.txt b/ALICE3/Core/CMakeLists.txt index 55f916861a6..de89ec6f1a4 100644 --- a/ALICE3/Core/CMakeLists.txt +++ b/ALICE3/Core/CMakeLists.txt @@ -11,11 +11,16 @@ o2physics_add_library(ALICE3Core SOURCES TrackUtilities.cxx + ConfigurationParser.cxx FlatLutEntry.cxx + FlatLutRootWriter.cxx FlatTrackSmearer.cxx GeometryContainer.cxx PUBLIC_LINK_LIBRARIES O2::Framework - O2Physics::AnalysisCore) + O2Physics::AnalysisCore + PRIVATE_LINK_LIBRARIES ROOT::Core + ROOT::RIO + ROOT::Tree) o2physics_target_root_dictionary(ALICE3Core HEADERS TrackUtilities.h diff --git a/ALICE3/Core/ConfigurationParser.cxx b/ALICE3/Core/ConfigurationParser.cxx new file mode 100644 index 00000000000..0682090d08f --- /dev/null +++ b/ALICE3/Core/ConfigurationParser.cxx @@ -0,0 +1,211 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file ConfigurationParser.cxx +/// \brief Utilities to access and parse TEnv configuration files for the ALICE3 fast simulation +/// \author Nicolò Jacazio, Università del Piemonte Orientale (IT) +/// + +#include "ConfigurationParser.h" + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace o2::fastsim +{ + +std::map> ConfigurationParser::parseTEnvConfiguration(std::string& filename, std::vector& entries) +{ + std::map> configMap; + filename = gSystem->ExpandPathName(filename.c_str()); + LOG(info) << "Parsing TEnv configuration file: " << filename; + TEnv env(filename.c_str()); + THashList* table = env.GetTable(); + entries.clear(); + for (int i = 0; i < table->GetEntries(); ++i) { + const std::string key = table->At(i)->GetName(); + // key should contain exactly one dot + if (key.find('.') == std::string::npos || key.find('.') != key.rfind('.')) { + LOG(fatal) << "Key " << key << " does not contain exactly one dot"; + continue; + } + const std::string firstPart = key.substr(0, key.find('.')); + if (std::find(entries.begin(), entries.end(), firstPart) == entries.end()) { + entries.push_back(firstPart); + } + } + env.Print(); + // Entries + for (const auto& entry : entries) { + LOG(info) << " Reading entry " << entry; + for (int i = 0; i < table->GetEntries(); ++i) { + const std::string key = table->At(i)->GetName(); + if (key.find(entry + ".") == 0) { + const std::string paramName = key.substr(key.find('.') + 1); + const std::string value = env.GetValue(key.c_str(), ""); + configMap[entry][paramName] = value; + } + } + } + return configMap; +} + +std::string ConfigurationParser::accessFile(const std::string& path, const std::string& downloadPath, o2::ccdb::BasicCCDBManager* ccdb, int timeoutSeconds) +{ + if (path.rfind("ccdb:", 0) == 0) { + const std::string ccdbPath = path.substr(5); // remove "ccdb:" prefix + const std::string localPath = Form("%s/%s/snapshot.root", downloadPath.c_str(), ccdbPath.c_str()); + const std::string lockFile = localPath + ".lock"; + const std::string doneFile = localPath + ".done"; + + // Create directory structure if it doesn't exist + std::string dirPath = localPath.substr(0, localPath.find_last_of('/')); + gSystem->mkdir(dirPath.c_str(), true); + + // Check if file is already fully downloaded + struct stat buffer; + if (stat(doneFile.c_str(), &buffer) == 0) { + LOG(info) << " --- Geometry configuration file already exists: " << localPath << ". Skipping download."; + return localPath; + } + + // Acquire file lock for inter-process synchronization + int lockFd = open(lockFile.c_str(), O_CREAT | O_RDWR, 0666); + if (lockFd == -1) { + LOG(error) << " --- Failed to create lock file: " << lockFile; + return localPath; + } + + // Try to acquire exclusive lock (non-blocking) + LOG(info) << " --- Attempting to acquire lock for: " << localPath; + int lockResult = flock(lockFd, LOCK_EX | LOCK_NB); + + if (lockResult == -1 && errno == EWOULDBLOCK) { + // Lock is held by another process - wait up to 10 minutes for download to complete + LOG(info) << " --- Lock is held by another process. Waiting for download to complete (up to 10 minutes)..."; + close(lockFd); + + const auto startTime = std::chrono::steady_clock::now(); + const auto timeout = std::chrono::minutes(10); + const auto checkInterval = std::chrono::seconds(5); + + while (true) { + // Check if download is complete + if (stat(doneFile.c_str(), &buffer) == 0) { + LOG(info) << " --- Geometry configuration file was downloaded by another process: " << localPath; + return localPath; + } + + // Check timeout + auto elapsed = std::chrono::steady_clock::now() - startTime; + if (elapsed >= timeout) { + LOG(fatal) << " --- Timeout waiting for file download: " << localPath << ". Waited for 10 minutes."; + return localPath; + } + + // Wait before checking again + std::this_thread::sleep_for(checkInterval); + } + } else if (lockResult == -1) { + LOG(error) << " --- Failed to acquire lock for: " << lockFile; + close(lockFd); + return localPath; + } + + // Lock acquired successfully - double-check if file was downloaded while we were trying + if (stat(doneFile.c_str(), &buffer) == 0) { + LOG(info) << " --- Geometry configuration file was downloaded by another process: " << localPath; + flock(lockFd, LOCK_UN); + close(lockFd); + return localPath; + } + + // File does not exist, retrieve from CCDB + LOG(info) << " --- CCDB source detected for " << path; + std::map metadata; + bool status = ccdb->getCCDBAccessor().retrieveBlob(ccdbPath, downloadPath, metadata, 1); + if (!status) { + flock(lockFd, LOCK_UN); + close(lockFd); + LOG(fatal) << " --- Failed to retrieve configuration from CCDB for path: " << ccdbPath; + return ""; + } + LOG(info) << " --- Retrieved configuration from CCDB to: " << localPath; + + // Verify the integrity of the downloaded file + if (stat(localPath.c_str(), &buffer) != 0) { + flock(lockFd, LOCK_UN); + close(lockFd); + LOG(fatal) << " --- Downloaded file does not exist or is not accessible: " << localPath; + return ""; + } + if (buffer.st_size == 0) { + flock(lockFd, LOCK_UN); + close(lockFd); + LOG(fatal) << " --- Downloaded file is empty: " << localPath; + return ""; + } + LOG(info) << " --- File integrity verified. Size: " << buffer.st_size << " bytes"; + + // Create done marker file to indicate successful download + std::ofstream doneMarker(doneFile); + doneMarker.close(); + + // Release lock + flock(lockFd, LOCK_UN); + close(lockFd); + + // If timeout is specified, schedule file deletion after timeout + if (timeoutSeconds > 0) { + LOG(info) << " --- Deleting configuration file after timeout: " << localPath; + std::thread deletionThread([localPath, doneFile, timeoutSeconds]() { + LOG(info) << " --- Operating deletion of configuration file after timeout: " << localPath; + std::this_thread::sleep_for(std::chrono::seconds(timeoutSeconds)); + if (std::remove(localPath.c_str()) == 0) { + LOG(info) << " --- File deleted successfully: " << localPath; + } else { + LOG(warning) << " --- Failed to delete file: " << localPath; + } + // Also remove the done marker file + if (std::remove(doneFile.c_str()) == 0) { + LOG(info) << " --- Done marker deleted: " << doneFile; + } + }); + deletionThread.detach(); + } + + return localPath; + } + return path; +} + +} // namespace o2::fastsim diff --git a/ALICE3/Core/ConfigurationParser.h b/ALICE3/Core/ConfigurationParser.h new file mode 100644 index 00000000000..b92a02e59ec --- /dev/null +++ b/ALICE3/Core/ConfigurationParser.h @@ -0,0 +1,53 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file ConfigurationParser.h +/// \brief Utilities to access and parse TEnv configuration files for the ALICE3 fast simulation +/// \author Nicolò Jacazio, Università del Piemonte Orientale (IT) +/// + +#ifndef ALICE3_CORE_CONFIGURATIONPARSER_H_ +#define ALICE3_CORE_CONFIGURATIONPARSER_H_ + +#include + +#include +#include +#include + +namespace o2::fastsim +{ + +class ConfigurationParser +{ + public: + /** + * @brief Parses a TEnv configuration file with keys of the form "." and returns the key-value pairs split per entry + * @param filename Path to the TEnv configuration file + * @param entries Vector to store the order of the entries as they appear in the file + * @return A map where each key is an entry name and the value is another map of key-value pairs for that entry + */ + static std::map> parseTEnvConfiguration(std::string& filename, std::vector& entries); + + /** + * @brief Accesses a file given its path, which can be either a local path or a ccdb path (starting with "ccdb:"). In the first case it returns the local path, in the second it retrieves the file from ccdb and returns the local path to the retrieved file. + * @param path The path to the file, either local or ccdb (starting with "ccdb:") + * @param downloadPath The local path where to download the file if it's a ccdb path. Default is "/tmp/GeometryContainer/" + * @param ccdb Pointer to the CCDB manager to use for retrieving the file if it's a ccdb path. Must be set when path is a ccdb path. + * @param timeoutSeconds If positive, then this function will wait for these seconds after download before removing the downloaded file. + * @return The local path to the file, either the original local path or the path to the retrieved file from ccdb + */ + static std::string accessFile(const std::string& path, const std::string& downloadPath = "/tmp/GeometryContainer/", o2::ccdb::BasicCCDBManager* ccdb = nullptr, int timeoutSeconds = 0); +}; + +} // namespace o2::fastsim + +#endif // ALICE3_CORE_CONFIGURATIONPARSER_H_ diff --git a/ALICE3/Core/Decayer.h b/ALICE3/Core/Decayer.h index 8f88e4e311b..fb39a372cb2 100644 --- a/ALICE3/Core/Decayer.h +++ b/ALICE3/Core/Decayer.h @@ -19,10 +19,13 @@ #ifndef ALICE3_CORE_DECAYER_H_ #define ALICE3_CORE_DECAYER_H_ +#include "ALICE3/Core/ConfigurationParser.h" #include "ALICE3/Core/OTFParticle.h" #include "ALICE3/Core/TrackUtilities.h" +#include #include +#include #include #include @@ -34,6 +37,10 @@ #include #include #include +#include +#include +#include +#include #include namespace o2::upgrade @@ -45,6 +52,53 @@ class Decayer // Default constructor Decayer() = default; + static constexpr double BranchingRatioTolerance = 0.01; + + struct DecayChannel { + double branchingRatio{}; + std::vector daughters; + }; + + template + void loadDecayTable(const std::string& path, const TDatabase& pdgDB, o2::ccdb::BasicCCDBManager* ccdb = nullptr) + { + std::string fileName = o2::fastsim::ConfigurationParser::accessFile(path, "./.ALICE3/Configuration/", ccdb); + std::vector mothers; + const std::map> config = o2::fastsim::ConfigurationParser::parseTEnvConfiguration(fileName, mothers); + + mDecayTable.clear(); + for (const auto& [motherName, channels] : config) { + const int motherPdg = std::stoi(motherName); + if (!pdgDB->GetParticle(motherPdg)) { + LOG(fatal) << "Decay table: unknown mother PDG code " << motherPdg; + } + for (const auto& [channelName, value] : channels) { + DecayChannel channel; + std::istringstream stream(value); + stream >> channel.branchingRatio; + int daughterPdg{}; + while (stream >> daughterPdg) { + if (!pdgDB->GetParticle(daughterPdg)) { + LOG(fatal) << "Decay table: unknown daughter PDG code " << daughterPdg << " in " << motherName << "." << channelName; + } + channel.daughters.push_back(daughterPdg); + } + if (!stream.eof() || channel.branchingRatio <= 0. || channel.daughters.size() < 2) { + LOG(fatal) << "Decay table: invalid channel " << motherName << "." << channelName << ": \"" << value << "\""; + } + mDecayTable[motherPdg].push_back(channel); + } + double brTotal = 0.; + for (const auto& channel : mDecayTable[motherPdg]) { + brTotal += channel.branchingRatio; + } + if (std::abs(brTotal - 1.) > BranchingRatioTolerance) { + LOG(fatal) << "Decay table: branching ratios of PDG " << motherPdg << " sum to " << brTotal << ", expected 1 within " << BranchingRatioTolerance; + } + LOG(info) << "Decay table: overriding " << mDecayTable[motherPdg].size() << " decay channel(s) of PDG " << motherPdg; + } + } + template std::vector decayParticle(const OTFParticle& particle, const TDatabase& pdgDB) { @@ -69,21 +123,22 @@ class Decayer py = particle.py() * std::cos(mTheta) + particle.px() * std::sin(mTheta); } - double brTotal = 0.; e = std::sqrt(mass * mass + px * px + py * py + particle.pz() * particle.pz()); - for (int ch = 0; ch < particleInfo->NDecayChannels(); ++ch) { - brTotal += particleInfo->DecayChannel(ch)->BranchingRatio(); + const std::vector channels = getDecayChannels(particle.pdgCode(), particleInfo); + + double brTotal = 0.; + for (const auto& channel : channels) { + brTotal += channel.branchingRatio; } double brSum = 0.; std::vector dauMasses; std::vector pdgCodesDaughters; const double randomChannel = mRand3.Uniform(0., brTotal); - for (int ch = 0; ch < particleInfo->NDecayChannels(); ++ch) { - brSum += particleInfo->DecayChannel(ch)->BranchingRatio(); + for (const auto& channel : channels) { + brSum += channel.branchingRatio; if (randomChannel < brSum) { - for (int dau = 0; dau < particleInfo->DecayChannel(ch)->NDaughters(); ++dau) { - const int pdgDau = particleInfo->DecayChannel(ch)->DaughterPdgCode(dau); + for (const int pdgDau : channel.daughters) { pdgCodesDaughters.push_back(pdgDau); const auto& dauInfo = pdgDB->GetParticle(pdgDau); dauMasses.push_back(dauInfo->Mass()); @@ -102,16 +157,16 @@ class Decayer decay.Generate(); std::vector decayProducts; + decayProducts.reserve(dauMasses.size()); for (size_t i = 0; i < dauMasses.size(); ++i) { - o2::upgrade::OTFParticle particle; - TLorentzVector dau = *decay.GetDecay(i); - particle.setPDG(pdgCodesDaughters[i]); - particle.setVxVyVz(mVx, mVy, mVz); - particle.setPxPyPzE(dau.Px(), dau.Py(), dau.Pz(), dau.E()); - particle.setBitOn(o2::upgrade::DecayerBits::ProducedByDecayer); - decayProducts.push_back(particle); + o2::upgrade::OTFParticle daughter; + const TLorentzVector& dau = *decay.GetDecay(i); + daughter.setPDG(pdgCodesDaughters[i]); + daughter.setVxVyVz(mVx, mVy, mVz); + daughter.setPxPyPzE(dau.Px(), dau.Py(), dau.Pz(), dau.E()); + daughter.setBitOn(o2::upgrade::DecayerBits::ProducedByDecayer); + decayProducts.push_back(daughter); } - return decayProducts; } @@ -166,6 +221,27 @@ class Decayer [[nodiscard]] float getDecayRadius() const { return static_cast(std::hypot(mVx, mVy)); } private: + /// Decay channels from the loaded decay table if the mother is listed there, otherwise from TDatabasePDG + template + std::vector getDecayChannels(const int pdgCode, const TParticleInfo& particleInfo) const + { + if (const auto it = mDecayTable.find(pdgCode); it != mDecayTable.end()) { + return it->second; + } + + std::vector channels; + for (int ch = 0; ch < particleInfo->NDecayChannels(); ++ch) { + DecayChannel channel; + channel.branchingRatio = particleInfo->DecayChannel(ch)->BranchingRatio(); + for (int dau = 0; dau < particleInfo->DecayChannel(ch)->NDaughters(); ++dau) { + channel.daughters.push_back(particleInfo->DecayChannel(ch)->DaughterPdgCode(dau)); + } + channels.push_back(channel); + } + return channels; + } + + std::unordered_map> mDecayTable; double mBz{20.}; // kG double mVx{-1.}, mVy{-1.}, mVz{-1.}; double mTheta{}; diff --git a/ALICE3/Core/DetLayer.h b/ALICE3/Core/DetLayer.h index b3ed3ed6b34..eb5b1cf0550 100644 --- a/ALICE3/Core/DetLayer.h +++ b/ALICE3/Core/DetLayer.h @@ -108,25 +108,25 @@ struct DetLayer { TString mName; // position variables - float mR; // radius in centimeters - float mZ; // mZ dimension in centimeters + float mR{}; // radius in centimeters + float mZ{}; // mZ dimension in centimeters // material variables - float mX0; // radiation length - float mXrho; // density + float mX0{}; // radiation length + float mXrho{}; // density // resolution variables for active layers - float mResRPhi; // RPhi resolution in centimeters - float mResZ; // Z resolution in centimeters + float mResRPhi{}; // RPhi resolution in centimeters + float mResZ{}; // Z resolution in centimeters // efficiency - float mEff; // detection efficiency + float mEff{1.f}; // detection efficiency // dead regions in phi (in radians) TGraph* mDeadPhiRegions = nullptr; // layer type - int mType; // 0: undefined/inert, 1: silicon, 2: gas/tpc + int mType{0}; // 0: undefined/inert, 1: silicon, 2: gas/tpc }; } // namespace o2::fastsim diff --git a/ALICE3/Core/FastTracker.cxx b/ALICE3/Core/FastTracker.cxx index 20eb7087f16..5bf0bcd87e3 100644 --- a/ALICE3/Core/FastTracker.cxx +++ b/ALICE3/Core/FastTracker.cxx @@ -312,6 +312,8 @@ int FastTracker::fastTrack(o2::track::TrackParCov inputTrack, o2::track::TrackPa nIntercepts = 0; nSiliconPoints = 0; nGasPoints = 0; + trackIU = -1.f; + trackOU = -1.f; std::array posIni{}; // provision for != PV inputTrack.getXYZGlo(posIni); const float initialRadius = std::hypot(posIni[0], posIni[1]); @@ -416,6 +418,9 @@ int FastTracker::fastTrack(o2::track::TrackParCov inputTrack, o2::track::TrackPa firstLayerReached = il; } lastLayerReached = il; + if (layers[il].isTOF()) { + continue; // TOF layers are not used for tracking, but are kept in range for the innermost/outermost update + } nIntercepts++; } @@ -475,7 +480,7 @@ int FastTracker::fastTrack(o2::track::TrackParCov inputTrack, o2::track::TrackPa continue; } - if (!layers[il].isInert()) { // only update covm for tracker hits + if (!layers[il].isInert() && !layers[il].isTOF()) { // only update covm for tracker hits const o2::track::TrackParametrization::dim2_t hitpoint = {xyz1[1], xyz1[2]}; const o2::track::TrackParametrization::dim3_t hitpointcov = {layers[il].getResolutionRPhi() * layers[il].getResolutionRPhi(), 0.f, layers[il].getResolutionZ() * layers[il].getResolutionZ()}; @@ -510,7 +515,14 @@ int FastTracker::fastTrack(o2::track::TrackParCov inputTrack, o2::track::TrackPa } hits.push_back(thisHit); - if (!layers[il].isInert()) { // good hit probability calculation + if (!layers[il].isInert()) { + trackIU = std::hypot(spacePoint[0], spacePoint[1]); + if (trackOU < 0.f) { + trackOU = trackIU; + } + } + + if (!layers[il].isInert() && !layers[il].isTOF()) { // good hit probability calculation float sigYCmb = o2::math_utils::sqrt(inwardTrack.getSigmaY2() + layers[il].getResolutionRPhi() * layers[il].getResolutionRPhi()); float sigZCmb = o2::math_utils::sqrt(inwardTrack.getSigmaZ2() + layers[il].getResolutionZ() * layers[il].getResolutionZ()); goodHitProbability[il] = probGoodChiSqHit(layers[il].getRadius() * 100, sigYCmb * 100, sigZCmb * 100); diff --git a/ALICE3/Core/FastTracker.h b/ALICE3/Core/FastTracker.h index d3d9a11d4ed..1d272595bbb 100644 --- a/ALICE3/Core/FastTracker.h +++ b/ALICE3/Core/FastTracker.h @@ -58,7 +58,7 @@ class FastTracker /// \param phiEnd End angle of the dead region (in radians) void addDeadPhiRegionInLayer(const std::string& layerName, float phiStart, float phiEnd); [[nodiscard]] DetLayer getLayer(const int layer) const { return layers[layer]; } - [[nodiscard]] std::vector getLayers() const { return layers; } + [[nodiscard]] const std::vector& getLayers() const { return layers; } [[nodiscard]] int getLayerIndex(const std::string& name) const; [[nodiscard]] size_t getNLayers() const { return layers.size(); } [[nodiscard]] bool isLayerInert(const int layer) const { return layers[layer].isInert(); } @@ -127,6 +127,8 @@ class FastTracker [[nodiscard]] int getNIntercepts() const { return nIntercepts; } [[nodiscard]] int getNSiliconPoints() const { return nSiliconPoints; } [[nodiscard]] int getNGasPoints() const { return nGasPoints; } + [[nodiscard]] float getTrackIU() const { return trackIU; } + [[nodiscard]] float getTrackOU() const { return trackOU; } [[nodiscard]] float getGoodHitProb(int layer) const { return (layer >= 0 && static_cast(layer) < goodHitProbability.size()) ? goodHitProbability[layer] : 0.0f; @@ -172,6 +174,8 @@ class FastTracker int nIntercepts = 0; /// found in first outward propagation int nSiliconPoints = 0; /// silicon-based space points added to track int nGasPoints = 0; /// tpc-based space points added to track + float trackIU = -1.f; /// innermost update: radius of the innermost active-layer hit added to track + float trackOU = -1.f; /// outermost update: radius of the outermost active-layer hit added to track std::vector goodHitProbability; ClassDef(FastTracker, 1); diff --git a/ALICE3/Core/FastTrackerLinkDef.h b/ALICE3/Core/FastTrackerLinkDef.h index 9885d281a4d..03e3a3c3f86 100644 --- a/ALICE3/Core/FastTrackerLinkDef.h +++ b/ALICE3/Core/FastTrackerLinkDef.h @@ -9,8 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef ALICE3_CORE_FASTTRACKERLINKDEF_H_ -#define ALICE3_CORE_FASTTRACKERLINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -19,5 +18,3 @@ #pragma link C++ class o2::fastsim::GeometryContainer + ; #pragma link C++ class o2::fastsim::FastTracker + ; #pragma link C++ class o2::fastsim::FlatLutWriter + ; - -#endif // ALICE3_CORE_FASTTRACKERLINKDEF_H_ diff --git a/ALICE3/Core/FlatLutEntry.cxx b/ALICE3/Core/FlatLutEntry.cxx index 984157f4520..66a6cedf860 100644 --- a/ALICE3/Core/FlatLutEntry.cxx +++ b/ALICE3/Core/FlatLutEntry.cxx @@ -140,23 +140,23 @@ size_t FlatLutData::getEntryOffset(int nch_bin, int rad_bin, int eta_bin, int pt const lutEntry_t* FlatLutData::getEntryRef(int nch_bin, int rad_bin, int eta_bin, int pt_bin) const { size_t offset = getEntryOffset(nch_bin, rad_bin, eta_bin, pt_bin); - return reinterpret_cast(mDataRef.data() + offset); + return reinterpret_cast(mDataRef.data() + offset); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) } lutEntry_t* FlatLutData::getEntry(int nch_bin, int rad_bin, int eta_bin, int pt_bin) { size_t offset = getEntryOffset(nch_bin, rad_bin, eta_bin, pt_bin); - return reinterpret_cast(mData.data() + offset); + return reinterpret_cast(mData.data() + offset); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) } const lutHeader_t& FlatLutData::getHeaderRef() const { - return *static_cast(static_cast(mDataRef.data())); + return *reinterpret_cast(mDataRef.data()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) } lutHeader_t& FlatLutData::getHeader() { - return *static_cast(static_cast(mData.data())); + return *reinterpret_cast(mData.data()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) } void FlatLutData::updateRef() @@ -216,7 +216,7 @@ lutHeader_t FlatLutData::previewHeader(const uint8_t* buffer, size_t size) if (size < sizeof(lutHeader_t)) { throw framework::runtime_error_f("Buffer too small for LUT header: expected at least %zu, got %zu", sizeof(lutHeader_t), size); } - const auto* header = static_cast(static_cast(buffer)); + const auto* header = reinterpret_cast(buffer); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) if (!header->checkVersion()) { throw framework::runtime_error_f("LUT header version mismatch: expected %d, got %d", LUTCOVM_VERSION, header->version); } @@ -246,7 +246,7 @@ FlatLutData FlatLutData::ViewFromBuffer(const uint8_t* buffer, size_t size) FlatLutData FlatLutData::ViewFromBuffer(std::span const& span) { - return ViewFromBuffer(reinterpret_cast(span.data()), span.size_bytes()); + return ViewFromBuffer(reinterpret_cast(span.data()), span.size_bytes()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) } bool FlatLutData::isLoaded() const @@ -257,7 +257,7 @@ bool FlatLutData::isLoaded() const lutHeader_t FlatLutData::previewHeader(std::ifstream& file, const char* filename) { lutHeader_t tempHeader; - file.read(reinterpret_cast(&tempHeader), sizeof(lutHeader_t)); + file.read(reinterpret_cast(&tempHeader), sizeof(lutHeader_t)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) if (file.gcount() != static_cast(sizeof(lutHeader_t))) { throw framework::runtime_error_f("Failed to read LUT header from %s", filename); } @@ -282,7 +282,7 @@ FlatLutData FlatLutData::loadFromFile(std::ifstream& file, const char* filename) size_t numEntries = static_cast(data.mNchBins) * data.mRadBins * data.mEtaBins * data.mPtBins; size_t entriesSize = numEntries * sizeof(lutEntry_t); - file.read(reinterpret_cast(data.data() + headerSize), entriesSize); + file.read(reinterpret_cast(data.data() + headerSize), entriesSize); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) if (file.gcount() != static_cast(entriesSize)) { throw framework::runtime_error_f("Failed to read LUT entries from %s: expected %zu bytes, got %zu", filename, entriesSize, static_cast(file.gcount())); } diff --git a/ALICE3/Core/FlatLutRootWriter.cxx b/ALICE3/Core/FlatLutRootWriter.cxx new file mode 100644 index 00000000000..162084dcffe --- /dev/null +++ b/ALICE3/Core/FlatLutRootWriter.cxx @@ -0,0 +1,210 @@ +// Copyright 2019-2020 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. + +#include "ALICE3/Core/FlatLutRootWriter.h" + +#include +#include + +#include +#include +#include + +namespace o2::fastsim +{ + +FlatLutRootWriter::FlatLutRootWriter(const lutHeader_t& header, bool covarianceStoresSigma) + : mHeader(header), + mCovarianceStoresSigma(covarianceStoresSigma) +{ +} + +void FlatLutRootWriter::reserve(std::size_t n) +{ + mRows.reserve(n); +} + +void FlatLutRootWriter::addEntry(int entryIndex, + int nchBin, + int radBin, + int etaBin, + int ptBin, + const lutEntry_t& entry) +{ + FlatLutRootRow row; + row.entryIndex = entryIndex; + row.nchBin = nchBin; + row.radBin = radBin; + row.etaBin = etaBin; + row.ptBin = ptBin; + + row.nch = entry.nch; + row.eta = entry.eta; + row.pt = entry.pt; + + row.valid = entry.valid; + row.eff = entry.eff; + row.eff2 = entry.eff2; + row.itof = entry.itof; + row.otof = entry.otof; + + std::copy(entry.covm.begin(), entry.covm.end(), row.covm.begin()); + std::copy(entry.eigval.begin(), entry.eigval.end(), row.eigval.begin()); + row.eigvec = entry.eigvec; + row.eiginv = entry.eiginv; + + mRows.push_back(std::move(row)); +} + +bool FlatLutRootWriter::writeToFile(const std::string& rootFileName) const +{ + TFile output(rootFileName.c_str(), "RECREATE"); + if (output.IsZombie()) { + return false; + } + + int version = mHeader.version; + int pdg = mHeader.pdg; + float mass = mHeader.mass; + float field = mHeader.field; + + int nch_nbins = mHeader.nchmap.nbins; + float nch_min = mHeader.nchmap.min; + float nch_max = mHeader.nchmap.max; + bool nch_log = mHeader.nchmap.log; + + int rad_nbins = mHeader.radmap.nbins; + float rad_min = mHeader.radmap.min; + float rad_max = mHeader.radmap.max; + bool rad_log = mHeader.radmap.log; + + int eta_nbins = mHeader.etamap.nbins; + float eta_min = mHeader.etamap.min; + float eta_max = mHeader.etamap.max; + bool eta_log = mHeader.etamap.log; + + int pt_nbins = mHeader.ptmap.nbins; + float pt_min = mHeader.ptmap.min; + float pt_max = mHeader.ptmap.max; + bool pt_log = mHeader.ptmap.log; + + bool covarianceStoresSigma = mCovarianceStoresSigma; + + TTree headerTree("lutHeader", "LUT header"); + headerTree.Branch("version", &version, "version/I"); + headerTree.Branch("pdg", &pdg, "pdg/I"); + headerTree.Branch("mass", &mass, "mass/F"); + headerTree.Branch("field", &field, "field/F"); + + headerTree.Branch("nch_nbins", &nch_nbins, "nch_nbins/I"); + headerTree.Branch("nch_min", &nch_min, "nch_min/F"); + headerTree.Branch("nch_max", &nch_max, "nch_max/F"); + headerTree.Branch("nch_log", &nch_log, "nch_log/O"); + + headerTree.Branch("rad_nbins", &rad_nbins, "rad_nbins/I"); + headerTree.Branch("rad_min", &rad_min, "rad_min/F"); + headerTree.Branch("rad_max", &rad_max, "rad_max/F"); + headerTree.Branch("rad_log", &rad_log, "rad_log/O"); + + headerTree.Branch("eta_nbins", &eta_nbins, "eta_nbins/I"); + headerTree.Branch("eta_min", &eta_min, "eta_min/F"); + headerTree.Branch("eta_max", &eta_max, "eta_max/F"); + headerTree.Branch("eta_log", &eta_log, "eta_log/O"); + + headerTree.Branch("pt_nbins", &pt_nbins, "pt_nbins/I"); + headerTree.Branch("pt_min", &pt_min, "pt_min/F"); + headerTree.Branch("pt_max", &pt_max, "pt_max/F"); + headerTree.Branch("pt_log", &pt_log, "pt_log/O"); + + headerTree.Branch("covarianceStoresSigma", &covarianceStoresSigma, "covarianceStoresSigma/O"); + headerTree.Fill(); + + TTree entryTree("lutEntries", "LUT entries"); + + int entryIndex = -1; + int nchBin = -1; + int radBin = -1; + int etaBin = -1; + int ptBin = -1; + + float nch = 0.f; + float eta = 0.f; + float pt = 0.f; + + bool valid = false; + float eff = 0.f; + float eff2 = 0.f; + float itof = 0.f; + float otof = 0.f; + + float covm[kNumCovarianceTerms] = {0.f}; + float eigval[kNumEigenModes] = {0.f}; + float eigvec[kNumEigenModes * kNumEigenModes] = {0.f}; + float eiginv[kNumEigenModes * kNumEigenModes] = {0.f}; + + entryTree.Branch("entryIndex", &entryIndex, "entryIndex/I"); + entryTree.Branch("nchBin", &nchBin, "nchBin/I"); + entryTree.Branch("radBin", &radBin, "radBin/I"); + entryTree.Branch("etaBin", &etaBin, "etaBin/I"); + entryTree.Branch("ptBin", &ptBin, "ptBin/I"); + + entryTree.Branch("nch", &nch, "nch/F"); + entryTree.Branch("eta", &eta, "eta/F"); + entryTree.Branch("pt", &pt, "pt/F"); + + entryTree.Branch("valid", &valid, "valid/O"); + entryTree.Branch("eff", &eff, "eff/F"); + entryTree.Branch("eff2", &eff2, "eff2/F"); + entryTree.Branch("itof", &itof, "itof/F"); + entryTree.Branch("otof", &otof, "otof/F"); + + entryTree.Branch("covm", covm, "covm[15]/F"); + entryTree.Branch("eigval", eigval, "eigval[5]/F"); + entryTree.Branch("eigvec", eigvec, "eigvec[25]/F"); + entryTree.Branch("eiginv", eiginv, "eiginv[25]/F"); + + for (const auto& row : mRows) { + entryIndex = row.entryIndex; + nchBin = row.nchBin; + radBin = row.radBin; + etaBin = row.etaBin; + ptBin = row.ptBin; + + nch = row.nch; + eta = row.eta; + pt = row.pt; + + valid = row.valid; + eff = row.eff; + eff2 = row.eff2; + itof = row.itof; + otof = row.otof; + + std::copy(row.covm.begin(), row.covm.end(), covm); + std::copy(row.eigval.begin(), row.eigval.end(), eigval); + for (int i = 0; i < kNumEigenModes; ++i) { + for (int j = 0; j < kNumEigenModes; ++j) { + eigvec[kNumEigenModes * i + j] = row.eigvec[i][j]; + eiginv[kNumEigenModes * i + j] = row.eiginv[i][j]; + } + } + + entryTree.Fill(); + } + + headerTree.Write(); + entryTree.Write(); + output.Close(); + + return true; +} + +} // namespace o2::fastsim diff --git a/ALICE3/Core/FlatLutRootWriter.h b/ALICE3/Core/FlatLutRootWriter.h new file mode 100644 index 00000000000..4360c7f72ae --- /dev/null +++ b/ALICE3/Core/FlatLutRootWriter.h @@ -0,0 +1,71 @@ +// Copyright 2019-2020 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. + +#ifndef ALICE3_CORE_FLATLUTROOTWRITER_H_ +#define ALICE3_CORE_FLATLUTROOTWRITER_H_ + +#include "ALICE3/Core/FlatLutEntry.h" + +#include +#include +#include +#include + +namespace o2::fastsim +{ + +struct FlatLutRootRow { + int entryIndex = -1; + int nchBin = -1; + int radBin = -1; + int etaBin = -1; + int ptBin = -1; + + float nch = 0.f; + float eta = 0.f; + float pt = 0.f; + + bool valid = false; + float eff = 0.f; + float eff2 = 0.f; + float itof = 0.f; + float otof = 0.f; + + std::array covm{}; + std::array eigval{}; + EigenMatrix eigvec{}; + EigenMatrix eiginv{}; +}; + +class FlatLutRootWriter +{ + public: + FlatLutRootWriter(const lutHeader_t& header, bool covarianceStoresSigma); + + void reserve(std::size_t n); + void addEntry(int entryIndex, + int nchBin, + int radBin, + int etaBin, + int ptBin, + const lutEntry_t& entry); + + bool writeToFile(const std::string& rootFileName) const; + + private: + lutHeader_t mHeader; + bool mCovarianceStoresSigma = false; + std::vector mRows; +}; + +} // namespace o2::fastsim + +#endif // ALICE3_CORE_FLATLUTROOTWRITER_H_ diff --git a/ALICE3/Core/FlatLutWriter.cxx b/ALICE3/Core/FlatLutWriter.cxx index dfb874af223..a85c242a8b1 100644 --- a/ALICE3/Core/FlatLutWriter.cxx +++ b/ALICE3/Core/FlatLutWriter.cxx @@ -266,7 +266,7 @@ void FlatLutWriter::lutWrite(const char* filename, int pdg, float field, size_t setMap(lutHeader.ptmap, mPtBinning); // Write header to file - lutFile.write(reinterpret_cast(&lutHeader), sizeof(lutHeader_t)); + lutFile.write(reinterpret_cast(&lutHeader), sizeof(lutHeader_t)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) if (!lutFile.good()) { LOGF(error, "Failed to write LUT header to %s", filename); lutFile.close(); @@ -361,7 +361,7 @@ void FlatLutWriter::lutWrite(const char* filename, int pdg, float field, size_t diagonalise(lutEntry); // Write entry to file - lutFile.write(reinterpret_cast(&lutEntry), sizeof(lutEntry_t)); + lutFile.write(reinterpret_cast(&lutEntry), sizeof(lutEntry_t)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) if (!lutFile.good()) { LOGF(error, "Failed to write LUT entry at index [%d,%d,%d,%d]", inch, irad, ieta, ipt); lutFile.close(); diff --git a/ALICE3/Core/FlatTrackSmearer.cxx b/ALICE3/Core/FlatTrackSmearer.cxx index 64c370d52a1..9617315d2e4 100644 --- a/ALICE3/Core/FlatTrackSmearer.cxx +++ b/ALICE3/Core/FlatTrackSmearer.cxx @@ -11,6 +11,7 @@ #include "FlatTrackSmearer.h" +#include "ALICE3/Core/ConfigurationParser.h" #include "ALICE3/Core/FlatLutEntry.h" #include "ALICE3/Core/GeometryContainer.h" @@ -96,7 +97,7 @@ bool TrackSmearer::loadTable(int pdg, const char* filename, bool forceReload) } LOGF(info, "Loading %s LUT file: '%s'", getParticleName(pdg), filename); - const std::string localFilename = o2::fastsim::GeometryEntry::accessFile(filename, "./.ALICE3/LUTs/", mCcdbManager, 10); + const std::string localFilename = o2::fastsim::ConfigurationParser::accessFile(filename, "./.ALICE3/LUTs/", mCcdbManager, o2::fastsim::GeometryContainer::cleanLutWhenLoaded() ? 10 : 0); std::ifstream lutFile(localFilename, std::ifstream::binary); if (!lutFile.is_open()) { @@ -170,7 +171,7 @@ bool TrackSmearer::viewTable(int pdg, const uint8_t* buffer, size_t size, bool f bool TrackSmearer::viewTable(int pdg, std::span const& span, bool forceReload) { - return viewTable(pdg, reinterpret_cast(span.data()), span.size_bytes(), forceReload); + return viewTable(pdg, reinterpret_cast(span.data()), span.size_bytes(), forceReload); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) } bool TrackSmearer::hasTable(int pdg) const diff --git a/ALICE3/Core/GeometryContainer.cxx b/ALICE3/Core/GeometryContainer.cxx index 79f1a2d67c5..130c3f9e1e9 100644 --- a/ALICE3/Core/GeometryContainer.cxx +++ b/ALICE3/Core/GeometryContainer.cxx @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. /// -/// \file GeometryContainer.h +/// \file GeometryContainer.cxx /// \author Nicolò Jacazio, Università del Piemonte Orientale (IT) /// \brief Set of utilities for the ALICE3 geometry handling /// \since February 13, 2026 @@ -19,70 +19,16 @@ #include "Common/Core/TableHelper.h" -#include #include #include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include #include #include -#include #include -#include -#include - namespace o2::fastsim { -std::map> GeometryEntry::parseTEnvConfiguration(std::string& filename, std::vector& layers) -{ - std::map> configMap; - filename = gSystem->ExpandPathName(filename.c_str()); - LOG(info) << "Parsing TEnv configuration file: " << filename; - TEnv env(filename.c_str()); - THashList* table = env.GetTable(); - layers.clear(); - for (int i = 0; i < table->GetEntries(); ++i) { - const std::string key = table->At(i)->GetName(); - // key should contain exactly one dot - if (key.find('.') == std::string::npos || key.find('.') != key.rfind('.')) { - LOG(fatal) << "Key " << key << " does not contain exactly one dot"; - continue; - } - const std::string firstPart = key.substr(0, key.find('.')); - if (std::find(layers.begin(), layers.end(), firstPart) == layers.end()) { - layers.push_back(firstPart); - } - } - env.Print(); - // Layers - for (const auto& layer : layers) { - LOG(info) << " Reading layer " << layer; - for (int i = 0; i < table->GetEntries(); ++i) { - const std::string key = table->At(i)->GetName(); - if (key.find(layer + ".") == 0) { - const std::string paramName = key.substr(key.find('.') + 1); - const std::string value = env.GetValue(key.c_str(), ""); - configMap[layer][paramName] = value; - } - } - } - return configMap; -} - bool GeometryContainer::mCleanLutWhenLoaded = true; void GeometryContainer::init(o2::framework::InitContext& initContext) { @@ -94,15 +40,15 @@ void GeometryContainer::init(o2::framework::InitContext& initContext) } LOG(info) << "Size of detector configuration: " << detectorConfiguration.size(); - bool cleanLutWhenLoaded; - const bool foundCleanLutWhenLoaded = common::core::getTaskOptionValue(initContext, "on-the-fly-detector-geometry-provider", "cleanLutWhenLoaded", cleanLutWhenLoaded, false); + bool cleanLut; + const bool foundCleanLutWhenLoaded = common::core::getTaskOptionValue(initContext, "on-the-fly-detector-geometry-provider", "cleanLutWhenLoaded", cleanLut, false); if (!foundCleanLutWhenLoaded) { LOG(fatal) << "Could not retrieve foundCleanLutWhenLoaded option from OnTheFlyDetectorGeometryProvider task."; return; } - setLutCleanupSetting(cleanLutWhenLoaded); + setLutCleanupSetting(cleanLut); - for (std::string& configFile : detectorConfiguration) { + for (const std::string& configFile : detectorConfiguration) { LOG(info) << "Detector geometry configuration file used: " << configFile; addEntry(configFile); } @@ -159,134 +105,4 @@ void GeometryEntry::replaceValue(const std::string& layerName, const std::string setValue(layerName, key, value); } -std::string GeometryEntry::accessFile(const std::string& path, const std::string& downloadPath, o2::ccdb::BasicCCDBManager* ccdb, int timeoutSeconds) -{ - - if (path.rfind("ccdb:", 0) == 0) { - const std::string ccdbPath = path.substr(5); // remove "ccdb:" prefix - const std::string localPath = Form("%s/%s/snapshot.root", downloadPath.c_str(), ccdbPath.c_str()); - const std::string lockFile = localPath + ".lock"; - const std::string doneFile = localPath + ".done"; - - // Create directory structure if it doesn't exist - std::string dirPath = localPath.substr(0, localPath.find_last_of('/')); - gSystem->mkdir(dirPath.c_str(), true); - - // Check if file is already fully downloaded - struct stat buffer; - if (stat(doneFile.c_str(), &buffer) == 0) { - LOG(info) << " --- Geometry configuration file already exists: " << localPath << ". Skipping download."; - return localPath; - } - - // Acquire file lock for inter-process synchronization - int lockFd = open(lockFile.c_str(), O_CREAT | O_RDWR, 0666); - if (lockFd == -1) { - LOG(error) << " --- Failed to create lock file: " << lockFile; - return localPath; - } - - // Try to acquire exclusive lock (non-blocking) - LOG(info) << " --- Attempting to acquire lock for: " << localPath; - int lockResult = flock(lockFd, LOCK_EX | LOCK_NB); - - if (lockResult == -1 && errno == EWOULDBLOCK) { - // Lock is held by another process - wait up to 10 minutes for download to complete - LOG(info) << " --- Lock is held by another process. Waiting for download to complete (up to 10 minutes)..."; - close(lockFd); - - const auto startTime = std::chrono::steady_clock::now(); - const auto timeout = std::chrono::minutes(10); - const auto checkInterval = std::chrono::seconds(5); - - while (true) { - // Check if download is complete - if (stat(doneFile.c_str(), &buffer) == 0) { - LOG(info) << " --- Geometry configuration file was downloaded by another process: " << localPath; - return localPath; - } - - // Check timeout - auto elapsed = std::chrono::steady_clock::now() - startTime; - if (elapsed >= timeout) { - LOG(fatal) << " --- Timeout waiting for geometry file download: " << localPath << ". Waited for 10 minutes."; - return localPath; - } - - // Wait before checking again - std::this_thread::sleep_for(checkInterval); - } - } else if (lockResult == -1) { - LOG(error) << " --- Failed to acquire lock for: " << lockFile; - close(lockFd); - return localPath; - } - - // Lock acquired successfully - double-check if file was downloaded while we were trying - if (stat(doneFile.c_str(), &buffer) == 0) { - LOG(info) << " --- Geometry configuration file was downloaded by another process: " << localPath; - flock(lockFd, LOCK_UN); - close(lockFd); - return localPath; - } - - // File does not exist, retrieve from CCDB - LOG(info) << " --- CCDB source detected for detector geometry " << path; - std::map metadata; - bool status = ccdb->getCCDBAccessor().retrieveBlob(ccdbPath, downloadPath, metadata, 1); - if (!status) { - flock(lockFd, LOCK_UN); - close(lockFd); - LOG(fatal) << " --- Failed to retrieve geometry configuration from CCDB for path: " << ccdbPath; - return ""; - } - LOG(info) << " --- Retrieved geometry configuration from CCDB to: " << localPath; - - // Verify the integrity of the downloaded file - if (stat(localPath.c_str(), &buffer) != 0) { - flock(lockFd, LOCK_UN); - close(lockFd); - LOG(fatal) << " --- Downloaded file does not exist or is not accessible: " << localPath; - return ""; - } - if (buffer.st_size == 0) { - flock(lockFd, LOCK_UN); - close(lockFd); - LOG(fatal) << " --- Downloaded file is empty: " << localPath; - return ""; - } - LOG(info) << " --- File integrity verified. Size: " << buffer.st_size << " bytes"; - - // Create done marker file to indicate successful download - std::ofstream doneMarker(doneFile); - doneMarker.close(); - - // Release lock - flock(lockFd, LOCK_UN); - close(lockFd); - - // If timeout is specified, schedule file deletion after timeout - if (timeoutSeconds > 0 && GeometryContainer::cleanLutWhenLoaded()) { - LOG(info) << " --- Deleting geometry configuration file after timeout: " << localPath; - std::thread deletionThread([localPath, doneFile, timeoutSeconds]() { - LOG(info) << " --- Operating deletion of geometry configuration file after timeout: " << localPath; - std::this_thread::sleep_for(std::chrono::seconds(timeoutSeconds)); - if (std::remove(localPath.c_str()) == 0) { - LOG(info) << " --- File deleted successfully: " << localPath; - } else { - LOG(warning) << " --- Failed to delete file: " << localPath; - } - // Also remove the done marker file - if (std::remove(doneFile.c_str()) == 0) { - LOG(info) << " --- Done marker deleted: " << doneFile; - } - }); - deletionThread.detach(); - } - - return localPath; - } - return path; -} - } // namespace o2::fastsim diff --git a/ALICE3/Core/GeometryContainer.h b/ALICE3/Core/GeometryContainer.h index 67037c1b183..deb69663e3b 100644 --- a/ALICE3/Core/GeometryContainer.h +++ b/ALICE3/Core/GeometryContainer.h @@ -18,6 +18,8 @@ #ifndef ALICE3_CORE_GEOMETRYCONTAINER_H_ #define ALICE3_CORE_GEOMETRYCONTAINER_H_ +#include "ALICE3/Core/ConfigurationParser.h" + #include #include #include @@ -34,36 +36,18 @@ struct GeometryEntry { // Default constructor GeometryEntry() = default; explicit GeometryEntry(const std::string& filename, o2::ccdb::BasicCCDBManager* ccdb = nullptr) + : mFileName(ConfigurationParser::accessFile(filename, "./.ALICE3/Configuration/", ccdb)) { - mFileName = accessFile(filename, "./.ALICE3/Configuration/", ccdb); - mConfigurations = GeometryEntry::parseTEnvConfiguration(mFileName, mLayerNames); + mConfigurations = ConfigurationParser::parseTEnvConfiguration(mFileName, mLayerNames); LOG(info) << "Loaded geometry configuration from file: " << mFileName << " with " << mLayerNames.size() << " layers."; if (mLayerNames.empty()) { LOG(warning) << "No layers found in geometry configuration file: " << filename; } } - /** - * @brief Parses a TEnv configuration file and returns the key-value pairs split per entry - * @param filename Path to the TEnv configuration file - * @param layers Vector to store the order of the layers as they appear in the file - * @return A map where each key is a layer name and the value is another map of key-value pairs for that layer - */ - static std::map> parseTEnvConfiguration(std::string& filename, std::vector& layers); - - /** - * @brief Accesses a file given its path, which can be either a local path or a ccdb path (starting with "ccdb:"). In the first case it returns the local path, in the second it retrieves the file from ccdb and returns the local path to the retrieved file. - * @param path The path to the file, either local or ccdb (starting with "ccdb:") - * @param downloadPath The local path where to download the file if it's a ccdb path. Default is "/tmp/GeometryContainer/" - * @param ccdb Pointer to the CCDB manager to use for retrieving the file if it's a ccdb path. If nullptr, the function will create a temporary CCDB manager instance. Default is nullptr. - * @param timeoutSeconds If positive, then this function will wait for these seconds after download before removing the downloaded file. - * @return The local path to the file, either the original local path or the path to the retrieved file from ccdb - */ - static std::string accessFile(const std::string& path, const std::string& downloadPath = "/tmp/GeometryContainer/", o2::ccdb::BasicCCDBManager* ccdb = nullptr, int timeoutSeconds = 0); - - std::map> getConfigurations() const { return mConfigurations; } + const std::map>& getConfigurations() const { return mConfigurations; } std::map getConfiguration(const std::string& layerName) const; - std::vector getLayerNames() const { return mLayerNames; } + const std::vector& getLayerNames() const { return mLayerNames; } bool hasValue(const std::string& layerName, const std::string& key) const; std::string getValue(const std::string& layerName, const std::string& key, bool require = true) const; void setValue(const std::string& layerName, const std::string& key, const std::string& value) { mConfigurations[layerName][key] = value; } diff --git a/ALICE3/DataModel/OTFMID.h b/ALICE3/DataModel/OTFMID.h new file mode 100644 index 00000000000..e188f75fd14 --- /dev/null +++ b/ALICE3/DataModel/OTFMID.h @@ -0,0 +1,68 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// \file OTFMID.h +/// \author David Dobrigkeit Chinellato +/// \author Antonio Ortiz, UNAM +/// \since 11/05/2023 +/// \brief Set of tables for the ALICE3 OTFMID information +/// TODO: remove the columns that are not usefulf for me + +#ifndef ALICE3_DATAMODEL_OTFMID_H_ +#define ALICE3_DATAMODEL_OTFMID_H_ + +#include + +#include +#include + +namespace o2::aod +{ +namespace upgrade_mid +{ +DECLARE_SOA_COLUMN(MIDTrackTime, midTrackTime, float); //! Track time generated at the MID +DECLARE_SOA_COLUMN(MIDTrackLength, midTrackLength, float); //! track length for calculation of MID (generated) + +DECLARE_SOA_COLUMN(MIDEventTime, midEventTime, float); //! Event time reconstructed with the MID +DECLARE_SOA_COLUMN(MIDEventTimeErr, midEventTimeErr, float); //! Uncertainty on the event time reconstructed with the MID +DECLARE_SOA_COLUMN(MIDTrackPosReco, midTrackPosReco, std::vector); //! Measured position in MID +DECLARE_SOA_COLUMN(MIDTrackPos, midTrackPos, std::vector); //! Expected position in MID +DECLARE_SOA_COLUMN(MIDCandidate, midCandidate, bool); //! Muon candidate +DECLARE_SOA_COLUMN(MIDTrackTimeReco, midTrackTimeReco, float); //! Track time measured at MID +DECLARE_SOA_COLUMN(MIDTrackLengthReco, midTrackLengthReco, float); //! track length for calculation of MID (reconstructed) + +} // namespace upgrade_mid + +DECLARE_SOA_TABLE(UpgradeMidMCs, "AOD", "UPGRADEMIDMC", + upgrade_mid::MIDTrackTime, + upgrade_mid::MIDTrackLength); + +DECLARE_SOA_TABLE(UpgradeMids, "AOD", "UPGRADEMID", + upgrade_mid::MIDEventTime, + upgrade_mid::MIDEventTimeErr, + upgrade_mid::MIDTrackPosReco, + upgrade_mid::MIDTrackTimeReco, + upgrade_mid::MIDTrackLengthReco, + upgrade_mid::MIDCandidate); + +DECLARE_SOA_TABLE(UpgradeMidExpecteds, "AOD", "UPGRADEMIDEXPT", + upgrade_mid::MIDTrackPos, + upgrade_mid::MIDTrackTime, + upgrade_mid::MIDTrackLength); + +using UpgradeMidMC = UpgradeMidMCs::iterator; +using UpgradeMid = UpgradeMids::iterator; +using UpgradeMidExpected = UpgradeMidExpecteds::iterator; + +} // namespace o2::aod + +#endif // ALICE3_DATAMODEL_OTFMID_H_ diff --git a/ALICE3/DataModel/OTFTOF.h b/ALICE3/DataModel/OTFTOF.h index 8643abc959c..621e878a1e7 100644 --- a/ALICE3/DataModel/OTFTOF.h +++ b/ALICE3/DataModel/OTFTOF.h @@ -79,6 +79,8 @@ DECLARE_SOA_COLUMN(OuterTOFExpectedTimeDe, outerTOFExpectedTimeDe, float); //! DECLARE_SOA_COLUMN(OuterTOFExpectedTimeTr, outerTOFExpectedTimeTr, float); //! Reconstructed expected time at the OuterTOF for the Triton mass hypotheses DECLARE_SOA_COLUMN(OuterTOFExpectedTimeHe3, outerTOFExpectedTimeHe3, float); //! Reconstructed expected time at the OuterTOF for the Helium3 mass hypotheses DECLARE_SOA_COLUMN(OuterTOFExpectedTimeAl, outerTOFExpectedTimeAl, float); //! Reconstructed expected time at the OuterTOF for the Alpha mass hypotheses +DECLARE_SOA_COLUMN(HasInnerTOF, hasInnerTOF, bool); //! InnerTOF short-lived values are filled (false if dummy) +DECLARE_SOA_COLUMN(HasOuterTOF, hasOuterTOF, bool); //! OuterTOF short-lived values are filled (false if dummy) DECLARE_SOA_DYNAMIC_COLUMN(NSigmaInnerTOF, nSigmaInnerTOF, //! General function to get the nSigma for the InnerTOF [](const float el, const float mu, @@ -260,10 +262,15 @@ DECLARE_SOA_TABLE(UpgradeTofShortLiveds, "AOD", "UPGRTOFSHRTLVD", upgrade_tof::OuterTOFExpectedTimeXi, upgrade_tof::OuterTOFExpectedTimeOm); +DECLARE_SOA_TABLE(UpgradeTofShortLivedSignals, "AOD", "UPGRTOFSLSIGNAL", + upgrade_tof::HasInnerTOF, + upgrade_tof::HasOuterTOF); + using UpgradeTofMC = UpgradeTofMCs::iterator; using UpgradeTof = UpgradeTofs::iterator; using UpgradeTofExpectedTime = UpgradeTofExpectedTimes::iterator; using UpgradeTofShortLived = UpgradeTofShortLiveds::iterator; +using UpgradeTofShortLivedSignal = UpgradeTofShortLivedSignals::iterator; } // namespace o2::aod diff --git a/ALICE3/DataModel/ReducedTablesAlice3.h b/ALICE3/DataModel/ReducedTablesAlice3.h index 363ba50842f..7ff870f9308 100644 --- a/ALICE3/DataModel/ReducedTablesAlice3.h +++ b/ALICE3/DataModel/ReducedTablesAlice3.h @@ -20,6 +20,7 @@ #include "PWGDQ/DataModel/ReducedInfoTables.h" +#include "ALICE3/DataModel/OTFMID.h" #include "ALICE3/DataModel/OTFPIDTrk.h" #include "ALICE3/DataModel/OTFRICH.h" #include "ALICE3/DataModel/OTFTOF.h" @@ -275,6 +276,14 @@ DECLARE_SOA_TABLE(ReducedA3PIDTOF, "AOD", "REA3PIDTOF", upgrade_tof::NSigmaHelium3OuterTOF, upgrade_tof::NSigmaAlphaOuterTOF>); +DECLARE_SOA_TABLE(ReducedA3PIDMID, "AOD", "REA3PIDMID", + upgrade_mid::MIDEventTime, + upgrade_mid::MIDEventTimeErr, + upgrade_mid::MIDTrackPosReco, + upgrade_mid::MIDTrackTimeReco, + upgrade_mid::MIDTrackLengthReco, + upgrade_mid::MIDCandidate); + DECLARE_SOA_TABLE(ReducedA3PIDRich, "AOD", "REA3PIDRICH", upgrade_rich::NSigmaElectronRich, upgrade_rich::NSigmaMuonRich, diff --git a/ALICE3/DataModel/tracksAlice3.h b/ALICE3/DataModel/tracksAlice3.h index 8e19feb0d0e..37c53e6e38d 100644 --- a/ALICE3/DataModel/tracksAlice3.h +++ b/ALICE3/DataModel/tracksAlice3.h @@ -26,38 +26,60 @@ namespace o2::aod { namespace track_alice3 { + DECLARE_SOA_COLUMN(IsReconstructed, isReconstructed, bool); //! is reconstructed or not DECLARE_SOA_COLUMN(NSiliconHits, nSiliconHits, int); //! number of silicon hits DECLARE_SOA_COLUMN(NTPCHits, nTPCHits, int); //! number of tpc hits DECLARE_SOA_COLUMN(PdgCode, pdgCode, int); //! PDG code of the linked truth MC particle DECLARE_SOA_COLUMN(TrackType, trackType, int); //! Type of the track DECLARE_SOA_COLUMN(IsPVContributor, isPVContributor, bool); //! Has track contributed to the PV fit? +DECLARE_SOA_COLUMN(TrackIU, trackIU, float); //! Innermost update: radius (cm) of the innermost hit assigned to the track +DECLARE_SOA_COLUMN(TrackOU, trackOU, float); //! Outermost update: radius (cm) of the outermost hit assigned to the track +DECLARE_SOA_COLUMN(SecondaryVertexRadius, secondaryVertexRadius, float); //! Radius of the secondary vertex associated with the track +DECLARE_SOA_COLUMN(SecondaryVertexX, secondaryVertexX, float); //! X position of the secondary vertex associated with the track +DECLARE_SOA_COLUMN(SecondaryVertexY, secondaryVertexY, float); //! Y position of the secondary vertex associated with the track +DECLARE_SOA_COLUMN(SecondaryVertexZ, secondaryVertexZ, float); //! Z position of the secondary vertex associated with the track + } // namespace track_alice3 DECLARE_SOA_TABLE(TracksAlice3, "AOD", "TRACKSALICE3", - track_alice3::IsReconstructed); + track_alice3::IsReconstructed, + track_alice3::TrackIU, + track_alice3::TrackOU); using TrackAlice3 = TracksAlice3::iterator; DECLARE_SOA_TABLE(TracksAlice3Pdg, "AOD", "TRACKSALICE3PDG", track_alice3::PdgCode); using TrackAlice3Pdg = TracksAlice3Pdg::iterator; -DECLARE_SOA_TABLE(TracksExtraA3, "AOD", "TracksExtraA3", +DECLARE_SOA_TABLE(TracksExtraA3_000, "AOD", "TracksExtraA3", track_alice3::NSiliconHits, track_alice3::NTPCHits, track_alice3::TrackType, track_alice3::IsPVContributor); +DECLARE_SOA_TABLE_VERSIONED(TracksExtraA3_001, "AOD", "TracksExtraA3", 1, + track_alice3::NSiliconHits, + track_alice3::NTPCHits, + track_alice3::TrackType, + track_alice3::IsPVContributor, + track_alice3::SecondaryVertexRadius, + track_alice3::SecondaryVertexX, + track_alice3::SecondaryVertexY, + track_alice3::SecondaryVertexZ); +using TracksExtraA3 = TracksExtraA3_001; using TrackExtraA3 = TracksExtraA3::iterator; - namespace mcparticle_alice3 { + DECLARE_SOA_COLUMN(NHits, nHits, int); //! number of silicon hits DECLARE_SOA_COLUMN(Charge, charge, float); //! particle charge DECLARE_SOA_COLUMN(DecayRadius, decayRadius, float); //! Radius for decayed particle produced by decayer (-1 if not decayed) DECLARE_SOA_BITMAP_COLUMN(DecayerBits, decayerBits, 8); //! Bit mask for particle produced by the OTF decayer +DECLARE_SOA_COLUMN(SecondaryVertexRadius, secondaryVertexRadius, float); //! Radius of the secondary vertex associated with the track } // namespace mcparticle_alice3 DECLARE_SOA_TABLE(MCParticlesExtraA3, "AOD", "MCParticlesExtraA3", mcparticle_alice3::NHits, - mcparticle_alice3::Charge); + mcparticle_alice3::Charge, + mcparticle_alice3::SecondaryVertexRadius); using MCParticleExtraA3 = MCParticlesExtraA3::iterator; DECLARE_SOA_TABLE(OTFParticleExtras, "AOD", "OTFPARTICLEEXTRA", diff --git a/ALICE3/Macros/Configuration/a3geometry_v3.ini b/ALICE3/Macros/Configuration/a3geometry_v3.ini index e4eb415c133..3381df08209 100644 --- a/ALICE3/Macros/Configuration/a3geometry_v3.ini +++ b/ALICE3/Macros/Configuration/a3geometry_v3.ini @@ -92,15 +92,6 @@ B05.resZ: 0.001 B05.eff: 1.00 B05.type: 1 -iTOF.r: 19 -iTOF.z: 250 -iTOF.x0: 0.01*3. -iTOF.xrho: 0.03 -iTOF.resRPhi: 0.001 -iTOF.resZ: 0.001 -iTOF.eff: 1.00 -iTOF.type: 0 - B06.r: 20. B06.z: 250 B06.x0: 0.01 @@ -110,6 +101,15 @@ B06.resZ: 0.001 B06.eff: 1.00 B06.type: 1 +iTOF.r: 21 +iTOF.z: 250 +iTOF.x0: 0.01*3. +iTOF.xrho: 0.03 +iTOF.resRPhi: 0.001 +iTOF.resZ: 0.001 +iTOF.eff: 1.00 +iTOF.type: 3 + B07.r: 30. B07.z: 250 B07.x0: 0.01 @@ -146,6 +146,15 @@ B10.resZ: 0.001 B10.eff: 1.00 B10.type: 1 +oTOF.r: 92 +oTOF.z: 250 +oTOF.x0: 0.01*3. +oTOF.xrho: 0.03 +oTOF.resRPhi: 0.001 +oTOF.resZ: 0.001 +oTOF.eff: 1.00 +oTOF.type: 3 + # Lookup tables global.lutEl: ccdb:/Users/j/jekarlss/LUTs/v3/b/el global.lutMu: ccdb:/Users/j/jekarlss/LUTs/v3/b/mu diff --git a/ALICE3/Macros/Configuration/decayTable_example.ini b/ALICE3/Macros/Configuration/decayTable_example.ini new file mode 100644 index 00000000000..777defd3570 --- /dev/null +++ b/ALICE3/Macros/Configuration/decayTable_example.ini @@ -0,0 +1,16 @@ +# Example decay table for the on-the-fly decayer (configurable "decayTable") +# Overrides the TDatabasePDG decay channels of the listed mothers; unlisted particles are unaffected. +# Format: .: ... +# Branching ratios of each mother should sum to 1 (within 0.01); small deviations are renormalised. + +# Lambda -> p pi- only +3122.ch0: 0.639 2212 -211 +-3122.ch0: 0.639 -2112 111 + +# Lambda -> n pi0 only +3122.ch1: 0.361 2112 111 +-3122.ch1: 0.361 -2112 111 + +# Xi- -> Lambda pi- +3312.ch0: 1.0 3122 -211 +-3312.ch0: 1.0 -3122 211 diff --git a/ALICE3/TableProducer/OTF/CMakeLists.txt b/ALICE3/TableProducer/OTF/CMakeLists.txt index 6fe3f41a02f..d92809cae76 100644 --- a/ALICE3/TableProducer/OTF/CMakeLists.txt +++ b/ALICE3/TableProducer/OTF/CMakeLists.txt @@ -24,6 +24,11 @@ o2physics_add_dpl_workflow(onthefly-tofpid PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::ReconstructionDataFormats O2::DetectorsCommonDataFormats O2Physics::ALICE3Core O2Physics::FastTracker COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(onthefly-midpid + SOURCES onTheFlyMidPid.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::ReconstructionDataFormats O2::DetectorsCommonDataFormats O2Physics::ALICE3Core O2Physics::FastTracker + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(onthefly-richpid SOURCES onTheFlyRichPid.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::ReconstructionDataFormats O2::DetectorsCommonDataFormats O2Physics::ALICE3Core O2Physics::FastTracker diff --git a/ALICE3/TableProducer/OTF/onTheFlyDecayer.cxx b/ALICE3/TableProducer/OTF/onTheFlyDecayer.cxx index 2c8c4dea0c9..c6f587fdd3c 100644 --- a/ALICE3/TableProducer/OTF/onTheFlyDecayer.cxx +++ b/ALICE3/TableProducer/OTF/onTheFlyDecayer.cxx @@ -20,6 +20,7 @@ #include "ALICE3/Core/TrackUtilities.h" #include "ALICE3/DataModel/tracksAlice3.h" +#include #include #include #include @@ -92,10 +93,12 @@ struct OnTheFlyDecayer { o2::upgrade::Decayer decayer; Service pdgDB{}; + Service ccdb{}; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; Configurable seed{"seed", 0, "Set seed for particle decayer"}; Configurable magneticField{"magneticField", 20., "Magnetic field (kG)"}; + Configurable decayTable{"decayTable", "", "Decay table overriding the TDatabasePDG decay channels of the listed particles (local path or ccdb:), empty to disable"}; Configurable> enabledDecays{"enabledDecays", {DefaultParameters[0].data(), NumDecays, NumParameters, particleNames, parameterNames}, "Enable option for particle to be decayed: 0 - no, 1 - yes"}; @@ -111,6 +114,10 @@ struct OnTheFlyDecayer { LOG(info) << " --- Using magnetic field: " << magneticField; decayer.setSeed(seed); decayer.setBField(magneticField); + if (!decayTable.value.empty()) { + LOG(info) << " --- Using decay table: " << decayTable.value; + decayer.loadDecayTable(decayTable.value, pdgDB, ccdb.operator->()); + } for (int i = 0; i < NumDecays; ++i) { if (enabledDecays->get(particleNames[i].c_str(), "enable") != 0) { LOG(info) << " --- Decay enabled: " << pdgCodes[i]; diff --git a/ALICE3/TableProducer/OTF/onTheFlyMidPid.cxx b/ALICE3/TableProducer/OTF/onTheFlyMidPid.cxx new file mode 100644 index 00000000000..689da6f6e83 --- /dev/null +++ b/ALICE3/TableProducer/OTF/onTheFlyMidPid.cxx @@ -0,0 +1,1123 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file onTheFlyMidPid.cxx +/// +/// \brief This task goes straight from a combination of track table and mcParticles +/// and a custom MID configuration to a table of muon id for the particles +/// being analysed. +/// +/// \author David Dobrigkeit Chinellato, UNICAMP +/// \author Antonio Ortiz, UNAM +/// \since May 22, 2024 +/// + +#include "GeometryContainer.h" + +#include "ALICE3/Core/FlatTrackSmearer.h" +#include "ALICE3/Core/TrackUtilities.h" +#include "ALICE3/DataModel/OTFCollision.h" +#include "ALICE3/DataModel/OTFMID.h" +#include "Common/Core/trackUtilities.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace o2; +using namespace o2::framework; + +struct OnTheFlyMidPid { + Produces upgradeMidMC; + Produces upgradeMid; + Produces upgradeMidExpected; + + // necessary for particle charges + Service pdgDatabase; + // Necessary for LUTs + Service ccdb; + + // these are the settings governing the MID layer to be used + // in the evolution of this effort + struct : ConfigurableGroup { + Configurable considerEventTime{"considerEventTime", true, "flag to consider event time"}; + // Parametrization of muon/pion absortion in iron + Configurable magRadiusC{"magRadiusC", 160., "Magnet coil radius (cm)"}; + Configurable absRadius1{"absRadius1", 220., "Absorber inner radius (cm)"}; + Configurable absRadius2{"absRadius2", 290., "Absorber outer radius (cm)"}; + Configurable absDensity{"absDensity", 7.874, "iron density (g/cm3)"}; + Configurable absLambdaPi{"absLambdaPi", 20.41, "illustrative effective interaction length (cm)"}; + Configurable absDeDx{"absDeDxMIP", 1.7, "dE/(rho dx) MIP MeV/g/cm2"}; + Configurable absFakeAt3{"absFakeAt3", 0.032, "fake-muon efficiency at 3 GeV/c from GEANT4+BDT abs(eta)<1.24, 70 cm"}; + Configurable absFakeAt10{"absFakeAt10", 0.08, "fake-muon efficiency at 10 GeV/c from GEANT4+BDT abs(eta)<1.24, 70 cm"}; + Configurable absSigmaEta{"absSigmaEta", 10.65, "user-configurable phenomenological eta width"}; + Configurable absPionFractionAt3{"absPionFractionAt3", 0.5, "parameter from GEANT4+BDT"}; // assumed, NOT measured + Configurable absEtaMax{"absEtaMax", 1.24, "max eta covered by detector"}; + Configurable midRadius{"midRadius", 302.5, "MID radius (cm)"}; + Configurable midLength{"midLength", 1000.0, "MID length (cm)"}; + Configurable> midCellDimension{"midCellDimension", {5.1, 5.1}, "MID cell dimension in Z and RPhi (cm)"}; + Configurable midFractionOfInactiveArea{"midFractionOfInactiveArea", (0.1 / 5.1), "MID fraction of inactive area"}; + Configurable midTimeReso{"midTimeReso", 1500, "MID time error (ps)"}; + Configurable nStepsLIntegrator{"nStepsLIntegrator", 200, "number of steps in length integrator"}; + Configurable multiplicityEtaRange{"multiplicityEtaRange", 0.800000012, "eta range to compute the multiplicity"}; + Configurable flagIncludeTrackTimeRes{"flagIncludeTrackTimeRes", true, "flag to include or exclude track time resolution"}; + Configurable flagMIDLoadDelphesLUTs{"flagMIDLoadDelphesLUTs", false, "flag to load Delphes LUTs for tracking correction (use recoTrack parameters if false)"}; + } simConfig; + + struct : ConfigurableGroup { + Configurable doQAplots{"doQAplots", true, "do basic velocity plot qa"}; + Configurable doSeparationVsPt{"doSeparationVsPt", true, "Produce plots vs pt or p"}; + Configurable nBinsBeta{"nBinsBeta", 2200, "number of bins in beta"}; + Configurable nBinsP{"nBinsP", 80, "number of bins in momentum/pT depending on doSeparationVsPt"}; + Configurable nBinsTrackLength{"nBinsTrackLength", 500, "number of bins in track length"}; + Configurable nBinsNsigmaWrongSpecies{"nBinsNsigmaWrongSpecies", 200, "number of bins in Nsigma plot (wrong species)"}; + Configurable minNsigmaRange{"minNsigmaRange", -10, "lower limit for the nsigma axis"}; + Configurable maxNsigmaRange{"maxNsigmaRange", +10, "upper limit for the nsigma axis"}; + Configurable nBinsDeltaCorrectSpecies{"nBinsDeltaCorrectSpecies", 200, "number of bins in Delta plot (correct speies)"}; + Configurable nBinsDeltaWrongSpecies{"nBinsDeltaWrongSpecies", 200, "number of bins in Delta plot (wrong species)"}; + Configurable nBinsTrackDeltaLength{"nBinsTrackDeltaLength", 100, "number of bins in delta track length"}; + Configurable nBinsNsigmaCorrectSpecies{"nBinsNsigmaCorrectSpecies", 200, "number of bins in Nsigma plot (correct speies)"}; + Configurable minDeltaRange{"minDeltaRange", -100, "lower limit for the nsigma axis"}; + Configurable maxDeltaRange{"maxDeltaRange", +100, "upper limit for the nsigma axis"}; + Configurable nBinsTimeRes{"nBinsTimeRes", 400, "number of bins plots time resolution"}; + Configurable nBinsRelativeEtaPt{"nBinsRelativeEtaPt", 400, "number of bins plots pt and eta relative errors"}; + Configurable nBinsEta{"nBinsEta", 400, "number of bins plot relative eta error"}; + Configurable nBinsMult{"nBinsMult", 200, "number of bins in multiplicity"}; + Configurable maxMultRange{"maxMultRange", 1000.f, "upper limit in multiplicity plots"}; + } plotsConfig; + + o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; + + // Track smearer array, one per geometry + // std::vector> mSmearer; + std::vector> mSmearer; + // needed: random number generator for smearing + TRandom3 pRandomNumberGenerator; + + // for handling basic QA histograms if requested + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + OutputObj listEfficiency{"efficiency"}; + + // Configuration defined at init time + o2::fastsim::GeometryContainer mGeoContainer; + float mMagneticField = 0.0f; + void init(o2::framework::InitContext& initContext) + { + mGeoContainer.setCcdbManager(ccdb.operator->()); + mGeoContainer.init(initContext); + + const int nGeometries = mGeoContainer.getNumberOfConfigurations(); + mMagneticField = mGeoContainer.getFloatValue(0, "global", "magneticfield"); + + pRandomNumberGenerator.SetSeed(0); // fully randomize + if (simConfig.flagMIDLoadDelphesLUTs) { + for (int icfg = 0; icfg < nGeometries; ++icfg) { + mSmearer.emplace_back(std::make_unique()); + mSmearer[icfg]->setCcdbManager(ccdb.operator->()); + std::map globalConfiguration = mGeoContainer.getConfiguration(icfg, "global"); + for (const auto& entry : globalConfiguration) { + int pdg = 0; + if (entry.first.find("lut") != 0) { + continue; + } + if (entry.first.find("lutEl") != std::string::npos) { + pdg = kElectron; + } else if (entry.first.find("lutMu") != std::string::npos) { + pdg = kMuonMinus; + } else if (entry.first.find("lutPi") != std::string::npos) { + pdg = kPiPlus; + } else if (entry.first.find("lutKa") != std::string::npos) { + pdg = kKPlus; + } else if (entry.first.find("lutPr") != std::string::npos) { + pdg = kProton; + } else if (entry.first.find("lutDe") != std::string::npos) { + pdg = o2::constants::physics::kDeuteron; + } else if (entry.first.find("lutTr") != std::string::npos) { + pdg = o2::constants::physics::kTriton; + } else if (entry.first.find("lutHe3") != std::string::npos) { + pdg = o2::constants::physics::kHelium3; + } else if (entry.first.find("lutAl") != std::string::npos) { + pdg = o2::constants::physics::kAlpha; + } + + std::string filename = entry.second; + if (pdg == 0) { + LOG(fatal) << "Unknown LUT entry " << entry.first << " for global configuration"; + } + LOG(info) << "Loading LUT for pdg " << pdg << " for config " << icfg << " from provided file '" << filename << "'"; + if (filename.empty()) { + LOG(warning) << "No LUT file passed for pdg " << pdg << ", skipping."; + } + // strip from leading/trailing spaces + filename.erase(0, filename.find_first_not_of(" ")); + filename.erase(filename.find_last_not_of(" ") + 1); + if (filename.empty()) { + LOG(warning) << "No LUT file passed for pdg " << pdg << ", skipping."; + } + bool success = mSmearer[icfg]->loadTable(pdg, filename.c_str()); + if (!success) { + LOG(fatal) << "Having issue with loading the LUT " << pdg << " " << filename; + } + } + } + } + + if (plotsConfig.doQAplots) { + const AxisSpec axisdNdeta{plotsConfig.nBinsMult, 0.0f, plotsConfig.maxMultRange, Form("dN/d#eta in |#eta| < %f", simConfig.multiplicityEtaRange.value)}; + + histos.add("h1dNdeta", "h2dNdeta", kTH1F, {axisdNdeta}); + histos.add("h2dEventTime", "h2dEventTime", kTH2F, {{2000, -10000, 10000, "computed"}, {2000, -10000, 10000, "generated"}}); + histos.add("h1dEventTimegen", "h1dEventTimegen", kTH1F, {{200, -1000, 1000, "generated"}}); + histos.add("h1dEventTimerec", "h1dEventTimerec", kTH1F, {{2000, -10000, 10000, "computed"}}); + histos.add("h1dEventTimedelta", "h1dEventTimedelta", kTH1F, {{200, -1000, 1000, "generated - computed"}}); + histos.add("h2dEventTimeres", "h2dEventTimeres", kTH2F, {axisdNdeta, {300, 0, 300, "resolution"}}); + listEfficiency.setObject(new THashList); + listEfficiency->Add(new TEfficiency("effEventTime", "effEventTime", plotsConfig.nBinsMult, 0.0f, plotsConfig.maxMultRange)); + + const AxisSpec axisMomentum{static_cast(plotsConfig.nBinsP), 0.0f, +10.0f, "#it{p} (GeV/#it{c})"}; + const AxisSpec axisRigidity{static_cast(plotsConfig.nBinsP), 0.0f, +10.0f, "#it{p} / |#it{z}| (GeV/#it{c})"}; + const AxisSpec axisVelocity{static_cast(plotsConfig.nBinsBeta), 0.0f, +1.1f, "Measured #beta"}; + const AxisSpec axisMomentumSmall{static_cast(plotsConfig.nBinsP), 0.0f, +1.0f, "#it{p} (GeV/#it{c})"}; + const AxisSpec axisTrackLength{static_cast(plotsConfig.nBinsTrackLength), 0.0f, 1000.0f, "Track length (cm)"}; + const AxisSpec axisTrackDeltaLength{static_cast(plotsConfig.nBinsTrackDeltaLength), 0.0f, 30.0f, "Delta Track length (cm)"}; + + histos.add("h2dVelocityVsMomentumInner", "h2dVelocityVsMomentumInner", kTH2F, {axisMomentum, axisVelocity}); + histos.add("h2dVelocityVsRigidityInner", "h2dVelocityVsRigidityInner", kTH2F, {axisRigidity, axisVelocity}); + histos.add("h2dTrackLengthInnerVsPt", "h2dTrackLengthInnerVsPt", kTH2F, {axisMomentum, axisTrackLength}); + histos.add("h2dTrackLengthInnerRecoVsPt", "h2dTrackLengthInnerRecoVsPt", kTH2F, {axisMomentum, axisTrackLength}); + + histos.add("h2dTrackLengthB2vsB0", "h2dDeltaTrackLengthInnerVsPt; for B=0; for B=2 T", kTH2F, {axisTrackLength, axisTrackLength}); + histos.add("h2HitMap", "h2HitMap", kTH2F, {{200, -simConfig.midLength / 2, simConfig.midLength / 2}, {1000, 0, simConfig.midRadius * 2 * M_PI}}); + histos.add("h2HitMapRec", "h2HitMapRec", kTH2F, {{200, -simConfig.midLength / 2, simConfig.midLength / 2}, {1000, 0, simConfig.midRadius * 2 * M_PI}}); + histos.add("h2HitDeltaXY", "h2HitDeltaXY", kTH2F, {{200, -100, 100, "#Delta#it{x} (cm)"}, {200, -100, 100, "#Delta#it{y} (cm)"}}); + + const AxisSpec axisPt{static_cast(plotsConfig.nBinsP), 0.0f, +10.0f, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisPtRes{1000, -1.0f, +1.0f, "(#it{p}_{T}^{gen}-#it{p}_{T}^{rec})/#it{p}_{T}^{gen}"}; + const AxisSpec axisEta{static_cast(plotsConfig.nBinsEta), -2.0f, +2.0f, "#eta"}; + const AxisSpec axisRelativePt{static_cast(plotsConfig.nBinsRelativeEtaPt), 0.0f, +10.0f, "#it{#sigma_{p_{T}}} / #it{p_{T}} (%)"}; + const AxisSpec axisRelativeEta{static_cast(plotsConfig.nBinsRelativeEtaPt), 0.0f, +10.0f, "#it{#sigma_{#eta}} / #it{#eta} (%)"}; + histos.add("h2dEtaVsPt", "h2dTrackLengthInnerRecoVsPt", kTH2F, {axisMomentum, axisEta}); + histos.add("h2dEtaVsP", "h2dTrackLengthInnerRecoVsP", kTH2F, {axisMomentum, axisEta}); + + const AxisSpec axisDelta{plotsConfig.nBinsDeltaCorrectSpecies, plotsConfig.minDeltaRange, plotsConfig.maxDeltaRange, "#Delta#{t} (ps)"}; + const AxisSpec axisNsigmaCorrect{plotsConfig.nBinsNsigmaCorrectSpecies, plotsConfig.minNsigmaRange, plotsConfig.maxNsigmaRange, "N#sigma - True "}; + const AxisSpec axisDeltaWrong{plotsConfig.nBinsDeltaWrongSpecies, plotsConfig.minDeltaRange, plotsConfig.maxDeltaRange, "#Delta - True "}; + const AxisSpec axisX{plotsConfig.doSeparationVsPt.value ? axisPt : axisMomentum}; + // const AxisSpec axisNSigma{axisNsigmaCorrect}; + + const AxisSpec axisTrackTimeRes{plotsConfig.nBinsTimeRes, 0.0f, +3000.0f, "Track time resolution (ps)"}; + const AxisSpec axisTotalTimeRes{plotsConfig.nBinsTimeRes, 0.0f, +3000.0f, "Total time resolution (ps)"}; + + histos.add("h1PtRes", "", kTH2F, {axisMomentum, axisPtRes}); + histos.add("h2EkinVsEloss", "", kTH2F, {{200, 0.0f, 2000.f, "#it{E}_{loss} (MeV)"}, {200, 0.0f, 20000.f, "KE (MeV)"}}); + } + } + + struct MIDEfficiency { + private: + const float layerRadius; + const float layerLength; + const float cellDimensionZ; + const float cellDimensionRPhi; + const float fractionInactive; + const float magField; + + TAxis* axisZ = nullptr; + TAxis* axisRPhi = nullptr; + TAxis* axisInCellZ = nullptr; + TAxis* axisInCellRPhi = nullptr; + + TH2F* hHitMapInCell = nullptr; + TH2F* hHitMapInCellBefore = nullptr; + TH2F* hHitMap = nullptr; + + public: + ~MIDEfficiency() + { + if (0) { + hHitMap->SaveAs(Form("/tmp/%s.png", hHitMap->GetName())); + hHitMapInCell->SaveAs(Form("/tmp/%s.png", hHitMapInCell->GetName())); + hHitMapInCellBefore->SaveAs(Form("/tmp/%s.png", hHitMapInCellBefore->GetName())); + } + + delete axisZ; + delete axisRPhi; + delete axisInCellZ; + delete axisInCellRPhi; + delete hHitMap; + delete hHitMapInCell; + delete hHitMapInCellBefore; + } + + MIDEfficiency(const MIDEfficiency&) = delete; + MIDEfficiency& operator=(const MIDEfficiency&) = delete; + + MIDEfficiency(float r, float l, std::array pDimensions, float fIA, float m) + : layerRadius(r), layerLength(l), cellDimensionZ(pDimensions[0]), cellDimensionRPhi(pDimensions[1]), fractionInactive(fIA), magField(m) + { + // Assuming square cells for simplicity + const float circumference = 2.0f * M_PI * layerRadius; + axisZ = new TAxis(static_cast(layerLength / cellDimensionZ), -layerLength / 2, layerLength); + axisRPhi = new TAxis(static_cast(circumference / cellDimensionRPhi), 0.f, circumference); + + const float inactiveBorderRPhi = cellDimensionRPhi * std::sqrt(fractionInactive) / 2; + const float inactiveBorderZ = cellDimensionZ * std::sqrt(fractionInactive) / 2; + const double arrayRPhi[4] = {-cellDimensionRPhi / 2, -cellDimensionRPhi / 2 + inactiveBorderRPhi, cellDimensionRPhi / 2 - inactiveBorderRPhi, cellDimensionRPhi / 2}; + for (int i = 0; i < 4; i++) { + LOG(info) << "arrayRPhi[" << i << "] = " << arrayRPhi[i]; + } + axisInCellRPhi = new TAxis(3, arrayRPhi); + const double arrayZ[4] = {-cellDimensionZ / 2, -cellDimensionZ / 2 + inactiveBorderZ, cellDimensionZ / 2 - inactiveBorderZ, cellDimensionZ / 2}; + for (int i = 0; i < 4; i++) { + LOG(info) << "arrayZ[" << i << "] = " << arrayZ[i]; + } + axisInCellZ = new TAxis(3, arrayZ); + + hHitMap = new TH2F(Form("hHitMap_R%.0f", layerRadius), "HitMap;z (cm); r#phi (cm)", 1000, -1000, 1000, 1000, -1000, 1000); + hHitMapInCell = new TH2F(Form("hHitMapInCell_R%.0f", layerRadius), "HitMapInCell;z (cm); r#phi (cm)", 1000, -10, 10, 1000, -10, 10); + hHitMapInCellBefore = new TH2F(Form("hHitMapInCellBefore_R%.0f", layerRadius), "HitMapInCell;z (cm); r#phi (cm)", 1000, -10, 10, 1000, -10, 10); + } + + bool isInMIDActiveArea(std::array hitPosition) + { + if (fractionInactive <= 0.0f) { + return true; + } + if (fractionInactive >= 1.0f) { + return false; + } + + // Convert 3D position to cylindrical coordinates for area calculation + const float phi = std::atan2(hitPosition[1], hitPosition[0]); + const float rphi = phi * layerRadius; + const float z = hitPosition[2]; + const float r = std::sqrt(hitPosition[0] * hitPosition[0] + hitPosition[1] * hitPosition[1]); + + // Check if hit is within layer geometric acceptance + if (std::abs(layerRadius - r) > 10.f) { + LOG(debug) << "Hit out of MID layer acceptance: r=" << r << " cm with respect to the layer radius " << layerRadius; + return false; + } + if (std::abs(z) > layerLength / 2.0f) { + LOG(debug) << "Hit out of MID layer acceptance: z=" << z << " cm with respect to the layer length " << layerLength; + return false; + } + + const int cellIndexPhi = axisRPhi->FindBin(rphi); + const int cellIndexZ = axisZ->FindBin(z); + + if (cellIndexPhi <= 0 || cellIndexPhi > axisRPhi->GetNbins() || cellIndexZ <= 0 || cellIndexZ > axisZ->GetNbins()) { + return false; + } + // Calculate local position within the pixel + const float localRPhi = (rphi - axisRPhi->GetBinCenter(cellIndexPhi)); + const float localZ = (z - axisZ->GetBinCenter(cellIndexZ)); + + // The difference between the hit and the pixel position cannot be greater than the size of the pixel + if (std::abs(localRPhi - axisRPhi->GetBinCenter(cellIndexPhi)) > axisRPhi->GetBinWidth(cellIndexPhi)) { + // LOG(warning) << "Local hit difference in phi is bigger than the pixel size"; + } + if (std::abs(localZ - axisZ->GetBinCenter(cellIndexZ)) > axisZ->GetBinWidth(cellIndexZ)) { + // LOG(warning) << "Local hit difference in z is bigger than the pixel size"; + } + hHitMapInCellBefore->Fill(localZ, localRPhi); + switch (axisInCellRPhi->FindBin(localRPhi)) { + case 0: + case 1: + case 3: + case 4: + return false; + } + switch (axisInCellZ->FindBin(localZ)) { + case 0: + case 1: + case 3: + case 4: + return false; + } + hHitMapInCell->Fill(localZ, localRPhi); + hHitMap->Fill(z, rphi); + return true; + } + + /// Check if a track hits the active area (convenience function) + /// \param track the track parameters for automatic hit position calculation + /// \return true if the hit is in the active area + bool isTrackInActiveArea(const o2::track::TrackParCov& track) + { + if (fractionInactive <= 0.f) { + return true; + } + float x, y, z; + if (!track.getXatLabR(layerRadius, x, magField)) { + LOG(debug) << "Could not propagate track to TOF layer at radius " << layerRadius << " cm"; + return false; + } + bool b; + ROOT::Math::PositionVector3D hit = track.getXYZGloAt(x, magField, b); + if (!b) { + LOG(debug) << "Could not get hit position at radius " << layerRadius << " cm"; + return false; + } + hit.GetCoordinates(x, y, z); + return isInMIDActiveArea(std::array{x, y, z}); + } + }; + + bool isInMIDActiveArea(const o2::track::TrackParCov& track) + { + static MIDEfficiency midLayer(simConfig.midRadius, simConfig.midLength, simConfig.midCellDimension, simConfig.midFractionOfInactiveArea, mMagneticField); + return midLayer.isTrackInActiveArea(track); + } + + struct TracksWithTime { + TracksWithTime(int pdgCode, + std::pair midTime, + std::pair trackLengthMID, + std::pair trackLengthInIron, + std::pair, std::array> posInMid, + std::pair momentum, + std::pair pseudorapidity, + float noSmearingPt) + : mPdgCode(pdgCode), + mMIDTime(midTime), + mTrackLengthMID(trackLengthMID), + mTrackLengthInIron(trackLengthInIron), + mPosInMid(posInMid), + mMomentum(momentum), + mPseudorapidity{pseudorapidity}, + mNoSmearingPt{noSmearingPt} {} + + int mPdgCode; + std::pair mMIDTime; // Measured time and expected resolution at the MID [ps] + std::pair mTrackLengthMID; // Measured and generated length at the MID [cm] + std::pair mTrackLengthInIron; // Measured and generated length within absorber [cm] + std::pair, std::array> mPosInMid; + std::pair mMomentum; // Measured momentum and uncertainty on the momentum [GeV/c] + std::pair mPseudorapidity; // Measured pseudorapidity and uncertainty on the pseudorapidity + float mNoSmearingPt; // No smearing pt + }; + + std::vector tracksWithTime; + bool eventTime(const std::vector& tracks, + std::array& tzero) + { + + float sum = 0.; + float sumw = 0.; + + // Todo: check the different mass hypothesis iteratively + for (const auto& track : tracks) { + auto pdgInfo = pdgDatabase->GetParticle(track.mPdgCode); + if (pdgInfo == nullptr) { + continue; + } + const float mass = pdgInfo->Mass(); + const float mass2 = mass * mass; + const float tof = track.mMIDTime.first; // [ps] + if (tof <= 0) { + continue; + } + const float etof = track.mMIDTime.second; // [ps] + const float length = track.mTrackLengthMID.first; // [cm] + float p = track.mMomentum.first; // [GeV/c] + p *= std::abs(pdgInfo->Charge()) / 3.; // Total momentum + const float ep = track.mMomentum.second; // [GeV/c] + const float p2 = p * p; + const float lengthOverC = length * o2::constants::physics::invLightSpeedCm2PS; + const float texp = lengthOverC / p * std::sqrt(mass2 + p2); + // LOG(info) << "TOF: " << tof << " " << etof << " vs " << texp; + const float etexp = lengthOverC * mass2 / p2 / std::sqrt(mass2 + p2) * ep; + const float sigma = std::sqrt(etexp * etexp + etof * etof); + const float deltat = tof - texp; + + const float w = 1. / (sigma * sigma); + + sum += w * deltat; + sumw += w; + } + + static constexpr float kMaxEventTimeResolution = 200.f; + if (sumw <= 0. || tracks.size() <= 1 || std::sqrt(1. / sumw) > kMaxEventTimeResolution) { + tzero[0] = 0.; // [ps] + tzero[1] = kMaxEventTimeResolution; // [ps] + return false; + } + + tzero[0] = sum / sumw; + tzero[1] = std::sqrt(1. / sumw); + return true; + } + + /// returns track time resolution + /// \param pt the transverse momentum of the tarck + /// \param eta the pseudorapidity of the tarck + /// \param trackPtResolution the absolute resolution on pt + /// \param trackEtaResolution the absolute resolution on eta + /// \param mass the mass of the particle + /// \param detRadius the radius of the cylindrical layer + /// \param magneticField the magnetic field (along Z) + double calculateTrackTimeResolutionAdvanced(const float pt, + const float eta, + const float trackPtResolution, + const float trackEtaResolution, + const float mass, + const float detRadius, + const float magneticField) + { + // Compute tracking contribution to timing using the error propagation formula + // Uses light speed in m/ps, magnetic field in T (*0.1 for conversion kGauss -> T) + float a0 = mass * mass; + float a1 = 0.299792458 * (0.1 * magneticField) * (0.01 * o2::constants::physics::LightSpeedCm2NS / 1e+3); + float a2 = (detRadius * 0.01) * (detRadius * 0.01) * (0.299792458) * (0.299792458) * (0.1 * magneticField) * (0.1 * magneticField) / 2.0; + float dtofOndPt = (std::pow(pt, 4) * std::pow(std::cosh(eta), 2) * std::acos(1.0 - a2 / std::pow(pt, 2)) - 2.0 * a2 * std::pow(pt, 2) * (a0 + std::pow(pt * std::cosh(eta), 2)) / std::sqrt(a2 * (2.0 * std::pow(pt, 2) - a2))) / (a1 * std::pow(pt, 3) * std::sqrt(a0 + std::pow(pt * std::cosh(eta), 2))); + float dtofOndEta = std::pow(pt, 2) * std::sinh(eta) * std::cosh(eta) * std::acos(1.0 - a2 / std::pow(pt, 2)) / (a1 * std::sqrt(a0 + std::pow(pt * std::cosh(eta), 2))); + float trackTimeResolution = std::hypot(std::fabs(dtofOndPt) * trackPtResolution, std::fabs(dtofOndEta) * trackEtaResolution); + return trackTimeResolution; + } + + /* + // ============================================================ + // Track length in a B = 0 region + // + // Calculates the distance from the CURRENT track position + // to the cylinder of radius "radius". + // ============================================================ + */ + float computeTrackLengthB0(const o2::track::TrackParCov& track, + const float radius) + { + // Current global position + std::array pos{}; + track.getXYZGlo(pos); + + // Current global momentum + std::array mom{}; + if (!track.getPxPyPzGlo(mom)) { + return -100.f; + } + + const float px = mom[0]; + const float py = mom[1]; + // const float pz = mom[2]; + + const float pt = std::hypot(px, py); + + if (pt <= 0.f) { + return -100.f; + } + + // Unit vector in transverse plane + const float ux = px / pt; + const float uy = py / pt; + + const float b = pos[0] * ux + pos[1] * uy; + const float c = pos[0] * pos[0] + + pos[1] * pos[1] - + radius * radius; + + const float discriminant = b * b - c; + + if (discriminant < 0.f) { + return -100.f; + } + + const float sqrtDisc = std::sqrt(discriminant); + + const float t1 = -b + sqrtDisc; + const float t2 = -b - sqrtDisc; + + // We want the outward/forward intersection + float t = -1.f; + + if (t1 > 0.f && t2 > 0.f) { + t = std::min(t1, t2); + } else if (t1 > 0.f) { + t = t1; + } else if (t2 > 0.f) { + t = t2; + } else { + return -100.f; + } + + return t * std::sqrt(1.f + track.getTgl() * track.getTgl()); + } + + /* + // ============================================================ + // MID piecewise track length + // + // B = +20.0 kG R < R0 + // B = 0.0 kG R0 < R < R1 + // B = -14.3 kG R1 < R < R2 + // + // Returns total track length from current track position to R3. + // ============================================================ + */ + float computeTrackLengthMID(const o2::track::TrackParCov& inputTrack, + float R0, + float R1, + float R2, + float R3, + std::array& posMID, + std::array& deltaMID, + float& deltaposInIron) + { + float B0_m = +20.0f; // kG + float B1_m = 0.0f; // kG + float B2_m = 0.0f; + B2_m = -B0_m * pow(R0, 2) / (pow(R2, 2) - pow(R1, 2)); // kG + float B3_m = 0.0f; // kG + + // Work with a copy + auto track = inputTrack; + + float totalLength = 0.f; + float xAtR = 0.f; + + // ========================================================== + // REGION 1 + // current position -> R0 + // B = +20 kG + // ========================================================== + + const float L0 = + o2::upgrade::computeTrackLength(track, R0, B0_m); + + if (L0 < 0.f) { + return -100.f; + } + + totalLength += L0; + + // Find local x corresponding to global radius R0 + if (!track.getXatLabR(R0, + xAtR, + B0_m, + o2::track::DirOutward)) { + return -100.f; + } + + // Propagate track to R0 + if (!track.propagateTo(xAtR, B0_m)) { + return -100.f; + } + + // ========================================================== + // REGION 2 + // R0 -> R1 + // B = 0 + // ========================================================== + + const float L01 = computeTrackLengthB0(track, R1); + + if (L01 < 0.f) { + return -100.f; + } + + totalLength += L01; + + // Find local x at R1 assuming B = 0 + if (!track.getXatLabR(R1, + xAtR, + B1_m, + o2::track::DirOutward)) { + return -100.f; + } + + // Straight-line propagation to R1 + if (!track.propagateTo(xAtR, B1_m)) { + return -100.f; + } + + // ========================================================== + // REGION 3 (absorber) + // R1 -> R2 + // B = -14.3 kG + // ========================================================== + + std::array pos1{}; + track.getXYZGlo(pos1); + + float L12 = + o2::upgrade::computeTrackLength(track, R2, B2_m); + + if (L12 < 0.f) { + return -100.f; + } + totalLength += L12; + deltaposInIron = L12; + // Find local x corresponding to global radius R2 + if (!track.getXatLabR(R2, + xAtR, + B2_m, + o2::track::DirOutward)) { + return -100.f; + } + + // Propagate track to R2 + if (!track.propagateTo(xAtR, B2_m)) { + return -100.f; + } + + // ========================================================== + // REGION 2 + // R2 -> R3 + // B = 0 + // ========================================================== + + const float L23 = computeTrackLengthB0(track, R3); + + if (L23 < 0.f) { + return -100.f; + } + + totalLength += L23; + + // Find local x corresponding to global radius R3 + if (!track.getXatLabR(R3, + xAtR, + B3_m, + o2::track::DirOutward)) { + return -100.f; + } + + // Propagate track to R3 + if (!track.propagateTo(xAtR, B3_m)) { + return -100.f; + } + + // ========================================================== + // GLOBAL POSITION AT R3 + // ========================================================== + + std::array pos3{}; + track.getXYZGlo(pos3); + + posMID[0] = pos3[0]; + posMID[1] = pos3[1]; + posMID[2] = pos3[2]; + + // Displacement projections + deltaMID[0] = posMID[0] - pos1[0]; + deltaMID[1] = posMID[1] - pos1[1]; + deltaMID[2] = posMID[2] - pos1[2]; + + return totalLength; + } + // Multiple Coulomb scattering angle in iron + // + // momentum : particle momentum [GeV/c] + // thickness : iron thickness [cm] + // + // returns theta0 [rad] + + float moliereThetaIron(float momentum, float mass, float thickness) + { + float X0 = 1.757f; // radiation length of iron [cm] + // constexpr float mMuon = 0.105658f; // muon mass [GeV/c^2] + + if (momentum <= 0.f || thickness <= 0.f) { + return 0.f; + } + + // beta = p/E + const float energy = std::sqrt(momentum * momentum + + mass * mass); + + const float beta = momentum / energy; + + // Thickness in radiation lengths + const float xOverX0 = thickness / X0; + + // Highland approximation + const float theta0 = (0.0136f / (beta * momentum)) * sqrt(xOverX0) * (1.f + 0.038f * log(xOverX0)); + + return theta0; + } + float fPi(double p) + { + return p > 1. ? 1. - std::exp(-(p - 1.) / 1.5) : 0.; + } + float fMu(double p) + { + return p > 0.7 ? (1. - std::exp(-(p - 0.7) / 0.8)) * std::exp(-p / 3.) : 0.; + } + float refPi(double p) + { + if (p <= 1.) + return 0.; + float f3 = fPi(3.); + float mu10 = simConfig.absFakeAt3 * (1. - simConfig.absPionFractionAt3) * fMu(10.) / fMu(3.); + float slope = ((simConfig.absFakeAt10 - mu10) / (simConfig.absFakeAt3 * simConfig.absPionFractionAt3) / (fPi(10.) / f3) - 1.) / 7.; + return std::max(0., simConfig.absFakeAt3 * simConfig.absPionFractionAt3) * fPi(p) / f3 * (1. + slope * (p - 3.)); + } + float refMu(double p) + { + return simConfig.absFakeAt3 * (1. - simConfig.absPionFractionAt3) * fMu(p) / fMu(3.); + } + float decayAir(double p, double airCm) + { + if (p <= 0.) + return 0.; + return -std::expm1(-airCm * 0.13957039 / (p * 780.45)); // assume pion mass, ctau=780.45 cm + } + float survivalAir(double p, double airCm) + { + return 1. - decayAir(p, airCm); + } + // Range proxy: kinetic energy minus mean ionization loss in iron. + // Smoothed only to represent missing fluctuations; NOT a Geant4 range calculation. + float rangeTurnOn(double p, double ironCm, double mass) + { + if (p <= 0.) + return 0.; + float kinetic = std::sqrt(p * p + mass * mass) - mass; + float deficit = kinetic - (simConfig.absDensity * simConfig.absDeDx / 1000.) * ironCm; + float width = 0.10; // GeV, phenomenological + return 1. / (1. + std::exp(-std::max(-60., std::min(60., deficit / width)))); + } + float etaShape(double eta) + { + return std::exp(-0.5 * eta * eta / (simConfig.absSigmaEta * simConfig.absSigmaEta)); + } + float pion(float p, float eta, float airCm, float ironCm) + { + if (p <= 0.) + return 0.; + const float c = std::cosh(eta); + const float survivalRatio = survivalAir(p, airCm * c) / survivalAir(p, simConfig.absRadius1 * c); + const float hadronRatio = std::exp(-(ironCm - (simConfig.absRadius2 - simConfig.absRadius1)) * c / simConfig.absLambdaPi); + // Ratio to reference range at the SAME eta; reference shape is phenomenological. + const float rangeRatio = rangeTurnOn(p, ironCm * c, 0.13957039) / rangeTurnOn(p, (simConfig.absRadius2 - simConfig.absRadius1) * c, 0.13957039); // assume pion mass + return std::max(0., refPi(p) * etaShape(eta) * survivalRatio * hadronRatio * rangeRatio); + } + float muon(float p, float eta, float airCm, float ironCm) + { + if (p <= 0.) + return 0.; + const float c = std::cosh(eta); + const float d0 = decayAir(p, simConfig.absRadius1 * c); + const float decayRatio = d0 > 0. ? decayAir(p, airCm * c) / d0 : 0.; + // Effective secondary-muon momentum proxy: 0.8 * pion momentum. + // Secondary muons born in iron are NOT modeled; this is a phenomenological air-decay term. + const float pMu = 0.8 * p; + const float rangeRatio = rangeTurnOn(pMu, ironCm * c, 0.1056583755) / + rangeTurnOn(pMu, (simConfig.absRadius2 - simConfig.absRadius1) * c, 0.1056583755); + return std::max(0., refMu(p) * etaShape(eta) * decayRatio * rangeRatio); + } + float total(float p, float eta, float airCm, float ironCm) + { + return std::min(1., pion(p, eta, airCm, ironCm) + muon(p, eta, airCm, ironCm)); + } + float meanEta(float p, float airCm, float ironCm) + { + const int n = 1000; + float s = 0.; + for (int i = 0; i < n; ++i) { + float eta = -simConfig.absEtaMax + (i + .5) * 2. * simConfig.absEtaMax / n; + s += total(p, eta, airCm, ironCm); + } + return s / n; + } + + void process(soa::Join::iterator const& collision, + soa::Join const& tracks, + aod::McParticles const&, + aod::McCollisions const&) + { + o2::dataformats::VertexBase pvVtx({collision.posX(), collision.posY(), collision.posZ()}, + {collision.covXX(), collision.covXY(), collision.covYY(), + collision.covXZ(), collision.covYZ(), collision.covZZ()}); + + std::array mcPvCov = {0.}; + o2::dataformats::VertexBase mcPvVtx({0.0f, 0.0f, 0.0f}, mcPvCov); + const float eventCollisionTimePS = (simConfig.considerEventTime.value ? collision.collisionTime() * 1e3 : 0.f); // convert ns to ps + if (collision.has_mcCollision()) { + auto mcCollision = collision.mcCollision(); + mcPvVtx.setX(mcCollision.posX()); + mcPvVtx.setY(mcCollision.posY()); + mcPvVtx.setZ(mcCollision.posZ()); + } // else remains untreated for now + + // First we compute the number of charged particles in the event if LUTs are loaded + float dNdEta = 0.f; + for (const auto& track : tracks) { + if (!track.has_mcParticle()) + continue; + auto mcParticle = track.mcParticle(); + if (std::abs(mcParticle.eta()) > simConfig.multiplicityEtaRange) { + continue; + } + if (mcParticle.has_daughters()) { + continue; + } + const auto& pdgInfo = pdgDatabase->GetParticle(mcParticle.pdgCode()); + if (!pdgInfo) { + // LOG(warning) << "PDG code " << mcParticle.pdgCode() << " not found in the database"; + continue; + } + if (pdgInfo->Charge() == 0) { + continue; + } + dNdEta += 1.f; + } + if (plotsConfig.doQAplots) { + histos.fill(HIST("h1dNdeta"), dNdEta); + } + + tracksWithTime.clear(); // clear the vector of tracks with time to prepare the cache for the next event + tracksWithTime.reserve(tracks.size()); + // First loop to generate the arrival time of the particles to the MID layers + for (const auto& track : tracks) { + // first step: find precise arrival time (if any) + // --- convert track into perfect track + if (!track.has_mcParticle()) { // should always be OK but check please + upgradeMidMC(-999.f, -999.f); + continue; + } else { + LOG(debug) << "Track without mcParticle found!"; + } + const auto& mcParticle = track.mcParticle(); + float true_pt = mcParticle.pt(); + o2::track::TrackParCov o2track = o2::upgrade::convertMCParticleToO2Track(mcParticle, pdgDatabase); + // get mass to calculate velocity + auto pdgInfo = pdgDatabase->GetParticle(mcParticle.pdgCode()); + if (pdgInfo == nullptr) { + LOG(error) << "PDG code " << mcParticle.pdgCode() << " not found in the database"; + upgradeMidMC(-999.f, -999.f); + continue; + } + + float xPv = -100.f; + static constexpr float kTrkXThreshold = -99.f; // Threshold to consider a good propagation of the track + if (o2track.propagateToDCA(mcPvVtx, mMagneticField)) { + xPv = o2track.getX(); + } + float trackLengthMID = -1; + float trackLengthMIDB2 = -1; + std::array posMid{-999., -999., -999.}; + std::array delaPosMid{-999., -999., -999.}; + float trackLengthInIron = -999.; + if (xPv > kTrkXThreshold) { + trackLengthMIDB2 = o2::upgrade::computeTrackLength(o2track, simConfig.magRadiusC, mMagneticField); + trackLengthMID = computeTrackLengthMID(o2track, simConfig.magRadiusC, simConfig.absRadius1, simConfig.absRadius2, simConfig.midRadius, posMid, delaPosMid, trackLengthInIron); + // thetamolInIron = moliereThetaIron(o2track.getP(), pdgInfo->Mass(), trackLengthInIron); + } + if (plotsConfig.doQAplots) { + histos.fill(HIST("h2dTrackLengthB2vsB0"), trackLengthMIDB2, trackLengthMID); + } + + // Check if the track hit a sensitive area of the MID + const bool activeMID = isInMIDActiveArea(o2track); + if (!activeMID) { + trackLengthMID = -999.f; + } else { + if (plotsConfig.doQAplots) { + float phi_tmp = std::atan2(posMid[1], posMid[0]); + if (phi_tmp < 0.f) { + phi_tmp += 2.f * M_PI; + } + histos.fill(HIST("h2HitMap"), posMid[2], simConfig.midRadius * phi_tmp); + } + } + + const float v = o2::upgrade::computeParticleVelocity(o2track.getP(), pdgInfo->Mass()); + const float expectedTimeMID = trackLengthMID > 0 ? trackLengthMID / v + eventCollisionTimePS : -999.f; // arrival time to the MID in ps + + upgradeMidMC(expectedTimeMID, trackLengthMID); + + // Smear with expected resolutions + const float measuredTimeMID = pRandomNumberGenerator.Gaus(expectedTimeMID, simConfig.midTimeReso); + + // Now we calculate the expected arrival time following certain mass hypotheses + // and the (imperfect!) reconstructed track parametrizations + float trackLengthRecoMID = -1; + auto recoTrack = getTrackParCov(track); + xPv = -100.f; + std::array posRecMid{-999., -999., -999.}; + std::array deltaPosRecMid{-999., -999., -999.}; + float trackLengthRecoInIron = -1.; + trackLengthRecoInIron = -1.; + trackLengthRecoMID = -1.; + if (recoTrack.propagateToDCA(pvVtx, mMagneticField)) { + xPv = recoTrack.getX(); + } + if (xPv > kTrkXThreshold) { + trackLengthRecoMID = computeTrackLengthMID(recoTrack, simConfig.magRadiusC, simConfig.absRadius1, simConfig.absRadius2, simConfig.midRadius, posRecMid, deltaPosRecMid, trackLengthRecoInIron); + // mimic scattering in iron + std::array mom{-999., -999., -999.}; + recoTrack.getPxPyPzGlo(mom); + for (int jj = 0; jj < 3; ++jj) { + float posRectmp = 0.0f; + float molieretmp = 0.0f; + molieretmp = moliereThetaIron(mom[jj], pdgInfo->Mass(), deltaPosRecMid[jj]); + posRectmp = pRandomNumberGenerator.Gaus(posRecMid[jj], molieretmp); + posRecMid[jj] = posRectmp; + } + if (plotsConfig.doQAplots) { + float phi_tmp = std::atan2(posRecMid[1], posRecMid[0]); + if (phi_tmp < 0.f) { + phi_tmp += 2.f * M_PI; + } + histos.fill(HIST("h2HitMapRec"), posRecMid[2], simConfig.midRadius * phi_tmp); + histos.fill(HIST("h2HitDeltaXY"), posRecMid[0] - posMid[0], posRecMid[1] - posMid[1]); + histos.fill(HIST("h1PtRes"), true_pt, (true_pt - recoTrack.getPt()) / true_pt); + } + } + // cache the track info needed for the event time calculation + // Reuse or emplace a new object in the vector + tracksWithTime.emplace_back(TracksWithTime{mcParticle.pdgCode(), + {measuredTimeMID, expectedTimeMID}, + {trackLengthRecoMID, trackLengthMID}, + {trackLengthRecoInIron, trackLengthInIron}, + {posRecMid, posMid}, + {recoTrack.getP(), recoTrack.getSigma1Pt2()}, + {recoTrack.getEta(), recoTrack.getSigmaTgl2()}, + o2track.getPt()}); + } + // Now we compute the event time for the tracks + + std::array tzero = {0.f, 0.f}; + bool etStatus = false; + if (simConfig.considerEventTime.value) { + etStatus = eventTime(tracksWithTime, tzero); + if (!etStatus) { + LOG(debug) << "Event time calculation failed with " << tracksWithTime.size() << " tracks with time and " << dNdEta << " charged particles"; + } + } + + if (plotsConfig.doQAplots) { + histos.fill(HIST("h2dEventTime"), tzero[0], eventCollisionTimePS); + histos.fill(HIST("h1dEventTimegen"), eventCollisionTimePS); + histos.fill(HIST("h1dEventTimerec"), tzero[0]); + histos.fill(HIST("h2dEventTimeres"), dNdEta, tzero[1]); + if (etStatus) { + histos.fill(HIST("h1dEventTimedelta"), eventCollisionTimePS - tzero[0]); + } + dynamic_cast(listEfficiency->At(0))->Fill(etStatus, dNdEta); + } + + // Then we do a second loop to compute the measured quantities with the measured event time + int trackWithTimeIndex = 0; + for (const auto& track : tracks) { + if (!track.has_mcParticle()) { // should always be OK but check please + continue; + } + const auto& mcParticle = track.mcParticle(); + + const auto& trkWithTime = tracksWithTime[trackWithTimeIndex++]; + const float trackLengthRecoMID = trkWithTime.mTrackLengthMID.first; + const float trackLengthMID = trkWithTime.mTrackLengthMID.second; + const float measuredTimeMID = trkWithTime.mMIDTime.first; + const float expecTimeMID = trkWithTime.mMIDTime.second; + const float momentum = trkWithTime.mMomentum.first; + const float pseudorapidity = trkWithTime.mPseudorapidity.first; + const float noSmearingPt = trkWithTime.mNoSmearingPt; + std::array expectedPosMid = trkWithTime.mPosInMid.second; + std::array measuredPosMid = trkWithTime.mPosInMid.first; + float trackLengthInIronRec = trkWithTime.mTrackLengthInIron.first; + bool isMuCandidate = false; + if (abs(pseudorapidity) < simConfig.absEtaMax) { + if (trackLengthInIronRec > 0) { + float kineticEnergy = std::sqrt(pow(momentum, 2) + pow(0.1057, 2)) - 0.1057; + float energyloss = trackLengthInIronRec * simConfig.absDeDx * simConfig.absDensity / 1000.; + if (abs(trkWithTime.mPdgCode) == 13) { + if ((kineticEnergy - energyloss) > 0.1) { + if (plotsConfig.doQAplots) { + histos.fill(HIST("h2EkinVsEloss"), energyloss * 1000., kineticEnergy * 1000.); + } + isMuCandidate = true; + } + } else if (abs(trkWithTime.mPdgCode) == 11) { + isMuCandidate = false; + } else { // mostly hadrons, parametrization from simulations using pions, GEANT4+BDT + float eff_rndm = pRandomNumberGenerator.Uniform(0., 1.); + float eff_had = total(momentum, pseudorapidity, trackLengthRecoMID - trackLengthInIronRec, trackLengthInIronRec); + if (eff_rndm < eff_had && (kineticEnergy - energyloss) > 0.1) { + if (plotsConfig.doQAplots) { + histos.fill(HIST("h2EkinVsEloss"), energyloss * 1000., kineticEnergy * 1000.); + } + isMuCandidate = true; + } + } + } + } + auto truePdgInfo = pdgDatabase->GetParticle(mcParticle.pdgCode()); + float rigidity = momentum; // fallback to momentum if charge unknown + + // Use MC truth charge for rigidity calculation + if (truePdgInfo) { + const float trueCharge = std::abs(truePdgInfo->Charge()) / 3.0f; + if (trueCharge > 0) { + rigidity = momentum / trueCharge; + } + } + + if (plotsConfig.doQAplots) { + // unit conversion: length in cm, time in ps + const float midBeta = (trackLengthRecoMID / measuredTimeMID) / o2::constants::physics::LightSpeedCm2PS; + if (trackLengthRecoMID > 0) { + histos.fill(HIST("h2dVelocityVsMomentumInner"), momentum, midBeta); + histos.fill(HIST("h2dVelocityVsRigidityInner"), rigidity, midBeta); + histos.fill(HIST("h2dTrackLengthInnerVsPt"), noSmearingPt, trackLengthMID); + histos.fill(HIST("h2dTrackLengthInnerRecoVsPt"), noSmearingPt, trackLengthRecoMID); + histos.fill(HIST("h2dEtaVsPt"), noSmearingPt, pseudorapidity); + histos.fill(HIST("h2dEtaVsP"), momentum, pseudorapidity); + } + } + std::vector measuredPosMidVec(measuredPosMid.begin(), measuredPosMid.end()); + upgradeMid(tzero[0], tzero[1], measuredPosMidVec, measuredTimeMID, trackLengthRecoMID, isMuCandidate); + + std::vector expectedPosMidVec(expectedPosMid.begin(), expectedPosMid.end()); + upgradeMidExpected(expectedPosMidVec, expecTimeMID, trackLengthMID); + } + + if (trackWithTimeIndex != tracks.size()) { + LOG(fatal) << "Track with time index " << trackWithTimeIndex << " does not match the number of tracks " << tracks.size(); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{adaptAnalysisTask(cfgc)}; } diff --git a/ALICE3/TableProducer/OTF/onTheFlyTofPid.cxx b/ALICE3/TableProducer/OTF/onTheFlyTofPid.cxx index e50656494fc..f1ba29ab1d4 100644 --- a/ALICE3/TableProducer/OTF/onTheFlyTofPid.cxx +++ b/ALICE3/TableProducer/OTF/onTheFlyTofPid.cxx @@ -30,6 +30,7 @@ #include "ALICE3/Core/TrackUtilities.h" #include "ALICE3/DataModel/OTFCollision.h" #include "ALICE3/DataModel/OTFTOF.h" +#include "ALICE3/DataModel/tracksAlice3.h" #include "Common/Core/trackUtilities.h" #include @@ -77,6 +78,7 @@ struct OnTheFlyTofPid { Produces upgradeTof; Produces upgradeTofExpectedTime; Produces upgradeTofShortLived; + Produces upgradeTofShortLivedSignal; // necessary for particle charges Service pdgDatabase{}; @@ -103,6 +105,7 @@ struct OnTheFlyTofPid { Configurable multiplicityEtaRange{"multiplicityEtaRange", 0.800000012, "eta range to compute the multiplicity"}; Configurable flagIncludeTrackTimeRes{"flagIncludeTrackTimeRes", true, "flag to include or exclude track time resolution"}; Configurable flagTOFLoadDelphesLUTs{"flagTOFLoadDelphesLUTs", false, "flag to load Delphes LUTs for tracking correction (use recoTrack parameters if false)"}; + Configurable shortLivedRadiusTolerance{"shortLivedRadiusTolerance", 0.1f, "tolerance (cm) when checking if a TOF layer is within the innermost and outermost track update of short-lived particles"}; } simConfig; struct : ConfigurableGroup { @@ -166,8 +169,8 @@ struct OnTheFlyTofPid { std::array, NParticles>, NParticles> h2dOuterDeltaTrue; struct ParticleInfo { - std::string_view texName{}; - std::string_view name{}; + std::string_view texName; + std::string_view name; ParticleId type{}; int pdgCode{}; double mass{}; @@ -211,6 +214,39 @@ struct OnTheFlyTofPid { particleHe, particleAl}; + template + void fillShortLivedTables(const TTrack& track, + std::array nSigmaInnerTOF, + std::array nSigmaOuterTOF, + std::array expectedTimeInnerTOF, + std::array expectedTimeOuterTOF, + const std::pair& trackLengthInnerTOF, + const std::pair& trackLengthOuterTOF) + { + const auto isWithinTrackUpdates = [&](const float tofRadius) { + return (track.trackIU() - simConfig.shortLivedRadiusTolerance <= tofRadius) && (tofRadius <= track.trackOU() + simConfig.shortLivedRadiusTolerance); + }; + + const bool hasInnerTOF = isWithinTrackUpdates(simConfig.innerTOFRadius) && trackLengthInnerTOF.first > 0 && trackLengthInnerTOF.second > 0; + const bool hasOuterTOF = isWithinTrackUpdates(simConfig.outerTOFRadius) && trackLengthOuterTOF.first > 0 && trackLengthOuterTOF.second > 0; + static constexpr float DummyValue = -999.f; + for (const int ii : {Sp, Sm, Xi, Om}) { + if (!hasInnerTOF) { + nSigmaInnerTOF[ii] = DummyValue; + expectedTimeInnerTOF[ii] = DummyValue; + } + if (!hasOuterTOF) { + nSigmaOuterTOF[ii] = DummyValue; + expectedTimeOuterTOF[ii] = DummyValue; + } + } + upgradeTofShortLived(nSigmaInnerTOF[Sp], nSigmaInnerTOF[Sm], nSigmaInnerTOF[Xi], nSigmaInnerTOF[Om], + nSigmaOuterTOF[Sp], nSigmaOuterTOF[Sm], nSigmaOuterTOF[Xi], nSigmaOuterTOF[Om], + expectedTimeInnerTOF[Sp], expectedTimeInnerTOF[Sm], expectedTimeInnerTOF[Xi], expectedTimeInnerTOF[Om], + expectedTimeOuterTOF[Sp], expectedTimeOuterTOF[Sm], expectedTimeOuterTOF[Xi], expectedTimeOuterTOF[Om]); + upgradeTofShortLivedSignal(hasInnerTOF, hasOuterTOF); + } + bool doQaForParticle(const int pdgCode) { return std::find(plotsConfig.particlesForQa.value.begin(), plotsConfig.particlesForQa.value.end(), std::abs(pdgCode)) != plotsConfig.particlesForQa.value.end(); @@ -347,7 +383,7 @@ struct OnTheFlyTofPid { if (!doQaForParticle(Particles[iHyp].pdgCode)) { continue; } - const auto& pNameHypo = Particles[iHyp].texName; + const auto& pNameHypo = Particles[iHyp].name; const std::string nameTitleInner = std::format("h2dInnerNsigmaTrue{}Vs{}", pNameTrue, pNameHypo); const std::string nameTitleOuter = std::format("h2dOuterNsigmaTrue{}Vs{}", pNameTrue, pNameHypo); @@ -652,7 +688,7 @@ struct OnTheFlyTofPid { } void process(soa::Join::iterator const& collision, - soa::Join const& tracks, + soa::Join const& tracks, aod::McParticles const&, aod::McCollisions const&) { @@ -971,10 +1007,8 @@ struct OnTheFlyTofPid { measuredTimeOuterTOF, trackLengthRecoOuterTOF); upgradeTofExpectedTime(expectedTimeInnerTOF[El], expectedTimeInnerTOF[Mu], expectedTimeInnerTOF[Pi], expectedTimeInnerTOF[Ka], expectedTimeInnerTOF[Pr], expectedTimeInnerTOF[De], expectedTimeInnerTOF[Tr], expectedTimeInnerTOF[He], expectedTimeInnerTOF[Al], expectedTimeOuterTOF[El], expectedTimeOuterTOF[Mu], expectedTimeOuterTOF[Pi], expectedTimeOuterTOF[Ka], expectedTimeOuterTOF[Pr], expectedTimeOuterTOF[De], expectedTimeOuterTOF[Tr], expectedTimeOuterTOF[He], expectedTimeOuterTOF[Al]); - upgradeTofShortLived(nSigmaInnerTOF[Sp], nSigmaInnerTOF[Sm], nSigmaInnerTOF[Xi], nSigmaInnerTOF[Om], - nSigmaOuterTOF[Sp], nSigmaOuterTOF[Sm], nSigmaOuterTOF[Xi], nSigmaOuterTOF[Om], - expectedTimeInnerTOF[Sp], expectedTimeInnerTOF[Sm], expectedTimeInnerTOF[Xi], expectedTimeInnerTOF[Om], - expectedTimeOuterTOF[Sp], expectedTimeOuterTOF[Sm], expectedTimeOuterTOF[Xi], expectedTimeOuterTOF[Om]); + fillShortLivedTables(track, nSigmaInnerTOF, nSigmaOuterTOF, expectedTimeInnerTOF, expectedTimeOuterTOF, + trkWithTime.mTrackLengthInnerTOF, trkWithTime.mTrackLengthOuterTOF); } if (trackWithTimeIndex != tracks.size()) { diff --git a/ALICE3/TableProducer/OTF/onTheFlyTracker.cxx b/ALICE3/TableProducer/OTF/onTheFlyTracker.cxx index ca0bc59230d..d958418952d 100644 --- a/ALICE3/TableProducer/OTF/onTheFlyTracker.cxx +++ b/ALICE3/TableProducer/OTF/onTheFlyTracker.cxx @@ -98,7 +98,6 @@ #include #include #include -#include #include #include @@ -259,7 +258,7 @@ struct OnTheFlyTracker { // V0 names for filling histograms static constexpr int NtypesV0 = 3; - static constexpr std::string_view NameV0s[NtypesV0] = {"K0", "Lambda", "AntiLambda"}; + static constexpr std::array NameV0s = {"K0", "Lambda", "AntiLambda"}; // Class to hold the track information for the O2 vertexing class TrackAlice3 : public o2::track::TrackParCov @@ -299,6 +298,8 @@ struct OnTheFlyTracker { int nSiliconHits = 0; ///< number of silicon hits int nTPCHits = 0; ///< number of TPC hits TrackType trackType = TrackType::kNone; + float trackIU = -1.f; ///< radius of the innermost hit (-1 if not available) + float trackOU = -1.f; ///< radius of the outermost hit (-1 if not available) }; // Helper struct to pass cascade information @@ -1471,7 +1472,7 @@ struct OnTheFlyTracker { continue; } for (int indexDetector = 0; indexDetector < mGeoContainer.getNumberOfConfigurations(); indexDetector++) { - std::string path = Form("V0Building_Configuration_%i/%s/", indexDetector, NameV0s[indexV0].data()); + std::string path = Form("V0Building_Configuration_%i/%s/", indexDetector, NameV0s[indexV0]); fillHist(path + "hGen", v0DecayRadius2D, mcParticle.pt()); fillHist(path + "hGenNegDaughterFromV0", v0DecayRadius2D, v0DecayProducts[0].Pt()); fillHist(path + "hGenPosDaughterFromV0", v0DecayRadius2D, v0DecayProducts[1].Pt()); @@ -1558,21 +1559,21 @@ struct OnTheFlyTracker { fillHist(Form("V0Building_Configuration_%i/hV0Building", icfg), 1.0f); for (size_t indexV0 = 0; indexV0 < V0PDGs.size(); indexV0++) { if (mcParticle.pdgCode() == V0PDGs[indexV0]) { - fillHist(Form("V0Building_Configuration_%i/%s/hReco", icfg, NameV0s[indexV0].data()), v0DecayRadius2D, mcParticle.pt()); + fillHist(Form("V0Building_Configuration_%i/%s/hReco", icfg, NameV0s[indexV0]), v0DecayRadius2D, mcParticle.pt()); } } } if (isV0Reco[0]) { for (size_t indexV0 = 0; indexV0 < V0PDGs.size(); indexV0++) { if (mcParticle.pdgCode() == V0PDGs[indexV0]) { - fillHist(Form("V0Building_Configuration_%i/%s/hRecoNegDaughterFromV0", icfg, NameV0s[indexV0].data()), v0DecayRadius2D, v0DecayProducts[0].Pt()); + fillHist(Form("V0Building_Configuration_%i/%s/hRecoNegDaughterFromV0", icfg, NameV0s[indexV0]), v0DecayRadius2D, v0DecayProducts[0].Pt()); } } } if (isV0Reco[1]) { for (size_t indexV0 = 0; indexV0 < V0PDGs.size(); indexV0++) { if (mcParticle.pdgCode() == V0PDGs[indexV0]) { - fillHist(Form("V0Building_Configuration_%i/%s/hRecoPosDaughterFromV0", icfg, NameV0s[indexV0].data()), v0DecayRadius2D, v0DecayProducts[1].Pt()); + fillHist(Form("V0Building_Configuration_%i/%s/hRecoPosDaughterFromV0", icfg, NameV0s[indexV0]), v0DecayRadius2D, v0DecayProducts[1].Pt()); } } } @@ -1815,7 +1816,7 @@ struct OnTheFlyTracker { trackParCov.getSigmaTgl2(), trackParCov.getSigma1PtY(), trackParCov.getSigma1PtZ(), trackParCov.getSigma1PtSnp(), trackParCov.getSigma1PtTgl(), trackParCov.getSigma1Pt2()); tableMcTrackLabels(trackParCov.mcLabel, 0); - tableTracksExtraA3(trackParCov.nSiliconHits, trackParCov.nTPCHits, trackParCov.trackType, trackParCov.isPVContributor); + tableTracksExtraA3(trackParCov.nSiliconHits, trackParCov.nTPCHits, trackParCov.trackType, trackParCov.isPVContributor, -1, -1, -1, -1); // populate extra tables if required to do so if (populateTracksExtra) { @@ -1828,7 +1829,7 @@ struct OnTheFlyTracker { tableTrackSelection(static_cast(0), false, false, false, false, false, false); tableTrackSelectionExtension(false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false); } - tableTracksAlice3(true); + tableTracksAlice3(true, trackParCov.trackIU, trackParCov.trackOU); } } @@ -1949,12 +1950,14 @@ struct OnTheFlyTracker { bool reconstructed = true; int nTrkHits = 0; + std::array hitRadii = {-1.f, -1.f}; // {innermost, outermost} if (enablePrimarySmearing) { if (fastPrimaryTrackerSettings.fastTrackPrimaries && longLivedToBeHandled) { o2::track::TrackParCov perfectTrackParCov = o2::upgrade::convertMCParticleToO2Track(mcParticle, pdgDB); perfectTrackParCov.setPID(pdgCodeToPID(mcParticle.pdgCode())); computeBremsstrahlungLoss(mcParticle, perfectTrackParCov); nTrkHits = fastTracker[icfg]->fastTrack(perfectTrackParCov, trackParCov, dNdEta); + hitRadii = {fastTracker[icfg]->getTrackIU(), fastTracker[icfg]->getTrackOU()}; if (nTrkHits < fastPrimaryTrackerSettings.minSiliconHits) { reconstructed = false; } @@ -1965,6 +1968,7 @@ struct OnTheFlyTracker { const std::array decayVtx = decayer.generateDecayVertex(mcParticle, pdgDB); const float decayRadius2D = std::hypot(decayVtx[0], decayVtx[1]); nTrkHits = fastTracker[icfg]->fastTrack(perfectTrackParCov, trackParCov, dNdEta, decayRadius2D); + hitRadii = {fastTracker[icfg]->getTrackIU(), fastTracker[icfg]->getTrackOU()}; if (nTrkHits < fastPrimaryTrackerSettings.minSiliconHits) { reconstructed = false; } @@ -2033,8 +2037,12 @@ struct OnTheFlyTracker { bool isPVContributor = trackType == TrackType::kRecoPrimary; if (reconstructed) { recoPrimaries.push_back(TrackAlice3{trackParCov, mcParticle.globalIndex(), trackTime, TimeResolutionUs, isDecayDaughter, isPVContributor, false, 0, nTrkHits, trackType}); + recoPrimaries.back().trackIU = hitRadii[0]; + recoPrimaries.back().trackOU = hitRadii[1]; } else { ghostPrimaries.push_back(TrackAlice3{trackParCov, mcParticle.globalIndex(), trackTime, TimeResolutionUs, isDecayDaughter, isPVContributor, false, 0, nTrkHits, trackType}); + ghostPrimaries.back().trackIU = hitRadii[0]; + ghostPrimaries.back().trackOU = hitRadii[1]; } } @@ -2176,6 +2184,7 @@ struct OnTheFlyTracker { bool reconstructed = false; int nTrkHits = 0; + std::array hitRadii = {-1.f, -1.f}; // {innermost, outermost} const bool isSecondary = !otfParticle.isPrimary() && otfParticle.checkBit(o2::upgrade::DecayerBits::ProducedByDecayer) && otfParticle.isAlive(); if (enablePrimarySmearing && longLivedToBeHandled && otfParticle.isPrimary()) { o2::upgrade::convertMCParticleToO2Track(mcParticle, trackParCov, pdgDB); @@ -2186,6 +2195,7 @@ struct OnTheFlyTracker { perfectTrackParCov.setPID(pdgCodeToPID(mcParticle.pdgCode())); computeBremsstrahlungLoss(mcParticle, perfectTrackParCov); nTrkHits = fastTracker[icfg]->fastTrack(perfectTrackParCov, trackParCov, dNdEta, mcParticle.decayRadius()); + hitRadii = {fastTracker[icfg]->getTrackIU(), fastTracker[icfg]->getTrackOU()}; getHist(histPath + "h2dGenShortLivedParticleRadius")->Fill(mcParticle.decayRadius(), perfectTrackParCov.getPt()); getHist(histPath + "h2dGenRadiusIniVsDecay")->Fill(std::hypot(perfectTrackParCov.getX(), perfectTrackParCov.getY()), mcParticle.decayRadius()); if (nTrkHits < fastPrimaryTrackerSettings.minSiliconHits) { @@ -2203,6 +2213,7 @@ struct OnTheFlyTracker { computeBremsstrahlungLoss(mcParticle, perfectTrackParCov); perfectTrackParCov.setPID(pdgCodeToPID(mcParticle.pdgCode())); nTrkHits = fastTracker[icfg]->fastTrack(perfectTrackParCov, trackParCov, dNdEta); + hitRadii = {fastTracker[icfg]->getTrackIU(), fastTracker[icfg]->getTrackOU()}; reconstructed = nTrkHits >= fastTrackerSettings.minSiliconHits; } @@ -2260,9 +2271,13 @@ struct OnTheFlyTracker { if (reconstructed) { tracksAlice3.push_back(TrackAlice3{trackParCov, mcParticle.globalIndex(), time, TimeResolutionUs, isSecondary, true, false, 0, nTrkHits, kRecoPrimary}); + tracksAlice3.back().trackIU = hitRadii[0]; + tracksAlice3.back().trackOU = hitRadii[1]; getHist(histPath + "hPtReconstructedPr")->Fill(trackParCov.getPt()); } else { ghostTracksAlice3.push_back(TrackAlice3{trackParCov, mcParticle.globalIndex(), time, TimeResolutionUs, isSecondary, false, false, false, 0, nTrkHits, kGhostPrimary}); + ghostTracksAlice3.back().trackIU = hitRadii[0]; + ghostTracksAlice3.back().trackOU = hitRadii[1]; } } diff --git a/ALICE3/TableProducer/OTF/onTheFlyTrackerPid.cxx b/ALICE3/TableProducer/OTF/onTheFlyTrackerPid.cxx index bf7a1f1350e..51a8dcb95db 100644 --- a/ALICE3/TableProducer/OTF/onTheFlyTrackerPid.cxx +++ b/ALICE3/TableProducer/OTF/onTheFlyTrackerPid.cxx @@ -11,23 +11,20 @@ /// /// \file onTheFlyTrackerPid.cxx /// -/// \brief This task produces the PID information that can be obtained from the tracker layers (i.e. ToT and possibly cluster size). -/// So far only ToT implemented. It currently contemplates 5 (9) particle types: electrons, muons, pions, kaons and -/// protons (as well as deuterons, tritons, helium-3 and alphas/helium-4 if added via the event generator). +/// \brief This task simulates the PID signal that can be obtained from the tracker layers as ToT (Time-over-Threshold). +/// The dE/dx of every track is sampled from a per-track LUT made from the full simulation. +/// It contemplates 9 particle types: electrons, muons, pions, kaons, protons, deuterons, tritons, +/// helium-3 and alphas/helium-4. /// /// \author Henrik Fribert TUM /// \author Nicolò Jacazio Università del Piemonte Orientale /// \since May 22, 2025 /// -#include "GeometryContainer.h" - -#include "ALICE3/Core/TrackUtilities.h" #include "ALICE3/DataModel/OTFPIDTrk.h" #include #include -#include #include #include #include @@ -38,732 +35,402 @@ #include #include #include -#include -#include -#include -#include #include #include #include +#include +#include #include #include #include #include #include -#include #include #include +#include #include #include #include #include -#include #include using namespace o2; using namespace o2::framework; -static constexpr std::array kTrackerRadii = {0.5f, 1.2f, 2.5f, 3.75f, 7.0f, 12.0f, 20.0f, 30.0f, 45.0f, 60.0f, 80.0f}; - -// Constants for magic numbers -static constexpr int kMinEntriesForProjection = 10; -static constexpr double kMinMomentumForLogBins = 0.05; -static constexpr double kMaxMomentumForLogBins = 10.0; -static constexpr int kMinLayerForTruncation = 3; - -// Constants for validation conditions -static constexpr size_t kMinValidHits = 1; -static constexpr size_t kLowValidHits = 2; -static constexpr size_t kMidLowValidHits = 3; -static constexpr size_t kMidHighValidHits = 4; -static constexpr size_t kHighValidHits1 = 5; -static constexpr size_t kHighValidHits2 = 6; -static constexpr size_t kMaxValidHits = 7; - -// Constants for truncated mean calculation -static constexpr size_t kMaxValidHitsForTruncation7Plus = 4; -static constexpr size_t kMaxValidHitsForTruncation56 = 3; -static constexpr size_t kMaxValidHitsForTruncation34 = 2; -static constexpr size_t kMaxValidHitsForTruncation12 = 1; - -class ToTLUT +static constexpr double MinMomentumForLogBins = 0.05; +static constexpr double MaxMomentumForLogBins = 10.0; + +/// Local path of a LUT file; "ccdb:" is downloaded once to /tmp/ToTLUTs//snapshot.root +std::string getLocalLutFile(const std::string& filename, o2::ccdb::BasicCCDBManager* ccdbManager) { - public: - explicit ToTLUT(int maxLayers, - int etaBins, float etaMin, float etaMax, - int pBins, float pMin, float pMax, - float analysisEtaMinVal = 0.0f, float analysisEtaMaxVal = 1.0f, - float analysisPMinVal = 0.0f, float analysisPMaxVal = 10.0f) - : mMaxLayers(maxLayers), - mAnalysisEtaMin(analysisEtaMinVal), - mAnalysisEtaMax(analysisEtaMaxVal), - mAnalysisPMin(analysisPMinVal), - mAnalysisPMax(analysisPMaxVal), - mEtaBins(etaBins), - mEtaMin(etaMin), - mEtaMax(etaMax), - mPBins(pBins), - mPMin(pMin), - mPMax(pMax), - mEtaBinWidth((etaMax - etaMin) / etaBins), - mPBinWidth((pMax - pMin) / pBins) - { - mPdgToIndexMap.reserve(10); - mIndexToPdgMap.reserve(10); + if (!filename.starts_with("ccdb:")) { + return filename; } - ToTLUT() = delete; - - ~ToTLUT() - { - for (const auto& hist_ptr : mLUTHistogramFlat) { - - delete hist_ptr; + const std::string ccdbPath = filename.substr(5); + const std::string outPath = "/tmp/ToTLUTs/"; + const std::string localFilename = outPath + ccdbPath + "/snapshot.root"; + if (!std::ifstream(localFilename).is_open()) { + if (!ccdbManager) { + LOG(fatal) << "CCDB manager not set. Please set it before loading LUT from CCDB."; + } + std::map metadata; + LOG(info) << "Retrieving " << localFilename << " from CCDB path: " << ccdbPath; + ccdbManager->getCCDBAccessor().retrieveBlob(ccdbPath, outPath, metadata, 1); + if (!std::ifstream(localFilename).is_open()) { + LOG(fatal) << "Could not find downloaded CCDB file " << localFilename; } } + return localFilename; +} - void setCcdbManager(o2::ccdb::BasicCCDBManager* mgr) { mCcdbManager = mgr; } - - bool load(int pdg, const std::string& filename) +/// Per-track dE/dx LUT; for every species, |eta| bin and rigidity bin the +/// distribution of the track dE/dx in e-, as a histogram. The dE/dx of a track is sampled from it, +/// interpolated linearly in log(rigidity) between the rigidity bin centres with the same random number. +class TrackDedxLut +{ + public: + template + bool load(const std::string& filename, o2::ccdb::BasicCCDBManager* ccdbManager, const std::array& pdgCodes) { - if (filename.empty()) { - LOG(warning) << "Provided filename is empty for PDG " << pdg; + std::unique_ptr f(TFile::Open(getLocalLutFile(filename, ccdbManager).c_str())); + if (!f || f->IsZombie()) { + LOG(fatal) << "Failed to open dE/dx LUT file: " << filename; return false; } - if (filename.starts_with("ccdb:")) { // Check if filename starts with "ccdb:" - std::string ccdbPath = filename.substr(5); // remove "ccdb:" prefix - const std::string outPath = "/tmp/ToTLUTs/"; - const std::string localFilename = outPath + ccdbPath + "/snapshot.root"; - std::ifstream checkFile(localFilename); - if (!checkFile.is_open()) { // File is not found, need to download it from CCDB - if (!mCcdbManager) { - LOG(fatal) << "CCDB manager not set. Please set it before loading LUT from CCDB."; - } - std::map metadata; - LOG(info) << "Retrieving " << localFilename << " from CCDB path: " << ccdbPath; - mCcdbManager->getCCDBAccessor().retrieveBlob(ccdbPath, outPath, metadata, 1); - std::ifstream testFile(localFilename); - if (!testFile.is_open()) { - LOG(fatal) << "Could not find downloaded CCDB file for PDG " << pdg << " in file " << localFilename; - return false; - } - testFile.close(); - return load(pdg, localFilename); - } // File is found, proceed to load it - checkFile.close(); - return load(pdg, localFilename); - } - // In case the file is already available locally - TFile* f = TFile::Open(filename.c_str()); - if (!f || f->IsZombie()) { - LOG(fatal) << "Failed to open LUT file: " << filename; + const auto* etaBins = f->Get("etaBins"); + const auto* rigBins = f->Get("rigBins"); + const auto* dedxBins = f->Get("dedxBins"); + if (!etaBins || !rigBins || !dedxBins) { + LOG(fatal) << "No etaBins/rigBins/dedxBins in dE/dx LUT file: " << filename; return false; } - - int currentPdgIdx = 0; - auto it = mPdgToIndexMap.find(pdg); - if (it == mPdgToIndexMap.end()) { - currentPdgIdx = mIndexToPdgMap.size(); - mPdgToIndexMap[pdg] = currentPdgIdx; - mIndexToPdgMap.push_back(pdg); - - size_t totalSize = (currentPdgIdx + 1) * mMaxLayers * mEtaBins * mPBins; - if (mLUTHistogramFlat.size() < totalSize) { - mLUTHistogramFlat.resize(totalSize, nullptr); - } - } else { - currentPdgIdx = it->second; + mEtaEdges = binEdges(etaBins); + mRigEdges = binEdges(rigBins); + mDedxEdges = binEdges(dedxBins); + mLogRigCentres.clear(); + for (size_t i = 0; i + 1 < mRigEdges.size(); ++i) { + mLogRigCentres.push_back(0.5 * (std::log(mRigEdges[i]) + std::log(mRigEdges[i + 1]))); } - - bool success = true; - for (int layer = 0; layer < mMaxLayers; ++layer) { - for (int etaBin = 0; etaBin < mEtaBins; ++etaBin) { - float etaMinBin = mEtaMin + etaBin * mEtaBinWidth; - float etaMaxBin = etaMinBin + mEtaBinWidth; - - float etaCenter = (etaMinBin + etaMaxBin) / 2.0f; - if (std::abs(etaCenter) < mAnalysisEtaMin || std::abs(etaCenter) > mAnalysisEtaMax) { + const size_t nBins = (mEtaEdges.size() - 1) * (mRigEdges.size() - 1); + const size_t nDedx = mDedxEdges.size() - 1; + + bool allLoaded = true; + for (const auto& pdg : pdgCodes) { + std::unique_ptr> h(f->Get>(Form("h_%d", pdg))); + if (!h || h->size() != nBins * nDedx) { + LOG(warning) << "No dE/dx LUT for PDG " << pdg << " in " << filename; + allLoaded = false; + continue; + } + // cumulative distributions and raw moments, only for the filled bins + auto& offset = mOffset[pdg]; + auto& cdf = mCdf[pdg]; + auto& mean = mMean[pdg]; + auto& sigma = mSigma[pdg]; + offset.assign(nBins, -1); + mean.assign(nBins, -1.f); + sigma.assign(nBins, -1.f); + for (size_t b = 0; b < nBins; ++b) { + const float* hb = h->data() + b * nDedx; + double sum = 0., m1 = 0., m2 = 0.; + for (size_t k = 0; k < nDedx; ++k) { + const double x = 0.5 * (mDedxEdges[k] + mDedxEdges[k + 1]); + sum += hb[k]; + m1 += hb[k] * x; + m2 += hb[k] * x * x; + } + if (sum <= 0.) { continue; } - - for (int pBin = 0; pBin < mPBins; ++pBin) { - float pMinBin = mPMin + pBin * mPBinWidth; - float pMaxBin = pMinBin + mPBinWidth; - float pCenter = (pMinBin + pMaxBin) / 2.0f; - - if (pCenter < mAnalysisPMin || pCenter >= mAnalysisPMax) { - continue; - } - - TString histName = Form("tot_%d_barrel%d_eta%.2f-%.2f_p%.2f-%.2f", pdg, layer, etaMinBin, etaMaxBin, pMinBin, pMaxBin); - - TH1F* histFromFile = dynamic_cast(f->Get(histName)); - if (histFromFile) { - TH1F* clonedHist = dynamic_cast(histFromFile->Clone()); - clonedHist->SetDirectory(nullptr); - - size_t flatIdx = getFlatIndex(currentPdgIdx, layer, etaBin, pBin); - mLUTHistogramFlat[flatIdx] = clonedHist; - } else { - size_t flatIdx = getFlatIndex(currentPdgIdx, layer, etaBin, pBin); - mLUTHistogramFlat[flatIdx] = nullptr; - if (layer >= kMinLayerForTruncation) { - success = false; - } - } + offset[b] = cdf.size(); + double cum = 0.; + for (size_t k = 0; k < nDedx; ++k) { + cum += hb[k]; + cdf.push_back(cum / sum); } + const double mu = m1 / sum; + mean[b] = mu; + sigma[b] = std::sqrt(std::max(0., m2 / sum - mu * mu)); } } - - f->Close(); - delete f; - return success; + LOG(info) << "Loaded dE/dx LUT " << filename << " (" << mEtaEdges.size() - 1 << " |eta| x " << mRigEdges.size() - 1 + << " rigidity x " << mDedxEdges.size() - 1 << " dE/dx bins)"; + return allLoaded; } - TH1F* getHistogramForSampling(int pdgIdx, int layer, int etaBin, int pBin) const + /// Sampled dE/dx for uniform random number u, or -1 if there is no LUT entry + float sample(int pdg, float eta, float rigidity, double u) const { - if (pdgIdx < 0 || static_cast(pdgIdx) >= getNumPdgTypes() || - layer < 0 || layer >= mMaxLayers || - etaBin < 0 || etaBin >= mEtaBins || pBin < 0 || pBin >= mPBins) { - return nullptr; + int bin = -1, binNext = -1; + float weight = 0.f; + if (!locate(pdg, eta, rigidity, bin, binNext, weight)) { + return -1.f; } - size_t flatIdx = getFlatIndex(pdgIdx, layer, etaBin, pBin); - return (flatIdx < mLUTHistogramFlat.size()) ? mLUTHistogramFlat[flatIdx] : nullptr; - } - - int getPdgIndex(int pdgCode) const - { - auto it = mPdgToIndexMap.find(pdgCode); - if (it != mPdgToIndexMap.end()) { - return it->second; + const auto& offset = mOffset.at(pdg); + const float* cdf = mCdf.at(pdg).data(); + float dedx = inverseCdf(cdf + offset[bin], u); + if (binNext >= 0) { + dedx += weight * (inverseCdf(cdf + offset[binNext], u) - dedx); } - return -1; - } - - inline int getEtaBin(float eta) const - { - const float clampedEta = std::max(mEtaMin, std::min(eta, mEtaMax - 1e-6f)); - return std::min(static_cast((clampedEta - mEtaMin) / mEtaBinWidth), mEtaBins - 1); - } - - inline int getPBin(float p) const - { - const float clampedP = std::max(mPMin, std::min(p, mPMax - 1e-6f)); - return std::min(static_cast((clampedP - mPMin) / mPBinWidth), mPBins - 1); + return dedx; } - inline size_t getFlatIndex(int pdgIdx, int layer, int etaBin, int pBin) const + bool getMeanSigma(int pdg, float eta, float rigidity, float& mean, float& sigma) const { - return ((pdgIdx * mMaxLayers + layer) * mEtaBins + etaBin) * mPBins + pBin; - } - - size_t getNumPdgTypes() const - { - return mIndexToPdgMap.size(); + int bin = -1, binNext = -1; + float weight = 0.f; + if (!locate(pdg, eta, rigidity, bin, binNext, weight)) { + return false; + } + const auto& m = mMean.at(pdg); + const auto& s = mSigma.at(pdg); + mean = m[bin]; + sigma = s[bin]; + if (binNext >= 0) { + mean += weight * (m[binNext] - mean); + sigma += weight * (s[binNext] - sigma); + } + return sigma > 0.f; } - std::vector mLUTHistogramFlat; - - std::unordered_map mPdgToIndexMap; - std::vector mIndexToPdgMap; - - int mMaxLayers; - float mAnalysisEtaMin; - float mAnalysisEtaMax; - float mAnalysisPMin; - float mAnalysisPMax; - int mEtaBins; - float mEtaMin; - float mEtaMax; - int mPBins; - float mPMin; - float mPMax; - - float mEtaBinWidth; - float mPBinWidth; - private: - o2::ccdb::BasicCCDBManager* mCcdbManager = nullptr; -}; - -struct OnTheFlyTrackerPid { - static constexpr int kNumHypothesisParticles = 9; - std::array, kNumHypothesisParticles>, kNumHypothesisParticles> h2dBarrelNsigmaTrue; - std::array, kNumHypothesisParticles> h2dHitsPerTrackVsP; - std::array, kNumHypothesisParticles> h2dToTvsPperParticle; - std::array, kNumHypothesisParticles> h2dHitsPerTrackVsPLin; - std::array, kNumHypothesisParticles> h2dToTvsPperParticleLin; - - float calculateNsigma(float measuredToT, float expectedToT, float resolution) + /// LUT bin of the track and the neighbouring rigidity bin on its side (-1 if empty) + bool locate(int pdg, float eta, float rigidity, int& bin, int& binNext, float& weight) const { - if (resolution <= 0) { - return 999.f; + const auto it = mOffset.find(pdg); + const float absEta = std::abs(eta); + if (it == mOffset.end() || mEtaEdges.empty() || absEta < mEtaEdges.front() || absEta >= mEtaEdges.back() || + rigidity < mRigEdges.front() || rigidity >= mRigEdges.back()) { + return false; } - return (measuredToT - expectedToT) / resolution; - } - - float getToTMeanFromMomentumSlice(const std::shared_ptr& hist, float momentum) - { - if (!hist) { - return -1.f; + const int nRig = mRigEdges.size() - 1; + const int iEta = std::upper_bound(mEtaEdges.begin(), mEtaEdges.end(), absEta) - mEtaEdges.begin() - 1; + const int iRig = std::upper_bound(mRigEdges.begin(), mRigEdges.end(), rigidity) - mRigEdges.begin() - 1; + bin = iEta * nRig + iRig; + if (it->second[bin] < 0) { + return false; } - int binX = hist->GetXaxis()->FindBin(momentum); - TH1D* proj = hist->ProjectionY("temp", binX, binX); - if (proj->GetEntries() < kMinEntriesForProjection) { - delete proj; - return -1.f; + binNext = -1; + weight = 0.f; + const double logRig = std::log(rigidity); + const int iNext = logRig < mLogRigCentres[iRig] ? iRig - 1 : iRig + 1; + if (iNext >= 0 && iNext < nRig && it->second[iEta * nRig + iNext] >= 0) { + binNext = iEta * nRig + iNext; + weight = (logRig - mLogRigCentres[iRig]) / (mLogRigCentres[iNext] - mLogRigCentres[iRig]); } - float mean = proj->GetMean(); - delete proj; - return mean; + return true; } - float getToTResolutionFromMomentumSlice(const std::shared_ptr& hist, float momentum) + /// dE/dx at which the cumulative distribution reaches u, uniform within the dE/dx bin + float inverseCdf(const float* cdf, double u) const { - if (!hist) { - return -1.f; - } - int binX = hist->GetXaxis()->FindBin(momentum); - TH1D* proj = hist->ProjectionY("temp", binX, binX); - if (proj->GetEntries() < kMinEntriesForProjection) { - delete proj; - return -1.f; - } - float stddev = proj->GetStdDev(); - delete proj; - return stddev; + const int nDedx = mDedxEdges.size() - 1; + const int k = std::min(static_cast(std::upper_bound(cdf, cdf + nDedx, u) - cdf), nDedx - 1); + const double lo = k == 0 ? 0. : cdf[k - 1]; + const double frac = cdf[k] > lo ? (u - lo) / (cdf[k] - lo) : 0.5; + return mDedxEdges[k] + frac * (mDedxEdges[k + 1] - mDedxEdges[k]); } - float computeTrackLength(o2::track::TrackParCov track, float radius, float magneticField) + static std::vector binEdges(const TH1* h) { - o2::math_utils::CircleXYf_t trcCircle; - float sna = NAN, csa = NAN; - track.getCircleParams(magneticField, trcCircle, sna, csa); - - const float centerDistance = std::hypot(trcCircle.xC, trcCircle.yC); - - if (centerDistance < trcCircle.rC + radius && centerDistance > std::fabs(trcCircle.rC - radius)) { - float length = 0.0f; - const float ux = trcCircle.xC / centerDistance; - const float uy = trcCircle.yC / centerDistance; - const float vx = -uy; - const float vy = +ux; - const float radical = (centerDistance * centerDistance - trcCircle.rC * trcCircle.rC + radius * radius) / (2.0f * centerDistance); - const float displace = (0.5f / centerDistance) * std::sqrt( - (-centerDistance + trcCircle.rC - radius) * - (-centerDistance - trcCircle.rC + radius) * - (-centerDistance + trcCircle.rC + radius) * - (centerDistance + trcCircle.rC + radius)); - - const std::array point1 = {radical * ux + displace * vx, radical * uy + displace * vy}; - const std::array point2 = {radical * ux - displace * vx, radical * uy - displace * vy}; - - std::array mom{}; - track.getPxPyPzGlo(mom); - const float scalarProduct1 = point1[0] * mom[0] + point1[1] * mom[1]; - const float scalarProduct2 = point2[0] * mom[0] + point2[1] * mom[1]; - - std::array startPoint{}; - track.getXYZGlo(startPoint); - - float cosAngle = -1000, modulus = -1000; - - if (scalarProduct1 > scalarProduct2) { - modulus = std::hypot(point1[0] - trcCircle.xC, point1[1] - trcCircle.yC) * std::hypot(startPoint[0] - trcCircle.xC, startPoint[1] - trcCircle.yC); - cosAngle = (point1[0] - trcCircle.xC) * (startPoint[0] - trcCircle.xC) + (point1[1] - trcCircle.yC) * (startPoint[1] - trcCircle.yC); - } else { - modulus = std::hypot(point2[0] - trcCircle.xC, point2[1] - trcCircle.yC) * std::hypot(startPoint[0] - trcCircle.xC, startPoint[1] - trcCircle.yC); - cosAngle = (point2[0] - trcCircle.xC) * (startPoint[0] - trcCircle.xC) + (point2[1] - trcCircle.yC) * (startPoint[1] - trcCircle.yC); - } - cosAngle /= modulus; - length = trcCircle.rC * std::acos(cosAngle); - length *= std::sqrt(1.0f + track.getTgl() * track.getTgl()); - return length; + std::vector edges; + for (int i = 1; i <= h->GetNbinsX() + 1; ++i) { + edges.push_back(h->GetXaxis()->GetBinLowEdge(i)); } - return -100.f; + return edges; } + std::vector mEtaEdges; + std::vector mRigEdges; + std::vector mLogRigCentres; + std::vector mDedxEdges; + std::unordered_map> mOffset; // |pdg| -> start of the cdf of bin etaBin * nRig + rigBin, -1 if empty + std::unordered_map> mCdf; // |pdg| -> cumulative dE/dx distributions of the filled bins + std::unordered_map> mMean; // |pdg| -> mean dE/dx per bin, -1 if empty + std::unordered_map> mSigma; +}; + +struct OnTheFlyTrackerPid { + static constexpr int NumSpecies = 9; + static constexpr std::array PdgCodes = {11, 13, 211, 321, 2212, 1000010020, 1000010030, 1000020030, 1000020040}; + static constexpr std::array SpeciesNames = {"Elec", "Muon", "Pion", "Kaon", "Prot", "Deut", "Trit", "He3", "Al"}; + static constexpr std::array SpeciesLabels = {"#it{e}", "#it{#mu}", "#it{#pi}", "#it{K}", "#it{p}", "#it{d}", "#it{t}", "#it{^{3}He}", "#it{^{4}He}"}; + static constexpr float NoNsigma = 999.f; + + std::array, NumSpecies> h2dToTvsPperParticle; + std::array, NumSpecies> h2dToTvsPperParticleLin; + std::array, NumSpecies> h3dToTvsPEtaPerParticle; + std::array, NumSpecies> h2dTracks; + std::array, NumSpecies> h2dTracksWithDedx; + Produces tableUpgradeTrkPidSignals; Produces tableUpgradeTrkPids; Service pdgDatabase{}; Service ccdb{}; - std::unique_ptr mToTLUT; + TrackDedxLut mTrackLut; + TRandom3 mRandom; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - Configurable lutTotEl{"lutTotEl", "ccdb:Users/h/hfribert/ToT_LUTs_ALMIRA/Scale1/PDG_11", "ToT LUT for electrons"}; - Configurable lutTotMu{"lutTotMu", "ccdb:Users/h/hfribert/ToT_LUTs_ALMIRA/Scale1/PDG_13", "ToT LUT for muons"}; - Configurable lutTotPi{"lutTotPi", "ccdb:Users/h/hfribert/ToT_LUTs_ALMIRA/Scale1/PDG_211", "ToT LUT for pions"}; - Configurable lutTotKa{"lutTotKa", "ccdb:Users/h/hfribert/ToT_LUTs_ALMIRA/Scale1/PDG_321", "ToT LUT for kaons"}; - Configurable lutTotPr{"lutTotPr", "ccdb:Users/h/hfribert/ToT_LUTs_ALMIRA/Scale1/PDG_2212", "ToT LUT for protons"}; - Configurable lutTotDe{"lutTotDe", "ccdb:Users/h/hfribert/ToT_LUTs_ALMIRA/Scale1/PDG_1000010020", "ToT LUT for deuteron"}; - Configurable lutTotTr{"lutTotTr", "ccdb:Users/h/hfribert/ToT_LUTs_ALMIRA/Scale1/PDG_1000010030", "ToT LUT for triton"}; - Configurable lutTotHe{"lutTotHe", "ccdb:Users/h/hfribert/ToT_LUTs_ALMIRA/Scale1/PDG_1000020030", "ToT LUT for helium-3"}; - Configurable lutTotAl{"lutTotAl", "ccdb:Users/h/hfribert/ToT_LUTs_ALMIRA/Scale1/PDG_1000020040", "ToT LUT for alphas"}; - - Configurable maxBarrelLayers{"maxBarrelLayers", 11, "Maximum number of barrel layers"}; - Configurable numLogBins{"numLogBins", 200, "Number of logarithmic momentum bins"}; - Configurable analysisEtaMin{"analysisEtaMin", 0.0f, "Minimum |eta| for LUT loading optimization"}; - Configurable analysisEtaMax{"analysisEtaMax", 1.0f, "Maximum |eta| for LUT loading optimization"}; - Configurable analysisPMin{"analysisPMin", 0.0f, "Minimum |p| (GeV/c) for LUT loading optimization"}; - Configurable analysisPMax{"analysisPMax", 10.0f, "Maximum |p| (GeV/c) for LUT loading optimization"}; - - // LUT binning (has to match LUTs) - Configurable lutEtaBins{"lutEtaBins", 50, "Number of eta bins in the LUT"}; - Configurable lutEtaMin{"lutEtaMin", -2.5f, "Minimum eta in the LUT"}; - Configurable lutEtaMax{"lutEtaMax", 2.5f, "Maximum eta in the LUT"}; - Configurable lutPBins{"lutPBins", 500, "Number of momentum bins in the LUT"}; - Configurable lutPMin{"lutPMin", 0.0f, "Minimum momentum in the LUT (GeV/c)"}; - Configurable lutPMax{"lutPMax", 10.0f, "Maximum momentum in the LUT (GeV/c)"}; - - // ToT histogram axis - Configurable totHistBins{"totHistBins", 600, "Number of bins on the ToT axis"}; - Configurable totHistMin{"totHistMin", 0.0f, "Minimum of the ToT axis (#mus/10#mum)"}; - Configurable totHistMax{"totHistMax", 10.0f, "Maximum of the ToT axis (#mus/10#mum)"}; + Configurable trackLut{"trackLut", "", "Per-track dE/dx LUT from MakeToTLUT.C (local file or ccdb:)"}; + Configurable seed{"seed", 0, "Seed for the dE/dx sampling (0: different in every job)"}; + Configurable primariesOnly{"primariesOnly", false, "Fill the histograms only with physical primaries (the tables are filled for all tracks)"}; + Configurable useTrueKinematics{"useTrueKinematics", false, "Fill the histograms vs the true instead of the reconstructed p/z and |eta| (the dE/dx is always sampled at the true ones)"}; + + // dE/dx axis, logarithmic + Configurable numDedxBins{"numDedxBins", 300, "Number of logarithmic dE/dx bins"}; + Configurable dedxMin{"dedxMin", 1e2f, "Minimum of the dE/dx axis in e-"}; + Configurable dedxMax{"dedxMax", 1e5f, "Maximum of the dE/dx axis in e-"}; + + ConfigurableAxis axisEta{"axisEta", {VARIABLE_WIDTH, 0.0f, 0.5f, 1.0f, 1.5f, 2.0f, 2.5f, 3.0f, 4.0f}, "|#eta| binning of the eta-differential histograms"}; // Momentum axis binning choice - Configurable enableLogPBins{"enableLogPBins", true, "Enable logarithmic momentum binning for ToT vs p histograms"}; - Configurable enableLinearPBins{"enableLinearPBins", false, "Enable linear momentum binning for ToT vs p histograms"}; + Configurable numLogBins{"numLogBins", 200, "Number of logarithmic momentum bins"}; + Configurable enableLogPBins{"enableLogPBins", true, "Enable logarithmic momentum binning for dE/dx vs p histograms"}; + Configurable enableLinearPBins{"enableLinearPBins", false, "Enable linear momentum binning for dE/dx vs p histograms"}; Configurable numLinearBins{"numLinearBins", 500, "Number of linear momentum bins"}; Configurable linearPMin{"linearPMin", 0.0f, "Minimum momentum for linear-binned histograms (GeV/c)"}; Configurable linearPMax{"linearPMax", 10.0f, "Maximum momentum for linear-binned histograms (GeV/c)"}; - std::vector mLogBins; - - std::array mHypothesisPdgCodes = { - 11, // Electron - 13, // Muon - 211, // Pion - 321, // Kaon - 2212, // Proton - 1000010020, // Deuteron - 1000010030, // Triton - 1000020030, // Helium-3 - 1000020040 // Alpha - }; - - // Configuration defined at init time - o2::fastsim::GeometryContainer mGeoContainer; - float mMagneticField = 0.0f; - void init(o2::framework::InitContext& initContext) + void init(o2::framework::InitContext&) { ccdb->setURL("http://alice-ccdb.cern.ch"); ccdb->setTimestamp(-1); - mGeoContainer.setCcdbManager(ccdb.operator->()); - mGeoContainer.init(initContext); - mMagneticField = mGeoContainer.getFloatValue(0, "global", "magneticfield"); - - if (static_cast(maxBarrelLayers.value) > kTrackerRadii.size()) { - LOG(fatal) << "Configured maxBarrelLayers (" << maxBarrelLayers.value - << ") exceeds the size of kTrackerRadii (" << kTrackerRadii.size() - << "). Please adjust maxBarrelLayers."; - } - - mToTLUT = std::make_unique(maxBarrelLayers.value, - lutEtaBins.value, lutEtaMin.value, lutEtaMax.value, - lutPBins.value, lutPMin.value, lutPMax.value, - analysisEtaMin.value, analysisEtaMax.value, - analysisPMin.value, analysisPMax.value); - - mToTLUT->setCcdbManager(ccdb.operator->()); - - bool loaded = true; - loaded &= mToTLUT->load(11, lutTotEl.value); - loaded &= mToTLUT->load(13, lutTotMu.value); - loaded &= mToTLUT->load(211, lutTotPi.value); - loaded &= mToTLUT->load(321, lutTotKa.value); - loaded &= mToTLUT->load(2212, lutTotPr.value); - loaded &= mToTLUT->load(1000010020, lutTotDe.value); - loaded &= mToTLUT->load(1000010030, lutTotTr.value); - loaded &= mToTLUT->load(1000020030, lutTotHe.value); - loaded &= mToTLUT->load(1000020040, lutTotAl.value); - - if (!loaded) { - LOG(warning) << "Failed to load one or more ToT LUTs. PID results might be incomplete."; + mRandom.SetSeed(seed.value); + if (!mTrackLut.load(trackLut.value, ccdb.operator->(), PdgCodes)) { + LOG(warning) << "dE/dx LUT incomplete, species without LUT get no tracker PID."; } + bookHistograms(); + } - // Logarithmic momentum bins - mLogBins.clear(); - double pMin = kMinMomentumForLogBins; - double pMax = kMaxMomentumForLogBins; - double logMin = std::log10(pMin); - double logMax = std::log10(pMax); - double dLog = (logMax - logMin) / numLogBins.value; - for (int i = 0; i <= numLogBins.value; ++i) { - mLogBins.push_back(std::pow(10, logMin + i * dLog)); + static std::vector logBinning(int nBins, double min, double max) + { + std::vector edges; + const double logMin = std::log10(min); + const double dLog = (std::log10(max) - logMin) / nBins; + for (int i = 0; i <= nBins; ++i) { + edges.push_back(std::pow(10, logMin + i * dLog)); } + return edges; + } - const AxisSpec axisToT{totHistBins.value, static_cast(totHistMin.value), static_cast(totHistMax.value), "ToT (#mus/10#mum)"}; - const AxisSpec axisNsigma{200, -10., 10., "N#sigma"}; - const AxisSpec axisLayer{maxBarrelLayers.value, -0.5, static_cast(maxBarrelLayers.value) - 0.5, "Layer"}; - const AxisSpec axisHitsPerTrack{maxBarrelLayers.value + 1, -0.5, static_cast(maxBarrelLayers.value) + 0.5, "# Hits per track"}; - + void bookHistograms() + { if (!enableLogPBins.value && !enableLinearPBins.value) { LOG(fatal) << "At least one of enableLogPBins or enableLinearPBins must be true."; } - histos.add("hHitLayers", "Number of hits on each detector layer;Layer;Counts", kTH1F, {axisLayer}); - histos.add("hHitMultiplicity", "Hit multiplicity along the track; # Hits per track;Counts", kTH1F, {axisHitsPerTrack}); + const std::vector logBins = logBinning(numLogBins.value, MinMomentumForLogBins, MaxMomentumForLogBins); + const std::string signalTitle = "d#it{E}/d#it{x} (e^{-})"; + const AxisSpec axisDedx{logBinning(numDedxBins.value, dedxMin.value, dedxMax.value), signalTitle}; + const AxisSpec axisMomentumLog{logBins, "#it{p/z} (GeV/#it{c})"}; + const AxisSpec axisMomentumLin{numLinearBins.value, static_cast(linearPMin.value), static_cast(linearPMax.value), "#it{p/z} (GeV/#it{c})"}; + const AxisSpec axisAbsEta{axisEta, "|#eta|"}; if (enableLogPBins.value) { - const AxisSpec axisMomentumLog{mLogBins, "#it{p/z} (GeV/#it{c})"}; - histos.add("hToTvsP", "ToT vs #it{p/z}; #it{p/z} (GeV/#it{c}); ToT (#mus/10#mum)", kTH2F, {axisMomentumLog, axisToT}); - histos.add("hToTvsMom", "ToT vs #it{p}; #it{p} (GeV/#it{c}); ToT (#mus/10#mum)", kTH2F, {mLogBins, axisToT}); + histos.add("hToTvsP", ("Signal vs #it{p/z}; #it{p/z} (GeV/#it{c}); " + signalTitle).c_str(), kTH2F, {axisMomentumLog, axisDedx}); + histos.add("hToTvsMom", ("Signal vs #it{p}; #it{p} (GeV/#it{c}); " + signalTitle).c_str(), kTH2F, {logBins, axisDedx}); } - if (enableLinearPBins.value) { - const AxisSpec axisMomentumLin{numLinearBins.value, static_cast(linearPMin.value), static_cast(linearPMax.value), "#it{p/z} (GeV/#it{c})"}; - histos.add("hToTvsPLin", "ToT vs #it{p/z} (linear); #it{p/z} (GeV/#it{c}); ToT (#mus/10#mum)", kTH2F, {axisMomentumLin, axisToT}); - histos.add("hToTvsMomLin", "ToT vs #it{p} (linear); #it{p} (GeV/#it{c}); ToT (#mus/10#mum)", kTH2F, {axisMomentumLin, axisToT}); + histos.add("hToTvsPLin", ("Signal vs #it{p/z} (linear); #it{p/z} (GeV/#it{c}); " + signalTitle).c_str(), kTH2F, {axisMomentumLin, axisDedx}); + histos.add("hToTvsMomLin", ("Signal vs #it{p} (linear); #it{p} (GeV/#it{c}); " + signalTitle).c_str(), kTH2F, {axisMomentumLin, axisDedx}); } - std::vector> particleInfo = { - {11, "Elec"}, {13, "Muon"}, {211, "Pion"}, {321, "Kaon"}, {2212, "Prot"}, {1000010020, "Deut"}, {1000010030, "Trit"}, {1000020030, "He3"}, {1000020040, "Al"}}; - - for (size_t iTrue = 0; iTrue < particleInfo.size(); ++iTrue) { - std::string trueName = particleInfo[iTrue].second; - std::string trueNamePretty = trueName; // Fallback - if (trueName == "Elec") { - trueNamePretty = "#it{e}"; - } else if (trueName == "Muon") { - trueNamePretty = "#it{#mu}"; - } else if (trueName == "Pion") { - trueNamePretty = "#it{#pi}"; - } else if (trueName == "Kaon") { - trueNamePretty = "#it{K}"; - } else if (trueName == "Prot") { - trueNamePretty = "#it{p}"; - } else if (trueName == "Deut") { - trueNamePretty = "#it{d}"; - } else if (trueName == "Trit") { - trueNamePretty = "#it{t}"; - } else if (trueName == "He3") { - trueNamePretty = "#it{^{3}He}"; - } else if (trueName == "Al") { - trueNamePretty = "#it{^{4}He}"; - } - + for (int i = 0; i < NumSpecies; ++i) { if (enableLogPBins.value) { - const AxisSpec axisMomentumLog{mLogBins, "#it{p/z} (GeV/#it{c})"}; - - std::string hitsVsPName = "HitsPerTrack/hHitsPerTrackVsP_" + trueName; - std::string hitsVsPTitle = "N_hits vs #it{p/z} for " + trueNamePretty + "; #it{p/z} (GeV/#it{c}); N_hits"; - h2dHitsPerTrackVsP[iTrue] = histos.add(hitsVsPName.c_str(), hitsVsPTitle.c_str(), kTH2F, {axisMomentumLog, axisHitsPerTrack}); - - std::string totVsPName = "ToTvsP/hToTvsP_" + trueName; - std::string totVsPTitle = "ToT vs #it{p/z} for " + trueNamePretty + "; #it{p/z} (GeV/#it{c}); ToT (#mus/10#mum)"; - h2dToTvsPperParticle[iTrue] = histos.add(totVsPName.c_str(), totVsPTitle.c_str(), kTH2F, {axisMomentumLog, axisToT}); - - for (size_t iHyp = 0; iHyp < particleInfo.size(); ++iHyp) { - std::string hypName = particleInfo[iHyp].second; - std::string hypNamePretty = hypName; // Fallback - if (hypName == "Elec") { - hypNamePretty = "#it{e}"; - } else if (hypName == "Muon") { - hypNamePretty = "#it{#mu}"; - } else if (hypName == "Pion") { - hypNamePretty = "#it{#pi}"; - } else if (hypName == "Kaon") { - hypNamePretty = "#it{K}"; - } else if (hypName == "Prot") { - hypNamePretty = "#it{p}"; - } else if (hypName == "Deut") { - hypNamePretty = "#it{d}"; - } else if (hypName == "Trit") { - hypNamePretty = "#it{t}"; - } else if (hypName == "He3") { - hypNamePretty = "#it{^{3}He}"; - } else if (hypName == "Al") { - hypNamePretty = "#it{^{4}He}"; - } - - const std::string histName = std::format("NSigma/BarrelNsigmaTrue{}Vs{}Hypothesis", trueName, hypName); - const std::string histTitle = std::format("Nsigma (True {} vs Hyp {}); #it{{p/z}} (GeV/#it{{c}}); N#sigma", trueNamePretty, hypNamePretty); - h2dBarrelNsigmaTrue[iTrue][iHyp] = histos.add(histName.c_str(), histTitle.c_str(), kTH2F, {axisMomentumLog, axisNsigma}); - } + const std::string name = std::format("ToTvsP/hToTvsP_{}", SpeciesNames[i]); + const std::string title = std::format("Signal vs #it{{p/z}} for {}; #it{{p/z}} (GeV/#it{{c}}); {}", SpeciesLabels[i], signalTitle); + h2dToTvsPperParticle[i] = histos.add(name.c_str(), title.c_str(), kTH2F, {axisMomentumLog, axisDedx}); } - if (enableLinearPBins.value) { - const AxisSpec axisMomentumLin{numLinearBins.value, static_cast(linearPMin.value), static_cast(linearPMax.value), "#it{p/z} (GeV/#it{c})"}; + const std::string name = std::format("ToTvsPLin/hToTvsPLin_{}", SpeciesNames[i]); + const std::string title = std::format("Signal vs #it{{p/z}} for {} (linear); #it{{p/z}} (GeV/#it{{c}}); {}", SpeciesLabels[i], signalTitle); + h2dToTvsPperParticleLin[i] = histos.add(name.c_str(), title.c_str(), kTH2F, {axisMomentumLin, axisDedx}); + } - const std::string hitsVsPLinName = std::format("HitsPerTrackLin/hHitsPerTrackVsPLin_{}", trueName); - const std::string hitsVsPLinTitle = std::format("N_hits vs #it{{p/z}} for {} (linear); #it{{p/z}} (GeV/#it{{c}}); N_hits", trueNamePretty); - h2dHitsPerTrackVsPLin[iTrue] = histos.add(hitsVsPLinName.c_str(), hitsVsPLinTitle.c_str(), kTH2F, {axisMomentumLin, axisHitsPerTrack}); + const std::string nameEta = std::format("ToTvsPEta/hToTvsPEta_{}", SpeciesNames[i]); + const std::string titleEta = std::format("Signal vs #it{{p/z}} and |#eta| for {}; #it{{p/z}} (GeV/#it{{c}}); |#eta|; {}", SpeciesLabels[i], signalTitle); + h3dToTvsPEtaPerParticle[i] = histos.add(nameEta.c_str(), titleEta.c_str(), kTH3F, {axisMomentumLog, axisAbsEta, axisDedx}); - const std::string totVsPLinName = std::format("ToTvsPLin/hToTvsPLin_{}", trueName); - const std::string totVsPLinTitle = std::format("ToT vs #it{{p/z}} for {} (linear); #it{{p/z}} (GeV/#it{{c}}); ToT (#mus/10#mum)", trueNamePretty); - h2dToTvsPperParticleLin[iTrue] = histos.add(totVsPLinName.c_str(), totVsPLinTitle.c_str(), kTH2F, {axisMomentumLin, axisToT}); - } + const std::string nameTracks = std::format("Bookkeeping/hTracks_{}", SpeciesNames[i]); + const std::string titleTracks = std::format("Reconstructed {}; #it{{p/z}} (GeV/#it{{c}}); |#eta|", SpeciesLabels[i]); + h2dTracks[i] = histos.add(nameTracks.c_str(), titleTracks.c_str(), kTH2F, {axisMomentumLog, axisAbsEta}); + + const std::string nameWithDedx = std::format("Bookkeeping/hTracksWithDedx_{}", SpeciesNames[i]); + const std::string titleWithDedx = std::format("Reconstructed {} with dE/dx; #it{{p/z}} (GeV/#it{{c}}); |#eta|", SpeciesLabels[i]); + h2dTracksWithDedx[i] = histos.add(nameWithDedx.c_str(), titleWithDedx.c_str(), kTH2F, {axisMomentumLog, axisAbsEta}); } } - void process(soa::Join::iterator const& collision, - soa::Join const& tracks, - aod::McParticles const& /*mcParticles*/, - aod::McCollisions const& /*mcCollisions*/) + /// dE/dx of the track (-1 if there is none) and its nsigma for every species + template + float processTrack(TTrack const& track, std::array& nSigma) { - o2::dataformats::VertexBase mcPvVtx({0.0f, 0.0f, 0.0f}, {0.}); - - if (collision.has_mcCollision()) { - const auto& mcCollisionObject = collision.mcCollision(); - mcPvVtx.setX(mcCollisionObject.posX()); - mcPvVtx.setY(mcCollisionObject.posY()); - mcPvVtx.setZ(mcCollisionObject.posZ()); + if (!track.has_mcParticle()) { + return -1.f; } - - for (const auto& track : tracks) { - float truncatedMeanToT = -1.0f; - std::array nSigmaValues{}; - nSigmaValues.fill(999.f); - - if (!track.has_mcParticle()) { - tableUpgradeTrkPidSignals(truncatedMeanToT); - tableUpgradeTrkPids(nSigmaValues[0], nSigmaValues[1], nSigmaValues[2], nSigmaValues[3], - nSigmaValues[4], nSigmaValues[5], nSigmaValues[6], nSigmaValues[7], nSigmaValues[8]); - continue; - } - - const auto& mcParticle = track.mcParticle(); - - const auto& pdgInfo = pdgDatabase->GetParticle(mcParticle.pdgCode()); - if (!pdgInfo) { - tableUpgradeTrkPidSignals(truncatedMeanToT); - tableUpgradeTrkPids(nSigmaValues[0], nSigmaValues[1], nSigmaValues[2], nSigmaValues[3], - nSigmaValues[4], nSigmaValues[5], nSigmaValues[6], nSigmaValues[7], nSigmaValues[8]); - continue; - } - - const float p = mcParticle.p(); - const float eta = mcParticle.eta(); - const int truePdgCode = std::abs(mcParticle.pdgCode()); - - float rigidity = p; - if (pdgInfo) { - const float trueCharge = std::abs(pdgInfo->Charge()) / 3.0f; - if (trueCharge > 0) { - rigidity = p / trueCharge; - } - } - - int truePdgIdx = mToTLUT->getPdgIndex(truePdgCode); - if (truePdgIdx == -1) { - tableUpgradeTrkPidSignals(truncatedMeanToT); - tableUpgradeTrkPids(nSigmaValues[0], nSigmaValues[1], nSigmaValues[2], nSigmaValues[3], - nSigmaValues[4], nSigmaValues[5], nSigmaValues[6], nSigmaValues[7], nSigmaValues[8]); - continue; - } - - const int binnedP = mToTLUT->getPBin(p); - const int binnedEta = mToTLUT->getEtaBin(std::abs(eta)); - - uint16_t hitMap = 0; - int nHitLayers = 0; - o2::track::TrackParCov o2track = o2::upgrade::convertMCParticleToO2Track(mcParticle, pdgDatabase); - - float xPv = -100.f; - static constexpr float kTrkXThreshold = -99.f; - if (o2track.propagateToDCA(mcPvVtx, mMagneticField)) { - xPv = o2track.getX(); - } - - if (xPv > kTrkXThreshold) { - for (int layer = 0; layer < maxBarrelLayers.value; ++layer) { - float layerRadius = kTrackerRadii[layer]; - float trackLength = computeTrackLength(o2track, layerRadius, mMagneticField); - - if (trackLength > 0) { - hitMap |= (1 << layer); - histos.fill(HIST("hHitLayers"), layer); - nHitLayers++; - } - } - } - - histos.fill(HIST("hHitMultiplicity"), nHitLayers); - if (enableLogPBins.value) { - h2dHitsPerTrackVsP[truePdgIdx]->Fill(rigidity, nHitLayers); - } - if (enableLinearPBins.value) { - h2dHitsPerTrackVsPLin[truePdgIdx]->Fill(rigidity, nHitLayers); - } - - std::vector validToTs; - - for (int layer = kMinLayerForTruncation; layer < maxBarrelLayers.value; ++layer) { - if (((hitMap >> layer) & 0x1) != 0) { - TH1F* totHist = mToTLUT->getHistogramForSampling(truePdgIdx, layer, binnedEta, binnedP); - - if (totHist && totHist->GetEntries() > 1) { - float sampledToT = totHist->GetRandom(); - validToTs.push_back(sampledToT); - } - } - } - - truncatedMeanToT = -1.0f; - const size_t nValid = validToTs.size(); - size_t nUse = 0; - - if (nValid == kMidLowValidHits || nValid == kMidHighValidHits) { - nUse = kMaxValidHitsForTruncation34; - } else if (nValid == kMinValidHits || nValid == kLowValidHits) { - nUse = kMaxValidHitsForTruncation12; - } else if (nValid == kHighValidHits1 || nValid == kHighValidHits2) { - nUse = kMaxValidHitsForTruncation56; - } else if (nValid >= kMaxValidHits) { - nUse = kMaxValidHitsForTruncation7Plus; - } - - if (nUse > 0 && nValid >= nUse) { - std::sort(validToTs.begin(), validToTs.end()); - float sum = 0.0f; - for (size_t i = 0; i < nUse; ++i) { - sum += validToTs[i]; - } - truncatedMeanToT = sum / static_cast(nUse); - - if (enableLogPBins.value) { - histos.fill(HIST("hToTvsMom"), p, truncatedMeanToT); - histos.fill(HIST("hToTvsP"), rigidity, truncatedMeanToT); - h2dToTvsPperParticle[truePdgIdx]->Fill(rigidity, truncatedMeanToT); - } - if (enableLinearPBins.value) { - histos.fill(HIST("hToTvsMomLin"), p, truncatedMeanToT); - histos.fill(HIST("hToTvsPLin"), rigidity, truncatedMeanToT); - h2dToTvsPperParticleLin[truePdgIdx]->Fill(rigidity, truncatedMeanToT); + const auto& mcParticle = track.mcParticle(); + const int pdg = std::abs(mcParticle.pdgCode()); + const auto itSpecies = std::find(PdgCodes.begin(), PdgCodes.end(), pdg); + if (itSpecies == PdgCodes.end()) { + return -1.f; + } + const auto* pdgInfo = pdgDatabase->GetParticle(mcParticle.pdgCode()); + if (!pdgInfo || pdgInfo->Charge() == 0) { + return -1.f; + } + const int iSpecies = std::distance(PdgCodes.begin(), itSpecies); + const float absCharge = std::abs(pdgInfo->Charge()) / 3.f; + const float rigidityTrue = mcParticle.p() / absCharge; + const float dedx = mTrackLut.sample(pdg, mcParticle.eta(), rigidityTrue, mRandom.Rndm()); + + if (dedx > 0.f) { + for (int i = 0; i < NumSpecies; ++i) { + float mean = 0.f, sigma = 0.f; + if (mTrackLut.getMeanSigma(PdgCodes[i], track.eta(), track.p(), mean, sigma)) { + nSigma[i] = (dedx - mean) / sigma; } } + } - nSigmaValues.fill(999.f); - - if (enableLogPBins.value && truncatedMeanToT > 0) { - for (size_t iHyp = 0; iHyp < mHypothesisPdgCodes.size(); ++iHyp) { - int hypPdgCode = mHypothesisPdgCodes[iHyp]; - int hypPdgIdx = mToTLUT->getPdgIndex(hypPdgCode); - - if (hypPdgIdx == -1) { - nSigmaValues[iHyp] = 999.f; - continue; - } - - float expectedToT = getToTMeanFromMomentumSlice(h2dToTvsPperParticle[hypPdgIdx], rigidity); - float resolution = getToTResolutionFromMomentumSlice(h2dToTvsPperParticle[hypPdgIdx], rigidity); - - if (expectedToT > 0 && resolution > 0) { - nSigmaValues[iHyp] = calculateNsigma(truncatedMeanToT, expectedToT, resolution); - h2dBarrelNsigmaTrue[truePdgIdx][iHyp]->Fill(rigidity, nSigmaValues[iHyp]); - } else { - nSigmaValues[iHyp] = 999.f; - } - } - } + if (primariesOnly.value && !mcParticle.isPhysicalPrimary()) { + return dedx; + } + const float rigidity = useTrueKinematics.value ? rigidityTrue : track.p(); + const float absEta = std::abs(useTrueKinematics.value ? mcParticle.eta() : track.eta()); + const float p = rigidity * absCharge; + h2dTracks[iSpecies]->Fill(rigidity, absEta); + if (dedx < 0.f) { + return dedx; + } + h2dTracksWithDedx[iSpecies]->Fill(rigidity, absEta); + h3dToTvsPEtaPerParticle[iSpecies]->Fill(rigidity, absEta, dedx); + if (enableLogPBins.value) { + histos.fill(HIST("hToTvsMom"), p, dedx); + histos.fill(HIST("hToTvsP"), rigidity, dedx); + h2dToTvsPperParticle[iSpecies]->Fill(rigidity, dedx); + } + if (enableLinearPBins.value) { + histos.fill(HIST("hToTvsMomLin"), p, dedx); + histos.fill(HIST("hToTvsPLin"), rigidity, dedx); + h2dToTvsPperParticleLin[iSpecies]->Fill(rigidity, dedx); + } + return dedx; + } - tableUpgradeTrkPidSignals(truncatedMeanToT); - tableUpgradeTrkPids(nSigmaValues[0], nSigmaValues[1], nSigmaValues[2], nSigmaValues[3], - nSigmaValues[4], nSigmaValues[5], nSigmaValues[6], nSigmaValues[7], nSigmaValues[8]); + void process(soa::Join const& tracks, + aod::McParticles const&) + { + std::array nSigma{}; + for (const auto& track : tracks) { + nSigma.fill(NoNsigma); + const float dedx = processTrack(track, nSigma); + tableUpgradeTrkPidSignals(dedx); + tableUpgradeTrkPids(nSigma[0], nSigma[1], nSigma[2], nSigma[3], nSigma[4], nSigma[5], nSigma[6], nSigma[7], nSigma[8]); } } }; diff --git a/ALICE3/TableProducer/alice3-correlatorDDbar.cxx b/ALICE3/TableProducer/alice3-correlatorDDbar.cxx index 0e27bec4476..bb1b54bd03e 100644 --- a/ALICE3/TableProducer/alice3-correlatorDDbar.cxx +++ b/ALICE3/TableProducer/alice3-correlatorDDbar.cxx @@ -89,8 +89,6 @@ struct alice3correlatorddbar { Configurable> binsPt{"binsPt", std::vector{o2::analysis::hf_cuts_d0_to_pi_k::vecBinsPt}, "pT bin limits for candidate mass plots and efficiency"}; Configurable> efficiencyD{"efficiencyD", std::vector{efficiencyDmeson_v}, "Efficiency values for D0 meson"}; - // HfHelper hfHelper; //not needed for now - Partition> selectedCandidates = aod::a3D0meson::y > -yCandMax&& aod::a3D0meson::y ptCandMin && (aod::a3D0Selection::isSelD0 >= selectionFlagD0 || aod::a3D0Selection::isSelD0bar >= selectionFlagD0bar); Partition> selectedCandidatesMC = aod::a3D0meson::y > -yCandMax&& aod::a3D0meson::y ptCandMin && (aod::a3D0Selection::isSelD0 >= selectionFlagD0 || aod::a3D0Selection::isSelD0bar >= selectionFlagD0bar); @@ -115,7 +113,7 @@ struct alice3correlatorddbar { void init(InitContext&) { - auto vbins = (std::vector)binsPt; + auto vbins = std::vector(binsPt); registry.add("hMass", "D0,D0bar candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassD0", "D0,D0bar candidates;inv. mass D0 only (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassD0bar", "D0,D0bar candidates;inv. mass D0bar only (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); diff --git a/ALICE3/TableProducer/alice3-dileptonsmearing.cxx b/ALICE3/TableProducer/alice3-dileptonsmearing.cxx index cca45f14c38..4136e34da82 100644 --- a/ALICE3/TableProducer/alice3-dileptonsmearing.cxx +++ b/ALICE3/TableProducer/alice3-dileptonsmearing.cxx @@ -89,8 +89,42 @@ struct alice3dileptonsmearer { Configurable cfgMinPt{"cfgMinPt", -1, "if ptgen is smaller than this threshold, this value is used as input for ptgen."}; } pion_filenames; + struct : ConfigurableGroup { + std::string prefix = "kaon_filename_group"; + Configurable cfgNDSmearing{"cfgNDSmearing", false, "apply ND-correlated smearing"}; + Configurable cfgResFileName{"cfgResFileName", "", "name of resolution file"}; + Configurable cfgResNDHistName{"cfgResNDHistName", "hs_reso", "name of ND resolution file"}; + Configurable cfgResPtHistName{"cfgResPtHistName", "RelPtResArrCocktail", "histogram name for pt in resolution file"}; + Configurable cfgResEtaHistName{"cfgResEtaHistName", "EtaResArr", "histogram name for eta in resolution file"}; + Configurable cfgResPhiPosHistName{"cfgResPhiPosHistName", "PhiPosResArr", "histogram name for phi pos in resolution file"}; + Configurable cfgResPhiNegHistName{"cfgResPhiNegHistName", "PhiEleResArr", "hisogram for phi neg in resolution file"}; + Configurable cfgEffFileName{"cfgEffFileName", "", "name of efficiency file"}; + Configurable cfgEffHistName{"cfgEffHistName", "fhwEffpT", "name of efficiency histogram"}; + Configurable cfgCcdbPathRes{"cfgCcdbPathRes", "", "path to the ccdb object for resolution"}; + Configurable cfgCcdbPathEff{"cfgCcdbPathEff", "", "path to the ccdb object for efficiency"}; + Configurable cfgMinPt{"cfgMinPt", -1, "if ptgen is smaller than this threshold, this value is used as input for ptgen."}; + } kaon_filenames; + + struct : ConfigurableGroup { + std::string prefix = "proton_filename_group"; + Configurable cfgNDSmearing{"cfgNDSmearing", false, "apply ND-correlated smearing"}; + Configurable cfgResFileName{"cfgResFileName", "", "name of resolution file"}; + Configurable cfgResNDHistName{"cfgResNDHistName", "hs_reso", "name of ND resolution file"}; + Configurable cfgResPtHistName{"cfgResPtHistName", "RelPtResArrCocktail", "histogram name for pt in resolution file"}; + Configurable cfgResEtaHistName{"cfgResEtaHistName", "EtaResArr", "histogram name for eta in resolution file"}; + Configurable cfgResPhiPosHistName{"cfgResPhiPosHistName", "PhiPosResArr", "histogram name for phi pos in resolution file"}; + Configurable cfgResPhiNegHistName{"cfgResPhiNegHistName", "PhiEleResArr", "hisogram for phi neg in resolution file"}; + Configurable cfgEffFileName{"cfgEffFileName", "", "name of efficiency file"}; + Configurable cfgEffHistName{"cfgEffHistName", "fhwEffpT", "name of efficiency histogram"}; + Configurable cfgCcdbPathRes{"cfgCcdbPathRes", "", "path to the ccdb object for resolution"}; + Configurable cfgCcdbPathEff{"cfgCcdbPathEff", "", "path to the ccdb object for efficiency"}; + Configurable cfgMinPt{"cfgMinPt", -1, "if ptgen is smaller than this threshold, this value is used as input for ptgen."}; + } proton_filenames; + MomentumSmearer smearer_Electron; MomentumSmearer smearer_Pion; + MomentumSmearer smearer_Kaon; + MomentumSmearer smearer_Proton; Service ccdb; void init(InitContext&) @@ -129,6 +163,32 @@ struct alice3dileptonsmearer { smearer_Pion.setDCAHistName(""); smearer_Pion.setMinPt(pion_filenames.cfgMinPt); + smearer_Kaon.setNDSmearing(kaon_filenames.cfgNDSmearing.value); + smearer_Kaon.setResFileName(TString(kaon_filenames.cfgResFileName)); + smearer_Kaon.setResNDHistName(TString(kaon_filenames.cfgResNDHistName)); + smearer_Kaon.setResPtHistName(TString(kaon_filenames.cfgResPtHistName)); + smearer_Kaon.setResEtaHistName(TString(kaon_filenames.cfgResEtaHistName)); + smearer_Kaon.setResPhiPosHistName(TString(kaon_filenames.cfgResPhiPosHistName)); + smearer_Kaon.setResPhiNegHistName(TString(kaon_filenames.cfgResPhiNegHistName)); + smearer_Kaon.setEffFileName(TString(kaon_filenames.cfgEffFileName)); + smearer_Kaon.setEffHistName(TString(kaon_filenames.cfgEffHistName)); + smearer_Kaon.setDCAFileName(""); + smearer_Kaon.setDCAHistName(""); + smearer_Kaon.setMinPt(kaon_filenames.cfgMinPt); + + smearer_Proton.setNDSmearing(proton_filenames.cfgNDSmearing.value); + smearer_Proton.setResFileName(TString(proton_filenames.cfgResFileName)); + smearer_Proton.setResNDHistName(TString(proton_filenames.cfgResNDHistName)); + smearer_Proton.setResPtHistName(TString(proton_filenames.cfgResPtHistName)); + smearer_Proton.setResEtaHistName(TString(proton_filenames.cfgResEtaHistName)); + smearer_Proton.setResPhiPosHistName(TString(proton_filenames.cfgResPhiPosHistName)); + smearer_Proton.setResPhiNegHistName(TString(proton_filenames.cfgResPhiNegHistName)); + smearer_Proton.setEffFileName(TString(proton_filenames.cfgEffFileName)); + smearer_Proton.setEffHistName(TString(proton_filenames.cfgEffHistName)); + smearer_Proton.setDCAFileName(""); + smearer_Proton.setDCAHistName(""); + smearer_Proton.setMinPt(proton_filenames.cfgMinPt); + if (cfgFromCcdb) { ccdb->setURL(cfgCcdbUrl); ccdb->setCaching(true); @@ -148,10 +208,26 @@ struct alice3dileptonsmearer { smearer_Pion.setCcdbPathDCA(""); smearer_Pion.setTimestamp(timestamp); smearer_Pion.setCcdb(ccdb); + + smearer_Kaon.setCcdbPathRes(TString(kaon_filenames.cfgCcdbPathRes)); + smearer_Kaon.setCcdbPathEff(TString(kaon_filenames.cfgCcdbPathEff)); + // smearer_Kaon.setCcdbPathDCA(TString(kaon_filenames.fConfigCcdbPathDCA)); + smearer_Kaon.setCcdbPathDCA(""); + smearer_Kaon.setTimestamp(timestamp); + smearer_Kaon.setCcdb(ccdb); + + smearer_Proton.setCcdbPathRes(TString(proton_filenames.cfgCcdbPathRes)); + smearer_Proton.setCcdbPathEff(TString(proton_filenames.cfgCcdbPathEff)); + // smearer_Proton.setCcdbPathDCA(TString(proton_filenames.fConfigCcdbPathDCA)); + smearer_Proton.setCcdbPathDCA(""); + smearer_Proton.setTimestamp(timestamp); + smearer_Proton.setCcdb(ccdb); } smearer_Electron.init(); smearer_Pion.init(); + smearer_Kaon.init(); + smearer_Proton.init(); } void processACTSHybrid(MyTracks const& tracks, const aod::McParticles& /*mcParticles*/) @@ -211,6 +287,54 @@ struct alice3dileptonsmearer { } // fill the table smearedelectron(ptsmeared, etasmeared, phismeared, selected); + } else if (std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus) { + int ch = -1; + if (mcParticle.pdgCode() < 0) { + ch = 1; + } + float ptsmeared = 0, etasmeared = 0, phismeared = 0; + // apply smearing for electrons or muons. + smearer_Kaon.applySmearing(centrality, ch, ptgen, etagen, phigen, ptsmeared, etasmeared, phismeared); + // get the efficiency if there, otherwise applied zero + if ((TString(kaon_filenames.cfgCcdbPathEff).CompareTo("") != 0) || (TString(kaon_filenames.cfgEffFileName).CompareTo("") != 0)) { + float efficiency = smearer_Kaon.getEfficiency(ptgen, etagen, phigen); + // Generate a random double between 0 and 1 + double myRandom = gRandom->Uniform(0, 1); + // Select + if (myRandom < efficiency) { + selected = true; + } else { + selected = false; + } + } else { + selected = false; + } + // fill the table + smearedelectron(ptsmeared, etasmeared, phismeared, selected); + } else if (std::abs(mcParticle.pdgCode()) == PDG_t::kProton) { + int ch = -1; + if (mcParticle.pdgCode() < 0) { + ch = 1; + } + float ptsmeared = 0, etasmeared = 0, phismeared = 0; + // apply smearing for electrons or muons. + smearer_Proton.applySmearing(centrality, ch, ptgen, etagen, phigen, ptsmeared, etasmeared, phismeared); + // get the efficiency if there, otherwise applied zero + if ((TString(proton_filenames.cfgCcdbPathEff).CompareTo("") != 0) || (TString(proton_filenames.cfgEffFileName).CompareTo("") != 0)) { + float efficiency = smearer_Proton.getEfficiency(ptgen, etagen, phigen); + // Generate a random double between 0 and 1 + double myRandom = gRandom->Uniform(0, 1); + // Select + if (myRandom < efficiency) { + selected = true; + } else { + selected = false; + } + } else { + selected = false; + } + // fill the table + smearedelectron(ptsmeared, etasmeared, phismeared, selected); } else { // don't apply smearing and reject completely smearedelectron(ptgen, etagen, phigen, false); @@ -247,36 +371,40 @@ struct alice3dileptonchecksmearer { void init(o2::framework::InitContext&) { - registry.add("Electron/hCorrelation_Pt", "pT correlation;p_{T,l}^{gen} (GeV/c);p_{T,l}^{smeared} (GeV/c)", {HistType::kTH2F, {{1000, 0.0f, 10.0f}, {1000, 0.0f, 10.0f}}}); - registry.add("Electron/hCorrelation_Eta", "eta correlation;#eta_{l}^{gen};#eta_{l}^{smeared}", {HistType::kTH2F, {{200, -1.0f, +1.0f}, {200, -1.0f, +1.0f}}}); - registry.add("Electron/hCorrelation_Phi", "phi correlation;#varphi_{l}^{gen} (rad.);#varphi_{l}^{smeared} (rad.)", {HistType::kTH2F, {{100, 0.0f, TMath::TwoPi()}, {100, 0.0f, TMath::TwoPi()}}}); - - // Binning for resolution - AxisSpec axisPtRes{ptResBins, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}; - AxisSpec axisDeltaptRes{deltaptResBins, "(p^{gen}_{T} - p^{rec}_{T}) / p^{gen}_{T}"}; - AxisSpec axisDeltaetaRes{deltaetaResBins, "#eta^{gen} - #eta^{rec}"}; - AxisSpec axisDeltaphiRes{deltaphiResBins, "#varphi^{gen} - #varphi^{rec} (rad.)"}; - // Binning for efficiency - AxisSpec axiseta{etaBins, "#eta"}; - - if (!cfgUsePtVecRes) { - registry.add("Electron/PtGen_DeltaPtOverPtGen", "", HistType::kTH2D, {axisPtRes, axisDeltaptRes}, true); - registry.add("Electron/PtGen_DeltaEta", "", HistType::kTH2D, {axisPtRes, axisDeltaetaRes}, true); - registry.add("Electron/PtGen_DeltaPhi_Neg", "", HistType::kTH2D, {axisPtRes, axisDeltaphiRes}, true); - registry.add("Electron/PtGen_DeltaPhi_Pos", "", HistType::kTH2D, {axisPtRes, axisDeltaphiRes}, true); - registry.add("Electron/PtEtaGen", "", HistType::kTH2D, {axisPtRes, axiseta}, true); - registry.add("Electron/PtEtaRec", "", HistType::kTH2D, {axisPtRes, axiseta}, true); - } else { - registry.add("Electron/PtGen_DeltaPtOverPtGen", "", HistType::kTH2D, {{ptResBinsVec, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}, axisDeltaptRes}, true); - registry.add("Electron/PtGen_DeltaEta", "", HistType::kTH2D, {{ptResBinsVec, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}, axisDeltaetaRes}, true); - registry.add("Electron/PtGen_DeltaPhi_Neg", "", HistType::kTH2D, {{ptResBinsVec, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}, axisDeltaphiRes}, true); - registry.add("Electron/PtGen_DeltaPhi_Pos", "", HistType::kTH2D, {{ptResBinsVec, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}, axisDeltaphiRes}, true); - registry.add("Electron/PtEtaGen", "", HistType::kTH2D, {{ptResBinsVec, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}, axiseta}, true); - registry.add("Electron/PtEtaRec", "", HistType::kTH2D, {{ptResBinsVec, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}, axiseta}, true); - } + if (doprocessCheckACTSHybrid) { + registry.add("Electron/hCorrelation_Pt", "pT correlation;p_{T,l}^{gen} (GeV/c);p_{T,l}^{smeared} (GeV/c)", {HistType::kTH2F, {{1000, 0.0f, 10.0f}, {1000, 0.0f, 10.0f}}}); + registry.add("Electron/hCorrelation_Eta", "eta correlation;#eta_{l}^{gen};#eta_{l}^{smeared}", {HistType::kTH2F, {{200, -1.0f, +1.0f}, {200, -1.0f, +1.0f}}}); + registry.add("Electron/hCorrelation_Phi", "phi correlation;#varphi_{l}^{gen} (rad.);#varphi_{l}^{smeared} (rad.)", {HistType::kTH2F, {{100, 0.0f, TMath::TwoPi()}, {100, 0.0f, TMath::TwoPi()}}}); + + // Binning for resolution + AxisSpec axisPtRes{ptResBins, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}; + AxisSpec axisDeltaptRes{deltaptResBins, "(p^{gen}_{T} - p^{rec}_{T}) / p^{gen}_{T}"}; + AxisSpec axisDeltaetaRes{deltaetaResBins, "#eta^{gen} - #eta^{rec}"}; + AxisSpec axisDeltaphiRes{deltaphiResBins, "#varphi^{gen} - #varphi^{rec} (rad.)"}; + // Binning for efficiency + AxisSpec axiseta{etaBins, "#eta"}; + + if (!cfgUsePtVecRes) { + registry.add("Electron/PtGen_DeltaPtOverPtGen", "", HistType::kTH2D, {axisPtRes, axisDeltaptRes}, true); + registry.add("Electron/PtGen_DeltaEta", "", HistType::kTH2D, {axisPtRes, axisDeltaetaRes}, true); + registry.add("Electron/PtGen_DeltaPhi_Neg", "", HistType::kTH2D, {axisPtRes, axisDeltaphiRes}, true); + registry.add("Electron/PtGen_DeltaPhi_Pos", "", HistType::kTH2D, {axisPtRes, axisDeltaphiRes}, true); + registry.add("Electron/PtEtaGen", "", HistType::kTH2D, {axisPtRes, axiseta}, true); + registry.add("Electron/PtEtaRec", "", HistType::kTH2D, {axisPtRes, axiseta}, true); + } else { + registry.add("Electron/PtGen_DeltaPtOverPtGen", "", HistType::kTH2D, {{ptResBinsVec, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}, axisDeltaptRes}, true); + registry.add("Electron/PtGen_DeltaEta", "", HistType::kTH2D, {{ptResBinsVec, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}, axisDeltaetaRes}, true); + registry.add("Electron/PtGen_DeltaPhi_Neg", "", HistType::kTH2D, {{ptResBinsVec, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}, axisDeltaphiRes}, true); + registry.add("Electron/PtGen_DeltaPhi_Pos", "", HistType::kTH2D, {{ptResBinsVec, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}, axisDeltaphiRes}, true); + registry.add("Electron/PtEtaGen", "", HistType::kTH2D, {{ptResBinsVec, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}, axiseta}, true); + registry.add("Electron/PtEtaRec", "", HistType::kTH2D, {{ptResBinsVec, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}, axiseta}, true); + } - registry.addClone("Electron/", "Others/"); - registry.addClone("Electron/", "Pions/"); + registry.addClone("Electron/", "Other/"); + registry.addClone("Electron/", "Pion/"); + registry.addClone("Electron/", "Kaon/"); + registry.addClone("Electron/", "Proton/"); + } } void processCheckACTSHybrid(MyTracksWithSmearing const& tracks, const aod::McParticles& /*mcParticles*/) @@ -309,36 +437,68 @@ struct alice3dileptonchecksmearer { registry.fill(HIST("Electron/PtEtaRec"), mcParticle.pt(), mcParticle.eta()); } } else if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus) { - registry.fill(HIST("Pions/PtGen_DeltaPtOverPtGen"), mcParticle.pt(), deltaptoverpt); - registry.fill(HIST("Pions/PtGen_DeltaEta"), mcParticle.pt(), deltaeta); + registry.fill(HIST("Pion/PtGen_DeltaPtOverPtGen"), mcParticle.pt(), deltaptoverpt); + registry.fill(HIST("Pion/PtGen_DeltaEta"), mcParticle.pt(), deltaeta); + if (mcParticle.pdgCode() < 0) { // e+ + registry.fill(HIST("Pion/PtGen_DeltaPhi_Pos"), mcParticle.pt(), deltaphi); + } else { // e- + registry.fill(HIST("Pion/PtGen_DeltaPhi_Neg"), mcParticle.pt(), deltaphi); + } + registry.fill(HIST("Pion/hCorrelation_Pt"), mcParticle.pt(), track.ptSmeared()); + registry.fill(HIST("Pion/hCorrelation_Eta"), mcParticle.eta(), track.etaSmeared()); + registry.fill(HIST("Pion/hCorrelation_Phi"), mcParticle.phi(), track.phiSmeared()); + // efficiency + registry.fill(HIST("Pion/PtEtaGen"), mcParticle.pt(), mcParticle.eta()); + if (track.selected()) { + registry.fill(HIST("Pion/PtEtaRec"), mcParticle.pt(), mcParticle.eta()); + } + } else if (std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus) { + registry.fill(HIST("Kaon/PtGen_DeltaPtOverPtGen"), mcParticle.pt(), deltaptoverpt); + registry.fill(HIST("Kaon/PtGen_DeltaEta"), mcParticle.pt(), deltaeta); + if (mcParticle.pdgCode() < 0) { // e+ + registry.fill(HIST("Kaon/PtGen_DeltaPhi_Pos"), mcParticle.pt(), deltaphi); + } else { // e- + registry.fill(HIST("Kaon/PtGen_DeltaPhi_Neg"), mcParticle.pt(), deltaphi); + } + registry.fill(HIST("Kaon/hCorrelation_Pt"), mcParticle.pt(), track.ptSmeared()); + registry.fill(HIST("Kaon/hCorrelation_Eta"), mcParticle.eta(), track.etaSmeared()); + registry.fill(HIST("Kaon/hCorrelation_Phi"), mcParticle.phi(), track.phiSmeared()); + // efficiency + registry.fill(HIST("Kaon/PtEtaGen"), mcParticle.pt(), mcParticle.eta()); + if (track.selected()) { + registry.fill(HIST("Kaon/PtEtaRec"), mcParticle.pt(), mcParticle.eta()); + } + } else if (std::abs(mcParticle.pdgCode()) == PDG_t::kProton) { + registry.fill(HIST("Proton/PtGen_DeltaPtOverPtGen"), mcParticle.pt(), deltaptoverpt); + registry.fill(HIST("Proton/PtGen_DeltaEta"), mcParticle.pt(), deltaeta); if (mcParticle.pdgCode() < 0) { // e+ - registry.fill(HIST("Pions/PtGen_DeltaPhi_Pos"), mcParticle.pt(), deltaphi); + registry.fill(HIST("Proton/PtGen_DeltaPhi_Pos"), mcParticle.pt(), deltaphi); } else { // e- - registry.fill(HIST("Pions/PtGen_DeltaPhi_Neg"), mcParticle.pt(), deltaphi); + registry.fill(HIST("Proton/PtGen_DeltaPhi_Neg"), mcParticle.pt(), deltaphi); } - registry.fill(HIST("Pions/hCorrelation_Pt"), mcParticle.pt(), track.ptSmeared()); - registry.fill(HIST("Pions/hCorrelation_Eta"), mcParticle.eta(), track.etaSmeared()); - registry.fill(HIST("Pions/hCorrelation_Phi"), mcParticle.phi(), track.phiSmeared()); + registry.fill(HIST("Proton/hCorrelation_Pt"), mcParticle.pt(), track.ptSmeared()); + registry.fill(HIST("Proton/hCorrelation_Eta"), mcParticle.eta(), track.etaSmeared()); + registry.fill(HIST("Proton/hCorrelation_Phi"), mcParticle.phi(), track.phiSmeared()); // efficiency - registry.fill(HIST("Pions/PtEtaGen"), mcParticle.pt(), mcParticle.eta()); + registry.fill(HIST("Proton/PtEtaGen"), mcParticle.pt(), mcParticle.eta()); if (track.selected()) { - registry.fill(HIST("Pions/PtEtaRec"), mcParticle.pt(), mcParticle.eta()); + registry.fill(HIST("Proton/PtEtaRec"), mcParticle.pt(), mcParticle.eta()); } } else { - registry.fill(HIST("Others/PtGen_DeltaPtOverPtGen"), mcParticle.pt(), deltaptoverpt); - registry.fill(HIST("Others/PtGen_DeltaEta"), mcParticle.pt(), deltaeta); + registry.fill(HIST("Other/PtGen_DeltaPtOverPtGen"), mcParticle.pt(), deltaptoverpt); + registry.fill(HIST("Other/PtGen_DeltaEta"), mcParticle.pt(), deltaeta); if (mcParticle.pdgCode() < 0) { // e+ - registry.fill(HIST("Others/PtGen_DeltaPhi_Pos"), mcParticle.pt(), deltaphi); + registry.fill(HIST("Other/PtGen_DeltaPhi_Pos"), mcParticle.pt(), deltaphi); } else { // e- - registry.fill(HIST("Others/PtGen_DeltaPhi_Neg"), mcParticle.pt(), deltaphi); + registry.fill(HIST("Other/PtGen_DeltaPhi_Neg"), mcParticle.pt(), deltaphi); } - registry.fill(HIST("Others/hCorrelation_Pt"), mcParticle.pt(), track.ptSmeared()); - registry.fill(HIST("Others/hCorrelation_Eta"), mcParticle.eta(), track.etaSmeared()); - registry.fill(HIST("Others/hCorrelation_Phi"), mcParticle.phi(), track.phiSmeared()); + registry.fill(HIST("Other/hCorrelation_Pt"), mcParticle.pt(), track.ptSmeared()); + registry.fill(HIST("Other/hCorrelation_Eta"), mcParticle.eta(), track.etaSmeared()); + registry.fill(HIST("Other/hCorrelation_Phi"), mcParticle.phi(), track.phiSmeared()); // efficiency - registry.fill(HIST("Others/PtEtaGen"), mcParticle.pt(), mcParticle.eta()); + registry.fill(HIST("Other/PtEtaGen"), mcParticle.pt(), mcParticle.eta()); if (track.selected()) { - registry.fill(HIST("Others/PtEtaRec"), mcParticle.pt(), mcParticle.eta()); + registry.fill(HIST("Other/PtEtaRec"), mcParticle.pt(), mcParticle.eta()); } } } // end of loop diff --git a/ALICE3/TableProducer/alice3DqTableMaker.cxx b/ALICE3/TableProducer/alice3DqTableMaker.cxx index 041fc71ebfe..1cbbb4fa8a9 100644 --- a/ALICE3/TableProducer/alice3DqTableMaker.cxx +++ b/ALICE3/TableProducer/alice3DqTableMaker.cxx @@ -301,7 +301,7 @@ struct Alice3DqTableMaker { fStatsList.setObject(new TList()); fStatsList->SetOwner(true); std::vector eventLabels{"Collisions before filtering", "Before cuts", "After cuts"}; - TH2I* histEvents = new TH2I("EventStats", "Event statistics", eventLabels.size(), -0.5, eventLabels.size() - 0.5, o2::aod::evsel::kNsel + 1, -0.5, (float)o2::aod::evsel::kNsel + 0.5); + TH2I* histEvents = new TH2I("EventStats", "Event statistics", eventLabels.size(), -0.5, eventLabels.size() - 0.5, o2::aod::evsel::kNsel + 1, -0.5, static_cast(o2::aod::evsel::kNsel) + 0.5); int ibX = 1; for (auto label = eventLabels.begin(); label != eventLabels.end(); label++, ibX++) { histEvents->GetXaxis()->SetBinLabel(ibX, (*label).Data()); @@ -583,7 +583,7 @@ struct Alice3DqTableMaker { if (fDoDetailedQA) { j = 0; for (const auto& cut : fTrackCuts) { - if ((trackTempFilterMap & (uint8_t(1) << j)) != 0u) { + if ((trackTempFilterMap & (static_cast(1) << j)) != 0u) { fHistMan->FillHistClass(Form("TrackBarrel_%s_%s", cut->GetName(), sig->GetName()), dqefficiency_helpers::varValues()); // fill the reconstructed truth } j++; diff --git a/ALICE3/TableProducer/alice3HfSelector3Prong.cxx b/ALICE3/TableProducer/alice3HfSelector3Prong.cxx index 89c34ff1cf3..f502842ef02 100644 --- a/ALICE3/TableProducer/alice3HfSelector3Prong.cxx +++ b/ALICE3/TableProducer/alice3HfSelector3Prong.cxx @@ -93,7 +93,6 @@ struct Alice3HfSelector3Prong { Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB"}; Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; - HfHelperAlice3 hfHelper; o2::analysis::HfMlResponse3Prong mlResponse; o2::ccdb::CcdbApi ccdbApi; @@ -114,7 +113,7 @@ struct Alice3HfSelector3Prong { labels[1 + aod::SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } @@ -236,7 +235,7 @@ struct Alice3HfSelector3Prong { template bool selectionCandidateMass(int const ptBin, const TCandidate& cand) { - const float massCand = hfHelper.getCandMass(cand); + const float massCand = HfHelperAlice3::getCandMass(cand); // cut on mass window if (std::abs(massCand - MassReference) > cuts->get(ptBin, "m")) { return false; diff --git a/ALICE3/TableProducer/alice3HfTreeCreator3Prong.cxx b/ALICE3/TableProducer/alice3HfTreeCreator3Prong.cxx index 7a97f1420ff..bc456974afb 100644 --- a/ALICE3/TableProducer/alice3HfTreeCreator3Prong.cxx +++ b/ALICE3/TableProducer/alice3HfTreeCreator3Prong.cxx @@ -272,8 +272,6 @@ struct Alice3HfTreeCreator3Prong { Configurable downSampleFactor{"downSampleFactor", 1., "Fraction of cands to keep"}; Configurable ptMaxForDownSample{"ptMaxForDownSample", 10., "Maximum pt for the application of the downsampling factor"}; - HfHelperAlice3 hfHelper; - using CandsLcRec = soa::Filtered>; using CandsLcRecWMl = soa::Filtered>; using CandsMcGen = soa::Join; @@ -399,7 +397,7 @@ struct Alice3HfTreeCreator3Prong { rowCandKine3Ps( cand.ptProng0(), cand.ptProng1(), cand.ptProng2(), cand.pt(), RecoDecay::p(cand.px(), cand.py(), cand.pz()), cand.eta(), cand.phi(), - hfHelper.getCandMass(cand), hfHelper.getCandY(cand), hfHelper.getCandEnergy(cand)); + HfHelperAlice3::getCandMass(cand), HfHelperAlice3::getCandY(cand), HfHelperAlice3::getCandEnergy(cand)); } if (fillTables.fillDaugKineInfo) { rowDaugMoms( @@ -442,7 +440,7 @@ struct Alice3HfTreeCreator3Prong { { for (const auto& part : parts) { if (fillTables.fillCandGenKineInfo) { - rowCand3PGen(part.pt(), part.eta(), part.phi(), hfHelper.getCandY(part)); + rowCand3PGen(part.pt(), part.eta(), part.phi(), HfHelperAlice3::getCandY(part)); } if (fillTables.fillMcMatchGenInfo) { rowCand3PMcMatchGen(part.flagMcGen(), part.originMcGen()); diff --git a/ALICE3/TableProducer/alice3TrackingTranslator.cxx b/ALICE3/TableProducer/alice3TrackingTranslator.cxx index 983092eb228..e82d7f663de 100644 --- a/ALICE3/TableProducer/alice3TrackingTranslator.cxx +++ b/ALICE3/TableProducer/alice3TrackingTranslator.cxx @@ -100,13 +100,8 @@ struct Alice3TrackingTranslator { LOG(info) << "Alice3TrackingTranslator init called"; } -#define SETADDRESS(branchname, branchvar) \ - if (mTree->SetBranchAddress(branchname, &branchvar)) { \ - LOG(fatal) << "Could not set branch address for " << branchname; \ - } - struct FileStruct { - FileStruct(const std::string& filename, const std::string& treename) : mFile(filename.c_str(), "READ") + FileStruct(const std::string& filename, const std::string& treename) : mFile(filename.c_str(), "READ"), mTree(nullptr) { if (mFile.IsZombie()) { LOG(fatal) << "Could not open file '" << filename << "'"; @@ -118,6 +113,13 @@ struct Alice3TrackingTranslator { LOG(fatal) << treename << " tree not found in " << filename; } } + template + void setBranchAddress(const char* branchname, T& branchvar) + { + if (mTree->SetBranchAddress(branchname, &branchvar)) { + LOG(fatal) << "Could not set branch address for " << branchname; + } + } void setEventEntry(Long64_t entry) { if (mTree->GetEntry(entry) < 0) { @@ -126,30 +128,30 @@ struct Alice3TrackingTranslator { } Long64_t getEntries() const { return mTree->GetEntries(); } TFile mFile; - TTree* mTree; + TTree* mTree{nullptr}; }; struct ParticleStruct : public FileStruct { - ParticleStruct(const std::string& filename, const std::string& treename) : FileStruct(std::move(filename), std::move(treename)) + ParticleStruct(const std::string& filename, const std::string& treename) : FileStruct(filename, treename) { // mTree->Print(); - SETADDRESS("particle_type", m_particle_type); - SETADDRESS("vx", m_vx); - SETADDRESS("vy", m_vy); - SETADDRESS("vz", m_vz); - SETADDRESS("vt", m_vt); - SETADDRESS("px", m_px); - SETADDRESS("py", m_py); - SETADDRESS("pz", m_pz); - SETADDRESS("m", m_m); - SETADDRESS("p", m_p); - SETADDRESS("q", m_q); - SETADDRESS("number_of_hits", m_number_of_hits); - SETADDRESS("vertex_primary", m_vertex_primary); - SETADDRESS("vertex_secondary", m_vertex_secondary); - SETADDRESS("generation", m_generation); - SETADDRESS("sub_particle", m_sub_particle); - SETADDRESS("particle", m_particle); + setBranchAddress("particle_type", m_particle_type); + setBranchAddress("vx", m_vx); + setBranchAddress("vy", m_vy); + setBranchAddress("vz", m_vz); + setBranchAddress("vt", m_vt); + setBranchAddress("px", m_px); + setBranchAddress("py", m_py); + setBranchAddress("pz", m_pz); + setBranchAddress("m", m_m); + setBranchAddress("p", m_p); + setBranchAddress("q", m_q); + setBranchAddress("number_of_hits", m_number_of_hits); + setBranchAddress("vertex_primary", m_vertex_primary); + setBranchAddress("vertex_secondary", m_vertex_secondary); + setBranchAddress("generation", m_generation); + setBranchAddress("sub_particle", m_sub_particle); + setBranchAddress("particle", m_particle); } std::vector* m_particle_type = nullptr; std::vector* m_vx = nullptr; @@ -175,20 +177,20 @@ struct Alice3TrackingTranslator { struct VertexStruct : public FileStruct { VertexStruct(const std::string& filename, const std::string& treename) : FileStruct(filename, treename) { - SETADDRESS("vx", m_x); - SETADDRESS("vy", m_y); - SETADDRESS("vz", m_z); - SETADDRESS("vt", m_t); - SETADDRESS("incoming_particles_vertex_primary", m_incoming_particles_vertex_primary); - SETADDRESS("incoming_particles_vertex_secondary", m_incoming_particles_vertex_secondary); - SETADDRESS("incoming_particles_generation", m_incoming_particles_generation); - SETADDRESS("incoming_particles_sub_particle", m_incoming_particles_sub_particle); - SETADDRESS("incoming_particles_particle", m_incoming_particles_particle); - SETADDRESS("outgoing_particles_vertex_primary", m_outgoing_particles_vertex_primary); - SETADDRESS("outgoing_particles_vertex_secondary", m_outgoing_particles_vertex_secondary); - SETADDRESS("outgoing_particles_generation", m_outgoing_particles_generation); - SETADDRESS("outgoing_particles_sub_particle", m_outgoing_particles_sub_particle); - SETADDRESS("outgoing_particles_particle", m_outgoing_particles_particle); + setBranchAddress("vx", m_x); + setBranchAddress("vy", m_y); + setBranchAddress("vz", m_z); + setBranchAddress("vt", m_t); + setBranchAddress("incoming_particles_vertex_primary", m_incoming_particles_vertex_primary); + setBranchAddress("incoming_particles_vertex_secondary", m_incoming_particles_vertex_secondary); + setBranchAddress("incoming_particles_generation", m_incoming_particles_generation); + setBranchAddress("incoming_particles_sub_particle", m_incoming_particles_sub_particle); + setBranchAddress("incoming_particles_particle", m_incoming_particles_particle); + setBranchAddress("outgoing_particles_vertex_primary", m_outgoing_particles_vertex_primary); + setBranchAddress("outgoing_particles_vertex_secondary", m_outgoing_particles_vertex_secondary); + setBranchAddress("outgoing_particles_generation", m_outgoing_particles_generation); + setBranchAddress("outgoing_particles_sub_particle", m_outgoing_particles_sub_particle); + setBranchAddress("outgoing_particles_particle", m_outgoing_particles_particle); } std::vector* m_x = nullptr; std::vector* m_y = nullptr; @@ -207,58 +209,58 @@ struct Alice3TrackingTranslator { }; struct TrackStruct : public FileStruct { - TrackStruct(const std::string& filename, const std::string& treename) : FileStruct(std::move(filename), std::move(treename)) + TrackStruct(const std::string& filename, const std::string& treename) : FileStruct(filename, treename) { mTree->Print(); // Set branch addresses for ACTS track parameters - SETADDRESS("event_nr", m_event_nr); - SETADDRESS("nMeasurements", m_nMeasurements); - SETADDRESS("nStates", m_nStates); - SETADDRESS("nHoles", m_nHoles); - SETADDRESS("chi2Sum", m_chi2Sum); - SETADDRESS("NDF", m_NDF); - SETADDRESS("nMajorityHits", m_nMajorityHits); - SETADDRESS("t_charge", m_t_charge); - SETADDRESS("t_vx", m_t_vx); - SETADDRESS("t_vy", m_t_vy); - SETADDRESS("t_vz", m_t_vz); - SETADDRESS("t_px", m_t_px); - SETADDRESS("t_py", m_t_py); - SETADDRESS("t_pz", m_t_pz); - SETADDRESS("majorityParticleId_vertex_primary", m_majorityParticleId_vertex_primary); - SETADDRESS("majorityParticleId_vertex_secondary", m_majorityParticleId_vertex_secondary); - SETADDRESS("majorityParticleId_generation", m_majorityParticleId_generation); - SETADDRESS("majorityParticleId_sub_particle", m_majorityParticleId_sub_particle); - SETADDRESS("majorityParticleId_particle", m_majorityParticleId_particle); - SETADDRESS("eQOP_fit", m_eQOP_fit); - SETADDRESS("eX_fit", m_eX_fit); - SETADDRESS("eY_fit", m_eY_fit); - SETADDRESS("eZ_fit", m_eZ_fit); - SETADDRESS("ePX_fit", m_ePX_fit); - SETADDRESS("ePY_fit", m_ePY_fit); - SETADDRESS("ePZ_fit", m_ePZ_fit); - SETADDRESS("cov_eX_eX", m_cov_eX_eX); - // SETADDRESS("cov_eY_eY", m_cov_eY_eY); - SETADDRESS("cov_eZ_eZ", m_cov_eZ_eZ); - SETADDRESS("cov_ePX_ePX", m_cov_ePX_ePX); - SETADDRESS("cov_ePY_ePY", m_cov_ePY_ePY); - SETADDRESS("cov_ePZ_ePZ", m_cov_ePZ_ePZ); - SETADDRESS("cov_eX_ePX", m_cov_eX_ePX); - SETADDRESS("cov_eY_ePY", m_cov_eY_ePY); - SETADDRESS("cov_eZ_ePZ", m_cov_eZ_ePZ); - SETADDRESS("cov_eX_eY", m_cov_eX_eY); - SETADDRESS("cov_eX_eZ", m_cov_eX_eZ); - SETADDRESS("cov_eY_eZ", m_cov_eY_eZ); - SETADDRESS("cov_ePX_ePY", m_cov_ePX_ePY); - SETADDRESS("cov_ePX_ePZ", m_cov_ePX_ePZ); - SETADDRESS("cov_ePY_ePZ", m_cov_ePY_ePZ); - SETADDRESS("cov_eX_ePY", m_cov_eX_ePY); - SETADDRESS("cov_eX_ePZ", m_cov_eX_ePZ); - SETADDRESS("cov_eY_ePX", m_cov_eY_ePX); - SETADDRESS("cov_eY_ePZ", m_cov_eY_ePZ); - SETADDRESS("cov_eZ_ePX", m_cov_eZ_ePX); - SETADDRESS("cov_eZ_ePY", m_cov_eZ_ePY); + setBranchAddress("event_nr", m_event_nr); + setBranchAddress("nMeasurements", m_nMeasurements); + setBranchAddress("nStates", m_nStates); + setBranchAddress("nHoles", m_nHoles); + setBranchAddress("chi2Sum", m_chi2Sum); + setBranchAddress("NDF", m_NDF); + setBranchAddress("nMajorityHits", m_nMajorityHits); + setBranchAddress("t_charge", m_t_charge); + setBranchAddress("t_vx", m_t_vx); + setBranchAddress("t_vy", m_t_vy); + setBranchAddress("t_vz", m_t_vz); + setBranchAddress("t_px", m_t_px); + setBranchAddress("t_py", m_t_py); + setBranchAddress("t_pz", m_t_pz); + setBranchAddress("majorityParticleId_vertex_primary", m_majorityParticleId_vertex_primary); + setBranchAddress("majorityParticleId_vertex_secondary", m_majorityParticleId_vertex_secondary); + setBranchAddress("majorityParticleId_generation", m_majorityParticleId_generation); + setBranchAddress("majorityParticleId_sub_particle", m_majorityParticleId_sub_particle); + setBranchAddress("majorityParticleId_particle", m_majorityParticleId_particle); + setBranchAddress("eQOP_fit", m_eQOP_fit); + setBranchAddress("eX_fit", m_eX_fit); + setBranchAddress("eY_fit", m_eY_fit); + setBranchAddress("eZ_fit", m_eZ_fit); + setBranchAddress("ePX_fit", m_ePX_fit); + setBranchAddress("ePY_fit", m_ePY_fit); + setBranchAddress("ePZ_fit", m_ePZ_fit); + setBranchAddress("cov_eX_eX", m_cov_eX_eX); + setBranchAddress("cov_eY_eY", m_cov_eY_eY); + setBranchAddress("cov_eZ_eZ", m_cov_eZ_eZ); + setBranchAddress("cov_ePX_ePX", m_cov_ePX_ePX); + setBranchAddress("cov_ePY_ePY", m_cov_ePY_ePY); + setBranchAddress("cov_ePZ_ePZ", m_cov_ePZ_ePZ); + setBranchAddress("cov_eX_ePX", m_cov_eX_ePX); + setBranchAddress("cov_eY_ePY", m_cov_eY_ePY); + setBranchAddress("cov_eZ_ePZ", m_cov_eZ_ePZ); + setBranchAddress("cov_eX_eY", m_cov_eX_eY); + setBranchAddress("cov_eX_eZ", m_cov_eX_eZ); + setBranchAddress("cov_eY_eZ", m_cov_eY_eZ); + setBranchAddress("cov_ePX_ePY", m_cov_ePX_ePY); + setBranchAddress("cov_ePX_ePZ", m_cov_ePX_ePZ); + setBranchAddress("cov_ePY_ePZ", m_cov_ePY_ePZ); + setBranchAddress("cov_eX_ePY", m_cov_eX_ePY); + setBranchAddress("cov_eX_ePZ", m_cov_eX_ePZ); + setBranchAddress("cov_eY_ePX", m_cov_eY_ePX); + setBranchAddress("cov_eY_ePZ", m_cov_eY_ePZ); + setBranchAddress("cov_eZ_ePX", m_cov_eZ_ePX); + setBranchAddress("cov_eZ_ePY", m_cov_eZ_ePY); } // Define track-related members here UInt_t* m_event_nr = nullptr; @@ -278,8 +280,8 @@ struct Alice3TrackingTranslator { std::vector* m_ePZ_fit = nullptr; // global momentum pz // covariance matrices for fitted track parameters (if available) - std::vector* m_cov_eX_eX = nullptr; // covariance of global position x - // std::vector* m_cov_eY_eY = nullptr; // covariance of global position y + std::vector* m_cov_eX_eX = nullptr; // covariance of global position x + std::vector* m_cov_eY_eY = nullptr; // covariance of global position y std::vector* m_cov_eZ_eZ = nullptr; // covariance of global position z std::vector* m_cov_ePX_ePX = nullptr; // covariance of global momentum px std::vector* m_cov_ePY_ePY = nullptr; // covariance of global momentum py @@ -319,34 +321,35 @@ struct Alice3TrackingTranslator { }; struct HitsStruct : public FileStruct { - HitsStruct(const std::string& filename, const std::string& treename) : FileStruct(std::move(filename), std::move(treename)) + HitsStruct(const std::string& filename, const std::string& treename) : FileStruct(filename, treename) { mTree->Print(); - SETADDRESS("barcode", barcode); + setBranchAddress("barcode", barcode); } std::vector* barcode = nullptr; }; - void addMCParticle(int collIndex, ParticleStruct& fileParticles, int iParticle, uint8_t flags, int firstMother, int firstDaughter, int secondDaughter, int numberOfHits) + void addMCParticle(int collIndex, ParticleStruct& fileParticles, int iParticle, uint8_t flags, int firstMother, int firstDaughter, int secondDaughter, int numberOfHits, float secondaryVertexRadius) { - int mothers[2] = {firstMother, firstMother}; - int daughters[2] = {firstDaughter, secondDaughter}; + std::array mothers = {firstMother, firstMother}; + std::array daughters = {firstDaughter, secondDaughter}; tableStoredMcParticles(collIndex, // mcCollisionId fileParticles.m_particle_type->at(iParticle), // pdgCode 0, // statusCode flags, // flags mothers, // mothersIds - daughters, // daughtersIdSlice + daughters.data(), // daughtersIdSlice 1.0f, // weight fileParticles.m_px->at(iParticle), // m_px fileParticles.m_py->at(iParticle), // m_py fileParticles.m_pz->at(iParticle), // m_pz std::hypot(fileParticles.m_p->at(iParticle), fileParticles.m_m->at(iParticle)), // e - fileParticles.m_vx->at(iParticle), // m_vx - fileParticles.m_vy->at(iParticle), // m_vy - fileParticles.m_vz->at(iParticle), // m_vz - fileParticles.m_vt->at(iParticle)); // m_vt + fileParticles.m_vx->at(iParticle) * 0.1, // m_vx + fileParticles.m_vy->at(iParticle) * 0.1, // m_vy + fileParticles.m_vz->at(iParticle) * 0.1, // m_vz + fileParticles.m_vt->at(iParticle) * 0.1); // m_vt tableMCParticlesExtraA3(numberOfHits, // number_of_hits - fileParticles.m_q->at(iParticle)); // charge + fileParticles.m_q->at(iParticle), // charge + secondaryVertexRadius); // secondary_vertex_radius }; void process(o2::aod::BCs const&) @@ -359,8 +362,8 @@ struct Alice3TrackingTranslator { TList* filesList = dir.GetListOfFiles(); if (filesList) { TIter next(filesList); - TSystemFile* file; - while ((file = static_cast(next()))) { + TSystemFile* file = nullptr; + while ((file = dynamic_cast(next()))) { TString fname = file->GetName(); LOG(debug) << "Found file: " << fname.Data(); if (!file->IsDirectory() && fname.EndsWith(".root")) { @@ -419,18 +422,20 @@ struct Alice3TrackingTranslator { // Convert tracks from ACTS to ALICE format const size_t nParticlesSim = fileParticlesSim.m_vx->size(); const size_t nTracks = fileTracksummary.m_ePX_fit->size(); - std::vector idMCparticles; + // std::vector idMCparticles; // fixme: unused // local index k within this event -> global AO2D index int firstIdxThisEvent = tableStoredMcParticles.lastIndex() + 1; // barcode -> global AO2D index (used later for track labels) std::map barcodeToGlobalIdx; - std::map> barcodeToVertexPosition; + std::map> globalToVertexPosition; std::map GlobalIdxToMotherIdx; + std::map GlobalIdxSecVertexRadiusIncoming; std::map GlobalIdxToPDGCode; std::map GlobalToLocalIdx; std::map> GlobalIdxToDaughterIdxs; + for (size_t iPart = 0; iPart < nParticlesSim; ++iPart) { Key key = keyOf( fileParticlesSim.m_vertex_primary->at(iPart), @@ -438,7 +443,7 @@ struct Alice3TrackingTranslator { fileParticlesSim.m_particle->at(iPart), fileParticlesSim.m_generation->at(iPart), fileParticlesSim.m_sub_particle->at(iPart)); - barcodeToGlobalIdx[key] = firstIdxThisEvent + (int)iPart; + barcodeToGlobalIdx[key] = firstIdxThisEvent + static_cast(iPart); if (iPart == 0) { tableMcCollisions(0, // mccollision::BCId, @@ -465,6 +470,7 @@ struct Alice3TrackingTranslator { auto iteratorMother = barcodeToGlobalIdx.find(kd); if (iteratorMother != barcodeToGlobalIdx.end()) { int motherIdx = iteratorMother->second; + GlobalIdxSecVertexRadiusIncoming[motherIdx] = std::hypot(fileVertices.m_x->at(j) * 0.1, fileVertices.m_y->at(j) * 0.1); for (size_t d2 = 0; d2 < (*fileVertices.m_outgoing_particles_vertex_secondary)[j].size(); ++d2) { Key kd2 = keyOf( (*fileVertices.m_outgoing_particles_vertex_primary)[j][d2], @@ -472,12 +478,12 @@ struct Alice3TrackingTranslator { (*fileVertices.m_outgoing_particles_particle)[j][d2], (*fileVertices.m_outgoing_particles_generation)[j][d2], (*fileVertices.m_outgoing_particles_sub_particle)[j][d2]); - barcodeToVertexPosition[kd2].push_back(fileVertices.m_x->at(j)); - barcodeToVertexPosition[kd2].push_back(fileVertices.m_y->at(j)); - barcodeToVertexPosition[kd2].push_back(fileVertices.m_z->at(j)); auto iteratorDaughter = barcodeToGlobalIdx.find(kd2); if (iteratorDaughter != barcodeToGlobalIdx.end()) { int daughterIdx = iteratorDaughter->second; + globalToVertexPosition[daughterIdx].push_back(fileVertices.m_x->at(j)); + globalToVertexPosition[daughterIdx].push_back(fileVertices.m_y->at(j)); + globalToVertexPosition[daughterIdx].push_back(fileVertices.m_z->at(j)); GlobalIdxToMotherIdx[daughterIdx] = motherIdx; GlobalIdxToDaughterIdxs[motherIdx].push_back(daughterIdx); } @@ -486,21 +492,29 @@ struct Alice3TrackingTranslator { } } for (size_t iPart = 0; iPart < nParticlesSim; ++iPart) { - int globalIdx = firstIdxThisEvent + (int)iPart; + float secondaryVertexRadius = -1.0f; + int globalIdx = firstIdxThisEvent + static_cast(iPart); int motherIdx = -1; - if (GlobalIdxToMotherIdx.find(globalIdx) != GlobalIdxToMotherIdx.end()) { + if (GlobalIdxToMotherIdx.contains(globalIdx)) { motherIdx = GlobalIdxToMotherIdx[globalIdx]; } int firstDaughter = -1; int secondDaughter = -1; - if (GlobalIdxToDaughterIdxs.find(globalIdx) != GlobalIdxToDaughterIdxs.end()) { + if (GlobalIdxToDaughterIdxs.contains(globalIdx)) { const auto& daughters = GlobalIdxToDaughterIdxs[globalIdx]; - if (daughters.size() > 0) { + if (!daughters.empty()) { firstDaughter = daughters[0]; } if (daughters.size() > 1) { secondDaughter = daughters[1]; } + if (globalToVertexPosition.contains(globalIdx)) { + const auto& vertexPos = globalToVertexPosition[globalIdx]; + secondaryVertexRadius = std::hypot(vertexPos[0] * 0.1, vertexPos[1] * 0.1); + } + if (secondaryVertexRadius == -1) { + secondaryVertexRadius = GlobalIdxSecVertexRadiusIncoming[globalIdx]; + } } // Determine flags for the MC particle // TODO: For now primary are only those without mother, but all based on PhysicalPrimary definition should be considered. @@ -513,24 +527,24 @@ struct Alice3TrackingTranslator { } GlobalIdxToPDGCode[globalIdx] = fileParticlesSim.m_particle_type->at(iPart); GlobalToLocalIdx[globalIdx] = iPart; - addMCParticle(tableMcCollisions.lastIndex(), fileParticlesSim, iPart, flags, motherIdx, firstDaughter, secondDaughter, fileParticlesSim.m_number_of_hits->at(iPart)); + addMCParticle(tableMcCollisions.lastIndex(), fileParticlesSim, iPart, flags, motherIdx, firstDaughter, secondDaughter, fileParticlesSim.m_number_of_hits->at(iPart), secondaryVertexRadius); } - - tableCollisions(0, // bcId - collisionX, // posX - collisionY, // posY - collisionZ, // posZ - 0.0f, // covXX - 0.0f, // covXY - 0.0f, // covXZ - 0.0f, // covYY - 0.0f, // covYZ - 0.0f, // covZZ - 0, // flags - 0.0f, // m_chi2Sum - 0, // numContrib - 0.0f, // collisionTime - 0.0f); // collisionTimeRes + const float scale = 0.1f; + tableCollisions(0, // bcId + collisionX * scale, // posX + collisionY * scale, // posY + collisionZ * scale, // posZ + 0.0f, // covXX + 0.0f, // covXY + 0.0f, // covXZ + 0.0f, // covYY + 0.0f, // covYZ + 0.0f, // covZZ + 0, // flags + 0.0f, // m_chi2Sum + 0, // numContrib + 0.0f, // collisionTime + 0.0f); // collisionTimeRes tableMcCollisionLabels(iEvent, // mcCollisionId 0); // mcMask @@ -585,7 +599,7 @@ struct Alice3TrackingTranslator { // Fill covariance matrices float cxx = fileTracksummary.m_cov_eX_eX->at(iTrack); - float cyy = cxx; // fileTracksummary.cov_eY_eY->at(iTrack); + float cyy = fileTracksummary.m_cov_eY_eY->at(iTrack); float czz = fileTracksummary.m_cov_eZ_eZ->at(iTrack); float cxy = fileTracksummary.m_cov_eX_eY->at(iTrack); float cxz = fileTracksummary.m_cov_eX_eZ->at(iTrack); @@ -606,7 +620,8 @@ struct Alice3TrackingTranslator { float czpy = fileTracksummary.m_cov_eZ_ePY->at(iTrack); float czpz = fileTracksummary.m_cov_eZ_ePZ->at(iTrack); // Create TrackParCov object with covariance matrix - std::array position = {x, y, z}; + + std::array position = {x * scale, y * scale, z * scale}; std::array momentum = {px, py, pz}; std::array trackCov = {cxx, cxy, cyy, cxz, cyz, czz, cxpx, cypx, czpx, cpxpx, cxpy, cypy, czpy, cpxpy, cpypy, cxpz, cypz, czpz, cpxpz, cpypz, cpzpz}; o2::track::TrackParCov trackParCov(position, momentum, trackCov, charge); @@ -666,16 +681,31 @@ struct Alice3TrackingTranslator { tableTracksDCACov(0.0f, // sigmaDcaXY2 0.0f); // sigmaDcaZ2 // Fill ALICE3 specific tables - tableTracksAlice3(true); // isReconstructed - if (mcParticleIdx > 0) + tableTracksAlice3(true, // isReconstructed + -1.f, // todo: track innermost update + -1.f); // todo: track outermost update + if (mcParticleIdx > 0) { tableTracksAlice3Pdg(GlobalIdxToPDGCode[mcParticleIdx]); // PdgCode to the linked MC truth particle - else + } else { tableTracksAlice3Pdg(0); // No linked MC truth particle + } - tableTracksExtraA3(m_nMeasurements, // nSiliconHits (using m_nMeasurements as proxy) - 0, // nTPCHits - 0, // trackType - false); // isPVContributor + float secondaryVertexRadius = 0; + std::array vertexPos = {0.0, 0.0, 0.0}; + if (mcParticleIdx > 0) { + vertexPos[0] = fileParticlesSim.m_vx->at(GlobalToLocalIdx[mcParticleIdx]) * 0.1; + vertexPos[1] = fileParticlesSim.m_vy->at(GlobalToLocalIdx[mcParticleIdx]) * 0.1; + vertexPos[2] = fileParticlesSim.m_vz->at(GlobalToLocalIdx[mcParticleIdx]) * 0.1; + secondaryVertexRadius = std::hypot(vertexPos[0], vertexPos[1]); + } + tableTracksExtraA3(m_nMeasurements, // nSiliconHits (using m_nMeasurements as proxy) + 0, // nTPCHits + 0, // trackType + false, // isPVContributor + secondaryVertexRadius, // secondaryVertexRadius + vertexPos[0], + vertexPos[1], + vertexPos[2]); // Fill extra track info tableStoredTracksExtra(0.f, // TPCInnerParam static_cast(0), // Flags @@ -728,17 +758,17 @@ struct Alice3TrackingTranslator { } }; -o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& cfgc) +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) { o2::framework::WorkflowSpec w; - if (cfgc.options().hasOption("aod-file")) { - std::string inputFile = cfgc.options().get("aod-file"); + if (context.options().hasOption("aod-file")) { + auto inputFile = context.options().get("aod-file"); if (!inputFile.empty()) { LOG(info) << " " << inputFile; TString tinputFile(inputFile.c_str()); inputPath = gSystem->DirName(tinputFile); } } - w.push_back(adaptAnalysisTask(cfgc)); + w.push_back(adaptAnalysisTask(context)); return w; } diff --git a/ALICE3/TableProducer/alice3strangenessFinder.cxx b/ALICE3/TableProducer/alice3strangenessFinder.cxx index 9725d9506cf..e70931f7c43 100644 --- a/ALICE3/TableProducer/alice3strangenessFinder.cxx +++ b/ALICE3/TableProducer/alice3strangenessFinder.cxx @@ -21,7 +21,6 @@ #include "PWGLF/DataModel/LFStrangenessTables.h" -#include "ALICE3/Core/GeometryContainer.h" #include "ALICE3/Core/TrackUtilities.h" #include "ALICE3/DataModel/OTFPIDTrk.h" #include "ALICE3/DataModel/OTFRICH.h" @@ -32,7 +31,6 @@ #include "Common/Core/trackUtilities.h" #include "Common/DataModel/TrackSelectionTables.h" -#include #include #include #include @@ -50,6 +48,7 @@ #include #include +#include #include #include @@ -66,7 +65,7 @@ using namespace o2::framework; using namespace o2::constants::physics; using Alice3TracksWPid = soa::Join; -using Alice3TracksACTS = soa::Join; +using Alice3TracksACTS = soa::Join; using Alice3TracksOTF = soa::Join; using Alice3MCParticles = soa::Join; @@ -74,9 +73,6 @@ struct Alice3strangenessFinder { SliceCache cache; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - Service ccdb{}; - o2::fastsim::GeometryContainer geoContainer; - std::vector> trackingLayers; Produces v0CandidateIndices; // contains V0 candidate indices Produces v0CandidateCores; // contains V0 candidate core information @@ -95,7 +91,7 @@ struct Alice3strangenessFinder { ConfigurableAxis axisDCA{"axisDCA", {200, 0, 200}, "DCA axis"}; ConfigurableAxis axisEta{"axisEta", {80, -4.f, 4.f}, "Eta axis"}; - ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.0f, 0.025f, 0.05f, 0.075f, 0.1f, 0.125f, 0.15f, 0.175f, 0.2f, 0.225f, 0.25f, 0.275f, 0.3f, 0.325f, 0.35f, 0.375f, 0.4f, 0.425f, 0.45f, 0.475f, 0.5f, 0.525f, 0.55f, 0.575f, 0.6f, 0.625f, 0.65f, 0.675f, 0.7f, 0.725f, 0.75f, 0.775f, 0.8f, 0.82f, 0.85f, 0.875f, 0.9f, 0.925f, 0.95f, 0.975f, 1.0f, 1.05f, 1.1f}, "pt axis for QA histograms"}; + ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.0f, 0.05f, 0.1f, 0.15f, 0.2f, 0.25f, 0.3f, 0.35f, 0.4f, 0.45f, 0.5f, 0.55f, 0.6f, 0.65f, 0.7f, 0.75f, 0.8f, 0.85f, 0.9f, 0.95f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, 2.0f, 2.25f, 2.5, 2.75, 3.0f, 3.5f, 4.0f, 4.5f, 5.0f, 6.0f, 7.0f, 8.0f, 10.0f, 12.f, 15.f}, "pt axis for QA histograms"}; // DCA Fitter struct : ConfigurableGroup { @@ -138,11 +134,24 @@ struct Alice3strangenessFinder { Configurable cascMaxEta{"cascMaxEta", 2.5f, "DCA between cascade daughters (cm)"}; } presel; + // Daughter Track Eff calculation + struct : ConfigurableGroup { + std::string prefix = "daughEff"; + Configurable analyseK0Daughters{"analyseK0Daughters", true, ""}; + Configurable analyseLambdaDaughters{"analyseLambdaDaughters", false, ""}; + Configurable analyseAntiLambdaDaughters{"analysAntiLambdaDaughters", false, ""}; + Configurable analyseXiMinusDaughters{"analyseXiMinusDaughters", false, ""}; + Configurable analyseXiPlusDaughters{"analyseXiPlusDaughters", false, ""}; + Configurable analysOmegaMinusDaughters{"analysOmegaMinusDaughters", false, ""}; + Configurable analyseOmegaPlusDaughters{"analyseOmegaPlusDaughters", false, ""}; + } daughEff; + // Operation Configurable magneticField{"magneticField", 20.0f, "Magnetic field (in kilogauss)"}; Configurable mcSameMotherCheck{"mcSameMotherCheck", true, "check if tracks come from the same MC mother"}; // for the ACTS study + Configurable useGeneratedSVposition{"useGeneratedSVposition", false, "use the MC truth position of V0 decay"}; Configurable isK0Gun{"isK0Gun", false, "is K0s Monte Carlo gun used"}; Configurable isLambdaGun{"isLambdaGun", true, "is Lambda Monte Carlo gun used"}; Configurable skipFitter{"skipFitter", false, "calculate V0 properties without calling the DCA fitter, using only the track parameters "}; @@ -151,7 +160,6 @@ struct Alice3strangenessFinder { o2::vertexing::DCAFitterN<2> fitter; Service pdgDB{}; static constexpr float ToMicrons = 1e+4; - static constexpr int LutConfig = 0; // todo // partitions for v0/casc dau tracks Partition positiveSecondaryTracksACTS = @@ -189,6 +197,8 @@ struct Alice3strangenessFinder { // decay properties float dcaDau{}; + float posDauDCAxy{}; + float negDauDCAxy{}; float eta{}; std::array p{}; std::array posSV{}; @@ -199,7 +209,7 @@ struct Alice3strangenessFinder { float dcaToPV{}; }; - void init(InitContext& initContext) + void init(InitContext&) { // Initialization code here fitter.setBz(magneticField); @@ -256,13 +266,31 @@ struct Alice3strangenessFinder { hV0Counter->GetXaxis()->SetBinLabel(3, "AntiLambda"); hV0Counter->GetXaxis()->SetBinLabel(4, "Misidentified"); - histos.add("hRadiusVsHitsNeg", "", kTH2D, {{400, 0, 400}, {12, 0.5, 12.5}}); // radius vs hist for MC studies - histos.add("hRadiusVsHitsPos", "", kTH2D, {{400, 0, 400}, {12, 0.5, 12.5}}); // radius vs hist for MC studies + auto hPtResolutionPositive = histos.add("hPtResolutionPositive", "hPtResolutionPositive", kTH2D, {{axisPt}, {axisPt}}); + hPtResolutionPositive->GetXaxis()->SetTitle("p_{T}^{reco} (GeV/c)"); + hPtResolutionPositive->GetYaxis()->SetTitle("p_{T}^{true} (GeV/c)"); + + auto hPtResolutionNegative = histos.add("hPtResolutionNegative", "hPtResolutionNegative", kTH2D, {{axisPt}, {axisPt}}); + hPtResolutionNegative->GetXaxis()->SetTitle("p_{T}^{reco} (GeV/c)"); + hPtResolutionNegative->GetYaxis()->SetTitle("p_{T}^{true} (GeV/c)"); + + auto hPtRes1DNegDau = histos.add("hPtRes1DNegDau", "hPtRes1DNegDau", kTH1D, {{100, -0.2, 0.2}}); + hPtRes1DNegDau->GetXaxis()->SetTitle("(p_{T}^{gen} - p_{T}^{reco})/p_{T}^{gen}"); + auto hPtRes1DPosDau = histos.add("hPtRes1DPosDau", "hPtRes1DPosDau", kTH1D, {{100, -0.2, 0.2}}); + hPtRes1DPosDau->GetXaxis()->SetTitle("(p_{T}^{gen} - p_{T}^{reco})/p_{T}^{gen}"); auto hV0Building = histos.add("hV0Building", "hV0Building", kTH1D, {{10, 0.5, 10.5}}); - hV0Building->GetXaxis()->SetBinLabel(1, "Pair"); - hV0Building->GetXaxis()->SetBinLabel(2, "Pdg check"); - hV0Building->GetXaxis()->SetBinLabel(3, "DCA Fitter"); + if (doprocessFindV0CandidateOTF || doprocessFindV0CandidateACTS) { + hV0Building->GetXaxis()->SetBinLabel(1, "Pair"); + hV0Building->GetXaxis()->SetBinLabel(2, "Pdg check"); + hV0Building->GetXaxis()->SetBinLabel(3, "DCA Fitter"); + } + if (doprocessMCTrueFromACTS) { + hV0Building->GetXaxis()->SetBinLabel(1, "Pair"); + hV0Building->GetXaxis()->SetBinLabel(2, "PID"); + hV0Building->GetXaxis()->SetBinLabel(3, "Mother Check"); + hV0Building->GetXaxis()->SetBinLabel(4, "DCA Fitter"); + } auto hCascadeBuilding = histos.add("hCascadeBuilding", "hCascadeBuilding", kTH1D, {{10, 0.5, 10.5}}); hCascadeBuilding->GetXaxis()->SetBinLabel(1, "Attempts"); @@ -280,47 +308,47 @@ struct Alice3strangenessFinder { histos.add("Generated/hGeneratedAntiOmega", "hGeneratedAntiOmega", kTH2D, {{axisPt}, {axisEta}}); } - if (doprocessFindV0CandidateOTF) { - ccdb->setURL("http://alice-ccdb.cern.ch"); - ccdb->setTimestamp(-1); - geoContainer.setCcdbManager(ccdb.operator->()); - geoContainer.init(initContext); - const int nGeometries = geoContainer.getNumberOfConfigurations(); - trackingLayers.resize(nGeometries); - - for (int icfg = 0; icfg < nGeometries; ++icfg) { - auto globalConfiguration = geoContainer.getConfigurations(icfg); - - for (const auto& [outerKey, innerMap] : globalConfiguration) { - if (outerKey.empty() || outerKey[0] != 'B') { - continue; - } - - auto it = innerMap.find("r"); - if (it != innerMap.end()) { - trackingLayers[icfg].push_back(std::stof(it->second)); - } - } - } - } - if (buildCascade) { histos.add("CascadeBuilding/hDcaBetweenDaus", "hDcaBetweenDaus", kTH1D, {{axisDCA}}); histos.add("CascadeBuilding/hXiMass", "", kTH1D, {axisXiMass}); histos.add("CascadeBuilding/hOmegaMass", "", kTH1D, {axisOmegaMass}); } - histos.add("hPx", "", kTH1D, {{200, -10.f, 10.f}}); - histos.add("hPy", "", kTH1D, {{200, -10.f, 10.f}}); - histos.add("hPz", "", kTH1D, {{200, -10.f, 10.f}}); - - histos.add("hPxPairs", "", kTH1D, {{200, -10.f, 10.f}}); - histos.add("hPyPairs", "", kTH1D, {{200, -10.f, 10.f}}); - histos.add("hPzPairs", "", kTH1D, {{200, -10.f, 10.f}}); - - histos.add("hPxV0CandDaughter", "", kTH1D, {{200, -10.f, 10.f}}); - histos.add("hPyV0CandDaughter", "", kTH1D, {{200, -10.f, 10.f}}); - histos.add("hPzV0CandDaughter", "", kTH1D, {{200, -10.f, 10.f}}); + histos.add("hPxPosRepropagated", "", kTH2D, {{200, -10.f, 10.f}, {200, -10.f, 10.f}}); + histos.add("hPyPosRepropagated", "", kTH2D, {{200, -10.f, 10.f}, {200, -10.f, 10.f}}); + histos.add("hPzPosRepropagated", "", kTH2D, {{200, -10.f, 10.f}, {200, -10.f, 10.f}}); + + histos.add("hPxNegRepropagated", "", kTH2D, {{200, -10.f, 10.f}, {200, -10.f, 10.f}}); + histos.add("hPyNegRepropagated", "", kTH2D, {{200, -10.f, 10.f}, {200, -10.f, 10.f}}); + histos.add("hPzNegRepropagated", "", kTH2D, {{200, -10.f, 10.f}, {200, -10.f, 10.f}}); + histos.add("hRadius", "", kTH1D, {{100, 0, 100}}); + histos.add("hRadiusGenNeg", "", kTH1D, {{100, 0, 100}}); + histos.add("hRadiusGenPos", "", kTH1D, {{100, 0, 100}}); + + histos.add("hMomentumPosRepropagated", "", kTH2D, {{500, 0.f, 15.f}, {500, -0.f, 15.f}}); + histos.add("hMomentumNegRepropagated", "", kTH2D, {{500, 0.f, 15.f}, {500, -0.f, 15.f}}); + + histos.add("hCosPhiRepropagated", "", kTH2D, {{100, 0.8f, 1.f}, {100, 0.8f, 1.f}}); + + if (doprocessDaughterTracksForEffACTS || doprocessDaughterTracksForEffOTF) { + histos.add("K0Short/hNegDaughterRecoTruePtEta", "", kTH2D, {{axisPt}, {axisEta}}); + histos.add("K0Short/hNegDaughterRecoPtEta", "", kTH2D, {{axisPt}, {axisEta}}); + histos.add("K0Short/hPosDaughterRecoTruePtEta", "", kTH2D, {{axisPt}, {axisEta}}); + histos.add("K0Short/hPosDaughterRecoPtEta", "", kTH2D, {{axisPt}, {axisEta}}); + histos.addClone("K0Short/", "Lambda"); + histos.addClone("K0Short/", "AntiLambda"); + } + if (doprocessMCTrueFromACTS) { + histos.add("K0Short/hNegDaughterGenPtEta", "", kTH2D, {{axisPt}, {axisEta}}); + histos.add("K0Short/hPosDaughterGenPtEta", "", kTH2D, {{axisPt}, {axisEta}}); + histos.add("Lambda/hNegDaughterGenPtEta", "", kTH2D, {{axisPt}, {axisEta}}); + histos.add("Lambda/hPosDaughterGenPtEta", "", kTH2D, {{axisPt}, {axisEta}}); + histos.add("AntiLambda/hNegDaughterGenPtEta", "", kTH2D, {{axisPt}, {axisEta}}); + histos.add("AntiLambda/hPosDaughterGenPtEta", "", kTH2D, {{axisPt}, {axisEta}}); + + histos.add("hRadiusVsHitsNeg", "", kTH2D, {{100, 0, 100}, {12, 0.5, 12.5}}); // radius vs hist for MC studies + histos.add("hRadiusVsHitsPos", "", kTH2D, {{100, 0, 100}, {12, 0.5, 12.5}}); // radius vs hist for MC studies + } histos.print(); } @@ -373,18 +401,21 @@ struct Alice3strangenessFinder { } // end association check return returnValue; } - - template - [[nodiscard]] float getFirstLayerHitRadius(const TTrackType& track, const std::vector& layers) + template + bool checkSameMotherGen(TMCParticle const& mcParticle1, TMCParticle const& mcParticle2, Alice3MCParticles const& mcParticles) { - const float trueRadius = std::hypot(track.x(), track.y()); - for (const float layerRadius : layers) { - if (layerRadius >= trueRadius) { - return layerRadius; + bool returnValue = false; + if (mcParticle1.has_mothers() && mcParticle2.has_mothers()) { + auto motherIds1 = mcParticle1.mothersIds(); + auto motherIds2 = mcParticle2.mothersIds(); + if (!motherIds1.empty() && !motherIds2.empty() && motherIds1[0] == motherIds2[0]) { + auto mother = mcParticles.rawIteratorAt(motherIds1[0] - mcParticles.rawIteratorAt(0).globalIndex()); + if (mother.isPhysicalPrimary()) { + returnValue = true; + } } - } - static constexpr float OutsideALICE3 = 100.f; - return OutsideALICE3; + } // end association check + return returnValue; } template @@ -435,9 +466,15 @@ struct Alice3strangenessFinder { thisCandidate.p[1] = thisCandidate.pDau0[1] + thisCandidate.pDau1[1]; thisCandidate.p[2] = thisCandidate.pDau0[2] + thisCandidate.pDau1[2]; const auto posSV = fitter.getPCACandidatePos(); - thisCandidate.posSV[0] = posSV[0]; - thisCandidate.posSV[1] = posSV[1]; - thisCandidate.posSV[2] = posSV[2]; + if (useOriginalTrackParams) { + thisCandidate.posSV[0] = t0.getX(); + thisCandidate.posSV[1] = t0.getY(); + thisCandidate.posSV[2] = t0.getZ(); + } else { + thisCandidate.posSV[0] = posSV[0]; + thisCandidate.posSV[1] = posSV[1]; + thisCandidate.posSV[2] = posSV[2]; + } std::array covA = {0}; std::array covB = {0}; @@ -470,6 +507,9 @@ struct Alice3strangenessFinder { thisCandidate.dcaToPV = calculateDCAStraightToPV(thisCandidate.posSV[0], thisCandidate.posSV[1], thisCandidate.posSV[2], thisCandidate.p[0], thisCandidate.p[1], thisCandidate.p[2], vtx[0], vtx[1], vtx[2]); + std::array dcaInfo = {999.0f, 999.0f}; // by default, take large value to make sure candidate accepted, TODO implement proper DCA calculation + thisCandidate.posDauDCAxy = dcaInfo[0]; + thisCandidate.negDauDCAxy = dcaInfo[1]; } else { t0.getPxPyPzGlo(thisCandidate.pDau0); t1.getPxPyPzGlo(thisCandidate.pDau1); @@ -486,6 +526,8 @@ struct Alice3strangenessFinder { thisCandidate.dcaToPV = calculateDCAStraightToPV(thisCandidate.posSV[0], thisCandidate.posSV[1], thisCandidate.posSV[2], thisCandidate.p[0], thisCandidate.p[1], thisCandidate.p[2], vtx[0], vtx[1], vtx[2]); + thisCandidate.posDauDCAxy = 999.0f; + thisCandidate.negDauDCAxy = 999.0f; } return true; } @@ -493,21 +535,39 @@ struct Alice3strangenessFinder { void processGenerated(aod::McParticles const&) { for (const auto& mcParticle : trueK0s) { + if (!mcParticle.isPhysicalPrimary()) { + continue; + } histos.fill(HIST("Generated/hGeneratedK0s"), mcParticle.pt(), mcParticle.eta()); } for (const auto& mcParticle : trueLambda) { + if (!mcParticle.isPhysicalPrimary()) { + continue; + } histos.fill(HIST("Generated/hGeneratedLambda"), mcParticle.pt(), mcParticle.eta()); } for (const auto& mcParticle : trueAntiLambda) { + if (!mcParticle.isPhysicalPrimary()) { + continue; + } histos.fill(HIST("Generated/hGeneratedAntiLambda"), mcParticle.pt(), mcParticle.eta()); } for (const auto& mcParticle : trueXi) { + if (!mcParticle.isPhysicalPrimary()) { + continue; + } histos.fill(HIST("Generated/hGeneratedXi"), mcParticle.pt(), mcParticle.eta()); } for (const auto& mcParticle : trueAntiXi) { + if (!mcParticle.isPhysicalPrimary()) { + continue; + } histos.fill(HIST("Generated/hGeneratedAntiXi"), mcParticle.pt(), mcParticle.eta()); } for (const auto& mcParticle : trueOmega) { + if (!mcParticle.isPhysicalPrimary()) { + continue; + } histos.fill(HIST("Generated/hGeneratedOmega"), mcParticle.pt(), mcParticle.eta()); } for (const auto& mcParticle : trueAntiOmega) { @@ -527,18 +587,12 @@ struct Alice3strangenessFinder { } o2::track::TrackParCov pos = getTrackParCov(posTrack); - // if constexpr (requires { posTrack.pdgCode(); }) { - // if (posTrack.pdgCode() == kPiPlus) { - histos.fill(HIST("hPx"), posTrack.px()); - histos.fill(HIST("hPy"), posTrack.py()); - histos.fill(HIST("hPz"), posTrack.pz()); - // } - // } for (auto const& negTrack : negTracksGrouped) { if (!negTrack.isReconstructed()) { continue; // no ghost tracks } + o2::track::TrackParCov neg = getTrackParCov(negTrack); histos.fill(HIST("hV0Building"), 1.0); if (mcSameMotherCheck && !checkSameMother(posTrack, negTrack)) { @@ -546,68 +600,65 @@ struct Alice3strangenessFinder { } if (isK0Gun || isLambdaGun) { - // ACTS: pdg code attached to track - if constexpr (requires { posTrack.pdgCode(); }) { - if ((posTrack.pdgCode() != kPiPlus && negTrack.pdgCode() != kPiMinus) && isK0Gun) { - continue; - } - if ((posTrack.pdgCode() != kProton && negTrack.pdgCode() != kPiMinus) && isLambdaGun) { - continue; - } - } else { - // OTF: pdg code from mcParticle table - if (!posTrack.has_mcParticle() && !negTrack.has_mcParticle()) { - continue; - } - auto mcParticlePos = posTrack.template mcParticle_as(); - auto mcParticleNeg = negTrack.template mcParticle_as(); - if ((mcParticlePos.pdgCode() != kPiPlus && mcParticleNeg.pdgCode() != kPiMinus) && isK0Gun) { - continue; - } - if ((mcParticlePos.pdgCode() != kProton && mcParticleNeg.pdgCode() != kPiMinus) && isLambdaGun) { - continue; - } + if (!posTrack.has_mcParticle() || !negTrack.has_mcParticle()) { + continue; + } + auto mcParticlePos = posTrack.template mcParticle_as(); + auto mcParticleNeg = negTrack.template mcParticle_as(); + if ((mcParticlePos.pdgCode() != kPiPlus && mcParticleNeg.pdgCode() != kPiMinus) && isK0Gun) { + continue; + } + if ((mcParticlePos.pdgCode() != kProton && mcParticleNeg.pdgCode() != kPiMinus) && isLambdaGun) { + continue; } } - Candidate v0Cand; histos.fill(HIST("hV0Building"), 2.0); - o2::track::TrackParCov neg = getTrackParCov(negTrack); if (!buildDecayCandidateTwoBody(pos, neg, vtx, v0Cand)) { continue; // failed at building candidate } histos.fill(HIST("hV0Building"), 3.0); - // TODO: not all ACTS tracks have MC association, so this check is not possible for all candidates, fix is needed - // auto mcParticle1 = posTrack.template mcParticle_as(); - // if (mcParticle1.pdgCode() == kK0Short) { - // histos.fill(HIST("hV0Counter"), 0.5); - // } else if (mcParticle1.pdgCode() == kLambda0) { - // histos.fill(HIST("hV0Counter"), 1.5); - // } else if (mcParticle1.pdgCode() == kLambda0Bar) { - // histos.fill(HIST("hV0Counter"), 2.5); - // } else { - // histos.fill(HIST("hV0Counter"), 3.5); - // } - histos.fill(HIST("hPxPairs"), posTrack.px()); - histos.fill(HIST("hPyPairs"), posTrack.py()); - histos.fill(HIST("hPzPairs"), posTrack.pz()); - - histos.fill(HIST("hPxV0CandDaughter"), v0Cand.pDau0[0]); - histos.fill(HIST("hPyV0CandDaughter"), v0Cand.pDau0[1]); - histos.fill(HIST("hPzV0CandDaughter"), v0Cand.pDau0[2]); - + const float radiusV0 = std::hypot(v0Cand.posSV[0], v0Cand.posSV[1]); + std::array posSV = {v0Cand.posSV[0], v0Cand.posSV[1], v0Cand.posSV[2]}; + double p_pos_orig = sqrt(posTrack.px() * posTrack.px() + posTrack.py() * posTrack.py() + posTrack.pz() * posTrack.pz()); + double p_neg_orig = sqrt(negTrack.px() * negTrack.px() + negTrack.py() * negTrack.py() + negTrack.pz() * negTrack.pz()); + double p_pos_reprop = sqrt(v0Cand.pDau0[0] * v0Cand.pDau0[0] + v0Cand.pDau0[1] * v0Cand.pDau0[1] + v0Cand.pDau0[2] * v0Cand.pDau0[2]); + double p_neg_reprop = sqrt(v0Cand.pDau1[0] * v0Cand.pDau1[0] + v0Cand.pDau1[1] * v0Cand.pDau1[1] + v0Cand.pDau1[2] * v0Cand.pDau1[2]); + double cosOpeningAngleReprop = (v0Cand.pDau0[0] * v0Cand.pDau1[0] + v0Cand.pDau0[1] * v0Cand.pDau1[1] + v0Cand.pDau0[2] * v0Cand.pDau1[2]) / (sqrt(v0Cand.pDau0[0] * v0Cand.pDau0[0] + v0Cand.pDau0[1] * v0Cand.pDau0[1] + v0Cand.pDau0[2] * v0Cand.pDau0[2]) * sqrt(v0Cand.pDau1[0] * v0Cand.pDau1[0] + v0Cand.pDau1[1] * v0Cand.pDau1[1] + v0Cand.pDau1[2] * v0Cand.pDau1[2])); + double cosOpeningAngleOrig = (posTrack.px() * negTrack.px() + posTrack.py() * negTrack.py() + posTrack.pz() * negTrack.pz()) / (sqrt(posTrack.px() * posTrack.px() + posTrack.py() * posTrack.py() + posTrack.pz() * posTrack.pz()) * sqrt(negTrack.px() * negTrack.px() + negTrack.py() * negTrack.py() + negTrack.pz() * negTrack.pz())); + + if constexpr (requires { negTrack.secondaryVertexRadius(); }) { + histos.fill(HIST("hRadiusGenNeg"), negTrack.secondaryVertexRadius()); + histos.fill(HIST("hRadiusGenPos"), posTrack.secondaryVertexRadius()); + if (useGeneratedSVposition) { + posSV[0] = posTrack.secondaryVertexX(); + posSV[1] = posTrack.secondaryVertexY(); + posSV[2] = posTrack.secondaryVertexZ(); + } + } v0CandidateIndices(collision.globalIndex(), posTrack.globalIndex(), negTrack.globalIndex(), -1); - v0CandidateCores(v0Cand.posSV[0], v0Cand.posSV[1], v0Cand.posSV[2], + v0CandidateCores(posSV[0], posSV[1], posSV[2], v0Cand.pDau0[0], v0Cand.pDau0[1], v0Cand.pDau0[2], v0Cand.pDau1[0], v0Cand.pDau1[1], v0Cand.pDau1[2], - v0Cand.dcaDau, posTrack.dcaXY(), negTrack.dcaXY(), + v0Cand.dcaDau, v0Cand.posDauDCAxy, v0Cand.negDauDCAxy, v0Cand.cosPA, v0Cand.dcaToPV); + histos.fill(HIST("hRadius"), radiusV0); + histos.fill(HIST("hPxPosRepropagated"), posTrack.px(), v0Cand.pDau0[0]); + histos.fill(HIST("hPyPosRepropagated"), posTrack.py(), v0Cand.pDau0[1]); + histos.fill(HIST("hPzPosRepropagated"), posTrack.pz(), v0Cand.pDau0[2]); + histos.fill(HIST("hPxNegRepropagated"), negTrack.px(), v0Cand.pDau1[0]); + histos.fill(HIST("hPyNegRepropagated"), negTrack.py(), v0Cand.pDau1[1]); + histos.fill(HIST("hPzNegRepropagated"), negTrack.pz(), v0Cand.pDau1[2]); + + histos.fill(HIST("hCosPhiRepropagated"), cosOpeningAngleOrig, cosOpeningAngleReprop); + histos.fill(HIST("hMomentumPosRepropagated"), p_pos_orig, p_pos_reprop); + histos.fill(HIST("hMomentumNegRepropagated"), p_neg_orig, p_neg_reprop); const float lambdaMassHypothesis = RecoDecay::m(std::array{std::array{v0Cand.pDau0[0], v0Cand.pDau0[1], v0Cand.pDau0[2]}, std::array{v0Cand.pDau1[0], v0Cand.pDau1[1], v0Cand.pDau1[2]}}, @@ -670,17 +721,14 @@ struct Alice3strangenessFinder { continue; // candidate outside of acceptance } - const float radiusV0 = std::hypot(v0Cand.posSV[0], v0Cand.posSV[1]); + // const float radiusV0 = std::hypot(v0Cand.posSV[0], v0Cand.posSV[1]); const float radiusCasc = std::hypot(cascCand.posSV[0], cascCand.posSV[1]); - const float posCausalityRadius = getFirstLayerHitRadius(posTrack, trackingLayers[LutConfig]); - const float negCausalityRadius = getFirstLayerHitRadius(negTrack, trackingLayers[LutConfig]); - const float bachCausalityRadius = getFirstLayerHitRadius(bachTrack, trackingLayers[LutConfig]); - - if (posCausalityRadius < radiusV0) { + // tracks without update information (IU < 0) are not checked + if (posTrack.trackIU() > 0.f && posTrack.trackIU() < radiusV0) { continue; // positive track hit a layer before v0 decayed } - if (negCausalityRadius < radiusV0) { + if (negTrack.trackIU() > 0.f && negTrack.trackIU() < radiusV0) { continue; // negative track hit a layer before v0 decayed } @@ -688,7 +736,7 @@ struct Alice3strangenessFinder { continue; // v0 decayed before cascade } - if (bachCausalityRadius < radiusCasc) { + if (bachTrack.trackIU() > 0.f && bachTrack.trackIU() < radiusCasc) { continue; // bachelor track hit a layer before v0 decayed } @@ -741,7 +789,7 @@ struct Alice3strangenessFinder { } // end posTrack } - void processMCTrueFromACTS(aod::McCollision const& collision, Alice3MCParticles const&) + void processMCTrueFromACTS(aod::McCollision const& collision, Alice3MCParticles const& mcParticles) { auto negativeMCParticlesGrouped = negativeMCParticles->sliceByCached(aod::mcparticle::mcCollisionId, collision.globalIndex(), cache); @@ -754,21 +802,66 @@ struct Alice3strangenessFinder { bool isLambda = false; bool isAntiLambda = false; int iPosPart = 0; + auto posMotherPdgCode = 0; + auto negMotherPdgCode = 0; for (auto const& posParticle : positiveMCParticlesGrouped) { - radiusPos = std::hypot(posParticle.vx(), posParticle.vy()); - histos.fill(HIST("hRadiusVsHitsPos"), radiusPos, posParticle.nHits()); + if (posParticle.pdgCode() != kPiPlus && posParticle.pdgCode() != kProton) { + continue; + } + + if (posParticle.has_mothers()) { + auto motherIds = posParticle.mothersIds(); + if (!motherIds.empty()) { + auto mother = mcParticles.rawIteratorAt(motherIds[0] - mcParticles.rawIteratorAt(0).globalIndex()); + if (mother.isPhysicalPrimary()) { + radiusPos = std::hypot(posParticle.vx(), posParticle.vy()); + histos.fill(HIST("hRadiusVsHitsPos"), radiusPos, posParticle.nHits()); + posMotherPdgCode = mother.pdgCode(); + if (posMotherPdgCode == kK0Short) { + histos.fill(HIST("K0Short/hPosDaughterGenPtEta"), posParticle.pt(), posParticle.eta()); + } + if (posMotherPdgCode == kLambda0) { + histos.fill(HIST("Lambda/hPosDaughterGenPtEta"), posParticle.pt(), posParticle.eta()); + } + if (posMotherPdgCode == kLambda0Bar) { + histos.fill(HIST("AntiLambda/hPosDaughterGenPtEta"), posParticle.pt(), posParticle.eta()); + } + } + } + } + for (auto const& negParticle : negativeMCParticlesGrouped) { if (negParticle.pdgCode() != kPiMinus && negParticle.pdgCode() != kProtonBar) { continue; } radiusNeg = std::hypot(negParticle.vx(), negParticle.vy()); if (iPosPart == 0) { - histos.fill(HIST("hRadiusVsHitsNeg"), radiusNeg, negParticle.nHits()); + if (negParticle.has_mothers()) { + auto motherIds = negParticle.mothersIds(); + if (!motherIds.empty()) { + auto mother = mcParticles.rawIteratorAt(motherIds[0] - mcParticles.rawIteratorAt(0).globalIndex()); + if (mother.isPhysicalPrimary()) { + radiusNeg = std::hypot(negParticle.vx(), negParticle.vy()); + histos.fill(HIST("hRadiusVsHitsNeg"), radiusNeg, negParticle.nHits()); + negMotherPdgCode = mother.pdgCode(); + if (negMotherPdgCode == kK0Short) { + histos.fill(HIST("K0Short/hNegDaughterGenPtEta"), negParticle.pt(), negParticle.eta()); + } + if (negMotherPdgCode == kLambda0) { + histos.fill(HIST("Lambda/hNegDaughterGenPtEta"), negParticle.pt(), negParticle.eta()); + } + if (negMotherPdgCode == kLambda0Bar) { + histos.fill(HIST("AntiLambda/hNegDaughterGenPtEta"), negParticle.pt(), negParticle.eta()); + } + } + } + } } if (radiusPos == radiusNeg) { - isK0s = (posParticle.pdgCode() == kPiPlus && negParticle.pdgCode() == kPiMinus); - isLambda = (posParticle.pdgCode() == kProton && negParticle.pdgCode() == kPiMinus); - isAntiLambda = (posParticle.pdgCode() == kPiPlus && negParticle.pdgCode() == kProtonBar); + histos.fill(HIST("hV0Building"), 1.0); + isK0s = (posParticle.pdgCode() == kPiPlus && negParticle.pdgCode() == kPiMinus && posMotherPdgCode == kK0Short && negMotherPdgCode == kK0Short); + isLambda = (posParticle.pdgCode() == kProton && negParticle.pdgCode() == kPiMinus && posMotherPdgCode == kLambda0 && negMotherPdgCode == kLambda0); + isAntiLambda = (posParticle.pdgCode() == kPiPlus && negParticle.pdgCode() == kProtonBar && posMotherPdgCode == kLambda0Bar && negMotherPdgCode == kLambda0Bar); if (isK0s || isLambda || isAntiLambda) { if (!isK0s && isK0Gun) { continue; @@ -776,6 +869,11 @@ struct Alice3strangenessFinder { if (!isLambda && isLambdaGun) { continue; } + histos.fill(HIST("hV0Building"), 2.0); + if (mcSameMotherCheck && !checkSameMotherGen(posParticle, negParticle, mcParticles)) { + continue; + } + histos.fill(HIST("hV0Building"), 3.0); Candidate v0cand; std::vector v0DecayVertex; v0DecayVertex.push_back(negParticle.vx()); @@ -790,10 +888,11 @@ struct Alice3strangenessFinder { if (!buildDecayCandidateTwoBody(posParCov, negParCov, vtx, v0cand)) { continue; } + histos.fill(HIST("hV0Building"), 4.0); v0CandidateIndices(collision.globalIndex(), posParticle.globalIndex(), negParticle.globalIndex(), - 0); + -1); v0CandidateCores(v0cand.posSV[0], v0cand.posSV[1], v0cand.posSV[2], v0cand.pDau0[0], v0cand.pDau0[1], v0cand.pDau0[2], v0cand.pDau1[0], v0cand.pDau1[1], v0cand.pDau1[2], @@ -810,10 +909,75 @@ struct Alice3strangenessFinder { } } } + negMotherPdgCode = 0; } iPosPart++; + posMotherPdgCode = 0; + } + } + template + void processDaughterTrack(const TTracksGrouped& tracksGrouped, int daughterType) + { + // for the daughter track efficiency calculation + for (auto const& track : tracksGrouped) { + if (!track.isReconstructed()) { + continue; // no ghost tracks + } + if (!track.has_mcParticle()) { + continue; + } + float pt = std::hypot(track.px(), track.py()); + float eta = RecoDecay::eta(std::array{track.px(), track.py(), track.pz()}); + auto mcParticle = track.template mcParticle_as(); + if (mcParticle.has_mothers()) { + for (const auto& m1 : mcParticle.template mothers_as()) { + if (daughEff.analyseK0Daughters && m1.pdgCode() == kK0Short && m1.isPhysicalPrimary()) { + if (daughterType == 0 && mcParticle.pdgCode() == kPiMinus) { + histos.fill(HIST("K0Short/hNegDaughterRecoTruePtEta"), mcParticle.pt(), mcParticle.eta()); + histos.fill(HIST("K0Short/hNegDaughterRecoPtEta"), pt, eta); + histos.fill(HIST("hPtResolutionNegative"), pt, mcParticle.pt()); + histos.fill(HIST("hPtRes1DNegDau"), (mcParticle.pt() - pt) / mcParticle.pt()); + } + if (daughterType == 1 && mcParticle.pdgCode() == kPiPlus) { + histos.fill(HIST("K0Short/hPosDaughterRecoTruePtEta"), mcParticle.pt(), mcParticle.eta()); + histos.fill(HIST("K0Short/hPosDaughterRecoPtEta"), pt, eta); + histos.fill(HIST("hPtResolutionPositive"), std::hypot(track.px(), track.py()), mcParticle.pt()); + histos.fill(HIST("hPtRes1DPosDau"), (mcParticle.pt() - pt) / mcParticle.pt()); + } + } + if (daughEff.analyseLambdaDaughters && m1.pdgCode() == kLambda0 && m1.isPhysicalPrimary()) { + if (daughterType == 0 && mcParticle.pdgCode() == kPiMinus) { + histos.fill(HIST("Lambda/hNegDaughterRecoTruePtEta"), mcParticle.pt(), mcParticle.eta()); + histos.fill(HIST("Lambda/hNegDaughterRecoPtEta"), pt, eta); + histos.fill(HIST("hPtResolutionNegative"), pt, mcParticle.pt()); + histos.fill(HIST("hPtRes1DNegDau"), (mcParticle.pt() - pt) / mcParticle.pt()); + } + if (daughterType == 1 && mcParticle.pdgCode() == kProton) { + histos.fill(HIST("Lambda/hPosDaughterRecoTruePtEta"), mcParticle.pt(), mcParticle.eta()); + histos.fill(HIST("Lambda/hPosDaughterRecoPtEta"), pt, eta); + histos.fill(HIST("hPtResolutionPositive"), pt, mcParticle.pt()); + histos.fill(HIST("hPtRes1DPosDau"), (mcParticle.pt() - pt) / mcParticle.pt()); + } + } + if (daughEff.analyseAntiLambdaDaughters && m1.pdgCode() == kLambda0Bar && m1.isPhysicalPrimary()) { + if (daughterType == 0 && mcParticle.pdgCode() == kProtonBar) { + histos.fill(HIST("AntiLambda/hNegDaughterRecoTruePtEta"), mcParticle.pt(), mcParticle.eta()); + histos.fill(HIST("AntiLambda/hNegDaughterRecoPtEta"), pt, eta); + histos.fill(HIST("hPtResolutionNegative"), pt, mcParticle.pt()); + histos.fill(HIST("hPtRes1DNegDau"), (mcParticle.pt() - pt) / mcParticle.pt()); + } + if (daughterType == 1 && mcParticle.pdgCode() == kPiPlus) { + histos.fill(HIST("AntiLambda/hPosDaughterRecoTruePtEta"), mcParticle.pt(), mcParticle.eta()); + histos.fill(HIST("AntiLambda/hPosDaughterRecoPtEta"), pt, eta); + histos.fill(HIST("hPtResolutionPositive"), pt, mcParticle.pt()); + histos.fill(HIST("hPtRes1DPosDau"), (mcParticle.pt() - pt) / mcParticle.pt()); + } + } + } + } } } + // void processFindV0CandidateWithPid(aod::Collision const& collision, aod::McParticles const& mcParticles, Alice3TracksWPid const&) // { // auto negativeSecondaryPionsGrouped = negativeSecondaryPions->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); @@ -838,14 +1002,35 @@ struct Alice3strangenessFinder { processFindV0CandidateNoPid(collision, negTracksGrouped, posTracksGrouped, bachTracksGrouped); } + void processDaughterTracksForEffACTS(aod::Collision const& collision, Alice3TracksACTS const&, aod::McParticles const&) + { + auto negTracksGrouped = negativeSecondaryTracksACTS->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + auto posTracksGrouped = positiveSecondaryTracksACTS->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + // auto bachTracksGrouped = bachelorTracksACTS->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + processDaughterTrack(negTracksGrouped, 0); + processDaughterTrack(posTracksGrouped, 1); + // processDaughterTrack(bachTracksGrouped,2); + } + void processDaughterTracksForEffOTF(aod::Collision const& collision, Alice3TracksOTF const&, aod::McParticles const&) + { + auto negTracksGrouped = negativeSecondaryTracksOTF->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + auto posTracksGrouped = positiveSecondaryTracksOTF->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + // auto bachTracksGrouped = bachelorTracksOTF->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + processDaughterTrack(negTracksGrouped, 0); + processDaughterTrack(posTracksGrouped, 1); + // processDaughterTrack(bachTracksGrouped,2); + } + PROCESS_SWITCH(Alice3strangenessFinder, processGenerated, "process generated information", true); PROCESS_SWITCH(Alice3strangenessFinder, processFindV0CandidateACTS, "find V0 without PID from ACTS reconstruction", true); PROCESS_SWITCH(Alice3strangenessFinder, processFindV0CandidateOTF, "find V0 without PID from OTF simulations", false); PROCESS_SWITCH(Alice3strangenessFinder, processMCTrueFromACTS, "process MC truth from ACTS", false); + PROCESS_SWITCH(Alice3strangenessFinder, processDaughterTracksForEffACTS, "process daughter trakcs from ACTS", true); + PROCESS_SWITCH(Alice3strangenessFinder, processDaughterTracksForEffOTF, "process daughter trakcs from ACTS", false); // PROCESS_SWITCH(alice3strangenessFinder, processFindV0CandidateWithPid, "find V0 with PID", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(context)}; } diff --git a/ALICE3/Tasks/CMakeLists.txt b/ALICE3/Tasks/CMakeLists.txt index 6e6fbc37ea0..3c05a20d372 100644 --- a/ALICE3/Tasks/CMakeLists.txt +++ b/ALICE3/Tasks/CMakeLists.txt @@ -21,7 +21,7 @@ o2physics_add_dpl_workflow(alice3-qa-singleparticle o2physics_add_dpl_workflow(alice3-lutmaker SOURCES alice3-lutmaker.cxx - PUBLIC_LINK_LIBRARIES O2::Framework FairRoot::Base O2Physics::AnalysisCore + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(alice3-pid-separation-power @@ -44,6 +44,11 @@ o2physics_add_dpl_workflow(alice3-prefilterdilepton PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(alice3-prefilterdimuon + SOURCES alice3-prefilterdimuon.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::FrameworkPhysicsSupport + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(alice3-taskcorrelationddbar SOURCES alice3-taskcorrelationDDbar.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore diff --git a/ALICE3/Tasks/alice3-dilepton.cxx b/ALICE3/Tasks/alice3-dilepton.cxx index f9b3d5e0d5f..e6fa5079cc3 100644 --- a/ALICE3/Tasks/alice3-dilepton.cxx +++ b/ALICE3/Tasks/alice3-dilepton.cxx @@ -528,7 +528,8 @@ struct Alice3Dilepton { SliceCache cache_rec; Configurable pdg{"pdg", 11, "pdg code for analysis. dielectron:11, dimuon:13"}; - Configurable requireHFEid{"requireHFEid", true, "Require HFE identification"}; + Configurable requireDCid{"requireDCid", false, "Require pi0 or photon as mother"}; + Configurable requireHFEid{"requireHFEid", false, "Require HFE identification"}; Configurable contamination{"contamination", false, "Fill only pairs with one misidentifixed electrons"}; Configurable ptMin{"ptMin", 0.f, "Lower limit in pT"}; Configurable ptMax{"ptMax", 5.f, "Upper limit in pT"}; @@ -630,6 +631,26 @@ struct Alice3Dilepton { } } + template + bool IsDC(TTrack const& track, TMCParticles const& mcparticles) + { + if (!track.has_mcParticle()) { + return false; + } + const auto p1 = track.template mcParticle_as(); + if (!p1.has_mothers()) { + return false; + } + int motherId = p1.mothersIds()[0]; + if (motherId > -1) { + auto mp = mcparticles.rawIteratorAt(motherId); + if ((std::abs(mp.pdgCode()) == PDG_t::kPi0) || (std::abs(mp.pdgCode()) == PDG_t::kGamma)) { + return true; + } + } + return false; + } + template bool IsHF(TTrack const& track, TMCParticles const& mcparticles) { @@ -843,6 +864,11 @@ struct Alice3Dilepton { continue; } } + if (requireDCid) { + if (!IsDC(t1, mcParticles) && !IsDC(t2, mcParticles)) { + continue; + } + } float pair_dca_xy = 999.f; ROOT::Math::PtEtaPhiMVector v12 = buildPairDCA(t1, t2, pair_dca_xy); @@ -873,6 +899,11 @@ struct Alice3Dilepton { continue; } } + if (requireDCid) { + if (!IsDC(t1, mcParticles) && !IsDC(t2, mcParticles)) { + continue; + } + } float pair_dca_xy = 999.f; ROOT::Math::PtEtaPhiMVector v12 = buildPairDCA(t1, t2, pair_dca_xy); diff --git a/ALICE3/Tasks/alice3-prefilterdimuon.cxx b/ALICE3/Tasks/alice3-prefilterdimuon.cxx new file mode 100644 index 00000000000..c797296b4a6 --- /dev/null +++ b/ALICE3/Tasks/alice3-prefilterdimuon.cxx @@ -0,0 +1,418 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// \file alice3-dimuon.cxx +/// \author s.scheid@cern.ch, daiki.sekihata@cern.ch +/// + +#include "ALICE3/DataModel/OTFMID.h" +#include "ALICE3/DataModel/prefilterDilepton.h" +#include "ALICE3/DataModel/tracksAlice3.h" +#include "Common/Core/RecoDecay.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include + +#include +#include +#include +#include + +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::soa; +using namespace o2::framework; +using namespace o2::framework::expressions; + +struct Alice3DimuonEventCentSelection { + + Service inspdg; + + Produces eventCentSel; + Configurable minFITPart{"minFITPart", 0.f, "Minimum number of charged particles in the FIT acceptance"}; + Configurable maxFITPart{"maxFITPart", 0.f, "Maximum number of charged particles in the FIT acceptance"}; + + HistogramRegistry registry{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + void init(InitContext&) + { + registry.add("Generated/Before/ParticlesFit", "Charged Particles in Fit acceptance per event", kTH1F, {{15000, 0, 15000}}); + registry.add("Generated/After/ParticlesFit", "Charged Particles in Fit acceptance per event", kTH1F, {{15000, 0, 15000}}); + registry.add("Generated/Before/ParticlesEta", "Charged Particles per event", kTH1F, {{1200, -6, 6}}); + registry.add("Generated/After/ParticlesEta", "Charged Particles per event", kTH1F, {{1200, -6, 6}}); + } + + using MyEvents = soa::Join; + Preslice perMCCollision = o2::aod::mcparticle::mcCollisionId; + + void processGen(MyEvents::iterator const& event, o2::aod::McCollisions const&, aod::McParticles const& mcParticles) + { + if (!event.has_mcCollision()) { + eventCentSel(0); + } else { + auto mccollision = event.mcCollision(); + auto mcParticles_per_coll = mcParticles.sliceBy(perMCCollision, mccollision.globalIndex()); + int nParticlesFIT = 0; + for (const auto& mcParticle : mcParticles_per_coll) { + if (mcParticle.isPhysicalPrimary()) { + auto pdgParticle = inspdg->GetParticle(mcParticle.pdgCode()); + if (pdgParticle) { + float charge = pdgParticle->Charge() / 3.f; // Charge in units of |e| + if (std::abs(charge) >= 1.) { + registry.fill(HIST("Generated/Before/ParticlesEta"), mcParticle.eta()); + if ((2.2 < mcParticle.eta() && mcParticle.eta() < 5.0) || (-3.4 < mcParticle.eta() && mcParticle.eta() < -2.3)) { + nParticlesFIT++; + } + } + } + } + } // end of mc particle loop + registry.fill(HIST("Generated/Before/ParticlesFit"), nParticlesFIT); + if (nParticlesFIT > minFITPart && nParticlesFIT < maxFITPart) { + registry.fill(HIST("Generated/After/ParticlesFit"), nParticlesFIT); + for (const auto& mcParticle : mcParticles_per_coll) { + if (mcParticle.isPhysicalPrimary()) { + auto pdgParticle = inspdg->GetParticle(mcParticle.pdgCode()); + if (pdgParticle) { + float charge = pdgParticle->Charge() / 3.f; // Charge in units of |e| + if (std::abs(charge) >= 1.) { + registry.fill(HIST("Generated/After/ParticlesEta"), mcParticle.eta()); + } + } + } + } // end of mc particle loop + eventCentSel(1); + } else { + eventCentSel(0); + } + } // if mc collision loop + } // end of process + + void processDummy(MyEvents::iterator const&) + { + eventCentSel(1); + } + + PROCESS_SWITCH(Alice3DimuonEventCentSelection, processGen, "Select centrality", false); + PROCESS_SWITCH(Alice3DimuonEventCentSelection, processDummy, "Dummy", true); +}; + +struct Alice3DimuonPrefilter { + + Produces pfb_derived; + + SliceCache cache_mc; + SliceCache cache_rec; + + Service inspdg; + + Configurable ptMin{"ptMin", 0.f, "Lower limit in pT"}; + Configurable ptMax{"ptMax", 5.f, "Upper limit in pT"}; + Configurable etaMin{"etaMin", -5.f, "Lower limit in eta"}; + Configurable etaMax{"etaMax", 5.f, "Upper limit in eta"}; + Configurable maxMass{"maxMass", 5.f, "Upper limit in mass"}; + Configurable maxOp{"maxOp", 5.f, "Upper limit in opening angle"}; + Configurable isMuMID{"isMuMID", true, "Muon candidate reaching MID"}; + Configurable nBinsP{"nBinsP", 80, "number of bins in momentum/pT depending on doSeparationVsPt"}; + Configurable nBinsEta{"nBinsEta", 400, "number of bins plot relative eta error"}; + std::unordered_map map_pfb; // map track.globalIndex -> prefilter bit + + HistogramRegistry registry{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + using MyTracksWithSmearings = soa::Join; + using MyTracksWithSmearing = MyTracksWithSmearings::iterator; + using MyTracksMCs = soa::Join; + using MyTracksMC = MyTracksMCs::iterator; + using DileptonCollisions = soa::Join; + using DileptonCollision = DileptonCollisions::iterator; + + Preslice perCollision = aod::track::collisionId; + Partition posTracks = o2::aod::track::signed1Pt > 0.f; + Partition negTracks = o2::aod::track::signed1Pt < 0.f; + Partition posTracksWithSmearing = o2::aod::track::signed1Pt > 0.f; + Partition negTracksWithSmearing = o2::aod::track::signed1Pt < 0.f; + + void init(InitContext&) + { + const AxisSpec axisM{500, 0, 5, "#it{m}_{ll} (GeV/#it{c}^{2})"}; + // const AxisSpec axisPt{1000, 0, 10, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisPt{nBinsP, 0.0f, +10.0f, "#it{p}_{T} (GeV/#it{c})"}; + // const AxisSpec axisSigmaEl{200, -10, 10, "n#sigma_{El}"}; + const AxisSpec axisTrackLengthMID{310, 0., 310., "Track length (cm)"}; + // const AxisSpec axisEta{1000, -5, 5, "#it{#eta}"}; + const AxisSpec axisEta{static_cast(nBinsEta), -2.0f, +2.0f, "#eta"}; + const AxisSpec axisPhi{360, 0, TMath::TwoPi(), "#it{#varphi} (rad.)"}; + const AxisSpec axisProdx{2000, -100, 100, "Prod. Vertex X (cm)"}; + const AxisSpec axisPrody{2000, -100, 100, "Prod. Vertex Y (cm)"}; + const AxisSpec axisProdz{2000, -100, 100, "Prod. Vertex Z (cm)"}; + + registry.add("Reconstructed/Pair/ULS/Mass_Pt", "Pair Mass vs. Pt", kTH2F, {axisM, axisPt}, true); + registry.add("ReconstructedFiltered/Pair/ULS/Mass_Pt", "Pair Mass vs. Pt", kTH2F, {axisM, axisPt}, true); + registry.add("Reconstructed/Track/Pt", "Track Pt", kTH1F, {axisPt}); + registry.add("Reconstructed/Track/Eta_P", "Track Eta vs Track P", kTH2F, {axisPt, axisEta}); + registry.add("Reconstructed/Track/Eta", "Track Eta", kTH1F, {axisEta}); + registry.add("Reconstructed/Track/Phi", "Track Phi", kTH1F, {axisPhi}); + registry.add("Reconstructed/Track/Eta_Pt", "Eta vs. Pt", kTH2F, {axisPt, axisEta}, true); + + if (doprocessPreFilter) { + // registry.add("Reconstructed/Track/SigmaOTofvspt", "Track #sigma oTOF", kTH2F, {axisPt, axisSigmaEl}); + // registry.add("Reconstructed/Track/SigmaITofvspt", "Track #sigma iTOF", kTH2F, {axisPt, axisSigmaEl}); + // registry.add("Reconstructed/Track/SigmaRichvspt", "Track #sigma RICH", kTH2F, {axisPt, axisSigmaEl}); + registry.add("Reconstructed/Track/midTrackLength", "Track length MID", kTH1F, {axisTrackLengthMID}); + } + } + + template + void PreFilter(TTracks const& posTracks_coll, TTracks const& negTracks_coll) + { + for (const auto& pos : posTracks_coll) { + if constexpr (isWithSmearing) { + if (!pos.isReconstructed() || !pos.selected()) { + continue; + } + if (pos.etaSmeared() < etaMin || etaMax < pos.etaSmeared()) { + continue; + } + if (pos.ptSmeared() < ptMin || ptMax < pos.ptSmeared()) { + continue; + } + registry.fill(HIST("Reconstructed/Track/Pt"), pos.ptSmeared()); + registry.fill(HIST("Reconstructed/Track/Eta"), pos.etaSmeared()); + // registry.fill(HIST("Reconstructed/Track/Eta_P"), pos.momentumSmeared(),pos.etaSmeared()); + registry.fill(HIST("Reconstructed/Track/Phi"), pos.phiSmeared()); + registry.fill(HIST("Reconstructed/Track/Eta_Pt"), pos.ptSmeared(), pos.etaSmeared()); + } else { + if (!pos.isReconstructed()) { + continue; + } + if (pos.eta() < etaMin || etaMax < pos.eta()) { + continue; + } + if (pos.pt() < ptMin || ptMax < pos.pt()) { + continue; + } + if (pos.midCandidate()) { + registry.fill(HIST("Reconstructed/Track/midTrackLength"), pos.midTrackLength()); + registry.fill(HIST("Reconstructed/Track/Pt"), pos.pt()); + registry.fill(HIST("Reconstructed/Track/Eta"), pos.eta()); + registry.fill(HIST("Reconstructed/Track/Eta_P"), pos.p(), pos.eta()); + registry.fill(HIST("Reconstructed/Track/Phi"), pos.phi()); + registry.fill(HIST("Reconstructed/Track/Eta_Pt"), pos.pt(), pos.eta()); + } + } + } + for (const auto& ele : negTracks_coll) { + if constexpr (isWithSmearing) { + if (!ele.isReconstructed() || !ele.selected()) { + continue; + } + if (ele.etaSmeared() < etaMin || etaMax < ele.etaSmeared()) { + continue; + } + if (ele.ptSmeared() < ptMin || ptMax < ele.ptSmeared()) { + continue; + } + registry.fill(HIST("Reconstructed/Track/Pt"), ele.ptSmeared()); + registry.fill(HIST("Reconstructed/Track/Eta"), ele.etaSmeared()); + // registry.fill(HIST("Reconstructed/Track/Eta_P"), ele.pSmeared(),ele.etaSmeared()); + registry.fill(HIST("Reconstructed/Track/Phi"), ele.phiSmeared()); + registry.fill(HIST("Reconstructed/Track/Eta_Pt"), ele.ptSmeared(), ele.etaSmeared()); + } else { + if (!ele.isReconstructed()) { + continue; + } + if (ele.eta() < etaMin || etaMax < ele.eta()) { + continue; + } + if (ele.pt() < ptMin || ptMax < ele.pt()) { + continue; + } + if (ele.midCandidate()) { + registry.fill(HIST("Reconstructed/Track/midTrackLength"), ele.midTrackLength()); + registry.fill(HIST("Reconstructed/Track/Pt"), ele.pt()); + registry.fill(HIST("Reconstructed/Track/Eta"), ele.eta()); + registry.fill(HIST("Reconstructed/Track/Eta_P"), ele.p(), ele.eta()); + registry.fill(HIST("Reconstructed/Track/Phi"), ele.phi()); + registry.fill(HIST("Reconstructed/Track/Eta_Pt"), ele.pt(), ele.eta()); + } + } + } + + for (const auto& [pos, ele] : combinations(CombinationsFullIndexPolicy(posTracks_coll, negTracks_coll))) { // ULS + if constexpr (isWithSmearing) { + if (!pos.isReconstructed() || !pos.selected()) { + continue; + } + if (pos.etaSmeared() < etaMin || etaMax < pos.etaSmeared()) { + continue; + } + if (pos.ptSmeared() < ptMin || ptMax < pos.ptSmeared()) { + continue; + } + if (!ele.isReconstructed() || !ele.selected()) { + continue; + } + if (ele.etaSmeared() < etaMin || etaMax < ele.etaSmeared()) { + continue; + } + if (ele.ptSmeared() < ptMin || ptMax < ele.ptSmeared()) { + continue; + } + } else { + if (!pos.isReconstructed()) { + continue; + } + if (pos.eta() < etaMin || etaMax < pos.eta()) { + continue; + } + if (pos.pt() < ptMin || ptMax < pos.pt()) { + continue; + } + if (!ele.isReconstructed()) { + continue; + } + if (ele.eta() < etaMin || etaMax < ele.eta()) { + continue; + } + if (ele.pt() < ptMin || ptMax < ele.pt()) { + continue; + } + } + if constexpr (isWithSmearing) { + + ROOT::Math::PtEtaPhiMVector v1(pos.ptSmeared(), pos.etaSmeared(), pos.phiSmeared(), o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v2(ele.ptSmeared(), ele.etaSmeared(), ele.phiSmeared(), o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; + float angle = RecoDecay::constrainAngle(ROOT::Math::VectorUtil::Angle(v1, v2), -M_PI); + // o2::math_utils::bringToPMPi(angle); + + registry.fill(HIST("Reconstructed/Pair/ULS/Mass_Pt"), v12.M(), v12.Pt()); + + if (v12.M() < maxMass && std::abs(angle) < maxOp) { + map_pfb[pos.globalIndex()] = 1; + map_pfb[ele.globalIndex()] = 1; + registry.fill(HIST("ReconstructedFiltered/Pair/ULS/Mass_Pt"), v12.M(), v12.Pt()); + } + + } else { + + if (pos.midCandidate()) { + if (ele.midCandidate()) { + + ROOT::Math::PtEtaPhiMVector v1(pos.pt(), pos.eta(), pos.phi(), o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v2(ele.pt(), ele.eta(), ele.phi(), o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; + float angle = RecoDecay::constrainAngle(ROOT::Math::VectorUtil::Angle(v1, v2), -M_PI); + // o2::math_utils::bringToPMPi(angle); + + registry.fill(HIST("Reconstructed/Pair/ULS/Mass_Pt"), v12.M(), v12.Pt()); + + if (v12.M() < maxMass && std::abs(angle) < maxOp) { + map_pfb[pos.globalIndex()] = 1; + map_pfb[ele.globalIndex()] = 1; + registry.fill(HIST("ReconstructedFiltered/Pair/ULS/Mass_Pt"), v12.M(), v12.Pt()); + } + } + } + } + } // combination + } + + void processPreFilter(DileptonCollisions const& collisions, MyTracksMCs const& tracks) + { + for (const auto& track : tracks) { + map_pfb[track.globalIndex()] = 0; + } // end of track loop + + for (const auto& collision : collisions) { + auto negTracks_coll = negTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache_rec); + auto posTracks_coll = posTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache_rec); + + if (collision.isEventCentSelected() == 0) { + for (const auto& pos : posTracks_coll) { + map_pfb[pos.globalIndex()] = 0; + } + for (const auto& neg : negTracks_coll) { + map_pfb[neg.globalIndex()] = 0; + } + continue; + } + PreFilter(posTracks_coll, negTracks_coll); + } + for (const auto& track : tracks) { + pfb_derived(map_pfb[track.globalIndex()]); + } // end of track loop + map_pfb.clear(); + } + + void processPreFilterWithSmearing(DileptonCollisions const& collisions, MyTracksWithSmearings const& tracks) + { + for (const auto& track : tracks) { + map_pfb[track.globalIndex()] = 0; + } // end of track loop + + for (const auto& collision : collisions) { + auto negTracks_coll = negTracksWithSmearing->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache_rec); + auto posTracks_coll = posTracksWithSmearing->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache_rec); + + if (collision.isEventCentSelected() == 0) { + for (const auto& pos : posTracks_coll) { + map_pfb[pos.globalIndex()] = 0; + } + for (const auto& neg : negTracks_coll) { + map_pfb[neg.globalIndex()] = 0; + } + continue; + } + PreFilter(posTracks_coll, negTracks_coll); + } + for (const auto& track : tracks) { + pfb_derived(map_pfb[track.globalIndex()]); + } // end of track loop + map_pfb.clear(); + } + + void processDummy(MyTracksMCs const& tracks) + { + for (int i = 0; i < tracks.size(); i++) { + pfb_derived(0); + } + } + + PROCESS_SWITCH(Alice3DimuonPrefilter, processPreFilter, "Run prefilter", false); + PROCESS_SWITCH(Alice3DimuonPrefilter, processPreFilterWithSmearing, "Run prefilter with smearing", false); + PROCESS_SWITCH(Alice3DimuonPrefilter, processDummy, "Dummy", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc)}; +} diff --git a/ALICE3/Tasks/alice3-qa-multiplicity.cxx b/ALICE3/Tasks/alice3-qa-multiplicity.cxx index a5440ff8d53..5e81cd8fd5a 100644 --- a/ALICE3/Tasks/alice3-qa-multiplicity.cxx +++ b/ALICE3/Tasks/alice3-qa-multiplicity.cxx @@ -41,7 +41,7 @@ struct ALICE3MultTask { void init(InitContext&) { - const AxisSpec axisMult{MaxMult.value > 10000.f ? 10000 : (int)MaxMult, 0, MaxMult, "Reconstructed tracks"}; + const AxisSpec axisMult{MaxMult.value > 10000.f ? 10000 : static_cast(MaxMult), 0, MaxMult, "Reconstructed tracks"}; TString tit = Form("%.3f < #it{#eta} < %.3f", MinEta.value, MaxEta.value); histos.add("multiplicity/numberOfTracksNoCut", tit, kTH1D, {axisMult}); histos.add("multiplicity/numberOfTracks", tit, kTH1D, {axisMult}); diff --git a/ALICE3/Tasks/alice3-qa-singleparticle.cxx b/ALICE3/Tasks/alice3-qa-singleparticle.cxx index 25d21fcc7cd..4fe232bf07f 100644 --- a/ALICE3/Tasks/alice3-qa-singleparticle.cxx +++ b/ALICE3/Tasks/alice3-qa-singleparticle.cxx @@ -10,7 +10,7 @@ // or submit itself to any jurisdiction. /// -/// \file qa-singleparticle.cxx +/// \file alice3-qa-singleparticle.cxx /// \author Nicolò Jacazio nicolo.jacazio@cern.ch /// \brief Task to monitor the single particle QA, at the particle and track level, showing the tracked and the origin of particles /// @@ -28,7 +28,6 @@ #include #include -#include #include #include #include @@ -38,7 +37,9 @@ #include #include +#include #include +#include #include using namespace o2; @@ -46,26 +47,55 @@ using namespace o2::framework; using namespace o2::framework::expressions; struct Alice3SingleParticle { - Service pdg; - Configurable PDG{"PDG", 2212, "PDG code of the particle of interest"}; - Configurable IsStable{"IsStable", 0, "Flag to check stable particles"}; + Service pdg{}; HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - Configurable ptBins{"pt-bins", 500, "Number of pT bins"}; - Configurable ptMin{"pt-min", 0.f, "Lower limit in pT"}; - Configurable ptMax{"pt-max", 10.f, "Upper limit in pT"}; - Configurable etaBins{"eta-bins", 500, "Number of eta bins"}; - Configurable etaMin{"eta-min", -3.f, "Lower limit in eta"}; - Configurable etaMax{"eta-max", 3.f, "Upper limit in eta"}; - Configurable yMin{"y-min", -3.f, "Lower limit in y"}; - Configurable yMax{"y-max", 3.f, "Upper limit in y"}; - Configurable prodBins{"prod-bins", 100, "Number of production vertex bins"}; - Configurable prodMin{"prod-min", -1.f, "Lower limit in production vertex"}; - Configurable prodMax{"prod-max", 1.f, "Upper limit in production vertex"}; - Configurable prodBinsZ{"prod-bins-z", 100, "Number of production vertex bins"}; - Configurable prodMinZ{"prod-min-z", -10.f, "Lower limit in production vertex along Z"}; - Configurable prodMaxZ{"prod-max-z", 10.f, "Upper limit in production vertex along Z"}; - Configurable charge{"charge", 1.f, "Particle charge to scale the reconstructed momentum"}; - Configurable doPrint{"doPrint", false, "Flag to print debug messages"}; + Configurable pdgCode{"pdgCode", 2212, "PDG code of the particle of interest"}; + + struct : ConfigurableGroup { + std::string prefix = "settings"; + Configurable isStable{"isStable", 0, "Flag to check stable particles"}; + Configurable charge{"charge", 1.f, "Particle charge to scale the reconstructed momentum"}; + Configurable nchEtaMax{"nchEtaMax", 0.5f, "|eta| acceptance of the Nch estimator (use the Ditto tune mActivityEtaMax)"}; + Configurable doPrint{"doPrint", false, "Flag to print debug messages"}; + } settings; + + // The pT, eta, y and production-vertex-z axes also define the selection of the particles of interest + struct : ConfigurableGroup { + std::string prefix = "settingsAxes"; + ConfigurableAxis axisMomentum{"axisMomentum", {500, 0.f, 10.f}, "Binning for p and pT"}; + ConfigurableAxis axisMomentumComponent{"axisMomentumComponent", {500, -10.f, 10.f}, "Binning for px, py and pz"}; + ConfigurableAxis axisEnergy{"axisEnergy", {500, 0.f, 1000.f}, "Binning for the energy"}; + ConfigurableAxis axisEta{"axisEta", {500, -3.f, 3.f}, "Binning for eta"}; + ConfigurableAxis axisY{"axisY", {200, -3.f, 3.f}, "Binning for rapidity"}; + ConfigurableAxis axisVtxXY{"axisVtxXY", {100, -1.f, 1.f}, "Binning for the event vertex in X and Y"}; + ConfigurableAxis axisVtxZ{"axisVtxZ", {100, -20.f, 20.f}, "Binning for the event vertex in Z"}; + ConfigurableAxis axisProdXY{"axisProdXY", {100, -1.f, 1.f}, "Binning for the production vertex in X and Y"}; + ConfigurableAxis axisProdZ{"axisProdZ", {100, -10.f, 10.f}, "Binning for the production vertex in Z"}; + ConfigurableAxis axisProdRadius{"axisProdRadius", {100, 0.f, 2.f}, "Binning for the production vertex radius"}; + ConfigurableAxis axisNch{"axisNch", {150, 0.f, 150.f}, "Charged-particle multiplicity"}; + ConfigurableAxis axisNSignal{"axisNSignal", {20, -0.5f, 19.5f}, "Number of selected particles of interest per collision"}; + ConfigurableAxis distanceAxis{"distanceAxis", {1000, 0, 1e-10}, "Distance (cm)"}; + } settingsAxes; + + std::vector particlesOfInterest; // global indices of the MC particles selected in genericProcessParticles, per DF + + // Selection range [min, max) of the particles of interest, taken from an axis so that every selected particle is inside its histograms + struct Range { + Range() = default; + Range(double min_, double max_, int64_t bins_) : min(min_), max(max_), bins(bins_) {} + double min = 0.; + double max = 0.; + int64_t bins = 0; + int getBins() const { return static_cast(bins); } + [[nodiscard]] bool contains(double value) const { return value >= min && value < max; } + }; + Range ptRange, etaRange, yRange, prodXYrange, prodZRange; + + static Range getRange(const ConfigurableAxis& axis) + { + const AxisSpec spec{axis}; + return Range(spec.binEdges.front(), spec.binEdges.back(), spec.getNbins()); + } void init(InitContext&) { @@ -95,107 +125,131 @@ struct Alice3SingleParticle { pdg->AddParticle("helium4", "helium4", o2::constants::physics::MassAlpha, kTRUE, 0.0, 6, "Nucleus", 1000020040); pdg->AddAntiParticle("anti-helium4", -1000020040); - const TString tit = Form("%i", PDG.value); - const AxisSpec axisVx{100, -1, 1, "Vtx_{x}"}; - const AxisSpec axisVy{100, -1, 1, "Vtx_{y}"}; - const AxisSpec axisVz{100, -20, 20, "Vtx_{z}"}; + ptRange = getRange(settingsAxes.axisMomentum); + etaRange = getRange(settingsAxes.axisEta); + yRange = getRange(settingsAxes.axisY); + prodXYrange = getRange(settingsAxes.axisProdXY); + prodZRange = getRange(settingsAxes.axisProdZ); + LOG(info) << "Selecting particles of interest in pT [" << ptRange.min << ", " << ptRange.max << "), eta [" << etaRange.min << ", " << etaRange.max + << "), y [" << yRange.min << ", " << yRange.max << "), production z [" << prodZRange.min << ", " << prodZRange.max << ")"; + + const TString titlePdgCode = Form("%i", pdgCode.value); const AxisSpec axisPDGs{100, 0.f, 100.f, "PDG code"}; const AxisSpec axisCharge{21, -10.5f, 10.5f, "Charge (e/3)"}; - const AxisSpec axisP{ptBins, ptMax, ptMax, "#it{p} (GeV/#it{c})"}; - const AxisSpec axisPt{ptBins, ptMin, ptMax, "#it{p}_{T} (GeV/#it{c})"}; - const AxisSpec axisPtMC{ptBins, ptMin, ptMax, "#it{p}_{T}^{MC} (GeV/#it{c})"}; - const AxisSpec axisPx{ptBins, -ptMax, ptMax, "#it{p}_{x} (GeV/#it{c})"}; - const AxisSpec axisPy{ptBins, -ptMax, ptMax, "#it{p}_{y} (GeV/#it{c})"}; - const AxisSpec axisPz{ptBins, -ptMax, ptMax, "#it{p}_{z} (GeV/#it{c})"}; - const AxisSpec axisEta{etaBins, etaMin, etaMax, "#it{#eta}"}; - const AxisSpec axisY{etaBins, yMin, yMax, "#it{y}"}; - const AxisSpec axisE{etaBins, 0, 1000, "E"}; - const AxisSpec axisProdx{prodBins, prodMin, prodMax, "Prod. Vertex X (cm)"}; - const AxisSpec axisPrody{prodBins, prodMin, prodMax, "Prod. Vertex Y (cm)"}; - const AxisSpec axisProdz{prodBinsZ, prodMinZ, prodMaxZ, "Prod. Vertex Z (cm)"}; - const AxisSpec axisProdRadius{prodBins, 0., 2. * prodMax, "Prod. Vertex Radius (cm)"}; + const AxisSpec axisP{settingsAxes.axisMomentum, "#it{p} (GeV/#it{c})"}; + const AxisSpec axisPt{settingsAxes.axisMomentum, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisPtMC{settingsAxes.axisMomentum, "#it{p}_{T}^{MC} (GeV/#it{c})"}; + const AxisSpec axisPx{settingsAxes.axisMomentumComponent, "#it{p}_{x} (GeV/#it{c})"}; + const AxisSpec axisPy{settingsAxes.axisMomentumComponent, "#it{p}_{y} (GeV/#it{c})"}; + const AxisSpec axisPz{settingsAxes.axisMomentumComponent, "#it{p}_{z} (GeV/#it{c})"}; + const AxisSpec axisEta{settingsAxes.axisEta, "#it{#eta}"}; + const AxisSpec axisY{settingsAxes.axisY, "#it{y}"}; + const AxisSpec axisE{etaRange.getBins(), 0, 1000, "E"}; + const AxisSpec axisProdx{settingsAxes.axisProdXY, "Prod. Vertex X (cm)"}; + const AxisSpec axisPrody{settingsAxes.axisProdXY, "Prod. Vertex Y (cm)"}; + const AxisSpec axisProdz{settingsAxes.axisProdZ, "Prod. Vertex Z (cm)"}; + const AxisSpec axisProdRadius{prodXYrange.getBins(), 0., 2. * prodXYrange.max, "Prod. Vertex Radius (cm)"}; + const AxisSpec axisDistx{settingsAxes.distanceAxis, "Distance X (fm)"}; + const AxisSpec axisDisty{settingsAxes.distanceAxis, "Distance Y (fm)"}; + const AxisSpec axisDistz{settingsAxes.distanceAxis, "Distance Z (fm)"}; + const AxisSpec axisProdDistance{settingsAxes.distanceAxis, "Distance Radius (fm)"}; + const AxisSpec axisVx{settingsAxes.axisVtxXY, "Vertex X (cm)"}; + const AxisSpec axisVy{settingsAxes.axisVtxXY, "Vertex Y (cm)"}; + const AxisSpec axisVz{settingsAxes.axisVtxZ, "Vertex Z (cm)"}; + + histos.add("AnalysedParticlePdgCode", "Analysed particle PDG code", kTH1D, {{1, -0.5, 0.5}}); + histos.get(HIST("AnalysedParticlePdgCode"))->SetBinContent(1, pdgCode); + + const auto* particle = pdg->GetParticle(pdgCode); + histos.add("AnalysedParticleMass", "Analysed particle mass", kTH1D, {{1, -0.5, 0.5}}); + histos.get(HIST("AnalysedParticleMass"))->SetBinContent(1, particle ? particle->Mass() : -1.); histos.add("event/VtxX", "Vertex X", kTH1D, {axisVx}); histos.add("event/VtxY", "Vertex Y", kTH1D, {axisVy}); histos.add("event/VtxZ", "Vertex Z", kTH1D, {axisVz}); + histos.add("event/Nch", "Events;N_{ch};Events", kTH1D, {settingsAxes.axisNch}); + histos.add("particle/NSignalVsNch", "Particles of interest per event " + titlePdgCode + ";N_{ch};N_{signal}", kTH2D, {settingsAxes.axisNch, settingsAxes.axisNSignal}); histos.add("particle/PDGs", "Particle PDGs", kTH2D, {axisPDGs, axisCharge}); histos.add("particle/PDGsPrimaries", "Particle PDGs of Primaries", kTH2D, {axisPDGs, axisCharge}); histos.add("particle/PDGsSecondaries", "Particle PDGs of Secondaries", kTH2D, {axisPDGs, axisCharge}); - auto h = histos.add("particle/producedProcess", "Particle process of production " + tit, kTH1D, {{kMaxMCProcess, -0.5, -0.5 + kMaxMCProcess, "getProcess"}}); + auto h = histos.add("particle/producedProcess", "Particle process of production " + titlePdgCode, kTH1D, {{kMaxMCProcess, -0.5, -0.5 + kMaxMCProcess, "getProcess"}}); for (int i = 0; i < kMaxMCProcess; i++) { h->GetXaxis()->SetBinLabel(i + 1, TMCProcessName[i]); } - histos.add("particle/Pt", "Particle Pt " + tit, kTH1D, {axisPt}); - histos.add("particle/P", "Particle P " + tit, kTH1D, {axisP}); - histos.add("particle/primariesPt", "Particle Pt (primary) " + tit, kTH1D, {axisPt}); - histos.add("particle/primariesP", "Particle P (primary) " + tit, kTH1D, {axisP}); - histos.add("particle/secondariesPt", "Particle Pt (secondary) " + tit, kTH1D, {axisPt}); - histos.add("particle/secondariesP", "Particle P (secondary) " + tit, kTH1D, {axisP}); - histos.add("particle/prodVx", "Particle Prod. Vertex X " + tit, kTH1D, {axisProdx}); - histos.add("particle/prodVy", "Particle Prod. Vertex Y " + tit, kTH1D, {axisPrody}); - histos.add("particle/prodVz", "Particle Prod. Vertex Z " + tit, kTH1D, {axisProdz}); - histos.add("particle/prodRadius", "Particle Prod. Vertex Radius " + tit, kTH1D, {axisProdRadius}); - histos.add("particle/prodVxVy", "Particle Prod. Vertex X vs Vertex Y " + tit, kTH2D, {axisProdx, axisPrody}); - histos.add("particle/prodVxVyPrm", "Particle Prod. Vertex X vs Vertex Y Primary " + tit, kTH2D, {axisProdx, axisPrody}); - histos.add("particle/prodVxVyStr", "Particle Prod. Vertex X vs Vertex Y Decay " + tit, kTH2D, {axisProdx, axisPrody}); - histos.add("particle/prodVxVyMat", "Particle Prod. Vertex X vs Vertex Y Material " + tit, kTH2D, {axisProdx, axisPrody}); - histos.add("particle/prodVxVsPt", "Particle Prod. Vertex X " + tit, kTH2D, {axisPt, axisProdx}); - histos.add("particle/prodVyVsPt", "Particle Prod. Vertex Y " + tit, kTH2D, {axisPt, axisPrody}); - histos.add("particle/prodVzVsPt", "Particle Prod. Vertex Z " + tit, kTH2D, {axisPt, axisProdz}); - histos.add("particle/prodRadiusVsPt", "Particle Prod. Vertex Radius " + tit, kTH2D, {axisPt, axisProdRadius}); - histos.add("particle/prodRadiusVsEta", "Particle Prod. Vertex Radius " + tit, kTH2D, {axisEta, axisProdRadius}); - histos.add("particle/prodRadius3DVsPt", "Particle Prod. Vertex Radius XYZ " + tit, kTH2D, {axisPt, axisProdRadius}); - histos.add("particle/Eta", "Particle Eta " + tit, kTH1D, {axisEta}); - histos.add("particle/primariesEta", "Particle Eta (primary) " + tit, kTH1D, {axisEta}); - histos.add("particle/secondariesEta", "Particle Eta (secondary) " + tit, kTH1D, {axisEta}); - histos.add("particle/Y", "Particle Y " + tit, kTH1D, {axisY}); - histos.add("particle/primariesY", "Particle Y (primary)" + tit, kTH1D, {axisY}); - histos.add("particle/secondariesY", "Particle Y (secondary)" + tit, kTH1D, {axisY}); - histos.add("particle/EvsPz", "Particle E vs Pz " + tit, kTH2D, {axisE, axisPz}); - histos.add("particle/YvzPz", "Particle Y vs Pz " + tit, kTH2D, {axisY, axisPz}); - histos.add("particle/EtavzPz", "Particle Eta vs Pz " + tit, kTH2D, {axisEta, axisPz}); - histos.add("particle/PtvzPz", "Particle Pt vs Pz " + tit, kTH2D, {axisPt, axisPz}); - histos.add("particle/PvzPz", "Particle P vs Pz " + tit, kTH2D, {axisP, axisPz}); - histos.add("particle/Px", "Particle Px " + tit, kTH1D, {axisPx}); - histos.add("particle/Py", "Particle Py " + tit, kTH1D, {axisPy}); - histos.add("particle/Pz", "Particle Pz " + tit, kTH1D, {axisPz}); - histos.add("particle/decayDistanceX", "Particle decay distance " + tit, kTH1D, {axisProdx}); - histos.add("particle/decayDistanceY", "Particle decay distance " + tit, kTH1D, {axisPrody}); - histos.add("particle/decayDistanceZ", "Particle decay distance " + tit, kTH1D, {axisProdz}); - histos.add("particle/decayDistanceVsPt", "Particle decay distance " + tit, kTH2D, {axisPt, axisProdRadius}); - histos.add("particle/decayCtau", "Particle decay distance " + tit, kTH1D, {{1000, 0, 10000, "c#tau (#mum)"}}); - - histos.add("particle/daughters/Number", "Number of Daughters " + tit, kTH1D, {{20, -0.5, 19.5}}); - histos.add("particle/daughters/PDGs", "Daughters PDGs " + tit, kTH2D, {axisPDGs, axisCharge}); - histos.add("particle/daughters/PDGsPrimaries", "Daughters PDGs Primaries of " + tit, kTH2D, {axisPDGs, axisCharge}); - histos.add("particle/daughters/PDGsSecondaries", "Daughters PDGs Secondaries of " + tit, kTH2D, {axisPDGs, axisCharge}); - histos.add("particle/daughters/prodVx", "Daughters Prod. Vertex X " + tit, kTH1D, {axisProdx}); - histos.add("particle/daughters/prodVy", "Daughters Prod. Vertex Y " + tit, kTH1D, {axisPrody}); - histos.add("particle/daughters/prodVz", "Daughters Prod. Vertex Z " + tit, kTH1D, {axisProdz}); - histos.add("particle/daughters/prodRadiusVsPt", "Daughters Prod. Vertex Radius " + tit, kTH2D, {axisPt, axisProdRadius}); - histos.add("particle/daughters/prodRadius3DVsPt", "Daughters Prod. Vertex Radius XYZ " + tit, kTH2D, {axisPt, axisProdRadius}); - - histos.add("particle/mothers/Number", "Number of Mothers " + tit, kTH1D, {{20, -0.5, 19.5}}); - histos.add("particle/mothers/PDGs", "Mothers PDGs " + tit, kTH2D, {axisPDGs, axisCharge}); - histos.add("particle/mothers/PDGsPrimaries", "Mothers PDGs Primaries of " + tit, kTH2D, {axisPDGs, axisCharge}); - histos.add("particle/mothers/PDGsSecondaries", "Mothers PDGs Secondaries of " + tit, kTH2D, {axisPDGs, axisCharge}); - histos.add("particle/mothers/prodVx", "Mothers Prod. Vertex X " + tit, kTH1D, {axisProdx}); - histos.add("particle/mothers/prodVy", "Mothers Prod. Vertex Y " + tit, kTH1D, {axisPrody}); - histos.add("particle/mothers/prodVz", "Mothers Prod. Vertex Z " + tit, kTH1D, {axisProdz}); - histos.add("particle/mothers/prodRadiusVsPt", "Mothers Prod. Vertex Radius " + tit, kTH2D, {axisPt, axisProdRadius}); - histos.add("particle/mothers/prodRadius3DVsPt", "Mothers Prod. Vertex Radius XYZ " + tit, kTH2D, {axisPt, axisProdRadius}); + histos.add("particle/Pt", "Particle Pt " + titlePdgCode, kTH1D, {axisPt}); + histos.add("particle/P", "Particle P " + titlePdgCode, kTH1D, {axisP}); + histos.add("particle/primariesPt", "Particle Pt (primary) " + titlePdgCode, kTH1D, {axisPt}); + histos.add("particle/primariesP", "Particle P (primary) " + titlePdgCode, kTH1D, {axisP}); + histos.add("particle/secondariesPt", "Particle Pt (secondary) " + titlePdgCode, kTH1D, {axisPt}); + histos.add("particle/secondariesP", "Particle P (secondary) " + titlePdgCode, kTH1D, {axisP}); + histos.add("particle/prodVx", "Particle Prod. Vertex X " + titlePdgCode, kTH1D, {axisProdx}); + histos.add("particle/prodVy", "Particle Prod. Vertex Y " + titlePdgCode, kTH1D, {axisPrody}); + histos.add("particle/prodVz", "Particle Prod. Vertex Z " + titlePdgCode, kTH1D, {axisProdz}); + histos.add("particle/prodRadius", "Particle Prod. Vertex Radius XY " + titlePdgCode, kTH1D, {axisProdRadius}); + histos.add("particle/prodRadius3D", "Particle Prod. Vertex Radius XYZ " + titlePdgCode, kTH1D, {axisProdRadius}); + histos.add("particle/prodDistance", "Particle Prod. Vertex Distance XY " + titlePdgCode, kTH1D, {axisProdDistance}); + histos.add("particle/prodDistance3D", "Particle Prod. Vertex Distance XYZ " + titlePdgCode, kTH1D, {axisProdDistance}); + histos.add("particle/prodVxVy", "Particle Prod. Vertex X vs Vertex Y " + titlePdgCode, kTH2D, {axisProdx, axisPrody}); + histos.add("particle/prodVxVyPrm", "Particle Prod. Vertex X vs Vertex Y Primary " + titlePdgCode, kTH2D, {axisProdx, axisPrody}); + histos.add("particle/prodVxVyStr", "Particle Prod. Vertex X vs Vertex Y Decay " + titlePdgCode, kTH2D, {axisProdx, axisPrody}); + histos.add("particle/prodVxVyMat", "Particle Prod. Vertex X vs Vertex Y Material " + titlePdgCode, kTH2D, {axisProdx, axisPrody}); + histos.add("particle/prodVxVsPt", "Particle Prod. Vertex X " + titlePdgCode, kTH2D, {axisPt, axisProdx}); + histos.add("particle/prodVyVsPt", "Particle Prod. Vertex Y " + titlePdgCode, kTH2D, {axisPt, axisPrody}); + histos.add("particle/prodVzVsPt", "Particle Prod. Vertex Z " + titlePdgCode, kTH2D, {axisPt, axisProdz}); + histos.add("particle/prodRadiusVsPt", "Particle Prod. Vertex Radius XY " + titlePdgCode, kTH2D, {axisPt, axisProdRadius}); + histos.add("particle/prodRadiusVsEta", "Particle Prod. Vertex Radius XY " + titlePdgCode, kTH2D, {axisEta, axisProdRadius}); + histos.add("particle/prodRadius3DVsPt", "Particle Prod. Vertex Radius XYZ " + titlePdgCode, kTH2D, {axisPt, axisProdRadius}); + histos.add("particle/Eta", "Particle Eta " + titlePdgCode, kTH1D, {axisEta}); + histos.add("particle/primariesEta", "Particle Eta (primary) " + titlePdgCode, kTH1D, {axisEta}); + histos.add("particle/secondariesEta", "Particle Eta (secondary) " + titlePdgCode, kTH1D, {axisEta}); + histos.add("particle/Y", "Particle Y " + titlePdgCode, kTH1D, {axisY}); + histos.add("particle/primariesY", "Particle Y (primary)" + titlePdgCode, kTH1D, {axisY}); + histos.add("particle/secondariesY", "Particle Y (secondary)" + titlePdgCode, kTH1D, {axisY}); + histos.add("particle/EvsPz", "Particle E vs Pz " + titlePdgCode, kTH2D, {axisE, axisPz}); + histos.add("particle/YvzPz", "Particle Y vs Pz " + titlePdgCode, kTH2D, {axisY, axisPz}); + histos.add("particle/EtavzPz", "Particle Eta vs Pz " + titlePdgCode, kTH2D, {axisEta, axisPz}); + histos.add("particle/PtvzPz", "Particle Pt vs Pz " + titlePdgCode, kTH2D, {axisPt, axisPz}); + histos.add("particle/PvzPz", "Particle P vs Pz " + titlePdgCode, kTH2D, {axisP, axisPz}); + histos.add("particle/Px", "Particle Px " + titlePdgCode, kTH1D, {axisPx}); + histos.add("particle/Py", "Particle Py " + titlePdgCode, kTH1D, {axisPy}); + histos.add("particle/Pz", "Particle Pz " + titlePdgCode, kTH1D, {axisPz}); + histos.add("particle/decayDistanceX", "Particle decay distance " + titlePdgCode, kTH1D, {axisProdx}); + histos.add("particle/decayDistanceY", "Particle decay distance " + titlePdgCode, kTH1D, {axisPrody}); + histos.add("particle/decayDistanceZ", "Particle decay distance " + titlePdgCode, kTH1D, {axisProdz}); + histos.add("particle/decayDistanceVsPt", "Particle decay distance " + titlePdgCode, kTH2D, {axisPt, axisProdRadius}); + histos.add("particle/decayCtau", "Particle decay distance " + titlePdgCode, kTH1D, {{1000, 0, 10000, "c#tau (#mum)"}}); + + histos.add("particle/daughters/Number", "Number of Daughters " + titlePdgCode, kTH1D, {{20, -0.5, 19.5}}); + histos.add("particle/daughters/PDGs", "Daughters PDGs " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); + histos.add("particle/daughters/PDGsPrimaries", "Daughters PDGs Primaries of " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); + histos.add("particle/daughters/PDGsSecondaries", "Daughters PDGs Secondaries of " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); + histos.add("particle/daughters/prodVx", "Daughters Prod. Vertex X " + titlePdgCode, kTH1D, {axisProdx}); + histos.add("particle/daughters/prodVy", "Daughters Prod. Vertex Y " + titlePdgCode, kTH1D, {axisPrody}); + histos.add("particle/daughters/prodVz", "Daughters Prod. Vertex Z " + titlePdgCode, kTH1D, {axisProdz}); + histos.add("particle/daughters/prodRadiusVsPt", "Daughters Prod. Vertex Radius " + titlePdgCode, kTH2D, {axisPt, axisProdRadius}); + histos.add("particle/daughters/prodRadius3DVsPt", "Daughters Prod. Vertex Radius XYZ " + titlePdgCode, kTH2D, {axisPt, axisProdRadius}); + + histos.add("particle/mothers/Number", "Number of Mothers " + titlePdgCode, kTH1D, {{20, -0.5, 19.5}}); + histos.add("particle/mothers/PDGs", "Mothers PDGs " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); + histos.add("particle/mothers/PDGsPrimaries", "Mothers PDGs Primaries of " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); + histos.add("particle/mothers/PDGsSecondaries", "Mothers PDGs Secondaries of " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); + histos.add("particle/mothers/prodVx", "Mothers Prod. Vertex X " + titlePdgCode, kTH1D, {axisProdx}); + histos.add("particle/mothers/prodVy", "Mothers Prod. Vertex Y " + titlePdgCode, kTH1D, {axisPrody}); + histos.add("particle/mothers/prodVz", "Mothers Prod. Vertex Z " + titlePdgCode, kTH1D, {axisProdz}); + histos.add("particle/mothers/prodRadiusVsPt", "Mothers Prod. Vertex Radius " + titlePdgCode, kTH2D, {axisPt, axisProdRadius}); + histos.add("particle/mothers/prodRadius3DVsPt", "Mothers Prod. Vertex Radius XYZ " + titlePdgCode, kTH2D, {axisPt, axisProdRadius}); // Go up one generation - histos.add("particle/mothers/mothers/Number", "Number of Mothers mothers " + tit, kTH1D, {{20, -0.5, 19.5}}); - histos.add("particle/mothers/mothers/PDGs", "Mothers mothers PDGs " + tit, kTH2D, {axisPDGs, axisCharge}); - histos.add("particle/mothers/mothers/PDGsPrimaries", "Mothers mothers PDGs Primaries of " + tit, kTH2D, {axisPDGs, axisCharge}); - histos.add("particle/mothers/mothers/PDGsSecondaries", "Mothers mothers PDGs Secondaries of " + tit, kTH2D, {axisPDGs, axisCharge}); - histos.add("particle/mothers/mothers/prodVx", "Mothers mothers Prod. Vertex X " + tit, kTH1D, {axisProdx}); - histos.add("particle/mothers/mothers/prodVy", "Mothers mothers Prod. Vertex Y " + tit, kTH1D, {axisPrody}); - histos.add("particle/mothers/mothers/prodVz", "Mothers mothers Prod. Vertex Z " + tit, kTH1D, {axisProdz}); - histos.add("particle/mothers/mothers/prodRadiusVsPt", "Mothers mothers Prod. Vertex Radius " + tit, kTH2D, {axisPt, axisProdRadius}); - histos.add("particle/mothers/mothers/prodRadius3DVsPt", "Mothers mothers Prod. Vertex Radius XYZ " + tit, kTH2D, {axisPt, axisProdRadius}); + histos.add("particle/mothers/mothers/Number", "Number of Mothers mothers " + titlePdgCode, kTH1D, {{20, -0.5, 19.5}}); + histos.add("particle/mothers/mothers/PDGs", "Mothers mothers PDGs " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); + histos.add("particle/mothers/mothers/PDGsPrimaries", "Mothers mothers PDGs Primaries of " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); + histos.add("particle/mothers/mothers/PDGsSecondaries", "Mothers mothers PDGs Secondaries of " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); + histos.add("particle/mothers/mothers/prodVx", "Mothers mothers Prod. Vertex X " + titlePdgCode, kTH1D, {axisProdx}); + histos.add("particle/mothers/mothers/prodVy", "Mothers mothers Prod. Vertex Y " + titlePdgCode, kTH1D, {axisPrody}); + histos.add("particle/mothers/mothers/prodVz", "Mothers mothers Prod. Vertex Z " + titlePdgCode, kTH1D, {axisProdz}); + histos.add("particle/mothers/mothers/prodRadiusVsPt", "Mothers mothers Prod. Vertex Radius " + titlePdgCode, kTH2D, {axisPt, axisProdRadius}); + histos.add("particle/mothers/mothers/prodRadius3DVsPt", "Mothers mothers Prod. Vertex Radius XYZ " + titlePdgCode, kTH2D, {axisPt, axisProdRadius}); if (doprocessParticleOnly) { return; @@ -203,15 +257,15 @@ struct Alice3SingleParticle { histos.add("track/PDGs", "Track PDGs", kTH2D, {axisPDGs, axisCharge}); histos.add("track/withoutParticle", "Tracks without particles", kTH1D, {axisPt}); histos.add("track/tofPDGs", "Track wTOF PDGs", kTH2D, {axisPDGs, axisCharge}); - histos.add("track/Pt", "Track Pt " + tit, kTH1D, {axisPt}); - histos.add("track/primariesPt", "Track Pt (primary) " + tit, kTH1D, {axisPt}); - histos.add("track/secondariesPt", "Track Pt (secondary) " + tit, kTH1D, {axisPt}); - histos.add("track/PtvsMCPt", "Track Pt vs MC Pt " + tit, kTH2D, {axisPt, axisPtMC}); - histos.add("track/Eta", "Track Eta " + tit, kTH1D, {axisEta}); - histos.add("track/primariesEta", "Track Eta (primary) " + tit, kTH1D, {axisEta}); - histos.add("track/secondariesEta", "Track Eta (secondary) " + tit, kTH1D, {axisEta}); - histos.add("track/primaries", "Source for primaries " + tit, kTH2D, {axisPDGs, axisCharge}); - histos.add("track/secondaries", "Source for secondaries " + tit, kTH2D, {axisPDGs, axisCharge}); + histos.add("track/Pt", "Track Pt " + titlePdgCode, kTH1D, {axisPt}); + histos.add("track/primariesPt", "Track Pt (primary) " + titlePdgCode, kTH1D, {axisPt}); + histos.add("track/secondariesPt", "Track Pt (secondary) " + titlePdgCode, kTH1D, {axisPt}); + histos.add("track/PtvsMCPt", "Track Pt vs MC Pt " + titlePdgCode, kTH2D, {axisPt, axisPt}); + histos.add("track/Eta", "Track Eta " + titlePdgCode, kTH1D, {axisEta}); + histos.add("track/primariesEta", "Track Eta (primary) " + titlePdgCode, kTH1D, {axisEta}); + histos.add("track/secondariesEta", "Track Eta (secondary) " + titlePdgCode, kTH1D, {axisEta}); + histos.add("track/primaries", "Source for primaries " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); + histos.add("track/secondaries", "Source for secondaries " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); } template @@ -230,236 +284,101 @@ struct Alice3SingleParticle { return Form("%i", particle.pdgCode()); } - void processStandard(const o2::aod::McCollisions& colls, - const soa::Join& tracks, - const aod::McParticles& mcParticles) + // Nch estimator: charged physical primaries in |eta| < settings.nchEtaMax + template + bool isNchParticle(ParticleType const& particle) { - for (const auto& col : colls) { - histos.fill(HIST("event/VtxX"), col.posX()); - histos.fill(HIST("event/VtxY"), col.posY()); - histos.fill(HIST("event/VtxZ"), col.posZ()); + if (!particle.isPhysicalPrimary() || std::abs(particle.eta()) >= settings.nchEtaMax) { + return false; } - std::vector ParticlesOfInterest; - for (const auto& mcParticle : mcParticles) { - const auto& pdgString = getPdgCodeString(mcParticle); - const auto& pdgCharge = getCharge(mcParticle); - histos.get(HIST("particle/PDGs"))->Fill(pdgString, pdgCharge, 1.f); - if (mcParticle.isPhysicalPrimary()) { - histos.get(HIST("particle/PDGsPrimaries"))->Fill(pdgString, pdgCharge, 1.f); - } else { - histos.get(HIST("particle/PDGsSecondaries"))->Fill(pdgString, pdgCharge, 1.f); - } - if (PDG != 0 && mcParticle.pdgCode() != PDG) { - continue; - } - if (mcParticle.y() < yMin || mcParticle.y() > yMax) { - continue; - } - if (mcParticle.eta() < etaMin || mcParticle.eta() > etaMax) { - continue; - } - if (mcParticle.pt() < ptMin || mcParticle.pt() > ptMax) { - continue; - } - if (mcParticle.vz() < prodMinZ || mcParticle.vz() > prodMaxZ) { - continue; - } - histos.fill(HIST("particle/producedProcess"), mcParticle.getProcess()); - histos.fill(HIST("particle/Pt"), mcParticle.pt()); - histos.fill(HIST("particle/P"), mcParticle.p()); - histos.fill(HIST("particle/Eta"), mcParticle.eta()); - if (mcParticle.isPhysicalPrimary()) { - histos.fill(HIST("particle/primariesPt"), mcParticle.pt()); - histos.fill(HIST("particle/primariesP"), mcParticle.p()); - histos.fill(HIST("particle/primariesEta"), mcParticle.eta()); - histos.fill(HIST("particle/primariesY"), mcParticle.y()); - } else { - histos.fill(HIST("particle/secondariesPt"), mcParticle.pt()); - histos.fill(HIST("particle/secondariesP"), mcParticle.p()); - histos.fill(HIST("particle/secondariesEta"), mcParticle.eta()); - histos.fill(HIST("particle/secondariesY"), mcParticle.y()); - } - histos.fill(HIST("particle/EvsPz"), mcParticle.e(), mcParticle.pz()); - histos.fill(HIST("particle/Y"), mcParticle.y()); - histos.fill(HIST("particle/YvzPz"), mcParticle.y(), mcParticle.pz()); - histos.fill(HIST("particle/EtavzPz"), mcParticle.eta(), mcParticle.pz()); - histos.fill(HIST("particle/PvzPz"), mcParticle.p(), mcParticle.pz()); - histos.fill(HIST("particle/PtvzPz"), mcParticle.pt(), mcParticle.pz()); - histos.fill(HIST("particle/Px"), mcParticle.px()); - histos.fill(HIST("particle/Py"), mcParticle.py()); - histos.fill(HIST("particle/Pz"), mcParticle.pz()); - histos.fill(HIST("particle/prodVx"), mcParticle.vx()); - histos.fill(HIST("particle/prodVy"), mcParticle.vy()); - histos.fill(HIST("particle/prodVz"), mcParticle.vz()); - if (mcParticle.has_daughters()) { - const auto& daughters = mcParticle.daughters_as(); - histos.fill(HIST("particle/daughters/Number"), daughters.size()); - bool filledDistance = false; - for (const auto& daughter : daughters) { - const auto& pdgStringDau = getPdgCodeString(daughter); - const auto& pdgChargeDau = getCharge(daughter); - - histos.get(HIST("particle/daughters/PDGs"))->Fill(pdgStringDau, pdgChargeDau, 1.f); - if (mcParticle.isPhysicalPrimary()) { - histos.get(HIST("particle/daughters/PDGsPrimaries"))->Fill(pdgStringDau, pdgChargeDau, 1.f); - } else { - histos.get(HIST("particle/daughters/PDGsSecondaries"))->Fill(pdgStringDau, pdgChargeDau, 1.f); - } - - histos.fill(HIST("particle/daughters/prodVx"), daughter.vx()); - histos.fill(HIST("particle/daughters/prodVy"), daughter.vy()); - histos.fill(HIST("particle/daughters/prodVz"), daughter.vz()); - if (filledDistance) { - continue; - } - filledDistance = true; - histos.fill(HIST("particle/daughters/prodRadiusVsPt"), mcParticle.pt(), std::sqrt(daughter.vx() * daughter.vx() + daughter.vy() * daughter.vy())); - histos.fill(HIST("particle/daughters/prodRadius3DVsPt"), mcParticle.pt(), std::sqrt(daughter.vx() * daughter.vx() + daughter.vy() * daughter.vy() + daughter.vz() * daughter.vz())); - - const float dx = daughter.vx() - mcParticle.vx(); - const float dy = daughter.vy() - mcParticle.vy(); - const float dz = daughter.vz() - mcParticle.vz(); - histos.fill(HIST("particle/decayDistanceX"), dx); - histos.fill(HIST("particle/decayDistanceY"), dy); - histos.fill(HIST("particle/decayDistanceZ"), dz); - histos.fill(HIST("particle/decayDistanceVsPt"), mcParticle.pt(), std::sqrt((dx) * (dx) + (dy) * (dy) + (dz) * (dz))); - if (pdg->GetParticle(mcParticle.pdgCode())) { - const float mass = pdg->GetParticle(mcParticle.pdgCode())->Mass(); - histos.fill(HIST("particle/decayCtau"), std::sqrt((dx) * (dx) + (dy) * (dy) + (dz) * (dz)) / mcParticle.pt() * mass * 10 * 1000); - } - } - } else { - histos.fill(HIST("particle/daughters/Number"), 0.f); - } - if (mcParticle.has_mothers()) { - const auto& mothers = mcParticle.mothers_as(); - for (const auto& mother : mothers) { - const auto& pdgStringMot = getPdgCodeString(mother); - const auto& pdgChargeMot = getCharge(mother); - - histos.get(HIST("particle/mothers/PDGs"))->Fill(pdgStringMot, pdgChargeMot, 1.f); - if (mcParticle.isPhysicalPrimary()) { - histos.get(HIST("particle/mothers/PDGsPrimaries"))->Fill(pdgStringMot, pdgChargeMot, 1.f); - } else { - histos.get(HIST("particle/mothers/PDGsSecondaries"))->Fill(pdgStringMot, pdgChargeMot, 1.f); - } - - histos.fill(HIST("particle/mothers/prodVx"), mother.vx()); - histos.fill(HIST("particle/mothers/prodVy"), mother.vy()); - histos.fill(HIST("particle/mothers/prodVz"), mother.vz()); - histos.fill(HIST("particle/mothers/prodRadiusVsPt"), mother.pt(), std::sqrt(mother.vx() * mother.vx() + mother.vy() * mother.vy())); - histos.fill(HIST("particle/mothers/prodRadius3DVsPt"), mother.pt(), std::sqrt(mother.vx() * mother.vx() + mother.vy() * mother.vy() + mother.vz() * mother.vz())); - if (mother.has_mothers()) { - const auto& mothers2 = mother.mothers_as(); - for (const auto& mother2 : mothers2) { - const auto& pdgStringMot2 = getPdgCodeString(mother2); - const auto& pdgChargeMot2 = getCharge(mother2); - - histos.get(HIST("particle/mothers/mothers/PDGs"))->Fill(pdgStringMot2, pdgChargeMot2, 1.f); - if (mcParticle.isPhysicalPrimary()) { - histos.get(HIST("particle/mothers/mothers/PDGsPrimaries"))->Fill(pdgStringMot2, pdgChargeMot2, 1.f); - } else { - histos.get(HIST("particle/mothers/mothers/PDGsSecondaries"))->Fill(pdgStringMot2, pdgChargeMot2, 1.f); - } - - histos.fill(HIST("particle/mothers/mothers/prodVx"), mother2.vx()); - histos.fill(HIST("particle/mothers/mothers/prodVy"), mother2.vy()); - histos.fill(HIST("particle/mothers/mothers/prodVz"), mother2.vz()); - histos.fill(HIST("particle/mothers/mothers/prodRadiusVsPt"), mother2.pt(), std::sqrt(mother2.vx() * mother2.vx() + mother2.vy() * mother2.vy())); - histos.fill(HIST("particle/mothers/mothers/prodRadius3DVsPt"), mother2.pt(), std::sqrt(mother2.vx() * mother2.vx() + mother2.vy() * mother2.vy() + mother2.vz() * mother2.vz())); - } - } - } - } + const auto* pdgParticle = pdg->GetParticle(particle.pdgCode()); + return pdgParticle && pdgParticle->Charge() != 0; + } - histos.fill(HIST("particle/prodRadius"), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy())); - histos.fill(HIST("particle/prodVxVy"), mcParticle.vx(), mcParticle.vy()); - if (!mcParticle.isPhysicalPrimary()) { - if (mcParticle.getProcess() == 4) { - histos.fill(HIST("particle/prodVxVyStr"), mcParticle.vx(), mcParticle.vy()); - } else { - histos.fill(HIST("particle/prodVxVyMat"), mcParticle.vx(), mcParticle.vy()); - } - } else { - histos.fill(HIST("particle/prodVxVyPrm"), mcParticle.vx(), mcParticle.vy()); - } - histos.fill(HIST("particle/prodVxVsPt"), mcParticle.pt(), mcParticle.vx()); - histos.fill(HIST("particle/prodVyVsPt"), mcParticle.pt(), mcParticle.vy()); - histos.fill(HIST("particle/prodVzVsPt"), mcParticle.pt(), mcParticle.vz()); - histos.fill(HIST("particle/prodRadiusVsPt"), mcParticle.pt(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy())); - histos.fill(HIST("particle/prodRadiusVsEta"), mcParticle.eta(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy())); - histos.fill(HIST("particle/prodRadius3DVsPt"), mcParticle.pt(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy() + mcParticle.vz() * mcParticle.vz())); - ParticlesOfInterest.push_back(mcParticle.globalIndex()); + void fillSignalMultiplicity(const std::vector& nchPerCollision, const std::vector& nSignalPerCollision) + { + for (std::size_t iCollision = 0; iCollision < nchPerCollision.size(); ++iCollision) { + histos.fill(HIST("event/Nch"), nchPerCollision[iCollision]); + histos.fill(HIST("particle/NSignalVsNch"), nchPerCollision[iCollision], nSignalPerCollision[iCollision]); } - + } + // Track histograms. Must be called after genericProcessParticles, which fills particlesOfInterest. + template + void genericProcessTracks(const TTracks& tracks) + { for (const auto& track : tracks) { if (!track.has_mcParticle()) { histos.fill(HIST("track/withoutParticle"), track.pt()); continue; } - const auto mcParticle = track.mcParticle_as(); + const auto mcParticle = track.template mcParticle_as(); histos.get(HIST("track/PDGs"))->Fill(getPdgCodeString(mcParticle), getCharge(mcParticle), 1.f); if (track.hasTOF()) { histos.get(HIST("track/tofPDGs"))->Fill(getPdgCodeString(mcParticle), getCharge(mcParticle), 1.f); } - if (IsStable.value == 0) { - LOG(info) << mcParticle.pdgCode() << " asked for " << PDG.value; + if (settings.isStable.value == 0) { + if (settings.doPrint) { + LOG(info) << mcParticle.pdgCode() << " asked for " << pdgCode.value; + } if (!mcParticle.has_mothers()) { continue; } // auto mothers = mcParticle.mothers(); - auto mothers = mcParticle.mothers_as(); - const auto ParticleIsInteresting = std::find(ParticlesOfInterest.begin(), ParticlesOfInterest.end(), mothers[0].globalIndex()) != ParticlesOfInterest.end(); - if (!ParticleIsInteresting) { + auto mothers = mcParticle.template mothers_as(); + const auto particleIsInteresting = std::find(particlesOfInterest.begin(), particlesOfInterest.end(), mothers[0].globalIndex()) != particlesOfInterest.end(); + if (!particleIsInteresting) { continue; } - if (doPrint) { + if (settings.doPrint) { LOG(info) << "Track " << track.globalIndex() << " comes from a " << mothers[0].pdgCode() << " and is a " << mcParticle.pdgCode(); } } else { - if (PDG != 0 && mcParticle.pdgCode() != PDG) { + if (pdgCode != 0 && mcParticle.pdgCode() != pdgCode) { continue; } - histos.fill(HIST("track/Pt"), track.pt() * charge); - histos.fill(HIST("track/PtvsMCPt"), track.pt() * charge, mcParticle.pt()); + histos.fill(HIST("track/Pt"), track.pt() * settings.charge); + histos.fill(HIST("track/PtvsMCPt"), track.pt() * settings.charge, mcParticle.pt()); histos.fill(HIST("track/Eta"), track.eta()); if (mcParticle.isPhysicalPrimary()) { - histos.fill(HIST("track/primariesPt"), track.pt() * charge); + histos.fill(HIST("track/primariesPt"), track.pt() * settings.charge); histos.fill(HIST("track/primariesEta"), track.eta()); } else { - histos.fill(HIST("track/secondariesPt"), track.pt() * charge); + histos.fill(HIST("track/secondariesPt"), track.pt() * settings.charge); histos.fill(HIST("track/secondariesEta"), track.eta()); } if (!mcParticle.has_mothers()) { - if (doPrint) { + if (settings.doPrint) { LOG(info) << "Track " << track.globalIndex() << " is a " << mcParticle.pdgCode(); } continue; } // auto mothers = mcParticle.mothers(); - auto mothers = mcParticle.mothers_as(); + auto mothers = mcParticle.template mothers_as(); if (mcParticle.isPhysicalPrimary()) { histos.get(HIST("track/primaries"))->Fill(getPdgCodeString(mothers[0]), getCharge(mothers[0]), 1.f); } else { histos.get(HIST("track/secondaries"))->Fill(getPdgCodeString(mothers[0]), getCharge(mothers[0]), 1.f); } - if (doPrint) { + if (settings.doPrint) { LOG(info) << "Track " << track.globalIndex() << " is a " << mcParticle.pdgCode() << " and comes from a " << mothers[0].pdgCode() << " and is " << (mcParticle.isPhysicalPrimary() ? "" : "not") << " a primary"; } } } } - PROCESS_SWITCH(Alice3SingleParticle, processStandard, "Process IU tracks", true); - void processParticleOnly(const o2::aod::McCollisions& colls, - const aod::McParticles& mcParticles) + // Event and MC particle histograms. Stores the selected MC particles in particlesOfInterest. + void genericProcessParticles(const o2::aod::McCollisions& colls, + const aod::McParticles& mcParticles) { for (const auto& col : colls) { histos.fill(HIST("event/VtxX"), col.posX()); histos.fill(HIST("event/VtxY"), col.posY()); histos.fill(HIST("event/VtxZ"), col.posZ()); } + std::vector nchPerCollision(colls.size(), 0); + std::vector nSignalPerCollision(colls.size(), 0); + particlesOfInterest.clear(); for (const auto& mcParticle : mcParticles) { const auto& pdgString = getPdgCodeString(mcParticle); const auto& pdgCharge = getCharge(mcParticle); @@ -469,18 +388,25 @@ struct Alice3SingleParticle { } else { histos.get(HIST("particle/PDGsSecondaries"))->Fill(pdgString, pdgCharge, 1.f); } - if (PDG != 0 && mcParticle.pdgCode() != PDG) { + if (isNchParticle(mcParticle)) { + ++nchPerCollision.at(mcParticle.mcCollisionId()); + } + if (pdgCode != 0 && mcParticle.pdgCode() != pdgCode) { continue; } - if (mcParticle.y() < yMin || mcParticle.y() > yMax) { + if (!yRange.contains(mcParticle.y())) { continue; } - if (mcParticle.eta() < etaMin || mcParticle.eta() > etaMax) { + if (!etaRange.contains(mcParticle.eta())) { continue; } - if (mcParticle.vz() < prodMinZ || mcParticle.vz() > prodMaxZ) { + if (!ptRange.contains(mcParticle.pt())) { continue; } + if (!prodZRange.contains(mcParticle.vz())) { + continue; + } + ++nSignalPerCollision.at(mcParticle.mcCollisionId()); histos.fill(HIST("particle/producedProcess"), mcParticle.getProcess()); histos.fill(HIST("particle/Pt"), mcParticle.pt()); histos.fill(HIST("particle/P"), mcParticle.p()); @@ -549,7 +475,7 @@ struct Alice3SingleParticle { histos.fill(HIST("particle/daughters/Number"), 0.f); } if (mcParticle.has_mothers()) { - auto mothers = mcParticle.mothers_as(); + const auto& mothers = mcParticle.mothers_as(); for (const auto& mother : mothers) { const auto& pdgStringMot = getPdgCodeString(mother); const auto& pdgChargeMot = getCharge(mother); @@ -566,13 +492,33 @@ struct Alice3SingleParticle { histos.fill(HIST("particle/mothers/prodVz"), mother.vz()); histos.fill(HIST("particle/mothers/prodRadiusVsPt"), mother.pt(), std::sqrt(mother.vx() * mother.vx() + mother.vy() * mother.vy())); histos.fill(HIST("particle/mothers/prodRadius3DVsPt"), mother.pt(), std::sqrt(mother.vx() * mother.vx() + mother.vy() * mother.vy() + mother.vz() * mother.vz())); + if (mother.has_mothers()) { + const auto& mothers2 = mother.mothers_as(); + for (const auto& mother2 : mothers2) { + const auto& pdgStringMot2 = getPdgCodeString(mother2); + const auto& pdgChargeMot2 = getCharge(mother2); + + histos.get(HIST("particle/mothers/mothers/PDGs"))->Fill(pdgStringMot2, pdgChargeMot2, 1.f); + if (mcParticle.isPhysicalPrimary()) { + histos.get(HIST("particle/mothers/mothers/PDGsPrimaries"))->Fill(pdgStringMot2, pdgChargeMot2, 1.f); + } else { + histos.get(HIST("particle/mothers/mothers/PDGsSecondaries"))->Fill(pdgStringMot2, pdgChargeMot2, 1.f); + } + + histos.fill(HIST("particle/mothers/mothers/prodVx"), mother2.vx()); + histos.fill(HIST("particle/mothers/mothers/prodVy"), mother2.vy()); + histos.fill(HIST("particle/mothers/mothers/prodVz"), mother2.vz()); + histos.fill(HIST("particle/mothers/mothers/prodRadiusVsPt"), mother2.pt(), std::sqrt(mother2.vx() * mother2.vx() + mother2.vy() * mother2.vy())); + histos.fill(HIST("particle/mothers/mothers/prodRadius3DVsPt"), mother2.pt(), std::sqrt(mother2.vx() * mother2.vx() + mother2.vy() * mother2.vy() + mother2.vz() * mother2.vz())); + } + } } } histos.fill(HIST("particle/prodRadius"), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy())); histos.fill(HIST("particle/prodVxVy"), mcParticle.vx(), mcParticle.vy()); if (!mcParticle.isPhysicalPrimary()) { - if (mcParticle.getProcess() == 4) { + if (mcParticle.getProcess() == TMCProcess::kPDecay) { histos.fill(HIST("particle/prodVxVyStr"), mcParticle.vx(), mcParticle.vy()); } else { histos.fill(HIST("particle/prodVxVyMat"), mcParticle.vx(), mcParticle.vy()); @@ -586,187 +532,35 @@ struct Alice3SingleParticle { histos.fill(HIST("particle/prodRadiusVsPt"), mcParticle.pt(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy())); histos.fill(HIST("particle/prodRadiusVsEta"), mcParticle.eta(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy())); histos.fill(HIST("particle/prodRadius3DVsPt"), mcParticle.pt(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy() + mcParticle.vz() * mcParticle.vz())); + particlesOfInterest.push_back(mcParticle.globalIndex()); } + fillSignalMultiplicity(nchPerCollision, nSignalPerCollision); + } + + void processStandard(const o2::aod::McCollisions& colls, + const soa::Join& tracks, + const aod::McParticles& mcParticles) + { + genericProcessParticles(colls, mcParticles); + genericProcessTracks(tracks); + } + + void processParticleOnly(const o2::aod::McCollisions& colls, + const aod::McParticles& mcParticles) + { + genericProcessParticles(colls, mcParticles); } - PROCESS_SWITCH(Alice3SingleParticle, processParticleOnly, "Process Particle only", false); void processNonIU(const o2::aod::McCollisions& colls, const soa::Join& tracks, const aod::McParticles& mcParticles) { - for (const auto& col : colls) { - histos.fill(HIST("event/VtxX"), col.posX()); - histos.fill(HIST("event/VtxY"), col.posY()); - histos.fill(HIST("event/VtxZ"), col.posZ()); - } - std::vector ParticlesOfInterest; - for (const auto& mcParticle : mcParticles) { - const auto& pdgString = getPdgCodeString(mcParticle); - const auto& pdgCharge = getCharge(mcParticle); - histos.get(HIST("particle/PDGs"))->Fill(pdgString, pdgCharge, 1.f); - if (mcParticle.isPhysicalPrimary()) { - histos.get(HIST("particle/PDGsPrimaries"))->Fill(pdgString, pdgCharge, 1.f); - } else { - histos.get(HIST("particle/PDGsSecondaries"))->Fill(pdgString, pdgCharge, 1.f); - } - if (PDG != 0 && mcParticle.pdgCode() != PDG) { - continue; - } - if (mcParticle.y() < yMin || mcParticle.y() > yMax) { - continue; - } - if (mcParticle.eta() < etaMin || mcParticle.eta() > etaMax) { - continue; - } - if (mcParticle.pt() < ptMin || mcParticle.pt() > ptMax) { - continue; - } - histos.fill(HIST("particle/Pt"), mcParticle.pt()); - histos.fill(HIST("particle/P"), mcParticle.p()); - histos.fill(HIST("particle/Eta"), mcParticle.eta()); - if (mcParticle.isPhysicalPrimary()) { - histos.fill(HIST("particle/primariesPt"), mcParticle.pt()); - histos.fill(HIST("particle/primariesP"), mcParticle.p()); - histos.fill(HIST("particle/primariesEta"), mcParticle.eta()); - histos.fill(HIST("particle/primariesY"), mcParticle.y()); - } else { - histos.fill(HIST("particle/secondariesPt"), mcParticle.pt()); - histos.fill(HIST("particle/secondariesP"), mcParticle.p()); - histos.fill(HIST("particle/secondariesEta"), mcParticle.eta()); - histos.fill(HIST("particle/secondariesY"), mcParticle.y()); - } - histos.fill(HIST("particle/EvsPz"), mcParticle.e(), mcParticle.pz()); - histos.fill(HIST("particle/Y"), mcParticle.y()); - histos.fill(HIST("particle/YvzPz"), mcParticle.y(), mcParticle.pz()); - histos.fill(HIST("particle/EtavzPz"), mcParticle.eta(), mcParticle.pz()); - histos.fill(HIST("particle/PvzPz"), mcParticle.p(), mcParticle.pz()); - histos.fill(HIST("particle/PtvzPz"), mcParticle.pt(), mcParticle.pz()); - histos.fill(HIST("particle/Px"), mcParticle.px()); - histos.fill(HIST("particle/Py"), mcParticle.py()); - histos.fill(HIST("particle/Pz"), mcParticle.pz()); - histos.fill(HIST("particle/prodVx"), mcParticle.vx()); - histos.fill(HIST("particle/prodVy"), mcParticle.vy()); - histos.fill(HIST("particle/prodVz"), mcParticle.vz()); - if (mcParticle.has_daughters()) { - auto daughters = mcParticle.daughters_as(); - histos.fill(HIST("particle/daughters/Number"), daughters.size()); - for (const auto& daughter : daughters) { - const auto& pdgStringDau = getPdgCodeString(daughter); - const auto& pdgChargeDau = getCharge(daughter); - - histos.get(HIST("particle/daughters/PDGs"))->Fill(pdgStringDau, pdgChargeDau, 1.f); - if (mcParticle.isPhysicalPrimary()) { - histos.get(HIST("particle/daughters/PDGsPrimaries"))->Fill(pdgStringDau, pdgChargeDau, 1.f); - } else { - histos.get(HIST("particle/daughters/PDGsSecondaries"))->Fill(pdgStringDau, pdgChargeDau, 1.f); - } - - histos.fill(HIST("particle/daughters/prodVx"), daughter.vx()); - histos.fill(HIST("particle/daughters/prodVy"), daughter.vy()); - histos.fill(HIST("particle/daughters/prodVz"), daughter.vz()); - histos.fill(HIST("particle/daughters/prodRadiusVsPt"), mcParticle.pt(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy())); - histos.fill(HIST("particle/daughters/prodRadius3DVsPt"), mcParticle.pt(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy() + mcParticle.vz() * mcParticle.vz())); - } - } else { - histos.fill(HIST("particle/daughters/Number"), 0.f); - } - if (mcParticle.has_mothers()) { - auto mothers = mcParticle.mothers_as(); - for (const auto& mother : mothers) { - const auto& pdgStringMot = getPdgCodeString(mother); - const auto& pdgChargeMot = getCharge(mother); - - histos.get(HIST("particle/mothers/PDGs"))->Fill(pdgStringMot, pdgChargeMot, 1.f); - if (mcParticle.isPhysicalPrimary()) { - histos.get(HIST("particle/mothers/PDGsPrimaries"))->Fill(pdgStringMot, pdgChargeMot, 1.f); - } else { - histos.get(HIST("particle/mothers/PDGsSecondaries"))->Fill(pdgStringMot, pdgChargeMot, 1.f); - } - - histos.fill(HIST("particle/mothers/prodVx"), mother.vx()); - histos.fill(HIST("particle/mothers/prodVy"), mother.vy()); - histos.fill(HIST("particle/mothers/prodVz"), mother.vz()); - histos.fill(HIST("particle/mothers/prodRadiusVsPt"), mother.pt(), std::sqrt(mother.vx() * mother.vx() + mother.vy() * mother.vy())); - histos.fill(HIST("particle/mothers/prodRadius3DVsPt"), mother.pt(), std::sqrt(mother.vx() * mother.vx() + mother.vy() * mother.vy() + mother.vz() * mother.vz())); - } - } - - histos.fill(HIST("particle/prodRadius"), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy())); - histos.fill(HIST("particle/prodVxVy"), mcParticle.vx(), mcParticle.vy()); - if (!mcParticle.isPhysicalPrimary()) { - if (mcParticle.getProcess() == 4) { - histos.fill(HIST("particle/prodVxVyStr"), mcParticle.vx(), mcParticle.vy()); - } else { - histos.fill(HIST("particle/prodVxVyMat"), mcParticle.vx(), mcParticle.vy()); - } - } else { - histos.fill(HIST("particle/prodVxVyPrm"), mcParticle.vx(), mcParticle.vy()); - } - histos.fill(HIST("particle/prodVxVsPt"), mcParticle.pt(), mcParticle.vx()); - histos.fill(HIST("particle/prodVyVsPt"), mcParticle.pt(), mcParticle.vy()); - histos.fill(HIST("particle/prodVzVsPt"), mcParticle.pt(), mcParticle.vz()); - histos.fill(HIST("particle/prodRadiusVsPt"), mcParticle.pt(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy())); - histos.fill(HIST("particle/prodRadiusVsEta"), mcParticle.eta(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy())); - histos.fill(HIST("particle/prodRadius3DVsPt"), mcParticle.pt(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy() + mcParticle.vz() * mcParticle.vz())); - ParticlesOfInterest.push_back(mcParticle.globalIndex()); - } - - for (const auto& track : tracks) { - if (!track.has_mcParticle()) { - histos.fill(HIST("track/withoutParticle"), track.pt()); - continue; - } - const auto mcParticle = track.mcParticle_as(); - histos.get(HIST("track/PDGs"))->Fill(getPdgCodeString(mcParticle), getCharge(mcParticle), 1.f); - if (track.hasTOF()) { - histos.get(HIST("track/tofPDGs"))->Fill(getPdgCodeString(mcParticle), getCharge(mcParticle), 1.f); - } - if (IsStable.value == 0) { - if (!mcParticle.has_mothers()) { - continue; - } - const auto& mothers = mcParticle.mothers_as(); - const auto& ParticleIsInteresting = std::find(ParticlesOfInterest.begin(), ParticlesOfInterest.end(), mothers[0].globalIndex()) != ParticlesOfInterest.end(); - if (!ParticleIsInteresting) { - continue; - } - if (doPrint) { - LOG(info) << "Track " << track.globalIndex() << " comes from a " << mothers[0].pdgCode() << " and is a " << mcParticle.pdgCode(); - } - } else { - if (PDG != 0 && mcParticle.pdgCode() != PDG) { - continue; - } - histos.fill(HIST("track/Pt"), track.pt() * charge); - histos.fill(HIST("track/PtvsMCPt"), track.pt() * charge, mcParticle.pt()); - histos.fill(HIST("track/Eta"), track.eta()); - if (mcParticle.isPhysicalPrimary()) { - histos.fill(HIST("track/primariesPt"), track.pt() * charge); - histos.fill(HIST("track/primariesEta"), track.eta()); - } else { - histos.fill(HIST("track/secondariesPt"), track.pt() * charge); - histos.fill(HIST("track/secondariesEta"), track.eta()); - } - - if (!mcParticle.has_mothers()) { - if (doPrint) { - LOG(info) << "Track " << track.globalIndex() << " is a " << mcParticle.pdgCode(); - } - continue; - } - // auto mothers = mcParticle.mothers(); - auto mothers = mcParticle.mothers_as(); - if (mcParticle.isPhysicalPrimary()) { - histos.get(HIST("track/primaries"))->Fill(getPdgCodeString(mothers[0]), getCharge(mothers[0]), 1.f); - } else { - histos.get(HIST("track/secondaries"))->Fill(getPdgCodeString(mothers[0]), getCharge(mothers[0]), 1.f); - } - if (doPrint) { - LOG(info) << "Track " << track.globalIndex() << " is a " << mcParticle.pdgCode() << " and comes from a " << mothers[0].pdgCode() << " and is " << (mcParticle.isPhysicalPrimary() ? "" : "not") << " a primary"; - } - } - } + genericProcessParticles(colls, mcParticles); + genericProcessTracks(tracks); } + + PROCESS_SWITCH(Alice3SingleParticle, processStandard, "Process IU tracks", false); + PROCESS_SWITCH(Alice3SingleParticle, processParticleOnly, "Process Particle only", true); PROCESS_SWITCH(Alice3SingleParticle, processNonIU, "Process non IU tracks", false); }; diff --git a/ALICE3/Tasks/alice3DqEfficiency.cxx b/ALICE3/Tasks/alice3DqEfficiency.cxx index 59757563955..d90a6f0b320 100644 --- a/ALICE3/Tasks/alice3DqEfficiency.cxx +++ b/ALICE3/Tasks/alice3DqEfficiency.cxx @@ -775,7 +775,7 @@ struct Alice3DqEfficiencyAnalysisSameEventPairing { std::vector fGenMCSignals; std::vector fPairCuts; - AnalysisCompositeCut fMCGenAccCut; + AnalysisCompositeCut fMCGenAccCut{}; bool fUseMCGenAccCut = false; uint32_t fTrackFilterMask = 0; // mask for the track cuts required in this task to be applied on the barrel cuts produced upstream @@ -983,7 +983,7 @@ struct Alice3DqEfficiencyAnalysisSameEventPairing { fHistMan->SetUseDefaultVariableNames(true); fHistMan->SetDefaultVarNames(dqefficiency_helpers::varNames(), dqefficiency_helpers::varUnits()); - VarManager::SetCollisionSystem((TString)fConfigOptions.syst, fConfigOptions.energy); // set collision system and center of mass energy + VarManager::SetCollisionSystem(TString(fConfigOptions.syst), fConfigOptions.energy); // set collision system and center of mass energy defineHistograms(fHistMan, histNames.Data(), cfgAddSEPHistogram.value.data()); // define all histograms dqhistograms::AddHistogramsFromJSON(fHistMan, cfgAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON diff --git a/ALICE3/Tasks/alice3HfTask3Prong.cxx b/ALICE3/Tasks/alice3HfTask3Prong.cxx index 4f21bada994..8901ae6e8a9 100644 --- a/ALICE3/Tasks/alice3HfTask3Prong.cxx +++ b/ALICE3/Tasks/alice3HfTask3Prong.cxx @@ -58,7 +58,6 @@ struct Alice3HfTask3Prong { Configurable ccdbPathGrp{"ccdbPathGrp", "GLO/GRP/GRP", "Path of the grp file (Run 2)"}; Configurable ccdbPathGrpMag{"ccdbPathGrpMag", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object (Run 3)"}; - HfHelperAlice3 hfHelper; SliceCache cache; Service ccdb; @@ -119,7 +118,7 @@ struct Alice3HfTask3Prong { registry.add(("MC/gen/nonprompt/" + histoName + "GenNonPrompt").c_str(), ("MC particles (matched, non-prompt);" + xAxisTitle + ";" + yAxisTitle).c_str(), configSpec); }; - auto vbins = (std::vector)binsPt; + auto vbins = std::vector(binsPt); /// Reconstructed Histograms addHistogramsRec("hMass", "inv. mass (p K #pi) (GeV/#it{c}^{2})", "", {HistType::kTH1F, {{600, 1.98, 2.58}}}); @@ -271,7 +270,7 @@ struct Alice3HfTask3Prong { for (const auto& candidate : candidates) { registry.fill(HIST("MC/rec/hCandidateCounter"), 1.); /// rapidity selection - if (yCandRecoMax >= 0. && std::abs(hfHelper.getCandY(candidate)) > yCandRecoMax) { + if (yCandRecoMax >= 0. && std::abs(HfHelperAlice3::getCandY(candidate)) > yCandRecoMax) { continue; } registry.fill(HIST("MC/rec/hCandidateCounter"), 2.); @@ -311,7 +310,7 @@ struct Alice3HfTask3Prong { if (candidate.isSelMassHypo0()) { registry.fill(HIST("hSelectionStatus"), 0., pt); - double mass = hfHelper.getCandMass(candidate); + double mass = HfHelperAlice3::getCandMass(candidate); /// Fill histograms fillHistogramsRecSig(candidate, mass, false); if (originType == RecoDecay::OriginType::Prompt) { @@ -334,7 +333,7 @@ struct Alice3HfTask3Prong { if (fillSwapMassHypo) { if (candidate.isSelMassHypo1()) { registry.fill(HIST("hSelectionStatus"), 1., pt); - double mass = hfHelper.getCandMass(candidate); + double mass = HfHelperAlice3::getCandMass(candidate); /// Fill histograms fillHistogramsRecSig(candidate, mass, true); if (originType == RecoDecay::OriginType::Prompt) { @@ -357,12 +356,12 @@ struct Alice3HfTask3Prong { } } else { // Background if (candidate.isSelMassHypo0()) { - double mass = hfHelper.getCandMass(candidate); + double mass = HfHelperAlice3::getCandMass(candidate); fillHistogramsRecSig(candidate, mass, false); } if (fillSwapMassHypo) { if (candidate.isSelMassHypo1()) { - double mass = hfHelper.getCandMass(candidate); + double mass = HfHelperAlice3::getCandMass(candidate); fillHistogramsRecSig(candidate, mass, true); } } @@ -385,10 +384,10 @@ struct Alice3HfTask3Prong { static constexpr auto histoSuffix = HIST("Gen") + HIST(SignalSuffixes[SignalType]); registry.fill(histoPrefix + HIST("hPt") + histoSuffix, particle.pt()); registry.fill(histoPrefix + HIST("hEta") + histoSuffix, particle.eta()); - registry.fill(histoPrefix + HIST("hY") + histoSuffix, hfHelper.getCandY(particle)); + registry.fill(histoPrefix + HIST("hY") + histoSuffix, HfHelperAlice3::getCandY(particle)); registry.fill(histoPrefix + HIST("hPhi") + histoSuffix, particle.phi()); registry.fill(histoPrefix + HIST("hEtaVsPt") + histoSuffix, particle.eta(), particle.pt()); - registry.fill(histoPrefix + HIST("hYVsPt") + histoSuffix, hfHelper.getCandY(particle), particle.pt()); + registry.fill(histoPrefix + HIST("hYVsPt") + histoSuffix, HfHelperAlice3::getCandY(particle), particle.pt()); registry.fill(histoPrefix + HIST("hPhiVsPt") + histoSuffix, particle.phi(), particle.pt()); if (particle.has_daughters()) { const auto firstDauIdx = particle.daughtersIds().front(); @@ -424,7 +423,7 @@ struct Alice3HfTask3Prong { // MC gen. for (const auto& particle : mcParticles) { if (std::abs(particle.flagMcGen()) == selectedPdg) { - double yGen = hfHelper.getCandY(particle); + double yGen = HfHelperAlice3::getCandY(particle); if (yCandGenMax >= 0. && std::abs(yGen) > yCandGenMax) { continue; } diff --git a/ALICE3/Tasks/alice3Strangeness.cxx b/ALICE3/Tasks/alice3Strangeness.cxx index 4733c899d16..0a4543e4a34 100644 --- a/ALICE3/Tasks/alice3Strangeness.cxx +++ b/ALICE3/Tasks/alice3Strangeness.cxx @@ -22,6 +22,7 @@ #include "ALICE3/DataModel/OTFCollision.h" #include "ALICE3/DataModel/OTFStrangeness.h" #include "ALICE3/DataModel/tracksAlice3.h" +#include "Common/Core/RecoDecay.h" #include "Common/DataModel/TrackSelectionTables.h" #include @@ -101,6 +102,7 @@ struct Alice3Strangeness { Configurable cosPAngleSelection{"cosPAngleSelection", 0.97f, "cosine of pointing angle selection"}; Configurable dcaDaughtersToPVSelection{"dcaDaughtersToPVSelection", 0.05f, "DCA daughters to primary vertex selection"}; Configurable v0RadiusSelection{"v0RadiusSelection", 0.5f, "V0 radius selection"}; + Configurable v0MaxRadiusSelection{"v0MaxRadiusSelection", 3.0f, "V0 maximum radius selection"}; Configurable armenterosSelection{"armenterosSelection", 0.2f, "Armenteros-Podolanski selection in [0]* alpha"}; Configurable competingMassRejectionK0{"competingMassRejectionK0", 0.005f, "competing mass rejection for K0"}; Configurable competingMassRejectionLambda{"competingMassRejectionLambda", 0.01f, "competing mass rejection for Lambda"}; @@ -166,11 +168,11 @@ struct Alice3Strangeness { Configurable tofOuter{"tofOuter", false, "enable particle identification selection"}; } cascadeFlags; - uint16_t appliedSelectionCheckMask; - double selectionCheck; - double selectionCheckPos; - const int posDaugDCAselIDx = 3; - static constexpr std::string_view KSelectionNames[] = {"DCAV0Daughters", "PointingAngle", "DCAtoPVNegDaughter", "DCAtoPVPosDaughter", "V0Radius", "ProperLifeTime"}; + uint16_t appliedSelectionCheckMask{}; + double selectionCheck{}; + double selectionCheckPos{}; + static constexpr int posDaugDCAselIDx = 3; + static constexpr std::array KSelectionNames = {"DCAV0Daughters", "PointingAngle", "DCAtoPVNegDaughter", "DCAtoPVPosDaughter", "V0Radius", "ProperLifeTime"}; static constexpr float ToMicrons = 1e+4f; static constexpr float CtauXi = 4.91f; @@ -214,7 +216,7 @@ struct Alice3Strangeness { template void configureNSigmas(const TCascade& cascade) { - const Cascade::Type analysedCascadeType = static_cast(cascadeFlags.analyseCascade.value); + const auto analysedCascadeType = static_cast(cascadeFlags.analyseCascade.value); switch (analysedCascadeType) { case Cascade::Xi: nSigmaBachInnerTof = cascade.nSigmaInnerTofXiBachPi(); @@ -295,8 +297,10 @@ struct Alice3Strangeness { histos.add("reconstructedCandidates/hEtaDaughters", "hEtaDaughters", kTH1F, {histAxes.axisEta}); histos.add("reconstructedCandidates/K0/hMass", "hMass", kTH3D, {histAxes.axisK0Mass, histAxes.axisPt, histAxes.axisEta}); histos.add("reconstructedCandidates/K0/hMass1D", "hMass1D", kTH1D, {histAxes.axisK0Mass}); + histos.add("reconstructedCandidates/K0/hMassRadius", "hMassRadius", kTH3D, {histAxes.axisK0Mass, histAxes.axisPt, {200, 0, 200}}); histos.add("reconstructedCandidates/Lambda/hMass", "hMass", kTH3D, {histAxes.axisLambdaMass, histAxes.axisPt, histAxes.axisEta}); histos.add("reconstructedCandidates/Lambda/hMass1D", "hMass1D", kTH1D, {histAxes.axisLambdaMass}); + histos.add("reconstructedCandidates/Lambda/hMassRadius", "hMassRadius", kTH3D, {histAxes.axisLambdaMass, histAxes.axisPt, {200, 0, 200}}); histos.add("reconstructedCandidates/hArmeterosBeforeAllSelections", "hArmeterosBeforeAllSelections", kTH2D, {{100, -1.0f, 1.0f}, {200, 0.0f, 0.5f}}); histos.add("reconstructedCandidates/hArmeterosAfterAllSelections", "hArmeterosAfterAllSelections", kTH2D, {{100, -1.0f, 1.0f}, {200, 0.0f, 0.5f}}); histos.add("reconstructedCandidates/hPtEtaPosDaughter", "hPtEtaPosDaughter", kTH2D, {histAxes.axisPt, histAxes.axisEta}); @@ -402,6 +406,12 @@ struct Alice3Strangeness { } histos.addClone("reconstructedCandidates/Lambda/", "reconstructedCandidates/AntiLambda/"); + if (doprocessMCTruth) { + histos.add("mcTruth/K0Short/hPtRadius", "hPtRadius", kTH2D, {histAxes.axisPt, histAxes.axisRadius}); + histos.add("mcTruth/Lambda/hPtRadius", "hPtRadius", kTH2D, {histAxes.axisPt, histAxes.axisRadius}); + histos.add("mcTruth/AntiLambda/hPtRadius", "hPtRadius", kTH2D, {histAxes.axisPt, histAxes.axisRadius}); + } + appliedSelectionCheckMask = 0; if (!v0SelectionFlags.applyDCAdaughterSelection) { SETBIT(appliedSelectionCheckMask, 0); @@ -440,20 +450,25 @@ struct Alice3Strangeness { histos.fill(HIST("K0/hSelections"), 0); // all candidates histos.fill(HIST("K0/hDCANegDaughter"), negV0Daughter.dcaXY()); histos.fill(HIST("K0/hDCAPosDaughter"), posV0Daughter.dcaXY()); - if (std::abs(negV0Daughter.dcaXY()) < v0SelectionValues.dcaDaughtersToPVSelection) + if (std::abs(negV0Daughter.dcaXY()) < v0SelectionValues.dcaDaughtersToPVSelection) { continue; + } histos.fill(HIST("K0/hSelections"), 1); // dcaXY cut - if (std::abs(posV0Daughter.dcaXY()) < v0SelectionValues.dcaDaughtersToPVSelection) + if (std::abs(posV0Daughter.dcaXY()) < v0SelectionValues.dcaDaughtersToPVSelection) { continue; + } histos.fill(HIST("K0/hSelections"), 2); // dcaXY cut - if (v0Cand.dcaV0Daughters() > v0SelectionValues.dcaDaughterSelection) + if (v0Cand.dcaV0Daughters() > v0SelectionValues.dcaDaughterSelection) { continue; + } histos.fill(HIST("K0/hSelections"), 3); // dca between daughters - if (v0Cand.v0Radius() < v0SelectionValues.v0RadiusSelection) + if (v0Cand.v0Radius() < v0SelectionValues.v0RadiusSelection) { continue; + } histos.fill(HIST("K0/hSelections"), 4); // radius cut - if (std::abs(negV0Daughter.eta()) > v0SelectionValues.etaDaughterSelection || std::abs(posV0Daughter.eta()) > v0SelectionValues.etaDaughterSelection) + if (std::abs(negV0Daughter.eta()) > v0SelectionValues.etaDaughterSelection || std::abs(posV0Daughter.eta()) > v0SelectionValues.etaDaughterSelection) { continue; + } histos.fill(HIST("K0/hSelections"), 5); // eta cut histos.fill(HIST("K0/hMassSelected"), v0Cand.mK0Short(), v0Cand.pt()); } @@ -486,78 +501,92 @@ struct Alice3Strangeness { histos.fill(HIST("reconstructedCandidates/hArmeterosBeforeAllSelections"), v0.alpha(), v0.qtArm()); histos.fill(HIST("hV0CandidateCounter"), 0.5); if (v0SelectionFlags.applyRapiditySelection) { - if (isK0 && std::abs(v0.yK0Short()) > v0SelectionValues.yK0Selection) + if (isK0 && std::abs(v0.yK0Short()) > v0SelectionValues.yK0Selection) { continue; - if ((isLambda || isAntiLambda) && std::abs(v0.yLambda()) > v0SelectionValues.yLambdaSelection) + } + if ((isLambda || isAntiLambda) && std::abs(v0.yLambda()) > v0SelectionValues.yLambdaSelection) { continue; + } } histos.fill(HIST("hV0CandidateCounter"), 1.5); if (v0SelectionFlags.applyDCAdaughterSelection) { - if (std::abs(v0.dcaV0Daughters()) > v0SelectionValues.dcaDaughterSelection) + if (std::abs(v0.dcaV0Daughters()) > v0SelectionValues.dcaDaughterSelection) { continue; + } } else { selectionCheck = v0.dcaV0Daughters(); } histos.fill(HIST("hV0CandidateCounter"), 2.5); if (v0SelectionFlags.applyCosOfPAngleSelection) { - if (v0.cosPA() < v0SelectionValues.cosPAngleSelection) + if (v0.cosPA() < v0SelectionValues.cosPAngleSelection) { continue; + } } else { selectionCheck = std::acos(v0.cosPA()); } histos.fill(HIST("hV0CandidateCounter"), 3.5); if (v0SelectionFlags.applyDCADaughtersToPVSelection) { if ((std::abs(v0.dcaNegToPV()) < v0SelectionValues.dcaDaughtersToPVSelection) || - (std::abs(v0.dcaPosToPV()) < v0SelectionValues.dcaDaughtersToPVSelection)) + (std::abs(v0.dcaPosToPV()) < v0SelectionValues.dcaDaughtersToPVSelection)) { continue; + } } else { selectionCheckPos = std::abs(v0.dcaPosToPV()); selectionCheck = std::abs(v0.dcaNegToPV()); } histos.fill(HIST("hV0CandidateCounter"), 4.5); if (v0SelectionFlags.applyV0RadiusSelection) { - if (v0.v0radius() < v0SelectionValues.v0RadiusSelection) + if (v0.v0radius() < v0SelectionValues.v0RadiusSelection || v0.v0radius() > v0SelectionValues.v0MaxRadiusSelection) { continue; + } } else { selectionCheck = v0.v0radius(); } histos.fill(HIST("hV0CandidateCounter"), 5.5); if (isK0) { if (v0SelectionFlags.applyArmenterosSelection) { - if (v0.qtArm() < v0SelectionValues.armenterosSelection * std::abs(v0.alpha())) + if (v0.qtArm() < v0SelectionValues.armenterosSelection * std::abs(v0.alpha())) { continue; + } } } histos.fill(HIST("hV0CandidateCounter"), 6.5); if (isK0 && v0SelectionFlags.applyCompetingMassRejection) { - if (std::abs(v0.mLambda() - o2::constants::physics::MassLambda0) < v0SelectionValues.competingMassRejectionK0) + if (std::abs(v0.mLambda() - o2::constants::physics::MassLambda0) < v0SelectionValues.competingMassRejectionK0) { continue; - if (std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda0) < v0SelectionValues.competingMassRejectionK0) + } + if (std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda0) < v0SelectionValues.competingMassRejectionK0) { continue; + } } if ((isLambda || isAntiLambda) && v0SelectionFlags.applyCompetingMassRejection) { - if (std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) < v0SelectionValues.competingMassRejectionLambda) + if (std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) < v0SelectionValues.competingMassRejectionLambda) { continue; + } } histos.fill(HIST("hV0CandidateCounter"), 7.5); if (v0SelectionFlags.applyLifetimeSelection) { - if (isK0 && v0.distOverTotMom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassK0Short > v0SelectionValues.lifetimecutak0) + if (isK0 && v0.distOverTotMom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassK0Short > v0SelectionValues.lifetimecutak0) { continue; - if ((isLambda || isAntiLambda) && v0.distOverTotMom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0 > v0SelectionValues.lifetimecutambda) + } + if ((isLambda || isAntiLambda) && v0.distOverTotMom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0 > v0SelectionValues.lifetimecutambda) { continue; + } } else { - if (isK0) + if (isK0) { selectionCheck = v0.distOverTotMom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassK0Short; - else + } else { selectionCheck = v0.distOverTotMom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0; + } } histos.fill(HIST("hV0CandidateCounter"), 8.5); if (v0SelectionFlags.analyseRecoV0Candidates) { auto posTrack = v0.template posTrack_as(); auto negTrack = v0.template negTrack_as(); if (v0SelectionFlags.applyEtaDaughterSelection) { - if (std::abs(posTrack.eta()) > v0SelectionValues.etaDaughterSelection || std::abs(negTrack.eta()) > v0SelectionValues.etaDaughterSelection) + if (std::abs(posTrack.eta()) > v0SelectionValues.etaDaughterSelection || std::abs(negTrack.eta()) > v0SelectionValues.etaDaughterSelection) { continue; + } } histos.fill(HIST("reconstructedCandidates/hEtaDaughters"), posTrack.eta()); histos.fill(HIST("reconstructedCandidates/hEtaDaughters"), negTrack.eta()); @@ -581,14 +610,18 @@ struct Alice3Strangeness { static_for<0, 5>([&](auto i) { constexpr int In = i.value; if (TESTBIT(appliedSelectionCheckMask, In)) { - if (In == posDaugDCAselIDx) + if (In == posDaugDCAselIDx) { selectionCheck = selectionCheckPos; - if (isK0) + } + if (isK0) { histos.fill(HIST("reconstructedCandidates/K0/h") + HIST(KSelectionNames[In]), v0.mK0Short(), v0.pt(), selectionCheck); - if (isLambda) + } + if (isLambda) { histos.fill(HIST("reconstructedCandidates/Lambda/h") + HIST(KSelectionNames[In]), v0.mLambda(), v0.pt(), selectionCheck); - if (isAntiLambda) + } + if (isAntiLambda) { histos.fill(HIST("reconstructedCandidates/AntiLambda/h") + HIST(KSelectionNames[In]), v0.mAntiLambda(), v0.pt(), selectionCheck); + } } }); } @@ -596,14 +629,17 @@ struct Alice3Strangeness { if (isK0) { histos.fill(HIST("reconstructedCandidates/K0/hMass"), v0.mK0Short(), v0.pt(), v0.eta()); histos.fill(HIST("reconstructedCandidates/K0/hMass1D"), v0.mK0Short()); + histos.fill(HIST("reconstructedCandidates/K0/hMassRadius"), v0.mK0Short(), v0.pt(), v0.v0radius()); } if (isLambda) { histos.fill(HIST("reconstructedCandidates/Lambda/hMass"), v0.mLambda(), v0.pt(), v0.eta()); histos.fill(HIST("reconstructedCandidates/Lambda/hMass1D"), v0.mLambda()); + histos.fill(HIST("reconstructedCandidates/Lambda/hMassRadius"), v0.mLambda(), v0.pt(), v0.v0radius()); } if (isAntiLambda) { histos.fill(HIST("reconstructedCandidates/AntiLambda/hMass"), v0.mAntiLambda(), v0.pt(), v0.eta()); histos.fill(HIST("reconstructedCandidates/AntiLambda/hMass1D"), v0.mAntiLambda()); + histos.fill(HIST("reconstructedCandidates/AntiLambda/hMassRadius"), v0.mAntiLambda(), v0.pt(), v0.v0radius()); } } } @@ -850,14 +886,37 @@ struct Alice3Strangeness { } } } + void processMCTruth(soa::Join const& mcParticles) + { + + for (auto const& mcPart : mcParticles) { + + if (!mcPart.isPhysicalPrimary()) { + continue; + } + + float ptmc = RecoDecay::sqrtSumOfSquares(mcPart.px(), mcPart.py()); + + if (mcPart.pdgCode() == 310) { + histos.fill(HIST("mcTruth/K0Short/hPtRadius"), ptmc, mcPart.secondaryVertexRadius()); + } + if (mcPart.pdgCode() == 3122) { + histos.fill(HIST("mcTruth/Lambda/hPtRadius"), ptmc, mcPart.secondaryVertexRadius()); + } + if (mcPart.pdgCode() == -3122) { + histos.fill(HIST("mcTruth/AntiLambda/hPtRadius"), ptmc, mcPart.secondaryVertexRadius()); + } + } + } PROCESS_SWITCH(Alice3Strangeness, processAllFindableCandidates, "", false); PROCESS_SWITCH(Alice3Strangeness, processFoundV0Candidates, "", true); + PROCESS_SWITCH(Alice3Strangeness, processMCTruth, "", true); PROCESS_SWITCH(Alice3Strangeness, processFoundCascadeCandidates, "", true); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(context)}; } diff --git a/ALICE3/Tasks/alice3TrackingPerformance.cxx b/ALICE3/Tasks/alice3TrackingPerformance.cxx index 77ebd8a1e3d..fa69139b33a 100644 --- a/ALICE3/Tasks/alice3TrackingPerformance.cxx +++ b/ALICE3/Tasks/alice3TrackingPerformance.cxx @@ -34,6 +34,8 @@ #include #include +#include +#include #include #include #include @@ -41,36 +43,60 @@ using namespace o2; using namespace o2::framework; -std::shared_ptr particlePdgCodes; -std::map> particlePtDistribution; -std::map> particleEtaDistribution; -std::map> ptDistribution; -std::map> ptResolutionVsPt; -std::map> ptResolutionVsEta; -std::map> invPtResolutionVsPt; -std::map> invPtResolutionVsEta; -std::map> dcaXyResolutionVsPt; -std::map> dcaZResolutionVsPt; -std::map> covariancePtPtVsPt; -std::map> covarianceDcaXyDcaXyVsPt; -std::map> covarianceDcaZDcaZVsPt; struct Alice3TrackingPerformance { - Configurable> pdgCodes{"pdgCodes", {0, 211}, "List of PDG codes to consider for efficiency calculation. (0 means all)"}; + Configurable> pdgCodes{"pdgCodes", {211}, "List of signed PDG codes to consider for performance evaluation"}; HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - ConfigurableAxis ptAxis{"ptAxis", {500, 0, 100}, "#it{p}_{T} (GeV/#it{c})"}; - ConfigurableAxis etaAxis{"etaAxis", {100, -5.f, 5.f}, "#eta"}; - ConfigurableAxis invPtDeltaAxis{"invPtDeltaAxis", {1000, -0.1f, 0.1f}, "1./#it{p}_{T}^{gen} - 1./#it{p}_{T}^{reco} (GeV/#it{c})^{-1}"}; + struct : ConfigurableGroup { + std::string prefix = "Axes"; // JSON group name + ConfigurableAxis ptAxis{"ptAxis", {500, 0, 100}, "#it{p}_{T} (GeV/#it{c})"}; + ConfigurableAxis etaAxis{"etaAxis", {100, -5.f, 5.f}, "#eta"}; + ConfigurableAxis invPtDeltaAxis{"invPtDeltaAxis", {1000, -0.1f, 0.1f}, "1./#it{p}_{T}^{gen} - 1./#it{p}_{T}^{reco} (GeV/#it{c})^{-1}"}; + ConfigurableAxis dcaAxis{"dcaAxis", {1000, -0.1f, 0.1f}, "DCA (cm)"}; + } settingsAxes; + + struct : ConfigurableGroup { + std::string prefix = "Selections"; // JSON group name + Configurable vtxSelX{"vtxSelX", -1.f, "Vertex selection in X (cm), if negative no selection is applied"}; + Configurable vtxSelY{"vtxSelY", -1.f, "Vertex selection in Y (cm), if negative no selection is applied"}; + Configurable vtxSelZ{"vtxSelZ", -1.f, "Vertex selection in Z (cm), if negative no selection is applied"}; + } settingsSelections; + + // Histograms + std::shared_ptr particlePdgCodes; + std::map> particlePtDistribution; + std::map> particleEtaDistribution; + std::map> ptDistribution; + std::map> ptResolutionVsPt; + std::map> ptResolutionVsEta; + std::map> invPtResolutionVsPt; + std::map> invPtResolutionVsEta; + std::map> dcaXyResolutionVsPt; + std::map> dcaZResolutionVsPt; + std::map> covariancePtPtVsPt; + std::map> covarianceDcaXyDcaXyVsPt; + std::map> covarianceDcaZDcaZVsPt; void init(o2::framework::InitContext&) { + // Check if pdgCodes has duplicates and remove them + std::sort(pdgCodes.value.begin(), pdgCodes.value.end()); + pdgCodes.value.erase(std::unique(pdgCodes.value.begin(), pdgCodes.value.end()), pdgCodes.value.end()); + // Add 0 to the list of pdgCodes if it is not already present + if (std::find(pdgCodes.value.begin(), pdgCodes.value.end(), 0) == pdgCodes.value.end()) { + pdgCodes.value.push_back(0); + } const AxisSpec axisPtDelta{100, -1, 1, "(#it{p}_{T}^{reco} - #it{p}_{T}^{gen}) / #it{p}_{T}^{gen}"}; - const AxisSpec axisDcaXy{100, -1, 1, "DCA_{xy} (cm)"}; - const AxisSpec axisDcaZ{100, -1, 1, "DCA_{z} (cm)"}; + const AxisSpec axisPt{settingsAxes.ptAxis, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisInvPt{settingsAxes.invPtDeltaAxis, "1/#it{p}_{T} (GeV/#it{c})^{-1}"}; + const AxisSpec axisEta{settingsAxes.etaAxis, "#eta"}; + const AxisSpec axisDcaXy{settingsAxes.dcaAxis, "DCA_{xy} (cm)"}; + const AxisSpec axisDcaZ{settingsAxes.dcaAxis, "DCA_{z} (cm)"}; const AxisSpec axisCovariancePtPt{100, 0, 10, "cov(#it{p}_{T}, #it{p}_{T}) ((GeV/#it{c})^{2})"}; const AxisSpec axisCovarianceDcaXyDcaXy{100, 0, 1, "cov(DCA_{xy}, DCA_{xy}) (cm^{2})"}; const AxisSpec axisCovarianceDcaZDcaZ{100, 0, 1, "cov(DCA_{z}, DCA_{z}) (cm^{2})"}; - particlePdgCodes = histos.add("particlePdgCodes", "", kTH1D, {AxisSpec{100, -0.5, 99.5, "PDG Code"}}); + const int nBinsPdgCodes = static_cast(pdgCodes.value.size()); + particlePdgCodes = histos.add("particlePdgCodes", "", kTH1D, {AxisSpec{nBinsPdgCodes, -0.5, -0.5 + nBinsPdgCodes, "PDG Code"}}); for (const int& pdg : pdgCodes.value) { std::string prefix = Form("%i", pdg); if (pdg < 0) { @@ -79,24 +105,26 @@ struct Alice3TrackingPerformance { const std::string tag = "_" + prefix; prefix += "/"; auto histoName = [&](const std::string& name) { return Form("%s%s%s", prefix.c_str(), name.c_str(), tag.c_str()); }; - particlePtDistribution[pdg] = histos.add(histoName("particlePtDistribution"), "", kTH1D, {ptAxis}); - particleEtaDistribution[pdg] = histos.add(histoName("particleEtaDistribution"), "", kTH1D, {etaAxis}); - - ptDistribution[pdg] = histos.add(histoName("ptDistribution"), "", kTH1D, {ptAxis}); - ptResolutionVsPt[pdg] = histos.add(histoName("ptResolutionVsPt"), "", kTH2D, {ptAxis, axisPtDelta}); - ptResolutionVsEta[pdg] = histos.add(histoName("ptResolutionVsEta"), "", kTProfile2D, {ptAxis, etaAxis}); - invPtResolutionVsPt[pdg] = histos.add(histoName("invPtResolutionVsPt"), "", kTH2D, {ptAxis, invPtDeltaAxis}); - invPtResolutionVsEta[pdg] = histos.add(histoName("invPtResolutionVsEta"), "", kTProfile2D, {ptAxis, etaAxis}); - dcaXyResolutionVsPt[pdg] = histos.add(histoName("dcaXyResolutionVsPt"), "", kTH2D, {ptAxis, axisDcaXy}); - dcaZResolutionVsPt[pdg] = histos.add(histoName("dcaZResolutionVsPt"), "", kTH2D, {ptAxis, axisDcaZ}); - covariancePtPtVsPt[pdg] = histos.add(histoName("covariancePtPtVsPt"), "", kTH2D, {ptAxis, axisCovariancePtPt}); - covarianceDcaXyDcaXyVsPt[pdg] = histos.add(histoName("covarianceDcaXyDcaXyVsPt"), "", kTH2D, {ptAxis, axisCovarianceDcaXyDcaXy}); - covarianceDcaZDcaZVsPt[pdg] = histos.add(histoName("covarianceDcaZDcaZVsPt"), "", kTH2D, {ptAxis, axisCovarianceDcaZDcaZ}); + particlePtDistribution[pdg] = histos.add(histoName("particlePtDistribution"), "", kTH1D, {axisPt}); + LOG(info) << "Added histogram: " << histoName("particlePtDistribution"); + particleEtaDistribution[pdg] = histos.add(histoName("particleEtaDistribution"), "", kTH1D, {axisEta}); + + ptDistribution[pdg] = histos.add(histoName("ptDistribution"), "", kTH1D, {axisPt}); + ptResolutionVsPt[pdg] = histos.add(histoName("ptResolutionVsPt"), "", kTH2D, {axisPt, axisPtDelta}); + ptResolutionVsEta[pdg] = histos.add(histoName("ptResolutionVsEta"), "", kTProfile2D, {axisPt, axisEta}); + invPtResolutionVsPt[pdg] = histos.add(histoName("invPtResolutionVsPt"), "", kTH2D, {axisPt, axisInvPt}); + invPtResolutionVsEta[pdg] = histos.add(histoName("invPtResolutionVsEta"), "", kTProfile2D, {axisPt, axisEta}); + dcaXyResolutionVsPt[pdg] = histos.add(histoName("dcaXyResolutionVsPt"), "", kTH2D, {axisPt, axisDcaXy}); + dcaZResolutionVsPt[pdg] = histos.add(histoName("dcaZResolutionVsPt"), "", kTH2D, {axisPt, axisDcaZ}); + covariancePtPtVsPt[pdg] = histos.add(histoName("covariancePtPtVsPt"), "", kTH2D, {axisPt, axisCovariancePtPt}); + covarianceDcaXyDcaXyVsPt[pdg] = histos.add(histoName("covarianceDcaXyDcaXyVsPt"), "", kTH2D, {axisPt, axisCovarianceDcaXyDcaXy}); + covarianceDcaZDcaZVsPt[pdg] = histos.add(histoName("covarianceDcaZDcaZVsPt"), "", kTH2D, {axisPt, axisCovarianceDcaZDcaZ}); } } void process(soa::Join const& tracks, - aod::McParticles const& mcParticles) + aod::McParticles const& mcParticles, + aod::McCollisions const&) { auto isParticleSelected = [&](const o2::aod::McParticle& p) { if (!p.isPhysicalPrimary()) { @@ -107,6 +135,18 @@ struct Alice3TrackingPerformance { }; for (const auto& mcParticle : mcParticles) { + // Selecting collisions + const auto& collision = mcParticle.mcCollision(); + if (settingsSelections.vtxSelX >= 0.f && std::abs(collision.posX()) > settingsSelections.vtxSelX) { + continue; + } + if (settingsSelections.vtxSelY >= 0.f && std::abs(collision.posY()) > settingsSelections.vtxSelY) { + continue; + } + if (settingsSelections.vtxSelZ >= 0.f && std::abs(collision.posZ()) > settingsSelections.vtxSelZ) { + continue; + } + particlePdgCodes->Fill(Form("%d", mcParticle.pdgCode()), 1); if (!isParticleSelected(mcParticle)) { continue; @@ -125,7 +165,7 @@ struct Alice3TrackingPerformance { } const auto& mcParticle = track.mcParticle(); const float ptResolution = (track.pt() - mcParticle.pt()) / mcParticle.pt(); - const float invptResolution = 1.f / track.pt() - 1.f / mcParticle.pt(); + const float invptResolution = (track.pt() > 0.f ? 1.f / track.pt() : 0.f) - (mcParticle.pt() > 0.f ? 1.f / mcParticle.pt() : 0.f); const float covariancePtPt = track.sigma1Pt() * track.sigma1Pt() * track.pt() * track.pt() * track.pt() * track.pt(); auto fillResolutionHistograms = [&](const int p) { @@ -137,8 +177,8 @@ struct Alice3TrackingPerformance { dcaXyResolutionVsPt[p]->Fill(mcParticle.pt(), track.dcaXY()); dcaZResolutionVsPt[p]->Fill(mcParticle.pt(), track.dcaZ()); covariancePtPtVsPt[p]->Fill(mcParticle.pt(), covariancePtPt); - covarianceDcaXyDcaXyVsPt[p]->Fill(mcParticle.pt(), track.sigmaDcaXY2()); - covarianceDcaZDcaZVsPt[p]->Fill(mcParticle.pt(), track.sigmaDcaZ2()); + // covarianceDcaXyDcaXyVsPt[p]->Fill(mcParticle.pt(), track.sigmaDcaXY2()); + // covarianceDcaZDcaZVsPt[p]->Fill(mcParticle.pt(), track.sigmaDcaZ2()); }; fillResolutionHistograms(0); diff --git a/CODEOWNERS b/CODEOWNERS index 8faaf6abff2..7a7a8b840a3 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -49,11 +49,17 @@ /PWGCF/TwoParticleCorrelations @alibuild @jaelpark @wenyaCern @victor-gonzalez @dsarkaralice @lauraser /PWGCF/Tutorial @alibuild @ariedel-cern @victor-gonzalez @dsarkaralice @lauraser -/PWGDQ @alibuild @iarsene @mcoquet642 @XiaozhiBai @mguilbau +#PWG-DQ +/PWGDQ @alibuild @iarsene @XiaozhiBai @mguilbau @axti98 @lucamicheletti93 @glegras @bhagyasahoo @csuire @shreyasiacharya + +#PWG-EM /PWGEM @alibuild @feisenhu @dsekihat @jokonig @hscheid /PWGEM/Dilepton @alibuild @mikesas @rbailhac @dsekihat @feisenhu @hscheid @jokonig /PWGEM/PhotonMeson @alibuild @mhemmer-cern @dsekihat @jokonig @hscheid @amaringarcia @reygers + +#PWG-HF /PWGHF @alibuild @vkucera @fcolamar @fgrosa @fcatalan92 @mfaggin @mmazzilli @deepathoms @NicoleBastid @hahassan7 @jpxrk @apalasciano @zhangbiao-phy @gluparel @stefanopolitano @xinyepeng @singhra1994 + # PWG-LF /PWGLF @alibuild @omvazque @skundu692 @mpuccio /PWGLF/DataModel @alibuild @omvazque @skundu692 @mpuccio @gbencedi @abmodak @mario-krueger @maciacco @HorstMa @dmallick2 @smaff92 @ercolessi @romainschotter @prottayCMT @lhusova @@ -75,15 +81,15 @@ /PWGMM/Lumi @alibuild @omvazque @mpuccio @skundu692 @aalkin @jgcn @gbencedi @abmodak /PWGMM/UE @alibuild @omvazque @mpuccio @skundu692 @aalkin @aortizve @jgcn -/PWGUD @alibuild @amatyja @rolavick +/PWGUD @alibuild @amatyja @ekryshen /PWGJE @alibuild @nzardosh @fjonasALICE @jaimenorman @mhemmer-cern /Tools/PIDML @alibuild @saganatt -/Tools/ML @alibuild @fcatalan92 @fmazzasc +/Tools/ML @alibuild @fcatalan92 @fmazzasc @ChSonnabend /Tutorials/PWGCF @alibuild @jgrosseo @victor-gonzalez @zchochul -/Tutorials/PWGDQ @alibuild @iarsene @mcoquet642 @XiaozhiBai @mguilbau +/Tutorials/PWGDQ @alibuild @iarsene @XiaozhiBai @mguilbau @axti98 @lucamicheletti93 @glegras @bhagyasahoo @csuire @shreyasiacharya /Tutorials/PWGEM @alibuild @mikesas @rbailhac @dsekihat @ivorobye @feisenhu /Tutorials/PWGHF @alibuild @vkucera @fcolamar @fgrosa @gluparel @xinyepeng /Tutorials/PWGJE @alibuild @nzardosh @fjonasALICE @jaimenorman /Tutorials/PWGLF @alibuild @alcaliva @lbariogl @BongHwi @lbarnby @ercolessi @iravasen @njacazio @romainschotter @skundu692 @lhusova /Tutorials/PWGMM @alibuild @aalkin @ddobrigk -/Tutorials/PWGUD @alibuild @pbuehler @amatyja +/Tutorials/PWGUD @alibuild @amatyja @ekryshen diff --git a/CPPLINT.cfg b/CPPLINT.cfg index 1613014234b..53dade3df6c 100644 --- a/CPPLINT.cfg +++ b/CPPLINT.cfg @@ -1 +1,2 @@ -filter=-build/c++11,-build/c++17,-build/include_order,-build/namespaces,-readability/fn_size,-readability/nolint,-readability/todo,-runtime/references,-whitespace/blank_line,-whitespace/braces,-whitespace/comments,-whitespace/indent_namespace,-whitespace/line_length,-whitespace/semicolon,-whitespace/todo +filter=-build/c++11,-build/c++17,-build/include_order,-build/namespaces,-readability/alt_tokens,-readability/fn_size,-readability/nolint,-readability/todo,-runtime/references,-whitespace/blank_line,-whitespace/braces,-whitespace/comments,-whitespace/indent_namespace,-whitespace/line_length,-whitespace/newline,-whitespace/parens,-whitespace/semicolon,-whitespace/todo +extensions=C,c,c++,cc,cl,cpp,cu,cuh,cxx,cxx.in,h,h++,hh,h.in,hpp,hxx,inc,inl,macro diff --git a/Common/CCDB/AnalysisCCDBLinkDef.h b/Common/CCDB/AnalysisCCDBLinkDef.h index bb2da231284..b8253021f88 100644 --- a/Common/CCDB/AnalysisCCDBLinkDef.h +++ b/Common/CCDB/AnalysisCCDBLinkDef.h @@ -14,8 +14,7 @@ /// /// \author Evgeny Kryshen -#ifndef COMMON_CCDB_ANALYSISCCDBLINKDEF_H_ -#define COMMON_CCDB_ANALYSISCCDBLINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -24,5 +23,3 @@ #pragma link C++ class EventSelectionParams + ; #pragma link C++ class TriggerAliases + ; #pragma link C++ class std::map < uint64_t, uint32_t> + ; - -#endif // COMMON_CCDB_ANALYSISCCDBLINKDEF_H_ diff --git a/Common/CCDB/TriggerAliases.cxx b/Common/CCDB/TriggerAliases.cxx index c3d28889bca..5ad08da8798 100644 --- a/Common/CCDB/TriggerAliases.cxx +++ b/Common/CCDB/TriggerAliases.cxx @@ -13,10 +13,11 @@ #include +#include #include #include -std::string aliasLabels[kNaliases] = { +const std::array aliasLabels{ "kINT7", "kEMC7", "kINT7inMUON", diff --git a/Common/CCDB/TriggerAliases.h b/Common/CCDB/TriggerAliases.h index 60dbd965741..ffe00462a5b 100644 --- a/Common/CCDB/TriggerAliases.h +++ b/Common/CCDB/TriggerAliases.h @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -51,7 +52,7 @@ enum triggerAliases { kNaliases }; -extern std::string aliasLabels[kNaliases]; +extern const std::array aliasLabels; class TriggerAliases { diff --git a/Common/CCDB/macros/upload_event_selection_params.C b/Common/CCDB/macros/upload_event_selection_params.C index c6385c4d09d..f5995d730c4 100644 --- a/Common/CCDB/macros/upload_event_selection_params.C +++ b/Common/CCDB/macros/upload_event_selection_params.C @@ -18,6 +18,7 @@ #include +#include #include #include #include @@ -33,12 +34,12 @@ void upload_event_selection_params() // ccdb.truncate("EventSelection/EventSelectionParams"); ccdb.init("https://alice-ccdb.cern.ch"); - const int nPeriodsMax = 100; - EventSelectionParams* par[nPeriodsMax]; - string period[nPeriodsMax]; - int runFirst[nPeriodsMax]; - int runLast[nPeriodsMax]; - bool isNew[nPeriodsMax] = {0}; + constexpr int NPeriodsMax{100}; + std::array par{}; + std::array period{}; + std::array runFirst{}; + std::array runLast{}; + std::array isNew{}; int n = 0; period[n] = "pp2010"; @@ -256,7 +257,7 @@ void upload_event_selection_params() auto sor = o2::ccdb::BasicCCDBManager::getRunDuration(ccdb, runFirst[i]).first; auto eor = o2::ccdb::BasicCCDBManager::getRunDuration(ccdb, runLast[i]).second; - printf("sor=%llu eor=%llu\n", sor, eor); + printf("sor=%ld eor=%ld\n", sor, eor); metadata["period"] = period[i]; metadata["run_first"] = Form("%d", runFirst[i]); metadata["run_last"] = Form("%d", runLast[i]); diff --git a/Common/Core/AnalysisCoreLinkDef.h b/Common/Core/AnalysisCoreLinkDef.h index b376ccc7f10..21eb52ac586 100644 --- a/Common/Core/AnalysisCoreLinkDef.h +++ b/Common/Core/AnalysisCoreLinkDef.h @@ -9,8 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef COMMON_CORE_ANALYSISCORELINKDEF_H_ -#define COMMON_CORE_ANALYSISCORELINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -25,5 +24,3 @@ #pragma link C++ class o2::pid::tpc::Response + ; #pragma link C++ class OrbitRange + ; - -#endif // COMMON_CORE_ANALYSISCORELINKDEF_H_ diff --git a/Common/Core/CMakeLists.txt b/Common/Core/CMakeLists.txt index deaacfbd972..8e9afaf06c9 100644 --- a/Common/Core/CMakeLists.txt +++ b/Common/Core/CMakeLists.txt @@ -38,12 +38,28 @@ o2physics_add_library(AnalysisCore # is linked here. add_library(AnalysisPCH OBJECT analysisPCH.cxx) target_link_libraries(AnalysisPCH PUBLIC O2::Framework O2Physics::AnalysisCore) +# The PCH compile is one of the heaviest translation units in the tree -- it +# instantiates the whole data model up front -- but add_library() does not +# put it in the 'analysis' job pool the way o2physics_add_dpl_workflow() does +# for the workflows. Left out, it runs unthrottled next to the pooled +# compilations and blows past the per-job memory budget the pool exists to +# enforce. +set_property(TARGET AnalysisPCH PROPERTY JOB_POOL_COMPILE analysis) if(NOT DEFINED ENV{USE_RECC}) target_precompile_headers(AnalysisPCH PRIVATE + # Measured over all 1463 workflow translation units in the tree: these are + # the framework headers a large majority include and that were not + # precompiled here. Percentages are of those 1463. + # 78% + # 78% + # 76% + # 70% + # 69% + # 67% # NOT runDataProcessing.h. It states the contract itself: "we need to # declare the customize method before include this file". Precompiling it # makes it the first include in every workflow, so a source's customize() is @@ -51,6 +67,7 @@ if(NOT DEFINED ENV{USE_RECC}) # --dump-workflow with "missing option: ...". The link succeeds, so this # surfaces as a runtime failure with no hint that a PCH caused it. + # 51% diff --git a/Common/Core/CollisionAssociation.h b/Common/Core/CollisionAssociation.h index b0b3748817a..383a1ec10ce 100644 --- a/Common/Core/CollisionAssociation.h +++ b/Common/Core/CollisionAssociation.h @@ -273,8 +273,8 @@ class CollisionAssociation // then the track is an MFT track, or an MFT track with additionnal joined info // in this case TrackTimeResIsRange thresholdTime = trackTimeRes + mNumSigmaForTimeCompat * std::sqrt(collTimeRes2) + mTimeMargin; - } else if constexpr (TTracks::template contains()) { - // the track is a fwd track, with a gaussian time resolution + } else { + // the track is FwdTrack or FwdTracksReAlign, with a gaussian time resolution thresholdTime = mNumSigmaForTimeCompat * std::sqrt(sigmaTimeRes2) + mTimeMargin; } } diff --git a/Common/Core/EventFilteringUtilsLinkDef.h b/Common/Core/EventFilteringUtilsLinkDef.h index 3f029b8aa9a..394c051784f 100644 --- a/Common/Core/EventFilteringUtilsLinkDef.h +++ b/Common/Core/EventFilteringUtilsLinkDef.h @@ -9,6 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +// NOLINT(build/header_guard) + #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; diff --git a/Common/Core/FFitWeights.cxx b/Common/Core/FFitWeights.cxx index f502b4bc3c4..30d4922d13c 100644 --- a/Common/Core/FFitWeights.cxx +++ b/Common/Core/FFitWeights.cxx @@ -86,8 +86,8 @@ void FFitWeights::init() fW_data->Add(new TProfile("pMeanPt", "", centBin, 0, centBin)); fW_data->Add(new TH2D("hPtWeight", "", centBin, 0, centBin, ptBin, ptAxis->GetXmin(), ptAxis->GetXmax())); - ptProfCent = reinterpret_cast(fW_data->FindObject("pMeanPt")); - h2ptCent = reinterpret_cast(fW_data->FindObject("hPtWeight")); + ptProfCent = dynamic_cast(fW_data->FindObject("pMeanPt")); + h2ptCent = dynamic_cast(fW_data->FindObject("hPtWeight")); }; void FFitWeights::fillWeights(float centrality, float qn, int nh, const char* pf) @@ -98,10 +98,10 @@ void FFitWeights::fillWeights(float centrality, float qn, int nh, const char* pf if (!tar) return; - TH2D* th2 = reinterpret_cast(tar->FindObject(this->getQName(nh, pf))); + TH2D* th2 = dynamic_cast(tar->FindObject(this->getQName(nh, pf))); if (!th2) { tar->Add(new TH2D(this->getQName(nh, pf), this->getAxisName(nh, pf), centBin, 0, centBin, qAxis->GetNbins(), qAxis->GetXmin(), qAxis->GetXmax())); - th2 = reinterpret_cast(tar->At(tar->GetEntries() - 1)); + th2 = dynamic_cast(tar->At(tar->GetEntries() - 1)); } th2->Fill(centrality, qn); }; @@ -117,7 +117,7 @@ void FFitWeights::fillPt(float centrality, float pt, float weight, bool first) float FFitWeights::getPtMult(float centrality) { if (!ptProfCent) { - ptProfCent = reinterpret_cast(fW_data->FindObject("pMeanPt")); + ptProfCent = dynamic_cast(fW_data->FindObject("pMeanPt")); } return ptProfCent->GetBinContent(ptProfCent->FindBin(centrality)); }; @@ -132,7 +132,7 @@ Long64_t FFitWeights::Merge(TCollection* collist) } FFitWeights* lW = 0; TIter allW(collist); - while ((lW = (reinterpret_cast(allW())))) { + while ((lW = (dynamic_cast(allW())))) { addArray(fW_data, lW->getDataArray()); nmerged++; } @@ -150,7 +150,7 @@ void FFitWeights::addArray(TObjArray* targ, TObjArray* sour) auto* tObj = targ->FindObject(obj->GetName()); if (!tObj) { - auto* clone = static_cast(obj->Clone(obj->GetName())); + auto* clone = obj->Clone(obj->GetName()); if (auto* h = dynamic_cast(clone)) h->SetDirectory(0); targ->Add(clone); @@ -168,7 +168,7 @@ void FFitWeights::mptSel() if (!tar) return; - TH2D* th2 = reinterpret_cast(tar->FindObject("hPtWeight")); + TH2D* th2 = dynamic_cast(tar->FindObject("hPtWeight")); if (!th2) { return; } @@ -198,7 +198,7 @@ void FFitWeights::qSelection(const std::vector& nhv, const std::vector(tar->FindObject(this->getQName(nh, pf.c_str())))}; + TH2D* th2{dynamic_cast(tar->FindObject(this->getQName(nh, pf.c_str())))}; if (!th2) { LOGF(info, "FFitWeights qh not found!"); return; diff --git a/Common/Core/FFitWeightsLinkDef.h b/Common/Core/FFitWeightsLinkDef.h index 1960be8f4df..3c604376da9 100644 --- a/Common/Core/FFitWeightsLinkDef.h +++ b/Common/Core/FFitWeightsLinkDef.h @@ -14,9 +14,6 @@ /// /// \author Joachim C. K. B. Hansen -#ifndef COMMON_CORE_FFITWEIGHTSLINKDEF_H_ -#define COMMON_CORE_FFITWEIGHTSLINKDEF_H_ +// NOLINT(build/header_guard) #pragma link C++ class FFitWeights + ; - -#endif // COMMON_CORE_FFITWEIGHTSLINKDEF_H_ diff --git a/Common/Core/MetadataHelper.cxx b/Common/Core/MetadataHelper.cxx index c1712c3f3b6..7530e2a9900 100644 --- a/Common/Core/MetadataHelper.cxx +++ b/Common/Core/MetadataHelper.cxx @@ -25,6 +25,7 @@ #include #include +#include #include using namespace o2::common::core; @@ -153,6 +154,34 @@ std::string MetadataHelper::getO2Version() const return get("O2Version"); } +std::string MetadataHelper::getGenerator() const +{ + if (!isMC()) { + return {}; + } + + // MC production tag (LPMProductionTag) -> generator name, for AO2Ds without the generator in the metadata + static const std::map mcGeneratorMap = { + {"LHC26f3", "EPOS"}, + {"LHC26k3", "EPOS"}}; + + // FIXME: use the "Generator" key first once it is available in AO2Ds (it must also be added to the key list in the constructor) + // if (isKeyDefined("Generator")) { + // return get("Generator"); + // } + + // older AO2Ds: rely on the LPMProductionTag to generator map + if (isKeyDefined("LPMProductionTag")) { + auto it = mcGeneratorMap.find(get("LPMProductionTag")); + if (it != mcGeneratorMap.end()) { + return it->second; + } + } + + // no generator found in the metadata, fall back to PYTHIA + return "PYTHIA"; +} + bool MetadataHelper::isCommitInSoftwareTag(const std::string& commitHash, const std::string& ccdbUrl) const { const std::string softwareTag = getO2Version(); diff --git a/Common/Core/MetadataHelper.h b/Common/Core/MetadataHelper.h index 7b871084f9a..45e511a01a9 100644 --- a/Common/Core/MetadataHelper.h +++ b/Common/Core/MetadataHelper.h @@ -55,6 +55,10 @@ struct MetadataHelper { /// @return the O2 version from the metadata std::string getO2Version() const; + /// @brief Function to get the MC generator name from the metadata + /// @return the generator name (e.g. "PYTHIA", "EPOS"), "PYTHIA" for MC if it cannot be determined, or an empty string for data + std::string getGenerator() const; + /// @brief Function to get the metadata value for a given key /// @param key the key of the metadata /// @return the value of the metadata. Throws an exception if the key is not found diff --git a/Common/Core/PID/PIDTOFParamService.cxx b/Common/Core/PID/PIDTOFParamService.cxx index efd840273fa..e8138e21e28 100644 --- a/Common/Core/PID/PIDTOFParamService.cxx +++ b/Common/Core/PID/PIDTOFParamService.cxx @@ -298,7 +298,7 @@ struct TOFSupport : o2::framework::ServicePlugin { }, .configure = CommonServices::noConfiguration(), .exit = [](ServiceRegistryRef, void* service) { - auto* resp = reinterpret_cast(service); + auto* resp = static_cast(service); delete resp; }, .kind = ServiceKind::Serial}; } diff --git a/Common/Core/PID/TPCPIDResponse.h b/Common/Core/PID/TPCPIDResponse.h index 5120950fa5f..a8b082cad54 100644 --- a/Common/Core/PID/TPCPIDResponse.h +++ b/Common/Core/PID/TPCPIDResponse.h @@ -25,6 +25,7 @@ #include #include +#include #include namespace o2::pid::tpc @@ -77,7 +78,7 @@ class Response float GetExpectedSigma(const CollisionType& collision, const TrackType& trk, const o2::track::PID::ID id) const; /// Gets the expected resolution of the track with multTPC explicitly provided template - float GetExpectedSigmaAtMultiplicity(const long multTPC, const TrackType& trk, const o2::track::PID::ID id) const; + float GetExpectedSigmaAtMultiplicity(const int64_t multTPC, const TrackType& trk, const o2::track::PID::ID id) const; /// Gets the number of sigmas with respect the expected value template float GetNumberOfSigma(const CollisionType& collision, const TrackType& trk, const o2::track::PID::ID id) const; @@ -86,7 +87,7 @@ class Response float GetNumberOfSigmaMCTuned(const CollisionType& collision, const TrackType& trk, const o2::track::PID::ID id, float mcTunedTPCSignal) const; // Number of sigmas with respect to expected for MC, defining a tune-on-data signal value, explicit multTPC template - float GetNumberOfSigmaMCTunedAtMultiplicity(const long multTPC, const TrackType& trk, const o2::track::PID::ID id, float mcTunedTPCSignal) const; + float GetNumberOfSigmaMCTunedAtMultiplicity(const int64_t multTPC, const TrackType& trk, const o2::track::PID::ID id, float mcTunedTPCSignal) const; /// Gets the deviation to the expected signal template float GetSignalDelta(const TrackType& trk, const o2::track::PID::ID id) const; @@ -99,7 +100,7 @@ class Response private: /// Compute expected sigma given a pre-computed expected signal, avoiding a redundant Bethe-Bloch call. template - float sigmaFromSignal(float expectedSignal, const long multTPC, const TrackType& track, const o2::track::PID::ID id) const; + float sigmaFromSignal(float expectedSignal, const int64_t multTPC, const TrackType& track, const o2::track::PID::ID id) const; std::array mBetheBlochParams = {0.03209809958934784, 19.9768009185791, 2.5266601063857674e-16, 2.7212300300598145, 6.080920219421387}; std::array mResolutionParamsDefault = {0.07, 0.0}; @@ -135,7 +136,7 @@ inline float Response::GetExpectedSigma(const CollisionType& collision, const Tr /// Gets the expected resolution of the measurement template -inline float Response::GetExpectedSigmaAtMultiplicity(const long multTPC, const TrackType& track, const o2::track::PID::ID id) const +inline float Response::GetExpectedSigmaAtMultiplicity(const int64_t multTPC, const TrackType& track, const o2::track::PID::ID id) const { if (!track.hasTPC()) { return -999.f; @@ -144,7 +145,7 @@ inline float Response::GetExpectedSigmaAtMultiplicity(const long multTPC, const } template -inline float Response::sigmaFromSignal(float expectedSignal, const long multTPC, const TrackType& track, const o2::track::PID::ID id) const +inline float Response::sigmaFromSignal(float expectedSignal, const int64_t multTPC, const TrackType& track, const o2::track::PID::ID id) const { float resolution = 0.f; if (mUseDefaultResolutionParam) { @@ -188,7 +189,7 @@ inline float Response::GetNumberOfSigmaMCTuned(const CollisionType& collision, c } template -inline float Response::GetNumberOfSigmaMCTunedAtMultiplicity(const long multTPC, const TrackType& trk, const o2::track::PID::ID id, float mcTunedTPCSignal) const +inline float Response::GetNumberOfSigmaMCTunedAtMultiplicity(const int64_t multTPC, const TrackType& trk, const o2::track::PID::ID id, float mcTunedTPCSignal) const { const float signal = GetExpectedSignal(trk, id); if (signal < 0.f) { diff --git a/Common/Core/RecoDecay.h b/Common/Core/RecoDecay.h index 075a304c2bf..b8e8bedefdd 100644 --- a/Common/Core/RecoDecay.h +++ b/Common/Core/RecoDecay.h @@ -27,6 +27,7 @@ #include // std::abs, std::sqrt #include // std::size_t #include // intX_t +#include // std::iota #include // std::apply #include // std::decay_t #include // std::move @@ -49,6 +50,10 @@ struct RecoDecay { }; static constexpr int8_t StatusCodeAfterFlavourOscillation{92}; // decay products after B0(s) flavour oscillation + static constexpr int8_t StatusCodePrimaryMin{81}; // primary particles (after hadronization) have status code >= 81 + static constexpr int8_t StatusCodePrimaryMax{89}; // primary particles (after hadronization) have status code <= 89 + static constexpr int8_t StatusCodeRhadronMin{101}; // R-hadrons (after hadronization) have status code >= 101 + static constexpr int8_t StatusCodeRhadronMax{109}; // R-hadrons (after hadronization) have status code <= 109 static constexpr int PdgQuarkMax{8}; // largest quark PDG code; o2-linter: disable=pdg/explicit-code (t' does not have a named constant.) static constexpr int PdgBosonMin{PDG_t::kGluon}; // smallest boson (gauge or H) PDG code static constexpr int PdgBosonMax{37}; // largest boson (gauge or H) PDG code; o2-linter: disable=pdg/explicit-code (H+ does not have a named constant.) @@ -561,6 +566,46 @@ struct RecoDecay { return maxNormDeltaIP; } + template + static void getOrderedMotherIds(const PPart& particle, + std::vector& motherIdsOrdered, + const bool searchUpToQuark = false) + { + if (!particle.has_mothers()) { + return; + } + + if (particle.mothersIds().front() == particle.globalIndex()) { + return; + } + + if (!searchUpToQuark) { + motherIdsOrdered.push_back(particle.mothersIds().front()); + } else { + if (particle.mothersIds().size() == 2) { + if (particle.mothersIds().front() == particle.mothersIds().back()) { + motherIdsOrdered.push_back(particle.mothersIds().front()); + } else if (particle.mothersIds().front() < particle.mothersIds().back()) { + auto absStatusCode = std::abs(particle.getGenStatusCode()); + if ((absStatusCode >= StatusCodePrimaryMin && absStatusCode <= StatusCodePrimaryMax) || (absStatusCode >= StatusCodeRhadronMin && absStatusCode <= StatusCodeRhadronMax)) { + motherIdsOrdered.resize(particle.mothersIds().back() - particle.mothersIds().front() + 1); + std::iota(motherIdsOrdered.begin(), motherIdsOrdered.end(), particle.mothersIds().front()); + } else { + motherIdsOrdered.push_back(particle.mothersIds().front()); + motherIdsOrdered.push_back(particle.mothersIds().back()); + } + } else if (particle.mothersIds().front() > particle.mothersIds().back()) { + if (particle.mothersIds().back() != 0) { + motherIdsOrdered.push_back(particle.mothersIds().back()); + } + motherIdsOrdered.push_back(particle.mothersIds().front()); + } + } else if (particle.mothersIds().size() == 1) { + motherIdsOrdered.push_back(particle.mothersIds().front()); + } + } + } + /// Finds the mother of an MC particle by looking for the expected PDG code in the mother chain. /// \tparam acceptFlavourOscillation switch to accept decays where the mother oscillated (e.g. B0 -> B0bar) /// \param particlesMC table with MC particles @@ -599,10 +644,12 @@ struct RecoDecay { for (auto iPart : arrayIds[-stage]) { // check all the particles that were the mothers at the previous stage, o2-linter: disable=const-ref-in-for-loop (int elements) auto particleMother = particlesMC.rawIteratorAt(iPart - particlesMC.offset()); if (particleMother.has_mothers()) { - // If searchUpToQuark is false we only take the first mother (since decay products only have one mother) - auto lastMotherIdxToCheck = searchUpToQuark ? particleMother.mothersIds().back() : particleMother.mothersIds().front(); - for (auto iMother = particleMother.mothersIds().front(); iMother <= lastMotherIdxToCheck; ++iMother) { // loop over the mother particles of the analysed particle - if (std::find(arrayIdsStage.begin(), arrayIdsStage.end(), iMother) != arrayIdsStage.end()) { // if a mother is still present in the vector, do not check it again + + std::vector motherIdsOrdered{}; + getOrderedMotherIds(particleMother, motherIdsOrdered, searchUpToQuark); + + for (auto const& iMother : motherIdsOrdered) { // loop over the mother particles of the analysed particle + if (std::find(arrayIdsStage.begin(), arrayIdsStage.end(), iMother) != arrayIdsStage.end()) { // if a mother is still present in the vector, do not check it again continue; } auto mother = particlesMC.rawIteratorAt(iMother - particlesMC.offset()); @@ -1059,6 +1106,7 @@ struct RecoDecay { while (arrayIds[-stage].size() > 0) { // vector of mother indices for the current stage std::vector arrayIdsStage{}; + std::vector motherIdsOrdered{}; for (auto iPart : arrayIds[-stage]) { // check all the particles that were the mothers at the previous stage, o2-linter: disable=const-ref-in-for-loop (int elements) auto particleMother = particlesMC.rawIteratorAt(iPart - particlesMC.offset()); if (particleMother.has_mothers()) { @@ -1072,8 +1120,9 @@ struct RecoDecay { } } - for (auto iMother = particleMother.mothersIds().front(); iMother <= particleMother.mothersIds().back(); ++iMother) { // loop over the mother particles of the analysed particle - if (std::find(arrayIdsStage.begin(), arrayIdsStage.end(), iMother) != arrayIdsStage.end()) { // if a mother is still present in the vector, do not check it again + getOrderedMotherIds(particleMother, motherIdsOrdered, searchUpToQuark); + for (auto const& iMother : motherIdsOrdered) { // loop over the mother particles of the analysed particle + if (std::find(arrayIdsStage.begin(), arrayIdsStage.end(), iMother) != arrayIdsStage.end()) { // if a mother is still present in the vector, do not check it again continue; } auto mother = particlesMC.rawIteratorAt(iMother - particlesMC.offset()); @@ -1161,30 +1210,11 @@ struct RecoDecay { auto particleMother = particlesMC.rawIteratorAt(iPart - particlesMC.offset()); if (particleMother.has_mothers()) { - // we break immediately if searchUpToQuark is false and the first mother is a quark or a boson (a hadron should never be the mother of a parton) - if (!searchUpToQuark) { - auto mother = particlesMC.rawIteratorAt(particleMother.mothersIds().front() - particlesMC.offset()); - auto pdgParticleIMother = std::abs(mother.pdgCode()); // PDG code of the mother - if (pdgParticleIMother <= PdgQuarkMax || (pdgParticleIMother >= PdgBosonMin && pdgParticleIMother <= PdgBosonMax)) { - // auto PDGPaticle = std::abs(particleMother.pdgCode()); - if ( - (pdgParticle / PdgDivisorMeson == PDG_t::kBottom || // b mesons - pdgParticle / PdgDivisorBaryon == PDG_t::kBottom) // b baryons - ) { - return OriginType::NonPrompt; // beauty - } - if ( - (pdgParticle / PdgDivisorMeson == PDG_t::kCharm || // c mesons - pdgParticle / PdgDivisorBaryon == PDG_t::kCharm) // c baryons - ) { - return OriginType::Prompt; // charm - } - break; - } - } + std::vector motherIdsOrdered{}; + getOrderedMotherIds(particleMother, motherIdsOrdered, searchUpToQuark); - for (auto iMother = particleMother.mothersIds().front(); iMother <= particleMother.mothersIds().back(); ++iMother) { // loop over the mother particles of the analysed particle - if (std::find(arrayIdsStage.begin(), arrayIdsStage.end(), iMother) != arrayIdsStage.end()) { // if a mother is still present in the vector, do not check it again + for (auto const& iMother : motherIdsOrdered) { // loop over the mother particles of the analysed particle + if (std::find(arrayIdsStage.begin(), arrayIdsStage.end(), iMother) != arrayIdsStage.end()) { // if a mother is still present in the vector, do not check it again continue; } auto mother = particlesMC.rawIteratorAt(iMother - particlesMC.offset()); diff --git a/Common/Core/TrackSelection.cxx b/Common/Core/TrackSelection.cxx index 97de07b2bd0..5ee1ce57b55 100644 --- a/Common/Core/TrackSelection.cxx +++ b/Common/Core/TrackSelection.cxx @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -37,7 +38,7 @@ bool TrackSelection::FulfillsITSHitRequirements(uint8_t itsClusterMap) const return true; } -const std::string TrackSelection::mCutNames[static_cast(TrackSelection::TrackCuts::kNCuts)] = {"TrackType", "PtRange", "EtaRange", "TPCNCls", "TPCCrossedRows", "TPCCrossedRowsOverNCls", "TPCChi2NDF", "TPCRefit", "ITSNCls", "ITSChi2NDF", "ITSRefit", "ITSHits", "GoldenChi2", "DCAxy", "DCAz", "TPCFracSharedCls"}; +const std::array(TrackSelection::TrackCuts::kNCuts)> TrackSelection::mCutNames{"TrackType", "PtRange", "EtaRange", "TPCNCls", "TPCCrossedRows", "TPCCrossedRowsOverNCls", "TPCChi2NDF", "TPCRefit", "ITSNCls", "ITSChi2NDF", "ITSRefit", "ITSHits", "GoldenChi2", "DCAxy", "DCAz", "TPCFracSharedCls"}; void TrackSelection::SetTrackType(o2::aod::track::TrackTypeEnum trackType) { @@ -149,7 +150,7 @@ void TrackSelection::print() const for (int i = 0; i < static_cast(TrackCuts::kNCuts); i++) { switch (static_cast(i)) { case TrackCuts::kTrackType: - LOG(info) << mCutNames[i].data() << " == " << static_cast(mTrackType); + LOG(info) << mCutNames[i] << " == " << static_cast(mTrackType); break; case TrackCuts::kPtRange: LOG(info) << mCutNames[i] << " in [" << mMinPt << ", " << mMaxPt << "]"; diff --git a/Common/Core/TrackSelection.h b/Common/Core/TrackSelection.h index 80e676c625c..ff0c92b9baf 100644 --- a/Common/Core/TrackSelection.h +++ b/Common/Core/TrackSelection.h @@ -20,6 +20,7 @@ #include +#include #include #include #include @@ -68,7 +69,7 @@ class TrackSelection ppPass3 }; - static const std::string mCutNames[static_cast(TrackCuts::kNCuts)]; + static const std::array(TrackCuts::kNCuts)> mCutNames; // True for Run 2 track types. Hoisted out of the per-cut IsSelected() so that // the trackType() column is dereferenced once per track instead of once per cut. diff --git a/Common/Core/Zorro.cxx b/Common/Core/Zorro.cxx index 6eb0b61c118..f110b8a9a85 100644 --- a/Common/Core/Zorro.cxx +++ b/Common/Core/Zorro.cxx @@ -72,7 +72,7 @@ void Zorro::populateHistRegistry(o2::framework::HistogramRegistry& histRegistry, for (int iBin{2}; iBin < mSelections->GetNbinsX(); ++iBin) { // Exclude first and last bins as they are total number of analysed and selected events, respectively mAnalysedTriggers->GetXaxis()->SetBinLabel(iBin - 1, mSelections->GetXaxis()->GetBinLabel(iBin)); } - std::shared_ptr selections = histRegistry.add((folderName + "/" + std::to_string(runNumber) + "/" + "Selections").data(), "", o2::framework::HistType::kTH1D, {{mSelections->GetNbinsX(), -0.5, static_cast(mSelections->GetNbinsX() - 0.5)}}); + std::shared_ptr selections = histRegistry.add((folderName + "/" + std::to_string(runNumber) + "/" + "Selections").data(), "", o2::framework::HistType::kTH1D, {{mSelections->GetNbinsX(), -0.5, (mSelections->GetNbinsX() - 0.5)}}); selections->SetBit(TH1::kIsAverage); for (int iBin{1}; iBin <= mSelections->GetNbinsX(); ++iBin) { selections->GetXaxis()->SetBinLabel(iBin, mSelections->GetXaxis()->GetBinLabel(iBin)); @@ -81,7 +81,7 @@ void Zorro::populateHistRegistry(o2::framework::HistogramRegistry& histRegistry, } } if (mScalers) { - std::shared_ptr scalers = histRegistry.add((folderName + "/" + std::to_string(runNumber) + "/" + "Scalers").data(), "", o2::framework::HistType::kTH1D, {{mScalers->GetNbinsX(), -0.5, static_cast(mScalers->GetNbinsX() - 0.5)}}); + std::shared_ptr scalers = histRegistry.add((folderName + "/" + std::to_string(runNumber) + "/" + "Scalers").data(), "", o2::framework::HistType::kTH1D, {{mScalers->GetNbinsX(), -0.5, (mScalers->GetNbinsX() - 0.5)}}); scalers->SetBit(TH1::kIsAverage); for (int iBin{1}; iBin <= mScalers->GetNbinsX(); ++iBin) { scalers->GetXaxis()->SetBinLabel(iBin, mScalers->GetXaxis()->GetBinLabel(iBin)); @@ -90,7 +90,7 @@ void Zorro::populateHistRegistry(o2::framework::HistogramRegistry& histRegistry, } } if (mInspectedTVX) { - std::shared_ptr inspectedTVX = histRegistry.add((folderName + "/" + std::to_string(runNumber) + "/" + "InspectedTVX").data(), "", o2::framework::HistType::kTH1D, {{mInspectedTVX->GetNbinsX(), -0.5, static_cast(mInspectedTVX->GetNbinsX() - 0.5)}}); + std::shared_ptr inspectedTVX = histRegistry.add((folderName + "/" + std::to_string(runNumber) + "/" + "InspectedTVX").data(), "", o2::framework::HistType::kTH1D, {{mInspectedTVX->GetNbinsX(), -0.5, (mInspectedTVX->GetNbinsX() - 0.5)}}); inspectedTVX->SetBit(TH1::kIsAverage); for (int iBin{1}; iBin <= mInspectedTVX->GetNbinsX(); ++iBin) { inspectedTVX->GetXaxis()->SetBinLabel(iBin, mInspectedTVX->GetXaxis()->GetBinLabel(iBin)); @@ -99,7 +99,7 @@ void Zorro::populateHistRegistry(o2::framework::HistogramRegistry& histRegistry, } } if (mTOIs.size()) { - mAnalysedTriggersOfInterest = histRegistry.add((folderName + "/" + std::to_string(runNumber) + "/" + "AnalysedTriggersOfInterest").data(), "", o2::framework::HistType::kTH1D, {{static_cast(mTOIs.size()), -0.5, static_cast(mTOIs.size() - 0.5)}}).get(); + mAnalysedTriggersOfInterest = histRegistry.add((folderName + "/" + std::to_string(runNumber) + "/" + "AnalysedTriggersOfInterest").data(), "", o2::framework::HistType::kTH1D, {{static_cast(mTOIs.size()), -0.5, (mTOIs.size() - 0.5)}}).get(); for (size_t i{0}; i < mTOIs.size(); ++i) { mAnalysedTriggersOfInterest->GetXaxis()->SetBinLabel(i + 1, mTOIs[i].data()); } diff --git a/Common/Core/ZorroSummary.cxx b/Common/Core/ZorroSummary.cxx index 0a1012c3edc..bb020dc0924 100644 --- a/Common/Core/ZorroSummary.cxx +++ b/Common/Core/ZorroSummary.cxx @@ -20,7 +20,7 @@ void ZorroSummary::Copy(TObject& c) const { - static_cast(c) = *this; + dynamic_cast(c) = *this; } Long64_t ZorroSummary::Merge(TCollection* list) diff --git a/Common/Core/macros/testCollisionTypeHelper.C b/Common/Core/macros/testCollisionTypeHelper.C index f84970a46a3..345fc01ceaa 100644 --- a/Common/Core/macros/testCollisionTypeHelper.C +++ b/Common/Core/macros/testCollisionTypeHelper.C @@ -26,5 +26,5 @@ void testCollisionTypeHelper(int runNumber = 544124) o2::parameters::GRPLHCIFData* grpo = ccdb.getSpecificForRun("GLO/Config/GRPLHCIF", runNumber); grpo->print(); - int collsys = o2::common::core::CollisionSystemType::getCollisionTypeFromGrp(grpo); + [[maybe_unused]] int collsys = o2::common::core::CollisionSystemType::getCollisionTypeFromGrp(grpo); } diff --git a/Common/Core/macros/testMetadataHelper.C b/Common/Core/macros/testMetadataHelper.C index dd20cba9c44..12b29935455 100644 --- a/Common/Core/macros/testMetadataHelper.C +++ b/Common/Core/macros/testMetadataHelper.C @@ -45,7 +45,7 @@ auto readMetadata(std::unique_ptr& currentFile) -> std::vector { // Get the metadata, if any - auto m = (TMap*)currentFile->Get("metaData"); + auto m = dynamic_cast(currentFile->Get("metaData")); if (!m) { return {}; } @@ -59,7 +59,7 @@ auto readMetadata(std::unique_ptr& currentFile) -> std::vectorGetName()); first = false; } - auto objString = (TObjString*)m->GetValue(obj); + auto objString = dynamic_cast(m->GetValue(obj)); std::string key = "aod-metadata-" + std::string(obj->GetName()); LOGP(info, "- {}: {} goes into key {}", obj->GetName(), objString->String().Data(), key); char const* value = strdup(objString->String()); diff --git a/Common/DataModel/Centrality.h b/Common/DataModel/Centrality.h index 3bdf839eed8..a7f073fde92 100644 --- a/Common/DataModel/Centrality.h +++ b/Common/DataModel/Centrality.h @@ -100,18 +100,22 @@ using BCCentFT0C = BCCentFT0Cs::iterator; template concept HasRun2Centrality = requires(T&& t) { + // NOLINTBEGIN(readability/braces) { t.centRun2V0M() }; { t.centRun2CL0() }; { t.centRun2CL1() }; + // NOLINTEND(readability/braces) }; template concept HasCentrality = requires(T&& t) { + // NOLINTBEGIN(readability/braces) { t.centFV0A() }; { t.centFT0M() }; { t.centFT0A() }; { t.centFT0C() }; { t.centNTPV() }; + // NOLINTEND(readability/braces) }; } // namespace o2::aod diff --git a/Common/DataModel/GloCCDBObjects.h b/Common/DataModel/GloCCDBObjects.h index 4612f8ba02a..21b8bb7d610 100644 --- a/Common/DataModel/GloCCDBObjects.h +++ b/Common/DataModel/GloCCDBObjects.h @@ -59,6 +59,9 @@ #include #include +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) + #include #include #include diff --git a/Common/DataModel/Multiplicity.h b/Common/DataModel/Multiplicity.h index c258fd0c426..df6dee0520e 100644 --- a/Common/DataModel/Multiplicity.h +++ b/Common/DataModel/Multiplicity.h @@ -103,6 +103,7 @@ DECLARE_SOA_COLUMN(TimeToNeNext, timeToNeNext, float); //! DECLARE_SOA_COLUMN(FT0TriggerMask, ft0TriggerMask, uint8_t); //! DECLARE_SOA_COLUMN(MultFV0AOuter, multFV0AOuter, float); //! FV0 without innermost ring DECLARE_SOA_COLUMN(MultFT0AOuter, multFT0AOuter, float); //! FT0A without innermost ring +DECLARE_SOA_COLUMN(MultFT0COuter, multFT0COuter, float); //! FT0C without innermost ring // Timing information DECLARE_SOA_COLUMN(TimeZNA, timeZNA, float); //! @@ -144,7 +145,7 @@ DECLARE_SOA_TABLE(MFTMults, "AOD", "MFTMULT", //! Multiplicity with MFT DECLARE_SOA_TABLE(FITExtraMults, "AOD", "FITEXTRAMULT", //! Extra information from FIT detectors mult::MultFV0AOuter, mult::MultFT0AOuter, - mult::FT0TriggerMask); + mult::MultFT0COuter); using BarrelMults = soa::Join; using Mults = soa::Join; @@ -216,6 +217,8 @@ DECLARE_SOA_TABLE_VERSIONED(MultMCExtras_001, "AOD", "MULTMCEXTRA", 1, //! Table using MultMCExtras = MultMCExtras_001; using MultMCExtra = MultMCExtras::iterator; using MultsExtraMC = MultMCExtras; // for backwards compatibility with previous naming scheme +template +using MultsExtraMCFrom = MultMCExtras_001From; // crosslinks namespace mult @@ -311,6 +314,22 @@ DECLARE_SOA_TABLE_VERSIONED(MultBCs_001, "AOD", "MULTBC", 1, //! mult::MultFV0AOuter, mult::MultFT0AOuter); +DECLARE_SOA_TABLE_VERSIONED(MultBCs_002, "AOD", "MULTBC", 2, //! + mult::MultFT0A, + mult::MultFT0C, + mult::MultFV0A, + mult::MultFDDA, + mult::MultFDDC, + mult::MultZNA, + mult::MultZNC, + mult::MultZEM1, + mult::MultZEM2, + mult::MultZPA, + mult::MultZPC, + mult::MultFV0AOuter, + mult::MultFT0AOuter, + mult::MultFT0COuter); + DECLARE_SOA_TABLE(MultBcSel_000, "AOD", "MULTBCSEL", //! BC selection bits joinable with multBCs evsel::Selection); @@ -339,7 +358,7 @@ DECLARE_SOA_TABLE(TimeBCs, "AOD", "TIMEBC", //! mult::TimeZPA, mult::TimeZPC) -using MultBCs = MultBCs_001; +using MultBCs = MultBCs_002; using MultBcSel = MultBcSel_001; using MultBC = MultBCs::iterator; diff --git a/Common/DataModel/TpcCCDBObjects.h b/Common/DataModel/TpcCCDBObjects.h index 19b455e0234..9449946ff6c 100644 --- a/Common/DataModel/TpcCCDBObjects.h +++ b/Common/DataModel/TpcCCDBObjects.h @@ -29,6 +29,9 @@ #include #include +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) + namespace o2::aod { namespace ccdbTpc diff --git a/Common/DataModel/TrackTunerCCDBObjects.h b/Common/DataModel/TrackTunerCCDBObjects.h index 2e4817efff3..5c27ea2f9eb 100644 --- a/Common/DataModel/TrackTunerCCDBObjects.h +++ b/Common/DataModel/TrackTunerCCDBObjects.h @@ -28,6 +28,8 @@ #include #include +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) #include namespace o2::aod diff --git a/Common/DataModel/ZDCLightIons.h b/Common/DataModel/ZDCLightIons.h index b0e8e247622..a3d6db2b207 100644 --- a/Common/DataModel/ZDCLightIons.h +++ b/Common/DataModel/ZDCLightIons.h @@ -56,7 +56,8 @@ DECLARE_SOA_COLUMN(CentralityFt0c, centralityFt0c, float); //! Centrality DECLARE_SOA_COLUMN(CentralityFt0a, centralityFt0a, float); //! Centrality DECLARE_SOA_COLUMN(CentralityFt0m, centralityFt0m, float); //! Centrality DECLARE_SOA_COLUMN(Timestamp, timestamp, uint64_t); //! Timestamp -DECLARE_SOA_COLUMN(SelectionBits, selectionBits, uint8_t); //! Selection Flags +DECLARE_SOA_COLUMN(SelectionBits, selectionBits, uint8_t); //! Selection flags x collisions +DECLARE_SOA_COLUMN(BcMask, bcMask, uint8_t); //! Selection flags x BC } // namespace zdclightions DECLARE_SOA_TABLE(ZDCLightIons, "AOD", "ZDCTABLELIGHTIONS", @@ -92,7 +93,8 @@ DECLARE_SOA_TABLE(ZDCLightIons, "AOD", "ZDCTABLELIGHTIONS", zdclightions::CentralityFt0a, zdclightions::CentralityFt0m, zdclightions::Timestamp, - zdclightions::SelectionBits); + zdclightions::SelectionBits, + zdclightions::BcMask); } // namespace o2::aod #endif // COMMON_DATAMODEL_ZDCLIGHTIONS_H_ diff --git a/Common/TableProducer/Converters/CMakeLists.txt b/Common/TableProducer/Converters/CMakeLists.txt index a8b14efaca3..600dfea3d9b 100644 --- a/Common/TableProducer/Converters/CMakeLists.txt +++ b/Common/TableProducer/Converters/CMakeLists.txt @@ -118,3 +118,8 @@ o2physics_add_dpl_workflow(mult-bcs-converter SOURCES multBCsConverter.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(mult-bcs-002-converter + SOURCES multBCs002Converter.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/Common/TableProducer/Converters/multBCs002Converter.cxx b/Common/TableProducer/Converters/multBCs002Converter.cxx new file mode 100644 index 00000000000..dfed7182c1a --- /dev/null +++ b/Common/TableProducer/Converters/multBCs002Converter.cxx @@ -0,0 +1,51 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file multBCs002Converter.cxx +/// \brief Converts MultBCs table from version 001 to 002 +/// \author Jesper Karlsson Gumrpecht + +#include "Common/DataModel/Multiplicity.h" + +#include +#include +#include +#include + +struct MultBCs002Converter { + o2::framework::Produces multBC; + static constexpr float DummyValue = -1.f; + void process(o2::aod::MultBCs_001 const& multBCs) + { + multBC.reserve(multBCs.size()); + for (const auto& multbc : multBCs) { + multBC(multbc.multFT0A(), + multbc.multFT0C(), + multbc.multFV0A(), + multbc.multFDDA(), + multbc.multFDDC(), + multbc.multZNA(), + multbc.multZNC(), + multbc.multZEM1(), + multbc.multZEM2(), + multbc.multZPA(), + multbc.multZPC(), + multbc.multFV0AOuter(), + multbc.multFT0AOuter(), + DummyValue /* dummy amplitude for FT0C Outer */); + } + } +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& cfgc) +{ + return o2::framework::WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/Common/TableProducer/PID/pidTOFBase.cxx b/Common/TableProducer/PID/pidTOFBase.cxx index 2ef09079f5e..733da06b1ea 100644 --- a/Common/TableProducer/PID/pidTOFBase.cxx +++ b/Common/TableProducer/PID/pidTOFBase.cxx @@ -376,7 +376,7 @@ struct tofEventTime { tableFlags(0); tableEvTime(0.f, 999.f); if (enableTableTOFOnly) { - tableEvTimeTOFOnly((uint8_t)0, 0.f, 0.f, -1); + tableEvTimeTOFOnly(static_cast(0), 0.f, 0.f, -1); } continue; } @@ -408,7 +408,7 @@ struct tofEventTime { tableFlags(flags); tableEvTime(et, erret); if (enableTableTOFOnly) { - tableEvTimeTOFOnly((uint8_t)filterForTOFEventTime(trk), et, erret, evTimeTOF.mEventTimeMultiplicity); + tableEvTimeTOFOnly(static_cast(filterForTOFEventTime(trk)), et, erret, evTimeTOF.mEventTimeMultiplicity); } } } @@ -438,7 +438,7 @@ struct tofEventTime { tableFlags(0); tableEvTime(0.f, 999.f); if (enableTableTOFOnly) { - tableEvTimeTOFOnly((uint8_t)0, 0.f, 0.f, -1); + tableEvTimeTOFOnly(static_cast(0), 0.f, 0.f, -1); } continue; } @@ -469,7 +469,7 @@ struct tofEventTime { // Reset the event time eventTime = 0.f; sumOfWeights = 0.f; - weight = 0.f; + // weight = 0.f; // FIXME: unused // Remove the bias on TOF ev. time if constexpr (removeTOFEvTimeBias) { evTimeTOF.removeBias(trk, nGoodTracksForTOF, t0TOF[0], t0TOF[1], 2); @@ -504,7 +504,7 @@ struct tofEventTime { } tableEvTime(eventTime / sumOfWeights, std::sqrt(1. / sumOfWeights)); if (enableTableTOFOnly) { - tableEvTimeTOFOnly((uint8_t)filterForTOFEventTime(trk), t0TOF[0], t0TOF[1], evTimeTOF.mEventTimeMultiplicity); + tableEvTimeTOFOnly(static_cast(filterForTOFEventTime(trk)), t0TOF[0], t0TOF[1], evTimeTOF.mEventTimeMultiplicity); } } } @@ -529,7 +529,7 @@ struct tofEventTime { for (auto const& t : tracks) { // Loop on collisions if (enableTableTOFOnly) { - tableEvTimeTOFOnly((uint8_t)0, 0.f, 0.f, -1); + tableEvTimeTOFOnly(static_cast(0), 0.f, 0.f, -1); } if (!t.has_collision() || collisions.size() == 0) { // Track was not assigned, cannot compute event time tableFlags(0); diff --git a/Common/TableProducer/PID/pidTOFMerge.cxx b/Common/TableProducer/PID/pidTOFMerge.cxx index f5ec6e68a6f..337e828630c 100644 --- a/Common/TableProducer/PID/pidTOFMerge.cxx +++ b/Common/TableProducer/PID/pidTOFMerge.cxx @@ -405,7 +405,7 @@ struct tofEventTime { tableFlags(0); tableEvTime(0.f, 999.f); if (enableTableEvTimeTOFOnly) { - tableEvTimeTOFOnly((uint8_t)0, 0.f, 0.f, -1); + tableEvTimeTOFOnly(static_cast(0), 0.f, 0.f, -1); } continue; } @@ -435,7 +435,7 @@ struct tofEventTime { // Reset the event time eventTime = 0.f; sumOfWeights = 0.f; - weight = 0.f; + // weight = 0.f; // FIXME: unused // Remove the bias on TOF ev. time if constexpr (kRemoveTOFEvTimeBias) { evTimeMakerTOF.removeBias(trk, nGoodTracksForTOF, t0TOF[0], t0TOF[1], 2); @@ -470,7 +470,7 @@ struct tofEventTime { } tableEvTime(eventTime / sumOfWeights, std::sqrt(1. / sumOfWeights)); if (enableTableEvTimeTOFOnly) { - tableEvTimeTOFOnly((uint8_t)filterForTOFEventTime(trk), t0TOF[0], t0TOF[1], evTimeMakerTOF.mEventTimeMultiplicity); + tableEvTimeTOFOnly(static_cast(filterForTOFEventTime(trk)), t0TOF[0], t0TOF[1], evTimeMakerTOF.mEventTimeMultiplicity); } } } @@ -481,7 +481,7 @@ struct tofEventTime { tableFlags(0); tableEvTime(0.f, 999.f); if (enableTableEvTimeTOFOnly) { - tableEvTimeTOFOnly((uint8_t)0, 0.f, 0.f, -1); + tableEvTimeTOFOnly(static_cast(0), 0.f, 0.f, -1); } continue; } @@ -513,14 +513,14 @@ struct tofEventTime { tableFlags(flags); tableEvTime(et, erret); if (enableTableEvTimeTOFOnly) { - tableEvTimeTOFOnly((uint8_t)filterForTOFEventTime(trk), et, erret, evTimeMakerTOF.mEventTimeMultiplicity); + tableEvTimeTOFOnly(static_cast(filterForTOFEventTime(trk)), et, erret, evTimeMakerTOF.mEventTimeMultiplicity); } } } } else if (mComputeEvTimeWithTOF == 0 && mComputeEvTimeWithFT0 == 1) { for (auto const& t : tracks) { // Loop on collisions if (enableTableEvTimeTOFOnly) { - tableEvTimeTOFOnly((uint8_t)0, 0.f, 0.f, -1); + tableEvTimeTOFOnly(static_cast(0), 0.f, 0.f, -1); } if (!t.has_collision() || collisions.size() == 0) { // Track was not assigned, cannot compute event time tableFlags(0); diff --git a/Common/TableProducer/PID/pidTPCBase.cxx b/Common/TableProducer/PID/pidTPCBase.cxx index 1a86f0f70ec..422a6d7c435 100644 --- a/Common/TableProducer/PID/pidTPCBase.cxx +++ b/Common/TableProducer/PID/pidTPCBase.cxx @@ -61,7 +61,7 @@ struct PidMultiplicity { } using TrksIU = soa::Join; - Partition tracksWithTPCIU = (aod::track::tpcNClsFindable > (uint8_t)0); + Partition tracksWithTPCIU = (aod::track::tpcNClsFindable > static_cast(0)); void processIU(aod::Collision const& collision, TrksIU const&) { if (!enableTable) { @@ -73,7 +73,7 @@ struct PidMultiplicity { PROCESS_SWITCH(PidMultiplicity, processIU, "Process with IU tracks, faster but works on Run3 only", false); using Trks = soa::Join; - Partition tracksWithTPC = (aod::track::tpcNClsFindable > (uint8_t)0); + Partition tracksWithTPC = (aod::track::tpcNClsFindable > static_cast(0)); void processStandard(aod::Collision const& collision, Trks const&) { if (!enableTable) { diff --git a/Common/TableProducer/caloClusterProducer.cxx b/Common/TableProducer/caloClusterProducer.cxx index 5227b133335..c7707eb3652 100644 --- a/Common/TableProducer/caloClusterProducer.cxx +++ b/Common/TableProducer/caloClusterProducer.cxx @@ -1116,9 +1116,9 @@ struct CaloClusterProducer { if (cpvdist != 99.) { // was evaluated cpvdist = std::sqrt(cpvdist); // was squared } - if (trackdist != 99.) { // was evaluated - trackdist = std::sqrt(trackdist); // was squared - } + // if (trackdist != 99.) { // was evaluated + // trackdist = std::sqrt(trackdist); // was squared // FIXME: unused + // } float lambdaShort = 0., lambdaLong = 0.; clu.getElipsAxis(lambdaShort, lambdaLong); @@ -1544,9 +1544,9 @@ struct CaloClusterProducer { if (cpvdist != 99.) { // was evaluated cpvdist = std::sqrt(cpvdist); // was squared } - if (trackdist != 99.) { // was evaluated - trackdist = std::sqrt(trackdist); // was squared - } + // if (trackdist != 99.) { // was evaluated + // trackdist = std::sqrt(trackdist); // was squared // FIXME: unused + // } float lambdaShort = 0., lambdaLong = 0.; clu.getElipsAxis(lambdaShort, lambdaLong); diff --git a/Common/TableProducer/match-mft-ft0.cxx b/Common/TableProducer/match-mft-ft0.cxx index 43d43ebab84..05bd98f6952 100644 --- a/Common/TableProducer/match-mft-ft0.cxx +++ b/Common/TableProducer/match-mft-ft0.cxx @@ -147,8 +147,8 @@ T getCompatibleBCs(aod::MFTTracks::iterator const& track, aod::Collision const& int64_t minBCId = bcIt.globalIndex(); - if ((int64_t)bcIt.globalBC() < firstBC + deltaBC) { - while (bcIt != bcs.end() && (int64_t)bcIt.globalBC() < firstBC + deltaBC) { + if (static_cast(bcIt.globalBC()) < firstBC + deltaBC) { + while (bcIt != bcs.end() && static_cast(bcIt.globalBC()) < firstBC + deltaBC) { minBCId = bcIt.globalIndex(); ++bcIt; @@ -163,11 +163,11 @@ T getCompatibleBCs(aod::MFTTracks::iterator const& track, aod::Collision const& } else { // here bcIt.globalBC() >= firstBC + deltaBC - while (bcIt != bcs.begin() && (int64_t)bcIt.globalBC() >= (int64_t)firstBC + deltaBC) { + while (bcIt != bcs.begin() && static_cast(bcIt.globalBC()) >= firstBC + deltaBC) { minBCId = bcIt.globalIndex(); --bcIt; } - if (bcIt == bcs.begin() && (int64_t)bcIt.globalBC() >= (int64_t)firstBC + deltaBC) { + if (bcIt == bcs.begin() && static_cast(bcIt.globalBC()) >= firstBC + deltaBC) { minBCId = bcIt.globalIndex(); } ++bcIt; // retrieve the pointer which gave minBCId and minGlobalBC @@ -178,12 +178,12 @@ T getCompatibleBCs(aod::MFTTracks::iterator const& track, aod::Collision const& int64_t maxBCId = bcIt.globalIndex(); - if ((int64_t)bcIt.globalBC() > (int64_t)lastBC + deltaBC) { + if (static_cast(bcIt.globalBC()) > lastBC + deltaBC) { // the previous minimum is actually bigger than the right boundary if (bcIt != bcs.begin()) { - --bcIt; // let's check the previous element in the BC list - if ((int64_t)bcIt.globalBC() < (int64_t)firstBC + deltaBC) // if this previous element is smaller than the left boundary + --bcIt; // let's check the previous element in the BC list + if (static_cast(bcIt.globalBC()) < firstBC + deltaBC) // if this previous element is smaller than the left boundary { // means that the slice of compatible BCs is empty @@ -192,18 +192,18 @@ T getCompatibleBCs(aod::MFTTracks::iterator const& track, aod::Collision const& } } - if ((int64_t)bcIt.globalBC() < (int64_t)firstBC + deltaBC) { + if (static_cast(bcIt.globalBC()) < firstBC + deltaBC) { // the previous minimum is actually smaller than the right boundary ++bcIt; - if (bcIt != bcs.end() && ((int64_t)bcIt.globalBC() > (int64_t)lastBC + deltaBC)) { + if (bcIt != bcs.end() && (static_cast(bcIt.globalBC()) > lastBC + deltaBC)) { // check the following element return bcs.emptySlice(); } } - while (bcIt != bcs.end() && (int64_t)bcIt.globalBC() <= (int64_t)lastBC + deltaBC) { + while (bcIt != bcs.end() && static_cast(bcIt.globalBC()) <= lastBC + deltaBC) { maxBCId = bcIt.globalIndex(); ++bcIt; @@ -275,7 +275,7 @@ struct matchmftft0 { o2::base::Propagator::initFieldFromGRP(grpmag); // for some reason this is necessary for the next next line runNumber = bc.runNumber(); - o2::field::MagneticField* field = static_cast(TGeoGlobalMagField::Instance()->GetField()); + o2::field::MagneticField* field = dynamic_cast(TGeoGlobalMagField::Instance()->GetField()); Bz = field->getBz(centerMFT); // gives error if the propagator is not initFielded LOG(info) << "The field at the center of the MFT is Bz = " << Bz; @@ -367,7 +367,7 @@ struct matchmftft0 { auto ft0s = bc.ft0s(); i = 0; // reinitialise - D = 0.0; + // D = 0.0; // FIXME: unused minD = 999.9; for (auto const& ft0 : ft0s) { // printf("---------ft0.bcId %d\n", ft0.bcId()); diff --git a/Common/TableProducer/match-mft-mch-data-mc.cxx b/Common/TableProducer/match-mft-mch-data-mc.cxx index 7b3be6fa633..df7a0846fd9 100644 --- a/Common/TableProducer/match-mft-mch-data-mc.cxx +++ b/Common/TableProducer/match-mft-mch-data-mc.cxx @@ -643,7 +643,7 @@ struct match_mft_mch_data_mc { ccdbManager->get(geoPath); } o2::mch::TrackExtrap::setField(); - fieldB = static_cast(TGeoGlobalMagField::Instance()->GetField()); + fieldB = dynamic_cast(TGeoGlobalMagField::Instance()->GetField()); } void init(o2::framework::InitContext&) @@ -703,7 +703,7 @@ struct match_mft_mch_data_mc { tableMatchingParams(matching.getGMPtAtDCA(), matching.getGMEtaAtDCA(), - static_cast(matching.getGMQ()), + matching.getGMQ(), matching.getDpt(), matching.getDx(), matching.getDy(), @@ -732,7 +732,7 @@ struct match_mft_mch_data_mc { tableMixMatchingParams(matching.getGMPtAtDCA(), matching.getGMEtaAtDCA(), - static_cast(matching.getGMQ()), + matching.getGMQ(), matching.getDpt(), matching.getDx(), matching.getDy(), diff --git a/Common/TableProducer/match-mft-mch-data.cxx b/Common/TableProducer/match-mft-mch-data.cxx index 4a6b9be135e..c001315b2cd 100644 --- a/Common/TableProducer/match-mft-mch-data.cxx +++ b/Common/TableProducer/match-mft-mch-data.cxx @@ -643,7 +643,7 @@ struct match_mft_mch_data_mc { ccdbManager->get(geoPath); } o2::mch::TrackExtrap::setField(); - fieldB = static_cast(TGeoGlobalMagField::Instance()->GetField()); + fieldB = dynamic_cast(TGeoGlobalMagField::Instance()->GetField()); } void init(o2::framework::InitContext&) @@ -703,7 +703,7 @@ struct match_mft_mch_data_mc { tableMatchingParams(matching.getGMPtAtDCA(), matching.getGMEtaAtDCA(), - static_cast(matching.getGMQ()), + matching.getGMQ(), matching.getDpt(), matching.getDx(), matching.getDy(), @@ -732,7 +732,7 @@ struct match_mft_mch_data_mc { tableMixMatchingParams(matching.getGMPtAtDCA(), matching.getGMEtaAtDCA(), - static_cast(matching.getGMQ()), + matching.getGMQ(), matching.getDpt(), matching.getDx(), matching.getDy(), diff --git a/Common/TableProducer/multiplicityExtraTable.cxx b/Common/TableProducer/multiplicityExtraTable.cxx index 3211b23a346..19a1280f60c 100644 --- a/Common/TableProducer/multiplicityExtraTable.cxx +++ b/Common/TableProducer/multiplicityExtraTable.cxx @@ -44,7 +44,6 @@ using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; using BCPattern = std::bitset; -const int nBCsPerOrbit = o2::constants::lhc::LHCMaxBunches; struct MultiplicityExtraTable { Produces multBC; @@ -62,6 +61,7 @@ struct MultiplicityExtraTable { Configurable bcDownscaleFactor{"bcDownscaleFactor", 2, "Downscale factor for BC table (0: save nothing, 1: save all)"}; Configurable minFT0CforBCTable{"minFT0CforBCTable", 25.0f, "Minimum FT0C amplitude to fill BC table to reduce data"}; Configurable saveOnlyBCsWithCollisions{"saveOnlyBCsWithCollisions", true, "save only BCs with collisions in them"}; + Configurable requestMinSignalForFT0C{"requestMinSignalForFT0C", true, "save only BCs with minimum FT0C signal"}; Configurable bcTableFloatPrecision{"bcTableFloatPrecision", 0.1, "float precision in bc table for data reduction"}; @@ -126,14 +126,14 @@ struct MultiplicityExtraTable { float multFT0C = 0.f; if (bc.has_ft0()) { auto ft0 = bc.ft0(); - for (const auto amplitude : ft0.amplitudeC()) { + for (const auto amplitude : ft0.amplitudeC()) { // o2-linter: disable=const-ref-in-for-loop (cheap float copy) multFT0C += amplitude; } } else { multFT0C = -999.0f; } - if (multFT0C < minFT0CforBCTable) { + if (requestMinSignalForFT0C && multFT0C < minFT0CforBCTable) { continue; // skip this event } @@ -166,6 +166,7 @@ struct MultiplicityExtraTable { float multFDDA = 0.f; float multFDDC = 0.f; float multFT0AOuter = 0.f; + float multFT0COuter = 0.f; float multFV0AOuter = 0.f; // ZDC amplitudes @@ -196,7 +197,7 @@ struct MultiplicityExtraTable { // initialize - from Arvind newRunNumber = bc.runNumber(); - int localBC = bc.globalBC() % nBCsPerOrbit; + int localBC = bc.globalBC() % o2::constants::lhc::LHCMaxBunches; if (newRunNumber != oldRunNumber) { auto soreor = o2::ccdb::BasicCCDBManager::getRunDuration(ccdbApi, newRunNumber); @@ -224,8 +225,12 @@ struct MultiplicityExtraTable { multFT0AOuter += ft0.amplitudeA()[ii]; } } - for (const auto amplitude : ft0.amplitudeC()) { - multFT0C += amplitude; + for (size_t ii = 0; ii < ft0.amplitudeC().size(); ++ii) { + multFT0C += ft0.amplitudeC()[ii]; + static constexpr int MaxChannelIdInnerRingFT0C = 48; + if (ft0.channelC()[ii] > MaxChannelIdInnerRingFT0C) { + multFT0COuter += ft0.amplitudeC()[ii]; + } } posZFT0 = ft0.posZ(); posZFT0valid = ft0.isValidTime(); @@ -261,10 +266,10 @@ struct MultiplicityExtraTable { std::bitset<8> fFDDTriggers = fdd.triggerMask(); multFDDTriggerBits = static_cast(fFDDTriggers.to_ulong()); - for (const auto amplitude : fdd.chargeA()) { + for (const auto amplitude : fdd.chargeA()) { // o2-linter: disable=const-ref-in-for-loop (cheap float copy) multFDDA += amplitude; } - for (const auto amplitude : fdd.chargeC()) { + for (const auto amplitude : fdd.chargeC()) { // o2-linter: disable=const-ref-in-for-loop (cheap float copy) multFDDC += amplitude; } @@ -313,7 +318,8 @@ struct MultiplicityExtraTable { tru(multZPA), tru(multZPC), tru(multFV0AOuter), - tru(multFT0AOuter)); + tru(multFT0AOuter), + tru(multFT0COuter)); multBcSel( bc.selection_raw(), diff --git a/Common/TableProducer/muonRealignment.cxx b/Common/TableProducer/muonRealignment.cxx index 5b1a597a73c..b0585f8ba9f 100644 --- a/Common/TableProducer/muonRealignment.cxx +++ b/Common/TableProducer/muonRealignment.cxx @@ -289,16 +289,16 @@ struct MuonRealignment { fwdTrkCovRealignInfo.sigPhi = TMath::Sqrt(fwdtrack.getCovariances()(2, 2)); fwdTrkCovRealignInfo.sigTgl = TMath::Sqrt(fwdtrack.getCovariances()(3, 3)); fwdTrkCovRealignInfo.sig1Pt = TMath::Sqrt(fwdtrack.getCovariances()(4, 4)); - fwdTrkCovRealignInfo.rhoXY = (Char_t)(128. * fwdtrack.getCovariances()(0, 1) / (fwdTrkCovRealignInfo.sigX * fwdTrkCovRealignInfo.sigY)); - fwdTrkCovRealignInfo.rhoPhiX = (Char_t)(128. * fwdtrack.getCovariances()(0, 2) / (fwdTrkCovRealignInfo.sigPhi * fwdTrkCovRealignInfo.sigX)); - fwdTrkCovRealignInfo.rhoPhiY = (Char_t)(128. * fwdtrack.getCovariances()(1, 2) / (fwdTrkCovRealignInfo.sigPhi * fwdTrkCovRealignInfo.sigY)); - fwdTrkCovRealignInfo.rhoTglX = (Char_t)(128. * fwdtrack.getCovariances()(0, 3) / (fwdTrkCovRealignInfo.sigTgl * fwdTrkCovRealignInfo.sigX)); - fwdTrkCovRealignInfo.rhoTglY = (Char_t)(128. * fwdtrack.getCovariances()(1, 3) / (fwdTrkCovRealignInfo.sigTgl * fwdTrkCovRealignInfo.sigY)); - fwdTrkCovRealignInfo.rhoTglPhi = (Char_t)(128. * fwdtrack.getCovariances()(2, 3) / (fwdTrkCovRealignInfo.sigTgl * fwdTrkCovRealignInfo.sigPhi)); - fwdTrkCovRealignInfo.rho1PtX = (Char_t)(128. * fwdtrack.getCovariances()(0, 4) / (fwdTrkCovRealignInfo.sig1Pt * fwdTrkCovRealignInfo.sigX)); - fwdTrkCovRealignInfo.rho1PtY = (Char_t)(128. * fwdtrack.getCovariances()(1, 4) / (fwdTrkCovRealignInfo.sig1Pt * fwdTrkCovRealignInfo.sigY)); - fwdTrkCovRealignInfo.rho1PtPhi = (Char_t)(128. * fwdtrack.getCovariances()(2, 4) / (fwdTrkCovRealignInfo.sig1Pt * fwdTrkCovRealignInfo.sigPhi)); - fwdTrkCovRealignInfo.rho1PtTgl = (Char_t)(128. * fwdtrack.getCovariances()(3, 4) / (fwdTrkCovRealignInfo.sig1Pt * fwdTrkCovRealignInfo.sigTgl)); + fwdTrkCovRealignInfo.rhoXY = static_cast(128. * fwdtrack.getCovariances()(0, 1) / (fwdTrkCovRealignInfo.sigX * fwdTrkCovRealignInfo.sigY)); + fwdTrkCovRealignInfo.rhoPhiX = static_cast(128. * fwdtrack.getCovariances()(0, 2) / (fwdTrkCovRealignInfo.sigPhi * fwdTrkCovRealignInfo.sigX)); + fwdTrkCovRealignInfo.rhoPhiY = static_cast(128. * fwdtrack.getCovariances()(1, 2) / (fwdTrkCovRealignInfo.sigPhi * fwdTrkCovRealignInfo.sigY)); + fwdTrkCovRealignInfo.rhoTglX = static_cast(128. * fwdtrack.getCovariances()(0, 3) / (fwdTrkCovRealignInfo.sigTgl * fwdTrkCovRealignInfo.sigX)); + fwdTrkCovRealignInfo.rhoTglY = static_cast(128. * fwdtrack.getCovariances()(1, 3) / (fwdTrkCovRealignInfo.sigTgl * fwdTrkCovRealignInfo.sigY)); + fwdTrkCovRealignInfo.rhoTglPhi = static_cast(128. * fwdtrack.getCovariances()(2, 3) / (fwdTrkCovRealignInfo.sigTgl * fwdTrkCovRealignInfo.sigPhi)); + fwdTrkCovRealignInfo.rho1PtX = static_cast(128. * fwdtrack.getCovariances()(0, 4) / (fwdTrkCovRealignInfo.sig1Pt * fwdTrkCovRealignInfo.sigX)); + fwdTrkCovRealignInfo.rho1PtY = static_cast(128. * fwdtrack.getCovariances()(1, 4) / (fwdTrkCovRealignInfo.sig1Pt * fwdTrkCovRealignInfo.sigY)); + fwdTrkCovRealignInfo.rho1PtPhi = static_cast(128. * fwdtrack.getCovariances()(2, 4) / (fwdTrkCovRealignInfo.sig1Pt * fwdTrkCovRealignInfo.sigPhi)); + fwdTrkCovRealignInfo.rho1PtTgl = static_cast(128. * fwdtrack.getCovariances()(3, 4) / (fwdTrkCovRealignInfo.sig1Pt * fwdTrkCovRealignInfo.sigTgl)); LOGF(debug, "TrackParm %d, x:%g y:%g z:%g phi:%g tgl:%g InvQPt:%g chi2:%g nClusters:%d", muon.globalIndex(), muon.x(), muon.y(), muon.z(), muon.phi(), muon.tgl(), muon.signed1Pt(), muon.chi2(), muon.nClusters()); LOGF(debug, "Re-aligned trackParm %d, x:%g y:%g z:%g phi:%g tgl:%g InvQPt:%g chi2:%g nClusters:%d removable:%d", muonRealignId, fwdtrack.getX(), fwdtrack.getY(), fwdtrack.getZ(), fwdtrack.getPhi(), fwdtrack.getTgl(), fwdtrack.getInvQPt(), fwdtrack.getTrackChi2(), convertedTrack.getNClusters(), removable); // Fill refitted track info diff --git a/Common/TableProducer/propagationServiceV2.cxx b/Common/TableProducer/propagationServiceV2.cxx index 0fb3f09b6b4..c22544027fa 100644 --- a/Common/TableProducer/propagationServiceV2.cxx +++ b/Common/TableProducer/propagationServiceV2.cxx @@ -37,8 +37,8 @@ #include #include #include -#include #include +#include #include #include #include diff --git a/Common/TableProducer/trackselection.cxx b/Common/TableProducer/trackselection.cxx index e8619bd9ccb..4cd90b0c1c7 100644 --- a/Common/TableProducer/trackselection.cxx +++ b/Common/TableProducer/trackselection.cxx @@ -160,7 +160,7 @@ struct TrackSelectionTask { const o2::aod::track::TrackSelectionFlags::flagtype trackflagGlob = globalTracks.IsSelectedMask(track); if (produceTable == 1) { - filterTable((uint8_t)0, + filterTable(static_cast(0), trackflagGlob, filtBit1.IsSelected(track), filtBit2.IsSelected(track), @@ -200,7 +200,7 @@ struct TrackSelectionTask { for (const auto& track : tracks) { o2::aod::track::TrackSelectionFlags::flagtype trackflagGlob = globalTracks.IsSelectedMask(track); if (produceTable == 1) { - filterTable((uint8_t)globalTracksSDD.IsSelected(track), + filterTable(static_cast(globalTracksSDD.IsSelected(track)), trackflagGlob, filtBit1.IsSelected(track), filtBit2.IsSelected(track), diff --git a/Common/TableProducer/zdcTaskLightIons.cxx b/Common/TableProducer/zdcTaskLightIons.cxx index d778f5087a6..7681551328c 100644 --- a/Common/TableProducer/zdcTaskLightIons.cxx +++ b/Common/TableProducer/zdcTaskLightIons.cxx @@ -18,6 +18,9 @@ #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/ZDCLightIons.h" +#include +#include +#include #include #include #include @@ -31,7 +34,10 @@ #include #include +#include #include +#include +#include using namespace o2; using namespace o2::aod; @@ -45,6 +51,14 @@ using ColEvSels = soa::Join zdcTableLI; + Service ccdb; + static const int nBCsPerOrbit = o2::constants::lhc::LHCMaxBunches; + std::bitset beamPatternA; + std::bitset beamPatternC; + std::bitset bcPatternB; + std::bitset bcPatternA; + std::bitset bcPatternC; + std::bitset bcPatternE; // Configurable parameters Configurable nBinsTiming{"nBinsTiming", 200, "n bins for debunching histo"}; @@ -78,6 +92,14 @@ struct ZdcTaskLightIons { nEventSelections }; + enum bcMaskBits { + bcMask_beamB, + bcMask_beamA, + bcMask_beamC, + bcMask_beamE, + nBits + }; + void init(InitContext const&) { registry.add("zdcDebunchHist", "ZN sum vs. diff; ZNA-ZNC (ns); ZNA+ZNC (ns)", {HistType::kTH2D, {{nBinsTiming, -20., 20.}, {nBinsTiming, -20., 20.}}}); @@ -106,50 +128,50 @@ struct ZdcTaskLightIons { selected = std::fabs(collision.posZ()) < cfgEvSelVtxZ; if (selected) { - selectionBits |= (uint8_t)(0x1u << evSel_zvtx); + selectionBits |= static_cast(0x1u << evSel_zvtx); registry.fill(HIST("hEventCount"), evSel_zvtx); } selected = collision.sel8(); if (selected) { - selectionBits |= (uint8_t)(0x1u << evSel_sel8); + selectionBits |= static_cast(0x1u << evSel_sel8); registry.fill(HIST("hEventCount"), evSel_sel8); } auto occupancy = collision.trackOccupancyInTimeRange(); selected = occupancy <= cfgEvSelsMaxOccupancy; if (selected) { - selectionBits |= (uint8_t)(0x1u << evSel_occupancy); + selectionBits |= static_cast(0x1u << evSel_occupancy); registry.fill(HIST("hEventCount"), evSel_occupancy); } selected = collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup); if (selected) { - selectionBits |= (uint8_t)(0x1u << evSel_kNoSameBunchPileup); + selectionBits |= static_cast(0x1u << evSel_kNoSameBunchPileup); registry.fill(HIST("hEventCount"), evSel_kNoSameBunchPileup); } selected = collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV); if (selected) { - selectionBits |= (uint8_t)(0x1u << evSel_kIsGoodZvtxFT0vsPV); + selectionBits |= static_cast(0x1u << evSel_kIsGoodZvtxFT0vsPV); registry.fill(HIST("hEventCount"), evSel_kIsGoodZvtxFT0vsPV); } selected = collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard); if (selected) { - selectionBits |= (uint8_t)(0x1u << evSel_kNoCollInTimeRangeStandard); + selectionBits |= static_cast(0x1u << evSel_kNoCollInTimeRangeStandard); registry.fill(HIST("hEventCount"), evSel_kNoCollInTimeRangeStandard); } selected = collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder); if (selected) { - selectionBits |= (uint8_t)(0x1u << evSel_kNoITSROFrameBorder); + selectionBits |= static_cast(0x1u << evSel_kNoITSROFrameBorder); registry.fill(HIST("hEventCount"), evSel_kNoITSROFrameBorder); } selected = collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll); if (selected) { - selectionBits |= (uint8_t)(0x1u << evSel_kIsGoodITSLayersAll); + selectionBits |= static_cast(0x1u << evSel_kIsGoodITSLayersAll); registry.fill(HIST("hEventCount"), evSel_kIsGoodITSLayersAll); } @@ -158,9 +180,46 @@ struct ZdcTaskLightIons { void processZDCBC(BCsRun3 const& bcs, aod::Zdcs const& /*zdcs*/) { + auto& ccdbMgr = o2::ccdb::BasicCCDBManager::instance(); + uint64_t timeStamp = bcs.iteratorAt(0).timestamp(); + std::map metadata; + // use getSpecific to set metadata to avoid crashes related to specific run number + auto grplhcif = ccdbMgr.getSpecific("GLO/Config/GRPLHCIF", timeStamp, metadata); + // auto grplhcif = ccdb->getForTimeStamp("GLO/Config/GRPLHCIF", timeStamp); + if (grplhcif == nullptr) { + LOG(fatal) << "GRPLHCIFData not in database, timestamp:" << timeStamp; + } + + beamPatternA = grplhcif->getBunchFilling().getBeamPattern(0); + beamPatternC = grplhcif->getBunchFilling().getBeamPattern(1); + bcPatternB = grplhcif->getBunchFilling().getBCPattern(); + bcPatternA = beamPatternA & ~beamPatternC; + bcPatternC = ~beamPatternA & beamPatternC; + bcPatternE = ~beamPatternA & ~beamPatternC; + for (const auto& bc : bcs) { + if (bc.has_zdc()) { + // int64_t timestamp = bc.timestamp(); + auto timestampFromSOR = (bc.timestamp() - grplhcif->getFillNumberTime()) / 1e3; // Convert to seconds + + int bcInOrbit = bc.globalBC() % nBCsPerOrbit; + + uint8_t maskSel = 0; + if (bcPatternB[bcInOrbit]) { + maskSel |= static_cast(0x1u << bcMask_beamB); + } + if (bcPatternA[bcInOrbit]) { + maskSel |= static_cast(0x1u << bcMask_beamA); + } + if (bcPatternC[bcInOrbit]) { + maskSel |= static_cast(0x1u << bcMask_beamC); + } + if (bcPatternE[bcInOrbit]) { + maskSel |= static_cast(0x1u << bcMask_beamE); + } + auto tdcZNA = bc.zdc().timeZNA(); auto tdcZNC = bc.zdc().timeZNC(); auto tdcZPA = bc.zdc().timeZPA(); @@ -225,8 +284,9 @@ struct ZdcTaskLightIons { -1, -1, -1, -1., -1, -1, -1, - bc.timestamp(), - -1); + timestampFromSOR, + 0, + maskSel); } } } @@ -333,7 +393,8 @@ struct ZdcTaskLightIons { zv, centralityFT0C, centralityFT0A, centralityFT0M, foundBC.timestamp(), - evSelection); + evSelection, + 0); } } } diff --git a/Common/Tasks/centralityQa.cxx b/Common/Tasks/centralityQa.cxx index 72ad8e21925..2bf1fd28e77 100644 --- a/Common/Tasks/centralityQa.cxx +++ b/Common/Tasks/centralityQa.cxx @@ -332,6 +332,9 @@ struct CentralityQa { getHist("hBCSelection")->GetXaxis()->SetBinLabel(8, "isFlangeEvent"); insertHist("hAmpVsChFT0A", "hAmpVsChFT0A;Channel; Amplitude", kTH2D, {{96, -0.5, 95.5}, axisChannelAmplitude}); insertHist("hAmpVsChFT0C", "hAmpVsChFT0C;Channel; Amplitude", kTH2D, {{112, -0.5, 111.5}, axisChannelAmplitude}); + insertHist("hAmpVsChFV0A", "hAmpVsChFV0A;Channel; Amplitude", kTH2D, {{48, -0.5, 47.5}, axisChannelAmplitude}); + insertHist("hAmpVsChFDDA", "hAmpVsChFDDA;Channel; Amplitude", kTH2D, {{8, -0.5, 7.5}, axisChannelAmplitude}); + insertHist("hAmpVsChFDDC", "hAmpVsChFDDC;Channel; Amplitude", kTH2D, {{8, -0.5, 7.5}, axisChannelAmplitude}); } histos.print(); @@ -883,7 +886,7 @@ struct CentralityQa { } using BCsWithRun3Matchings = soa::Join; - void processBunchCrossings(soa::Join::iterator const& bc, aod::FT0s const&, aod::FV0As const&) + void processBunchCrossings(soa::Join::iterator const& bc, aod::FT0s const&, aod::FV0As const&, aod::FDDs const&) { if (!isBunchCrossingAccepted(bc, true)) { return; @@ -899,6 +902,24 @@ struct CentralityQa { histos.fill(HIST("hAmpVsChFT0C"), ft0.channelC()[ii], ft0.amplitudeC()[ii]); } } + + if (bc.has_fv0a()) { + const auto& fv0a = bc.fv0a(); + for (size_t ii{0}; ii < fv0a.channel().size(); ++ii) { + histos.fill(HIST("hAmpVsChFV0A"), fv0a.channel()[ii], fv0a.amplitude()[ii]); + } + } + + if (bc.has_fdd()) { + const auto& fdd = bc.fdd(); + for (size_t ii{0}; ii < std::size(fdd.chargeA()); ++ii) { + histos.fill(HIST("hAmpVsChFDDA"), ii, fdd.chargeA()[ii]); + } + + for (size_t ii{0}; ii < std::size(fdd.chargeC()); ++ii) { + histos.fill(HIST("hAmpVsChFDDC"), ii, fdd.chargeC()[ii]); + } + } } PROCESS_SWITCH(CentralityQa, processRun2, "Process with Run 2 V0A", false); diff --git a/Common/Tasks/centralityStudy.cxx b/Common/Tasks/centralityStudy.cxx index fab358689b1..7168d78b5cc 100644 --- a/Common/Tasks/centralityStudy.cxx +++ b/Common/Tasks/centralityStudy.cxx @@ -185,7 +185,7 @@ struct CentralityStudy { Configurable factorFT0C{"factorFT0C", 1.00f, "scale the raw FT0C signal for convenience"}; Configurable factorFV0A{"factorFV0A", 1.00f, "scale the raw FV0A signal for convenience"}; Configurable factorFT0M{"factorFT0M", 1.00f, "scale the raw FT0M signal for convenience"}; - Configurable factorFV0AT0C{"factorFV0AT0C", 1.00f, "scale the raw FV0A+FT0C signal for convenience"}; + Configurable factorFIT{"factorFIT", 1.00f, "scale the raw FV0A+FT0C signal for convenience"}; Configurable normFT0A{"normFT0A", 1.00f, "scale the self-normalised FT0A signal; scale.normFT0A * FT0A / "}; Configurable normFT0C{"normFT0C", 1.00f, "scale the self-normalised FT0C signal; scale.normFT0C * FT0C / "}; Configurable normFV0A{"normFV0A", 1.00f, "scale the self-normalised FV0A signal; scale.normFV0A * FV0A / "}; @@ -206,6 +206,7 @@ struct CentralityStudy { ConfigurableAxis axisFT0COccupancy{"axisFT0COccupancy", {50, 0, 80000}, "FT0C occupancy"}; // For one-dimensional plots, where binning is no issue + ConfigurableAxis axisMultUltraFineFIT{"axisMultUltraFineFIT", {60000, 0, 60000}, "FT0M + FV0 amplitude"}; ConfigurableAxis axisMultUltraFineFV0A{"axisMultUltraFineFV0A", {60000, 0, 60000}, "FV0A amplitude"}; ConfigurableAxis axisMultUltraFineFT0C{"axisMultUltraFineFT0C", {60000, 0, 60000}, "FT0C amplitude"}; ConfigurableAxis axisMultUltraFineFT0A{"axisMultUltraFineFT0A", {60000, 0, 60000}, "FT0A amplitude"}; @@ -240,6 +241,16 @@ struct CentralityStudy { return std::get>(histPointers[name]); } + // fill the global histogram and, if enabled, its run-by-run copy with the same values + template + void fillHist(const TName& name, Ts... values) + { + histos.fill(name, values...); + if (studies.doRunByRunHistograms) { + getHist(histPath + TName::str)->Fill(values...); + } + } + void init(InitContext&) { ccdb->setURL(ccdbSettings.ccdbURL); @@ -273,8 +284,13 @@ struct CentralityStudy { histos.add("hFT0C_Collisions", "hFT0C_Collisions", kTH1D, {axisMultUltraFineFT0C}); histos.add("hFT0M_Collisions", "hFT0M_Collisions", kTH1D, {axisMultUltraFineFT0M}); histos.add("hFV0A_Collisions", "hFV0A_Collisions", kTH1D, {axisMultUltraFineFV0A}); + histos.add("hFV0AOuter_Collisions", "hFV0AOuter_Collisions", kTH1D, {axisMultUltraFineFV0A}); histos.add("hFT0AOuter_Collisions", "hFT0AOuter_Collisions", kTH1D, {axisMultUltraFineFT0A}); + histos.add("hFT0COuter_Collisions", "hFT0COuter_Collisions", kTH1D, {axisMultUltraFineFT0C}); histos.add("hFT0MOuterA_Collisions", "hFT0MOuterA_Collisions", kTH1D, {axisMultUltraFineFT0M}); + histos.add("hFIT_Collisions", "hFIT_Collisions", kTH1D, {axisMultUltraFineFIT}); + histos.add("hFIT_FullV0_Collisions", "hFIT_FullV0_Collisions", kTH1D, {axisMultUltraFineFIT}); + histos.add("hFIT_All_Collisions", "hFIT_All_Collisions", kTH1D, {axisMultUltraFineFIT}); histos.add("hNGlobalTracks", "hNGlobalTracks", kTH1D, {axisMultUltraFineGlobalTracks}); histos.add("hNMFTTracks", "hNMFTTracks", kTH1D, {axisMultUltraFineMFTTracks}); @@ -344,6 +360,7 @@ struct CentralityStudy { histos.add("hNGlobalTracksVsFT0A", "hNGlobalTracksVsFT0A", kTH2F, {axisMultFT0A, axisMultGlobalTracks}); histos.add("hNGlobalTracksVsFT0C", "hNGlobalTracksVsFT0C", kTH2F, {axisMultFT0C, axisMultGlobalTracks}); histos.add("hNGlobalTracksVsFT0M", "hNGlobalTracksVsFT0M", kTH2F, {axisMultFT0M, axisMultGlobalTracks}); + histos.add("hNGlobalTracksVsFT0MOuterA", "hNGlobalTracksVsFT0MOuterA", kTH2F, {axisMultFT0M, axisMultGlobalTracks}); histos.add("hNGlobalTracksVsFV0A", "hNGlobalTracksVsFV0A", kTH2F, {axisMultFV0A, axisMultGlobalTracks}); histos.add("hNGlobalTracksVsFDDA", "hNGlobalTracksVsFDDA", kTH2F, {axisMultFDDA, axisMultGlobalTracks}); histos.add("hNGlobalTracksVsFDDC", "hNGlobalTracksVsFDDC", kTH2F, {axisMultFDDC, axisMultGlobalTracks}); @@ -407,14 +424,20 @@ struct CentralityStudy { histos.get(HIST("hBCSelection"))->GetXaxis()->SetBinLabel(8, "zpac time"); histos.get(HIST("hBCSelection"))->GetXaxis()->SetBinLabel(9, "isFlangeEvent"); + histos.add("hFIT_BCs", "hFIT_BCs", kTH1D, {axisMultUltraFineFIT}); + histos.add("hFIT_FullV0_BCs", "hFIT_FullV0_BCs", kTH1D, {axisMultUltraFineFIT}); + histos.add("hFIT_All_BCs", "hFIT_All_BCs", kTH1D, {axisMultUltraFineFIT}); histos.add("hFT0C_BCs", "hFT0C_BCs", kTH1D, {axisMultUltraFineFT0C}); + histos.add("hFT0COuter_BCs", "hFT0COuter_BCs", kTH1D, {axisMultUltraFineFT0C}); histos.add("hFT0A_BCs", "hFT0A_BCs", kTH1D, {axisMultUltraFineFT0A}); histos.add("hFT0AOuter_BCs", "hFT0AOuter_BCs", kTH1D, {axisMultUltraFineFT0A}); histos.add("hFT0M_BCs", "hFT0M_BCs", kTH1D, {axisMultUltraFineFT0M}); histos.add("hFT0MOuterA_BCs", "hFT0MOuterA_BCs", kTH1D, {axisMultUltraFineFT0M}); histos.add("hFV0A_BCs", "hFV0A_BCs", kTH1D, {axisMultUltraFineFV0A}); + histos.add("hFV0AOuter_BCs", "hFV0AOuter_BCs", kTH1D, {axisMultUltraFineFV0A}); histos.add("hFV0AT0C_BCs", "hFV0AT0C_BCs", kTH1D, {axisMultUltraFineFV0AT0C}); + histos.add("hFV0AOuterT0C_BCs", "hFV0AOuterT0C_BCs", kTH1D, {axisMultUltraFineFV0AT0C}); histos.add("hScaledFT0M_BCs", "hScaledFT0M_BCs", kTH1D, {axisMultUltraFineScaledFT0M}); histos.add("hScaledFV0AT0C_BCs", "hScaledFV0AT0C_BCs", kTH1D, {axisMultUltraFineScaledFV0AT0C}); @@ -541,11 +564,17 @@ struct CentralityStudy { getHist(histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(19, "bcsel"); histPointers.insert({histPath + "hFT0C_Collisions", histos.add((histPath + "hFT0C_Collisions").c_str(), "hFT0C_Collisions", {kTH1D, {{axisMultUltraFineFT0C}}})}); + histPointers.insert({histPath + "hFT0COuter_Collisions", histos.add((histPath + "hFT0COuter_Collisions").c_str(), "hFT0COuter_Collisions", {kTH1D, {{axisMultUltraFineFT0C}}})}); histPointers.insert({histPath + "hFT0A_Collisions", histos.add((histPath + "hFT0A_Collisions").c_str(), "hFT0A_Collisions", {kTH1D, {{axisMultUltraFineFT0A}}})}); - histPointers.insert({histPath + "hFT0AOuter_Collisions", histos.add((histPath + "hFT0AOuter_Collisions").c_str(), "hFT0AOuter_Collisions", {kTH1D, {{axisMultUltraFineFT0C}}})}); - histPointers.insert({histPath + "hFT0MOuterA_Collisions", histos.add((histPath + "hFT0MOuterA_Collisions").c_str(), "hFT0MOuterA_Collisions", {kTH1D, {{axisMultUltraFineFT0A}}})}); + histPointers.insert({histPath + "hFT0AOuter_Collisions", histos.add((histPath + "hFT0AOuter_Collisions").c_str(), "hFT0AOuter_Collisions", {kTH1D, {{axisMultUltraFineFT0A}}})}); + histPointers.insert({histPath + "hFV0AOuter_Collisions", histos.add((histPath + "hFV0AOuter_Collisions").c_str(), "hFV0AOuter_Collisions", {kTH1D, {{axisMultUltraFineFV0A}}})}); + histPointers.insert({histPath + "hFT0MOuterA_Collisions", histos.add((histPath + "hFT0MOuterA_Collisions").c_str(), "hFT0MOuterA_Collisions", {kTH1D, {{axisMultUltraFineFT0M}}})}); histPointers.insert({histPath + "hFT0M_Collisions", histos.add((histPath + "hFT0M_Collisions").c_str(), "hFT0M_Collisions", {kTH1D, {{axisMultUltraFineFT0M}}})}); histPointers.insert({histPath + "hFV0A_Collisions", histos.add((histPath + "hFV0A_Collisions").c_str(), "hFV0A_Collisions", {kTH1D, {{axisMultUltraFineFV0A}}})}); + histPointers.insert({histPath + "hFIT_Collisions", histos.add((histPath + "hFIT_Collisions").c_str(), "hFIT_Collisions", {kTH1D, {{axisMultUltraFineFIT}}})}); + histPointers.insert({histPath + "hFIT_FullV0_Collisions", histos.add((histPath + "hFIT_FullV0_Collisions").c_str(), "hFIT_FullV0_Collisions", {kTH1D, {{axisMultUltraFineFIT}}})}); + histPointers.insert({histPath + "hFIT_All_Collisions", histos.add((histPath + "hFIT_All_Collisions").c_str(), "hFIT_All_Collisions", {kTH1D, {{axisMultUltraFineFIT}}})}); + histPointers.insert({histPath + "hNGlobalTracks", histos.add((histPath + "hNGlobalTracks").c_str(), "hNGlobalTracks", {kTH1D, {{axisMultUltraFineGlobalTracks}}})}); histPointers.insert({histPath + "hNMFTTracks", histos.add((histPath + "hNMFTTracks").c_str(), "hNMFTTracks", {kTH1D, {{axisMultUltraFineMFTTracks}}})}); histPointers.insert({histPath + "hNPVContributors", histos.add((histPath + "hNPVContributors").c_str(), "hNPVContributors", {kTH1D, {{axisMultUltraFinePVContributors}}})}); @@ -598,6 +627,7 @@ struct CentralityStudy { histPointers.insert({histPath + "hNGlobalTracksVsFT0A", histos.add((histPath + "hNGlobalTracksVsFT0A").c_str(), "hNGlobalTracksVsFT0A", {kTH2F, {{axisMultFT0A, axisMultGlobalTracks}}})}); histPointers.insert({histPath + "hNGlobalTracksVsFT0C", histos.add((histPath + "hNGlobalTracksVsFT0C").c_str(), "hNGlobalTracksVsFT0C", {kTH2F, {{axisMultFT0C, axisMultGlobalTracks}}})}); histPointers.insert({histPath + "hNGlobalTracksVsFT0M", histos.add((histPath + "hNGlobalTracksVsFT0M").c_str(), "hNGlobalTracksVsFT0M", {kTH2F, {{axisMultFT0M, axisMultGlobalTracks}}})}); + histPointers.insert({histPath + "hNGlobalTracksVsFT0MOuterA", histos.add((histPath + "hNGlobalTracksVsFT0MOuterA").c_str(), "hNGlobalTracksVsFT0MOuterA", {kTH2F, {{axisMultFT0M, axisMultGlobalTracks}}})}); histPointers.insert({histPath + "hNGlobalTracksVsFV0A", histos.add((histPath + "hNGlobalTracksVsFV0A").c_str(), "hNGlobalTracksVsFV0A", {kTH2F, {{axisMultFV0A, axisMultGlobalTracks}}})}); histPointers.insert({histPath + "hNGlobalTracksVsNMFTTracks", histos.add((histPath + "hNGlobalTracksVsNMFTTracks").c_str(), "hNGlobalTracksVsNMFTTracks", {kTH2F, {{axisMultMFTTracks, axisMultGlobalTracks}}})}); histPointers.insert({histPath + "hNGlobalTracksVsNTPV", histos.add((histPath + "hNGlobalTracksVsNTPV").c_str(), "hNGlobalTracksVsNTPV", {kTH2F, {{axisMultPVContributors, axisMultGlobalTracks}}})}); @@ -703,19 +733,13 @@ struct CentralityStudy { initRun(collision); configureCentrality(collision); - histos.fill(HIST("hCollisionSelection"), 0); // all collisions - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(0); - } + fillHist(HIST("hCollisionSelection"), 0); // all collisions if (evsel.applySel8 && !collision.multSel8()) { return; } - histos.fill(HIST("hCollisionSelection"), 1); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(1); - } + fillHist(HIST("hCollisionSelection"), 1); // calculate vertex-Z-equalized quantities if desired float multFV0A = collision.multFV0A(); @@ -778,8 +802,7 @@ struct CentralityStudy { passRequireIsVertexTOFmatched && passRequireIsVertexTRDmatched && passRejectSameBunchPileup && passRejectITSinROFpileupStandard && passRejectITSinROFpileupStrict && passSelectUPCcollisions && passRejectCollInTimeRangeNarrow && passINELgtZERO) { getHist(histPath + "hFT0CvsPVz_Collisions")->Fill(collision.multPVz(), multFT0C * scale.factorFT0C); - getHist(histPath + "hFT0CvsPVz_Collisions")->Fill(collision.multPVz(), multFT0C * scale.factorFT0C); - getHist(histPath + "hFT0AvsPVz_Collisions")->Fill(collision.multPVz(), multFT0A * scale.factorFT0C); + getHist(histPath + "hFT0AvsPVz_Collisions")->Fill(collision.multPVz(), multFT0A * scale.factorFT0A); getHist(histPath + "hFV0AvsPVz_Collisions")->Fill(collision.multPVz(), multFV0A * scale.factorFV0A); getHist(histPath + "hNGlobalTracksvsPVz_Collisions")->Fill(collision.multPVz(), multNTracksGlobal); getHist(histPath + "hNMFTTracksvsPVz_Collisions")->Fill(collision.multPVz(), mftNtracks); @@ -792,10 +815,7 @@ struct CentralityStudy { return; } - histos.fill(HIST("hCollisionSelection"), 2); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(2); - } + fillHist(HIST("hCollisionSelection"), 2); // _______________________________________________________ // Extra event selections start here @@ -803,53 +823,32 @@ struct CentralityStudy { return; } - histos.fill(HIST("hCollisionSelection"), 3 /* Not at ITS ROF border */); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(3); - } + fillHist(HIST("hCollisionSelection"), 3 /* Not at ITS ROF border */); if (!passRejectTFBorder) { return; } - histos.fill(HIST("hCollisionSelection"), 4 /* Not at TF border */); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(4); - } + fillHist(HIST("hCollisionSelection"), 4 /* Not at TF border */); if (!passRequireIsVertexITSTPC) { return; } - histos.fill(HIST("hCollisionSelection"), 5 /* Contains at least one ITS-TPC track */); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(5); - } + fillHist(HIST("hCollisionSelection"), 5 /* Contains at least one ITS-TPC track */); if (!passRequireIsGoodZvtxFT0VsPV) { return; } - histos.fill(HIST("hCollisionSelection"), 6 /* PV position consistency check */); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(6); - } + fillHist(HIST("hCollisionSelection"), 6 /* PV position consistency check */); if (!passRequireIsVertexTOFmatched) { return; } - histos.fill(HIST("hCollisionSelection"), 7 /* PV with at least one contributor matched with TOF */); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(7); - } + fillHist(HIST("hCollisionSelection"), 7 /* PV with at least one contributor matched with TOF */); if (!passRequireIsVertexTRDmatched) { return; } - histos.fill(HIST("hCollisionSelection"), 8 /* PV with at least one contributor matched with TRD */); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(8); - } + fillHist(HIST("hCollisionSelection"), 8 /* PV with at least one contributor matched with TRD */); if (!passRejectSameBunchPileup) { return; } - histos.fill(HIST("hCollisionSelection"), 9 /* Not at same bunch pile-up */); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(9); - } + fillHist(HIST("hCollisionSelection"), 9 /* Not at same bunch pile-up */); // do this only if information is available if constexpr (requires { collision.timeToNext(); }) { float timeToNeighbour = std::min( @@ -860,39 +859,24 @@ struct CentralityStudy { return; } } - histos.fill(HIST("hCollisionSelection"), 10 /* has suspicious neighbour */); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(10); - } + fillHist(HIST("hCollisionSelection"), 10 /* has suspicious neighbour */); if (!passRejectITSinROFpileupStandard) { return; } - histos.fill(HIST("hCollisionSelection"), 11 /* Not ITS ROF pileup (standard) */); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(11); - } + fillHist(HIST("hCollisionSelection"), 11 /* Not ITS ROF pileup (standard) */); if (!passRejectITSinROFpileupStrict) { return; } - histos.fill(HIST("hCollisionSelection"), 12 /* Not ITS ROF pileup (strict) */); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(12); - } + fillHist(HIST("hCollisionSelection"), 12 /* Not ITS ROF pileup (strict) */); if (!passSelectUPCcollisions) { // if zero then NOT upc, otherwise UPC return; } - histos.fill(HIST("hCollisionSelection"), 13 /* is UPC event */); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(13); - } + fillHist(HIST("hCollisionSelection"), 13 /* is UPC event */); if (!passRejectCollInTimeRangeNarrow) { return; } - histos.fill(HIST("hCollisionSelection"), 14 /* Reject collision in narrow time range */); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(14); - } + fillHist(HIST("hCollisionSelection"), 14 /* Reject collision in narrow time range */); if (evsel.rejectUpc) { if (collision.multFT0C() < cfgFwd.maxFT0CforZNACselection && @@ -909,10 +893,7 @@ struct CentralityStudy { return; } } - histos.fill(HIST("hCollisionSelection"), 15 /* pass em/upc rejection */); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(15); - } + fillHist(HIST("hCollisionSelection"), 15 /* pass em/upc rejection */); if (evsel.rejectIsFlangeEvent) { if constexpr (requires { collision.has_multBC(); }) { if (collision.has_multBC()) { @@ -926,18 +907,12 @@ struct CentralityStudy { } } - histos.fill(HIST("hCollisionSelection"), 16 /* reject flange events */); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(16); - } + fillHist(HIST("hCollisionSelection"), 16 /* reject flange events */); if (!passINELgtZERO) { return; } - histos.fill(HIST("hCollisionSelection"), 17 /* is INEL > 0 */); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(17); - } + fillHist(HIST("hCollisionSelection"), 17 /* is INEL > 0 */); if (evsel.applyBcSel) { if constexpr (requires { collision.has_multBC(); }) { @@ -957,14 +932,11 @@ struct CentralityStudy { } } - histos.fill(HIST("hCollisionSelection"), 18 /* bc selections */); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(18); - } + fillHist(HIST("hCollisionSelection"), 18 /* bc selections */); // if we got here, we also finally fill the FT0C histogram, please histos.fill(HIST("hNPVContributors"), collision.multNTracksPV()); - histos.fill(HIST("hFT0A_Collisions"), collision.multFT0A() * scale.factorFT0C); + histos.fill(HIST("hFT0A_Collisions"), collision.multFT0A() * scale.factorFT0A); histos.fill(HIST("hFT0C_Collisions"), collision.multFT0C() * scale.factorFT0C); histos.fill(HIST("hFT0M_Collisions"), (collision.multFT0A() + collision.multFT0C()) * scale.factorFT0M); histos.fill(HIST("hFV0A_Collisions"), collision.multFV0A() * scale.factorFV0A); @@ -997,24 +969,14 @@ struct CentralityStudy { } if (studies.do2DPlots) { - histos.fill(HIST("hNContribsVsFT0C"), collision.multFT0C() * scale.factorFT0C, collision.multPVTotalContributors()); - histos.fill(HIST("hNContribsVsFV0A"), collision.multFV0A() * scale.factorFV0A, collision.multPVTotalContributors()); - histos.fill(HIST("hMatchedVsITSOnly"), collision.multNTracksITSOnly(), collision.multNTracksITSTPC()); + fillHist(HIST("hNContribsVsFT0C"), collision.multFT0C() * scale.factorFT0C, collision.multPVTotalContributors()); + fillHist(HIST("hNContribsVsFV0A"), collision.multFV0A() * scale.factorFV0A, collision.multPVTotalContributors()); + fillHist(HIST("hMatchedVsITSOnly"), collision.multNTracksITSOnly(), collision.multNTracksITSTPC()); // correlate also FIT detector signals - histos.fill(HIST("hFT0AVsFT0C"), collision.multFT0C() * scale.factorFT0C, collision.multFT0A()); - histos.fill(HIST("hFV0AVsFT0C"), collision.multFT0C() * scale.factorFT0C, collision.multFV0A()); - histos.fill(HIST("hFDDAVsFT0C"), collision.multFT0C() * scale.factorFT0C, collision.multFDDA()); - histos.fill(HIST("hFDDCVsFT0C"), collision.multFT0C() * scale.factorFT0C, collision.multFDDC()); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hNContribsVsFT0C")->Fill(collision.multFT0C() * scale.factorFT0C, collision.multPVTotalContributors()); - getHist(histPath + "hNContribsVsFV0A")->Fill(collision.multFV0A() * scale.factorFV0A, collision.multPVTotalContributors()); - getHist(histPath + "hMatchedVsITSOnly")->Fill(collision.multNTracksITSOnly(), collision.multNTracksITSTPC()); - // correlate also FIT detector signals - getHist(histPath + "hFT0AVsFT0C")->Fill(collision.multFT0C() * scale.factorFT0C, collision.multFT0A()); - getHist(histPath + "hFV0AVsFT0C")->Fill(collision.multFT0C() * scale.factorFT0C, collision.multFV0A()); - getHist(histPath + "hFDDAVsFT0C")->Fill(collision.multFT0C() * scale.factorFT0C, collision.multFDDA()); - getHist(histPath + "hFDDCVsFT0C")->Fill(collision.multFT0C() * scale.factorFT0C, collision.multFDDC()); - } + fillHist(HIST("hFT0AVsFT0C"), collision.multFT0C() * scale.factorFT0C, collision.multFT0A()); + fillHist(HIST("hFV0AVsFT0C"), collision.multFT0C() * scale.factorFT0C, collision.multFV0A()); + fillHist(HIST("hFDDAVsFT0C"), collision.multFT0C() * scale.factorFT0C, collision.multFDDA()); + fillHist(HIST("hFDDCVsFT0C"), collision.multFT0C() * scale.factorFT0C, collision.multFDDC()); } if (studies.doOccupancyStudyVsCentrality2d) { @@ -1032,91 +994,45 @@ struct CentralityStudy { } if (studies.doNGlobalTracksVsRawSignals) { - histos.fill(HIST("hNGlobalTracksVsFT0A"), multFT0A, multNTracksGlobal); - histos.fill(HIST("hNGlobalTracksVsFT0C"), multFT0C, multNTracksGlobal); - histos.fill(HIST("hNGlobalTracksVsFT0M"), (multFT0A + multFT0C), multNTracksGlobal); - histos.fill(HIST("hNGlobalTracksVsFV0A"), multFV0A, multNTracksGlobal); - histos.fill(HIST("hNGlobalTracksVsNMFTTracks"), mftNtracks, multNTracksGlobal); - histos.fill(HIST("hNGlobalTracksVsNTPV"), multNTracksPV, multNTracksGlobal); - - // per run - if (studies.doRunByRunHistograms) { - getHist(histPath + "hNGlobalTracksVsFT0A")->Fill(multFT0A, multNTracksGlobal); - getHist(histPath + "hNGlobalTracksVsFT0C")->Fill(multFT0C, multNTracksGlobal); - getHist(histPath + "hNGlobalTracksVsFT0M")->Fill(multFT0A + multFT0C, multNTracksGlobal); - getHist(histPath + "hNGlobalTracksVsFV0A")->Fill(multFV0A, multNTracksGlobal); - getHist(histPath + "hNGlobalTracksVsNMFTTracks")->Fill(mftNtracks, multNTracksGlobal); - getHist(histPath + "hNGlobalTracksVsNTPV")->Fill(multNTracksPV, multNTracksGlobal); - } + fillHist(HIST("hNGlobalTracksVsFT0A"), multFT0A, multNTracksGlobal); + fillHist(HIST("hNGlobalTracksVsFT0C"), multFT0C, multNTracksGlobal); + fillHist(HIST("hNGlobalTracksVsFT0M"), (multFT0A + multFT0C), multNTracksGlobal); + fillHist(HIST("hNGlobalTracksVsFV0A"), multFV0A, multNTracksGlobal); + fillHist(HIST("hNGlobalTracksVsNMFTTracks"), mftNtracks, multNTracksGlobal); + fillHist(HIST("hNGlobalTracksVsNTPV"), multNTracksPV, multNTracksGlobal); } if (studies.doNTracksPVeta05VsRawSignals) { - histos.fill(HIST("hNTracksPVeta05VsFT0A"), multFT0A, multNTracksPV05); - histos.fill(HIST("hNTracksPVeta05VsFT0C"), multFT0C, multNTracksPV05); - histos.fill(HIST("hNTracksPVeta05VsFT0M"), (multFT0A + multFT0C), multNTracksPV05); - histos.fill(HIST("hNTracksPVeta05VsFV0A"), multFV0A, multNTracksPV05); - histos.fill(HIST("hNTracksPVeta05VsFDDA"), multFDDA, multNTracksPV05); - histos.fill(HIST("hNTracksPVeta05VsFDDC"), multFDDC, multNTracksPV05); - histos.fill(HIST("hNTracksPVeta05VsNMFTTracks"), mftNtracks, multNTracksPV05); - histos.fill(HIST("hNTracksPVeta05VsNTPV"), multNTracksPV, multNTracksPV05); - - // per run - if (studies.doRunByRunHistograms) { - getHist(histPath + "hNTracksPVeta05VsFT0A")->Fill(multFT0A, multNTracksPV05); - getHist(histPath + "hNTracksPVeta05VsFT0C")->Fill(multFT0C, multNTracksPV05); - getHist(histPath + "hNTracksPVeta05VsFT0M")->Fill(multFT0A + multFT0C, multNTracksPV05); - getHist(histPath + "hNTracksPVeta05VsFV0A")->Fill(multFV0A, multNTracksPV05); - getHist(histPath + "hNTracksPVeta05VsFDDA")->Fill(multFDDA, multNTracksPV05); - getHist(histPath + "hNTracksPVeta05VsFDDC")->Fill(multFDDC, multNTracksPV05); - getHist(histPath + "hNTracksPVeta05VsNMFTTracks")->Fill(mftNtracks, multNTracksPV05); - getHist(histPath + "hNTracksPVeta05VsNTPV")->Fill(multNTracksPV, multNTracksPV05); - } + fillHist(HIST("hNTracksPVeta05VsFT0A"), multFT0A, multNTracksPV05); + fillHist(HIST("hNTracksPVeta05VsFT0C"), multFT0C, multNTracksPV05); + fillHist(HIST("hNTracksPVeta05VsFT0M"), (multFT0A + multFT0C), multNTracksPV05); + fillHist(HIST("hNTracksPVeta05VsFV0A"), multFV0A, multNTracksPV05); + fillHist(HIST("hNTracksPVeta05VsFDDA"), multFDDA, multNTracksPV05); + fillHist(HIST("hNTracksPVeta05VsFDDC"), multFDDC, multNTracksPV05); + fillHist(HIST("hNTracksPVeta05VsNMFTTracks"), mftNtracks, multNTracksPV05); + fillHist(HIST("hNTracksPVeta05VsNTPV"), multNTracksPV, multNTracksPV05); } if (studies.doNTracksPVeta08VsRawSignals) { - histos.fill(HIST("hNTracksPVeta08VsFT0A"), multFT0A, multNTracksPV); - histos.fill(HIST("hNTracksPVeta08VsFT0C"), multFT0C, multNTracksPV); - histos.fill(HIST("hNTracksPVeta08VsFT0M"), (multFT0A + multFT0C), multNTracksPV); - histos.fill(HIST("hNTracksPVeta08VsFV0A"), multFV0A, multNTracksPV); - histos.fill(HIST("hNTracksPVeta08VsFDDA"), multFDDA, multNTracksPV); - histos.fill(HIST("hNTracksPVeta08VsFDDC"), multFDDC, multNTracksPV); - histos.fill(HIST("hNTracksPVeta08VsNMFTTracks"), mftNtracks, multNTracksPV); - histos.fill(HIST("hNTracksPVeta08VsNTPV"), multNTracksPV, multNTracksPV); - - // per run - if (studies.doRunByRunHistograms) { - getHist(histPath + "hNTracksPVeta08VsFT0A")->Fill(multFT0A, multNTracksPV); - getHist(histPath + "hNTracksPVeta08VsFT0C")->Fill(multFT0C, multNTracksPV); - getHist(histPath + "hNTracksPVeta08VsFT0M")->Fill(multFT0A + multFT0C, multNTracksPV); - getHist(histPath + "hNTracksPVeta08VsFV0A")->Fill(multFV0A, multNTracksPV); - getHist(histPath + "hNTracksPVeta08VsFDDA")->Fill(multFDDA, multNTracksPV); - getHist(histPath + "hNTracksPVeta08VsFDDC")->Fill(multFDDC, multNTracksPV); - getHist(histPath + "hNTracksPVeta08VsNMFTTracks")->Fill(mftNtracks, multNTracksPV); - getHist(histPath + "hNTracksPVeta08VsNTPV")->Fill(multNTracksPV, multNTracksPV); - } + fillHist(HIST("hNTracksPVeta08VsFT0A"), multFT0A, multNTracksPV); + fillHist(HIST("hNTracksPVeta08VsFT0C"), multFT0C, multNTracksPV); + fillHist(HIST("hNTracksPVeta08VsFT0M"), (multFT0A + multFT0C), multNTracksPV); + fillHist(HIST("hNTracksPVeta08VsFV0A"), multFV0A, multNTracksPV); + fillHist(HIST("hNTracksPVeta08VsFDDA"), multFDDA, multNTracksPV); + fillHist(HIST("hNTracksPVeta08VsFDDC"), multFDDC, multNTracksPV); + fillHist(HIST("hNTracksPVeta08VsNMFTTracks"), mftNtracks, multNTracksPV); + fillHist(HIST("hNTracksPVeta08VsNTPV"), multNTracksPV, multNTracksPV); } if (studies.doNTracksPVeta10VsRawSignals) { - histos.fill(HIST("hNTracksPVeta10VsFT0A"), multFT0A, multNTracksPV10); - histos.fill(HIST("hNTracksPVeta10VsFT0C"), multFT0C, multNTracksPV10); - histos.fill(HIST("hNTracksPVeta10VsFT0M"), (multFT0A + multFT0C), multNTracksPV10); - histos.fill(HIST("hNTracksPVeta10VsFV0A"), multFV0A, multNTracksPV10); - histos.fill(HIST("hNTracksPVeta10VsFDDA"), multFDDA, multNTracksPV10); - histos.fill(HIST("hNTracksPVeta10VsFDDC"), multFDDC, multNTracksPV10); - histos.fill(HIST("hNTracksPVeta10VsNMFTTracks"), mftNtracks, multNTracksPV10); - histos.fill(HIST("hNTracksPVeta10VsNTPV"), multNTracksPV, multNTracksPV10); - - // per run - if (studies.doRunByRunHistograms) { - getHist(histPath + "hNTracksPVeta10VsFT0A")->Fill(multFT0A, multNTracksPV10); - getHist(histPath + "hNTracksPVeta10VsFT0C")->Fill(multFT0C, multNTracksPV10); - getHist(histPath + "hNTracksPVeta10VsFT0M")->Fill(multFT0A + multFT0C, multNTracksPV10); - getHist(histPath + "hNTracksPVeta10VsFV0A")->Fill(multFV0A, multNTracksPV10); - getHist(histPath + "hNTracksPVeta10VsFDDA")->Fill(multFDDA, multNTracksPV10); - getHist(histPath + "hNTracksPVeta10VsFDDC")->Fill(multFDDC, multNTracksPV10); - getHist(histPath + "hNTracksPVeta10VsNMFTTracks")->Fill(mftNtracks, multNTracksPV10); - getHist(histPath + "hNTracksPVeta10VsNTPV")->Fill(multNTracksPV, multNTracksPV10); - } + fillHist(HIST("hNTracksPVeta10VsFT0A"), multFT0A, multNTracksPV10); + fillHist(HIST("hNTracksPVeta10VsFT0C"), multFT0C, multNTracksPV10); + fillHist(HIST("hNTracksPVeta10VsFT0M"), (multFT0A + multFT0C), multNTracksPV10); + fillHist(HIST("hNTracksPVeta10VsFV0A"), multFV0A, multNTracksPV10); + fillHist(HIST("hNTracksPVeta10VsFDDA"), multFDDA, multNTracksPV10); + fillHist(HIST("hNTracksPVeta10VsFDDC"), multFDDC, multNTracksPV10); + fillHist(HIST("hNTracksPVeta10VsNMFTTracks"), mftNtracks, multNTracksPV10); + fillHist(HIST("hNTracksPVeta10VsNTPV"), multNTracksPV, multNTracksPV10); } if constexpr (requires { collision.multMCExtraId(); }) { @@ -1140,22 +1056,13 @@ struct CentralityStudy { // if the table has centrality information // process FT0C centrality plots if (doprocessCollisionsWithCentrality || ccdbSettings.fetchCentralityCalibration) { - histos.fill(HIST("hCentrality"), centFT0C); - histos.fill(HIST("hNContribsVsCentrality"), centFT0C, collision.multPVTotalContributors()); - histos.fill(HIST("hNITSTPCTracksVsCentrality"), centFT0C, collision.multNTracksITSTPC()); - histos.fill(HIST("hNITSOnlyTracksVsCentrality"), centFT0C, collision.multNTracksITSOnly()); - histos.fill(HIST("hNGlobalTracksVsCentrality"), centFT0C, collision.multNTracksGlobal()); - histos.fill(HIST("hNMFTTracksVsCentrality"), centFT0C, collision.mftNtracks()); - histos.fill(HIST("hPVChi2VsCentrality"), centFT0C, collision.multPVChi2()); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCentrality")->Fill(centFT0C); - getHist(histPath + "hNContribsVsCentrality")->Fill(centFT0C, collision.multPVTotalContributors()); - getHist(histPath + "hNITSTPCTracksVsCentrality")->Fill(centFT0C, collision.multNTracksITSTPC()); - getHist(histPath + "hNITSOnlyTracksVsCentrality")->Fill(centFT0C, collision.multNTracksITSOnly()); - getHist(histPath + "hNGlobalTracksVsCentrality")->Fill(centFT0C, collision.multNTracksGlobal()); - getHist(histPath + "hNMFTTracksVsCentrality")->Fill(centFT0C, collision.mftNtracks()); - getHist(histPath + "hPVChi2VsCentrality")->Fill(centFT0C, collision.multPVChi2()); - } + fillHist(HIST("hCentrality"), centFT0C); + fillHist(HIST("hNContribsVsCentrality"), centFT0C, collision.multPVTotalContributors()); + fillHist(HIST("hNITSTPCTracksVsCentrality"), centFT0C, collision.multNTracksITSTPC()); + fillHist(HIST("hNITSOnlyTracksVsCentrality"), centFT0C, collision.multNTracksITSOnly()); + fillHist(HIST("hNGlobalTracksVsCentrality"), centFT0C, collision.multNTracksGlobal()); + fillHist(HIST("hNMFTTracksVsCentrality"), centFT0C, collision.mftNtracks()); + fillHist(HIST("hPVChi2VsCentrality"), centFT0C, collision.multPVChi2()); if (studies.doOccupancyStudyVsCentrality2d) { histos.fill(HIST("hNcontribsProfileVsTrackOccupancyVsCentrality"), collision.trackOccupancyInTimeRange(), centFT0C, collision.multPVTotalContributors()); histos.fill(HIST("hNGlobalTracksProfileVsTrackOccupancyVsCentrality"), collision.trackOccupancyInTimeRange(), centFT0C, collision.multNTracksGlobal()); @@ -1174,43 +1081,40 @@ struct CentralityStudy { if constexpr (requires { collision.has_multBC(); }) { if (collision.has_multBC()) { auto multbc = collision.template multBC_as>(); - histos.fill(HIST("hFT0AOuter_Collisions"), multbc.multFT0AOuter() * scale.factorFT0A); - histos.fill(HIST("hFT0MOuterA_Collisions"), multbc.multFT0AOuter() + multbc.multFT0C() * scale.factorFT0M); + fillHist(HIST("hFV0AOuter_Collisions"), multbc.multFV0AOuter() * scale.factorFV0A); + fillHist(HIST("hFT0AOuter_Collisions"), multbc.multFT0AOuter() * scale.factorFT0A); + fillHist(HIST("hFT0MOuterA_Collisions"), (multbc.multFT0AOuter() + multbc.multFT0C()) * scale.factorFT0M); + + fillHist(HIST("hFT0COuter_Collisions"), multbc.multFT0COuter() * scale.factorFT0C); + fillHist(HIST("hFIT_Collisions"), (multbc.multFT0C() + multbc.multFT0AOuter() + multbc.multFV0AOuter()) * scale.factorFIT); + fillHist(HIST("hFIT_FullV0_Collisions"), (multbc.multFT0C() + multbc.multFT0AOuter() + multbc.multFV0A()) * scale.factorFIT); + fillHist(HIST("hFIT_All_Collisions"), (multbc.multFT0C() + multbc.multFT0A() + multbc.multFV0A()) * scale.factorFIT); if (studies.do2DPlots) { - histos.fill(HIST("hFT0AOuterVsFT0C"), multbc.multFT0C() * scale.factorFT0C, multbc.multFT0AOuter() * scale.factorFT0A); + fillHist(HIST("hFT0AOuterVsFT0C"), multbc.multFT0C() * scale.factorFT0C, multbc.multFT0AOuter() * scale.factorFT0A); + } + if (studies.doNGlobalTracksVsRawSignals) { + fillHist(HIST("hNGlobalTracksVsFT0MOuterA"), multbc.multFT0AOuter() + multbc.multFT0C(), multNTracksGlobal); } const uint64_t bcTimestamp = multbc.timestamp(); const float interactionRate = mRateFetcher.fetch(ccdb.service, bcTimestamp, mRunNumber, ccdbSettings.irSource.value, ccdbSettings.irCrashOnNull) / 1000.; // kHz - histos.fill(HIST("hInteractionRate"), interactionRate); + fillHist(HIST("hInteractionRate"), interactionRate); if (doprocessCollisionsWithCentrality || ccdbSettings.fetchCentralityCalibration) { - histos.fill(HIST("hInteractionRateVsCentrality"), centFT0C, interactionRate); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hInteractionRateVsCentrality")->Fill(centFT0C, interactionRate); - } + fillHist(HIST("hInteractionRateVsCentrality"), centFT0C, interactionRate); } - if (studies.doRunByRunHistograms) { - getHist(histPath + "hInteractionRate")->Fill(interactionRate); - getHist(histPath + "hFT0AOuter_Collisions")->Fill(multbc.multFT0AOuter() * scale.factorFT0A); - getHist(histPath + "hFT0MOuterA_Collisions")->Fill(multbc.multFT0AOuter() + multbc.multFT0C() * scale.factorFT0M); - if (studies.do2DPlots) { - getHist(histPath + "hFT0AOuterVsFT0C")->Fill(multbc.multFT0C() * scale.factorFT0C, multbc.multFT0AOuter() * scale.factorFT0A); - } - - if (studies.doTimeStudies) { - const float hoursAfterStartOfRun = static_cast(bcTimestamp - startOfRunTimestamp) / 3600000.0; - getHist(histPath + "hFT0AVsTime")->Fill(hoursAfterStartOfRun, collision.multFT0A()); - getHist(histPath + "hFT0CVsTime")->Fill(hoursAfterStartOfRun, collision.multFT0C()); - getHist(histPath + "hFT0MVsTime")->Fill(hoursAfterStartOfRun, collision.multFT0M()); - getHist(histPath + "hFV0AVsTime")->Fill(hoursAfterStartOfRun, collision.multFV0A()); - getHist(histPath + "hFV0AOuterVsTime")->Fill(hoursAfterStartOfRun, multbc.multFV0AOuter()); - getHist(histPath + "hMFTTracksVsTime")->Fill(hoursAfterStartOfRun, collision.mftNtracks()); - getHist(histPath + "hNGlobalVsTime")->Fill(hoursAfterStartOfRun, collision.multNTracksGlobal()); - getHist(histPath + "hNTPVContributorsVsTime")->Fill(hoursAfterStartOfRun, collision.multPVTotalContributors()); - getHist(histPath + "hPVzProfileCoVsTime")->Fill(hoursAfterStartOfRun, collision.multPVz()); - getHist(histPath + "hPVzProfileBcVsTime")->Fill(hoursAfterStartOfRun, multbc.multFT0PosZ()); - getHist(histPath + "hIRProfileVsTime")->Fill(hoursAfterStartOfRun, interactionRate); - } + if (studies.doRunByRunHistograms && studies.doTimeStudies) { + const float hoursAfterStartOfRun = static_cast(bcTimestamp - startOfRunTimestamp) / 3600000.0; + getHist(histPath + "hFT0AVsTime")->Fill(hoursAfterStartOfRun, collision.multFT0A()); + getHist(histPath + "hFT0CVsTime")->Fill(hoursAfterStartOfRun, collision.multFT0C()); + getHist(histPath + "hFT0MVsTime")->Fill(hoursAfterStartOfRun, collision.multFT0M()); + getHist(histPath + "hFV0AVsTime")->Fill(hoursAfterStartOfRun, collision.multFV0A()); + getHist(histPath + "hFV0AOuterVsTime")->Fill(hoursAfterStartOfRun, multbc.multFV0AOuter()); + getHist(histPath + "hMFTTracksVsTime")->Fill(hoursAfterStartOfRun, collision.mftNtracks()); + getHist(histPath + "hNGlobalVsTime")->Fill(hoursAfterStartOfRun, collision.multNTracksGlobal()); + getHist(histPath + "hNTPVContributorsVsTime")->Fill(hoursAfterStartOfRun, collision.multPVTotalContributors()); + getHist(histPath + "hPVzProfileCoVsTime")->Fill(hoursAfterStartOfRun, collision.multPVz()); + getHist(histPath + "hPVzProfileBcVsTime")->Fill(hoursAfterStartOfRun, multbc.multFT0PosZ()); + getHist(histPath + "hIRProfileVsTime")->Fill(hoursAfterStartOfRun, interactionRate); } } } @@ -1348,13 +1252,18 @@ struct CentralityStudy { // if we got here, we also finally fill the FT0C histogram, please histos.fill(HIST("hFT0C_BCs"), multbc.multFT0C() * scale.factorFT0C); + histos.fill(HIST("hFT0COuter_BCs"), multbc.multFT0COuter() * scale.factorFT0C); histos.fill(HIST("hFT0A_BCs"), multbc.multFT0A() * scale.factorFT0A); histos.fill(HIST("hFT0AOuter_BCs"), multbc.multFT0AOuter() * scale.factorFT0A); + histos.fill(HIST("hFV0AOuter_BCs"), multbc.multFV0AOuter() * scale.factorFV0A); histos.fill(HIST("hFT0M_BCs"), (multbc.multFT0A() + multbc.multFT0C()) * scale.factorFT0M); histos.fill(HIST("hFT0MOuterA_BCs"), (multbc.multFT0AOuter() + multbc.multFT0C()) * scale.factorFT0M); histos.fill(HIST("hFV0A_BCs"), multbc.multFV0A() * scale.factorFV0A); - histos.fill(HIST("hFV0AT0C_BCs"), (multbc.multFV0A() + multbc.multFT0C()) * scale.factorFV0AT0C); - + histos.fill(HIST("hFV0AT0C_BCs"), (multbc.multFV0A() + multbc.multFT0C()) * scale.factorFIT); + histos.fill(HIST("hFV0AOuterT0C_BCs"), (multbc.multFV0AOuter() + multbc.multFT0C()) * scale.factorFIT); + histos.fill(HIST("hFIT_BCs"), (multbc.multFT0C() + multbc.multFT0AOuter() + multbc.multFV0AOuter()) * scale.factorFIT); + histos.fill(HIST("hFIT_FullV0_BCs"), (multbc.multFT0C() + multbc.multFT0AOuter() + multbc.multFV0A()) * scale.factorFIT); + histos.fill(HIST("hFIT_All_BCs"), (multbc.multFT0C() + multbc.multFT0A() + multbc.multFV0A()) * scale.factorFIT); if (studies.do2DPlots) { histos.fill(HIST("hFT0AVsFT0C_BCs"), multbc.multFT0C() * scale.factorFT0C, multbc.multFT0A() * scale.factorFT0A); histos.fill(HIST("hFV0AVsFT0C_BCs"), multbc.multFT0C() * scale.factorFT0C, multbc.multFV0A() * scale.factorFV0A); @@ -1384,9 +1293,9 @@ struct CentralityStudy { continue; } - const float selfNormFV0A = scale.normFT0A * multbc.multFV0A() / histos.get(HIST("hFT0A_BCs"))->GetMean(); - const float selfNormFT0A = scale.normFV0A * multbc.multFT0A() / histos.get(HIST("hFT0C_BCs"))->GetMean(); - const float selfNormFT0C = scale.normFT0C * multbc.multFT0C() / histos.get(HIST("hFV0A_BCs"))->GetMean(); + const float selfNormFV0A = scale.normFV0A * multbc.multFV0A() / histos.get(HIST("hFV0A_BCs"))->GetMean(); + const float selfNormFT0A = scale.normFT0A * multbc.multFT0A() / histos.get(HIST("hFT0A_BCs"))->GetMean(); + const float selfNormFT0C = scale.normFT0C * multbc.multFT0C() / histos.get(HIST("hFT0C_BCs"))->GetMean(); histos.fill(HIST("hScaledFT0M_BCs"), selfNormFT0A + selfNormFT0C); histos.fill(HIST("hScaledFV0AT0C_BCs"), selfNormFV0A + selfNormFT0C); } diff --git a/Common/Tasks/checkDataModelMC.cxx b/Common/Tasks/checkDataModelMC.cxx index 8eb6efc03f2..105ada03c72 100644 --- a/Common/Tasks/checkDataModelMC.cxx +++ b/Common/Tasks/checkDataModelMC.cxx @@ -57,7 +57,7 @@ void checkDaughters(const T& particlesMC, } } for (const auto& idxDau : particle.daughtersIds()) { - if (idxDau >= 0 && ((uint64_t)idxDau > offset + particlesMC.size() || (uint64_t)idxDau < offset)) { + if (idxDau >= 0 && (static_cast(idxDau) > offset + particlesMC.size() || static_cast(idxDau) < offset)) { if (debugMode) { debugHisto->Fill(1); } else { diff --git a/Common/Tasks/flowTest.cxx b/Common/Tasks/flowTest.cxx index bfb42acff04..0e3334e91f1 100644 --- a/Common/Tasks/flowTest.cxx +++ b/Common/Tasks/flowTest.cxx @@ -30,6 +30,8 @@ #include #include +#include + using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; @@ -100,7 +102,7 @@ struct flowTest { if (evPhi < 0) evPhi += 2. * TMath::Pi(); - long nCh = 0; + int64_t nCh = 0; if (imp > minB && imp < maxB) { // event within range diff --git a/Common/Tasks/multiplicityQa.cxx b/Common/Tasks/multiplicityQa.cxx index 2178164854c..6a6c09188cf 100644 --- a/Common/Tasks/multiplicityQa.cxx +++ b/Common/Tasks/multiplicityQa.cxx @@ -290,8 +290,8 @@ struct MultiplicityQa { return; } - long NcontribsTOF = 0; - long NcontribsTRD = 0; + int64_t NcontribsTOF = 0; + int64_t NcontribsTRD = 0; for (auto& track : tracks) { if (track.isPVContributor()) { if (track.hasTRD()) @@ -347,7 +347,7 @@ struct MultiplicityQa { Preslice perColIU = aod::track::collisionId; using Run3Tracks = soa::Join; - Partition pvContribTracksIUEta1 = (nabs(aod::track::eta) < 1.0f) && ((aod::track::flags & (uint32_t)o2::aod::track::PVContributor) == (uint32_t)o2::aod::track::PVContributor); + Partition pvContribTracksIUEta1 = (nabs(aod::track::eta) < 1.0f) && ((aod::track::flags & static_cast(o2::aod::track::PVContributor)) == static_cast(o2::aod::track::PVContributor)); void processMCCollisions(soa::Join::iterator const& mcCollision, aod::McParticles const& mcParticles, soa::SmallGroups> const& collisions, Run3Tracks const&) { diff --git a/Common/Tasks/qaMuon.cxx b/Common/Tasks/qaMuon.cxx index dd1a9c6d76d..2e5aa56c8e4 100644 --- a/Common/Tasks/qaMuon.cxx +++ b/Common/Tasks/qaMuon.cxx @@ -372,7 +372,7 @@ struct muonQa { // Global muons plots // ====================== int nTrackTypes = static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::MCHStandaloneTrack) + 1; - AxisSpec trackTypeAxis = {static_cast(nTrackTypes), 0.0, static_cast(nTrackTypes), "track type"}; + AxisSpec trackTypeAxis = {nTrackTypes, 0.0, static_cast(nTrackTypes), "track type"}; registry.add("global-muons/nTracksPerType", "Number of tracks per type", {HistType::kTH1F, {trackTypeAxis}}); AxisSpec nCandidatesAxis = {static_cast(fNCandidatesMax), 0.0, static_cast(fNCandidatesMax), "match candidate rank"}; @@ -1104,7 +1104,7 @@ struct muonQa { template void TransformMFT(TTrack& track) { - if constexpr (static_cast(GlobalFwdFillMap)) { + if constexpr (GlobalFwdFillMap) { auto mchTrack = mMatching.FwdtoMCH(track); doTransformMFT(mchTrack); @@ -1604,7 +1604,7 @@ struct muonQa { fgValues.nClusters = muon.nClusters(); fgValues.sign = muon.sign(); - if constexpr (static_cast(MuonFillMap)) { + if constexpr (MuonFillMap) { // Direct info from AO2D without re-propagation fgValues.pDca = muon.pDca(); fgValues.rabs = muon.rAtAbsorberEnd(); @@ -1772,7 +1772,7 @@ struct muonQa { fgValues.nClusters = muon.nClusters(); fgValues.sign = muon.sign(); - if constexpr (static_cast(MuonFillMap)) { + if constexpr (MuonFillMap) { o2::dataformats::GlobalFwdTrack track; SMatrix5 tpars(muon.x(), muon.y(), muon.phi(), muon.tgl(), muon.signed1Pt()); @@ -1809,7 +1809,7 @@ struct muonQa { o2::dataformats::GlobalFwdTrack track; - if constexpr (static_cast(Scaled)) { + if constexpr (Scaled) { double pMCH = fgValuesMCH.p; int sign = fgValuesMCH.sign; @@ -1897,7 +1897,7 @@ struct muonQa { float dcaXY = std::sqrt(fgValues.dcaX * fgValues.dcaX + fgValues.dcaY * fgValues.dcaY); fgValues.dcaXY = dcaXY; - if constexpr (static_cast(MuonFillMap)) { + if constexpr (MuonFillMap) { float p = muon.p(); fgValues.pDca = p * dcaXY; } @@ -1920,7 +1920,7 @@ struct muonQa { template void FillMuonHistograms(Var const& fgValuesMCH, Var const& fgValuesMCHpv, Var const& fgValuesMFT, Var const& fgValuesGlobal, VarVector const& fgValuesCandidates) { - if constexpr (static_cast(MuonFillMap)) { + if constexpr (MuonFillMap) { // Muon histograms if (IsGoodMuon(fgValuesMCH, fgValuesMCHpv, 1.E10, configMuons.fPMchLow, configMuons.fPtMchLow, configMuons.fEtaMchLow, configMuons.fEtaMchUp, configMuons.fRabsLow, configMuons.fRabsUp, configMuons.fSigmaPdcaUp)) { registry.get(HIST("muons/TrackChi2"))->Fill(fgValuesMCH.chi2); @@ -1996,7 +1996,7 @@ struct muonQa { } } - if constexpr (static_cast(GlobalMuonFillMap)) { + if constexpr (GlobalMuonFillMap) { // Global muon histograms if (IsGoodMuon(fgValuesMCH, fgValuesMCHpv, 1.E10, configMuons.fPMchLow, configMuons.fPtMchLow, configMFTs.fEtaMftLow, configMFTs.fEtaMftUp, configMuons.fRabsLow, configMuons.fRabsUp, configMuons.fSigmaPdcaUp)) { registry.get(HIST("global-muons/TrackChi2"))->Fill(fgValuesMCH.chi2); @@ -2048,7 +2048,7 @@ struct muonQa { } } - if constexpr (static_cast(GlobalMatchingFillMap)) { + if constexpr (GlobalMatchingFillMap) { // Global muon histograms if (IsGoodMuon(fgValuesMCH, fgValuesMCHpv, 1.E10, configMuons.fPMchLow, configMuons.fPtMchLow, configMFTs.fEtaMftLow, configMFTs.fEtaMftUp, configMuons.fRabsLow, configMuons.fRabsUp, configMuons.fSigmaPdcaUp)) { if (IsGoodGlobalMatching(fgValuesMFT, configMFTs.fTrackChi2MftUp, configMFTs.fTrackNClustMftLow, fMatchingChi2MftMchUp)) { @@ -2155,7 +2155,7 @@ struct muonQa { template void FillDCAHistograms(VarT const& fgValuesMCH, VarT const& fgValuesMCHpv, VarT const& fgValuesMFT, VarC const& fgValuesColl, int sign, int quadrant, bool same, bool mixed) { - if constexpr (static_cast(MuonFillMap)) { + if constexpr (MuonFillMap) { auto phiDeg = fgValuesMCH.phi * 180.f / TMath::Pi(); if (same) { std::get>(dcaHistos[1][quadrant][0]["DCA_x"])->Fill(fgValuesMCH.dcaX); @@ -2184,7 +2184,7 @@ struct muonQa { } } - if constexpr (static_cast(GlobalMuonFillMap)) { + if constexpr (GlobalMuonFillMap) { auto phiDeg = fgValuesMFT.phi * 180.f / TMath::Pi(); if (same) { std::get>(dcaHistos[0][quadrant][0]["DCA_x"])->Fill(fgValuesMFT.dcaX); @@ -2212,7 +2212,7 @@ struct muonQa { } } - if constexpr (static_cast(GlobalMatchingFillMap)) { + if constexpr (GlobalMatchingFillMap) { auto phiDegMCH = fgValuesMCH.phi * 180.f / TMath::Pi(); auto phiDegMFT = fgValuesMFT.phi * 180.f / TMath::Pi(); if (same) { @@ -2290,7 +2290,7 @@ struct muonQa { std::array yPos{yCls, fgValuesProp.y}; double phiClus = std::atan2(yCls, xCls) * 180 / TMath::Pi(); - if constexpr (static_cast(MuonFillMap)) { + if constexpr (MuonFillMap) { if (IsGoodMuon(fgValuesMCH, fgValuesMCHpv, configMuons.fTrackChi2MchUp, 20., configMuons.fPtMchLow, configMFTs.fEtaMftLow, configMFTs.fEtaMftUp, configMuons.fRabsLow, configMuons.fRabsUp, configMuons.fSigmaPdcaUp)) { if (same) { std::get>(mchResidualsHistosPerDE[topBottom][posNeg][chamber]["dx_vs_de"])->Fill(deIndex, xPos[0] - xPos[1]); @@ -2303,7 +2303,7 @@ struct muonQa { } } - if constexpr (static_cast(MFTFillMap)) { + if constexpr (MFTFillMap) { if (IsGoodMuon(fgValuesMCH, fgValuesMCHpv, configMuons.fTrackChi2MchUp, 20., configMuons.fPtMchLow, configMFTs.fEtaMftLow, configMFTs.fEtaMftUp, configMuons.fRabsLow, configMuons.fRabsUp, configMuons.fSigmaPdcaUp)) { if (IsGoodMFT(fgValuesMFT, configMFTs.fTrackChi2MftUp, configMFTs.fTrackNClustMftLow)) { if (same) { @@ -2356,9 +2356,9 @@ struct muonQa { base::Propagator::initFieldFromGRP(grpmag); TrackExtrap::setField(); TrackExtrap::useExtrapV2(); - fieldB = static_cast(TGeoGlobalMagField::Instance()->GetField()); // for MFT - double centerMFT[3] = {0, 0, -61.4}; // or use middle point between Vtx and MFT? - Bz = fieldB->getBz(centerMFT); // Get field at centre of MFT + fieldB = dynamic_cast(TGeoGlobalMagField::Instance()->GetField()); // for MFT + double centerMFT[3] = {0, 0, -61.4}; // or use middle point between Vtx and MFT? + Bz = fieldB->getBz(centerMFT); // Get field at centre of MFT } else { LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", bcs.begin().timestamp()); } @@ -2424,7 +2424,7 @@ struct muonQa { typename TMCHTrack, typename VarC, typename VarT> void runDCA(TEventMap const& collisions, TMFTTracks const& mfts, TMFTTrack const& mfttrack, TTrack const& muon, TMCHTrack const& mchtrack, mch::Track const& mchrealigned, VarC& fgValuesColl, VarT& fgValuesMCH, VarT& fgValuesMCHpv, VarT& fgValuesMFT) { - if constexpr (static_cast(MuonFillMap)) { + if constexpr (MuonFillMap) { // track selection if (!IsGoodMuon(fgValuesMCH, fgValuesMCHpv, configMuons.fTrackChi2MchUp, 30., 4., configMFTs.fEtaMftLow, configMFTs.fEtaMftUp, configMuons.fRabsLow, configMuons.fRabsUp, configMuons.fSigmaPdcaUp)) { @@ -2456,7 +2456,7 @@ struct muonQa { } } - if constexpr (static_cast(GlobalMuonFillMap)) { + if constexpr (GlobalMuonFillMap) { auto mftsThisCollision = mfts.sliceBy(mftPerCollision, fgValuesColl.globalIndex); for (auto const& mft : mftsThisCollision) { @@ -2499,7 +2499,7 @@ struct muonQa { } } - if constexpr (static_cast(GlobalMatchingFillMap)) { + if constexpr (GlobalMatchingFillMap) { // track selection if (!IsGoodMuon(fgValuesMCH, fgValuesMCHpv, configMuons.fTrackChi2MchUp, 30., 4., configMFTs.fEtaMftLow, configMFTs.fEtaMftUp, configMuons.fRabsLow, configMuons.fRabsUp, configMuons.fSigmaPdcaUp)) { diff --git a/Common/Tasks/zdcTableReader.cxx b/Common/Tasks/zdcTableReader.cxx index 07a581564cd..6efececddab 100644 --- a/Common/Tasks/zdcTableReader.cxx +++ b/Common/Tasks/zdcTableReader.cxx @@ -27,21 +27,35 @@ #include #include +#include + using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::aod; +#define CHECK_BIT(var, pos) (((var) >> (pos)) & 1) + struct ZDCLIAnalysis { - // Configurable number of bins - Configurable useZvtx{"useZvtx", false, "If true uses Z_vertex"}; - Configurable zVval{"zVval", 10., "Z_vertex cut value"}; - Configurable tStampMin{"tStampMin", 100000., "minimum value for timestamp"}; - Configurable tStampMax{"tStampMax", 100000., ",maximum value for timestamp"}; + // Configurable + Configurable selectBC{"selectBC", 0, "Select BC events"}; + Configurable selectOnlyB{"selectOnlyB", 0, "Select BC with A && C"}; + Configurable selectColl{"selectColl", 0, "Select ALICE collision events"}; + // + Configurable tStampOffset{"tStampOffset", 0, "offset value for timestamp"}; + Configurable nBinstStamp{"nBinstStamp", 1000, "no. bins in histo vs. timestamp"}; + Configurable tStampMax{"tStampMax", 1000, ", maximum value for timestamp"}; + // + Configurable tdcCut{"tdcCut", false, "Flag for TDC cut"}; + Configurable tdcZNmincut{"tdcZNmincut", -1.5, "Min. ZN TDC cut value"}; + Configurable tdcZNmaxcut{"tdcZNmaxcut", 1.5, "Max. ZN TDC cut value"}; + Configurable tdcZPmincut{"tdcZPmincut", -1.5, "Min. ZP TDC cut value"}; + Configurable tdcZPmaxcut{"tdcZPmaxcut", 1.5, "Max. ZP TDC cut value"}; // Configurable nBinsADC{"nBinsADC", 1000, "n bins 4 ZDC ADCs"}; - Configurable nBinsAmp{"nBinsAmp", 1025, "n bins 4 ZDC amplitudes"}; + Configurable nBinsAmpZN{"nBinsAmpZN", 1025, "n bins 4 ZN amplitudes"}; + Configurable nBinsAmpZP{"nBinsAmpZP", 1025, "n bins 4 ZP amplitudes"}; Configurable nBinsTDC{"nBinsTDC", 480, "n bins 4 TDCs"}; Configurable nBinsFit{"nBinsFit", 1000, "n bins 4 FIT"}; Configurable MaxZN{"MaxZN", 4099.5, "Max 4 ZN histos"}; @@ -51,54 +65,110 @@ struct ZDCLIAnalysis { Configurable MaxMultFV0{"MaxMultFV0", 3000, "Max 4 FV0 histos"}; Configurable MaxMultFT0{"MaxMultFT0", 3000, "Max 4 FT0 histos"}; // + Configurable enCalibZNA{"enCalibZNA", 1.0, "Energy calibration ZNA"}; + Configurable enCalibZNC{"enCalibZNC", 1.0, "Energy calibration ZNC"}; + Configurable enCalibZPA{"enCalibZPA", 1.0, "Energy calibration ZPA"}; + Configurable enCalibZPC{"enCalibZPC", 1.0, "Energy calibration ZPC"}; + // + Configurable applyZDCcut{"applyZDCcut", false, "Apply ZDC cut for light ion analysis"}; + Configurable zdcaCutLow{"zdcaCutLow", 12., "ZDCA cut for light ion analysis"}; + Configurable zdccCutLow{"zdccCutLow", 12., "ZDCC cut for light ion analysis"}; + // + Configurable selectZvtx{"selectZvtx", true, "Activate Z vertex selection"}; + Configurable sel8{"sel8", true, "Activate sel8 selection"}; + Configurable triggerTVX{"triggerTVX", true, "Activate trigger TVX selection"}; + Configurable doOccupancySel{"doOccupancySel", false, "Activate occupancy selection"}; + Configurable noSameBunchPileupCut{"noSameBunchPileupCut", true, "Activate no same bunch pileup selection"}; + Configurable isGoodZvtxFT0vsPV{"isGoodZvtxFT0vsPV", true, "Activate is good Z vertex FT0 vs PV selection"}; + Configurable noCollInTimeRangeStandard{"noCollInTimeRangeStandard", true, "Activate no collision in time range standard selection"}; + Configurable noTimeFrameBorder{"noTimeFrameBorder", true, "Activate no time frame border selection"}; + Configurable noITSROFFrameBorder{"noITSROFFrameBorder", true, "Activate no ITS ROF frame border selection"}; + Configurable isGoodITSLayersAll{"isGoodITSLayersAll", false, "Activate is good ITS layers all selection"}; + // HistogramRegistry registry{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; void init(InitContext const&) { - registry.add("hZNApmc", "ZNApmc; ZNA amplitude; Entries", {HistType::kTH1F, {{nBinsAmp, -0.5, MaxZN}}}); - registry.add("hZPApmc", "ZPApmc; ZPA amplitude; Entries", {HistType::kTH1F, {{nBinsAmp, -0.5, MaxZP}}}); - registry.add("hZNCpmc", "ZNCpmc; ZNC amplitude; Entries", {HistType::kTH1F, {{nBinsAmp, -0.5, MaxZN}}}); - registry.add("hZPCpmc", "ZPCpmc; ZPC amplitude; Entries", {HistType::kTH1F, {{nBinsAmp, -0.5, MaxZP}}}); - registry.add("hZEM", "ZEM; ZEM1+ZEM2 amplitude; Entries", {HistType::kTH1F, {{nBinsAmp, -0.5, MaxZEM}}}); - registry.add("hZNAamplvsADC", "ZNA amplitude vs. ADC; ZNA ADC; ZNA amplitude", {HistType::kTH2F, {{{nBinsAmp, -0.5, 3. * MaxZN}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZNCamplvsADC", "ZNC amplitude vs. ADC; ZNC ADC; ZNC amplitude", {HistType::kTH2F, {{{nBinsAmp, -0.5, 3. * MaxZN}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZPAamplvsADC", "ZPA amplitude vs. ADC; ZPA ADC; ZPA amplitude", {HistType::kTH2F, {{{nBinsAmp, -0.5, 3. * MaxZP}, {nBinsAmp, -0.5, MaxZP}}}}); - registry.add("hZPCamplvsADC", "ZPC amplitude vs. ADC; ZPC ADC; ZPC amplitude", {HistType::kTH2F, {{{nBinsAmp, -0.5, 3. * MaxZP}, {nBinsAmp, -0.5, MaxZP}}}}); - registry.add("hZNvsZEM", "ZN vs ZEM; ZEM; ZNA+ZNC", {HistType::kTH2F, {{{nBinsAmp, -0.5, MaxZEM}, {nBinsAmp, -0.5, 2. * MaxZN}}}}); - registry.add("hZNAvsZNC", "ZNA vs ZNC; ZNC; ZNA", {HistType::kTH2F, {{{nBinsAmp, -0.5, MaxZN}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZPAvsZPC", "ZPA vs ZPC; ZPC; ZPA", {HistType::kTH2F, {{{nBinsAmp, -0.5, MaxZP}, {nBinsAmp, -0.5, MaxZP}}}}); - registry.add("hZNAvsZPA", "ZNA vs ZPA; ZPA; ZNA", {HistType::kTH2F, {{{nBinsAmp, -0.5, MaxZP}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZNCvsZPC", "ZNC vs ZPC; ZPC; ZNC", {HistType::kTH2F, {{{nBinsAmp, -0.5, MaxZP}, {nBinsAmp, -0.5, MaxZN}}}}); + registry.add("hBCmask", "mask; BC; counts", {HistType::kTH1F, {{5, 0., 5.}}}); + registry.add("hcounts", "counts; selections; counts", {HistType::kTH1F, {{14, 0., 14.}}}); + registry.add("hzvertex", "z vertex; z_vertex (cm); Entries", {HistType::kTH1F, {{200, -20., 20.}}}); + // + registry.add("hZNApmc", "ZNA pmc; ZNA amplitude; Entries", {HistType::kTH1F, {{nBinsAmpZN, -0.5, MaxZN}}}); + registry.add("hZPApmc", "ZPA pmc; ZPA amplitude; Entries", {HistType::kTH1F, {{nBinsAmpZP, -0.5, MaxZP}}}); + registry.add("hZNCpmc", "ZNC pmc; ZNC amplitude; Entries", {HistType::kTH1F, {{nBinsAmpZN, -0.5, MaxZN}}}); + registry.add("hZPCpmc", "ZPC pmc; ZPC amplitude; Entries", {HistType::kTH1F, {{nBinsAmpZP, -0.5, MaxZP}}}); + registry.add("hZEM", "ZEM; ZEM1+ZEM2 amplitude; Entries", {HistType::kTH1F, {{nBinsAmpZP, -0.5, MaxZEM}}}); + registry.add("hZDCA", "ZNA+ZPA; ZNA+ZPA; Entries", {HistType::kTH1F, {{nBinsAmpZN + nBinsAmpZP, -0.5, MaxZN + MaxZP}}}); + registry.add("hZDCC", "ZNC+ZPC; ZNC+ZPC; Entries", {HistType::kTH1F, {{nBinsAmpZN + nBinsAmpZP, -0.5, MaxZN + MaxZP}}}); + // + registry.add("hZDCApmcwZDCCcut", "ZDCA w. ZDCC cut; ZDCA amplitude; Entries", {HistType::kTH1F, {{nBinsAmpZN + nBinsAmpZP, -0.5, MaxZN + MaxZP}}}); + registry.add("hZDCCpmcwZDCAcut", "ZDCC w. ZDCA cut; ZDCC amplitude; Entries", {HistType::kTH1F, {{nBinsAmpZN + nBinsAmpZP, -0.5, MaxZN + MaxZP}}}); + // + registry.add("hZNAtdc", "ZNA tdc; ZNA tdc; Entries", {HistType::kTH1F, {{520, -14., 12.}}}); + registry.add("hZPAtdc", "ZPA tdc; ZPA tdc; Entries", {HistType::kTH1F, {{520, -14., 12.}}}); + registry.add("hZNCtdc", "ZNC tdc; ZNC tdc; Entries", {HistType::kTH1F, {{520, -14., 12.}}}); + registry.add("hZPCtdc", "ZPC tdc; ZPC tdc; Entries", {HistType::kTH1F, {{520, -14., 12.}}}); + // + registry.add("hZDCCvsA", "ZDC side C vs. side A; ZDCA; ZDCC", {HistType::kTH2F, {{{nBinsAmpZN + nBinsAmpZP, -0.5, MaxZN + MaxZP}, {nBinsAmpZN + nBinsAmpZP, -0.5, MaxZN + MaxZP}}}}); + // + registry.add("hZNAamplvsADC", "ZNA amplitude vs. ADC; ZNA ADC; ZNA amplitude", {HistType::kTH2F, {{{nBinsAmpZN, -0.5, 3. * MaxZN}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZNCamplvsADC", "ZNC amplitude vs. ADC; ZNC ADC; ZNC amplitude", {HistType::kTH2F, {{{nBinsAmpZN, -0.5, 3. * MaxZN}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZPAamplvsADC", "ZPA amplitude vs. ADC; ZPA ADC; ZPA amplitude", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, 3. * MaxZP}, {nBinsAmpZP, -0.5, MaxZP}}}}); + registry.add("hZPCamplvsADC", "ZPC amplitude vs. ADC; ZPC ADC; ZPC amplitude", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, 3. * MaxZP}, {nBinsAmpZP, -0.5, MaxZP}}}}); // - registry.add("hZNCcvsZNCsum", "ZNC PMC vs PMsum; ZNCC ADC; ZNCsum", {HistType::kTH2F, {{{nBinsADC, -0.5, 3. * MaxZN}, {nBinsADC, -0.5, 3. * MaxZN}}}}); - registry.add("hZNAcvsZNAsum", "ZNA PMC vs PMsum; ZNAsum", {HistType::kTH2F, {{{nBinsADC, -0.5, 3. * MaxZN}, {nBinsADC, -0.5, 3. * MaxZN}}}}); + registry.add("hZNvsZEM", "ZN vs ZEM; ZEM; ZNA+ZNC", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, MaxZEM}, {nBinsAmpZN, -0.5, 2. * MaxZN}}}}); + registry.add("hZPvsZEM", "ZP vs ZEM; ZEM; ZPA+ZPC", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, MaxZEM}, {nBinsAmpZP, -0.5, 2. * MaxZP}}}}); + registry.add("hZNAvsZEM", "ZNA vs ZEM; ZEM; ZNA", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, MaxZEM}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZNCvsZEM", "ZNC vs ZEM; ZEM; ZNC", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, MaxZEM}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZPAvsZEM", "ZPA vs ZEM; ZEM; ZPA", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, MaxZEM}, {nBinsAmpZP, -0.5, MaxZP}}}}); + registry.add("hZPCvsZEM", "ZPC vs ZEM; ZEM; ZPC", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, MaxZEM}, {nBinsAmpZP, -0.5, MaxZP}}}}); // - registry.add("hZNCvstdc", "ZNC vs tdc; ZNC amplitude; ZNC TDC", {HistType::kTH2F, {{{480, -13.5, 11.45}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZNAvstdc", "ZNA vs tdc; ZNA amplitude; ZNA TDC", {HistType::kTH2F, {{{480, -13.5, 11.45}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZPCvstdc", "ZPC vs tdc; ZPC amplitude; ZPC TDC", {HistType::kTH2F, {{{480, -13.5, 11.45}, {nBinsAmp, -0.5, MaxZP}}}}); - registry.add("hZPAvstdc", "ZPA vs tdc; ZPA amplitude; ZPA TDC", {HistType::kTH2F, {{{480, -13.5, 11.45}, {nBinsAmp, -0.5, MaxZP}}}}); + registry.add("hZNAvsZNC", "ZNA vs ZNC; ZNC; ZNA", {HistType::kTH2F, {{{nBinsAmpZN, -0.5, MaxZN}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZPAvsZPC", "ZPA vs ZPC; ZPC; ZPA", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, MaxZP}, {nBinsAmpZP, -0.5, MaxZP}}}}); + registry.add("hZNAvsZPA", "ZNA vs ZPA; ZPA; ZNA", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, MaxZP}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZNCvsZPC", "ZNC vs ZPC; ZPC; ZNC", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, MaxZP}, {nBinsAmpZN, -0.5, MaxZN}}}}); // - registry.add("hZNvsV0A", "ZN vs V0A", {HistType::kTH2F, {{{nBinsFit, 0., MaxMultFV0}, {nBinsAmp, -0.5, 2. * MaxZN}}}}); - registry.add("hZNAvsFT0A", "ZNA vs FT0A", {HistType::kTH2F, {{{nBinsFit, 0., MaxMultFT0}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZNCvsFT0C", "ZNC vs FT0C", {HistType::kTH2F, {{{nBinsFit, 0., MaxMultFT0}, {nBinsAmp, -0.5, MaxZN}}}}); + registry.add("hZNCcvsZNCsum", "ZNC PMC vs PMsum; ZNCC ADC; ZNC sum", {HistType::kTH2F, {{{nBinsADC, -0.5, 3. * MaxZN}, {nBinsADC, -0.5, 3. * MaxZN}}}}); + registry.add("hZNAcvsZNAsum", "ZNA PMC vs PMsum; ZNAC ADC; ZNA sum", {HistType::kTH2F, {{{nBinsADC, -0.5, 3. * MaxZN}, {nBinsADC, -0.5, 3. * MaxZN}}}}); // - registry.add("hZNAvscentrFT0A", "ZNA vs centrality FT0A", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZNAvscentrFT0C", "ZNA vs centrality FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZNAvscentrFT0M", "ZNA vs centrality FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZPAvscentrFT0A", "ZPA vs centrality FT0A", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZP}}}}); - registry.add("hZPAvscentrFT0C", "ZPA vs centrality FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZP}}}}); - registry.add("hZPAvscentrFT0M", "ZPA vs centrality FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZP}}}}); - registry.add("hZNCvscentrFT0A", "ZNC vs centrality FT0A", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZNCvscentrFT0C", "ZNC vs centrality FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZNCvscentrFT0M", "ZNC vs centrality FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZPCvscentrFT0A", "ZPC vs centrality FT0A", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZP}}}}); - registry.add("hZPCvscentrFT0C", "ZPC vs centrality FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZP}}}}); - registry.add("hZPCvscentrFT0M", "ZPC vs centrality FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZP}}}}); + registry.add("hZNCvstdc", "ZNC vs tdc; ZNC TDC (ns); ZNC amplitude", {HistType::kTH2F, {{{480, -13.5, 11.45}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZNAvstdc", "ZNA vs tdc; ZNA TDC (ns); ZNA amplitude", {HistType::kTH2F, {{{480, -13.5, 11.45}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZPCvstdc", "ZPC vs tdc; ZPC TDC (ns); ZPC amplitude", {HistType::kTH2F, {{{480, -13.5, 11.45}, {nBinsAmpZP, -0.5, MaxZP}}}}); + registry.add("hZPAvstdc", "ZPA vs tdc; ZPA TDC (ns); ZPA amplitude", {HistType::kTH2F, {{{480, -13.5, 11.45}, {nBinsAmpZP, -0.5, MaxZP}}}}); // - registry.add("hZNAvstimestamp", "ZNA vs timestamp", {HistType::kTH2F, {{{100, tStampMin, tStampMax}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZNCvstimestamp", "ZNC vs timestamp", {HistType::kTH2F, {{{100, tStampMin, tStampMax}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZPAvstimestamp", "ZPA vs timestamp", {HistType::kTH2F, {{{100, tStampMin, tStampMax}, {nBinsAmp, -0.5, MaxZP}}}}); - registry.add("hZPCvstimestamp", "ZPC vs timestamp", {HistType::kTH2F, {{{100, tStampMin, tStampMax}, {nBinsAmp, -0.5, MaxZP}}}}); + registry.add("hZNvsV0A", "ZN vs V0A", {HistType::kTH2F, {{{nBinsFit, 0., MaxMultFV0}, {nBinsAmpZN, -0.5, 2. * MaxZN}}}}); + registry.add("hZNAvsFT0A", "ZNA vs FT0A", {HistType::kTH2F, {{{nBinsFit, 0., MaxMultFT0}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZNCvsFT0C", "ZNC vs FT0C", {HistType::kTH2F, {{{nBinsFit, 0., MaxMultFT0}, {nBinsAmpZN, -0.5, MaxZN}}}}); + // + registry.add("hZNAvscentrFT0A", "ZNA vs centrality FT0A", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZNAvscentrFT0C", "ZNA vs centrality FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZNAvscentrFT0M", "ZNA vs centrality FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZPAvscentrFT0A", "ZPA vs centrality FT0A", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZP, -0.5, MaxZP}}}}); + registry.add("hZPAvscentrFT0C", "ZPA vs centrality FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZP, -0.5, MaxZP}}}}); + registry.add("hZPAvscentrFT0M", "ZPA vs centrality FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZP, -0.5, MaxZP}}}}); + registry.add("hZNCvscentrFT0A", "ZNC vs centrality FT0A", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZNCvscentrFT0C", "ZNC vs centrality FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZNCvscentrFT0M", "ZNC vs centrality FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZPCvscentrFT0A", "ZPC vs centrality FT0A", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZP, -0.5, MaxZP}}}}); + registry.add("hZPCvscentrFT0C", "ZPC vs centrality FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZP, -0.5, MaxZP}}}}); + registry.add("hZPCvscentrFT0M", "ZPC vs centrality FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZP, -0.5, MaxZP}}}}); + // + registry.add("hNnZNAvscentrFT0C", "N_{neutrons} in ZNA vs FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {12, -0.5, 11.5}}}}); + registry.add("hNnZNCvscentrFT0C", "N_{neutrons} in ZNC vs FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {12, -0.5, 11.5}}}}); + registry.add("hNpZPAvscentrFT0C", "N_{protons} in ZPA vs FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {12, -0.5, 11.5}}}}); + registry.add("hNpZPCvscentrFT0C", "N_{protons} in ZPC vs FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {12, -0.5, 11.5}}}}); + registry.add("hNnZNAvscentrFT0M", "N_{neutrons} in ZNA vs FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {12, -0.5, 11.5}}}}); + registry.add("hNnZNCvscentrFT0M", "N_{neutrons} in ZNC vs FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {12, -0.5, 11.5}}}}); + registry.add("hNpZPAvscentrFT0M", "N_{protons} in ZPA vs FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {12, -0.5, 11.5}}}}); + registry.add("hNpZPCvscentrFT0M", "N_{protons} in ZPC vs FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {12, -0.5, 11.5}}}}); + // + registry.add("hNpvsNnZNA", "N_{protons} vs N_{neutrons} in ZNA", {HistType::kTH2F, {{{12, -0.5, 11.5}, {12, -0.5, 11.5}}}}); + registry.add("hNpvsNnZNC", "N_{protons} vs N_{neutrons} in ZNC", {HistType::kTH2F, {{{12, -0.5, 11.5}, {12, -0.5, 11.5}}}}); + // + registry.add("hZNAvstimestamp", "ZNA vs timestamp", {HistType::kTH2F, {{{nBinstStamp, 0., tStampMax}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZNCvstimestamp", "ZNC vs timestamp", {HistType::kTH2F, {{{nBinstStamp, 0., tStampMax}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZPAvstimestamp", "ZPA vs timestamp", {HistType::kTH2F, {{{nBinstStamp, 0., tStampMax}, {nBinsAmpZP, -0.5, MaxZP}}}}); + registry.add("hZPCvstimestamp", "ZPC vs timestamp", {HistType::kTH2F, {{{nBinstStamp, 0., tStampMax}, {nBinsAmpZP, -0.5, MaxZP}}}}); } void process(aod::ZDCLightIons const& zdclightions) @@ -108,6 +178,8 @@ struct ZDCLIAnalysis { auto tdcznc = zdc.zncTdc(); auto tdczpa = zdc.zpaTdc(); auto tdczpc = zdc.zpcTdc(); + auto tdczem1 = zdc.zem1Tdc(); + auto tdczem2 = zdc.zem2Tdc(); auto zna = zdc.znaAmpl(); auto znaADC = zdc.znaPmc(); auto znapm1 = zdc.znaPm1(); @@ -134,55 +206,416 @@ struct ZDCLIAnalysis { auto centrFT0A = zdc.centralityFt0a(); auto centrFT0M = zdc.centralityFt0m(); auto timestamp = zdc.timestamp(); - // auto selectionBits = zdc.selectionBits(); - - if ((useZvtx && (zvtx < zVval)) || !useZvtx) { - registry.get(HIST("hZNApmc"))->Fill(zna); - registry.get(HIST("hZNCpmc"))->Fill(znc); - registry.get(HIST("hZPApmc"))->Fill(zpa); - registry.get(HIST("hZPCpmc"))->Fill(zpc); - registry.get(HIST("hZEM"))->Fill(zem1 + zem2); + auto selectionBits = zdc.selectionBits(); + auto bcMask = zdc.bcMask(); + + bool isZNAtdc = false; + bool isZNCtdc = false; + bool isZPAtdc = false; + bool isZPCtdc = false; + bool isZEMtdc = false; + + if (tdczna > -99999.0) { + isZNAtdc = true; + } + if (tdcznc > -99999.0) { + isZNCtdc = true; + } + if (tdczpa > -99999.0) { + isZPAtdc = true; + } + if (tdczpc > -99999.0) { + isZPCtdc = true; + } + if (tdczem1 > -99999.0 || tdczem2 > -99999.0) { + isZEMtdc = true; + } + + if (tdcCut) { // TDC cuts applied + if ((tdczna < tdcZNmincut) || (tdczna > tdcZNmaxcut)) { + isZNAtdc = false; + } + if ((tdcznc < tdcZNmincut) || (tdcznc > tdcZNmaxcut)) { + isZNCtdc = false; + } + if ((tdczpa < tdcZPmincut) || (tdczpa > tdcZPmaxcut)) { + isZPAtdc = false; + } + if ((tdczpc < tdcZPmincut) || (tdczpc > tdcZPmaxcut)) { + isZPCtdc = false; + } + } + + bool eventSelected = false; + + // for BC events ------- + if (selectBC) { + // printf("BCmask: %x \n", bcMask); + + registry.get(HIST("hBCmask"))->Fill(0., 1.); + auto isB = CHECK_BIT(bcMask, 0); + if (isB) { + registry.get(HIST("hBCmask"))->Fill(1., 1.); + } + if (CHECK_BIT(bcMask, 1)) { + registry.get(HIST("hBCmask"))->Fill(2., 1.); + } + if (CHECK_BIT(bcMask, 2)) { + registry.get(HIST("hBCmask"))->Fill(3., 1.); + } + if (CHECK_BIT(bcMask, 3)) { + registry.get(HIST("hBCmask"))->Fill(4., 1.); + } + // + if (selectOnlyB && isB) { + eventSelected = true; + } else if (!selectOnlyB) { + eventSelected = true; + } + } + + // for collision events ------- + if (selectColl) { + bool zvtxSel = false; + if (selectZvtx && CHECK_BIT(selectionBits, 0)) { + zvtxSel = true; + } else if (!selectZvtx) { + zvtxSel = true; + } + // + bool ottoSel = false; + if (sel8 && CHECK_BIT(selectionBits, 1)) { + ottoSel = true; + } else if (!sel8) { + ottoSel = true; + } + // + bool isdoOccupancySel = false; + if (doOccupancySel && CHECK_BIT(selectionBits, 2)) + isdoOccupancySel = true; + else if (!doOccupancySel) + isdoOccupancySel = true; + // + bool isnoSameBunchPileupCut = false; + if (noSameBunchPileupCut && CHECK_BIT(selectionBits, 3)) + isnoSameBunchPileupCut = true; + else if (!noSameBunchPileupCut) + isnoSameBunchPileupCut = true; + // + bool isGoodZvtxFT0vsPVsel = false; + if (isGoodZvtxFT0vsPV && CHECK_BIT(selectionBits, 4)) + isGoodZvtxFT0vsPVsel = true; + else if (!isGoodZvtxFT0vsPV) + isGoodZvtxFT0vsPVsel = true; // - registry.get(HIST("hZNAamplvsADC"))->Fill(znaADC, zna); - registry.get(HIST("hZNCamplvsADC"))->Fill(zncADC, znc); - registry.get(HIST("hZPAamplvsADC"))->Fill(zpaADC, zpa); - registry.get(HIST("hZPCamplvsADC"))->Fill(zpcADC, zpc); + bool isnoCollInTimeRangeStandard = false; + if (noCollInTimeRangeStandard && CHECK_BIT(selectionBits, 5)) + isnoCollInTimeRangeStandard = true; + else if (!noCollInTimeRangeStandard) + isnoCollInTimeRangeStandard = true; // - registry.get(HIST("hZNvsZEM"))->Fill(zem1 + zem2, zna + znc); - registry.get(HIST("hZNAvsZNC"))->Fill(znc, zna); - registry.get(HIST("hZPAvsZPC"))->Fill(zpc, zpa); - registry.get(HIST("hZNAvsZPA"))->Fill(zpa, zna); - registry.get(HIST("hZNCvsZPC"))->Fill(zpc, znc); + bool isnoTimeFrameBorder = false; + if (noTimeFrameBorder && CHECK_BIT(selectionBits, 6)) + isnoTimeFrameBorder = true; + else if (!noTimeFrameBorder) + isnoTimeFrameBorder = true; // - registry.get(HIST("hZNAvstdc"))->Fill(tdczna, zna); - registry.get(HIST("hZNCvstdc"))->Fill(tdcznc, znc); - registry.get(HIST("hZPAvstdc"))->Fill(tdczpa, zpa); - registry.get(HIST("hZPCvstdc"))->Fill(tdczpc, zpc); + bool isnoITSROFFrameBorder = false; + if (noITSROFFrameBorder && CHECK_BIT(selectionBits, 7)) + isnoITSROFFrameBorder = true; + else if (!noITSROFFrameBorder) + isnoITSROFFrameBorder = true; // - registry.get(HIST("hZNAcvsZNAsum"))->Fill(znapm1 + znapm2 + znapm3 + znapm4, zna); - registry.get(HIST("hZNCcvsZNCsum"))->Fill(zncpm1 + zncpm2 + zncpm3 + zncpm4, znc); + bool isGoodITSLayersAllsel = false; + if (isGoodITSLayersAll && CHECK_BIT(selectionBits, 8)) + isGoodITSLayersAllsel = true; + else if (!isGoodITSLayersAll) + isGoodITSLayersAllsel = true; + // + bool istriggerTVX = false; + if (triggerTVX && CHECK_BIT(selectionBits, 9)) + istriggerTVX = true; + else if (!triggerTVX) + istriggerTVX = true; + + if (zvtxSel && ottoSel && istriggerTVX && isdoOccupancySel && isnoSameBunchPileupCut && isGoodZvtxFT0vsPVsel && isnoCollInTimeRangeStandard && isnoTimeFrameBorder && isnoITSROFFrameBorder && isGoodITSLayersAllsel) + eventSelected = true; + // if (zvtxSel && ottoSel && isnoSameBunchPileupCut) eventSelected = true; + + if (eventSelected) { + registry.get(HIST("hcounts"))->Fill(0., 1.); + if (isZNAtdc) + registry.get(HIST("hcounts"))->Fill(1., 1.); + if (isZPAtdc) + registry.get(HIST("hcounts"))->Fill(2., 1.); + if (isZNCtdc) + registry.get(HIST("hcounts"))->Fill(3., 1.); + if (isZPCtdc) + registry.get(HIST("hcounts"))->Fill(4., 1.); + if (isZNAtdc || isZNCtdc) + registry.get(HIST("hcounts"))->Fill(5., 1.); + if (isZNAtdc || isZPAtdc) + registry.get(HIST("hcounts"))->Fill(6., 1.); + if (isZNCtdc || isZPCtdc) + registry.get(HIST("hcounts"))->Fill(7., 1.); + if (isZNAtdc || isZPAtdc || isZNCtdc || isZPCtdc) + registry.get(HIST("hcounts"))->Fill(8., 1.); + if (isZNAtdc && isZNCtdc) + registry.get(HIST("hcounts"))->Fill(9., 1.); + if (isZNAtdc && isZPAtdc) + registry.get(HIST("hcounts"))->Fill(10., 1.); + if (isZNCtdc && isZPCtdc) + registry.get(HIST("hcounts"))->Fill(11., 1.); + if (isZEMtdc) + registry.get(HIST("hcounts"))->Fill(12., 1.); + } + } + + if (eventSelected) { + + if (enCalibZNA > 0.) { + zna *= enCalibZNA; + znaADC *= enCalibZNA; + znapm1 *= enCalibZNA; + znapm2 *= enCalibZNA; + znapm3 *= enCalibZNA; + znapm4 *= enCalibZNA; + } + if (enCalibZNC > 0.) { + znc *= enCalibZNC; + zncADC *= enCalibZNC; + zncpm1 *= enCalibZNC; + zncpm2 *= enCalibZNC; + zncpm3 *= enCalibZNC; + zncpm4 *= enCalibZNC; + } + if (enCalibZPA > 0.) { + zpa *= enCalibZPA; + zpaADC *= enCalibZPA; + } + if (enCalibZPC > 0.) { + zpc *= enCalibZPC; + zpcADC *= enCalibZPC; + } + + registry.get(HIST("hzvertex"))->Fill(zvtx); + + if (!applyZDCcut) { + + if (isZNAtdc) + registry.get(HIST("hZNApmc"))->Fill(zna); + if (isZNCtdc) + registry.get(HIST("hZNCpmc"))->Fill(znc); + if (isZPAtdc) + registry.get(HIST("hZPApmc"))->Fill(zpa); + if (isZPCtdc) + registry.get(HIST("hZPCpmc"))->Fill(zpc); + // + if (isZNAtdc || isZPAtdc) + registry.get(HIST("hZDCA"))->Fill(zna + zpa); + if (isZNCtdc || isZPCtdc) + registry.get(HIST("hZDCC"))->Fill(znc + zpc); + // + if (isZNAtdc) + registry.get(HIST("hZNAamplvsADC"))->Fill(znaADC, zna); + if (isZNCtdc) + registry.get(HIST("hZNCamplvsADC"))->Fill(zncADC, znc); + if (isZPAtdc) + registry.get(HIST("hZPAamplvsADC"))->Fill(zpaADC, zpa); + if (isZPCtdc) + registry.get(HIST("hZPCamplvsADC"))->Fill(zpcADC, zpc); + // + if (isZNAtdc || isZNCtdc) + registry.get(HIST("hZNAvsZNC"))->Fill(znc, zna); + if (isZPAtdc || isZPCtdc) + registry.get(HIST("hZPAvsZPC"))->Fill(zpc, zpa); + if (isZNAtdc || isZPAtdc) + registry.get(HIST("hZNAvsZPA"))->Fill(zpa, zna); + if (isZNCtdc || isZPCtdc) + registry.get(HIST("hZNCvsZPC"))->Fill(zpc, znc); + // + if (isZNAtdc) + registry.get(HIST("hZNAvstdc"))->Fill(tdczna, zna); + if (isZNCtdc) + registry.get(HIST("hZNCvstdc"))->Fill(tdcznc, znc); + if (isZPAtdc) + registry.get(HIST("hZPAvstdc"))->Fill(tdczpa, zpa); + if (isZPCtdc) + registry.get(HIST("hZPCvstdc"))->Fill(tdczpc, zpc); + // + if (isZNAtdc) + registry.get(HIST("hZNAcvsZNAsum"))->Fill(0.25 * (znapm1 + znapm2 + znapm3 + znapm4), zna); + if (isZNCtdc) + registry.get(HIST("hZNCcvsZNCsum"))->Fill(0.25 * (zncpm1 + zncpm2 + zncpm3 + zncpm4), znc); + // + if (isZNAtdc || isZNCtdc) + registry.get(HIST("hZNvsV0A"))->Fill(multV0A / 100., zna + znc); + if (isZNAtdc) + registry.get(HIST("hZNAvsFT0A"))->Fill((multFT0A) / 100., zna); + if (isZNCtdc) + registry.get(HIST("hZNCvsFT0C"))->Fill((multFT0C) / 100., znc); + } else { + bool isZNChigh = false; + if (znc >= zdccCutLow) + isZNChigh = true; + bool isZNAhigh = false; + if (zna >= zdcaCutLow) + isZNAhigh = true; + bool isZPAhigh = false; + if (zpa >= zdcaCutLow) + isZPAhigh = true; + bool isZPChigh = false; + if (zpc >= zdccCutLow) + isZPChigh = true; + // + if (isZNAtdc && isZNChigh) + registry.get(HIST("hZNApmc"))->Fill(zna); + if (isZNCtdc && isZNAhigh) + registry.get(HIST("hZNCpmc"))->Fill(znc); + if (isZPAtdc && isZPChigh) + registry.get(HIST("hZPApmc"))->Fill(zpa); + if (isZPCtdc && isZPAhigh) + registry.get(HIST("hZPCpmc"))->Fill(zpc); + // + if ((isZNAtdc || isZPAtdc) && (isZNChigh && isZPChigh)) + registry.get(HIST("hZDCA"))->Fill(zna + zpa); + if ((isZNCtdc || isZPCtdc) && (isZNAhigh && isZPAhigh)) + registry.get(HIST("hZDCC"))->Fill(znc + zpc); + // + if (isZNAtdc && isZNChigh) + registry.get(HIST("hZNAamplvsADC"))->Fill(znaADC, zna); + if (isZNCtdc && isZNAhigh) + registry.get(HIST("hZNCamplvsADC"))->Fill(zncADC, znc); + if (isZPAtdc && isZPChigh) + registry.get(HIST("hZPAamplvsADC"))->Fill(zpaADC, zpa); + if (isZPCtdc && isZPAhigh) + registry.get(HIST("hZPCamplvsADC"))->Fill(zpcADC, zpc); + // + if (isZNAtdc && isZNChigh) + registry.get(HIST("hZNAvstdc"))->Fill(tdczna, zna); + if (isZNCtdc && isZNAhigh) + registry.get(HIST("hZNCvstdc"))->Fill(tdcznc, znc); + if (isZPAtdc && isZPChigh) + registry.get(HIST("hZPAvstdc"))->Fill(tdczpa, zpa); + if (isZPCtdc && isZPAhigh) + registry.get(HIST("hZPCvstdc"))->Fill(tdczpc, zpc); + // + if (isZNAtdc && isZNChigh) + registry.get(HIST("hZNAcvsZNAsum"))->Fill(0.25 * (znapm1 + znapm2 + znapm3 + znapm4), zna); + if (isZNCtdc && isZNAhigh) + registry.get(HIST("hZNCcvsZNCsum"))->Fill(0.25 * (zncpm1 + zncpm2 + zncpm3 + zncpm4), znc); + // + if (isZNAtdc && isZNChigh) + registry.get(HIST("hZNAvsFT0A"))->Fill((multFT0A) / 100., zna); + if (isZNCtdc && isZNAhigh) + registry.get(HIST("hZNCvsFT0C"))->Fill((multFT0C) / 100., znc); + } + + // Timestamp + /*if (tStampOffset > timestamp) { + printf("\n\n ################# OFFSET timestamp too large!!!!!!!!!!!!!!!!!!!!!!!!!! > timestamp %lu \n\n", timestamp); + return; + }*/ + // float tsh = (timestamp / 1000.) - (tStampOffset / 1000.); // in hours + /*if (timestamp > tStampMax) { + printf("\n\n MAXIMUM timestamp too small!!!!!!!!!!!!!!!!!!!!!!!!!! > timestamp-offset %f \n\n", timestamp); + return; + }*/ + + if (!applyZDCcut) { + if (isZNAtdc) + registry.get(HIST("hZNAvstimestamp"))->Fill(timestamp, zna); + if (isZNCtdc) + registry.get(HIST("hZNCvstimestamp"))->Fill(timestamp, znc); + if (isZPAtdc) + registry.get(HIST("hZPAvstimestamp"))->Fill(timestamp, zpa); + if (isZPCtdc) + registry.get(HIST("hZPCvstimestamp"))->Fill(timestamp, zpc); + } else { + if (isZNAtdc && znc >= zdccCutLow) { + registry.get(HIST("hZNAvstimestamp"))->Fill(timestamp, zna); + } + if (isZPAtdc && zpc >= zdccCutLow) { + registry.get(HIST("hZPAvstimestamp"))->Fill(timestamp, zpa); + } + if ((isZNAtdc || isZPAtdc) && (znc >= zdccCutLow && zpc >= zdccCutLow)) { + registry.get(HIST("hZDCApmcwZDCCcut"))->Fill(zna + zpa); + } + if (isZNCtdc && zna >= zdcaCutLow) { + registry.get(HIST("hZNCvstimestamp"))->Fill(timestamp, znc); + } + if (isZPCtdc && zpa >= zdcaCutLow) { + registry.get(HIST("hZPCvstimestamp"))->Fill(timestamp, zpc); + } + if ((isZNCtdc || isZPCtdc) && (zna >= zdcaCutLow && zpa >= zdcaCutLow)) { + registry.get(HIST("hZDCCpmcwZDCAcut"))->Fill(znc + zpc); + } + } + // TDCs + if (isZNAtdc) + registry.get(HIST("hZNAtdc"))->Fill(tdczna); + if (isZNCtdc) + registry.get(HIST("hZNCtdc"))->Fill(tdcznc); + if (isZPAtdc) + registry.get(HIST("hZPAtdc"))->Fill(tdczpa); + if (isZPCtdc) + registry.get(HIST("hZPCtdc"))->Fill(tdczpc); // - registry.get(HIST("hZNvsV0A"))->Fill(multV0A / 100., zna + znc); - registry.get(HIST("hZNAvsFT0A"))->Fill((multFT0A) / 100., zna); - registry.get(HIST("hZNCvsFT0C"))->Fill((multFT0C) / 100., znc); + if (isZEMtdc) { + registry.get(HIST("hZEM"))->Fill(zem1 + zem2); + registry.get(HIST("hZNAvsZEM"))->Fill(zem1 + zem2, zna); + registry.get(HIST("hZNCvsZEM"))->Fill(zem1 + zem2, znc); + registry.get(HIST("hZPAvsZEM"))->Fill(zem1 + zem2, zpa); + registry.get(HIST("hZPCvsZEM"))->Fill(zem1 + zem2, zpc); + } + if (isZNAtdc || isZNCtdc) + registry.get(HIST("hZNvsZEM"))->Fill(zem1 + zem2, zna + znc); + if (isZPAtdc || isZPCtdc) + registry.get(HIST("hZPvsZEM"))->Fill(zem1 + zem2, zpa + zpc); // - registry.get(HIST("hZNAvscentrFT0A"))->Fill(centrFT0A, zna); - registry.get(HIST("hZNAvscentrFT0C"))->Fill(centrFT0C, zna); - registry.get(HIST("hZNAvscentrFT0M"))->Fill(centrFT0M, zna); - registry.get(HIST("hZPAvscentrFT0A"))->Fill(centrFT0A, zpa); - registry.get(HIST("hZPAvscentrFT0C"))->Fill(centrFT0C, zpa); - registry.get(HIST("hZPAvscentrFT0M"))->Fill(centrFT0M, zpa); - registry.get(HIST("hZNCvscentrFT0A"))->Fill(centrFT0A, znc); - registry.get(HIST("hZNCvscentrFT0C"))->Fill(centrFT0C, znc); - registry.get(HIST("hZNCvscentrFT0M"))->Fill(centrFT0M, znc); - registry.get(HIST("hZPCvscentrFT0A"))->Fill(centrFT0A, zpc); - registry.get(HIST("hZPCvscentrFT0C"))->Fill(centrFT0C, zpc); - registry.get(HIST("hZPCvscentrFT0M"))->Fill(centrFT0M, zpc); + if (isZNAtdc || isZNCtdc || isZPAtdc || isZPCtdc) + registry.get(HIST("hZDCCvsA"))->Fill(zna + zpa, znc + zpc); // - registry.get(HIST("hZNAvstimestamp"))->Fill(timestamp, zna); - registry.get(HIST("hZNCvstimestamp"))->Fill(timestamp, znc); - registry.get(HIST("hZPAvstimestamp"))->Fill(timestamp, zpa); - registry.get(HIST("hZPCvstimestamp"))->Fill(timestamp, zpc); + if (centrFT0C > -1. && centrFT0C < 101.) { + if (isZNAtdc) + registry.get(HIST("hZNAvscentrFT0C"))->Fill(centrFT0C, zna); + if (isZPAtdc) + registry.get(HIST("hZPAvscentrFT0C"))->Fill(centrFT0C, zpa); + if (isZNCtdc) + registry.get(HIST("hZNCvscentrFT0C"))->Fill(centrFT0C, znc); + if (isZPCtdc) + registry.get(HIST("hZPCvscentrFT0C"))->Fill(centrFT0C, zpc); + registry.get(HIST("hNnZNAvscentrFT0C"))->Fill(centrFT0C, zna / 2.680); + registry.get(HIST("hNnZNCvscentrFT0C"))->Fill(centrFT0C, znc / 2.680); + registry.get(HIST("hNpZPAvscentrFT0C"))->Fill(centrFT0C, zpa / 2.680); + registry.get(HIST("hNpZPCvscentrFT0C"))->Fill(centrFT0C, zpc / 2.680); + } + if (centrFT0A > -1. && centrFT0A < 101.) { + if (isZNAtdc) + registry.get(HIST("hZNAvscentrFT0A"))->Fill(centrFT0A, zna); + if (isZPAtdc) + registry.get(HIST("hZPAvscentrFT0A"))->Fill(centrFT0A, zpa); + if (isZNCtdc) + registry.get(HIST("hZNCvscentrFT0A"))->Fill(centrFT0A, znc); + if (isZPCtdc) + registry.get(HIST("hZPCvscentrFT0A"))->Fill(centrFT0A, zpc); + } + if (centrFT0M > -1. && centrFT0M < 101.) { + if (isZNAtdc) + registry.get(HIST("hZNAvscentrFT0M"))->Fill(centrFT0M, zna); + if (isZPAtdc) + registry.get(HIST("hZPAvscentrFT0M"))->Fill(centrFT0M, zpa); + if (isZNCtdc) + registry.get(HIST("hZNCvscentrFT0M"))->Fill(centrFT0M, znc); + if (isZPCtdc) + registry.get(HIST("hZPCvscentrFT0M"))->Fill(centrFT0M, zpc); + registry.get(HIST("hNnZNAvscentrFT0M"))->Fill(centrFT0M, zna / 2.680); + registry.get(HIST("hNnZNCvscentrFT0M"))->Fill(centrFT0M, znc / 2.680); + registry.get(HIST("hNpZPAvscentrFT0M"))->Fill(centrFT0M, zpa / 2.680); + registry.get(HIST("hNpZPCvscentrFT0M"))->Fill(centrFT0M, zpc / 2.680); + registry.get(HIST("hNpvsNnZNA"))->Fill(zna / 2.680, zpa / 2.680); + registry.get(HIST("hNpvsNnZNC"))->Fill(znc / 2.680, zpc / 2.680); + } } } } diff --git a/Common/Tools/EventSelectionModule.h b/Common/Tools/EventSelectionModule.h index 09f114364c7..0d4fda380b0 100644 --- a/Common/Tools/EventSelectionModule.h +++ b/Common/Tools/EventSelectionModule.h @@ -54,7 +54,6 @@ #include #include #include -#include #include #include #include @@ -225,7 +224,7 @@ class BcSelectionModule // first bc of the first orbit bcSOR = static_cast((sorTimestamp * 1000 - orbitResetMUS) / o2::constants::lhc::LHCOrbitMUS) * nBCsPerOrbit; // duration of TF in bcs - nBCsPerTF = 32; // hard-coded for Run3 MC (no info from ccdb at the moment) + nBCsPerTF = 32 * nBCsPerOrbit; // hard-coded 32 orbits per TF for Run3 MC (no info from ccdb at the moment) } else { auto runInfo = (!isMC) ? o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(o2::ccdb::BasicCCDBManager::instance(), run) : o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(o2::ccdb::BasicCCDBManager::instance(), run, strLPMProductionTag); @@ -615,6 +614,281 @@ class BcSelectionModule } // end processRun3 }; // end BcSelectionModule +// Occupancy calculation (used by EventSelectionModule) +// The occupancy wrt a reference point is calculated from other collisions ("sources") nearby in time: +// in the same ITS ROF, in the previous ITS ROF and in a time window around the reference. + +class OccupancyCalculator +{ + public: + // per-collision input for the occupancy calculation + struct Source { + int64_t globalBC = 0; // global BC of the collision (after the collision-BC matching) + int nTracksITS567 = 0; // number of PV contributors with >= 5 ITS clusters + int proxyNtracks = 0; // nTracksITS567, or FT0C/10 if the collision is close to an ITS ROF border + float ampFT0C = 0; // FT0C amplitude in the found BC + float vZ = 0; // z of the primary vertex + bool isRejectedByTFborderCut = false; // the found BC is close to a TF border (kNoTimeFrameBorder is not set) + }; + + // occupancy estimators and flags wrt a given reference + struct Result { + int nTracksITS567inTimeWin = 0; // (weighted) number of ITS tracks from other collisions in the time window, -1 if undefined (too close to TF borders) + float sumAmpFT0CinTimeWin = 0; // (weighted) sum of FT0C amplitudes from other collisions in the time window, -1 if undefined + float medianTime = 0; // delta time (us) at which the occupancy in the time window crosses 1/2 of its full value + bool noCollInTimeRangeNarrow = false; // no collisions within +/- TimeRangeVetoOnCollNarrow + bool noCollInTimeRangeStrict = false; // no collisions within +/- TimeRangeVetoOnCollStrict + bool noCollInTimeRangeStandard = false; // no high-mult collisions within [-4, 2] us and no collisions within the narrow range + bool noCollInRofStrict = false; // no other collisions in the same ITS ROF + bool noCollInRofStandard = false; // no collisions in the same ITS ROF with FT0C above FT0CamplPerCollCutVetoOnCollInROF + bool noCollInRofWithCloseVz = false; // no collisions in the same ITS ROF with close vZ + bool noHighMultCollInPrevRof = false; // total FT0C amplitude in the previous ITS ROF below FT0CamplPerCollCutVetoOnCollInROF + }; + + // prepare n sources (one per collision, same order as in the collision table) with all fields reset + void resetSources(size_t n) { mSources.assign(n, Source{}); } + // source i (for filling) + Source& source(int32_t i) { return mSources[i]; } + + // set run-dependent constants and configurables + template + void configure(int64_t bcSOR, int64_t nBCsPerTF, int rofOffset, int rofLength, TEvSelOpts const& opts) + { + mBcSOR = bcSOR; + mNBCsPerTF = nBCsPerTF; + mRofOffset = rofOffset; + mRofLength = rofLength; + mTimeWinMinNS = opts.confTimeIntervalForOccupancyCalculationMin.value * 1e3; // ns + mTimeWinMaxNS = opts.confTimeIntervalForOccupancyCalculationMax.value * 1e3; // ns + mTimeRangeVetoOnCollNarrow = opts.confTimeRangeVetoOnCollNarrow.value; + mTimeRangeVetoOnCollStrict = opts.confTimeRangeVetoOnCollStrict.value; + mFT0CamplCutVetoOnCollInTimeRange = opts.confFT0CamplCutVetoOnCollInTimeRange.value; + mFT0CamplCutVetoOnCollInROF = opts.confFT0CamplCutVetoOnCollInROF.value; + mEpsilonVzDiffVetoInROF = opts.confEpsilonVzDiffVetoInROF.value; + mUseWeightsForOccupancy = opts.confUseWeightsForOccupancyVariable.value; + } + + static float calcWeightForOccupancy(float dt) + { + float wOccup = 0; + if (dt >= -40 && dt < -5) // o2-linter: disable=magic-number (collisions in the past) + wOccup = 1. / 1225 * (dt + 40) * (dt + 40); // o2-linter: disable=magic-number (parabolic weight for past collisions) + else if (dt >= -5 && dt < 15) // o2-linter: disable=magic-number (collisions near a given one) + wOccup = 1; + else if (dt >= 15 && dt < 40) // o2-linter: disable=magic-number (collisions from the future) + wOccup = -0.4 / 25 * dt + 1.24; // o2-linter: disable=magic-number (linear weight for future collisions) + else if (dt >= 40 && dt < 100) // o2-linter: disable=magic-number (collisions from the distant future) + wOccup = -0.4 / 60 * dt + 0.6 + 0.8 / 3; // o2-linter: disable=magic-number (linear weight for distant future collisions) + return wOccup; + } + + // occupancy wrt mSources[iRef] (the source itself is excluded) + template + Result calculateForSource(int32_t iRef, std::map const& mapGlobalBcWithTVX, TFT0CofBC const& getFT0CofBC) + { + return calculate(mSources[iRef].globalBC, mSources[iRef].vZ, iRef - 1, iRef + 1, mapGlobalBcWithTVX, getFT0CofBC); + } + + // occupancy wrt refGlobalBC (refVz is used for the in-ROF veto on collisions with close vZ) + // sources are scanned backward starting from index iBefore and forward starting from index iAfter + // mapGlobalBcWithTVX: global BC -> BC index for colliding BCs with TVX, used for the FT0C-based estimate near TF borders + // getFT0CofBC(bcIndex, ampFT0C): returns false if there is no FT0 in this BC, otherwise sets ampFT0C + template + Result calculate(int64_t refGlobalBC, float refVz, int32_t iBefore, int32_t iAfter, std::map const& mapGlobalBcWithTVX, TFT0CofBC const& getFT0CofBC) + { + Result res; + const int32_t nSources = mSources.size(); + const int64_t tfId = getTFid(refGlobalBC); + const int64_t rofId = getRofId(refGlobalBC); + const int bcInTF = (refGlobalBC - mBcSOR) % mNBCsPerTF; + // info for occupancy in +/- windows is available (i.e. the reference is not too close to the TF borders), 10 BCs is margin + const bool isFullInfoForOccupancy = ((bcInTF - 10) * bcNS > -mTimeWinMinNS) && ((mNBCsPerTF - 10 - bcInTF) * bcNS > mTimeWinMaxNS); // o2-linter: disable=magic-number (margin in BCs) + // collisions within the time window can be close to the TF end => switch to FT0C-based estimate for them + const bool canHaveAssocCollsWithinLastDriftTime = (mNBCsPerTF - bcInTF < 4000 * 2); // o2-linter: disable=magic-number (BCs within last drift time window) + + // ### in-ROF occupancy + int nITS567tracksForSameRofVetoStrict = 0; // to veto events with other collisions in the same ITS ROF + int nCollsInRofWithFT0CAboveVetoStandard = 0; // to veto events with other collisions in the same ITS ROF, with per-collision multiplicity above threshold + int nITS567tracksForRofVetoOnCloseVz = 0; // to veto events with nearby collisions with close vZ + auto addCollInSameRof = [&](const Source& src) { + nITS567tracksForSameRofVetoStrict += src.nTracksITS567; + if (src.ampFT0C > mFT0CamplCutVetoOnCollInROF) + nCollsInRofWithFT0CAboveVetoStandard++; + if (std::fabs(src.vZ - refVz) < mEpsilonVzDiffVetoInROF) + nITS567tracksForRofVetoOnCloseVz += src.nTracksITS567; + }; + // collisions in the same TF and ROF before and after the reference + for (int32_t i = iBefore; i >= 0; i--) { + int64_t thisBC = mSources[i].globalBC; + if (getTFid(thisBC) != tfId || getRofId(thisBC) != rofId) + break; + addCollInSameRof(mSources[i]); + } + for (int32_t i = iAfter; i < nSources; i++) { + int64_t thisBC = mSources[i].globalBC; + if (getTFid(thisBC) != tfId || getRofId(thisBC) != rofId) + break; + addCollInSameRof(mSources[i]); + } + res.noCollInRofStrict = (nITS567tracksForSameRofVetoStrict == 0); + res.noCollInRofStandard = (nCollsInRofWithFT0CAboveVetoStandard == 0); + res.noCollInRofWithCloseVz = (nITS567tracksForRofVetoOnCloseVz == 0); + + // ### occupancy in the previous ROF + float totalFT0amplInPrevROF = 0; + for (int32_t i = iBefore; i >= 0; i--) { + int64_t thisBC = mSources[i].globalBC; + if (getTFid(thisBC) != tfId) + break; + int64_t thisRofId = getRofId(thisBC); + if (thisRofId == rofId - 1) + totalFT0amplInPrevROF += mSources[i].ampFT0C; + else if (thisRofId < rofId - 1) + break; + } + // veto events if FT0C amplitude in previous ITS ROF is above threshold + res.noHighMultCollInPrevRof = (totalFT0amplInPrevROF < mFT0CamplCutVetoOnCollInROF); + + // ### occupancy in the time window + int nITS567tracksInFullTimeWindow = 0; + float sumAmpFT0CInFullTimeWindow = 0; + int nITS567tracksForVetoNarrow = 0; // to veto events with nearby collisions (narrow range) + int nITS567tracksForVetoStrict = 0; // to veto events with nearby collisions + int nCollsWithFT0CAboveVetoStandard = 0; // to veto events with nearby collisions that have per-collision multiplicity above threshold + int iFirstRejectedByTFborderCut = -1; + int proxyTotalMultInTimeWin = 0; + mDeltaTimeMult.clear(); // (delta time in ns, proxy for N tracks) of collisions in the time window, for the median time + auto addCollInTimeWin = [&](int32_t i, float dtNS) { + const Source& src = mSources[i]; + mDeltaTimeMult.emplace_back(dtNS, src.proxyNtracks); + proxyTotalMultInTimeWin = proxyTotalMultInTimeWin + static_cast(src.proxyNtracks); // int + float, as in the original std::accumulate over floats + + float dt = dtNS / 1e3; // ns -> us + // counting tracks from other collisions in fixed time windows + if (std::fabs(dt) < mTimeRangeVetoOnCollNarrow) + nITS567tracksForVetoNarrow += src.proxyNtracks; + if (std::fabs(dt) < mTimeRangeVetoOnCollStrict) + nITS567tracksForVetoStrict += src.proxyNtracks; + + // veto on high-mult collisions nearby, where artificial structures in the dt-occupancy plots are observed + if (dt > -4.0 && dt < 2.0 && src.ampFT0C > mFT0CamplCutVetoOnCollInTimeRange) { // o2-linter: disable=magic-number (dt in us, time window for high-mult veto) + nCollsWithFT0CAboveVetoStandard++; + } + + // check if we are close to TF borders => N ITS tracks is not reliable, and FT0C ampl will be used for occupancy estimation (see below) + if (src.isRejectedByTFborderCut) { + if (iFirstRejectedByTFborderCut == -1) + iFirstRejectedByTFborderCut = i; + return; + } + + // weighted occupancy calc: + if (isFullInfoForOccupancy) { + float wOccup = 1.; + if (mUseWeightsForOccupancy) { + wOccup = calcWeightForOccupancy(dt); + } + nITS567tracksInFullTimeWindow += wOccup * src.proxyNtracks; + sumAmpFT0CInFullTimeWindow += wOccup * src.ampFT0C; + } + }; + // collisions in the time window before and after the reference + for (int32_t i = iBefore; i >= 0; i--) { + int64_t thisBC = mSources[i].globalBC; + if (getTFid(thisBC) != tfId) + break; + float dt = (thisBC - refGlobalBC) * bcNS; // ns + if (dt < mTimeWinMinNS) + break; + addCollInTimeWin(i, dt); + } + for (int32_t i = iAfter; i < nSources; i++) { + int64_t thisBC = mSources[i].globalBC; + if (getTFid(thisBC) != tfId) + break; + float dt = (thisBC - refGlobalBC) * bcNS; // ns + if (dt > mTimeWinMaxNS) + break; + addCollInTimeWin(i, dt); + } + + // median time for the occupancy in the time window + std::sort(mDeltaTimeMult.begin(), mDeltaTimeMult.end()); // sorts by delta time + float sumMult = 0.0; + for (const auto& [dtNS, mult] : mDeltaTimeMult) { + sumMult += mult; + if (sumMult > proxyTotalMultInTimeWin / 2.0) { + res.medianTime = dtNS / 1e3; // ns -> us + break; + } + } + for (const auto& dtMult : mDeltaTimeMult) { + LOGP(debug, "dt={} mult={}", dtMult.first, dtMult.second); + } + LOGP(debug, " --> median time = {}", res.medianTime); + + // if some collisions in the time window are close to the TF border - take FT0C amplitude instead of nTracks, using TVX BCs + if (isFullInfoForOccupancy && canHaveAssocCollsWithinLastDriftTime && iFirstRejectedByTFborderCut >= 0) { + for (auto it = mapGlobalBcWithTVX.find(mSources[iFirstRejectedByTFborderCut].globalBC); it != mapGlobalBcWithTVX.end(); ++it) { + int64_t thisFoundGlobalBC = it->first; + if (getTFid(thisFoundGlobalBC) != tfId) + break; + float dt = (thisFoundGlobalBC - refGlobalBC) * bcNS; // ns + if (dt > mTimeWinMaxNS) + break; + float multT0C = -1; + if (getFT0CofBC(it->second, multT0C)) { + float wOccup = 1.; + if (mUseWeightsForOccupancy) { + wOccup = calcWeightForOccupancy(dt / 1e3); // ns -> us + } + if (multT0C > 50.) // o2-linter: disable=magic-number (multiplicity threshold for occupancy calc) + { + nITS567tracksInFullTimeWindow += wOccup * multT0C / 10.; + sumAmpFT0CInFullTimeWindow += wOccup * multT0C; + } + } + } + } + + // protection against TF borders + if (!isFullInfoForOccupancy) { // occupancy in undefined (too close to TF borders) + nITS567tracksInFullTimeWindow = -1; + sumAmpFT0CInFullTimeWindow = -1; + } + res.nTracksITS567inTimeWin = nITS567tracksInFullTimeWindow; // occupancy by a sum of number of ITS tracks (without the reference collision) + res.sumAmpFT0CinTimeWin = sumAmpFT0CInFullTimeWindow; // occupancy by a sum of FT0C amplitudes (without the reference collision) + + // occupancy flags based on nearby collisions + res.noCollInTimeRangeNarrow = (nITS567tracksForVetoNarrow == 0); + res.noCollInTimeRangeStrict = (nITS567tracksForVetoStrict == 0); + res.noCollInTimeRangeStandard = (nCollsWithFT0CAboveVetoStandard == 0 && nITS567tracksForVetoNarrow == 0); + return res; + } + + private: + std::vector mSources; // one entry per collision, same order as in the collision table + + int64_t mBcSOR = -1; // global bc of the start of the first orbit + int64_t mNBCsPerTF = -1; // duration of TF in bcs + int mRofOffset = -1; // ITS ROF offset, in bc + int mRofLength = -1; // ITS ROF length, in bc + float mTimeWinMinNS = 0; // time window for the occupancy calculation, ns + float mTimeWinMaxNS = 0; // time window for the occupancy calculation, ns + float mTimeRangeVetoOnCollNarrow = 0; // us + float mTimeRangeVetoOnCollStrict = 0; // us + int mFT0CamplCutVetoOnCollInTimeRange = 0; // max FT0C amplitude for each nearby collision in the time range + float mFT0CamplCutVetoOnCollInROF = 0; // max FT0C amplitude for each nearby collision in the ITS ROF + float mEpsilonVzDiffVetoInROF = 0; // min distance in z to nearby collisions inside the ITS ROF, cm + bool mUseWeightsForOccupancy = true; // use delta-time weights for the occupancy estimators + + std::vector> mDeltaTimeMult; // buffer for the median time calculation (reused to avoid allocations) + + int64_t getTFid(int64_t globalBC) const { return (globalBC - mBcSOR) / mNBCsPerTF; } + int64_t getRofId(int64_t globalBC) const { return (globalBC + nBCsPerOrbit - mRofOffset) / mRofLength; } +}; // end OccupancyCalculator + +// EventSelectionModule class EventSelectionModule { public: @@ -626,7 +900,6 @@ class EventSelectionModule int run3min = 500000; int lastRun = -1; // last run number (needed to access ccdb only if run!=lastRun) std::bitset bcPatternB; // bc pattern of colliding bunches - std::vector bcsPattern; // pattern of colliding BCs int64_t bcSOR = -1; // global bc of the start of the first orbit int64_t nBCsPerTF = -1; // duration of TF in bcs, should be 128*3564 or 32*3564 @@ -640,18 +913,17 @@ class EventSelectionModule std::vector diffVzParMean; // parameterization for mean of diff vZ by FT0 vs by tracks std::vector diffVzParSigma; // parameterization for stddev of diff vZ by FT0 vs by tracks - int32_t findClosest(const int64_t globalBC, const std::map& bcs) + // first colliding BC (in ascending order) within +/- maxDiff of localBC, or -1 if none + int32_t findBCinPattern(int32_t localBC, int32_t maxDiff = 20) const // o2-linter: disable=magic-number (max BC difference to match pattern) { - auto it = bcs.lower_bound(globalBC); - int64_t bc1 = it->first; - int32_t index1 = it->second; - if (it != bcs.begin()) - --it; - int64_t bc2 = it->first; - int32_t index2 = it->second; - int64_t dbc1 = std::abs(bc1 - globalBC); - int64_t dbc2 = std::abs(bc2 - globalBC); - return (dbc1 <= dbc2) ? index1 : index2; + int32_t first = std::max(0, localBC - maxDiff); + int32_t last = std::min(nBCsPerOrbit - 1, localBC + maxDiff); + for (int32_t b = first; b <= last; b++) { + if (bcPatternB[b]) { + return b; + } + } + return -1; } // helper function to find median time in the vector of TOF or TRD-track times @@ -692,24 +964,12 @@ class EventSelectionModule return bestGlobalBC; } - float calcWeightForOccupancy(float dt) - { - float wOccup = 0; - if (dt >= -40 && dt < -5) // o2-linter: disable=magic-number (collisions in the past) - wOccup = 1. / 1225 * (dt + 40) * (dt + 40); // o2-linter: disable=magic-number (parabolic weight for past collisions) - else if (dt >= -5 && dt < 15) // o2-linter: disable=magic-number (collisions near a given one) - wOccup = 1; - else if (dt >= 15 && dt < 40) // o2-linter: disable=magic-number (collisions from the future) - wOccup = -0.4 / 25 * dt + 1.24; // o2-linter: disable=magic-number (linear weight for future collisions) - else if (dt >= 40 && dt < 100) // o2-linter: disable=magic-number (collisions from the distant future) - wOccup = -0.4 / 60 * dt + 0.6 + 0.8 / 3; // o2-linter: disable=magic-number (linear weight for distant future collisions) - return wOccup; - } - // declaration of structs here // (N.B.: will be invisible to the outside, create your own copies) o2::common::eventselection::EvselConfigurables evselOpts; + OccupancyCalculator occupancyCalc; // occupancy estimators and flags (keeps its buffers between DFs) + template void init(TContext& context, TEvSelOpts const& external_evselopts, THistoRegistry& histos, TMetadataInfo const& metadataInfo) { @@ -765,14 +1025,24 @@ class EventSelectionModule // colliding bc pattern int64_t ts = timestamps[0]; - // getForTimeStamp replaced with getSpecific to set metadata to zero - // avoids crash related to specific run number - auto grplhcif = ccdb->template getSpecific("GLO/Config/GRPLHCIF", ts); - bcPatternB = grplhcif->getBunchFilling().getBCPattern(); - bcsPattern = grplhcif->getBunchFilling().getFilledBCs(); + // GRPLHCIF is taken from AggregatedRunInfo (fetched at mid-run, without metadata) + bcPatternB = runInfo.grpLHC->getBunchFilling().getBCPattern(); if (runLightIons >= 0) { - for (uint32_t i = 0; i < bcsPattern.size(); i++) - LOGP(debug, "bcsPattern: i={} bc={}", i, bcsPattern.at(i)); + for (int32_t b = 0; b < nBCsPerOrbit; b++) { + if (bcPatternB[b]) { + LOGP(debug, "bcPatternB: colliding bc={}", b); + } + } + } + + // TEMPORARY cross-check: compare with GRPLHCIF fetched at the timestamp of the first BC (previous approach). + // Must stay after the copy above: this fetch may replace the cached object runInfo.grpLHC points to. + // getSpecific (instead of getForTimeStamp) sets metadata to zero, avoids crash related to specific run number + auto grplhcifTs = ccdb->template getSpecific("GLO/Config/GRPLHCIF", ts); + if (grplhcifTs->getBunchFilling().getBCPattern() != bcPatternB) { + LOGP(error, "EventSelectionModule: bcPatternB mismatch for run {}: runInfo.grpLHC (mid-run) vs GRPLHCIF at ts={}", run, ts); + } else { + LOGP(info, "EventSelectionModule: bcPatternB cross-check OK for run {} (ts={})", run, ts); } // extract ITS ROF parameters @@ -936,9 +1206,7 @@ class EventSelectionModule } return; } - std::vector vTracksITS567perColl(cols.size(), 0); // counter of tracks per collision for occupancy studies - std::vector vAmpFT0CperColl(cols.size(), 0); // amplitude FT0C per collision - std::vector vCollVz(cols.size(), 0); // vector with vZ positions for each collision + occupancyCalc.resetSources(cols.size()); // per-collision inputs for the occupancy calculation std::vector vIsVertexITSTPC(cols.size(), 0); // at least one of vertex contributors is ITS-TPC track std::vector vIsVertexTOFmatched(cols.size(), 0); // at least one of vertex contributors is matched to TOF std::vector vIsVertexTRDmatched(cols.size(), 0); // at least one of vertex contributors is matched to TRD @@ -964,7 +1232,7 @@ class EventSelectionModule // first loop to match collisions to TVX, also extract other per-collision information for further use for (const auto& col : cols) { int32_t colIndex = col.globalIndex(); - vCollVz[colIndex] = col.posZ(); + occupancyCalc.source(colIndex).vZ = col.posZ(); vTrackTimesTOF.clear(); vTrackTimesTRDnoTOF.clear(); @@ -974,8 +1242,8 @@ class EventSelectionModule float sumTime = 0, sumW = 0, sumHighPtTime = 0, sumHighPtW = 0; for (const auto& track : colPvTracks) { float trackTime = track.trackTime(); - if (track.itsNCls() >= 5) // o2-linter: disable=magic-number (indeed counting layers 5 6 7) - vTracksITS567perColl[colIndex]++; + if (track.itsNCls() >= 5) // o2-linter: disable=magic-number (number of ITS clusters >=5) + occupancyCalc.source(colIndex).nTracksITS567++; if (track.hasTRD()) vIsVertexTRDmatched[colIndex] = 1; if (track.hasTPC()) @@ -1019,15 +1287,10 @@ class EventSelectionModule // alternative collision-BC matching (currently: test mode, the aim is to improve pileup rejection) if (runLightIons >= 0) { foundGlobalBC = globalBC; - // find closest nominal bc in pattern - for (uint32_t i = 0; i < bcsPattern.size(); i++) { - int32_t localBC = globalBC % nBCsPerOrbit; - int32_t bcFromPattern = bcsPattern.at(i); - int64_t bcDiff = bcFromPattern - localBC; - if (std::abs(bcDiff) <= 20) { // o2-linter: disable=magic-number (max BC difference to match pattern) - foundGlobalBC = (globalBC / nBCsPerOrbit) * nBCsPerOrbit + bcFromPattern; - break; // the bc in pattern is found - } + // find nominal bc in pattern + int32_t bcFromPattern = findBCinPattern(globalBC % nBCsPerOrbit); + if (bcFromPattern >= 0) { // the bc in pattern is found + foundGlobalBC = (globalBC / nBCsPerOrbit) * nBCsPerOrbit + bcFromPattern; } // matched with TOF --> precise time, match to TVX, but keep the nominal foundGlobalBC from pattern @@ -1053,18 +1316,14 @@ class EventSelectionModule // end of if TOF-matched vertex } else { // for non-TOF and low-mult vertices, consider nearby nominal bcs - int64_t meanBC = globalBC + TMath::Nint(sumHighPtTime / sumHighPtW / bcNS); + int64_t meanBC = globalBC + (sumHighPtW > 0 ? TMath::Nint(sumHighPtTime / sumHighPtW / bcNS) : 0); int64_t bestGlobalBC = findBestGlobalBC(meanBC, evselOpts.confSigmaBCforHighPtTracks, vNcontributors[colIndex], col.posZ(), mapGlobalBcVtxZ); if (bestGlobalBC > 0) { foundGlobalBC = bestGlobalBC; - // find closest nominal bc in pattern - for (uint32_t j = 0; j < bcsPattern.size(); j++) { - int32_t bcFromPatternBest = bcsPattern.at(j); - int64_t bcDiff = bcFromPatternBest - (bestGlobalBC % nBCsPerOrbit); - if (std::abs(bcDiff) <= 20) { // o2-linter: disable=magic-number (max BC difference to match pattern) - foundGlobalBC = (bestGlobalBC / nBCsPerOrbit) * nBCsPerOrbit + bcFromPatternBest; - break; // the bc in pattern is found - } + // find nominal bc in pattern + int32_t bcFromPatternBest = findBCinPattern(bestGlobalBC % nBCsPerOrbit); + if (bcFromPatternBest >= 0) { // the bc in pattern is found + foundGlobalBC = (bestGlobalBC / nBCsPerOrbit) * nBCsPerOrbit + bcFromPatternBest; } foundBCindex = mapGlobalBcWithTVX[bestGlobalBC]; } else { // failed to find a proper TVX with small vZ difference @@ -1098,7 +1357,7 @@ class EventSelectionModule } else if (nPvTracksHighPtTPCnoTOFnoTRD > 0) { // for collisions with high-pt ITSTPC-nonTOF-nonTRD tracks // search in 3*confSigmaBCforHighPtTracks range (3*4 bcs by default) - int64_t meanBC = globalBC + TMath::Nint(sumHighPtTime / sumHighPtW / bcNS); + int64_t meanBC = globalBC + (sumHighPtW > 0 ? TMath::Nint(sumHighPtTime / sumHighPtW / bcNS) : 0); int64_t bestGlobalBC = findBestGlobalBC(meanBC, evselOpts.confSigmaBCforHighPtTracks, vNcontributors[colIndex], col.posZ(), mapGlobalBcVtxZ); if (bestGlobalBC > 0) { foundGlobalBC = bestGlobalBC; @@ -1145,300 +1404,33 @@ class EventSelectionModule } } - // pre-loop for occupancy calculation - std::vector vIsFullInfoForOccupancy(cols.size(), 0); // info for occupancy in +/- windows is available (i.e. a given coll is not too close to the TF borders) - std::vector vIsCollRejectedByTFborderCut(cols.size(), 0); // helper vector with - std::vector vCanHaveAssocCollsWithinLastDriftTime(cols.size(), 0); // to see if for some collisions in the occupancy calc (that are close to TF border) we will switch to FT0C based occupancy estimation - std::vector vProxyForCollNtracks(cols.size(), 0); // n tracks, or - if a collision is close to ITS ROF border - scaled FT0C mult - std::vector vMedianTimeForOccupancy(cols.size(), 0); // median time for the occupancy in a defined time window - - const float timeWinOccupancyCalcMinNS = evselOpts.confTimeIntervalForOccupancyCalculationMin * 1e3; // ns - const float timeWinOccupancyCalcMaxNS = evselOpts.confTimeIntervalForOccupancyCalculationMax * 1e3; // ns - - for (const auto& col : cols) { - int32_t colIndex = col.globalIndex(); - int64_t foundGlobalBC = vFoundGlobalBC[colIndex]; - int bcInTF = (foundGlobalBC - bcSOR) % nBCsPerTF; - vIsFullInfoForOccupancy[colIndex] = ((bcInTF - 10) * bcNS > -timeWinOccupancyCalcMinNS) && ((nBCsPerTF - 10 - bcInTF) * bcNS > timeWinOccupancyCalcMaxNS) ? true : false; // 10 BCs is margin - - int32_t foundBC = vFoundBCindex[colIndex]; - auto bcselEntry = bcselbuffer[foundBC]; - // check if we are close to ITS ROF borders => N ITS tracks is not reliable, and FT0C ampl can be used for occupancy estimation - // denominator for vAmpFT0CperColl is the approximate conversion factor b/n FT0C ampl and number of PV tracks after cuts - vProxyForCollNtracks[colIndex] = BITCHECK64(bcselEntry.selection, aod::evsel::kNoITSROFrameBorder) ? vTracksITS567perColl[colIndex] : vAmpFT0CperColl[colIndex] / 10.; - - if (!BITCHECK64(bcselEntry.selection, aod::evsel::kNoTimeFrameBorder)) { - vIsCollRejectedByTFborderCut[colIndex] = true; - } - if (nBCsPerTF - bcInTF < 4000 * 2) { // o2-linter: disable=magic-number (BCs within last drift time window) - vCanHaveAssocCollsWithinLastDriftTime[colIndex] = true; - } - } - - // save indices of collisions for occupancy calculation (both in ROF and in time range) - std::vector> vCollsInSameITSROF; - std::vector> vCollsInPrevITSROF; - std::vector> vCollsInTimeWin; - std::vector> vTimeDeltaForColls; // delta time wrt a given collision + // fill remaining per-collision inputs for the occupancy calculation for (const auto& col : cols) { int32_t colIndex = col.globalIndex(); - int64_t foundGlobalBC = vFoundGlobalBC[colIndex]; - auto bcselEntr = bcselbuffer[vFoundBCindex[colIndex]]; - if (bcselEntr.foundFT0Id > -1) { + OccupancyCalculator::Source& occupSrc = occupancyCalc.source(colIndex); + occupSrc.globalBC = vFoundGlobalBC[colIndex]; + const auto& bcselEntry = bcselbuffer[vFoundBCindex[colIndex]]; + if (bcselEntry.foundFT0Id > -1) { // required: explicit ft0s table - auto foundFT0 = ft0s.rawIteratorAt(bcselEntr.foundFT0Id); - vAmpFT0CperColl[colIndex] = foundFT0.sumAmpC(); - } - - int64_t tfId = (foundGlobalBC - bcSOR) / nBCsPerTF; - int64_t rofId = (foundGlobalBC + nBCsPerOrbit - rofOffset) / rofLength; - - // ### for in-ROF occupancy - std::vector vAssocCollInSameROF; - // find all collisions in the same ROF before a given collision - int32_t minColIndex = colIndex - 1; - while (minColIndex >= 0) { - int64_t thisBC = vFoundGlobalBC[minColIndex]; - // check if this is still the same TF - int64_t thisTFid = (thisBC - bcSOR) / nBCsPerTF; - if (thisTFid != tfId) - break; - // int thisRofIdInTF = (thisBC - rofOffset) / rofLength; - int64_t thisRofId = (thisBC + nBCsPerOrbit - rofOffset) / rofLength; - - // check if we are within the same ROF - if (thisRofId != rofId) - break; - vAssocCollInSameROF.push_back(minColIndex); - minColIndex--; - } - // find all collisions in the same ROF after the current one - int32_t maxColIndex = colIndex + 1; - while (maxColIndex < cols.size()) { - int64_t thisBC = vFoundGlobalBC[maxColIndex]; - int64_t thisTFid = (thisBC - bcSOR) / nBCsPerTF; - if (thisTFid != tfId) - break; - int64_t thisRofId = (thisBC + nBCsPerOrbit - rofOffset) / rofLength; - if (thisRofId != rofId) - break; - vAssocCollInSameROF.push_back(maxColIndex); - maxColIndex++; - } - vCollsInSameITSROF.push_back(vAssocCollInSameROF); - - // ### bookkeep collisions in previous ROF - std::vector vAssocCollInPrevROF; - minColIndex = colIndex - 1; - while (minColIndex >= 0) { - int64_t thisBC = vFoundGlobalBC[minColIndex]; - // check if this is still the same TF - int64_t thisTFid = (thisBC - bcSOR) / nBCsPerTF; - if (thisTFid != tfId) - break; - int64_t thisRofId = (thisBC + nBCsPerOrbit - rofOffset) / rofLength; - if (thisRofId == rofId - 1) - vAssocCollInPrevROF.push_back(minColIndex); - else if (thisRofId < rofId - 1) - break; - minColIndex--; - } - vCollsInPrevITSROF.push_back(vAssocCollInPrevROF); - - // ### for occupancy in time windows - std::vector vAssocToThisCol; - std::vector vCollsTimeDeltaWrtGivenColl; - std::vector vProxyNtracksAssocColls; // mult of associated collisions, for the median time calc - // find all collisions in time window before the current one - minColIndex = colIndex - 1; - while (minColIndex >= 0) { - int64_t thisBC = vFoundGlobalBC[minColIndex]; - // check if this is still the same TF - int64_t thisTFid = (thisBC - bcSOR) / nBCsPerTF; - if (thisTFid != tfId) - break; - float dt = (thisBC - foundGlobalBC) * bcNS; // ns - // check if we are within the chosen time range - if (dt < timeWinOccupancyCalcMinNS) - break; - vAssocToThisCol.push_back(minColIndex); - vCollsTimeDeltaWrtGivenColl.push_back(dt); - vProxyNtracksAssocColls.push_back(vProxyForCollNtracks[minColIndex]); - minColIndex--; - } - // find all collisions in time window after the current one - maxColIndex = colIndex + 1; - while (maxColIndex < cols.size()) { - int64_t thisBC = vFoundGlobalBC[maxColIndex]; - int64_t thisTFid = (thisBC - bcSOR) / nBCsPerTF; - if (thisTFid != tfId) - break; - float dt = (thisBC - foundGlobalBC) * bcNS; // ns - if (dt > timeWinOccupancyCalcMaxNS) - break; - vAssocToThisCol.push_back(maxColIndex); - vCollsTimeDeltaWrtGivenColl.push_back(dt); - vProxyNtracksAssocColls.push_back(vProxyForCollNtracks[maxColIndex]); - maxColIndex++; - } - vCollsInTimeWin.push_back(vAssocToThisCol); - vTimeDeltaForColls.push_back(vCollsTimeDeltaWrtGivenColl); - - // calculation of the median time for the occupancy in a given time window - std::vector> pairsDeltaTimeMult; - for (size_t iCol = 0; iCol < vCollsTimeDeltaWrtGivenColl.size(); iCol++) - pairsDeltaTimeMult.emplace_back(vCollsTimeDeltaWrtGivenColl[iCol], vProxyNtracksAssocColls[iCol]); - std::sort(pairsDeltaTimeMult.begin(), pairsDeltaTimeMult.end()); // sorts by first element by default - int proxyTotalMultInTimeWin = std::accumulate(vProxyNtracksAssocColls.begin(), vProxyNtracksAssocColls.end(), 0); - - float sumMult = 0.0; - for (size_t iCol = 0; iCol < vCollsTimeDeltaWrtGivenColl.size(); iCol++) { - sumMult += pairsDeltaTimeMult[iCol].second; - if (sumMult > proxyTotalMultInTimeWin / 2.0) { - vMedianTimeForOccupancy[colIndex] = pairsDeltaTimeMult[iCol].first / 1e3; // ns -> us - break; - } - } - for (size_t iCol = 0; iCol < vCollsTimeDeltaWrtGivenColl.size(); iCol++) { - LOGP(debug, "dt={} mult={}", pairsDeltaTimeMult[iCol].first, pairsDeltaTimeMult[iCol].second); + auto foundFT0 = ft0s.rawIteratorAt(bcselEntry.foundFT0Id); + occupSrc.ampFT0C = foundFT0.sumAmpC(); } - LOGP(debug, " --> median time = {}", vMedianTimeForOccupancy[colIndex]); + // check if we are close to ITS ROF borders => N ITS tracks is not reliable, and FT0C ampl can be used for occupancy estimation + // denominator for ampFT0C is the approximate conversion factor b/n FT0C ampl and number of PV tracks after cuts + occupSrc.proxyNtracks = BITCHECK64(bcselEntry.selection, aod::evsel::kNoITSROFrameBorder) ? occupSrc.nTracksITS567 : occupSrc.ampFT0C / 10.; + occupSrc.isRejectedByTFborderCut = !BITCHECK64(bcselEntry.selection, aod::evsel::kNoTimeFrameBorder); } + occupancyCalc.configure(bcSOR, nBCsPerTF, rofOffset, rofLength, evselOpts); - // perform the occupancy calculation per ITS ROF and also in the pre-defined time window - std::vector vNumTracksITS567inFullTimeWin(cols.size(), 0); // counter of tracks in full time window for occupancy studies (excluding given event) - std::vector vSumAmpFT0CinFullTimeWin(cols.size(), 0); // sum of FT0C of tracks in full time window for occupancy studies (excluding given event) - - std::vector vNoCollInTimeRangeStrict(cols.size(), 0); // no collisions in a specified time range - std::vector vNoCollInTimeRangeNarrow(cols.size(), 0); // no collisions in a specified time range (narrow) - std::vector vNoHighMultCollInTimeRange(cols.size(), 0); // no high-mult collisions in a specified time range - - std::vector vNoCollInSameRofStrict(cols.size(), 0); // to veto events with other collisions in the same ITS ROF - std::vector vNoCollInSameRofStandard(cols.size(), 0); // to veto events with other collisions in the same ITS ROF, with per-collision multiplicity above threshold - std::vector vNoCollInSameRofWithCloseVz(cols.size(), 0); // to veto events with nearby collisions with close vZ - std::vector vNoHighMultCollInPrevRof(cols.size(), 0); // veto events if FT0C amplitude in previous ITS ROF is above threshold - - for (const auto& col : cols) { - int32_t colIndex = col.globalIndex(); - float vZ = col.posZ(); - - // ### in-ROF occupancy - const std::vector& vAssocCollInSameROF = vCollsInSameITSROF[colIndex]; - int nITS567tracksForSameRofVetoStrict = 0; // to veto events with other collisions in the same ITS ROF - int nCollsInRofWithFT0CAboveVetoStandard = 0; // to veto events with other collisions in the same ITS ROF, with per-collision multiplicity above threshold - int nITS567tracksForRofVetoOnCloseVz = 0; // to veto events with nearby collisions with close vZ - for (uint32_t iCol = 0; iCol < vAssocCollInSameROF.size(); iCol++) { - int thisColIndex = vAssocCollInSameROF[iCol]; - nITS567tracksForSameRofVetoStrict += vTracksITS567perColl[thisColIndex]; - if (vAmpFT0CperColl[thisColIndex] > evselOpts.confFT0CamplCutVetoOnCollInROF) - nCollsInRofWithFT0CAboveVetoStandard++; - if (std::fabs(vCollVz[thisColIndex] - vZ) < evselOpts.confEpsilonVzDiffVetoInROF) - nITS567tracksForRofVetoOnCloseVz += vTracksITS567perColl[thisColIndex]; - } - // in-ROF occupancy flags - vNoCollInSameRofStrict[colIndex] = (nITS567tracksForSameRofVetoStrict == 0); - vNoCollInSameRofStandard[colIndex] = (nCollsInRofWithFT0CAboveVetoStandard == 0); - vNoCollInSameRofWithCloseVz[colIndex] = (nITS567tracksForRofVetoOnCloseVz == 0); - - // ### occupancy in previous ROF - const std::vector& vAssocCollInPrevROF = vCollsInPrevITSROF[colIndex]; - float totalFT0amplInPrevROF = 0; - for (uint32_t iCol = 0; iCol < vAssocCollInPrevROF.size(); iCol++) { - int thisColIndex = vAssocCollInPrevROF[iCol]; - totalFT0amplInPrevROF += vAmpFT0CperColl[thisColIndex]; + // FT0C amplitude in a given BC, for the occupancy estimation near TF borders + auto getFT0CofBC = [&bcs](int32_t bcIndex, float& ampFT0C) { + auto bc = bcs.iteratorAt(bcIndex); + if (!bc.has_ft0()) { + return false; } - // veto events if FT0C amplitude in previous ITS ROF is above threshold - vNoHighMultCollInPrevRof[colIndex] = (totalFT0amplInPrevROF < evselOpts.confFT0CamplCutVetoOnCollInROF); - - // ### occupancy in time windows - const std::vector& vAssocToThisCol = vCollsInTimeWin[colIndex]; - const std::vector& vCollsTimeDeltaWrtGivenColl = vTimeDeltaForColls[colIndex]; - int nITS567tracksInFullTimeWindow = 0; - float sumAmpFT0CInFullTimeWindow = 0; - int nITS567tracksForVetoNarrow = 0; // to veto events with nearby collisions (narrow range) with per-collision multiplicity above threshold - int nITS567tracksForVetoStrict = 0; // to veto events with nearby collisions - int nCollsWithFT0CAboveVetoStandard = 0; // to veto events with nearby collisions that have per-collision multiplicity above threshold - int colIndexFirstRejectedByTFborderCut = -1; - for (uint32_t iCol = 0; iCol < vAssocToThisCol.size(); iCol++) { - int thisColIndex = vAssocToThisCol[iCol]; - float dt = vCollsTimeDeltaWrtGivenColl[iCol] / 1e3; // ns -> us - // counting tracks from other collisions in fixed time windows - if (std::fabs(dt) < evselOpts.confTimeRangeVetoOnCollNarrow) - nITS567tracksForVetoNarrow += vProxyForCollNtracks[thisColIndex]; - if (std::fabs(dt) < evselOpts.confTimeRangeVetoOnCollStrict) - nITS567tracksForVetoStrict += vProxyForCollNtracks[thisColIndex]; - - // veto on high-mult collisions nearby, where artificial structures in the dt-occupancy plots are observed - if (dt > -4.0 && dt < 2.0 && vAmpFT0CperColl[thisColIndex] > evselOpts.confFT0CamplCutVetoOnCollInTimeRange) { // o2-linter: disable=magic-number (dt in us, time window for high-mult veto) - nCollsWithFT0CAboveVetoStandard++; - } - - // check if we are close to TF borders => N ITS tracks is not reliable, and FT0C ampl will be used for occupancy estimation (a loop below) - if (vIsCollRejectedByTFborderCut[thisColIndex]) { - if (colIndexFirstRejectedByTFborderCut == -1) - colIndexFirstRejectedByTFborderCut = thisColIndex; - continue; - } - - // weighted occupancy calc: - if (vIsFullInfoForOccupancy[colIndex]) { - float wOccup = 1.; - if (evselOpts.confUseWeightsForOccupancyVariable) { - // weighted occupancy - wOccup = calcWeightForOccupancy(dt); - } - nITS567tracksInFullTimeWindow += wOccup * vProxyForCollNtracks[thisColIndex]; - sumAmpFT0CInFullTimeWindow += wOccup * vAmpFT0CperColl[thisColIndex]; - } - } - - // if some associated collisions are close to TF border - take FT0C amplitude instead of nTracks, using BC table - if (vIsFullInfoForOccupancy[colIndex] && vCanHaveAssocCollsWithinLastDriftTime[colIndex] && colIndexFirstRejectedByTFborderCut >= 0) { - int64_t foundGlobalBC = vFoundGlobalBC[colIndex]; - int64_t tfId = (foundGlobalBC - bcSOR) / nBCsPerTF; - std::map::iterator it = mapGlobalBcWithTVX.find(vFoundGlobalBC[colIndexFirstRejectedByTFborderCut]); - while (it != mapGlobalBcWithTVX.end()) { - int64_t thisFoundGlobalBC = it->first; - int32_t thisFoundBCindex = it->second; - auto bc = bcs.iteratorAt(thisFoundBCindex); - int64_t thisTFid = (bc.globalBC() - bcSOR) / nBCsPerTF; - if (thisTFid != tfId) - break; - - float dt = (thisFoundGlobalBC - foundGlobalBC) * bcNS; // ns - if (dt > timeWinOccupancyCalcMaxNS) - break; - - float multT0C = -1; - if (bc.has_ft0()) { - multT0C = bc.ft0().sumAmpC(); - float wOccup = 1.; - if (evselOpts.confUseWeightsForOccupancyVariable) { - wOccup = calcWeightForOccupancy(dt / 1e3); // ns -> us - } - if (multT0C > 50.) // o2-linter: disable=magic-number (multiplicity threshold for occupancy calc) - { - nITS567tracksInFullTimeWindow += wOccup * multT0C / 10.; - sumAmpFT0CInFullTimeWindow += wOccup * multT0C; - } - } - it++; - } - } - - // protection against TF borders - if (!vIsFullInfoForOccupancy[colIndex]) { // occupancy in undefined (too close to TF borders) - nITS567tracksInFullTimeWindow = -1; - sumAmpFT0CInFullTimeWindow = -1; - } - - vNumTracksITS567inFullTimeWin[colIndex] = nITS567tracksInFullTimeWindow; // occupancy by a sum of number of ITS tracks (without a current collision) - vSumAmpFT0CinFullTimeWin[colIndex] = sumAmpFT0CInFullTimeWindow; // occupancy by a sum of FT0C amplitudes (without a current collision) - // occupancy flags based on nearby collisions - vNoCollInTimeRangeNarrow[colIndex] = (nITS567tracksForVetoNarrow == 0); - vNoCollInTimeRangeStrict[colIndex] = (nITS567tracksForVetoStrict == 0); - vNoHighMultCollInTimeRange[colIndex] = (nCollsWithFT0CAboveVetoStandard == 0 && nITS567tracksForVetoNarrow == 0); - } // end of the occupancy calculation + ampFT0C = bc.ft0().sumAmpC(); + return true; + }; for (const auto& col : cols) { int32_t colIndex = col.globalIndex(); @@ -1450,6 +1442,9 @@ class EventSelectionModule int32_t foundFDD = bcselEntry.foundFDDId; int32_t foundZDC = bcselEntry.foundZDCId; + // occupancy estimators and flags wrt this collision + const OccupancyCalculator::Result occ = occupancyCalc.calculateForSource(colIndex, mapGlobalBcWithTVX, getFT0CofBC); + // compare zVtx from FT0 and from PV bool isGoodZvtxFT0vsPV = 0; if (bcselEntry.foundFT0Id > -1) { @@ -1488,14 +1483,14 @@ class EventSelectionModule selection |= isGoodZvtxFT0vsPV ? BIT(aod::evsel::kIsGoodZvtxFT0vsPV) : 0; // selection bits based on occupancy time pattern - selection |= vNoCollInTimeRangeNarrow[colIndex] ? BIT(aod::evsel::kNoCollInTimeRangeNarrow) : 0; - selection |= vNoCollInTimeRangeStrict[colIndex] ? BIT(aod::evsel::kNoCollInTimeRangeStrict) : 0; - selection |= vNoHighMultCollInTimeRange[colIndex] ? BIT(aod::evsel::kNoCollInTimeRangeStandard) : 0; + selection |= occ.noCollInTimeRangeNarrow ? BIT(aod::evsel::kNoCollInTimeRangeNarrow) : 0; + selection |= occ.noCollInTimeRangeStrict ? BIT(aod::evsel::kNoCollInTimeRangeStrict) : 0; + selection |= occ.noCollInTimeRangeStandard ? BIT(aod::evsel::kNoCollInTimeRangeStandard) : 0; // selection bits based on ITS in-ROF occupancy - selection |= vNoCollInSameRofStrict[colIndex] ? BIT(aod::evsel::kNoCollInRofStrict) : 0; - selection |= (vNoCollInSameRofStandard[colIndex] && vNoCollInSameRofWithCloseVz[colIndex]) ? BIT(aod::evsel::kNoCollInRofStandard) : 0; - selection |= vNoHighMultCollInPrevRof[colIndex] ? BIT(aod::evsel::kNoHighMultCollInPrevRof) : 0; + selection |= occ.noCollInRofStrict ? BIT(aod::evsel::kNoCollInRofStrict) : 0; + selection |= (occ.noCollInRofStandard && occ.noCollInRofWithCloseVz) ? BIT(aod::evsel::kNoCollInRofStandard) : 0; + selection |= occ.noHighMultCollInPrevRof ? BIT(aod::evsel::kNoHighMultCollInPrevRof) : 0; // copy rct flags from bcsel table uint32_t rct = bcselEntry.rct; @@ -1522,7 +1517,7 @@ class EventSelectionModule } evsel(alias, selection, rct, sel7, sel8, foundBC, foundFT0, foundFV0, foundFDD, foundZDC, - vNumTracksITS567inFullTimeWin[colIndex], vSumAmpFT0CinFullTimeWin[colIndex], vMedianTimeForOccupancy[colIndex]); + occ.nTracksITS567inTimeWin, occ.sumAmpFT0CinTimeWin, occ.medianTime); } } // end processRun3 }; // end EventSelectionModule diff --git a/Common/Tools/Multiplicity/MultModule.h b/Common/Tools/Multiplicity/MultModule.h index 3c9151ca78c..b63af1bc3c5 100644 --- a/Common/Tools/Multiplicity/MultModule.h +++ b/Common/Tools/Multiplicity/MultModule.h @@ -36,6 +36,7 @@ #include #include +#include #include #include #include @@ -345,7 +346,7 @@ struct standardConfigurables : o2::framework::ConfigurableGroup { o2::framework::Configurable reconstructionPass{"reconstructionPass", "", {"Apass to use when fetching the calibration tables. Empty (default) does not check for any pass. Use `metadata` to fetch it from the AO2D metadata. Otherwise it will override the metadata."}}; // centrality operation - o2::framework::Configurable generatorName{"generatorName", "", {"Specify if and only if this is MC. Typical: PYTHIA"}}; + o2::framework::Configurable generatorName{"generatorName", "", {"MC generator name for centrality calibration (e.g. PYTHIA, EPOS). Empty: autodetect from metadata in Run 3, falling back to PYTHIA. Specify if and only if this is MC to override autodetect from metadata"}}; o2::framework::Configurable embedINELgtZEROselection{"embedINELgtZEROselection", false, {"Option to do percentile 100.5 if not INELgtZERO"}}; }; @@ -480,7 +481,7 @@ class MultModule LOGF(info, "Metadata information: isMC? %i", metadataInfo.isMC()); const auto& workflows = context.services().template get(); - TString listOfRequestors[nTablesConst]; + std::array listOfRequestors{}; for (int i = 0; i < nTablesConst; i++) { int f = internalOpts.enabledTables->get(tableNames[i].c_str(), "enable"); if (f == 1) { @@ -687,14 +688,14 @@ class MultModule } if (lCalibObjects) { - hVtxZFV0A = static_cast(lCalibObjects->FindObject("hVtxZFV0A")); - hVtxZFT0A = static_cast(lCalibObjects->FindObject("hVtxZFT0A")); - hVtxZFT0C = static_cast(lCalibObjects->FindObject("hVtxZFT0C")); - hVtxZFDDA = static_cast(lCalibObjects->FindObject("hVtxZFDDA")); - hVtxZFDDC = static_cast(lCalibObjects->FindObject("hVtxZFDDC")); - hVtxZNTracks = static_cast(lCalibObjects->FindObject("hVtxZNTracksPV")); - hVtxZNMFTTracks = static_cast(lCalibObjects->FindObject("hVtxZMFT")); - hVtxZNGlobalTracks = static_cast(lCalibObjects->FindObject("hVtxZNGlobals")); + hVtxZFV0A = dynamic_cast(lCalibObjects->FindObject("hVtxZFV0A")); + hVtxZFT0A = dynamic_cast(lCalibObjects->FindObject("hVtxZFT0A")); + hVtxZFT0C = dynamic_cast(lCalibObjects->FindObject("hVtxZFT0C")); + hVtxZFDDA = dynamic_cast(lCalibObjects->FindObject("hVtxZFDDA")); + hVtxZFDDC = dynamic_cast(lCalibObjects->FindObject("hVtxZFDDC")); + hVtxZNTracks = dynamic_cast(lCalibObjects->FindObject("hVtxZNTracksPV")); + hVtxZNMFTTracks = dynamic_cast(lCalibObjects->FindObject("hVtxZMFT")); + hVtxZNGlobalTracks = dynamic_cast(lCalibObjects->FindObject("hVtxZNGlobals")); lCalibLoaded = true; // Capture error if (!hVtxZFV0A || !hVtxZFT0A || !hVtxZFT0C || !hVtxZFDDA || !hVtxZFDDC || !hVtxZNTracks) { @@ -1097,11 +1098,11 @@ class MultModule Run2CL1Info.mCalibrationStored = false; if (callst != nullptr) { auto getccdb = [callst](const char* ccdbhname) { - TH1* h = reinterpret_cast(callst->FindObject(ccdbhname)); + TH1* h = dynamic_cast(callst->FindObject(ccdbhname)); return h; }; auto getformulaccdb = [callst](const char* ccdbhname) { - TFormula* f = reinterpret_cast(callst->FindObject(ccdbhname)); + TFormula* f = dynamic_cast(callst->FindObject(ccdbhname)); return f; }; @@ -1198,16 +1199,10 @@ class MultModule mRunNumberCentrality = bc.runNumber(); // mark that this run has been attempted already regardless of outcome LOGF(info, "centrality loading procedure for timestamp=%llu, run number=%d", bc.timestamp(), bc.runNumber()); - // capture the need for PYTHIA calibration in Pb-Pb runs - if (metadataInfo.isMC() && mRunNumber >= 544013 && mRunNumber <= 545367 && internalOpts.generatorName.value.empty()) { - LOGF(info, "This is MC for Pb-Pb. Setting generatorName automatically to PYTHIA"); - internalOpts.generatorName.value = "PYTHIA"; - } - - // capture the need for PYTHIA calibration in light ion runs automatically - if (metadataInfo.isMC() && mRunNumber >= 564250 && mRunNumber <= 564472 && internalOpts.generatorName.value.empty()) { - LOGF(info, "This is MC for light ion runs. Setting generatorName automatically to PYTHIA"); - internalOpts.generatorName.value = "PYTHIA"; + // autodetect the generator from the metadata if not explicitly configured + if (metadataInfo.isMC() && internalOpts.generatorName.value.empty()) { + internalOpts.generatorName.value = metadataInfo.getGenerator(); + LOGF(info, "Setting generatorName automatically from metadata to %s", internalOpts.generatorName.value); } LOGF(info, "centrality loading procedure for timestamp=%llu, run number=%d", bc.timestamp(), bc.runNumber()); @@ -1252,8 +1247,8 @@ class MultModule if (callst != nullptr) { LOGF(info, "Getting new histograms with %d run number for %d run number", mRunNumber, bc.runNumber()); auto getccdb = [callst, bc](struct CalibrationInfo& estimator, const o2::framework::Configurable& generatorName) { // TODO: to consider the name inside the estimator structure - estimator.mhMultSelCalib = reinterpret_cast(callst->FindObject(TString::Format("hCalibZeq%s", estimator.name.c_str()).Data())); - estimator.mMCScale = reinterpret_cast(callst->FindObject(TString::Format("%s-%s", generatorName->c_str(), estimator.name.c_str()).Data())); + estimator.mhMultSelCalib = dynamic_cast(callst->FindObject(TString::Format("hCalibZeq%s", estimator.name.c_str()).Data())); + estimator.mMCScale = dynamic_cast(callst->FindObject(TString::Format("%s-%s", generatorName->c_str(), estimator.name.c_str()).Data())); if (estimator.mhMultSelCalib != nullptr) { if (generatorName->length() != 0) { LOGF(info, "Retrieving MC calibration for %d, generator name: %s", bc.runNumber(), generatorName->c_str()); diff --git a/Common/Tools/Multiplicity/macros/runCalibration.C b/Common/Tools/Multiplicity/macros/runCalibration.C index 0620bb5e1db..2264df723db 100644 --- a/Common/Tools/Multiplicity/macros/runCalibration.C +++ b/Common/Tools/Multiplicity/macros/runCalibration.C @@ -38,12 +38,12 @@ void runCalibration(const TString& lInputFileName = "results/AR_544122_glauberNB TFile* file = new TFile(lInputFileName.Data(), "READ"); file->ls(); - TH1F* hData = (TH1F*)file->Get("hV0MUltraFine"); - TH1F* hGlauberParameters = (TH1F*)file->Get("hGlauberParameters"); - TH1F* hGlauberFitRange = (TH1F*)file->Get("hGlauberFitRange"); + TH1F* hData = dynamic_cast(file->Get("hV0MUltraFine")); + TH1F* hGlauberParameters = dynamic_cast(file->Get("hGlauberParameters")); + [[maybe_unused]] TH1F* hGlauberFitRange = dynamic_cast(file->Get("hGlauberFitRange")); hData->SetName("hData"); - TH1F* hStitched = (TH1F*)hData->Clone("hStitched"); - TH1F* hFit = (TH1F*)file->Get("hGlauber"); + TH1F* hStitched = dynamic_cast(hData->Clone("hStitched")); + TH1F* hFit = dynamic_cast(file->Get("hGlauber")); TCanvas* c1 = new TCanvas("c1", "", 800, 600); c1->SetLeftMargin(0.17); @@ -160,7 +160,7 @@ void runCalibration(const TString& lInputFileName = "results/AR_544122_glauberNB // Step 1: open the (Npart, Ncoll) pair information, provide TFile* fbasefile = new TFile("basehistos.root", "READ"); - TH2D* hNpNc = (TH2D*)fbasefile->Get("hNpNc"); + TH2D* hNpNc = dynamic_cast(fbasefile->Get("hNpNc")); g->SetNpartNcollCorrelation(hNpNc); g->InitializeNpNc(); diff --git a/Common/Tools/Multiplicity/macros/runGlauberFit.C b/Common/Tools/Multiplicity/macros/runGlauberFit.C index 5c07a000405..ea4b33c8a48 100644 --- a/Common/Tools/Multiplicity/macros/runGlauberFit.C +++ b/Common/Tools/Multiplicity/macros/runGlauberFit.C @@ -39,9 +39,9 @@ Double_t FastIntegrate(TF1* f1, Double_t a, Double_t b, Int_t n = 5) // Do fast integration with N sampling points const Int_t nc = n; Double_t x[nc], y[nc]; - Double_t lWidth = (b - a) / ((double)(n - 1)); + Double_t lWidth = (b - a) / (static_cast(n - 1)); for (Int_t ii = 0; ii < n; ii++) { - x[ii] = a + ((double)(ii)) * lWidth; + x[ii] = a + (static_cast(ii)) * lWidth; y[ii] = f1->Eval(x[ii]); } // Now go via trapezoids, please (this probably has a name) @@ -98,7 +98,7 @@ int runGlauberFit(const TString& lInputFileName = "AnalysisResultsLHC24ar.root", std::cout << "Problem with file!" << std::endl; TH1F* hV0Mfine = 0x0; - hV0Mfine = (TH1F*)file->Get(Form("centrality-study/%s", histogramName.Data())); + hV0Mfine = dynamic_cast(file->Get(Form("centrality-study/%s", histogramName.Data()))); // disregard bin zero std::cout << "Received bin zero content: " << hV0Mfine->GetBinContent(0) << ", will set to zero..." << std::endl; @@ -124,8 +124,8 @@ int runGlauberFit(const TString& lInputFileName = "AnalysisResultsLHC24ar.root", TFile* fOutput = new TFile(outputFile.Data(), "RECREATE"); - TH1F* hV0M = (TH1F*)hV0Mfine->Clone("hV0M"); - TH1F* hV0MUltraFine = (TH1F*)hV0Mfine->Clone("hV0MUltraFine"); + TH1F* hV0M = dynamic_cast(hV0Mfine->Clone("hV0M")); + TH1F* hV0MUltraFine = dynamic_cast(hV0Mfine->Clone("hV0MUltraFine")); hV0M->SetName("hV0M"); hV0M->SetTitle(""); @@ -210,7 +210,7 @@ int runGlauberFit(const TString& lInputFileName = "AnalysisResultsLHC24ar.root", // Step 1: open the (Npart, Ncoll) pair information, provide TFile* fbasefile = new TFile("basehistos.root", "READ"); - TH2D* hNpNc = (TH2D*)fbasefile->Get("hNpNc"); + TH2D* hNpNc = dynamic_cast(fbasefile->Get("hNpNc")); // return to proper scope fOutput->cd(); g->SetNpartNcollCorrelation(hNpNc); @@ -291,8 +291,8 @@ int runGlauberFit(const TString& lInputFileName = "AnalysisResultsLHC24ar.root", gStyle->SetOptStat(0); // Do a ratio plot - TH1D* hGlauber = (TH1D*)hV0MUltraFine->Clone("hGlauber"); - TH1D* hRatio = (TH1D*)hV0MUltraFine->Clone("hRatio"); + TH1D* hGlauber = dynamic_cast(hV0MUltraFine->Clone("hGlauber")); + TH1D* hRatio = dynamic_cast(hV0MUltraFine->Clone("hRatio")); hGlauber->Reset(); c1->cd(1); @@ -345,7 +345,7 @@ int runGlauberFit(const TString& lInputFileName = "AnalysisResultsLHC24ar.root", lFitRangeLine->SetLineStyle(2); lFitRangeLine->Draw(); - TH1D* hRatioGrayed = (TH1D*)hRatio->Clone("hRatioGrayed"); + TH1D* hRatioGrayed = dynamic_cast(hRatio->Clone("hRatioGrayed")); hRatioGrayed->SetMarkerColor(kGray + 2); hRatioGrayed->SetLineColor(kGray + 2); hRatioGrayed->Draw("same"); @@ -357,8 +357,8 @@ int runGlauberFit(const TString& lInputFileName = "AnalysisResultsLHC24ar.root", c1->cd(1); TLatex* lat = new TLatex(); lat->SetNDC(); - Float_t lPosText = 0.76; - Float_t lYShift = 0.25; + // Float_t lPosText = 0.76; // FIXME: unused + // Float_t lYShift = 0.25; // FIXME: unused lat->SetTextSize(0.042); // save the glauber parameters explicitly diff --git a/Common/Tools/Multiplicity/macros/saveCorrelation.C b/Common/Tools/Multiplicity/macros/saveCorrelation.C index 73bf8d30f29..ed0376fab54 100644 --- a/Common/Tools/Multiplicity/macros/saveCorrelation.C +++ b/Common/Tools/Multiplicity/macros/saveCorrelation.C @@ -29,23 +29,23 @@ void saveCorrelation(const TString& filename = "gmc-PbPb-snn68.21-md0.40-nd-1.0-rc1-smax99.0.root", const TString& outputFile = "basehistos.root") { TFile* fin = new TFile(filename.Data(), "READ"); - TNtuple* ntup = (TNtuple*)fin->Get("nt_Pb_Pb"); + TNtuple* ntup = dynamic_cast(fin->Get("nt_Pb_Pb")); // try other Pb nuclear profiles in case "Pb" - "Pb" not found if (!ntup) { - ntup = (TNtuple*)fin->Get("nt_Pbpn_Pbpn"); + ntup = dynamic_cast(fin->Get("nt_Pbpn_Pbpn")); } if (!ntup) { - ntup = (TNtuple*)fin->Get("nt_PbpnVar1_PbpnVar1"); + ntup = dynamic_cast(fin->Get("nt_PbpnVar1_PbpnVar1")); } if (!ntup) { - ntup = (TNtuple*)fin->Get("nt_PbpnVar2_PbpnVar2"); + ntup = dynamic_cast(fin->Get("nt_PbpnVar2_PbpnVar2")); } if (!ntup) { - ntup = (TNtuple*)fin->Get("nt_PbpnVar3_PbpnVar3"); + ntup = dynamic_cast(fin->Get("nt_PbpnVar3_PbpnVar3")); } if (!ntup) { - ntup = (TNtuple*)fin->Get("nt_PbpnVar4_PbpnVar4"); + ntup = dynamic_cast(fin->Get("nt_PbpnVar4_PbpnVar4")); } if (!ntup) { @@ -59,6 +59,7 @@ void saveCorrelation(const TString& filename = "gmc-PbPb-snn68.21-md0.40-nd-1.0- // 2D correlation plot necessary for Glauber + NBD fitting // The provided range should be enough for Pb-Pb + // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores,clang-diagnostic-unused-variable) TH2D* hNpNc = new TH2D("hNpNc", "", 500, -0.5, 499.5, 2500, -0.5, 2499.5); // let's draw this on screen for inspection diff --git a/Common/Tools/Multiplicity/multCalibrator.cxx b/Common/Tools/Multiplicity/multCalibrator.cxx index d65f615d584..8d25df8e32e 100644 --- a/Common/Tools/Multiplicity/multCalibrator.cxx +++ b/Common/Tools/Multiplicity/multCalibrator.cxx @@ -30,6 +30,7 @@ #include #include // FIXME +#include using namespace std; @@ -107,7 +108,7 @@ Bool_t multCalibrator::Calibrate() // Step 1: verify if input file contains desired histograms TH1D* hRaw[kNCentEstim]; for (Int_t iv = 0; iv < kNCentEstim; iv++) { - hRaw[iv] = reinterpret_cast(fileInput->Get(Form("multiplicity-qa/multiplicityQa/h%s", fCentEstimName[iv].Data()))); + hRaw[iv] = dynamic_cast(fileInput->Get(Form("multiplicity-qa/multiplicityQa/h%s", fCentEstimName[iv].Data()))); if (!hRaw[iv]) { cout << Form("File does not contain histogram h%s, which is necessary for calibration!", fCentEstimName[iv].Data()) << endl; return kFALSE; @@ -353,8 +354,8 @@ TH1F* multCalibrator::GetCalibrationHistogram(TH1* histoRaw, const TString& lHis // place squarely at the middle to ensure it's all fine lMiddleOfBins[lB - 1] = 0.5 * (lDesiredBoundaries[lB] + lDesiredBoundaries[lB - 1]); } - Double_t lBounds[lNDesiredBoundaries + 1]; - Double_t lPrecision[lNDesiredBoundaries + 1]; + std::vector lBounds(lNDesiredBoundaries + 1); + std::vector lPrecision(lNDesiredBoundaries + 1); if (fAnchorPointValue > 0) { lBounds[0] = 0; @@ -382,7 +383,7 @@ TH1F* multCalibrator::GetCalibrationHistogram(TH1* histoRaw, const TString& lHis cout << histoRaw->GetName() << " boundaries, percentile: " << lDesiredBoundaries[ii] << "%\t Signal value = " << lBounds[lDisplacedii] << "\tprecision = " << lPrecision[ii] << "% " << lPrecisionString.Data() << endl; } } - TH1F* hCalib = new TH1F(lHistoName.Data(), "", fAnchorPointValue < 0 ? lNDesiredBoundaries - 1 : lNDesiredBoundaries, lBounds); + TH1F* hCalib = new TH1F(lHistoName.Data(), "", fAnchorPointValue < 0 ? lNDesiredBoundaries - 1 : lNDesiredBoundaries, lBounds.data()); hCalib->SetDirectory(0); hCalib->SetBinContent(0, 100.5); hCalib->SetBinContent(1, 100.5); diff --git a/Common/Tools/Multiplicity/multGlauberNBDFitter.cxx b/Common/Tools/Multiplicity/multGlauberNBDFitter.cxx index e3c4635533d..c5d067c7e22 100644 --- a/Common/Tools/Multiplicity/multGlauberNBDFitter.cxx +++ b/Common/Tools/Multiplicity/multGlauberNBDFitter.cxx @@ -443,7 +443,20 @@ double multGlauberNBDFitter::ContinuousNBD(const double n, const double mu, cons return F; } -void multGlauberNBDFitter::CalculateAvNpNc(TProfile* lNPartProf, TProfile* lNCollProf, TH2F* lNPart2DPlot, TH2F* lNColl2DPlot, TH1F* hPercentileMap, double lLoRange, double lHiRange, TH3D* lNpNcEcc, TH2F* lEcc2DPlot, TH3D* lNpNcB, TH2F* lB2DPlot, TH2F* lNancestor2DPlot, double fProbabilityCutoff) +void multGlauberNBDFitter::CalculateAvNpNc(TProfile* lNPartProf, + TProfile* lNCollProf, + TH2F* lNPart2DPlot, + TH2F* lNColl2DPlot, + TH1F* hPercentileMap, + double lLoRange, + double lHiRange, + TH3D* lNpNcEcc, + TH2F* lEcc2DPlot, + TH3D* lNpNcB, + TH2F* lB2DPlot, + TH2F* lNancestor2DPlot, + TH2F* lMult2DPlot, + double fProbabilityCutoff) { LOG(info) << "Calculating , in centrality bins..."; LOG(info) << "Range to calculate: " << lLoRange << " to " << lHiRange; @@ -557,6 +570,9 @@ void multGlauberNBDFitter::CalculateAvNpNc(TProfile* lNPartProf, TProfile* lNCol // fill cross-check histogram with lNancestorCount at lNancestors value lNancestor2DPlot->Fill(lMultValueToFill, lNancestors, lProbability); } + if (lMult2DPlot) { + lMult2DPlot->Fill(lMultValueToFill, lMultValue, lProbability); + } if (lNPart2DPlot) { lNPart2DPlot->Fill(lMultValueToFill, fNpart[ibin], lProbability); } diff --git a/Common/Tools/Multiplicity/multGlauberNBDFitter.h b/Common/Tools/Multiplicity/multGlauberNBDFitter.h index 7bf8c5a0346..3c5f6370722 100644 --- a/Common/Tools/Multiplicity/multGlauberNBDFitter.h +++ b/Common/Tools/Multiplicity/multGlauberNBDFitter.h @@ -131,7 +131,20 @@ class multGlauberNBDFitter : public TNamed // For estimating Npart, Ncoll in multiplicity bins // also viable: eccentricity, impact parameter, ancestor cross-check plot - void CalculateAvNpNc(TProfile* lNPartProf, TProfile* lNCollProf, TH2F* lNPart2DPlot, TH2F* lNColl2DPlot, TH1F* hPercentileMap, double lLoRange = -1, double lHiRange = -1, TH3D* lNpNcEcc = nullptr, TH2F* lEcc2DPlot = nullptr, TH3D* lNpNcB = nullptr, TH2F* lB2DPlot = nullptr, TH2F* lNancestor2DPlot = nullptr, double fProbabilityCutoff = -1); + void CalculateAvNpNc(TProfile* lNPartProf, + TProfile* lNCollProf, + TH2F* lNPart2DPlot, + TH2F* lNColl2DPlot, + TH1F* hPercentileMap, + double lLoRange = -1, + double lHiRange = -1, + TH3D* lNpNcEcc = nullptr, + TH2F* lEcc2DPlot = nullptr, + TH3D* lNpNcB = nullptr, + TH2F* lB2DPlot = nullptr, + TH2F* lNancestor2DPlot = nullptr, + TH2F* lMult2DPlot = nullptr, + double fProbabilityCutoff = -1); private: // This function serves as the (analytical) NBD diff --git a/Common/Tools/Multiplicity/multMCCalibrator.cxx b/Common/Tools/Multiplicity/multMCCalibrator.cxx index be3bd71596f..9567200ed84 100644 --- a/Common/Tools/Multiplicity/multMCCalibrator.cxx +++ b/Common/Tools/Multiplicity/multMCCalibrator.cxx @@ -91,13 +91,13 @@ Bool_t multMCCalibrator::Calibrate() TProfile* hProfSim[multCalibrator::kNCentEstim]; cout << " * acquiring input profiles..." << endl; for (Int_t iv = 0; iv < multCalibrator::kNCentEstim; iv++) { - hProfData[iv] = reinterpret_cast(fileData->Get(Form("multiplicity-qa/multiplicityQa/hProf%s", multCalibrator::fCentEstimName[iv].Data()))); + hProfData[iv] = dynamic_cast(fileData->Get(Form("multiplicity-qa/multiplicityQa/hProf%s", multCalibrator::fCentEstimName[iv].Data()))); if (!hProfData[iv]) { cout << Form("Data file does not contain histogram h%s, which is necessary for calibration!", multCalibrator::fCentEstimName[iv].Data()) << endl; return kFALSE; } hProfData[iv]->SetName(Form("hProfData_%s", multCalibrator::fCentEstimName[iv].Data())); - hProfSim[iv] = reinterpret_cast(fileSim->Get(Form("multiplicity-qa/multiplicityQa/hProf%s", multCalibrator::fCentEstimName[iv].Data()))); + hProfSim[iv] = dynamic_cast(fileSim->Get(Form("multiplicity-qa/multiplicityQa/hProf%s", multCalibrator::fCentEstimName[iv].Data()))); if (!hProfSim[iv]) { cout << Form("Sim file does not contain histogram h%s, which is necessary for calibration!", multCalibrator::fCentEstimName[iv].Data()) << endl; return kFALSE; diff --git a/Common/Tools/Multiplicity/multToolsLinkDef.h b/Common/Tools/Multiplicity/multToolsLinkDef.h index f8740f5afcb..2f2cd969c20 100644 --- a/Common/Tools/Multiplicity/multToolsLinkDef.h +++ b/Common/Tools/Multiplicity/multToolsLinkDef.h @@ -9,6 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +// NOLINT(build/header_guard) + #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; diff --git a/Common/Tools/PID/checkPidPacking.cxx b/Common/Tools/PID/checkPidPacking.cxx index dc497240ef3..1147f27be98 100644 --- a/Common/Tools/PID/checkPidPacking.cxx +++ b/Common/Tools/PID/checkPidPacking.cxx @@ -49,14 +49,14 @@ bool process(std::string outputName, int nevents = 100000) LOG(info) << "Bin width = " << T::bin_width << " vs histo " << hgaus->GetXaxis()->GetBinWidth(1); hgaus->SetLineColor(2); hgaus->SetLineStyle(1); - TH1F* hgausPacked = static_cast(hgaus->Clone("hgausPacked")); + TH1F* hgausPacked = dynamic_cast(hgaus->Clone("hgausPacked")); hgausPacked->SetLineColor(4); hgausPacked->SetLineStyle(2); - TH1F* huniform = static_cast(hgaus->Clone("huniform")); + TH1F* huniform = dynamic_cast(hgaus->Clone("huniform")); huniform->SetLineColor(2); huniform->SetLineStyle(1); - TH1F* huniformPacked = static_cast(hgaus->Clone("huniformPacked")); + TH1F* huniformPacked = dynamic_cast(hgaus->Clone("huniformPacked")); huniformPacked->SetLineColor(4); huniformPacked->SetLineStyle(2); diff --git a/Common/Tools/PID/pidTPCModule.h b/Common/Tools/PID/pidTPCModule.h index b38bc532c44..44234ed5dfa 100644 --- a/Common/Tools/PID/pidTPCModule.h +++ b/Common/Tools/PID/pidTPCModule.h @@ -49,6 +49,7 @@ #include #include +#include #include #include #include @@ -570,11 +571,12 @@ class pidTPCModule std::unique_ptr networkPrediction(new float[predictionSize * NParticleTypes]); // For each mass hypotheses const float nNclNormalization = response->GetNClNormalization(); - float durationNetwork = 0; + float durationNetwork = 0.f; std::vector trackProperties(trackPropSize); + std::vector outputNetwork; // output buffer, allocation is reused for all mass hypotheses uint64_t counterTrackProps = 0; - int loopCounter = 0; + uint64_t loopCounter = 0; // To load the Hadronic rate once for each collision std::vector hadronicRateForCollision(collisions.size(), 0.0f); @@ -626,14 +628,13 @@ class pidTPCModule } const auto startNetworkEval = std::chrono::high_resolution_clock::now(); - const float* const outputNetwork = network.evalModel(trackProperties); + network.evalModel(trackProperties, outputNetwork); const auto stopNetworkEval = std::chrono::high_resolution_clock::now(); durationNetwork += std::chrono::duration>(stopNetworkEval - startNetworkEval).count(); - for (uint64_t kPrediction = 0; kPrediction < predictionSize; kPrediction += outputDimensions) { - for (int lOutputDim = 0; lOutputDim < outputDimensions; ++lOutputDim) { - networkPrediction[kPrediction + lOutputDim + predictionSize * loopCounter] = outputNetwork[kPrediction + lOutputDim]; - } + if (outputNetwork.size() != predictionSize) { + LOG(fatal) << "Network output size (" << outputNetwork.size() << ") does not match the expected prediction size (" << predictionSize << ")"; } + std::copy(outputNetwork.begin(), outputNetwork.end(), networkPrediction.get() + predictionSize * loopCounter); counterTrackProps = 0; ++loopCounter; @@ -722,7 +723,11 @@ class pidTPCModule } auto trackiterator = tracks.begin(); if constexpr (requires { trackiterator.mcParticleId(); }) { - gRandom->SetSeed(0); // Ensure unique seed from UUID for each process call + if (bcs.size() != 0) { + const auto timestamp = bcs.begin().timestamp(); + LOG(info) << "gRandom is seeded with timestamp of the 0-th bunch crossing (" << timestamp << ")"; + gRandom->SetSeed(timestamp); + } } // preparatory step: we need the multiplicities for each collision diff --git a/Common/Tools/TrackPropagationModule.h b/Common/Tools/TrackPropagationModule.h index d7f7dd6a75d..2583b2c9bed 100644 --- a/Common/Tools/TrackPropagationModule.h +++ b/Common/Tools/TrackPropagationModule.h @@ -316,7 +316,7 @@ class TrackPropagationModule } } // auto trackParCov = getTrackParCov(track); - o2::aod::track::TrackTypeEnum trackType = (o2::aod::track::TrackTypeEnum)track.trackType(); + o2::aod::track::TrackTypeEnum trackType{track.trackType()}; // std::array trackPxPyPz; // std::array trackPxPyPzTuned = {0.0, 0.0, 0.0}; double q2OverPtNew = -9999.; diff --git a/Common/Tools/TrackTuner.h b/Common/Tools/TrackTuner.h index 4e1fdcd8bf9..48ed49cdab8 100644 --- a/Common/Tools/TrackTuner.h +++ b/Common/Tools/TrackTuner.h @@ -85,10 +85,10 @@ struct TrackTuner : o2::framework::ConfigurableGroup { bool updateCurvatureIU = false; // To update the track parameter Q/Pt in trackIU table, particularly used for V0 mass width dependence on Q/Pt bool updatePulls = false; bool isInputFileFromCCDB = false; // query input file from CCDB or local folder - std::string pathInputFile = ""; // Path to file containing DCAxy, DCAz graphs from data and MC - std::string nameInputFile = ""; // Name of the file containing DCAxy, DCAz graphs from data and MC - std::string pathFileQoverPt = ""; // Path to file containing Q/Pt correction graphs from data and MC (only one proxy provided, i.e. D0 sigma graphs from data and MC) - std::string nameFileQoverPt = ""; // file name containing Q/Pt correction graphs from data and MC + std::string pathInputFile{}; // Path to file containing DCAxy, DCAz graphs from data and MC + std::string nameInputFile{}; // Name of the file containing DCAxy, DCAz graphs from data and MC + std::string pathFileQoverPt{}; // Path to file containing Q/Pt correction graphs from data and MC (only one proxy provided, i.e. D0 sigma graphs from data and MC) + std::string nameFileQoverPt{}; // file name containing Q/Pt correction graphs from data and MC bool usePvRefitCorrections = false; // establish whether to use corrections obtained with or w/o PV refit float qOverPtMC = -1.; // 1/pt MC float qOverPtData = -1.; // 1/pt data @@ -99,7 +99,7 @@ struct TrackTuner : o2::framework::ConfigurableGroup { int nPhiBins = 1; int runNumber = 0; // first run number considered in analysis (useful only if autoDetectDcaCalib = true) bool areGraphsConfigured = false; - std::string outputString = ""; + std::string outputString{}; std::map metadata; @@ -191,6 +191,10 @@ struct TrackTuner : o2::framework::ConfigurableGroup { LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: +++ Run list: 559348 <= runNumber && runNumber <= 559387 +++"; LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: +++ [CASE 7]: pp, 5.36 TeV 2024, period LHC24aq: CCDB path Users/m/mfaggin/test/inputsTrackTuner/pp2024/ppRef/polarity_negative +++"; LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: +++ Run list: 559408 <= runNumber && runNumber <= 559456 +++"; + LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: +++ [CASE 8]: pp, 13.6 TeV 2025, 2026, CCDB path Users/m/mfaggin/test/inputsTrackTuner/pp2026/pass1/vsPhi +++"; + LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: +++ Run list: (563182 <= runNumber <= 567592 (2025)) || (569945 <= runNumber <= 571787 (2026)) +++"; + LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: +++ [CASE 9]: p-O, 9.62 TeV 2025, CCDB path Users/m/mfaggin/test/inputsTrackTuner/pO +++"; + LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: +++ Run list: 564250 <= runNumber <= 564297 +++"; LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"; LOG(info) << ""; @@ -252,6 +256,22 @@ struct TrackTuner : o2::framework::ConfigurableGroup { pathInputFile = "Users/m/mfaggin/test/inputsTrackTuner/pp2024/ppRef/polarity_negative"; LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: >>> pp, 5.36 TeV 2024, period LHC24aq: CCDB path " << pathInputFile; LOG(info) << " >>> Run list: 559408 <= runNumber && runNumber <= 559456"; + } else if ((563182 <= runNumber && runNumber <= 567592) || (569945 <= runNumber && runNumber <= 571787)) { + /// + /// [CASE 8]: pp, 13.6 TeV 2025, 2026, CCDB path Users/m/mfaggin/test/inputsTrackTuner/pp2026/pass1/vsPhi + /// Run list: 563182 <= runNumber <= 567592 (2025)) || (569945 <= runNumber <= 571787 (2026) + /// + pathInputFile = "Users/m/mfaggin/test/inputsTrackTuner/pp2026/pass1/vsPhi"; + LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: >>> pp, 13.6 TeV 2025, 2026, CCDB path " << pathInputFile; + LOG(info) << " >>> Run list: (563182 <= runNumber <= 567592 (2025)) || (569945 <= runNumber <= 571787 (2026)) "; + } else if (564250 <= runNumber && runNumber <= 564297) { + /// + /// [CASE 9]: p-O, 9.62 TeV 2025, CCDB path Users/m/mfaggin/test/inputsTrackTuner/pO + /// Run list: 564250 <= runNumber <= 564297 + /// + pathInputFile = "Users/m/mfaggin/test/inputsTrackTuner/pO"; + LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: >>> p-O, 9.62 TeV 2025, CCDB path " << pathInputFile; + LOG(info) << " >>> Run list: 564250 <= runNumber <= 564297 "; } else { LOG(fatal) << "runNumber " << runNumber << " not supported for the autodetection. Please switch to manual configuration of the TrackTuner object. Aborting..."; } @@ -370,8 +390,9 @@ struct TrackTuner : o2::framework::ConfigurableGroup { LOG(info) << "[TrackTuner]"; LOG(info) << "[TrackTuner] >>> String slices:"; - for (const std::string& s : slices) + for (const std::string& s : slices) { LOG(info) << "[TrackTuner] " << s; + } /// check if the number of input parameters is correct if (static_cast(slices.size()) != NPars) { @@ -399,9 +420,9 @@ struct TrackTuner : o2::framework::ConfigurableGroup { /// further lambda expression to handle bool initialization auto setBoolFromString = [=](bool& b, const std::string& str) { - if (!str.compare("1") || str.find("true") != std::string::npos || str.find("True") != std::string::npos || str.find("TRUE") != std::string::npos) { + if (str == "1" || str.find("true") != std::string::npos || str.find("True") != std::string::npos || str.find("TRUE") != std::string::npos) { b = true; - } else if (!str.compare("0") || str.find("false") != std::string::npos || str.find("False") != std::string::npos || str.find("FALSE") != std::string::npos) { + } else if (str == "0" || str.find("false") != std::string::npos || str.find("False") != std::string::npos || str.find("FALSE") != std::string::npos) { b = false; } else { LOG(fatal) << "[TrackTuner] Wrong bool initialization from configuration "; @@ -412,36 +433,36 @@ struct TrackTuner : o2::framework::ConfigurableGroup { LOG(info) << "[TrackTuner] >>> Parameters after the custom settings"; // Configure autoDetectDcaCalib setBoolFromString(autoDetectDcaCalib, getValueString(AutoDetectDcaCalib)); - outputString += "autoDetectDcaCalib=" + std::to_string(autoDetectDcaCalib); + outputString += "autoDetectDcaCalib=" + std::to_string(static_cast(autoDetectDcaCalib)); LOG(info) << "[TrackTuner] autoDetectDcaCalib = " << autoDetectDcaCalib; // Configure debugInfo setBoolFromString(debugInfo, getValueString(DebugInfo)); LOG(info) << "[TrackTuner] debugInfo = " << debugInfo; - outputString += ", debugInfo=" + std::to_string(debugInfo); + outputString += ", debugInfo=" + std::to_string(static_cast(debugInfo)); // Configure updateTrackDCAs setBoolFromString(updateTrackDCAs, getValueString(UpdateTrackDCAs)); LOG(info) << "[TrackTuner] updateTrackDCAs = " << updateTrackDCAs; - outputString += ", updateTrackDCAs=" + std::to_string(updateTrackDCAs); + outputString += ", updateTrackDCAs=" + std::to_string(static_cast(updateTrackDCAs)); // Configure updateTrackCovMat setBoolFromString(updateTrackCovMat, getValueString(UpdateTrackCovMat)); LOG(info) << "[TrackTuner] updateTrackCovMat = " << updateTrackCovMat; - outputString += ", updateTrackCovMat=" + std::to_string(updateTrackCovMat); + outputString += ", updateTrackCovMat=" + std::to_string(static_cast(updateTrackCovMat)); // Configure updateCurvature setBoolFromString(updateCurvature, getValueString(UpdateCurvature)); LOG(info) << "[TrackTuner] updateCurvature = " << updateCurvature; - outputString += ", updateCurvature=" + std::to_string(updateCurvature); + outputString += ", updateCurvature=" + std::to_string(static_cast(updateCurvature)); // Configure updateCurvatureIU setBoolFromString(updateCurvatureIU, getValueString(UpdateCurvatureIU)); LOG(info) << "[TrackTuner] updateCurvatureIU = " << updateCurvatureIU; - outputString += ", updateCurvatureIU=" + std::to_string(updateCurvatureIU); + outputString += ", updateCurvatureIU=" + std::to_string(static_cast(updateCurvatureIU)); // Configure updatePulls setBoolFromString(updatePulls, getValueString(UpdatePulls)); LOG(info) << "[TrackTuner] updatePulls = " << updatePulls; - outputString += ", updatePulls=" + std::to_string(updatePulls); + outputString += ", updatePulls=" + std::to_string(static_cast(updatePulls)); // Configure isInputFileFromCCDB setBoolFromString(isInputFileFromCCDB, getValueString(IsInputFileFromCCDB)); LOG(info) << "[TrackTuner] isInputFileFromCCDB = " << isInputFileFromCCDB; - outputString += ", isInputFileFromCCDB=" + std::to_string(isInputFileFromCCDB); + outputString += ", isInputFileFromCCDB=" + std::to_string(static_cast(isInputFileFromCCDB)); // Configure pathInputFile pathFileQoverPt = getValueString(PathFileQoverPt); outputString += ", pathFileQoverPt=" + pathFileQoverPt; @@ -456,7 +477,7 @@ struct TrackTuner : o2::framework::ConfigurableGroup { LOG(info) << "[TrackTuner] nameFileQoverPt = " << nameFileQoverPt; // Configure usePvRefitCorrections setBoolFromString(usePvRefitCorrections, getValueString(UsePvRefitCorrections)); - outputString += ", usePvRefitCorrections=" + std::to_string(usePvRefitCorrections); + outputString += ", usePvRefitCorrections=" + std::to_string(static_cast(usePvRefitCorrections)); LOG(info) << "[TrackTuner] usePvRefitCorrections = " << usePvRefitCorrections; // Configure qOverPtMC qOverPtMC = std::stof(getValueString(QOverPtMC)); @@ -469,8 +490,9 @@ struct TrackTuner : o2::framework::ConfigurableGroup { // Configure nPhiBins nPhiBins = std::stoi(getValueString(NPhiBins)); outputString += ", nPhiBins=" + std::to_string(nPhiBins); - if (nPhiBins < 0) + if (nPhiBins < 0) { LOG(fatal) << "[TrackTuner] negative nPhiBins!" << nPhiBins; + } LOG(info) << "[TrackTuner] nPhiBins = " << nPhiBins; // Configure pathInputFile if (!autoDetectDcaCalib) { @@ -505,36 +527,36 @@ struct TrackTuner : o2::framework::ConfigurableGroup { LOG(info) << "[TrackTuner] >>> Parameters after the custom settings"; // Configure autoDetectDcaCalib autoDetectDcaCalib = cfgAutoDetectDcaCalib; - outputString += "autoDetectDcaCalib=" + std::to_string(autoDetectDcaCalib); + outputString += "autoDetectDcaCalib=" + std::to_string(static_cast(autoDetectDcaCalib)); LOG(info) << "[TrackTuner] autoDetectDcaCalib = " << autoDetectDcaCalib; // Configure debugInfo debugInfo = cfgDebugInfo; LOG(info) << "[TrackTuner] debugInfo = " << debugInfo; - outputString += ", debugInfo=" + std::to_string(debugInfo); + outputString += ", debugInfo=" + std::to_string(static_cast(debugInfo)); // Configure updateTrackDCAs updateTrackDCAs = cfgUpdateTrackDCAs; LOG(info) << "[TrackTuner] updateTrackDCAs = " << updateTrackDCAs; - outputString += ", updateTrackDCAs=" + std::to_string(updateTrackDCAs); + outputString += ", updateTrackDCAs=" + std::to_string(static_cast(updateTrackDCAs)); // Configure updateTrackCovMat updateTrackCovMat = cfgUpdateTrackCovMat; LOG(info) << "[TrackTuner] updateTrackCovMat = " << updateTrackCovMat; - outputString += ", updateTrackCovMat=" + std::to_string(updateTrackCovMat); + outputString += ", updateTrackCovMat=" + std::to_string(static_cast(updateTrackCovMat)); // Configure updateCurvature updateCurvature = cfgUpdateCurvature; LOG(info) << "[TrackTuner] updateCurvature = " << updateCurvature; - outputString += ", updateCurvature=" + std::to_string(updateCurvature); + outputString += ", updateCurvature=" + std::to_string(static_cast(updateCurvature)); // Configure updateCurvatureIU updateCurvatureIU = cfgUpdateCurvatureIU; LOG(info) << "[TrackTuner] updateCurvatureIU = " << updateCurvatureIU; - outputString += ", updateCurvatureIU=" + std::to_string(updateCurvatureIU); + outputString += ", updateCurvatureIU=" + std::to_string(static_cast(updateCurvatureIU)); // Configure updatePulls updatePulls = cfgUpdatePulls; LOG(info) << "[TrackTuner] updatePulls = " << updatePulls; - outputString += ", updatePulls=" + std::to_string(updatePulls); + outputString += ", updatePulls=" + std::to_string(static_cast(updatePulls)); // Configure isInputFileFromCCDB isInputFileFromCCDB = cfgIsInputFileFromCCDB; LOG(info) << "[TrackTuner] isInputFileFromCCDB = " << isInputFileFromCCDB; - outputString += ", isInputFileFromCCDB=" + std::to_string(isInputFileFromCCDB); + outputString += ", isInputFileFromCCDB=" + std::to_string(static_cast(isInputFileFromCCDB)); // Configure pathQoverPt pathFileQoverPt = cfgPathFileQoverPt; outputString += ", pathFileQoverPt=" + pathFileQoverPt; @@ -549,7 +571,7 @@ struct TrackTuner : o2::framework::ConfigurableGroup { LOG(info) << "[TrackTuner] nameFileQoverPt = " << nameFileQoverPt; // Configure usePvRefitCorrections usePvRefitCorrections = cfgUsePvRefitCorrections; - outputString += ", usePvRefitCorrections=" + std::to_string(usePvRefitCorrections); + outputString += ", usePvRefitCorrections=" + std::to_string(static_cast(usePvRefitCorrections)); LOG(info) << "[TrackTuner] usePvRefitCorrections = " << usePvRefitCorrections; // Configure qOverPtMC qOverPtMC = cfgQOverPtMC; @@ -562,8 +584,9 @@ struct TrackTuner : o2::framework::ConfigurableGroup { // Configure nPhiBins nPhiBins = cfgNPhiBins; outputString += ", nPhiBins=" + std::to_string(nPhiBins); - if (nPhiBins < 0) + if (nPhiBins < 0) { LOG(fatal) << "[TrackTuner] negative nPhiBins!" << nPhiBins; + } LOG(info) << "[TrackTuner] nPhiBins = " << nPhiBins; // Configure pathInputFile if (!autoDetectDcaCalib) { @@ -596,9 +619,6 @@ struct TrackTuner : o2::framework::ConfigurableGroup { TList* ccdb_object_dca = nullptr; TList* ccdb_object_qoverpt = nullptr; - std::string grOneOverPtPionNameMC = "sigmaVsPtMc"; - std::string grOneOverPtPionNameData = "sigmaVsPtData"; - if (isInputFileFromCCDB) { /// use input correction file from CCDB @@ -623,10 +643,13 @@ struct TrackTuner : o2::framework::ConfigurableGroup { /// open the input correction file - q/pt correction TFile* inputFileQoverPt = TFile::Open(fullNameFileQoverPt.c_str(), "READ"); - if (!inputFileQoverPt && (updateCurvature || updateCurvatureIU)) { - LOG(fatal) << "Something wrong with the Q/Pt input file" << fullNameFileQoverPt << " for Q/Pt correction. Fix it!"; + if (!inputFileQoverPt) { + if (updateCurvature || updateCurvatureIU) { + LOG(fatal) << "Something wrong with the Q/Pt input file" << fullNameFileQoverPt << " for Q/Pt correction. Fix it!"; + } + } else { + ccdb_object_qoverpt = dynamic_cast(inputFileQoverPt->Get("ccdb_object")); } - ccdb_object_qoverpt = dynamic_cast(inputFileQoverPt->Get("ccdb_object")); } getDcaGraphs(ccdb_object_dca, ccdb_object_qoverpt); @@ -648,14 +671,15 @@ struct TrackTuner : o2::framework::ConfigurableGroup { if (usePvRefitCorrections) { dir = "withPvRefit"; } - TList* td = dynamic_cast(ccdb_object_dca->FindObject(dir.c_str())); + auto td = dynamic_cast(ccdb_object_dca->FindObject(dir.c_str())); if (!td) { LOG(fatal) << "[TrackTuner] TList " << td << " not found in ccdb_object_dca. Fix it!"; } int inputNphiBins = nPhiBins; - if (inputNphiBins == 0) + if (inputNphiBins == 0) { nPhiBins = 1; // old phi_independent settings + } // reserve memory and initialize vector for needed number of graphs grDcaXYResVsPtPionMC.resize(nPhiBins); @@ -757,8 +781,9 @@ struct TrackTuner : o2::framework::ConfigurableGroup { // get phibin double phiMC = mcparticle.phi(); - if (phiMC < 0.) + if (phiMC < 0.) { phiMC += o2::constants::math::TwoPI; // 2 * std::numbers::pi;// + } int phiBin = phiMC / (o2::constants::math::TwoPI + 0.0000001) * nPhiBins; // 0.0000001 just a numerical protection dcaXYResMC = evalGraph(ptMC, grDcaXYResVsPtPionMC[phiBin].get()); @@ -1194,10 +1219,12 @@ struct TrackTuner : o2::framework::ConfigurableGroup { int nPoints = graph->GetN(); double xMin = graph->GetX()[0]; double xMax = graph->GetX()[nPoints - 1]; - if (x > xMax) + if (x > xMax) { return graph->Eval(xMax); - if (x < xMin) + } + if (x < xMin) { return graph->Eval(xMin); + } return graph->Eval(x); } }; diff --git a/Common/Tools/trackSelectionRequestLinkDef.h b/Common/Tools/trackSelectionRequestLinkDef.h index ae9e95b415b..93b201ce15a 100644 --- a/Common/Tools/trackSelectionRequestLinkDef.h +++ b/Common/Tools/trackSelectionRequestLinkDef.h @@ -9,6 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +// NOLINT(build/header_guard) + #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; diff --git a/DPG/Tasks/AOTEvent/CMakeLists.txt b/DPG/Tasks/AOTEvent/CMakeLists.txt index 59e049e6356..202543fa7c2 100644 --- a/DPG/Tasks/AOTEvent/CMakeLists.txt +++ b/DPG/Tasks/AOTEvent/CMakeLists.txt @@ -11,7 +11,7 @@ o2physics_add_dpl_workflow(event-selection-qa SOURCES eventSelectionQa.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2::DetectorsBase O2::DataFormatsITSMFT + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2::DetectorsBase O2::DataFormatsITSMFT O2::TPCBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lumi-qa diff --git a/DPG/Tasks/AOTEvent/detectorOccupancyQa.cxx b/DPG/Tasks/AOTEvent/detectorOccupancyQa.cxx index 5e6cffea36d..9da260edcec 100644 --- a/DPG/Tasks/AOTEvent/detectorOccupancyQa.cxx +++ b/DPG/Tasks/AOTEvent/detectorOccupancyQa.cxx @@ -162,7 +162,7 @@ struct DetectorOccupancyQaTask { } // track QA counters histos.add("nTrackCounter_after_cuts_QA", "", kTH1D, {{12, -0.5, 11.5, "track QA"}}); - TAxis* axTrackCounters = reinterpret_cast(histos.get(HIST("nTrackCounter_after_cuts_QA"))->GetXaxis()); + TAxis* axTrackCounters = histos.get(HIST("nTrackCounter_after_cuts_QA"))->GetXaxis(); axTrackCounters->SetBinLabel(1, "all"); axTrackCounters->SetBinLabel(2, "PVcontrib"); axTrackCounters->SetBinLabel(3, "ptCut"); @@ -1087,7 +1087,6 @@ struct DetectorOccupancyQaTask { // July 2025: more for data vs MC: if (track.hasTPC() && occupancy >= 0 && confAddBasicQAhistos) { float pt = track.pt(); - // pt 0.2-0.5 if (pt > 0.2 && pt < 0.5) { if (nPV >= 10 && nPV < 400) { histos.fill(HIST("QA_noTPCcuts/nPV_10_200/tpcNClsFindable_vs_occup_pt_02_05"), track.tpcNClsFindable(), occupancy); @@ -1098,9 +1097,7 @@ struct DetectorOccupancyQaTask { histos.fill(HIST("QA_noTPCcuts/nPV_above2000/tpcNClsFound_vs_occup_pt_02_05"), track.tpcNClsFound(), occupancy); histos.fill(HIST("QA_noTPCcuts/nPV_above2000/tpcChi2NCl_vs_occup_pt_02_05"), track.tpcChi2NCl(), occupancy); } - } - // pt 0.5-1.0 - else if (pt > 0.5 && pt < 1.0) { + } else if (pt > 0.5 && pt < 1.0) { if (nPV >= 10 && nPV < 400) { histos.fill(HIST("QA_noTPCcuts/nPV_10_200/tpcNClsFindable_vs_occup_pt_05_10"), track.tpcNClsFindable(), occupancy); histos.fill(HIST("QA_noTPCcuts/nPV_10_200/tpcNClsFound_vs_occup_pt_05_10"), track.tpcNClsFound(), occupancy); @@ -1110,9 +1107,7 @@ struct DetectorOccupancyQaTask { histos.fill(HIST("QA_noTPCcuts/nPV_above2000/tpcNClsFound_vs_occup_pt_05_10"), track.tpcNClsFound(), occupancy); histos.fill(HIST("QA_noTPCcuts/nPV_above2000/tpcChi2NCl_vs_occup_pt_05_10"), track.tpcChi2NCl(), occupancy); } - } - // pt > 1.0 - else if (pt > 1.0) { + } else if (pt > 1.0) { if (nPV >= 10 && nPV < 400) { histos.fill(HIST("QA_noTPCcuts/nPV_10_200/tpcNClsFindable_vs_occup_pt_above1_0"), track.tpcNClsFindable(), occupancy); histos.fill(HIST("QA_noTPCcuts/nPV_10_200/tpcNClsFound_vs_occup_pt_above1_0"), track.tpcNClsFound(), occupancy); @@ -1383,8 +1378,8 @@ struct DetectorOccupancyQaTask { int tpcNClsFound = track.tpcNClsFound(); int tpcNClsCrossedRows = track.tpcNClsCrossedRows(); - if (sign > 0) // positive tracks - { + if (sign > 0) { + // positive tracks histos.fill(HIST("track_distr_nITStrThisEv_10_200/kine_vs_weighted_occup/PV_hPt_pos"), pt, occupancy); histos.fill(HIST("track_distr_nITStrThisEv_10_200/kine_vs_weighted_occup/PV_hEta_pos"), eta, occupancy); histos.fill(HIST("track_distr_nITStrThisEv_10_200/kine_vs_weighted_occup/PV_hPhi_pos"), phi, occupancy, pt); @@ -1401,8 +1396,8 @@ struct DetectorOccupancyQaTask { histos.fill(HIST("track_distr_nITStrThisEv_10_200/kine_vs_weighted_occup/QA_tpcNClsFound_pos"), tpcNClsFound); histos.fill(HIST("track_distr_nITStrThisEv_10_200/kine_vs_weighted_occup/QA_tpcNClsCrossedRows_pos"), tpcNClsCrossedRows); } - } else // negative tracks - { + } else { + // negative tracks histos.fill(HIST("track_distr_nITStrThisEv_10_200/kine_vs_weighted_occup/PV_hPt_neg"), pt, occupancy); histos.fill(HIST("track_distr_nITStrThisEv_10_200/kine_vs_weighted_occup/PV_hEta_neg"), eta, occupancy); histos.fill(HIST("track_distr_nITStrThisEv_10_200/kine_vs_weighted_occup/PV_hPhi_neg"), phi, occupancy, pt); @@ -1449,8 +1444,8 @@ struct DetectorOccupancyQaTask { } // end of TPC good global // July 2025: for data vs MC kine distr comparison - if (sign > 0) // positive tracks - { + if (sign > 0) { + // positive tracks histos.fill(HIST("track_distr_nITStrThisEv_above_2000/kine_vs_weighted_occup/PV_hPt_pos"), pt, occupancy); histos.fill(HIST("track_distr_nITStrThisEv_above_2000/kine_vs_weighted_occup/PV_hEta_pos"), eta, occupancy); histos.fill(HIST("track_distr_nITStrThisEv_above_2000/kine_vs_weighted_occup/PV_hPhi_pos"), phi, occupancy, pt); @@ -1463,8 +1458,8 @@ struct DetectorOccupancyQaTask { histos.fill(HIST("track_distr_nITStrThisEv_above_2000/kine_vs_weighted_occup/hPhi_posModifiedQA"), phi); } } - } else // negative tracks - { + } else { + // negative tracks histos.fill(HIST("track_distr_nITStrThisEv_above_2000/kine_vs_weighted_occup/PV_hPt_neg"), pt, occupancy); histos.fill(HIST("track_distr_nITStrThisEv_above_2000/kine_vs_weighted_occup/PV_hEta_neg"), eta, occupancy); histos.fill(HIST("track_distr_nITStrThisEv_above_2000/kine_vs_weighted_occup/PV_hPhi_neg"), phi, occupancy, pt); diff --git a/DPG/Tasks/AOTEvent/eventSelectionQa.cxx b/DPG/Tasks/AOTEvent/eventSelectionQa.cxx index 56a4c9a28ac..eb2896efb16 100644 --- a/DPG/Tasks/AOTEvent/eventSelectionQa.cxx +++ b/DPG/Tasks/AOTEvent/eventSelectionQa.cxx @@ -44,6 +44,7 @@ #include #include +#include #include #include @@ -72,7 +73,8 @@ struct EventSelectionQaTask { Configurable nGlobalBCs{"nGlobalBCs", 100000, "number of global bcs for detailed monitoring"}; Configurable isLowFlux{"isLowFlux", 1, "1 - low flux (pp, pPb), 0 - high flux (PbPb)"}; Configurable fillITSdeadStaveHists{"fillITSdeadStaveHists", 0, "0 - no, 1 - yes"}; - Configurable fillTPCnClsVsOccupancyHists{"fillTPCnClsVsOccupancyHists", 0, "0 - no, 1 - yes"}; + Configurable useTestWithTrackTools{"useTestWithTrackTools", 0, "0 - no, 1 - yes"}; // experimental + Configurable thresholdNrowsForTrackToolTest{"thresholdNrowsForTrackToolTest", 120, "threshold for Nrows for TrackTool test"}; // minimum number of TPC rows (experimental) Service ccdb; HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -91,6 +93,8 @@ struct EventSelectionQaTask { std::string strPassName = ""; // RecoPassName (for data) or AnchorPassName (for MC) from metadata bool isMC = false; + o2::tpc::recohelpers::TPCSectorEdgeConfig tpcSectorEdgeConfig; // for special selection of tracks within TPC + std::bitset bcPatternA; std::bitset bcPatternC; std::bitset bcPatternB; @@ -121,6 +125,12 @@ struct EventSelectionQaTask { strLPMProductionTag = metadataInfo.get("LPMProductionTag"); // to extract info from ccdb by the tag strPassName = metadataInfo.get(isMC ? "AnchorPassName" : "RecoPassName"); + // for special selection of tracks within TPC (Oct 2026) + tpcSectorEdgeConfig.guardLowerCm = 0.5f; + tpcSectorEdgeConfig.guardUpperCm = 0.5f; + tpcSectorEdgeConfig.zMaxCm = 250.f; + tpcSectorEdgeConfig.maxSnp = 0.9f; + const AxisSpec axisMultV0M{1000, 0., 40000., "V0M multiplicity"}; const AxisSpec axisMultV0A{1000, 0., isLowFlux ? 40000. : 200000., "V0A multiplicity"}; const AxisSpec axisMultV0C{1000, 0., 30000., "V0C multiplicity"}; @@ -374,35 +384,20 @@ struct EventSelectionQaTask { histos.add("occupancyQA/hOccupancyByFT0CvsByTracks", "", kTH2D, {{150, 0, 15000}, {150, 0, 150000}}); // 3D histograms: nGlobalTracks with cls567 as y-axis, V0A as x-axis: - const AxisSpec axisNtracksPV{200, -0.5, 5000 - 0.5, "n ITS PV tracks"}; - const AxisSpec axisNtracksPVTPC{160, -0.5, 4000 - 0.5, "n ITS-TPC PV tracks"}; + const AxisSpec axisNtracks{240, -0.5, 6000 - 0.5, "n ITS PV tracks"}; const AxisSpec axisNtracksTPConly{160, -0.5, 8000 - 0.5, "n TPC-only tracks"}; const AxisSpec axisMultV0AForOccup{20, 0., static_cast(200000), "mult V0A"}; const AxisSpec axisOccupancyTracks{150, 0., 15000, "occupancy (n ITS tracks weighted)"}; - histos.add("occupancyQA/hNumTracksPV_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracksPV, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracksPVTPC, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksPVTPCLooseCuts_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracksPVTPC, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksITS_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracksPV, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksITSTPC_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracksPVTPC, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksPV_vs_V0A_vs_occupancy_NarrowDeltaTimeCut", "", kTH3F, {axisMultV0AForOccup, axisNtracksPV, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy_NarrowDeltaTimeCut", "", kTH3F, {axisMultV0AForOccup, axisNtracksPVTPC, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksPV_vs_V0A_vs_occupancy_StandardDeltaTimeCut", "", kTH3F, {axisMultV0AForOccup, axisNtracksPV, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy_StandardDeltaTimeCut", "", kTH3F, {axisMultV0AForOccup, axisNtracksPVTPC, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksPV_vs_V0A_vs_occupancy_GoodITSLayersAllCut", "", kTH3F, {axisMultV0AForOccup, axisNtracksPV, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy_GoodITSLayersAllCut", "", kTH3F, {axisMultV0AForOccup, axisNtracksPVTPC, axisOccupancyTracks}); - // requested by TPC experts: nTPConly tracks vs occupancy - histos.add("occupancyQA/hNumTracksTPConly_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracksTPConly, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksTPConlyNoITS_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracksTPConly, axisOccupancyTracks}); - // request from experts to add track properties vs occupancy, to compare data vs MC - if (fillTPCnClsVsOccupancyHists) { - const AxisSpec axisOccupancyForTrackQA{60, 0., 15000, "occupancy (n ITS tracks weighted)"}; - const AxisSpec axisNTPCcls{150, 0, 150, "n TPC clusters"}; - histos.add("occupancyQA/tpcNClsFound_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNTPCcls, axisOccupancyForTrackQA}); - histos.add("occupancyQA/tpcNClsFindable_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNTPCcls, axisOccupancyForTrackQA}); - histos.add("occupancyQA/tpcNClsShared_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNTPCcls, axisOccupancyForTrackQA}); - histos.add("occupancyQA/tpcNCrossedRows_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNTPCcls, axisOccupancyForTrackQA}); - const AxisSpec axisChi2TPC{150, 0, 15, "chi2Ncl TPC"}; - histos.add("occupancyQA/tpcChi2_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisChi2TPC, axisOccupancyForTrackQA}); + histos.add("occupancyQA/hNumTracksITS_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracks, axisOccupancyTracks}); + histos.add("occupancyQA/hNumTracksITSTPC_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracks, axisOccupancyTracks}); + histos.add("occupancyQA/hNumTracksPV_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracks, axisOccupancyTracks}); + histos.add("occupancyQA/hNumTracksPVhasTPC_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracks, axisOccupancyTracks}); + histos.add("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracks, axisOccupancyTracks}); + + // requested by TPC experts: check with tracks at given NrowsThreshold + if (useTestWithTrackTools) { + histos.add("occupancyQA/hNumTracksITS_nTpcRowsAvailableAboveThrsh_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracks, axisOccupancyTracks}); + histos.add("occupancyQA/hNumTracksPV_nTpcRowsAvailableAboveThrsh_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracks, axisOccupancyTracks}); } // ITS in-ROF occupancy @@ -412,7 +407,7 @@ struct EventSelectionQaTask { histos.add("occupancyQA/dEdx_vs_centr_vs_occup_narrow_p_win", "dE/dx", kTH3F, {{20, 0, 4000, "n PV tracks"}, {60, 0, 15000, "occupancy"}, {800, 0.0, 800.0, "dE/dx (a. u.)"}}); } - } + } // end of init() void processRun2( ColEvSels const& cols, @@ -1177,14 +1172,13 @@ struct EventSelectionQaTask { // count tracks of different types auto tracksGrouped = tracks.sliceBy(perCollision, colIndex); int nPV = 0; - int nTPConly = 0; - // int nTPConlyWithDeDxCut = 0; - int nTPConlyNoITS = 0; - int nContributorsAfterEtaTPCCuts = 0; - int nContributorsAfterEtaTPCLooseCuts = 0; + int nPVafterTPCcuts = 0; + int nPVhasTPC = 0; - int nTracksITS = 0; - int nTracksITSTPC = 0; + int nITS = 0; + int nITSTPC = 0; + int nITSatNTPCrowsThrsh = 0; + int nPVatNTPCrowsThrsh = 0; bool isTVX = col.selection_bit(kIsTriggerTVX); @@ -1194,53 +1188,61 @@ struct EventSelectionQaTask { for (const auto& track : tracksGrouped) { int trackBcDiff = bcDiff + track.trackTime() / o2::constants::lhc::LHCBunchSpacingNS; - if (track.hasTPC() && std::fabs(track.eta()) < 0.8 && track.pt() > 0.2 && track.tpcNClsFound() > 50 && track.tpcNClsCrossedRows() > 50 && track.tpcChi2NCl() < 4) { - nTPConly++; - // if (track.tpcSignal() > 20) - // nTPConlyWithDeDxCut++; - if (!track.hasITS()) - nTPConlyNoITS++; + // Oct 2026: test presence of track at 85 cm radius using new tools - https://github.com/AliceO2Group/AliceO2/tree/dev/Common/TrackTools + bool isTrackWithNrowsAvailable = false; + if (useTestWithTrackTools) { + const float bzKG = 5.f; + const float radiusOfInterest = 85.f; // cm + auto trackParLocal = o2::recohelpers::makeTrackParFromLocalParameters( + track.x(), + track.alpha(), + track.y(), + track.z(), + track.snp(), + track.tgl(), + track.signed1Pt()); + auto trackAt85 = o2::recohelpers::getParametersAtRadiusH(trackParLocal, radiusOfInterest, bzKG); + auto l0 = o2::tpc::recohelpers::sectorEdgeStackLinear(trackParLocal, bzKG, tpcSectorEdgeConfig); + + isTrackWithNrowsAvailable = (trackAt85.valid() && l0.nRowsAvailable > thresholdNrowsForTrackToolTest); } if (std::fabs(track.eta()) < 0.8 && track.pt() > 0.2) { if (track.hasITS()) { - nTracksITS++; + nITS++; if (track.hasTPC()) - nTracksITSTPC++; + nITSTPC++; + if (useTestWithTrackTools && isTrackWithNrowsAvailable) { + nITSatNTPCrowsThrsh++; + } } } if (!track.isPVContributor()) continue; + // high-quality contributors for ROF border QA and occupancy study + if (col.sel8() && fabs(col.posZ()) < 10) { + if (std::fabs(track.eta()) < 0.8 && track.pt() > 0.2 && track.itsNCls() >= 5) { + nPV++; + if (track.hasTPC()) { + nPVhasTPC++; + if (track.tpcNClsFound() > 50) { + nPVafterTPCcuts++; + // ROF border QA + histos.fill(HIST("ITSROFborderQA/hFoundBC_kTVX_nITSlayers_for_ITSTPCtracks"), localBC, track.itsNCls()); + histos.fill(HIST("ITSROFborderQA/hFoundBC_kTVX_counter_ITSTPCtracks"), localBC); + } + if (useTestWithTrackTools && isTrackWithNrowsAvailable) { + nPVatNTPCrowsThrsh++; + } + } // end of hasTPC + } + } // end of sel8 and vZ cuts + if (track.itsNCls() >= 5) vTracksITS567perColl[colIndex]++; - // high-quality contributors for ROF border QA and occupancy study - if (std::fabs(track.eta()) < 0.8 && track.pt() > 0.2 && track.itsNCls() >= 5) { - nPV++; - if (track.hasTPC()) { - nContributorsAfterEtaTPCLooseCuts++; - - if (!isLowFlux && fillTPCnClsVsOccupancyHists && col.sel8() && col.selection_bit(kNoSameBunchPileup) && fabs(col.posZ()) < 10 && occupancyByTracks >= 0) { - histos.fill(HIST("occupancyQA/tpcNClsFound_vs_V0A_vs_occupancy"), multV0A, track.tpcNClsFound(), occupancyByTracks); - histos.fill(HIST("occupancyQA/tpcNClsFindable_vs_V0A_vs_occupancy"), multV0A, track.tpcNClsFindable(), occupancyByTracks); - histos.fill(HIST("occupancyQA/tpcNClsShared_vs_V0A_vs_occupancy"), multV0A, track.tpcNClsShared(), occupancyByTracks); - histos.fill(HIST("occupancyQA/tpcChi2_vs_V0A_vs_occupancy"), multV0A, track.tpcChi2NCl(), occupancyByTracks); - int tpcNClsFindableMinusCrossedRowsCorrected = track.tpcNClsFindableMinusCrossedRows(); - // correct for a buggy behaviour due to int8 and uint8 difference: - if (tpcNClsFindableMinusCrossedRowsCorrected < -70) - tpcNClsFindableMinusCrossedRowsCorrected += 256; - histos.fill(HIST("occupancyQA/tpcNCrossedRows_vs_V0A_vs_occupancy"), multV0A, track.tpcNClsFindable() - tpcNClsFindableMinusCrossedRowsCorrected, occupancyByTracks); - } - } // end of hasTPC - if (col.sel8() && fabs(col.posZ()) < 10 && track.tpcNClsFound() > 50 && track.tpcNClsCrossedRows() > 80 && track.itsChi2NCl() < 36 && track.tpcChi2NCl() < 4) { - nContributorsAfterEtaTPCCuts++; - // ROF border QA - histos.fill(HIST("ITSROFborderQA/hFoundBC_kTVX_nITSlayers_for_ITSTPCtracks"), localBC, track.itsNCls()); - histos.fill(HIST("ITSROFborderQA/hFoundBC_kTVX_counter_ITSTPCtracks"), localBC); - } - } if (!track.hasTPC()) histos.fill(HIST("hITStrackBcDiff"), trackBcDiff); if (track.hasTOF()) { @@ -1256,32 +1258,23 @@ struct EventSelectionQaTask { histos.fill(HIST("hSecondsTVXvsBcDif"), bc.timestamp() / 1000., trackBcDiff); } // end of track loop - histos.fill(HIST("hNcontribAfterCutsVsBcInTF"), bcInTF, nContributorsAfterEtaTPCCuts); + histos.fill(HIST("hNcontribAfterCutsVsBcInTF"), bcInTF, nPVafterTPCcuts); if (!isLowFlux && col.sel8() && col.selection_bit(kNoSameBunchPileup) && fabs(col.posZ()) < 10) { histos.fill(HIST("occupancyQA/hOccupancyByTracks"), occupancyByTracks); histos.fill(HIST("occupancyQA/hOccupancyByFT0C"), occupancyByFT0C); if (occupancyByTracks >= 0) { histos.fill(HIST("occupancyQA/hOccupancyByFT0CvsByTracks"), occupancyByTracks, occupancyByFT0C); + histos.fill(HIST("occupancyQA/hNumTracksITS_vs_V0A_vs_occupancy"), multV0A, nITS, occupancyByTracks); + histos.fill(HIST("occupancyQA/hNumTracksITSTPC_vs_V0A_vs_occupancy"), multV0A, nITSTPC, occupancyByTracks); histos.fill(HIST("occupancyQA/hNumTracksPV_vs_V0A_vs_occupancy"), multV0A, nPV, occupancyByTracks); - histos.fill(HIST("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy"), multV0A, nContributorsAfterEtaTPCCuts, occupancyByTracks); - histos.fill(HIST("occupancyQA/hNumTracksPVTPCLooseCuts_vs_V0A_vs_occupancy"), multV0A, nContributorsAfterEtaTPCLooseCuts, occupancyByTracks); - histos.fill(HIST("occupancyQA/hNumTracksITS_vs_V0A_vs_occupancy"), multV0A, nTracksITS, occupancyByTracks); - histos.fill(HIST("occupancyQA/hNumTracksITSTPC_vs_V0A_vs_occupancy"), multV0A, nTracksITSTPC, occupancyByTracks); - if (col.selection_bit(kNoCollInTimeRangeNarrow)) { - histos.fill(HIST("occupancyQA/hNumTracksPV_vs_V0A_vs_occupancy_NarrowDeltaTimeCut"), multV0A, nPV, occupancyByTracks); - histos.fill(HIST("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy_NarrowDeltaTimeCut"), multV0A, nContributorsAfterEtaTPCCuts, occupancyByTracks); - } - if (col.selection_bit(kNoCollInTimeRangeStandard)) { - histos.fill(HIST("occupancyQA/hNumTracksPV_vs_V0A_vs_occupancy_StandardDeltaTimeCut"), multV0A, nPV, occupancyByTracks); - histos.fill(HIST("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy_StandardDeltaTimeCut"), multV0A, nContributorsAfterEtaTPCCuts, occupancyByTracks); - } - if (col.selection_bit(kIsGoodITSLayersAll)) { - histos.fill(HIST("occupancyQA/hNumTracksPV_vs_V0A_vs_occupancy_GoodITSLayersAllCut"), multV0A, nPV, occupancyByTracks); - histos.fill(HIST("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy_GoodITSLayersAllCut"), multV0A, nContributorsAfterEtaTPCCuts, occupancyByTracks); + histos.fill(HIST("occupancyQA/hNumTracksPVhasTPC_vs_V0A_vs_occupancy"), multV0A, nPVhasTPC, occupancyByTracks); + histos.fill(HIST("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy"), multV0A, nPVafterTPCcuts, occupancyByTracks); + + if (useTestWithTrackTools) { + histos.fill(HIST("occupancyQA/hNumTracksITS_nTpcRowsAvailableAboveThrsh_vs_V0A_vs_occupancy"), multV0A, nITSatNTPCrowsThrsh, occupancyByTracks); + histos.fill(HIST("occupancyQA/hNumTracksPV_nTpcRowsAvailableAboveThrsh_vs_V0A_vs_occupancy"), multV0A, nPVatNTPCrowsThrsh, occupancyByTracks); } - histos.fill(HIST("occupancyQA/hNumTracksTPConly_vs_V0A_vs_occupancy"), multV0A, nTPConly, occupancyByTracks); - histos.fill(HIST("occupancyQA/hNumTracksTPConlyNoITS_vs_V0A_vs_occupancy"), multV0A, nTPConlyNoITS, occupancyByTracks); // dE/dx QA for a narrow pT bin for (const auto& track : tracksGrouped) { @@ -1289,7 +1282,7 @@ struct EventSelectionQaTask { continue; if (std::fabs(track.eta()) < 0.8 && track.pt() > 0.2 && track.itsNCls() >= 5) { float signedP = track.sign() * track.tpcInnerParam(); - if (std::fabs(signedP) > 0.38 && std::fabs(signedP) < 0.4 && track.tpcNClsFound() > 50 && track.tpcNClsCrossedRows() > 80 && track.itsChi2NCl() < 36 && track.tpcChi2NCl() < 4) { + if (std::fabs(signedP) > 0.38 && std::fabs(signedP) < 0.4 && track.tpcNClsFound() > 50) { float dEdx = track.tpcSignal(); histos.fill(HIST("occupancyQA/dEdx_vs_centr_vs_occup_narrow_p_win"), nPV, occupancyByTracks, dEdx); } diff --git a/DPG/Tasks/AOTEvent/lightIonsEvSelQa.cxx b/DPG/Tasks/AOTEvent/lightIonsEvSelQa.cxx index b85b1d3b42c..3a5579ebe51 100644 --- a/DPG/Tasks/AOTEvent/lightIonsEvSelQa.cxx +++ b/DPG/Tasks/AOTEvent/lightIonsEvSelQa.cxx @@ -991,9 +991,9 @@ struct LightIonsEvSelQa { // vZ diff (FT0 vs by tracks) bool badVzDiff = 0; - if (confUseDiffVzCutFromEvSel) + if (confUseDiffVzCutFromEvSel) { badVzDiff = !col.selection_bit(kIsGoodZvtxFT0vsPV); - else { // tune by hand + } else { // tune by hand float meanDiff = 0.0; // cm // O-O if (lastRunNumber == 564356) diff --git a/DPG/Tasks/AOTEvent/lumiQa.cxx b/DPG/Tasks/AOTEvent/lumiQa.cxx index f03419fff52..6402fc52769 100644 --- a/DPG/Tasks/AOTEvent/lumiQa.cxx +++ b/DPG/Tasks/AOTEvent/lumiQa.cxx @@ -162,13 +162,13 @@ struct LumiQaTask { return; } - hCalibV0A = reinterpret_cast(callst->FindObject("hCalibZeqFV0")); + hCalibV0A = dynamic_cast(callst->FindObject("hCalibZeqFV0")); if (hCalibV0A == nullptr) { LOGF(info, "hCalibZeqFV0 histogram is not available for run=%d at timestamp=%llu", runNumber, ts); return; } - hCalibT0C = reinterpret_cast(callst->FindObject("hCalibZeqFT0C")); + hCalibT0C = dynamic_cast(callst->FindObject("hCalibZeqFT0C")); if (hCalibT0C == nullptr) { LOGF(info, "hCalibZeqFT0C histogram is not available for run=%d at timestamp=%llu", runNumber, ts); return; diff --git a/DPG/Tasks/AOTEvent/timeDependentQa.cxx b/DPG/Tasks/AOTEvent/timeDependentQa.cxx index 03f9850a1b5..194b9f49803 100644 --- a/DPG/Tasks/AOTEvent/timeDependentQa.cxx +++ b/DPG/Tasks/AOTEvent/timeDependentQa.cxx @@ -227,7 +227,12 @@ struct TimeDependentQaTask { histos.add("hSecondsCollisions", "", kTH1D, {axisSeconds}); histos.add("hSecondsCollisionsNoPileup", "", kTH1D, {axisSeconds}); histos.add("hSecondsIR", "", kTH1D, {axisSeconds}); + histos.add("hSecondsVzBeforeAllCuts", "", kTH1D, {axisSeconds}); + histos.add("hSecondsVxBeforeAllCuts", "", kTH1D, {axisSeconds}); + histos.add("hSecondsVyBeforeAllCuts", "", kTH1D, {axisSeconds}); histos.add("hSecondsVz", "", kTH1D, {axisSeconds}); + histos.add("hSecondsVx", "", kTH1D, {axisSeconds}); + histos.add("hSecondsVy", "", kTH1D, {axisSeconds}); histos.add("hSecondsFT0Camlp", "", kTH1D, {axisSeconds}); histos.add("hSecondsFT0CamlpByColMult", "", kTH1D, {axisSeconds}); histos.add("hSecondsFT0AamlpByColMult", "", kTH1D, {axisSeconds}); @@ -289,7 +294,7 @@ struct TimeDependentQaTask { // ### QA event selection bits int nEvSelBits = enNumEvSelBits; histos.add("hSecondsEventSelBits", "", kTH2F, {axisSecondsWideBins, {nEvSelBits, -0.5, nEvSelBits - 0.5, "Monitoring of event selection bits"}}); - TAxis* axSelBits = reinterpret_cast(histos.get(HIST("hSecondsEventSelBits"))->GetYaxis()); + TAxis* axSelBits = histos.get(HIST("hSecondsEventSelBits"))->GetYaxis(); axSelBits->SetBinLabel(1 + enCollisionsAll, "collisionsAll"); axSelBits->SetBinLabel(1 + enIsTriggerTVX, "IsTriggerTVX"); axSelBits->SetBinLabel(1 + enNoTimeFrameBorder, "NoTimeFrameBorder"); @@ -325,7 +330,7 @@ struct TimeDependentQaTask { // ### QA RCT flags int nRctFlagsTotal = enNumRctFlagsTotal; histos.add("hSecondsRCTflags", "", kTH2F, {axisSecondsWideBins, {nRctFlagsTotal + 2, -0.5, nRctFlagsTotal + 2 - 0.5, "Monitoring of RCT flags"}}); - axRctFlags = reinterpret_cast(histos.get(HIST("hSecondsRCTflags"))->GetYaxis()); + axRctFlags = histos.get(HIST("hSecondsRCTflags"))->GetYaxis(); axRctFlags->SetBinLabel(1, "NcollisionsSel8"); axRctFlags->SetBinLabel(2, "CcdbNotFound"); axRctFlags->SetBinLabel(3 + kCPVBad, "CPVBad"); @@ -527,6 +532,10 @@ struct TimeDependentQaTask { histos.fill(HIST("hSecondsUPCverticesBeforeAllCuts"), secFromSOR, isVertexUPC ? 1 : 0); + histos.fill(HIST("hSecondsVzBeforeAllCuts"), secFromSOR, col.posZ()); + histos.fill(HIST("hSecondsVxBeforeAllCuts"), secFromSOR, col.posX()); + histos.fill(HIST("hSecondsVyBeforeAllCuts"), secFromSOR, col.posY()); + if (std::fabs(col.posZ()) > 10) continue; @@ -546,6 +555,8 @@ struct TimeDependentQaTask { if (col.selection_bit(kNoSameBunchPileup)) histos.fill(HIST("hSecondsCollisionsNoPileup"), secFromSOR); histos.fill(HIST("hSecondsVz"), secFromSOR, col.posZ()); + histos.fill(HIST("hSecondsVx"), secFromSOR, col.posX()); + histos.fill(HIST("hSecondsVy"), secFromSOR, col.posY()); histos.fill(HIST("hSecondsFT0Camlp"), secFromSOR, bc.foundFT0().sumAmpC()); histos.fill(HIST("hSecondsFT0CamlpByColMult"), secFromSOR, col.multFT0C()); histos.fill(HIST("hSecondsFT0AamlpByColMult"), secFromSOR, col.multFT0A()); diff --git a/DPG/Tasks/AOTTrack/MonitorFilterBit.cxx b/DPG/Tasks/AOTTrack/MonitorFilterBit.cxx index 648c1258127..2b00c8a2599 100644 --- a/DPG/Tasks/AOTTrack/MonitorFilterBit.cxx +++ b/DPG/Tasks/AOTTrack/MonitorFilterBit.cxx @@ -103,15 +103,15 @@ struct CheckFilterBit { using Tracksextension = soa::Join; using TracksextensionMC = soa::Join; SliceCache cache; - Partition positiveTPConlyTracks = o2::aod::track::signed1Pt > fzero&& o2::aod::track::tpcNClsFindable > (uint8_t)0 && o2::aod::track::itsChi2NCl < (float_t)0 && o2::aod::track::trackType == (uint8_t)o2::aod::track::TrackTypeEnum::Track; //&& (o2::aod::track::detectorMap & o2::aod::track::TPC) ==o2::aod::track::TPC && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==0; - Partition negativeTPConlyTracks = o2::aod::track::signed1Pt < fzero && o2::aod::track::tpcNClsFindable > (uint8_t)0 && o2::aod::track::itsChi2NCl < (float_t)0 && o2::aod::track::trackType == (uint8_t)o2::aod::track::TrackTypeEnum::Track; //&& (o2::aod::track::detectorMap & o2::aod::track::TPC) ==o2::aod::track::TPC && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==0; - Partition positiveITSonlyTracks = o2::aod::track::signed1Pt > fzero&& o2::aod::track::tpcChi2NCl<(float_t)0 && o2::aod::track::itsChi2NCl>(float_t) 0 && o2::aod::track::trackType == (uint8_t)o2::aod::track::TrackTypeEnum::Track; // && (o2::aod::track::detectorMap & o2::aod::track::TPC) ==0 && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==o2::aod::track::ITS && o2::aod::track::passedITSNCls==true;// && o2::aod::track::itsNCls==7; - Partition negativeITSonlyTracks = o2::aod::track::signed1Pt < fzero && o2::aod::track::tpcChi2NCl < (float_t)0 && o2::aod::track::itsChi2NCl > (float_t)0 && o2::aod::track::trackType == (uint8_t)o2::aod::track::TrackTypeEnum::Track; // && (o2::aod::track::detectorMap & o2::aod::track::TPC) ==0 &&(o2::aod::track::detectorMap & o2::aod::track::ITS) ==o2::aod::track::ITS && o2::aod::track::passedITSNCls==true;// && o2::aod::track::itsNCls==7; - - Partition positiveTPConlyTracksMC = o2::aod::track::signed1Pt > fzero&& o2::aod::track::tpcNClsFindable > (uint8_t)0 && o2::aod::track::itsChi2NCl < (float_t)0 && o2::aod::track::trackType == (uint8_t)o2::aod::track::TrackTypeEnum::Track; //&& (o2::aod::track::detectorMap & o2::aod::track::TPC) ==o2::aod::track::TPC && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==0; - Partition negativeTPConlyTracksMC = o2::aod::track::signed1Pt < fzero && o2::aod::track::tpcNClsFindable > (uint8_t)0 && o2::aod::track::itsChi2NCl < (float_t)0 && o2::aod::track::trackType == (uint8_t)o2::aod::track::TrackTypeEnum::Track; //&& (o2::aod::track::detectorMap & o2::aod::track::TPC) ==o2::aod::track::TPC && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==0; - Partition positiveITSonlyTracksMC = o2::aod::track::signed1Pt > fzero&& o2::aod::track::tpcChi2NCl<(float_t)0 && o2::aod::track::itsChi2NCl>(float_t) 0 && o2::aod::track::trackType == (uint8_t)o2::aod::track::TrackTypeEnum::Track; // && (o2::aod::track::detectorMap & o2::aod::track::TPC) ==0 && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==o2::aod::track::ITS && o2::aod::track::passedITSNCls==true;// && o2::aod::track::itsNCls==7; - Partition negativeITSonlyTracksMC = o2::aod::track::signed1Pt < fzero && o2::aod::track::tpcChi2NCl < (float_t)0 && o2::aod::track::itsChi2NCl > (float_t)0 && o2::aod::track::trackType == (uint8_t)o2::aod::track::TrackTypeEnum::Track; // && (o2::aod::track::detectorMap & o2::aod::track::TPC) ==0 &&(o2::aod::track::detectorMap & o2::aod::track::ITS) ==o2::aod::track::ITS && o2::aod::track::passedITSNCls==true;// && o2::aod::track::itsNCls==7; + Partition positiveTPConlyTracks = o2::aod::track::signed1Pt > fzero&& o2::aod::track::tpcNClsFindable > static_cast(0) && o2::aod::track::itsChi2NCl < static_cast(0) && o2::aod::track::trackType == static_cast(o2::aod::track::TrackTypeEnum::Track); //&& (o2::aod::track::detectorMap & o2::aod::track::TPC) ==o2::aod::track::TPC && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==0; + Partition negativeTPConlyTracks = o2::aod::track::signed1Pt < fzero && o2::aod::track::tpcNClsFindable > static_cast(0) && o2::aod::track::itsChi2NCl < static_cast(0) && o2::aod::track::trackType == static_cast(o2::aod::track::TrackTypeEnum::Track); //&& (o2::aod::track::detectorMap & o2::aod::track::TPC) ==o2::aod::track::TPC && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==0; + Partition positiveITSonlyTracks = o2::aod::track::signed1Pt > fzero&& o2::aod::track::tpcChi2NCl(0) && o2::aod::track::itsChi2NCl> static_cast(0) && o2::aod::track::trackType == static_cast(o2::aod::track::TrackTypeEnum::Track); // && (o2::aod::track::detectorMap & o2::aod::track::TPC) ==0 && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==o2::aod::track::ITS && o2::aod::track::passedITSNCls==true;// && o2::aod::track::itsNCls==7; + Partition negativeITSonlyTracks = o2::aod::track::signed1Pt < fzero && o2::aod::track::tpcChi2NCl < static_cast(0) && o2::aod::track::itsChi2NCl > static_cast(0) && o2::aod::track::trackType == static_cast(o2::aod::track::TrackTypeEnum::Track); // && (o2::aod::track::detectorMap & o2::aod::track::TPC) ==0 &&(o2::aod::track::detectorMap & o2::aod::track::ITS) ==o2::aod::track::ITS && o2::aod::track::passedITSNCls==true;// && o2::aod::track::itsNCls==7; + + Partition positiveTPConlyTracksMC = o2::aod::track::signed1Pt > fzero&& o2::aod::track::tpcNClsFindable > static_cast(0) && o2::aod::track::itsChi2NCl < static_cast(0) && o2::aod::track::trackType == static_cast(o2::aod::track::TrackTypeEnum::Track); //&& (o2::aod::track::detectorMap & o2::aod::track::TPC) ==o2::aod::track::TPC && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==0; + Partition negativeTPConlyTracksMC = o2::aod::track::signed1Pt < fzero && o2::aod::track::tpcNClsFindable > static_cast(0) && o2::aod::track::itsChi2NCl < static_cast(0) && o2::aod::track::trackType == static_cast(o2::aod::track::TrackTypeEnum::Track); //&& (o2::aod::track::detectorMap & o2::aod::track::TPC) ==o2::aod::track::TPC && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==0; + Partition positiveITSonlyTracksMC = o2::aod::track::signed1Pt > fzero&& o2::aod::track::tpcChi2NCl(0) && o2::aod::track::itsChi2NCl> static_cast(0) && o2::aod::track::trackType == static_cast(o2::aod::track::TrackTypeEnum::Track); // && (o2::aod::track::detectorMap & o2::aod::track::TPC) ==0 && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==o2::aod::track::ITS && o2::aod::track::passedITSNCls==true;// && o2::aod::track::itsNCls==7; + Partition negativeITSonlyTracksMC = o2::aod::track::signed1Pt < fzero && o2::aod::track::tpcChi2NCl < static_cast(0) && o2::aod::track::itsChi2NCl > static_cast(0) && o2::aod::track::trackType == static_cast(o2::aod::track::TrackTypeEnum::Track); // && (o2::aod::track::detectorMap & o2::aod::track::TPC) ==0 &&(o2::aod::track::detectorMap & o2::aod::track::ITS) ==o2::aod::track::ITS && o2::aod::track::passedITSNCls==true;// && o2::aod::track::itsNCls==7; void init(InitContext const&) { diff --git a/DPG/Tasks/AOTTrack/PID/HMPID/CMakeLists.txt b/DPG/Tasks/AOTTrack/PID/HMPID/CMakeLists.txt index d33a73eb12f..748576f57eb 100644 --- a/DPG/Tasks/AOTTrack/PID/HMPID/CMakeLists.txt +++ b/DPG/Tasks/AOTTrack/PID/HMPID/CMakeLists.txt @@ -20,7 +20,13 @@ o2physics_add_dpl_workflow(hmpid-table-producer PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::HMPIDBase COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(hmpid-deuteron - SOURCES hmpidDeuteron.cxx +o2physics_add_dpl_workflow(hmpid-abs-cross-section + SOURCES hmpidAbsCrossSection.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::ReconstructionDataFormats O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(hmpid-cross-section-corrections + SOURCES hmpidCrossSectionCorrections.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::ReconstructionDataFormats O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + diff --git a/DPG/Tasks/AOTTrack/PID/HMPID/hmpidAbsCrossSection.cxx b/DPG/Tasks/AOTTrack/PID/HMPID/hmpidAbsCrossSection.cxx new file mode 100644 index 00000000000..aa8a79e6bc2 --- /dev/null +++ b/DPG/Tasks/AOTTrack/PID/HMPID/hmpidAbsCrossSection.cxx @@ -0,0 +1,389 @@ +// Copyright 2019-2020 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 Analysis task for the absorption cross section using HMPID (de,pi,ka,pr) +// \authors rocco.liotino@cern.ch + +#include "tableHMPID.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +struct HmpidAbsCrossSection { + + HistogramRegistry registryDA{"registryDA", {}, OutputObjHandlingPolicy::AnalysisObject}; + + Configurable minReqClusterITS{"minReqClusterITS", 4.0, "min number of clusters required in ITS"}; + Configurable minTPCnClsFound{"minTPCnClsFound", 50.0f, "minTPCnClsFound"}; + Configurable minNCrossedRowsTPC{"minNCrossedRowsTPC", 70.0f, "min number of crossed rows TPC"}; + Configurable maxChi2ITS{"maxChi2ITS", 36.0f, "max chi2 per cluster ITS"}; + Configurable maxChi2TPC{"maxChi2TPC", 4.0f, "max chi2 per cluster TPC"}; + Configurable maxDCAxy{"maxDCAxy", 0.5f, "maxDCAxy"}; + Configurable maxDCAz{"maxDCAz", 0.5f, "maxDCAz"}; + + // Configurables for PbPb collisions + Configurable collisionsPbPb{"collisionsPbPb", false, "Require PbPb collisions"}; + Configurable centralityCut{"centralityCut", 30.0f, "centrality cut for PbPb collisions"}; + + // Configurables for PID cuts + Configurable nsigmaTPCMin{"nsigmaTPCMin", -3.0, "nsigmaTPCMin"}; + Configurable nsigmaTPCMax{"nsigmaTPCMax", +3.0, "nsigmaTPCMax"}; + Configurable nsigmaTOFMin{"nsigmaTOFMin", -3.0, "nsigmaTOFMin"}; + Configurable nsigmaTOFMax{"nsigmaTOFMax", +3.0, "nsigmaTOFMax"}; + + // Particle species selection + Configurable particleSpecies{"particleSpecies", "de", "Particle species: de (deuteron), pr (proton), pi (pion), ka (kaon)"}; + + // Configurable offset for residuals distributions + Configurable offsetX{"offsetX", 0.696712f, "Residuals offset - X direction"}; // mean value (old) + Configurable offsetY{"offsetY", 2.32849f, "Residuals offset - Y direction"}; // mean value (old) + + static constexpr float UnsetOffset = -999.f; + static constexpr int MaxOffsetBins = 35; + static constexpr int OffsetColumns = 8; + + static std::vector makeOffsetRowLabels() + { + std::vector labels; + labels.reserve(MaxOffsetBins); + for (int bin = 0; bin < MaxOffsetBins; ++bin) { + labels.emplace_back("pBin" + std::to_string(bin)); + } + return labels; + } + + Configurable offsetMomMin{"offsetMomMin", 0.5f, "Minimum momentum for residual offset correction"}; + Configurable offsetMomMax{"offsetMomMax", 4.0f, "Maximum momentum for residual offset correction"}; + Configurable offsetMomStep{"offsetMomStep", 0.5f, "Momentum bin width for residual offset correction"}; + + Configurable> residualOffsets{"residualOffsets", LabeledArray{Array2D{std::vector(MaxOffsetBins * OffsetColumns, UnsetOffset), MaxOffsetBins, OffsetColumns}, makeOffsetRowLabels(), {"xPosR2", "yPosR2", "xNegR2", "yNegR2", "xPosR4", "yPosR4", "xNegR4", "yNegR4"}}, "Residual offsets by momentum bin, chamber and charge"}; + + // Configurables for plotting + Configurable momNBins{"momNBins", 450, "Momentum bins"}; + Configurable momMin{"momMin", 0.5f, "Momentum min"}; + Configurable momMax{"momMax", 5.0f, "Momentum max"}; + + Configurable deltaRNBins{"deltaRNBins", 100, "DeltaR bins"}; + Configurable deltaRMin{"deltaRMin", 0.0f, "DeltaR min"}; + Configurable deltaRMax{"deltaRMax", 50.0f, "DeltaR max"}; + + Configurable nSigmaTPCNBins{"nSigmaTPCNBins", 100, "nSigmaTPC bins"}; + Configurable nSigmaTPCMinPlot{"nSigmaTPCMinPlot", -5.0f, "nSigmaTPC min"}; + Configurable nSigmaTPCMaxPlot{"nSigmaTPCMaxPlot", 5.0f, "nSigmaTPC max"}; + + Configurable nSigmaTOFNBins{"nSigmaTOFNBins", 100, "nSigmaTOF bins"}; + Configurable nSigmaTOFMinPlot{"nSigmaTOFMinPlot", -5.0f, "nSigmaTOF min"}; + Configurable nSigmaTOFMaxPlot{"nSigmaTOFMaxPlot", 5.0f, "nSigmaTOF max"}; + + static constexpr int Rich2 = 2, Rich4 = 4; + // static constexpr Rich0 = 0, Rich1 = 1, Rich3 = 3, Rich5 = 5, Rich6 = 6; + // static constexpr int Nch = 7; + + void init(o2::framework::InitContext const&) + { + // Axes for histograms + AxisSpec axisMom{momNBins, momMin, momMax, "#it{p} (GeV/#it{c})", "axisMom"}; + AxisSpec axisDeltaR{deltaRNBins, deltaRMin, deltaRMax, "#Delta R (cm)", "axisDeltaR"}; + AxisSpec axisNsigmaTOF{nSigmaTOFNBins, nSigmaTOFMinPlot, nSigmaTOFMaxPlot, "n#sigma_{TOF}", "axisNsigmaTOF"}; + AxisSpec axisNsigmaTPC{nSigmaTPCNBins, nSigmaTPCMinPlot, nSigmaTPCMaxPlot, "n#sigma_{TPC}", "axisNsigmaTPC"}; + AxisSpec axisChamber{10, -1.5, 8.5, "Chamber number", "axisChamber"}; + + // 1D integrated histograms (quick view) + registryDA.add("incoming_Pos_8cm", "incoming_Pos_8cm", HistType::kTH1F, {axisMom}); + registryDA.add("incoming_Pos_4cm", "incoming_Pos_4cm", HistType::kTH1F, {axisMom}); + + registryDA.add("DeltaR_Pos_8cm", "DeltaR_Pos_8cm", HistType::kTH1F, {axisDeltaR}); + registryDA.add("DeltaR_Pos_4cm", "DeltaR_Pos_4cm", HistType::kTH1F, {axisDeltaR}); + + registryDA.add("incoming_Neg_8cm", "incoming_Neg_8cm", HistType::kTH1F, {axisMom}); + registryDA.add("incoming_Neg_4cm", "incoming_Neg_4cm", HistType::kTH1F, {axisMom}); + + registryDA.add("DeltaR_Neg_8cm", "DeltaR_Neg_8cm", HistType::kTH1F, {axisDeltaR}); + registryDA.add("DeltaR_Neg_4cm", "DeltaR_Neg_4cm", HistType::kTH1F, {axisDeltaR}); + + // Positive candidates histograms + registryDA.add("nSigmaTPC_vs_momHMPID_PreCut_Pos_8cm", "nSigmaTPC_vs_momHMPID_PreCut_Pos_8cm", HistType::kTH2F, {axisMom, axisNsigmaTPC}); + registryDA.add("nSigmaTOF_vs_momHMPID_PreCut_Pos_8cm", "nSigmaTOF_vs_momHMPID_PreCut_Pos_8cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + registryDA.add("nSigmaTPC_vs_momHMPID_PreCut_Pos_4cm", "nSigmaTPC_vs_momHMPID_PreCut_Pos_4cm", HistType::kTH2F, {axisMom, axisNsigmaTPC}); + registryDA.add("nSigmaTOF_vs_momHMPID_PreCut_Pos_4cm", "nSigmaTOF_vs_momHMPID_PreCut_Pos_4cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + + // TPC only candidates histograms + registryDA.add("nSigmaTOF_vs_momHMPID_TPCOnly_Pos_8cm", "nSigmaTOF_vs_momHMPID_TPCOnly_Pos_8cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + registryDA.add("nSigmaTOF_vs_momHMPID_TPCOnly_Pos_4cm", "nSigmaTOF_vs_momHMPID_TPCOnly_Pos_4cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + + // after TPC+TOF cuts candidates histograms + registryDA.add("nSigmaTOF_vs_momHMPID_Pos_8cm", "nSigmaTOF_vs_momHMPID_Pos_8cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + registryDA.add("nSigmaTOF_vs_momHMPID_Pos_4cm", "nSigmaTOF_vs_momHMPID_Pos_4cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + + registryDA.add("DeltaR_vs_momHMPID_Pos_8cm", "DeltaR_vs_momHMPID_Pos_8cm", HistType::kTH2F, {axisMom, axisDeltaR}); + registryDA.add("DeltaR_vs_momHMPID_Pos_4cm", "DeltaR_vs_momHMPID_Pos_4cm", HistType::kTH2F, {axisMom, axisDeltaR}); + + // corrected residuals distributions + registryDA.add("residualsX_Vs_momentum_Pos_8cm", "residualsX_Vs_momentum_Pos_8cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "x_{mip} - x_{track} (cm)"}}); + registryDA.add("residualsY_Vs_momentum_Pos_8cm", "residualsY_Vs_momentum_Pos_8cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "y_{mip} - y_{track} (cm)"}}); + registryDA.add("residualsX_Vs_momentum_Pos_4cm", "residualsX_Vs_momentum_Pos_4cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "x_{mip} - x_{track} (cm)"}}); + registryDA.add("residualsY_Vs_momentum_Pos_4cm", "residualsY_Vs_momentum_Pos_4cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "y_{mip} - y_{track} (cm)"}}); + + // Negative candidates histograms + registryDA.add("nSigmaTPC_vs_momHMPID_PreCut_Neg_8cm", "nSigmaTPC_vs_momHMPID_PreCut_Neg_8cm", HistType::kTH2F, {axisMom, axisNsigmaTPC}); + registryDA.add("nSigmaTOF_vs_momHMPID_PreCut_Neg_8cm", "nSigmaTOF_vs_momHMPID_PreCut_Neg_8cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + registryDA.add("nSigmaTPC_vs_momHMPID_PreCut_Neg_4cm", "nSigmaTPC_vs_momHMPID_PreCut_Neg_4cm", HistType::kTH2F, {axisMom, axisNsigmaTPC}); + registryDA.add("nSigmaTOF_vs_momHMPID_PreCut_Neg_4cm", "nSigmaTOF_vs_momHMPID_PreCut_Neg_4cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + + // TPC only candidates histograms + registryDA.add("nSigmaTOF_vs_momHMPID_TPCOnly_Neg_8cm", "nSigmaTOF_vs_momHMPID_TPCOnly_Neg_8cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + registryDA.add("nSigmaTOF_vs_momHMPID_TPCOnly_Neg_4cm", "nSigmaTOF_vs_momHMPID_TPCOnly_Neg_4cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + + // after TPC+TOF cuts candidates histograms + registryDA.add("nSigmaTOF_vs_momHMPID_Neg_8cm", "nSigmaTOF_vs_momHMPID_Neg_8cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + registryDA.add("nSigmaTOF_vs_momHMPID_Neg_4cm", "nSigmaTOF_vs_momHMPID_Neg_4cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + + registryDA.add("DeltaR_vs_momHMPID_Neg_8cm", "DeltaR_vs_momHMPID_Neg_8cm", HistType::kTH2F, {axisMom, axisDeltaR}); + registryDA.add("DeltaR_vs_momHMPID_Neg_4cm", "DeltaR_vs_momHMPID_Neg_4cm", HistType::kTH2F, {axisMom, axisDeltaR}); + + // corrected residuals distributions + registryDA.add("residualsX_Vs_momentum_Neg_8cm", "residualsX_Vs_momentum_Neg_8cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "x_{mip} - x_{track} (cm)"}}); + registryDA.add("residualsY_Vs_momentum_Neg_8cm", "residualsY_Vs_momentum_Neg_8cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "y_{mip} - y_{track} (cm)"}}); + registryDA.add("residualsX_Vs_momentum_Neg_4cm", "residualsX_Vs_momentum_Neg_4cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "x_{mip} - x_{track} (cm)"}}); + registryDA.add("residualsY_Vs_momentum_Neg_4cm", "residualsY_Vs_momentum_Neg_4cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "y_{mip} - y_{track} (cm)"}}); + + // nsigma TPC vs nsigma TOF for candidates + registryDA.add("pidTPC_TOF_p_ch_Pos", "pidTPC_TOF_p_ch_Pos", HistType::kTHnSparseF, {axisMom, axisNsigmaTPC, axisNsigmaTOF, axisChamber}); + registryDA.add("pidTPC_TOF_p_ch_Neg", "pidTPC_TOF_p_ch_Neg", HistType::kTHnSparseF, {axisMom, axisNsigmaTPC, axisNsigmaTOF, axisChamber}); + } // end init + + void process(aod::HmpidAnalysis const& hmpidTable) + { + for (const auto& hmpid : hmpidTable) { + + // ------------------------- + // track filters + // ------------------------- + if (hmpid.itsNCluster() < minReqClusterITS) { + continue; + } + if (hmpid.tpcNCluster() < minTPCnClsFound) { + continue; + } + if (hmpid.tpcNClsCrossedRows() < minNCrossedRowsTPC) { + continue; + } + if (hmpid.tpcChi2() > maxChi2TPC) { + continue; + } + if (hmpid.itsChi2() > maxChi2ITS) { + continue; + } + if (std::abs(hmpid.dcaXY()) > maxDCAxy) { + continue; + } + if (std::abs(hmpid.dcaZ()) > maxDCAz) { + continue; + } + + // ------------------------- + // derived quantities + // ------------------------- + const float momHmpid = hmpid.momentumHmpid(); + const float momAbs = std::abs(momHmpid); + + const int chamber = static_cast(hmpid.chamber()); + const bool isPos = momHmpid > 0; + const bool isNeg = momHmpid < 0; + + // Select chambers with absorber + if (chamber != Rich2 && chamber != Rich4) { + continue; + } + + // Default residual offsets + float usedOffsetX = offsetX.value; + float usedOffsetY = offsetY.value; + + // Momentum-dependent offset correction + if (momAbs >= offsetMomMin.value && momAbs < offsetMomMax.value && (isPos || isNeg)) { + const int pBin = static_cast((momAbs - offsetMomMin.value) / offsetMomStep.value); + if (pBin >= 0 && pBin < static_cast(residualOffsets->rows())) { + const int col = (chamber == Rich2 ? 0 : 4) + (isPos ? 0 : 2); + const float binnedOffsetX = residualOffsets->get(pBin, col); + const float binnedOffsetY = residualOffsets->get(pBin, col + 1); + + if (binnedOffsetX != UnsetOffset) { + usedOffsetX = binnedOffsetX; + } + if (binnedOffsetY != UnsetOffset) { + usedOffsetY = binnedOffsetY; + } + } + } + + // Corrected residuals + const float dxCorr = (hmpid.xMip() - hmpid.xTrack()) - usedOffsetX; + const float dyCorr = (hmpid.yMip() - hmpid.yTrack()) - usedOffsetY; + const float drCorr = std::hypot(dxCorr, dyCorr); + + // check only HMPID chamber with an absorber (only RICH 2 (4cm abs) and RICH4 (8cm abs)) + if (chamber != Rich2 && chamber != Rich4) { + continue; + } + + // select particle species + + float nSigmaTPC = 0.f; + float nSigmaTOF = 0.f; + + // Select particle species + if (particleSpecies.value == "de") { + nSigmaTPC = hmpid.tpcNSigmaDe(); + nSigmaTOF = hmpid.tofNSigmaDe(); + } else if (particleSpecies.value == "pr") { + nSigmaTPC = hmpid.tpcNSigmaPr(); + nSigmaTOF = hmpid.tofNSigmaPr(); + } else if (particleSpecies.value == "pi") { + nSigmaTPC = hmpid.tpcNSigmaPi(); + nSigmaTOF = hmpid.tofNSigmaPi(); + } else if (particleSpecies.value == "ka") { + nSigmaTPC = hmpid.tpcNSigmaKa(); + nSigmaTOF = hmpid.tofNSigmaKa(); + } else { + LOGF(fatal, "Invalid particle species: %s", + particleSpecies.value.c_str()); + } + + // ------------------------- + // nsigma distributions pre-cuts + // ------------------------- + if (isPos) { + registryDA.fill(HIST("pidTPC_TOF_p_ch_Pos"), momAbs, nSigmaTPC, nSigmaTOF, chamber); + if (chamber == Rich2) { + registryDA.fill(HIST("nSigmaTPC_vs_momHMPID_PreCut_Pos_4cm"), momAbs, nSigmaTPC); + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_PreCut_Pos_4cm"), momAbs, nSigmaTOF); + } + if (chamber == Rich4) { + registryDA.fill(HIST("nSigmaTPC_vs_momHMPID_PreCut_Pos_8cm"), momAbs, nSigmaTPC); + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_PreCut_Pos_8cm"), momAbs, nSigmaTOF); + } + } + if (isNeg) { + registryDA.fill(HIST("pidTPC_TOF_p_ch_Neg"), momAbs, nSigmaTPC, nSigmaTOF, chamber); + if (chamber == Rich2) { + registryDA.fill(HIST("nSigmaTPC_vs_momHMPID_PreCut_Neg_4cm"), momAbs, nSigmaTPC); + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_PreCut_Neg_4cm"), momAbs, nSigmaTOF); + } + if (chamber == Rich4) { + registryDA.fill(HIST("nSigmaTPC_vs_momHMPID_PreCut_Neg_8cm"), momAbs, nSigmaTPC); + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_PreCut_Neg_8cm"), momAbs, nSigmaTOF); + } + } + + // ------------------------- + // deuteron candidate cuts - TPC + // ------------------------- + if (nSigmaTPC < nsigmaTPCMin || nSigmaTPC > nsigmaTPCMax) { + continue; + } + + // ------------------------- + // post TPC cut + // ------------------------- + if (isPos) { + if (chamber == Rich2) { + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_TPCOnly_Pos_8cm"), momAbs, nSigmaTOF); + } + if (chamber == Rich4) { + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_TPCOnly_Pos_4cm"), momAbs, nSigmaTOF); + } + } + if (isNeg) { + if (chamber == Rich2) { + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_TPCOnly_Neg_8cm"), momAbs, nSigmaTOF); + } + if (chamber == Rich4) { + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_TPCOnly_Neg_4cm"), momAbs, nSigmaTOF); + } + } + + // ------------------------- + // deuteron candidate cuts - TOF + // ------------------------- + if (nSigmaTOF < nsigmaTOFMin || nSigmaTOF > nsigmaTOFMax) { + continue; + } + + // Pb-Pb collisions centrality requirement + if (collisionsPbPb) { + if (hmpid.centrality() < centralityCut) { + continue; + } + } + + // Fill distributions for absorption cross section measurement + if (isPos && chamber == Rich4) { + registryDA.fill(HIST("incoming_Pos_8cm"), momAbs); + registryDA.fill(HIST("DeltaR_Pos_8cm"), drCorr); + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_Pos_8cm"), momAbs, nSigmaTOF); + registryDA.fill(HIST("DeltaR_vs_momHMPID_Pos_8cm"), momAbs, drCorr); + registryDA.fill(HIST("residualsX_Vs_momentum_Pos_8cm"), momAbs, dxCorr); + registryDA.fill(HIST("residualsY_Vs_momentum_Pos_8cm"), momAbs, dyCorr); + } + + if (isNeg && chamber == Rich4) { + registryDA.fill(HIST("incoming_Neg_8cm"), momAbs); + registryDA.fill(HIST("DeltaR_Neg_8cm"), drCorr); + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_Neg_8cm"), momAbs, nSigmaTOF); + registryDA.fill(HIST("DeltaR_vs_momHMPID_Neg_8cm"), momAbs, drCorr); + + registryDA.fill(HIST("residualsX_Vs_momentum_Neg_8cm"), momAbs, dxCorr); + registryDA.fill(HIST("residualsY_Vs_momentum_Neg_8cm"), momAbs, dyCorr); + } + + if (isPos && chamber == Rich2) { + registryDA.fill(HIST("incoming_Pos_4cm"), momAbs); + registryDA.fill(HIST("DeltaR_Pos_4cm"), drCorr); + registryDA.fill(HIST("DeltaR_vs_momHMPID_Pos_4cm"), momAbs, drCorr); + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_Pos_4cm"), momAbs, nSigmaTOF); + + registryDA.fill(HIST("residualsX_Vs_momentum_Pos_4cm"), momAbs, dxCorr); + registryDA.fill(HIST("residualsY_Vs_momentum_Pos_4cm"), momAbs, dyCorr); + } + + if (isNeg && chamber == Rich2) { + registryDA.fill(HIST("incoming_Neg_4cm"), momAbs); + registryDA.fill(HIST("DeltaR_Neg_4cm"), drCorr); + registryDA.fill(HIST("DeltaR_vs_momHMPID_Neg_4cm"), momAbs, drCorr); + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_Neg_4cm"), momAbs, nSigmaTOF); + + registryDA.fill(HIST("residualsX_Vs_momentum_Neg_4cm"), momAbs, dxCorr); + registryDA.fill(HIST("residualsY_Vs_momentum_Neg_4cm"), momAbs, dyCorr); + } + } // end of loop over hmpidTable + } // end of process +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + }; +} diff --git a/DPG/Tasks/AOTTrack/PID/HMPID/hmpidCrossSectionCorrections.cxx b/DPG/Tasks/AOTTrack/PID/HMPID/hmpidCrossSectionCorrections.cxx new file mode 100644 index 00000000000..8ca94444d60 --- /dev/null +++ b/DPG/Tasks/AOTTrack/PID/HMPID/hmpidCrossSectionCorrections.cxx @@ -0,0 +1,374 @@ +// Copyright 2019-2020 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 Analysis task for the (anti-)deuteron absorption cross section using HMPID +// \authors rocco.liotino@cern.ch + +#include "tableHMPID.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +using namespace o2; +using namespace o2::framework; + +struct HmpidCrossSectionCorrections { + + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // Configurables for main track quality criteria + Configurable minReqClusterITS{"minReqClusterITS", 4.0, "min number of clusters required in ITS"}; + Configurable minTPCnClsFound{"minTPCnClsFound", 50.0f, "minTPCnClsFound"}; + Configurable minNCrossedRowsTPC{"minNCrossedRowsTPC", 70.0f, "min number of crossed rows TPC"}; + Configurable maxChi2ITS{"maxChi2ITS", 36.0f, "max chi2 per cluster ITS"}; + Configurable maxChi2TPC{"maxChi2TPC", 4.0f, "max chi2 per cluster TPC"}; + Configurable maxDCAxy{"maxDCAxy", 0.5f, "maxDCAxy"}; + Configurable maxDCAz{"maxDCAz", 0.5f, "maxDCAz"}; + + // Configurables for QA selection + Configurable qMipMin{"qMipMin", 200.0f, "minimum MIP charge for QA selection"}; + Configurable qMipMax{"qMipMax", 2000.0f, "maximum MIP charge for QA selection"}; + Configurable cutDistanceMipTrack{"cutDistanceMipTrack", 10.0f, "maximum raw MIP-track distance for QA selection"}; + + // Configurables for plotting + Configurable momNBins{"momNBins", 490, "Momentum bins"}; + Configurable momMin{"momMin", 0.1f, "Momentum min"}; + Configurable momMax{"momMax", 5.0f, "Momentum max"}; + + Configurable etaNBins{"etaNBins", 100, "Eta bins"}; + Configurable etaMin{"etaMin", -5.0f, "Eta min"}; + Configurable etaMax{"etaMax", 5.0f, "Eta max"}; + + Configurable phiNBins{"phiNBins", 100, "Phi bins"}; + Configurable phiMin{"phiMin", -10.0f, "Phi min"}; + Configurable phiMax{"phiMax", 10.0f, "Phi max"}; + + Configurable mapNBins{"mapNBins", 135, "Map bins (x or y)"}; + Configurable mapMin{"mapMin", 0.0f, "Map min (x or y) in cm"}; + Configurable mapMax{"mapMax", 135.0f, "Map max (x or y) in cm"}; + + Configurable residualsNBins{"residualsNBins", 400, "Residuals bins"}; + Configurable residualsMin{"residualsMin", -20.0f, "Residuals min (x or y) in cm"}; + Configurable residualsMax{"residualsMax", 20.0f, "Residuals max (x or y) in cm"}; + + static constexpr int Rich0 = 0, Rich1 = 1, Rich2 = 2, Rich3 = 3, Rich4 = 4, Rich5 = 5, Rich6 = 6; + static constexpr int Nch = 7; + + void init(o2::framework::InitContext const&) + { + // Axes for histograms + AxisSpec axisHmpidMom{momNBins, momMin, momMax, "#it{p} (GeV/#it{c})", "axisHmpidMom"}; + AxisSpec axisGlobalMom{momNBins, momMin, momMax, "#it{p} (GeV/#it{c})", "axisGlobalMom"}; + AxisSpec axisMipX{mapNBins, mapMin, mapMax, "X_{MIP} (cm)", "axisMipX"}; + AxisSpec axisMipY{mapNBins, mapMin, mapMax, "Y_{MIP} (cm)", "axisMipY"}; + AxisSpec axisGlobalTrackX{mapNBins, mapMin, mapMax, "X_{track} (cm)", "axisGlobalTrackX"}; + AxisSpec axisGlobalTrackY{mapNBins, mapMin, mapMax, "Y_{track} (cm)", "axisGlobalTrackY"}; + AxisSpec axisEta{etaNBins, etaMin, etaMax, "#eta", "axisEta"}; + AxisSpec axisPhi{phiNBins, phiMin, phiMax, "#phi", "axisPhi"}; + AxisSpec axisResiduals{residualsNBins, residualsMin, residualsMax, "Residuals (cm)", "axisResiduals"}; + AxisSpec axisChamber{10, -1.5, 8.5, "Chamber number", "axisChamber"}; + + for (int iCh = 0; iCh < Nch; iCh++) { + registry.add(Form("hEtaRich%d", iCh), Form("hEtaRich%d", iCh), HistType::kTH1F, {axisEta}); + registry.add(Form("hPhiRich%d", iCh), Form("hPhiRich%d", iCh), HistType::kTH1F, {axisPhi}); + + registry.add(Form("hmpidXYHmpidMipRich%dPos", iCh), "MIP XY - Q_{MIP} cut;X_{MIP} (cm);Y_{MIP} (cm)", HistType::kTHnSparseF, {axisHmpidMom, axisMipX, axisMipY}); + registry.add(Form("hmpidXYGlobalTrackRich%dPos", iCh), "Global track XY - all;X_{track} (cm);Y_{track} (cm)", HistType::kTHnSparseF, {axisGlobalMom, axisGlobalTrackX, axisGlobalTrackY}); + registry.add(Form("hmpidXYHmpidMipRich%dNeg", iCh), "MIP XY - Q_{MIP} cut;X_{MIP} (cm);Y_{MIP} (cm)", HistType::kTHnSparseF, {axisHmpidMom, axisMipX, axisMipY}); + registry.add(Form("hmpidXYGlobalTrackRich%dNeg", iCh), "Global track XY - all;X_{track} (cm);Y_{track} (cm)", HistType::kTHnSparseF, {axisGlobalMom, axisGlobalTrackX, axisGlobalTrackY}); + + registry.add(Form("residualsXVsMomentumRich%dPos", iCh), Form("residualsXVsMomentumRich%dPos", iCh), HistType::kTH2F, {axisHmpidMom, axisResiduals}); + registry.add(Form("residualsYVsMomentumRich%dPos", iCh), Form("residualsYVsMomentumRich%dPos", iCh), HistType::kTH2F, {axisHmpidMom, axisResiduals}); + registry.add(Form("residualsXVsMomentumRich%dNeg", iCh), Form("residualsXVsMomentumRich%dNeg", iCh), HistType::kTH2F, {axisHmpidMom, axisResiduals}); + registry.add(Form("residualsYVsMomentumRich%dNeg", iCh), Form("residualsYVsMomentumRich%dNeg", iCh), HistType::kTH2F, {axisHmpidMom, axisResiduals}); + + // Momentum and Position correlations + registry.add(Form("hMomTrackVsHmpidRich%dPos", iCh), Form("hMomTrackVsHmpidRich%dPos", iCh), HistType::kTH2F, {axisGlobalMom, axisHmpidMom}); + registry.add(Form("hXTrackVsXMipRich%dPos", iCh), Form("hXTrackVsXMipRich%dPos", iCh), HistType::kTH2F, {axisGlobalTrackX, axisMipX}); + registry.add(Form("hYTrackVsYMipRich%dPos", iCh), Form("hYTrackVsYMipRich%dPos", iCh), HistType::kTH2F, {axisGlobalTrackY, axisMipY}); + registry.add(Form("hMomTrackVsHmpidRich%dNeg", iCh), Form("hMomTrackVsHmpidRich%dNeg", iCh), HistType::kTH2F, {axisGlobalMom, axisHmpidMom}); + registry.add(Form("hXTrackVsXMipRich%dNeg", iCh), Form("hXTrackVsXMipRich%dNeg", iCh), HistType::kTH2F, {axisGlobalTrackX, axisMipX}); + registry.add(Form("hYTrackVsYMipRich%dNeg", iCh), Form("hYTrackVsYMipRich%dNeg", iCh), HistType::kTH2F, {axisGlobalTrackY, axisMipY}); + } // end loop over chambers + + } // end init + + void process(aod::HmpidAnalysis const& hmpidTable) + { + for (const auto& hmpid : hmpidTable) { + if (hmpid.itsNCluster() < minReqClusterITS) { + continue; + } + if (hmpid.tpcNCluster() < minTPCnClsFound) { + continue; + } + if (hmpid.tpcNClsCrossedRows() < minNCrossedRowsTPC) { + continue; + } + if (hmpid.tpcChi2() > maxChi2TPC) { + continue; + } + if (hmpid.itsChi2() > maxChi2ITS) { + continue; + } + if (std::abs(hmpid.dcaXY()) > maxDCAxy) { + continue; + } + if (std::abs(hmpid.dcaZ()) > maxDCAz) { + continue; + } + + // ------------------------- + // derived quantities + // ------------------------- + const int chamber = static_cast(hmpid.chamber()); + + const float momHmpid = hmpid.momentumHmpid(); + const float momTrackAbs = hmpid.momentumTrack(); + const float momHmpidAbs = std::abs(momHmpid); + + const bool isPos = momHmpid > 0; + const bool isNeg = momHmpid < 0; + const bool passQ = hmpid.chargeMip() > qMipMin && hmpid.chargeMip() < qMipMax; + + switch (chamber) { + case Rich0: + registry.fill(HIST("hEtaRich0"), hmpid.etaTrack()); + registry.fill(HIST("hPhiRich0"), hmpid.phiTrack()); + if (isPos) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich0Pos"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich0Pos"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich0Pos"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich0Pos"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich0Pos"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich0Pos"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich0Pos"), hmpid.yTrack(), hmpid.yMip()); + } + if (isNeg) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich0Neg"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich0Neg"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich0Neg"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich0Neg"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich0Neg"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich0Neg"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich0Neg"), hmpid.yTrack(), hmpid.yMip()); + } + break; + case Rich1: + registry.fill(HIST("hEtaRich1"), hmpid.etaTrack()); + registry.fill(HIST("hPhiRich1"), hmpid.phiTrack()); + if (isPos) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich1Pos"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich1Pos"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich1Pos"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich1Pos"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich1Pos"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich1Pos"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich1Pos"), hmpid.yTrack(), hmpid.yMip()); + } + if (isNeg) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich1Neg"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich1Neg"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich1Neg"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich1Neg"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich1Neg"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich1Neg"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich1Neg"), hmpid.yTrack(), hmpid.yMip()); + } + break; + case Rich2: + registry.fill(HIST("hEtaRich2"), hmpid.etaTrack()); + registry.fill(HIST("hPhiRich2"), hmpid.phiTrack()); + if (isPos) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich2Pos"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich2Pos"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich2Pos"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich2Pos"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich2Pos"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich2Pos"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich2Pos"), hmpid.yTrack(), hmpid.yMip()); + } + if (isNeg) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich2Neg"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich2Neg"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich2Neg"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich2Neg"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich2Neg"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich2Neg"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich2Neg"), hmpid.yTrack(), hmpid.yMip()); + } + break; + case Rich3: + registry.fill(HIST("hEtaRich3"), hmpid.etaTrack()); + registry.fill(HIST("hPhiRich3"), hmpid.phiTrack()); + if (isPos) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich3Pos"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich3Pos"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich3Pos"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich3Pos"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich3Pos"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich3Pos"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich3Pos"), hmpid.yTrack(), hmpid.yMip()); + } + if (isNeg) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich3Neg"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich3Neg"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich3Neg"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich3Neg"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich3Neg"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich3Neg"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich3Neg"), hmpid.yTrack(), hmpid.yMip()); + } + break; + case Rich4: + registry.fill(HIST("hEtaRich4"), hmpid.etaTrack()); + registry.fill(HIST("hPhiRich4"), hmpid.phiTrack()); + if (isPos) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich4Pos"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich4Pos"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich4Pos"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich4Pos"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich4Pos"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich4Pos"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich4Pos"), hmpid.yTrack(), hmpid.yMip()); + } + if (isNeg) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich4Neg"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich4Neg"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich4Neg"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich4Neg"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich4Neg"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich4Neg"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich4Neg"), hmpid.yTrack(), hmpid.yMip()); + } + break; + case Rich5: + registry.fill(HIST("hEtaRich5"), hmpid.etaTrack()); + registry.fill(HIST("hPhiRich5"), hmpid.phiTrack()); + if (isPos) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich5Pos"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich5Pos"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich5Pos"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich5Pos"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich5Pos"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich5Pos"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich5Pos"), hmpid.yTrack(), hmpid.yMip()); + } + if (isNeg) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich5Neg"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich5Neg"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich5Neg"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich5Neg"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich5Neg"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich5Neg"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich5Neg"), hmpid.yTrack(), hmpid.yMip()); + } + break; + case Rich6: + registry.fill(HIST("hEtaRich6"), hmpid.etaTrack()); + registry.fill(HIST("hPhiRich6"), hmpid.phiTrack()); + if (isPos) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich6Pos"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich6Pos"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich6Pos"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich6Pos"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich6Pos"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich6Pos"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich6Pos"), hmpid.yTrack(), hmpid.yMip()); + } + if (isNeg) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich6Neg"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich6Neg"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich6Neg"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich6Neg"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich6Neg"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich6Neg"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich6Neg"), hmpid.yTrack(), hmpid.yMip()); + } + break; + default: + break; + } // end switch on chamber + } // end of loop over hmpidTable + } // end process +}; // end struct + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + }; +} diff --git a/DPG/Tasks/AOTTrack/PID/HMPID/hmpidDeuteron.cxx b/DPG/Tasks/AOTTrack/PID/HMPID/hmpidDeuteron.cxx deleted file mode 100644 index e41e69d842c..00000000000 --- a/DPG/Tasks/AOTTrack/PID/HMPID/hmpidDeuteron.cxx +++ /dev/null @@ -1,437 +0,0 @@ -// Copyright 2019-2020 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 Analysis task for the (anti-)deuteron absorption cross section using HMPID -// \authors rocco.liotino@cern.ch - -#include "tableHMPID.h" - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -using namespace o2; -using namespace o2::framework; - -struct HmpidDeuteron { - HistogramRegistry registryDA{"registryDA", {}, OutputObjHandlingPolicy::AnalysisObject}; - - Configurable nsigmaTPCMin{"nsigmaTPCMin", -3.0, "nsigmaTPCMin"}; - Configurable nsigmaTPCMax{"nsigmaTPCMax", +3.0, "nsigmaTPCMax"}; - Configurable nsigmaTOFMin{"nsigmaTOFMin", -3.0, "nsigmaTOFMin"}; - Configurable nsigmaTOFMax{"nsigmaTOFMax", +3.0, "nsigmaTOFMax"}; - Configurable minReqClusterITS{"minReqClusterITS", 4.0, "min number of clusters required in ITS"}; - Configurable minTPCnClsFound{"minTPCnClsFound", 50.0f, "minTPCnClsFound"}; - Configurable minNCrossedRowsTPC{"minNCrossedRowsTPC", 70.0f, "min number of crossed rows TPC"}; - Configurable maxChi2ITS{"maxChi2ITS", 36.0f, "max chi2 per cluster ITS"}; - Configurable maxChi2TPC{"maxChi2TPC", 4.0f, "max chi2 per cluster TPC"}; - Configurable maxDCAxy{"maxDCAxy", 0.5f, "maxDCAxy"}; - Configurable maxDCAz{"maxDCAz", 0.5f, "maxDCAz"}; - - Configurable cutMinMomGlobalTrack{"cutMinMomGlobalTrack", 1.5f, "minimum momentum of global track"}; - - Configurable offsetX{"offsetX", 0.696712f, "Residuals offset - X direction"}; - Configurable offsetY{"offsetY", 2.32849f, "Residuals offset - Y direction"}; - - // Configurables for plotting - // Momentum - Configurable momNBins{"momNBins", 490, "Momentum bins"}; - Configurable momMin{"momMin", 0.1f, "Momentum min"}; - Configurable momMax{"momMax", 5.0f, "Momentum max"}; - - // DeltaR - Configurable deltaRNBins{"deltaRNBins", 500, "DeltaR bins"}; - Configurable deltaRMin{"deltaRMin", 0.0f, "DeltaR min"}; - Configurable deltaRMax{"deltaRMax", 50.0f, "DeltaR max"}; - - // nSigma TPC - Configurable nSigmaTPCNBins{"nSigmaTPCNBins", 20, "nSigmaTPC bins"}; - Configurable nSigmaTPCMinPlot{"nSigmaTPCMinPlot", -5.0f, "nSigmaTPC min"}; - Configurable nSigmaTPCMaxPlot{"nSigmaTPCMaxPlot", 5.0f, "nSigmaTPC max"}; - - // nSigma TOF - Configurable nSigmaTOFNBins{"nSigmaTOFNBins", 20, "nSigmaTOF bins"}; - Configurable nSigmaTOFMinPlot{"nSigmaTOFMinPlot", -5.0f, "nSigmaTOF min"}; - Configurable nSigmaTOFMaxPlot{"nSigmaTOFMaxPlot", 5.0f, "nSigmaTOF max"}; - - // Charge - Configurable chargeNBins{"chargeNBins", 1000, "Charge bins"}; - Configurable chargeMin{"chargeMin", 200.0f, "Charge min"}; - Configurable chargeMax{"chargeMax", 2200.0f, "Charge max"}; - - // Eta - Configurable etaNBins{"etaNBins", 100, "Eta bins"}; - Configurable etaMin{"etaMin", -5.0f, "Eta min"}; - Configurable etaMax{"etaMax", 5.0f, "Eta max"}; - - // Phi - Configurable phiNBins{"phiNBins", 100, "Phi bins"}; - Configurable phiMin{"phiMin", -10.0f, "Phi min"}; - Configurable phiMax{"phiMax", 10.0f, "Phi max"}; - - // Mass - Configurable massNBins{"massNBins", 1000, "Mass bins"}; - Configurable massMin{"massMin", -10.0f, "Mass min"}; - Configurable massMax{"massMax", 10.0f, "Mass max"}; - - // variables for chamber_number and HVs/PCs - static constexpr int Rich0 = 0, Rich1 = 1, Rich2 = 2, Rich3 = 3, Rich4 = 4, Rich5 = 5, Rich6 = 6; - static constexpr int Nch = 7; - static constexpr float Nmean = 1.28947; // meanIdxRad(); method from param not working, using value from HMPIDBase/Param.cxx - - void init(o2::framework::InitContext const&) - { - - // Axes for histograms - AxisSpec axisMom{momNBins, momMin, momMax, "#it{p} (GeV/#it{c})", "axisMom"}; - AxisSpec axisDeltaR{deltaRNBins, deltaRMin, deltaRMax, "#Delta R (cm)", "axisDeltaR"}; - AxisSpec axisNsigmaTOF{nSigmaTOFNBins, nSigmaTOFMinPlot, nSigmaTOFMaxPlot, "n#sigma_{TOF}", "axisNsigmaTOF"}; - AxisSpec axisNsigmaTPC{nSigmaTPCNBins, nSigmaTPCMinPlot, nSigmaTPCMaxPlot, "n#sigma_{TPC}", "axisNsigmaTPC"}; - AxisSpec axisCharge{chargeNBins, chargeMin, chargeMax, "Charge (ADC)", "axisCharge"}; - AxisSpec axisEta{etaNBins, etaMin, etaMax, "#eta", "axisEta"}; - AxisSpec axisPhi{phiNBins, phiMin, phiMax, "#phi", "axisPhi"}; - AxisSpec axisMass{massNBins, massMin, massMax, "mass^{2} (GeV/#it{c}^{2})", "axisMass"}; - AxisSpec axisChamber{10, -1.5, 8.5, "Chamber number", "axisChamber"}; - - // Deuteron Pos - registryDA.add("incomingDe_Pos_8cm", "incomingDe_Pos_8cm", HistType::kTH1F, {axisMom}); - registryDA.add("incomingDe_Pos_4cm", "incomingDe_Pos_4cm", HistType::kTH1F, {axisMom}); - - registryDA.add("De_Pos_deltaR_8cm", "De_Pos_deltaR_8cm", HistType::kTH1F, {axisDeltaR}); - registryDA.add("De_Pos_deltaR_4cm", "De_Pos_deltaR_4cm", HistType::kTH1F, {axisDeltaR}); - - registryDA.add("nSigmaTOF_vs_momHMPID_DePos_8cm", "nSigmaTOF_vs_momHMPID_DePos_8cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); - registryDA.add("nSigmaTOF_vs_momHMPID_DePos_4cm", "nSigmaTOF_vs_momHMPID_DePos_4cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); - - registryDA.add("survivingDe_Pos_8cm", "survivingDe_Pos_8cm", HistType::kTH2F, {axisMom, axisDeltaR}); - registryDA.add("survivingDe_Pos_4cm", "survivingDe_Pos_4cm", HistType::kTH2F, {axisMom, axisDeltaR}); - - registryDA.add("De_Pos_Q_8cm", "De_Pos_Q_8cm", HistType::kTH2F, {axisMom, axisCharge}); - registryDA.add("De_Pos_Q_4cm", "De_Pos_Q_4cm", HistType::kTH2F, {axisMom, axisCharge}); - - // nsigma plots - registryDA.add("nSigmaTPC_vs_momHMPID_Cut_DePos", "nSigmaTPC_vs_momHMPID_Cut_DePos", HistType::kTH2F, {axisMom, axisNsigmaTPC}); - registryDA.add("nSigmaTOF_vs_momHMPID_Cut_DePos", "nSigmaTOF_vs_momHMPID_Cut_DePos", HistType::kTH2F, {axisMom, axisNsigmaTOF}); - - // check residuals distributions - registryDA.add("residualsX_Vs_momentum_Neg_8cm", "residualsX_Vs_momentum_Neg_8cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "x_{mip} - x_{track} (cm)"}}); - registryDA.add("residualsY_Vs_momentum_Neg_8cm", "residualsY_Vs_momentum_Neg_8cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "y_{mip} - y_{track} (cm)"}}); - registryDA.add("residualsX_Vs_momentum_Pos_8cm", "residualsX_Vs_momentum_Pos_8cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "x_{mip} - x_{track} (cm)"}}); - registryDA.add("residualsY_Vs_momentum_Pos_8cm", "residualsY_Vs_momentum_Pos_8cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "y_{mip} - y_{track} (cm)"}}); - - // Deuteron Neg - registryDA.add("incomingDe_Neg_8cm", "incomingDe_Neg_8cm", HistType::kTH1F, {axisMom}); - registryDA.add("incomingDe_Neg_4cm", "incomingDe_Neg_4cm", HistType::kTH1F, {axisMom}); - - registryDA.add("De_Neg_deltaR_8cm", "De_Neg_deltaR_8cm", HistType::kTH1F, {axisDeltaR}); - registryDA.add("De_Neg_deltaR_4cm", "De_Neg_deltaR_4cm", HistType::kTH1F, {axisDeltaR}); - - registryDA.add("nSigmaTOF_vs_momHMPID_DeNeg_8cm", "nSigmaTOF_vs_momHMPID_DeNeg_8cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); - registryDA.add("nSigmaTOF_vs_momHMPID_DeNeg_4cm", "nSigmaTOF_vs_momHMPID_DeNeg_4cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); - - registryDA.add("survivingDe_Neg_8cm", "survivingDe_Neg_8cm", HistType::kTH2F, {axisMom, axisDeltaR}); - registryDA.add("survivingDe_Neg_4cm", "survivingDe_Neg_4cm", HistType::kTH2F, {axisMom, axisDeltaR}); - registryDA.add("De_Neg_Q_8cm", "De_Neg_Q_8cm", HistType::kTH2F, {axisMom, axisCharge}); - registryDA.add("De_Neg_Q_4cm", "De_Neg_Q_4cm", HistType::kTH2F, {axisMom, axisCharge}); - registryDA.add("De_Neg_momentum", "De_Neg_momentum", HistType::kTH2F, {{100, 0.0, 5.0, "#it{p}_{vtx} (GeV/#it{c})"}, {100, 0.0, 5.0, "#it{p}_{hmpid} (GeV/#it{c})"}}); - - // check residuals distributions - registryDA.add("residualsX_Vs_momentum_Neg_4cm", "residualsX_Vs_momentum_Neg_4cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "x_{mip} - x_{track} (cm)"}}); - registryDA.add("residualsY_Vs_momentum_Neg_4cm", "residualsY_Vs_momentum_Neg_4cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "y_{mip} - y_{track} (cm)"}}); - registryDA.add("residualsX_Vs_momentum_Pos_4cm", "residualsX_Vs_momentum_Pos_4cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "x_{mip} - x_{track} (cm)"}}); - registryDA.add("residualsY_Vs_momentum_Pos_4cm", "residualsY_Vs_momentum_Pos_4cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "y_{mip} - y_{track} (cm)"}}); - - // nsigma plots - registryDA.add("nSigmaTPC_vs_momHMPID_Cut_DeNeg", "nSigmaTPC_vs_momHMPID_Cut_DeNeg", HistType::kTH2F, {axisMom, axisNsigmaTPC}); - registryDA.add("nSigmaTOF_vs_momHMPID_Cut_DeNeg", "nSigmaTOF_vs_momHMPID_Cut_DeNeg", HistType::kTH2F, {axisMom, axisNsigmaTOF}); - - // general plots - registryDA.add("hEta", "hEta", kTH1F, {axisEta}); - registryDA.add("hPhi", "hPhi", kTH1F, {axisPhi}); - registryDA.add("hMomentumHmpid", "hMomentumHmpid", kTH1F, {{1000, -10.f, 10.f, "#it{p}_{hmpid} (GeV/#it{c})"}}); - - registryDA.add("hMass", "hMass", kTH1F, {axisMass}); - registryDA.add("hMass_postDeuteron", "hMass_postDeuteron", kTH1F, {axisMass}); - registryDA.add("hMass_Vs_centrality", "hMass_Vs_centrality", kTH2F, {axisMass, {100, 0.0f, 100.0f, "centrality (%)"}}); - - for (int iCh = 0; iCh < Nch; iCh++) { - registryDA.add(Form("De_Pos_deltaR_%d", iCh), Form("De_Pos_deltaR_%d", iCh), HistType::kTH1F, {axisDeltaR}); - registryDA.add(Form("De_Neg_deltaR_%d", iCh), Form("De_Neg_deltaR_%d", iCh), HistType::kTH1F, {axisDeltaR}); - registryDA.add(Form("hEta_%d", iCh), Form("hEta_%d", iCh), HistType::kTH1F, {axisEta}); - registryDA.add(Form("hPhi_%d", iCh), Form("hPhi_%d", iCh), HistType::kTH1F, {axisPhi}); - } - } - - void process(aod::HmpidAnalysis const& hmpidTable) - { - for (const auto& hmpid : hmpidTable) { - - // ------------------------- - // track filters - // ------------------------- - if (hmpid.itsNCluster() < minReqClusterITS) { - continue; - } - if (hmpid.tpcNCluster() < minTPCnClsFound) { - continue; - } - if (hmpid.tpcNClsCrossedRows() < minNCrossedRowsTPC) { - continue; - } - if (hmpid.tpcChi2() > maxChi2TPC) { - continue; - } - if (hmpid.itsChi2() > maxChi2ITS) { - continue; - } - if (std::abs(hmpid.dcaXY()) > maxDCAxy) { - continue; - } - if (std::abs(hmpid.dcaZ()) > maxDCAz) { - continue; - } - - // ------------------------- - // derived quantities - // ------------------------- - const float momHmpid = hmpid.momentumHmpid(); - const float momAbs = std::abs(momHmpid); - - const float dx = hmpid.xMip() - hmpid.xTrack(); - const float dy = hmpid.yMip() - hmpid.yTrack(); - const float dr = std::hypot(dx - offsetX, dy - offsetY); - - const float mass = - std::pow(Nmean * momAbs * std::cos(hmpid.chAngle()), 2) - - std::pow(momAbs, 2); - - const int chamber = static_cast(hmpid.chamber()); - const bool isPos = momHmpid > 0; - const bool isNeg = momHmpid < 0; - - // ------------------------- - // global distributions - // ------------------------- - registryDA.fill(HIST("hEta"), hmpid.etaTrack()); - registryDA.fill(HIST("hPhi"), hmpid.phiTrack()); - registryDA.fill(HIST("hMomentumHmpid"), momHmpid); - - if (hmpid.chAngle() > 0) { - registryDA.fill(HIST("hMass"), mass); - registryDA.fill(HIST("hMass_Vs_centrality"), mass, hmpid.centrality()); - } - - // ------------------------- - // RICH chamber maps (0–6) - // ------------------------- - if (chamber == Rich0) { - registryDA.fill(HIST("hEta_0"), hmpid.etaTrack()); - registryDA.fill(HIST("hPhi_0"), hmpid.phiTrack()); - } - if (chamber == Rich1) { - registryDA.fill(HIST("hEta_1"), hmpid.etaTrack()); - registryDA.fill(HIST("hPhi_1"), hmpid.phiTrack()); - } - if (chamber == Rich2) { - registryDA.fill(HIST("hEta_2"), hmpid.etaTrack()); - registryDA.fill(HIST("hPhi_2"), hmpid.phiTrack()); - } - if (chamber == Rich3) { - registryDA.fill(HIST("hEta_3"), hmpid.etaTrack()); - registryDA.fill(HIST("hPhi_3"), hmpid.phiTrack()); - } - if (chamber == Rich4) { - registryDA.fill(HIST("hEta_4"), hmpid.etaTrack()); - registryDA.fill(HIST("hPhi_4"), hmpid.phiTrack()); - } - if (chamber == Rich5) { - registryDA.fill(HIST("hEta_5"), hmpid.etaTrack()); - registryDA.fill(HIST("hPhi_5"), hmpid.phiTrack()); - } - if (chamber == Rich6) { - registryDA.fill(HIST("hEta_6"), hmpid.etaTrack()); - registryDA.fill(HIST("hPhi_6"), hmpid.phiTrack()); - } - - // ------------------------- - // deuteron candidate cuts - TPC - // ------------------------- - if (hmpid.tpcNSigmaDe() < nsigmaTPCMin || - hmpid.tpcNSigmaDe() > nsigmaTPCMax) { - continue; - } - - // ------------------------- - // post TPC cut - // ------------------------- - if (isPos) { - registryDA.fill(HIST("nSigmaTPC_vs_momHMPID_Cut_DePos"), - momAbs, hmpid.tpcNSigmaDe()); - } - if (isNeg) { - registryDA.fill(HIST("nSigmaTPC_vs_momHMPID_Cut_DeNeg"), - momAbs, hmpid.tpcNSigmaDe()); - } - - registryDA.fill(HIST("hMass_postDeuteron"), mass); - - // ------------------------- - // deuteron candidate cuts - TOF - // ------------------------- - if (hmpid.tofNSigmaDe() < nsigmaTOFMin || - hmpid.tofNSigmaDe() > nsigmaTOFMax) { - continue; - } - - // ------------------------- - // TOF nsigma - // ------------------------- - if (isPos) { - registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_Cut_DePos"), - momAbs, hmpid.tofNSigmaDe()); - } - - if (isNeg) { - registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_Cut_DeNeg"), - momAbs, hmpid.tofNSigmaDe()); - } - - // ------------------------- - // DEUTERON CANDIDATES AFTER ALL CUTS - // ------------------------- - - // ------------------------- - // deltaR per chamber (Pos/Neg) - // ------------------------- - if (chamber == Rich0) { - if (isPos) { - registryDA.fill(HIST("De_Pos_deltaR_0"), dr); - } - if (isNeg) { - registryDA.fill(HIST("De_Neg_deltaR_0"), dr); - } - } - if (chamber == Rich1) { - if (isPos) { - registryDA.fill(HIST("De_Pos_deltaR_1"), dr); - } - if (isNeg) { - registryDA.fill(HIST("De_Neg_deltaR_1"), dr); - } - } - if (chamber == Rich2) { - if (isPos) { - registryDA.fill(HIST("De_Pos_deltaR_2"), dr); - } - if (isNeg) { - registryDA.fill(HIST("De_Neg_deltaR_2"), dr); - } - } - if (chamber == Rich3) { - if (isPos) { - registryDA.fill(HIST("De_Pos_deltaR_3"), dr); - } - if (isNeg) { - registryDA.fill(HIST("De_Neg_deltaR_3"), dr); - } - } - if (chamber == Rich4) { - if (isPos) { - registryDA.fill(HIST("De_Pos_deltaR_4"), dr); - } - if (isNeg) { - registryDA.fill(HIST("De_Neg_deltaR_4"), dr); - } - } - if (chamber == Rich5) { - if (isPos) { - registryDA.fill(HIST("De_Pos_deltaR_5"), dr); - } - if (isNeg) { - registryDA.fill(HIST("De_Neg_deltaR_5"), dr); - } - } - if (chamber == Rich6) { - if (isPos) { - registryDA.fill(HIST("De_Pos_deltaR_6"), dr); - } - if (isNeg) { - registryDA.fill(HIST("De_Neg_deltaR_6"), dr); - } - } - - // ------------------------- - // absorbers - // ------------------------- - const int abs4cm = 2; - const int abs8cm = 4; - - if (isPos && chamber == abs8cm) { - registryDA.fill(HIST("incomingDe_Pos_8cm"), momAbs); - registryDA.fill(HIST("survivingDe_Pos_8cm"), momAbs, dr); - registryDA.fill(HIST("De_Pos_Q_8cm"), momAbs, hmpid.chargeMip()); - registryDA.fill(HIST("De_Pos_deltaR_8cm"), dr); - registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_DePos_8cm"), momAbs, hmpid.tofNSigmaDe()); - - registryDA.fill(HIST("residualsX_Vs_momentum_Pos_8cm"), momAbs, dx); - registryDA.fill(HIST("residualsY_Vs_momentum_Pos_8cm"), momAbs, dy); - } - - if (isNeg && chamber == abs8cm) { - registryDA.fill(HIST("incomingDe_Neg_8cm"), momAbs); - registryDA.fill(HIST("survivingDe_Neg_8cm"), momAbs, dr); - registryDA.fill(HIST("De_Neg_Q_8cm"), momAbs, hmpid.chargeMip()); - registryDA.fill(HIST("De_Neg_deltaR_8cm"), dr); - registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_DeNeg_8cm"), momAbs, hmpid.tofNSigmaDe()); - - registryDA.fill(HIST("residualsX_Vs_momentum_Neg_8cm"), momAbs, dx); - registryDA.fill(HIST("residualsY_Vs_momentum_Neg_8cm"), momAbs, dy); - } - - if (isPos && chamber == abs4cm) { - registryDA.fill(HIST("incomingDe_Pos_4cm"), momAbs); - registryDA.fill(HIST("survivingDe_Pos_4cm"), momAbs, dr); - registryDA.fill(HIST("De_Pos_Q_4cm"), momAbs, hmpid.chargeMip()); - registryDA.fill(HIST("De_Pos_deltaR_4cm"), dr); - registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_DePos_4cm"), momAbs, hmpid.tofNSigmaDe()); - - registryDA.fill(HIST("residualsX_Vs_momentum_Pos_4cm"), momAbs, dx); - registryDA.fill(HIST("residualsY_Vs_momentum_Pos_4cm"), momAbs, dy); - } - - if (isNeg && chamber == abs4cm) { - registryDA.fill(HIST("incomingDe_Neg_4cm"), momAbs); - registryDA.fill(HIST("survivingDe_Neg_4cm"), momAbs, dr); - registryDA.fill(HIST("De_Neg_Q_4cm"), momAbs, hmpid.chargeMip()); - registryDA.fill(HIST("De_Neg_deltaR_4cm"), dr); - registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_DeNeg_4cm"), momAbs, hmpid.tofNSigmaDe()); - - registryDA.fill(HIST("residualsX_Vs_momentum_Neg_4cm"), momAbs, dx); - registryDA.fill(HIST("residualsY_Vs_momentum_Neg_4cm"), momAbs, dy); - } - } // end of loop over hmpidTable - } // end of process -}; - -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - return WorkflowSpec{ - adaptAnalysisTask(cfgc), - }; -} diff --git a/DPG/Tasks/AOTTrack/PID/TOF/qaPIDTOFEvTime.cxx b/DPG/Tasks/AOTTrack/PID/TOF/qaPIDTOFEvTime.cxx index f9b6f0c620a..eaf04ba94da 100644 --- a/DPG/Tasks/AOTTrack/PID/TOF/qaPIDTOFEvTime.cxx +++ b/DPG/Tasks/AOTTrack/PID/TOF/qaPIDTOFEvTime.cxx @@ -436,11 +436,11 @@ struct tofPidCollisionTimeQa { histos.fill(HIST("goodforevtime/tofSignalPerCollision"), ncolls % 6000, trk.tofSignal()); } } - static_cast(listEfficiency->FindObject("effTOFEvTime"))->Fill(t.isEvTimeTOF(), nTracksWithTOF); - static_cast(listEfficiency->FindObject("effT0AEvTime"))->Fill(collision.has_foundFT0() && collision.t0ACorrectedValid(), nTracksWithTOF); - static_cast(listEfficiency->FindObject("effT0CEvTime"))->Fill(collision.has_foundFT0() && collision.t0CCorrectedValid(), nTracksWithTOF); - static_cast(listEfficiency->FindObject("effT0ACEvTime"))->Fill(collision.has_foundFT0() && collision.t0ACValid(), nTracksWithTOF); - static_cast(listEfficiency->FindObject("effTOFT0ACEvTime"))->Fill(t.isEvTimeTOF() && collision.has_foundFT0() && collision.t0ACorrectedValid(), nTracksWithTOF); + dynamic_cast(listEfficiency->FindObject("effTOFEvTime"))->Fill(t.isEvTimeTOF(), nTracksWithTOF); + dynamic_cast(listEfficiency->FindObject("effT0AEvTime"))->Fill(collision.has_foundFT0() && collision.t0ACorrectedValid(), nTracksWithTOF); + dynamic_cast(listEfficiency->FindObject("effT0CEvTime"))->Fill(collision.has_foundFT0() && collision.t0CCorrectedValid(), nTracksWithTOF); + dynamic_cast(listEfficiency->FindObject("effT0ACEvTime"))->Fill(collision.has_foundFT0() && collision.t0ACValid(), nTracksWithTOF); + dynamic_cast(listEfficiency->FindObject("effTOFT0ACEvTime"))->Fill(t.isEvTimeTOF() && collision.has_foundFT0() && collision.t0ACorrectedValid(), nTracksWithTOF); } } PROCESS_SWITCH(tofPidCollisionTimeQa, processData, "Process data", true); diff --git a/DPG/Tasks/AOTTrack/V0Cascades/qaEfficiencyV0s.cxx b/DPG/Tasks/AOTTrack/V0Cascades/qaEfficiencyV0s.cxx index ff6236ec9a9..8a5a06a9b07 100644 --- a/DPG/Tasks/AOTTrack/V0Cascades/qaEfficiencyV0s.cxx +++ b/DPG/Tasks/AOTTrack/V0Cascades/qaEfficiencyV0s.cxx @@ -125,12 +125,12 @@ struct QaEfficiencyV0s { } for (int i = 0; i < nSpecies; i++) { - static_cast(listEfficiencyMC->At(i * nSpecies))->SetPassedHistogram(*histograms[i][kHistoPtNum].get(), "f"); - static_cast(listEfficiencyMC->At(i * nSpecies))->SetTotalHistogram(*histograms[i][kHistoPtDen].get(), "f"); - static_cast(listEfficiencyMC->At(i * nSpecies + 1))->SetPassedHistogram(*histogramsPrm[i][kHistoPtNum].get(), "f"); - static_cast(listEfficiencyMC->At(i * nSpecies + 1))->SetTotalHistogram(*histogramsPrm[i][kHistoPtDen].get(), "f"); - static_cast(listEfficiencyMC->At(i * nSpecies + 2))->SetPassedHistogram(*histogramsPrmRap[i][kHistoPtNum].get(), "f"); - static_cast(listEfficiencyMC->At(i * nSpecies + 2))->SetTotalHistogram(*histogramsPrmRap[i][kHistoPtDen].get(), "f"); + dynamic_cast(listEfficiencyMC->At(i * nSpecies))->SetPassedHistogram(*histograms[i][kHistoPtNum].get(), "f"); + dynamic_cast(listEfficiencyMC->At(i * nSpecies))->SetTotalHistogram(*histograms[i][kHistoPtDen].get(), "f"); + dynamic_cast(listEfficiencyMC->At(i * nSpecies + 1))->SetPassedHistogram(*histogramsPrm[i][kHistoPtNum].get(), "f"); + dynamic_cast(listEfficiencyMC->At(i * nSpecies + 1))->SetTotalHistogram(*histogramsPrm[i][kHistoPtDen].get(), "f"); + dynamic_cast(listEfficiencyMC->At(i * nSpecies + 2))->SetPassedHistogram(*histogramsPrmRap[i][kHistoPtNum].get(), "f"); + dynamic_cast(listEfficiencyMC->At(i * nSpecies + 2))->SetTotalHistogram(*histogramsPrmRap[i][kHistoPtDen].get(), "f"); } } }; diff --git a/DPG/Tasks/AOTTrack/derivedDataCreatorD0Calibration.cxx b/DPG/Tasks/AOTTrack/derivedDataCreatorD0Calibration.cxx index 7a45020ba65..4d1f8c3b0f1 100644 --- a/DPG/Tasks/AOTTrack/derivedDataCreatorD0Calibration.cxx +++ b/DPG/Tasks/AOTTrack/derivedDataCreatorD0Calibration.cxx @@ -117,6 +117,14 @@ struct DerivedDataCreatorD0Calibration { std::string prefix = "ml"; } cfgMl; + struct SelectedTrack { + int globalIndex; + o2::dataformats::DCA dca; + o2::track::TrackParametrizationWithError trackParCov; + int pidPion; + int pidKaon; + }; + using TracksWCovExtraPid = soa::Join; using TracksWCovExtraTmoPid = soa::Join; using TracksWCovExtraTmoPidAndQa = soa::Join; @@ -152,7 +160,7 @@ struct DerivedDataCreatorD0Calibration { ccdb->setLocalObjectValidityChecking(); if (cfgDownsampling.apply) { - histDownSampl.setObject(reinterpret_cast(ccdb->getSpecific(cfgDownsampling.pathCcdbWeights))); + histDownSampl.setObject(static_cast(ccdb->getSpecific(cfgDownsampling.pathCcdbWeights))); } if (cfgMl.apply) { @@ -187,7 +195,7 @@ struct DerivedDataCreatorD0Calibration { template void runDataCreation(CollisionsWEvSel const& collisions, aod::TrackAssoc const& trackIndices, - TTracks const&, + TTracks const& tracks, aod::BCsWithTimestamps const&, TTrackMeanOccs const&, TTrackQa const&) @@ -227,87 +235,65 @@ struct DerivedDataCreatorD0Calibration { eventIR.setFromLong(bc.globalBC()); auto groupedTrackIndices = trackIndices.sliceBy(trackIndicesPerCollision, collision.globalIndex()); - std::vector dcaNeg; // we cache the negative track, to avoid the same computation for each positive track - dcaNeg.resize(groupedTrackIndices.size()); - std::vector isSelectedNeg(groupedTrackIndices.size(), true); - std::vector isPropagatedNeg(groupedTrackIndices.size(), false); - std::vector pidTrackNegKaon(groupedTrackIndices.size(), -1); - std::vector pidTrackNegPion(groupedTrackIndices.size(), -1); - - for (auto const& trackIndexPos : groupedTrackIndices) { - auto trackPos = trackIndexPos.template track_as(); + // we cache tracks + std::vector tracksSelectedPos; + std::vector tracksSelectedNeg; + tracksSelectedPos.reserve(groupedTrackIndices.size() / 2); + tracksSelectedNeg.reserve(groupedTrackIndices.size() / 2); + + // first loop on tracks to find the selected ones + for (auto const& trackIndex : groupedTrackIndices) { + auto track = trackIndex.template track_as(); // track selections - if (trackPos.sign() < 0) { // first positive track - continue; - } - if (!trackPos.isGlobalTrackWoDCA()) { + if (!track.isGlobalTrackWoDCA()) { continue; } - if (trackPos.pt() < cfgTrackCuts.ptMin) { + if (track.pt() < cfgTrackCuts.ptMin) { continue; } - if (std::abs(trackPos.eta()) > cfgTrackCuts.absEtaMax) { + if (std::abs(track.eta()) > cfgTrackCuts.absEtaMax) { continue; } - auto trackParCovPos = getTrackParCov(trackPos); - o2::dataformats::DCA dcaPos(trackPos.dcaXY(), trackPos.dcaZ(), trackPos.cYY(), trackPos.cZY(), trackPos.cZZ()); - if (trackPos.collisionId() != collision.globalIndex()) { - trackParCovPos.propagateToDCA(primaryVertex, bz, &dcaPos); + auto trackParCov = getTrackParCov(track); + auto dcaTmp = o2::dataformats::DCA(track.dcaXY(), track.dcaZ(), track.cYY(), track.cZY(), track.cZZ()); + if (track.collisionId() != collision.globalIndex()) { + trackParCov.propagateToDCA(primaryVertex, bz, &dcaTmp); } - if (!isSelectedTrackDca(cfgTrackCuts.binsPt, cfgTrackCuts.limitsDca, trackParCovPos.getPt(), dcaPos.getY(), dcaPos.getZ())) { + if (!isSelectedTrackDca(cfgTrackCuts.binsPt, cfgTrackCuts.limitsDca, trackParCov.getPt(), dcaTmp.getY(), dcaTmp.getZ())) { continue; } - - int pidTrackPosKaon{-1}; - int pidTrackPosPion{-1}; + int pidTrackKaon{-1}; + int pidTrackPion{-1}; if (cfgTrackCuts.usePidTpcOnly) { /// kaon TPC PID positive daughter - pidTrackPosKaon = selectorKaon.statusTpc(trackPos); + pidTrackKaon = selectorKaon.statusTpc(track); /// pion TPC PID positive daughter - pidTrackPosPion = selectorPion.statusTpc(trackPos); + pidTrackPion = selectorPion.statusTpc(track); } else { /// kaon TPC, TOF PID positive daughter - pidTrackPosKaon = selectorKaon.statusTpcAndTof(trackPos); + pidTrackKaon = selectorKaon.statusTpcAndTof(track); /// pion TPC, TOF PID positive daughter - pidTrackPosPion = selectorPion.statusTpcAndTof(trackPos); + pidTrackPion = selectorPion.statusTpcAndTof(track); + } + if (track.sign() > 0) { // first positive track + tracksSelectedPos.push_back({trackIndex.trackId(), + dcaTmp, + trackParCov, + pidTrackPion, + pidTrackKaon}); + } else if (track.sign() < 0) { + tracksSelectedNeg.push_back({trackIndex.trackId(), + dcaTmp, + trackParCov, + pidTrackPion, + pidTrackKaon}); } + } - int iTrack{-1}; - for (auto const& trackIndexNeg : groupedTrackIndices) { - iTrack++; - if (!isSelectedNeg[iTrack]) { - continue; - } - auto trackNeg = trackIndexNeg.template track_as(); - // track selections - if (trackNeg.sign() > 0) { // second negative track - isSelectedNeg[iTrack] = false; - continue; - } - if (!trackNeg.isGlobalTrackWoDCA()) { - isSelectedNeg[iTrack] = false; - continue; - } - if (trackNeg.pt() < cfgTrackCuts.ptMin) { - isSelectedNeg[iTrack] = false; - continue; - } - if (std::abs(trackNeg.eta()) > cfgTrackCuts.absEtaMax) { - isSelectedNeg[iTrack] = false; - continue; - } - auto trackParCovNeg = getTrackParCov(trackNeg); - if (!isPropagatedNeg[iTrack]) { - dcaNeg[iTrack] = o2::dataformats::DCA(trackNeg.dcaXY(), trackNeg.dcaZ(), trackNeg.cYY(), trackNeg.cZY(), trackNeg.cZZ()); - if (trackNeg.collisionId() != collision.globalIndex()) { - trackParCovNeg.propagateToDCA(primaryVertex, bz, &dcaNeg[iTrack]); - } - isPropagatedNeg[iTrack] = true; - } - if (!isSelectedTrackDca(cfgTrackCuts.binsPt, cfgTrackCuts.limitsDca, trackParCovNeg.getPt(), dcaNeg[iTrack].getY(), dcaNeg[iTrack].getZ())) { - isSelectedNeg[iTrack] = false; - continue; - } + for (auto const& trackSelPos : tracksSelectedPos) { + auto trackPos = tracks.rawIteratorAt(trackSelPos.globalIndex); + for (auto const& trackSelNeg : tracksSelectedNeg) { + auto trackNeg = tracks.rawIteratorAt(trackSelNeg.globalIndex); // preselections // pt @@ -337,24 +323,12 @@ struct DerivedDataCreatorD0Calibration { } } - if (cfgTrackCuts.usePidTpcOnly) { - /// kaon TPC PID negative daughter - pidTrackNegKaon[iTrack] = selectorKaon.statusTpc(trackNeg); - /// pion TPC PID negative daughter - pidTrackNegPion[iTrack] = selectorPion.statusTpc(trackNeg); - } else { - /// kaon TPC, TOF PID negative daughter - pidTrackNegKaon[iTrack] = selectorKaon.statusTpcAndTof(trackNeg); - /// pion TPC, TOF PID negative daughter - pidTrackNegPion[iTrack] = selectorPion.statusTpcAndTof(trackNeg); - } - // PID uint8_t massHypo{D0MassHypo::D0AndD0Bar}; // both mass hypotheses a priori - if (pidTrackPosPion == TrackSelectorPID::Rejected || pidTrackNegKaon[iTrack] == TrackSelectorPID::Rejected) { + if (trackSelPos.pidPion == TrackSelectorPID::Rejected || trackSelNeg.pidKaon == TrackSelectorPID::Rejected) { massHypo -= D0MassHypo::D0; // exclude D0 } - if (pidTrackNegPion[iTrack] == TrackSelectorPID::Rejected || pidTrackPosKaon == TrackSelectorPID::Rejected) { + if (trackSelNeg.pidPion == TrackSelectorPID::Rejected || trackSelPos.pidKaon == TrackSelectorPID::Rejected) { massHypo -= D0MassHypo::D0Bar; // exclude D0Bar } if (massHypo == 0) { @@ -362,7 +336,7 @@ struct DerivedDataCreatorD0Calibration { } // d0xd0 - if (dcaPos.getY() * dcaNeg[iTrack].getY() > cuts->get(ptBinNoVtxD0, CutsIds::MaxD0D0)) { + if (trackSelPos.dca.getY() * trackSelNeg.dca.getY() > cuts->get(ptBinNoVtxD0, CutsIds::MaxD0D0)) { continue; } @@ -384,7 +358,7 @@ struct DerivedDataCreatorD0Calibration { } // reconstruct vertex - if (df.process(trackParCovPos, trackParCovNeg) == 0) { + if (df.process(trackSelPos.trackParCov, trackSelNeg.trackParCov) == 0) { continue; } const auto& secondaryVertex = df.getPCACandidate(); @@ -411,7 +385,7 @@ struct DerivedDataCreatorD0Calibration { } // d0xd0 - if (dcaPos.getY() * dcaNeg[iTrack].getY() > cuts->get(ptBinD0, CutsIds::MaxD0D0)) { + if (trackSelPos.dca.getY() * trackSelNeg.dca.getY() > cuts->get(ptBinD0, CutsIds::MaxD0D0)) { continue; } // cospa @@ -476,7 +450,7 @@ struct DerivedDataCreatorD0Calibration { } else { // apply BDT models if (cfgMl.apply) { - featuresCandD0 = {dcaPos.getY(), dcaNeg[iTrack].getY(), chi2PCA, cosPaD0, cosPaXYD0, decLenXYD0, decLenD0, dcaPos.getY() * dcaNeg[iTrack].getY(), aod::pid_tpc_tof_utils::combineNSigma(trackPos.tpcNSigmaPi(), trackPos.tofNSigmaPi()), aod::pid_tpc_tof_utils::combineNSigma(trackNeg.tpcNSigmaKa(), trackNeg.tofNSigmaKa()), trackPos.tpcNSigmaPi(), trackPos.tpcNSigmaKa(), aod::pid_tpc_tof_utils::combineNSigma(trackPos.tpcNSigmaKa(), trackPos.tofNSigmaKa()), trackNeg.tpcNSigmaPi(), trackNeg.tpcNSigmaKa(), aod::pid_tpc_tof_utils::combineNSigma(trackNeg.tpcNSigmaPi(), trackNeg.tofNSigmaPi())}; + featuresCandD0 = {trackSelPos.dca.getY(), trackSelNeg.dca.getY(), chi2PCA, cosPaD0, cosPaXYD0, decLenXYD0, decLenD0, trackSelPos.dca.getY() * trackSelNeg.dca.getY(), aod::pid_tpc_tof_utils::combineNSigma(trackPos.tpcNSigmaPi(), trackPos.tofNSigmaPi()), aod::pid_tpc_tof_utils::combineNSigma(trackNeg.tpcNSigmaKa(), trackNeg.tofNSigmaKa()), trackPos.tpcNSigmaPi(), trackPos.tpcNSigmaKa(), aod::pid_tpc_tof_utils::combineNSigma(trackPos.tpcNSigmaKa(), trackPos.tofNSigmaKa()), trackNeg.tpcNSigmaPi(), trackNeg.tpcNSigmaKa(), aod::pid_tpc_tof_utils::combineNSigma(trackNeg.tpcNSigmaPi(), trackNeg.tofNSigmaPi())}; if (!mlResponse.isSelectedMl(featuresCandD0, ptD0, bdtScoresD0)) { massHypo -= D0MassHypo::D0; } else { // selected, we compute cost* @@ -501,7 +475,7 @@ struct DerivedDataCreatorD0Calibration { } else { // apply BDT models if (cfgMl.apply) { - featuresCandD0bar = {dcaPos.getY(), dcaNeg[iTrack].getY(), chi2PCA, cosPaD0, cosPaXYD0, decLenXYD0, decLenD0, dcaPos.getY() * dcaNeg[iTrack].getY(), aod::pid_tpc_tof_utils::combineNSigma(trackPos.tpcNSigmaPi(), trackPos.tofNSigmaPi()), aod::pid_tpc_tof_utils::combineNSigma(trackNeg.tpcNSigmaKa(), trackNeg.tofNSigmaKa()), trackPos.tpcNSigmaPi(), trackPos.tpcNSigmaKa(), aod::pid_tpc_tof_utils::combineNSigma(trackPos.tpcNSigmaKa(), trackPos.tofNSigmaKa()), trackNeg.tpcNSigmaPi(), trackNeg.tpcNSigmaKa(), aod::pid_tpc_tof_utils::combineNSigma(trackNeg.tpcNSigmaPi(), trackNeg.tofNSigmaPi())}; + featuresCandD0bar = {trackSelPos.dca.getY(), trackSelNeg.dca.getY(), chi2PCA, cosPaD0, cosPaXYD0, decLenXYD0, decLenD0, trackSelPos.dca.getY() * trackSelNeg.dca.getY(), aod::pid_tpc_tof_utils::combineNSigma(trackPos.tpcNSigmaPi(), trackPos.tofNSigmaPi()), aod::pid_tpc_tof_utils::combineNSigma(trackNeg.tpcNSigmaKa(), trackNeg.tofNSigmaKa()), trackPos.tpcNSigmaPi(), trackPos.tpcNSigmaKa(), aod::pid_tpc_tof_utils::combineNSigma(trackPos.tpcNSigmaKa(), trackPos.tofNSigmaKa()), trackNeg.tpcNSigmaPi(), trackNeg.tpcNSigmaKa(), aod::pid_tpc_tof_utils::combineNSigma(trackNeg.tpcNSigmaPi(), trackNeg.tofNSigmaPi())}; if (!mlResponse.isSelectedMl(featuresCandD0bar, ptD0, bdtScoresD0bar)) { massHypo -= D0MassHypo::D0Bar; } else { // selected, we compute cost* @@ -537,7 +511,7 @@ struct DerivedDataCreatorD0Calibration { collision.covYZ(), collision.covZZ(), collision.numContrib(), - uint8_t(std::round(collision.centFT0C())), + static_cast(std::round(collision.centFT0C())), getCompressedOccupancy(collision.trackOccupancyInTimeRange()), getCompressedOccupancy(collision.ft0cOccupancyInTimeRange()), eventIR.orbit, @@ -694,8 +668,8 @@ struct DerivedDataCreatorD0Calibration { deltaRefGloParamQ2Pt, deltaTOFdX, deltaTOFdZ, - o2::math_utils::detail::truncateFloatFraction(dcaPos.getY(), precisionDcaMask), - o2::math_utils::detail::truncateFloatFraction(dcaPos.getZ(), precisionDcaMask), + o2::math_utils::detail::truncateFloatFraction(trackSelPos.dca.getY(), precisionDcaMask), + o2::math_utils::detail::truncateFloatFraction(trackSelPos.dca.getZ(), precisionDcaMask), getCompressedNumSigmaPid(trackPos.tpcNSigmaPi()), getCompressedNumSigmaPid(trackPos.tpcNSigmaKa()), getCompressedNumSigmaPid(trackPos.tofNSigmaPi()), @@ -861,8 +835,8 @@ struct DerivedDataCreatorD0Calibration { deltaRefGloParamQ2Pt, deltaTOFdX, deltaTOFdZ, - o2::math_utils::detail::truncateFloatFraction(dcaNeg[iTrack].getY(), precisionDcaMask), - o2::math_utils::detail::truncateFloatFraction(dcaNeg[iTrack].getZ(), precisionDcaMask), + o2::math_utils::detail::truncateFloatFraction(trackSelNeg.dca.getY(), precisionDcaMask), + o2::math_utils::detail::truncateFloatFraction(trackSelNeg.dca.getZ(), precisionDcaMask), getCompressedNumSigmaPid(trackNeg.tpcNSigmaPi()), getCompressedNumSigmaPid(trackNeg.tpcNSigmaKa()), getCompressedNumSigmaPid(trackNeg.tofNSigmaPi()), diff --git a/DPG/Tasks/AOTTrack/qaEfficiency.cxx b/DPG/Tasks/AOTTrack/qaEfficiency.cxx index 6a998053a8a..c051ae4cc9e 100644 --- a/DPG/Tasks/AOTTrack/qaEfficiency.cxx +++ b/DPG/Tasks/AOTTrack/qaEfficiency.cxx @@ -1359,7 +1359,7 @@ struct QaEfficiency { const char* partName = particleName(pdgSign, id); LOG(debug) << "Filling efficiency for particle " << static_cast(id) << " " << partName; - THashList* subList = static_cast(listEfficiencyMC->FindObject(partName)); + THashList* subList = dynamic_cast(listEfficiencyMC->FindObject(partName)); if (!subList) { LOG(warning) << "Cannot find list of efficiency objects for particle " << partName; return; @@ -1367,7 +1367,7 @@ struct QaEfficiency { // Filling 1D efficiencies auto doFillEfficiency = [&](const TString& effname, const auto& num, const auto& den) { - TEfficiency* eff = static_cast(subList->FindObject(effname)); + TEfficiency* eff = dynamic_cast(subList->FindObject(effname)); if (!eff) { LOG(warning) << "Cannot find TEfficiency " << effname; return; @@ -1437,7 +1437,7 @@ struct QaEfficiency { // Filling 2D efficiencies auto fillEfficiency2D = [&](const TString& effname, const auto& num, const auto& den) { - TEfficiency* eff = static_cast(subList->FindObject(effname)); + TEfficiency* eff = dynamic_cast(subList->FindObject(effname)); if (!eff) { LOG(warning) << "Cannot find TEfficiency " << effname; return; @@ -1802,10 +1802,10 @@ struct QaEfficiency { } float centrality = -1.; float occupancy = -1.; - if (doOccupancy) { - centrality = getCentralityColl(groupedCollisions); - occupancy = getOccupancyColl(groupedCollisions); - } + // if (doOccupancy) { + // centrality = getCentralityColl(groupedCollisions); // FIXME: unused + // occupancy = getOccupancyColl(groupedCollisions); // FIXME: unused + // } histos.fill(HIST("MC/generatedCollisions"), 2); if (skipEventsWithoutTPCTracks) { int nTPCTracks = 0; diff --git a/DPG/Tasks/AOTTrack/qaEventTrackLite.cxx b/DPG/Tasks/AOTTrack/qaEventTrackLite.cxx index dc1f2bed237..a77a17c11d1 100644 --- a/DPG/Tasks/AOTTrack/qaEventTrackLite.cxx +++ b/DPG/Tasks/AOTTrack/qaEventTrackLite.cxx @@ -336,8 +336,8 @@ struct qaEventTrackLite { Filter itsChi2 = (bTpcOnly.node() == true) || (chi2ItsMin < o2::aod::track::itsChi2NCl && o2::aod::track::itsChi2NCl < chi2ItsMax); // TPC Filter tpcChi2s = (bItsStandalone.node() == true) || (chi2TpcMin < o2::aod::track::tpcChi2NCl && o2::aod::track::tpcChi2NCl < chi2TpcMax); - Filter tpcNclusters = (bItsStandalone.node() == true) || (o2::aod::dpgtrack::tpcNClsFound > (int16_t)nClusterTpcMin); - Filter tpcNcrossedRows = (bItsStandalone.node() == true) || (o2::aod::dpgtrack::tpcNClsCrossedRows > (int16_t)nCrossedRowsTpcMin); + Filter tpcNclusters = (bItsStandalone.node() == true) || (o2::aod::dpgtrack::tpcNClsFound > static_cast(nClusterTpcMin)); + Filter tpcNcrossedRows = (bItsStandalone.node() == true) || (o2::aod::dpgtrack::tpcNClsCrossedRows > static_cast(nCrossedRowsTpcMin)); Filter tpcNcrossedRowsOverFindableClusters = (bItsStandalone.node() == true) || (o2::aod::dpgtrack::tpcCrossedRowsOverFindableCls > nCrossedRowsTpcOverFindableClustersTpcMin); // TOF Filter tofChi = o2::aod::track::tofChi2 > chi2TofMin; diff --git a/DPG/Tasks/AOTTrack/qaEventTrackLite.h b/DPG/Tasks/AOTTrack/qaEventTrackLite.h index 3af51e15d65..9ea778698b7 100644 --- a/DPG/Tasks/AOTTrack/qaEventTrackLite.h +++ b/DPG/Tasks/AOTTrack/qaEventTrackLite.h @@ -106,7 +106,7 @@ DECLARE_SOA_COLUMN(Pt, pt, float); DECLARE_SOA_COLUMN(Eta, eta, float); //! Eta DECLARE_SOA_COLUMN(Phi, phi, float); //! Phi DECLARE_SOA_COLUMN(PtReso, ptReso, float); //! Pt resolution -DECLARE_SOA_COLUMN(Sign, sign, short); //! Sign +DECLARE_SOA_COLUMN(Sign, sign, int16_t); //! Sign DECLARE_SOA_COLUMN(HasITS, hasITS, bool); //! Track has the ITS DECLARE_SOA_COLUMN(HasTPC, hasTPC, bool); //! Track has the TPC DECLARE_SOA_COLUMN(HasTRD, hasTRD, bool); //! Track has the TRD diff --git a/DPG/Tasks/AOTTrack/qaMatchEff.cxx b/DPG/Tasks/AOTTrack/qaMatchEff.cxx index d34c897ac55..09090795713 100644 --- a/DPG/Tasks/AOTTrack/qaMatchEff.cxx +++ b/DPG/Tasks/AOTTrack/qaMatchEff.cxx @@ -1565,7 +1565,7 @@ struct qaMatchEff { // // here n of clusters of TPC assigned to float for histos (and to hack it if needed) :) // - Float_t clustpc = (Float_t)track.tpcNClsFound(); + Float_t clustpc = static_cast(track.tpcNClsFound()); // Float_t findcltpc = (Float_t)track.tpcNClsFindable(); // Float_t crowstpc = (Float_t)track.tpcNClsCrossedRows(); // Float_t finclusmincrotpc = (Float_t)track.tpcNClsFindableMinusCrossedRows(); diff --git a/DPG/Tasks/AOTTrack/qaTrackSplitting.cxx b/DPG/Tasks/AOTTrack/qaTrackSplitting.cxx index 5daa92f6b0a..97ba21592f9 100644 --- a/DPG/Tasks/AOTTrack/qaTrackSplitting.cxx +++ b/DPG/Tasks/AOTTrack/qaTrackSplitting.cxx @@ -99,8 +99,8 @@ struct qaTrackSplitting { using TrackCandidates = o2::soa::Join; Filter trackFilterEta = nabs(aod::track::eta - cfgCustomTrackCuts.windowEta) < cfgCustomTrackCuts.windowEtaWidth; Filter trackFilterPhi = nabs(aod::track::phi - cfgCustomTrackCuts.windowPhi) < cfgCustomTrackCuts.windowPhiWidth; - Filter trackFilterITS = (aod::track::itsClusterSizes > (uint32_t)0); - Filter trackFilterTPC = (aod::track::tpcNClsFindable > (uint8_t)0); + Filter trackFilterITS = (aod::track::itsClusterSizes > static_cast(0)); + Filter trackFilterTPC = (aod::track::tpcNClsFindable > static_cast(0)); // Filter trackFilterType = (aod::track::TrackType == aod::track::Track); // Filter filterPt = nabs(aod::track::pt - cfgCustomTrackCuts.windowPt) < cfgCustomTrackCuts.windowPtWidth; void processData(CollisionCandidates const& collisions, diff --git a/DPG/Tasks/Monitor/MC/taskMcSimpleQC.cxx b/DPG/Tasks/Monitor/MC/taskMcSimpleQC.cxx index fc38e305938..f17df748019 100644 --- a/DPG/Tasks/Monitor/MC/taskMcSimpleQC.cxx +++ b/DPG/Tasks/Monitor/MC/taskMcSimpleQC.cxx @@ -56,7 +56,7 @@ struct mcSimpleQc { McParticles const&) { // checks on collisions - registry.fill(HIST("histRecoOverGenCollisions"), float(collisions.size()) / mcCollisions.size()); + registry.fill(HIST("histRecoOverGenCollisions"), static_cast(collisions.size()) / mcCollisions.size()); for (const auto& collision : collisions) { if (collision.has_mcCollision()) { @@ -79,7 +79,7 @@ struct mcSimpleQc { } } registry.fill(HIST("histNumGlobalTracks"), nTracks); - registry.fill(HIST("histFracAmbiguousTracks"), float(nAmbTracks) / nTracks); + registry.fill(HIST("histFracAmbiguousTracks"), static_cast(nAmbTracks) / nTracks); } } }; diff --git a/DPG/Tasks/TOF/tofSkimsTableCreator.h b/DPG/Tasks/TOF/tofSkimsTableCreator.h index f3a6f63bb9b..80db220c030 100644 --- a/DPG/Tasks/TOF/tofSkimsTableCreator.h +++ b/DPG/Tasks/TOF/tofSkimsTableCreator.h @@ -46,7 +46,7 @@ DECLARE_SOA_COLUMN(DeltaTPi, deltaTPi, float); //! Difference betw DECLARE_SOA_COLUMN(DeltaTKa, deltaTKa, float); //! Difference between the tofSignal and the kaon expected signal DECLARE_SOA_COLUMN(DeltaTPr, deltaTPr, float); //! Difference between the tofSignal and the proton expected signal DECLARE_SOA_COLUMN(DoubleDelta, doubleDelta, float); //! Double difference between DeltaT -DECLARE_SOA_COLUMN(RefSign, refSign, short); //! Sign of the reference track +DECLARE_SOA_COLUMN(RefSign, refSign, int16_t); //! Sign of the reference track DECLARE_SOA_COLUMN(PIDForTracking, pidForTracking, uint8_t); //! Index for mass hypothesis used in tracking see PID.h for definition DECLARE_SOA_COLUMN(EvTimeT0AC, evTimeT0AC, float); //! Event time of the track computed with the T0AC DECLARE_SOA_COLUMN(EvTimeT0ACErr, evTimeT0ACErr, float); //! Resolution of the event time of the track computed with the T0AC diff --git a/DPG/Tasks/TPC/tpcSkimsTableCreator.cxx b/DPG/Tasks/TPC/tpcSkimsTableCreator.cxx index 3aaa024874e..ec4c98f2d6d 100644 --- a/DPG/Tasks/TPC/tpcSkimsTableCreator.cxx +++ b/DPG/Tasks/TPC/tpcSkimsTableCreator.cxx @@ -166,7 +166,7 @@ struct TreeWriterTpcV0 { o2::aod::rctsel::RCTFlagsChecker rctChecker{}; - TRandom3* fRndm = new TRandom3(0); + TRandom3* fRndm = new TRandom3(); using Trks = soa::Join; using TrksWithDEdxCorrection = soa::Join; @@ -420,6 +420,12 @@ struct TreeWriterTpcV0 { template void runV0(Colls const& collisions, TrksType const& myTracks, V0sWithID const& myV0s, CascsWithID const& myCascs, TrkQAType const& tracksQA) { + if (collisions.size() != 0) { + const auto timestamp = collisions.begin().bc_as().timestamp(); + LOG(info) << "fRndm is seeded with timestamp of the 0-th bunch crossing (" << timestamp << ")"; + fRndm->SetSeed(timestamp); + } + constexpr bool IsWithTrackQa = ModeId != ModeStandard; std::vector labelTrack2TrackQA; @@ -692,20 +698,20 @@ struct TreeWriterTpcTof { HistogramRegistry registry{"registry", {}}; struct TofTrack { - bool isApplyHardCutOnly; - double maxMomHardCutOnly; - double maxMomTPCOnly; - double tpcNSigma; - double nSigmaTPCOnly; - double downsamplingTsalis; - double mass; - double tofNSigma; - double itsNSigma; - double tpcExpSignal; - o2::track::PID::ID pid; - double dwnSmplFactor; - double nSigmaTofTpctof; - double nSigmaTpcTpctof; + bool isApplyHardCutOnly{false}; + double maxMomHardCutOnly{UndefValueDouble}; + double maxMomTPCOnly{UndefValueDouble}; + double tpcNSigma{UndefValueDouble}; + double nSigmaTPCOnly{UndefValueDouble}; + double downsamplingTsalis{UndefValueDouble}; + double mass{UndefValueDouble}; + double tofNSigma{UndefValueDouble}; + double itsNSigma{UndefValueDouble}; + double tpcExpSignal{UndefValueDouble}; + o2::track::PID::ID pid{0}; + double dwnSmplFactor{UndefValueDouble}; + double nSigmaTofTpctof{UndefValueDouble}; + double nSigmaTpcTpctof{UndefValueDouble}; }; Service ccdb{}; @@ -714,7 +720,7 @@ struct TreeWriterTpcTof { o2::aod::rctsel::RCTFlagsChecker rctChecker{}; - TRandom3* fRndm = new TRandom3(0); + TRandom3* fRndm = new TRandom3(); using Trks = soa::Join void runTof(Colls const& collisions, TrksType const& myTracks, TrkQAType const& tracksQA, Preslice const& perCollisionTracksType, TMOType const& trackMeanOccs) { + if (collisions.size() != 0) { + const auto timestamp = collisions.begin().bc_as().timestamp(); + LOG(info) << "fRndm is seeded with timestamp of the 0-th bunch crossing (" << timestamp << ")"; + fRndm->SetSeed(timestamp); + } + constexpr bool IsWithTrackQa = ModeId != ModeStandard; std::vector labelTrack2TrackQA; diff --git a/DPG/Tasks/TPC/tpcTreeCreatorLight.cxx b/DPG/Tasks/TPC/tpcTreeCreatorLight.cxx index 7d9ae11ea1b..1d44db1e78d 100644 --- a/DPG/Tasks/TPC/tpcTreeCreatorLight.cxx +++ b/DPG/Tasks/TPC/tpcTreeCreatorLight.cxx @@ -87,7 +87,7 @@ struct TreeWriterTPCTOF { const double p = track.tpcInnerParam(); const int multTPC = collision.multTPC(); - const double pseudoRndm = track.pt() * 1000. - (int64_t)(track.pt() * 1000); + const double pseudoRndm = track.pt() * 1000. - static_cast(track.pt() * 1000); if (pseudoRndm < dwnSmplFactor) { rowTPCTOFTree(track.tpcSignal(), p, diff --git a/DPG/Tasks/TPC/treeCreatorPidTpcDiagnostics.cxx b/DPG/Tasks/TPC/treeCreatorPidTpcDiagnostics.cxx index 9f30e558d2c..8b20cb6889a 100644 --- a/DPG/Tasks/TPC/treeCreatorPidTpcDiagnostics.cxx +++ b/DPG/Tasks/TPC/treeCreatorPidTpcDiagnostics.cxx @@ -41,8 +41,12 @@ #include #include +#include #include +#include +#include + #include #include #include @@ -53,16 +57,16 @@ using namespace o2::framework; using namespace o2::track; using namespace o2::dpg_tpcskimstablecreator; -#define DO_FOR_ALL_PARTICLES(MACRO) \ - MACRO(El, Electron) \ - MACRO(Mu, Muon) \ - MACRO(Pi, Pion) \ - MACRO(Ka, Kaon) \ - MACRO(Pr, Proton) \ - MACRO(De, Deuteron) \ - MACRO(Tr, Triton) \ - MACRO(He, Helium3) \ - MACRO(Al, Alpha) +#define DO_FOR_ALL_PARTICLES(MACRO, ARG) \ + MACRO(El, Electron, ARG) \ + MACRO(Mu, Muon, ARG) \ + MACRO(Pi, Pion, ARG) \ + MACRO(Ka, Kaon, ARG) \ + MACRO(Pr, Proton, ARG) \ + MACRO(De, Deuteron, ARG) \ + MACRO(Tr, Triton, ARG) \ + MACRO(He, Helium3, ARG) \ + MACRO(Al, Alpha, ARG) struct TreeCreatorPidTpcDiagnostics { Produces rowPidTpcDiagnostics; @@ -86,28 +90,39 @@ struct TreeCreatorPidTpcDiagnostics { Configurable treatLimitedAcceptanceAsBad{"treatLimitedAcceptanceAsBad", false, "reject all events where the detectors relevant for the specified Runlist are flagged as LimitedAcceptance"}; Configurable requireGoodRct{"requireGoodRct", false, "require good detector flag in run condtion table"}; -#define DECLARE_PARTICLE_WISE_CONFIGURABLES(ParticleNameShort, ParticleNameLong) \ - Configurable cutTpcInnerParameterMin##ParticleNameLong{"cutTpcInnerParameterMin" #ParticleNameLong, 0.f, "Lower-value cut on tpcInnerParam for " #ParticleNameLong}; /* o2-linter: disable=name/configurable (Configurable defined in macro)*/ \ - Configurable cutTpcInnerParameterMax##ParticleNameLong{"cutTpcInnerParameterMax" #ParticleNameLong, 999.f, "Upper-value cut on tpcInnerParam for " #ParticleNameLong}; /* o2-linter: disable=name/configurable (Configurable defined in macro)*/ \ - Configurable cutNSigmaTpcAbs##ParticleNameLong{"cutNSigmaTpcAbs" #ParticleNameLong, 999.f, "Cut on absolute value of nSigmaTpc for " #ParticleNameLong}; // o2-linter: disable=name/configurable (Configurable defined in macro) - - DO_FOR_ALL_PARTICLES(DECLARE_PARTICLE_WISE_CONFIGURABLES) + struct ParticleWiseCfgs : ConfigurableGroup { + std::string prefix = "particleWiseCfgs"; // JSON group name + +#define DECLARE_PARTICLE_WISE_CONFIGURABLES(Unused1, ParticleNameLong, Unused2) \ + Configurable cutTpcInnerParameterMin##ParticleNameLong{"cutTpcInnerParameterMin" #ParticleNameLong, 0.f, "Lower-value cut on tpcInnerParam for " #ParticleNameLong}; /* o2-linter: disable=name/configurable (Configurable defined in macro)*/ \ + Configurable cutTpcInnerParameterMax##ParticleNameLong{"cutTpcInnerParameterMax" #ParticleNameLong, 999.f, "Upper-value cut on tpcInnerParam for " #ParticleNameLong}; /* o2-linter: disable=name/configurable (Configurable defined in macro)*/ \ + Configurable cutExpSigmaMax##ParticleNameLong{"cutExpSigmaMax" #ParticleNameLong, 1e9f, "Upper-value cut on expected sigma for " #ParticleNameLong}; /* o2-linter: disable=name/configurable (Configurable defined in macro)*/ \ + Configurable cutDeDxExpectedMin##ParticleNameLong{"cutDeDxExpectedMin" #ParticleNameLong, -1e9f, "Lower-value cut on expected dE/dx for " #ParticleNameLong}; /* o2-linter: disable=name/configurable (Configurable defined in macro)*/ \ + Configurable cutDeDxExpectedMax##ParticleNameLong{"cutDeDxExpectedMax" #ParticleNameLong, 1e9f, "Upper-value cut on expected dE/dx for " #ParticleNameLong}; /* o2-linter: disable=name/configurable (Configurable defined in macro)*/ \ + Configurable cutDeDxDiffMin##ParticleNameLong{"cutDeDxDiffMin" #ParticleNameLong, -1e9f, "Lower-value cut on real - expected dE/dx difference for " #ParticleNameLong}; /* o2-linter: disable=name/configurable (Configurable defined in macro)*/ \ + Configurable cutDeDxDiffMax##ParticleNameLong{"cutDeDxDiffMax" #ParticleNameLong, 1e9f, "Upper-value cut on real - expected dE/dx difference for " #ParticleNameLong}; /* o2-linter: disable=name/configurable (Configurable defined in macro)*/ \ + Configurable cutNSigmaTpcAbs##ParticleNameLong{"cutNSigmaTpcAbs" #ParticleNameLong, 999.f, "Cut on absolute value of nSigmaTpc for " #ParticleNameLong}; /* o2-linter: disable=name/configurable (Configurable defined in macro)*/ \ + Configurable downsampleFactor##ParticleNameLong{"downsampleFactor" #ParticleNameLong, 1.f, "Downsampling factor for " #ParticleNameLong}; // o2-linter: disable=name/configurable (Configurable defined in macro) + + DO_FOR_ALL_PARTICLES(DECLARE_PARTICLE_WISE_CONFIGURABLES, _) #undef DECLARE_PARTICLE_WISE_CONFIGURABLES + } particleWiseCfgs; -#define PACK_CONFIGURABLES_TO_ARRAY(ParticleNameShort, ParticleNameLong) &cutTpcInnerParameterMin##ParticleNameLong, - std::array*, PID::Alpha + 1> cutTpcInnerParameterMin{ - DO_FOR_ALL_PARTICLES(PACK_CONFIGURABLES_TO_ARRAY)}; -#undef PACK_CONFIGURABLES_TO_ARRAY +#define PARTICLE_WISE_CFG_NAMES \ + (cutTpcInnerParameterMin)(cutTpcInnerParameterMax)(cutExpSigmaMax)(cutDeDxExpectedMin)(cutDeDxExpectedMax)(cutDeDxDiffMin)(cutDeDxDiffMax)(cutNSigmaTpcAbs)(downsampleFactor) -#define PACK_CONFIGURABLES_TO_ARRAY(ParticleNameShort, ParticleNameLong) &cutTpcInnerParameterMax##ParticleNameLong, - std::array*, PID::Alpha + 1> cutTpcInnerParameterMax{ - DO_FOR_ALL_PARTICLES(PACK_CONFIGURABLES_TO_ARRAY)}; -#undef PACK_CONFIGURABLES_TO_ARRAY +#define MAKE_PARTICLE_CUT_POINTER(Unused, ParticleNameLong, CfgName) \ + &particleWiseCfgs.BOOST_PP_CAT(CfgName, ParticleNameLong), + +#define PACK_CONFIGURABLES_TO_ARRAY(Unused1, Unused2, CfgName) \ + std::array*, PID::Alpha + 1> CfgName{ \ + DO_FOR_ALL_PARTICLES(MAKE_PARTICLE_CUT_POINTER, CfgName)}; + + BOOST_PP_SEQ_FOR_EACH(PACK_CONFIGURABLES_TO_ARRAY, _, PARTICLE_WISE_CFG_NAMES) -#define PACK_CONFIGURABLES_TO_ARRAY(ParticleNameShort, ParticleNameLong) &cutNSigmaTpcAbs##ParticleNameLong, - std::array*, PID::Alpha + 1> cutNSigmaTpcAbs{ - DO_FOR_ALL_PARTICLES(PACK_CONFIGURABLES_TO_ARRAY)}; #undef PACK_CONFIGURABLES_TO_ARRAY +#undef MAKE_PARTICLE_CUT_POINTER +#undef PARTICLE_WISE_CFG_NAMES HistogramRegistry registry{"registry", {}}; @@ -117,6 +132,8 @@ struct TreeCreatorPidTpcDiagnostics { o2::aod::rctsel::RCTFlagsChecker rctChecker; + TRandom3 mRnd; + using CollisionsExtra = soa::Join; using TrackCandidates = soa::Join; @@ -132,7 +149,7 @@ struct TreeCreatorPidTpcDiagnostics { int enabledProcesses{0}; switch (ParticleId) { -#define INIT_PARTICLE(ParticleNameShort, ParticleNameLong) \ +#define INIT_PARTICLE(Unused1, ParticleNameLong, Unused2) \ case PID::ParticleNameLong: \ if (!doprocess##ParticleNameLong && !doprocessFull##ParticleNameLong && !doprocessFullWithTOF##ParticleNameLong) { \ return false; \ @@ -149,7 +166,7 @@ struct TreeCreatorPidTpcDiagnostics { LOG(info) << "Enabled TPC QA for " << #ParticleNameLong; \ break; - DO_FOR_ALL_PARTICLES(INIT_PARTICLE) + DO_FOR_ALL_PARTICLES(INIT_PARTICLE, _) #undef INIT_PARTICLE } if (enabledProcesses != 1) { @@ -207,11 +224,16 @@ struct TreeCreatorPidTpcDiagnostics { isFirstCollision = false; const float ft0Occ = collision.ft0cOccupancyInTimeRange(); const float multTPC = collision.multTPC() / MultiplicityNorm; - const auto hadronicRate = !irSource.empty() ? mRateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * OneToKilo : 0.; + const int runnumber = bc.runNumber(); + const auto hadronicRate = !irSource.empty() ? mRateFetcher.fetch(ccdb.service, bc.timestamp(), runnumber, irSource) * OneToKilo : 0.; const auto tracksFromCollision = tracks.sliceBy(perCollisionTracks, static_cast(collision.globalIndex())); for (const auto& track : tracksFromCollision) { + if (*downsampleFactor.at(ParticleId) < 1.f && mRnd.Uniform(1) > *downsampleFactor.at(ParticleId)) { + continue; + } + bool isGoodTrack = isTrackSelected(track, trackSelection); isGoodTrack &= (!requireGlobalTrack || track.isGlobalTrack()); isGoodTrack &= (!requireIts || track.hasITS()); @@ -233,10 +255,6 @@ struct TreeCreatorPidTpcDiagnostics { isGoodTrack &= (tpcInnerParam <= *cutTpcInnerParameterMax.at(ParticleId)); isGoodTrack &= (std::fabs(nSigmaTpc) <= *cutNSigmaTpcAbs.at(ParticleId)); - if (!isGoodTrack) { - continue; - } - float dedxDiff{UndefValueFloat}; float dedxExpected{UndefValueFloat}; float expSigma{UndefValueFloat}; @@ -245,6 +263,16 @@ struct TreeCreatorPidTpcDiagnostics { dedxDiff = o2::aod::pidutils::tpcExpSignalDiff(track); dedxExpected = track.tpcSignal() - dedxDiff; expSigma = o2::aod::pidutils::tpcExpSigma(track); + + isGoodTrack &= (expSigma <= *cutExpSigmaMax.at(ParticleId)); + isGoodTrack &= (dedxExpected >= *cutDeDxExpectedMin.at(ParticleId)); + isGoodTrack &= (dedxExpected <= *cutDeDxExpectedMax.at(ParticleId)); + isGoodTrack &= (dedxDiff >= *cutDeDxDiffMin.at(ParticleId)); + isGoodTrack &= (dedxDiff <= *cutDeDxDiffMax.at(ParticleId)); + } + + if (!isGoodTrack) { + continue; } float nSigmaTof{UndefValueFloat}; @@ -253,7 +281,7 @@ struct TreeCreatorPidTpcDiagnostics { nSigmaTof = o2::aod::pidutils::tofNSigma(track); } - rowPidTpcDiagnostics(isGoodRctEvent, ParticleId, ft0Occ, hadronicRate, multTPC, nClNormalized, nclPID, phi, tgl, tpcInnerParam, rapidity, momentum, signed1Pt, nSigmaTpc, dedxExpected, dedxDiff, expSigma, nSigmaTof); + rowPidTpcDiagnostics(isGoodRctEvent, ParticleId, ft0Occ, hadronicRate, multTPC, nClNormalized, nclPID, runnumber, phi, tgl, tpcInnerParam, rapidity, momentum, signed1Pt, nSigmaTpc, dedxExpected, dedxDiff, expSigma, nSigmaTof); } // tracksFromCollision } // collisions ++mProcessedParticles; @@ -265,7 +293,17 @@ struct TreeCreatorPidTpcDiagnostics { } } -#define MAKE_PROCESS_FUNCTIONS(ParticleNameShort, ParticleNameLong) \ + void process(const aod::Collisions& collisions, + const aod::BCsWithTimestamps&) + { + if (collisions.size() != 0) { + const auto timestamp = collisions.begin().bc_as().timestamp(); + LOG(info) << "mRnd is seeded with timestamp of the 0-th bunch crossing (" << timestamp << ")"; + mRnd.SetSeed(timestamp); + } + } + +#define MAKE_PROCESS_FUNCTIONS(ParticleNameShort, ParticleNameLong, Unused) \ void process##ParticleNameLong(CollisionsExtra const& collisions, \ soa::Join const& tracks, \ aod::BCsWithTimestamps const&) \ @@ -290,12 +328,12 @@ struct TreeCreatorPidTpcDiagnostics { } \ PROCESS_SWITCH(TreeCreatorPidTpcDiagnostics, processFullWithTOF##ParticleNameLong, Form("Process for the %s hypothesis for full TPC PID QA with the TOF info added", #ParticleNameLong), false); - DO_FOR_ALL_PARTICLES(MAKE_PROCESS_FUNCTIONS) + DO_FOR_ALL_PARTICLES(MAKE_PROCESS_FUNCTIONS, _) #undef MAKE_PROCESS_FUNCTIONS }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(context)}; } #undef DO_FOR_ALL_PARTICLES diff --git a/DPG/Tasks/TPC/treeCreatorPidTpcDiagnostics.h b/DPG/Tasks/TPC/treeCreatorPidTpcDiagnostics.h index e75aa5f7e71..235a802aa54 100644 --- a/DPG/Tasks/TPC/treeCreatorPidTpcDiagnostics.h +++ b/DPG/Tasks/TPC/treeCreatorPidTpcDiagnostics.h @@ -41,6 +41,7 @@ DECLARE_SOA_TABLE(PidTpcDiagnostics, "AOD", "PIDTPCDIAG", tpcskims::NormMultTPC, tpcskims::NormNClustersTPC, tpcskims::NormNClustersTPCPID, + tpcskims::RunNumber, o2::aod::track::Phi, o2::aod::track::Tgl, o2::aod::track::TPCInnerParam, diff --git a/EventFiltering/PWGHF/HFFilterHelpers.h b/EventFiltering/PWGHF/HFFilterHelpers.h index 13bcfa93fa1..288cdf8fac5 100644 --- a/EventFiltering/PWGHF/HFFilterHelpers.h +++ b/EventFiltering/PWGHF/HFFilterHelpers.h @@ -2004,7 +2004,7 @@ inline void HfFilterHelper::setTpcRecalibMaps(o2::framework::Service(calibList->FindObject(mapNames[iMap].data())); + mHistMapPiPrKaDe[iMap] = dynamic_cast(calibList->FindObject(mapNames[iMap].data())); if (!mHistMapPiPrKaDe[iMap]) { LOG(fatal) << "Cannot find histogram: " << mapNames[iMap].data(); return; diff --git a/EventFiltering/PWGJE/jetFilter.cxx b/EventFiltering/PWGJE/jetFilter.cxx index d9f9f153333..a74c146e86b 100644 --- a/EventFiltering/PWGJE/jetFilter.cxx +++ b/EventFiltering/PWGJE/jetFilter.cxx @@ -98,7 +98,7 @@ struct jetFilter { void init(o2::framework::InitContext&) { triggerJetR = TMath::Nint(cfgJetR * 100.0f); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(evSel)); spectra.add("fCollZpos", "collision z position", HistType::kTH1F, diff --git a/EventFiltering/PWGUD/diffractionBCFilter.cxx b/EventFiltering/PWGUD/diffractionBCFilter.cxx index b72c026e0e4..74d4672f981 100644 --- a/EventFiltering/PWGUD/diffractionBCFilter.cxx +++ b/EventFiltering/PWGUD/diffractionBCFilter.cxx @@ -112,11 +112,11 @@ struct tracksWGTInBCs { LOGF(debug, " Number of BCs %d", atr.bc().size()); if (atr.bc().size() > 0) { auto firstCompatibleBC = atr.bc().begin().globalBC(); - closestBC = (uint64_t)(firstCompatibleBC + - (track.trackTime() / o2::constants::lhc::LHCBunchSpacingNS)); + closestBC = static_cast(firstCompatibleBC + + (track.trackTime() / o2::constants::lhc::LHCBunchSpacingNS)); // update tracksInBCList LOGF(debug, "closestBC %d", closestBC); - tracksInBCList[closestBC].emplace_back((int32_t)track.globalIndex()); + tracksInBCList[closestBC].emplace_back(static_cast(track.globalIndex())); } } else { // registry.get(HIST("barrelTracks"))->Fill(2., 1.); @@ -128,7 +128,7 @@ struct tracksWGTInBCs { // update tracksInBCList LOGF(debug, "closestBC %d", closestBC); - tracksInBCList[closestBC].emplace_back((int32_t)track.globalIndex()); + tracksInBCList[closestBC].emplace_back(static_cast(track.globalIndex())); } } LOGF(debug, "track finished.\n"); @@ -195,12 +195,12 @@ struct tracksWGTInBCs { LOGF(debug, " Number of BCs %d", aftr.bc().size()); if (aftr.bc().size() > 0) { auto firstCompatibleBC = aftr.bc().begin().globalBC(); - closestBC = (uint64_t)(firstCompatibleBC + - (fwdTrack.trackTime() / o2::constants::lhc::LHCBunchSpacingNS)); + closestBC = static_cast(firstCompatibleBC + + (fwdTrack.trackTime() / o2::constants::lhc::LHCBunchSpacingNS)); // update fwdTracksWGTInBCList LOGF(debug, "closestBC %d", closestBC); - fwdTracksWGTInBCList[closestBC].emplace_back((int32_t)fwdTrack.globalIndex()); + fwdTracksWGTInBCList[closestBC].emplace_back(static_cast(fwdTrack.globalIndex())); } } else { // registry.get(HIST("forwardTracks"))->Fill(2., 1.); @@ -212,7 +212,7 @@ struct tracksWGTInBCs { // update fwdTracksWGTInBCList LOGF(debug, "closestBC %d", closestBC); - fwdTracksWGTInBCList[closestBC].emplace_back((int32_t)fwdTrack.globalIndex()); + fwdTracksWGTInBCList[closestBC].emplace_back(static_cast(fwdTrack.globalIndex())); } } LOGF(debug, "FwdTrack finished.\n"); @@ -282,7 +282,7 @@ struct DGBCFilterRun3 { void init(InitContext&) { - diffCuts = (DGCutparHolder)DGCuts; + diffCuts = DGCutparHolder(DGCuts); } // some general Collisions and Tracks filter diff --git a/EventFiltering/PWGUD/diffractionFilter.cxx b/EventFiltering/PWGUD/diffractionFilter.cxx index b9fed2f424d..83bc848612e 100644 --- a/EventFiltering/PWGUD/diffractionFilter.cxx +++ b/EventFiltering/PWGUD/diffractionFilter.cxx @@ -89,13 +89,13 @@ struct DGFilterRun3 { { // reset collision counter collCounter = 0; - verbose = (int64_t)Verbose; + verbose = static_cast(Verbose); if (verbose <= 0) { verbose = 1000000000000; } // cut holder - diffCuts = (DGCutparHolder)diffCutsHolder; + diffCuts = DGCutparHolder(diffCutsHolder); // create histograms // stat diff --git a/EventFiltering/macros/cefpOutputChecker.C b/EventFiltering/macros/cefpOutputChecker.C index 48295a1746e..f6c4ffd78e5 100644 --- a/EventFiltering/macros/cefpOutputChecker.C +++ b/EventFiltering/macros/cefpOutputChecker.C @@ -21,18 +21,18 @@ void cefpOutputChecker(std::string histoFile = "AnalysisResults.root", std::string treeFile = "AO2D.root") { TFile referenceFile(histoFile.data()); - TH1* refScalers = (TH1*)referenceFile.Get("central-event-filter-task/scalers/mScalers"); - TH1* refFilters = (TH1*)referenceFile.Get("central-event-filter-task/scalers/mFiltered"); + TH1* refScalers = dynamic_cast(referenceFile.Get("central-event-filter-task/scalers/mScalers")); + TH1* refFilters = dynamic_cast(referenceFile.Get("central-event-filter-task/scalers/mFiltered")); TFile outputFile("cefpOutputChecker.root", "recreate"); - TH1* newFilters = (TH1*)refFilters->Clone("newFilters"); - TH1* newScalers = (TH1*)refScalers->Clone("newScalers"); + TH1* newFilters = dynamic_cast(refFilters->Clone("newFilters")); + TH1* newScalers = dynamic_cast(refScalers->Clone("newScalers")); newFilters->Reset(); newScalers->Reset(); TFile inputFile(treeFile.data()); for (auto key : *(inputFile.GetListOfKeys())) { - TTree* cefpTree = (TTree*)inputFile.Get(Form("%s/O2cefpdecision", key->GetName())); + TTree* cefpTree = dynamic_cast(inputFile.Get(Form("%s/O2cefpdecision", key->GetName()))); if (!cefpTree) continue; ULong64_t fCefpSelected, fCefpTriggered; diff --git a/EventFiltering/macros/checkBCrangesSkimming.C b/EventFiltering/macros/checkBCrangesSkimming.C index d7379030956..4224297f570 100644 --- a/EventFiltering/macros/checkBCrangesSkimming.C +++ b/EventFiltering/macros/checkBCrangesSkimming.C @@ -620,28 +620,28 @@ void checkBCrangesSkimming(std::string AnaFileName = "AnalysisResults.root", std TH1D *hOriginalSinglesRatio, *hOriginalDoublesRatio, *hOriginalMultiplesRatio; TH1D *hSkimmedSinglesRatio, *hSkimmedDoublesRatio, *hSkimmedMultiplesRatio; - hTriggerEff = reinterpret_cast(hSkimmedTotal.Clone("hTriggerEff")); + hTriggerEff = dynamic_cast(hSkimmedTotal.Clone("hTriggerEff")); hTriggerEff->SetTitle((runNumber + " skimmed efficiency;; Skimmed / Original").data()); hTriggerEff->Divide(&hOriginalTotal); hTriggerMatchesRatio.Divide(&hOriginalSingles); hTriggerSingleMatchesRatio.Divide(&hOriginalSingles); - hOriginalSinglesRatio = reinterpret_cast(hOriginalSingles.Clone("hOriginalSinglesRatio")); + hOriginalSinglesRatio = dynamic_cast(hOriginalSingles.Clone("hOriginalSinglesRatio")); hOriginalSinglesRatio->SetTitle((runNumber + " Original;;Singles / Total").data()); hOriginalSinglesRatio->Divide(&hOriginalTotal); - hOriginalDoublesRatio = reinterpret_cast(hOriginalDoubles.Clone("hOriginalDoublesRatio")); + hOriginalDoublesRatio = dynamic_cast(hOriginalDoubles.Clone("hOriginalDoublesRatio")); hOriginalDoublesRatio->SetTitle((runNumber + " Original;;Doubles / Total").data()); hOriginalDoublesRatio->Divide(&hOriginalTotal); - hOriginalMultiplesRatio = reinterpret_cast(hOriginalMultiples.Clone("hOriginalMultiplesRatio")); + hOriginalMultiplesRatio = dynamic_cast(hOriginalMultiples.Clone("hOriginalMultiplesRatio")); hOriginalMultiplesRatio->SetTitle((runNumber + " Original;;Multiples / Total").data()); hOriginalMultiplesRatio->Divide(&hOriginalTotal); - hSkimmedSinglesRatio = reinterpret_cast(hSkimmedSingles.Clone("hSkimmedSinglesRatio")); + hSkimmedSinglesRatio = dynamic_cast(hSkimmedSingles.Clone("hSkimmedSinglesRatio")); hSkimmedSinglesRatio->SetTitle((runNumber + " Skimmed;;Singles / Total").data()); hSkimmedSinglesRatio->Divide(&hSkimmedTotal); - hSkimmedDoublesRatio = reinterpret_cast(hSkimmedDoubles.Clone("hSkimmedDoublesRatio")); + hSkimmedDoublesRatio = dynamic_cast(hSkimmedDoubles.Clone("hSkimmedDoublesRatio")); hSkimmedDoublesRatio->SetTitle((runNumber + " Skimmed;;Doubles / Total").data()); hSkimmedDoublesRatio->Divide(&hSkimmedTotal); - hSkimmedMultiplesRatio = reinterpret_cast(hSkimmedMultiples.Clone("hSkimmedMultiplesRatio")); + hSkimmedMultiplesRatio = dynamic_cast(hSkimmedMultiples.Clone("hSkimmedMultiplesRatio")); hSkimmedMultiplesRatio->SetTitle((runNumber + " Skimmed;;Multiples / Total").data()); hSkimmedMultiplesRatio->Divide(&hSkimmedTotal); diff --git a/EventFiltering/macros/checkSkimming.C b/EventFiltering/macros/checkSkimming.C index 34d58a803fe..6f1961fb7bd 100644 --- a/EventFiltering/macros/checkSkimming.C +++ b/EventFiltering/macros/checkSkimming.C @@ -124,7 +124,7 @@ void checkSkimming(const std::string& listName = "period.txt", bool skipDownscal std::string original_path = line.substr(0, pos); std::string skimmed_path = line.substr(pos + 1); checkSkimming(original_path, skimmed_path, outputFile, skipDownscaled); - TH1* hRatio = (TH1*)gDirectory->Get("hRatio"); + TH1* hRatio = dynamic_cast(gDirectory->Get("hRatio")); hRatio->Draw(); std::string suffix = counter == 0 ? "(" : (counter == lineCounter - 1 ? ")" : ""); c1.Print((periodName + ".pdf" + suffix).data()); diff --git a/EventFiltering/macros/getMenu.C b/EventFiltering/macros/getMenu.C index 6e7e480f587..2166cf4635d 100644 --- a/EventFiltering/macros/getMenu.C +++ b/EventFiltering/macros/getMenu.C @@ -69,7 +69,7 @@ std::vector getMenuForPeriod(std::string period) TGrid::Connect("alien://"); TFile* scalersFile = TFile::Open((std::string("alien://") + firstLine).data(), "READ"); - TH1D* counters = (TH1D*)scalersFile->Get("central-event-filter-task/scalers/mFiltered"); + TH1D* counters = dynamic_cast(scalersFile->Get("central-event-filter-task/scalers/mFiltered")); TAxis* axis = counters->GetXaxis(); std::vector binLabels(axis->GetNbins() - 2); diff --git a/EventFiltering/macros/selectivityPlot.C b/EventFiltering/macros/selectivityPlot.C index c7f710fe42d..7847c320744 100644 --- a/EventFiltering/macros/selectivityPlot.C +++ b/EventFiltering/macros/selectivityPlot.C @@ -45,7 +45,7 @@ void selectivityPlot(int runNumber = 550781, const TString& inputfile = "Analysi Canvas_1->SetFrameBorderMode(0); TFile* file = new TFile(inputfile.Data()); - TH1D* mFiltered = (TH1D*)file->Get("central-event-filter-task/scalers/mFiltered"); + TH1D* mFiltered = dynamic_cast(file->Get("central-event-filter-task/scalers/mFiltered")); double nEvents = mFiltered->GetBinContent(1); mFiltered->Scale(1. / nEvents); mFiltered->SetLineColor(2); diff --git a/EventFiltering/macros/uploadOTSobjects.C b/EventFiltering/macros/uploadOTSobjects.C index e9982fafaaa..378526c7a3f 100644 --- a/EventFiltering/macros/uploadOTSobjects.C +++ b/EventFiltering/macros/uploadOTSobjects.C @@ -67,15 +67,15 @@ void uploadOTSobjects(std::string inputList, const std::string& passName, bool u auto orbitResetTimestamp = (*ctp)[0]; path = useAlien ? "alien://" + path : path; std::unique_ptr scalersFile{TFile::Open((path + "/AnalysisResults_fullrun.root").data(), "READ")}; - TH1* scalers = static_cast(scalersFile->Get("central-event-filter-task/scalers/mScalers")); - TH1* filters = static_cast(scalersFile->Get("central-event-filter-task/scalers/mFiltered")); + TH1* scalers = dynamic_cast(scalersFile->Get("central-event-filter-task/scalers/mScalers")); + TH1* filters = dynamic_cast(scalersFile->Get("central-event-filter-task/scalers/mFiltered")); api.storeAsTFile(scalers, baseCCDBpath + "FilterCounters", metadata, duration.first, duration.second + 1); api.storeAsTFile(filters, baseCCDBpath + "SelectionCounters", metadata, duration.first, duration.second + 1); - TH1* hCounterTVX = static_cast(scalersFile->Get("bc-selection-task/hCounterTVX")); + TH1* hCounterTVX = dynamic_cast(scalersFile->Get("bc-selection-task/hCounterTVX")); if (!hCounterTVX) { - hCounterTVX = static_cast(scalersFile->Get("lumi-task/hCounterTVX")); + hCounterTVX = dynamic_cast(scalersFile->Get("lumi-task/hCounterTVX")); if (!hCounterTVX) { - hCounterTVX = static_cast(scalersFile->Get("eventselection-run3/luminosity/hCounterTVX")); + hCounterTVX = dynamic_cast(scalersFile->Get("eventselection-run3/luminosity/hCounterTVX")); if (!hCounterTVX) { std::cout << "No hCounterTVX histogram found in the file, skipping upload for run " << runString << std::endl; continue; @@ -88,7 +88,7 @@ void uploadOTSobjects(std::string inputList, const std::string& passName, bool u std::unique_ptr bcRangesFile{TFile::Open((path + "/bcRanges_fullrun.root").data(), "READ")}; int Nmax = 0; for (auto key : *(bcRangesFile->GetListOfKeys())) { - TTree* cefpTree = static_cast(bcRangesFile->Get(Form("%s/selectedBC", key->GetName()))); + TTree* cefpTree = dynamic_cast(bcRangesFile->Get(Form("%s/selectedBC", key->GetName()))); if (!cefpTree) continue; ZorroHelper bci; @@ -134,7 +134,7 @@ void uploadOTSobjects(std::string inputList, const std::string& passName, bool u auto bcNext{nextHelper.bcAOD < nextHelper.bcEvSel ? nextHelper.bcAOD : nextHelper.bcEvSel}; if (bcNext - bcEnd > 2001 / o2::constants::lhc::LHCBunchSpacingMUS) { /// ensure a gap of 2ms between chunks chunk.insert(chunk.begin(), zorroHelpers.begin() + helperIndex, zorroHelpers.begin() + endIndex + 1); - endTS = (orbitResetTimestamp + int64_t(bcEnd * o2::constants::lhc::LHCBunchSpacingNS * 1e-3)) / 1000 + 1; + endTS = (orbitResetTimestamp + static_cast(bcEnd * o2::constants::lhc::LHCBunchSpacingNS * 1e-3)) / 1000 + 1; break; } bcEnd = nextHelper.bcAOD > nextHelper.bcEvSel ? nextHelper.bcAOD : nextHelper.bcEvSel; diff --git a/PWGCF/Core/AnalysisConfigurableCuts.h b/PWGCF/Core/AnalysisConfigurableCuts.h index 76b7b759f07..aebb6b2f407 100644 --- a/PWGCF/Core/AnalysisConfigurableCuts.h +++ b/PWGCF/Core/AnalysisConfigurableCuts.h @@ -151,7 +151,7 @@ class SimpleInclusiveCut : public TNamed SimpleInclusiveCut& operator=(const SimpleInclusiveCut&); private: - ClassDef(SimpleInclusiveCut, 1); + ClassDefOverride(SimpleInclusiveCut, 1); }; } // namespace analysis diff --git a/PWGCF/Core/CorrelationContainer.cxx b/PWGCF/Core/CorrelationContainer.cxx index 8cbd834ec8f..4590d9403d8 100644 --- a/PWGCF/Core/CorrelationContainer.cxx +++ b/PWGCF/Core/CorrelationContainer.cxx @@ -24,11 +24,12 @@ #include #include #include +#include +#include +#include #include #include #include -#include -#include #include #include #include @@ -36,6 +37,9 @@ #include #include +#include +#include +#include #include #include @@ -45,7 +49,7 @@ using namespace o2::constants::math; ClassImp(CorrelationContainer); -const Int_t CorrelationContainer::fgkCFSteps = 11; +const int CorrelationContainer::fgkCFSteps = 11; CorrelationContainer::CorrelationContainer() : TNamed(), mPairHist(nullptr), @@ -63,9 +67,9 @@ CorrelationContainer::CorrelationContainer() : TNamed(), mZVtxMax(0), mTrackEtaCut(0), mWeightPerEvent(0), - mSkipScaleMixedEvent(kFALSE), + mSkipScaleMixedEvent(false), mCache(nullptr), - mGetMultCacheOn(kFALSE), + mGetMultCacheOn(false), mGetMultCache(nullptr) { // Default constructor @@ -90,9 +94,9 @@ CorrelationContainer::CorrelationContainer(const char* name, const char* objTitl mZVtxMax(0), mTrackEtaCut(0), mWeightPerEvent(0), - mSkipScaleMixedEvent(kFALSE), + mSkipScaleMixedEvent(false), mCache(nullptr), - mGetMultCacheOn(kFALSE), + mGetMultCacheOn(false), mGetMultCache(nullptr) { // correlationAxis has to provide a 6 length list of AxisSpec which contain: @@ -101,6 +105,7 @@ CorrelationContainer::CorrelationContainer(const char* name, const char* objTitl // // efficiencyAxis has to provide a 3 length list of AxisSpec which contains: // eta (for efficiency), pt (for efficiency), vertex (for efficiency) + // and can provide a fourth one, e.g. phi (for efficiency) // The third axis of correlationAxis (multiplicity/centrality) is used also for the efficiency // // (optional) userAxis can contain optional user axes which are added to pair and trigger histogram and are @@ -112,7 +117,7 @@ CorrelationContainer::CorrelationContainer(const char* name, const char* objTitl std::vector pairAxis(correlationAxis); pairAxis.insert(pairAxis.end(), userAxis.begin(), userAxis.end()); - long bins = 1; + int64_t bins = 1; for (const auto& axis : pairAxis) { bins *= axis.getNbins(); } @@ -124,7 +129,11 @@ CorrelationContainer::CorrelationContainer(const char* name, const char* objTitl triggerAxis.insert(triggerAxis.end(), userAxis.begin(), userAxis.end()); mTriggerHist = HistFactory::createHist({"mTriggerHist", "d^{2}N_{ch}/d#varphid#eta", {HistType::kStepTHnF, triggerAxis, fgkCFSteps}}).release(); - mTrackHistEfficiency = HistFactory::createHist({"mTrackHistEfficiency", "Tracking efficiency", {HistType::kStepTHnF, {efficiencyAxis[0], efficiencyAxis[1], {5, -0.5, 4.5, "species"}, correlationAxis[3], efficiencyAxis[2]}, fgkCFSteps}}).release(); + std::vector trackEfficiencyAxis({efficiencyAxis[0], efficiencyAxis[1], {5, -0.5, 4.5, "species"}, correlationAxis[3], efficiencyAxis[2]}); + if (efficiencyAxis.size() > 3) { + trackEfficiencyAxis.push_back(efficiencyAxis[3]); + } + mTrackHistEfficiency = HistFactory::createHist({"mTrackHistEfficiency", "Tracking efficiency", {HistType::kStepTHnF, trackEfficiencyAxis, fgkCFSteps}}).release(); mEventCount = HistFactory::createHist({"mEventCount", ";step;centrality;count", {HistType::kTH2F, {{fgkCFSteps + 2, -2.5, -0.5 + fgkCFSteps, "step"}, correlationAxis[3]}}}).release(); } @@ -145,16 +154,16 @@ CorrelationContainer::CorrelationContainer(const CorrelationContainer& c) : TNam mZVtxMax(0), mTrackEtaCut(0), mWeightPerEvent(0), - mSkipScaleMixedEvent(kFALSE), + mSkipScaleMixedEvent(false), mCache(nullptr), - mGetMultCacheOn(kFALSE), + mGetMultCacheOn(false), mGetMultCache(nullptr) { // // CorrelationContainer copy constructor // - ((CorrelationContainer&)c).Copy(*this); + c.Copy(*this); } //____________________________________________________________________ @@ -194,7 +203,7 @@ CorrelationContainer& CorrelationContainer::operator=(const CorrelationContainer // assigment operator if (this != &c) { - ((CorrelationContainer&)c).Copy(*this); + c.Copy(*this); } return *this; @@ -205,7 +214,7 @@ void CorrelationContainer::Copy(TObject& c) const { // copy function - CorrelationContainer& target = (CorrelationContainer&)c; + auto& target = dynamic_cast(c); if (mPairHist) { target.mPairHist = dynamic_cast(mPairHist->Clone()); @@ -252,14 +261,14 @@ Long64_t CorrelationContainer::Merge(TCollection* list) TObject* obj = nullptr; // collections of objects - const UInt_t kMaxLists = 4; + const unsigned int kMaxLists = 4; TList** lists = new TList*[kMaxLists]; - for (UInt_t i = 0; i < kMaxLists; i++) { + for (unsigned int i = 0; i < kMaxLists; i++) { lists[i] = new TList; } - Int_t count = 0; + int count = 0; while ((obj = iter->Next())) { CorrelationContainer* entry = dynamic_cast(obj); @@ -285,7 +294,7 @@ Long64_t CorrelationContainer::Merge(TCollection* list) mTrackHistEfficiency->Merge(lists[2]); mEventCount->Merge(lists[3]); - for (UInt_t i = 0; i < kMaxLists; i++) { + for (unsigned int i = 0; i < kMaxLists; i++) { delete lists[i]; } @@ -316,7 +325,7 @@ void CorrelationContainer::resetBinLimits(THnBase* grid, int max_dimension) max_dimension = grid->GetNdimensions(); } - for (Int_t i = 0; i < max_dimension; i++) { + for (int i = 0; i < max_dimension; i++) { if (grid->GetAxis(i)->TestBit(TAxis::kAxisRange)) { grid->GetAxis(i)->SetRange(0, 0); // reset range } @@ -324,14 +333,14 @@ void CorrelationContainer::resetBinLimits(THnBase* grid, int max_dimension) } //____________________________________________________________________ -void CorrelationContainer::countEmptyBins(CorrelationContainer::CFStep step, Float_t ptTriggerMin, Float_t ptTriggerMax) +void CorrelationContainer::countEmptyBins(CorrelationContainer::CFStep step, float ptTriggerMin, float ptTriggerMax) { // prints the number of empty bins in the track end event histograms in the given step - Int_t binBegin[4]; - Int_t binEnd[4]; + int binBegin[4]; + int binEnd[4]; - for (Int_t i = 0; i < 4; i++) { + for (int i = 0; i < 4; i++) { binBegin[i] = 1; binEnd[i] = mPairHist->getTHn(step)->GetAxis(i)->GetNbins(); } @@ -354,11 +363,11 @@ void CorrelationContainer::countEmptyBins(CorrelationContainer::CFStep step, Flo // start from multiplicity 1 binBegin[3] = mPairHist->getTHn(step)->GetAxis(3)->FindBin(1); - Int_t total = 0; - Int_t count = 0; - Int_t vars[4]; + int total = 0; + int count = 0; + int vars[4]; - for (Int_t i = 0; i < 4; i++) { + for (int i = 0; i < 4; i++) { vars[i] = binBegin[i]; } @@ -374,7 +383,7 @@ void CorrelationContainer::countEmptyBins(CorrelationContainer::CFStep step, Flo } vars[3]++; - for (Int_t i = 3; i > 0; i--) { + for (int i = 3; i > 0; i--) { if (vars[i] == binEnd[i] + 1) { vars[i] = binBegin[i]; vars[i - 1]++; @@ -391,7 +400,7 @@ void CorrelationContainer::countEmptyBins(CorrelationContainer::CFStep step, Flo } //____________________________________________________________________ -void CorrelationContainer::getHistsZVtxMult(CorrelationContainer::CFStep step, Float_t ptTriggerMin, Float_t ptTriggerMax, THnBase** trackHist, TH2** eventHist) +void CorrelationContainer::getHistsZVtxMult(CorrelationContainer::CFStep step, float ptTriggerMin, float ptTriggerMax, THnBase** trackHist, TH2** eventHist) { // Calculates a 4d histogram with deltaphi, deltaeta, zvtx, multiplicity on track level and // a 2d histogram on event level (as fct of zvtx, multiplicity) @@ -414,15 +423,15 @@ void CorrelationContainer::getHistsZVtxMult(CorrelationContainer::CFStep step, F sparse->GetAxis(1)->SetRangeUser(mPtMin, mPtMax); } - Int_t firstBin = sparse->GetAxis(2)->FindBin(ptTriggerMin); - Int_t lastBin = sparse->GetAxis(2)->FindBin(ptTriggerMax); + int firstBin = sparse->GetAxis(2)->FindBin(ptTriggerMin); + int lastBin = sparse->GetAxis(2)->FindBin(ptTriggerMax); LOGF(info, "Using trigger pT range %d --> %d", firstBin, lastBin); sparse->GetAxis(2)->SetRange(firstBin, lastBin); mTriggerHist->getTHn(step)->GetAxis(0)->SetRange(firstBin, lastBin); - Int_t dimensions[] = {4, 0, 5, 3}; + int dimensions[] = {4, 0, 5, 3}; THnBase* tmpTrackHist = sparse->ProjectionND(4, dimensions, "E"); - *eventHist = (TH2*)mTriggerHist->getTHn(step)->Projection(1, 2); // NOTE the syntax is Projection(Y, X) --> produces a histogram where x = axis 2 (vertex) and y = axis 1 (multiplicity) + *eventHist = static_cast(mTriggerHist->getTHn(step)->Projection(1, 2)); // NOTE the syntax is Projection(Y, X) --> produces a histogram where x = axis 2 (vertex) and y = axis 1 (multiplicity) // convert to THn *trackHist = changeToThn(tmpTrackHist); delete tmpTrackHist; @@ -431,7 +440,7 @@ void CorrelationContainer::getHistsZVtxMult(CorrelationContainer::CFStep step, F resetBinLimits(mTriggerHist->getTHn(step), 3); } -TH2* CorrelationContainer::getPerTriggerYield(CorrelationContainer::CFStep step, Float_t ptTriggerMin, Float_t ptTriggerMax, Bool_t normalizePerTrigger) +TH2* CorrelationContainer::getPerTriggerYield(CorrelationContainer::CFStep step, float ptTriggerMin, float ptTriggerMax, bool normalizePerTrigger) { // Calculate per trigger yield without considering mixed event // Sums over all vertex bins, and respects the pT and centrality limits set in the class @@ -471,7 +480,7 @@ TH2* CorrelationContainer::getPerTriggerYield(CorrelationContainer::CFStep step, } TH2* yield = trackSameAll->Projection(1, 0, "E"); - Float_t triggers = eventSameAll->Integral(vertexBinBegin, vertexBinEnd, multBinBegin, multBinEnd); + float triggers = eventSameAll->Integral(vertexBinBegin, vertexBinEnd, multBinBegin, multBinEnd); if (normalizePerTrigger) { LOGF(info, "Dividing %f tracks by %f triggers", yield->Integral(), triggers); @@ -481,7 +490,7 @@ TH2* CorrelationContainer::getPerTriggerYield(CorrelationContainer::CFStep step, } // normalizate to dphi width - Float_t normalization = yield->GetXaxis()->GetBinWidth(1); + float normalization = yield->GetXaxis()->GetBinWidth(1); yield->Scale(1.0 / normalization); delete trackSameAll; @@ -490,7 +499,7 @@ TH2* CorrelationContainer::getPerTriggerYield(CorrelationContainer::CFStep step, return yield; } -TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, CorrelationContainer::CFStep step, Float_t ptTriggerMin, Float_t ptTriggerMax, Bool_t normalizePerTrigger, Int_t stepForMixed, Int_t* trigger) +TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, CorrelationContainer::CFStep step, float ptTriggerMin, float ptTriggerMax, bool normalizePerTrigger, int stepForMixed, int* trigger) { // Extract 2D per trigger yield with mixed event correction. The quantity is calculated for *each* vertex bin and multiplicity bin and then a sum of ratios is performed: // 1_N [ (same/mixed)_1 + (same/mixed)_2 + (same/mixed)_3 + ... ] @@ -506,8 +515,8 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati // normalizePerTrigger: divide through number of triggers // do not add this hists to the directory - Bool_t oldStatus = TH1::AddDirectoryStatus(); - TH1::AddDirectory(kFALSE); + bool oldStatus = TH1::AddDirectoryStatus(); + TH1::AddDirectory(false); TH2* totalTracks = nullptr; @@ -518,11 +527,11 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati TH2* eventMixedAll = nullptr; TH2* eventMixedAllStep6 = nullptr; - Long64_t totalEvents = 0; - Int_t nCorrelationFunctions = 0; + int64_t totalEvents = 0; + int nCorrelationFunctions = 0; getHistsZVtxMult(step, ptTriggerMin, ptTriggerMax, &trackSameAll, &eventSameAll); - mixed->getHistsZVtxMult((stepForMixed == -1) ? step : (CFStep)stepForMixed, ptTriggerMin, ptTriggerMax, &trackMixedAll, &eventMixedAll); + mixed->getHistsZVtxMult((stepForMixed == -1) ? step : static_cast(stepForMixed), ptTriggerMin, ptTriggerMax, &trackMixedAll, &eventMixedAll); // If we ask for histograms from step8 (TTR cut applied) there is a hole at 0,0; so this cannot be used for the // mixed-event normalization. If step6 is available, the normalization factor is read out from that one. @@ -548,15 +557,15 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati multBinEnd = multAxis->FindBin(mCentralityMax); } - for (Int_t multBin = TMath::Max(1, multBinBegin); multBin <= TMath::Min(multAxis->GetNbins(), multBinEnd); multBin++) { + for (int multBin = std::max(1, multBinBegin); multBin <= std::min(multAxis->GetNbins(), multBinEnd); multBin++) { trackSameAll->GetAxis(3)->SetRange(multBin, multBin); trackMixedAll->GetAxis(3)->SetRange(multBin, multBin); if (trackMixedAllStep6) { trackMixedAllStep6->GetAxis(3)->SetRange(multBin, multBin); } - Double_t mixedNorm = 1; - Double_t mixedNormError = 0; + double mixedNorm = 1; + double mixedNormError = 0; if (!mSkipScaleMixedEvent) { // get mixed normalization correction factor: is independent of vertex bin if scaled with number of triggers @@ -569,40 +578,40 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati tracksMixed = trackMixedAll->Projection(1, 0, "E"); } // Printf("%f", tracksMixed->Integral()); - Float_t binWidthEta = tracksMixed->GetYaxis()->GetBinWidth(1); + float binWidthEta = tracksMixed->GetYaxis()->GetBinWidth(1); if (stepForMixed == -1 && step == kCFStepBiasStudy && !trackMixedAllStep6) { // get mixed event normalization by assuming full acceptance at deta at 0 (integrate over dphi), excluding (0, 0) - Float_t phiExclude = 0.41; + float phiExclude = 0.41; mixedNorm = tracksMixed->IntegralAndError(1, tracksMixed->GetXaxis()->FindBin(-phiExclude) - 1, tracksMixed->GetYaxis()->FindBin(-0.01), tracksMixed->GetYaxis()->FindBin(0.01), mixedNormError); - Double_t mixedNormError2 = 0; - Double_t mixedNorm2 = tracksMixed->IntegralAndError(tracksMixed->GetXaxis()->FindBin(phiExclude) + 1, tracksMixed->GetNbinsX(), tracksMixed->GetYaxis()->FindBin(-0.01), tracksMixed->GetYaxis()->FindBin(0.01), mixedNormError2); + double mixedNormError2 = 0; + double mixedNorm2 = tracksMixed->IntegralAndError(tracksMixed->GetXaxis()->FindBin(phiExclude) + 1, tracksMixed->GetNbinsX(), tracksMixed->GetYaxis()->FindBin(-0.01), tracksMixed->GetYaxis()->FindBin(0.01), mixedNormError2); if (mixedNormError == 0 || mixedNormError2 == 0) { LOGF(error, "ERROR: Skipping multiplicity %d because mixed event is empty %f %f %f %f", multBin, mixedNorm, mixedNormError, mixedNorm2, mixedNormError2); continue; } - Int_t nBinsMixedNorm = (tracksMixed->GetXaxis()->FindBin(-phiExclude) - 1 - 1 + 1) * (tracksMixed->GetYaxis()->FindBin(0.01) - tracksMixed->GetYaxis()->FindBin(-0.01) + 1); + int nBinsMixedNorm = (tracksMixed->GetXaxis()->FindBin(-phiExclude) - 1 - 1 + 1) * (tracksMixed->GetYaxis()->FindBin(0.01) - tracksMixed->GetYaxis()->FindBin(-0.01) + 1); mixedNorm /= nBinsMixedNorm; mixedNormError /= nBinsMixedNorm; - Int_t nBinsMixedNorm2 = (tracksMixed->GetNbinsX() - tracksMixed->GetXaxis()->FindBin(phiExclude) - 1 + 1) * (tracksMixed->GetYaxis()->FindBin(0.01) - tracksMixed->GetYaxis()->FindBin(-0.01) + 1); + int nBinsMixedNorm2 = (tracksMixed->GetNbinsX() - tracksMixed->GetXaxis()->FindBin(phiExclude) - 1 + 1) * (tracksMixed->GetYaxis()->FindBin(0.01) - tracksMixed->GetYaxis()->FindBin(-0.01) + 1); mixedNorm2 /= nBinsMixedNorm2; mixedNormError2 /= nBinsMixedNorm2; mixedNorm = mixedNorm / mixedNormError / mixedNormError + mixedNorm2 / mixedNormError2 / mixedNormError2; - mixedNormError = TMath::Sqrt(1.0 / (1.0 / mixedNormError / mixedNormError + 1.0 / mixedNormError2 / mixedNormError2)); + mixedNormError = std::sqrt(1.0 / (1.0 / mixedNormError / mixedNormError + 1.0 / mixedNormError2 / mixedNormError2)); mixedNorm *= mixedNormError * mixedNormError; } else { // get mixed event normalization at (0,0) // NOTE if variable bin size is used around (0,0) to reduce two-track effect to limited bins, the normalization gets a bit tricky here (finite bin correction and normalization are made only for fixed size bins). // The normalization factor has to determined in a bin as large as the normal bin size, as a proxy the bin with index (1, 1) is used - Float_t binWidthPhi = tracksMixed->GetXaxis()->GetBinWidth(1); + float binWidthPhi = tracksMixed->GetXaxis()->GetBinWidth(1); mixedNorm = tracksMixed->IntegralAndError(tracksMixed->GetXaxis()->FindBin(-binWidthPhi + 1e-4), tracksMixed->GetXaxis()->FindBin(binWidthPhi - 1e-4), tracksMixed->GetYaxis()->FindBin(-binWidthEta + 1e-4), tracksMixed->GetYaxis()->FindBin(binWidthEta - 1e-4), mixedNormError); - Int_t nBinsMixedNorm = 4; // NOTE this is fixed on purpose, even if binning is made finer around (0,0), this corresponds to the equivalent of four "large" bins around (0,0) + int nBinsMixedNorm = 4; // NOTE this is fixed on purpose, even if binning is made finer around (0,0), this corresponds to the equivalent of four "large" bins around (0,0) mixedNorm /= nBinsMixedNorm; mixedNormError /= nBinsMixedNorm; @@ -614,7 +623,7 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati // finite bin correction if (mTrackEtaCut > 0) { - Double_t finiteBinCorrection = -1.0 / (2 * mTrackEtaCut) * binWidthEta / 2 + 1; + double finiteBinCorrection = -1.0 / (2 * mTrackEtaCut) * binWidthEta / 2 + 1; LOGF(info, "Finite bin correction: %f", finiteBinCorrection); mixedNorm /= finiteBinCorrection; mixedNormError /= finiteBinCorrection; @@ -622,7 +631,7 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati LOGF(error, "ERROR: mTrackEtaCut not set. Finite bin correction cannot be applied. Continuing anyway..."); } - Float_t triggers = eventMixedAll->Integral(1, eventMixedAll->GetNbinsX(), multBin, multBin); + float triggers = eventMixedAll->Integral(1, eventMixedAll->GetNbinsX(), multBin, multBin); // Printf("%f +- %f | %f | %f", mixedNorm, mixedNormError, triggers, mixedNorm / triggers); if (triggers <= 0) { LOGF(error, "ERROR: Skipping multiplicity %d because mixed event is empty", multBin); @@ -630,7 +639,6 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati } mixedNorm /= triggers; - mixedNormError /= triggers; delete tracksMixed; } else { @@ -647,15 +655,15 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati // normParameters->Fill(mixedNorm); TAxis* vertexAxis = trackSameAll->GetAxis(2); - Int_t vertexBinBegin = 1; - Int_t vertexBinEnd = vertexAxis->GetNbins(); + int vertexBinBegin = 1; + int vertexBinEnd = vertexAxis->GetNbins(); if (mZVtxMax > mZVtxMin) { vertexBinBegin = vertexAxis->FindBin(mZVtxMin); vertexBinEnd = vertexAxis->FindBin(mZVtxMax); } - for (Int_t vertexBin = vertexBinBegin; vertexBin <= vertexBinEnd; vertexBin++) { + for (int vertexBin = vertexBinBegin; vertexBin <= vertexBinEnd; vertexBin++) { trackSameAll->GetAxis(2)->SetRange(vertexBin, vertexBin); trackMixedAll->GetAxis(2)->SetRange(vertexBin, vertexBin); @@ -666,13 +674,13 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati // TH1* histMixedproj = mixedTwoD->ProjectionY(); // histMixedproj->Scale(1.0 / mixedTwoD->GetNbinsX()); // - // for (Int_t x=1; x<=mixedTwoD->GetNbinsX(); x++) - // for (Int_t y=1; y<=mixedTwoD->GetNbinsY(); y++) + // for (int x=1; x<=mixedTwoD->GetNbinsX(); x++) + // for (int y=1; y<=mixedTwoD->GetNbinsY(); y++) // mixedTwoD->SetBinContent(x, y, histMixedproj->GetBinContent(y)); // delete histMixedproj; - Float_t triggers2 = eventMixedAll->Integral(vertexBin, vertexBin, multBin, multBin); + float triggers2 = eventMixedAll->Integral(vertexBin, vertexBin, multBin, multBin); if (triggers2 <= 0) { LOGF(error, "ERROR: Skipping multiplicity %d vertex bin %d because mixed event is empty", multBin, vertexBin); } else { @@ -687,14 +695,14 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati // new TCanvas; tracksMixed->DrawClone("SURF1"); // some code to judge the relative contribution of the different correlation functions to the overall uncertainty - Double_t sums[] = {0, 0, 0}; - Double_t errors[] = {0, 0, 0}; + double sums[] = {0, 0, 0}; + double errors[] = {0, 0, 0}; - Int_t checkBinYBegin = 1; // tracksSame->GetXaxis()->FindBin(-0.79); - Int_t checkBinYEnd = tracksSame->GetNbinsY(); // tracksSame->GetXaxis()->FindBin(0.79); + int checkBinYBegin = 1; // tracksSame->GetXaxis()->FindBin(-0.79); + int checkBinYEnd = tracksSame->GetNbinsY(); // tracksSame->GetXaxis()->FindBin(0.79); - for (Int_t x = 1; x <= tracksSame->GetNbinsX(); x++) { - for (Int_t y = checkBinYBegin; y <= checkBinYEnd; y++) { + for (int x = 1; x <= tracksSame->GetNbinsX(); x++) { + for (int y = checkBinYBegin; y <= checkBinYEnd; y++) { sums[0] += tracksSame->GetBinContent(x, y); errors[0] += tracksSame->GetBinError(x, y); sums[1] += tracksMixed->GetBinContent(x, y); @@ -704,14 +712,14 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati tracksSame->Divide(tracksMixed); - for (Int_t x = 1; x <= tracksSame->GetNbinsX(); x++) { - for (Int_t y = checkBinYBegin; y <= checkBinYEnd; y++) { + for (int x = 1; x <= tracksSame->GetNbinsX(); x++) { + for (int y = checkBinYBegin; y <= checkBinYEnd; y++) { sums[2] += tracksSame->GetBinContent(x, y); errors[2] += tracksSame->GetBinError(x, y); } } - for (Int_t x = 0; x < 3; x++) { + for (int x = 0; x < 3; x++) { if (sums[x] > 0) { errors[x] /= sums[x]; } @@ -727,7 +735,7 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati */ if (!totalTracks) { - totalTracks = (TH2*)tracksSame->Clone("totalTracks"); + totalTracks = dynamic_cast(tracksSame->Clone("totalTracks")); } else { totalTracks->Add(tracksSame); } @@ -745,11 +753,11 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati } if (totalTracks) { - Double_t sums[] = {0, 0, 0}; - Double_t errors[] = {0, 0, 0}; + double sums[] = {0, 0, 0}; + double errors[] = {0, 0, 0}; - for (Int_t x = 1; x <= totalTracks->GetNbinsX(); x++) { - for (Int_t y = 1; y <= totalTracks->GetNbinsY(); y++) { + for (int x = 1; x <= totalTracks->GetNbinsX(); x++) { + for (int y = 1; y <= totalTracks->GetNbinsY(); y++) { sums[0] += totalTracks->GetBinContent(x, y); errors[0] += totalTracks->GetBinError(x, y); } @@ -759,17 +767,17 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati } if (normalizePerTrigger) { - LOGF(info, "Dividing %f tracks by %lld events (%d correlation function(s)) (error %f)", totalTracks->Integral(), totalEvents, nCorrelationFunctions, errors[0]); + LOGF(info, "Dividing %f tracks by %ld events (%d correlation function(s)) (error %f)", totalTracks->Integral(), totalEvents, nCorrelationFunctions, errors[0]); if (totalEvents > 0) { totalTracks->Scale(1.0 / totalEvents); } } if (trigger != nullptr) { - *trigger = (Int_t)totalEvents; + *trigger = static_cast(totalEvents); } // normalizate to dphi width - Float_t normalization = totalTracks->GetXaxis()->GetBinWidth(1); + float normalization = totalTracks->GetXaxis()->GetBinWidth(1); totalTracks->Scale(1.0 / normalization); } @@ -787,14 +795,14 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati return totalTracks; } -TH1* CorrelationContainer::getTriggersAsFunctionOfMultiplicity(CorrelationContainer::CFStep step, Float_t ptTriggerMin, Float_t ptTriggerMax) +TH1* CorrelationContainer::getTriggersAsFunctionOfMultiplicity(CorrelationContainer::CFStep step, float ptTriggerMin, float ptTriggerMax) { // returns the distribution of triggers as function of centrality/multiplicity resetBinLimits(mTriggerHist->getTHn(step), 3); - Int_t firstBin = mTriggerHist->getTHn(step)->GetAxis(0)->FindBin(ptTriggerMin); - Int_t lastBin = mTriggerHist->getTHn(step)->GetAxis(0)->FindBin(ptTriggerMax); + int firstBin = mTriggerHist->getTHn(step)->GetAxis(0)->FindBin(ptTriggerMin); + int lastBin = mTriggerHist->getTHn(step)->GetAxis(0)->FindBin(ptTriggerMax); LOGF(info, "Using pT range %d --> %d", firstBin, lastBin); mTriggerHist->getTHn(step)->GetAxis(0)->SetRange(firstBin, lastBin); @@ -826,7 +834,7 @@ THnBase* CorrelationContainer::getTrackEfficiencyND(EfficiencyStep step1, Effici sourceContainer->getTHn(step2)->GetAxis(0)->SetRangeUser(mEtaMin, mEtaMax); } - Int_t dimensions[] = {0, 1, 3, 4}; + int dimensions[] = {0, 1, 3, 4}; THnBase* generated = sourceContainer->getTHn(step1)->ProjectionND(4, dimensions); THnBase* measured = sourceContainer->getTHn(step2)->ProjectionND(4, dimensions); @@ -835,7 +843,7 @@ THnBase* CorrelationContainer::getTrackEfficiencyND(EfficiencyStep step1, Effici resetBinLimits(sourceContainer->getTHn(step1)); resetBinLimits(sourceContainer->getTHn(step2)); - THnBase* clone = (THnBase*)measured->Clone(); + auto* clone = dynamic_cast(measured->Clone()); clone->Divide(measured, generated, 1, 1, "B"); @@ -846,7 +854,7 @@ THnBase* CorrelationContainer::getTrackEfficiencyND(EfficiencyStep step1, Effici } //____________________________________________________________________ -TH1* CorrelationContainer::getTrackEfficiency(EfficiencyStep step1, EfficiencyStep step2, Int_t axis1, Int_t axis2, Int_t source, Int_t axis3) +TH1* CorrelationContainer::getTrackEfficiency(EfficiencyStep step1, EfficiencyStep step2, int axis1, int axis2, int source, int axis3) { // creates a track-level efficiency by dividing step2 by step1 // projected to axis1 and axis2 (optional if >= 0) @@ -907,8 +915,8 @@ TH1* CorrelationContainer::getTrackEfficiency(EfficiencyStep step1, EfficiencySt } // check for bins with less than 50 entries, print warning - Int_t binBegin[3]; - Int_t binEnd[3]; + int binBegin[3]; + int binEnd[3]; binBegin[0] = 1; binBegin[1] = 1; @@ -935,7 +943,7 @@ TH1* CorrelationContainer::getTrackEfficiency(EfficiencyStep step1, EfficiencySt if (mPtMax > mPtMin) { // TODO this is just checking up to 15 for now - Float_t ptMax = TMath::Min((Float_t)15., mPtMax); + float ptMax = std::min(static_cast(15.), mPtMax); if (axis1 == 1) { binBegin[0] = generated->GetXaxis()->FindBin(mPtMin); binEnd[0] = generated->GetXaxis()->FindBin(ptMax); @@ -950,15 +958,15 @@ TH1* CorrelationContainer::getTrackEfficiency(EfficiencyStep step1, EfficiencySt } } - Int_t total = 0; - Int_t count = 0; - Int_t vars[3]; + int total = 0; + int count = 0; + int vars[3]; - for (Int_t i = 0; i < 3; i++) { + for (int i = 0; i < 3; i++) { vars[i] = binBegin[i]; } - const Int_t limit = 50; + const int limit = 50; while (1) { if (generated->GetDimension() == 1 && generated->GetBinContent(vars[0]) < limit) { LOGF(info, "Empty bin at %s=%.2f (%.2f entries)", generated->GetXaxis()->GetTitle(), generated->GetXaxis()->GetBinCenter(vars[0]), generated->GetBinContent(vars[0])); @@ -1006,23 +1014,25 @@ TH1* CorrelationContainer::getTrackEfficiency(EfficiencyStep step1, EfficiencySt // 2d rebin with variable axis does not exist in root TH1* tmp = measured; - measured = new TH2D(Form("%s_rebinned", tmp->GetName()), tmp->GetTitle(), axis->GetNbins(), axis->GetXbins()->GetArray(), tmp->GetNbinsY(), tmp->GetYaxis()->GetXbins()->GetArray()); - for (Int_t x = 1; x <= tmp->GetNbinsX(); x++) { - for (Int_t y = 1; y <= tmp->GetNbinsY(); y++) { - ((TH2*)measured)->Fill(tmp->GetXaxis()->GetBinCenter(x), tmp->GetYaxis()->GetBinCenter(y), tmp->GetBinContent(x, y)); - measured->SetBinError(x, y, 0); // cannot trust bin error, set to 0 + auto* measuredRebinned = new TH2D(Form("%s_rebinned", tmp->GetName()), tmp->GetTitle(), axis->GetNbins(), axis->GetXbins()->GetArray(), tmp->GetNbinsY(), tmp->GetYaxis()->GetXbins()->GetArray()); + for (int x = 1; x <= tmp->GetNbinsX(); x++) { + for (int y = 1; y <= tmp->GetNbinsY(); y++) { + measuredRebinned->Fill(tmp->GetXaxis()->GetBinCenter(x), tmp->GetYaxis()->GetBinCenter(y), tmp->GetBinContent(x, y)); + measuredRebinned->SetBinError(x, y, 0); // cannot trust bin error, set to 0 } } + measured = measuredRebinned; delete tmp; tmp = generated; - generated = new TH2D(Form("%s_rebinned", tmp->GetName()), tmp->GetTitle(), axis->GetNbins(), axis->GetXbins()->GetArray(), tmp->GetNbinsY(), tmp->GetYaxis()->GetXbins()->GetArray()); - for (Int_t x = 1; x <= tmp->GetNbinsX(); x++) { - for (Int_t y = 1; y <= tmp->GetNbinsY(); y++) { - ((TH2*)generated)->Fill(tmp->GetXaxis()->GetBinCenter(x), tmp->GetYaxis()->GetBinCenter(y), tmp->GetBinContent(x, y)); - generated->SetBinError(x, y, 0); // cannot trust bin error, set to 0 + auto* generatedRebinned = new TH2D(Form("%s_rebinned", tmp->GetName()), tmp->GetTitle(), axis->GetNbins(), axis->GetXbins()->GetArray(), tmp->GetNbinsY(), tmp->GetYaxis()->GetXbins()->GetArray()); + for (int x = 1; x <= tmp->GetNbinsX(); x++) { + for (int y = 1; y <= tmp->GetNbinsY(); y++) { + generatedRebinned->Fill(tmp->GetXaxis()->GetBinCenter(x), tmp->GetYaxis()->GetBinCenter(y), tmp->GetBinContent(x, y)); + generatedRebinned->SetBinError(x, y, 0); // cannot trust bin error, set to 0 } } + generated = generatedRebinned; delete tmp; } else { TH1* tmp = measured; @@ -1046,7 +1056,7 @@ TH1* CorrelationContainer::getTrackEfficiency(EfficiencyStep step1, EfficiencySt LOGF(fatal, "Invalid binning --> %d %d", axis->GetNbins(), measured->GetNbinsX()); } - Double_t newBins[] = {0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 12.0, 14.0, 16.0, 18.0, 20.0, 25.0, 30.0, 35.0, 40.0, 45.0, 50.0, 100.0}; + double newBins[] = {0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 12.0, 14.0, 16.0, 18.0, 20.0, 25.0, 30.0, 35.0, 40.0, 45.0, 50.0, 100.0}; // reduce binning below 5 GeV/c TH1* tmp = measured; @@ -1056,7 +1066,7 @@ TH1* CorrelationContainer::getTrackEfficiency(EfficiencyStep step1, EfficiencySt // increase binning above 5 GeV/c tmp = measured; measured = new TH1F(Form("%s_rebinned2", tmp->GetName()), tmp->GetTitle(), axis->GetNbins(), axis->GetBinLowEdge(1), axis->GetBinUpEdge(axis->GetNbins())); - for (Int_t bin = 1; bin <= measured->GetNbinsX(); bin++) { + for (int bin = 1; bin <= measured->GetNbinsX(); bin++) { measured->SetBinContent(bin, tmp->GetBinContent(tmp->FindBin(measured->GetBinCenter(bin)))); measured->SetBinError(bin, tmp->GetBinError(tmp->FindBin(measured->GetBinCenter(bin)))); } @@ -1070,7 +1080,7 @@ TH1* CorrelationContainer::getTrackEfficiency(EfficiencyStep step1, EfficiencySt // increase binning above 5 GeV/c tmp = generated; generated = new TH1F(Form("%s_rebinned2", tmp->GetName()), tmp->GetTitle(), axis->GetNbins(), axis->GetBinLowEdge(1), axis->GetBinUpEdge(axis->GetNbins())); - for (Int_t bin = 1; bin <= generated->GetNbinsX(); bin++) { + for (int bin = 1; bin <= generated->GetNbinsX(); bin++) { generated->SetBinContent(bin, tmp->GetBinContent(tmp->FindBin(generated->GetBinCenter(bin)))); generated->SetBinError(bin, tmp->GetBinError(tmp->FindBin(generated->GetBinCenter(bin)))); } @@ -1089,7 +1099,7 @@ TH1* CorrelationContainer::getTrackEfficiency(EfficiencyStep step1, EfficiencySt } //____________________________________________________________________ -TH1* CorrelationContainer::getEventEfficiency(EfficiencyStep step1, EfficiencyStep step2, Int_t axis1, Int_t axis2, Float_t ptTriggerMin, Float_t ptTriggerMax) +TH1* CorrelationContainer::getEventEfficiency(EfficiencyStep step1, EfficiencyStep step2, int axis1, int axis2, float ptTriggerMin, float ptTriggerMax) { // creates a event-level efficiency by dividing step2 by step1 // projected to axis1 and axis2 (optional if >= 0) @@ -1132,9 +1142,9 @@ void CorrelationContainer::weightHistogram(TH3* hist1, TH1* hist2) LOGF(fatal, "Inconsistent binning %d %d", hist1->GetNbinsZ(), hist2->GetNbinsX()); } - for (Int_t x = 1; x <= hist1->GetNbinsX(); x++) { - for (Int_t y = 1; y <= hist1->GetNbinsY(); y++) { - for (Int_t z = 1; z <= hist1->GetNbinsZ(); z++) { + for (int x = 1; x <= hist1->GetNbinsX(); x++) { + for (int y = 1; y <= hist1->GetNbinsY(); y++) { + for (int z = 1; z <= hist1->GetNbinsZ(); z++) { if (hist2->GetBinContent(z) > 0) { hist1->SetBinContent(x, y, z, hist1->GetBinContent(x, y, z) / hist2->GetBinContent(z)); hist1->SetBinError(x, y, z, hist1->GetBinError(x, y, z) / hist2->GetBinContent(z)); @@ -1148,7 +1158,7 @@ void CorrelationContainer::weightHistogram(TH3* hist1, TH1* hist2) } //____________________________________________________________________ -TH1* CorrelationContainer::getBias(EfficiencyStep step1, EfficiencyStep step2, const char* axis, Float_t leadPtMin, Float_t leadPtMax, Int_t weighting) +TH1* CorrelationContainer::getBias(EfficiencyStep step1, EfficiencyStep step2, const char* axis, float leadPtMin, float leadPtMax, int weighting) { // extracts the track-level bias (integrating out the multiplicity) between two steps (dividing step2 by step1) // done by weighting the track-level distribution with the number of events as function of leading pT @@ -1169,14 +1179,14 @@ TH1* CorrelationContainer::getBias(EfficiencyStep step1, EfficiencyStep step2, c resetBinLimits(mTriggerHist->getTHn(step2), 3); setBinLimits(tmp->getTHn(step2)); - TH1D* events1 = (TH1D*)mTriggerHist->getTHn(step1)->Projection(0); - TH3D* hist1 = (TH3D*)tmp->getTHn(step1)->Projection(0, tmp->getNVar() - 1, 2); + TH1D* events1 = mTriggerHist->getTHn(step1)->Projection(0); + TH3D* hist1 = tmp->getTHn(step1)->Projection(0, tmp->getNVar() - 1, 2); if (weighting == 0) { weightHistogram(hist1, events1); } - TH1D* events2 = (TH1D*)mTriggerHist->getTHn(step2)->Projection(0); - TH3D* hist2 = (TH3D*)tmp->getTHn(step2)->Projection(0, tmp->getNVar() - 1, 2); + TH1D* events2 = mTriggerHist->getTHn(step2)->Projection(0); + TH3D* hist2 = tmp->getTHn(step2)->Projection(0, tmp->getNVar() - 1, 2); if (weighting == 0) { weightHistogram(hist2, events2); } @@ -1252,7 +1262,7 @@ TH2D* CorrelationContainer::getTrackingEfficiencyCentrality() } //____________________________________________________________________ -TH1D* CorrelationContainer::getTrackingEfficiency(Int_t axis) +TH1D* CorrelationContainer::getTrackingEfficiency(int axis) { // extracts the tracking efficiency for primary particles // integrates over the regions and all other variables than pT (axis == 0) and eta (axis == 1) to increase the statistics @@ -1261,7 +1271,7 @@ TH1D* CorrelationContainer::getTrackingEfficiency(Int_t axis) } //____________________________________________________________________ -TH1D* CorrelationContainer::getFakeRate(Int_t axis) +TH1D* CorrelationContainer::getFakeRate(int axis) { return dynamic_cast(getTrackEfficiency(EfficiencyStep::RecoAll, EfficiencyStep::Fake, axis)); } @@ -1277,7 +1287,7 @@ TH2D* CorrelationContainer::getTrackingCorrection() } //____________________________________________________________________ -TH1D* CorrelationContainer::getTrackingCorrection(Int_t axis) +TH1D* CorrelationContainer::getTrackingCorrection(int axis) { // extracts the correction for tracking efficiency and secondaries // integrates over the regions and all other variables than pT (axis == 0) and eta (axis == 1) to increase the statistics @@ -1308,7 +1318,7 @@ TH2D* CorrelationContainer::getTrackingEfficiencyCorrectionCentrality() } //____________________________________________________________________ -TH1D* CorrelationContainer::getTrackingEfficiencyCorrection(Int_t axis) +TH1D* CorrelationContainer::getTrackingEfficiencyCorrection(int axis) { // extracts the correction for tracking efficiency // integrates over the regions and all other variables than pT (axis == 0) and eta (axis == 1) to increase the statistics @@ -1339,7 +1349,7 @@ TH2D* CorrelationContainer::getTrackingContaminationCentrality() } //____________________________________________________________________ -TH1D* CorrelationContainer::getTrackingContamination(Int_t axis) +TH1D* CorrelationContainer::getTrackingContamination(int axis) { // extracts the tracking contamination by secondaries // integrates over the regions and all other variables than pT (axis == 0) and eta (axis == 1) to increase the statistics @@ -1385,7 +1395,7 @@ void CorrelationContainer::deepCopy(CorrelationContainer* from) // copies the entries of this object's members from the object to this object // fills using the fill function and thus allows that the objects have different binning - for (Int_t step = 0; step < mPairHist->getNSteps(); step++) { + for (int step = 0; step < mPairHist->getNSteps(); step++) { LOGF(info, "Copying step %d", step); THnBase* target = mPairHist->getTHn(step); THnBase* source = from->mPairHist->getTHn(step); @@ -1394,7 +1404,7 @@ void CorrelationContainer::deepCopy(CorrelationContainer* from) target->RebinnedAdd(source); } - for (Int_t step = 0; step < mTriggerHist->getNSteps(); step++) { + for (int step = 0; step < mTriggerHist->getNSteps(); step++) { LOGF(info, "Ev: Copying step %d", step); THnBase* target = mTriggerHist->getTHn(step); THnBase* source = from->mTriggerHist->getTHn(step); @@ -1403,7 +1413,7 @@ void CorrelationContainer::deepCopy(CorrelationContainer* from) target->RebinnedAdd(source); } - for (Int_t step = 0; step < TMath::Min(mTrackHistEfficiency->getNSteps(), from->mTrackHistEfficiency->getNSteps()); step++) { + for (int step = 0; step < std::min(mTrackHistEfficiency->getNSteps(), from->mTrackHistEfficiency->getNSteps()); step++) { if (!from->mTrackHistEfficiency->getTHn(step)) { continue; } @@ -1421,7 +1431,7 @@ void CorrelationContainer::symmetrizepTBins() { // copy pt,a < pt,t bins to pt,a > pt,t (inverting deltaphi and delta eta as it should be) including symmetric bins - for (Int_t step = 0; step < mPairHist->getNSteps(); step++) { + for (int step = 0; step < mPairHist->getNSteps(); step++) { LOGF(info, "Copying step %d", step); THnBase* target = mPairHist->getTHn(step); if (target->GetEntries() == 0) { @@ -1429,47 +1439,47 @@ void CorrelationContainer::symmetrizepTBins() } // for symmetric bins - THnBase* source = (THnBase*)target->Clone(); + auto* source = dynamic_cast(target->Clone()); // axes: 0 delta eta; 1 pT,a; 2 pT,t; 3 centrality; 4 delta phi; 5 vtx-z - for (Int_t i3 = 1; i3 <= target->GetAxis(3)->GetNbins(); i3++) { - for (Int_t i5 = 1; i5 <= target->GetAxis(5)->GetNbins(); i5++) { - for (Int_t i1 = 1; i1 <= target->GetAxis(1)->GetNbins(); i1++) { - for (Int_t i2 = 1; i2 <= target->GetAxis(2)->GetNbins(); i2++) { + for (int i3 = 1; i3 <= target->GetAxis(3)->GetNbins(); i3++) { + for (int i5 = 1; i5 <= target->GetAxis(5)->GetNbins(); i5++) { + for (int i1 = 1; i1 <= target->GetAxis(1)->GetNbins(); i1++) { + for (int i2 = 1; i2 <= target->GetAxis(2)->GetNbins(); i2++) { // find source bin - Int_t binA = target->GetAxis(1)->FindBin(target->GetAxis(2)->GetBinCenter(i2)); - Int_t binT = target->GetAxis(2)->FindBin(target->GetAxis(1)->GetBinCenter(i1)); + int binA = target->GetAxis(1)->FindBin(target->GetAxis(2)->GetBinCenter(i2)); + int binT = target->GetAxis(2)->FindBin(target->GetAxis(1)->GetBinCenter(i1)); LOGF(info, "(%d %d) Copying from %d %d to %d %d", i3, i5, binA, binT, i1, i2); - for (Int_t i0 = 1; i0 <= target->GetAxis(0)->GetNbins(); i0++) { - for (Int_t i4 = 1; i4 <= target->GetAxis(4)->GetNbins(); i4++) { - Int_t binEta = target->GetAxis(0)->FindBin(-target->GetAxis(0)->GetBinCenter(i0)); - Double_t phi = -target->GetAxis(4)->GetBinCenter(i4); + for (int i0 = 1; i0 <= target->GetAxis(0)->GetNbins(); i0++) { + for (int i4 = 1; i4 <= target->GetAxis(4)->GetNbins(); i4++) { + int binEta = target->GetAxis(0)->FindBin(-target->GetAxis(0)->GetBinCenter(i0)); + double phi = -target->GetAxis(4)->GetBinCenter(i4); if (phi < -PIHalf) { phi += TwoPI; } - Int_t binPhi = target->GetAxis(4)->FindBin(phi); + int binPhi = target->GetAxis(4)->FindBin(phi); - Int_t binSource[] = {binEta, binA, binT, i3, binPhi, i5}; - Int_t binTarget[] = {i0, i1, i2, i3, i4, i5}; + int binSource[] = {binEta, binA, binT, i3, binPhi, i5}; + int binTarget[] = {i0, i1, i2, i3, i4, i5}; - Double_t value = source->GetBinContent(binSource); - Double_t error = source->GetBinError(binSource); + double value = source->GetBinContent(binSource); + double error = source->GetBinError(binSource); if (error == 0) { continue; } - Double_t value2 = target->GetBinContent(binTarget); - Double_t error2 = target->GetBinError(binTarget); + double value2 = target->GetBinContent(binTarget); + double error2 = target->GetBinError(binTarget); - Double_t sum = value; - Double_t err = error; + double sum = value; + double err = error; if (error2 > 0) { sum = value + value2; - err = TMath::Sqrt(error * error + error2 * error2); + err = std::sqrt(error * error + error2 * error2); } // Printf(" Values: %f +- %f; %f +- %f --> %f +- %f", value, error, value2, error2, sum, err); @@ -1488,13 +1498,13 @@ void CorrelationContainer::symmetrizepTBins() } //____________________________________________________________________ -void CorrelationContainer::extendTrackingEfficiency(Bool_t verbose) +void CorrelationContainer::extendTrackingEfficiency(bool verbose) { // fits the tracking efficiency at high pT with a constant and fills all bins with this tracking efficiency - Float_t fitRangeBegin = 5.01; - Float_t fitRangeEnd = 14.99; - Float_t extendRangeBegin = 10.01; + float fitRangeBegin = 5.01; + float fitRangeEnd = 14.99; + float extendRangeBegin = 10.01; if (mTrackHistEfficiency->getNVar() == 3) { TH1* obj = getTrackingEfficiency(1); @@ -1506,7 +1516,7 @@ void CorrelationContainer::extendTrackingEfficiency(Bool_t verbose) obj->Fit("pol0", (verbose) ? "+" : "0+", "SAME", fitRangeBegin, fitRangeEnd); - Float_t trackingEff = obj->GetFunction("pol0")->GetParameter(0); + float trackingEff = obj->GetFunction("pol0")->GetParameter(0); obj = getTrackingContamination(1); @@ -1517,17 +1527,17 @@ void CorrelationContainer::extendTrackingEfficiency(Bool_t verbose) obj->Fit("pol0", (verbose) ? "+" : "0+", "SAME", fitRangeBegin, fitRangeEnd); - Float_t trackingCont = obj->GetFunction("pol0")->GetParameter(0); + float trackingCont = obj->GetFunction("pol0")->GetParameter(0); LOGF(info, "CorrelationContainer::extendTrackingEfficiency: Fitted efficiency between %f and %f and got %f tracking efficiency and %f tracking contamination correction. Extending from %f onwards (within %f < eta < %f)", fitRangeBegin, fitRangeEnd, trackingEff, trackingCont, extendRangeBegin, mEtaMin, mEtaMax); // extend for full pT range - for (Int_t x = mTrackHistEfficiency->getTHn(0)->GetAxis(0)->FindBin(mEtaMin); x <= mTrackHistEfficiency->getTHn(0)->GetAxis(0)->FindBin(mEtaMax); x++) { - for (Int_t y = mTrackHistEfficiency->getTHn(0)->GetAxis(1)->FindBin(extendRangeBegin); y <= mTrackHistEfficiency->getTHn(0)->GetAxis(1)->GetNbins(); y++) { - for (Int_t z = 1; z <= mTrackHistEfficiency->getTHn(0)->GetAxis(2)->GetNbins(); z++) // particle type axis + for (int x = mTrackHistEfficiency->getTHn(0)->GetAxis(0)->FindBin(mEtaMin); x <= mTrackHistEfficiency->getTHn(0)->GetAxis(0)->FindBin(mEtaMax); x++) { + for (int y = mTrackHistEfficiency->getTHn(0)->GetAxis(1)->FindBin(extendRangeBegin); y <= mTrackHistEfficiency->getTHn(0)->GetAxis(1)->GetNbins(); y++) { + for (int z = 1; z <= mTrackHistEfficiency->getTHn(0)->GetAxis(2)->GetNbins(); z++) // particle type axis { - Int_t bins[3]; + int bins[3]; bins[0] = x; bins[1] = y; bins[2] = z; @@ -1540,28 +1550,28 @@ void CorrelationContainer::extendTrackingEfficiency(Bool_t verbose) } } else if (mTrackHistEfficiency->getNVar() == 4) { // fit in centrality intervals of 20% for efficiency, one bin for contamination - Float_t* trackingEff = nullptr; - Float_t* trackingCont = nullptr; - Float_t centralityBins[] = {0, 10, 20, 40, 60, 100}; - Int_t nCentralityBins = 5; + float* trackingEff = nullptr; + float* trackingCont = nullptr; + float centralityBins[] = {0, 10, 20, 40, 60, 100}; + int nCentralityBins = 5; LOGF(info, "CorrelationContainer::extendTrackingEfficiency: Fitting efficiencies between %f and %f. Extending from %f onwards (within %f < eta < %f)", fitRangeBegin, fitRangeEnd, extendRangeBegin, mEtaMin, mEtaMax); // 0 = eff; 1 = cont - for (Int_t caseNo = 0; caseNo < 2; caseNo++) { - Float_t* target = nullptr; - Int_t centralityBinsLocal = nCentralityBins; + for (int caseNo = 0; caseNo < 2; caseNo++) { + float* target = nullptr; + int centralityBinsLocal = nCentralityBins; if (caseNo == 0) { - trackingEff = new Float_t[centralityBinsLocal]; + trackingEff = new float[centralityBinsLocal]; target = trackingEff; } else { centralityBinsLocal = 1; - trackingCont = new Float_t[centralityBinsLocal]; + trackingCont = new float[centralityBinsLocal]; target = trackingCont; } - for (Int_t i = 0; i < centralityBinsLocal; i++) { + for (int i = 0; i < centralityBinsLocal; i++) { if (centralityBinsLocal == 1) { setCentralityRange(centralityBins[0] + 0.1, centralityBins[nCentralityBins] - 0.1); } else { @@ -1572,7 +1582,7 @@ void CorrelationContainer::extendTrackingEfficiency(Bool_t verbose) new TCanvas; proj->DrawCopy(); } - if ((Int_t)proj->Fit("pol0", (verbose) ? "+" : "Q0+", "SAME", fitRangeBegin, fitRangeEnd) == 0) { + if (static_cast(proj->Fit("pol0", (verbose) ? "+" : "Q0+", "SAME", fitRangeBegin, fitRangeEnd)) == 0) { target[i] = proj->GetFunction("pol0")->GetParameter(0); } else { target[i] = 0; @@ -1582,20 +1592,20 @@ void CorrelationContainer::extendTrackingEfficiency(Bool_t verbose) } // extend for full pT range - for (Int_t x = mTrackHistEfficiency->getTHn(0)->GetAxis(0)->FindBin(mEtaMin); x <= mTrackHistEfficiency->getTHn(0)->GetAxis(0)->FindBin(mEtaMax); x++) { - for (Int_t y = mTrackHistEfficiency->getTHn(0)->GetAxis(1)->FindBin(extendRangeBegin); y <= mTrackHistEfficiency->getTHn(0)->GetAxis(1)->GetNbins(); y++) { - for (Int_t z = 1; z <= mTrackHistEfficiency->getTHn(0)->GetAxis(2)->GetNbins(); z++) // particle type axis + for (int x = mTrackHistEfficiency->getTHn(0)->GetAxis(0)->FindBin(mEtaMin); x <= mTrackHistEfficiency->getTHn(0)->GetAxis(0)->FindBin(mEtaMax); x++) { + for (int y = mTrackHistEfficiency->getTHn(0)->GetAxis(1)->FindBin(extendRangeBegin); y <= mTrackHistEfficiency->getTHn(0)->GetAxis(1)->GetNbins(); y++) { + for (int z = 1; z <= mTrackHistEfficiency->getTHn(0)->GetAxis(2)->GetNbins(); z++) // particle type axis { - for (Int_t z2 = 1; z2 <= mTrackHistEfficiency->getTHn(0)->GetAxis(3)->GetNbins(); z2++) // centrality axis + for (int z2 = 1; z2 <= mTrackHistEfficiency->getTHn(0)->GetAxis(3)->GetNbins(); z2++) // centrality axis { - Int_t bins[4]; + int bins[4]; bins[0] = x; bins[1] = y; bins[2] = z; bins[3] = z2; - Int_t z2Bin = 0; + int z2Bin = 0; while (centralityBins[z2Bin + 1] < mTrackHistEfficiency->getTHn(0)->GetAxis(3)->GetBinCenter(z2)) { z2Bin++; } @@ -1622,11 +1632,11 @@ void CorrelationContainer::extendTrackingEfficiency(Bool_t verbose) } /* -void CorrelationContainer::Scale(Double_t factor) +void CorrelationContainer::Scale(double factor) { // scales all contained histograms by the given factor - for (Int_t i=0; i<4; i++) + for (int i=0; i<4; i++) if (mTrackHist[i]) mTrackHist[i]->Scale(factor); @@ -1638,15 +1648,15 @@ void CorrelationContainer::Reset() { // resets all contained histograms - for (Int_t step = 0; step < mPairHist->getNSteps(); step++) { + for (int step = 0; step < mPairHist->getNSteps(); step++) { mPairHist->getTHn(step)->Reset(); } - for (Int_t step = 0; step < mTriggerHist->getNSteps(); step++) { + for (int step = 0; step < mTriggerHist->getNSteps(); step++) { mTriggerHist->getTHn(step)->Reset(); } - for (Int_t step = 0; step < mTrackHistEfficiency->getNSteps(); step++) { + for (int step = 0; step < mTrackHistEfficiency->getNSteps(); step++) { mTrackHistEfficiency->getTHn(step)->Reset(); } } @@ -1658,7 +1668,7 @@ THnBase* CorrelationContainer::changeToThn(THnBase* sparse) return THn::CreateHn(Form("%s_thn", sparse->GetName()), sparse->GetTitle(), sparse); } -void CorrelationContainer::fillEvent(Float_t centrality, CFStep step) +void CorrelationContainer::fillEvent(float centrality, CFStep step) { // Fill per-event information mEventCount->Fill(step, centrality); diff --git a/PWGCF/Core/PWGCFCoreLinkDef.h b/PWGCF/Core/PWGCFCoreLinkDef.h index fcc73500be2..235b4a7d2b7 100644 --- a/PWGCF/Core/PWGCFCoreLinkDef.h +++ b/PWGCF/Core/PWGCFCoreLinkDef.h @@ -8,8 +8,8 @@ // 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. -#ifndef PWGCF_CORE_PWGCFCORELINKDEF_H_ -#define PWGCF_CORE_PWGCFCORELINKDEF_H_ + +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -23,4 +23,3 @@ #pragma link C++ class o2::analysis::TrackSelectionPIDCfg + ; #pragma link C++ class o2::analysis::SimpleInclusiveCut + ; #pragma link C++ class CorrelationContainer + ; -#endif // PWGCF_CORE_PWGCFCORELINKDEF_H_ diff --git a/PWGCF/DataModel/FemtoDerived.h b/PWGCF/DataModel/FemtoDerived.h index 0b779485244..cac62250bbe 100644 --- a/PWGCF/DataModel/FemtoDerived.h +++ b/PWGCF/DataModel/FemtoDerived.h @@ -743,6 +743,8 @@ enum ParticleOriginMCTruth { kSecondaryDaughterXistar0, //! Daughter from a Xi*^0 decay kSecondaryDaughterXistarMinus, //! Daughter from a Xi*^- decay kElse, //! none of the above; (NOTE: used to catch bugs. will be removed once MC usage is properly validated) + kSecondaryFromHF, //! Kaon from a charm- or beauty-hadron decay chain + kSecondaryFromLF, //! Kaon from a light-flavour decay without a heavy-flavour ancestor kNOriginMCTruthTypes }; @@ -762,7 +764,9 @@ static constexpr std::string_view ParticleOriginMCTruthName[kNOriginMCTruthTypes "_SecondaryDaughterOmegaMinus", "_SecondaryDaughterXistar0", "_SecondaryDaughterXistarMinus", - "_Else"}; + "_Else", + "_SecondaryFromHF", + "_SecondaryFromLF"}; /// Distinguished between reconstructed and truth enum MCType { diff --git a/PWGCF/EbyEFluctuations/Tasks/FactorialMomentsTask.cxx b/PWGCF/EbyEFluctuations/Tasks/FactorialMomentsTask.cxx index 26331d34127..3cd857c3655 100644 --- a/PWGCF/EbyEFluctuations/Tasks/FactorialMomentsTask.cxx +++ b/PWGCF/EbyEFluctuations/Tasks/FactorialMomentsTask.cxx @@ -8,8 +8,9 @@ // 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 This task is for Normalized Factorial Moments Analysis: PRC 85, 044914 (2012), nucl-ex:1411.6083 + +/// \file FactorialMomentsTask.cxx +/// \brief This task is for Normalized Factorial Moments Analysis: Hwa and Yang, Phys. Rev. C 85, 044914 (2012) /// \author Salman Malik /// \author Balwan Singh @@ -29,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -36,94 +38,69 @@ #include #include #include -#include #include #include #include -#include #include #include -#include +#include #include #include using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; + struct FactorialMomentsTask { + Configurable useDcaCut{"useDcaCut", true, "Enable DCA cut"}; + Configurable usePVContributor{"usePVContributor", true, + "Require the track to contribute to the primary-vertex fit (Run 3)"}; + Configurable useTPCChi2{"useTPCChi2", true, "Enable TPC chi2 cut"}; + Configurable useTPCCluster{"useTPCCluster", false, "Select tracks with TPCCluster"}; Configurable useITS{"useITS", false, "Select tracks with ITS"}; Configurable useTPC{"useTPC", false, "Select tracks with TPC"}; Configurable useGlobal{"useGlobal", true, "Select global tracks"}; Configurable applyCheckPtForRec{"applyCheckPtForRec", false, "Apply checkpT for reconstructed tracks"}; Configurable applyCheckPtForMC{"applyCheckPtForMC", true, "Apply checkpT for MC-generated tracks"}; - Configurable cfgEvSelkNoITSROFrameBorder{"cfgEvSelkNoITSROFrameBorder", true, "ITSROFrame border event selection cut"}; - Configurable cfgEvSelkNoTimeFrameBorder{"cfgEvSelkNoTimeFrameBorder", true, "TimeFrame border event selection cut"}; + Configurable smearPhi{"smearPhi", false, "Randomise track phi with a Gaussian of sigma=2pi before filling the eta-phi lattices"}; + Configurable evSelNoITSROFrameBorder{"evSelNoITSROFrameBorder", true, "ITSROFrame border event selection cut"}; + Configurable evSelNoTimeFrameBorder{"evSelNoTimeFrameBorder", true, "TimeFrame border event selection cut"}; Configurable centralEta{"centralEta", 0.9, "eta limit for tracks"}; Configurable numPt{"numPt", 5, "number of pT bins"}; Configurable ptMin{"ptMin", 0.2f, "lower pT cut"}; Configurable dcaXY{"dcaXY", 0.1f, "DCA xy cut"}; Configurable dcaZ{"dcaZ", 1.0f, "DCA z cut"}; - Configurable cfgCutTpcChi2NCl{"cfgCutTpcChi2NCl", 2.5f, "Maximum TPCchi2NCl"}; - Configurable cfgCutItsChi2NCl{"cfgCutItsChi2NCl", 40.0f, "Maximum ITSchi2NCl"}; - Configurable mintPCCls{"mintPCCls", 70.0f, "minimum number of TPC clusters"}; + Configurable maxTPCChi2NCl{"maxTPCChi2NCl", 4.0f, "Maximum TPCchi2NCl"}; + Configurable maxITSChi2NCl{"maxITSChi2NCl", 40.0f, "Maximum ITSchi2NCl"}; + Configurable minTPCNCls{"minTPCNCls", 70.0f, "minimum number of TPC clusters"}; Configurable> centLimits{"centLimits", {0, 5}, "centrality min and max"}; Configurable> vertexXYZ{"vertexXYZ", {0.3f, 0.4f, 10.0f}, "vertex cuts"}; Configurable> ptCuts{"ptCuts", {0.2f, 2.0f}, "pT cuts"}; - Configurable isApplySameBunchPileup{"isApplySameBunchPileup", true, "Enable SameBunchPileup cut"}; - Configurable isApplyGoodZvtxFT0vsPV{"isApplyGoodZvtxFT0vsPV", true, "Enable GoodZvtxFT0vsPV cut"}; - Configurable cfgUseGoodITSLayerAllCut{"cfgUseGoodITSLayerAllCut", true, "Remove time interval with dead ITS zone"}; - Configurable isApplyVertexITSTPC{"isApplyVertexITSTPC", true, "Enable VertexITSTPC cut"}; - Configurable isApplyVertexTOFmatched{"isApplyVertexTOFmatched", true, "Enable VertexTOFmatched cut"}; - Configurable isApplyVertexTRDmatched{"isApplyVertexTRDmatched", true, "Enable VertexTRDmatched cut"}; - Configurable isApplyExtraCorrCut{"isApplyExtraCorrCut", false, "Enable extra NPVtracks vs FTOC correlation cut"}; - Configurable isApplyExtraPhiCut{"isApplyExtraPhiCut", false, "Enable extra phi cut"}; + Configurable evSelNoSameBunchPileup{"evSelNoSameBunchPileup", true, "Enable SameBunchPileup cut"}; + Configurable evSelGoodZvtxFT0vsPV{"evSelGoodZvtxFT0vsPV", true, "Enable GoodZvtxFT0vsPV cut"}; + Configurable evSelGoodITSLayersAll{"evSelGoodITSLayersAll", true, "Remove time interval with dead ITS zone"}; + Configurable evSelVertexITSTPC{"evSelVertexITSTPC", true, "Enable VertexITSTPC cut"}; + Configurable evSelVertexTOFmatched{"evSelVertexTOFmatched", true, "Enable VertexTOFmatched cut"}; + Configurable evSelVertexTRDmatched{"evSelVertexTRDmatched", true, "Enable VertexTRDmatched cut"}; + Configurable applyExtraCorrCut{"applyExtraCorrCut", false, "Enable extra NPVtracks vs FTOC correlation cut"}; + Configurable applyExtraPhiCut{"applyExtraPhiCut", false, "Enable extra phi cut"}; Configurable includeGlobalTracks{"includeGlobalTracks", false, "Enable Global Tracks"}; Configurable includeTPCTracks{"includeTPCTracks", false, "TPC Tracks"}; Configurable includeITSTracks{"includeITSTracks", false, "ITS Tracks"}; - Configurable samplesize{"samplesize", 100, "Sample size"}; + Configurable nSlots{"nSlots", 1250, "Number of hash slots for merge-safe subsampling (slot = splitmix64(globalBC) % nSlots); must be identical for every job"}; Configurable useMC{"useMC", false, "Use MC information"}; - Configurable useGlobalTrack{"useGlobalTrack", true, "Require global track in filter"}; - Configurable cfgITScluster{"cfgITScluster", 6, "Minimum Number of ITS cluster"}; - Configurable cfgTPCcluster{"cfgTPCcluster", 80, "Minimum Number of TPC cluster"}; - Configurable cfgTPCnCrossedRows{"cfgTPCnCrossedRows", 70, "Minimum Number of TPC crossed-rows"}; - Configurable cfgTPCnCrossedRowsOverFindableCls{"cfgTPCnCrossedRowsOverFindableCls", 0.8, "Minimum ratio of crossed rows over findable clusters TPC"}; + Configurable useCrossedRows{"useCrossedRows", true, "Require minimum number of TPC crossed rows"}; + Configurable minITSClusters{"minITSClusters", 6, "Minimum Number of ITS cluster"}; + Configurable minTPCClusters{"minTPCClusters", 70, "Minimum Number of TPC cluster"}; + Configurable minTPCCrossedRows{"minTPCCrossedRows", 70, "Minimum Number of TPC crossed-rows"}; + Configurable minTPCCrossedRowsOverFindableCls{"minTPCCrossedRowsOverFindableCls", 0.8, "Minimum ratio of crossed rows over findable clusters TPC"}; Configurable reduceOutput{"reduceOutput", 0, "Suppress info level output (0 = all output, 1 = per collision, 2 = none)"}; Filter filterTracks = (nabs(aod::track::eta) < centralEta) && (aod::track::pt >= ptMin); Filter filterCollisions = (nabs(aod::collision::posZ) < vertexXYZ.value[2]) && (nabs(aod::collision::posX) < vertexXYZ.value[0]) && (nabs(aod::collision::posY) < vertexXYZ.value[1]); Service pdg{}; // Histograms - HistogramRegistry histos1{ - "histos1", - { - {"h3DVtxZetaPhi", "VtxZ vs #eta vs #phi;VtxZ;#eta;#phi", {HistType::kTH3F, {{20, -10, 10}, {16, -0.8, 0.8}, {100, 0., o2::constants::math::TwoPI}}}}, - {"hRecoPtBefore", "Reco pT before cuts;pt (GeV/c);Counts", {HistType::kTH1F, {{1000, 0.0, 20.0}}}}, - {"hGenPtBefore", "Gen pT before cuts;pt (GeV/c);Counts", {HistType::kTH1F, {{1000, 0.0, 20.0}}}}, - {"hRecoPtAfter", "Reco pT after cuts;pt (GeV/c);Counts", {HistType::kTH1F, {{1000, 0.0, 20.0}}}}, - {"hGenPtAfter", "Gen pT after cuts;pt (GeV/c);Counts", {HistType::kTH1F, {{1000, 0.0, 20.0}}}}, - {"hRecoEtaBefore", "Reco #eta before cuts;#eta;Counts", {HistType::kTH1F, {{200, -2.0, 2.0}}}}, - {"mCentMCImpactFull", "Impact Parameter (MC All Events);Impact Parameter b [fm];Counts", {HistType::kTH1F, {{200, 0.0, 50.0}}}}, - {"mCentMCImpact005", "Impact Parameter (MC 0–5%);Impact Parameter b [fm];Counts", {HistType::kTH1F, {{200, 0.0, 20.0}}}}, - {"hGenEtaBefore", "Gen #eta before cuts;#eta;Counts", {HistType::kTH1F, {{200, -2.0, 2.0}}}}, - {"hRecoEtaAfter", "Reco #eta after cuts;#eta;Counts", {HistType::kTH1F, {{200, -2.0, 2.0}}}}, - {"hGenEtaAfter", "Gen #eta after cuts;#eta;Counts", {HistType::kTH1F, {{200, -2.0, 2.0}}}}, - {"hRecoPhiBefore", "Reco #phi before cuts;#phi;Counts", {HistType::kTH1F, {{100, 0, 6.3}}}}, - {"hGenPhiBefore", "Gen #phi before cuts;#phi;Counts", {HistType::kTH1F, {{100, 0, 6.3}}}}, - {"hRecoPhiAfter", "Reco #phi after cuts;#phi;Counts", {HistType::kTH1F, {{100, 0, 6.3}}}}, - {"hGenCharge", "Gen particle charge;Charge;Counts", {HistType::kTH1F, {{13, -6.5, 6.5}}}}, - {"hGenPhiAfter", "Gen #phi after cuts;#phi;Counts", {HistType::kTH1F, {{100, 0, 6.3}}}}, - {"mChargeDist", "MC particle charge;Charge;Counts", {HistType::kTH1F, {{9, -4.5, 4.5}}}}, - {"mDiffPhi", "dphi between selected MC particles;dphi;Counts", {HistType::kTH1F, {{2000, -0.01, 0.01}}}}, - {"mPrimariesPerEvent", "Primary MC particles per event;N_{primaries};Counts", {HistType::kTH1I, {{20000, 0, 20000}}}}, - {"mDiffPt", "dpt between selected MC particles;dpt (GeV/c);Counts", {HistType::kTH1F, {{2000, -0.01, 0.01}}}}, - {"mDiffEta", "deta between selected MC particles;deta;Counts", {HistType::kTH1F, {{2000, -0.01, 0.01}}}}, - {"mADiffPhi", "dphi between selected MC particles;dphi;Counts", {HistType::kTH1F, {{2000, -0.01, 0.01}}}}, - {"mADiffPt", "dpt between selected MC particles;dpt (GeV/c);Counts", {HistType::kTH1F, {{2000, -0.01, 0.01}}}}, - {"mADiffEta", "deta between selected MC particles;deta;Counts", {HistType::kTH1F, {{2000, -0.01, 0.01}}}}, - }, - }; - HistogramRegistry histos{ "histos", { @@ -132,7 +109,7 @@ struct FactorialMomentsTask { {"mChargeBefore", "Charge before MC cuts;charge;entries", {HistType::kTH1F, {{7, -3.5, 3.5}}}}, {"mChargeAfter", "Charge after MC cuts;charge;entries", {HistType::kTH1F, {{7, -3.5, 3.5}}}}, {"mCollID", "collisionID", {HistType::kTH1I, {{1000, -10000, 10000}}}}, - {"mCentFT0M", "centFT0C_0A", {HistType::kTH1F, {{100, 0, 100}}}}, + {"mCentFT0M", "centFT0M / Run2 V0M", {HistType::kTH1F, {{100, 0, 100}}}}, {"mCentFV0A", "centFV0A", {HistType::kTH1F, {{100, 0, 100}}}}, {"mCentFT0A", "centFT0A", {HistType::kTH1F, {{100, 0, 100}}}}, {"mCentFT0C", "centFT0C", {HistType::kTH1F, {{100, 0, 100}}}}, @@ -142,7 +119,6 @@ struct FactorialMomentsTask { {"mEta", "#eta", {HistType::kTH1F, {{1000, -2, 2}}}}, {"mPt", "#pt", {HistType::kTH1F, {{1000, -0.01, 50}}}}, {"mPhi", "#phi", {HistType::kTH1F, {{100, 0, o2::constants::math::TwoPI}}}}, - {"mEvents", "events", {HistType::kTH1D, {{5, -0.5, 4.5}}}}, {"mNFindableClsTPC", "findable TPC clusters;findable clusters", {HistType::kTH1F, {{100, 0, 200}}}}, {"mNClsTPC", "number of clusters TPC; nClusters TPC", {HistType::kTH1F, {{100, 0, 200}}}}, {"mNClsITS", "number of clusters ITS; nClusters ITS", {HistType::kTH1F, {{100, 0, 10}}}}, @@ -150,10 +126,8 @@ struct FactorialMomentsTask { {"mChi2ITS", "chi2 ITS", {HistType::kTH1F, {{100, 0, 10}}}}, {"mChi2TRD", "chi2 TRD", {HistType::kTH1F, {{100, 0, 100}}}}, {"mDCAxy", "DCA xy", {HistType::kTH1F, {{500, -0.8, 0.8}}}}, - {"mDCAx", "DCA z", {HistType::kTH1F, {{500, -2.0, 2.0}}}}, + {"mDCAz", "DCA z", {HistType::kTH1F, {{500, -2.0, 2.0}}}}, {"mDCAxyPt", "DCA xy vs #pt;#pt;DCAxy", {HistType::kTH2F, {{100, 0, 20}, {500, -0.5, 0.5}}}}, - {"mDCAxyPtbcut", "DCA xy vs #pt;#pt;DCAxycut", {HistType::kTH2F, {{100, 0, 20}, {500, -0.5, 0.5}}}}, - {"mDCAzPtbcut", "DCA z vs #pt;#pt;DCAzcut", {HistType::kTH2F, {{100, 0, 20}, {100, -2.0, 2.0}}}}, {"mDCAzPt", "DCA z vs #pt;#pt;DCAz", {HistType::kTH2F, {{100, 0, 20}, {100, -2.0, 2.0}}}}, {"mNSharedClsTPC", "shared clusters in TPC", {HistType::kTH1F, {{100, 0, 10}}}}, {"mCrossedRowsTPC", "crossedrows in TPC", {HistType::kTH1F, {{100, 0, 200}}}}, @@ -165,88 +139,121 @@ struct FactorialMomentsTask { OutputObjHandlingPolicy::AnalysisObject, true}; float collisionZ = 0.f; - double dcaxyMaxTrackPar0 = 0.0105; - double dcaxyMaxTrackPar1 = 0.035; - double dcaxyMaxTrackPar2 = 1.1; + static constexpr double DcaXY0 = 0.0105; + static constexpr double DcaXY1 = 0.035; + static constexpr double DcaXY2 = 1.1; static const int nBins = 52; - double kMinCharge = 1e-6; + static const int nCentLim = 2; + static const int nPtVal = 2; + static const int nVertexXYZ = 3; + static constexpr int NPt = 5; + static constexpr double MinCharge = 1e-6; + static constexpr int MaxSlots = 1000000; static const int nfqOrder = 6; - int countSamples = 0; - int testc1 = 0, testc2 = 0, testc3 = 0; + static constexpr int EvAll = 1; + static constexpr int EvSel8 = 2; + static constexpr int EvITSROFrame = 3; + static constexpr int EvTimeFrame = 4; + static constexpr int EvSameBunch = 5; + static constexpr int EvGoodITS = 6; + static constexpr int EvGoodZvtx = 7; + static constexpr int EvVertexITSTPC = 8; + static constexpr int EvCent = 9; + static constexpr int EvAccepted = 10; + static constexpr int NEventSteps = 10; std::array binningM{}; - std::array countTracks{0, 0, 0, 0, 0}; - std::array, 5>, 6> fqEvent{}; - std::array, 5>, 6> fqEventSampled{}; + std::array countTracks{}; + std::array, 5>, nfqOrder> fqEvent{}; std::array, 5> binConEvent{}; - std::array, 5>, 6> binConEventSampled{}; - std::array, 5>, nfqOrder> nSubsamples{}; - std::array, 5>, nfqOrder> tmpFqErr{}; std::vector> mHistArrReset; - std::vector> mHistArrQA; + std::vector> mHistEtaQA; + std::vector> mHistPtQA; + std::vector> mHistPhiQA; + std::vector> mHistMultQA; std::vector> mHistArrEff; std::vector> mFqBinFinal; std::vector> mBinConFinal; - std::vector> mFqBinFinalSampled; - std::vector> mBinConFinalSampled; - std::vector> mFqError; + // merge-safe slot output: additive per-slot sums, hadd-exact + std::shared_ptr mSlotNev; + std::vector> mSlotFq; + std::vector> mSlotOcc; + uint64_t mEventGlobalBC = 0; void init(o2::framework::InitContext&) { - // NOTE: check to make number of pt and the vector consistent - if (numPt != static_cast(ptCuts.value.size()) / 2) { - for (int i = numPt; i < static_cast(ptCuts.value.size() / 2); i++) { - ptCuts.value[2 * i] = 0; - ptCuts.value[2 * i + 1] = 0; + if (numPt.value < 1 || numPt.value > NPt) { + LOG(fatal) << "numPt must be between 1 and " << NPt << ", got " << numPt.value; + } + if (static_cast(ptCuts.value.size()) < nPtVal * numPt.value) { + LOG(fatal) << "ptCuts needs " << nPtVal * numPt.value << " values for numPt=" << numPt.value + << ", got " << ptCuts.value.size(); + } + if (nSlots.value < 1 || nSlots.value > MaxSlots) { + LOG(fatal) << "nSlots must be in [1, " << MaxSlots << "], got " << nSlots.value; + } + if (centLimits.value.size() != nCentLim || vertexXYZ.value.size() != nVertexXYZ) { + LOG(fatal) << "centLimits needs " << nCentLim << " and vertexXYZ needs " << nVertexXYZ << " values"; + } + if (applyCheckPtForRec.value == applyCheckPtForMC.value) { + LOG(fatal) << "exactly one of applyCheckPtForRec / applyCheckPtForMC must be enabled"; + } + TString cfg = Form("FMtask centLimits=%d,%d numPt=%d centralEta=%g ptMin=%g dcaXY=%g dcaZ=%g useMC=%d nfqOrder=%d smearPhi=%d nSlots=%d", + centLimits.value[0], centLimits.value[1], numPt.value, centralEta.value, + ptMin.value, dcaXY.value, dcaZ.value, useMC.value ? 1 : 0, nfqOrder, smearPhi.value ? 1 : 0, nSlots.value); + cfg += Form(" vertexXYZ=%g,%g,%g", vertexXYZ.value[0], vertexXYZ.value[1], vertexXYZ.value[2]); + cfg += " ptCuts="; + for (int i = 0; i < static_cast(ptCuts.value.size()); i++) { + cfg += Form("%s%g", i > 0 ? "," : "", ptCuts.value[i]); + } + cfg += Form(" useDcaCut=%d usePVContributor=%d useTPCChi2=%d useTPCCluster=%d useCrossedRows=%d", + useDcaCut.value ? 1 : 0, usePVContributor.value ? 1 : 0, useTPCChi2.value ? 1 : 0, + useTPCCluster.value ? 1 : 0, useCrossedRows.value ? 1 : 0); + cfg += Form(" useITS=%d useTPC=%d useGlobal=%d maxTPCChi2NCl=%g minTPCClusters=%d minTPCCrossedRows=%d", + useITS.value ? 1 : 0, useTPC.value ? 1 : 0, useGlobal.value ? 1 : 0, + maxTPCChi2NCl.value, minTPCClusters.value, minTPCCrossedRows.value); + histos.add("metaConfig", cfg.Data(), HistType::kTH1D, {{1, 0.5, 1.5}}); + auto mEventSelected = std::get>(histos.add("mEventSelected", "eventSelected", HistType::kTH1D, {{NEventSteps, 0.5, NEventSteps + 0.5}})); + const std::array evLabels = {"all", "sel8", "kNoITSROFrameBorder", "kNoTimeFrameBorder", "kNoSameBunchPileup", "kIsGoodITSLayersAll", "kIsGoodZvtxFT0vsPV", "kIsVertexITSTPC", "centrality", "accepted"}; + for (int iStep = 0; iStep < NEventSteps; ++iStep) { + mEventSelected->GetXaxis()->SetBinLabel(iStep + 1, evLabels[iStep]); + } + // merge-safe slot output: additive per-slot sums, hadd-exact (see slotpool.C) + const AxisSpec slotAxis{nSlots.value, -0.5, nSlots.value - 0.5, ""}; + const AxisSpec slotMAxis{nBins, -0.5, nBins - 0.5, ""}; + mSlotNev = std::get>(histos.add("slots/nEv", "events per slot;slot;events", HistType::kTH1F, {slotAxis})); + // bin1 = merged jobs, bin2 = sum of their nSlots: mismatch witness (hadd keeps the first axis silently) + auto slotParams = std::get>(histos.add("slots/params", "slot campaign parameters;jobs;entries", HistType::kTH1D, {{2, 0.5, 2.5}})); + slotParams->SetBinContent(1, 1.0); + slotParams->SetBinContent(2, static_cast(nSlots.value)); + for (int iPt = 0; iPt < numPt; ++iPt) { + mSlotOcc.push_back(std::get>(histos.add(Form("slots/sumOcc_pt%d", iPt + 1), + Form("sum of occupancy per slot for bin %.2f-%.2f;slot;M", ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), + HistType::kTH2F, {slotAxis, slotMAxis}))); + for (int iq = 0; iq < nfqOrder; ++iq) { + mSlotFq.push_back(std::get>(histos.add(Form("slots/sumFq_pt%d_o%d", iPt + 1, iq + 2), + Form("sum of f_%d per slot for bin %.2f-%.2f;slot;M", iq + 2, ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), + HistType::kTH2F, {slotAxis, slotMAxis}))); } } - std::array axisPt = {AxisSpec{100, -0.01, 3 * ptCuts.value[1], ""}, AxisSpec{100, -0.01, 3 * ptCuts.value[3], ""}, AxisSpec{100, -0.01, 3 * ptCuts.value[5], ""}, AxisSpec{100, -0.01, 3 * ptCuts.value[7], ""}, AxisSpec{100, -0.01, 3 * ptCuts.value[9], ""}}; // pT axis - auto mEventSelected = std::get>(histos.add("mEventSelected", "eventSelected", HistType::kTH1D, {{7, 0.5, 8.5}})); - mEventSelected->GetXaxis()->SetBinLabel(0, "all"); - mEventSelected->GetXaxis()->SetBinLabel(1, "sel8"); - mEventSelected->GetXaxis()->SetBinLabel(2, "kNoITSROFrameBorder"); - mEventSelected->GetXaxis()->SetBinLabel(3, "kNoTimeFrameBorder"); - mEventSelected->GetXaxis()->SetBinLabel(4, "sameBunchPileup"); - mEventSelected->GetXaxis()->SetBinLabel(5, "kIsGoodITSLayersAll"); - mEventSelected->GetXaxis()->SetBinLabel(6, "kIsGoodZvtxFT0vsPV"); - mEventSelected->GetXaxis()->SetBinLabel(7, "FTOC"); - auto mTrackSelected = std::get>(histos.add( - "mTrackSelected", "Track Selection Steps", HistType::kTH1D, {{5, 0.5, 5.5}})); - mTrackSelected->GetXaxis()->SetBinLabel(1, "all"); - mTrackSelected->GetXaxis()->SetBinLabel(2, "charge"); - mTrackSelected->GetXaxis()->SetBinLabel(3, "PIDs"); - mTrackSelected->GetXaxis()->SetBinLabel(4, "Pids exclude pions"); - mTrackSelected->GetXaxis()->SetBinLabel(5, "Pids exclude pions + kions"); - if (useMC) { - auto mMcTrackSelected = std::get>(histos.add("mMcTrackSelected", "mcTrackSelection", HistType::kTH1D, {{5, 0.5, 5.5}})); - } for (int iM = 0; iM < nBins; ++iM) { binningM[iM] = 2 * (iM + 2); } for (int iPt = 0; iPt < numPt; ++iPt) { + const AxisSpec ptAxis{100, -0.01, 3 * ptCuts.value[2 * iPt + 1], ""}; mHistArrEff.push_back(std::get>(histos.add(Form("bin%i/m3DVtxZetaPhi", iPt + 1), Form("#eta #phi #vtxz for bin %.2f-%.2f;vz;#eta;#phi", ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH3F, {{20, -10, 10}, {16, -0.8, +0.8}, {100, 0., o2::constants::math::TwoPI}}))); - mHistArrQA.push_back(std::get>(histos.add(Form("bin%i/mEta", iPt + 1), Form("#eta for bin %.2f-%.2f;#eta", ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{1000, -2, 2}}))); - mHistArrQA.push_back(std::get>(histos.add(Form("bin%i/mPt", iPt + 1), Form("pT for bin %.2f-%.2f;pT", ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {axisPt[iPt]}))); - mHistArrQA.push_back(std::get>(histos.add(Form("bin%i/mPhi", iPt + 1), Form("#phi for bin %.2f-%.2f;#phi", ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{1000, 0, o2::constants::math::TwoPI}}))); - mHistArrQA.push_back(std::get>(histos.add(Form("bin%i/mMultiplicity", iPt + 1), Form("Multiplicity for bin %.2f-%.2f;Multiplicity", ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{1000, 0, 15000}}))); + mHistEtaQA.push_back(std::get>(histos.add(Form("bin%i/mEta", iPt + 1), Form("#eta for bin %.2f-%.2f;#eta", ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{1000, -2, 2}}))); + mHistPtQA.push_back(std::get>(histos.add(Form("bin%i/mPt", iPt + 1), Form("pT for bin %.2f-%.2f;pT", ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {ptAxis}))); + mHistPhiQA.push_back(std::get>(histos.add(Form("bin%i/mPhi", iPt + 1), Form("#phi for bin %.2f-%.2f;#phi", ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{1000, 0, o2::constants::math::TwoPI}}))); + mHistMultQA.push_back(std::get>(histos.add(Form("bin%i/mMultiplicity", iPt + 1), Form("Multiplicity for bin %.2f-%.2f;Multiplicity", ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{1000, 0, 15000}}))); for (int iM = 0; iM < nBins; ++iM) { auto mHistsR = std::get>(histos.add(Form("bin%i/Reset/mEtaPhi%i", iPt + 1, iM), Form("#eta#phi_%i for bin %.2f-%.2f;#eta;#phi", iM, ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH2F, {{binningM[iM], -0.8, 0.8}, {binningM[iM], 0, o2::constants::math::TwoPI}})); mHistArrReset.push_back(mHistsR); - for (int iq = 0; iq < nfqOrder; ++iq) { - tmpFqErr[iq][iPt][iM] = new TH1D(Form("tmpFqErr%i%i%i", iq, iPt, iM), Form("tmpFqErr%i%i%i", iq, iPt, iM), 100, 0, 10); - } } for (int i = 0; i < nfqOrder; ++i) { auto mHistFq = std::get>(histos.add(Form("mFinalFq%i_bin%i", i + 2, iPt + 1), Form("Final F_%i for bin %.2f-%.2f;M", i + 2, ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{nBins, -0.5, nBins - 0.5}})); mFqBinFinal.push_back(mHistFq); auto mHistAv = std::get>(histos.add(Form("mFinalAvBin%i_bin%i", i + 2, iPt + 1), Form("Final AvBin_%i for bin %.2f-%.2f;M", i + 2, ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{nBins, -0.5, nBins - 0.5}})); mBinConFinal.push_back(mHistAv); - auto mHistFqSampled = std::get>(histos.add(Form("mFinalFq%iSampled_bin%i", i + 2, iPt + 1), Form("Final F_%i for bin %.2f-%.2f;M", i + 2, ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{nBins, -0.5, nBins - 0.5}})); - mFqBinFinalSampled.push_back(mHistFqSampled); - auto mHistAvSampled = std::get>(histos.add(Form("mFinalAvBin%iSampled_bin%i", i + 2, iPt + 1), Form("Final AvBin_%i for bin %.2f-%.2f;M", i + 2, ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{nBins, -0.5, nBins - 0.5}})); - mBinConFinalSampled.push_back(mHistAvSampled); - - auto mHistError = std::get>(histos.add(Form("mFqError%i_bin%i", i + 2, iPt + 1), Form("Error for F_%i for bin %.2f-%.2f;M", i + 2, ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{nBins, -0.5, nBins - 0.5}})); - mFqError.push_back(mHistError); } } } @@ -256,12 +263,14 @@ struct FactorialMomentsTask { for (auto iPt = 0; iPt < numPt; ++iPt) { if (track.pt() > ptCuts.value[2 * iPt] && track.pt() < ptCuts.value[2 * iPt + 1]) { float iphi = track.phi(); - iphi = gRandom->Gaus(iphi, o2::constants::math::TwoPI); - iphi = RecoDecay::constrainAngle(iphi, 0.); + if (smearPhi) { + iphi = gRandom->Gaus(iphi, o2::constants::math::TwoPI); + iphi = RecoDecay::constrainAngle(iphi, 0.); + } mHistArrEff[iPt]->Fill(collisionZ, track.eta(), iphi); - mHistArrQA[iPt * 4]->Fill(track.eta()); - mHistArrQA[iPt * 4 + 1]->Fill(track.pt()); - mHistArrQA[iPt * 4 + 2]->Fill(iphi); + mHistEtaQA[iPt]->Fill(track.eta()); + mHistPtQA[iPt]->Fill(track.pt()); + mHistPhiQA[iPt]->Fill(iphi); countTracks[iPt]++; for (auto iM = 0; iM < nBins; ++iM) { mHistArrReset[iPt * nBins + iM]->Fill(track.eta(), iphi); @@ -269,106 +278,254 @@ struct FactorialMomentsTask { } } } - void calculateMoments(std::vector> hist) + // splitmix64: deterministic stateless hash of the global BC + static uint64_t slotOf(uint64_t x) + { + x += 0x9e3779b97f4a7c15ULL; + x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ULL; + x = (x ^ (x >> 27)) * 0x94d049bb133111ebULL; + return x ^ (x >> 31); + } + // additive fold of the current accepted event into its slot (never Fill with weights: no fSumw2, hadd-exact) + void accumulateSlot() + { + const int slot = static_cast(slotOf(mEventGlobalBC) % static_cast(nSlots.value)); + mSlotNev->SetBinContent(slot + 1, mSlotNev->GetBinContent(slot + 1) + 1.); + for (int iPt = 0; iPt < numPt; ++iPt) { + auto* occ = mSlotOcc[iPt].get(); + for (int iM = 0; iM < nBins; ++iM) { + occ->SetBinContent(slot + 1, iM + 1, occ->GetBinContent(slot + 1, iM + 1) + binConEvent[iPt][iM]); + for (int iq = 0; iq < nfqOrder; ++iq) { + auto* fq = mSlotFq[iPt * nfqOrder + iq].get(); + fq->SetBinContent(slot + 1, iM + 1, fq->GetBinContent(slot + 1, iM + 1) + fqEvent[iq][iPt][iM]); + } + } + } + } + void calculateMoments(const std::vector>& hist) { - double binContent = 0; - countSamples++; - bool compSample = kFALSE; - if (countSamples == samplesize) { - compSample = kTRUE; - countSamples = 0; - } - // Calculate the normalized factorial moments + // normalized factorial moments per accepted event, per pT bin and M for (int iPt = 0; iPt < numPt; ++iPt) { for (int iM = 0; iM < nBins; ++iM) { - binContent = 0; - std::array sumfqBin{0.}; + double binContent = 0; + std::array sumfqBin{0.}; + const double m2 = static_cast(binningM[iM]) * static_cast(binningM[iM]); + const auto& histM = hist[iPt * nBins + iM]; - for (int iEta = 1; iEta <= hist[iPt * nBins + iM]->GetNbinsX(); ++iEta) { - for (int iPhi = 1; iPhi <= hist[iPt * nBins + iM]->GetNbinsY(); ++iPhi) { - double binconVal = 0; - binconVal = hist[iPt * nBins + iM]->GetBinContent(iEta, iPhi); + for (int iEta = 1; iEta <= histM->GetNbinsX(); ++iEta) { + for (int iPhi = 1; iPhi <= histM->GetNbinsY(); ++iPhi) { + const double binconVal = histM->GetBinContent(iEta, iPhi); binContent += binconVal; for (int iq = 0; iq < nfqOrder; ++iq) { double fqBin = 0; if (binconVal >= iq + 2) { - fqBin = TMath::Factorial(static_cast(binconVal)) / (TMath::Factorial(static_cast(binconVal) - (iq + 2))); - } - if (std::isnan(fqBin)) { - break; + const int nOcc = static_cast(binconVal); + fqBin = 1.0; + for (int k = 0; k < iq + 2; ++k) { + fqBin *= (nOcc - k); + } } sumfqBin[iq] += fqBin; } } } - binConEvent[iPt][iM] = binContent / (std::pow(binningM[iM], 2)); + binConEvent[iPt][iM] = binContent / m2; for (int iq = 0; iq < nfqOrder; ++iq) { - // Always accumulate raw fq numerator and binCon unconditionally so - // both sums run over exactly the same set of events. - fqEvent[iq][iPt][iM] = sumfqBin[iq] / (std::pow(binningM[iM], 2)); - fqEventSampled[iq][iPt][iM] += fqEvent[iq][iPt][iM]; - binConEventSampled[iq][iPt][iM] += binConEvent[iPt][iM]; - mFqBinFinal[iPt * 6 + iq]->Fill(iM, fqEvent[iq][iPt][iM]); - mBinConFinal[iPt * 6 + iq]->Fill(iM, binConEvent[iPt][iM]); - if (compSample) { - const double avM = binConEventSampled[iq][iPt][iM] / samplesize; - mBinConFinalSampled[iPt * 6 + iq]->Fill(iM, avM); - const double den = std::pow(avM, iq + 2); - const double fqM = ((fqEventSampled[iq][iPt][iM]) / samplesize); + fqEvent[iq][iPt][iM] = sumfqBin[iq] / m2; + mFqBinFinal[iPt * nfqOrder + iq]->Fill(iM, fqEvent[iq][iPt][iM]); + mBinConFinal[iPt * nfqOrder + iq]->Fill(iM, binConEvent[iPt][iM]); + } + } + } + accumulateSlot(); + } - const double tmp = (den > 0.) ? fqM / den : 0.; - mFqBinFinalSampled[iPt * 6 + iq]->Fill(iM, tmp); + template + bool passDcaCut(const T& track) const + { + return std::fabs(track.dcaXY()) < (DcaXY0 + DcaXY1 / std::pow(track.pt(), DcaXY2)); + } - tmpFqErr[iq][iPt][iM]->Fill(tmp); - ++nSubsamples[iq][iPt][iM]; - const double stdErr = (nSubsamples[iq][iPt][iM] > 1) - ? tmpFqErr[iq][iPt][iM]->GetStdDev() / std::sqrt(static_cast(nSubsamples[iq][iPt][iM])) - : 0.; - mFqError[iPt * 6 + iq]->SetBinContent(iM + 1, stdErr); - fqEventSampled[iq][iPt][iM] = 0; - binConEventSampled[iq][iPt][iM] = 0; - } - } // end of loop over order of F - } // end of loop over M bins - } // end of loop over pT bins + template + void fillTrackQA(const T& track) + { + histos.fill(HIST("mCollID"), track.collisionId()); + histos.fill(HIST("mEta"), track.eta()); + histos.fill(HIST("mPt"), track.pt()); + histos.fill(HIST("mPhi"), track.phi()); + histos.fill(HIST("mNFindableClsTPC"), track.tpcNClsFindable()); + histos.fill(HIST("mNClsTPC"), track.tpcNClsFound()); + histos.fill(HIST("mNClsITS"), track.itsNCls()); + histos.fill(HIST("mChi2TPC"), track.tpcChi2NCl()); + histos.fill(HIST("mChi2ITS"), track.itsChi2NCl()); + histos.fill(HIST("mChi2TRD"), track.trdChi2()); + histos.fill(HIST("mDCAxy"), track.dcaXY()); + histos.fill(HIST("mDCAz"), track.dcaZ()); + histos.fill(HIST("mtpcsignalvspt"), track.pt(), track.tpcSignal()); + histos.fill(HIST("mDCAxyPt"), track.pt(), track.dcaXY()); + histos.fill(HIST("mDCAzPt"), track.pt(), track.dcaZ()); + histos.fill(HIST("mNSharedClsTPC"), track.tpcNClsShared()); + histos.fill(HIST("mCrossedRowsTPC"), track.tpcNClsCrossedRows()); + histos.fill(HIST("mNFinClsminusCRows"), track.tpcNClsFindableMinusCrossedRows()); + histos.fill(HIST("mNFractionShClsTPC"), track.tpcFractionSharedCls()); + histos.fill(HIST("mSharedClsvsPt"), track.pt(), track.tpcNClsShared()); + const double crossedRows = track.tpcNClsCrossedRows(); + histos.fill(HIST("mSharedClsProbvsPt"), track.pt(), crossedRows > 0. ? track.tpcFractionSharedCls() / crossedRows : 0.); } - // write a template that takes coll and tracks from processRun3 and processMCRec - using TracksFMs = soa::Filtered>; - Preslice perMcCollision = aod::mcparticle::mcCollisionId; - void processRun3(soa::Filtered>::iterator const& coll, TracksFMs const& tracks) + template + void fillMcQA(const T& mcParts) { - // selection of events - histos.fill(HIST("mEventSelected"), 0); + for (auto const& mc : mcParts) { + int pdgCode = mc.pdgCode(); + auto pdgInfo = pdg->GetParticle(pdgCode); + if (!pdgInfo) { + continue; + } + double charge = pdgInfo->Charge(); + double physCharge = charge / 3.0; + histos.fill(HIST("mChargeBefore"), physCharge); + if (mc.isPhysicalPrimary() && std::abs(mc.eta()) < centralEta && std::abs(physCharge) >= MinCharge) { + histos.fill(HIST("mChargeAfter"), physCharge); + histos.fill(HIST("mEta"), mc.eta()); + histos.fill(HIST("mPt"), mc.pt()); + histos.fill(HIST("mPhi"), mc.phi()); + if (applyCheckPtForMC && !applyCheckPtForRec) { + checkpT(mc); + } + } + } + } + + template + bool selectEventRun3(const T& coll) + { + histos.fill(HIST("mEventSelected"), EvAll); if (!coll.sel8()) { - return; + return false; } - if (cfgEvSelkNoITSROFrameBorder && !(coll.selection_bit(o2::aod::evsel::kNoITSROFrameBorder))) { - return; + histos.fill(HIST("mEventSelected"), EvSel8); + if (evSelNoITSROFrameBorder && !(coll.selection_bit(o2::aod::evsel::kNoITSROFrameBorder))) { + return false; + } + histos.fill(HIST("mEventSelected"), EvITSROFrame); + if (evSelNoTimeFrameBorder && !(coll.selection_bit(o2::aod::evsel::kNoTimeFrameBorder))) { + return false; + } + histos.fill(HIST("mEventSelected"), EvTimeFrame); + if (evSelNoSameBunchPileup && !coll.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return false; + } + histos.fill(HIST("mEventSelected"), EvSameBunch); + if (evSelGoodITSLayersAll && !(coll.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll))) { + return false; + } + histos.fill(HIST("mEventSelected"), EvGoodITS); + if (evSelGoodZvtxFT0vsPV && !coll.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + histos.fill(HIST("mEventSelected"), EvGoodZvtx); + if (coll.centFT0C() < centLimits.value[0] || coll.centFT0C() > centLimits.value[1]) { + return false; } + collisionZ = coll.posZ(); + histos.fill(HIST("mEventSelected"), EvCent); + histos.fill(HIST("mEventSelected"), EvAccepted); + return true; + } - histos.fill(HIST("mEventSelected"), 2); - if (cfgEvSelkNoTimeFrameBorder && !(coll.selection_bit(o2::aod::evsel::kNoTimeFrameBorder))) { - return; + template + bool selectEventMCRec(const T& coll) + { + histos.fill(HIST("mEventSelected"), EvAll); + if (!coll.sel8()) { + return false; } - histos.fill(HIST("mEventSelected"), 3); - if (isApplySameBunchPileup && !coll.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { - return; + histos.fill(HIST("mEventSelected"), EvSel8); + if (evSelNoSameBunchPileup && !coll.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return false; } - histos.fill(HIST("mEventSelected"), 4); - if (cfgUseGoodITSLayerAllCut && !(coll.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll))) { - return; + histos.fill(HIST("mEventSelected"), EvSameBunch); + if (evSelGoodZvtxFT0vsPV && !coll.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; } - histos.fill(HIST("mEventSelected"), 5); - if (isApplyGoodZvtxFT0vsPV && !coll.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { - return; + histos.fill(HIST("mEventSelected"), EvGoodZvtx); + if (evSelVertexITSTPC && !coll.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + return false; } - histos.fill(HIST("mEventSelected"), 6); + histos.fill(HIST("mEventSelected"), EvVertexITSTPC); if (coll.centFT0C() < centLimits.value[0] || coll.centFT0C() > centLimits.value[1]) { - return; + return false; } collisionZ = coll.posZ(); - histos.fill(HIST("mEventSelected"), 7); + histos.fill(HIST("mEventSelected"), EvCent); + histos.fill(HIST("mEventSelected"), EvAccepted); + return true; + } + + template + bool selectTrack(const T& track) + { + if (useITS && !track.hasITS()) { + return false; + } + if (useTPC && !track.hasTPC()) { + return false; + } + if (useGlobal && !track.isGlobalTrack()) { + return false; + } + if (useCrossedRows && track.tpcNClsCrossedRows() < minTPCCrossedRows) { + return false; + } + if (useDcaCut && !passDcaCut(track)) { + return false; + } + if (usePVContributor && !track.isPVContributor()) { + return false; + } + if (useTPCChi2 && track.tpcChi2NCl() > maxTPCChi2NCl) { + return false; + } + if (useTPCCluster && track.tpcNClsFound() < minTPCClusters) { + return false; + } + return true; + } + + void beginEvent() + { + for (int iPt = 0; iPt < numPt; ++iPt) { + if (countTracks[iPt] == 0) { + continue; + } + for (int iM = 0; iM < nBins; ++iM) { + mHistArrReset[iPt * nBins + iM]->Reset(); + } + } + countTracks.fill(0); + fqEvent = {}; + binConEvent = {}; + } + + void endEvent() + { + for (int iPt = 0; iPt < numPt; ++iPt) { + if (countTracks[iPt] > 0) { + mHistMultQA[iPt]->Fill(countTracks[iPt]); + } + } + calculateMoments(mHistArrReset); + } + + using TracksFMs = soa::Filtered>; + Preslice perMcCollision = aod::mcparticle::mcCollisionId; + void processRun3(soa::Filtered>::iterator const& coll, TracksFMs const& tracks, aod::BCs const&) + { + mEventGlobalBC = coll.bc_as().globalBC(); + if (!selectEventRun3(coll)) { + return; + } histos.fill(HIST("mVertexX"), coll.posX()); histos.fill(HIST("mVertexY"), coll.posY()); histos.fill(HIST("mVertexZ"), coll.posZ()); @@ -376,159 +533,45 @@ struct FactorialMomentsTask { histos.fill(HIST("mCentFV0A"), coll.centFV0A()); histos.fill(HIST("mCentFT0A"), coll.centFT0A()); histos.fill(HIST("mCentFT0C"), coll.centFT0C()); - for (auto const& h : mHistArrReset) { - h->Reset(); - } - countTracks = {0, 0, 0, 0, 0}; - fqEvent = {{{{{0, 0, 0, 0, 0, 0}}}}}; - binConEvent = {{{0, 0, 0, 0, 0}}}; + beginEvent(); for (auto const& track : tracks) { - if (useITS && !track.hasITS()) { - continue; - } - if (useTPC && !track.hasTPC()) { - continue; - } - if (useGlobal && !track.isGlobalTrack()) { + if (!selectTrack(track)) { continue; } - - if (std::fabs(track.dcaXY()) < (dcaxyMaxTrackPar0 + dcaxyMaxTrackPar1 / std::pow(track.pt(), dcaxyMaxTrackPar2))) { - histos.fill(HIST("mCollID"), track.collisionId()); - histos.fill(HIST("mEta"), track.eta()); - histos.fill(HIST("mPt"), track.pt()); - histos.fill(HIST("mPhi"), track.phi()); - histos.fill(HIST("mNFindableClsTPC"), track.tpcNClsFindable()); - histos.fill(HIST("mNClsTPC"), track.tpcNClsFound()); - histos.fill(HIST("mNClsITS"), track.itsNCls()); - histos.fill(HIST("mChi2TPC"), track.tpcChi2NCl()); - histos.fill(HIST("mChi2ITS"), track.itsChi2NCl()); - histos.fill(HIST("mChi2TRD"), track.trdChi2()); - histos.fill(HIST("mDCAxy"), track.dcaXY()); - histos.fill(HIST("mtpcsignalvspt"), track.pt(), track.tpcSignal()); - histos.fill(HIST("mDCAxyPt"), track.pt(), track.dcaXY()); - histos.fill(HIST("mDCAzPt"), track.pt(), track.dcaZ()); - histos.fill(HIST("mDCAzPt"), track.pt(), track.dcaZ()); - histos.fill(HIST("mNSharedClsTPC"), track.tpcNClsShared()); - histos.fill(HIST("mCrossedRowsTPC"), track.tpcNClsCrossedRows()); - histos.fill(HIST("mNFinClsminusCRows"), track.tpcNClsFindableMinusCrossedRows()); - histos.fill(HIST("mNFractionShClsTPC"), track.tpcFractionSharedCls()); - histos.fill(HIST("mSharedClsvsPt"), track.pt(), track.tpcNClsShared()); - histos.fill(HIST("mSharedClsProbvsPt"), track.pt(), track.tpcFractionSharedCls() / track.tpcNClsCrossedRows()); - checkpT(track); - } - } - for (int iPt = 0; iPt < numPt; ++iPt) { - if (countTracks[iPt] > 0) { - mHistArrQA[iPt * 4 + 3]->Fill(countTracks[iPt]); - } + fillTrackQA(track); + checkpT(track); } - calculateMoments(mHistArrReset); + endEvent(); } PROCESS_SWITCH(FactorialMomentsTask, processRun3, "main process function", false); using CollisionCandidateMCRec = soa::Join; using TracksMc = soa::Filtered>; - void processMCRec(soa::Filtered::iterator const& coll, TracksMc const& colltracks, aod::McParticles const& mcParticles, aod::McCollisions const&) + void processMCRec(soa::Filtered::iterator const& coll, TracksMc const& colltracks, aod::McParticles const& mcParticles, aod::McCollisions const&, aod::BCs const&) { + mEventGlobalBC = coll.bc_as().globalBC(); if (!coll.has_mcCollision()) { return; } - histos.fill(HIST("mEventSelected"), 0); - if (!coll.sel8()) { - return; - } - histos.fill(HIST("mEventSelected"), 1); - if (isApplySameBunchPileup && !coll.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + if (!selectEventMCRec(coll)) { return; } - histos.fill(HIST("mEventSelected"), 2); - if (isApplyGoodZvtxFT0vsPV && !coll.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { - return; - } - histos.fill(HIST("mEventSelected"), 3); - if (isApplyVertexITSTPC && !coll.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { - return; - } - histos.fill(HIST("mEventSelected"), 4); - if (coll.centFT0C() < centLimits.value[0] || coll.centFT0C() > centLimits.value[1]) { - return; - } - - histos.fill(HIST("mEventSelected"), 5); histos.fill(HIST("mVertexX"), coll.posX()); histos.fill(HIST("mVertexY"), coll.posY()); histos.fill(HIST("mVertexZ"), coll.posZ()); histos.fill(HIST("mCentFT0C"), coll.centFT0C()); - for (auto const& h : mHistArrReset) { - h->Reset(); - } - countTracks = {0, 0, 0, 0, 0}; - fqEvent = {{{{{0, 0, 0, 0, 0, 0}}}}}; - binConEvent = {{{0, 0, 0, 0, 0}}}; + beginEvent(); for (auto const& track : colltracks) { - if (useITS && !track.hasITS()) { - continue; - } - if (useTPC && !track.hasTPC()) { - continue; - } - if (useGlobal && !track.isGlobalTrack()) { + if (!selectTrack(track)) { continue; } - if (std::fabs(track.dcaXY()) < (dcaxyMaxTrackPar0 + dcaxyMaxTrackPar1 / std::pow(track.pt(), dcaxyMaxTrackPar2))) { - histos.fill(HIST("mCollID"), track.collisionId()); - histos.fill(HIST("mEta"), track.eta()); - histos.fill(HIST("mPt"), track.pt()); - histos.fill(HIST("mPhi"), track.phi()); - histos.fill(HIST("mNFindableClsTPC"), track.tpcNClsFindable()); - histos.fill(HIST("mNClsTPC"), track.tpcNClsFound()); - histos.fill(HIST("mNClsITS"), track.itsNCls()); - histos.fill(HIST("mChi2TPC"), track.tpcChi2NCl()); - histos.fill(HIST("mChi2ITS"), track.itsChi2NCl()); - histos.fill(HIST("mChi2TRD"), track.trdChi2()); - histos.fill(HIST("mDCAxy"), track.dcaXY()); - histos.fill(HIST("mDCAx"), track.dcaZ()); - histos.fill(HIST("mtpcsignalvspt"), track.pt(), track.tpcSignal()); - histos.fill(HIST("mDCAxyPt"), track.pt(), track.dcaXY()); - histos.fill(HIST("mDCAzPt"), track.pt(), track.dcaZ()); - histos.fill(HIST("mNSharedClsTPC"), track.tpcNClsShared()); - histos.fill(HIST("mCrossedRowsTPC"), track.tpcNClsCrossedRows()); - histos.fill(HIST("mNFinClsminusCRows"), track.tpcNClsFindableMinusCrossedRows()); - histos.fill(HIST("mNFractionShClsTPC"), track.tpcFractionSharedCls()); - histos.fill(HIST("mSharedClsvsPt"), track.pt(), track.tpcNClsShared()); - histos.fill(HIST("mSharedClsProbvsPt"), track.pt(), track.tpcFractionSharedCls() / track.tpcNClsCrossedRows()); - if (applyCheckPtForRec && !applyCheckPtForMC) { - checkpT(track); - } + fillTrackQA(track); + if (applyCheckPtForRec && !applyCheckPtForMC) { + checkpT(track); } } auto mcParts = mcParticles.sliceBy(perMcCollision, coll.mcCollision().globalIndex()); - for (auto const& mc : mcParts) { - int pdgCode = mc.pdgCode(); - auto pdgInfo = pdg->GetParticle(pdgCode); - if (!pdgInfo) { - continue; - } - double charge = pdgInfo->Charge(); - double physCharge = charge / 3.0; - histos.fill(HIST("mChargeBefore"), physCharge); - if (mc.isPhysicalPrimary() && std::abs(mc.eta()) < centralEta && std::abs(physCharge) >= kMinCharge) { - histos.fill(HIST("mChargeAfter"), physCharge); - histos.fill(HIST("mEta"), mc.eta()); - histos.fill(HIST("mPt"), mc.pt()); - histos.fill(HIST("mPhi"), mc.phi()); - if (applyCheckPtForMC && !applyCheckPtForRec) { - checkpT(mc); - } - } - } - for (auto iPt = 0; iPt < numPt; ++iPt) { - if (countTracks[iPt] > 0) { - mHistArrQA[iPt * 4 + 3]->Fill(countTracks[iPt]); - } - } - histos.fill(HIST("mEventSelected"), 6); - calculateMoments(mHistArrReset); + fillMcQA(mcParts); + endEvent(); } PROCESS_SWITCH(FactorialMomentsTask, processMCRec, "main process function", false); using EventSelectionrun2 = soa::Join; @@ -543,132 +586,64 @@ struct FactorialMomentsTask { BCsWithRun2Info const&) { auto bc = coll.bc_as(); + mEventGlobalBC = bc.globalBC(); if (!(static_cast(bc.eventCuts() & BIT(aod::Run2EventCuts::kAliEventCutsAccepted)))) { return; } if (coll.centRun2V0M() < centLimits.value[0] || coll.centRun2V0M() > centLimits.value[1]) { return; } + collisionZ = coll.posZ(); histos.fill(HIST("mVertexX"), coll.posX()); histos.fill(HIST("mVertexY"), coll.posY()); histos.fill(HIST("mVertexZ"), coll.posZ()); histos.fill(HIST("mCentFT0M"), coll.centRun2V0M()); - for (auto const& h : mHistArrReset) { - h->Reset(); - } - countTracks = {0, 0, 0, 0, 0}; - fqEvent = {{{{{0, 0, 0, 0, 0, 0}}}}}; - binConEvent = {{{0, 0, 0, 0, 0}}}; + beginEvent(); for (auto const& track : tracks) { double recoCharge = (track.sign() != 0) ? track.sign() : 0.; - if (std::abs(track.eta()) < centralEta && track.isGlobalTrack() && std::abs(recoCharge) >= kMinCharge) { - histos.fill(HIST("mCollID"), track.collisionId()); - histos.fill(HIST("mNFindableClsTPC"), track.tpcNClsFindable()); - histos.fill(HIST("mNClsTPC"), track.tpcNClsFound()); - histos.fill(HIST("mNClsITS"), track.itsNCls()); - histos.fill(HIST("mChi2TPC"), track.tpcChi2NCl()); - histos.fill(HIST("mChi2ITS"), track.itsChi2NCl()); - histos.fill(HIST("mChi2TRD"), track.trdChi2()); - histos.fill(HIST("mNSharedClsTPC"), track.tpcNClsShared()); - histos.fill(HIST("mCrossedRowsTPC"), track.tpcNClsCrossedRows()); - histos.fill(HIST("mNFinClsminusCRows"), track.tpcNClsFindableMinusCrossedRows()); - histos.fill(HIST("mNFractionShClsTPC"), track.tpcFractionSharedCls()); - histos.fill(HIST("mSharedClsvsPt"), track.pt(), track.tpcNClsShared()); - histos.fill(HIST("mSharedClsProbvsPt"), track.pt(), track.tpcFractionSharedCls() / track.tpcNClsCrossedRows()); + if (std::abs(track.eta()) < centralEta && track.isGlobalTrack() && std::abs(recoCharge) >= MinCharge) { + fillTrackQA(track); if (applyCheckPtForRec && !applyCheckPtForMC) { checkpT(track); } } } auto mcParts = mcParticles.sliceBy(perMcCollision, coll.mcCollision().globalIndex()); - for (auto const& mc : mcParts) { - int pdgCode = mc.pdgCode(); - auto pdgInfo = pdg->GetParticle(pdgCode); - if (!pdgInfo) { - continue; - } - double charge = pdgInfo->Charge(); - double physCharge = charge / 3.0; - histos.fill(HIST("mChargeBefore"), physCharge); - if (mc.isPhysicalPrimary() && std::abs(mc.eta()) < centralEta && std::abs(physCharge) >= kMinCharge) { - histos.fill(HIST("mChargeAfter"), physCharge); - histos.fill(HIST("mEta"), mc.eta()); - histos.fill(HIST("mPt"), mc.pt()); - histos.fill(HIST("mPhi"), mc.phi()); - if (applyCheckPtForMC && !applyCheckPtForRec) { - checkpT(mc); - } - } - } - - for (int iPt = 0; iPt < numPt; ++iPt) { - if (countTracks[iPt] > 0) { - mHistArrQA[iPt * 4 + 3]->Fill(countTracks[iPt]); - } - } - - calculateMoments(mHistArrReset); + fillMcQA(mcParts); + endEvent(); } - PROCESS_SWITCH(FactorialMomentsTask, processMcRun2, "process MC Run2", true); - void processRun2(soa::Filtered>::iterator const& coll, TracksFMs const& tracks) + PROCESS_SWITCH(FactorialMomentsTask, processMcRun2, "process MC Run2", false); + void processRun2(soa::Filtered>::iterator const& coll, TracksFMs const& tracks, aod::BCs const&) { + mEventGlobalBC = coll.bc_as().globalBC(); if ((!coll.alias_bit(kINT7)) || (!coll.sel7())) { return; } if (coll.centRun2V0M() < centLimits.value[0] || coll.centRun2V0M() > centLimits.value[1]) { return; } + collisionZ = coll.posZ(); histos.fill(HIST("mVertexX"), coll.posX()); histos.fill(HIST("mVertexY"), coll.posY()); histos.fill(HIST("mVertexZ"), coll.posZ()); histos.fill(HIST("mCentFT0M"), coll.centRun2V0M()); - - for (auto const& h : mHistArrReset) { - h->Reset(); - } - countTracks = {0, 0, 0, 0, 0}; - fqEvent = {{{{{0, 0, 0, 0, 0, 0}}}}}; - binConEvent = {{{0, 0, 0, 0, 0}}}; + beginEvent(); for (auto const& track : tracks) { - if ((track.pt() < ptMin) || (!track.isGlobalTrack()) || (track.tpcNClsFindable() < mintPCCls)) { + if ((track.pt() < ptMin) || (!track.isGlobalTrack()) || (track.tpcNClsFindable() < minTPCNCls)) { continue; } - histos.fill(HIST("mCollID"), track.collisionId()); - histos.fill(HIST("mEta"), track.eta()); - histos.fill(HIST("mPt"), track.pt()); - histos.fill(HIST("mPhi"), track.phi()); - histos.fill(HIST("mNFindableClsTPC"), track.tpcNClsFindable()); - histos.fill(HIST("mNClsTPC"), track.tpcNClsFound()); - histos.fill(HIST("mNClsITS"), track.itsNCls()); - histos.fill(HIST("mChi2TPC"), track.tpcChi2NCl()); - histos.fill(HIST("mChi2ITS"), track.itsChi2NCl()); - histos.fill(HIST("mChi2TRD"), track.trdChi2()); - histos.fill(HIST("mDCAxy"), track.dcaXY()); - histos.fill(HIST("mDCAx"), track.dcaZ()); - histos.fill(HIST("mDCAxyPt"), track.pt(), track.dcaXY()); - histos.fill(HIST("mDCAzPt"), track.pt(), track.dcaZ()); - histos.fill(HIST("mNSharedClsTPC"), track.tpcNClsShared()); - histos.fill(HIST("mCrossedRowsTPC"), track.tpcNClsCrossedRows()); - histos.fill(HIST("mNFinClsminusCRows"), track.tpcNClsFindableMinusCrossedRows()); - histos.fill(HIST("mNFractionShClsTPC"), track.tpcFractionSharedCls()); - histos.fill(HIST("mSharedClsvsPt"), track.pt(), track.tpcNClsShared()); - histos.fill(HIST("mSharedClsProbvsPt"), track.pt(), track.tpcFractionSharedCls() / track.tpcNClsCrossedRows()); + fillTrackQA(track); checkpT(track); } - for (int iPt = 0; iPt < numPt; ++iPt) { - if (countTracks[iPt] > 0) { - mHistArrQA[iPt * 4 + 3]->Fill(countTracks[iPt]); - } - } - // Calculate the normalized factorial moments - calculateMoments(mHistArrReset); + endEvent(); } PROCESS_SWITCH(FactorialMomentsTask, processRun2, "for RUN2", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) + +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc), + adaptAnalysisTask(context), }; } diff --git a/PWGCF/EbyEFluctuations/Tasks/NetProtonCumulants.cxx b/PWGCF/EbyEFluctuations/Tasks/NetProtonCumulants.cxx index 68f4b4bc7a8..4f7c7de7d4c 100644 --- a/PWGCF/EbyEFluctuations/Tasks/NetProtonCumulants.cxx +++ b/PWGCF/EbyEFluctuations/Tasks/NetProtonCumulants.cxx @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -69,7 +70,7 @@ struct NetProtonCumulants_Table_QA { // Filter command*********** Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; - Filter trackFilter = (nabs(aod::track::eta) < 0.8f) && (aod::track::pt > cfgCutPtLower) && (aod::track::pt < 5.0f) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t) true)) && (aod::track::tpcChi2NCl < cfgCutTpcChi2NCl); + Filter trackFilter = (nabs(aod::track::eta) < 0.8f) && (aod::track::pt > cfgCutPtLower) && (aod::track::pt < 5.0f) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (aod::track::tpcChi2NCl < cfgCutTpcChi2NCl); // Connect to ccdb Service ccdb; diff --git a/PWGCF/EbyEFluctuations/Tasks/meanPtFlucId.cxx b/PWGCF/EbyEFluctuations/Tasks/meanPtFlucId.cxx index 7f4432ae05e..9af2293e4e3 100644 --- a/PWGCF/EbyEFluctuations/Tasks/meanPtFlucId.cxx +++ b/PWGCF/EbyEFluctuations/Tasks/meanPtFlucId.cxx @@ -61,46 +61,71 @@ struct MeanPtFlucId { Configurable nPBins{"nPBins", 300, ""}; Configurable nPhiBins{"nPhiBins", 100, ""}; Configurable nPtBins{"nPtBins", 50, "Number of pT bins"}; - Configurable cfgCutPtMax{"cfgCutPtMax", 2.0, "maximum pT"}; - Configurable cfgCutPtMin{"cfgCutPtMin", 0.2, "minimum pT"}; - Configurable cfgCutEta{"cfgCutEta", 0.8, "Eta cut"}; - Configurable cfgDcaPtDepOffset{"cfgDcaPtDepOffset", 0.004, "Offset of the pT-dependent DCA cut (cm)"}; - Configurable cfgDcaPtDepScale{"cfgDcaPtDepScale", 0.013, "1/pT coefficient of the pT-dependent DCA cut (cm GeV/c)"}; - Configurable cfgCutPosZ{"cfgCutPosZ", 7.0, "cut for vertex Z"}; - Configurable cfgPosZ{"cfgPosZ", true, "Position Z"}; - Configurable cfgCutNSig2{"cfgCutNSig2", 2.0, "nSigma cut: 2"}; - Configurable cfgCutNSig3{"cfgCutNSig3", 3.0, "nSigma cut: 3"}; - Configurable cfgCutNSig5{"cfgCutNSig5", 5.0, "nSigma cut: 5"}; - Configurable cfgCutNSigTpcTof{"cfgCutNSigTpcTof", 3.0, "nSigma TPC cut when TPC + TOF "}; - Configurable cfgSelCutNSigTpcPi{"cfgSelCutNSigTpcPi", 2.0, "nSigma TPC cut for pion selection"}; - Configurable cfgSelCutNSigTpcKa{"cfgSelCutNSigTpcKa", 2.0, "nSigma TPC cut for kaon selection"}; - Configurable cfgSelCutNSigTpcPr{"cfgSelCutNSigTpcPr", 2.0, "nSigma TPC cut for proton selection"}; - Configurable cfgSelCutNSigTofPi{"cfgSelCutNSigTofPi", 2.0, "nSigma TOF cut for pion selection"}; - Configurable cfgSelCutNSigTofKa{"cfgSelCutNSigTofKa", 2.0, "nSigma TOF cut for kaon selection"}; - Configurable cfgSelCutNSigTofPr{"cfgSelCutNSigTofPr", 2.0, "nSigma TOF cut for proton selection"}; - Configurable cfgCutPiPtMin{"cfgCutPiPtMin", 0.2, "Minimum pion p_{T} cut"}; - Configurable cfgCutKaPtMin{"cfgCutKaPtMin", 0.3, "Minimum kaon p_{T} cut"}; - Configurable cfgCutPrPtMin{"cfgCutPrPtMin", 0.5, "Minimum proton p_{T} cut"}; - Configurable cfgCutPiThrsldP{"cfgCutPiThrsldP", 0.7, "Momentum threshold for requiring pion TOF PID"}; - Configurable cfgCutKaThrsldP{"cfgCutKaThrsldP", 0.8, "Momentum threshold for requiring kaon TOF PID"}; - Configurable cfgCutPrThrsldP{"cfgCutPrThrsldP", 0.8, "Momentum threshold for requiring proton TOF PID"}; - Configurable cfgP0U{"cfgP0U", 6.36269, "p_{0} for upper cut for N_{TPC} vs N{sim}"}; - Configurable cfgP1U{"cfgP1U", 2.22108, "p_{1} for upper cut for N_{TPC} vs N{sim}"}; - Configurable cfgP2U{"cfgP2U", -0.0118451, "p_{2} for upper cut for N_{TPC} vs N{sim}"}; - Configurable cfgP0L{"cfgP0L", 23.8213, "p_{0} for lower cut for N_{TPC} vs N{sim}"}; - Configurable cfgP1L{"cfgP1L", -0.402328, "p_{1} for lower cut for N_{TPC} vs N{sim}"}; - Configurable cfgP2L{"cfgP2L", 0.0128423, "p_{2} for lower cut for N_{TPC} vs N{sim}"}; + struct : ConfigurableGroup { + Configurable cfgCutPtMax{"cfgCutPtMax", 2.0, "maximum pT"}; + Configurable cfgCutPtMin{"cfgCutPtMin", 0.2, "minimum pT"}; + Configurable cfgCutEta{"cfgCutEta", 0.8, "Eta cut"}; + Configurable cfgCutITSChi2NCl{"cfgCutITSChi2NCl", 36.0, "ITS Chi2/NCl cut"}; + Configurable cfgCutTPCChi2NCl{"cfgCutTPCChi2NCl", 4.0, "TPC Chi2/NCl cut"}; + Configurable cfgCutTPCNCls{"cfgCutTPCNCls", 70.0, "TPC NCls cut"}; + Configurable cfgCutDCAxy{"cfgCutDCAxy", 0.1, "DCAxy cut"}; + Configurable cfgCutDCAz{"cfgCutDCAz", 0.2, "DCAz cut"}; + Configurable cfgDcaPtDepOffset{"cfgDcaPtDepOffset", 0.004, "Offset of the pT-dependent DCA cut (cm)"}; + Configurable cfgDcaPtDepScale{"cfgDcaPtDepScale", 0.013, "1/pT coefficient of the pT-dependent DCA cut (cm GeV/c)"}; + Configurable cfgCutNCrossedRowsTPC{"cfgCutNCrossedRowsTPC", 70, "Minimum number of crossed TPC rows"}; + Configurable cfgGlobalTrack{"cfgGlobalTrack", true, "Require the standard global-track selection (includes its DCA cuts)"}; + Configurable cfgGlobalTrackWoDCA{"cfgGlobalTrackWoDCA", false, "Require global-track quality without built-in DCA cuts; disable cfgGlobalTrack to use only this selection"}; + Configurable cfgDcaXYPtDep{"cfgDcaXYPtDep", true, "Apply the pT-dependent absolute DCAxy cut; takes precedence over cfgDcaXY"}; + Configurable cfgNTPCCrossedRows{"cfgNTPCCrossedRows", false, "Apply the minimum number of crossed TPC rows"}; + Configurable cfgTPCChi2NCl{"cfgTPCChi2NCl", false, "Chi2 per N_{TPC} Clusters Cut "}; + Configurable cfgITSChi2NCl{"cfgITSChi2NCl", false, "Chi2 per N_{ITS} Clusters Cut "}; + Configurable cfgDcaXY{"cfgDcaXY", false, "DCA XY Cut "}; + Configurable cfgDcaZ{"cfgDcaZ", false, "DCA Z Cut "}; + Configurable cfgDcaZPtDep{"cfgDcaZPtDep", false, "DCA Z pT dependent Cut "}; + } trackCuts; + struct : ConfigurableGroup { + Configurable cfgCutPosZ{"cfgCutPosZ", 7.0, "cut for vertex Z"}; + Configurable cfgP0U{"cfgP0U", 6.36269, "p_{0} for upper cut for N_{TPC} vs N{sim}"}; + Configurable cfgP1U{"cfgP1U", 2.22108, "p_{1} for upper cut for N_{TPC} vs N{sim}"}; + Configurable cfgP2U{"cfgP2U", -0.0118451, "p_{2} for upper cut for N_{TPC} vs N{sim}"}; + Configurable cfgP0L{"cfgP0L", 23.8213, "p_{0} for lower cut for N_{TPC} vs N{sim}"}; + Configurable cfgP1L{"cfgP1L", -0.402328, "p_{1} for lower cut for N_{TPC} vs N{sim}"}; + Configurable cfgP2L{"cfgP2L", 0.0128423, "p_{2} for lower cut for N_{TPC} vs N{sim}"}; + Configurable cfgSel8{"cfgSel8", true, "Sel8 trigger"}; + Configurable cfgPosZ{"cfgPosZ", true, "Position Z"}; + Configurable cfgCentFT0C{"cfgCentFT0C", false, "Use FT0C for centrality instead of FT0M"}; + Configurable cfgCentFV0A{"cfgCentFV0A", false, "Use FV0A for centrality instead of FT0M"}; + Configurable cfgNoSameBunchPileup{"cfgNoSameBunchPileup", true, "kNoSameBunchPileup"}; + Configurable cfgIsVertexITSTPC{"cfgIsVertexITSTPC", true, "kIsVertexITSTPC"}; + Configurable cfgLightIonCuts{"cfgLightIonCuts", false, "Light Ion Cuts"}; + Configurable cfgIsGoodITSLayersAll{"cfgIsGoodITSLayersAll", true, "kIsGoodITSLayersAll"}; + Configurable cfgIsGoodZvtxFT0vsPV{"cfgIsGoodZvtxFT0vsPV", true, "kIsGoodZvtxFT0vsPV"}; + Configurable cfgNtpcEventCut{"cfgNtpcEventCut", false, "N_{TPC} Event Cut "}; + } eventCuts; + struct : ConfigurableGroup { + Configurable cfgCutNSig3{"cfgCutNSig3", 3.0, "nSigma cut: 3"}; + Configurable cfgCutNSig5{"cfgCutNSig5", 5.0, "nSigma cut: 5"}; + Configurable cfgCutNSigTpcTof{"cfgCutNSigTpcTof", 3.0, "nSigma TPC cut when TPC + TOF "}; + Configurable cfgSelCutNSigTpcPi{"cfgSelCutNSigTpcPi", 3.0, "nSigma TPC cut for pion selection"}; + Configurable cfgSelCutNSigTpcKa{"cfgSelCutNSigTpcKa", 3.0, "nSigma TPC cut for kaon selection"}; + Configurable cfgSelCutNSigTpcPr{"cfgSelCutNSigTpcPr", 3.0, "nSigma TPC cut for proton selection"}; + Configurable cfgSelCutNSigTofPi{"cfgSelCutNSigTofPi", 3.0, "nSigma TOF cut for pion selection"}; + Configurable cfgSelCutNSigTofKa{"cfgSelCutNSigTofKa", 3.0, "nSigma TOF cut for kaon selection"}; + Configurable cfgSelCutNSigTofPr{"cfgSelCutNSigTofPr", 3.0, "nSigma TOF cut for proton selection"}; + Configurable cfgTofShiftPi{"cfgTofShiftPi", 0.f, "Pion TOF nSigma offset to subtract (not a time shift in ps)"}; + Configurable cfgTofShiftKa{"cfgTofShiftKa", 0.f, "Kaon TOF nSigma offset to subtract (not a time shift in ps)"}; + Configurable cfgTofShiftPr{"cfgTofShiftPr", 0.f, "Proton TOF nSigma offset to subtract (not a time shift in ps)"}; + Configurable cfgCutPiPtMin{"cfgCutPiPtMin", 0.2, "Minimum pion p_{T} cut"}; + Configurable cfgCutKaPtMin{"cfgCutKaPtMin", 0.3, "Minimum kaon p_{T} cut"}; + Configurable cfgCutPrPtMin{"cfgCutPrPtMin", 0.5, "Minimum proton p_{T} cut"}; + Configurable cfgCutPiThrsldP{"cfgCutPiThrsldP", 0.7, "Momentum threshold for requiring pion TOF PID"}; + Configurable cfgCutKaThrsldP{"cfgCutKaThrsldP", 0.55, "Momentum threshold for requiring kaon TOF PID"}; + Configurable cfgCutPrThrsldP{"cfgCutPrThrsldP", 0.8, "Momentum threshold for requiring proton TOF PID"}; + Configurable cfgRejTrk{"cfgRejTrk", true, "Rejected Tracks"}; + } pidConfig; Configurable cfgP0corr{"cfgP0corr", 0.0001, "p_{0} for corrected N_{TPC} "}; Configurable cfgP1corr{"cfgP1corr", 0.0001, "p_{1} for corrected N_{TPC} "}; Configurable cfgP2corr{"cfgP2corr", 0.0001, "p_{2} for corrected N_{TPC} "}; - Configurable cfgSel8{"cfgSel8", true, "Sel8 trigger"}; - Configurable cfgNoSameBunchPileup{"cfgNoSameBunchPileup", true, "kNoSameBunchPileup"}; - Configurable cfgIsVertexITSTPC{"cfgIsVertexITSTPC", true, "kIsVertexITSTPC"}; - Configurable cfgLightIonCuts{"cfgLightIonCuts", false, "Light Ion Cuts"}; - Configurable cfgIsGoodITSLayersAll{"cfgIsGoodITSLayersAll", true, "kIsGoodITSLayersAll"}; - Configurable cfgIsGoodZvtxFT0vsPV{"cfgIsGoodZvtxFT0vsPV", true, "kIsGoodZvtxFT0vsPV"}; - Configurable cfgRejTrk{"cfgRejTrk", true, "Rejected Tracks"}; - Configurable cfgNtpcEventCut{"cfgNtpcEventCut", true, "N_{TPC} Event Cut for reco"}; Configurable cfgApplyEfficiencyCorrection{"cfgApplyEfficiencyCorrection", false, "Apply measured track-by-track efficiency weights"}; Configurable cfgUseCombinedPurityEfficiencyWeight{"cfgUseCombinedPurityEfficiencyWeight", false, "Use the legacy Ngen/Nselected weight instead of the efficiency-only 1/epsilon weight"}; Configurable cfgEfficiencyCCDBUrl{"cfgEfficiencyCCDBUrl", "http://ccdb-test.cern.ch:8080", "Efficiency CCDB URL"}; @@ -117,6 +142,16 @@ struct MeanPtFlucId { Configurable> ptBins{"ptBins", {0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00, 1.05, 1.10, 1.15, 1.20, 1.25, 1.30, 1.35, 1.40, 1.45, 1.50, 1.55, 1.60, 1.65, 1.70, 1.75, 1.80, 1.85, 1.90, 1.95, 2.00}, "p_{T} bins"}; Configurable> etaBins{"etaBins", {-0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8}, "#eta bins"}; + struct : ConfigurableGroup { + Configurable cfgGenCutPosZ{"cfgGenCutPosZ", false, "Apply a generated collision vertex-Z cut in generator-only mode"}; + Configurable cfgGenMaxPosZ{"cfgGenMaxPosZ", 7.f, "Maximum absolute generated vertex Z (cm)"}; + Configurable cfgGenCentralityCalibration{"cfgGenCentralityCalibration", false, "Collect forward multiplicity only; derive percentile lookup before analysis"}; + Configurable cfgGenForwardMultMax{"cfgGenForwardMultMax", 10000, "Largest integer forward multiplicity in calibration histogram"}; + Configurable> cfgGenCentralityLookup{"cfgGenCentralityLookup", {}, "Percentile indexed by integer generated forward multiplicity (fixed reference sample)"}; + Configurable cfgGenCentralityEstimator{"cfgGenCentralityEstimator", "", "Estimator of the supplied lookup: FT0M, FT0C or FV0A"}; + Configurable cfgGenUseEventWeight{"cfgGenUseEventWeight", false, "Use positive mcCollision.weight() consistently for generator calibration, events and matrices"}; + } genConfig; + Service pdg{}; Service ccdb{}; @@ -167,10 +202,14 @@ struct MeanPtFlucId { Gen_Charged, Gen_Pion, Gen_Kaon, - Gen_Proton + Gen_Proton, + Model_Charged, + Model_Pion, + Model_Kaon, + Model_Proton }; - static constexpr std::array Directories{ + static constexpr std::array Directories{ "QA/after/", "QA/Pion/", "QA/Kaon/", @@ -182,7 +221,11 @@ struct MeanPtFlucId { "Gen/Charged/", "Gen/Pion/", "Gen/Kaon/", - "Gen/Proton/"}; + "Gen/Proton/", + "Model/Charged/", + "Model/Pion/", + "Model/Kaon/", + "Model/Proton/"}; static constexpr auto const& dire = Directories; enum CorrectedMode { @@ -240,16 +283,20 @@ struct MeanPtFlucId { void init(InitContext const&) { + if (!std::isfinite(pidConfig.cfgTofShiftPi.value) || !std::isfinite(pidConfig.cfgTofShiftKa.value) || !std::isfinite(pidConfig.cfgTofShiftPr.value)) { + LOGF(fatal, "TOF nSigma shifts must be finite: pion=%g, kaon=%g, proton=%g", + pidConfig.cfgTofShiftPi.value, pidConfig.cfgTofShiftKa.value, pidConfig.cfgTofShiftPr.value); + } - if (nPtBins <= 0 || cfgCutPtMin >= cfgCutPtMax || cfgCutPiPtMin >= cfgCutPtMax || - cfgCutKaPtMin >= cfgCutPtMax || cfgCutPrPtMin >= cfgCutPtMax) { + if (nPtBins <= 0 || trackCuts.cfgCutPtMin >= trackCuts.cfgCutPtMax || pidConfig.cfgCutPiPtMin >= trackCuts.cfgCutPtMax || + pidConfig.cfgCutKaPtMin >= trackCuts.cfgCutPtMax || pidConfig.cfgCutPrPtMin >= trackCuts.cfgCutPtMax) { LOGF(fatal, "Invalid pT matrix binning: nPtBins=%d, max=%g, minima=(%g,%g,%g,%g)", - static_cast(nPtBins), static_cast(cfgCutPtMax), - static_cast(cfgCutPtMin), static_cast(cfgCutPiPtMin), - static_cast(cfgCutKaPtMin), static_cast(cfgCutPrPtMin)); + static_cast(nPtBins), static_cast(trackCuts.cfgCutPtMax), + static_cast(trackCuts.cfgCutPtMin), static_cast(pidConfig.cfgCutPiPtMin), + static_cast(pidConfig.cfgCutKaPtMin), static_cast(pidConfig.cfgCutPrPtMin)); } - if (cfgApplyEfficiencyCorrection) { + if (cfgApplyEfficiencyCorrection && (doprocessRun3 || doprocessMCRecoSimRun3)) { ccdb->setURL(cfgEfficiencyCCDBUrl.value); ccdb->setCaching(true); auto* efficiencyList = ccdb->getForTimeStamp(cfgEfficiencyCCDBPath.value, cfgEfficiencyTimestamp.value); @@ -273,10 +320,10 @@ struct MeanPtFlucId { auto validateEfficiencyMap = [&](const TH3* map, const char* name, double minPt) { constexpr double AxisTolerance = 1.e-6; constexpr double MaximumCentrality = 100.; - const bool coversEta = map->GetXaxis()->GetXmin() <= -cfgCutEta + AxisTolerance && - map->GetXaxis()->GetXmax() >= cfgCutEta - AxisTolerance; + const bool coversEta = map->GetXaxis()->GetXmin() <= -trackCuts.cfgCutEta + AxisTolerance && + map->GetXaxis()->GetXmax() >= trackCuts.cfgCutEta - AxisTolerance; const bool coversPt = map->GetYaxis()->GetXmin() <= minPt + AxisTolerance && - map->GetYaxis()->GetXmax() >= cfgCutPtMax - AxisTolerance; + map->GetYaxis()->GetXmax() >= trackCuts.cfgCutPtMax - AxisTolerance; const bool coversCentrality = map->GetZaxis()->GetXmin() <= AxisTolerance && map->GetZaxis()->GetXmax() >= MaximumCentrality - AxisTolerance; if (!coversEta || !coversPt || !coversCentrality) { @@ -288,20 +335,20 @@ struct MeanPtFlucId { map->GetXaxis()->GetXmin(), map->GetXaxis()->GetXmax(), map->GetYaxis()->GetXmin(), map->GetYaxis()->GetXmax(), map->GetZaxis()->GetXmin(), map->GetZaxis()->GetXmax(), - -static_cast(cfgCutEta), static_cast(cfgCutEta), - minPt, static_cast(cfgCutPtMax)); + -static_cast(trackCuts.cfgCutEta), static_cast(trackCuts.cfgCutEta), + minPt, static_cast(trackCuts.cfgCutPtMax)); } }; - validateEfficiencyMap(efficiencyWeightCh, chargedMapName, cfgCutPtMin); - validateEfficiencyMap(efficiencyWeightPi, pionMapName, cfgCutPiPtMin); - validateEfficiencyMap(efficiencyWeightKa, kaonMapName, cfgCutKaPtMin); - validateEfficiencyMap(efficiencyWeightPr, protonMapName, cfgCutPrPtMin); + validateEfficiencyMap(efficiencyWeightCh, chargedMapName, trackCuts.cfgCutPtMin); + validateEfficiencyMap(efficiencyWeightPi, pionMapName, pidConfig.cfgCutPiPtMin); + validateEfficiencyMap(efficiencyWeightKa, kaonMapName, pidConfig.cfgCutKaPtMin); + validateEfficiencyMap(efficiencyWeightPr, protonMapName, pidConfig.cfgCutPrPtMin); } - const AxisSpec axisPtCh{nPtBins, cfgCutPtMin, cfgCutPtMax, "p_{T} (GeV/c)"}; - const AxisSpec axisPtPi{nPtBins, cfgCutPiPtMin, cfgCutPtMax, "p_{T} (GeV/c)"}; - const AxisSpec axisPtKa{nPtBins, cfgCutKaPtMin, cfgCutPtMax, "p_{T} (GeV/c)"}; - const AxisSpec axisPtPr{nPtBins, cfgCutPrPtMin, cfgCutPtMax, "p_{T} (GeV/c)"}; + const AxisSpec axisPtCh{nPtBins, trackCuts.cfgCutPtMin, trackCuts.cfgCutPtMax, "p_{T} (GeV/c)"}; + const AxisSpec axisPtPi{nPtBins, pidConfig.cfgCutPiPtMin, trackCuts.cfgCutPtMax, "p_{T} (GeV/c)"}; + const AxisSpec axisPtKa{nPtBins, pidConfig.cfgCutKaPtMin, trackCuts.cfgCutPtMax, "p_{T} (GeV/c)"}; + const AxisSpec axisPtPr{nPtBins, pidConfig.cfgCutPrPtMin, trackCuts.cfgCutPtMax, "p_{T} (GeV/c)"}; const AxisSpec axisCol{3, 1, 4, ""}; const AxisSpec axisTrack{5, 1, 6, ""}; @@ -316,8 +363,10 @@ struct MeanPtFlucId { const AxisSpec axisMultCorr{multCorrBins, "N_{Corr} "}; const AxisSpec axisMultMC{multMCBins, "N_{Gen} "}; const AxisSpec axisMultFT0M{multFT0MBins, "N_{FT0M}"}; - const AxisSpec axisCentFT0M{101, 0, 101, "FT0M (%)"}; - const AxisSpec axisCent{centBins, "FT0M (%)"}; + // Match the estimator precedence used by getConfiguredCentrality(). + const std::string centralityLabel = eventCuts.cfgCentFT0C ? "FT0C (%)" : (eventCuts.cfgCentFV0A ? "FV0A (%)" : "FT0M (%)"); + const AxisSpec axisCentFT0M{101, 0, 101, centralityLabel}; + const AxisSpec axisCent{centBins, centralityLabel}; const AxisSpec axisVtxZ{80, -20., 20., "V_{Z} (cm)"}; const AxisSpec axisDCAz{dcaZBins, "DCA_{Z} (cm)"}; const AxisSpec axisDCAxy{dcaXYBins, "DCA_{XY} (cm)"}; @@ -344,7 +393,7 @@ struct MeanPtFlucId { hist.add("QA/before/h_VtxZ", "V_{Z}", kTH1D, {axisVtxZ}); hist.add("QA/before/h_NTPC", "N_{TPC}", kTH1D, {axisMultTPC}); hist.add("QA/before/h_NFT0M", "FT0M Multiplicity", kTH1D, {axisMultFT0M}); - hist.add("QA/before/h_CentM", "FT0M (%)", kTH1D, {axisCentFT0M}); + hist.add("QA/before/h_CentM", centralityLabel.c_str(), kTH1D, {axisCentFT0M}); hist.add("QA/before/h2_TPCSignal", "TPC Signal", tpcSignalHist); hist.add("QA/before/h2_TOFSignal", "TOF Signal", tofSignalHist); @@ -365,10 +414,10 @@ struct MeanPtFlucId { hist.add("QA/after/h_TPCChi2perCluster", "TPC #Chi^{2}/Cluster", kTH1D, {axisChi2}); hist.add("QA/after/h_ITSChi2perCluster", "ITS #Chi^{2}/Cluster", kTH1D, {axisChi2}); hist.add("QA/after/h_crossedTPC", "Crossed TPC", kTH1D, {axisCrossedTPC}); - hist.add("QA/after/h2_NTPC_CentM", "N_{TPC} vs FT0M(%)", kTH2D, {{axisCentFT0M}, {axisMultTPC}}); + hist.add("QA/after/h2_NTPC_CentM", ("N_{TPC} vs " + centralityLabel).c_str(), kTH2D, {{axisCentFT0M}, {axisMultTPC}}); hist.add("QA/after/h2_NTPC_NFT0M", "N_{TPC} vs N_{FT0M}", kTH2D, {{axisMultFT0M}, {axisMultTPC}}); hist.add("QA/after/p_NTPC_NFT0M", "N_{TPC} vs N_{FT0M} (Profile)", kTProfile, {axisMultFT0M}); - hist.add("QA/after/p_NTPC_CentM", "N_{TPC} vs FT0M(%) (Profile)", kTProfile, {axisCentFT0M}); + hist.add("QA/after/p_NTPC_CentM", ("N_{TPC} vs " + centralityLabel + " (Profile)").c_str(), kTProfile, {axisCentFT0M}); hist.add("QA/after/h_DCAxy_primary", "DCA_{XY} (Primary)", kTH1D, {axisDCAxy}); hist.add("QA/after/h_DCAz_primary", "DCA_{Z} (Primary)", kTH1D, {axisDCAz}); hist.add("QA/after/h_DCAxy_secondary", "DCA_{XY} (Secondary)", kTH1D, {axisDCAxy}); @@ -378,7 +427,7 @@ struct MeanPtFlucId { hist.add("QA/after/h2_NTPC_NSim", "Truth vs Reco Multiplicty TPC", kTH2D, {{axisMultTPC}, {axisMultMC}}); hist.add("QA/after/p_NTPC_NSim", "Truth vs Reco Multiplicty TPC", kTProfile, {axisMultTPC}); hist.add("QA/after/h2_Ncorr_NSim", "Truth vs Corrected Multiplicty TPC", kTH2D, {{axisMultCorr}, {axisMultMC}}); - hist.add("QA/after/h2_Ncorr_CentM", "N_{corr} vs FT0M(%)", kTH2D, {{axisCentFT0M}, {axisMultCorr}}); + hist.add("QA/after/h2_Ncorr_CentM", ("N_{corr} vs " + centralityLabel).c_str(), kTH2D, {{axisCentFT0M}, {axisMultCorr}}); }; addEventQAHistograms(); @@ -418,7 +467,7 @@ struct MeanPtFlucId { hist.add("QA/Charged/h2_Pt_Eta", "p_{T} vs #eta ", kTH2D, {{axisEta}, {axisPtCh}}); hist.add("QA/Charged/h3_Pt_Eta_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtCh}, {axisCent}}); hist.add("QA/Charged/h3_Pt_EtaMC_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtCh}, {axisCent}}); - hist.add("QA/Charged/h3_Pt_EtaMC_primary_centFT0M", "Selected primary MC particles;#eta_{MC};p_{T,MC};FT0M (%)", kTH3D, {{axisEta}, {axisPtCh}, {axisCent}}); + hist.add("QA/Charged/h3_Pt_EtaTruth_primary_centFT0M", ("Selected truth primary tracks in reconstructed coordinates;#eta;p_{T};" + centralityLabel).c_str(), kTH3D, {{axisEta}, {axisPtCh}, {axisCent}}); hist.add("QA/Charged/h2_Pt_EtaMC", "p_{T} vs #eta (MC)", kTH2D, {{axisEta}, {axisPtCh}}); }; addTrackQAHistograms(); @@ -429,7 +478,7 @@ struct MeanPtFlucId { hist.add("QA/Pion/h2_Pt_Eta", "p_{T} vs #eta ", kTH2D, {{axisEta}, {axisPtPi}}); hist.add("QA/Pion/h3_Pt_Eta_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtPi}, {axisCent}}); hist.add("QA/Pion/h3_Pt_EtaMC_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtPi}, {axisCent}}); - hist.add("QA/Pion/h3_Pt_EtaMC_primary_centFT0M", "Correctly selected primary pions;#eta_{MC};p_{T,MC};FT0M (%)", kTH3D, {{axisEta}, {axisPtPi}, {axisCent}}); + hist.add("QA/Pion/h3_Pt_EtaTruth_primary_centFT0M", ("Selected truth primary tracks in reconstructed coordinates;#eta;p_{T};" + centralityLabel).c_str(), kTH3D, {{axisEta}, {axisPtPi}, {axisCent}}); hist.add("QA/Pion/h2_Pt_EtaMC", "p_{T} vs #eta (MC)", kTH2D, {{axisEta}, {axisPtPi}}); hist.add("QA/Pion/h_PtPos", "p_{T} (positive) ", kTH1D, {axisPtPi}); hist.add("QA/Pion/h_PtNeg", "p_{T} (negative) ", kTH1D, {axisPtPi}); @@ -446,7 +495,7 @@ struct MeanPtFlucId { hist.add("QA/Kaon/h2_Pt_Eta", "p_{T} vs #eta ", kTH2D, {{axisEta}, {axisPtKa}}); hist.add("QA/Kaon/h3_Pt_Eta_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtKa}, {axisCent}}); hist.add("QA/Kaon/h3_Pt_EtaMC_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtKa}, {axisCent}}); - hist.add("QA/Kaon/h3_Pt_EtaMC_primary_centFT0M", "Correctly selected primary kaons;#eta_{MC};p_{T,MC};FT0M (%)", kTH3D, {{axisEta}, {axisPtKa}, {axisCent}}); + hist.add("QA/Kaon/h3_Pt_EtaTruth_primary_centFT0M", ("Selected truth primary tracks in reconstructed coordinates;#eta;p_{T};" + centralityLabel).c_str(), kTH3D, {{axisEta}, {axisPtKa}, {axisCent}}); hist.add("QA/Kaon/h2_Pt_EtaMC", "p_{T} vs #eta (MC)", kTH2D, {{axisEta}, {axisPtKa}}); hist.add("QA/Kaon/h_PtPos", "p_{T} (positive) ", kTH1D, {axisPtKa}); hist.add("QA/Kaon/h_PtNeg", "p_{T} (negative) ", kTH1D, {axisPtKa}); @@ -463,7 +512,7 @@ struct MeanPtFlucId { hist.add("QA/Proton/h2_Pt_Eta", "p_{T} vs #eta ", kTH2D, {{axisEta}, {axisPtPr}}); hist.add("QA/Proton/h3_Pt_Eta_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtPr}, {axisCent}}); hist.add("QA/Proton/h3_Pt_EtaMC_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtPr}, {axisCent}}); - hist.add("QA/Proton/h3_Pt_EtaMC_primary_centFT0M", "Correctly selected primary protons;#eta_{MC};p_{T,MC};FT0M (%)", kTH3D, {{axisEta}, {axisPtPr}, {axisCent}}); + hist.add("QA/Proton/h3_Pt_EtaTruth_primary_centFT0M", ("Selected truth primary tracks in reconstructed coordinates;#eta;p_{T};" + centralityLabel).c_str(), kTH3D, {{axisEta}, {axisPtPr}, {axisCent}}); hist.add("QA/Proton/h2_Pt_EtaMC", "p_{T} vs #eta (MC)", kTH2D, {{axisEta}, {axisPtPr}}); hist.add("QA/Proton/h_PtPos", "p_{T} (positive) ", kTH1D, {axisPtPr}); hist.add("QA/Proton/h_PtNeg", "p_{T} (negative) ", kTH1D, {axisPtPr}); @@ -558,6 +607,53 @@ struct MeanPtFlucId { }; addGeneratedHistograms(); + if (doprocessMCGenOnly) { + if (genConfig.cfgGenCutPosZ && (!std::isfinite(genConfig.cfgGenMaxPosZ.value) || genConfig.cfgGenMaxPosZ <= 0.f)) { + LOGF(fatal, "cfgGenMaxPosZ must be finite and positive"); + } + const std::string estimator = eventCuts.cfgCentFT0C ? "FT0C" : (eventCuts.cfgCentFV0A ? "FV0A" : "FT0M"); + constexpr size_t MinimumCentralityLookupEntries = 2; + constexpr double MinimumCentralityPercentile = 0.; + constexpr double MaximumCentralityPercentile = 100.; + if (genConfig.cfgGenForwardMultMax < 1) { + LOGF(fatal, "cfgGenForwardMultMax must be positive"); + } + if (!genConfig.cfgGenCentralityCalibration) { + const auto& lookup = genConfig.cfgGenCentralityLookup.value; + if (genConfig.cfgGenCentralityEstimator.value != estimator || lookup.size() < MinimumCentralityLookupEntries) { + LOGF(fatal, "Supply a centrality lookup for %s, or enable cfgGenCentralityCalibration for the first pass", estimator.c_str()); + } + for (size_t i = 0; i < lookup.size(); ++i) { + if (!std::isfinite(lookup[i]) || lookup[i] < MinimumCentralityPercentile || lookup[i] > MaximumCentralityPercentile || (i != 0 && lookup[i] > lookup[i - 1])) { + LOGF(fatal, "Generator centrality lookup must be finite, within [0,100] and non-increasing"); + } + } + } + const AxisSpec axisModelCent{centBins, "Generated " + estimator + " percentile (%)"}; + const AxisSpec axisForward{genConfig.cfgGenForwardMultMax.value + 1, -0.5, genConfig.cfgGenForwardMultMax.value + 0.5, "Generated " + estimator + " multiplicity"}; + hist.add("Model/h_ForwardMult", "Reference forward multiplicity after generator event cuts", kTH1D, {axisForward}); + hist.add("Model/h_Centrality", "Events used for matrices (including zero selected particles)", kTH1D, {axisModelCent}); + hist.add("Model/h_CentralityStatus", "Centrality lookup;Status;Events", kTH1D, {{2, 0.5, 2.5}}); + hist.get(HIST("Model/h_CentralityStatus"))->GetXaxis()->SetBinLabel(1, "Calibrated"); + hist.get(HIST("Model/h_CentralityStatus"))->GetXaxis()->SetBinLabel(2, "Outside lookup"); + const AxisSpec axisModelMult{multMCBins, "N_{ch}^{gen} (physical primaries in |#eta| acceptance, all p_{T})"}; + hist.add("Model/h_EventCount", "Generated events;Selection;Events", kTH1D, {{2, 0.5, 2.5}}); + hist.get(HIST("Model/h_EventCount"))->GetXaxis()->SetBinLabel(1, "All generated"); + hist.get(HIST("Model/h_EventCount"))->GetXaxis()->SetBinLabel(2, "Accepted"); + hist.add("Model/h_VtxZ", "Generated vertex Z", kTH1D, {axisVtxZ}); + hist.add("Model/h_Nch", "Accepted events (including zero selected particles)", kTH1D, {axisModelMult}); + auto addModelSpecies = [&](const std::string& directory, const AxisSpec& axisPtId) { + hist.add(directory + "h_Pt", "Generated physical primaries", kTH1D, {axisPtId}); + hist.add(directory + "h_Eta", "Generated physical primaries", kTH1D, {axisEta}); + hist.add(directory + "hN1Matrix", "Generated single-particle density", kTH2D, {axisModelCent, axisPtId}); + hist.add(directory + "hN2Matrix", "Generated ordered-pair density", kTH3D, {axisModelCent, axisPtId, axisPtId}); + }; + addModelSpecies("Model/Charged/", axisPtCh); + addModelSpecies("Model/Pion/", axisPtPi); + addModelSpecies("Model/Kaon/", axisPtKa); + addModelSpecies("Model/Proton/", axisPtPr); + } + hist.add("QA/h_collisions_info", "Collisions info", kTH1D, {axisCol}); hist.add("Gen/h_collisions_info", "Collisions info", kTH1D, {axisCol}); hist.add("Gen/h_collision_recgen", "Number of Collisions ", kTH1D, {axisCol}); @@ -577,7 +673,7 @@ struct MeanPtFlucId { hist.get(HIST("QA/after/h_counts_evSelCuts"))->GetXaxis()->SetBinLabel(SelCollisionLabels::kSelColSel8, "kSelColSel8"); hist.get(HIST("QA/after/h_counts_evSelCuts"))->GetXaxis()->SetBinLabel(SelCollisionLabels::kSelColNoSameBunchPileup, "kSelColNoSameBunchPileup"); hist.get(HIST("QA/after/h_counts_evSelCuts"))->GetXaxis()->SetBinLabel(SelCollisionLabels::kSelColIsVertexITSTPC, "kSelColIsVertexITSTPC"); - if (cfgLightIonCuts) { + if (eventCuts.cfgLightIonCuts) { hist.get(HIST("QA/after/h_counts_evSelCuts"))->GetXaxis()->SetBinLabel(SelCollisionLabels::kSelColIsGoodITSLayersAll, "kSelColIsGoodITSLayersAll"); hist.get(HIST("QA/after/h_counts_evSelCuts"))->GetXaxis()->SetBinLabel(SelCollisionLabels::kSelColIsGoodZvtxFT0vsPV, "kSelColIsGoodZvtxFT0vsPV"); } @@ -587,51 +683,62 @@ struct MeanPtFlucId { int nTPC = 0, nFT0M = 0; int nSim = 0; + template + float getConfiguredCentrality(T const& col) const + { + if (eventCuts.cfgCentFT0C.value) { + return col.centFT0C(); + } + if (eventCuts.cfgCentFV0A.value) { + return col.centFV0A(); + } + return col.centFT0M(); + } + // Event selection cuts: template bool selRun3Col(T const& col) { - - centFT0M = col.centFT0M(); + centFT0M = getConfiguredCentrality(col); nTPC = col.multNTracksHasTPC(); nFT0M = col.multFT0M(); hist.fill(HIST("QA/after/h_counts_evSelCuts"), kBeforeSelCol); - if (cfgPosZ) { - if (std::abs(col.posZ()) > cfgCutPosZ) { + if (eventCuts.cfgPosZ) { + if (std::abs(col.posZ()) > eventCuts.cfgCutPosZ) { return false; } hist.fill(HIST("QA/after/h_counts_evSelCuts"), kSelColPosZ); } - if (cfgSel8) { + if (eventCuts.cfgSel8) { if (!col.sel8()) { return false; } hist.fill(HIST("QA/after/h_counts_evSelCuts"), kSelColSel8); } - if (cfgNoSameBunchPileup) { + if (eventCuts.cfgNoSameBunchPileup) { if (!col.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { return false; } hist.fill(HIST("QA/after/h_counts_evSelCuts"), kSelColNoSameBunchPileup); } - if (cfgIsVertexITSTPC) { + if (eventCuts.cfgIsVertexITSTPC) { if (!col.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { return false; } hist.fill(HIST("QA/after/h_counts_evSelCuts"), kSelColIsVertexITSTPC); } - if (cfgLightIonCuts) { - if (cfgIsGoodITSLayersAll) { + if (eventCuts.cfgLightIonCuts) { + if (eventCuts.cfgIsGoodITSLayersAll) { if (!col.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { return false; } hist.fill(HIST("QA/after/h_counts_evSelCuts"), kSelColIsGoodITSLayersAll); } - if (cfgIsGoodZvtxFT0vsPV) { + if (eventCuts.cfgIsGoodZvtxFT0vsPV) { if (!col.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { return false; } @@ -646,15 +753,19 @@ struct MeanPtFlucId { template bool selTrack(T const& track) { - if (!track.isGlobalTrack()) { + if (trackCuts.cfgGlobalTrack && !track.isGlobalTrack()) { + return false; + } + + if (trackCuts.cfgGlobalTrackWoDCA && !track.isGlobalTrackWoDCA()) { return false; } - if (track.pt() < cfgCutPtMin) { + if (track.pt() < trackCuts.cfgCutPtMin) { return false; } - if (track.pt() >= cfgCutPtMax) { + if (track.pt() >= trackCuts.cfgCutPtMax) { return false; } @@ -662,16 +773,41 @@ struct MeanPtFlucId { return false; } - const float maxDca = cfgDcaPtDepOffset + cfgDcaPtDepScale / track.pt(); - // if (std::abs(track.dcaXY()) >= maxDca) { - // return false; - // } + const float maxDca = trackCuts.cfgDcaPtDepOffset + trackCuts.cfgDcaPtDepScale / track.pt(); - if (std::abs(track.dcaZ()) >= maxDca) { + if (trackCuts.cfgDcaXYPtDep) { + if (std::abs(track.dcaXY()) >= maxDca) { + return false; + } + } else if (trackCuts.cfgDcaXY) { + if (std::abs(track.dcaXY()) >= trackCuts.cfgCutDCAxy) { + return false; + } + } + + if (trackCuts.cfgDcaZPtDep) { + if (std::abs(track.dcaZ()) >= maxDca) { + return false; + } + } else if (trackCuts.cfgDcaZ) { + if (std::abs(track.dcaZ()) >= trackCuts.cfgCutDCAz) { + return false; + } + } + + if (std::abs(track.eta()) >= trackCuts.cfgCutEta) { + return false; + } + + if (trackCuts.cfgTPCChi2NCl && track.tpcChi2NCl() > trackCuts.cfgCutTPCChi2NCl) { + return false; + } + + if (trackCuts.cfgITSChi2NCl && track.itsChi2NCl() > trackCuts.cfgCutITSChi2NCl) { return false; } - if (std::abs(track.eta()) >= cfgCutEta) { + if (trackCuts.cfgNTPCCrossedRows && track.tpcNClsCrossedRows() < trackCuts.cfgCutNCrossedRowsTPC) { return false; } @@ -682,19 +818,32 @@ struct MeanPtFlucId { template bool rejectTracks(T const& track) { - return ((track.tpcNSigmaEl()) > -cfgCutNSig3 && - (track.tpcNSigmaEl()) < cfgCutNSig5) && - (std::fabs(track.tpcNSigmaPi()) > cfgCutNSig3 && - std::fabs(track.tpcNSigmaKa()) > cfgCutNSig3 && - std::fabs(track.tpcNSigmaPr()) > cfgCutNSig3); + return ((track.tpcNSigmaEl()) > -pidConfig.cfgCutNSig3 && + (track.tpcNSigmaEl()) < pidConfig.cfgCutNSig5) && + (std::fabs(track.tpcNSigmaPi()) > pidConfig.cfgCutNSig3 && + std::fabs(track.tpcNSigmaKa()) > pidConfig.cfgCutNSig3 && + std::fabs(track.tpcNSigmaPr()) > pidConfig.cfgCutNSig3); + } + + // Share the corrected hypotheses between PID selection and selected-particle QA. + // The before-PID histograms intentionally retain raw values for calibration. + template + std::array getTofNSigmaForPid(T const& track) const + { + std::array values = {-999.f, track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr()}; + if (track.hasTOF()) { + values[static_cast(PIDType::kPions)] -= pidConfig.cfgTofShiftPi.value; + values[static_cast(PIDType::kKaons)] -= pidConfig.cfgTofShiftKa.value; + values[static_cast(PIDType::kProtons)] -= pidConfig.cfgTofShiftPr.value; + } + return values; } // PID cuts for identified particles template - bool identifyParticle(T const& track, PIDType species, PIDType reject1, PIDType reject2, float p, float momThreshold) + bool identifyParticle(T const& track, PIDType species, PIDType reject1, PIDType reject2, float p, float momThreshold, const std::array& tofNSigma) { const std::array tpcNSigma = {-999.f, track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; - const std::array tofNSigma = {-999.f, track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr()}; const int selSpecies = static_cast(species); const int rejSpecies1 = static_cast(reject1); @@ -705,16 +854,16 @@ struct MeanPtFlucId { switch (species) { case PIDType::kPions: - tpcSelectionCut = cfgSelCutNSigTpcPi; - tofSelectionCut = cfgSelCutNSigTofPi; + tpcSelectionCut = pidConfig.cfgSelCutNSigTpcPi; + tofSelectionCut = pidConfig.cfgSelCutNSigTofPi; break; case PIDType::kKaons: - tpcSelectionCut = cfgSelCutNSigTpcKa; - tofSelectionCut = cfgSelCutNSigTofKa; + tpcSelectionCut = pidConfig.cfgSelCutNSigTpcKa; + tofSelectionCut = pidConfig.cfgSelCutNSigTofKa; break; case PIDType::kProtons: - tpcSelectionCut = cfgSelCutNSigTpcPr; - tofSelectionCut = cfgSelCutNSigTofPr; + tpcSelectionCut = pidConfig.cfgSelCutNSigTpcPr; + tofSelectionCut = pidConfig.cfgSelCutNSigTofPr; break; default: return false; @@ -725,7 +874,7 @@ struct MeanPtFlucId { const bool passesTOF = selectedTofNSigma < tofSelectionCut && std::abs(tofNSigma[rejSpecies1]) > selectedTofNSigma && std::abs(tofNSigma[rejSpecies2]) > selectedTofNSigma; - const bool passesTPC = std::abs(tpcNSigma[selSpecies]) < cfgCutNSigTpcTof; + const bool passesTPC = std::abs(tpcNSigma[selSpecies]) < pidConfig.cfgCutNSigTpcTof; return passesTPC && passesTOF; } @@ -755,7 +904,7 @@ struct MeanPtFlucId { hist.fill(HIST("QA/before/h_VtxZ"), col.posZ()); hist.fill(HIST("QA/before/h_Counts"), 2); hist.fill(HIST("QA/before/h_NTPC"), col.multNTracksHasTPC()); - hist.fill(HIST("QA/before/h_CentM"), col.centFT0M()); + hist.fill(HIST("QA/before/h_CentM"), getConfiguredCentrality(col)); hist.fill(HIST("QA/before/h_NFT0M"), col.multFT0M()); } @@ -918,10 +1067,10 @@ struct MeanPtFlucId { } template - void fillMatrices(const std::vector& tracks, double ptMin) + void fillMatrices(const std::vector& tracks, double ptMin, double eventCoordinate, double eventWeight = 1.) { const int nBins = nPtBins; - const double binWidth = (cfgCutPtMax - ptMin) / nBins; + const double binWidth = (trackCuts.cfgCutPtMax - ptMin) / nBins; matrixNInBin.assign(nBins, 0); matrixOccupiedBins.clear(); matrixOccupiedBins.reserve(std::min(static_cast(nBins), tracks.size())); @@ -939,7 +1088,7 @@ struct MeanPtFlucId { for (const int& ptBin : matrixOccupiedBins) { const double ptCenter = ptMin + (ptBin + 0.5) * binWidth; - hist.fill(HIST(dire[Mode]) + HIST("hN1Matrix"), centFT0M, ptCenter, static_cast(matrixNInBin[ptBin])); + hist.fill(HIST(dire[Mode]) + HIST("hN1Matrix"), eventCoordinate, ptCenter, eventWeight * static_cast(matrixNInBin[ptBin])); } for (size_t i = 0; i < matrixOccupiedBins.size(); ++i) { @@ -947,19 +1096,25 @@ struct MeanPtFlucId { const double ptCenter1 = ptMin + (ptBin1 + 0.5) * binWidth; const double nPairDiagonal = static_cast(matrixNInBin[ptBin1]) * (matrixNInBin[ptBin1] - 1); if (nPairDiagonal > 0.0) { - hist.fill(HIST(dire[Mode]) + HIST("hN2Matrix"), centFT0M, ptCenter1, ptCenter1, nPairDiagonal); + hist.fill(HIST(dire[Mode]) + HIST("hN2Matrix"), eventCoordinate, ptCenter1, ptCenter1, eventWeight * nPairDiagonal); } for (size_t j = i + 1; j < matrixOccupiedBins.size(); ++j) { const int ptBin2 = matrixOccupiedBins[j]; const double ptCenter2 = ptMin + (ptBin2 + 0.5) * binWidth; const double nPair = static_cast(matrixNInBin[ptBin1]) * matrixNInBin[ptBin2]; - hist.fill(HIST(dire[Mode]) + HIST("hN2Matrix"), centFT0M, ptCenter1, ptCenter2, nPair); - hist.fill(HIST(dire[Mode]) + HIST("hN2Matrix"), centFT0M, ptCenter2, ptCenter1, nPair); + hist.fill(HIST(dire[Mode]) + HIST("hN2Matrix"), eventCoordinate, ptCenter1, ptCenter2, eventWeight * nPair); + hist.fill(HIST(dire[Mode]) + HIST("hN2Matrix"), eventCoordinate, ptCenter2, ptCenter1, eventWeight * nPair); } } } + template + void fillMatrices(const std::vector& tracks, double ptMin) + { + fillMatrices(tracks, ptMin, centFT0M); + } + bool getEfficiencyWeight(PIDType species, double pt, double eta, double& weight) const { TH3* weightMap = nullptr; @@ -998,7 +1153,7 @@ struct MeanPtFlucId { void fillCorrectedMatrices(const std::vector& tracks, double ptMin) { const int nBins = nPtBins; - const double binWidth = (cfgCutPtMax - ptMin) / nBins; + const double binWidth = (trackCuts.cfgCutPtMax - ptMin) / nBins; matrixWInBin.assign(nBins, 0.); matrixW2InBin.assign(nBins, 0.); matrixOccupiedBins.clear(); @@ -1092,9 +1247,10 @@ struct MeanPtFlucId { float nSigmaTPCPi = track.tpcNSigmaPi(); float nSigmaTPCKa = track.tpcNSigmaKa(); float nSigmaTPCPr = track.tpcNSigmaPr(); - float nSigmaTOFPi = track.tofNSigmaPi(); - float nSigmaTOFKa = track.tofNSigmaKa(); - float nSigmaTOFPr = track.tofNSigmaPr(); + const auto tofNSigma = getTofNSigmaForPid(track); + const float nSigmaTOFPi = tofNSigma[static_cast(PIDType::kPions)]; + const float nSigmaTOFKa = tofNSigma[static_cast(PIDType::kKaons)]; + const float nSigmaTOFPr = tofNSigma[static_cast(PIDType::kProtons)]; float rapPi = track.rapidity(MassPiPlus); float rapKa = track.rapidity(MassKPlus); float rapPr = track.rapidity(MassProton); @@ -1129,16 +1285,13 @@ struct MeanPtFlucId { hist.fill(HIST("QA/after/h_DCAxy_primary"), track.dcaXY()); hist.fill(HIST("QA/after/h_DCAz_primary"), track.dcaZ()); hist.fill(HIST("QA/Charged/h_PtTruth_primary"), track.pt()); - const bool mcInChargedAcceptance = ptMC >= cfgCutPtMin && ptMC < cfgCutPtMax && std::abs(etaMC) < cfgCutEta; - if (mcInChargedAcceptance) { - hist.fill(HIST("QA/Charged/h3_Pt_EtaMC_primary_centFT0M"), etaMC, ptMC, centFT0M); - if (cfgApplyEfficiencyCorrection && !cfgUseCombinedPurityEfficiencyWeight) { - double weight = 0.; - if (!getEfficiencyWeight(PIDType::kNone, ptMC, etaMC, weight)) { - LOGF(fatal, "Undefined truth-matched charged efficiency weight at pT=%g, eta=%g, centrality=%g", ptMC, etaMC, centFT0M); - } - chargedTracksTruthMatchedCorrected.push_back({ptMC, weight}); + hist.fill(HIST("QA/Charged/h3_Pt_EtaTruth_primary_centFT0M"), track.eta(), track.pt(), centFT0M); + if (cfgApplyEfficiencyCorrection && !cfgUseCombinedPurityEfficiencyWeight) { + double weight = 0.; + if (!getEfficiencyWeight(PIDType::kNone, pt, eta, weight)) { + LOGF(fatal, "Undefined truth-matched charged efficiency weight at pT=%g, eta=%g, centrality=%g", pt, eta, centFT0M); } + chargedTracksTruthMatchedCorrected.push_back({pt, weight}); } } else { hist.fill(HIST("QA/after/h_DCAxy_secondary"), track.dcaXY()); @@ -1166,14 +1319,14 @@ struct MeanPtFlucId { fillBeforePIDQAHistos(track); - if (cfgRejTrk && rejectTracks(track)) { + if (pidConfig.cfgRejTrk && rejectTracks(track)) { continue; } - auto selIDPion = identifyParticle(track, PIDType::kPions, PIDType::kKaons, PIDType::kProtons, p, cfgCutPiThrsldP); - auto selIDKaon = identifyParticle(track, PIDType::kKaons, PIDType::kPions, PIDType::kProtons, p, cfgCutKaThrsldP); - auto selIDProton = identifyParticle(track, PIDType::kProtons, PIDType::kPions, PIDType::kKaons, p, cfgCutPrThrsldP); + auto selIDPion = identifyParticle(track, PIDType::kPions, PIDType::kKaons, PIDType::kProtons, p, pidConfig.cfgCutPiThrsldP, tofNSigma); + auto selIDKaon = identifyParticle(track, PIDType::kKaons, PIDType::kPions, PIDType::kProtons, p, pidConfig.cfgCutKaThrsldP, tofNSigma); + auto selIDProton = identifyParticle(track, PIDType::kProtons, PIDType::kPions, PIDType::kKaons, p, pidConfig.cfgCutPrThrsldP, tofNSigma); - if (selIDPion && track.pt() >= cfgCutPiPtMin) { + if (selIDPion && track.pt() >= pidConfig.cfgCutPiPtMin) { if (cfgApplyEfficiencyCorrection) { double weight = 0.; if (!getEfficiencyWeight(PIDType::kPions, pt, eta, weight)) { @@ -1183,7 +1336,7 @@ struct MeanPtFlucId { } fillIdParticleQAHistos(track, ptMC, etaMC, rapPi, nSigmaTPCPi, nSigmaTOFPi, pionTracks); } - if (selIDKaon && track.pt() >= cfgCutKaPtMin) { + if (selIDKaon && track.pt() >= pidConfig.cfgCutKaPtMin) { if (cfgApplyEfficiencyCorrection) { double weight = 0.; if (!getEfficiencyWeight(PIDType::kKaons, pt, eta, weight)) { @@ -1193,7 +1346,7 @@ struct MeanPtFlucId { } fillIdParticleQAHistos(track, ptMC, etaMC, rapKa, nSigmaTPCKa, nSigmaTOFKa, kaonTracks); } - if (selIDProton && track.pt() >= cfgCutPrPtMin) { + if (selIDProton && track.pt() >= pidConfig.cfgCutPrPtMin) { if (cfgApplyEfficiencyCorrection) { double weight = 0.; if (!getEfficiencyWeight(PIDType::kProtons, pt, eta, weight)) { @@ -1207,69 +1360,60 @@ struct MeanPtFlucId { if constexpr (RecoFlag) { auto mc = track.template mcParticle_as(); int pid = mc.pdgCode(); - if (selIDPion && track.pt() >= cfgCutPiPtMin) { + if (selIDPion && track.pt() >= pidConfig.cfgCutPiPtMin) { if (std::abs(pid) == kPiPlus) { hist.fill(HIST("QA/Pion/h_PtTruth"), track.pt()); fillPtMCHist(false, pt, eta, rapPi, phi, pid, kPiPlus, kPiMinus); if (mc.isPhysicalPrimary()) { hist.fill(HIST("QA/Pion/h_PtTruth_primary"), track.pt()); + hist.fill(HIST("QA/Pion/h3_Pt_EtaTruth_primary_centFT0M"), track.eta(), track.pt(), centFT0M); hist.fill(HIST("QA/Pion/h2_Pt_EtaTruth_primary"), track.eta(), track.pt()); - const bool mcInPionAcceptance = ptMC >= cfgCutPiPtMin && ptMC < cfgCutPtMax && std::abs(etaMC) < cfgCutEta; - if (mcInPionAcceptance) { - hist.fill(HIST("QA/Pion/h3_Pt_EtaMC_primary_centFT0M"), etaMC, ptMC, centFT0M); - if (cfgApplyEfficiencyCorrection && !cfgUseCombinedPurityEfficiencyWeight) { - double weight = 0.; - if (!getEfficiencyWeight(PIDType::kPions, ptMC, etaMC, weight)) { - LOGF(fatal, "Undefined truth-matched pion efficiency weight at pT=%g, eta=%g, centrality=%g", ptMC, etaMC, centFT0M); - } - pionTracksTruthMatchedCorrected.push_back({ptMC, weight}); + if (cfgApplyEfficiencyCorrection && !cfgUseCombinedPurityEfficiencyWeight) { + double weight = 0.; + if (!getEfficiencyWeight(PIDType::kPions, pt, eta, weight)) { + LOGF(fatal, "Undefined truth-matched pion efficiency weight at pT=%g, eta=%g, centrality=%g", pt, eta, centFT0M); } + pionTracksTruthMatchedCorrected.push_back({pt, weight}); } } else { hist.fill(HIST("QA/Pion/h_PtTruth_secondary"), track.pt()); } } } - if (selIDKaon && track.pt() >= cfgCutKaPtMin) { + if (selIDKaon && track.pt() >= pidConfig.cfgCutKaPtMin) { if (std::abs(pid) == kKPlus) { hist.fill(HIST("QA/Kaon/h_PtTruth"), track.pt()); fillPtMCHist(false, pt, eta, rapKa, phi, pid, kKPlus, kKMinus); if (mc.isPhysicalPrimary()) { hist.fill(HIST("QA/Kaon/h_PtTruth_primary"), track.pt()); + hist.fill(HIST("QA/Kaon/h3_Pt_EtaTruth_primary_centFT0M"), track.eta(), track.pt(), centFT0M); hist.fill(HIST("QA/Kaon/h2_Pt_EtaTruth_primary"), track.eta(), track.pt()); - const bool mcInKaonAcceptance = ptMC >= cfgCutKaPtMin && ptMC < cfgCutPtMax && std::abs(etaMC) < cfgCutEta; - if (mcInKaonAcceptance) { - hist.fill(HIST("QA/Kaon/h3_Pt_EtaMC_primary_centFT0M"), etaMC, ptMC, centFT0M); - if (cfgApplyEfficiencyCorrection && !cfgUseCombinedPurityEfficiencyWeight) { - double weight = 0.; - if (!getEfficiencyWeight(PIDType::kKaons, ptMC, etaMC, weight)) { - LOGF(fatal, "Undefined truth-matched kaon efficiency weight at pT=%g, eta=%g, centrality=%g", ptMC, etaMC, centFT0M); - } - kaonTracksTruthMatchedCorrected.push_back({ptMC, weight}); + if (cfgApplyEfficiencyCorrection && !cfgUseCombinedPurityEfficiencyWeight) { + double weight = 0.; + if (!getEfficiencyWeight(PIDType::kKaons, pt, eta, weight)) { + LOGF(fatal, "Undefined truth-matched kaon efficiency weight at pT=%g, eta=%g, centrality=%g", pt, eta, centFT0M); } + kaonTracksTruthMatchedCorrected.push_back({pt, weight}); } } else { hist.fill(HIST("QA/Kaon/h_PtTruth_secondary"), track.pt()); } } } - if (selIDProton && track.pt() >= cfgCutPrPtMin) { + if (selIDProton && track.pt() >= pidConfig.cfgCutPrPtMin) { if (std::abs(pid) == kProton) { hist.fill(HIST("QA/Proton/h_PtTruth"), track.pt()); fillPtMCHist(false, pt, eta, rapPr, phi, pid, kProton, kProtonBar); if (mc.isPhysicalPrimary()) { hist.fill(HIST("QA/Proton/h_PtTruth_primary"), track.pt()); + hist.fill(HIST("QA/Proton/h3_Pt_EtaTruth_primary_centFT0M"), track.eta(), track.pt(), centFT0M); hist.fill(HIST("QA/Proton/h2_Pt_EtaTruth_primary"), track.eta(), track.pt()); - const bool mcInProtonAcceptance = ptMC >= cfgCutPrPtMin && ptMC < cfgCutPtMax && std::abs(etaMC) < cfgCutEta; - if (mcInProtonAcceptance) { - hist.fill(HIST("QA/Proton/h3_Pt_EtaMC_primary_centFT0M"), etaMC, ptMC, centFT0M); - if (cfgApplyEfficiencyCorrection && !cfgUseCombinedPurityEfficiencyWeight) { - double weight = 0.; - if (!getEfficiencyWeight(PIDType::kProtons, ptMC, etaMC, weight)) { - LOGF(fatal, "Undefined truth-matched proton efficiency weight at pT=%g, eta=%g, centrality=%g", ptMC, etaMC, centFT0M); - } - protonTracksTruthMatchedCorrected.push_back({ptMC, weight}); + if (cfgApplyEfficiencyCorrection && !cfgUseCombinedPurityEfficiencyWeight) { + double weight = 0.; + if (!getEfficiencyWeight(PIDType::kProtons, pt, eta, weight)) { + LOGF(fatal, "Undefined truth-matched proton efficiency weight at pT=%g, eta=%g, centrality=%g", pt, eta, centFT0M); } + protonTracksTruthMatchedCorrected.push_back({pt, weight}); } } else { hist.fill(HIST("QA/Proton/h_PtTruth_secondary"), track.pt()); @@ -1278,22 +1422,22 @@ struct MeanPtFlucId { } } } - fillMatrices(chargedTracks, cfgCutPtMin); - fillMatrices(pionTracks, cfgCutPiPtMin); - fillMatrices(kaonTracks, cfgCutKaPtMin); - fillMatrices(protonTracks, cfgCutPrPtMin); + fillMatrices(chargedTracks, trackCuts.cfgCutPtMin); + fillMatrices(pionTracks, pidConfig.cfgCutPiPtMin); + fillMatrices(kaonTracks, pidConfig.cfgCutKaPtMin); + fillMatrices(protonTracks, pidConfig.cfgCutPrPtMin); if (cfgApplyEfficiencyCorrection) { - fillCorrectedMatrices(chargedTracksCorrected, cfgCutPtMin); - fillCorrectedMatrices(pionTracksCorrected, cfgCutPiPtMin); - fillCorrectedMatrices(kaonTracksCorrected, cfgCutKaPtMin); - fillCorrectedMatrices(protonTracksCorrected, cfgCutPrPtMin); + fillCorrectedMatrices(chargedTracksCorrected, trackCuts.cfgCutPtMin); + fillCorrectedMatrices(pionTracksCorrected, pidConfig.cfgCutPiPtMin); + fillCorrectedMatrices(kaonTracksCorrected, pidConfig.cfgCutKaPtMin); + fillCorrectedMatrices(protonTracksCorrected, pidConfig.cfgCutPrPtMin); if constexpr (RecoFlag) { if (!cfgUseCombinedPurityEfficiencyWeight) { - fillCorrectedMatrices(chargedTracksTruthMatchedCorrected, cfgCutPtMin); - fillCorrectedMatrices(pionTracksTruthMatchedCorrected, cfgCutPiPtMin); - fillCorrectedMatrices(kaonTracksTruthMatchedCorrected, cfgCutKaPtMin); - fillCorrectedMatrices(protonTracksTruthMatchedCorrected, cfgCutPrPtMin); + fillCorrectedMatrices(chargedTracksTruthMatchedCorrected, trackCuts.cfgCutPtMin); + fillCorrectedMatrices(pionTracksTruthMatchedCorrected, pidConfig.cfgCutPiPtMin); + fillCorrectedMatrices(kaonTracksTruthMatchedCorrected, pidConfig.cfgCutKaPtMin); + fillCorrectedMatrices(protonTracksTruthMatchedCorrected, pidConfig.cfgCutPrPtMin); } } } @@ -1304,7 +1448,7 @@ struct MeanPtFlucId { { int multiplicity = 0; for (auto const& mcPart : mcParticles) { - if (!mcPart.isPhysicalPrimary() || std::abs(mcPart.eta()) >= cfgCutEta) { + if (!mcPart.isPhysicalPrimary() || std::abs(mcPart.eta()) >= trackCuts.cfgCutEta) { continue; } auto* particle = pdg->GetParticle(mcPart.pdgCode()); @@ -1317,11 +1461,11 @@ struct MeanPtFlucId { bool passesNtpcEventCut() { - if (!cfgNtpcEventCut) { + if (!eventCuts.cfgNtpcEventCut) { return true; } - const float lower = cfgP2L * nSim * nSim + cfgP1L * nSim - cfgP0L; - const float upper = cfgP2U * nSim * nSim + cfgP1U * nSim + cfgP0U; + const float lower = eventCuts.cfgP2L * nSim * nSim + eventCuts.cfgP1L * nSim - eventCuts.cfgP0L; + const float upper = eventCuts.cfgP2U * nSim * nSim + eventCuts.cfgP1U * nSim + eventCuts.cfgP0U; return nTPC >= lower && nTPC <= upper; } @@ -1354,11 +1498,11 @@ struct MeanPtFlucId { eta = mcPart.eta(); phi = mcPart.phi(); - if (std::abs(eta) < cfgCutEta) { + if (std::abs(eta) < trackCuts.cfgCutEta) { nSim++; } - if (pt >= cfgCutPtMin && pt < cfgCutPtMax && std::abs(eta) < cfgCutEta) { + if (pt >= trackCuts.cfgCutPtMin && pt < trackCuts.cfgCutPtMax && std::abs(eta) < trackCuts.cfgCutEta) { chargedTracksGen.push_back({pt}); hist.fill(HIST("Gen/Charged/h_PtTruth"), pt); hist.fill(HIST("Gen/Charged/h_PtTruth_e"), pt); @@ -1371,25 +1515,25 @@ struct MeanPtFlucId { rap = mcPart.y(); auto pid = mcPart.pdgCode(); - if (std::abs(pid) == kPiPlus && pt >= cfgCutPiPtMin) { + if (std::abs(pid) == kPiPlus && pt >= pidConfig.cfgCutPiPtMin) { pionTracksGen.push_back({pt}); fillPtMCHist(true, pt, eta, rap, phi, pid, kPiPlus, kPiMinus); } - if (std::abs(pid) == kKPlus && pt >= cfgCutKaPtMin) { + if (std::abs(pid) == kKPlus && pt >= pidConfig.cfgCutKaPtMin) { kaonTracksGen.push_back({pt}); fillPtMCHist(true, pt, eta, rap, phi, pid, kKPlus, kKMinus); } - if (std::abs(pid) == kProton && pt >= cfgCutPrPtMin) { + if (std::abs(pid) == kProton && pt >= pidConfig.cfgCutPrPtMin) { protonTracksGen.push_back({pt}); fillPtMCHist(true, pt, eta, rap, phi, pid, kProton, kProtonBar); } } } - fillMatrices(chargedTracksGen, cfgCutPtMin); - fillMatrices(pionTracksGen, cfgCutPiPtMin); - fillMatrices(kaonTracksGen, cfgCutKaPtMin); - fillMatrices(protonTracksGen, cfgCutPrPtMin); + fillMatrices(chargedTracksGen, trackCuts.cfgCutPtMin); + fillMatrices(pionTracksGen, pidConfig.cfgCutPiPtMin); + fillMatrices(kaonTracksGen, pidConfig.cfgCutKaPtMin); + fillMatrices(protonTracksGen, pidConfig.cfgCutPrPtMin); hist.fill(HIST("Gen/h_Counts"), 2); hist.fill(HIST("Gen/h_VtxZ"), mcCol.posZ()); @@ -1437,14 +1581,14 @@ struct MeanPtFlucId { aod::pidTOFFullPi, aod::pidTPCFullPi, aod::pidTOFFullPr, aod::pidTPCFullPr, aod::pidTOFFullKa, aod::pidTPCFullKa, aod::pidTOFFullEl, aod::pidTPCFullEl, aod::pidTOFbeta, aod::pidTOFmass>; - using MyRun3Collisions = soa::Join; - using MyRun3MCCollisions = soa::Join; + using MyRun3Collisions = soa::Join; + using MyRun3MCCollisions = soa::Join; using MyMCTracks = soa::Join; SliceCache cache; - Preslice perCollision = aod::track::collisionId; + PresliceOptional perCollision = aod::track::collisionId; void processRun3(MyRun3Collisions::iterator const& col, MyAllTracks const& tracks) { @@ -1458,6 +1602,115 @@ struct MeanPtFlucId { analyzeMC(mcCol, cols, tracks, mcParts); } PROCESS_SWITCH(MeanPtFlucId, processMCRecoSimRun3, "process MC Reconstructed & Truth Run-3", true); + + template + int countGenForwardMultiplicity(M const& particles) + { + constexpr double MinimumChargedParticleCharge = 1.e-3; + constexpr double Ft0aEtaMin = 3.5; + constexpr double Ft0aEtaMax = 4.9; + constexpr double Ft0cEtaMin = -3.3; + constexpr double Ft0cEtaMax = -2.1; + constexpr double Fv0aEtaMin = 2.2; + constexpr double Fv0aEtaMax = 5.0; + int ft0a = 0, ft0c = 0, fv0a = 0; + // Match Common/Tools/Multiplicity/MultModule.h::collisionProcessMonteCarlo. + // No analysis pT/eta cuts: these are forward charged physical primaries. + for (auto const& particle : particles) { + if (!particle.isPhysicalPrimary()) { + continue; + } + auto* definition = pdg->GetParticle(particle.pdgCode()); + if (!definition || std::abs(definition->Charge()) < MinimumChargedParticleCharge) { + continue; + } + const double eta = particle.eta(); + ft0a += (Ft0aEtaMin < eta && eta < Ft0aEtaMax); + ft0c += (Ft0cEtaMin < eta && eta < Ft0cEtaMax); + fv0a += (Fv0aEtaMin < eta && eta < Fv0aEtaMax); + } + return eventCuts.cfgCentFT0C ? ft0c : (eventCuts.cfgCentFV0A ? fv0a : ft0a + ft0c); + } + + double getGenCentrality(int forwardMultiplicity) const + { + const auto& lookup = genConfig.cfgGenCentralityLookup.value; + if (forwardMultiplicity < 0 || static_cast(forwardMultiplicity) >= lookup.size()) { + return -1.; // Do not extrapolate beyond the reference distribution. + } + return std::min(lookup[forwardMultiplicity], std::nextafter(100., 0.)); + } + + // Only MC tables are inputs: no reconstructed event, track, centrality or PID selection. + // The framework groups mcParts by the current mcCol through mcCollisionId. + void processMCGenOnly(aod::McCollisions::iterator const& mcCol, aod::McParticles const& mcParts) + { + const double eventWeight = genConfig.cfgGenUseEventWeight ? mcCol.weight() : 1.; + if (!std::isfinite(eventWeight) || eventWeight <= 0.) { + LOGF(fatal, "Generator percentile analysis requires finite positive event weights"); + } + hist.fill(HIST("Model/h_EventCount"), 1, eventWeight); + if (genConfig.cfgGenCutPosZ && std::abs(mcCol.posZ()) > genConfig.cfgGenMaxPosZ) { + return; + } + hist.fill(HIST("Model/h_EventCount"), 2, eventWeight); + hist.fill(HIST("Model/h_VtxZ"), mcCol.posZ(), eventWeight); + const int forwardMultiplicity = countGenForwardMultiplicity(mcParts); + hist.fill(HIST("Model/h_ForwardMult"), forwardMultiplicity, eventWeight); + if (genConfig.cfgGenCentralityCalibration) { + return; + } + const double centrality = getGenCentrality(forwardMultiplicity); + if (centrality < 0.) { + hist.fill(HIST("Model/h_CentralityStatus"), 2, eventWeight); + return; + } + hist.fill(HIST("Model/h_CentralityStatus"), 1, eventWeight); + hist.fill(HIST("Model/h_Centrality"), centrality, eventWeight); + const int multiplicity = countSimMultiplicity(mcParts); + hist.fill(HIST("Model/h_Nch"), multiplicity, eventWeight); + + std::array, 4> modelTracks; + for (auto const& particle : mcParts) { + if (!particle.isPhysicalPrimary()) { + continue; + } + auto* definition = pdg->GetParticle(particle.pdgCode()); + if (!definition || definition->Charge() == 0.) { + continue; + } + const float pt = particle.pt(); + const float eta = particle.eta(); + if (!std::isfinite(pt) || !std::isfinite(eta) || + pt < trackCuts.cfgCutPtMin || pt >= trackCuts.cfgCutPtMax || std::abs(eta) >= trackCuts.cfgCutEta) { + continue; + } + modelTracks[0].push_back({pt}); + hist.fill(HIST("Model/Charged/h_Pt"), pt, eventWeight); + hist.fill(HIST("Model/Charged/h_Eta"), eta, eventWeight); + const int code = std::abs(particle.pdgCode()); + if (code == kPiPlus && pt >= pidConfig.cfgCutPiPtMin) { + modelTracks[1].push_back({pt}); + hist.fill(HIST("Model/Pion/h_Pt"), pt, eventWeight); + hist.fill(HIST("Model/Pion/h_Eta"), eta, eventWeight); + } + if (code == kKPlus && pt >= pidConfig.cfgCutKaPtMin) { + modelTracks[2].push_back({pt}); + hist.fill(HIST("Model/Kaon/h_Pt"), pt, eventWeight); + hist.fill(HIST("Model/Kaon/h_Eta"), eta, eventWeight); + } + if (code == kProton && pt >= pidConfig.cfgCutPrPtMin) { + modelTracks[3].push_back({pt}); + hist.fill(HIST("Model/Proton/h_Pt"), pt, eventWeight); + hist.fill(HIST("Model/Proton/h_Eta"), eta, eventWeight); + } + } + fillMatrices(modelTracks[0], trackCuts.cfgCutPtMin, centrality, eventWeight); + fillMatrices(modelTracks[1], pidConfig.cfgCutPiPtMin, centrality, eventWeight); + fillMatrices(modelTracks[2], pidConfig.cfgCutKaPtMin, centrality, eventWeight); + fillMatrices(modelTracks[3], pidConfig.cfgCutPrPtMin, centrality, eventWeight); + } + PROCESS_SWITCH(MeanPtFlucId, processMCGenOnly, "Independent generator-only model comparison", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& context) diff --git a/PWGCF/EbyEFluctuations/Tasks/partNumFluc.cxx b/PWGCF/EbyEFluctuations/Tasks/partNumFluc.cxx index fe441f712d1..5f2ee67ac07 100644 --- a/PWGCF/EbyEFluctuations/Tasks/partNumFluc.cxx +++ b/PWGCF/EbyEFluctuations/Tasks/partNumFluc.cxx @@ -15,6 +15,7 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/McCollisionExtra.h" @@ -72,16 +73,16 @@ #include #include -#define C_CS(cs) /* NOLINT(cppcoreguidelines-macro-usage) */ \ - [](const std::index_sequence) constexpr -> ConstStr<(cs)[indices]...> { \ - static_assert(std::is_array_v> && \ - std::same_as>, char>); \ - return ConstStr<(cs)[indices]...>{}; \ +#define C_CS(cs) /* NOLINT(cppcoreguidelines-macro-usage) */ \ + [](const std::index_sequence&) constexpr noexcept -> ConstStr<(cs)[indices]...> { \ + static_assert(std::is_array_v> && \ + std::same_as>, char>); \ + return ConstStr<(cs)[indices]...>{}; \ }(std::make_index_sequence{}) -#define C_SV(sv) /* NOLINT(cppcoreguidelines-macro-usage) */ \ - [](const std::index_sequence) constexpr -> ConstStr<(sv)[indices]...> { \ - static_assert(std::same_as, std::string_view>); \ - return ConstStr<(sv)[indices]...>{}; \ +#define C_SV(sv) /* NOLINT(cppcoreguidelines-macro-usage) */ \ + [](const std::index_sequence&) constexpr noexcept -> ConstStr<(sv)[indices]...> { \ + static_assert(std::same_as, std::string_view>); \ + return ConstStr<(sv)[indices]...>{}; \ }(std::make_index_sequence<(sv).size()>{}) using namespace o2; @@ -324,31 +325,31 @@ template requires IsValidEnum constexpr std::int32_t NEs{toI(E::N)}; -enum class NameKind { +enum class NameKind : std::int8_t { Default = 0, Lower, Display, DisplayLower, N }; -enum class DataMode { +enum class DataMode : std::int8_t { McMcParticle = 0, McTrack, RawTrack, N }; -enum class Selection { +enum class Selection : std::int8_t { Loose = 0, Default, Tight, N }; -enum class RangeEdge { +enum class RangeEdge : std::int8_t { Min = 0, Max, N }; -enum class McEventSelection { +enum class McEventSelection : std::int8_t { All = 0, Good, Inel, @@ -356,7 +357,7 @@ enum class McEventSelection { NRecoCollisions, N }; -enum class EventSelection { +enum class EventSelection : std::int8_t { All = 0, Good, Run, @@ -369,64 +370,64 @@ enum class EventSelection { NPvContributors, N }; -enum class CentralityDefinition { +enum class CentralityDefinition : std::int8_t { Ft0a = 0, Ft0c, Ft0m, N }; -enum class DcaMeasure { +enum class DcaMeasure : std::int8_t { Mean = 0, Sigma, N }; -enum class DcaAxis { +enum class DcaAxis : std::int8_t { Xy = 0, Z, N }; -enum class Detector { +enum class Detector : std::int8_t { Tpc = 0, Tof, N }; -enum class PidStrategy { +enum class PidStrategy : std::int8_t { Tpc = 0, TpcTof, N }; -enum class PidStrategyAll { +enum class PidStrategyAll : std::int8_t { Tpc = 0, Tof, TpcTofSeparated, TpcTofCombined, N }; -enum class ParticleSpecies { +enum class ParticleSpecies : std::int8_t { Pion = 0, Kaon, Proton, N }; -enum class ParticleSpeciesAll { +enum class ParticleSpeciesAll : std::int8_t { All = 0, Pion, Kaon, Proton, N }; -enum class ChargeSpecies { +enum class ChargeSpecies : std::int8_t { Plus = 0, Minus, N }; -enum class ParticleNumber { +enum class ParticleNumber : std::int8_t { Charge = 0, Kaon, Proton, N }; -enum class ChargeNumber { +enum class ChargeNumber : std::int8_t { Plus = 0, Minus, Total, @@ -783,7 +784,7 @@ template constexpr std::int32_t nEnabled(const Configurable>& cfg) noexcept { const LabeledArray& la{cfg.value}; - return std::count_if(la[0], la[0] + la.rows() * la.cols(), [](const T x) constexpr -> bool { return x != T{}; }); + return std::ranges::count_if(std::span{la[0], static_cast(la.rows()) * static_cast(la.cols())}, [](const T input) constexpr noexcept -> bool { return input != T{}; }); } template requires std::is_arithmetic_v @@ -794,7 +795,7 @@ constexpr bool isEnabled(const Configurable>& cfg) noexcept double interpolate(const TH3* const h, const double x, const double y, const double z) { - if (!h) { + if (h == nullptr) { return 0.; } @@ -918,18 +919,13 @@ struct PartNumFluc { return std::abs(nSigmaPid) < TruncationAbsNSigmaPid && std::abs(nSigmaPidShifted) < TruncationAbsNSigmaPid ? nSigmaPidShifted : -TruncationAbsNSigmaPid; } - [[nodiscard]] double getNSigmaPidCombined(const std::int32_t indexParticleSpecies) const noexcept - { - return truncateNSigmaPid(std::copysign(std::hypot(nsSigmaPid[toI(Detector::Tpc)][indexParticleSpecies], nsSigmaPid[toI(Detector::Tof)][indexParticleSpecies]), nsSigmaPid[toI(Detector::Tpc)][indexParticleSpecies] + nsSigmaPid[toI(Detector::Tof)][indexParticleSpecies])); - } - std::array> dcas{}; std::int32_t sign{}; double pt{}; double eta{}; double phi{}; std::array> havePid{}; - std::array>, NEs> nsSigmaPid{[]() consteval -> std::array>, NEs> { + std::array>, NEs> nsSigmaPid{[]() consteval noexcept -> std::array>, NEs> { std::array>, NEs> result{}; std::array> source{}; source.fill(-TruncationAbsNSigmaPid); @@ -937,6 +933,10 @@ struct PartNumFluc { return result; }()}; + [[nodiscard]] double getNSigmaPidCombined(const std::int32_t indexParticleSpecies) const noexcept + { + return truncateNSigmaPid(std::copysign(std::hypot(nsSigmaPid[toI(Detector::Tpc)][indexParticleSpecies], nsSigmaPid[toI(Detector::Tof)][indexParticleSpecies]), nsSigmaPid[toI(Detector::Tpc)][indexParticleSpecies] + nsSigmaPid[toI(Detector::Tof)][indexParticleSpecies])); + } constexpr void clear() noexcept { *this = {}; } } holderTrack{}; @@ -944,7 +944,14 @@ struct PartNumFluc { template static constexpr T convert(const double input) noexcept { - return std::numeric_limits::lowest() <= input && input <= std::numeric_limits::max() ? static_cast(std::rint(input)) : (std::is_signed_v ? std::numeric_limits::lowest() : std::numeric_limits::max()); + if (std::numeric_limits::lowest() <= input && input <= std::numeric_limits::max()) { + return static_cast(std::rint(input)); + } + if constexpr (std::is_signed_v) { + return std::numeric_limits::lowest(); + } else { + return std::numeric_limits::max(); + } } std::array> nsMcParticles{}; @@ -1039,13 +1046,13 @@ struct PartNumFluc { Configurable> cfgCutsMinTpcNCrossedRows{"cfgCutsMinTpcNCrossedRows", {std::array>{70, 80, 90}.data(), NEs, getDisplayNames()}, "Minimum numbers of crossed rows TPC"}; Configurable cfgCutMinTpcNCrossedRowsRatio{"cfgCutMinTpcNCrossedRowsRatio", 0.8, "Minimum ratio of crossed rows over findable clusters TPC"}; Configurable cfgFlagRecalibrationDca{"cfgFlagRecalibrationDca", false, "DCA recalibration flag"}; - Configurable> cfgCutsMaxAbsNSigmaDca{"cfgCutsMaxAbsNSigmaDca", {std::array * NEs>{3., 2.5, 2., 3., 2.5, 2.}.data(), NEs, NEs, getDisplayNames(), getDisplayNames()}, "Maximum absolute nSigma values of DCA (cm)"}; - Configurable> cfgCutsRangePt{"cfgCutsRangePt", {std::array * NEs>{0.2, 2., 0.3, 2., 0.4, 2.}.data(), NEs, NEs, getDisplayNames(), getDisplayNames()}, "pT ranges (GeV/c)"}; + Configurable> cfgCutsMaxAbsNSigmaDca{"cfgCutsMaxAbsNSigmaDca", {std::array(NEs) * static_cast(NEs)>{3., 2.5, 2., 3., 2.5, 2.}.data(), NEs, NEs, getDisplayNames(), getDisplayNames()}, "Maximum absolute nSigma values of DCA (cm)"}; + Configurable> cfgCutsRangePt{"cfgCutsRangePt", {std::array(NEs) * static_cast(NEs)>{0.2, 2., 0.3, 2., 0.4, 2.}.data(), NEs, NEs, getDisplayNames(), getDisplayNames()}, "pT ranges (GeV/c)"}; Configurable cfgCutMaxAbsEta{"cfgCutMaxAbsEta", 0.8, "Maximum absolute eta"}; Configurable> cfgThresholdsPtTofPid{"cfgThresholdsPtTofPid", {std::array>{0.4, 0.4, 0.8}.data(), NEs, getDisplayNames()}, "pT (GeV/c) thresholds for TOF PID"}; Configurable> cfgFlagsRecalibrationNSigmaPid{"cfgFlagsRecalibrationNSigmaPid", {std::array>{}.data(), NEs, getDisplayNames()}, "nSigma PID recalibration flags"}; Configurable cfgFlagRejectionOthers{"cfgFlagRejectionOthers", false, "Other particle species rejection flag"}; - Configurable> cfgCutsMaxAbsNSigmaPid{"cfgCutsMaxAbsNSigmaPid", {std::array * NEs>{2.5, 2., 1.5, 2.5, 2., 1.5, 2.5, 2., 1.5}.data(), NEs, NEs, getDisplayNames(), getDisplayNames()}, "Maximum absolute nSigma values for PID"}; + Configurable> cfgCutsMaxAbsNSigmaPid{"cfgCutsMaxAbsNSigmaPid", {std::array(NEs) * static_cast(NEs)>{2.5, 2., 1.5, 2.5, 2., 1.5, 2.5, 2., 1.5}.data(), NEs, NEs, getDisplayNames(), getDisplayNames()}, "Maximum absolute nSigma values for PID"}; Configurable cfgFlagMcParticlePhysicalPrimary{"cfgFlagMcParticlePhysicalPrimary", true, "Flag of requiring physical primary MC particle"}; Configurable cfgFlagMcParticleMomentum{"cfgFlagMcParticleMomentum", true, "Flag of using momentum of MC particle"}; } cgTrack{}; @@ -1132,12 +1139,12 @@ struct PartNumFluc { } for (std::int32_t const& iDcaAxis : std::views::iota(0, NEs)) { for (std::int32_t const& iSelection : std::views::iota(0, NEs)) { - holderMember.configSelection->cutsMaxAbsNSigmaDca[iDcaAxis][iSelection] = cgTrack.cfgCutsMaxAbsNSigmaDca.value.get(getName(iDcaAxis).data(), getName(iSelection).data()); + holderMember.configSelection->cutsMaxAbsNSigmaDca[iDcaAxis][iSelection] = cgTrack.cfgCutsMaxAbsNSigmaDca.value.get(std::string{getName(iDcaAxis)}.c_str(), std::string{getName(iSelection)}.c_str()); } } for (std::int32_t const& iParticleSpecies : std::views::iota(0, NEs)) { for (std::int32_t const& iSelection : std::views::iota(0, NEs)) { - holderMember.configSelection->cutsMaxAbsNSigmaPid[iParticleSpecies][iSelection] = cgTrack.cfgCutsMaxAbsNSigmaPid.value.get(getName(iParticleSpecies).data(), getName(iSelection).data()); + holderMember.configSelection->cutsMaxAbsNSigmaPid[iParticleSpecies][iSelection] = cgTrack.cfgCutsMaxAbsNSigmaPid.value.get(std::string{getName(iParticleSpecies)}.c_str(), std::string{getName(iSelection)}.c_str()); } } @@ -1264,7 +1271,7 @@ struct PartNumFluc { if (cgAnalysis.cfgFlagQaRun.value) { LOG(info) << "Enabling run QA."; - const HistogramConfigSpec hcsQaRun{HistType::kTProfile, {{static_cast(holderCcdb.runNumbersToIndicesRunAndGroup.size()), -0.5, holderCcdb.runNumbersToIndicesRunAndGroup.size() - 0.5, "Run Index"}}}; + const HistogramConfigSpec hcsQaRun{HistType::kTProfile, {{static_cast(holderCcdb.runNumbersToIndicesRunAndGroup.size()), -0.5, static_cast(holderCcdb.runNumbersToIndicesRunAndGroup.size()) - 0.5, "Run Index"}}}; for (const auto& [name, title, isChargeSpeciesSeparated] : std::to_array>( {{"Vx", "#LT#it{V}_{#it{x}}#GT (cm)", false}, @@ -1361,7 +1368,7 @@ struct PartNumFluc { {"hPtDcaZ", "", {HistType::kTHnSparseD, {asPt, {250, -0.5, 0.5, "DCA_{#it{z}} (cm)"}}}}, {"pCentralityPtEtaDcaZ", ";;#LTDCA_{#it{z}}#GT (cm)", hcsQaDcaProfile}})) { for (std::int32_t const& iChargeSpecies : std::views::iota(0, NEs)) { - hrQaDca.add(std::format("{}_{}", name, getName(iChargeSpecies)).c_str(), title.data(), hcs); + hrQaDca.add(std::format("{}_{}", name, getName(iChargeSpecies)).c_str(), std::string{title}.c_str(), hcs); } } } @@ -1375,7 +1382,10 @@ struct PartNumFluc { for (std::int32_t const& iPidStrategy : std::views::iota(0, NEs)) { for (std::int32_t const& iChargeSpecies : std::views::iota(0, NEs)) { - hrQaAcceptance.add(std::format("h{}Pt_{}Edge{}{}", std::cmp_equal(iParticleSpeciesAll, toI(ParticleSpeciesAll::All)) ? "Eta" : "Rapidity", getName(iPidStrategy), getName(iParticleSpeciesAll), getName(iChargeSpecies)).c_str(), "", {HistType::kTHnSparseD, {{300, -1.5, 1.5, std::cmp_equal(iParticleSpeciesAll, toI(ParticleSpeciesAll::All)) ? "#it{#eta}" : "#it{y}"}, {250, 0., 2.5, "#it{p}_{T} (GeV/#it{c})"}}}); + hrQaAcceptance.add(std::format("hEta{}Pt{}_{}Edge{}{}", doProcessMc.value ? "Mc" : "", doProcessMc.value ? "Mc" : "", getName(iPidStrategy), getName(iParticleSpeciesAll), getName(iChargeSpecies)).c_str(), "", {HistType::kTHnSparseD, {{300, -1.5, 1.5, doProcessMc.value ? "#it{#eta}_{Gen}" : "#it{#eta}"}, {250, 0., 2.5, doProcessMc.value ? "#it{p}_{T}^{Gen} (GeV/#it{c})" : "#it{p}_{T} (GeV/#it{c})"}}}); + if (!std::cmp_equal(iParticleSpeciesAll, toI(ParticleSpeciesAll::All))) { + hrQaAcceptance.add(std::format("hRapidity{}Pt{}_{}Edge{}{}", doProcessMc.value ? "Mc" : "", doProcessMc.value ? "Mc" : "", getName(iPidStrategy), getName(iParticleSpeciesAll), getName(iChargeSpecies)).c_str(), "", {HistType::kTHnSparseD, {{300, -1.5, 1.5, doProcessMc.value ? "#it{y}_{Gen}" : "#it{y}"}, {250, 0., 2.5, doProcessMc.value ? "#it{p}_{T}^{Gen} (GeV/#it{c})" : "#it{p}_{T} (GeV/#it{c})"}}}); + } } } } @@ -1437,19 +1447,17 @@ struct PartNumFluc { } } - if (doProcessMc.value) { - if (cgAnalysis.cfgFlagQaMc.value) { - LOG(info) << "Enabling MC QA."; + if (doProcessMc.value && cgAnalysis.cfgFlagQaMc.value) { + LOG(info) << "Enabling MC QA."; - const double maxAbsVz{std::ceil(cgEvent.cfgFlagMcCollisionVz.value ? cgEvent.cfgCutMaxAbsVzMc.value : cgEvent.cfgCutMaxAbsVz.value)}; - const AxisSpec asCentrality{20, 0., 100., "Centrality (%)"}; + const double maxAbsVz{std::ceil(cgEvent.cfgFlagMcCollisionVz.value ? cgEvent.cfgCutMaxAbsVzMc.value : cgEvent.cfgCutMaxAbsVz.value)}; + const AxisSpec asCentrality{20, 0., 100., "Centrality (%)"}; - hrQaMc.add("hNCollisionsPerMcCollision", "", {HistType::kTH1D, {{20, -0.5, 19.5, "No. of collisions per MC collision"}}}); - hrQaMc.add("hCentralityNTracksPerMcParticle", "", {HistType::kTHnSparseF, {asCentrality, {20, -0.5, 19.5, "No. of tracks per MC particle"}}}); - hrQaMc.add("hCentralityVzMcDeltaVz", "", {HistType::kTHnSparseF, {asCentrality, {static_cast(maxAbsVz) * 20, -maxAbsVz, maxAbsVz, "#it{V}_{#it{z}}^{Gen} (cm)"}, {200, -0.2, 0.2, "#it{V}_{#it{z}}^{Rec}#minus#it{V}_{#it{z}}^{Gen} (cm)"}}}); - hrQaMc.add("hCentralityPtMcEtaMcDeltaPt", "", {HistType::kTHnSparseF, {asCentrality, {200, 0., 2., "#it{p}_{T}^{Gen} (GeV/#it{c})"}, {24, -1.2, 1.2, "#it{#eta}_{Gen}"}, {320, -0.8, 0.8, "#it{p}_{T}^{Rec}#minus#it{p}_{T}^{Gen} (GeV/#it{c})"}}}); - hrQaMc.add("hCentralityPtMcEtaMcDeltaEta", "", {HistType::kTHnSparseF, {asCentrality, {20, 0., 2., "#it{p}_{T}^{Gen} (GeV/#it{c})"}, {240, -1.2, 1.2, "#it{#eta}_{Gen}"}, {160, -0.4, 0.4, "#it{#eta}_{Rec}#minus#it{#eta}_{Gen}"}}}); - } + hrQaMc.add("hNCollisionsPerMcCollision", "", {HistType::kTH1D, {{20, -0.5, 19.5, "No. of collisions per MC collision"}}}); + hrQaMc.add("hCentralityNTracksPerMcParticle", "", {HistType::kTHnSparseF, {asCentrality, {20, -0.5, 19.5, "No. of tracks per MC particle"}}}); + hrQaMc.add("hCentralityVzMcDeltaVz", "", {HistType::kTHnSparseF, {asCentrality, {static_cast(maxAbsVz) * 20, -maxAbsVz, maxAbsVz, "#it{V}_{#it{z}}^{Gen} (cm)"}, {200, -0.2, 0.2, "#it{V}_{#it{z}}^{Rec}#minus#it{V}_{#it{z}}^{Gen} (cm)"}}}); + hrQaMc.add("hCentralityPtMcEtaMcDeltaPt", "", {HistType::kTHnSparseF, {asCentrality, {200, 0., 2., "#it{p}_{T}^{Gen} (GeV/#it{c})"}, {24, -1.2, 1.2, "#it{#eta}_{Gen}"}, {320, -0.8, 0.8, "#it{p}_{T}^{Rec}#minus#it{p}_{T}^{Gen} (GeV/#it{c})"}}}); + hrQaMc.add("hCentralityPtMcEtaMcDeltaEta", "", {HistType::kTHnSparseF, {asCentrality, {20, 0., 2., "#it{p}_{T}^{Gen} (GeV/#it{c})"}, {240, -1.2, 1.2, "#it{#eta}_{Gen}"}, {160, -0.4, 0.4, "#it{#eta}_{Rec}#minus#it{#eta}_{Gen}"}}}); } for (std::int32_t const& iParticleSpecies : std::views::iota(0, NEs)) { @@ -1566,7 +1574,7 @@ struct PartNumFluc { LOG(fatal) << "Invalid gRunNumberGroupIndex!"; } for (std::int32_t const& iRun : std::views::iota(0, gRunNumberGroupIndex->GetN())) { - holderCcdb.runNumbersToIndicesRunAndGroup[static_cast(std::rint(gRunNumberGroupIndex->GetX()[iRun]))] = {iRun, static_cast(std::rint(gRunNumberGroupIndex->GetY()[iRun]))}; + holderCcdb.runNumbersToIndicesRunAndGroup[static_cast(std::rint(gRunNumberGroupIndex->GetPointX(iRun)))] = {iRun, static_cast(std::rint(gRunNumberGroupIndex->GetPointY(iRun)))}; } if (cgEvent.cfgFlagRejectionRunBadMc.value) { @@ -1575,8 +1583,8 @@ struct PartNumFluc { LOG(fatal) << "Invalid gRunNumberGroupIndex_mc!"; } for (std::int32_t const& iRun : std::views::iota(0, gRunNumberGroupIndexMc->GetN())) { - if (std::cmp_less_equal(static_cast(std::rint(gRunNumberGroupIndexMc->GetY()[iRun])), 0)) { - if (const auto iter{holderCcdb.runNumbersToIndicesRunAndGroup.find(static_cast(std::rint(gRunNumberGroupIndexMc->GetX()[iRun])))}; iter != holderCcdb.runNumbersToIndicesRunAndGroup.end() && std::cmp_greater(iter->second.second, 0)) { + if (std::cmp_less_equal(static_cast(std::rint(gRunNumberGroupIndexMc->GetPointY(iRun))), 0)) { + if (const auto iter{holderCcdb.runNumbersToIndicesRunAndGroup.find(static_cast(std::rint(gRunNumberGroupIndexMc->GetPointX(iRun))))}; iter != holderCcdb.runNumbersToIndicesRunAndGroup.end() && std::cmp_greater(iter->second.second, 0)) { iter->second.second = -iter->second.second; } } @@ -1768,7 +1776,7 @@ struct PartNumFluc { template requires IsValidEnumValue - bool isPid() const + bool isPid() const noexcept { if constexpr (ParticleSpeciesAllValue == ParticleSpeciesAll::All) { return ChargeSpeciesValue == ChargeSpecies::Plus ? std::cmp_greater(holderMcParticle.charge, 0) : std::cmp_less(holderMcParticle.charge, 0); @@ -1924,9 +1932,9 @@ struct PartNumFluc { fillByDcaAxis.template operator()(); } - template - requires IsValidEnumValue - void fillQaAcceptanceByParticleSpeciesAll(const T& track) + template + requires IsValidEnumValue && (DataModeValue == DataMode::McMcParticle || DataModeValue == DataMode::RawTrack) + void fillQaAcceptanceByParticleSpeciesAll() { if (!cgAnalysis.cfgFlagsQaAcceptance.value.get(toI(ParticleSpeciesAllValue))) { return; @@ -1935,13 +1943,29 @@ struct PartNumFluc { const auto fillByChargeSpecies{ [this] requires IsValidEnumValue - (const auto& name, const double input) -> void { + () -> void { const auto fillByPidStrategy{ - [this, input, &name] + [this] requires IsValidEnumValue () -> void { - if (isPid(PidStrategyValue), ParticleSpeciesAllValue>(false)) { - hrQaAcceptance.fill(C_CS("h") + name + C_CS("Pt_") + C_SV(getName(PidStrategyValue)) + C_CS("Edge") + C_SV(getName(ParticleSpeciesAllValue)) + C_SV(getName(ChargeSpeciesValue)), input, holderTrack.pt); // NOLINT(clang-analyzer-core.NonNullParamChecker) + if constexpr (DataModeValue == DataMode::McMcParticle) { + if (!isPid(PidStrategyValue), ParticleSpeciesAll::All>(false) || !isPid()) { + return; + } + + hrQaAcceptance.fill(C_CS("hEtaMcPtMc_") + C_SV(getName(PidStrategyValue)) + C_CS("Edge") + C_SV(getName(ParticleSpeciesAllValue)) + C_SV(getName(ChargeSpeciesValue)), holderMcParticle.eta, holderMcParticle.pt); + if constexpr (ParticleSpeciesAllValue != ParticleSpeciesAll::All) { + hrQaAcceptance.fill(C_CS("hRapidityMcPtMc_") + C_SV(getName(PidStrategyValue)) + C_CS("Edge") + C_SV(getName(ParticleSpeciesAllValue)) + C_SV(getName(ChargeSpeciesValue)), RecoDecayPtEtaPhi::y(holderMcParticle.pt, holderMcParticle.eta, getMass(getValue(ParticleSpeciesAllValue))), holderMcParticle.pt); + } + } else { // DataModeValue == DataMode::RawTrack + if (!isPid(PidStrategyValue), ParticleSpeciesAllValue>(false)) { + return; + } + + hrQaAcceptance.fill(C_CS("hEtaPt_") + C_SV(getName(PidStrategyValue)) + C_CS("Edge") + C_SV(getName(ParticleSpeciesAllValue)) + C_SV(getName(ChargeSpeciesValue)), holderTrack.eta, holderTrack.pt); + if constexpr (ParticleSpeciesAllValue != ParticleSpeciesAll::All) { + hrQaAcceptance.fill(C_CS("hRapidityPt_") + C_SV(getName(PidStrategyValue)) + C_CS("Edge") + C_SV(getName(ParticleSpeciesAllValue)) + C_SV(getName(ChargeSpeciesValue)), RecoDecayPtEtaPhi::y(holderTrack.pt, holderTrack.eta, getMass(getValue(ParticleSpeciesAllValue))), holderTrack.pt); + } } }}; @@ -1949,18 +1973,10 @@ struct PartNumFluc { fillByPidStrategy.template operator()(); }}; - if constexpr (ParticleSpeciesAllValue == ParticleSpeciesAll::All) { - if (std::cmp_greater(holderTrack.sign, 0)) { - fillByChargeSpecies.template operator()(C_CS("Eta"), holderTrack.eta); - } else { - fillByChargeSpecies.template operator()(C_CS("Eta"), holderTrack.eta); - } + if (std::cmp_greater(DataModeValue == DataMode::McMcParticle ? holderMcParticle.charge : holderTrack.sign, 0)) { + fillByChargeSpecies.template operator()(); } else { - if (std::cmp_greater(holderTrack.sign, 0)) { - fillByChargeSpecies.template operator()(C_CS("Rapidity"), track.rapidity(getMass(getValue(ParticleSpeciesAllValue)))); - } else { - fillByChargeSpecies.template operator()(C_CS("Rapidity"), track.rapidity(getMass(getValue(ParticleSpeciesAllValue)))); - } + fillByChargeSpecies.template operator()(); } } @@ -2056,7 +2072,7 @@ struct PartNumFluc { return; } - const std::int32_t chargeSign{[](const std::int32_t chargeMcParticle, const std::int32_t signTrack) constexpr -> std::int32_t { + const std::int32_t chargeSign{[](const std::int32_t chargeMcParticle, const std::int32_t signTrack) constexpr noexcept -> std::int32_t { if constexpr (DataModeValue == DataMode::McMcParticle) { return chargeMcParticle; } else { @@ -2190,7 +2206,7 @@ struct PartNumFluc { return; } - const std::int32_t chargeSign{[](const std::int32_t chargeMcParticle, const std::int32_t signTrack) constexpr -> std::int32_t { + const std::int32_t chargeSign{[](const std::int32_t chargeMcParticle, const std::int32_t signTrack) constexpr noexcept -> std::int32_t { if constexpr (DataModeValue == DataMode::McMcParticle) { return chargeMcParticle; } else { @@ -2198,7 +2214,7 @@ struct PartNumFluc { } }(holderMcParticle.charge, holderTrack.sign)}; - const bool doUseMcParticleMomentum{[](const bool flagMcParticleMomentum) constexpr -> bool { + const bool doUseMcParticleMomentum{[](const bool flagMcParticleMomentum) constexpr noexcept -> bool { if constexpr (DataModeValue == DataMode::McMcParticle) { return true; } else if constexpr (DataModeValue == DataMode::McTrack) { @@ -2224,7 +2240,7 @@ struct PartNumFluc { return false; } - const bool doUseTofPid{[](const bool doUseMcParticleMomentumValue, const double ptMcParticle, const double ptTrack, const double thresholdPtTofPid) constexpr -> bool { + const bool doUseTofPid{[](const bool doUseMcParticleMomentumValue, const double ptMcParticle, const double ptTrack, const double thresholdPtTofPid) constexpr noexcept -> bool { if constexpr (DataModeValue == DataMode::McMcParticle) { return ptMcParticle >= thresholdPtTofPid; } else if constexpr (DataModeValue == DataMode::McTrack) { @@ -2436,16 +2452,6 @@ struct PartNumFluc { if (!isGoodDca()) { return false; } - - if (holderMember.doQaAcceptance) { - const double vz{doProcessMc.value && cgEvent.cfgFlagMcCollisionVz.value ? holderMcEvent.vz : holderEvent.vz}; - if (holderTrack.eta * vz > 0. && std::abs(vz) > (doProcessMc.value && cgEvent.cfgFlagMcCollisionVz.value ? cgEvent.cfgCutMaxAbsVzMc.value : cgEvent.cfgCutMaxAbsVz.value) - 1.) { - fillQaAcceptanceByParticleSpeciesAll(track); - fillQaAcceptanceByParticleSpeciesAll(track); - fillQaAcceptanceByParticleSpeciesAll(track); - fillQaAcceptanceByParticleSpeciesAll(track); - } - } } return true; @@ -2834,6 +2840,22 @@ struct PartNumFluc { const auto& tracksMatched{tracks.sliceBy(psg.tracksPerMcParticle, mcParticle.globalIndex())}; + if (holderMember.doQaAcceptance && mcParticle.isPhysicalPrimary() && std::cmp_equal(tracksMatched.size(), 1)) { + const double vz{cgEvent.cfgFlagMcCollisionVz.value ? holderMcEvent.vz : holderEvent.vz}; + if (holderMcParticle.eta * vz > 0. && std::abs(vz) > (cgEvent.cfgFlagMcCollisionVz.value ? cgEvent.cfgCutMaxAbsVzMc.value : cgEvent.cfgCutMaxAbsVz.value) - 1.) { + for (const auto& track : tracksMatched) { + if (!setTrack(track)) { + continue; + } + + fillQaAcceptanceByParticleSpeciesAll(); + fillQaAcceptanceByParticleSpeciesAll(); + fillQaAcceptanceByParticleSpeciesAll(); + fillQaAcceptanceByParticleSpeciesAll(); + } + } + } + if (cgAnalysis.cfgFlagQaMc.value && (!cgTrack.cfgFlagMcParticlePhysicalPrimary.value || mcParticle.isPhysicalPrimary())) { hrQaMc.fill(C_CS("hCentralityNTracksPerMcParticle"), holderEvent.centralityCalibration, tracksMatched.size()); } @@ -2951,6 +2973,13 @@ struct PartNumFluc { continue; } + if (holderMember.doQaAcceptance && holderTrack.eta * holderEvent.vz > 0. && std::abs(holderEvent.vz) > cgEvent.cfgCutMaxAbsVz.value - 1.) { + fillQaAcceptanceByParticleSpeciesAll(); + fillQaAcceptanceByParticleSpeciesAll(); + fillQaAcceptanceByParticleSpeciesAll(); + fillQaAcceptanceByParticleSpeciesAll(); + } + if (holderMember.doQaPhi) { fillQaPhiByParticleSpeciesAll(); fillQaPhiByParticleSpeciesAll(); diff --git a/PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx b/PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx index 60955d61098..45f4d3b2aa8 100644 --- a/PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx +++ b/PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx @@ -16,7 +16,6 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" -#include "Common/DataModel/FT0Corrected.h" #include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/TrackSelectionTables.h" @@ -24,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -36,16 +36,22 @@ #include #include +#include +#include #include #include #include +#include #include +#include #include #include #include #include #include +#include + #include #include #include @@ -66,13 +72,16 @@ using namespace constants::math; struct RadialFlowDecorr { // --- fixed constants --------------------------------------------------------- - static constexpr int KnFt0cCell = 96; static constexpr int KIntM = 4; // pT-moment order used in the sums (need m up to 3 for c3) static constexpr int KIntK = 4; // weight-power order used in the sums (need k up to 3 for c3) static constexpr int KNEtaHalfBinsMax = 8; // 0.8 / 0.1: finest (DATA) bins per side static constexpr int KNEtaMax = 2 * KNEtaHalfBinsMax + 1; // + index-0 full-range reference bin + // Bootstrap: at most KMaxBoot replicas of 8 profiles each, in one registry of at most + // 512 objects. nBoot = min(cfgNBootstrap, KMaxBoot) replicas are filled. + static constexpr int KMaxBoot = 60; + static constexpr float KFloatEpsilon = 1e-6f; static constexpr float KEtaEdgeTolerance = 1e-3f; // slack for cfgCutEta being an integer multiple of the bin width static constexpr float KBinOffset = 0.5f; @@ -95,11 +104,15 @@ struct RadialFlowDecorr { static constexpr float KVzMax = 40.f; static constexpr int KNbinsEtaFine = 20; static constexpr float KEtaFineMax = 1.f; - static constexpr float KCentMax = 90; + // occupancy QA axis + static constexpr int KNbinsOccQA = 150; + static constexpr float KOccQAMax = 15000.f; - // Bootstrap: KMaxBoot is the compile-time storage cap; the number actually - // filled is nBoot = min(cfgNBootstrap, KMaxBoot). - static constexpr int KMaxBoot = 64; + // Forward detectors: FT0 channels 0-95 are A side, 96-207 C side; FV0-A has 48 cells. + static constexpr int KNChFT0A = 96; + static constexpr int KNChFT0 = 208; + static constexpr int KNChFV0 = 48; + static constexpr int KNFwdMax = 32; // detector totals + eta sub-bins enum ECentralityEstimator { kCentFT0C = 1, @@ -113,26 +126,234 @@ struct RadialFlowDecorr { kOO = 3, kpp = 4 }; + enum EITSLayerCut { + kITSLayerAuto = -1, // nominal: pp kIsGoodITSLayer0123, other systems kIsGoodITSLayersAll + kITSLayerNone = 0, + kITSLayer3 = 1, + kITSLayer0123 = 2, + kITSLayersAll = 3 + }; + enum EFwdDet { + kFT0A = 0, + kFT0C, + kFV0A, + kNFwdDet + }; + // Histogram level: DATA uses level 0 only; MC fills truth, reco and efficiency-corrected reco. + enum ELevel { + kGen = 0, + kReco, + kRecoEffCorr, + kNLevels + }; + // Last axis of the multi-dimensional objects. + enum EMeanObs { // Prof_Mean_Nch_etabin_obs + kMeanPt = 0, + kMeanN, + kNMeanObs + }; + enum EFlucObs { // Prof_Obs_Nch_etabin_obs + kObsN = 0, // N(eta), efficiency-corrected + kObsMeanPt, // [pT](eta) + kObsC2, // c2 within the bin + kObsC3, // c3 within the bin + kObsC2Sub, // , mirror pair, at the A bin + kObsC3Sub, // c2_A _C at A (AAB) and c2_C _A at C (ABB), mirror pair + kNFlucObs + }; + enum EEffCat { // efficiency maps h3_* (centrality, pT, eta) + kEffAllPrim = 0, // charged primaries of the MC collision + kEffRecoMatchedPrim, // reco tracks of primaries of the same MC collision, at the MC pT and eta + kEffAllReco, + kEffRecoSec, // secondaries of the same MC collision + kEffRecoOtherColl, // particles of another MC collision (pile-up, wrong association) + kEffRecoFake, // reco tracks without MC label + kNEffCat + }; + inline static const std::array effMapName = { + "h3_AllPrimary", "h3_RecoMatchedToPrimary", "h3_AllReco", "h3_RecoUnMatchedToPrimary_Secondary", + "h3_RecoUnMatchedToPrimary_OtherCollision", "h3_RecoUnMatchedToPrimary_Fake"}; + enum ETrackOrigin { // MCQA/Hist3D_Nch_pT_origin + kOriginPrimary = 0, + kOriginSecondary, + kOriginOtherColl, + kOriginNoLabel, + kNOrigin + }; + enum EVtxQA { // MCQA/Hist2D_dVz_type + kVtxSingle = 0, // the only reco collision of the MC collision + kVtxSplitBest, // the one used among several + kVtxSplitOther, // the others (skipped) + kNVtxQA + }; + enum EFlatType { // hFlatMaps + kFlatRaw = 0, + kFlatEffWtd, // (1 - fake) / eff (Base only) + kFlatWtd, // all weights of the pass + kNFlatType + }; + enum EChanQA { // FwdQA/Prof_Channel_qa + kChAmpRaw = 0, + kChAmpCorr, + kChEta, + kNChQA + }; + // Event cut flow (hEvtCount, x axis); the last two bins count selected events only + enum EEvtCut { + kEvAll = 0, + kEvSel8, + kEvVtxZ, + kEvGoodZvtx, + kEvTFBorder, + kEvITSROFBorder, + kEvVertexITSTPC, + kEvSameBunch, + kEvTimeRange, + kEvITSLayer, + kEvAddPileup, + kEvVariationCut, + kEvCent, + kEvInfoPrevRof, // selected events passing kNoHighMultCollInPrevRof + kEvInfoOccUnknown, // selected events with occupancy < 0 (time-frame border) + kNEvCut + }; + // Track cut flow (hTrkCount, x axis) + enum ETrkCut { + kTrkAll = 0, + kTrkSign, + kTrkHasColl, + kTrkPVContrib, + kTrkPtEta, + kTrkGlobal, + kTrkITS, + kTrkTPC, + kTrkTPCChi2, + kTrkDCAz, + kTrkVariationCut, + kTrkWeight, // valid efficiency (and flattening) weight: used + kNTrkCut + }; + + // Cut QA (processCutQA): each quantity the Base selection cuts on, before and after it. + enum ECutQAStage { + kQABefore = 0, // events: all collisions; tracks: all tracks of the selected events + kQAAfter, // Base-selected events / tracks + kNQAStage + }; + enum ECutQAEvt1D { + kQAVtxZ = 0, + kQACent, + kQAOccupancy, + kNQAEvt1D + }; + enum ECutQAEvt2D { + kQANchNPV = 0, + kQANchCent, + kNQAEvt2D + }; + enum ECutQATrk1D { + kQAPt = 0, + kQAEta, + kQAItsNCls, + kQAItsChi2, + kQATpcNClsFound, + kQATpcCrossedRows, + kQATpcCrossedRowsOverFindable, + kQATpcChi2, + kQAPVContributor, + kQAGlobalTrack, + kNQATrk1D + }; + enum ECutQATrk2D { + kQADcaXYPt = 0, + kQADcaZPt, + kNQATrk2D + }; - // Systematic-variation selector for DATA. Base is the main measurement; each - // variation reads its own DataMean object (systSuffix) for the correlation step. - // kSystEtaBinning additionally changes the eta bin width via cfgEtaBinWidth. + // Systematic variations, all evaluated in every job. Base is the nominal measurement. + // The cut values are the K* constants below; only the occupancy limits are configurable. + // Cent : FT0M instead of the nominal estimator (cfgCentralityChoice, FT0C) + // Chi2TPC : chi2 per TPC cluster < 2 instead of cfgCutTpcChi2NCl + // DCAxy : |DCAxy| < 5/7 x (globalTrack pT-dependent DCAxy cut), i.e. 5 instead of 7 sigma + // DCAz : |DCAz| < 1 cm instead of cfgCutTracKDcaMaxZ + // DCAxy and DCAz are null checks: with the nominal isPVContributor requirement even + // 5 sigma DCAxy or |DCAz| < 0.05 cm removes only ~0.2% of the selected tracks + // Eff : efficiency scaled by -10% at all pT (cancels in [pT]: a check) + // EffpTDep : efficiency scaled by -10% at cfgPtMin, linearly to 0 at 10 GeV/c + // Flat : no acceptance (flattening) weights + // ITSLayer : ITS good-layer requirement one step looser (All -> 0123, 0123 -> Layer3) + // NITS : itsNCls >= 4 instead of cfgITScluster + // NTPC : tpcNClsFound >= 80 instead of cfgTPCcluster + // Pileup : additionally kNoCollInRofStandard + // Vz : |z_vtx| < 8 cm instead of cfgVtxZCut + // NchBin2 : Base selection and inputs, observables (and its own mean pass) in N_ch bins + // of 2 (PbPb, OO) or 1 (pp) tracks: cross-check of the N_ch binning of the mean subtraction + // Occupancy : 0 <= trackOccupancyInTimeRange < cfgSystOccMax* of the system + // PileupNSigma : n-sigma pile-up bands narrower by 1 (mean and fluctuation passes) + // Since the observables are stored versus N_ch, Cent differs from Base only in the events + // kept (cent < cfgCentMax) and in its (centrality, N_ch) map, which defines its classes. enum ESystType { kSystBase = 0, - kSystDCA, + kSystCent, + kSystChi2TPC, + kSystDCAxy, + kSystDCAz, kSystEff, + kSystEffPtDep, kSystFlat, + kSystITSLayer, kSystNITS, kSystNTPC, kSystPileup, kSystVz, - kSystEtaBinning, - kNSystType + kSystNchBin, + kSystOccupancy, + kSystPileupNSigma, + kNSyst }; - // Suffix appended to the DataMean CCDB path per systematic (Base -> no suffix). - inline static const std::vector systSuffix = { - "", "_systDCA", "_systEff", "_systFlat", - "_systNITS", "_systNTPC", "_systPileup", "_systVz", "_systEtaBinning"}; + inline static const std::array systName = { + "Base", "systCent", "systChi2TPC", "systDCAxy", "systDCAz", "systEff", "systEffpTDep", + "systFlat", "systITSLayer", "systNITS", "systNTPC", "systPileup", "systVz", "systNchBin2", + "systOccupancy", "systPileupNSigma"}; + static constexpr uint32_t KAllSystMask = (1 << kNSyst) - 1; + static constexpr int KNSystPerRegistry = 4; // variations 1..15 are spread over 4 registries + // Cut of each variation (fixed; the occupancy limits are configurables, cfgSystOccMax*). + // Pileup adds kNoCollInRofStandard; ITSLayer loosens the nominal ITS good-layer requirement by one step. + static constexpr int KSystCentralityChoice = 2; // Cent: FT0M + static constexpr float KSystChi2TPC = 2.f; // Chi2TPC: chi2 per TPC cluster < 2 (nominal 2.5) + static constexpr float KSystDcaXyScale = 5.f / 7.f; // DCAxy: 5 sigma (nominal 7 sigma) + static constexpr float KSystDcaMaxZ = 1.f; // DCAz: |DCAz| < 1 cm (nominal 2 cm) + static constexpr float KEffVarPercent = -10.f; // Eff, EffpTDep: efficiency scaled by -10% + static constexpr float KEffVarPtZero = 10.f; // EffpTDep: variation falls linearly to 0 at 10 GeV/c + static constexpr int KSystITScluster = 4; // NITS: itsNCls >= 4 (nominal 5) + static constexpr int KSystTPCcluster = 80; // NTPC: tpcNClsFound >= 80 (nominal 70) + static constexpr float KSystVtxZCut = 8.f; // Vz: |z_vtx| < 8 cm (nominal 10 cm) + static constexpr int KSystNchBinWidthPbPb = 2; // NchBin2: N_ch bin width, PbPb (nominal 10) + static constexpr int KSystNchBinWidthOO = 2; // NchBin2: OO and NeNe (nominal 5) + static constexpr int KSystNchBinWidthPP = 1; // NchBin2: pp (nominal 2) + static constexpr float KSystPupnSigStep = 1.f; // PileupNSigma: n-sigma bands narrower by 1 + static constexpr double KMinBandEntries = 50.; // n-sigma band bins with fewer flattening-pass events apply no band cut + + // Output folder of a variation (Base keeps the top level). + static std::string systPrefix(int v) { return v == kSystBase ? std::string{} : systName[v] + "/"; } + // Suffix appended to every CCDB object path of a variation. + static std::string systSuffix(int v) { return v == kSystBase ? std::string{} : "_" + systName[v]; } + // Variation whose efficiency map v uses. Cent, Eff, EffpTDep, Flat, NchBin2 and PileupNSigma + // have the Base selection in the efficiency pass and use the Base map; Eff and EffpTDep scale + // it when applied. (The n-sigma bands are applied only in the mean and fluctuation passes.) + static constexpr int effSource(int v) + { + return (v == kSystCent || v == kSystEff || v == kSystEffPtDep || v == kSystFlat || v == kSystNchBin || v == kSystPileupNSigma) ? static_cast(kSystBase) : v; + } + // Variation whose flattening map v uses. Cent, NchBin2 and PileupNSigma use the Base map; + // Flat uses none (-1). + static constexpr int flatSource(int v) + { + if (v == kSystFlat) { + return -1; + } + return (v == kSystCent || v == kSystNchBin || v == kSystPileupNSigma) ? static_cast(kSystBase) : v; + } static constexpr float KinvalidCentrality = -1.0f; @@ -143,66 +364,118 @@ struct RadialFlowDecorr { int nBoot = 0; // active bootstrap samples (set in init()) bool doBoot = false; // bootstrap active for this run (base data fluc only) + // Variations filled by each pass (set in init()): maskEff in the MC efficiency pass, + // maskFlat in the flattening passes, maskAll in the mean and fluctuation passes. + uint32_t maskAll = 1; + uint32_t maskEff = 1; + uint32_t maskFlat = 1; + int itsLayerNom = kITSLayersAll; + int itsLayerVar = kITSLayer0123; + int occupancyMax = 0; // Occupancy variation cut of this system + float pupNSigma = 5.f; // width of the n-sigma pile-up bands of this system + int nchWidth = 10; // N_ch bin width of the observables of this system + int nchWidthNchBin2 = 2; // N_ch bin width of NchBin2 for this system + + // Forward bins: 0-2 detector totals (FT0A, FT0C, FV0A), then the eta sub-bins of each detector. + int nFwd = kNFwdDet; + std::array, kNFwdDet> fwdEdges; + std::array fwdOffset{}; + std::vector fwdLabels; + // --- configurables ----------------------------------------------------------- - Configurable cfgVtxZCut{"cfgVtxZCut", 10.f, "|z_{vtx}| acceptance (cm): collision filter + explicit event/particle vertex checks"}; + // Event selection (nominal) + struct : ConfigurableGroup { + Configurable cfgVtxZCut{"cfgVtxZCut", 10.f, "|z_{vtx}| acceptance (cm)"}; + Configurable cfgCentralityChoice{"cfgCentralityChoice", 1, "Which centrality estimator? 1-->FT0C, 2-->FT0M, 3-->FDDM, 4-->FV0A"}; + Configurable cfgCentMax{"cfgCentMax", 99.f, "Events with centrality below this value (%) are kept"}; + Configurable cfgIsGoodZvtxFT0VsPV{"cfgIsGoodZvtxFT0VsPV", true, "Good Vertexing cut"}; + Configurable cfgEvSelNoTimeFrameBorder{"cfgEvSelNoTimeFrameBorder", true, "kNoTimeFrameBorder"}; + Configurable cfgEvSelNoITSROFrameBorder{"cfgEvSelNoITSROFrameBorder", true, "kNoITSROFrameBorder"}; + Configurable cfgEvSelIsVertexITSTPC{"cfgEvSelIsVertexITSTPC", true, "kIsVertexITSTPC"}; + Configurable cfgEvSelNoSameBunchPileup{"cfgEvSelNoSameBunchPileup", true, "Pileup removal: kNoSameBunchPileup"}; + Configurable cfgEvSelNoCollInTimeRangeStd{"cfgEvSelNoCollInTimeRangeStd", true, "Pileup removal: kNoCollInTimeRangeStandard"}; + Configurable cfgITSLayerCut{"cfgITSLayerCut", -1, "ITS good-layer cut: -1 auto (pp 0123, else All), 0 none, 1 Layer3, 2 Layer0123, 3 LayersAll"}; + } cfgEvSel; + + // Pile-up cuts on the correlations of N_ch (global tracks) with N_PV and centrality. + // The linear cut is applied in every pass. The flattening passes (MC and DATA) fill + // Hist2D_globalTracks_cent and Hist2D_globalTracks_PVTracks after it; the mean +- n sigma + // bands of N_ch from these maps are then applied in the mean and fluctuation passes. + struct : ConfigurableGroup { + Configurable cfgPupnSigPbPb{"cfgPupnSigPbPb", 5.f, "Width of the n-sigma bands (number of RMS), PbPb"}; + Configurable cfgPupnSigOO{"cfgPupnSigOO", 5.f, "Width of the n-sigma bands (number of RMS), OO and NeNe"}; + Configurable cfgPupnSigPP{"cfgPupnSigPP", 6.f, "Width of the n-sigma bands (number of RMS), pp"}; + Configurable cfgApplySigPupCut{"cfgApplySigPupCut", 0, "Mean/Fluc: n-sigma band of N_ch versus centrality"}; + Configurable cfgApplySigPupCutPV{"cfgApplySigPupCutPV", 0, "Mean/Fluc: n-sigma band of N_ch versus N_PV"}; + Configurable cfgApplyLinPupCut{"cfgApplyLinPupCut", 0, "Linear cut of N_ch versus N_PV (every pass)"}; + Configurable cfgLinPupParam0{"cfgLinPupParam0", 3.0f, "(Upper) Linear Pileup Cut Const"}; + Configurable cfgLinPupParam1{"cfgLinPupParam1", 3.0f, "(Upper) Linear Pileup Slope"}; + Configurable cfgLinPupParam2{"cfgLinPupParam2", 3.0f, "(Lower) Linear Pileup Cut Const"}; + Configurable cfgLinPupParam3{"cfgLinPupParam3", 3.0f, "(Lower) Linear Pileup Slope"}; + } cfgPileup; + + // Track selection (nominal), on top of isGlobalTrack (Run 3 globalTrack, incl. its 7 sigma pT-dependent DCAxy cut) Configurable cfgPtMin{"cfgPtMin", 0.2f, "min pT for observables"}; Configurable cfgPtMax{"cfgPtMax", 5.0f, "max pT for observables"}; - Configurable cfgCutTracKDcaMaxZ{"cfgCutTracKDcaMaxZ", 2.0f, "Maximum DcaZ"}; - Configurable cfgCutTracKDcaMaxXY{"cfgCutTracKDcaMaxXY", 0.2f, "Maximum DcaXY"}; - - Configurable cfgPtDepDCAxy{"cfgPtDepDCAxy", false, "Use pt-dependent DCAxy cut"}; - Configurable cfgDcaXyP0{"cfgDcaXyP0", 0.0026f, "p0 for DCAxy"}; - Configurable cfgDcaXyP1{"cfgDcaXyP1", 0.005f, "p1 for DCAxy"}; - Configurable cfgDcaXyP2{"cfgDcaXyP2", 1.01f, "p2 for DCAxy"}; - - Configurable cfgPtDepDCAz{"cfgPtDepDCAz", false, "Use pt-dependent DCAz cut"}; - Configurable cfgDcaZP0{"cfgDcaZP0", 0.0026f, "p0 for DCAz"}; - Configurable cfgDcaZP1{"cfgDcaZP1", 0.005f, "p1 for DCAz"}; - Configurable cfgDcaZP2{"cfgDcaZP2", 1.01f, "p2 for DCAz"}; - - Configurable cfgITScluster{"cfgITScluster", 1, "Minimum Number of ITS cluster"}; - Configurable cfgTPCcluster{"cfgTPCcluster", 80, "Minimum Number of TPC cluster"}; - Configurable cfgTPCnCrossedRows{"cfgTPCnCrossedRows", 70, "Minimum Number of TPC crossed-rows"}; - Configurable cfgCutPtLower{"cfgCutPtLower", 0.2f, "Lower pT cut (track selection)"}; Configurable cfgCutPtUpper{"cfgCutPtUpper", 10.0f, "Higher pT cut (track selection)"}; Configurable cfgCutEta{"cfgCutEta", 0.8f, "absolute Eta cut"}; Configurable cfgEtaBinWidth{"cfgEtaBinWidth", 0.1f, "DATA only: width of each narrow observable eta bin (eta units); cfgCutEta must be an integer multiple so eta=0 is a bin edge. MC is pinned to 0.2."}; - Configurable cfgCentralityChoice{"cfgCentralityChoice", 1, "Which centrality estimator? 1-->FT0C, 2-->FT0M, 3-->FDDM, 4-->FV0A"}; - Configurable cfgEvSelNoSameBunchPileup{"cfgEvSelNoSameBunchPileup", true, "Pileup removal"}; - Configurable cfgUseGoodITSLayerAllCut{"cfgUseGoodITSLayerAllCut", true, "Remove time interval with dead ITS zone"}; - Configurable cfgIsGoodZvtxFT0VsPV{"cfgIsGoodZvtxFT0VsPV", true, "Good Vertexing cut"}; - - Configurable cfgPupnSig{"cfgPupnSig", 6.0f, "Additional Pileup Cut"}; - Configurable cfgApplySigPupCut{"cfgApplySigPupCut", 0, "nSig Pileup Cut"}; - Configurable cfgApplyLinPupCut{"cfgApplyLinPupCut", 0, "Lin Pileup Cut"}; - Configurable cfgLinPupParam0{"cfgLinPupParam0", 3.0f, "(Upper) Linear Pileup Cut Const"}; - Configurable cfgLinPupParam1{"cfgLinPupParam1", 3.0f, "(Upper) Linear Pileup Slope"}; - Configurable cfgLinPupParam2{"cfgLinPupParam2", 3.0f, "(Lower) Linear Pileup Cut Const"}; - Configurable cfgLinPupParam3{"cfgLinPupParam3", 3.0f, "(Lower) Linear Pileup Slope"}; - - Configurable cfgNchPbMax{"cfgNchPbMax", 5000, "Max Nch range for PbPb collisions"}; - Configurable cfgNchOMax{"cfgNchOMax", 800, "Max Nch range for OO collisions"}; + Configurable cfgCutTracKDcaMaxZ{"cfgCutTracKDcaMaxZ", 2.0f, "Maximum |DCAz| (cm)"}; + Configurable cfgITScluster{"cfgITScluster", 5, "Nominal: itsNCls >= value"}; + Configurable cfgTPCcluster{"cfgTPCcluster", 70, "Nominal: tpcNClsFound >= value"}; + Configurable cfgCutTpcChi2NCl{"cfgCutTpcChi2NCl", 2.5f, "Nominal: tpcChi2NCl < value"}; + Configurable cfgTPCnCrossedRows{"cfgTPCnCrossedRows", 70, "Minimum Number of TPC crossed-rows"}; + + // Systematic variations (names stay flat in the JSON) + struct : ConfigurableGroup { + Configurable cfgSystMask{"cfgSystMask", static_cast(KAllSystMask), "Bitmask of active variations, bit i = ESystType i: 0 Base (always on), 1 Cent, 2 Chi2TPC, 3 DCAxy, 4 DCAz, 5 Eff, 6 EffpTDep, 7 Flat, 8 ITSLayer, 9 NITS, 10 NTPC, 11 Pileup, 12 Vz, 13 NchBin2, 14 Occupancy, 15 PileupNSigma"}; + Configurable cfgDcaXyP0{"cfgDcaXyP0", 0.0105f, "globalTrack DCAxy cut p0 + p1 / pT^p2 (trackselection dcaSetup=0; dcaSetup=1: 0.004, 0.013, 1)"}; + Configurable cfgDcaXyP1{"cfgDcaXyP1", 0.035f, "globalTrack DCAxy cut p1"}; + Configurable cfgDcaXyP2{"cfgDcaXyP2", 1.1f, "globalTrack DCAxy cut p2"}; + Configurable cfgSystOccMaxPbPb{"cfgSystOccMaxPbPb", 2000, "Occupancy: maximum trackOccupancyInTimeRange, PbPb"}; + Configurable cfgSystOccMaxOO{"cfgSystOccMaxOO", 1000, "Occupancy: maximum trackOccupancyInTimeRange, OO and NeNe"}; + Configurable cfgSystOccMaxPP{"cfgSystOccMaxPP", 500, "Occupancy: maximum trackOccupancyInTimeRange, pp"}; + } cfgSyst; + + // N_ch axes. Largest N_ch (global tracks) seen in the data: 3542 (PbPb), 328 (OO), 72 (pp) + struct : ConfigurableGroup { + Configurable cfgNchPbMax{"cfgNchPbMax", 4000, "Max N_ch (global tracks) and N_PV of the axes, PbPb"}; + Configurable cfgNchOMax{"cfgNchOMax", 400, "Max N_ch (global tracks) and N_PV of the axes, OO and NeNe"}; + Configurable cfgNchPPMax{"cfgNchPPMax", 100, "Max N_ch (global tracks) and N_PV of the axes, pp"}; + Configurable cfgNchBinWidthPbPb{"cfgNchBinWidthPbPb", 10, "N_ch bin width (tracks) of the observables, PbPb; Mean and Fluc passes must use the same value"}; + Configurable cfgNchBinWidthOO{"cfgNchBinWidthOO", 5, "N_ch bin width (tracks) of the observables, OO and NeNe"}; + Configurable cfgNchBinWidthPP{"cfgNchBinWidthPP", 2, "N_ch bin width (tracks) of the observables, pp"}; + } cfgNch; Configurable cfgSys{"cfgSys", 1, "Which collision system? 1-->PbPb, 2-->NeNe, 3-->OO, 4-->pp"}; - Configurable cfgSystType{"cfgSystType", 0, "Systematic variation: 0=Base,1=systDCA,2=systEff,3=systFlat,4=systNEta,5=systNITS,6=systNTPC,7=systPileup,8=systVz,9=systEtaBinning"}; - Configurable cfgNBootstrap{"cfgNBootstrap", 16, "Number of Poisson bootstrap samples (base data run only)"}; + Configurable cfgNBootstrap{"cfgNBootstrap", 20, "Number of Poisson bootstrap samples (Base, DataFluc only)"}; Configurable cfgBootstrapSeed{"cfgBootstrapSeed", 0, "TRandom3 seed for bootstrap (0 = machine-random per job)"}; Configurable cfgFlat{"cfgFlat", false, "Whether to use flattening weights"}; Configurable cfgEff{"cfgEff", false, "Whether to use Efficiency weights"}; - Configurable cfgZDC{"cfgZDC", false, "Whether to use ZDC for pileup histograms"}; + + // Forward multiplicity (FT0A, FT0C, FV0A amplitudes) correlated with mid-rapidity [pT] and N + struct : ConfigurableGroup { + Configurable cfgFwd{"cfgFwd", true, "Mean/Fluc: forward amplitudes and their covariance with [pT](eta) and N(eta)"}; + Configurable cfgFwdGainEq{"cfgFwdGainEq", true, "DATA: apply the FT0/FV0 gain equalisation from cfgGainEqPath"}; + Configurable cfgGainEqPath{"cfgGainEqPath", "Analysis/EventPlane/GainEq", "CCDB path of the FT0/FV0 gain equalisation (+ /FT0, /FV0); central objects per run for LHC23zzh Pb-Pb and 2025 O-O/Ne-Ne"}; + Configurable cfgFwdVzCorr{"cfgFwdVzCorr", true, "Channel eta relative to the primary vertex (else to z = 0)"}; + Configurable> cfgFwdEtaEdgesFT0A{"cfgFwdEtaEdgesFT0A", {3.5f, 4.2f, 4.9f}, "FT0A eta sub-bin edges"}; + Configurable> cfgFwdEtaEdgesFT0C{"cfgFwdEtaEdgesFT0C", {-3.3f, -2.7f, -2.1f}, "FT0C eta sub-bin edges"}; + Configurable> cfgFwdEtaEdgesFV0A{"cfgFwdEtaEdgesFV0A", {2.2f, 2.7f, 3.2f, 3.9f, 4.5f, 5.1f}, "FV0A eta sub-bin edges"}; + } cfgFwdOpt; Configurable cfgCCDBurl{"cfgCCDBurl", "https://alice-ccdb.cern.ch", "ccdb url"}; Configurable cfgCCDBUserPath{"cfgCCDBUserPath", "/Users/s/somadutt", "Base CCDB path"}; - ConfigurableAxis cfgAxisCent{"cfgAxisCent", {0.0, 1.0, 5.0, 10, 20, 40, 60, 80, 100}, "centrality axis (percentile)"}; - // --- axes -------------------------------------------------------------------- - AxisSpec centAxis{cfgAxisCent, "Centrality (%)"}; AxisSpec centAxis1Per{100, 0.0, 100.0, "Centrality (%)"}; - AxisSpec nChAxis{1, 0., 1., "Nch", "Nch"}; - AxisSpec nChAxis2{1, 0., 1., "Nch", "Nch"}; + AxisSpec centAxisBoot{20, 0.0, 100.0, "Centrality (%)"}; // bootstrap replicas: final 5% classes + AxisSpec multQAAxis{1, 0., 1., "Nch", "Nch"}; // QA (N_PV correlations) + AxisSpec nchAxisObs{1, 0., 1., "N_{ch}"}; // every observable (bins of nchWidth) + AxisSpec nchAxisNchBin2{1, 0., 1., "N_{ch}"}; // NchBin2 (bins of nchWidthNchBin2) + AxisSpec centAxisEff{{0., 5., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100.}, "Centrality (%)"}; // efficiency maps AxisSpec vzAxis{5, -12.5, 12.5, "Vz"}; AxisSpec chgAxis{3, -1.5, 1.5}; @@ -217,13 +490,9 @@ struct RadialFlowDecorr { // binning and are rebuilt in init(). Placeholders here. AxisSpec etaAxis{9, -0.9, 0.9, "#eta"}; AxisSpec etaBinAxis{10, -0.5, 9.5, "#eta bin Number"}; - - AxisSpec gapAxis{{-1.5, -1.3, -1.1, -0.9, -0.7, -0.5, -0.3, -0.1, - 0.1, 0.3, 0.5, 0.7, 0.9, 1.1, 1.3, 1.5}, - "Gap"}; - AxisSpec sumAxis{{-1.5, -1.3, -1.1, -0.9, -0.7, -0.5, -0.3, -0.1, - 0.1, 0.3, 0.5, 0.7, 0.9, 1.1, 1.3, 1.5}, - "Sum"}; + AxisSpec gapAxis{15, -1.5, 1.5, "#Delta#eta"}; + AxisSpec sumAxis{15, -1.5, 1.5, "#Sigma#eta"}; + AxisSpec fwdAxis{kNFwdDet, -0.5, static_cast(kNFwdDet) - 0.5, "forward bin"}; // --- process switches -------------------------------------------------------- Configurable cfgRunGetEff{"cfgRunGetEff", false, "Run MC pass to build efficiency/fake maps"}; @@ -237,194 +506,419 @@ struct RadialFlowDecorr { Service ccdb{}; Service pdg{}; + + // histos: Base and QA; histosBoot: bootstrap replicas; histosSystA-D: the variations, + // 4 per registry (a registry holds at most 512 objects). HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry histosBoot{"HistosBoot", {}, OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry histosSystA{"HistosSystA", {}, OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry histosSystB{"HistosSystB", {}, OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry histosSystC{"HistosSystC", {}, OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry histosSystD{"HistosSystD", {}, OutputObjHandlingPolicy::AnalysisObject}; + + HistogramRegistry& reg(int v) + { + if (v == kSystBase) { + return histos; + } + switch ((v - 1) / KNSystPerRegistry) { + case 0: + return histosSystA; + case 1: + return histosSystB; + case 2: + return histosSystC; + default: + return histosSystD; + } + } + + const AxisSpec& nchAxisOf(int v) const { return v == kSystNchBin ? nchAxisNchBin2 : nchAxisObs; } + int nchBinWidthOf(int v) const { return v == kSystNchBin ? nchWidthNchBin2 : nchWidth; } TRandom3 rng; // bootstrap Poisson weights - // --- persistent state -------------------------------------------------------- - struct InternalState { - TH3F* hEff = nullptr; - TH3F* hFake = nullptr; - THnSparseF* hFlatWeight = nullptr; - - std::vector> mLimitsNchCent; - float mMinXNchCent = 0, mMaxXNchCent = 0; - - // MC mean maps (per eta bin): truth / reco / reco-eff-corrected - TProfile2D* pmeanTruNchEtabinStep2 = nullptr; - TProfile2D* pmeanRecoNchEtabinStep2 = nullptr; - TProfile2D* pmeanRecoEffcorrNchEtabinStep2 = nullptr; - TProfile2D* pmeanMultTruNchEtabinStep2 = nullptr; - TProfile2D* pmeanMultRecoNchEtabinStep2 = nullptr; - TProfile2D* pmeanMultRecoEffcorrNchEtabinStep2 = nullptr; - - // Data mean maps (per eta bin) - TProfile2D* pmeanNchEtabinStep2 = nullptr; - TProfile2D* pmeanMultNchEtabinStep2 = nullptr; - - TProfile* pmeanFT0AmultpvStep2 = nullptr; - TProfile* pmeanFT0CmultpvStep2 = nullptr; - } state; - o2::ft0::Geometry ft0Det; - - // --- bootstrap replica storage (base data fluc only) ------------------------- - struct BootstrapHists { - std::array, KMaxBoot> amplFT0ACent{}; - std::array, KMaxBoot> amplFT0AMult{}; - std::array, KMaxBoot> amplFT0CCent{}; - std::array, KMaxBoot> amplFT0CMult{}; - std::array, KMaxBoot> multCent{}; - std::array, KMaxBoot> multMult{}; - std::array, KMaxBoot> meanpTCent{}; - std::array, KMaxBoot> meanpTMult{}; - std::array, KMaxBoot> c2Cent{}; - std::array, KMaxBoot> c2Mult{}; - std::array, KMaxBoot> c3Cent{}; - std::array, KMaxBoot> c3Mult{}; - std::array, KMaxBoot> c2SubCent{}; - std::array, KMaxBoot> c2SubMult{}; - std::array, KMaxBoot> c3SubCent{}; - std::array, KMaxBoot> c3SubMult{}; - std::array, KMaxBoot> covCent{}; - std::array, KMaxBoot> covMult{}; - std::array, KMaxBoot> covFT0ACent{}; - std::array, KMaxBoot> covFT0AMult{}; - std::array, KMaxBoot> covFT0CCent{}; - std::array, KMaxBoot> covFT0CMult{}; - std::array, KMaxBoot> c2Sub2D{}; - std::array, KMaxBoot> c3Sub2D{}; - std::array, KMaxBoot> gapSum2D{}; - std::array, KMaxBoot> c3GapSum2D{}; - std::array, KMaxBoot> cov2D{}; - std::array, KMaxBoot> covFT0A2D{}; - std::array, KMaxBoot> covFT0C2D{}; - } bs; + // --- per-variation inputs ---------------------------------------------------- + struct SystInputs { + // efficiency and fake rate versus (centrality, pT, eta), and integrated over centrality (one + // centrality bin: used where the cell is empty or not a valid efficiency) + TH3* hEff = nullptr; + TH3* hFake = nullptr; + TH3* hEffPtEta = nullptr; + TH3* hFakePtEta = nullptr; + THnSparseF* hFlat = nullptr; + // mean pass, per level: <[pT]> and vs (N_ch, eta bin), vs (N_ch, forward bin) + std::array pmean{}; + std::array pmeanFwd{}; + }; + std::array inputs{}; + + // mean +- n sigma of N_ch per bin of x (centrality or N_PV), from the flattening pass + struct NSigmaBand { + std::vector> meanRms; // per bin of x; rms < 0 for an empty bin + float xMin = 0.f, xMax = 0.f; + bool applied = false; + }; + enum EPileupBand { + kBandCent = 0, + kBandPV, + kNBands + }; + std::array pileupBands{}; + + // --- per-variation histograms, filled through pointers (HIST() needs names known at compile time) --- + struct EffHists { + std::array, kNEffCat> maps{}; // (centrality, pT, eta), one per EEffCat + }; + struct FlatHists { + std::shared_ptr maps; // (vz, sign, pT, eta, phi, EFlatType) + }; + struct MeanHists { + std::shared_ptr mean; // (N_ch, eta bin, EMeanObs) + std::shared_ptr fwdMean; // (N_ch, forward bin): + }; + struct FlucHists { + std::shared_ptr obs; // (N_ch, eta bin, EFlucObs) + std::shared_ptr c2Sub2D; // (N_ch, eta_A, eta_C) + std::shared_ptr c3Sub2D; // (N_ch, eta_A pair, eta_C single) + std::shared_ptr gapSum2D; // (N_ch, Delta eta, Sigma eta) of the C2 map + std::shared_ptr c3GapSum2D; // (N_ch, eta_pair - eta_single, eta_pair + eta_single) + std::shared_ptr fwdVar; // (N_ch, forward bin): + std::shared_ptr covFwdPt; // (N_ch, eta bin, forward bin): + std::shared_ptr covFwdN; // (N_ch, eta bin, forward bin): + }; + struct { + std::shared_ptr evtCount; // (EEvtCut, variation) + std::shared_ptr trkCount; // (ETrkCut, variation) + std::array, kNSyst> evtCount1D{}; // EEvtCut, one per variation + std::array, kNSyst> trkCount1D{}; // ETrkCut, one per variation + std::shared_ptr nchOccupancy; // (N_ch, occupancy) of the Base events + std::array, kNQAStage>, kNQAEvt1D> qaEvt1D{}; + std::array, kNQAStage>, kNQAEvt2D> qaEvt2D{}; + std::array, kNQAStage>, kNQATrk1D> qaTrk1D{}; + std::array, kNQAStage>, kNQATrk2D> qaTrk2D{}; + std::array, kNSyst> nchCent{}; // (centrality, N_ch) per variation + std::array eff{}; + std::array flat{}; + std::array, kNSyst> mean{}; + std::array, kNSyst> fluc{}; + std::array boot{}; + std::shared_ptr fwdChannel; // (channel: FT0 0-207, FV0 208-255; EChanQA) + std::shared_ptr fwdEta; // amplitude-weighted eta per forward bin + std::shared_ptr mcTrackOrigin; // (N_ch, pT, ETrackOrigin) of the Base tracks + std::shared_ptr mcVtx; // (z_reco - z_true, EVtxQA) + } hists; + + // --- forward detector calibration (per run) ---------------------------------- + struct { + o2::ft0::Geometry ft0Det; + o2::fv0::Geometry* fv0Det = nullptr; + int run = -1; + std::array, KNChFT0> posFT0{}; + std::array, KNChFV0> posFV0{}; + std::array gainFT0{}; + std::array gainFV0{}; + } fwdCalib; + + // --- per-event work arrays --------------------------------------------------- + // Power sums S_{m,k} = sum w^k pT^m per eta bin. + struct PowerSums { + std::array, KIntM>, KNEtaMax> pmwk{}; + std::array, KNEtaMax> wk{}; + void reset() + { + for (auto& a : pmwk) { + for (auto& b : a) { + b.fill(0.); + } + } + for (auto& a : wk) { + a.fill(0.); + } + } + void add(int ieta, double pt, double w) + { + double wPow = 1.; + for (int k = 0; k < KIntK; ++k) { + double ptPow = 1.; + for (int m = 0; m < KIntM; ++m) { + pmwk[ieta][m][k] += wPow * ptPow; + ptPow *= pt; + } + wk[ieta][k] += wPow; + wPow *= w; + } + } + }; + std::array, kNSyst> sums{}; + // track cut flow of the current event, added to hTrkCount once per event + std::array, kNSyst> trkCounts{}; + + struct FwdSignal { + std::array ampl{}; // amplitude (data, MC reco) or number of particles (MC truth) + std::array valid{}; // detector read out in this event + }; + + struct EventSelection { + uint32_t mask = 0; + std::array cent{}; + int occupancy = -1; + }; + + // Per-event observables of one (variation, level). + struct EventObs { + float nCh = 0.f; + std::array nW{}, mean{}, c2{}, c3{}, p1kBar{}, dN{}; + std::array fwd{}, dFwd{}; + std::array fwdValid{}; + }; // =========================================================================== // Selection helpers // =========================================================================== template - bool isEventSelected(const T& col) + static bool passItsLayerCut(int cut, const T& col) { - histos.fill(HIST("hEvtCount"), 0.5); - if (!col.sel8()) { - return false; + switch (cut) { + case kITSLayer3: + return col.selection_bit(o2::aod::evsel::kIsGoodITSLayer3); + case kITSLayer0123: + return col.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123); + case kITSLayersAll: + return col.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll); + default: + return true; } - histos.fill(HIST("hEvtCount"), 1.5); - if (std::abs(col.posZ()) > cfgVtxZCut) { + } + + // N_ch within mean +- nSigma RMS at x (events outside the x range of the band are rejected, + // empty bins of the band accept every event). + static bool insideBand(const NSigmaBand& band, float x, float nCh, float nSigma) + { + const auto& mr = band.meanRms; + int bin = 1 + static_cast((x - band.xMin) / (band.xMax - band.xMin) * (mr.size() - 2)); + if (bin < 1 || bin >= static_cast(mr.size() - 1)) { return false; } - histos.fill(HIST("hEvtCount"), 2.5); - if (cfgEvSelNoSameBunchPileup && !col.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { - return false; + if (mr[bin].second < 0.f) { + return true; } - histos.fill(HIST("hEvtCount"), 3.5); - if (cfgUseGoodITSLayerAllCut && !col.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + return std::abs(nCh - mr[bin].first) <= nSigma * mr[bin].second; + } + + // Linear cut and n-sigma bands of width nSigma. + bool isPassAddPileup(float multPV, int trksize, float cent, float nSigma) + { + if (pileupBands[kBandCent].applied && !insideBand(pileupBands[kBandCent], cent, trksize, nSigma)) { return false; } - histos.fill(HIST("hEvtCount"), 4.5); - if (cfgIsGoodZvtxFT0VsPV && !col.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + if (pileupBands[kBandPV].applied && !insideBand(pileupBands[kBandPV], multPV, trksize, nSigma)) { return false; } - histos.fill(HIST("hEvtCount"), 5.5); + if (cfgPileup.cfgApplyLinPupCut) { + if (trksize > (cfgPileup.cfgLinPupParam0 + cfgPileup.cfgLinPupParam1 * multPV)) { + return false; + } + if (trksize < (cfgPileup.cfgLinPupParam2 + cfgPileup.cfgLinPupParam3 * multPV)) { + return false; + } + } return true; } - bool isPassAddPileup(float multPV, int trksize, float cent) + // One event at cut-flow stage s of variation v: hEvtCountSyst and that variation's hEvtCount. + void countEvent(int s, int v) { - auto checkLimits = [](float x, float y, const std::vector>& limits, float xM, float xMx) { - if (limits.empty()) { - return true; - } - int bin = 1 + static_cast((x - xM) / (xMx - xM) * (limits.size() - 2)); - if (bin < 1 || bin >= static_cast(limits.size() - 1)) { - return false; + hists.evtCount->Fill(s, v); + if (hists.evtCount1D[v]) { + hists.evtCount1D[v]->Fill(s); + } + } + + // Event selection of all active variations: returns the variations the event passes and its + // centrality for each of them. The cuts common to all variations come first. The event cut + // flow of every active variation is filled unless count is false (cut QA). + template + EventSelection isEventSelected(const T& col, int nCh, uint32_t active, bool count = true) + { + EventSelection ev; + auto countAll = [&](int s) { + for (int v = 0; v < kNSyst && count; ++v) { + if (TESTBIT(active, v)) { + countEvent(s, v); + } } - return (y >= limits[bin].first && y <= limits[bin].second); }; - if (cfgApplySigPupCut) { - if (!checkLimits(cent, trksize, state.mLimitsNchCent, state.mMinXNchCent, state.mMaxXNchCent)) { - return false; - } - histos.fill(HIST("hEvtCount"), 6.5); + countAll(kEvAll); + if (!col.sel8()) { + return ev; } - if (cfgApplyLinPupCut) { - if (trksize > (cfgLinPupParam0 + cfgLinPupParam1 * multPV)) { - return false; + countAll(kEvSel8); + if (std::abs(col.posZ()) > cfgEvSel.cfgVtxZCut) { + return ev; + } + countAll(kEvVtxZ); + if (cfgEvSel.cfgIsGoodZvtxFT0VsPV && !col.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return ev; + } + countAll(kEvGoodZvtx); + if (cfgEvSel.cfgEvSelNoTimeFrameBorder && !col.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + return ev; + } + countAll(kEvTFBorder); + if (cfgEvSel.cfgEvSelNoITSROFrameBorder && !col.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + return ev; + } + countAll(kEvITSROFBorder); + if (cfgEvSel.cfgEvSelIsVertexITSTPC && !col.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + return ev; + } + countAll(kEvVertexITSTPC); + + const float centBase = getCentrality(col, cfgEvSel.cfgCentralityChoice); + const float multPV = col.multNTracksPV(); + ev.occupancy = col.trackOccupancyInTimeRange(); + const bool passAddPileup = isPassAddPileup(multPV, nCh, centBase, pupNSigma); + const bool passAddPileupTight = isPassAddPileup(multPV, nCh, centBase, pupNSigma - KSystPupnSigStep); + + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(active, v)) { + continue; } - histos.fill(HIST("hEvtCount"), 7.5); - if (trksize < (cfgLinPupParam2 + cfgLinPupParam3 * multPV)) { - return false; + auto countOne = [&](int s) { + if (count) { + countEvent(s, v); + } + }; + const float cent = (v == kSystCent) ? getCentrality(col, KSystCentralityChoice) : centBase; + ev.cent[v] = cent; + if (cfgEvSel.cfgEvSelNoSameBunchPileup && !col.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + continue; + } + countOne(kEvSameBunch); + if (cfgEvSel.cfgEvSelNoCollInTimeRangeStd && !col.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + continue; + } + countOne(kEvTimeRange); + if (!passItsLayerCut(v == kSystITSLayer ? itsLayerVar : itsLayerNom, col)) { + continue; + } + countOne(kEvITSLayer); + if (!(v == kSystPileupNSigma ? passAddPileupTight : passAddPileup)) { + continue; + } + countOne(kEvAddPileup); + // the cut of the event-selection variations + if (v == kSystPileup && !col.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + continue; + } + if (v == kSystVz && !(std::abs(col.posZ()) < KSystVtxZCut)) { + continue; + } + if (v == kSystOccupancy && (ev.occupancy < 0 || ev.occupancy >= occupancyMax)) { + continue; + } + countOne(kEvVariationCut); + if (!(cent < cfgEvSel.cfgCentMax)) { + continue; + } + countOne(kEvCent); + SETBIT(ev.mask, v); + if (col.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + countOne(kEvInfoPrevRof); + } + if (ev.occupancy < 0) { + countOne(kEvInfoOccUnknown); } - histos.fill(HIST("hEvtCount"), 8.5); } - return true; + return ev; } + // Track selection of all variations the event passed (evMask): returns the variations the + // track passes. The cuts common to all variations come first. The cut flow is collected in + // trkCounts (unless count is false: cut QA) and added to the track counters by flushTrackCounts(). template - bool isTrackSelected(const T& trk) + uint32_t isTrackSelected(const T& trk, uint32_t evMask, bool count = true) { - histos.fill(HIST("hTrkCount"), 0.5); + const double inc = count ? 1. : 0.; + auto countAll = [&](int s) { + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(evMask, v)) { + trkCounts[v][s] += inc; + } + } + }; + countAll(kTrkAll); if (trk.sign() == 0) { - return false; + return 0; } - histos.fill(HIST("hTrkCount"), 1.5); + countAll(kTrkSign); if (!trk.has_collision()) { - return false; + return 0; } - histos.fill(HIST("hTrkCount"), 2.5); + countAll(kTrkHasColl); if (!trk.isPVContributor()) { - return false; - } - histos.fill(HIST("hTrkCount"), 3.5); - if (!(trk.itsNCls() > cfgITScluster)) { - return false; - } - histos.fill(HIST("hTrkCount"), 4.5); - if (!(trk.tpcNClsFound() >= cfgTPCcluster)) { - return false; - } - histos.fill(HIST("hTrkCount"), 5.5); - if (!(trk.tpcNClsCrossedRows() >= cfgTPCnCrossedRows)) { - return false; + return 0; } - histos.fill(HIST("hTrkCount"), 6.5); + countAll(kTrkPVContrib); if (trk.pt() < cfgCutPtLower || trk.pt() > cfgCutPtUpper || std::abs(trk.eta()) > cfgCutEta) { - return false; + return 0; } - histos.fill(HIST("hTrkCount"), 7.5); - if (!trk.isGlobalTrack()) { - return false; + countAll(kTrkPtEta); + if (!trk.isGlobalTrack() || trk.tpcNClsCrossedRows() < cfgTPCnCrossedRows) { + return 0; } - histos.fill(HIST("hTrkCount"), 8.5); + countAll(kTrkGlobal); - if (cfgPtDepDCAxy) { - float maxDcaXY = cfgDcaXyP0 + cfgDcaXyP1 / std::pow(trk.pt(), cfgDcaXyP2); - if (std::abs(trk.dcaXY()) > maxDcaXY) { - return false; + const float maxDcaXY = KSystDcaXyScale * (cfgSyst.cfgDcaXyP0 + cfgSyst.cfgDcaXyP1 / std::pow(trk.pt(), cfgSyst.cfgDcaXyP2.value)); + uint32_t mask = 0; + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(evMask, v)) { + continue; } - histos.fill(HIST("hTrkCount"), 9.5); - } else { - if (std::abs(trk.dcaXY()) > cfgCutTracKDcaMaxXY) { - return false; + if (!(trk.itsNCls() >= (v == kSystNITS ? KSystITScluster : cfgITScluster.value))) { + continue; } - histos.fill(HIST("hTrkCount"), 9.5); - } - if (cfgPtDepDCAz) { - float maxDcaZ = cfgDcaZP0 + cfgDcaZP1 / std::pow(trk.pt(), cfgDcaZP2); - if (std::abs(trk.dcaZ()) > maxDcaZ) { - return false; + trkCounts[v][kTrkITS] += inc; + if (!(trk.tpcNClsFound() >= (v == kSystNTPC ? KSystTPCcluster : cfgTPCcluster.value))) { + continue; } - histos.fill(HIST("hTrkCount"), 10.5); - } else { - if (std::abs(trk.dcaZ()) > cfgCutTracKDcaMaxZ) { - return false; + trkCounts[v][kTrkTPC] += inc; + if (!(trk.tpcChi2NCl() < (v == kSystChi2TPC ? KSystChi2TPC : cfgCutTpcChi2NCl.value))) { + continue; + } + trkCounts[v][kTrkTPCChi2] += inc; + if (!(std::abs(trk.dcaZ()) < (v == kSystDCAz ? KSystDcaMaxZ : cfgCutTracKDcaMaxZ.value))) { + continue; + } + trkCounts[v][kTrkDCAz] += inc; + // the cut of the DCAxy variation + if (v == kSystDCAxy && !(std::abs(trk.dcaXY()) < maxDcaXY)) { + continue; + } + trkCounts[v][kTrkVariationCut] += inc; + SETBIT(mask, v); + } + return mask; + } + + // Adds the track cut flow of the event to hTrkCountSyst and hTrkCount. + void flushTrackCounts() + { + for (int v = 0; v < kNSyst; ++v) { + for (int s = 0; s < kNTrkCut; ++s) { + if (trkCounts[v][s] > 0.) { + hists.trkCount->Fill(s, v, trkCounts[v][s]); + if (hists.trkCount1D[v]) { + hists.trkCount1D[v]->Fill(s, trkCounts[v][s]); + } + trkCounts[v][s] = 0.; + } } - histos.fill(HIST("hTrkCount"), 10.5); } - return true; } + // Charged primary in the observable acceptance (event vertex cuts are applied to the reco collision). template bool isParticleSelected(const T& particle) { @@ -438,100 +932,125 @@ struct RadialFlowDecorr { if (particle.pt() < cfgCutPtLower || particle.pt() > cfgCutPtUpper || std::abs(particle.eta()) > cfgCutEta) { return false; } - if (std::abs(particle.vz()) > cfgVtxZCut) { - return false; - } return true; } - float getCentrality(const auto& col) const + // Origin of a reco MC track (ETrackOrigin) with respect to the MC collision of coll. + template + int trackOrigin(const T& track, const C& coll) const + { + if (!track.has_mcParticle()) { + return kOriginNoLabel; + } + const auto& particle = track.mcParticle(); + if (particle.mcCollisionId() != coll.mcCollisionId()) { + return kOriginOtherColl; + } + return particle.isPhysicalPrimary() ? kOriginPrimary : kOriginSecondary; + } + + float getCentrality(const auto& col, int choice) const { - if (cfgCentralityChoice.value == kCentFT0C) { + if (choice == kCentFT0C) { return col.centFT0C(); } - if (cfgCentralityChoice.value == kCentFT0M) { + if (choice == kCentFT0M) { return col.centFT0M(); } - if (cfgCentralityChoice.value == kCentFDDM) { + if (choice == kCentFDDM) { return col.centFDDM(); } - if (cfgCentralityChoice.value == kCentFV0A) { + if (choice == kCentFV0A) { return col.centFV0A(); } return KinvalidCentrality; } - // Inclusive efficiency/fake lookup (no species dependence). - float getEfficiency(float mult, float pt, float eta, - int effidx, bool useEff) const + // Observable eta bins containing eta: the full-range bin 0 and one narrow bin. + int etaBinsOf(float eta, std::array& bins) const { - if (!useEff) { - return (effidx == 0) ? 1.0f : 0.0f; + int n = 0; + for (int ieta = 0; ieta < nEta && n < 2; ++ieta) { + if (eta > etaLw[ieta] && eta <= etaUp[ieta]) { + bins[n++] = ieta; + } } + return n; + } - const float invalid = - std::numeric_limits::quiet_NaN(); - - TH3F* h = (effidx == 0) ? state.hEff : state.hFake; - - if (!h || !std::isfinite(mult) || - !std::isfinite(pt) || !std::isfinite(eta)) { + // =========================================================================== + // Corrections + // =========================================================================== + // Inclusive efficiency/fake lookup (no species dependence). + static float getEfficiency(const TH3* h, float cent, float pt, float eta, bool isEff) + { + const float invalid = std::numeric_limits::quiet_NaN(); + if (!h || !std::isfinite(cent) || !std::isfinite(pt) || !std::isfinite(eta)) { return invalid; } - - int ibx = h->GetXaxis()->FindFixBin(mult); - - // Reject Nch underflow, clamp overflow. + int ibx = h->GetXaxis()->FindFixBin(cent); + // Reject centrality underflow, clamp overflow. if (ibx < 1) { return invalid; } - ibx = std::min(ibx, h->GetNbinsX()); - int iby = h->GetYaxis()->FindFixBin(pt); int ibz = h->GetZaxis()->FindFixBin(eta); - // Do not extrapolate in pT or eta. - if (iby < 1 || iby > h->GetNbinsY() || - ibz < 1 || ibz > h->GetNbinsZ()) { + if (iby < 1 || iby > h->GetNbinsY() || ibz < 1 || ibz > h->GetNbinsZ()) { return invalid; } - const double val = h->GetBinContent(ibx, iby, ibz); const double err = h->GetBinError(ibx, iby, ibz); - - // A bin without a positive error is not a valid measurement. - if (!std::isfinite(val) || - !std::isfinite(err) || err <= 0.0) { + // A bin without a positive error is not a valid measurement, except a fake rate of 0 + // (no fake track in the bin), which has no error. + if (!std::isfinite(val) || !std::isfinite(err) || (err <= 0.0 && (isEff || val != 0.0))) { return invalid; } - // Physical-domain checks. - if (val < 0.0 || val > 1.0 || - (effidx == 0 && val == 0.0)) { + if (val < 0.0 || val > 1.0 || (isEff && val == 0.0)) { return invalid; } - return static_cast(val); } - // Reject missing or invalid efficiency/fake corrections consistently in all passes. - bool getValidEffFake(float mult, float pt, float eta, bool useEff, - float& eff, float& fake) const + // Nominal efficiency and fake rate of the map of variation v (no variation factor). + bool getValidEffFake(int v, float cent, float pt, float eta, float& eff, float& fake) { - eff = getEfficiency(mult, pt, eta, 0, useEff); - fake = getEfficiency(mult, pt, eta, 1, useEff); - return std::isfinite(eff) && std::isfinite(fake) && - eff > KFloatEpsilon && eff <= 1.f && - fake >= 0.f && fake < 1.f; + if (!cfgEff) { + eff = 1.f; + fake = 0.f; + return true; + } + const auto& in = inputs[v]; + auto valid = [&]() { return std::isfinite(eff) && std::isfinite(fake) && eff > KFloatEpsilon && eff <= 1.f && fake >= 0.f && fake < 1.f; }; + eff = getEfficiency(in.hEff, cent, pt, eta, true); + fake = getEfficiency(in.hFake, cent, pt, eta, false); + if (valid()) { + return true; + } + // empty or statistically invalid cell, or no valid centrality: the map integrated over centrality + const float centAll = in.hEffPtEta ? in.hEffPtEta->GetXaxis()->GetBinCenter(1) : 0.f; + eff = getEfficiency(in.hEffPtEta, centAll, pt, eta, true); + fake = getEfficiency(in.hFakePtEta, centAll, pt, eta, false); + return valid(); } - float getFlatteningWeight(float vz, float chg, float pt, float eta, float phi, bool useFlat) const + float efficiencyVariationFactor(int v, float pt) { - if (!useFlat) { - return 1.0; + if (v == kSystEff) { + return 1.f + 0.01f * KEffVarPercent; + } + if (v == kSystEffPtDep) { + // Linear in pT: full variation at cfgPtMin, none at KEffVarPtZero. + const float shape = std::clamp((KEffVarPtZero - pt) / (KEffVarPtZero - cfgPtMin), 0.f, 1.f); + return 1.f + 0.01f * KEffVarPercent * shape; } - THnSparseF* h = state.hFlatWeight; + return 1.f; + } + + static float getFlatteningWeight(const THnSparseF* h, float vz, float chg, float pt, float eta, float phi) + { if (!h) { return 0.0; } @@ -544,228 +1063,471 @@ struct RadialFlowDecorr { return h->GetBinContent(bins.data()); } - TH3F* rebinNchMap(const TH3F* h, int sysConfig, - const char* newName) const + // Weight w = w_flat (1 - fake) / eff of the track for every variation in mask; a map shared + // by several variations is looked up once. Returns the variations with a usable weight and + // the Base efficiency, fake rate and flattening weight for the QA profiles. + uint32_t getTrackWeights(uint32_t mask, float cent, float vz, float sign, float pt, float eta, float phi, bool useFlat, + std::array& weights, float& effBase, float& fakeBase, float& flatBase) { - if (!h) { - return nullptr; + std::array effDone{}; + std::array effS{}, fakeS{}, flatS{}; + std::array flatDone{}; + uint32_t weightMask = 0; + effBase = std::numeric_limits::quiet_NaN(); + fakeBase = std::numeric_limits::quiet_NaN(); + flatBase = 1.f; + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(mask, v)) { + continue; + } + const int es = effSource(v); + if (effDone[es] == 0) { + effDone[es] = getValidEffFake(es, cent, pt, eta, effS[es], fakeS[es]) ? 1 : -1; + } + if (effDone[es] < 0) { + continue; + } + const float eff = effS[es] * efficiencyVariationFactor(v, pt); + if (!(eff > KFloatEpsilon)) { + continue; + } + float flat = 1.f; + const int fs = flatSource(v); + if (useFlat && cfgFlat && fs >= 0) { + if (!flatDone[fs]) { + flatS[fs] = getFlatteningWeight(inputs[fs].hFlat, vz, sign, pt, eta, phi); + flatDone[fs] = true; + } + flat = flatS[fs]; + } + const float weight = flat * (1.0f - fakeS[es]) / eff; + if (!std::isfinite(weight) || weight <= 0.f) { + continue; + } + weights[v] = weight; + SETBIT(weightMask, v); + trkCounts[v][kTrkWeight] += 1.; + if (v == kSystBase) { + effBase = eff; + fakeBase = fakeS[es]; + flatBase = flat; + } } + return weightMask; + } - // PbPb: 100 tracks; NeNe, OO, pp: 10 tracks - const int trackStep = (sysConfig == 1) ? 100 : 10; + // Per-event two- and three-particle pT correlators (standard method) from the + // power sums, following arXiv:2112.03397, Eqs. 2-3. + struct C2C3Result { + double c2 = std::numeric_limits::quiet_NaN(); + double c3 = std::numeric_limits::quiet_NaN(); + }; - const int nx = h->GetNbinsX(); - const int ny = h->GetNbinsY(); - const int nz = h->GetNbinsZ(); + template + C2C3Result calculateC2C3FromSums(const std::array, M>& sumpmwk, const std::array& sumwk, float referenceMeanPt) const + { + C2C3Result r; + const double s1 = sumwk[1]; + if (s1 <= 0.) { + return r; + } + const double refpt = referenceMeanPt; + const double p11 = sumpmwk[1][1] / s1; + const double p1Bar1 = p11 - refpt; - const double originalWidth = h->GetXaxis()->GetBinWidth(1); + // --- c2: needs >=2 particles (D2 = 1 - tau1 > eps) --- + const double tau1 = sumwk[2] / (s1 * s1); + const double denom2 = 1. - tau1; + if (denom2 > KFloatEpsilon) { + const double p12 = sumpmwk[1][2] / sumwk[2]; + const double p22 = sumpmwk[2][2] / sumwk[2]; + const double p2Bar2 = p22 - 2.0 * p12 * refpt + refpt * refpt; + r.c2 = (p1Bar1 * p1Bar1 - tau1 * p2Bar2) / denom2; + } - const int groupSize = - static_cast(std::lround(trackStep / originalWidth)); + // --- c3: needs >=3 particles (D3 = 1 - 3 tau1 + 2 tau2 > eps) --- + const double tau2 = sumwk[3] / (s1 * s1 * s1); + const double denom3 = 1. - 3.0 * tau1 + 2.0 * tau2; + if (denom3 > KFloatEpsilon) { + const double p12 = sumpmwk[1][2] / sumwk[2]; + const double p22 = sumpmwk[2][2] / sumwk[2]; + const double p13 = sumpmwk[1][3] / sumwk[3]; + const double p23 = sumpmwk[2][3] / sumwk[3]; + const double p33 = sumpmwk[3][3] / sumwk[3]; + const double p2Bar2 = p22 - 2.0 * p12 * refpt + refpt * refpt; + const double p3Bar3 = p33 - 3.0 * p23 * refpt + 3.0 * p13 * refpt * refpt - refpt * refpt * refpt; + r.c3 = (p1Bar1 * p1Bar1 * p1Bar1 - 3.0 * tau1 * p2Bar2 * p1Bar1 + 2.0 * tau2 * p3Bar3) / denom3; + } + return r; + } - // Ensure the input binning allows exact 10/100-track groups - if (groupSize < 1 || - std::abs(groupSize * originalWidth - trackStep) > KFloatEpsilon) { - LOGF(fatal, "Nch bin width is incompatible with %d-track rebinning", - trackStep); - return nullptr; + // =========================================================================== + // Forward detectors + // =========================================================================== + // Eta sub-bin of detector det containing eta, -1 outside its edges. + int fwdSubBin(int det, float eta) const + { + const auto& e = fwdEdges[det]; + if (e.size() < 2 || eta < e.front() || eta >= e.back()) { + return -1; } + const int i = static_cast(std::upper_bound(e.begin(), e.end(), eta) - e.begin()) - 1; + return fwdOffset[det] + i; + } - for (int ix = 1; ix <= nx; ++ix) { - if (std::abs(h->GetXaxis()->GetBinWidth(ix) - - originalWidth) > KFloatEpsilon) { - LOGF(fatal, "Input Nch axis must have uniform bin widths"); - return nullptr; + void addFwd(int det, float eta, float ampl, FwdSignal& fwd) + { + fwd.ampl[det] += ampl; + const int sub = fwdSubBin(det, eta); + if (sub >= 0) { + fwd.ampl[sub] += ampl; + } + if (hists.fwdEta) { + hists.fwdEta->Fill(det, eta, ampl); + if (sub >= 0) { + hists.fwdEta->Fill(sub, eta, ampl); } } + } - // Construct the new Nch bin edges - std::vector xEdges; - xEdges.push_back(h->GetXaxis()->GetBinLowEdge(1)); - - for (int ix = 1; ix <= nx; ix += groupSize) { - const int last = std::min(ix + groupSize - 1, nx); - xEdges.push_back(h->GetXaxis()->GetBinUpEdge(last)); + void markFwdValid(int det, FwdSignal& fwd) const + { + fwd.valid[det] = true; + for (int i = 0; i + 1 < static_cast(fwdEdges[det].size()); ++i) { + fwd.valid[fwdOffset[det] + i] = true; } + } - // Preserve the original pT and eta axes exactly - auto copyEdges = [](const TAxis* axis) { - std::vector edges; - - for (int i = 1; i <= axis->GetNbins(); ++i) { - edges.push_back(axis->GetBinLowEdge(i)); - } - - edges.push_back(axis->GetBinUpEdge(axis->GetNbins())); - return edges; - }; - - const auto yEdges = copyEdges(h->GetYaxis()); - const auto zEdges = copyEdges(h->GetZaxis()); - - const int nNewX = static_cast(xEdges.size()) - 1; - - auto* rebinned = new TH3F( - newName, h->GetTitle(), - nNewX, xEdges.data(), - ny, yEdges.data(), - nz, zEdges.data()); - - rebinned->SetDirectory(nullptr); - rebinned->Sumw2(); - - // Rebin independently for each (pT, eta) slice - for (int iy = 1; iy <= ny; ++iy) { - for (int iz = 1; iz <= nz; ++iz) { - - int lastValid = 0; - double lastValue = 0.; - double lastError = 0.; - - for (int ib = 1; ib <= nNewX; ++ib) { - - const int first = (ib - 1) * groupSize + 1; - const int last = std::min(first + groupSize - 1, nx); - - double sumW = 0.; - double sumVW = 0.; - - for (int ix = first; ix <= last; ++ix) { - - const double v = h->GetBinContent(ix, iy, iz); - const double e = h->GetBinError(ix, iy, iz); - - // Inverse-variance weighting requires a positive error - if (!std::isfinite(v) || - !std::isfinite(e) || e <= 0.) { - continue; - } - - const double weight = 1. / (e * e); + float channelEta(const std::array& p, float vz) + { + const float r = std::hypot(p[0], p[1]); + const float dz = p[2] - (cfgFwdOpt.cfgFwdVzCorr ? vz : 0.f); + return r > 0.f ? std::asinh(dz / r) : std::numeric_limits::quiet_NaN(); + } - sumW += weight; - sumVW += v * weight; - } + void fillChannelQA(int ch, float raw, float corr, float eta) + { + if (hists.fwdChannel) { + hists.fwdChannel->Fill(ch, kChAmpRaw, raw); + hists.fwdChannel->Fill(ch, kChAmpCorr, corr); + hists.fwdChannel->Fill(ch, kChEta, eta); + } + } - if (sumW > 0.) { + // Channel positions with the FT0/FV0 alignment (as in PWGCF/flowDecorrelation) and the + // gain equalisation, loaded at each new run. + template + void loadFwdCalib(const B& bc, bool applyGain) + { + if (bc.runNumber() == fwdCalib.run) { + return; + } + fwdCalib.run = bc.runNumber(); + const uint64_t ts = bc.timestamp(); - const double avg = sumVW / sumW; - const double err = std::sqrt(1. / sumW); + auto* alFT0 = ccdb->getForTimeStamp>("FT0/Calib/Align", ts); + auto* alFV0 = ccdb->getForTimeStamp>("FV0/Calib/Align", ts); + if (!alFT0 || alFT0->size() < 2 || !alFV0 || alFV0->size() < 2) { + LOGF(fatal, "Could not load FT0/FV0 alignment (FT0/Calib/Align, FV0/Calib/Align) for timestamp %llu", ts); + return; + } - rebinned->SetBinContent(ib, iy, iz, avg); - rebinned->SetBinError(ib, iy, iz, err); + // offsetFT0[0]: FT0A, offsetFT0[1]: FT0C + fwdCalib.ft0Det.calculateChannelCenter(); + for (int ch = 0; ch < KNChFT0; ++ch) { + const int side = ch < KNChFT0A ? 0 : 1; + const auto c = fwdCalib.ft0Det.getChannelCenter(ch); + float z = c.Z() + (*alFT0)[side].getZ(); + if (ch >= KNChFT0A) { + z = -z; + } + fwdCalib.posFT0[ch] = {static_cast(c.X() + (*alFT0)[side].getX()), static_cast(c.Y() + (*alFT0)[side].getY()), z}; + } + // offsetFV0[0]: left half, offsetFV0[1]: right half + static constexpr std::array KCellsInLeft = {0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27, 32, 40, 33, 41, 34, 42, 35, 43}; + for (int ch = 0; ch < KNChFV0; ++ch) { + const bool left = std::find(KCellsInLeft.begin(), KCellsInLeft.end(), ch) != KCellsInLeft.end(); + const auto& off = (*alFV0)[left ? 0 : 1]; + const auto c = fwdCalib.fv0Det->getReadoutCenter(ch); + fwdCalib.posFV0[ch] = {static_cast(c.x + off.getX()), static_cast(c.y + off.getY()), static_cast(c.z + off.getZ())}; + } + + fwdCalib.gainFT0.fill(1.f); + fwdCalib.gainFV0.fill(1.f); + if (applyGain && cfgFwdOpt.cfgFwdGainEq) { + auto* gFT0 = ccdb->getForTimeStamp>(cfgFwdOpt.cfgGainEqPath.value + "/FT0", ts); + auto* gFV0 = ccdb->getForTimeStamp>(cfgFwdOpt.cfgGainEqPath.value + "/FV0", ts); + if (gFT0 && gFT0->size() >= static_cast(KNChFT0)) { + std::copy_n(gFT0->begin(), KNChFT0, fwdCalib.gainFT0.begin()); + } else { + LOGF(warning, "No FT0 gain equalisation at %s/FT0 for run %d: unit gains", cfgFwdOpt.cfgGainEqPath.value.c_str(), fwdCalib.run); + } + if (gFV0 && gFV0->size() >= static_cast(KNChFV0)) { + std::copy_n(gFV0->begin(), KNChFV0, fwdCalib.gainFV0.begin()); + } else { + LOGF(warning, "No FV0 gain equalisation at %s/FV0 for run %d: unit gains", cfgFwdOpt.cfgGainEqPath.value.c_str(), fwdCalib.run); + } + } + LOGF(info, "Forward-detector calibration loaded for run %d", fwdCalib.run); + } - lastValid = ib; - lastValue = avg; - lastError = err; - } + // Gain-equalised amplitudes of FT0A, FT0C and FV0A, totals and eta sub-bins. + template + void computeFwdReco(const C& col, float vz, FwdSignal& fwd, bool qa) + { + fwd = {}; + auto corr = [](float a, float g) { return g > 0.f ? a / g : a; }; + if (col.has_foundFT0()) { + const auto& ft0 = col.foundFT0(); + markFwdValid(kFT0A, fwd); + markFwdValid(kFT0C, fwd); + for (std::size_t i = 0; i < ft0.channelA().size(); ++i) { + const int id = ft0.channelA()[i]; + if (id < 0 || id >= KNChFT0A) { + continue; } - - // Extend the final valid value into trailing empty bins. - // This also allows safe overflow clamping at readout. - for (int ib = lastValid + 1; ib <= nNewX; ++ib) { - if (lastValid == 0) { - break; - } - - rebinned->SetBinContent(ib, iy, iz, lastValue); - rebinned->SetBinError(ib, iy, iz, lastError); + const float a = corr(ft0.amplitudeA()[i], fwdCalib.gainFT0[id]); + const float eta = channelEta(fwdCalib.posFT0[id], vz); + addFwd(kFT0A, eta, a, fwd); + if (qa) { + fillChannelQA(id, ft0.amplitudeA()[i], a, eta); + } + } + for (std::size_t i = 0; i < ft0.channelC().size(); ++i) { + const int id = ft0.channelC()[i] + KNChFT0A; + if (id < KNChFT0A || id >= KNChFT0) { + continue; + } + const float a = corr(ft0.amplitudeC()[i], fwdCalib.gainFT0[id]); + const float eta = channelEta(fwdCalib.posFT0[id], vz); + addFwd(kFT0C, eta, a, fwd); + if (qa) { + fillChannelQA(id, ft0.amplitudeC()[i], a, eta); } } } + if (col.has_foundFV0()) { + const auto& fv0 = col.foundFV0(); + markFwdValid(kFV0A, fwd); + for (std::size_t i = 0; i < fv0.channel().size(); ++i) { + const int id = fv0.channel()[i]; + if (id < 0 || id >= KNChFV0) { + continue; + } + const float a = corr(fv0.amplitude()[i], fwdCalib.gainFV0[id]); + const float eta = channelEta(fwdCalib.posFV0[id], vz); + addFwd(kFV0A, eta, a, fwd); + if (qa) { + fillChannelQA(KNChFT0 + id, fv0.amplitude()[i], a, eta); + } + } + } + } - return rebinned; + // Number of charged primaries in the acceptance of each forward detector (MC truth). + template + void computeFwdTruth(const P& particles, FwdSignal& fwd) + { + fwd = {}; + for (int det = 0; det < kNFwdDet; ++det) { + markFwdValid(det, fwd); + } + for (const auto& particle : particles) { + if (!particle.isPhysicalPrimary()) { + continue; + } + const float eta = particle.eta(); + if (std::abs(eta) < 1.5f) { + continue; + } + auto* pd = pdg->GetParticle(particle.pdgCode()); + if (!pd || std::abs(pd->Charge()) == 0) { + continue; + } + for (int det = 0; det < kNFwdDet; ++det) { + const int sub = fwdSubBin(det, eta); + if (sub >= 0) { + fwd.ampl[det] += 1.; + fwd.ampl[sub] += 1.; + } + } + } } - std::vector* offsetFT0 = nullptr; - uint64_t mLastTimestamp = 0; - double getEtaFT0(uint64_t globalChno, int i) + // =========================================================================== + // Observables + // =========================================================================== + // Bin content of p at (x, ybin[, zbin]), NaN for an empty bin. + static double profileValue(const TProfile2D* p, double x, int ybin) { - if (i > 1 || i < 0) { - LOGF(fatal, "kFIT Index %d out of range", i); + if (!p) { + return std::numeric_limits::quiet_NaN(); } - auto chPos = ft0Det.getChannelCenter(globalChno); - auto x = chPos.X() + (*offsetFT0)[i].getX(); - auto y = chPos.Y() + (*offsetFT0)[i].getY(); - auto z = chPos.Z() + (*offsetFT0)[i].getZ(); - if (i == 1) { - z = -std::abs(z); - } else if (i == 0) { - z = std::abs(z); + const int gb = p->GetBin(p->GetXaxis()->FindBin(x), ybin); + if (p->GetBinEntries(gb) <= 0.) { + return std::numeric_limits::quiet_NaN(); } - auto r = std::sqrt(x * x + y * y); - auto theta = std::atan2(r, z); - return -std::log(std::tan(0.5 * theta)); + return p->GetBinContent(gb); + } + static double profileValue(const TProfile3D* p, double x, int ybin, int zbin) + { + if (!p) { + return std::numeric_limits::quiet_NaN(); + } + const int gb = p->GetBin(p->GetXaxis()->FindBin(x), ybin, zbin); + if (p->GetBinEntries(gb) <= 0.) { + return std::numeric_limits::quiet_NaN(); + } + return p->GetBinContent(gb); } - void loadAlignParam(uint64_t timestamp) + void computeEventObs(const PowerSums& s, int v, int level, const FwdSignal* fwd, float nCh, EventObs& evObs) { - if (timestamp == mLastTimestamp && offsetFT0 != nullptr) { - return; + const auto& input = inputs[v]; + const double nan = std::numeric_limits::quiet_NaN(); + evObs.nCh = nCh; + for (int ieta = 0; ieta < nEta; ++ieta) { + evObs.nW[ieta] = s.wk[ieta][1]; + evObs.mean[ieta] = (s.wk[ieta][1] > KFloatEpsilon) ? (s.pmwk[ieta][1][1] / s.wk[ieta][1]) : nan; + evObs.c2[ieta] = nan; + evObs.c3[ieta] = nan; + evObs.p1kBar[ieta] = nan; + // c2 (>=2 tracks), c3 (>=3 tracks) and the pT deviation need a usable reference + const double mmpt = profileValue(input.pmean[level], nCh, ieta + 1, kMeanPt + 1); + if (std::isfinite(mmpt) && mmpt != 0) { + const auto corr = calculateC2C3FromSums(s.pmwk[ieta], s.wk[ieta], mmpt); + evObs.c2[ieta] = corr.c2; + evObs.c3[ieta] = corr.c3; + if (std::isfinite(evObs.mean[ieta])) { + evObs.p1kBar[ieta] = evObs.mean[ieta] - mmpt; + } + } + evObs.dN[ieta] = evObs.nW[ieta] - profileValue(input.pmean[level], nCh, ieta + 1, kMeanN + 1); } - offsetFT0 = ccdb->getForTimeStamp>("FT0/Calib/Align", timestamp); - if (!offsetFT0) { - LOGF(fatal, "Failed to load valid FT0 alignment from CCDB!"); + evObs.fwdValid.fill(false); + if (!fwd || !input.pmeanFwd[level]) { return; } - mLastTimestamp = timestamp; - LOGF(info, "Loaded FT0 alignment for timestamp %llu", timestamp); + for (int k = 0; k < nFwd; ++k) { + if (!fwd->valid[k]) { + continue; + } + const double m = profileValue(input.pmeanFwd[level], nCh, k + 1); + if (!std::isfinite(m)) { + continue; + } + evObs.fwd[k] = fwd->ampl[k]; + evObs.dFwd[k] = fwd->ampl[k] - m; + evObs.fwdValid[k] = true; + } } - // Per-event two- and three-particle pT correlators (standard method) from the - // power sums, following arXiv:2112.03397, Eqs. 2-3. - struct C2C3Result { - double c2 = std::numeric_limits::quiet_NaN(); - double c3 = std::numeric_limits::quiet_NaN(); - }; - - template - C2C3Result calculateC2C3FromSums(const std::array, M>& sumpmwk, const std::array& sumwk, float referenceMeanPt) const + // Fill the fluctuation profiles of one (variation, level) at x = N_ch, or of one bootstrap + // replica with weight w at x = centrality. + void fillFluc(const FlucHists& h, const EventObs& evObs, double w, float x) { - C2C3Result r; - const double s1 = sumwk[1]; - if (s1 <= 0.) { - return r; + auto fill = [w](const std::shared_ptr& p, double a, double b, double c, double val) { + if (p) { + p->Fill(a, b, c, val, w); + } + }; + + // multiplicity, meanpT, C2, C3 vs eta bin + for (int ieta = 0; ieta < nEta; ++ieta) { + fill(h.obs, x, ieta, kObsN, evObs.nW[ieta]); + if (std::isfinite(evObs.mean[ieta])) { + fill(h.obs, x, ieta, kObsMeanPt, evObs.mean[ieta]); + } + if (std::isfinite(evObs.c2[ieta])) { + fill(h.obs, x, ieta, kObsC2, evObs.c2[ieta]); + } + if (std::isfinite(evObs.c3[ieta])) { + fill(h.obs, x, ieta, kObsC3, evObs.c3[ieta]); + } } - const double refpt = referenceMeanPt; - const double p11 = sumpmwk[1][1] / s1; - const double p1Bar1 = p11 - refpt; - // --- c2: needs >=2 particles (D2 = 1 - tau1 > eps) --- - const double tau1 = sumwk[2] / (s1 * s1); - const double denom2 = 1. - tau1; - if (denom2 > KFloatEpsilon) { - const double p12 = sumpmwk[1][2] / sumwk[2]; - const double p22 = sumpmwk[2][2] / sumwk[2]; - const double p2Bar2 = p22 - 2.0 * p12 * refpt + refpt * refpt; - r.c2 = (p1Bar1 * p1Bar1 - tau1 * p2Bar2) / denom2; + // mirror-pair subevent (C2Sub, C3Sub) vs eta bin + for (int ietaA = 1; ietaA <= (nEta - 1) / 2; ++ietaA) { + int ietaC = nEta - ietaA; + float c2Sub = evObs.p1kBar[ietaA] * evObs.p1kBar[ietaC]; + // Mirror pairs (A, C = -A): both orderings are filled, each at the bin of its pair, + // so bins A < 0 hold AAB and bins C > 0 hold ABB of the same mirror pair. For a + // symmetric collision system the two agree in expectation: eta -> -eta maps + // AAB(a, -a) onto AAB(-a, a) = ABB(a, -a). Comparing bin A with bin nEta - A is + // therefore a forward/backward check, not two independent observables. + float c3SubA = evObs.c2[ietaA] * evObs.p1kBar[ietaC]; // AAB: 2 particles from A, 1 from C + float c3SubC = evObs.c2[ietaC] * evObs.p1kBar[ietaA]; // ABB: 2 particles from C, 1 from A + + if (std::isfinite(c2Sub)) { + fill(h.obs, x, ietaA, kObsC2Sub, c2Sub); + } + if (std::isfinite(c3SubA)) { + fill(h.obs, x, ietaA, kObsC3Sub, c3SubA); + } + if (std::isfinite(c3SubC)) { + fill(h.obs, x, ietaC, kObsC3Sub, c3SubC); + } } - // --- c3: needs >=3 particles (D3 = 1 - 3 tau1 + 2 tau2 > eps) --- - const double tau2 = sumwk[3] / (s1 * s1 * s1); - const double denom3 = 1. - 3.0 * tau1 + 2.0 * tau2; - if (denom3 > KFloatEpsilon) { - const double p12 = sumpmwk[1][2] / sumwk[2]; - const double p22 = sumpmwk[2][2] / sumwk[2]; - const double p13 = sumpmwk[1][3] / sumwk[3]; - const double p23 = sumpmwk[2][3] / sumwk[3]; - const double p33 = sumpmwk[3][3] / sumwk[3]; - const double p2Bar2 = p22 - 2.0 * p12 * refpt + refpt * refpt; - const double p3Bar3 = p33 - 3.0 * p23 * refpt + 3.0 * p13 * refpt * refpt - refpt * refpt * refpt; - r.c3 = (p1Bar1 * p1Bar1 * p1Bar1 - 3.0 * tau1 * p2Bar2 * p1Bar1 + 2.0 * tau2 * p3Bar3) / denom3; + // full 2D subevent maps, in (eta_A, eta_C) and in (Delta eta, Sigma eta) + for (int ietaA = 1; ietaA < nEta; ++ietaA) { + for (int ietaC = 1; ietaC < nEta; ++ietaC) { + float etaValA = (etaLw[ietaA] + etaUp[ietaA]) / 2.0f; + float etaValB = (etaLw[ietaC] + etaUp[ietaC]) / 2.0f; + const float bw = etaUp[ietaA] - etaLw[ietaA]; + const float gap = (ietaA - ietaC) * bw; + const float sum = 2.f * etaLw[1] + (ietaA + ietaC - 1) * bw; // = cA + cC on the lattice + + float c2Sub = (ietaA == ietaC) ? static_cast(evObs.c2[ietaA]) : evObs.p1kBar[ietaA] * evObs.p1kBar[ietaC]; + // C3 sub-event map, ORDERED: cell (A,C) = c2[A] p1kBar[C] = _A _C, + // two particles from A and one from C ("AAB"). The same pair of bins read the + // other way round, cell (C,A) = c2[C] p1kBar[A], is "ABB" (one from A, two from C). + // Both are filled, so the map carries AAB and ABB of every bin pair: the row at + // eta_A = eta_ref is AAB with the pair at eta_ref, the column is ABB. Unlike C2, + // this map is NOT symmetric under A <-> C, and the two triangles must not be + // averaged or treated as duplicates. Diagonal: the within-bin c3 ("AAA"). + float c3Sub2D = (ietaA == ietaC) ? static_cast(evObs.c3[ietaA]) : evObs.c2[ietaA] * evObs.p1kBar[ietaC]; + + if (std::isfinite(c2Sub)) { + fill(h.c2Sub2D, x, etaValA, etaValB, c2Sub); + fill(h.gapSum2D, x, gap, sum, c2Sub); + } + if (std::isfinite(c3Sub2D)) { + fill(h.c3Sub2D, x, etaValA, etaValB, c3Sub2D); + fill(h.c3GapSum2D, x, gap, sum, c3Sub2D); + } + } + } + + // forward amplitude a_k: , and , with + // da = a - , d[pT] = [pT] - <[pT]>, dN = N - , the means at N_ch from the mean pass + for (int k = 0; k < nFwd; ++k) { + if (!evObs.fwdValid[k]) { + continue; + } + const double da = evObs.dFwd[k]; + if (h.fwdVar) { + h.fwdVar->Fill(x, k, da * da, w); + } + for (int ieta = 0; ieta < nEta; ++ieta) { + if (std::isfinite(evObs.p1kBar[ieta])) { + fill(h.covFwdPt, x, ieta, k, evObs.p1kBar[ieta] * da); + } + if (std::isfinite(evObs.dN[ieta])) { + fill(h.covFwdN, x, ieta, k, evObs.dN[ieta] * da); + } + } } - return r; } // =========================================================================== // Table joins // =========================================================================== using GeneralCollisions = soa::Join; - Filter collisionFilter = nabs(aod::collision::posZ) < cfgVtxZCut; + Filter collisionFilter = nabs(aod::collision::posZ) < cfgEvSel.cfgVtxZCut; using AodCollisionsSel = soa::Filtered; using UnfilteredTracks = soa::Join< @@ -774,312 +1536,394 @@ struct RadialFlowDecorr { using AodTracksSel = soa::Filtered; using TCs = soa::Join; using FilteredTCs = soa::Filtered; - using BCsRun3 = soa::Join; using MyRun3MCCollisions = soa::Join< aod::Collisions, aod::EvSels, aod::Mults, aod::MultsExtra, aod::CentFT0Cs, aod::CentFT0Ms, aod::CentFDDMs, aod::CentFV0As, aod::CentNGlobals, aod::McCollisionLabels>; + // reconstructed collisions of one MC collision + using McRecoCollisions = soa::SmallGroups; - PresliceUnsorted colPerMcCollision = aod::mccollisionlabel::mcCollisionId; + Preslice tracksPerCollision = aod::track::collisionId; // =========================================================================== // Histogram declarations // =========================================================================== + // Bin labels only on TH1/TH2 and THnSparse axes: ROOT merges labelled axes by label, which for + // profiles and TH3 extends the axis and sums all bins into a new one, or drops the entries of + // bins with zero content. Their bins are named in the axis title instead. + static void labelAxis(TAxis* ax, const std::vector& labels) + { + for (std::size_t i = 0; i < labels.size() && static_cast(i) < ax->GetNbins(); ++i) { + ax->SetBinLabel(i + 1, labels[i]); + } + } + + void labelSystAxis(TAxis* ax) const + { + for (int v = 0; v < kNSyst && v < ax->GetNbins(); ++v) { + ax->SetBinLabel(v + 1, systName[v].c_str()); + } + } + void declareCommonQA() { histos.add("hVtxZ_after_sel", ";z_{vtx} (cm)", kTH1F, {{KNbinsZvtx, KZvtxMin, KZvtxMax}}); histos.add("hVtxZ", ";z_{vtx} (cm)", kTH1F, {{KNbinsZvtx, KZvtxMin, KZvtxMax}}); histos.add("hCentrality", ";centrality (%)", kTH1F, {{centAxis1Per}}); - histos.add("Hist2D_globalTracks_PVTracks", ";N_{global};N_{PV}", kTH2F, {{nChAxis}, {nChAxis}}); - histos.add("Hist2D_cent_nch", ";N_{PV};cent (%)", kTH2F, {{nChAxis}, {centAxis1Per}}); - histos.add("Hist2D_globalTracks_cent", "cent (%);N_{global}", kTH2F, {{centAxis1Per}, {nChAxis}}); - histos.add("Hist2D_PVTracks_cent", "cent (%);N_{PV}", kTH2F, {{centAxis1Per}, {nChAxis}}); + histos.add("Hist2D_globalTracks_PVTracks", ";N_{PV};N_{ch} (global tracks)", kTH2F, {{multQAAxis}, {multQAAxis}}); + histos.add("Hist2D_PVTracks_cent", "cent (%);N_{PV}", kTH2F, {{centAxis1Per}, {multQAAxis}}); + hists.nchOccupancy = histos.add("Hist2D_Nch_occupancy", ";N_{ch} (global tracks);trackOccupancyInTimeRange", kTH2F, {{nchAxisObs}, {KNbinsOccQA, 0., KOccQAMax}}); histos.add("hP", ";p (GeV/c)", kTH1F, {{KNbinsPt, KPMin, KPMax}}); histos.add("hPt", ";p_{T} (GeV/c)", kTH1F, {{KNbinsPt, KPtMin, KPtMax}}); histos.add("hEta", ";#eta", kTH1F, {{KNbinsEtaFine, KEtaMin, KEtaMax}}); histos.add("hPhi", ";#phi", kTH1F, {{KNbinsPhi, KPhiMin, TwoPI}}); - histos.add("hEvtCount", "Number of Event;; Count", kTH1F, {{9, 0, 9}}); - histos.get(HIST("hEvtCount"))->GetXaxis()->SetBinLabel(1, "all Events"); - histos.get(HIST("hEvtCount"))->GetXaxis()->SetBinLabel(2, "after sel8"); - histos.get(HIST("hEvtCount"))->GetXaxis()->SetBinLabel(3, "after VertexZ Cut"); - histos.get(HIST("hEvtCount"))->GetXaxis()->SetBinLabel(4, "after kNoSameBunchPileup"); - histos.get(HIST("hEvtCount"))->GetXaxis()->SetBinLabel(5, "after kIsGoodZvtxFT0vsPV"); - histos.get(HIST("hEvtCount"))->GetXaxis()->SetBinLabel(6, "after kIsGoodITSLayersAll"); - histos.get(HIST("hEvtCount"))->GetXaxis()->SetBinLabel(7, "after PVTracksCent Pileup Cut"); - histos.get(HIST("hEvtCount"))->GetXaxis()->SetBinLabel(8, "after Linear Pileup Cut (Up)"); - histos.get(HIST("hEvtCount"))->GetXaxis()->SetBinLabel(9, "after Linear Pileup Cut (Lw)"); - - histos.add("hTrkCount", "Number of Tracks;; Count", kTH1F, {{11, 0, 11}}); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(1, "all Tracks"); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(2, "after sign!=0"); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(3, "after has_collision"); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(4, "after isPVContributor"); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(5, "after itsNCls"); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(6, "after tpcNClsFound"); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(7, "after tpcNClsCrossedRows"); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(8, "after pT,#eta selections"); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(9, "after isGlobalTrack"); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(10, "after dcaXY"); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(11, "after dcaZ"); - } - - void declareMCCommonHists() - { - histos.add("h3_AllPrimary", ";N_{PV};p_{T};#eta", kTH3F, {{nChAxis2}, {KNbinsPtRes, KPtMin, KPtMax}, {KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}}); - histos.add("h3_RecoMatchedToPrimary", ";N_{PV};p_{T};#eta", kTH3F, {{nChAxis2}, {KNbinsPtRes, KPtMin, KPtMax}, {KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}}); - histos.add("h3_AllReco", ";N_{PV};p_{T};#eta", kTH3F, {{nChAxis2}, {KNbinsPtRes, KPtMin, KPtMax}, {KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}}); - histos.add("h3_RecoUnMatchedToPrimary_Secondary", ";N_{PV};p_{T};#eta", kTH3F, {{nChAxis2}, {KNbinsPtRes, KPtMin, KPtMax}, {KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}}); - histos.add("h3_RecoUnMatchedToPrimary_Fake", ";N_{PV};p_{T};#eta", kTH3F, {{nChAxis2}, {KNbinsPtRes, KPtMin, KPtMax}, {KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}}); + // cut flows of every variation (y bin 1 = Base); 1D copies per variation in declareCounts() + hists.evtCount = histos.add("hEvtCountSyst", "Number of events;;variation", kTH2D, {{kNEvCut, -0.5, static_cast(kNEvCut) - 0.5}, {kNSyst, -0.5, static_cast(kNSyst) - 0.5}}); + labelAxis(hists.evtCount->GetXaxis(), evtCutLabels()); + labelSystAxis(hists.evtCount->GetYaxis()); + + hists.trkCount = histos.add("hTrkCountSyst", "Number of tracks;;variation", kTH2D, {{kNTrkCut, -0.5, static_cast(kNTrkCut) - 0.5}, {kNSyst, -0.5, static_cast(kNSyst) - 0.5}}); + labelAxis(hists.trkCount->GetXaxis(), trkCutLabels()); + labelSystAxis(hists.trkCount->GetYaxis()); + } + + static std::vector evtCutLabels() + { + return {"all", "sel8", "|z_{vtx}|", "kIsGoodZvtxFT0vsPV", "kNoTimeFrameBorder", "kNoITSROFrameBorder", "kIsVertexITSTPC", + "kNoSameBunchPileup", "kNoCollInTimeRangeStandard", + "ITS good layers", "n#sigma/linear pile-up", "variation cut", "cent < cfgCentMax (selected)", + "selected: kNoHighMultCollInPrevRof", "selected: occupancy < 0"}; + } + static std::vector trkCutLabels() + { + return {"all", "sign#neq0", "has_collision", "isPVContributor", "p_{T}, #eta", "isGlobalTrack, crossed rows", + "itsNCls", "tpcNClsFound", "tpcChi2NCl", "DCAz", "variation cut (selected)", "valid weight (used)"}; + } + + // hEvtCount and hTrkCount of each variation of this pass (Base at the top level, the others + // in their folder), so that every variation's output carries its own cut flow. + void declareCounts(uint32_t mask) + { + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(mask, v)) { + continue; + } + auto& r = reg(v); + hists.evtCount1D[v] = r.add((systPrefix(v) + "hEvtCount").c_str(), ("Number of events, " + systName[v]).c_str(), kTH1D, {{kNEvCut, -0.5, static_cast(kNEvCut) - 0.5}}); + labelAxis(hists.evtCount1D[v]->GetXaxis(), evtCutLabels()); + hists.trkCount1D[v] = r.add((systPrefix(v) + "hTrkCount").c_str(), ("Number of tracks, " + systName[v]).c_str(), kTH1D, {{kNTrkCut, -0.5, static_cast(kNTrkCut) - 0.5}}); + labelAxis(hists.trkCount1D[v]->GetXaxis(), trkCutLabels()); + } + } + + void declareCutQA() + { + const AxisSpec ptAxis{100, 0., 10., "p_{T} (GeV/c)"}; + for (int st = 0; st < kNQAStage; ++st) { + const std::string d = st == kQABefore ? "CutQA/before/" : "CutQA/after/"; + auto h1 = [&](const char* name, const char* title, const AxisSpec& ax) { return histos.add((d + name).c_str(), title, kTH1F, {ax}); }; + auto h2 = [&](const char* name, const char* title, const AxisSpec& ax, const AxisSpec& ay) { return histos.add((d + name).c_str(), title, kTH2F, {ax, ay}); }; + hists.qaEvt1D[kQAVtxZ][st] = h1("hVtxZ", ";z_{vtx} (cm)", {200, -20., 20.}); + hists.qaEvt1D[kQACent][st] = h1("hCent", ";centrality (%)", {centAxis1Per}); + hists.qaEvt1D[kQAOccupancy][st] = h1("hOccupancy", ";trackOccupancyInTimeRange", {KNbinsOccQA, 0., KOccQAMax}); + hists.qaEvt2D[kQANchNPV][st] = h2("hNch_NPV", ";N_{PV};N_{ch} (global tracks)", multQAAxis, multQAAxis); + hists.qaEvt2D[kQANchCent][st] = h2("hNch_cent", ";centrality (%);N_{ch} (global tracks)", centAxis1Per, multQAAxis); + hists.qaTrk1D[kQAPt][st] = h1("hTrkPt", "", ptAxis); + hists.qaTrk1D[kQAEta][st] = h1("hTrkEta", ";#eta", {60, -1.5, 1.5}); + hists.qaTrk1D[kQAItsNCls][st] = h1("hTrkItsNCls", ";ITS clusters", {8, -0.5, 7.5}); + hists.qaTrk1D[kQAItsChi2][st] = h1("hTrkItsChi2NCl", ";ITS #chi^{2}/cluster", {80, 0., 40.}); + hists.qaTrk1D[kQATpcNClsFound][st] = h1("hTrkTpcNClsFound", ";TPC found clusters", {161, -0.5, 160.5}); + hists.qaTrk1D[kQATpcCrossedRows][st] = h1("hTrkTpcCrossedRows", ";TPC crossed rows", {161, -0.5, 160.5}); + hists.qaTrk1D[kQATpcCrossedRowsOverFindable][st] = h1("hTrkTpcCrossedRowsOverFindable", ";TPC crossed rows / findable clusters", {75, 0., 1.5}); + hists.qaTrk1D[kQATpcChi2][st] = h1("hTrkTpcChi2NCl", ";TPC #chi^{2}/cluster", {100, 0., 10.}); + hists.qaTrk1D[kQAPVContributor][st] = h1("hTrkPVContributor", ";isPVContributor", {2, -0.5, 1.5}); + hists.qaTrk1D[kQAGlobalTrack][st] = h1("hTrkGlobalTrack", ";isGlobalTrack", {2, -0.5, 1.5}); + hists.qaTrk2D[kQADcaXYPt][st] = h2("hTrkDcaXY_pt", ";p_{T} (GeV/c);DCA_{xy} (cm)", ptAxis, {400, -1., 1.}); + hists.qaTrk2D[kQADcaZPt][st] = h2("hTrkDcaZ_pt", ";p_{T} (GeV/c);DCA_{z} (cm)", ptAxis, {300, -3., 3.}); + } + } + + template + void fillCutQAEvent(int st, const C& col, int nCh, float cent) + { + hists.qaEvt1D[kQAVtxZ][st]->Fill(col.posZ()); + hists.qaEvt1D[kQACent][st]->Fill(cent); + hists.qaEvt1D[kQAOccupancy][st]->Fill(col.trackOccupancyInTimeRange()); + hists.qaEvt2D[kQANchNPV][st]->Fill(col.multNTracksPV(), nCh); + hists.qaEvt2D[kQANchCent][st]->Fill(cent, nCh); + } + + template + void fillCutQATrack(int st, const T& trk) + { + hists.qaTrk1D[kQAPt][st]->Fill(trk.pt()); + hists.qaTrk1D[kQAEta][st]->Fill(trk.eta()); + hists.qaTrk1D[kQAItsNCls][st]->Fill(trk.itsNCls()); + hists.qaTrk1D[kQAItsChi2][st]->Fill(trk.itsChi2NCl()); + hists.qaTrk1D[kQATpcNClsFound][st]->Fill(trk.tpcNClsFound()); + hists.qaTrk1D[kQATpcCrossedRows][st]->Fill(trk.tpcNClsCrossedRows()); + hists.qaTrk1D[kQATpcCrossedRowsOverFindable][st]->Fill(trk.tpcCrossedRowsOverFindableCls()); + hists.qaTrk1D[kQATpcChi2][st]->Fill(trk.tpcChi2NCl()); + hists.qaTrk1D[kQAPVContributor][st]->Fill(trk.isPVContributor()); + hists.qaTrk1D[kQAGlobalTrack][st]->Fill(trk.isGlobalTrack()); + hists.qaTrk2D[kQADcaXYPt][st]->Fill(trk.pt(), trk.dcaXY()); + hists.qaTrk2D[kQADcaZPt][st]->Fill(trk.pt(), trk.dcaZ()); + } + + // (centrality, N_ch) of the selected events of every variation: its event count and the + // centrality of each N_ch bin with the estimator of the variation. + void declareNchCent(uint32_t mask) + { + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(mask, v) || hists.nchCent[v]) { + continue; + } + hists.nchCent[v] = reg(v).add((systPrefix(v) + "Hist2D_globalTracks_cent").c_str(), "cent (%);N_{ch} (global tracks)", kTH2F, {{centAxis1Per}, {nchAxisOf(v)}}); + } + } + + void declareFwdQA() + { + if (hists.fwdChannel) { + return; + } + hists.fwdChannel = histos.add("FwdQA/Prof_Channel_qa", ";channel (FT0A 0-95, FT0C 96-207, FV0A 208-255);quantity (0 amplitude, 1 gain-equalised amplitude, 2 #eta aligned vs z_{vtx})", kTProfile2D, {{KNChFT0 + KNChFV0, -0.5, KNChFT0 + KNChFV0 - 0.5}, {kNChQA, -0.5, static_cast(kNChQA) - 0.5}}); + hists.fwdEta = histos.add("FwdQA/Prof_Eta_fwdbin", ";forward bin;amplitude-weighted #LT#eta#GT", kTProfile, {fwdAxis}); + } + void declareEff(uint32_t mask) + { + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(mask, v)) { + continue; + } + for (int c = 0; c < kNEffCat; ++c) { + hists.eff[v].maps[c] = reg(v).add((systPrefix(v) + effMapName[c]).c_str(), ";centrality (%);p_{T} (GeV/c);#eta", kTH3F, + {{centAxisEff}, {KNbinsPtRes, KPtMin, KPtMax}, {KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}}); + } + } + } + + void declareMCQA() + { histos.add("ptResolution", ";p_{T}^{MC};(p_{T}^{reco}-p_{T}^{MC})/p_{T}^{MC}", kTH2F, {{KNbinsPtRes, KPtMin, KPtMax}, {100, -0.2, 0.2}}); histos.add("etaResolution", ";#eta^{MC};#eta^{reco}-#eta^{MC}", kTH2F, {{KNbinsEtaRes, -KEtaFineMax, KEtaFineMax}, {100, -0.02, 0.02}}); histos.add("etaTruthReco", ";#eta^{MC};#eta^{reco}", kTH2F, {{KNbinsEtaRes, -KEtaFineMax, KEtaFineMax}, {KNbinsEtaRes, -KEtaFineMax, KEtaFineMax}}); histos.add("TruthTracKVz", ";Vz^{MC};Vz^{Reco}", kTH2F, {{KNbinsVz, KVzMin, KVzMax}, {KNbinsVz, KVzMin, KVzMax}}); histos.add("vzResolution", ";Vz^{MC};(Vz^{reco}-Vz^{MC})/Vz^{MC}", kTH2F, {{KNbinsVz, KVzMin, KVzMax}, {100, -0.1, 0.1}}); + hists.mcTrackOrigin = histos.add("MCQA/Hist3D_Nch_pT_origin", ";N_{ch};p_{T} (GeV/c);origin (0 primary, 1 secondary, 2 other MC collision, 3 no label)", kTH3F, {{nchAxisObs}, {KNbinsPtRes, KPtMin, KPtMax}, {kNOrigin, -0.5, static_cast(kNOrigin) - 0.5}}); } - void declareMCGetFlatHists() + // vertex QA of all MC passes + void declareMCVtxQA() { - histos.add("MCGen/hEtaPhiReco", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); - histos.add("MCGen/hEtaPhiRecoEffWtd", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); - histos.add("MCGen/hEtaPhiRecoWtd", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); + if (hists.mcVtx) { + return; + } + hists.mcVtx = histos.add("MCQA/Hist2D_dVz_type", ";z_{vtx}^{reco} - z_{vtx}^{MC} (cm);reco collision", kTH2F, {{200, -0.5, 0.5}, {kNVtxQA, -0.5, static_cast(kNVtxQA) - 0.5}}); + labelAxis(hists.mcVtx->GetYaxis(), {"single", "split: used", "split: skipped"}); } - void declareMCMeanHists() + // (vz, sign, pT, eta, phi) maps, unweighted, efficiency-weighted and with all weights; + // dir = "" (DATA) or "MCReco/". + void declareFlat(uint32_t mask, const std::string& dir) { - histos.add("Eff_cent", ";cent", kTProfile, {centAxis1Per}); - histos.add("Eff_Ntrk", ";N_{PV}", kTProfile, {nChAxis2}); - histos.add("Eff_pT", ";p_{T}", kTProfile, {{KNbinsPtRes, KPtMin, KPtMax}}); - histos.add("Eff_eta", ";#eta", kTProfile, {{KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}}); - histos.add("Fake_cent", ";cent", kTProfile, {centAxis1Per}); - histos.add("Fake_Ntrk", ";N_{PV}", kTProfile, {nChAxis2}); - histos.add("Fake_pT", ";p_{T}", kTProfile, {{KNbinsPtRes, KPtMin, KPtMax}}); - histos.add("Fake_eta", ";#eta", kTProfile, {{KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}}); - histos.add("wgt_cent", ";cent", kTProfile, {centAxis1Per}); - histos.add("wgt_Ntrk", ";N_{PV}", kTProfile, {nChAxis2}); - histos.add("wgt_pT", ";p_{T}", kTProfile, {{KNbinsPtRes, KPtMin, KPtMax}}); - histos.add("wgt_eta", ";#eta", kTProfile, {{KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}}); - - histos.add("pmeanFT0Amultpv", ";N_{PV};Ampl", kTProfile, {nChAxis}); - histos.add("pmeanFT0Cmultpv", ";N_{PV};Ampl", kTProfile, {nChAxis}); - histos.add("pmeanFT0A_cent", ";cent;Ampl", kTProfile, {centAxis1Per}); - histos.add("pmeanFT0C_cent", ";cent;Ampl", kTProfile, {centAxis1Per}); - histos.add("pmean_cent_id_eta_FT0", ";cent;id;#eta", kTProfile3D, {{centAxis1Per}, {200, -0.5, 199.5}, {100, -5.0, 5.0}}); - histos.add("h3_cent_id_eta_FT0", ";cent;id;#eta", kTH3F, {{centAxis1Per}, {200, -0.5, 199.5}, {100, -5.0, 5.0}}); - - histos.add("MCGen/Prof_Cent_Nchrec", ";cent;#LT N#GT", kTProfile, {centAxis1Per}); - histos.add("MCGen/Prof_Mult_Nchrec", ";mult;#LT N#GT", kTProfile, {nChAxis}); - histos.add("MCGen/Prof_Cent_MeanpT", ";cent;#LT p_{T}#GT", kTProfile, {centAxis1Per}); - histos.add("MCGen/Prof_Mult_MeanpT", ";mult;#LT p_{T}#GT", kTProfile, {nChAxis}); - - histos.add("pmeanTru_nch_etabin", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("pmeanReco_nch_etabin", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("pmeanRecoEffcorr_nch_etabin", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("pmeanMultTru_nch_etabin", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("pmeanMultReco_nch_etabin", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("pmeanMultRecoEffcorr_nch_etabin", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - - histos.add("MCGen/hEtaPhiReco", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); - histos.add("MCGen/hEtaPhiRecoEffWtd", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); - histos.add("MCGen/hEtaPhiRecoWtd", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); - - histos.add("Prof2D_MeanpTSub_Tru", ";cent;etaA;etaC", kTProfile3D, {{centAxis1Per}, {etaBinAxis}, {etaBinAxis}}); - histos.add("Prof2D_MeanpTSub_Reco", ";cent;etaA;etaC", kTProfile3D, {{centAxis1Per}, {etaBinAxis}, {etaBinAxis}}); - histos.add("Prof2D_MeanpTSub_RecoEffCorr", ";cent;etaA;etaC", kTProfile3D, {{centAxis1Per}, {etaBinAxis}, {etaBinAxis}}); + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(mask, v) || hists.flat[v].maps) { + continue; + } + hists.flat[v].maps = reg(v).add((systPrefix(v) + dir + "hFlatMaps").c_str(), ";vz;sign;pt;eta;phi;weight", kTHnSparseF, + {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}, {kNFlatType, -0.5, static_cast(kNFlatType) - 0.5}}); + labelAxis(hists.flat[v].maps->GetAxis(5), {"raw", "(1-fake)/eff", "all weights"}); + } } - void declareMCFlucHists() + // Mean-pass profiles; MC: in MCGen/, MCReco/, MCRecoEffCorr/. + void declareMean(uint32_t mask, bool isMC) { - histos.add("MCGen/Prof_Cent_NEta_Nchrec", ";cent;eta", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("MCGen/Prof_Mult_NEta_Nchrec", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("MCGen/Prof_Cent_NEta_MeanpT", ";cent;eta", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("MCGen/Prof_Mult_NEta_MeanpT", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - - histos.add("MCGen/Prof_MeanpT_Cent_etabin", ";cent;eta", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("MCGen/Prof_C2_Cent_etabin", ";cent;eta", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("MCGen/Prof_C2Sub_Cent_etabin", ";cent;eta", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("MCGen/Prof_Cov_Cent_etabin", ";cent;eta", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("MCGen/Prof_CovFT0A_Cent_etabin", ";cent;eta", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("MCGen/Prof_CovFT0C_Cent_etabin", ";cent;eta", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - - histos.add("MCGen/Prof_MeanpT_Mult_etabin", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("MCGen/Prof_C2_Mult_etabin", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("MCGen/Prof_C2Sub_Mult_etabin", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("MCGen/Prof_Cov_Mult_etabin", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("MCGen/Prof_CovFT0A_Mult_etabin", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("MCGen/Prof_CovFT0C_Mult_etabin", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - - histos.add("MCGen/Prof_C3_Cent_etabin", ";cent;eta", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("MCGen/Prof_C3_Mult_etabin", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("MCGen/Prof_C3Sub_Cent_etabin", ";cent;eta", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("MCGen/Prof_C3Sub_Mult_etabin", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - - histos.add("MCGen/hEtaPhiReco", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); - histos.add("MCGen/hEtaPhiRecoEffWtd", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); - histos.add("MCGen/hEtaPhiRecoWtd", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); - - histos.add("MCGen/Prof_C2Sub2D_Cent_etaA_etaC", ";cent;etaA;etaC", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - histos.add("MCGen/Prof_GapSum2D", ";cent;gap;sum", kTProfile3D, {{centAxis1Per}, {gapAxis}, {sumAxis}}); - histos.add("MCGen/Prof_C3Sub2D_Cent_etaA_etaC", ";cent;etaA;etaC", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - histos.add("MCGen/Prof_C3GapSum2D", ";cent;gap;sum", kTProfile3D, {{centAxis1Per}, {gapAxis}, {sumAxis}}); - histos.add("MCGen/Prof_Cov2D_Cent_etaA_etaC", ";cent;etaA;etaC", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - histos.add("MCGen/Prof_CovFT0A2D_Cent_etaA_etaC", ";cent;etaA;etaC", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - histos.add("MCGen/Prof_CovFT0C2D_Cent_etaA_etaC", ";cent;etaA;etaC", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); + static const std::array mcDir = {"MCGen/", "MCReco/", "MCRecoEffCorr/"}; + const int nLev = isMC ? kNLevels : 1; + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(mask, v)) { + continue; + } + auto& r = reg(v); + for (int l = 0; l < nLev; ++l) { + const std::string p = systPrefix(v) + (isMC ? mcDir[l] : std::string{}); + auto& h = hists.mean[v][l]; + h.mean = r.add((p + "Prof_Mean_Nch_etabin_obs").c_str(), ";N_{ch};#eta-bin;observable (0 #LT[p_{T}]#GT, 1 #LT N#GT)", kTProfile3D, {{nchAxisOf(v)}, {etaBinAxis}, {kNMeanObs, -0.5, static_cast(kNMeanObs) - 0.5}}); + if (cfgFwdOpt.cfgFwd) { + h.fwdMean = r.add((p + "Prof_FwdAmpMean_Nch_fwdbin").c_str(), ";N_{ch};forward bin;#LT a#GT", kTProfile2D, {{nchAxisOf(v)}, {fwdAxis}}); + } + } + } } - void declareDataGetFlatHists() + // Fluctuation-pass profiles named prefix + name + suffix, with x axis ax. + // withGapSum: also the maps in (Delta eta, Sigma eta). + FlucHists declareFluc(HistogramRegistry& r, const std::string& p, const std::string& s, const AxisSpec& ax, bool withGapSum) { - histos.add("hEtaPhiReco", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); - histos.add("hEtaPhiRecoEffWtd", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); - histos.add("hEtaPhiRecoWtd", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); - histos.add("hnTrkPVZDC", ";N_{PV};ZDC_{A+C}", kTH2F, {{nChAxis2}, {200, 0, 3000}}); - histos.add("hNchZDC", ";N_{trk};ZDC_{A+C}", kTH2F, {{nChAxis2}, {200, 0, 30000}}); + FlucHists h; + auto n = [&](const char* name) { return p + name + s; }; + h.obs = r.add(n("Prof_Obs_Nch_etabin_obs").c_str(), ";N_{ch};#eta-bin;observable (0 N, 1 [p_{T}], 2 c_{2}, 3 c_{3}, 4 C_{2}^{sub} mirror, 5 C_{3}^{sub} mirror)", kTProfile3D, {{ax}, {etaBinAxis}, {kNFlucObs, -0.5, static_cast(kNFlucObs) - 0.5}}); + h.c2Sub2D = r.add(n("Prof_C2Sub2D_Nch_etaA_etaC").c_str(), ";N_{ch};#eta_{A};#eta_{C}", kTProfile3D, {{ax}, {etaAxis}, {etaAxis}}); + h.c3Sub2D = r.add(n("Prof_C3Sub2D_Nch_etaA_etaC").c_str(), ";N_{ch};#eta_{A} (pair);#eta_{C} (single)", kTProfile3D, {{ax}, {etaAxis}, {etaAxis}}); + if (withGapSum) { + h.gapSum2D = r.add(n("Prof_GapSum2D_Nch_gap_sum").c_str(), ";N_{ch};#eta_{A} #minus #eta_{C} (Gap);#eta_{A} + #eta_{C} (Sum)", kTProfile3D, {{ax}, {gapAxis}, {sumAxis}}); + h.c3GapSum2D = r.add(n("Prof_C3GapSum2D_Nch_gap_sum").c_str(), ";N_{ch};#eta_{pair} #minus #eta_{single};#eta_{pair} + #eta_{single}", kTProfile3D, {{ax}, {gapAxis}, {sumAxis}}); + } + if (cfgFwdOpt.cfgFwd) { + h.fwdVar = r.add(n("Prof_FwdAmpVar_Nch_fwdbin").c_str(), ";N_{ch};forward bin;#LT #delta a^{2}#GT", kTProfile2D, {{ax}, {fwdAxis}}); + h.covFwdPt = r.add(n("Prof_CovFwdAmpMeanpT_Nch_etabin_fwdbin").c_str(), ";N_{ch};#eta-bin;forward bin;#LT #delta[p_{T}] #delta a#GT", kTProfile3D, {{ax}, {etaBinAxis}, {fwdAxis}}); + h.covFwdN = r.add(n("Prof_CovFwdAmpNch_Nch_etabin_fwdbin").c_str(), ";N_{ch};#eta-bin;forward bin;#LT #delta N #delta a#GT", kTProfile3D, {{ax}, {etaBinAxis}, {fwdAxis}}); + } + return h; } - void declareDataMeanHists() + // Gap/sum maps for every DATA variation; in MC only for Base, to limit the memory. + void declareFlucAll(uint32_t mask, bool isMC) { - histos.add("pmeanFT0Amultpv", "N_{PV}; AmplitudeA", kTProfile, {nChAxis}); - histos.add("pmeanFT0A_cent", "cent; AmplitudeA", kTProfile, {centAxis1Per}); - histos.add("pmeanFT0Cmultpv", "N_{PV}; AmplitudeC", kTProfile, {nChAxis}); - histos.add("pmeanFT0C_cent", "cent; AmplitudeC", kTProfile, {centAxis1Per}); + static const std::array mcDir = {"MCGen/", "MCReco/", "MCRecoEffCorr/"}; + const int nLev = isMC ? kNLevels : 1; + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(mask, v)) { + continue; + } + for (int l = 0; l < nLev; ++l) { + hists.fluc[v][l] = declareFluc(reg(v), systPrefix(v) + (isMC ? mcDir[l] : std::string{}), "", nchAxisOf(v), !isMC || v == kSystBase); + } + } + } - histos.add("pmean_cent_id_eta_FT0", ";cent;channel id; #eta;amplitude", kTProfile3D, {{centAxis1Per}, {200, -0.5, 199.5}, {100, -5.0, 5.0}}); - histos.add("h3_cent_id_eta_FT0", ";cent;channel id; #eta", kTH3F, {{centAxis1Per}, {200, -0.5, 199.5}, {100, -5.0, 5.0}}); + // Poisson-bootstrap replicas of all fluctuation profiles (Base, DataFluc only). Their x axis + // is the Base centrality in 5% classes, the binning in which the errors are evaluated. + void declareBootstrapHists() + { + for (int s = 0; s < nBoot; ++s) { + hists.boot[s] = declareFluc(histosBoot, "Bootstrap/", Form("_sample%d", s), centAxisBoot, true); + } + } - histos.add("Prof_Cent_Nchrec", ";cent;#LT N_{PV}#GT", kTProfile, {centAxis1Per}); - histos.add("Prof_Mult_Nchrec", ";N_{PV};#LT N_{PV}#GT", kTProfile, {nChAxis}); - histos.add("Prof_Cent_MeanpT", ";cent;#LT p_{T}#GT", kTProfile, {centAxis1Per}); - histos.add("Prof_Mult_MeanpT", ";N_{PV};#LT p_{T}#GT", kTProfile, {nChAxis}); + // Histograms of each pass, booked after the inputs so that switched-off variations get none. + void declareMCGetEffHists() + { + declareNchCent(maskEff); + declareEff(maskEff); + declareMCQA(); + } - histos.add("pmean_nch_etabin", ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("pmeanMult_nch_etabin", ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("pmean_cent_etabin", ";Centrality (%);#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("pmeanMult_cent_etabin", ";Centrality (%);#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); + void declareMCGetFlatHists() + { + declareNchCent(maskFlat); + declareFlat(maskFlat, "MCReco/"); + } - histos.add("hEtaPhiReco", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); - histos.add("hEtaPhiRecoEffWtd", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); - histos.add("hEtaPhiRecoWtd", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); + void declareMCMeanHists() + { + declareNchCent(maskAll); + declareFlat(1, "MCReco/"); // Base map with all weights, as QA + if (cfgFwdOpt.cfgFwd) { + declareFwdQA(); + } + declareMean(maskAll, true); + histos.add("Prof_Weights_pT_type", ";p_{T};weight (0 eff, 1 fake, 2 w)", kTProfile2D, {{KNbinsPtRes, KPtMin, KPtMax}, {3, -0.5, 2.5}}); + } - histos.add("Prof2D_MeanpTSub", ";cent;#eta_{A} bin;#eta_{C} bin", kTProfile3D, {{centAxis1Per}, {etaBinAxis}, {etaBinAxis}}); + void declareMCFlucHists() + { + declareNchCent(maskAll); + declareFlat(1, "MCReco/"); + if (cfgFwdOpt.cfgFwd) { + declareFwdQA(); + } + declareFlucAll(maskAll, true); + } - histos.add("pEffWeight_pt_eta_cent", ";p_{T} (GeV/c);#eta;cent;#LT eff#GT", kTProfile3D, {{KNbinsPtRes, KPtMin, KPtMax}, {KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}, {centAxis1Per}}); - histos.add("pFakeWeight_pt_eta_cent", ";p_{T} (GeV/c);#eta;cent;#LT fake#GT", kTProfile3D, {{KNbinsPtRes, KPtMin, KPtMax}, {KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}, {centAxis1Per}}); - histos.add("pFlatWeight_pt_eta_cent", ";p_{T} (GeV/c);#eta;cent;#LT w_{#phi}#GT", kTProfile3D, {{KNbinsPtRes, KPtMin, KPtMax}, {KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}, {centAxis1Per}}); + void declareDataGetFlatHists() + { + declareNchCent(maskFlat); + declareFlat(maskFlat, ""); } - void declareDataFlucHists() + void declareDataMeanHists() { - histos.add("Prof_MeanpT_Cent_etabin", ";cent;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("Prof_MeanpT_Mult_etabin", ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("Prof_C2_Cent_etabin", ";cent;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("Prof_C2_Mult_etabin", ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("Prof_C2Sub_Cent_etabin", ";Centrality;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("Prof_C2Sub_Mult_etabin", ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("Prof_Cov_Cent_etabin", ";Centrality;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("Prof_Cov_Mult_etabin", ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("Prof_CovFT0A_Cent_etabin", ";Centrality;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("Prof_CovFT0A_Mult_etabin", ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("Prof_CovFT0C_Cent_etabin", ";Centrality;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("Prof_CovFT0C_Mult_etabin", ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - - histos.add("Prof_C3_Cent_etabin", ";cent;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("Prof_C3_Mult_etabin", ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("Prof_C3Sub_Cent_etabin", ";Centrality;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("Prof_C3Sub_Mult_etabin", ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - - histos.add("hEtaPhiReco", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); - histos.add("hEtaPhiRecoEffWtd", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); - histos.add("hEtaPhiRecoWtd", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); - - histos.add("Prof_C2Sub2D_Cent_etaA_etaC", ";cent;#eta_{A};#eta_{C}", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - histos.add("Prof_GapSum2D", ";cent;#Delta#eta (Gap);#Sigma#eta/2 (Sum)", kTProfile3D, {{centAxis1Per}, {gapAxis}, {sumAxis}}); - histos.add("Prof_C3Sub2D_Cent_etaA_etaC", ";cent;#eta_{A};#eta_{C}", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - histos.add("Prof_C3GapSum2D", ";cent;#Delta#eta (Gap);#Sigma#eta/2 (Sum)", kTProfile3D, {{centAxis1Per}, {gapAxis}, {sumAxis}}); - histos.add("Prof_Cov2D_Cent_etaA_etaC", ";cent;#eta_{A};#eta_{C}", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - histos.add("Prof_CovFT0A2D_Cent_etaA_etaC", ";cent;#eta_{A};#eta_{C}", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - histos.add("Prof_CovFT0C2D_Cent_etaA_etaC", ";cent;#eta_{A};#eta_{C}", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - } - - // 30 Poisson-bootstrap replicas of every final fluctuation observable - // (base data run only). Filled by pointer to bypass the compile-time HIST() - // macro, which cannot take a runtime sample index. - void declareBootstrapHists() + declareNchCent(maskAll); + declareFlat(1, ""); + if (cfgFwdOpt.cfgFwd) { + declareFwdQA(); + } + declareMean(maskAll, false); + histos.add("Prof_Weights_pt_eta_type", ";p_{T} (GeV/c);#eta;weight (0 eff, 1 fake, 2 w_{#phi})", kTProfile3D, {{KNbinsPtRes, KPtMin, KPtMax}, {KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}, {3, -0.5, 2.5}}); + } + + void declareDataFlucHists() { - for (int s = 0; s < nBoot; ++s) { - bs.multCent[s] = histos.add(Form("Bootstrap/Prof_Mult_Cent_etabin_sample%d", s), ";cent;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - bs.multMult[s] = histos.add(Form("Bootstrap/Prof_Mult_Mult_etabin_sample%d", s), ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - bs.amplFT0ACent[s] = histos.add(Form("Bootstrap/Prof_AmplFT0A_Cent_sample%d", s), ";cent;AmplitudeA", kTProfile, {centAxis1Per}); - bs.amplFT0AMult[s] = histos.add(Form("Bootstrap/Prof_AmplFT0A_Mult_sample%d", s), ";N_{PV};AmplitudeA", kTProfile, {nChAxis}); - bs.amplFT0CCent[s] = histos.add(Form("Bootstrap/Prof_AmplFT0C_Cent_sample%d", s), ";cent;AmplitudeC", kTProfile, {centAxis1Per}); - bs.amplFT0CMult[s] = histos.add(Form("Bootstrap/Prof_AmplFT0C_Mult_sample%d", s), ";N_{PV};AmplitudeC", kTProfile, {nChAxis}); - bs.meanpTCent[s] = histos.add(Form("Bootstrap/Prof_MeanpT_Cent_etabin_sample%d", s), ";cent;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - bs.meanpTMult[s] = histos.add(Form("Bootstrap/Prof_MeanpT_Mult_etabin_sample%d", s), ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - bs.c2Cent[s] = histos.add(Form("Bootstrap/Prof_C2_Cent_etabin_sample%d", s), ";cent;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - bs.c2Mult[s] = histos.add(Form("Bootstrap/Prof_C2_Mult_etabin_sample%d", s), ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - bs.c2SubCent[s] = histos.add(Form("Bootstrap/Prof_C2Sub_Cent_etabin_sample%d", s), ";cent;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - bs.c2SubMult[s] = histos.add(Form("Bootstrap/Prof_C2Sub_Mult_etabin_sample%d", s), ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - bs.c3Cent[s] = histos.add(Form("Bootstrap/Prof_C3_Cent_etabin_sample%d", s), ";cent;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - bs.c3Mult[s] = histos.add(Form("Bootstrap/Prof_C3_Mult_etabin_sample%d", s), ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - bs.c3SubCent[s] = histos.add(Form("Bootstrap/Prof_C3Sub_Cent_etabin_sample%d", s), ";cent;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - bs.c3SubMult[s] = histos.add(Form("Bootstrap/Prof_C3Sub_Mult_etabin_sample%d", s), ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - bs.covCent[s] = histos.add(Form("Bootstrap/Prof_Cov_Cent_etabin_sample%d", s), ";cent;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - bs.covMult[s] = histos.add(Form("Bootstrap/Prof_Cov_Mult_etabin_sample%d", s), ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - bs.covFT0ACent[s] = histos.add(Form("Bootstrap/Prof_CovFT0A_Cent_etabin_sample%d", s), ";cent;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - bs.covFT0AMult[s] = histos.add(Form("Bootstrap/Prof_CovFT0A_Mult_etabin_sample%d", s), ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - bs.covFT0CCent[s] = histos.add(Form("Bootstrap/Prof_CovFT0C_Cent_etabin_sample%d", s), ";cent;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - bs.covFT0CMult[s] = histos.add(Form("Bootstrap/Prof_CovFT0C_Mult_etabin_sample%d", s), ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - bs.c2Sub2D[s] = histos.add(Form("Bootstrap/Prof_C2Sub2D_Cent_etaA_etaC_sample%d", s), ";cent;#eta_{A};#eta_{C}", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - bs.gapSum2D[s] = histos.add(Form("Bootstrap/Prof_GapSum2D_sample%d", s), ";cent;gap;sum", kTProfile3D, {{centAxis1Per}, {gapAxis}, {sumAxis}}); - bs.c3Sub2D[s] = histos.add(Form("Bootstrap/Prof_C3Sub2D_Cent_etaA_etaC_sample%d", s), ";cent;#eta_{A};#eta_{C}", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - bs.c3GapSum2D[s] = histos.add(Form("Bootstrap/Prof_C3GapSum2D_sample%d", s), ";cent;gap;sum", kTProfile3D, {{centAxis1Per}, {gapAxis}, {sumAxis}}); - bs.cov2D[s] = histos.add(Form("Bootstrap/Prof_Cov2D_Cent_etaA_etaC_sample%d", s), ";cent;#eta_{A};#eta_{C}", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - bs.covFT0A2D[s] = histos.add(Form("Bootstrap/Prof_CovFT0A2D_Cent_etaA_etaC_sample%d", s), ";cent;#eta_{A};#eta_{C}", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - bs.covFT0C2D[s] = histos.add(Form("Bootstrap/Prof_CovFT0C2D_Cent_etaA_etaC_sample%d", s), ";cent;#eta_{A};#eta_{C}", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); + declareNchCent(maskAll); + declareFlat(1, ""); + if (cfgFwdOpt.cfgFwd) { + declareFwdQA(); + } + declareFlucAll(maskAll, false); + if (doBoot) { + declareBootstrapHists(); } } // =========================================================================== // CCDB helpers // =========================================================================== - THnSparseF* buildWeightMapFromRaw(THnSparseF* hRaw, const char* mapName) + static void copyAxis(TAxis* dst, const TAxis* src) { - if (!hRaw) { - LOGF(error, "Raw eta-phi map for '%s' is null; no flattening will be applied.", mapName); - return nullptr; + const TArrayD* xb = src->GetXbins(); + if (xb->GetSize() > 0) { + dst->Set(src->GetNbins(), xb->GetArray()); + } else { + dst->Set(src->GetNbins(), src->GetXmin(), src->GetXmax()); } - auto hWMap = dynamic_cast(hRaw->Clone(mapName)); - hWMap->SetTitle(Form("Flattening Weight Map %s (w_{#phi} = / N_{#phi})", mapName)); - hWMap->Reset(); - auto axV = hRaw->GetAxis(0); - auto axChg = hRaw->GetAxis(1); - auto axPt = hRaw->GetAxis(2); - auto axE = hRaw->GetAxis(3); - auto axP = hRaw->GetAxis(4); + } - std::array bins{}; - for (int iv = 1; iv <= axV->GetNbins(); ++iv) { - bins[0] = iv; - for (int ichg = 1; ichg <= axChg->GetNbins(); ++ichg) { - bins[1] = ichg; - for (int ipt = 1; ipt <= axPt->GetNbins(); ++ipt) { - bins[2] = ipt; - for (int ie = 1; ie <= axE->GetNbins(); ++ie) { - bins[3] = ie; + // Flattening weights w_phi = / N_phi from bin `type` of the last axis of hFlatMaps. + THnSparseF* buildWeightMapFromRaw(const THnSparse* hRaw, int type, const char* mapName) + { + if (!hRaw || hRaw->GetNdimensions() != 6) { + LOGF(error, "Raw eta-phi map for '%s' is null or not 6D; no flattening will be applied.", mapName); + return nullptr; + } + std::array nb{}; + for (int i = 0; i < 5; ++i) { + nb[i] = hRaw->GetAxis(i)->GetNbins(); + } + auto* hWMap = new THnSparseF(mapName, Form("Flattening Weight Map %s (w_{#phi} = / N_{#phi})", mapName), 5, nb.data()); + for (int i = 0; i < 5; ++i) { + copyAxis(hWMap->GetAxis(i), hRaw->GetAxis(i)); + } + + std::array bins{}; + std::array wbins{}; + bins[5] = type + 1; + for (int iv = 1; iv <= nb[0]; ++iv) { + bins[0] = wbins[0] = iv; + for (int ichg = 1; ichg <= nb[1]; ++ichg) { + bins[1] = wbins[1] = ichg; + for (int ipt = 1; ipt <= nb[2]; ++ipt) { + bins[2] = wbins[2] = ipt; + for (int ie = 1; ie <= nb[3]; ++ie) { + bins[3] = wbins[3] = ie; double sum = 0.0; - int nphi = axP->GetNbins(); + const int nphi = nb[4]; for (int ip = 1; ip <= nphi; ++ip) { bins[4] = ip; sum += hRaw->GetBinContent(bins.data()); } const double avg = (nphi > 0 ? sum / nphi : 0.0); for (int ip = 1; ip <= nphi; ++ip) { - bins[4] = ip; + bins[4] = wbins[4] = ip; const double raw = hRaw->GetBinContent(bins.data()); const double w = (avg > 0.0 && raw > 0.0) ? (avg / raw) : 1.0; - hWMap->SetBinContent(bins.data(), w); + hWMap->SetBinContent(wbins.data(), w); } } } @@ -1105,9 +1949,117 @@ struct RadialFlowDecorr { LOGF(error, "%s is not the expected profile type (it is %s)", name, obj->ClassName()); return; } - target = dynamic_cast(tp->Clone()); - target->SetDirectory(nullptr); - LOGF(info, "Loaded %s from list", name); + target = dynamic_cast(tp->Clone()); + target->SetDirectory(nullptr); + LOGF(info, "Loaded %s from list", name); + } + + // Switch variation v off because an input is missing; a missing Base input is fatal. + void dropSyst(int v, const std::string& why) + { + if (v == kSystBase) { + LOGF(fatal, "Base input missing: %s", why.c_str()); + return; + } + LOGF(error, "Variation %s disabled: %s", systName[v].c_str(), why.c_str()); + CLRBIT(maskAll, v); + CLRBIT(maskEff, v); + CLRBIT(maskFlat, v); + } + + // Efficiency and fake-rate maps of variation s versus (centrality, pT, eta): + // eff = matched primaries / primaries, fake = (secondaries + other collision + no label) / all reco. + bool loadEffMaps(int s, const std::string& path, int64_t now) + { + auto* lst = ccdb->getForTimeStamp(path, now); + if (!lst) { + return false; + } + std::array h{}; + for (int c = 0; c < kNEffCat; ++c) { + h[c] = dynamic_cast(lst->FindObject(effMapName[c].c_str())); + if (!h[c]) { + LOGF(error, "Missing efficiency map %s in %s", effMapName[c].c_str(), path.c_str()); + return false; + } + } + auto* eff = dynamic_cast(h[kEffRecoMatchedPrim]->Clone(Form("hEff_%d", s))); + auto* fake = dynamic_cast(h[kEffRecoSec]->Clone(Form("hFake_%d", s))); + eff->SetDirectory(nullptr); + fake->SetDirectory(nullptr); + fake->Add(h[kEffRecoOtherColl]); + fake->Add(h[kEffRecoFake]); + // integrated over centrality (one centrality bin), for empty or invalid cells + const int nCent = eff->GetNbinsX(); + auto ratio = [&](TH3* num, TH3* den, const char* name) { + auto* r = num->RebinX(nCent, name); + std::unique_ptr d(den->RebinX(nCent, Form("%s_den", name))); + r->SetDirectory(nullptr); + r->Divide(d.get()); + return r; + }; + inputs[s].hEffPtEta = ratio(eff, h[kEffAllPrim], Form("hEff_%d_ptEta", s)); + inputs[s].hFakePtEta = ratio(fake, h[kEffAllReco], Form("hFake_%d_ptEta", s)); + eff->Divide(h[kEffAllPrim]); + fake->Divide(h[kEffAllReco]); + inputs[s].hEff = eff; + inputs[s].hFake = fake; + return true; + } + + THnSparseF* loadFlatMap(int s, const std::string& path, const std::string& dir, int64_t now) + { + auto* lst = ccdb->getForTimeStamp(path, now); + if (!lst) { + return nullptr; + } + const std::string hName = dir + "hFlatMaps"; + const auto* hRaw = dynamic_cast(lst->FindObject(hName.c_str())); + if (!hRaw) { + LOGF(error, "Flattening map '%s' not found in %s", hName.c_str(), path.c_str()); + return nullptr; + } + return buildWeightMapFromRaw(hRaw, cfgEff ? kFlatWtd : kFlatRaw, Form("hFlatWeight_%d", s)); + } + + // Mean-pass objects of variation v for the fluctuation pass. + void loadMeans(int v, const std::string& path, bool isMC, int64_t now) + { + static const std::array mcDir = {"MCGen/", "MCReco/", "MCRecoEffCorr/"}; + auto* lst = ccdb->getForTimeStamp(path, now); + if (!lst) { + dropSyst(v, "no mean-pT list at " + path); + return; + } + const int nLev = isMC ? kNLevels : 1; + for (int l = 0; l < nLev; ++l) { + const std::string d = isMC ? mcDir[l] : std::string{}; + loadProfileFromList(lst, (d + "Prof_Mean_Nch_etabin_obs").c_str(), inputs[v].pmean[l]); + if (!inputs[v].pmean[l]) { + dropSyst(v, "mean-pT profile missing in " + path); + return; + } + // The fluctuation pass reads its reference at (N_ch bin, eta bin), so the binning of the + // mean pass has to be the same. + const auto* p = inputs[v].pmean[l]; + if (p->GetYaxis()->GetNbins() != nEta + 1 || + p->GetXaxis()->GetNbins() != nchAxisOf(v).getNbins() || + std::abs(p->GetXaxis()->GetBinWidth(1) - nchBinWidthOf(v)) > KFloatEpsilon) { + LOGF(fatal, + "Mean pass %s has %d N_ch bins of width %.1f and %d eta bins; this job uses %d bins of " + "width %d and %d eta bins. Rerun the mean pass with the same binning.", + path.c_str(), p->GetXaxis()->GetNbins(), p->GetXaxis()->GetBinWidth(1), p->GetYaxis()->GetNbins(), + nchAxisOf(v).getNbins(), nchBinWidthOf(v), nEta + 1); + } + if (cfgFwdOpt.cfgFwd) { + loadProfileFromList(lst, (d + "Prof_FwdAmpMean_Nch_fwdbin").c_str(), inputs[v].pmeanFwd[l]); + if (inputs[v].pmeanFwd[l] && inputs[v].pmeanFwd[l]->GetYaxis()->GetNbins() != nFwd) { + LOGF(error, "%s: mean pass has %d forward bins, this job %d; forward covariances not filled", + path.c_str(), inputs[v].pmeanFwd[l]->GetYaxis()->GetNbins(), nFwd); + inputs[v].pmeanFwd[l] = nullptr; + } + } + } } // =========================================================================== @@ -1116,13 +2068,27 @@ struct RadialFlowDecorr { void init(InitContext&) { TH1::SetDefaultSumw2(true); - // Nch axes by system + // Multiplicity axes: multQAAxis for the QA, nchAxisObs (nchAxisNchBin2) for the observables. + const int nMax = (cfgSys == kPbPb) ? cfgNch.cfgNchPbMax.value : (cfgSys == kpp ? cfgNch.cfgNchPPMax.value : cfgNch.cfgNchOMax.value); if (cfgSys == kPbPb) { - nChAxis = {cfgNchPbMax / 2, KBinOffset, cfgNchPbMax + KBinOffset, "Nch", "PV-contributor track multiplicity"}; - nChAxis2 = {cfgNchPbMax / 4, KBinOffset, cfgNchPbMax + KBinOffset, "Nch", "PV-contributor track multiplicity"}; + multQAAxis = {nMax / 2, KBinOffset, nMax + KBinOffset, "Nch", "PV-contributor track multiplicity"}; } else { - nChAxis = {cfgNchOMax, KBinOffset, cfgNchOMax + KBinOffset, "Nch", "PV-contributor track multiplicity"}; - nChAxis2 = {cfgNchOMax, KBinOffset, cfgNchOMax + KBinOffset, "Nch", "PV-contributor track multiplicity"}; + multQAAxis = {nMax, KBinOffset, nMax + KBinOffset, "Nch", "PV-contributor track multiplicity"}; + } + { + // bin k holds k*width ... (k+1)*width - 1 tracks + auto makeNchAxis = [&](int width) { + if (width < 1) { + LOGF(fatal, "N_ch bin width must be >= 1 (got %d)", width); + } + const int nb = (nMax + width - 1) / width; + return AxisSpec{nb, -KBinOffset, nb * width - KBinOffset, "N_{ch}"}; + }; + const bool pp = (cfgSys == kpp); + nchWidth = (cfgSys == kPbPb) ? cfgNch.cfgNchBinWidthPbPb.value : (pp ? cfgNch.cfgNchBinWidthPP.value : cfgNch.cfgNchBinWidthOO.value); + nchWidthNchBin2 = (cfgSys == kPbPb) ? KSystNchBinWidthPbPb : (pp ? KSystNchBinWidthPP : KSystNchBinWidthOO); + nchAxisObs = makeNchAxis(nchWidth); + nchAxisNchBin2 = makeNchAxis(nchWidthNchBin2); } // ---- run type: MC is pinned to the reference binning, DATA is configurable ---- @@ -1178,13 +2144,78 @@ struct RadialFlowDecorr { } etaAxis = AxisSpec{obsEdges, "#eta"}; etaBinAxis = AxisSpec{nEta + 1, -0.5, static_cast(nEta) + 0.5, "#eta bin Number"}; - LOGF(info, "Observable eta binning (%s): %d bins of width %.2f over |eta|<%.2f (+ reference), nEta=%d", - isMcRun ? "MC pinned" : "DATA", nbins, width, halfEta, nEta); + // (Delta eta, Sigma eta): fills are integer multiples of w, so 2N-1 bins + // of width w centred on them, out to (N-1/2) w. + const int nGS = 2 * nbins - 1; + const double gsMax = (nbins - 0.5) * width; + gapAxis = AxisSpec{nGS, -gsMax, gsMax, "#Delta#eta"}; + sumAxis = AxisSpec{nGS, -gsMax, gsMax, "#Sigma#eta"}; + } + + // ---- forward bins: detector totals, then the eta sub-bins of each detector ---- + { + static const std::array detName = {"FT0A", "FT0C", "FV0A"}; + fwdEdges = {cfgFwdOpt.cfgFwdEtaEdgesFT0A.value, cfgFwdOpt.cfgFwdEtaEdgesFT0C.value, cfgFwdOpt.cfgFwdEtaEdgesFV0A.value}; + fwdLabels.assign(detName.begin(), detName.end()); + nFwd = kNFwdDet; + for (int det = 0; det < kNFwdDet; ++det) { + auto& e = fwdEdges[det]; + if (e.size() < 2 || !std::is_sorted(e.begin(), e.end())) { + LOGF(fatal, "%s eta edges must be >= 2 increasing values", detName[det]); + } + fwdOffset[det] = nFwd; + for (std::size_t i = 0; i + 1 < e.size(); ++i) { + fwdLabels.push_back(Form("%s %.1f<#eta<%.1f", detName[det], e[i], e[i + 1])); + ++nFwd; + } + } + if (nFwd > KNFwdMax) { + LOGF(fatal, "%d forward bins exceed KNFwdMax=%d", nFwd, KNFwdMax); + } + fwdAxis = AxisSpec{nFwd, -0.5, nFwd - 0.5, "forward bin"}; + std::string list; + for (int k = 0; k < nFwd; ++k) { + list += Form("%s%d %s", k > 0 ? ", " : "", k, fwdLabels[k].c_str()); + } + LOGF(info, "RadialFlowDecorr forward bins: %s", list.c_str()); + } + + // ---- variations ---- + const uint32_t active = (static_cast(cfgSyst.cfgSystMask.value) | 1) & KAllSystMask; + maskAll = maskEff = maskFlat = 0; + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(active, v)) { + continue; + } + SETBIT(maskAll, v); + if (effSource(v) == v) { + SETBIT(maskEff, v); + } + if (flatSource(v) == v) { + SETBIT(maskFlat, v); + } + } + const bool isPP = (cfgSys == kpp); + itsLayerNom = cfgEvSel.cfgITSLayerCut >= 0 ? cfgEvSel.cfgITSLayerCut.value : (isPP ? kITSLayer0123 : kITSLayersAll); + if (itsLayerNom == kITSLayersAll) { + itsLayerVar = kITSLayer0123; + } else if (itsLayerNom == kITSLayer0123) { + itsLayerVar = kITSLayer3; + } else { + itsLayerVar = kITSLayersAll; + } + occupancyMax = (cfgSys == kPbPb) ? cfgSyst.cfgSystOccMaxPbPb.value : (isPP ? cfgSyst.cfgSystOccMaxPP.value : cfgSyst.cfgSystOccMaxOO.value); + pupNSigma = (cfgSys == kPbPb) ? cfgPileup.cfgPupnSigPbPb.value : (isPP ? cfgPileup.cfgPupnSigPP.value : cfgPileup.cfgPupnSigOO.value); + if (cfgEvSel.cfgCentralityChoice == KSystCentralityChoice && TESTBIT(maskAll, kSystCent)) { + LOGF(warning, "systCent uses the same centrality estimator as Base (%d).", cfgEvSel.cfgCentralityChoice.value); } - // bootstrap active only for the base data fluctuation pass - doBoot = cfgRunDataFluc && (cfgSystType == kSystBase) && (cfgNBootstrap > 0); + // bootstrap: Base data fluctuation pass only + doBoot = cfgRunDataFluc && (cfgNBootstrap > 0); nBoot = std::min(cfgNBootstrap, KMaxBoot); + if (cfgNBootstrap > KMaxBoot) { + LOGF(warning, "cfgNBootstrap=%d capped at %d", cfgNBootstrap.value, KMaxBoot); + } rng.SetSeed(cfgBootstrapSeed); // ---- CCDB ---- @@ -1193,9 +2224,10 @@ struct RadialFlowDecorr { ccdb->setLocalObjectValidityChecking(); int64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); ccdb->setCreatedNotAfter(now); - - loadAlignParam(now); - ft0Det.calculateChannelCenter(); + // a missing object returns nullptr: every lookup is checked (a missing variation input switches + // the variation off, a missing Base input or alignment is fatal, a missing gain gives unit gains) + ccdb->setFatalWhenNull(false); + fwdCalib.fv0Det = o2::fv0::Geometry::instance(o2::fv0::Geometry::eUninitialized); std::string sysDir; switch (cfgSys) { @@ -1214,962 +2246,674 @@ struct RadialFlowDecorr { default: LOGF(fatal, "Invalid cfgSys value: %d", cfgSys.value); } - std::string pathEff = cfgCCDBUserPath.value + "/" + sysDir + "/Job1_EffMaps"; - std::string pathMCFlat = cfgCCDBUserPath.value + "/" + sysDir + "/Job1_MCFlatMaps"; - std::string pathMCMean = cfgCCDBUserPath.value + "/" + sysDir + "/Job2_MCMean"; - std::string pathDataFlat = cfgCCDBUserPath.value + "/" + sysDir + "/Job1_DataFlatMaps"; - std::string pathDataMean = cfgCCDBUserPath.value + "/" + sysDir + "/Job2_DataMean"; + const std::string pathEff = cfgCCDBUserPath.value + "/" + sysDir + "/Job1_EffMaps"; + const std::string pathMCFlat = cfgCCDBUserPath.value + "/" + sysDir + "/Job1_MCFlatMaps"; + const std::string pathMCMean = cfgCCDBUserPath.value + "/" + sysDir + "/Job2_MCMean"; + const std::string pathDataFlat = cfgCCDBUserPath.value + "/" + sysDir + "/Job1_DataFlatMaps"; + const std::string pathDataMean = cfgCCDBUserPath.value + "/" + sysDir + "/Job2_DataMean"; - // ---- declarations ---- - declareCommonQA(); - if (cfgRunMCMean || cfgRunMCFluc || cfgRunGetEff) { - declareMCCommonHists(); - } - if (cfgRunGetMCFlat) { - declareMCGetFlatHists(); - histos.addClone("MCGen/", "MCReco/"); - histos.addClone("MCGen/", "MCRecoEffCorr/"); - } - if (cfgRunMCMean) { - declareMCMeanHists(); - histos.addClone("MCGen/", "MCReco/"); - histos.addClone("MCGen/", "MCRecoEffCorr/"); - } - if (cfgRunMCFluc) { - declareMCFlucHists(); - histos.addClone("MCGen/", "MCReco/"); - histos.addClone("MCGen/", "MCRecoEffCorr/"); + // ---- inputs of each variation; a variation without its inputs is switched off ---- + // variations used by the enabled passes + uint32_t used = 0; + if (cfgRunGetEff) { + used |= maskEff; } - if (cfgRunGetDataFlat) { - declareDataGetFlatHists(); - } - if (cfgRunDataMean) { - declareDataMeanHists(); + if (cfgRunGetMCFlat || cfgRunGetDataFlat) { + used |= maskFlat; } - if (cfgRunDataFluc) { - declareDataFlucHists(); - if (doBoot) { - declareBootstrapHists(); - } + if (cfgRunMCMean || cfgRunMCFluc || cfgRunDataMean || cfgRunDataFluc) { + used |= maskAll; } - // ---- efficiency/fake maps (inclusive) ---- - if (!cfgRunGetEff && cfgEff) { - auto* lst = ccdb->getForTimeStamp(pathEff, now); - if (!lst) { - LOGF(fatal, "Efficiency maps required but CCDB list is null at %s!", pathEff.c_str()); - return; - } - - // 1. Process Efficiency Map - auto* hNum = dynamic_cast(lst->FindObject("h3_RecoMatchedToPrimary")); - auto* hDen = dynamic_cast(lst->FindObject("h3_AllPrimary")); - if (hNum && hDen) { - state.hEff = dynamic_cast(hNum->Clone("hEff")); - state.hEff->SetDirectory(nullptr); - state.hEff->Sumw2(); - state.hEff->Divide(hDen); - - TH3F* rebinnedEff = rebinNchMap(state.hEff, cfgSys.value, "hEffRebinned"); - if (!rebinnedEff) { - LOGF(fatal, "Failed to rebin efficiency map"); - return; + // efficiency/fake maps + if (cfgEff && (cfgRunGetMCFlat || cfgRunMCMean || cfgRunMCFluc || isDataRun)) { + std::array loaded{}; + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(used, v)) { + continue; } - - delete state.hEff; - state.hEff = rebinnedEff; - - } else { - LOGF(fatal, "Missing CCDB objects for efficiency (h3_RecoMatchedToPrimary / h3_AllPrimary)."); - } - - // 2. Process Fake Map - auto* hNumS = dynamic_cast(lst->FindObject("h3_RecoUnMatchedToPrimary_Secondary")); - auto* hNumF = dynamic_cast(lst->FindObject("h3_RecoUnMatchedToPrimary_Fake")); - auto* hDenF = dynamic_cast(lst->FindObject("h3_AllReco")); - if (hNumS && hNumF && hDenF) { - state.hFake = dynamic_cast(hNumS->Clone("hFake")); - state.hFake->SetDirectory(nullptr); - state.hFake->Sumw2(); - state.hFake->Add(hNumF); // Secondary + fake: add exactly once. - state.hFake->Divide(hDenF); - - TH3F* rebinnedFake = rebinNchMap(state.hFake, cfgSys.value, "hFakeRebinned"); - if (!rebinnedFake) { - LOGF(fatal, "Failed to rebin fake map"); - return; + const int s = effSource(v); + if (loaded[s] == 0) { + loaded[s] = loadEffMaps(s, pathEff + systSuffix(s), now) ? 1 : -1; } - - delete state.hFake; - state.hFake = rebinnedFake; - - } else { - LOGF(fatal, "Missing CCDB objects for fakes."); - } - } - - // ---- flattening maps (inclusive) ---- - if (!cfgRunGetEff && cfgFlat) { - if (cfgRunDataMean || cfgRunDataFluc) { - auto* lstDataFlat = ccdb->getForTimeStamp(pathDataFlat, now); - if (lstDataFlat) { - std::string hName = cfgEff ? "hEtaPhiRecoWtd" : "hEtaPhiReco"; - auto* hRaw = dynamic_cast(lstDataFlat->FindObject(hName.c_str())); - if (hRaw) { - state.hFlatWeight = buildWeightMapFromRaw(hRaw, "hFlatWeight"); - } else { - LOGF(error, "Data flattening map '%s' not found.", hName.c_str()); - } - } else { - LOGF(error, "Could not retrieve Data Flattening TList from: %s", pathDataFlat.c_str()); - } - } - if (cfgRunMCMean || cfgRunMCFluc) { - auto* lstMCFlat = ccdb->getForTimeStamp(pathMCFlat, now); - if (lstMCFlat) { - std::string hName = cfgEff ? "MCReco/hEtaPhiRecoWtd" : "MCReco/hEtaPhiReco"; - auto* hRaw = dynamic_cast(lstMCFlat->FindObject(hName.c_str())); - if (hRaw) { - state.hFlatWeight = buildWeightMapFromRaw(hRaw, "hFlatWeight"); - } else { - LOGF(warning, "MC flattening source '%s' not found.", hName.c_str()); - } - } else { - LOGF(error, "Could not retrieve MC Flattening TList from: %s", pathMCFlat.c_str()); + if (loaded[s] < 0) { + dropSyst(v, "no efficiency maps at " + pathEff + systSuffix(s)); + continue; + } + inputs[v].hEff = inputs[s].hEff; + inputs[v].hFake = inputs[s].hFake; + inputs[v].hEffPtEta = inputs[s].hEffPtEta; + inputs[v].hFakePtEta = inputs[s].hFakePtEta; + } + } + + // flattening maps + if (cfgFlat && (cfgRunMCMean || cfgRunMCFluc || cfgRunDataMean || cfgRunDataFluc)) { + const bool mc = cfgRunMCMean || cfgRunMCFluc; + const std::string& path = mc ? pathMCFlat : pathDataFlat; + const std::string dir = mc ? "MCReco/" : ""; + std::array loaded{}; + for (int v = 0; v < kNSyst; ++v) { + const int s = flatSource(v); + if (!TESTBIT(maskAll, v) || s < 0) { + continue; } + if (loaded[s] == 0) { + inputs[s].hFlat = loadFlatMap(s, path + systSuffix(s), dir, now); + loaded[s] = inputs[s].hFlat ? 1 : -1; + } + if (loaded[s] < 0) { + dropSyst(v, "no flattening map at " + path + systSuffix(s)); + continue; + } + inputs[v].hFlat = inputs[s].hFlat; } } - // ---- sigma-pileup limits ---- - // These were produced by the (now removed) nSigma pass. They are re-sourced - // from the flattening-map file, which also stores Hist2D_globalTracks_cent. - // Loaded only when the sigma-pileup cut is actually requested. - if (cfgApplySigPupCut) { - const bool mcSide = (cfgRunGetEff || cfgRunGetMCFlat || cfgRunMCMean || cfgRunMCFluc); - std::string limPath = mcSide ? pathMCFlat : pathDataFlat; - auto* limList = ccdb->getForTimeStamp(limPath, now); - if (limList) { - auto loadLimits = [&](const char* name, std::vector>& limits, float& xMin, float& xMax) { - auto* h2 = dynamic_cast(limList->FindObject(name)); + // ---- n-sigma pile-up bands, from the Base flattening output of the same production ---- + // The flattening and efficiency passes only apply the linear cut: their maps define the bands. + { + const bool meanOrFluc = cfgRunMCMean || cfgRunMCFluc || cfgRunDataMean || cfgRunDataFluc; + const bool wantCent = cfgPileup.cfgApplySigPupCut && meanOrFluc; + const bool wantPV = cfgPileup.cfgApplySigPupCutPV && meanOrFluc; + if ((wantCent || wantPV) && (cfgRunGetEff || cfgRunGetMCFlat || cfgRunGetDataFlat)) { + LOGF(fatal, "The n-sigma pile-up bands come from the flattening pass: run it in a separate job before the mean/fluctuation passes."); + } + if (wantCent || wantPV) { + const std::string& bandPath = isMcRun ? pathMCFlat : pathDataFlat; + auto* bandList = ccdb->getForTimeStamp(bandPath, now); + if (!bandList) { + LOGF(fatal, "n-sigma pile-up bands requested but no flattening output at %s", bandPath.c_str()); + } + auto makeBand = [&](const char* name, NSigmaBand& band) { + auto* h2 = dynamic_cast(bandList->FindObject(name)); if (!h2) { + LOGF(fatal, "n-sigma pile-up band: %s missing in %s", name, bandPath.c_str()); return; } - std::unique_ptr prof(h2->ProfileX("ptmp", 1, -1, "S")); - int nBins = prof->GetNbinsX(); - xMin = prof->GetXaxis()->GetXmin(); - xMax = prof->GetXaxis()->GetXmax(); - limits.assign(nBins + 2, {-99999.f, 999999.f}); + std::unique_ptr prof(h2->ProfileX(Form("%s_band", name), 1, -1, "S")); + const int nBins = prof->GetNbinsX(); + band.xMin = prof->GetXaxis()->GetXmin(); + band.xMax = prof->GetXaxis()->GetXmax(); + band.meanRms.assign(nBins + 2, {0.f, -1.f}); for (int i = 1; i <= nBins; ++i) { - float mean = prof->GetBinContent(i); - float rms = prof->GetBinError(i); - limits[i] = {mean - cfgPupnSig * rms, mean + cfgPupnSig * rms}; + // a sparse bin (high-N_PV tail, peripheral edge) gets no band: its RMS is not measured + if (prof->GetBinEntries(i) >= KMinBandEntries) { + band.meanRms[i] = {static_cast(prof->GetBinContent(i)), static_cast(prof->GetBinError(i))}; + } } + band.applied = true; + LOGF(info, "n-sigma pile-up band (%.1f sigma, PileupNSigma %.1f) from %s in %s", pupNSigma, + pupNSigma - KSystPupnSigStep, name, bandPath.c_str()); }; - loadLimits("Hist2D_globalTracks_cent", state.mLimitsNchCent, state.mMinXNchCent, state.mMaxXNchCent); - } else { - LOGF(warning, "sigma-pileup limits source list missing at %s; sigma-pileup cut effectively disabled.", limPath.c_str()); - } - } - - // ---- MC mean profiles for MC fluc ---- - if (cfgRunMCFluc) { - LOGF(info, "Loading MC Mean profiles from CCDB path: %s", pathMCMean.c_str()); - auto* lstMCMean = ccdb->getForTimeStamp(pathMCMean, now); - if (lstMCMean) { - loadProfileFromList(lstMCMean, "pmeanFT0Amultpv", state.pmeanFT0AmultpvStep2); - loadProfileFromList(lstMCMean, "pmeanFT0Cmultpv", state.pmeanFT0CmultpvStep2); - loadProfileFromList(lstMCMean, "pmeanTru_nch_etabin", state.pmeanTruNchEtabinStep2); - loadProfileFromList(lstMCMean, "pmeanReco_nch_etabin", state.pmeanRecoNchEtabinStep2); - loadProfileFromList(lstMCMean, "pmeanRecoEffcorr_nch_etabin", state.pmeanRecoEffcorrNchEtabinStep2); - loadProfileFromList(lstMCMean, "pmeanMultTru_nch_etabin", state.pmeanMultTruNchEtabinStep2); - loadProfileFromList(lstMCMean, "pmeanMultReco_nch_etabin", state.pmeanMultRecoNchEtabinStep2); - loadProfileFromList(lstMCMean, "pmeanMultRecoEffcorr_nch_etabin", state.pmeanMultRecoEffcorrNchEtabinStep2); - // MC is pinned to 0.2-wide bins; guard against a stale MC-mean object built - // at a different width (its y-axis would then not have nEta+1 bins). - if (state.pmeanTruNchEtabinStep2 && - state.pmeanTruNchEtabinStep2->GetYaxis()->GetNbins() != nEta + 1) { - LOGF(fatal, - "MC Mean eta binning mismatch: loaded profile has %d y-bins, this job " - "expects nEta+1=%d. Rebuild the MC-mean pass at the current binning.", - state.pmeanTruNchEtabinStep2->GetYaxis()->GetNbins(), nEta + 1); + if (wantCent) { + makeBand("Hist2D_globalTracks_cent", pileupBands[kBandCent]); + } + if (wantPV) { + makeBand("Hist2D_globalTracks_PVTracks", pileupBands[kBandPV]); } - } else { - LOGF(error, "Could not retrieve TList for MC Mean from: %s", pathMCMean.c_str()); } } - // ---- Data mean profiles for data fluc (one per systematic variation) ---- - if (cfgRunDataFluc) { - int st = std::clamp(cfgSystType, 0, kNSystType - 1); - std::string meanPath = pathDataMean + systSuffix[st]; - LOGF(info, "Loading Data Mean profiles for systematic '%s' from: %s", - (st == kSystBase ? "Base" : systSuffix[st].c_str()), meanPath.c_str()); - auto* lstDataMean = ccdb->getForTimeStamp(meanPath, now); - if (lstDataMean) { - loadProfileFromList(lstDataMean, "pmeanFT0Amultpv", state.pmeanFT0AmultpvStep2); - loadProfileFromList(lstDataMean, "pmeanFT0Cmultpv", state.pmeanFT0CmultpvStep2); - loadProfileFromList(lstDataMean, "pmean_nch_etabin", state.pmeanNchEtabinStep2); - loadProfileFromList(lstDataMean, "pmeanMult_nch_etabin", state.pmeanMultNchEtabinStep2); - // The DataMean and DataFluc passes must share cfgEtaBinWidth, else the - // (ibx, ieta+1) read in processDataFluc maps a fluc bin index onto a - // different physical eta slice, silently. The mean profile y-axis has - // nEta+1 bins by construction (etaBinAxis). - if (state.pmeanNchEtabinStep2 && - state.pmeanNchEtabinStep2->GetYaxis()->GetNbins() != nEta + 1) { - LOGF(fatal, - "Data Mean eta binning mismatch: loaded profile has %d y-bins, this job " - "expects nEta+1=%d -> DataMean and DataFluc used different cfgEtaBinWidth.", - state.pmeanNchEtabinStep2->GetYaxis()->GetNbins(), nEta + 1); + // ---- mean profiles for the fluctuation passes (one per variation) ---- + if (cfgRunMCFluc || cfgRunDataFluc) { + const std::string& path = cfgRunMCFluc ? pathMCMean : pathDataMean; + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(maskAll, v)) { + LOGF(info, "Loading mean profiles of %s from %s", systName[v].c_str(), (path + systSuffix(v)).c_str()); + loadMeans(v, path + systSuffix(v), cfgRunMCFluc, now); } - } else { - LOGF(error, "Could not retrieve TList for Data Mean from: %s", meanPath.c_str()); } } - LOGF(info, "CCDB initialization complete for RadialFlowDecorr."); - } - // =========================================================================== - // MC: build efficiency / fake maps (inclusive) - // =========================================================================== - void processGetEffHists(MyRun3MCCollisions::iterator const& mcCollision, FilteredTCs const& mcTracks, aod::McParticles const& mcParticles) - { - histos.fill(HIST("hVtxZ"), mcCollision.posZ()); - if (!mcCollision.has_mcCollision() || !isEventSelected(mcCollision)) { - return; + // ---- declarations ---- + declareCommonQA(); + if (isMcRun) { + declareMCVtxQA(); } - float cent = getCentrality(mcCollision); - if (cent > KCentMax) { - return; + declareCounts(cfgRunGetEff ? maskEff : ((cfgRunGetMCFlat || cfgRunGetDataFlat) ? maskFlat : maskAll)); + if (doprocessCutQA) { + declareCutQA(); } - float multPV = mcCollision.multNTracksPV(); - float vz = mcCollision.posZ(); - if (!isPassAddPileup(multPV, mcTracks.size(), cent)) { - return; + if (cfgRunGetEff) { + declareMCGetEffHists(); } - - histos.fill(HIST("hVtxZ_after_sel"), mcCollision.posZ()); - histos.fill(HIST("hCentrality"), cent); - histos.fill(HIST("Hist2D_globalTracks_PVTracks"), multPV, mcTracks.size()); - histos.fill(HIST("Hist2D_cent_nch"), mcTracks.size(), cent); - histos.fill(HIST("Hist2D_globalTracks_cent"), cent, mcTracks.size()); - histos.fill(HIST("Hist2D_PVTracks_cent"), cent, multPV); - - for (const auto& particle : mcParticles) { - if (particle.mcCollisionId() != mcCollision.mcCollisionId()) { - continue; - } - if (!isParticleSelected(particle) || !particle.isPhysicalPrimary()) { - continue; - } - histos.fill(HIST("h3_AllPrimary"), multPV, particle.pt(), particle.eta()); + if (cfgRunGetMCFlat) { + declareMCGetFlatHists(); } - - for (const auto& track : mcTracks) { - if (track.collisionId() != mcCollision.index()) { - continue; - } - if (!isTrackSelected(track)) { - continue; - } - - float pt = track.pt(), eta = track.eta(); - histos.fill(HIST("hPt"), pt); - histos.fill(HIST("hEta"), eta); - histos.fill(HIST("h3_AllReco"), multPV, pt, eta); - - if (track.has_mcParticle()) { - auto mcP = track.mcParticle(); - if (mcP.isPhysicalPrimary()) { - histos.fill(HIST("ptResolution"), mcP.pt(), (pt - mcP.pt()) / mcP.pt()); - histos.fill(HIST("etaResolution"), mcP.eta(), eta - mcP.eta()); - histos.fill(HIST("etaTruthReco"), mcP.eta(), eta); - histos.fill(HIST("vzResolution"), mcP.vz(), (vz - mcP.vz()) / mcP.vz()); - histos.fill(HIST("TruthTracKVz"), mcP.vz(), vz); - histos.fill(HIST("h3_RecoMatchedToPrimary"), multPV, mcP.pt(), mcP.eta()); - } else { - histos.fill(HIST("h3_RecoUnMatchedToPrimary_Secondary"), multPV, pt, eta); - } - } else { - histos.fill(HIST("h3_RecoUnMatchedToPrimary_Fake"), multPV, pt, eta); - } + if (cfgRunMCMean) { + declareMCMeanHists(); } - } - PROCESS_SWITCH(RadialFlowDecorr, processGetEffHists, "process MC to calculate EffWeights", cfgRunGetEff); - - // =========================================================================== - // MC: build flattening maps (inclusive) - // =========================================================================== - void processMCFlat(MyRun3MCCollisions::iterator const& mcCollision, FilteredTCs const& mcTracks) - { - histos.fill(HIST("hVtxZ"), mcCollision.posZ()); - if (!mcCollision.has_mcCollision() || !isEventSelected(mcCollision)) { - return; + if (cfgRunMCFluc) { + declareMCFlucHists(); } - float cent = getCentrality(mcCollision); - if (cent > KCentMax) { - return; + if (cfgRunGetDataFlat) { + declareDataGetFlatHists(); } - float multPV = mcCollision.multNTracksPV(); - float vz = mcCollision.posZ(); - if (!isPassAddPileup(multPV, mcTracks.size(), cent)) { - return; + if (cfgRunDataMean) { + declareDataMeanHists(); + } + if (cfgRunDataFluc) { + declareDataFlucHists(); } - histos.fill(HIST("hVtxZ_after_sel"), mcCollision.posZ()); - histos.fill(HIST("hCentrality"), cent); - histos.fill(HIST("Hist2D_globalTracks_PVTracks"), multPV, mcTracks.size()); - histos.fill(HIST("Hist2D_cent_nch"), mcTracks.size(), cent); - histos.fill(HIST("Hist2D_globalTracks_cent"), cent, mcTracks.size()); - histos.fill(HIST("Hist2D_PVTracks_cent"), cent, multPV); - - for (const auto& track : mcTracks) { - if (track.collisionId() != mcCollision.index()) { - continue; - } - if (!isTrackSelected(track)) { - continue; - } - - float pt = track.pt(), eta = track.eta(), phi = track.phi(); - auto sign = track.sign(); - histos.fill(HIST("hPt"), pt); - histos.fill(HIST("hEta"), eta); - histos.fill(HIST("hPhi"), phi); - - float eff = 1.f, fake = 0.f; - if (!getValidEffFake(multPV, pt, eta, cfgEff, eff, fake)) { - continue; - } - float w = (1.0f - fake) / eff; - if (std::isfinite(w) && w > 0.f) { - histos.fill(HIST("MCReco/hEtaPhiRecoEffWtd"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("MCReco/hEtaPhiReco"), vz, sign, pt, eta, phi, 1.0); - histos.fill(HIST("MCReco/hEtaPhiRecoWtd"), vz, sign, pt, eta, phi, w); + std::string list; + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(used & maskAll, v) || (cfgRunGetEff && TESTBIT(maskEff, v)) || ((cfgRunGetMCFlat || cfgRunGetDataFlat) && TESTBIT(maskFlat, v))) { + list += " " + systName[v]; } } + LOGF(info, "RadialFlowDecorr variations in this job:%s (ITS layer cut nominal %d, variation %d; N_ch bins of %d, NchBin2 %d; occupancy cut %d)", + list.c_str(), itsLayerNom, itsLayerVar, nchWidth, nchWidthNchBin2, occupancyMax); + LOGF(info, "CCDB initialization complete for RadialFlowDecorr."); } - PROCESS_SWITCH(RadialFlowDecorr, processMCFlat, "process MC to calculate FlatWeights", cfgRunGetMCFlat); // =========================================================================== - // MC: mean pT (truth / reco / reco-eff-corrected) + // Helpers of the process functions // =========================================================================== - void processMCMean(MyRun3MCCollisions::iterator const& mcCollision, FilteredTCs const& mcTracks, aod::FT0s const&, aod::McParticles const& mcParticles) + // (vz, sign, pT, eta, phi) map of variation v: raw, with all weights w and, for Base, with + // the efficiency weight wEff = (1 - fake) / eff. + void fillFlat(int v, float vz, float sign, float pt, float eta, float phi, float w, float wEff) { - std::array sumWiTruth{}, sumWiptiTruth{}; - std::array sumWiReco{}, sumWiptiReco{}; - std::array sumWiRecoEffCorr{}, sumWiptiRecoEffCorr{}; - - histos.fill(HIST("hVtxZ"), mcCollision.posZ()); - if (!mcCollision.has_mcCollision() || !isEventSelected(mcCollision)) { - return; - } - float cent = getCentrality(mcCollision); - if (cent > KCentMax) { + const auto& h = hists.flat[v].maps; + if (!h) { return; } - float multPV = mcCollision.multNTracksPV(); - float vz = mcCollision.posZ(); - if (!isPassAddPileup(multPV, mcTracks.size(), cent)) { - return; + std::array x = {vz, sign, pt, eta, phi, static_cast(kFlatRaw)}; + h->Fill(x.data(), 1.); + x[5] = kFlatWtd; + h->Fill(x.data(), w); + if (v == kSystBase) { + x[5] = kFlatEffWtd; + h->Fill(x.data(), wEff); } + } - histos.fill(HIST("hVtxZ_after_sel"), mcCollision.posZ()); - histos.fill(HIST("hCentrality"), cent); - histos.fill(HIST("Hist2D_globalTracks_PVTracks"), multPV, mcTracks.size()); - histos.fill(HIST("Hist2D_cent_nch"), mcTracks.size(), cent); - histos.fill(HIST("Hist2D_globalTracks_cent"), cent, mcTracks.size()); - histos.fill(HIST("Hist2D_PVTracks_cent"), cent, multPV); - - // --- truth --- - for (const auto& particle : mcParticles) { - if (particle.mcCollisionId() != mcCollision.mcCollisionId()) { - continue; - } - if (!isParticleSelected(particle) || !particle.isPhysicalPrimary()) { - continue; - } - float pt = particle.pt(), eta = particle.eta(); - if (pt <= cfgPtMin || pt > cfgPtMax) { - continue; - } - for (int ieta = 0; ieta < nEta; ++ieta) { - if (eta <= etaLw[ieta] || eta > etaUp[ieta]) { - continue; - } - sumWiTruth[ieta]++; - sumWiptiTruth[ieta] += pt; + // The reco collision with the most PV contributors; with split vertices the others are + // skipped, so the truth of an MC collision enters once. Fills the vertex QA. + template + int64_t bestRecoCollision(const R& recoCollisions, float mcVz) + { + int64_t best = -1; + int maxContrib = -1; + int nReco = 0; + for (const auto& rc : recoCollisions) { + ++nReco; + if (rc.numContrib() > maxContrib) { + maxContrib = rc.numContrib(); + best = rc.globalIndex(); } } - - histos.fill(HIST("MCGen/Prof_Cent_Nchrec"), cent, sumWiTruth[0]); - histos.fill(HIST("MCGen/Prof_Mult_Nchrec"), multPV, sumWiTruth[0]); - if (sumWiTruth[0] > 1.0f) { - histos.fill(HIST("MCGen/Prof_Cent_MeanpT"), cent, sumWiptiTruth[0] / sumWiTruth[0]); - histos.fill(HIST("MCGen/Prof_Mult_MeanpT"), multPV, sumWiptiTruth[0] / sumWiTruth[0]); + for (const auto& rc : recoCollisions) { + const int type = (nReco == 1) ? kVtxSingle : (rc.globalIndex() == best ? kVtxSplitBest : kVtxSplitOther); + hists.mcVtx->Fill(rc.posZ() - mcVz, type); } + return best; + } - // --- reco --- - for (const auto& track : mcTracks) { - if (track.collisionId() != mcCollision.index()) { + // =========================================================================== + // MC: build efficiency / fake maps (inclusive) + // =========================================================================== + void processGetEffHists(aod::McCollisions::iterator const& mcCollision, McRecoCollisions const& recoCollisions, + FilteredTCs const& mcTracks, aod::McParticles const& mcParticles) + { + const int64_t best = bestRecoCollision(recoCollisions, mcCollision.posZ()); + for (const auto& coll : recoCollisions) { + if (coll.globalIndex() != best) { continue; } - if (!isTrackSelected(track)) { - continue; + const auto tracks = mcTracks.sliceBy(tracksPerCollision, coll.globalIndex()); + histos.fill(HIST("hVtxZ"), coll.posZ()); + const auto ev = isEventSelected(coll, tracks.size(), maskEff); + if (ev.mask == 0) { + return; } - float pt = track.pt(), eta = track.eta(), phi = track.phi(); - if (pt <= cfgPtMin || pt > cfgPtMax) { - continue; + const float nCh = tracks.size(); + const float multPV = coll.multNTracksPV(); + const float vz = coll.posZ(); + const float cent = ev.cent[kSystBase]; + + if (TESTBIT(ev.mask, kSystBase)) { + histos.fill(HIST("hVtxZ_after_sel"), vz); + histos.fill(HIST("hCentrality"), ev.cent[kSystBase]); + histos.fill(HIST("Hist2D_globalTracks_PVTracks"), multPV, nCh); + histos.fill(HIST("Hist2D_PVTracks_cent"), ev.cent[kSystBase], multPV); + hists.nchOccupancy->Fill(nCh, ev.occupancy); + } + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v) && hists.nchCent[v]) { + hists.nchCent[v]->Fill(ev.cent[v], nCh); + } } - auto sign = track.sign(); - histos.fill(HIST("hPt"), pt); - histos.fill(HIST("hEta"), eta); - histos.fill(HIST("hPhi"), phi); - float eff = 1.f, fake = 0.f; - if (!getValidEffFake(multPV, pt, eta, cfgEff, eff, fake)) { - continue; - } - float flatW = getFlatteningWeight(vz, sign, pt, eta, phi, cfgFlat); - float w = flatW * (1.0 - fake) / eff; - if (!std::isfinite(w) || w <= 0.f || eff <= KFloatEpsilon) { - continue; - } + // one entry in the efficiency map of category cat of every variation in mask + auto fillEffMaps = [&](uint32_t mask, float pt, float eta, int cat) { + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(mask, v)) { + hists.eff[v].maps[cat]->Fill(cent, pt, eta); + } + } + }; - for (int ieta = 0; ieta < nEta; ++ieta) { - if (eta <= etaLw[ieta] || eta > etaUp[ieta]) { + // --- truth: charged primaries of the MC collision --- + for (const auto& particle : mcParticles) { + if (!isParticleSelected(particle) || !particle.isPhysicalPrimary()) { continue; } - sumWiReco[ieta]++; - sumWiptiReco[ieta] += pt; - sumWiRecoEffCorr[ieta] += w; - sumWiptiRecoEffCorr[ieta] += w * pt; + fillEffMaps(ev.mask, particle.pt(), particle.eta(), kEffAllPrim); } - histos.fill(HIST("Eff_cent"), cent, eff); - histos.fill(HIST("Fake_cent"), cent, fake); - histos.fill(HIST("wgt_cent"), cent, w); - histos.fill(HIST("Eff_Ntrk"), multPV, eff); - histos.fill(HIST("Fake_Ntrk"), multPV, fake); - histos.fill(HIST("wgt_Ntrk"), multPV, w); - histos.fill(HIST("Eff_pT"), pt, eff); - histos.fill(HIST("Fake_pT"), pt, fake); - histos.fill(HIST("wgt_pT"), pt, w); - histos.fill(HIST("Eff_eta"), eta, eff); - histos.fill(HIST("Fake_eta"), eta, fake); - histos.fill(HIST("wgt_eta"), eta, w); - - histos.fill(HIST("hEtaPhiReco"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoWtd"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("hEtaPhiRecoEffWtd"), vz, sign, pt, eta, phi, (1.0 - fake) / eff); - } - - // subevent mean-pT maps - for (int ietaA = 0; ietaA < nEta; ++ietaA) { - for (int ietaC = 0; ietaC < nEta; ++ietaC) { - float nTruAB = sumWiTruth[ietaA] + sumWiTruth[ietaC]; - float nRecoAB = sumWiReco[ietaA] + sumWiReco[ietaC]; - float nCorrAB = sumWiRecoEffCorr[ietaA] + sumWiRecoEffCorr[ietaC]; - - if (nTruAB > 0) { - histos.fill(HIST("Prof2D_MeanpTSub_Tru"), cent, ietaA, ietaC, (sumWiptiTruth[ietaA] + sumWiptiTruth[ietaC]) / nTruAB); + // --- reco --- + for (const auto& track : tracks) { + const uint32_t trackMask = isTrackSelected(track, ev.mask); + if (trackMask == 0) { + continue; } - if (nRecoAB > 0) { - histos.fill(HIST("Prof2D_MeanpTSub_Reco"), cent, ietaA, ietaC, (sumWiptiReco[ietaA] + sumWiptiReco[ietaC]) / nRecoAB); + float pt = track.pt(), eta = track.eta(); + const bool isBase = TESTBIT(trackMask, kSystBase); + const int origin = trackOrigin(track, coll); + if (isBase) { + histos.fill(HIST("hPt"), pt); + histos.fill(HIST("hEta"), eta); + hists.mcTrackOrigin->Fill(nCh, pt, origin); } - if (nCorrAB > 0) { - histos.fill(HIST("Prof2D_MeanpTSub_RecoEffCorr"), cent, ietaA, ietaC, (sumWiptiRecoEffCorr[ietaA] + sumWiptiRecoEffCorr[ietaC]) / nCorrAB); + fillEffMaps(trackMask, pt, eta, kEffAllReco); + if (origin == kOriginPrimary) { + const auto& mcP = track.mcParticle(); + if (isBase) { + histos.fill(HIST("ptResolution"), mcP.pt(), (pt - mcP.pt()) / mcP.pt()); + histos.fill(HIST("etaResolution"), mcP.eta(), eta - mcP.eta()); + histos.fill(HIST("etaTruthReco"), mcP.eta(), eta); + histos.fill(HIST("vzResolution"), mcP.vz(), (vz - mcP.vz()) / mcP.vz()); + histos.fill(HIST("TruthTracKVz"), mcP.vz(), vz); + } + fillEffMaps(trackMask, mcP.pt(), mcP.eta(), kEffRecoMatchedPrim); + } else if (origin == kOriginSecondary) { + fillEffMaps(trackMask, pt, eta, kEffRecoSec); + } else if (origin == kOriginOtherColl) { + fillEffMaps(trackMask, pt, eta, kEffRecoOtherColl); + } else { + fillEffMaps(trackMask, pt, eta, kEffRecoFake); } } - - if (sumWiTruth[ietaA] > 0) { - histos.fill(HIST("pmeanTru_nch_etabin"), multPV, ietaA, sumWiptiTruth[ietaA] / sumWiTruth[ietaA]); - histos.fill(HIST("pmeanMultTru_nch_etabin"), multPV, ietaA, sumWiTruth[ietaA]); - } - if (sumWiReco[ietaA] > 0) { - histos.fill(HIST("pmeanReco_nch_etabin"), multPV, ietaA, sumWiptiReco[ietaA] / sumWiReco[ietaA]); - histos.fill(HIST("pmeanMultReco_nch_etabin"), multPV, ietaA, sumWiReco[ietaA]); - } - if (sumWiRecoEffCorr[ietaA] > 0) { - histos.fill(HIST("pmeanRecoEffcorr_nch_etabin"), multPV, ietaA, sumWiptiRecoEffCorr[ietaA] / sumWiRecoEffCorr[ietaA]); - histos.fill(HIST("pmeanMultRecoEffcorr_nch_etabin"), multPV, ietaA, sumWiRecoEffCorr[ietaA]); - } - } - - // FT0 - double amplFT0A = 0, amplFT0C = 0; - if (mcCollision.has_foundFT0()) { - const auto& ft0 = mcCollision.foundFT0(); - for (std::size_t iCh = 0; iCh < ft0.channelA().size(); iCh++) { - auto chanelid = ft0.channelA()[iCh]; - float ampl = ft0.amplitudeA()[iCh]; - amplFT0A += ampl; - auto eta = getEtaFT0(chanelid, 0); - histos.fill(HIST("pmean_cent_id_eta_FT0"), cent, chanelid, eta, ampl); - histos.fill(HIST("h3_cent_id_eta_FT0"), cent, chanelid, eta, ampl); - } - for (std::size_t iCh = 0; iCh < ft0.channelC().size(); iCh++) { - auto chanelid = ft0.channelC()[iCh]; - auto globalId = chanelid + KnFt0cCell; - float ampl = ft0.amplitudeC()[iCh]; - auto eta = getEtaFT0(globalId, 1); - amplFT0C += ampl; - histos.fill(HIST("pmean_cent_id_eta_FT0"), cent, globalId, eta, ampl); - histos.fill(HIST("h3_cent_id_eta_FT0"), cent, globalId, eta, ampl); - } + flushTrackCounts(); } - histos.fill(HIST("pmeanFT0Amultpv"), multPV, amplFT0A); - histos.fill(HIST("pmeanFT0A_cent"), cent, amplFT0A); - histos.fill(HIST("pmeanFT0Cmultpv"), multPV, amplFT0C); - histos.fill(HIST("pmeanFT0C_cent"), cent, amplFT0C); } - PROCESS_SWITCH(RadialFlowDecorr, processMCMean, "process MC to calculate mean pt", cfgRunMCMean); + PROCESS_SWITCH(RadialFlowDecorr, processGetEffHists, "process MC to calculate EffWeights", cfgRunGetEff); // =========================================================================== - // MC: fluctuations (C2, subevent) at three levels + // MC: build flattening maps (inclusive) // =========================================================================== - void processMCFluc(MyRun3MCCollisions::iterator const& mcCollision, FilteredTCs const& mcTracks, aod::FT0s const&, aod::McParticles const& mcParticles) + void processMCFlat(aod::McCollisions::iterator const& mcCollision, McRecoCollisions const& recoCollisions, FilteredTCs const& mcTracks) { - if (!state.pmeanTruNchEtabinStep2 || !state.pmeanRecoNchEtabinStep2 || !state.pmeanRecoEffcorrNchEtabinStep2 || - !state.pmeanMultTruNchEtabinStep2 || !state.pmeanMultRecoNchEtabinStep2 || !state.pmeanMultRecoEffcorrNchEtabinStep2) { - LOGF(warning, "MC fluc: mean pT or mult map missing"); - return; - } - - std::array, KIntM>, KNEtaMax> sumPmwkTru{}; - std::array, KNEtaMax> sumWkTru{}; - std::array, KIntM>, KNEtaMax> sumPmwkReco{}; - std::array, KNEtaMax> sumWkReco{}; - std::array, KIntM>, KNEtaMax> sumPmwkRecoEffCor{}; - std::array, KNEtaMax> sumWkRecoEffCor{}; - - std::array meanTru{}, c2Tru{}, c3Tru{}; - std::array meanReco{}, c2Reco{}, c3Reco{}; - std::array meanRecoEffCor{}, c2RecoEffCor{}, c3RecoEffCor{}; - - std::array meanTruMult{}, meanRecoMult{}, meanRecoEffCorMult{}; - std::array p1kBarTru{}, p1kBarReco{}, p1kBarRecoEffCor{}; - std::array p1kBarTruMult{}, p1kBarRecoMult{}, p1kBarRecoEffCorMult{}; - - if (!mcCollision.has_mcCollision() || !isEventSelected(mcCollision)) { - return; - } - float cent = getCentrality(mcCollision); - if (cent > KCentMax) { - return; - } - float multPV = mcCollision.multNTracksPV(); - float vz = mcCollision.posZ(); - if (!isPassAddPileup(multPV, mcTracks.size(), cent)) { - return; - } - - histos.fill(HIST("hVtxZ_after_sel"), mcCollision.posZ()); - histos.fill(HIST("hCentrality"), cent); - histos.fill(HIST("Hist2D_globalTracks_PVTracks"), multPV, mcTracks.size()); - histos.fill(HIST("Hist2D_cent_nch"), mcTracks.size(), cent); - histos.fill(HIST("Hist2D_globalTracks_cent"), cent, mcTracks.size()); - histos.fill(HIST("Hist2D_PVTracks_cent"), cent, multPV); - - double p1kBarFt0A = 0.0, p1kBarFt0C = 0.0; - - // --- truth sums --- - for (const auto& particle : mcParticles) { - if (particle.mcCollisionId() != mcCollision.mcCollisionId()) { - continue; - } - if (!isParticleSelected(particle) || !particle.isPhysicalPrimary()) { - continue; - } - float pt = particle.pt(); - if (pt <= cfgPtMin || pt > cfgPtMax) { - continue; - } - float eta = particle.eta(); - for (int ieta = 0; ieta < nEta; ++ieta) { - if (eta <= etaLw[ieta] || eta > etaUp[ieta]) { - continue; - } - for (int k = 0; k < KIntK; ++k) { - for (int m = 0; m < KIntM; ++m) { - sumPmwkTru[ieta][m][k] += std::pow(pt, m); - } - sumWkTru[ieta][k]++; - } - } - } - - // --- reco sums --- - for (const auto& track : mcTracks) { - if (track.collisionId() != mcCollision.index()) { + const int64_t best = bestRecoCollision(recoCollisions, mcCollision.posZ()); + for (const auto& coll : recoCollisions) { + if (coll.globalIndex() != best) { continue; } - if (!isTrackSelected(track)) { - continue; - } - float pt = track.pt(); - if (pt <= cfgPtMin || pt > cfgPtMax) { - continue; + const auto tracks = mcTracks.sliceBy(tracksPerCollision, coll.globalIndex()); + histos.fill(HIST("hVtxZ"), coll.posZ()); + const auto ev = isEventSelected(coll, tracks.size(), maskFlat); + if (ev.mask == 0) { + return; } - float eta = track.eta(), phi = track.phi(); - auto sign = track.sign(); - histos.fill(HIST("hPt"), pt); - histos.fill(HIST("hEta"), eta); - histos.fill(HIST("hPhi"), phi); + const float nCh = tracks.size(); + const float multPV = coll.multNTracksPV(); + const float vz = coll.posZ(); - float eff = 1.f, fake = 0.f; - if (!getValidEffFake(multPV, pt, eta, cfgEff, eff, fake)) { - continue; + if (TESTBIT(ev.mask, kSystBase)) { + histos.fill(HIST("hVtxZ_after_sel"), vz); + histos.fill(HIST("hCentrality"), ev.cent[kSystBase]); + histos.fill(HIST("Hist2D_globalTracks_PVTracks"), multPV, nCh); + histos.fill(HIST("Hist2D_PVTracks_cent"), ev.cent[kSystBase], multPV); + hists.nchOccupancy->Fill(nCh, ev.occupancy); } - float flatW = getFlatteningWeight(vz, sign, pt, eta, phi, cfgFlat); - float w = flatW * (1.0 - fake) / eff; - if (!std::isfinite(w) || w <= 0.f || eff <= KFloatEpsilon) { - continue; + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v) && hists.nchCent[v]) { + hists.nchCent[v]->Fill(ev.cent[v], nCh); + } } - for (int ieta = 0; ieta < nEta; ++ieta) { - if (eta <= etaLw[ieta] || eta > etaUp[ieta]) { + std::array w{}; + for (const auto& track : tracks) { + const uint32_t trackMask = isTrackSelected(track, ev.mask); + if (trackMask == 0) { continue; } - for (int k = 0; k < KIntK; ++k) { - for (int m = 0; m < KIntM; ++m) { - sumPmwkReco[ieta][m][k] += std::pow(1.0, k) * std::pow(pt, m); - sumPmwkRecoEffCor[ieta][m][k] += std::pow(w, k) * std::pow(pt, m); - } - sumWkReco[ieta][k] += std::pow(1.0, k); - sumWkRecoEffCor[ieta][k] += std::pow(w, k); + float pt = track.pt(); + if (pt <= cfgPtMin || pt > cfgPtMax) { + continue; + } + float eta = track.eta(), phi = track.phi(); + float sign = track.sign(); + if (TESTBIT(trackMask, kSystBase)) { + histos.fill(HIST("hPt"), pt); + histos.fill(HIST("hEta"), eta); + histos.fill(HIST("hPhi"), phi); } - } - - histos.fill(HIST("hEtaPhiReco"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoWtd"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("hEtaPhiRecoEffWtd"), vz, sign, pt, eta, phi, (1.0 - fake) / eff); - } - - for (int ieta = 0; ieta < nEta; ++ieta) { - const int ibx = state.pmeanTruNchEtabinStep2->GetXaxis()->FindBin(multPV); - const int iby = ieta + 1; - - meanTruMult[ieta] = sumWkTru[ieta][1]; - meanRecoMult[ieta] = sumWkReco[ieta][1]; - meanRecoEffCorMult[ieta] = sumWkRecoEffCor[ieta][1]; - - float mmptTru = state.pmeanTruNchEtabinStep2->GetBinContent(ibx, iby); - float mmptReco = state.pmeanRecoNchEtabinStep2->GetBinContent(ibx, iby); - float mmptRecoEffCor = state.pmeanRecoEffcorrNchEtabinStep2->GetBinContent(ibx, iby); - - float mmMultTru = state.pmeanMultTruNchEtabinStep2->GetBinContent(ibx, iby); - float mmMultReco = state.pmeanMultRecoNchEtabinStep2->GetBinContent(ibx, iby); - float mmMultRecoEffCor = state.pmeanMultRecoEffcorrNchEtabinStep2->GetBinContent(ibx, iby); - - // covariance requires both bins populated: gate each multiplicity deviation on - // the same >=1-track condition as the mean, so cov skips unless both bins qualify - p1kBarTruMult[ieta] = (sumWkTru[ieta][1] >= 1.0) ? (meanTruMult[ieta] - mmMultTru) : std::numeric_limits::quiet_NaN(); - p1kBarRecoMult[ieta] = (sumWkReco[ieta][1] >= 1.0) ? (meanRecoMult[ieta] - mmMultReco) : std::numeric_limits::quiet_NaN(); - p1kBarRecoEffCorMult[ieta] = (sumWkRecoEffCor[ieta][1] >= 1.0) ? (meanRecoEffCorMult[ieta] - mmMultRecoEffCor) : std::numeric_limits::quiet_NaN(); - - // truth - meanTru[ieta] = (sumWkTru[ieta][1] >= 1.0) ? (sumPmwkTru[ieta][1][1] / sumWkTru[ieta][1]) : std::numeric_limits::quiet_NaN(); - c2Tru[ieta] = std::numeric_limits::quiet_NaN(); - c3Tru[ieta] = std::numeric_limits::quiet_NaN(); - p1kBarTru[ieta] = std::numeric_limits::quiet_NaN(); - if (std::isfinite(mmptTru) && mmptTru != 0) { - const auto corr = calculateC2C3FromSums(sumPmwkTru[ieta], sumWkTru[ieta], mmptTru); - c2Tru[ieta] = corr.c2; - c3Tru[ieta] = corr.c3; - if (std::isfinite(meanTru[ieta])) { - p1kBarTru[ieta] = meanTru[ieta] - mmptTru; - } - } - // reco - meanReco[ieta] = (sumWkReco[ieta][1] >= 1.0) ? (sumPmwkReco[ieta][1][1] / sumWkReco[ieta][1]) : std::numeric_limits::quiet_NaN(); - c2Reco[ieta] = std::numeric_limits::quiet_NaN(); - c3Reco[ieta] = std::numeric_limits::quiet_NaN(); - p1kBarReco[ieta] = std::numeric_limits::quiet_NaN(); - if (std::isfinite(mmptReco) && mmptReco != 0) { - const auto corr = calculateC2C3FromSums(sumPmwkReco[ieta], sumWkReco[ieta], mmptReco); - c2Reco[ieta] = corr.c2; - c3Reco[ieta] = corr.c3; - if (std::isfinite(meanReco[ieta])) { - p1kBarReco[ieta] = meanReco[ieta] - mmptReco; - } - } - // reco, efficiency-corrected - meanRecoEffCor[ieta] = (sumWkRecoEffCor[ieta][1] >= 1.0) ? (sumPmwkRecoEffCor[ieta][1][1] / sumWkRecoEffCor[ieta][1]) : std::numeric_limits::quiet_NaN(); - c2RecoEffCor[ieta] = std::numeric_limits::quiet_NaN(); - c3RecoEffCor[ieta] = std::numeric_limits::quiet_NaN(); - p1kBarRecoEffCor[ieta] = std::numeric_limits::quiet_NaN(); - if (std::isfinite(mmptRecoEffCor) && mmptRecoEffCor != 0) { - const auto corr = calculateC2C3FromSums(sumPmwkRecoEffCor[ieta], sumWkRecoEffCor[ieta], mmptRecoEffCor); - c2RecoEffCor[ieta] = corr.c2; - c3RecoEffCor[ieta] = corr.c3; - if (std::isfinite(meanRecoEffCor[ieta])) { - p1kBarRecoEffCor[ieta] = meanRecoEffCor[ieta] - mmptRecoEffCor; - } - } - } - - double amplFT0A = 0, amplFT0C = 0; - if (mcCollision.has_foundFT0()) { - const auto& ft0 = mcCollision.foundFT0(); - for (std::size_t iCh = 0; iCh < ft0.channelA().size(); iCh++) { - amplFT0A += ft0.amplitudeA()[iCh]; - } - for (std::size_t iCh = 0; iCh < ft0.channelC().size(); iCh++) { - amplFT0C += ft0.amplitudeC()[iCh]; - } - } - p1kBarFt0A = amplFT0A - state.pmeanFT0AmultpvStep2->GetBinContent(state.pmeanFT0AmultpvStep2->GetXaxis()->FindBin(multPV)); - p1kBarFt0C = amplFT0C - state.pmeanFT0CmultpvStep2->GetBinContent(state.pmeanFT0CmultpvStep2->GetXaxis()->FindBin(multPV)); - - // per-eta counts & means - for (int ieta = 0; ieta < nEta; ++ieta) { - histos.fill(HIST("MCGen/Prof_Cent_NEta_Nchrec"), cent, ieta, sumWkTru[ieta][1]); - histos.fill(HIST("MCGen/Prof_Mult_NEta_Nchrec"), multPV, ieta, sumWkTru[ieta][1]); - histos.fill(HIST("MCReco/Prof_Cent_NEta_Nchrec"), cent, ieta, sumWkReco[ieta][1]); - histos.fill(HIST("MCReco/Prof_Mult_NEta_Nchrec"), multPV, ieta, sumWkReco[ieta][1]); - histos.fill(HIST("MCRecoEffCorr/Prof_Cent_NEta_Nchrec"), cent, ieta, sumWkRecoEffCor[ieta][1]); - histos.fill(HIST("MCRecoEffCorr/Prof_Mult_NEta_Nchrec"), multPV, ieta, sumWkRecoEffCor[ieta][1]); - if (sumWkTru[ieta][1] > 1.0f) { - histos.fill(HIST("MCGen/Prof_Cent_NEta_MeanpT"), cent, ieta, meanTru[ieta]); - histos.fill(HIST("MCGen/Prof_Mult_NEta_MeanpT"), multPV, ieta, meanTru[ieta]); - } - if (sumWkReco[ieta][1] > 1.0f) { - histos.fill(HIST("MCReco/Prof_Cent_NEta_MeanpT"), cent, ieta, meanReco[ieta]); - histos.fill(HIST("MCReco/Prof_Mult_NEta_MeanpT"), multPV, ieta, meanReco[ieta]); - } - if (sumWkRecoEffCor[ieta][1] > 1.0f) { - histos.fill(HIST("MCRecoEffCorr/Prof_Cent_NEta_MeanpT"), cent, ieta, meanRecoEffCor[ieta]); - histos.fill(HIST("MCRecoEffCorr/Prof_Mult_NEta_MeanpT"), multPV, ieta, meanRecoEffCor[ieta]); + // efficiency weights only: the flattening map is what this pass measures + float eff = 1.f, fake = 0.f, flatWeight = 1.f; + const uint32_t weightMask = getTrackWeights(trackMask, ev.cent[kSystBase], vz, sign, pt, eta, phi, false, w, eff, fake, flatWeight); + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(weightMask, v)) { + fillFlat(v, vz, sign, pt, eta, phi, w[v], w[v]); + } + } } + flushTrackCounts(); } + } + PROCESS_SWITCH(RadialFlowDecorr, processMCFlat, "process MC to calculate FlatWeights", cfgRunGetMCFlat); - // meanpT & C2 vs eta bin - for (int ieta = 0; ieta < nEta; ++ieta) { - if (std::isfinite(meanTru[ieta])) { - histos.fill(HIST("MCGen/Prof_MeanpT_Cent_etabin"), cent, ieta, meanTru[ieta]); - histos.fill(HIST("MCGen/Prof_MeanpT_Mult_etabin"), multPV, ieta, meanTru[ieta]); - } - if (std::isfinite(c2Tru[ieta])) { - histos.fill(HIST("MCGen/Prof_C2_Cent_etabin"), cent, ieta, c2Tru[ieta]); - histos.fill(HIST("MCGen/Prof_C2_Mult_etabin"), multPV, ieta, c2Tru[ieta]); + // =========================================================================== + // MC: mean pT (truth / reco / reco-eff-corrected) + // =========================================================================== + void processMCMean(aod::McCollisions::iterator const& mcCollision, McRecoCollisions const& recoCollisions, + aod::BCsWithTimestamps const&, aod::FT0s const&, aod::FV0As const&, + FilteredTCs const& mcTracks, aod::McParticles const& mcParticles) + { + const int64_t best = bestRecoCollision(recoCollisions, mcCollision.posZ()); + for (const auto& coll : recoCollisions) { + if (coll.globalIndex() != best) { + continue; } - if (std::isfinite(c3Tru[ieta])) { - histos.fill(HIST("MCGen/Prof_C3_Cent_etabin"), cent, ieta, c3Tru[ieta]); - histos.fill(HIST("MCGen/Prof_C3_Mult_etabin"), multPV, ieta, c3Tru[ieta]); + const auto tracks = mcTracks.sliceBy(tracksPerCollision, coll.globalIndex()); + histos.fill(HIST("hVtxZ"), coll.posZ()); + const auto ev = isEventSelected(coll, tracks.size(), maskAll); + if (ev.mask == 0) { + return; } - if (std::isfinite(meanReco[ieta])) { - histos.fill(HIST("MCReco/Prof_MeanpT_Cent_etabin"), cent, ieta, meanReco[ieta]); - histos.fill(HIST("MCReco/Prof_MeanpT_Mult_etabin"), multPV, ieta, meanReco[ieta]); + const float nCh = tracks.size(); + const float multPV = coll.multNTracksPV(); + const float vz = coll.posZ(); + + if (TESTBIT(ev.mask, kSystBase)) { + histos.fill(HIST("hVtxZ_after_sel"), vz); + histos.fill(HIST("hCentrality"), ev.cent[kSystBase]); + histos.fill(HIST("Hist2D_globalTracks_PVTracks"), multPV, nCh); + histos.fill(HIST("Hist2D_PVTracks_cent"), ev.cent[kSystBase], multPV); + hists.nchOccupancy->Fill(nCh, ev.occupancy); } - if (std::isfinite(c2Reco[ieta])) { - histos.fill(HIST("MCReco/Prof_C2_Cent_etabin"), cent, ieta, c2Reco[ieta]); - histos.fill(HIST("MCReco/Prof_C2_Mult_etabin"), multPV, ieta, c2Reco[ieta]); + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v) && hists.nchCent[v]) { + hists.nchCent[v]->Fill(ev.cent[v], nCh); + } } - if (std::isfinite(c3Reco[ieta])) { - histos.fill(HIST("MCReco/Prof_C3_Cent_etabin"), cent, ieta, c3Reco[ieta]); - histos.fill(HIST("MCReco/Prof_C3_Mult_etabin"), multPV, ieta, c3Reco[ieta]); + + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v)) { + for (auto& s : sums[v]) { + s.reset(); + } + } } - if (std::isfinite(meanRecoEffCor[ieta])) { - histos.fill(HIST("MCRecoEffCorr/Prof_MeanpT_Cent_etabin"), cent, ieta, meanRecoEffCor[ieta]); - histos.fill(HIST("MCRecoEffCorr/Prof_MeanpT_Mult_etabin"), multPV, ieta, meanRecoEffCor[ieta]); + + // --- truth: independent of the track cuts, copied to every selected variation --- + PowerSums& sumsTru = sums[kSystBase][kGen]; + if (!TESTBIT(ev.mask, kSystBase)) { + sumsTru.reset(); } - if (std::isfinite(c2RecoEffCor[ieta])) { - histos.fill(HIST("MCRecoEffCorr/Prof_C2_Cent_etabin"), cent, ieta, c2RecoEffCor[ieta]); - histos.fill(HIST("MCRecoEffCorr/Prof_C2_Mult_etabin"), multPV, ieta, c2RecoEffCor[ieta]); + for (const auto& particle : mcParticles) { + if (!isParticleSelected(particle) || !particle.isPhysicalPrimary()) { + continue; + } + float pt = particle.pt(), eta = particle.eta(); + if (pt <= cfgPtMin || pt > cfgPtMax) { + continue; + } + for (int ieta = 0; ieta < nEta; ++ieta) { + if (eta <= etaLw[ieta] || eta > etaUp[ieta]) { + continue; + } + sumsTru.add(ieta, pt, 1.); + } } - if (std::isfinite(c3RecoEffCor[ieta])) { - histos.fill(HIST("MCRecoEffCorr/Prof_C3_Cent_etabin"), cent, ieta, c3RecoEffCor[ieta]); - histos.fill(HIST("MCRecoEffCorr/Prof_C3_Mult_etabin"), multPV, ieta, c3RecoEffCor[ieta]); + for (int v = 1; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v)) { + sums[v][kGen] = sumsTru; + } } - } - - // mirror-pair subevent (C2Sub) & covariances vs eta bin - for (int ietaA = 1; ietaA <= (nEta - 1) / 2; ++ietaA) { - int ietaC = nEta - ietaA; - float c2SubTru = p1kBarTru[ietaA] * p1kBarTru[ietaC]; - float c2SubReco = p1kBarReco[ietaA] * p1kBarReco[ietaC]; - float c2SubRecoEffCor = p1kBarRecoEffCor[ietaA] * p1kBarRecoEffCor[ietaC]; + // --- reco --- + std::array w{}; + for (const auto& track : tracks) { + const uint32_t trackMask = isTrackSelected(track, ev.mask); + if (trackMask == 0) { + continue; + } + float pt = track.pt(); + if (pt <= cfgPtMin || pt > cfgPtMax) { + continue; + } + float eta = track.eta(), phi = track.phi(); + float sign = track.sign(); + if (TESTBIT(trackMask, kSystBase)) { + histos.fill(HIST("hPt"), pt); + histos.fill(HIST("hEta"), eta); + histos.fill(HIST("hPhi"), phi); + } - float c3SubTruA = c2Tru[ietaA] * p1kBarTru[ietaC]; // 2 from A, 1 from C - float c3SubTruC = c2Tru[ietaC] * p1kBarTru[ietaA]; // 2 from C, 1 from A - float c3SubRecoA = c2Reco[ietaA] * p1kBarReco[ietaC]; - float c3SubRecoC = c2Reco[ietaC] * p1kBarReco[ietaA]; - float c3SubRecoEffCorA = c2RecoEffCor[ietaA] * p1kBarRecoEffCor[ietaC]; - float c3SubRecoEffCorC = c2RecoEffCor[ietaC] * p1kBarRecoEffCor[ietaA]; + // reco level: every selected track, independent of the corrections + for (int ieta = 0; ieta < nEta; ++ieta) { + if (eta <= etaLw[ieta] || eta > etaUp[ieta]) { + continue; + } + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(trackMask, v)) { + sums[v][kReco].add(ieta, pt, 1.); + } + } + } - float covTru = p1kBarTruMult[ietaA] * p1kBarTru[ietaC]; - float covReco = p1kBarRecoMult[ietaA] * p1kBarReco[ietaC]; - float covRecoEffCor = p1kBarRecoEffCorMult[ietaA] * p1kBarRecoEffCor[ietaC]; + float eff = 1.f, fake = 0.f, flatWeight = 1.f; + const uint32_t weightMask = getTrackWeights(trackMask, ev.cent[kSystBase], vz, sign, pt, eta, phi, true, w, eff, fake, flatWeight); + if (weightMask == 0) { + continue; + } + if (TESTBIT(weightMask, kSystBase)) { + histos.fill(HIST("Prof_Weights_pT_type"), pt, 0, eff); + histos.fill(HIST("Prof_Weights_pT_type"), pt, 1, fake); + histos.fill(HIST("Prof_Weights_pT_type"), pt, 2, w[kSystBase]); + fillFlat(kSystBase, vz, sign, pt, eta, phi, w[kSystBase], (1.f - fake) / eff); + } - if (std::isfinite(c2SubTru)) { - histos.fill(HIST("MCGen/Prof_C2Sub_Cent_etabin"), cent, ietaA, c2SubTru); - histos.fill(HIST("MCGen/Prof_C2Sub_Mult_etabin"), multPV, ietaA, c2SubTru); - } - if (std::isfinite(c2SubReco)) { - histos.fill(HIST("MCReco/Prof_C2Sub_Cent_etabin"), cent, ietaA, c2SubReco); - histos.fill(HIST("MCReco/Prof_C2Sub_Mult_etabin"), multPV, ietaA, c2SubReco); - } - if (std::isfinite(c2SubRecoEffCor)) { - histos.fill(HIST("MCRecoEffCorr/Prof_C2Sub_Cent_etabin"), cent, ietaA, c2SubRecoEffCor); - histos.fill(HIST("MCRecoEffCorr/Prof_C2Sub_Mult_etabin"), multPV, ietaA, c2SubRecoEffCor); - } - if (std::isfinite(c3SubTruA)) { - histos.fill(HIST("MCGen/Prof_C3Sub_Cent_etabin"), cent, ietaA, c3SubTruA); - histos.fill(HIST("MCGen/Prof_C3Sub_Mult_etabin"), multPV, ietaA, c3SubTruA); - } - if (std::isfinite(c3SubTruC)) { - histos.fill(HIST("MCGen/Prof_C3Sub_Cent_etabin"), cent, ietaC, c3SubTruC); - histos.fill(HIST("MCGen/Prof_C3Sub_Mult_etabin"), multPV, ietaC, c3SubTruC); - } - if (std::isfinite(c3SubRecoA)) { - histos.fill(HIST("MCReco/Prof_C3Sub_Cent_etabin"), cent, ietaA, c3SubRecoA); - histos.fill(HIST("MCReco/Prof_C3Sub_Mult_etabin"), multPV, ietaA, c3SubRecoA); - } - if (std::isfinite(c3SubRecoC)) { - histos.fill(HIST("MCReco/Prof_C3Sub_Cent_etabin"), cent, ietaC, c3SubRecoC); - histos.fill(HIST("MCReco/Prof_C3Sub_Mult_etabin"), multPV, ietaC, c3SubRecoC); - } - if (std::isfinite(c3SubRecoEffCorA)) { - histos.fill(HIST("MCRecoEffCorr/Prof_C3Sub_Cent_etabin"), cent, ietaA, c3SubRecoEffCorA); - histos.fill(HIST("MCRecoEffCorr/Prof_C3Sub_Mult_etabin"), multPV, ietaA, c3SubRecoEffCorA); - } - if (std::isfinite(c3SubRecoEffCorC)) { - histos.fill(HIST("MCRecoEffCorr/Prof_C3Sub_Cent_etabin"), cent, ietaC, c3SubRecoEffCorC); - histos.fill(HIST("MCRecoEffCorr/Prof_C3Sub_Mult_etabin"), multPV, ietaC, c3SubRecoEffCorC); - } - if (std::isfinite(covTru)) { - histos.fill(HIST("MCGen/Prof_Cov_Cent_etabin"), cent, ietaA, covTru); - histos.fill(HIST("MCGen/Prof_Cov_Mult_etabin"), multPV, ietaA, covTru); + // reco, efficiency-corrected + for (int ieta = 0; ieta < nEta; ++ieta) { + if (eta <= etaLw[ieta] || eta > etaUp[ieta]) { + continue; + } + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(weightMask, v)) { + sums[v][kRecoEffCorr].add(ieta, pt, w[v]); + } + } + } } - if (std::isfinite(covReco)) { - histos.fill(HIST("MCReco/Prof_Cov_Cent_etabin"), cent, ietaA, covReco); - histos.fill(HIST("MCReco/Prof_Cov_Mult_etabin"), multPV, ietaA, covReco); + flushTrackCounts(); + + // --- forward detectors: charged primaries (truth) and amplitudes (reco) --- + FwdSignal fwdTru, fwdReco; + if (cfgFwdOpt.cfgFwd) { + loadFwdCalib(coll.template bc_as(), false); + computeFwdTruth(mcParticles, fwdTru); + computeFwdReco(coll, vz, fwdReco, TESTBIT(ev.mask, kSystBase)); } - if (std::isfinite(covRecoEffCor)) { - histos.fill(HIST("MCRecoEffCorr/Prof_Cov_Cent_etabin"), cent, ietaA, covRecoEffCor); - histos.fill(HIST("MCRecoEffCorr/Prof_Cov_Mult_etabin"), multPV, ietaA, covRecoEffCor); + + // --- <[pT]> and per eta bin, per forward bin --- + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(ev.mask, v)) { + continue; + } + for (int l = 0; l < kNLevels; ++l) { + const auto& s = sums[v][l]; + const auto& h = hists.mean[v][l]; + for (int ieta = 0; ieta < nEta; ++ieta) { + const double n = s.wk[ieta][1]; + h.mean->Fill(nCh, ieta, kMeanN, n); + if (n <= KFloatEpsilon) { + continue; + } + const double mpt = s.pmwk[ieta][1][1] / n; + if (std::isfinite(mpt)) { + h.mean->Fill(nCh, ieta, kMeanPt, mpt); + } + } + const FwdSignal& fwd = (l == kGen) ? fwdTru : fwdReco; + if (cfgFwdOpt.cfgFwd && h.fwdMean) { + for (int k = 0; k < nFwd; ++k) { + if (fwd.valid[k]) { + h.fwdMean->Fill(nCh, k, fwd.ampl[k]); + } + } + } + } } } + } + PROCESS_SWITCH(RadialFlowDecorr, processMCMean, "process MC to calculate mean pt", cfgRunMCMean); - // FT0 covariance vs narrow eta bin (full range, indexed by the actual pT bin) - for (int ieta = 1; ieta < nEta; ++ieta) { - float covFT0ATru = p1kBarFt0A * p1kBarTru[ieta]; - float covFT0AReco = p1kBarFt0A * p1kBarReco[ieta]; - float covFT0ARecoEffCor = p1kBarFt0A * p1kBarRecoEffCor[ieta]; - float covFT0CTru = p1kBarFt0C * p1kBarTru[ieta]; - float covFT0CReco = p1kBarFt0C * p1kBarReco[ieta]; - float covFT0CRecoEffCor = p1kBarFt0C * p1kBarRecoEffCor[ieta]; - - if (std::isfinite(covFT0ATru)) { - histos.fill(HIST("MCGen/Prof_CovFT0A_Cent_etabin"), cent, ieta, covFT0ATru); - histos.fill(HIST("MCGen/Prof_CovFT0A_Mult_etabin"), multPV, ieta, covFT0ATru); - } - if (std::isfinite(covFT0AReco)) { - histos.fill(HIST("MCReco/Prof_CovFT0A_Cent_etabin"), cent, ieta, covFT0AReco); - histos.fill(HIST("MCReco/Prof_CovFT0A_Mult_etabin"), multPV, ieta, covFT0AReco); - } - if (std::isfinite(covFT0ARecoEffCor)) { - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0A_Cent_etabin"), cent, ieta, covFT0ARecoEffCor); - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0A_Mult_etabin"), multPV, ieta, covFT0ARecoEffCor); + // =========================================================================== + // MC: fluctuations (C2, subevent, forward covariances) at three levels + // =========================================================================== + void processMCFluc(aod::McCollisions::iterator const& mcCollision, McRecoCollisions const& recoCollisions, + aod::BCsWithTimestamps const&, aod::FT0s const&, aod::FV0As const&, + FilteredTCs const& mcTracks, aod::McParticles const& mcParticles) + { + const int64_t best = bestRecoCollision(recoCollisions, mcCollision.posZ()); + for (const auto& coll : recoCollisions) { + if (coll.globalIndex() != best) { + continue; } - if (std::isfinite(covFT0CTru)) { - histos.fill(HIST("MCGen/Prof_CovFT0C_Cent_etabin"), cent, ieta, covFT0CTru); - histos.fill(HIST("MCGen/Prof_CovFT0C_Mult_etabin"), multPV, ieta, covFT0CTru); + const auto tracks = mcTracks.sliceBy(tracksPerCollision, coll.globalIndex()); + histos.fill(HIST("hVtxZ"), coll.posZ()); + const auto ev = isEventSelected(coll, tracks.size(), maskAll); + if (ev.mask == 0) { + return; } - if (std::isfinite(covFT0CReco)) { - histos.fill(HIST("MCReco/Prof_CovFT0C_Cent_etabin"), cent, ieta, covFT0CReco); - histos.fill(HIST("MCReco/Prof_CovFT0C_Mult_etabin"), multPV, ieta, covFT0CReco); + const float nCh = tracks.size(); + const float multPV = coll.multNTracksPV(); + const float vz = coll.posZ(); + + if (TESTBIT(ev.mask, kSystBase)) { + histos.fill(HIST("hVtxZ_after_sel"), vz); + histos.fill(HIST("hCentrality"), ev.cent[kSystBase]); + histos.fill(HIST("Hist2D_globalTracks_PVTracks"), multPV, nCh); + histos.fill(HIST("Hist2D_PVTracks_cent"), ev.cent[kSystBase], multPV); + hists.nchOccupancy->Fill(nCh, ev.occupancy); } - if (std::isfinite(covFT0CRecoEffCor)) { - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0C_Cent_etabin"), cent, ieta, covFT0CRecoEffCor); - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0C_Mult_etabin"), multPV, ieta, covFT0CRecoEffCor); + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v) && hists.nchCent[v]) { + hists.nchCent[v]->Fill(ev.cent[v], nCh); + } } - } - - // full 2D subevent map - for (int ietaA = 1; ietaA < nEta; ++ietaA) { - for (int ietaC = 1; ietaC < nEta; ++ietaC) { - float etaValA = (etaLw[ietaA] + etaUp[ietaA]) / 2.0f; - float etaValB = (etaLw[ietaC] + etaUp[ietaC]) / 2.0f; - float gap = etaValA - etaValB; - float sum = (etaValA + etaValB); - - float c2SubTru = (ietaA == ietaC) ? static_cast(c2Tru[ietaA]) : p1kBarTru[ietaA] * p1kBarTru[ietaC]; - float c2SubReco = (ietaA == ietaC) ? static_cast(c2Reco[ietaA]) : p1kBarReco[ietaA] * p1kBarReco[ietaC]; - float c2SubRecoEffCor = (ietaA == ietaC) ? static_cast(c2RecoEffCor[ietaA]) : p1kBarRecoEffCor[ietaA] * p1kBarRecoEffCor[ietaC]; - - float c3Sub2DTru = (ietaA == ietaC) ? static_cast(c3Tru[ietaA]) : c2Tru[ietaA] * p1kBarTru[ietaC]; // diag: within-bin c3; off-diag: 2 from A, 1 from C - float c3Sub2DReco = (ietaA == ietaC) ? static_cast(c3Reco[ietaA]) : c2Reco[ietaA] * p1kBarReco[ietaC]; - float c3Sub2DRecoEffCor = (ietaA == ietaC) ? static_cast(c3RecoEffCor[ietaA]) : c2RecoEffCor[ietaA] * p1kBarRecoEffCor[ietaC]; - float covTru = p1kBarTruMult[ietaA] * p1kBarTru[ietaC]; - float covReco = p1kBarRecoMult[ietaA] * p1kBarReco[ietaC]; - float covRecoEffCor = p1kBarRecoEffCorMult[ietaA] * p1kBarRecoEffCor[ietaC]; - - float covFT0ATru = p1kBarFt0A * p1kBarTru[ietaC]; - float covFT0AReco = p1kBarFt0A * p1kBarReco[ietaC]; - float covFT0ARecoEffCor = p1kBarFt0A * p1kBarRecoEffCor[ietaC]; - - float covFT0CTru = p1kBarFt0C * p1kBarTru[ietaA]; - float covFT0CReco = p1kBarFt0C * p1kBarReco[ietaA]; - float covFT0CRecoEffCor = p1kBarFt0C * p1kBarRecoEffCor[ietaA]; - - if (std::isfinite(c2SubTru)) { - histos.fill(HIST("MCGen/Prof_C2Sub2D_Cent_etaA_etaC"), cent, etaValA, etaValB, c2SubTru); - histos.fill(HIST("MCGen/Prof_GapSum2D"), cent, gap, sum, c2SubTru); - } - if (std::isfinite(c2SubReco)) { - histos.fill(HIST("MCReco/Prof_C2Sub2D_Cent_etaA_etaC"), cent, etaValA, etaValB, c2SubReco); - histos.fill(HIST("MCReco/Prof_GapSum2D"), cent, gap, sum, c2SubReco); - } - if (std::isfinite(c2SubRecoEffCor)) { - histos.fill(HIST("MCRecoEffCorr/Prof_C2Sub2D_Cent_etaA_etaC"), cent, etaValA, etaValB, c2SubRecoEffCor); - histos.fill(HIST("MCRecoEffCorr/Prof_GapSum2D"), cent, gap, sum, c2SubRecoEffCor); + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v)) { + for (auto& s : sums[v]) { + s.reset(); + } } + } - if (std::isfinite(c3Sub2DTru)) { - histos.fill(HIST("MCGen/Prof_C3Sub2D_Cent_etaA_etaC"), cent, etaValA, etaValB, c3Sub2DTru); - histos.fill(HIST("MCGen/Prof_C3GapSum2D"), cent, gap, sum, c3Sub2DTru); + // --- truth sums: independent of the track cuts, copied to every selected variation --- + PowerSums& sumsTru = sums[kSystBase][kGen]; + if (!TESTBIT(ev.mask, kSystBase)) { + sumsTru.reset(); + } + for (const auto& particle : mcParticles) { + if (!isParticleSelected(particle) || !particle.isPhysicalPrimary()) { + continue; + } + float pt = particle.pt(), eta = particle.eta(); + if (pt <= cfgPtMin || pt > cfgPtMax) { + continue; } - if (std::isfinite(c3Sub2DReco)) { - histos.fill(HIST("MCReco/Prof_C3Sub2D_Cent_etaA_etaC"), cent, etaValA, etaValB, c3Sub2DReco); - histos.fill(HIST("MCReco/Prof_C3GapSum2D"), cent, gap, sum, c3Sub2DReco); + for (int ieta = 0; ieta < nEta; ++ieta) { + if (eta <= etaLw[ieta] || eta > etaUp[ieta]) { + continue; + } + sumsTru.add(ieta, pt, 1.); } - if (std::isfinite(c3Sub2DRecoEffCor)) { - histos.fill(HIST("MCRecoEffCorr/Prof_C3Sub2D_Cent_etaA_etaC"), cent, etaValA, etaValB, c3Sub2DRecoEffCor); - histos.fill(HIST("MCRecoEffCorr/Prof_C3GapSum2D"), cent, gap, sum, c3Sub2DRecoEffCor); + } + for (int v = 1; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v)) { + sums[v][kGen] = sumsTru; } + } - if (std::isfinite(covTru)) { - histos.fill(HIST("MCGen/Prof_Cov2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covTru); + // --- reco sums --- + std::array w{}; + for (const auto& track : tracks) { + const uint32_t trackMask = isTrackSelected(track, ev.mask); + if (trackMask == 0) { + continue; } - if (std::isfinite(covReco)) { - histos.fill(HIST("MCReco/Prof_Cov2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covReco); + float pt = track.pt(); + if (pt <= cfgPtMin || pt > cfgPtMax) { + continue; } - if (std::isfinite(covRecoEffCor)) { - histos.fill(HIST("MCRecoEffCorr/Prof_Cov2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covRecoEffCor); + float eta = track.eta(), phi = track.phi(); + float sign = track.sign(); + if (TESTBIT(trackMask, kSystBase)) { + histos.fill(HIST("hPt"), pt); + histos.fill(HIST("hEta"), eta); + histos.fill(HIST("hPhi"), phi); } - if (std::isfinite(covFT0ATru)) { - histos.fill(HIST("MCGen/Prof_CovFT0A2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covFT0ATru); + // reco level: every selected track, independent of the corrections + for (int ieta = 0; ieta < nEta; ++ieta) { + if (eta <= etaLw[ieta] || eta > etaUp[ieta]) { + continue; + } + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(trackMask, v)) { + sums[v][kReco].add(ieta, pt, 1.); + } + } } - if (std::isfinite(covFT0AReco)) { - histos.fill(HIST("MCReco/Prof_CovFT0A2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covFT0AReco); + + float eff = 1.f, fake = 0.f, flatWeight = 1.f; + const uint32_t weightMask = getTrackWeights(trackMask, ev.cent[kSystBase], vz, sign, pt, eta, phi, true, w, eff, fake, flatWeight); + if (weightMask == 0) { + continue; } - if (std::isfinite(covFT0ARecoEffCor)) { - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0A2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covFT0ARecoEffCor); + if (TESTBIT(weightMask, kSystBase)) { + fillFlat(kSystBase, vz, sign, pt, eta, phi, w[kSystBase], (1.f - fake) / eff); } - if (std::isfinite(covFT0CTru)) { - histos.fill(HIST("MCGen/Prof_CovFT0C2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covFT0CTru); + // reco, efficiency-corrected + for (int ieta = 0; ieta < nEta; ++ieta) { + if (eta <= etaLw[ieta] || eta > etaUp[ieta]) { + continue; + } + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(weightMask, v)) { + sums[v][kRecoEffCorr].add(ieta, pt, w[v]); + } + } } - if (std::isfinite(covFT0CReco)) { - histos.fill(HIST("MCReco/Prof_CovFT0C2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covFT0CReco); + } + flushTrackCounts(); + + // --- forward detectors: charged primaries (truth) and amplitudes (reco) --- + FwdSignal fwdTru, fwdReco; + if (cfgFwdOpt.cfgFwd) { + loadFwdCalib(coll.template bc_as(), false); + computeFwdTruth(mcParticles, fwdTru); + computeFwdReco(coll, vz, fwdReco, TESTBIT(ev.mask, kSystBase)); + } + + // --- c2, c3, subevent correlators and forward covariances of every variation and level --- + EventObs evObs; + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(ev.mask, v)) { + continue; } - if (std::isfinite(covFT0CRecoEffCor)) { - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0C2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covFT0CRecoEffCor); + for (int l = 0; l < kNLevels; ++l) { + const FwdSignal* fwd = cfgFwdOpt.cfgFwd ? (l == kGen ? &fwdTru : &fwdReco) : nullptr; + computeEventObs(sums[v][l], v, l, fwd, nCh, evObs); + fillFluc(hists.fluc[v][l], evObs, 1., nCh); } } } @@ -2179,32 +2923,34 @@ struct RadialFlowDecorr { // =========================================================================== // DATA: build flattening maps (inclusive) // =========================================================================== - void processGetDataFlat(AodCollisionsSel::iterator const& coll, BCsRun3 const& /*bcs*/, aod::Zdcs const& /*zdcsData*/, AodTracksSel const& tracks) + void processGetDataFlat(AodCollisionsSel::iterator const& coll, AodTracksSel const& tracks) { histos.fill(HIST("hVtxZ"), coll.posZ()); - if (!isEventSelected(coll)) { + const auto ev = isEventSelected(coll, tracks.size(), maskFlat); + if (ev.mask == 0) { return; } - float cent = getCentrality(coll); - if (cent > KCentMax) { - return; + const float nCh = tracks.size(); + const float multPV = coll.multNTracksPV(); + const float vz = coll.posZ(); + + if (TESTBIT(ev.mask, kSystBase)) { + histos.fill(HIST("hVtxZ_after_sel"), vz); + histos.fill(HIST("hCentrality"), ev.cent[kSystBase]); + histos.fill(HIST("Hist2D_globalTracks_PVTracks"), multPV, nCh); + histos.fill(HIST("Hist2D_PVTracks_cent"), ev.cent[kSystBase], multPV); + hists.nchOccupancy->Fill(nCh, ev.occupancy); } - if (!isPassAddPileup(coll.multNTracksPV(), tracks.size(), cent)) { - return; + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v) && hists.nchCent[v]) { + hists.nchCent[v]->Fill(ev.cent[v], nCh); + } } - histos.fill(HIST("hVtxZ_after_sel"), coll.posZ()); - histos.fill(HIST("hCentrality"), cent); - histos.fill(HIST("Hist2D_globalTracks_PVTracks"), coll.multNTracksPV(), tracks.size()); - histos.fill(HIST("Hist2D_cent_nch"), tracks.size(), cent); - histos.fill(HIST("Hist2D_globalTracks_cent"), cent, tracks.size()); - histos.fill(HIST("Hist2D_PVTracks_cent"), cent, coll.multNTracksPV()); - - int ntrk = 0; - float vz = coll.posZ(); - + std::array w{}; for (const auto& track : tracks) { - if (!isTrackSelected(track)) { + const uint32_t trackMask = isTrackSelected(track, ev.mask); + if (trackMask == 0) { continue; } float pt = track.pt(); @@ -2212,467 +2958,281 @@ struct RadialFlowDecorr { continue; } float eta = track.eta(), phi = track.phi(); - auto sign = track.sign(); - - histos.fill(HIST("hPt"), pt); - histos.fill(HIST("hEta"), eta); - histos.fill(HIST("hPhi"), phi); - if (eta > etaLw[0] && eta < etaUp[0]) { - ntrk++; - } - - float eff = 1.f, fake = 0.f; - if (!getValidEffFake(coll.multNTracksPV(), pt, eta, cfgEff, eff, fake)) { - continue; - } - float w = (1.0f - fake) / eff; - if (!std::isfinite(w) || w <= 0.f) { - continue; - } - - histos.fill(HIST("hEtaPhiReco"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoEffWtd"), vz, sign, pt, eta, phi, (1.0f - fake) / eff); - histos.fill(HIST("hEtaPhiRecoWtd"), vz, sign, pt, eta, phi, w); - } - - if (cfgZDC) { - const auto& foundBC = coll.foundBC_as(); - if (!foundBC.has_zdc()) { - return; + float sign = track.sign(); + if (TESTBIT(trackMask, kSystBase)) { + histos.fill(HIST("hPt"), pt); + histos.fill(HIST("hEta"), eta); + histos.fill(HIST("hPhi"), phi); + } + + // efficiency weights only: the flattening map is what this pass measures + float eff = 1.f, fake = 0.f, flatWeight = 1.f; + const uint32_t weightMask = getTrackWeights(trackMask, ev.cent[kSystBase], vz, sign, pt, eta, phi, false, w, eff, fake, flatWeight); + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(weightMask, v)) { + fillFlat(v, vz, sign, pt, eta, phi, w[v], w[v]); + } } - auto zdc = foundBC.zdc(); - auto zdcAmp = zdc.energyCommonZNA() + zdc.energyCommonZNC(); - histos.fill(HIST("hnTrkPVZDC"), coll.multNTracksPV(), zdcAmp); - histos.fill(HIST("hNchZDC"), ntrk, zdcAmp); } + flushTrackCounts(); } PROCESS_SWITCH(RadialFlowDecorr, processGetDataFlat, "process data to calculate Flattening maps", cfgRunGetDataFlat); // =========================================================================== // DATA: mean pT // =========================================================================== - void processDataMean(AodCollisionsSel::iterator const& coll, BCsRun3 const& /*bcs*/, aod::Zdcs const& /*zdcsData*/, aod::FT0s const&, AodTracksSel const& tracks) + void processDataMean(AodCollisionsSel::iterator const& coll, aod::BCsWithTimestamps const&, aod::FT0s const&, aod::FV0As const&, AodTracksSel const& tracks) { - std::array sumWi{}, sumWipti{}; - - if (!isEventSelected(coll)) { + const auto ev = isEventSelected(coll, tracks.size(), maskAll); + if (ev.mask == 0) { return; } - float cent = getCentrality(coll); - if (cent > KCentMax) { - return; + const float nCh = tracks.size(); + const float multPV = coll.multNTracksPV(); + const float vz = coll.posZ(); + + if (TESTBIT(ev.mask, kSystBase)) { + histos.fill(HIST("hVtxZ_after_sel"), vz); + histos.fill(HIST("hCentrality"), ev.cent[kSystBase]); + histos.fill(HIST("Hist2D_globalTracks_PVTracks"), multPV, nCh); + histos.fill(HIST("Hist2D_PVTracks_cent"), ev.cent[kSystBase], multPV); + hists.nchOccupancy->Fill(nCh, ev.occupancy); } - if (!isPassAddPileup(coll.multNTracksPV(), tracks.size(), cent)) { - return; + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v) && hists.nchCent[v]) { + hists.nchCent[v]->Fill(ev.cent[v], nCh); + } } - histos.fill(HIST("hVtxZ_after_sel"), coll.posZ()); - histos.fill(HIST("hCentrality"), cent); - histos.fill(HIST("Hist2D_globalTracks_PVTracks"), coll.multNTracksPV(), tracks.size()); - histos.fill(HIST("Hist2D_cent_nch"), tracks.size(), cent); - histos.fill(HIST("Hist2D_globalTracks_cent"), cent, tracks.size()); - histos.fill(HIST("Hist2D_PVTracks_cent"), cent, coll.multNTracksPV()); - - float vz = coll.posZ(); - + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v)) { + sums[v][0].reset(); + } + } + std::array w{}; for (const auto& track : tracks) { - if (!isTrackSelected(track)) { + const uint32_t trackMask = isTrackSelected(track, ev.mask); + if (trackMask == 0) { continue; } float p = track.p(); float pt = track.pt(); - float eta = track.eta(), phi = track.phi(); - auto sign = track.sign(); if (p < KFloatEpsilon) { continue; } if (pt <= cfgPtMin || pt > cfgPtMax) { continue; } - - histos.fill(HIST("hP"), p); - histos.fill(HIST("hPt"), pt); - histos.fill(HIST("hEta"), eta); - histos.fill(HIST("hPhi"), phi); - - float eff = 1.f, fake = 0.f; - if (!getValidEffFake(coll.multNTracksPV(), pt, eta, cfgEff, eff, fake)) { - continue; + float eta = track.eta(), phi = track.phi(); + float sign = track.sign(); + if (TESTBIT(trackMask, kSystBase)) { + histos.fill(HIST("hP"), p); + histos.fill(HIST("hPt"), pt); + histos.fill(HIST("hEta"), eta); + histos.fill(HIST("hPhi"), phi); } - float flatWeight = getFlatteningWeight(vz, sign, pt, eta, phi, cfgFlat); - - histos.fill(HIST("pEffWeight_pt_eta_cent"), pt, eta, cent, eff); - histos.fill(HIST("pFakeWeight_pt_eta_cent"), pt, eta, cent, fake); - histos.fill(HIST("pFlatWeight_pt_eta_cent"), pt, eta, cent, flatWeight); - if (eff <= KFloatEpsilon) { + float eff = 1.f, fake = 0.f, flatWeight = 1.f; + const uint32_t weightMask = getTrackWeights(trackMask, ev.cent[kSystBase], vz, sign, pt, eta, phi, true, w, eff, fake, flatWeight); + if (weightMask == 0) { continue; } - - float w = flatWeight * (1.0f - fake) / eff; - if (!std::isfinite(w) || w <= 0.f) { - continue; + if (TESTBIT(weightMask, kSystBase)) { + histos.fill(HIST("Prof_Weights_pt_eta_type"), pt, eta, 0, eff); + histos.fill(HIST("Prof_Weights_pt_eta_type"), pt, eta, 1, fake); + histos.fill(HIST("Prof_Weights_pt_eta_type"), pt, eta, 2, flatWeight); + fillFlat(kSystBase, vz, sign, pt, eta, phi, w[kSystBase], (1.f - fake) / eff); } - histos.fill(HIST("hEtaPhiReco"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoEffWtd"), vz, sign, pt, eta, phi, (1.0f - fake) / eff); - histos.fill(HIST("hEtaPhiRecoWtd"), vz, sign, pt, eta, phi, w); - for (int ieta = 0; ieta < nEta; ++ieta) { if (eta <= etaLw[ieta] || eta > etaUp[ieta]) { continue; } - sumWi[ieta] += w; - sumWipti[ieta] += w * pt; + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(weightMask, v)) { + sums[v][0].add(ieta, pt, w[v]); + } + } } } + flushTrackCounts(); - if (sumWi[0] >= 1.0f) { - histos.fill(HIST("Prof_Cent_Nchrec"), cent, sumWi[0]); - histos.fill(HIST("Prof_Mult_Nchrec"), coll.multNTracksPV(), sumWi[0]); - histos.fill(HIST("Prof_Cent_MeanpT"), cent, sumWipti[0] / sumWi[0]); - histos.fill(HIST("Prof_Mult_MeanpT"), coll.multNTracksPV(), sumWipti[0] / sumWi[0]); + // --- forward detectors: gain-equalised amplitudes --- + FwdSignal fwd; + if (cfgFwdOpt.cfgFwd) { + loadFwdCalib(coll.template bc_as(), true); + computeFwdReco(coll, vz, fwd, TESTBIT(ev.mask, kSystBase)); } - for (int ietaA = 0; ietaA < nEta; ++ietaA) { - for (int ietaC = 0; ietaC < nEta; ++ietaC) { - if ((sumWi[ietaA] < 1.0f) || (sumWi[ietaC] < 1.0f)) { + // --- <[pT]> and per eta bin, per forward bin --- + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(ev.mask, v)) { + continue; + } + const auto& s = sums[v][0]; + const auto& h = hists.mean[v][0]; + for (int ieta = 0; ieta < nEta; ++ieta) { + const double n = s.wk[ieta][1]; + h.mean->Fill(nCh, ieta, kMeanN, n); + if (n <= KFloatEpsilon) { continue; } - double wCorrAB = sumWi[ietaA] + sumWi[ietaC]; - if (wCorrAB > 0) { - float mptsub = (sumWipti[ietaA] + sumWipti[ietaC]) / wCorrAB; - histos.fill(HIST("Prof2D_MeanpTSub"), cent, ietaA, ietaC, mptsub); - } - } - if (sumWi[ietaA] >= 1.0f) { - double mpt = sumWipti[ietaA] / sumWi[ietaA]; + const double mpt = s.pmwk[ieta][1][1] / n; if (std::isfinite(mpt)) { - histos.fill(HIST("pmean_nch_etabin"), coll.multNTracksPV(), ietaA, mpt); - histos.fill(HIST("pmeanMult_nch_etabin"), coll.multNTracksPV(), ietaA, sumWi[ietaA]); - histos.fill(HIST("pmean_cent_etabin"), cent, ietaA, mpt); - histos.fill(HIST("pmeanMult_cent_etabin"), cent, ietaA, sumWi[ietaA]); + h.mean->Fill(nCh, ieta, kMeanPt, mpt); } } - } - - double amplFT0A = 0, amplFT0C = 0; - if (coll.has_foundFT0()) { - const auto& ft0 = coll.foundFT0(); - for (std::size_t iCh = 0; iCh < ft0.channelA().size(); iCh++) { - auto chanelid = ft0.channelA()[iCh]; - float ampl = ft0.amplitudeA()[iCh]; - amplFT0A += ampl; - auto eta = getEtaFT0(chanelid, 0); - histos.fill(HIST("pmean_cent_id_eta_FT0"), cent, chanelid, eta, ampl); - histos.fill(HIST("h3_cent_id_eta_FT0"), cent, chanelid, eta, ampl); - } - for (std::size_t iCh = 0; iCh < ft0.channelC().size(); iCh++) { - auto chanelid = ft0.channelC()[iCh]; - auto globalId = chanelid + KnFt0cCell; - float ampl = ft0.amplitudeC()[iCh]; - amplFT0C += ampl; - auto eta = getEtaFT0(globalId, 1); - histos.fill(HIST("pmean_cent_id_eta_FT0"), cent, globalId, eta, ampl); - histos.fill(HIST("h3_cent_id_eta_FT0"), cent, globalId, eta, ampl); + if (cfgFwdOpt.cfgFwd && h.fwdMean) { + for (int k = 0; k < nFwd; ++k) { + if (fwd.valid[k]) { + h.fwdMean->Fill(nCh, k, fwd.ampl[k]); + } + } } } - histos.fill(HIST("pmeanFT0Amultpv"), coll.multNTracksPV(), amplFT0A); - histos.fill(HIST("pmeanFT0A_cent"), cent, amplFT0A); - histos.fill(HIST("pmeanFT0Cmultpv"), coll.multNTracksPV(), amplFT0C); - histos.fill(HIST("pmeanFT0C_cent"), cent, amplFT0C); } PROCESS_SWITCH(RadialFlowDecorr, processDataMean, "process data to calculate mean pT", cfgRunDataMean); // =========================================================================== - // DATA: fluctuations (C2, subevent) + Poisson bootstrap for the base run + // DATA: fluctuations (C2, subevent, forward covariances) + Poisson bootstrap for Base // =========================================================================== - void processDataFluc(AodCollisionsSel::iterator const& coll, BCsRun3 const& /*bcs*/, aod::Zdcs const& /*zdcsData*/, aod::FT0s const&, AodTracksSel const& tracks) + void processDataFluc(AodCollisionsSel::iterator const& coll, aod::BCsWithTimestamps const&, aod::FT0s const&, aod::FV0As const&, AodTracksSel const& tracks) { - if (!isEventSelected(coll)) { - return; - } - float cent = getCentrality(coll); - if (cent > KCentMax) { - return; - } - if (!isPassAddPileup(coll.multNTracksPV(), tracks.size(), cent)) { + const auto ev = isEventSelected(coll, tracks.size(), maskAll); + if (ev.mask == 0) { return; } + const float nCh = tracks.size(); + const float multPV = coll.multNTracksPV(); + const float vz = coll.posZ(); - histos.fill(HIST("hVtxZ_after_sel"), coll.posZ()); - histos.fill(HIST("hCentrality"), cent); - histos.fill(HIST("Hist2D_globalTracks_PVTracks"), coll.multNTracksPV(), tracks.size()); - histos.fill(HIST("Hist2D_cent_nch"), tracks.size(), cent); - histos.fill(HIST("Hist2D_globalTracks_cent"), cent, tracks.size()); - histos.fill(HIST("Hist2D_PVTracks_cent"), cent, coll.multNTracksPV()); - - if (!state.pmeanNchEtabinStep2 || !state.pmeanMultNchEtabinStep2) { - LOGF(warning, "Data fluc: mean pT or mult map missing"); - return; - } - if (cfgEff && (!state.hEff || !state.hFake)) { - LOGF(warning, "Data fluc: Efficiency maps requested but not present."); - return; + if (TESTBIT(ev.mask, kSystBase)) { + histos.fill(HIST("hVtxZ_after_sel"), vz); + histos.fill(HIST("hCentrality"), ev.cent[kSystBase]); + histos.fill(HIST("Hist2D_globalTracks_PVTracks"), multPV, nCh); + histos.fill(HIST("Hist2D_PVTracks_cent"), ev.cent[kSystBase], multPV); + hists.nchOccupancy->Fill(nCh, ev.occupancy); } - - if (cfgFlat && !state.hFlatWeight) { - LOGF(warning, "Data fluc: Flattening map requested but not present."); - return; + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v) && hists.nchCent[v]) { + hists.nchCent[v]->Fill(ev.cent[v], nCh); + } } - std::array, KIntM>, KNEtaMax> sumpmwk{}; - std::array, KNEtaMax> sumwk{}; - std::array mean{}, c2{}, c3{}, p1kBar{}; - std::array meanMult{}, p1kBarMult{}; - - // --- Poisson bootstrap: one weight per sample per event --- - std::array poisW{}; - if (doBoot) { - for (int s = 0; s < nBoot; ++s) { - poisW[s] = rng.Poisson(1.0); + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v)) { + sums[v][0].reset(); } } - auto fillBS1D = [&](std::array, KMaxBoot>& arr, double x, double val) { - if (!doBoot) { - return; - } - for (int s = 0; s < nBoot; ++s) { - arr[s]->Fill(x, val, poisW[s]); - } - }; - auto fillBS2D = [&](std::array, KMaxBoot>& arr, double x, double y, double val) { - if (!doBoot) { - return; - } - for (int s = 0; s < nBoot; ++s) { - arr[s]->Fill(x, y, val, poisW[s]); - } - }; - auto fillBS3D = [&](std::array, KMaxBoot>& arr, double x, double y, double z, double val) { - if (!doBoot) { - return; - } - for (int s = 0; s < nBoot; ++s) { - arr[s]->Fill(x, y, z, val, poisW[s]); - } - }; - - float vz = coll.posZ(); - + std::array w{}; for (const auto& track : tracks) { - if (!isTrackSelected(track)) { + const uint32_t trackMask = isTrackSelected(track, ev.mask); + if (trackMask == 0) { continue; } float p = track.p(); float pt = track.pt(); - float eta = track.eta(), phi = track.phi(); - auto sign = track.sign(); if (p < KFloatEpsilon) { continue; } if (pt <= cfgPtMin || pt > cfgPtMax) { continue; } + float eta = track.eta(), phi = track.phi(); + float sign = track.sign(); + if (TESTBIT(trackMask, kSystBase)) { + histos.fill(HIST("hP"), p); + histos.fill(HIST("hPt"), pt); + histos.fill(HIST("hEta"), eta); + histos.fill(HIST("hPhi"), phi); + } - float eff = 1.f, fake = 0.f; - if (!getValidEffFake(coll.multNTracksPV(), pt, eta, cfgEff, eff, fake)) { + float eff = 1.f, fake = 0.f, flatWeight = 1.f; + const uint32_t weightMask = getTrackWeights(trackMask, ev.cent[kSystBase], vz, sign, pt, eta, phi, true, w, eff, fake, flatWeight); + if (weightMask == 0) { continue; } - float flatWeight = getFlatteningWeight(vz, sign, pt, eta, phi, cfgFlat); - float w = flatWeight * (1.0f - fake) / eff; - if (!std::isfinite(w) || w <= 0.f) { - continue; + if (TESTBIT(weightMask, kSystBase)) { + fillFlat(kSystBase, vz, sign, pt, eta, phi, w[kSystBase], (1.f - fake) / eff); } for (int ieta = 0; ieta < nEta; ++ieta) { if (eta <= etaLw[ieta] || eta > etaUp[ieta]) { continue; } - for (int k = 0; k < KIntK; ++k) { - for (int m = 0; m < KIntM; ++m) { - sumpmwk[ieta][m][k] += std::pow(w, k) * std::pow(pt, m); + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(weightMask, v)) { + sums[v][0].add(ieta, pt, w[v]); } - sumwk[ieta][k] += std::pow(w, k); } } } + flushTrackCounts(); - double amplFT0A = 0, amplFT0C = 0; - if (coll.has_foundFT0()) { - const auto& ft0 = coll.foundFT0(); - for (std::size_t iCh = 0; iCh < ft0.channelA().size(); iCh++) { - amplFT0A += ft0.amplitudeA()[iCh]; - } - for (std::size_t iCh = 0; iCh < ft0.channelC().size(); iCh++) { - amplFT0C += ft0.amplitudeC()[iCh]; - } + // --- forward detectors: gain-equalised amplitudes --- + FwdSignal fwd; + if (cfgFwdOpt.cfgFwd) { + loadFwdCalib(coll.template bc_as(), true); + computeFwdReco(coll, vz, fwd, TESTBIT(ev.mask, kSystBase)); } - fillBS1D(bs.amplFT0ACent, cent, amplFT0A); - fillBS1D(bs.amplFT0AMult, coll.multNTracksPV(), amplFT0A); - fillBS1D(bs.amplFT0CCent, cent, amplFT0C); - fillBS1D(bs.amplFT0CMult, coll.multNTracksPV(), amplFT0C); - - double p1kBarFt0A = amplFT0A - state.pmeanFT0AmultpvStep2->GetBinContent(state.pmeanFT0AmultpvStep2->GetXaxis()->FindBin(coll.multNTracksPV())); - double p1kBarFt0C = amplFT0C - state.pmeanFT0CmultpvStep2->GetBinContent(state.pmeanFT0CmultpvStep2->GetXaxis()->FindBin(coll.multNTracksPV())); - - for (int ieta = 0; ieta < nEta; ++ieta) { - const int ibx = state.pmeanNchEtabinStep2->GetXaxis()->FindBin(coll.multNTracksPV()); - const int iby = ieta + 1; - float mmpt = state.pmeanNchEtabinStep2->GetBinContent(ibx, iby); - float mmMult = state.pmeanMultNchEtabinStep2->GetBinContent(ibx, iby); - - meanMult[ieta] = sumwk[ieta][1]; - // covariance requires both bins populated: gate the multiplicity deviation on - // the same >=1-track condition as the mean, so cov skips unless both bins qualify - p1kBarMult[ieta] = (sumwk[ieta][1] >= 1.0) ? (meanMult[ieta] - mmMult) : std::numeric_limits::quiet_NaN(); - - // mean pT of the bin: valid only with >=1 (weighted) track - mean[ieta] = (sumwk[ieta][1] >= 1.0) ? (sumpmwk[ieta][1][1] / sumwk[ieta][1]) : std::numeric_limits::quiet_NaN(); - - // c2 (>=2 tracks), c3 (>=3 tracks) and the pT deviation need a usable reference - c2[ieta] = std::numeric_limits::quiet_NaN(); - c3[ieta] = std::numeric_limits::quiet_NaN(); - p1kBar[ieta] = std::numeric_limits::quiet_NaN(); - if (std::isfinite(mmpt) && mmpt != 0) { - const auto corr = calculateC2C3FromSums(sumpmwk[ieta], sumwk[ieta], mmpt); - c2[ieta] = corr.c2; - c3[ieta] = corr.c3; - if (std::isfinite(mean[ieta])) { - p1kBar[ieta] = mean[ieta] - mmpt; - } + // --- Poisson bootstrap: one weight per sample per event --- + std::array poisW{}; + if (doBoot) { + for (int s = 0; s < nBoot; ++s) { + poisW[s] = rng.Poisson(1.0); } } - // meanpT & C2 vs eta bin - for (int ieta = 0; ieta < nEta; ++ieta) { - if (std::isfinite(meanMult[ieta])) { - fillBS2D(bs.multCent, cent, ieta, meanMult[ieta]); - fillBS2D(bs.multMult, coll.multNTracksPV(), ieta, meanMult[ieta]); - } - - if (std::isfinite(mean[ieta])) { - histos.fill(HIST("Prof_MeanpT_Cent_etabin"), cent, ieta, mean[ieta]); - histos.fill(HIST("Prof_MeanpT_Mult_etabin"), coll.multNTracksPV(), ieta, mean[ieta]); - fillBS2D(bs.meanpTCent, cent, ieta, mean[ieta]); - fillBS2D(bs.meanpTMult, coll.multNTracksPV(), ieta, mean[ieta]); - } - if (std::isfinite(c2[ieta])) { - histos.fill(HIST("Prof_C2_Cent_etabin"), cent, ieta, c2[ieta]); - histos.fill(HIST("Prof_C2_Mult_etabin"), coll.multNTracksPV(), ieta, c2[ieta]); - fillBS2D(bs.c2Cent, cent, ieta, c2[ieta]); - fillBS2D(bs.c2Mult, coll.multNTracksPV(), ieta, c2[ieta]); + // --- c2, c3, subevent correlators and forward covariances of every variation --- + EventObs evObs; + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(ev.mask, v)) { + continue; } - if (std::isfinite(c3[ieta])) { - histos.fill(HIST("Prof_C3_Cent_etabin"), cent, ieta, c3[ieta]); - histos.fill(HIST("Prof_C3_Mult_etabin"), coll.multNTracksPV(), ieta, c3[ieta]); - fillBS2D(bs.c3Cent, cent, ieta, c3[ieta]); - fillBS2D(bs.c3Mult, coll.multNTracksPV(), ieta, c3[ieta]); + computeEventObs(sums[v][0], v, 0, cfgFwdOpt.cfgFwd ? &fwd : nullptr, nCh, evObs); + fillFluc(hists.fluc[v][0], evObs, 1., nCh); + // bootstrap replicas of Base, in the 5% centrality classes + if (v == kSystBase && doBoot) { + for (int s = 0; s < nBoot; ++s) { + if (poisW[s] > 0.) { + fillFluc(hists.boot[s], evObs, poisW[s], ev.cent[kSystBase]); + } + } } } + } + PROCESS_SWITCH(RadialFlowDecorr, processDataFluc, "process data to calculate fluc pT", cfgRunDataFluc); - // mirror-pair subevent (C2Sub) & covariances vs eta bin - for (int ietaA = 1; ietaA <= (nEta - 1) / 2; ++ietaA) { - int ietaC = nEta - ietaA; - float c2Sub = p1kBar[ietaA] * p1kBar[ietaC]; - float c3SubA = c2[ietaA] * p1kBar[ietaC]; // 2 particles from A, 1 from C - float c3SubC = c2[ietaC] * p1kBar[ietaA]; // 2 particles from C, 1 from A - float covAC = p1kBarMult[ietaA] * p1kBar[ietaC]; - float covCA = p1kBar[ietaA] * p1kBarMult[ietaC]; - - if (std::isfinite(c2Sub)) { - histos.fill(HIST("Prof_C2Sub_Cent_etabin"), cent, ietaA, c2Sub); - histos.fill(HIST("Prof_C2Sub_Mult_etabin"), coll.multNTracksPV(), ietaA, c2Sub); - fillBS2D(bs.c2SubCent, cent, ietaA, c2Sub); - fillBS2D(bs.c2SubMult, coll.multNTracksPV(), ietaA, c2Sub); - } - if (std::isfinite(c3SubA)) { - histos.fill(HIST("Prof_C3Sub_Cent_etabin"), cent, ietaA, c3SubA); - histos.fill(HIST("Prof_C3Sub_Mult_etabin"), coll.multNTracksPV(), ietaA, c3SubA); - fillBS2D(bs.c3SubCent, cent, ietaA, c3SubA); - fillBS2D(bs.c3SubMult, coll.multNTracksPV(), ietaA, c3SubA); - } - if (std::isfinite(c3SubC)) { - histos.fill(HIST("Prof_C3Sub_Cent_etabin"), cent, ietaC, c3SubC); - histos.fill(HIST("Prof_C3Sub_Mult_etabin"), coll.multNTracksPV(), ietaC, c3SubC); - fillBS2D(bs.c3SubCent, cent, ietaC, c3SubC); - fillBS2D(bs.c3SubMult, coll.multNTracksPV(), ietaC, c3SubC); - } - if (std::isfinite(covAC)) { - histos.fill(HIST("Prof_Cov_Cent_etabin"), cent, ietaA, covAC); - histos.fill(HIST("Prof_Cov_Mult_etabin"), coll.multNTracksPV(), ietaA, covAC); - fillBS2D(bs.covCent, cent, ietaA, covAC); - fillBS2D(bs.covMult, coll.multNTracksPV(), ietaA, covAC); - } - if (std::isfinite(covCA)) { - histos.fill(HIST("Prof_Cov_Cent_etabin"), cent, ietaC, covCA); - histos.fill(HIST("Prof_Cov_Mult_etabin"), coll.multNTracksPV(), ietaC, covCA); - fillBS2D(bs.covCent, cent, ietaC, covCA); - fillBS2D(bs.covMult, coll.multNTracksPV(), ietaC, covCA); + // =========================================================================== + // Cut QA + // =========================================================================== + // On the unfiltered collisions and tracks (the analysis passes see only the filtered ones): + // every quantity the Base selection cuts on, before and after it. The counters are not + // filled here. N_ch is the number of tracks of the analysis track filter. + void processCutQA(GeneralCollisions::iterator const& col, UnfilteredTracks const& tracks) + { + int nCh = 0; + for (const auto& trk : tracks) { + if (trk.isGlobalTrack() && trk.pt() > KPtMin && trk.pt() < KPtMax) { + ++nCh; } } - - // FT0 covariance vs narrow eta bin (full range, indexed by the actual pT bin) - for (int ieta = 1; ieta < nEta; ++ieta) { - float covFT0Aeta = p1kBarFt0A * p1kBar[ieta]; - float covFT0Ceta = p1kBarFt0C * p1kBar[ieta]; - if (std::isfinite(covFT0Aeta)) { - histos.fill(HIST("Prof_CovFT0A_Cent_etabin"), cent, ieta, covFT0Aeta); - histos.fill(HIST("Prof_CovFT0A_Mult_etabin"), coll.multNTracksPV(), ieta, covFT0Aeta); - fillBS2D(bs.covFT0ACent, cent, ieta, covFT0Aeta); - fillBS2D(bs.covFT0AMult, coll.multNTracksPV(), ieta, covFT0Aeta); - } - if (std::isfinite(covFT0Ceta)) { - histos.fill(HIST("Prof_CovFT0C_Cent_etabin"), cent, ieta, covFT0Ceta); - histos.fill(HIST("Prof_CovFT0C_Mult_etabin"), coll.multNTracksPV(), ieta, covFT0Ceta); - fillBS2D(bs.covFT0CCent, cent, ieta, covFT0Ceta); - fillBS2D(bs.covFT0CMult, coll.multNTracksPV(), ieta, covFT0Ceta); - } + const float cent = getCentrality(col, cfgEvSel.cfgCentralityChoice); + fillCutQAEvent(kQABefore, col, nCh, cent); + const auto ev = isEventSelected(col, nCh, 1 << kSystBase, false); + if (!TESTBIT(ev.mask, kSystBase)) { + return; } - - // full 2D subevent map - for (int ietaA = 1; ietaA < nEta; ++ietaA) { - for (int ietaC = 1; ietaC < nEta; ++ietaC) { - float etaValA = (etaLw[ietaA] + etaUp[ietaA]) / 2.0f; - float etaValB = (etaLw[ietaC] + etaUp[ietaC]) / 2.0f; - float gap = etaValA - etaValB; - float sum = (etaValA + etaValB); - - float c2Sub = (ietaA == ietaC) ? static_cast(c2[ietaA]) : p1kBar[ietaA] * p1kBar[ietaC]; - float c3Sub2D = (ietaA == ietaC) ? static_cast(c3[ietaA]) : c2[ietaA] * p1kBar[ietaC]; // diag: within-bin c3; off-diag: 2 from A, 1 from C - float cov = p1kBarMult[ietaA] * p1kBar[ietaC]; - float covFT0A = p1kBarFt0A * p1kBar[ietaC]; - float covFT0C = p1kBarFt0C * p1kBar[ietaA]; - - if (std::isfinite(c2Sub)) { - histos.fill(HIST("Prof_C2Sub2D_Cent_etaA_etaC"), cent, etaValA, etaValB, c2Sub); - histos.fill(HIST("Prof_GapSum2D"), cent, gap, sum, c2Sub); - fillBS3D(bs.c2Sub2D, cent, etaValA, etaValB, c2Sub); - fillBS3D(bs.gapSum2D, cent, gap, sum, c2Sub); - } - if (std::isfinite(c3Sub2D)) { - histos.fill(HIST("Prof_C3Sub2D_Cent_etaA_etaC"), cent, etaValA, etaValB, c3Sub2D); - histos.fill(HIST("Prof_C3GapSum2D"), cent, gap, sum, c3Sub2D); - fillBS3D(bs.c3Sub2D, cent, etaValA, etaValB, c3Sub2D); - fillBS3D(bs.c3GapSum2D, cent, gap, sum, c3Sub2D); - } - if (std::isfinite(cov)) { - histos.fill(HIST("Prof_Cov2D_Cent_etaA_etaC"), cent, etaValA, etaValB, cov); - fillBS3D(bs.cov2D, cent, etaValA, etaValB, cov); - } - if (std::isfinite(covFT0A)) { - histos.fill(HIST("Prof_CovFT0A2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covFT0A); - fillBS3D(bs.covFT0A2D, cent, etaValA, etaValB, covFT0A); - } - if (std::isfinite(covFT0C)) { - histos.fill(HIST("Prof_CovFT0C2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covFT0C); - fillBS3D(bs.covFT0C2D, cent, etaValA, etaValB, covFT0C); - } + fillCutQAEvent(kQAAfter, col, nCh, cent); + for (const auto& trk : tracks) { + fillCutQATrack(kQABefore, trk); + if (TESTBIT(isTrackSelected(trk, 1 << kSystBase, false), kSystBase)) { + fillCutQATrack(kQAAfter, trk); } } } - PROCESS_SWITCH(RadialFlowDecorr, processDataFluc, "process data to calculate fluc pT", cfgRunDataFluc); + PROCESS_SWITCH(RadialFlowDecorr, processCutQA, "QA of the event and track cut quantities before and after the Base selection", true); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGCF/Femto/Core/baseSelection.h b/PWGCF/Femto/Core/baseSelection.h index 30fe70231e5..a477594bd61 100644 --- a/PWGCF/Femto/Core/baseSelection.h +++ b/PWGCF/Femto/Core/baseSelection.h @@ -78,7 +78,7 @@ class BaseSelection { // check index if (static_cast(observableIndex) >= NumObservables) { - LOG(fatal) << "Observable is not valid. Observable (index) has to be smaller than " << NumObservables; + LOG(fatal) << "Selection '" << selectionName << "': observable is not valid. Observable (index) has to be smaller than " << NumObservables; } // init selection container for selection at given index mSelectionContainers.at(observableIndex) = selectioncontainer::SelectionContainer(selectionName, @@ -112,7 +112,7 @@ class BaseSelection bool isOptionalCut) { if (static_cast(observableIndex) >= NumObservables) { - LOG(fatal) << "Observable is not valid. Observable (index) has to be smaller than " << NumObservables; + LOG(fatal) << "Selection '" << selectionName << "': observable is not valid. Observable (index) has to be smaller than " << NumObservables; } mSelectionContainers.at(observableIndex) = selectioncontainer::SelectionContainer(selectionName, lowerLimit, @@ -125,31 +125,36 @@ class BaseSelection addSelection(observableIndex); } - /// \brief Add a static-value based selection for a specific observable. + /// \brief Add a range-based selection (either inside or outside the configured ranges) for a + /// specific observable. /// \param observableIndex Index of the observable. /// \param selectionName Name of the selection. - /// \param selectionValues Vector of threshold values. - /// \param limitType Type of limit (from limits::LimitType). + /// \param selectionRanges Vector of "lower;upper" range strings. /// \param skipMostPermissiveBit Whether to skip the loosest threshold when assembling the bitmask. /// \param isMinimalCut Whether this cut is mandatory (must be passed for the candidate to be accepted). /// \param isOptionalCut Whether this cut is optional (candidate is accepted if any optional cut passes). + /// \param limitType Either limits::kInsideRange (value must fall inside) or limits::kOutsideRange + /// (value must fall outside, e.g. rejecting a competing mass hypothesis). A + /// fully-open range entry (";") is exempt for either type -- see SelectionContainer. void addSelection(int observableIndex, std::string const& selectionName, std::vector const& selectionRanges, bool skipMostPermissiveBit, bool isMinimalCut, - bool isOptionalCut) + bool isOptionalCut, + limits::LimitType limitType = limits::kInsideRange) { // check index if (static_cast(observableIndex) >= NumObservables) { - LOG(fatal) << "Observable is not valid. Observable (index) has to be smaller than " << NumObservables; + LOG(fatal) << "Selection '" << selectionName << "': observable is not valid. Observable (index) has to be smaller than " << NumObservables; } // init selection container for selection at given index mSelectionContainers.at(observableIndex) = selectioncontainer::SelectionContainer(selectionName, selectionRanges, mPassThrough ? false : skipMostPermissiveBit, mPassThrough ? false : isMinimalCut, - mPassThrough ? false : isOptionalCut); + mPassThrough ? false : isOptionalCut, + limitType); addSelection(observableIndex); } @@ -186,7 +191,7 @@ class BaseSelection mSelectionContainers.at(observableIndex) = selectioncontainer::SelectionContainer(selectionName, std::vector{1}, limits::LimitType::kEqual, false, false, false); break; default: - LOG(fatal) << "Invalid switch for boolean selection"; + LOG(fatal) << "Selection '" << selectionName << "': invalid switch for boolean selection"; } addSelection(observableIndex); } @@ -248,10 +253,46 @@ class BaseSelection } } + /// \brief Evaluate the same observable for two independently-computed values of the same + /// candidate and store only the logical AND of the two resulting per-threshold bitmasks + /// \param observableIndex Index of the observable. + /// \param value1 First value to evaluate. + /// \param value2 Second value to evaluate. + void evaluateObservable(int observableIndex, T value1, T value2) + { + // if there are no selections configured, bail out + if (mSelectionContainers.at(observableIndex).isEmpty()) { + return; + } + // if a previous observable already failed a minimal selection, + // there is no point in evaluating further observables + if (!mPassesMinimalSelections) { + return; + } + auto& container = mSelectionContainers.at(observableIndex); + container.evaluate(value1); + auto const bitmask1 = container.getRawBitmask(); + container.evaluate(value2); + auto const bitmask2 = container.getRawBitmask(); + container.setBitmask((bitmask1 & bitmask2).to_ullong()); + // if any minimal selection is not fulfilled, the candidate is rejected + if (mHasMinimalSelection) { + if (!container.passesAsMinimalCut()) { + mPassesMinimalSelections = false; + } + } + // if any optional selection is fulfilled, the candidate is accepted + if (mHasOptionalSelection) { + if (container.passesAsOptionalCut()) { + mPassesOptionalSelections = true; + } + } + } + /// \brief Evaluate a single observable against its configured selections. /// \param observableIndex Index of the observable. /// \param values Vector of values of the observable. - void evaluateObservable(int observableIndex, std::vector values) + void evaluateObservable(int observableIndex, std::vector const& values) { // if there are no selections configured, bail out if (mSelectionContainers.at(observableIndex).isEmpty()) { @@ -283,6 +324,11 @@ class BaseSelection /// \param comments Vector of comment strings, one per selection threshold. void addComments(int observableIndex, std::vector const& comments) { mSelectionContainers.at(observableIndex).addComments(comments); } + /// \brief Attach the same comment to every selection threshold of a specific observable. + /// \param observableIndex Index of the observable. + /// \param comment Comment string applied to all thresholds. + void addComments(int observableIndex, std::string const& comment) { mSelectionContainers.at(observableIndex).addComments(comment); } + [[nodiscard]] bool isPassThrough() const { return mPassThrough; } /// \brief Check whether all required and optional cuts are passed. @@ -377,7 +423,7 @@ class BaseSelection /// \brief Print the full configuration of all selections to the log. /// \param objectName Name of the object owning this selection (used as label in the log output). - void printSelections(const std::string& objectName) const + void printSelections(const char* objectName) const { LOG(info) << "Printing Configuration of " << objectName; for (size_t idx = 0; idx < mSelectionContainers.size(); ++idx) { @@ -531,7 +577,7 @@ class BaseSelection } if (mNSelectionBits > sizeof(BitmaskType) * CHAR_BIT) { - LOG(fatal) << "Too many selections. At most " << sizeof(BitmaskType) * CHAR_BIT << " number of bits are supported"; + LOG(fatal) << "Selection '" << mSelectionContainers.at(observableIndex).getSelectionName() << "': too many selections. At most " << sizeof(BitmaskType) * CHAR_BIT << " number of bits are supported"; } } diff --git a/PWGCF/Femto/Core/cascadeBuilder.h b/PWGCF/Femto/Core/cascadeBuilder.h index 1248e0bfc8d..3dda17bf3a5 100644 --- a/PWGCF/Femto/Core/cascadeBuilder.h +++ b/PWGCF/Femto/Core/cascadeBuilder.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -47,54 +48,49 @@ struct ConfCascadeFilters : o2::framework::ConfigurableGroup { o2::framework::Configurable etaMax{"etaMax", 10.f, "Maximum eta"}; o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; - o2::framework::Configurable massLambdaMin{"massLambdaMin", 1.0f, "Minimum mass of Lambda daughters in cascade decay"}; - o2::framework::Configurable massLambdaMax{"massLambdaMax", 1.2f, "Maximum mass of Lambda daughters in cascade decay"}; o2::framework::Configurable massXiMin{"massXiMin", 1.2f, "Minimum Xi mass"}; o2::framework::Configurable massXiMax{"massXiMax", 1.4f, "Maximum Xi mass"}; - o2::framework::Configurable rejectHypothesisOmega{"rejectHypothesisOmega", false, "Rejection of Omega hypothesis for Xi candidates"}; - o2::framework::Configurable rejectMassOmegaMin{"rejectMassOmegaMin", 1.668f, "Reject minimum Omega mass for Xi hypothesis"}; - o2::framework::Configurable rejectMassOmegaMax{"rejectMassOmegaMax", 1.676f, "Reject maximum Omega mass for Xi hypothesis"}; o2::framework::Configurable massOmegaMin{"massOmegaMin", 1.5f, "Minimum Omega mass"}; o2::framework::Configurable massOmegaMax{"massOmegaMax", 1.9f, "Maximum Omega mass"}; - o2::framework::Configurable rejectHypothesisXi{"rejectHypothesisXi", false, "Rejection of Xi hypothesis for Omega candidates"}; - o2::framework::Configurable rejectMassXiMin{"rejectMassXiMin", 1.317f, "Reject Minimum Xi mass for Omega hypothesis"}; - o2::framework::Configurable rejectMassXiMax{"rejectMassXiMax", 1.325f, "Reject Maximum Xi mass for Omega hypothesis"}; }; // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define CASCADE_DEFAULT_BITS \ - o2::framework::Configurable passThrough{"passThrough", false, "If true, all Cascades are passed through. Bits for all selections are stored."}; \ - o2::framework::Configurable> cascadeCpaMin{"cascadeCpaMin", {"0.95"}, "Minimum CPA as TFormula, x=pt"}; \ - o2::framework::Configurable> cascadePaMax{"cascadePaMax", {}, "Maximum pointing angle (rad) as TFormula, x=pt. Empty to disable"}; \ - o2::framework::Configurable> cascadeTransRadMin{"cascadeTransRadMin", {0.9f}, "Minimum transverse radius (cm)"}; \ - o2::framework::Configurable> cascadeDcaDauMax{"cascadeDcaDauMax", {0.25f}, "Maximum DCA between the daughters at decay vertex (cm)"}; \ - o2::framework::Configurable> lambdaCpaMin{"lambdaCpaMin", {0.78f}, "Minimum cosine of pointing angle"}; \ - o2::framework::Configurable> lambdaTransRadMin{"lambdaTransRadMin", {0.9f}, "Minimum transverse radius (cm)"}; \ - o2::framework::Configurable> lambdaDcaDauMax{"lambdaDcaDauMax", {0.5f}, "Maximum DCA between the daughters at decay vertex (cm)"}; \ - o2::framework::Configurable> lambdaDcaToPvMin{"lambdaDcaToPvMin", {0.3f}, "Minimum DCA between the lambda and primary vertex"}; \ - o2::framework::Configurable> dauAbsEtaMax{"dauAbsEtaMax", {0.8f}, "Maximum |eta| of all daughters"}; \ - o2::framework::Configurable> dauAbsDcaxyMin{"dauAbsDcaxyMin", {0.05f}, "Minimum |DCAxy| of the daughters and bachelor from primary vertex (cm)"}; \ - o2::framework::Configurable> dauTpcClustersMin{"dauTpcClustersMin", {80.f}, "Minimum number of TPC clusters for daughter tracks"}; \ - o2::framework::Configurable> posDauTpc{"posDauTpc", {5.f}, "Maximum |nsimga_Pion/Proton| TPC for positive daughter tracks"}; \ - o2::framework::Configurable> negDauTpc{"negDauTpc", {5.f}, "Maximum |nsimga_Pion/Proton| TPC for negative daughter tracks"}; \ - o2::framework::Configurable> posDauTof{"posDauTof", {}, "Maximum |nsimga_Pion/Proton| TOF for positive daughter tracks"}; \ - o2::framework::Configurable> negDauTof{"negDauTof", {}, "Maximum |nsigma_Pion/Proton| TOF for negative daughter tracks"}; \ - o2::framework::Configurable requireTof{"requireTof", false, "If true, TOF PID is a mandatory selection"}; \ - o2::framework::Configurable keepTracksWithoutTof{"keepTracksWithoutTof", true, "If true, candidates whose daughters have no TOF signal are kept"}; +#define CASCADE_DEFAULT_BITS \ + o2::framework::Configurable passThrough{"passThrough", false, "If true, all Cascades are passed through. Bits for all selections are stored."}; \ + o2::framework::Configurable> cascadeCpaMin{"cascadeCpaMin", {"0.95"}, "Minimum CPA as TFormula, x=pt"}; \ + o2::framework::Configurable> cascadePaMax{"cascadePaMax", {}, "Maximum pointing angle (rad) as TFormula, x=pt. Empty to disable"}; \ + o2::framework::Configurable> cascadeTransRadMin{"cascadeTransRadMin", {0.9f}, "Minimum transverse radius (cm)"}; \ + o2::framework::Configurable> cascadeDcaDauMax{"cascadeDcaDauMax", {0.25f}, "Maximum DCA between the daughters at decay vertex (cm)"}; \ + o2::framework::Configurable> lambdaCpaMin{"lambdaCpaMin", {0.78f}, "Minimum cosine of pointing angle"}; \ + o2::framework::Configurable> lambdaTransRadMin{"lambdaTransRadMin", {0.9f}, "Minimum transverse radius (cm)"}; \ + o2::framework::Configurable> lambdaDcaDauMax{"lambdaDcaDauMax", {0.5f}, "Maximum DCA between the daughters at decay vertex (cm)"}; \ + o2::framework::Configurable> lambdaDcaToPvMin{"lambdaDcaToPvMin", {0.3f}, "Minimum DCA between the lambda and primary vertex"}; \ + o2::framework::Configurable> lambdaMassRanges{"lambdaMassRanges", {"1.0;1.2"}, "Lambda daughter mass ranges (lower;upper) to accept -- bit is set if the cascade's mLambda falls inside the range"}; \ + o2::framework::Configurable> dauAbsEtaMax{"dauAbsEtaMax", {0.8f}, "Maximum |eta| of all daughters"}; \ + o2::framework::Configurable> dauAbsDcaxyMin{"dauAbsDcaxyMin", {0.05f}, "Minimum |DCAxy| of the daughters and bachelor from primary vertex (cm)"}; \ + o2::framework::Configurable> dauTpcClustersMin{"dauTpcClustersMin", {80.f}, "Minimum number of TPC clusters for daughter tracks"}; \ + o2::framework::Configurable> posDauTpc{"posDauTpc", {5.f}, "Maximum |nsigma_Pion/Proton| TPC for positive daughter tracks"}; \ + o2::framework::Configurable> negDauTpc{"negDauTpc", {5.f}, "Maximum |nsigma_Pion/Proton| TPC for negative daughter tracks"}; \ + o2::framework::Configurable> posDauTof{"posDauTof", {}, "Maximum |nsigma_Pion/Proton| TOF for positive daughter tracks"}; \ + o2::framework::Configurable> negDauTof{"negDauTof", {}, "Maximum |nsigma_Pion/Proton| TOF for negative daughter tracks"}; \ + o2::framework::Configurable requireTof{"requireTof", false, "If true, the TOF PID cut is mandatory. For daughters with a TOF signal, the candidate is rejected on failure. For daughters without a TOF signal, the candidate is rejected unless keepTracksWithoutTof is true"}; \ + o2::framework::Configurable keepTracksWithoutTof{"keepTracksWithoutTof", true, "If true, daughters without a TOF signal pass the TOF PID cut unconditionally, overriding requireTof. If false, daughters without a TOF signal fail the TOF cut (and are rejected if requireTof is true)"}; struct ConfXiBits : o2::framework::ConfigurableGroup { std::string prefix = std::string("XiBits"); CASCADE_DEFAULT_BITS - o2::framework::Configurable> bachelorTpcPion{"bachelorTpcPion", {5.f}, "Maximum |nsimga_Pion| TPC for bachelor tracks"}; - o2::framework::Configurable> bachelorTofPion{"bachelorTofPion", {}, "Maximum |nsimga_Pion| TOF for bachelor tracks"}; + o2::framework::Configurable> bachelorTpcPion{"bachelorTpcPion", {5.f}, "Maximum |nsigma_Pion| TPC for bachelor tracks"}; + o2::framework::Configurable> bachelorTofPion{"bachelorTofPion", {}, "Maximum |nsigma_Pion| TOF for bachelor tracks"}; + o2::framework::Configurable> rejectOmegaMassRanges{"rejectOmegaMassRanges", {"1.668;1.676"}, "Omega mass ranges (lower;upper) to reject -- bit is set if the candidate's mOmega falls outside the range"}; }; struct ConfOmegaBits : o2::framework::ConfigurableGroup { std::string prefix = std::string("OmegaBits"); CASCADE_DEFAULT_BITS - o2::framework::Configurable> bachelorTpcKaon{"bachelorTpcKaon", {5.f}, "Maximum |nsimga_Kaon| TPC for bachelor tracks"}; - o2::framework::Configurable> bachelorTofKaon{"bachelorTofKaon", {}, "Maximum |nsimga_Kaon| TOF for bachelor tracks"}; + o2::framework::Configurable> bachelorTpcKaon{"bachelorTpcKaon", {5.f}, "Maximum |nsigma_Kaon| TPC for bachelor tracks"}; + o2::framework::Configurable> bachelorTofKaon{"bachelorTofKaon", {}, "Maximum |nsigma_Kaon| TOF for bachelor tracks"}; + o2::framework::Configurable> rejectXiMassRanges{"rejectXiMassRanges", {"1.317;1.325"}, "Xi mass ranges (lower;upper) to reject -- bit is set if the candidate's mXi falls outside the range"}; }; #undef CASCADE_DEFAULT_BITS @@ -130,19 +126,24 @@ enum CascadeSels { // selections for cascades kCascadeCpaMin, ///< Min. CPA (cosine pointing angle) kCascadePaMax, ///< Max. PA (pointing angle in rad); disabled by default - kCascadeDcaDaughMax, ///< Max. DCA of the daughers at decay vertex + kCascadeDcaDaughMax, ///< Max. DCA of the daughters at decay vertex kCascadeTransRadMin, ///< max. transverse radius // selection for lambda daughter kLambdaCpaMin, ///< Min. CPA of the lambda kLambdaDcaDauMax, ///< Max. DCA between the lambda daughters at lambda decay vertex - kLambdaTransRadMin, ///< Min. tranverse radius of the lambda + kLambdaTransRadMin, ///< Min. transverse radius of the lambda kLambdaDcaToPvMin, ///< Min. DCA of the lambda to the primary vertex + kLambdaMassRange, ///< Lambda daughter mass must fall inside the configured range(s) - // selection for bachelor/daugthers + // rejection of competing mass hypotheses (range-based: bit is set if the candidate is outside the range) + kRejectOmegaMass, ///< Xi: reject Omega mass hypothesis + kRejectXiMass, ///< Omega: reject Xi mass hypothesis + + // selection for bachelor/daughters kDauAbsEtaMax, ///< Max. |eta| of daughter tracks kDauTpcClsMin, ///< Min. number of TPC clusters of daughters/bachelor - kDauAbsDcaxyMin, ///< Min. |DCAxy| of the daughers and bachelor from primary vertex + kDauAbsDcaxyMin, ///< Min. |DCAxy| of the daughters and bachelor from primary vertex // PID selection for cascade bachelor kBachelorTpcPion, ///< TPC Pion PID for bachelor @@ -150,7 +151,7 @@ enum CascadeSels { kBachelorTofPion, ///< TOF Pion PID for bachelor kBachelorTofKaon, ///< TOF Kaon PID for bachelor /// - // PID selection for lambda daughers + // PID selection for lambda daughters kPosDauTpc, ///< TPC PID for positive daughter kNegDauTpc, ///< TPC PID for negative daughter kPosDauTof, ///< TOF PID for positive daughter @@ -172,6 +173,10 @@ const std::unordered_map cascadeSelectionNames = { {kLambdaDcaDauMax, "Lambda DCA Daughter Max"}, {kLambdaTransRadMin, "Lambda Transverse Radius Min"}, {kLambdaDcaToPvMin, "Lambda DCA to PV Min"}, + {kLambdaMassRange, "Lambda Daughter Mass Range"}, + + {kRejectOmegaMass, "Reject Omega mass hypothesis"}, + {kRejectXiMass, "Reject Xi mass hypothesis"}, {kDauAbsEtaMax, "Daughter Abs Eta Max"}, {kDauTpcClsMin, "Daughter TPC Clusters Min"}, @@ -194,14 +199,8 @@ enum CascadeFilters { kEtaMax, kPhiMin, kPhiMax, - kLambdaMassMin, - kLambdaMassMax, - kXiMassMin, - kXiMassMax, - kRejectionOmegaMass, - kOmegaMassMin, - kOmegaMassMax, - kRejectionXiMass, + kMassMin, ///< signal mass window lower bound, for this cascadeType's own mass hypothesis + kMassMax, ///< signal mass window upper bound, for this cascadeType's own mass hypothesis kCascadeFiltersMax }; @@ -214,14 +213,8 @@ const std::unordered_map cascadeFilterNames = { {kEtaMax, "etaMax"}, {kPhiMin, "phiMin"}, {kPhiMax, "phiMax"}, - {kLambdaMassMin, "lambdaMassMin"}, - {kLambdaMassMax, "lambdaMassMax"}, - {kXiMassMin, "xiMassMin"}, - {kXiMassMax, "xiMassMax"}, - {kRejectionOmegaMass, "rejectOmega"}, - {kOmegaMassMin, "omegaMassMin"}, - {kOmegaMassMax, "omegaMassMax"}, - {kRejectionXiMass, "rejectXi"}, + {kMassMin, "massMin"}, + {kMassMax, "massMax"}, }; /// \class FemtoDreamTrackCuts @@ -244,34 +237,33 @@ class CascadeSelection : public baseselection::BaseSelection produceXiMasks{"produceXiMasks", -1, "Produce XiMasks (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceXiExtras{"produceXiExtras", -1, "Produce XiExtras (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceXiFrac{"produceXiFrac", -1, "Produce XiFrac (cpa for fraction extraction) (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceOmegas{"produceOmegas", -1, "Produce Omegas (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceLiteOmegas{"produceLiteOmegas", -1, "Produce LiteOmegas (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceOmegaMasks{"produceOmegaMasks", -1, "Produce OmegaMasks (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceOmegaExtras{"produceOmegaExtras", -1, "Produce OmegaExtras (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceOmegaFrac{"produceOmegaFrac", -1, "Produce OmegaFrac (cpa for fraction extraction) (-1: auto; 0 off; 1 on)"}; }; template @@ -540,6 +519,7 @@ class CascadeBuilder mProduceXis = utils::enableTable("FXis_001", table.produceXis.value, initContext); mProduceXiMasks = utils::enableTable("FXiMasks_002", table.produceXiMasks.value, initContext); mProduceXiExtras = utils::enableTable("FXiExtras_001", table.produceXiExtras.value, initContext); + mProduceXiFrac = utils::enableTable("FXiFrac_001", table.produceXiFrac.value, initContext); if (mProduceXis && mProduceLiteXis) { LOG(fatal) << "FXis and FLiteXis are mutually exclusive -- enable only one. " @@ -563,6 +543,7 @@ class CascadeBuilder mProduceLiteOmegas = utils::enableTable("FLiteOmegas_001", table.produceLiteOmegas.value, initContext); mProduceOmegaMasks = utils::enableTable("FOmegaMasks_002", table.produceOmegaMasks.value, initContext); mProduceOmegaExtras = utils::enableTable("FOmegaExtras_001", table.produceOmegaExtras.value, initContext); + mProduceOmegaFrac = utils::enableTable("FOmegaFrac_001", table.produceOmegaFrac.value, initContext); if (mProduceOmegas && mProduceLiteOmegas) { LOG(fatal) << "FOmegas and FLiteOmegas are mutually exclusive -- enable only one. " @@ -581,8 +562,8 @@ class CascadeBuilder } } - if (mProduceXis || mProduceLiteXis || mProduceXiExtras || mProduceXiMasks || - mProduceOmegas || mProduceLiteOmegas || mProduceOmegaMasks || mProduceOmegaExtras) { + if (mProduceXis || mProduceLiteXis || mProduceXiExtras || mProduceXiFrac || mProduceXiMasks || + mProduceOmegas || mProduceLiteOmegas || mProduceOmegaMasks || mProduceOmegaExtras || mProduceOmegaFrac) { mFillAnyTable = true; } else { LOG(info) << "No tables configured, Selection object will not be configured..."; @@ -726,6 +707,9 @@ class CascadeBuilder strangeTofPosDau, strangeTofNegDau); } + if (mProduceXiFrac) { + cascadeProducts.producedXiFrac(cascade.casccosPA(col.posX(), col.posY(), col.posZ())); + } } if constexpr (modes::isEqual(cascadeType, modes::Cascade::kOmega)) { if (mProduceOmegas) { @@ -766,6 +750,9 @@ class CascadeBuilder strangeTofPosDau, strangeTofNegDau); } + if (mProduceOmegaFrac) { + cascadeProducts.producedOmegaFrac(cascade.casccosPA(col.posX(), col.posY(), col.posZ())); + } } } @@ -779,10 +766,12 @@ class CascadeBuilder bool mProduceLiteXis = false; bool mProduceXiMasks = false; bool mProduceXiExtras = false; + bool mProduceXiFrac = false; bool mProduceOmegas = false; bool mProduceLiteOmegas = false; bool mProduceOmegaMasks = false; bool mProduceOmegaExtras = false; + bool mProduceOmegaFrac = false; }; struct ConfCascadeTablesDerivedToDerived : o2::framework::ConfigurableGroup { diff --git a/PWGCF/Femto/Core/cascadeHistManager.h b/PWGCF/Femto/Core/cascadeHistManager.h index 5d60057b60a..73d13b7a6a8 100644 --- a/PWGCF/Femto/Core/cascadeHistManager.h +++ b/PWGCF/Femto/Core/cascadeHistManager.h @@ -46,7 +46,8 @@ enum CascadeHist { kPhi, kMass, kSign, - kPtVsMass, // nice to have during analysis + kPtVsMass, // nice to have during analysis + kFractionCpa, // fraction estimation: pt vs cosPa, binned independently of the QA cosPa axis // qa variables kMassXi, kMassOmega, @@ -66,7 +67,6 @@ enum CascadeHist { kPtVsEta, kPtVsPhi, kPhiVsEta, - kPtVsCosPa, kPtVsPa, kPtVsMassXi, kPtVsMassOmega, @@ -83,7 +83,7 @@ enum CascadeHist { kTruePtVsPt, kTrueEtaVsEta, kTruePhiVsPhi, - // histograms for fraction estimation of v0s + // histograms for fraction estimation of cascades kNoMcParticle, kPrimary, kFromWrongCollision, @@ -107,6 +107,7 @@ struct ConfCascadeBinning : o2::framework::ConfigurableGroup { o2::framework::ConfigurableAxis mass{"mass", {{1000, 1.f, 2.f}}, "Mass"}; o2::framework::ConfigurableAxis sign{"sign", {{3, -1.5, 1.5}}, "Sign"}; o2::framework::ConfigurableAxis pdgCodes{"pdgCodes", {{8001, -4000.5, 4000.5}}, "MC ONLY: PDG codes of reconstructed cascades"}; + o2::framework::ConfigurableAxis fractionCpa{"fractionCpa", {{100, 0.95, 1}}, "Cosine of pointing angle (fraction estimation)"}; }; constexpr const char PrefixXiBinning[] = "XiBinning"; @@ -120,16 +121,16 @@ struct ConfCascadeQaBinning : o2::framework::ConfigurableGroup { o2::framework::Configurable plot2d{"plot2d", true, "Enable 2d Qa histograms"}; o2::framework::Configurable plotOrigins{"plotOrigins", true, "MC ONLY: Plot pt vs cosPa for different particle origins"}; o2::framework::Configurable> pdgCodesForMothersOfSecondary{"pdgCodesForMothersOfSecondary", {3312, 3334}, "MC ONLY: PDG codes of mothers of secondaries (Max 3 will be considered)"}; - o2::framework::ConfigurableAxis cosPa{"cosPa", {{100, 0.9, 1}}, "Cosine of poiting angle"}; - o2::framework::ConfigurableAxis pa{"pa", {{180, 0, 1.f * o2::constants::math::PI}}, "Poiting angle"}; + o2::framework::ConfigurableAxis cosPa{"cosPa", {{100, 0.95, 1}}, "Cosine of pointing angle"}; + o2::framework::ConfigurableAxis pa{"pa", {{180, 0, 1.f * o2::constants::math::PI}}, "Pointing angle"}; o2::framework::ConfigurableAxis dauDcaAtDecay{"dauDcaAtDecay", {{150, 0, 1.5}}, "Daughter DCA at decay vertex"}; o2::framework::ConfigurableAxis transRadius{"transRadius", {{100, 0, 100}}, "Transverse radius"}; - o2::framework::ConfigurableAxis massXi{"massXi", {{400, 1.2f, 1.6f}}, "mass for antiparticle hypothesis"}; - o2::framework::ConfigurableAxis massOmega{"massOmega", {{400, 1.4f, 1.8f}}, "mass for antiparticle hypothesis"}; + o2::framework::ConfigurableAxis massXi{"massXi", {{400, 1.2f, 1.6f}}, "Mass under the Xi hypothesis"}; + o2::framework::ConfigurableAxis massOmega{"massOmega", {{400, 1.4f, 1.8f}}, "Mass under the Omega hypothesis"}; o2::framework::ConfigurableAxis lambdaMass{"lambdaMass", {{200, 1., 1.2}}, "Mass of daughter lambda"}; o2::framework::ConfigurableAxis lambdaCosPa{"lambdaCosPa", {{100, 0.9, 1}}, "Cosine of pointing angle of daughter lambda"}; o2::framework::ConfigurableAxis lambdaDauDca{"lambdaDauDca", {{150, 0, 1.0}}, "DCA of lambda daughters at lambda decay vertex"}; - o2::framework::ConfigurableAxis lambdaTransRadius{"lambdaTransRadius", {{100, 0, 100}}, "DCA of lambda daughters at lambda decay vertex"}; + o2::framework::ConfigurableAxis lambdaTransRadius{"lambdaTransRadius", {{100, 0, 100}}, "Transverse radius of daughter #Lambda decay vertex"}; o2::framework::ConfigurableAxis lambdaDcaToPv{"lambdaDcaToPv", {{100, 0, 200}}, "DCA of lambda daughter from primary vertex"}; o2::framework::ConfigurableAxis strangeTof{"strangeTof", {{500, -5, 5}}, "Strangeness TOF vs TOF Nsigma for bachelor/daughters"}; }; @@ -149,6 +150,7 @@ constexpr std::array, kCascadeHistLast> HistT {kMass, o2::framework::HistType::kTH1F, "hMass", "Invariant Mass; m_{Inv} (GeV/#it{c}^{2}); Entries"}, {kSign, o2::framework::HistType::kTH1F, "hSign", "Sign (-1 -> particle, 0 -> self conjugate, +1 -> antiparticle); sign; Entries"}, {kPtVsMass, o2::framework::HistType::kTH2F, "hPtVsMass", "Transverse momentum vs invariant mass; p_{T} (GeV/#it{c}); m_{Inv} (GeV/#it{c}^{2})"}, + {kFractionCpa, o2::framework::HistType::kTH2F, "hFractionCpa", "p_{T} vs Cosine of pointing angle (fraction estimation); p_{T} (GeV/#it{c}); cos(#alpha)"}, {kMassXi, o2::framework::HistType::kTH1F, "hMassXi", "Mass #Xi; m_{#Lambda#pi} (GeV/#it{c}^{2}); Entries"}, {kMassOmega, o2::framework::HistType::kTH1F, "hMassOmega", "mass #Omega; m_{#LambdaK} (GeV/#it{c}^{2}); Entries"}, {kCosPa, o2::framework::HistType::kTH1F, "hCosPa", "Cosine of pointing angle; cos(#alpha); Entries"}, @@ -156,7 +158,7 @@ constexpr std::array, kCascadeHistLast> HistT {kDecayDauDca, o2::framework::HistType::kTH1F, "hDauDca", "Daughter DCA at decay vertex ; DCA_{Decay vertex} (cm); Entries"}, {kTransRadius, o2::framework::HistType::kTH1F, "hTransRadius", "Transverse radius ; r_{xy} (cm); Entries"}, {kLambdaMass, o2::framework::HistType::kTH1F, "hLambdaMass", "Invariant mass of daughter lambda ; m_{#Lambda dau} (GeV/#it{c}^{2}); Entries"}, - {kLambdaCosPa, o2::framework::HistType::kTH1F, "hLambdaCosPa", "Cosine of poiting angle of daughter lambda ; cos_{#Lambda dau}(#alpha); Entries"}, + {kLambdaCosPa, o2::framework::HistType::kTH1F, "hLambdaCosPa", "Cosine of pointing angle of daughter lambda ; cos_{#Lambda dau}(#alpha); Entries"}, {kLambdaDauDca, o2::framework::HistType::kTH1F, "hLambdaDauDca", "Daughter DCA at #Lambda decay vertex ; DCA_{#Lambda dau decay vertex} (cm); Entries"}, {kLambdaTransRadius, o2::framework::HistType::kTH1F, "hLambdaTransRadius", "Transverse radius of daughter #Lambda ; r_{xy,#Lambda dau} (cm); Entries"}, {kLambdaDcaToPv, o2::framework::HistType::kTH1F, "hLambdaDcaToPv", "DCA to primary vertex of daughter #Lambda ; DCA_{#Lambda} (cm); Entries"}, @@ -166,8 +168,7 @@ constexpr std::array, kCascadeHistLast> HistT {kPtVsEta, o2::framework::HistType::kTH2F, "hPtVsEta", "p_{T} vs #eta; p_{T} (GeV/#it{c}) ; #eta"}, {kPtVsPhi, o2::framework::HistType::kTH2F, "hPtVsPhi", "p_{T} vs #varphi; p_{T} (GeV/#it{c}) ; #varphi"}, {kPhiVsEta, o2::framework::HistType::kTH2F, "hPhiVsEta", "#varphi vs #eta; #varphi ; #eta"}, - {kPtVsCosPa, o2::framework::HistType::kTH2F, "hPtVsCosPa", "p_{T} vs Cosine of poiting angle; p_{T} (GeV/#it{c}); cos(#alpha)"}, - {kPtVsPa, o2::framework::HistType::kTH2F, "hPtVsPa", "p_{T} vs Poiting angle; p_{T} (GeV/#it{c}); #alpha"}, + {kPtVsPa, o2::framework::HistType::kTH2F, "hPtVsPa", "p_{T} vs Pointing angle; p_{T} (GeV/#it{c}); #alpha"}, {kPtVsMassXi, o2::framework::HistType::kTH2F, "hPtVsMassXi", "p_{T} vs mass #Xi; p_{T} (GeV/#it{c}); m_{#Lambda#pi} (GeV/#it{c}^{2})"}, {kPtVsMassOmega, o2::framework::HistType::kTH2F, "hPtVsMassOmega", "p_{T} vs mass #Omega; p_{T} (GeV/#it{c}); m_{#LambdaK} (GeV/#it{c}^{2})"}, {kPtVsMassLambda, o2::framework::HistType::kTH2F, "hPtVsMassLambda", "p_{T} vs mass daughter #Lambda; p_{T} (GeV/#it{c}); m_{#Lambda dau} (GeV/#it{c}^{2})"}, @@ -176,9 +177,9 @@ constexpr std::array, kCascadeHistLast> HistT {kStrangeTofVsTofPosDau, o2::framework::HistType::kTH2F, "hStrangeTofVsTofPosDau", "TOF_{Strange} vs TOF_{Tracking} of positive Daughter; n#sigma_{TOF, strange}; n#sigma_{TOF, tracking}"}, {kStrangeTofVsTofNegDau, o2::framework::HistType::kTH2F, "hStrangeTofVsTofNegDau", "TOF_{Strange} vs TOF_{Tracking} of negative Daughter; n#sigma_{TOF, strange}; n#sigma_{TOF, tracking}"}, {kOrigin, o2::framework::HistType::kTH1F, "hOrigin", "Status Codes (=Origin); Status Code; Entries"}, - {kPdg, o2::framework::HistType::kTH1F, "hPdg", "PDG Codes of reconstructed v0; PDG Code; Entries"}, - {kPdgMother, o2::framework::HistType::kTH1F, "hPdgMother", "PDG Codes of mother of reconstructed v0; PDG Code; Entries"}, - {kPdgPartonicMother, o2::framework::HistType::kTH1F, "hPdgPartonicMother", "PDG Codes of partonic mother of reconstructed v0; PDG Code; Entries"}, + {kPdg, o2::framework::HistType::kTH1F, "hPdg", "PDG Codes of reconstructed cascade; PDG Code; Entries"}, + {kPdgMother, o2::framework::HistType::kTH1F, "hPdgMother", "PDG Codes of mother of reconstructed cascade; PDG Code; Entries"}, + {kPdgPartonicMother, o2::framework::HistType::kTH1F, "hPdgPartonicMother", "PDG Codes of partonic mother of reconstructed cascade; PDG Code; Entries"}, {kTruePtVsPt, o2::framework::HistType::kTH2F, "hTruePtVsPt", "True transverse momentum vs transverse momentum; p_{T,True} (GeV/#it{c}); p_{T,True} (GeV/#it{c})"}, {kTrueEtaVsEta, o2::framework::HistType::kTH2F, "hTrueEtaVsEta", "True pseudorapdity vs pseudorapdity; #eta_{True}; #eta"}, {kTruePhiVsPhi, o2::framework::HistType::kTH2F, "hTruePhiVsPhi", "True azimuthal angle vs azimuthal angle; #varphi_{True}; #varphi"}, @@ -194,13 +195,14 @@ constexpr std::array, kCascadeHistLast> HistT }; // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define CASCADE_HIST_ANALYSIS_MAP(conf) \ - {kPt, {(conf).pt}}, \ - {kEta, {(conf).eta}}, \ - {kPhi, {(conf).phi}}, \ - {kMass, {(conf).mass}}, \ - {kSign, {(conf).sign}}, \ - {kPtVsMass, {(conf).pt, (conf).mass}}, +#define CASCADE_HIST_ANALYSIS_MAP(conf) \ + {kPt, {(conf).pt}}, \ + {kEta, {(conf).eta}}, \ + {kPhi, {(conf).phi}}, \ + {kMass, {(conf).mass}}, \ + {kSign, {(conf).sign}}, \ + {kPtVsMass, {(conf).pt, (conf).mass}}, \ + {kFractionCpa, {(conf).pt, (conf).fractionCpa}}, // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define CASCADE_HIST_MC_MAP(conf) \ @@ -228,7 +230,6 @@ constexpr std::array, kCascadeHistLast> HistT {kPtVsEta, {(confAnalysis).pt, (confAnalysis).eta}}, \ {kPtVsPhi, {(confAnalysis).pt, (confAnalysis).phi}}, \ {kPhiVsEta, {(confAnalysis).phi, (confAnalysis).eta}}, \ - {kPtVsCosPa, {(confAnalysis).pt, (confQa).cosPa}}, \ {kPtVsPa, {(confAnalysis).pt, (confQa).pa}}, \ {kMassXi, {(confQa).massXi}}, \ {kMassOmega, {(confQa).massOmega}}, \ @@ -240,17 +241,20 @@ constexpr std::array, kCascadeHistLast> HistT {kStrangeTofVsTofPosDau, {(confQa).strangeTof, (confQa).strangeTof}}, \ {kStrangeTofVsTofNegDau, {(confQa).strangeTof, (confQa).strangeTof}}, +// per-origin truth-split variants of the FitVars fraction-estimation CPA histogram; binned on +// fractionCpa (not the QA-only cosPa) so they are available wherever FitVars is, including the +// pair task where no QA binning config exists // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define CASCADE_HIST_MC_QA_MAP(confAnalysis, confQa) \ - {kNoMcParticle, {(confAnalysis).pt, (confQa).cosPa}}, \ - {kPrimary, {(confAnalysis).pt, (confQa).cosPa}}, \ - {kFromWrongCollision, {(confAnalysis).pt, (confQa).cosPa}}, \ - {kFromMaterial, {(confAnalysis).pt, (confQa).cosPa}}, \ - {kMissidentified, {(confAnalysis).pt, (confQa).cosPa}}, \ - {kSecondary1, {(confAnalysis).pt, (confQa).cosPa}}, \ - {kSecondary2, {(confAnalysis).pt, (confQa).cosPa}}, \ - {kSecondary3, {(confAnalysis).pt, (confQa).cosPa}}, \ - {kSecondaryOther, {(confAnalysis).pt, (confQa).cosPa}}, +#define CASCADE_HIST_FITVARS_MC_MAP(conf) \ + {kNoMcParticle, {(conf).pt, (conf).fractionCpa}}, \ + {kPrimary, {(conf).pt, (conf).fractionCpa}}, \ + {kFromWrongCollision, {(conf).pt, (conf).fractionCpa}}, \ + {kFromMaterial, {(conf).pt, (conf).fractionCpa}}, \ + {kMissidentified, {(conf).pt, (conf).fractionCpa}}, \ + {kSecondary1, {(conf).pt, (conf).fractionCpa}}, \ + {kSecondary2, {(conf).pt, (conf).fractionCpa}}, \ + {kSecondary3, {(conf).pt, (conf).fractionCpa}}, \ + {kSecondaryOther, {(conf).pt, (conf).fractionCpa}}, template auto makeCascadeHistSpecMap(const T& confBinningAnalysis) @@ -264,7 +268,8 @@ auto makeCascadeMcHistSpecMap(const T& confBinningAnalysis) { return std::map>{ CASCADE_HIST_ANALYSIS_MAP(confBinningAnalysis) - CASCADE_HIST_MC_MAP(confBinningAnalysis)}; + CASCADE_HIST_MC_MAP(confBinningAnalysis) + CASCADE_HIST_FITVARS_MC_MAP(confBinningAnalysis)}; } template @@ -282,24 +287,27 @@ std::map> makeCascadeMcQaHistS CASCADE_HIST_ANALYSIS_MAP(confBinningAnalysis) CASCADE_HIST_QA_MAP(confBinningAnalysis, confBinningQa) CASCADE_HIST_MC_MAP(confBinningAnalysis) - CASCADE_HIST_MC_QA_MAP(confBinningAnalysis, confBinningQa)}; + CASCADE_HIST_FITVARS_MC_MAP(confBinningAnalysis)}; } #undef CASCADE_HIST_ANALYSIS_MAP #undef CASCADE_HIST_MC_MAP #undef CASCADE_HIST_QA_MAP -#undef CASCADE_HIST_MC_QA_MAP +#undef CASCADE_HIST_FITVARS_MC_MAP constexpr char PrefixXiQa[] = "XiQA/"; -constexpr char PrefixXi[] = "Xi/"; +constexpr char PrefixXiSe[] = "Xi/SE/"; +constexpr char PrefixXiMe[] = "Xi/ME/"; constexpr char PrefixOmegaQa[] = "OmegaQa/"; -constexpr char PrefixOmega[] = "Omega/"; +constexpr char PrefixOmegaSe[] = "Omega/SE/"; +constexpr char PrefixOmegaMe[] = "Omega/ME/"; constexpr char PrefixLambdaCascade[] = "LambdaCascadeQa/"; constexpr std::string_view AnalysisDir = "Analysis/"; constexpr std::string_view QaDir = "QA/"; constexpr std::string_view McDir = "MC/"; +constexpr std::string_view FitVarsDir = "FitVars/"; /// \class FemtoDreamEventHisto /// \brief Class for histogramming event properties @@ -372,6 +380,9 @@ class CascadeHistManager if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { this->initMc(cascadeSpecs); } + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { + this->template initFitVars(cascadeSpecs); + } } template @@ -441,6 +452,9 @@ class CascadeHistManager if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { this->initMc(cascadeSpecs); } + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { + this->template initFitVars(cascadeSpecs); + } } template @@ -459,6 +473,9 @@ class CascadeHistManager if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { fillQa(cascadeCandidate, bachelor, posDaughter, negDaughter); } + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { + this->fillFitVars(cascadeCandidate); + } } template @@ -480,6 +497,9 @@ class CascadeHistManager if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { this->template fillMc(cascadeCandidate, col, mcParticles, mcMothers, mcPartonicMothers); } + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { + this->fillFitVars(cascadeCandidate); + } } private: @@ -531,7 +551,6 @@ class CascadeHistManager mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsEta, HistTable), getHistDesc(kPtVsEta, HistTable), getHistType(kPtVsEta, HistTable), {cascadeSpecs.at(kPtVsEta)}); mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsPhi, HistTable), getHistDesc(kPtVsPhi, HistTable), getHistType(kPtVsPhi, HistTable), {cascadeSpecs.at(kPtVsPhi)}); mHistogramRegistry->add(qaDir + getHistNameV2(kPhiVsEta, HistTable), getHistDesc(kPhiVsEta, HistTable), getHistType(kPhiVsEta, HistTable), {cascadeSpecs.at(kPhiVsEta)}); - mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsCosPa, HistTable), getHistDesc(kPtVsCosPa, HistTable), getHistType(kPtVsCosPa, HistTable), {cascadeSpecs.at(kPtVsCosPa)}); mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsPa, HistTable), getHistDesc(kPtVsPa, HistTable), getHistType(kPtVsPa, HistTable), {cascadeSpecs.at(kPtVsPa)}); mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsMassXi, HistTable), getHistDesc(kPtVsMassXi, HistTable), getHistType(kPtVsMassXi, HistTable), {cascadeSpecs.at(kPtVsMassXi)}); @@ -564,24 +583,36 @@ class CascadeHistManager mHistogramRegistry->add(mcDir + getHistNameV2(kPdg, HistTable), getHistDesc(kPdg, HistTable), getHistType(kPdg, HistTable), {V0Specs.at(kPdg)}); mHistogramRegistry->add(mcDir + getHistNameV2(kPdgMother, HistTable), getHistDesc(kPdgMother, HistTable), getHistType(kPdgMother, HistTable), {V0Specs.at(kPdgMother)}); mHistogramRegistry->add(mcDir + getHistNameV2(kPdgPartonicMother, HistTable), getHistDesc(kPdgPartonicMother, HistTable), getHistType(kPdgPartonicMother, HistTable), {V0Specs.at(kPdgPartonicMother)}); + } - if (mPlotOrigins) { - mHistogramRegistry->add(mcDir + getHistNameV2(kNoMcParticle, HistTable), getHistDesc(kNoMcParticle, HistTable), getHistType(kNoMcParticle, HistTable), {V0Specs.at(kNoMcParticle)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kPrimary, HistTable), getHistDesc(kPrimary, HistTable), getHistType(kPrimary, HistTable), {V0Specs.at(kPrimary)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kFromWrongCollision, HistTable), getHistDesc(kFromWrongCollision, HistTable), getHistType(kFromWrongCollision, HistTable), {V0Specs.at(kFromWrongCollision)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kFromMaterial, HistTable), getHistDesc(kFromMaterial, HistTable), getHistType(kFromMaterial, HistTable), {V0Specs.at(kFromMaterial)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kMissidentified, HistTable), getHistDesc(kMissidentified, HistTable), getHistType(kMissidentified, HistTable), {V0Specs.at(kMissidentified)}); + // fraction estimation (CPA); only called when mode carries kFitVars + template + void initFitVars(std::map> const& cascadeSpecs) + { + std::string fitVarsDir = std::string(cascadePrefix) + std::string(FitVarsDir); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kFractionCpa, HistTable), getHistDesc(kFractionCpa, HistTable), getHistType(kFractionCpa, HistTable), {cascadeSpecs.at(kFractionCpa)}); - if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel1) { - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondary1, HistTable), getHistDesc(kSecondary1, HistTable), getHistType(kSecondary1, HistTable), {V0Specs.at(kSecondary1)}); - } - if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel2) { - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondary2, HistTable), getHistDesc(kSecondary2, HistTable), getHistType(kSecondary2, HistTable), {V0Specs.at(kSecondary2)}); - } - if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel3) { - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondary3, HistTable), getHistDesc(kSecondary3, HistTable), getHistType(kSecondary3, HistTable), {V0Specs.at(kSecondary3)}); + // per-origin truth-split fraction-CPA templates; need MC truth, so only booked when the mode + // also carries kMc + if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { + if (mPlotOrigins) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kNoMcParticle, HistTable), getHistDesc(kNoMcParticle, HistTable), getHistType(kNoMcParticle, HistTable), {cascadeSpecs.at(kNoMcParticle)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kPrimary, HistTable), getHistDesc(kPrimary, HistTable), getHistType(kPrimary, HistTable), {cascadeSpecs.at(kPrimary)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kFromWrongCollision, HistTable), getHistDesc(kFromWrongCollision, HistTable), getHistType(kFromWrongCollision, HistTable), {cascadeSpecs.at(kFromWrongCollision)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kFromMaterial, HistTable), getHistDesc(kFromMaterial, HistTable), getHistType(kFromMaterial, HistTable), {cascadeSpecs.at(kFromMaterial)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kMissidentified, HistTable), getHistDesc(kMissidentified, HistTable), getHistType(kMissidentified, HistTable), {cascadeSpecs.at(kMissidentified)}); + + if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel1) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondary1, HistTable), getHistDesc(kSecondary1, HistTable), getHistType(kSecondary1, HistTable), {cascadeSpecs.at(kSecondary1)}); + } + if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel2) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondary2, HistTable), getHistDesc(kSecondary2, HistTable), getHistType(kSecondary2, HistTable), {cascadeSpecs.at(kSecondary2)}); + } + if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel3) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondary3, HistTable), getHistDesc(kSecondary3, HistTable), getHistType(kSecondary3, HistTable), {cascadeSpecs.at(kSecondary3)}); + } + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondaryOther, HistTable), getHistDesc(kSecondaryOther, HistTable), getHistType(kSecondaryOther, HistTable), {cascadeSpecs.at(kSecondaryOther)}); } - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondaryOther, HistTable), getHistDesc(kSecondaryOther, HistTable), getHistType(kSecondaryOther, HistTable), {V0Specs.at(kSecondaryOther)}); } } @@ -596,6 +627,12 @@ class CascadeHistManager mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(AnalysisDir) + HIST(getHistName(kPtVsMass, HistTable)), cascadeCandidate.pt(), cascadeCandidate.mass()); } + template + void fillFitVars(T const& cascadeCandidate) + { + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(FitVarsDir) + HIST(getHistName(kFractionCpa, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); + } + template void fillQa(T1 const& cascadeCandidate, T2 const& bachelor, T3 const& posDau, T4 const& negDau) { @@ -642,7 +679,6 @@ class CascadeHistManager mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kPtVsEta, HistTable)), cascadeCandidate.pt(), cascadeCandidate.eta()); mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kPtVsPhi, HistTable)), cascadeCandidate.pt(), cascadeCandidate.phi()); mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kPhiVsEta, HistTable)), cascadeCandidate.phi(), cascadeCandidate.eta()); - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kPtVsCosPa, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kPtVsPa, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadePa()); mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kPtVsMassXi, HistTable)), cascadeCandidate.pt(), massXi); mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kPtVsMassOmega, HistTable)), cascadeCandidate.pt(), massOmega); @@ -661,9 +697,9 @@ class CascadeHistManager if (!cascadeCandidate.has_fMcParticle()) { mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kPdg, HistTable)), 0); mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kOrigin, HistTable)), static_cast(modes::McOrigin::kNoMcParticle)); - if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { if (mPlotOrigins) { - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kNoMcParticle, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(FitVarsDir) + HIST(getHistName(kNoMcParticle, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); } } return; @@ -706,24 +742,21 @@ class CascadeHistManager mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kPdgPartonicMother, HistTable)), 0); } - if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { if (mPlotOrigins) { if (fromWrongCollision) { - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kFromWrongCollision, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(FitVarsDir) + HIST(getHistName(kFromWrongCollision, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); } else if (isMissidentified) { // if it is, we fill it as such - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kMissidentified, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(FitVarsDir) + HIST(getHistName(kMissidentified, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); } else { // if not, we fill it acccoridng to its origin switch (static_cast(mcParticle.origin())) { case modes::McOrigin::kPhysicalPrimary: - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kPrimary, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); - break; - case modes::McOrigin::kFromWrongCollision: - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kFromWrongCollision, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(FitVarsDir) + HIST(getHistName(kPrimary, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); break; case modes::McOrigin::kFromMaterial: - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kFromMaterial, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(FitVarsDir) + HIST(getHistName(kFromMaterial, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); break; case modes::McOrigin::kFromSecondaryDecay: if (mcParticle.has_fMcMother()) { @@ -731,18 +764,18 @@ class CascadeHistManager int motherPdgCode = std::abs(mother.pdgCode()); // Switch on PDG of the mother if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel1 && motherPdgCode == mPdgCodesSecondaryMother[0]) { - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kSecondary1, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondary1, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); } else if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel2 && motherPdgCode == mPdgCodesSecondaryMother[1]) { - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kSecondary2, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondary2, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); } else if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel3 && motherPdgCode == mPdgCodesSecondaryMother[2]) { - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kSecondary3, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondary3, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); } else { - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kSecondaryOther, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondaryOther, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); } } break; default: - LOG(warn) << "Encounted partilce with unknown origin!"; + LOG(warn) << "Encountered particle with unknown origin!"; break; } } @@ -752,7 +785,10 @@ class CascadeHistManager o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; int mPdgCode = 0; - bool mPlotOrigins = false; + // true by default so the per-origin FitVars templates are booked/filled even where + // enableOptionalHistograms() is never called (e.g. the pair task, which has no QA-binning + // config); the QA task can still opt out via its plotOrigins Configurable + bool mPlotOrigins = true; int mPlotNSecondaries = 0; std::array mPdgCodesSecondaryMother = {0}; bool mPlot2d = false; diff --git a/PWGCF/Femto/Core/charmHadronBuilder.h b/PWGCF/Femto/Core/charmHadronBuilder.h index 62f235da485..feda7ce2d0e 100644 --- a/PWGCF/Femto/Core/charmHadronBuilder.h +++ b/PWGCF/Femto/Core/charmHadronBuilder.h @@ -92,7 +92,7 @@ struct ConfD0Selection : o2::framework::ConfigurableGroup { o2::framework::Configurable ptMin{"ptMin", 1.f, "Minimum pT"}; o2::framework::Configurable ptMax{"ptMax", 3.f, "Maximum pT"}; // acceptance is applied as a rapidity cut in the builder; the eta/phi windows - // are kept open and exist only to satisfy macro + // are kept open and exist only to satisfy the partition macro o2::framework::Configurable etaMin{"etaMin", -0.8f, "Minimum eta"}; o2::framework::Configurable etaMax{"etaMax", 0.8f, "Maximum eta"}; o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; @@ -177,6 +177,7 @@ enum CharmHadronFilters { kEtaMax, kPhiMin, kPhiMax, + kUseYCut, kYMin, kYMax, kMassMin, @@ -198,6 +199,7 @@ const std::unordered_map charmHadronFilterNames {kEtaMax, "Maximum eta"}, {kPhiMin, "Minimum phi"}, {kPhiMax, "Maximum phi"}, + {kUseYCut, "Use rapidity cut instead of eta cut"}, {kYMin, "Minimum rapidity"}, {kYMax, "Maximum rapidity"}, {kMassMin, "Minimum invariant mass"}, @@ -266,6 +268,7 @@ class CharmHadronSelection : public baseselection::BaseSelection(mUseYCut)}, {charmHadronFilterNames.at(kYMin), mYMin}, {charmHadronFilterNames.at(kYMax), mYMax}, {charmHadronFilterNames.at(kMassMin), mMassMin}, @@ -286,7 +289,7 @@ class CharmHadronSelection : public baseselection::BaseSelectionevaluateObservable(kImpactParameterProngSqSumMin, candidate.impactParameterProngSqSum()); } else { this->evaluateObservable(kImpactParameterProductMax, candidate.impactParameter0() * candidate.impactParameter1()); - this->evaluateObservable(kCosThetaStarMax, mHfHelper.cosThetaStarD0(candidate)); + this->evaluateObservable(kCosThetaStarMax, HfHelper::cosThetaStarD0(candidate)); } this->template assembleBitmask(); } @@ -356,9 +359,9 @@ class CharmHadronSelection : public baseselection::BaseSelection()) { - y = mHfHelper.yLc(candidate); + y = HfHelper::yLc(candidate); } else { - y = mHfHelper.yD0(candidate); + y = HfHelper::yD0(candidate); } p = y > mYMin; this->template fillFilter(kYMin, p); @@ -384,7 +387,6 @@ class CharmHadronSelection : public baseselection::BaseSelection(collisionProducts, col); int64_t const protonDauIndex = trackBuilder.template getDaughterIndex(protonProng, trackProducts, collisionBuilder); @@ -591,8 +593,8 @@ class CharmHadronBuilder // remap the prongs onto the accepted hypothesis so that the proton is always first auto const& protonProng = isPKPi ? prong0 : prong2; auto const& pionProng = isPKPi ? prong2 : prong0; - float const mass = isPKPi ? mHfHelper.invMassLcToPKPi(candidate) : mHfHelper.invMassLcToPiKP(candidate); - float const massCompeting = isPKPi ? mHfHelper.invMassLcToPiKP(candidate) : mHfHelper.invMassLcToPKPi(candidate); + float const mass = isPKPi ? HfHelper::invMassLcToPKPi(candidate) : HfHelper::invMassLcToPiKP(candidate); + float const massCompeting = isPKPi ? HfHelper::invMassLcToPiKP(candidate) : HfHelper::invMassLcToPKPi(candidate); collisionBuilder.template fillMcCollision(collisionProducts, col, mcCols, mcProducts, mcBuilder); @@ -631,9 +633,9 @@ class CharmHadronBuilder int64_t negDauIndex = trackBuilder.template getDaughterIndex(prong1, trackProducts, collisionBuilder); if constexpr (modes::isEqual(hadronType, modes::CharmHadron::kD0)) { - this->fillD0Tables(collisionProducts, charmHadronProducts, candidate, candidate.pt(), mHfHelper.invMassD0ToPiK(candidate), posDauIndex, negDauIndex); + this->fillD0Tables(collisionProducts, charmHadronProducts, candidate, candidate.pt(), HfHelper::invMassD0ToPiK(candidate), posDauIndex, negDauIndex); } else { - this->fillD0Tables(collisionProducts, charmHadronProducts, candidate, -candidate.pt(), mHfHelper.invMassD0barToKPi(candidate), posDauIndex, negDauIndex); + this->fillD0Tables(collisionProducts, charmHadronProducts, candidate, -candidate.pt(), HfHelper::invMassD0barToKPi(candidate), posDauIndex, negDauIndex); } } @@ -680,9 +682,9 @@ class CharmHadronBuilder int64_t negDauIndex = trackBuilder.template getDaughterIndex(prong1, trackProducts, mcCols, collisionBuilder, mcParticles, mcBuilder, mcProducts); if constexpr (modes::isEqual(hadronType, modes::CharmHadron::kD0)) { - this->fillD0Tables(collisionProducts, charmHadronProducts, candidate, candidate.pt(), mHfHelper.invMassD0ToPiK(candidate), posDauIndex, negDauIndex); + this->fillD0Tables(collisionProducts, charmHadronProducts, candidate, candidate.pt(), HfHelper::invMassD0ToPiK(candidate), posDauIndex, negDauIndex); } else { - this->fillD0Tables(collisionProducts, charmHadronProducts, candidate, -candidate.pt(), mHfHelper.invMassD0barToKPi(candidate), posDauIndex, negDauIndex); + this->fillD0Tables(collisionProducts, charmHadronProducts, candidate, -candidate.pt(), HfHelper::invMassD0barToKPi(candidate), posDauIndex, negDauIndex); } mcBuilder.template fillMcD0WithLabel(candidate, tracks, mcParticles, mcCols, mcProducts); } @@ -708,7 +710,6 @@ class CharmHadronBuilder private: CharmHadronSelection mCharmHadronSelection; - HfHelper mHfHelper; bool mProduceD0s = false; bool mProduceD0Masks = false; diff --git a/PWGCF/Femto/Core/charmHadronHistManager.h b/PWGCF/Femto/Core/charmHadronHistManager.h index f5d81d93f5b..a7f78047ac2 100644 --- a/PWGCF/Femto/Core/charmHadronHistManager.h +++ b/PWGCF/Femto/Core/charmHadronHistManager.h @@ -88,15 +88,15 @@ enum CharmHadronHist { }; // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define CHARMHADRON_DEFAULT_BINNING(defaultMassMin, defaultMassMax) \ - o2::framework::ConfigurableAxis pt{"pt", {{600, 0, 6}}, "Pt"}; \ - o2::framework::ConfigurableAxis eta{"eta", {{300, -1.5, 1.5}}, "Eta"}; \ - o2::framework::ConfigurableAxis phi{"phi", {{720, 0, 1.f * o2::constants::math::TwoPI}}, "Phi"}; \ - o2::framework::ConfigurableAxis mass{"mass", {{200, (defaultMassMin), (defaultMassMax)}}, "Mass"}; \ - o2::framework::ConfigurableAxis sign{"sign", {{3, -1.5, 1.5}}, "Sign"}; \ - o2::framework::ConfigurableAxis charmHadrons{"charmHadrons", {{8001, -4000.5, 4000.5}}, "MC ONLY: CharmHadrons codes of reconstructed D0s"}; \ - o2::framework::ConfigurableAxis pt2d{"pt2d", {{240, 0, 6}}, "Pt for 2D QA"}; \ - o2::framework::ConfigurableAxis eta2d{"eta2d", {{200, -1.5, 1.5}}, "Eta for 2D QA"}; \ +#define CHARMHADRON_DEFAULT_BINNING(defaultMassMin, defaultMassMax) \ + o2::framework::ConfigurableAxis pt{"pt", {{600, 0, 6}}, "Pt"}; \ + o2::framework::ConfigurableAxis eta{"eta", {{300, -1.5, 1.5}}, "Eta"}; \ + o2::framework::ConfigurableAxis phi{"phi", {{720, 0, 1.f * o2::constants::math::TwoPI}}, "Phi"}; \ + o2::framework::ConfigurableAxis mass{"mass", {{200, (defaultMassMin), (defaultMassMax)}}, "Mass"}; \ + o2::framework::ConfigurableAxis sign{"sign", {{3, -1.5, 1.5}}, "Sign"}; \ + o2::framework::ConfigurableAxis charmHadrons{"charmHadrons", {{8001, -4000.5, 4000.5}}, "MC ONLY: PDG codes of reconstructed charm hadrons"}; \ + o2::framework::ConfigurableAxis pt2d{"pt2d", {{240, 0, 6}}, "Pt for 2D QA"}; \ + o2::framework::ConfigurableAxis eta2d{"eta2d", {{200, -1.5, 1.5}}, "Eta for 2D QA"}; \ o2::framework::ConfigurableAxis phi2d{"phi2d", {{200, 0, 1.f * o2::constants::math::TwoPI}}, "Phi for 2D QA"}; template @@ -128,7 +128,7 @@ struct ConfCharmHadronQaBinning : o2::framework::ConfigurableGroup { o2::framework::ConfigurableAxis massD0{"massD0", {{200, 1.7, 2.0}}, "Mass of candidates selected as D0"}; o2::framework::ConfigurableAxis massD0bar{"massD0bar", {{200, 1.7, 2.0}}, "Mass of candidates selected as D0bar"}; o2::framework::ConfigurableAxis mlScore{"mlScore", {{100, 0.f, 1.f}}, "BDT ML score (bkg/prompt/non-prompt)"}; - o2::framework::ConfigurableAxis cpa{"cpa", {{100, 0.9f, 1.f}}, "Cosine of pointing angle"}; + o2::framework::ConfigurableAxis cpa{"cpa", {{100, 0.95f, 1.f}}, "Cosine of pointing angle"}; o2::framework::ConfigurableAxis decayLength{"decayLength", {{200, 0.f, 0.2f}}, "Decay length (cm)"}; o2::framework::ConfigurableAxis impactParameterProduct{"impactParameterProduct", {{200, -0.001f, 0.001f}}, "Product of daughter impact parameters (cm^2)"}; o2::framework::ConfigurableAxis cosThetaStar{"cosThetaStar", {{100, -1.f, 1.f}}, "Cosine of decay angle in D0 rest frame"}; @@ -144,7 +144,7 @@ struct ConfLcQaBinning : o2::framework::ConfigurableGroup { o2::framework::ConfigurableAxis massLcToPKPi{"massLcToPKPi", {{200, 2.15, 2.42}}, "Mass under the p K pi hypothesis"}; o2::framework::ConfigurableAxis massLcToPiKP{"massLcToPiKP", {{200, 2.15, 2.42}}, "Mass under the pi K p hypothesis"}; o2::framework::ConfigurableAxis mlScore{"mlScore", {{100, 0.f, 1.f}}, "BDT ML score (bkg/prompt/non-prompt)"}; - o2::framework::ConfigurableAxis cpa{"cpa", {{100, 0.9f, 1.f}}, "Cosine of pointing angle"}; + o2::framework::ConfigurableAxis cpa{"cpa", {{100, 0.95f, 1.f}}, "Cosine of pointing angle"}; o2::framework::ConfigurableAxis decayLength{"decayLength", {{200, 0.f, 0.2f}}, "Decay length (cm)"}; o2::framework::ConfigurableAxis chi2Pca{"chi2Pca", {{200, 0.f, 0.1f}}, "Sum of distances of the secondary vertex to its prongs (cm)"}; o2::framework::ConfigurableAxis impactParameterProngSqSum{"impactParameterProngSqSum", {{200, 0.f, 0.01f}}, "Sum of squared prong impact parameters (cm^2)"}; @@ -291,10 +291,15 @@ auto makeLcMcQaHistSpecMap(const T1& confBinningAnalysis, const T2& confBinningQ #undef LC_HIST_QA_MAP // prefixes for the output directories in the histogram registry -constexpr char PrefixD01[] = "D01/"; -constexpr char PrefixD02[] = "D02/"; +// same-event / mixed-event siblings: single-candidate histograms for a given slot, split by +// whether the candidate came from a same-event or mixed-event pair +constexpr char PrefixD01Se[] = "D01/SE/"; +constexpr char PrefixD02Se[] = "D02/SE/"; +constexpr char PrefixD01Me[] = "D01/ME/"; +constexpr char PrefixD02Me[] = "D02/ME/"; constexpr char PrefixD0Qa[] = "D0QA/"; -constexpr char PrefixLc1[] = "Lc1/"; +constexpr char PrefixLc1Se[] = "Lc1/SE/"; +constexpr char PrefixLc1Me[] = "Lc1/ME/"; constexpr char PrefixLcQa[] = "LcQA/"; constexpr std::string_view AnalysisDir = "Analysis/"; constexpr std::string_view McDir = "MC/"; diff --git a/PWGCF/Femto/Core/closePairRejection.h b/PWGCF/Femto/Core/closePairRejection.h index a8db746c394..d7d8b320171 100644 --- a/PWGCF/Femto/Core/closePairRejection.h +++ b/PWGCF/Femto/Core/closePairRejection.h @@ -17,6 +17,7 @@ #define PWGCF_FEMTO_CORE_CLOSEPAIRREJECTION_H_ #include "PWGCF/Femto/Core/histManager.h" +#include "PWGCF/Femto/Core/modes.h" #include "Common/Core/RecoDecay.h" @@ -61,16 +62,18 @@ enum CprHist { // template configurable group for Cpr // DefaultCutAverage: default of cutAverage (e.g. false for pairs of generated particles, where no detector effects are present) -template +// DefaultKinematicVariable: default of kinematicVariable (e.g. Q3 for triplets, kstar for pairs) +template struct ConfCpr : o2::framework::ConfigurableGroup { std::string prefix = std::string(Prefix); o2::framework::Configurable cutAverage{"cutAverage", DefaultCutAverage, "Apply CPR if the average deta-dphistar is below the configured values"}; o2::framework::Configurable cutAnyRadius{"cutAnyRadius", false, "Apply CPR if the deta-dphistar is below the configured values at any radius"}; - o2::framework::Configurable cutElipsoidal{"cutElipsoidal", true, "If true, apply CPR as episoidal cut. If false use rectangluar cut."}; + o2::framework::Configurable cutElipsoidal{"cutElipsoidal", true, "If true, apply CPR as ellipsoidal cut. If false use rectangular cut."}; o2::framework::Configurable plotAllRadii{"plotAllRadii", true, "Plot deta-dphi distribution at all radii"}; o2::framework::Configurable plotAverage{"plotAverage", true, "Plot average deta dphi distribution"}; - o2::framework::Configurable plotAngularCorrelation{"plotAngularCorrelation", false, "Plot angular correlation of particles (eta1 vs eta2 & phi1 vs phi2"}; + o2::framework::Configurable plotAngularCorrelation{"plotAngularCorrelation", false, "Plot angular correlation of particles (eta1 vs eta2 & phi1 vs phi2)"}; o2::framework::Configurable plotKinematic{"plotKinematic", true, "Plot kinematic (kstar/Q3) distribution of blocked pairs/triplets"}; + o2::framework::Configurable kinematicVariable{"kinematicVariable", static_cast(DefaultKinematicVariable), "Type of kinematic variable (0->kstar, 1->kT, 2->mT, 3->Q3) reported by the kinematic plot"}; o2::framework::Configurable detaMax{"detaMax", 0.01f, "Maximium deta"}; o2::framework::Configurable dphistarMax{"dphistarMax", 0.01f, "Maximum dphistar"}; o2::framework::Configurable detaCenter{"detaCenter", 0.f, "Center of deta cut"}; @@ -95,6 +98,7 @@ constexpr const char PrefixCprTrackLcPion[] = "CprTrackLcPion"; constexpr const char PrefixCprD0DaughterD0DaughterPos[] = "CprD0DaughterD0DaughterPos"; constexpr const char PrefixCprD0DaughterD0DaughterNeg[] = "CprD0DaughterD0DaughterNeg"; constexpr const char PrefixCprTrackResonanceDaughter[] = "CprTrackResonanceDaughter"; +constexpr const char PrefixCprTrackTwoPhotonResonanceDaughter[] = "CprTrackTwoPhotonResonanceDaughter"; constexpr const char PrefixCprTrackKinkDaughter[] = "CprTrackKinkDaughter"; constexpr const char PrefixCprV0DaughterV0DaughterPos[] = "CprV0DaughterV0DaughterPos"; constexpr const char PrefixCprV0DaughterV0DaughterNeg[] = "CprV0DaughterV0DaughterNeg"; @@ -113,6 +117,8 @@ using ConfCprTrackLcPion = ConfCpr; using ConfCprD0DaugherD0DaughterPos = ConfCpr; using ConfCprD0DaugherD0DaughterNeg = ConfCpr; using ConfCprTrackResonanceDaughter = ConfCpr; +// no cut by default: CPR is a no-op for two-photon resonances (see ClosePairRejectionTrackTwoPhotonResonance) +using ConfCprTrackTwoPhotonResonanceDaughter = ConfCpr; using ConfCprTrackKinkDaughter = ConfCpr; using ConfCprV0DaugherV0DaughterPos = ConfCpr; using ConfCprV0DaugherV0DaughterNeg = ConfCpr; @@ -152,6 +158,8 @@ constexpr char PrefixV0TwoTrackResonancePosMe[] = "CPR_V0Resonance_PosDau/ME/"; constexpr char PrefixV0TwoTrackResonanceNegMe[] = "CPR_V0Resonance_NegDau/ME/"; constexpr char PrefixTrackTwoTrackResonanceSe[] = "CPR_TrackResonanceDau/SE/"; constexpr char PrefixTrackTwoTrackResonanceMe[] = "CPR_TrackResonanceDau/ME/"; +constexpr char PrefixTrackTwoPhotonResonanceSe[] = "CPR_TrackTwoPhotonResonanceDau/SE/"; +constexpr char PrefixTrackTwoPhotonResonanceMe[] = "CPR_TrackTwoPhotonResonanceDau/ME/"; constexpr char PrefixTrackCascadeBachelorSe[] = "CPR_TrackCascadeBachelor/SE/"; constexpr char PrefixTrackCascadeBachelorMe[] = "CPR_TrackCascadeBachelor/ME/"; constexpr char PrefixTrackKinkSe[] = "CPR_TrackKink/SE/"; @@ -172,7 +180,7 @@ constexpr std::array, kCprHistogramLast> HistTabl {kRadius6, o2::framework::HistType::kTH2F, "hRadius6", "Radius 6: #Delta #eta vs #Delta #phi*; #Delta #eta; #Delta #phi*"}, {kRadius7, o2::framework::HistType::kTH2F, "hRadius7", "Radius 7: #Delta #eta vs #Delta #phi*; #Delta #eta; #Delta #phi*"}, {kRadius8, o2::framework::HistType::kTH2F, "hRadius8", "Radius 8: #Delta #eta vs #Delta #phi*; #Delta #eta; #Delta #phi*"}, - {kKinematic, o2::framework::HistType::kTH1F, "hKinematic", "Kinematic distribution of blocked pairs; kinematic Variable (GeV/c#it{c}); Entries"}, + {kKinematic, o2::framework::HistType::kTH1F, "hKinematic", "Kinematic distribution of blocked pairs; kinematic Variable (GeV/#it{c}); Entries"}, {kPhi1VsPhi2, o2::framework::HistType::kTH2F, "hPhi1vsPhi2", "#phi_{1} vs #phi_{2}; #phi_{1}; #phi_{2}"}, {kEta1VsEta2, o2::framework::HistType::kTH2F, "hEta1VsEta2", "#eta_{1} vs #eta_{2}; #eta_{1}; #eta_{2}"}}}; @@ -236,6 +244,7 @@ class CloseTrackRejection mPlotAngularCorrelation = confCpr.plotAngularCorrelation.value; mPlotKinematic = confCpr.plotKinematic.value; + mKinematicVariable = static_cast(confCpr.kinematicVariable.value); if (confCpr.seed.value >= 0) { uint64_t randomSeed = 0; @@ -254,7 +263,8 @@ class CloseTrackRejection mHistogramRegistry = registry; if (mPlotKinematic) { - mHistogramRegistry->add(std::string(prefix) + getHistNameV2(kKinematic, HistTable), getHistDesc(kKinematic, HistTable), getHistType(kKinematic, HistTable), {specs.at(kKinematic)}); + std::string kinematicDesc = histmanager::replaceFirstAxisTitle(getHistDesc(kKinematic, HistTable), modes::kinematicVariableToAxisTitle(mKinematicVariable)); + mHistogramRegistry->add(std::string(prefix) + getHistNameV2(kKinematic, HistTable), kinematicDesc.c_str(), getHistType(kKinematic, HistTable), {specs.at(kKinematic)}); } if (mPlotAverage) { @@ -296,9 +306,9 @@ class CloseTrackRejection bool isClose = mUseEpisoidalCut ? isClosePairEpisoidalCut() : isClosePairRectengularCut(); if (isClose) { - fillBlocked(pairHistManager.getKinematic()); + fillBlocked(pairHistManager.getKinematic(mKinematicVariable)); } else { - fillPassed(pairHistManager.getKinematic()); + fillPassed(pairHistManager.getKinematic(mKinematicVariable)); } return isClose; @@ -320,32 +330,40 @@ class CloseTrackRejection swapTracks = (mSwapDist(mRng) == 1); } - auto t1 = swapTracks ? track2 : track1; - auto t2 = swapTracks ? track1 : track2; + // get kinematics of the particles + // if configured, randomly swap particle 1 and particle 2 + float eta1 = swapTracks ? track2.eta() : track1.eta(); + float eta2 = swapTracks ? track1.eta() : track2.eta(); + float phi1 = swapTracks ? track2.phi() : track1.phi(); + float phi2 = swapTracks ? track1.phi() : track2.phi(); + float signedPt1 = swapTracks ? track2.signedPt() : track1.signedPt(); + float signedPt2 = swapTracks ? track1.signedPt() : track2.signedPt(); + int chargeAbs1 = swapTracks ? mChargeAbsTrack2 : mChargeAbsTrack1; + int chargeAbs2 = swapTracks ? mChargeAbsTrack1 : mChargeAbsTrack2; - mDeta = t1.eta() - t2.eta(); + mDeta = eta1 - eta2; for (size_t i = 0; i < TpcRadii.size(); i++) { - auto phistar1 = phistar(mMagField, TpcRadii.at(i), mChargeAbsTrack1 * t1.signedPt(), t1.phi()); - auto phistar2 = phistar(mMagField, TpcRadii.at(i), mChargeAbsTrack2 * t2.signedPt(), t2.phi()); + auto phistar1 = phistar(mMagField, TpcRadii.at(i), chargeAbs1 * signedPt1, phi1); + auto phistar2 = phistar(mMagField, TpcRadii.at(i), chargeAbs2 * signedPt2, phi2); if (phistar1 && phistar2) { mDphistar.at(i) = RecoDecay::constrainAngle(phistar1.value() - phistar2.value(), -o2::constants::math::PI); // constrain angular difference between -pi and pi mDphistarMask.at(i) = true; count++; } } - // for small momemeta the calculation of phistar might fail, if the particle did not reach one or more of the outer radii + // for small momenta the calculation of phistar might fail, if the particle did not reach one or more of the outer radii if (count > 0) { mAverageDphistar = std::accumulate(mDphistar.begin(), mDphistar.end(), 0.f) / count; // only average values if phistar could be computed } else { - mAverageDphistar = 0.f; // if computation at all radii fail, set it 0 + mAverageDphistar = 0.f; // if computation at all radii failed, set it 0 } if (mPlotAngularCorrelation) { - mPhi1 = t1.phi(); - mPhi2 = t2.phi(); - mEta1 = t1.eta(); - mEta2 = t2.eta(); + mPhi1 = phi1; + mPhi2 = phi2; + mEta1 = eta1; + mEta2 = eta2; } } @@ -480,6 +498,7 @@ class CloseTrackRejection bool mPlotAverage = false; bool mPlotAngularCorrelation = false; bool mPlotKinematic = false; + modes::KinematicVariable mKinematicVariable = modes::KinematicVariable::kKstar; float mKinematicMin = -1.f; float mKinematicMax = -1.f; @@ -518,10 +537,10 @@ template class ClosePairRejectionTrackTrack { public: - template + template void init(o2::framework::HistogramRegistry* registry, std::map> const& specs, - T const& confCpr, + T1 const& confCpr, int absChargeTrack1, int absChargeTrack2) { @@ -584,10 +603,10 @@ template class ClosePairRejectionTrackV0 // can also be used for any particle type that has pos/neg daughters, like resonances { public: - template + template void init(o2::framework::HistogramRegistry* registry, std::map> const& specs, - T const& confCpr, + T1 const& confCpr, int absChargeTrack) { mCtr.init(registry, specs, confCpr, absChargeTrack, 1); @@ -706,10 +725,10 @@ template class ClosePairRejectionTrackKink { public: - template + template void init(o2::framework::HistogramRegistry* registry, std::map> const& specs, - T const& confCpr, + T1 const& confCpr, int absChargeTrack) { mCtr.init(registry, specs, confCpr, absChargeTrack, 1); @@ -731,14 +750,40 @@ class ClosePairRejectionTrackKink CloseTrackRejection mCtr; }; +// PCM photon daughters are, for now, not indexed into the track table (see photonBuilder.h): +// they are TPC-only tracks, so they cannot be confused for a global track the way two V0 +// daughters or a track and a V0 daughter can -- there is no detector-overlap effect to correct +// for. This is a no-op placeholder so Track-TwoPhotonResonance pairs can use the same generic +// pair-building code path. +template +class ClosePairRejectionTrackTwoPhotonResonance +{ + public: + template + void init(o2::framework::HistogramRegistry* /*registry*/, + std::map> const& /*specs*/, + T1 const& /*confCpr*/, + int /*absChargeTrack*/) + { + } + + void setMagField(float /*magField*/) {} + + template + [[nodiscard]] bool isClosePair(T1 const& /*track*/, T2 const& /*resonance*/, T3 const& /*photonTable*/, T4 const& /*pairHistManager*/) + { + return false; + } +}; + template class ClosePairRejectionMcParticleMcParticle { public: - template + template void init(o2::framework::HistogramRegistry* registry, std::map> const& specs, - T const& confCpr) + T1 const& confCpr) { mCtr.init(registry, specs, confCpr, 1, 1); mCtr.setMagField(confCpr.magField.value); diff --git a/PWGCF/Femto/Core/closeTripletRejection.h b/PWGCF/Femto/Core/closeTripletRejection.h index 3a2ad4c6847..a3e2f1b8cc7 100644 --- a/PWGCF/Femto/Core/closeTripletRejection.h +++ b/PWGCF/Femto/Core/closeTripletRejection.h @@ -17,6 +17,7 @@ #define PWGCF_FEMTO_CORE_CLOSETRIPLETREJECTION_H_ #include "PWGCF/Femto/Core/closePairRejection.h" +#include "PWGCF/Femto/Core/modes.h" #include #include @@ -31,11 +32,12 @@ constexpr const char PrefixCtrTrackTrackV0[] = "CtrTrackTrackV0"; constexpr const char PrefixCtrTrackTrackCascade[] = "CtrTrackTrackCascade"; constexpr const char PrefixCtrMcParticleMcParticleMcParticle[] = "CtrMcParticleMcParticleMcParticle"; -using ConfCtrTrackTrackTrack = closepairrejection::ConfCpr; -using ConfCtrTrackTrackV0 = closepairrejection::ConfCpr; -using ConfCtrTrackTrackCascade = closepairrejection::ConfCpr; +// Q3 (not kstar) is the natural kinematic variable for triplets, so it is the default here +using ConfCtrTrackTrackTrack = closepairrejection::ConfCpr; +using ConfCtrTrackTrackV0 = closepairrejection::ConfCpr; +using ConfCtrTrackTrackCascade = closepairrejection::ConfCpr; // generated particles, no cut by default (close triplet rejection corrects a detector effect) -using ConfCtrMcParticleMcParticleMcParticle = closepairrejection::ConfCpr; +using ConfCtrMcParticleMcParticleMcParticle = closepairrejection::ConfCpr; // directory names constexpr char PrefixTrack1Track2Se[] = "CPR_Track1Track2/SE/"; @@ -81,10 +83,10 @@ class CloseTripletRejectionTrackTrackTrack CloseTripletRejectionTrackTrackTrack() = default; ~CloseTripletRejectionTrackTrackTrack() = default; - template + template void init(o2::framework::HistogramRegistry* registry, std::map> const& specs, - T const& confCpr, + T1 const& confCpr, int absChargeTrack1, int absChargeTrack2, int absChargeTrack3) @@ -128,10 +130,10 @@ class CloseTripletRejectionTrackTrackV0 CloseTripletRejectionTrackTrackV0() = default; ~CloseTripletRejectionTrackTrackV0() = default; - template + template void init(o2::framework::HistogramRegistry* registry, std::map> const& specs, - T const& confCpr, + T1 const& confCpr, int absChargeTrack1, int absChargeTrack2) { @@ -225,10 +227,10 @@ class CloseTripletRejectionMcParticleMcParticleMcParticle CloseTripletRejectionMcParticleMcParticleMcParticle() = default; ~CloseTripletRejectionMcParticleMcParticleMcParticle() = default; - template + template void init(o2::framework::HistogramRegistry* registry, std::map> const& specs, - T const& confCpr) + T1 const& confCpr) { mCtr12.init(registry, specs, confCpr); mCtr23.init(registry, specs, confCpr); diff --git a/PWGCF/Femto/Core/collisionBuilder.h b/PWGCF/Femto/Core/collisionBuilder.h index 1c4737bdb09..03bda34b671 100644 --- a/PWGCF/Femto/Core/collisionBuilder.h +++ b/PWGCF/Femto/Core/collisionBuilder.h @@ -57,28 +57,28 @@ struct ConfCollisionFilters : o2::framework::ConfigurableGroup { o2::framework::Configurable sphericityMax{"sphericityMax", 1.f, "Maximum sphericity"}; o2::framework::Configurable magFieldMin{"magFieldMin", -5, "Minimum magnetic field strength (kG)"}; o2::framework::Configurable magFieldMax{"magFieldMax", 5, "Maximum magnetic field strength (kG)"}; - o2::framework::Configurable subGeneratorId{"subGeneratorId", 0, "MC ONLY: If positive, keep 0 = MB, <0 triggered on something"}; + o2::framework::Configurable subGeneratorId{"subGeneratorId", -1, "MC ONLY: Sub-generator ID to keep (0 = MB). Only applied if >= 0; set to -1 (default) to disable and keep all sub-generators"}; }; struct ConfCollisionBits : o2::framework::ConfigurableGroup { std::string prefix = std::string("CollisionBits"); o2::framework::Configurable passThrough{"passThrough", false, "If true, all collisions are passed through. Bits for all enabled (non-zero) selection flags are stored, disabled flags (0) are not."}; - o2::framework::Configurable sel8{"sel8", 1, "Use sel8 (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable rctFlags{"rctFlags", 1, "RCT flags ok, checker configured via CollisionRctFlags (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable noSameBunchPileup{"noSameBunchPileup", 0, "Reject collisions in case of pileup with another collision in the same foundBC (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable isVertexItsTpc{"isVertexItsTpc", 0, "At least one ITS-TPC track found for the vertex (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable isGoodZvtxFt0VsPv{"isGoodZvtxFt0VsPv", 0, "small difference between z-vertex from PV and from FT0 (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable noCollInTimeRangeNarrow{"noCollInTimeRangeNarrow", 0, "no other collisions in specified time range (narrower than Strict)(-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable noCollInTimeRangeStrict{"noCollInTimeRangeStrict", 0, "no other collisions in specified time range strict (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable noCollInTimeRangeStandard{"noCollInTimeRangeStandard", 0, "no other collisions in specified time range with per-collision multiplicity above threshold (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable noCollInRofStrict{"noCollInRofStrict", 0, "no other collisions in this Readout Frame strict (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable noCollInRofStandard{"noCollInRofStandard", 0, "no other collisions in this Readout Frame with per-collision multiplicity above threshold (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable noHighMultCollInPrevRof{"noHighMultCollInPrevRof", 0, "veto an event if FT0C amplitude in previous ITS ROF is above threshold (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable isGoodItsLayer3{"isGoodItsLayer3", 0, "number of inactive chips on ITS layer 3 is below maximum allowed value (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable isGoodItsLayer0123{"isGoodItsLayer0123", 0, "numbers of inactive chips on ITS layers 0-3 are below maximum allowed values (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable isGoodItsLayerAll{"isGoodItsLayerAll", 0, "numbers of inactive chips on all ITS layers are below maximum allowed values (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable> occupancyMin{"occupancyMin", {}, "Minimum occpancy"}; - o2::framework::Configurable> occupancyMax{"occupancyMax", {}, "Maximum occpancy"}; + o2::framework::Configurable sel8{"sel8", 1, "Use sel8 (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable rctFlags{"rctFlags", 1, "RCT flags ok, checker configured via CollisionRctFlags (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable noSameBunchPileup{"noSameBunchPileup", 0, "Reject collisions in case of pileup with another collision in the same foundBC (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable isVertexItsTpc{"isVertexItsTpc", 0, "At least one ITS-TPC track found for the vertex (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable isGoodZvtxFt0VsPv{"isGoodZvtxFt0VsPv", 0, "small difference between z-vertex from PV and from FT0 (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable noCollInTimeRangeNarrow{"noCollInTimeRangeNarrow", 0, "no other collisions in specified time range (narrower than Strict)(-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable noCollInTimeRangeStrict{"noCollInTimeRangeStrict", 0, "no other collisions in specified time range strict (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable noCollInTimeRangeStandard{"noCollInTimeRangeStandard", 0, "no other collisions in specified time range with per-collision multiplicity above threshold (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable noCollInRofStrict{"noCollInRofStrict", 0, "no other collisions in this Readout Frame strict (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable noCollInRofStandard{"noCollInRofStandard", 0, "no other collisions in this Readout Frame with per-collision multiplicity above threshold (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable noHighMultCollInPrevRof{"noHighMultCollInPrevRof", 0, "veto an event if FT0C amplitude in previous ITS ROF is above threshold (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable isGoodItsLayer3{"isGoodItsLayer3", 0, "number of inactive chips on ITS layer 3 is below maximum allowed value (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable isGoodItsLayer0123{"isGoodItsLayer0123", 0, "numbers of inactive chips on ITS layers 0-3 are below maximum allowed values (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable isGoodItsLayerAll{"isGoodItsLayerAll", 0, "numbers of inactive chips on all ITS layers are below maximum allowed values (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable> occupancyMin{"occupancyMin", {}, "Minimum occupancy"}; + o2::framework::Configurable> occupancyMax{"occupancyMax", {}, "Maximum occupancy"}; o2::framework::Configurable> sphericityMin{"sphericityMin", {}, "Minimum sphericity"}; o2::framework::Configurable> sphericityMax{"sphericityMax", {}, "Maximum sphericity"}; o2::framework::Configurable> triggers{"triggers", {}, "List of all triggers to be used"}; @@ -90,7 +90,7 @@ struct ConfCollisionBits : o2::framework::ConfigurableGroup { struct ConfCcdb : o2::framework::ConfigurableGroup { std::string prefix = std::string("ConfCcdb"); o2::framework::Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "URL to ccdb"}; - o2::framework::Configurable grpPath{"grpPath", "GLO/Config/GRPMagField", "Path to GRP object (Run3 -> GLO/Config/GRPMagField, Run2 -> GLO/GRP/GRP"}; + o2::framework::Configurable grpPath{"grpPath", "GLO/Config/GRPMagField", "Path to GRP object (Run3 -> GLO/Config/GRPMagField, Run2 -> GLO/GRP/GRP)"}; o2::framework::Configurable triggerPath{"triggerPath", "EventFiltering/Zorro/", "CCDB path for trigger information"}; o2::framework::Configurable magFieldForced{"magFieldForced", 0, "Force value for magnetic field (kG). This will skip calls to the ccdb. Deactivate by setting value to 0"}; }; @@ -118,7 +118,7 @@ struct ConfCollisionSelection : o2::framework::ConfigurableGroup { /// enum for all collision selections enum CollisionSels { - // collsion selection flags + // collision selection flags kSel8, ///< Sel8 kRctFlags, ///< RCT flags ok kNoSameBunchPileUp, ///< Reject collisions in case of pileup with another collision in the same foundBC @@ -153,10 +153,10 @@ const std::unordered_map collisionSelectionNames = { {kIsGoodZvtxFt0VsPv, "Is good zvtx FT0 vs PV"}, {kNoCollInTimeRangeNarrow, "No collision in time range narrow"}, {kNoCollInTimeRangeStrict, "No collision in time range strict"}, - {kNoCollInTimeRangeStandard, "No collission in time range standard"}, - {kNoCollInRofStrict, "No collsion in ROF strict"}, + {kNoCollInTimeRangeStandard, "No collision in time range standard"}, + {kNoCollInRofStrict, "No collision in ROF strict"}, {kNoCollInRofStandard, "No collision in ROF standard"}, - {kNoHighMultCollInPrevRof, "No high mult collsions in previous ROF"}, + {kNoHighMultCollInPrevRof, "No high mult collisions in previous ROF"}, {kIsGoodItsLayer3, "Is good ITS layer 3"}, {kIsGoodItsLayer0123, "Is good ITS layer 0-3"}, {kIsGoodItsLayerAll, "Is good ITS layer all"}, @@ -262,6 +262,7 @@ class CollisionSelection : public baseselection::BaseSelectionaddSelection(kSel8, collisionSelectionNames.at(kSel8), config.sel8.value); this->addSelection(kRctFlags, collisionSelectionNames.at(kRctFlags), config.rctFlags.value); + this->addComments(kRctFlags, "label = " + confRct.label.value + "; useZdc = " + std::to_string(confRct.useZdc.value) + "; treatLimitedAcceptanceAsBad = " + std::to_string(confRct.treatLimitedAcceptanceAsBad.value)); this->addSelection(kNoSameBunchPileUp, collisionSelectionNames.at(kNoSameBunchPileUp), config.noSameBunchPileup.value); this->addSelection(kIsVertexItsTpc, collisionSelectionNames.at(kIsVertexItsTpc), config.isVertexItsTpc.value); this->addSelection(kIsGoodZvtxFt0VsPv, collisionSelectionNames.at(kIsGoodZvtxFt0VsPv), config.isGoodZvtxFt0VsPv.value); @@ -333,7 +334,7 @@ class CollisionSelection : public baseselection::BaseSelection - void setSphericity(T tracks) + void setSphericity(T const& tracks) { mSphericity = computeSphericity(tracks); } @@ -462,7 +463,7 @@ class CollisionSelection : public baseselection::BaseSelectionreset(); - // casting bool to float gurantees false -> 0 and true -> 1 + // casting bool to float guarantees false -> 0 and true -> 1 // and we check for equality to 1, so evaluation succeeds if the selection bit is true this->evaluateObservable(kSel8, static_cast(col.sel8())); this->evaluateObservable(kNoSameBunchPileUp, static_cast(col.selection_bit(o2::aod::evsel::kNoSameBunchPileup))); diff --git a/PWGCF/Femto/Core/collisionHistManager.h b/PWGCF/Femto/Core/collisionHistManager.h index f233455fbbe..2c77591e0c4 100644 --- a/PWGCF/Femto/Core/collisionHistManager.h +++ b/PWGCF/Femto/Core/collisionHistManager.h @@ -87,7 +87,7 @@ constexpr std::array, kColHistLast> HistTable = { {kCentVsMult, o2::framework::HistType::kTH2F, "hCentVsMult", "Centrality vs Multiplicity; Centrality (%); Multiplicity"}, {kMultVsSphericity, o2::framework::HistType::kTH2F, "hMultVsSphericity", "Multiplicity vs Sphericity; Multiplicity; Sphericity"}, {kCentVsSphericity, o2::framework::HistType::kTH2F, "hCentVsSphericity", "Centrality vs Sphericity; Centrality (%); Sphericity"}, - {kFT0AvsFT0C, o2::framework::HistType::kTH2F, "hFT0AvsFT0C", "FT0A centrality vs FT0C centrality; Centrality_{FT0A} (%); Centrality_{FT0C}"}, + {kFT0AvsFT0C, o2::framework::HistType::kTH2F, "hFT0AvsFT0C", "FT0A centrality vs FT0C centrality; Centrality_{FT0A} (%); Centrality_{FT0C} (%)"}, // event shape {kQvector, o2::framework::HistType::kTH1F, "hQvector", "q-vector; q-vector; Entries"}, {kEventPlaneAngle, o2::framework::HistType::kTH1F, "hEventPlaneAngle", "Event Plane angle; #Psi_{EP}; Entries"}, diff --git a/PWGCF/Femto/Core/collisionTagger.h b/PWGCF/Femto/Core/collisionTagger.h new file mode 100644 index 00000000000..4372aadbd15 --- /dev/null +++ b/PWGCF/Femto/Core/collisionTagger.h @@ -0,0 +1,1016 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file collisionTagger.h +/// \brief Shared machinery for the self-configuring collision tagger (TableProducer/femtoProducerCollisionTagger.cxx) +/// and for pair/triplet tasks that want to read back their own assigned tag bit(s). +/// +/// The tagger walks the running workflow's device list once, and for every matching pair/triplet +/// task instance it finds, assigns the next free bit(s) of FColTags::collisionTag to that +/// instance's particle role(s), in discovery order. A consuming task cannot read the tagger's +/// bit assignment back out of it directly: RunningWorkflowInfo only exposes each device's +/// CLI/JSON-resolved option values, a static snapshot taken once at workflow construction -- it is +/// never updated by what another device's init() computes at runtime, and there is no ordering +/// guarantee between different devices' init() calls regardless. So instead, discoverTaggedRoles() +/// is the single canonical implementation of the bit-assignment walk, included and called +/// identically by the tagger (to assign bits and fill FColTags) and by every consuming task (to +/// recompute, from the same static device list, which bit(s) ended up belonging to its own device +/// name). Keeping this walk in one place is what keeps the two sides from silently drifting apart. +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_COLLISIONTAGGER_H_ +#define PWGCF_FEMTO_CORE_COLLISIONTAGGER_H_ + +#include "PWGCF/Femto/Core/dataTypes.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace o2::analysis::femto::collisiontagger +{ + +constexpr int MaxTagBits = 8 * sizeof(datatypes::CollisionTagType); + +// Supporting an arbitrary number of same-type "subwagons" (same task, different cuts, all running +// in one train) rules out reusing Partition for the cut check itself: it is a compile-time +// construct, permanently bound to one specific Configurable object's fields. So instead, each +// matching sibling device's configured cut VALUES are copied into a plain value struct (no +// Configurable, no Partition), one push_back per discovered subwagon, and checked by hand against +// each particle -- the same approach femtoDreamCollisionMasker.cxx uses for its own multi-variant +// support. The check functions below are a direct, faithful port of the corresponding +// MAKE_*_PARTITION macro in Core/partitions.h, just expressed as a runtime check instead of a +// compiled Gandiva expression. +struct TrackSelectionValues { + int chargeAbs = 1; + int chargeSign = 1; + float ptMin = 0.f; + float ptMax = 1e6f; + float etaMin = -1e6f; + float etaMax = 1e6f; + float phiMin = 0.f; + float phiMax = 1.f * o2::constants::math::TwoPI; + datatypes::TrackMaskType maskLowMomentum = 0; + datatypes::TrackMaskType maskHighMomentum = 0; + datatypes::TrackMaskType rejectionMaskLowMomentum = 0; + datatypes::TrackMaskType rejectionMaskHighMomentum = 0; + float pidThres = 1e6f; +}; + +// Despite the name, this shape (sign + pt + eta + phi + mass window + flat mask) is NOT +// V0-specific: MAKE_CASCADE_PARTITION, MAKE_SIGMA_PARTITION, MAKE_SIGMAPLUS_PARTITION, +// MAKE_D0_PARTITION, and MAKE_CHARM3PRONG_PARTITION in Core/partitions.h are all byte-for-byte +// identical to MAKE_LAMBDA_PARTITION, and CascadeMaskType/KinkMaskType/CharmHadronMaskType are +// all, like V0MaskType, uint32_t -- so this one struct (and signedV0Passes()/readV0Selection() +// below) is reused verbatim for Xi/Omega (cascade), Sigma/SigmaPlus (kink), and D0/Lc +// (charm hadron) selections too, not just Lambda. Only SigmaPlusPcm needs its own struct +// (KinkPcmSelectionValues below), for the one extra mlScoreMin field it has beyond this shape. +struct V0SelectionValues { + float ptMin = 0.f; + float ptMax = 1e6f; + float etaMin = -1e6f; + float etaMax = 1e6f; + float phiMin = 0.f; + float phiMax = 1.f * o2::constants::math::TwoPI; + float massMin = 0.f; + float massMax = 1e6f; + datatypes::V0MaskType mask = 0; + int sign = 0; // only used for lambdas; k0shorts carry a dummy value for compatibility +}; + +// one bit = one pair type for one task instance: both sides' selections, read together, since the +// bit means "both are present", not "particle 1 is present" or "particle 2 is present" separately +struct TrackTrackVariant { + TrackSelectionValues sel1; + TrackSelectionValues sel2; + bool sameSpecies = false; + int bit = 0; +}; + +// sel1/sel2 (not trackSel/v0Sel) for the same reason TrackTrackVariant above uses sel1/sel2: so +// tagCrossVariants() below can be written once, generically, for any cross-type pair -- naming +// the fields after what they ARE (track selection, v0 selection) would tie the generic helper's +// code to this one particle-type combination. +struct TrackV0Variant { + TrackSelectionValues sel1; + V0SelectionValues sel2; + int bit = 0; +}; + +// SigmaPlusPcm's selection is V0SelectionValues plus one extra field (mlScoreMin), so it needs +// its own value struct, check function (kinkPcmPasses, below readV0Selection), and variant type +// -- everything else about it (sign/pt/eta/phi/mass/mask) is identical to V0SelectionValues. +struct KinkPcmSelectionValues { + float ptMin = 0.f; + float ptMax = 1e6f; + float etaMin = -1e6f; + float etaMax = 1e6f; + float phiMin = 0.f; + float phiMax = 1.f * o2::constants::math::TwoPI; + float massMin = 0.f; + float massMax = 1e6f; + datatypes::KinkMaskType mask = 0; + int sign = 0; + float mlScoreMin = -1.f; +}; + +struct TrackKinkPcmVariant { + TrackSelectionValues sel1; + KinkPcmSelectionValues sel2; + int bit = 0; +}; + +// Same-type pair where BOTH legs use the V0SelectionValues shape: covers v0-v0 (lambda-lambda, +// k0short-k0short, lambda-k0short) and d0-d0 (same shape, see V0SelectionValues above), checked +// with tagPairVariants() when sel1/sel2 are the same species (sameSpecies-aware) or tagCrossVariants() +// when they are not (e.g. lambda-k0short: sameSpecies is meaningless there and left false/unused). +struct V0V0Variant { + V0SelectionValues sel1; + V0SelectionValues sel2; + bool sameSpecies = false; + int bit = 0; +}; + +// Phi -> K+K-, Rho0 -> pi+pi-: both daughters are the same species, so MAKE_RESONANCE_0_PARTITON +// (Core/partitions.h) only needs one pair of momentum-threshold-gated daughter masks, applied to +// whichever of the two stored columns (maskPosDau/maskNegDau) belongs to the daughter that was +// above/below threshold at build time (posDauHasHighMomentum/negDauHasHighMomentum, read straight +// off the candidate -- these switches are baked in at production, not re-derived here). +struct TwoTrackResonanceSelectionValues { + float ptMin = 0.f; + float ptMax = 1e6f; + float etaMin = -1e6f; + float etaMax = 1e6f; + float phiMin = 0.f; + float phiMax = 1.f * o2::constants::math::TwoPI; + float massMin = 0.f; + float massMax = 1e6f; + datatypes::TrackMaskType dauMaskBelowThres = 0; + datatypes::TrackMaskType dauMaskAboveThres = 0; +}; + +// Kstar0 -> K+pi-, Kstar0bar -> K-pi+: the physical pos/neg daughter's species flips with the +// candidate's charge sign (see MAKE_RESONANCE_1_PARTITON in Core/partitions.h), so the daughter +// masks are named by species (kaon/pion) rather than by charge, and sign picks which hypothesis a +// given candidate's signedPt must match. +struct Kstar0SelectionValues { + int sign = 0; + float ptMin = 0.f; + float ptMax = 1e6f; + float etaMin = -1e6f; + float etaMax = 1e6f; + float phiMin = 0.f; + float phiMax = 1.f * o2::constants::math::TwoPI; + float massMin = 0.f; + float massMax = 1e6f; + datatypes::TrackMaskType kaonDauMaskBelowThres = 0; + datatypes::TrackMaskType kaonDauMaskAboveThres = 0; + datatypes::TrackMaskType pionDauMaskBelowThres = 0; + datatypes::TrackMaskType pionDauMaskAboveThres = 0; +}; + +// Pi0/Eta -> gamma gamma: photon daughters have no momentum-dependent PID switch and no pos/neg +// distinction, so (unlike TwoTrackResonanceSelectionValues above) there is only one flat mask, +// already the AND of both legs' own masks at production time (see femtotwophotonresonances::MaskDau +// in FemtoTables.h) -- same shape as V0SelectionValues/k0shortPasses, just plain pt (no sign) and a +// PhotonMaskType mask instead of V0MaskType. +struct TwoPhotonResonanceSelectionValues { + float ptMin = 0.f; + float ptMax = 1e6f; + float etaMin = -1e6f; + float etaMax = 1e6f; + float phiMin = 0.f; + float phiMax = 1.f * o2::constants::math::TwoPI; + float massMin = 0.f; + float massMax = 1e6f; + datatypes::PhotonMaskType mask = 0; +}; + +// Cross-type pair variants for the three resonance-pairing tasks: track-twoTrackResonance (Phi/Rho0 +// share one shape, Kstar0 needs its own because of the kaon/pion mask swap), track-twoPhotonResonance +// (Pi0/Eta), and v0-twoTrackResonance (lambda/k0short x phi/rho0/kstar0 -- same split as track-side). +struct TrackTwoTrackResonanceVariant { + TrackSelectionValues sel1; + TwoTrackResonanceSelectionValues sel2; + int bit = 0; +}; + +struct TrackKstar0Variant { + TrackSelectionValues sel1; + Kstar0SelectionValues sel2; + int bit = 0; +}; + +struct TrackTwoPhotonResonanceVariant { + TrackSelectionValues sel1; + TwoPhotonResonanceSelectionValues sel2; + int bit = 0; +}; + +struct V0TwoTrackResonanceVariant { + V0SelectionValues sel1; + TwoTrackResonanceSelectionValues sel2; + int bit = 0; +}; + +struct V0Kstar0Variant { + V0SelectionValues sel1; + Kstar0SelectionValues sel2; + int bit = 0; +}; + +// Triplet variants, mirroring the "123 same, 12 same, or all different" pattern +// TripletTrackTrackTrackBuilder/TripletTrackTrackV0Builder/TripletTrackTrackCascadeBuilder use +// (Core/tripletBuilder.h, confMixing.particle123AreSameSpecies / particle12AreSameSpecies -- only +// these two combinations are supported, never e.g. "1&3 same"). Same-type (track-track-track): +// all three roles share one table, so same123 needs 3 distinct entries passing sel1 (sel2/sel3 +// ignored, mirrored onto sel1 at discovery time -- same trick as TrackTrackVariant's sameSpecies +// above), same12 needs 2 distinct passing sel1 plus 1 more passing sel3 (sel2 ignored). +struct TrackTrackTrackVariant { + TrackSelectionValues sel1; + TrackSelectionValues sel2; + TrackSelectionValues sel3; + bool same123 = false; + bool same12 = false; + int bit = 0; +}; + +// Cross-type (track-track-v0, track-track-cascade): roles 1&2 share the track table, role 3 is a +// different table (v0 or cascade, hence V0SelectionValues shape) -- only same12 is possible here +// (no same123 since role 3 is never the same type as roles 1&2). +struct TrackTrackV0Variant { + TrackSelectionValues sel1; + TrackSelectionValues sel2; + V0SelectionValues sel3; + bool same12 = false; + int bit = 0; +}; + +inline TrackSelectionValues readTrackSelection(o2::framework::DeviceSpec const& device, std::string const& prefix) +{ + TrackSelectionValues v; + for (auto const& option : device.options) { + if (option.name == prefix + ".chargeAbs") { + v.chargeAbs = option.defaultValue.get(); + } else if (option.name == prefix + ".chargeSign") { + v.chargeSign = option.defaultValue.get(); + } else if (option.name == prefix + ".ptMin") { + v.ptMin = option.defaultValue.get(); + } else if (option.name == prefix + ".ptMax") { + v.ptMax = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMin") { + v.etaMin = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMax") { + v.etaMax = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMin") { + v.phiMin = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMax") { + v.phiMax = option.defaultValue.get(); + } else if (option.name == prefix + ".maskLowMomentum") { + v.maskLowMomentum = option.defaultValue.get(); + } else if (option.name == prefix + ".maskHighMomentum") { + v.maskHighMomentum = option.defaultValue.get(); + } else if (option.name == prefix + ".rejectionMaskLowMomentum") { + v.rejectionMaskLowMomentum = option.defaultValue.get(); + } else if (option.name == prefix + ".rejectionMaskHighMomentum") { + v.rejectionMaskHighMomentum = option.defaultValue.get(); + } else if (option.name == prefix + ".pidThres") { + v.pidThres = option.defaultValue.get(); + } + } + return v; +} + +inline V0SelectionValues readV0Selection(o2::framework::DeviceSpec const& device, std::string const& prefix) +{ + V0SelectionValues v; + for (auto const& option : device.options) { + if (option.name == prefix + ".ptMin") { + v.ptMin = option.defaultValue.get(); + } else if (option.name == prefix + ".ptMax") { + v.ptMax = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMin") { + v.etaMin = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMax") { + v.etaMax = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMin") { + v.phiMin = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMax") { + v.phiMax = option.defaultValue.get(); + } else if (option.name == prefix + ".massMin") { + v.massMin = option.defaultValue.get(); + } else if (option.name == prefix + ".massMax") { + v.massMax = option.defaultValue.get(); + } else if (option.name == prefix + ".mask") { + v.mask = option.defaultValue.get(); + } else if (option.name == prefix + ".sign") { + v.sign = option.defaultValue.get(); + } + } + return v; +} + +inline KinkPcmSelectionValues readKinkPcmSelection(o2::framework::DeviceSpec const& device, std::string const& prefix) +{ + KinkPcmSelectionValues v; + for (auto const& option : device.options) { + if (option.name == prefix + ".ptMin") { + v.ptMin = option.defaultValue.get(); + } else if (option.name == prefix + ".ptMax") { + v.ptMax = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMin") { + v.etaMin = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMax") { + v.etaMax = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMin") { + v.phiMin = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMax") { + v.phiMax = option.defaultValue.get(); + } else if (option.name == prefix + ".massMin") { + v.massMin = option.defaultValue.get(); + } else if (option.name == prefix + ".massMax") { + v.massMax = option.defaultValue.get(); + } else if (option.name == prefix + ".mask") { + v.mask = option.defaultValue.get(); + } else if (option.name == prefix + ".sign") { + v.sign = option.defaultValue.get(); + } else if (option.name == prefix + ".mlScoreMin") { + v.mlScoreMin = option.defaultValue.get(); + } + } + return v; +} + +inline TwoTrackResonanceSelectionValues readTwoTrackResonanceSelection(o2::framework::DeviceSpec const& device, std::string const& prefix) +{ + TwoTrackResonanceSelectionValues v; + for (auto const& option : device.options) { + if (option.name == prefix + ".ptMin") { + v.ptMin = option.defaultValue.get(); + } else if (option.name == prefix + ".ptMax") { + v.ptMax = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMin") { + v.etaMin = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMax") { + v.etaMax = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMin") { + v.phiMin = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMax") { + v.phiMax = option.defaultValue.get(); + } else if (option.name == prefix + ".massMin") { + v.massMin = option.defaultValue.get(); + } else if (option.name == prefix + ".massMax") { + v.massMax = option.defaultValue.get(); + } else if (option.name == prefix + ".dauMaskBelowThres") { + v.dauMaskBelowThres = option.defaultValue.get(); + } else if (option.name == prefix + ".dauMaskAboveThres") { + v.dauMaskAboveThres = option.defaultValue.get(); + } + } + return v; +} + +inline Kstar0SelectionValues readKstar0Selection(o2::framework::DeviceSpec const& device, std::string const& prefix) +{ + Kstar0SelectionValues v; + for (auto const& option : device.options) { + if (option.name == prefix + ".sign") { + v.sign = option.defaultValue.get(); + } else if (option.name == prefix + ".ptMin") { + v.ptMin = option.defaultValue.get(); + } else if (option.name == prefix + ".ptMax") { + v.ptMax = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMin") { + v.etaMin = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMax") { + v.etaMax = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMin") { + v.phiMin = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMax") { + v.phiMax = option.defaultValue.get(); + } else if (option.name == prefix + ".massMin") { + v.massMin = option.defaultValue.get(); + } else if (option.name == prefix + ".massMax") { + v.massMax = option.defaultValue.get(); + } else if (option.name == prefix + ".kaonDauMaskBelowThres") { + v.kaonDauMaskBelowThres = option.defaultValue.get(); + } else if (option.name == prefix + ".kaonDauMaskAboveThres") { + v.kaonDauMaskAboveThres = option.defaultValue.get(); + } else if (option.name == prefix + ".pionDauMaskBelowThres") { + v.pionDauMaskBelowThres = option.defaultValue.get(); + } else if (option.name == prefix + ".pionDauMaskAboveThres") { + v.pionDauMaskAboveThres = option.defaultValue.get(); + } + } + return v; +} + +inline TwoPhotonResonanceSelectionValues readTwoPhotonResonanceSelection(o2::framework::DeviceSpec const& device, std::string const& prefix) +{ + TwoPhotonResonanceSelectionValues v; + for (auto const& option : device.options) { + if (option.name == prefix + ".ptMin") { + v.ptMin = option.defaultValue.get(); + } else if (option.name == prefix + ".ptMax") { + v.ptMax = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMin") { + v.etaMin = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMax") { + v.etaMax = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMin") { + v.phiMin = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMax") { + v.phiMax = option.defaultValue.get(); + } else if (option.name == prefix + ".massMin") { + v.massMin = option.defaultValue.get(); + } else if (option.name == prefix + ".massMax") { + v.massMax = option.defaultValue.get(); + } else if (option.name == prefix + ".dauMask") { + v.mask = option.defaultValue.get(); + } + } + return v; +} + +// reads the current value of a plain (non-group-prefixed) boolean option on a sibling device, +// e.g. one of its doprocessXXX-backed PROCESS_SWITCH flags +inline bool hasEnabledOption(o2::framework::DeviceSpec const& device, std::string const& name) +{ + for (auto const& option : device.options) { + if (option.name == name) { + return option.defaultValue.get(); + } + } + return false; +} + +// true if ANY enabled option on this device has a name starting with `prefix` -- used instead of +// hasEnabledOption() where a pair/triplet task has several *WithTagging* process functions that +// all consume the same tag bit (processMixedEventWithTagging, processMixedEventWithTaggingAndMass, +// ...AndEventShape, ...AndFitVars, ...): matching the shared prefix means enabling any one of +// those variants correctly triggers bit assignment, instead of only the base one happening to +// have the exact name this check was written against. +inline bool hasAnyEnabledOptionStartingWith(o2::framework::DeviceSpec const& device, std::string const& prefix) +{ + for (auto const& option : device.options) { + if (option.name.starts_with(prefix) && option.defaultValue.get()) { + return true; + } + } + return false; +} + +// mirrors MAKE_TRACK_PARTITION(selection) in Core/partitions.h +template +bool trackPasses(T const& track, TrackSelectionValues const& sel) +{ + if (sel.chargeSign != 0 && ((sel.chargeSign > 0) != (track.signedPt() > 0.f))) { + return false; + } + float absPt = std::fabs(sel.chargeAbs * track.signedPt()); + if (absPt <= sel.ptMin || absPt >= sel.ptMax) { + return false; + } + if (!(track.eta() > sel.etaMin && track.eta() < sel.etaMax)) { + return false; + } + if (!(track.phi() > sel.phiMin && track.phi() < sel.phiMax)) { + return false; + } + float momentum = absPt * std::cosh(track.eta()); + if (momentum <= sel.pidThres) { + return (track.mask() & sel.maskLowMomentum) == sel.maskLowMomentum && + (track.mask() & sel.rejectionMaskLowMomentum) == static_cast(0); + } + return (track.mask() & sel.maskHighMomentum) == sel.maskHighMomentum && + (track.mask() & sel.rejectionMaskHighMomentum) == static_cast(0); +} + +// mirrors MAKE_LAMBDA_PARTITION(selection) in Core/partitions.h -- also mirrors +// MAKE_CASCADE_PARTITION/MAKE_SIGMA_PARTITION/MAKE_SIGMAPLUS_PARTITION/MAKE_D0_PARTITION/ +// MAKE_CHARM3PRONG_PARTITION, which are byte-for-byte identical, so this same function is reused +// as-is for Xi/Omega, Sigma/SigmaPlus, and D0/Lc candidates too (see the comment on +// V0SelectionValues above). +template +bool signedV0Passes(T const& v0, V0SelectionValues const& sel) +{ + if (sel.sign != 0 && ((sel.sign > 0) != (v0.signedPt() > 0.f))) { + return false; + } + float absPt = std::fabs(v0.signedPt()); + if (absPt <= sel.ptMin || absPt >= sel.ptMax) { + return false; + } + if (!(v0.eta() > sel.etaMin && v0.eta() < sel.etaMax)) { + return false; + } + if (!(v0.phi() > sel.phiMin && v0.phi() < sel.phiMax)) { + return false; + } + if (!(v0.mass() > sel.massMin && v0.mass() < sel.massMax)) { + return false; + } + return (v0.mask() & sel.mask) == sel.mask; +} + +// mirrors MAKE_SIGMAPLUSPCM_PARTITION(selection) in Core/partitions.h -- signedV0Passes() above +// plus the one extra mlScoreMin check. +template +bool kinkPcmPasses(T const& kink, KinkPcmSelectionValues const& sel) +{ + if (sel.sign != 0 && ((sel.sign > 0) != (kink.signedPt() > 0.f))) { + return false; + } + float absPt = std::fabs(kink.signedPt()); + if (absPt <= sel.ptMin || absPt >= sel.ptMax) { + return false; + } + if (!(kink.eta() > sel.etaMin && kink.eta() < sel.etaMax)) { + return false; + } + if (!(kink.phi() > sel.phiMin && kink.phi() < sel.phiMax)) { + return false; + } + if (!(kink.mass() > sel.massMin && kink.mass() < sel.massMax)) { + return false; + } + if (!(kink.mlScore() >= sel.mlScoreMin)) { + return false; + } + return (kink.mask() & sel.mask) == sel.mask; +} + +// mirrors MAKE_K0SHORT_PARTITION(selection) in Core/partitions.h +template +bool k0shortPasses(T const& v0, V0SelectionValues const& sel) +{ + if (!(v0.pt() > sel.ptMin && v0.pt() < sel.ptMax)) { + return false; + } + if (!(v0.eta() > sel.etaMin && v0.eta() < sel.etaMax)) { + return false; + } + if (!(v0.phi() > sel.phiMin && v0.phi() < sel.phiMax)) { + return false; + } + if (!(v0.mass() > sel.massMin && v0.mass() < sel.massMax)) { + return false; + } + return (v0.mask() & sel.mask) == sel.mask; +} + +// mirrors MAKE_RESONANCE_0_PARTITON(selection) in Core/partitions.h (Phi, Rho0) +template +bool twoTrackResonancePasses(T const& res, TwoTrackResonanceSelectionValues const& sel) +{ + if (!(res.pt() > sel.ptMin && res.pt() < sel.ptMax)) { + return false; + } + if (!(res.eta() > sel.etaMin && res.eta() < sel.etaMax)) { + return false; + } + if (!(res.phi() > sel.phiMin && res.phi() < sel.phiMax)) { + return false; + } + if (!(res.mass() > sel.massMin && res.mass() < sel.massMax)) { + return false; + } + datatypes::TrackMaskType posMask = res.posDauHasHighMomentum() ? sel.dauMaskAboveThres : sel.dauMaskBelowThres; + if ((res.maskPosDau() & posMask) != posMask) { + return false; + } + datatypes::TrackMaskType negMask = res.negDauHasHighMomentum() ? sel.dauMaskAboveThres : sel.dauMaskBelowThres; + return (res.maskNegDau() & negMask) == negMask; +} + +// mirrors MAKE_RESONANCE_1_PARTITON(selection) in Core/partitions.h (Kstar0/Kstar0bar) -- pos/neg +// daughter <-> kaon/pion resolved from the candidate's own signedPt, same as the real builder. +template +bool kstar0Passes(T const& kstar0, Kstar0SelectionValues const& sel) +{ + if (sel.sign != 0 && ((sel.sign > 0) != (kstar0.signedPt() > 0.f))) { + return false; + } + float absPt = std::fabs(kstar0.signedPt()); + if (absPt <= sel.ptMin || absPt >= sel.ptMax) { + return false; + } + if (!(kstar0.eta() > sel.etaMin && kstar0.eta() < sel.etaMax)) { + return false; + } + if (!(kstar0.phi() > sel.phiMin && kstar0.phi() < sel.phiMax)) { + return false; + } + if (!(kstar0.mass() > sel.massMin && kstar0.mass() < sel.massMax)) { + return false; + } + bool posIsKaon = kstar0.signedPt() > 0.f; + datatypes::TrackMaskType posMaskThres = posIsKaon + ? (kstar0.posDauHasHighMomentum() ? sel.kaonDauMaskAboveThres : sel.kaonDauMaskBelowThres) + : (kstar0.posDauHasHighMomentum() ? sel.pionDauMaskAboveThres : sel.pionDauMaskBelowThres); + if ((kstar0.maskPosDau() & posMaskThres) != posMaskThres) { + return false; + } + datatypes::TrackMaskType negMaskThres = posIsKaon + ? (kstar0.negDauHasHighMomentum() ? sel.pionDauMaskAboveThres : sel.pionDauMaskBelowThres) + : (kstar0.negDauHasHighMomentum() ? sel.kaonDauMaskAboveThres : sel.kaonDauMaskBelowThres); + return (kstar0.maskNegDau() & negMaskThres) == negMaskThres; +} + +// mirrors MAKE_TWOPHOTONRESONANCE_PARTITION(selection) in Core/partitions.h (Pi0, Eta) +template +bool twoPhotonResonancePasses(T const& res, TwoPhotonResonanceSelectionValues const& sel) +{ + if (!(res.pt() > sel.ptMin && res.pt() < sel.ptMax)) { + return false; + } + if (!(res.eta() > sel.etaMin && res.eta() < sel.etaMax)) { + return false; + } + if (!(res.phi() > sel.phiMin && res.phi() < sel.phiMax)) { + return false; + } + if (!(res.mass() > sel.massMin && res.mass() < sel.massMax)) { + return false; + } + return (res.maskDau() & sel.mask) == sel.mask; +} + +using CollisionTagBitset = std::bitset; + +// true if at least `needed` entries in `range` satisfy `passes(entry, sel)`. The one primitive +// every per-collision tagging check below is built from; a new particle type only ever needs a +// new `passes` function (trackPasses/signedV0Passes/k0shortPasses above are examples), never a new +// copy of this loop. +template +inline bool hasAtLeast(Range const& range, Selection const& sel, PassesFn const& passes, int needed = 1) +{ + int count = 0; + for (auto const& entry : range) { + if (passes(entry, sel) && ++count >= needed) { + return true; + } + } + return false; +} + +// Sets tag.set(v.bit) for every variant where `range` has what it takes to form a SAME-TYPE pair +// (both v.sel1 and v.sel2 satisfied -- or, for a same-species pair, 2 distinct entries passing +// the one shared selection v.sel2). Reusable for any future same-type pair (V0V0, D0D0, ...) -- +// just a different `variants`/`range`/`passes` combination, the counting logic doesn't change. +template +inline void tagPairVariants(CollisionTagBitset& tag, std::vector const& variants, Range const& range, PassesFn const& passes) +{ + for (auto const& v : variants) { + bool ok1 = hasAtLeast(range, v.sel1, passes, 1); + bool ok2 = hasAtLeast(range, v.sel2, passes, v.sameSpecies ? 2 : 1); + if (ok1 && ok2) { + tag.set(v.bit); + } + } +} + +// Sets tag.set(v.bit) for every variant where range1 has what it takes for role 1 (v.sel1, +// checked with passes1) AND range2 has what it takes for role 2 (v.sel2, checked with passes2). +// Reusable for any future cross-type pair (track-cascade, track-kink, track-D0, ...) -- just a +// different `variants`/ranges/passes combination. +template +inline void tagCrossVariants(CollisionTagBitset& tag, std::vector const& variants, Range1 const& range1, PassesFn1 const& passes1, Range2 const& range2, PassesFn2 const& passes2) +{ + for (auto const& v : variants) { + if (hasAtLeast(range1, v.sel1, passes1, 1) && hasAtLeast(range2, v.sel2, passes2, 1)) { + tag.set(v.bit); + } + } +} + +// One-call convenience for the common case of a single tagPairVariants() check immediately +// followed by producing the tag row -- covers any task type whose process function only ever +// needs one same-type-pair check per collision (e.g. processTrackTrack). +template +inline void tagAndProduce(Produces& producedTags, std::vector const& variants, Range const& range, PassesFn const& passes) +{ + CollisionTagBitset tag; + tagPairVariants(tag, variants, range, passes); + producedTags(static_cast(tag.to_ullong())); +} + +// Sets tag.set(v.bit) for every variant where `range` has what it takes to form a SAME-TYPE +// triplet (track-track-track): same123 needs 3 distinct entries passing sel1; same12 needs 2 +// distinct passing sel1 plus 1 more passing sel3; otherwise sel1/sel2/sel3 each need their own +// independent match. Mirrors TripletTrackTrackTrackBuilder exactly (see TrackTrackTrackVariant). +template +inline void tagTripletVariants(CollisionTagBitset& tag, std::vector const& variants, Range const& range, PassesFn const& passes) +{ + for (auto const& v : variants) { + bool ok = false; + if (v.same123) { + ok = hasAtLeast(range, v.sel1, passes, 3); + } else if (v.same12) { + ok = hasAtLeast(range, v.sel1, passes, 2) && hasAtLeast(range, v.sel3, passes, 1); + } else { + ok = hasAtLeast(range, v.sel1, passes, 1) && hasAtLeast(range, v.sel2, passes, 1) && hasAtLeast(range, v.sel3, passes, 1); + } + if (ok) { + tag.set(v.bit); + } + } +} + +// Sets tag.set(v.bit) for every variant where range1 (roles 1&2, one table) and range2 (role 3, a +// different table) have what it takes to form a CROSS-TYPE triplet (track-track-v0, +// track-track-cascade): same12 needs 2 distinct entries in range1 passing sel1; otherwise role 1 +// and role 2 each need their own independent match in range1. Mirrors +// TripletTrackTrackV0Builder/TripletTrackTrackCascadeBuilder exactly (see TrackTrackV0Variant). +template +inline void tagCrossTripletVariants(CollisionTagBitset& tag, std::vector const& variants, Range1 const& range1, PassesFn1 const& passes1, Range2 const& range2, PassesFn2 const& passes2) +{ + for (auto const& v : variants) { + bool ok = v.same12 ? hasAtLeast(range1, v.sel1, passes1, 2) + : (hasAtLeast(range1, v.sel1, passes1, 1) && hasAtLeast(range1, v.sel2, passes1, 1)); + if (ok && hasAtLeast(range2, v.sel3, passes2, 1)) { + tag.set(v.bit); + } + } +} + +// Role names and device-name substrings, declared once: a consuming task needs the exact same +// role spelling discoverTaggedRoles() below uses, to look its own bit back up via findOwnBit() -- +// fed in as ConfTagging's template argument below, so a task's role is fixed at compile time +// rather than a loose string repeated at every call site. Declared as named `char` arrays, not +// `const char*` pointing at string literals: a pointer non-type template argument must be the +// address of a named object with static storage duration, and the standard specifically +// disallows forming that address from a string literal directly (two identical literals aren't +// guaranteed the same address). A named array has none of that ambiguity, and decays to +// `const char*` on its own wherever a plain pointer is expected (ConfTagging +// below, the explicit static_cast(...) in discoverTaggedRoles(), etc.). `inline` (not just +// `constexpr`) matters too: without it, a namespace-scope variable has internal linkage, so each +// translation unit would get its own copy at its own address -- fine for a value, but the same +// template instantiation must denote the same entity everywhere it's used. `inline` guarantees +// one shared address. The device-name substrings are, unavoidably, the literal +// o2physics_add_dpl_workflow() names from the respective CMakeLists.txt -- there is no way to +// derive those at compile time, so they are hardcoded here, exactly once, rather than repeated at +// every call site. +namespace role +{ +inline constexpr char TrackTrackPair[] = "TrackTrackPair"; +inline constexpr char TrackLambdaPair[] = "TrackLambdaPair"; +inline constexpr char TrackK0shortPair[] = "TrackK0shortPair"; +inline constexpr char TrackXiPair[] = "TrackXiPair"; +inline constexpr char TrackOmegaPair[] = "TrackOmegaPair"; +inline constexpr char TrackSigmaPair[] = "TrackSigmaPair"; +inline constexpr char TrackSigmaPlusPair[] = "TrackSigmaPlusPair"; +inline constexpr char TrackSigmaPlusPcmPair[] = "TrackSigmaPlusPcmPair"; +inline constexpr char TrackD0Pair[] = "TrackD0Pair"; +inline constexpr char TrackLcPair[] = "TrackLcPair"; +inline constexpr char LambdaLambdaPair[] = "LambdaLambdaPair"; +inline constexpr char K0shortK0shortPair[] = "K0shortK0shortPair"; +inline constexpr char LambdaK0shortPair[] = "LambdaK0shortPair"; +inline constexpr char D0D0Pair[] = "D0D0Pair"; +inline constexpr char TrackPhiPair[] = "TrackPhiPair"; +inline constexpr char TrackKstar0Pair[] = "TrackKstar0Pair"; +inline constexpr char TrackRho0Pair[] = "TrackRho0Pair"; +inline constexpr char TrackPi0Pair[] = "TrackPi0Pair"; +inline constexpr char TrackEtaPair[] = "TrackEtaPair"; +inline constexpr char LambdaPhiPair[] = "LambdaPhiPair"; +inline constexpr char LambdaKstar0Pair[] = "LambdaKstar0Pair"; +inline constexpr char LambdaRho0Pair[] = "LambdaRho0Pair"; +inline constexpr char K0shortPhiPair[] = "K0shortPhiPair"; +inline constexpr char K0shortKstar0Pair[] = "K0shortKstar0Pair"; +inline constexpr char K0shortRho0Pair[] = "K0shortRho0Pair"; +inline constexpr char TrackTrackTrackTriplet[] = "TrackTrackTrackTriplet"; +inline constexpr char TrackTrackLambdaTriplet[] = "TrackTrackLambdaTriplet"; +inline constexpr char TrackTrackXiTriplet[] = "TrackTrackXiTriplet"; +inline constexpr char TrackTrackOmegaTriplet[] = "TrackTrackOmegaTriplet"; +} // namespace role + +namespace devicename +{ +constexpr const char* FemtoPairTrackTrack = "femto-pair-track-track"; +constexpr const char* FemtoPairTrackV0 = "femto-pair-track-v0"; +constexpr const char* FemtoPairTrackCascade = "femto-pair-track-cascade"; +constexpr const char* FemtoPairTrackKink = "femto-pair-track-kink"; +constexpr const char* FemtoPairTrackCharmHadron = "femto-pair-track-charm-hadron"; +constexpr const char* FemtoPairV0V0 = "femto-pair-v0-v0"; +constexpr const char* FemtoPairD0D0 = "femto-pair-d0-d0"; +constexpr const char* FemtoPairTrackTwoTrackResonance = "femto-pair-track-two-track-resonance"; +constexpr const char* FemtoPairTrackTwoPhotonResonance = "femto-pair-track-two-photon-resonance"; +constexpr const char* FemtoPairV0TwoTrackResonance = "femto-pair-v0-two-track-resonance"; +constexpr const char* FemtoTripletTrackTrackTrack = "femto-triplet-track-track-track"; +constexpr const char* FemtoTripletTrackTrackV0 = "femto-triplet-track-track-v0"; +constexpr const char* FemtoTripletTrackTrackCascade = "femto-triplet-track-track-cascade"; +} // namespace devicename + +// One assigned bit of FColTags::collisionTag: `deviceName` is the matched sibling device's own +// (unique) DPL device name, `role` names which particle-selection group of that device the bit +// covers (e.g. "TrackSelection1", "LambdaSelection1"), and `bit` is its position in collisionTag. +// One assigned bit means "this collision has what it takes to form a pair of this TYPE": both the +// particle-1-role candidate and the particle-2-role candidate (or, for a same-species pair, at +// least 2 distinct candidates passing the one shared selection) are present. One bit per pair type +// per task instance -- not one bit per particle role -- since during mixing the same bit is +// checked identically on both the anchor and the partner collision (either one could end up +// supplying either role across the full mixing sweep). +struct TaggedRole { + std::string deviceName; + std::string role; + int bit = 0; +}; + +// The single canonical bit-assignment walk: scans the workflow's device list in order and, for +// every recognized pair/triplet task instance found, consumes the next free bit(s) for its +// role(s) -- exactly what femtoProducerCollisionTagger.cxx uses to decide what to tag, and what a +// consuming task calls to recompute (from the same static device list) which bit(s) ended up +// being its own. Keep this in sync with femtoProducerCollisionTagger.cxx's supported task types: whichever +// task types are added there must be added here identically, in the same order, or the tagger and +// its consumers will silently disagree about which bit means what. +inline std::vector discoverTaggedRoles(o2::framework::RunningWorkflowInfo const& workflows) +{ + std::vector roles; + int counter = 0; + for (o2::framework::DeviceSpec const& device : workflows.devices) { + if (device.name.find(devicename::FemtoPairTrackTrack) != std::string::npos) { + // Gated on whether ANY of the *WithTagging* process functions that actually consume this + // bit is enabled (processMixedEventWithTagging itself, or one of the + // ...AndMass/...AndEventShape/...AndFitVars combined variants -- all share the same role + // and tag bit, since the tag-bit check and the extra joined columns are independent + // concerns). An instance that enables none of them will never look this bit up, so + // assigning it one anyway would just waste a slot out of the hard-capped budget + // (MaxTagBits below) for nothing. + if (hasAnyEnabledOptionStartingWith(device, "processMixedEventWithTagging")) { + roles.push_back({device.name, static_cast(role::TrackTrackPair), counter++}); + } + } else if (device.name.find(devicename::FemtoPairTrackV0) != std::string::npos) { + // Each species gates independently on its own *MixedEventWithTagging* switch family -- + // Lambda and K0short are activated completely independently of each other (e.g. + // same-event-only for one species, tagged-mixed-event for the other is a legitimate + // combination), and, same as TrackTrackPair above, same-event/Mc/WithFitVars-without-tagging + // variants are irrelevant here since none of them ever consume a tag bit. + if (hasAnyEnabledOptionStartingWith(device, "processLambdaMixedEventWithTagging")) { + roles.push_back({device.name, static_cast(role::TrackLambdaPair), counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processK0shortMixedEventWithTagging")) { + roles.push_back({device.name, static_cast(role::TrackK0shortPair), counter++}); + } + } else if (device.name.find(devicename::FemtoPairTrackCascade) != std::string::npos) { + // Xi and Omega are mutually exclusive on a single instance (the task itself fatals if + // both are enabled), but gating independently costs nothing and needs no special-casing + // here. + if (hasAnyEnabledOptionStartingWith(device, "processXiMixedEventWithTagging")) { + roles.push_back({device.name, static_cast(role::TrackXiPair), counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processOmegaMixedEventWithTagging")) { + roles.push_back({device.name, static_cast(role::TrackOmegaPair), counter++}); + } + } else if (device.name.find(devicename::FemtoPairTrackKink) != std::string::npos) { + if (hasAnyEnabledOptionStartingWith(device, "processSigmaMixedEventWithTagging")) { + roles.push_back({device.name, static_cast(role::TrackSigmaPair), counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processSigmaPlusMixedEventWithTagging")) { + roles.push_back({device.name, static_cast(role::TrackSigmaPlusPair), counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processSigmaPlusPcmMixedEventWithTagging")) { + roles.push_back({device.name, static_cast(role::TrackSigmaPlusPcmPair), counter++}); + } + } else if (device.name.find(devicename::FemtoPairTrackCharmHadron) != std::string::npos) { + if (hasAnyEnabledOptionStartingWith(device, "processD0MixedEventWithTagging")) { + roles.push_back({device.name, static_cast(role::TrackD0Pair), counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processLcMixedEventWithTagging")) { + roles.push_back({device.name, static_cast(role::TrackLcPair), counter++}); + } + } else if (device.name.find(devicename::FemtoPairV0V0) != std::string::npos) { + // Lambda-lambda, k0short-k0short and lambda-k0short are mutually exclusive on a single + // instance (the task itself fatals if more than one is enabled), but gating independently + // costs nothing and needs no special-casing here. + if (hasAnyEnabledOptionStartingWith(device, "processLambdaLambdaMixedEventWithTagging")) { + roles.push_back({device.name, static_cast(role::LambdaLambdaPair), counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processK0shortK0shortMixedEventWithTagging")) { + roles.push_back({device.name, static_cast(role::K0shortK0shortPair), counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processLambdaK0shortMixedEventWithTagging")) { + roles.push_back({device.name, static_cast(role::LambdaK0shortPair), counter++}); + } + } else if (device.name.find(devicename::FemtoPairD0D0) != std::string::npos) { + // Only one pair type possible per instance (sign on D0Selection1/2 picks the hypothesis), so + // this task's WithTagging process function is just "processMixedEventWithTagging", same local + // name as femtoPairTrackTrack.cxx's -- fine since option names are scoped per-device. + if (hasAnyEnabledOptionStartingWith(device, "processMixedEventWithTagging")) { + roles.push_back({device.name, static_cast(role::D0D0Pair), counter++}); + } + } else if (device.name.find(devicename::FemtoPairTrackTwoTrackResonance) != std::string::npos) { + // Phi, Kstar0 and Rho0 are mutually exclusive on a single instance (the task itself fatals + // if more than one is enabled), but gating independently costs nothing here either. + if (hasAnyEnabledOptionStartingWith(device, "processPhiMixedEventWithTagging")) { + roles.push_back({device.name, static_cast(role::TrackPhiPair), counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processKstar0MixedEventWithTagging")) { + roles.push_back({device.name, static_cast(role::TrackKstar0Pair), counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processRho0MixedEventWithTagging")) { + roles.push_back({device.name, static_cast(role::TrackRho0Pair), counter++}); + } + } else if (device.name.find(devicename::FemtoPairTrackTwoPhotonResonance) != std::string::npos) { + // Pi0 and Eta are mutually exclusive on a single instance, same as above. + if (hasAnyEnabledOptionStartingWith(device, "processPi0MixedEventWithTagging")) { + roles.push_back({device.name, static_cast(role::TrackPi0Pair), counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processEtaMixedEventWithTagging")) { + roles.push_back({device.name, static_cast(role::TrackEtaPair), counter++}); + } + } else if (device.name.find(devicename::FemtoPairV0TwoTrackResonance) != std::string::npos) { + // All six v0 x resonance combinations are mutually exclusive on a single instance (the task + // itself fatals if more than one is enabled), same as above. + if (hasAnyEnabledOptionStartingWith(device, "processLambdaPhiMixedEventWithTagging")) { + roles.push_back({device.name, static_cast(role::LambdaPhiPair), counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processLambdaKstar0MixedEventWithTagging")) { + roles.push_back({device.name, static_cast(role::LambdaKstar0Pair), counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processLambdaRho0MixedEventWithTagging")) { + roles.push_back({device.name, static_cast(role::LambdaRho0Pair), counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processK0shortPhiMixedEventWithTagging")) { + roles.push_back({device.name, static_cast(role::K0shortPhiPair), counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processK0shortKstar0MixedEventWithTagging")) { + roles.push_back({device.name, static_cast(role::K0shortKstar0Pair), counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processK0shortRho0MixedEventWithTagging")) { + roles.push_back({device.name, static_cast(role::K0shortRho0Pair), counter++}); + } + } else if (device.name.find(devicename::FemtoTripletTrackTrackTrack) != std::string::npos) { + if (hasAnyEnabledOptionStartingWith(device, "processMixedEventWithTagging")) { + roles.push_back({device.name, static_cast(role::TrackTrackTrackTriplet), counter++}); + } + } else if (device.name.find(devicename::FemtoTripletTrackTrackV0) != std::string::npos) { + if (hasAnyEnabledOptionStartingWith(device, "processMixedEventWithTagging")) { + roles.push_back({device.name, static_cast(role::TrackTrackLambdaTriplet), counter++}); + } + } else if (device.name.find(devicename::FemtoTripletTrackTrackCascade) != std::string::npos) { + // Xi and Omega are mutually exclusive on a single instance (the task itself fatals if both + // are enabled), but gating independently costs nothing here either. + if (hasAnyEnabledOptionStartingWith(device, "processXiMixedEventWithTagging")) { + roles.push_back({device.name, static_cast(role::TrackTrackXiTriplet), counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processOmegaMixedEventWithTagging")) { + roles.push_back({device.name, static_cast(role::TrackTrackOmegaTriplet), counter++}); + } + } + // TODO: add the remaining triplet task types (mc-particle triplets, ...), following the same + // pattern. Whatever is added here must also be added to femtoProducerCollisionTagger.cxx's + // init(), in the same order, since that is where the selection VALUES behind each of these + // bits actually get read and checked. + } + return roles; +} + +// Convenience for a consuming task: find the bit this device's own `role` was assigned, or -1 if +// it was never reached (e.g. this device's own process switch wasn't enabled on the tagger, so it +// never requested this role at all). +inline int findOwnBit(std::vector const& roles, std::string const& ownDeviceName, std::string const& role) +{ + for (auto const& r : roles) { + if (r.deviceName == ownDeviceName && r.role == role) { + return r.bit; + } + } + return -1; +} + +// Carries a consuming task's own, fixed pair-type role (Role, one of the role:: constants above, +// supplied at the instantiation site -- e.g. ConfTagging confTagging;) and +// the bit that role was assigned, discovered once in init() into `internalTagMask` (not read from +// the JSON config). Bundling both together means a call like findOwnBit(roles, ownDevice.name, +// confTagging.Role) always uses the exact role this same object's mask gets set for, instead of +// the role being a second, independently-spelled literal at the call site. Every task that opts +// into tagger-filtered mixing instantiates one of these; wrapped in a ConfigurableGroup (rather +// than a bare Configurable) so more fields can be added here later without having to touch every +// consumer. `prefix` folds in Role itself: a task with more than one pair type (e.g. +// femtoPairTrackV0.cxx's Lambda and K0short, each needing their own tag bit) instantiates +// ConfTagging twice, and without this, both would register the exact same CLI/JSON option name +// ("Tagging.internalTagMask" twice over), which DPL rejects at startup as an ambiguous option. +template +struct ConfTagging : o2::framework::ConfigurableGroup { + std::string prefix = std::string("Tagging") + role; + static constexpr char const* Role = role; + o2::framework::Configurable internalTagMask{"internalTagMask", 0, "internal: discovered collision tagger bitmask, set automatically in init() -- do not set manually"}; +}; + +} // namespace o2::analysis::femto::collisiontagger + +#endif // PWGCF_FEMTO_CORE_COLLISIONTAGGER_H_ diff --git a/PWGCF/Femto/Core/dataTypes.h b/PWGCF/Femto/Core/dataTypes.h index 25c5d84147c..d96d1cf69bf 100644 --- a/PWGCF/Femto/Core/dataTypes.h +++ b/PWGCF/Femto/Core/dataTypes.h @@ -35,6 +35,9 @@ using V0MaskType = uint32_t; using V0MaskType001 = uint16_t; // old data type, was too narrow using V0Type = uint16_t; +// datatypes for photons +using PhotonMaskType = uint16_t; + // datatypes for kinks using KinkMaskType = uint32_t; using KinkType = uint8_t; @@ -44,6 +47,12 @@ using TwoTrackResonanceMaskType = uint32_t; // two track resonance types using TwoTrackResonanceType = uint16_t; +// datatype for resonances built from two photons (pi0, eta, ...) +using TwoPhotonResonanceType = uint16_t; + +// datatype for resonances built from two tracks and a two-photon resonance (e.g. omega(782) -> pi+ pi- pi0) +using TrackTrackTwoPhotonResonanceType = uint8_t; + // datatypes for cascades using CascadeMaskType = uint32_t; using CascadeMaskType001 = uint16_t; // old data type, was too narrow diff --git a/PWGCF/Femto/Core/dndetaBuilder.h b/PWGCF/Femto/Core/dndetaBuilder.h index 651960283fb..2404c573ec3 100644 --- a/PWGCF/Femto/Core/dndetaBuilder.h +++ b/PWGCF/Femto/Core/dndetaBuilder.h @@ -232,7 +232,8 @@ class DndetaBuilder /// - reco: for every mc collision only the reconstructed collision with the most PV contributors is used /// (emulates bestCollisionIndex); it has to pass the full event selection including the reco trigger /// - gen: efficiency histograms for all generated collisions, loss histograms for the generated event class - /// (vertex z, optional INEL>0, optional generator-level trigger) + /// (optional INEL>0, optional generator-level trigger); the loss block's reco requirement is Sel8 + /// only (no vtxZ, see isSelectedByMaskOnly), the vtxZ cut only gates the efficiency block above template void processMc(T1 const& cols, T2 const& mcCols, @@ -263,11 +264,20 @@ class DndetaBuilder } // reconstructed side + // selectedReco: full "selvtx" selection (Sel8 + |vtxZ| < cut + trigger), feeds the Gen-efficiency block (Ctrk) + // hasSel8Reco: "sel" selection (Sel8 only, no vtxZ, no trigger), feeds the event/signal loss block (Csel); + // per the analysis note, Csel's Gsel/Esel correspond to the Sel8 class only, Cvtx (the vtxZ-related bias) is + // not implemented (every Run3 event with a vertex is already within the stored range), so the loss-block + // reco requirement must not carry the vtxZ cut like the Ctrk-side "selvtx" one does std::vector selectedReco(nMcCols, -1); + std::vector hasSel8Reco(nMcCols, false); for (auto const& col : cols) { if (!col.has_fMcCol() || bestReco[static_cast(col.fMcColId())] != col.globalIndex()) { continue; } + if (isSelectedByMaskOnly(col)) { + hasSel8Reco[static_cast(col.fMcColId())] = true; + } if (!selectCollision(col, [&]() { return recoTrigger(col); })) { @@ -300,13 +310,12 @@ class DndetaBuilder } } - // event and signal loss: generated event class, true centrality + // event and signal loss: generated event class (INEL or INEL>0), true centrality + // Gall/Gsel (and Eall/Esel) are defined by the analysis note without any vtxZ restriction - the vtxZ + // cut belongs only to Ctrk's "selvtx" quantities above, so it is intentionally not applied here if (!mDoMcLoss) { continue; } - if (mcCol.posZ() < mVtxZMin || mcCol.posZ() > mVtxZMax) { - continue; - } int nChMult = 0; int nChEta1 = 0; for (auto const& particle : particles) { @@ -324,13 +333,14 @@ class DndetaBuilder if (mUseGenTrigger && !genTrigger(mcCol)) { continue; } + const bool hasSel8RecoForLoss = hasSel8Reco[mcColIndex]; const auto mult = static_cast(nChMult); - mHistManager.fillLossEvent(mcCol.cent(), mult, hasSelectedReco); + mHistManager.fillLossEvent(mcCol.cent(), mult, hasSel8RecoForLoss); for (auto const& particle : particles) { if (!isGenParticleSelected(particle, pdg)) { continue; } - mHistManager.fillLossParticle(particle.eta(), mcCol.cent(), mult, hasSelectedReco); + mHistManager.fillLossParticle(particle.eta(), mcCol.cent(), mult, hasSel8RecoForLoss); } } } @@ -353,6 +363,18 @@ class DndetaBuilder LOG(info) << name << ": pT range " << conf.ptMin.value << " - " << conf.ptMax.value << " GeV/c, eta range " << conf.etaMin.value << " - " << conf.etaMax.value; } + /// selected by maks only class (no vtxZ, no INEL>0, no trigger; pure predicate, no cutflow side effects), used to + /// build Gsel/Esel of the event/signal loss block - kept separate from selectCollision() below, whose + /// vtxZ-gated "selvtx" result must not be reused for the loss block (see processMc) + template + bool isSelectedByMaskOnly(T const& col) const + { + return col.mult() >= mMultMin && col.mult() <= mMultMax && + col.cent() >= mCentMin && col.cent() <= mCentMax && + col.magField() >= mMagFieldMin && col.magField() <= mMagFieldMax && + (col.mask() & mCollisionMask) == mCollisionMask; + } + /// event selection with cutflow; the trigger is evaluated last, so the trigger QA /// only contains events that passed all other event selections template diff --git a/PWGCF/Femto/Core/dndetaHistManager.h b/PWGCF/Femto/Core/dndetaHistManager.h index ed4f425aed6..69b2107d575 100644 --- a/PWGCF/Femto/Core/dndetaHistManager.h +++ b/PWGCF/Femto/Core/dndetaHistManager.h @@ -11,7 +11,7 @@ /// \file dndetaHistManager.h /// \brief histogram manager for charged-particle pseudorapidity density measurements on femto derived data -/// \author anton.riedel@tum.de, TU München, anton.riedel@tum.de +/// \author Anton Riedel, TU München, anton.riedel@tum.de #ifndef PWGCF_FEMTO_CORE_DNDETAHISTMANAGER_H_ #define PWGCF_FEMTO_CORE_DNDETAHISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/femtoMath.h b/PWGCF/Femto/Core/femtoMath.h new file mode 100644 index 00000000000..f686b85d9b9 --- /dev/null +++ b/PWGCF/Femto/Core/femtoMath.h @@ -0,0 +1,284 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoMath.h +/// \brief shared kinematic math (kstar, Q3, mt, kt, spherical-harmonics kinematics, ...) +/// used by the pair and triplet histogram managers +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_FEMTOMATH_H_ +#define PWGCF_FEMTO_CORE_FEMTOMATH_H_ + +#include "PWGCF/Femto/Core/modes.h" + +#include "Common/Core/RecoDecay.h" + +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector3Dfwd.h) +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include + +#include +#include +#include +#include +#include + +namespace o2::analysis::femto::femtomath +{ + +// SH reference frame selectors for computeShKinematics(), mirrors ConfPairBinning.shFrame +constexpr int ShFrameLcmsNonIdentical = 0; +constexpr int ShFrameLcmsIdentical = 1; +constexpr int ShFramePrf = 2; + +inline float getKt(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) +{ + auto sum = (part1 + part2); + double kt = 0.5 * sum.Pt(); + return static_cast(kt); +} + +inline float getMt(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2, modes::TransverseMassType mtType = modes::TransverseMassType::kAveragePdgMass) +{ + auto sum = part1 + part2; + double mt = 0; + double mass = 0; + switch (mtType) { + case modes::TransverseMassType::kAveragePdgMass: + mass = 0.5 * (part1.M() + part2.M()); + mt = std::hypot(0.5 * sum.Pt(), mass); + break; + case modes::TransverseMassType::kReducedPdgMass: + mass = 2. * (part1.M() * part2.M()) / (part1.M() + part2.M()); + mt = std::hypot(0.5 * sum.Pt(), mass); + break; + case modes::TransverseMassType::kMt4Vector: + mt = 0.5 * sum.Mt(); + break; + default: + LOG(fatal) << "Invalid transverse mass type, breaking..."; + } + return static_cast(mt); +} + +inline float getMinv(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) +{ + return static_cast((part1 + part2).M()); +} + +inline float getKstar(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) +{ + // Use Cartesian 4-vectors: addition/M2() become pure arithmetic + const ROOT::Math::PxPyPzEVector p1(part1); + const ROOT::Math::PxPyPzEVector p2(part2); + + // Mandelstam s = (p1 + p2)^2 + const double s = (p1 + p2).M2(); + const double m1sq = p1.M2(); + const double m2sq = p2.M2(); + + // Källen function λ(s, m1^2, m2^2) = (s - m1^2 - m2^2)² - 4*m1^2*m2^2 + const double kallen = (s - m1sq - m2sq) * (s - m1sq - m2sq) - 4.0 * m1sq * m2sq; + + // k* = 0.5 * sqrt(λ/s) + return static_cast(0.5 * std::sqrt(std::max(0.0, kallen) / s)); +} + +// Alpha: azimuthal opening angle between the two particles' transverse momenta, +// i.e. phi2 - phi1 wrapped to [-pi, pi). Purely a lab-frame angular variable (no +// boosting involved) -- small |alpha| means the pair sits close together in phi, +// which is where detector effects like track splitting/merging and two-track +// resolution losses tend to show up. +inline float getAlpha(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) +{ + return static_cast(RecoDecay::constrainAngle(part2.phi() - part1.phi(), -o2::constants::math::PI)); +} + +// BetaLab: lab-frame angle between the pair's relative 3-momentum (p1 - p2) and its +// total 3-momentum (p1 + p2, i.e. the kT direction). Note the (p1 - p2) here is the +// raw 3-vector difference, not the Lorentz-invariant k* -- this is a lab-frame proxy +// for how the pair's separation axis is oriented relative to its direction of motion, +// without needing to boost to the pair rest frame. +inline float getBetaLab(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) +{ + ROOT::Math::XYZVector p1(part1.Px(), part1.Py(), part1.Pz()); + ROOT::Math::XYZVector p2(part2.Px(), part2.Py(), part2.Pz()); + + ROOT::Math::XYZVector kstar = p1 - p2; + ROOT::Math::XYZVector kt = p1 + p2; + + return static_cast(std::acos(kstar.Dot(kt) / std::sqrt(kstar.Mag2() * kt.Mag2()))); +} + +inline std::tuple computeBertschPrattLCMS(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) +{ + const ROOT::Math::PxPyPzEVector p1(part1); + const ROOT::Math::PxPyPzEVector p2(part2); + const ROOT::Math::PxPyPzEVector pSum = p1 + p2; + const ROOT::Math::PxPyPzEVector pDiff = p1 - p2; + + const double tPx = pSum.Px(); + const double tPy = pSum.Py(); + const double tPz = pSum.Pz(); + const double tE = pSum.E(); + + const double tPt = std::sqrt(tPx * tPx + tPy * tPy); + const double tMt = std::sqrt(tE * tE - tPz * tPz); + + static constexpr double MinTransverseMomentum = 1e-9; + if (tPt < MinTransverseMomentum || tMt < MinTransverseMomentum) { + return {0.0f, 0.0f, 0.0f}; + } + + const double betaL = tPz / tE; + const double gammaL = tE / tMt; + + // out/side/long are linear projections of the momentum, so projecting the + // difference p1-p2 directly is equivalent to (and half the divisions of) + // computing each particle's out/side/long component and subtracting. + const double qOut = (pDiff.Px() * tPx + pDiff.Py() * tPy) / tPt; + const double qSide = (pDiff.Py() * tPx - pDiff.Px() * tPy) / tPt; + const double qLong = gammaL * (pDiff.Pz() - betaL * pDiff.E()); + + return {static_cast(qOut), static_cast(qSide), static_cast(qLong)}; +} + +// Return the bin index for value given ascending bin edges, or -1 if out of range. +// edges = {e0, e1, ..., eN} defines N bins [e0,e1), [e1,e2), ..., [e_{N-1},eN). +inline int findShBin(double value, std::vector const& edges) +{ + static constexpr std::size_t MinEdgesForOneBin = 2; + if (edges.size() < MinEdgesForOneBin || value < edges.front() || value >= edges.back()) { + return -1; + } + for (std::size_t i = 0; i < edges.size() - 1; ++i) { + if (value >= edges[i] && value < edges[i + 1]) { + return static_cast(i); + } + } + return -1; +} + +// Kinematics feeding the spherical-harmonics decomposition, ported 1:1 from +// FemtoUniverseMath::newpairfunc so results are numerically comparable. +// Returns {kv, out, side, long}, selected by shFrame: +// ShFrameLcmsNonIdentical: {kstar, fDKOut, fDKSide, fDKLong} (LCMS components of particle 1) +// ShFrameLcmsIdentical: {qinv, outLCMS, sideLCMS, longLCMS} (LCMS q-differences) +// ShFramePrf: {|qPRF|, outPRF, sidePRF, longPRF} (matches FemtoUniverse isIdenPRF=true) +inline std::tuple computeShKinematics(ROOT::Math::PtEtaPhiMVector const& part1, + ROOT::Math::PtEtaPhiMVector const& part2, + int shFrame) +{ + const ROOT::Math::PxPyPzEVector p1(part1); + const ROOT::Math::PxPyPzEVector p2(part2); + const ROOT::Math::PxPyPzEVector sum = p1 + p2; + + const double tPx = sum.Px(); + const double tPy = sum.Py(); + const double tPz = sum.Pz(); + const double tE = sum.E(); + const double tPtSq = tPx * tPx + tPy * tPy; + const double tMtSq = tE * tE - tPz * tPz; + + static constexpr double MinTransverseMomentum = 1e-9; + if (tPtSq < MinTransverseMomentum || tMtSq < MinTransverseMomentum) { + return {0.f, 0.f, 0.f, 0.f}; + } + const double tPt = std::sqrt(tPtSq); + const double tMt = std::sqrt(tMtSq); + const double tM = std::sqrt(std::max(0.0, tMtSq - tPtSq)); + + const double beta = tPz / tE; + const double gamma = tE / tMt; + + // LCMS components of particle 1 (fDKOut/Side/Long in newpairfunc) + const double e1 = p1.E(); + const double fDKOut = (p1.Px() * tPx + p1.Py() * tPy) / tPt; + const double fDKSide = (-p1.Px() * tPy + p1.Py() * tPx) / tPt; + const double fDKLong = gamma * (p1.Pz() - beta * e1); + const double fDE = gamma * (e1 - beta * p1.Pz()); + + // LCMS components of particle 2 + const double e2 = p2.E(); + const double px2 = (p2.Px() * tPx + p2.Py() * tPy) / tPt; + const double py2 = (p2.Py() * tPx - p2.Px() * tPy) / tPt; + const double pz2 = gamma * (p2.Pz() - beta * e2); + + const double outLCMS = fDKOut - px2; + const double sideLCMS = fDKSide - py2; + const double longLCMS = fDKLong - pz2; + + if (shFrame == ShFrameLcmsIdentical) { + // LCMS identical: Ylm <- LCMS q-differences, axis <- qinv + const double qinv = std::sqrt(outLCMS * outLCMS + sideLCMS * sideLCMS + longLCMS * longLCMS); + return {static_cast(qinv), + static_cast(outLCMS), static_cast(sideLCMS), static_cast(longLCMS)}; + } + if (shFrame == ShFramePrf) { + // PRF: Ylm <- PRF q-differences, axis <- |q_PRF| (matches FemtoUniverse isIdenPRF=true) + const double pE2LCMS = gamma * (e2 - beta * p2.Pz()); + const double betaOut = tPt / tMt; + const double gammaOut = tMt / tM; + const double outPRF = gammaOut * (outLCMS - betaOut * (fDE - pE2LCMS)); + const double sidePRF = sideLCMS; + const double longPRF = longLCMS; + const double qPRF = std::sqrt(outPRF * outPRF + sidePRF * sidePRF + longPRF * longPRF); + return {static_cast(qPRF), + static_cast(outPRF), static_cast(sidePRF), static_cast(longPRF)}; + } + // LCMS non-identical (default): Ylm <- LCMS components of particle 1, axis <- k* + const double betaOut = tPt / tMt; + const double gammaOut = tMt / tM; + const double fKOut = gammaOut * (fDKOut - betaOut * fDE); + const double kstar = std::sqrt(fKOut * fKOut + fDKSide * fDKSide + fDKLong * fDKLong); + return {static_cast(kstar), + static_cast(fDKOut), static_cast(fDKSide), static_cast(fDKLong)}; +} + +template +inline float getPairPhiFromEventPlaneAngle(const ROOT::Math::PtEtaPhiMVector& part1, const ROOT::Math::PtEtaPhiMVector& part2, const T1& col) +{ + const ROOT::Math::PtEtaPhiMVector trackSum = part1 + part2; + float phi = RecoDecay::constrainAngle(trackSum.Phi() - col.eventPlaneAngle()); + return phi; +} + +// triplet-only helpers + +inline ROOT::Math::PxPyPzEVector getqij(ROOT::Math::PxPyPzEVector const& vi, ROOT::Math::PxPyPzEVector const& vj) +{ + auto trackSum = vi + vj; + auto trackDifference = vi - vj; + const double s = trackSum.M2(); + const double scaling = (s != 0.0) ? (vi.M2() - vj.M2()) / s : 0.0; + return trackDifference - scaling * trackSum; +} + +inline float getQ3(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2, ROOT::Math::PtEtaPhiMVector const& part3) +{ + // upfront conversion to PxPyPzEVector + const ROOT::Math::PxPyPzEVector p1(part1); + const ROOT::Math::PxPyPzEVector p2(part2); + const ROOT::Math::PxPyPzEVector p3(part3); + auto q12 = getqij(p1, p2); + auto q23 = getqij(p2, p3); + auto q31 = getqij(p3, p1); + double q = q12.M2() + q23.M2() + q31.M2(); + return static_cast(std::sqrt(std::max(0.0, -q))); +} + +} // namespace o2::analysis::femto::femtomath + +#endif // PWGCF_FEMTO_CORE_FEMTOMATH_H_ diff --git a/PWGCF/Femto/Core/femtoSpherHarMath.h b/PWGCF/Femto/Core/femtoSpherHarMath.h index 717368f830d..65cd7e98dd5 100644 --- a/PWGCF/Femto/Core/femtoSpherHarMath.h +++ b/PWGCF/Femto/Core/femtoSpherHarMath.h @@ -34,6 +34,11 @@ class SpherHarMath static constexpr int TrigCacheSize = MaxSupportedL + 1; // sin/cos powers cache size (indices 0..5) static constexpr double SmallLength = 1e-10; // numerical guard for |r|, |z| + // thresholds for legendreUpToYlm(): the l=N block is only filled once lmax reaches N + static constexpr int MinLmaxForL3Block = 3; + static constexpr int MinLmaxForL4Block = 4; + static constexpr int MinLmaxForL5Block = 5; + /// Values of various coefficients void initializeYlms() { @@ -138,7 +143,7 @@ class SpherHarMath } // Legendre polynomials l=3 - if (lmax > 2) { // o2-linter: disable=magic-number (l index, mirrors lmax>0/1 above) + if (lmax >= MinLmaxForL3Block) { lbuf[6] = sins[3]; lbuf[7] = sins[2] * coss[1]; lbuf[8] = (5 * coss[2] - 1) * sins[1]; @@ -146,7 +151,7 @@ class SpherHarMath } // Legendre polynomials l=4 - if (lmax > 3) { // o2-linter: disable=magic-number (l index) + if (lmax >= MinLmaxForL4Block) { lbuf[10] = sins[4]; lbuf[11] = sins[3] * coss[1]; lbuf[12] = (7 * coss[2] - 1) * sins[2]; @@ -155,7 +160,7 @@ class SpherHarMath } // Legendre polynomials l=5 - if (lmax > 4) { // o2-linter: disable=magic-number (l index) + if (lmax >= MinLmaxForL5Block) { lbuf[15] = sins[5]; lbuf[16] = sins[4] * coss[1]; lbuf[17] = (9 * coss[2] - 1) * sins[3]; diff --git a/PWGCF/Femto/Core/femtoUtils.h b/PWGCF/Femto/Core/femtoUtils.h index 643ddffe654..9b9c16c4884 100644 --- a/PWGCF/Femto/Core/femtoUtils.h +++ b/PWGCF/Femto/Core/femtoUtils.h @@ -16,8 +16,10 @@ #ifndef PWGCF_FEMTO_CORE_FEMTOUTILS_H_ #define PWGCF_FEMTO_CORE_FEMTOUTILS_H_ +#include "Common/Core/RecoDecay.h" #include "Common/Core/TableHelper.h" +#include #include #include #include @@ -141,6 +143,12 @@ inline double getPdgMass(int pdgCode) case kOmegaMinus: mass = o2::constants::physics::MassOmegaMinus; break; + case kPi0: + mass = o2::constants::physics::MassPi0; + break; + case o2::constants::physics::kEta: + mass = o2::constants::physics::MassEta; + break; default: LOG(warn) << "PDG code " << pdgCode << " is not suppored. Return 0... "; } @@ -167,14 +175,26 @@ concept HasEventShapeRow = requires(T row) { template concept HasEventShape = HasEventShapeRow> || (requires { typename std::decay_t::iterator; } && HasEventShapeRow::iterator>); -/// Recalculate pT for Kinks (Sigmas) using kinematic constraints -inline float calcPtnew(float pxMother, float pyMother, float pzMother, float pxDaughter, float pyDaughter, float pzDaughter) +/// Azimuth of the momentum of a charged particle at the primary vertex, from its azimuth at the decay vertex. +/// The decay vertex (xDecay, yDecay) has to be given relative to the primary vertex. In a solenoidal field the transverse +/// trajectory is a circle through both points, and the tangents at the two ends of a chord make the same angle with it: +/// phiPV = 2*phiChord - phiDecay (independent of polarity). +inline float phiAtPrimaryVertex(float phiDecay, float xDecay, float yDecay) +{ + if (std::abs(xDecay) < o2::constants::math::Epsilon && std::abs(yDecay) < o2::constants::math::Epsilon) { + return phiDecay; + } + return RecoDecay::constrainAngle(2.f * std::atan2(yDecay, xDecay) - phiDecay); +} + +/// Recalculate the pT of a kink mother from its direction and the charged daughter momentum +inline float calcPtnew(float pxMother, float pyMother, float pzMother, float pxDaughter, float pyDaughter, float pzDaughter, + float massMother, float massChargedDaughter, float massNeutralDaughter) { float almost0 = 1e-6f; - // Particle masses in GeV/c^2 - auto massPion = o2::constants::physics::MassPionCharged; - auto massNeutron = o2::constants::physics::MassNeutron; - auto massSigmaMinus = o2::constants::physics::MassSigmaMinus; + const float massPion = massChargedDaughter; + const float massNeutron = massNeutralDaughter; + const float massSigmaMinus = massMother; // Calculate mother momentum and direction versor float pMother = std::sqrt(pxMother * pxMother + pyMother * pyMother + pzMother * pzMother); @@ -350,6 +370,59 @@ float unBinLogUnsigned(T binned, float magMin, float magMax) return mag; } +// --------------------------------------------------------------------------- +// purity/fraction packing: up to 4 species' log-binned nSigma values (see +// binLogSigned) packed bitwise into a single int64_t column. Slot +// assignment (which species occupies which of the 4 slots) is decided at +// producer init time from which requirePidXxx flags are active, and exposed +// via a self-documenting histogram -- these functions only do the fixed-slot +// bit shifting, nothing species-specific. +// --------------------------------------------------------------------------- + +constexpr int PurityNSlots = 4; + +/// pack 4 already-binned uint16_t codes (one per slot) into a single int64_t +inline int64_t packPurity4(uint16_t slot0, uint16_t slot1, uint16_t slot2, uint16_t slot3) +{ + return (static_cast(slot0) << 48) | + (static_cast(slot1) << 32) | + (static_cast(slot2) << 16) | + static_cast(slot3); +} + +/// extract the raw uint16_t code of one of the 4 slots (0..3) from a packed int64_t +inline uint16_t unpackPurity4(int64_t packed, int slot) +{ + switch (slot) { + case 0: + return static_cast((packed >> 48) & 0xFFFF); + case 1: + return static_cast((packed >> 32) & 0xFFFF); + case 2: + return static_cast((packed >> 16) & 0xFFFF); + case 3: + return static_cast(packed & 0xFFFF); + default: + LOG(fatal) << "unpackPurity4: invalid slot " << slot << " (must be 0-3). Breaking..."; + return 0; + } +} + +/// pack 4 raw signed nSigma values (one per slot, unused slots pass 0) directly into a packed int64_t +inline int64_t packPurityNSigma4(float nSigma0, float nSigma1, float nSigma2, float nSigma3, float magMin, float magMax) +{ + return packPurity4(binLogSigned(nSigma0, magMin, magMax), + binLogSigned(nSigma1, magMin, magMax), + binLogSigned(nSigma2, magMin, magMax), + binLogSigned(nSigma3, magMin, magMax)); +} + +/// unpack the nSigma value of one of the 4 slots (0..3) from a packed int64_t +inline float unpackPurityNSigma4(int64_t packed, int slot, float magMin, float magMax) +{ + return unBinLogSigned(unpackPurity4(packed, slot), magMin, magMax); +} + }; // namespace utils }; // namespace o2::analysis::femto // diff --git a/PWGCF/Femto/Core/histManager.h b/PWGCF/Femto/Core/histManager.h index a0070a08f9f..a5f2aeb6175 100644 --- a/PWGCF/Femto/Core/histManager.h +++ b/PWGCF/Femto/Core/histManager.h @@ -85,5 +85,19 @@ constexpr const char* getHistDesc(EnumType variable, const ArrayType& array) return it != array.end() ? it->histdesc.data() : ""; } + +// Replace the x-axis segment of a "title; x axis; y axis[; z axis...]" histogram description +// with newTitle. Used to patch a static HistTable description at runtime once the actual axis +// quantity (e.g. which momentum type, which kinematic variable) is resolved from a Configurable. +inline std::string replaceFirstAxisTitle(std::string_view desc, std::string_view newTitle) +{ + std::string result(desc); + const auto first = result.find(';'); + const auto second = (first == std::string::npos) ? std::string::npos : result.find(';', first + 1); + if (second != std::string::npos) { + result.replace(first + 1, second - first - 1, " " + std::string(newTitle) + " "); + } + return result; +} } // namespace o2::analysis::femto::histmanager #endif // PWGCF_FEMTO_CORE_HISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/kinkBuilder.h b/PWGCF/Femto/Core/kinkBuilder.h index 009be2f6dd4..84d518dbe15 100644 --- a/PWGCF/Femto/Core/kinkBuilder.h +++ b/PWGCF/Femto/Core/kinkBuilder.h @@ -27,6 +27,7 @@ #include "Common/Core/RecoDecay.h" #include +#include #include #include #include @@ -52,32 +53,38 @@ struct ConfKinkFilters : o2::framework::ConfigurableGroup { o2::framework::Configurable etaMax{"etaMax", 10.f, "Maximum eta"}; o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; + o2::framework::Configurable kinkPhiAtPv{"kinkPhiAtPv", true, "Kinks and PCM SigmaPlus: store the azimuth of the Sigma momentum at the primary vertex"}; o2::framework::Configurable massMinSigma{"massMinSigma", 1.1f, "Minimum mass for Sigma hypothesis"}; o2::framework::Configurable massMaxSigma{"massMaxSigma", 1.3f, "Maximum mass for Sigma hypothesis"}; o2::framework::Configurable massMinSigmaPlus{"massMinSigmaPlus", 1.1f, "Minimum mass for SigmaPlus hypothesis"}; o2::framework::Configurable massMaxSigmaPlus{"massMaxSigmaPlus", 1.3f, "Maximum mass for SigmaPlus hypothesis"}; + o2::framework::Configurable massMinSigmaPlusPcm{"massMinSigmaPlusPcm", 1.1f, "Minimum mass for SigmaPlus (PCM) hypothesis"}; + o2::framework::Configurable massMaxSigmaPlusPcm{"massMaxSigmaPlusPcm", 1.3f, "Maximum mass for SigmaPlus (PCM) hypothesis"}; + o2::framework::Configurable mlScoreMinSigmaPlusPcm{"mlScoreMinSigmaPlusPcm", -1.f, "Minimum BDT score for SigmaPlus (PCM) (-1: no cut)"}; }; // selections bits for all kinks // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define KINK_DEFAULT_BITS \ - o2::framework::Configurable passThrough{"passThrough", false, "If true, all Kinks are passed through. Bits for all selections are stored."}; \ - o2::framework::Configurable> kinkTopoDcaMax{"kinkTopoDcaMax", {2.0f}, "Maximum kink topological DCA"}; \ - o2::framework::Configurable> transRadMin{"transRadMin", {20.f}, "Minimum transverse radius (cm)"}; \ - o2::framework::Configurable> transRadMax{"transRadMax", {100.f}, "Maximum transverse radius (cm)"}; \ - o2::framework::Configurable> dauAbsEtaMax{"dauAbsEtaMax", {1.0f}, "Maximum absolute pseudorapidity for daughter track"}; \ - o2::framework::Configurable> dauDcaPvMin{"dauDcaPvMin", {0.1f}, "Minimum DCA of daughter from primary vertex (cm)"}; \ - o2::framework::Configurable> mothDcaPvMax{"mothDcaPvMax", {1.0f}, "Maximum DCA of mother from primary vertex (cm)"}; \ - o2::framework::Configurable> alphaAPMin{"alphaAPMin", {-1.0f}, "Minimum Alpha_AP for Sigma candidates"}; \ - o2::framework::Configurable> alphaAPMax{"alphaAPMax", {0.0f}, "Maximum Alpha_AP for Sigma candidates"}; \ - o2::framework::Configurable> qtAPMin{"qtAPMin", {0.15f}, "Minimum qT_AP for Sigma candidates"}; \ - o2::framework::Configurable> qtAPMax{"qtAPMax", {0.2f}, "Maximum qT_AP for Sigma candidates"}; \ - o2::framework::Configurable> cosPointingAngleMin{"cosPointingAngleMin", {0.0f}, "Minimum cosine of pointing angle"}; +#define KINK_DEFAULT_BITS \ + o2::framework::Configurable passThrough{"passThrough", false, "If true, all Kinks are passed through. Bits for all selections are stored."}; \ + o2::framework::Configurable> kinkTopoDcaMax{"kinkTopoDcaMax", {2.0f}, "Maximum kink topological DCA"}; \ + o2::framework::Configurable> transRadMin{"transRadMin", {19.6f}, "Minimum transverse radius (cm)"}; \ + o2::framework::Configurable> transRadMax{"transRadMax", {100.f}, "Maximum transverse radius (cm)"}; \ + o2::framework::Configurable> dauAbsEtaMax{"dauAbsEtaMax", {1.0f}, "Maximum absolute pseudorapidity for daughter track"}; \ + o2::framework::Configurable> dauDcaPvMin{"dauDcaPvMin", {0.1f}, "Minimum DCA of daughter from primary vertex (cm)"}; \ + o2::framework::Configurable> mothDcaPvMax{"mothDcaPvMax", {1.0f}, "Maximum DCA of mother from primary vertex (cm)"}; \ + o2::framework::Configurable> qtAPMin{"qtAPMin", {0.15f}, "Minimum qT_AP for Sigma candidates"}; \ + o2::framework::Configurable> qtAPMax{"qtAPMax", {0.2f}, "Maximum qT_AP for Sigma candidates"}; \ + o2::framework::Configurable> cosPointingAngleMin{"cosPointingAngleMin", {0.0f}, "Minimum cosine of pointing angle"}; \ + o2::framework::Configurable> ptOriginalMin{"ptOriginalMin", {1.2f}, "Minimum original (not recalculated) pT of the mother (GeV/c)"}; \ + o2::framework::Configurable> ptOriginalMax{"ptOriginalMax", {10.f}, "Maximum original (not recalculated) pT of the mother (GeV/c)"}; // derived selection bits for sigma struct ConfSigmaBits : o2::framework::ConfigurableGroup { std::string prefix = std::string("SigmaBits"); KINK_DEFAULT_BITS + o2::framework::Configurable> alphaAPMin{"alphaAPMin", {-1.0f}, "Minimum Alpha_AP for Sigma candidates"}; + o2::framework::Configurable> alphaAPMax{"alphaAPMax", {0.0f}, "Maximum Alpha_AP for Sigma candidates"}; o2::framework::Configurable> chaDauTpcPion{"chaDauTpcPion", {5.f}, "Maximum |nsigma_Pion| TPC for charged daughter tracks"}; }; @@ -85,25 +92,42 @@ struct ConfSigmaBits : o2::framework::ConfigurableGroup { struct ConfSigmaPlusBits : o2::framework::ConfigurableGroup { std::string prefix = std::string("SigmaPlusBits"); KINK_DEFAULT_BITS + o2::framework::Configurable> alphaAPMin{"alphaAPMin", {0.0f}, "Minimum Alpha_AP for SigmaPlus candidates"}; + o2::framework::Configurable> alphaAPMax{"alphaAPMax", {1.0f}, "Maximum Alpha_AP for SigmaPlus candidates"}; o2::framework::Configurable> chaDauTpcProton{"chaDauTpcProton", {5.f}, "Maximum |nsigma_Proton| TPC for charged daughter tracks"}; - o2::framework::Configurable> chaDauTofProton{"chaDauTofProton", {5.f}, "Maximum combined |nsigma_Proton| (TPC+TOF) for charged daughter tracks"}; - o2::framework::Configurable pidThres{"pidThres", 0.75f, "Momentum threshold for using TOF/combined pid for daughter tracks (GeV/c)"}; + o2::framework::Configurable> chaDauTofProton{"chaDauTofProton", {}, "Maximum |nsigma_Proton| TOF for charged daughter tracks"}; + o2::framework::Configurable requireTof{"requireTof", false, "If true, TOF PID is a minimal selection. If false, TOF PID is optional"}; + o2::framework::Configurable keepTracksWithoutTof{"keepTracksWithoutTof", true, "If true, the bit mask for the TOF selection will be true for all limits if the daughter track has no TOF"}; }; #undef KINK_DEFAULT_BITS +// selection bits for sigma plus reconstructed with a PCM photon +struct ConfSigmaPlusPcmBits : o2::framework::ConfigurableGroup { + std::string prefix = std::string("SigmaPlusPcmBits"); + o2::framework::Configurable passThrough{"passThrough", false, "If true, all SigmaPlusPcm are passed through. Bits for all selections are stored."}; + o2::framework::Configurable> flightDirTiltMax{"flightDirTiltMax", {0.05f}, "Maximum tilt of the flight direction (rad)"}; + o2::framework::Configurable> mothDcaPvMax{"mothDcaPvMax", {1.0f}, "Maximum DCA of mother from primary vertex (cm)"}; + o2::framework::Configurable> chaDauDcaZMax{"chaDauDcaZMax", {0.3f}, "Maximum |DCAz| of charged daughter from primary vertex (cm)"}; + o2::framework::Configurable> photonDcaZMax{"photonDcaZMax", {2.0f}, "Maximum |DCAz| of the photon from primary vertex (cm)"}; + o2::framework::Configurable> chaDauTpcProton{"chaDauTpcProton", {3.f}, "Maximum |nsigma_Proton| TPC for charged daughter tracks"}; + o2::framework::Configurable> chaDauTofProton{"chaDauTofProton", {}, "Maximum |nsigma_Proton| TOF for charged daughter tracks"}; + o2::framework::Configurable requireTof{"requireTof", false, "If true, TOF PID is a minimal selection. If false, TOF PID is optional"}; + o2::framework::Configurable keepTracksWithoutTof{"keepTracksWithoutTof", true, "If true, the bit mask for the TOF selection will be true for all limits if the daughter track has no TOF"}; +}; + // base selection for analysis task for kinks // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define KINK_DEFAULT_SELECTIONS(defaultMassMin, defaultMassMax, defaultPdgCode) \ - o2::framework::Configurable pdgCodeAbs{"pdgCodeAbs", (defaultPdgCode), "PDG code. Select antipartilce via sign"}; \ + o2::framework::Configurable pdgCodeAbs{"pdgCodeAbs", (defaultPdgCode), "PDG code. Select antiparticle via sign"}; \ o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; \ o2::framework::Configurable ptMax{"ptMax", 999.f, "Maximum pT"}; \ o2::framework::Configurable etaMin{"etaMin", -10.f, "Minimum eta"}; \ o2::framework::Configurable etaMax{"etaMax", 10.f, "Maximum eta"}; \ o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; \ o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; \ - o2::framework::Configurable massMin{"massMin", (defaultMassMin), "Minimum invariant mass for Sigma"}; \ - o2::framework::Configurable massMax{"massMax", (defaultMassMax), "Maximum invariant mass for Sigma"}; \ + o2::framework::Configurable massMin{"massMin", (defaultMassMin), "Minimum invariant mass"}; \ + o2::framework::Configurable massMax{"massMax", (defaultMassMax), "Maximum invariant mass"}; \ o2::framework::Configurable mask{"mask", 0x0, "Bitmask for kink selection"}; // base selection for analysis task for sigmas @@ -122,6 +146,15 @@ struct ConfSigmaPlusSelection : o2::framework::ConfigurableGroup { o2::framework::Configurable sign{"sign", 1, "Sign of the Sigma mother track (e.g. +1 for Sigma+ or -1 for AntiSigma+)"}; }; +// base selection for analysis task for sigma plus reconstructed with a PCM photon +template +struct ConfSigmaPlusPcmSelection : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + KINK_DEFAULT_SELECTIONS(1.1, 1.3, 3222) + o2::framework::Configurable sign{"sign", 1, "Sign of the Sigma candidate (e.g. +1 for Sigma+ or -1 for AntiSigma+)"}; + o2::framework::Configurable mlScoreMin{"mlScoreMin", -1.f, "Minimum BDT score (-1: no cut)"}; +}; + #undef KINK_DEFAULT_SELECTIONS constexpr const char PrefixSigmaSelection1[] = "SigmaSelection1"; @@ -132,6 +165,10 @@ constexpr const char PrefixSigmaPlusSelection1[] = "SigmaPlusSelection1"; constexpr const char PrefixSigmaPlusSelection2[] = "SigmaPlusSelection2"; using ConfSigmaPlusSelection1 = ConfSigmaPlusSelection; using ConfSigmaPlusSelection2 = ConfSigmaPlusSelection; +constexpr const char PrefixSigmaPlusPcmSelection1[] = "SigmaPlusPcmSelection1"; +constexpr const char PrefixSigmaPlusPcmSelection2[] = "SigmaPlusPcmSelection2"; +using ConfSigmaPlusPcmSelection1 = ConfSigmaPlusPcmSelection; +using ConfSigmaPlusPcmSelection2 = ConfSigmaPlusPcmSelection; /// The different selections for kinks enum KinkSeles { @@ -153,11 +190,19 @@ enum KinkSeles { kQtAPMax, kCosPointingAngleMin, + kPtOriginalMin, + kPtOriginalMax, + + kFlightDirTiltMax, + kChaDauDcaZMax, + kPhotonDcaZMax, + kKinkSelsMax }; constexpr char SigmaSelHistName[] = "hSigmaSelection"; constexpr char SigmaPlusSelHistName[] = "hSigmaPlusSelection"; +constexpr char SigmaPlusPcmSelHistName[] = "hSigmaPlusPcmSelection"; const char kinkSelsName[] = "Kink selection object"; const std::unordered_map kinkSelectionNames = { {kKinkTopoDcaMax, "kinkTopoDcaMax"}, @@ -173,7 +218,12 @@ const std::unordered_map kinkSelectionNames = { {kAlphaAPMax, "alphaAPMax"}, {kQtAPMin, "qtAPMin"}, {kQtAPMax, "qtAPMax"}, - {kCosPointingAngleMin, "cosPointingAngleMin"}}; + {kCosPointingAngleMin, "cosPointingAngleMin"}, + {kPtOriginalMin, "ptOriginalMin"}, + {kPtOriginalMax, "ptOriginalMax"}, + {kFlightDirTiltMax, "flightDirTiltMax"}, + {kChaDauDcaZMax, "chaDauDcaZMax"}, + {kPhotonDcaZMax, "photonDcaZMax"}}; /// enum for all kink pre-filters (evaluated in checkFilters, before the selection bitmask) enum KinkFilters { @@ -185,11 +235,14 @@ enum KinkFilters { kPhiMax, kMassMin, kMassMax, + kPhiAtPv, + kMlScoreMin, kKinkFiltersMax }; constexpr char SigmaFilterHistName[] = "hSigmaFilters"; constexpr char SigmaPlusFilterHistName[] = "hSigmaPlusFilters"; +constexpr char SigmaPlusPcmFilterHistName[] = "hSigmaPlusPcmFilters"; const std::unordered_map kinkFilterNames = { {kPtMin, "ptMin"}, {kPtMax, "ptMax"}, @@ -198,7 +251,9 @@ const std::unordered_map kinkFilterNames = { {kPhiMin, "phiMin"}, {kPhiMax, "phiMax"}, {kMassMin, "massMin"}, - {kMassMax, "massMax"}}; + {kMassMax, "massMax"}, + {kPhiAtPv, "kinkPhiAtPv"}, + {kMlScoreMin, "mlScoreMin"}}; /// \class KinkCuts /// \brief Cut class to contain and execute all cuts applied to kinks @@ -220,6 +275,7 @@ class KinkSelection : public baseselection::BaseSelectionaddSelection(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); + mKeepTracksWithoutTof = config.keepTracksWithoutTof.value; + this->addSelection(kChaDaughTpcProton, kinkSelectionNames.at(kChaDaughTpcProton), config.chaDauTpcProton.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kChaDaughTofProton, kinkSelectionNames.at(kChaDaughTofProton), config.chaDauTofProton.value, limits::kAbsUpperLimit, true, config.requireTof.value, false); + this->addComments(kChaDaughTofProton, "requireTof = " + std::to_string(config.requireTof.value) + "; keepTracksWithoutTof = " + std::to_string(mKeepTracksWithoutTof)); } this->addSelection(kKinkTopoDcaMax, kinkSelectionNames.at(kKinkTopoDcaMax), config.kinkTopoDcaMax.value, limits::kUpperLimit, true, true, false); @@ -247,6 +304,8 @@ class KinkSelection : public baseselection::BaseSelectionaddSelection(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->addSelection(kPtOriginalMin, kinkSelectionNames.at(kPtOriginalMin), config.ptOriginalMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kPtOriginalMax, kinkSelectionNames.at(kPtOriginalMax), config.ptOriginalMax.value, limits::kUpperLimit, true, true, false); this->setupSelectionHistogram(registry); @@ -262,6 +321,7 @@ class KinkSelection : public baseselection::BaseSelection(mKinkPhiAtPv)}, }); } if constexpr (modes::isEqual(kinkType, modes::Kink::kSigmaPlus)) { @@ -276,12 +336,13 @@ class KinkSelection : public baseselection::BaseSelection(mKinkPhiAtPv)}, }); } }; template - void computeKinkKinematics(T1 const& kinkCand, T2 const& /*tracks*/, T3 const& col) + void computeKinkKinematics(T1 const& kinkCand, T2 const& /*tracks*/, T3 const& /*col*/) { std::array momMother = {kinkCand.pxMoth(), kinkCand.pyMoth(), kinkCand.pzMoth()}; float kinkMomP = RecoDecay::p(momMother); @@ -300,12 +361,11 @@ class KinkSelection : public baseselection::BaseSelection 0.f) ? std::sqrt(std::max(0.f, p2A - dp * dp / p2V0)) : 0.f; - std::array vMother = {kinkCand.xDecVtx() - col.posX(), kinkCand.yDecVtx() - col.posY(), kinkCand.zDecVtx() - col.posZ()}; + std::array vMother = {kinkCand.xDecVtx(), kinkCand.yDecVtx(), kinkCand.zDecVtx()}; float vMotherNorm = std::sqrt(std::inner_product(vMother.begin(), vMother.end(), vMother.begin(), 0.f)); mCosPointingAngle = (vMotherNorm > 0.f && kinkMomP > 0.f) ? (std::inner_product(momMother.begin(), momMother.end(), vMother.begin(), 0.f)) / (kinkMomP * vMotherNorm) : 0.f; mTransRadius = std::hypot(kinkCand.xDecVtx(), kinkCand.yDecVtx()); - mKinkDauP = kinkDauP; mKinkDauEta = RecoDecay::eta(momDaughter); mKinkAngle = 0.f; @@ -327,6 +387,9 @@ class KinkSelection : public baseselection::BaseSelectionevaluateObservable(kQtAPMin, mQtAp); this->evaluateObservable(kQtAPMax, mQtAp); this->evaluateObservable(kCosPointingAngleMin, mCosPointingAngle); + // the stored pT is the recalculated one, the original pT is only available as selection bits + this->evaluateObservable(kPtOriginalMin, mKinkMotherPtOriginal); + this->evaluateObservable(kPtOriginalMax, mKinkMotherPtOriginal); this->evaluateObservable(kKinkTopoDcaMax, kinkCand.dcaKinkTopo()); // Compute transRadius @@ -345,14 +408,11 @@ class KinkSelection : public baseselection::BaseSelectionevaluateObservable(kChaDaughTpcPion, chaDaughter.tpcNSigmaPi()); } if constexpr (modes::isEqual(kinkType, modes::Kink::kSigmaPlus)) { - if (mKinkDauP < mPidThreshold) { - this->evaluateObservable(kChaDaughTpcProton, chaDaughter.tpcNSigmaPr()); + this->evaluateObservable(kChaDaughTpcProton, chaDaughter.tpcNSigmaPr()); + if (chaDaughter.hasTOF()) { + this->evaluateObservable(kChaDaughTofProton, chaDaughter.tofNSigmaPr()); } else { - if (chaDaughter.hasTOF()) { - this->evaluateObservable(kChaDaughTofProton, std::abs(chaDaughter.tofNSigmaPr())); - } else { - this->evaluateObservable(kChaDaughTofProton, 999.f); - } + this->evaluateObservable(kChaDaughTofProton, mKeepTracksWithoutTof ? 0.f : 999.f); } } @@ -367,10 +427,24 @@ class KinkSelection : public baseselection::BaseSelection 0.f) ? ptRecalc : std::hypot(momMother[0], momMother[1]); + // Recalculate pT using kinematic constraints of the decay channel + float ptRecalc = -999.f; + if constexpr (modes::isEqual(kinkType, modes::Kink::kSigma)) { + // Sigma- -> pi- n + ptRecalc = utils::calcPtnew(momMother[0], momMother[1], momMother[2], momDaughter[0], momDaughter[1], momDaughter[2], + o2::constants::physics::MassSigmaMinus, o2::constants::physics::MassPionCharged, o2::constants::physics::MassNeutron); + } + if constexpr (modes::isEqual(kinkType, modes::Kink::kSigmaPlus)) { + // Sigma+ -> p pi0 + ptRecalc = utils::calcPtnew(momMother[0], momMother[1], momMother[2], momDaughter[0], momDaughter[1], momDaughter[2], + o2::constants::physics::MassSigmaPlus, o2::constants::physics::MassProton, o2::constants::physics::MassPionNeutral); + } + mKinkMotherPtOriginal = std::hypot(momMother[0], momMother[1]); + mKinkMotherPt = (ptRecalc > 0.f) ? ptRecalc : mKinkMotherPtOriginal; } template @@ -403,6 +477,8 @@ class KinkSelection : public baseselection::BaseSelectiontemplate fillFilter(kPhiMax, p); pass &= p; + this->template fillFilter(kPhiAtPv, mKinkPhiAtPv); + if constexpr (modes::isEqual(kinkType, modes::Kink::kSigma)) { float sigmaMass = kinkCand.mSigmaMinus(); @@ -443,7 +519,7 @@ class KinkSelection : public baseselection::BaseSelection producedLiteSigmaPlus; o2::framework::Produces producedSigmaPlusMasks; o2::framework::Produces producedSigmaPlusExtras; + o2::framework::Produces producedSigmaPlusPcm; + o2::framework::Produces producedLiteSigmaPlusPcm; + o2::framework::Produces producedSigmaPlusPcmMasks; + o2::framework::Produces producedSigmaPlusPcmMlScores; + o2::framework::Produces producedSigmaPlusPcmExtras; }; struct ConfKinkTables : o2::framework::ConfigurableGroup { @@ -489,6 +571,11 @@ struct ConfKinkTables : o2::framework::ConfigurableGroup { o2::framework::Configurable produceLiteSigmaPlus{"produceLiteSigmaPlus", -1, "Produce LiteSigmaPlus (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceSigmaPlusMasks{"produceSigmaPlusMasks", -1, "Produce SigmaPlusMasks (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceSigmaPlusExtras{"produceSigmaPlusExtras", -1, "Produce SigmaPlusExtras (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceSigmaPlusPcm{"produceSigmaPlusPcm", -1, "Produce SigmaPlusPcm (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceLiteSigmaPlusPcm{"produceLiteSigmaPlusPcm", -1, "Produce LiteSigmaPlusPcm (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceSigmaPlusPcmMasks{"produceSigmaPlusPcmMasks", -1, "Produce SigmaPlusPcmMasks (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceSigmaPlusPcmMlScores{"produceSigmaPlusPcmMlScores", -1, "Produce SigmaPlusPcmMlScores (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceSigmaPlusPcmExtras{"produceSigmaPlusPcmExtras", -1, "Produce SigmaPlusPcmExtras (-1: auto; 0 off; 1 on)"}; }; template @@ -530,7 +617,7 @@ class KinkBuilder mProduceSigmaPlus = utils::enableTable("FSigmaPlus_001", table.produceSigmaPlus.value, initContext); mProduceLiteSigmaPlus = utils::enableTable("FLiteSigmaPlus_001", table.produceLiteSigmaPlus.value, initContext); mProduceSigmaPlusMasks = utils::enableTable("FSigmaPlusMasks_001", table.produceSigmaPlusMasks.value, initContext); - mProduceSigmaPlusExtras = utils::enableTable("FSigmaPlusExtras_001", table.produceSigmaPlusExtras.value, initContext); + mProduceSigmaPlusExtras = utils::enableTable("FSigmaPlusExtras_002", table.produceSigmaPlusExtras.value, initContext); if (mProduceSigmaPlus && mProduceLiteSigmaPlus) { LOG(fatal) << "FSigmaPlus and FLiteSigmaPlus are mutually exclusive -- enable only one. " @@ -549,7 +636,8 @@ class KinkBuilder } } - if (mProduceSigmas || mProduceSigmaMasks || mProduceSigmaExtras || mProduceSigmaPlus || mProduceSigmaPlusMasks || mProduceSigmaPlusExtras) { + if (mProduceSigmas || mProduceLiteSigmas || mProduceSigmaMasks || mProduceSigmaExtras || + mProduceSigmaPlus || mProduceLiteSigmaPlus || mProduceSigmaPlusMasks || mProduceSigmaPlusExtras) { mFillAnyTable = true; } else { LOG(info) << "No tables configured, Selection object will not be configured..."; @@ -590,7 +678,8 @@ class KinkBuilder fillSigma(collisionBuilder, kinkProducts, kink, daughterIndex); } if constexpr (modes::isEqual(kinkType, modes::Kink::kSigmaPlus)) { - fillSigmaPlus(collisionBuilder, kinkProducts, kink, daughterIndex); + auto mother = kink.template trackMoth_as(); + fillSigmaPlus(collisionBuilder, kinkProducts, kink, daughter, mother, daughterIndex); } } } @@ -626,7 +715,8 @@ class KinkBuilder mcBuilder.template fillMcSigmaWithLabel(daughter, mcParticles, mcCols, mcProducts); } if constexpr (modes::isEqual(kinkType, modes::Kink::kSigmaPlus)) { - fillSigmaPlus(collisionBuilder, kinkProducts, kink, daughterIndex); + auto mother = kink.template trackMoth_as(); + fillSigmaPlus(collisionBuilder, kinkProducts, kink, daughter, mother, daughterIndex); mcBuilder.template fillMcSigmaPlusWithLabel(daughter, mcParticles, mcCols, mcProducts); } } @@ -670,8 +760,8 @@ class KinkBuilder } } - template - void fillSigmaPlus(T1& collisionBuilder, T2& kinkProducts, T3 const& kink, int64_t daughterIndex) + template + void fillSigmaPlus(T1& collisionBuilder, T2& kinkProducts, T3 const& kink, T4 const& daughter, T5 const& mother, int64_t daughterIndex) { // Mass is calculated from original momentum components stored in kink table float mass = kink.mSigmaPlus(); @@ -703,7 +793,13 @@ class KinkBuilder kink.xDecVtx(), kink.yDecVtx(), kink.zDecVtx(), - mKinkSelection.getKinkTransRadius()); + mKinkSelection.getKinkTransRadius(), + kink.dcaKinkTopo(), + daughter.tpcNSigmaPr(), + daughter.tpcNSigmaPi(), + daughter.tofNSigmaPr(), + kink.ptMoth(), + mother.itsClusterSizes()); } } @@ -723,10 +819,328 @@ class KinkBuilder bool mProduceSigmaPlusExtras = false; }; +/// \class SigmaPlusPcmSelection +/// \brief Cut class to contain and execute all cuts applied to sigma plus reconstructed with a PCM photon +template +class SigmaPlusPcmSelection : public baseselection::BaseSelection +{ + public: + SigmaPlusPcmSelection() = default; + ~SigmaPlusPcmSelection() override = default; + + template + void configure(o2::framework::HistogramRegistry* registry, T1& config, T2& filter) + { + this->init(config.passThrough.value); + + mPtMin = filter.ptMin.value; + mPtMax = filter.ptMax.value; + mEtaMin = filter.etaMin.value; + mEtaMax = filter.etaMax.value; + mPhiMin = filter.phiMin.value; + mPhiMax = filter.phiMax.value; + mPhiAtPv = filter.kinkPhiAtPv.value; + mMassLowerLimit = filter.massMinSigmaPlusPcm.value; + mMassUpperLimit = filter.massMaxSigmaPlusPcm.value; + mMlScoreMin = filter.mlScoreMinSigmaPlusPcm.value; + mKeepTracksWithoutTof = config.keepTracksWithoutTof.value; + + this->addSelection(kFlightDirTiltMax, kinkSelectionNames.at(kFlightDirTiltMax), config.flightDirTiltMax.value, limits::kUpperLimit, true, true, false); + this->addSelection(kMothDcaPvMax, kinkSelectionNames.at(kMothDcaPvMax), config.mothDcaPvMax.value, limits::kUpperLimit, true, true, false); + this->addSelection(kChaDauDcaZMax, kinkSelectionNames.at(kChaDauDcaZMax), config.chaDauDcaZMax.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kPhotonDcaZMax, kinkSelectionNames.at(kPhotonDcaZMax), config.photonDcaZMax.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kChaDaughTpcProton, kinkSelectionNames.at(kChaDaughTpcProton), config.chaDauTpcProton.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kChaDaughTofProton, kinkSelectionNames.at(kChaDaughTofProton), config.chaDauTofProton.value, limits::kAbsUpperLimit, true, config.requireTof.value, false); + this->addComments(kChaDaughTofProton, "requireTof = " + std::to_string(config.requireTof.value) + "; keepTracksWithoutTof = " + std::to_string(mKeepTracksWithoutTof)); + + this->setupSelectionHistogram(registry); + this->template setupFilterHistogram( + registry, + { + {kinkFilterNames.at(kPtMin), mPtMin}, + {kinkFilterNames.at(kPtMax), mPtMax}, + {kinkFilterNames.at(kEtaMin), mEtaMin}, + {kinkFilterNames.at(kEtaMax), mEtaMax}, + {kinkFilterNames.at(kPhiMin), mPhiMin}, + {kinkFilterNames.at(kPhiMax), mPhiMax}, + {kinkFilterNames.at(kMassMin), mMassLowerLimit}, + {kinkFilterNames.at(kMassMax), mMassUpperLimit}, + {kinkFilterNames.at(kPhiAtPv), static_cast(mPhiAtPv)}, + {kinkFilterNames.at(kMlScoreMin), mMlScoreMin}, + }); + }; + + template + void computeKinematics(T1 const& sigmaPlusPcmCand, T2 const& col) + { + std::array momMother = {sigmaPlusPcmCand.pxSigmaPlus(), sigmaPlusPcmCand.pySigmaPlus(), sigmaPlusPcmCand.pzSigmaPlus()}; + mPt = RecoDecay::pt(momMother); + mEta = RecoDecay::eta(momMother); + mPhi = RecoDecay::phi(momMother); + if (mPhiAtPv) { + mPhi = utils::phiAtPrimaryVertex(mPhi, sigmaPlusPcmCand.xDecVtx() - col.posX(), sigmaPlusPcmCand.yDecVtx() - col.posY()); + } + mMass = sigmaPlusPcmCand.massSigmaPlus(); + mMlScore = sigmaPlusPcmCand.mlScore(); + } + + template + bool checkFilters(T const& /*sigmaPlusPcmCand*/) const + { + bool pass = true; + bool p = false; + + p = mPt > mPtMin; + this->template fillFilter(kPtMin, p); + pass &= p; + + p = mPt < mPtMax; + this->template fillFilter(kPtMax, p); + pass &= p; + + p = mEta > mEtaMin; + this->template fillFilter(kEtaMin, p); + pass &= p; + + p = mEta < mEtaMax; + this->template fillFilter(kEtaMax, p); + pass &= p; + + p = mPhi > mPhiMin; + this->template fillFilter(kPhiMin, p); + pass &= p; + + p = mPhi < mPhiMax; + this->template fillFilter(kPhiMax, p); + pass &= p; + + this->template fillFilter(kPhiAtPv, mPhiAtPv); + + p = mMass > mMassLowerLimit; + this->template fillFilter(kMassMin, p); + pass &= p; + + p = mMass < mMassUpperLimit; + this->template fillFilter(kMassMax, p); + pass &= p; + + p = mMlScore >= mMlScoreMin; + this->template fillFilter(kMlScoreMin, p); + pass &= p; + + this->template fillFilterSummary(pass); + + return this->isPassThrough() || pass; + } + + template + void applySelections(T1 const& sigmaPlusPcmCand, T2 const& /*tracks*/) + { + this->reset(); + this->evaluateObservable(kFlightDirTiltMax, sigmaPlusPcmCand.flightDirTilt()); + this->evaluateObservable(kMothDcaPvMax, sigmaPlusPcmCand.candDcaToPV()); + this->evaluateObservable(kChaDauDcaZMax, sigmaPlusPcmCand.protonDcaZ()); + this->evaluateObservable(kPhotonDcaZMax, sigmaPlusPcmCand.photonDcaZToPV()); + + auto chaDaughter = sigmaPlusPcmCand.template protonTrack_as(); + this->evaluateObservable(kChaDaughTpcProton, chaDaughter.tpcNSigmaPr()); + if (chaDaughter.hasTOF()) { + this->evaluateObservable(kChaDaughTofProton, chaDaughter.tofNSigmaPr()); + } else { + this->evaluateObservable(kChaDaughTofProton, mKeepTracksWithoutTof ? 0.f : 999.f); + } + + this->assembleBitmask(); + }; + + [[nodiscard]] float getPt() const { return mPt; } + [[nodiscard]] float getEta() const { return mEta; } + [[nodiscard]] float getPhi() const { return mPhi; } + [[nodiscard]] float getMass() const { return mMass; } + + private: + float mMassLowerLimit = 1.1f; + float mMassUpperLimit = 1.3f; + float mMlScoreMin = -1.f; + bool mKeepTracksWithoutTof = true; + + // kinematic filters + float mPtMin = 0.f; + float mPtMax = 6.f; + float mEtaMin = -1.f; + float mEtaMax = 1.f; + float mPhiMin = 0.f; + bool mPhiAtPv = true; + float mPhiMax = o2::constants::math::TwoPI; + + // mother kinematic + float mPt = 0.f; + float mEta = 0.f; + float mPhi = 0.f; + float mMass = 0.f; + float mMlScore = 0.f; +}; + +template +class SigmaPlusPcmBuilder +{ + public: + SigmaPlusPcmBuilder() = default; + ~SigmaPlusPcmBuilder() = default; + + template + void init(o2::framework::HistogramRegistry* registry, T1& config, T2& filter, T3& table, T4& initContext, T5& trackBuilder) + { + LOG(info) << "Initialize femto SigmaPlusPcm builder..."; + mProduceSigmaPlusPcm = utils::enableTable("FSigmaPlusPcm_001", table.produceSigmaPlusPcm.value, initContext); + mProduceLiteSigmaPlusPcm = utils::enableTable("FLiteSigmaPlusPcm_001", table.produceLiteSigmaPlusPcm.value, initContext); + mProduceSigmaPlusPcmMasks = utils::enableTable("FSigmaPlusPcmMasks_001", table.produceSigmaPlusPcmMasks.value, initContext); + mProduceSigmaPlusPcmMlScores = utils::enableTable("FSigmaPlusPcmMlScores_001", table.produceSigmaPlusPcmMlScores.value, initContext); + mProduceSigmaPlusPcmExtras = utils::enableTable("FSigmaPlusPcmExtras_001", table.produceSigmaPlusPcmExtras.value, initContext); + + if (mProduceSigmaPlusPcm && mProduceLiteSigmaPlusPcm) { + LOG(fatal) << "FSigmaPlusPcm and FLiteSigmaPlusPcm are mutually exclusive -- enable only one. " + << "FLiteSigmaPlusPcm is meant to replace FSigmaPlusPcm at the producer stage (for better compression in derived data); " + << "use the dedicated converter task to reconstruct FSigmaPlusPcm from FLiteSigmaPlusPcm downstream."; + } + if (mProduceSigmaPlusPcm && !trackBuilder.producingTracks()) { + LOG(fatal) << "FSigmaPlusPcm is enabled, but the track builder is not producing FTracks (full precision). " + << "FSigmaPlusPcm stores the daughter index into FTracks -- enable TrackTables.produceTracks, " + << "or switch to FLiteSigmaPlusPcm if TrackTables.produceLiteTracks is enabled instead."; + } + if (mProduceLiteSigmaPlusPcm && !trackBuilder.producingLiteTracks()) { + LOG(fatal) << "FLiteSigmaPlusPcm is enabled, but the track builder is not producing FLiteTracks. " + << "FLiteSigmaPlusPcm stores the daughter index into FLiteTracks -- enable TrackTables.produceLiteTracks, " + << "or switch to FSigmaPlusPcm if TrackTables.produceTracks is enabled instead."; + } + + if (mProduceSigmaPlusPcm || mProduceLiteSigmaPlusPcm || mProduceSigmaPlusPcmMasks || mProduceSigmaPlusPcmMlScores || mProduceSigmaPlusPcmExtras) { + mFillAnyTable = true; + } else { + LOG(info) << "No tables configured, Selection object will not be configured..."; + LOG(info) << "Initialization done..."; + return; + } + mSigmaPlusPcmSelection.configure(registry, config, filter); + mSigmaPlusPcmSelection.printSelections(kinkSelsName); + LOG(info) << "Initialization done..."; + } + + template + void fillSigmaPlusPcms(T1 const& col, T2& collisionBuilder, T3& collisionProducts, T4& trackProducts, T5& kinkProducts, T6 const& sigmaPlusPcms, T7 const& tracks, T8& trackBuilder) + { + if (!mFillAnyTable) { + return; + } + int64_t daughterIndex = 0; + + for (const auto& sigmaPlusPcm : sigmaPlusPcms) { + mSigmaPlusPcmSelection.computeKinematics(sigmaPlusPcm, col); + if (!mSigmaPlusPcmSelection.checkFilters(sigmaPlusPcm)) { + continue; + } + mSigmaPlusPcmSelection.applySelections(sigmaPlusPcm, tracks); + if (!mSigmaPlusPcmSelection.passesAllRequiredSelections()) { + continue; + } + + collisionBuilder.template fillCollision(collisionProducts, col); + + auto daughter = sigmaPlusPcm.template protonTrack_as(); + daughterIndex = trackBuilder.template getDaughterIndex(daughter, trackProducts, collisionBuilder); + fillSigmaPlusPcm(collisionBuilder, kinkProducts, sigmaPlusPcm, daughterIndex); + } + } + + template + void fillMcSigmaPlusPcms(T1 const& col, T2& collisionBuilder, T3& collisionProducts, T4 const& mcCols, T5& trackProducts, T6& kinkProducts, T7 const& sigmaPlusPcms, T8 const& tracks, T9& trackBuilder, T10 const& mcParticles, T11& mcBuilder, T12& mcProducts) + { + if (!mFillAnyTable) { + return; + } + int64_t daughterIndex = 0; + + for (const auto& sigmaPlusPcm : sigmaPlusPcms) { + mSigmaPlusPcmSelection.computeKinematics(sigmaPlusPcm, col); + if (!mSigmaPlusPcmSelection.checkFilters(sigmaPlusPcm)) { + continue; + } + mSigmaPlusPcmSelection.applySelections(sigmaPlusPcm, tracks); + if (!mSigmaPlusPcmSelection.passesAllRequiredSelections()) { + continue; + } + + collisionBuilder.template fillMcCollision(collisionProducts, col, mcCols, mcProducts, mcBuilder); + + auto daughter = sigmaPlusPcm.template protonTrack_as(); + daughterIndex = trackBuilder.template getDaughterIndex(daughter, trackProducts, mcCols, collisionBuilder, mcParticles, mcBuilder, mcProducts); + fillSigmaPlusPcm(collisionBuilder, kinkProducts, sigmaPlusPcm, daughterIndex); + mcBuilder.template fillMcSigmaPlusPcmWithLabel(sigmaPlusPcm, daughter, mcParticles, mcCols, mcProducts); + } + } + + template + void fillSigmaPlusPcm(T1& collisionBuilder, T2& kinkProducts, T3 const& sigmaPlusPcm, int64_t daughterIndex) + { + float signedPt = sigmaPlusPcm.protonSign() * mSigmaPlusPcmSelection.getPt(); + if (mProduceSigmaPlusPcm) { + kinkProducts.producedSigmaPlusPcm(collisionBuilder.collisionIndex(), + signedPt, + mSigmaPlusPcmSelection.getEta(), + mSigmaPlusPcmSelection.getPhi(), + mSigmaPlusPcmSelection.getMass(), + daughterIndex); + } + if (mProduceLiteSigmaPlusPcm) { + kinkProducts.producedLiteSigmaPlusPcm(collisionBuilder.collisionIndex(), + o2::aod::femtobase::lite::binSignedPt(signedPt), + o2::aod::femtobase::lite::binEta(mSigmaPlusPcmSelection.getEta()), + o2::aod::femtobase::lite::binPhi(mSigmaPlusPcmSelection.getPhi()), + o2::aod::femtokinks::lite::binSigmaMass(mSigmaPlusPcmSelection.getMass()), + daughterIndex); + } + if (mProduceSigmaPlusPcmMasks) { + kinkProducts.producedSigmaPlusPcmMasks(mSigmaPlusPcmSelection.getBitmask()); + } + if (mProduceSigmaPlusPcmMlScores) { + kinkProducts.producedSigmaPlusPcmMlScores(sigmaPlusPcm.mlScore()); + } + if (mProduceSigmaPlusPcmExtras) { + kinkProducts.producedSigmaPlusPcmExtras( + sigmaPlusPcm.flightDirTilt(), + sigmaPlusPcm.candDcaToPV(), + sigmaPlusPcm.protonDcaXY(), + sigmaPlusPcm.protonDcaZ(), + sigmaPlusPcm.nSigmaTPCProton(), + sigmaPlusPcm.nSigmaTOFProton(), + sigmaPlusPcm.photonDcaXYToPV(), + sigmaPlusPcm.photonDcaZToPV(), + sigmaPlusPcm.photonCosPAToPV(), + sigmaPlusPcm.photonConvRadius(), + sigmaPlusPcm.antiSigmaPointingAngle(), + sigmaPlusPcm.dcaProtonGamma(), + sigmaPlusPcm.radius()); + } + } + + [[nodiscard]] bool fillAnyTable() const { return mFillAnyTable; } + [[nodiscard]] bool isPassThrough() const { return mSigmaPlusPcmSelection.isPassThrough(); } + + private: + SigmaPlusPcmSelection mSigmaPlusPcmSelection; + bool mFillAnyTable = false; + bool mProduceSigmaPlusPcm = false; + bool mProduceLiteSigmaPlusPcm = false; + bool mProduceSigmaPlusPcmMasks = false; + bool mProduceSigmaPlusPcmMlScores = false; + bool mProduceSigmaPlusPcmExtras = false; +}; + struct ConfKinkTablesDerivedToDerived : o2::framework::ConfigurableGroup { std::string prefix = std::string("KinkTables"); o2::framework::Configurable limitSigma{"limitSigma", 1, "Require at least this many Sigmas in the collision. Set to 0 to skip Sigmas entirely (not written to output)."}; o2::framework::Configurable limitSigmaPlus{"limitSigmaPlus", 0, "Require at least this many SigmaPlus in the collision. Set to 0 to skip SigmaPlus entirely (not written to output)."}; + o2::framework::Configurable limitSigmaPlusPcm{"limitSigmaPlusPcm", 0, "Require at least this many SigmaPlusPcm in the collision. Set to 0 to skip SigmaPlusPcm entirely (not written to output)."}; }; struct KinkBuilderDerivedToDerivedProducts : o2::framework::ProducesGroup { @@ -734,6 +1148,11 @@ struct KinkBuilderDerivedToDerivedProducts : o2::framework::ProducesGroup { o2::framework::Produces producedSigmaMasks; o2::framework::Produces producedSigmaPluses; o2::framework::Produces producedSigmaPlusMasks; + o2::framework::Produces producedSigmaPlusExtras; + o2::framework::Produces producedSigmaPlusPcms; + o2::framework::Produces producedSigmaPlusPcmMasks; + o2::framework::Produces producedSigmaPlusPcmMlScores; + o2::framework::Produces producedSigmaPlusPcmExtras; }; class KinkBuilderDerivedToDerived @@ -744,12 +1163,13 @@ class KinkBuilderDerivedToDerived { mLimitSigma = config.limitSigma.value; mLimitSigmaPlus = config.limitSigmaPlus.value; + mLimitSigmaPlusPcm = config.limitSigmaPlusPcm.value; - if (mLimitSigma < 0 || mLimitSigmaPlus < 0) { - LOG(fatal) << "Kink limits must be non-negative (got " << mLimitSigma << " and " << mLimitSigmaPlus << "). Breaking..."; + if (mLimitSigma < 0 || mLimitSigmaPlus < 0 || mLimitSigmaPlusPcm < 0) { + LOG(fatal) << "Kink limits must be non-negative (got " << mLimitSigma << ", " << mLimitSigmaPlus << " and " << mLimitSigmaPlusPcm << "). Breaking..."; } - if (mLimitSigma == 0 && mLimitSigmaPlus == 0) { - LOG(fatal) << "Both sigma limit and sigmaplus limit are 0. Breaking..."; + if (mLimitSigma == 0 && mLimitSigmaPlus == 0 && mLimitSigmaPlusPcm == 0) { + LOG(fatal) << "Sigma, sigmaplus and sigmapluspcm limits are all 0. Breaking..."; } } @@ -773,6 +1193,16 @@ class KinkBuilderDerivedToDerived return sigmaPlusSlice.size() < static_cast(mLimitSigmaPlus); } + template + bool collisionHasTooFewSigmaPlusPcm(T1 const& col, T2 const& /*sigmaPlusPcmTable*/, T3& partitionSigmaPlusPcm, T4& cache) + { + if (mLimitSigmaPlusPcm == 0) { // sigma pluses (PCM) disabled, cannot reject on them + return false; + } + auto sigmaPlusPcmSlice = partitionSigmaPlusPcm->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + return sigmaPlusPcmSlice.size() < static_cast(mLimitSigmaPlusPcm); + } + template void processSigma(T1 const& col, T2 const& /*sigmaTable*/, T3 const& oldTrackTable, T4& partitionSigma, T5& trackBuilder, T6& cache, T7& newSigmaTable, T8& newTrackTable, T9& newCollisionTable) { @@ -818,12 +1248,162 @@ class KinkBuilderDerivedToDerived sigmaPlus.mass(), chaDaughterIndex); newSigmaPlusTable.producedSigmaPlusMasks(sigmaPlus.mask()); + fillSigmaPlusExtensions(sigmaPlus, newSigmaPlusTable); + } + } + + template + void processSigmaWithMc(T1 const& col, T2 const& /*sigmaTable*/, T3 const& oldTrackTable, T4& partitionSigma, T5& trackBuilder, T6& cache, T7& newSigmaTable, T8& newTrackTable, T9& newCollisionTable, + T10& mcBuilder, T11 const& mcCols, T12 const& mcParticles, T13 const& mcMothers, T14 const& mcPartonicMothers, T15& mcProducts) + { + if (mLimitSigma == 0) { + return; + } + + auto sigmaSlice = partitionSigma->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + + for (auto const& sigma : sigmaSlice) { + auto chaDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(sigma.chaDauId(), oldTrackTable)); + + int64_t chaDaughterIndex = trackBuilder.getDaughterIndex(chaDaughter, newTrackTable, newCollisionTable, mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcProducts); + + newSigmaTable.producedSigmas(newCollisionTable.producedCollision.lastIndex(), + sigma.signedPt(), + sigma.eta(), + sigma.phi(), + sigma.mass(), + chaDaughterIndex); + newSigmaTable.producedSigmaMasks(sigma.mask()); + mcBuilder.fillSigmaWithLabel(sigma, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcProducts); + } + } + + template + void processSigmaPlusWithMc(T1 const& col, T2 const& /*sigmaPlusTable*/, T3 const& oldTrackTable, T4& partitionSigmaPlus, T5& trackBuilder, T6& cache, T7& newSigmaPlusTable, T8& newTrackTable, T9& newCollisionTable, + T10& mcBuilder, T11 const& mcCols, T12 const& mcParticles, T13 const& mcMothers, T14 const& mcPartonicMothers, T15& mcProducts) + { + if (mLimitSigmaPlus == 0) { + return; + } + + auto sigmaPlusSlice = partitionSigmaPlus->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + + for (auto const& sigmaPlus : sigmaPlusSlice) { + auto chaDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(sigmaPlus.chaDauId(), oldTrackTable)); + + int64_t chaDaughterIndex = trackBuilder.getDaughterIndex(chaDaughter, newTrackTable, newCollisionTable, mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcProducts); + + newSigmaPlusTable.producedSigmaPluses(newCollisionTable.producedCollision.lastIndex(), + sigmaPlus.signedPt(), + sigmaPlus.eta(), + sigmaPlus.phi(), + sigmaPlus.mass(), + chaDaughterIndex); + newSigmaPlusTable.producedSigmaPlusMasks(sigmaPlus.mask()); + fillSigmaPlusExtensions(sigmaPlus, newSigmaPlusTable); + mcBuilder.fillSigmaPlusWithLabel(sigmaPlus, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcProducts); + } + } + + template + void processSigmaPlusPcm(T1 const& col, T2 const& /*sigmaPlusPcmTable*/, T3 const& oldTrackTable, T4& partitionSigmaPlusPcm, T5& trackBuilder, T6& cache, T7& newSigmaPlusPcmTable, T8& newTrackTable, T9& newCollisionTable) + { + if (mLimitSigmaPlusPcm == 0) { // sigma pluses (PCM) disabled + return; + } + + auto sigmaPlusPcmSlice = partitionSigmaPlusPcm->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + + for (auto const& sigmaPlusPcm : sigmaPlusPcmSlice) { + auto chaDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(sigmaPlusPcm.chaDauId(), oldTrackTable)); + + int64_t chaDaughterIndex = trackBuilder.getDaughterIndex(chaDaughter, newTrackTable, newCollisionTable); + + newSigmaPlusPcmTable.producedSigmaPlusPcms(newCollisionTable.producedCollision.lastIndex(), + sigmaPlusPcm.signedPt(), + sigmaPlusPcm.eta(), + sigmaPlusPcm.phi(), + sigmaPlusPcm.mass(), + chaDaughterIndex); + newSigmaPlusPcmTable.producedSigmaPlusPcmMasks(sigmaPlusPcm.mask()); + fillSigmaPlusPcmExtensions(sigmaPlusPcm, newSigmaPlusPcmTable); + } + } + + template + void processSigmaPlusPcmWithMc(T1 const& col, T2 const& /*sigmaPlusPcmTable*/, T3 const& oldTrackTable, T4& partitionSigmaPlusPcm, T5& trackBuilder, T6& cache, T7& newSigmaPlusPcmTable, T8& newTrackTable, T9& newCollisionTable, + T10& mcBuilder, T11 const& mcCols, T12 const& mcParticles, T13 const& mcMothers, T14 const& mcPartonicMothers, T15& mcProducts) + { + if (mLimitSigmaPlusPcm == 0) { + return; + } + + auto sigmaPlusPcmSlice = partitionSigmaPlusPcm->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + + for (auto const& sigmaPlusPcm : sigmaPlusPcmSlice) { + auto chaDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(sigmaPlusPcm.chaDauId(), oldTrackTable)); + + int64_t chaDaughterIndex = trackBuilder.getDaughterIndex(chaDaughter, newTrackTable, newCollisionTable, mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcProducts); + + newSigmaPlusPcmTable.producedSigmaPlusPcms(newCollisionTable.producedCollision.lastIndex(), + sigmaPlusPcm.signedPt(), + sigmaPlusPcm.eta(), + sigmaPlusPcm.phi(), + sigmaPlusPcm.mass(), + chaDaughterIndex); + newSigmaPlusPcmTable.producedSigmaPlusPcmMasks(sigmaPlusPcm.mask()); + fillSigmaPlusPcmExtensions(sigmaPlusPcm, newSigmaPlusPcmTable); + mcBuilder.fillSigmaPlusPcmWithLabel(sigmaPlusPcm, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcProducts); } } private: + template + void fillSigmaPlusExtensions(T1 const& sigmaPlus, T2& newSigmaPlusTable) + { + if constexpr (requires(T1 t) { t.transRadius(); }) { + newSigmaPlusTable.producedSigmaPlusExtras(sigmaPlus.kinkAngle(), + sigmaPlus.dcaDaugToPV(), + sigmaPlus.dcaMothToPV(), + sigmaPlus.decayVtxX(), + sigmaPlus.decayVtxY(), + sigmaPlus.decayVtxZ(), + sigmaPlus.transRadius(), + sigmaPlus.dcaKinkTopo(), + sigmaPlus.dauTpcNSigmaPr(), + sigmaPlus.dauTpcNSigmaPi(), + sigmaPlus.dauTofNSigmaPr(), + sigmaPlus.ptOriginal(), + sigmaPlus.itsClusterSizes()); + } + } + + template + void fillSigmaPlusPcmExtensions(T1 const& sigmaPlusPcm, T2& newSigmaPlusPcmTable) + { + if constexpr (requires(T1 t) { t.mlScore(); }) { + newSigmaPlusPcmTable.producedSigmaPlusPcmMlScores(sigmaPlusPcm.mlScore()); + } + if constexpr (requires(T1 t) { t.photonConvRadius(); }) { + newSigmaPlusPcmTable.producedSigmaPlusPcmExtras(sigmaPlusPcm.flightDirTilt(), + sigmaPlusPcm.dcaMothToPV(), + sigmaPlusPcm.dauDcaXY(), + sigmaPlusPcm.dauDcaZ(), + sigmaPlusPcm.dauTpcNSigmaPr(), + sigmaPlusPcm.dauTofNSigmaPr(), + sigmaPlusPcm.photonDcaXYToPV(), + sigmaPlusPcm.photonDcaZToPV(), + sigmaPlusPcm.photonCosPAToPV(), + sigmaPlusPcm.photonConvRadius(), + sigmaPlusPcm.antiSigmaPointingAngle(), + sigmaPlusPcm.dcaDauPhoton(), + sigmaPlusPcm.transRadius()); + } + } + int mLimitSigma = 0; int mLimitSigmaPlus = 0; + int mLimitSigmaPlusPcm = 0; }; } // namespace o2::analysis::femto::kinkbuilder #endif // PWGCF_FEMTO_CORE_KINKBUILDER_H_ diff --git a/PWGCF/Femto/Core/kinkHistManager.h b/PWGCF/Femto/Core/kinkHistManager.h index 3e98b8865e0..2d9eed98b87 100644 --- a/PWGCF/Femto/Core/kinkHistManager.h +++ b/PWGCF/Femto/Core/kinkHistManager.h @@ -93,7 +93,7 @@ constexpr std::size_t MaxSecondary = 3; o2::framework::ConfigurableAxis phi{"phi", {{720, 0, 1.f * o2::constants::math::TwoPI}}, "Phi"}; \ o2::framework::ConfigurableAxis mass{"mass", {{200, (defaultMassMin), (defaultMassMax)}}, "Mass"}; \ o2::framework::ConfigurableAxis sign{"sign", {{3, -1.5, 1.5}}, "Sign"}; \ - o2::framework::ConfigurableAxis pdgCodes{"pdgCodes", {{8001, -4000.5, 4000.5}}, "PDG codes of selected V0s"}; + o2::framework::ConfigurableAxis pdgCodes{"pdgCodes", {{8001, -4000.5, 4000.5}}, "PDG codes of selected kinks"}; template struct ConfSigmaBinning : o2::framework::ConfigurableGroup { @@ -105,6 +105,11 @@ struct ConfSigmaPlusBinning : o2::framework::ConfigurableGroup { std::string prefix = Prefix; KINK_DEFAULT_BINNING(1.1, 1.3) }; +template +struct ConfSigmaPlusPcmBinning : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + KINK_DEFAULT_BINNING(1.1, 1.3) +}; #undef KINK_DEFAULT_BINNING @@ -114,6 +119,9 @@ using ConfSigmaBinning1 = ConfSigmaBinning; constexpr const char PrefixSigmaPlusBinning1[] = "SigmaPlusBinning1"; using ConfSigmaPlusBinning1 = ConfSigmaPlusBinning; +constexpr const char PrefixSigmaPlusPcmBinning1[] = "SigmaPlusPcmBinning1"; +using ConfSigmaPlusPcmBinning1 = ConfSigmaPlusPcmBinning; + template struct ConfKinkQaBinning : o2::framework::ConfigurableGroup { std::string prefix = Prefix; @@ -157,7 +165,7 @@ constexpr std::array, kKinkHistLast> HistTable = {kOrigin, o2::framework::HistType::kTH1F, "hOrigin", "Status Codes (=Origin); Status Code; Entries"}, {kPdg, o2::framework::HistType::kTH1F, "hPdg", "PDG Codes of reconstructed kinks; PDG Code; Entries"}, {kPdgMother, o2::framework::HistType::kTH1F, "hPdgMother", "PDG Codes of mother of reconstructed kink; PDG Code; Entries"}, - {kPdgPartonicMother, o2::framework::HistType::kTH1F, "hPdgPartonicMother", "PDG Codes of partonic mother reconstructed knik; PDG Code; Entries"}, + {kPdgPartonicMother, o2::framework::HistType::kTH1F, "hPdgPartonicMother", "PDG Codes of partonic mother of reconstructed kink; PDG Code; Entries"}, {kTruePt, o2::framework::HistType::kTH1F, "hTruePt", "True transverse momentum; p_{T} (GeV/#it{c}); Entries"}, {kTrueEta, o2::framework::HistType::kTH1F, "hTrueEta", "True pseudorapdity; #eta; Entries"}, {kTruePhi, o2::framework::HistType::kTH1F, "hTruePhi", "True azimuthal angle; #varphi; Entries"}, @@ -167,9 +175,9 @@ constexpr std::array, kKinkHistLast> HistTable = {kFromMaterial, o2::framework::HistType::kTH2F, "hFromMaterial", "Particles from material; p_{T} (GeV/#it{c}); kink angle"}, {kMissidentified, o2::framework::HistType::kTH2F, "hMissidentified", "Missidentified particles (fake/wrong PDG code); p_{T} (GeV/#it{c}); kink angle"}, {kSecondary1, o2::framework::HistType::kTH2F, "hFromSecondary1", "Particles from secondary decay; p_{T} (GeV/#it{c}); kink angle"}, - {kSecondary2, o2::framework::HistType::kTH2F, "hFromSecondary2", "Particles from seconary decay; p_{T} (GeV/#it{c}); kink angle"}, - {kSecondary3, o2::framework::HistType::kTH2F, "hFromSecondary3", "Particles from seconary decay; p_{T} (GeV/#it{c}); kink angle"}, - {kSecondaryOther, o2::framework::HistType::kTH2F, "hFromSecondaryOther", "Particles from every other seconary decay; p_{T} (GeV/#it{c}); kink angle"}}}; + {kSecondary2, o2::framework::HistType::kTH2F, "hFromSecondary2", "Particles from secondary decay; p_{T} (GeV/#it{c}); kink angle"}, + {kSecondary3, o2::framework::HistType::kTH2F, "hFromSecondary3", "Particles from secondary decay; p_{T} (GeV/#it{c}); kink angle"}, + {kSecondaryOther, o2::framework::HistType::kTH2F, "hFromSecondaryOther", "Particles from every other secondary decay; p_{T} (GeV/#it{c}); kink angle"}}}; // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define KINK_HIST_ANALYSIS_MAP(conf) \ @@ -255,11 +263,15 @@ std::map> makeKinkMcQaHistSpecMap #undef KINK_HIST_MC_QA_MAP constexpr char PrefixSigmaQa[] = "SigmaQA/"; -constexpr char PrefixSigma1[] = "Sigma1/"; +constexpr char PrefixSigma1Se[] = "Sigma1/SE/"; +constexpr char PrefixSigma1Me[] = "Sigma1/ME/"; constexpr char PrefixSigma2[] = "Sigma2/"; constexpr char PrefixSigmaPlusQa[] = "SigmaPlusQA/"; -constexpr char PrefixSigmaPlus1[] = "SigmaPlus1/"; +constexpr char PrefixSigmaPlus1Se[] = "SigmaPlus1/SE/"; +constexpr char PrefixSigmaPlus1Me[] = "SigmaPlus1/ME/"; constexpr char PrefixSigmaPlus2[] = "SigmaPlus2/"; +constexpr char PrefixSigmaPlusPcm1Se[] = "SigmaPlusPcm1/SE/"; +constexpr char PrefixSigmaPlusPcm1Me[] = "SigmaPlusPcm1/ME/"; constexpr std::string_view AnalysisDir = "Analysis/"; constexpr std::string_view QaDir = "QA/"; @@ -284,25 +296,27 @@ class KinkHistManager std::map> const& ChaDauSpecs) { mHistogramRegistry = registry; - mPdgCode = std::abs(ConfKinkSelection.pdgCodeAbs.value) * ConfKinkSelection.sign.value; + mPdgCode = std::abs(ConfKinkSelection.pdgCodeAbs.value); int chaDauPdgCodeAbs = 0; int chaDauCharge = 0; const int absCharge = 1; - if (std::abs(mPdgCode) == PDG_t::kSigmaMinus) { + if (mPdgCode == PDG_t::kSigmaMinus) { if (ConfKinkSelection.sign.value < 0) { chaDauPdgCodeAbs = std::abs(PDG_t::kPiMinus); chaDauCharge = -1; } else { + mPdgCode = -1 * mPdgCode; // anti-Sigma- is positively charged and has negative pdg code chaDauPdgCodeAbs = std::abs(PDG_t::kPiPlus); chaDauCharge = 1; } - } else if (std::abs(mPdgCode) == PDG_t::kSigmaPlus) { + } else if (mPdgCode == PDG_t::kSigmaPlus) { if (ConfKinkSelection.sign.value > 0) { chaDauPdgCodeAbs = std::abs(PDG_t::kProton); chaDauCharge = 1; } else { + mPdgCode = -1 * mPdgCode; // anti-Sigma+ is negatively charged and has negative pdg code chaDauPdgCodeAbs = std::abs(PDG_t::kProtonBar); chaDauCharge = -1; } @@ -332,26 +346,28 @@ class KinkHistManager T3 const& ConfChaDauBinningQa) { mHistogramRegistry = registry; - mPdgCode = std::abs(ConfKinkSelection.pdgCodeAbs.value) * ConfKinkSelection.sign.value; + mPdgCode = std::abs(ConfKinkSelection.pdgCodeAbs.value); this->enableOptionalHistograms(ConfKinkBinningQa); int chaDauPdgCodeAbs = 0; int chaDauCharge = 0; const int absCharge = 1; - if (std::abs(mPdgCode) == PDG_t::kSigmaMinus) { + if (mPdgCode == PDG_t::kSigmaMinus) { if (ConfKinkSelection.sign.value < 0) { chaDauPdgCodeAbs = std::abs(PDG_t::kPiMinus); chaDauCharge = -1; } else { + mPdgCode = -1 * mPdgCode; // anti-Sigma- is positively charged and has negative pdg code chaDauPdgCodeAbs = std::abs(PDG_t::kPiPlus); chaDauCharge = 1; } - } else if (std::abs(mPdgCode) == PDG_t::kSigmaPlus) { + } else if (mPdgCode == PDG_t::kSigmaPlus) { if (ConfKinkSelection.sign.value > 0) { chaDauPdgCodeAbs = std::abs(PDG_t::kProton); chaDauCharge = 1; } else { + mPdgCode = -1 * mPdgCode; // anti-Sigma+ is negatively charged and has negative pdg code chaDauPdgCodeAbs = std::abs(PDG_t::kProtonBar); chaDauCharge = -1; } @@ -502,7 +518,7 @@ class KinkHistManager mHistogramRegistry->fill(HIST(kinkPrefix) + HIST(AnalysisDir) + HIST(getHistName(kPhi, HistTable)), kinkcandidate.phi()); mHistogramRegistry->fill(HIST(kinkPrefix) + HIST(AnalysisDir) + HIST(getHistName(kMass, HistTable)), kinkcandidate.mass()); - if constexpr (isEqual(kink, modes::Kink::kSigma) || isEqual(kink, modes::Kink::kSigmaPlus)) { + if constexpr (isEqual(kink, modes::Kink::kSigma) || isEqual(kink, modes::Kink::kSigmaPlus) || isEqual(kink, modes::Kink::kSigmaPlusPcm)) { mHistogramRegistry->fill(HIST(kinkPrefix) + HIST(AnalysisDir) + HIST(getHistName(kSign, HistTable)), kinkcandidate.sign()); } } @@ -595,9 +611,6 @@ class KinkHistManager case modes::McOrigin::kPhysicalPrimary: mHistogramRegistry->fill(HIST(kinkPrefix) + HIST(McDir) + HIST(getHistName(kPrimary, HistTable)), kinkCandidate.pt(), kinkCandidate.kinkAngle()); break; - case modes::McOrigin::kFromWrongCollision: - mHistogramRegistry->fill(HIST(kinkPrefix) + HIST(McDir) + HIST(getHistName(kFromWrongCollision, HistTable)), kinkCandidate.pt(), kinkCandidate.kinkAngle()); - break; case modes::McOrigin::kFromMaterial: mHistogramRegistry->fill(HIST(kinkPrefix) + HIST(McDir) + HIST(getHistName(kFromMaterial, HistTable)), kinkCandidate.pt(), kinkCandidate.kinkAngle()); break; @@ -618,7 +631,7 @@ class KinkHistManager } break; default: - LOG(warn) << "Encounted partilce with unknown origin!"; + LOG(warn) << "Encountered particle with unknown origin!"; break; } } diff --git a/PWGCF/Femto/Core/mcBuilder.h b/PWGCF/Femto/Core/mcBuilder.h index 4b9526d4ae9..8cde761ad01 100644 --- a/PWGCF/Femto/Core/mcBuilder.h +++ b/PWGCF/Femto/Core/mcBuilder.h @@ -66,6 +66,7 @@ struct McBuilderProducts : o2::framework::ProducesGroup { o2::framework::Produces producedLcLabels; o2::framework::Produces producedSigmaLabels; o2::framework::Produces producedSigmaPlusLabels; + o2::framework::Produces producedSigmaPlusPcmLabels; o2::framework::Produces producedXiLabels; o2::framework::Produces producedOmegaLabels; }; @@ -84,8 +85,9 @@ struct ConfMcTables : o2::framework::ConfigurableGroup { o2::framework::Configurable producedK0shortLabels{"producedK0shortLabels", -1, "Produce k0short labels (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable producedD0Labels{"producedD0Labels", -1, "Produce D0 labels (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable producedLcLabels{"producedLcLabels", -1, "Produce Lc labels (-1: auto; 0 off; 1 on)"}; - o2::framework::Configurable producedSigmaLabels{"producedSigmaLabels", -1, "Produce k0short labels (-1: auto; 0 off; 1 on)"}; - o2::framework::Configurable producedSigmaPlusLabels{"producedSigmaPlusLabels", -1, "Produce k0short labels (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable producedSigmaLabels{"producedSigmaLabels", -1, "Produce sigma labels (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable producedSigmaPlusLabels{"producedSigmaPlusLabels", -1, "Produce sigma plus labels (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable producedSigmaPlusPcmLabels{"producedSigmaPlusPcmLabels", -1, "Produce sigma plus (PCM) labels (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable producedXiLabels{"producedXiLabels", -1, "Produce xi labels (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable producedOmegaLabels{"producedOmegaLabels", -1, "Produce omega labels (-1: auto; 0 off; 1 on)"}; }; @@ -159,6 +161,7 @@ class McBuilder mProduceLcLabels = utils::enableTable("FLcLabels_001", table.producedLcLabels.value, initContext); mProduceSigmaLabels = utils::enableTable("FSigmaLabels_001", table.producedSigmaLabels.value, initContext); mProduceSigmaPlusLabels = utils::enableTable("FSigmaPlusLabels_001", table.producedSigmaPlusLabels.value, initContext); + mProduceSigmaPlusPcmLabels = utils::enableTable("FSigmaPlusPcmLabels_001", table.producedSigmaPlusPcmLabels.value, initContext); mProduceXiLabels = utils::enableTable("FXiLabels_001", table.producedXiLabels.value, initContext); mProduceOmegaLabels = utils::enableTable("FOmegaLabels_001", table.producedOmegaLabels.value, initContext); @@ -167,7 +170,7 @@ class McBuilder mProduceMcMothers || mProduceMcPartonicMothers || mProduceTrackLabels || mProduceLambdaLabels || mProduceK0shortLabels || - mProduceSigmaLabels || mProduceSigmaPlusLabels || + mProduceSigmaLabels || mProduceSigmaPlusLabels || mProduceSigmaPlusPcmLabels || mProduceXiLabels || mProduceOmegaLabels || mProduceD0Labels || mProduceLcLabels) { mFillAnyTable = true; @@ -197,7 +200,7 @@ class McBuilder this->fillMcCollision(mcCol, mcProducts); } // Add label - mcProducts.producedCollisionLabels(mCollisionMap.at(originalIndex)); // mc collsions has been added so we can now safely retrieve the index + mcProducts.producedCollisionLabels(mCollisionMap.at(originalIndex)); // mc collision has been added so we can now safely retrieve the index } else { // If no MC collision associated, fill empty label mcProducts.producedCollisionLabels(-1); @@ -426,6 +429,21 @@ class McBuilder fillMcLabelGeneric(sigmaPlusDaughter, mcParticles, mcCols, mcProducts, [](auto& prod, int64_t p) { prod.producedSigmaPlusLabels(p); }, true); } + template + void fillMcSigmaPlusPcmWithLabel(T1 const& sigmaPlusPcm, T2 const& chaDaughter, T3 const& mcParticles, T4 const& mcCols, T5& mcProducts) + { + if (!mProduceSigmaPlusPcmLabels) { + mcProducts.producedSigmaPlusPcmLabels(-1); + return; + } + // true sigma plus (proton and photon from it): label of the sigma plus, otherwise label of the proton, its mother gives the origin of the background + if (sigmaPlusPcm.has_mcParticle()) { + fillMcLabelGeneric(sigmaPlusPcm, mcParticles, mcCols, mcProducts, [](auto& prod, int64_t p) { prod.producedSigmaPlusPcmLabels(p); }); + } else { + fillMcLabelGeneric(chaDaughter, mcParticles, mcCols, mcProducts, [](auto& prod, int64_t p) { prod.producedSigmaPlusPcmLabels(p); }); + } + } + template void fillMcXiWithLabel(T1 const& xi, T2 const& mcParticles, T3 const& mcCols, T4& mcProducts) { @@ -556,7 +574,7 @@ class McBuilder int64_t mcMotherRow = -1; if (mProduceMcMothers && mcParticle.has_mothers()) { auto mothers = mcParticle.template mothers_as(); - auto motherParticle = mothers.front(); + auto const& motherParticle = mothers.front(); auto mcMotherIndex = motherParticle.globalIndex(); auto itM = mMcMotherMap.find(mcMotherIndex); @@ -636,30 +654,30 @@ class McBuilder } template - int64_t findFirstPartonicMother(const T1& mcParticle, const T2& mcParticles) + int64_t findFirstPartonicMother(const T1& mcParticle, const T2& mcParticles, int depth = 0) { - if (!mcParticle.has_mothers()) { + // guards against cyclic/malformed mother chains in some MC productions, which would + // otherwise recurse unboundedly and crash with a stack overflow; no physically sensible + // generator history is anywhere close to this deep + constexpr int MaxPartonicMotherDepth = 100; + if (depth >= MaxPartonicMotherDepth) { + // some productions hit this on a large fraction of particles (e.g. a mother-range + // heuristic below misfiring on this production's indexing convention), so warn once per + // device lifetime rather than once per particle + if (!mWarnedPartonicMotherDepth) { + LOG(warn) << "findFirstPartonicMother exceeded max recursion depth (" << MaxPartonicMotherDepth << ") at least once; mother chain may be cyclic or malformed for this MC production. Consider using findLastPartonicMother instead. Further occurrences will be suppressed."; + mWarnedPartonicMotherDepth = true; + } return -1; } - auto motherIds = mcParticle.mothersIds(); - // adapted these checks from MCUtils in PWGEM - const int defaultMotherSize = 2; - std::vector allMotherIds; - if (motherIds.size() == defaultMotherSize && motherIds[1] > motherIds[0]) { - // keep for now, might be needed later - // && ((80 < std::abs(o2::mcgenstatus::getGenStatusCode(p.statusCode())) && std::abs(o2::mcgenstatus::getGenStatusCode(p.statusCode())) < 90) || (100 < std::abs(o2::mcgenstatus::getGenStatusCode(p.statusCode())) && std::abs(o2::mcgenstatus::getGenStatusCode(p.statusCode())) < 110))) { - // Pythia: mother range - for (int i = motherIds[0]; i <= motherIds[1]; i++) { - allMotherIds.push_back(i); - } - } else { - // Otherwise just use them as given - for (const int& id : motherIds) { - allMotherIds.push_back(id); - } + if (!mcParticle.has_mothers()) { + return -1; } + // RecoDecay::getOrderedMotherIds() implements the Pythia mother-id-range expansion (gated on the particle's gen status code) + std::vector allMotherIds; + RecoDecay::getOrderedMotherIds(mcParticle, allMotherIds, /*searchUpToQuark=*/true); // Loop over all mothers - for (const int& i : allMotherIds) { + for (const int64_t& i : allMotherIds) { if (i < 0 || i >= mcParticles.size()) { continue; @@ -671,7 +689,7 @@ class McBuilder return i; // Found a parton → return index } // Recurse upward - int64_t found = this->findFirstPartonicMother(mother, mcParticles); + int64_t found = this->findFirstPartonicMother(mother, mcParticles, depth + 1); if (found != -1) { return found; } @@ -690,19 +708,12 @@ class McBuilder if (!current.has_mothers()) { break; } - auto motherIds = current.mothersIds(); - int nextIndex = -1; - const int defaultMotherSize = 2; - if (motherIds.size() == defaultMotherSize && motherIds[1] > motherIds[0]) { - nextIndex = motherIds[0]; - } else { - for (const int& id : motherIds) { - if (id >= 0 && id < mcParticles.size()) { - nextIndex = id; - break; - } - } - } + // RecoDecay::getOrderedMotherIds() implements the Pythia mother-id-range expansion (gated on + // the particle's gen status code). This function only ever follows a single branch upward, + // so just the first ordered id is used. + std::vector motherIdsOrdered; + RecoDecay::getOrderedMotherIds(current, motherIdsOrdered, /*searchUpToQuark=*/true); + int64_t nextIndex = motherIdsOrdered.empty() ? -1 : motherIdsOrdered.front(); if (nextIndex < 0 || nextIndex >= mcParticles.size()) { break; @@ -727,6 +738,7 @@ class McBuilder bool mPassThrough = false; bool mFindLastPartonicMother = false; + bool mWarnedPartonicMotherDepth = false; bool mFillAnyTable = false; bool mProduceMcCollisions = false; bool mProduceMcParticles = false; @@ -741,6 +753,7 @@ class McBuilder bool mProduceLcLabels = false; bool mProduceSigmaLabels = false; bool mProduceSigmaPlusLabels = false; + bool mProduceSigmaPlusPcmLabels = false; bool mProduceXiLabels = false; bool mProduceOmegaLabels = false; bool mProduceMcMotherLabels = false; @@ -765,6 +778,9 @@ struct McBuilderDerivedToDerivedProducts : o2::framework::ProducesGroup { o2::framework::Produces producedCollisionLabels; o2::framework::Produces producedTrackLabels; + o2::framework::Produces producedSigmaLabels; + o2::framework::Produces producedSigmaPlusLabels; + o2::framework::Produces producedSigmaPlusPcmLabels; }; struct ConfMcTablesDerivedToDerived : o2::framework::ConfigurableGroup { @@ -824,6 +840,42 @@ class McBuilderDerivedToDerived this->getOrCreateMcParticleRow(mcParticle, mcCols, mcMothers, mcPartonicMothers, mcProducts)); } + template + void fillSigmaWithLabel(T1 const& sigma, T2 const& mcCols, T3 const& /*mcParticles*/, T4 const& mcMothers, T5 const& mcPartonicMothers, T6& mcProducts) + { + if (!sigma.has_fMcParticle()) { + mcProducts.producedSigmaLabels(-1); + return; + } + auto mcParticle = sigma.template fMcParticle_as(); + mcProducts.producedSigmaLabels( + this->getOrCreateMcParticleRow(mcParticle, mcCols, mcMothers, mcPartonicMothers, mcProducts)); + } + + template + void fillSigmaPlusWithLabel(T1 const& sigmaPlus, T2 const& mcCols, T3 const& /*mcParticles*/, T4 const& mcMothers, T5 const& mcPartonicMothers, T6& mcProducts) + { + if (!sigmaPlus.has_fMcParticle()) { + mcProducts.producedSigmaPlusLabels(-1); + return; + } + auto mcParticle = sigmaPlus.template fMcParticle_as(); + mcProducts.producedSigmaPlusLabels( + this->getOrCreateMcParticleRow(mcParticle, mcCols, mcMothers, mcPartonicMothers, mcProducts)); + } + + template + void fillSigmaPlusPcmWithLabel(T1 const& sigmaPlusPcm, T2 const& mcCols, T3 const& /*mcParticles*/, T4 const& mcMothers, T5 const& mcPartonicMothers, T6& mcProducts) + { + if (!sigmaPlusPcm.has_fMcParticle()) { + mcProducts.producedSigmaPlusPcmLabels(-1); + return; + } + auto mcParticle = sigmaPlusPcm.template fMcParticle_as(); + mcProducts.producedSigmaPlusPcmLabels( + this->getOrCreateMcParticleRow(mcParticle, mcCols, mcMothers, mcPartonicMothers, mcProducts)); + } + template void reset(T1 const& mcCols, T2 const& mcParticles) { diff --git a/PWGCF/Femto/Core/mcParticleHistManager.h b/PWGCF/Femto/Core/mcParticleHistManager.h index 641558589f5..ce9c8bd4bed 100644 --- a/PWGCF/Femto/Core/mcParticleHistManager.h +++ b/PWGCF/Femto/Core/mcParticleHistManager.h @@ -146,7 +146,12 @@ auto makeMcParticleHistSpecMap(const T& confBinning) inline constexpr char PrefixMcParticle1[] = "McParticle1/"; inline constexpr char PrefixMcParticle2[] = "McParticle2/"; -inline constexpr char PrefixMcParticle3[] = "McParticle3/"; +inline constexpr char PrefixMcParticle1Se[] = "McParticle1Se/"; +inline constexpr char PrefixMcParticle2Se[] = "McParticle2Se/"; +inline constexpr char PrefixMcParticle3Se[] = "McParticle3Se/"; +inline constexpr char PrefixMcParticle1Me[] = "McParticle1Me/"; +inline constexpr char PrefixMcParticle2Me[] = "McParticle2Me/"; +inline constexpr char PrefixMcParticle3Me[] = "McParticle3Me/"; constexpr std::string_view McDir = "MC/"; @@ -213,9 +218,10 @@ class McParticleHistManager if (mPlotOrigins) { // NOTE: kNoMcParticle and kMissidentified are kept here only for enum/histogram // symmetry with TrackHist — they describe reco-vs-truth mismatches that cannot - // occur for a pure MC-truth particle, so these bins are expected to stay empty - // unless the producer assigns those origin codes for some other reason - // (e.g. pileup-associated particles tagged as kFromWrongCollision). + // occur for a pure MC-truth particle, so these bins are expected to stay empty. + // kFromWrongCollision is not handled here at all: mcBuilder.h's getOrigin() never + // assigns it (only kPhysicalPrimary/kFromSecondaryDecay/kFromMaterial), so it would + // be dead code the same way it was in the per-candidate hist managers. mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kOrigin, HistTable)), mcParticle.origin()); switch (static_cast(mcParticle.origin())) { case modes::McOrigin::kNoMcParticle: @@ -224,9 +230,6 @@ class McParticleHistManager case modes::McOrigin::kPhysicalPrimary: mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kPrimary, HistTable)), mcParticle.pt()); break; - case modes::McOrigin::kFromWrongCollision: - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kFromWrongCollision, HistTable)), mcParticle.pt()); - break; case modes::McOrigin::kFromMaterial: mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kFromMaterial, HistTable)), mcParticle.pt()); break; diff --git a/PWGCF/Femto/Core/modes.h b/PWGCF/Femto/Core/modes.h index ca55b122c80..1920096a835 100644 --- a/PWGCF/Femto/Core/modes.h +++ b/PWGCF/Femto/Core/modes.h @@ -53,15 +53,22 @@ enum class Mode : uint32_t { kMc = bit(2), kSe = bit(3), kMe = bit(4), + kFitVars = bit(5), // purity/fraction histograms (tracks: packed nSigma tables; V0s: cpa) -- gates booking at init time kReco_Qa = kReco | kQa, kReco_Mc = kReco | kMc, kReco_Qa_Mc = kReco | kQa | kMc, + kReco_Qa_FitVars = kReco | kQa | kFitVars, + kReco_Qa_Mc_FitVars = kReco | kQa | kMc | kFitVars, kSe_Reco = kSe | kReco, kMe_Reco = kMe | kReco, kSe_Reco_Mc = kSe | kReco | kMc, kMe_Reco_Mc = kMe | kReco | kMc, kSe_Mc = kSe | kMc, kMe_Mc = kMe | kMc, + kSe_Reco_FitVars = kSe | kReco | kFitVars, + kMe_Reco_FitVars = kMe | kReco | kFitVars, + kSe_Reco_Mc_FitVars = kSe | kReco | kMc | kFitVars, + kMe_Reco_Mc_FitVars = kMe | kReco | kMc | kFitVars, }; enum class System : uint32_t { @@ -113,6 +120,7 @@ enum class Particle : o2::analysis::femto::datatypes::ParticleType { kKink = 4, kCascade = 5, kCharmHadron = 6, + kTwoPhotonResonance = 7, }; enum class McOrigin : o2::analysis::femto::datatypes::McOriginType { @@ -218,7 +226,8 @@ enum class V0 : o2::analysis::femto::datatypes::V0Type { enum class Kink : o2::analysis::femto::datatypes::KinkType { kSigma, - kSigmaPlus + kSigmaPlus, + kSigmaPlusPcm }; enum class Cascade : o2::analysis::femto::datatypes::CascadeType { @@ -234,6 +243,18 @@ enum class TwoTrackResonance : o2::analysis::femto::datatypes::TwoTrackResonance kKstar0Bar }; +// resonances reconstructed from two photons (PCM), e.g. pi0 -> gamma gamma, eta -> gamma gamma +enum class TwoPhotonResonance : o2::analysis::femto::datatypes::TwoPhotonResonanceType { + kPi0, + kEta +}; + +// resonances reconstructed from two tracks and a two-photon resonance, e.g. omega(782) -> pi+ pi- pi0. +// Named kOmegaMeson (not kOmega) to not collide with Cascade::kOmega, the Omega- baryon. +enum class TrackTrackTwoPhotonResonance : o2::analysis::femto::datatypes::TrackTrackTwoPhotonResonanceType { + kOmegaMeson +}; + enum class CharmHadron : o2::analysis::femto::datatypes::CharmHadronType { kD0, kD0Bar, @@ -261,5 +282,22 @@ enum class KinematicVariable : o2::analysis::femto::datatypes::KinematicVariable kKinematicVariableLast = 4 }; +/// axis title for the kinematic variable plotted by CPR / pair- and triplet-cleaner QA histograms +constexpr const char* kinematicVariableToAxisTitle(KinematicVariable type) +{ + switch (type) { + case KinematicVariable::kKstar: + return "k* (GeV/#it{c})"; + case KinematicVariable::kKt: + return "k_{T} (GeV/#it{c})"; + case KinematicVariable::kMt: + return "m_{T} (GeV/#it{c}^{2})"; + case KinematicVariable::kQ3: + return "Q_{3} (GeV/#it{c})"; + default: + return "kinematic variable (GeV/#it{c})"; + } +} + }; // namespace o2::analysis::femto::modes #endif // PWGCF_FEMTO_CORE_MODES_H_ diff --git a/PWGCF/Femto/Core/pairBuilder.h b/PWGCF/Femto/Core/pairBuilder.h index 0b408b64487..dd157bbb0ae 100644 --- a/PWGCF/Femto/Core/pairBuilder.h +++ b/PWGCF/Femto/Core/pairBuilder.h @@ -29,6 +29,7 @@ #include "PWGCF/Femto/Core/pairProcessHelpers.h" #include "PWGCF/Femto/Core/particleCleaner.h" #include "PWGCF/Femto/Core/trackHistManager.h" +#include "PWGCF/Femto/Core/twoPhotonResonanceHistManager.h" #include "PWGCF/Femto/Core/twoTrackResonanceHistManager.h" #include "PWGCF/Femto/Core/v0HistManager.h" #include "PWGCF/Femto/DataModel/FemtoTables.h" @@ -51,7 +52,7 @@ namespace o2::analysis::femto::pairbuilder const int64_t nLimitPartitionIdenticalParticles = 2; const int64_t nLimitPartitionParticles = 1; -template +template class PairTrackTrackBuilder { public: @@ -105,7 +106,8 @@ class PairTrackTrackBuilder if (mSameSpecies) { mTrackCleaner1.init(confTrackCleaner1); - mTrackHistManager1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1); mPairHistManagerSe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection1.pdgCodeAbs.value); mPairHistManagerSe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection1.chargeAbs.value); @@ -117,8 +119,10 @@ class PairTrackTrackBuilder } else { mTrackCleaner1.init(confTrackCleaner1); mTrackCleaner2.init(confTrackCleaner2); - mTrackHistManager1.template init(registry, trackHistSpec1, confTrackSelection1); - mTrackHistManager2.template init(registry, trackHistSpec2, confTrackSelection2); + mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerSe2.template init(registry, trackHistSpec2, confTrackSelection2); + mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerMe2.template init(registry, trackHistSpec2, confTrackSelection2); mPairHistManagerSe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection2.pdgCodeAbs.value); mPairHistManagerSe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection2.chargeAbs.value); @@ -132,6 +136,7 @@ class PairTrackTrackBuilder // setup mixing mMixingPolicy = static_cast(confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; // setup rng if necessary if (confMixing.seed.value >= 0) { @@ -163,7 +168,7 @@ class PairTrackTrackBuilder if (mMixIdenticalParticles) { pairOrder = static_cast(mDist(mRng)); } - return pairprocesshelpers::processSameEvent(trackSlice1, trackTable, col, mTrackHistManager1, mPairHistManagerSe, mCprSe, mPcSe, pairOrder); + return pairprocesshelpers::processSameEvent(trackSlice1, trackTable, col, mTrackHistManagerSe1, mPairHistManagerSe, mCprSe, mPcSe, pairOrder); } auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); @@ -173,7 +178,7 @@ class PairTrackTrackBuilder } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(trackSlice1, trackSlice2, trackTable, col, mTrackHistManager1, mTrackHistManager2, mPairHistManagerSe, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice1, trackSlice2, trackTable, col, mTrackHistManagerSe1, mTrackHistManagerSe2, mPairHistManagerSe, mCprSe, mPcSe); } // mc @@ -192,7 +197,7 @@ class PairTrackTrackBuilder if (mMixIdenticalParticles) { pairOrder = static_cast(mDist(mRng)); } - return pairprocesshelpers::processSameEvent(trackSlice1, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mPairHistManagerSe, mTrackCleaner1, mCprSe, mPcSe, pairOrder); + return pairprocesshelpers::processSameEvent(trackSlice1, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe1, mPairHistManagerSe, mTrackCleaner1, mCprSe, mPcSe, pairOrder); } auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); @@ -202,7 +207,7 @@ class PairTrackTrackBuilder } mColHistManager.template fill(col, mcCols); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(trackSlice1, trackSlice2, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mTrackHistManager2, mPairHistManagerSe, mTrackCleaner1, mTrackCleaner2, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice1, trackSlice2, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe1, mTrackHistManagerSe2, mPairHistManagerSe, mTrackCleaner1, mTrackCleaner2, mCprSe, mPcSe); } template @@ -212,17 +217,17 @@ class PairTrackTrackBuilder if (mSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1); break; case static_cast(pairhistmanager::kVtxCentEventPlaneAngle): if constexpr (utils::HasEventShape) { - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxCentEventPlaneAngle, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxCentEventPlaneAngle, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1); } else { LOG(fatal) << "Mixing policy kVtxCentEventPlaneAngle requires a collision table with event-shape columns. Breaking..."; } @@ -233,17 +238,17 @@ class PairTrackTrackBuilder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2); break; case static_cast(pairhistmanager::kVtxCentEventPlaneAngle): if constexpr (utils::HasEventShape) { - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxCentEventPlaneAngle, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxCentEventPlaneAngle, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2); } else { LOG(fatal) << "Mixing policy kVtxCentEventPlaneAngle requires a collision table with event-shape columns. Breaking..."; } @@ -260,17 +265,17 @@ class PairTrackTrackBuilder if (mSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1); break; case static_cast(pairhistmanager::kVtxCentEventPlaneAngle): if constexpr (utils::HasEventShape) { - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCentEventPlaneAngle, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCentEventPlaneAngle, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1); } else { LOG(fatal) << "Mixing policy kVtxCentEventPlaneAngle requires a collision table with event-shape columns. Breaking..."; } @@ -281,17 +286,17 @@ class PairTrackTrackBuilder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2); break; case static_cast(pairhistmanager::kVtxCentEventPlaneAngle): if constexpr (utils::HasEventShape) { - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCentEventPlaneAngle, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCentEventPlaneAngle, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2); } else { LOG(fatal) << "Mixing policy kVtxCentEventPlaneAngle requires a collision table with event-shape columns. Breaking..."; } @@ -304,8 +309,12 @@ class PairTrackTrackBuilder private: colhistmanager::CollisionHistManager mColHistManager; - trackhistmanager::TrackHistManager mTrackHistManager1; - trackhistmanager::TrackHistManager mTrackHistManager2; + trackhistmanager::TrackHistManager mTrackHistManagerSe1; + trackhistmanager::TrackHistManager mTrackHistManagerSe2; + // single-particle histograms for particles that end up in mixed-event pairs -- same config + // objects as the same-event managers above, just a different histogram directory + trackhistmanager::TrackHistManager mTrackHistManagerMe1; + trackhistmanager::TrackHistManager mTrackHistManagerMe2; pairhistmanager::PairHistManager mPairHistManagerSe; pairhistmanager::PairHistManager mPairHistManagerMe; closepairrejection::ClosePairRejectionTrackTrack mCprSe; @@ -316,6 +325,7 @@ class PairTrackTrackBuilder particlecleaner::ParticleCleaner mTrackCleaner2; pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; int mMixingDepth = 5; + int mEventStride = 1; bool mSameSpecies = false; bool mMixIdenticalParticles = false; std::mt19937 mRng; @@ -328,6 +338,12 @@ template (registry, V0HistSpec1, confV0Selection1, PosDauHistSpec1, NegDauHistSpec1); + mV0HistManagerSe1.template init(registry, V0HistSpec1, confV0Selection1, PosDauHistSpec1, NegDauHistSpec1); + mV0HistManagerMe1.template init(registry, V0HistSpec1, confV0Selection1, PosDauHistSpec1, NegDauHistSpec1); if (mSameSpecies) { mPairHistManagerSe.setMass(confV0Selection1.pdgCodeAbs.value, confV0Selection1.pdgCodeAbs.value); @@ -421,7 +438,8 @@ class PairV0V0Builder mPairHistManagerMe.setCharge(1, 1); mCprMe.init(registry, cprHistSpecPos, cprHistSpecNeg, confCprPos, confCprNeg); } else { - mV0HistManager2.template init(registry, V0HistSpec2, confV0Selection2, PosDauHistSpec2, NegDauHistSpec2); + mV0HistManagerSe2.template init(registry, V0HistSpec2, confV0Selection2, PosDauHistSpec2, NegDauHistSpec2); + mV0HistManagerMe2.template init(registry, V0HistSpec2, confV0Selection2, PosDauHistSpec2, NegDauHistSpec2); mPairHistManagerSe.setMass(confV0Selection1.pdgCodeAbs.value, confV0Selection2.pdgCodeAbs.value); mPairHistManagerSe.setCharge(1, 1); @@ -435,6 +453,7 @@ class PairV0V0Builder // setup mixing mMixingPolicy = static_cast(confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; // setup rng if necessary // mDist is bounded at declaration, so it stays valid even if this branch is not taken @@ -465,7 +484,7 @@ class PairV0V0Builder if (mMixIdenticalParticles) { pairOrder = static_cast(mDist(mRng)); } - return pairprocesshelpers::processSameEvent(v0Slice1, trackTable, col, mV0HistManager1, mPairHistManagerSe, mCprSe, mPcSe, pairOrder); + return pairprocesshelpers::processSameEvent(v0Slice1, trackTable, col, mV0HistManagerSe1, mPairHistManagerSe, mCprSe, mPcSe, pairOrder); } auto v0Slice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); @@ -475,7 +494,7 @@ class PairV0V0Builder } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(v0Slice1, v0Slice2, trackTable, col, mV0HistManager1, mV0HistManager2, mPairHistManagerSe, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(v0Slice1, v0Slice2, trackTable, col, mV0HistManagerSe1, mV0HistManagerSe2, mPairHistManagerSe, mCprSe, mPcSe); } // mc @@ -494,7 +513,7 @@ class PairV0V0Builder if (mMixIdenticalParticles) { pairOrder = static_cast(mDist(mRng)); } - return pairprocesshelpers::processSameEvent(v0Slice1, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mV0HistManager1, mPairHistManagerSe, mV0Cleaner1, mCprSe, mPcSe, pairOrder); + return pairprocesshelpers::processSameEvent(v0Slice1, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mV0HistManagerSe1, mPairHistManagerSe, mV0Cleaner1, mCprSe, mPcSe, pairOrder); } auto v0Slice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); @@ -504,7 +523,7 @@ class PairV0V0Builder } mColHistManager.template fill(col, mcCols); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(v0Slice1, v0Slice2, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mV0HistManager1, mV0HistManager2, mPairHistManagerSe, mV0Cleaner1, mV0Cleaner2, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(v0Slice1, v0Slice2, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mV0HistManagerSe1, mV0HistManagerSe2, mPairHistManagerSe, mV0Cleaner1, mV0Cleaner2, mCprSe, mPcSe); } template @@ -514,13 +533,13 @@ class PairV0V0Builder if (mSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe1); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe1); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe1); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -528,13 +547,13 @@ class PairV0V0Builder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe2); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe2); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe2); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -548,13 +567,13 @@ class PairV0V0Builder if (mSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner1, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner1, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe1); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner1, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner1, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe1); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner1, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner1, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe1); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -562,13 +581,13 @@ class PairV0V0Builder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner2, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner2, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe2); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner2, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner2, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe2); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner2, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner2, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe2); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -578,8 +597,10 @@ class PairV0V0Builder private: colhistmanager::CollisionHistManager mColHistManager; - v0histmanager::V0HistManager mV0HistManager1; - v0histmanager::V0HistManager mV0HistManager2; + v0histmanager::V0HistManager mV0HistManagerSe1; + v0histmanager::V0HistManager mV0HistManagerSe2; + v0histmanager::V0HistManager mV0HistManagerMe1; + v0histmanager::V0HistManager mV0HistManagerMe2; particlecleaner::ParticleCleaner mV0Cleaner1; particlecleaner::ParticleCleaner mV0Cleaner2; pairhistmanager::PairHistManager mPairHistManagerSe; @@ -591,6 +612,7 @@ class PairV0V0Builder pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; bool mSameSpecies = false; int mMixingDepth = 5; + int mEventStride = 1; bool mMixIdenticalParticles = false; std::mt19937 mRng; std::uniform_int_distribution<> mDist{static_cast(pairprocesshelpers::kOrder12), @@ -603,6 +625,12 @@ template (registry, D0HistSpec1, confD0Selection1, PosDauHistSpec1, NegDauHistSpec1); + mD0HistManagerSe1.template init(registry, D0HistSpec1, confD0Selection1, PosDauHistSpec1, NegDauHistSpec1); + mD0HistManagerMe1.template init(registry, D0HistSpec1, confD0Selection1, PosDauHistSpec1, NegDauHistSpec1); auto prongPdgCodes = [](int sign) -> std::pair { if (sign > 0) { @@ -708,7 +737,8 @@ class PairD0D0Builder mPairHistManagerMe.setCharge(1, 1); mCprMe.init(registry, cprHistSpecPos, cprHistSpecNeg, confCprPos, confCprNeg); } else { - mD0HistManager2.template init(registry, D0HistSpec2, confD0Selection2, PosDauHistSpec2, NegDauHistSpec2); + mD0HistManagerSe2.template init(registry, D0HistSpec2, confD0Selection2, PosDauHistSpec2, NegDauHistSpec2); + mD0HistManagerMe2.template init(registry, D0HistSpec2, confD0Selection2, PosDauHistSpec2, NegDauHistSpec2); auto [posDauPdg2, negDauPdg2] = prongPdgCodes(confD0Selection2.sign.value); mPairHistManagerSe.setMass(confD0Selection1.pdgCodeAbs.value, posDauPdg1, negDauPdg1, confD0Selection2.pdgCodeAbs.value, posDauPdg2, negDauPdg2); @@ -723,6 +753,7 @@ class PairD0D0Builder // setup mixing mMixingPolicy = static_cast(confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; // setup rng if necessary // mDist is bounded at declaration, so it stays valid even if this branch is not taken if (confMixing.seed.value >= 0) { @@ -752,7 +783,7 @@ class PairD0D0Builder if (mMixIdenticalParticles) { pairOrder = static_cast(mDist(mRng)); } - return pairprocesshelpers::processSameEvent(d0Slice1, trackTable, col, mD0HistManager1, mPairHistManagerSe, mCprSe, mPcSe, pairOrder); + return pairprocesshelpers::processSameEvent(d0Slice1, trackTable, col, mD0HistManagerSe1, mPairHistManagerSe, mCprSe, mPcSe, pairOrder); } auto d0Slice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); @@ -762,7 +793,7 @@ class PairD0D0Builder } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(d0Slice1, d0Slice2, trackTable, col, mD0HistManager1, mD0HistManager2, mPairHistManagerSe, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(d0Slice1, d0Slice2, trackTable, col, mD0HistManagerSe1, mD0HistManagerSe2, mPairHistManagerSe, mCprSe, mPcSe); } // mc @@ -781,7 +812,7 @@ class PairD0D0Builder if (mMixIdenticalParticles) { pairOrder = static_cast(mDist(mRng)); } - return pairprocesshelpers::processSameEvent(d0Slice1, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mD0HistManager1, mPairHistManagerSe, mD0Cleaner1, mCprSe, mPcSe, pairOrder); + return pairprocesshelpers::processSameEvent(d0Slice1, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mD0HistManagerSe1, mPairHistManagerSe, mD0Cleaner1, mCprSe, mPcSe, pairOrder); } auto d0Slice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); auto d0Slice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); @@ -790,7 +821,7 @@ class PairD0D0Builder } mColHistManager.template fill(col, mcCols); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(d0Slice1, d0Slice2, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mD0HistManager1, mD0HistManager2, mPairHistManagerSe, mD0Cleaner1, mD0Cleaner2, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(d0Slice1, d0Slice2, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mD0HistManagerSe1, mD0HistManagerSe2, mPairHistManagerSe, mD0Cleaner1, mD0Cleaner2, mCprSe, mPcSe); } template @@ -799,13 +830,13 @@ class PairD0D0Builder if (mSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe1); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe1); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe1); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -813,13 +844,13 @@ class PairD0D0Builder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe2); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe2); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe2); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -833,13 +864,13 @@ class PairD0D0Builder if (mSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner1, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner1, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe1); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner1, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner1, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe1); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner1, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner1, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe1); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -847,13 +878,13 @@ class PairD0D0Builder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner2, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner2, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe2); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner2, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner2, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe2); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner2, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner2, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe2); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -863,8 +894,10 @@ class PairD0D0Builder private: colhistmanager::CollisionHistManager mColHistManager; - charmhadronhistmanager::CharmHadronHistManager mD0HistManager1; // the third prong prefix is unused for 2-prong candidates - charmhadronhistmanager::CharmHadronHistManager mD0HistManager2; // the third prong prefix is unused for 2-prong candidates + charmhadronhistmanager::CharmHadronHistManager mD0HistManagerSe1; // the third prong prefix is unused for 2-prong candidates + charmhadronhistmanager::CharmHadronHistManager mD0HistManagerSe2; // the third prong prefix is unused for 2-prong candidates + charmhadronhistmanager::CharmHadronHistManager mD0HistManagerMe1; // the third prong prefix is unused for 2-prong candidates + charmhadronhistmanager::CharmHadronHistManager mD0HistManagerMe2; // the third prong prefix is unused for 2-prong candidates particlecleaner::ParticleCleaner mD0Cleaner1; particlecleaner::ParticleCleaner mD0Cleaner2; pairhistmanager::PairHistManager mPairHistManagerSe; @@ -876,6 +909,7 @@ class PairD0D0Builder pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; bool mSameSpecies = false; int mMixingDepth = 5; + int mEventStride = 1; bool mMixIdenticalParticles = false; std::mt19937 mRng; std::uniform_int_distribution<> mDist{static_cast(pairprocesshelpers::kOrder12), @@ -886,6 +920,10 @@ template (registry, colHistSpec, confCollisionBinning); - mTrackHistManager.template init(registry, trackHistSpec, confTrackSelection); - mD0HistManager.template init(registry, d0HistSpec, confD0Selection, posDauHistSpec, negDauHistSpec); + mTrackHistManagerSe.template init(registry, trackHistSpec, confTrackSelection); + mD0HistManagerSe.template init(registry, d0HistSpec, confD0Selection, posDauHistSpec, negDauHistSpec); + mTrackHistManagerMe.template init(registry, trackHistSpec, confTrackSelection); + mD0HistManagerMe.template init(registry, d0HistSpec, confD0Selection, posDauHistSpec, negDauHistSpec); mTrackCleaner.init(confTrackCleaner); mD0Cleaner.init(confD0Cleaner); @@ -971,6 +1011,7 @@ class PairTrackD0Builder // setup mixing mMixingPolicy = static_cast(confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; } /// \return true if at least one pair passed all pair selections (usable as pair trigger) @@ -984,7 +1025,7 @@ class PairTrackD0Builder } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(trackSlice, d0Slice, trackTable, col, mTrackHistManager, mD0HistManager, mPairHistManagerSe, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, d0Slice, trackTable, col, mTrackHistManagerSe, mD0HistManagerSe, mPairHistManagerSe, mCprSe, mPcSe); } template @@ -992,13 +1033,13 @@ class PairTrackD0Builder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, trackPartition, d0Partition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, d0Partition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mD0HistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, d0Partition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, d0Partition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mD0HistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, d0Partition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, d0Partition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mD0HistManagerMe); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1016,7 +1057,7 @@ class PairTrackD0Builder } mColHistManager.template fill(col, mcCols); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(trackSlice, d0Slice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager, mD0HistManager, mPairHistManagerSe, mTrackCleaner, mD0Cleaner, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, d0Slice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe, mD0HistManagerSe, mPairHistManagerSe, mTrackCleaner, mD0Cleaner, mCprSe, mPcSe); } template @@ -1024,13 +1065,13 @@ class PairTrackD0Builder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, d0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mD0Cleaner, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, d0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mD0Cleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mD0HistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, d0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mD0Cleaner, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, d0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mD0Cleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mD0HistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, d0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mD0Cleaner, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, d0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mD0Cleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mD0HistManagerMe); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1039,8 +1080,10 @@ class PairTrackD0Builder private: colhistmanager::CollisionHistManager mColHistManager; - trackhistmanager::TrackHistManager mTrackHistManager; - charmhadronhistmanager::CharmHadronHistManager mD0HistManager; // the third prong prefix is unused for 2-prong candidates + trackhistmanager::TrackHistManager mTrackHistManagerSe; + charmhadronhistmanager::CharmHadronHistManager mD0HistManagerSe; // the third prong prefix is unused for 2-prong candidates + trackhistmanager::TrackHistManager mTrackHistManagerMe; + charmhadronhistmanager::CharmHadronHistManager mD0HistManagerMe; // the third prong prefix is unused for 2-prong candidates particlecleaner::ParticleCleaner mTrackCleaner; particlecleaner::ParticleCleaner mD0Cleaner; pairhistmanager::PairHistManager mPairHistManagerSe; @@ -1051,6 +1094,7 @@ class PairTrackD0Builder paircleaner::TrackV0PairCleaner mPcMe; pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; int mMixingDepth = 5; + int mEventStride = 1; }; template (registry, colHistSpec, confCollisionBinning); - mTrackHistManager.template init(registry, trackHistSpec, confTrackSelection); - mLcHistManager.template init(registry, lcHistSpec, confLcSelection, protonDauHistSpec, kaonDauHistSpec, pionDauHistSpec); + mTrackHistManagerSe.template init(registry, trackHistSpec, confTrackSelection); + mLcHistManagerSe.template init(registry, lcHistSpec, confLcSelection, protonDauHistSpec, kaonDauHistSpec, pionDauHistSpec); + mTrackHistManagerMe.template init(registry, trackHistSpec, confTrackSelection); + mLcHistManagerMe.template init(registry, lcHistSpec, confLcSelection, protonDauHistSpec, kaonDauHistSpec, pionDauHistSpec); mTrackCleaner.init(confTrackCleaner); mLcCleaner.init(confLcCleaner); @@ -1129,6 +1180,7 @@ class PairTrackLcBuilder // setup mixing mMixingPolicy = static_cast(confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; } /// \return true if at least one pair passed all pair selections (usable as pair trigger) @@ -1142,7 +1194,7 @@ class PairTrackLcBuilder } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(trackSlice, lcSlice, trackTable, col, mTrackHistManager, mLcHistManager, mPairHistManagerSe, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, lcSlice, trackTable, col, mTrackHistManagerSe, mLcHistManagerSe, mPairHistManagerSe, mCprSe, mPcSe); } template @@ -1150,13 +1202,13 @@ class PairTrackLcBuilder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, trackPartition, lcPartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, lcPartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mLcHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, lcPartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, lcPartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mLcHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, lcPartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, lcPartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mLcHistManagerMe); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1174,7 +1226,7 @@ class PairTrackLcBuilder } mColHistManager.template fill(col, mcCols); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(trackSlice, lcSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager, mLcHistManager, mPairHistManagerSe, mTrackCleaner, mLcCleaner, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, lcSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe, mLcHistManagerSe, mPairHistManagerSe, mTrackCleaner, mLcCleaner, mCprSe, mPcSe); } template @@ -1182,13 +1234,13 @@ class PairTrackLcBuilder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, lcPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mLcCleaner, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, lcPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mLcCleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mLcHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, lcPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mLcCleaner, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, lcPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mLcCleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mLcHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, lcPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mLcCleaner, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, lcPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mLcCleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mLcHistManagerMe); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1197,8 +1249,10 @@ class PairTrackLcBuilder private: colhistmanager::CollisionHistManager mColHistManager; - trackhistmanager::TrackHistManager mTrackHistManager; - charmhadronhistmanager::CharmHadronHistManager mLcHistManager; + trackhistmanager::TrackHistManager mTrackHistManagerSe; + charmhadronhistmanager::CharmHadronHistManager mLcHistManagerSe; + trackhistmanager::TrackHistManager mTrackHistManagerMe; + charmhadronhistmanager::CharmHadronHistManager mLcHistManagerMe; particlecleaner::ParticleCleaner mTrackCleaner; particlecleaner::ParticleCleaner mLcCleaner; pairhistmanager::PairHistManager mPairHistManagerSe; @@ -1209,12 +1263,17 @@ class PairTrackLcBuilder paircleaner::TrackLcPairCleaner mPcMe; pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; int mMixingDepth = 5; + int mEventStride = 1; }; template (registry, colHistSpec, confCollisionBinning); - mTrackHistManager.template init(registry, trackHistSpec, confTrackSelection); - mV0HistManager.template init(registry, v0HistSpec, confV0Selection, posDauHistSpec, negDauHistSpec); + mTrackHistManagerSe.template init(registry, trackHistSpec, confTrackSelection); + mV0HistManagerSe.template init(registry, v0HistSpec, confV0Selection, posDauHistSpec, negDauHistSpec); + mTrackHistManagerMe.template init(registry, trackHistSpec, confTrackSelection); + mV0HistManagerMe.template init(registry, v0HistSpec, confV0Selection, posDauHistSpec, negDauHistSpec); mTrackCleaner.init(confTrackCleaner); mV0Cleaner.init(confV0Cleaner); @@ -1302,6 +1363,7 @@ class PairTrackV0Builder // setup mixing mMixingPolicy = static_cast(confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; } /// \return true if at least one pair passed all pair selections (usable as pair trigger) @@ -1315,7 +1377,7 @@ class PairTrackV0Builder } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(trackSlice, v0Slice, trackTable, col, mTrackHistManager, mV0HistManager, mPairHistManagerSe, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, v0Slice, trackTable, col, mTrackHistManagerSe, mV0HistManagerSe, mPairHistManagerSe, mCprSe, mPcSe); } /// \return true if at least one pair passed all pair selections (usable as pair trigger) @@ -1329,7 +1391,7 @@ class PairTrackV0Builder } mColHistManager.template fill(col, mcCols); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(trackSlice, v0Slice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager, mV0HistManager, mPairHistManagerSe, mTrackCleaner, mV0Cleaner, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, v0Slice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe, mV0HistManagerSe, mPairHistManagerSe, mTrackCleaner, mV0Cleaner, mCprSe, mPcSe); } template @@ -1337,13 +1399,13 @@ class PairTrackV0Builder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, trackPartition, v0Partition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, v0Partition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mV0HistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, v0Partition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, v0Partition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mV0HistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, v0Partition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, v0Partition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mV0HistManagerMe); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1355,13 +1417,13 @@ class PairTrackV0Builder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, v0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mV0Cleaner, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, v0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mV0Cleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mV0HistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, v0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mV0Cleaner, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, v0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mV0Cleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mV0HistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, v0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mV0Cleaner, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, v0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mV0Cleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mV0HistManagerMe); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1370,8 +1432,10 @@ class PairTrackV0Builder private: colhistmanager::CollisionHistManager mColHistManager; - trackhistmanager::TrackHistManager mTrackHistManager; - v0histmanager::V0HistManager mV0HistManager; + trackhistmanager::TrackHistManager mTrackHistManagerSe; + v0histmanager::V0HistManager mV0HistManagerSe; + trackhistmanager::TrackHistManager mTrackHistManagerMe; + v0histmanager::V0HistManager mV0HistManagerMe; particlecleaner::ParticleCleaner mTrackCleaner; particlecleaner::ParticleCleaner mV0Cleaner; pairhistmanager::PairHistManager mPairHistManagerSe; @@ -1382,12 +1446,150 @@ class PairTrackV0Builder paircleaner::TrackV0PairCleaner mPcMe; pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; int mMixingDepth = 5; + int mEventStride = 1; +}; + +// Track-TwoPhotonResonance pair builder. +// Note: close pair rejection is a no-op (ClosePairRejectionTrackTwoPhotonResonance) and pair cleaning +// reuses the plain TrackTrackPairCleaner (globalIndex-only), since photon daughters are, for +// now, not indexed into FTracks: they are TPC-only tracks, so they cannot be confused for a +// global track the way two V0 daughters or a track and a V0 daughter can. +template +class PairTrackTwoPhotonResonanceBuilder +{ + public: + PairTrackTwoPhotonResonanceBuilder() = default; + ~PairTrackTwoPhotonResonanceBuilder() = default; + + template + void init(o2::framework::HistogramRegistry* registry, + T1 const& confCollisionBinning, + T2 const& confTrackSelection, + T3 const& confResonanceSelection, + T4 const& confCpr, + T5 const& confMixing, + T6 const& confPairBinning, + T7 const& confPairCuts, + T8 const& confDauQaBinning, + std::map> const& colHistSpec, + std::map> const& trackHistSpec, + std::map> const& resonanceHistSpec, + std::map> const& dauHistSpec, + std::map> const& pairHistSpec, + std::map> const& cprHistSpec, + std::map> const& pairCleanerHistSpec) + { + mColHistManager.template init(registry, colHistSpec, confCollisionBinning); + + mTrackHistManagerSe.template init(registry, trackHistSpec, confTrackSelection); + mResonanceHistManagerSe.template init(registry, resonanceHistSpec, dauHistSpec, confDauQaBinning); + mTrackHistManagerMe.template init(registry, trackHistSpec, confTrackSelection); + mResonanceHistManagerMe.template init(registry, resonanceHistSpec, dauHistSpec, confDauQaBinning); + + mPairHistManagerSe.template init(registry, pairHistSpec, confPairBinning, confPairCuts, confMixing); + mPairHistManagerSe.setMass(confTrackSelection.pdgCodeAbs.value, confResonanceSelection.pdgCodeAbs.value); + mPairHistManagerSe.setCharge(confTrackSelection.chargeAbs.value, 1); + mCprSe.init(registry, cprHistSpec, confCpr, confTrackSelection.chargeAbs.value); + + mPairHistManagerMe.template init(registry, pairHistSpec, confPairBinning, confPairCuts, confMixing); + mPairHistManagerMe.setMass(confTrackSelection.pdgCodeAbs.value, confResonanceSelection.pdgCodeAbs.value); + mPairHistManagerMe.setCharge(confTrackSelection.chargeAbs.value, 1); + mCprMe.init(registry, cprHistSpec, confCpr, confTrackSelection.chargeAbs.value); + + mPcSe.template init(registry, pairCleanerHistSpec, confPairCuts); + mPcMe.template init(registry, pairCleanerHistSpec, confPairCuts); + + // setup mixing + mMixingPolicy = static_cast(confMixing.policy.value); + mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; + } + + /// \return true if at least one pair passed all pair selections (usable as pair trigger) + template + bool processSameEvent(T1 const& col, T2& trackPartition, T3& resonancePartition, T4& photonTable, T5& cache) + { + auto trackSlice = trackPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto resonanceSlice = resonancePartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (trackSlice.size() < nLimitPartitionParticles || resonanceSlice.size() < nLimitPartitionParticles) { + return false; + } + mColHistManager.template fill(col); + mCprSe.setMagField(col.magField()); + return pairprocesshelpers::processSameEvent(trackSlice, resonanceSlice, photonTable, col, mTrackHistManagerSe, mResonanceHistManagerSe, mPairHistManagerSe, mCprSe, mPcSe); + } + + template + void processMixedEvent(T1 const& cols, T2& trackPartition, T3& resonancePartition, T4& photonTable, T5& cache, T6& binsVtxMult, T7& binsVtxCent, T8& binsVtxMultCent) + { + switch (mMixingPolicy) { + case static_cast(pairhistmanager::kVtxMult): + pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, photonTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mResonanceHistManagerMe); + break; + case static_cast(pairhistmanager::kVtxCent): + pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, photonTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mResonanceHistManagerMe); + break; + case static_cast(pairhistmanager::kVtxMultCent): + pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, photonTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mResonanceHistManagerMe); + break; + default: + LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + } + } + + private: + colhistmanager::CollisionHistManager mColHistManager; + trackhistmanager::TrackHistManager mTrackHistManagerSe; + twophotonresonancehistmanager::TwoPhotonResonanceHistManager mResonanceHistManagerSe; + trackhistmanager::TrackHistManager mTrackHistManagerMe; + twophotonresonancehistmanager::TwoPhotonResonanceHistManager mResonanceHistManagerMe; + pairhistmanager::PairHistManager mPairHistManagerSe; + pairhistmanager::PairHistManager mPairHistManagerMe; + closepairrejection::ClosePairRejectionTrackTwoPhotonResonance mCprSe; + closepairrejection::ClosePairRejectionTrackTwoPhotonResonance mCprMe; + paircleaner::TrackTrackPairCleaner mPcSe; + paircleaner::TrackTrackPairCleaner mPcMe; + pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; + int mMixingDepth = 5; + int mEventStride = 1; }; template (registry, colHistSpec, confCollisionBinning); - mTrackHistManager.template init(registry, trackHistSpec, confTrackSelection); - mResonanceHistManager.template init(registry, resonanceHistSpec, confResonanceSelection, posDauHistSpec, negDauHistSpec); + mTrackHistManagerSe.template init(registry, trackHistSpec, confTrackSelection); + mResonanceHistManagerSe.template init(registry, resonanceHistSpec, confResonanceSelection, posDauHistSpec, negDauHistSpec); + mTrackHistManagerMe.template init(registry, trackHistSpec, confTrackSelection); + mResonanceHistManagerMe.template init(registry, resonanceHistSpec, confResonanceSelection, posDauHistSpec, negDauHistSpec); mPairHistManagerSe.template init(registry, pairHistSpec, confPairBinning, confPairCuts, confMixing); mPairHistManagerSe.setMass(confTrackSelection.pdgCodeAbs.value, confResonanceSelection.pdgCodeAbs.value); @@ -1453,6 +1657,7 @@ class PairTrackTwoTrackResonanceBuilder // setup mixing mMixingPolicy = static_cast(confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; } /// \return true if at least one pair passed all pair selections (usable as pair trigger) @@ -1466,7 +1671,7 @@ class PairTrackTwoTrackResonanceBuilder } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(trackSlice, resonanaceSlice, trackTable, col, mTrackHistManager, mResonanceHistManager, mPairHistManagerSe, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, resonanaceSlice, trackTable, col, mTrackHistManagerSe, mResonanceHistManagerSe, mPairHistManagerSe, mCprSe, mPcSe); } template @@ -1474,13 +1679,13 @@ class PairTrackTwoTrackResonanceBuilder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mResonanceHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mResonanceHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mResonanceHistManagerMe); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1489,8 +1694,10 @@ class PairTrackTwoTrackResonanceBuilder private: colhistmanager::CollisionHistManager mColHistManager; - trackhistmanager::TrackHistManager mTrackHistManager; - twotrackresonancehistmanager::TwoTrackResonanceHistManager mResonanceHistManager; + trackhistmanager::TrackHistManager mTrackHistManagerSe; + twotrackresonancehistmanager::TwoTrackResonanceHistManager mResonanceHistManagerSe; + trackhistmanager::TrackHistManager mTrackHistManagerMe; + twotrackresonancehistmanager::TwoTrackResonanceHistManager mResonanceHistManagerMe; pairhistmanager::PairHistManager mPairHistManagerSe; pairhistmanager::PairHistManager mPairHistManagerMe; closepairrejection::ClosePairRejectionTrackV0 mCprSe; // cpr for twotrackresonances and v0 work the same way @@ -1499,6 +1706,7 @@ class PairTrackTwoTrackResonanceBuilder paircleaner::TrackV0PairCleaner mPcMe; // pc for twotrackresonances and v0 work the same way pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; int mMixingDepth = 5; + int mEventStride = 1; }; template (registry, colHistSpec, confCollisionBinning); - mV0HistManager.template init(registry, v0HistSpec, confV0Selection, V0posDauHistSpec, V0negDauHistSpec); - mResonanceHistManager.template init(registry, resonanceHistSpec, confResonanceSelection, ResonancePosDauHistSpec, ResonanceNegDauHistSpec); + mV0HistManagerSe.template init(registry, v0HistSpec, confV0Selection, V0posDauHistSpec, V0negDauHistSpec); + mResonanceHistManagerSe.template init(registry, resonanceHistSpec, confResonanceSelection, ResonancePosDauHistSpec, ResonanceNegDauHistSpec); + mV0HistManagerMe.template init(registry, v0HistSpec, confV0Selection, V0posDauHistSpec, V0negDauHistSpec); + mResonanceHistManagerMe.template init(registry, resonanceHistSpec, confResonanceSelection, ResonancePosDauHistSpec, ResonanceNegDauHistSpec); mPairHistManagerSe.template init(registry, pairHistSpec, confPairBinning, confPairCuts, confMixing); mPairHistManagerSe.setMass(confV0Selection.pdgCodeAbs.value, confResonanceSelection.pdgCodeAbs.value); @@ -1583,6 +1799,7 @@ class PairV0TwoTrackResonanceBuilder // setup mixing mMixingPolicy = static_cast(confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; } /// \return true if at least one pair passed all pair selections (usable as pair trigger) @@ -1596,7 +1813,7 @@ class PairV0TwoTrackResonanceBuilder } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(v0Slice, resonanaceSlice, trackTable, col, mV0HistManager, mResonanceHistManager, mPairHistManagerSe, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(v0Slice, resonanaceSlice, trackTable, col, mV0HistManagerSe, mResonanceHistManagerSe, mPairHistManagerSe, mCprSe, mPcSe); } template @@ -1604,13 +1821,13 @@ class PairV0TwoTrackResonanceBuilder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, v0Partition, resonancePartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, v0Partition, resonancePartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mV0HistManagerMe, mResonanceHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, v0Partition, resonancePartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, v0Partition, resonancePartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mV0HistManagerMe, mResonanceHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, v0Partition, resonancePartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, v0Partition, resonancePartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mV0HistManagerMe, mResonanceHistManagerMe); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1619,8 +1836,10 @@ class PairV0TwoTrackResonanceBuilder private: colhistmanager::CollisionHistManager mColHistManager; - v0histmanager::V0HistManager mV0HistManager; - twotrackresonancehistmanager::TwoTrackResonanceHistManager mResonanceHistManager; + v0histmanager::V0HistManager mV0HistManagerSe; + twotrackresonancehistmanager::TwoTrackResonanceHistManager mResonanceHistManagerSe; + v0histmanager::V0HistManager mV0HistManagerMe; + twotrackresonancehistmanager::TwoTrackResonanceHistManager mResonanceHistManagerMe; pairhistmanager::PairHistManager mPairHistManagerSe; pairhistmanager::PairHistManager mPairHistManagerMe; closepairrejection::ClosePairRejectionV0V0 mCprSe; // cpr for twotrackresonances and v0 work the same way @@ -1629,11 +1848,15 @@ class PairV0TwoTrackResonanceBuilder paircleaner::V0V0PairCleaner mPcMe; // pc for twotrackresonances and v0 work the same way pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; int mMixingDepth = 5; + int mEventStride = 1; }; template (registry, colHistSpec, confCollisionBinning); - mTrackHistManager.template init(registry, trackHistSpec, confTrackSelection); - mKinkHistManager.template init(registry, kinkHistSpec, confKinkSelection, chaDauHistSpec); + mTrackHistManagerSe.template init(registry, trackHistSpec, confTrackSelection); + mKinkHistManagerSe.template init(registry, kinkHistSpec, confKinkSelection, chaDauHistSpec); + mTrackHistManagerMe.template init(registry, trackHistSpec, confTrackSelection); + mKinkHistManagerMe.template init(registry, kinkHistSpec, confKinkSelection, chaDauHistSpec); mTrackCleaner.init(confTrackCleaner); mKinkCleaner.init(confKinkCleaner); @@ -1704,6 +1929,7 @@ class PairTrackKinkBuilder // setup mixing mMixingPolicy = static_cast(confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; } /// \return true if at least one pair passed all pair selections (usable as pair trigger) @@ -1717,7 +1943,7 @@ class PairTrackKinkBuilder } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(trackSlice, kinkSlice, trackTable, col, mTrackHistManager, mKinkHistManager, mPairHistManagerSe, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, kinkSlice, trackTable, col, mTrackHistManagerSe, mKinkHistManagerSe, mPairHistManagerSe, mCprSe, mPcSe); } /// \return true if at least one pair passed all pair selections (usable as pair trigger) @@ -1731,7 +1957,7 @@ class PairTrackKinkBuilder } mColHistManager.template fill(col, mcCols); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(trackSlice, kinkSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager, mKinkHistManager, mPairHistManagerSe, mTrackCleaner, mKinkCleaner, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, kinkSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe, mKinkHistManagerSe, mPairHistManagerSe, mTrackCleaner, mKinkCleaner, mCprSe, mPcSe); } template @@ -1739,13 +1965,13 @@ class PairTrackKinkBuilder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, trackPartition, kinkPartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, kinkPartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mKinkHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, kinkPartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, kinkPartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mKinkHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, kinkPartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, kinkPartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mKinkHistManagerMe); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1757,13 +1983,13 @@ class PairTrackKinkBuilder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, kinkPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mKinkCleaner, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, kinkPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mKinkCleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mKinkHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, kinkPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mKinkCleaner, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, kinkPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mKinkCleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mKinkHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, kinkPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mKinkCleaner, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, kinkPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mKinkCleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mKinkHistManagerMe); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1772,8 +1998,10 @@ class PairTrackKinkBuilder private: colhistmanager::CollisionHistManager mColHistManager; - trackhistmanager::TrackHistManager mTrackHistManager; - kinkhistmanager::KinkHistManager mKinkHistManager; + trackhistmanager::TrackHistManager mTrackHistManagerSe; + kinkhistmanager::KinkHistManager mKinkHistManagerSe; + trackhistmanager::TrackHistManager mTrackHistManagerMe; + kinkhistmanager::KinkHistManager mKinkHistManagerMe; particlecleaner::ParticleCleaner mTrackCleaner; particlecleaner::ParticleCleaner mKinkCleaner; pairhistmanager::PairHistManager mPairHistManagerSe; @@ -1784,6 +2012,7 @@ class PairTrackKinkBuilder paircleaner::TrackKinkPairCleaner mPcMe; pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; int mMixingDepth = 5; + int mEventStride = 1; }; template (registry, colHistSpec, confCollisionBinning); - mTrackHistManager.template init(registry, trackHistSpec, confTrackSelection); - mCascadeHistManager.template init(registry, cascadeHistSpec, confCascadeSelection, bachelorHistSpec, posDauHistSpec, negDauHistSpec); + mTrackHistManagerSe.template init(registry, trackHistSpec, confTrackSelection); + mCascadeHistManagerSe.template init(registry, cascadeHistSpec, confCascadeSelection, bachelorHistSpec, posDauHistSpec, negDauHistSpec); + mTrackHistManagerMe.template init(registry, trackHistSpec, confTrackSelection); + mCascadeHistManagerMe.template init(registry, cascadeHistSpec, confCascadeSelection, bachelorHistSpec, posDauHistSpec, negDauHistSpec); mTrackCleaner.init(confTrackCleaner); mCascadeCleaner.init(confCascadeCleaner); @@ -1871,6 +2107,7 @@ class PairTrackCascadeBuilder // setup mixing mMixingPolicy = static_cast(confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; } /// \return true if at least one pair passed all pair selections (usable as pair trigger) @@ -1884,7 +2121,7 @@ class PairTrackCascadeBuilder } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(trackSlice, cascadeSlice, trackTable, col, mTrackHistManager, mCascadeHistManager, mPairHistManagerSe, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, cascadeSlice, trackTable, col, mTrackHistManagerSe, mCascadeHistManagerSe, mPairHistManagerSe, mCprSe, mPcSe); } /// \return true if at least one pair passed all pair selections (usable as pair trigger) @@ -1898,7 +2135,7 @@ class PairTrackCascadeBuilder } mColHistManager.template fill(col, mcCols); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(trackSlice, cascadeSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager, mCascadeHistManager, mPairHistManagerSe, mTrackCleaner, mCascadeCleaner, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, cascadeSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe, mCascadeHistManagerSe, mPairHistManagerSe, mTrackCleaner, mCascadeCleaner, mCprSe, mPcSe); } template @@ -1906,13 +2143,13 @@ class PairTrackCascadeBuilder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, trackPartition, cascadePartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, cascadePartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, cascadePartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, cascadePartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, cascadePartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, cascadePartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mCascadeHistManagerMe); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1924,13 +2161,13 @@ class PairTrackCascadeBuilder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, cascadePartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mCascadeCleaner, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, cascadePartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mCascadeCleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, cascadePartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mCascadeCleaner, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, cascadePartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mCascadeCleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, cascadePartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mCascadeCleaner, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, cascadePartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mCascadeCleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mCascadeHistManagerMe); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1939,8 +2176,10 @@ class PairTrackCascadeBuilder private: colhistmanager::CollisionHistManager mColHistManager; - trackhistmanager::TrackHistManager mTrackHistManager; - cascadehistmanager::CascadeHistManager mCascadeHistManager; + trackhistmanager::TrackHistManager mTrackHistManagerSe; + cascadehistmanager::CascadeHistManager mCascadeHistManagerSe; + trackhistmanager::TrackHistManager mTrackHistManagerMe; + cascadehistmanager::CascadeHistManager mCascadeHistManagerMe; particlecleaner::ParticleCleaner mTrackCleaner; particlecleaner::ParticleCleaner mCascadeCleaner; pairhistmanager::PairHistManager mPairHistManagerSe; @@ -1951,9 +2190,10 @@ class PairTrackCascadeBuilder paircleaner::TrackCascadePairCleaner mPcMe; pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; int mMixingDepth = 5; + int mEventStride = 1; }; -template +template class PairMcParticleMcParticleBuilder { public: @@ -2011,7 +2251,8 @@ class PairMcParticleMcParticleBuilder if (mSameSpecies) { mMcParticleCleaner1.init(confMcParticleCleaner1); - mMcParticleHistManager1.init(registry, mcParticleHistSpec1, confMcParticleBinning1); + mMcParticleHistManagerSe1.init(registry, mcParticleHistSpec1, confMcParticleBinning1); + mMcParticleHistManagerMe1.init(registry, mcParticleHistSpec1, confMcParticleBinning1); mPairHistManagerSe.setMass(confMcParticleSelection1.pdgCodeAbs.value, confMcParticleSelection1.pdgCodeAbs.value); mPairHistManagerSe.setCharge(1, 1); @@ -2023,8 +2264,10 @@ class PairMcParticleMcParticleBuilder } else { mMcParticleCleaner1.init(confMcParticleCleaner1); mMcParticleCleaner2.init(confMcParticleCleaner2); - mMcParticleHistManager1.init(registry, mcParticleHistSpec1, confMcParticleBinning1); - mMcParticleHistManager2.init(registry, mcParticleHistSpec2, confMcParticleBinning2); + mMcParticleHistManagerSe1.init(registry, mcParticleHistSpec1, confMcParticleBinning1); + mMcParticleHistManagerSe2.init(registry, mcParticleHistSpec2, confMcParticleBinning2); + mMcParticleHistManagerMe1.init(registry, mcParticleHistSpec1, confMcParticleBinning1); + mMcParticleHistManagerMe2.init(registry, mcParticleHistSpec2, confMcParticleBinning2); mPairHistManagerSe.setMass(confMcParticleSelection1.pdgCodeAbs.value, confMcParticleSelection2.pdgCodeAbs.value); mPairHistManagerSe.setCharge(1, 1); @@ -2038,6 +2281,7 @@ class PairMcParticleMcParticleBuilder // setup mixing mMixingPolicy = static_cast(confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; // setup rng if necessary if (confMixing.seed.value >= 0) { @@ -2069,7 +2313,7 @@ class PairMcParticleMcParticleBuilder if (mMixIdenticalParticles) { pairOrder = static_cast(mDist(mRng)); } - return pairprocesshelpers::processSameEvent(mcParticleSlice, mcParticles, mcMothers, mcPartonicMothers, col, mMcParticleHistManager1, mPairHistManagerSe, mMcParticleCleaner1, mCprSe, mPcSe, pairOrder); + return pairprocesshelpers::processSameEvent(mcParticleSlice, mcParticles, mcMothers, mcPartonicMothers, col, mMcParticleHistManagerSe1, mPairHistManagerSe, mMcParticleCleaner1, mCprSe, mPcSe, pairOrder); } auto mcParticleSlice1 = partition1->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, col.globalIndex(), cache); @@ -2078,7 +2322,7 @@ class PairMcParticleMcParticleBuilder return false; } mColHistManager.template fill(col); - return pairprocesshelpers::processSameEvent(mcParticleSlice1, mcParticleSlice2, mcParticles, mcMothers, mcPartonicMothers, col, mMcParticleHistManager1, mMcParticleHistManager2, mPairHistManagerSe, mMcParticleCleaner1, mMcParticleCleaner2, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(mcParticleSlice1, mcParticleSlice2, mcParticles, mcMothers, mcPartonicMothers, col, mMcParticleHistManagerSe1, mMcParticleHistManagerSe2, mPairHistManagerSe, mMcParticleCleaner1, mMcParticleCleaner2, mCprSe, mPcSe); } template @@ -2087,13 +2331,13 @@ class PairMcParticleMcParticleBuilder if (mSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mCprMe, mPcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mCprMe, mPcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mCprMe, mPcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -2101,13 +2345,13 @@ class PairMcParticleMcParticleBuilder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner2, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner2, mCprMe, mPcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe2); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner2, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner2, mCprMe, mPcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe2); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner2, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner2, mCprMe, mPcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe2); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -2117,8 +2361,10 @@ class PairMcParticleMcParticleBuilder private: colhistmanager::CollisionHistManager mColHistManager; - mcparticlehistmanager::McParticleHistManager mMcParticleHistManager1; - mcparticlehistmanager::McParticleHistManager mMcParticleHistManager2; + mcparticlehistmanager::McParticleHistManager mMcParticleHistManagerSe1; + mcparticlehistmanager::McParticleHistManager mMcParticleHistManagerSe2; + mcparticlehistmanager::McParticleHistManager mMcParticleHistManagerMe1; + mcparticlehistmanager::McParticleHistManager mMcParticleHistManagerMe2; pairhistmanager::PairHistManager mPairHistManagerSe; pairhistmanager::PairHistManager mPairHistManagerMe; closepairrejection::ClosePairRejectionMcParticleMcParticle mCprSe; @@ -2129,6 +2375,7 @@ class PairMcParticleMcParticleBuilder particlecleaner::ParticleCleaner mMcParticleCleaner2; pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; int mMixingDepth = 5; + int mEventStride = 1; bool mSameSpecies = false; bool mMixIdenticalParticles = false; std::mt19937 mRng; diff --git a/PWGCF/Femto/Core/pairCleaner.h b/PWGCF/Femto/Core/pairCleaner.h index 6bf859149b8..358ba59962f 100644 --- a/PWGCF/Femto/Core/pairCleaner.h +++ b/PWGCF/Femto/Core/pairCleaner.h @@ -84,6 +84,8 @@ constexpr char PrefixPairCleanerTrackLcSe[] = "TrackLcCleaner/SE/"; constexpr char PrefixPairCleanerTrackLcMe[] = "TrackLcCleaner/ME/"; constexpr char PrefixPairCleanerTrackResonanceSe[] = "TrackResonanceCleaner/SE/"; constexpr char PrefixPairCleanerTrackResonanceMe[] = "TrackResonanceCleaner/ME/"; +constexpr char PrefixPairCleanerTrackTwoPhotonResonanceSe[] = "TrackTwoPhotonResonanceCleaner/SE/"; +constexpr char PrefixPairCleanerTrackTwoPhotonResonanceMe[] = "TrackTwoPhotonResonanceCleaner/ME/"; constexpr char PrefixPairCleanerV0ResonanceSe[] = "V0ResonanceCleaner/SE/"; constexpr char PrefixPairCleanerV0ResonanceMe[] = "V0ResonanceCleaner/ME/"; constexpr char PrefixPairCleanerTrackKinkSe[] = "TrackKinkCleaner/SE/"; @@ -100,6 +102,9 @@ class BasePairCleaner BasePairCleaner() = default; virtual ~BasePairCleaner() = default; + // pairCuts is the pair/triplet histogram manager's own ConfPairCuts/ConfTripletCuts group, which + // carries this cleaner's own kinematicVariable Configurable (independent from CPR's and from + // whatever the pair/triplet histogram manager itself may be plotting elsewhere). template void init(o2::framework::HistogramRegistry* registry, T1 const& specs, @@ -120,7 +125,9 @@ class BasePairCleaner mHistogramRegistry->add(std::string(prefix) + getHistNameV2(kStats, HistTable), getHistDesc(kStats, HistTable), getHistType(kStats, HistTable), {axisStats}); mHistogramRegistry->get(HIST(prefix) + HIST(histmanager::getHistName(kStats, HistTable)))->GetXaxis()->SetBinLabel(1, "All analyzed reco pairs"); mHistogramRegistry->get(HIST(prefix) + HIST(histmanager::getHistName(kStats, HistTable)))->GetXaxis()->SetBinLabel(2, "All blocked reco pairs"); - mHistogramRegistry->add(std::string(prefix) + getHistNameV2(kKinematic, HistTable), getHistDesc(kKinematic, HistTable), getHistType(kKinematic, HistTable), {specs.at(kKinematic)}); + mKinematicVariable = static_cast(pairCuts.kinematicVariable.value); + std::string kinematicDesc = histmanager::replaceFirstAxisTitle(getHistDesc(kKinematic, HistTable), modes::kinematicVariableToAxisTitle(mKinematicVariable)); + mHistogramRegistry->add(std::string(prefix) + getHistNameV2(kKinematic, HistTable), kinematicDesc.c_str(), getHistType(kKinematic, HistTable), {specs.at(kKinematic)}); } protected: @@ -158,13 +165,13 @@ class BasePairCleaner bool mcPairHasNonCommonAncestor(T1 const& particle1, T2 const& particle2, T3 const& partonicMothers) const { if (mUseMotherAsAncestor) { - // if one of the two particles has no associated mother, we cannot know if they have a common anchestor, so we break out with false + // if one of the two particles has no associated mother, we cannot know if they have a common ancestor, so we break out with false if (!particle1.has_fMcMother() || !particle2.has_fMcMother()) { return false; } return !this->mcPairHasCommonMother(particle1, particle2); } - // if one of the two particles has no associated partonic mother, we cannot know if they have a common anchestor, so we break out with false + // if one of the two particles has no associated partonic mother, we cannot know if they have a common ancestor, so we break out with false if (!particle1.has_fMcPartMoth() || !particle2.has_fMcPartMoth()) { return false; } @@ -175,7 +182,7 @@ class BasePairCleaner template bool pairHasCommonAncestor(T1 const& particle1, T2 const& particle2, T3 const& /*mcparticles*/, T4 const& partonicMothers) const { - // if one of the two particles has no associated mc particle, we cannot know if they have a common anchestor, so we break out with false + // if one of the two particles has no associated mc particle, we cannot know if they have a common ancestor, so we break out with false if (!particle1.has_fMcParticle() || !particle2.has_fMcParticle()) { return false; } @@ -190,7 +197,7 @@ class BasePairCleaner template bool pairHasNonCommonAncestor(T1 const& particle1, T2 const& particle2, T3 const& /*mcparticles*/, T4 const& partonicMothers) const { - // if one of the two particles has no associated mc particle, we cannot know if they have a common anchestor, so we break out with false + // if one of the two particles has no associated mc particle, we cannot know if they have a common ancestor, so we break out with false if (!particle1.has_fMcParticle() || !particle2.has_fMcParticle()) { return false; } @@ -204,13 +211,14 @@ class BasePairCleaner bool mMixPairsWithCommonAncestor = false; bool mMixPairsWithNonCommonAncestor = false; + modes::KinematicVariable mKinematicVariable = modes::KinematicVariable::kKstar; private: // require both particles to originate from the same partonic mother template bool mcPairHasCommonPartonicMother(T1 const& particle1, T2 const& particle2, T3 const& /*partonicMothers*/) const { - // if one of the two particles has no associated partonic mother, we cannot know if they have a common anchestor, so we break out with false + // if one of the two particles has no associated partonic mother, we cannot know if they have a common ancestor, so we break out with false if (!particle1.has_fMcPartMoth() || !particle2.has_fMcPartMoth()) { return false; } @@ -227,7 +235,7 @@ class BasePairCleaner template bool mcPairHasCommonMother(T1 const& particle1, T2 const& particle2) const { - // if one of the two particles has no associated mother, we cannot know if they have a common anchestor, so we break out with false + // if one of the two particles has no associated mother, we cannot know if they have a common ancestor, so we break out with false if (!particle1.has_fMcMother() || !particle2.has_fMcMother()) { return false; } @@ -251,7 +259,7 @@ class TrackTrackPairCleaner : public BasePairCleaner this->fillAll(); bool isClean = this->isCleanParticlePair(track1, track2); if (!isClean) { - this->fillBlocked(pairHistManager.getKinematic()); + this->fillBlocked(pairHistManager.getKinematic(this->mKinematicVariable)); } return isClean; } @@ -292,7 +300,7 @@ class V0V0PairCleaner : public BasePairCleaner // also works for particl this->isCleanParticlePair(posDaughter1, posDaughter2) && this->isCleanParticlePair(negDaughter1, negDaughter2) && this->isCleanParticlePair(posDaughter1, negDaughter2) && this->isCleanParticlePair(negDaughter1, posDaughter2); if (!isClean) { - this->fillBlocked(pairHistManager.getKinematic()); + this->fillBlocked(pairHistManager.getKinematic(this->mKinematicVariable)); } return isClean; } @@ -329,7 +337,7 @@ class TrackV0PairCleaner : public BasePairCleaner // also works for part auto negDaughter = trackTable.rawIteratorAt(v0.negDauId() - trackTable.offset()); bool isClean = this->isCleanParticlePair(posDaughter, track) && this->isCleanParticlePair(negDaughter, track); if (!isClean) { - this->fillBlocked(pairHistManager.getKinematic()); + this->fillBlocked(pairHistManager.getKinematic(this->mKinematicVariable)); } return isClean; } @@ -365,7 +373,7 @@ class TrackKinkPairCleaner : public BasePairCleaner auto chaDaughter = trackTable.rawIteratorAt(kink.chaDauId() - trackTable.offset()); bool isClean = this->isCleanParticlePair(chaDaughter, track); if (!isClean) { - this->fillBlocked(pairHistManager.getKinematic()); + this->fillBlocked(pairHistManager.getKinematic(this->mKinematicVariable)); } return isClean; } @@ -403,7 +411,7 @@ class TrackCascadePairCleaner : public BasePairCleaner auto negDaughter = trackTable.rawIteratorAt(cascade.negDauId() - trackTable.offset()); bool isClean = this->isCleanParticlePair(bachelor, track) && this->isCleanParticlePair(posDaughter, track) && this->isCleanParticlePair(negDaughter, track); if (!isClean) { - this->fillBlocked(pairHistManager.getKinematic()); + this->fillBlocked(pairHistManager.getKinematic(this->mKinematicVariable)); } return isClean; } @@ -441,7 +449,7 @@ class TrackLcPairCleaner : public BasePairCleaner auto prong2 = trackTable.rawIteratorAt(lc.prong2DauId() - trackTable.offset()); bool isClean = this->isCleanParticlePair(prong0, track) && this->isCleanParticlePair(prong1, track) && this->isCleanParticlePair(prong2, track); if (!isClean) { - this->fillBlocked(pairHistManager.getKinematic()); + this->fillBlocked(pairHistManager.getKinematic(this->mKinematicVariable)); } return isClean; } @@ -476,7 +484,7 @@ class McParticleMcParticlePairCleaner : public BasePairCleaner this->fillAll(); bool isClean = this->isCleanParticlePair(particle1, particle2); if (!isClean) { - this->fillBlocked(pairHistManager.getKinematic()); + this->fillBlocked(pairHistManager.getKinematic(this->mKinematicVariable)); } return isClean; } diff --git a/PWGCF/Femto/Core/pairHistManager.h b/PWGCF/Femto/Core/pairHistManager.h index c2fef5d2659..b6bb3ca1942 100644 --- a/PWGCF/Femto/Core/pairHistManager.h +++ b/PWGCF/Femto/Core/pairHistManager.h @@ -11,11 +11,12 @@ /// \file pairHistManager.h /// \brief histogram manager for pair tasks -/// \author anton.riedel@tum.de, TU München, anton.riedel@tum.de +/// \author Anton Riedel, TU München, anton.riedel@tum.de #ifndef PWGCF_FEMTO_CORE_PAIRHISTMANAGER_H_ #define PWGCF_FEMTO_CORE_PAIRHISTMANAGER_H_ +#include "PWGCF/Femto/Core/femtoMath.h" #include "PWGCF/Femto/Core/femtoSpherHarMath.h" #include "PWGCF/Femto/Core/femtoUtils.h" #include "PWGCF/Femto/Core/histManager.h" @@ -34,7 +35,6 @@ #include #include -#include #include #include #include @@ -105,6 +105,19 @@ enum PairHist { kKstarVsMtVsMinvVsKtVsPt1VsPt2VsMultVsCent, // dalitz plots kDalitz, // between a track and pos/neg daughter of another particle + // angles + kAlpha, // angle between pT1 and pT2 of the paritcles in the lab frame + kAlphaVsKstar, + kAlphaVsKt, + kAlphaVsMt, + kBetaLab, // angle between kT and kstar in lab frame + kBetaLabVsKstar, + kBetaLabVsKt, + kBetaLabVsMt, + kKstarVsAlphaVsMtVsMult, + kKstarVsAlphaVsMtVsPt1VsPt2VsMult, + kKstarVsBetaLabVsMtVsMult, + kKstarVsBetaLabVsMtVsPt1VsPt2VsMult, // reco-vs-mc-truth correlation (requires BOTH a reco pair and matched mc info) kTrueKstarVsKstar, kTrueKtVsKt, @@ -180,6 +193,7 @@ struct ConfMixing : o2::framework::ConfigurableGroup { o2::framework::ConfigurableAxis eventPlaneAngle{"eventPlaneAngle", {10, 0.f, 1.f * o2::constants::math::TwoPI}, "Mixing bins - event plane angle"}; o2::framework::Configurable depth{"depth", 5, "Number of events for mixing"}; o2::framework::Configurable policy{"policy", 0, "Binning policy for mixing (alywas in combination with z-vertex) -> 0: multiplicity, -> 1: centrality, -> 2: multiplicity and centrality, -> 3: centrality and event plane angle"}; + o2::framework::Configurable eventStride{"eventStride", 1, "Only use every Nth collision as a mixing anchor (1 = use every collision). Reduces mixed-event combinatorics roughly 1/eventStride."}; o2::framework::Configurable sameSpecies{"sameSpecies", false, "Enable if particle 1 and particle 2 are the same"}; o2::framework::Configurable seed{"seed", -1, "Seed to randomize particle 1 and particle 2 (if they are identical). Set to negative value to deactivate. Set to 0 to generate unique seed in time."}; o2::framework::Configurable enablePairCorrelationQa{"enablePairCorrelationQa", true, "Enable pair-level correlation QA (same-event + mixed-event)"}; @@ -213,6 +227,14 @@ struct ConfPairBinning : o2::framework::ConfigurableGroup { o2::framework::Configurable plotKstarVsMtVsMinvVsKtVsPt1VsPt2VsMultVsCent{"plotKstarVsMtVsMinvVsKtVsPt1VsPt2VsMultVsCent", false, "(Reco) Enable 8D histogram (Kstar Vs Mt Vs Minv Vs Kt Vs Pt1 Vs Pt2 Vs Mult Vs Cent)"}; o2::framework::Configurable plotDalitz{"plotDalitz", false, "(Reco) Enable dalitz plot. Not supported for pure mc-truth pairs (no trackTable/daughter structure)"}; o2::framework::Configurable plotDeltaEtaDeltaPhi{"plotDeltaEtaDeltaPhi", false, "(Reco/Mc) Plot #Delta#phi vs #Delta#eta"}; + o2::framework::Configurable plotAlpha{"plotAlpha", false, "(Reco/Mc) Plot alpha (angle between pT1 and pT2) 1D distribution, and (if plot2D) vs kstar/kT/mT"}; + o2::framework::ConfigurableAxis alpha{"alpha", {{180, -o2::constants::math::PI, o2::constants::math::PI}}, "alpha (angle between pT1 and pT2)"}; + o2::framework::Configurable plotKstarVsAlphaVsMtVsMult{"plotKstarVsAlphaVsMtVsMult", false, "(Reco/Mc) Enable 4D histogram (Kstar Vs Alpha Vs Mt Vs Mult)"}; + o2::framework::Configurable plotKstarVsAlphaVsMtVsPt1VsPt2VsMult{"plotKstarVsAlphaVsMtVsPt1VsPt2VsMult", false, "(Reco/Mc) Enable 6D histogram (Kstar Vs Alpha Vs Mt Vs Pt1 Vs Pt2 Vs Mult)"}; + o2::framework::Configurable plotBetaLab{"plotBetaLab", false, "(Reco/Mc) Plot betaLab (angle between kT and k* in lab frame) 1D distribution, and (if plot2D) vs kstar/kT/mT"}; + o2::framework::ConfigurableAxis betaLab{"betaLab", {{90, 0.f, o2::constants::math::PI}}, "betaLab (angle between kT and k* in lab frame)"}; + o2::framework::Configurable plotKstarVsBetaLabVsMtVsMult{"plotKstarVsBetaLabVsMtVsMult", false, "(Reco/Mc) Enable 4D histogram (Kstar Vs BetaLab Vs Mt Vs Mult)"}; + o2::framework::Configurable plotKstarVsBetaLabVsMtVsPt1VsPt2VsMult{"plotKstarVsBetaLabVsMtVsPt1VsPt2VsMult", false, "(Reco/Mc) Enable 6D histogram (Kstar Vs BetaLab Vs Mt Vs Pt1 Vs Pt2 Vs Mult)"}; o2::framework::ConfigurableAxis kstar{"kstar", {{600, 0, 6}}, "kstar"}; o2::framework::ConfigurableAxis kt{"kt", {{600, 0, 6}}, "kt"}; o2::framework::ConfigurableAxis mt{"mt", {{500, 0.8, 5.8}}, "mt"}; @@ -227,7 +249,6 @@ struct ConfPairBinning : o2::framework::ConfigurableGroup { o2::framework::ConfigurableAxis dalitzM12{"dalitzM12", {{100, 0, 10}}, "Mass12 binning of darlitz plot"}; o2::framework::ConfigurableAxis dalitzM13{"dalitzM13", {{100, 0, 10}}, "Mass13 binning of darlitz plot"}; o2::framework::Configurable transverseMassType{"transverseMassType", static_cast(modes::TransverseMassType::kAveragePdgMass), "Type of transverse mass (0-> Average Pdg Mass, 1-> Reduced Pdg Mass, 2-> Mt from combined 4 vector)"}; - o2::framework::Configurable kinematicVariable{"kinematicVariable", static_cast(modes::KinematicVariable::kKstar), "Type of kinematic variable (0->kstar, 1->kT, 2->mT) for PC and CPR plots"}; o2::framework::ConfigurableAxis binningDeltaEta{"binningDeltaEta", {{35, -1.6, 1.6}}, "Delta eta"}; o2::framework::ConfigurableAxis binningDeltaPhi{"binningDeltaPhi", {{35, -o2::constants::math::PIHalf, 3 * o2::constants::math::PIHalf}}, "Delta phi"}; o2::framework::Configurable plotBertschPratt{"plotBertschPratt", false, "(Reco/Mc) Enable 1D projections and 3D (q_out, q_side, q_long) Bertsch-Pratt histograms in LCMS"}; @@ -257,9 +278,10 @@ struct ConfPairCuts : o2::framework::ConfigurableGroup { o2::framework::Configurable mtMin{"mtMin", -1, "Minimal mt (set to -1 to deactivate)"}; o2::framework::Configurable massInvMin{"massInvMin", -1, "Minimal invariant mass (set to -1 to deactivate)"}; o2::framework::Configurable massInvMax{"massInvMax", -1, "Maximal invariant mass (set to -1 to deactivate)"}; - o2::framework::Configurable mixOnlyCommonAncestor{"mixOnlyCommonAncestor", false, "Require pair to have common anchestor (in the same event)"}; - o2::framework::Configurable mixOnlyNonCommonAncestor{"mixOnlyNonCommonAncestor", false, "Require pair to have non-common anchestor (in the same event)"}; + o2::framework::Configurable mixOnlyCommonAncestor{"mixOnlyCommonAncestor", false, "Require pair to have common ancestor (in the same event)"}; + o2::framework::Configurable mixOnlyNonCommonAncestor{"mixOnlyNonCommonAncestor", false, "Require pair to have non-common ancestor (in the same event)"}; o2::framework::Configurable useMotherAsAncestor{"useMotherAsAncestor", false, "Use the first ancestor (i.e. the direct mother) instead of the partonic mother when requiring (non-)common ancestry"}; + o2::framework::Configurable kinematicVariable{"kinematicVariable", static_cast(modes::KinematicVariable::kKstar), "Type of kinematic variable (0->kstar, 1->kT, 2->mT) reported by the pair-cleaner kinematic plot"}; }; // the enum gives the correct index in the array @@ -348,13 +370,26 @@ constexpr std::array, kPairHistogramLast> {kTrueKstarVsMtVsPt1VsPt2VsMult, o2::framework::HistType::kTHnSparseF, "hTrueKstarVsMtVsPt1VsPt2VsMult", "k* vs m_{T} vs p_{T,1} vs p_{T,2} vs multiplicity (mc-truth pair); k*_{True} (GeV/#it{c}); m_{T,True} (GeV/#it{c}^{2}); p_{T,1,True} (GeV/#it{c}); p_{T,2,True} (GeV/#it{c}); Multiplicity_{True};"}, {kTrueKstarVsMtVsPt1VsPt2VsMultVsCent, o2::framework::HistType::kTHnSparseF, "hTrueKstarVsMtVsPt1VsPt2VsMultVsCent", "k* vs m_{T} vs p_{T,1} vs p_{T,2} vs multiplicity vs centrality (mc-truth pair); k*_{True} (GeV/#it{c}); m_{T,True} (GeV/#it{c}^{2}); p_{T,1,True} (GeV/#it{c}); p_{T,2,True} (GeV/#it{c}); Multiplicity_{True}; Centrality_{True} (%);"}, // mixing qa - {kSeNpart1VsNpart2, o2::framework::HistType::kTH2F, "hSeNpart1VsNpart2", "# unique particle 1 vs # unique particle 2 in each same event; # partilce 1; # particle 2"}, - {kMeMixingWindowRaw, o2::framework::HistType::kTH1F, "hMeMixingWindowRaw", "Raw Mixing Window; Raw Mixing Window Entries"}, + {kSeNpart1VsNpart2, o2::framework::HistType::kTH2F, "hSeNpart1VsNpart2", "# unique particle 1 vs # unique particle 2 in each same event; # particle 1; # particle 2"}, + {kMeMixingWindowRaw, o2::framework::HistType::kTH1F, "hMeMixingWindowRaw", "Raw Mixing Window; Raw Mixing Window; Entries"}, {kMeMixingWindowEffective, o2::framework::HistType::kTH1F, "hMeMixingWindowEffective", "Effective Mixing Window; Effective Mixing Window; Entries"}, - {kMeNpart1VsNpart2, o2::framework::HistType::kTH2F, "hMeNpart1VsNpart2", "# unique particle 1 vs # unique partilce 2 in each mixing bin; # partilce 1; # particle 2"}, + {kMeNpart1VsNpart2, o2::framework::HistType::kTH2F, "hMeNpart1VsNpart2", "# unique particle 1 vs # unique particle 2 in each mixing bin; # particle 1; # particle 2"}, {kMeVtz1VsMult1VsCent1VsVtz2VsMult2VsCent2, o2::framework::HistType::kTHnSparseF, "hVtz1VsMult1VsCent1VsVtz2VsMult2VsCent2", "Mixing bins; V_{z,1} (cm); multiplicity_{1}; centrality_{1} (%); V_{z,2} (cm); multiplicity_{2}; centrality_{2} (%)"}, // angular {kDeltaEtaDeltaPhi, o2::framework::HistType::kTH2F, "hDeltaEtaDeltaPhi", "#Delta#phi vs #Delta#eta; #Delta#phi; #Delta#eta"}, + // angles + {kAlpha, o2::framework::HistType::kTH1F, "hAlpha", "angle between p_{T,1} and p_{T,2}; #alpha; Entries"}, + {kAlphaVsKstar, o2::framework::HistType::kTH2F, "hAlphaVsKstar", "#alpha vs k*; #alpha; k* (GeV/#it{c})"}, + {kAlphaVsKt, o2::framework::HistType::kTH2F, "hAlphaVsKt", "#alpha vs k_{T}; #alpha; k_{T} (GeV/#it{c})"}, + {kAlphaVsMt, o2::framework::HistType::kTH2F, "hAlphaVsMt", "#alpha vs m_{T}; #alpha; m_{T} (GeV/#it{c}^{2})"}, + {kBetaLab, o2::framework::HistType::kTH1F, "hBetaLab", "angle between k_{T} and k* (lab frame); #beta_{lab}; Entries"}, + {kBetaLabVsKstar, o2::framework::HistType::kTH2F, "hBetaLabVsKstar", "#beta_{lab} vs k*; #beta_{lab}; k* (GeV/#it{c})"}, + {kBetaLabVsKt, o2::framework::HistType::kTH2F, "hBetaLabVsKt", "#beta_{lab} vs k_{T}; #beta_{lab}; k_{T} (GeV/#it{c})"}, + {kBetaLabVsMt, o2::framework::HistType::kTH2F, "hBetaLabVsMt", "#beta_{lab} vs m_{T}; #beta_{lab}; m_{T} (GeV/#it{c}^{2})"}, + {kKstarVsAlphaVsMtVsMult, o2::framework::HistType::kTHnSparseF, "hKstarVsAlphaVsMtVsMult", "k* vs #alpha vs m_{T} vs multiplicity; k* (GeV/#it{c}); #alpha; m_{T} (GeV/#it{c}^{2}); Multiplicity;"}, + {kKstarVsAlphaVsMtVsPt1VsPt2VsMult, o2::framework::HistType::kTHnSparseF, "hKstarVsAlphaVsMtVsPt1VsPt2VsMult", "k* vs #alpha vs m_{T} vs p_{T,1} vs p_{T,2} vs multiplicity; k* (GeV/#it{c}); #alpha; m_{T} (GeV/#it{c}^{2}); p_{T,1} (GeV/#it{c}); p_{T,2} (GeV/#it{c}); Multiplicity;"}, + {kKstarVsBetaLabVsMtVsMult, o2::framework::HistType::kTHnSparseF, "hKstarVsBetaLabVsMtVsMult", "k* vs #beta_{lab} vs m_{T} vs multiplicity; k* (GeV/#it{c}); #beta_{lab}; m_{T} (GeV/#it{c}^{2}); Multiplicity;"}, + {kKstarVsBetaLabVsMtVsPt1VsPt2VsMult, o2::framework::HistType::kTHnSparseF, "hKstarVsBetaLabVsMtVsPt1VsPt2VsMult", "k* vs #beta_{lab} vs m_{T} vs p_{T,1} vs p_{T,2} vs multiplicity; k* (GeV/#it{c}); #beta_{lab}; m_{T} (GeV/#it{c}^{2}); p_{T,1} (GeV/#it{c}); p_{T,2} (GeV/#it{c}); Multiplicity;"}, // Bertsch-Pratt 3D decomposition in LCMS {kQout, o2::framework::HistType::kTH1F, "hQout", "q_{out} in LCMS; q_{out} (GeV/#it{c}); Entries"}, {kQside, o2::framework::HistType::kTH1F, "hQside", "q_{side} in LCMS; q_{side} (GeV/#it{c}); Entries"}, @@ -410,6 +445,18 @@ constexpr std::array, kPairHistogramLast> {kKstarVsMtVsMinvVsKtVsPt1VsPt2VsMultVsCent, {(confAnalysis).kstar, (confAnalysis).mt, (confAnalysis).massInv, (confAnalysis).kt, (confAnalysis).pt1, (confAnalysis).pt2, (confAnalysis).multiplicity, (confAnalysis).centrality}}, \ {kDalitz, {(confAnalysis).kstar, (confAnalysis).dalitzMtot, (confAnalysis).dalitzM12, (confAnalysis).dalitzM13}}, \ {kDeltaEtaDeltaPhi, {(confAnalysis).binningDeltaPhi, (confAnalysis).binningDeltaEta}}, \ + {kAlpha, {(confAnalysis).alpha}}, \ + {kAlphaVsKstar, {(confAnalysis).alpha, (confAnalysis).kstar}}, \ + {kAlphaVsKt, {(confAnalysis).alpha, (confAnalysis).kt}}, \ + {kAlphaVsMt, {(confAnalysis).alpha, (confAnalysis).mt}}, \ + {kBetaLab, {(confAnalysis).betaLab}}, \ + {kBetaLabVsKstar, {(confAnalysis).betaLab, (confAnalysis).kstar}}, \ + {kBetaLabVsKt, {(confAnalysis).betaLab, (confAnalysis).kt}}, \ + {kBetaLabVsMt, {(confAnalysis).betaLab, (confAnalysis).mt}}, \ + {kKstarVsAlphaVsMtVsMult, {(confAnalysis).kstar, (confAnalysis).alpha, (confAnalysis).mt, (confAnalysis).multiplicity}}, \ + {kKstarVsAlphaVsMtVsPt1VsPt2VsMult, {(confAnalysis).kstar, (confAnalysis).alpha, (confAnalysis).mt, (confAnalysis).pt1, (confAnalysis).pt2, (confAnalysis).multiplicity}}, \ + {kKstarVsBetaLabVsMtVsMult, {(confAnalysis).kstar, (confAnalysis).betaLab, (confAnalysis).mt, (confAnalysis).multiplicity}}, \ + {kKstarVsBetaLabVsMtVsPt1VsPt2VsMult, {(confAnalysis).kstar, (confAnalysis).betaLab, (confAnalysis).mt, (confAnalysis).pt1, (confAnalysis).pt2, (confAnalysis).multiplicity}}, \ {kQout, {(confAnalysis).qout}}, \ {kQside, {(confAnalysis).qside}}, \ {kQlong, {(confAnalysis).qlong}}, \ @@ -522,6 +569,11 @@ constexpr char PrefixV0V0Me[] = "V0V0/ME/"; constexpr char PrefixTrackResonanceSe[] = "TrackResonance/SE/"; constexpr char PrefixTrackResonanceMe[] = "TrackResonance/ME/"; +constexpr char PrefixTrackPi0Se[] = "TrackPi0/SE/"; +constexpr char PrefixTrackPi0Me[] = "TrackPi0/ME/"; +constexpr char PrefixTrackEtaSe[] = "TrackEta/SE/"; +constexpr char PrefixTrackEtaMe[] = "TrackEta/ME/"; + constexpr char PrefixV0ResonanceSe[] = "V0Resonance/SE/"; constexpr char PrefixV0ResonanceMe[] = "V0Resonance/ME/"; @@ -588,6 +640,12 @@ class PairHistManager mPlotDalitz = ConfPairBinning.plotDalitz.value; mPlotDeltaEtaDeltaPhi = ConfPairBinning.plotDeltaEtaDeltaPhi.value; + mPlotAlpha = ConfPairBinning.plotAlpha.value; + mPlotKstarVsAlphaVsMtVsMult = ConfPairBinning.plotKstarVsAlphaVsMtVsMult.value; + mPlotKstarVsAlphaVsMtVsPt1VsPt2VsMult = ConfPairBinning.plotKstarVsAlphaVsMtVsPt1VsPt2VsMult.value; + mPlotBetaLab = ConfPairBinning.plotBetaLab.value; + mPlotKstarVsBetaLabVsMtVsMult = ConfPairBinning.plotKstarVsBetaLabVsMtVsMult.value; + mPlotKstarVsBetaLabVsMtVsPt1VsPt2VsMult = ConfPairBinning.plotKstarVsBetaLabVsMtVsPt1VsPt2VsMult.value; mPlotBertschPratt = ConfPairBinning.plotBertschPratt.value; mPlotEventShape = ConfPairBinning.plotEventShape.value; @@ -609,9 +667,6 @@ class PairHistManager // transverse mass type mMtType = static_cast(ConfPairBinning.transverseMassType.value); - // kinematic variable - mKinematicVariable = static_cast(ConfPairBinning.kinematicVariable.value); - // values for cuts mKstarMin = ConfPairCuts.kstarMin.value; mKstarMax = ConfPairCuts.kstarMax.value; @@ -702,14 +757,14 @@ class PairHistManager mTrueParticle2 = ROOT::Math::PtEtaPhiMVector(mcParticle2.pt(), mcParticle2.eta(), mcParticle2.phi(), mPdgMass2); // compute true kinematics - mTrueKt = getKt(mTrueParticle1, mTrueParticle2); - mTrueMt = getMt(mTrueParticle1, mTrueParticle2); - mTrueMinv = getMinv(mTrueParticle1, mTrueParticle2); - mTrueKstar = getKstar(mTrueParticle1, mTrueParticle2); + mTrueKt = femtomath::getKt(mTrueParticle1, mTrueParticle2); + mTrueMt = femtomath::getMt(mTrueParticle1, mTrueParticle2, mMtType); + mTrueMinv = femtomath::getMinv(mTrueParticle1, mTrueParticle2); + mTrueKstar = femtomath::getKstar(mTrueParticle1, mTrueParticle2); if (mPlotBertschPratt) { std::tie(mTrueQout, mTrueQside, mTrueQlong) = - computeBertschPrattLCMS(mTrueParticle1, mTrueParticle2); + femtomath::computeBertschPrattLCMS(mTrueParticle1, mTrueParticle2); } } @@ -755,10 +810,10 @@ class PairHistManager mTrueParticle1 = ROOT::Math::PtEtaPhiMVector(particle1.pt(), particle1.eta(), particle1.phi(), mPdgMass1); mTrueParticle2 = ROOT::Math::PtEtaPhiMVector(particle2.pt(), particle2.eta(), particle2.phi(), mPdgMass2); - mTrueKt = getKt(mTrueParticle1, mTrueParticle2); - mTrueMt = getMt(mTrueParticle1, mTrueParticle2); - mTrueMinv = getMinv(mTrueParticle1, mTrueParticle2); - mTrueKstar = getKstar(mTrueParticle1, mTrueParticle2); + mTrueKt = femtomath::getKt(mTrueParticle1, mTrueParticle2); + mTrueMt = femtomath::getMt(mTrueParticle1, mTrueParticle2, mMtType); + mTrueMinv = femtomath::getMinv(mTrueParticle1, mTrueParticle2); + mTrueKstar = femtomath::getKstar(mTrueParticle1, mTrueParticle2); // in the pure mc-truth path there is no reco counterpart, so the generated values are also // stored in the reco members; they are used by getKinematic(), i.e. by the kinematic histograms @@ -769,7 +824,7 @@ class PairHistManager mKstar = mTrueKstar; if (mPlotBertschPratt) { - std::tie(mTrueQout, mTrueQside, mTrueQlong) = computeBertschPrattLCMS(mTrueParticle1, mTrueParticle2); + std::tie(mTrueQout, mTrueQside, mTrueQlong) = femtomath::computeBertschPrattLCMS(mTrueParticle1, mTrueParticle2); } if (mPlotDeltaEtaDeltaPhi) { mTrueDeltaEta = particle1.eta() - particle2.eta(); @@ -885,9 +940,11 @@ class PairHistManager } } - float getKinematic() const + // kinematicVariable is supplied by the caller (CPR/pair cleaner), each of which carries its own + // Configurable for it -- this manager has no opinion of its own on which variable to report + float getKinematic(modes::KinematicVariable kinematicVariable) const { - switch (mKinematicVariable) { + switch (kinematicVariable) { case modes::KinematicVariable::kKstar: return mKstar; break; @@ -1004,6 +1061,34 @@ class PairHistManager if (mPlotDeltaEtaDeltaPhi) { mHistogramRegistry->add(analysisDir + getHistNameV2(kDeltaEtaDeltaPhi, HistTable), getHistDesc(kDeltaEtaDeltaPhi, HistTable), getHistType(kDeltaEtaDeltaPhi, HistTable), {Specs.at(kDeltaEtaDeltaPhi)}); } + if (mPlotAlpha) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kAlpha, HistTable), getHistDesc(kAlpha, HistTable), getHistType(kAlpha, HistTable), {Specs.at(kAlpha)}); + if (mPlot2d) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kAlphaVsKstar, HistTable), getHistDesc(kAlphaVsKstar, HistTable), getHistType(kAlphaVsKstar, HistTable), {Specs.at(kAlphaVsKstar)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kAlphaVsKt, HistTable), getHistDesc(kAlphaVsKt, HistTable), getHistType(kAlphaVsKt, HistTable), {Specs.at(kAlphaVsKt)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kAlphaVsMt, HistTable), getHistDesc(kAlphaVsMt, HistTable), getHistType(kAlphaVsMt, HistTable), {Specs.at(kAlphaVsMt)}); + } + } + if (mPlotKstarVsAlphaVsMtVsMult) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kKstarVsAlphaVsMtVsMult, HistTable), getHistDesc(kKstarVsAlphaVsMtVsMult, HistTable), getHistType(kKstarVsAlphaVsMtVsMult, HistTable), {Specs.at(kKstarVsAlphaVsMtVsMult)}); + } + if (mPlotKstarVsAlphaVsMtVsPt1VsPt2VsMult) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kKstarVsAlphaVsMtVsPt1VsPt2VsMult, HistTable), getHistDesc(kKstarVsAlphaVsMtVsPt1VsPt2VsMult, HistTable), getHistType(kKstarVsAlphaVsMtVsPt1VsPt2VsMult, HistTable), {Specs.at(kKstarVsAlphaVsMtVsPt1VsPt2VsMult)}); + } + if (mPlotBetaLab) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kBetaLab, HistTable), getHistDesc(kBetaLab, HistTable), getHistType(kBetaLab, HistTable), {Specs.at(kBetaLab)}); + if (mPlot2d) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kBetaLabVsKstar, HistTable), getHistDesc(kBetaLabVsKstar, HistTable), getHistType(kBetaLabVsKstar, HistTable), {Specs.at(kBetaLabVsKstar)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kBetaLabVsKt, HistTable), getHistDesc(kBetaLabVsKt, HistTable), getHistType(kBetaLabVsKt, HistTable), {Specs.at(kBetaLabVsKt)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kBetaLabVsMt, HistTable), getHistDesc(kBetaLabVsMt, HistTable), getHistType(kBetaLabVsMt, HistTable), {Specs.at(kBetaLabVsMt)}); + } + } + if (mPlotKstarVsBetaLabVsMtVsMult) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kKstarVsBetaLabVsMtVsMult, HistTable), getHistDesc(kKstarVsBetaLabVsMtVsMult, HistTable), getHistType(kKstarVsBetaLabVsMtVsMult, HistTable), {Specs.at(kKstarVsBetaLabVsMtVsMult)}); + } + if (mPlotKstarVsBetaLabVsMtVsPt1VsPt2VsMult) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kKstarVsBetaLabVsMtVsPt1VsPt2VsMult, HistTable), getHistDesc(kKstarVsBetaLabVsMtVsPt1VsPt2VsMult, HistTable), getHistType(kKstarVsBetaLabVsMtVsPt1VsPt2VsMult, HistTable), {Specs.at(kKstarVsBetaLabVsMtVsPt1VsPt2VsMult)}); + } if (mPlotBertschPratt) { mHistogramRegistry->add(analysisDir + getHistNameV2(kQout, HistTable), getHistDesc(kQout, HistTable), getHistType(kQout, HistTable), {Specs.at(kQout)}); mHistogramRegistry->add(analysisDir + getHistNameV2(kQside, HistTable), getHistDesc(kQside, HistTable), getHistType(kQside, HistTable), {Specs.at(kQside)}); @@ -1226,27 +1311,27 @@ class PairHistManager mParticle2 = ROOT::Math::PtEtaPhiMVector(mAbsCharge2 * particle2.pt(), particle2.eta(), particle2.phi(), mass2); // set kT - mKt = getKt(mParticle1, mParticle2); + mKt = femtomath::getKt(mParticle1, mParticle2); // set mT - mMt = getMt(mParticle1, mParticle2); + mMt = femtomath::getMt(mParticle1, mParticle2, mMtType); // set Minv - mMassInv = getMinv(mParticle1, mParticle2); + mMassInv = femtomath::getMinv(mParticle1, mParticle2); // set kstar - mKstar = getKstar(mParticle1, mParticle2); + mKstar = femtomath::getKstar(mParticle1, mParticle2); if (mPlotBertschPratt) { - std::tie(mQout, mQside, mQlong) = computeBertschPrattLCMS(mParticle1, mParticle2); + std::tie(mQout, mQside, mQlong) = femtomath::computeBertschPrattLCMS(mParticle1, mParticle2); } if (mPlotEventShape) { if (!mPlotBertschPratt) { - std::tie(mQout, mQside, mQlong) = computeBertschPrattLCMS(mParticle1, mParticle2); + std::tie(mQout, mQside, mQlong) = femtomath::computeBertschPrattLCMS(mParticle1, mParticle2); } if constexpr (utils::HasEventShape) { - mPairPhiFromEventPlaneAngle = getPairPhiFromEventPlaneAngle(mParticle1, mParticle2, col); + mPairPhiFromEventPlaneAngle = femtomath::getPairPhiFromEventPlaneAngle(mParticle1, mParticle2, col); mQvector = col.qvec(); } else { LOG(fatal) << "plotEventShape is enabled but the collision table has no event-shape columns. Breaking..."; @@ -1254,7 +1339,7 @@ class PairHistManager } if (mPlotSH) { - std::tie(mShKv, mShOut, mShSide, mShLong) = computeShKinematics(mParticle1, mParticle2); + std::tie(mShKv, mShOut, mShSide, mShLong) = femtomath::computeShKinematics(mParticle1, mParticle2, mShFrame); } if (mPlotDeltaEtaDeltaPhi) { @@ -1262,6 +1347,13 @@ class PairHistManager mDeltaPhi = RecoDecay::constrainAngle(particle1.phi() - particle2.phi(), -o2::constants::math::PIHalf); } + if (mPlotAlpha || mPlotKstarVsAlphaVsMtVsMult || mPlotKstarVsAlphaVsMtVsPt1VsPt2VsMult) { + mAlpha = femtomath::getAlpha(mParticle1, mParticle2); + } + if (mPlotBetaLab || mPlotKstarVsBetaLabVsMtVsMult || mPlotKstarVsBetaLabVsMtVsPt1VsPt2VsMult) { + mBetaLab = femtomath::getBetaLab(mParticle1, mParticle2); + } + if (mPlotDalitz) { if constexpr (modes::isEqual(particleType1, modes::Particle::kTrack) && (modes::isEqual(particleType2, modes::Particle::kV0) || modes::isEqual(particleType2, modes::Particle::kTwoTrackResonance) || modes::isEqual(particleType2, modes::Particle::kCharmHadron)) && requires(T2 p) { p.posDauId(); p.negDauId(); }) { @@ -1372,6 +1464,34 @@ class PairHistManager if (mPlotDeltaEtaDeltaPhi) { mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kDeltaEtaDeltaPhi, HistTable)), mDeltaPhi, mDeltaEta); } + if (mPlotAlpha) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kAlpha, HistTable)), mAlpha); + if (mPlot2d) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kAlphaVsKstar, HistTable)), mAlpha, mKstar); + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kAlphaVsKt, HistTable)), mAlpha, mKt); + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kAlphaVsMt, HistTable)), mAlpha, mMt); + } + } + if (mPlotKstarVsAlphaVsMtVsMult) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstarVsAlphaVsMtVsMult, HistTable)), mKstar, mAlpha, mMt, mMult); + } + if (mPlotKstarVsAlphaVsMtVsPt1VsPt2VsMult) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstarVsAlphaVsMtVsPt1VsPt2VsMult, HistTable)), mKstar, mAlpha, mMt, mParticle1.Pt(), mParticle2.Pt(), mMult); + } + if (mPlotBetaLab) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kBetaLab, HistTable)), mBetaLab); + if (mPlot2d) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kBetaLabVsKstar, HistTable)), mBetaLab, mKstar); + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kBetaLabVsKt, HistTable)), mBetaLab, mKt); + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kBetaLabVsMt, HistTable)), mBetaLab, mMt); + } + } + if (mPlotKstarVsBetaLabVsMtVsMult) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstarVsBetaLabVsMtVsMult, HistTable)), mKstar, mBetaLab, mMt, mMult); + } + if (mPlotKstarVsBetaLabVsMtVsPt1VsPt2VsMult) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstarVsBetaLabVsMtVsPt1VsPt2VsMult, HistTable)), mKstar, mBetaLab, mMt, mParticle1.Pt(), mParticle2.Pt(), mMult); + } if (mPlotBertschPratt) { mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kQout, HistTable)), mQout); mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kQside, HistTable)), mQside); @@ -1383,8 +1503,8 @@ class PairHistManager } if (mPlotSH) { const float shCentValue = mShUseCent ? mCent : mMult; - const int iCent = findShBin(shCentValue, mShCentEdges); - const int iKt = findShBin(mKt, mShKtEdges); + const int iCent = femtomath::findShBin(shCentValue, mShCentEdges); + const int iKt = femtomath::findShBin(mKt, mShKtEdges); if (iCent >= 0 && iKt >= 0) { mYlm.doYlmUpToL(mShLMax, mShOut, mShSide, mShLong, mShYlmBuffer.data()); for (std::size_t i = 0; i < mShYlmBuffer.size(); ++i) { @@ -1406,7 +1526,7 @@ class PairHistManager if (mShPlot1D) { mShBinCount[iCent][iKt]->Fill(mShKv, 1.0); // FemtoUniverse h1D = f3d[0]: qinv (=2k*) for identical-LCMS, else k*. - const float sh1DValue = (mShFrame == ShFrameLcmsIdentical) ? (2.0f * mKstar) : mKstar; + const float sh1DValue = (mShFrame == femtomath::ShFrameLcmsIdentical) ? (2.0f * mKstar) : mKstar; mSh1D[iCent][iKt]->Fill(sh1DValue); } } @@ -1483,196 +1603,6 @@ class PairHistManager } } - float getKt(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) - { - auto sum = (part1 + part2); - double kt = 0.5 * sum.Pt(); - return static_cast(kt); - } - - float getMt(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) - { - auto sum = part1 + part2; - double mt = 0; - double averageMass = 0; - double reducedMass = 0; - switch (mMtType) { - case modes::TransverseMassType::kAveragePdgMass: - averageMass = 0.5 * (part1.M() + part2.M()); - mt = std::hypot(0.5 * sum.Pt(), averageMass); - break; - case modes::TransverseMassType::kReducedPdgMass: - reducedMass = 2. * (part1.M() * part2.M()) / (part1.M() + part2.M()); - mt = std::hypot(0.5 * sum.Pt(), reducedMass); - break; - case modes::TransverseMassType::kMt4Vector: - mt = 0.5 * sum.Mt(); - break; - default: - LOG(fatal) << "Invalid transverse mass type, breaking..."; - } - return static_cast(mt); - } - - float getMinv(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) - { - return static_cast((part1 + part2).M()); - } - - float getKstar(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) - { - // Use Cartesian 4-vectors: addition/M2() become pure arithmetic - const ROOT::Math::PxPyPzEVector p1(part1); - const ROOT::Math::PxPyPzEVector p2(part2); - - // Mandelstam s = (p1 + p2)^2 - const double s = (p1 + p2).M2(); - const double m1sq = p1.M2(); - const double m2sq = p2.M2(); - - // Källen function λ(s, m1^2, m2^2) = (s - m1^2 - m2^2)² - 4*m1^2*m2^2 - const double kallen = (s - m1sq - m2sq) * (s - m1sq - m2sq) - 4.0 * m1sq * m2sq; - - // k* = 0.5 * sqrt(λ/s) - return static_cast(0.5 * std::sqrt(std::max(0.0, kallen) / s)); - } - - std::tuple computeBertschPrattLCMS(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) - { - const ROOT::Math::PxPyPzEVector p1(part1); - const ROOT::Math::PxPyPzEVector p2(part2); - const ROOT::Math::PxPyPzEVector pSum = p1 + p2; - - const double tPx = pSum.Px(); - const double tPy = pSum.Py(); - const double tPz = pSum.Pz(); - const double tE = pSum.E(); - - const double tPt = std::sqrt(tPx * tPx + tPy * tPy); - const double tMt = std::sqrt(tE * tE - tPz * tPz); - - static constexpr double MinTransverseMomentum = 1e-9; - if (tPt < MinTransverseMomentum || tMt < MinTransverseMomentum) { - return {0.0f, 0.0f, 0.0f}; - } - - const double betaL = tPz / tE; - const double gammaL = tE / tMt; - - const double kout1 = (p1.Px() * tPx + p1.Py() * tPy) / tPt; - const double kside1 = (-p1.Px() * tPy + p1.Py() * tPx) / tPt; - const double klong1 = gammaL * (p1.Pz() - betaL * p1.E()); - - const double kout2 = (p2.Px() * tPx + p2.Py() * tPy) / tPt; - const double kside2 = (p2.Py() * tPx - p2.Px() * tPy) / tPt; - const double klong2 = gammaL * (p2.Pz() - betaL * p2.E()); - - auto qOut = static_cast(kout1 - kout2); - auto qSide = static_cast(kside1 - kside2); - auto qLong = static_cast(klong1 - klong2); - - return {qOut, qSide, qLong}; - } - - // Return the bin index for value given ascending bin edges, or -1 if out of range. - // edges = {e0, e1, ..., eN} defines N bins [e0,e1), [e1,e2), ..., [e_{N-1},eN). - static int findShBin(double value, std::vector const& edges) - { - static constexpr std::size_t MinEdgesForOneBin = 2; - if (edges.size() < MinEdgesForOneBin || value < edges.front() || value >= edges.back()) { - return -1; - } - for (std::size_t i = 0; i < edges.size() - 1; ++i) { - if (value >= edges[i] && value < edges[i + 1]) { - return static_cast(i); - } - } - return -1; - } - - // Kinematics feeding the spherical-harmonics decomposition, ported 1:1 from - // FemtoUniverseMath::newpairfunc so results are numerically comparable. - // Returns {kv, out, side, long}, selected by mShFrame: - // ShFrameLcmsNonIdentical: {kstar, fDKOut, fDKSide, fDKLong} (LCMS components of particle 1) - // ShFrameLcmsIdentical: {qinv, outLCMS, sideLCMS, longLCMS} (LCMS q-differences) - // ShFramePrf: {|qPRF|, outPRF, sidePRF, longPRF} (matches FemtoUniverse isIdenPRF=true) - std::tuple computeShKinematics(ROOT::Math::PtEtaPhiMVector const& part1, - ROOT::Math::PtEtaPhiMVector const& part2) - { - const ROOT::Math::PxPyPzEVector p1(part1); - const ROOT::Math::PxPyPzEVector p2(part2); - const ROOT::Math::PxPyPzEVector sum = p1 + p2; - - const double tPx = sum.Px(); - const double tPy = sum.Py(); - const double tPz = sum.Pz(); - const double tE = sum.E(); - const double tPtSq = tPx * tPx + tPy * tPy; - const double tMtSq = tE * tE - tPz * tPz; - - static constexpr double MinTransverseMomentum = 1e-9; - if (tPtSq < MinTransverseMomentum || tMtSq < MinTransverseMomentum) { - return {0.f, 0.f, 0.f, 0.f}; - } - const double tPt = std::sqrt(tPtSq); - const double tMt = std::sqrt(tMtSq); - const double tM = std::sqrt(std::max(0.0, tMtSq - tPtSq)); - - const double beta = tPz / tE; - const double gamma = tE / tMt; - - // LCMS components of particle 1 (fDKOut/Side/Long in newpairfunc) - const double e1 = p1.E(); - const double fDKOut = (p1.Px() * tPx + p1.Py() * tPy) / tPt; - const double fDKSide = (-p1.Px() * tPy + p1.Py() * tPx) / tPt; - const double fDKLong = gamma * (p1.Pz() - beta * e1); - const double fDE = gamma * (e1 - beta * p1.Pz()); - - // LCMS components of particle 2 - const double e2 = p2.E(); - const double px2 = (p2.Px() * tPx + p2.Py() * tPy) / tPt; - const double py2 = (p2.Py() * tPx - p2.Px() * tPy) / tPt; - const double pz2 = gamma * (p2.Pz() - beta * e2); - - const double outLCMS = fDKOut - px2; - const double sideLCMS = fDKSide - py2; - const double longLCMS = fDKLong - pz2; - - if (mShFrame == ShFrameLcmsIdentical) { - // LCMS identical: Ylm <- LCMS q-differences, axis <- qinv - const double qinv = std::sqrt(outLCMS * outLCMS + sideLCMS * sideLCMS + longLCMS * longLCMS); - return {static_cast(qinv), - static_cast(outLCMS), static_cast(sideLCMS), static_cast(longLCMS)}; - } - if (mShFrame == ShFramePrf) { - // PRF: Ylm <- PRF q-differences, axis <- |q_PRF| (matches FemtoUniverse isIdenPRF=true) - const double pE2LCMS = gamma * (e2 - beta * p2.Pz()); - const double betaOut = tPt / tMt; - const double gammaOut = tMt / tM; - const double outPRF = gammaOut * (outLCMS - betaOut * (fDE - pE2LCMS)); - const double sidePRF = sideLCMS; - const double longPRF = longLCMS; - const double qPRF = std::sqrt(outPRF * outPRF + sidePRF * sidePRF + longPRF * longPRF); - return {static_cast(qPRF), - static_cast(outPRF), static_cast(sidePRF), static_cast(longPRF)}; - } - // LCMS non-identical (default): Ylm <- LCMS components of particle 1, axis <- k* - const double betaOut = tPt / tMt; - const double gammaOut = tMt / tM; - const double fKOut = gammaOut * (fDKOut - betaOut * fDE); - const double kstar = std::sqrt(fKOut * fKOut + fDKSide * fDKSide + fDKLong * fDKLong); - return {static_cast(kstar), - static_cast(fDKOut), static_cast(fDKSide), static_cast(fDKLong)}; - } - - template - float getPairPhiFromEventPlaneAngle(const ROOT::Math::PtEtaPhiMVector& part1, const ROOT::Math::PtEtaPhiMVector& part2, const T1& col) - { - const ROOT::Math::PtEtaPhiMVector trackSum = part1 + part2; - float phi = RecoDecay::constrainAngle(trackSum.Phi() - col.eventPlaneAngle()); - return phi; - } - o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; bool mUsePdgMass = true; double mPdgMass1 = 0.; @@ -1683,7 +1613,6 @@ class PairHistManager double mPdgMassNegDau2 = 0; modes::TransverseMassType mMtType = modes::TransverseMassType::kAveragePdgMass; - modes::KinematicVariable mKinematicVariable = modes::KinematicVariable::kKstar; int mAbsCharge1 = 1; int mAbsCharge2 = 1; @@ -1700,6 +1629,8 @@ class PairHistManager double mMass12 = 0.; double mMass13 = 0.; double mMassTot2 = 0.; + float mAlpha = 0.f; + float mBetaLab = 0.f; // mc (used for both reco-vs-truth correlation AND pure mc-truth-only pairs — // for the latter, these are simply the primary/only kinematic values, not a @@ -1762,6 +1693,13 @@ class PairHistManager bool mPlotDalitz = false; bool mPlotDeltaEtaDeltaPhi = false; + bool mPlotAlpha = false; + bool mPlotKstarVsAlphaVsMtVsMult = false; + bool mPlotKstarVsAlphaVsMtVsPt1VsPt2VsMult = false; + bool mPlotBetaLab = false; + bool mPlotKstarVsBetaLabVsMtVsMult = false; + bool mPlotKstarVsBetaLabVsMtVsPt1VsPt2VsMult = false; + bool mPlotBertschPratt = false; bool mPlotEventShape = false; @@ -1781,9 +1719,6 @@ class PairHistManager bool mShUseCent = false; int mShLMax = 1; int mShFrame = 0; - static constexpr int ShFrameLcmsNonIdentical = 0; - static constexpr int ShFrameLcmsIdentical = 1; - static constexpr int ShFramePrf = 2; o2::framework::AxisSpec mShKstarSpec{{60, 0.0f, 0.3f}, "k* (GeV/#it{c})"}; // set in init() o2::framework::AxisSpec mShCentSpec{{1, 0.0f, 200.0f}, "mult/cent"}; diff --git a/PWGCF/Femto/Core/pairProcessHelpers.h b/PWGCF/Femto/Core/pairProcessHelpers.h index 54323079df3..6a41c2095c8 100644 --- a/PWGCF/Femto/Core/pairProcessHelpers.h +++ b/PWGCF/Femto/Core/pairProcessHelpers.h @@ -429,7 +429,9 @@ template + typename T10, + typename T11, + typename T12> void processMixedEvent(T1 const& Collisions, T2& Partition1, T3& Partition2, @@ -439,11 +441,18 @@ void processMixedEvent(T1 const& Collisions, T7 const& depth, T8& PairHistManager, T9& CprManager, - T10& PcManager) + T10& PcManager, + int eventStride, + T11& ParticleHistManagerMe1, + T12& ParticleHistManagerMe2) { int64_t lastCollisionIndex = -1; int windowSizeRaw = 0; int windowSizeEffective = 0; + // counts actual anchor windows encountered (not raw collision IDs, which may have gaps from + // upstream event selection) so "every Nth anchor" is evenly spaced regardless of such gaps + int64_t windowCounter = -1; + bool skipThisWindow = false; // collision1 is fixed across each mixing window, so its track slice is // materialized once per window and reused for every mixing partner, instead @@ -455,13 +464,24 @@ void processMixedEvent(T1 const& Collisions, // --- new window --- if (collision1.globalIndex() != lastCollisionIndex) { - if (lastCollisionIndex != -1) { + if (lastCollisionIndex != -1 && !skipThisWindow) { PairHistManager.fillMixingQaMePerMixingBin(windowSizeRaw, windowSizeEffective); } windowSizeRaw = 0; windowSizeEffective = 0; lastCollisionIndex = collision1.globalIndex(); - sliceParticle1.emplace(Partition1->sliceByCached(o2::aod::femtobase::stored::fColId, collision1.globalIndex(), cache)); + ++windowCounter; + // skip entire anchor windows to reduce mixed-event combinatorics without truncating any + // single event's particle content -- every anchor that IS processed still gets its full + // `depth` worth of mixing partners + skipThisWindow = eventStride > 1 && windowCounter % eventStride != 0; + if (!skipThisWindow) { + sliceParticle1.emplace(Partition1->sliceByCached(o2::aod::femtobase::stored::fColId, collision1.globalIndex(), cache)); + } + } + + if (skipThisWindow) { + continue; } ++windowSizeRaw; @@ -502,6 +522,11 @@ void processMixedEvent(T1 const& Collisions, hasValidPair = true; PairHistManager.trackParticlesPerEvent(p1, p2); PairHistManager.template fill(); + // single-particle histograms for particles that make it into an accepted mixed-event + // pair -- filled once per accepted pair (not deduplicated), so a particle contributes + // here exactly as often as it actually contributes to the mixed-event pair sample. + ParticleHistManagerMe1.template fill(p1, TrackTable); + ParticleHistManagerMe2.template fill(p2, TrackTable); } } @@ -535,7 +560,9 @@ template + typename T16, + typename T17, + typename T18> void processMixedEvent(T1 const& Collisions, T2 const& mcCollisions, T3& Partition1, @@ -551,11 +578,18 @@ void processMixedEvent(T1 const& Collisions, T13& ParticleCleaner1, T14& ParticleCleaner2, T15& CprManager, - T16& PcManager) + T16& PcManager, + int eventStride, + T17& ParticleHistManagerMe1, + T18& ParticleHistManagerMe2) { int64_t lastCollisionIndex = -1; int windowSizeRaw = 0; int windowSizeEffective = 0; + // counts actual anchor windows encountered (not raw collision IDs, which may have gaps from + // upstream event selection) so "every Nth anchor" is evenly spaced regardless of such gaps + int64_t windowCounter = -1; + bool skipThisWindow = false; // collision1 is fixed across each mixing window, so its track slice is // materialized once per window and reused for every mixing partner, instead @@ -565,13 +599,24 @@ void processMixedEvent(T1 const& Collisions, for (auto const& [collision1, collision2] : o2::soa::selfCombinations(policy, depth, -1, Collisions, Collisions)) { if (collision1.globalIndex() != lastCollisionIndex) { - if (lastCollisionIndex != -1) { + if (lastCollisionIndex != -1 && !skipThisWindow) { PairHistManager.fillMixingQaMePerMixingBin(windowSizeRaw, windowSizeEffective); } windowSizeRaw = 0; windowSizeEffective = 0; lastCollisionIndex = collision1.globalIndex(); - sliceParticle1.emplace(Partition1->sliceByCached(o2::aod::femtobase::stored::fColId, collision1.globalIndex(), cache)); + ++windowCounter; + // skip entire anchor windows to reduce mixed-event combinatorics without truncating any + // single event's particle content -- every anchor that IS processed still gets its full + // `depth` worth of mixing partners + skipThisWindow = eventStride > 1 && windowCounter % eventStride != 0; + if (!skipThisWindow) { + sliceParticle1.emplace(Partition1->sliceByCached(o2::aod::femtobase::stored::fColId, collision1.globalIndex(), cache)); + } + } + + if (skipThisWindow) { + continue; } ++windowSizeRaw; @@ -618,6 +663,9 @@ void processMixedEvent(T1 const& Collisions, hasValidPair = true; PairHistManager.trackParticlesPerEvent(p1, p2); PairHistManager.template fill(); + // single-particle histograms for particles that make it into an accepted mixed-event pair + ParticleHistManagerMe1.template fill(p1, TrackTable, collision1, mcParticles, mcMothers, mcPartonicMothers); + ParticleHistManagerMe2.template fill(p2, TrackTable, collision2, mcParticles, mcMothers, mcPartonicMothers); } } @@ -647,7 +695,9 @@ template + typename T14, + typename T15, + typename T16> void processMixedEvent(T1 const& Collisions, T2& Partition1, T3& Partition2, @@ -661,24 +711,42 @@ void processMixedEvent(T1 const& Collisions, T11& ParticleCleaner1, T12& ParticleCleaner2, T13& CprManager, - T14& PcManager) + T14& PcManager, + int eventStride, + T15& ParticleHistManagerMe1, + T16& ParticleHistManagerMe2) { int64_t lastCollisionIndex = -1; int windowSizeRaw = 0; int windowSizeEffective = 0; + // counts actual anchor windows encountered (not raw collision IDs, which may have gaps from + // upstream event selection) so "every Nth anchor" is evenly spaced regardless of such gaps + int64_t windowCounter = -1; + bool skipThisWindow = false; std::optionalsliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, 0, cache))> sliceParticle1; for (auto const& [collision1, collision2] : o2::soa::selfCombinations(policy, depth, -1, Collisions, Collisions)) { if (collision1.globalIndex() != lastCollisionIndex) { - if (lastCollisionIndex != -1) { + if (lastCollisionIndex != -1 && !skipThisWindow) { PairHistManager.fillMixingQaMePerMixingBin(windowSizeRaw, windowSizeEffective); } windowSizeRaw = 0; windowSizeEffective = 0; lastCollisionIndex = collision1.globalIndex(); - sliceParticle1.emplace(Partition1->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, collision1.globalIndex(), cache)); + ++windowCounter; + // skip entire anchor windows to reduce mixed-event combinatorics without truncating any + // single event's particle content -- every anchor that IS processed still gets its full + // `depth` worth of mixing partners + skipThisWindow = eventStride > 1 && windowCounter % eventStride != 0; + if (!skipThisWindow) { + sliceParticle1.emplace(Partition1->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, collision1.globalIndex(), cache)); + } + } + + if (skipThisWindow) { + continue; } ++windowSizeRaw; @@ -718,6 +786,9 @@ void processMixedEvent(T1 const& Collisions, hasValidPair = true; PairHistManager.trackParticlesPerEvent(p1, p2); PairHistManager.template fill(); + // single-particle histograms for particles that make it into an accepted mixed-event pair + ParticleHistManagerMe1.fill(p1, mcMothers, mcPartonicMothers); + ParticleHistManagerMe2.fill(p2, mcMothers, mcPartonicMothers); } } diff --git a/PWGCF/Femto/Core/particleCleaner.h b/PWGCF/Femto/Core/particleCleaner.h index 6c29b6ced89..f284385cce9 100644 --- a/PWGCF/Femto/Core/particleCleaner.h +++ b/PWGCF/Femto/Core/particleCleaner.h @@ -31,11 +31,11 @@ struct ConfParticleCleaner : o2::framework::ConfigurableGroup { o2::framework::Configurable> rejectedPdgCodes{"rejectedPdgCodes", {}, "Reject particles with this exact pdg code (including the sign!)"}; o2::framework::Configurable rejectParticleWithoutMcParticle{"rejectParticleWithoutMcParticle", false, "If true, particles which have no associated MC information, are rejected"}; o2::framework::Configurable rejectParticleWithoutMcMother{"rejectParticleWithoutMcMother", false, "If true, particles which have no associated mother are rejected"}; - o2::framework::Configurable rejectParticleWithoutMcPartonicMother{"rejectParticleWithoutMcPartonicMother", false, "If true, all particles which have no associated partonic mother"}; - o2::framework::Configurable> requiredMotherPdgCodes{"requiredMotherPdgCodes", {}, "Only consider particles whose mothers have one of the supplied pdg codes (inclduing the sign!)"}; - o2::framework::Configurable> rejectMotherPdgCodes{"rejectMotherPdgCodes", {}, "Only consider particles whose mothers do not have one of the supplied pdg codes (inclduing the sign!)"}; - o2::framework::Configurable> requiredPartonicMotherPdgCodes{"requiredPartonicMotherPdgCodes", {}, "Only consider particles whose partonic mothers have one of the supplied pdg codes (inclduing the sign!)"}; - o2::framework::Configurable> rejectPartonicMotherPdgCodes{"rejectPartonicMotherPdgCodes", {}, "Only consider particles whose mothers do not have one of the supplied pdg codes (inclduing the sign!)"}; + o2::framework::Configurable rejectParticleWithoutMcPartonicMother{"rejectParticleWithoutMcPartonicMother", false, "If true, particles which have no associated partonic mother are rejected"}; + o2::framework::Configurable> requiredMotherPdgCodes{"requiredMotherPdgCodes", {}, "Only consider particles whose mothers have one of the supplied pdg codes (including the sign!)"}; + o2::framework::Configurable> rejectMotherPdgCodes{"rejectMotherPdgCodes", {}, "Only consider particles whose mothers do not have one of the supplied pdg codes (including the sign!)"}; + o2::framework::Configurable> requiredPartonicMotherPdgCodes{"requiredPartonicMotherPdgCodes", {}, "Only consider particles whose partonic mothers have one of the supplied pdg codes (including the sign!)"}; + o2::framework::Configurable> rejectPartonicMotherPdgCodes{"rejectPartonicMotherPdgCodes", {}, "Only consider particles whose partonic mothers do not have one of the supplied pdg codes (including the sign!)"}; }; constexpr const char PrefixTrackCleaner1[] = "TrackCleaner1"; @@ -73,6 +73,11 @@ constexpr const char PrefixSigmaPlusCleaner2[] = "SigmaPlusCleaner2"; using ConfSigmaPlusCleaner1 = ConfParticleCleaner; using ConfSigmaPlusCleaner2 = ConfParticleCleaner; +constexpr const char PrefixSigmaPlusPcmCleaner1[] = "SigmaPlusPcmCleaner1"; +constexpr const char PrefixSigmaPlusPcmCleaner2[] = "SigmaPlusPcmCleaner2"; +using ConfSigmaPlusPcmCleaner1 = ConfParticleCleaner; +using ConfSigmaPlusPcmCleaner2 = ConfParticleCleaner; + constexpr const char PrefixXiCleaner1[] = "XiCleaner1"; constexpr const char PrefixXiCleaner2[] = "XiCleaner2"; using ConfXiCleaner1 = ConfParticleCleaner; diff --git a/PWGCF/Femto/Core/partitions.h b/PWGCF/Femto/Core/partitions.h index fac0704c71f..6a3a473ecf9 100644 --- a/PWGCF/Femto/Core/partitions.h +++ b/PWGCF/Femto/Core/partitions.h @@ -56,25 +56,47 @@ (o2::aod::femtobase::stored::mass > (selection).massMin) && \ (o2::aod::femtobase::stored::mass < (selection).massMax) -// partition for phis and rhos, i.e. resonance that are their own antiparticle -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define MAKE_RESONANCE_0_PARTITON(selection) \ - (o2::aod::femtobase::stored::pt > (selection).ptMin) && \ - (o2::aod::femtobase::stored::pt < (selection).ptMax) && \ - (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ - (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ - (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ - (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ - (o2::aod::femtobase::stored::mass > (selection).massMin) && \ - (o2::aod::femtobase::stored::mass < (selection).massMax) && \ - ifnode(o2::aod::femtotwotrackresonances::posDauHasHighMomentum, \ - ncheckbit(o2::aod::femtotwotrackresonances::maskPosDau, (selection).posDauMaskAboveThres), \ - ncheckbit(o2::aod::femtotwotrackresonances::maskPosDau, (selection).posDauMaskBelowThres)) && \ - ifnode(o2::aod::femtotwotrackresonances::negDauHasHighMomentum, \ - ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, (selection).negDauMaskAboveThres), \ - ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, (selection).negDauMaskBelowThres)) - -// partition for kstars, they have distinct antiparticle +// partition for resonance daughters (pion+/-, kaon+/- legs): unlike MAKE_TRACK_PARTITION, chargeSign +// and chargeAbs are compile-time constants on the selection struct (ConfResonanceDaughterSelection in +// trackBuilder.h), not runtime Configurables, so the sign comparison is just a comparison to a constant +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_RESONANCE_DAUGHTER_PARTITION(selection) \ + ((selection).chargeSign > 0 ? o2::aod::femtobase::stored::signedPt > 0.f : o2::aod::femtobase::stored::signedPt < 0.f) && \ + (nabs((selection).chargeAbs * o2::aod::femtobase::stored::signedPt) > (selection).ptMin) && \ + (nabs((selection).chargeAbs * o2::aod::femtobase::stored::signedPt) < (selection).ptMax) && \ + (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ + (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ + (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ + (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ + ifnode(TRACK_MOMENTUM((selection).chargeAbs, o2::aod::femtobase::stored::signedPt, o2::aod::femtobase::stored::eta) <= (selection).pidThres, \ + ncheckbit(o2::aod::femtotracks::mask, (selection).maskLowMomentum) && \ + (o2::aod::femtotracks::mask & (selection).rejectionMaskLowMomentum) == static_cast(0), \ + ncheckbit(o2::aod::femtotracks::mask, (selection).maskHighMomentum) && \ + (o2::aod::femtotracks::mask & (selection).rejectionMaskHighMomentum) == static_cast(0)) + +// partition for phis and rhos, i.e. resonance that are their own antiparticle -- both daughters are +// the same species, so the selection carries a single dauMask pair applied to both stored columns +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_RESONANCE_0_PARTITON(selection) \ + (o2::aod::femtobase::stored::pt > (selection).ptMin) && \ + (o2::aod::femtobase::stored::pt < (selection).ptMax) && \ + (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ + (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ + (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ + (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ + (o2::aod::femtobase::stored::mass > (selection).massMin) && \ + (o2::aod::femtobase::stored::mass < (selection).massMax) && \ + ifnode(o2::aod::femtotwotrackresonances::posDauHasHighMomentum, \ + ncheckbit(o2::aod::femtotwotrackresonances::maskPosDau, (selection).dauMaskAboveThres), \ + ncheckbit(o2::aod::femtotwotrackresonances::maskPosDau, (selection).dauMaskBelowThres)) && \ + ifnode(o2::aod::femtotwotrackresonances::negDauHasHighMomentum, \ + ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, (selection).dauMaskAboveThres), \ + ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, (selection).dauMaskBelowThres)) + +// partition for kstars, they have distinct antiparticle -- K*0 (K+ pi-) and K*0bar (K- pi+) swap +// which physical daughter (pos/neg) is the kaon vs the pion, so the selection carries masks named by +// species (kaon/pion) and this macro resolves pos/neg <-> kaon/pion from the candidate's own +// signedPt, the same way it's reconstructed (no need for the user to reason about this downstream) // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define MAKE_RESONANCE_1_PARTITON(selection) \ ifnode((selection).sign.node() != 0, \ @@ -87,12 +109,57 @@ (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ (o2::aod::femtobase::stored::mass > (selection).massMin) && \ (o2::aod::femtobase::stored::mass < (selection).massMax) && \ - ifnode(o2::aod::femtotwotrackresonances::posDauHasHighMomentum, \ - ncheckbit(o2::aod::femtotwotrackresonances::maskPosDau, (selection).posDauMaskAboveThres), \ - ncheckbit(o2::aod::femtotwotrackresonances::maskPosDau, (selection).posDauMaskBelowThres)) && \ - ifnode(o2::aod::femtotwotrackresonances::negDauHasHighMomentum, \ - ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, (selection).negDauMaskAboveThres), \ - ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, (selection).negDauMaskBelowThres)) + ifnode(o2::aod::femtobase::stored::signedPt > 0.f, \ + ifnode(o2::aod::femtotwotrackresonances::posDauHasHighMomentum, \ + ncheckbit(o2::aod::femtotwotrackresonances::maskPosDau, (selection).kaonDauMaskAboveThres), \ + ncheckbit(o2::aod::femtotwotrackresonances::maskPosDau, (selection).kaonDauMaskBelowThres)) && \ + ifnode(o2::aod::femtotwotrackresonances::negDauHasHighMomentum, \ + ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, (selection).pionDauMaskAboveThres), \ + ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, (selection).pionDauMaskBelowThres)), \ + ifnode(o2::aod::femtotwotrackresonances::posDauHasHighMomentum, \ + ncheckbit(o2::aod::femtotwotrackresonances::maskPosDau, (selection).pionDauMaskAboveThres), \ + ncheckbit(o2::aod::femtotwotrackresonances::maskPosDau, (selection).pionDauMaskBelowThres)) && \ + ifnode(o2::aod::femtotwotrackresonances::negDauHasHighMomentum, \ + ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, (selection).kaonDauMaskAboveThres), \ + ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, (selection).kaonDauMaskBelowThres))) + +// partition for resonances built from two photons (pi0, eta, ...): unlike MAKE_RESONANCE_0/1_PARTITON +// there is no sign and no momentum-threshold PID switch, and just a single mask check -- both +// daughters are plain photons with the same selection requirement, so the candidate only stores the +// AND of both legs' own masks (see femtotwophotonresonances::MaskDau) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_TWOPHOTONRESONANCE_PARTITION(selection) \ + (o2::aod::femtobase::stored::pt > (selection).ptMin) && \ + (o2::aod::femtobase::stored::pt < (selection).ptMax) && \ + (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ + (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ + (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ + (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ + (o2::aod::femtobase::stored::mass > (selection).massMin) && \ + (o2::aod::femtobase::stored::mass < (selection).massMax) && \ + ncheckbit(o2::aod::femtotwophotonresonances::maskDau, (selection).dauMask) + +// partition for resonances built from two tracks and a two-photon resonance (e.g. omega(782) -> +// pi+ pi- pi0): same pattern as MAKE_RESONANCE_0_PARTITON (both charged daughters are the same +// species -- pions, just opposite charge, no kstar-style species swap -- so a single dauMask pair +// covers both), plus a 3rd mask for the neutral daughter (no momentum-threshold split there) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_TRACKTRACKTWOPHOTONRESONANCE_PARTITION(selection) \ + (o2::aod::femtobase::stored::pt > (selection).ptMin) && \ + (o2::aod::femtobase::stored::pt < (selection).ptMax) && \ + (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ + (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ + (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ + (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ + (o2::aod::femtobase::stored::mass > (selection).massMin) && \ + (o2::aod::femtobase::stored::mass < (selection).massMax) && \ + ifnode(o2::aod::femtotrackstwophotonresonances::posDauHasHighMomentum, \ + ncheckbit(o2::aod::femtotrackstwophotonresonances::maskPosDau, (selection).pionDauMaskAboveThres), \ + ncheckbit(o2::aod::femtotrackstwophotonresonances::maskPosDau, (selection).pionDauMaskBelowThres)) && \ + ifnode(o2::aod::femtotrackstwophotonresonances::negDauHasHighMomentum, \ + ncheckbit(o2::aod::femtotrackstwophotonresonances::maskNegDau, (selection).pionDauMaskAboveThres), \ + ncheckbit(o2::aod::femtotrackstwophotonresonances::maskNegDau, (selection).pionDauMaskBelowThres)) && \ + ncheckbit(o2::aod::femtotrackstwophotonresonances::maskNeutralDau, (selection).neutralDauMask) // partition for lambdas // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) @@ -123,6 +190,18 @@ (o2::aod::femtobase::stored::mass < (selection).massMax) && \ ncheckbit(o2::aod::femtov0s::mask, (selection).mask) +// partition for photons (PCM) +// no sign/mass handling: photons are their own antiparticle and have no mass window +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_PHOTON_PARTITION(selection) \ + (o2::aod::femtobase::stored::pt > (selection).ptMin) && \ + (o2::aod::femtobase::stored::pt < (selection).ptMax) && \ + (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ + (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ + (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ + (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ + ncheckbit(o2::aod::femtophotons::mask, (selection).mask) + // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define MAKE_CASCADE_PARTITION(selection) \ ifnode((selection).sign.node() != 0, \ @@ -165,6 +244,21 @@ (o2::aod::femtobase::stored::mass < (selection).massMax) && \ ncheckbit(o2::aod::femtokinks::mask, (selection).mask) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_SIGMAPLUSPCM_PARTITION(selection) \ + ifnode((selection).sign.node() != 0, \ + ifnode((selection).sign.node() > 0, o2::aod::femtobase::stored::signedPt > 0.f, o2::aod::femtobase::stored::signedPt < 0.f), true) && \ + (nabs(o2::aod::femtobase::stored::signedPt) > (selection).ptMin) && \ + (nabs(o2::aod::femtobase::stored::signedPt) < (selection).ptMax) && \ + (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ + (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ + (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ + (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ + (o2::aod::femtobase::stored::mass > (selection).massMin) && \ + (o2::aod::femtobase::stored::mass < (selection).massMax) && \ + (o2::aod::femtokinks::mlScore >= (selection).mlScoreMin) && \ + ncheckbit(o2::aod::femtokinks::mask, (selection).mask) + // partition for D0 // sign of signedPt encodes the hypothesis: D0 (+) / D0bar (-) // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) diff --git a/PWGCF/Femto/Core/photonBuilder.h b/PWGCF/Femto/Core/photonBuilder.h new file mode 100644 index 00000000000..d820c6dca72 --- /dev/null +++ b/PWGCF/Femto/Core/photonBuilder.h @@ -0,0 +1,413 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file photonBuilder.h +/// \brief photon (PCM) builder +/// +/// Default selection values for the mandatory cuts (dauTpcNSigmaElAbsMax, keepDaughtersWithoutTpc) +/// are taken from the PCM branch of EventFiltering/PWGEM/EMPhotonFilter.cxx (isSelectedSecondary() +/// and the min_pt_pcm_photon cut in runFilter()): both V0 daughters must have |TPC nSigma_e| < 3.5 +/// whenever a TPC signal is available, and a daughter without TPC is not rejected. Note that +/// EMPhotonFilter.cxx also declares minpt_v0, maxeta_v0, max_dcatopv_xy_v0 and max_dcatopv_z_v0, +/// but never applies them -- they are dead configurables in that task. The additional geometric +/// cuts here (cosPA, DCA to PV, chi2/NDF, radius) are standard PCM conversion-quality cuts, not +/// used by that filter task, so they default to permissive (no-op) values. +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_PHOTONBUILDER_H_ +#define PWGCF_FEMTO_CORE_PHOTONBUILDER_H_ + +#include "PWGCF/Femto/Core/baseSelection.h" +#include "PWGCF/Femto/Core/dataTypes.h" +#include "PWGCF/Femto/Core/femtoUtils.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/selectionContainer.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace o2::analysis::femto::photonbuilder +{ + +// filters applied in the producer task (loose pre-selection, applied before quality/PID cuts) +struct ConfPhotonFilters : o2::framework::ConfigurableGroup { + std::string prefix = std::string("PhotonFilters"); + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT (matches EMPhotonFilter min_pt_pcm_photon default)"}; + o2::framework::Configurable ptMax{"ptMax", 99.f, "Maximum pT"}; + o2::framework::Configurable etaMin{"etaMin", -10.f, "Minimum eta"}; + o2::framework::Configurable etaMax{"etaMax", 10.f, "Maximum eta"}; + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; + o2::framework::Configurable mGammaAbsMax{"mGammaAbsMax", 1e10f, "Maximum |m_ee| of the conversion (KF) at the secondary vertex"}; +}; + +// selection bits for photons (PCM) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define PHOTON_DEFAULT_BITS \ + o2::framework::Configurable passThrough{"passThrough", false, "If true, all photons are passed through. Bits for all selections are stored."}; \ + o2::framework::Configurable> cosPaMin{"cosPaMin", {-1.f}, "Minimum cosine of pointing angle (not used by EMPhotonFilter, default is permissive)"}; \ + o2::framework::Configurable> dcaToPvXYAbsMax{"dcaToPvXYAbsMax", {1e10f}, "Maximum |DCAxy| of the photon (V0) to the PV (cm)"}; \ + o2::framework::Configurable> dcaToPvZAbsMax{"dcaToPvZAbsMax", {1e10f}, "Maximum |DCAz| of the photon (V0) to the PV (cm)"}; \ + o2::framework::Configurable> chi2NdfMax{"chi2NdfMax", {1e10f}, "Maximum chi2/NDF of the KF conversion vertex"}; \ + o2::framework::Configurable> v0RadiusMin{"v0RadiusMin", {0.f}, "Minimum transverse radius of the conversion point (cm)"}; \ + o2::framework::Configurable> v0RadiusMax{"v0RadiusMax", {1e10f}, "Maximum transverse radius of the conversion point (cm)"}; \ + o2::framework::Configurable> dauAbsEtaMax{"dauAbsEtaMax", {0.9f}, "Maximum |eta| for daughter tracks (matches EMPhotonFilter maxeta_v0, though unused there)"}; \ + o2::framework::Configurable> dauTpcNSigmaElAbsMax{"dauTpcNSigmaElAbsMax", {3.5f}, "Maximum |TPC nSigma_e| for V0 daughters (from EMPhotonFilter isSelectedSecondary)"}; \ + o2::framework::Configurable keepDaughtersWithoutTpc{"keepDaughtersWithoutTpc", true, "If true, a daughter without a TPC signal is not rejected by the TPC electron PID cut (matches EMPhotonFilter behaviour)"}; + +struct ConfPhotonBits : o2::framework::ConfigurableGroup { + std::string prefix = std::string("PhotonBits"); + PHOTON_DEFAULT_BITS +}; + +#undef PHOTON_DEFAULT_BITS + +// base selection for QA/analysis tasks, used to re-select already produced photons by mask +// (analogous to ConfLambdaSelection/ConfK0shortSelection in spirit, but photons have neither +// a PDG hypothesis nor a mass window) +template +struct ConfPhotonSelection : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; + o2::framework::Configurable ptMax{"ptMax", 999.f, "Maximum pT"}; + o2::framework::Configurable etaMin{"etaMin", -10.f, "Minimum eta"}; + o2::framework::Configurable etaMax{"etaMax", 10.f, "Maximum eta"}; + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; + o2::framework::Configurable mask{"mask", 0, "Bitmask for photon selection"}; +}; + +constexpr const char PrefixPhotonSelection1[] = "PhotonSelection1"; +using ConfPhotonSelection1 = ConfPhotonSelection; + +/// The different selections for photons (PCM) +enum PhotonSels { + kCosPaMin, ///< Min. CPA (cosine pointing angle) of the conversion to the PV + kDcaToPvXYAbsMax, ///< Max. |DCAxy| of the photon to the PV + kDcaToPvZAbsMax, ///< Max. |DCAz| of the photon to the PV + kChi2NdfMax, ///< Max. chi2/NDF of the KF conversion vertex + kV0RadiusMin, ///< Min. transverse radius of the conversion point + kV0RadiusMax, ///< Max. transverse radius of the conversion point + kDauAbsEtaMax, ///< Max. absolute pseudorapidity of the daughters + kPosDauTpcNSigmaEl, ///< TPC electron PID for positive daughter + kNegDauTpcNSigmaEl, ///< TPC electron PID for negative daughter + kPhotonSelsMax +}; + +constexpr char PhotonSelHistName[] = "hPhotonSelection"; +constexpr char PhotonSelsName[] = "Photon selection object"; +const std::unordered_map photonSelectionNames = { + {kCosPaMin, "Min. CPA (cosine pointing angle)"}, + {kDcaToPvXYAbsMax, "Max. |DCAxy| to PV"}, + {kDcaToPvZAbsMax, "Max. |DCAz| to PV"}, + {kChi2NdfMax, "Max. chi2/NDF of conversion vertex"}, + {kV0RadiusMin, "Min. transverse radius"}, + {kV0RadiusMax, "Max. transverse radius"}, + {kDauAbsEtaMax, "Max. absolute pseudorapidity of daughters"}, + {kPosDauTpcNSigmaEl, "TPC electron PID for positive daughter"}, + {kNegDauTpcNSigmaEl, "TPC electron PID for negative daughter"}, +}; + +// enum for all photon (pre)filters (loose pre-selection, applied before quality/PID cuts) +enum PhotonFilters { + kPtMin, + kPtMax, + kEtaMin, + kEtaMax, + kPhiMin, + kPhiMax, + kMGammaAbsMax, + kPhotonFiltersMax +}; + +constexpr char PhotonFilterHistName[] = "hPhotonFilters"; +const std::unordered_map photonFilterNames = { + {kPtMin, "ptMin"}, + {kPtMax, "ptMax"}, + {kEtaMin, "etaMin"}, + {kEtaMax, "etaMax"}, + {kPhiMin, "phiMin"}, + {kPhiMax, "phiMax"}, + {kMGammaAbsMax, "mGammaAbsMax"}, +}; + +/// \brief Cut class to contain and execute all cuts applied to PCM photons (V0PhotonsKF) +template +class PhotonSelection : public baseselection::BaseSelection +{ + public: + PhotonSelection() = default; + ~PhotonSelection() override = default; + + template + void configure(o2::framework::HistogramRegistry* registry, T1& config, T2& filter) + { + this->init(config.passThrough.value); + + mPtMin = filter.ptMin.value; + mPtMax = filter.ptMax.value; + mEtaMin = filter.etaMin.value; + mEtaMax = filter.etaMax.value; + mPhiMin = filter.phiMin.value; + mPhiMax = filter.phiMax.value; + mMGammaAbsMax = filter.mGammaAbsMax.value; + mKeepDaughtersWithoutTpc = config.keepDaughtersWithoutTpc.value; + + this->addSelection(kCosPaMin, photonSelectionNames.at(kCosPaMin), config.cosPaMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kDcaToPvXYAbsMax, photonSelectionNames.at(kDcaToPvXYAbsMax), config.dcaToPvXYAbsMax.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kDcaToPvZAbsMax, photonSelectionNames.at(kDcaToPvZAbsMax), config.dcaToPvZAbsMax.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kChi2NdfMax, photonSelectionNames.at(kChi2NdfMax), config.chi2NdfMax.value, limits::kUpperLimit, true, true, false); + this->addSelection(kV0RadiusMin, photonSelectionNames.at(kV0RadiusMin), config.v0RadiusMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kV0RadiusMax, photonSelectionNames.at(kV0RadiusMax), config.v0RadiusMax.value, limits::kUpperLimit, true, true, false); + this->addSelection(kDauAbsEtaMax, photonSelectionNames.at(kDauAbsEtaMax), config.dauAbsEtaMax.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kPosDauTpcNSigmaEl, photonSelectionNames.at(kPosDauTpcNSigmaEl), config.dauTpcNSigmaElAbsMax.value, limits::kAbsUpperLimit, true, true, false); + this->addComments(kPosDauTpcNSigmaEl, "keepDaughtersWithoutTpc = " + std::to_string(mKeepDaughtersWithoutTpc)); + this->addSelection(kNegDauTpcNSigmaEl, photonSelectionNames.at(kNegDauTpcNSigmaEl), config.dauTpcNSigmaElAbsMax.value, limits::kAbsUpperLimit, true, true, false); + this->addComments(kNegDauTpcNSigmaEl, "keepDaughtersWithoutTpc = " + std::to_string(mKeepDaughtersWithoutTpc)); + + this->setupSelectionHistogram(registry); + this->template setupFilterHistogram( + registry, + { + {photonFilterNames.at(kPtMin), mPtMin}, + {photonFilterNames.at(kPtMax), mPtMax}, + {photonFilterNames.at(kEtaMin), mEtaMin}, + {photonFilterNames.at(kEtaMax), mEtaMax}, + {photonFilterNames.at(kPhiMin), mPhiMin}, + {photonFilterNames.at(kPhiMax), mPhiMax}, + {photonFilterNames.at(kMGammaAbsMax), mMGammaAbsMax}, + }); + } + + template + void applySelections(T1 const& photon, T2 const& /*v0legs*/) + { + this->reset(); + this->evaluateObservable(kCosPaMin, photon.cospa()); + this->evaluateObservable(kDcaToPvXYAbsMax, photon.dcaXYtopv()); + this->evaluateObservable(kDcaToPvZAbsMax, photon.dcaZtopv()); + this->evaluateObservable(kChi2NdfMax, photon.chiSquareNDF()); + this->evaluateObservable(kV0RadiusMin, photon.v0radius()); + this->evaluateObservable(kV0RadiusMax, photon.v0radius()); + + auto posDaughter = photon.template posTrack_as(); + auto negDaughter = photon.template negTrack_as(); + + std::array etaAbsDaughters = {std::fabs(posDaughter.eta()), std::fabs(negDaughter.eta())}; + this->evaluateObservable(kDauAbsEtaMax, *std::max_element(etaAbsDaughters.begin(), etaAbsDaughters.end())); + + // TPC electron PID: evaluate only when a TPC signal is available, matching + // EMPhotonFilter::isSelectedSecondary(), which never rejects a daughter without TPC. + // If keepDaughtersWithoutTpc is false, feed a value that fails every limit instead of + // skipping evaluation entirely, so this (always mandatory) cut correctly rejects the + // candidate instead of silently letting it through + auto evaluateDaughterTpcEl = [this](PhotonSels bit, bool hasTpc, float tpcNSigmaEl) { + if (hasTpc) { + this->evaluateObservable(bit, tpcNSigmaEl); + } else if (mKeepDaughtersWithoutTpc) { + this->evaluateObservable(bit, 0.f); + } else { + this->evaluateObservable(bit, std::numeric_limits::max()); + } + }; + evaluateDaughterTpcEl(kPosDauTpcNSigmaEl, posDaughter.hasTPC(), posDaughter.tpcNSigmaEl()); + evaluateDaughterTpcEl(kNegDauTpcNSigmaEl, negDaughter.hasTPC(), negDaughter.tpcNSigmaEl()); + + this->assembleBitmask(); + } + + template + bool checkFilters(const T& photon) const + { + bool pass = true; + bool p = false; + + p = photon.pt() > mPtMin; + this->template fillFilter(kPtMin, p); + pass &= p; + + p = photon.pt() < mPtMax; + this->template fillFilter(kPtMax, p); + pass &= p; + + p = photon.eta() > mEtaMin; + this->template fillFilter(kEtaMin, p); + pass &= p; + + p = photon.eta() < mEtaMax; + this->template fillFilter(kEtaMax, p); + pass &= p; + + p = photon.phi() > mPhiMin; + this->template fillFilter(kPhiMin, p); + pass &= p; + + p = photon.phi() < mPhiMax; + this->template fillFilter(kPhiMax, p); + pass &= p; + + p = std::fabs(photon.mGamma()) < mMGammaAbsMax; + this->template fillFilter(kMGammaAbsMax, p); + pass &= p; + + this->template fillFilterSummary(pass); + + return this->isPassThrough() || pass; + } + + protected: + // kinematic filters + float mPtMin = 0.f; + float mPtMax = 99.f; + float mEtaMin = -10.f; + float mEtaMax = 10.f; + float mPhiMin = 0.f; + float mPhiMax = o2::constants::math::TwoPI; + float mMGammaAbsMax = 1e10f; + + bool mKeepDaughtersWithoutTpc = true; +}; + +struct PhotonBuilderProducts : o2::framework::ProducesGroup { + o2::framework::Produces producedPhotons; + o2::framework::Produces producedLitePhotons; + o2::framework::Produces producedPhotonMasks; + o2::framework::Produces producedPhotonExtras; +}; + +struct ConfPhotonTables : o2::framework::ConfigurableGroup { + std::string prefix = std::string("PhotonTables"); + o2::framework::Configurable producePhotons{"producePhotons", -1, "Produce Photons (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceLitePhotons{"produceLitePhotons", -1, "Produce LitePhotons (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable producePhotonMasks{"producePhotonMasks", -1, "Produce PhotonMasks (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable producePhotonExtras{"producePhotonExtras", -1, "Produce PhotonExtras (-1: auto; 0 off; 1 on)"}; +}; + +/// \brief Builder for PCM photons (V0PhotonsKF + V0Legs, from PWGEM/PhotonMeson) +/// +/// Unlike the strangeness V0 builder, daughter (conversion electron) tracks are, for now, not +/// indexed into FTracks: the legs (V0Legs) are TPC-only tracks, so they cannot be confused for +/// (i.e. share hits with/be the same reconstructed object as) a global track the way two V0 +/// daughters or a track and a V0 daughter can -- there is no detector-overlap effect for close +/// pair rejection to correct for. Only photon-level kinematics plus daughter PID/quality +/// diagnostics (FPhotonExtras) are stored. +class PhotonBuilder +{ + public: + PhotonBuilder() = default; + ~PhotonBuilder() = default; + + template + void init(o2::framework::HistogramRegistry* registry, T1& config, T2& filter, T3& table, T4& initContext) + { + LOG(info) << "Initialize femto photon (PCM) builder..."; + mProducePhotons = utils::enableTable("FPhotons_001", table.producePhotons.value, initContext); + mProduceLitePhotons = utils::enableTable("FLitePhotons_001", table.produceLitePhotons.value, initContext); + mProducePhotonMasks = utils::enableTable("FPhotonMasks_001", table.producePhotonMasks.value, initContext); + mProducePhotonExtras = utils::enableTable("FPhotonExtras_001", table.producePhotonExtras.value, initContext); + + if (mProducePhotons && mProduceLitePhotons) { + LOG(fatal) << "FPhotons and FLitePhotons are mutually exclusive -- enable only one. " + << "FLitePhotons is meant to replace FPhotons at the producer stage (for better compression in derived data)."; + } + if (mProducePhotons || mProduceLitePhotons || mProducePhotonMasks || mProducePhotonExtras) { + mFillAnyTable = true; + } else { + LOG(info) << "No tables configured, Selection object will not be configured..."; + LOG(info) << "Initialization done..."; + return; + } + mPhotonSelection.configure(registry, config, filter); + mPhotonSelection.printSelections(PhotonSelsName); + LOG(info) << "Initialization done..."; + } + + template + void fillPhotons(T1 const& col, T2& collisionBuilder, T3& collisionProducts, T4& photonProducts, T5 const& photons, T6 const& v0legs) + { + if (!mFillAnyTable) { + return; + } + for (const auto& photon : photons) { + if (!mPhotonSelection.checkFilters(photon)) { + continue; + } + mPhotonSelection.applySelections(photon, v0legs); + if (!mPhotonSelection.passesAllRequiredSelections()) { + continue; + } + + collisionBuilder.template fillCollision(collisionProducts, col); + + if (mProducePhotons) { + photonProducts.producedPhotons(collisionBuilder.collisionIndex(), + photon.pt(), + photon.eta(), + photon.phi()); + } + if (mProduceLitePhotons) { + photonProducts.producedLitePhotons(collisionBuilder.collisionIndex(), + o2::aod::femtobase::lite::binUnsignedPt(photon.pt()), + o2::aod::femtobase::lite::binEta(photon.eta()), + o2::aod::femtobase::lite::binPhi(photon.phi())); + } + if (mProducePhotonMasks) { + photonProducts.producedPhotonMasks(mPhotonSelection.getBitmask()); + } + if (mProducePhotonExtras) { + auto posDaughter = photon.template posTrack_as(); + auto negDaughter = photon.template negTrack_as(); + photonProducts.producedPhotonExtras( + photon.cospa(), + photon.dcaXYtopv(), + photon.dcaZtopv(), + photon.chiSquareNDF(), + photon.v0radius(), + posDaughter.pt(), + negDaughter.pt(), + posDaughter.tpcInnerParam(), + negDaughter.tpcInnerParam(), + posDaughter.tpcSignal(), + negDaughter.tpcSignal(), + posDaughter.tpcNSigmaEl(), + negDaughter.tpcNSigmaEl(), + photon.vx(), + photon.vy(), + photon.vz()); + } + } + } + + [[nodiscard]] bool fillAnyTable() const { return mFillAnyTable; } + [[nodiscard]] bool isPassThrough() const { return mPhotonSelection.isPassThrough(); } + + private: + PhotonSelection mPhotonSelection; + bool mFillAnyTable = false; + bool mProducePhotons = false; + bool mProduceLitePhotons = false; + bool mProducePhotonMasks = false; + bool mProducePhotonExtras = false; +}; + +} // namespace o2::analysis::femto::photonbuilder +#endif // PWGCF_FEMTO_CORE_PHOTONBUILDER_H_ diff --git a/PWGCF/Femto/Core/photonHistManager.h b/PWGCF/Femto/Core/photonHistManager.h new file mode 100644 index 00000000000..96ab98cc711 --- /dev/null +++ b/PWGCF/Femto/Core/photonHistManager.h @@ -0,0 +1,303 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file photonHistManager.h +/// \brief histogram manager for photon (PCM) histograms +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_PHOTONHISTMANAGER_H_ +#define PWGCF_FEMTO_CORE_PHOTONHISTMANAGER_H_ + +#include "PWGCF/Femto/Core/histManager.h" +#include "PWGCF/Femto/Core/modes.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace o2::analysis::femto::photonhistmanager +{ +// enum for photon (PCM) histograms +enum PhotonHist { + // analysis + kPt, + kEta, + kPhi, + // qa variables + kCosPa, + kPa, + kDcaToPvXY, + kDcaToPvZ, + kChi2Ndf, + kV0Radius, + kDecayVtxX, + kDecayVtxY, + kDecayVtxZ, + kDecayVtx, + kPosDauTpcNSigmaEl, + kNegDauTpcNSigmaEl, + kPosDauPt, + kNegDauPt, + // 2d qa + kPtVsEta, + kPtVsPhi, + kPhiVsEta, + kPtVsCosPa, + kPosDauVsNegDauTpcNSigmaEl, + kPosDauTpcSignalVsP, + kNegDauTpcSignalVsP, + + kPhotonHistLast +}; + +struct ConfPhotonBinning : o2::framework::ConfigurableGroup { + std::string prefix = std::string("PhotonBinning"); + o2::framework::ConfigurableAxis pt{"pt", {{600, 0, 6}}, "Pt"}; + o2::framework::ConfigurableAxis eta{"eta", {{300, -1.5, 1.5}}, "Eta"}; + o2::framework::ConfigurableAxis phi{"phi", {{720, 0, 1.f * o2::constants::math::TwoPI}}, "Phi"}; +}; + +struct ConfPhotonQaBinning : o2::framework::ConfigurableGroup { + std::string prefix = std::string("PhotonQaBinning"); + o2::framework::Configurable plot2d{"plot2d", true, "Generate various 2D QA plots"}; + o2::framework::ConfigurableAxis cosPa{"cosPa", {{100, 0.95, 1}}, "Cosine of pointing angle"}; + o2::framework::ConfigurableAxis pa{"pa", {{180, 0, 1.f * o2::constants::math::PI}}, "Pointing angle"}; + o2::framework::ConfigurableAxis dcaToPv{"dcaToPv", {{200, -1, 1}}, "DCA of the photon to the PV (cm)"}; + o2::framework::ConfigurableAxis chi2Ndf{"chi2Ndf", {{100, 0, 50}}, "Chi2/NDF of the KF conversion vertex"}; + o2::framework::ConfigurableAxis v0Radius{"v0Radius", {{200, 0, 200}}, "Transverse radius of the conversion point (cm)"}; + o2::framework::ConfigurableAxis decayVertex{"decayVertex", {{200, 0, 200}}, "Distance of the conversion point from the nominal interaction point (cm)"}; + o2::framework::ConfigurableAxis tpcNSigmaEl{"tpcNSigmaEl", {{200, -10, 10}}, "TPC n sigma electron for daughters"}; + o2::framework::ConfigurableAxis dauPt{"dauPt", {{300, 0, 3}}, "Pt of the daughters"}; + o2::framework::ConfigurableAxis dauTpcInnerParam{"dauTpcInnerParam", {{200, 0, 4}}, "Momentum of the daughters at the inner wall of the TPC (GeV/#it{c})"}; + o2::framework::ConfigurableAxis dauTpcSignal{"dauTpcSignal", {{200, 0, 200}}, "TPC dE/dx of the daughters"}; +}; + +// must be in sync with enum PhotonHist +constexpr std::array, kPhotonHistLast> HistTable = { + {{kPt, o2::framework::HistType::kTH1F, "hPt", "Transverse Momentum; p_{T} (GeV/#it{c}); Entries"}, + {kEta, o2::framework::HistType::kTH1F, "hEta", "Pseudorapdity; #eta; Entries"}, + {kPhi, o2::framework::HistType::kTH1F, "hPhi", "Azimuthal angle; #varphi; Entries"}, + {kCosPa, o2::framework::HistType::kTH1F, "hCosPa", "Cosine of pointing angle; cos(#alpha); Entries"}, + {kPa, o2::framework::HistType::kTH1F, "hPa", "Pointing angle; #alpha; Entries"}, + {kDcaToPvXY, o2::framework::HistType::kTH1F, "hDcaToPvXY", "DCA_{xy} to PV; DCA_{xy} (cm); Entries"}, + {kDcaToPvZ, o2::framework::HistType::kTH1F, "hDcaToPvZ", "DCA_{z} to PV; DCA_{z} (cm); Entries"}, + {kChi2Ndf, o2::framework::HistType::kTH1F, "hChi2Ndf", "Chi2/NDF of KF conversion vertex; #chi^{2}/NDF; Entries"}, + {kV0Radius, o2::framework::HistType::kTH1F, "hV0Radius", "Transverse radius; r_{xy} (cm); Entries"}, + {kDecayVtxX, o2::framework::HistType::kTH1F, "hDecayVtxX", "X coordinate of conversion point; DV_{X} (cm); Entries"}, + {kDecayVtxY, o2::framework::HistType::kTH1F, "hDecayVtxY", "Y coordinate of conversion point; DV_{Y} (cm); Entries"}, + {kDecayVtxZ, o2::framework::HistType::kTH1F, "hDecayVtxZ", "Z coordinate of conversion point; DV_{Z} (cm); Entries"}, + {kDecayVtx, o2::framework::HistType::kTH1F, "hDecayVtx", "Distance of conversion point from primary vertex; DV (cm); Entries"}, + {kPosDauTpcNSigmaEl, o2::framework::HistType::kTH1F, "hPosDauTpcNSigmaEl", "TPC n#sigma_{e} of positive daughter; n#sigma_{TPC,e}; Entries"}, + {kNegDauTpcNSigmaEl, o2::framework::HistType::kTH1F, "hNegDauTpcNSigmaEl", "TPC n#sigma_{e} of negative daughter; n#sigma_{TPC,e}; Entries"}, + {kPosDauPt, o2::framework::HistType::kTH1F, "hPosDauPt", "p_{T} of positive daughter; p_{T} (GeV/#it{c}); Entries"}, + {kNegDauPt, o2::framework::HistType::kTH1F, "hNegDauPt", "p_{T} of negative daughter; p_{T} (GeV/#it{c}); Entries"}, + {kPtVsEta, o2::framework::HistType::kTH2F, "hPtVsEta", "p_{T} vs #eta; p_{T} (GeV/#it{c}) ; #eta"}, + {kPtVsPhi, o2::framework::HistType::kTH2F, "hPtVsPhi", "p_{T} vs #varphi; p_{T} (GeV/#it{c}) ; #varphi"}, + {kPhiVsEta, o2::framework::HistType::kTH2F, "hPhiVsEta", "#varphi vs #eta; #varphi ; #eta"}, + {kPtVsCosPa, o2::framework::HistType::kTH2F, "hPtVsCosPa", "p_{T} vs cosine of pointing angle; p_{T} (GeV/#it{c}); cos(#alpha)"}, + {kPosDauVsNegDauTpcNSigmaEl, o2::framework::HistType::kTH2F, "hPosDauVsNegDauTpcNSigmaEl", "TPC n#sigma_{e} of positive vs negative daughter; n#sigma_{TPC,e} (pos.); n#sigma_{TPC,e} (neg.)"}, + {kPosDauTpcSignalVsP, o2::framework::HistType::kTH2F, "hPosDauTpcSignalVsP", "TPC dE/dx vs p of positive daughter; p_{TPC} (GeV/#it{c}); dE/dx"}, + {kNegDauTpcSignalVsP, o2::framework::HistType::kTH2F, "hNegDauTpcSignalVsP", "TPC dE/dx vs p of negative daughter; p_{TPC} (GeV/#it{c}); dE/dx"}}, +}; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define PHOTON_HIST_ANALYSIS_MAP(conf) \ + {kPt, {(conf).pt}}, \ + {kEta, {(conf).eta}}, \ + {kPhi, {(conf).phi}}, + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define PHOTON_HIST_QA_MAP(confAnalysis, confQa) \ + {kCosPa, {(confQa).cosPa}}, \ + {kPa, {(confQa).pa}}, \ + {kDcaToPvXY, {(confQa).dcaToPv}}, \ + {kDcaToPvZ, {(confQa).dcaToPv}}, \ + {kChi2Ndf, {(confQa).chi2Ndf}}, \ + {kV0Radius, {(confQa).v0Radius}}, \ + {kDecayVtxX, {(confQa).decayVertex}}, \ + {kDecayVtxY, {(confQa).decayVertex}}, \ + {kDecayVtxZ, {(confQa).decayVertex}}, \ + {kDecayVtx, {(confQa).decayVertex}}, \ + {kPosDauTpcNSigmaEl, {(confQa).tpcNSigmaEl}}, \ + {kNegDauTpcNSigmaEl, {(confQa).tpcNSigmaEl}}, \ + {kPosDauPt, {(confQa).dauPt}}, \ + {kNegDauPt, {(confQa).dauPt}}, \ + {kPtVsEta, {(confAnalysis).pt, (confAnalysis).eta}}, \ + {kPtVsPhi, {(confAnalysis).pt, (confAnalysis).phi}}, \ + {kPhiVsEta, {(confAnalysis).phi, (confAnalysis).eta}}, \ + {kPtVsCosPa, {(confAnalysis).pt, (confQa).cosPa}}, \ + {kPosDauVsNegDauTpcNSigmaEl, {(confQa).tpcNSigmaEl, (confQa).tpcNSigmaEl}}, \ + {kPosDauTpcSignalVsP, {(confQa).dauTpcInnerParam, (confQa).dauTpcSignal}}, \ + {kNegDauTpcSignalVsP, {(confQa).dauTpcInnerParam, (confQa).dauTpcSignal}}, + +template +auto makePhotonHistSpecMap(const T& confBinningAnalysis) +{ + return std::map>{ + PHOTON_HIST_ANALYSIS_MAP(confBinningAnalysis)}; +} + +template +std::map> makePhotonQaHistSpecMap(T1 const& confBinningAnalysis, T2 const& confBinningQa) +{ + return std::map>{ + PHOTON_HIST_ANALYSIS_MAP(confBinningAnalysis) + PHOTON_HIST_QA_MAP(confBinningAnalysis, confBinningQa)}; +} + +#undef PHOTON_HIST_ANALYSIS_MAP +#undef PHOTON_HIST_QA_MAP + +constexpr char PrefixPhotonQa[] = "PhotonQA/"; +// used only by the standalone QA task +constexpr char PrefixTwoPhotonResonanceDau1Qa[] = "TwoPhotonResonanceDau1Qa/"; +constexpr char PrefixTwoPhotonResonanceDau2Qa[] = "TwoPhotonResonanceDau2Qa/"; +// same-event siblings, used only by a pair builder's same-event resonance manager +constexpr char PrefixTwoPhotonResonanceDau1Se[] = "TwoPhotonResonanceDau1Se/"; +constexpr char PrefixTwoPhotonResonanceDau2Se[] = "TwoPhotonResonanceDau2Se/"; +// mixed-event siblings, used only by a pair builder's mixed-event resonance manager +constexpr char PrefixTwoPhotonResonanceDau1Me[] = "TwoPhotonResonanceDau1Me/"; +constexpr char PrefixTwoPhotonResonanceDau2Me[] = "TwoPhotonResonanceDau2Me/"; + +constexpr std::string_view AnalysisDir = "Analysis/"; +constexpr std::string_view QaDir = "QA/"; + +/// \class PhotonHistManager +/// \brief Histogram manager for PCM photon candidates (reco + QA only, no MC) +template +class PhotonHistManager +{ + public: + PhotonHistManager() = default; + ~PhotonHistManager() = default; + + // init for analysis only + template + void init(o2::framework::HistogramRegistry* registry, std::map> const& specs) + { + mHistogramRegistry = registry; + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { + this->initAnalysis(specs); + } + } + + // init for analysis + qa + template + void init(o2::framework::HistogramRegistry* registry, std::map> const& specs, T const& confQaBinning) + { + mHistogramRegistry = registry; + mPlot2d = confQaBinning.plot2d.value; + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { + this->initAnalysis(specs); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + this->initQa(specs); + } + } + + template + void fill(T const& photon) + { + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { + this->fillAnalysis(photon); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + this->fillQa(photon); + } + } + + private: + void initAnalysis(std::map> const& specs) + { + std::string analysisDir = std::string(Prefix) + std::string(AnalysisDir); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPt, HistTable), getHistDesc(kPt, HistTable), getHistType(kPt, HistTable), {specs.at(kPt)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kEta, HistTable), getHistDesc(kEta, HistTable), getHistType(kEta, HistTable), {specs.at(kEta)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPhi, HistTable), getHistDesc(kPhi, HistTable), getHistType(kPhi, HistTable), {specs.at(kPhi)}); + } + + void initQa(std::map> const& specs) + { + std::string qaDir = std::string(Prefix) + std::string(QaDir); + mHistogramRegistry->add(qaDir + getHistNameV2(kCosPa, HistTable), getHistDesc(kCosPa, HistTable), getHistType(kCosPa, HistTable), {specs.at(kCosPa)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPa, HistTable), getHistDesc(kPa, HistTable), getHistType(kPa, HistTable), {specs.at(kPa)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kDcaToPvXY, HistTable), getHistDesc(kDcaToPvXY, HistTable), getHistType(kDcaToPvXY, HistTable), {specs.at(kDcaToPvXY)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kDcaToPvZ, HistTable), getHistDesc(kDcaToPvZ, HistTable), getHistType(kDcaToPvZ, HistTable), {specs.at(kDcaToPvZ)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kChi2Ndf, HistTable), getHistDesc(kChi2Ndf, HistTable), getHistType(kChi2Ndf, HistTable), {specs.at(kChi2Ndf)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kV0Radius, HistTable), getHistDesc(kV0Radius, HistTable), getHistType(kV0Radius, HistTable), {specs.at(kV0Radius)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kDecayVtxX, HistTable), getHistDesc(kDecayVtxX, HistTable), getHistType(kDecayVtxX, HistTable), {specs.at(kDecayVtxX)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kDecayVtxY, HistTable), getHistDesc(kDecayVtxY, HistTable), getHistType(kDecayVtxY, HistTable), {specs.at(kDecayVtxY)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kDecayVtxZ, HistTable), getHistDesc(kDecayVtxZ, HistTable), getHistType(kDecayVtxZ, HistTable), {specs.at(kDecayVtxZ)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kDecayVtx, HistTable), getHistDesc(kDecayVtx, HistTable), getHistType(kDecayVtx, HistTable), {specs.at(kDecayVtx)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPosDauTpcNSigmaEl, HistTable), getHistDesc(kPosDauTpcNSigmaEl, HistTable), getHistType(kPosDauTpcNSigmaEl, HistTable), {specs.at(kPosDauTpcNSigmaEl)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kNegDauTpcNSigmaEl, HistTable), getHistDesc(kNegDauTpcNSigmaEl, HistTable), getHistType(kNegDauTpcNSigmaEl, HistTable), {specs.at(kNegDauTpcNSigmaEl)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPosDauPt, HistTable), getHistDesc(kPosDauPt, HistTable), getHistType(kPosDauPt, HistTable), {specs.at(kPosDauPt)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kNegDauPt, HistTable), getHistDesc(kNegDauPt, HistTable), getHistType(kNegDauPt, HistTable), {specs.at(kNegDauPt)}); + + if (mPlot2d) { + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsEta, HistTable), getHistDesc(kPtVsEta, HistTable), getHistType(kPtVsEta, HistTable), {specs.at(kPtVsEta)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsPhi, HistTable), getHistDesc(kPtVsPhi, HistTable), getHistType(kPtVsPhi, HistTable), {specs.at(kPtVsPhi)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPhiVsEta, HistTable), getHistDesc(kPhiVsEta, HistTable), getHistType(kPhiVsEta, HistTable), {specs.at(kPhiVsEta)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsCosPa, HistTable), getHistDesc(kPtVsCosPa, HistTable), getHistType(kPtVsCosPa, HistTable), {specs.at(kPtVsCosPa)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPosDauVsNegDauTpcNSigmaEl, HistTable), getHistDesc(kPosDauVsNegDauTpcNSigmaEl, HistTable), getHistType(kPosDauVsNegDauTpcNSigmaEl, HistTable), {specs.at(kPosDauVsNegDauTpcNSigmaEl)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPosDauTpcSignalVsP, HistTable), getHistDesc(kPosDauTpcSignalVsP, HistTable), getHistType(kPosDauTpcSignalVsP, HistTable), {specs.at(kPosDauTpcSignalVsP)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kNegDauTpcSignalVsP, HistTable), getHistDesc(kNegDauTpcSignalVsP, HistTable), getHistType(kNegDauTpcSignalVsP, HistTable), {specs.at(kNegDauTpcSignalVsP)}); + } + } + + template + void fillAnalysis(T const& photon) + { + mHistogramRegistry->fill(HIST(Prefix) + HIST(AnalysisDir) + HIST(getHistName(kPt, HistTable)), photon.pt()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(AnalysisDir) + HIST(getHistName(kEta, HistTable)), photon.eta()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(AnalysisDir) + HIST(getHistName(kPhi, HistTable)), photon.phi()); + } + + template + void fillQa(T const& photon) + { + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kCosPa, HistTable)), photon.cosPa()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPa, HistTable)), photon.pa()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kDcaToPvXY, HistTable)), photon.dcaToPvXY()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kDcaToPvZ, HistTable)), photon.dcaToPvZ()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kChi2Ndf, HistTable)), photon.chi2Ndf()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kV0Radius, HistTable)), photon.v0Radius()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kDecayVtxX, HistTable)), photon.decayVtxX()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kDecayVtxY, HistTable)), photon.decayVtxY()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kDecayVtxZ, HistTable)), photon.decayVtxZ()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kDecayVtx, HistTable)), photon.decayVtx()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPosDauTpcNSigmaEl, HistTable)), photon.posDauTpcNSigmaEl()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kNegDauTpcNSigmaEl, HistTable)), photon.negDauTpcNSigmaEl()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPosDauPt, HistTable)), photon.posDauPt()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kNegDauPt, HistTable)), photon.negDauPt()); + + if (mPlot2d) { + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsEta, HistTable)), photon.pt(), photon.eta()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsPhi, HistTable)), photon.pt(), photon.phi()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPhiVsEta, HistTable)), photon.phi(), photon.eta()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsCosPa, HistTable)), photon.pt(), photon.cosPa()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPosDauVsNegDauTpcNSigmaEl, HistTable)), photon.posDauTpcNSigmaEl(), photon.negDauTpcNSigmaEl()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPosDauTpcSignalVsP, HistTable)), photon.posDauTpcInnerParam(), photon.posDauTpcSignal()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kNegDauTpcSignalVsP, HistTable)), photon.negDauTpcInnerParam(), photon.negDauTpcSignal()); + } + } + + o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; + bool mPlot2d = false; +}; +} // namespace o2::analysis::femto::photonhistmanager +#endif // PWGCF_FEMTO_CORE_PHOTONHISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/selectionContainer.h b/PWGCF/Femto/Core/selectionContainer.h index cf3b7da0d11..0017215b718 100644 --- a/PWGCF/Femto/Core/selectionContainer.h +++ b/PWGCF/Femto/Core/selectionContainer.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -50,7 +51,8 @@ enum LimitType { kUpperLimit, ///< simple upper limit, e.g. p_T < 1 G kAbsLowerFunctionLimit, ///< lower limit on the absolute value given by a function, e.g. |DCA_xy| > f(pt) kEqual, ///< value must equal a fixed threshold, e.g. sign == 1 kEqualArray, ///< each element of a value array must equal the corresponding threshold - kRange, ///< value must fall within [lower, upper]; either bound can be omitted (open interval) + kInsideRange, ///< value must fall within [lower, upper]; either bound can be omitted (open interval) + kOutsideRange, ///< value must fall outside [lower, upper]; fully-open range (both bounds omitted) disables the cut kLimitTypeLast }; @@ -65,7 +67,8 @@ inline const std::unordered_map limitTypeAsStrings = { {kAbsLowerFunctionLimit, "Absolute Lower Function Limit"}, {kEqual, "Equal"}, {kEqualArray, "EqualArray"}, - {kRange, "Range"}, + {kInsideRange, "Inside Range"}, + {kOutsideRange, "Outside Range"}, {kLimitTypeLast, "Last Limit Type"}, }; @@ -120,10 +123,10 @@ class SelectionContainer mIsOptionalCut(isOptionalCut) { if (mSelectionValues.size() > sizeof(BitmaskType) * CHAR_BIT) { - LOG(fatal) << "Too many selections for single a observable. Limit is " << sizeof(BitmaskType) * CHAR_BIT; + LOG(fatal) << "Selection '" << mSelectionName << "': too many selections for a single observable. Limit is " << sizeof(BitmaskType) * CHAR_BIT; } if (isMinimalCut && isOptionalCut) { - LOG(fatal) << "A selection cannot be both minimal and optional"; + LOG(fatal) << "Selection '" << mSelectionName << "': a selection cannot be both minimal and optional"; } // values for selection are not necessarily ordered correctly sortSelections(); @@ -131,34 +134,46 @@ class SelectionContainer /// \brief Constructor for range-based selections defined as "lower;upper" strings. /// Either bound may be omitted to represent an open interval, e.g. ";1.0" means value < 1.0. + /// A fully-open range (both bounds omitted, i.e. ";") is exempt: for kInsideRange it means + /// "no constraint" (every value is inside); for kOutsideRange it means "no rejection" (every + /// value counts as outside) -- the natural reading breaks down for kOutsideRange otherwise, + /// since an infinitely wide excluded region would reject everything instead of nothing. /// \param selectionName Name of the observable this container manages. /// \param rangeStrings Vector of range strings, each of the form "lower;upper". /// \param skipMostPermissiveBit Whether to skip the most permissive threshold when assembling the bitmask. /// \param isMinimalCut Whether this selection is mandatory (candidate is rejected if it fails). /// \param isOptionalCut Whether this selection is optional (candidate is accepted if any optional cut passes). + /// \param limitType Either kInsideRange (value must fall inside the interval) or kOutsideRange + /// (value must fall outside it, e.g. rejecting a competing mass hypothesis). SelectionContainer(std::string const& selectionName, std::vector const& rangeStrings, bool skipMostPermissiveBit, bool isMinimalCut, - bool isOptionalCut) + bool isOptionalCut, + limits::LimitType limitType = limits::kInsideRange) : mSelectionName(selectionName), - mLimitType(limits::kRange), + mLimitType(limitType), mSkipMostPermissiveBit(skipMostPermissiveBit), mIsMinimalCut(isMinimalCut), mIsOptionalCut(isOptionalCut) { + if (mLimitType != limits::kInsideRange && mLimitType != limits::kOutsideRange) { + LOG(fatal) << "Selection '" << mSelectionName << "': range-string selections only support kInsideRange or kOutsideRange"; + } if (rangeStrings.size() > sizeof(BitmaskType) * CHAR_BIT) { - LOG(fatal) << "Too many selections for a single observable. Limit is " << sizeof(BitmaskType) * CHAR_BIT; + LOG(fatal) << "Selection '" << mSelectionName << "': too many selections for a single observable. Limit is " << sizeof(BitmaskType) * CHAR_BIT; } if (isMinimalCut && isOptionalCut) { - LOG(fatal) << "A selection cannot be both minimal and optional"; + LOG(fatal) << "Selection '" << mSelectionName << "': a selection cannot be both minimal and optional"; } for (auto const& rangeStr : rangeStrings) { T lower, upper; parseRangeString(rangeStr, lower, upper); mSelectionRanges.emplace_back(lower, upper); } - // ranges are sorted by their lenghts, i.e. from widest range to tightest range + // for kInsideRange, ranges are sorted from widest (most permissive) to tightest; for + // kOutsideRange it's the opposite -- the narrowest excluded region is the most permissive + // (fewer values get rejected) // in principle the ranges do not have to include each other, exepct this is configured as minimal cut, check is added at the end // to cover both cases, this also means we always check all ranges, when assembling the bit mask sortSelections(); @@ -167,15 +182,25 @@ class SelectionContainer mSelectionValues.push_back(mSelectionRanges[i].second - mSelectionRanges[i].first); } - // for minimal range cut, ranges must be strictly nested (each range contains all narrower ones) - // this is required for the early-exit logic in passesAsMinimalCut() to be correct + // for a minimal range cut, ranges must be strictly nested (each range contains all ranges that + // sort after it) -- this is required for the single-bit shortcut in passesAsMinimalCut() to be + // correct. For kInsideRange (sorted widest-first) that means range[i] must contain range[i+1]; + // for kOutsideRange (sorted narrowest-first) it's the reverse: range[i] must be contained in + // range[i+1]. A fully-open ("reject nothing") entry is the empty interval [max, -max], which is + // trivially contained in (or containing, for kInsideRange) anything, so this check already holds + // for it without any special-casing. if (isMinimalCut) { for (std::size_t i = 0; i + 1 < mSelectionRanges.size(); i++) { - if (mSelectionRanges[i].first > mSelectionRanges[i + 1].first || - mSelectionRanges[i].second < mSelectionRanges[i + 1].second) { - LOG(fatal) << "Ranges for minimal cut " << selectionName + bool const nested = (mLimitType == limits::kOutsideRange) + ? (mSelectionRanges[i].first >= mSelectionRanges[i + 1].first && + mSelectionRanges[i].second <= mSelectionRanges[i + 1].second) + : (mSelectionRanges[i].first <= mSelectionRanges[i + 1].first && + mSelectionRanges[i].second >= mSelectionRanges[i + 1].second); + if (!nested) { + LOG(fatal) << "Selection '" << selectionName << "': ranges for minimal cut" << " are not nested. Range [" << mSelectionRanges[i].first << ";" << mSelectionRanges[i].second - << "] does not contain [" << mSelectionRanges[i + 1].first << ";" << mSelectionRanges[i + 1].second << "]"; + << "] is not " << (mLimitType == limits::kOutsideRange ? "contained in" : "containing") + << " [" << mSelectionRanges[i + 1].first << ";" << mSelectionRanges[i + 1].second << "]"; } } } @@ -205,10 +230,10 @@ class SelectionContainer mIsOptionalCut(isOptionalCut) { if (functions.size() > sizeof(BitmaskType) * CHAR_BIT) { - LOG(fatal) << "Too many selections for single a observable. Limit is " << sizeof(BitmaskType) * CHAR_BIT; + LOG(fatal) << "Selection '" << mSelectionName << "': too many selections for a single observable. Limit is " << sizeof(BitmaskType) * CHAR_BIT; } if (isMinimalCut && isOptionalCut) { - LOG(fatal) << "A selection cannot be both minimal and optional"; + LOG(fatal) << "Selection '" << mSelectionName << "': a selection cannot be both minimal and optional"; } for (std::size_t i = 0; i < functions.size(); i++) { mSelectionFunctions.emplace_back((mSelectionName + std::to_string(i)).c_str(), functions.at(i).c_str(), lowerLimit, upperLimit); @@ -231,11 +256,18 @@ class SelectionContainer // note: threshold values may be reordered by sortSelections() or sortFunctions(), // so comments must be provided in the already-sorted order if (comments.size() != getNSelections()) { - LOG(fatal) << "Number of comments and number of selections are inconsistent"; + LOG(fatal) << "Selection '" << mSelectionName << "': number of comments and number of selections are inconsistent"; } mComments = comments; } + /// \brief Attach the same comment to every threshold of this selection. + /// \param comment Comment string applied to all thresholds. + void addComments(std::string const& comment) + { + mComments = std::vector(getNSelections(), comment); + } + /// \brief Get comments attached to the selection thresholds. /// \return Vector of comment strings. [[nodiscard]] std::string getComment(int selectionIndex) const @@ -311,7 +343,7 @@ class SelectionContainer } } break; - case (limits::kRange): + case (limits::kInsideRange): // ranges are sorted widest-first but a narrower range passing does not imply a wider one fails (no check on boundries), // so all ranges must be checked explicitly — no early exit for (std::size_t i = 0; i < mSelectionRanges.size(); i++) { @@ -320,6 +352,18 @@ class SelectionContainer } } break; + case (limits::kOutsideRange): + // ranges are sorted narrowest-first (most permissive -- smallest excluded region); same + // no-early-exit reasoning as kInsideRange applies, just with the pass condition inverted. + // A fully-open range string (";") is parsed as the empty interval [max, -max] (see + // parseRangeString()), so this plain check already evaluates true for every value -- no + // special-casing needed here to express "reject nothing". + for (std::size_t i = 0; i < mSelectionRanges.size(); i++) { + if (value < mSelectionRanges.at(i).first || value > mSelectionRanges.at(i).second) { + mBitmask.set(i); + } + } + break; case (limits::kEqual): // kEqual has no natural ordering, so all thresholds must be checked and we cannot bail early for (std::size_t i = 0; i < mSelectionValues.size(); i++) { @@ -339,7 +383,7 @@ class SelectionContainer void evaluate(std::vector const& values) { if (values.size() != mSelectionValues.size()) { - LOG(fatal) << "Wrong number of values have been passed"; + LOG(fatal) << "Selection '" << mSelectionName << "': wrong number of values have been passed"; } // reset the bitmask before evaluating mBitmask.reset(); @@ -378,6 +422,14 @@ class SelectionContainer mBitmask = std::bitset(bitmask); } + /// \brief Retrieve the raw (unshifted) internal bitmask, including the most-permissive-threshold + /// bit even if mSkipMostPermissiveBit is set. Used when two separate evaluate() calls for + /// the same observable need to be combined externally (e.g. logical AND) before the + /// result is written back via setBitmask() -- unlike getBitmask(), this never shifts, so + /// it stays the correct counterpart to setBitmask() (which also never shifts). + /// \return Bitset exactly as currently stored. + [[nodiscard]] std::bitset const& getRawBitmask() const { return mBitmask; } + /// \brief Reset the internal bitmask to zero. void reset() { mBitmask.reset(); } @@ -411,7 +463,7 @@ class SelectionContainer T getLoosestSelection() const { if (mSelectionValues.empty()) { - LOG(fatal) << "No selections configured"; + LOG(fatal) << "Selection '" << mSelectionName << "': no selections configured"; } return mSelectionValues.at(0); } @@ -451,25 +503,65 @@ class SelectionContainer /// \brief Build a histogram bin label string encoding the full configuration of a single threshold. /// \param selectionIndex Index of the threshold within this container. /// \return Encoded label string. + /// \brief Describes an open (sentinel, i.e. omitted-by-the-user) side of a range, if any. + /// For kOutsideRange, a single omitted side flips which half of the "value < lower || + /// value > upper" OR-condition stays active, so the resulting satisfied/reject direction + /// is spelled out explicitly rather than named generically -- omitting the upper bound + /// there means the lower-bound inequality alone remains, i.e. "reject value < lower". + /// \param range Lower/upper bound pair for a kInsideRange/kOutsideRange threshold. + /// \return A human-readable description of the effective cut, or "" if both sides are bounded. + [[nodiscard]] std::string getOpenSideComment(std::pair const& range) const + { + bool const lowerOpen = std::abs(range.first) == std::numeric_limits::max(); + bool const upperOpen = std::abs(range.second) == std::numeric_limits::max(); + if (!lowerOpen && !upperOpen) { + return ""; + } + if (lowerOpen && upperOpen) { + return "no cut"; + } + std::ostringstream oss; + if (mLimitType == limits::kInsideRange) { + oss << "accept value " << (upperOpen ? ">= " : "<= ") << (upperOpen ? range.first : range.second); + } else { + oss << "reject value " << (upperOpen ? "< " : "> ") << (upperOpen ? range.first : range.second); + } + return oss.str(); + } + [[nodiscard]] std::string getBinLabel(int selectionIndex) const { std::ostringstream oss; // Determine value string std::string valueStr; + std::string autoComment; - if (mLimitType == limits::kRange) { - // Print actual lower;upper interval + if (mLimitType == limits::kInsideRange || mLimitType == limits::kOutsideRange) { + // Print the actual lower;upper interval -- kept as real, parseable numbers (not a word like + // "no cut") since this Value field is read by cutculator.py; an open side (lower and/or + // upper bound left at the sentinel, i.e. omitted by the user) is instead flagged via the + // Comment field below. const auto& range = mSelectionRanges.at(selectionIndex); std::ostringstream rangeStream; rangeStream << range.first << RangeDelimiter << range.second; valueStr = rangeStream.str(); + autoComment = getOpenSideComment(range); } else if (mSelectionFunctions.empty()) { valueStr = std::to_string(mSelectionValues.at(selectionIndex)); } else { valueStr = mSelectionFunctions.at(selectionIndex).GetExpFormula().Data(); } + // combine rather than override -- e.g. TOF PID selections attach a "minMomTofX = ..." comment + // to every threshold via addComments(), independent of whether a given threshold's range + // also happens to be open-sided + std::string const userComment = mComments.empty() ? "" : mComments.at(selectionIndex); + std::string comment = userComment; + if (!autoComment.empty()) { + comment = userComment.empty() ? autoComment : autoComment + "; " + userComment; + } + oss << "SelectionName" << ValueDelimiter << mSelectionName << SectionDelimiter << "LimitType" << ValueDelimiter << getLimitTypeAsString() << SectionDelimiter << "MinimalCut" << ValueDelimiter << (mIsMinimalCut ? "1" : "0") << SectionDelimiter @@ -479,7 +571,7 @@ class SelectionContainer << "Offset" << ValueDelimiter << mOffset << SectionDelimiter << "Value" << ValueDelimiter << valueStr << SectionDelimiter << "BitPosition" << ValueDelimiter << (mSkipMostPermissiveBit ? (selectionIndex == 0 ? NoValue : std::to_string(mOffset + selectionIndex - 1)) : std::to_string(mOffset + selectionIndex)) << SectionDelimiter - << "Comment" << ValueDelimiter << (mComments.empty() ? NoValue : mComments.at(selectionIndex)); + << "Comment" << ValueDelimiter << (comment.empty() ? NoValue : comment); return oss.str(); } @@ -492,8 +584,16 @@ class SelectionContainer return std::string(mSelectionFunctions.at(selectionIndex).GetExpFormula().Data()); } if (!mSelectionRanges.empty()) { + const auto& range = mSelectionRanges.at(selectionIndex); + bool const lowerOpen = std::abs(range.first) == std::numeric_limits::max(); + bool const upperOpen = std::abs(range.second) == std::numeric_limits::max(); + if (lowerOpen && upperOpen) { + return std::string{"no cut"}; + } std::ostringstream oss; - oss << mSelectionRanges.at(selectionIndex).first << ";" << mSelectionRanges.at(selectionIndex).second; + oss << (lowerOpen ? "-inf" : std::to_string(range.first).c_str()) + << ";" + << (upperOpen ? "+inf" : std::to_string(range.second).c_str()); return oss.str(); } return std::to_string(mSelectionValues.at(selectionIndex)); @@ -506,7 +606,7 @@ class SelectionContainer [[nodiscard]] int getBitPosition(int selectionIndex) const { if (selectionIndex == 0 && mSkipMostPermissiveBit) { - LOG(fatal) << "Trying to accessed the bit position of a skipped selection. Breaking..."; + LOG(fatal) << "Selection '" << mSelectionName << "': trying to access the bit position of a skipped selection. Breaking..."; return -1; } if (mSkipMostPermissiveBit) { @@ -552,13 +652,23 @@ class SelectionContainer case (limits::kAbsLowerLimit): std::sort(mSelectionValues.begin(), mSelectionValues.end(), [](T a, T b) { return a < b; }); break; - case (limits::kRange): + case (limits::kInsideRange): // sort by range width descending so the most permissive (widest) range comes first std::sort(mSelectionRanges.begin(), mSelectionRanges.end(), [](std::pair const& a, std::pair const& b) { return (a.second - a.first) > (b.second - b.first); }); break; + case (limits::kOutsideRange): + // opposite of kInsideRange: the most permissive (most likely to be satisfied) exclusion is + // the narrowest one, since it rejects the fewest values. A fully-open ("reject nothing") + // range parses to the empty interval [max, -max] (see parseRangeString()), whose "width" is + // the most negative possible value, so it naturally sorts first without any special-casing. + std::sort(mSelectionRanges.begin(), mSelectionRanges.end(), + [](std::pair const& a, std::pair const& b) { + return (a.second - a.first) < (b.second - b.first); + }); + break; default: break; } @@ -584,7 +694,12 @@ class SelectionContainer /// \brief Parse a range string of the form "lower;upper" into a lower and upper bound. /// Either bound may be omitted (e.g. ";1.0" or "0.5;"), in which case - /// -/+ numeric_limits::max() is used respectively. + /// -/+ numeric_limits::max() is used respectively. For kOutsideRange, a fully-open string + /// (";", both bounds omitted) is the one exception: it is parsed as the empty interval + /// [max, -max] instead of [-max, max] -- every value then trivially fails the normal + /// "value >= lower && value <= upper" check, i.e. every value counts as outside, which is + /// what "reject nothing" needs. [-max, max] would instead mean every value is inside, + /// i.e. nothing is ever outside, which would reject everything -- the opposite intent. /// \param rangeStr Input string to parse. /// \param lower Output lower bound. /// \param upper Output upper bound. @@ -592,23 +707,60 @@ class SelectionContainer { auto pos = rangeStr.find(';'); if (pos == std::string::npos) { - LOG(fatal) << "Range string '" << rangeStr << "' is missing ';' separator. Expected format: 'lower;upper'"; + LOG(fatal) << "Selection '" << mSelectionName << "': range string '" << rangeStr << "' is missing ';' separator. Expected format: 'lower;upper'"; + } + if (rangeStr.find(';', pos + 1) != std::string::npos) { + LOG(fatal) << "Selection '" << mSelectionName << "': range string '" << rangeStr << "' has more than one ';' separator. Expected format: 'lower;upper'"; } std::string lowerStr = rangeStr.substr(0, pos); std::string upperStr = rangeStr.substr(pos + 1); - lower = lowerStr.empty() ? -std::numeric_limits::max() : static_cast(std::stod(lowerStr)); - upper = upperStr.empty() ? std::numeric_limits::max() : static_cast(std::stod(upperStr)); + if (lowerStr.empty() && upperStr.empty() && mLimitType == limits::kOutsideRange) { + // kOutsideRange's "value < lower || value > upper" is the negation of kInsideRange's + // "value >= lower && value <= upper" -- substituting the same [-max, +max] that makes the + // latter trivially true for every value would make the former trivially FALSE for every + // value instead (nothing is < -max or > +max), i.e. "reject everything" rather than the + // intended "reject nothing". The empty interval [max, -max] is the encoding that keeps the + // disjunction trivially true for every value instead, giving the correct "exempt" meaning. + lower = std::numeric_limits::max(); + upper = -std::numeric_limits::max(); + return; + } + + lower = lowerStr.empty() ? -std::numeric_limits::max() : parseBound(rangeStr, lowerStr); + upper = upperStr.empty() ? std::numeric_limits::max() : parseBound(rangeStr, upperStr); if (lower >= upper) { - LOG(fatal) << "Range string '" << rangeStr << "' has lower bound >= upper bound"; + LOG(fatal) << "Selection '" << mSelectionName << "': range string '" << rangeStr << "' has lower bound >= upper bound"; } } + /// \brief Parse a single range bound and make sure the whole string was consumed, so that + /// malformed input such as "3;5" (extra ';') or "-3,5" (comma used as decimal + /// separator) is rejected instead of silently truncated by std::stod (which happily + /// parses just the numeric prefix "3" or "-3" and ignores the rest). + /// \param rangeStr Full "lower;upper" string, only used for the error message. + /// \param boundStr The individual bound substring (lower or upper) to parse. + T parseBound(std::string const& rangeStr, std::string const& boundStr) const + { + std::size_t idx = 0; + double value = 0.; + try { + value = std::stod(boundStr, &idx); + } catch (std::exception const& e) { + LOG(fatal) << "Selection '" << mSelectionName << "': range string '" << rangeStr << "' contains bound '" << boundStr << "' that is not a valid number (" << e.what() << ")"; + } + if (idx != boundStr.size()) { + LOG(fatal) << "Selection '" << mSelectionName << "': range string '" << rangeStr << "' contains bound '" << boundStr + << "' with trailing characters ('" << boundStr.substr(idx) << "') that could not be parsed"; + } + return static_cast(value); + } + std::string mSelectionName; std::vector mSelectionValues = {}; ///< Threshold values, sorted from most permissive to most restrictive std::vector mSelectionFunctions; ///< TF1 threshold functions (empty for static selections) - std::vector> mSelectionRanges = {}; ///< Lower and upper bounds for kRange selections, one pair per threshold + std::vector> mSelectionRanges = {}; ///< Lower and upper bounds for kInsideRange/kOutsideRange selections, one pair per threshold limits::LimitType mLimitType = limits::kLimitTypeLast; ///< Comparison type applied during evaluation bool mSkipMostPermissiveBit = false; ///< If true, the most permissive threshold does not occupy a bit in the global bitmask bool mIsMinimalCut = false; ///< If true, this selection is mandatory; failing it rejects the candidate diff --git a/PWGCF/Femto/Core/trackBuilder.h b/PWGCF/Femto/Core/trackBuilder.h index e499cd26381..3e703ddd94e 100644 --- a/PWGCF/Femto/Core/trackBuilder.h +++ b/PWGCF/Femto/Core/trackBuilder.h @@ -27,8 +27,12 @@ #include #include #include +#include #include +#include + +#include #include #include #include @@ -59,83 +63,83 @@ struct ConfTrackBits : o2::framework::ConfigurableGroup { o2::framework::Configurable> tpcSharedClusterFractionMax{"tpcSharedClusterFractionMax", {1.f}, "Maximum fraction of shared clusters in TPC"}; o2::framework::Configurable> itsClustersMin{"itsClustersMin", {5.f}, "Minimum number of clusters in ITS"}; o2::framework::Configurable> itsIbClustersMin{"itsIbClustersMin", {3.f}, "Minimum number of clusters in inner barrel (max 3) of ITS"}; - 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> dcaxyMax{"dcaxyMax", {"0.004 + 0.013*pow(x, -1)"}, "Maximum |dca_xy| as a function of pT. Has to be a valid TFormula, 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 TFormula, where x=pt"}; o2::framework::Configurable> itsChi2Max{"itsChi2Max", {}, "Maximum ITS chi2 per cluster"}; o2::framework::Configurable> tpcChi2Max{"tpcChi2Max", {}, "Maximum TPC chi2 per cluster"}; // Electron PID cuts - o2::framework::Configurable requirePidElectron{"requirePidElectron", false, "Make election PID optional"}; + o2::framework::Configurable requirePidElectron{"requirePidElectron", false, "Make electron PID optional"}; o2::framework::Configurable minMomTofElectron{"minMomTofElectron", 0.3, "Minimum momentum to required TOF PID for Electron"}; o2::framework::Configurable> itsElectron{"itsElectron", {}, "Ranges LowerLimit;UpperLimit for nsigma_ITS for Electron PID"}; o2::framework::Configurable> tpcElectron{"tpcElectron", {}, "Ranges LowerLimit;UpperLimit for nsigma_TPC for Electron PID"}; - o2::framework::Configurable> tofElectron{"tofElectron", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Electron PID"}; + o2::framework::Configurable> tofElectron{"tofElectron", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Electron PID. Momentum threshold above which TOF is required: minMomTofElectron"}; o2::framework::Configurable> tpcitsElectron{"tpcitsElectron", {}, "Maximum nsigma_TPCITS for Electron PID"}; - o2::framework::Configurable> tpctofElectron{"tpctofElectron", {}, "Maximum nsigma_TPCTOF for Electron PID"}; + o2::framework::Configurable> tpctofElectron{"tpctofElectron", {}, "Maximum nsigma_TPCTOF for Electron PID. Momentum threshold above which TOF is required: minMomTofElectron"}; // Pion PID cuts - o2::framework::Configurable requirePidPion{"requirePidPion", false, "Make election PID optional"}; + o2::framework::Configurable requirePidPion{"requirePidPion", false, "Make pion PID optional"}; o2::framework::Configurable minMomTofPion{"minMomTofPion", 0.5, "Minimum momentum to required TOF PID for Pion"}; o2::framework::Configurable> itsPion{"itsPion", {}, "Ranges LowerLimit;UpperLimit for nsigma_ITS for Pion PID"}; o2::framework::Configurable> tpcPion{"tpcPion", {}, "Ranges LowerLimit;UpperLimit for nsigma_TPC for Pion PID"}; - o2::framework::Configurable> tofPion{"tofPion", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Pion PID"}; + o2::framework::Configurable> tofPion{"tofPion", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Pion PID. Momentum threshold above which TOF is required: minMomTofPion"}; o2::framework::Configurable> tpcitsPion{"tpcitsPion", {}, "Maximum nsigma_TPCITS for Pion PID"}; - o2::framework::Configurable> tpctofPion{"tpctofPion", {}, "Maximum nsigma_TPCTOF for Pion PID"}; + o2::framework::Configurable> tpctofPion{"tpctofPion", {}, "Maximum nsigma_TPCTOF for Pion PID. Momentum threshold above which TOF is required: minMomTofPion"}; // Kaon PID cuts - o2::framework::Configurable requirePidKaon{"requirePidKaon", false, "Make election PID optional"}; + o2::framework::Configurable requirePidKaon{"requirePidKaon", false, "Make kaon PID optional"}; o2::framework::Configurable minMomTofKaon{"minMomTofKaon", 0.4, "Minimum momentum to required TOF PID for Kaon"}; o2::framework::Configurable> itsKaon{"itsKaon", {}, "Ranges LowerLimit;UpperLimit for nsigma_ITS for Kaon PID"}; o2::framework::Configurable> tpcKaon{"tpcKaon", {}, "Ranges LowerLimit;UpperLimit for nsigma_TPC for Kaon PID"}; - o2::framework::Configurable> tofKaon{"tofKaon", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Kaon PID"}; + o2::framework::Configurable> tofKaon{"tofKaon", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Kaon PID. Momentum threshold above which TOF is required: minMomTofKaon"}; o2::framework::Configurable> tpcitsKaon{"tpcitsKaon", {}, "Maximum nsigma_TPCITS for Kaon PID"}; - o2::framework::Configurable> tpctofKaon{"tpctofKaon", {}, "Maximum nsigma_TPCTOF for Kaon PID"}; + o2::framework::Configurable> tpctofKaon{"tpctofKaon", {}, "Maximum nsigma_TPCTOF for Kaon PID. Momentum threshold above which TOF is required: minMomTofKaon"}; // Proton PID cuts - o2::framework::Configurable requirePidProton{"requirePidProton", true, "Make election PID optional"}; + o2::framework::Configurable requirePidProton{"requirePidProton", true, "Make proton PID optional"}; o2::framework::Configurable minMomTofProton{"minMomTofProton", 0.75, "Minimum momentum to required TOF PID for Proton"}; o2::framework::Configurable> itsProton{"itsProton", {}, "Ranges LowerLimit;UpperLimit for nsigma_ITS for Proton PID"}; o2::framework::Configurable> tpcProton{"tpcProton", {}, "Ranges LowerLimit;UpperLimit for nsigma_TPC for Proton PID"}; - o2::framework::Configurable> tofProton{"tofProton", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Proton PID"}; + o2::framework::Configurable> tofProton{"tofProton", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Proton PID. Momentum threshold above which TOF is required: minMomTofProton"}; o2::framework::Configurable> tpcitsProton{"tpcitsProton", {}, "Maximum nsigma_TPCITS for Proton PID"}; - o2::framework::Configurable> tpctofProton{"tpctofProton", {}, "Maximum nsigma_TPCTOF for Proton PID"}; + o2::framework::Configurable> tpctofProton{"tpctofProton", {}, "Maximum nsigma_TPCTOF for Proton PID. Momentum threshold above which TOF is required: minMomTofProton"}; // Deuteron PID cuts - o2::framework::Configurable requirePidDeuteron{"requirePidDeuteron", false, "Make election PID optional"}; + o2::framework::Configurable requirePidDeuteron{"requirePidDeuteron", false, "Make deuteron PID optional"}; o2::framework::Configurable minMomTofDeuteron{"minMomTofDeuteron", 1.2, "Minimum momentum to required TOF PID for Deuteron"}; o2::framework::Configurable> itsDeuteron{"itsDeuteron", {}, "Ranges LowerLimit;UpperLimit for nsigma_ITS for Deuteron PID"}; o2::framework::Configurable> tpcDeuteron{"tpcDeuteron", {}, "Ranges LowerLimit;UpperLimit for nsigma_TPC for Deuteron PID"}; - o2::framework::Configurable> tofDeuteron{"tofDeuteron", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Deuteron PID"}; + o2::framework::Configurable> tofDeuteron{"tofDeuteron", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Deuteron PID. Momentum threshold above which TOF is required: minMomTofDeuteron"}; o2::framework::Configurable> tpcitsDeuteron{"tpcitsDeuteron", {}, "Maximum nsigma_TPCITS for Deuteron PID"}; - o2::framework::Configurable> tpctofDeuteron{"tpctofDeuteron", {}, "Maximum nsigma_TPCTOF for Deuteron PID"}; + o2::framework::Configurable> tpctofDeuteron{"tpctofDeuteron", {}, "Maximum nsigma_TPCTOF for Deuteron PID. Momentum threshold above which TOF is required: minMomTofDeuteron"}; // Triton PID cuts - o2::framework::Configurable requirePidTriton{"requirePidTriton", false, "Make election PID optional"}; + o2::framework::Configurable requirePidTriton{"requirePidTriton", false, "Make triton PID optional"}; o2::framework::Configurable minMomTofTriton{"minMomTofTriton", 1.4, "Minimum momentum to required TOF PID for Triton"}; o2::framework::Configurable> itsTriton{"itsTriton", {}, "Ranges LowerLimit;UpperLimit for nsigma_ITS for Triton PID"}; o2::framework::Configurable> tpcTriton{"tpcTriton", {}, "Ranges LowerLimit;UpperLimit for nsigma_TPC for Triton PID"}; - o2::framework::Configurable> tofTriton{"tofTriton", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Triton PID"}; + o2::framework::Configurable> tofTriton{"tofTriton", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Triton PID. Momentum threshold above which TOF is required: minMomTofTriton"}; o2::framework::Configurable> tpcitsTriton{"tpcitsTriton", {}, "Maximum nsigma_TPCITS for Triton PID"}; - o2::framework::Configurable> tpctofTriton{"tpctofTriton", {}, "Maximum nsigma_TPCTOF for Triton PID"}; + o2::framework::Configurable> tpctofTriton{"tpctofTriton", {}, "Maximum nsigma_TPCTOF for Triton PID. Momentum threshold above which TOF is required: minMomTofTriton"}; // Helium PID cuts - o2::framework::Configurable requirePidHelium{"requirePidHelium", false, "Make election PID optional"}; + o2::framework::Configurable requirePidHelium{"requirePidHelium", false, "Make helium PID optional"}; o2::framework::Configurable minMomTofHelium{"minMomTofHelium", 1.6, "Minimum momentum to required TOF PID for Helium"}; o2::framework::Configurable> itsHelium{"itsHelium", {}, "Ranges LowerLimit;UpperLimit for nsigma_ITS for Helium PID"}; o2::framework::Configurable> tpcHelium{"tpcHelium", {}, "Ranges LowerLimit;UpperLimit for nsigma_TPC for Helium PID"}; - o2::framework::Configurable> tofHelium{"tofHelium", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Helium PID"}; + o2::framework::Configurable> tofHelium{"tofHelium", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Helium PID. Momentum threshold above which TOF is required: minMomTofHelium"}; o2::framework::Configurable> tpcitsHelium{"tpcitsHelium", {}, "Maximum nsigma_TPCITS for Helium PID"}; - o2::framework::Configurable> tpctofHelium{"tpctofHelium", {}, "Maximum nsigma_TPCTOF for Helium PID"}; + o2::framework::Configurable> tpctofHelium{"tpctofHelium", {}, "Maximum nsigma_TPCTOF for Helium PID. Momentum threshold above which TOF is required: minMomTofHelium"}; }; // define the template structure for TrackSelection -template +template struct ConfTrackSelection : public o2::framework::ConfigurableGroup { std::string prefix = Prefix; // Unique prefix based on the template argument // configuration parameters o2::framework::Configurable pdgCodeAbs{"pdgCodeAbs", 2212, "Absolute value of PDG code. Set sign of charge to -1 for antiparticle."}; o2::framework::Configurable chargeAbs{"chargeAbs", 1, "Absolute value of charge (e.g. 1 for most tracks, 2 for He3)"}; - o2::framework::Configurable chargeSign{"chargeSign", 1, "Track charge sign: +1 for positive, -1 for negative, 0 for both"}; + o2::framework::Configurable chargeSign{"chargeSign", DefaultChargeSign, "Track charge sign: +1 for positive, -1 for negative, 0 for both"}; // filters for kinematics o2::framework::Configurable ptMin{"ptMin", 0.0f, "Minimum pT (GeV/c)"}; o2::framework::Configurable ptMax{"ptMax", 6.f, "Maximum pT (GeV/c)"}; @@ -153,6 +157,8 @@ struct ConfTrackSelection : public o2::framework::ConfigurableGroup { o2::framework::Configurable rejectionMaskHighMomentum{"rejectionMaskHighMomentum", 0ul, "Bitmask for rejections above momentum threshold"}; // momentum threshold for PID usage o2::framework::Configurable pidThres{"pidThres", 1.2f, "Momentum threshold for using TPCTOF/TOF pid for tracks with large momentum (GeV/c)"}; + // which species slot this selection corresponds to in the packed purity tables (FitVars only) + o2::framework::Configurable puritySlotMask{"puritySlotMask", 1, "Bitmask (bit N = slot N) selecting which purity slot to plot, read off hPuritySlotAssignment in the producer output."}; }; // Define unique prefixes as constexpr string literals @@ -160,12 +166,6 @@ constexpr const char PrefixTrackSelection1[] = "TrackSelection1"; constexpr const char PrefixTrackSelection2[] = "TrackSelection2"; constexpr const char PrefixTrackSelection3[] = "TrackSelection3"; -// for resonances builder -constexpr const char PrefixPionPlus[] = "PionPlusSelection"; -constexpr const char PrefixPionMinus[] = "PionMinusSelection"; -constexpr const char PrefixKaonPlus[] = "KaonPlusSelection"; -constexpr const char PrefixKaonMinus[] = "KaonMinusSelection"; - // for dN/deta: tracks with TPC (ITS + TPC quality bits) and tracks without TPC (ITS quality bits only) // set maskLowMomentum = maskHighMomentum (quality bits only), chargeSign = 0 and the eta/pT range of the measurement constexpr const char PrefixTrackSelectionDndetaGlobal[] = "TrackSelectionDndetaGlobal"; // Global = ITS + TPC @@ -176,13 +176,45 @@ using ConfTrackSelection1 = ConfTrackSelection; using ConfTrackSelection2 = ConfTrackSelection; using ConfTrackSelection3 = ConfTrackSelection; -using ConfPionPlusSelection = ConfTrackSelection; -using ConfPionMinusSelection = ConfTrackSelection; -using ConfKaonPlusSelection = ConfTrackSelection; -using ConfKaonMinusSelection = ConfTrackSelection; +using ConfTrackSelectionDndetaGlobal = ConfTrackSelection; +using ConfTrackSelectionDndetaItsOnly = ConfTrackSelection; + +// Track selection for resonance daughters (pion+/-, kaon+/-). Charge sign and abs charge are fixed +// by the species itself, so they are compile-time constants rather than configurables. No pdg code +// here at all: the daughter mass used to reconstruct the resonance is set directly in the resonance +// builder (e.g. MassPiPlus/MassKMinus), never read from this selection. Used exclusively by the +// resonance builders via MAKE_RESONANCE_DAUGHTER_PARTITION. +template +struct ConfResonanceDaughterSelection : public o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + static const int chargeAbs = 1; + static const int chargeSign = ChargeSign; + // filters for kinematics + o2::framework::Configurable ptMin{"ptMin", 0.0f, "Minimum pT (GeV/c)"}; + o2::framework::Configurable ptMax{"ptMax", 6.f, "Maximum pT (GeV/c)"}; + o2::framework::Configurable etaMin{"etaMin", -0.9f, "Minimum eta"}; + o2::framework::Configurable etaMax{"etaMax", 0.9f, "Maximum eta"}; + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; + // track selection masks + o2::framework::Configurable maskLowMomentum{"maskLowMomentum", 1ul, "Bitmask for selections below momentum threshold"}; + o2::framework::Configurable maskHighMomentum{"maskHighMomentum", 2ul, "Bitmask for selections above momentum threshold"}; + // track rejection masks + o2::framework::Configurable rejectionMaskLowMomentum{"rejectionMaskLowMomentum", 0ul, "Bitmask for rejections below momentum threshold"}; + o2::framework::Configurable rejectionMaskHighMomentum{"rejectionMaskHighMomentum", 0ul, "Bitmask for rejections above momentum threshold"}; + // momentum threshold for PID usage + o2::framework::Configurable pidThres{"pidThres", PidThresDefault, "Momentum threshold for using TPCTOF/TOF pid for tracks with large momentum (GeV/c)"}; +}; + +constexpr const char PrefixPionPlus[] = "PionPlusSelection"; +constexpr const char PrefixPionMinus[] = "PionMinusSelection"; +constexpr const char PrefixKaonPlus[] = "KaonPlusSelection"; +constexpr const char PrefixKaonMinus[] = "KaonMinusSelection"; -using ConfTrackSelectionDndetaGlobal = ConfTrackSelection; -using ConfTrackSelectionDndetaItsOnly = ConfTrackSelection; +using ConfPionPlusSelection = ConfResonanceDaughterSelection; +using ConfPionMinusSelection = ConfResonanceDaughterSelection; +using ConfKaonPlusSelection = ConfResonanceDaughterSelection; +using ConfKaonMinusSelection = ConfResonanceDaughterSelection; /// enum for all track selections enum TrackSels { @@ -200,7 +232,7 @@ enum TrackSels { kTPCchi2Max, ///< Max. TPC chi2 per cluster /// track pid cuts - kItsElectron, ///< ITS Electon PID + kItsElectron, ///< ITS Electron PID kItsPion, ///< ITS Pion PID kItsKaon, ///< ITS Kaon PID kItsProton, ///< ITS Proton PID @@ -208,7 +240,7 @@ enum TrackSels { kItsTriton, ///< ITS Triton PID kItsHelium, ///< ITS He3 PID - kTpcElectron, ///< TPC Electon PID + kTpcElectron, ///< TPC Electron PID kTpcPion, ///< TPC Pion PID kTpcKaon, ///< TPC Kaon PID kTpcProton, ///< TPC Proton PID @@ -216,7 +248,7 @@ enum TrackSels { kTpcTriton, ///< TPC Triton PID kTpcHelium, ///< TPC He3 PID - kTofElectron, ///< TOF Electon PID + kTofElectron, ///< TOF Electron PID kTofPion, ///< TOF Pion PID kTofKaon, ///< TOF Kaon PID kTofProton, ///< TOF Proton PID @@ -224,7 +256,7 @@ enum TrackSels { kTofTriton, ///< TOF Triton PID kTofHelium, ///< TOF He3 PID - kTpcitsElectron, ///< TPC+ITS Electon PID + kTpcitsElectron, ///< TPC+ITS Electron PID kTpcitsPion, ///< TPC+ITS Pion PID kTpcitsKaon, ///< TPC+ITS Kaon PID kTpcitsProton, ///< TPC+ITS Proton PID @@ -232,7 +264,7 @@ enum TrackSels { kTpcitsTriton, ///< TPC+ITS Triton PID kTpcitsHelium, ///< TPC+ITS He3 PID - kTpctofElectron, ///< TPC+TOF Electon PID + kTpctofElectron, ///< TPC+TOF Electron PID kTpctofPion, ///< TPC+TOF Pion PID kTpctofKaon, ///< TPC+TOF Kaon PID kTpctofProton, ///< TPC+TOF Proton PID @@ -289,7 +321,7 @@ const std::unordered_map trackSelectionNames = { {kTpcitsProton, "TPC+ITS Proton PID"}, {kTpcitsDeuteron, "TPC+ITS Deuteron PID"}, {kTpcitsTriton, "TPC+ITS Triton PID"}, - {kTpcitsHelium, "TPC+ITS He PID"}, + {kTpcitsHelium, "TPC+ITS He3 PID"}, {kTpctofElectron, "TPC+TOF Electron PID"}, {kTpctofPion, "TPC+TOF Pion PID"}, @@ -358,56 +390,70 @@ class TrackSelection : public baseselection::BaseSelectionaddSelection(kItsElectron, trackSelectionNames.at(kItsElectron), config.itsElectron.value, false, false, config.requirePidElectron); this->addSelection(kTpcElectron, trackSelectionNames.at(kTpcElectron), config.tpcElectron.value, false, false, config.requirePidElectron); this->addSelection(kTofElectron, trackSelectionNames.at(kTofElectron), config.tofElectron.value, false, false, config.requirePidElectron); + this->addComments(kTofElectron, "minMomTofElectron = " + std::to_string(config.minMomTofElectron.value)); this->addSelection(kTpcitsElectron, trackSelectionNames.at(kTpcitsElectron), config.tpcitsElectron.value, limits::kUpperLimit, false, false, config.requirePidElectron); this->addSelection(kTpctofElectron, trackSelectionNames.at(kTpctofElectron), config.tpctofElectron.value, limits::kUpperLimit, false, false, config.requirePidElectron); + this->addComments(kTpctofElectron, "minMomTofElectron = " + std::to_string(config.minMomTofElectron.value)); mElectronTofThres = config.minMomTofElectron.value; // add selections for Pion pid this->addSelection(kItsPion, trackSelectionNames.at(kItsPion), config.itsPion.value, false, false, config.requirePidPion); this->addSelection(kTpcPion, trackSelectionNames.at(kTpcPion), config.tpcPion.value, false, false, config.requirePidPion); this->addSelection(kTofPion, trackSelectionNames.at(kTofPion), config.tofPion.value, false, false, config.requirePidPion); + this->addComments(kTofPion, "minMomTofPion = " + std::to_string(config.minMomTofPion.value)); this->addSelection(kTpcitsPion, trackSelectionNames.at(kTpcitsPion), config.tpcitsPion.value, limits::kUpperLimit, false, false, config.requirePidPion); this->addSelection(kTpctofPion, trackSelectionNames.at(kTpctofPion), config.tpctofPion.value, limits::kUpperLimit, false, false, config.requirePidPion); + this->addComments(kTpctofPion, "minMomTofPion = " + std::to_string(config.minMomTofPion.value)); mPionTofThres = config.minMomTofPion.value; // add selections for Kaon pid this->addSelection(kItsKaon, trackSelectionNames.at(kItsKaon), config.itsKaon.value, false, false, config.requirePidKaon); this->addSelection(kTpcKaon, trackSelectionNames.at(kTpcKaon), config.tpcKaon.value, false, false, config.requirePidKaon); this->addSelection(kTofKaon, trackSelectionNames.at(kTofKaon), config.tofKaon.value, false, false, config.requirePidKaon); + this->addComments(kTofKaon, "minMomTofKaon = " + std::to_string(config.minMomTofKaon.value)); this->addSelection(kTpcitsKaon, trackSelectionNames.at(kTpcitsKaon), config.tpcitsKaon.value, limits::kUpperLimit, false, false, config.requirePidKaon); this->addSelection(kTpctofKaon, trackSelectionNames.at(kTpctofKaon), config.tpctofKaon.value, limits::kUpperLimit, false, false, config.requirePidKaon); + this->addComments(kTpctofKaon, "minMomTofKaon = " + std::to_string(config.minMomTofKaon.value)); mKaonTofThres = config.minMomTofKaon.value; // add selections for Proton pid this->addSelection(kItsProton, trackSelectionNames.at(kItsProton), config.itsProton.value, false, false, config.requirePidProton); this->addSelection(kTpcProton, trackSelectionNames.at(kTpcProton), config.tpcProton.value, false, false, config.requirePidProton); this->addSelection(kTofProton, trackSelectionNames.at(kTofProton), config.tofProton.value, false, false, config.requirePidProton); + this->addComments(kTofProton, "minMomTofProton = " + std::to_string(config.minMomTofProton.value)); this->addSelection(kTpcitsProton, trackSelectionNames.at(kTpcitsProton), config.tpcitsProton.value, limits::kUpperLimit, false, false, config.requirePidProton); this->addSelection(kTpctofProton, trackSelectionNames.at(kTpctofProton), config.tpctofProton.value, limits::kUpperLimit, false, false, config.requirePidProton); + this->addComments(kTpctofProton, "minMomTofProton = " + std::to_string(config.minMomTofProton.value)); mProtonTofThres = config.minMomTofProton.value; // add selections for Deuteron pid this->addSelection(kItsDeuteron, trackSelectionNames.at(kItsDeuteron), config.itsDeuteron.value, false, false, config.requirePidDeuteron); this->addSelection(kTpcDeuteron, trackSelectionNames.at(kTpcDeuteron), config.tpcDeuteron.value, false, false, config.requirePidDeuteron); this->addSelection(kTofDeuteron, trackSelectionNames.at(kTofDeuteron), config.tofDeuteron.value, false, false, config.requirePidDeuteron); + this->addComments(kTofDeuteron, "minMomTofDeuteron = " + std::to_string(config.minMomTofDeuteron.value)); this->addSelection(kTpcitsDeuteron, trackSelectionNames.at(kTpcitsDeuteron), config.tpcitsDeuteron.value, limits::kUpperLimit, false, false, config.requirePidDeuteron); this->addSelection(kTpctofDeuteron, trackSelectionNames.at(kTpctofDeuteron), config.tpctofDeuteron.value, limits::kUpperLimit, false, false, config.requirePidDeuteron); + this->addComments(kTpctofDeuteron, "minMomTofDeuteron = " + std::to_string(config.minMomTofDeuteron.value)); mDeuteronTofThres = config.minMomTofDeuteron.value; // add selections for Triton pid this->addSelection(kItsTriton, trackSelectionNames.at(kItsTriton), config.itsTriton.value, false, false, config.requirePidTriton); this->addSelection(kTpcTriton, trackSelectionNames.at(kTpcTriton), config.tpcTriton.value, false, false, config.requirePidTriton); this->addSelection(kTofTriton, trackSelectionNames.at(kTofTriton), config.tofTriton.value, false, false, config.requirePidTriton); + this->addComments(kTofTriton, "minMomTofTriton = " + std::to_string(config.minMomTofTriton.value)); this->addSelection(kTpcitsTriton, trackSelectionNames.at(kTpcitsTriton), config.tpcitsTriton.value, limits::kUpperLimit, false, false, config.requirePidTriton); this->addSelection(kTpctofTriton, trackSelectionNames.at(kTpctofTriton), config.tpctofTriton.value, limits::kUpperLimit, false, false, config.requirePidTriton); + this->addComments(kTpctofTriton, "minMomTofTriton = " + std::to_string(config.minMomTofTriton.value)); mTritonTofThres = config.minMomTofTriton.value; // add selections for Helium pid this->addSelection(kItsHelium, trackSelectionNames.at(kItsHelium), config.itsHelium.value, false, false, config.requirePidHelium); this->addSelection(kTpcHelium, trackSelectionNames.at(kTpcHelium), config.tpcHelium.value, false, false, config.requirePidHelium); this->addSelection(kTofHelium, trackSelectionNames.at(kTofHelium), config.tofHelium.value, false, false, config.requirePidHelium); + this->addComments(kTofHelium, "minMomTofHelium = " + std::to_string(config.minMomTofHelium.value)); this->addSelection(kTpcitsHelium, trackSelectionNames.at(kTpcitsHelium), config.tpcitsHelium.value, limits::kUpperLimit, false, false, config.requirePidHelium); this->addSelection(kTpctofHelium, trackSelectionNames.at(kTpctofHelium), config.tpctofHelium.value, limits::kUpperLimit, false, false, config.requirePidHelium); + this->addComments(kTpctofHelium, "minMomTofHelium = " + std::to_string(config.minMomTofHelium.value)); mHeliumTofThres = config.minMomTofHelium.value; this->setupSelectionHistogram(registry); @@ -558,6 +604,9 @@ struct TrackBuilderProducts : o2::framework::ProducesGroup { o2::framework::Produces producedDeuteronPids; o2::framework::Produces producedTritonPids; o2::framework::Produces producedHeliumPids; + o2::framework::Produces producedItsPurity; + o2::framework::Produces producedTpcPurity; + o2::framework::Produces producedTofPurity; }; struct ConfTrackTables : o2::framework::ConfigurableGroup { @@ -575,6 +624,9 @@ struct ConfTrackTables : o2::framework::ConfigurableGroup { o2::framework::Configurable produceDeuteronPids{"produceDeuteronPids", -1, "Produce DeuteronPids (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceTritonPids{"produceTritonPids", -1, "Produce TritonPids (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceHeliumPids{"produceHeliumPids", -1, "Produce HeliumPids (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceItsPurity{"produceItsPurity", -1, "Produce ItsPurity (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceTpcPurity{"produceTpcPurity", -1, "Produce TpcPurity (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceTofPurity{"produceTofPurity", -1, "Produce TofPurity (-1: auto; 0 off; 1 on)"}; }; template @@ -602,6 +654,59 @@ class TrackBuilder mProduceDeuteronPids = utils::enableTable("FDeuteronPids_001", table.produceDeuteronPids.value, initContext); mProduceTritonPids = utils::enableTable("FTritonPids_001", table.produceTritonPids.value, initContext); mProduceHeliumPids = utils::enableTable("FHeliumPids_001", table.produceHeliumPids.value, initContext); + mProduceItsPurity = utils::enableTable("FItsPurity_001", table.produceItsPurity.value, initContext); + mProduceTpcPurity = utils::enableTable("FTpcPurity_001", table.produceTpcPurity.value, initContext); + mProduceTofPurity = utils::enableTable("FTofPurity_001", table.produceTofPurity.value, initContext); + + if (mProduceItsPurity || mProduceTpcPurity || mProduceTofPurity) { + // assign each active species (requirePidXxx == true) to the next free slot (0-3), in + // canonical order (PuritySpeciesName); species with requirePidXxx == false get no slot. + const std::array requirePid = { + config.requirePidElectron.value, config.requirePidPion.value, config.requirePidKaon.value, + config.requirePidProton.value, config.requirePidDeuteron.value, config.requirePidTriton.value, config.requirePidHelium.value}; + + int nextSlot = 0; + for (int i = 0; i < PuritySpeciesLast; i++) { + if (!requirePid[i]) { + continue; + } + if (nextSlot >= utils::PurityNSlots) { + LOG(fatal) << "More than " << utils::PurityNSlots << " species have requirePidXxx enabled, " + << "but the purity tables only have " << utils::PurityNSlots << " slots. " + << "Disable requirePidXxx for species you don't need purity info for. Breaking..."; + } + mPuritySlot[i] = nextSlot; + nextSlot++; + } + + // one bin per species (own SelectionName -> own cutculator group), plus the usual + // trailing "All analyzed"/"All passed" pair that cutculator always skips by position. + // BitPosition___X (unassigned species) is deliberately excluded from cutculator's + // "neutral" category, so nothing is selectable for a species that isn't configured here. + // Picking the one assigned bit for a species gives exactly 1<add("hPuritySlotAssignment", "Purity slot assignment", o2::framework::HistType::kTH1F, {{PuritySpeciesLast + 2, -0.5, PuritySpeciesLast + 2 - 0.5}}); + auto hSlotAssignment = registry->get(HIST("hPuritySlotAssignment")); + for (int i = 0; i < PuritySpeciesLast; i++) { + std::string bitPosition = mPuritySlot[i] >= 0 ? std::to_string(mPuritySlot[i]) : "X"; + std::string value = "Not configured"; + if (mPuritySlot[i] >= 0) { + value = "Slot "; + value += std::to_string(mPuritySlot[i]); + } + std::string label = "SelectionName___"; + label += PuritySpeciesName[i]; + label += ":::MinimalCut___0:::OptionalCut___0:::BitPosition___"; + label += bitPosition; + label += ":::Value___"; + label += value; + hSlotAssignment->GetXaxis()->SetBinLabel(i + 1, label.c_str()); + hSlotAssignment->Fill(i, 1); + } + hSlotAssignment->GetXaxis()->SetBinLabel(PuritySpeciesLast + 1, "All analyzed"); + hSlotAssignment->GetXaxis()->SetBinLabel(PuritySpeciesLast + 2, "All passed"); + } if (mProduceTracks && mProduceLiteTracks) { LOG(fatal) << "FTracks and FLiteTracks are mutually exclusive -- enable only one. " @@ -620,7 +725,7 @@ class TrackBuilder << "or switch to FTracks if CollisionTables.produceCollisions is enabled instead."; } - if (mProduceTracks || mProduceLiteTracks || mProduceTrackMasks || mProduceTrackMass || mProduceTrackDcas || mProduceTrackExtras || mProduceElectronPids || mProducePionPids || mProduceKaonPids || mProduceProtonPids || mProduceDeuteronPids || mProduceTritonPids || mProduceHeliumPids) { + if (mProduceTracks || mProduceLiteTracks || mProduceTrackMasks || mProduceTrackMass || mProduceTrackDcas || mProduceTrackExtras || mProduceElectronPids || mProducePionPids || mProduceKaonPids || mProduceProtonPids || mProduceDeuteronPids || mProduceTritonPids || mProduceHeliumPids || mProduceItsPurity || mProduceTpcPurity || mProduceTofPurity) { mFillAnyTable = true; } else { LOG(info) << "No tables configured, Selection object will not be configured..."; @@ -658,6 +763,19 @@ class TrackBuilder if (!mProduceTracks && !mProduceLiteTracks) { return false; } + // getDaughterIndex() already checks indexMap and skips calling fillTrack() when the track is + // already present, so reaching this point with an existing entry only happens via the direct + // standalone-track path (fillTracks()/fillMcTracks()). Two distinct causes land here: the track + // was already registered as someone's daughter before its own collision was processed, or the + // track is itself associated with more than one collision and was already filled for an earlier + // one. Either way, keep the earlier row rather than writing a duplicate; its mask/fillType + // reflects whichever collision/role filled it first, not necessarily this one. + if (utils::getIndex(track.globalIndex(), indexMap)) { + LOG(warn) << "Track " << track.globalIndex() << " was already filled (as a daughter, or under another " + << "collision it is associated with); skipping the duplicate standalone fill. Its mask/fillType " + << "reflects whichever fill happened first, not necessarily this collision's selection."; + return true; + } int64_t lastIndex = 0; if (mProduceTracks) { trackProducts.producedTracks(collisionBuilder.collisionIndex(), @@ -757,6 +875,41 @@ class TrackBuilder } trackProducts.producedHeliumPids(itsHe, track.tpcNSigmaHe(), track.tofNSigmaHe()); } + if (mProduceItsPurity || mProduceTpcPurity || mProduceTofPurity) { + // raw nSigma per species, same values already used for the FXxxPids blocks above -- + // unused slots (species without a requirePidXxx flag, or not covered by this track + // type) stay at the 0 placeholder, same contract as the missing-Tof case + std::array itsNSigma = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; + std::array tpcNSigma = {track.tpcNSigmaEl(), track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr(), track.tpcNSigmaDe(), track.tpcNSigmaTr(), track.tpcNSigmaHe()}; + std::array tofNSigma = {track.tofNSigmaEl(), track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr(), track.tofNSigmaDe(), track.tofNSigmaTr(), track.tofNSigmaHe()}; + + if constexpr (requires(T1 t) { t.itsNSigmaEl(); }) { + itsNSigma = {track.itsNSigmaEl(), track.itsNSigmaPi(), track.itsNSigmaKa(), track.itsNSigmaPr(), track.itsNSigmaDe(), track.itsNSigmaTr(), track.itsNSigmaHe()}; + } + + std::array itsSlots = {0.f, 0.f, 0.f, 0.f}; + std::array tpcSlots = {0.f, 0.f, 0.f, 0.f}; + std::array tofSlots = {0.f, 0.f, 0.f, 0.f}; + bool hasTof = track.hasTOF(); + for (int i = 0; i < PuritySpeciesLast; i++) { + if (mPuritySlot[i] < 0) { + continue; + } + itsSlots[mPuritySlot[i]] = itsNSigma[i]; + tpcSlots[mPuritySlot[i]] = tpcNSigma[i]; + tofSlots[mPuritySlot[i]] = hasTof ? tofNSigma[i] : 0.f; // PurityHasTof is the gate; 0 here is just a placeholder + } + + if (mProduceItsPurity) { + trackProducts.producedItsPurity(utils::packPurityNSigma4(itsSlots[0], itsSlots[1], itsSlots[2], itsSlots[3], o2::aod::femtotracks::NSigmaPurityMagMin, o2::aod::femtotracks::NSigmaPurityMagMax)); + } + if (mProduceTpcPurity) { + trackProducts.producedTpcPurity(utils::packPurityNSigma4(tpcSlots[0], tpcSlots[1], tpcSlots[2], tpcSlots[3], o2::aod::femtotracks::NSigmaPurityMagMin, o2::aod::femtotracks::NSigmaPurityMagMax)); + } + if (mProduceTofPurity) { + trackProducts.producedTofPurity(utils::packPurityNSigma4(tofSlots[0], tofSlots[1], tofSlots[2], tofSlots[3], o2::aod::femtotracks::NSigmaPurityMagMin, o2::aod::femtotracks::NSigmaPurityMagMax), hasTof); + } + } return true; } @@ -812,7 +965,7 @@ class TrackBuilder { auto result = utils::getIndex(daughter.globalIndex(), indexMap); if (result) { - // daugher already in track table + // daughter already in track table return result.value(); } if (!this->template fillMcTrack(daughter, daughter, trackProducts, mcCols, collisionBuilder, mcParticles, mcBuilder, mcProducts)) { @@ -852,14 +1005,30 @@ class TrackBuilder bool mProduceDeuteronPids = false; bool mProduceTritonPids = false; bool mProduceHeliumPids = false; + bool mProduceItsPurity = false; + bool mProduceTpcPurity = false; + bool mProduceTofPurity = false; - std::unordered_map indexMap; // for mapping tracks to daughers of lambdas, cascades and resonances ... + // canonical species order for purity slot assignment -- fixed, not configurable (see + // init() for how requirePidXxx flags map onto slots 0-3) + static constexpr int PuritySpeciesLast = 7; + static constexpr std::array PuritySpeciesName = {"Electron", "Pion", "Kaon", "Proton", "Deuteron", "Triton", "Helium"}; + // mPuritySlot[i] = assigned slot (0-3) for species i (PuritySpeciesName order), or -1 if not assigned + std::array mPuritySlot = {-1, -1, -1, -1, -1, -1, -1}; + + std::unordered_map indexMap; // for mapping tracks to daughters of lambdas, cascades and resonances ... }; struct TrackBuilderDerivedToDerivedProducts : o2::framework::ProducesGroup { o2::framework::Produces producedTracks; o2::framework::Produces producedTrackMasks; o2::framework::Produces producedTrackMass; + // purity/fraction, copied through if the input track type has them joined (same + // compile-time-detected, opt-in-by-type pattern as producedTrackMass above) + o2::framework::Produces producedItsPurity; + o2::framework::Produces producedTpcPurity; + o2::framework::Produces producedTofPurity; + o2::framework::Produces producedTrackDcas; }; struct ConfTrackTablesDerivedToDerived : o2::framework::ConfigurableGroup { @@ -937,6 +1106,14 @@ class TrackBuilderDerivedToDerived if constexpr (utils::HasMass) { trackProducts.producedTrackMass(track.mass()); } + if constexpr (requires(T1 t) { t.itsPackedNSigma(); }) { + trackProducts.producedItsPurity(track.itsPackedNSigma()); + trackProducts.producedTpcPurity(track.tpcPackedNSigma()); + trackProducts.producedTofPurity(track.tofPackedNSigma(), track.purityHasTof()); + } + if constexpr (requires(T1 t) { t.dcaXY(); }) { + trackProducts.producedTrackDcas(track.dcaXY(), track.dcaZ()); + } const int64_t idx = trackProducts.producedTracks.lastIndex(); indexMap.emplace(track.globalIndex(), idx); @@ -984,6 +1161,13 @@ class TrackBuilderDerivedToDerived return this->fillTrack(daughter, trackProducts, collisionProducts); } + template + int64_t getDaughterIndex(const T1& daughter, T2& trackProducts, T3& collisionProducts, + T4& mcBuilder, T5 const& mcCols, T6 const& mcParticles, T7 const& mcMothers, T8 const& mcPartonicMothers, T9& mcProducts) + { + return this->fillTrackWithMcLabel(daughter, trackProducts, collisionProducts, mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcProducts); + } + template void reset(T const& tracks) { diff --git a/PWGCF/Femto/Core/trackHistManager.h b/PWGCF/Femto/Core/trackHistManager.h index 1a4312a8195..66e4fe3fab3 100644 --- a/PWGCF/Femto/Core/trackHistManager.h +++ b/PWGCF/Femto/Core/trackHistManager.h @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -75,7 +76,6 @@ enum TrackHist { kPtVsDcaxy, kPtVsDcaz, kPtVsDca, - kPtVsDcaxyVsDcaz, // DCAxy vs DCAz vs pt for extraction of particle fraction kPVsPTpc, // its pid kItsSignal, @@ -121,6 +121,29 @@ enum TrackHist { kTpctofDeuteron, kTpctofTriton, kTpctofHelium, + // momentum vs tpc vs its pid (uncombined, orthogonal nSigma axes -- lets one cut on both at once) + kTpcVsItsElectron, + kTpcVsItsPion, + kTpcVsItsKaon, + kTpcVsItsProton, + kTpcVsItsDeuteron, + kTpcVsItsTriton, + kTpcVsItsHelium, + // momentum vs tpc vs tof pid (uncombined, orthogonal nSigma axes -- lets one cut on both at once) + kTpcVsTofElectron, + kTpcVsTofPion, + kTpcVsTofKaon, + kTpcVsTofProton, + kTpcVsTofDeuteron, + kTpcVsTofTriton, + kTpcVsTofHelium, + // purity (species picked at runtime via puritySlotMask, see hPuritySlotAssignment) + kItsPurity, + kTpcPurity, + kTofPurity, + kTpcitsPurity, + kTpctofPurity, + kFractionDca, // mc kOrigin, kPdg, @@ -154,6 +177,10 @@ struct ConfTrackBinning : o2::framework::ConfigurableGroup { o2::framework::ConfigurableAxis sign{"sign", {{3, -1.5, 1.5}}, "Sign"}; o2::framework::ConfigurableAxis mass{"mass", {{200, 0.f, 2.f}}, "Mass (if enabled, plot TOF mass, otherwise PDG mass)"}; o2::framework::ConfigurableAxis pdgCodes{"pdgCodes", {{8001, -4000.5, 4000.5}}, "MC ONLY: PDG codes of selected tracks"}; + o2::framework::ConfigurableAxis purityNSigma{"purityNSigma", {{3000, -15, 15}}, "nSigma decoded from the packed purity tables (species picked via puritySlotMask); signed, for the raw Its/Tpc/Tof values"}; + o2::framework::ConfigurableAxis purityNSigmaComb{"purityNSigmaComb", {{2000, 0, 20}}, "Combined nSigma (hypot of two raw values); always non-negative, for Tpc+Its/Tpc+Tof"}; + o2::framework::ConfigurableAxis fractionDcaXy{"fractionDcaXy", {{300, -0.3, 0.3}}, "DCA_xy (fraction estimation, needs FTrackDcas joined)"}; + o2::framework::ConfigurableAxis fractionDcaZ{"fractionDcaZ", {{300, -0.3, 0.3}}, "DCA_z (fraction estimation, needs FTrackDcas joined)"}; }; constexpr const char PrefixTrackBinning1[] = "TrackBinning1"; @@ -208,25 +235,24 @@ using ConfKinkChaDauBinning = ConfTrackBinning; using ConfResonancePosDauBinning = ConfTrackBinning; using ConfResonanceNegDauBinning = ConfTrackBinning; using ConfPionPlusBinning = ConfTrackBinning; -using ConfPionMinusBinning = ConfTrackBinning; +using ConfPionMinusBinning = ConfTrackBinning; using ConfKaonPlusBinning = ConfTrackBinning; -using ConfKaonMinusBinning = ConfTrackBinning; +using ConfKaonMinusBinning = ConfTrackBinning; template struct ConfTrackQaBinning : o2::framework::ConfigurableGroup { std::string prefix = Prefix; o2::framework::Configurable momentumType{"momentumType", static_cast(modes::MomentumType::kPAtPv), "Momentum on x-axis (0->Pt, 1->P at PV, 2->P at TPC inner wall)"}; o2::framework::Configurable plot2d{"plot2d", true, "Generate various 2D QA plots"}; - o2::framework::Configurable plotElectronPid{"plotElectronPid", true, "Generate plots for Electron PID"}; - o2::framework::Configurable plotPionPid{"plotPionPid", true, "Generate plots for Pion PID"}; - o2::framework::Configurable plotKaonPid{"plotKaonPid", true, "Generate plots for Kaon PID"}; + o2::framework::Configurable plotElectronPid{"plotElectronPid", false, "Generate plots for Electron PID"}; + o2::framework::Configurable plotPionPid{"plotPionPid", false, "Generate plots for Pion PID"}; + o2::framework::Configurable plotKaonPid{"plotKaonPid", false, "Generate plots for Kaon PID"}; o2::framework::Configurable plotProtonPid{"plotProtonPid", true, "Generate plots for Proton PID"}; - o2::framework::Configurable plotDeuteronPid{"plotDeuteronPid", true, "Generate plots for Deuteron PID"}; - o2::framework::Configurable plotTritonPid{"plotTritonPid", true, "Generate plots for Triton PID"}; - o2::framework::Configurable plotHeliumPid{"plotHeliumPid", true, "Generate plots for Helium PID"}; + o2::framework::Configurable plotDeuteronPid{"plotDeuteronPid", false, "Generate plots for Deuteron PID"}; + o2::framework::Configurable plotTritonPid{"plotTritonPid", false, "Generate plots for Triton PID"}; + o2::framework::Configurable plotHeliumPid{"plotHeliumPid", false, "Generate plots for Helium PID"}; o2::framework::Configurable plotOrigins{"plotOrigins", true, "MC ONLY: Plot pt vs DCAxy vs DCAz for different particle origins"}; o2::framework::Configurable> pdgCodesForMothersOfSecondary{"pdgCodesForMothersOfSecondary", {3122}, "MC ONLY: PDG codes of mothers of secondaries (Max 3 will be considered)"}; - o2::framework::Configurable plotDcaCorrelation{"plotDcaCorrelation", true, "Plot pt vs DCAxy vs DCAz"}; o2::framework::ConfigurableAxis itsCluster{"itsCluster", {{8, -0.5, 7.5}}, "ITS cluster"}; o2::framework::ConfigurableAxis itsClusterIb{"itsClusterIb", {{4, -0.5, 3.5}}, "ITS cluster in inner barrel"}; o2::framework::ConfigurableAxis tpcCrossedRows{"tpcCrossedRows", {{161, -0.5, 160.5}}, "TPC cluster"}; @@ -338,9 +364,9 @@ constexpr std::array, kTrackHistLast> {kItsClusterIb, o2::framework::HistType::kTH1F, "hItsClusterIb", "ITS cluster in inner barrel; ITS IB cluster; Entries"}, {kTpcCrossedRows, o2::framework::HistType::kTH1F, "hTpcCrossedRows", "TPC crossed rows; TPC crossed rows; Entries"}, {kTpcCluster, o2::framework::HistType::kTH1F, "hTpcCluster", "TPC cluster found; TPC cluster found; Entries"}, - {kTpcClusterOverCrossedRows, o2::framework::HistType::kTH1F, "hTpcClusterOverCrossedRows", "TPC cluster found over TPC crossed rows; TPC cluster found / Tpc crossed rows; Entries"}, - {kTpcClusterShared, o2::framework::HistType::kTH1F, "hTpcClusterShared", "TPC cluster shared; TPC cluster shared ; Entries"}, - {kTpcClusterFractionShared, o2::framework::HistType::kTH1F, "hTpcClusterFractionShared", "TPC cluster fraction shared; TPC cluster shared / TPC cluster found ; Entries"}, + {kTpcClusterOverCrossedRows, o2::framework::HistType::kTH1F, "hTpcClusterOverCrossedRows", "TPC cluster found over TPC crossed rows; TPC cluster found / TPC crossed rows; Entries"}, + {kTpcClusterShared, o2::framework::HistType::kTH1F, "hTpcClusterShared", "TPC cluster shared; TPC cluster shared; Entries"}, + {kTpcClusterFractionShared, o2::framework::HistType::kTH1F, "hTpcClusterFractionShared", "TPC cluster fraction shared; TPC cluster shared / TPC cluster found; Entries"}, {kItsChi2, o2::framework::HistType::kTH1F, "hItsChi2", "ITS #chi^{2} / cluster; #chi^{2}_{ITS} / N_{cls}; Entries"}, {kTpcChi2, o2::framework::HistType::kTH1F, "hTpcChi2", "TPC #chi^{2} / cluster; #chi^{2}_{TPC} / N_{cls}; Entries"}, {kTrackType, o2::framework::HistType::kTH1F, "hTrackType", "Type the track row was stored as; Track type; Entries"}, @@ -360,7 +386,6 @@ constexpr std::array, kTrackHistLast> {kPtVsDcaxy, o2::framework::HistType::kTH2F, "hPtVsDcaxy", "p_{T} vs DCA_{XY}; p_{T} (GeV/#it{c}); DCA_{XY} (cm)"}, {kPtVsDcaz, o2::framework::HistType::kTH2F, "hPtVsDcaz", "p_{T} vs DCA_{Z}; p_{T} (GeV/#it{c}); DCA_{Z} (cm)"}, {kPtVsDca, o2::framework::HistType::kTH2F, "hPtVsDca", "p_{T} vs DCA; p_{T} (GeV/#it{c}); DCA (cm)"}, - {kPtVsDcaxyVsDcaz, o2::framework::HistType::kTHnSparseF, "hPtVsDcaxyVsDcaz", "Transverse momentum vs DCA_{xy} vs DCA_{z}; p_{T} (GeV/#it{c}); DCA_{XY} (cm); DCA_{Z} (cm);"}, {kPVsPTpc, o2::framework::HistType::kTH2F, "hPVsPTpc", "Correlation p_{global} and p_{TPC}; p_{global} (GeV/#it{c}); p_{tpc} (GeV/#it{c})"}, {kItsSignal, o2::framework::HistType::kTH2F, "hItsSignal", "ITS Signal; p (GeV/#it{c}) ; x "}, {kItsElectron, o2::framework::HistType::kTH2F, "hItsPidElectron", "ITS PID Electron; p (GeV/#it{c}) ; n#sigma_{ITS,el}"}, @@ -387,7 +412,7 @@ constexpr std::array, kTrackHistLast> {kTofDeuteron, o2::framework::HistType::kTH2F, "hTofPidDeuteron", "TOF PID Deuteron; p (GeV/#it{c}) ; n#sigma_{TOF,de}"}, {kTofTriton, o2::framework::HistType::kTH2F, "hTofPidTriton", "TOF PID Triton; p (GeV/#it{c}) ; n#sigma_{TOF,tr}"}, {kTofHelium, o2::framework::HistType::kTH2F, "hTofPidHelium", "TOF PID Helium; p (GeV/#it{c}) ; n#sigma_{TOF,he}"}, - {kTpcitsElectron, o2::framework::HistType::kTH2F, "hTpcitsPidElectron", "TPC+ITS PID Electron; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,el}^{2}+n#sigma_{ITS,el}^{2}}"}, + {kTpcitsElectron, o2::framework::HistType::kTH2F, "hTpcitsPidElectron", "TPC+ITS PID Electron; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,el}^{2}+n#sigma_{its,el}^{2}}"}, {kTpcitsPion, o2::framework::HistType::kTH2F, "hTpcitsPidPion", "TPC+ITS PID Pion; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,pi}^{2}+n#sigma_{its,pi}^{2}}"}, {kTpcitsKaon, o2::framework::HistType::kTH2F, "hTpcitsPidKaon", "TPC+ITS PID Kaon; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,ka}^{2}+n#sigma_{its,ka}^{2}}"}, {kTpcitsProton, o2::framework::HistType::kTH2F, "hTpcitsPidProton", "TPC+ITS PID Proton; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,pr}^{2}+n#sigma_{its,pr}^{2}}"}, @@ -401,6 +426,26 @@ constexpr std::array, kTrackHistLast> {kTpctofDeuteron, o2::framework::HistType::kTH2F, "hTpctofPidDeuteron", "TPC+TOF PID Deuteron; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,de}^{2}+n#sigma_{TOF,de}^{2}}"}, {kTpctofTriton, o2::framework::HistType::kTH2F, "hTpctofPidTriton", "TPC+TOF PID Triton; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,tr}^{2}+n#sigma_{TOF,tr}^{2}}"}, {kTpctofHelium, o2::framework::HistType::kTH2F, "hTpctofPidHelium", "TPC+TOF PID Helium; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,he}^{2}+n#sigma_{TOF,he}^{2}}"}, + {kTpcVsItsElectron, o2::framework::HistType::kTHnSparseF, "hTpcVsItsPidElectron", "TPC vs ITS PID Electron; p (GeV/#it{c}) ; n#sigma_{TPC,el} ; n#sigma_{ITS,el}"}, + {kTpcVsItsPion, o2::framework::HistType::kTHnSparseF, "hTpcVsItsPidPion", "TPC vs ITS PID Pion; p (GeV/#it{c}) ; n#sigma_{TPC,pi} ; n#sigma_{ITS,pi}"}, + {kTpcVsItsKaon, o2::framework::HistType::kTHnSparseF, "hTpcVsItsPidKaon", "TPC vs ITS PID Kaon; p (GeV/#it{c}) ; n#sigma_{TPC,ka} ; n#sigma_{ITS,ka}"}, + {kTpcVsItsProton, o2::framework::HistType::kTHnSparseF, "hTpcVsItsPidProton", "TPC vs ITS PID Proton; p (GeV/#it{c}) ; n#sigma_{TPC,pr} ; n#sigma_{ITS,pr}"}, + {kTpcVsItsDeuteron, o2::framework::HistType::kTHnSparseF, "hTpcVsItsPidDeuteron", "TPC vs ITS PID Deuteron; p (GeV/#it{c}) ; n#sigma_{TPC,de} ; n#sigma_{ITS,de}"}, + {kTpcVsItsTriton, o2::framework::HistType::kTHnSparseF, "hTpcVsItsPidTriton", "TPC vs ITS PID Triton; p (GeV/#it{c}) ; n#sigma_{TPC,tr} ; n#sigma_{ITS,tr}"}, + {kTpcVsItsHelium, o2::framework::HistType::kTHnSparseF, "hTpcVsItsPidHelium", "TPC vs ITS PID Helium; p (GeV/#it{c}) ; n#sigma_{TPC,he} ; n#sigma_{ITS,he}"}, + {kTpcVsTofElectron, o2::framework::HistType::kTHnSparseF, "hTpcVsTofPidElectron", "TPC vs TOF PID Electron; p (GeV/#it{c}) ; n#sigma_{TPC,el} ; n#sigma_{TOF,el}"}, + {kTpcVsTofPion, o2::framework::HistType::kTHnSparseF, "hTpcVsTofPidPion", "TPC vs TOF PID Pion; p (GeV/#it{c}) ; n#sigma_{TPC,pi} ; n#sigma_{TOF,pi}"}, + {kTpcVsTofKaon, o2::framework::HistType::kTHnSparseF, "hTpcVsTofPidKaon", "TPC vs TOF PID Kaon; p (GeV/#it{c}) ; n#sigma_{TPC,ka} ; n#sigma_{TOF,ka}"}, + {kTpcVsTofProton, o2::framework::HistType::kTHnSparseF, "hTpcVsTofPidProton", "TPC vs TOF PID Proton; p (GeV/#it{c}) ; n#sigma_{TPC,pr} ; n#sigma_{TOF,pr}"}, + {kTpcVsTofDeuteron, o2::framework::HistType::kTHnSparseF, "hTpcVsTofPidDeuteron", "TPC vs TOF PID Deuteron; p (GeV/#it{c}) ; n#sigma_{TPC,de} ; n#sigma_{TOF,de}"}, + {kTpcVsTofTriton, o2::framework::HistType::kTHnSparseF, "hTpcVsTofPidTriton", "TPC vs TOF PID Triton; p (GeV/#it{c}) ; n#sigma_{TPC,tr} ; n#sigma_{TOF,tr}"}, + {kTpcVsTofHelium, o2::framework::HistType::kTHnSparseF, "hTpcVsTofPidHelium", "TPC vs TOF PID Helium; p (GeV/#it{c}) ; n#sigma_{TPC,he} ; n#sigma_{TOF,he}"}, + {kItsPurity, o2::framework::HistType::kTH2F, "hItsPurity", "ITS purity nSigma (species picked via puritySlotMask); p_{T} (GeV/#it{c}) ; n#sigma_{ITS}"}, + {kTpcPurity, o2::framework::HistType::kTH2F, "hTpcPurity", "TPC purity nSigma (species picked via puritySlotMask); p_{T} (GeV/#it{c}) ; n#sigma_{TPC}"}, + {kTofPurity, o2::framework::HistType::kTH2F, "hTofPurity", "TOF purity nSigma (species picked via puritySlotMask); p_{T} (GeV/#it{c}) ; n#sigma_{TOF}"}, + {kTpcitsPurity, o2::framework::HistType::kTH2F, "hTpcitsPurity", "TPC+ITS combined purity nSigma (species picked via puritySlotMask); p_{T} (GeV/#it{c}) ; #sqrt{n#sigma_{TPC}^{2}+n#sigma_{ITS}^{2}}"}, + {kTpctofPurity, o2::framework::HistType::kTH2F, "hTpctofPurity", "TPC+TOF combined purity nSigma (species picked via puritySlotMask); p_{T} (GeV/#it{c}) ; #sqrt{n#sigma_{TPC}^{2}+n#sigma_{TOF}^{2}}"}, + {kFractionDca, o2::framework::HistType::kTHnSparseF, "hFractionDca", "DCA for fraction extraction; p_{T} (GeV/#it{c}) ; DCA_{xy} (cm) ; DCA_{z} (cm)"}, {kOrigin, o2::framework::HistType::kTH1F, "hOrigin", "Status Codes (=Origin); Status Code; Entries"}, {kPdg, o2::framework::HistType::kTH1F, "hPdg", "PDG Codes of selected tracks; PDG Code; Entries"}, {kPdgMother, o2::framework::HistType::kTH1F, "hPdgMother", "PDG Codes of mother of selected tracks; PDG Code; Entries"}, @@ -414,18 +459,24 @@ constexpr std::array, kTrackHistLast> {kFromMaterial, o2::framework::HistType::kTHnSparseF, "hFromMaterial", "Particles from material; p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm);"}, {kMissidentified, o2::framework::HistType::kTHnSparseF, "hMissidentified", "Missidentified particles (fake/wrong PDG code); p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm);"}, {kSecondary1, o2::framework::HistType::kTHnSparseF, "hFromSecondary1", "Particles from secondary decay; p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm);"}, - {kSecondary2, o2::framework::HistType::kTHnSparseF, "hFromSecondary2", "Particles from seconary decay; p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm);"}, - {kSecondary3, o2::framework::HistType::kTHnSparseF, "hFromSecondary3", "Particles from seconary decay; p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm);"}, - {kSecondaryOther, o2::framework::HistType::kTHnSparseF, "hFromSecondaryOther", "Particles from every other seconary decay; p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm);"}, + {kSecondary2, o2::framework::HistType::kTHnSparseF, "hFromSecondary2", "Particles from secondary decay; p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm);"}, + {kSecondary3, o2::framework::HistType::kTHnSparseF, "hFromSecondary3", "Particles from secondary decay; p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm);"}, + {kSecondaryOther, o2::framework::HistType::kTHnSparseF, "hFromSecondaryOther", "Particles from every other secondary decay; p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm);"}, }}; // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define TRACK_HIST_ANALYSIS_MAP(conf) \ - {kPt, {(conf).pt}}, \ - {kEta, {(conf).eta}}, \ - {kPhi, {(conf).phi}}, \ - {kSign, {(conf).sign}}, \ - {kMass, {(conf).mass}}, +#define TRACK_HIST_ANALYSIS_MAP(conf) \ + {kPt, {(conf).pt}}, \ + {kEta, {(conf).eta}}, \ + {kPhi, {(conf).phi}}, \ + {kSign, {(conf).sign}}, \ + {kMass, {(conf).mass}}, \ + {kItsPurity, {(conf).pt, (conf).purityNSigma}}, \ + {kTpcPurity, {(conf).pt, (conf).purityNSigma}}, \ + {kTofPurity, {(conf).pt, (conf).purityNSigma}}, \ + {kTpcitsPurity, {(conf).pt, (conf).purityNSigmaComb}}, \ + {kTpctofPurity, {(conf).pt, (conf).purityNSigmaComb}}, \ + {kFractionDca, {(conf).pt, (conf).fractionDcaXy, (conf).fractionDcaZ}}, // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define TRACK_HIST_QA_MAP(confAnalysis, confQa) \ @@ -456,7 +507,6 @@ constexpr std::array, kTrackHistLast> {kPtVsDcaxy, {(confAnalysis).pt, (confQa).dcaXy}}, \ {kPtVsDcaz, {(confAnalysis).pt, (confQa).dcaZ}}, \ {kPtVsDca, {(confAnalysis).pt, (confQa).dca}}, \ - {kPtVsDcaxyVsDcaz, {(confAnalysis).pt, (confQa).dcaXy, (confQa).dcaZ}}, \ {kPVsPTpc, {(confQa).p, (confQa).p}}, \ {kItsSignal, {(confQa).p, (confQa).itsSignal}}, \ {kItsElectron, {(confQa).p, (confQa).itsElectron}}, \ @@ -496,7 +546,21 @@ constexpr std::array, kTrackHistLast> {kTpctofProton, {(confQa).p, (confQa).tpctofProton}}, \ {kTpctofDeuteron, {(confQa).p, (confQa).tpctofDeuteron}}, \ {kTpctofTriton, {(confQa).p, (confQa).tpctofTriton}}, \ - {kTpctofHelium, {(confQa).p, (confQa).tpctofHelium}}, + {kTpctofHelium, {(confQa).p, (confQa).tpctofHelium}}, \ + {kTpcVsItsElectron, {(confQa).p, (confQa).tpcElectron, (confQa).itsElectron}}, \ + {kTpcVsItsPion, {(confQa).p, (confQa).tpcPion, (confQa).itsPion}}, \ + {kTpcVsItsKaon, {(confQa).p, (confQa).tpcKaon, (confQa).itsKaon}}, \ + {kTpcVsItsProton, {(confQa).p, (confQa).tpcProton, (confQa).itsProton}}, \ + {kTpcVsItsDeuteron, {(confQa).p, (confQa).tpcDeuteron, (confQa).itsDeuteron}}, \ + {kTpcVsItsTriton, {(confQa).p, (confQa).tpcTriton, (confQa).itsTriton}}, \ + {kTpcVsItsHelium, {(confQa).p, (confQa).tpcHelium, (confQa).itsHelium}}, \ + {kTpcVsTofElectron, {(confQa).p, (confQa).tpcElectron, (confQa).tofElectron}}, \ + {kTpcVsTofPion, {(confQa).p, (confQa).tpcPion, (confQa).tofPion}}, \ + {kTpcVsTofKaon, {(confQa).p, (confQa).tpcKaon, (confQa).tofKaon}}, \ + {kTpcVsTofProton, {(confQa).p, (confQa).tpcProton, (confQa).tofProton}}, \ + {kTpcVsTofDeuteron, {(confQa).p, (confQa).tpcDeuteron, (confQa).tofDeuteron}}, \ + {kTpcVsTofTriton, {(confQa).p, (confQa).tpcTriton, (confQa).tofTriton}}, \ + {kTpcVsTofHelium, {(confQa).p, (confQa).tpcHelium, (confQa).tofHelium}}, // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define TRACK_HIST_MC_MAP(conf) \ @@ -507,17 +571,20 @@ constexpr std::array, kTrackHistLast> {kPdgMother, {(conf).pdgCodes}}, \ {kPdgPartonicMother, {(conf).pdgCodes}}, +// per-origin truth-split variants of the FitVars fraction-estimation DCA histogram; binned on +// fractionDcaXy/fractionDcaZ (not the QA-only dcaXy/dcaZ) so they are available wherever FitVars +// is, including the pair task where no QA binning config exists // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define TRACK_HIST_MC_QA_MAP(confAnalysis, confQa) \ - {kNoMcParticle, {(confAnalysis).pt, (confQa).dcaXy, (confQa).dcaZ}}, \ - {kPrimary, {(confAnalysis).pt, (confQa).dcaXy, (confQa).dcaZ}}, \ - {kFromWrongCollision, {(confAnalysis).pt, (confQa).dcaXy, (confQa).dcaZ}}, \ - {kFromMaterial, {(confAnalysis).pt, (confQa).dcaXy, (confQa).dcaZ}}, \ - {kMissidentified, {(confAnalysis).pt, (confQa).dcaXy, (confQa).dcaZ}}, \ - {kSecondary1, {(confAnalysis).pt, (confQa).dcaXy, (confQa).dcaZ}}, \ - {kSecondary2, {(confAnalysis).pt, (confQa).dcaXy, (confQa).dcaZ}}, \ - {kSecondary3, {(confAnalysis).pt, (confQa).dcaXy, (confQa).dcaZ}}, \ - {kSecondaryOther, {(confAnalysis).pt, (confQa).dcaXy, (confQa).dcaZ}}, +#define TRACK_HIST_FITVARS_MC_MAP(conf) \ + {kNoMcParticle, {(conf).pt, (conf).fractionDcaXy, (conf).fractionDcaZ}}, \ + {kPrimary, {(conf).pt, (conf).fractionDcaXy, (conf).fractionDcaZ}}, \ + {kFromWrongCollision, {(conf).pt, (conf).fractionDcaXy, (conf).fractionDcaZ}}, \ + {kFromMaterial, {(conf).pt, (conf).fractionDcaXy, (conf).fractionDcaZ}}, \ + {kMissidentified, {(conf).pt, (conf).fractionDcaXy, (conf).fractionDcaZ}}, \ + {kSecondary1, {(conf).pt, (conf).fractionDcaXy, (conf).fractionDcaZ}}, \ + {kSecondary2, {(conf).pt, (conf).fractionDcaXy, (conf).fractionDcaZ}}, \ + {kSecondary3, {(conf).pt, (conf).fractionDcaXy, (conf).fractionDcaZ}}, \ + {kSecondaryOther, {(conf).pt, (conf).fractionDcaXy, (conf).fractionDcaZ}}, template auto makeTrackHistSpecMap(const T& confBinningAnalysis) @@ -531,7 +598,8 @@ auto makeTrackMcHistSpecMap(T const& confBinningAnalysis) { return std::map>{ TRACK_HIST_ANALYSIS_MAP(confBinningAnalysis) - TRACK_HIST_MC_MAP(confBinningAnalysis)}; + TRACK_HIST_MC_MAP(confBinningAnalysis) + TRACK_HIST_FITVARS_MC_MAP(confBinningAnalysis)}; }; template @@ -549,62 +617,88 @@ auto makeTrackMcQaHistSpecMap(T1 const& confBinningAnalysis, T2 const& confBinni TRACK_HIST_ANALYSIS_MAP(confBinningAnalysis) TRACK_HIST_QA_MAP(confBinningAnalysis, confBinningQa) TRACK_HIST_MC_MAP(confBinningAnalysis) - TRACK_HIST_MC_QA_MAP(confBinningAnalysis, confBinningQa)}; + TRACK_HIST_FITVARS_MC_MAP(confBinningAnalysis)}; }; #undef TRACK_HIST_ANALYSIS_MAP #undef TRACK_HIST_QA_MAP #undef TRACK_HIST_MC_MAP -#undef TRACK_HIST_MC_QA_MAP +#undef TRACK_HIST_FITVARS_MC_MAP constexpr char PrefixTrackQa[] = "TrackQA/"; -constexpr char PrefixTrack1[] = "Track1/"; -constexpr char PrefixTrack2[] = "Track2/"; +// same-event / mixed-event siblings: single-particle histograms for a given track slot, split by +// whether the particle came from a same-event or mixed-event pair +constexpr char PrefixTrack1Se[] = "Track1/SE/"; +constexpr char PrefixTrack2Se[] = "Track2/SE/"; +constexpr char PrefixTrack3Se[] = "Track3/SE/"; +constexpr char PrefixTrack1Me[] = "Track1/ME/"; +constexpr char PrefixTrack2Me[] = "Track2/ME/"; +constexpr char PrefixTrack3Me[] = "Track3/ME/"; // for dN/deta tracks constexpr char PrefixDndetaTrackGlobal[] = "DndetaTrackGlobal/"; constexpr char PrefixDndetaTrackItsOnly[] = "DndetaTrackItsOnly/"; -constexpr char PrefixTrack3[] = "Track3/"; -constexpr char PrefixResonancePosDaughter[] = "ResonancePosDau/"; -constexpr char PrefixResonanceNegDaughter[] = "ResonanceNegDau/"; +// daughter-track prefixes below: Se/Me siblings for pair-builder single-particle histograms +// (split by whether the daughter came from a same-event or mixed-event candidate), plus the +// original, unsplit Qa variants used by standalone QA tasks (left untouched). +constexpr char PrefixResonancePosDaughterSe[] = "ResonancePosDau/SE/"; +constexpr char PrefixResonanceNegDaughterSe[] = "ResonanceNegDau/SE/"; +constexpr char PrefixResonancePosDaughterMe[] = "ResonancePosDau/ME/"; +constexpr char PrefixResonanceNegDaughterMe[] = "ResonanceNegDau/ME/"; constexpr char PrefixResonancePosDaughterQa[] = "ResonancePosDauQa/"; constexpr char PrefixResonanceNegDaughterQa[] = "ResonanceNegDauQa/"; -constexpr char PrefixD01PosDaughter[] = "D01PosDau/"; -constexpr char PrefixD01NegDaughter[] = "D01NegDau/"; -constexpr char PrefixD02PosDaughter[] = "D02PosDau/"; -constexpr char PrefixD02NegDaughter[] = "D02NegDau/"; +constexpr char PrefixD01PosDaughterSe[] = "D01PosDau/SE/"; +constexpr char PrefixD01NegDaughterSe[] = "D01NegDau/SE/"; +constexpr char PrefixD02PosDaughterSe[] = "D02PosDau/SE/"; +constexpr char PrefixD02NegDaughterSe[] = "D02NegDau/SE/"; +constexpr char PrefixD01PosDaughterMe[] = "D01PosDau/ME/"; +constexpr char PrefixD01NegDaughterMe[] = "D01NegDau/ME/"; +constexpr char PrefixD02PosDaughterMe[] = "D02PosDau/ME/"; +constexpr char PrefixD02NegDaughterMe[] = "D02NegDau/ME/"; constexpr char PrefixD01PosDaughterQa[] = "D01PosDauQa/"; constexpr char PrefixD01NegDaughterQa[] = "D01NegDauQa/"; -constexpr char PrefixLc1ProtonDaughter[] = "Lc1ProtonDau/"; -constexpr char PrefixLc1KaonDaughter[] = "Lc1KaonDau/"; -constexpr char PrefixLc1PionDaughter[] = "Lc1PionDau/"; +constexpr char PrefixLc1ProtonDaughterSe[] = "Lc1ProtonDau/SE/"; +constexpr char PrefixLc1KaonDaughterSe[] = "Lc1KaonDau/SE/"; +constexpr char PrefixLc1PionDaughterSe[] = "Lc1PionDau/SE/"; +constexpr char PrefixLc1ProtonDaughterMe[] = "Lc1ProtonDau/ME/"; +constexpr char PrefixLc1KaonDaughterMe[] = "Lc1KaonDau/ME/"; +constexpr char PrefixLc1PionDaughterMe[] = "Lc1PionDau/ME/"; constexpr char PrefixLc1ProtonDaughterQa[] = "Lc1ProtonDauQa/"; constexpr char PrefixLc1KaonDaughterQa[] = "Lc1KaonDauQa/"; constexpr char PrefixLc1PionDaughterQa[] = "Lc1PionDauQa/"; -constexpr char PrefixV01PosDaughter[] = "V01PosDau/"; -constexpr char PrefixV01NegDaughter[] = "V01NegDau/"; -constexpr char PrefixV02PosDaughter[] = "V02PosDau/"; -constexpr char PrefixV02NegDaughter[] = "V02NegDau/"; +constexpr char PrefixV01PosDaughterSe[] = "V01PosDau/SE/"; +constexpr char PrefixV01NegDaughterSe[] = "V01NegDau/SE/"; +constexpr char PrefixV02PosDaughterSe[] = "V02PosDau/SE/"; +constexpr char PrefixV02NegDaughterSe[] = "V02NegDau/SE/"; +constexpr char PrefixV01PosDaughterMe[] = "V01PosDau/ME/"; +constexpr char PrefixV01NegDaughterMe[] = "V01NegDau/ME/"; +constexpr char PrefixV02PosDaughterMe[] = "V02PosDau/ME/"; +constexpr char PrefixV02NegDaughterMe[] = "V02NegDau/ME/"; constexpr char PrefixV0PosDaughterQa[] = "V0PosDauQa/"; constexpr char PrefixV0NegDaughterQa[] = "V0NegDauQa/"; -constexpr char PrefixCascadePosDaughter[] = "CascadePosDau/"; -constexpr char PrefixCascadeNegDaughter[] = "CascadeNegDau/"; -constexpr char PrefixCascadeBachelor[] = "CascadeBachelor/"; +constexpr char PrefixCascadePosDaughterSe[] = "CascadePosDau/SE/"; +constexpr char PrefixCascadeNegDaughterSe[] = "CascadeNegDau/SE/"; +constexpr char PrefixCascadeBachelorSe[] = "CascadeBachelor/SE/"; +constexpr char PrefixCascadePosDaughterMe[] = "CascadePosDau/ME/"; +constexpr char PrefixCascadeNegDaughterMe[] = "CascadeNegDau/ME/"; +constexpr char PrefixCascadeBachelorMe[] = "CascadeBachelor/ME/"; constexpr char PrefixCascadePosDaughterQa[] = "CascadePosDauQa/"; constexpr char PrefixCascadeNegDaughterQa[] = "CascadeNegDauQa/"; constexpr char PrefixCascadeBachelorQa[] = "CascadeBachelorQa/"; -constexpr char PrefixKinkChaDaughter[] = "KinkChaDau/"; +constexpr char PrefixKinkChaDaughterSe[] = "KinkChaDau/SE/"; +constexpr char PrefixKinkChaDaughterMe[] = "KinkChaDau/ME/"; constexpr char PrefixKinkChaDaughterQa[] = "KinkChaDauQa/"; constexpr std::string_view AnalysisDir = "Analysis/"; constexpr std::string_view QaDir = "QA/"; constexpr std::string_view PidDir = "PID/"; constexpr std::string_view McDir = "MC/"; +constexpr std::string_view FitVarsDir = "FitVars/"; template class TrackHistManager @@ -613,26 +707,24 @@ class TrackHistManager TrackHistManager() = default; ~TrackHistManager() = default; - // init for analysis and mc + // init for analysis, qa, mc and fitvars -- from a selection config object (track-track pairs, + // triplets, standalone QA task's own species). puritySlotMask lives on ConfTrackSelection + // itself (it picks which species slot in the packed purity tables this selection corresponds + // to), so it travels along with the selection object instead of needing its own parameter at + // every layer between the task and here. template void init(o2::framework::HistogramRegistry* registry, std::map> const& Specs, T const& ConfTrackSelection) { - mHistogramRegistry = registry; - mAbsCharge = std::abs(ConfTrackSelection.chargeAbs.value); - mPdgCode = std::abs(ConfTrackSelection.pdgCodeAbs.value) * ConfTrackSelection.chargeSign.value; - if constexpr (isFlagSet(mode, modes::Mode::kReco)) { - this->initAnalysis(Specs); - } - if constexpr (isFlagSet(mode, modes::Mode::kQa)) { - this->initQa(Specs); - } - if constexpr (isFlagSet(mode, modes::Mode::kMc)) { - this->initMc(Specs); - } + this->template initCommon(registry, Specs, + std::abs(ConfTrackSelection.chargeAbs.value), + std::abs(ConfTrackSelection.pdgCodeAbs.value) * ConfTrackSelection.chargeSign.value, + ConfTrackSelection.puritySlotMask.value); } + // same, from raw charge/pdg ints (V0/cascade/resonance daughter sub-managers, which have no + // selection config object of their own -- the parent builder already applied the selection) template void init(o2::framework::HistogramRegistry* registry, std::map> const& Specs, @@ -640,21 +732,10 @@ class TrackHistManager int ChargeSign, int PdgCodeAbs) { - mHistogramRegistry = registry; - mAbsCharge = std::abs(ChargeAbs); - mPdgCode = std::abs(PdgCodeAbs) * ChargeSign; - if constexpr (isFlagSet(mode, modes::Mode::kReco)) { - this->initAnalysis(Specs); - } - if constexpr (isFlagSet(mode, modes::Mode::kQa)) { - this->initQa(Specs); - } - if constexpr (isFlagSet(mode, modes::Mode::kMc)) { - this->initMc(Specs); - } + this->template initCommon(registry, Specs, std::abs(ChargeAbs), std::abs(PdgCodeAbs) * ChargeSign, 1); } - // init for analysis and qa and mc + // same as above, but also configure the QA-only optional histograms (species PID toggles, plot2d, ...) template void init(o2::framework::HistogramRegistry* registry, std::map> const& Specs, @@ -686,6 +767,9 @@ class TrackHistManager if constexpr (isFlagSet(mode, modes::Mode::kQa)) { this->fillQa(track); } + if constexpr (isFlagSet(mode, modes::Mode::kFitVars)) { + this->template fillFitVars(track); + } } template @@ -700,9 +784,37 @@ class TrackHistManager if constexpr (isFlagSet(mode, modes::Mode::kMc)) { this->template fillMc(track, col, mcParticles, mcMothers, mcPartonicMothers); } + if constexpr (isFlagSet(mode, modes::Mode::kFitVars)) { + this->template fillFitVars(track); + } } private: + // shared dispatch for all init() overloads above -- the only thing that differs between them + // is how mAbsCharge/mPdgCode/mPuritySlotMask get computed + template + void initCommon(o2::framework::HistogramRegistry* registry, + std::map> const& Specs, + int absCharge, int pdgCode, int puritySlotMask) + { + mHistogramRegistry = registry; + mAbsCharge = absCharge; + mPdgCode = pdgCode; + mPuritySlotMask = puritySlotMask; + if constexpr (isFlagSet(mode, modes::Mode::kReco)) { + this->initAnalysis(Specs); + } + if constexpr (isFlagSet(mode, modes::Mode::kQa)) { + this->initQa(Specs); + } + if constexpr (isFlagSet(mode, modes::Mode::kMc)) { + this->initMc(Specs); + } + if constexpr (isFlagSet(mode, modes::Mode::kFitVars)) { + this->template initFitVars(Specs); + } + } + template void enableOptionalHistograms(T const& ConfBinningQa) { @@ -716,8 +828,6 @@ class TrackHistManager mPlotHeliumPid = ConfBinningQa.plotHeliumPid.value; mMomentumType = static_cast(ConfBinningQa.momentumType.value); - mPlotDcaCorrelation = ConfBinningQa.plotDcaCorrelation.value; - mPlotOrigins = ConfBinningQa.plotOrigins.value; mPlotNSecondaries = ConfBinningQa.pdgCodesForMothersOfSecondary.value.size(); @@ -740,6 +850,50 @@ class TrackHistManager mHistogramRegistry->add(analysisDir + getHistNameV2(kMass, HistTable), getHistDesc(kMass, HistTable), getHistType(kMass, HistTable), {Specs.at(kMass)}); } + // purity (packed nSigma tables) and fraction (DCA) -- only called when mode carries kFitVars, + // which also implies the track type actually has the relevant tables joined. + // Purity itself is skipped in QA mode: the QA task already has full per-species PID + // (Its/Tpc/Tof/Tpcits/Tpctof/TpcVsIts/TpcVsTof in PID/), so the puritySlotMask-selected, + // single-species Purity histograms would be pure redundancy there -- they only earn their + // keep in the pair task, where there is no per-species breakdown to fall back on. + template + void initFitVars(std::map> const& Specs) + { + std::string fitVarsDir = std::string(prefix) + std::string(FitVarsDir); + if constexpr (!isFlagSet(mode, modes::Mode::kQa)) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kItsPurity, HistTable), getHistDesc(kItsPurity, HistTable), getHistType(kItsPurity, HistTable), {Specs.at(kItsPurity)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kTpcPurity, HistTable), getHistDesc(kTpcPurity, HistTable), getHistType(kTpcPurity, HistTable), {Specs.at(kTpcPurity)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kTofPurity, HistTable), getHistDesc(kTofPurity, HistTable), getHistType(kTofPurity, HistTable), {Specs.at(kTofPurity)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kTpcitsPurity, HistTable), getHistDesc(kTpcitsPurity, HistTable), getHistType(kTpcitsPurity, HistTable), {Specs.at(kTpcitsPurity)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kTpctofPurity, HistTable), getHistDesc(kTpctofPurity, HistTable), getHistType(kTpctofPurity, HistTable), {Specs.at(kTpctofPurity)}); + } + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kFractionDca, HistTable), getHistDesc(kFractionDca, HistTable), getHistType(kFractionDca, HistTable), {Specs.at(kFractionDca)}); + + // per-origin truth-split fraction-DCA templates; need MC truth, so only booked when the mode + // also carries kMc (available in the QA task and in the pair task's SE/ME, consistently, since + // they use the same fractionDcaXy/fractionDcaZ binning as hFractionDca above) + if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { + if (mPlotOrigins) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kNoMcParticle, HistTable), getHistDesc(kNoMcParticle, HistTable), getHistType(kNoMcParticle, HistTable), {Specs.at(kNoMcParticle)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kPrimary, HistTable), getHistDesc(kPrimary, HistTable), getHistType(kPrimary, HistTable), {Specs.at(kPrimary)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kFromWrongCollision, HistTable), getHistDesc(kFromWrongCollision, HistTable), getHistType(kFromWrongCollision, HistTable), {Specs.at(kFromWrongCollision)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kFromMaterial, HistTable), getHistDesc(kFromMaterial, HistTable), getHistType(kFromMaterial, HistTable), {Specs.at(kFromMaterial)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kMissidentified, HistTable), getHistDesc(kMissidentified, HistTable), getHistType(kMissidentified, HistTable), {Specs.at(kMissidentified)}); + + if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel1) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondary1, HistTable), getHistDesc(kSecondary1, HistTable), getHistType(kSecondary1, HistTable), {Specs.at(kSecondary1)}); + } + if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel2) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondary2, HistTable), getHistDesc(kSecondary2, HistTable), getHistType(kSecondary2, HistTable), {Specs.at(kSecondary2)}); + } + if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel3) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondary3, HistTable), getHistDesc(kSecondary3, HistTable), getHistType(kSecondary3, HistTable), {Specs.at(kSecondary3)}); + } + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondaryOther, HistTable), getHistDesc(kSecondaryOther, HistTable), getHistType(kSecondaryOther, HistTable), {Specs.at(kSecondaryOther)}); + } + } + } + void initQa(std::map> const& Specs) { std::string qaDir = std::string(prefix) + std::string(QaDir); @@ -786,10 +940,6 @@ class TrackHistManager mHistogramRegistry->add(qaDir + getHistNameV2(kPVsPTpc, HistTable), getHistDesc(kPVsPTpc, HistTable), getHistType(kPVsPTpc, HistTable), {Specs.at(kPVsPTpc)}); } - if (mPlotDcaCorrelation) { - mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsDcaxyVsDcaz, HistTable), getHistDesc(kPtVsDcaxyVsDcaz, HistTable), getHistType(kPtVsDcaxyVsDcaz, HistTable), {Specs.at(kPtVsDcaxyVsDcaz)}); - } - std::string pidDir = std::string(prefix) + std::string(PidDir); this->addPidHistogram(pidDir, kItsSignal, Specs); @@ -803,6 +953,8 @@ class TrackHistManager this->addPidHistogram(pidDir, kTofElectron, Specs); this->addPidHistogram(pidDir, kTpcitsElectron, Specs); this->addPidHistogram(pidDir, kTpctofElectron, Specs); + this->addPidHistogram(pidDir, kTpcVsItsElectron, Specs); + this->addPidHistogram(pidDir, kTpcVsTofElectron, Specs); } if (mPlotPionPid) { @@ -811,6 +963,8 @@ class TrackHistManager this->addPidHistogram(pidDir, kTofPion, Specs); this->addPidHistogram(pidDir, kTpcitsPion, Specs); this->addPidHistogram(pidDir, kTpctofPion, Specs); + this->addPidHistogram(pidDir, kTpcVsItsPion, Specs); + this->addPidHistogram(pidDir, kTpcVsTofPion, Specs); } if (mPlotKaonPid) { @@ -819,6 +973,8 @@ class TrackHistManager this->addPidHistogram(pidDir, kTofKaon, Specs); this->addPidHistogram(pidDir, kTpcitsKaon, Specs); this->addPidHistogram(pidDir, kTpctofKaon, Specs); + this->addPidHistogram(pidDir, kTpcVsItsKaon, Specs); + this->addPidHistogram(pidDir, kTpcVsTofKaon, Specs); } if (mPlotProtonPid) { @@ -827,6 +983,8 @@ class TrackHistManager this->addPidHistogram(pidDir, kTofProton, Specs); this->addPidHistogram(pidDir, kTpcitsProton, Specs); this->addPidHistogram(pidDir, kTpctofProton, Specs); + this->addPidHistogram(pidDir, kTpcVsItsProton, Specs); + this->addPidHistogram(pidDir, kTpcVsTofProton, Specs); } if (mPlotDeuteronPid) { @@ -835,6 +993,8 @@ class TrackHistManager this->addPidHistogram(pidDir, kTofDeuteron, Specs); this->addPidHistogram(pidDir, kTpcitsDeuteron, Specs); this->addPidHistogram(pidDir, kTpctofDeuteron, Specs); + this->addPidHistogram(pidDir, kTpcVsItsDeuteron, Specs); + this->addPidHistogram(pidDir, kTpcVsTofDeuteron, Specs); } if (mPlotTritonPid) { @@ -843,6 +1003,8 @@ class TrackHistManager this->addPidHistogram(pidDir, kTofTriton, Specs); this->addPidHistogram(pidDir, kTpcitsTriton, Specs); this->addPidHistogram(pidDir, kTpctofTriton, Specs); + this->addPidHistogram(pidDir, kTpcVsItsTriton, Specs); + this->addPidHistogram(pidDir, kTpcVsTofTriton, Specs); } if (mPlotHeliumPid) { @@ -851,6 +1013,8 @@ class TrackHistManager this->addPidHistogram(pidDir, kTofHelium, Specs); this->addPidHistogram(pidDir, kTpcitsHelium, Specs); this->addPidHistogram(pidDir, kTpctofHelium, Specs); + this->addPidHistogram(pidDir, kTpcVsItsHelium, Specs); + this->addPidHistogram(pidDir, kTpcVsTofHelium, Specs); } } @@ -858,13 +1022,7 @@ class TrackHistManager /// with the one matching the configured momentum type void addPidHistogram(std::string const& pidDir, TrackHist hist, std::map> const& Specs) { - // descriptions have the form "title; x axis ; y axis" - std::string desc(getHistDesc(hist, HistTable)); - const auto first = desc.find(';'); - const auto second = (first == std::string::npos) ? std::string::npos : desc.find(';', first + 1); - if (second != std::string::npos) { - desc.replace(first + 1, second - first - 1, " " + std::string(modes::momentumTypeToAxisTitle(mMomentumType)) + " "); - } + std::string desc = histmanager::replaceFirstAxisTitle(getHistDesc(hist, HistTable), modes::momentumTypeToAxisTitle(mMomentumType)); mHistogramRegistry->add(pidDir + getHistNameV2(hist, HistTable), desc.c_str(), getHistType(hist, HistTable), {Specs.at(hist)}); } @@ -888,25 +1046,6 @@ class TrackHistManager mHistogramRegistry->add(mcDir + getHistNameV2(kPdg, HistTable), getHistDesc(kPdg, HistTable), getHistType(kPdg, HistTable), {Specs.at(kPdg)}); mHistogramRegistry->add(mcDir + getHistNameV2(kPdgMother, HistTable), getHistDesc(kPdgMother, HistTable), getHistType(kPdgMother, HistTable), {Specs.at(kPdgMother)}); mHistogramRegistry->add(mcDir + getHistNameV2(kPdgPartonicMother, HistTable), getHistDesc(kPdgPartonicMother, HistTable), getHistType(kPdgPartonicMother, HistTable), {Specs.at(kPdgPartonicMother)}); - - if (mPlotOrigins) { - mHistogramRegistry->add(mcDir + getHistNameV2(kNoMcParticle, HistTable), getHistDesc(kNoMcParticle, HistTable), getHistType(kNoMcParticle, HistTable), {Specs.at(kNoMcParticle)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kPrimary, HistTable), getHistDesc(kPrimary, HistTable), getHistType(kPrimary, HistTable), {Specs.at(kPrimary)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kFromWrongCollision, HistTable), getHistDesc(kFromWrongCollision, HistTable), getHistType(kFromWrongCollision, HistTable), {Specs.at(kFromWrongCollision)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kFromMaterial, HistTable), getHistDesc(kFromMaterial, HistTable), getHistType(kFromMaterial, HistTable), {Specs.at(kFromMaterial)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kMissidentified, HistTable), getHistDesc(kMissidentified, HistTable), getHistType(kMissidentified, HistTable), {Specs.at(kMissidentified)}); - - if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel1) { - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondary1, HistTable), getHistDesc(kSecondary1, HistTable), getHistType(kSecondary1, HistTable), {Specs.at(kSecondary1)}); - } - if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel2) { - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondary2, HistTable), getHistDesc(kSecondary2, HistTable), getHistType(kSecondary2, HistTable), {Specs.at(kSecondary2)}); - } - if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel3) { - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondary3, HistTable), getHistDesc(kSecondary3, HistTable), getHistType(kSecondary3, HistTable), {Specs.at(kSecondary3)}); - } - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondaryOther, HistTable), getHistDesc(kSecondaryOther, HistTable), getHistType(kSecondaryOther, HistTable), {Specs.at(kSecondaryOther)}); - } } template @@ -923,6 +1062,55 @@ class TrackHistManager } } + // purity (packed nSigma tables) and fraction (DCA) -- fills whichever of the two is actually + // joined on T, independently of the other; mPuritySlotMask picks the species slot (one bit set, + // e.g. from cutculator reading hPuritySlotAssignment), everything is filled unconditionally + // once that slot is decoded -- no momentum-regime selection, the analyst picks the right + // combination/range downstream. Purity itself is skipped in QA mode, mirroring initFitVars. + template + void fillFitVars(T const& track) + { + if constexpr (!isFlagSet(mode, modes::Mode::kQa) && requires(T t) { t.itsNSigmaSlot0(); }) { + float itsNSigma = 0.f; + float tpcNSigma = 0.f; + float tofNSigma = 0.f; + switch (mPuritySlotMask) { + case 1: + itsNSigma = track.itsNSigmaSlot0(); + tpcNSigma = track.tpcNSigmaSlot0(); + tofNSigma = track.tofNSigmaSlot0(); + break; + case 2: + itsNSigma = track.itsNSigmaSlot1(); + tpcNSigma = track.tpcNSigmaSlot1(); + tofNSigma = track.tofNSigmaSlot1(); + break; + case 4: + itsNSigma = track.itsNSigmaSlot2(); + tpcNSigma = track.tpcNSigmaSlot2(); + tofNSigma = track.tofNSigmaSlot2(); + break; + case 8: + itsNSigma = track.itsNSigmaSlot3(); + tpcNSigma = track.tpcNSigmaSlot3(); + tofNSigma = track.tofNSigmaSlot3(); + break; + default: + LOG(fatal) << "puritySlotMask must have exactly one bit set (1, 2, 4 or 8), got " << mPuritySlotMask << ". Breaking..."; + } + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kItsPurity, HistTable)), mAbsCharge * track.pt(), itsNSigma); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kTpcPurity, HistTable)), mAbsCharge * track.pt(), tpcNSigma); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kTpcitsPurity, HistTable)), mAbsCharge * track.pt(), std::hypot(tpcNSigma, itsNSigma)); + if (track.purityHasTof()) { + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kTofPurity, HistTable)), mAbsCharge * track.pt(), tofNSigma); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kTpctofPurity, HistTable)), mAbsCharge * track.pt(), std::hypot(tpcNSigma, tofNSigma)); + } + } + if constexpr (requires(T t) { t.dcaXY(); }) { + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kFractionDca, HistTable)), mAbsCharge * track.pt(), track.dcaXY(), track.dcaZ()); + } + } + template void fillQa(T const& track) { @@ -959,10 +1147,6 @@ class TrackHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kPVsPTpc, HistTable)), track.p(), track.tpcInnerParam()); } - if (mPlotDcaCorrelation) { - mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kPtVsDcaxyVsDcaz, HistTable)), mAbsCharge * track.pt(), track.dcaXY(), track.dcaZ()); - } - float momentum = 0.f; if (mMomentumType == modes::MomentumType::kPt) { momentum = mAbsCharge * track.pt(); @@ -986,6 +1170,8 @@ class TrackHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTofElectron, HistTable)), momentum, track.tofNSigmaEl()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcitsElectron, HistTable)), momentum, track.tpcitsNSigmaEl()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpctofElectron, HistTable)), momentum, track.tpctofNSigmaEl()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsItsElectron, HistTable)), momentum, track.tpcNSigmaEl(), track.itsNSigmaEl()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsTofElectron, HistTable)), momentum, track.tpcNSigmaEl(), track.tofNSigmaEl()); } if (mPlotPionPid) { @@ -994,6 +1180,8 @@ class TrackHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTofPion, HistTable)), momentum, track.tofNSigmaPi()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcitsPion, HistTable)), momentum, track.tpcitsNSigmaPi()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpctofPion, HistTable)), momentum, track.tpctofNSigmaPi()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsItsPion, HistTable)), momentum, track.tpcNSigmaPi(), track.itsNSigmaPi()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsTofPion, HistTable)), momentum, track.tpcNSigmaPi(), track.tofNSigmaPi()); } if (mPlotKaonPid) { @@ -1002,6 +1190,8 @@ class TrackHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTofKaon, HistTable)), momentum, track.tofNSigmaKa()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcitsKaon, HistTable)), momentum, track.tpcitsNSigmaKa()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpctofKaon, HistTable)), momentum, track.tpctofNSigmaKa()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsItsKaon, HistTable)), momentum, track.tpcNSigmaKa(), track.itsNSigmaKa()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsTofKaon, HistTable)), momentum, track.tpcNSigmaKa(), track.tofNSigmaKa()); } if (mPlotProtonPid) { @@ -1010,6 +1200,8 @@ class TrackHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTofProton, HistTable)), momentum, track.tofNSigmaPr()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcitsProton, HistTable)), momentum, track.tpcitsNSigmaPr()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpctofProton, HistTable)), momentum, track.tpctofNSigmaPr()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsItsProton, HistTable)), momentum, track.tpcNSigmaPr(), track.itsNSigmaPr()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsTofProton, HistTable)), momentum, track.tpcNSigmaPr(), track.tofNSigmaPr()); } if (mPlotDeuteronPid) { @@ -1018,6 +1210,8 @@ class TrackHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTofDeuteron, HistTable)), momentum, track.tofNSigmaDe()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcitsDeuteron, HistTable)), momentum, track.tpcitsNSigmaDe()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpctofDeuteron, HistTable)), momentum, track.tpctofNSigmaDe()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsItsDeuteron, HistTable)), momentum, track.tpcNSigmaDe(), track.itsNSigmaDe()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsTofDeuteron, HistTable)), momentum, track.tpcNSigmaDe(), track.tofNSigmaDe()); } if (mPlotTritonPid) { @@ -1026,6 +1220,8 @@ class TrackHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTofTriton, HistTable)), momentum, track.tofNSigmaTr()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcitsTriton, HistTable)), momentum, track.tpcitsNSigmaTr()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpctofTriton, HistTable)), momentum, track.tpctofNSigmaTr()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsItsTriton, HistTable)), momentum, track.tpcNSigmaTr(), track.itsNSigmaTr()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsTofTriton, HistTable)), momentum, track.tpcNSigmaTr(), track.tofNSigmaTr()); } if (mPlotHeliumPid) { @@ -1034,6 +1230,8 @@ class TrackHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTofHelium, HistTable)), momentum, track.tofNSigmaHe()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcitsHelium, HistTable)), momentum, track.tpcitsNSigmaHe()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpctofHelium, HistTable)), momentum, track.tpctofNSigmaHe()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsItsHelium, HistTable)), momentum, track.tpcNSigmaHe(), track.itsNSigmaHe()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsTofHelium, HistTable)), momentum, track.tpcNSigmaHe(), track.tofNSigmaHe()); } } @@ -1044,9 +1242,9 @@ class TrackHistManager if (!track.has_fMcParticle()) { mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kPdg, HistTable)), 0); mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kOrigin, HistTable)), static_cast(modes::McOrigin::kNoMcParticle)); - if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { if (mPlotOrigins) { - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kNoMcParticle, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kNoMcParticle, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); } } return; @@ -1091,22 +1289,22 @@ class TrackHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kPdgPartonicMother, HistTable)), 0); } - if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { if (mPlotOrigins) { // check first if particle is from a wrong collision if (fromWrongCollision) { - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kFromWrongCollision, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kFromWrongCollision, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); } else if (isMissidentified) { // if it is, we fill it as such - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kMissidentified, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kMissidentified, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); } else { // if not, we fill it acccoridng to its origin switch (static_cast(mcParticle.origin())) { case modes::McOrigin::kPhysicalPrimary: - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kPrimary, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kPrimary, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); break; case modes::McOrigin::kFromMaterial: - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kFromMaterial, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kFromMaterial, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); break; case modes::McOrigin::kFromSecondaryDecay: if (mcParticle.has_fMcMother()) { @@ -1114,18 +1312,18 @@ class TrackHistManager int motherPdgCode = std::abs(mother.pdgCode()); // Switch on PDG of the mother if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel1 && motherPdgCode == mPdgCodesSecondaryMother[0]) { - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kSecondary1, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondary1, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); } else if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel2 && motherPdgCode == mPdgCodesSecondaryMother[1]) { - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kSecondary2, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondary2, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); } else if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel3 && motherPdgCode == mPdgCodesSecondaryMother[2]) { - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kSecondary3, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondary3, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); } else { - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kSecondaryOther, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondaryOther, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); } } break; default: - LOG(warn) << "Encounted partilce with unknown origin!"; + LOG(warn) << "Encountered particle with unknown origin!"; break; } } @@ -1136,6 +1334,7 @@ class TrackHistManager o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; int mAbsCharge = 1; int mPdgCode = 0; + int mPuritySlotMask = 1; bool mPlot2d = false; bool mPlotElectronPid = false; bool mPlotPionPid = false; @@ -1144,8 +1343,10 @@ class TrackHistManager bool mPlotDeuteronPid = false; bool mPlotTritonPid = false; bool mPlotHeliumPid = false; - bool mPlotDcaCorrelation = false; - bool mPlotOrigins = false; + // true by default so the per-origin FitVars templates are booked/filled even where + // enableOptionalHistograms() is never called (e.g. the pair task, which has no QA-binning + // config); the QA task can still opt out via its plotOrigins Configurable + bool mPlotOrigins = true; int mPlotNSecondaries = 0; std::array mPdgCodesSecondaryMother = {0}; modes::MomentumType mMomentumType = modes::MomentumType::kPAtPv; diff --git a/PWGCF/Femto/Core/trackTrackTwoPhotonResonanceBuilder.h b/PWGCF/Femto/Core/trackTrackTwoPhotonResonanceBuilder.h new file mode 100644 index 00000000000..dacdde4f272 --- /dev/null +++ b/PWGCF/Femto/Core/trackTrackTwoPhotonResonanceBuilder.h @@ -0,0 +1,235 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file trackTrackTwoPhotonResonanceBuilder.h +/// \brief builder for resonances reconstructed from two tracks and a two-photon resonance +/// (e.g. omega(782) -> pi+ pi- pi0) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_TRACKTRACKTWOPHOTONRESONANCEBUILDER_H_ +#define PWGCF_FEMTO_CORE_TRACKTRACKTWOPHOTONRESONANCEBUILDER_H_ + +#include "PWGCF/Femto/Core/dataTypes.h" +#include "PWGCF/Femto/Core/femtoUtils.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include "Common/Core/RecoDecay.h" + +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include + +#include + +namespace o2::analysis::femto::trackstwophotonresonancebuilder +{ +// filters applied in the producer task (loose pre-selection, applied before the mass window itself) +struct ConfOmegaMesonFilters : o2::framework::ConfigurableGroup { + std::string prefix = std::string("OmegaMesonFilters"); + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; + o2::framework::Configurable ptMax{"ptMax", 10.f, "Maximum pT"}; + o2::framework::Configurable etaMin{"etaMin", -0.9f, "Minimum eta"}; + o2::framework::Configurable etaMax{"etaMax", 0.9f, "Maximum eta"}; + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; + o2::framework::Configurable massMin{"massMin", 0.6f, "Minimum invariant mass"}; + o2::framework::Configurable massMax{"massMax", 0.95f, "Maximum invariant mass"}; +}; + +// selection used downstream (QA/pairing) to re-select already produced resonances by mass window +// and daughter mask. both charged daughters are pions (just opposite charge, no species swap the +// way kstar's kaon/pion does), so -- same as TWOTRACKRESONANCE_SAME_SPECIES_MASK for phi/rho0 -- +// a single pair of mask thresholds is applied to both. plus a 3rd mask for the neutral daughter (no +// momentum-threshold split there, same as femtotwophotonresonances' single dauMask) +template +struct ConfOmegaMesonSelection : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + o2::framework::Configurable pdgCodeAbs{"pdgCodeAbs", o2::constants::physics::Pdg::kOmega, "Absolute PDG code of the resonance (pdg[omega(782)]=223)"}; + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; + o2::framework::Configurable ptMax{"ptMax", 10.f, "Maximum pT"}; + o2::framework::Configurable etaMin{"etaMin", -0.9f, "Minimum eta"}; + o2::framework::Configurable etaMax{"etaMax", 0.9f, "Maximum eta"}; + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; + o2::framework::Configurable massMin{"massMin", 0.6f, "Minimum invariant mass"}; + o2::framework::Configurable massMax{"massMax", 0.95f, "Maximum invariant mass"}; + o2::framework::Configurable pionDauMaskBelowThres{"pionDauMaskBelowThres", 0x10u, "Bitmask for both pion daughters below threshold"}; + o2::framework::Configurable pionDauMaskAboveThres{"pionDauMaskAboveThres", 0x8u, "Bitmask for both pion daughters above threshold"}; + o2::framework::Configurable neutralDauMask{"neutralDauMask", 0, "Bitmask required for the neutral (two-photon-resonance) daughter"}; +}; + +constexpr const char PrefixOmegaMesonSelection1[] = "OmegaMesonSelection1"; +using ConfOmegaMesonSelection1 = ConfOmegaMesonSelection; + +struct TrackTrackTwoPhotonResonanceBuilderProducts : o2::framework::ProducesGroup { + o2::framework::Produces producedOmegaMesons; + o2::framework::Produces producedOmegaMesonMasks; +}; + +struct ConfTrackTrackTwoPhotonResonanceTables : o2::framework::ConfigurableGroup { + std::string prefix = std::string("TrackTrackTwoPhotonResonanceTables"); + o2::framework::Configurable produceOmegaMesons{"produceOmegaMesons", -1, "Produce OmegaMesons (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceOmegaMesonMasks{"produceOmegaMesonMasks", -1, "Produce OmegaMesonMasks (-1: auto; 0 off; 1 on)"}; +}; + +/// \brief Builder for resonances reconstructed from two tracks and a two-photon resonance +/// (e.g. omega(782) -> pi+ pi- pi0). +/// +/// posDau/negDau are drawn from already-selected FTracks partitions (sign and PID handled by the +/// normal track selection, same as any other track daughter use case), and neutralDau from an +/// already-built FPi0s/FEtas partition. This builder does the 3-body combinatorics, the +/// resonance's own mass window, and -- like TwoTrackResonanceBuilder -- copies the daughters' +/// bitmasks onto the candidate so systematic variations can be reselected downstream from the +/// candidate table alone, without rejoining the daughter tables. +template +class TrackTrackTwoPhotonResonanceBuilder +{ + public: + TrackTrackTwoPhotonResonanceBuilder() = default; + ~TrackTrackTwoPhotonResonanceBuilder() = default; + + template + void init(T1& confFilter, T2& confPosDauSelection, T3& confNegDauSelection, T4& confTable, T5& initContext) + { + mMassMin = confFilter.massMin.value; + mMassMax = confFilter.massMax.value; + mPtMin = confFilter.ptMin.value; + mPtMax = confFilter.ptMax.value; + mEtaMin = confFilter.etaMin.value; + mEtaMax = confFilter.etaMax.value; + mPhiMin = confFilter.phiMin.value; + mPhiMax = confFilter.phiMax.value; + + mPosDauThreshold = confPosDauSelection.pidThres.value; + mNegDauThreshold = confNegDauSelection.pidThres.value; + + if constexpr (modes::isEqual(resoType, modes::TrackTrackTwoPhotonResonance::kOmegaMeson)) { + LOG(info) << "Initialize femto OmegaMeson builder..."; + mPosDauMass = o2::constants::physics::MassPiPlus; + mNegDauMass = o2::constants::physics::MassPiMinus; + mProduceOmegaMesons = utils::enableTable("FOmegaMesons_001", confTable.produceOmegaMesons.value, initContext); + mProduceOmegaMesonMasks = utils::enableTable("FOmegaMesonMasks_001", confTable.produceOmegaMesonMasks.value, initContext); + } + + if (mProduceOmegaMesons || mProduceOmegaMesonMasks) { + mFillAnyTable = true; + } else { + LOG(info) << "No tables configured, Selection object will not be configured..."; + LOG(info) << "Initialization done..."; + return; + } + LOG(info) << "Initialization done..."; + } + + template + void fillResonances(T1 const& col, T2& resonanceProducts, T3& posDauPartition, T4& negDauPartition, T5& neutralDauPartition, T6& cache) + { + if (!mFillAnyTable) { + return; + } + auto posDauSlice = posDauPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto negDauSlice = negDauPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto neutralDauSlice = neutralDauPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + for (auto const& [posDau, negDau, neutralDau] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(posDauSlice, negDauSlice, neutralDauSlice))) { + this->fillResonance(col, posDau, negDau, neutralDau, resonanceProducts); + } + } + + private: + template + void reconstructResonance(T1 const& posDau, T2 const& negDau, T3 const& neutralDau) + { + ROOT::Math::PtEtaPhiMVector vecPosDau{posDau.pt(), posDau.eta(), posDau.phi(), mPosDauMass}; + ROOT::Math::PtEtaPhiMVector vecNegDau{negDau.pt(), negDau.eta(), negDau.phi(), mNegDauMass}; + ROOT::Math::PtEtaPhiMVector vecNeutralDau{neutralDau.pt(), neutralDau.eta(), neutralDau.phi(), neutralDau.mass()}; + ROOT::Math::PtEtaPhiMVector vecResonance = vecPosDau + vecNegDau + vecNeutralDau; + + mPt = vecResonance.Pt(); + mEta = vecResonance.Eta(); + mPhi = RecoDecay::constrainAngle(vecResonance.Phi()); + mMass = vecResonance.M(); + } + + [[nodiscard]] bool checkFilters() const + { + return ((mMass > mMassMin && mMass < mMassMax) && + (mPt > mPtMin && mPt < mPtMax) && + (mEta > mEtaMin && mEta < mEtaMax) && + (mPhi >= mPhiMin && mPhi < mPhiMax)); + } + + template + void fillResonance(T1 const& col, T2 const& posDau, T3 const& negDau, T4 const& neutralDau, T5& resonanceProducts) + { + reconstructResonance(posDau, negDau, neutralDau); + if (!checkFilters()) { + return; + } + + bool posDauHasHighMomentum = posDau.p() > mPosDauThreshold; + bool negDauHasHighMomentum = negDau.p() > mNegDauThreshold; + + if constexpr (modes::isEqual(resoType, modes::TrackTrackTwoPhotonResonance::kOmegaMeson)) { + if (mProduceOmegaMesons) { + resonanceProducts.producedOmegaMesons(col.globalIndex(), + mPt, + mEta, + mPhi, + mMass, + posDau.globalIndex(), + negDau.globalIndex(), + neutralDau.globalIndex()); + } + if (mProduceOmegaMesonMasks) { + resonanceProducts.producedOmegaMesonMasks(posDau.mask(), + posDauHasHighMomentum, + negDau.mask(), + negDauHasHighMomentum, + neutralDau.maskDau()); + } + } + } + + // cached kinematics of the resonance + float mPt = 0.f; + float mEta = 0.f; + float mPhi = 0.f; + float mMass = 0.f; + + float mMassMin = 0.f; + float mMassMax = 1.f; + float mPtMin = 0.f; + float mPtMax = 10.f; + float mEtaMin = -0.9f; + float mEtaMax = 0.9f; + float mPhiMin = 0.f; + float mPhiMax = o2::constants::math::TwoPI; + + float mPosDauMass = 0.f; + float mNegDauMass = 0.f; + + float mPosDauThreshold = 0.f; + float mNegDauThreshold = 0.f; + + bool mFillAnyTable = false; + bool mProduceOmegaMesons = false; + bool mProduceOmegaMesonMasks = false; +}; + +} // namespace o2::analysis::femto::trackstwophotonresonancebuilder +#endif // PWGCF_FEMTO_CORE_TRACKTRACKTWOPHOTONRESONANCEBUILDER_H_ diff --git a/PWGCF/Femto/Core/trackTrackTwoPhotonResonanceHistManager.h b/PWGCF/Femto/Core/trackTrackTwoPhotonResonanceHistManager.h new file mode 100644 index 00000000000..e50ade02d91 --- /dev/null +++ b/PWGCF/Femto/Core/trackTrackTwoPhotonResonanceHistManager.h @@ -0,0 +1,228 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file trackTrackTwoPhotonResonanceHistManager.h +/// \brief histogram manager for resonances built from two tracks and a two-photon resonance +/// (e.g. omega(782) -> pi+ pi- pi0) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_TRACKTRACKTWOPHOTONRESONANCEHISTMANAGER_H_ +#define PWGCF_FEMTO_CORE_TRACKTRACKTWOPHOTONRESONANCEHISTMANAGER_H_ + +#include "PWGCF/Femto/Core/histManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/photonHistManager.h" +#include "PWGCF/Femto/Core/trackHistManager.h" +#include "PWGCF/Femto/Core/twoPhotonResonanceHistManager.h" + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +namespace o2::analysis::femto::trackstwophotonresonancehistmanager +{ +enum TrackTrackTwoPhotonResonanceHist { + // analysis + kPt, + kEta, + kPhi, + kMass, + kPtVsMass, + // 2d qa + kPtVsEta, + kPtVsPhi, + kPhiVsEta, + kTrackTrackTwoPhotonResonanceHistLast +}; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TRACKTRACKTWOPHOTONRESONANCE_DEFAULT_BINNING(defaultMassMin, defaultMassMax) \ + o2::framework::ConfigurableAxis pt{"pt", {{600, 0, 6}}, "Pt"}; \ + o2::framework::ConfigurableAxis eta{"eta", {{300, -1.5, 1.5}}, "Eta"}; \ + o2::framework::ConfigurableAxis phi{"phi", {{720, 0, 1.f * o2::constants::math::TwoPI}}, "Phi"}; \ + o2::framework::ConfigurableAxis mass{"mass", {{200, (defaultMassMin), (defaultMassMax)}}, "Mass"}; + +struct ConfOmegaMesonBinning : o2::framework::ConfigurableGroup { + std::string prefix = std::string("OmegaMesonBinning"); + TRACKTRACKTWOPHOTONRESONANCE_DEFAULT_BINNING(0.6f, 0.95f) +}; +#undef TRACKTRACKTWOPHOTONRESONANCE_DEFAULT_BINNING + +constexpr std::array, kTrackTrackTwoPhotonResonanceHistLast> HistTable = { + {{kPt, o2::framework::HistType::kTH1F, "hPt", "Transverse Momentum; p_{T} (GeV/#it{c}); Entries"}, + {kEta, o2::framework::HistType::kTH1F, "hEta", "Pseudorapdity; #eta; Entries"}, + {kPhi, o2::framework::HistType::kTH1F, "hPhi", "Azimuthal angle; #varphi; Entries"}, + {kMass, o2::framework::HistType::kTH1F, "hMass", "Invariant mass; m (GeV/#it{c}^{2}); Entries"}, + {kPtVsMass, o2::framework::HistType::kTH2F, "hPtVsMass", "p_{T} vs invariant mass; p_{T} (GeV/#it{c}); m (GeV/#it{c}^{2})"}, + {kPtVsEta, o2::framework::HistType::kTH2F, "hPtVsEta", "p_{T} vs #eta; p_{T} (GeV/#it{c}) ; #eta"}, + {kPtVsPhi, o2::framework::HistType::kTH2F, "hPtVsPhi", "p_{T} vs #varphi;p_{T} (GeV/#it{c});#varphi"}, + {kPhiVsEta, o2::framework::HistType::kTH2F, "hPhiVsEta", "#varphi vs #eta; #varphi ; #eta"}}}; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TRACKTRACKTWOPHOTONRESONANCE_HIST_ANALYSIS_MAP(conf) \ + {kPt, {(conf).pt}}, \ + {kEta, {(conf).eta}}, \ + {kPhi, {(conf).phi}}, \ + {kMass, {(conf).mass}}, \ + {kPtVsMass, {(conf).pt, (conf).mass}}, + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TRACKTRACKTWOPHOTONRESONANCE_HIST_QA_MAP(conf) \ + {kPtVsEta, {(conf).pt, (conf).eta}}, \ + {kPtVsPhi, {(conf).pt, (conf).phi}}, \ + {kPhiVsEta, {(conf).phi, (conf).eta}}, + +template +auto makeTrackTrackTwoPhotonResonanceHistSpecMap(const T& confBinningAnalysis) +{ + return std::map>{ + TRACKTRACKTWOPHOTONRESONANCE_HIST_ANALYSIS_MAP(confBinningAnalysis)}; +} + +template +auto makeTrackTrackTwoPhotonResonanceQaHistSpecMap(const T& confBinningAnalysis) +{ + return std::map>{ + TRACKTRACKTWOPHOTONRESONANCE_HIST_ANALYSIS_MAP(confBinningAnalysis) + TRACKTRACKTWOPHOTONRESONANCE_HIST_QA_MAP(confBinningAnalysis)}; +} + +#undef TRACKTRACKTWOPHOTONRESONANCE_HIST_ANALYSIS_MAP +#undef TRACKTRACKTWOPHOTONRESONANCE_HIST_QA_MAP + +constexpr char PrefixOmegaMeson[] = "OmegaMeson/"; +constexpr char PrefixOmegaMesonNeutralDau[] = "OmegaMesonNeutralDau/"; +constexpr char PrefixOmegaMesonNeutralDauPhotonDau1[] = "OmegaMesonNeutralDauPhotonDau1Qa/"; +constexpr char PrefixOmegaMesonNeutralDauPhotonDau2[] = "OmegaMesonNeutralDauPhotonDau2Qa/"; + +constexpr std::string_view AnalysisDir = "Analysis/"; +constexpr std::string_view QaDir = "QA/"; + +/// \class TrackTrackTwoPhotonResonanceHistManager +/// \brief Histogram manager for resonances built from two tracks and a two-photon resonance (e.g. +/// omega(782) -> pi+ pi- pi0). The charged daughters reuse the regular TrackHistManager; +/// the embedded two-photon-resonance daughter reuses TwoPhotonResonanceHistManager as-is +/// (same reco+QA histograms, including its own photon-leg QA, that the dedicated Pi0/Eta +/// QA task gets) by also threading the FPhotons table through to it. +template +class TrackTrackTwoPhotonResonanceHistManager +{ + public: + TrackTrackTwoPhotonResonanceHistManager() = default; + ~TrackTrackTwoPhotonResonanceHistManager() = default; + + template + void init(o2::framework::HistogramRegistry* registry, + std::map> const& ResoSpecs, + std::map> const& PosDauSpecs, + std::map> const& NegDauSpecs, + std::map> const& NeutralDauSpecs, + std::map> const& NeutralDauPhotonSpecs, + T1 const& ConfPosDauQaBinning, + T2 const& ConfNegDauQaBinning, + T3 const& ConfNeutralDauPhotonQaBinning) + { + mHistogramRegistry = registry; + + // omega(782) -> pi+ pi- pi0: both charged daughters are pions + mPosDauManager.template init(registry, PosDauSpecs, 1, 1, kPiPlus, ConfPosDauQaBinning); + mNegDauManager.template init(registry, NegDauSpecs, 1, -1, kPiPlus, ConfNegDauQaBinning); + + // the embedded pi0/eta candidate is a fully-fledged femto table with its own mask/extras + // columns, so unlike the plain-track daughters above, it can just follow the outer mode. + mNeutralDauManager.template init(registry, NeutralDauSpecs, NeutralDauPhotonSpecs, ConfNeutralDauPhotonQaBinning); + + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { + initAnalysis(ResoSpecs); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + initQa(ResoSpecs); + } + } + + template + void fill(T1 const& resonance, T2 const& tracks, T3 const& neutralDaus, T4 const& photons) + { + auto posDau = tracks.rawIteratorAt(resonance.posDauId() - tracks.offset()); + mPosDauManager.template fill(posDau, tracks); + auto negDau = tracks.rawIteratorAt(resonance.negDauId() - tracks.offset()); + mNegDauManager.template fill(negDau, tracks); + auto neutralDau = neutralDaus.rawIteratorAt(resonance.neutralDauId() - neutralDaus.offset()); + mNeutralDauManager.template fill(neutralDau, photons); + + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { + fillAnalysis(resonance); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + fillQa(resonance); + } + } + + private: + void initAnalysis(std::map> const& ResoSpecs) + { + std::string analysisDir = std::string(resoPrefix) + std::string(AnalysisDir); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPt, HistTable), getHistDesc(kPt, HistTable), getHistType(kPt, HistTable), {ResoSpecs.at(kPt)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kEta, HistTable), getHistDesc(kEta, HistTable), getHistType(kEta, HistTable), {ResoSpecs.at(kEta)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPhi, HistTable), getHistDesc(kPhi, HistTable), getHistType(kPhi, HistTable), {ResoSpecs.at(kPhi)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kMass, HistTable), getHistDesc(kMass, HistTable), getHistType(kMass, HistTable), {ResoSpecs.at(kMass)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPtVsMass, HistTable), getHistDesc(kPtVsMass, HistTable), getHistType(kPtVsMass, HistTable), {ResoSpecs.at(kPtVsMass)}); + } + + void initQa(std::map> const& ResoSpecs) + { + std::string qaDir = std::string(resoPrefix) + std::string(QaDir); + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsEta, HistTable), getHistDesc(kPtVsEta, HistTable), getHistType(kPtVsEta, HistTable), {ResoSpecs.at(kPtVsEta)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsPhi, HistTable), getHistDesc(kPtVsPhi, HistTable), getHistType(kPtVsPhi, HistTable), {ResoSpecs.at(kPtVsPhi)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPhiVsEta, HistTable), getHistDesc(kPhiVsEta, HistTable), getHistType(kPhiVsEta, HistTable), {ResoSpecs.at(kPhiVsEta)}); + } + + template + void fillAnalysis(T const& resonance) + { + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kPt, HistTable)), resonance.pt()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kEta, HistTable)), resonance.eta()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kPhi, HistTable)), resonance.phi()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kMass, HistTable)), resonance.mass()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kPtVsMass, HistTable)), resonance.pt(), resonance.mass()); + } + + template + void fillQa(T const& resonance) + { + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(QaDir) + HIST(getHistName(kPtVsEta, HistTable)), resonance.pt(), resonance.eta()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(QaDir) + HIST(getHistName(kPtVsPhi, HistTable)), resonance.pt(), resonance.phi()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(QaDir) + HIST(getHistName(kPhiVsEta, HistTable)), resonance.phi(), resonance.eta()); + } + + o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; + trackhistmanager::TrackHistManager mPosDauManager; + trackhistmanager::TrackHistManager mNegDauManager; + twophotonresonancehistmanager::TwoPhotonResonanceHistManager mNeutralDauManager; +}; +} // namespace o2::analysis::femto::trackstwophotonresonancehistmanager +#endif // PWGCF_FEMTO_CORE_TRACKTRACKTWOPHOTONRESONANCEHISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/tripletBuilder.h b/PWGCF/Femto/Core/tripletBuilder.h index ec68bd83de1..8a39f818c8b 100644 --- a/PWGCF/Femto/Core/tripletBuilder.h +++ b/PWGCF/Femto/Core/tripletBuilder.h @@ -49,6 +49,9 @@ const int64_t nLimitPartitionParticles = 1; template (registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1); mTripletHistManagerSe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection1.pdgCodeAbs.value, confTrackSelection1.pdgCodeAbs.value); mTripletHistManagerSe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection1.chargeAbs.value, confTrackSelection1.chargeAbs.value); @@ -134,8 +138,10 @@ class TripletTrackTrackTrackBuilder // Track1 & Track2 & are the same particle species and track 3 is something else mCleaner1.init(confCleaner1); mCleaner3.init(confCleaner3); - mTrackHistManager1.template init(registry, trackHistSpec1, confTrackSelection1); - mTrackHistManager3.template init(registry, trackHistSpec3, confTrackSelection3); + mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerSe3.template init(registry, trackHistSpec3, confTrackSelection3); + mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerMe3.template init(registry, trackHistSpec3, confTrackSelection3); mTripletHistManagerSe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection1.pdgCodeAbs.value, confTrackSelection3.pdgCodeAbs.value); mTripletHistManagerSe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection1.chargeAbs.value, confTrackSelection3.chargeAbs.value); @@ -149,9 +155,12 @@ class TripletTrackTrackTrackBuilder mCleaner1.init(confCleaner1); mCleaner2.init(confCleaner2); mCleaner3.init(confCleaner3); - mTrackHistManager1.template init(registry, trackHistSpec1, confTrackSelection1); - mTrackHistManager2.template init(registry, trackHistSpec2, confTrackSelection2); - mTrackHistManager3.template init(registry, trackHistSpec3, confTrackSelection3); + mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerSe2.template init(registry, trackHistSpec2, confTrackSelection2); + mTrackHistManagerSe3.template init(registry, trackHistSpec3, confTrackSelection3); + mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerMe2.template init(registry, trackHistSpec2, confTrackSelection2); + mTrackHistManagerMe3.template init(registry, trackHistSpec3, confTrackSelection3); mTripletHistManagerSe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection2.pdgCodeAbs.value, confTrackSelection3.pdgCodeAbs.value); mTripletHistManagerSe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection2.chargeAbs.value, confTrackSelection3.chargeAbs.value); @@ -165,6 +174,7 @@ class TripletTrackTrackTrackBuilder // setup mixing mMixingPolicy = static_cast(confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; // setup rng if necessary if (confMixing.seed.value >= 0) { @@ -201,7 +211,7 @@ class TripletTrackTrackTrackBuilder if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - return tripletprocesshelpers::processSameEvent(trackSlice1, trackTable, col, mTrackHistManager1, mTripletHistManagerSe, mCtrSe, mTcSe, tripletOrder); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackTable, col, mTrackHistManagerSe1, mTripletHistManagerSe, mCtrSe, mTcSe, tripletOrder); } if (mTrack1Track2AreSameSpecies) { @@ -215,7 +225,7 @@ class TripletTrackTrackTrackBuilder if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice3, trackTable, col, mTrackHistManager1, mTrackHistManager3, mTripletHistManagerSe, mCtrSe, mTcSe, tripletOrder); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice3, trackTable, col, mTrackHistManagerSe1, mTrackHistManagerSe3, mTripletHistManagerSe, mCtrSe, mTcSe, tripletOrder); } auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); @@ -226,7 +236,7 @@ class TripletTrackTrackTrackBuilder } mColHistManager.template fill(col); mCtrSe.setMagField(col.magField()); - return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, trackSlice3, trackTable, col, mTrackHistManager1, mTrackHistManager2, mTrackHistManager3, mTripletHistManagerSe, mCtrSe, mTcSe); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, trackSlice3, trackTable, col, mTrackHistManagerSe1, mTrackHistManagerSe2, mTrackHistManagerSe3, mTripletHistManagerSe, mCtrSe, mTcSe); } // mc @@ -245,7 +255,7 @@ class TripletTrackTrackTrackBuilder if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - return tripletprocesshelpers::processSameEvent(trackSlice1, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mTripletHistManagerSe, mCleaner1, mCtrSe, mTcSe, tripletOrder); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe1, mTripletHistManagerSe, mCleaner1, mCtrSe, mTcSe, tripletOrder); } if (mTrack1Track2AreSameSpecies) { auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); @@ -258,7 +268,7 @@ class TripletTrackTrackTrackBuilder if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice3, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mTrackHistManager3, mTripletHistManagerSe, mCleaner1, mCleaner3, mCtrSe, mTcSe, tripletOrder); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice3, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe1, mTrackHistManagerSe3, mTripletHistManagerSe, mCleaner1, mCleaner3, mCtrSe, mTcSe, tripletOrder); } auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); @@ -269,7 +279,7 @@ class TripletTrackTrackTrackBuilder } mColHistManager.template fill(col, mcCols); mCtrSe.setMagField(col.magField()); - return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, trackSlice3, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mTrackHistManager2, mTrackHistManager3, mTripletHistManagerSe, mCleaner1, mCleaner2, mCleaner3, mCtrSe, mTcSe); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, trackSlice3, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe1, mTrackHistManagerSe2, mTrackHistManagerSe3, mTripletHistManagerSe, mCleaner1, mCleaner2, mCleaner3, mCtrSe, mTcSe); } // data @@ -279,13 +289,13 @@ class TripletTrackTrackTrackBuilder if (mTrack1Track2Track3AreSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition1, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition1, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe1); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition1, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition1, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe1); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition1, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition1, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe1); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -293,13 +303,13 @@ class TripletTrackTrackTrackBuilder } else if (mTrack1Track2AreSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe3); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe3); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe3); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -307,13 +317,13 @@ class TripletTrackTrackTrackBuilder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mTrackHistManagerMe3); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mTrackHistManagerMe3); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mTrackHistManagerMe3); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -328,13 +338,13 @@ class TripletTrackTrackTrackBuilder if (mTrack1Track2Track3AreSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner1, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner1, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe1); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner1, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner1, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe1); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner1, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner1, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe1); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -342,13 +352,13 @@ class TripletTrackTrackTrackBuilder } else if (mTrack1Track2AreSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner3, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner3, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe3); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner3, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner3, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe3); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner3, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner3, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe3); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -356,13 +366,13 @@ class TripletTrackTrackTrackBuilder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mCleaner3, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mCleaner3, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mTrackHistManagerMe3); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mCleaner3, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mCleaner3, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mTrackHistManagerMe3); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mCleaner3, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mCleaner3, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mTrackHistManagerMe3); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -372,9 +382,12 @@ class TripletTrackTrackTrackBuilder private: colhistmanager::CollisionHistManager mColHistManager; - trackhistmanager::TrackHistManager mTrackHistManager1; - trackhistmanager::TrackHistManager mTrackHistManager2; - trackhistmanager::TrackHistManager mTrackHistManager3; + trackhistmanager::TrackHistManager mTrackHistManagerSe1; + trackhistmanager::TrackHistManager mTrackHistManagerSe2; + trackhistmanager::TrackHistManager mTrackHistManagerSe3; + trackhistmanager::TrackHistManager mTrackHistManagerMe1; + trackhistmanager::TrackHistManager mTrackHistManagerMe2; + trackhistmanager::TrackHistManager mTrackHistManagerMe3; particlecleaner::ParticleCleaner mCleaner1; particlecleaner::ParticleCleaner mCleaner2; particlecleaner::ParticleCleaner mCleaner3; @@ -389,6 +402,7 @@ class TripletTrackTrackTrackBuilder bool mTrack1Track2Track3AreSameSpecies = false; bool mTrack1Track2AreSameSpecies = false; int mMixingDepth = 5; + int mEventStride = 1; bool mMixIdenticalParticles = false; std::mt19937 mRng; std::uniform_int_distribution<> mDist; @@ -400,6 +414,11 @@ template (registry, trackHistSpec1, confTrackSelection1); - mV0HistManager.template init(registry, v0histSpec, confV0Selection, posDauhistSpec, negDauhistSpec); + mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1); + mV0HistManagerSe.template init(registry, v0histSpec, confV0Selection, posDauhistSpec, negDauhistSpec); + mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1); + mV0HistManagerMe.template init(registry, v0histSpec, confV0Selection, posDauhistSpec, negDauhistSpec); mTripletHistManagerSe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection1.pdgCodeAbs.value, confV0Selection.pdgCodeAbs.value); mTripletHistManagerSe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection1.chargeAbs.value, 1); @@ -487,9 +508,12 @@ class TripletTrackTrackV0Builder // all three tracks are different mCleaner1.init(confCleaner1); mCleaner2.init(confCleaner2); - mTrackHistManager1.template init(registry, trackHistSpec1, confTrackSelection1); - mTrackHistManager2.template init(registry, trackHistSpec2, confTrackSelection2); - mV0HistManager.template init(registry, v0histSpec, confV0Selection, posDauhistSpec, negDauhistSpec); + mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerSe2.template init(registry, trackHistSpec2, confTrackSelection2); + mV0HistManagerSe.template init(registry, v0histSpec, confV0Selection, posDauhistSpec, negDauhistSpec); + mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerMe2.template init(registry, trackHistSpec2, confTrackSelection2); + mV0HistManagerMe.template init(registry, v0histSpec, confV0Selection, posDauhistSpec, negDauhistSpec); mTripletHistManagerSe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection2.pdgCodeAbs.value, confV0Selection.pdgCodeAbs.value); mTripletHistManagerSe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection2.chargeAbs.value, 1); @@ -503,6 +527,7 @@ class TripletTrackTrackV0Builder // setup mixing mMixingPolicy = static_cast(confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; // setup rng if necessary if (confMixing.seed.value >= 0) { @@ -535,7 +560,7 @@ class TripletTrackTrackV0Builder if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - return tripletprocesshelpers::processSameEvent(trackSlice1, v0Slice, trackTable, col, mTrackHistManager1, mV0HistManager, mTripletHistManagerSe, mCtrSe, mTcSe, tripletOrder); + return tripletprocesshelpers::processSameEvent(trackSlice1, v0Slice, trackTable, col, mTrackHistManagerSe1, mV0HistManagerSe, mTripletHistManagerSe, mCtrSe, mTcSe, tripletOrder); } auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); @@ -546,7 +571,7 @@ class TripletTrackTrackV0Builder } mColHistManager.template fill(col); mCtrSe.setMagField(col.magField()); - return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, v0Slice, trackTable, col, mTrackHistManager1, mTrackHistManager2, mV0HistManager, mTripletHistManagerSe, mCtrSe, mTcSe); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, v0Slice, trackTable, col, mTrackHistManagerSe1, mTrackHistManagerSe2, mV0HistManagerSe, mTripletHistManagerSe, mCtrSe, mTcSe); } // mc @@ -566,7 +591,7 @@ class TripletTrackTrackV0Builder if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - return tripletprocesshelpers::processSameEvent(trackSlice1, v0Slice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mV0HistManager, mTripletHistManagerSe, mCleaner1, mV0Cleaner, mCtrSe, mTcSe, tripletOrder); + return tripletprocesshelpers::processSameEvent(trackSlice1, v0Slice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe1, mV0HistManagerSe, mTripletHistManagerSe, mCleaner1, mV0Cleaner, mCtrSe, mTcSe, tripletOrder); } auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); @@ -577,7 +602,7 @@ class TripletTrackTrackV0Builder } mColHistManager.template fill(col, mcCols); mCtrSe.setMagField(col.magField()); - return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, v0Slice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mTrackHistManager2, mV0HistManager, mTripletHistManagerSe, mCleaner1, mCleaner2, mV0Cleaner, mCtrSe, mTcSe); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, v0Slice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe1, mTrackHistManagerSe2, mV0HistManagerSe, mTripletHistManagerSe, mCleaner1, mCleaner2, mV0Cleaner, mCtrSe, mTcSe); } // data @@ -587,13 +612,13 @@ class TripletTrackTrackV0Builder if (mTrack1Track2AreSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mV0HistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mV0HistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mV0HistManagerMe); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -601,13 +626,13 @@ class TripletTrackTrackV0Builder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mV0HistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mV0HistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mV0HistManagerMe); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -622,13 +647,13 @@ class TripletTrackTrackV0Builder if (mTrack1Track2AreSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mV0Cleaner, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mV0Cleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mV0HistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mV0Cleaner, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mV0Cleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mV0HistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mV0Cleaner, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mV0Cleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mV0HistManagerMe); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -636,13 +661,13 @@ class TripletTrackTrackV0Builder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mV0Cleaner, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mV0Cleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mV0HistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mV0Cleaner, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mV0Cleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mV0HistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mV0Cleaner, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mV0Cleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mV0HistManagerMe); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -652,9 +677,12 @@ class TripletTrackTrackV0Builder private: colhistmanager::CollisionHistManager mColHistManager; - trackhistmanager::TrackHistManager mTrackHistManager1; - trackhistmanager::TrackHistManager mTrackHistManager2; - v0histmanager::V0HistManager mV0HistManager; + trackhistmanager::TrackHistManager mTrackHistManagerSe1; + trackhistmanager::TrackHistManager mTrackHistManagerSe2; + v0histmanager::V0HistManager mV0HistManagerSe; + trackhistmanager::TrackHistManager mTrackHistManagerMe1; + trackhistmanager::TrackHistManager mTrackHistManagerMe2; + v0histmanager::V0HistManager mV0HistManagerMe; particlecleaner::ParticleCleaner mCleaner1; particlecleaner::ParticleCleaner mCleaner2; particlecleaner::ParticleCleaner mV0Cleaner; @@ -668,6 +696,7 @@ class TripletTrackTrackV0Builder triplethistmanager::MixingPolicy mMixingPolicy = triplethistmanager::MixingPolicy::kVtxMult; bool mTrack1Track2AreSameSpecies = false; int mMixingDepth = 5; + int mEventStride = 1; bool mMixIdenticalParticles = false; std::mt19937 mRng; std::uniform_int_distribution<> mDist; @@ -680,6 +709,12 @@ template (registry, trackHistSpec1, confTrackSelection1); - mCascadeHistManager.template init(registry, cascadeHistSpec, confCascadeSelection, bachelorHistSpec, posDauHistSpec, negDauHistSpec); + mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1); + mCascadeHistManagerSe.template init(registry, cascadeHistSpec, confCascadeSelection, bachelorHistSpec, posDauHistSpec, negDauHistSpec); + mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1); + mCascadeHistManagerMe.template init(registry, cascadeHistSpec, confCascadeSelection, bachelorHistSpec, posDauHistSpec, negDauHistSpec); mTripletHistManagerSe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection1.pdgCodeAbs.value, confCascadeSelection.pdgCodeAbs.value); mTripletHistManagerSe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection1.chargeAbs.value, 1); mCtrSe.init(registry, ctrHistSpec, cprHistSpecBachelor, cprHistSpecV0Daughter, confCtr, confCprBachelor, confCprV0Daughter, confTrackSelection1.chargeAbs.value, confTrackSelection1.chargeAbs.value); @@ -778,9 +815,12 @@ class TripletTrackTrackCascadeBuilder // all three tracks are different mTrackCleaner1.init(confTrackCleaner1); mTrackCleaner2.init(confTrackCleaner2); - mTrackHistManager1.template init(registry, trackHistSpec1, confTrackSelection1); - mTrackHistManager2.template init(registry, trackHistSpec2, confTrackSelection2); - mCascadeHistManager.template init(registry, cascadeHistSpec, confCascadeSelection, bachelorHistSpec, posDauHistSpec, negDauHistSpec); + mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerSe2.template init(registry, trackHistSpec2, confTrackSelection2); + mCascadeHistManagerSe.template init(registry, cascadeHistSpec, confCascadeSelection, bachelorHistSpec, posDauHistSpec, negDauHistSpec); + mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerMe2.template init(registry, trackHistSpec2, confTrackSelection2); + mCascadeHistManagerMe.template init(registry, cascadeHistSpec, confCascadeSelection, bachelorHistSpec, posDauHistSpec, negDauHistSpec); mTripletHistManagerSe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection2.pdgCodeAbs.value, confCascadeSelection.pdgCodeAbs.value); mTripletHistManagerSe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection2.chargeAbs.value, 1); @@ -794,6 +834,7 @@ class TripletTrackTrackCascadeBuilder // setup mixing mMixingPolicy = static_cast(confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; // setup rng if necessary if (confMixing.seed.value >= 0) { uint64_t randomSeed = 0; @@ -825,7 +866,7 @@ class TripletTrackTrackCascadeBuilder if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - return tripletprocesshelpers::processSameEvent(trackSlice1, cascadeSlice, trackTable, col, mTrackHistManager1, mCascadeHistManager, mTripletHistManagerSe, mCtrSe, mTcSe, tripletOrder); + return tripletprocesshelpers::processSameEvent(trackSlice1, cascadeSlice, trackTable, col, mTrackHistManagerSe1, mCascadeHistManagerSe, mTripletHistManagerSe, mCtrSe, mTcSe, tripletOrder); } auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); @@ -836,7 +877,7 @@ class TripletTrackTrackCascadeBuilder } mColHistManager.template fill(col); mCtrSe.setMagField(col.magField()); - return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, cascadeSlice, trackTable, col, mTrackHistManager1, mTrackHistManager2, mCascadeHistManager, mTripletHistManagerSe, mCtrSe, mTcSe); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, cascadeSlice, trackTable, col, mTrackHistManagerSe1, mTrackHistManagerSe2, mCascadeHistManagerSe, mTripletHistManagerSe, mCtrSe, mTcSe); } // mc @@ -856,7 +897,7 @@ class TripletTrackTrackCascadeBuilder if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - return tripletprocesshelpers::processSameEvent(trackSlice1, cascadeSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mCascadeHistManager, mTripletHistManagerSe, mTrackCleaner1, mCascadeCleaner, mCtrSe, mTcSe, tripletOrder); + return tripletprocesshelpers::processSameEvent(trackSlice1, cascadeSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe1, mCascadeHistManagerSe, mTripletHistManagerSe, mTrackCleaner1, mCascadeCleaner, mCtrSe, mTcSe, tripletOrder); } auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); @@ -867,7 +908,7 @@ class TripletTrackTrackCascadeBuilder } mColHistManager.template fill(col, mcCols); mCtrSe.setMagField(col.magField()); - return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, cascadeSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mTrackHistManager2, mCascadeHistManager, mTripletHistManagerSe, mTrackCleaner1, mTrackCleaner2, mCascadeCleaner, mCtrSe, mTcSe); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, cascadeSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe1, mTrackHistManagerSe2, mCascadeHistManagerSe, mTripletHistManagerSe, mTrackCleaner1, mTrackCleaner2, mCascadeCleaner, mCtrSe, mTcSe); } // data @@ -877,13 +918,13 @@ class TripletTrackTrackCascadeBuilder if (mTrack1Track2AreSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mCascadeHistManagerMe); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -891,13 +932,13 @@ class TripletTrackTrackCascadeBuilder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mCascadeHistManagerMe); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -912,13 +953,13 @@ class TripletTrackTrackCascadeBuilder if (mTrack1Track2AreSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCascadeCleaner, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCascadeCleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCascadeCleaner, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCascadeCleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCascadeCleaner, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCascadeCleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mCascadeHistManagerMe); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -926,13 +967,13 @@ class TripletTrackTrackCascadeBuilder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCascadeCleaner, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCascadeCleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCascadeCleaner, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCascadeCleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCascadeCleaner, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCascadeCleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mCascadeHistManagerMe); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -942,9 +983,12 @@ class TripletTrackTrackCascadeBuilder private: colhistmanager::CollisionHistManager mColHistManager; - trackhistmanager::TrackHistManager mTrackHistManager1; - trackhistmanager::TrackHistManager mTrackHistManager2; - cascadehistmanager::CascadeHistManager mCascadeHistManager; + trackhistmanager::TrackHistManager mTrackHistManagerSe1; + trackhistmanager::TrackHistManager mTrackHistManagerSe2; + cascadehistmanager::CascadeHistManager mCascadeHistManagerSe; + trackhistmanager::TrackHistManager mTrackHistManagerMe1; + trackhistmanager::TrackHistManager mTrackHistManagerMe2; + cascadehistmanager::CascadeHistManager mCascadeHistManagerMe; particlecleaner::ParticleCleaner mTrackCleaner1; particlecleaner::ParticleCleaner mTrackCleaner2; particlecleaner::ParticleCleaner mCascadeCleaner; @@ -958,6 +1002,7 @@ class TripletTrackTrackCascadeBuilder triplethistmanager::MixingPolicy mMixingPolicy = triplethistmanager::MixingPolicy::kVtxMult; bool mTrack1Track2AreSameSpecies = false; int mMixingDepth = 5; + int mEventStride = 1; bool mMixIdenticalParticles = false; std::mt19937 mRng; std::uniform_int_distribution<> mDist; @@ -968,6 +1013,9 @@ class TripletTrackTrackCascadeBuilder template (confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; // setup rng if necessary if (confMixing.seed.value >= 0) { @@ -1084,7 +1139,7 @@ class TripletMcParticleMcParticleMcParticleBuilder if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - return tripletprocesshelpers::processSameEventMcTruth(mcParticleSlice1, mcParticles, mcMothers, mcPartonicMothers, mcCol, mMcParticleHistManager1, mTripletHistManagerSe, mMcParticleCleaner1, mCtrSe, mTcSe, tripletOrder); + return tripletprocesshelpers::processSameEventMcTruth(mcParticleSlice1, mcParticles, mcMothers, mcPartonicMothers, mcCol, mMcParticleHistManagerSe1, mTripletHistManagerSe, mMcParticleCleaner1, mCtrSe, mTcSe, tripletOrder); } if (mParticle1Particle2AreSameSpecies) { @@ -1097,7 +1152,7 @@ class TripletMcParticleMcParticleMcParticleBuilder if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - return tripletprocesshelpers::processSameEventMcTruth(mcParticleSlice1, mcParticleSlice3, mcParticles, mcMothers, mcPartonicMothers, mcCol, mMcParticleHistManager1, mMcParticleHistManager3, mTripletHistManagerSe, mMcParticleCleaner1, mMcParticleCleaner3, mCtrSe, mTcSe, tripletOrder); + return tripletprocesshelpers::processSameEventMcTruth(mcParticleSlice1, mcParticleSlice3, mcParticles, mcMothers, mcPartonicMothers, mcCol, mMcParticleHistManagerSe1, mMcParticleHistManagerSe3, mTripletHistManagerSe, mMcParticleCleaner1, mMcParticleCleaner3, mCtrSe, mTcSe, tripletOrder); } auto mcParticleSlice1 = partition1->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, mcCol.globalIndex(), cache); @@ -1107,38 +1162,68 @@ class TripletMcParticleMcParticleMcParticleBuilder return false; } mColHistManager.template fill(mcCol); - return tripletprocesshelpers::processSameEventMcTruth(mcParticleSlice1, mcParticleSlice2, mcParticleSlice3, mcParticles, mcMothers, mcPartonicMothers, mcCol, mMcParticleHistManager1, mMcParticleHistManager2, mMcParticleHistManager3, mTripletHistManagerSe, mMcParticleCleaner1, mMcParticleCleaner2, mMcParticleCleaner3, mCtrSe, mTcSe); + return tripletprocesshelpers::processSameEventMcTruth(mcParticleSlice1, mcParticleSlice2, mcParticleSlice3, mcParticles, mcMothers, mcPartonicMothers, mcCol, mMcParticleHistManagerSe1, mMcParticleHistManagerSe2, mMcParticleHistManagerSe3, mTripletHistManagerSe, mMcParticleCleaner1, mMcParticleCleaner2, mMcParticleCleaner3, mCtrSe, mTcSe); } template void processMixedEvent(T1 const& mcCols, T2 const& mcParticles, T3 const& mcMothers, T4 const& mcPartonicMothers, T5& partition1, T6& partition2, T7& partition3, T8& cache, T9& binsVtxMult, T10& binsVtxCent, T11& binsVtxMultCent) { - // for identical species the same partition is used several times - auto& p2 = (mParticle1Particle2Particle3AreSameSpecies || mParticle1Particle2AreSameSpecies) ? partition1 : partition2; - auto& p3 = mParticle1Particle2Particle3AreSameSpecies ? partition1 : partition3; - auto& cleaner2 = (mParticle1Particle2Particle3AreSameSpecies || mParticle1Particle2AreSameSpecies) ? mMcParticleCleaner1 : mMcParticleCleaner2; - auto& cleaner3 = mParticle1Particle2Particle3AreSameSpecies ? mMcParticleCleaner1 : mMcParticleCleaner3; - - switch (mMixingPolicy) { - case triplethistmanager::MixingPolicy::kVtxMult: - tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, p2, p3, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, cleaner2, cleaner3, mCtrMe, mTcMe); - break; - case triplethistmanager::MixingPolicy::kVtxCent: - tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, p2, p3, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, cleaner2, cleaner3, mCtrMe, mTcMe); - break; - case triplethistmanager::MixingPolicy::kVtxMultCent: - tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, p2, p3, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, cleaner2, cleaner3, mCtrMe, mTcMe); - break; - default: - LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + // mMcParticleHistManagerMeN are distinct types (templated on a per-slot prefix), so -- unlike + // the partitions/cleaners above, which share a single concrete type and can be selected via a + // ternary reference -- the histogram managers must be selected via explicit branching instead + if (mParticle1Particle2Particle3AreSameSpecies) { + switch (mMixingPolicy) { + case triplethistmanager::MixingPolicy::kVtxMult: + tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, partition1, partition1, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mMcParticleCleaner1, mCtrMe, mTcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1); + break; + case triplethistmanager::MixingPolicy::kVtxCent: + tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, partition1, partition1, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mMcParticleCleaner1, mCtrMe, mTcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1); + break; + case triplethistmanager::MixingPolicy::kVtxMultCent: + tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, partition1, partition1, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mMcParticleCleaner1, mCtrMe, mTcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1); + break; + default: + LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + } + } else if (mParticle1Particle2AreSameSpecies) { + switch (mMixingPolicy) { + case triplethistmanager::MixingPolicy::kVtxMult: + tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, partition1, partition3, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mMcParticleCleaner3, mCtrMe, mTcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1, mMcParticleHistManagerMe3); + break; + case triplethistmanager::MixingPolicy::kVtxCent: + tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, partition1, partition3, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mMcParticleCleaner3, mCtrMe, mTcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1, mMcParticleHistManagerMe3); + break; + case triplethistmanager::MixingPolicy::kVtxMultCent: + tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, partition1, partition3, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mMcParticleCleaner3, mCtrMe, mTcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1, mMcParticleHistManagerMe3); + break; + default: + LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + } + } else { + switch (mMixingPolicy) { + case triplethistmanager::MixingPolicy::kVtxMult: + tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, partition2, partition3, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner2, mMcParticleCleaner3, mCtrMe, mTcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe2, mMcParticleHistManagerMe3); + break; + case triplethistmanager::MixingPolicy::kVtxCent: + tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, partition2, partition3, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner2, mMcParticleCleaner3, mCtrMe, mTcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe2, mMcParticleHistManagerMe3); + break; + case triplethistmanager::MixingPolicy::kVtxMultCent: + tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, partition2, partition3, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner2, mMcParticleCleaner3, mCtrMe, mTcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe2, mMcParticleHistManagerMe3); + break; + default: + LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + } } } private: colhistmanager::CollisionHistManager mColHistManager; - mcparticlehistmanager::McParticleHistManager mMcParticleHistManager1; - mcparticlehistmanager::McParticleHistManager mMcParticleHistManager2; - mcparticlehistmanager::McParticleHistManager mMcParticleHistManager3; + mcparticlehistmanager::McParticleHistManager mMcParticleHistManagerSe1; + mcparticlehistmanager::McParticleHistManager mMcParticleHistManagerSe2; + mcparticlehistmanager::McParticleHistManager mMcParticleHistManagerSe3; + mcparticlehistmanager::McParticleHistManager mMcParticleHistManagerMe1; + mcparticlehistmanager::McParticleHistManager mMcParticleHistManagerMe2; + mcparticlehistmanager::McParticleHistManager mMcParticleHistManagerMe3; triplethistmanager::TripletHistManager mTripletHistManagerSe; triplethistmanager::TripletHistManager mTripletHistManagerMe; closetripletrejection::CloseTripletRejectionMcParticleMcParticleMcParticle mCtrSe; @@ -1150,6 +1235,7 @@ class TripletMcParticleMcParticleMcParticleBuilder particlecleaner::ParticleCleaner mMcParticleCleaner3; triplethistmanager::MixingPolicy mMixingPolicy = triplethistmanager::MixingPolicy::kVtxMult; int mMixingDepth = 5; + int mEventStride = 1; bool mParticle1Particle2Particle3AreSameSpecies = false; bool mParticle1Particle2AreSameSpecies = false; bool mMixIdenticalParticles = false; diff --git a/PWGCF/Femto/Core/tripletCleaner.h b/PWGCF/Femto/Core/tripletCleaner.h index 85a89c77ff9..1c7f42f4cce 100644 --- a/PWGCF/Femto/Core/tripletCleaner.h +++ b/PWGCF/Femto/Core/tripletCleaner.h @@ -44,7 +44,7 @@ class TrackTrackTrackTripletCleaner : public paircleaner::BasePairCleanerisCleanParticlePair(track2, track3) && this->isCleanParticlePair(track1, track3); if (!isClean) { - this->fillBlocked(tripletHistManager.getKinematic()); + this->fillBlocked(tripletHistManager.getKinematic(this->mKinematicVariable)); } return isClean; } @@ -90,7 +90,7 @@ class TrackTrackV0TripletCleaner : public paircleaner::BasePairCleaner this->isCleanParticlePair(track2, posDaughter) && this->isCleanParticlePair(track2, negDaughter); if (!isClean) { - this->fillBlocked(tripletHistManager.getKinematic()); + this->fillBlocked(tripletHistManager.getKinematic(this->mKinematicVariable)); } return isClean; } @@ -139,7 +139,7 @@ class TrackTrackCascadeTripletCleaner : public paircleaner::BasePairCleanerisCleanParticlePair(track2, negDaughter) && this->isCleanParticlePair(track2, bachelor); if (!isClean) { - this->fillBlocked(tripletHistManager.getKinematic()); + this->fillBlocked(tripletHistManager.getKinematic(this->mKinematicVariable)); } return isClean; } @@ -182,7 +182,7 @@ class McParticleMcParticleMcParticleTripletCleaner : public paircleaner::BasePai this->isCleanParticlePair(particle2, particle3) && this->isCleanParticlePair(particle1, particle3); if (!isClean) { - this->fillBlocked(tripletHistManager.getKinematic()); + this->fillBlocked(tripletHistManager.getKinematic(this->mKinematicVariable)); } return isClean; } diff --git a/PWGCF/Femto/Core/tripletHistManager.h b/PWGCF/Femto/Core/tripletHistManager.h index b11e38905d0..b7eadec7642 100644 --- a/PWGCF/Femto/Core/tripletHistManager.h +++ b/PWGCF/Femto/Core/tripletHistManager.h @@ -11,11 +11,12 @@ /// \file tripletHistManager.h /// \brief histogram manager for triplet tasks -/// \author anton.riedel@tum.de, TU München, anton.riedel@tum.de +/// \author Anton Riedel, TU München, anton.riedel@tum.de #ifndef PWGCF_FEMTO_CORE_TRIPLETHISTMANAGER_H_ #define PWGCF_FEMTO_CORE_TRIPLETHISTMANAGER_H_ +#include "PWGCF/Femto/Core/femtoMath.h" #include "PWGCF/Femto/Core/femtoUtils.h" #include "PWGCF/Femto/Core/histManager.h" #include "PWGCF/Femto/Core/modes.h" @@ -28,7 +29,6 @@ #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include -#include #include #include #include @@ -44,9 +44,8 @@ namespace o2::analysis::femto::triplethistmanager enum TripletHist { // standard 1D kQ3, - kMt, // average mt of all pairs in the triplet - // kstar betweeen single particles - kKstar12, + kMt, // average mt of all pairs in the triplet + kKstar12, // kstar between single particles, always computed kKstar13, kKstar23, // standard 2D @@ -67,6 +66,7 @@ enum TripletHist { kQ3VsMtVsMultVsCent, kQ3VsMtVsPt1VsPt2VsPt3VsMult, kQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent, + kQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3, // higher dimensions with mass // mc kTrueQ3VsQ3, @@ -111,6 +111,7 @@ struct ConfMixing : o2::framework::ConfigurableGroup { o2::framework::ConfigurableAxis vtxBins{"vtxBins", {o2::framework::VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; o2::framework::Configurable depth{"depth", 5, "Number of events for mixing"}; o2::framework::Configurable policy{"policy", 0, "Binning policy for mixing (alywas in combination with z-vertex) -> 0: multiplicity, -> 1: centrality, -> 2: both"}; + o2::framework::Configurable eventStride{"eventStride", 1, "Only use every Nth collision as a mixing anchor (1 = use every collision). Reduces mixed-event combinatorics roughly 1/eventStride."}; o2::framework::Configurable seed{"seed", -1, "Seed to randomize particle 1/2/3 (if they are identical). Set to negative value to deactivate. Set to 0 to generate unique seed in time."}; o2::framework::Configurable particle123AreSameSpecies{"particle123AreSameSpecies", false, "Particle 1,2 and 3 are of the same species"}; o2::framework::Configurable particle12AreSameSpecies{"particle12AreSameSpecies", false, "Particle 1 and 2 are of the same species"}; @@ -123,13 +124,13 @@ struct ConfTripletBinning : o2::framework::ConfigurableGroup { std::string prefix = std::string("TripletBinning"); o2::framework::Configurable plot1D{"plot1D", true, "Enable 1D histograms"}; o2::framework::Configurable plot2D{"plot2D", true, "Enable 2D histograms"}; - o2::framework::Configurable plotKstar{"plotKstar", false, "Enable kstar histograms"}; o2::framework::Configurable plotPt1VsPt2VsPt3{"plotPt1VsPt2VsPt3", false, "Enable 3D histogram (Pt1 vs Pt2 vs Pt3)"}; o2::framework::Configurable plotQ3VsPt1VsPt2VsPt3{"plotQ3VsPt1VsPt2VsPt3", false, "Enable 4D histogram (Q3 vs Pt1 vs Pt2 vs Pt3)"}; o2::framework::Configurable plotQ3VsMtVsMult{"plotQ3VsMtVsMult", false, "Enable 3D histogram (Q3 vs Mt vs Mult)"}; o2::framework::Configurable plotQ3VsMtVsMultVsCent{"plotQ3VsMtVsMultVsCent", false, "Enable 4D histogram (Q3 vs Mt vs Mult Vs Cent)"}; o2::framework::Configurable plotQ3VsMtVsPt1VsPt2VsPt3VsMult{"plotQ3VsMtVsPt1VsPt2VsPt3VsMult", false, "Enable 6D histogram (Q3 vs Mt Vs Pt1 vs Pt2 vs Pt3 vs Mult)"}; o2::framework::Configurable plotQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent{"plotQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent", false, "Enable 7D histogram (Q3 vs Mt Vs Pt1 vs Pt2 vs Pt3 vs Mult vs Cent)"}; + o2::framework::Configurable plotQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3{"plotQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3", false, "Enable 7D histogram (Q3 vs kstar12 vs kstar13 vs kstar23 vs Pt1 vs Pt2 vs Pt3)"}; o2::framework::ConfigurableAxis q3{"q3", {{600, 0, 6}}, "q3"}; o2::framework::ConfigurableAxis kstar{"kstar", {{600, 0, 6}}, "kstar (between pairs of particles)"}; o2::framework::ConfigurableAxis mt{"mt", {{500, 0.8, 5.8}}, "mt"}; @@ -142,18 +143,24 @@ struct ConfTripletBinning : o2::framework::ConfigurableGroup { o2::framework::ConfigurableAxis mass2{"mass2", {{100, 0, 2}}, "Mass binning for particle 2 (if particle has mass getter, otherwise PDG mass)"}; o2::framework::ConfigurableAxis mass3{"mass3", {{100, 0, 2}}, "Mass binning for particle 3 (if particle has mass getter, otherwise PDG mass)"}; o2::framework::Configurable transverseMassType{"transverseMassType", static_cast(modes::TransverseMassType::kAveragePdgMass), "Type of transverse mass (0-> Average Pdg Mass, 1-> Reduced Pdg Mass, 2-> Mt from combined 4 vector)"}; - o2::framework::Configurable kinematicVariable{"kinematicVariable", static_cast(modes::KinematicVariable::kQ3), "Type of kinematic variable (2->mT, 3->Q3) for PC and CPR plots"}; }; struct ConfTripletCuts : o2::framework::ConfigurableGroup { std::string prefix = std::string("TripletCuts"); - o2::framework::Configurable q3Max{"q3Max", -1, "Maximal kstar (set to -1 to deactivate)"}; - o2::framework::Configurable q3Min{"q3Min", -1, "Minimal kstar (set to -1 to deactivate)"}; + o2::framework::Configurable q3Max{"q3Max", -1, "Maximal Q3 (set to -1 to deactivate)"}; + o2::framework::Configurable q3Min{"q3Min", -1, "Minimal Q3 (set to -1 to deactivate)"}; o2::framework::Configurable mtMax{"mtMax", -1, "Maximal mt (set to -1 to deactivate)"}; o2::framework::Configurable mtMin{"mtMin", -1, "Minimal mt (set to -1 to deactivate)"}; - o2::framework::Configurable mixOnlyCommonAncestor{"mixOnlyCommonAncestor", false, "Require pair to have common anchestor (in the same event)"}; - o2::framework::Configurable mixOnlyNonCommonAncestor{"mixOnlyNonCommonAncestor", false, "Require pair to have non-common anchestor (in the same event)"}; + o2::framework::Configurable kstar12Min{"kstar12Min", -1, "Minimal k* between particle 1 and particle 2 (set to -1 to deactivate)"}; + o2::framework::Configurable kstar12Max{"kstar12Max", -1, "Maximal k* between particle 1 and particle 2 (set to -1 to deactivate)"}; + o2::framework::Configurable kstar13Min{"kstar13Min", -1, "Minimal k* between particle 1 and particle 3 (set to -1 to deactivate)"}; + o2::framework::Configurable kstar13Max{"kstar13Max", -1, "Maximal k* between particle 1 and particle 3 (set to -1 to deactivate)"}; + o2::framework::Configurable kstar23Min{"kstar23Min", -1, "Minimal k* between particle 2 and particle 3 (set to -1 to deactivate)"}; + o2::framework::Configurable kstar23Max{"kstar23Max", -1, "Maximal k* between particle 2 and particle 3 (set to -1 to deactivate)"}; + o2::framework::Configurable mixOnlyCommonAncestor{"mixOnlyCommonAncestor", false, "Require pair to have common ancestor (in the same event)"}; + o2::framework::Configurable mixOnlyNonCommonAncestor{"mixOnlyNonCommonAncestor", false, "Require pair to have non-common ancestor (in the same event)"}; o2::framework::Configurable useMotherAsAncestor{"useMotherAsAncestor", false, "Use the first ancestor (i.e. the direct mother) instead of the partonic mother when requiring (non-)common ancestry"}; + o2::framework::Configurable kinematicVariable{"kinematicVariable", static_cast(modes::KinematicVariable::kQ3), "Type of kinematic variable (2->mT, 3->Q3) reported by the triplet-cleaner kinematic plot"}; }; // the enum gives the correct index in the array @@ -181,6 +188,7 @@ constexpr std::array, kTripletHistogramLast> // n-D with mass {kQ3VsMtVsPt1VsPt2VsPt3VsMult, o2::framework::HistType::kTHnSparseF, "hQ3VsMtVsPt1VsPt2VsPt3VsMult", "Q_{3} vs m_{T} vs p_{T,1} vs p_{T,2} vs p_{T,3} vs multiplicity; Q_{3} (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); p_{T,1} (GeV/#it{c}) ; p_{T,2} (GeV/#it{c}); p_{T,3} (GeV/#it{c}); Multiplicity;"}, {kQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent, o2::framework::HistType::kTHnSparseF, "hQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent", "Q_{3} vs m_{T} vs p_{T,1} vs p_{T,2} vs p_{T,3} vs multiplicity vs centrality; Q_{3} (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); p_{T,1} (GeV/#it{c}) ; p_{T,2} (GeV/#it{c}); p_{T,3} (GeV/#it{c}); Multiplicity; Centrality (%);"}, + {kQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3, o2::framework::HistType::kTHnSparseF, "hQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3", "Q_{3} vs k*_{12} vs k*_{13} vs k*_{23} vs p_{T,1} vs p_{T,2} vs p_{T,3}; Q_{3} (GeV/#it{c}); k*_{12} (GeV/#it{c}); k*_{13} (GeV/#it{c}); k*_{23} (GeV/#it{c}); p_{T,1} (GeV/#it{c}); p_{T,2} (GeV/#it{c}); p_{T,3} (GeV/#it{c});"}, {kTrueQ3VsQ3, o2::framework::HistType::kTH2F, "hTrueQ3VsQ3", "Q_{3,True} vs Q_{3}; Q_{3,true} (GeV/#it{c}); Q_{3} (GeV/#it{c})"}, {kTrueMtVsMt, o2::framework::HistType::kTH2F, "hTrueMtVsMt", "m_{T,True} vs m_{T}; m_{T,True} (GeV/#it{c}^{2}); m_{T} (GeV/#it{c}^{2})"}, {kTrueMultVsMult, o2::framework::HistType::kTH2F, "hTrueMultVsMult", "Multiplicity_{True} vs Multiplicity; Multiplicity_{True} ; Multiplicity"}, @@ -206,24 +214,25 @@ constexpr std::array, kTripletHistogramLast> }}; // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define TRIPLET_HIST_ANALYSIS_MAP(conf, confMixing) \ - {kQ3, {(conf).q3}}, \ - {kMt, {(conf).mt}}, \ - {kKstar12, {(conf).kstar}}, \ - {kKstar13, {(conf).kstar}}, \ - {kKstar23, {(conf).kstar}}, \ - {kPt1VsQ3, {(conf).pt1, (conf).q3}}, \ - {kPt2VsQ3, {(conf).pt2, (conf).q3}}, \ - {kPt3VsQ3, {(conf).pt3, (conf).q3}}, \ - {kQ3VsMt, {(conf).q3, (conf).mt}}, \ - {kQ3VsMult, {(conf).q3, (conf).multiplicity}}, \ - {kQ3VsCent, {(conf).q3, (conf).centrality}}, \ - {kPt1VsPt2VsPt3, {(conf).pt1, (conf).pt2, (conf).pt3}}, \ - {kQ3VsPt1VsPt2VsPt3, {(conf).q3, (conf).pt1, (conf).pt2, (conf).pt3}}, \ - {kQ3VsMtVsMult, {(conf).q3, (conf).mt, (conf).multiplicity}}, \ - {kQ3VsMtVsMultVsCent, {(conf).q3, (conf).mt, (conf).multiplicity, (conf).centrality}}, \ - {kQ3VsMtVsPt1VsPt2VsPt3VsMult, {(conf).q3, (conf).mt, (conf).pt1, (conf).pt2, (conf).pt3, (conf).multiplicity}}, \ - {kQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent, {(conf).q3, (conf).mt, (conf).pt1, (conf).pt2, (conf).pt3, (conf).multiplicity, (conf).centrality}}, +#define TRIPLET_HIST_ANALYSIS_MAP(conf, confMixing) \ + {kQ3, {(conf).q3}}, \ + {kMt, {(conf).mt}}, \ + {kKstar12, {(conf).kstar}}, \ + {kKstar13, {(conf).kstar}}, \ + {kKstar23, {(conf).kstar}}, \ + {kPt1VsQ3, {(conf).pt1, (conf).q3}}, \ + {kPt2VsQ3, {(conf).pt2, (conf).q3}}, \ + {kPt3VsQ3, {(conf).pt3, (conf).q3}}, \ + {kQ3VsMt, {(conf).q3, (conf).mt}}, \ + {kQ3VsMult, {(conf).q3, (conf).multiplicity}}, \ + {kQ3VsCent, {(conf).q3, (conf).centrality}}, \ + {kPt1VsPt2VsPt3, {(conf).pt1, (conf).pt2, (conf).pt3}}, \ + {kQ3VsPt1VsPt2VsPt3, {(conf).q3, (conf).pt1, (conf).pt2, (conf).pt3}}, \ + {kQ3VsMtVsMult, {(conf).q3, (conf).mt, (conf).multiplicity}}, \ + {kQ3VsMtVsMultVsCent, {(conf).q3, (conf).mt, (conf).multiplicity, (conf).centrality}}, \ + {kQ3VsMtVsPt1VsPt2VsPt3VsMult, {(conf).q3, (conf).mt, (conf).pt1, (conf).pt2, (conf).pt3, (conf).multiplicity}}, \ + {kQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent, {(conf).q3, (conf).mt, (conf).pt1, (conf).pt2, (conf).pt3, (conf).multiplicity, (conf).centrality}}, \ + {kQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3, {(conf).q3, (conf).kstar, (conf).kstar, (conf).kstar, (conf).pt1, (conf).pt2, (conf).pt3}}, // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define TRIPLET_HIST_MIXING_QA_MAP(confMixing) \ @@ -326,21 +335,26 @@ class TripletHistManager // transverse mass type mMtType = static_cast(ConfTripletBinning.transverseMassType.value); - mKinematicVariable = static_cast(ConfTripletBinning.kinematicVariable.value); // values for cuts mQ3Min = ConfTripletCuts.q3Min.value; mQ3Max = ConfTripletCuts.q3Max.value; mMtMin = ConfTripletCuts.mtMin.value; mMtMax = ConfTripletCuts.mtMax.value; + mKstar12Min = ConfTripletCuts.kstar12Min.value; + mKstar12Max = ConfTripletCuts.kstar12Max.value; + mKstar13Min = ConfTripletCuts.kstar13Min.value; + mKstar13Max = ConfTripletCuts.kstar13Max.value; + mKstar23Min = ConfTripletCuts.kstar23Min.value; + mKstar23Max = ConfTripletCuts.kstar23Max.value; - mPlotKstar = ConfTripletBinning.plotKstar.value; mPlotPt1VsPt2VsPt3 = ConfTripletBinning.plotPt1VsPt2VsPt3.value; mPlotQ3VsPt1VsPt2VsPt3 = ConfTripletBinning.plotQ3VsPt1VsPt2VsPt3.value; mPlotQ3VsMtVsMult = ConfTripletBinning.plotQ3VsMtVsMult.value; mPlotQ3VsMtVsMultVsCent = ConfTripletBinning.plotQ3VsMtVsMultVsCent.value; mPlotQ3VsMtVsPt1VsPt2VsPt3VsMult = ConfTripletBinning.plotQ3VsMtVsPt1VsPt2VsPt3VsMult.value; mPlotQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent = ConfTripletBinning.plotQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent.value; + mPlotQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3 = ConfTripletBinning.plotQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3.value; mPairCorrelationQa = ConfMixing.enablePairCorrelationQa.value; mEventMixingQa = ConfMixing.enableEventMixingQa.value; @@ -389,13 +403,11 @@ class TripletHistManager // set mT mMt = getMt(mParticle1, mParticle2, mParticle3); // set Q3 - mQ3 = getQ3(mParticle1, mParticle2, mParticle3); + mQ3 = femtomath::getQ3(mParticle1, mParticle2, mParticle3); - if (mPlotKstar) { - mKstar12 = getKstar(mParticle1, mParticle2); - mKstar13 = getKstar(mParticle1, mParticle3); - mKstar23 = getKstar(mParticle2, mParticle3); - } + mKstar12 = femtomath::getKstar(mParticle1, mParticle2); + mKstar13 = femtomath::getKstar(mParticle1, mParticle3); + mKstar23 = femtomath::getKstar(mParticle2, mParticle3); // if one of the particles has a mass getter, we cache the value for the filling later if constexpr (utils::HasMass) { @@ -436,7 +448,7 @@ class TripletHistManager mTrueParticle3 = ROOT::Math::PtEtaPhiMVector(particle3.pt(), particle3.eta(), particle3.phi(), mPdgMass3); mTrueMt = getMt(mTrueParticle1, mTrueParticle2, mTrueParticle3); - mTrueQ3 = getQ3(mTrueParticle1, mTrueParticle2, mTrueParticle3); + mTrueQ3 = femtomath::getQ3(mTrueParticle1, mTrueParticle2, mTrueParticle3); // in the pure mc-truth path there is no reco counterpart, so the generated values are also // stored in the reco members; they are used by getKinematic(), i.e. by the kinematic histograms @@ -444,14 +456,12 @@ class TripletHistManager mMt = mTrueMt; mQ3 = mTrueQ3; - if (mPlotKstar) { - mTrueKstar12 = getKstar(mTrueParticle1, mTrueParticle2); - mTrueKstar13 = getKstar(mTrueParticle1, mTrueParticle3); - mTrueKstar23 = getKstar(mTrueParticle2, mTrueParticle3); - mKstar12 = mTrueKstar12; - mKstar13 = mTrueKstar13; - mKstar23 = mTrueKstar23; - } + mTrueKstar12 = femtomath::getKstar(mTrueParticle1, mTrueParticle2); + mTrueKstar13 = femtomath::getKstar(mTrueParticle1, mTrueParticle3); + mTrueKstar23 = femtomath::getKstar(mTrueParticle2, mTrueParticle3); + mKstar12 = mTrueKstar12; + mKstar13 = mTrueKstar13; + mKstar23 = mTrueKstar23; } // same event: one (truth) collision for true mult/cent @@ -492,7 +502,7 @@ class TripletHistManager // set true mT mTrueMt = getMt(mTrueParticle1, mTrueParticle2, mTrueParticle3); // set true Q3 - mTrueQ3 = getQ3(mTrueParticle1, mTrueParticle2, mTrueParticle3); + mTrueQ3 = femtomath::getQ3(mTrueParticle1, mTrueParticle2, mTrueParticle3); } template @@ -534,7 +544,13 @@ class TripletHistManager return (!(mQ3Min > 0.f) || mTrueQ3 > mQ3Min) && (!(mQ3Max > 0.f) || mTrueQ3 < mQ3Max) && (!(mMtMin > 0.f) || mTrueMt > mMtMin) && - (!(mMtMax > 0.f) || mTrueMt < mMtMax); + (!(mMtMax > 0.f) || mTrueMt < mMtMax) && + (!(mKstar12Min > 0.f) || mTrueKstar12 > mKstar12Min) && + (!(mKstar12Max > 0.f) || mTrueKstar12 < mKstar12Max) && + (!(mKstar13Min > 0.f) || mTrueKstar13 > mKstar13Min) && + (!(mKstar13Max > 0.f) || mTrueKstar13 < mKstar13Max) && + (!(mKstar23Min > 0.f) || mTrueKstar23 > mKstar23Min) && + (!(mKstar23Max > 0.f) || mTrueKstar23 < mKstar23Max); } bool checkTripletCuts() const @@ -542,7 +558,13 @@ class TripletHistManager return (!(mQ3Min > 0.f) || mQ3 > mQ3Min) && (!(mQ3Max > 0.f) || mQ3 < mQ3Max) && (!(mMtMin > 0.f) || mMt > mMtMin) && - (!(mMtMax > 0.f) || mMt < mMtMax); + (!(mMtMax > 0.f) || mMt < mMtMax) && + (!(mKstar12Min > 0.f) || mKstar12 > mKstar12Min) && + (!(mKstar12Max > 0.f) || mKstar12 < mKstar12Max) && + (!(mKstar13Min > 0.f) || mKstar13 > mKstar13Min) && + (!(mKstar13Max > 0.f) || mKstar13 < mKstar13Max) && + (!(mKstar23Min > 0.f) || mKstar23 > mKstar23Min) && + (!(mKstar23Max > 0.f) || mKstar23 < mKstar23Max); } template @@ -559,9 +581,15 @@ class TripletHistManager } } - float getKinematic() const + // kinematicVariable is supplied by the caller (CPR/triplet cleaner), each of which carries its + // own Configurable for it -- this manager has no opinion of its own on which variable to report. + // kstar has no single value for a triplet, so kKstar reports the average of the three pair kstars. + float getKinematic(modes::KinematicVariable kinematicVariable) const { - switch (mKinematicVariable) { + switch (kinematicVariable) { + case modes::KinematicVariable::kKstar: + return (mKstar12 + mKstar13 + mKstar23) / 3.f; + break; case modes::KinematicVariable::kMt: return mMt; break; @@ -629,52 +657,15 @@ class TripletHistManager } private: - ROOT::Math::PxPyPzEVector getqij(ROOT::Math::PxPyPzEVector const& vi, ROOT::Math::PxPyPzEVector const& vj) - { - auto trackSum = vi + vj; - auto trackDifference = vi - vj; - const double s = trackSum.M2(); - const double scaling = (s != 0.0) ? (vi.M2() - vj.M2()) / s : 0.0; - return trackDifference - scaling * trackSum; - } - - float getQ3(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2, ROOT::Math::PtEtaPhiMVector const& part3) - { - // upfront conversion to PxPyPzEVector - const ROOT::Math::PxPyPzEVector p1(part1); - const ROOT::Math::PxPyPzEVector p2(part2); - const ROOT::Math::PxPyPzEVector p3(part3); - auto q12 = getqij(p1, p2); - auto q23 = getqij(p2, p3); - auto q31 = getqij(p3, p1); - double q = q12.M2() + q23.M2() + q31.M2(); - return static_cast(std::sqrt(std::max(0.0, -q))); - } - - float getKstar(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) - { - // Use Cartesian 4-vectors: addition/M2() become pure arithmetic - const ROOT::Math::PxPyPzEVector p1(part1); - const ROOT::Math::PxPyPzEVector p2(part2); - - // Mandelstam s = (p1 + p2)^2 - const double s = (p1 + p2).M2(); - const double m1sq = p1.M2(); - const double m2sq = p2.M2(); - - // Källen function λ(s, m1^2, m2^2) = (s - m1^2 - m2^2)² - 4*m1^2*m2^2 - const double kallen = (s - m1sq - m2sq) * (s - m1sq - m2sq) - 4.0 * m1sq * m2sq; - - // k* = 0.5 * sqrt(λ/s) - return static_cast(0.5 * std::sqrt(std::max(0.0, kallen) / s)); - } - void initAnalysis(std::map> const& Specs) { std::string analysisDir = std::string(prefix) + std::string(AnalysisDir); if (mPlot1d) { mHistogramRegistry->add(analysisDir + getHistNameV2(kQ3, HistTable), getHistDesc(kQ3, HistTable), getHistType(kQ3, HistTable), {Specs.at(kQ3)}); mHistogramRegistry->add(analysisDir + getHistNameV2(kMt, HistTable), getHistDesc(kMt, HistTable), getHistType(kMt, HistTable), {Specs.at(kMt)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kKstar12, HistTable), getHistDesc(kKstar12, HistTable), getHistType(kKstar12, HistTable), {Specs.at(kKstar12)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kKstar13, HistTable), getHistDesc(kKstar13, HistTable), getHistType(kKstar13, HistTable), {Specs.at(kKstar13)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kKstar23, HistTable), getHistDesc(kKstar23, HistTable), getHistType(kKstar23, HistTable), {Specs.at(kKstar23)}); } if (mPlot2d) { mHistogramRegistry->add(analysisDir + getHistNameV2(kPt1VsQ3, HistTable), getHistDesc(kPt1VsQ3, HistTable), getHistType(kPt1VsQ3, HistTable), {Specs.at(kPt1VsQ3)}); @@ -686,12 +677,6 @@ class TripletHistManager // TODO: implement histograms differential im mass of the particles } - if (mPlotKstar) { - mHistogramRegistry->add(analysisDir + getHistNameV2(kKstar12, HistTable), getHistDesc(kKstar12, HistTable), getHistType(kKstar12, HistTable), {Specs.at(kKstar12)}); - mHistogramRegistry->add(analysisDir + getHistNameV2(kKstar13, HistTable), getHistDesc(kKstar13, HistTable), getHistType(kKstar13, HistTable), {Specs.at(kKstar13)}); - mHistogramRegistry->add(analysisDir + getHistNameV2(kKstar23, HistTable), getHistDesc(kKstar23, HistTable), getHistType(kKstar23, HistTable), {Specs.at(kKstar23)}); - } - if (mPlotPt1VsPt2VsPt3) { mHistogramRegistry->add(analysisDir + getHistNameV2(kPt1VsPt2VsPt3, HistTable), getHistDesc(kPt1VsPt2VsPt3, HistTable), getHistType(kPt1VsPt2VsPt3, HistTable), {Specs.at(kPt1VsPt2VsPt3)}); } @@ -710,6 +695,9 @@ class TripletHistManager if (mPlotQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent) { mHistogramRegistry->add(analysisDir + getHistNameV2(kQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent, HistTable), getHistDesc(kQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent, HistTable), getHistType(kQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent, HistTable), {Specs.at(kQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent)}); } + if (mPlotQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3, HistTable), getHistDesc(kQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3, HistTable), getHistType(kQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3, HistTable), {Specs.at(kQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3)}); + } } void initMc(std::map> const& Specs) @@ -742,7 +730,7 @@ class TripletHistManager } } - // pure mc-truth triplet (kMc without kReco) — reuses the mPlot1d/mPlot2d/mPlotKstar flags, + // pure mc-truth triplet (kMc without kReco) — reuses the mPlot1d/mPlot2d flags, // since these are histogram-content flags, not mode flags void initMcTruth(std::map> const& Specs) { @@ -750,6 +738,9 @@ class TripletHistManager if (mPlot1d) { mHistogramRegistry->add(mcDir + getHistNameV2(kTrueQ3, HistTable), getHistDesc(kTrueQ3, HistTable), getHistType(kTrueQ3, HistTable), {Specs.at(kTrueQ3)}); mHistogramRegistry->add(mcDir + getHistNameV2(kTrueMt, HistTable), getHistDesc(kTrueMt, HistTable), getHistType(kTrueMt, HistTable), {Specs.at(kTrueMt)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueKstar12, HistTable), getHistDesc(kTrueKstar12, HistTable), getHistType(kTrueKstar12, HistTable), {Specs.at(kTrueKstar12)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueKstar13, HistTable), getHistDesc(kTrueKstar13, HistTable), getHistType(kTrueKstar13, HistTable), {Specs.at(kTrueKstar13)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueKstar23, HistTable), getHistDesc(kTrueKstar23, HistTable), getHistType(kTrueKstar23, HistTable), {Specs.at(kTrueKstar23)}); } if (mPlot2d) { mHistogramRegistry->add(mcDir + getHistNameV2(kTruePt1VsTrueQ3, HistTable), getHistDesc(kTruePt1VsTrueQ3, HistTable), getHistType(kTruePt1VsTrueQ3, HistTable), {Specs.at(kTruePt1VsTrueQ3)}); @@ -759,11 +750,6 @@ class TripletHistManager mHistogramRegistry->add(mcDir + getHistNameV2(kTrueQ3VsTrueMult, HistTable), getHistDesc(kTrueQ3VsTrueMult, HistTable), getHistType(kTrueQ3VsTrueMult, HistTable), {Specs.at(kTrueQ3VsTrueMult)}); mHistogramRegistry->add(mcDir + getHistNameV2(kTrueQ3VsTrueCent, HistTable), getHistDesc(kTrueQ3VsTrueCent, HistTable), getHistType(kTrueQ3VsTrueCent, HistTable), {Specs.at(kTrueQ3VsTrueCent)}); } - if (mPlotKstar) { - mHistogramRegistry->add(mcDir + getHistNameV2(kTrueKstar12, HistTable), getHistDesc(kTrueKstar12, HistTable), getHistType(kTrueKstar12, HistTable), {Specs.at(kTrueKstar12)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kTrueKstar13, HistTable), getHistDesc(kTrueKstar13, HistTable), getHistType(kTrueKstar13, HistTable), {Specs.at(kTrueKstar13)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kTrueKstar23, HistTable), getHistDesc(kTrueKstar23, HistTable), getHistType(kTrueKstar23, HistTable), {Specs.at(kTrueKstar23)}); - } } void fillMcTruth() @@ -771,6 +757,9 @@ class TripletHistManager if (mPlot1d) { mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueQ3, HistTable)), mTrueQ3); mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueMt, HistTable)), mTrueMt); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueKstar12, HistTable)), mTrueKstar12); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueKstar13, HistTable)), mTrueKstar13); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueKstar23, HistTable)), mTrueKstar23); } if (mPlot2d) { mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTruePt1VsTrueQ3, HistTable)), mTrueParticle1.Pt(), mTrueQ3); @@ -780,11 +769,6 @@ class TripletHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueQ3VsTrueMult, HistTable)), mTrueQ3, mTrueMult); mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueQ3VsTrueCent, HistTable)), mTrueQ3, mTrueCent); } - if (mPlotKstar) { - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueKstar12, HistTable)), mTrueKstar12); - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueKstar13, HistTable)), mTrueKstar13); - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueKstar23, HistTable)), mTrueKstar23); - } } void fillAnalysis() @@ -792,6 +776,9 @@ class TripletHistManager if (mPlot1d) { mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kQ3, HistTable)), mQ3); mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kMt, HistTable)), mMt); + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstar12, HistTable)), mKstar12); + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstar13, HistTable)), mKstar13); + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstar23, HistTable)), mKstar23); } if (mPlot2d) { mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kPt1VsQ3, HistTable)), mParticle1.Pt(), mQ3); @@ -802,12 +789,6 @@ class TripletHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kQ3VsCent, HistTable)), mQ3, mCent); } - if (mPlotKstar) { - mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstar12, HistTable)), mKstar12); - mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstar13, HistTable)), mKstar13); - mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstar23, HistTable)), mKstar23); - } - if (mPlotPt1VsPt2VsPt3) { mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kPt1VsPt2VsPt3, HistTable)), mParticle1.Pt(), mParticle2.Pt(), mParticle3.Pt()); } @@ -826,6 +807,9 @@ class TripletHistManager if (mPlotQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent) { mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent, HistTable)), mQ3, mMt, mParticle1.Pt(), mParticle2.Pt(), mParticle3.Pt(), mMult, mCent); } + if (mPlotQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3, HistTable)), mQ3, mKstar12, mKstar13, mKstar23, mParticle1.Pt(), mParticle2.Pt(), mParticle3.Pt()); + } } void fillMc() @@ -840,33 +824,11 @@ class TripletHistManager } } - double getMt(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) - { - auto sum = part1 + part2; - double mt = 0.; - double averageMass = 0.; - double reducedMass = 0.; - switch (mMtType) { - case modes::TransverseMassType::kAveragePdgMass: - averageMass = 0.5 * (part1.M() + part2.M()); - mt = std::hypot(0.5 * sum.Pt(), averageMass); - break; - case modes::TransverseMassType::kReducedPdgMass: - reducedMass = 2. * (part1.M() * part2.M() / (part1.M() + part2.M())); - mt = std::hypot(0.5 * sum.Pt(), reducedMass); - break; - case modes::TransverseMassType::kMt4Vector: - mt = 0.5 * sum.Mt(); - break; - default: - LOG(fatal) << "Invalid transverse mass type, breaking..."; - } - return mt; - } - float getMt(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2, ROOT::Math::PtEtaPhiMVector const& part3) { - return static_cast((getMt(part1, part2) + getMt(part2, part3) + getMt(part1, part3)) / 3.); + // all three pairwise combinations are intentional, not a mixed-up argument order + // NOLINTNEXTLINE(readability-suspicious-call-argument) + return (femtomath::getMt(part1, part2, mMtType) + femtomath::getMt(part2, part3, mMtType) + femtomath::getMt(part1, part3, mMtType)) / 3.f; } o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; @@ -875,7 +837,6 @@ class TripletHistManager double mPdgMass3 = 0.; modes::TransverseMassType mMtType = modes::TransverseMassType::kAveragePdgMass; - modes::KinematicVariable mKinematicVariable = modes::KinematicVariable::kQ3; int mAbsCharge1 = 1; int mAbsCharge2 = 1; @@ -913,18 +874,24 @@ class TripletHistManager float mQ3Max = -1.f; float mMtMin = -1.f; float mMtMax = -1.f; + float mKstar12Min = -1.f; + float mKstar12Max = -1.f; + float mKstar13Min = -1.f; + float mKstar13Max = -1.f; + float mKstar23Min = -1.f; + float mKstar23Max = -1.f; // flags bool mPlot1d = true; bool mPlot2d = true; - bool mPlotKstar = false; bool mPlotPt1VsPt2VsPt3 = false; bool mPlotQ3VsPt1VsPt2VsPt3 = false; bool mPlotQ3VsMtVsMult = false; bool mPlotQ3VsMtVsMultVsCent = false; bool mPlotQ3VsMtVsPt1VsPt2VsPt3VsMult = false; bool mPlotQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent = false; + bool mPlotQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3 = false; // mixing qa bool mPairCorrelationQa = false; diff --git a/PWGCF/Femto/Core/tripletProcessHelpers.h b/PWGCF/Femto/Core/tripletProcessHelpers.h index a5d7b812305..df75ace9ef9 100644 --- a/PWGCF/Femto/Core/tripletProcessHelpers.h +++ b/PWGCF/Femto/Core/tripletProcessHelpers.h @@ -754,7 +754,10 @@ template + typename T11, + typename T12, + typename T13, + typename T14> void processMixedEvent(T1 const& Collisions, T2& Partition1, T3& Partition2, @@ -765,12 +768,20 @@ void processMixedEvent(T1 const& Collisions, T8 const& depth, T9& TripletHistManager, T10& CtrManager, - T11& TcManager) + T11& TcManager, + int eventStride, + T12& ParticleHistManagerMe1, + T13& ParticleHistManagerMe2, + T14& ParticleHistManagerMe3) { int64_t lastCollisionIndex1 = -1; int64_t lastCollisionIndex2 = -1; int windowSizeRaw = 0; int windowSizeEffective = 0; + // counts actual outer-anchor windows encountered (not raw collision IDs, which may have gaps + // from upstream event selection) so "every Nth anchor" is evenly spaced regardless of such gaps + int64_t windowCounter = -1; + bool skipThisWindow = false; // collision1 stays fixed across the outer mixing window, and collision2 stays fixed across each inner sub-window (it only advances once collision3 wraps // back to its start Both slices are therefore materialized once per window/sub-window and reused, instead of being re-sliced, i.e. a fresh arrow Slice + selection copy @@ -781,14 +792,25 @@ void processMixedEvent(T1 const& Collisions, // outer window if (collision1.globalIndex() != lastCollisionIndex1) { - if (lastCollisionIndex1 != -1) { + if (lastCollisionIndex1 != -1 && !skipThisWindow) { TripletHistManager.fillMixingQaMePerMixingBin(windowSizeRaw, windowSizeEffective); } windowSizeRaw = 0; windowSizeEffective = 0; lastCollisionIndex1 = collision1.globalIndex(); lastCollisionIndex2 = -1; // force sliceParticle2 to refresh below - sliceParticle1.emplace(Partition1->sliceByCached(o2::aod::femtobase::stored::fColId, collision1.globalIndex(), cache)); + ++windowCounter; + // skip entire outer-anchor windows to reduce mixed-event combinatorics without truncating + // any single event's particle content -- every anchor that IS processed still gets its + // full `depth` worth of mixing partners + skipThisWindow = eventStride > 1 && windowCounter % eventStride != 0; + if (!skipThisWindow) { + sliceParticle1.emplace(Partition1->sliceByCached(o2::aod::femtobase::stored::fColId, collision1.globalIndex(), cache)); + } + } + + if (skipThisWindow) { + continue; } // inner sub-window @@ -839,6 +861,10 @@ void processMixedEvent(T1 const& Collisions, hasValidTriplet = true; TripletHistManager.trackParticlesPerEvent(p1, p2, p3); TripletHistManager.template fill(); + // single-particle histograms for particles that make it into an accepted mixed-event triplet + ParticleHistManagerMe1.template fill(p1, TrackTable); + ParticleHistManagerMe2.template fill(p2, TrackTable); + ParticleHistManagerMe3.template fill(p3, TrackTable); } } @@ -874,7 +900,10 @@ template + typename T18, + typename T19, + typename T20, + typename T21> void processMixedEvent(T1 const& Collisions, T2 const& mcCollisions, T3& Partition1, @@ -892,12 +921,20 @@ void processMixedEvent(T1 const& Collisions, T15& Cleaner2, T16& Cleaner3, T17& CtrManager, - T18& TcManager) + T18& TcManager, + int eventStride, + T19& ParticleHistManagerMe1, + T20& ParticleHistManagerMe2, + T21& ParticleHistManagerMe3) { int64_t lastCollisionIndex1 = -1; int64_t lastCollisionIndex2 = -1; int windowSizeRaw = 0; int windowSizeEffective = 0; + // counts actual outer-anchor windows encountered (not raw collision IDs, which may have gaps + // from upstream event selection) so "every Nth anchor" is evenly spaced regardless of such gaps + int64_t windowCounter = -1; + bool skipThisWindow = false; // collision1 stays fixed across the outer mixing window, and collision2 stays fixed across each inner sub-window (it only advances once collision3 wraps // back to its start Both slices are therefore materialized once per window/sub-window and reused, instead of being re-sliced, i.e. a fresh arrow Slice + selection copy @@ -908,14 +945,25 @@ void processMixedEvent(T1 const& Collisions, // outer window if (collision1.globalIndex() != lastCollisionIndex1) { - if (lastCollisionIndex1 != -1) { + if (lastCollisionIndex1 != -1 && !skipThisWindow) { TripletHistManager.fillMixingQaMePerMixingBin(windowSizeRaw, windowSizeEffective); } windowSizeRaw = 0; windowSizeEffective = 0; lastCollisionIndex1 = collision1.globalIndex(); lastCollisionIndex2 = -1; // force sliceParticle2 to refresh below - sliceParticle1.emplace(Partition1->sliceByCached(o2::aod::femtobase::stored::fColId, collision1.globalIndex(), cache)); + ++windowCounter; + // skip entire outer-anchor windows to reduce mixed-event combinatorics without truncating + // any single event's particle content -- every anchor that IS processed still gets its + // full `depth` worth of mixing partners + skipThisWindow = eventStride > 1 && windowCounter % eventStride != 0; + if (!skipThisWindow) { + sliceParticle1.emplace(Partition1->sliceByCached(o2::aod::femtobase::stored::fColId, collision1.globalIndex(), cache)); + } + } + + if (skipThisWindow) { + continue; } // inner sub-window @@ -973,6 +1021,10 @@ void processMixedEvent(T1 const& Collisions, hasValidTriplet = true; TripletHistManager.trackParticlesPerEvent(p1, p2, p3); TripletHistManager.template fill(); + // single-particle histograms for particles that make it into an accepted mixed-event triplet + ParticleHistManagerMe1.template fill(p1, TrackTable); + ParticleHistManagerMe2.template fill(p2, TrackTable); + ParticleHistManagerMe3.template fill(p3, TrackTable); } } @@ -993,7 +1045,8 @@ void processMixedEvent(T1 const& Collisions, // mc collisions carry no magnetic field, so there is no magnetic field check here template + typename T9, typename T10, typename T11, typename T12, typename T13, typename T14, typename T15, typename T16, + typename T17, typename T18, typename T19> void processMixedEventMcTruth(T1 const& McCollisions, T2& Partition1, T3& Partition2, @@ -1009,12 +1062,20 @@ void processMixedEventMcTruth(T1 const& McCollisions, T13& ParticleCleaner2, T14& ParticleCleaner3, T15& CtrManager, - T16& TcManager) + T16& TcManager, + int eventStride, + T17& ParticleHistManagerMe1, + T18& ParticleHistManagerMe2, + T19& ParticleHistManagerMe3) { int64_t lastCollisionIndex1 = -1; int64_t lastCollisionIndex2 = -1; int windowSizeRaw = 0; int windowSizeEffective = 0; + // counts actual outer-anchor windows encountered (not raw collision IDs, which may have gaps + // from upstream event selection) so "every Nth anchor" is evenly spaced regardless of such gaps + int64_t windowCounter = -1; + bool skipThisWindow = false; std::optionalsliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, 0, cache))> sliceParticle1; std::optionalsliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, 0, cache))> sliceParticle2; @@ -1023,14 +1084,25 @@ void processMixedEventMcTruth(T1 const& McCollisions, // outer window if (collision1.globalIndex() != lastCollisionIndex1) { - if (lastCollisionIndex1 != -1) { + if (lastCollisionIndex1 != -1 && !skipThisWindow) { TripletHistManager.fillMixingQaMePerMixingBin(windowSizeRaw, windowSizeEffective); } windowSizeRaw = 0; windowSizeEffective = 0; lastCollisionIndex1 = collision1.globalIndex(); lastCollisionIndex2 = -1; // force sliceParticle2 to refresh below - sliceParticle1.emplace(Partition1->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, collision1.globalIndex(), cache)); + ++windowCounter; + // skip entire outer-anchor windows to reduce mixed-event combinatorics without truncating + // any single event's particle content -- every anchor that IS processed still gets its + // full `depth` worth of mixing partners + skipThisWindow = eventStride > 1 && windowCounter % eventStride != 0; + if (!skipThisWindow) { + sliceParticle1.emplace(Partition1->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, collision1.globalIndex(), cache)); + } + } + + if (skipThisWindow) { + continue; } // inner sub-window @@ -1077,6 +1149,10 @@ void processMixedEventMcTruth(T1 const& McCollisions, hasValidTriplet = true; TripletHistManager.trackParticlesPerEvent(p1, p2, p3); TripletHistManager.template fill(); + // single-particle histograms for particles that make it into an accepted mixed-event triplet + ParticleHistManagerMe1.fill(p1, mcMothers, mcPartonicMothers); + ParticleHistManagerMe2.fill(p2, mcMothers, mcPartonicMothers); + ParticleHistManagerMe3.fill(p3, mcMothers, mcPartonicMothers); } } diff --git a/PWGCF/Femto/Core/twoPhotonResonanceBuilder.h b/PWGCF/Femto/Core/twoPhotonResonanceBuilder.h new file mode 100644 index 00000000000..5883ca23411 --- /dev/null +++ b/PWGCF/Femto/Core/twoPhotonResonanceBuilder.h @@ -0,0 +1,246 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file twoPhotonResonanceBuilder.h +/// \brief two photon resonance builder (pi0, eta, ... -> gamma gamma) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_TWOPHOTONRESONANCEBUILDER_H_ +#define PWGCF_FEMTO_CORE_TWOPHOTONRESONANCEBUILDER_H_ + +#include "PWGCF/Femto/Core/dataTypes.h" +#include "PWGCF/Femto/Core/femtoUtils.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include "Common/Core/RecoDecay.h" + +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include + +#include + +namespace o2::analysis::femto::twophotonresonancebuilder +{ +template +struct ConfTwoPhotonResonanceFilters : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; + o2::framework::Configurable ptMax{"ptMax", 10.f, "Maximum pT"}; + o2::framework::Configurable etaMin{"etaMin", -0.9f, "Minimum eta"}; + o2::framework::Configurable etaMax{"etaMax", 0.9f, "Maximum eta"}; + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; + o2::framework::Configurable massMin{"massMin", 0.f, "Minimum invariant mass for the resonance"}; + o2::framework::Configurable massMax{"massMax", 1.f, "Maximum invariant mass for the resonance"}; +}; +constexpr const char PrefixPi0Filters[] = "Pi0Filters1"; +constexpr const char PrefixEtaFilters[] = "EtaFilters1"; + +using ConfPi0Filters = ConfTwoPhotonResonanceFilters; +using ConfEtaFilters = ConfTwoPhotonResonanceFilters; + +// selection used downstream (QA/pairing) to re-select already produced resonances by mass window + +// daughter mask. unlike TWOTRACKRESONANCE_DEFAULT_SELECTION there is no pos/neg split and no +// momentum-threshold PID switch: photon daughters are unordered and have no momentum-dependent PID +// regime. there is also just a single dauMask (not one per leg): both legs are plain photons with +// the same selection requirement, so the candidate only ever stores/needs the AND of both legs' +// masks (see femtotwophotonresonances::MaskDau in FemtoTables.h) -- no per-leg distinction to select on. +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TWOPHOTONRESONANCE_DEFAULT_SELECTION(defaultMassMin, defaultMassMax, defaultPdgCodeAbs) \ + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; \ + o2::framework::Configurable ptMax{"ptMax", 10.f, "Maximum pT"}; \ + o2::framework::Configurable etaMin{"etaMin", -0.9f, "Minimum eta"}; \ + o2::framework::Configurable etaMax{"etaMax", 0.9f, "Maximum eta"}; \ + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; \ + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; \ + o2::framework::Configurable massMin{"massMin", (defaultMassMin), "Minimum invariant mass for the resonance"}; \ + o2::framework::Configurable massMax{"massMax", (defaultMassMax), "Maximum invariant mass for the resonance"}; \ + o2::framework::Configurable pdgCodeAbs{"pdgCodeAbs", (defaultPdgCodeAbs), "Absolute PDG code of the resonance, used to set its mass for kstar/kt/mt (not the reconstructed two-photon mass)"}; \ + o2::framework::Configurable dauMask{"dauMask", 0, "Bitmask required for both photon daughters"}; + +struct ConfPi0Selection : o2::framework::ConfigurableGroup { + std::string prefix = std::string("Pi0Selection1"); + TWOPHOTONRESONANCE_DEFAULT_SELECTION(0.1f, 0.17f, kPi0) +}; + +struct ConfEtaSelection : o2::framework::ConfigurableGroup { + std::string prefix = std::string("EtaSelection1"); + TWOPHOTONRESONANCE_DEFAULT_SELECTION(0.4f, 0.7f, o2::constants::physics::kEta) +}; + +#undef TWOPHOTONRESONANCE_DEFAULT_SELECTION + +struct TwoPhotonResonanceBuilderProducts : o2::framework::ProducesGroup { + o2::framework::Produces producedPi0s; + o2::framework::Produces producedPi0Masks; + o2::framework::Produces producedEtas; + o2::framework::Produces producedEtaMasks; +}; + +struct ConfTwoPhotonResonanceTables : o2::framework::ConfigurableGroup { + std::string prefix = std::string("TwoPhotonResonanceTables"); + o2::framework::Configurable producePi0s{"producePi0s", -1, "Produce Pi0s (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable producePi0Masks{"producePi0Masks", -1, "Produce Pi0Masks (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceEtas{"produceEtas", -1, "Produce Etas (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceEtaMasks{"produceEtaMasks", -1, "Produce EtaMasks (-1: auto; 0 off; 1 on)"}; +}; + +/// \brief Builder for resonances reconstructed from two PCM photons (pi0, eta, ... -> gamma gamma) +/// +/// Unlike TwoTrackResonanceBuilder, there is no pos/neg daughter distinction (photons are their own +/// antiparticle) and no momentum-threshold PID switch (photon selection has no momentum-dependent PID +/// regime the way track PID does), so daughter combinatorics run over a single photon partition using +/// CombinationsStrictlyUpperIndexPolicy to avoid self-pairing/double-counting. +template +class TwoPhotonResonanceBuilder +{ + public: + TwoPhotonResonanceBuilder() = default; + ~TwoPhotonResonanceBuilder() = default; + + template + void init(T1& confFilter, T2& confTable, T3& initContext) + { + mMassMin = confFilter.massMin.value; + mMassMax = confFilter.massMax.value; + mPtMin = confFilter.ptMin.value; + mPtMax = confFilter.ptMax.value; + mEtaMin = confFilter.etaMin.value; + mEtaMax = confFilter.etaMax.value; + mPhiMin = confFilter.phiMin.value; + mPhiMax = confFilter.phiMax.value; + + if constexpr (modes::isEqual(resoType, modes::TwoPhotonResonance::kPi0)) { + LOG(info) << "Initialize femto Pi0 builder..."; + mProducePi0s = utils::enableTable("FPi0s_001", confTable.producePi0s.value, initContext); + mProducePi0Masks = utils::enableTable("FPi0Masks_001", confTable.producePi0Masks.value, initContext); + } + if constexpr (modes::isEqual(resoType, modes::TwoPhotonResonance::kEta)) { + LOG(info) << "Initialize femto Eta builder..."; + mProduceEtas = utils::enableTable("FEtas_001", confTable.produceEtas.value, initContext); + mProduceEtaMasks = utils::enableTable("FEtaMasks_001", confTable.produceEtaMasks.value, initContext); + } + + if (mProducePi0s || mProducePi0Masks || mProduceEtas || mProduceEtaMasks) { + mFillAnyTable = true; + } else { + LOG(info) << "No tables configured, Selection object will not be configured..."; + LOG(info) << "Initialization done..."; + return; + } + LOG(info) << "Initialization done..."; + } + + template + void fillResonances(T1 const& col, T2& resonanceProducts, T3& photonPartition, T4& cache) + { + if (!mFillAnyTable) { + return; + } + auto photonSlice = photonPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + for (auto const& [dau1, dau2] : o2::soa::combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(photonSlice, photonSlice))) { + this->fillResonance(col, dau1, dau2, resonanceProducts); + } + } + + private: + template + void reconstructResonance(T1 const& dau1, T2 const& dau2) + { + ROOT::Math::PtEtaPhiMVector vecDau1{dau1.pt(), dau1.eta(), dau1.phi(), 0.f}; + ROOT::Math::PtEtaPhiMVector vecDau2{dau2.pt(), dau2.eta(), dau2.phi(), 0.f}; + ROOT::Math::PtEtaPhiMVector vecResonance = vecDau1 + vecDau2; + + mPt = vecResonance.Pt(); + mEta = vecResonance.Eta(); + mPhi = RecoDecay::constrainAngle(vecResonance.Phi()); + mMass = vecResonance.M(); + } + + [[nodiscard]] bool checkFilters() const + { + return ((mMass > mMassMin && mMass < mMassMax) && + (mPt > mPtMin && mPt < mPtMax) && + (mEta > mEtaMin && mEta < mEtaMax) && + (mPhi >= mPhiMin && mPhi < mPhiMax)); + } + + template + void fillResonance(T1 const& col, T2 const& dau1, T3 const& dau2, T4& resonanceProducts) + { + reconstructResonance(dau1, dau2); + if (!checkFilters()) { + return; + } + + if constexpr (modes::isEqual(resoType, modes::TwoPhotonResonance::kPi0)) { + if (mProducePi0s) { + resonanceProducts.producedPi0s(col.globalIndex(), + mPt, + mEta, + mPhi, + mMass, + dau1.globalIndex(), + dau2.globalIndex()); + } + if (mProducePi0Masks) { + resonanceProducts.producedPi0Masks(dau1.mask() & dau2.mask()); + } + } + if constexpr (modes::isEqual(resoType, modes::TwoPhotonResonance::kEta)) { + if (mProduceEtas) { + resonanceProducts.producedEtas(col.globalIndex(), + mPt, + mEta, + mPhi, + mMass, + dau1.globalIndex(), + dau2.globalIndex()); + } + if (mProduceEtaMasks) { + resonanceProducts.producedEtaMasks(dau1.mask() & dau2.mask()); + } + } + } + + // cached kinematics of the resonance + float mPt = 0; + float mEta = 0; + float mPhi = 0; + float mMass = 0; + + float mMassMin = 0.f; + float mMassMax = 0.f; + float mPtMin = 0.f; + float mPtMax = 0.f; + float mEtaMin = 0.f; + float mEtaMax = 0.f; + float mPhiMin = 0.f; + float mPhiMax = 0.f; + + bool mFillAnyTable = false; + bool mProducePi0s = false; + bool mProducePi0Masks = false; + bool mProduceEtas = false; + bool mProduceEtaMasks = false; +}; + +} // namespace o2::analysis::femto::twophotonresonancebuilder +#endif // PWGCF_FEMTO_CORE_TWOPHOTONRESONANCEBUILDER_H_ diff --git a/PWGCF/Femto/Core/twoPhotonResonanceHistManager.h b/PWGCF/Femto/Core/twoPhotonResonanceHistManager.h new file mode 100644 index 00000000000..52e19c78bba --- /dev/null +++ b/PWGCF/Femto/Core/twoPhotonResonanceHistManager.h @@ -0,0 +1,214 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file twoPhotonResonanceHistManager.h +/// \brief histogram manager for resonances built from two photons (pi0, eta, ...) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_TWOPHOTONRESONANCEHISTMANAGER_H_ +#define PWGCF_FEMTO_CORE_TWOPHOTONRESONANCEHISTMANAGER_H_ + +#include "PWGCF/Femto/Core/histManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/photonHistManager.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace o2::analysis::femto::twophotonresonancehistmanager +{ +enum TwoPhotonResonanceHist { + // analysis + kPt, + kEta, + kPhi, + kMass, + kPtVsMass, + // 2d qa + kPtVsEta, + kPtVsPhi, + kPhiVsEta, + kTwoPhotonResonanceHistLast +}; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TWOPHOTONRESONANCE_DEFAULT_BINNING(defaultMassMin, defaultMassMax) \ + o2::framework::ConfigurableAxis pt{"pt", {{600, 0, 6}}, "Pt"}; \ + o2::framework::ConfigurableAxis eta{"eta", {{300, -1.5, 1.5}}, "Eta"}; \ + o2::framework::ConfigurableAxis phi{"phi", {{720, 0, 1.f * o2::constants::math::TwoPI}}, "Phi"}; \ + o2::framework::ConfigurableAxis mass{"mass", {{200, (defaultMassMin), (defaultMassMax)}}, "Mass"}; + +struct ConfPi0Binning : o2::framework::ConfigurableGroup { + std::string prefix = std::string("Pi0Binning"); + TWOPHOTONRESONANCE_DEFAULT_BINNING(0.f, 0.3f) +}; + +struct ConfEtaBinning : o2::framework::ConfigurableGroup { + std::string prefix = std::string("EtaBinning"); + TWOPHOTONRESONANCE_DEFAULT_BINNING(0.2f, 0.9f) +}; +#undef TWOPHOTONRESONANCE_DEFAULT_BINNING + +constexpr std::array, kTwoPhotonResonanceHistLast> HistTable = { + {{kPt, o2::framework::HistType::kTH1F, "hPt", "Transverse Momentum; p_{T} (GeV/#it{c}); Entries"}, + {kEta, o2::framework::HistType::kTH1F, "hEta", "Pseudorapdity; #eta; Entries"}, + {kPhi, o2::framework::HistType::kTH1F, "hPhi", "Azimuthal angle; #varphi; Entries"}, + {kMass, o2::framework::HistType::kTH1F, "hMass", "Invariant mass; m_{#gamma#gamma} (GeV/#it{c}^{2}); Entries"}, + {kPtVsMass, o2::framework::HistType::kTH2F, "hPtVsMass", "p_{T} vs invariant mass; p_{T} (GeV/#it{c}); m_{#gamma#gamma} (GeV/#it{c}^{2})"}, + {kPtVsEta, o2::framework::HistType::kTH2F, "hPtVsEta", "p_{T} vs #eta; p_{T} (GeV/#it{c}) ; #eta"}, + {kPtVsPhi, o2::framework::HistType::kTH2F, "hPtVsPhi", "p_{T} vs #varphi;p_{T} (GeV/#it{c});#varphi"}, + {kPhiVsEta, o2::framework::HistType::kTH2F, "hPhiVsEta", "#varphi vs #eta; #varphi ; #eta"}}, +}; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TWOPHOTONRESONANCE_HIST_ANALYSIS_MAP(conf) \ + {kPt, {(conf).pt}}, \ + {kEta, {(conf).eta}}, \ + {kPhi, {(conf).phi}}, \ + {kMass, {(conf).mass}}, \ + {kPtVsMass, {(conf).pt, (conf).mass}}, + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TWOPHOTONRESONANCE_HIST_QA_MAP(conf) \ + {kPtVsEta, {(conf).pt, (conf).eta}}, \ + {kPtVsPhi, {(conf).pt, (conf).phi}}, \ + {kPhiVsEta, {(conf).phi, (conf).eta}}, \ + {kPtVsMass, {(conf).pt, (conf).mass}}, + +template +std::map> makeTwoPhotonResonanceHistSpecMap(const T& confBinningAnalysis) +{ + return std::map>{ + TWOPHOTONRESONANCE_HIST_ANALYSIS_MAP(confBinningAnalysis)}; +} + +template +auto makeTwoPhotonResonanceQaHistSpecMap(const T& confBinningAnalysis) +{ + return std::map>{ + TWOPHOTONRESONANCE_HIST_ANALYSIS_MAP(confBinningAnalysis) + TWOPHOTONRESONANCE_HIST_QA_MAP(confBinningAnalysis)}; +} + +#undef TWOPHOTONRESONANCE_HIST_ANALYSIS_MAP +#undef TWOPHOTONRESONANCE_HIST_QA_MAP + +// used only by the standalone QA task +constexpr char PrefixPi0[] = "Pi0/"; +constexpr char PrefixEta[] = "Eta/"; +// same-event sibling, used only by a pair builder's same-event resonance manager +constexpr char PrefixPi0Se[] = "Pi0/SE/"; +constexpr char PrefixEtaSe[] = "Eta/SE/"; +// mixed-event sibling, used only by a pair builder's mixed-event resonance manager +constexpr char PrefixPi0Me[] = "Pi0/ME/"; +constexpr char PrefixEtaMe[] = "Eta/ME/"; + +constexpr std::string_view AnalysisDir = "Analysis/"; +constexpr std::string_view QaDir = "QA/"; + +/// \class TwoPhotonResonanceHistManager +/// \brief Histogram manager for resonances built from two PCM photons (reco + QA only, no MC). +/// Both daughters share the same PhotonHistManager-based sub-manager treatment: there is no +/// pos/neg distinction, nor a PDG-dependent daughter lookup the way TwoTrackResonanceHistManager +/// needs (photon daughters are always massless, regardless of the mother species). +template +class TwoPhotonResonanceHistManager +{ + public: + TwoPhotonResonanceHistManager() = default; + ~TwoPhotonResonanceHistManager() = default; + + template + void init(o2::framework::HistogramRegistry* registry, + std::map> const& ResoSpecs, + std::map> const& DauSpecs, + T1 const& ConfDauQaBinning) + { + mHistogramRegistry = registry; + mDau1Manager.template init(registry, DauSpecs, ConfDauQaBinning); + mDau2Manager.template init(registry, DauSpecs, ConfDauQaBinning); + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { + initAnalysis(ResoSpecs); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + initQa(ResoSpecs); + } + } + + template + void fill(T1 const& resonance, T2 const& photons) + { + auto dau1 = photons.rawIteratorAt(resonance.dau1Id() - photons.offset()); + mDau1Manager.template fill(dau1); + auto dau2 = photons.rawIteratorAt(resonance.dau2Id() - photons.offset()); + mDau2Manager.template fill(dau2); + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { + fillAnalysis(resonance); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + fillQa(resonance); + } + } + + private: + void initAnalysis(std::map> const& ResoSpecs) + { + std::string analysisDir = std::string(resoPrefix) + std::string(AnalysisDir); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPt, HistTable), getHistDesc(kPt, HistTable), getHistType(kPt, HistTable), {ResoSpecs.at(kPt)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kEta, HistTable), getHistDesc(kEta, HistTable), getHistType(kEta, HistTable), {ResoSpecs.at(kEta)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPhi, HistTable), getHistDesc(kPhi, HistTable), getHistType(kPhi, HistTable), {ResoSpecs.at(kPhi)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kMass, HistTable), getHistDesc(kMass, HistTable), getHistType(kMass, HistTable), {ResoSpecs.at(kMass)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPtVsMass, HistTable), getHistDesc(kPtVsMass, HistTable), getHistType(kPtVsMass, HistTable), {ResoSpecs.at(kPtVsMass)}); + } + + void initQa(std::map> const& ResoSpecs) + { + std::string qaDir = std::string(resoPrefix) + std::string(QaDir); + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsEta, HistTable), getHistDesc(kPtVsEta, HistTable), getHistType(kPtVsEta, HistTable), {ResoSpecs.at(kPtVsEta)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsPhi, HistTable), getHistDesc(kPtVsPhi, HistTable), getHistType(kPtVsPhi, HistTable), {ResoSpecs.at(kPtVsPhi)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPhiVsEta, HistTable), getHistDesc(kPhiVsEta, HistTable), getHistType(kPhiVsEta, HistTable), {ResoSpecs.at(kPhiVsEta)}); + } + + template + void fillAnalysis(T const& resonance) + { + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kPt, HistTable)), resonance.pt()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kEta, HistTable)), resonance.eta()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kPhi, HistTable)), resonance.phi()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kMass, HistTable)), resonance.mass()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kPtVsMass, HistTable)), resonance.pt(), resonance.mass()); + } + + template + void fillQa(T const& resonance) + { + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(QaDir) + HIST(getHistName(kPtVsEta, HistTable)), resonance.pt(), resonance.eta()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(QaDir) + HIST(getHistName(kPtVsPhi, HistTable)), resonance.pt(), resonance.phi()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(QaDir) + HIST(getHistName(kPhiVsEta, HistTable)), resonance.phi(), resonance.eta()); + } + + o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; + photonhistmanager::PhotonHistManager mDau1Manager; + photonhistmanager::PhotonHistManager mDau2Manager; +}; +} // namespace o2::analysis::femto::twophotonresonancehistmanager +#endif // PWGCF_FEMTO_CORE_TWOPHOTONRESONANCEHISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/twoTrackResonanceBuilder.h b/PWGCF/Femto/Core/twoTrackResonanceBuilder.h index 126f447b7d0..26228021eea 100644 --- a/PWGCF/Femto/Core/twoTrackResonanceBuilder.h +++ b/PWGCF/Femto/Core/twoTrackResonanceBuilder.h @@ -33,6 +33,7 @@ #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include +#include #include namespace o2::analysis::femto::twotrackresonancebuilder @@ -58,40 +59,60 @@ using ConfPhiFilters = ConfTwoTrackResonanceFilters; using ConfKstarFilters = ConfTwoTrackResonanceFilters; // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define TWOTRACKRESONANCE_DEFAULT_SELECTION(defaultPdgCode, defaultMassMin, defaultMassMax) \ - o2::framework::Configurable pdgCodeAbs{"pdgCodeAbs", (defaultPdgCode), "Resonance PDG code. Set sign to minus 1 for antiparticle"}; \ - o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; \ - o2::framework::Configurable ptMax{"ptMax", 6.f, "Maximum pT"}; \ - o2::framework::Configurable etaMin{"etaMin", -0.9f, "Minimum eta"}; \ - o2::framework::Configurable etaMax{"etaMax", 0.9f, "Maximum eta"}; \ - o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; \ - o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; \ - o2::framework::Configurable massMin{"massMin", (defaultMassMin), "Minimum invariant mass for Resonance"}; \ - o2::framework::Configurable massMax{"massMax", (defaultMassMax), "Maximum invariant mass for Resonance"}; \ - o2::framework::Configurable posDauMaskBelowThres{"posDauMaskBelowThres", 0x10u, "Bitmask for positive daughter below threshold"}; \ - o2::framework::Configurable posDauMaskAboveThres{"posDauMaskAboveThres", 0x8u, "Bitmask for positive daughter above threshold"}; \ - o2::framework::Configurable negDauMaskBelowThres{"negDauMaskBelowThres", 0x2u, "Bitmask for negative daughter below threshold"}; \ - o2::framework::Configurable negDauMaskAboveThres{"negDauMaskAboveThres", 0x1u, "Bitmask for negative daughter above threshold"}; +#define TWOTRACKRESONANCE_DEFAULT_SELECTION(defaultPdgCode, defaultMassMin, defaultMassMax) \ + o2::framework::Configurable pdgCodeAbs{"pdgCodeAbs", (defaultPdgCode), "Resonance PDG code. Set sign to minus 1 for antiparticle"}; \ + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; \ + o2::framework::Configurable ptMax{"ptMax", 6.f, "Maximum pT"}; \ + o2::framework::Configurable etaMin{"etaMin", -0.9f, "Minimum eta"}; \ + o2::framework::Configurable etaMax{"etaMax", 0.9f, "Maximum eta"}; \ + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; \ + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; \ + o2::framework::Configurable massMin{"massMin", (defaultMassMin), "Minimum invariant mass for Resonance"}; \ + o2::framework::Configurable massMax{"massMax", (defaultMassMax), "Maximum invariant mass for Resonance"}; + +// phi -> K+ K-, rho0 -> pi+ pi-: both daughters are the same species, so a single pair of mask +// thresholds is enough, applied to both the positive and negative daughter alike +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TWOTRACKRESONANCE_SAME_SPECIES_MASK \ + o2::framework::Configurable dauMaskBelowThres{"dauMaskBelowThres", 0x10u, "Bitmask for both daughters below threshold"}; \ + o2::framework::Configurable dauMaskAboveThres{"dauMaskAboveThres", 0x8u, "Bitmask for both daughters above threshold"}; struct ConfPhiSelection : o2::framework::ConfigurableGroup { std::string prefix = std::string("PhiSelection"); TWOTRACKRESONANCE_DEFAULT_SELECTION(333, 0.95f, 1.05f) - o2::framework::Configurable sign{"sign", 1, "Dummy value for compatability"}; + TWOTRACKRESONANCE_SAME_SPECIES_MASK + o2::framework::Configurable sign{"sign", 1, "Dummy value for compatibility"}; }; struct ConfRho0Selection : o2::framework::ConfigurableGroup { std::string prefix = std::string("Rho0Selection"); TWOTRACKRESONANCE_DEFAULT_SELECTION(113, 0.7f, 0.84f) - o2::framework::Configurable sign{"sign", 1, "Dummy value for compatability"}; + TWOTRACKRESONANCE_SAME_SPECIES_MASK + o2::framework::Configurable sign{"sign", 1, "Dummy value for compatibility"}; }; +#undef TWOTRACKRESONANCE_SAME_SPECIES_MASK + +// K*0 -> K+ pi-, K*0bar -> K- pi+: the physical pos/neg daughter's species flips with the +// candidate's charge sign, so the masks are named by species (kaon/pion) rather than by charge -- +// MAKE_RESONANCE_1_PARTITON resolves which stored column (maskPosDau/maskNegDau) each applies to +// from the candidate's own signedPt, so the user never has to reason about pos/neg <-> kaon/pion +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TWOTRACKRESONANCE_KSTAR_MASK \ + o2::framework::Configurable kaonDauMaskBelowThres{"kaonDauMaskBelowThres", 0x10u, "Bitmask for the kaon daughter below threshold"}; \ + o2::framework::Configurable kaonDauMaskAboveThres{"kaonDauMaskAboveThres", 0x8u, "Bitmask for the kaon daughter above threshold"}; \ + o2::framework::Configurable pionDauMaskBelowThres{"pionDauMaskBelowThres", 0x2u, "Bitmask for the pion daughter below threshold"}; \ + o2::framework::Configurable pionDauMaskAboveThres{"pionDauMaskAboveThres", 0x1u, "Bitmask for the pion daughter above threshold"}; + struct ConfKstar0Selection : o2::framework::ConfigurableGroup { std::string prefix = std::string("Kstar0Selection"); o2::framework::Configurable sign{"sign", 1, "Sign (+1 for Kstar0 and -1 for Kstar0Bar) "}; TWOTRACKRESONANCE_DEFAULT_SELECTION(313, 0.8f, 1.0f) + TWOTRACKRESONANCE_KSTAR_MASK }; #undef TWOTRACKRESONANCE_DEFAULT_SELECTION +#undef TWOTRACKRESONANCE_KSTAR_MASK struct TwoTrackResonanceBuilderProducts : o2::framework::ProducesGroup { o2::framework::Produces producedPhis; @@ -106,7 +127,7 @@ struct ConfTwoTrackResonanceTables : o2::framework::ConfigurableGroup { std::string prefix = std::string("TwoTrackResonanceTables"); o2::framework::Configurable producePhis{"producePhis", -1, "Produce Phis (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable producePhiMasks{"producePhiMasks", -1, "Produce PhiMasks (-1: auto; 0 off; 1 on)"}; - o2::framework::Configurable produceKstar0s{"produceKstar0s", -1, "Produce K0stars (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceKstar0s{"produceKstar0s", -1, "Produce Kstar0s (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceKstar0Masks{"produceKstar0Masks", -1, "Produce Kstar0Masks (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceRho0s{"produceRho0s", -1, "Produce Rho0s (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceRho0Masks{"produceRho0Masks", -1, "Produce Rho0Masks (-1: auto; 0 off; 1 on)"}; @@ -182,7 +203,6 @@ class TwoTrackResonanceBuilder } auto posDaughterSlice = posDaughterPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); auto negDaughterSlice = negDaughterPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - // LOG(warn) << "Size: " << posDaughterSlice.size() << " " << negDaughterSlice.size(); for (auto const& [posDaughter, negDaughter] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(posDaughterSlice, negDaughterSlice))) { this->fillResonance(col, posDaughter, negDaughter, resonanceProducts); } @@ -322,5 +342,120 @@ class TwoTrackResonanceBuilder }; // namespace twotrackresonancebuilder +struct ConfTwoTrackResonanceTablesDerivedToDerived : o2::framework::ConfigurableGroup { + std::string prefix = std::string("TwoTrackResonanceTables"); + o2::framework::Configurable limitRho0{"limitRho0", 1, "Require at least this many Rho0s in the collision. Set to 0 to skip Rho0s entirely (not written to output)."}; + o2::framework::Configurable limitPhi{"limitPhi", 0, "Require at least this many Phis in the collision. Set to 0 to skip Phis entirely (not written to output)."}; +}; + +struct TwoTrackResonanceBuilderDerivedToDerivedProducts : o2::framework::ProducesGroup { + o2::framework::Produces producedRho0s; + o2::framework::Produces producedRho0Masks; + o2::framework::Produces producedPhis; + o2::framework::Produces producedPhiMasks; +}; + +class TwoTrackResonanceBuilderDerivedToDerived +{ + public: + template + void init(T& config) + { + mLimitRho0 = config.limitRho0.value; + mLimitPhi = config.limitPhi.value; + + if (mLimitRho0 < 0 || mLimitPhi < 0) { + LOG(fatal) << "Two-track resonance limits must be non-negative (got " << mLimitRho0 << " and " << mLimitPhi << "). Breaking..."; + } + if (mLimitRho0 == 0 && mLimitPhi == 0) { + LOG(fatal) << "Both Rho0 limit and Phi limit are 0. Breaking..."; + } + } + + template + bool collisionHasTooFewRho0s(T1 const& col, T2 const& /*rho0Table*/, T3& partitionRho0, T4& cache) + { + if (mLimitRho0 == 0) { // Rho0s disabled, cannot reject on them + return false; + } + auto rho0Slice = partitionRho0->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + return rho0Slice.size() < static_cast(mLimitRho0); + } + + template + bool collisionHasTooFewPhis(T1 const& col, T2 const& /*phiTable*/, T3& partitionPhi, T4& cache) + { + if (mLimitPhi == 0) { // Phis disabled, cannot reject on them + return false; + } + auto phiSlice = partitionPhi->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + return phiSlice.size() < static_cast(mLimitPhi); + } + + template + void processRho0s(T1 const& col, T2 const& /*rho0Table*/, T3 const& oldTrackTable, T4& partitionRho0, T5& trackBuilder, T6& cache, T7& newRho0Table, T8& newTrackTable, T9& newCollisionTable) + { + if (mLimitRho0 == 0) { // Rho0s disabled + return; + } + + auto rho0Slice = partitionRho0->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + + for (auto const& rho0 : rho0Slice) { + auto posDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(rho0.posDauId(), oldTrackTable)); + auto negDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(rho0.negDauId(), oldTrackTable)); + + int64_t posDaughterIndex = trackBuilder.getDaughterIndex(posDaughter, newTrackTable, newCollisionTable); + int64_t negDaughterIndex = trackBuilder.getDaughterIndex(negDaughter, newTrackTable, newCollisionTable); + + newRho0Table.producedRho0s(newCollisionTable.producedCollision.lastIndex(), + rho0.pt(), + rho0.eta(), + rho0.phi(), + rho0.mass(), + posDaughterIndex, + negDaughterIndex); + newRho0Table.producedRho0Masks(rho0.maskPosDau(), + rho0.posDauHasHighMomentum(), + rho0.maskNegDau(), + rho0.negDauHasHighMomentum()); + } + } + + template + void processPhis(T1 const& col, T2 const& /*phiTable*/, T3 const& oldTrackTable, T4& partitionPhi, T5& trackBuilder, T6& cache, T7& newPhiTable, T8& newTrackTable, T9& newCollisionTable) + { + if (mLimitPhi == 0) { // Phis disabled + return; + } + + auto phiSlice = partitionPhi->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + + for (auto const& phi : phiSlice) { + auto posDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(phi.posDauId(), oldTrackTable)); + auto negDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(phi.negDauId(), oldTrackTable)); + + int64_t posDaughterIndex = trackBuilder.getDaughterIndex(posDaughter, newTrackTable, newCollisionTable); + int64_t negDaughterIndex = trackBuilder.getDaughterIndex(negDaughter, newTrackTable, newCollisionTable); + + newPhiTable.producedPhis(newCollisionTable.producedCollision.lastIndex(), + phi.pt(), + phi.eta(), + phi.phi(), + phi.mass(), + posDaughterIndex, + negDaughterIndex); + newPhiTable.producedPhiMasks(phi.maskPosDau(), + phi.posDauHasHighMomentum(), + phi.maskNegDau(), + phi.negDauHasHighMomentum()); + } + } + + private: + int mLimitRho0 = 0; + int mLimitPhi = 0; +}; + } // namespace o2::analysis::femto::twotrackresonancebuilder #endif // PWGCF_FEMTO_CORE_TWOTRACKRESONANCEBUILDER_H_ diff --git a/PWGCF/Femto/Core/twoTrackResonanceHistManager.h b/PWGCF/Femto/Core/twoTrackResonanceHistManager.h index 1f9149ddc26..95cdd37c01d 100644 --- a/PWGCF/Femto/Core/twoTrackResonanceHistManager.h +++ b/PWGCF/Femto/Core/twoTrackResonanceHistManager.h @@ -122,9 +122,18 @@ auto makeTwoTrackResonanceQaHistSpecMap(const T& confBinningAnalysis) #undef TWOTRACKRESONANCE_HIST_ANALYSIS_MAP #undef TWOTRACKRESONANCE_HIST_QA_MAP +// used only by the standalone QA task constexpr char PrefixRho[] = "Rho0/"; constexpr char PrefixPhi[] = "Phi/"; constexpr char PrefixKstar[] = "Kstar0/"; +// same-event siblings, used only by a pair builder's same-event resonance manager +constexpr char PrefixRhoSe[] = "Rho0/SE/"; +constexpr char PrefixPhiSe[] = "Phi/SE/"; +constexpr char PrefixKstarSe[] = "Kstar0/SE/"; +// mixed-event siblings, used only by a pair builder's mixed-event resonance manager +constexpr char PrefixRhoMe[] = "Rho0/ME/"; +constexpr char PrefixPhiMe[] = "Phi/ME/"; +constexpr char PrefixKstarMe[] = "Kstar0/ME/"; constexpr std::string_view AnalysisDir = "Analysis/"; constexpr std::string_view QaDir = "QA/"; diff --git a/PWGCF/Femto/Core/v0Builder.h b/PWGCF/Femto/Core/v0Builder.h index 6792d395071..154233ebfa4 100644 --- a/PWGCF/Femto/Core/v0Builder.h +++ b/PWGCF/Femto/Core/v0Builder.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -50,53 +51,49 @@ struct ConfV0Filters : o2::framework::ConfigurableGroup { o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; o2::framework::Configurable massMinLambda{"massMinLambda", 1.f, "Minimum mass for Lambda hypothesis"}; o2::framework::Configurable massMaxLambda{"massMaxLambda", 1.2f, "Maximum mass for Lambda hypothesis"}; - o2::framework::Configurable rejectHypothesisK0short{"rejectHypothesisK0short", true, "Rejection of K0short hypothesis for Lambda candidates"}; - o2::framework::Configurable rejectMassMinK0short{"rejectMassMinK0short", 0.475f, "Minimum mass to rejection K0short hypothesis for Lambda candidates"}; - o2::framework::Configurable rejectMassMaxK0short{"rejectMassMaxK0short", 0.515f, "Maximum mass to rejection K0short hypothesis for Lambda candidates"}; - o2::framework::Configurable massMinK0short{"massMinK0short", 0.45f, "Minimum mass for K0Short hypothesis"}; - o2::framework::Configurable massMaxK0short{"massMaxK0short", 0.53f, "Maximum mass for K0Short hypothesis"}; - o2::framework::Configurable rejectHypothesisLambda{"rejectHypothesisLambda", true, "Rejection of Lambda hypothesis for K0short candidates"}; - o2::framework::Configurable rejectMassMinLambda{"rejectMassMinLambda", 1.11f, "Minimum mass to rejection K0short hypothesis for Lambda candidates"}; - o2::framework::Configurable rejectMassMaxLambda{"rejectMassMaxLambda", 1.12f, "Maximum mass to rejection K0short hypothesis for Lambda candidates"}; + o2::framework::Configurable massMinK0short{"massMinK0short", 0.485f, "Minimum mass for K0Short hypothesis"}; + o2::framework::Configurable massMaxK0short{"massMaxK0short", 0.51f, "Maximum mass for K0Short hypothesis"}; }; // selections bits for all v0s // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define V0_DEFAULT_BITS \ - o2::framework::Configurable passThrough{"passThrough", false, "If true, all V0s are passed through. Bits for all selections are stored."}; \ - o2::framework::Configurable> dcaDauMax{"dcaDauMax", {1.5f}, "Maximum DCA between the daughters at V0 decay vertex (cm)"}; \ - o2::framework::Configurable> cpaMin{"cpaMin", {0.99f}, "Minimum cosine of pointing angle"}; \ - o2::framework::Configurable> transRadMin{"transRadMin", {0.2f}, "Minimum transverse radius (cm)"}; \ - o2::framework::Configurable> transRadMax{"transRadMax", {100.f}, "Maximum transverse radius (cm)"}; \ - o2::framework::Configurable> decayVtxMax{"decayVtxMax", {100.f}, "Maximum distance in x,y,z of the decay vertex from primary vertex (cm)"}; \ - o2::framework::Configurable> dauAbsEtaMax{"dauAbsEtaMax", {0.8f}, "Maximum |eta| for daughter tracks"}; \ - o2::framework::Configurable> dauAbsDcaxyMin{"dauAbsDcaxyMin", {0.05f}, "Minimum DCAxy of the daughters from primary vertex (cm)"}; \ - o2::framework::Configurable> dauTpcClustersMin{"dauTpcClustersMin", {80.f}, "Minimum number of TPC clusters for daughter tracks"}; \ - o2::framework::Configurable requireTof{"requireTof", false, "If true, TOF PID is a minimal selection. If false, TOF PID is an optional"}; \ - o2::framework::Configurable keepTracksWithoutTof{"keepTracksWithoutTof", true, "If true, the bit mask for the TOF selection will be ture for all limits if the daugher track has no TOF"}; +#define V0_DEFAULT_BITS \ + o2::framework::Configurable passThrough{"passThrough", false, "If true, all V0s are passed through. Bits for all selections are stored."}; \ + o2::framework::Configurable> dcaDauMax{"dcaDauMax", {1.5f}, "Maximum DCA between the daughters at V0 decay vertex (cm)"}; \ + o2::framework::Configurable> cpaMin{"cpaMin", {0.99f}, "Minimum cosine of pointing angle"}; \ + o2::framework::Configurable> transRadMin{"transRadMin", {0.2f}, "Minimum transverse radius (cm)"}; \ + o2::framework::Configurable> transRadMax{"transRadMax", {100.f}, "Maximum transverse radius (cm)"}; \ + o2::framework::Configurable> decayVtxMax{"decayVtxMax", {100.f}, "Maximum distance in x,y,z of the decay vertex from primary vertex (cm)"}; \ + o2::framework::Configurable> dauAbsEtaMax{"dauAbsEtaMax", {0.8f}, "Maximum |eta| for daughter tracks"}; \ + o2::framework::Configurable> dauAbsDcaxyMin{"dauAbsDcaxyMin", {0.05f}, "Minimum DCAxy of the daughters from primary vertex (cm)"}; \ + o2::framework::Configurable> dauTpcClustersMin{"dauTpcClustersMin", {80.f}, "Minimum number of TPC clusters for daughter tracks"}; \ + o2::framework::Configurable requireTof{"requireTof", false, "If true, the TOF PID cut is mandatory. For daughters with a TOF signal, the candidate is rejected on failure. For daughters without a TOF signal, the candidate is rejected unless keepTracksWithoutTof is true"}; \ + o2::framework::Configurable keepTracksWithoutTof{"keepTracksWithoutTof", true, "If true, daughters without a TOF signal pass the TOF PID cut unconditionally, overriding requireTof. If false, daughters without a TOF signal fail the TOF cut (and are rejected if requireTof is true)"}; // derived selection bits for lambda struct ConfLambdaBits : o2::framework::ConfigurableGroup { std::string prefix = std::string("LambdaBits"); V0_DEFAULT_BITS - o2::framework::Configurable> posDauTpcPion{"posDauTpcPion", {5.f}, "Maximum |nsimga_Pion| TPC for positive daughter tracks"}; - o2::framework::Configurable> posDauTpcProton{"posDauTpcProton", {5.f}, "Maximum |nsimga_Proton| TPC for positive daughter tracks"}; - o2::framework::Configurable> negDauTpcPion{"negDauTpcPion", {5.f}, "Maximum |nsimga_Pion| TPC for negative daughter tracks"}; - o2::framework::Configurable> negDauTpcProton{"negDauTpcProton", {5.f}, "Maximum |nsimga_Proton| TPC negative for daughter tracks"}; + o2::framework::Configurable> posDauTpcPion{"posDauTpcPion", {5.f}, "Maximum |nsigma_Pion| TPC for positive daughter tracks"}; + o2::framework::Configurable> posDauTpcProton{"posDauTpcProton", {5.f}, "Maximum |nsigma_Proton| TPC for positive daughter tracks"}; + o2::framework::Configurable> negDauTpcPion{"negDauTpcPion", {5.f}, "Maximum |nsigma_Pion| TPC for negative daughter tracks"}; + o2::framework::Configurable> negDauTpcProton{"negDauTpcProton", {5.f}, "Maximum |nsigma_Proton| TPC for negative daughter tracks"}; o2::framework::Configurable> posDauTofPion{"posDauTofPion", {}, "Maximum |nsigma_Pion| TOF for positive daughter tracks"}; o2::framework::Configurable> posDauTofProton{"posDauTofProton", {}, "Maximum |nsigma_Proton| TOF for positive daughter tracks"}; o2::framework::Configurable> negDauTofPion{"negDauTofPion", {}, "Maximum |nsigma_Pion| TOF for negative daughter tracks"}; o2::framework::Configurable> negDauTofProton{"negDauTofProton", {}, "Maximum |nsigma_Proton| TOF for negative daughter tracks"}; + o2::framework::Configurable> rejectK0shortMassRanges{"rejectK0shortMassRanges", {"0.475;0.515"}, "K0short mass ranges (lower;upper) to reject -- bit is set if the candidate's mK0Short falls outside the range"}; }; // derived selection bits for K0Short struct ConfK0shortBits : o2::framework::ConfigurableGroup { std::string prefix = std::string("K0shortBits"); V0_DEFAULT_BITS - o2::framework::Configurable> posDauTpcPion{"posDauTpcPion", {5.f}, "Maximum |nsimga_Pion| TPC for positive daughter tracks"}; - o2::framework::Configurable> negDauTpcPion{"negDauTpcPion", {5.f}, "Maximum |nsimga_Pion| TPC for negative daughter tracks"}; + o2::framework::Configurable> posDauTpcPion{"posDauTpcPion", {5.f}, "Maximum |nsigma_Pion| TPC for positive daughter tracks"}; + o2::framework::Configurable> negDauTpcPion{"negDauTpcPion", {5.f}, "Maximum |nsigma_Pion| TPC for negative daughter tracks"}; o2::framework::Configurable> posDauTofPion{"posDauTofPion", {}, "Maximum |nsigma_Pion| TOF for positive daughter tracks"}; o2::framework::Configurable> negDauTofPion{"negDauTofPion", {}, "Maximum |nsigma_Pion| TOF for negative daughter tracks"}; + o2::framework::Configurable> rejectLambdaMassRanges{"rejectLambdaMassRanges", {"1.11;1.12"}, "Lambda/AntiLambda mass ranges (lower;upper) to reject -- bit is set if the candidate's mLambda (resp. mAntiLambda) falls outside the range; same range for both since they share the same PDG mass"}; }; #undef V0_DEFAULT_BITS @@ -127,8 +124,8 @@ struct ConfLambdaSelection : o2::framework::ConfigurableGroup { template struct ConfK0shortSelection : o2::framework::ConfigurableGroup { std::string prefix = Prefix; - V0_DEFAULT_SELECTIONS(0.47, 0.51, 310) - o2::framework::Configurable sign{"sign", 0, "Dummy value. For compatability with Lambda selection"}; + V0_DEFAULT_SELECTIONS(0.485, 0.51, 310) + o2::framework::Configurable sign{"sign", 0, "Dummy value. For compatibility with Lambda selection"}; }; #undef V0_DEFAULT_SELECTIONS @@ -173,6 +170,10 @@ enum V0Sels { kNegDaughTofPion, ///< TOF Pion PID for negative daughter kNegDaughTofProton, ///< TOF Proton PID for negative daughter + // rejection of competing mass hypotheses (range-based: bit is set if the candidate is outside the range) + kRejectK0shortMass, ///< Lambda/AntiLambda: reject K0short mass hypothesis + kRejectLambdaMass, ///< K0short: reject Lambda/AntiLambda mass hypothesis (single bit, see applySelections()) + kV0SelsMax }; @@ -198,7 +199,9 @@ const std::unordered_map v0SelectionNames = { {kPosDaughTofPion, "TOF Pion PID for positive daughter"}, {kPosDaughTofProton, "TOF Proton PID for positive daughter"}, {kNegDaughTofPion, "TOF Pion PID for negative daughter"}, - {kNegDaughTofProton, "TOF Proton PID for negative daughter"}}; + {kNegDaughTofProton, "TOF Proton PID for negative daughter"}, + {kRejectK0shortMass, "Reject K0short mass hypothesis"}, + {kRejectLambdaMass, "Reject Lambda/AntiLambda mass hypothesis"}}; // enum for all track filters (loose pre-selection, applied before quality/PID cuts) enum V0Filters { @@ -208,12 +211,8 @@ enum V0Filters { kEtaMax, kPhiMin, kPhiMax, - kRejectionK0shortMass, - kK0shortMassMin, - kK0shortMassMax, - kRejectionLambdaMass, - kLambdaMassMin, - kLambdaMassMax, + kMassMin, ///< signal mass window lower bound, for this v0Type's own mass hypothesis + kMassMax, ///< signal mass window upper bound, for this v0Type's own mass hypothesis kV0FiltersMax }; @@ -227,12 +226,8 @@ const std::unordered_map v0FilterNames = { {kEtaMax, "etaMax"}, {kPhiMin, "phiMin"}, {kPhiMax, "phiMax"}, - {kRejectionK0shortMass, "rejectK0short"}, - {kK0shortMassMin, "k0shortMassMin"}, - {kK0shortMassMax, "k0shortMassMax"}, - {kRejectionLambdaMass, "rejectLambda"}, - {kLambdaMassMin, "lambdaMassMin"}, - {kLambdaMassMax, "lambdaMassMax"}, + {kMassMin, "massMin"}, + {kMassMax, "massMax"}, }; /// \brief Cut class to contain and execute all cuts applied to v0s @@ -256,39 +251,44 @@ class V0Selection : public baseselection::BaseSelection produceLambdaMasks{"produceLambdaMasks", -1, "Produce LambdaMasks (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceLambdaExtras{"produceLambdaExtras", -1, "Produce LambdaExtras (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceLambdaFrac{"produceLambdaFrac", -1, "Produce LambdaFrac (cpa for fraction extraction) (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceK0shorts{"produceK0shorts", -1, "Produce K0shorts (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceLiteK0shorts{"produceLiteK0shorts", -1, "Produce LiteK0shorts (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceK0shortMasks{"produceK0shortMasks", -1, "Produce K0shortMasks (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceK0shortExtras{"produceK0shortExtras", -1, "Produce K0shortExtras (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceK0shortFrac{"produceK0shortFrac", -1, "Produce K0shortFrac (cpa for fraction extraction) (-1: auto; 0 off; 1 on)"}; }; template @@ -534,6 +532,7 @@ class V0Builder mProduceLiteLambdas = utils::enableTable("FLiteLambdas_001", table.produceLiteLambdas.value, initContext); mProduceLambdaMasks = utils::enableTable("FLambdaMasks_002", table.produceLambdaMasks.value, initContext); mProduceLambdaExtras = utils::enableTable("FLambdaExtras_001", table.produceLambdaExtras.value, initContext); + mProduceLambdaFrac = utils::enableTable("FLambdaFrac_001", table.produceLambdaFrac.value, initContext); if (mProduceLambdas && mProduceLiteLambdas) { LOG(fatal) << "FLambdas and FLiteLambdas are mutually exclusive -- enable only one. " @@ -557,6 +556,7 @@ class V0Builder mProduceLiteK0shorts = utils::enableTable("FLiteK0shorts_001", table.produceLiteK0shorts.value, initContext); mProduceK0shortMasks = utils::enableTable("FK0shortMasks_002", table.produceK0shortMasks.value, initContext); mProduceK0shortExtras = utils::enableTable("FK0shortExtras_001", table.produceK0shortExtras.value, initContext); + mProduceK0shortFrac = utils::enableTable("FK0shortFrac_001", table.produceK0shortFrac.value, initContext); if (mProduceK0shorts && mProduceLiteK0shorts) { LOG(fatal) << "FK0shorts and FLiteK0shorts are mutually exclusive -- enable only one. " @@ -574,8 +574,8 @@ class V0Builder << "or switch to FK0shorts if TrackTables.produceTracks is enabled instead."; } } - if (mProduceLambdas || mProduceLiteLambdas || mProduceLambdaMasks || mProduceLambdaExtras || - mProduceK0shorts || mProduceLiteK0shorts || mProduceK0shortMasks || mProduceK0shortExtras) { + if (mProduceLambdas || mProduceLiteLambdas || mProduceLambdaMasks || mProduceLambdaExtras || mProduceLambdaFrac || + mProduceK0shorts || mProduceLiteK0shorts || mProduceK0shortMasks || mProduceK0shortExtras || mProduceK0shortFrac) { mFillAnyTable = true; } else { LOG(info) << "No tables configured, Selection object will not be configured..."; @@ -715,7 +715,12 @@ class V0Builder v0.v0radius(), v0.x(), v0.y(), - v0.z()); + v0.z(), + v0.alpha(), + v0.qtarm()); + } + if (mProduceLambdaFrac) { + v0Products.producedLambdaFrac(v0.v0cosPA()); } } @@ -754,7 +759,12 @@ class V0Builder v0.v0radius(), v0.x(), v0.y(), - v0.z()); + v0.z(), + v0.alpha(), + v0.qtarm()); + } + if (mProduceK0shortFrac) { + v0Products.producedK0shortFrac(v0.v0cosPA()); } } @@ -768,10 +778,12 @@ class V0Builder bool mProduceLiteLambdas = false; bool mProduceLambdaMasks = false; bool mProduceLambdaExtras = false; + bool mProduceLambdaFrac = false; bool mProduceK0shorts = false; bool mProduceLiteK0shorts = false; bool mProduceK0shortMasks = false; bool mProduceK0shortExtras = false; + bool mProduceK0shortFrac = false; }; struct ConfV0TablesDerivedToDerived : o2::framework::ConfigurableGroup { diff --git a/PWGCF/Femto/Core/v0HistManager.h b/PWGCF/Femto/Core/v0HistManager.h index 4f55c529358..058edf2e6c8 100644 --- a/PWGCF/Femto/Core/v0HistManager.h +++ b/PWGCF/Femto/Core/v0HistManager.h @@ -46,7 +46,8 @@ enum V0Hist { kPhi, kMass, kSign, - kPtVsMass, // nice to have during analysis + kPtVsMass, // nice to have during analysis + kFractionCpa, // fraction estimation: pt vs cosPa, binned independently of the QA cosPa axis // qa variables kMassLambda, kMassAntiLambda, @@ -65,7 +66,6 @@ enum V0Hist { kPtVsEta, kPtVsPhi, kPhiVsEta, - kPtVsCosPa, kPtVsPa, kPtVsLambdaMass, kPtVsAntiLambdaMass, @@ -75,6 +75,7 @@ enum V0Hist { kK0shortMassVsAntiLambdaMass, kStrangeTofVsTofPosDau, kStrangeTofVsTofNegDau, + kArmenterosQtVsAlpha, // mc kOrigin, kPdg, @@ -100,13 +101,14 @@ enum V0Hist { constexpr std::size_t MaxSecondary = 3; // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define V0_DEFAULT_BINNING(defaultMassMin, defaultMassMax) \ - o2::framework::ConfigurableAxis pt{"pt", {{600, 0, 6}}, "Pt"}; \ - o2::framework::ConfigurableAxis eta{"eta", {{300, -1.5, 1.5}}, "Eta"}; \ - o2::framework::ConfigurableAxis phi{"phi", {{720, 0, 1.f * o2::constants::math::TwoPI}}, "Phi"}; \ - o2::framework::ConfigurableAxis mass{"mass", {{200, (defaultMassMin), (defaultMassMax)}}, "Mass"}; \ - o2::framework::ConfigurableAxis sign{"sign", {{3, -1.5, 1.5}}, "Sign"}; \ - o2::framework::ConfigurableAxis pdgCodes{"pdgCodes", {{8001, -4000.5, 4000.5}}, "MC ONLY: PDG codes of reconstructed V0s"}; +#define V0_DEFAULT_BINNING(defaultMassMin, defaultMassMax) \ + o2::framework::ConfigurableAxis pt{"pt", {{600, 0, 6}}, "Pt"}; \ + o2::framework::ConfigurableAxis eta{"eta", {{300, -1.5, 1.5}}, "Eta"}; \ + o2::framework::ConfigurableAxis phi{"phi", {{720, 0, 1.f * o2::constants::math::TwoPI}}, "Phi"}; \ + o2::framework::ConfigurableAxis mass{"mass", {{200, (defaultMassMin), (defaultMassMax)}}, "Mass"}; \ + o2::framework::ConfigurableAxis sign{"sign", {{3, -1.5, 1.5}}, "Sign"}; \ + o2::framework::ConfigurableAxis pdgCodes{"pdgCodes", {{8001, -4000.5, 4000.5}}, "MC ONLY: PDG codes of reconstructed V0s"}; \ + o2::framework::ConfigurableAxis fractionCpa{"fractionCpa", {{100, 0.95, 1}}, "Cosine of pointing angle (fraction estimation)"}; template struct ConfLambdaBinning : o2::framework::ConfigurableGroup { @@ -136,15 +138,17 @@ struct ConfV0QaBinning : o2::framework::ConfigurableGroup { o2::framework::Configurable plot2d{"plot2d", true, "Generate various 2D QA plots"}; o2::framework::Configurable plotOrigins{"plotOrigins", true, "MC ONLY: Plot pt vs cosPa for different particle origins"}; o2::framework::Configurable> pdgCodesForMothersOfSecondary{"pdgCodesForMothersOfSecondary", {3312, 3334}, "MC ONLY: PDG codes of mothers of secondaries (Max 3 will be considered)"}; - o2::framework::ConfigurableAxis cosPa{"cosPa", {{100, 0.9, 1}}, "Cosine of poiting angle"}; - o2::framework::ConfigurableAxis pa{"pa", {{180, 0, 1.f * o2::constants::math::PI}}, "Poiting angle"}; + o2::framework::ConfigurableAxis cosPa{"cosPa", {{100, 0.95, 1}}, "Cosine of pointing angle"}; + o2::framework::ConfigurableAxis pa{"pa", {{180, 0, 1.f * o2::constants::math::PI}}, "Pointing angle"}; o2::framework::ConfigurableAxis dauDcaAtDecay{"dauDcaAtDecay", {{150, 0, 1.5}}, "Daughter DCA at decay vertex"}; o2::framework::ConfigurableAxis decayVertex{"decayVertex", {{100, 0, 100}}, "Decay vertex"}; o2::framework::ConfigurableAxis transRadius{"transRadius", {{100, 0, 100}}, "Transverse radius"}; - o2::framework::ConfigurableAxis massLambda{"massLambda", {{200, 1, 1.2}}, "mass for antiparticle hypothesis"}; + o2::framework::ConfigurableAxis massLambda{"massLambda", {{200, 1, 1.2}}, "mass for particle hypothesis"}; o2::framework::ConfigurableAxis massAntiLambda{"massAntiLambda", {{100, 1, 1.2}}, "mass for antiparticle hypothesis"}; o2::framework::ConfigurableAxis massK0short{"massK0short", {{200, 0.45, 0.55}}, "Mass for k0short hypothesis"}; o2::framework::ConfigurableAxis strangeTof{"strangeTof", {{500, -5, 5}}, "Strangeness TOF vs TOF Nsigma for daughters"}; + o2::framework::ConfigurableAxis armenterosAlpha{"armenterosAlpha", {{200, -1, 1}}, "Armenteros-Podolanski alpha"}; + o2::framework::ConfigurableAxis armenterosQt{"armenterosQt", {{150, 0, 0.3}}, "Armenteros-Podolanski qt (GeV/#it{c})"}; }; constexpr const char PrefixLambdaQaBinning1[] = "LambdaQaBinning1"; @@ -162,6 +166,7 @@ constexpr std::array, kV0HistLast> HistTable = { {kMass, o2::framework::HistType::kTH1F, "hMass", "Invariant Mass; m_{Inv} (GeV/#it{c}^{2}); Entries"}, {kSign, o2::framework::HistType::kTH1F, "hSign", "Sign (-1 -> antiparticle, 0 -> self conjugate, +1 -> particle); sign; Entries"}, {kPtVsMass, o2::framework::HistType::kTH2F, "hPtVsMass", "Transverse momentum vs invariant mass; p_{T} (GeV/#it{c}); m_{Inv} (GeV/#it{c}^{2})"}, + {kFractionCpa, o2::framework::HistType::kTH2F, "hFractionCpa", "p_{T} vs Cosine of pointing angle (fraction estimation); p_{T} (GeV/#it{c}); cos(#alpha)"}, {kMassLambda, o2::framework::HistType::kTH1F, "hMassLambda", "#Lambda mass; m_{p#pi^{-}} (GeV/#it{c}^{2}); Entries"}, {kMassAntiLambda, o2::framework::HistType::kTH1F, "hMassAntiLambda", "#bar{#Lambda} mass; m_{#bar{p}#pi^{+}} (GeV/#it{c}^{2}); Entries"}, {kMassK0short, o2::framework::HistType::kTH1F, "hMassK0short", "K^{0}_{s} mass; m_{#pi^{+}#pi^{-}} (GeV/#it{c}^{2}); Entries"}, @@ -178,8 +183,7 @@ constexpr std::array, kV0HistLast> HistTable = { {kPtVsEta, o2::framework::HistType::kTH2F, "hPtVsEta", "p_{T} vs #eta; p_{T} (GeV/#it{c}) ; #eta"}, {kPtVsPhi, o2::framework::HistType::kTH2F, "hPtVsPhi", "p_{T} vs #varphi; p_{T} (GeV/#it{c}) ; #varphi"}, {kPhiVsEta, o2::framework::HistType::kTH2F, "hPhiVsEta", "#varphi vs #eta; #varphi ; #eta"}, - {kPtVsCosPa, o2::framework::HistType::kTH2F, "hPtVsCosPa", "p_{T} vs Cosine of poiting angle;p_{T} (GeV/#it{c}); cos(#alpha);"}, - {kPtVsPa, o2::framework::HistType::kTH2F, "hPtVsPa", "p_{T} vs Poiting angle;p_{T} (GeV/#it{c}); #alpha"}, + {kPtVsPa, o2::framework::HistType::kTH2F, "hPtVsPa", "p_{T} vs Pointing angle;p_{T} (GeV/#it{c}); #alpha"}, {kPtVsLambdaMass, o2::framework::HistType::kTH2F, "hPtVsLambdaMass", "p_{T} vs #Lambda mass; p_{T} (GeV/#it{c}); m_{p#pi^{-}} (GeV/#it{c}^{2})"}, {kPtVsAntiLambdaMass, o2::framework::HistType::kTH2F, "hPtVsAntiLambdaMass", "p_{T} vs #bar{#Lambda} mass; p_{T} (GeV/#it{c}); m_{#bar{p}#pi^{+}} (GeV/#it{c}^{2})"}, {kPtVsK0shortMass, o2::framework::HistType::kTH2F, "hPtVsK0shortMass", "p_{T} vs K^{0}_{S} mass; p_{T} (GeV/#it{c}); m_{#pi^{+}#pi^{-}} (GeV/#it{c}^{2})"}, @@ -188,6 +192,7 @@ constexpr std::array, kV0HistLast> HistTable = { {kLambdaMassVsAntiLambdaMass, o2::framework::HistType::kTH2F, "hLambdaMassVsAntiLambdaMass", "#Lambda mass vs #bar{#Lambda}; m_{p#pi^{-}} (GeV/#it{c}^{2}); m_{#bar{p}#pi^{+}} (GeV/#it{c}^{2})"}, {kStrangeTofVsTofPosDau, o2::framework::HistType::kTH2F, "hStrangeTofVsTofPosDau", "TOF_{Strange} vs TOF_{Tracking} of positive Daughter; n#sigma_{TOF, strange}; n#sigma_{TOF, tracking}"}, {kStrangeTofVsTofNegDau, o2::framework::HistType::kTH2F, "hStrangeTofVsTofNegDau", "TOF_{Strange} vs TOF_{Tracking} of negative Daughter; n#sigma_{TOF, strange}; n#sigma_{TOF, tracking}"}, + {kArmenterosQtVsAlpha, o2::framework::HistType::kTH2F, "hArmenterosPodolanski", "Armenteros-Podolanski; #alpha; q_{T} (GeV/#it{c})"}, {kOrigin, o2::framework::HistType::kTH1F, "hOrigin", "Status Codes (=Origin); Status Code; Entries"}, {kPdg, o2::framework::HistType::kTH1F, "hPdg", "PDG Codes of reconstructed v0; PDG Code; Entries"}, {kPdgMother, o2::framework::HistType::kTH1F, "hPdgMother", "PDG Codes of mother of reconstructed v0; PDG Code; Entries"}, @@ -207,13 +212,14 @@ constexpr std::array, kV0HistLast> HistTable = { }; // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define V0_HIST_ANALYSIS_MAP(conf) \ - {kPt, {(conf).pt}}, \ - {kEta, {(conf).eta}}, \ - {kPhi, {(conf).phi}}, \ - {kMass, {(conf).mass}}, \ - {kSign, {(conf).sign}}, \ - {kPtVsMass, {(conf).pt, (conf).mass}}, +#define V0_HIST_ANALYSIS_MAP(conf) \ + {kPt, {(conf).pt}}, \ + {kEta, {(conf).eta}}, \ + {kPhi, {(conf).phi}}, \ + {kMass, {(conf).mass}}, \ + {kSign, {(conf).sign}}, \ + {kPtVsMass, {(conf).pt, (conf).mass}}, \ + {kFractionCpa, {(conf).pt, (conf).fractionCpa}}, // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define V0_HIST_MC_MAP(conf) \ @@ -239,7 +245,6 @@ constexpr std::array, kV0HistLast> HistTable = { {kPtVsEta, {(confAnalysis).pt, (confAnalysis).eta}}, \ {kPtVsPhi, {(confAnalysis).pt, (confAnalysis).phi}}, \ {kPhiVsEta, {(confAnalysis).phi, (confAnalysis).eta}}, \ - {kPtVsCosPa, {(confAnalysis).pt, (confQa).cosPa}}, \ {kPtVsPa, {(confAnalysis).pt, (confQa).pa}}, \ {kMassLambda, {(confQa).massLambda}}, \ {kMassAntiLambda, {(confQa).massAntiLambda}}, \ @@ -251,19 +256,23 @@ constexpr std::array, kV0HistLast> HistTable = { {kK0shortMassVsLambdaMass, {(confQa).massK0short, (confQa).massLambda}}, \ {kK0shortMassVsAntiLambdaMass, {(confQa).massK0short, (confQa).massAntiLambda}}, \ {kStrangeTofVsTofPosDau, {(confQa).strangeTof, (confQa).strangeTof}}, \ - {kStrangeTofVsTofNegDau, {(confQa).strangeTof, (confQa).strangeTof}}, + {kStrangeTofVsTofNegDau, {(confQa).strangeTof, (confQa).strangeTof}}, \ + {kArmenterosQtVsAlpha, {(confQa).armenterosAlpha, (confQa).armenterosQt}}, +// per-origin truth-split variants of the FitVars fraction-estimation CPA histogram; binned on +// fractionCpa (not the QA-only cosPa) so they are available wherever FitVars is, including the +// pair task where no QA binning config exists // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define V0_HIST_MC_QA_MAP(confAnalysis, confQa) \ - {kNoMcParticle, {(confAnalysis).pt, (confQa).cosPa}}, \ - {kPrimary, {(confAnalysis).pt, (confQa).cosPa}}, \ - {kFromWrongCollision, {(confAnalysis).pt, (confQa).cosPa}}, \ - {kFromMaterial, {(confAnalysis).pt, (confQa).cosPa}}, \ - {kMissidentified, {(confAnalysis).pt, (confQa).cosPa}}, \ - {kSecondary1, {(confAnalysis).pt, (confQa).cosPa}}, \ - {kSecondary2, {(confAnalysis).pt, (confQa).cosPa}}, \ - {kSecondary3, {(confAnalysis).pt, (confQa).cosPa}}, \ - {kSecondaryOther, {(confAnalysis).pt, (confQa).cosPa}}, +#define V0_HIST_FITVARS_MC_MAP(conf) \ + {kNoMcParticle, {(conf).pt, (conf).fractionCpa}}, \ + {kPrimary, {(conf).pt, (conf).fractionCpa}}, \ + {kFromWrongCollision, {(conf).pt, (conf).fractionCpa}}, \ + {kFromMaterial, {(conf).pt, (conf).fractionCpa}}, \ + {kMissidentified, {(conf).pt, (conf).fractionCpa}}, \ + {kSecondary1, {(conf).pt, (conf).fractionCpa}}, \ + {kSecondary2, {(conf).pt, (conf).fractionCpa}}, \ + {kSecondary3, {(conf).pt, (conf).fractionCpa}}, \ + {kSecondaryOther, {(conf).pt, (conf).fractionCpa}}, template auto makeV0HistSpecMap(const T& confBinningAnalysis) @@ -277,7 +286,8 @@ auto makeV0McHistSpecMap(const T& confBinningAnalysis) { return std::map>{ V0_HIST_ANALYSIS_MAP(confBinningAnalysis) - V0_HIST_MC_MAP(confBinningAnalysis)}; + V0_HIST_MC_MAP(confBinningAnalysis) + V0_HIST_FITVARS_MC_MAP(confBinningAnalysis)}; } template @@ -295,26 +305,33 @@ std::map> makeV0McQaHistSpecMap(T1 V0_HIST_ANALYSIS_MAP(confBinningAnalysis) V0_HIST_QA_MAP(confBinningAnalysis, confBinningQa) V0_HIST_MC_MAP(confBinningAnalysis) - V0_HIST_MC_QA_MAP(confBinningAnalysis, confBinningQa)}; + V0_HIST_FITVARS_MC_MAP(confBinningAnalysis)}; } #undef V0_HIST_ANALYSIS_MAP #undef V0_HIST_QA_MAP #undef V0_HIST_MC_MAP -#undef V0_HIST_MC_QA_MAP +#undef V0_HIST_FITVARS_MC_MAP constexpr char PrefixLambdaQa[] = "LambdaQA/"; -constexpr char PrefixLambda1[] = "Lambda1/"; -constexpr char PrefixLambda2[] = "Lambda2/"; +// same-event / mixed-event siblings: single-candidate histograms for a given slot, split by +// whether the candidate came from a same-event or mixed-event pair +constexpr char PrefixLambda1Se[] = "Lambda1/SE/"; +constexpr char PrefixLambda2Se[] = "Lambda2/SE/"; +constexpr char PrefixLambda1Me[] = "Lambda1/ME/"; +constexpr char PrefixLambda2Me[] = "Lambda2/ME/"; constexpr char PrefixK0shortQa[] = "K0shortQa/"; -constexpr char PrefixK0short1[] = "K0short1/"; -constexpr char PrefixK0short2[] = "K0short2/"; +constexpr char PrefixK0short1Se[] = "K0short1/SE/"; +constexpr char PrefixK0short2Se[] = "K0short2/SE/"; +constexpr char PrefixK0short1Me[] = "K0short1/ME/"; +constexpr char PrefixK0short2Me[] = "K0short2/ME/"; constexpr char PrefixLambdaCascade[] = "LambdaCascadeQa/"; constexpr std::string_view AnalysisDir = "Analysis/"; constexpr std::string_view QaDir = "QA/"; constexpr std::string_view McDir = "MC/"; +constexpr std::string_view FitVarsDir = "FitVars/"; /// \class FemtoDreamEventHisto /// \brief Class for histogramming event properties @@ -371,6 +388,9 @@ class V0HistManager if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { this->initMc(V0Specs); } + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { + this->template initFitVars(V0Specs); + } } // init for analysis and qa and and mc @@ -422,6 +442,9 @@ class V0HistManager if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { this->initMc(V0Specs); } + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { + this->template initFitVars(V0Specs); + } } template @@ -438,6 +461,9 @@ class V0HistManager if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { this->fillQa(v0candidate, posDaughter, negDaughter); } + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { + this->fillFitVars(v0candidate); + } } template @@ -456,6 +482,9 @@ class V0HistManager if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { this->template fillMc(v0candidate, col, mcParticles, mcMothers, mcPartonicMothers); } + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { + this->fillFitVars(v0candidate); + } } private: @@ -504,7 +533,6 @@ class V0HistManager mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsEta, HistTable), getHistDesc(kPtVsEta, HistTable), getHistType(kPtVsEta, HistTable), {V0Specs.at(kPtVsEta)}); mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsPhi, HistTable), getHistDesc(kPtVsPhi, HistTable), getHistType(kPtVsPhi, HistTable), {V0Specs.at(kPtVsPhi)}); mHistogramRegistry->add(qaDir + getHistNameV2(kPhiVsEta, HistTable), getHistDesc(kPhiVsEta, HistTable), getHistType(kPhiVsEta, HistTable), {V0Specs.at(kPhiVsEta)}); - mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsCosPa, HistTable), getHistDesc(kPtVsCosPa, HistTable), getHistType(kPtVsCosPa, HistTable), {V0Specs.at(kPtVsCosPa)}); mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsPa, HistTable), getHistDesc(kPtVsPa, HistTable), getHistType(kPtVsPa, HistTable), {V0Specs.at(kPtVsPa)}); mHistogramRegistry->add(qaDir + getHistNameV2(kMassLambda, HistTable), getHistDesc(kMassLambda, HistTable), getHistType(kMassLambda, HistTable), {V0Specs.at(kMassLambda)}); @@ -518,6 +546,7 @@ class V0HistManager mHistogramRegistry->add(qaDir + getHistNameV2(kK0shortMassVsAntiLambdaMass, HistTable), getHistDesc(kK0shortMassVsAntiLambdaMass, HistTable), getHistType(kK0shortMassVsAntiLambdaMass, HistTable), {V0Specs.at(kK0shortMassVsAntiLambdaMass)}); mHistogramRegistry->add(qaDir + getHistNameV2(kStrangeTofVsTofPosDau, HistTable), getHistDesc(kStrangeTofVsTofPosDau, HistTable), getHistType(kStrangeTofVsTofPosDau, HistTable), {V0Specs.at(kStrangeTofVsTofPosDau)}); mHistogramRegistry->add(qaDir + getHistNameV2(kStrangeTofVsTofNegDau, HistTable), getHistDesc(kStrangeTofVsTofNegDau, HistTable), getHistType(kStrangeTofVsTofNegDau, HistTable), {V0Specs.at(kStrangeTofVsTofNegDau)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kArmenterosQtVsAlpha, HistTable), getHistDesc(kArmenterosQtVsAlpha, HistTable), getHistType(kArmenterosQtVsAlpha, HistTable), {V0Specs.at(kArmenterosQtVsAlpha)}); } } @@ -541,24 +570,36 @@ class V0HistManager mHistogramRegistry->add(mcDir + getHistNameV2(kPdg, HistTable), getHistDesc(kPdg, HistTable), getHistType(kPdg, HistTable), {V0Specs.at(kPdg)}); mHistogramRegistry->add(mcDir + getHistNameV2(kPdgMother, HistTable), getHistDesc(kPdgMother, HistTable), getHistType(kPdgMother, HistTable), {V0Specs.at(kPdgMother)}); mHistogramRegistry->add(mcDir + getHistNameV2(kPdgPartonicMother, HistTable), getHistDesc(kPdgPartonicMother, HistTable), getHistType(kPdgPartonicMother, HistTable), {V0Specs.at(kPdgPartonicMother)}); + } - if (mPlotOrigins) { - mHistogramRegistry->add(mcDir + getHistNameV2(kNoMcParticle, HistTable), getHistDesc(kNoMcParticle, HistTable), getHistType(kNoMcParticle, HistTable), {V0Specs.at(kNoMcParticle)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kPrimary, HistTable), getHistDesc(kPrimary, HistTable), getHistType(kPrimary, HistTable), {V0Specs.at(kPrimary)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kFromWrongCollision, HistTable), getHistDesc(kFromWrongCollision, HistTable), getHistType(kFromWrongCollision, HistTable), {V0Specs.at(kFromWrongCollision)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kFromMaterial, HistTable), getHistDesc(kFromMaterial, HistTable), getHistType(kFromMaterial, HistTable), {V0Specs.at(kFromMaterial)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kMissidentified, HistTable), getHistDesc(kMissidentified, HistTable), getHistType(kMissidentified, HistTable), {V0Specs.at(kMissidentified)}); + // fraction estimation (CPA); only called when mode carries kFitVars + template + void initFitVars(std::map> const& V0Specs) + { + std::string fitVarsDir = std::string(v0Prefix) + std::string(FitVarsDir); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kFractionCpa, HistTable), getHistDesc(kFractionCpa, HistTable), getHistType(kFractionCpa, HistTable), {V0Specs.at(kFractionCpa)}); - if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel1) { - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondary1, HistTable), getHistDesc(kSecondary1, HistTable), getHistType(kSecondary1, HistTable), {V0Specs.at(kSecondary1)}); - } - if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel2) { - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondary2, HistTable), getHistDesc(kSecondary2, HistTable), getHistType(kSecondary2, HistTable), {V0Specs.at(kSecondary2)}); - } - if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel3) { - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondary3, HistTable), getHistDesc(kSecondary3, HistTable), getHistType(kSecondary3, HistTable), {V0Specs.at(kSecondary3)}); + // per-origin truth-split fraction-CPA templates; need MC truth, so only booked when the mode + // also carries kMc + if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { + if (mPlotOrigins) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kNoMcParticle, HistTable), getHistDesc(kNoMcParticle, HistTable), getHistType(kNoMcParticle, HistTable), {V0Specs.at(kNoMcParticle)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kPrimary, HistTable), getHistDesc(kPrimary, HistTable), getHistType(kPrimary, HistTable), {V0Specs.at(kPrimary)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kFromWrongCollision, HistTable), getHistDesc(kFromWrongCollision, HistTable), getHistType(kFromWrongCollision, HistTable), {V0Specs.at(kFromWrongCollision)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kFromMaterial, HistTable), getHistDesc(kFromMaterial, HistTable), getHistType(kFromMaterial, HistTable), {V0Specs.at(kFromMaterial)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kMissidentified, HistTable), getHistDesc(kMissidentified, HistTable), getHistType(kMissidentified, HistTable), {V0Specs.at(kMissidentified)}); + + if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel1) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondary1, HistTable), getHistDesc(kSecondary1, HistTable), getHistType(kSecondary1, HistTable), {V0Specs.at(kSecondary1)}); + } + if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel2) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondary2, HistTable), getHistDesc(kSecondary2, HistTable), getHistType(kSecondary2, HistTable), {V0Specs.at(kSecondary2)}); + } + if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel3) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondary3, HistTable), getHistDesc(kSecondary3, HistTable), getHistType(kSecondary3, HistTable), {V0Specs.at(kSecondary3)}); + } + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondaryOther, HistTable), getHistDesc(kSecondaryOther, HistTable), getHistType(kSecondaryOther, HistTable), {V0Specs.at(kSecondaryOther)}); } - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondaryOther, HistTable), getHistDesc(kSecondaryOther, HistTable), getHistType(kSecondaryOther, HistTable), {V0Specs.at(kSecondaryOther)}); } } @@ -579,6 +620,12 @@ class V0HistManager } } + template + void fillFitVars(T const& v0candidate) + { + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(FitVarsDir) + HIST(getHistName(kFractionCpa, HistTable)), v0candidate.pt(), v0candidate.cosPa()); + } + template void fillQa(T1 const& v0candidate, T2 const& posDau, T3 const& negDau) { @@ -629,7 +676,6 @@ class V0HistManager mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsEta, HistTable)), v0candidate.pt(), v0candidate.eta()); mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsPhi, HistTable)), v0candidate.pt(), v0candidate.phi()); mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kPhiVsEta, HistTable)), v0candidate.phi(), v0candidate.eta()); - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsCosPa, HistTable)), v0candidate.pt(), v0candidate.cosPa()); mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsPa, HistTable)), v0candidate.pt(), v0candidate.pa()); mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsLambdaMass, HistTable)), v0candidate.pt(), massLambda); @@ -640,6 +686,7 @@ class V0HistManager mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kK0shortMassVsAntiLambdaMass, HistTable)), massK0short, massAntiLambda); mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kStrangeTofVsTofPosDau, HistTable)), v0candidate.strangeTofPosDau(), tofPosDau); mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kStrangeTofVsTofNegDau, HistTable)), v0candidate.strangeTofNegDau(), tofNegDau); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kArmenterosQtVsAlpha, HistTable)), v0candidate.alpha(), v0candidate.qtArm()); } } @@ -650,9 +697,9 @@ class V0HistManager if (!v0Candidate.has_fMcParticle()) { mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kPdg, HistTable)), 0); mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kOrigin, HistTable)), static_cast(modes::McOrigin::kNoMcParticle)); - if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { if (mPlotOrigins) { - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kNoMcParticle, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(FitVarsDir) + HIST(getHistName(kNoMcParticle, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); } } return; @@ -695,25 +742,22 @@ class V0HistManager mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kPdgPartonicMother, HistTable)), 0); } - if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { if (mPlotOrigins) { // check first if particle is missidentified if (fromWrongCollision) { - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kFromWrongCollision, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(FitVarsDir) + HIST(getHistName(kFromWrongCollision, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); } else if (isMissidentified) { // if it is, we fill it as such - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kMissidentified, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(FitVarsDir) + HIST(getHistName(kMissidentified, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); } else { // if not, we fill it acccoridng to its origin switch (static_cast(mcParticle.origin())) { case modes::McOrigin::kPhysicalPrimary: - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kPrimary, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); - break; - case modes::McOrigin::kFromWrongCollision: - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kFromWrongCollision, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(FitVarsDir) + HIST(getHistName(kPrimary, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); break; case modes::McOrigin::kFromMaterial: - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kFromMaterial, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(FitVarsDir) + HIST(getHistName(kFromMaterial, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); break; case modes::McOrigin::kFromSecondaryDecay: if (mcParticle.has_fMcMother()) { @@ -721,18 +765,18 @@ class V0HistManager int motherPdgCode = std::abs(mother.pdgCode()); // Switch on PDG of the mother if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel1 && motherPdgCode == mPdgCodesSecondaryMother[0]) { - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kSecondary1, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondary1, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); } else if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel2 && motherPdgCode == mPdgCodesSecondaryMother[1]) { - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kSecondary2, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondary2, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); } else if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel3 && motherPdgCode == mPdgCodesSecondaryMother[2]) { - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kSecondary3, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondary3, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); } else { - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kSecondaryOther, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondaryOther, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); } } break; default: - LOG(warn) << "Encounted partilce with unknown origin!"; + LOG(warn) << "Encountered particle with unknown origin!"; break; } } @@ -742,7 +786,10 @@ class V0HistManager o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; int mPdgCode = 0; - bool mPlotOrigins = false; + // true by default so the per-origin FitVars templates are booked/filled even where + // enableOptionalHistograms() is never called (e.g. the pair task, which has no QA-binning + // config); the QA task can still opt out via its plotOrigins Configurable + bool mPlotOrigins = true; int mPlotNSecondaries = 0; std::array mPdgCodesSecondaryMother = {0}; bool mPlot2d = false; diff --git a/PWGCF/Femto/DataModel/FemtoTables.h b/PWGCF/Femto/DataModel/FemtoTables.h index 958fb9cb99d..651e591d3d6 100644 --- a/PWGCF/Femto/DataModel/FemtoTables.h +++ b/PWGCF/Femto/DataModel/FemtoTables.h @@ -143,6 +143,18 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FColMasks_001, "FCOLMASK", 1, //! collision m using FColMasks = FColMasks_001; using StoredFColMasks = StoredFColMasks_001; +// table for collision tags: one bit per (wagon, pair type) combination the collision tagger was +// self-configured to check, set if the collision has what it takes to form at least one pair of +// that type (both particle-1-role and particle-2-role candidates present, or, for a same-species +// pair, at least 2 distinct candidates passing the one shared selection). Used to pre-filter the +// mixed-event pool instead of discovering empty collisions only after slicing. Which bit means +// what is assigned at self-configuration time and logged by the tagger; see +// PWGCF/Femto/TableProducer/femtoProducerCollisionTagger.cxx. +DECLARE_SOA_TABLE_STAGED_VERSIONED(FColTags_001, "FCOLTAG", 1, //! collision tags + femtocollisions::CollisionTag); +using FColTags = FColTags_001; +using StoredFColTags = StoredFColTags_001; + DECLARE_SOA_TABLE_STAGED_VERSIONED(FColSphericities_001, "FCOLSPHERICITY", 1, //! sphericity femtocollisions::Sphericity); using FColSphericities = FColSphericities_001; @@ -391,6 +403,38 @@ DECLARE_SOA_DYNAMIC_COLUMN(TpctofNSigmaDe, tpctofNSigmaDe, [](float tpc, float t DECLARE_SOA_DYNAMIC_COLUMN(TpctofNSigmaTr, tpctofNSigmaTr, [](float tpc, float tof) -> float { return std::hypot(tpc, tof); }); //! Combined Nsigma separation with Tpc and Tof for triton DECLARE_SOA_DYNAMIC_COLUMN(TpctofNSigmaHe, tpctofNSigmaHe, [](float tpc, float tof) -> float { return std::hypot(tpc, tof); }); //! Combined Nsigma separation with Tpc and Tof for helium3 +// --------------------------------------------------------------------------- +// purity: up to 4 species' nSigma (per detector: Its, Tpc, Tof) packed into a +// single int64_t column each, see utils::packPurity4/unpackPurity4. Which +// species occupies which of the 4 slots is decided at producer init time +// (from the active requirePidXxx flags, in canonical species order) and +// exposed via a self-documenting histogram -- it is not stored per-row. +// --------------------------------------------------------------------------- +constexpr float NSigmaPurityMagMin = 1e-3f; // resolution floor near the signal peak +constexpr float NSigmaPurityMagMax = 50.f; // clamp for the tails + +DECLARE_SOA_COLUMN(ItsPackedNSigma, itsPackedNSigma, int64_t); //! 4 species' Its nSigma, log-binned and packed +DECLARE_SOA_COLUMN(TpcPackedNSigma, tpcPackedNSigma, int64_t); //! 4 species' Tpc nSigma, log-binned and packed +DECLARE_SOA_COLUMN(TofPackedNSigma, tofPackedNSigma, int64_t); //! 4 species' Tof nSigma, log-binned and packed +// if false, all 4 Tof slots are meaningless placeholders (producer writes 0 to all of them) -- +// check this flag before reading any TofNSigmaSlotN; never infer missingness from the slot value +DECLARE_SOA_COLUMN(PurityHasTof, purityHasTof, bool); //! whether the Tof slots are meaningful for this track (species-independent) + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) +// each dynamic column below is bound to its PackedNSigma column at the table-declaration +// site, e.g. femtotracks::ItsNSigmaSlot0 +#define FEMTO_PURITY_SLOT_COLUMN(Detector, detector) \ + DECLARE_SOA_DYNAMIC_COLUMN(Detector##NSigmaSlot0, detector##NSigmaSlot0, [](int64_t packed) -> float { return o2::analysis::femto::utils::unpackPurityNSigma4(packed, 0, NSigmaPurityMagMin, NSigmaPurityMagMax); }); \ + DECLARE_SOA_DYNAMIC_COLUMN(Detector##NSigmaSlot1, detector##NSigmaSlot1, [](int64_t packed) -> float { return o2::analysis::femto::utils::unpackPurityNSigma4(packed, 1, NSigmaPurityMagMin, NSigmaPurityMagMax); }); \ + DECLARE_SOA_DYNAMIC_COLUMN(Detector##NSigmaSlot2, detector##NSigmaSlot2, [](int64_t packed) -> float { return o2::analysis::femto::utils::unpackPurityNSigma4(packed, 2, NSigmaPurityMagMin, NSigmaPurityMagMax); }); \ + DECLARE_SOA_DYNAMIC_COLUMN(Detector##NSigmaSlot3, detector##NSigmaSlot3, [](int64_t packed) -> float { return o2::analysis::femto::utils::unpackPurityNSigma4(packed, 3, NSigmaPurityMagMin, NSigmaPurityMagMax); }); + +FEMTO_PURITY_SLOT_COLUMN(Its, its) +FEMTO_PURITY_SLOT_COLUMN(Tpc, tpc) +FEMTO_PURITY_SLOT_COLUMN(Tof, tof) +#undef FEMTO_PURITY_SLOT_COLUMN +// NOLINTEND(cppcoreguidelines-macro-usage) + } // namespace femtotracks // table for basic track information @@ -445,6 +489,7 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FTrackDcas_001, "FTRACKDCAS", 1, //! track dc femtotracks::DcaZ, femtotracks::Dca); using FTrackDcas = FTrackDcas_001; +using StoredFTrackDcas = StoredFTrackDcas_001; // table for extra track information DECLARE_SOA_TABLE_STAGED_VERSIONED(FTrackExtras_001, "FTRACKEXTRA", 1, //! track extra information @@ -523,6 +568,39 @@ using FHeliumPids = FHeliumPids_001; using FTrackPids = soa::Join; +// tables for track purity extraction: up to 4 species' nSigma packed per detector (see +// femtotracks::PurityNSlots / utils::packPurity4). Slot assignment is not stored here -- +// it is exposed separately via a self-documenting histogram at producer init time. +DECLARE_SOA_TABLE_STAGED_VERSIONED(FItsPurity_001, "FITSPURITY", 1, //! up to 4 species' Its nSigma, packed + femtotracks::ItsPackedNSigma, + femtotracks::ItsNSigmaSlot0, + femtotracks::ItsNSigmaSlot1, + femtotracks::ItsNSigmaSlot2, + femtotracks::ItsNSigmaSlot3); +using FItsPurity = FItsPurity_001; +using StoredFItsPurity = StoredFItsPurity_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FTpcPurity_001, "FTPCPURITY", 1, //! up to 4 species' Tpc nSigma, packed + femtotracks::TpcPackedNSigma, + femtotracks::TpcNSigmaSlot0, + femtotracks::TpcNSigmaSlot1, + femtotracks::TpcNSigmaSlot2, + femtotracks::TpcNSigmaSlot3); +using FTpcPurity = FTpcPurity_001; +using StoredFTpcPurity = StoredFTpcPurity_001; + +// also carries PurityHasTof (species-independent: whether this track reached Tof at all) -- +// the most natural home for it, since it only matters for interpreting the Tof slots +DECLARE_SOA_TABLE_STAGED_VERSIONED(FTofPurity_001, "FTOFPURITY", 1, //! up to 4 species' Tof nSigma, packed + femtotracks::TofPackedNSigma, + femtotracks::PurityHasTof, + femtotracks::TofNSigmaSlot0, + femtotracks::TofNSigmaSlot1, + femtotracks::TofNSigmaSlot2, + femtotracks::TofNSigmaSlot3); +using FTofPurity = FTofPurity_001; +using StoredFTofPurity = StoredFTofPurity_001; + namespace femtotwotrackresonances { // columns for resonance bit masks @@ -552,12 +630,14 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FPhis_001, "FPHI", 1, //! femto phis femtobase::dynamic::Pz, femtobase::dynamic::Theta); using FPhis = FPhis_001; +using StoredFPhis = StoredFPhis_001; DECLARE_SOA_TABLE_STAGED_VERSIONED(FPhiMasks_001, "FPHIMASK", 1, //! mask for phis femtotwotrackresonances::MaskPosDau, femtotwotrackresonances::PosDauHasHighMomentum, femtotwotrackresonances::MaskNegDau, femtotwotrackresonances::NegDauHasHighMomentum); using FPhiMasks = FPhiMasks_001; +using StoredFPhiMasks = StoredFPhiMasks_001; // table for kstars DECLARE_SOA_TABLE_STAGED_VERSIONED(FKstar0s_001, "FKSTAR0", 1, //! femto k0star @@ -599,12 +679,14 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FRho0s_001, "FRHO0", 1, //! femto rho0s femtobase::dynamic::Pz, femtobase::dynamic::Theta); using FRho0s = FRho0s_001; +using StoredFRho0s = StoredFRho0s_001; DECLARE_SOA_TABLE_STAGED_VERSIONED(FRho0Masks_001, "FRHO0MASK", 1, //! rho0s masks femtotwotrackresonances::MaskPosDau, femtotwotrackresonances::PosDauHasHighMomentum, femtotwotrackresonances::MaskNegDau, femtotwotrackresonances::NegDauHasHighMomentum); using FRho0Masks = FRho0Masks_001; +using StoredFRho0Masks = StoredFRho0Masks_001; namespace femtov0s { @@ -637,6 +719,8 @@ DECLARE_SOA_DYNAMIC_COLUMN(DecayVtx, decayVtx, //! distance of d [](float vtxX, float vtxY, float vtxZ) -> float { return std::hypot(vtxX, vtxY, vtxZ); }); +DECLARE_SOA_COLUMN(Alpha, alpha, float); //! Armenteros-Podolanski alpha +DECLARE_SOA_COLUMN(QtArm, qtArm, float); //! Armenteros-Podolanski qt // id columns for Lambda daughter tracks DECLARE_SOA_INDEX_COLUMN_FULL(PosDau, posDau, int32_t, FTracks, "_PosDau"); //! index column for positive daughter track @@ -740,11 +824,18 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FLambdaExtras_001, "FLAMBDAEXTRA", 1, //! lam femtov0s::DecayVtxX, femtov0s::DecayVtxY, femtov0s::DecayVtxZ, + femtov0s::Alpha, + femtov0s::QtArm, femtov0s::DecayVtx, femtov0s::Pa); using FLambdaExtras = FLambdaExtras_001; +// table for lambda fraction extraction (cpa only -- mass is already in FLambdas, no need to duplicate it) +DECLARE_SOA_TABLE_STAGED_VERSIONED(FLambdaFrac_001, "FLAMBDAFRAC", 1, //! lambda cpa for fraction extraction + femtov0s::CosPa); +using FLambdaFrac = FLambdaFrac_001; + // table for basic k0short information DECLARE_SOA_TABLE_STAGED_VERSIONED(FK0shorts_001, "FK0SHORT", 1, //! femto k0shorts o2::soa::Index<>, @@ -800,11 +891,204 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FK0shortExtras_001, "FK0SHORTEXTRA", 1, //! k femtov0s::DecayVtxX, femtov0s::DecayVtxY, femtov0s::DecayVtxZ, + femtov0s::Alpha, + femtov0s::QtArm, femtov0s::DecayVtx, femtov0s::Pa); using FK0shortExtras = FK0shortExtras_001; +// table for k0short fraction extraction (cpa only -- mass is already in FK0shorts, no need to duplicate it) +DECLARE_SOA_TABLE_STAGED_VERSIONED(FK0shortFrac_001, "FK0SHORTFRAC", 1, //! k0short cpa for fraction extraction + femtov0s::CosPa); +using FK0shortFrac = FK0shortFrac_001; + +namespace femtophotons +{ +// columns for bit masks +DECLARE_SOA_COLUMN(Mask, mask, o2::analysis::femto::datatypes::PhotonMaskType); //! Bitmask for photon (PCM) selections + +// columns for debug information +DECLARE_SOA_COLUMN(CosPa, cosPa, float); //! Cosine of pointing angle of the V0 (conversion) to the primary vertex +DECLARE_SOA_DYNAMIC_COLUMN(Pa, pa, //! pointing angle + [](float cosPa) -> float { + return std::acos(cosPa); + }); +DECLARE_SOA_COLUMN(DcaToPvXY, dcaToPvXY, float); //! DCAxy of the photon (V0) to the primary vertex +DECLARE_SOA_COLUMN(DcaToPvZ, dcaToPvZ, float); //! DCAz of the photon (V0) to the primary vertex +DECLARE_SOA_COLUMN(Chi2Ndf, chi2Ndf, float); //! Chi2 / NDF of the reconstructed conversion vertex (KF) +DECLARE_SOA_COLUMN(V0Radius, v0Radius, float); //! Transverse radius of the conversion point +DECLARE_SOA_COLUMN(PosDauTpcNSigmaEl, posDauTpcNSigmaEl, float); //! TPC electron nSigma of the positive daughter +DECLARE_SOA_COLUMN(NegDauTpcNSigmaEl, negDauTpcNSigmaEl, float); //! TPC electron nSigma of the negative daughter +DECLARE_SOA_COLUMN(PosDauPt, posDauPt, float); //! pT of the positive daughter (at the conversion point) +DECLARE_SOA_COLUMN(NegDauPt, negDauPt, float); //! pT of the negative daughter (at the conversion point) +DECLARE_SOA_COLUMN(PosDauTpcInnerParam, posDauTpcInnerParam, float); //! momentum of the positive daughter at the inner wall of the TPC +DECLARE_SOA_COLUMN(NegDauTpcInnerParam, negDauTpcInnerParam, float); //! momentum of the negative daughter at the inner wall of the TPC +DECLARE_SOA_COLUMN(PosDauTpcSignal, posDauTpcSignal, float); //! TPC dE/dx of the positive daughter +DECLARE_SOA_COLUMN(NegDauTpcSignal, negDauTpcSignal, float); //! TPC dE/dx of the negative daughter +DECLARE_SOA_COLUMN(DecayVtxX, decayVtxX, float); //! x coordinate of conversion point +DECLARE_SOA_COLUMN(DecayVtxY, decayVtxY, float); //! y coordinate of conversion point +DECLARE_SOA_COLUMN(DecayVtxZ, decayVtxZ, float); //! z coordinate of conversion point +DECLARE_SOA_DYNAMIC_COLUMN(DecayVtx, decayVtx, //! distance of conversion point from nominal interaction point + [](float vtxX, float vtxY, float vtxZ) -> float { + return std::hypot(vtxX, vtxY, vtxZ); + }); +} // namespace femtophotons + +// table for basic PCM photon information (mass is fixed to 0 by construction, not stored) +DECLARE_SOA_TABLE_STAGED_VERSIONED(FPhotons_001, "FPHOTON", 1, //! femto PCM photons + o2::soa::Index<>, + femtobase::stored::FColId, + femtobase::stored::Pt, + femtobase::stored::Eta, + femtobase::stored::Phi, + femtobase::dynamic::P, + femtobase::dynamic::Px, + femtobase::dynamic::Py, + femtobase::dynamic::Pz, + femtobase::dynamic::Theta); +using FPhotons = FPhotons_001; +using StoredFPhotons = StoredFPhotons_001; + +// table for basic PCM photon information with compressed/binned kinematics +DECLARE_SOA_TABLE_STAGED_VERSIONED(FLitePhotons_001, "FLITEPHOTON", 1, + o2::soa::Index<>, + femtobase::stored::FLiteColId, + femtobase::lite::UnsignedBinnedPt, + femtobase::lite::BinnedEta, + femtobase::lite::BinnedPhi, + femtobase::lite::unsignedpt::Pt, + femtobase::lite::Eta, + femtobase::lite::Phi); +using FLitePhotons = FLitePhotons_001; +using FLitePhoton = FLitePhotons::iterator; +using StoredFLitePhotons = StoredFLitePhotons_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FPhotonMasks_001, "FPHOTONMASK", 1, //! photon (PCM) masks + femtophotons::Mask); +using FPhotonMasks = FPhotonMasks_001; +using StoredFPhotonMasks = StoredFPhotonMasks_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FPhotonExtras_001, "FPHOTONEXTRA", 1, //! photon (PCM) extra information + femtophotons::CosPa, + femtophotons::DcaToPvXY, + femtophotons::DcaToPvZ, + femtophotons::Chi2Ndf, + femtophotons::V0Radius, + femtophotons::PosDauPt, + femtophotons::NegDauPt, + femtophotons::PosDauTpcInnerParam, + femtophotons::NegDauTpcInnerParam, + femtophotons::PosDauTpcSignal, + femtophotons::NegDauTpcSignal, + femtophotons::PosDauTpcNSigmaEl, + femtophotons::NegDauTpcNSigmaEl, + femtophotons::DecayVtxX, + femtophotons::DecayVtxY, + femtophotons::DecayVtxZ, + femtophotons::DecayVtx, + femtophotons::Pa); +using FPhotonExtras = FPhotonExtras_001; +using StoredFPhotonExtras = StoredFPhotonExtras_001; + +namespace femtotwophotonresonances +{ +// column for the daughter (photon) bit mask -- no momentum-threshold bit: photon daughters have no +// momentum-dependent PID switch the way charged-track daughters do in femtotwotrackresonances. Both +// legs are plain photons with the same selection requirement, so only the AND of both legs' own +// masks is stored (not one column per leg): requiring a set of bits in the AND is equivalent to +// requiring that same set of bits in both legs individually. +DECLARE_SOA_COLUMN(MaskDau, maskDau, o2::analysis::femto::datatypes::PhotonMaskType); //! Bitmask for the photon daughters (AND of both legs' own masks) + +// id columns for the two photon daughters (unordered, no pos/neg distinction) +DECLARE_SOA_INDEX_COLUMN_FULL(Dau1, dau1, int32_t, FPhotons, "_Dau1"); //! index column for first photon daughter +DECLARE_SOA_INDEX_COLUMN_FULL(Dau2, dau2, int32_t, FPhotons, "_Dau2"); //! index column for second photon daughter +} // namespace femtotwophotonresonances + +// table for pi0s (-> gamma gamma) +DECLARE_SOA_TABLE_STAGED_VERSIONED(FPi0s_001, "FPI0", 1, //! femto pi0s + o2::soa::Index<>, + femtobase::stored::FColId, + femtobase::stored::Pt, + femtobase::stored::Eta, + femtobase::stored::Phi, + femtobase::stored::Mass, + femtotwophotonresonances::Dau1Id, + femtotwophotonresonances::Dau2Id, + femtobase::dynamic::P, + femtobase::dynamic::Px, + femtobase::dynamic::Py, + femtobase::dynamic::Pz, + femtobase::dynamic::Theta); +using FPi0s = FPi0s_001; +DECLARE_SOA_TABLE_STAGED_VERSIONED(FPi0Masks_001, "FPI0MASK", 1, //! mask for pi0s + femtotwophotonresonances::MaskDau); +using FPi0Masks = FPi0Masks_001; + +// table for etas (-> gamma gamma) +DECLARE_SOA_TABLE_STAGED_VERSIONED(FEtas_001, "FETA", 1, //! femto etas + o2::soa::Index<>, + femtobase::stored::FColId, + femtobase::stored::Pt, + femtobase::stored::Eta, + femtobase::stored::Phi, + femtobase::stored::Mass, + femtotwophotonresonances::Dau1Id, + femtotwophotonresonances::Dau2Id, + femtobase::dynamic::P, + femtobase::dynamic::Px, + femtobase::dynamic::Py, + femtobase::dynamic::Pz, + femtobase::dynamic::Theta); +using FEtas = FEtas_001; +DECLARE_SOA_TABLE_STAGED_VERSIONED(FEtaMasks_001, "FETAMASK", 1, //! mask for etas + femtotwophotonresonances::MaskDau); +using FEtaMasks = FEtaMasks_001; + +namespace femtotrackstwophotonresonances +{ +// columns for daughter bit masks, copied onto the candidate at build time -- same pattern as +// femtotwotrackresonances: systematic reselection happens on the candidate itself, not by rejoining +// the daughter tables. The two charged daughters keep their own momentum-threshold PID switch; the +// neutral (pi0/eta) daughter has no such switch and no dedicated mask of its own -- it is a direct +// copy of its FPi0Masks/FEtaMasks maskDau (itself already the AND of its two photon legs). +DECLARE_SOA_COLUMN(MaskPosDau, maskPosDau, o2::analysis::femto::datatypes::TrackMaskType); //! Bitmask for positive charged-track daughter +DECLARE_SOA_COLUMN(PosDauHasHighMomentum, posDauHasHighMomentum, bool); //! switch for pid threshold +DECLARE_SOA_COLUMN(MaskNegDau, maskNegDau, o2::analysis::femto::datatypes::TrackMaskType); //! Bitmask for negative charged-track daughter +DECLARE_SOA_COLUMN(NegDauHasHighMomentum, negDauHasHighMomentum, bool); //! switch for pid threshold +DECLARE_SOA_COLUMN(MaskNeutralDau, maskNeutralDau, o2::analysis::femto::datatypes::PhotonMaskType); //! Bitmask for the two-photon-resonance daughter (copy of its own maskDau) + +// id columns for the two charged-track daughters and the two-photon-resonance daughter +DECLARE_SOA_INDEX_COLUMN_FULL(PosDau, posDau, int32_t, FTracks, "_PosDau"); //! index column for the positive charged-track daughter +DECLARE_SOA_INDEX_COLUMN_FULL(NegDau, negDau, int32_t, FTracks, "_NegDau"); //! index column for the negative charged-track daughter +DECLARE_SOA_INDEX_COLUMN_FULL(NeutralDau, neutralDau, int32_t, FPi0s, "_NeutralDau"); //! index column for the two-photon-resonance daughter +} // namespace femtotrackstwophotonresonances + +// table for resonances built from two tracks and a two-photon resonance (e.g. omega(782) -> pi+ pi- pi0) +DECLARE_SOA_TABLE_STAGED_VERSIONED(FOmegaMesons_001, "FOMEMES", 1, //! femto omega mesons + o2::soa::Index<>, + femtobase::stored::FColId, + femtobase::stored::Pt, + femtobase::stored::Eta, + femtobase::stored::Phi, + femtobase::stored::Mass, + femtotrackstwophotonresonances::PosDauId, + femtotrackstwophotonresonances::NegDauId, + femtotrackstwophotonresonances::NeutralDauId, + femtobase::dynamic::P, + femtobase::dynamic::Px, + femtobase::dynamic::Py, + femtobase::dynamic::Pz, + femtobase::dynamic::Theta); +using FOmegaMesons = FOmegaMesons_001; +DECLARE_SOA_TABLE_STAGED_VERSIONED(FOmegaMesonMasks_001, "FOMEMESMASK", 1, //! mask for omega mesons + femtotrackstwophotonresonances::MaskPosDau, + femtotrackstwophotonresonances::PosDauHasHighMomentum, + femtotrackstwophotonresonances::MaskNegDau, + femtotrackstwophotonresonances::NegDauHasHighMomentum, + femtotrackstwophotonresonances::MaskNeutralDau); +using FOmegaMesonMasks = FOmegaMesonMasks_001; + namespace femtokinks { // columns for bit masks @@ -818,6 +1102,23 @@ DECLARE_SOA_COLUMN(DecayVtxX, decayVtxX, float); //! x coordinate of decay v DECLARE_SOA_COLUMN(DecayVtxY, decayVtxY, float); //! y coordinate of decay vertex (relative to PV) DECLARE_SOA_COLUMN(DecayVtxZ, decayVtxZ, float); //! z coordinate of decay vertex (relative to PV) DECLARE_SOA_COLUMN(TransRadius, transRadius, float); //! Transverse decay radius from PV +DECLARE_SOA_COLUMN(DcaKinkTopo, dcaKinkTopo, float); +DECLARE_SOA_COLUMN(DauTpcNSigmaPr, dauTpcNSigmaPr, float); +DECLARE_SOA_COLUMN(DauTpcNSigmaPi, dauTpcNSigmaPi, float); +DECLARE_SOA_COLUMN(DauTofNSigmaPr, dauTofNSigmaPr, float); +DECLARE_SOA_COLUMN(PtOriginal, ptOriginal, float); + +// columns for sigma plus reconstructed with a PCM photon +DECLARE_SOA_COLUMN(MlScore, mlScore, float); //! BDT score of the candidate +DECLARE_SOA_COLUMN(FlightDirTilt, flightDirTilt, float); //! Tilt of the flight direction needed for a real solution of the missing photon +DECLARE_SOA_COLUMN(DauDcaXY, dauDcaXY, float); //! DCAxy of the charged daughter to the primary vertex +DECLARE_SOA_COLUMN(DauDcaZ, dauDcaZ, float); //! DCAz of the charged daughter to the primary vertex +DECLARE_SOA_COLUMN(PhotonDcaXYToPV, photonDcaXYToPV, float); //! Transverse DCA of the photon flight line to the primary vertex +DECLARE_SOA_COLUMN(PhotonDcaZToPV, photonDcaZToPV, float); //! Longitudinal DCA of the photon flight line to the primary vertex +DECLARE_SOA_COLUMN(PhotonCosPAToPV, photonCosPAToPV, float); //! Cosine of the photon pointing angle to the primary vertex +DECLARE_SOA_COLUMN(PhotonConvRadius, photonConvRadius, float); //! Conversion radius of the photon +DECLARE_SOA_COLUMN(AntiSigmaPointingAngle, antiSigmaPointingAngle, float); //! Angle between the unbent proton momentum and the flight direction +DECLARE_SOA_COLUMN(DcaDauPhoton, dcaDauPhoton, float); //! DCA between charged daughter and photon at the decay vertex // id column for charged daughter track DECLARE_SOA_INDEX_COLUMN_FULL(ChaDau, chaDau, int32_t, FTracks, "_ChaDau"); //! @@ -971,7 +1272,89 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmaPlusExtras_001, "FSIGMAPLUSEXTRAS", 1, femtokinks::DecayVtxZ, femtokinks::TransRadius); -using FSigmaPlusExtras = FSigmaPlusExtras_001; +DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmaPlusExtras_002, "FSIGMAPLUSEXTRAS", 2, + femtokinks::KinkAngle, + femtokinks::DcaDaugToPV, + femtokinks::DcaMothToPV, + femtokinks::DecayVtxX, + femtokinks::DecayVtxY, + femtokinks::DecayVtxZ, + femtokinks::TransRadius, + femtokinks::DcaKinkTopo, + femtokinks::DauTpcNSigmaPr, + femtokinks::DauTpcNSigmaPi, + femtokinks::DauTofNSigmaPr, + femtokinks::PtOriginal, + femtotracks::ItsClusterSizes); + +using FSigmaPlusExtras = FSigmaPlusExtras_002; +using StoredFSigmaPlusExtras = StoredFSigmaPlusExtras_002; + +// table for basic sigma plus information, sigma plus reconstructed with a PCM photon +DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmaPlusPcm_001, "FSIGMAPLUSPCM", 1, + o2::soa::Index<>, + femtobase::stored::FColId, + femtobase::stored::SignedPt, // use sign to differentiate between sigma plus (+1) and anti sigma plus (-1) + femtobase::stored::Eta, + femtobase::stored::Phi, + femtobase::stored::Mass, + femtokinks::ChaDauId, + femtobase::dynamic::Sign, + femtobase::dynamic::Pt, + femtobase::dynamic::P, + femtobase::dynamic::Px, + femtobase::dynamic::Py, + femtobase::dynamic::Pz, + femtobase::dynamic::Theta); +using FSigmaPlusPcm = FSigmaPlusPcm_001; +using FSigmaPlusPcmRow = FSigmaPlusPcm::iterator; +using StoredFSigmaPlusPcm = StoredFSigmaPlusPcm_001; + +// table for basic sigma plus information, sigma plus reconstructed with a PCM photon, compressed/binned kinematics +DECLARE_SOA_TABLE_STAGED_VERSIONED(FLiteSigmaPlusPcm_001, "FLITESIGMAPCM", 1, + o2::soa::Index<>, + femtobase::stored::FLiteColId, + femtobase::lite::SignedBinnedPt, + femtobase::lite::BinnedEta, + femtobase::lite::BinnedPhi, + femtokinks::lite::BinnedSigmaMass, + femtokinks::lite::ChaDauId, + femtobase::lite::Sign, + femtobase::lite::SignedPt, + femtobase::lite::signedpt::Pt, + femtobase::lite::Eta, + femtobase::lite::Phi, + femtokinks::lite::SigmaMass); +using FLiteSigmaPlusPcm = FLiteSigmaPlusPcm_001; +using FLiteSigmaPlusPcmRow = FLiteSigmaPlusPcm::iterator; +using StoredFLiteSigmaPlusPcm = StoredFLiteSigmaPlusPcm_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmaPlusPcmMasks_001, "FSIGMAPCMMASKS", 1, + femtokinks::Mask); +using FSigmaPlusPcmMasks = FSigmaPlusPcmMasks_001; +using StoredFSigmaPlusPcmMasks = StoredFSigmaPlusPcmMasks_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmaPlusPcmMlScores_001, "FSIGMAPCMML", 1, + femtokinks::MlScore); +using FSigmaPlusPcmMlScores = FSigmaPlusPcmMlScores_001; +using StoredFSigmaPlusPcmMlScores = StoredFSigmaPlusPcmMlScores_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmaPlusPcmExtras_001, "FSIGMAPCMEXTRA", 1, + femtokinks::FlightDirTilt, + femtokinks::DcaMothToPV, + femtokinks::DauDcaXY, + femtokinks::DauDcaZ, + femtokinks::DauTpcNSigmaPr, + femtokinks::DauTofNSigmaPr, + femtokinks::PhotonDcaXYToPV, + femtokinks::PhotonDcaZToPV, + femtokinks::PhotonCosPAToPV, + femtokinks::PhotonConvRadius, + femtokinks::AntiSigmaPointingAngle, + femtokinks::DcaDauPhoton, + femtokinks::TransRadius); +using FSigmaPlusPcmExtras = FSigmaPlusPcmExtras_001; +using StoredFSigmaPlusPcmExtras = StoredFSigmaPlusPcmExtras_001; namespace femtocascades { @@ -1106,6 +1489,12 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FXiExtras_001, "FXIEXTRA", 1, //! xi extra in femtocascades::CascadePa); using FXiExtras = FXiExtras_001; +// table for xi fraction extraction (cascade cpa only -- mass is already in FXis, and the daughter +// lambda's own cpa is not needed here) +DECLARE_SOA_TABLE_STAGED_VERSIONED(FXiFrac_001, "FXIFRAC", 1, //! xi cpa for fraction extraction + femtocascades::CascadeCosPa); +using FXiFrac = FXiFrac_001; + DECLARE_SOA_TABLE_STAGED_VERSIONED(FOmegas_001, "FOMEGA", 1, //! femto omegas o2::soa::Index<>, femtobase::stored::FColId, @@ -1172,6 +1561,12 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FOmegaExtras_001, "FOMEGAEXTRA", 1, //! omega femtocascades::CascadePa); using FOmegaExtras = FOmegaExtras_001; +// table for omega fraction extraction (cascade cpa only -- mass is already in FOmegas, and the daughter +// lambda's own cpa is not needed here) +DECLARE_SOA_TABLE_STAGED_VERSIONED(FOmegaFrac_001, "FOMEGAFRAC", 1, //! omega cpa for fraction extraction + femtocascades::CascadeCosPa); +using FOmegaFrac = FOmegaFrac_001; + namespace femtocharmhadrons { // bitmask column @@ -1449,6 +1844,13 @@ using FSigmaPlusLabel = FSigmaPlusLabels::iterator; using StoredFSigmaPlusLabels = StoredFSigmaPlusLabels_001; using StoredFSigmaPlusLabel = StoredFSigmaPlusLabels::iterator; +DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmaPlusPcmLabels_001, "FSIGMAPCMLABEL", 1, //! label from femto sigma plus (PCM) to femto mc particle + femtolabels::FMcParticleId); +using FSigmaPlusPcmLabels = FSigmaPlusPcmLabels_001; +using FSigmaPlusPcmLabel = FSigmaPlusPcmLabels::iterator; +using StoredFSigmaPlusPcmLabels = StoredFSigmaPlusPcmLabels_001; +using StoredFSigmaPlusPcmLabel = StoredFSigmaPlusPcmLabels::iterator; + DECLARE_SOA_TABLE_STAGED_VERSIONED(FXiLabels_001, "FXILABEL", 1, //! label from femto xi to femto mc particle femtolabels::FMcParticleId); using FXiLabels = FXiLabels_001; diff --git a/PWGCF/Femto/FemtoNuclei/DataModel/HadronNucleiTables.h b/PWGCF/Femto/FemtoNuclei/DataModel/HadronNucleiTables.h index 161be5544ab..98b3e7e14dc 100644 --- a/PWGCF/Femto/FemtoNuclei/DataModel/HadronNucleiTables.h +++ b/PWGCF/Femto/FemtoNuclei/DataModel/HadronNucleiTables.h @@ -24,6 +24,64 @@ namespace o2::aod { +// Independent mixed-event audit output. IDs are unique only together with the +// two UUID words identifying the task instance. Not an extension of pair rows. +namespace hnmediag +{ +DECLARE_SOA_COLUMN(InstanceHi, instanceHi, uint64_t); +DECLARE_SOA_COLUMN(InstanceLo, instanceLo, uint64_t); +DECLARE_SOA_COLUMN(CallId, callId, uint64_t); +DECLARE_SOA_COLUMN(Timeslice, timeslice, uint64_t); +DECLARE_SOA_COLUMN(TfCounter, tfCounter, uint32_t); +DECLARE_SOA_COLUMN(TfRun, tfRun, uint32_t); +DECLARE_SOA_COLUMN(Kind, kind, uint8_t); +DECLARE_SOA_COLUMN(Status, status, uint8_t); +DECLARE_SOA_COLUMN(PoolEpoch, poolEpoch, uint64_t); +DECLARE_SOA_COLUMN(Run, run, int32_t); +DECLARE_SOA_COLUMN(PreviousRun, previousRun, int32_t); +DECLARE_SOA_COLUMN(EventId, eventId, int64_t); +DECLARE_SOA_COLUMN(PartnerId, partnerId, int64_t); +DECLARE_SOA_COLUMN(InputCollisionId, inputCollisionId, int64_t); +DECLARE_SOA_COLUMN(GlobalBc, globalBc, uint64_t); +DECLARE_SOA_COLUMN(Timestamp, timestamp, uint64_t); +DECLARE_SOA_COLUMN(ZVertex, zVertex, float); +DECLARE_SOA_COLUMN(Centrality, centrality, float); +DECLARE_SOA_COLUMN(PvMultiplicity, pvMultiplicity, float); +DECLARE_SOA_COLUMN(NumContrib, numContrib, uint32_t); +DECLARE_SOA_COLUMN(NNuclei, nNuclei, uint32_t); +DECLARE_SOA_COLUMN(NHadrons, nHadrons, uint32_t); +DECLARE_SOA_COLUMN(PoolBin, poolBin, int32_t); +DECLARE_SOA_COLUMN(PoolBefore, poolBefore, uint32_t); +DECLARE_SOA_COLUMN(PoolAfter, poolAfter, uint32_t); +DECLARE_SOA_COLUMN(BufferedEvents, bufferedEvents, uint64_t); +DECLARE_SOA_COLUMN(OccupiedBins, occupiedBins, uint32_t); +DECLARE_SOA_COLUMN(ExpectedPairs, expectedPairs, uint64_t); +DECLARE_SOA_COLUMN(AcceptedPairs, acceptedPairs, uint64_t); +DECLARE_SOA_COLUMN(RejectedPairs, rejectedPairs, uint64_t); +DECLARE_SOA_COLUMN(Combinations, combinations, uint64_t); +DECLARE_SOA_COLUMN(InputCollisions, inputCollisions, uint64_t); +DECLARE_SOA_COLUMN(PassedEventSelection, passedEventSelection, uint64_t); +DECLARE_SOA_COLUMN(PassedZorro, passedZorro, uint64_t); +} // namespace hnmediag +DECLARE_SOA_TABLE(HadNucleiMEDiag, "AOD", "HNMEDIAG", + hnmediag::InstanceHi, hnmediag::InstanceLo, hnmediag::CallId, + hnmediag::Timeslice, hnmediag::TfCounter, hnmediag::TfRun, + hnmediag::Kind, hnmediag::Status, hnmediag::PoolEpoch, + hnmediag::Run, hnmediag::PreviousRun, hnmediag::EventId, + hnmediag::InputCollisionId, hnmediag::GlobalBc, hnmediag::Timestamp, + hnmediag::ZVertex, hnmediag::Centrality, hnmediag::PvMultiplicity, + hnmediag::NumContrib, hnmediag::NNuclei, hnmediag::NHadrons, + hnmediag::PoolBin, hnmediag::PoolBefore, hnmediag::PoolAfter, + hnmediag::BufferedEvents, hnmediag::OccupiedBins, + hnmediag::ExpectedPairs, hnmediag::AcceptedPairs, hnmediag::RejectedPairs, + hnmediag::Combinations, hnmediag::InputCollisions, + hnmediag::PassedEventSelection, hnmediag::PassedZorro); +DECLARE_SOA_TABLE(HadNucleiMEDiagLinks, "AOD", "HNMEDIAGLINK", + hnmediag::InstanceHi, hnmediag::InstanceLo, hnmediag::CallId, + hnmediag::PoolEpoch, hnmediag::Run, hnmediag::EventId, + hnmediag::PartnerId, hnmediag::PoolBin, + hnmediag::ExpectedPairs, hnmediag::AcceptedPairs); + namespace hadron_nuclei_tables { @@ -125,6 +183,7 @@ DECLARE_SOA_COLUMN(ZVertex, zVertex, float); DECLARE_SOA_COLUMN(Multiplicity, multiplicity, uint16_t); DECLARE_SOA_COLUMN(CentFT0C, centFT0C, float); DECLARE_SOA_COLUMN(MultiplicityFT0C, multiplicityFT0C, float); +DECLARE_SOA_COLUMN(MultNTracksPV, multNTracksPV, int32_t); } // namespace hadron_nuclei_tables @@ -210,7 +269,8 @@ DECLARE_SOA_TABLE(HadronNucleiMult, "AOD", "HADNUCLEIMULT", hadron_nuclei_tables::ZVertex, hadron_nuclei_tables::Multiplicity, hadron_nuclei_tables::CentFT0C, - hadron_nuclei_tables::MultiplicityFT0C) + hadron_nuclei_tables::MultiplicityFT0C, + hadron_nuclei_tables::MultNTracksPV) // Reduced hadron-hypertriton pair output. Reconstructable quantities such as // k*, invariant mass and pointing variables are intentionally derived offline. diff --git a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx index fc838b5f3b3..c375e7ed193 100644 --- a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx +++ b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx @@ -43,11 +43,16 @@ #include #include #include +#include #include +#include +#include #include #include #include #include +#include +#include #include #include #include @@ -62,6 +67,7 @@ #include #include #include +#include #include #include @@ -83,21 +89,20 @@ using std::array; using CollBracket = o2::math_utils::Bracket; using HyperCandidates = aod::DataHypCandsWColl; using HyperCandidatesMC = aod::MCHypCandsWColl; -using CollisionsFull = soa::Join; -// The standard Femto producer stores multNTracksPV as its collision -// multiplicity. Subscribe to PVMults only in the mixed-event process so the -// same vertex--multiplicity pool definition can optionally be reproduced. -using CollisionsFullWithPVMult = soa::Join; -using CollisionsFullMC = soa::Join; +// Save the PV-track multiplicity alongside numContrib in the event-info output +// for both same-event and mixed-event pairs. +using CollisionsFull = soa::Join; +using CollisionsFullWithPVMult = CollisionsFull; +using CollisionsFullMC = soa::Join; using HadHyperCollisionsFull = soa::Join; using HadHyperCollisionsFullMC = soa::Join; -using TrackCandidates = soa::Join; -using TrackCandidatesMC = soa::Join; +using TrackCandidates = soa::Join; +using TrackCandidatesMC = soa::Join; // The DCA-template process explicitly subscribes to the propagated covariance // table. This makes the propagation service enter the branch where TrackTuner // corrections can be applied when enabled, without adding that dependency to // unrelated MC process switches. -using TrackCandidatesMCDca = soa::Join; +using TrackCandidatesMCDca = soa::Join; namespace { @@ -311,6 +316,7 @@ struct BufferedCollision { uint16_t numContrib{0u}; float centFT0C{0.f}; float multFT0C{0.f}; + int32_t multNTracksPV{0}; std::vector nuclei; std::vector hadrons; }; @@ -322,6 +328,17 @@ struct HadNucleiFemto { Produces mOutputHadHyperDataTable; Produces mOutputHadHyperMCTable; Produces mOutputMultiplicityTable; + Produces mOutputMEDiagnostic; + Produces mOutputMEDiagnosticLinks; + + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"meDiagnostic"}; + Configurable saveRejectedEvents{"saveRejectedEvents", false, "Save rejected collision rows too (potentially very large); rejection counters are always recorded"}; + Configurable savePartnerLinks{"savePartnerLinks", true, "Save one diagnostic row per undirected mixed-event combination"}; + Configurable productionTag{"productionTag", "unspecified", "Production label recorded with diagnostic instance UUID"}; + Configurable codeTag{"codeTag", "unspecified", "Exact source/build revision label for the diagnostic run"}; + } meDiagnostic; struct : o2::framework::ConfigurableGroup { // cppcheck-suppress unusedStructMember @@ -386,6 +403,12 @@ struct HadNucleiFemto { // cppcheck-suppress unusedStructMember std::string prefix{"deuteronPid"}; // Deuteron purity and PID cuts + Configurable settingTPCNClsFoundMinDe{"settingTPCNClsFoundMinDe", 110, "Minimum found TPC clusters for deuterons"}; + Configurable settingTPCCrossedRowsMinDe{"settingTPCCrossedRowsMinDe", 100, "Minimum crossed TPC rows for deuterons"}; + Configurable settingDeDCAxyOffset{"settingDeDCAxyOffset", 0.004f, "Deuteron DCAxy cut offset in cm"}; + Configurable settingDeDCAxyPtCoeff{"settingDeDCAxyPtCoeff", 0.013f, "Deuteron DCAxy inverse-pT cut coefficient"}; + Configurable settingDeDCAzOffset{"settingDeDCAzOffset", 0.004f, "Deuteron DCAz cut offset in cm"}; + Configurable settingDeDCAzPtCoeff{"settingDeDCAzPtCoeff", 0.013f, "Deuteron DCAz inverse-pT cut coefficient"}; Configurable settingCutPinMinDe{"settingCutPinMinDe", 0.0f, "Minimum Pin for De"}; Configurable settingCutDeptMin{"settingCutDeptMin", 0.6f, "Minimum PT cut on De"}; Configurable settingCutDeptMax{"settingCutDeptMax", 1.6f, "Maximum PT cut on De"}; @@ -411,6 +434,9 @@ struct HadNucleiFemto { // cppcheck-suppress unusedStructMember std::string prefix{"tritonPid"}; // Triton track-quality, purity and PID cuts + Configurable settingITSNClsMinTr{"settingITSNClsMinTr", 5, "Minimum number of ITS clusters for triton candidates"}; + Configurable settingTPCChi2NClMaxTr{"settingTPCChi2NClMaxTr", 4.f, "Maximum TPC chi2 per cluster for triton candidates"}; + Configurable settingITSChi2NClMaxTr{"settingITSChi2NClMaxTr", 36.f, "Maximum ITS chi2 per cluster for triton candidates"}; Configurable settingPIDMomentumSplitTr{"settingPIDMomentumSplitTr", 2.f, "Momentum separating low- and high-momentum triton TPC PID"}; Configurable settingCutTPCNsigmaLowPTr{"settingCutTPCNsigmaLowPTr", 3.f, "Maximum absolute TPC n-sigma for tritons below the momentum split"}; Configurable settingCutITSNsigmaLowPTr{"settingCutITSNsigmaLowPTr", 3.f, "Maximum absolute ITS n-sigma for tritons below the momentum split"}; @@ -418,6 +444,7 @@ struct HadNucleiFemto { Configurable settingCutTPCNsigmaHighPMaxTr{"settingCutTPCNsigmaHighPMaxTr", 3.f, "Maximum TPC n-sigma for tritons above the momentum split"}; Configurable settingTOFMassMomentumMinTr{"settingTOFMassMomentumMinTr", 1.2f, "Minimum momentum to apply the triton TOF mass cut"}; Configurable settingTOFMassMinTr{"settingTOFMassMinTr", 2.5f, "Minimum triton TOF mass"}; + Configurable settingUseTOFMassTableTr{"settingUseTOFMassTableTr", false, "Use pidTOFmass mass for triton PID and purity instead of computeTOFMass"}; Configurable settingTOFMassMaxTr{"settingTOFMassMaxTr", 3.4f, "Maximum triton TOF mass"}; Configurable settingTPCRejectNsig{"settingTPCRejectNsig", 3.f, "Minimum absolute TPC n-sigma from deuteron, proton and pion hypotheses"}; } tritonPid; @@ -599,7 +626,9 @@ struct HadNucleiFemto { // cppcheck-suppress unusedStructMember std::string prefix{"zorro"}; // Zorro - Configurable settingSkimmedProcessing{"settingSkimmedProcessing", false, "Skimmed dataset processing"}; + Configurable settingEnableTriggerSelection{"settingEnableTriggerSelection", false, "Enable Zorro event selection for the configured software triggers"}; + Configurable settingSoftwareTriggerNames{"settingSoftwareTriggerNames", "fHighFt0Mult", "Software trigger names, separated by commas (OR selection)"}; + Configurable settingBaseCCDBPathForTriggers{"settingBaseCCDBPathForTriggers", "EventFiltering/Zorro/", "CCDB path for Zorro trigger information"}; } zorro; struct : o2::framework::ConfigurableGroup { @@ -647,6 +676,29 @@ struct HadNucleiFemto { int mMixingRunNumber{-1}; int mHyperMixingRunNumber{-1}; int mHyperMCMixingRunNumber{-1}; + // A separate state machine: the ordinary ME pools and counters are untouched. + std::unordered_map> mDiagnosticPools; + TimingInfo mDiagnosticTiming; + std::string mDiagnosticUUID; + uint64_t mDiagnosticInstanceHi{0}, mDiagnosticInstanceLo{0}; + uint64_t mDiagnosticCall{0}, mDiagnosticEpoch{0}, mDiagnosticBuffered{0}; + uint64_t mDiagnosticExpected{0}, mDiagnosticAccepted{0}, mDiagnosticCombinations{0}; + int64_t mNextDiagnosticEventId{0}; + int mDiagnosticRun{-1}; + static constexpr uint8_t DiagnosticEventKind = 3; + struct MEDiagnosticRow { + uint8_t kind{DiagnosticEventKind}, status{0}; + int run{-1}, previousRun{-1}; + int64_t eventId{-1}, inputCollisionId{-1}; + uint64_t globalBC{0}, timestamp{0}; + float z{0.f}, centrality{0.f}, multiplicity{0.f}; + uint32_t numContrib{0}, nNuclei{0}, nHadrons{0}; + int poolBin{-1}; + uint32_t before{0}, after{0}; + uint64_t expected{0}, accepted{0}, combinations{0}; + uint64_t inputCollisions{0}, passedEventSelection{0}, passedZorro{0}; + }; + HistogramRegistry mDiagnosticRegistry{"MEDiagnostic", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; o2::vertexing::DCAFitterN<2> mFitter; int mRunNumber{0}; @@ -728,6 +780,28 @@ struct HadNucleiFemto { {"fraction/hDcaMotherPdgHad", "Hadron DCA by exact direct-mother PDG; signed reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality;origin;collision association;mother PDG sign;mother PDG high;mother PDG low", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {40, 0.f, 100.f}, {NDcaOrigins, -0.5f, static_cast(NDcaOrigins) - 0.5f}, {NCollisionAssociations, -0.5f, static_cast(NCollisionAssociations) - 0.5f}, {3, -1.5f, 1.5f}, {MotherPdgHighMax + 1, -0.5f, static_cast(MotherPdgHighMax) + 0.5f}, {MotherPdgChunkBase, -0.5f, static_cast(MotherPdgChunkBase) - 0.5f}}}}, {"fraction/hDcaMotherPdgNu", "Nucleus DCA by exact direct-mother PDG; signed physical reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality;origin;collision association;mother PDG sign;mother PDG high;mother PDG low", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {NucleusDcaFitBins, -NucleusDcaFitAxisMax, NucleusDcaFitAxisMax}, {NucleusDcaFitBins, -NucleusDcaFitAxisMax, NucleusDcaFitAxisMax}, {40, 0.f, 100.f}, {NDcaOrigins, -0.5f, static_cast(NDcaOrigins) - 0.5f}, {NCollisionAssociations, -0.5f, static_cast(NCollisionAssociations) - 0.5f}, {3, -1.5f, 1.5f}, {MotherPdgHighMax + 1, -0.5f, static_cast(MotherPdgHighMax) + 0.5f}, {MotherPdgChunkBase, -0.5f, static_cast(MotherPdgChunkBase) - 0.5f}}}}, + // Generator denominators and source-resolved response matrices for the + // hypertriton-to-primary He3 and triton constraints. Generated momenta + // are physical particle momenta and must never receive the reconstructed + // He3-track x2 rigidity conversion. The two-body histograms are + // conditional on their generated decay channel, so the physical + // branching ratios remain independent offline inputs. + {"fractionMCGen/hPrimaryHe3", "Physical-primary generated He3 in selected MC collisions;signed generated p_{T}^{He3} (GeV/c);generated y^{He3};centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCGen/hHypertriton", "Physical-primary generated hypertritons in selected MC collisions;signed generated p_{T}^{hypertriton} (GeV/c);generated y^{hypertriton};centrality", {HistType::kTHnSparseF, {{400, -10.f, 10.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCGen/hHypertritonToHe3Pi", "Physical-primary generated hypertritons in the He3-pion channel;signed generated p_{T}^{hypertriton} (GeV/c);generated y^{hypertriton};centrality", {HistType::kTHnSparseF, {{400, -10.f, 10.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCGen/hHypertritonDaughterHe3", "Generated direct He3 daughters in the hypertriton-He3-pion channel;signed generated p_{T}^{He3} (GeV/c);generated y^{He3};centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCGen/hHypertritonPtVsDaughterHe3Pt", "Generated hypertriton-to-He3 decay mapping;signed generated p_{T}^{hypertriton} (GeV/c);signed generated p_{T}^{He3} (GeV/c);generated y^{hypertriton};generated y^{He3};centrality", {HistType::kTHnSparseF, {{400, -10.f, 10.f}, {280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCGen/hPrimaryTriton", "Physical-primary generated tritons in selected MC collisions;signed generated p_{T}^{triton} (GeV/c);generated y^{triton};centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCGen/hHypertritonToTritonPi0", "Physical-primary generated hypertritons in the triton-pi0 channel;signed generated p_{T}^{hypertriton} (GeV/c);generated y^{hypertriton};centrality", {HistType::kTHnSparseF, {{400, -10.f, 10.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCGen/hHypertritonDaughterTriton", "Generated direct triton daughters in the hypertriton-triton-pi0 channel;signed generated p_{T}^{triton} (GeV/c);generated y^{triton};centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCGen/hHypertritonPtVsDaughterTritonPt", "Generated hypertriton-to-triton decay mapping;signed generated p_{T}^{hypertriton} (GeV/c);signed generated p_{T}^{triton} (GeV/c);generated y^{hypertriton};generated y^{triton};centrality", {HistType::kTHnSparseF, {{400, -10.f, 10.f}, {280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCReco/hPrimaryHe3GenPtVsRecoPt", "DCA-selected physical-primary He3 response;signed generated p_{T}^{He3} (GeV/c);signed physical reconstructed p_{T}^{He3} (GeV/c);generated y^{He3};centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCReco/hHypertritonDaughterHe3GenPtVsRecoPt", "DCA-selected hypertriton-daughter He3 response;signed generated p_{T}^{He3} (GeV/c);signed physical reconstructed p_{T}^{He3} (GeV/c);generated y^{He3};centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCReco/hHypertritonParentPtVsDaughterRecoPt", "DCA-selected hypertriton parent-to-He3 response;signed generated p_{T}^{hypertriton} (GeV/c);signed physical reconstructed p_{T}^{He3} (GeV/c);generated y^{hypertriton};generated y^{He3};centrality", {HistType::kTHnSparseF, {{400, -10.f, 10.f}, {280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCReco/hPrimaryTritonGenPtVsRecoPt", "DCA-selected physical-primary triton response;signed generated p_{T}^{triton} (GeV/c);signed physical reconstructed p_{T}^{triton} (GeV/c);generated y^{triton};centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCReco/hHypertritonDaughterTritonGenPtVsRecoPt", "DCA-selected hypertriton-daughter triton response;signed generated p_{T}^{triton} (GeV/c);signed physical reconstructed p_{T}^{triton} (GeV/c);generated y^{triton};centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCReco/hHypertritonParentPtVsDaughterTritonRecoPt", "DCA-selected hypertriton parent-to-triton response;signed generated p_{T}^{hypertriton} (GeV/c);signed physical reconstructed p_{T}^{triton} (GeV/c);generated y^{hypertriton};generated y^{triton};centrality", {HistType::kTHnSparseF, {{400, -10.f, 10.f}, {280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + // Hierarchical MC truth purity counters: all = correct species + mis-ID // + no label; correct species = correct collision + wrong collision; // correct collision = primary + weak + material. @@ -765,14 +839,17 @@ struct HadNucleiFemto { {"h2NsigmaHadKaTOF", "NsigmaHad TOF distribution; #it{p}_{T}(GeV/#it{c}); n#sigma_{TPC}(K)", {HistType::kTH1F, {{200, -5.0f, 5.0f}}}}, // Purity + {"h2MassTOFTrComputedVsTable", "Triton TOF mass comparison before mass cut;computed mass (GeV/#it{c}^{2});table mass (GeV/#it{c}^{2})", {HistType::kTH2F, {{280, 0.f, 7.f}, {280, 0.f, 7.f}}}}, + {"purity/h2MassTOFTrComputedVsTable", "Triton TOF mass comparison before mass cut;computed mass (GeV/#it{c}^{2});table mass (GeV/#it{c}^{2})", {HistType::kTH2F, {{280, 0.f, 7.f}, {280, 0.f, 7.f}}}}, + {"purity/h2MassTOFTr_preselection", "Triton TOF mass before mass cut; signed #it{p}_{T} (GeV/#it{c}); m_{TOF} (GeV/#it{c}^{2})", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {280, 0.0f, 7.0f}}}}, {"purity/h2NsigmaNuTPC_preselection", "NsigmaNu TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {400, -10.0f, 10.0f}}}}, {"purity/h2NsigmaNuTPC_preselecComp", "NsigmaNu TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {400, -10.0f, 10.0f}}}}, {"purity/h2NSigmaNuITS_preselection", "NsigmaNu ITS distribution; signed #it{p}_{T} (GeV/#it{c}); n#sigma_{ITS} Nu", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {120, -5.0f, 5.0f}}}}, {"purity/h2NsigmaNuTOF_preselection", "NsigmaNu TOF distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TOF}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {400, -10.0f, 10.0f}}}}, - {"purity/h2NsigmaNuComb_preselection", "NsigmaNu TPCTOF comb distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{comb}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {100, 0.0f, 7.0f}}}}, + {"purity/h2NsigmaNuComb_preselection", "NsigmaNu TPCTOF comb distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{comb}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {100, 0.0f, 10.0f}}}}, {"purity/h2NsigmaHadTPC_preselection", "NsigmaNu TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {400, -10.0f, 10.0f}}}}, {"purity/h2NsigmaHadTOF_preselection", "NsigmaHad TOF distribution; #iit{p}_{T} (GeV/#it{c}); n#sigma_{TOF}(p)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {400, -10.0f, 10.0f}}}}, - {"purity/h2NsigmaHadComb_preselection", "NsigmaHad TPCTOF comb distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{comb}(had)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {100, 0.0f, 7.0f}}}}, + {"purity/h2NsigmaHadComb_preselection", "NsigmaHad TPCTOF comb distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{comb}(had)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {100, 0.0f, 10.0f}}}}, // Hypertriton {"hHe3TPCnsigma", "NsigmaHe3 TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(He3)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {200, -5.0f, 5.0f}}}}, @@ -800,8 +877,44 @@ struct HadNucleiFemto { false, true}; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& initContext) { + if (doprocessMixedEventDiagnostic) { + // AnalysisTask does not automatically invoke a task endOfStream method. + // CallbackService appends this callback without replacing output flushing. + initContext.services().get().set( + [this](EndOfStreamContext& context) { endOfStream(context); }); + if (doprocessMixedEvent || doprocessSameEvent || doprocessMC || doprocessHyper || doprocessMCHyper || doprocessPurity || doprocessDcaFractionData || doprocessDcaFractionPurityMC) { + LOG(fatal) << "processMixedEventDiagnostic must run alone; disable the other HadNucleiFemto processes to avoid shared selection-QA contamination"; + } + if (eventMixing.settingNoMixedEvents.value <= 0) { + LOG(fatal) << "ME diagnostic requires a positive mixing depth"; + } + mDiagnosticUUID = TUUID().AsString(); + std::string uuidWords = mDiagnosticUUID; + uuidWords.erase(std::remove(uuidWords.begin(), uuidWords.end(), '-'), uuidWords.end()); + mDiagnosticInstanceHi = std::stoull(uuidWords.substr(0, 16), nullptr, 16); + mDiagnosticInstanceLo = std::stoull(uuidWords.substr(16, 16), nullptr, 16); + const auto& device = initContext.services().get(); + const char* alienJob = std::getenv("ALIEN_PROC_ID"); + LOG(info) << "ME_DIAG INIT instance=" << mDiagnosticUUID << " device=" << device.id + << " rank=" << device.rank << " inputTimesliceId=" << device.inputTimesliceId + << " job=" << (alienJob ? alienJob : "unknown") + << " production=" << meDiagnostic.productionTag.value << " code=" << meDiagnostic.codeTag.value + << " depth=" << eventMixing.settingNoMixedEvents.value + << " requireBoth=" << eventMixing.settingRequireBothSpeciesForMixing.value + << " multiplicityBinning=" << eventMixing.settingUseMultiplicityBinning.value + << " applyCPR=" << CPR.settingApplyClosePairRejection.value; + for (const auto& name : {"hLifecycle", "hSelection"}) { + mDiagnosticRegistry.add(name, name, HistType::kTH1D, {{6, -0.5, 5.5}}); + } + mDiagnosticRegistry.add("hPoolOccupancy", "Prior partners;partners;events", HistType::kTH1D, {{101, -0.5, 100.5}}); + mDiagnosticRegistry.add("hPairs", "0=expected,1=accepted,2=CPR rejected;counter;pairs", HistType::kTH1D, {{3, -0.5, 2.5}}); + // The global sink keys by task + histogram name, not registry directory. + for (const auto& name : {"hMEDiagKStar_LS_M", "hMEDiagKStar_LS_A", "hMEDiagKStar_US_M", "hMEDiagKStar_US_A"}) { + mDiagnosticRegistry.add(name, name, HistType::kTH1D, {{300, 0., 3.}}); + } + } const bool processHyperPairs = doprocessHyper || doprocessMCHyper; if (processHyperPairs && hadHyper.maxOutputKstar.value == 0.f) { LOG(fatal) << "Hadron-hypertriton mode requires a nonzero output k* range"; @@ -935,8 +1048,9 @@ struct HadNucleiFemto { return; } if constexpr (!isMC) { - if (zorro.settingSkimmedProcessing) { - mZorro.initCCDB(mCcdb.service, bc.runNumber(), bc.timestamp(), zorroTriggerMask()); + if (useZorroSelection()) { + mZorro.setBaseCCDBPath(zorro.settingBaseCCDBPathForTriggers.value); + mZorro.initCCDB(mCcdb.service, bc.runNumber(), bc.timestamp(), zorro.settingSoftwareTriggerNames.value); mZorro.populateHistRegistry(mQaRegistry, bc.runNumber()); } } @@ -1007,7 +1121,7 @@ struct HadNucleiFemto { template bool passesZorroSelection(const Tcollision& collision) { - if (!zorro.settingSkimmedProcessing) { + if (!useZorroSelection()) { return true; } auto bc = collision.template bc_as(); @@ -1026,7 +1140,7 @@ struct HadNucleiFemto { mQaRegistry.fill(HIST("hEvents"), 1); if constexpr (!isMC) { - if (zorro.settingSkimmedProcessing) { + if (useZorroSelection()) { if (!passesZorroSelection(collision)) { return false; } @@ -1130,16 +1244,14 @@ struct HadNucleiFemto { return false; } - constexpr int minTPCNClsFound = 110; - constexpr int minTPCNClsCrossedRows = 100; constexpr float minTPCCrossedRowsOverFound = 0.f; constexpr int maxTPCNClsShared = 160; constexpr float maxSharedTPCFraction = 1.f; constexpr int minITSNClsInnerBarrel = 1; const float tpcCrossedRowsOverFound = candidate.tpcNClsFound() > 0 ? static_cast(candidate.tpcNClsCrossedRows()) / candidate.tpcNClsFound() : 0.f; - return !(candidate.tpcNClsFound() < minTPCNClsFound || - candidate.tpcNClsCrossedRows() < minTPCNClsCrossedRows || + return !(candidate.tpcNClsFound() < deuteronPid.settingTPCNClsFoundMinDe || + candidate.tpcNClsCrossedRows() < deuteronPid.settingTPCCrossedRowsMinDe || tpcCrossedRowsOverFound < minTPCCrossedRowsOverFound || candidate.tpcNClsShared() > maxTPCNClsShared || candidate.tpcFractionSharedCls() > maxSharedTPCFraction || @@ -1154,19 +1266,16 @@ struct HadNucleiFemto { constexpr float maxAbsDcaXY = 0.2f; constexpr float maxAbsDcaZ = 0.2f; constexpr int minTPCCrossedRows = 70; - constexpr float maxTPCChi2NCl = 5.f; constexpr float maxTPCFractionSharedCls = 0.3f; - constexpr int minITSNCls = 5; - constexpr float maxITSChi2NCl = 10.f; return !(std::abs(candidate.eta()) >= maxAbsEta || std::abs(candidate.dcaXY()) >= maxAbsDcaXY || std::abs(candidate.dcaZ()) >= maxAbsDcaZ || candidate.tpcNClsCrossedRows() < minTPCCrossedRows || - candidate.tpcChi2NCl() >= maxTPCChi2NCl || + candidate.tpcChi2NCl() >= tritonPid.settingTPCChi2NClMaxTr || candidate.tpcFractionSharedCls() >= maxTPCFractionSharedCls || - candidate.itsNCls() < minITSNCls || - candidate.itsChi2NCl() >= maxITSChi2NCl); + candidate.itsNCls() < tritonPid.settingITSNClsMinTr || + candidate.itsChi2NCl() >= tritonPid.settingITSChi2NClMaxTr); } bool useDeuteronNucleus() const @@ -1184,12 +1293,9 @@ struct HadNucleiFemto { return species.settingNuPDGCode.value == He3PDG; } - const char* zorroTriggerMask() const + bool useZorroSelection() const { - if (useTritonNucleus()) { - return "fTritonFemto"; - } - return useHelium3Nucleus() ? "fHe" : "fDe"; + return zorro.settingEnableTriggerSelection.value; } const char* nucleusBetheBlochLabel() const @@ -1741,8 +1847,8 @@ struct HadNucleiFemto { if (absPt <= 0.f) { return false; } - const float deDCAxyMax = 0.004f + 0.013f / absPt; - const float deDCAzMax = 0.004f + 0.013f / absPt; + const float deDCAxyMax = deuteronPid.settingDeDCAxyOffset + deuteronPid.settingDeDCAxyPtCoeff / absPt; + const float deDCAzMax = deuteronPid.settingDeDCAzOffset + deuteronPid.settingDeDCAzPtCoeff / absPt; if (std::abs(candidate.dcaXY()) > deDCAxyMax || std::abs(candidate.dcaZ()) > deDCAzMax) { return false; } @@ -1822,6 +1928,26 @@ struct HadNucleiFemto { return std::abs(candidate.tpcInnerParam()) * std::sqrt(1.f / (beta * beta) - 1.f); } + template + float tritonTOFMass(const Ttrack& candidate) const + { + return tritonPid.settingUseTOFMassTableTr.value ? candidate.mass() : computeTOFMass(candidate); + } + + template + void fillTritonTOFMassComparison(const Ttrack& candidate, bool isPurity) + { + const float computedMass = computeTOFMass(candidate); + const float tableMass = candidate.mass(); + if (std::isfinite(computedMass) && std::isfinite(tableMass)) { + if (isPurity) { + mQaRegistry.fill(HIST("purity/h2MassTOFTrComputedVsTable"), computedMass, tableMass); + } else { + mQaRegistry.fill(HIST("h2MassTOFTrComputedVsTable"), computedMass, tableMass); + } + } + } + template bool selectionPIDTr(const Ttrack& candidate) { @@ -1854,9 +1980,10 @@ struct HadNucleiFemto { if (!candidate.hasTOF()) { return false; } - const float massTOFTr = computeTOFMass(candidate); + fillTritonTOFMassComparison(candidate, false); + const float massTOFTr = tritonTOFMass(candidate); mQaRegistry.fill(HIST("h2MassTOFTr"), candidate.sign() * absP, massTOFTr); - if (massTOFTr <= tritonPid.settingTOFMassMinTr || massTOFTr >= tritonPid.settingTOFMassMaxTr) { + if (!std::isfinite(massTOFTr) || massTOFTr <= tritonPid.settingTOFMassMinTr || massTOFTr >= tritonPid.settingTOFMassMaxTr) { return false; } } @@ -2546,7 +2673,8 @@ struct HadNucleiFemto { collision.posZ(), collision.numContrib(), collision.centFT0C(), - collision.multFT0C()); + collision.multFT0C(), + collision.multNTracksPV()); } } @@ -2559,7 +2687,8 @@ struct HadNucleiFemto { collision.posZ, collision.numContrib, collision.centFT0C, - collision.multFT0C); + collision.multFT0C, + collision.multNTracksPV); } } @@ -3272,6 +3401,7 @@ struct HadNucleiFemto { currentCollision.numContrib = collision.numContrib(); currentCollision.centFT0C = collision.centFT0C(); currentCollision.multFT0C = collision.multFT0C(); + currentCollision.multNTracksPV = collision.multNTracksPV(); const uint64_t collIdx = collision.globalIndex(); auto tracksThisCollision = tracks.sliceBy(mPerCol, collIdx); @@ -3332,6 +3462,219 @@ struct HadNucleiFemto { } PROCESS_SWITCH(HadNucleiFemto, processMixedEvent, "Process Mixed event", false); + void run(ProcessingContext& context) + { + if (doprocessMixedEventDiagnostic) { + // TimingInfo is stream-scoped; access it through the active context. + mDiagnosticTiming = context.services().get(); + } + } + + void writeMEDiagnostic(const MEDiagnosticRow& row) + { + mOutputMEDiagnostic(mDiagnosticInstanceHi, mDiagnosticInstanceLo, mDiagnosticCall, + static_cast(mDiagnosticTiming.timeslice), mDiagnosticTiming.tfCounter, mDiagnosticTiming.runNumber, + row.kind, row.status, mDiagnosticEpoch, row.run, row.previousRun, row.eventId, + row.inputCollisionId, row.globalBC, row.timestamp, row.z, row.centrality, row.multiplicity, + row.numContrib, row.nNuclei, row.nHadrons, row.poolBin, row.before, row.after, + mDiagnosticBuffered, static_cast(mDiagnosticPools.size()), + row.expected, row.accepted, row.expected - row.accepted, row.combinations, + row.inputCollisions, row.passedEventSelection, row.passedZorro); + if (row.kind != DiagnosticEventKind) { + mDiagnosticRegistry.fill(HIST("hLifecycle"), row.kind); + } + } + + bool fillDiagnosticMixedPair(const BufferedTrack& nucleus, const BufferedTrack& hadron) + { + // Same CPR predicate and kinematics as fillMixedPair; separate final QA, + // and never write the ordinary pair/multiplicity tables from this process. + if (isClosePair(nucleus, hadron, /*fillQA*/ true)) { + return false; + } + HadNucandidate candidate; + fillBufferedCandidateInfo(nucleus, hadron, candidate); + if (candidate.recoPtNu() > 0) { + if (candidate.isBkgUS) { + mDiagnosticRegistry.fill(HIST("hMEDiagKStar_US_M"), candidate.kstar); + } else { + mDiagnosticRegistry.fill(HIST("hMEDiagKStar_LS_M"), candidate.kstar); + } + } else if (candidate.isBkgUS) { + mDiagnosticRegistry.fill(HIST("hMEDiagKStar_US_A"), candidate.kstar); + } else { + mDiagnosticRegistry.fill(HIST("hMEDiagKStar_LS_A"), candidate.kstar); + } + return true; + } + + void processMixedEventDiagnostic(const CollisionsFullWithPVMult& collisions, const TrackCandidates& tracks, const aod::BCsWithTimestamps&) + { + const CentralityBinningType centralityBinningPolicy{{axisVertex, axisCentrality}, true}; + const MultiplicityBinningType multiplicityBinningPolicy{{axisVertex, axisMultiplicity}, true}; + ++mDiagnosticCall; + MEDiagnosticRow call; + call.run = mDiagnosticRun; + call.inputCollisions = collisions.size(); + if (mDiagnosticCall == 1) { + call.kind = 0; // Instance initialization, emitted in a valid table context. + writeMEDiagnostic(call); + } + call.kind = 1; + writeMEDiagnostic(call); + LOG(info) << "ME_DIAG BEGIN instance=" << mDiagnosticUUID << " call=" << mDiagnosticCall + << " timeslice=" << mDiagnosticTiming.timeslice << " tfCounter=" << mDiagnosticTiming.tfCounter + << " run=" << mDiagnosticRun << " buffered=" << mDiagnosticBuffered << " bins=" << mDiagnosticPools.size(); + + for (const auto& collision : collisions) { + const auto bc = collision.template bc_as(); + MEDiagnosticRow row; + row.run = bc.runNumber(); + row.inputCollisionId = collision.globalIndex(); + row.globalBC = bc.globalBC(); + row.timestamp = bc.timestamp(); + row.z = collision.posZ(); + row.centrality = collision.centFT0C(); + row.multiplicity = collision.multNTracksPV(); + row.numContrib = collision.numContrib(); + mDiagnosticRegistry.fill(HIST("hSelection"), 0); + if (!passesEventSelection(collision)) { + row.status = 1; + if (meDiagnostic.saveRejectedEvents) { + writeMEDiagnostic(row); + } + continue; + } + ++call.passedEventSelection; + mDiagnosticRegistry.fill(HIST("hSelection"), 1); + if (!passesZorroSelection(collision)) { + row.status = 2; + if (meDiagnostic.saveRejectedEvents) { + writeMEDiagnostic(row); + } + continue; + } + ++call.passedZorro; + mDiagnosticRegistry.fill(HIST("hSelection"), 2); + // Match the ordinary ME ordering: observe the run before species/bin cuts. + if (mDiagnosticRun != bc.runNumber()) { + MEDiagnosticRow reset = row; + reset.kind = 2; + reset.status = mDiagnosticRun < 0 ? 0 : 1; + reset.previousRun = mDiagnosticRun; + ++mDiagnosticEpoch; + writeMEDiagnostic(reset); // Contains the pre-clear buffered totals. + LOG(info) << "ME_DIAG CLEAR instance=" << mDiagnosticUUID << " call=" << mDiagnosticCall + << " reason=" << (mDiagnosticRun < 0 ? "first-run" : "run-change") + << " oldRun=" << mDiagnosticRun << " newRun=" << bc.runNumber() + << " epoch=" << mDiagnosticEpoch << " buffered=" << mDiagnosticBuffered + << " bins=" << mDiagnosticPools.size(); + mDiagnosticPools.clear(); + mDiagnosticBuffered = 0; + mDiagnosticRun = bc.runNumber(); + } + BufferedCollision current; + current.eventId = mNextDiagnosticEventId++; + current.posZ = collision.posZ(); + current.numContrib = collision.numContrib(); + current.centFT0C = collision.centFT0C(); + current.multFT0C = collision.multFT0C(); + current.multNTracksPV = collision.multNTracksPV(); + row.eventId = current.eventId; + auto tracksThisCollision = tracks.sliceBy(mPerCol, collision.globalIndex()); + tracksThisCollision.bindExternalIndices(&tracks); + for (const auto& track : tracksThisCollision) { + if (selectTrackNu(track) && selectionPIDNu(track)) { + current.nuclei.push_back(makeBufferedTrack(track, true, current.eventId)); + } + if (selectTrackHadron(track) && selectionPIDHadron(track)) { + current.hadrons.push_back(makeBufferedTrack(track, false, current.eventId)); + } + } + row.nNuclei = current.nuclei.size(); + row.nHadrons = current.hadrons.size(); + if (eventMixing.settingRequireBothSpeciesForMixing.value && (current.nuclei.empty() || current.hadrons.empty())) { + row.status = 3; + if (meDiagnostic.saveRejectedEvents) { + writeMEDiagnostic(row); + } + continue; + } + mDiagnosticRegistry.fill(HIST("hSelection"), 3); + row.poolBin = eventMixing.settingUseMultiplicityBinning.value + ? multiplicityBinningPolicy.getBin(std::make_tuple(collision.posZ(), collision.multNTracksPV())) + : centralityBinningPolicy.getBin(std::make_tuple(collision.posZ(), collision.centFT0C())); + if (row.poolBin < 0) { + row.status = 4; + if (meDiagnostic.saveRejectedEvents) { + writeMEDiagnostic(row); + } + continue; + } + mDiagnosticRegistry.fill(HIST("hSelection"), 4); + auto& pool = mDiagnosticPools[row.poolBin]; + row.before = pool.size(); + row.combinations = pool.size(); + mDiagnosticRegistry.fill(HIST("hPoolOccupancy"), row.before); + for (const auto& partner : pool) { + const uint64_t expected = static_cast(current.nuclei.size()) * partner.hadrons.size() + + static_cast(partner.nuclei.size()) * current.hadrons.size(); + uint64_t accepted = 0; + for (const auto& nucleus : current.nuclei) { + for (const auto& hadron : partner.hadrons) { + accepted += fillDiagnosticMixedPair(nucleus, hadron); + } + } + for (const auto& nucleus : partner.nuclei) { + for (const auto& hadron : current.hadrons) { + accepted += fillDiagnosticMixedPair(nucleus, hadron); + } + } + row.expected += expected; + row.accepted += accepted; + if (meDiagnostic.savePartnerLinks) { + mOutputMEDiagnosticLinks(mDiagnosticInstanceHi, mDiagnosticInstanceLo, mDiagnosticCall, + mDiagnosticEpoch, mDiagnosticRun, current.eventId, partner.eventId, row.poolBin, expected, accepted); + } + } + if (pool.size() >= static_cast(eventMixing.settingNoMixedEvents.value)) { + pool.pop_front(); + } else { + ++mDiagnosticBuffered; + } + pool.push_back(std::move(current)); + row.after = pool.size(); + writeMEDiagnostic(row); + call.expected += row.expected; + call.accepted += row.accepted; + call.combinations += row.combinations; + mDiagnosticRegistry.fill(HIST("hPairs"), 0., static_cast(row.expected)); + mDiagnosticRegistry.fill(HIST("hPairs"), 1., static_cast(row.accepted)); + mDiagnosticRegistry.fill(HIST("hPairs"), 2., static_cast(row.expected - row.accepted)); + } + call.kind = 4; + call.run = mDiagnosticRun; + writeMEDiagnostic(call); + mDiagnosticExpected += call.expected; + mDiagnosticAccepted += call.accepted; + mDiagnosticCombinations += call.combinations; + LOG(info) << "ME_DIAG END instance=" << mDiagnosticUUID << " call=" << mDiagnosticCall + << " buffered=" << mDiagnosticBuffered << " bins=" << mDiagnosticPools.size() + << " combinations=" << call.combinations << " expected=" << call.expected << " accepted=" << call.accepted; + } + PROCESS_SWITCH(HadNucleiFemto, processMixedEventDiagnostic, "Independent ME pool lifecycle diagnostic (run alone)", false); + + void endOfStream(EndOfStreamContext&) + { + if (doprocessMixedEventDiagnostic) { + // EOS table writes are not guaranteed to be flushed; final snapshot in log. + LOG(info) << "ME_DIAG EOS instance=" << mDiagnosticUUID << " calls=" << mDiagnosticCall + << " epochs=" << mDiagnosticEpoch << " buffered=" << mDiagnosticBuffered + << " bins=" << mDiagnosticPools.size() << " combinations=" << mDiagnosticCombinations + << " expected=" << mDiagnosticExpected << " accepted=" << mDiagnosticAccepted; + } + } + // Produce the data distributions fitted offline with the MC templates. All // nominal quality and PID selections are applied; only DCA is relaxed. void processDcaFractionData(const CollisionsFull& collisions, const TrackCandidates& tracks, const aod::BCsWithTimestamps& bcs) @@ -3399,6 +3742,54 @@ struct HadNucleiFemto { const float productionRadius = std::hypot(particle.vx(), particle.vy()); const float signedPt = signedPhysicalPt(track, isNucleus); + // These response matrices use the relaxed DCA-fit selection under which + // this function is called. Keep only correct-collision He3 or triton so + // the reconstructed numerators match the selected-MC-collision generator + // denominators filled below. Hypertriton responses are conditional on the + // explicit He3-pion or triton-pi0 channel; physical branching ratios are + // deliberately not folded into these histograms. + const int absoluteParticlePdg = std::abs(particle.pdgCode()); + if (isNucleus && matchesRecoCollision && (absoluteParticlePdg == He3PDG || absoluteParticlePdg == TritonPDG)) { + const float signedGeneratedPt = particle.pdgCode() > 0 ? particle.pt() : -particle.pt(); + const float generatedRapidity = particle.y(); + if (origin == Primary) { + if (absoluteParticlePdg == He3PDG) { + mQaRegistry.fill(HIST("fractionMCReco/hPrimaryHe3GenPtVsRecoPt"), signedGeneratedPt, signedPt, generatedRapidity, centrality); + } else { + mQaRegistry.fill(HIST("fractionMCReco/hPrimaryTritonGenPtVsRecoPt"), signedGeneratedPt, signedPt, generatedRapidity, centrality); + } + } else if (origin == WeakDecay && particle.has_mothers()) { + for (const auto& mother : particle.template mothers_as()) { + if (std::abs(mother.pdgCode()) != HyperTritonPDG || !mother.isPhysicalPrimary() || + ((mother.pdgCode() > 0) != (particle.pdgCode() > 0))) { + continue; + } + const int expectedPionPdg = absoluteParticlePdg == He3PDG + ? (mother.pdgCode() > 0 ? -PDG_t::kPiPlus : PDG_t::kPiPlus) + : PDG_t::kPi0; + bool hasExpectedPion = false; + for (const auto& daughter : mother.template daughters_as()) { + if (daughter.pdgCode() == expectedPionPdg) { + hasExpectedPion = true; + break; + } + } + if (!hasExpectedPion) { + continue; + } + const float signedParentPt = mother.pdgCode() > 0 ? mother.pt() : -mother.pt(); + if (absoluteParticlePdg == He3PDG) { + mQaRegistry.fill(HIST("fractionMCReco/hHypertritonDaughterHe3GenPtVsRecoPt"), signedGeneratedPt, signedPt, generatedRapidity, centrality); + mQaRegistry.fill(HIST("fractionMCReco/hHypertritonParentPtVsDaughterRecoPt"), signedParentPt, signedPt, mother.y(), generatedRapidity, centrality); + } else { + mQaRegistry.fill(HIST("fractionMCReco/hHypertritonDaughterTritonGenPtVsRecoPt"), signedGeneratedPt, signedPt, generatedRapidity, centrality); + mQaRegistry.fill(HIST("fractionMCReco/hHypertritonParentPtVsDaughterTritonRecoPt"), signedParentPt, signedPt, mother.y(), generatedRapidity, centrality); + } + break; + } + } + } + // The detailed histogram is always filled for a truth-PDG-matched track, // including wrong-collision associations, so tighter choices can be made // offline without rerunning the table producer. @@ -3483,15 +3874,93 @@ struct HadNucleiFemto { } } + using SelectedMcCollisionInfo = std::unordered_map>; + + void fillMCGeneratorNucleiFeedDown(const aod::McParticles& mcParticles, const SelectedMcCollisionInfo& selectedMcCollisions) + { + for (const auto& particle : mcParticles) { + const auto collision = selectedMcCollisions.find(particle.mcCollisionId()); + if (collision == selectedMcCollisions.end()) { + continue; + } + const float centrality = collision->second.second; + const int particlePdg = particle.pdgCode(); + const int absolutePdg = std::abs(particlePdg); + const float signedPt = particlePdg > 0 ? particle.pt() : -particle.pt(); + + if (absolutePdg == He3PDG && particle.isPhysicalPrimary()) { + mQaRegistry.fill(HIST("fractionMCGen/hPrimaryHe3"), signedPt, particle.y(), centrality); + continue; + } + if (absolutePdg == TritonPDG && particle.isPhysicalPrimary()) { + mQaRegistry.fill(HIST("fractionMCGen/hPrimaryTriton"), signedPt, particle.y(), centrality); + continue; + } + if (absolutePdg != HyperTritonPDG || !particle.isPhysicalPrimary()) { + continue; + } + + mQaRegistry.fill(HIST("fractionMCGen/hHypertriton"), signedPt, particle.y(), centrality); + + const int expectedHe3Pdg = particlePdg > 0 ? He3PDG : -He3PDG; + const int expectedTritonPdg = particlePdg > 0 ? TritonPDG : -TritonPDG; + const int expectedChargedPionPdg = particlePdg > 0 ? -PDG_t::kPiPlus : PDG_t::kPiPlus; + bool hasExpectedHe3 = false; + bool hasExpectedTriton = false; + bool hasExpectedChargedPion = false; + bool hasPi0 = false; + float signedHe3Pt = 0.f; + float he3Rapidity = 0.f; + float signedTritonPt = 0.f; + float tritonRapidity = 0.f; + for (const auto& daughter : particle.template daughters_as()) { + if (daughter.pdgCode() == expectedHe3Pdg) { + hasExpectedHe3 = true; + signedHe3Pt = daughter.pdgCode() > 0 ? daughter.pt() : -daughter.pt(); + he3Rapidity = daughter.y(); + } else if (daughter.pdgCode() == expectedTritonPdg) { + hasExpectedTriton = true; + signedTritonPt = daughter.pdgCode() > 0 ? daughter.pt() : -daughter.pt(); + tritonRapidity = daughter.y(); + } else if (daughter.pdgCode() == expectedChargedPionPdg) { + hasExpectedChargedPion = true; + } else if (daughter.pdgCode() == PDG_t::kPi0) { + hasPi0 = true; + } + } + + if (hasExpectedHe3 && hasExpectedChargedPion) { + mQaRegistry.fill(HIST("fractionMCGen/hHypertritonToHe3Pi"), signedPt, particle.y(), centrality); + mQaRegistry.fill(HIST("fractionMCGen/hHypertritonDaughterHe3"), signedHe3Pt, he3Rapidity, centrality); + mQaRegistry.fill(HIST("fractionMCGen/hHypertritonPtVsDaughterHe3Pt"), signedPt, signedHe3Pt, particle.y(), he3Rapidity, centrality); + } + if (hasExpectedTriton && hasPi0) { + mQaRegistry.fill(HIST("fractionMCGen/hHypertritonToTritonPi0"), signedPt, particle.y(), centrality); + mQaRegistry.fill(HIST("fractionMCGen/hHypertritonDaughterTriton"), signedTritonPt, tritonRapidity, centrality); + mQaRegistry.fill(HIST("fractionMCGen/hHypertritonPtVsDaughterTritonPt"), signedPt, signedTritonPt, particle.y(), tritonRapidity, centrality); + } + } + } + // MC DCA templates use the relaxed-DCA selection. MC purity uses the full // nominal candidate selection, including its DCA requirement. - void processDcaFractionPurityMC(const CollisionsFullMC& collisions, const TrackCandidatesMCDca& tracks, const aod::McParticles&, const aod::BCsWithTimestamps& bcs) + void processDcaFractionPurityMC(const CollisionsFullMC& collisions, const TrackCandidatesMCDca& tracks, const aod::McParticles& mcParticles, const aod::BCsWithTimestamps& bcs) { + SelectedMcCollisionInfo selectedMcCollisions; for (const auto& collision : collisions) { if (!selectCollision(collision, bcs)) { continue; } + if (collision.has_mcCollision()) { + const int64_t mcCollisionId = collision.mcCollisionId(); + const int numberOfContributors = collision.numContrib(); + const auto storedCollision = selectedMcCollisions.find(mcCollisionId); + if (storedCollision == selectedMcCollisions.end() || numberOfContributors > storedCollision->second.first) { + selectedMcCollisions[mcCollisionId] = {numberOfContributors, collision.centFT0C()}; + } + } + const uint64_t collIdx = collision.globalIndex(); auto tracksThisCollision = tracks.sliceBy(mPerColMC, collIdx); tracksThisCollision.bindExternalIndices(&tracks); @@ -3552,6 +4021,7 @@ struct HadNucleiFemto { } } } + fillMCGeneratorNucleiFeedDown(mcParticles, selectedMcCollisions); } PROCESS_SWITCH(HadNucleiFemto, processDcaFractionPurityMC, "Produce MC DCA templates and truth-purity counters", false); @@ -3642,10 +4112,34 @@ struct HadNucleiFemto { } } else if (passTrackNu && useTritonNucleus()) { const float tpcNSigmaTr = track.tpcNSigmaTr(); + const float absPTr = std::abs(track.p()); + const float signedPtTr = track.sign() * track.pt(); + const bool passTPCRejectTr = !(std::abs(track.tpcNSigmaDe()) < tritonPid.settingTPCRejectNsig || + std::abs(track.tpcNSigmaPr()) < tritonPid.settingTPCRejectNsig || + std::abs(track.tpcNSigmaPi()) < tritonPid.settingTPCRejectNsig); o2::aod::ITSResponse itsResponse; - const float itsNSigmaTr = itsResponse.nSigmaITS(track.itsClusterSizes(), std::abs(track.p()), track.eta()); - mQaRegistry.fill(HIST("purity/h2NsigmaNuTPC_preselection"), track.sign() * track.pt(), tpcNSigmaTr); - mQaRegistry.fill(HIST("purity/h2NSigmaNuITS_preselection"), track.sign() * track.pt(), itsNSigmaTr); + const float itsNSigmaTr = itsResponse.nSigmaITS(track.itsClusterSizes(), absPTr, track.eta()); + if (passTPCRejectTr) { + mQaRegistry.fill(HIST("purity/h2NSigmaNuITS_preselection"), signedPtTr, itsNSigmaTr); + } + const bool passITSTr = absPTr >= tritonPid.settingPIDMomentumSplitTr || + std::abs(itsNSigmaTr) < tritonPid.settingCutITSNsigmaLowPTr; + const bool passTPCTr = absPTr < tritonPid.settingPIDMomentumSplitTr + ? std::abs(tpcNSigmaTr) < tritonPid.settingCutTPCNsigmaLowPTr + : tpcNSigmaTr > tritonPid.settingCutTPCNsigmaHighPMinTr && + tpcNSigmaTr < tritonPid.settingCutTPCNsigmaHighPMaxTr; + // Keep triton TPC PID open, while matching the species-rejection and ITS cuts. + if (passTPCRejectTr && passITSTr) { + mQaRegistry.fill(HIST("purity/h2NsigmaNuTPC_preselection"), signedPtTr, tpcNSigmaTr); + // Apply the triton TPC PID cut but keep TOF mass uncut to retain its sidebands. + if (passTPCTr && absPTr > tritonPid.settingTOFMassMomentumMinTr && track.hasTOF()) { + fillTritonTOFMassComparison(track, true); + const float massTOFTr = tritonTOFMass(track); + if (std::isfinite(massTOFTr)) { + mQaRegistry.fill(HIST("purity/h2MassTOFTr_preselection"), signedPtTr, massTOFTr); + } + } + } } const bool isHadronSelected = passTrackHad && selectionPIDHadron(track); diff --git a/PWGCF/Femto/FemtoNuclei/Tasks/piDeFemtoSystematics.cxx b/PWGCF/Femto/FemtoNuclei/Tasks/piDeFemtoSystematics.cxx index 515dbde5ba7..08328b97ca9 100644 --- a/PWGCF/Femto/FemtoNuclei/Tasks/piDeFemtoSystematics.cxx +++ b/PWGCF/Femto/FemtoNuclei/Tasks/piDeFemtoSystematics.cxx @@ -202,7 +202,7 @@ struct PiDeFemtoSystematics { ConfigurableAxis centralityAxis{"centralityAxis", {100, 0., 100.}, "centrality axis"}; using MixingBinning = ColumnBinningPolicy; - MixingBinning mixingBinning{{mixingVertexAxis, mixingCentralityAxis}, true}; + MixingBinning mixingBinning{{mixingVertexAxis, mixingCentralityAxis}}; SliceCache cache; SameKindPair mixedEventPairs{ mixingBinning, eventCuts.numberOfMixedEvents, -1, &cache}; diff --git a/PWGCF/Femto/TableProducer/CMakeLists.txt b/PWGCF/Femto/TableProducer/CMakeLists.txt index 70caef3c49c..d804d6e39fa 100644 --- a/PWGCF/Femto/TableProducer/CMakeLists.txt +++ b/PWGCF/Femto/TableProducer/CMakeLists.txt @@ -11,7 +11,12 @@ o2physics_add_dpl_workflow(femto-producer SOURCES ./femtoProducer.cxx - PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils O2Physics::PWGEMPhotonMesonCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(femto-producer-collision-tagger + SOURCES ./femtoProducerCollisionTagger.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(femto-producer-derived-to-derived @@ -38,3 +43,13 @@ o2physics_add_dpl_workflow(femto-producer-resonances SOURCES ./femtoProducerResonances.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(femto-producer-two-photon-resonances + SOURCES ./femtoProducerTwoPhotonResonances.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(femto-producer-track-track-two-photon-resonances + SOURCES ./femtoProducerTrackTrackTwoPhotonResonances.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGCF/Femto/TableProducer/femtoProducer.cxx b/PWGCF/Femto/TableProducer/femtoProducer.cxx index 0053724e5a9..7ca55f69bed 100644 --- a/PWGCF/Femto/TableProducer/femtoProducer.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducer.cxx @@ -19,8 +19,10 @@ #include "PWGCF/Femto/Core/kinkBuilder.h" #include "PWGCF/Femto/Core/mcBuilder.h" #include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/photonBuilder.h" #include "PWGCF/Femto/Core/trackBuilder.h" #include "PWGCF/Femto/Core/v0Builder.h" +#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" #include "PWGLF/DataModel/LFKinkDecayTables.h" @@ -92,19 +94,53 @@ using Run3RecoCascades = o2::soa::Join; using Run3Kinks = o2::aod::KinkCands; +using Run3SigmaPlusPcms = o2::aod::SigmaPlusCands; +using Run3RecoSigmaPlusPcms = o2::aod::SigmaPlusCandsMC; + using Run3McGenParticles = o2::aod::McParticles; } // namespace o2::analysis::femto::rawinputs struct FemtoProducer { - o2::framework::Preslice perMcCollision = o2::aod::mcparticle::mcCollisionId; - o2::framework::Preslice perColRecoTracks = o2::aod::track::collisionId; - o2::framework::Preslice perColRecoV0s = o2::aod::v0data::collisionId; - o2::framework::Preslice perColRecoCascades = o2::aod::cascdata::collisionId; - o2::framework::Preslice perColRecoKinks = o2::aod::kinkcand::collisionId; - o2::framework::Preslice perColRecoD0s = o2::aod::hf_cand::collisionId; - o2::framework::Preslice perColRecoLcs = o2::aod::hf_cand::collisionId; + // all Preslice declarations, grouped into a single data member + struct : public o2::framework::PresliceGroup { + o2::framework::Preslice perMcCollision = o2::aod::mcparticle::mcCollisionId; + o2::framework::Preslice perColTracks = o2::aod::track::collisionId; + o2::framework::Preslice perColRecoTracks = o2::aod::track::collisionId; + o2::framework::Preslice perColV0s = o2::aod::v0data::collisionId; + o2::framework::Preslice perColRecoV0s = o2::aod::v0data::collisionId; + o2::framework::Preslice perColRecoPCMPhotons = o2::aod::v0photonkf::collisionId; + o2::framework::Preslice perColCascades = o2::aod::cascdata::collisionId; + o2::framework::Preslice perColRecoCascades = o2::aod::cascdata::collisionId; + o2::framework::Preslice perColRecoKinks = o2::aod::kinkcand::collisionId; + o2::framework::Preslice perColSigmaPlusPcms = o2::aod::sigmapluscand::collisionId; + o2::framework::Preslice perColRecoSigmaPlusPcms = o2::aod::sigmapluscand::collisionId; + o2::framework::Preslice perColD0s = o2::aod::hf_cand::collisionId; + o2::framework::Preslice perColRecoD0s = o2::aod::hf_cand::collisionId; + o2::framework::Preslice perColLcs = o2::aod::hf_cand::collisionId; + o2::framework::Preslice perColRecoLcs = o2::aod::hf_cand::collisionId; + } preslices; + + // plain builder helper objects, grouped into a single data member + struct { + collisionbuilder::CollisionBuilder collisionBuilder; + trackbuilder::TrackBuilder trackBuilder; + v0builder::V0Builder k0shortBuilder; + v0builder::V0Builder lambdaBuilder; + v0builder::V0Builder antilambdaBuilder; + photonbuilder::PhotonBuilder photonBuilder; + charmhadronbuilder::CharmHadronBuilder d0Builder; + charmhadronbuilder::CharmHadronBuilder d0barBuilder; + charmhadronbuilder::CharmHadronBuilder lcBuilder; + charmhadronbuilder::CharmHadronBuilder lcBarBuilder; + cascadebuilder::CascadeBuilder xiBuilder; + cascadebuilder::CascadeBuilder omegaBuilder; + kinkbuilder::KinkBuilder sigmaBuilder; + kinkbuilder::KinkBuilder sigmaPlusBuilder; + kinkbuilder::SigmaPlusPcmBuilder sigmaPlusPcmBuilder; + mcbuilder::McBuilder mcBuilder; + } builders; // ccdb config collisionbuilder::ConfCcdb confCcdb; @@ -115,12 +151,10 @@ struct FemtoProducer { collisionbuilder::ConfCollisionFilters confCollisionFilters; collisionbuilder::ConfCollisionBits confCollisionBits; collisionbuilder::ConfCollisionRctFlags confCollisionRctFlags; - collisionbuilder::CollisionBuilder collisionBuilder; // track builder trackbuilder::TrackBuilderProducts trackBuilderProducts; trackbuilder::ConfTrackTables confTrackTables; - trackbuilder::TrackBuilder trackBuilder; trackbuilder::ConfTrackBits confTrackBits; trackbuilder::ConfTrackFilters confTrackFilters; @@ -129,10 +163,13 @@ struct FemtoProducer { v0builder::ConfV0Tables confV0Tables; v0builder::ConfV0Filters confV0Filters; v0builder::ConfK0shortBits confK0shortBits; - v0builder::V0Builder k0shortBuilder; v0builder::ConfLambdaBits confLambdaBits; - v0builder::V0Builder lambdaBuilder; - v0builder::V0Builder antilambdaBuilder; + + // photon (PCM) builder + photonbuilder::PhotonBuilderProducts photonBuilderProducts; + photonbuilder::ConfPhotonTables confPhotonTables; + photonbuilder::ConfPhotonFilters confPhotonFilters; + photonbuilder::ConfPhotonBits confPhotonBits; // charm hadron builder charmhadronbuilder::CharmHadronBuilderProducts charmHadronBuilderProducts; @@ -140,34 +177,26 @@ struct FemtoProducer { charmhadronbuilder::ConfCharmHadronTables confCharmHadronTables; charmhadronbuilder::ConfD0Bits confD0Bits; charmhadronbuilder::ConfLcBits confLcBits; - charmhadronbuilder::CharmHadronBuilder d0Builder; - charmhadronbuilder::CharmHadronBuilder d0barBuilder; - charmhadronbuilder::CharmHadronBuilder lcBuilder; - charmhadronbuilder::CharmHadronBuilder lcBarBuilder; // cascade builder cascadebuilder::CascadeBuilderProducts cascadeBuilderProducts; cascadebuilder::ConfCascadeTables confCascadeTables; cascadebuilder::ConfCascadeFilters confCascadeFilters; cascadebuilder::ConfXiBits confXiBits; - cascadebuilder::CascadeBuilder xiBuilder; cascadebuilder::ConfOmegaBits confOmegaBits; - cascadebuilder::CascadeBuilder omegaBuilder; // kink builder kinkbuilder::KinkBuilderProducts kinkBuilderProducts; kinkbuilder::ConfKinkTables confKinkTables; kinkbuilder::ConfKinkFilters confKinkFilters; kinkbuilder::ConfSigmaBits confSigmaBits; - kinkbuilder::KinkBuilder sigmaBuilder; kinkbuilder::ConfSigmaPlusBits confSigmaPlusBits; - kinkbuilder::KinkBuilder sigmaPlusBuilder; + kinkbuilder::ConfSigmaPlusPcmBits confSigmaPlusPcmBits; // mc builder mcbuilder::ConfMc confMc; mcbuilder::ConfMcTables confMcTables; mcbuilder::McBuilderProducts mcProducts; - mcbuilder::McBuilder mcBuilder; // histogramming o2::framework::HistogramRegistry hRegistry{"FemtoProducer", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; @@ -188,67 +217,84 @@ struct FemtoProducer { // ---- builders ----------------------------------------------------------- // Order matters here: CollisionBuilder must init before TrackBuilder (which checks - // collisionBuilder.producingCollisions()/producingLiteCollisions()), and TrackBuilder + // builders.collisionBuilder.producingCollisions()/producingLiteCollisions()), and TrackBuilder // must init before the V0/Cascade/Kink builders (which check - // trackBuilder.producingTracks()/producingLiteTracks()). - collisionBuilder.init(&hRegistry, confCollisionFilters, confCollisionBits, confCollisionRctFlags, confCcdb, confCollisionTables, context); - trackBuilder.init(&hRegistry, confTrackBits, confTrackFilters, confTrackTables, context, collisionBuilder); + // builders.trackBuilder.producingTracks()/producingLiteTracks()). + builders.collisionBuilder.init(&hRegistry, confCollisionFilters, confCollisionBits, confCollisionRctFlags, confCcdb, confCollisionTables, context); + builders.trackBuilder.init(&hRegistry, confTrackBits, confTrackFilters, confTrackTables, context, builders.collisionBuilder); + + builders.k0shortBuilder.init(&hRegistry, confK0shortBits, confV0Filters, confV0Tables, context, builders.trackBuilder); + builders.lambdaBuilder.init(&hRegistry, confLambdaBits, confV0Filters, confV0Tables, context, builders.trackBuilder); + builders.antilambdaBuilder.init(&hRegistry, confLambdaBits, confV0Filters, confV0Tables, context, builders.trackBuilder); - k0shortBuilder.init(&hRegistry, confK0shortBits, confV0Filters, confV0Tables, context, trackBuilder); - lambdaBuilder.init(&hRegistry, confLambdaBits, confV0Filters, confV0Tables, context, trackBuilder); - antilambdaBuilder.init(&hRegistry, confLambdaBits, confV0Filters, confV0Tables, context, trackBuilder); + builders.photonBuilder.init(&hRegistry, confPhotonBits, confPhotonFilters, confPhotonTables, context); - d0Builder.init(&hRegistry, confD0Bits, confCharmHadronFilters, confCharmHadronTables, context); - d0barBuilder.init(&hRegistry, confD0Bits, confCharmHadronFilters, confCharmHadronTables, context); + builders.d0Builder.init(&hRegistry, confD0Bits, confCharmHadronFilters, confCharmHadronTables, context); + builders.d0barBuilder.init(&hRegistry, confD0Bits, confCharmHadronFilters, confCharmHadronTables, context); - lcBuilder.init(&hRegistry, confLcBits, confCharmHadronFilters, confCharmHadronTables, context); - lcBarBuilder.init(&hRegistry, confLcBits, confCharmHadronFilters, confCharmHadronTables, context); + builders.lcBuilder.init(&hRegistry, confLcBits, confCharmHadronFilters, confCharmHadronTables, context); + builders.lcBarBuilder.init(&hRegistry, confLcBits, confCharmHadronFilters, confCharmHadronTables, context); - sigmaBuilder.init(&hRegistry, confSigmaBits, confKinkFilters, confKinkTables, context, trackBuilder); - sigmaPlusBuilder.init(&hRegistry, confSigmaPlusBits, confKinkFilters, confKinkTables, context, trackBuilder); + builders.sigmaBuilder.init(&hRegistry, confSigmaBits, confKinkFilters, confKinkTables, context, builders.trackBuilder); + builders.sigmaPlusBuilder.init(&hRegistry, confSigmaPlusBits, confKinkFilters, confKinkTables, context, builders.trackBuilder); + builders.sigmaPlusPcmBuilder.init(&hRegistry, confSigmaPlusPcmBits, confKinkFilters, confKinkTables, context, builders.trackBuilder); - xiBuilder.init(&hRegistry, confXiBits, confCascadeFilters, confCascadeTables, context, trackBuilder); - omegaBuilder.init(&hRegistry, confOmegaBits, confCascadeFilters, confCascadeTables, context, trackBuilder); + builders.xiBuilder.init(&hRegistry, confXiBits, confCascadeFilters, confCascadeTables, context, builders.trackBuilder); + builders.omegaBuilder.init(&hRegistry, confOmegaBits, confCascadeFilters, confCascadeTables, context, builders.trackBuilder); - mcBuilder.init(confMc, confMcTables, context); + builders.mcBuilder.init(confMc, confMcTables, context); // ---- guard: enabled tables vs. enabled process function ------------------ - if ((xiBuilder.fillAnyTable() || omegaBuilder.fillAnyTable()) && + if ((builders.xiBuilder.fillAnyTable() || builders.omegaBuilder.fillAnyTable()) && (!doprocessTracksV0sCascadesRun3pp && !doprocessTracksV0sCascadesRun3PbPb && !doprocessTracksV0sCascadesRun3PbPbWithEventShape && - !doprocessTracksV0sCascadesKinksRun3pp && !doprocessTracksV0sCascadesRun3ppMc && + !doprocessTracksV0sCascadesPhotonsRun3PbPbWithEventShape && + !doprocessTracksV0sCascadesKinksRun3pp && !doprocessTracksV0sCascadesKinksPhotonsRun3pp && + !doprocessTracksV0sCascadesRun3ppMc && !doprocessTracksV0sCascadesRun3PbPbMc)) { LOG(fatal) << "At least one cascade table is enabled, but wrong process function is enabled. Breaking..."; } - if ((lambdaBuilder.fillAnyTable() || antilambdaBuilder.fillAnyTable() || k0shortBuilder.fillAnyTable()) && + if ((builders.lambdaBuilder.fillAnyTable() || builders.antilambdaBuilder.fillAnyTable() || builders.k0shortBuilder.fillAnyTable()) && (!doprocessTracksV0sCascadesRun3pp && !doprocessTracksV0sCascadesRun3PbPb && - !doprocessTracksV0sRun3pp && !doprocessTracksV0sCascadesKinksRun3pp && - !doprocessTracksV0sCascadesRun3PbPbWithEventShape && + !doprocessTracksV0sRun3pp && !doprocessTracksV0sCascadesKinksRun3pp && !doprocessTracksV0sCascadesKinksPhotonsRun3pp && + !doprocessTracksV0sCascadesRun3PbPbWithEventShape && !doprocessTracksV0sCascadesPhotonsRun3PbPbWithEventShape && !doprocessTracksV0sRun3ppMc && !doprocessTracksV0sRun3PbPb && !doprocessTracksV0sRun3PbPbMc && !doprocessTracksV0sCascadesRun3ppMc && !doprocessTracksV0sCascadesRun3PbPbMc && !doprocessTracksV0sKinksRun3ppMc)) { LOG(fatal) << "At least one v0 table is enabled, but wrong process function is enabled. Breaking..."; } - if ((sigmaBuilder.fillAnyTable() || sigmaPlusBuilder.fillAnyTable()) && - (!doprocessTracksKinksRun3pp && !doprocessTracksV0sCascadesKinksRun3pp && - !doprocessTracksKinksRun3ppMc && !doprocessTracksV0sKinksRun3ppMc)) { + if (builders.photonBuilder.fillAnyTable() && + (!doprocessTracksPhotonsRun3pp && !doprocessTracksPhotonsRun3PbPb && + !doprocessTracksV0sCascadesKinksPhotonsRun3pp && !doprocessTracksV0sCascadesPhotonsRun3PbPbWithEventShape)) { + LOG(fatal) << "At least one photon table is enabled, but wrong process function is enabled. Breaking..."; + } + if ((builders.sigmaBuilder.fillAnyTable() || builders.sigmaPlusBuilder.fillAnyTable()) && + (!doprocessTracksKinksRun3pp && !doprocessTracksV0sCascadesKinksRun3pp && !doprocessTracksV0sCascadesKinksPhotonsRun3pp && + !doprocessTracksKinksRun3ppMc && !doprocessTracksV0sKinksRun3ppMc && + !doprocessTracksKinksSigmaPlusPcmsRun3pp && !doprocessTracksKinksSigmaPlusPcmsRun3ppMc)) { LOG(fatal) << "At least one kink table is enabled, but wrong process function is enabled. Breaking..."; } - if ((d0Builder.fillAnyTable() || d0barBuilder.fillAnyTable()) && + if (builders.sigmaPlusPcmBuilder.fillAnyTable() && + (!doprocessTracksSigmaPlusPcmsRun3pp && !doprocessTracksSigmaPlusPcmsRun3ppMc && + !doprocessTracksKinksSigmaPlusPcmsRun3pp && !doprocessTracksKinksSigmaPlusPcmsRun3ppMc)) { + LOG(fatal) << "At least one sigma plus (PCM) table is enabled, but wrong process function is enabled. Breaking..."; + } + if ((builders.d0Builder.fillAnyTable() || builders.d0barBuilder.fillAnyTable()) && (!doprocessTracksD0sRun3pp && !doprocessTracksD0sRun3PbPb && !doprocessTracksD0sRun3ppMc && !doprocessTracksD0sRun3PbPbMc)) { LOG(fatal) << "At least one d0 table is enabled, but wrong process function is enabled. Breaking..."; } - if ((lcBuilder.fillAnyTable() || lcBarBuilder.fillAnyTable()) && + if ((builders.lcBuilder.fillAnyTable() || builders.lcBarBuilder.fillAnyTable()) && (!doprocessTracksLcsRun3pp && !doprocessTracksLcsRun3PbPb && !doprocessTracksLcsRun3ppMc && !doprocessTracksLcsRun3PbPbMc)) { LOG(fatal) << "At least one lc table is enabled, but wrong process function is enabled. Breaking..."; } - if (mcBuilder.fillAnyTable() && + if (builders.mcBuilder.fillAnyTable() && (!doprocessTracksRun3ppMc && !doprocessTracksRun3PbPbMc && !doprocessTracksV0sRun3ppMc && !doprocessTracksV0sRun3PbPbMc && !doprocessTracksV0sCascadesRun3ppMc && !doprocessTracksV0sCascadesRun3PbPbMc && !doprocessTracksKinksRun3ppMc && !doprocessTracksV0sKinksRun3ppMc && + !doprocessTracksSigmaPlusPcmsRun3ppMc && !doprocessTracksKinksSigmaPlusPcmsRun3ppMc && !doprocessTracksD0sRun3ppMc && !doprocessTracksD0sRun3PbPbMc && !doprocessTracksLcsRun3ppMc && !doprocessTracksLcsRun3PbPbMc && !doprocessMcOnly)) { @@ -264,20 +310,22 @@ struct FemtoProducer { } }; // substitute the real accessors if they live on the builders rather than the conf groups - add("collision", collisionBuilder.fillAnyTable(), collisionBuilder.isPassThrough()); - add("track", trackBuilder.fillAnyTable(), trackBuilder.isPassThrough()); - add("k0short", k0shortBuilder.fillAnyTable(), k0shortBuilder.isPassThrough()); - add("lambda", lambdaBuilder.fillAnyTable(), lambdaBuilder.isPassThrough()); - add("antilambda", antilambdaBuilder.fillAnyTable(), antilambdaBuilder.isPassThrough()); - add("d0", d0Builder.fillAnyTable(), d0Builder.isPassThrough()); - add("d0bar", d0barBuilder.fillAnyTable(), d0barBuilder.isPassThrough()); - add("lc", lcBuilder.fillAnyTable(), lcBuilder.isPassThrough()); - add("lcbar", lcBarBuilder.fillAnyTable(), lcBarBuilder.isPassThrough()); - add("sigma", sigmaBuilder.fillAnyTable(), sigmaBuilder.isPassThrough()); - add("sigmaplus", sigmaPlusBuilder.fillAnyTable(), sigmaPlusBuilder.isPassThrough()); - add("xi", xiBuilder.fillAnyTable(), xiBuilder.isPassThrough()); - add("omega", omegaBuilder.fillAnyTable(), omegaBuilder.isPassThrough()); - add("mc", mcBuilder.fillAnyTable(), mcBuilder.isPassThrough()); + add("collision", builders.collisionBuilder.fillAnyTable(), builders.collisionBuilder.isPassThrough()); + add("track", builders.trackBuilder.fillAnyTable(), builders.trackBuilder.isPassThrough()); + add("k0short", builders.k0shortBuilder.fillAnyTable(), builders.k0shortBuilder.isPassThrough()); + add("lambda", builders.lambdaBuilder.fillAnyTable(), builders.lambdaBuilder.isPassThrough()); + add("antilambda", builders.antilambdaBuilder.fillAnyTable(), builders.antilambdaBuilder.isPassThrough()); + add("photon", builders.photonBuilder.fillAnyTable(), builders.photonBuilder.isPassThrough()); + add("d0", builders.d0Builder.fillAnyTable(), builders.d0Builder.isPassThrough()); + add("d0bar", builders.d0barBuilder.fillAnyTable(), builders.d0barBuilder.isPassThrough()); + add("lc", builders.lcBuilder.fillAnyTable(), builders.lcBuilder.isPassThrough()); + add("lcbar", builders.lcBarBuilder.fillAnyTable(), builders.lcBarBuilder.isPassThrough()); + add("sigma", builders.sigmaBuilder.fillAnyTable(), builders.sigmaBuilder.isPassThrough()); + add("sigmaplus", builders.sigmaPlusBuilder.fillAnyTable(), builders.sigmaPlusBuilder.isPassThrough()); + add("sigmapluspcm", builders.sigmaPlusPcmBuilder.fillAnyTable(), builders.sigmaPlusPcmBuilder.isPassThrough()); + add("xi", builders.xiBuilder.fillAnyTable(), builders.xiBuilder.isPassThrough()); + add("omega", builders.omegaBuilder.fillAnyTable(), builders.omegaBuilder.isPassThrough()); + add("mc", builders.mcBuilder.fillAnyTable(), builders.mcBuilder.isPassThrough()); const bool anyPassThrough = std::any_of(passThroughFlags.begin(), passThroughFlags.end(), [](const auto& f) { return f.second; }); @@ -298,19 +346,19 @@ struct FemtoProducer { // in mc pass-through every generated collision is written, but reco collisions of other // sub-generators are still rejected in checkCollision -> biased event-loss denominator - if (mcBuilder.isPassThrough() && collisionBuilder.isPassThrough() && collisionBuilder.subGeneratorId() >= 0) { + if (builders.mcBuilder.isPassThrough() && builders.collisionBuilder.isPassThrough() && builders.collisionBuilder.subGeneratorId() >= 0) { LOG(warn) << "MC and collision pass-through are enabled, but CollisionFilter.subGeneratorId = " - << collisionBuilder.subGeneratorId() << " still rejects reconstructed collisions of other sub-generators, " + << builders.collisionBuilder.subGeneratorId() << " still rejects reconstructed collisions of other sub-generators, " << "while all generated collisions are written. Set it to -1 for unbiased event/signal-loss corrections."; } // daughter tracks can be written a second time under the collision of their mother candidate; // without FTrackExtras (fillType) these rows cannot be told apart from selected tracks downstream - const bool anyDaughterBuilder = k0shortBuilder.fillAnyTable() || lambdaBuilder.fillAnyTable() || antilambdaBuilder.fillAnyTable() || - xiBuilder.fillAnyTable() || omegaBuilder.fillAnyTable() || - sigmaBuilder.fillAnyTable() || sigmaPlusBuilder.fillAnyTable() || - d0Builder.fillAnyTable() || d0barBuilder.fillAnyTable() || lcBuilder.fillAnyTable() || lcBarBuilder.fillAnyTable(); - if (trackBuilder.isPassThrough() && anyDaughterBuilder && !trackBuilder.producingTrackExtras()) { + const bool anyDaughterBuilder = builders.k0shortBuilder.fillAnyTable() || builders.lambdaBuilder.fillAnyTable() || builders.antilambdaBuilder.fillAnyTable() || + builders.xiBuilder.fillAnyTable() || builders.omegaBuilder.fillAnyTable() || + builders.sigmaBuilder.fillAnyTable() || builders.sigmaPlusBuilder.fillAnyTable() || builders.sigmaPlusPcmBuilder.fillAnyTable() || + builders.d0Builder.fillAnyTable() || builders.d0barBuilder.fillAnyTable() || builders.lcBuilder.fillAnyTable() || builders.lcBarBuilder.fillAnyTable(); + if (builders.trackBuilder.isPassThrough() && anyDaughterBuilder && !builders.trackBuilder.producingTrackExtras()) { LOG(warn) << "Track pass-through is enabled together with candidates that register daughter tracks, but FTrackExtras is not produced. " << "Daughter-only track rows cannot be identified downstream (enable TrackTables.produceTrackExtras)."; } @@ -322,12 +370,18 @@ struct FemtoProducer { static_cast(doprocessTracksRun3PbPb) + static_cast(doprocessTracksRun3PbPbWithEventShape) + static_cast(doprocessTracksV0sRun3pp) + + static_cast(doprocessTracksPhotonsRun3pp) + + static_cast(doprocessTracksPhotonsRun3PbPb) + static_cast(doprocessTracksV0sRun3PbPb) + static_cast(doprocessTracksV0sCascadesRun3pp) + static_cast(doprocessTracksV0sCascadesRun3PbPb) + static_cast(doprocessTracksV0sCascadesRun3PbPbWithEventShape) + + static_cast(doprocessTracksV0sCascadesPhotonsRun3PbPbWithEventShape) + static_cast(doprocessTracksKinksRun3pp) + static_cast(doprocessTracksV0sCascadesKinksRun3pp) + + static_cast(doprocessTracksV0sCascadesKinksPhotonsRun3pp) + + static_cast(doprocessTracksSigmaPlusPcmsRun3pp) + + static_cast(doprocessTracksKinksSigmaPlusPcmsRun3pp) + static_cast(doprocessTracksD0sRun3pp) + static_cast(doprocessTracksD0sRun3PbPb) + static_cast(doprocessTracksRun3ppMc) + @@ -338,6 +392,8 @@ struct FemtoProducer { static_cast(doprocessTracksV0sCascadesRun3PbPbMc) + static_cast(doprocessTracksKinksRun3ppMc) + static_cast(doprocessTracksV0sKinksRun3ppMc) + + static_cast(doprocessTracksSigmaPlusPcmsRun3ppMc) + + static_cast(doprocessTracksKinksSigmaPlusPcmsRun3ppMc) + static_cast(doprocessTracksD0sRun3ppMc) + static_cast(doprocessTracksD0sRun3PbPbMc) + static_cast(doprocessTracksLcsRun3pp) + @@ -357,17 +413,17 @@ struct FemtoProducer { template bool processCollisions(T1 const& col, T2 const& /* bcs*/, T3 const& tracks) { - collisionBuilder.reset(); + builders.collisionBuilder.reset(); auto bc = col.template bc_as(); - collisionBuilder.initCollision(bc, col, tracks, ccdb, hRegistry); - if (!collisionBuilder.checkCollision(col)) { + builders.collisionBuilder.initCollision(bc, col, tracks, ccdb, hRegistry); + if (!builders.collisionBuilder.checkCollision(col)) { return false; } // Normally the collision row is written lazily, once a candidate is found. In // pass-through we need every accepted collision, including those with no candidate, // otherwise event-normalised observables lose exactly those events. - if (collisionBuilder.isPassThrough()) { - collisionBuilder.fillCollision(collisionBuilderProducts, col); + if (builders.collisionBuilder.isPassThrough()) { + builders.collisionBuilder.fillCollision(collisionBuilderProducts, col); } return true; } @@ -375,298 +431,530 @@ struct FemtoProducer { template bool processMcCollisions(T1 const& col, T2 const& mcCols, T3 const& /* bcs*/, T4 const& tracks) { - collisionBuilder.reset(); - // mcBuilder.reset() runs once per timeframe in the calling processTracksXXXMc function + builders.collisionBuilder.reset(); + // builders.mcBuilder.reset() runs once per timeframe in the calling processTracksXXXMc function // (both pass-through and lazy-fill mode); resetting here on every reco collision would // wipe the dedup maps and duplicate every mc collision/particle referenced by more than // one reco collision (e.g. split-vertex reconstruction under pileup) auto bc = col.template bc_as(); - collisionBuilder.initCollision(bc, col, tracks, ccdb, hRegistry); - if (!collisionBuilder.checkCollision(col, mcCols)) { + builders.collisionBuilder.initCollision(bc, col, tracks, ccdb, hRegistry); + if (!builders.collisionBuilder.checkCollision(col, mcCols)) { return false; } // NOTE: must be fillMcCollision, not fillCollision - fillMcCollision returns early on // mCollisionAlreadyFilled, so filling the collision first would permanently skip // the collision label - if (collisionBuilder.isPassThrough()) { - collisionBuilder.fillMcCollision(collisionBuilderProducts, col, mcCols, mcProducts, mcBuilder); + if (builders.collisionBuilder.isPassThrough()) { + builders.collisionBuilder.fillMcCollision(collisionBuilderProducts, col, mcCols, mcProducts, builders.mcBuilder); } return true; } // processing tracks + // NOTE: trackBuilder.reset() is NOT called here. It must happen exactly once per dataframe + // (in the calling processTracksXXX/processTracksXXXMc function, before the reco-collision + // loop), not once per collision, so that fillTrack()'s "protect against double filling" dedup + // (indexMap) can actually catch a track referenced from more than one collision in the same + // dataframe instead of being wiped before it gets the chance. template void processTracks(T1 const& col, T2 const& tracksWithItsPid) { - trackBuilder.reset(tracksWithItsPid); - trackBuilder.fillTracks(col, collisionBuilder, collisionBuilderProducts, tracksWithItsPid, trackBuilderProducts); + builders.trackBuilder.fillTracks(col, builders.collisionBuilder, collisionBuilderProducts, tracksWithItsPid, trackBuilderProducts); } template void processMcTracks(T1 const& col, T2 const& mcCols, T3 const& tracks, T4 const& tracksWithItsPid, T5 const& mcParticles) { - trackBuilder.reset(tracksWithItsPid); - trackBuilder.fillMcTracks(col, collisionBuilder, collisionBuilderProducts, mcCols, tracks, tracksWithItsPid, trackBuilderProducts, mcParticles, mcBuilder, mcProducts); + builders.trackBuilder.fillMcTracks(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, tracks, tracksWithItsPid, trackBuilderProducts, mcParticles, builders.mcBuilder, mcProducts); } // processing v0s template void processV0s(T1 const& col, T2 const& tracks, T3 const& v0s) { - lambdaBuilder.fillV0s(col, collisionBuilder, collisionBuilderProducts, trackBuilderProducts, v0builderProducts, v0s, tracks, trackBuilder); - antilambdaBuilder.fillV0s(col, collisionBuilder, collisionBuilderProducts, trackBuilderProducts, v0builderProducts, v0s, tracks, trackBuilder); - k0shortBuilder.fillV0s(col, collisionBuilder, collisionBuilderProducts, trackBuilderProducts, v0builderProducts, v0s, tracks, trackBuilder); + builders.lambdaBuilder.fillV0s(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, v0builderProducts, v0s, tracks, builders.trackBuilder); + builders.antilambdaBuilder.fillV0s(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, v0builderProducts, v0s, tracks, builders.trackBuilder); + builders.k0shortBuilder.fillV0s(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, v0builderProducts, v0s, tracks, builders.trackBuilder); } template void processMcV0s(T1 const& col, T2 const& mcCols, T3 const& tracks, T4 const& v0s, T5 const& mcParticles) { - lambdaBuilder.fillMcV0s(col, collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, v0builderProducts, v0s, tracks, trackBuilder, mcParticles, mcBuilder, mcProducts); - antilambdaBuilder.fillMcV0s(col, collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, v0builderProducts, v0s, tracks, trackBuilder, mcParticles, mcBuilder, mcProducts); - k0shortBuilder.fillMcV0s(col, collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, v0builderProducts, v0s, tracks, trackBuilder, mcParticles, mcBuilder, mcProducts); + builders.lambdaBuilder.fillMcV0s(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, v0builderProducts, v0s, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); + builders.antilambdaBuilder.fillMcV0s(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, v0builderProducts, v0s, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); + builders.k0shortBuilder.fillMcV0s(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, v0builderProducts, v0s, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); + } + + // processing photons (PCM) + template + void processPhotons(T1 const& col, T2 const& photons, T3 const& v0legs) + { + builders.photonBuilder.fillPhotons(col, builders.collisionBuilder, collisionBuilderProducts, photonBuilderProducts, photons, v0legs); } // processing kinks template void processKinks(T1 const& col, T2 const& tracks, T3 const& kinks) { - sigmaBuilder.fillKinks(col, collisionBuilder, collisionBuilderProducts, trackBuilderProducts, kinkBuilderProducts, kinks, tracks, trackBuilder); - sigmaPlusBuilder.fillKinks(col, collisionBuilder, collisionBuilderProducts, trackBuilderProducts, kinkBuilderProducts, kinks, tracks, trackBuilder); + builders.sigmaBuilder.fillKinks(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, kinkBuilderProducts, kinks, tracks, builders.trackBuilder); + builders.sigmaPlusBuilder.fillKinks(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, kinkBuilderProducts, kinks, tracks, builders.trackBuilder); } template void processMcKinks(T1 const& col, T2 const& mcCols, T3 const& tracks, T4 const& kinks, T5 const& mcParticles) { - sigmaBuilder.fillMcKinks(col, collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, kinkBuilderProducts, kinks, tracks, trackBuilder, mcParticles, mcBuilder, mcProducts); - sigmaPlusBuilder.fillMcKinks(col, collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, kinkBuilderProducts, kinks, tracks, trackBuilder, mcParticles, mcBuilder, mcProducts); + builders.sigmaBuilder.fillMcKinks(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, kinkBuilderProducts, kinks, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); + builders.sigmaPlusBuilder.fillMcKinks(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, kinkBuilderProducts, kinks, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); + } + + // processing sigma plus reconstructed with a PCM photon + template + void processSigmaPlusPcms(T1 const& col, T2 const& tracks, T3 const& sigmaPlusPcms) + { + builders.sigmaPlusPcmBuilder.fillSigmaPlusPcms(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, kinkBuilderProducts, sigmaPlusPcms, tracks, builders.trackBuilder); + } + template + void processMcSigmaPlusPcms(T1 const& col, T2 const& mcCols, T3 const& tracks, T4 const& sigmaPlusPcms, T5 const& mcParticles) + { + builders.sigmaPlusPcmBuilder.fillMcSigmaPlusPcms(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, kinkBuilderProducts, sigmaPlusPcms, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); } // processing cascades template void processCascades(T1 const& col, T2 const& tracks, T3 const& cascades) { - xiBuilder.fillCascades(col, collisionBuilder, collisionBuilderProducts, trackBuilderProducts, cascadeBuilderProducts, - cascades, tracks, trackBuilder); - omegaBuilder.fillCascades(col, collisionBuilder, collisionBuilderProducts, trackBuilderProducts, cascadeBuilderProducts, - cascades, tracks, trackBuilder); + builders.xiBuilder.fillCascades(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, cascadeBuilderProducts, + cascades, tracks, builders.trackBuilder); + builders.omegaBuilder.fillCascades(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, cascadeBuilderProducts, + cascades, tracks, builders.trackBuilder); } template void processMcCascades(T1 const& col, T2 const& mcCols, T3 const& tracks, T4 const& cascades, T5 const& mcParticles) { - xiBuilder.fillMcCascades(col, collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, cascadeBuilderProducts, cascades, tracks, trackBuilder, mcParticles, mcBuilder, mcProducts); - omegaBuilder.fillMcCascades(col, collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, cascadeBuilderProducts, cascades, tracks, trackBuilder, mcParticles, mcBuilder, mcProducts); + builders.xiBuilder.fillMcCascades(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, cascadeBuilderProducts, cascades, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); + builders.omegaBuilder.fillMcCascades(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, cascadeBuilderProducts, cascades, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); } // processing D0s template void processD0s(T1 const& col, T2 const& tracks, T3 const& candidates) { - d0Builder.fillCharmHadrons(col, collisionBuilder, collisionBuilderProducts, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, trackBuilder); - d0barBuilder.fillCharmHadrons(col, collisionBuilder, collisionBuilderProducts, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, trackBuilder); + builders.d0Builder.fillCharmHadrons(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, builders.trackBuilder); + builders.d0barBuilder.fillCharmHadrons(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, builders.trackBuilder); } template void processMcD0s(T1 const& col, T2 const& mcCols, T3 const& tracks, T4 const& candidates, T5 const& mcParticles) { - d0Builder.fillMcCharmHadrons(col, collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, trackBuilder, mcParticles, mcBuilder, mcProducts); - d0barBuilder.fillMcCharmHadrons(col, collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, trackBuilder, mcParticles, mcBuilder, mcProducts); + builders.d0Builder.fillMcCharmHadrons(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); + builders.d0barBuilder.fillMcCharmHadrons(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); } // processing Lcs template void processLcs(T1 const& col, T2 const& tracks, T3 const& candidates) { - lcBuilder.fillCharmHadrons(col, collisionBuilder, collisionBuilderProducts, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, trackBuilder); - lcBarBuilder.fillCharmHadrons(col, collisionBuilder, collisionBuilderProducts, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, trackBuilder); + builders.lcBuilder.fillCharmHadrons(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, builders.trackBuilder); + builders.lcBarBuilder.fillCharmHadrons(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, builders.trackBuilder); } template void processMcLcs(T1 const& col, T2 const& mcCols, T3 const& tracks, T4 const& candidates, T5 const& mcParticles) { - lcBuilder.fillMcCharmHadrons(col, collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, trackBuilder, mcParticles, mcBuilder, mcProducts); - lcBarBuilder.fillMcCharmHadrons(col, collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, trackBuilder, mcParticles, mcBuilder, mcProducts); + builders.lcBuilder.fillMcCharmHadrons(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); + builders.lcBarBuilder.fillMcCharmHadrons(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); } // ========================================================================== // process functions - data // ========================================================================== - void processTracksRun3pp(rawinputs::Run3PpCollisions::iterator const& col, + void processTracksRun3pp(rawinputs::Run3PpCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); } PROCESS_SWITCH(FemtoProducer, processTracksRun3pp, "Provide tracks", true); - void processTracksRun3PbPb(rawinputs::Run3PbPbCollisions::iterator const& col, + void processTracksRun3PbPb(rawinputs::Run3PbPbCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); } PROCESS_SWITCH(FemtoProducer, processTracksRun3PbPb, "Provide tracks in PbPb collisions", false); - void processTracksRun3PbPbWithEventShape(rawinputs::Run3PbPbCollisionsWithEventShape::iterator const& col, + void processTracksRun3PbPbWithEventShape(rawinputs::Run3PbPbCollisionsWithEventShape const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); } PROCESS_SWITCH(FemtoProducer, processTracksRun3PbPbWithEventShape, "Provide tracks in PbPb collisions with event shape information", false); - void processTracksV0sRun3pp(rawinputs::Run3PpCollisions::iterator const& col, + void processTracksV0sRun3pp(rawinputs::Run3PpCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, rawinputs::Run3Vzeros const& v0s) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto v0sThisCol = v0s.sliceBy(preslices.perColV0s, col.globalIndex()); + processV0s(col, tracksThisCol, v0sThisCol); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); - processV0s(col, tracks, v0s); } PROCESS_SWITCH(FemtoProducer, processTracksV0sRun3pp, "Provide tracks and v0s", false); - void processTracksV0sRun3PbPb(rawinputs::Run3PbPbCollisions::iterator const& col, + void processTracksPhotonsRun3pp(rawinputs::Run3PpCollisions const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3FullPidTracks const& tracks, + o2::aod::V0PhotonsKF const& photons, + o2::aod::V0Legs const& v0legs) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + // V0PhotonsKF/V0Legs carry a plain CollisionId column (not a framework index column), so they + // are not auto-grouped by the DPL binder and must be sliced manually, same as EMPhotonFilter.cxx + auto photonsThisCol = photons.sliceBy(preslices.perColRecoPCMPhotons, col.globalIndex()); + processPhotons(col, photonsThisCol, v0legs); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksPhotonsRun3pp, "Provide tracks and PCM photons", false); + + void processTracksPhotonsRun3PbPb(rawinputs::Run3PbPbCollisions const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3FullPidTracks const& tracks, + o2::aod::V0PhotonsKF const& photons, + o2::aod::V0Legs const& v0legs) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto photonsThisCol = photons.sliceBy(preslices.perColRecoPCMPhotons, col.globalIndex()); + processPhotons(col, photonsThisCol, v0legs); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksPhotonsRun3PbPb, "Provide tracks and PCM photons in PbPb collisions", false); + + void processTracksV0sRun3PbPb(rawinputs::Run3PbPbCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, rawinputs::Run3Vzeros const& v0s) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto v0sThisCol = v0s.sliceBy(preslices.perColV0s, col.globalIndex()); + processV0s(col, tracksThisCol, v0sThisCol); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); - processV0s(col, tracks, v0s); } PROCESS_SWITCH(FemtoProducer, processTracksV0sRun3PbPb, "Provide tracks and v0s in PbPb collisions", false); - void processTracksV0sCascadesRun3pp(rawinputs::Run3PpCollisions::iterator const& col, + void processTracksV0sCascadesRun3pp(rawinputs::Run3PpCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, rawinputs::Run3Vzeros const& v0s, rawinputs::Run3Cascades const& cascades) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto v0sThisCol = v0s.sliceBy(preslices.perColV0s, col.globalIndex()); + processV0s(col, tracksThisCol, v0sThisCol); + auto cascadesThisCol = cascades.sliceBy(preslices.perColCascades, col.globalIndex()); + processCascades(col, tracksThisCol, cascadesThisCol); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); - processV0s(col, tracks, v0s); - processCascades(col, tracks, cascades); } PROCESS_SWITCH(FemtoProducer, processTracksV0sCascadesRun3pp, "Provide tracks, v0s and cascades", false); - void processTracksV0sCascadesRun3PbPb(rawinputs::Run3PbPbCollisions::iterator const& col, + void processTracksV0sCascadesRun3PbPb(rawinputs::Run3PbPbCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, rawinputs::Run3Vzeros const& v0s, rawinputs::Run3Cascades const& cascades) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto v0sThisCol = v0s.sliceBy(preslices.perColV0s, col.globalIndex()); + processV0s(col, tracksThisCol, v0sThisCol); + auto cascadesThisCol = cascades.sliceBy(preslices.perColCascades, col.globalIndex()); + processCascades(col, tracksThisCol, cascadesThisCol); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); - processV0s(col, tracks, v0s); - processCascades(col, tracks, cascades); } PROCESS_SWITCH(FemtoProducer, processTracksV0sCascadesRun3PbPb, "Provide tracks, v0s and cascades in PbPb collisions", false); - void processTracksV0sCascadesRun3PbPbWithEventShape(rawinputs::Run3PbPbCollisionsWithEventShape::iterator const& col, + void processTracksV0sCascadesRun3PbPbWithEventShape(rawinputs::Run3PbPbCollisionsWithEventShape const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, rawinputs::Run3Vzeros const& v0s, rawinputs::Run3Cascades const& cascades) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto v0sThisCol = v0s.sliceBy(preslices.perColV0s, col.globalIndex()); + processV0s(col, tracksThisCol, v0sThisCol); + auto cascadesThisCol = cascades.sliceBy(preslices.perColCascades, col.globalIndex()); + processCascades(col, tracksThisCol, cascadesThisCol); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); - processV0s(col, tracks, v0s); - processCascades(col, tracks, cascades); } PROCESS_SWITCH(FemtoProducer, processTracksV0sCascadesRun3PbPbWithEventShape, "Provide tracks, v0s and cascades in PbPb collisions with event shape information", false); - void processTracksKinksRun3pp(rawinputs::Run3PpCollisions::iterator const& col, + void processTracksV0sCascadesPhotonsRun3PbPbWithEventShape(rawinputs::Run3PbPbCollisionsWithEventShape const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3FullPidTracks const& tracks, + rawinputs::Run3Vzeros const& v0s, + rawinputs::Run3Cascades const& cascades, + o2::aod::V0PhotonsKF const& photons, + o2::aod::V0Legs const& v0legs) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto v0sThisCol = v0s.sliceBy(preslices.perColV0s, col.globalIndex()); + processV0s(col, tracksThisCol, v0sThisCol); + auto cascadesThisCol = cascades.sliceBy(preslices.perColCascades, col.globalIndex()); + processCascades(col, tracksThisCol, cascadesThisCol); + // V0PhotonsKF/V0Legs carry a plain CollisionId column (not a framework index column), so they + // are not auto-grouped by the DPL binder and must be sliced manually, same as EMPhotonFilter.cxx + auto photonsThisCol = photons.sliceBy(preslices.perColRecoPCMPhotons, col.globalIndex()); + processPhotons(col, photonsThisCol, v0legs); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksV0sCascadesPhotonsRun3PbPbWithEventShape, "Provide tracks, v0s, cascades and PCM photons in PbPb collisions with event shape information", false); + + void processTracksKinksRun3pp(rawinputs::Run3PpCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, rawinputs::Run3Kinks const& kinks) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto kinksThisCol = kinks.sliceBy(preslices.perColRecoKinks, col.globalIndex()); + processKinks(col, tracksThisCol, kinksThisCol); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); - processKinks(col, tracks, kinks); } PROCESS_SWITCH(FemtoProducer, processTracksKinksRun3pp, "Provide tracks and kinks", false); - void processTracksV0sCascadesKinksRun3pp(rawinputs::Run3PpCollisions::iterator const& col, + void processTracksSigmaPlusPcmsRun3pp(rawinputs::Run3PpCollisions const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3FullPidTracks const& tracks, + rawinputs::Run3SigmaPlusPcms const& sigmaPlusPcms) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto sigmaPlusPcmsThisCol = sigmaPlusPcms.sliceBy(preslices.perColSigmaPlusPcms, col.globalIndex()); + processSigmaPlusPcms(col, tracksThisCol, sigmaPlusPcmsThisCol); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksSigmaPlusPcmsRun3pp, "Provide tracks and sigma plus reconstructed with a PCM photon", false); + + void processTracksKinksSigmaPlusPcmsRun3pp(rawinputs::Run3PpCollisions const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3FullPidTracks const& tracks, + rawinputs::Run3Kinks const& kinks, + rawinputs::Run3SigmaPlusPcms const& sigmaPlusPcms) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto kinksThisCol = kinks.sliceBy(preslices.perColRecoKinks, col.globalIndex()); + processKinks(col, tracksThisCol, kinksThisCol); + auto sigmaPlusPcmsThisCol = sigmaPlusPcms.sliceBy(preslices.perColSigmaPlusPcms, col.globalIndex()); + processSigmaPlusPcms(col, tracksThisCol, sigmaPlusPcmsThisCol); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksKinksSigmaPlusPcmsRun3pp, "Provide tracks, kinks and sigma plus reconstructed with a PCM photon", false); + + void processTracksV0sCascadesKinksRun3pp(rawinputs::Run3PpCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, rawinputs::Run3Vzeros const& v0s, rawinputs::Run3Cascades const& cascades, rawinputs::Run3Kinks const& kinks) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto v0sThisCol = v0s.sliceBy(preslices.perColV0s, col.globalIndex()); + processV0s(col, tracksThisCol, v0sThisCol); + auto cascadesThisCol = cascades.sliceBy(preslices.perColCascades, col.globalIndex()); + processCascades(col, tracksThisCol, cascadesThisCol); + auto kinksThisCol = kinks.sliceBy(preslices.perColRecoKinks, col.globalIndex()); + processKinks(col, tracksThisCol, kinksThisCol); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); - processV0s(col, tracks, v0s); - processCascades(col, tracks, cascades); - processKinks(col, tracks, kinks); } PROCESS_SWITCH(FemtoProducer, processTracksV0sCascadesKinksRun3pp, "Provide tracks, v0s, cascades and kinks", false); - void processTracksD0sRun3pp(rawinputs::Run3PpCollisions::iterator const& col, + void processTracksV0sCascadesKinksPhotonsRun3pp(rawinputs::Run3PpCollisions const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3FullPidTracks const& tracks, + rawinputs::Run3Vzeros const& v0s, + rawinputs::Run3Cascades const& cascades, + rawinputs::Run3Kinks const& kinks, + o2::aod::V0PhotonsKF const& photons, + o2::aod::V0Legs const& v0legs) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto v0sThisCol = v0s.sliceBy(preslices.perColV0s, col.globalIndex()); + processV0s(col, tracksThisCol, v0sThisCol); + auto cascadesThisCol = cascades.sliceBy(preslices.perColCascades, col.globalIndex()); + processCascades(col, tracksThisCol, cascadesThisCol); + auto kinksThisCol = kinks.sliceBy(preslices.perColRecoKinks, col.globalIndex()); + processKinks(col, tracksThisCol, kinksThisCol); + // V0PhotonsKF/V0Legs carry a plain CollisionId column (not a framework index column), so they + // are not auto-grouped by the DPL binder and must be sliced manually, same as EMPhotonFilter.cxx + auto photonsThisCol = photons.sliceBy(preslices.perColRecoPCMPhotons, col.globalIndex()); + processPhotons(col, photonsThisCol, v0legs); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksV0sCascadesKinksPhotonsRun3pp, "Provide tracks, v0s, cascades, kinks and PCM photons", false); + + void processTracksD0sRun3pp(rawinputs::Run3PpCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, rawinputs::Run3D0s const& candidates) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto candidatesThisCol = candidates.sliceBy(preslices.perColD0s, col.globalIndex()); + processD0s(col, tracksThisCol, candidatesThisCol); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); - processD0s(col, tracks, candidates); } PROCESS_SWITCH(FemtoProducer, processTracksD0sRun3pp, "Provide tracks and D0s", false); - void processTracksD0sRun3PbPb(rawinputs::Run3PbPbCollisions::iterator const& col, + void processTracksD0sRun3PbPb(rawinputs::Run3PbPbCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, rawinputs::Run3D0s const& candidates) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto candidatesThisCol = candidates.sliceBy(preslices.perColD0s, col.globalIndex()); + processD0s(col, tracksThisCol, candidatesThisCol); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); - processD0s(col, tracks, candidates); } PROCESS_SWITCH(FemtoProducer, processTracksD0sRun3PbPb, "Provide tracks and D0s in PbPb collisions", false); @@ -677,10 +965,10 @@ struct FemtoProducer { // does no auto-grouping. Tables that are iterated over (tracks, v0s, cascades, // kinks) are sliced per collision; the full track table is passed alongside so // the v0/cascade/kink builders can resolve their daughter indices. - // These functions own the per-timeframe reset of the mc builder maps (once, before the - // reco-collision loop, in both pass-through and lazy-fill mode); processMcCollisions never - // resets them itself, since it runs once per reco collision and the dedup maps have to - // survive across all reco collisions in the timeframe. + // These functions own the per-timeframe reset of the mc builder maps AND of trackBuilder + // (once, before the reco-collision loop, in both pass-through and lazy-fill mode); neither + // processMcCollisions nor processMcTracks resets them, since they run once per reco collision + // and the dedup maps have to survive across all reco collisions in the timeframe. // ========================================================================== void processTracksRun3ppMc(rawinputs::Run3PpMcGenCollisions const& mcCols, @@ -689,13 +977,14 @@ struct FemtoProducer { rawinputs::Run3McRecoTracks const& tracks, rawinputs::Run3McGenParticles const& mcParticles) { - mcBuilder.reset(mcCols, mcParticles); - if (mcBuilder.isPassThrough()) { - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } for (const auto& col : cols) { - auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } @@ -712,13 +1001,14 @@ struct FemtoProducer { rawinputs::Run3McRecoTracks const& tracks, rawinputs::Run3McGenParticles const& mcParticles) { - mcBuilder.reset(mcCols, mcParticles); - if (mcBuilder.isPassThrough()) { - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } for (const auto& col : cols) { - auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } @@ -736,13 +1026,14 @@ struct FemtoProducer { rawinputs::Run3RecoVzeros const& v0s, rawinputs::Run3McGenParticles const& mcParticles) { - mcBuilder.reset(mcCols, mcParticles); - if (mcBuilder.isPassThrough()) { - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } for (const auto& col : cols) { - auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } @@ -750,7 +1041,7 @@ struct FemtoProducer { o2::aod::pidits::ITSNSigmaPr, o2::aod::pidits::ITSNSigmaDe, o2::aod::pidits::ITSNSigmaTr, o2::aod::pidits::ITSNSigmaHe>(tracksThisCol); processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); - auto v0sThisCol = v0s.sliceBy(perColRecoV0s, col.globalIndex()); + auto v0sThisCol = v0s.sliceBy(preslices.perColRecoV0s, col.globalIndex()); processMcV0s(col, mcCols, tracks, v0sThisCol, mcParticles); } } @@ -763,13 +1054,14 @@ struct FemtoProducer { rawinputs::Run3RecoVzeros const& v0s, rawinputs::Run3McGenParticles const& mcParticles) { - mcBuilder.reset(mcCols, mcParticles); - if (mcBuilder.isPassThrough()) { - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } for (const auto& col : cols) { - auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } @@ -777,7 +1069,7 @@ struct FemtoProducer { o2::aod::pidits::ITSNSigmaPr, o2::aod::pidits::ITSNSigmaDe, o2::aod::pidits::ITSNSigmaTr, o2::aod::pidits::ITSNSigmaHe>(tracksThisCol); processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); - auto v0sThisCol = v0s.sliceBy(perColRecoV0s, col.globalIndex()); + auto v0sThisCol = v0s.sliceBy(preslices.perColRecoV0s, col.globalIndex()); processMcV0s(col, mcCols, tracks, v0sThisCol, mcParticles); } } @@ -791,13 +1083,14 @@ struct FemtoProducer { rawinputs::Run3RecoCascades const& cascades, rawinputs::Run3McGenParticles const& mcParticles) { - mcBuilder.reset(mcCols, mcParticles); - if (mcBuilder.isPassThrough()) { - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } for (const auto& col : cols) { - auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } @@ -805,10 +1098,10 @@ struct FemtoProducer { o2::aod::pidits::ITSNSigmaPr, o2::aod::pidits::ITSNSigmaDe, o2::aod::pidits::ITSNSigmaTr, o2::aod::pidits::ITSNSigmaHe>(tracksThisCol); processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); - auto v0sThisCol = v0s.sliceBy(perColRecoV0s, col.globalIndex()); + auto v0sThisCol = v0s.sliceBy(preslices.perColRecoV0s, col.globalIndex()); processMcV0s(col, mcCols, tracks, v0sThisCol, mcParticles); - auto cascadesThisCol = cascades.sliceBy(perColRecoCascades, col.globalIndex()); + auto cascadesThisCol = cascades.sliceBy(preslices.perColRecoCascades, col.globalIndex()); processMcCascades(col, mcCols, tracks, cascadesThisCol, mcParticles); } } @@ -822,13 +1115,14 @@ struct FemtoProducer { rawinputs::Run3RecoCascades const& cascades, rawinputs::Run3McGenParticles const& mcParticles) { - mcBuilder.reset(mcCols, mcParticles); - if (mcBuilder.isPassThrough()) { - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } for (const auto& col : cols) { - auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } @@ -836,10 +1130,10 @@ struct FemtoProducer { o2::aod::pidits::ITSNSigmaPr, o2::aod::pidits::ITSNSigmaDe, o2::aod::pidits::ITSNSigmaTr, o2::aod::pidits::ITSNSigmaHe>(tracksThisCol); processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); - auto v0sThisCol = v0s.sliceBy(perColRecoV0s, col.globalIndex()); + auto v0sThisCol = v0s.sliceBy(preslices.perColRecoV0s, col.globalIndex()); processMcV0s(col, mcCols, tracks, v0sThisCol, mcParticles); - auto cascadesThisCol = cascades.sliceBy(perColRecoCascades, col.globalIndex()); + auto cascadesThisCol = cascades.sliceBy(preslices.perColRecoCascades, col.globalIndex()); processMcCascades(col, mcCols, tracks, cascadesThisCol, mcParticles); } } @@ -852,13 +1146,14 @@ struct FemtoProducer { rawinputs::Run3Kinks const& kinks, rawinputs::Run3McGenParticles const& mcParticles) { - mcBuilder.reset(mcCols, mcParticles); - if (mcBuilder.isPassThrough()) { - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } for (const auto& col : cols) { - auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } @@ -866,12 +1161,71 @@ struct FemtoProducer { o2::aod::pidits::ITSNSigmaPr, o2::aod::pidits::ITSNSigmaDe, o2::aod::pidits::ITSNSigmaTr, o2::aod::pidits::ITSNSigmaHe>(tracksThisCol); processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); - auto kinksThisCol = kinks.sliceBy(perColRecoKinks, col.globalIndex()); + auto kinksThisCol = kinks.sliceBy(preslices.perColRecoKinks, col.globalIndex()); processMcKinks(col, mcCols, tracks, kinksThisCol, mcParticles); } } PROCESS_SWITCH(FemtoProducer, processTracksKinksRun3ppMc, "Provide reconstructed and generated tracks and kinks", false); + void processTracksSigmaPlusPcmsRun3ppMc(rawinputs::Run3PpMcGenCollisions const& mcCols, + rawinputs::Run3PpMcRecoCollisions const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3McRecoTracks const& tracks, + rawinputs::Run3RecoSigmaPlusPcms const& sigmaPlusPcms, + rawinputs::Run3McGenParticles const& mcParticles) + { + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); + } + + for (const auto& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); + if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); + + auto sigmaPlusPcmsThisCol = sigmaPlusPcms.sliceBy(preslices.perColRecoSigmaPlusPcms, col.globalIndex()); + processMcSigmaPlusPcms(col, mcCols, tracks, sigmaPlusPcmsThisCol, mcParticles); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksSigmaPlusPcmsRun3ppMc, "Provide reconstructed and generated tracks and sigma plus reconstructed with a PCM photon", false); + + void processTracksKinksSigmaPlusPcmsRun3ppMc(rawinputs::Run3PpMcGenCollisions const& mcCols, + rawinputs::Run3PpMcRecoCollisions const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3McRecoTracks const& tracks, + rawinputs::Run3Kinks const& kinks, + rawinputs::Run3RecoSigmaPlusPcms const& sigmaPlusPcms, + rawinputs::Run3McGenParticles const& mcParticles) + { + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); + } + + for (const auto& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); + if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); + + auto kinksThisCol = kinks.sliceBy(preslices.perColRecoKinks, col.globalIndex()); + processMcKinks(col, mcCols, tracks, kinksThisCol, mcParticles); + auto sigmaPlusPcmsThisCol = sigmaPlusPcms.sliceBy(preslices.perColRecoSigmaPlusPcms, col.globalIndex()); + processMcSigmaPlusPcms(col, mcCols, tracks, sigmaPlusPcmsThisCol, mcParticles); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksKinksSigmaPlusPcmsRun3ppMc, "Provide reconstructed and generated tracks, kinks and sigma plus reconstructed with a PCM photon", false); + void processTracksV0sKinksRun3ppMc(rawinputs::Run3PpMcGenCollisions const& mcCols, rawinputs::Run3PpMcRecoCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, @@ -880,13 +1234,14 @@ struct FemtoProducer { rawinputs::Run3Kinks const& kinks, rawinputs::Run3McGenParticles const& mcParticles) { - mcBuilder.reset(mcCols, mcParticles); - if (mcBuilder.isPassThrough()) { - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } for (const auto& col : cols) { - auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } @@ -894,10 +1249,10 @@ struct FemtoProducer { o2::aod::pidits::ITSNSigmaPr, o2::aod::pidits::ITSNSigmaDe, o2::aod::pidits::ITSNSigmaTr, o2::aod::pidits::ITSNSigmaHe>(tracksThisCol); processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); - auto v0sThisCol = v0s.sliceBy(perColRecoV0s, col.globalIndex()); + auto v0sThisCol = v0s.sliceBy(preslices.perColRecoV0s, col.globalIndex()); processMcV0s(col, mcCols, tracks, v0sThisCol, mcParticles); - auto kinksThisCol = kinks.sliceBy(perColRecoKinks, col.globalIndex()); + auto kinksThisCol = kinks.sliceBy(preslices.perColRecoKinks, col.globalIndex()); processMcKinks(col, mcCols, tracks, kinksThisCol, mcParticles); } } @@ -910,13 +1265,14 @@ struct FemtoProducer { rawinputs::Run3RecoD0s const& d0s, rawinputs::Run3McGenParticles const& mcParticles) { - mcBuilder.reset(mcCols, mcParticles); - if (mcBuilder.isPassThrough()) { - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } for (const auto& col : cols) { - auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } @@ -924,7 +1280,7 @@ struct FemtoProducer { o2::aod::pidits::ITSNSigmaPr, o2::aod::pidits::ITSNSigmaDe, o2::aod::pidits::ITSNSigmaTr, o2::aod::pidits::ITSNSigmaHe>(tracksThisCol); processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); - auto d0sThisCol = d0s.sliceBy(perColRecoD0s, col.globalIndex()); + auto d0sThisCol = d0s.sliceBy(preslices.perColRecoD0s, col.globalIndex()); processMcD0s(col, mcCols, tracks, d0sThisCol, mcParticles); } } @@ -937,13 +1293,14 @@ struct FemtoProducer { rawinputs::Run3RecoD0s const& d0s, rawinputs::Run3McGenParticles const& mcParticles) { - mcBuilder.reset(mcCols, mcParticles); - if (mcBuilder.isPassThrough()) { - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } for (const auto& col : cols) { - auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } @@ -951,39 +1308,49 @@ struct FemtoProducer { o2::aod::pidits::ITSNSigmaPr, o2::aod::pidits::ITSNSigmaDe, o2::aod::pidits::ITSNSigmaTr, o2::aod::pidits::ITSNSigmaHe>(tracksThisCol); processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); - auto d0sThisCol = d0s.sliceBy(perColRecoD0s, col.globalIndex()); + auto d0sThisCol = d0s.sliceBy(preslices.perColRecoD0s, col.globalIndex()); processMcD0s(col, mcCols, tracks, d0sThisCol, mcParticles); } } PROCESS_SWITCH(FemtoProducer, processTracksD0sRun3PbPbMc, "Provide reconstructed and generated tracks and D0s in PbPb collisions", false); - void processTracksLcsRun3pp(rawinputs::Run3PpCollisions::iterator const& col, + void processTracksLcsRun3pp(rawinputs::Run3PpCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, rawinputs::Run3Lcs const& candidates) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto candidatesThisCol = candidates.sliceBy(preslices.perColLcs, col.globalIndex()); + processLcs(col, tracksThisCol, candidatesThisCol); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); - processLcs(col, tracks, candidates); } PROCESS_SWITCH(FemtoProducer, processTracksLcsRun3pp, "Provide tracks and Lcs", false); - void processTracksLcsRun3PbPb(rawinputs::Run3PbPbCollisions::iterator const& col, + void processTracksLcsRun3PbPb(rawinputs::Run3PbPbCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, rawinputs::Run3Lcs const& candidates) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto candidatesThisCol = candidates.sliceBy(preslices.perColLcs, col.globalIndex()); + processLcs(col, tracksThisCol, candidatesThisCol); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); - processLcs(col, tracks, candidates); } PROCESS_SWITCH(FemtoProducer, processTracksLcsRun3PbPb, "Provide tracks and Lcs in PbPb collisions", false); @@ -994,13 +1361,14 @@ struct FemtoProducer { rawinputs::Run3RecoLcs const& lcs, rawinputs::Run3McGenParticles const& mcParticles) { - mcBuilder.reset(mcCols, mcParticles); - if (mcBuilder.isPassThrough()) { - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } for (const auto& col : cols) { - auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } @@ -1008,7 +1376,7 @@ struct FemtoProducer { o2::aod::pidits::ITSNSigmaPr, o2::aod::pidits::ITSNSigmaDe, o2::aod::pidits::ITSNSigmaTr, o2::aod::pidits::ITSNSigmaHe>(tracksThisCol); processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); - auto lcsThisCol = lcs.sliceBy(perColRecoLcs, col.globalIndex()); + auto lcsThisCol = lcs.sliceBy(preslices.perColRecoLcs, col.globalIndex()); processMcLcs(col, mcCols, tracks, lcsThisCol, mcParticles); } } @@ -1021,13 +1389,14 @@ struct FemtoProducer { rawinputs::Run3RecoLcs const& lcs, rawinputs::Run3McGenParticles const& mcParticles) { - mcBuilder.reset(mcCols, mcParticles); - if (mcBuilder.isPassThrough()) { - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } for (const auto& col : cols) { - auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } @@ -1035,7 +1404,7 @@ struct FemtoProducer { o2::aod::pidits::ITSNSigmaPr, o2::aod::pidits::ITSNSigmaDe, o2::aod::pidits::ITSNSigmaTr, o2::aod::pidits::ITSNSigmaHe>(tracksThisCol); processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); - auto lcsThisCol = lcs.sliceBy(perColRecoLcs, col.globalIndex()); + auto lcsThisCol = lcs.sliceBy(preslices.perColRecoLcs, col.globalIndex()); processMcLcs(col, mcCols, tracks, lcsThisCol, mcParticles); } } @@ -1048,12 +1417,12 @@ struct FemtoProducer { // do not preslice the mcParticles table passed to fillMcParticle, otherwise the finding of the partonic mother can fail void processMcOnly(o2::aod::McCollisions const& mcCols, o2::aod::McParticles const& mcParticles) { - mcBuilder.reset(mcCols, mcParticles); + builders.mcBuilder.reset(mcCols, mcParticles); for (const auto& mcCol : mcCols) { - auto particlesThisCollision = mcParticles.sliceBy(perMcCollision, mcCol.globalIndex()); - mcBuilder.fillMcCollision(mcCol, particlesThisCollision, mcProducts, pdgDb); + auto particlesThisCollision = mcParticles.sliceBy(preslices.perMcCollision, mcCol.globalIndex()); + builders.mcBuilder.fillMcCollision(mcCol, particlesThisCollision, mcProducts, pdgDb); for (const auto& mcParticle : particlesThisCollision) { - mcBuilder.fillMcParticle(mcParticle, mcParticles, mcCol, mcProducts); + builders.mcBuilder.fillMcParticle(mcParticle, mcParticles, mcCol, mcProducts); } } } diff --git a/PWGCF/Femto/TableProducer/femtoProducerCollisionTagger.cxx b/PWGCF/Femto/TableProducer/femtoProducerCollisionTagger.cxx new file mode 100644 index 00000000000..788a8588cd9 --- /dev/null +++ b/PWGCF/Femto/TableProducer/femtoProducerCollisionTagger.cxx @@ -0,0 +1,782 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoProducerCollisionTagger.cxx +/// \brief Self-configuring task that tags femto collisions with a bit per (pair/triplet task +/// instance, pair type) found in the same workflow, set if the collision has what it +/// takes to form at least one pair of that type: both the particle-1-role and +/// particle-2-role candidate present (or, for a same-species pair, at least 2 distinct +/// candidates passing the one shared selection). Used to pre-filter the mixed-event pool +/// instead of discovering empty collisions only after slicing. Supports any number of +/// same-type "subwagons" (the same task run several times with different cuts in one +/// train) -- bits are assigned in discovery order. +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#include "PWGCF/Femto/Core/collisionTagger.h" +#include "PWGCF/Femto/Core/dataTypes.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace o2::analysis::femto; + +struct FemtoProducerCollisionTagger { + o2::framework::Produces producedTags; + + // Process functions below take a single collision ITERATOR (not the whole table) as their + // first argument, with the track/v0 tables as later arguments -- the same auto-grouping-by- + // fColId mechanism every processSameEvent() in this codebase already relies on, so tracks/ + // lambdas/k0shorts arrive pre-sliced to that one collision with no manual Preslice/SliceCache/ + // sliceBy() needed here at all. + using FemtoCollisions = o2::soa::Join; + using FemtoCollision = FemtoCollisions::iterator; + using FemtoTracks = o2::soa::Join; + using FemtoLambdas = o2::soa::Join; + using FemtoK0shorts = o2::soa::Join; + using FemtoXis = o2::soa::Join; + using FemtoOmegas = o2::soa::Join; + using FemtoSigmas = o2::soa::Join; + using FemtoSigmaPlus = o2::soa::Join; + using FemtoSigmaPlusPcm = o2::soa::Join; + using FemtoD0s = o2::soa::Join; + using FemtoLcs = o2::soa::Join; + using FemtoPhis = o2::soa::Join; + using FemtoKstar0s = o2::soa::Join; + using FemtoRho0s = o2::soa::Join; + using FemtoPi0s = o2::soa::Join; + using FemtoEtas = o2::soa::Join; + + // --- femto-pair-track-track: one bit per instance, "a track-track pair is possible here" --- + std::vector mTrackTrackVariants; + + // --- femto-pair-track-v0: one bit per instance per enabled v0 species --- + std::vector mTrackLambdaVariants; + std::vector mTrackK0shortVariants; + + // --- femto-pair-track-cascade: one bit per instance per enabled cascade species --- + std::vector mTrackXiVariants; + std::vector mTrackOmegaVariants; + + // --- femto-pair-track-kink: one bit per instance per enabled kink species --- + std::vector mTrackSigmaVariants; + std::vector mTrackSigmaPlusVariants; + std::vector mTrackSigmaPlusPcmVariants; + + // --- femto-pair-track-charm-hadron: one bit per instance per enabled charm hadron species --- + std::vector mTrackD0Variants; + std::vector mTrackLcVariants; + + // --- femto-pair-v0-v0: one bit per instance per enabled v0-v0 combination --- + std::vector mLambdaLambdaVariants; + std::vector mK0shortK0shortVariants; + std::vector mLambdaK0shortVariants; + + // --- femto-pair-d0-d0: one bit per instance --- + std::vector mD0D0Variants; + + // --- femto-pair-track-two-track-resonance: one bit per instance per enabled species --- + std::vector mTrackPhiVariants; + std::vector mTrackKstar0Variants; + std::vector mTrackRho0Variants; + + // --- femto-pair-track-two-photon-resonance: one bit per instance per enabled species --- + std::vector mTrackPi0Variants; + std::vector mTrackEtaVariants; + + // --- femto-pair-v0-two-track-resonance: one bit per instance per enabled combination --- + std::vector mLambdaPhiVariants; + std::vector mLambdaKstar0Variants; + std::vector mLambdaRho0Variants; + std::vector mK0shortPhiVariants; + std::vector mK0shortKstar0Variants; + std::vector mK0shortRho0Variants; + + // --- femto-triplet-track-track-track: one bit per instance --- + std::vector mTrackTrackTrackVariants; + + // --- femto-triplet-track-track-v0: one bit per instance --- + std::vector mTrackTrackLambdaVariants; + + // --- femto-triplet-track-track-cascade: one bit per instance per enabled cascade species --- + std::vector mTrackTrackXiVariants; + std::vector mTrackTrackOmegaVariants; + + void init(o2::framework::InitContext& context) + { + // FColTags gets exactly one row written per collision; if more than one process function were + // enabled, each would try to write its own row for the same collision. Until FColTags is + // extended to carry one column per wagon type, only one process function may run at a time -- + // run separate tagger instances (with separate output files/merges) for separate wagon types. + int activeProcesses = static_cast(doprocessTrackTrack) + static_cast(doprocessTrackLambda) + static_cast(doprocessTrackK0short) + static_cast(doprocessTrackXi) + static_cast(doprocessTrackOmega) + static_cast(doprocessTrackSigma) + static_cast(doprocessTrackSigmaPlus) + static_cast(doprocessTrackSigmaPlusPcm) + static_cast(doprocessTrackD0) + static_cast(doprocessTrackLc) + static_cast(doprocessLambdaLambda) + static_cast(doprocessK0shortK0short) + static_cast(doprocessLambdaK0short) + static_cast(doprocessD0D0) + static_cast(doprocessTrackTwoTrackResonance) + static_cast(doprocessTrackTwoPhotonResonance) + static_cast(doprocessLambdaTwoTrackResonance) + static_cast(doprocessK0shortTwoTrackResonance) + static_cast(doprocessTripletTrackTrackTrack) + static_cast(doprocessTripletTrackTrackV0) + static_cast(doprocessTripletTrackTrackXi) + static_cast(doprocessTripletTrackTrackOmega); + if (activeProcesses != 1) { + LOG(fatal) << "Exactly one process function must be enabled on the collision tagger, found " << activeProcesses << ". Breaking..."; + } + + LOG(info) << "*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*"; + LOG(info) << " Collision tagger self-configuration "; + LOG(info) << "*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*"; + + // discoverTaggedRoles() is the single canonical bit-assignment walk, shared with every + // consuming pair/triplet task (see Core/collisionTagger.h) -- it assigns bits to every + // recognized task instance found in the workflow, regardless of which of THIS tagger + // instance's own process functions is enabled, so that bit numbers stay globally consistent + // even when multiple tagger instances (one per wagon type) run side by side. + auto& workflows = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + + auto findDevice = [&workflows](std::string const& name) -> o2::framework::DeviceSpec const& { + for (auto const& device : workflows.devices) { + if (device.name == name) { + return device; + } + } + LOG(fatal) << "Device " << name << " vanished between discovery and lookup. Breaking..."; + return workflows.devices.front(); // unreachable, silences -Wreturn-type + }; + + for (auto const& r : roles) { + LOG(info) << " bit " << r.bit << " = " << r.deviceName << " " << r.role; + if (doprocessTrackTrack && r.role == static_cast(collisiontagger::role::TrackTrackPair) && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackTrack) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + collisiontagger::TrackTrackVariant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sameSpecies = collisiontagger::hasEnabledOption(device, "Mixing.sameSpecies"); + // When sameSpecies is on, the real pair task's own dispatcher (PairTrackTrackBuilder:: + // processMixedEvent in pairBuilder.h) uses partition1 for BOTH roles and never consults + // TrackSelection2 at all -- mirror that here instead of reading TrackSelection2 off the + // device, since the user is only expected to configure TrackSelection1 in that mode and + // TrackSelection2 may be left at an unrelated default. + v.sel2 = v.sameSpecies ? v.sel1 : collisiontagger::readTrackSelection(device, "TrackSelection2"); + v.bit = r.bit; + mTrackTrackVariants.push_back(v); + } else if (doprocessTrackLambda && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackV0) != std::string::npos && r.role == static_cast(collisiontagger::role::TrackLambdaPair)) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + collisiontagger::TrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "LambdaSelection1"); + v.bit = r.bit; + mTrackLambdaVariants.push_back(v); + } else if (doprocessTrackK0short && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackV0) != std::string::npos && r.role == static_cast(collisiontagger::role::TrackK0shortPair)) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + collisiontagger::TrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "K0shortSelection1"); + v.bit = r.bit; + mTrackK0shortVariants.push_back(v); + } else if (doprocessTrackXi && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackCascade) != std::string::npos && r.role == static_cast(collisiontagger::role::TrackXiPair)) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + collisiontagger::TrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "XiSelection"); + v.bit = r.bit; + mTrackXiVariants.push_back(v); + } else if (doprocessTrackOmega && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackCascade) != std::string::npos && r.role == static_cast(collisiontagger::role::TrackOmegaPair)) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + collisiontagger::TrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "OmegaSelection"); + v.bit = r.bit; + mTrackOmegaVariants.push_back(v); + } else if (doprocessTrackSigma && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackKink) != std::string::npos && r.role == static_cast(collisiontagger::role::TrackSigmaPair)) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + collisiontagger::TrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "SigmaSelection1"); + v.bit = r.bit; + mTrackSigmaVariants.push_back(v); + } else if (doprocessTrackSigmaPlus && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackKink) != std::string::npos && r.role == static_cast(collisiontagger::role::TrackSigmaPlusPair)) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + collisiontagger::TrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "SigmaPlusSelection1"); + v.bit = r.bit; + mTrackSigmaPlusVariants.push_back(v); + } else if (doprocessTrackSigmaPlusPcm && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackKink) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + if (r.role == static_cast(collisiontagger::role::TrackSigmaPlusPcmPair)) { + collisiontagger::TrackKinkPcmVariant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readKinkPcmSelection(device, "SigmaPlusPcmSelection1"); + v.bit = r.bit; + mTrackSigmaPlusPcmVariants.push_back(v); + } + } else if ((doprocessTrackD0 || doprocessTrackLc) && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackCharmHadron) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + if (r.role == static_cast(collisiontagger::role::TrackD0Pair)) { + collisiontagger::TrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "D0Selection1"); + v.bit = r.bit; + mTrackD0Variants.push_back(v); + } else if (r.role == static_cast(collisiontagger::role::TrackLcPair)) { + collisiontagger::TrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "LcSelection1"); + v.bit = r.bit; + mTrackLcVariants.push_back(v); + } + } else if (doprocessLambdaLambda && r.deviceName.find(collisiontagger::devicename::FemtoPairV0V0) != std::string::npos && r.role == static_cast(collisiontagger::role::LambdaLambdaPair)) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + collisiontagger::V0V0Variant v; + v.sel1 = collisiontagger::readV0Selection(device, "LambdaSelection1"); + v.sameSpecies = collisiontagger::hasEnabledOption(device, "Mixing.sameSpecies"); + // Mirror PairV0V0Builder::processMixedEvent(), which uses partition1 for BOTH roles and + // never consults LambdaSelection2 at all when sameSpecies -- same override as + // TrackTrackPair above. + v.sel2 = v.sameSpecies ? v.sel1 : collisiontagger::readV0Selection(device, "LambdaSelection2"); + v.bit = r.bit; + mLambdaLambdaVariants.push_back(v); + } else if (doprocessK0shortK0short && r.deviceName.find(collisiontagger::devicename::FemtoPairV0V0) != std::string::npos && r.role == static_cast(collisiontagger::role::K0shortK0shortPair)) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + collisiontagger::V0V0Variant v; + v.sel1 = collisiontagger::readV0Selection(device, "K0shortSelection1"); + v.sameSpecies = collisiontagger::hasEnabledOption(device, "Mixing.sameSpecies"); + v.sel2 = v.sameSpecies ? v.sel1 : collisiontagger::readV0Selection(device, "K0shortSelection2"); + v.bit = r.bit; + mK0shortK0shortVariants.push_back(v); + } else if (doprocessLambdaK0short && r.deviceName.find(collisiontagger::devicename::FemtoPairV0V0) != std::string::npos && r.role == static_cast(collisiontagger::role::LambdaK0shortPair)) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + collisiontagger::V0V0Variant v; + v.sel1 = collisiontagger::readV0Selection(device, "LambdaSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "K0shortSelection2"); + v.bit = r.bit; + mLambdaK0shortVariants.push_back(v); + } else if (doprocessD0D0 && r.deviceName.find(collisiontagger::devicename::FemtoPairD0D0) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + collisiontagger::V0V0Variant v; + v.sel1 = collisiontagger::readV0Selection(device, "D0Selection1"); + collisiontagger::V0SelectionValues sel2Raw = collisiontagger::readV0Selection(device, "D0Selection2"); + bool confSameSpecies = collisiontagger::hasEnabledOption(device, "Mixing.sameSpecies"); + // PairD0D0Builder::init() forces sameSpecies off when the two signs disagree (see + // pairBuilder.h) -- mirror that override here too, otherwise a sign-flip-only d0-d0bar + // config would be mis-tagged as a same-species pair. + v.sameSpecies = confSameSpecies && (v.sel1.sign == sel2Raw.sign); + v.sel2 = v.sameSpecies ? v.sel1 : sel2Raw; + v.bit = r.bit; + mD0D0Variants.push_back(v); + } else if (doprocessTrackTwoTrackResonance && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackTwoTrackResonance) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + if (r.role == static_cast(collisiontagger::role::TrackPhiPair)) { + collisiontagger::TrackTwoTrackResonanceVariant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readTwoTrackResonanceSelection(device, "PhiSelection"); + v.bit = r.bit; + mTrackPhiVariants.push_back(v); + } else if (r.role == static_cast(collisiontagger::role::TrackKstar0Pair)) { + collisiontagger::TrackKstar0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readKstar0Selection(device, "Kstar0Selection"); + v.bit = r.bit; + mTrackKstar0Variants.push_back(v); + } else if (r.role == static_cast(collisiontagger::role::TrackRho0Pair)) { + collisiontagger::TrackTwoTrackResonanceVariant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readTwoTrackResonanceSelection(device, "Rho0Selection"); + v.bit = r.bit; + mTrackRho0Variants.push_back(v); + } + } else if (doprocessTrackTwoPhotonResonance && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackTwoPhotonResonance) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + if (r.role == static_cast(collisiontagger::role::TrackPi0Pair)) { + collisiontagger::TrackTwoPhotonResonanceVariant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readTwoPhotonResonanceSelection(device, "Pi0Selection1"); + v.bit = r.bit; + mTrackPi0Variants.push_back(v); + } else if (r.role == static_cast(collisiontagger::role::TrackEtaPair)) { + collisiontagger::TrackTwoPhotonResonanceVariant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readTwoPhotonResonanceSelection(device, "EtaSelection1"); + v.bit = r.bit; + mTrackEtaVariants.push_back(v); + } + } else if (doprocessLambdaTwoTrackResonance && r.deviceName.find(collisiontagger::devicename::FemtoPairV0TwoTrackResonance) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + if (r.role == static_cast(collisiontagger::role::LambdaPhiPair)) { + collisiontagger::V0TwoTrackResonanceVariant v; + v.sel1 = collisiontagger::readV0Selection(device, "LambdaSelection1"); + v.sel2 = collisiontagger::readTwoTrackResonanceSelection(device, "PhiSelection"); + v.bit = r.bit; + mLambdaPhiVariants.push_back(v); + } else if (r.role == static_cast(collisiontagger::role::LambdaKstar0Pair)) { + collisiontagger::V0Kstar0Variant v; + v.sel1 = collisiontagger::readV0Selection(device, "LambdaSelection1"); + v.sel2 = collisiontagger::readKstar0Selection(device, "Kstar0Selection"); + v.bit = r.bit; + mLambdaKstar0Variants.push_back(v); + } else if (r.role == static_cast(collisiontagger::role::LambdaRho0Pair)) { + collisiontagger::V0TwoTrackResonanceVariant v; + v.sel1 = collisiontagger::readV0Selection(device, "LambdaSelection1"); + v.sel2 = collisiontagger::readTwoTrackResonanceSelection(device, "Rho0Selection"); + v.bit = r.bit; + mLambdaRho0Variants.push_back(v); + } + } else if (doprocessK0shortTwoTrackResonance && r.deviceName.find(collisiontagger::devicename::FemtoPairV0TwoTrackResonance) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + if (r.role == static_cast(collisiontagger::role::K0shortPhiPair)) { + collisiontagger::V0TwoTrackResonanceVariant v; + v.sel1 = collisiontagger::readV0Selection(device, "K0shortSelection1"); + v.sel2 = collisiontagger::readTwoTrackResonanceSelection(device, "PhiSelection"); + v.bit = r.bit; + mK0shortPhiVariants.push_back(v); + } else if (r.role == static_cast(collisiontagger::role::K0shortKstar0Pair)) { + collisiontagger::V0Kstar0Variant v; + v.sel1 = collisiontagger::readV0Selection(device, "K0shortSelection1"); + v.sel2 = collisiontagger::readKstar0Selection(device, "Kstar0Selection"); + v.bit = r.bit; + mK0shortKstar0Variants.push_back(v); + } else if (r.role == static_cast(collisiontagger::role::K0shortRho0Pair)) { + collisiontagger::V0TwoTrackResonanceVariant v; + v.sel1 = collisiontagger::readV0Selection(device, "K0shortSelection1"); + v.sel2 = collisiontagger::readTwoTrackResonanceSelection(device, "Rho0Selection"); + v.bit = r.bit; + mK0shortRho0Variants.push_back(v); + } + } else if (doprocessTripletTrackTrackTrack && r.deviceName.find(collisiontagger::devicename::FemtoTripletTrackTrackTrack) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + collisiontagger::TrackTrackTrackVariant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + collisiontagger::TrackSelectionValues sel2Raw = collisiontagger::readTrackSelection(device, "TrackSelection2"); + collisiontagger::TrackSelectionValues sel3Raw = collisiontagger::readTrackSelection(device, "TrackSelection3"); + v.same123 = collisiontagger::hasEnabledOption(device, "Mixing.particle123AreSameSpecies"); + v.same12 = collisiontagger::hasEnabledOption(device, "Mixing.particle12AreSameSpecies"); + // Mirror TripletTrackTrackTrackBuilder::processMixedEvent(), which ignores + // TrackSelection2/3 entirely when same123 (uses partition1 for all three roles), and + // ignores TrackSelection2 (but not 3) when same12 -- same override trick as + // TrackTrackVariant's sameSpecies above. + v.sel2 = (v.same123 || v.same12) ? v.sel1 : sel2Raw; + v.sel3 = v.same123 ? v.sel1 : sel3Raw; + v.bit = r.bit; + mTrackTrackTrackVariants.push_back(v); + } else if (doprocessTripletTrackTrackV0 && r.deviceName.find(collisiontagger::devicename::FemtoTripletTrackTrackV0) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + collisiontagger::TrackTrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + collisiontagger::TrackSelectionValues sel2Raw = collisiontagger::readTrackSelection(device, "TrackSelection2"); + v.same12 = collisiontagger::hasEnabledOption(device, "Mixing.particle12AreSameSpecies"); + v.sel2 = v.same12 ? v.sel1 : sel2Raw; + v.sel3 = collisiontagger::readV0Selection(device, "LambdaSelection1"); + v.bit = r.bit; + mTrackTrackLambdaVariants.push_back(v); + } else if (doprocessTripletTrackTrackXi && r.deviceName.find(collisiontagger::devicename::FemtoTripletTrackTrackCascade) != std::string::npos && r.role == static_cast(collisiontagger::role::TrackTrackXiTriplet)) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + collisiontagger::TrackSelectionValues sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + collisiontagger::TrackSelectionValues sel2Raw = collisiontagger::readTrackSelection(device, "TrackSelection2"); + bool same12 = collisiontagger::hasEnabledOption(device, "Mixing.particle12AreSameSpecies"); + collisiontagger::TrackTrackV0Variant v; + v.sel1 = sel1; + v.sel2 = same12 ? sel1 : sel2Raw; + v.same12 = same12; + v.sel3 = collisiontagger::readV0Selection(device, "XiSelection"); + v.bit = r.bit; + mTrackTrackXiVariants.push_back(v); + } else if (doprocessTripletTrackTrackOmega && r.deviceName.find(collisiontagger::devicename::FemtoTripletTrackTrackCascade) != std::string::npos && r.role == static_cast(collisiontagger::role::TrackTrackOmegaTriplet)) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + collisiontagger::TrackSelectionValues sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + collisiontagger::TrackSelectionValues sel2Raw = collisiontagger::readTrackSelection(device, "TrackSelection2"); + bool same12 = collisiontagger::hasEnabledOption(device, "Mixing.particle12AreSameSpecies"); + collisiontagger::TrackTrackV0Variant v; + v.sel1 = sel1; + v.sel2 = same12 ? sel1 : sel2Raw; + v.same12 = same12; + v.sel3 = collisiontagger::readV0Selection(device, "OmegaSelection"); + v.bit = r.bit; + mTrackTrackOmegaVariants.push_back(v); + } + // TODO: add the remaining triplet task types (mc-particle triplets, ...), following the + // same pattern. + } + + if (doprocessTrackTrack && mTrackTrackVariants.empty()) { + LOG(fatal) << "processTrackTrack is enabled, but no femto-pair-track-track device was found in this workflow."; + } + if (doprocessTrackLambda && mTrackLambdaVariants.empty()) { + LOG(fatal) << "processTrackLambda is enabled, but no femto-pair-track-v0 device (with processLambdaMixedEventWithTagging " + << "enabled) was found in this workflow."; + } + if (doprocessTrackK0short && mTrackK0shortVariants.empty()) { + LOG(fatal) << "processTrackK0short is enabled, but no femto-pair-track-v0 device (with processK0shortMixedEventWithTagging " + << "enabled) was found in this workflow."; + } + if (doprocessTrackXi && mTrackXiVariants.empty()) { + LOG(fatal) << "processTrackXi is enabled, but no femto-pair-track-cascade device (with processXiMixedEventWithTagging " + << "enabled) was found in this workflow."; + } + if (doprocessTrackOmega && mTrackOmegaVariants.empty()) { + LOG(fatal) << "processTrackOmega is enabled, but no femto-pair-track-cascade device (with processOmegaMixedEventWithTagging " + << "enabled) was found in this workflow."; + } + if (doprocessTrackSigma && mTrackSigmaVariants.empty()) { + LOG(fatal) << "processTrackSigma is enabled, but no femto-pair-track-kink device (with processSigmaMixedEventWithTagging " + << "enabled) was found in this workflow."; + } + if (doprocessTrackSigmaPlus && mTrackSigmaPlusVariants.empty()) { + LOG(fatal) << "processTrackSigmaPlus is enabled, but no femto-pair-track-kink device (with processSigmaPlusMixedEventWithTagging " + << "enabled) was found in this workflow."; + } + if (doprocessTrackSigmaPlusPcm && mTrackSigmaPlusPcmVariants.empty()) { + LOG(fatal) << "processTrackSigmaPlusPcm is enabled, but no femto-pair-track-kink device (with " + << "processSigmaPlusPcmMixedEventWithTagging enabled) was found in this workflow."; + } + if (doprocessTrackD0 && mTrackD0Variants.empty()) { + LOG(fatal) << "processTrackD0 is enabled, but no femto-pair-track-charm-hadron device (with processD0MixedEventWithTagging " + << "enabled) was found in this workflow."; + } + if (doprocessTrackLc && mTrackLcVariants.empty()) { + LOG(fatal) << "processTrackLc is enabled, but no femto-pair-track-charm-hadron device (with processLcMixedEventWithTagging " + << "enabled) was found in this workflow."; + } + if (doprocessLambdaLambda && mLambdaLambdaVariants.empty()) { + LOG(fatal) << "processLambdaLambda is enabled, but no femto-pair-v0-v0 device (with processLambdaLambdaMixedEventWithTagging " + << "enabled) was found in this workflow."; + } + if (doprocessK0shortK0short && mK0shortK0shortVariants.empty()) { + LOG(fatal) << "processK0shortK0short is enabled, but no femto-pair-v0-v0 device (with processK0shortK0shortMixedEventWithTagging " + << "enabled) was found in this workflow."; + } + if (doprocessLambdaK0short && mLambdaK0shortVariants.empty()) { + LOG(fatal) << "processLambdaK0short is enabled, but no femto-pair-v0-v0 device (with processLambdaK0shortMixedEventWithTagging " + << "enabled) was found in this workflow."; + } + if (doprocessD0D0 && mD0D0Variants.empty()) { + LOG(fatal) << "processD0D0 is enabled, but no femto-pair-d0-d0 device (with processMixedEventWithTagging enabled) was found in this workflow."; + } + if (doprocessTrackTwoTrackResonance && mTrackPhiVariants.empty() && mTrackKstar0Variants.empty() && mTrackRho0Variants.empty()) { + LOG(fatal) << "processTrackTwoTrackResonance is enabled, but no femto-pair-track-two-track-resonance device (with " + << "processPhiMixedEventWithTagging/processKstar0MixedEventWithTagging/processRho0MixedEventWithTagging enabled) " + << "was found in this workflow."; + } + if (doprocessTrackTwoPhotonResonance && mTrackPi0Variants.empty() && mTrackEtaVariants.empty()) { + LOG(fatal) << "processTrackTwoPhotonResonance is enabled, but no femto-pair-track-two-photon-resonance device (with " + << "processPi0MixedEventWithTagging/processEtaMixedEventWithTagging enabled) was found in this workflow."; + } + if (doprocessLambdaTwoTrackResonance && mLambdaPhiVariants.empty() && mLambdaKstar0Variants.empty() && mLambdaRho0Variants.empty()) { + LOG(fatal) << "processLambdaTwoTrackResonance is enabled, but no femto-pair-v0-two-track-resonance device (with one of " + << "processLambdaPhiMixedEventWithTagging/processLambdaKstar0MixedEventWithTagging/" + << "processLambdaRho0MixedEventWithTagging enabled) was found in this workflow."; + } + if (doprocessK0shortTwoTrackResonance && mK0shortPhiVariants.empty() && mK0shortKstar0Variants.empty() && mK0shortRho0Variants.empty()) { + LOG(fatal) << "processK0shortTwoTrackResonance is enabled, but no femto-pair-v0-two-track-resonance device (with one of " + << "processK0shortPhiMixedEventWithTagging/processK0shortKstar0MixedEventWithTagging/" + << "processK0shortRho0MixedEventWithTagging enabled) was found in this workflow."; + } + if (doprocessTripletTrackTrackTrack && mTrackTrackTrackVariants.empty()) { + LOG(fatal) << "processTripletTrackTrackTrack is enabled, but no femto-triplet-track-track-track device (with " + << "processMixedEventWithTagging enabled) was found in this workflow."; + } + if (doprocessTripletTrackTrackV0 && mTrackTrackLambdaVariants.empty()) { + LOG(fatal) << "processTripletTrackTrackV0 is enabled, but no femto-triplet-track-track-v0 device (with " + << "processMixedEventWithTagging enabled) was found in this workflow."; + } + if (doprocessTripletTrackTrackXi && mTrackTrackXiVariants.empty()) { + LOG(fatal) << "processTripletTrackTrackXi is enabled, but no femto-triplet-track-track-cascade device (with " + << "processXiMixedEventWithTagging enabled) was found in this workflow."; + } + if (doprocessTripletTrackTrackOmega && mTrackTrackOmegaVariants.empty()) { + LOG(fatal) << "processTripletTrackTrackOmega is enabled, but no femto-triplet-track-track-cascade device (with " + << "processOmegaMixedEventWithTagging enabled) was found in this workflow."; + } + if (!roles.empty() && roles.back().bit >= collisiontagger::MaxTagBits) { + LOG(fatal) << "Too many tag bits requested (" << (roles.back().bit + 1) << "); collision tagger supports up to " << collisiontagger::MaxTagBits << "."; + } + } + + // `tracksThisCol` arrives already sliced to `col` (see FemtoCollision comment above) -- the + // entire per-collision tagging step is one call to tagAndProduce(), which builds the tag + // bitset, runs the same-type-pair check (both track roles' selections, or 2 distinct entries + // for a same-species pair) against every variant, and produces the row. + void processTrackTrack(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol) + { + collisiontagger::tagAndProduce(producedTags, mTrackTrackVariants, tracksThisCol, + [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackTrack, "Tag collisions for femto-pair-track-track wagon(s)", false); + + // Lambda and K0short are split into two separate process functions (rather than one combined + // processTrackV0 reading both FemtoLambdas and FemtoK0shorts unconditionally) because the + // consuming femto-pair-track-v0 task itself enforces that a single instance only ever tags + // Lambda OR K0short, never both (see its init(): "Both lambda-track and k0short-track + // processing is enabled" is fatal there) -- so requiring both tables as DPL inputs here would + // make the tagger unusable against any input file that only has one of the two V0 species + // derived (e.g. Lambda-only productions with no FK0shorts table at all: DPL subscribes to a + // process function's full argument list regardless of whether the branch using it is actually + // reached at runtime, so the AOD reader fails outright looking for a tree that was never + // produced, instead of this cleanly tagging zero K0short pairs). + void processTrackLambda(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoLambdas const& lambdasThisCol) + { + collisiontagger::CollisionTagBitset tag; + auto trackPassesFn = [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }; + collisiontagger::tagCrossVariants(tag, mTrackLambdaVariants, tracksThisCol, trackPassesFn, lambdasThisCol, + [](auto const& lambda, auto const& sel) { return collisiontagger::signedV0Passes(lambda, sel); }); + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackLambda, "Tag collisions for femto-pair-track-v0 wagon(s) (Lambda)", false); + + void processTrackK0short(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoK0shorts const& k0shortsThisCol) + { + collisiontagger::CollisionTagBitset tag; + auto trackPassesFn = [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }; + collisiontagger::tagCrossVariants(tag, mTrackK0shortVariants, tracksThisCol, trackPassesFn, k0shortsThisCol, + [](auto const& k0short, auto const& sel) { return collisiontagger::k0shortPasses(k0short, sel); }); + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackK0short, "Tag collisions for femto-pair-track-v0 wagon(s) (K0short)", false); + + // Xi and Omega are split into separate process functions (rather than one combined + // processTrackCascade reading both FemtoXis and FemtoOmegas unconditionally): same reasoning as + // processTrackLambda/processTrackK0short above -- femtoProducerDerivedToDerived.cxx's + // processTracksOmegas has no corresponding "always comes with Xi" guarantee (Xi and Omega are + // each their own independently-switchable, default-off producer process function), so a + // Xi-only (or Omega-only) derived file would otherwise make the AOD reader fail looking for a + // tree that was never produced. + void processTrackXi(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoXis const& xisThisCol) + { + collisiontagger::CollisionTagBitset tag; + collisiontagger::tagCrossVariants(tag, mTrackXiVariants, tracksThisCol, [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }, xisThisCol, [](auto const& cascade, auto const& sel) { return collisiontagger::signedV0Passes(cascade, sel); }); + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackXi, "Tag collisions for femto-pair-track-cascade wagon(s) (Xi)", false); + + void processTrackOmega(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoOmegas const& omegasThisCol) + { + collisiontagger::CollisionTagBitset tag; + collisiontagger::tagCrossVariants(tag, mTrackOmegaVariants, tracksThisCol, [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }, omegasThisCol, [](auto const& cascade, auto const& sel) { return collisiontagger::signedV0Passes(cascade, sel); }); + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackOmega, "Tag collisions for femto-pair-track-cascade wagon(s) (Omega)", false); + + // Sigma and SigmaPlus are likewise split: femtoProducerDerivedToDerived.cxx's processTracksSigma + // and processTracksSigmaPlus are each their own independently-switchable, default-off process + // function, not bundled -- same risk as Xi/Omega and Lambda/K0short above. SigmaPlusPcm already + // had its own process function below (comes from a genuinely separate, mutually exclusive + // producer process function), unaffected by this split. + void processTrackSigma(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoSigmas const& sigmasThisCol) + { + collisiontagger::CollisionTagBitset tag; + collisiontagger::tagCrossVariants(tag, mTrackSigmaVariants, tracksThisCol, [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }, sigmasThisCol, [](auto const& sigma, auto const& sel) { return collisiontagger::signedV0Passes(sigma, sel); }); + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackSigma, "Tag collisions for femto-pair-track-kink wagon(s) (Sigma)", false); + + void processTrackSigmaPlus(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoSigmaPlus const& sigmaPlusThisCol) + { + collisiontagger::CollisionTagBitset tag; + collisiontagger::tagCrossVariants(tag, mTrackSigmaPlusVariants, tracksThisCol, [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }, sigmaPlusThisCol, [](auto const& sigmaPlus, auto const& sel) { return collisiontagger::signedV0Passes(sigmaPlus, sel); }); + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackSigmaPlus, "Tag collisions for femto-pair-track-kink wagon(s) (SigmaPlus)", false); + + void processTrackSigmaPlusPcm(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoSigmaPlusPcm const& sigmaPlusPcmThisCol) + { + collisiontagger::CollisionTagBitset tag; + collisiontagger::tagCrossVariants(tag, mTrackSigmaPlusPcmVariants, tracksThisCol, [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }, sigmaPlusPcmThisCol, [](auto const& sigmaPlusPcm, auto const& sel) { return collisiontagger::kinkPcmPasses(sigmaPlusPcm, sel); }); + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackSigmaPlusPcm, "Tag collisions for femto-pair-track-kink wagon(s) (SigmaPlusPcm)", false); + + // D0 and Lc are also mutually exclusive on the producer side (separate process functions, counted + // together in its own "exactly one process enabled" check), so each gets its own process function + // here too, for the same reason as SigmaPlusPcm above. + void processTrackD0(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoD0s const& d0sThisCol) + { + collisiontagger::CollisionTagBitset tag; + collisiontagger::tagCrossVariants(tag, mTrackD0Variants, tracksThisCol, [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }, d0sThisCol, [](auto const& d0, auto const& sel) { return collisiontagger::signedV0Passes(d0, sel); }); + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackD0, "Tag collisions for femto-pair-track-charm-hadron wagon(s) (D0)", false); + + void processTrackLc(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoLcs const& lcsThisCol) + { + collisiontagger::CollisionTagBitset tag; + collisiontagger::tagCrossVariants(tag, mTrackLcVariants, tracksThisCol, [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }, lcsThisCol, [](auto const& lc, auto const& sel) { return collisiontagger::signedV0Passes(lc, sel); }); + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackLc, "Tag collisions for femto-pair-track-charm-hadron wagon(s) (Lc)", false); + + // No track table involved here at all -- v0-v0 is checked purely between the two v0 tables + // themselves (lambda/k0short), same as D0D0 below is checked purely within FemtoD0s. + // Split by which table(s) are actually needed: LambdaLambda only needs FemtoLambdas, + // K0shortK0short only needs FemtoK0shorts -- same reasoning as the other splits above + // (femtoProducerDerivedToDerived.cxx's processTracksLambdas/processTracksK0shorts are each + // independently switchable, default off). LambdaK0short is a genuine cross-species pair, so it + // legitimately needs both tables and stays as-is. + void processLambdaLambda(FemtoCollision const& /*col*/, FemtoLambdas const& lambdasThisCol) + { + collisiontagger::CollisionTagBitset tag; + collisiontagger::tagPairVariants(tag, mLambdaLambdaVariants, lambdasThisCol, + [](auto const& v0, auto const& sel) { return collisiontagger::signedV0Passes(v0, sel); }); + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processLambdaLambda, "Tag collisions for femto-pair-v0-v0 wagon(s) (Lambda-Lambda)", false); + + void processK0shortK0short(FemtoCollision const& /*col*/, FemtoK0shorts const& k0shortsThisCol) + { + collisiontagger::CollisionTagBitset tag; + collisiontagger::tagPairVariants(tag, mK0shortK0shortVariants, k0shortsThisCol, + [](auto const& v0, auto const& sel) { return collisiontagger::k0shortPasses(v0, sel); }); + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processK0shortK0short, "Tag collisions for femto-pair-v0-v0 wagon(s) (K0short-K0short)", false); + + void processLambdaK0short(FemtoCollision const& /*col*/, FemtoLambdas const& lambdasThisCol, FemtoK0shorts const& k0shortsThisCol) + { + collisiontagger::CollisionTagBitset tag; + collisiontagger::tagCrossVariants(tag, mLambdaK0shortVariants, lambdasThisCol, [](auto const& v0, auto const& sel) { return collisiontagger::signedV0Passes(v0, sel); }, k0shortsThisCol, [](auto const& v0, auto const& sel) { return collisiontagger::k0shortPasses(v0, sel); }); + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processLambdaK0short, "Tag collisions for femto-pair-v0-v0 wagon(s) (Lambda-K0short)", false); + + void processD0D0(FemtoCollision const& /*col*/, FemtoD0s const& d0sThisCol) + { + collisiontagger::tagAndProduce(producedTags, mD0D0Variants, d0sThisCol, + [](auto const& d0, auto const& sel) { return collisiontagger::signedV0Passes(d0, sel); }); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processD0D0, "Tag collisions for femto-pair-d0-d0 wagon(s)", false); + + // femto-producer-resonances has no mutual-exclusivity gate between Phi/Kstar0/Rho0 (all three + // default to enabled, built from the same track pool with different PID hypotheses), so they can + // safely be combined into one process function here too. + void processTrackTwoTrackResonance(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoPhis const& phisThisCol, FemtoKstar0s const& kstar0sThisCol, FemtoRho0s const& rho0sThisCol) + { + collisiontagger::CollisionTagBitset tag; + auto trackPassesFn = [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }; + if (!mTrackPhiVariants.empty()) { + collisiontagger::tagCrossVariants(tag, mTrackPhiVariants, tracksThisCol, trackPassesFn, phisThisCol, + [](auto const& phi, auto const& sel) { return collisiontagger::twoTrackResonancePasses(phi, sel); }); + } + if (!mTrackKstar0Variants.empty()) { + collisiontagger::tagCrossVariants(tag, mTrackKstar0Variants, tracksThisCol, trackPassesFn, kstar0sThisCol, + [](auto const& kstar0, auto const& sel) { return collisiontagger::kstar0Passes(kstar0, sel); }); + } + if (!mTrackRho0Variants.empty()) { + collisiontagger::tagCrossVariants(tag, mTrackRho0Variants, tracksThisCol, trackPassesFn, rho0sThisCol, + [](auto const& rho0, auto const& sel) { return collisiontagger::twoTrackResonancePasses(rho0, sel); }); + } + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackTwoTrackResonance, "Tag collisions for femto-pair-track-two-track-resonance wagon(s)", false); + + // femto-producer-two-photon-resonances also has no mutual-exclusivity gate between Pi0/Eta. + void processTrackTwoPhotonResonance(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoPi0s const& pi0sThisCol, FemtoEtas const& etasThisCol) + { + collisiontagger::CollisionTagBitset tag; + auto trackPassesFn = [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }; + if (!mTrackPi0Variants.empty()) { + collisiontagger::tagCrossVariants(tag, mTrackPi0Variants, tracksThisCol, trackPassesFn, pi0sThisCol, + [](auto const& pi0, auto const& sel) { return collisiontagger::twoPhotonResonancePasses(pi0, sel); }); + } + if (!mTrackEtaVariants.empty()) { + collisiontagger::tagCrossVariants(tag, mTrackEtaVariants, tracksThisCol, trackPassesFn, etasThisCol, + [](auto const& eta, auto const& sel) { return collisiontagger::twoPhotonResonancePasses(eta, sel); }); + } + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackTwoPhotonResonance, "Tag collisions for femto-pair-track-two-photon-resonance wagon(s)", false); + + // No track table involved here at all -- checked purely between the v0 tables (lambda/k0short) + // and the resonance tables (phi/kstar0/rho0) themselves. Split by V0 species (Lambda vs + // K0short) for the same reason as processLambdaLambda/processK0shortK0short above -- each + // needs only its own V0 table. Phi/Kstar0/Rho0 stay combined within each (that axis is safe: + // femtoProducerResonances.cxx builds all three by default with no mutual-exclusivity gate, see + // processTrackTwoTrackResonance below). + void processLambdaTwoTrackResonance(FemtoCollision const& /*col*/, FemtoLambdas const& lambdasThisCol, FemtoPhis const& phisThisCol, FemtoKstar0s const& kstar0sThisCol, FemtoRho0s const& rho0sThisCol) + { + collisiontagger::CollisionTagBitset tag; + auto signedV0PassesFn = [](auto const& lambda, auto const& sel) { return collisiontagger::signedV0Passes(lambda, sel); }; + if (!mLambdaPhiVariants.empty()) { + collisiontagger::tagCrossVariants(tag, mLambdaPhiVariants, lambdasThisCol, signedV0PassesFn, phisThisCol, + [](auto const& phi, auto const& sel) { return collisiontagger::twoTrackResonancePasses(phi, sel); }); + } + if (!mLambdaKstar0Variants.empty()) { + collisiontagger::tagCrossVariants(tag, mLambdaKstar0Variants, lambdasThisCol, signedV0PassesFn, kstar0sThisCol, + [](auto const& kstar0, auto const& sel) { return collisiontagger::kstar0Passes(kstar0, sel); }); + } + if (!mLambdaRho0Variants.empty()) { + collisiontagger::tagCrossVariants(tag, mLambdaRho0Variants, lambdasThisCol, signedV0PassesFn, rho0sThisCol, + [](auto const& rho0, auto const& sel) { return collisiontagger::twoTrackResonancePasses(rho0, sel); }); + } + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processLambdaTwoTrackResonance, "Tag collisions for femto-pair-v0-two-track-resonance wagon(s) (Lambda)", false); + + void processK0shortTwoTrackResonance(FemtoCollision const& /*col*/, FemtoK0shorts const& k0shortsThisCol, FemtoPhis const& phisThisCol, FemtoKstar0s const& kstar0sThisCol, FemtoRho0s const& rho0sThisCol) + { + collisiontagger::CollisionTagBitset tag; + auto k0shortPassesFn = [](auto const& k0short, auto const& sel) { return collisiontagger::k0shortPasses(k0short, sel); }; + if (!mK0shortPhiVariants.empty()) { + collisiontagger::tagCrossVariants(tag, mK0shortPhiVariants, k0shortsThisCol, k0shortPassesFn, phisThisCol, + [](auto const& phi, auto const& sel) { return collisiontagger::twoTrackResonancePasses(phi, sel); }); + } + if (!mK0shortKstar0Variants.empty()) { + collisiontagger::tagCrossVariants(tag, mK0shortKstar0Variants, k0shortsThisCol, k0shortPassesFn, kstar0sThisCol, + [](auto const& kstar0, auto const& sel) { return collisiontagger::kstar0Passes(kstar0, sel); }); + } + if (!mK0shortRho0Variants.empty()) { + collisiontagger::tagCrossVariants(tag, mK0shortRho0Variants, k0shortsThisCol, k0shortPassesFn, rho0sThisCol, + [](auto const& rho0, auto const& sel) { return collisiontagger::twoTrackResonancePasses(rho0, sel); }); + } + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processK0shortTwoTrackResonance, "Tag collisions for femto-pair-v0-two-track-resonance wagon(s) (K0short)", false); + + void processTripletTrackTrackTrack(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol) + { + collisiontagger::CollisionTagBitset tag; + collisiontagger::tagTripletVariants(tag, mTrackTrackTrackVariants, tracksThisCol, + [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }); + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTripletTrackTrackTrack, "Tag collisions for femto-triplet-track-track-track wagon(s)", false); + + void processTripletTrackTrackV0(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoLambdas const& lambdasThisCol) + { + collisiontagger::CollisionTagBitset tag; + collisiontagger::tagCrossTripletVariants(tag, mTrackTrackLambdaVariants, tracksThisCol, [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }, lambdasThisCol, [](auto const& lambda, auto const& sel) { return collisiontagger::signedV0Passes(lambda, sel); }); + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTripletTrackTrackV0, "Tag collisions for femto-triplet-track-track-v0 wagon(s)", false); + + // Split by Xi/Omega for the same reason as processTrackXi/processTrackOmega above. + void processTripletTrackTrackXi(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoXis const& xisThisCol) + { + collisiontagger::CollisionTagBitset tag; + collisiontagger::tagCrossTripletVariants(tag, mTrackTrackXiVariants, tracksThisCol, [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }, xisThisCol, [](auto const& cascade, auto const& sel) { return collisiontagger::signedV0Passes(cascade, sel); }); + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTripletTrackTrackXi, "Tag collisions for femto-triplet-track-track-cascade wagon(s) (Xi)", false); + + void processTripletTrackTrackOmega(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoOmegas const& omegasThisCol) + { + collisiontagger::CollisionTagBitset tag; + collisiontagger::tagCrossTripletVariants(tag, mTrackTrackOmegaVariants, tracksThisCol, [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }, omegasThisCol, [](auto const& cascade, auto const& sel) { return collisiontagger::signedV0Passes(cascade, sel); }); + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTripletTrackTrackOmega, "Tag collisions for femto-triplet-track-track-cascade wagon(s) (Omega)", false); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{ + o2::framework::adaptAnalysisTask(context), + }; + return workflow; +} diff --git a/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx b/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx index 0168d308901..c7c560e0f8e 100644 --- a/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx @@ -19,6 +19,7 @@ #include "PWGCF/Femto/Core/mcBuilder.h" #include "PWGCF/Femto/Core/partitions.h" #include "PWGCF/Femto/Core/trackBuilder.h" +#include "PWGCF/Femto/Core/twoTrackResonanceBuilder.h" #include "PWGCF/Femto/Core/v0Builder.h" #include "PWGCF/Femto/DataModel/FemtoTables.h" @@ -32,267 +33,699 @@ using namespace o2::analysis::femto; -struct FemtoProducerDerivedToDerived { - - // setup tables - using FemtoCollisions = o2::soa::Join; - - using FilteredFemtoCollisions = o2::soa::Filtered; - using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; - - using FemtoMcCollisions = o2::soa::Join; - using FilteredFemtoMcCollisions = o2::soa::Filtered; - - using FemtoTracks = o2::soa::Join; - using FemtoTracksWithMass = o2::soa::Join; - using FemtoLambdas = o2::soa::Join; - using FemtoK0shorts = o2::soa::Join; - using FemtoXis = o2::soa::Join; - using FemtoOmegas = o2::soa::Join; - using FemtoSigma = o2::soa::Join; - using FemtoSigmaPlus = o2::soa::Join; +// kept outside the task struct on purpose: the struct is reflected via a brace-decomposition +// mechanism capped at 100 direct data members (see o2::framework::PresliceGroup for the same +// concern applied to Preslice members below); type aliases don't need to live inside the struct +// at all, so moving them here keeps that budget free for actual data members. +namespace femtotables +{ +using FemtoCollisions = o2::soa::Join; + +using FilteredFemtoCollisions = o2::soa::Filtered; +using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; + +using FemtoMcCollisions = o2::soa::Join; +using FilteredFemtoMcCollisions = o2::soa::Filtered; + +using FemtoTracks = o2::soa::Join; +using FemtoTracksWithMass = o2::soa::Join; +// opt-in: carries the packed purity tables and FTrackDcas through to the re-written output -- +// needs the upstream producer's TrackTables.produceItsPurity/produceTpcPurity/produceTofPurity/produceTrackDcas enabled +using FemtoTracksWithFitVars = o2::soa::Join; +using FemtoLambdas = o2::soa::Join; +using FemtoK0shorts = o2::soa::Join; +using FemtoRho0s = o2::soa::Join; +using FemtoPhis = o2::soa::Join; +using FemtoXis = o2::soa::Join; +using FemtoOmegas = o2::soa::Join; +using FemtoSigma = o2::soa::Join; +using FemtoSigmaPlus = o2::soa::Join; +using FemtoSigmaPlusExtended = o2::soa::Join; +using FemtoSigmaPlusPcm = o2::soa::Join; +using FemtoSigmaPlusPcmExtended = o2::soa::Join; + +using FemtoMcTracks = o2::soa::Join; +using FemtoMcSigma = o2::soa::Join; +using FemtoMcSigmaPlus = o2::soa::Join; +using FemtoMcSigmaPlusExtended = o2::soa::Join; +using FemtoMcSigmaPlusPcm = o2::soa::Join; +using FemtoMcSigmaPlusPcmExtended = o2::soa::Join; +using FemtoMcParticles = o2::soa::Join; +} // namespace femtotables - using FemtoMcTracks = o2::soa::Join; - using FemtoMcParticles = o2::soa::Join; +struct FemtoProducerDerivedToDerived { o2::framework::SliceCache cache; + // all fColId Preslice declarations, grouped into a single data member + struct : public o2::framework::PresliceGroup { + o2::framework::Preslice perColTracks = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColTracksWithMass = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColTracksWithFitVars = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColLambdas = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColK0shorts = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColRho0s = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColPhis = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColSigma = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColSigmaPlus = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColSigmaPlusExtended = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColSigmaPlusPcm = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColSigmaPlusPcmExtended = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColXis = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColOmegas = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColMcTracks = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColMcSigma = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColMcSigmaPlus = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColMcSigmaPlusExtended = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColMcSigmaPlusPcm = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColMcSigmaPlusPcmExtended = o2::aod::femtobase::stored::fColId; + } preslices; + + // plain builder helper objects, grouped into a single data member (not Configurable/Partition/ + // Preslice/Produces, so the framework never needs to discover them individually -- free to nest) + struct { + collisionbuilder::CollisionBuilderDerivedToDerived collisionBuilder; + trackbuilder::TrackBuilderDerivedToDerived trackBuilder; + v0builder::V0BuilderDerivedToDerived v0Builder; + twotrackresonancebuilder::TwoTrackResonanceBuilderDerivedToDerived resonanceBuilder; + kinkbuilder::KinkBuilderDerivedToDerived kinkBuilder; + cascadebuilder::CascadeBuilderDerivedToDerived cascadeBuilder; + mcbuilder::McBuilderDerivedToDerived mcBuilder; + } builders; + // collision builder collisionbuilder::ConfCollisionSelection collisionSelection; o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); collisionbuilder::CollisionBuilderDerivedToDerivedProducts collisionBuilderProducts; - collisionbuilder::CollisionBuilderDerivedToDerived collisionBuilder; // track builder - trackbuilder::TrackBuilderDerivedToDerived trackBuilder; trackbuilder::TrackBuilderDerivedToDerivedProducts trackBuilderProducts; trackbuilder::ConfTrackTablesDerivedToDerived confTrackBuilder; trackbuilder::ConfTrackSelection1 trackSelections1; trackbuilder::ConfTrackSelection2 trackSelections2; - o2::framework::Partition trackPartition1 = MAKE_TRACK_PARTITION(trackSelections1); - o2::framework::Partition trackPartition2 = MAKE_TRACK_PARTITION(trackSelections2); - o2::framework::Preslice perColTracks = o2::aod::femtobase::stored::fColId; + o2::framework::Partition trackPartition1 = MAKE_TRACK_PARTITION(trackSelections1); + o2::framework::Partition trackPartition2 = MAKE_TRACK_PARTITION(trackSelections2); + + o2::framework::Partition trackWithMassPartition1 = MAKE_TRACK_PARTITION_WITH_MASS(trackSelections1); + o2::framework::Partition trackWithMassPartition2 = MAKE_TRACK_PARTITION_WITH_MASS(trackSelections2); - o2::framework::Partition trackWithMassPartition1 = MAKE_TRACK_PARTITION_WITH_MASS(trackSelections1); - o2::framework::Partition trackWithMassPartition2 = MAKE_TRACK_PARTITION_WITH_MASS(trackSelections2); - o2::framework::Preslice perColTracksWithMass = o2::aod::femtobase::stored::fColId; + o2::framework::Partition trackWithFitVarsPartition1 = MAKE_TRACK_PARTITION(trackSelections1); + o2::framework::Partition trackWithFitVarsPartition2 = MAKE_TRACK_PARTITION(trackSelections2); // v0 builder - v0builder::V0BuilderDerivedToDerived v0Builder; v0builder::V0BuilderDerivedToDerivedProducts v0BuilderProducts; v0builder::ConfV0TablesDerivedToDerived confV0Builder; v0builder::ConfLambdaSelection1 lambdaSelection1; - o2::framework::Partition lambdaPartition = MAKE_LAMBDA_PARTITION(lambdaSelection1); - o2::framework::Preslice perColLambdas = o2::aod::femtobase::stored::fColId; + o2::framework::Partition lambdaPartition = MAKE_LAMBDA_PARTITION(lambdaSelection1); v0builder::ConfK0shortSelection1 k0shortSelection1; - o2::framework::Partition k0shortPartition = MAKE_K0SHORT_PARTITION(k0shortSelection1); - o2::framework::Preslice perColK0shorts = o2::aod::femtobase::stored::fColId; + o2::framework::Partition k0shortPartition = MAKE_K0SHORT_PARTITION(k0shortSelection1); + + // two-track resonance builder + twotrackresonancebuilder::TwoTrackResonanceBuilderDerivedToDerivedProducts resonanceBuilderProducts; + twotrackresonancebuilder::ConfTwoTrackResonanceTablesDerivedToDerived confResonanceBuilder; + + twotrackresonancebuilder::ConfRho0Selection rho0Selection; + o2::framework::Partition rho0Partition = MAKE_RESONANCE_0_PARTITON(rho0Selection); + + twotrackresonancebuilder::ConfPhiSelection phiSelection; + o2::framework::Partition phiPartition = MAKE_RESONANCE_0_PARTITON(phiSelection); // kink builder - kinkbuilder::KinkBuilderDerivedToDerived kinkBuilder; kinkbuilder::KinkBuilderDerivedToDerivedProducts kinkBuilderProducts; kinkbuilder::ConfKinkTablesDerivedToDerived confKinkBuilder; kinkbuilder::ConfSigmaSelection1 sigmaSelection1; - o2::framework::Partition sigmaPartition = MAKE_SIGMA_PARTITION(sigmaSelection1); - o2::framework::Preslice perColSigma = o2::aod::femtobase::stored::fColId; + o2::framework::Partition sigmaPartition = MAKE_SIGMA_PARTITION(sigmaSelection1); kinkbuilder::ConfSigmaPlusSelection1 sigmaPlusSelection1; - o2::framework::Partition sigmaPlusPartition = MAKE_SIGMAPLUS_PARTITION(sigmaPlusSelection1); - o2::framework::Preslice perColSigmaPlus = o2::aod::femtobase::stored::fColId; + o2::framework::Partition sigmaPlusPartition = MAKE_SIGMAPLUS_PARTITION(sigmaPlusSelection1); + o2::framework::Partition sigmaPlusExtendedPartition = MAKE_SIGMAPLUS_PARTITION(sigmaPlusSelection1); + + kinkbuilder::ConfSigmaPlusPcmSelection1 sigmaPlusPcmSelection1; + o2::framework::Partition sigmaPlusPcmPartition = MAKE_SIGMAPLUSPCM_PARTITION(sigmaPlusPcmSelection1); + o2::framework::Partition sigmaPlusPcmExtendedPartition = MAKE_SIGMAPLUSPCM_PARTITION(sigmaPlusPcmSelection1); // cascade builder - cascadebuilder::CascadeBuilderDerivedToDerived cascadeBuilder; cascadebuilder::CascadeBuilderDerivedToDerivedProducts cascadeBuilderProducts; cascadebuilder::ConfCascadeTablesDerivedToDerived confCascadeBuilder; cascadebuilder::ConfXiSelection xiSelection; - o2::framework::Partition xiPartition = MAKE_CASCADE_PARTITION(xiSelection); - o2::framework::Preslice perColXis = o2::aod::femtobase::stored::fColId; + o2::framework::Partition xiPartition = MAKE_CASCADE_PARTITION(xiSelection); cascadebuilder::ConfOmegaSelection omegaSelection; - o2::framework::Partition omegaPartition = MAKE_CASCADE_PARTITION(omegaSelection); - o2::framework::Preslice perColOmegas = o2::aod::femtobase::stored::fColId; + o2::framework::Partition omegaPartition = MAKE_CASCADE_PARTITION(omegaSelection); // mc builder - mcbuilder::McBuilderDerivedToDerived mcBuilder; mcbuilder::McBuilderDerivedToDerivedProducts mcBuilderProducts; mcbuilder::ConfMcTablesDerivedToDerived confMcBuilder; - o2::framework::Partition mcTrackPartition1 = MAKE_TRACK_PARTITION(trackSelections1); - o2::framework::Partition mcTrackPartition2 = MAKE_TRACK_PARTITION(trackSelections2); - o2::framework::Preslice perColMcTracks = o2::aod::femtobase::stored::fColId; + o2::framework::Partition mcTrackPartition1 = MAKE_TRACK_PARTITION(trackSelections1); + o2::framework::Partition mcTrackPartition2 = MAKE_TRACK_PARTITION(trackSelections2); + + o2::framework::Partition mcSigmaPartition = MAKE_SIGMA_PARTITION(sigmaSelection1); + + o2::framework::Partition mcSigmaPlusPartition = MAKE_SIGMAPLUS_PARTITION(sigmaPlusSelection1); + o2::framework::Partition mcSigmaPlusExtendedPartition = MAKE_SIGMAPLUS_PARTITION(sigmaPlusSelection1); + + o2::framework::Partition mcSigmaPlusPcmPartition = MAKE_SIGMAPLUSPCM_PARTITION(sigmaPlusPcmSelection1); + o2::framework::Partition mcSigmaPlusPcmExtendedPartition = MAKE_SIGMAPLUSPCM_PARTITION(sigmaPlusPcmSelection1); void init(o2::framework::InitContext& /*context*/) { const int activeProcesses = - static_cast(doprocessTracks) + static_cast(doprocessTracksWithMass) + - static_cast(doprocessTracksLambdas) + static_cast(doprocessTracksWithMassLambdas) + + static_cast(doprocessTracks) + static_cast(doprocessTracksWithMass) + static_cast(doprocessTracksWithFitVars) + + static_cast(doprocessTracksLambdas) + static_cast(doprocessTracksWithMassLambdas) + static_cast(doprocessTracksWithFitVarsLambdas) + static_cast(doprocessLambdas) + - static_cast(doprocessTracksXis) + static_cast(doprocessTracksOmegas) + - static_cast(doprocessTracksK0shorts) + static_cast(doprocessTracksSigma) + - static_cast(doprocessTracksSigmaPlus) + - static_cast(doprocessTracksMc); + static_cast(doprocessTracksXis) + static_cast(doprocessTracksWithFitVarsXis) + + static_cast(doprocessTracksOmegas) + static_cast(doprocessTracksWithFitVarsOmegas) + + static_cast(doprocessTracksK0shorts) + static_cast(doprocessTracksWithFitVarsK0shorts) + static_cast(doprocessTracksSigma) + + static_cast(doprocessTracksSigmaPlus) + static_cast(doprocessTracksSigmaPlusExtended) + + static_cast(doprocessTracksSigmaPlusPcm) + static_cast(doprocessTracksSigmaPlusPcmExtended) + + static_cast(doprocessTracksRho0) + static_cast(doprocessTracksPhi) + + static_cast(doprocessLambdasXis) + + static_cast(doprocessTracksMc) + static_cast(doprocessTracksSigmaMc) + + static_cast(doprocessTracksSigmaPlusMc) + static_cast(doprocessTracksSigmaPlusExtendedMc) + + static_cast(doprocessTracksSigmaPlusPcmMc) + static_cast(doprocessTracksSigmaPlusPcmExtendedMc); if (activeProcesses != 1) { LOG(fatal) << "Exactly one process function must be activated (got " << activeProcesses << ")."; } - trackBuilder.init(confTrackBuilder); - v0Builder.init(confV0Builder); - cascadeBuilder.init(confCascadeBuilder); - kinkBuilder.init(confKinkBuilder); - mcBuilder.init(confMcBuilder); + // which builders the currently active process function actually exercises -- + // only those need their limit configuration validated (see e.g. TrackBuilderDerivedToDerived::init()); + // an unused builder is free to keep its (possibly all-zero) default limits + const bool needsTrackBuilder = + doprocessTracks || doprocessTracksWithMass || doprocessTracksWithFitVars || + doprocessTracksLambdas || doprocessTracksWithMassLambdas || doprocessTracksWithFitVarsLambdas || + doprocessTracksXis || doprocessTracksWithFitVarsXis || + doprocessTracksOmegas || doprocessTracksWithFitVarsOmegas || + doprocessTracksK0shorts || doprocessTracksWithFitVarsK0shorts || + doprocessTracksSigma || doprocessTracksSigmaPlus || doprocessTracksSigmaPlusExtended || + doprocessTracksSigmaPlusPcm || doprocessTracksSigmaPlusPcmExtended || + doprocessTracksRho0 || doprocessTracksPhi || + doprocessTracksMc || doprocessTracksSigmaMc || doprocessTracksSigmaPlusMc || + doprocessTracksSigmaPlusExtendedMc || doprocessTracksSigmaPlusPcmMc || doprocessTracksSigmaPlusPcmExtendedMc; + + const bool needsV0Builder = + doprocessTracksLambdas || doprocessTracksWithMassLambdas || doprocessTracksWithFitVarsLambdas || + doprocessLambdas || + doprocessTracksK0shorts || doprocessTracksWithFitVarsK0shorts || + doprocessLambdasXis; + + const bool needsCascadeBuilder = + doprocessTracksXis || doprocessTracksWithFitVarsXis || + doprocessTracksOmegas || doprocessTracksWithFitVarsOmegas || + doprocessLambdasXis; + + const bool needsKinkBuilder = + doprocessTracksSigma || doprocessTracksSigmaPlus || doprocessTracksSigmaPlusExtended || + doprocessTracksSigmaPlusPcm || doprocessTracksSigmaPlusPcmExtended || + doprocessTracksSigmaMc || doprocessTracksSigmaPlusMc || doprocessTracksSigmaPlusExtendedMc || + doprocessTracksSigmaPlusPcmMc || doprocessTracksSigmaPlusPcmExtendedMc; + + const bool needsResonanceBuilder = doprocessTracksRho0 || doprocessTracksPhi; + + const bool needsMcBuilder = + doprocessTracksMc || doprocessTracksSigmaMc || doprocessTracksSigmaPlusMc || + doprocessTracksSigmaPlusExtendedMc || doprocessTracksSigmaPlusPcmMc || doprocessTracksSigmaPlusPcmExtendedMc; + + // skip init() entirely for builders the active process doesn't touch: their limit + // members default to 0 and are only ever read by the collisionHasTooFew*/process* + // methods that the active process also doesn't call, so leaving them uninitialized + // (i.e. at their 0 default) is harmless -- this also sidesteps each builder's "all + // limits are 0" fatal check, which would otherwise fire for an unrelated species + if (needsTrackBuilder) { + builders.trackBuilder.init(confTrackBuilder); + } + if (needsV0Builder) { + builders.v0Builder.init(confV0Builder); + } + if (needsCascadeBuilder) { + builders.cascadeBuilder.init(confCascadeBuilder); + } + if (needsKinkBuilder) { + builders.kinkBuilder.init(confKinkBuilder); + } + if (needsResonanceBuilder) { + builders.resonanceBuilder.init(confResonanceBuilder); + } + if (needsMcBuilder) { + builders.mcBuilder.init(confMcBuilder); + } } // process functions - void processTracks(FilteredFemtoCollision const& col, FemtoTracks const& tracks) + void processTracks(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks) { - if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); } - trackBuilder.reset(tracks); - collisionBuilder.processCollision(col, collisionBuilderProducts); - trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracks, "Process tracks", true); - void processTracksWithMass(FilteredFemtoCollision const& col, FemtoTracksWithMass const& tracks) + void processTracksWithMass(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracksWithMass const& tracks) { - if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithMassPartition1, trackWithMassPartition2, cache)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithMassPartition1, trackWithMassPartition2, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackWithMassPartition1, trackWithMassPartition2, cache, trackBuilderProducts, collisionBuilderProducts); } - trackBuilder.reset(tracks); - collisionBuilder.processCollision(col, collisionBuilderProducts); - trackBuilder.processTracks(col, tracks, trackWithMassPartition1, trackWithMassPartition2, cache, trackBuilderProducts, collisionBuilderProducts); } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithMass, "Process tracks with mass", false); - void processTracksLambdas(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoLambdas const& lambdas) + void processTracksWithFitVars(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracksWithFitVars const& tracks) { - if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithFitVars, "Process tracks, carrying through FitVars tables", false); + + void processTracksLambdas(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoLambdas const& lambdas) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.v0Builder.processLambdas(col, lambdas, tracks, lambdaPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); } - trackBuilder.reset(tracks); - collisionBuilder.processCollision(col, collisionBuilderProducts); - trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - v0Builder.processLambdas(col, lambdas, tracks, lambdaPartition, trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksLambdas, "Process lambdas and tracks", false); - void processTracksWithMassLambdas(FilteredFemtoCollision const& col, FemtoTracksWithMass const& tracks, FemtoLambdas const& lambdas) + void processTracksWithMassLambdas(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracksWithMass const& tracks, femtotables::FemtoLambdas const& lambdas) { - if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithMassPartition1, trackWithMassPartition2, cache) || v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithMassPartition1, trackWithMassPartition2, cache) || builders.v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackWithMassPartition1, trackWithMassPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.v0Builder.processLambdas(col, lambdas, tracks, lambdaPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); } - trackBuilder.reset(tracks); - collisionBuilder.processCollision(col, collisionBuilderProducts); - trackBuilder.processTracks(col, tracks, trackWithMassPartition1, trackWithMassPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - v0Builder.processLambdas(col, lambdas, tracks, lambdaPartition, trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithMassLambdas, "Process lambdas and tracks with mass", false); - void processLambdas(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoLambdas const& lambdas) + void processTracksWithFitVarsLambdas(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracksWithFitVars const& tracks, femtotables::FemtoLambdas const& lambdas) { - if (v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache) || builders.v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.v0Builder.processLambdas(col, lambdas, tracks, lambdaPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithFitVarsLambdas, "Process lambdas and tracks, carrying through FitVars tables", false); + + void processLambdas(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoLambdas const& lambdas) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.v0Builder.processLambdas(col, lambdas, tracks, lambdaPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); } - trackBuilder.reset(tracks); - collisionBuilder.processCollision(col, collisionBuilderProducts); - v0Builder.processLambdas(col, lambdas, tracks, lambdaPartition, trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processLambdas, "Process lambdas only (produced track table contains daughers only)", false); - void processTracksXis(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoXis const& xis) + void processTracksXis(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoXis const& xis) { - if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || cascadeBuilder.collisionHasTooFewXis(col, xis, xiPartition, cache)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.cascadeBuilder.collisionHasTooFewXis(col, xis, xiPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.cascadeBuilder.processXis(col, xis, tracks, xiPartition, builders.trackBuilder, cache, cascadeBuilderProducts, trackBuilderProducts, collisionBuilderProducts); } - trackBuilder.reset(tracks); - collisionBuilder.processCollision(col, collisionBuilderProducts); - trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - cascadeBuilder.processXis(col, xis, tracks, xiPartition, trackBuilder, cache, cascadeBuilderProducts, trackBuilderProducts, collisionBuilderProducts); } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksXis, "Process tracks and xis", false); - void processTracksOmegas(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoOmegas const& omegas) + void processTracksWithFitVarsXis(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracksWithFitVars const& tracks, femtotables::FemtoXis const& xis) { - if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || cascadeBuilder.collisionHasTooFewOmegas(col, omegas, omegaPartition, cache)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache) || builders.cascadeBuilder.collisionHasTooFewXis(col, xis, xiPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.cascadeBuilder.processXis(col, xis, tracks, xiPartition, builders.trackBuilder, cache, cascadeBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithFitVarsXis, "Process tracks and xis, carrying through FitVars tables", false); + + void processTracksOmegas(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoOmegas const& omegas) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.cascadeBuilder.collisionHasTooFewOmegas(col, omegas, omegaPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.cascadeBuilder.processOmegas(col, omegas, tracks, omegaPartition, builders.trackBuilder, cache, cascadeBuilderProducts, trackBuilderProducts, collisionBuilderProducts); } - trackBuilder.reset(tracks); - collisionBuilder.processCollision(col, collisionBuilderProducts); - trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - cascadeBuilder.processOmegas(col, omegas, tracks, omegaPartition, trackBuilder, cache, cascadeBuilderProducts, trackBuilderProducts, collisionBuilderProducts); } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksOmegas, "Process tracks and omegas", false); - void processTracksK0shorts(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoK0shorts const& k0shorts) + void processTracksWithFitVarsOmegas(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracksWithFitVars const& tracks, femtotables::FemtoOmegas const& omegas) { - if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || v0Builder.collisionHasTooFewK0shorts(col, k0shorts, k0shortPartition, cache)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache) || builders.cascadeBuilder.collisionHasTooFewOmegas(col, omegas, omegaPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.cascadeBuilder.processOmegas(col, omegas, tracks, omegaPartition, builders.trackBuilder, cache, cascadeBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithFitVarsOmegas, "Process tracks and omegas, carrying through FitVars tables", false); + + void processTracksK0shorts(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoK0shorts const& k0shorts) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.v0Builder.collisionHasTooFewK0shorts(col, k0shorts, k0shortPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.v0Builder.processK0shorts(col, k0shorts, tracks, k0shortPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); } - trackBuilder.reset(tracks); - collisionBuilder.processCollision(col, collisionBuilderProducts); - trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - v0Builder.processK0shorts(col, k0shorts, tracks, k0shortPartition, trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksK0shorts, "Process k0short and tracks", false); - void processTracksSigma(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoSigma const& sigma) + void processTracksWithFitVarsK0shorts(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracksWithFitVars const& tracks, femtotables::FemtoK0shorts const& k0shorts) { - if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || kinkBuilder.collisionHasTooFewSigma(col, sigma, sigmaPartition, cache)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache) || builders.v0Builder.collisionHasTooFewK0shorts(col, k0shorts, k0shortPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.v0Builder.processK0shorts(col, k0shorts, tracks, k0shortPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithFitVarsK0shorts, "Process k0short and tracks, carrying through FitVars tables", false); + + void processTracksSigma(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoSigma const& sigma) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigma(col, sigma, sigmaPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.kinkBuilder.processSigma(col, sigma, tracks, sigmaPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); } - trackBuilder.reset(tracks); - collisionBuilder.processCollision(col, collisionBuilderProducts); - trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - kinkBuilder.processSigma(col, sigma, tracks, sigmaPartition, trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigma, "Process sigma and tracks", false); - void processTracksSigmaPlus(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoSigmaPlus const& sigmaplus) + void processTracksSigmaPlus(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoSigmaPlus const& sigmaplus) { - if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || kinkBuilder.collisionHasTooFewSigmaPlus(col, sigmaplus, sigmaPlusPartition, cache)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlus(col, sigmaplus, sigmaPlusPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.kinkBuilder.processSigmaPlus(col, sigmaplus, tracks, sigmaPlusPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); } - trackBuilder.reset(tracks); - collisionBuilder.processCollision(col, collisionBuilderProducts); - trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - kinkBuilder.processSigmaPlus(col, sigmaplus, tracks, sigmaPlusPartition, trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlus, "Process sigmaPlus and tracks", false); + void processTracksSigmaPlusExtended(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoSigmaPlusExtended const& sigmaplus) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlus(col, sigmaplus, sigmaPlusExtendedPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.kinkBuilder.processSigmaPlus(col, sigmaplus, tracks, sigmaPlusExtendedPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlusExtended, "Process sigmaPlus (with SigmaPlusExtras) and tracks", false); + + void processTracksSigmaPlusPcm(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoSigmaPlusPcm const& sigmapluspcm) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlusPcm(col, sigmapluspcm, sigmaPlusPcmPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.kinkBuilder.processSigmaPlusPcm(col, sigmapluspcm, tracks, sigmaPlusPcmPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlusPcm, "Process sigmaPlusPcm (with SigmaPlusPcmMlScores, selected on the BDT score) and tracks", false); + + void processTracksSigmaPlusPcmExtended(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoSigmaPlusPcmExtended const& sigmapluspcm) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlusPcm(col, sigmapluspcm, sigmaPlusPcmExtendedPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.kinkBuilder.processSigmaPlusPcm(col, sigmapluspcm, tracks, sigmaPlusPcmExtendedPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlusPcmExtended, "Process sigmaPlusPcm (with SigmaPlusPcmMlScores and SigmaPlusPcmExtras, selected on the BDT score) and tracks", false); + + void processTracksRho0(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoRho0s const& rho0s) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.resonanceBuilder.collisionHasTooFewRho0s(col, rho0s, rho0Partition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.resonanceBuilder.processRho0s(col, rho0s, tracks, rho0Partition, builders.trackBuilder, cache, resonanceBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksRho0, "Process rho0 and tracks", false); + + void processTracksPhi(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoPhis const& phis) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.resonanceBuilder.collisionHasTooFewPhis(col, phis, phiPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.resonanceBuilder.processPhis(col, phis, tracks, phiPartition, builders.trackBuilder, cache, resonanceBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksPhi, "Process phi and tracks", false); + + void processLambdasXis(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoLambdas const& lambdas, femtotables::FemtoXis const& xis) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache) || builders.cascadeBuilder.collisionHasTooFewXis(col, xis, xiPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.v0Builder.processLambdas(col, lambdas, tracks, lambdaPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); + builders.cascadeBuilder.processXis(col, xis, tracks, xiPartition, builders.trackBuilder, cache, cascadeBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processLambdasXis, "Process lambdas and xis", false); + // mc processing - // // NOTE: unlike the other process functions, this one subscribes to the full - // collision table and iterates manually. The MC index maps are keyed by source - // global index and must live for the whole dataframe - void processTracksMc(FilteredFemtoMcCollisions const& cols, - FemtoMcTracks const& tracks, + void processTracksMc(femtotables::FilteredFemtoMcCollisions const& cols, + femtotables::FemtoMcTracks const& tracks, o2::aod::FMcCols const& mcCols, - FemtoMcParticles const& mcParticles, + femtotables::FemtoMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { // once per dataframe - mcBuilder.reset(mcCols, mcParticles); + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); for (auto const& col : cols) { - if (trackBuilder.collisionHasTooFewTracks(col, tracks, mcTrackPartition1, mcTrackPartition2, cache)) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, mcTrackPartition1, mcTrackPartition2, cache)) { continue; } - // track indexMap is per collision by construction, so it resets here - trackBuilder.reset(tracks); - collisionBuilder.processCollision(col, collisionBuilderProducts); - mcBuilder.fillCollisionWithLabel(col, mcCols, mcBuilderProducts); - trackBuilder.processTracksWithMc(col, tracks, mcTrackPartition1, mcTrackPartition2, cache, - trackBuilderProducts, collisionBuilderProducts, - mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.mcBuilder.fillCollisionWithLabel(col, mcCols, mcBuilderProducts); + builders.trackBuilder.processTracksWithMc(col, tracks, mcTrackPartition1, mcTrackPartition2, cache, + trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); } } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksMc, "Process tracks with MC information", false); + + void processTracksSigmaMc(femtotables::FilteredFemtoMcCollisions const& cols, + femtotables::FemtoMcTracks const& tracks, + femtotables::FemtoMcSigma const& sigma, + o2::aod::FMcCols const& mcCols, + femtotables::FemtoMcParticles const& mcParticles, + o2::aod::FMcMothers const& mcMothers, + o2::aod::FMcPartMoths const& mcPartonicMothers) + { + // once per dataframe + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, mcTrackPartition1, mcTrackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigma(col, sigma, mcSigmaPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.mcBuilder.fillCollisionWithLabel(col, mcCols, mcBuilderProducts); + builders.trackBuilder.processTracksWithMc(col, tracks, mcTrackPartition1, mcTrackPartition2, cache, + trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + builders.kinkBuilder.processSigmaWithMc(col, sigma, tracks, mcSigmaPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaMc, "Process sigma and tracks with MC information", false); + + void processTracksSigmaPlusMc(femtotables::FilteredFemtoMcCollisions const& cols, + femtotables::FemtoMcTracks const& tracks, + femtotables::FemtoMcSigmaPlus const& sigmaplus, + o2::aod::FMcCols const& mcCols, + femtotables::FemtoMcParticles const& mcParticles, + o2::aod::FMcMothers const& mcMothers, + o2::aod::FMcPartMoths const& mcPartonicMothers) + { + // once per dataframe + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, mcTrackPartition1, mcTrackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlus(col, sigmaplus, mcSigmaPlusPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.mcBuilder.fillCollisionWithLabel(col, mcCols, mcBuilderProducts); + builders.trackBuilder.processTracksWithMc(col, tracks, mcTrackPartition1, mcTrackPartition2, cache, + trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + builders.kinkBuilder.processSigmaPlusWithMc(col, sigmaplus, tracks, mcSigmaPlusPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlusMc, "Process sigmaPlus and tracks with MC information", false); + + void processTracksSigmaPlusExtendedMc(femtotables::FilteredFemtoMcCollisions const& cols, + femtotables::FemtoMcTracks const& tracks, + femtotables::FemtoMcSigmaPlusExtended const& sigmaplus, + o2::aod::FMcCols const& mcCols, + femtotables::FemtoMcParticles const& mcParticles, + o2::aod::FMcMothers const& mcMothers, + o2::aod::FMcPartMoths const& mcPartonicMothers) + { + // once per dataframe + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, mcTrackPartition1, mcTrackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlus(col, sigmaplus, mcSigmaPlusExtendedPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.mcBuilder.fillCollisionWithLabel(col, mcCols, mcBuilderProducts); + builders.trackBuilder.processTracksWithMc(col, tracks, mcTrackPartition1, mcTrackPartition2, cache, + trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + builders.kinkBuilder.processSigmaPlusWithMc(col, sigmaplus, tracks, mcSigmaPlusExtendedPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlusExtendedMc, "Process sigmaPlus (with SigmaPlusExtras) and tracks with MC information", false); + + void processTracksSigmaPlusPcmMc(femtotables::FilteredFemtoMcCollisions const& cols, + femtotables::FemtoMcTracks const& tracks, + femtotables::FemtoMcSigmaPlusPcm const& sigmapluspcm, + o2::aod::FMcCols const& mcCols, + femtotables::FemtoMcParticles const& mcParticles, + o2::aod::FMcMothers const& mcMothers, + o2::aod::FMcPartMoths const& mcPartonicMothers) + { + // once per dataframe + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, mcTrackPartition1, mcTrackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlusPcm(col, sigmapluspcm, mcSigmaPlusPcmPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.mcBuilder.fillCollisionWithLabel(col, mcCols, mcBuilderProducts); + builders.trackBuilder.processTracksWithMc(col, tracks, mcTrackPartition1, mcTrackPartition2, cache, + trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + builders.kinkBuilder.processSigmaPlusPcmWithMc(col, sigmapluspcm, tracks, mcSigmaPlusPcmPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlusPcmMc, "Process sigmaPlusPcm (with SigmaPlusPcmMlScores, selected on the BDT score) and tracks with MC information", false); + + void processTracksSigmaPlusPcmExtendedMc(femtotables::FilteredFemtoMcCollisions const& cols, + femtotables::FemtoMcTracks const& tracks, + femtotables::FemtoMcSigmaPlusPcmExtended const& sigmapluspcm, + o2::aod::FMcCols const& mcCols, + femtotables::FemtoMcParticles const& mcParticles, + o2::aod::FMcMothers const& mcMothers, + o2::aod::FMcPartMoths const& mcPartonicMothers) + { + // once per dataframe + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, mcTrackPartition1, mcTrackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlusPcm(col, sigmapluspcm, mcSigmaPlusPcmExtendedPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.mcBuilder.fillCollisionWithLabel(col, mcCols, mcBuilderProducts); + builders.trackBuilder.processTracksWithMc(col, tracks, mcTrackPartition1, mcTrackPartition2, cache, + trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + builders.kinkBuilder.processSigmaPlusPcmWithMc(col, sigmapluspcm, tracks, mcSigmaPlusPcmExtendedPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlusPcmExtendedMc, "Process sigmaPlusPcm (with SigmaPlusPcmMlScores and SigmaPlusPcmExtras, selected on the BDT score) and tracks with MC information", false); }; o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) diff --git a/PWGCF/Femto/TableProducer/femtoProducerKinkPtConverter.cxx b/PWGCF/Femto/TableProducer/femtoProducerKinkPtConverter.cxx index 8f61ef26d16..b97853e6829 100644 --- a/PWGCF/Femto/TableProducer/femtoProducerKinkPtConverter.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducerKinkPtConverter.cxx @@ -16,6 +16,7 @@ #include "PWGCF/Femto/Core/femtoUtils.h" #include "PWGCF/Femto/DataModel/FemtoTables.h" +#include #include #include #include @@ -73,7 +74,9 @@ struct FemtoProducerKinkPtConverter { float pyMoth = sigma.pt() * std::sin(sigma.phi()); float pzMoth = sigma.pt() * std::sinh(sigma.eta()); - float ptRecalc = utils::calcPtnew(pxMoth, pyMoth, pzMoth, pxDaug, pyDaug, pzDaug); + // Sigma- -> pi- n + float ptRecalc = utils::calcPtnew(pxMoth, pyMoth, pzMoth, pxDaug, pyDaug, pzDaug, + o2::constants::physics::MassSigmaMinus, o2::constants::physics::MassPionCharged, o2::constants::physics::MassNeutron); ROOT::Math::PtEtaPhiMVector recalcVec(ptRecalc, sigma.eta(), sigma.phi(), sigma.mass()); float ptFrom4Vec = recalcVec.Pt(); diff --git a/PWGCF/Femto/TableProducer/femtoProducerLiteConverter.cxx b/PWGCF/Femto/TableProducer/femtoProducerLiteConverter.cxx index 3d2c5fe0eef..fe68e9c13ad 100644 --- a/PWGCF/Femto/TableProducer/femtoProducerLiteConverter.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducerLiteConverter.cxx @@ -34,6 +34,7 @@ struct FemtoProducerLiteConverter { o2::framework::Produces producedOmegas; o2::framework::Produces producedSigmas; o2::framework::Produces producedSigmaPlus; + o2::framework::Produces producedSigmaPlusPcm; void init(o2::framework::InitContext&) { @@ -138,6 +139,17 @@ struct FemtoProducerLiteConverter { liteSigmaPlus.chaDauId()); } PROCESS_SWITCH(FemtoProducerLiteConverter, processLiteSigmaPlus, "Convert FLiteSigmaPlus to FSigmaPlus", false); + + void processLiteSigmaPlusPcm(o2::aod::FLiteSigmaPlusPcm::iterator const& liteSigmaPlusPcm) + { + producedSigmaPlusPcm(liteSigmaPlusPcm.fLiteColId(), + liteSigmaPlusPcm.signedPt(), + liteSigmaPlusPcm.eta(), + liteSigmaPlusPcm.phi(), + liteSigmaPlusPcm.sigmaMass(), + liteSigmaPlusPcm.chaDauId()); + } + PROCESS_SWITCH(FemtoProducerLiteConverter, processLiteSigmaPlusPcm, "Convert FLiteSigmaPlusPcm to FSigmaPlusPcm", false); }; o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) diff --git a/PWGCF/Femto/TableProducer/femtoProducerResonances.cxx b/PWGCF/Femto/TableProducer/femtoProducerResonances.cxx index 38b787c2c0e..179023fc657 100644 --- a/PWGCF/Femto/TableProducer/femtoProducerResonances.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducerResonances.cxx @@ -47,16 +47,16 @@ struct FemtoProducerResonances { // setup for resonance daughter tracks trackbuilder::ConfPionPlusSelection confPionPlusSelection; - o2::framework::Partition pionPlusPartition = MAKE_TRACK_PARTITION(confPionPlusSelection); + o2::framework::Partition pionPlusPartition = MAKE_RESONANCE_DAUGHTER_PARTITION(confPionPlusSelection); trackbuilder::ConfPionMinusSelection confPionMinusSelection; - o2::framework::Partition pionMinusPartition = MAKE_TRACK_PARTITION(confPionMinusSelection); + o2::framework::Partition pionMinusPartition = MAKE_RESONANCE_DAUGHTER_PARTITION(confPionMinusSelection); trackbuilder::ConfKaonPlusSelection confKaonPlusSelection; - o2::framework::Partition kaonPlusPartition = MAKE_TRACK_PARTITION(confKaonPlusSelection); + o2::framework::Partition kaonPlusPartition = MAKE_RESONANCE_DAUGHTER_PARTITION(confKaonPlusSelection); trackbuilder::ConfKaonMinusSelection confKaonMinusSelection; - o2::framework::Partition kaonMinusPartition = MAKE_TRACK_PARTITION(confKaonMinusSelection); + o2::framework::Partition kaonMinusPartition = MAKE_RESONANCE_DAUGHTER_PARTITION(confKaonMinusSelection); o2::framework::Preslice perColtracks = o2::aod::femtobase::stored::fColId; diff --git a/PWGCF/Femto/TableProducer/femtoProducerTrackTrackTwoPhotonResonances.cxx b/PWGCF/Femto/TableProducer/femtoProducerTrackTrackTwoPhotonResonances.cxx new file mode 100644 index 00000000000..20308a135a4 --- /dev/null +++ b/PWGCF/Femto/TableProducer/femtoProducerTrackTrackTwoPhotonResonances.cxx @@ -0,0 +1,92 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoProducerTrackTrackTwoPhotonResonances.cxx +/// \brief Task that produces femto tables for resonances built from two tracks and a two-photon +/// resonance (e.g. omega(782) -> pi+ pi- pi0) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#include "PWGCF/Femto/Core/collisionBuilder.h" +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/Core/trackBuilder.h" +#include "PWGCF/Femto/Core/trackTrackTwoPhotonResonanceBuilder.h" +#include "PWGCF/Femto/Core/twoPhotonResonanceBuilder.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include + +using namespace o2::analysis::femto; + +// reads the already-built FTracks (for the charged pion daughters) and FPi0s (for the neutral +// daughter) and combines them into omega(782) -> pi+ pi- pi0 candidates. A 3rd-stage producer, +// chained after femtoProducer.cxx (FTracks) and femtoProducerTwoPhotonResonances.cxx (FPi0s), same +// pattern as femtoProducerTwoPhotonResonances.cxx itself reading femtoProducer.cxx's FPhotons. +struct FemtoProducerTrackTrackTwoPhotonResonances { + + using FemtoCollisions = o2::soa::Join; + using FilteredFemtoCollisions = o2::soa::Filtered; + using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; + + using FemtoTracks = o2::soa::Join; + using FemtoPi0s = o2::soa::Join; + + o2::framework::SliceCache cache; + + // setup collisions + collisionbuilder::ConfCollisionSelection collisionSelection; + o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); + colhistmanager::ConfCollisionBinning confCollisionBinning; + + // setup for the two charged (pion) daughters. Dedicated pion-species selections, same ones + // femtoProducerResonances.cxx uses for other two track resonances + trackbuilder::ConfPionPlusSelection posDauSelection; + o2::framework::Partition posDauPartition = MAKE_RESONANCE_DAUGHTER_PARTITION(posDauSelection); + o2::framework::Preslice perColPosDau = o2::aod::femtobase::stored::fColId; + + trackbuilder::ConfPionMinusSelection negDauSelection; + o2::framework::Partition negDauPartition = MAKE_RESONANCE_DAUGHTER_PARTITION(negDauSelection); + o2::framework::Preslice perColNegDau = o2::aod::femtobase::stored::fColId; + + // setup for the neutral (pi0) daughter + twophotonresonancebuilder::ConfPi0Selection neutralDauSelection; + o2::framework::Partition neutralDauPartition = MAKE_TWOPHOTONRESONANCE_PARTITION(neutralDauSelection); + o2::framework::Preslice perColNeutralDau = o2::aod::femtobase::stored::fColId; + + // resonance filter and builder + trackstwophotonresonancebuilder::ConfOmegaMesonFilters confOmegaMesonFilter; + trackstwophotonresonancebuilder::ConfTrackTrackTwoPhotonResonanceTables confOmegaMesonTables; + trackstwophotonresonancebuilder::TrackTrackTwoPhotonResonanceBuilderProducts omegaMesonBuilderProducts; + trackstwophotonresonancebuilder::TrackTrackTwoPhotonResonanceBuilder omegaMesonBuilder; + + void init(o2::framework::InitContext& context) + { + omegaMesonBuilder.init(confOmegaMesonFilter, posDauSelection, negDauSelection, confOmegaMesonTables, context); + } + + void processOmegaMesons(FilteredFemtoCollision const& col, FemtoTracks const& /*tracks*/, FemtoPi0s const& /*pi0s*/) + { + omegaMesonBuilder.fillResonances(col, omegaMesonBuilderProducts, posDauPartition, negDauPartition, neutralDauPartition, cache); + } + PROCESS_SWITCH(FemtoProducerTrackTrackTwoPhotonResonances, processOmegaMesons, "Build OmegaMeson candidates", true); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{adaptAnalysisTask(context)}; + return workflow; +} diff --git a/PWGCF/Femto/TableProducer/femtoProducerTwoPhotonResonances.cxx b/PWGCF/Femto/TableProducer/femtoProducerTwoPhotonResonances.cxx new file mode 100644 index 00000000000..4752d5f3a54 --- /dev/null +++ b/PWGCF/Femto/TableProducer/femtoProducerTwoPhotonResonances.cxx @@ -0,0 +1,88 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoProducerTwoPhotonResonances.cxx +/// \brief Tasks that produces femto tables for resonances built from two PCM photons (pi0, eta, ...) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#include "PWGCF/Femto/Core/collisionBuilder.h" +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/Core/photonBuilder.h" +#include "PWGCF/Femto/Core/twoPhotonResonanceBuilder.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include + +using namespace o2::analysis::femto; + +struct FemtoProducerTwoPhotonResonances { + + using FemtoCollisions = o2::soa::Join; + using FilteredFemtoCollisions = o2::soa::Filtered; + using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; + + using FemtoPhotons = o2::soa::Join; + + o2::framework::SliceCache cache; + + // setup collisions + collisionbuilder::ConfCollisionSelection collisionSelection; + o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); + colhistmanager::ConfCollisionBinning confCollisionBinning; + + // setup for resonance daughter photons + photonbuilder::ConfPhotonSelection1 confPhotonSelection; + o2::framework::Partition photonPartition = MAKE_PHOTON_PARTITION(confPhotonSelection); + o2::framework::Preslice perColPhotons = o2::aod::femtobase::stored::fColId; + + // resonance filters + twophotonresonancebuilder::ConfPi0Filters confPi0Filter; + twophotonresonancebuilder::ConfEtaFilters confEtaFilter; + + // resonance builders + twophotonresonancebuilder::ConfTwoPhotonResonanceTables confTwoPhotonResonanceTables; + twophotonresonancebuilder::TwoPhotonResonanceBuilderProducts twoPhotonResonanceBuilderProducts; + twophotonresonancebuilder::TwoPhotonResonanceBuilder pi0Builder; + twophotonresonancebuilder::TwoPhotonResonanceBuilder etaBuilder; + + void init(o2::framework::InitContext& context) + { + // init builders + pi0Builder.init(confPi0Filter, confTwoPhotonResonanceTables, context); + etaBuilder.init(confEtaFilter, confTwoPhotonResonanceTables, context); + } + + // process functions + void processPi0(FilteredFemtoCollision const& col, FemtoPhotons const& /*photons*/) + { + pi0Builder.fillResonances(col, twoPhotonResonanceBuilderProducts, photonPartition, cache); + } + PROCESS_SWITCH(FemtoProducerTwoPhotonResonances, processPi0, "Build Pi0 candidates", true); + + void processEta(FilteredFemtoCollision const& col, FemtoPhotons const& /*photons*/) + { + etaBuilder.fillResonances(col, twoPhotonResonanceBuilderProducts, photonPartition, cache); + } + PROCESS_SWITCH(FemtoProducerTwoPhotonResonances, processEta, "Build Eta candidates", true); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{adaptAnalysisTask(context)}; + return workflow; +} diff --git a/PWGCF/Femto/Tasks/CMakeLists.txt b/PWGCF/Femto/Tasks/CMakeLists.txt index 0aa26cf38d7..1f9647bd7b2 100644 --- a/PWGCF/Femto/Tasks/CMakeLists.txt +++ b/PWGCF/Femto/Tasks/CMakeLists.txt @@ -19,11 +19,26 @@ o2physics_add_dpl_workflow(femto-twotrackresonance-qa PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(femto-two-photon-resonance-qa + SOURCES femtoTwoPhotonResonanceQa.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(femto-track-track-two-photon-resonance-qa + SOURCES femtoTrackTrackTwoPhotonResonanceQa.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(femto-v0-qa SOURCES femtoV0Qa.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(femto-photon-qa + SOURCES femtoPhotonQa.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(femto-kink-qa SOURCES femtoKinkQa.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore @@ -69,6 +84,11 @@ o2physics_add_dpl_workflow(femto-pair-track-two-track-resonance PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(femto-pair-track-two-photon-resonance + SOURCES femtoPairTrackTwoPhotonResonance.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(femto-pair-v0-two-track-resonance SOURCES femtoPairV0TwoTrackResonance.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore diff --git a/PWGCF/Femto/Tasks/femtoCascadeQa.cxx b/PWGCF/Femto/Tasks/femtoCascadeQa.cxx index 63daed2c1a5..78b8ae337dc 100644 --- a/PWGCF/Femto/Tasks/femtoCascadeQa.cxx +++ b/PWGCF/Femto/Tasks/femtoCascadeQa.cxx @@ -147,11 +147,11 @@ struct FemtoCascadeQa { negDaughterHistSpec = trackhistmanager::makeTrackQaHistSpecMap(confNegDaughterBinning, confNegDaughterQaBinning); if (doprocessXi) { xiHistSpec = cascadehistmanager::makeCascadeQaHistSpecMap(confXiBinning, confXiQaBinning); - xiHistManager.init(&hRegistry, xiHistSpec, confXiSelection, confXiQaBinning, bachelorHistSpec, confBachelorQaBinning, posDaughterHistSpec, confPosDaughterQaBinning, negDaughterHistSpec, confNegDaughterQaBinning); + xiHistManager.init(&hRegistry, xiHistSpec, confXiSelection, confXiQaBinning, bachelorHistSpec, confBachelorQaBinning, posDaughterHistSpec, confPosDaughterQaBinning, negDaughterHistSpec, confNegDaughterQaBinning); } if (doprocessOmega) { omegaHistSpec = cascadehistmanager::makeCascadeQaHistSpecMap(confOmegaBinning, confOmegaQaBinning); - omegaHistManager.init(&hRegistry, omegaHistSpec, confOmegaSelection, confOmegaQaBinning, bachelorHistSpec, confBachelorQaBinning, posDaughterHistSpec, confPosDaughterQaBinning, negDaughterHistSpec, confNegDaughterQaBinning); + omegaHistManager.init(&hRegistry, omegaHistSpec, confOmegaSelection, confOmegaQaBinning, bachelorHistSpec, confBachelorQaBinning, posDaughterHistSpec, confPosDaughterQaBinning, negDaughterHistSpec, confNegDaughterQaBinning); } } else { colHistSpec = colhistmanager::makeColMcQaHistSpecMap(confCollisionBinning, confCollisionQaBinning); @@ -161,11 +161,11 @@ struct FemtoCascadeQa { negDaughterHistSpec = trackhistmanager::makeTrackMcQaHistSpecMap(confNegDaughterBinning, confNegDaughterQaBinning); if (doprocessXiMc) { xiHistSpec = cascadehistmanager::makeCascadeMcQaHistSpecMap(confXiBinning, confXiQaBinning); - xiHistManager.init(&hRegistry, xiHistSpec, confXiSelection, confXiQaBinning, bachelorHistSpec, confBachelorQaBinning, posDaughterHistSpec, confPosDaughterQaBinning, negDaughterHistSpec, confNegDaughterQaBinning); + xiHistManager.init(&hRegistry, xiHistSpec, confXiSelection, confXiQaBinning, bachelorHistSpec, confBachelorQaBinning, posDaughterHistSpec, confPosDaughterQaBinning, negDaughterHistSpec, confNegDaughterQaBinning); } if (doprocessOmegaMc) { omegaHistSpec = cascadehistmanager::makeCascadeMcQaHistSpecMap(confOmegaBinning, confOmegaQaBinning); - omegaHistManager.init(&hRegistry, omegaHistSpec, confOmegaSelection, confOmegaQaBinning, bachelorHistSpec, confBachelorQaBinning, posDaughterHistSpec, confPosDaughterQaBinning, negDaughterHistSpec, confNegDaughterQaBinning); + omegaHistManager.init(&hRegistry, omegaHistSpec, confOmegaSelection, confOmegaQaBinning, bachelorHistSpec, confBachelorQaBinning, posDaughterHistSpec, confPosDaughterQaBinning, negDaughterHistSpec, confNegDaughterQaBinning); } } hRegistry.print(); @@ -179,7 +179,7 @@ struct FemtoCascadeQa { } colHistManager.fill(col); for (auto const& xi : xiSlice) { - xiHistManager.fill(xi, tracks); + xiHistManager.fill(xi, tracks); } } PROCESS_SWITCH(FemtoCascadeQa, processXi, "Process Xis", true); @@ -195,7 +195,7 @@ struct FemtoCascadeQa { if (!xiCleaner.isClean(xi, mcParticles, mcMothers, mcPartonicMothers)) { continue; } - xiHistManager.fill(xi, tracks, col, mcParticles, mcMothers, mcPartonicMothers); + xiHistManager.fill(xi, tracks, col, mcParticles, mcMothers, mcPartonicMothers); } } PROCESS_SWITCH(FemtoCascadeQa, processXiMc, "Process Xis with MC information", false); @@ -208,7 +208,7 @@ struct FemtoCascadeQa { } colHistManager.fill(col); for (auto const& omega : omegaSlice) { - omegaHistManager.fill(omega, tracks); + omegaHistManager.fill(omega, tracks); } } PROCESS_SWITCH(FemtoCascadeQa, processOmega, "Process Omegas", false); @@ -224,7 +224,7 @@ struct FemtoCascadeQa { if (!omegaCleaner.isClean(omega, mcParticles, mcMothers, mcPartonicMothers)) { continue; } - omegaHistManager.fill(omega, tracks, col, mcParticles, mcMothers, mcPartonicMothers); + omegaHistManager.fill(omega, tracks, col, mcParticles, mcMothers, mcPartonicMothers); } } PROCESS_SWITCH(FemtoCascadeQa, processOmegaMc, "Process Omegas with MC information", false); diff --git a/PWGCF/Femto/Tasks/femtoDndetaPair.cxx b/PWGCF/Femto/Tasks/femtoDndetaPair.cxx index 922e5f59a6e..df35e04f725 100644 --- a/PWGCF/Femto/Tasks/femtoDndetaPair.cxx +++ b/PWGCF/Femto/Tasks/femtoDndetaPair.cxx @@ -170,8 +170,10 @@ struct FemtoDndetaPair { closepairrejection::ConfCprTrackV0Daughter confCprTrackV0; pairbuilder::PairTrackTrackBuilder< - trackhistmanager::PrefixTrack1, - trackhistmanager::PrefixTrack2, + trackhistmanager::PrefixTrack1Se, + trackhistmanager::PrefixTrack2Se, + trackhistmanager::PrefixTrack1Me, + trackhistmanager::PrefixTrack2Me, pairhistmanager::PrefixTrackTrackSe, pairhistmanager::PrefixTrackTrackMe, closepairrejection::PrefixTrackTrackSe, @@ -179,10 +181,14 @@ struct FemtoDndetaPair { pairTrackTrackBuilder; pairbuilder::PairTrackV0Builder< - trackhistmanager::PrefixTrack1, - v0histmanager::PrefixLambda1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, + trackhistmanager::PrefixTrack1Se, + v0histmanager::PrefixLambda1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + trackhistmanager::PrefixTrack1Me, + v0histmanager::PrefixLambda1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, pairhistmanager::PrefixTrackV0Se, pairhistmanager::PrefixTrackV0Me, closepairrejection::PrefixTrackV0DaughterSe, @@ -191,8 +197,10 @@ struct FemtoDndetaPair { pairTrackLambdaBuilder; pairbuilder::PairMcParticleMcParticleBuilder< - mcparticlehistmanager::PrefixMcParticle1, - mcparticlehistmanager::PrefixMcParticle2, + mcparticlehistmanager::PrefixMcParticle1Se, + mcparticlehistmanager::PrefixMcParticle2Se, + mcparticlehistmanager::PrefixMcParticle1Me, + mcparticlehistmanager::PrefixMcParticle2Me, pairhistmanager::PrefixMcParticleMcParticleSe, pairhistmanager::PrefixMcParticleMcParticleMe, closepairrejection::PrefixMcParticleMcParticleSe, diff --git a/PWGCF/Femto/Tasks/femtoDndetaTriplet.cxx b/PWGCF/Femto/Tasks/femtoDndetaTriplet.cxx index 4be3c0f0751..44e830e1aed 100644 --- a/PWGCF/Femto/Tasks/femtoDndetaTriplet.cxx +++ b/PWGCF/Femto/Tasks/femtoDndetaTriplet.cxx @@ -183,9 +183,12 @@ struct FemtoDndetaTriplet { closetripletrejection::ConfCtrMcParticleMcParticleMcParticle confCtrMcParticleMcParticleMcParticle; // generator-level trigger, no cut by default tripletbuilder::TripletTrackTrackTrackBuilder< - trackhistmanager::PrefixTrack1, - trackhistmanager::PrefixTrack2, - trackhistmanager::PrefixTrack3, + trackhistmanager::PrefixTrack1Se, + trackhistmanager::PrefixTrack2Se, + trackhistmanager::PrefixTrack3Se, + trackhistmanager::PrefixTrack1Me, + trackhistmanager::PrefixTrack2Me, + trackhistmanager::PrefixTrack3Me, triplethistmanager::PrefixTrackTrackTrackSe, triplethistmanager::PrefixTrackTrackTrackMe, closetripletrejection::PrefixTrack1Track2Se, @@ -198,11 +201,16 @@ struct FemtoDndetaTriplet { tripletbuilder::TripletTrackTrackV0Builder< modes::V0::kLambda, - trackhistmanager::PrefixTrack1, - trackhistmanager::PrefixTrack2, - v0histmanager::PrefixLambda1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, + trackhistmanager::PrefixTrack1Se, + trackhistmanager::PrefixTrack2Se, + v0histmanager::PrefixLambda1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + trackhistmanager::PrefixTrack1Me, + trackhistmanager::PrefixTrack2Me, + v0histmanager::PrefixLambda1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, triplethistmanager::PrefixTrackTrackLambdaSe, triplethistmanager::PrefixTrackTrackLambdaMe, closetripletrejection::PrefixTrack1Track2Se, @@ -214,9 +222,12 @@ struct FemtoDndetaTriplet { tripletTrackTrackLambdaBuilder; tripletbuilder::TripletMcParticleMcParticleMcParticleBuilder< - mcparticlehistmanager::PrefixMcParticle1, - mcparticlehistmanager::PrefixMcParticle2, - mcparticlehistmanager::PrefixMcParticle3, + mcparticlehistmanager::PrefixMcParticle1Se, + mcparticlehistmanager::PrefixMcParticle2Se, + mcparticlehistmanager::PrefixMcParticle3Se, + mcparticlehistmanager::PrefixMcParticle1Me, + mcparticlehistmanager::PrefixMcParticle2Me, + mcparticlehistmanager::PrefixMcParticle3Me, triplethistmanager::PrefixMcParticleMcParticleMcParticleSe, triplethistmanager::PrefixMcParticleMcParticleMcParticleMe, closetripletrejection::PrefixMcParticle1McParticle2Se, diff --git a/PWGCF/Femto/Tasks/femtoMcParticleQa.cxx b/PWGCF/Femto/Tasks/femtoMcParticleQa.cxx index f7f0706e16a..ca7560853da 100644 --- a/PWGCF/Femto/Tasks/femtoMcParticleQa.cxx +++ b/PWGCF/Femto/Tasks/femtoMcParticleQa.cxx @@ -81,18 +81,27 @@ struct FemtoMcParticleQa { hRegistry.print(); }; - void process(FilteredFemtoMcCollision const& col, FemtoMcParticles const& /*mcParticles*/, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + // deliberately takes the full (ungrouped) FilteredFemtoMcCollisions table rather than its ::iterator: + // pairing an ::iterator first argument with FemtoMcParticles as a second table argument would make + // DPL auto-register a sorted per-collision slicing cache for fMcColId, which then collides with the + // manual sliceByCachedUnsorted() below (see the same workaround in femtoDndetaPair.cxx/femtoDndetaTriplet.cxx); + // FemtoMcParticles must still appear as an argument (even though it's only accessed via the partition) + // so DPL actually subscribes to it -- dropping it entirely leaves mcParticlesPartition1 unbound and + // segfaults on the first sliceByCachedUnsorted() call + void process(FilteredFemtoMcCollisions const& cols, FemtoMcParticles const& /*mcParticles*/, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - auto mcParticleSlice = mcParticlesPartition1->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, col.globalIndex(), cache); - if (mcParticleSlice.size() == 0) { - return; - } - colHistManager.fill(col); - for (auto const& mcParticle : mcParticleSlice) { - if (!mcParticleCleaner.isClean(mcParticle, mcMothers, mcPartonicMothers)) { + for (auto const& col : cols) { + auto mcParticleSlice = mcParticlesPartition1->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, col.globalIndex(), cache); + if (mcParticleSlice.size() == 0) { continue; } - mcParticleHistManager1.fill(mcParticle, mcMothers, mcPartonicMothers); + colHistManager.fill(col); + for (auto const& mcParticle : mcParticleSlice) { + if (!mcParticleCleaner.isClean(mcParticle, mcMothers, mcPartonicMothers)) { + continue; + } + mcParticleHistManager1.fill(mcParticle, mcMothers, mcPartonicMothers); + } } } }; diff --git a/PWGCF/Femto/Tasks/femtoPairD0D0.cxx b/PWGCF/Femto/Tasks/femtoPairD0D0.cxx index 66c7decf0f4..340ace0914e 100644 --- a/PWGCF/Femto/Tasks/femtoPairD0D0.cxx +++ b/PWGCF/Femto/Tasks/femtoPairD0D0.cxx @@ -18,6 +18,8 @@ #include "PWGCF/Femto/Core/closePairRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" +#include "PWGCF/Femto/Core/dataTypes.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" @@ -32,11 +34,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -68,6 +72,10 @@ struct FemtoPairD0D0 { using FemtoMcParticlesWithLabel = o2::soa::Join; + // opt-in: carries FColTags, only needed by processMixedEventWithTagging. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + o2::framework::SliceCache cache; // setup collisions @@ -75,6 +83,9 @@ struct FemtoPairD0D0 { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by processMixedEventWithTagging below. + collisiontagger::ConfTagging confTaggingD0D0; + // setup for daughters // separate binning per d0 slot, so that both hypotheses can be binned independently trackhistmanager::ConfD01PosDauBinning confD01PosDauBinning; @@ -104,12 +115,18 @@ struct FemtoPairD0D0 { paircleaner::ConfPairCleanerBinning confPairCleaner; pairbuilder::PairD0D0Builder< - charmhadronhistmanager::PrefixD01, - trackhistmanager::PrefixD01PosDaughter, - trackhistmanager::PrefixD01NegDaughter, - charmhadronhistmanager::PrefixD02, - trackhistmanager::PrefixD02PosDaughter, - trackhistmanager::PrefixD02NegDaughter, + charmhadronhistmanager::PrefixD01Se, + trackhistmanager::PrefixD01PosDaughterSe, + trackhistmanager::PrefixD01NegDaughterSe, + charmhadronhistmanager::PrefixD02Se, + trackhistmanager::PrefixD02PosDaughterSe, + trackhistmanager::PrefixD02NegDaughterSe, + charmhadronhistmanager::PrefixD01Me, + trackhistmanager::PrefixD01PosDaughterMe, + trackhistmanager::PrefixD01NegDaughterMe, + charmhadronhistmanager::PrefixD02Me, + trackhistmanager::PrefixD02PosDaughterMe, + trackhistmanager::PrefixD02NegDaughterMe, pairhistmanager::PrefixD0D0Se, pairhistmanager::PrefixD0D0Me, closepairrejection::PrefixD0D0PosSe, @@ -124,9 +141,9 @@ struct FemtoPairD0D0 { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; pairhistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoD0D0", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; @@ -135,9 +152,9 @@ struct FemtoPairD0D0 { closepairrejection::ConfCprD0DaugherD0DaughterPos confCprPos; closepairrejection::ConfCprD0DaugherD0DaughterNeg confCprNeg; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - bool processData = doprocessSameEvent || doprocessMixedEvent; + bool processData = doprocessSameEvent || doprocessMixedEvent || doprocessMixedEventWithTagging; bool processMc = doprocessSameEventMc || doprocessMixedEventMc; if (processData && processMc) { @@ -147,11 +164,24 @@ struct FemtoPairD0D0 { LOG(fatal) << "Neither data nor mc processing is enabled. Breaking..."; } + if (doprocessMixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingD0D0.Role); + if (bit < 0) { + LOG(fatal) << "processMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processD0D0 enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingD0D0.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (D0D0): " << bit; + } + // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; // setup histograms std::map> colHistSpec; @@ -202,6 +232,16 @@ struct FemtoPairD0D0 { } PROCESS_SWITCH(FemtoPairD0D0, processSameEventMc, "Enable processing same event processing for d0-d0 with mc information", false); + // Opt-in counterpart of processMixedEvent() below -- same "Partition declared and bound inside + // process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. + void processMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoD0s const& /*d0s*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingD0D0.internalTagMask); + taggedCollisions.bindTable(cols); + pairD0D0Builder.processMixedEvent(taggedCollisions, tracks, d0Partition1, d0Partition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairD0D0, processMixedEventWithTagging, "Enable processing mixed event processing for d0-d0, pre-filtered using the collision tagger", false); + void processMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoD0s const& /*d0s*/) { pairD0D0Builder.processMixedEvent(cols, tracks, d0Partition1, d0Partition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); diff --git a/PWGCF/Femto/Tasks/femtoPairMcParticleMcParticle.cxx b/PWGCF/Femto/Tasks/femtoPairMcParticleMcParticle.cxx index 7be627972f8..f4eb09769b3 100644 --- a/PWGCF/Femto/Tasks/femtoPairMcParticleMcParticle.cxx +++ b/PWGCF/Femto/Tasks/femtoPairMcParticleMcParticle.cxx @@ -86,8 +86,10 @@ struct FemtoPairMcParticleMcParticle { closepairrejection::ConfCprTrackTrack confCpr; pairbuilder::PairMcParticleMcParticleBuilder< - mcparticlehistmanager::PrefixMcParticle1, - mcparticlehistmanager::PrefixMcParticle2, + mcparticlehistmanager::PrefixMcParticle1Se, + mcparticlehistmanager::PrefixMcParticle2Se, + mcparticlehistmanager::PrefixMcParticle1Me, + mcparticlehistmanager::PrefixMcParticle2Me, pairhistmanager::PrefixMcParticleMcParticleSe, pairhistmanager::PrefixMcParticleMcParticleMe, closepairrejection::PrefixMcParticleMcParticleSe, @@ -98,9 +100,9 @@ struct FemtoPairMcParticleMcParticle { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; pairhistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoMcParticleMcParticle", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; @@ -109,9 +111,9 @@ struct FemtoPairMcParticleMcParticle { { // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; // setup histogram specs std::map> colHistSpec; @@ -130,9 +132,16 @@ struct FemtoPairMcParticleMcParticle { hRegistry.print(); }; - void processSameEvent(FilteredFemtoMcCollision const& col, FemtoMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + // deliberately takes the full (ungrouped) FilteredFemtoMcCollisions table rather than its ::iterator: + // pairing an ::iterator first argument with FemtoMcParticles as a second table argument would make + // DPL auto-register a sorted per-collision slicing cache for fMcColId, which then collides with the + // manual sliceByCachedUnsorted() used inside the builder (see the same workaround in + // femtoDndetaPair.cxx/femtoDndetaTriplet.cxx); we run the per-collision loop ourselves instead + void processSameEvent(FilteredFemtoMcCollisions const& cols, FemtoMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairMcParticleMcParticleBuilder.processSameEvent(col, mcParticles, mcMothers, mcPartonicMothers, mcParticlesPartition1, mcParticlesPartition2, cache); + for (auto const& col : cols) { + pairMcParticleMcParticleBuilder.processSameEvent(col, mcParticles, mcMothers, mcPartonicMothers, mcParticlesPartition1, mcParticlesPartition2, cache); + } } PROCESS_SWITCH(FemtoPairMcParticleMcParticle, processSameEvent, "Enable processing same event processing", true); diff --git a/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx b/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx index 4e70a7ab76f..290d910e1aa 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx @@ -18,6 +18,8 @@ #include "PWGCF/Femto/Core/closePairRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" +#include "PWGCF/Femto/Core/dataTypes.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" @@ -33,11 +35,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -56,13 +60,22 @@ struct FemtoPairTrackCascade { using FilteredFemtoCollisionsWithLabel = o2::soa::Filtered; using FilteredFemtoCollisionWithLabel = FilteredFemtoCollisionsWithLabel::iterator; + // opt-in FColTags , only needed by processXiMixedEventWithTagging/processOmegaMixedEventWithTagging + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + using FemtoTracks = o2::soa::Join; using FemtoXis = o2::soa::Join; using FemtoOmegas = o2::soa::Join; - using FemtoTracksWithLabel = o2::soa::Join; - using FemtoXisWithLabel = o2::soa::Join; - using FemtoOmegasWithLabel = o2::soa::Join; + using FemtoTracksWithLabel = o2::soa::Join; + using FemtoXisWithLabel = o2::soa::Join; + using FemtoOmegasWithLabel = o2::soa::Join; + + // reco-only + FitVars (no MC labels, but carries the opt-in purity/fraction tables) + using FemtoTracksWithFitVars = o2::soa::Join; + using FemtoXisWithFitVars = o2::soa::Join; + using FemtoOmegasWithFitVars = o2::soa::Join; using FemtoMcParticlesWithLabel = o2::soa::Join; @@ -73,6 +86,11 @@ struct FemtoPairTrackCascade { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by processXiMixedEventWithTagging/processOmegaMixedEventWithTagging -- + // two separate instances since Xi and Omega are tagged independently (see init()). + collisiontagger::ConfTagging confTaggingXi; + collisiontagger::ConfTagging confTaggingOmega; + // setup tracks trackbuilder::ConfTrackSelection1 confTrackSelection; trackhistmanager::ConfTrackBinning1 confTrackBinning; @@ -84,6 +102,9 @@ struct FemtoPairTrackCascade { o2::framework::Partition trackWithLabelPartition = MAKE_TRACK_PARTITION(confTrackSelection); o2::framework::Preslice perColtracksWithLabel = o2::aod::femtobase::stored::fColId; + o2::framework::Partition trackWithFitVarsPartition = MAKE_TRACK_PARTITION(confTrackSelection); + o2::framework::Preslice perColTracksWithFitVars = o2::aod::femtobase::stored::fColId; + // setup for daughters/bachelor trackhistmanager::ConfCascadePosDauBinning confPosDauBinning; trackhistmanager::ConfCascadeNegDauBinning confNegDauBinning; @@ -100,6 +121,9 @@ struct FemtoPairTrackCascade { o2::framework::Partition xiWithLabelPartition = MAKE_CASCADE_PARTITION(confXiSelection); o2::framework::Preslice perColXisWithLabel = o2::aod::femtobase::stored::fColId; + o2::framework::Partition xiWithFitVarsPartition = MAKE_CASCADE_PARTITION(confXiSelection); + o2::framework::Preslice perColXisWithFitVars = o2::aod::femtobase::stored::fColId; + // setup omegas cascadebuilder::ConfOmegaSelection confOmegaSelection; cascadehistmanager::ConfOmegaBinning confOmegaBinning; @@ -111,17 +135,25 @@ struct FemtoPairTrackCascade { o2::framework::Partition omegaWithLabelPartition = MAKE_CASCADE_PARTITION(confOmegaSelection); o2::framework::Preslice perColOmegasWithLabel = o2::aod::femtobase::stored::fColId; + o2::framework::Partition omegaWithFitVarsPartition = MAKE_CASCADE_PARTITION(confOmegaSelection); + o2::framework::Preslice perColOmegasWithFitVars = o2::aod::femtobase::stored::fColId; + // setup pairs pairhistmanager::ConfPairBinning confPairBinning; pairhistmanager::ConfPairCuts confPairCuts; paircleaner::ConfPairCleanerBinning confPairCleaner; pairbuilder::PairTrackCascadeBuilder< - trackhistmanager::PrefixTrack1, - cascadehistmanager::PrefixXi, - trackhistmanager::PrefixCascadeBachelor, - trackhistmanager::PrefixCascadePosDaughter, - trackhistmanager::PrefixCascadeNegDaughter, + trackhistmanager::PrefixTrack1Se, + cascadehistmanager::PrefixXiSe, + trackhistmanager::PrefixCascadeBachelorSe, + trackhistmanager::PrefixCascadePosDaughterSe, + trackhistmanager::PrefixCascadeNegDaughterSe, + trackhistmanager::PrefixTrack1Me, + cascadehistmanager::PrefixXiMe, + trackhistmanager::PrefixCascadeBachelorMe, + trackhistmanager::PrefixCascadePosDaughterMe, + trackhistmanager::PrefixCascadeNegDaughterMe, pairhistmanager::PrefixTrackCascadeSe, pairhistmanager::PrefixTrackCascadeMe, closepairrejection::PrefixTrackCascadeBachelorSe, @@ -132,11 +164,16 @@ struct FemtoPairTrackCascade { pairTrackXiBuilder; pairbuilder::PairTrackCascadeBuilder< - trackhistmanager::PrefixTrack1, - cascadehistmanager::PrefixOmega, - trackhistmanager::PrefixCascadeBachelor, - trackhistmanager::PrefixCascadePosDaughter, - trackhistmanager::PrefixCascadeNegDaughter, + trackhistmanager::PrefixTrack1Se, + cascadehistmanager::PrefixOmegaSe, + trackhistmanager::PrefixCascadeBachelorSe, + trackhistmanager::PrefixCascadePosDaughterSe, + trackhistmanager::PrefixCascadeNegDaughterSe, + trackhistmanager::PrefixTrack1Me, + cascadehistmanager::PrefixOmegaMe, + trackhistmanager::PrefixCascadeBachelorMe, + trackhistmanager::PrefixCascadePosDaughterMe, + trackhistmanager::PrefixCascadeNegDaughterMe, pairhistmanager::PrefixTrackCascadeSe, pairhistmanager::PrefixTrackCascadeMe, closepairrejection::PrefixTrackCascadeBachelorSe, @@ -150,9 +187,9 @@ struct FemtoPairTrackCascade { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; pairhistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoTrackCascade", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; @@ -161,17 +198,43 @@ struct FemtoPairTrackCascade { closepairrejection::ConfCprTrackCascadeBachelor confCprBachelor; closepairrejection::ConfCprTrackV0Daughter confCprV0Daughter; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - bool processData = doprocessXiSameEvent || doprocessXiMixedEvent || doprocessOmegaSameEvent || doprocessOmegaMixedEvent; + bool processData = doprocessXiSameEvent || doprocessXiMixedEvent || doprocessOmegaSameEvent || doprocessOmegaMixedEvent || doprocessXiMixedEventWithTagging || doprocessOmegaMixedEventWithTagging; + bool processFitVars = doprocessXiSameEventWithFitVars || doprocessXiMixedEventWithFitVars || doprocessOmegaSameEventWithFitVars || doprocessOmegaMixedEventWithFitVars; bool processMc = doprocessXiSameEventMc || doprocessXiMixedEventMc || doprocessOmegaSameEventMc || doprocessOmegaMixedEventMc; - if (processData && processMc) { - LOG(fatal) << "Both data and mc processing is enabled. Breaking..."; + if (static_cast(processData) + static_cast(processFitVars) + static_cast(processMc) > 1) { + LOG(fatal) << "More than one of data, FitVars or mc processing is activated. Breaking..."; } - bool processXi = doprocessXiSameEvent || doprocessXiSameEventMc || doprocessXiMixedEvent || doprocessXiMixedEventMc; - bool processOmega = doprocessOmegaSameEvent || doprocessOmegaSameEventMc || doprocessOmegaMixedEvent || doprocessOmegaMixedEventMc; + bool processXi = doprocessXiSameEvent || doprocessXiSameEventWithFitVars || doprocessXiSameEventMc || doprocessXiMixedEvent || doprocessXiMixedEventWithFitVars || doprocessXiMixedEventMc || doprocessXiMixedEventWithTagging; + bool processOmega = doprocessOmegaSameEvent || doprocessOmegaSameEventWithFitVars || doprocessOmegaSameEventMc || doprocessOmegaMixedEvent || doprocessOmegaMixedEventWithFitVars || doprocessOmegaMixedEventMc || doprocessOmegaMixedEventWithTagging; + + // Each species discovers its own tag bit independently (see Core/collisionTagger.h). + if (doprocessXiMixedEventWithTagging || doprocessOmegaMixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + if (doprocessXiMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingXi.Role); + if (bit < 0) { + LOG(fatal) << "processXiMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackCascade enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingXi.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Xi): " << bit; + } + if (doprocessOmegaMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingOmega.Role); + if (bit < 0) { + LOG(fatal) << "processOmegaMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackCascade enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingOmega.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Omega): " << bit; + } + } if (processXi && processOmega) { LOG(fatal) << "Both xi-track and omega-track processing is enabled. Breaking..."; @@ -179,9 +242,9 @@ struct FemtoPairTrackCascade { // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; std::map> colHistSpec; std::map> trackHistSpec; @@ -196,7 +259,21 @@ struct FemtoPairTrackCascade { std::map> cprHistSpecV0Daughter = closepairrejection::makeCprHistSpecMap(confCprV0Daughter); std::map> pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); - if (processData) { + if (processFitVars) { + colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); + trackHistSpec = trackhistmanager::makeTrackHistSpecMap(confTrackBinning); + bachelorHistSpec = trackhistmanager::makeTrackHistSpecMap(confBachelorBinning); + posDauSpec = trackhistmanager::makeTrackHistSpecMap(confPosDauBinning); + negDauSpec = trackhistmanager::makeTrackHistSpecMap(confNegDauBinning); + pairTrackCascadeHistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); + if (processXi) { + xiHistSpec = cascadehistmanager::makeCascadeHistSpecMap(confXiBinning); + pairTrackXiBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confXiSelection, confXiCleaner, confCprBachelor, confCprV0Daughter, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, xiHistSpec, bachelorHistSpec, posDauSpec, negDauSpec, pairTrackCascadeHistSpec, cprHistSpecBachelor, cprHistSpecV0Daughter, pairCleanerHistSpec); + } else { + omegaHistSpec = cascadehistmanager::makeCascadeHistSpecMap(confOmegaBinning); + pairTrackOmegaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confOmegaSelection, confOmegaCleaner, confCprBachelor, confCprV0Daughter, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, omegaHistSpec, bachelorHistSpec, posDauSpec, negDauSpec, pairTrackCascadeHistSpec, cprHistSpecBachelor, cprHistSpecV0Daughter, pairCleanerHistSpec); + } + } else if (processData) { colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); trackHistSpec = trackhistmanager::makeTrackHistSpecMap(confTrackBinning); bachelorHistSpec = trackhistmanager::makeTrackHistSpecMap(confBachelorBinning); @@ -220,10 +297,10 @@ struct FemtoPairTrackCascade { pairTrackCascadeHistSpec = pairhistmanager::makePairMcHistSpecMap(confPairBinning, confMixing); if (processXi) { xiHistSpec = cascadehistmanager::makeCascadeMcHistSpecMap(confXiBinning); - pairTrackXiBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confXiSelection, confXiCleaner, confCprBachelor, confCprV0Daughter, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, xiHistSpec, bachelorHistSpec, posDauSpec, negDauSpec, pairTrackCascadeHistSpec, cprHistSpecBachelor, cprHistSpecV0Daughter, pairCleanerHistSpec); + pairTrackXiBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confXiSelection, confXiCleaner, confCprBachelor, confCprV0Daughter, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, xiHistSpec, bachelorHistSpec, posDauSpec, negDauSpec, pairTrackCascadeHistSpec, cprHistSpecBachelor, cprHistSpecV0Daughter, pairCleanerHistSpec); } else { omegaHistSpec = cascadehistmanager::makeCascadeMcHistSpecMap(confOmegaBinning); - pairTrackOmegaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confOmegaSelection, confOmegaCleaner, confCprBachelor, confCprV0Daughter, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, omegaHistSpec, bachelorHistSpec, posDauSpec, negDauSpec, pairTrackCascadeHistSpec, cprHistSpecBachelor, cprHistSpecV0Daughter, pairCleanerHistSpec); + pairTrackOmegaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confOmegaSelection, confOmegaCleaner, confCprBachelor, confCprV0Daughter, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, omegaHistSpec, bachelorHistSpec, posDauSpec, negDauSpec, pairTrackCascadeHistSpec, cprHistSpecBachelor, cprHistSpecV0Daughter, pairCleanerHistSpec); } } hRegistry.print(); @@ -237,10 +314,21 @@ struct FemtoPairTrackCascade { void processXiSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoXisWithLabel const& xis, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackXiBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, xis, xiWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); + pairTrackXiBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, xis, xiWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); } PROCESS_SWITCH(FemtoPairTrackCascade, processXiSameEventMc, "Enable processing same event processing for tracks and xis with mc information", false); + // Opt-in counterpart of processXiMixedEvent() below -- same "Partition declared and bound + // inside process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. Requires + // a femto-collision-tagger device (processTrackCascade enabled) in the same workflow -- see init(). + void processXiMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoXis const& /*xis*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingXi.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackXiBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition, xiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackCascade, processXiMixedEventWithTagging, "Enable processing mixed event processing for tracks and xis, pre-filtered using the collision tagger", false); + void processXiMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoXis const& /*xis*/) { pairTrackXiBuilder.processMixedEvent(cols, tracks, trackPartition, xiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -249,10 +337,22 @@ struct FemtoPairTrackCascade { void processXiMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoXisWithLabel const& /*xis*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackXiBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, xiWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairTrackXiBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, xiWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoPairTrackCascade, processXiMixedEventMc, "Enable processing mixed event processing for tracks and xis with mc information", false); + void processXiSameEventWithFitVars(FilteredFemtoCollision const& col, FemtoTracksWithFitVars const& tracks, FemtoXisWithFitVars const& xis) + { + pairTrackXiBuilder.processSameEvent(col, tracks, trackWithFitVarsPartition, xis, xiWithFitVarsPartition, cache); + } + PROCESS_SWITCH(FemtoPairTrackCascade, processXiSameEventWithFitVars, "Enable processing same event processing for tracks and xis, carrying through FitVars tables", false); + + void processXiMixedEventWithFitVars(FilteredFemtoCollisions const& cols, FemtoTracksWithFitVars const& tracks, FemtoXisWithFitVars const& /*xis*/) + { + pairTrackXiBuilder.processMixedEvent(cols, tracks, trackWithFitVarsPartition, xiWithFitVarsPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackCascade, processXiMixedEventWithFitVars, "Enable processing mixed event processing for tracks and xis, carrying through FitVars tables", false); + void processOmegaSameEvent(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoOmegas const& omegas) { pairTrackOmegaBuilder.processSameEvent(col, tracks, trackPartition, omegas, omegaPartition, cache); @@ -261,10 +361,19 @@ struct FemtoPairTrackCascade { void processOmegaSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoOmegasWithLabel const& omegas, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackOmegaBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, omegas, omegaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); + pairTrackOmegaBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, omegas, omegaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); } PROCESS_SWITCH(FemtoPairTrackCascade, processOmegaSameEventMc, "Enable processing same event processing for tracks and omegas with mc information", false); + // Opt-in counterpart of processOmegaMixedEvent() below -- same idiom as processXiMixedEventWithTagging above. + void processOmegaMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoOmegas const& /*omegas*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingOmega.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackOmegaBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition, omegaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackCascade, processOmegaMixedEventWithTagging, "Enable processing mixed event processing for tracks and omegas, pre-filtered using the collision tagger", false); + void processOmegaMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoOmegas const& /*omegas*/) { pairTrackOmegaBuilder.processMixedEvent(cols, tracks, trackPartition, omegaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -273,9 +382,21 @@ struct FemtoPairTrackCascade { void processOmegaMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoOmegasWithLabel const& /*omegas*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackOmegaBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, omegaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairTrackOmegaBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, omegaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoPairTrackCascade, processOmegaMixedEventMc, "Enable processing mixed event processing for tracks and omegas with mc information", false); + + void processOmegaSameEventWithFitVars(FilteredFemtoCollision const& col, FemtoTracksWithFitVars const& tracks, FemtoOmegasWithFitVars const& omegas) + { + pairTrackOmegaBuilder.processSameEvent(col, tracks, trackWithFitVarsPartition, omegas, omegaWithFitVarsPartition, cache); + } + PROCESS_SWITCH(FemtoPairTrackCascade, processOmegaSameEventWithFitVars, "Enable processing same event processing for tracks and omegas, carrying through FitVars tables", false); + + void processOmegaMixedEventWithFitVars(FilteredFemtoCollisions const& cols, FemtoTracksWithFitVars const& tracks, FemtoOmegasWithFitVars const& /*omegas*/) + { + pairTrackOmegaBuilder.processMixedEvent(cols, tracks, trackWithFitVarsPartition, omegaWithFitVarsPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackCascade, processOmegaMixedEventWithFitVars, "Enable processing mixed event processing for tracks and omegas, carrying through FitVars tables", false); }; o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) diff --git a/PWGCF/Femto/Tasks/femtoPairTrackCharmHadron.cxx b/PWGCF/Femto/Tasks/femtoPairTrackCharmHadron.cxx index 600c3c3c033..69ec4fecb24 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackCharmHadron.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackCharmHadron.cxx @@ -18,6 +18,8 @@ #include "PWGCF/Femto/Core/closePairRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" +#include "PWGCF/Femto/Core/dataTypes.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" @@ -33,11 +35,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -60,6 +64,10 @@ struct FemtoPairTrackCharmHadron { using FilteredFemtoCollisionsWithLabel = o2::soa::Filtered; using FilteredFemtoCollisionWithLabel = FilteredFemtoCollisionsWithLabel::iterator; + // opt-in: carries FColTags, only needed by processD0MixedEventWithTagging/processLcMixedEventWithTagging. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + using FemtoTracksWithLabel = o2::soa::Join; using FemtoD0sWithLabel = o2::soa::Join; using FemtoLcsWithLabel = o2::soa::Join; @@ -72,6 +80,11 @@ struct FemtoPairTrackCharmHadron { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by processD0MixedEventWithTagging/processLcMixedEventWithTagging -- two + // separate instances since D0 and Lc are tagged independently (see init()). + collisiontagger::ConfTagging confTaggingD0; + collisiontagger::ConfTagging confTaggingLc; + // setup tracks trackbuilder::ConfTrackSelection1 confTrackSelection; trackhistmanager::ConfTrackBinning1 confTrackBinning; @@ -120,10 +133,14 @@ struct FemtoPairTrackCharmHadron { paircleaner::ConfPairCleanerBinning confPairCleaner; pairbuilder::PairTrackD0Builder< - trackhistmanager::PrefixTrack1, - charmhadronhistmanager::PrefixD01, - trackhistmanager::PrefixD01PosDaughter, - trackhistmanager::PrefixD01NegDaughter, + trackhistmanager::PrefixTrack1Se, + charmhadronhistmanager::PrefixD01Se, + trackhistmanager::PrefixD01PosDaughterSe, + trackhistmanager::PrefixD01NegDaughterSe, + trackhistmanager::PrefixTrack1Me, + charmhadronhistmanager::PrefixD01Me, + trackhistmanager::PrefixD01PosDaughterMe, + trackhistmanager::PrefixD01NegDaughterMe, pairhistmanager::PrefixTrackD0Se, pairhistmanager::PrefixTrackD0Me, closepairrejection::PrefixTrackD0DaughterSe, @@ -132,11 +149,16 @@ struct FemtoPairTrackCharmHadron { pairTrackD0Builder; pairbuilder::PairTrackLcBuilder< - trackhistmanager::PrefixTrack1, - charmhadronhistmanager::PrefixLc1, - trackhistmanager::PrefixLc1ProtonDaughter, - trackhistmanager::PrefixLc1KaonDaughter, - trackhistmanager::PrefixLc1PionDaughter, + trackhistmanager::PrefixTrack1Se, + charmhadronhistmanager::PrefixLc1Se, + trackhistmanager::PrefixLc1ProtonDaughterSe, + trackhistmanager::PrefixLc1KaonDaughterSe, + trackhistmanager::PrefixLc1PionDaughterSe, + trackhistmanager::PrefixTrack1Me, + charmhadronhistmanager::PrefixLc1Me, + trackhistmanager::PrefixLc1ProtonDaughterMe, + trackhistmanager::PrefixLc1KaonDaughterMe, + trackhistmanager::PrefixLc1PionDaughterMe, pairhistmanager::PrefixTrackLcSe, pairhistmanager::PrefixTrackLcMe, closepairrejection::PrefixTrackLcProtonSe, @@ -152,9 +174,9 @@ struct FemtoPairTrackCharmHadron { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; pairhistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoTrackD0", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; @@ -165,11 +187,11 @@ struct FemtoPairTrackCharmHadron { closepairrejection::ConfCprTrackLcKaon confCprLcKaon; closepairrejection::ConfCprTrackLcPion confCprLcPion; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - bool processD0 = doprocessD0SameEvent || doprocessD0MixedEvent; + bool processD0 = doprocessD0SameEvent || doprocessD0MixedEvent || doprocessD0MixedEventWithTagging; bool processD0Mc = doprocessD0SameEventMc || doprocessD0MixedEventMc; - bool processLc = doprocessLcSameEvent || doprocessLcMixedEvent; + bool processLc = doprocessLcSameEvent || doprocessLcMixedEvent || doprocessLcMixedEventWithTagging; bool processLcMc = doprocessLcSameEventMc || doprocessLcMixedEventMc; if ((static_cast(processD0) + static_cast(processD0Mc) + static_cast(processLc) + static_cast(processLcMc)) > 1) { @@ -178,11 +200,36 @@ struct FemtoPairTrackCharmHadron { bool processData = processD0 || processLc; + // Each species discovers its own tag bit independently (see Core/collisionTagger.h). + if (doprocessD0MixedEventWithTagging || doprocessLcMixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + if (doprocessD0MixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingD0.Role); + if (bit < 0) { + LOG(fatal) << "processD0MixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackD0 enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingD0.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (D0): " << bit; + } + if (doprocessLcMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingLc.Role); + if (bit < 0) { + LOG(fatal) << "processLcMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackLc enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingLc.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Lc): " << bit; + } + } + // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; std::map> colHistSpec; std::map> trackHistSpec; @@ -246,6 +293,16 @@ struct FemtoPairTrackCharmHadron { } PROCESS_SWITCH(FemtoPairTrackCharmHadron, processD0SameEvent, "Enable processing same event processing for tracks and D0s", true); + // Opt-in counterpart of processD0MixedEvent() below -- same "Partition declared and bound + // inside process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. + void processD0MixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoD0s const& /*d0s*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingD0.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackD0Builder.processMixedEvent(taggedCollisions, tracks, trackPartition, d0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackCharmHadron, processD0MixedEventWithTagging, "Enable processing mixed event processing for tracks and D0s, pre-filtered using the collision tagger", false); + void processD0MixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoD0s const& /*d0s*/) { pairTrackD0Builder.processMixedEvent(cols, tracks, trackPartition, d0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -270,6 +327,15 @@ struct FemtoPairTrackCharmHadron { } PROCESS_SWITCH(FemtoPairTrackCharmHadron, processLcSameEvent, "Enable processing same event processing for tracks and Lcs", false); + // Opt-in counterpart of processLcMixedEvent() below -- same idiom as processD0MixedEventWithTagging above. + void processLcMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoLcs const& /*lcs*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingLc.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackLcBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition, lcPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackCharmHadron, processLcMixedEventWithTagging, "Enable processing mixed event processing for tracks and Lcs, pre-filtered using the collision tagger", false); + void processLcMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoLcs const& /*lcs*/) { pairTrackLcBuilder.processMixedEvent(cols, tracks, trackPartition, lcPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); diff --git a/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx b/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx index bbf0da77024..4e760f037d4 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx @@ -17,6 +17,8 @@ #include "PWGCF/Femto/Core/closePairRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" +#include "PWGCF/Femto/Core/dataTypes.h" #include "PWGCF/Femto/Core/kinkBuilder.h" #include "PWGCF/Femto/Core/kinkHistManager.h" #include "PWGCF/Femto/Core/modes.h" @@ -34,11 +36,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -57,13 +61,19 @@ struct FemtoPairTrackKink { using FilteredFemtoCollisionsWithLabel = o2::soa::Filtered; using FilteredFemtoCollisionWithLabel = FilteredFemtoCollisionsWithLabel::iterator; + // opt-in: carries FColTags, only needed by the processXxxMixedEventWithTagging functions below. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + using FemtoTracks = o2::soa::Join; using FemtoSigmas = o2::soa::Join; using FemtoSigmaPlus = o2::soa::Join; + using FemtoSigmaPlusPcm = o2::soa::Join; using FemtoTracksWithLabel = o2::soa::Join; using FemtoSigmasWithLabel = o2::soa::Join; using FemtoSigmaPlusWithLabel = o2::soa::Join; + using FemtoSigmaPlusPcmWithLabel = o2::soa::Join; using FemtoMcParticlesWithLabel = o2::soa::Join; @@ -74,6 +84,12 @@ struct FemtoPairTrackKink { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by the processXxxMixedEventWithTagging functions below -- three separate + // instances since Sigma/SigmaPlus/SigmaPlusPcm are tagged independently (see init()). + collisiontagger::ConfTagging confTaggingSigma; + collisiontagger::ConfTagging confTaggingSigmaPlus; + collisiontagger::ConfTagging confTaggingSigmaPlusPcm; + // setup tracks trackbuilder::ConfTrackSelection1 confTrackSelection; trackhistmanager::ConfTrackBinning1 confTrackBinning; @@ -110,15 +126,29 @@ struct FemtoPairTrackKink { o2::framework::Partition sigmaPlusWithLabelPartition = MAKE_SIGMAPLUS_PARTITION(confSigmaPlusSelection); o2::framework::Preslice perColSigmaPlusWithLabel = o2::aod::femtobase::stored::fColId; + // setup for sigma plus reconstructed with a PCM photon + kinkbuilder::ConfSigmaPlusPcmSelection1 confSigmaPlusPcmSelection; + kinkhistmanager::ConfSigmaPlusPcmBinning1 confSigmaPlusPcmBinning; + particlecleaner::ConfSigmaPlusPcmCleaner1 confSigmaPlusPcmCleaner; + + o2::framework::Partition sigmaPlusPcmPartition = MAKE_SIGMAPLUSPCM_PARTITION(confSigmaPlusPcmSelection); + o2::framework::Preslice perColSigmaPlusPcm = o2::aod::femtobase::stored::fColId; + + o2::framework::Partition sigmaPlusPcmWithLabelPartition = MAKE_SIGMAPLUSPCM_PARTITION(confSigmaPlusPcmSelection); + o2::framework::Preslice perColSigmaPlusPcmWithLabel = o2::aod::femtobase::stored::fColId; + // setup pairs pairhistmanager::ConfPairBinning confPairBinning; pairhistmanager::ConfPairCuts confPairCuts; paircleaner::ConfPairCleanerBinning confPairCleaner; pairbuilder::PairTrackKinkBuilder< - trackhistmanager::PrefixTrack1, - kinkhistmanager::PrefixSigma1, - trackhistmanager::PrefixKinkChaDaughter, + trackhistmanager::PrefixTrack1Se, + kinkhistmanager::PrefixSigma1Se, + trackhistmanager::PrefixKinkChaDaughterSe, + trackhistmanager::PrefixTrack1Me, + kinkhistmanager::PrefixSigma1Me, + trackhistmanager::PrefixKinkChaDaughterMe, pairhistmanager::PrefixTrackKinkSe, pairhistmanager::PrefixTrackKinkMe, closepairrejection::PrefixTrackKinkSe, @@ -127,9 +157,12 @@ struct FemtoPairTrackKink { pairTrackSigmaBuilder; pairbuilder::PairTrackKinkBuilder< - trackhistmanager::PrefixTrack1, - kinkhistmanager::PrefixSigmaPlus1, - trackhistmanager::PrefixKinkChaDaughter, + trackhistmanager::PrefixTrack1Se, + kinkhistmanager::PrefixSigmaPlus1Se, + trackhistmanager::PrefixKinkChaDaughterSe, + trackhistmanager::PrefixTrack1Me, + kinkhistmanager::PrefixSigmaPlus1Me, + trackhistmanager::PrefixKinkChaDaughterMe, pairhistmanager::PrefixTrackKinkSe, pairhistmanager::PrefixTrackKinkMe, closepairrejection::PrefixTrackKinkSe, @@ -137,13 +170,27 @@ struct FemtoPairTrackKink { modes::Kink::kSigmaPlus> pairTrackSigmaPlusBuilder; + pairbuilder::PairTrackKinkBuilder< + trackhistmanager::PrefixTrack1Se, + kinkhistmanager::PrefixSigmaPlusPcm1Se, + trackhistmanager::PrefixKinkChaDaughterSe, + trackhistmanager::PrefixTrack1Me, + kinkhistmanager::PrefixSigmaPlusPcm1Me, + trackhistmanager::PrefixKinkChaDaughterMe, + pairhistmanager::PrefixTrackKinkSe, + pairhistmanager::PrefixTrackKinkMe, + closepairrejection::PrefixTrackKinkSe, + closepairrejection::PrefixTrackKinkMe, + modes::Kink::kSigmaPlusPcm> + pairTrackSigmaPlusPcmBuilder; + // setup mixing std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; pairhistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoTrackKink", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; @@ -151,33 +198,73 @@ struct FemtoPairTrackKink { // setup cpr closepairrejection::ConfCprTrackKinkDaughter confCpr; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - bool processData = doprocessSigmaSameEvent || doprocessSigmaMixedEvent || doprocessSigmaPlusSameEvent || doprocessSigmaPlusMixedEvent; - bool processMc = doprocessSigmaSameEventMc || doprocessSigmaMixedEventMc || doprocessSigmaPlusSameEventMc || doprocessSigmaPlusMixedEventMc; + bool processData = doprocessSigmaSameEvent || doprocessSigmaMixedEvent || doprocessSigmaPlusSameEvent || doprocessSigmaPlusMixedEvent || doprocessSigmaPlusPcmSameEvent || doprocessSigmaPlusPcmMixedEvent || doprocessSigmaMixedEventWithTagging || doprocessSigmaPlusMixedEventWithTagging || doprocessSigmaPlusPcmMixedEventWithTagging; + bool processMc = doprocessSigmaSameEventMc || doprocessSigmaMixedEventMc || doprocessSigmaPlusSameEventMc || doprocessSigmaPlusMixedEventMc || doprocessSigmaPlusPcmSameEventMc || doprocessSigmaPlusPcmMixedEventMc; if (processData && processMc) { LOG(fatal) << "Both data and mc processing is enabled. Breaking..."; } - bool processSigma = doprocessSigmaSameEvent || doprocessSigmaSameEventMc || doprocessSigmaMixedEvent || doprocessSigmaMixedEventMc; - bool processSigmaPlus = doprocessSigmaPlusSameEvent || doprocessSigmaPlusSameEventMc || doprocessSigmaPlusMixedEvent || doprocessSigmaPlusMixedEventMc; + bool processSigma = doprocessSigmaSameEvent || doprocessSigmaSameEventMc || doprocessSigmaMixedEvent || doprocessSigmaMixedEventMc || doprocessSigmaMixedEventWithTagging; + bool processSigmaPlus = doprocessSigmaPlusSameEvent || doprocessSigmaPlusSameEventMc || doprocessSigmaPlusMixedEvent || doprocessSigmaPlusMixedEventMc || doprocessSigmaPlusMixedEventWithTagging; + + bool processSigmaPlusPcm = doprocessSigmaPlusPcmSameEvent || doprocessSigmaPlusPcmSameEventMc || doprocessSigmaPlusPcmMixedEvent || doprocessSigmaPlusPcmMixedEventMc || doprocessSigmaPlusPcmMixedEventWithTagging; if (processSigma && processSigmaPlus) { LOG(fatal) << "Both Sigma-track and SigmaPlus-track processing is enabled. Breaking..."; } + if ((processSigma || processSigmaPlus) && processSigmaPlusPcm) { + LOG(fatal) << "Both Sigma/SigmaPlus-track and SigmaPlusPcm-track processing is enabled. Breaking..."; + } + + // Each species discovers its own tag bit independently (see Core/collisionTagger.h). + if (doprocessSigmaMixedEventWithTagging || doprocessSigmaPlusMixedEventWithTagging || doprocessSigmaPlusPcmMixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + if (doprocessSigmaMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingSigma.Role); + if (bit < 0) { + LOG(fatal) << "processSigmaMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackKink enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingSigma.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Sigma): " << bit; + } + if (doprocessSigmaPlusMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingSigmaPlus.Role); + if (bit < 0) { + LOG(fatal) << "processSigmaPlusMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackKink enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingSigmaPlus.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (SigmaPlus): " << bit; + } + if (doprocessSigmaPlusPcmMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingSigmaPlusPcm.Role); + if (bit < 0) { + LOG(fatal) << "processSigmaPlusPcmMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackSigmaPlusPcm enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingSigmaPlusPcm.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (SigmaPlusPcm): " << bit; + } + } // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; std::map> colHistSpec; std::map> trackHistSpec; std::map> chaDauSpec; std::map> sigmaHistSpec; std::map> sigmaPlusHistSpec; + std::map> sigmaPlusPcmHistSpec; std::map> pairTrackKinkHistSpec; std::map> cprHistSpec = closepairrejection::makeCprHistSpecMap(confCpr); std::map> pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); @@ -190,9 +277,12 @@ struct FemtoPairTrackKink { if (processSigma) { sigmaHistSpec = kinkhistmanager::makeKinkHistSpecMap(confSigmaBinning); pairTrackSigmaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confSigmaSelection, confSigmaCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, sigmaHistSpec, chaDauSpec, pairTrackKinkHistSpec, cprHistSpec, pairCleanerHistSpec); - } else { + } else if (processSigmaPlus) { sigmaPlusHistSpec = kinkhistmanager::makeKinkHistSpecMap(confSigmaPlusBinning); pairTrackSigmaPlusBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confSigmaPlusSelection, confSigmaPlusCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, sigmaPlusHistSpec, chaDauSpec, pairTrackKinkHistSpec, cprHistSpec, pairCleanerHistSpec); + } else { + sigmaPlusPcmHistSpec = kinkhistmanager::makeKinkHistSpecMap(confSigmaPlusPcmBinning); + pairTrackSigmaPlusPcmBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confSigmaPlusPcmSelection, confSigmaPlusPcmCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, sigmaPlusPcmHistSpec, chaDauSpec, pairTrackKinkHistSpec, cprHistSpec, pairCleanerHistSpec); } } else { colHistSpec = colhistmanager::makeColMcHistSpecMap(confCollisionBinning); @@ -202,9 +292,12 @@ struct FemtoPairTrackKink { if (processSigma) { sigmaHistSpec = kinkhistmanager::makeKinkMcHistSpecMap(confSigmaBinning); pairTrackSigmaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confSigmaSelection, confSigmaCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, sigmaHistSpec, chaDauSpec, pairTrackKinkHistSpec, cprHistSpec, pairCleanerHistSpec); - } else { + } else if (processSigmaPlus) { sigmaPlusHistSpec = kinkhistmanager::makeKinkMcHistSpecMap(confSigmaPlusBinning); pairTrackSigmaPlusBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confSigmaPlusSelection, confSigmaPlusCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, sigmaPlusHistSpec, chaDauSpec, pairTrackKinkHistSpec, cprHistSpec, pairCleanerHistSpec); + } else { + sigmaPlusPcmHistSpec = kinkhistmanager::makeKinkMcHistSpecMap(confSigmaPlusPcmBinning); + pairTrackSigmaPlusPcmBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confSigmaPlusPcmSelection, confSigmaPlusPcmCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, sigmaPlusPcmHistSpec, chaDauSpec, pairTrackKinkHistSpec, cprHistSpec, pairCleanerHistSpec); } } hRegistry.print(); @@ -222,6 +315,16 @@ struct FemtoPairTrackKink { } PROCESS_SWITCH(FemtoPairTrackKink, processSigmaSameEventMc, "Enable processing same event processing for tracks and sigmas with MC information", false); + // Opt-in counterpart of processSigmaMixedEvent() below -- same "Partition declared and bound + // inside process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. + void processSigmaMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoSigmas const& /*sigmas*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingSigma.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackSigmaBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition, sigmaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackKink, processSigmaMixedEventWithTagging, "Enable processing mixed event processing for tracks and sigmas, pre-filtered using the collision tagger", false); + void processSigmaMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoSigmas const& /*sigmas*/) { pairTrackSigmaBuilder.processMixedEvent(cols, tracks, trackPartition, sigmaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -246,6 +349,15 @@ struct FemtoPairTrackKink { } PROCESS_SWITCH(FemtoPairTrackKink, processSigmaPlusSameEventMc, "Enable processing same event processing for tracks and sigma plus with MC information", false); + // Opt-in counterpart of processSigmaPlusMixedEvent() below -- same idiom as processSigmaMixedEventWithTagging above. + void processSigmaPlusMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoSigmaPlus const& /*sigmaplus*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingSigmaPlus.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackSigmaPlusBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition, sigmaPlusPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackKink, processSigmaPlusMixedEventWithTagging, "Enable processing mixed event processing for tracks and sigma plus, pre-filtered using the collision tagger", false); + void processSigmaPlusMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoSigmaPlus const& /*sigmaplus*/) { pairTrackSigmaPlusBuilder.processMixedEvent(cols, tracks, trackPartition, sigmaPlusPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -257,6 +369,39 @@ struct FemtoPairTrackKink { pairTrackSigmaPlusBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, sigmaPlusWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoPairTrackKink, processSigmaPlusMixedEventMc, "Enable processing mixed event processing for tracks and sigma plus with MC information", false); + + void processSigmaPlusPcmSameEvent(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoSigmaPlusPcm const& sigmapluspcm) + { + pairTrackSigmaPlusPcmBuilder.processSameEvent(col, tracks, trackPartition, sigmapluspcm, sigmaPlusPcmPartition, cache); + } + PROCESS_SWITCH(FemtoPairTrackKink, processSigmaPlusPcmSameEvent, "Enable processing same event processing for tracks and sigma plus (PCM), selected on the mask and the BDT score", false); + + void processSigmaPlusPcmSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoSigmaPlusPcmWithLabel const& sigmapluspcm, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + { + pairTrackSigmaPlusPcmBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, sigmapluspcm, sigmaPlusPcmWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); + } + PROCESS_SWITCH(FemtoPairTrackKink, processSigmaPlusPcmSameEventMc, "Enable processing same event processing for tracks and sigma plus (PCM) with MC information", false); + + // Opt-in counterpart of processSigmaPlusPcmMixedEvent() below -- same idiom as processSigmaMixedEventWithTagging above. + void processSigmaPlusPcmMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoSigmaPlusPcm const& /*sigmapluspcm*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingSigmaPlusPcm.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackSigmaPlusPcmBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition, sigmaPlusPcmPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackKink, processSigmaPlusPcmMixedEventWithTagging, "Enable processing mixed event processing for tracks and sigma plus (PCM), pre-filtered using the collision tagger", false); + + void processSigmaPlusPcmMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoSigmaPlusPcm const& /*sigmapluspcm*/) + { + pairTrackSigmaPlusPcmBuilder.processMixedEvent(cols, tracks, trackPartition, sigmaPlusPcmPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackKink, processSigmaPlusPcmMixedEvent, "Enable processing mixed event processing for tracks and sigma plus (PCM), selected on the mask and the BDT score", false); + + void processSigmaPlusPcmMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoSigmaPlusPcmWithLabel const& /*sigmapluspcm*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + { + pairTrackSigmaPlusPcmBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, sigmaPlusPcmWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackKink, processSigmaPlusPcmMixedEventMc, "Enable processing mixed event processing for tracks and sigma plus (PCM) with MC information", false); }; o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) diff --git a/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx b/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx index 818db97e7eb..3bd8334ab7c 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx @@ -16,6 +16,8 @@ #include "PWGCF/Femto/Core/closePairRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" +#include "PWGCF/Femto/Core/dataTypes.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" @@ -32,11 +34,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -55,17 +59,40 @@ struct FemtoPairTrackTrack { using FilteredFemtoCollisionsWithLabel = o2::soa::Filtered; using FilteredFemtoCollisionWithLabel = FilteredFemtoCollisionsWithLabel::iterator; + // opt-in: carries FColTags (see TableProducer/femtoProducerCollisionTagger.cxx), only needed by + // processMixedEventWithTagging. collisionFilter below (declared on the base FemtoCollisions + // columns) auto-applies to this joined/filtered type too, exactly as it already does for + // FilteredFemtoCollisionsWithLabel above; the extra tag-bit condition is then applied via a + // Partition declared locally inside processMixedEventWithTagging itself (see there), the same + // "Partition declared and bound inside process()" idiom as Tutorials/src/eventMixing.cxx's + // MixedEventsPartitionedTracks -- so nothing here needs a second top-level Filter. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + // collision table with event shape, same filter as standard collisions using FemtoCollisionsWithEventShape = o2::soa::Join; using FilteredFemtoCollisionsWithEventShape = o2::soa::Filtered; using FilteredFemtoCollisionWithEventShape = FilteredFemtoCollisionsWithEventShape::iterator; + // opt-in: carries both FColShapes and FColTags, for processMixedEventWithTaggingAndEventShape -- + // same collisionFilter auto-apply reasoning as FemtoCollisionsWithTags above. + using FemtoCollisionsWithEventShapeAndTags = o2::soa::Join; + using FilteredFemtoCollisionsWithEventShapeAndTags = o2::soa::Filtered; + using FemtoTracks = o2::soa::Join; // for analysis which require particles at high pt, add tof mass so sidebands can be used using FemtoTracksWithMass = o2::soa::Join; - using FemtoTracksWithLabel = o2::soa::Join; + // opt-in: adds the packed purity tables (nSigma, see TrackSelection1/2.puritySlotMask) and FTrackDcas + // (fraction estimation) for the FitVars histograms. Needs the producer's + // TrackTables.produceItsPurity/produceTpcPurity/produceTofPurity/produceTrackDcas enabled. + using FemtoTracksWithFitVars = o2::soa::Join; + + // the Mc process functions always carry the purity/fraction tables (not opt-in like the data + // path's WithFitVars variant) -- requires the producer's TrackTables.produceItsPurity/ + // produceTpcPurity/produceTofPurity/produceTrackDcas enabled whenever MC processing is used + using FemtoTracksWithLabel = o2::soa::Join; using FemtoMcParticlesWithLabel = o2::soa::Join; @@ -76,6 +103,10 @@ struct FemtoPairTrackTrack { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by processMixedEventWithTagging -- see processMixedEventWithTagging for + // how it's used. + collisiontagger::ConfTagging confTagging; + // setup tracks trackbuilder::ConfTrackSelection1 confTrackSelections1; particlecleaner::ConfTrackCleaner1 confTrackCleaner1; @@ -93,6 +124,10 @@ struct FemtoPairTrackTrack { o2::framework::Partition trackWithMassPartition2 = MAKE_TRACK_PARTITION_WITH_MASS(confTrackSelections2); o2::framework::Preslice perColtracksWithMass = o2::aod::femtobase::stored::fColId; + o2::framework::Partition trackWithFitVarsPartition1 = MAKE_TRACK_PARTITION(confTrackSelections1); + o2::framework::Partition trackWithFitVarsPartition2 = MAKE_TRACK_PARTITION(confTrackSelections2); + o2::framework::Preslice perColtracksWithFitVars = o2::aod::femtobase::stored::fColId; + o2::framework::Partition trackWithLabelPartition1 = MAKE_TRACK_PARTITION(confTrackSelections1); o2::framework::Partition trackWithLabelPartition2 = MAKE_TRACK_PARTITION(confTrackSelections2); o2::framework::Preslice perColtracksWithLabel = o2::aod::femtobase::stored::fColId; @@ -105,8 +140,10 @@ struct FemtoPairTrackTrack { closepairrejection::ConfCprTrackTrack confCpr; pairbuilder::PairTrackTrackBuilder< - trackhistmanager::PrefixTrack1, - trackhistmanager::PrefixTrack2, + trackhistmanager::PrefixTrack1Se, + trackhistmanager::PrefixTrack2Se, + trackhistmanager::PrefixTrack1Me, + trackhistmanager::PrefixTrack2Me, pairhistmanager::PrefixTrackTrackSe, pairhistmanager::PrefixTrackTrackMe, closepairrejection::PrefixTrackTrackSe, @@ -118,32 +155,51 @@ struct FemtoPairTrackTrack { std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; std::vector defaultEventPlaneAngleBins{10, 0, o2::constants::math::TwoPI}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCentEventPlaneAngle{{defaultVtxBins, defaultCentBins, defaultEventPlaneAngleBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCentEventPlaneAngle{{defaultVtxBins, defaultCentBins, defaultEventPlaneAngleBins}}; pairhistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoTrackTrack", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - if ((static_cast(doprocessSameEvent) + static_cast(doprocessSameEventWithMass) + static_cast(doprocessSameEventWithEventShape) + static_cast(doprocessSameEventMc)) > 1 || - (static_cast(doprocessMixedEvent) + static_cast(doprocessMixedEventWithMass) + static_cast(doprocessMixedEventWithEventShape) + static_cast(doprocessMixedEventMc)) > 1) { + if ((static_cast(doprocessSameEvent) + static_cast(doprocessSameEventWithMass) + static_cast(doprocessSameEventWithEventShape) + static_cast(doprocessSameEventWithFitVars) + static_cast(doprocessSameEventMc)) > 1 || + (static_cast(doprocessMixedEvent) + static_cast(doprocessMixedEventWithMass) + static_cast(doprocessMixedEventWithEventShape) + static_cast(doprocessMixedEventWithFitVars) + static_cast(doprocessMixedEventMc) + static_cast(doprocessMixedEventWithTagging) + static_cast(doprocessMixedEventWithTaggingAndMass) + static_cast(doprocessMixedEventWithTaggingAndEventShape) + static_cast(doprocessMixedEventWithTaggingAndFitVars)) > 1) { LOG(fatal) << "More than 1 same or mixed event process function is activated. Breaking..."; } - bool processData = doprocessSameEvent || doprocessMixedEvent || doprocessSameEventWithMass || doprocessMixedEventWithMass || doprocessSameEventWithEventShape || doprocessMixedEventWithEventShape; + + if (doprocessMixedEventWithTagging || doprocessMixedEventWithTaggingAndMass || doprocessMixedEventWithTaggingAndEventShape || doprocessMixedEventWithTaggingAndFitVars) { + // discoverTaggedRoles() is the same canonical device-list walk femtoProducerCollisionTagger.cxx uses + // to assign bits -- replaying it here (rather than asking the tagger directly, which has no + // way to communicate a runtime-computed value back out through RunningWorkflowInfo) is how + // this task finds out which bit(s) ended up being its own. + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTagging.Role); + if (bit < 0) { + LOG(fatal) << "A processMixedEventWithTagging* function is enabled, but no femto-collision-tagger " + << "device (with processTrackTrack enabled) tagging this device (" << ownDevice.name + << ") was found in this workflow."; + } + confTagging.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << ": " << bit; + } + bool processData = doprocessSameEvent || doprocessMixedEvent || doprocessSameEventWithMass || doprocessMixedEventWithMass || doprocessSameEventWithEventShape || doprocessMixedEventWithEventShape || doprocessMixedEventWithTagging || doprocessMixedEventWithTaggingAndMass || doprocessMixedEventWithTaggingAndEventShape; + bool processFitVars = doprocessSameEventWithFitVars || doprocessMixedEventWithFitVars || doprocessMixedEventWithTaggingAndFitVars; bool processMc = doprocessSameEventMc || doprocessMixedEventMc; - if (processData && processMc) { - LOG(fatal) << "Both data and mc processing is activated. Breaking..."; + if (static_cast(processData) + static_cast(processFitVars) + static_cast(processMc) > 1) { + LOG(fatal) << "More than one of data, purity or mc processing is activated. Breaking..."; } // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; - mixBinsVtxCentEventPlaneAngle = {{confMixing.vtxBins.value, confMixing.centBins.value, confMixing.eventPlaneAngle.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; + mixBinsVtxCentEventPlaneAngle = {{confMixing.vtxBins.value, confMixing.centBins.value, confMixing.eventPlaneAngle.value}}; // setup histogram specs std::map> colHistSpec; @@ -153,7 +209,13 @@ struct FemtoPairTrackTrack { std::map> cprHistSpec = closepairrejection::makeCprHistSpecMap(confCpr); std::map> pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); - if (processData) { + if (processFitVars) { + colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); + trackHistSpec1 = trackhistmanager::makeTrackHistSpecMap(confTrackBinning1); + trackHistSpec2 = trackhistmanager::makeTrackHistSpecMap(confTrackBinning2); + pairHistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); + pairTrackTrackBuilder.init(&hRegistry, confCollisionBinning, confTrackSelections1, confTrackSelections2, confTrackCleaner1, confTrackCleaner2, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec1, trackHistSpec2, pairHistSpec, cprHistSpec, pairCleanerHistSpec); + } else if (processData) { colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); trackHistSpec1 = trackhistmanager::makeTrackHistSpecMap(confTrackBinning1); trackHistSpec2 = trackhistmanager::makeTrackHistSpecMap(confTrackBinning2); @@ -164,7 +226,7 @@ struct FemtoPairTrackTrack { trackHistSpec1 = trackhistmanager::makeTrackMcHistSpecMap(confTrackBinning1); trackHistSpec2 = trackhistmanager::makeTrackMcHistSpecMap(confTrackBinning2); pairHistSpec = pairhistmanager::makePairMcHistSpecMap(confPairBinning, confMixing); - pairTrackTrackBuilder.init(&hRegistry, confCollisionBinning, confTrackSelections1, confTrackSelections2, confTrackCleaner1, confTrackCleaner2, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec1, trackHistSpec2, pairHistSpec, cprHistSpec, pairCleanerHistSpec); + pairTrackTrackBuilder.init(&hRegistry, confCollisionBinning, confTrackSelections1, confTrackSelections2, confTrackCleaner1, confTrackCleaner2, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec1, trackHistSpec2, pairHistSpec, cprHistSpec, pairCleanerHistSpec); } hRegistry.print(); }; @@ -181,6 +243,12 @@ struct FemtoPairTrackTrack { } PROCESS_SWITCH(FemtoPairTrackTrack, processSameEventWithMass, "Enable processing same event processing (with track masses)", false); + void processSameEventWithFitVars(FilteredFemtoCollision const& col, FemtoTracksWithFitVars const& tracks) + { + pairTrackTrackBuilder.processSameEvent(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache); + } + PROCESS_SWITCH(FemtoPairTrackTrack, processSameEventWithFitVars, "Enable processing same event processing (with FitVars tables)", false); + void processSameEventWithEventShape(FilteredFemtoCollisionWithEventShape const& col, FemtoTracks const& tracks) { pairTrackTrackBuilder.processSameEvent(col, tracks, trackPartition1, trackPartition2, cache); @@ -189,10 +257,59 @@ struct FemtoPairTrackTrack { void processSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackTrackBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition1, trackWithLabelPartition2, mcParticles, mcMothers, mcPartonicMothers, cache); + pairTrackTrackBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition1, trackWithLabelPartition2, mcParticles, mcMothers, mcPartonicMothers, cache); } PROCESS_SWITCH(FemtoPairTrackTrack, processSameEventMc, "Enable processing same event processing", false); + // Opt-in counterpart of processMixedEvent() below: `cols` already gets the standard + // collisionFilter applied (Filtered picks it up automatically, exactly + // as FilteredFemtoCollisionsWithLabel above does). On top of that, a Partition is declared and + // bound right here, locally, to pick out only the collisions the tagger found a qualifying pair + // in -- the same "declare + bindTable() inside process()" idiom as + // Tutorials/src/eventMixing.cxx's MixedEventsPartitionedTracks, so this needs no change to + // pairProcessHelpers.h/pairBuilder.h: processMixedEvent is called exactly as below, just with a + // further-filtered collision view. Requires a femto-collision-tagger device (processTrackTrack + // enabled) in the same workflow -- see init(). + void processMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTagging.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackTrackBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition1, trackPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent, mixBinsVtxCentEventPlaneAngle); + } + PROCESS_SWITCH(FemtoPairTrackTrack, processMixedEventWithTagging, "Enable processing mixed event processing, pre-filtered using the collision tagger", false); + + // Same tagging filter as processMixedEventWithTagging above, carrying FemtoTracksWithMass + // instead of plain FemtoTracks -- the tag bit means the same thing regardless of which track + // table variant is used downstream, since it's computed from the same TrackSelection1/2 cut + // values either way. + void processMixedEventWithTaggingAndMass(FilteredFemtoCollisionsWithTags const& cols, FemtoTracksWithMass const& tracks) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTagging.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackTrackBuilder.processMixedEvent(taggedCollisions, tracks, trackWithMassPartition1, trackWithMassPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent, mixBinsVtxCentEventPlaneAngle); + } + PROCESS_SWITCH(FemtoPairTrackTrack, processMixedEventWithTaggingAndMass, "Enable processing mixed event processing, pre-filtered using the collision tagger (with track masses)", false); + + // Same tagging filter as processMixedEventWithTagging above, on a collision table that also + // carries FColShapes, needed for the kVtxCentEventPlaneAngle mixing policy. + void processMixedEventWithTaggingAndEventShape(FilteredFemtoCollisionsWithEventShapeAndTags const& cols, FemtoTracks const& tracks) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTagging.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackTrackBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition1, trackPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent, mixBinsVtxCentEventPlaneAngle); + } + PROCESS_SWITCH(FemtoPairTrackTrack, processMixedEventWithTaggingAndEventShape, "Enable processing mixed event processing, pre-filtered using the collision tagger, with event shape information", false); + + // Same tagging filter as processMixedEventWithTagging above, carrying FemtoTracksWithFitVars + // instead of plain FemtoTracks -- same rationale as processMixedEventWithTaggingAndMass above. + void processMixedEventWithTaggingAndFitVars(FilteredFemtoCollisionsWithTags const& cols, FemtoTracksWithFitVars const& tracks) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTagging.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackTrackBuilder.processMixedEvent(taggedCollisions, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent, mixBinsVtxCentEventPlaneAngle); + } + PROCESS_SWITCH(FemtoPairTrackTrack, processMixedEventWithTaggingAndFitVars, "Enable processing mixed event processing, pre-filtered using the collision tagger (with FitVars tables)", false); + void processMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks) { pairTrackTrackBuilder.processMixedEvent(cols, tracks, trackPartition1, trackPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent, mixBinsVtxCentEventPlaneAngle); @@ -205,6 +322,12 @@ struct FemtoPairTrackTrack { } PROCESS_SWITCH(FemtoPairTrackTrack, processMixedEventWithMass, "Enable processing mixed event processing (with track masses)", false); + void processMixedEventWithFitVars(FilteredFemtoCollisions const& cols, FemtoTracksWithFitVars const& tracks) + { + pairTrackTrackBuilder.processMixedEvent(cols, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent, mixBinsVtxCentEventPlaneAngle); + } + PROCESS_SWITCH(FemtoPairTrackTrack, processMixedEventWithFitVars, "Enable processing mixed event processing (with FitVars tables)", false); + void processMixedEventWithEventShape(FilteredFemtoCollisionsWithEventShape const& cols, FemtoTracks const& tracks) { pairTrackTrackBuilder.processMixedEvent(cols, tracks, trackPartition1, trackPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent, mixBinsVtxCentEventPlaneAngle); @@ -213,7 +336,7 @@ struct FemtoPairTrackTrack { void processMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackTrackBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition1, trackWithLabelPartition2, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent, mixBinsVtxCentEventPlaneAngle); + pairTrackTrackBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition1, trackWithLabelPartition2, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent, mixBinsVtxCentEventPlaneAngle); } PROCESS_SWITCH(FemtoPairTrackTrack, processMixedEventMc, "Enable processing mixed event processing", false); }; diff --git a/PWGCF/Femto/Tasks/femtoPairTrackTwoPhotonResonance.cxx b/PWGCF/Femto/Tasks/femtoPairTrackTwoPhotonResonance.cxx new file mode 100644 index 00000000000..b2ee1418fd4 --- /dev/null +++ b/PWGCF/Femto/Tasks/femtoPairTrackTwoPhotonResonance.cxx @@ -0,0 +1,273 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoPairTrackTwoPhotonResonance.cxx +/// \brief Task that computes correlation between tracks and resonances decaying into two photons (pi0, eta) +/// \author Anton Riedel, TU München, anton.riedel@cern.ch + +#include "PWGCF/Femto/Core/closePairRejection.h" +#include "PWGCF/Femto/Core/collisionBuilder.h" +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" +#include "PWGCF/Femto/Core/dataTypes.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/pairBuilder.h" +#include "PWGCF/Femto/Core/pairCleaner.h" +#include "PWGCF/Femto/Core/pairHistManager.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/Core/photonHistManager.h" +#include "PWGCF/Femto/Core/trackBuilder.h" +#include "PWGCF/Femto/Core/trackHistManager.h" +#include "PWGCF/Femto/Core/twoPhotonResonanceBuilder.h" +#include "PWGCF/Femto/Core/twoPhotonResonanceHistManager.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace o2::analysis::femto; + +struct FemtoPairTrackTwoPhotonResonance { + + // setup tables + using Collisions = o2::soa::Join; + using Collision = Collisions::iterator; + + using FilteredCollisions = o2::soa::Filtered; + using FilteredCollision = FilteredCollisions::iterator; + + using FemtoTracks = o2::soa::Join; + using FemtoPhotons = o2::soa::Join; + using FemtoPi0s = o2::soa::Join; + using FemtoEtas = o2::soa::Join; + + // opt-in: carries FColTags, only needed by the processXxxMixedEventWithTagging functions below. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + + o2::framework::SliceCache cache; + + // setup collisions + collisionbuilder::ConfCollisionSelection collisionSelection; + o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); + colhistmanager::ConfCollisionBinning confCollisionBinning; + + // opt-in, only needed by the processXxxMixedEventWithTagging functions below -- two separate + // instances since Pi0/Eta are mutually exclusive on one instance, but tagged independently + // (see init()). + collisiontagger::ConfTagging confTaggingPi0; + collisiontagger::ConfTagging confTaggingEta; + + // setup tracks + trackbuilder::ConfTrackSelection1 trackSelection; + trackhistmanager::ConfTrackBinning1 confTrackBinning; + o2::framework::Partition trackPartition = MAKE_TRACK_PARTITION(trackSelection); + o2::framework::Preslice perColTracks = o2::aod::femtobase::stored::fColId; + + // setup for photon daughters (shared between pi0 and eta -- unordered, no pos/neg or species asymmetry) + photonhistmanager::ConfPhotonBinning confPhotonDauBinning; + photonhistmanager::ConfPhotonQaBinning confPhotonDauQaBinning; + + // setup pi0s + twophotonresonancebuilder::ConfPi0Selection pi0Selection; + twophotonresonancehistmanager::ConfPi0Binning confPi0Binning; + o2::framework::Partition pi0Partition = MAKE_TWOPHOTONRESONANCE_PARTITION(pi0Selection); + o2::framework::Preslice perColPi0s = o2::aod::femtobase::stored::fColId; + + // setup etas + twophotonresonancebuilder::ConfEtaSelection etaSelection; + twophotonresonancehistmanager::ConfEtaBinning confEtaBinning; + o2::framework::Partition etaPartition = MAKE_TWOPHOTONRESONANCE_PARTITION(etaSelection); + o2::framework::Preslice perColEtas = o2::aod::femtobase::stored::fColId; + + // setup pairs + pairhistmanager::ConfPairBinning confPairBinning; + pairhistmanager::ConfPairCuts confPairCuts; + paircleaner::ConfPairCleanerBinning confPairCleaner; + + // setup for track-pi0 pairs + pairbuilder::PairTrackTwoPhotonResonanceBuilder< + trackhistmanager::PrefixTrack1Se, + twophotonresonancehistmanager::PrefixPi0Se, + photonhistmanager::PrefixTwoPhotonResonanceDau1Se, + photonhistmanager::PrefixTwoPhotonResonanceDau2Se, + trackhistmanager::PrefixTrack1Me, + twophotonresonancehistmanager::PrefixPi0Me, + photonhistmanager::PrefixTwoPhotonResonanceDau1Me, + photonhistmanager::PrefixTwoPhotonResonanceDau2Me, + pairhistmanager::PrefixTrackPi0Se, + pairhistmanager::PrefixTrackPi0Me, + closepairrejection::PrefixTrackTwoPhotonResonanceSe, + closepairrejection::PrefixTrackTwoPhotonResonanceMe, + modes::TwoPhotonResonance::kPi0> + pairTrackPi0Builder; + + // setup for track-eta pairs + pairbuilder::PairTrackTwoPhotonResonanceBuilder< + trackhistmanager::PrefixTrack1Se, + twophotonresonancehistmanager::PrefixEtaSe, + photonhistmanager::PrefixTwoPhotonResonanceDau1Se, + photonhistmanager::PrefixTwoPhotonResonanceDau2Se, + trackhistmanager::PrefixTrack1Me, + twophotonresonancehistmanager::PrefixEtaMe, + photonhistmanager::PrefixTwoPhotonResonanceDau1Me, + photonhistmanager::PrefixTwoPhotonResonanceDau2Me, + pairhistmanager::PrefixTrackEtaSe, + pairhistmanager::PrefixTrackEtaMe, + closepairrejection::PrefixTrackTwoPhotonResonanceSe, + closepairrejection::PrefixTrackTwoPhotonResonanceMe, + modes::TwoPhotonResonance::kEta> + pairTrackEtaBuilder; + + // setup mixing + std::vector defaultVtxBins{10, -10, 10}; + std::vector defaultMultBins{50, 0, 200}; + std::vector defaultCentBins{10, 0, 100}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; + pairhistmanager::ConfMixing confMixing; + + o2::framework::HistogramRegistry hRegistry{"FemtoTrackTwoPhotonResonance", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + + // setup cpr (no-op for two-photon resonances: PCM photon daughters are not indexed into the track + // table, so there is no trajectory to compare against a track's -- see closePairRejection.h) + closepairrejection::ConfCprTrackTwoPhotonResonanceDaughter confCpr; + + void init(o2::framework::InitContext& context) + { + + if (static_cast(doprocessPi0SameEvent || doprocessPi0MixedEvent || doprocessPi0MixedEventWithTagging) + + static_cast(doprocessEtaSameEvent || doprocessEtaMixedEvent || doprocessEtaMixedEventWithTagging) > + 1) { + LOG(fatal) << "Can only process SE/ME for pi0-tracks or eta-tracks"; + } + + // Each species discovers its own tag bit independently (see Core/collisionTagger.h). + if (doprocessPi0MixedEventWithTagging || doprocessEtaMixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + if (doprocessPi0MixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingPi0.Role); + if (bit < 0) { + LOG(fatal) << "processPi0MixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackTwoPhotonResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingPi0.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Pi0): " << bit; + } + if (doprocessEtaMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingEta.Role); + if (bit < 0) { + LOG(fatal) << "processEtaMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackTwoPhotonResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingEta.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Eta): " << bit; + } + } + + // setup columnpolicy for binning + // default values are used during instantiation, so we need to explicity update them here + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; + + // setup histograms + auto colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); + auto trackHistSpec = trackhistmanager::makeTrackHistSpecMap(confTrackBinning); + auto dauHistSpec = photonhistmanager::makePhotonHistSpecMap(confPhotonDauBinning); + auto cprHistSpec = closepairrejection::makeCprHistSpecMap(confCpr); + auto pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); + + // setup for pi0 + if (doprocessPi0SameEvent || doprocessPi0MixedEvent || doprocessPi0MixedEventWithTagging) { + auto pi0HistSpec = twophotonresonancehistmanager::makeTwoPhotonResonanceHistSpecMap(confPi0Binning); + auto pairTrackPi0HistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); + pairTrackPi0Builder.init(&hRegistry, confCollisionBinning, trackSelection, pi0Selection, confCpr, confMixing, confPairBinning, confPairCuts, confPhotonDauQaBinning, colHistSpec, trackHistSpec, pi0HistSpec, dauHistSpec, pairTrackPi0HistSpec, cprHistSpec, pairCleanerHistSpec); + } + + // setup for eta + if (doprocessEtaSameEvent || doprocessEtaMixedEvent || doprocessEtaMixedEventWithTagging) { + auto etaHistSpec = twophotonresonancehistmanager::makeTwoPhotonResonanceHistSpecMap(confEtaBinning); + auto pairTrackEtaHistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); + pairTrackEtaBuilder.init(&hRegistry, confCollisionBinning, trackSelection, etaSelection, confCpr, confMixing, confPairBinning, confPairCuts, confPhotonDauQaBinning, colHistSpec, trackHistSpec, etaHistSpec, dauHistSpec, pairTrackEtaHistSpec, cprHistSpec, pairCleanerHistSpec); + } + + hRegistry.print(); + }; + + void processPi0SameEvent(FilteredCollision const& col, FemtoTracks const& /*tracks*/, FemtoPi0s const& /*pi0s*/, FemtoPhotons const& photons) + { + pairTrackPi0Builder.processSameEvent(col, trackPartition, pi0Partition, photons, cache); + } + PROCESS_SWITCH(FemtoPairTrackTwoPhotonResonance, processPi0SameEvent, "Enable same event processing for tracks and pi0s", true); + + // Opt-in counterpart of processPi0MixedEvent() below -- same "Partition declared and bound + // inside process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. + void processPi0MixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& /*tracks*/, FemtoPi0s const& /*pi0s*/, FemtoPhotons const& photons) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingPi0.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackPi0Builder.processMixedEvent(taggedCollisions, trackPartition, pi0Partition, photons, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackTwoPhotonResonance, processPi0MixedEventWithTagging, "Enable mixed event processing for tracks and pi0s, pre-filtered using the collision tagger", false); + + void processPi0MixedEvent(FilteredCollisions const& cols, FemtoTracks const& /*tracks*/, FemtoPi0s const& /*pi0s*/, FemtoPhotons const& photons) + { + pairTrackPi0Builder.processMixedEvent(cols, trackPartition, pi0Partition, photons, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackTwoPhotonResonance, processPi0MixedEvent, "Enable mixed event processing for tracks and pi0s", true); + + void processEtaSameEvent(FilteredCollision const& col, FemtoTracks const& /*tracks*/, FemtoEtas const& /*etas*/, FemtoPhotons const& photons) + { + pairTrackEtaBuilder.processSameEvent(col, trackPartition, etaPartition, photons, cache); + } + PROCESS_SWITCH(FemtoPairTrackTwoPhotonResonance, processEtaSameEvent, "Enable same event processing for tracks and etas", false); + + // Opt-in counterpart of processEtaMixedEvent() below -- same idiom as + // processPi0MixedEventWithTagging above. + void processEtaMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& /*tracks*/, FemtoEtas const& /*etas*/, FemtoPhotons const& photons) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingEta.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackEtaBuilder.processMixedEvent(taggedCollisions, trackPartition, etaPartition, photons, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackTwoPhotonResonance, processEtaMixedEventWithTagging, "Enable mixed event processing for tracks and etas, pre-filtered using the collision tagger", false); + + void processEtaMixedEvent(FilteredCollisions const& cols, FemtoTracks const& /*tracks*/, FemtoEtas const& /*etas*/, FemtoPhotons const& photons) + { + pairTrackEtaBuilder.processMixedEvent(cols, trackPartition, etaPartition, photons, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackTwoPhotonResonance, processEtaMixedEvent, "Enable mixed event processing for tracks and etas", false); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{ + adaptAnalysisTask(context), + }; + return workflow; +} diff --git a/PWGCF/Femto/Tasks/femtoPairTrackTwoTrackResonance.cxx b/PWGCF/Femto/Tasks/femtoPairTrackTwoTrackResonance.cxx index 5197f851bcb..01c9d41b769 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackTwoTrackResonance.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackTwoTrackResonance.cxx @@ -16,6 +16,8 @@ #include "PWGCF/Femto/Core/closePairRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" +#include "PWGCF/Femto/Core/dataTypes.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" @@ -32,10 +34,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -57,6 +61,10 @@ struct FemtoPairTrackTwoTrackResonance { using FemtoKstar0s = o2::soa::Join; using FemtoRho0s = o2::soa::Join; + // opt-in: carries FColTags, only needed by the processXxxMixedEventWithTagging functions below. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + o2::framework::SliceCache cache; // setup collisions @@ -64,6 +72,13 @@ struct FemtoPairTrackTwoTrackResonance { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by the processXxxMixedEventWithTagging functions below -- three separate + // instances since Phi/Kstar0/Rho0 are mutually exclusive on one instance, but tagged + // independently (see init()). + collisiontagger::ConfTagging confTaggingPhi; + collisiontagger::ConfTagging confTaggingKstar0; + collisiontagger::ConfTagging confTaggingRho0; + // setup tracks trackbuilder::ConfTrackSelection1 trackSelection; trackhistmanager::ConfTrackBinning1 confTrackBinning; @@ -99,10 +114,14 @@ struct FemtoPairTrackTwoTrackResonance { // setup for track-phi pairs pairbuilder::PairTrackTwoTrackResonanceBuilder< - trackhistmanager::PrefixTrack1, - twotrackresonancehistmanager::PrefixPhi, - trackhistmanager::PrefixResonancePosDaughter, - trackhistmanager::PrefixResonanceNegDaughter, + trackhistmanager::PrefixTrack1Se, + twotrackresonancehistmanager::PrefixPhiSe, + trackhistmanager::PrefixResonancePosDaughterSe, + trackhistmanager::PrefixResonanceNegDaughterSe, + trackhistmanager::PrefixTrack1Me, + twotrackresonancehistmanager::PrefixPhiMe, + trackhistmanager::PrefixResonancePosDaughterMe, + trackhistmanager::PrefixResonanceNegDaughterMe, pairhistmanager::PrefixTrackResonanceSe, pairhistmanager::PrefixTrackResonanceMe, closepairrejection::PrefixTrackTwoTrackResonanceSe, @@ -112,10 +131,14 @@ struct FemtoPairTrackTwoTrackResonance { // setup for track-kstar0 pairs pairbuilder::PairTrackTwoTrackResonanceBuilder< - trackhistmanager::PrefixTrack1, - twotrackresonancehistmanager::PrefixKstar, - trackhistmanager::PrefixResonancePosDaughter, - trackhistmanager::PrefixResonanceNegDaughter, + trackhistmanager::PrefixTrack1Se, + twotrackresonancehistmanager::PrefixKstarSe, + trackhistmanager::PrefixResonancePosDaughterSe, + trackhistmanager::PrefixResonanceNegDaughterSe, + trackhistmanager::PrefixTrack1Me, + twotrackresonancehistmanager::PrefixKstarMe, + trackhistmanager::PrefixResonancePosDaughterMe, + trackhistmanager::PrefixResonanceNegDaughterMe, pairhistmanager::PrefixTrackResonanceSe, pairhistmanager::PrefixTrackResonanceMe, closepairrejection::PrefixTrackTwoTrackResonanceSe, @@ -125,10 +148,14 @@ struct FemtoPairTrackTwoTrackResonance { // setup for track-rho0 pairs pairbuilder::PairTrackTwoTrackResonanceBuilder< - trackhistmanager::PrefixTrack1, - twotrackresonancehistmanager::PrefixRho, - trackhistmanager::PrefixResonancePosDaughter, - trackhistmanager::PrefixResonanceNegDaughter, + trackhistmanager::PrefixTrack1Se, + twotrackresonancehistmanager::PrefixRhoSe, + trackhistmanager::PrefixResonancePosDaughterSe, + trackhistmanager::PrefixResonanceNegDaughterSe, + trackhistmanager::PrefixTrack1Me, + twotrackresonancehistmanager::PrefixRhoMe, + trackhistmanager::PrefixResonancePosDaughterMe, + trackhistmanager::PrefixResonanceNegDaughterMe, pairhistmanager::PrefixTrackResonanceSe, pairhistmanager::PrefixTrackResonanceMe, closepairrejection::PrefixTrackTwoTrackResonanceSe, @@ -140,9 +167,9 @@ struct FemtoPairTrackTwoTrackResonance { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; pairhistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoTrackTwoTrackResonance", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; @@ -150,18 +177,55 @@ struct FemtoPairTrackTwoTrackResonance { // setup cpr closepairrejection::ConfCprTrackResonanceDaughter confCpr; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - if ((static_cast(doprocessPhiSameEvent || doprocessPhiMixedEvent) + static_cast(doprocessKstar0SameEvent || doprocessKstar0MixedEvent)) + static_cast(doprocessRho0SameEvent || doprocessRho0MixedEvent) > 1) { + if ((static_cast(doprocessPhiSameEvent || doprocessPhiMixedEvent || doprocessPhiMixedEventWithTagging) + + static_cast(doprocessKstar0SameEvent || doprocessKstar0MixedEvent || doprocessKstar0MixedEventWithTagging)) + + static_cast(doprocessRho0SameEvent || doprocessRho0MixedEvent || doprocessRho0MixedEventWithTagging) > + 1) { LOG(fatal) << "Can only process SE/ME for phi-tracks, rho-tracks or k0*-tracks"; } + // Each species discovers its own tag bit independently (see Core/collisionTagger.h). + if (doprocessPhiMixedEventWithTagging || doprocessKstar0MixedEventWithTagging || doprocessRho0MixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + if (doprocessPhiMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingPhi.Role); + if (bit < 0) { + LOG(fatal) << "processPhiMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackTwoTrackResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingPhi.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Phi): " << bit; + } + if (doprocessKstar0MixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingKstar0.Role); + if (bit < 0) { + LOG(fatal) << "processKstar0MixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackTwoTrackResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingKstar0.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Kstar0): " << bit; + } + if (doprocessRho0MixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingRho0.Role); + if (bit < 0) { + LOG(fatal) << "processRho0MixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackTwoTrackResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingRho0.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Rho0): " << bit; + } + } + // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; // setup histograms auto colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); @@ -172,21 +236,21 @@ struct FemtoPairTrackTwoTrackResonance { auto pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); // setup for phi - if (doprocessPhiSameEvent || doprocessPhiMixedEvent) { + if (doprocessPhiSameEvent || doprocessPhiMixedEvent || doprocessPhiMixedEventWithTagging) { auto phiHistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confPhiBinning); auto pairTrackPhiHistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); pairTrackPhiBuilder.init(&hRegistry, confCollisionBinning, trackSelection, phiSelection, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, phiHistSpec, posDauSpec, negDauSpec, pairTrackPhiHistSpec, cprHistSpec, pairCleanerHistSpec); } // setup for kstar0 - if (doprocessKstar0SameEvent || doprocessKstar0MixedEvent) { + if (doprocessKstar0SameEvent || doprocessKstar0MixedEvent || doprocessKstar0MixedEventWithTagging) { auto kstar0HistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confKstar0Binning); auto pairTrackKstar0HistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); pairTrackKstar0Builder.init(&hRegistry, confCollisionBinning, trackSelection, kstar0Selection, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, kstar0HistSpec, posDauSpec, negDauSpec, pairTrackKstar0HistSpec, cprHistSpec, pairCleanerHistSpec); } // setup for kstar0 - if (doprocessRho0SameEvent || doprocessRho0MixedEvent) { + if (doprocessRho0SameEvent || doprocessRho0MixedEvent || doprocessRho0MixedEventWithTagging) { auto rho0HistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confRho0Binning); auto pairTrackRho0HistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); pairTrackRho0Builder.init(&hRegistry, confCollisionBinning, trackSelection, rho0Selection, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, rho0HistSpec, posDauSpec, negDauSpec, pairTrackRho0HistSpec, cprHistSpec, pairCleanerHistSpec); @@ -201,6 +265,16 @@ struct FemtoPairTrackTwoTrackResonance { } PROCESS_SWITCH(FemtoPairTrackTwoTrackResonance, processPhiSameEvent, "Enable processing same event processing for tracks and phis", true); + // Opt-in counterpart of processPhiMixedEvent() below -- same "Partition declared and bound + // inside process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. + void processPhiMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoPhis const& /*phis*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingPhi.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackPhiBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition, phiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackTwoTrackResonance, processPhiMixedEventWithTagging, "Enable processing mixed event processing for tracks and phis, pre-filtered using the collision tagger", false); + void processPhiMixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoPhis const& /*phis*/) { pairTrackPhiBuilder.processMixedEvent(cols, tracks, trackPartition, phiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -213,6 +287,16 @@ struct FemtoPairTrackTwoTrackResonance { } PROCESS_SWITCH(FemtoPairTrackTwoTrackResonance, processKstar0SameEvent, "Enable processing same event processing for tracks and kstar0s", false); + // Opt-in counterpart of processKstar0MixedEvent() below -- same idiom as + // processPhiMixedEventWithTagging above. + void processKstar0MixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoKstar0s const& /*kstar0s*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingKstar0.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackKstar0Builder.processMixedEvent(taggedCollisions, tracks, trackPartition, kstar0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackTwoTrackResonance, processKstar0MixedEventWithTagging, "Enable processing mixed event processing for tracks and kstar0s, pre-filtered using the collision tagger", false); + void processKstar0MixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoKstar0s const& /*kstar0s*/) { pairTrackKstar0Builder.processMixedEvent(cols, tracks, trackPartition, kstar0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -225,6 +309,16 @@ struct FemtoPairTrackTwoTrackResonance { } PROCESS_SWITCH(FemtoPairTrackTwoTrackResonance, processRho0SameEvent, "Enable processing same event processing for tracks and rho0s", false); + // Opt-in counterpart of processRho0MixedEvent() below -- same idiom as + // processPhiMixedEventWithTagging above. + void processRho0MixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoRho0s const& /*rho0s*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingRho0.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackRho0Builder.processMixedEvent(taggedCollisions, tracks, trackPartition, rho0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackTwoTrackResonance, processRho0MixedEventWithTagging, "Enable processing mixed event processing for tracks and rho0s, pre-filtered using the collision tagger", false); + void processRho0MixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoRho0s const& /*rho0s*/) { pairTrackRho0Builder.processMixedEvent(cols, tracks, trackPartition, rho0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); diff --git a/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx b/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx index 224567da38e..d7af5e129dd 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx @@ -16,6 +16,8 @@ #include "PWGCF/Femto/Core/closePairRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" +#include "PWGCF/Femto/Core/dataTypes.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" @@ -33,11 +35,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -56,13 +60,24 @@ struct FemtoPairTrackV0 { using FilteredFemtoCollisionsWithLabel = o2::soa::Filtered; using FilteredFemtoCollisionWithLabel = FilteredFemtoCollisionsWithLabel::iterator; + // opt-in: carries FColTags (see TableProducer/femtoProducerCollisionTagger.cxx), only needed by + // processLambdaMixedEventWithTagging/processK0shortMixedEventWithTagging -- same + // collisionFilter auto-apply reasoning as in femtoPairTrackTrack.cxx. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + using FemtoTracks = o2::soa::Join; using FemtoLambdas = o2::soa::Join; using FemtoK0shorts = o2::soa::Join; - using FemtoTracksWithLabel = o2::soa::Join; - using FemtoLambdasWithLabel = o2::soa::Join; - using FemtoK0shortsWithLabel = o2::soa::Join; + using FemtoTracksWithLabel = o2::soa::Join; + using FemtoLambdasWithLabel = o2::soa::Join; + using FemtoK0shortsWithLabel = o2::soa::Join; + + // reco-only + FitVars (no MC labels, but carries the opt-in purity/fraction tables) + using FemtoTracksWithFitVars = o2::soa::Join; + using FemtoLambdasWithFitVars = o2::soa::Join; + using FemtoK0shortsWithFitVars = o2::soa::Join; using FemtoMcParticlesWithLabel = o2::soa::Join; @@ -73,6 +88,12 @@ struct FemtoPairTrackV0 { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by processLambdaMixedEventWithTagging/processK0shortMixedEventWithTagging + // -- two separate instances since Lambda and K0short are tagged independently (see init()) and + // each gets its own tag bit. + collisiontagger::ConfTagging confTaggingLambda; + collisiontagger::ConfTagging confTaggingK0short; + // setup tracks trackbuilder::ConfTrackSelection1 confTrackSelection; trackhistmanager::ConfTrackBinning1 confTrackBinning; @@ -84,6 +105,9 @@ struct FemtoPairTrackV0 { o2::framework::Partition trackWithLabelPartition = MAKE_TRACK_PARTITION(confTrackSelection); o2::framework::Preslice perColtracksWithLabel = o2::aod::femtobase::stored::fColId; + o2::framework::Partition trackWithFitVarsPartition = MAKE_TRACK_PARTITION(confTrackSelection); + o2::framework::Preslice perColTracksWithFitVars = o2::aod::femtobase::stored::fColId; + // setup for daughters trackhistmanager::ConfV01PosDauBinning confPosDauBinning; trackhistmanager::ConfV01NegDauBinning confNegDauBinning; @@ -99,6 +123,9 @@ struct FemtoPairTrackV0 { o2::framework::Partition lambdaWithLabelPartition = MAKE_LAMBDA_PARTITION(lambdaSelection); o2::framework::Preslice perColLambdasWithLabel = o2::aod::femtobase::stored::fColId; + o2::framework::Partition lambdaWithFitVarsPartition = MAKE_LAMBDA_PARTITION(lambdaSelection); + o2::framework::Preslice perColLambdasWithFitVars = o2::aod::femtobase::stored::fColId; + // setup k0shorts v0builder::ConfK0shortSelection1 k0shortSelection; v0histmanager::ConfK0shortBinning1 confK0shortBinning; @@ -110,16 +137,23 @@ struct FemtoPairTrackV0 { o2::framework::Partition k0shortWithLabelPartition = MAKE_K0SHORT_PARTITION(k0shortSelection); o2::framework::Preslice perColk0shortsWithLabel = o2::aod::femtobase::stored::fColId; + o2::framework::Partition k0shortWithFitVarsPartition = MAKE_K0SHORT_PARTITION(k0shortSelection); + o2::framework::Preslice perColk0shortsWithFitVars = o2::aod::femtobase::stored::fColId; + // setup pairs pairhistmanager::ConfPairBinning confPairBinning; pairhistmanager::ConfPairCuts confPairCuts; paircleaner::ConfPairCleanerBinning confPairCleaner; pairbuilder::PairTrackV0Builder< - trackhistmanager::PrefixTrack1, - v0histmanager::PrefixLambda1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, + trackhistmanager::PrefixTrack1Se, + v0histmanager::PrefixLambda1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + trackhistmanager::PrefixTrack1Me, + v0histmanager::PrefixLambda1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, pairhistmanager::PrefixTrackV0Se, pairhistmanager::PrefixTrackV0Me, closepairrejection::PrefixTrackV0DaughterSe, @@ -128,10 +162,14 @@ struct FemtoPairTrackV0 { pairTrackLambdaBuilder; pairbuilder::PairTrackV0Builder< - trackhistmanager::PrefixTrack1, - v0histmanager::PrefixK0short1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, + trackhistmanager::PrefixTrack1Se, + v0histmanager::PrefixK0short1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + trackhistmanager::PrefixTrack1Me, + v0histmanager::PrefixK0short1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, pairhistmanager::PrefixTrackV0Se, pairhistmanager::PrefixTrackV0Me, closepairrejection::PrefixTrackV0DaughterSe, @@ -143,9 +181,9 @@ struct FemtoPairTrackV0 { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; pairhistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoTrackV0", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; @@ -153,27 +191,57 @@ struct FemtoPairTrackV0 { // setup cpr closepairrejection::ConfCprTrackV0Daughter confCpr; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - bool processData = doprocessLambdaSameEvent || doprocessLambdaMixedEvent || doprocessK0shortSameEvent || doprocessK0shortMixedEvent; + bool processData = doprocessLambdaSameEvent || doprocessLambdaMixedEvent || doprocessK0shortSameEvent || doprocessK0shortMixedEvent || doprocessLambdaMixedEventWithTagging || doprocessK0shortMixedEventWithTagging; + bool processFitVars = doprocessLambdaSameEventWithFitVars || doprocessLambdaMixedEventWithFitVars || doprocessK0shortSameEventWithFitVars || doprocessK0shortMixedEventWithFitVars; bool processMc = doprocessLambdaSameEventMc || doprocessLambdaMixedEventMc || doprocessK0shortSameEventMc || doprocessK0shortMixedEventMc; - if (processData && processMc) { - LOG(fatal) << "Both data and mc processing is enabled. Breaking..."; + if (static_cast(processData) + static_cast(processFitVars) + static_cast(processMc) > 1) { + LOG(fatal) << "More than one of data, FitVars or mc processing is activated. Breaking..."; } - bool processLambda = doprocessLambdaSameEvent || doprocessLambdaSameEventMc || doprocessLambdaMixedEvent || doprocessLambdaMixedEventMc; - bool processK0short = doprocessK0shortSameEvent || doprocessK0shortSameEventMc || doprocessK0shortMixedEvent || doprocessK0shortMixedEventMc; + bool processLambda = doprocessLambdaSameEvent || doprocessLambdaSameEventWithFitVars || doprocessLambdaSameEventMc || doprocessLambdaMixedEvent || doprocessLambdaMixedEventWithFitVars || doprocessLambdaMixedEventMc || doprocessLambdaMixedEventWithTagging; + bool processK0short = doprocessK0shortSameEvent || doprocessK0shortSameEventWithFitVars || doprocessK0shortSameEventMc || doprocessK0shortMixedEvent || doprocessK0shortMixedEventWithFitVars || doprocessK0shortMixedEventMc || doprocessK0shortMixedEventWithTagging; if (processLambda && processK0short) { LOG(fatal) << "Both lambda-track and k0short-track processing is enabled. Breaking..."; } + // Each species discovers its own tag bit independently (see Core/collisionTagger.h): + // same-event-only for one species and tagged-mixed-event for the other is a legitimate + // combination, so neither discovery is gated on the other's process switch. + if (doprocessLambdaMixedEventWithTagging || doprocessK0shortMixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + if (doprocessLambdaMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingLambda.Role); + if (bit < 0) { + LOG(fatal) << "processLambdaMixedEventWithTagging is enabled, but no femto-collision-tagger " + << "device (with processTrackV0 enabled) tagging this device (" << ownDevice.name + << ") was found in this workflow."; + } + confTaggingLambda.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Lambda): " << bit; + } + if (doprocessK0shortMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingK0short.Role); + if (bit < 0) { + LOG(fatal) << "processK0shortMixedEventWithTagging is enabled, but no femto-collision-tagger " + << "device (with processTrackV0 enabled) tagging this device (" << ownDevice.name + << ") was found in this workflow."; + } + confTaggingK0short.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (K0short): " << bit; + } + } + // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; std::map> colHistSpec; std::map> trackHistSpec; @@ -185,7 +253,20 @@ struct FemtoPairTrackV0 { std::map> cprHistSpec = closepairrejection::makeCprHistSpecMap(confCpr); std::map> pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); - if (processData) { + if (processFitVars) { + colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); + trackHistSpec = trackhistmanager::makeTrackHistSpecMap(confTrackBinning); + posDauSpec = trackhistmanager::makeTrackHistSpecMap(confPosDauBinning); + negDauSpec = trackhistmanager::makeTrackHistSpecMap(confNegDauBinning); + pairTrackV0HistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); + if (processLambda) { + lambdaHistSpec = v0histmanager::makeV0HistSpecMap(confLambdaBinning); + pairTrackLambdaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, lambdaSelection, confLambdaCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, lambdaHistSpec, posDauSpec, negDauSpec, pairTrackV0HistSpec, cprHistSpec, pairCleanerHistSpec); + } else { + k0shortHistSpec = v0histmanager::makeV0HistSpecMap(confK0shortBinning); + pairTrackK0shortBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, k0shortSelection, confK0shortCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, k0shortHistSpec, posDauSpec, negDauSpec, pairTrackV0HistSpec, cprHistSpec, pairCleanerHistSpec); + } + } else if (processData) { colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); trackHistSpec = trackhistmanager::makeTrackHistSpecMap(confTrackBinning); posDauSpec = trackhistmanager::makeTrackHistSpecMap(confPosDauBinning); @@ -206,10 +287,10 @@ struct FemtoPairTrackV0 { pairTrackV0HistSpec = pairhistmanager::makePairMcHistSpecMap(confPairBinning, confMixing); if (processLambda) { lambdaHistSpec = v0histmanager::makeV0McHistSpecMap(confLambdaBinning); - pairTrackLambdaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, lambdaSelection, confLambdaCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, lambdaHistSpec, posDauSpec, negDauSpec, pairTrackV0HistSpec, cprHistSpec, pairCleanerHistSpec); + pairTrackLambdaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, lambdaSelection, confLambdaCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, lambdaHistSpec, posDauSpec, negDauSpec, pairTrackV0HistSpec, cprHistSpec, pairCleanerHistSpec); } else { k0shortHistSpec = v0histmanager::makeV0McHistSpecMap(confK0shortBinning); - pairTrackK0shortBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, k0shortSelection, confK0shortCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, k0shortHistSpec, posDauSpec, negDauSpec, pairTrackV0HistSpec, cprHistSpec, pairCleanerHistSpec); + pairTrackK0shortBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, k0shortSelection, confK0shortCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, k0shortHistSpec, posDauSpec, negDauSpec, pairTrackV0HistSpec, cprHistSpec, pairCleanerHistSpec); } } hRegistry.print(); @@ -223,10 +304,21 @@ struct FemtoPairTrackV0 { void processLambdaSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoLambdasWithLabel const& lambdas, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackLambdaBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, lambdas, lambdaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); + pairTrackLambdaBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, lambdas, lambdaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); } PROCESS_SWITCH(FemtoPairTrackV0, processLambdaSameEventMc, "Enable processing same event processing for tracks and lambdas with MC information", false); + // Opt-in counterpart of processLambdaMixedEvent() below -- same "Partition declared and bound + // inside process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. Requires a + // femto-collision-tagger device (processTrackV0 enabled) in the same workflow -- see init(). + void processLambdaMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambas*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingLambda.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackLambdaBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition, lambdaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackV0, processLambdaMixedEventWithTagging, "Enable processing mixed event processing for tracks and lambdas, pre-filtered using the collision tagger", false); + void processLambdaMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambas*/) { pairTrackLambdaBuilder.processMixedEvent(cols, tracks, trackPartition, lambdaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -235,10 +327,22 @@ struct FemtoPairTrackV0 { void processLambdaMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoLambdasWithLabel const& /*lambas*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackLambdaBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, lambdaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairTrackLambdaBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, lambdaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoPairTrackV0, processLambdaMixedEventMc, "Enable processing mixed event processing for tracks and lambdas with MC information", false); + void processLambdaSameEventWithFitVars(FilteredFemtoCollision const& col, FemtoTracksWithFitVars const& tracks, FemtoLambdasWithFitVars const& lambdas) + { + pairTrackLambdaBuilder.processSameEvent(col, tracks, trackWithFitVarsPartition, lambdas, lambdaWithFitVarsPartition, cache); + } + PROCESS_SWITCH(FemtoPairTrackV0, processLambdaSameEventWithFitVars, "Enable processing same event processing for tracks and lambdas, carrying through FitVars tables", false); + + void processLambdaMixedEventWithFitVars(FilteredFemtoCollisions const& cols, FemtoTracksWithFitVars const& tracks, FemtoLambdasWithFitVars const& /*lambas*/) + { + pairTrackLambdaBuilder.processMixedEvent(cols, tracks, trackWithFitVarsPartition, lambdaWithFitVarsPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackV0, processLambdaMixedEventWithFitVars, "Enable processing mixed event processing for tracks and lambdas, carrying through FitVars tables", false); + void processK0shortSameEvent(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoK0shorts const& k0shorts) { pairTrackK0shortBuilder.processSameEvent(col, tracks, trackPartition, k0shorts, k0shortPartition, cache); @@ -247,10 +351,21 @@ struct FemtoPairTrackV0 { void processK0shortSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoK0shortsWithLabel const& k0shorts, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackK0shortBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, k0shorts, k0shortWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); + pairTrackK0shortBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, k0shorts, k0shortWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); } PROCESS_SWITCH(FemtoPairTrackV0, processK0shortSameEventMc, "Enable processing same event processing for tracks and k0shorts with MC information", false); + // Opt-in counterpart of processK0shortMixedEvent() below -- same "Partition declared and bound + // inside process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. Requires a + // femto-collision-tagger device (processTrackV0 enabled) in the same workflow -- see init(). + void processK0shortMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingK0short.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackK0shortBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition, k0shortPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackV0, processK0shortMixedEventWithTagging, "Enable processing mixed event processing for tracks and k0shorts, pre-filtered using the collision tagger", false); + void processK0shortMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/) { pairTrackK0shortBuilder.processMixedEvent(cols, tracks, trackPartition, k0shortPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -259,9 +374,21 @@ struct FemtoPairTrackV0 { void processK0shortMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoK0shortsWithLabel const& /*k0shorts*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackK0shortBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, k0shortWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairTrackK0shortBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, k0shortWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoPairTrackV0, processK0shortMixedEventMc, "Enable processing mixed event processing for tracks and k0shorts with mc information", false); + + void processK0shortSameEventWithFitVars(FilteredFemtoCollision const& col, FemtoTracksWithFitVars const& tracks, FemtoK0shortsWithFitVars const& k0shorts) + { + pairTrackK0shortBuilder.processSameEvent(col, tracks, trackWithFitVarsPartition, k0shorts, k0shortWithFitVarsPartition, cache); + } + PROCESS_SWITCH(FemtoPairTrackV0, processK0shortSameEventWithFitVars, "Enable processing same event processing for tracks and k0shorts, carrying through FitVars tables", false); + + void processK0shortMixedEventWithFitVars(FilteredFemtoCollisions const& cols, FemtoTracksWithFitVars const& tracks, FemtoK0shortsWithFitVars const& /*k0shorts*/) + { + pairTrackK0shortBuilder.processMixedEvent(cols, tracks, trackWithFitVarsPartition, k0shortWithFitVarsPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackV0, processK0shortMixedEventWithFitVars, "Enable processing mixed event processing for tracks and k0shorts, carrying through FitVars tables", false); }; o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) diff --git a/PWGCF/Femto/Tasks/femtoPairV0TwoTrackResonance.cxx b/PWGCF/Femto/Tasks/femtoPairV0TwoTrackResonance.cxx index 68ddc7d277f..b155fabf58b 100644 --- a/PWGCF/Femto/Tasks/femtoPairV0TwoTrackResonance.cxx +++ b/PWGCF/Femto/Tasks/femtoPairV0TwoTrackResonance.cxx @@ -16,6 +16,8 @@ #include "PWGCF/Femto/Core/closePairRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" +#include "PWGCF/Femto/Core/dataTypes.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" @@ -34,10 +36,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -63,6 +67,10 @@ struct FemtoPairV0TwoTrackResonance { using FemtoKstar0s = o2::soa::Join; using FemtoRho0s = o2::soa::Join; + // opt-in: carries FColTags, only needed by the processXxxMixedEventWithTagging functions below. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + o2::framework::SliceCache cache; // setup collisions @@ -70,6 +78,16 @@ struct FemtoPairV0TwoTrackResonance { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by the processXxxMixedEventWithTagging functions below -- six separate + // instances since all six v0-resonance combinations are mutually exclusive on one instance, but + // tagged independently (see init()). + collisiontagger::ConfTagging confTaggingLambdaPhi; + collisiontagger::ConfTagging confTaggingLambdaKstar0; + collisiontagger::ConfTagging confTaggingLambdaRho0; + collisiontagger::ConfTagging confTaggingK0shortPhi; + collisiontagger::ConfTagging confTaggingK0shortKstar0; + collisiontagger::ConfTagging confTaggingK0shortRho0; + // setup for daughters for v0s trackhistmanager::ConfV01PosDauBinning confV0PosDauBinning; trackhistmanager::ConfV01NegDauBinning confV0NegDauBinning; @@ -119,12 +137,18 @@ struct FemtoPairV0TwoTrackResonance { // setup for lambda-phi pairs pairbuilder::PairV0TwoTrackResonanceBuilder< - v0histmanager::PrefixLambda1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, - twotrackresonancehistmanager::PrefixPhi, - trackhistmanager::PrefixResonancePosDaughter, - trackhistmanager::PrefixResonanceNegDaughter, + v0histmanager::PrefixLambda1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + twotrackresonancehistmanager::PrefixPhiSe, + trackhistmanager::PrefixResonancePosDaughterSe, + trackhistmanager::PrefixResonanceNegDaughterSe, + v0histmanager::PrefixLambda1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + twotrackresonancehistmanager::PrefixPhiMe, + trackhistmanager::PrefixResonancePosDaughterMe, + trackhistmanager::PrefixResonanceNegDaughterMe, pairhistmanager::PrefixV0ResonanceSe, pairhistmanager::PrefixV0ResonanceMe, closepairrejection::PrefixV0TwoTrackResonancePosSe, @@ -137,12 +161,18 @@ struct FemtoPairV0TwoTrackResonance { // setup for lambda-kstar0 pairs pairbuilder::PairV0TwoTrackResonanceBuilder< - v0histmanager::PrefixLambda1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, - twotrackresonancehistmanager::PrefixKstar, - trackhistmanager::PrefixResonancePosDaughter, - trackhistmanager::PrefixResonanceNegDaughter, + v0histmanager::PrefixLambda1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + twotrackresonancehistmanager::PrefixKstarSe, + trackhistmanager::PrefixResonancePosDaughterSe, + trackhistmanager::PrefixResonanceNegDaughterSe, + v0histmanager::PrefixLambda1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + twotrackresonancehistmanager::PrefixKstarMe, + trackhistmanager::PrefixResonancePosDaughterMe, + trackhistmanager::PrefixResonanceNegDaughterMe, pairhistmanager::PrefixV0ResonanceSe, pairhistmanager::PrefixV0ResonanceMe, closepairrejection::PrefixV0TwoTrackResonancePosSe, @@ -155,12 +185,18 @@ struct FemtoPairV0TwoTrackResonance { // setup for lambda-rho0 pairs pairbuilder::PairV0TwoTrackResonanceBuilder< - v0histmanager::PrefixLambda1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, - twotrackresonancehistmanager::PrefixRho, - trackhistmanager::PrefixResonancePosDaughter, - trackhistmanager::PrefixResonanceNegDaughter, + v0histmanager::PrefixLambda1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + twotrackresonancehistmanager::PrefixRhoSe, + trackhistmanager::PrefixResonancePosDaughterSe, + trackhistmanager::PrefixResonanceNegDaughterSe, + v0histmanager::PrefixLambda1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + twotrackresonancehistmanager::PrefixRhoMe, + trackhistmanager::PrefixResonancePosDaughterMe, + trackhistmanager::PrefixResonanceNegDaughterMe, pairhistmanager::PrefixV0ResonanceSe, pairhistmanager::PrefixV0ResonanceMe, closepairrejection::PrefixV0TwoTrackResonancePosSe, @@ -173,12 +209,18 @@ struct FemtoPairV0TwoTrackResonance { // setup for k0short-phi pairs pairbuilder::PairV0TwoTrackResonanceBuilder< - v0histmanager::PrefixK0short1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, - twotrackresonancehistmanager::PrefixPhi, - trackhistmanager::PrefixResonancePosDaughter, - trackhistmanager::PrefixResonanceNegDaughter, + v0histmanager::PrefixK0short1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + twotrackresonancehistmanager::PrefixPhiSe, + trackhistmanager::PrefixResonancePosDaughterSe, + trackhistmanager::PrefixResonanceNegDaughterSe, + v0histmanager::PrefixK0short1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + twotrackresonancehistmanager::PrefixPhiMe, + trackhistmanager::PrefixResonancePosDaughterMe, + trackhistmanager::PrefixResonanceNegDaughterMe, pairhistmanager::PrefixV0ResonanceSe, pairhistmanager::PrefixV0ResonanceMe, closepairrejection::PrefixV0TwoTrackResonancePosSe, @@ -191,12 +233,18 @@ struct FemtoPairV0TwoTrackResonance { // setup for k0short-kstar0 pairs pairbuilder::PairV0TwoTrackResonanceBuilder< - v0histmanager::PrefixK0short1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, - twotrackresonancehistmanager::PrefixKstar, - trackhistmanager::PrefixResonancePosDaughter, - trackhistmanager::PrefixResonanceNegDaughter, + v0histmanager::PrefixK0short1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + twotrackresonancehistmanager::PrefixKstarSe, + trackhistmanager::PrefixResonancePosDaughterSe, + trackhistmanager::PrefixResonanceNegDaughterSe, + v0histmanager::PrefixK0short1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + twotrackresonancehistmanager::PrefixKstarMe, + trackhistmanager::PrefixResonancePosDaughterMe, + trackhistmanager::PrefixResonanceNegDaughterMe, pairhistmanager::PrefixV0ResonanceSe, pairhistmanager::PrefixV0ResonanceMe, closepairrejection::PrefixV0TwoTrackResonancePosSe, @@ -209,12 +257,18 @@ struct FemtoPairV0TwoTrackResonance { // setup for k0short-rho0 pairs pairbuilder::PairV0TwoTrackResonanceBuilder< - v0histmanager::PrefixK0short1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, - twotrackresonancehistmanager::PrefixRho, - trackhistmanager::PrefixResonancePosDaughter, - trackhistmanager::PrefixResonanceNegDaughter, + v0histmanager::PrefixK0short1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + twotrackresonancehistmanager::PrefixRhoSe, + trackhistmanager::PrefixResonancePosDaughterSe, + trackhistmanager::PrefixResonanceNegDaughterSe, + v0histmanager::PrefixK0short1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + twotrackresonancehistmanager::PrefixRhoMe, + trackhistmanager::PrefixResonancePosDaughterMe, + trackhistmanager::PrefixResonanceNegDaughterMe, pairhistmanager::PrefixV0ResonanceSe, pairhistmanager::PrefixV0ResonanceMe, closepairrejection::PrefixV0TwoTrackResonancePosSe, @@ -229,9 +283,9 @@ struct FemtoPairV0TwoTrackResonance { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; pairhistmanager::ConfMixing confMixing; @@ -241,25 +295,87 @@ struct FemtoPairV0TwoTrackResonance { o2::framework::HistogramRegistry hRegistry{"FemtoV0TwoTrackResonance", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { // all pair combinations share the same histogram prefixes, so only one combination can be processed per workflow instance - if (static_cast(doprocessLambdaPhiSameEvent || doprocessLambdaPhiMixedEvent) + - static_cast(doprocessLambdaKstar0SameEvent || doprocessLambdaKstar0MixedEvent) + - static_cast(doprocessLambdaRho0SameEvent || doprocessLambdaRho0MixedEvent) + - static_cast(doprocessK0shortPhiSameEvent || doprocessK0shortPhiMixedEvent) + - static_cast(doprocessK0shortKstar0SameEvent || doprocessK0shortKstar0MixedEvent) + - static_cast(doprocessK0shortRho0SameEvent || doprocessK0shortRho0MixedEvent) > + if (static_cast(doprocessLambdaPhiSameEvent || doprocessLambdaPhiMixedEvent || doprocessLambdaPhiMixedEventWithTagging) + + static_cast(doprocessLambdaKstar0SameEvent || doprocessLambdaKstar0MixedEvent || doprocessLambdaKstar0MixedEventWithTagging) + + static_cast(doprocessLambdaRho0SameEvent || doprocessLambdaRho0MixedEvent || doprocessLambdaRho0MixedEventWithTagging) + + static_cast(doprocessK0shortPhiSameEvent || doprocessK0shortPhiMixedEvent || doprocessK0shortPhiMixedEventWithTagging) + + static_cast(doprocessK0shortKstar0SameEvent || doprocessK0shortKstar0MixedEvent || doprocessK0shortKstar0MixedEventWithTagging) + + static_cast(doprocessK0shortRho0SameEvent || doprocessK0shortRho0MixedEvent || doprocessK0shortRho0MixedEventWithTagging) > 1) { LOG(fatal) << "Can only process one v0-resonance combination (lambda/k0short x phi/kstar0/rho0)"; } + // Each combination discovers its own tag bit independently (see Core/collisionTagger.h). + if (doprocessLambdaPhiMixedEventWithTagging || doprocessLambdaKstar0MixedEventWithTagging || doprocessLambdaRho0MixedEventWithTagging || + doprocessK0shortPhiMixedEventWithTagging || doprocessK0shortKstar0MixedEventWithTagging || doprocessK0shortRho0MixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + if (doprocessLambdaPhiMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingLambdaPhi.Role); + if (bit < 0) { + LOG(fatal) << "processLambdaPhiMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processV0TwoTrackResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingLambdaPhi.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (LambdaPhi): " << bit; + } + if (doprocessLambdaKstar0MixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingLambdaKstar0.Role); + if (bit < 0) { + LOG(fatal) << "processLambdaKstar0MixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processV0TwoTrackResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingLambdaKstar0.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (LambdaKstar0): " << bit; + } + if (doprocessLambdaRho0MixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingLambdaRho0.Role); + if (bit < 0) { + LOG(fatal) << "processLambdaRho0MixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processV0TwoTrackResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingLambdaRho0.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (LambdaRho0): " << bit; + } + if (doprocessK0shortPhiMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingK0shortPhi.Role); + if (bit < 0) { + LOG(fatal) << "processK0shortPhiMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processV0TwoTrackResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingK0shortPhi.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (K0shortPhi): " << bit; + } + if (doprocessK0shortKstar0MixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingK0shortKstar0.Role); + if (bit < 0) { + LOG(fatal) << "processK0shortKstar0MixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processV0TwoTrackResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingK0shortKstar0.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (K0shortKstar0): " << bit; + } + if (doprocessK0shortRho0MixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingK0shortRho0.Role); + if (bit < 0) { + LOG(fatal) << "processK0shortRho0MixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processV0TwoTrackResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingK0shortRho0.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (K0shortRho0): " << bit; + } + } + // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; // setup histograms auto colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); @@ -278,42 +394,42 @@ struct FemtoPairV0TwoTrackResonance { auto pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); // setup for lambda-phi - if (doprocessLambdaPhiSameEvent || doprocessLambdaPhiMixedEvent) { + if (doprocessLambdaPhiSameEvent || doprocessLambdaPhiMixedEvent || doprocessLambdaPhiMixedEventWithTagging) { auto lambdaHistSpec = v0histmanager::makeV0HistSpecMap(confLambdaBinning); auto phiHistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confPhiBinning); pairLambdaPhiBuilder.init(&hRegistry, confCollisionBinning, lambdaSelection, phiSelection, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, lambdaHistSpec, v0PosDauSpec, v0NegDauSpec, phiHistSpec, resoPosDauSpec, resoNegDauSpec, pairV0TwoTrackResonanceHistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); } // setup for lambda-kstar0 - if (doprocessLambdaKstar0SameEvent || doprocessLambdaKstar0MixedEvent) { + if (doprocessLambdaKstar0SameEvent || doprocessLambdaKstar0MixedEvent || doprocessLambdaKstar0MixedEventWithTagging) { auto lambdaHistSpec = v0histmanager::makeV0HistSpecMap(confLambdaBinning); auto kstar0HistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confKstar0Binning); pairLambdaKstar0Builder.init(&hRegistry, confCollisionBinning, lambdaSelection, kstar0Selection, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, lambdaHistSpec, v0PosDauSpec, v0NegDauSpec, kstar0HistSpec, resoPosDauSpec, resoNegDauSpec, pairV0TwoTrackResonanceHistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); } // setup for lambda-rho0 - if (doprocessLambdaRho0SameEvent || doprocessLambdaRho0MixedEvent) { + if (doprocessLambdaRho0SameEvent || doprocessLambdaRho0MixedEvent || doprocessLambdaRho0MixedEventWithTagging) { auto lambdaHistSpec = v0histmanager::makeV0HistSpecMap(confLambdaBinning); auto rho0HistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confRho0Binning); pairLambdaRho0Builder.init(&hRegistry, confCollisionBinning, lambdaSelection, rho0Selection, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, lambdaHistSpec, v0PosDauSpec, v0NegDauSpec, rho0HistSpec, resoPosDauSpec, resoNegDauSpec, pairV0TwoTrackResonanceHistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); } // setup for k0short-phi - if (doprocessK0shortPhiSameEvent || doprocessK0shortPhiMixedEvent) { + if (doprocessK0shortPhiSameEvent || doprocessK0shortPhiMixedEvent || doprocessK0shortPhiMixedEventWithTagging) { auto k0shortHistSpec = v0histmanager::makeV0HistSpecMap(confK0shortBinning); auto phiHistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confPhiBinning); pairK0shortPhiBuilder.init(&hRegistry, confCollisionBinning, k0shortSelection, phiSelection, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, k0shortHistSpec, v0PosDauSpec, v0NegDauSpec, phiHistSpec, resoPosDauSpec, resoNegDauSpec, pairV0TwoTrackResonanceHistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); } // setup for k0short-kstar0 - if (doprocessK0shortKstar0SameEvent || doprocessK0shortKstar0MixedEvent) { + if (doprocessK0shortKstar0SameEvent || doprocessK0shortKstar0MixedEvent || doprocessK0shortKstar0MixedEventWithTagging) { auto k0shortHistSpec = v0histmanager::makeV0HistSpecMap(confK0shortBinning); auto kstar0HistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confKstar0Binning); pairK0shortKstar0Builder.init(&hRegistry, confCollisionBinning, k0shortSelection, kstar0Selection, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, k0shortHistSpec, v0PosDauSpec, v0NegDauSpec, kstar0HistSpec, resoPosDauSpec, resoNegDauSpec, pairV0TwoTrackResonanceHistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); } // setup for k0short-rho0 - if (doprocessK0shortRho0SameEvent || doprocessK0shortRho0MixedEvent) { + if (doprocessK0shortRho0SameEvent || doprocessK0shortRho0MixedEvent || doprocessK0shortRho0MixedEventWithTagging) { auto k0shortHistSpec = v0histmanager::makeV0HistSpecMap(confK0shortBinning); auto rho0HistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confRho0Binning); pairK0shortRho0Builder.init(&hRegistry, confCollisionBinning, k0shortSelection, rho0Selection, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, k0shortHistSpec, v0PosDauSpec, v0NegDauSpec, rho0HistSpec, resoPosDauSpec, resoNegDauSpec, pairV0TwoTrackResonanceHistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); @@ -329,6 +445,16 @@ struct FemtoPairV0TwoTrackResonance { } PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processLambdaPhiSameEvent, "Enable processing same event processing for lambdas and phis", true); + // Opt-in counterpart of processLambdaPhiMixedEvent() below -- same "Partition declared and + // bound inside process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. + void processLambdaPhiMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoPhis const& /*phis*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingLambdaPhi.internalTagMask); + taggedCollisions.bindTable(cols); + pairLambdaPhiBuilder.processMixedEvent(taggedCollisions, tracks, lambdaPartition, phiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processLambdaPhiMixedEventWithTagging, "Enable processing mixed event processing for lambdas and phis, pre-filtered using the collision tagger", false); + void processLambdaPhiMixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoPhis const& /*phis*/) { pairLambdaPhiBuilder.processMixedEvent(cols, tracks, lambdaPartition, phiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -342,6 +468,16 @@ struct FemtoPairV0TwoTrackResonance { } PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processLambdaKstar0SameEvent, "Enable processing same event processing for lambdas and kstar0s", false); + // Opt-in counterpart of processLambdaKstar0MixedEvent() below -- same idiom as + // processLambdaPhiMixedEventWithTagging above. + void processLambdaKstar0MixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoKstar0s const& /*kstar0s*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingLambdaKstar0.internalTagMask); + taggedCollisions.bindTable(cols); + pairLambdaKstar0Builder.processMixedEvent(taggedCollisions, tracks, lambdaPartition, kstar0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processLambdaKstar0MixedEventWithTagging, "Enable processing mixed event processing for lambdas and kstar0s, pre-filtered using the collision tagger", false); + void processLambdaKstar0MixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoKstar0s const& /*kstar0s*/) { pairLambdaKstar0Builder.processMixedEvent(cols, tracks, lambdaPartition, kstar0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -355,6 +491,16 @@ struct FemtoPairV0TwoTrackResonance { } PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processLambdaRho0SameEvent, "Enable processing same event processing for lambdas and rho0s", false); + // Opt-in counterpart of processLambdaRho0MixedEvent() below -- same idiom as + // processLambdaPhiMixedEventWithTagging above. + void processLambdaRho0MixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoRho0s const& /*rho0s*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingLambdaRho0.internalTagMask); + taggedCollisions.bindTable(cols); + pairLambdaRho0Builder.processMixedEvent(taggedCollisions, tracks, lambdaPartition, rho0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processLambdaRho0MixedEventWithTagging, "Enable processing mixed event processing for lambdas and rho0s, pre-filtered using the collision tagger", false); + void processLambdaRho0MixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoRho0s const& /*rho0s*/) { pairLambdaRho0Builder.processMixedEvent(cols, tracks, lambdaPartition, rho0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -368,6 +514,16 @@ struct FemtoPairV0TwoTrackResonance { } PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processK0shortPhiSameEvent, "Enable processing same event processing for k0shorts and phis", false); + // Opt-in counterpart of processK0shortPhiMixedEvent() below -- same idiom as + // processLambdaPhiMixedEventWithTagging above. + void processK0shortPhiMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/, FemtoPhis const& /*phis*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingK0shortPhi.internalTagMask); + taggedCollisions.bindTable(cols); + pairK0shortPhiBuilder.processMixedEvent(taggedCollisions, tracks, k0shortPartition, phiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processK0shortPhiMixedEventWithTagging, "Enable processing mixed event processing for k0shorts and phis, pre-filtered using the collision tagger", false); + void processK0shortPhiMixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/, FemtoPhis const& /*phis*/) { pairK0shortPhiBuilder.processMixedEvent(cols, tracks, k0shortPartition, phiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -381,6 +537,16 @@ struct FemtoPairV0TwoTrackResonance { } PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processK0shortKstar0SameEvent, "Enable processing same event processing for k0shorts and kstar0s", false); + // Opt-in counterpart of processK0shortKstar0MixedEvent() below -- same idiom as + // processLambdaPhiMixedEventWithTagging above. + void processK0shortKstar0MixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/, FemtoKstar0s const& /*kstar0s*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingK0shortKstar0.internalTagMask); + taggedCollisions.bindTable(cols); + pairK0shortKstar0Builder.processMixedEvent(taggedCollisions, tracks, k0shortPartition, kstar0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processK0shortKstar0MixedEventWithTagging, "Enable processing mixed event processing for k0shorts and kstar0s, pre-filtered using the collision tagger", false); + void processK0shortKstar0MixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/, FemtoKstar0s const& /*kstar0s*/) { pairK0shortKstar0Builder.processMixedEvent(cols, tracks, k0shortPartition, kstar0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -394,6 +560,16 @@ struct FemtoPairV0TwoTrackResonance { } PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processK0shortRho0SameEvent, "Enable processing same event processing for k0shorts and rho0s", false); + // Opt-in counterpart of processK0shortRho0MixedEvent() below -- same idiom as + // processLambdaPhiMixedEventWithTagging above. + void processK0shortRho0MixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/, FemtoRho0s const& /*rho0s*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingK0shortRho0.internalTagMask); + taggedCollisions.bindTable(cols); + pairK0shortRho0Builder.processMixedEvent(taggedCollisions, tracks, k0shortPartition, rho0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processK0shortRho0MixedEventWithTagging, "Enable processing mixed event processing for k0shorts and rho0s, pre-filtered using the collision tagger", false); + void processK0shortRho0MixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/, FemtoRho0s const& /*rho0s*/) { pairK0shortRho0Builder.processMixedEvent(cols, tracks, k0shortPartition, rho0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); diff --git a/PWGCF/Femto/Tasks/femtoPairV0V0.cxx b/PWGCF/Femto/Tasks/femtoPairV0V0.cxx index dd676c5e43c..4602b00fff4 100644 --- a/PWGCF/Femto/Tasks/femtoPairV0V0.cxx +++ b/PWGCF/Femto/Tasks/femtoPairV0V0.cxx @@ -16,6 +16,8 @@ #include "PWGCF/Femto/Core/closePairRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" +#include "PWGCF/Femto/Core/dataTypes.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" @@ -32,11 +34,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -72,6 +76,10 @@ struct FemtoPairV0V0 { using FemtoMcParticlesWithLabel = o2::soa::Join; + // opt-in: carries FColTags, only needed by the processXxxMixedEventWithTagging functions below. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + o2::framework::SliceCache cache; // setup collisions @@ -79,6 +87,13 @@ struct FemtoPairV0V0 { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by the processXxxMixedEventWithTagging functions below -- three separate + // instances since lambda-lambda/k0short-k0short/lambda-k0short are mutually exclusive on one + // instance, but tagged independently (see init()). + collisiontagger::ConfTagging confTaggingLambdaLambda; + collisiontagger::ConfTagging confTaggingK0shortK0short; + collisiontagger::ConfTagging confTaggingLambdaK0short; + // setup for daughters // separate binning per v0 slot, so that e.g. lambda (proton/pion daughters) and // k0short (pion/pion daughters) can be binned independently @@ -125,12 +140,18 @@ struct FemtoPairV0V0 { paircleaner::ConfPairCleanerBinning confPairCleaner; pairbuilder::PairV0V0Builder< - v0histmanager::PrefixLambda1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, - v0histmanager::PrefixLambda2, - trackhistmanager::PrefixV02PosDaughter, - trackhistmanager::PrefixV02NegDaughter, + v0histmanager::PrefixLambda1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + v0histmanager::PrefixLambda2Se, + trackhistmanager::PrefixV02PosDaughterSe, + trackhistmanager::PrefixV02NegDaughterSe, + v0histmanager::PrefixLambda1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + v0histmanager::PrefixLambda2Me, + trackhistmanager::PrefixV02PosDaughterMe, + trackhistmanager::PrefixV02NegDaughterMe, pairhistmanager::PrefixV0V0Se, pairhistmanager::PrefixV0V0Me, closepairrejection::PrefixV0V0PosSe, @@ -142,12 +163,18 @@ struct FemtoPairV0V0 { pairLambdaLambdaBuilder; pairbuilder::PairV0V0Builder< - v0histmanager::PrefixK0short1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, - v0histmanager::PrefixK0short2, - trackhistmanager::PrefixV02PosDaughter, - trackhistmanager::PrefixV02NegDaughter, + v0histmanager::PrefixK0short1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + v0histmanager::PrefixK0short2Se, + trackhistmanager::PrefixV02PosDaughterSe, + trackhistmanager::PrefixV02NegDaughterSe, + v0histmanager::PrefixK0short1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + v0histmanager::PrefixK0short2Me, + trackhistmanager::PrefixV02PosDaughterMe, + trackhistmanager::PrefixV02NegDaughterMe, pairhistmanager::PrefixV0V0Se, pairhistmanager::PrefixV0V0Me, closepairrejection::PrefixV0V0PosSe, @@ -159,12 +186,18 @@ struct FemtoPairV0V0 { pairK0shortK0shortBuilder; pairbuilder::PairV0V0Builder< - v0histmanager::PrefixLambda1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, - v0histmanager::PrefixK0short2, - trackhistmanager::PrefixV02PosDaughter, - trackhistmanager::PrefixV02NegDaughter, + v0histmanager::PrefixLambda1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + v0histmanager::PrefixK0short2Se, + trackhistmanager::PrefixV02PosDaughterSe, + trackhistmanager::PrefixV02NegDaughterSe, + v0histmanager::PrefixLambda1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + v0histmanager::PrefixK0short2Me, + trackhistmanager::PrefixV02PosDaughterMe, + trackhistmanager::PrefixV02NegDaughterMe, pairhistmanager::PrefixV0V0Se, pairhistmanager::PrefixV0V0Me, closepairrejection::PrefixV0V0PosSe, @@ -179,9 +212,9 @@ struct FemtoPairV0V0 { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; pairhistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoV0V0", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; @@ -190,11 +223,11 @@ struct FemtoPairV0V0 { closepairrejection::ConfCprV0DaugherV0DaughterPos confCprPos; closepairrejection::ConfCprV0DaugherV0DaughterNeg confCprNeg; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - bool processData = doprocessLambdaLambdaSameEvent || doprocessLambdaLambdaMixedEvent || - doprocessK0shortK0shortSameEvent || doprocessK0shortK0shortMixedEvent || - doprocessLambdaK0shortSameEvent || doprocessLambdaK0shortMixedEvent; + bool processData = doprocessLambdaLambdaSameEvent || doprocessLambdaLambdaMixedEvent || doprocessLambdaLambdaMixedEventWithTagging || + doprocessK0shortK0shortSameEvent || doprocessK0shortK0shortMixedEvent || doprocessK0shortK0shortMixedEventWithTagging || + doprocessLambdaK0shortSameEvent || doprocessLambdaK0shortMixedEvent || doprocessLambdaK0shortMixedEventWithTagging; bool processMc = doprocessLambdaLambdaSameEventMc || doprocessLambdaLambdaMixedEventMc || doprocessK0shortK0shortSameEventMc || doprocessK0shortK0shortMixedEventMc || doprocessLambdaK0shortSameEventMc || doprocessLambdaK0shortMixedEventMc; @@ -206,22 +239,56 @@ struct FemtoPairV0V0 { LOG(fatal) << "Neither data nor mc processing is enabled. Breaking..."; } - bool processLambdaLambda = doprocessLambdaLambdaSameEvent || doprocessLambdaLambdaMixedEvent || + bool processLambdaLambda = doprocessLambdaLambdaSameEvent || doprocessLambdaLambdaMixedEvent || doprocessLambdaLambdaMixedEventWithTagging || doprocessLambdaLambdaSameEventMc || doprocessLambdaLambdaMixedEventMc; - bool processK0shortK0short = doprocessK0shortK0shortSameEvent || doprocessK0shortK0shortMixedEvent || + bool processK0shortK0short = doprocessK0shortK0shortSameEvent || doprocessK0shortK0shortMixedEvent || doprocessK0shortK0shortMixedEventWithTagging || doprocessK0shortK0shortSameEventMc || doprocessK0shortK0shortMixedEventMc; - bool processLambdaK0short = doprocessLambdaK0shortSameEvent || doprocessLambdaK0shortMixedEvent || + bool processLambdaK0short = doprocessLambdaK0shortSameEvent || doprocessLambdaK0shortMixedEvent || doprocessLambdaK0shortMixedEventWithTagging || doprocessLambdaK0shortSameEventMc || doprocessLambdaK0shortMixedEventMc; if (static_cast(processLambdaLambda) + static_cast(processK0shortK0short) + static_cast(processLambdaK0short) > 1) { LOG(fatal) << "Only one of lambda-lambda, k0short-k0short or lambda-k0short processing can be enabled. Breaking..."; } + // Each pair type discovers its own tag bit independently (see Core/collisionTagger.h). + if (doprocessLambdaLambdaMixedEventWithTagging || doprocessK0shortK0shortMixedEventWithTagging || doprocessLambdaK0shortMixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + if (doprocessLambdaLambdaMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingLambdaLambda.Role); + if (bit < 0) { + LOG(fatal) << "processLambdaLambdaMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processV0V0 enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingLambdaLambda.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (LambdaLambda): " << bit; + } + if (doprocessK0shortK0shortMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingK0shortK0short.Role); + if (bit < 0) { + LOG(fatal) << "processK0shortK0shortMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processV0V0 enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingK0shortK0short.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (K0shortK0short): " << bit; + } + if (doprocessLambdaK0shortMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingLambdaK0short.Role); + if (bit < 0) { + LOG(fatal) << "processLambdaK0shortMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processV0V0 enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingLambdaK0short.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (LambdaK0short): " << bit; + } + } + // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; // setup histograms std::map> colHistSpec; @@ -304,6 +371,16 @@ struct FemtoPairV0V0 { } PROCESS_SWITCH(FemtoPairV0V0, processLambdaLambdaSameEventMc, "Enable processing same event processing for lambda-lambda with mc information", false); + // Opt-in counterpart of processLambdaLambdaMixedEvent() below -- same "Partition declared and + // bound inside process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. + void processLambdaLambdaMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingLambdaLambda.internalTagMask); + taggedCollisions.bindTable(cols); + pairLambdaLambdaBuilder.processMixedEvent(taggedCollisions, tracks, lambdaPartition1, lambdaPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0V0, processLambdaLambdaMixedEventWithTagging, "Enable processing mixed event processing for lambda-lambda, pre-filtered using the collision tagger", false); + void processLambdaLambdaMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/) { pairLambdaLambdaBuilder.processMixedEvent(cols, tracks, lambdaPartition1, lambdaPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -328,6 +405,16 @@ struct FemtoPairV0V0 { } PROCESS_SWITCH(FemtoPairV0V0, processK0shortK0shortSameEventMc, "Enable processing same event processing for k0short-k0short with mc information", false); + // Opt-in counterpart of processK0shortK0shortMixedEvent() below -- same idiom as + // processLambdaLambdaMixedEventWithTagging above. + void processK0shortK0shortMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingK0shortK0short.internalTagMask); + taggedCollisions.bindTable(cols); + pairK0shortK0shortBuilder.processMixedEvent(taggedCollisions, tracks, k0shortPartition1, k0shortPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0V0, processK0shortK0shortMixedEventWithTagging, "Enable processing mixed event processing for k0short-k0short, pre-filtered using the collision tagger", false); + void processK0shortK0shortMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/) { pairK0shortK0shortBuilder.processMixedEvent(cols, tracks, k0shortPartition1, k0shortPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -352,6 +439,16 @@ struct FemtoPairV0V0 { } PROCESS_SWITCH(FemtoPairV0V0, processLambdaK0shortSameEventMc, "Enable processing same event processing for lambda-k0short with mc information", false); + // Opt-in counterpart of processLambdaK0shortMixedEvent() below -- same idiom as + // processLambdaLambdaMixedEventWithTagging above. + void processLambdaK0shortMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoK0shorts const& /*k0shorts*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingLambdaK0short.internalTagMask); + taggedCollisions.bindTable(cols); + pairLambdaK0shortBuilder.processMixedEvent(taggedCollisions, tracks, lambdaPartition1, k0shortPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0V0, processLambdaK0shortMixedEventWithTagging, "Enable processing mixed event processing for lambda-k0short, pre-filtered using the collision tagger", false); + void processLambdaK0shortMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoK0shorts const& /*k0shorts*/) { pairLambdaK0shortBuilder.processMixedEvent(cols, tracks, lambdaPartition1, k0shortPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); diff --git a/PWGCF/Femto/Tasks/femtoPhotonQa.cxx b/PWGCF/Femto/Tasks/femtoPhotonQa.cxx new file mode 100644 index 00000000000..2e074fb1ff3 --- /dev/null +++ b/PWGCF/Femto/Tasks/femtoPhotonQa.cxx @@ -0,0 +1,99 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoPhotonQa.cxx +/// \brief QA task for photons (PCM) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#include "PWGCF/Femto/Core/collisionBuilder.h" +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/Core/photonBuilder.h" +#include "PWGCF/Femto/Core/photonHistManager.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace o2::analysis::femto; + +struct FemtoPhotonQa { + + // setup tables + using FemtoCollisions = o2::soa::Join; + using FilteredFemtoCollisions = o2::soa::Filtered; + using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; + + using FemtoPhotons = o2::soa::Join; + + o2::framework::SliceCache cache; + + // setup for collisions + collisionbuilder::ConfCollisionSelection collisionSelection; + o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); + colhistmanager::CollisionHistManager colHistManager; + colhistmanager::ConfCollisionBinning confCollisionBinning; + colhistmanager::ConfCollisionQaBinning confCollisionQaBinning; + + // setup for photons + photonbuilder::ConfPhotonSelection1 confPhotonSelection; + + o2::framework::Partition photonPartition = MAKE_PHOTON_PARTITION(confPhotonSelection); + o2::framework::Preslice perColPhotons = o2::aod::femtobase::stored::fColId; + + photonhistmanager::ConfPhotonBinning confPhotonBinning; + photonhistmanager::ConfPhotonQaBinning confPhotonQaBinning; + photonhistmanager::PhotonHistManager photonHistManager; + + o2::framework::HistogramRegistry hRegistry{"FemtoPhotonQa", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + + void init(o2::framework::InitContext&) + { + auto colHistSpec = colhistmanager::makeColQaHistSpecMap(confCollisionBinning, confCollisionQaBinning); + colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); + + auto photonHistSpec = photonhistmanager::makePhotonQaHistSpecMap(confPhotonBinning, confPhotonQaBinning); + photonHistManager.init(&hRegistry, photonHistSpec, confPhotonQaBinning); + + hRegistry.print(); + } + + void processPhotons(FilteredFemtoCollision const& col, FemtoPhotons const& /*photons*/) + { + auto photonSlice = photonPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (photonSlice.size() == 0) { + return; + } + colHistManager.fill(col); + for (auto const& photon : photonSlice) { + photonHistManager.fill(photon); + } + } + PROCESS_SWITCH(FemtoPhotonQa, processPhotons, "Process photons", true); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{ + adaptAnalysisTask(context), + }; + return workflow; +} diff --git a/PWGCF/Femto/Tasks/femtoTrackQa.cxx b/PWGCF/Femto/Tasks/femtoTrackQa.cxx index 5a5f081e954..c3dc0898145 100644 --- a/PWGCF/Femto/Tasks/femtoTrackQa.cxx +++ b/PWGCF/Femto/Tasks/femtoTrackQa.cxx @@ -100,12 +100,12 @@ struct FemtoTrackQa { colHistSpec = colhistmanager::makeColQaHistSpecMap(confCollisionBinning, confCollisionQaBinning); colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); trackHistSpec = trackhistmanager::makeTrackQaHistSpecMap(confTrackBinning, confTrackQaBinning); - trackHistManager.init(&hRegistry, trackHistSpec, confTrackSelection, confTrackQaBinning); + trackHistManager.init(&hRegistry, trackHistSpec, confTrackSelection, confTrackQaBinning); } else { colHistSpec = colhistmanager::makeColMcQaHistSpecMap(confCollisionBinning, confCollisionQaBinning); colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); trackHistSpec = trackhistmanager::makeTrackMcQaHistSpecMap(confTrackBinning, confTrackQaBinning); - trackHistManager.init(&hRegistry, trackHistSpec, confTrackSelection, confTrackQaBinning); + trackHistManager.init(&hRegistry, trackHistSpec, confTrackSelection, confTrackQaBinning); } hRegistry.print(); }; @@ -118,7 +118,7 @@ struct FemtoTrackQa { } colHistManager.fill(col); for (auto const& track : trackSlice) { - trackHistManager.fill(track, tracks); + trackHistManager.fill(track, tracks); } }; PROCESS_SWITCH(FemtoTrackQa, processData, "Track QA in Data", true); @@ -134,7 +134,7 @@ struct FemtoTrackQa { if (!trackCleaner.isClean(track, mcParticles, mcMothers, mcPartonicMothers)) { continue; } - trackHistManager.fill(track, tracks, col, mcParticles, mcMothers, mcPartonicMothers); + trackHistManager.fill(track, tracks, col, mcParticles, mcMothers, mcPartonicMothers); } } PROCESS_SWITCH(FemtoTrackQa, processMc, "Track QA in Monte Carlo", false); @@ -147,7 +147,7 @@ struct FemtoTrackQa { } colHistManager.fill(col); for (auto const& track : trackSlice) { - trackHistManager.fill(track, tracks); + trackHistManager.fill(track, tracks); } }; PROCESS_SWITCH(FemtoTrackQa, processDataWithEventShape, "Track QA in Data with event shape information", false); diff --git a/PWGCF/Femto/Tasks/femtoTrackTrackTwoPhotonResonanceQa.cxx b/PWGCF/Femto/Tasks/femtoTrackTrackTwoPhotonResonanceQa.cxx new file mode 100644 index 00000000000..59c98e936c2 --- /dev/null +++ b/PWGCF/Femto/Tasks/femtoTrackTrackTwoPhotonResonanceQa.cxx @@ -0,0 +1,127 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoTrackTrackTwoPhotonResonanceQa.cxx +/// \brief QA task for resonances built from two tracks and a two-photon resonance (omega(782) -> pi+ pi- pi0) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#include "PWGCF/Femto/Core/collisionBuilder.h" +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/Core/photonHistManager.h" +#include "PWGCF/Femto/Core/trackHistManager.h" +#include "PWGCF/Femto/Core/trackTrackTwoPhotonResonanceBuilder.h" +#include "PWGCF/Femto/Core/trackTrackTwoPhotonResonanceHistManager.h" +#include "PWGCF/Femto/Core/twoPhotonResonanceHistManager.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2::analysis::femto; + +struct FemtoTrackTrackTwoPhotonResonanceQa { + + // setup tables + using FemtoCollisions = o2::soa::Join; + using FilteredFemtoCollisions = o2::soa::Filtered; + using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; + + using FemtoOmegaMesons = o2::soa::Join; + using FemtoTracks = o2::soa::Join; + using FemtoPi0s = o2::soa::Join; + using FemtoPhotons = o2::soa::Join; + + o2::framework::SliceCache cache; + + // setup for collisions + collisionbuilder::ConfCollisionSelection collisionSelection; + o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); + colhistmanager::CollisionHistManager colHistManager; + colhistmanager::ConfCollisionBinning confCollisionBinning; + colhistmanager::ConfCollisionQaBinning confCollisionQaBinning; + + // setup for omega mesons + trackstwophotonresonancebuilder::ConfOmegaMesonSelection1 confOmegaMesonSelection; + o2::framework::Partition omegaMesonPartition = MAKE_TRACKTRACKTWOPHOTONRESONANCE_PARTITION(confOmegaMesonSelection); + o2::framework::Preslice perColOmegaMesons = o2::aod::femtobase::stored::fColId; + + trackstwophotonresonancehistmanager::ConfOmegaMesonBinning confOmegaMesonBinning; + + // setup for the two charged (pion) daughters + trackhistmanager::ConfResonancePosDauBinning confPosDauBinning; + trackhistmanager::ConfResonancePosDauQaBinning confPosDauQaBinning; + trackhistmanager::ConfResonanceNegDauBinning confNegDauBinning; + trackhistmanager::ConfResonanceNegDauQaBinning confNegDauQaBinning; + + // setup for the neutral (pi0) daughter -- reuses the regular Pi0 binning/QA config, since the + // embedded candidate gets the same TwoPhotonResonanceHistManager treatment (including its own + // photon-leg QA) as the dedicated Pi0/Eta QA task + twophotonresonancehistmanager::ConfPi0Binning confNeutralDauBinning; + photonhistmanager::ConfPhotonBinning confNeutralDauPhotonBinning; + photonhistmanager::ConfPhotonQaBinning confNeutralDauPhotonQaBinning; + + trackstwophotonresonancehistmanager::TrackTrackTwoPhotonResonanceHistManager< + trackstwophotonresonancehistmanager::PrefixOmegaMeson, + trackhistmanager::PrefixResonancePosDaughterSe, + trackhistmanager::PrefixResonanceNegDaughterSe, + trackstwophotonresonancehistmanager::PrefixOmegaMesonNeutralDau, + trackstwophotonresonancehistmanager::PrefixOmegaMesonNeutralDauPhotonDau1, + trackstwophotonresonancehistmanager::PrefixOmegaMesonNeutralDauPhotonDau2, + modes::TrackTrackTwoPhotonResonance::kOmegaMeson, + modes::TwoPhotonResonance::kPi0> + omegaMesonHistManager; + + o2::framework::HistogramRegistry hRegistry{"FemtoTrackTrackTwoPhotonResonanceQa", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + + void init(o2::framework::InitContext&) + { + auto colHistSpec = colhistmanager::makeColQaHistSpecMap(confCollisionBinning, confCollisionQaBinning); + colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); + + auto omegaMesonHistSpec = trackstwophotonresonancehistmanager::makeTrackTrackTwoPhotonResonanceQaHistSpecMap(confOmegaMesonBinning); + auto posDauSpec = trackhistmanager::makeTrackQaHistSpecMap(confPosDauBinning, confPosDauQaBinning); + auto negDauSpec = trackhistmanager::makeTrackQaHistSpecMap(confNegDauBinning, confNegDauQaBinning); + auto neutralDauSpec = twophotonresonancehistmanager::makeTwoPhotonResonanceQaHistSpecMap(confNeutralDauBinning); + auto neutralDauPhotonSpec = photonhistmanager::makePhotonQaHistSpecMap(confNeutralDauPhotonBinning, confNeutralDauPhotonQaBinning); + + omegaMesonHistManager.init(&hRegistry, omegaMesonHistSpec, posDauSpec, negDauSpec, neutralDauSpec, neutralDauPhotonSpec, confPosDauQaBinning, confNegDauQaBinning, confNeutralDauPhotonQaBinning); + } + + void processOmegaMesons(FilteredFemtoCollision const& col, FemtoOmegaMesons const& /*omegaMesons*/, FemtoTracks const& tracks, FemtoPi0s const& pi0s, FemtoPhotons const& photons) + { + auto omegaMesonSlice = omegaMesonPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (omegaMesonSlice.size() == 0) { + return; + } + colHistManager.fill(col); + for (auto const& omegaMeson : omegaMesonSlice) { + omegaMesonHistManager.fill(omegaMeson, tracks, pi0s, photons); + } + } + PROCESS_SWITCH(FemtoTrackTrackTwoPhotonResonanceQa, processOmegaMesons, "Process OmegaMesons", true); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{ + adaptAnalysisTask(context), + }; + return workflow; +} diff --git a/PWGCF/Femto/Tasks/femtoTripletMcParticleMcParticleMcParticle.cxx b/PWGCF/Femto/Tasks/femtoTripletMcParticleMcParticleMcParticle.cxx index fc4c36b8ed6..c2cee7d7321 100644 --- a/PWGCF/Femto/Tasks/femtoTripletMcParticleMcParticleMcParticle.cxx +++ b/PWGCF/Femto/Tasks/femtoTripletMcParticleMcParticleMcParticle.cxx @@ -75,6 +75,7 @@ struct FemtoTripletMcParticleMcParticleMcParticle { o2::framework::Partition mcParticlesPartition1 = MAKE_MC_PARTICLE_PARTITION(confMcParticleSelection1); o2::framework::Partition mcParticlesPartition2 = MAKE_MC_PARTICLE_PARTITION(confMcParticleSelection2); o2::framework::Partition mcParticlesPartition3 = MAKE_MC_PARTICLE_PARTITION(confMcParticleSelection3); + o2::framework::PresliceUnsorted perColParticles = o2::aod::femtomcparticle::fMcColId; // setup triplets triplethistmanager::ConfTripletBinning confTripletBinning; @@ -84,9 +85,12 @@ struct FemtoTripletMcParticleMcParticleMcParticle { closetripletrejection::ConfCtrMcParticleMcParticleMcParticle confCtr; tripletbuilder::TripletMcParticleMcParticleMcParticleBuilder< - mcparticlehistmanager::PrefixMcParticle1, - mcparticlehistmanager::PrefixMcParticle2, - mcparticlehistmanager::PrefixMcParticle3, + mcparticlehistmanager::PrefixMcParticle1Se, + mcparticlehistmanager::PrefixMcParticle2Se, + mcparticlehistmanager::PrefixMcParticle3Se, + mcparticlehistmanager::PrefixMcParticle1Me, + mcparticlehistmanager::PrefixMcParticle2Me, + mcparticlehistmanager::PrefixMcParticle3Me, triplethistmanager::PrefixMcParticleMcParticleMcParticleSe, triplethistmanager::PrefixMcParticleMcParticleMcParticleMe, closetripletrejection::PrefixMcParticle1McParticle2Se, @@ -101,9 +105,9 @@ struct FemtoTripletMcParticleMcParticleMcParticle { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; triplethistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoMcParticleMcParticleMcParticle", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; @@ -112,9 +116,9 @@ struct FemtoTripletMcParticleMcParticleMcParticle { { // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; // setup histogram specs std::map> colHistSpec = colhistmanager::makeColMcHistSpecMap(confCollisionBinning); @@ -130,9 +134,16 @@ struct FemtoTripletMcParticleMcParticleMcParticle { hRegistry.print(); }; - void processSameEvent(FilteredFemtoMcCollision const& col, FemtoMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + // deliberately takes the full (ungrouped) FilteredFemtoMcCollisions table rather than its ::iterator: + // pairing an ::iterator first argument with FemtoMcParticles as a second table argument would make + // DPL auto-register a sorted per-collision slicing cache for fMcColId, which then collides with the + // manual sliceByCachedUnsorted() used inside the builder (see the same workaround in + // femtoDndetaPair.cxx/femtoDndetaTriplet.cxx); we run the per-collision loop ourselves instead + void processSameEvent(FilteredFemtoMcCollisions const& cols, FemtoMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - tripletMcParticleMcParticleMcParticleBuilder.processSameEvent(col, mcParticles, mcMothers, mcPartonicMothers, mcParticlesPartition1, mcParticlesPartition2, mcParticlesPartition3, cache); + for (auto const& col : cols) { + tripletMcParticleMcParticleMcParticleBuilder.processSameEvent(col, mcParticles, mcMothers, mcPartonicMothers, mcParticlesPartition1, mcParticlesPartition2, mcParticlesPartition3, cache); + } } PROCESS_SWITCH(FemtoTripletMcParticleMcParticleMcParticle, processSameEvent, "Enable processing same event processing", true); diff --git a/PWGCF/Femto/Tasks/femtoTripletTrackTrackCascade.cxx b/PWGCF/Femto/Tasks/femtoTripletTrackTrackCascade.cxx index b84c7418248..07a67e9ee76 100644 --- a/PWGCF/Femto/Tasks/femtoTripletTrackTrackCascade.cxx +++ b/PWGCF/Femto/Tasks/femtoTripletTrackTrackCascade.cxx @@ -19,6 +19,8 @@ #include "PWGCF/Femto/Core/closeTripletRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" +#include "PWGCF/Femto/Core/dataTypes.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairCleaner.h" #include "PWGCF/Femto/Core/particleCleaner.h" @@ -34,11 +36,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -66,12 +70,22 @@ struct FemtoTripletTrackTrackCascade { using FemtoMcParticlesWithLabel = o2::soa::Join; + // opt-in: carries FColTags, only needed by the processXxxMixedEventWithTagging functions below. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + o2::framework::SliceCache cache; // setup collisions collisionbuilder::ConfCollisionSelection collisionSelection; o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by the processXxxMixedEventWithTagging functions below -- two separate + // instances since Xi and Omega are mutually exclusive on one instance, but tagged independently + // (see init()). + collisiontagger::ConfTagging confTaggingXi; + collisiontagger::ConfTagging confTaggingOmega; + // setup tracks trackbuilder::ConfTrackSelection1 confTrackSelections1; trackhistmanager::ConfTrackBinning1 confTrackBinning1; @@ -124,12 +138,18 @@ struct FemtoTripletTrackTrackCascade { tripletbuilder::TripletTrackTrackCascadeBuilder< modes::Cascade::kXi, - trackhistmanager::PrefixTrack1, - trackhistmanager::PrefixTrack2, - cascadehistmanager::PrefixXi, - trackhistmanager::PrefixCascadeBachelor, - trackhistmanager::PrefixCascadePosDaughter, - trackhistmanager::PrefixCascadeNegDaughter, + trackhistmanager::PrefixTrack1Se, + trackhistmanager::PrefixTrack2Se, + cascadehistmanager::PrefixXiSe, + trackhistmanager::PrefixCascadeBachelorSe, + trackhistmanager::PrefixCascadePosDaughterSe, + trackhistmanager::PrefixCascadeNegDaughterSe, + trackhistmanager::PrefixTrack1Me, + trackhistmanager::PrefixTrack2Me, + cascadehistmanager::PrefixXiMe, + trackhistmanager::PrefixCascadeBachelorMe, + trackhistmanager::PrefixCascadePosDaughterMe, + trackhistmanager::PrefixCascadeNegDaughterMe, triplethistmanager::PrefixTrackTrackCascadeSe, triplethistmanager::PrefixTrackTrackCascadeMe, closetripletrejection::PrefixTrack1Track2Se, @@ -146,12 +166,18 @@ struct FemtoTripletTrackTrackCascade { tripletbuilder::TripletTrackTrackCascadeBuilder< modes::Cascade::kOmega, - trackhistmanager::PrefixTrack1, - trackhistmanager::PrefixTrack2, - cascadehistmanager::PrefixOmega, - trackhistmanager::PrefixCascadeBachelor, - trackhistmanager::PrefixCascadePosDaughter, - trackhistmanager::PrefixCascadeNegDaughter, + trackhistmanager::PrefixTrack1Se, + trackhistmanager::PrefixTrack2Se, + cascadehistmanager::PrefixOmegaSe, + trackhistmanager::PrefixCascadeBachelorSe, + trackhistmanager::PrefixCascadePosDaughterSe, + trackhistmanager::PrefixCascadeNegDaughterSe, + trackhistmanager::PrefixTrack1Me, + trackhistmanager::PrefixTrack2Me, + cascadehistmanager::PrefixOmegaMe, + trackhistmanager::PrefixCascadeBachelorMe, + trackhistmanager::PrefixCascadePosDaughterMe, + trackhistmanager::PrefixCascadeNegDaughterMe, triplethistmanager::PrefixTrackTrackCascadeSe, triplethistmanager::PrefixTrackTrackCascadeMe, closetripletrejection::PrefixTrack1Track2Se, @@ -170,9 +196,9 @@ struct FemtoTripletTrackTrackCascade { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; triplethistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoTrackTrackCascade", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; @@ -181,28 +207,54 @@ struct FemtoTripletTrackTrackCascade { closepairrejection::ConfCprTrackCascadeBachelor confCprBachelor; closepairrejection::ConfCprTrackV0Daughter confCprV0Daughter; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - if ((static_cast(doprocessXiSameEvent) + static_cast(doprocessXiSameEventMc)) > 1 || (static_cast(doprocessXiMixedEvent) + static_cast(doprocessXiMixedEventMc)) > 1 || (static_cast(doprocessOmegaSameEvent) + static_cast(doprocessOmegaSameEventMc)) > 1 || (static_cast(doprocessOmegaMixedEvent) + static_cast(doprocessOmegaMixedEventMc)) > 1) { + if ((static_cast(doprocessXiSameEvent) + static_cast(doprocessXiSameEventMc)) > 1 || (static_cast(doprocessXiMixedEvent) + static_cast(doprocessXiMixedEventWithTagging) + static_cast(doprocessXiMixedEventMc)) > 1 || (static_cast(doprocessOmegaSameEvent) + static_cast(doprocessOmegaSameEventMc)) > 1 || (static_cast(doprocessOmegaMixedEvent) + static_cast(doprocessOmegaMixedEventWithTagging) + static_cast(doprocessOmegaMixedEventMc)) > 1) { LOG(fatal) << "More than 1 same or mixed event process function is activated. Breaking..."; } - bool processData = doprocessXiSameEvent || doprocessXiMixedEvent || doprocessOmegaSameEvent || doprocessOmegaMixedEvent; + bool processData = doprocessXiSameEvent || doprocessXiMixedEvent || doprocessXiMixedEventWithTagging || doprocessOmegaSameEvent || doprocessOmegaMixedEvent || doprocessOmegaMixedEventWithTagging; bool processMc = doprocessXiSameEventMc || doprocessXiMixedEventMc || doprocessOmegaSameEventMc || doprocessOmegaMixedEventMc; if (processData && processMc) { LOG(fatal) << "Both data and mc processing is activated. Breaking..."; } - bool processXi = doprocessXiSameEvent || doprocessXiSameEventMc || doprocessXiMixedEvent || doprocessXiMixedEventMc; - bool processOmega = doprocessOmegaSameEvent || doprocessOmegaSameEventMc || doprocessOmegaMixedEvent || doprocessOmegaMixedEventMc; + bool processXi = doprocessXiSameEvent || doprocessXiSameEventMc || doprocessXiMixedEvent || doprocessXiMixedEventWithTagging || doprocessXiMixedEventMc; + bool processOmega = doprocessOmegaSameEvent || doprocessOmegaSameEventMc || doprocessOmegaMixedEvent || doprocessOmegaMixedEventWithTagging || doprocessOmegaMixedEventMc; if (processXi && processOmega) { LOG(fatal) << "Both xi-track and omega-track processing is enabled. Breaking..."; } + + // Each species discovers its own tag bit independently (see Core/collisionTagger.h). + if (doprocessXiMixedEventWithTagging || doprocessOmegaMixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + if (doprocessXiMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingXi.Role); + if (bit < 0) { + LOG(fatal) << "processXiMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTripletTrackTrackCascade enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingXi.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Xi): " << bit; + } + if (doprocessOmegaMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingOmega.Role); + if (bit < 0) { + LOG(fatal) << "processOmegaMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTripletTrackTrackCascade enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingOmega.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Omega): " << bit; + } + } + // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; // setup histogram specs std::map> colHistSpec; @@ -268,6 +320,16 @@ struct FemtoTripletTrackTrackCascade { } PROCESS_SWITCH(FemtoTripletTrackTrackCascade, processXiSameEventMc, "Enable processing same event processing for tracks and xis with mc information", false); + // Opt-in counterpart of processXiMixedEvent() below -- same "Partition declared and bound + // inside process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. + void processXiMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoXis const& /*xis*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingXi.internalTagMask); + taggedCollisions.bindTable(cols); + tripletTrackTrackXiBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition1, trackPartition2, xiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoTripletTrackTrackCascade, processXiMixedEventWithTagging, "Enable processing mixed event processing for tracks and xis, pre-filtered using the collision tagger", false); + void processXiMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoXis const& /*xis*/) { tripletTrackTrackXiBuilder.processMixedEvent(cols, tracks, trackPartition1, trackPartition2, xiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -292,6 +354,16 @@ struct FemtoTripletTrackTrackCascade { } PROCESS_SWITCH(FemtoTripletTrackTrackCascade, processOmegaSameEventMc, "Enable processing same event processing for tracks and omegas with mc information", false); + // Opt-in counterpart of processOmegaMixedEvent() below -- same idiom as + // processXiMixedEventWithTagging above. + void processOmegaMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoOmegas const& /*omegas*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingOmega.internalTagMask); + taggedCollisions.bindTable(cols); + tripletTrackTrackOmegaBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition1, trackPartition2, omegaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoTripletTrackTrackCascade, processOmegaMixedEventWithTagging, "Enable processing mixed event processing for tracks and omegas, pre-filtered using the collision tagger", false); + void processOmegaMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoOmegas const& /*omegas*/) { tripletTrackTrackOmegaBuilder.processMixedEvent(cols, tracks, trackPartition1, trackPartition2, omegaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); diff --git a/PWGCF/Femto/Tasks/femtoTripletTrackTrackTrack.cxx b/PWGCF/Femto/Tasks/femtoTripletTrackTrackTrack.cxx index 199038f6372..bbd1bc6e439 100644 --- a/PWGCF/Femto/Tasks/femtoTripletTrackTrackTrack.cxx +++ b/PWGCF/Femto/Tasks/femtoTripletTrackTrackTrack.cxx @@ -17,6 +17,8 @@ #include "PWGCF/Femto/Core/closeTripletRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" +#include "PWGCF/Femto/Core/dataTypes.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairCleaner.h" #include "PWGCF/Femto/Core/particleCleaner.h" @@ -32,11 +34,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -61,6 +65,10 @@ struct FemtoTripletTrackTrackTrack { using FemtoMcParticlesWithLabel = o2::soa::Join; + // opt-in: carries FColTags, only needed by processMixedEventWithTagging. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + o2::framework::SliceCache cache; // setup collisions @@ -68,6 +76,9 @@ struct FemtoTripletTrackTrackTrack { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by processMixedEventWithTagging below. + collisiontagger::ConfTagging confTaggingTriplet; + // setup tracks trackbuilder::ConfTrackSelection1 confTrackSelections1; trackhistmanager::ConfTrackBinning1 confTrackBinning1; @@ -99,9 +110,12 @@ struct FemtoTripletTrackTrackTrack { closetripletrejection::ConfCtrTrackTrackTrack confCtr; tripletbuilder::TripletTrackTrackTrackBuilder< - trackhistmanager::PrefixTrack1, - trackhistmanager::PrefixTrack2, - trackhistmanager::PrefixTrack3, + trackhistmanager::PrefixTrack1Se, + trackhistmanager::PrefixTrack2Se, + trackhistmanager::PrefixTrack3Se, + trackhistmanager::PrefixTrack1Me, + trackhistmanager::PrefixTrack2Me, + trackhistmanager::PrefixTrack3Me, triplethistmanager::PrefixTrackTrackTrackSe, triplethistmanager::PrefixTrackTrackTrackMe, closetripletrejection::PrefixTrack1Track2Se, @@ -116,29 +130,42 @@ struct FemtoTripletTrackTrackTrack { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; triplethistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoTrackTrackTrack", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - if ((static_cast(doprocessSameEvent) + static_cast(doprocessSameEventMc)) > 1 || (static_cast(doprocessMixedEvent) + static_cast(doprocessMixedEventMc)) > 1) { + if ((static_cast(doprocessSameEvent) + static_cast(doprocessSameEventMc)) > 1 || (static_cast(doprocessMixedEvent) + static_cast(doprocessMixedEventWithTagging) + static_cast(doprocessMixedEventMc)) > 1) { LOG(fatal) << "More than 1 same or mixed event process function is activated. Breaking..."; } - bool processData = doprocessSameEvent || doprocessMixedEvent; + bool processData = doprocessSameEvent || doprocessMixedEvent || doprocessMixedEventWithTagging; bool processMc = doprocessSameEventMc || doprocessMixedEventMc; if (processData && processMc) { LOG(fatal) << "Both data and mc processing is activated. Breaking..."; } + if (doprocessMixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingTriplet.Role); + if (bit < 0) { + LOG(fatal) << "processMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTripletTrackTrackTrack enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingTriplet.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << ": " << bit; + } + // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; // setup histogram specs @@ -180,6 +207,16 @@ struct FemtoTripletTrackTrackTrack { } PROCESS_SWITCH(FemtoTripletTrackTrackTrack, processSameEventMc, "Enable processing same event processing", false); + // Opt-in counterpart of processMixedEvent() below -- same "Partition declared and bound inside + // process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. + void processMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingTriplet.internalTagMask); + taggedCollisions.bindTable(cols); + tripletTrackTrackTrackBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition1, trackPartition2, trackPartition3, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoTripletTrackTrackTrack, processMixedEventWithTagging, "Enable processing mixed event processing, pre-filtered using the collision tagger", false); + void processMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks) { tripletTrackTrackTrackBuilder.processMixedEvent(cols, tracks, trackPartition1, trackPartition2, trackPartition3, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); diff --git a/PWGCF/Femto/Tasks/femtoTripletTrackTrackV0.cxx b/PWGCF/Femto/Tasks/femtoTripletTrackTrackV0.cxx index aa1610d48e1..df29c51daf0 100644 --- a/PWGCF/Femto/Tasks/femtoTripletTrackTrackV0.cxx +++ b/PWGCF/Femto/Tasks/femtoTripletTrackTrackV0.cxx @@ -17,6 +17,8 @@ #include "PWGCF/Femto/Core/closeTripletRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" +#include "PWGCF/Femto/Core/dataTypes.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairCleaner.h" #include "PWGCF/Femto/Core/particleCleaner.h" @@ -34,11 +36,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -68,6 +72,10 @@ struct FemtoTripletTrackTrackV0 { using FemtoMcParticlesWithLabel = o2::soa::Join; + // opt-in: carries FColTags, only needed by processMixedEventWithTagging. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + o2::framework::SliceCache cache; // setup collisions @@ -75,6 +83,9 @@ struct FemtoTripletTrackTrackV0 { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by processMixedEventWithTagging below. + collisiontagger::ConfTagging confTaggingTriplet; + // setup tracks trackbuilder::ConfTrackSelection1 confTrackSelections1; trackhistmanager::ConfTrackBinning1 confTrackBinning1; @@ -117,11 +128,16 @@ struct FemtoTripletTrackTrackV0 { tripletbuilder::TripletTrackTrackV0Builder< modes::V0::kLambda, - trackhistmanager::PrefixTrack1, - trackhistmanager::PrefixTrack2, - v0histmanager::PrefixLambda1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, + trackhistmanager::PrefixTrack1Se, + trackhistmanager::PrefixTrack2Se, + v0histmanager::PrefixLambda1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + trackhistmanager::PrefixTrack1Me, + trackhistmanager::PrefixTrack2Me, + v0histmanager::PrefixLambda1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, triplethistmanager::PrefixTrackTrackLambdaSe, triplethistmanager::PrefixTrackTrackLambdaMe, closetripletrejection::PrefixTrack1Track2Se, @@ -136,29 +152,42 @@ struct FemtoTripletTrackTrackV0 { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; triplethistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoTrackTrackV0", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - if ((static_cast(doprocessSameEvent) + static_cast(doprocessSameEventMc)) > 1 || (static_cast(doprocessMixedEvent) + static_cast(doprocessMixedEventMc)) > 1) { + if ((static_cast(doprocessSameEvent) + static_cast(doprocessSameEventMc)) > 1 || (static_cast(doprocessMixedEvent) + static_cast(doprocessMixedEventWithTagging) + static_cast(doprocessMixedEventMc)) > 1) { LOG(fatal) << "More than 1 same or mixed event process function is activated. Breaking..."; } - bool processData = doprocessSameEvent || doprocessMixedEvent; + bool processData = doprocessSameEvent || doprocessMixedEvent || doprocessMixedEventWithTagging; bool processMc = doprocessSameEventMc || doprocessMixedEventMc; if (processData && processMc) { LOG(fatal) << "Both data and mc processing is activated. Breaking..."; } + if (doprocessMixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingTriplet.Role); + if (bit < 0) { + LOG(fatal) << "processMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTripletTrackTrackV0 enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingTriplet.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << ": " << bit; + } + // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; // setup histogram specs std::map> colHistSpec; @@ -206,6 +235,16 @@ struct FemtoTripletTrackTrackV0 { } PROCESS_SWITCH(FemtoTripletTrackTrackV0, processSameEventMc, "Enable processing same event processing", false); + // Opt-in counterpart of processMixedEvent() below -- same "Partition declared and bound inside + // process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. + void processMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingTriplet.internalTagMask); + taggedCollisions.bindTable(cols); + tripletTrackTrackLambdaBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition1, trackPartition2, lambdaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoTripletTrackTrackV0, processMixedEventWithTagging, "Enable processing mixed event processing, pre-filtered using the collision tagger", false); + void processMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/) { tripletTrackTrackLambdaBuilder.processMixedEvent(cols, tracks, trackPartition1, trackPartition2, lambdaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); diff --git a/PWGCF/Femto/Tasks/femtoTwoPhotonResonanceQa.cxx b/PWGCF/Femto/Tasks/femtoTwoPhotonResonanceQa.cxx new file mode 100644 index 00000000000..afcf432c8e4 --- /dev/null +++ b/PWGCF/Femto/Tasks/femtoTwoPhotonResonanceQa.cxx @@ -0,0 +1,143 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoTwoPhotonResonanceQa.cxx +/// \brief QA task for resonances built from two photons (pi0, eta, ...) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#include "PWGCF/Femto/Core/collisionBuilder.h" +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/Core/photonHistManager.h" +#include "PWGCF/Femto/Core/twoPhotonResonanceBuilder.h" +#include "PWGCF/Femto/Core/twoPhotonResonanceHistManager.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2::analysis::femto; + +struct FemtoTwoPhotonResonanceQa { + + // setup tables + using FemtoCollisions = o2::soa::Join; + using FilteredFemtoCollisions = o2::soa::Filtered; + using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; + + using FemtoPi0s = o2::soa::Join; + using FemtoEtas = o2::soa::Join; + using FemtoPhotons = o2::soa::Join; + + o2::framework::SliceCache cache; + + // setup for collisions + collisionbuilder::ConfCollisionSelection collisionSelection; + o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); + colhistmanager::CollisionHistManager colHistManager; + colhistmanager::ConfCollisionBinning confCollisionBinning; + colhistmanager::ConfCollisionQaBinning confCollisionQaBinning; + + // setup for pi0s + twophotonresonancebuilder::ConfPi0Selection confPi0Selection; + o2::framework::Partition pi0Partition = MAKE_TWOPHOTONRESONANCE_PARTITION(confPi0Selection); + o2::framework::Preslice perColPi0s = o2::aod::femtobase::stored::fColId; + + twophotonresonancehistmanager::ConfPi0Binning confPi0Binning; + twophotonresonancehistmanager::TwoPhotonResonanceHistManager< + twophotonresonancehistmanager::PrefixPi0, + photonhistmanager::PrefixTwoPhotonResonanceDau1Qa, + photonhistmanager::PrefixTwoPhotonResonanceDau2Qa, + modes::TwoPhotonResonance::kPi0> + pi0HistManager; + + // setup for etas + twophotonresonancebuilder::ConfEtaSelection confEtaSelection; + o2::framework::Partition etaPartition = MAKE_TWOPHOTONRESONANCE_PARTITION(confEtaSelection); + o2::framework::Preslice perColEtas = o2::aod::femtobase::stored::fColId; + + twophotonresonancehistmanager::ConfEtaBinning confEtaBinning; + twophotonresonancehistmanager::TwoPhotonResonanceHistManager< + twophotonresonancehistmanager::PrefixEta, + photonhistmanager::PrefixTwoPhotonResonanceDau1Qa, + photonhistmanager::PrefixTwoPhotonResonanceDau2Qa, + modes::TwoPhotonResonance::kEta> + etaHistManager; + + // setup for daughters (shared between pi0 and eta -- unordered, no pos/neg or species asymmetry) + photonhistmanager::ConfPhotonBinning confPhotonDauBinning; + photonhistmanager::ConfPhotonQaBinning confPhotonDauQaBinning; + + o2::framework::HistogramRegistry hRegistry{"FemtoTwoPhotonResonanceQa", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + + void init(o2::framework::InitContext&) + { + auto colHistSpec = colhistmanager::makeColQaHistSpecMap(confCollisionBinning, confCollisionQaBinning); + colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); + + auto dauHistSpec = photonhistmanager::makePhotonQaHistSpecMap(confPhotonDauBinning, confPhotonDauQaBinning); + + if ((static_cast(doprocessPi0s) + static_cast(doprocessEtas)) > 1) { + LOG(fatal) << "Only one process can be activated"; + } + + if (doprocessPi0s) { + auto pi0HistSpec = twophotonresonancehistmanager::makeTwoPhotonResonanceQaHistSpecMap(confPi0Binning); + pi0HistManager.init(&hRegistry, pi0HistSpec, dauHistSpec, confPhotonDauQaBinning); + } + if (doprocessEtas) { + auto etaHistSpec = twophotonresonancehistmanager::makeTwoPhotonResonanceQaHistSpecMap(confEtaBinning); + etaHistManager.init(&hRegistry, etaHistSpec, dauHistSpec, confPhotonDauQaBinning); + } + } + + void processPi0s(FilteredFemtoCollision const& col, FemtoPi0s const& /*pi0s*/, FemtoPhotons const& photons) + { + auto pi0Slice = pi0Partition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (pi0Slice.size() == 0) { + return; + } + colHistManager.fill(col); + for (auto const& pi0 : pi0Slice) { + pi0HistManager.fill(pi0, photons); + } + } + PROCESS_SWITCH(FemtoTwoPhotonResonanceQa, processPi0s, "Process Pi0s", true); + + void processEtas(FilteredFemtoCollision const& col, FemtoEtas const& /*etas*/, FemtoPhotons const& photons) + { + auto etaSlice = etaPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (etaSlice.size() == 0) { + return; + } + colHistManager.fill(col); + for (auto const& eta : etaSlice) { + etaHistManager.fill(eta, photons); + } + } + PROCESS_SWITCH(FemtoTwoPhotonResonanceQa, processEtas, "Process Etas", false); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{ + adaptAnalysisTask(context), + }; + return workflow; +} diff --git a/PWGCF/Femto/Tasks/femtoV0Qa.cxx b/PWGCF/Femto/Tasks/femtoV0Qa.cxx index 18e6a485e99..4ae00099140 100644 --- a/PWGCF/Femto/Tasks/femtoV0Qa.cxx +++ b/PWGCF/Femto/Tasks/femtoV0Qa.cxx @@ -143,11 +143,11 @@ struct FemtoV0Qa { negDaughterHistSpec = trackhistmanager::makeTrackQaHistSpecMap(confV0NegDaughterBinning, confV0NegDaughterQaBinning); if (doprocessLambda) { lambdaHistSpec = v0histmanager::makeV0QaHistSpecMap(confLambdaBinning, confLambdaQaBinning); - lambdaHistManager.init(&hRegistry, lambdaHistSpec, confLambdaSelection, confLambdaQaBinning, posDaughterHistSpec, confV0PosDaughterQaBinning, negDaughterHistSpec, confV0NegDaughterQaBinning); + lambdaHistManager.init(&hRegistry, lambdaHistSpec, confLambdaSelection, confLambdaQaBinning, posDaughterHistSpec, confV0PosDaughterQaBinning, negDaughterHistSpec, confV0NegDaughterQaBinning); } if (doprocessK0short) { k0shortHistSpec = v0histmanager::makeV0QaHistSpecMap(confK0shortBinning, confK0shortQaBinning); - k0shortHistManager.init(&hRegistry, k0shortHistSpec, confK0shortSelection, confK0shortQaBinning, posDaughterHistSpec, confV0PosDaughterQaBinning, negDaughterHistSpec, confV0NegDaughterQaBinning); + k0shortHistManager.init(&hRegistry, k0shortHistSpec, confK0shortSelection, confK0shortQaBinning, posDaughterHistSpec, confV0PosDaughterQaBinning, negDaughterHistSpec, confV0NegDaughterQaBinning); } } else { colHistSpec = colhistmanager::makeColMcQaHistSpecMap(confCollisionBinning, confCollisionQaBinning); @@ -156,11 +156,11 @@ struct FemtoV0Qa { negDaughterHistSpec = trackhistmanager::makeTrackMcQaHistSpecMap(confV0NegDaughterBinning, confV0NegDaughterQaBinning); if (doprocessLambdaMc) { lambdaHistSpec = v0histmanager::makeV0McQaHistSpecMap(confLambdaBinning, confLambdaQaBinning); - lambdaHistManager.init(&hRegistry, lambdaHistSpec, confLambdaSelection, confLambdaQaBinning, posDaughterHistSpec, confV0PosDaughterQaBinning, negDaughterHistSpec, confV0NegDaughterQaBinning); + lambdaHistManager.init(&hRegistry, lambdaHistSpec, confLambdaSelection, confLambdaQaBinning, posDaughterHistSpec, confV0PosDaughterQaBinning, negDaughterHistSpec, confV0NegDaughterQaBinning); } if (doprocessK0shortMc) { k0shortHistSpec = v0histmanager::makeV0McQaHistSpecMap(confK0shortBinning, confK0shortQaBinning); - k0shortHistManager.init(&hRegistry, k0shortHistSpec, confK0shortSelection, confK0shortQaBinning, posDaughterHistSpec, confV0PosDaughterQaBinning, negDaughterHistSpec, confV0NegDaughterQaBinning); + k0shortHistManager.init(&hRegistry, k0shortHistSpec, confK0shortSelection, confK0shortQaBinning, posDaughterHistSpec, confV0PosDaughterQaBinning, negDaughterHistSpec, confV0NegDaughterQaBinning); } } hRegistry.print(); @@ -174,7 +174,7 @@ struct FemtoV0Qa { } colHistManager.fill(col); for (auto const& k0short : k0shortSlice) { - k0shortHistManager.fill(k0short, tracks); + k0shortHistManager.fill(k0short, tracks); } } PROCESS_SWITCH(FemtoV0Qa, processK0short, "Process k0shorts", false); @@ -190,7 +190,7 @@ struct FemtoV0Qa { if (!k0shortCleaner.isClean(k0short, mcParticles, mcMothers, mcPartonicMothers)) { continue; } - k0shortHistManager.fill(k0short, tracks, col, mcParticles, mcMothers, mcPartonicMothers); + k0shortHistManager.fill(k0short, tracks, col, mcParticles, mcMothers, mcPartonicMothers); } } PROCESS_SWITCH(FemtoV0Qa, processK0shortMc, "Process k0shorts with MC information", false); @@ -203,7 +203,7 @@ struct FemtoV0Qa { } colHistManager.fill(col); for (auto const& lambda : lambdaSlice) { - lambdaHistManager.fill(lambda, tracks); + lambdaHistManager.fill(lambda, tracks); } } PROCESS_SWITCH(FemtoV0Qa, processLambda, "Process lambdas", true); @@ -219,7 +219,7 @@ struct FemtoV0Qa { if (!lambdaCleaner.isClean(lambda, mcParticles, mcMothers, mcPartonicMothers)) { continue; } - lambdaHistManager.fill(lambda, tracks, col, mcParticles, mcMothers, mcPartonicMothers); + lambdaHistManager.fill(lambda, tracks, col, mcParticles, mcMothers, mcPartonicMothers); } } PROCESS_SWITCH(FemtoV0Qa, processLambdaMc, "Process lambdas with MC informaton", false); diff --git a/PWGCF/Femto3D/TableProducer/singleTrackSelector.cxx b/PWGCF/Femto3D/TableProducer/singleTrackSelector.cxx index 22c2d5d4fe0..7f20deb9c56 100644 --- a/PWGCF/Femto3D/TableProducer/singleTrackSelector.cxx +++ b/PWGCF/Femto3D/TableProducer/singleTrackSelector.cxx @@ -85,6 +85,7 @@ struct singleTrackSelector { Configurable> keepWithinNsigmaTPC{"keepWithinNsigmaTPC", std::vector{-4.0f, 4.0f}, "TPC range for preselection of particles specified with PDG"}; Configurable> _particlesToReject{"particlesToRejectPDGs", std::vector{211, 321, 1000020030}, "PDG codes of particles that will be rejected with TOF (only pion, kaon, proton and deurton are supported now)"}; Configurable> rejectWithinNsigmaTOF{"rejectWithinNsigmaTOF", std::vector{-5.0f, 5.0f}, "TOF rejection Nsigma range for particles specified with PDG to be rejected"}; + Configurable> rejectWithinNsigmaTPC{"rejectWithinNsigmaTPC", std::vector{-5.0f, 5.0f}, "TPC Nsigma range required together with rejectWithinNsigmaTOF for particles specified with PDG to be rejected"}; Configurable _pRemoveTofOutOfRange{"pRemoveTofOutOfRange", 100.f, "momentum starting from which request TOF nSigma to be within the stored range (-10 < Nsigma < 10)"}; Configurable> _ptRemoveTofOutOfRange{"ptRemoveTofOutOfRange", {100.f, -10.f, 10.f}, "transverse momentum starting from which request TOF nSigma to be within the stored range (-10 < Nsigma < 10)"}; @@ -247,7 +248,7 @@ struct singleTrackSelector { for (auto i : particlesToReject) { // if satisfied, want to continue in the upper loop (over tracks) -- skip the current track // cannot use simple 'continue' since it will be applied to the current loop, so have to use a flag - if (o2::aod::singletrackselector::TOFselection(track, std::make_pair(i, rejectWithinNsigmaTOF))) { + if (o2::aod::singletrackselector::TOFselection(track, std::make_pair(i, rejectWithinNsigmaTOF), rejectWithinNsigmaTPC.value)) { skip_track = true; break; } @@ -259,7 +260,7 @@ struct singleTrackSelector { for (auto ii : particlesToKeep) if (o2::aod::singletrackselector::TPCselection(track, std::make_pair(ii, keepWithinNsigmaTPC))) { - if (track.p() > _pRemoveTofOutOfRange && !o2::aod::singletrackselector::TOFselection(track, std::make_pair(ii, std::vector{-10.0, 10.0}), std::vector{-10.0, 10.0})) + if (track.p() > _pRemoveTofOutOfRange && !o2::aod::singletrackselector::TOFselection(track, std::make_pair(ii, std::vector{-6.0, 6.0}), std::vector{-10.0, 10.0})) continue; if (track.pt() > _ptRemoveTofOutOfRange.value[0] && !o2::aod::singletrackselector::TOFselection(track, std::make_pair(ii, std::vector{_ptRemoveTofOutOfRange.value[1], _ptRemoveTofOutOfRange.value[2]}), std::vector{-10.f, +10.f})) continue; diff --git a/PWGCF/Femto3D/Tasks/femto3dPairTask.cxx b/PWGCF/Femto3D/Tasks/femto3dPairTask.cxx index 4d08256aa4b..0d29ebf334f 100644 --- a/PWGCF/Femto3D/Tasks/femto3dPairTask.cxx +++ b/PWGCF/Femto3D/Tasks/femto3dPairTask.cxx @@ -94,8 +94,9 @@ struct FemtoCorrelations { Configurable> _tofNSigma_2{"tofNSigma_2", std::vector{-3.0f, 3.0f}, "second particle PID: Nsigma range in TOF"}; Configurable> _tpcNSigmaResidual_2{"tpcNSigmaResidual_2", std::vector{-5.0f, 5.0f}, "second particle PID: residual TPC Nsigma cut to use with the TOF"}; - Configurable _particlePDGtoReject{"particlePDGtoRejectFromSecond", 0, "applied only if the particles are non-identical and only to the second particle in the pair!!!"}; - Configurable> _rejectWithinNsigmaTOF{"rejectWithinNsigmaTOF", std::vector{-0.0f, 0.0f}, "TOF rejection Nsigma range for the particle specified with PDG to be rejected"}; + Configurable _particlePDGtoReject{"particlePDGtoRejectFromFirst", 211, "if the particles are non-identical then applied only to the first"}; + Configurable _rejectWithinNsigmaTPC{"rejectWithinNsigmaTPC", 0.0, "TPC rejection Nsigma abs range for the particle specified with PDG to be rejected"}; + Configurable _rejectWithinNsigmaTOF{"rejectWithinNsigmaTOF", 0.0, "TOF rejection Nsigma abs range for the particle specified with PDG to be rejected"}; Configurable _dPhiMode{"dPhiMode", 0, "Flag to choose how to calc. dphi*: 0 - at a fixed TPC radius; 1 - average over different TPC radii;"}; Configurable _radiusTPC{"radiusTPC", 1.2, "TPC radius to calculate phi_star for"}; @@ -122,7 +123,7 @@ struct FemtoCorrelations { // P.P.P.P.S this way is still being testing i might be reconsidered; might change in the future, keep looking at the source code Configurable _MEreductionFactor{"MEreductionFactor", 1, "only one (pseudo)randomly choosen event out per pair $value events will be processed and contribute to the final mixing (if < 2 -> all the possible event pairs (per vertex¢ bin) will be processed); implemented for the sake of efficiency; look at the source code;"}; - bool IsIdentical; + bool IsIdentical = true; std::pair> TPCcuts_1; std::pair> TOFcuts_1; @@ -258,7 +259,7 @@ struct FemtoCorrelations { } if (_fillDetaDphi > -1) { - TString suffix[] = {"_nocut", "_cut"}; + const TString suffix[2] = {"_nocut", "_cut"}; for (int f = 0; f < 2; f++) { std::vector> DoubleTrack_SE_histos_perMult; @@ -450,6 +451,9 @@ struct FemtoCorrelations { continue; if (track.sign() == _sign_1 && (track.p() < _PIDtrshld_1 ? o2::aod::singletrackselector::TPCselection(track, TPCcuts_1, _itsNSigma_1.value) : o2::aod::singletrackselector::TOFselection(track, TOFcuts_1, _tpcNSigmaResidual_1.value))) { // filling the map: eventID <-> selected particles1 + if ((_rejectWithinNsigmaTPC != 0.0 && std::fabs(o2::aod::singletrackselector::getTPCNsigma(track, _particlePDGtoReject)) < _rejectWithinNsigmaTPC) || (_rejectWithinNsigmaTOF != 0.0 && std::fabs(o2::aod::singletrackselector::getTOFNsigma(track, _particlePDGtoReject)) < _rejectWithinNsigmaTOF)) + continue; + selectedtracks_1[track.singleCollSelId()].push_back(std::make_shared::iterator>(track)); pHisto_first->Fill(track.p()); @@ -460,7 +464,7 @@ struct FemtoCorrelations { if (IsIdentical) { continue; - } else if (track.sign() != _sign_2 && !TOFselection(track, std::make_pair(_particlePDGtoReject, _rejectWithinNsigmaTOF)) && (track.p() < _PIDtrshld_2 ? o2::aod::singletrackselector::TPCselection(track, TPCcuts_2, _itsNSigma_2.value) : o2::aod::singletrackselector::TOFselection(track, TOFcuts_2, _tpcNSigmaResidual_2.value))) { // filling the map: eventID <-> selected particles2 if (see condition above ^) + } else if (track.sign() != _sign_2 && (track.p() < _PIDtrshld_2 ? o2::aod::singletrackselector::TPCselection(track, TPCcuts_2, _itsNSigma_2.value) : o2::aod::singletrackselector::TOFselection(track, TOFcuts_2, _tpcNSigmaResidual_2.value))) { // filling the map: eventID <-> selected particles2 if (see condition above ^) selectedtracks_2[track.singleCollSelId()].push_back(std::make_shared::iterator>(track)); pHisto_second->Fill(track.p()); diff --git a/PWGCF/Femto3D/Tasks/femto3dQA.cxx b/PWGCF/Femto3D/Tasks/femto3dQA.cxx index e55cd3c73ed..5e218e1a938 100644 --- a/PWGCF/Femto3D/Tasks/femto3dQA.cxx +++ b/PWGCF/Femto3D/Tasks/femto3dQA.cxx @@ -75,15 +75,16 @@ struct QAHistograms { Configurable _tpcFractionSharedCls{"maxTpcFractionSharedCls", 0.4, "maximum fraction of TPC shared clasters"}; Configurable _itsNCls{"minItsNCls", 0, "minimum allowed number of ITS clasters for a track"}; Configurable _itsChi2NCl{"itsChi2NCl", 100.0, "upper limit for chi2 value of a fit over ITS clasters for a track"}; - Configurable _particlePDG{"particlePDG", 2212, "PDG code of a particle to perform PID for (only pion, kaon, proton and deurton are supported now)"}; + Configurable _particlePDG{"particlePDG", 2212, "PDG code of a particle to perform PID for"}; Configurable> _tpcNSigma{"tpcNSigma", std::vector{-4.0f, 4.0f}, "Nsigma range in TPC before the TOF is used"}; Configurable> _itsNSigma{"itsNSigma", std::vector{-10.0f, 10.0f}, "Nsigma range in ITS to use along with TPC"}; Configurable _PIDtrshld{"PIDtrshld", 10.0, "value of momentum from which the PID is done with TOF (before that only TPC is used)"}; Configurable> _tofNSigma{"tofNSigma", std::vector{-4.0f, 4.0f}, "Nsigma range in TOF"}; Configurable> _tpcNSigmaResidual{"tpcNSigmaResidual", std::vector{-5.0f, 5.0f}, "residual TPC Nsigma cut to use with the TOF"}; - Configurable _particlePDGtoReject{"particlePDGtoReject", 211, "PDG codes of perticles that will be rejected with TOF (only pion, kaon, proton and deurton are supported now)"}; - Configurable> _rejectWithinNsigmaTOF{"rejectWithinNsigmaTOF", std::vector{-0.0f, 0.0f}, "TOF rejection Nsigma range for particles specified with PDG to be rejected"}; + Configurable _particlePDGtoReject{"particlePDGtoReject", 211, "PDG codes of particles that will be rejected with TPC and/or TOF"}; + Configurable _rejectWithinNsigmaTPC{"rejectWithinNsigmaTPC", 0.0, "TPC rejection Nsigma abs for particles specified with PDG to be rejected"}; + Configurable _rejectWithinNsigmaTOF{"rejectWithinNsigmaTOF", 0.0, "TOF rejection Nsigma abs for particles specified with PDG to be rejected"}; Configurable> _centCut{"centCut", std::pair{0.f, 100.f}, "[min., max.] centrality range to keep tracks within"}; @@ -235,7 +236,10 @@ struct QAHistograms { registry.fill(HIST("TOFSignal_nocuts"), track.p(), track.beta()); } - if (!TOFselection(track, std::make_pair(_particlePDGtoReject, _rejectWithinNsigmaTOF)) && (track.p() < _PIDtrshld ? o2::aod::singletrackselector::TPCselection(track, TPCcuts, _itsNSigma.value) : o2::aod::singletrackselector::TOFselection(track, TOFcuts, _tpcNSigmaResidual.value))) { + if (track.p() < _PIDtrshld ? o2::aod::singletrackselector::TPCselection(track, TPCcuts, _itsNSigma.value) : o2::aod::singletrackselector::TOFselection(track, TOFcuts, _tpcNSigmaResidual.value)) { + if ((_rejectWithinNsigmaTPC != 0.0 && std::fabs(o2::aod::singletrackselector::getTPCNsigma(track, _particlePDGtoReject)) < _rejectWithinNsigmaTPC) || (_rejectWithinNsigmaTOF != 0.0 && std::fabs(o2::aod::singletrackselector::getTOFNsigma(track, _particlePDGtoReject)) < _rejectWithinNsigmaTOF)) + continue; + registry.fill(HIST("eta"), track.eta()); registry.fill(HIST("phi"), track.phi()); registry.fill(HIST("px"), track.px()); diff --git a/PWGCF/Femto3D/Tasks/multEstimator.cxx b/PWGCF/Femto3D/Tasks/multEstimator.cxx index fc284861c28..1d6b9e845eb 100644 --- a/PWGCF/Femto3D/Tasks/multEstimator.cxx +++ b/PWGCF/Femto3D/Tasks/multEstimator.cxx @@ -14,20 +14,19 @@ /// \since 31 May 2023 #include "PWGCF/Femto3D/DataModel/PIDutils.h" -#include "PWGCF/Femto3D/DataModel/singletrackselector.h" +#include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/RCTSelectionFlags.h" #include "Common/CCDB/ctpRateFetcher.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseITS.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" #include -#include -#include #include #include #include @@ -42,9 +41,11 @@ #include #include +#include -#include #include +#include +#include #include #include #include diff --git a/PWGCF/FemtoDream/Core/femtoDreamContainer.h b/PWGCF/FemtoDream/Core/femtoDreamContainer.h index 98daa9eb00f..ec18b4a1d1a 100644 --- a/PWGCF/FemtoDream/Core/femtoDreamContainer.h +++ b/PWGCF/FemtoDream/Core/femtoDreamContainer.h @@ -30,6 +30,7 @@ #include +#include #include #include #include @@ -223,7 +224,12 @@ class FemtoDreamContainer void init_base_3Dqn(const std::string& folderName, const std::string& femtoDKout, const std::string& femtoDKside, const std::string& femtoDKlong, T& femtoDKoutAxis, T& femtoDKsideAxis, T& femtoDKlongAxis, T& mTAxi4D, T& multPercentileAxis4D, T& qnAxis, T& pairPhiAxis) { - mHistogramRegistry->add((folderName + "/relPair3dRmTMultPercentileQnPairphi").c_str(), ("; " + femtoDKout + femtoDKside + femtoDKlong + "; #it{m}_{T} (GeV/#it{c}); Centrality; qn; #varphi_{pair} - #Psi_{EP}").c_str(), o2::framework::HistType::kTHnSparseF, {femtoDKoutAxis, femtoDKsideAxis, femtoDKlongAxis, mTAxi4D, multPercentileAxis4D, qnAxis, pairPhiAxis}); + if (mStoreEProt) { + // DK_x, DK_y, DK_z are the EP-rotated (B-frame) axes; DK_y is out-of-plane (||B). + mHistogramRegistry->add((folderName + "/relPair3dEProtRmTMultPercentileQnPairphi").c_str(), "; DK_{x} (GeV/#it{c}); DK_{y} (GeV/#it{c}); DK_{z} (GeV/#it{c}); #it{m}_{T} (GeV/#it{c}); Centrality; qn; #varphi_{pair} - #Psi_{EP}", o2::framework::HistType::kTHnSparseF, {femtoDKoutAxis, femtoDKsideAxis, femtoDKlongAxis, mTAxi4D, multPercentileAxis4D, qnAxis, pairPhiAxis}); + } else { + mHistogramRegistry->add((folderName + "/relPair3dRmTMultPercentileQnPairphi").c_str(), ("; " + femtoDKout + femtoDKside + femtoDKlong + "; #it{m}_{T} (GeV/#it{c}); Centrality; qn; #varphi_{pair} - #Psi_{EP}").c_str(), o2::framework::HistType::kTHnSparseF, {femtoDKoutAxis, femtoDKsideAxis, femtoDKlongAxis, mTAxi4D, multPercentileAxis4D, qnAxis, pairPhiAxis}); + } } template @@ -261,15 +267,23 @@ class FemtoDreamContainer framework::AxisSpec qnAxis = {std::move(qnBins), "qn"}; framework::AxisSpec pairPhiAxis = {std::move(pairPhiBins), "#varphi_{pair} - #Psi_{EP} (rad)"}; + // EP-rotated (B-frame) axis labels, same binning as out/side/long. + framework::AxisSpec DKxAxis = {DKoutBins, "DK_{x} (GeV/#it{c})"}; + framework::AxisSpec DKyAxis = {DKsideBins, "DK_{y} (GeV/#it{c})"}; + framework::AxisSpec DKzAxis = {DKlongBins, "DK_{z} (GeV/#it{c})"}; + framework::AxisSpec& mom1Axis = mStoreEProt ? DKxAxis : DKoutAxis; + framework::AxisSpec& mom2Axis = mStoreEProt ? DKyAxis : DKsideAxis; + framework::AxisSpec& mom3Axis = mStoreEProt ? DKzAxis : DKlongAxis; + std::string folderName = static_cast(mFolderSuffix[mEventType]) + static_cast(o2::aod::femtodreamMCparticle::MCTypeName[o2::aod::femtodreamMCparticle::MCType::kRecon]) + static_cast("_3Dqn"); init_base_3Dqn(folderName, femtoObsDKout, femtoObsDKside, femtoObsDKlong, - DKoutAxis, DKsideAxis, DKlongAxis, mTAxis4D, multPercentileAxis4D, qnAxis, pairPhiAxis); + mom1Axis, mom2Axis, mom3Axis, mTAxis4D, multPercentileAxis4D, qnAxis, pairPhiAxis); if (isMC) { folderName = static_cast(mFolderSuffix[mEventType]) + static_cast(o2::aod::femtodreamMCparticle::MCTypeName[o2::aod::femtodreamMCparticle::MCType::kTruth]) + static_cast("_3Dqn"); init_base_3Dqn(folderName, femtoObsDKout, femtoObsDKside, femtoObsDKlong, - DKoutAxis, DKsideAxis, DKlongAxis, mTAxis4D, multPercentileAxis4D, qnAxis, pairPhiAxis); + mom1Axis, mom2Axis, mom3Axis, mTAxis4D, multPercentileAxis4D, qnAxis, pairPhiAxis); init_3Dqn_MC(folderName, femtoObsDKout, femtoObsDKside, femtoObsDKlong, DKoutAxis, DKsideAxis, DKlongAxis, smearingByOrigin); } @@ -286,6 +300,9 @@ class FemtoDreamContainer mPDGTwo = pdg2; } + /// Store EP-rotated (B-frame) DK_x,DK_y,DK_z instead of out,side,long. Call before init_3Dqn(). + void setStoreEProt(bool doStore) { mStoreEProt = doStore; } + /// Pass a pair to the container and compute all the relevant observables /// Called by setPair both in case of data/ and Monte Carlo reconstructed and for Monte Carlo truth /// \tparam T type of the femtodreamparticle @@ -508,6 +525,16 @@ class FemtoDreamContainer mHistogramRegistry->fill(HIST(mFolderSuffix[mEventType]) + HIST(o2::aod::femtodreamMCparticle::MCTypeName[mc]) + HIST("_3Dqn") + HIST("/relPair3dRmTMultPercentileQnPairphi"), femtoDKout, femtoDKside, femtoDKlong, mT, multPercentile, myQnBin, pairPhiEP); } + /// EP-rotated (B-frame) version: rotate (out, side) by the signed φ_pair − Ψ_EP so DK_y is out-of-plane (||B); DK_z unchanged. + template + void setPair_3Dqn_base(const float femtoDKout, const float femtoDKside, const float femtoDKlong, const float mT, const float multPercentile, const float myQnBin, const float pairPhiEP, const float pairPhiEPforRot) + { + const float DKx = femtoDKout * std::cos(pairPhiEPforRot) - femtoDKside * std::sin(pairPhiEPforRot); + const float DKy = femtoDKout * std::sin(pairPhiEPforRot) + femtoDKside * std::cos(pairPhiEPforRot); + const float DKz = femtoDKlong; + mHistogramRegistry->fill(HIST(mFolderSuffix[mEventType]) + HIST(o2::aod::femtodreamMCparticle::MCTypeName[mc]) + HIST("_3Dqn") + HIST("/relPair3dEProtRmTMultPercentileQnPairphi"), DKx, DKy, DKz, mT, multPercentile, myQnBin, pairPhiEP); + } + /// Called by setPair_3Dqn only in case of Monte Carlo truth /// Fills resolution of DKout, DKside, DKlong void setPair_3Dqn_MC(std::vector k3dMC, std::vector k3d, const int originPart1, const int originPart2, bool smearingByOrigin) @@ -535,9 +562,14 @@ class FemtoDreamContainer const float mT = FemtoDreamMath::getmT(part1, mMassOne, part2, mMassTwo); const float pairPhiEP = FemtoDreamMath::getPairPhiEP(part1, mMassOne, part2, mMassTwo, eventPlane); + const float pairPhiEPforRot = mStoreEProt ? FemtoDreamMath::getPairPhiEPSigned(part1, mMassOne, part2, mMassTwo, eventPlane) : 0.f; if (mHistogramRegistry) { - setPair_3Dqn_base(DKout, DKside, DKlong, mT, multPercentile, myQnBin, pairPhiEP); + if (mStoreEProt) { + setPair_3Dqn_base(DKout, DKside, DKlong, mT, multPercentile, myQnBin, pairPhiEP, pairPhiEPforRot); + } else { + setPair_3Dqn_base(DKout, DKside, DKlong, mT, multPercentile, myQnBin, pairPhiEP); + } if constexpr (isMC) { if (part1.has_fdMCParticle() && part2.has_fdMCParticle()) { @@ -549,9 +581,14 @@ class FemtoDreamContainer } const float mTMC = FemtoDreamMath::getmT(part1.fdMCParticle(), mMassOne, part2.fdMCParticle(), mMassTwo); const float pairPhiEPMC = FemtoDreamMath::getPairPhiEP(part1.fdMCParticle(), mMassOne, part2.fdMCParticle(), mMassTwo, eventPlane); + const float pairPhiEPMCforRot = mStoreEProt ? FemtoDreamMath::getPairPhiEPSigned(part1.fdMCParticle(), mMassOne, part2.fdMCParticle(), mMassTwo, eventPlane) : 0.f; if (std::abs(part1.fdMCParticle().pdgMCTruth()) == mPDGOne && std::abs(part2.fdMCParticle().pdgMCTruth()) == mPDGTwo) { // Note: all pair-histogramms are filled with MC truth information ONLY in case of non-fake candidates - setPair_3Dqn_base(k3dMC[1], k3dMC[2], k3dMC[3], mTMC, multPercentile, myQnBin, pairPhiEPMC); + if (mStoreEProt) { + setPair_3Dqn_base(k3dMC[1], k3dMC[2], k3dMC[3], mTMC, multPercentile, myQnBin, pairPhiEPMC, pairPhiEPMCforRot); + } else { + setPair_3Dqn_base(k3dMC[1], k3dMC[2], k3dMC[3], mTMC, multPercentile, myQnBin, pairPhiEPMC); + } setPair_3Dqn_MC(k3dMC, k3d, part1.fdMCParticle().partOriginMCTruth(), part2.fdMCParticle().partOriginMCTruth(), smearingByOrigin); } else { mHistogramRegistry->fill(HIST(mFolderSuffix[mEventType]) + HIST(o2::aod::femtodreamMCparticle::MCTypeName[o2::aod::femtodreamMCparticle::MCType::kTruth]) + HIST("/hFakePairsCounter"), 0); @@ -580,9 +617,14 @@ class FemtoDreamContainer const float mT = FemtoDreamMath::getmT(part1, mMassOne, part2, mMassTwo); const float pairPhiEP = FemtoDreamMath::getPairPhiEP(part1, mMassOne, part2, mMassTwo, EP1, EP2); + const float pairPhiEPforRot = mStoreEProt ? FemtoDreamMath::getPairPhiEPSigned(part1, mMassOne, part2, mMassTwo, EP1, EP2) : 0.f; if (mHistogramRegistry) { - setPair_3Dqn_base(DKout, DKside, DKlong, mT, multPercentile, myQnBin, pairPhiEP); + if (mStoreEProt) { + setPair_3Dqn_base(DKout, DKside, DKlong, mT, multPercentile, myQnBin, pairPhiEP, pairPhiEPforRot); + } else { + setPair_3Dqn_base(DKout, DKside, DKlong, mT, multPercentile, myQnBin, pairPhiEP); + } if constexpr (isMC) { if (part1.has_fdMCParticle() && part2.has_fdMCParticle()) { @@ -594,9 +636,14 @@ class FemtoDreamContainer } const float mTMC = FemtoDreamMath::getmT(part1.fdMCParticle(), mMassOne, part2.fdMCParticle(), mMassTwo); const float pairPhiEPMC = FemtoDreamMath::getPairPhiEP(part1.fdMCParticle(), mMassOne, part2.fdMCParticle(), mMassTwo, EP1, EP2); + const float pairPhiEPMCforRot = mStoreEProt ? FemtoDreamMath::getPairPhiEPSigned(part1.fdMCParticle(), mMassOne, part2.fdMCParticle(), mMassTwo, EP1, EP2) : 0.f; if (std::abs(part1.fdMCParticle().pdgMCTruth()) == mPDGOne && std::abs(part2.fdMCParticle().pdgMCTruth()) == mPDGTwo) { // Note: all pair-histogramms are filled with MC truth information ONLY in case of non-fake candidates - setPair_3Dqn_base(k3dMC[1], k3dMC[2], k3dMC[3], mTMC, multPercentile, myQnBin, pairPhiEPMC); + if (mStoreEProt) { + setPair_3Dqn_base(k3dMC[1], k3dMC[2], k3dMC[3], mTMC, multPercentile, myQnBin, pairPhiEPMC, pairPhiEPMCforRot); + } else { + setPair_3Dqn_base(k3dMC[1], k3dMC[2], k3dMC[3], mTMC, multPercentile, myQnBin, pairPhiEPMC); + } setPair_3Dqn_MC(k3dMC, k3d, part1.fdMCParticle().partOriginMCTruth(), part2.fdMCParticle().partOriginMCTruth(), smearingByOrigin); } else { mHistogramRegistry->fill(HIST(mFolderSuffix[mEventType]) + HIST(o2::aod::femtodreamMCparticle::MCTypeName[o2::aod::femtodreamMCparticle::MCType::kTruth]) + HIST("/hFakePairsCounter"), 0); @@ -619,6 +666,7 @@ class FemtoDreamContainer int mPDGOne = 0; ///< PDG code of particle 1 int mPDGTwo = 0; ///< PDG code of particle 2 float mHighkstarCut = 6.; + bool mStoreEProt = false; ///< Store EP-rotated (B-frame) DK_x,DK_y,DK_z instead of out,side,long }; } // namespace o2::analysis::femtoDream diff --git a/PWGCF/FemtoDream/Core/femtoDreamMath.h b/PWGCF/FemtoDream/Core/femtoDreamMath.h index 2ddc7291818..90a83a9ed72 100644 --- a/PWGCF/FemtoDream/Core/femtoDreamMath.h +++ b/PWGCF/FemtoDream/Core/femtoDreamMath.h @@ -373,6 +373,29 @@ class FemtoDreamMath phi_pair_onPsi = TMath::Abs(phi_pair_onPsi); return phi_pair_onPsi; } + + /// Signed φ_pair − Ψ_EP (same as getPairPhiEP without the final |·|); used as the B-frame rotation angle. + template + static float getPairPhiEPSigned(const T1& part1, const float mass1, const T2& part2, const float mass2, const float Psi_ep) + { + const ROOT::Math::PtEtaPhiMVector vecpart1(part1.pt(), part1.eta(), part1.phi(), mass1); + const ROOT::Math::PtEtaPhiMVector vecpart2(part2.pt(), part2.eta(), part2.phi(), mass2); + const ROOT::Math::PtEtaPhiMVector trackSum = vecpart1 + vecpart2; + return TVector2::Phi_mpi_pi(trackSum.Phi() - Psi_ep); + } + + /// Signed counterpart of the plane-calibrated (two-event-plane) getPairPhiEP. + template + static float getPairPhiEPSigned(const T1& part1, const float mass1, const T2& part2, const float mass2, const float Psi_ep1, const float Psi_ep2) + { + const ROOT::Math::PtEtaPhiMVector vecpart1(part1.pt(), part1.eta(), part1.phi(), mass1); + const ROOT::Math::PtEtaPhiMVector vecpart2(part2.pt(), part2.eta(), part2.phi(), mass2); + const float psidiff = Psi_ep2 - Psi_ep1; + const float newPhi2 = TVector2::Phi_mpi_pi(vecpart2.Phi() - psidiff); + const ROOT::Math::PtEtaPhiMVector vecpart2_calibd(vecpart2.Pt(), vecpart2.Eta(), newPhi2, vecpart2.M()); + const ROOT::Math::PtEtaPhiMVector trackSum = vecpart1 + vecpart2_calibd; + return TVector2::Phi_mpi_pi(trackSum.Phi() - Psi_ep1); + } }; } // namespace o2::analysis::femtoDream diff --git a/PWGCF/FemtoDream/Core/femtoDreamParticleHisto.h b/PWGCF/FemtoDream/Core/femtoDreamParticleHisto.h index 096b9bb7b28..410e79774a5 100644 --- a/PWGCF/FemtoDream/Core/femtoDreamParticleHisto.h +++ b/PWGCF/FemtoDream/Core/femtoDreamParticleHisto.h @@ -233,6 +233,12 @@ class FemtoDreamParticleHisto mHistogramRegistry->add((folderName + folderSuffix + "/Debug/hPDGmother_SecondaryDaughterLambda").c_str(), "; PDG mother; Entries", o2::framework::HistType::kTH1I, {{12001, -6000.5, 6000.5}}); mHistogramRegistry->add((folderName + folderSuffix + "/Debug/hPDGmother_SecondaryDaughterSigmaplus").c_str(), "; PDG mother; Entries", o2::framework::HistType::kTH1I, {{12001, -6000.5, 6000.5}}); mHistogramRegistry->add((folderName + folderSuffix + "/Debug/hPDGmother_SecondaryDaughterOmegaMinus").c_str(), "; PDG mother; Entries", o2::framework::HistType::kTH1I, {{12001, -6000.5, 6000.5}}); + if (std::abs(mPDG) == kKPlus) { + mHistogramRegistry->add((folderName + folderSuffix + "/Debug/hPDGmother_SecondaryFromHF").c_str(), "; PDG mother; Entries", o2::framework::HistType::kTH1I, {{12001, -6000.5, 6000.5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/Debug/hPDGmother_SecondaryFromLF").c_str(), "; PDG mother; Entries", o2::framework::HistType::kTH1I, {{12001, -6000.5, 6000.5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_SecondaryFromHF").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTHnSparseF, {tempFitVarpTAxis, tempFitVarAxis, dcazAxis, multAxis}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_SecondaryFromLF").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTHnSparseF, {tempFitVarpTAxis, tempFitVarAxis, dcazAxis, multAxis}); + } mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Primary").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTHnSparseF, {tempFitVarpTAxis, tempFitVarAxis, dcazAxis, multAxis}); mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Secondary").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTHnSparseF, {tempFitVarpTAxis, tempFitVarAxis, dcazAxis, multAxis}); @@ -245,6 +251,10 @@ class FemtoDreamParticleHisto mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Else").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTHnSparseF, {tempFitVarpTAxis, tempFitVarAxis, dcazAxis, multAxis}); } else { mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Primary").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); + if (std::abs(mPDG) == kKPlus) { + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_SecondaryFromHF").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_SecondaryFromLF").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); + } mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Secondary").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Material").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_WrongCollision").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); @@ -597,6 +607,16 @@ class FemtoDreamParticleHisto mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_Secondary"), part.pt(), part.tempFitVar(), part.dcaZ(), mult); break; + case (o2::aod::femtodreamMCparticle::kSecondaryFromHF): + mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/Debug/hPDGmother_SecondaryFromHF"), part.fdExtMCParticle().motherPDG()); + mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_SecondaryFromHF"), + part.pt(), part.tempFitVar(), part.dcaZ(), mult); + break; + case (o2::aod::femtodreamMCparticle::kSecondaryFromLF): + mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/Debug/hPDGmother_SecondaryFromLF"), part.fdExtMCParticle().motherPDG()); + mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_SecondaryFromLF"), + part.pt(), part.tempFitVar(), part.dcaZ(), mult); + break; case (o2::aod::femtodreamMCparticle::kMaterial): mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/Debug/hPDGmother_Material"), part.fdExtMCParticle().motherPDG()); mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_Material"), @@ -645,6 +665,14 @@ class FemtoDreamParticleHisto mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_Secondary"), part.pt(), part.tempFitVar()); break; + case (o2::aod::femtodreamMCparticle::kSecondaryFromHF): + mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_SecondaryFromHF"), + part.pt(), part.tempFitVar()); + break; + case (o2::aod::femtodreamMCparticle::kSecondaryFromLF): + mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_SecondaryFromLF"), + part.pt(), part.tempFitVar()); + break; case (o2::aod::femtodreamMCparticle::kMaterial): mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_Material"), part.pt(), part.tempFitVar()); diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackCascade.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackCascade.cxx index 1c793e973e3..3b8164861ee 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackCascade.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackCascade.cxx @@ -211,9 +211,9 @@ struct femtoDreamPairTaskTrackCascade { Configurable Depth{"Depth", 5, "Number of events for mixing"}; Configurable Policy{"BinPolicy", 0, "Binning policy for mixing - 0: multiplicity, 1: multipliciy percentile, 2: both"}; } Mixing; - ColumnBinningPolicy colBinningMult{{Mixing.BinVztx, Mixing.BinMult}, true}; - ColumnBinningPolicy colBinningMultPercentile{{Mixing.BinVztx, Mixing.BinMultPercentile}, true}; - ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.BinVztx, Mixing.BinMult, Mixing.BinMultPercentile}, true}; + ColumnBinningPolicy colBinningMult{{Mixing.BinVztx, Mixing.BinMult}}; + ColumnBinningPolicy colBinningMultPercentile{{Mixing.BinVztx, Mixing.BinMultPercentile}}; + ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.BinVztx, Mixing.BinMult, Mixing.BinMultPercentile}}; FemtoDreamContainer sameEventCont; FemtoDreamContainer mixedEventCont; FemtoDreamPairCleaner pairCleaner; @@ -227,9 +227,9 @@ struct femtoDreamPairTaskTrackCascade { void init(InitContext&) { // setup binnnig policy for mixing - colBinningMult = {{Mixing.BinVztx, Mixing.BinMult}, true}; - colBinningMultPercentile = {{Mixing.BinVztx, Mixing.BinMultPercentile}, true}; - colBinningMultMultPercentile = {{Mixing.BinVztx, Mixing.BinMult, Mixing.BinMultPercentile}, true}; + colBinningMult = {{Mixing.BinVztx, Mixing.BinMult}}; + colBinningMultPercentile = {{Mixing.BinVztx, Mixing.BinMultPercentile}}; + colBinningMultMultPercentile = {{Mixing.BinVztx, Mixing.BinMult, Mixing.BinMultPercentile}}; eventHisto.init(&Registry, Option.IsMC); trackHistoPartOne.init(&Registry, Binning.multTempFit, Option.Dummy, Binning.pTTrack, Option.Dummy, Option.Dummy, Binning.TempFitVarTrack, Option.Dummy, Option.Dummy, Option.Dummy, Option.Dummy, Option.Dummy, Option.Dummy, Option.IsMC, Track1.PDGCode); trackHistoPartTwo.init(&Registry, Binning.multTempFit, Option.Dummy, Binning.pTCascade, Option.Dummy, Option.Dummy, Binning.TempFitVarCascade, Option.Dummy, Option.Dummy, Option.Dummy, Option.Dummy, Binning.InvMass, Option.Dummy, Option.IsMC, Cascade2.PDGCode); diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackTrack.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackTrack.cxx index 22e69c80e49..cb482f58715 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackTrack.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackTrack.cxx @@ -115,6 +115,7 @@ struct femtoDreamPairTaskTrackTrack { ConfigurableAxis DKlong{"DKlong", {500, -2., 2.}, "binning DKlong for the 3-D femtoscopy plot: R_long(LCMS) vs mT vs multiplicity percentile vs qnBin vs pait phi wrt EP (set <> to true)"}; ConfigurableAxis qnBins{"qnBins", {10, 0, 10}, "binning of qn interval"}; ConfigurableAxis pairPhiBins{"pairPhiBins", {12, 0., TMath::Pi()}, "binning of pair phi"}; + Configurable storeEProt{"storeEProt", false, "Store EP-rotated (B-frame) DK_x,DK_y,DK_z instead of out,side,long in the 3D qn histogram"}; } EPCal; using FilteredCollisions = soa::Filtered; @@ -258,11 +259,11 @@ struct femtoDreamPairTaskTrackTrack { Configurable Policy{"Policy", 0, "Binning policy for mixing - 0: multiplicity, 1: multipliciy percentile, 2: both, 3: multipliciy percentile and qn value, 4: multipliciy percentile and event plane"}; } Mixing; - ColumnBinningPolicy colBinningMult{{Mixing.VztxMixBins, Mixing.MultMixBins}, true}; - ColumnBinningPolicy colBinningMultPercentile{{Mixing.VztxMixBins, Mixing.MultPercentileMixBins}, true}; - ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.VztxMixBins, Mixing.MultMixBins, Mixing.MultPercentileMixBins}, true}; - ColumnBinningPolicy colBinningMultPercentileqn{{Mixing.VztxMixBins, Mixing.MultPercentileMixBins, Mixing.QnMixBins}, true}; - ColumnBinningPolicy colBinningMultPercentileEP{{Mixing.VztxMixBins, Mixing.MultPercentileMixBins, Mixing.EPMixBins}, true}; + ColumnBinningPolicy colBinningMult{{Mixing.VztxMixBins, Mixing.MultMixBins}}; + ColumnBinningPolicy colBinningMultPercentile{{Mixing.VztxMixBins, Mixing.MultPercentileMixBins}}; + ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.VztxMixBins, Mixing.MultMixBins, Mixing.MultPercentileMixBins}}; + ColumnBinningPolicy colBinningMultPercentileqn{{Mixing.VztxMixBins, Mixing.MultPercentileMixBins, Mixing.QnMixBins}}; + ColumnBinningPolicy colBinningMultPercentileEP{{Mixing.VztxMixBins, Mixing.MultPercentileMixBins, Mixing.EPMixBins}}; FemtoDreamContainer sameEventCont; FemtoDreamContainer mixedEventCont; @@ -283,11 +284,11 @@ struct femtoDreamPairTaskTrackTrack { { // setup columnpolicy for binning - colBinningMult = {{Mixing.VztxMixBins, Mixing.MultMixBins}, true}; - colBinningMultPercentile = {{Mixing.VztxMixBins, Mixing.MultPercentileMixBins}, true}; - colBinningMultMultPercentile = {{Mixing.VztxMixBins, Mixing.MultMixBins, Mixing.MultPercentileMixBins}, true}; - colBinningMultPercentileqn = {{Mixing.VztxMixBins, Mixing.MultPercentileMixBins, Mixing.QnMixBins}, true}; - colBinningMultPercentileEP = {{Mixing.VztxMixBins, Mixing.MultPercentileMixBins, Mixing.EPMixBins}, true}; + colBinningMult = {{Mixing.VztxMixBins, Mixing.MultMixBins}}; + colBinningMultPercentile = {{Mixing.VztxMixBins, Mixing.MultPercentileMixBins}}; + colBinningMultMultPercentile = {{Mixing.VztxMixBins, Mixing.MultMixBins, Mixing.MultPercentileMixBins}}; + colBinningMultPercentileqn = {{Mixing.VztxMixBins, Mixing.MultPercentileMixBins, Mixing.QnMixBins}}; + colBinningMultPercentileEP = {{Mixing.VztxMixBins, Mixing.MultPercentileMixBins, Mixing.EPMixBins}}; if (Option.RandomizePair.value) { random = new TRandom3(0); @@ -333,6 +334,8 @@ struct femtoDreamPairTaskTrackTrack { } if (EPCal.do3DFemto) { + sameEventQnCont.setStoreEProt(EPCal.storeEProt); + mixedEventQnCont.setStoreEProt(EPCal.storeEProt); sameEventQnCont.init_3Dqn(&Registry, EPCal.DKout, EPCal.DKside, EPCal.DKlong, Binning4D.mT, Binning4D.multPercentile, Option.IsMC, EPCal.qnBins, EPCal.pairPhiBins, Option.SmearingByOrigin); mixedEventQnCont.init_3Dqn(&Registry, EPCal.DKout, EPCal.DKside, EPCal.DKlong, diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackV0.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackV0.cxx index da2559e82ba..ccf630b78a8 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackV0.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackV0.cxx @@ -224,9 +224,9 @@ struct femtoDreamPairTaskTrackV0 { Configurable Policy{"BinPolicy", 0, "Binning policy for mixing - 0: multiplicity, 1: multipliciy percentile, 2: both"}; } Mixing; - ColumnBinningPolicy colBinningMult{{Mixing.BinVztx, Mixing.BinMult}, true}; - ColumnBinningPolicy colBinningMultPercentile{{Mixing.BinVztx, Mixing.BinMultPercentile}, true}; - ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.BinVztx, Mixing.BinMult, Mixing.BinMultPercentile}, true}; + ColumnBinningPolicy colBinningMult{{Mixing.BinVztx, Mixing.BinMult}}; + ColumnBinningPolicy colBinningMultPercentile{{Mixing.BinVztx, Mixing.BinMultPercentile}}; + ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.BinVztx, Mixing.BinMult, Mixing.BinMultPercentile}}; FemtoDreamContainer sameEventCont; FemtoDreamContainer mixedEventCont; @@ -240,9 +240,9 @@ struct femtoDreamPairTaskTrackV0 { void init(InitContext& context) { // setup binnnig policy for mixing - colBinningMult = {{Mixing.BinVztx, Mixing.BinMult}, true}; - colBinningMultPercentile = {{Mixing.BinVztx, Mixing.BinMultPercentile}, true}; - colBinningMultMultPercentile = {{Mixing.BinVztx, Mixing.BinMult, Mixing.BinMultPercentile}, true}; + colBinningMult = {{Mixing.BinVztx, Mixing.BinMult}}; + colBinningMultPercentile = {{Mixing.BinVztx, Mixing.BinMultPercentile}}; + colBinningMultMultPercentile = {{Mixing.BinVztx, Mixing.BinMult, Mixing.BinMultPercentile}}; eventHisto.init(&Registry, Option.IsMC); trackHistoPartOne.init(&Registry, Binning.multTempFit, Option.Dummy, Binning.pTTrack, Option.Dummy, Option.Dummy, Binning.TempFitVarTrack, Option.Dummy, Option.Dummy, Option.Dummy, Option.Dummy, Option.Dummy, Option.Dummy, Option.IsMC, Track1.PDGCode); diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskV0Reso.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskV0Reso.cxx index 290682891c7..5bd1511f013 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskV0Reso.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskV0Reso.cxx @@ -223,9 +223,9 @@ struct FemtoDreamPairTaskV0Reso { (aod::femtodreamparticle::mLambda > Reso2.invMassMin) && (aod::femtodreamparticle::mLambda < Reso2.invMassMax); - ColumnBinningPolicy colBinningMult{{Mixing.binVztx, Mixing.binMult}, true}; - ColumnBinningPolicy colBinningMultPercentile{{Mixing.binVztx, Mixing.binMultPercentile}, true}; - ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.binVztx, Mixing.binMult, Mixing.binMultPercentile}, true}; + ColumnBinningPolicy colBinningMult{{Mixing.binVztx, Mixing.binMult}}; + ColumnBinningPolicy colBinningMultPercentile{{Mixing.binVztx, Mixing.binMultPercentile}}; + ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.binVztx, Mixing.binMult, Mixing.binMultPercentile}}; Filter eventMultiplicity = aod::femtodreamcollision::multNtr >= EventSel.multMin && aod::femtodreamcollision::multNtr <= EventSel.multMax; Filter eventMultiplicityPercentile = aod::femtodreamcollision::multV0M >= EventSel.multPercentileMin && aod::femtodreamcollision::multV0M <= EventSel.multPercentileMax; @@ -251,9 +251,9 @@ struct FemtoDreamPairTaskV0Reso { { // setup binnnig policy for mixing - colBinningMult = {{Mixing.binVztx, Mixing.binMult}, true}; - colBinningMultPercentile = {{Mixing.binVztx, Mixing.binMultPercentile}, true}; - colBinningMultMultPercentile = {{Mixing.binVztx, Mixing.binMult, Mixing.binMultPercentile}, true}; + colBinningMult = {{Mixing.binVztx, Mixing.binMult}}; + colBinningMultPercentile = {{Mixing.binVztx, Mixing.binMultPercentile}}; + colBinningMultMultPercentile = {{Mixing.binVztx, Mixing.binMult, Mixing.binMultPercentile}}; eventHisto.init(®istry, Option.isMC); diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskV0V0.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskV0V0.cxx index 82ba66ed43e..bc5191724c6 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskV0V0.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskV0V0.cxx @@ -208,9 +208,9 @@ struct femtoDreamPairTaskV0V0 { Configurable Policy{"Policy", 0, "Binning policy for mixing - 0: multiplicity, 1: multipliciy percentile, 2: both"}; } Mixing; - ColumnBinningPolicy colBinningMult{{Mixing.VztxMixBins, Mixing.MultMixBins}, true}; - ColumnBinningPolicy colBinningMultPercentile{{Mixing.VztxMixBins, Mixing.MultPercentileMixBins}, true}; - ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.VztxMixBins, Mixing.MultMixBins, Mixing.MultPercentileMixBins}, true}; + ColumnBinningPolicy colBinningMult{{Mixing.VztxMixBins, Mixing.MultMixBins}}; + ColumnBinningPolicy colBinningMultPercentile{{Mixing.VztxMixBins, Mixing.MultPercentileMixBins}}; + ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.VztxMixBins, Mixing.MultMixBins, Mixing.MultPercentileMixBins}}; FemtoDreamContainer sameEventCont; FemtoDreamContainer mixedEventCont; @@ -225,9 +225,9 @@ struct femtoDreamPairTaskV0V0 { void init(InitContext&) { // setup columnpolicy for binning - colBinningMult = {{Mixing.VztxMixBins, Mixing.MultMixBins}, true}; - colBinningMultPercentile = {{Mixing.VztxMixBins, Mixing.MultPercentileMixBins}, true}; - colBinningMultMultPercentile = {{Mixing.VztxMixBins, Mixing.MultMixBins, Mixing.MultPercentileMixBins}, true}; + colBinningMult = {{Mixing.VztxMixBins, Mixing.MultMixBins}}; + colBinningMultPercentile = {{Mixing.VztxMixBins, Mixing.MultPercentileMixBins}}; + colBinningMultMultPercentile = {{Mixing.VztxMixBins, Mixing.MultMixBins, Mixing.MultPercentileMixBins}}; if (Option.RandomizePair.value) { random = new TRandom3(0); diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackCascade.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackCascade.cxx index d1e127065f0..1599bedb0bb 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackCascade.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackCascade.cxx @@ -131,7 +131,7 @@ struct femtoDreamTripletTaskTrackTrackCascade { ConfigurableAxis ConfVtxBins{"ConfVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; ConfigurableAxis ConfQ3Bins{"ConfQ3Bins", {2000, 0., 8.}, "binning Q3"}; ConfigurableAxis ConfQ3BinsFor4D{"ConfQ3BinsFor4D", {500, 0., 2.}, "binning Q3 for 4D hist"}; - ColumnBinningPolicy colBinning{{ConfVtxBins, ConfMultBins}, true}; + ColumnBinningPolicy colBinning{{ConfVtxBins, ConfMultBins}}; Configurable ConfNEventsMix{"ConfNEventsMix", 5, "Number of events for mixing"}; Configurable ConfIsCPR{"ConfIsCPR", true, "Close Pair Rejection"}; Configurable ConfFillCPRQA{"ConfFillCPRQA", false, "Fill Close Pair Rejection plots as a function of eta and phi"}; @@ -157,7 +157,7 @@ struct femtoDreamTripletTaskTrackTrackCascade { void init(InitContext& context) { eventHisto.init(&qaRegistry, false); - colBinning = {{ConfVtxBins, ConfMultBins}, true}; + colBinning = {{ConfVtxBins, ConfMultBins}}; trackHistoSelectedParts.init(&qaRegistry, ConfDummy, ConfDummy, ConfTempFitVarpTBins, ConfDummy, ConfDummy, ConfTempFitVarBinsTrack, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfIsMC, ConfPDGCodePart); particleHistoSelectedCascades.init(&qaRegistry, ConfDummy, ConfDummy, ConfTempFitVarpTV0Bins, ConfDummy, ConfDummy, ConfTempFitVarBinsCascade, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfInvMassBins, ConfDummy, ConfIsMC, ConfPDGCodeCascade); particleHistoPosChild.init(&qaRegistry, ConfDummy, ConfDummy, ConfTempFitVarpTV0Child, ConfDummy, ConfDummy, ConfTempFitVarBinsCascadeChild, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfIsMC, 0); diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackTrack.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackTrack.cxx index 1b5bf143300..7456409d1bc 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackTrack.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackTrack.cxx @@ -122,7 +122,7 @@ struct femtoDreamTripletTaskTrackTrackTrack { ConfigurableAxis ConfMultBins{"ConfMultBins", {VARIABLE_WIDTH, 0.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; ConfigurableAxis ConfVtxBins{"ConfVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinning{{ConfVtxBins, ConfMultBins}, true}; + ColumnBinningPolicy colBinning{{ConfVtxBins, ConfMultBins}}; ConfigurableAxis ConfQ3Bins{"ConfQ3Bins", {2000, 0., 8.}, "binning Q3"}; ConfigurableAxis ConfQ3BinsFor4D{"ConfQ3BinsFor4D", {500, 0., 2.}, "binning Q3 for 4D hist"}; @@ -150,7 +150,7 @@ struct femtoDreamTripletTaskTrackTrackTrack { eventHisto.init(&qaRegistry, false); - colBinning = {{ConfVtxBins, ConfMultBins}, true}; + colBinning = {{ConfVtxBins, ConfMultBins}}; trackHistoSelectedParts.init(&qaRegistry, ConfBinmultTempFit, ConfDummy, ConfTempFitVarpTBins, ConfDummy, ConfDummy, ConfTempFitVarBins, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfIsMC, ConfPDGCodePart); trackHistoALLSelectedParts.init(&qaRegistry, ConfBinmultTempFit, ConfDummy, ConfTempFitVarpTBins, ConfDummy, ConfDummy, ConfTempFitVarBins, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfIsMC, ConfPDGCodePart); diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackTrackPbPb.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackTrackPbPb.cxx index 8be1593da40..9a5087d9f6b 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackTrackPbPb.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackTrackPbPb.cxx @@ -129,7 +129,7 @@ struct femtoDreamTripletTaskTrackTrackTrackPbPb { ConfigurableAxis ConfMultBins{"ConfMultBins", {VARIABLE_WIDTH, 0.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; ConfigurableAxis ConfVtxBins{"ConfVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinning{{ConfVtxBins, ConfMultBins}, true}; + ColumnBinningPolicy colBinning{{ConfVtxBins, ConfMultBins}}; ConfigurableAxis ConfQ3Bins{"ConfQ3Bins", {2000, 0., 8.}, "binning Q3"}; ConfigurableAxis ConfQ3BinsFor4D{"ConfQ3BinsFor4D", {500, 0., 2.}, "binning Q3 for 4D hist"}; @@ -157,7 +157,7 @@ struct femtoDreamTripletTaskTrackTrackTrackPbPb { eventHisto.init(&qaRegistry, false); - colBinning = {{ConfVtxBins, ConfMultBins}, true}; + colBinning = {{ConfVtxBins, ConfMultBins}}; trackHistoSelectedParts.init(&qaRegistry, ConfBinmultTempFit, ConfDummy, ConfTempFitVarpTBins, ConfDummy, ConfDummy, ConfTempFitVarBins, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfIsMC, ConfPDGCodePart); trackHistoALLSelectedParts.init(&qaRegistry, ConfBinmultTempFit, ConfDummy, ConfTempFitVarpTBins, ConfDummy, ConfDummy, ConfTempFitVarBins, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfIsMC, ConfPDGCodePart); diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackV0.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackV0.cxx index 84bcb611cae..8702d8d5c77 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackV0.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackV0.cxx @@ -167,7 +167,7 @@ struct femtoDreamTripletTaskTrackTrackV0 { ConfigurableAxis ConfMultBins{"ConfMultBins", {VARIABLE_WIDTH, 0.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; ConfigurableAxis ConfVtxBins{"ConfVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinning{{ConfVtxBins, ConfMultBins}, true}; + ColumnBinningPolicy colBinning{{ConfVtxBins, ConfMultBins}}; ConfigurableAxis ConfQ3Bins{"ConfQ3Bins", {2000, 0., 8.}, "binning Q3"}; ConfigurableAxis ConfQ3BinsFor4D{"ConfQ3BinsFor4D", {500, 0., 2.}, "binning Q3 for 4D hist"}; @@ -200,7 +200,7 @@ struct femtoDreamTripletTaskTrackTrackV0 { eventHisto.init(&qaRegistry, false); - colBinning = {{ConfVtxBins, ConfMultBins}, true}; + colBinning = {{ConfVtxBins, ConfMultBins}}; trackHistoSelectedParts.init(&qaRegistry, ConfDummy, ConfDummy, ConfTempFitVarpTBins, ConfDummy, ConfDummy, ConfTempFitVarBinsTrack, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfIsMC, ConfPDGCodePart); trackHistoALLSelectedParts.init(&qaRegistry, ConfDummy, ConfDummy, ConfTempFitVarpTBins, ConfDummy, ConfDummy, ConfTempFitVarBinsTrack, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfIsMC, ConfPDGCodePart); diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackV0PbPb.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackV0PbPb.cxx index e74ec599d5c..1eecc066d27 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackV0PbPb.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackV0PbPb.cxx @@ -179,7 +179,7 @@ struct FemtoDreamTripletTaskTrackTrackV0PbPb { ConfigurableAxis confMultBins{"confMultBins", {VARIABLE_WIDTH, 0.0f, 23.0f, 38.0f, 53.0f, 81.0f, 110.0f, 157.0f, 205.0f, 278.0f, 351.0f, 455.0f, 559.0f, 703.0f, 848.0f, 1050.0f, 1253.0f, 1530.0f, 1668.0f, 1857.0f, 2047.0f, 99999.f}, "Mixing bins - multiplicity"}; ConfigurableAxis confVtxBins{"confVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; ConfigurableAxis confQ3Bins{"confQ3Bins", {2000, 0., 8.}, "binning Q3"}; ConfigurableAxis confQ3BinsFor4D{"confQ3BinsFor4D", {500, 0., 2.}, "binning Q3 for 4D hist"}; @@ -212,7 +212,7 @@ struct FemtoDreamTripletTaskTrackTrackV0PbPb { eventHisto.init(&qaRegistry, false); - colBinning = {{confVtxBins, confMultBins}, true}; + colBinning = {{confVtxBins, confMultBins}}; trackHistoselectedParts.init(&qaRegistry, confDummy, confDummy, confTempFitVarpTBins, confDummy, confDummy, confTempFitVarBinsTrack, confDummy, confDummy, confDummy, confDummy, confDummy, confDummy, confIsMC, confPDGCodePart); trackHistoALLselectedParts.init(&qaRegistry, confDummy, confDummy, confTempFitVarpTBins, confDummy, confDummy, confTempFitVarBinsTrack, confDummy, confDummy, confDummy, confDummy, confDummy, confDummy, confIsMC, confPDGCodePart); diff --git a/PWGCF/FemtoDream/Tasks/femtodreamPairCascadeCascade.cxx b/PWGCF/FemtoDream/Tasks/femtodreamPairCascadeCascade.cxx index e68ed83f7d3..3c4ebd17713 100644 --- a/PWGCF/FemtoDream/Tasks/femtodreamPairCascadeCascade.cxx +++ b/PWGCF/FemtoDream/Tasks/femtodreamPairCascadeCascade.cxx @@ -200,9 +200,9 @@ struct FemtoDreamPairCascadeCascade { Configurable depth{"depth", 5, "Number of events for mixing"}; Configurable binPolicy{"binPolicy", 0, "Binning binPolicy for mixing - 0: multiplicity, 1: multipliciy percentile, 2: both"}; } Mixing; - ColumnBinningPolicy colBinningMult{{Mixing.binVztx, Mixing.binMult}, true}; - ColumnBinningPolicy colBinningMultPercentile{{Mixing.binVztx, Mixing.binMultPercentile}, true}; - ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.binVztx, Mixing.binMult, Mixing.binMultPercentile}, true}; + ColumnBinningPolicy colBinningMult{{Mixing.binVztx, Mixing.binMult}}; + ColumnBinningPolicy colBinningMultPercentile{{Mixing.binVztx, Mixing.binMultPercentile}}; + ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.binVztx, Mixing.binMult, Mixing.binMultPercentile}}; FemtoDreamContainer sameEventCont; FemtoDreamContainer mixedEventCont; FemtoDreamPairCleaner pairCleaner; @@ -216,9 +216,9 @@ struct FemtoDreamPairCascadeCascade { void init(InitContext&) { // setup binnnig binPolicy for mixing - colBinningMult = {{Mixing.binVztx, Mixing.binMult}, true}; - colBinningMultPercentile = {{Mixing.binVztx, Mixing.binMultPercentile}, true}; - colBinningMultMultPercentile = {{Mixing.binVztx, Mixing.binMult, Mixing.binMultPercentile}, true}; + colBinningMult = {{Mixing.binVztx, Mixing.binMult}}; + colBinningMultPercentile = {{Mixing.binVztx, Mixing.binMultPercentile}}; + colBinningMultMultPercentile = {{Mixing.binVztx, Mixing.binMult, Mixing.binMultPercentile}}; eventHisto.init(®istry, Option.isMC); cascHistoPartOne.init(®istry, Binning.multTempFit, Option.dummy, Binning.pTCascade, Option.dummy, Option.dummy, Binning.tempFitVarCascade, Option.dummy, Option.dummy, Option.dummy, Option.dummy, Option.dummy, Option.dummy, Option.isMC, Cascade1.pdgCode); diff --git a/PWGCF/FemtoUniverse/Core/FemtoUniverseCascadeSelection.h b/PWGCF/FemtoUniverse/Core/FemtoUniverseCascadeSelection.h index 43d3e2cf54b..920cfbdd2b2 100644 --- a/PWGCF/FemtoUniverse/Core/FemtoUniverseCascadeSelection.h +++ b/PWGCF/FemtoUniverse/Core/FemtoUniverseCascadeSelection.h @@ -86,7 +86,7 @@ class FemtoUniverseCascadeSelection public: FemtoUniverseCascadeSelection() - : nPtCascadeMinSel(0), nPtCascadeMaxSel(0), nEtaCascadeMaxSel(0), nDCAV0DaughMax(0), nCPAV0Min(0), nTranRadV0Min(0), nTranRadV0Max(0), nV0DecVtxMax(0), nDCACascadeDaughMax(0), nCPACascadeMin(0), nTranRadCascadeMin(0), nTranRadCascadeMax(0), nDecVtxMax(0), nDCAPosToPV(0), nDCANegToPV(0), nDCABachToPV(0), nDCAV0ToPV(0), pTCascadeMin(9999999.), pTCascadeMax(-9999999.), etaCascadeMax(-9999999.), fDCAV0DaughMax(-9999999.), fCPAV0Min(9999999.), fTranRadV0Min(9999999.), fTranRadV0Max(-9999999.), fV0DecVtxMax(-9999999.), fDCACascadeDaughMax(-9999999.), fCPACascadeMin(9999999.), fTranRadCascadeMin(9999999.), fTranRadCascadeMax(-9999999.), fDecVtxMax(-9999999.), fDCAPosToPV(9999999.), fDCANegToPV(9999999.), fDCABachToPV(9999999.), fDCAV0ToPV(9999999.), fV0InvMassLowLimit(1.05), fV0InvMassUpLimit(1.3), fInvMassLowLimitXi(1.25), fInvMassUpLimitXi(1.4), fInvMassLowLimitOmega(1.6), fInvMassUpLimitOmega(1.8) /*, fRejectCompetingMass(false), fInvMassCompetingLowLimit(1.5), fInvMassCompetingUpLimit(2.0), nSigmaPIDOffsetTPC(0.)*/ + : nPtCascadeMinSel(0), nPtCascadeMaxSel(0), nEtaCascadeMaxSel(0), nDCAV0DaughMax(0), nCPAV0Min(0), nTranRadV0Min(0), nTranRadV0Max(0), nV0DecVtxMax(0), nDCACascadeDaughMax(0), nCPACascadeMin(0), nTranRadCascadeMin(0), nTranRadCascadeMax(0), nDecVtxMax(0), nDCAPosToPV(0), nDCANegToPV(0), nDCABachToPV(0), nDCAV0ToPV(0), pTCascadeMin(9999999.), pTCascadeMax(-9999999.), etaCascadeMax(-9999999.), fDCAV0DaughMax(-9999999.), fCPAV0Min(9999999.), fTranRadV0Min(9999999.), fTranRadV0Max(-9999999.), fV0DecVtxMax(-9999999.), fDCACascadeDaughMax(-9999999.), fCPACascadeMin(9999999.), fTranRadCascadeMin(9999999.), fTranRadCascadeMax(-9999999.), fDecVtxMax(-9999999.), fDCAPosToPV(9999999.), fDCANegToPV(9999999.), fDCABachToPV(9999999.), fDCAV0ToPV(9999999.), fV0InvMassLowLimit(1.05), fV0InvMassUpLimit(1.3), fInvMassLowLimitXi(1.25), fInvMassUpLimitXi(1.4), fInvMassLowLimitOmega(1.6), fInvMassUpLimitOmega(1.8), fRejectCompetingMass(false) /*, fInvMassCompetingLowLimit(1.5), fInvMassCompetingUpLimit(2.0), nSigmaPIDOffsetTPC(0.)*/ { } @@ -176,6 +176,13 @@ class FemtoUniverseCascadeSelection fInvMassCompetingUpLimit = upLimit; }*/ + /// Set whether to reject competing mass for Omega + /// \param reject Boolean flag + void setRejectCompetingMassOmega(bool reject) + { + fRejectCompetingMass = reject; + } + private: int nPtCascadeMinSel; int nPtCascadeMaxSel; @@ -227,6 +234,8 @@ class FemtoUniverseCascadeSelection // float nSigmaPIDOffsetTPC; + bool fRejectCompetingMass; + FemtoUniverseTrackSelection posDaughTrack; FemtoUniverseTrackSelection negDaughTrack; FemtoUniverseTrackSelection bachTrackSel; @@ -437,11 +446,15 @@ bool FemtoUniverseCascadeSelection::isSelectedMinimal(Col const& col, Casc const const float invMassXi = cascade.mXi(); const float invMassOmega = cascade.mOmega(); + // Check individual mass window hypotheses + const bool isXi = (invMassXi >= fInvMassLowLimitXi && invMassXi <= fInvMassUpLimitXi); + const bool isOmega = (invMassOmega >= fInvMassLowLimitOmega && invMassOmega <= fInvMassUpLimitOmega); + const bool passOmega = isOmega && (!fRejectCompetingMass || !isXi); + if (invMassLambda < fV0InvMassLowLimit || invMassLambda > fV0InvMassUpLimit) { return false; } - // Accepts the cascade candidates as either Xi or Omega but not both - if ((invMassXi < fInvMassLowLimitXi || invMassXi > fInvMassUpLimitXi) == (invMassOmega < fInvMassLowLimitOmega || invMassOmega > fInvMassUpLimitOmega)) { + if (!isXi && !passOmega) { return false; } /*if (fRejectCompetingMass) { diff --git a/PWGCF/FemtoUniverse/TableProducer/femtoUniverseProducerTask.cxx b/PWGCF/FemtoUniverse/TableProducer/femtoUniverseProducerTask.cxx index d65918ea5f0..880003d8e81 100644 --- a/PWGCF/FemtoUniverse/TableProducer/femtoUniverseProducerTask.cxx +++ b/PWGCF/FemtoUniverse/TableProducer/femtoUniverseProducerTask.cxx @@ -324,6 +324,7 @@ struct FemtoUniverseProducerTask { Configurable confXiInvMassUpLimit{"confXiInvMassUpLimit", 1.40, "Upper limit of the Xi invariant mass"}; Configurable confOmegaInvMassLowLimit{"confOmegaInvMassLowLimit", 1.60, "Lower limit of the Omega invariant mass"}; Configurable confOmegaInvMassUpLimit{"confOmegaInvMassUpLimit", 1.80, "Upper limit of the Omega invariant mass"}; + Configurable confRejectCompetingMassOmega{"confRejectCompetingMassOmega", false, "Switch to reject competing mass for Omega"}; } ConfCascadeSelection; // PHI @@ -355,6 +356,7 @@ struct FemtoUniverseProducerTask { Configurable confPhiKaonNsigmaTOFfrom055to15{"confPhiKaonNsigmaTOFfrom055to15", 3.0, "Reject if Kaons in 0.55-1.5 are have TOF n sigma above this value."}; Configurable confPhiKaonNsigmaTPCfrom15{"confPhiKaonNsigmaTPCfrom15", 3.0, "Reject if Kaons above 1.5 are have TPC n sigma above this value."}; Configurable confPhiKaonNsigmaTOFfrom15{"confPhiKaonNsigmaTOFfrom15", 3.0, "Reject if Kaons above 1.5 are have TOF n sigma above this value."}; + Configurable confDoLikeSign{"confDoLikeSign", false, "Enable like-sign (fake Phi) background calculation"}; } ConfPhiSelection; // PDG codes for fillMCParticle function @@ -382,7 +384,6 @@ struct FemtoUniverseProducerTask { Configurable confNsigmaCombinedParticle{"confNsigmaCombinedParticle", 3.0, "TPC and TOF Sigma (combined) for particle (track) momentum > Confmom"}; } ConfPIDBitmask; - HfHelper hfHelper; bool isKaonNSigma(float mom, float nsigmaTPCK, float nsigmaTOFK) { if (mom < ConfPhiSelection.confMomKaon03) { // 0.0-0.3 @@ -692,15 +693,15 @@ struct FemtoUniverseProducerTask { cascadeCuts.setChildCuts(femto_universe_cascade_selection::kBachTrack, ConfCascadeSelection.confCascChildTPCnClsMin, femto_universe_track_selection::kTPCnClsMin, femto_universe_selection::kLowerLimit); cascadeCuts.setChildCuts(femto_universe_cascade_selection::kBachTrack, ConfCascadeSelection.confCascChildPIDnSigmaMax, femto_universe_track_selection::kPIDnSigmaMax, femto_universe_selection::kAbsUpperLimit); - // TODO cascadeCuts.setChildPIDSpecies(femto_universe_cascade_selection::kPosTrack, ConfV0Selection.confChildPIDspecies); cascadeCuts.setChildPIDSpecies(femto_universe_cascade_selection::kNegTrack, ConfV0Selection.confChildPIDspecies); cascadeCuts.setChildPIDSpecies(femto_universe_cascade_selection::kBachTrack, ConfCascadeSelection.confCascChildPIDspecies); // check if works correctly for bachelor track cascadeCuts.init(&cascadeQaRegistry); - // invmass cuts + // invmass cuts & competing mass rejection cascadeCuts.setInvMassLimits(ConfCascadeSelection.confXiInvMassLowLimit, ConfCascadeSelection.confOmegaInvMassLowLimit, ConfCascadeSelection.confXiInvMassUpLimit, ConfCascadeSelection.confOmegaInvMassUpLimit); + cascadeCuts.setRejectCompetingMassOmega(ConfCascadeSelection.confRejectCompetingMassOmega); } if (ConfGeneral.confIsActivatePhi) { @@ -829,17 +830,17 @@ struct FemtoUniverseProducerTask { -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., - hfHelper.yD0(particle), // getter transRadius - particle.mlProbD0()[0], // getter decayVtxX - particle.mlProbD0()[1], // getter decayVtxY - particle.mlProbD0()[2], // getter decayVtxZ - -999.); // Additional info for D0/D0bar + HfHelper::yD0(particle), // getter transRadius + particle.mlProbD0()[0], // getter decayVtxX + particle.mlProbD0()[1], // getter decayVtxY + particle.mlProbD0()[2], // getter decayVtxZ + -999.); // Additional info for D0/D0bar } else if constexpr (isD0barML) { outputDebugParts(-999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., - hfHelper.yD0(particle), // getter transRadius + HfHelper::yD0(particle), // getter transRadius particle.mlProbD0bar()[0], // getter decayVtxX particle.mlProbD0bar()[1], // getter decayVtxY particle.mlProbD0bar()[2], // getter decayVtxZ @@ -869,17 +870,17 @@ struct FemtoUniverseProducerTask { -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., - hfHelper.yD0(particle), // getter transRadius - particle.mlProbD0()[0], // getter decayVtxX - particle.mlProbD0()[1], // getter decayVtxY - particle.mlProbD0()[2], // getter decayVtxZ - -999.); // Additional info for D0/D0bar + HfHelper::yD0(particle), // getter transRadius + particle.mlProbD0()[0], // getter decayVtxX + particle.mlProbD0()[1], // getter decayVtxY + particle.mlProbD0()[2], // getter decayVtxZ + -999.); // Additional info for D0/D0bar } else if constexpr (isD0barML) { outputDebugParts(particle.flagMcMatchRec(), -999., -999., -999., -999., -999., -999., -999., -999., originMcReco, -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., - hfHelper.yD0(particle), // getter transRadius + HfHelper::yD0(particle), // getter transRadius particle.mlProbD0bar()[0], // getter decayVtxX particle.mlProbD0bar()[1], // getter decayVtxY particle.mlProbD0bar()[2], // getter decayVtxZ @@ -1719,7 +1720,7 @@ struct FemtoUniverseProducerTask { continue; } - if (ConfD0Selection.useYCutD0Cand && std::abs(hfHelper.yD0(hfCand)) > ConfD0Selection.yD0CandMax) { + if (ConfD0Selection.useYCutD0Cand && std::abs(HfHelper::yD0(hfCand)) > ConfD0Selection.yD0CandMax) { continue; } @@ -1737,18 +1738,18 @@ struct FemtoUniverseProducerTask { auto negtrack = hfCand.template prong1_as(); if (hfCand.isSelD0() == 1 && hfCand.isSelD0bar() == 0) { - invMassD0 = hfHelper.invMassD0ToPiK(hfCand); - invMassD0bar = -hfHelper.invMassD0barToKPi(hfCand); + invMassD0 = HfHelper::invMassD0ToPiK(hfCand); + invMassD0bar = -HfHelper::invMassD0barToKPi(hfCand); isD0D0bar = true; daughFlag = 1; } else if (hfCand.isSelD0() == 0 && hfCand.isSelD0bar() == 1) { - invMassD0 = -hfHelper.invMassD0ToPiK(hfCand); - invMassD0bar = hfHelper.invMassD0barToKPi(hfCand); + invMassD0 = -HfHelper::invMassD0ToPiK(hfCand); + invMassD0bar = HfHelper::invMassD0barToKPi(hfCand); isD0D0bar = true; daughFlag = -1; } else if (hfCand.isSelD0() == 1 && hfCand.isSelD0bar() == 1) { - invMassD0 = hfHelper.invMassD0ToPiK(hfCand); - invMassD0bar = hfHelper.invMassD0barToKPi(hfCand); + invMassD0 = HfHelper::invMassD0ToPiK(hfCand); + invMassD0bar = HfHelper::invMassD0barToKPi(hfCand); if (ConfD0Selection.storeD0D0barDoubleMassHypo) { isD0D0bar = true; daughFlag = 0; @@ -1842,7 +1843,7 @@ struct FemtoUniverseProducerTask { continue; } - if (ConfD0Selection.useYCutD0Cand && std::abs(hfHelper.yD0(hfCand)) > ConfD0Selection.yD0CandMax) { + if (ConfD0Selection.useYCutD0Cand && std::abs(HfHelper::yD0(hfCand)) > ConfD0Selection.yD0CandMax) { continue; } @@ -1859,18 +1860,18 @@ struct FemtoUniverseProducerTask { auto negtrack = hfCand.template prong1_as(); if (hfCand.isSelD0() == 1 && hfCand.isSelD0bar() == 0) { - invMassD0 = hfHelper.invMassD0ToPiK(hfCand); - invMassD0bar = -hfHelper.invMassD0barToKPi(hfCand); + invMassD0 = HfHelper::invMassD0ToPiK(hfCand); + invMassD0bar = -HfHelper::invMassD0barToKPi(hfCand); isD0D0bar = true; daughFlag = 1; } else if (hfCand.isSelD0() == 0 && hfCand.isSelD0bar() == 1) { - invMassD0 = -hfHelper.invMassD0ToPiK(hfCand); - invMassD0bar = hfHelper.invMassD0barToKPi(hfCand); + invMassD0 = -HfHelper::invMassD0ToPiK(hfCand); + invMassD0bar = HfHelper::invMassD0barToKPi(hfCand); isD0D0bar = true; daughFlag = -1; } else if (hfCand.isSelD0() == 1 && hfCand.isSelD0bar() == 1) { - invMassD0 = hfHelper.invMassD0ToPiK(hfCand); - invMassD0bar = hfHelper.invMassD0barToKPi(hfCand); + invMassD0 = HfHelper::invMassD0ToPiK(hfCand); + invMassD0bar = HfHelper::invMassD0barToKPi(hfCand); if (ConfD0Selection.storeD0D0barDoubleMassHypo) { isD0D0bar = true; daughFlag = 0; @@ -1974,7 +1975,7 @@ struct FemtoUniverseProducerTask { } } - if (ConfD0Selection.useYCutD0Cand && std::abs(hfHelper.yD0(hfCand)) > ConfD0Selection.yD0CandMax) { + if (ConfD0Selection.useYCutD0Cand && std::abs(HfHelper::yD0(hfCand)) > ConfD0Selection.yD0CandMax) { continue; } @@ -2000,18 +2001,18 @@ struct FemtoUniverseProducerTask { childIDs[1] = 0; if (hfCand.isSelD0() == 1 && hfCand.isSelD0bar() == 0) { - invMassD0 = hfHelper.invMassD0ToPiK(hfCand); - invMassD0bar = -hfHelper.invMassD0barToKPi(hfCand); + invMassD0 = HfHelper::invMassD0ToPiK(hfCand); + invMassD0bar = -HfHelper::invMassD0barToKPi(hfCand); isD0D0bar = true; daughFlag = 1; } else if (hfCand.isSelD0() == 0 && hfCand.isSelD0bar() == 1) { - invMassD0 = -hfHelper.invMassD0ToPiK(hfCand); - invMassD0bar = hfHelper.invMassD0barToKPi(hfCand); + invMassD0 = -HfHelper::invMassD0ToPiK(hfCand); + invMassD0bar = HfHelper::invMassD0barToKPi(hfCand); isD0D0bar = true; daughFlag = -1; } else if (hfCand.isSelD0() == 1 && hfCand.isSelD0bar() == 1) { - invMassD0 = hfHelper.invMassD0ToPiK(hfCand); - invMassD0bar = hfHelper.invMassD0barToKPi(hfCand); + invMassD0 = HfHelper::invMassD0ToPiK(hfCand); + invMassD0bar = HfHelper::invMassD0barToKPi(hfCand); if (ConfD0Selection.storeD0D0barDoubleMassHypo) { isD0D0bar = true; daughFlag = 0; @@ -2230,6 +2231,156 @@ struct FemtoUniverseProducerTask { } } + template + void fillLikeSignPhi(CollisionType const& col, TrackType const& tracks) + { + std::vector childIDs = {0, 0}; + std::vector tmpIDtrack; + + // Loop over all track combinations using FullIndexPolicy + for (const auto& [p1, p2] : combinations(soa::CombinationsFullIndexPolicy(tracks, tracks))) { + + // 1. Check minimal track quality cuts for both tracks + if (!trackCuts.isSelectedMinimal(p1) || !trackCuts.isSelectedMinimal(p2)) { + continue; + } + + if (p1.sign() != p2.sign()) { // K+K- pairs are already handled in fillPhi, so we skip them here, only K+K+ and K-K- + continue; + } + + // avoid double counting and self-pairing when using FullIndexPolicy + if (p1.globalIndex() >= p2.globalIndex()) { + continue; + } + + // cuts for daughters + if (ConfPhiSelection.confPhiDoLFPID4Kaons) { + if (!p1.isGlobalTrackWoDCA() || !p2.isGlobalTrackWoDCA() || !p1.isPVContributor() || !p2.isPVContributor()) { + continue; + } + if (!(isKaonNSigmaLF(p1.pt(), trackCuts.getNsigmaTPC(p1, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p1, o2::track::PID::Kaon), p1.hasTOF()))) { + continue; + } + if (!(isKaonNSigmaLF(p2.pt(), trackCuts.getNsigmaTPC(p2, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p2, o2::track::PID::Kaon), p2.hasTOF()))) { + continue; + } + } else { + if (!(isKaonNSigma(p1.pt(), trackCuts.getNsigmaTPC(p1, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p1, o2::track::PID::Kaon)))) { + continue; + } + if (!(isKaonNSigma(p2.pt(), trackCuts.getNsigmaTPC(p2, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p2, o2::track::PID::Kaon)))) { + continue; + } + } + + // Reject protons and pions contamination + if (isKaonRejected(p1.p(), trackCuts.getNsigmaTPC(p1, o2::track::PID::Proton), trackCuts.getNsigmaTOF(p1, o2::track::PID::Proton), trackCuts.getNsigmaTPC(p1, o2::track::PID::Pion), trackCuts.getNsigmaTOF(p1, o2::track::PID::Pion))) { + continue; + } + if (isKaonRejected(p2.p(), trackCuts.getNsigmaTPC(p2, o2::track::PID::Proton), trackCuts.getNsigmaTOF(p2, o2::track::PID::Proton), trackCuts.getNsigmaTPC(p2, o2::track::PID::Pion), trackCuts.getNsigmaTOF(p2, o2::track::PID::Pion))) { + continue; + } + + const auto mMassOne = o2::constants::physics::MassKPlus; + const auto mMassTwo = o2::constants::physics::MassKPlus; + + ROOT::Math::LorentzVector> part1Vec(p1.pt(), p1.eta(), p1.phi(), mMassOne); + ROOT::Math::LorentzVector> part2Vec(p2.pt(), p2.eta(), p2.phi(), mMassTwo); + + ROOT::Math::LorentzVector> sumVec(part1Vec); + sumVec += part2Vec; + + // kinematic cuts for phi candidate + float phiEta = sumVec.Eta(); + if (std::abs(phiEta) > ConfPhiSelection.confPhiEtaHighLimit.value) { + continue; + } + + float phiPt = sumVec.Pt(); + if ((phiPt < ConfPhiSelection.confPhiPtLowLimit.value) || (phiPt > ConfPhiSelection.confPhiPtHighLimit.value)) { + continue; + } + + float phiPhi = RecoDecay::constrainAngle(sumVec.Phi(), 0); + float phiM = sumVec.M(); + + if ((phiM < ConfPhiSelection.confPhiInvMassLowLimit) || (phiM > ConfPhiSelection.confPhiInvMassUpLimit)) { + continue; + } + + phiCuts.fillQA(col, p1, p1, p2, 321, 321); + + int postrackID = p1.globalIndex(); + int rowInPrimaryTrackTablePos = -1; + rowInPrimaryTrackTablePos = getRowDaughters(postrackID, tmpIDtrack); + childIDs[0] = rowInPrimaryTrackTablePos; + childIDs[1] = 0; + + // first daughter track + outputParts(outputCollision.lastIndex(), p1.pt(), + p1.eta(), p1.phi(), + aod::femtouniverseparticle::ParticleType::kPhiChild, + -999, + -999, + p1.dcaXY(), + childIDs, + 0, + p1.sign()); + const int rowOfPosTrack = outputParts.lastIndex(); + if constexpr (isMC) { + fillMCParticle(p1, o2::aod::femtouniverseparticle::ParticleType::kPhiChild); + } + + int negtrackID = p2.globalIndex(); + int rowInPrimaryTrackTableNeg = -1; + rowInPrimaryTrackTableNeg = getRowDaughters(negtrackID, tmpIDtrack); + childIDs[0] = 0; + childIDs[1] = rowInPrimaryTrackTableNeg; + + // second daughter track + outputParts(outputCollision.lastIndex(), + p2.pt(), + p2.eta(), + p2.phi(), + aod::femtouniverseparticle::ParticleType::kPhiChild, + -999, + -999, + p2.dcaXY(), + childIDs, + 0, + p2.sign()); + const int rowOfNegTrack = outputParts.lastIndex(); + if constexpr (isMC) { + fillMCParticle(p2, o2::aod::femtouniverseparticle::ParticleType::kPhiChild); + } + + std::vector indexChildID = {rowOfPosTrack, rowOfNegTrack}; + + // Define like-sign flag: +1 for (K+K+), -1 for (K-K-) + int likeSignFlag = p1.sign(); + + // Save the Fake Phi candidate from K+K+ or K-K- combination + outputParts(outputCollision.lastIndex(), + phiPt, + phiEta, + phiPhi, + aod::femtouniverseparticle::ParticleType::kPhi, + likeSignFlag, // Flag: +1 for K+K+, -1 for K-K-, before it was empty slot for cutcontainer + 0, + phiM, // Invariant mass of the fake Phi candidate + indexChildID, + phiM, + 0); + + if (confIsDebug) { + fillDebugParticle(p1); // QA for first daughter + fillDebugParticle(p2); // QA for second daughter + fillDebugParticle(p1); // QA for fake Phi mother + } + } + } + template void fillParticles(TrackType const& tracks, std::optional>> recoMcIds = std::nullopt) { @@ -2453,6 +2604,9 @@ struct FemtoUniverseProducerTask { } if (ConfGeneral.confIsActivatePhi) { fillPhi(col, tracks); + if (ConfPhiSelection.confDoLikeSign) { + fillLikeSignPhi(col, tracks); + } } } return colcheck; @@ -2629,6 +2783,9 @@ struct FemtoUniverseProducerTask { if (colcheck) { fillTracks(tracks); fillPhi(col, tracks); + if (ConfPhiSelection.confDoLikeSign) { + fillLikeSignPhi(col, tracks); + } } } PROCESS_SWITCH(FemtoUniverseProducerTask, processTrackPhiData, diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackCascadeExtended.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackCascadeExtended.cxx index bd7362e67b0..b9a740f4874 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackCascadeExtended.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackCascadeExtended.cxx @@ -1033,8 +1033,8 @@ struct femtoUniversePairTaskTrackCascadeExtended { // NOLINT(cppcoreguidelines-p template void doMixedEvent(const FilteredFDCollisions& cols, const TableType& parts, PartitionType& partsOne, PartitionType& partsTwo, [[maybe_unused]] const MCParticles& mcParts = nullptr) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; auto mixedCollProcessFunc = [&](auto& collision1, auto& collision2) -> void { const int multCol = confUseCent ? static_cast(collision1.multV0M()) : static_cast(collision1.multNtr()); @@ -1132,7 +1132,7 @@ struct femtoUniversePairTaskTrackCascadeExtended { // NOLINT(cppcoreguidelines-p template void doMixedEventCasc(const FilteredFDCollisions& cols, const TableType& parts, PartitionType& partsTwo) { - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; for (const auto& [collision1, collision2] : soa::selfCombinations(colBinning, 5, -1, cols, cols)) { const int multCol = confUseCent ? static_cast(collision1.multV0M()) : static_cast(collision1.multNtr()); @@ -1229,7 +1229,7 @@ struct femtoUniversePairTaskTrackCascadeExtended { // NOLINT(cppcoreguidelines-p template void doMixedEventTrack(const FilteredFDCollisions& cols, const TableType& parts, PartitionType& partsOne, PartitionType& partsTwo) { - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; for (const auto& [collision1, collision2] : soa::selfCombinations(colBinning, 5, -1, cols, cols)) { const int multCol = confUseCent ? static_cast(collision1.multV0M()) : static_cast(collision1.multNtr()); @@ -1379,7 +1379,7 @@ struct femtoUniversePairTaskTrackCascadeExtended { // NOLINT(cppcoreguidelines-p // MC truth for track - cascade void processMixedEventMCgen(const FilteredFDCollisions& cols, [[maybe_unused]] const aod::FDParticles& parts) { - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; for (const auto& [collision1, collision2] : soa::selfCombinations(colBinning, 5, -1, cols, cols)) { const int multCol = confUseCent ? static_cast(collision1.multV0M()) : static_cast(collision1.multNtr()); @@ -1408,7 +1408,7 @@ struct femtoUniversePairTaskTrackCascadeExtended { // NOLINT(cppcoreguidelines-p // MC truth for cascade - cascade void processMixedEventCascMCgen(const FilteredFDCollisions& cols, [[maybe_unused]] const aod::FDParticles& parts) { - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; for (const auto& [collision1, collision2] : soa::selfCombinations(colBinning, 5, -1, cols, cols)) { const int multCol = confUseCent ? static_cast(collision1.multV0M()) : static_cast(collision1.multNtr()); diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackD0.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackD0.cxx index 0e54930d4df..a1739dbe1e7 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackD0.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackD0.cxx @@ -25,8 +25,6 @@ #include "PWGHF/Core/DecayChannels.h" #include "PWGHF/Core/SelectorCuts.h" -#include "Common/Core/RecoDecay.h" - #include #include #include @@ -60,6 +58,8 @@ using namespace o2; using namespace o2::analysis; using namespace o2::analysis::femto_universe; using namespace o2::analysis::femto_universe::efficiency; +using namespace o2::constants::physics; +using namespace o2::constants::math; using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::soa; @@ -70,25 +70,6 @@ const int8_t genPromptD0 = 1; const int8_t genNonPromptD0 = 2; } // namespace -/// Returns deltaPhi value within the range [-pi/2, 3/2*pi] -/// -double getDeltaPhi(double phiD, double phiDbar) -{ - return RecoDecay::constrainAngle(phiDbar - phiD, -o2::constants::math::PIHalf); -} - -/// Returns deltaPhi value within the range [0, pi] -/// -double wrapDeltaPhi0PI(double phiD, double phiDbar) -{ - double deltaPhi = 0.0; - deltaPhi = RecoDecay::constrainAngle(phiDbar - phiD, 0.0); - if (deltaPhi > o2::constants::math::TwoPI) { - deltaPhi = o2::constants::math::TwoPI - deltaPhi; - } - return deltaPhi; -} - struct FemtoUniversePairTaskTrackD0 { Service pdgMC{}; @@ -99,13 +80,13 @@ struct FemtoUniversePairTaskTrackD0 { Preslice perCol = aod::femtouniverseparticle::fdCollisionId; Preslice perColFullSigma = aod::femtouniverseparticle::fdCollisionId; - using FemtoMcRecoParticles = soa::Join; - Preslice perColMc = aod::femtouniverseparticle::fdCollisionId; + using FemtoMcParticles = soa::Join; + Preslice perColMc = aod::femtouniverseparticle::fdCollisionId; /// Table for both particles struct : o2::framework::ConfigurableGroup { Configurable confIsMC{"confIsMC", false, "Enable additional Histogramms in the case of a MonteCarlo Run"}; - Configurable> confTrkPIDnSigmaMax{"confTrkPIDnSigmaMax", std::vector{4.f, 3.f, 2.f}, "This configurable needs to be the same as the one used in the producer task"}; + Configurable confIsDebug{"confIsDebug", true, "Enable filling additional histograms for track and D0"}; Configurable confUse3D{"confUse3D", false, "Enable three dimensional histogramms (to be used only for analysis with high statistics): k* vs mT vs multiplicity"}; Configurable confPhiBins{"confPhiBins", 29, "Number of phi bins in deta dphi"}; Configurable confEtaBins{"confEtaBins", 29, "Number of eta bins in deta dphi"}; @@ -119,7 +100,6 @@ struct FemtoUniversePairTaskTrackD0 { struct : o2::framework::ConfigurableGroup { Configurable confIsSame{"confIsSame", false, "Pairs of the same particle"}; Configurable confPDGCodeTrack{"confPDGCodeTrack", 2212, "Particle 2 - PDG code"}; - Configurable confPIDTrack{"confPIDTrack", 2, "Particle 2 - Read from cutCulator"}; // we also need the possibility to specify whether the bit is true/false ->std>>vector> Configurable confTrackSign{"confTrackSign", 1, "Track sign"}; Configurable confIsTrackIdentified{"confIsTrackIdentified", true, "Enable PID for the track"}; Configurable confTrackLowPtCut{"confTrackLowPtCut", 0.5, "Low pT cut of the track"}; @@ -181,47 +161,43 @@ struct FemtoUniversePairTaskTrackD0 { Configurable> binsPtFive{"binsPtFive", std::vector{hf_cuts_d0_to_pi_k::vecBinsPt}, "pT bin limits"}; Configurable confChooseD0trackCorr{"confChooseD0trackCorr", 0, "If 0 correlations with D0s, if 1 with D0bars"}; struct : o2::framework::ConfigurableGroup { + Configurable fillTrackQA{"fillTrackQA", false, "Fill additional histograms for track in PurityQA process"}; // Correlated background for D0/D0bar candidates Configurable fillCorrBkgs{"fillCorrBkgs", false, "Fill histograms with correlated background candidates"}; // Configurable to enable filling histograms with BDT scores values - Configurable fillBDTvsPt{"fillBDTvsPt", true, "Fill BDT vs pT histograms for D0/D0bar candidates"}; Configurable fillBDTvsPtVsMass{"fillBDTvsPtVsMass", false, "Fill BDT vs pT vs mass histograms for D0/D0bar candidates"}; } ConfFill; // Efficiency Configurable doEfficiencyCorr{"doEfficiencyCorr", false, "Apply efficiency corrections"}; /// Partitions for particle 1 - Partition partsTrack = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::sign == int8_t(ConfTrack.confTrackSign)) && (aod::femtouniverseparticle::pt > ConfTrack.confTrackLowPtCut) && (aod::femtouniverseparticle::pt < ConfTrack.confTrackHighPtCut) && (nabs(aod::femtouniverseparticle::eta) < ConfTrack.confTrackEtaMax); - Partition partsTrackFullSigma = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::sign == int8_t(ConfTrack.confTrackSign)) && (aod::femtouniverseparticle::pt > ConfTrack.confTrackLowPtCut) && (aod::femtouniverseparticle::pt < ConfTrack.confTrackHighPtCut) && (nabs(aod::femtouniverseparticle::eta) < ConfTrack.confTrackEtaMax); - Partition partsTrackMCReco = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::sign == int8_t(ConfTrack.confTrackSign)) && (aod::femtouniverseparticle::pt > ConfTrack.confTrackLowPtCut) && (aod::femtouniverseparticle::pt < ConfTrack.confTrackHighPtCut) && (nabs(aod::femtouniverseparticle::eta) < ConfTrack.confTrackEtaMax); - Partition partsTrackMCTruth = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) && (aod::femtouniverseparticle::pidCut == static_cast(ConfTrack.confPDGCodeTrack)) && (aod::femtouniverseparticle::pt > ConfTrack.confTrackLowPtCut) && (aod::femtouniverseparticle::pt < ConfTrack.confTrackHighPtCut) && (nabs(aod::femtouniverseparticle::eta) < ConfTrack.confTrackEtaMax); + Partition partsTrack = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::sign == static_cast(ConfTrack.confTrackSign)) && (aod::femtouniverseparticle::pt > ConfTrack.confTrackLowPtCut) && (aod::femtouniverseparticle::pt < ConfTrack.confTrackHighPtCut) && (nabs(aod::femtouniverseparticle::eta) < ConfTrack.confTrackEtaMax); + Partition partsTrackFullSigma = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::sign == static_cast(ConfTrack.confTrackSign)) && (aod::femtouniverseparticle::pt > ConfTrack.confTrackLowPtCut) && (aod::femtouniverseparticle::pt < ConfTrack.confTrackHighPtCut) && (nabs(aod::femtouniverseparticle::eta) < ConfTrack.confTrackEtaMax); + Partition partsTrackMCReco = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::sign == static_cast(ConfTrack.confTrackSign)) && (aod::femtouniverseparticle::pt > ConfTrack.confTrackLowPtCut) && (aod::femtouniverseparticle::pt < ConfTrack.confTrackHighPtCut) && (nabs(aod::femtouniverseparticle::eta) < ConfTrack.confTrackEtaMax); + Partition partsTrackMCTruth = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) && (aod::femtouniverseparticle::tempFitVar == static_cast(ConfTrack.confPDGCodeTrack)) && (aod::femtouniverseparticle::pt > ConfTrack.confTrackLowPtCut) && (aod::femtouniverseparticle::pt < ConfTrack.confTrackHighPtCut) && (nabs(aod::femtouniverseparticle::eta) < ConfTrack.confTrackEtaMax); /// Partitions for particle 2 /// Partition with all D0/D0bar mesons (which pass one and double mass hypothesis) - Partition partsAllDmesons = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mLambda > 0.0f) || (aod::femtouniverseparticle::mAntiLambda > 0.0f)) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); + Partition partsAllDmesons = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mLambda > 0.0f) || (aod::femtouniverseparticle::mAntiLambda > 0.0f)) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); /// Partition with D0 mesons only (one and double mass hypothesis) - Partition partsAllD0s = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); - /// Partition with D0 mesons only (one mass hypothesis) - Partition partsD0s = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::mAntiLambda < 0.0f) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); + Partition partSigD0s = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); /// Partition with D0s selected from the side-band (SB) regions (candidates with double mass hypothesis included) - Partition partsD0sFromSB = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barLeftSB && aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barLeftSB) || (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barRightSB && aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barRightSB)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); + Partition partSidebandD0s = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barLeftSB && aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barLeftSB) || (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barRightSB && aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barRightSB)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); /// Partition with D0bar mesons only (one and double mass hypothesis) - Partition partsAllD0bars = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); - /// Partition with D0bar mesons only (one mass hypothesis) - Partition partsD0bars = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mLambda < 0.0f) && (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); + Partition partSigD0bars = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); /// Partition with D0bars selected from the side-band (SB) regions (candidates with double mass hypothesis included) - Partition partsD0barsFromSB = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barLeftSB && aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barLeftSB) || (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barRightSB && aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barRightSB)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); + Partition partSidebandD0bars = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barLeftSB && aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barLeftSB) || (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barRightSB && aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barRightSB)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); /// Partition for MC Reco prompt D0/D0bar mesons - Partition partsPromptD0MCReco = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::tpcNClsFound == ConfDmesons.confD0OriginFlag); - Partition partsPromptD0barMCReco = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::tpcNClsFound == ConfDmesons.confD0OriginFlag); + Partition partsPromptD0MCReco = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::tpcNClsFound == ConfDmesons.confD0OriginFlag); + Partition partsPromptD0barMCReco = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::tpcNClsFound == ConfDmesons.confD0OriginFlag); /// Partition for MC Reco prompt D0/D0bar mesons (sideband candidates) - Partition partsPromptD0MCRecoSB = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barLeftSB && aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barLeftSB) || (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barRightSB && aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barRightSB)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::tpcNClsFound == ConfDmesons.confD0OriginFlag); - Partition partsPromptD0barMCRecoSB = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barLeftSB && aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barLeftSB) || (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barRightSB && aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barRightSB)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::tpcNClsFound == ConfDmesons.confD0OriginFlag); + Partition partsPromptD0MCRecoSB = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barLeftSB && aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barLeftSB) || (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barRightSB && aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barRightSB)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::tpcNClsFound == ConfDmesons.confD0OriginFlag); + Partition partsPromptD0barMCRecoSB = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barLeftSB && aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barLeftSB) || (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barRightSB && aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barRightSB)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::tpcNClsFound == ConfDmesons.confD0OriginFlag); // MC Truth D0/D0bar candidates - Partition partsD0MCTruth = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) && (aod::femtouniverseparticle::pidCut == static_cast(ConfDmesons.confPDGCodeD0)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar); - Partition partsD0barMCTruth = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) && (aod::femtouniverseparticle::pidCut == static_cast(ConfDmesons.confPDGCodeD0bar)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar); + Partition partsD0MCTruth = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) && (aod::femtouniverseparticle::tempFitVar == static_cast(ConfDmesons.confPDGCodeD0)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar); + Partition partsD0barMCTruth = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) && (aod::femtouniverseparticle::tempFitVar == static_cast(ConfDmesons.confPDGCodeD0bar)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar); /// Partition for D0/D0bar daughters - Partition partsDmesonsChildren = aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0Child); + Partition partsDmesonsChildren = aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kD0Child); /// Histogramming for particle 1 FemtoUniverseParticleHisto trackHistoPartTrack; @@ -232,10 +208,6 @@ struct FemtoUniversePairTaskTrackD0 { /// Histogramming for Event FemtoUniverseEventHisto eventHisto; - /// The configurables need to be passed to an std::vector - int vPIDTrack = 0; - std::vector kNsigma; - /// particle part ConfigurableAxis confTempFitVarBins{"confTempFitVarBins", {300, -0.15, 0.15}, "binning of the TempFitVar in the pT vs. TempFitVar plot"}; ConfigurableAxis confTempFitVarInvMassBins{"confTempFitVarInvMassBins", {6000, 0.9, 4.0}, "binning of the TempFitVar in the pT vs. TempFitVar plot"}; @@ -244,12 +216,11 @@ struct FemtoUniversePairTaskTrackD0 { /// Correlation part ConfigurableAxis confMultBins{"confMultBins", {VARIABLE_WIDTH, 0.0f, 4.0f, 8.0f, 12.0f, 16.0f, 20.0f, 24.0f, 28.0f, 32.0f, 36.0f, 40.0f, 44.0f, 48.0f, 52.0f, 56.0f, 60.0f, 64.0f, 68.0f, 72.0f, 76.0f, 80.0f, 84.0f, 88.0f, 92.0f, 96.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; // \todo to be obtained from the hash task - // ConfigurableAxis confMultBins{"confMultBins", {VARIABLE_WIDTH, 0.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; ConfigurableAxis confVtxBins{"confVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; ConfigurableAxis confmTBins3D{"confmTBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; ConfigurableAxis confmultBins3D{"confmultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "multiplicity Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; ConfigurableAxis confkstarBins{"confkstarBins", {1500, 0., 6.}, "binning kstar"}; ConfigurableAxis confkTBins{"confkTBins", {150, 0., 9.}, "binning kT"}; @@ -308,9 +279,9 @@ struct FemtoUniversePairTaskTrackD0 { {{"hPtD0", "D^{0} cand.;#it{p}_{T} (GeV/#it{c});counts", {HistType::kTH1F, {confPtBins}}}, {"hPtD0bar", "#bar{D^{0}};#it{p}_{T} (GeV/#it{c});counts", {HistType::kTH1F, {confPtBins}}}, {"hPtD0D0bar", "#bar{D^{0}};#it{p}_{T} (GeV/#it{c});counts", {HistType::kTH1F, {confPtBins}}}, - {"hPhiD0D0bar", ";#varphi (rad);counts", {HistType::kTH1F, {{80, 0., o2::constants::math::TwoPI}}}}, - {"hPhiD0", ";#varphi (rad);counts", {HistType::kTH1F, {{80, 0., o2::constants::math::TwoPI}}}}, - {"hPhiD0bar", ";#varphi (rad);counts", {HistType::kTH1F, {{80, 0., o2::constants::math::TwoPI}}}}, + {"hPhiD0D0bar", ";#varphi (rad);counts", {HistType::kTH1F, {{80, 0., TwoPI}}}}, + {"hPhiD0", ";#varphi (rad);counts", {HistType::kTH1F, {{80, 0., TwoPI}}}}, + {"hPhiD0bar", ";#varphi (rad);counts", {HistType::kTH1F, {{80, 0., TwoPI}}}}, {"hEtaD0D0bar", ";#eta ;counts", {HistType::kTH1F, {{200, -1., 1.}}}}, {"hEtaD0", ";#eta ;counts", {HistType::kTH1F, {{200, -1., 1.}}}}, {"hEtaD0bar", ";#eta ;counts", {HistType::kTH1F, {{200, -1., 1.}}}}, @@ -412,39 +383,32 @@ struct FemtoUniversePairTaskTrackD0 { efficiencyCalculator.init(); eventHisto.init(&qaRegistry); - qaRegistry.add("QA_D0D0barSelection/hInvMassD0", ";#it{M}(K^{-}#pi^{+}) (GeV/#it{c}^{2});counts", kTH1F, {confInvMassBins}); - qaRegistry.add("QA_D0D0barSelection/hPtD0", "D^{0} cand.;#it{p}_{T} (GeV/#it{c});counts", kTH1F, {confPtBins}); - qaRegistry.add("QA_D0D0barSelection/hInvMassD0bar", ";#it{M}(K^{-}#pi^{+}) (GeV/#it{c}^{2});counts", kTH1F, {confInvMassBins}); - qaRegistry.add("QA_D0D0barSelection/hPtD0bar", "#bar{D^{0}} cand.;#it{p}_{T} (GeV/#it{c});counts", kTH1F, {confPtBins}); - qaRegistry.add("QA_D0D0barSelection_SB/hInvMassD0", ";#it{M}(K^{-}#pi^{+}) (GeV/#it{c}^{2});counts", kTH1F, {confInvMassBins}); - qaRegistry.add("QA_D0D0barSelection_SB/hPtD0", "D^{0} cand.;#it{p}_{T} (GeV/#it{c});counts", kTH1F, {confPtBins}); - qaRegistry.add("D0_pos_daugh/nSigmaTPC", "; #it{p} (GeV/#it{c}); n#sigma_{TPC}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); qaRegistry.add("D0_pos_daugh/nSigmaTOF", "; #it{p} (GeV/#it{c}); n#sigma_{TOF}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); qaRegistry.add("D0_pos_daugh/pt", "; #it{p_T} (GeV/#it{c}); Counts", kTH1F, {{100, 0, 10}}); qaRegistry.add("D0_pos_daugh/eta", "; #it{eta}; Counts", kTH1F, {{200, -1.5, 1.5}}); - qaRegistry.add("D0_pos_daugh/phi", "; #it{varphi}; Counts", kTH1F, {{200, 0, o2::constants::math::TwoPI}}); + qaRegistry.add("D0_pos_daugh/phi", "; #it{varphi}; Counts", kTH1F, {{200, 0, TwoPI}}); qaRegistry.add("D0_pos_daugh/hDCAxy", "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {{100, 0, 10}, {500, -5, 5}}); qaRegistry.add("D0_neg_daugh/nSigmaTPC", "; #it{p} (GeV/#it{c}); n#sigma_{TPC}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); qaRegistry.add("D0_neg_daugh/nSigmaTOF", "; #it{p} (GeV/#it{c}); n#sigma_{TOF}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); qaRegistry.add("D0_neg_daugh/pt", "; #it{p_T} (GeV/#it{c}); Counts", kTH1F, {{100, 0, 10}}); qaRegistry.add("D0_neg_daugh/eta", "; #it{eta}; Counts", kTH1F, {{200, -1.5, 1.5}}); - qaRegistry.add("D0_neg_daugh/phi", "; #it{varphi}; Counts", kTH1F, {{200, 0, o2::constants::math::TwoPI}}); + qaRegistry.add("D0_neg_daugh/phi", "; #it{varphi}; Counts", kTH1F, {{200, 0, TwoPI}}); qaRegistry.add("D0_neg_daugh/hDCAxy", "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {{100, 0, 10}, {500, -5, 5}}); qaRegistry.add("D0bar_pos_daugh/nSigmaTPC", "; #it{p} (GeV/#it{c}); n#sigma_{TPC}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); qaRegistry.add("D0bar_pos_daugh/nSigmaTOF", "; #it{p} (GeV/#it{c}); n#sigma_{TOF}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); qaRegistry.add("D0bar_pos_daugh/pt", "; #it{p_T} (GeV/#it{c}); Counts", kTH1F, {{100, 0, 10}}); qaRegistry.add("D0bar_pos_daugh/eta", "; #it{eta}; Counts", kTH1F, {{200, -1.5, 1.5}}); - qaRegistry.add("D0bar_pos_daugh/phi", "; #it{varphi}; Counts", kTH1F, {{200, 0, o2::constants::math::TwoPI}}); + qaRegistry.add("D0bar_pos_daugh/phi", "; #it{varphi}; Counts", kTH1F, {{200, 0, TwoPI}}); qaRegistry.add("D0bar_pos_daugh/hDCAxy", "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {{100, 0, 10}, {500, -5, 5}}); qaRegistry.add("D0bar_neg_daugh/nSigmaTPC", "; #it{p} (GeV/#it{c}); n#sigma_{TPC}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); qaRegistry.add("D0bar_neg_daugh/nSigmaTOF", "; #it{p} (GeV/#it{c}); n#sigma_{TOF}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); qaRegistry.add("D0bar_neg_daugh/pt", "; #it{p_T} (GeV/#it{c}); Counts", kTH1F, {{100, 0, 10}}); qaRegistry.add("D0bar_neg_daugh/eta", "; #it{eta}; Counts", kTH1F, {{200, -1.5, 1.5}}); - qaRegistry.add("D0bar_neg_daugh/phi", "; #it{varphi}; Counts", kTH1F, {{200, 0, o2::constants::math::TwoPI}}); + qaRegistry.add("D0bar_neg_daugh/phi", "; #it{varphi}; Counts", kTH1F, {{200, 0, TwoPI}}); qaRegistry.add("D0bar_neg_daugh/hDCAxy", "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {{100, 0, 10}, {500, -5, 5}}); qaRegistry.add("Hadron/nSigmaTPCPr", "; #it{p} (GeV/#it{c}); n#sigma_{TPCPr}", kTH2F, {{100, 0, 10}, ConfBothTracks.confNSigmaTPCTOFAxis}); @@ -457,6 +421,20 @@ struct FemtoUniversePairTaskTrackD0 { qaRegistry.add("Hadron/nSigmaTOFKa", "; #it{p} (GeV/#it{c}); n#sigma_{TOFKa}", kTH2F, {{100, 0, 10}, ConfBothTracks.confNSigmaTPCTOFAxis}); qaRegistry.add("Hadron/nSigmaCombKa", "; #it{p} (GeV/#it{c}); n#sigma^{combined}_{Ka}", kTH2F, {{100, 0, 10}, ConfBothTracks.confNSigmaCombAxis}); + if (doprocessPurityQA && ConfFill.fillTrackQA) { + qaRegistry.add("TrackQA/hCharge", "; Charge; Entries", kTH1F, {{5, -2.5, 2.5}}); + qaRegistry.add("TrackQA/hTPCfindable", "; TPC findable clusters; Entries", kTH1F, {{163, -0.5, 162.5}}); + qaRegistry.add("TrackQA/hTPCfound", "; TPC found clusters; Entries", kTH1F, {{163, -0.5, 162.5}}); + qaRegistry.add("TrackQA/hTPCcrossedOverFindable", "; TPC ratio findable; Entries", kTH1F, {{150, 0., 1.5}}); + qaRegistry.add("TrackQA/hTPCcrossedRows", "; TPC crossed rows; Entries", kTH1F, {{163, -0.5, 162.5}}); + qaRegistry.add("TrackQA/hTPCfindableVsCrossed", ";TPC findable clusters ; TPC crossed rows;", kTH2F, {{163, -0.5, 162.5}, {163, -0.5, 162.5}}); + qaRegistry.add("TrackQA/hTPCshared", "; TPC shared clusters; Entries", kTH1F, {{163, -0.5, 162.5}}); + qaRegistry.add("TrackQA/hTPCfractionSharedCls", "; TPC fraction of shared clusters; Entries", kTH1F, {{100, 0.0, 1.0}}); + qaRegistry.add("TrackQA/hITSclusters", "; ITS clusters; Entries", kTH1F, {{10, -0.5, 9.5}}); + qaRegistry.add("TrackQA/hITSclustersIB", "; ITS clusters in IB; Entries", kTH1F, {{10, -0.5, 9.5}}); + qaRegistry.add("TrackQA/hDCAz", "; #it{p}_{T} (GeV/#it{c}); DCA_{z} (cm)", kTH2F, {{100, 0, 10}, {500, -5, 5}}); + qaRegistry.add("TrackQA/hTPCdEdX", "; #it{p} (GeV/#it{c}); TPC Signal", kTH2F, {{100, 0, 10}, {1000, 0, 1000}}); + } if (doprocessPurityQA && std::abs(ConfTrack.confPDGCodeTrack) == PDG_t::kProton) { qaRegistry.add("PurityQA/nSigmaFullTPCPr", "; #it{p} (GeV/#it{c}); n#sigma_{TPCPr}", kTH2F, {{100, 0, 10}, ConfBothTracks.confNSigmaTPCTOFAxis}); qaRegistry.add("PurityQA/nSigmaFullTOFPr", "; #it{p} (GeV/#it{c}); n#sigma_{TOFPr}", kTH2F, {{100, 0, 10}, ConfBothTracks.confNSigmaTPCTOFAxis}); @@ -472,8 +450,8 @@ struct FemtoUniversePairTaskTrackD0 { } // D0/D0bar histograms - auto vbins = (std::vector)binsPt; - auto vFivePtBins = (std::vector)binsPtFive; + auto vbins = binsPt.value; + auto vFivePtBins = binsPtFive.value; // Axes for THnSparse const AxisSpec thnAxisNonPromptScore{ConfThnAxes.thnConfigAxisNonPromptScore, "BDT score non-prompt"}; const AxisSpec thnAxisPromptScore{ConfThnAxes.thnConfigAxisPromptScore, "BDT score prompt"}; @@ -504,20 +482,18 @@ struct FemtoUniversePairTaskTrackD0 { registry.add("DebugBdt/hBdtScore0", ";BDT score;Entries", {HistType::kTH1F, {axisBdtScore}}); registry.add("DebugBdt/hBdtScore1", ";BDT score;Entries", {HistType::kTH1F, {axisBdtScore}}); registry.add("DebugBdt/hBdtScore2", ";BDT score;Entries", {HistType::kTH1F, {axisBdtScore}}); - if (ConfFill.fillBDTvsPt) { - registry.add("DebugBdtMcReco/hBdtScoresVsPtD0", "Thn for reconstructed D0 candidates", HistType::kTHnSparseD, axesTHnMc); - registry.get(HIST("DebugBdtMcReco/hBdtScoresVsPtD0"))->Sumw2(); - registry.add("DebugBdtMcReco/hBdtScoresVsPtD0bar", "Thn for reconstructed D0bar candidates", HistType::kTHnSparseD, axesTHnMc); - registry.get(HIST("DebugBdtMcReco/hBdtScoresVsPtD0bar"))->Sumw2(); - registry.add("DebugBdtMcReco/hBdtScoresVsPtPromptD0", "Thn for reconstructed prompt D0 candidates", HistType::kTHnSparseD, axesTHnFD); - registry.get(HIST("DebugBdtMcReco/hBdtScoresVsPtPromptD0"))->Sumw2(); - registry.add("DebugBdtMcReco/hBdtScoresVsPtNonPromptD0", "Thn for reconstructed non-prompt D0 candidates", HistType::kTHnSparseD, axesTHnFD); - registry.get(HIST("DebugBdtMcReco/hBdtScoresVsPtNonPromptD0"))->Sumw2(); - registry.add("DebugBdtMcReco/hBdtScoresVsPtPromptD0bar", "Thn for reconstructed prompt D0bar candidates", HistType::kTHnSparseD, axesTHnFD); - registry.get(HIST("DebugBdtMcReco/hBdtScoresVsPtPromptD0bar"))->Sumw2(); - registry.add("DebugBdtMcReco/hBdtScoresVsPtNonPromptD0bar", "Thn for reconstructed non-prompt D0bar candidates", HistType::kTHnSparseD, axesTHnFD); - registry.get(HIST("DebugBdtMcReco/hBdtScoresVsPtNonPromptD0bar"))->Sumw2(); - } + registry.add("DebugBdtMcReco/hBdtScoresVsPtD0", "Thn for reconstructed D0 candidates", HistType::kTHnSparseD, axesTHnMc); + registry.get(HIST("DebugBdtMcReco/hBdtScoresVsPtD0"))->Sumw2(); + registry.add("DebugBdtMcReco/hBdtScoresVsPtD0bar", "Thn for reconstructed D0bar candidates", HistType::kTHnSparseD, axesTHnMc); + registry.get(HIST("DebugBdtMcReco/hBdtScoresVsPtD0bar"))->Sumw2(); + registry.add("DebugBdtMcReco/hBdtScoresVsPtPromptD0", "Thn for reconstructed prompt D0 candidates", HistType::kTHnSparseD, axesTHnFD); + registry.get(HIST("DebugBdtMcReco/hBdtScoresVsPtPromptD0"))->Sumw2(); + registry.add("DebugBdtMcReco/hBdtScoresVsPtNonPromptD0", "Thn for reconstructed non-prompt D0 candidates", HistType::kTHnSparseD, axesTHnFD); + registry.get(HIST("DebugBdtMcReco/hBdtScoresVsPtNonPromptD0"))->Sumw2(); + registry.add("DebugBdtMcReco/hBdtScoresVsPtPromptD0bar", "Thn for reconstructed prompt D0bar candidates", HistType::kTHnSparseD, axesTHnFD); + registry.get(HIST("DebugBdtMcReco/hBdtScoresVsPtPromptD0bar"))->Sumw2(); + registry.add("DebugBdtMcReco/hBdtScoresVsPtNonPromptD0bar", "Thn for reconstructed non-prompt D0bar candidates", HistType::kTHnSparseD, axesTHnFD); + registry.get(HIST("DebugBdtMcReco/hBdtScoresVsPtNonPromptD0bar"))->Sumw2(); if (ConfFill.fillBDTvsPtVsMass) { registry.add("DebugBdt/hBdtScoresVsMassVsPtD0", "Thn for D0 candidates", HistType::kTHnSparseD, axesTHn); registry.get(HIST("DebugBdt/hBdtScoresVsMassVsPtD0"))->Sumw2(); @@ -531,16 +507,16 @@ struct FemtoUniversePairTaskTrackD0 { mcRecoRegistry.add("hMcRecD0PromptPt", "MC Reco prompt D0s;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {vbins}}); mcRecoRegistry.add("hMcRecD0NonPrompt", "MC Reco non-prompt D0s;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}, {400, -1.0, 1.0}}}); mcRecoRegistry.add("hMcRecD0NonPromptPt", "MC Reco non-prompt D0s;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {vbins}}); - mcRecoRegistry.add("hMcRecD0Phi", "MC Reco all D0s;#varphi (rad); counts", {HistType::kTH1F, {{80, 0., o2::constants::math::TwoPI}}}); - mcRecoRegistry.add("hMcRecD0PromptPhi", "MC Reco prompt D0s;#varphi (rad); counts", {HistType::kTH1F, {{80, 0., o2::constants::math::TwoPI}}}); - mcRecoRegistry.add("hMcRecD0NonPromptPhi", "MC Reco non-prompt D0s;#varphi (rad); counts", {HistType::kTH1F, {{80, 0., o2::constants::math::TwoPI}}}); + mcRecoRegistry.add("hMcRecD0Phi", "MC Reco all D0s;#varphi (rad); counts", {HistType::kTH1F, {{80, 0., TwoPI}}}); + mcRecoRegistry.add("hMcRecD0PromptPhi", "MC Reco prompt D0s;#varphi (rad); counts", {HistType::kTH1F, {{80, 0., TwoPI}}}); + mcRecoRegistry.add("hMcRecD0NonPromptPhi", "MC Reco non-prompt D0s;#varphi (rad); counts", {HistType::kTH1F, {{80, 0., TwoPI}}}); mcRecoRegistry.add("hMcRecD0bar", "MC Reco all D0bars;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}, {400, -1.0, 1.0}}}); mcRecoRegistry.add("hMcRecD0barPt", "MC Reco all D0bars;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {vbins}}); mcRecoRegistry.add("hMcRecD0barPrompt", "MC Reco prompt D0bars;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}, {400, -1.0, 1.0}}}); mcRecoRegistry.add("hMcRecD0barPromptPt", "MC Reco prompt D0bars;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {vbins}}); mcRecoRegistry.add("hMcRecD0barNonPrompt", "MC Reco non-prompt D0bars;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}, {400, -1.0, 1.0}}}); mcRecoRegistry.add("hMcRecD0barNonPromptPt", "MC Reco non-prompt D0bars;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {vbins}}); - mcRecoRegistry.add("hMcRecD0barPhi", "MC Reco all D0bars;#varphi (rad); counts", {HistType::kTH1F, {{80, 0., o2::constants::math::TwoPI}}}); + mcRecoRegistry.add("hMcRecD0barPhi", "MC Reco all D0bars;#varphi (rad); counts", {HistType::kTH1F, {{80, 0., TwoPI}}}); // Inv. mass histograms mcRecoRegistry.add("hMassVsPtD0Sig", "2-prong candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {confInvMassBins, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); mcRecoRegistry.add("hMassVsPtD0Refl", "2-prong candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {confInvMassBins, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); @@ -609,8 +585,8 @@ struct FemtoUniversePairTaskTrackD0 { mcTruthRegistry.add("hMcGenPrPt", "MC Truth proton;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {{500, 0, 5}}}); mcTruthRegistry.add("hMcGenAntiPrPt", "MC Truth antiproton;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {{500, 0, 5}}}); - trackHistoPartTrack.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarBins, ConfBothTracks.confIsMC, ConfTrack.confPDGCodeTrack); - trackHistoPartD0D0bar.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarInvMassBins, ConfBothTracks.confIsMC, ConfDmesons.confPDGCodeD0); + trackHistoPartTrack.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarBins, ConfBothTracks.confIsMC, ConfTrack.confPDGCodeTrack, ConfBothTracks.confIsDebug); + trackHistoPartD0D0bar.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarInvMassBins, ConfBothTracks.confIsMC, ConfDmesons.confPDGCodeD0, ConfBothTracks.confIsDebug); mixQaRegistry.add("MixingQA/hSECollisionBins", ";bin;Entries", kTH1F, {{120, -0.5, 119.5}}); mixQaRegistry.add("MixingQA/hMECollisionBins", ";bin;Entries", kTH1F, {{120, -0.5, 119.5}}); @@ -626,9 +602,6 @@ struct FemtoUniversePairTaskTrackD0 { if (ConfCPR.confIsCPR.value) { pairCloseRejection.init(&resultRegistry, &qaRegistry, ConfBothTracks.confDeltaEtaAxis, ConfBothTracks.confDeltaPhiStarAxis, ConfCPR.confCPRdeltaPhiCutMin.value, ConfCPR.confCPRdeltaPhiCutMax.value, ConfCPR.confCPRdeltaEtaCutMin.value, ConfCPR.confCPRdeltaEtaCutMax.value, ConfCPR.confCPRChosenRadii.value, ConfCPR.confCPRPlotPerRadii.value); } - - vPIDTrack = ConfTrack.confPIDTrack.value; - kNsigma = ConfBothTracks.confTrkPIDnSigmaMax.value; } template @@ -638,34 +611,6 @@ struct FemtoUniversePairTaskTrackD0 { eventHisto.fillQA(col); } - void processQAD0D0barSel(o2::aod::FdCollision const& col, FemtoFullParticles const&) - { - auto groupPartsD0s = partsD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - auto groupPartsD0bars = partsD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - auto groupPartsD0sFromSB = partsD0sFromSB->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - - // loop over selected D0 candidates - for (auto const& d0cand : groupPartsD0s) { - - qaRegistry.fill(HIST("QA_D0D0barSelection/hInvMassD0"), d0cand.mLambda()); - qaRegistry.fill(HIST("QA_D0D0barSelection/hPtD0"), d0cand.pt()); - } - // loop over selected D0bar candidates - for (auto const& d0barcand : groupPartsD0bars) { - - qaRegistry.fill(HIST("QA_D0D0barSelection/hInvMassD0bar"), d0barcand.mAntiLambda()); - qaRegistry.fill(HIST("QA_D0D0barSelection/hPtD0bar"), d0barcand.pt()); - } - - // loop over selected D0 candidates from SB regions - for (auto const& d0SB : groupPartsD0sFromSB) { - - qaRegistry.fill(HIST("QA_D0D0barSelection_SB/hInvMassD0"), d0SB.mLambda()); - qaRegistry.fill(HIST("QA_D0D0barSelection_SB/hPtD0"), d0SB.pt()); - } - } - PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processQAD0D0barSel, "Enable filling QA plots for selected D0/D0bar cand.", true); - void processAllDmesons(o2::aod::FdCollision const& col, FemtoFullParticles const&) { auto groupPartsAllD0D0barCands = partsAllDmesons->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); @@ -863,31 +808,31 @@ struct FemtoUniversePairTaskTrackD0 { } } - /// process function for to call doSameEvent with Data - /// call this process function if you need D0/D0bar candidates which pass only one mass hypothesis + /// process function to call doSameEvent with Data + /// call this process function if you D0-track pairs /// \param col subscribe to the collision table (Data) /// \param parts subscribe to the femtoUniverseParticleTable - void processSameEvent(o2::aod::FdCollision const& col, - FemtoFullParticles const& parts) + void processSameEventSigD0(o2::aod::FdCollision const& col, FemtoFullParticles const& parts) { fillCollision(col); - auto theGroupPartsTrack = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - auto theGroupPartsD0s = partsD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - auto theGroupPartsD0bars = partsD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto groupedTracks = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto groupedD0s = partSigD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - switch (confChooseD0trackCorr) { - case 0: - doSameEvent(theGroupPartsTrack, theGroupPartsD0s, parts, col.magField(), col.multNtr()); - break; - case 1: - doSameEvent(theGroupPartsTrack, theGroupPartsD0bars, parts, col.magField(), col.multNtr()); - break; - default: - break; - } + doSameEvent(groupedTracks, groupedD0s, parts, col.magField(), col.multNtr()); } - PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processSameEvent, "Enable processing same event", true); + PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processSameEventSigD0, "Enable processing same event for signal D0-track pairs", false); + + void processSameEventSigD0bar(o2::aod::FdCollision const& col, FemtoFullParticles const& parts) + { + fillCollision(col); + + auto groupedTracks = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto groupedD0bars = partSigD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + + doSameEvent(groupedTracks, groupedD0bars, parts, col.magField(), col.multNtr()); + } + PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processSameEventSigD0bar, "Enable processing same event for signal D0bar-track pairs", false); /// process function for to call doSameEvent with Data /// call this process function to include candidates which pass as well the selection for both D0 and D0bar candidates @@ -899,8 +844,8 @@ struct FemtoUniversePairTaskTrackD0 { fillCollision(col); auto theGroupPartsTrack = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - auto theGroupPartsD0s = partsAllD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - auto theGroupPartsD0bars = partsAllD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto theGroupPartsD0s = partSigD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto theGroupPartsD0bars = partSigD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); switch (confChooseD0trackCorr) { case 0: @@ -915,17 +860,39 @@ struct FemtoUniversePairTaskTrackD0 { } PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processSameEventWithDoubleHypo, "Enable processing same event", false); - /// process function for to call doSameEvent with Data - /// call this process to obtain the function for D0/D0bar candidates from side-band regions + /// process function to call doSameEvent with Data + /// call processSameEventSideBandD0 to obtain the function for D0 candidates from side-band regions /// \param col subscribe to the collision table (Data) /// \param parts subscribe to the femtoUniverseParticleTable + void processSameEventSideBandD0(o2::aod::FdCollision const& col, FemtoFullParticles const& parts) + { + fillCollision(col); + + auto groupedTracks = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto groupedD0s = partSidebandD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + + doSameEvent(groupedTracks, groupedD0s, parts, col.magField(), col.multNtr()); + } + PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processSameEventSideBandD0, "Enable processing same event for sideband D0-track pairs", false); + + void processSameEventSideBandD0bar(o2::aod::FdCollision const& col, FemtoFullParticles const& parts) + { + fillCollision(col); + + auto groupedTracks = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto groupedD0bars = partSidebandD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + + doSameEvent(groupedTracks, groupedD0bars, parts, col.magField(), col.multNtr()); + } + PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processSameEventSideBandD0bar, "Enable processing same event for sideband D0bar-track pairs", false); + void processSameEventSB(o2::aod::FdCollision const& col, FemtoFullParticles const& parts) { fillCollision(col); auto groupPartsTrack = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - auto groupPartsD0sFromSB = partsD0sFromSB->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - auto groupPartsD0barsFromSB = partsD0barsFromSB->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto groupPartsD0sFromSB = partSidebandD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto groupPartsD0barsFromSB = partSidebandD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); switch (confChooseD0trackCorr) { case 0: @@ -944,7 +911,7 @@ struct FemtoUniversePairTaskTrackD0 { /// \param col subscribe to the collision table (Monte Carlo Reconstructed) /// \param parts subscribe to joined table FemtoUniverseParticles and FemtoUniverseMCLables to access Monte Carlo truth /// \param FemtoUniverseMCParticles subscribe to the Monte Carlo truth table - void processSameEventMCRecoSignal(aod::FdCollision const& col, FemtoMcRecoParticles const& parts, aod::FdMCParticles const&) + void processSameEventMCRecoSignal(aod::FdCollision const& col, FemtoMcParticles const& parts, aod::FdMCParticles const&) { fillCollision(col); @@ -969,7 +936,7 @@ struct FemtoUniversePairTaskTrackD0 { /// \param col subscribe to the collision table (Monte Carlo Reconstructed) /// \param parts subscribe to joined table FemtoUniverseParticles and FemtoUniverseMCLables to access Monte Carlo truth /// \param FemtoUniverseMCParticles subscribe to the Monte Carlo truth table - void processSameEventMCRecoSB(aod::FdCollision const& col, FemtoMcRecoParticles const& parts, aod::FdMCParticles const&) + void processSameEventMCRecoSB(aod::FdCollision const& col, FemtoMcParticles const& parts, aod::FdMCParticles const&) { fillCollision(col); @@ -994,7 +961,7 @@ struct FemtoUniversePairTaskTrackD0 { /// \param col subscribe to the collision table /// \param parts subscribe to the joined table FemtoUniverseParticles and FemtoUniverseMCLables to access Monte Carlo truth /// \param FdMCParticles subscribe to the Monte Carlo truth table - void processSameEventMcTruth(aod::FdCollision const& col, FemtoMcRecoParticles const& parts, aod::FdMCParticles const&) + void processSameEventMcTruth(aod::FdCollision const& col, FemtoMcParticles const& parts, aod::FdMCParticles const&) { fillCollision(col); /// !!! needs to be finished -> add possibility to change between D0 and D0bar @@ -1065,20 +1032,18 @@ struct FemtoUniversePairTaskTrackD0 { } /// process function for to call doMixedEvent with Data - /// call this process function if you need D0/D0bar candidates which pass only one mass hypothesis + /// call this process function if you need D0-track ME function /// @param cols subscribe to the collisions table (Data) /// @param parts subscribe to the femtoUniverseParticleTable - void processMixedEvent(o2::aod::FdCollisions const& cols, - FemtoFullParticles const& parts) + void processMixedEventSigD0(o2::aod::FdCollisions const& cols, FemtoFullParticles const& parts) { for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, confNEventsMix, -1, cols, cols)) { const int multiplicityCol = collision1.multNtr(); mixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), multiplicityCol})); - auto groupPartsTrack = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); - auto theGroupPartsD0s = partsD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); - auto theGroupPartsD0bars = partsD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); + auto groupedTracks = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); + auto groupedD0s = partSigD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); const auto& magFieldTesla1 = collision1.magField(); const auto& magFieldTesla2 = collision2.magField(); @@ -1086,22 +1051,31 @@ struct FemtoUniversePairTaskTrackD0 { if (magFieldTesla1 != magFieldTesla2) { continue; } - /// \todo before mixing we should check whether both collisions contain a pair of particles! - // if (partsD0.size() == 0 || kNPart2Evt1 == 0 || kNPart1Evt2 == 0 || partsTrack.size() == 0 ) continue; + doMixedEvent(groupedTracks, groupedD0s, parts, magFieldTesla1, multiplicityCol); + } + } + PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processMixedEventSigD0, "Enable processing signal D0-track mixed events", false); - switch (confChooseD0trackCorr) { - case 0: - doMixedEvent(groupPartsTrack, theGroupPartsD0s, parts, magFieldTesla1, multiplicityCol); - break; - case 1: - doMixedEvent(groupPartsTrack, theGroupPartsD0bars, parts, magFieldTesla1, multiplicityCol); - break; - default: - break; + void processMixedEventSigD0bar(o2::aod::FdCollisions const& cols, FemtoFullParticles const& parts) + { + for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, confNEventsMix, -1, cols, cols)) { + + const int multiplicityCol = collision1.multNtr(); + mixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), multiplicityCol})); + + auto groupedTracks = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); + auto groupedD0bars = partSigD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); + + const auto& magFieldTesla1 = collision1.magField(); + const auto& magFieldTesla2 = collision2.magField(); + + if (magFieldTesla1 != magFieldTesla2) { + continue; } + doMixedEvent(groupedTracks, groupedD0bars, parts, magFieldTesla1, multiplicityCol); } } - PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processMixedEvent, "Enable processing mixed events", true); + PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processMixedEventSigD0bar, "Enable processing signal D0bar-track mixed events", false); /// process function for to call doMixedEvent with Data /// call this process function to include candidates which pass as well the selection for both D0 and D0bar candidates @@ -1116,8 +1090,8 @@ struct FemtoUniversePairTaskTrackD0 { mixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), multiplicityCol})); auto groupPartsTrack = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); - auto theGroupPartsD0s = partsAllD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); - auto theGroupPartsD0bars = partsAllD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); + auto theGroupPartsD0s = partSigD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); + auto theGroupPartsD0bars = partSigD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); const auto& magFieldTesla1 = collision1.magField(); const auto& magFieldTesla2 = collision2.magField(); @@ -1154,8 +1128,8 @@ struct FemtoUniversePairTaskTrackD0 { mixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), multiplicityCol})); auto groupPartsTrack = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); - auto groupPartsD0sFromSB = partsD0sFromSB->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); - auto groupPartsD0barsFromSB = partsD0barsFromSB->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); + auto groupPartsD0sFromSB = partSidebandD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); + auto groupPartsD0barsFromSB = partSidebandD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); const auto& magFieldTesla1 = collision1.magField(); const auto& magFieldTesla2 = collision2.magField(); @@ -1183,7 +1157,7 @@ struct FemtoUniversePairTaskTrackD0 { /// @param cols subscribe to the collisions table (Monte Carlo Reconstructed) /// @param parts subscribe to joined table FemtoUniverseParticles and FemtoUniverseMCLables to access Monte Carlo truth /// @param FemtoUniverseMCParticles subscribe to the Monte Carlo truth table - void processMixedEventMCRecoSignal(aod::FdCollisions const& cols, FemtoMcRecoParticles const& parts, aod::FdMCParticles const&) + void processMixedEventMCRecoSignal(aod::FdCollisions const& cols, FemtoMcParticles const& parts, aod::FdMCParticles const&) { for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, confNEventsMix, -1, cols, cols)) { @@ -1219,7 +1193,7 @@ struct FemtoUniversePairTaskTrackD0 { /// @param cols subscribe to the collisions table (Monte Carlo Reconstructed) /// @param parts subscribe to joined table FemtoUniverseParticles and FemtoUniverseMCLables to access Monte Carlo truth /// @param FemtoUniverseMCParticles subscribe to the Monte Carlo truth table - void processMixedEventMCRecoSB(aod::FdCollisions const& cols, FemtoMcRecoParticles const& parts, aod::FdMCParticles const&) + void processMixedEventMCRecoSB(aod::FdCollisions const& cols, FemtoMcParticles const& parts, aod::FdMCParticles const&) { for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, confNEventsMix, -1, cols, cols)) { @@ -1255,7 +1229,7 @@ struct FemtoUniversePairTaskTrackD0 { /// @param cols subscribe to the collisions table (Monte Carlo Reconstructed reconstructed) /// @param parts subscribe to joined table FemtoUniverseParticles and FemtoUniverseMCLables to access Monte Carlo truth /// @param FemtoUniverseMCParticles subscribe to the Monte Carlo truth table - void processMixedEventMCTruth(aod::FdCollisions const& cols, FemtoMcRecoParticles const& parts, aod::FdMCParticles const&) + void processMixedEventMCTruth(aod::FdCollisions const& cols, FemtoMcParticles const& parts, aod::FdMCParticles const&) { for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, confNEventsMix, -1, cols, cols)) { @@ -1287,7 +1261,7 @@ struct FemtoUniversePairTaskTrackD0 { } PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processMixedEventMCTruth, "MC Truth - enable processing mixed events for gen. D0/D0bar", false); - void processMcReco(FemtoMcRecoParticles const& recoParts, aod::FdMCParticles const& mcParts) + void processMcReco(FemtoMcParticles const& recoParts, aod::FdMCParticles const& mcParts) { for (auto const& part : recoParts) { auto mcPartId = part.fdMCParticleId(); @@ -1298,40 +1272,40 @@ struct FemtoUniversePairTaskTrackD0 { weight = 1.0f; // filling the histograms for identified hadrons - if (part.partType() == aod::femtouniverseparticle::ParticleType::kTrack) { + if ((part.partType() == aod::femtouniverseparticle::ParticleType::kTrack) && (part.pt() > ConfTrack.confTrackLowPtCut) && (part.pt() < ConfTrack.confTrackHighPtCut) && (std::abs(part.eta()) < ConfTrack.confTrackEtaMax)) { if (part.sign() > 0) { - if ((mcpart.pdgMCTruth() == PDG_t::kProton) && (part.pt() > ConfTrack.confTrackLowPtCut) && (part.pt() < ConfTrack.confTrackHighPtCut) && isParticleNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { + if ((mcpart.pdgMCTruth() == PDG_t::kProton) && isProtonNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton))) { mcRecoRegistry.fill(HIST("hMcRecPrPt"), part.pt()); if (mcpart.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kPrimary) { mcRecoRegistry.fill(HIST("hMcRecPrPtGenEtaGen"), mcpart.pt(), mcpart.eta()); } } - if ((mcpart.pdgMCTruth() == PDG_t::kPiPlus) && (part.pt() > ConfTrack.confTrackLowPtCut) && (part.pt() < ConfTrack.confTrackHighPtCut) && isParticleNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { + if ((mcpart.pdgMCTruth() == PDG_t::kPiPlus) && isPionNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion))) { mcRecoRegistry.fill(HIST("hMcRecPipPt"), part.pt()); if (mcpart.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kPrimary) { mcRecoRegistry.fill(HIST("hMcRecPipPtGenEtaGen"), mcpart.pt(), mcpart.eta()); } } - if ((mcpart.pdgMCTruth() == PDG_t::kKPlus) && (part.pt() > ConfTrack.confTrackLowPtCut) && (part.pt() < ConfTrack.confTrackHighPtCut) && isParticleNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { + if ((mcpart.pdgMCTruth() == PDG_t::kKPlus) && isKaonNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { mcRecoRegistry.fill(HIST("hMcRecKpPt"), part.pt()); if (mcpart.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kPrimary) { mcRecoRegistry.fill(HIST("hMcRecKpPtGenEtaGen"), mcpart.pt(), mcpart.eta()); } } } else if (part.sign() < 0) { - if ((mcpart.pdgMCTruth() == -PDG_t::kProton) && (part.pt() > ConfTrack.confTrackLowPtCut) && (part.pt() < ConfTrack.confTrackHighPtCut) && isParticleNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { + if ((mcpart.pdgMCTruth() == -PDG_t::kProton) && isProtonNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton))) { mcRecoRegistry.fill(HIST("hMcRecAntiPrPt"), part.pt()); if (mcpart.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kPrimary) { mcRecoRegistry.fill(HIST("hMcRecAntiPrPtGenEtaGen"), mcpart.pt(), mcpart.eta()); } } - if ((mcpart.pdgMCTruth() == -PDG_t::kPiPlus) && (part.pt() > ConfTrack.confTrackLowPtCut) && (part.pt() < ConfTrack.confTrackHighPtCut) && isParticleNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { + if ((mcpart.pdgMCTruth() == -PDG_t::kPiPlus) && isPionNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion))) { mcRecoRegistry.fill(HIST("hMcRecPimPt"), part.pt()); if (mcpart.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kPrimary) { mcRecoRegistry.fill(HIST("hMcRecPimPtGenEtaGen"), mcpart.pt(), mcpart.eta()); } } - if ((mcpart.pdgMCTruth() == -PDG_t::kKPlus) && (part.pt() > ConfTrack.confTrackLowPtCut) && (part.pt() < ConfTrack.confTrackHighPtCut) && isParticleNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { + if ((mcpart.pdgMCTruth() == -PDG_t::kKPlus) && isKaonNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { mcRecoRegistry.fill(HIST("hMcRecKmPt"), part.pt()); if (mcpart.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kPrimary) { mcRecoRegistry.fill(HIST("hMcRecKmPtGenEtaGen"), mcpart.pt(), mcpart.eta()); @@ -1350,16 +1324,12 @@ struct FemtoUniversePairTaskTrackD0 { mcRecoRegistry.fill(HIST("hMcRecD0Prompt"), part.pt(), part.eta()); mcRecoRegistry.fill(HIST("hMcRecD0PromptPt"), part.pt()); mcRecoRegistry.fill(HIST("hMcRecD0PromptPhi"), part.phi()); - if (ConfFill.fillBDTvsPt) { - registry.fill(HIST("DebugBdtMcReco/hBdtScoresVsPtPromptD0"), part.decayVtxX(), part.decayVtxY(), part.decayVtxZ(), part.pt()); - } + registry.fill(HIST("DebugBdtMcReco/hBdtScoresVsPtPromptD0"), part.decayVtxX(), part.decayVtxY(), part.decayVtxZ(), part.pt()); } else if (part.tpcNClsFound() == 1) { // non-prompt candidates mcRecoRegistry.fill(HIST("hMcRecD0NonPrompt"), part.pt(), part.eta()); mcRecoRegistry.fill(HIST("hMcRecD0NonPromptPt"), part.pt()); mcRecoRegistry.fill(HIST("hMcRecD0NonPromptPhi"), part.phi()); - if (ConfFill.fillBDTvsPt) { - registry.fill(HIST("DebugBdtMcReco/hBdtScoresVsPtNonPromptD0"), part.decayVtxX(), part.decayVtxY(), part.decayVtxZ(), part.pt()); - } + registry.fill(HIST("DebugBdtMcReco/hBdtScoresVsPtNonPromptD0"), part.decayVtxX(), part.decayVtxY(), part.decayVtxZ(), part.pt()); } } else if (mcpart.pdgMCTruth() == ConfDmesons.confPDGCodeD0bar) { mcRecoRegistry.fill(HIST("hMcRecD0bar"), part.pt(), part.eta()); @@ -1370,15 +1340,11 @@ struct FemtoUniversePairTaskTrackD0 { if (part.tpcNClsFound() == 0) { // prompt candidates mcRecoRegistry.fill(HIST("hMcRecD0barPrompt"), part.pt(), part.eta()); mcRecoRegistry.fill(HIST("hMcRecD0barPromptPt"), part.pt()); - if (ConfFill.fillBDTvsPt) { - registry.fill(HIST("DebugBdtMcReco/hBdtScoresVsPtNonPromptD0bar"), part.decayVtxX(), part.decayVtxY(), part.decayVtxZ(), part.pt()); - } + registry.fill(HIST("DebugBdtMcReco/hBdtScoresVsPtPromptD0bar"), part.decayVtxX(), part.decayVtxY(), part.decayVtxZ(), part.pt()); } else if (part.tpcNClsFound() == 1) { // non-prompt candidates mcRecoRegistry.fill(HIST("hMcRecD0barNonPrompt"), part.pt(), part.eta()); mcRecoRegistry.fill(HIST("hMcRecD0barNonPromptPt"), part.pt()); - if (ConfFill.fillBDTvsPt) { - registry.fill(HIST("DebugBdtMcReco/hBdtScoresVsPtNonPromptD0bar"), part.decayVtxX(), part.decayVtxY(), part.decayVtxZ(), part.pt()); - } + registry.fill(HIST("DebugBdtMcReco/hBdtScoresVsPtNonPromptD0bar"), part.decayVtxX(), part.decayVtxY(), part.decayVtxZ(), part.pt()); } } } @@ -1386,7 +1352,7 @@ struct FemtoUniversePairTaskTrackD0 { } PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processMcReco, "Process MC reco data", false); - void processMcRecoD0InvMass(FemtoMcRecoParticles const& recoParts) + void processMcRecoD0InvMass(FemtoMcParticles const& recoParts) { for (auto const& part : recoParts) { // filling the histograms for identified hadrons @@ -1432,13 +1398,13 @@ struct FemtoUniversePairTaskTrackD0 { } if (ConfFill.fillCorrBkgs) { if (part.sign() == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiKPi0) { // D0 -> pi+K-pi0 - mcRecoRegistry.fill(HIST("hMassVsPtD0barToPiKaPi"), part.mLambda(), part.pt(), weight); + mcRecoRegistry.fill(HIST("hMassVsPtD0barToPiKaPi"), part.mAntiLambda(), part.pt(), weight); } else if (part.sign() == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiPi) { // D0 -> pi+pi- - mcRecoRegistry.fill(HIST("hMassVsPtD0barToPiPi"), part.mLambda(), part.pt(), weight); + mcRecoRegistry.fill(HIST("hMassVsPtD0barToPiPi"), part.mAntiLambda(), part.pt(), weight); } else if (part.sign() == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiPiPi0) { // D0 -> pi+pi-pi0 - mcRecoRegistry.fill(HIST("hMassVsPtD0barToPiPiPi"), part.mLambda(), part.pt(), weight); + mcRecoRegistry.fill(HIST("hMassVsPtD0barToPiPiPi"), part.mAntiLambda(), part.pt(), weight); } else if (part.sign() == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToKK) { // D0 -> K+K- - mcRecoRegistry.fill(HIST("hMassVsPtD0barToKaKa"), part.mLambda(), part.pt(), weight); + mcRecoRegistry.fill(HIST("hMassVsPtD0barToKaKa"), part.mAntiLambda(), part.pt(), weight); } } if (part.tpcNClsFound() == 0) { // prompt candidates @@ -1455,7 +1421,7 @@ struct FemtoUniversePairTaskTrackD0 { void processMcTruth(aod::FDParticles const& parts) { for (auto const& part : parts) { - if (part.partType() != uint8_t(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) { + if (part.partType() != static_cast(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) { continue; } @@ -1538,8 +1504,7 @@ struct FemtoUniversePairTaskTrackD0 { /// \param col subscribe to the collision table (Data) /// \param parts subscribe to the femtoUniverseParticleTable - void processPurityQA(aod::FdCollision const& col, - FemtoFullSigmaParticles const&) + void processPurityQA(aod::FdCollision const& col, FemtoFullSigmaParticles const&) { fillCollision(col); @@ -1555,18 +1520,32 @@ struct FemtoUniversePairTaskTrackD0 { } trackHistoPartTrack.fillQA(part); + if (ConfFill.fillTrackQA) { + qaRegistry.fill(HIST("TrackQA/hCharge"), part.sign()); + qaRegistry.fill(HIST("TrackQA/hTPCfindable"), part.tpcNClsFindable()); + qaRegistry.fill(HIST("TrackQA/hTPCfound"), part.tpcNClsFound()); + qaRegistry.fill(HIST("TrackQA/hTPCcrossedOverFindable"), part.tpcCrossedRowsOverFindableCls()); + qaRegistry.fill(HIST("TrackQA/hTPCcrossedRows"), part.tpcNClsCrossedRows()); + qaRegistry.fill(HIST("TrackQA/hTPCfindableVsCrossed"), part.tpcNClsFindable(), part.tpcNClsCrossedRows()); + qaRegistry.fill(HIST("TrackQA/hTPCshared"), part.tpcNClsShared()); + qaRegistry.fill(HIST("TrackQA/hTPCfractionSharedCls"), part.tpcFractionSharedCls()); + qaRegistry.fill(HIST("TrackQA/hITSclusters"), part.itsNCls()); + qaRegistry.fill(HIST("TrackQA/hITSclustersIB"), part.itsNClsInnerBarrel()); + qaRegistry.fill(HIST("TrackQA/hDCAz"), part.pt(), part.dcaZ()); + qaRegistry.fill(HIST("TrackQA/hTPCdEdX"), part.p(), part.tpcSignal()); + } if (std::abs(ConfTrack.confPDGCodeTrack) == PDG_t::kProton) { - qaRegistry.fill(HIST("PurityQA/nSigmaFullTPCPr"), part.p(), part.tpcFullNSigmaPr()); - qaRegistry.fill(HIST("PurityQA/nSigmaFullTOFPr"), part.p(), part.tofFullNSigmaPr()); - qaRegistry.fill(HIST("PurityQA/nSigmaFullCombPr"), part.p(), std::hypot(part.tpcFullNSigmaPr(), part.tofFullNSigmaPr())); + qaRegistry.fill(HIST("PurityQA/nSigmaFullTPCPr"), part.pt(), part.tpcFullNSigmaPr()); + qaRegistry.fill(HIST("PurityQA/nSigmaFullTOFPr"), part.pt(), part.tofFullNSigmaPr()); + qaRegistry.fill(HIST("PurityQA/nSigmaFullCombPr"), part.pt(), std::hypot(part.tpcFullNSigmaPr(), part.tofFullNSigmaPr())); } else if (std::abs(ConfTrack.confPDGCodeTrack) == PDG_t::kPiPlus) { - qaRegistry.fill(HIST("PurityQA/nSigmaFullTPCPi"), part.p(), part.tpcFullNSigmaPi()); - qaRegistry.fill(HIST("PurityQA/nSigmaFullTOFPi"), part.p(), part.tofFullNSigmaPi()); - qaRegistry.fill(HIST("PurityQA/nSigmaFullCombPi"), part.p(), std::hypot(part.tpcFullNSigmaPi(), part.tofFullNSigmaPi())); + qaRegistry.fill(HIST("PurityQA/nSigmaFullTPCPi"), part.pt(), part.tpcFullNSigmaPi()); + qaRegistry.fill(HIST("PurityQA/nSigmaFullTOFPi"), part.pt(), part.tofFullNSigmaPi()); + qaRegistry.fill(HIST("PurityQA/nSigmaFullCombPi"), part.pt(), std::hypot(part.tpcFullNSigmaPi(), part.tofFullNSigmaPi())); } else if (std::abs(ConfTrack.confPDGCodeTrack) == PDG_t::kKPlus) { - qaRegistry.fill(HIST("PurityQA/nSigmaFullTPCKa"), part.p(), part.tpcFullNSigmaKa()); - qaRegistry.fill(HIST("PurityQA/nSigmaFullTOFKa"), part.p(), part.tofFullNSigmaKa()); - qaRegistry.fill(HIST("PurityQA/nSigmaFullCombKa"), part.p(), std::hypot(part.tpcFullNSigmaKa(), part.tofFullNSigmaKa())); + qaRegistry.fill(HIST("PurityQA/nSigmaFullTPCKa"), part.pt(), part.tpcFullNSigmaKa()); + qaRegistry.fill(HIST("PurityQA/nSigmaFullTOFKa"), part.pt(), part.tofFullNSigmaKa()); + qaRegistry.fill(HIST("PurityQA/nSigmaFullCombKa"), part.pt(), std::hypot(part.tpcFullNSigmaKa(), part.tofFullNSigmaKa())); } } } diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackNucleus.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackNucleus.cxx index 51f8ac5fd31..037c88b9837 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackNucleus.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackNucleus.cxx @@ -191,7 +191,7 @@ struct FemtoUniversePairTaskTrackNucleus { ConfigurableAxis confMtBins3D{"confMtBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; ConfigurableAxis confMultBins3D{"confMultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "Multiplicity binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; ConfigurableAxis confKstarBins{"confKstarBins", {1500, 0., 6.}, "Binning kstar"}; ConfigurableAxis confKtBins{"confKtBins", {150, 0., 9.}, "Binning kT"}; diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackPhi.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackPhi.cxx index 3cd734acc13..7aa4e4bc2c4 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackPhi.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackPhi.cxx @@ -44,6 +44,7 @@ #include #include +#include #include #include @@ -137,12 +138,12 @@ struct FemtoUniversePairTaskTrackPhi { Configurable confTrackUseRun3PIDforKaons{"confTrackUseRun3PIDforKaons", true, "Use Run3 PID for kaons from Veronika Barbasova's AN (https://alice-notes.web.cern.ch/node/1758). If this is on the other PID methods are ignored for kaons."}; /// Partitions for the track (particle 1) - Partition partsTrack = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && + Partition partsTrack = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::sign == confTrackSign) && (aod::femtouniverseparticle::pt > confTrackPtLow) && (aod::femtouniverseparticle::pt < confTrackPtHigh); - Partition partsTrackMCReco = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && + Partition partsTrackMCReco = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::sign == confTrackSign) && (aod::femtouniverseparticle::pt > confTrackPtLow) && (aod::femtouniverseparticle::pt < confTrackPtHigh); @@ -152,27 +153,31 @@ struct FemtoUniversePairTaskTrackPhi { Configurable confPhiPtHigh{"confPhiPtHigh", 4.0, "Higher limit of the Phi pT."}; Configurable confInvMassLowLimitPhi{"confInvMassLowLimitPhi", 1.011, "Lower limit of the Phi invariant mass"}; // change that to do invariant mass cut Configurable confInvMassUpLimitPhi{"confInvMassUpLimitPhi", 1.027, "Upper limit of the Phi invariant mass"}; + Configurable confFillMinvBackground{"confFillMinvBackground", false, "Fill background histograms for Phi invariant mass."}; + Configurable confFillMinvBackgroundKpKm{"confFillMinvBackgroundKpKm", false, "Fill background histograms for K+K- invariant mass."}; + Configurable confFillMinvBackgroundKpKp{"confFillMinvBackgroundKpKp", false, "Fill background histograms for K+K+ invariant mass."}; + Configurable confFillMinvBackgroundKmKm{"confFillMinvBackgroundKmKm", false, "Fill background histograms for K-K- invariant mass."}; /// Partitions for the Phi meson (particle 2) - Partition partsPhi = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kPhi)) && + Partition partsPhi = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kPhi)) && (aod::femtouniverseparticle::pt > confPhiPtLow) && (aod::femtouniverseparticle::pt < confPhiPtHigh) && (aod::femtouniverseparticle::tempFitVar > confInvMassLowLimitPhi) && (aod::femtouniverseparticle::tempFitVar < confInvMassUpLimitPhi); - Partition partsPhiMCReco = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kPhi)) && + Partition partsPhiMCReco = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kPhi)) && (aod::femtouniverseparticle::pt > confPhiPtLow) && (aod::femtouniverseparticle::pt < confPhiPtHigh) && (aod::femtouniverseparticle::tempFitVar > confInvMassLowLimitPhi) && (aod::femtouniverseparticle::tempFitVar < confInvMassUpLimitPhi); /// Partitions for Phi daughters kPhiChild - Partition partsPhiDaugh = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kPhiChild)); - Partition> partsPhiDaughMC = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kPhiChild)); + Partition partsPhiDaugh = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kPhiChild)); + Partition> partsPhiDaughMC = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kPhiChild)); // Partition for K+K- minv - Partition partsKaons = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)); - Partition> partsKaonsMC = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)); + Partition partsKaons = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kTrack)); + Partition> partsKaonsMC = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kTrack)); /// Histogramming for particle 1 FemtoUniverseParticleHisto trackHistoPartTrack; @@ -217,7 +222,7 @@ struct FemtoUniversePairTaskTrackPhi { HistogramRegistry registryDCA{"registryDCA", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; HistogramRegistry registryMCpT{"registryMCpT", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; - ColumnBinningPolicy colBinning{{confBinsVtx, confBinsMult}, true}; + ColumnBinningPolicy colBinning{{confBinsVtx, confBinsMult}}; HistogramRegistry effCorrRegistry{"EfficiencyCorrection", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -383,6 +388,7 @@ struct FemtoUniversePairTaskTrackPhi { registryPhiMinvBackground.add("InvariantMassKpKp", "; invariant mass K+K+; Counts", kTH1F, {{6000, 0.9, 4.0}}); registryPhiMinvBackground.add("InvariantMassKmKm", "; invariant mass K-K-; Counts", kTH1F, {{6000, 0.9, 4.0}}); + registryPhiMinvBackground.add("InvariantMassKpKm", "; invariant mass K+K-; Counts", kTH1F, {{6000, 0.9, 4.0}}); qaRegistry.add("Hadron_pos/nSigmaTPCPr", "; #it{p} (GeV/#it{c}); n#sigma_{TPCPr}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); qaRegistry.add("Hadron_pos/nSigmaTOFPr", "; #it{p} (GeV/#it{c}); n#sigma_{TOFPr}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); @@ -405,12 +411,12 @@ struct FemtoUniversePairTaskTrackPhi { registryMCtruth.add("MCtruthKp", "MC truth K+;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); registryMCtruth.add("MCtruthKpPt", "MC truth kaons positive;#it{p}_{T} (GeV/c)", {HistType::kTH1F, {{500, 0, 5}}}); // K- - registryMCtruth.add("MCtruthKm", "MC truth protons;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); + registryMCtruth.add("MCtruthKm", "MC truth kaons negative;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); registryMCtruth.add("MCtruthKmPt", "MC truth kaons negative;#it{p}_{T} (GeV/c)", {HistType::kTH1F, {{500, 0, 5}}}); // p registryMCtruth.add("MCtruthPpos", "MC truth protons;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); registryMCtruth.add("MCtruthPposPt", "MC truth protons;#it{p}_{T} (GeV/c)", {HistType::kTH1F, {{500, 0, 5}}}); - // pbar + // p- registryMCtruth.add("MCtruthPneg", "MC truth antiprotons;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); registryMCtruth.add("MCtruthPnegPt", "MC truth antiproton;#it{p}_{T} (GeV/c)", {HistType::kTH1F, {{500, 0, 5}}}); // phi @@ -423,7 +429,7 @@ struct FemtoUniversePairTaskTrackPhi { // p registryMCreco.add("MCrecoPpos", "MC reco proton;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); registryMCreco.add("MCrecoPposPt", "MC reco proton; #it{p_T} (GeV/#it{c}); Counts", kTH1F, {{500, 0, 5}}); - // pbar + // p- registryMCreco.add("MCrecoPneg", "MC reco antiproton;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); registryMCreco.add("MCrecoPnegPt", "MC reco antiproton; #it{p_T} (GeV/#it{c}); Counts", kTH1F, {{500, 0, 5}}); // phi @@ -549,31 +555,41 @@ struct FemtoUniversePairTaskTrackPhi { sameEventCont.setPair(track, phicandidate, multCol, confUse3D, mWeight); } - // // Used for better fitting of invariant mass background. - - // TLorentzVector part1Vec; - // TLorentzVector part2Vec; - - // float mMassOne = o2::constants::physics::MassKPlus; - // float mMassTwo = o2::constants::physics::MassKMinus; - - // for (auto const& [kaon1, kaon2] : combinations(CombinationsStrictlyUpperIndexPolicy(groupPartsKaons, groupPartsKaons))) { - // if ((kaon1.mAntiLambda() == 1) && (kaon2.mAntiLambda() == 1)) { - // part1Vec.SetPtEtaPhiM(kaon1.pt(), kaon1.eta(), kaon1.phi(), mMassOne); - // part2Vec.SetPtEtaPhiM(kaon2.pt(), kaon2.eta(), kaon2.phi(), mMassOne); - // TLorentzVector sumVec(part1Vec); - // sumVec += part2Vec; - // registryPhiMinvBackground.fill(HIST("InvariantMassKpKp"), sumVec.M()); - // } - // if ((kaon1.mAntiLambda() == -1) && (kaon2.mAntiLambda() == -1)) { - // part1Vec.SetPtEtaPhiM(kaon1.pt(), kaon1.eta(), kaon1.phi(), mMassTwo); - // part2Vec.SetPtEtaPhiM(kaon2.pt(), kaon2.eta(), kaon2.phi(), mMassTwo); - - // TLorentzVector sumVec(part1Vec); - // sumVec += part2Vec; - // registryPhiMinvBackground.fill(HIST("InvariantMassKmKm"), sumVec.M()); - // } - // } + if (confFillMinvBackground) { + // Used for better fitting of invariant mass background. + + TLorentzVector part1Vec; + TLorentzVector part2Vec; + + float mMassOne = o2::constants::physics::MassKPlus; + float mMassTwo = o2::constants::physics::MassKMinus; + + for (auto const& [kaon1, kaon2] : combinations(CombinationsStrictlyUpperIndexPolicy(partsKaons, partsKaons))) { + if ((confFillMinvBackgroundKpKp) && (kaon1.mAntiLambda() == 1) && (kaon2.mAntiLambda() == 1)) { + part1Vec.SetPtEtaPhiM(kaon1.pt(), kaon1.eta(), kaon1.phi(), mMassOne); + part2Vec.SetPtEtaPhiM(kaon2.pt(), kaon2.eta(), kaon2.phi(), mMassOne); + TLorentzVector sumVec(part1Vec); + sumVec += part2Vec; + registryPhiMinvBackground.fill(HIST("InvariantMassKpKp"), sumVec.M()); + } + if ((confFillMinvBackgroundKmKm) && (kaon1.mAntiLambda() == -1) && (kaon2.mAntiLambda() == -1)) { + part1Vec.SetPtEtaPhiM(kaon1.pt(), kaon1.eta(), kaon1.phi(), mMassTwo); + part2Vec.SetPtEtaPhiM(kaon2.pt(), kaon2.eta(), kaon2.phi(), mMassTwo); + + TLorentzVector sumVec(part1Vec); + sumVec += part2Vec; + registryPhiMinvBackground.fill(HIST("InvariantMassKmKm"), sumVec.M()); + } + if ((confFillMinvBackgroundKpKm) && (kaon1.mAntiLambda() == 1) && (kaon2.mAntiLambda() == -1)) { + part1Vec.SetPtEtaPhiM(kaon1.pt(), kaon1.eta(), kaon1.phi(), mMassOne); + part2Vec.SetPtEtaPhiM(kaon2.pt(), kaon2.eta(), kaon2.phi(), mMassTwo); + + TLorentzVector sumVec(part1Vec); + sumVec += part2Vec; + registryPhiMinvBackground.fill(HIST("InvariantMassKpKm"), sumVec.M()); + } + } + } } template @@ -668,7 +684,7 @@ struct FemtoUniversePairTaskTrackPhi { void processMCTruth(aod::FDParticles const& parts) { for (auto const& part : parts) { - if (part.partType() != uint8_t(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) { + if (part.partType() != static_cast(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) { continue; } diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrack.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrack.cxx index 6874668ca40..4b410fb8fca 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrack.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrack.cxx @@ -121,7 +121,7 @@ struct FemtoUniversePairTaskTrackTrack { ConfigurableAxis confmTBins3D{"confmTBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; ConfigurableAxis confMultBins3D{"confMultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "multiplicity Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; ConfigurableAxis confkstarBins{"confkstarBins", {1500, 0., 6.}, "binning kstar"}; ConfigurableAxis confkTBins{"confkTBins", {150, 0., 9.}, "binning kT"}; diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrack3DMultKtExtended.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrack3DMultKtExtended.cxx index 70f923f112b..def18f1deb1 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrack3DMultKtExtended.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrack3DMultKtExtended.cxx @@ -176,8 +176,8 @@ struct femtoUniversePairTaskTrackTrack3DMultKtExtended { ConfigurableAxis confmTBins3D{"confmTBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; ConfigurableAxis confmultBins3D{"confmultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "multiplicity Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningNtr{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningNtr{{confVtxBins, confMultBins}}; ConfigurableAxis confkstarBins{"confkstarBins", {300, -1.5, 1.5}, "binning kstar"}; ConfigurableAxis confkTBins{"confkTBins", {150, 0., 9.}, "binning kT"}; diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackExtended.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackExtended.cxx index 324bf757704..fe90355be5b 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackExtended.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackExtended.cxx @@ -166,7 +166,7 @@ struct FemtoUniversePairTaskTrackTrackExtended { ConfigurableAxis confmTBins3D{"confmTBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; ConfigurableAxis confMultBins3D{"confMultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "multiplicity Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; ConfigurableAxis confkstarBins{"confkstarBins", {1500, 0., 6.}, "binning kstar"}; ConfigurableAxis confkTBins{"confkTBins", {150, 0., 9.}, "binning kT"}; diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMC.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMC.cxx index 4b4fa24fff5..b0802f6521b 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMC.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMC.cxx @@ -157,7 +157,7 @@ struct femtoUniversePairTaskTrackTrackMC { ConfigurableAxis ConfmTBins3D{"ConfmTBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; ConfigurableAxis ConfmultBins3D{"ConfmultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "multiplicity Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; - ColumnBinningPolicy colBinning{{ConfVtxBins, ConfMultBins}, true}; + ColumnBinningPolicy colBinning{{ConfVtxBins, ConfMultBins}}; ConfigurableAxis ConfkstarBins{"ConfkstarBins", {1500, 0., 6.}, "binning kstar"}; ConfigurableAxis ConfkTBins{"ConfkTBins", {150, 0., 9.}, "binning kT"}; diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMcTruth.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMcTruth.cxx index 0d3634683c1..297cc03cfbd 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMcTruth.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMcTruth.cxx @@ -106,7 +106,7 @@ struct FemtoUniversePairTaskTrackTrackMcTruth { ConfigurableAxis confmTBins3D{"confmTBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; ConfigurableAxis confMultBins3D{"confMultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "multiplicity Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; ConfigurableAxis confkstarBins{"confkstarBins", {1500, 0., 6.}, "binning kstar"}; ConfigurableAxis confkTBins{"confkTBins", {150, 0., 9.}, "binning kT"}; diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMultKtExtended.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMultKtExtended.cxx index 19b0e9255da..e1f2f92f773 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMultKtExtended.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMultKtExtended.cxx @@ -208,7 +208,7 @@ struct FemtoUniversePairTaskTrackTrackMultKtExtended { ConfigurableAxis confmTBins3D{"confmTBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; ConfigurableAxis confmultBins3D{"confmultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "multiplicity Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; struct : o2::framework::ConfigurableGroup { Configurable confIsCPR{"confIsCPR", true, "Close Pair Rejection"}; @@ -487,7 +487,7 @@ struct FemtoUniversePairTaskTrackTrackMultKtExtended { void init(InitContext&) { - colBinning = ColumnBinningPolicy{{confVtxBins, confMultBins}, true}; + colBinning = ColumnBinningPolicy{{confVtxBins, confMultBins}}; eventHisto.init(&qaRegistry); diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackSpherHarMultKtExtended.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackSpherHarMultKtExtended.cxx index 465df0a76a6..3f622883254 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackSpherHarMultKtExtended.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackSpherHarMultKtExtended.cxx @@ -264,8 +264,8 @@ struct FemtoUniversePairTaskTrackTrackSpherHarMultKtExtended { ConfigurableAxis confmTBins3D{"confmTBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; ConfigurableAxis confmultBins3D{"confmultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "multiplicity Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBinsCent}, true}; - ColumnBinningPolicy colBinningNtr{{confVtxBins, confMultBinsMult}, true}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBinsCent}}; + ColumnBinningPolicy colBinningNtr{{confVtxBins, confMultBinsMult}}; ConfigurableAxis confkstarBins{"confkstarBins", {60, 0.0, 0.3}, "binning kstar"}; ConfigurableAxis confkTBins{"confkTBins", {150, 0., 9.}, "binning kT"}; @@ -482,8 +482,8 @@ struct FemtoUniversePairTaskTrackTrackSpherHarMultKtExtended { void init(InitContext&) { - colBinningCent = ColumnBinningPolicy{{confVtxBins, confMultBinsCent}, true}; - colBinningNtr = ColumnBinningPolicy{{confVtxBins, confMultBinsMult}, true}; + colBinningCent = ColumnBinningPolicy{{confVtxBins, confMultBinsCent}}; + colBinningNtr = ColumnBinningPolicy{{confVtxBins, confMultBinsMult}}; eventHisto.init(&qaRegistry); trackHistoPartOne.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarBins, twotracksconfigs.confIsMC, trackonefilter.confPDGCodePartOne, true); diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackSpherHarMultKtExtendedItsPid.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackSpherHarMultKtExtendedItsPid.cxx index 1c0c0cd0472..10ababfb0a5 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackSpherHarMultKtExtendedItsPid.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackSpherHarMultKtExtendedItsPid.cxx @@ -253,8 +253,8 @@ struct FemtoUniversePairTaskTrackTrackSpherHarMultKtExtendedItsPid { ConfigurableAxis confmTBins3D{"confmTBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; ConfigurableAxis confmultBins3D{"confmultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "multiplicity Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBinsCent}, true}; - ColumnBinningPolicy colBinningNtr{{confVtxBins, confMultBinsMult}, true}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBinsCent}}; + ColumnBinningPolicy colBinningNtr{{confVtxBins, confMultBinsMult}}; ConfigurableAxis confkstarBins{"confkstarBins", {60, 0.0, 0.3}, "binning kstar"}; ConfigurableAxis confkTBins{"confkTBins", {150, 0., 9.}, "binning kT"}; @@ -477,8 +477,8 @@ struct FemtoUniversePairTaskTrackTrackSpherHarMultKtExtendedItsPid { void init(InitContext&) { - colBinningCent = ColumnBinningPolicy{{confVtxBins, confMultBinsCent}, true}; - colBinningNtr = ColumnBinningPolicy{{confVtxBins, confMultBinsMult}, true}; + colBinningCent = ColumnBinningPolicy{{confVtxBins, confMultBinsCent}}; + colBinningNtr = ColumnBinningPolicy{{confVtxBins, confMultBinsMult}}; eventHisto.init(&qaRegistry); trackHistoPartOne.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarBins, twotracksconfigs.confIsMC, trackonefilter.confPDGCodePartOne, true, std::nullopt, twotracksconfigs.confIsAddITSNsigma); diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackV0Extended.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackV0Extended.cxx index a3ec2c9f607..1e3738446d9 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackV0Extended.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackV0Extended.cxx @@ -1116,8 +1116,8 @@ struct FemtoUniversePairTaskTrackV0Extended { template void doMixedEvent(FilteredFDCollisions const& cols, PartType const& parts, PartitionType& partsOne, PartitionType& partsTwo, [[maybe_unused]] const MCParticles& mcParts = nullptr) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; std::array, 3> v0DaughPtLowTable = {{{ConfV0Selection.confLPtChildProton, ConfV0Selection.confLPtChildPion}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildProton}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildPion}}}; std::array, 3> v0DaughPtHighTable = {{{ConfV0Selection.confHPtChildProton, ConfV0Selection.confHPtChildPion}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildProton}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildPion}}}; @@ -1221,8 +1221,8 @@ struct FemtoUniversePairTaskTrackV0Extended { template void doMixedEventV0(FilteredFDCollisions const& cols, PartType const& parts, PartitionType& partsTwo, [[maybe_unused]] const MCParticles& mcParts = nullptr) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; std::array, 3> v0DaughPtLowTable = {{{ConfV0Selection.confLPtChildProton, ConfV0Selection.confLPtChildPion}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildProton}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildPion}}}; std::array, 3> v0DaughPtHighTable = {{{ConfV0Selection.confHPtChildProton, ConfV0Selection.confHPtChildPion}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildProton}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildPion}}}; @@ -1403,8 +1403,8 @@ struct FemtoUniversePairTaskTrackV0Extended { /// This function processes MC mixed events for Track - V0 void processMCMixedEvent(FilteredFDCollisions const& cols, FemtoFullParticles const& parts) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; auto mixedCollProcessFunc = [&](auto& collision1, auto& collision2) -> void { const int multCol = confUseCent ? static_cast(collision1.multV0M()) : collision1.multNtr(); @@ -1453,8 +1453,8 @@ struct FemtoUniversePairTaskTrackV0Extended { /// This function processes MC mixed events for V0 - V0 void processMCMixedEventV0(FilteredFDCollisions const& cols, FemtoFullParticles const& /*parts*/) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; auto mixedCollProcessFunc = [&](auto& collision1, auto& collision2) -> void { const int multCol = confUseCent ? static_cast(collision1.multV0M()) : collision1.multNtr(); @@ -1545,8 +1545,8 @@ struct FemtoUniversePairTaskTrackV0Extended { void processPairFractions(FilteredFDCollisions const& cols, FemtoRecoParticles const& parts, aod::FdMCParticles const& mcparts) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; std::array, 3> v0DaughPtLowTable = {{{ConfV0Selection.confLPtChildProton, ConfV0Selection.confLPtChildPion}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildProton}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildPion}}}; std::array, 3> v0DaughPtHighTable = {{{ConfV0Selection.confHPtChildProton, ConfV0Selection.confHPtChildPion}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildProton}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildPion}}}; @@ -1621,8 +1621,8 @@ struct FemtoUniversePairTaskTrackV0Extended { void processPairFractionsV0(FilteredFDCollisions const& cols, FemtoRecoParticles const& parts, aod::FdMCParticles const& mcparts) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; std::array, 3> v0DaughPtLowTable = {{{ConfV0Selection.confLPtChildProton, ConfV0Selection.confLPtChildPion}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildProton}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildPion}}}; std::array, 3> v0DaughPtHighTable = {{{ConfV0Selection.confHPtChildProton, ConfV0Selection.confHPtChildPion}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildProton}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildPion}}}; @@ -1737,8 +1737,8 @@ struct FemtoUniversePairTaskTrackV0Extended { void processPairFractionsMCTruth(FilteredFDCollisions const& cols, FemtoFullParticles const& /*parts*/) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; auto mixedCollProcessFunc = [&](auto& collision1, auto& collision2) -> void { auto groupPartsOne = partsOneMCFull->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); @@ -1774,8 +1774,8 @@ struct FemtoUniversePairTaskTrackV0Extended { void processPairFractionsMCTruthV0(FilteredFDCollisions const& cols, FemtoFullParticles const& /*parts*/) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; auto mixedCollProcessFunc = [&](auto& collision1, auto& collision2) -> void { auto groupPartsOne = partsTwoMCFull->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackV0Helicity.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackV0Helicity.cxx index e59dfd95745..f7501d0388c 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackV0Helicity.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackV0Helicity.cxx @@ -866,8 +866,8 @@ struct FemtoUniversePairTaskTrackV0Helicity { template void doMixedEvent(FilteredFDCollisions const& cols, PartType const& parts, PartitionType& partitionOne, PartitionType& partitionTwo, int helRange, [[maybe_unused]] const MCParticles& mcParts = nullptr) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; auto mixedCollProcessFunc = [&](auto& collision1, auto& collision2) -> void { const int multCol = confUseCent ? collision1.multV0M() : collision1.multNtr(); @@ -1000,8 +1000,8 @@ struct FemtoUniversePairTaskTrackV0Helicity { /// This function processes the mixed event for V0 - V0 void processMixedEventV0(FilteredFDCollisions const& cols, FemtoFullParticles const& parts) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; auto mixedCollProcessFunc = [&](auto& collision1, auto& collision2) -> void { const int multCol = confUseCent ? collision1.multV0M() : collision1.multNtr(); @@ -1068,8 +1068,8 @@ struct FemtoUniversePairTaskTrackV0Helicity { /// This function processes MC mixed events for Track - V0 void processMCMixedEvent(FilteredFDCollisions const& cols, FemtoFullParticles const& parts) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; auto mixedCollProcessFunc = [&](auto& collision1, auto& collision2) -> void { const int multCol = confUseCent ? collision1.multV0M() : collision1.multNtr(); @@ -1116,8 +1116,8 @@ struct FemtoUniversePairTaskTrackV0Helicity { /// This function processes MC mixed events for V0 - V0 void processMCMixedEventV0(FilteredFDCollisions const& cols, FemtoFullParticles const& /*parts*/) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; auto mixedCollProcessFunc = [&](auto& collision1, auto& collision2) -> void { const int multCol = confUseCent ? collision1.multV0M() : collision1.multNtr(); diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskV0CascadeExtended.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskV0CascadeExtended.cxx index 45d430a626a..47666260743 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskV0CascadeExtended.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskV0CascadeExtended.cxx @@ -381,7 +381,7 @@ struct FemtoUniversePairTaskV0CascadeExtended { template void doMixedEvent(const FilteredFDCollisions& cols, const TableType& parts, PartitionType& partsOne, PartitionType& partsTwo) { - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; for (const auto& [collision1, collision2] : soa::selfCombinations(colBinning, 5, -1, cols, cols)) { const int multCol = collision1.multNtr(); diff --git a/PWGCF/FemtoWorld/Core/FemtoWorldTrackSelection.h b/PWGCF/FemtoWorld/Core/FemtoWorldTrackSelection.h index 360587d82ef..69e16841ce0 100644 --- a/PWGCF/FemtoWorld/Core/FemtoWorldTrackSelection.h +++ b/PWGCF/FemtoWorld/Core/FemtoWorldTrackSelection.h @@ -96,7 +96,7 @@ class FemtoWorldTrackSelection : public FemtoWorldObjectSelection void init(o2::framework::HistogramRegistry* registry); @@ -472,9 +472,7 @@ std::array FemtoWorldV0Selection::getCutContainer(C const& sign = -1.; } else if (abs(nSigmaPrPos) < nSigmaPIDMax && abs(nSigmaPiNeg) < nSigmaPIDMax && diffAntiLambda < diffLambda) { sign = 1.; - } - // if it happens that none of these are true, ignore the invariant mass - else { + } else { // if it happens that none of these are true, ignore the invariant mass if (abs(nSigmaPrNeg) < nSigmaPIDMax && abs(nSigmaPiPos) < nSigmaPIDMax) { sign = -1.; } else if (abs(nSigmaPrPos) < nSigmaPIDMax && abs(nSigmaPiNeg) < nSigmaPIDMax) { diff --git a/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerReducedTask.cxx b/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerReducedTask.cxx index 28ef5d0ea9e..0a0ba927a22 100644 --- a/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerReducedTask.cxx +++ b/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerReducedTask.cxx @@ -37,6 +37,8 @@ #include #include +#include + using namespace o2; using namespace o2::analysis::femtoWorld; using namespace o2::framework; @@ -129,7 +131,7 @@ struct femtoWorldProducerReducedTask { ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); - long now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + int64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); ccdb->setCreatedNotAfter(now); } diff --git a/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerTask.cxx b/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerTask.cxx index 0460e749cb0..78758a6c380 100644 --- a/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerTask.cxx +++ b/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerTask.cxx @@ -240,7 +240,6 @@ struct femtoWorldProducerTask { int mRunNumber; float mMagField; Service ccdb; /// Accessing the CCDB - HfHelper hfHelper; void init(InitContext&) { @@ -1168,7 +1167,7 @@ struct femtoWorldProducerTask { if (!(candidate.hfflag() & 1 << aod::hf_cand_2prong::DecayType::D0ToPiK)) { continue; } - if (config.yCandMax >= 0. && std::abs(hfHelper.yD0(candidate)) > config.yCandMax) { + if (config.yCandMax >= 0. && std::abs(HfHelper::yD0(candidate)) > config.yCandMax) { continue; } @@ -1297,11 +1296,11 @@ struct femtoWorldProducerTask { candidate.eta(), candidate.phi(), candidate.p(), - 0., // general mass - hfHelper.invMassD0ToPiK(candidate), // D0mass - hfHelper.invMassD0barToKPi(candidate), // D0bar mass - candidate.isSelD0(), // D0 flag - candidate.isSelD0bar(), // D0bar flag + 0., // general mass + HfHelper::invMassD0ToPiK(candidate), // D0mass + HfHelper::invMassD0barToKPi(candidate), // D0bar mass + candidate.isSelD0(), // D0 flag + candidate.isSelD0bar(), // D0bar flag aod::femtoworldparticle::ParticleType::kD0D0bar, 0., // cutContainerV0.at(femtoWorldV0Selection::V0ContainerPosition::kV0), 0, @@ -2115,7 +2114,7 @@ struct femtoWorldProducerTask { if (!(candidate.hfflag() & 1 << aod::hf_cand_2prong::DecayType::D0ToPiK)) { continue; } - if (config.yCandMax >= 0. && std::abs(hfHelper.yD0(candidate)) > config.yCandMax) { + if (config.yCandMax >= 0. && std::abs(HfHelper::yD0(candidate)) > config.yCandMax) { continue; } @@ -2246,11 +2245,11 @@ struct femtoWorldProducerTask { candidate.eta(), candidate.phi(), candidate.p(), - 0., // general mass - hfHelper.invMassD0ToPiK(candidate), // D0mass - hfHelper.invMassD0barToKPi(candidate), // D0bar mass - candidate.isSelD0(), // D0 flag - candidate.isSelD0bar(), // D0bar flag + 0., // general mass + HfHelper::invMassD0ToPiK(candidate), // D0mass + HfHelper::invMassD0barToKPi(candidate), // D0bar mass + candidate.isSelD0(), // D0 flag + candidate.isSelD0bar(), // D0bar flag aod::femtoworldparticle::ParticleType::kD0D0bar, 0., // cutContainerV0.at(femtoWorldV0Selection::V0ContainerPosition::kV0), 0, diff --git a/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerTaskV0Only.cxx b/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerTaskV0Only.cxx index 091afad61ba..72e78653f44 100644 --- a/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerTaskV0Only.cxx +++ b/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerTaskV0Only.cxx @@ -35,9 +35,12 @@ #include #include -#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include #include +#include + using namespace o2; using namespace o2::analysis::femtoWorld; using namespace o2::framework; @@ -180,7 +183,7 @@ struct femtoWorldProducerTaskV0Only { ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); - long now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + int64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); ccdb->setCreatedNotAfter(now); } diff --git a/PWGCF/FemtoWorld/Tasks/femtoPairLambdaAntilambda.cxx b/PWGCF/FemtoWorld/Tasks/femtoPairLambdaAntilambda.cxx index 97f8b7ec190..3fd9e35e397 100644 --- a/PWGCF/FemtoWorld/Tasks/femtoPairLambdaAntilambda.cxx +++ b/PWGCF/FemtoWorld/Tasks/femtoPairLambdaAntilambda.cxx @@ -112,12 +112,18 @@ struct FemtoPairLambdaAntilambda { paircleaner::ConfPairCleanerBinning confPairCleaner; pairbuilder::PairV0V0Builder< - v0histmanager::PrefixLambda1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, - v0histmanager::PrefixLambda2, - trackhistmanager::PrefixV02PosDaughter, - trackhistmanager::PrefixV02NegDaughter, + v0histmanager::PrefixLambda1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + v0histmanager::PrefixLambda2Se, + trackhistmanager::PrefixV02PosDaughterSe, + trackhistmanager::PrefixV02NegDaughterSe, + v0histmanager::PrefixLambda1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + v0histmanager::PrefixLambda2Me, + trackhistmanager::PrefixV02PosDaughterMe, + trackhistmanager::PrefixV02NegDaughterMe, pairhistmanager::PrefixV0V0Se, pairhistmanager::PrefixV0V0Me, closepairrejection::PrefixV0V0PosSe, @@ -129,12 +135,18 @@ struct FemtoPairLambdaAntilambda { pairLambdaLambdaBuilder; pairbuilder::PairV0V0Builder< - v0histmanager::PrefixK0short1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, - v0histmanager::PrefixK0short2, - trackhistmanager::PrefixV02PosDaughter, - trackhistmanager::PrefixV02NegDaughter, + v0histmanager::PrefixK0short1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + v0histmanager::PrefixK0short2Se, + trackhistmanager::PrefixV02PosDaughterSe, + trackhistmanager::PrefixV02NegDaughterSe, + v0histmanager::PrefixK0short1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + v0histmanager::PrefixK0short2Me, + trackhistmanager::PrefixV02PosDaughterMe, + trackhistmanager::PrefixV02NegDaughterMe, pairhistmanager::PrefixV0V0Se, pairhistmanager::PrefixV0V0Me, closepairrejection::PrefixV0V0PosSe, @@ -149,9 +161,9 @@ struct FemtoPairLambdaAntilambda { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; pairhistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoV0V0", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; @@ -173,9 +185,9 @@ struct FemtoPairLambdaAntilambda { // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; // setup histograms std::map> colHistSpec; diff --git a/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskPionPion.cxx b/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskPionPion.cxx index 5dd434d6b97..40ddbac7a6d 100644 --- a/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskPionPion.cxx +++ b/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskPionPion.cxx @@ -157,7 +157,7 @@ struct femtoWorldPairTaskPionPion { ConfigurableAxis CfgMultBins{"CfgMultBins", {VARIABLE_WIDTH, 0.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; ConfigurableAxis CfgVtxBins{"CfgVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}, true}; + ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}}; ConfigurableAxis CfgkstarBins{"CfgkstarBins", {1500, 0., 6.}, "binning kstar"}; ConfigurableAxis CfgkTBins{"CfgkTBins", {150, 0., 9.}, "binning kT"}; diff --git a/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskProtonAntiproton.cxx b/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskProtonAntiproton.cxx index 6ded175155f..44e36a37e98 100644 --- a/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskProtonAntiproton.cxx +++ b/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskProtonAntiproton.cxx @@ -153,7 +153,7 @@ struct femtoWorldPairTaskProtonAntiproton { ConfigurableAxis CfgMultBins{"CfgMultBins", {VARIABLE_WIDTH, 0.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; ConfigurableAxis CfgVtxBins{"CfgVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}, true}; + ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}}; ConfigurableAxis CfgkstarBins{"CfgkstarBins", {1500, 0., 6.}, "binning kstar"}; ConfigurableAxis CfgkTBins{"CfgkTBins", {150, 0., 9.}, "binning kT"}; diff --git a/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackD0.cxx b/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackD0.cxx index 5ca9109e6e7..1357578f0fa 100644 --- a/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackD0.cxx +++ b/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackD0.cxx @@ -367,7 +367,7 @@ struct femtoWorldPairTaskTrackD0 { void processMixedEvent(o2::aod::FemtoWorldCollisions& cols, o2::aod::FemtoWorldParticles&) { - ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}, true}; + ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}}; for (auto& [collision1, collision2] : soa::selfCombinations(colBinning, ConfNEventsMix, -1, cols, cols)) { diff --git a/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackPhi.cxx b/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackPhi.cxx index 426ca4af935..c339defa68e 100644 --- a/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackPhi.cxx +++ b/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackPhi.cxx @@ -327,7 +327,7 @@ struct femtoWorldPairTaskTrackPhi { void processMixedEvent(o2::aod::FemtoWorldCollisions& cols, o2::aod::FemtoWorldParticles&) { - ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}, true}; + ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}}; for (auto& [collision1, collision2] : soa::selfCombinations(colBinning, ConfNEventsMix, -1, cols, cols)) { diff --git a/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackTrack.cxx b/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackTrack.cxx index 1711a4fdc48..b8e514a06fe 100644 --- a/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackTrack.cxx +++ b/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackTrack.cxx @@ -156,7 +156,7 @@ struct femtoWorldPairTaskTrackTrack { ConfigurableAxis CfgMultBins{"CfgMultBins", {VARIABLE_WIDTH, 0.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; ConfigurableAxis CfgVtxBins{"CfgVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}, true}; + ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}}; ConfigurableAxis CfgkstarBins{"CfgkstarBins", {1500, 0., 6.}, "binning kstar"}; ConfigurableAxis CfgkTBins{"CfgkTBins", {150, 0., 9.}, "binning kT"}; @@ -214,32 +214,39 @@ struct femtoWorldPairTaskTrackTrack { if (mom > 0.5) { // if (TMath::Hypot( nsigmaTOFP, nsigmaTPCP )/TMath::Sqrt(2) < 3.0) if (mom < 2.0) { - if (TMath::Hypot(nsigmaTOFK, nsigmaTPCK) < fNsigma) + if (TMath::Hypot(nsigmaTOFK, nsigmaTPCK) < fNsigma) { return true; - } else if (TMath::Hypot(nsigmaTOFK, nsigmaTPCK) < fNsigma2) + } + } else if (TMath::Hypot(nsigmaTOFK, nsigmaTPCK) < fNsigma2) { return true; + } } else { - if (TMath::Abs(nsigmaTPCK) < fNsigma) + if (TMath::Abs(nsigmaTPCK) < fNsigma) { return true; + } } } else { - if (mom < 0.4) { if (nsigmaTOFK < -999.) { - if (TMath::Abs(nsigmaTPCK) < 2.0) + if (TMath::Abs(nsigmaTPCK) < 2.0) { return true; - } else if (TMath::Abs(nsigmaTOFK) < 3.0 && TMath::Abs(nsigmaTPCK) < 3.0) + } + } else if (TMath::Abs(nsigmaTOFK) < 3.0 && TMath::Abs(nsigmaTPCK) < 3.0) { return true; + } } else if (mom >= 0.4 && mom <= 0.6) { if (nsigmaTOFK < -999.) { - if (TMath::Abs(nsigmaTPCK) < 2.0) + if (TMath::Abs(nsigmaTPCK) < 2.0) { return true; - } else if (TMath::Abs(nsigmaTOFK) < 3.0 && TMath::Abs(nsigmaTPCK) < 3.0) + } + } else if (TMath::Abs(nsigmaTOFK) < 3.0 && TMath::Abs(nsigmaTPCK) < 3.0) { return true; + } } else if (nsigmaTOFK < -999.) { return false; - } else if (TMath::Abs(nsigmaTOFK) < 3.0 && TMath::Abs(nsigmaTPCK) < 3.0) + } else if (TMath::Abs(nsigmaTOFK) < 3.0 && TMath::Abs(nsigmaTPCK) < 3.0) { return true; + } } return false; } diff --git a/PWGCF/FemtoWorld/Tasks/femtoWorldPionAllPairTask.cxx b/PWGCF/FemtoWorld/Tasks/femtoWorldPionAllPairTask.cxx index 248225ecd46..95bd3db42ec 100644 --- a/PWGCF/FemtoWorld/Tasks/femtoWorldPionAllPairTask.cxx +++ b/PWGCF/FemtoWorld/Tasks/femtoWorldPionAllPairTask.cxx @@ -115,8 +115,8 @@ struct FemtoWorldIdenticalPionPair { // Configurable> CfgVtxBins{"CfgVtxBins",{-10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f},"Mixing bins - z-vertex"}; // Configurable> CfgCentBins{"CfgCentBins",{5.0f,10.0f,20.0f,30.0f,40.0f,50.0f},"Centrality Bins"}; - ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}, true}; - ColumnBinningPolicy colBinning2{{CfgVtxBins, CfgCentBinsMixing}, true}; + ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}}; + ColumnBinningPolicy colBinning2{{CfgVtxBins, CfgCentBinsMixing}}; ConfigurableAxis CfgkstarBins{"CfgkstarBins", {1500, 0., 6.}, "binning kstar"}; ConfigurableAxis CfgkTBins{"CfgkTBins", {150, 0., 9.}, "binning kT"}; diff --git a/PWGCF/FemtoWorld/Tasks/femtoWorldPionPairTaskTrackTrack.cxx b/PWGCF/FemtoWorld/Tasks/femtoWorldPionPairTaskTrackTrack.cxx index fdedf6e0870..200ea018be9 100644 --- a/PWGCF/FemtoWorld/Tasks/femtoWorldPionPairTaskTrackTrack.cxx +++ b/PWGCF/FemtoWorld/Tasks/femtoWorldPionPairTaskTrackTrack.cxx @@ -157,7 +157,7 @@ struct femtoWorldPionPairTaskTrackTrack { ConfigurableAxis CfgMultBins{"CfgMultBins", {VARIABLE_WIDTH, 0.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; ConfigurableAxis CfgVtxBins{"CfgVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}, true}; + ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}}; ConfigurableAxis CfgkstarBins{"CfgkstarBins", {1500, 0., 6.}, "binning kstar"}; ConfigurableAxis CfgkTBins{"CfgkTBins", {150, 0., 9.}, "binning kT"}; diff --git a/PWGCF/Flow/TableProducer/zdcQVectors.cxx b/PWGCF/Flow/TableProducer/zdcQVectors.cxx index 9c1cce63a0b..0aade4713d6 100644 --- a/PWGCF/Flow/TableProducer/zdcQVectors.cxx +++ b/PWGCF/Flow/TableProducer/zdcQVectors.cxx @@ -111,7 +111,7 @@ struct ZdcQVectors { } EvSel; struct : ConfigurableGroup { - O2_DEFINE_CONFIGURABLE(cfgRecenterForTimestamp, bool, false, "Add 1D recentering for timestamp"); + O2_DEFINE_CONFIGURABLE(cfgCCDBDir_RecenterForTimestamp, bool, false, "Add 1D recentering for timestamp"); O2_DEFINE_CONFIGURABLE(cfgCCDBdir_Timestamp, std::string, "Users/c/ckoster/ZDC/LHC23_PbPb_pass5/Timestamp", "CCDB directory for Timestamp recentering"); } extraTS; @@ -131,14 +131,12 @@ struct ZdcQVectors { O2_DEFINE_CONFIGURABLE(cfgVtxZ, float, 10.0f, "Accepted z-vertex range") O2_DEFINE_CONFIGURABLE(cfgMagField, float, 99999, "Configurable magnetic field; default CCDB will be queried") - O2_DEFINE_CONFIGURABLE(cfgEnergyCal, std::string, "Users/c/ckoster/ZDC/LHC23_PbPb_pass5/Energy", "ccdb path for energy calibration histos") - O2_DEFINE_CONFIGURABLE(cfgMeanv, std::string, "Users/c/ckoster/ZDC/LHC23_PbPb_pass5/vmean", "ccdb path for mean v histos") O2_DEFINE_CONFIGURABLE(cfgMinEntriesSparseBin, int, 1000, "Minimal number of entries allowed in 4D recentering histogram to use for recentering.") - O2_DEFINE_CONFIGURABLE(cfgRec, std::string, "Users/c/ckoster/ZDC/LHC23_PbPb_pass5", "ccdb path for recentering histos"); O2_DEFINE_CONFIGURABLE(cfgFillHistRegistry, bool, true, "Fill common registry with histograms"); O2_DEFINE_CONFIGURABLE(cfgFillCutAnalysis, bool, true, "Fill cut analysis with histograms"); O2_DEFINE_CONFIGURABLE(cfgFillNothing, bool, false, "Disable ALL Histograms -> ONLY use to reduce memory"); O2_DEFINE_CONFIGURABLE(cfgNoGain, bool, true, "Do not apply gain correction to ZDC energy calibration"); + O2_DEFINE_CONFIGURABLE(cfgNIterationsAfterShift, int, 1, "Number of iterations to perform after shift"); O2_DEFINE_CONFIGURABLE(cfgTrackSelsDCAxy, float, 0.2, "Cut on DCA in the transverse direction (cm)"); O2_DEFINE_CONFIGURABLE(cfgTrackSelsDCAz, float, 0.2, "Cut on DCA in the longitudinal direction (cm)"); @@ -146,11 +144,19 @@ struct ZdcQVectors { O2_DEFINE_CONFIGURABLE(cfgTrackSelsPtmax, float, 10, "maximum pt (GeV/c)"); O2_DEFINE_CONFIGURABLE(cfgTrackSelsEta, float, 0.8, "eta cut"); + O2_DEFINE_CONFIGURABLE(cfgCCDBDir_EnergyCal, std::string, "Users/c/ckoster/ZDC/LHC23_PbPb_pass5/Energy", "ccdb path for energy calibration histos") + O2_DEFINE_CONFIGURABLE(cfgCCDBDir_Meanv, std::string, "Users/c/ckoster/ZDC/LHC23_PbPb_pass5/vmean", "ccdb path for mean v histos") + O2_DEFINE_CONFIGURABLE(cfgCCDBDir_Rec, std::string, "Users/c/ckoster/ZDC/LHC23_PbPb_pass5", "ccdb path for recentering histos"); O2_DEFINE_CONFIGURABLE(cfgCCDBdir_Shift, std::string, "Users/c/ckoster/ZDC/LHC23_PbPb_pass5/Shift", "CCDB directory for Shift ZDC"); + O2_DEFINE_CONFIGURABLE(cfgCCDBdir_ShiftRec, std::string, "Users/c/ckoster/ZDC/LHC23_PbPb_pass5/noGain/AfterShift", "CCDB directory for recentering after Shift ZDC"); + Configurable> cfgSelVec{"cfgSelVec", std::vector{1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1}, "Put 1 for every event selection from SelectionCriteria that is used in flowSP"}; Configurable> cfgEvSelsMultPv{"cfgEvSelsMultPv", std::vector{2223.49, -75.1444, 0.963572, -0.00570399, 1.34877e-05, 3790.99, -137.064, 2.13044, -0.017122, 5.82834e-05}, "Multiplicity cuts (PV) first 5 parameters cutLOW last 5 cutHIGH (Default is +-2sigma pass5) "}; Configurable> cfgEvSelsMult{"cfgEvSelsMult", std::vector{1301.56, -41.4615, 0.478224, -0.00239449, 4.46966e-06, 2967.6, -102.927, 1.47488, -0.0106534, 3.28622e-05}, "Multiplicity cuts (Global) first 5 parameters cutLOW last 5 cutHIGH (Default is +-2sigma pass5) "}; + // Track selection DCA cut + std::unique_ptr fDCACut = std::make_unique("fDCACut", "0.0105 + 0.035 / TMath::Power(x,1.1)", 0, 100); + // define my..... // Filter collisionFilter = nabs(aod::collision::posZ) <; @@ -185,6 +191,7 @@ struct ZdcQVectors { kMeanv, kRec, kTimestamp, + kRecShift, nCalibModes }; @@ -204,10 +211,8 @@ struct ZdcQVectors { // keep track of calibration histos for each given step and iteration struct Calib { - std::vector calibList = std::vector(4, nullptr); // [0] Enerfy cal, [1] vmean, [2] recentering, [3] timestamp - std::vector calibfilesLoaded = std::vector(4, false); - int atStep = 0; - int atIteration = 0; + std::vector calibList = std::vector(5, nullptr); // [0] Enerfy cal, [1] vmean, [2] recentering, [3] timestamp, [4] recenter after shift Correction + std::vector calibfilesLoaded = std::vector(5, false); TProfile3D* shiftprofileC = nullptr; TProfile3D* shiftprofileA = nullptr; @@ -220,11 +225,13 @@ struct ZdcQVectors { uint64_t timestamp = 0; std::vector v = {0, 0, 0}; bool isSelected = 0; + int atIteration = 0; } cal; enum FillType { kBefore, - kAfter + kAfter, + kAfterShift }; void init(InitContext const&) @@ -270,6 +277,9 @@ struct ZdcQVectors { registry.add(Form("QA/before/hSPplaneA"), "hSPplaneA", kTH2D, {axisPsiA, axisCent}); registry.add(Form("QA/before/hSPplaneC"), "hSPplaneC", kTH2D, {axisPsiC, axisCent}); registry.add(Form("QA/before/hSPplaneFull"), "hSPplaneFull", kTH2D, {{100, -PI, PI}, axisCent}); + if (!cfgCCDBdir_ShiftRec.value.empty()) { + registry.addClone("QA/before/", "QA/afterShift/"); + } for (const auto& side : sides) { registry.add(Form("recentering/before/hZN%s_Qx_vs_Qy", side), Form("hZN%s_Qx_vs_Qy", side), kTH2F, {axisQ, axisQ}); } @@ -294,7 +304,7 @@ struct ZdcQVectors { registry.add(Form("recentering/before/hQ%s%s_vs_vy", coord, side), Form("hQ%s%s_vs_vy", coord, side), {HistType::kTProfile, {axisVy}}); registry.add(Form("recentering/before/hQ%s%s_vs_vz", coord, side), Form("hQ%s%s_vs_vz", coord, side), {HistType::kTProfile, {axisVz}}); registry.add(Form("recentering/before/hQ%s%s_vs_timestamp", coord, side), Form("hQ%s%s_vs_timestamp", coord, side), {HistType::kTProfile, {axisTimestamp}}); - registry.add(Form("recentering/Q%s%s_vs_iteration", coord, side), Form("hQ%s%s_vs_iteration", coord, side), {HistType::kTH2D, {{35, 0, 35}, axisQ}}); + registry.add(Form("recentering/Q%s%s_vs_iteration", coord, side), Form("hQ%s%s_vs_iteration", coord, side), {HistType::kTH2D, {{100, 0, 100}, axisQ}}); } // end of capCOORDS } // end of sides @@ -377,6 +387,10 @@ struct ZdcQVectors { registry.add("CutAnalysis/hvertex_vy", "hvertex_vy", kTProfile2D, {{1, 0., 1.}, {nEventSelections + 5, 0, nEventSelections + 5}}); registry.add("CutAnalysis/hvertex_vz", "hvertex_vz", kTProfile2D, {{1, 0., 1.}, {nEventSelections + 5, 0, nEventSelections + 5}}); } + + if (!cfgCCDBdir_ShiftRec.value.empty()) { + registry.addClone("recentering/before/", "recentering/afterShift/"); + } registry.addClone("recentering/before/", "recentering/after/"); registry.addClone("QA/before/", "QA/after/"); } @@ -614,7 +628,7 @@ struct ZdcQVectors { if (cfgFillNothing) { return; } - static constexpr std::array Time = {"before", "after"}; // todo move to struct like in flowSP + static constexpr std::array Time = {"before", "after", "afterShift"}; registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hZNA_Qx_vs_Qy"), qxa, qya); registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hZNC_Qx_vs_Qy"), qxc, qyc); @@ -675,11 +689,11 @@ struct ZdcQVectors { registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/ZNC_Qy_vs_Centrality"), centrality, qyc); // add psi!! - double psiA = 1.0 * std::atan2(qxc, qxa); + double psiA = 1.0 * std::atan2(qya, qxa); registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hSPplaneA"), psiA, centrality, 1); - double psiC = 1.0 * std::atan2(qyc, qya); + double psiC = 1.0 * std::atan2(qyc, qxc); registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hSPplaneC"), psiC, centrality, 1); - double psiFull = 1.0 * std::atan2(qxc + qyc, qxa + qya); + double psiFull = 1.0 * std::atan2(qya + qyc, qxa + qxc); registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hSPplaneFull"), psiFull, centrality, 1); } @@ -698,7 +712,6 @@ struct ZdcQVectors { cal.calibfilesLoaded[cm] = true; LOGF(info, "Loaded calibration histos from %s", ccdb_dir.c_str()); if (cm == kRec) { - cal.atStep = 5; cal.atIteration = 5; } } @@ -722,7 +735,7 @@ struct ZdcQVectors { if (!hist) { LOGF(fatal, "No calibration histo for iteration %i and step %i -> %s", iteration, step, objName); } - } else if (cm == kRec) { + } else if (cm == kRec || cm == kRecShift) { auto list = dynamic_cast(cal.calibList[cm]->FindObject(Form("it%i_step%i", iteration, step))); if (!list) { LOGF(fatal, "No calibration list for iteration %i and step %i", iteration, step); @@ -731,8 +744,6 @@ struct ZdcQVectors { if (!hist) { LOGF(fatal, "No calibration histo for iteration %i and step %i -> %s", iteration, step, objName); } - cal.atStep = step; - cal.atIteration = iteration; } if (!hist) { LOGF(fatal, "%s not available.. Abort..", objName); @@ -768,7 +779,7 @@ struct ZdcQVectors { bin = h->GetXaxis()->FindBin(TString::Format("%i", cal.runnumber)); } if (name.Contains("timestamp")) { - bin = h->GetXaxis()->FindBin(cal.timestamp); + bin = h->GetXaxis()->FindBin(cal.rsTimestamp); } calibConstant = h->GetBinContent(bin); } @@ -817,7 +828,6 @@ struct ZdcQVectors { std::vector cents; auto cent = collision.centFT0C(); - cents.push_back(collision.centFT0C()); if (cfgFT0Cvariant1) { @@ -853,6 +863,30 @@ struct ZdcQVectors { cal.runnumber = runnumber; cal.centrality = cent; + // load new calibrations for new runs only + if (runnumber != cal.lastRunNumber) { + cal.calibfilesLoaded[kEnergyCal] = false; + cal.calibList[kEnergyCal] = nullptr; + + cal.calibfilesLoaded[kMeanv] = false; + cal.calibList[kMeanv] = nullptr; + + cal.calibfilesLoaded[kRec] = false; + cal.calibList[kRec] = nullptr; + + cal.calibfilesLoaded[kTimestamp] = false; + cal.calibList[kTimestamp] = nullptr; + + cal.calibfilesLoaded[kRecShift] = false; + cal.calibList[kRecShift] = nullptr; + + cal.isShiftProfileFound = false; + cal.shiftprofileC = nullptr; + cal.shiftprofileA = nullptr; + + cal.atIteration = 0; + } + if (cfgFillHistRegistry && !cfgFillNothing) { registry.fill(HIST("QA/centrality_before"), cent); } @@ -891,16 +925,20 @@ struct ZdcQVectors { bool isZNChit = true; for (int i = 0; i < nTowers; ++i) { - if (i < nTowersPerSide && eZN[i] <= 0) + if (i < nTowersPerSide && eZN[i] <= 0) { isZNAhit = false; - if (i >= nTowersPerSide && eZN[i] <= 0) + } + if (i >= nTowersPerSide && eZN[i] <= 0) { isZNChit = false; + } } - if (zdcCol.energyCommonZNA() <= 0) + if (zdcCol.energyCommonZNA() <= 0) { isZNAhit = false; - if (zdcCol.energyCommonZNC() <= 0) + } + if (zdcCol.energyCommonZNC() <= 0) { isZNChit = false; + } // if ZNA or ZNC not hit correctly.. do not use event in q-vector calculation if (!isZNAhit || !isZNChit) { @@ -927,32 +965,13 @@ struct ZdcQVectors { } registry.fill(HIST("hEventCount"), evSel_CentCuts); - // load new calibrations for new runs only - if (runnumber != cal.lastRunNumber) { - cal.calibfilesLoaded[kEnergyCal] = false; - cal.calibList[kEnergyCal] = nullptr; - - cal.calibfilesLoaded[kMeanv] = false; - cal.calibList[kMeanv] = nullptr; - - cal.calibfilesLoaded[kRec] = false; - cal.calibList[kRec] = nullptr; - - cal.calibfilesLoaded[kTimestamp] = false; - cal.calibList[kTimestamp] = nullptr; - - cal.isShiftProfileFound = false; - cal.shiftprofileC = nullptr; - cal.shiftprofileA = nullptr; - } - // load the calibration histos for iteration 0 step 0 (Energy Calibration) if (!cfgNoGain) { - loadCalibrations(cfgEnergyCal.value, timestamp); + loadCalibrations(cfgCCDBDir_EnergyCal.value, timestamp); } // load the calibrations for the mean v - loadCalibrations(cfgMeanv.value, timestamp); + loadCalibrations(cfgCCDBDir_Meanv.value, timestamp); if (!cfgFillNothing && isEventSelected) { registry.get(HIST("vmean/hvertex_vx"))->Fill(Form("%d", runnumber), v[0]); @@ -1074,17 +1093,25 @@ struct ZdcQVectors { return; } - loadCalibrations(cfgRec.value, timestamp); + // Load in TList with all recentering histos + loadCalibrations(cfgCCDBDir_Rec.value, timestamp); - if (extraTS.cfgRecenterForTimestamp) { + // If CCDB dir given load recentering for extra step Timestamp + if (extraTS.cfgCCDBDir_RecenterForTimestamp) { loadCalibrations(extraTS.cfgCCDBdir_Timestamp.value, timestamp); } + // If CCDB dir given load recentering after shift correction. + if (!cfgCCDBdir_ShiftRec.value.empty()) { + loadCalibrations(cfgCCDBdir_ShiftRec.value, timestamp); + } + std::array qRec(q); if (cal.atIteration == 0) { - if (cal.isSelected && cfgFillHistRegistry && isEventSelected) + if (cal.isSelected && cfgFillHistRegistry && isEventSelected) { fillCommonRegistry(q[0], q[1], q[2], q[3], cal.v, cent, rsTimestamp); + } spTableZDC(runnumber, cents, cal.v, foundBC.timestamp(), q[0], q[1], q[2], q[3], cal.isSelected, eventSelectionFlags); cal.lastRunNumber = runnumber; @@ -1136,7 +1163,7 @@ struct ZdcQVectors { pb++; } - if (extraTS.cfgRecenterForTimestamp) { + if (extraTS.cfgCCDBDir_RecenterForTimestamp) { corrQxA.push_back(getCorrection(namesTS[0].Data(), it, 6)); corrQyA.push_back(getCorrection(namesTS[1].Data(), it, 6)); corrQxC.push_back(getCorrection(namesTS[2].Data(), it, 6)); @@ -1257,20 +1284,87 @@ struct ZdcQVectors { double qXcShift = std::hypot(qRec[2], qRec[3]) * std::cos(psiZDCCshift); double qYcShift = std::hypot(qRec[2], qRec[3]) * std::sin(psiZDCCshift); + if (!cal.calibfilesLoaded[kRecShift]) { + if (cal.isSelected && cfgFillHistRegistry && !cfgFillNothing && isEventSelected) { + fillCommonRegistry(qXaShift, qYaShift, qXcShift, qYcShift, cal.v, cent, rsTimestamp); + registry.fill(HIST("QA/centrality_after"), cent); + registry.get(HIST("QA/after/ZNA_Qx"))->Fill(Form("%d", runnumber), qXaShift); + registry.get(HIST("QA/after/ZNA_Qy"))->Fill(Form("%d", runnumber), qYaShift); + registry.get(HIST("QA/after/ZNC_Qx"))->Fill(Form("%d", runnumber), qXcShift); + registry.get(HIST("QA/after/ZNC_Qy"))->Fill(Form("%d", runnumber), qYcShift); + } + + spTableZDC(runnumber, cents, cal.v, foundBC.timestamp(), qXaShift, qYaShift, qXcShift, qYcShift, cal.isSelected, eventSelectionFlags); + qRec = {0, 0, 0, 0}; + + cal.lastRunNumber = runnumber; + return; + } + // vector of 4 + corrQxA.clear(); + corrQyA.clear(); + corrQxC.clear(); + corrQyC.clear(); + if (cal.isSelected && cfgFillHistRegistry && !cfgFillNothing && isEventSelected) { fillCommonRegistry(qXaShift, qYaShift, qXcShift, qYcShift, cal.v, cent, rsTimestamp); - registry.fill(HIST("QA/centrality_after"), cent); - registry.get(HIST("QA/after/ZNA_Qx"))->Fill(Form("%d", runnumber), qXaShift); - registry.get(HIST("QA/after/ZNA_Qy"))->Fill(Form("%d", runnumber), qYaShift); - registry.get(HIST("QA/after/ZNC_Qx"))->Fill(Form("%d", runnumber), qXcShift); - registry.get(HIST("QA/after/ZNC_Qy"))->Fill(Form("%d", runnumber), qYcShift); } - spTableZDC(runnumber, cents, cal.v, foundBC.timestamp(), qXaShift, qYaShift, qXcShift, qYcShift, cal.isSelected, eventSelectionFlags); - qRec = {0, 0, 0, 0}; + for (int it = 1; it <= cfgNIterationsAfterShift; it++) { + corrQxA.push_back(getCorrection(names[0][0].Data(), it, 1)); + corrQyA.push_back(getCorrection(names[0][1].Data(), it, 1)); + corrQxC.push_back(getCorrection(names[0][2].Data(), it, 1)); + corrQyC.push_back(getCorrection(names[0][3].Data(), it, 1)); - cal.lastRunNumber = runnumber; - return; + if (cfgFillHistRegistry && !cfgFillNothing && isEventSelected) { + registry.get(HIST("recentering/QXA_vs_iteration"))->Fill(pb + 1, qXaShift - std::accumulate(corrQxA.begin(), corrQxA.end(), 0.0)); + registry.get(HIST("recentering/QYA_vs_iteration"))->Fill(pb + 1, qYaShift - std::accumulate(corrQyA.begin(), corrQyA.end(), 0.0)); + registry.get(HIST("recentering/QXC_vs_iteration"))->Fill(pb + 1, qXcShift - std::accumulate(corrQxC.begin(), corrQxC.end(), 0.0)); + registry.get(HIST("recentering/QYC_vs_iteration"))->Fill(pb + 1, qYcShift - std::accumulate(corrQyC.begin(), corrQyC.end(), 0.0)); + } + pb++; + + for (int step = 2; step <= nSteps; step++) { + corrQxA.push_back(getCorrection(names[step - 1][0].Data(), it, step)); + corrQyA.push_back(getCorrection(names[step - 1][1].Data(), it, step)); + corrQxC.push_back(getCorrection(names[step - 1][2].Data(), it, step)); + corrQyC.push_back(getCorrection(names[step - 1][3].Data(), it, step)); + + if (cfgFillHistRegistry && !cfgFillNothing && isEventSelected) { + registry.get(HIST("recentering/QXA_vs_iteration"))->Fill(pb + 1, q[0] - std::accumulate(corrQxA.begin(), corrQxA.end(), 0.0)); + registry.get(HIST("recentering/QYA_vs_iteration"))->Fill(pb + 1, q[1] - std::accumulate(corrQyA.begin(), corrQyA.end(), 0.0)); + registry.get(HIST("recentering/QXC_vs_iteration"))->Fill(pb + 1, q[2] - std::accumulate(corrQxC.begin(), corrQxC.end(), 0.0)); + registry.get(HIST("recentering/QYC_vs_iteration"))->Fill(pb + 1, q[3] - std::accumulate(corrQyC.begin(), corrQyC.end(), 0.0)); + } + + pb++; + } + + double totalCorrectionQxAshift = std::accumulate(corrQxA.begin(), corrQxA.end(), 0.0); + double totalCorrectionQyAshift = std::accumulate(corrQyA.begin(), corrQyA.end(), 0.0); + double totalCorrectionQxCshift = std::accumulate(corrQxC.begin(), corrQxC.end(), 0.0); + double totalCorrectionQyCshift = std::accumulate(corrQyC.begin(), corrQyC.end(), 0.0); + + qXaShift -= totalCorrectionQxAshift; + qYaShift -= totalCorrectionQyAshift; + qXcShift -= totalCorrectionQxCshift; + qYcShift -= totalCorrectionQyCshift; + + if (cal.isSelected && cfgFillHistRegistry && !cfgFillNothing && isEventSelected) { + fillCommonRegistry(qXaShift, qYaShift, qXcShift, qYcShift, cal.v, cent, rsTimestamp); + registry.fill(HIST("QA/centrality_after"), cent); + registry.get(HIST("QA/after/ZNA_Qx"))->Fill(Form("%d", runnumber), qXaShift); + registry.get(HIST("QA/after/ZNA_Qy"))->Fill(Form("%d", runnumber), qYaShift); + registry.get(HIST("QA/after/ZNC_Qx"))->Fill(Form("%d", runnumber), qXcShift); + registry.get(HIST("QA/after/ZNC_Qy"))->Fill(Form("%d", runnumber), qYcShift); + } + + spTableZDC(runnumber, cents, cal.v, foundBC.timestamp(), qXaShift, qYaShift, qXcShift, qYcShift, cal.isSelected, eventSelectionFlags); + qRec = {0, 0, 0, 0}; + + cal.lastRunNumber = runnumber; + return; + } LOGF(warning, "We return without saving table... -> THis is a problem"); cal.lastRunNumber = runnumber; diff --git a/PWGCF/Flow/Tasks/CMakeLists.txt b/PWGCF/Flow/Tasks/CMakeLists.txt index 226449b78fe..ad5dbd56ba3 100644 --- a/PWGCF/Flow/Tasks/CMakeLists.txt +++ b/PWGCF/Flow/Tasks/CMakeLists.txt @@ -119,3 +119,7 @@ o2physics_add_dpl_workflow(flow-directed-flow-task PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(flow-transmutation + SOURCES flowTransmutation.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::GFWCore + COMPONENT_NAME Analysis) diff --git a/PWGCF/Flow/Tasks/flowEventPlane.cxx b/PWGCF/Flow/Tasks/flowEventPlane.cxx index 2ab9c8d97df..1605f2a0836 100644 --- a/PWGCF/Flow/Tasks/flowEventPlane.cxx +++ b/PWGCF/Flow/Tasks/flowEventPlane.cxx @@ -167,12 +167,16 @@ struct SpCalibTableProducer { Configurable cMaxZVtx{"cMaxZVtx", 10.0, "Max VtxZ cut"}; Configurable cMinCent{"cMinCent", 0., "Minumum Centrality"}; Configurable cMaxCent{"cMaxCent", 100.0, "Maximum Centrality"}; + Configurable cCentBins{"cCentBins", 100, "Number of bins in centrality axis"}; Configurable cSel8Trig{"cSel8Trig", true, "Sel8 (T0A + T0C) Selection Run3"}; Configurable cPileupReject{"cPileupReject", true, "Pileup rejection"}; Configurable cZVtxTimeDiff{"cZVtxTimeDiff", true, "z-vtx time diff selection"}; Configurable cIsGoodITSLayers{"cIsGoodITSLayers", true, "Good ITS Layers All"}; Configurable cMinOccupancy{"cMinOccupancy", 0, "Minimum FT0C Occupancy"}; Configurable cMaxOccupancy{"cMaxOccupancy", 1e6, "Maximum FT0C Occupancy"}; + Configurable capplyTdcCut{"capplyTdcCut", false, "Flag for TDC cut"}; + Configurable ctdcZnMin{"ctdcZnMin", -2.5, "Min ZN TDC cut"}; + Configurable ctdcZnMax{"ctdcZnMax", 2.5, "Max ZN TDC cut"}; // Histogram Registry. HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -184,7 +188,7 @@ struct SpCalibTableProducer { void init(InitContext const&) { // Histogram collision - histos.add("hCent", "Centrality", kTH1F, {{100, 0., 100., "FT0C%"}}); + histos.add("hCent", "Centrality", kTH1F, {{cCentBins, 0., 100., "FT0C%"}}); histos.add("hVz", "V_{z}", kTH1F, {{100, -10., 10., "V_{z}(cm)"}}); } @@ -226,6 +230,20 @@ struct SpCalibTableProducer { return true; } + template + bool selZdcHit(C const& zdc) + { + if (capplyTdcCut) { // a narrow TDC window is set + if ((zdc.timeZNC() <= ctdcZnMin) || (zdc.timeZNC() >= ctdcZnMax)) { + return false; + } + if ((zdc.timeZNA() <= ctdcZnMin) || (zdc.timeZNA() >= ctdcZnMax)) { + return false; + } + } + return true; + } + template void analyzeCollision(B const& bc, C const& collision) { @@ -243,6 +261,10 @@ struct SpCalibTableProducer { posZ = collision.posZ(); auto zdc = bc.zdc(); + if (!selZdcHit(zdc)) { + return; + } + std::vector znaEnergy, zncEnergy; for (int i = 0; i < kXYAC; ++i) { znaEnergy.push_back(zdc.energySectorZNA()[i]); @@ -292,12 +314,18 @@ struct SpectatorPlaneTableProducer { Configurable cMaxZVtx{"cMaxZVtx", 10.0, "Max VtxZ cut"}; Configurable cMinCent{"cMinCent", 0., "Minumum Centrality"}; Configurable cMaxCent{"cMaxCent", 100.0, "Maximum Centrality"}; + Configurable cCentBins{"cCentBins", 100, "Number of bins in centrality axis"}; Configurable cSel8Trig{"cSel8Trig", true, "Sel8 (T0A + T0C) Selection Run3"}; Configurable cPileupReject{"cPileupReject", true, "Pileup rejection"}; Configurable cZVtxTimeDiff{"cZVtxTimeDiff", true, "z-vtx time diff selection"}; Configurable cIsGoodITSLayers{"cIsGoodITSLayers", true, "Good ITS Layers All"}; Configurable cMinOccupancy{"cMinOccupancy", 0, "Minimum FT0C Occupancy"}; Configurable cMaxOccupancy{"cMaxOccupancy", 1e6, "Maximum FT0C Occupancy"}; + Configurable capplyTdcCut{"capplyTdcCut", false, "Flag for TDC cut"}; + Configurable ctdcZnMin{"ctdcZnMin", -2.5, "Min ZN TDC cut"}; + Configurable ctdcZnMax{"ctdcZnMax", 2.5, "Max ZN TDC cut"}; + + ConfigurableAxis axisVarCent{"axisVarCent", {VARIABLE_WIDTH, 0, 5, 10, 15, 20, 25, 30, 35, 40, 50, 60, 70, 80, 100}, "Variable width centrality bins"}; // Coarse binning factor Configurable cAxisCBF{"cAxisCBF", 10, "Coarse Bin Factor"}; @@ -313,6 +341,7 @@ struct SpectatorPlaneTableProducer { Configurable cRecentVxVy{"cRecentVxVy", true, "Vx / Vy recentering"}; // Corrections + Configurable cLoadCorrection{"cLoadCorrection", true, "Load corrections"}; Configurable cDoGainCalib{"cDoGainCalib", true, "Gain Calib Flag"}; Configurable cUseAlphaZDC{"cUseAlphaZDC", true, "Use Alpha ZDC"}; @@ -327,7 +356,6 @@ struct SpectatorPlaneTableProducer { // Tracks Configurable cTrackMinPt{"cTrackMinPt", 0.1, "p_{T} minimum"}; Configurable cTrackMaxPt{"cTrackMaxPt", 10.0, "p_{T} maximum"}; - Configurable cNEtaBins{"cNEtaBins", 7, "# of eta bins"}; Configurable cTrackEtaCut{"cTrackEtaCut", 0.8, "Pseudorapidity cut"}; Configurable cTrackGlobal{"cTrackGlobal", true, "Global Track"}; Configurable cTrackDcaXYCut{"cTrackDcaXYCut", 0.1, "DcaXY Cut"}; @@ -389,7 +417,7 @@ struct SpectatorPlaneTableProducer { // Define axes const AxisSpec axisZDCEnergy{500, 0, 500, "ZD[AC] Signal"}; - const AxisSpec axisCent{100, 0., 100, "FT0C%"}; + const AxisSpec axisCent{cCentBins, 0., 100, "FT0C%"}; const AxisSpec axisVx{cAxisVxyBins, cAxisVxMin, cAxisVxMax, "V_{X}(cm)"}; const AxisSpec axisVy{cAxisVxyBins, cAxisVyMin, cAxisVyMax, "V_{Y}(cm)"}; const AxisSpec axisVz{cAxisVzBins, cMinZVtx, cMaxZVtx, "V_{Z}(cm)"}; @@ -468,6 +496,7 @@ struct SpectatorPlaneTableProducer { // Directed flow QXY vector histos.add("DF/hQaQc", "X^{A}_{1}X^{C}_{1} + Y^{A}_{1}Y^{C}_{1}", kTProfile, {axisCent}); + histos.add("DF/hQaQcVarCent", "X^{A}_{1}X^{C}_{1} + Y^{A}_{1}Y^{C}_{1}", kTProfile, {axisVarCent}); } // Select collsion @@ -509,6 +538,20 @@ struct SpectatorPlaneTableProducer { return true; } + template + bool selZdcHit(C const& zdc) + { + if (capplyTdcCut) { // a narrow TDC window is set + if ((zdc.timeZNC() <= ctdcZnMin) || (zdc.timeZNC() >= ctdcZnMax)) { + return false; + } + if ((zdc.timeZNA() <= ctdcZnMin) || (zdc.timeZNA() >= ctdcZnMax)) { + return false; + } + } + return true; + } + // Load Gain Calibrations and ZDC Q-Vector Recentering Corrections void loadCorrections() { @@ -699,11 +742,18 @@ struct SpectatorPlaneTableProducer { // Zdc information auto zdc = bc.zdc(); + + if (!selZdcHit(zdc)) { + return false; + } auto znaEnergy = zdc.energySectorZNA(); auto zncEnergy = zdc.energySectorZNC(); auto znaEnergyCommon = zdc.energyCommonZNA(); auto zncEnergyCommon = zdc.energyCommonZNC(); + // Change in 3rd tower energy due to spurious peaks + zncEnergy[2] = zncEnergyCommon - zncEnergy[0] - zncEnergy[1] - zncEnergy[3]; + // Check energy deposits if (znaEnergyCommon <= 0 || zncEnergyCommon <= 0 || znaEnergy[0] <= 0 || znaEnergy[1] <= 0 || znaEnergy[2] <= 0 || znaEnergy[3] <= 0 || zncEnergy[0] <= 0 || zncEnergy[1] <= 0 || zncEnergy[2] <= 0 || zncEnergy[3] <= 0) { return false; @@ -901,6 +951,7 @@ struct SpectatorPlaneTableProducer { // Directed flow QXY vector float qac = (vSP[kXa] * vSP[kXc]) + (vSP[kYa] * vSP[kYc]); histos.fill(HIST("DF/hQaQc"), cent, qac); + histos.fill(HIST("DF/hQaQcVarCent"), cent, qac); } // Fill table @@ -925,7 +976,9 @@ struct SpectatorPlaneTableProducer { struct FlowEventPlane { // Tracks - Configurable cEtaBins{"cEtaBins", 5, "# of eta bins"}; + Configurable cEtaBinsCharged{"cEtaBinsCharged", 5, "# of eta bins"}; + Configurable cEtaBinsHadrons{"cEtaBinsHadrons", 5, "# of eta bins"}; + Configurable cEtaBinsStrange{"cEtaBinsStrange", 5, "# of eta bins"}; Configurable cEtaCut{"cEtaCut", 0.8, "Rapidity cut"}; // Pi,Ka,Pr @@ -960,6 +1013,8 @@ struct FlowEventPlane { Configurable cLambdaMinPt{"cLambdaMinPt", 0.6, "Lambda Min pT"}; Configurable cLambdaMaxPt{"cLambdaMaxPt", 6.0, "Lambda Max pT"}; + ConfigurableAxis axisVarCent{"axisVarCent", {VARIABLE_WIDTH, 0, 5, 10, 15, 20, 25, 30, 35, 40, 50, 60, 70, 80, 100}, "Variable width centrality bins"}; + // Histogram registry: an object to hold your histograms HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -970,13 +1025,15 @@ struct FlowEventPlane { void init(InitContext const&) { // Define axes - const AxisSpec axisCent{100, 0., 100, "FT0C%"}; + // const AxisSpec axisCent{100, 0., 100, "FT0C%"}; const AxisSpec axisXYac{600, -6, 6, "Q^{t}Q^{p}"}; const AxisSpec axisV1{400, -4, 4, "v_{1}"}; const AxisSpec axisTrackPt{100, 0., 10., "p_{T} (GeV/#it{c})"}; - const AxisSpec axisTrackEta{cEtaBins, -0.8, 0.8, "#eta"}; + const AxisSpec axisTrackEtaCharged{cEtaBinsCharged, -0.8, 0.8, "#etaCharged"}; + const AxisSpec axisTrackEtaHadrons{cEtaBinsHadrons, -0.8, 0.8, "#etaHadrons"}; + const AxisSpec axisTrackEtaStrange{cEtaBinsStrange, -0.8, 0.8, "#etaStrange"}; const AxisSpec axisTrackDcaXY{60, -0.15, 0.15, "DCA_{XY}"}; const AxisSpec axisTrackDcaZ{230, -1.15, 1.15, "DCA_{XY}"}; const AxisSpec axisTrackdEdx{360, 20, 200, "#frac{dE}{dx}"}; @@ -1004,12 +1061,12 @@ struct FlowEventPlane { histos.add("TrackQA/hPtDcaXY", "DCA_{XY} vs p_{T}", kTH2F, {axisTrackPt, axisTrackDcaXY}); histos.add("TrackQA/hPtDcaZ", "DCA_{Z} vs p_{T}", kTH2F, {axisTrackPt, axisTrackDcaZ}); histos.add("TrackQA/hTrackTPCdEdX", "hTrackTPCdEdX", kTH2F, {axisMomPID, axisdEdx}); - histos.add("DF/hAQu", "u_{x}X^{A}_{1} + u_{y}Y^{A}_{1}", kTProfile2D, {axisCent, axisTrackEta}); - histos.add("DF/hCQu", "u_{x}X^{C}_{1} + u_{y}Y^{C}_{1}", kTProfile2D, {axisCent, axisTrackEta}); - histos.add("DF/hAQuPos", "u_{x}X^{A}_{1} + u_{y}Y^{A}_{1}", kTProfile2D, {axisCent, axisTrackEta}); - histos.add("DF/hCQuPos", "u_{x}X^{C}_{1} + u_{y}Y^{C}_{1}", kTProfile2D, {axisCent, axisTrackEta}); - histos.add("DF/hAQuNeg", "u_{x}X^{A}_{1} + u_{y}Y^{A}_{1}", kTProfile2D, {axisCent, axisTrackEta}); - histos.add("DF/hCQuNeg", "u_{x}X^{C}_{1} + u_{y}Y^{C}_{1}", kTProfile2D, {axisCent, axisTrackEta}); + histos.add("DF/hAQu", "u_{x}X^{A}_{1} + u_{y}Y^{A}_{1}", kTProfile2D, {axisVarCent, axisTrackEtaCharged}); + histos.add("DF/hCQu", "u_{x}X^{C}_{1} + u_{y}Y^{C}_{1}", kTProfile2D, {axisVarCent, axisTrackEtaCharged}); + histos.add("DF/hAQuPos", "u_{x}X^{A}_{1} + u_{y}Y^{A}_{1}", kTProfile2D, {axisVarCent, axisTrackEtaCharged}); + histos.add("DF/hCQuPos", "u_{x}X^{C}_{1} + u_{y}Y^{C}_{1}", kTProfile2D, {axisVarCent, axisTrackEtaCharged}); + histos.add("DF/hAQuNeg", "u_{x}X^{A}_{1} + u_{y}Y^{A}_{1}", kTProfile2D, {axisVarCent, axisTrackEtaCharged}); + histos.add("DF/hCQuNeg", "u_{x}X^{C}_{1} + u_{y}Y^{C}_{1}", kTProfile2D, {axisVarCent, axisTrackEtaCharged}); } // Identified hadrons @@ -1018,22 +1075,22 @@ struct FlowEventPlane { histos.add("PartId/Pion/hTOFSignal", "#beta_{TOF} vs p_{T}", kTH2F, {axisMomPID, axisTrackTofSignal}); histos.add("PartId/Pion/hTPCNSigma", "n#sigma_{TPC} vs p_{T}", kTH2F, {axisMomPID, axisTrackNSigma}); histos.add("PartId/Pion/hTOFNSigma", "n#sigma_{TOF} vs p_{T}", kTH2F, {axisMomPID, axisTrackNSigma}); - histos.add("PartId/Pion/hAQuPos", "PartId/Pion/hAQuPos", kTProfile2D, {axisCent, axisTrackEta}); - histos.add("PartId/Pion/hAQuNeg", "PartId/Pion/hAQuNeg", kTProfile2D, {axisCent, axisTrackEta}); - histos.add("PartId/Pion/hCQuPos", "PartId/Pion/hCQuPos", kTProfile2D, {axisCent, axisTrackEta}); - histos.add("PartId/Pion/hCQuNeg", "PartId/Pion/hCQuNeg", kTProfile2D, {axisCent, axisTrackEta}); + histos.add("PartId/Pion/hAQuPos", "PartId/Pion/hAQuPos", kTProfile2D, {axisVarCent, axisTrackEtaHadrons}); + histos.add("PartId/Pion/hAQuNeg", "PartId/Pion/hAQuNeg", kTProfile2D, {axisVarCent, axisTrackEtaHadrons}); + histos.add("PartId/Pion/hCQuPos", "PartId/Pion/hCQuPos", kTProfile2D, {axisVarCent, axisTrackEtaHadrons}); + histos.add("PartId/Pion/hCQuNeg", "PartId/Pion/hCQuNeg", kTProfile2D, {axisVarCent, axisTrackEtaHadrons}); histos.addClone("PartId/Pion/", "PartId/Kaon/"); histos.addClone("PartId/Pion/", "PartId/Proton/"); } // Resonance if (doprocessResoFlow) { - histos.add("Reso/Phi/hSigCentEtaInvMass", "hUSCentEtaInvMass", kTH3F, {axisCent, axisTrackEta, axisPhiInvMass}); - histos.add("Reso/Phi/hBkgCentEtaInvMass", "hLSCentEtaInvMass", kTH3F, {axisCent, axisTrackEta, axisPhiInvMass}); - histos.add("Reso/Phi/Sig/hQuA", "hPhiQuA", kTProfile3D, {axisCent, axisTrackEta, axisPhiInvMass}); - histos.add("Reso/Phi/Sig/hQuC", "hPhiQuC", kTProfile3D, {axisCent, axisTrackEta, axisPhiInvMass}); - histos.add("Reso/Phi/Bkg/hQuA", "hPhiQuA", kTProfile3D, {axisCent, axisTrackEta, axisPhiInvMass}); - histos.add("Reso/Phi/Bkg/hQuC", "hPhiQuC", kTProfile3D, {axisCent, axisTrackEta, axisPhiInvMass}); + histos.add("Reso/Phi/hBkgCentEtaInvMass", "hLSCentEtaInvMass", kTH3F, {axisVarCent, axisTrackEtaStrange, axisPhiInvMass}); + histos.add("Reso/Phi/hSigCentEtaInvMass", "hUSCentEtaInvMass", kTH3F, {axisVarCent, axisTrackEtaStrange, axisPhiInvMass}); + histos.add("Reso/Phi/Sig/hQuA", "hPhiQuA", kTProfile3D, {axisVarCent, axisTrackEtaStrange, axisPhiInvMass}); + histos.add("Reso/Phi/Sig/hQuC", "hPhiQuC", kTProfile3D, {axisVarCent, axisTrackEtaStrange, axisPhiInvMass}); + histos.add("Reso/Phi/Bkg/hQuA", "hPhiQuA", kTProfile3D, {axisVarCent, axisTrackEtaStrange, axisPhiInvMass}); + histos.add("Reso/Phi/Bkg/hQuC", "hPhiQuC", kTProfile3D, {axisVarCent, axisTrackEtaStrange, axisPhiInvMass}); } // Lambda @@ -1053,14 +1110,14 @@ struct FlowEventPlane { histos.add("V0/Lambda/QA/hPosNsigPiVsP", "TPC n#sigma Pos Prong", kTH2F, {axisMomPID, axisNsigma}); histos.add("V0/Lambda/QA/hNegNsigPiVsP", "TPC n#sigma Neg Prong", kTH2F, {axisMomPID, axisNsigma}); histos.addClone("V0/Lambda/", "V0/K0Short/"); - histos.add("V0/Lambda/hMassVsRap", "hMassVsRap", kTH3F, {axisCent, axisTrackEta, axisLambdaInvMass}); - histos.add("V0/Lambda/Flow/hQuA", "hQuA", kTProfile3D, {axisCent, axisTrackEta, axisLambdaInvMass}); - histos.add("V0/Lambda/Flow/hQuC", "hQuC", kTProfile3D, {axisCent, axisTrackEta, axisLambdaInvMass}); + histos.add("V0/Lambda/hMassVsRap", "hMassVsRap", kTH3F, {axisVarCent, axisTrackEtaStrange, axisLambdaInvMass}); + histos.add("V0/Lambda/Flow/hQuA", "hQuA", kTProfile3D, {axisVarCent, axisTrackEtaStrange, axisLambdaInvMass}); + histos.add("V0/Lambda/Flow/hQuC", "hQuC", kTProfile3D, {axisVarCent, axisTrackEtaStrange, axisLambdaInvMass}); histos.addClone("V0/Lambda/", "V0/AntiLambda/"); histos.addClone("V0/Lambda/", "V0/LambdaAntiLambda/"); - histos.add("V0/K0Short/hMassVsRap", "hMassVsRap", kTH3F, {axisCent, axisTrackEta, axisK0ShortInvMass}); - histos.add("V0/K0Short/Flow/hQuA", "hQuA", kTProfile3D, {axisCent, axisTrackEta, axisK0ShortInvMass}); - histos.add("V0/K0Short/Flow/hQuC", "hQuC", kTProfile3D, {axisCent, axisTrackEta, axisK0ShortInvMass}); + histos.add("V0/K0Short/hMassVsRap", "hMassVsRap", kTH3F, {axisVarCent, axisTrackEtaStrange, axisK0ShortInvMass}); + histos.add("V0/K0Short/Flow/hQuA", "hQuA", kTProfile3D, {axisVarCent, axisTrackEtaStrange, axisK0ShortInvMass}); + histos.add("V0/K0Short/Flow/hQuC", "hQuC", kTProfile3D, {axisVarCent, axisTrackEtaStrange, axisK0ShortInvMass}); } } diff --git a/PWGCF/Flow/Tasks/flowSP.cxx b/PWGCF/Flow/Tasks/flowSP.cxx index 9e04b351488..26b96ec9c80 100644 --- a/PWGCF/Flow/Tasks/flowSP.cxx +++ b/PWGCF/Flow/Tasks/flowSP.cxx @@ -53,6 +53,7 @@ #include #include +#include #include #include #include @@ -71,7 +72,7 @@ using namespace o2::framework::expressions; using namespace o2::aod::rctsel; // using namespace o2::analysis; -#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; // NOLINT(bugprone-macro-parentheses) struct FlowSP { @@ -130,6 +131,7 @@ struct FlowSP { O2_DEFINE_CONFIGURABLE(cCentMin, float, 0, "Minimum cenrality for selected events"); O2_DEFINE_CONFIGURABLE(cCentMax, float, 90, "Maximum cenrality for selected events"); O2_DEFINE_CONFIGURABLE(cFilterLeptons, bool, true, "Filter out leptons from MCGenerated by requiring |pdgCode| > 100"); + O2_DEFINE_CONFIGURABLE(cDoGeneratedInReco, bool, true, "Do MC Generated inside the MCReco process function on mcCollisions"); // NUA and NUE weights O2_DEFINE_CONFIGURABLE(cFillWeights, bool, true, "Fill NUA weights"); O2_DEFINE_CONFIGURABLE(cFillWeightsPOS, bool, true, "Fill NUA weights only for positive charges"); @@ -142,9 +144,7 @@ struct FlowSP { // Additional track Selections O2_DEFINE_CONFIGURABLE(cTrackSelsUseAdditionalTrackCut, bool, false, "Bool to enable Additional Track Cut"); O2_DEFINE_CONFIGURABLE(cTrackSelsDoDCApt, bool, true, "Apply Pt dependent DCAz cut"); - O2_DEFINE_CONFIGURABLE(cTrackSelsDCApt1, float, 0.1, "DcaZ < const + (a * b) / pt^1.1 -> this sets a"); - O2_DEFINE_CONFIGURABLE(cTrackSelsDCApt2, float, 0.035, "DcaZ < const + (a * b) / pt^1.1 -> this sets b"); - O2_DEFINE_CONFIGURABLE(cTrackSelsDCAptConsMin, float, 0.1, "DcaZ < const + (a * b) / pt^1.1 -> this sets const"); + O2_DEFINE_CONFIGURABLE(cTrackSelsDCAfunc, std::string, "0.0105 + 0.035 / TMath::Power(x,1.1)", "Function for pT-dependent DCA-cut (Default y = 0.0105 + 0.035 / TMath::Power(x,1.1))"); O2_DEFINE_CONFIGURABLE(cTrackSelsPIDNsigma, float, 2.0, "nSigma cut for PID"); O2_DEFINE_CONFIGURABLE(cTrackSelDoTrackQAvsCent, bool, true, "Do track selection QA plots as function of centrality"); // harmonics for v coefficients @@ -176,8 +176,8 @@ struct FlowSP { Filter trackFilter = nabs(aod::track::eta) < cfg.cTrackSelsEta && aod::track::pt > cfg.cTrackSelsPtmin&& aod::track::pt < cfg.cTrackSelsPtmax && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true) || cfg.cIsMCReco) && nabs(aod::track::dcaXY) < cfg.cTrackSelsDCAxy&& nabs(aod::track::dcaZ) < cfg.cTrackSelsDCAz; Filter trackFilterMC = nabs(aod::mcparticle::eta) < cfg.cTrackSelsEta && aod::mcparticle::pt > cfg.cTrackSelsPtmin&& aod::mcparticle::pt < cfg.cTrackSelsPtmax; using GeneralCollisions = soa::Join; - using UnfilteredTracksPID = soa::Join; - using UnfilteredTracks = soa::Join; + using UnfilteredTracksPID = soa::Join; + using UnfilteredTracks = soa::Join; using UsedTracks = soa::Filtered; using UsedTracksPID = soa::Filtered; @@ -197,16 +197,16 @@ struct FlowSP { Preslice trackPerCollision = aod::track::collisionId; // Connect to ccdb - Service ccdb; - Service pdg; + Service ccdb{}; + Service pdg{}; // struct to hold the correction histos/ struct Config { - std::vector mEfficiency = {}; - std::vector mEfficiency2D = {}; - std::vector mEfficiency3D = {}; - std::vector mAcceptance = {}; - std::vector mAcceptance2D = {}; + std::vector mEfficiency; + std::vector mEfficiency2D; + std::vector mEfficiency3D; + std::vector mAcceptance; + std::vector mAcceptance2D; bool correctionsLoaded = false; int lastRunNumber = 0; @@ -290,6 +290,9 @@ struct FlowSP { std::unique_ptr fMultCutHigh = nullptr; std::unique_ptr fMultMultPVCut = nullptr; + // Track selection DCA cut + std::unique_ptr fDCACut = std::make_unique("fDCACut", cfg.cTrackSelsDCAfunc.value.c_str(), 0, 100); + enum SelectionCriteria { evSel_FilteredEvent, evSel_sel8, @@ -312,12 +315,12 @@ struct FlowSP { trackSel_ZeroCharge, trackSel_Eta, trackSel_Pt, + trackSel_NCls, + trackSel_TPCBoundary, trackSel_DCAxy, trackSel_DCAz, - trackSel_GlobalTracks, - trackSel_NCls, trackSel_FshCls, - trackSel_TPCBoundary, + trackSel_GlobalTracks, trackSel_ParticleWeights, nTrackSelections }; @@ -349,9 +352,9 @@ struct FlowSP { nParticleTypes }; - static constexpr std::string_view Charge[] = {"incl/", "pos/", "neg/"}; - static constexpr std::string_view Species[] = {"", "pion/", "kaon/", "proton/"}; - static constexpr std::string_view Time[] = {"before/", "after/"}; + static constexpr std::array Charge = {"incl/", "pos/", "neg/"}; + static constexpr std::array Species = {"", "pion/", "kaon/", "proton/"}; + static constexpr std::array Time = {"before/", "after/"}; void init(InitContext const&) { @@ -499,8 +502,9 @@ struct FlowSP { histos.add("incl/QA/after/hPhi_Eta_Pt_corrected", "", kTH3D, {axisPhi, axisEta, axisPt}); } - if (cfg.cFillQABefore) + if (cfg.cFillQABefore) { histos.addClone("incl/QA/after/", "incl/QA/before/"); + } } if (cfg.cFillPIDQA && doprocessDataPID) { @@ -548,9 +552,22 @@ struct FlowSP { } if (doprocessMCReco) { - registry.add("trackMCReco/after/incl/hIsPhysicalPrimary", "", {HistType::kTH3D, {{2, 0, 2}, axisCentrality, axisPt}}); - registry.get(HIST("trackMCReco/after/incl/hIsPhysicalPrimary"))->GetXaxis()->SetBinLabel(1, "Secondary"); - registry.get(HIST("trackMCReco/after/incl/hIsPhysicalPrimary"))->GetXaxis()->SetBinLabel(2, "Primary"); + registry.add("trackMCReco/after/incl/hIsPhysicalPrimary", "", {HistType::kTH3D, {axisCentrality, axisEta, axisPt}}); + registry.add("trackMCReco/after/incl/hIsNotPhysicalPrimary", "", {HistType::kTH3D, {axisCentrality, axisEta, axisPt}}); + registry.add("trackMCReco/incl/hPtMCPtTrack", "", {HistType::kTH2D, {axisPt, axisPt}}); + registry.add("trackMCReco/incl/hEtaMCEtaTrack", "", {HistType::kTH2D, {axisEta, axisEta}}); + registry.add("trackMCReco/incl/hPtPerTrackSelection", "", {HistType::kTH2D, {axisPt, {nTrackSelections, 0, nTrackSelections}}}); + registry.get(HIST("trackMCReco/incl/hPtPerTrackSelection"))->GetYaxis()->SetBinLabel(trackSel_Eta + 1, "Eta"); + registry.get(HIST("trackMCReco/incl/hPtPerTrackSelection"))->GetYaxis()->SetBinLabel(trackSel_Pt + 1, "Pt"); + registry.get(HIST("trackMCReco/incl/hPtPerTrackSelection"))->GetYaxis()->SetBinLabel(trackSel_DCAxy + 1, "DCAxy"); + registry.get(HIST("trackMCReco/incl/hPtPerTrackSelection"))->GetYaxis()->SetBinLabel(trackSel_DCAz + 1, "DCAz"); + registry.get(HIST("trackMCReco/incl/hPtPerTrackSelection"))->GetYaxis()->SetBinLabel(trackSel_GlobalTracks + 1, "GlobalTracks"); + registry.get(HIST("trackMCReco/incl/hPtPerTrackSelection"))->GetYaxis()->SetBinLabel(trackSel_NCls + 1, "nClusters TPC"); + registry.get(HIST("trackMCReco/incl/hPtPerTrackSelection"))->GetYaxis()->SetBinLabel(trackSel_FshCls + 1, "Frac. sh. Cls TPC"); + registry.get(HIST("trackMCReco/incl/hPtPerTrackSelection"))->GetYaxis()->SetBinLabel(trackSel_TPCBoundary + 1, "TPC Boundary"); + registry.get(HIST("trackMCReco/incl/hPtPerTrackSelection"))->GetYaxis()->SetBinLabel(trackSel_ZeroCharge + 1, "Only charged"); + registry.get(HIST("trackMCReco/incl/hPtPerTrackSelection"))->GetYaxis()->SetBinLabel(trackSel_ParticleWeights + 1, "Apply weights"); + registry.add("trackMCReco/hTrackSize_unFiltered", "", {HistType::kTH2D, {{100, 0, 4000}, axisCentrality}}); registry.add("trackMCReco/hTrackSize_Filtered", "", {HistType::kTH2D, {{100, 0, 4000}, axisCentrality}}); registry.add("trackMCReco/after/incl/hPt_hadron", "", {HistType::kTH3D, {axisPt, axisEta, axisCentrality}}); @@ -560,6 +577,8 @@ struct FlowSP { // Clone into particles and before/after registry.addClone("trackMCReco/after/incl/", "trackMCReco/after/pos/"); registry.addClone("trackMCReco/after/incl/", "trackMCReco/after/neg/"); + registry.addClone("trackMCReco/incl/", "trackMCReco/neg/"); + registry.addClone("trackMCReco/incl/", "trackMCReco/pos/"); registry.addClone("trackMCReco/after/", "trackMCReco/before/"); } @@ -681,14 +700,15 @@ struct FlowSP { registry.addClone("incl/", "neg/"); } } - - } else if (doprocessMCGen) { + } + if (doprocessMCGen || doprocessMCReco) { registry.add("trackMCGen/nCollReconstructedPerMcCollision", "", {HistType::kTH1D, {{10, -5, 5}}}); registry.add("trackMCGen/after/incl/hPt_hadron", "", {HistType::kTH3D, {axisPt, axisEta, axisCentrality}}); registry.add("trackMCGen/after/incl/hPt_proton", "", {HistType::kTH3D, {axisPt, axisEta, axisCentrality}}); registry.add("trackMCGen/after/incl/hPt_pion", "", {HistType::kTH3D, {axisPt, axisEta, axisCentrality}}); registry.add("trackMCGen/after/incl/hPt_kaon", "", {HistType::kTH3D, {axisPt, axisEta, axisCentrality}}); registry.add("trackMCGen/after/incl/phi_eta_vtxZ_gen", "", {HistType::kTH3D, {axisPhi, axisEta, axisVz}}); + registry.addClone("trackMCGen/after/incl/", "trackMCGen/after/pos/"); registry.addClone("trackMCGen/after/incl/", "trackMCGen/after/neg/"); registry.addClone("trackMCGen/after/", "trackMCGen/before/"); @@ -768,8 +788,9 @@ struct FlowSP { int etaind = hNUA->GetYaxis()->FindBin(eta); int vzind = hNUA->GetZaxis()->FindBin(vtxz); float weight = hNUA->GetBinContent(xind, etaind, vzind); - if (weight != 0) + if (weight != 0) { return 1. / weight; + } return 1; } @@ -809,13 +830,11 @@ struct FlowSP { } } - if (nIdentified == 0) { - return kUnidentified; // No PID match found - } else if (nIdentified == 1) { + if (nIdentified == 1) { return valPID; - } else { - return kUnidentified; // Multiple PID matches found } + + return kUnidentified; // Multiple PID matches found } int getMagneticField(uint64_t timestamp) @@ -850,28 +869,30 @@ struct FlowSP { void loadCorrections(uint64_t timestamp) { // corrections saved on CCDB as TList {incl, pos, neg} of GFWWeights (acc) TH1D (eff) objects! - if (conf.correctionsLoaded) + if (conf.correctionsLoaded) { return; + } int nWeights = 3; if (cfg.cUseNUA1D) { - if (cfg.cCCDB_NUA.value.empty() == false) { - TList* listCorrections = ccdb->getForTimeStamp(cfg.cCCDB_NUA, timestamp); - conf.mAcceptance.push_back(reinterpret_cast(listCorrections->FindObject("weights"))); - conf.mAcceptance.push_back(reinterpret_cast(listCorrections->FindObject("weights_positive"))); - conf.mAcceptance.push_back(reinterpret_cast(listCorrections->FindObject("weights_negative"))); + if (!cfg.cCCDB_NUA.value.empty()) { + auto* listCorrections = ccdb->getForTimeStamp(cfg.cCCDB_NUA, timestamp); + conf.mAcceptance.push_back(dynamic_cast(listCorrections->FindObject("weights"))); + conf.mAcceptance.push_back(dynamic_cast(listCorrections->FindObject("weights_positive"))); + conf.mAcceptance.push_back(dynamic_cast(listCorrections->FindObject("weights_negative"))); int sizeAcc = conf.mAcceptance.size(); - if (sizeAcc < nWeights) + if (sizeAcc < nWeights) { LOGF(fatal, "Could not load acceptance weights from %s", cfg.cCCDB_NUA.value.c_str()); - else + } else { LOGF(info, "Loaded acceptance weights from %s", cfg.cCCDB_NUA.value.c_str()); + } } else { LOGF(info, "cfg.cCCDB_NUA empty! No corrections loaded"); } } else if (cfg.cUseNUA2D) { - if (cfg.cCCDB_NUA.value.empty() == false) { - TH3D* hNUA2D = ccdb->getForTimeStamp(cfg.cCCDB_NUA, timestamp); + if (!cfg.cCCDB_NUA.value.empty()) { + auto* hNUA2D = ccdb->getForTimeStamp(cfg.cCCDB_NUA, timestamp); if (!hNUA2D) { LOGF(fatal, "Could not load acceptance weights from %s", cfg.cCCDB_NUA.value.c_str()); } else { @@ -883,39 +904,41 @@ struct FlowSP { } } // Get Efficiency correction - if (cfg.cCCDB_NUE.value.empty() == false) { - TList* listCorrections = ccdb->getForTimeStamp(cfg.cCCDB_NUE, timestamp); - conf.mEfficiency.push_back(reinterpret_cast(listCorrections->FindObject("Efficiency"))); - conf.mEfficiency.push_back(reinterpret_cast(listCorrections->FindObject("Efficiency_pos"))); - conf.mEfficiency.push_back(reinterpret_cast(listCorrections->FindObject("Efficiency_neg"))); + if (!cfg.cCCDB_NUE.value.empty()) { + auto* listCorrections = ccdb->getForTimeStamp(cfg.cCCDB_NUE, timestamp); + conf.mEfficiency.push_back(dynamic_cast(listCorrections->FindObject("Efficiency"))); + conf.mEfficiency.push_back(dynamic_cast(listCorrections->FindObject("Efficiency_pos"))); + conf.mEfficiency.push_back(dynamic_cast(listCorrections->FindObject("Efficiency_neg"))); int sizeEff = conf.mEfficiency.size(); - if (sizeEff < nWeights) + if (sizeEff < nWeights) { LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfg.cCCDB_NUE.value.c_str()); - else + } else { LOGF(info, "Loaded efficiency histogram from %s", cfg.cCCDB_NUE.value.c_str()); + } } else { LOGF(info, "cfg.cCCDB_NUE empty! No corrections loaded"); } // Get Efficiency correction - if (cfg.cCCDB_NUE2D.value.empty() == false) { - TList* listCorrections = ccdb->getForTimeStamp(cfg.cCCDB_NUE2D, timestamp); - conf.mEfficiency2D.push_back(reinterpret_cast(listCorrections->FindObject("Efficiency"))); - conf.mEfficiency2D.push_back(reinterpret_cast(listCorrections->FindObject("Efficiency_pos"))); - conf.mEfficiency2D.push_back(reinterpret_cast(listCorrections->FindObject("Efficiency_neg"))); + if (!cfg.cCCDB_NUE2D.value.empty()) { + auto* listCorrections = ccdb->getForTimeStamp(cfg.cCCDB_NUE2D, timestamp); + conf.mEfficiency2D.push_back(dynamic_cast(listCorrections->FindObject("Efficiency"))); + conf.mEfficiency2D.push_back(dynamic_cast(listCorrections->FindObject("Efficiency_pos"))); + conf.mEfficiency2D.push_back(dynamic_cast(listCorrections->FindObject("Efficiency_neg"))); int sizeEff = conf.mEfficiency2D.size(); - if (sizeEff < nWeights) + if (sizeEff < nWeights) { LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfg.cCCDB_NUE.value.c_str()); - else + } else { LOGF(info, "Loaded efficiency histogram from %s", cfg.cCCDB_NUE.value.c_str()); + } } else { LOGF(info, "cfg.cCCDB_NUE2 empty! No corrections loaded"); } - if (cfg.cCCDB_NUE3D.value.empty() == false) { - TList* listCorrections = ccdb->getForTimeStamp(cfg.cCCDB_NUE3D, timestamp); - conf.mEfficiency3D.push_back(reinterpret_cast(listCorrections->FindObject("Efficiency"))); - conf.mEfficiency3D.push_back(reinterpret_cast(listCorrections->FindObject("Efficiency_pos"))); - conf.mEfficiency3D.push_back(reinterpret_cast(listCorrections->FindObject("Efficiency_neg"))); + if (!cfg.cCCDB_NUE3D.value.empty()) { + auto* listCorrections = ccdb->getForTimeStamp(cfg.cCCDB_NUE3D, timestamp); + conf.mEfficiency3D.push_back(dynamic_cast(listCorrections->FindObject("Efficiency"))); + conf.mEfficiency3D.push_back(dynamic_cast(listCorrections->FindObject("Efficiency_pos"))); + conf.mEfficiency3D.push_back(dynamic_cast(listCorrections->FindObject("Efficiency_neg"))); int sizeEff = conf.mEfficiency3D.size(); if (sizeEff < nWeights) LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfg.cCCDB_NUE.value.c_str()); @@ -977,15 +1000,28 @@ struct FlowSP { return true; } + template + inline void fillPrimaryHistos(const McParticleObject& mcparticle) + { + + if (mcparticle.isPhysicalPrimary()) { + registry.fill(HIST("trackMCReco/") + HIST(Time[ft]) + HIST(Charge[ct]) + HIST("hIsPhysicalPrimary"), spm.centrality, mcparticle.eta(), mcparticle.pt()); + } else { + registry.fill(HIST("trackMCReco/") + HIST(Time[ft]) + HIST(Charge[ct]) + HIST("hIsNotPhysicalPrimary"), spm.centrality, mcparticle.eta(), mcparticle.pt()); + } + } + template bool eventSelected(const TCollision& collision, const int& multTrk) { - if (!collision.sel8()) + if (!collision.sel8()) { return 0; + } histos.fill(HIST("hEventCount"), evSel_sel8); - if (cfg.cEvtUseRCTFlagChecker && !rctChecker(collision)) + if (cfg.cEvtUseRCTFlagChecker && !rctChecker(collision)) { return 0; + } histos.fill(HIST("hEventCount"), evSel_RCTFlagsZDC); // Occupancy @@ -994,8 +1030,8 @@ struct FlowSP { if (occupancy > cfg.cEvSelsMaxOccupancy || occupancy < cfg.cEvSelsMinOccupancy) { return 0; } - histos.fill(HIST("hEventCount"), evSel_occupancy); } + histos.fill(HIST("hEventCount"), evSel_occupancy); if (cfg.cEvSelsNoSameBunchPileupCut) { if (!collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { @@ -1003,37 +1039,37 @@ struct FlowSP { // https://indico.cern.ch/event/1396220/#1-event-selection-with-its-rof return 0; } - histos.fill(HIST("hEventCount"), evSel_kNoSameBunchPileup); } + histos.fill(HIST("hEventCount"), evSel_kNoSameBunchPileup); if (cfg.cEvSelsIsGoodZvtxFT0vsPV) { if (!collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference // use this cut at low multiplicities with caution return 0; } - histos.fill(HIST("hEventCount"), evSel_kIsGoodZvtxFT0vsPV); } + histos.fill(HIST("hEventCount"), evSel_kIsGoodZvtxFT0vsPV); if (cfg.cEvSelsNoCollInTimeRangeStandard) { if (!collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { // Rejection of the collisions which have other events nearby return 0; } - histos.fill(HIST("hEventCount"), evSel_kNoCollInTimeRangeStandard); } + histos.fill(HIST("hEventCount"), evSel_kNoCollInTimeRangeStandard); if (cfg.cEvSelsNoCollInTimeRangeNarrow) { if (!collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeNarrow)) { // Rejection of the collisions which have other events nearby return 0; } - histos.fill(HIST("hEventCount"), evSel_kNoCollInTimeRangeNarrow); } + histos.fill(HIST("hEventCount"), evSel_kNoCollInTimeRangeNarrow); if (cfg.cEvSelsIsVertexITSTPC) { if (!collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { // selects collisions with at least one ITS-TPC track, and thus rejects vertices built from ITS-only tracks return 0; } - histos.fill(HIST("hEventCount"), evSel_kIsVertexITSTPC); } + histos.fill(HIST("hEventCount"), evSel_kIsVertexITSTPC); if (cfg.cEvSelsIsGoodITSLayersAll) { if (!collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { @@ -1041,14 +1077,14 @@ struct FlowSP { // https://indico.cern.ch/event/1493023/ (09-01-2025) return 0; } - histos.fill(HIST("hEventCount"), evSel_kIsGoodITSLayersAll); } + histos.fill(HIST("hEventCount"), evSel_kIsGoodITSLayersAll); if (cfg.cEvSelsIsGoodITSLayer0123) { if (!collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { return 0; } - histos.fill(HIST("hEventCount"), evSel_kIsGoodITSLayer0123); } + histos.fill(HIST("hEventCount"), evSel_kIsGoodITSLayer0123); if (cfg.cEvSelsUseAdditionalEventCut) { float vtxz = -999; @@ -1057,25 +1093,30 @@ struct FlowSP { float zRes = std::sqrt(collision.covZZ()); float minzRes = 0.25; int maxNumContrib = 20; - if (zRes > minzRes && collision.numContrib() < maxNumContrib) + if (zRes > minzRes && collision.numContrib() < maxNumContrib) { vtxz = -999; + } } auto multNTracksPV = collision.multNTracksPV(); - if (vtxz > cfg.cEvSelsVtxZ || vtxz < -cfg.cEvSelsVtxZ) + if (vtxz > cfg.cEvSelsVtxZ || vtxz < -cfg.cEvSelsVtxZ) { return 0; - if (multNTracksPV < fMultPVCutLow->Eval(collision.centFT0C())) + } + if (multNTracksPV < fMultPVCutLow->Eval(collision.centFT0C())) { return 0; - if (multNTracksPV > fMultPVCutHigh->Eval(collision.centFT0C())) + } + if (multNTracksPV > fMultPVCutHigh->Eval(collision.centFT0C())) { return 0; - if (multTrk < fMultCutLow->Eval(collision.centFT0C())) + } + if (multTrk < fMultCutLow->Eval(collision.centFT0C())) { return 0; - if (multTrk > fMultCutHigh->Eval(collision.centFT0C())) + } + if (multTrk > fMultCutHigh->Eval(collision.centFT0C())) { return 0; - - histos.fill(HIST("hEventCount"), evSel_MultCuts); + } } + histos.fill(HIST("hEventCount"), evSel_MultCuts); return 1; } @@ -1083,64 +1124,131 @@ struct FlowSP { template bool trackSelected(const TrackObject& track, const int& field) { - if (std::fabs(track.eta()) > cfg.cTrackSelsEta) + auto fillSpectraStudyMCReco = [&](TrackSelections sel) { + if constexpr (o2::framework::has_type_v) { + if (!track.has_mcParticle()) { + return; + } + const auto mcParticle = track.template mcParticle_as(); + if (doprocessMCReco && mcParticle.isPhysicalPrimary()) { + registry.fill(HIST("trackMCReco/incl/hPtPerTrackSelection"), mcParticle.pt(), sel); + if (track.sign() > 0) { + registry.fill(HIST("trackMCReco/pos/hPtPerTrackSelection"), mcParticle.pt(), sel); + } else if (track.sign() < 0) { + registry.fill(HIST("trackMCReco/neg/hPtPerTrackSelection"), mcParticle.pt(), sel); + } + } + } + }; + + if (std::fabs(track.eta()) > cfg.cTrackSelsEta) { return false; + } histos.fill(HIST("hTrackCount"), trackSel_Eta); + fillSpectraStudyMCReco(trackSel_Eta); - if (track.pt() < cfg.cTrackSelsPtmin || track.pt() > cfg.cTrackSelsPtmax) + if (track.pt() < cfg.cTrackSelsPtmin || track.pt() > cfg.cTrackSelsPtmax) { return false; - + } histos.fill(HIST("hTrackCount"), trackSel_Pt); + fillSpectraStudyMCReco(trackSel_Pt); - if (track.dcaXY() > cfg.cTrackSelsDCAxy) - return false; - - histos.fill(HIST("hTrackCount"), trackSel_DCAxy); - - if (track.dcaZ() > cfg.cTrackSelsDCAz) - return false; - - if (cfg.cTrackSelsDoDCApt && std::fabs(track.dcaZ()) > (cfg.cTrackSelsDCAptConsMin + (cfg.cTrackSelsDCApt1 * cfg.cTrackSelsDCApt2) / (std::pow(track.pt(), 1.1)))) - return false; - - histos.fill(HIST("hTrackCount"), trackSel_DCAz); - - if (track.tpcNClsCrossedRows() < cfg.cTrackSelsNcls) + if (track.tpcNClsCrossedRows() < cfg.cTrackSelsNcls) { return false; + } histos.fill(HIST("hTrackCount"), trackSel_NCls); - - if (track.tpcFractionSharedCls() > cfg.cTrackSelsFshcls) - return false; - histos.fill(HIST("hTrackCount"), trackSel_FshCls); + fillSpectraStudyMCReco(trackSel_NCls); double phimodn = track.phi(); - if (field < 0) // for negative polarity field + if (field < 0) { phimodn = o2::constants::math::TwoPI - phimodn; - if (track.sign() < 0) // for negative charge + } + if (track.sign() < 0) { phimodn = o2::constants::math::TwoPI - phimodn; - if (phimodn < 0) + } + if (phimodn < 0) { LOGF(warning, "phi < 0: %g", phimodn); + } phimodn += o2::constants::math::PI / 18.0; // to center gap in the middle phimodn = fmod(phimodn, o2::constants::math::PI / 9.0); - if (cfg.cFillTrackQA && cfg.cFillQABefore) + if (cfg.cFillTrackQA && cfg.cFillQABefore) { histos.fill(HIST("incl/QA/before/pt_phi"), track.pt(), phimodn); + } if (cfg.cTrackSelsUseAdditionalTrackCut) { - if (phimodn < fPhiCutHigh->Eval(track.pt()) && phimodn > fPhiCutLow->Eval(track.pt())) + if (phimodn < fPhiCutHigh->Eval(track.pt()) && phimodn > fPhiCutLow->Eval(track.pt())) { return false; // reject track + } } - if (cfg.cFillTrackQA) + if (cfg.cFillTrackQA) { histos.fill(HIST("incl/QA/after/pt_phi"), track.pt(), phimodn); + } histos.fill(HIST("hTrackCount"), trackSel_TPCBoundary); + fillSpectraStudyMCReco(trackSel_TPCBoundary); + + // Only fill primary/secondary histos for MC data. + if constexpr (o2::framework::has_type_v) { + auto mcParticle = track.template mcParticle_as(); + + fillPrimaryHistos(mcParticle); + if (spm.charge == kPositive) { + fillPrimaryHistos(mcParticle); + } else { + fillPrimaryHistos(mcParticle); + } + + if (std::fabs(track.dcaXY()) > cfg.cTrackSelsDCAxy || std::fabs(track.dcaXY()) > fDCACut->Eval(track.pt())) { + return false; + } + histos.fill(HIST("hTrackCount"), trackSel_DCAxy); + fillSpectraStudyMCReco(trackSel_DCAxy); + + if (std::fabs(track.dcaZ()) > cfg.cTrackSelsDCAz || (cfg.cTrackSelsDoDCApt && std::fabs(track.dcaZ()) > fDCACut->Eval(track.pt()))) { + return false; + } + histos.fill(HIST("hTrackCount"), trackSel_DCAz); + fillSpectraStudyMCReco(trackSel_DCAz); + + fillPrimaryHistos(mcParticle); + if (spm.charge == kPositive) { + fillPrimaryHistos(mcParticle); + } else { + fillPrimaryHistos(mcParticle); + } + } else { // Only apply DCA-cuts for data + if (std::fabs(track.dcaXY()) > cfg.cTrackSelsDCAxy || std::fabs(track.dcaXY()) > fDCACut->Eval(track.pt())) { + return false; + } + histos.fill(HIST("hTrackCount"), trackSel_DCAxy); + + if (std::fabs(track.dcaZ()) > cfg.cTrackSelsDCAz || (cfg.cTrackSelsDoDCApt && std::fabs(track.dcaZ()) > fDCACut->Eval(track.pt()))) { + return false; + } + histos.fill(HIST("hTrackCount"), trackSel_DCAz); + } + + if (track.tpcFractionSharedCls() > cfg.cTrackSelsFshcls) { + return false; + } + histos.fill(HIST("hTrackCount"), trackSel_FshCls); + fillSpectraStudyMCReco(trackSel_FshCls); + + if (!track.isGlobalTrack()) { + return false; + } + histos.fill(HIST("hTrackCount"), trackSel_GlobalTracks); + fillSpectraStudyMCReco(trackSel_GlobalTracks); + return true; } template inline void fillEventQA(const CollisionObject& collision, const TracksObject& tracks) { - if (!cfg.cFillEventQA) + if (!cfg.cFillEventQA) { return; + } histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("hCentFT0C"), collision.centFT0C(), spm.centWeight); histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("hCentNGlobal"), collision.centNGlobal(), spm.centWeight); @@ -1253,8 +1361,9 @@ struct FlowSP { template inline void fillTrackQA(const TrackObject& track) { - if (!cfg.cFillTrackQA) + if (!cfg.cFillTrackQA) { return; + } double weight = spm.wacc[ct][par] * spm.weff[ct][par] * spm.centWeight; @@ -1282,8 +1391,9 @@ struct FlowSP { template inline void fillPIDQA(const TrackObject& track) { - if (!cfg.cFillTrackQA) + if (!cfg.cFillTrackQA) { return; + } if constexpr (framework::has_type_v) { histos.fill(HIST(Charge[ct]) + HIST("pion/") + HIST("QA/") + HIST(Time[ft]) + HIST("hNsigmaTOF_pt"), track.pt(), track.tofNSigmaPi()); histos.fill(HIST(Charge[ct]) + HIST("pion/") + HIST("QA/") + HIST(Time[ft]) + HIST("hNsigmaTPC_pt"), track.pt(), track.tpcNSigmaPi()); @@ -1332,17 +1442,6 @@ struct FlowSP { } } - template - inline void fillPrimaryHistos(const McParticleObject& mcparticle) - { - - if (!mcparticle.isPhysicalPrimary()) { - registry.fill(HIST("trackMCReco/") + HIST(Time[ft]) + HIST(Charge[ct]) + HIST("hIsPhysicalPrimary"), 0, spm.centrality, mcparticle.pt()); - } else { - registry.fill(HIST("trackMCReco/") + HIST(Time[ft]) + HIST(Charge[ct]) + HIST("hIsPhysicalPrimary"), 1, spm.centrality, mcparticle.pt()); - } - } - template void fillAllQA(const TrackObject& track) { @@ -1381,27 +1480,34 @@ struct FlowSP { LOGF(info, "Size of mAcceptance: %i (should be 0)", (int)conf.mAcceptance.size()); } - if (cfg.cFillQABefore) + if (cfg.cFillQABefore) { fillEventQA(collision, tracks); + } loadCorrections(bc.timestamp()); spm.centrality = collision.centFT0C(); - if (cfg.cCentFT0Cvariant1) + if (cfg.cCentFT0Cvariant1) { spm.centrality = collision.centFT0CVariant1(); - if (cfg.cCentFT0M) + } + if (cfg.cCentFT0M) { spm.centrality = collision.centFT0M(); - if (cfg.cCentFV0A) + } + if (cfg.cCentFV0A) { spm.centrality = collision.centFV0A(); - if (cfg.cCentNGlobal) + } + if (cfg.cCentNGlobal) { spm.centrality = collision.centNGlobal(); + } - if (!eventSelected(collision, tracks.size())) + if (!eventSelected(collision, tracks.size())) { return; + } - if (!collision.isSelected()) // selected by ZDCQVectors task (checks signal in ZDC) --> only possible in data not MC + if (!collision.isSelected()) { // selected by ZDCQVectors task (checks signal in ZDC) --> only possible in data not MC return; + } histos.fill(HIST("hEventCount"), evSel_isSelectedZDC); // Always fill centrality histogram after event selections! @@ -1450,8 +1556,9 @@ struct FlowSP { histos.fill(HIST("QA/hFullEvPlaneRes"), spm.centrality, -1 * std::cos(spm.psiA - spm.psiC)); } - if (spm.centrality > cfg.cCentMax || spm.centrality < cfg.cCentMin) + if (spm.centrality > cfg.cCentMax || spm.centrality < cfg.cCentMin) { return; + } histos.fill(HIST("hEventCount"), evSel_CentCuts); @@ -1459,12 +1566,12 @@ struct FlowSP { // Only load once! // If not loaded set to 1 - if (cfg.cCCDBdir_QQ.value.empty() == false) { + if (!cfg.cCCDBdir_QQ.value.empty()) { if (!conf.clQQ) { - TList* hcorrList = ccdb->getForTimeStamp(cfg.cCCDBdir_QQ.value, bc.timestamp()); - conf.hcorrQQ = reinterpret_cast(hcorrList->FindObject("qAqCXY")); - conf.hcorrQQx = reinterpret_cast(hcorrList->FindObject("qAqCX")); - conf.hcorrQQy = reinterpret_cast(hcorrList->FindObject("qAqCY")); + auto* hcorrList = ccdb->getForTimeStamp(cfg.cCCDBdir_QQ.value, bc.timestamp()); + conf.hcorrQQ = dynamic_cast(hcorrList->FindObject("qAqCXY")); + conf.hcorrQQx = dynamic_cast(hcorrList->FindObject("qAqCX")); + conf.hcorrQQy = dynamic_cast(hcorrList->FindObject("qAqCY")); conf.clQQ = true; } spm.corrQQ = conf.hcorrQQ->GetBinContent(conf.hcorrQQ->FindBin(spm.centrality)); @@ -1473,19 +1580,20 @@ struct FlowSP { } double evPlaneRes = 1.; - if (cfg.cCCDBdir_SP.value.empty() == false) { + if (!cfg.cCCDBdir_SP.value.empty()) { if (!conf.clEvPlaneRes) { conf.hEvPlaneRes = ccdb->getForTimeStamp(cfg.cCCDBdir_SP.value, bc.timestamp()); conf.clEvPlaneRes = true; } evPlaneRes = conf.hEvPlaneRes->GetBinContent(conf.hEvPlaneRes->FindBin(spm.centrality)); - if (evPlaneRes < 0) + if (evPlaneRes < 0) { LOGF(fatal, " > 0 for centrality %.2f! Cannot determine resolution.. Change centrality ranges!!!", spm.centrality); + } evPlaneRes = std::sqrt(evPlaneRes); } spm.centWeight = 1.; - if (cfg.cCCDBdir_centrality.value.empty() == false) { + if (!cfg.cCCDBdir_centrality.value.empty()) { if (!conf.clCentrality) { conf.hCentrality = ccdb->getForTimeStamp(cfg.cCCDBdir_centrality.value, bc.timestamp()); conf.clCentrality = true; @@ -1523,8 +1631,9 @@ struct FlowSP { for (const auto& track : tracks) { - if (track.sign() == 0) + if (track.sign() == 0) { continue; + } histos.fill(HIST("hTrackCount"), trackSel_ZeroCharge); @@ -1534,11 +1643,12 @@ struct FlowSP { fillAllQA(track); } - if (!trackSelected(track, field)) + if (!trackSelected(track, field)) { continue; + } spm.meanPtWeight = 1.0; - if (cfg.cCCDBdir_meanPt.value.empty() == false) { + if (!cfg.cCCDBdir_meanPt.value.empty()) { if (!conf.clMeanPt) { conf.hMeanPt = ccdb->getForTimeStamp(cfg.cCCDBdir_meanPt.value, bc.timestamp()); conf.clMeanPt = true; @@ -1573,10 +1683,12 @@ struct FlowSP { } // Set weff and wacc for inclusive, negative and positive hadrons - if (!setCurrentParticleWeights(kInclusive, kUnidentified, phi, track.eta(), track.pt(), vtxz, spm.centrality)) + if (!setCurrentParticleWeights(kInclusive, kUnidentified, phi, track.eta(), track.pt(), vtxz, spm.centrality)) { continue; - if (!setCurrentParticleWeights(spm.charge, kUnidentified, phi, track.eta(), track.pt(), vtxz, spm.centrality)) + } + if (!setCurrentParticleWeights(spm.charge, kUnidentified, phi, track.eta(), track.pt(), vtxz, spm.centrality)) { continue; + } histos.fill(HIST("hTrackCount"), trackSel_ParticleWeights); @@ -1713,20 +1825,26 @@ struct FlowSP { spm.centrality = collision.centFT0C(); - if (cfg.cCentFT0Cvariant1) + if (cfg.cCentFT0Cvariant1) { spm.centrality = collision.centFT0CVariant1(); - if (cfg.cCentFT0M) + } + if (cfg.cCentFT0M) { spm.centrality = collision.centFT0M(); - if (cfg.cCentFV0A) + } + if (cfg.cCentFV0A) { spm.centrality = collision.centFV0A(); - if (cfg.cCentNGlobal) + } + if (cfg.cCentNGlobal) { spm.centrality = collision.centNGlobal(); + } - if (!eventSelected(collision, tracks.size())) + if (!eventSelected(collision, tracks.size())) { return; + } - if (!collision.isSelected()) // selected by ZDCQVectors task (checks signal in ZDC) --> only possible in data not MC + if (!collision.isSelected()) { // selected by ZDCQVectors task (checks signal in ZDC) --> only possible in data not MC return; + } histos.fill(HIST("hEventCount"), evSel_isSelectedZDC); // Always fill centrality histogram after event selections! @@ -1745,19 +1863,20 @@ struct FlowSP { // https://twiki.cern.ch/twiki/pub/ALICE/DirectedFlowAnalysisNote/vn_ZDC_ALICE_INT_NOTE_version02.pdf spm.psiFull = 1.0 * std::atan2(spm.qyA + spm.qyC, spm.qxA + spm.qxC); - if (spm.centrality > cfg.cCentMax || spm.centrality < cfg.cCentMin) + if (spm.centrality > cfg.cCentMax || spm.centrality < cfg.cCentMin) { return; + } // Load correlations and SP resolution needed for Scalar Product and event plane methods. // Only load once! // If not loaded set to 1 - if (cfg.cCCDBdir_QQ.value.empty() == false) { + if (!cfg.cCCDBdir_QQ.value.empty()) { if (!conf.clQQ) { - TList* hcorrList = ccdb->getForTimeStamp(cfg.cCCDBdir_QQ.value, bc.timestamp()); - conf.hcorrQQ = reinterpret_cast(hcorrList->FindObject("qAqCXY")); - conf.hcorrQQx = reinterpret_cast(hcorrList->FindObject("qAqCX")); - conf.hcorrQQy = reinterpret_cast(hcorrList->FindObject("qAqCY")); + auto* hcorrList = ccdb->getForTimeStamp(cfg.cCCDBdir_QQ.value, bc.timestamp()); + conf.hcorrQQ = dynamic_cast(hcorrList->FindObject("qAqCXY")); + conf.hcorrQQx = dynamic_cast(hcorrList->FindObject("qAqCX")); + conf.hcorrQQy = dynamic_cast(hcorrList->FindObject("qAqCY")); conf.clQQ = true; } spm.corrQQ = conf.hcorrQQ->GetBinContent(conf.hcorrQQ->FindBin(spm.centrality)); @@ -1766,19 +1885,20 @@ struct FlowSP { } double evPlaneRes = 1.; - if (cfg.cCCDBdir_SP.value.empty() == false) { + if (!cfg.cCCDBdir_SP.value.empty()) { if (!conf.clEvPlaneRes) { conf.hEvPlaneRes = ccdb->getForTimeStamp(cfg.cCCDBdir_SP.value, bc.timestamp()); conf.clEvPlaneRes = true; } evPlaneRes = conf.hEvPlaneRes->GetBinContent(conf.hEvPlaneRes->FindBin(spm.centrality)); - if (evPlaneRes < 0) + if (evPlaneRes < 0) { LOGF(fatal, " > 0 for centrality %.2f! Cannot determine resolution.. Change centrality ranges!!!", spm.centrality); + } evPlaneRes = std::sqrt(evPlaneRes); } spm.centWeight = 1.; - if (cfg.cCCDBdir_centrality.value.empty() == false) { + if (!cfg.cCCDBdir_centrality.value.empty()) { if (!conf.clCentrality) { conf.hCentrality = ccdb->getForTimeStamp(cfg.cCCDBdir_centrality.value, bc.timestamp()); conf.clCentrality = true; @@ -1799,8 +1919,9 @@ struct FlowSP { histos.fill(HIST("hPIDcounts"), trackPID, track.pt()); - if (track.sign() == 0) + if (track.sign() == 0) { continue; + } histos.fill(HIST("hTrackCount"), trackSel_ZeroCharge); @@ -1822,8 +1943,9 @@ struct FlowSP { } } - if (!trackSelected(track, field)) + if (!trackSelected(track, field)) { continue; + } // constrain angle to 0 -> [0,0+2pi] auto phi = RecoDecay::constrainAngle(track.phi(), 0); @@ -1913,7 +2035,7 @@ struct FlowSP { PROCESS_SWITCH(FlowSP, processDataPID, "Process analysis for non-derived data with PID", false); - void processMCReco(CC const& collision, aod::BCsWithTimestamps const&, TCs const& tracks, FilteredTCs const& filteredTracks, aod::McParticles const&) + void processMCReco(CC const& collision, aod::BCsWithTimestamps const&, TCs const& tracks, FilteredTCs const& filteredTracks, MCs const& McParts, aod::McCollisions const&) { auto bc = collision.template bc_as(); int standardMagField = 99999; @@ -1921,28 +2043,34 @@ struct FlowSP { spm.vz = collision.posZ(); spm.centrality = collision.centFT0C(); - if (cfg.cCentFT0Cvariant1) + if (cfg.cCentFT0Cvariant1) { spm.centrality = collision.centFT0CVariant1(); - if (cfg.cCentFT0M) + } + if (cfg.cCentFT0M) { spm.centrality = collision.centFT0M(); - if (cfg.cCentFV0A) + } + if (cfg.cCentFV0A) { spm.centrality = collision.centFV0A(); - if (cfg.cCentNGlobal) + } + if (cfg.cCentNGlobal) { spm.centrality = collision.centNGlobal(); + } - if (cfg.cFillQABefore) + if (cfg.cFillQABefore) { fillEventQA(collision, filteredTracks); + } - if (!eventSelected(collision, filteredTracks.size())) + if (!eventSelected(collision, filteredTracks.size())) { return; + } - if (spm.centrality > cfg.cCentMax || spm.centrality < cfg.cCentMin) + if (spm.centrality > cfg.cCentMax || spm.centrality < cfg.cCentMin) { return; + } histos.fill(HIST("hEventCount"), evSel_CentCuts); if (!collision.has_mcCollision()) { - LOGF(info, "No mccollision found for this collision"); return; } @@ -1951,15 +2079,17 @@ struct FlowSP { registry.fill(HIST("trackMCReco/hTrackSize_unFiltered"), tracks.size(), spm.centrality); registry.fill(HIST("trackMCReco/hTrackSize_Filtered"), filteredTracks.size(), spm.centrality); - for (const auto& track : filteredTracks) { + for (const auto& track : tracks) { - if (!track.has_mcParticle()) + if (!track.has_mcParticle()) { continue; + } - auto mcParticle = track.mcParticle(); + auto mcParticle = track.mcParticle_as(); - if (track.sign() == 0.0) + if (track.sign() == 0.0) { continue; + } histos.fill(HIST("hTrackCount"), trackSel_ZeroCharge); if (cfg.cFillWithMCParticle) { @@ -1993,24 +2123,23 @@ struct FlowSP { } } - fillPrimaryHistos(mcParticle); - if (spm.charge == kPositive) { - fillPrimaryHistos(mcParticle); - } else { - fillPrimaryHistos(mcParticle); + if (!trackSelected(track, field)) { + continue; } - if (!trackSelected(track, field)) + if (!mcParticle.isPhysicalPrimary()) { continue; + } - fillPrimaryHistos(mcParticle); + registry.fill(HIST("trackMCReco/incl/hPtMCPtTrack"), mcParticle.pt(), track.pt()); + registry.fill(HIST("trackMCReco/incl/hEtaMCEtaTrack"), mcParticle.eta(), track.eta()); if (spm.charge == kPositive) { - fillPrimaryHistos(mcParticle); - } else { - fillPrimaryHistos(mcParticle); + registry.fill(HIST("trackMCReco/pos/hPtMCPtTrack"), mcParticle.pt(), track.pt()); + registry.fill(HIST("trackMCReco/pos/hEtaMCEtaTrack"), mcParticle.eta(), track.eta()); + } else if (spm.charge == kNegative) { + registry.fill(HIST("trackMCReco/neg/hPtMCPtTrack"), mcParticle.pt(), track.pt()); + registry.fill(HIST("trackMCReco/neg/hEtaMCEtaTrack"), mcParticle.eta(), track.eta()); } - if (!mcParticle.isPhysicalPrimary()) - continue; if (cfg.cFillWithMCParticle) { fillMCPtHistos(mcParticle, mcParticle.pdgCode()); @@ -2035,6 +2164,60 @@ struct FlowSP { } } // end of track loop + + if (cfg.cDoGeneratedInReco) { + + auto mcCollision = collision.mcCollision(); + float vtxz = mcCollision.posZ(); + + // get McParticles which belong to mccollision + auto partSlice = McParts.sliceBy(partPerMcCollision, mcCollision.globalIndex()); + + for (const auto& particle : partSlice) { + + if (!particle.isPhysicalPrimary()) { + continue; + } + + auto pdgCode = particle.pdgCode(); + auto pdgInfo = pdg->GetParticle(pdgCode); + + if (std::abs(pdgInfo->Charge()) < 1) { + continue; + } + + spm.charge = (pdgInfo->Charge() > 0) ? kPositive : kNegative; + + int minVal = 100; + if (cfg.cFilterLeptons && std::abs(pdgCode) < minVal) { + continue; + } + + fillMCPtHistos(particle, pdgCode); + + registry.fill(HIST("trackMCGen/before/incl/phi_eta_vtxZ_gen"), particle.phi(), particle.eta(), vtxz); + + if (spm.charge == kPositive) { + registry.fill(HIST("trackMCGen/before/pos/phi_eta_vtxZ_gen"), particle.phi(), particle.eta(), vtxz); + } else { + registry.fill(HIST("trackMCGen/before/neg/phi_eta_vtxZ_gen"), particle.phi(), particle.eta(), vtxz); + } + + if (particle.eta() < -cfg.cTrackSelsEta || particle.eta() > cfg.cTrackSelsEta || particle.pt() < cfg.cTrackSelsPtmin || particle.pt() > cfg.cTrackSelsPtmax) { + continue; + } + + fillMCPtHistos(particle, pdgCode); + + registry.fill(HIST("trackMCGen/after/incl/phi_eta_vtxZ_gen"), particle.phi(), particle.eta(), vtxz); + + if (spm.charge == kPositive) { + registry.fill(HIST("trackMCGen/after/pos/phi_eta_vtxZ_gen"), particle.phi(), particle.eta(), vtxz); + } else { + registry.fill(HIST("trackMCGen/after/neg/phi_eta_vtxZ_gen"), particle.phi(), particle.eta(), vtxz); + } + } + } } PROCESS_SWITCH(FlowSP, processMCReco, "Process analysis for MC reconstructed events", false); @@ -2064,17 +2247,22 @@ struct FlowSP { auto filteredTrackSlice = filteredTracks.sliceBy(trackPerCollision, col.globalIndex()); spm.centrality = col.centFT0C(); - if (cfg.cCentFT0Cvariant1) + if (cfg.cCentFT0Cvariant1) { spm.centrality = col.centFT0CVariant1(); - if (cfg.cCentFT0M) + } + if (cfg.cCentFT0M) { spm.centrality = col.centFT0M(); - if (cfg.cCentFV0A) + } + if (cfg.cCentFV0A) { spm.centrality = col.centFV0A(); - if (cfg.cCentNGlobal) + } + if (cfg.cCentNGlobal) { spm.centrality = col.centNGlobal(); + } - if (cfg.cFillQABefore) + if (cfg.cFillQABefore) { fillEventQA(col, filteredTrackSlice); + } if (trackSlice.size() < 1) { colSelected = false; @@ -2095,20 +2283,23 @@ struct FlowSP { } // leave reconstructed collision loop - if (!colSelected) + if (!colSelected) { continue; + } float vtxz = mcCollision.posZ(); for (const auto& particle : partSlice) { - if (!particle.isPhysicalPrimary()) + if (!particle.isPhysicalPrimary()) { continue; + } auto pdgCode = particle.pdgCode(); auto pdgInfo = pdg->GetParticle(pdgCode); - if (std::abs(pdgInfo->Charge()) < 1) + if (std::abs(pdgInfo->Charge()) < 1) { continue; + } spm.charge = (pdgInfo->Charge() > 0) ? kPositive : kNegative; @@ -2127,8 +2318,9 @@ struct FlowSP { registry.fill(HIST("trackMCGen/before/neg/phi_eta_vtxZ_gen"), particle.phi(), particle.eta(), vtxz); } - if (particle.eta() < -cfg.cTrackSelsEta || particle.eta() > cfg.cTrackSelsEta || particle.pt() < cfg.cTrackSelsPtmin || particle.pt() > cfg.cTrackSelsPtmax) + if (particle.eta() < -cfg.cTrackSelsEta || particle.eta() > cfg.cTrackSelsEta || particle.pt() < cfg.cTrackSelsPtmin || particle.pt() > cfg.cTrackSelsPtmax) { continue; + } fillMCPtHistos(particle, pdgCode); @@ -2139,7 +2331,7 @@ struct FlowSP { } else { registry.fill(HIST("trackMCGen/after/neg/phi_eta_vtxZ_gen"), particle.phi(), particle.eta(), vtxz); } - } + } // end of parts } } PROCESS_SWITCH(FlowSP, processMCGen, "Process analysis for MC generated events", false); diff --git a/PWGCF/Flow/Tasks/flowTask.cxx b/PWGCF/Flow/Tasks/flowTask.cxx index 447ceeaa5d6..2471b77f282 100644 --- a/PWGCF/Flow/Tasks/flowTask.cxx +++ b/PWGCF/Flow/Tasks/flowTask.cxx @@ -101,6 +101,7 @@ struct FlowTask { Configurable> cfgPtPtGaps{"cfgPtPtGaps", {LongArrayDouble[0], 4, 2, {"subevent 1", "subevent 2", "subevent 3", "subevent 4"}, {"etamin", "etamax"}}, "{etamin,etamax} for all ptpt-subevents"}; O2_DEFINE_CONFIGURABLE(cfgEtaGapPtPtEnabled, bool, false, "switch of subevent pt-pt correlations") Configurable> cfgTrackCuts{"cfgTrackCuts", {TrackCutArray[0], 7, 2, {"chi2 per TPCcls", "TPC cluster", "TPC crossed rows", "ITS cluster", "DCAz", "DCAxy Nsigma", "DCAz Nsigma(override)"}, {"Nch", "Observable"}}, "separate Nch and observable track selections"}; + std::array, 7> trackCuts{}; enum TrackCut { // enum for labelledArray track selection kChi2prTPCcls = 0, // max chi2 per TPC clusters @@ -243,6 +244,14 @@ struct FlowTask { std::vector nPt; // fraction of particles in each pT bin in one event std::vector> listNptV2; std::vector> listPtX; + O2_DEFINE_CONFIGURABLE(cfgManualCk, bool, false, "Produce pt-pt variance") + O2_DEFINE_CONFIGURABLE(cfgNewPtContainer, bool, false, "Switch to new version of flowPtContainer. This switch will be removed after validation of new container.") + Configurable cfgNpcRegions{"cfgNpcRegions", {{"refN", "refP", "refMid", "refFull"}, {-0.8, 0.4, -0.4, -0.8}, {-0.4, 0.8, 0.4, 0.8}, {0, 0, 0, 0}, {1, 1, 1, 1}}, "Configurations for GFW regions"}; + Configurable cfgNpcPtVnCorrConfig{"cfgNpcPtVnCorrConfig", {{"refP {pt} refN {pt}", "refP {2} refMid {pt} refN {-2}", "refP {2} refMid {pt pt} refN {-2}", "refFull {pt}", "refFull {pt pt}", "refFull {pt pt pt}", "refFull {pt pt pt pt}", "refP {pt} refMid {pt} refN {pt}"}, {"ChGapPtPt", "ChGap22pt", "ChGap22ptpt", "ChFullPt", "ChFullPtPt", "ChFullPtPtPt", "ChFullPtPtPtPt", "ChGapPtPtPtABC"}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}}, "Configurations for vn-pt correlations based on new version flowPtContainer"}; + Configurable> cfgNpcGFWCorr{"cfgNpcGFWCorr", std::vector{"refN {2} refP {-2}", "refN {3} refP {-3}"}, "GFW corrconfig for new version flowPtContainer"}; + GFWRegions npcRegions; + GFWCorrConfigs npcPtVnCorrConfig; + GFW npcGfw; } cfgAdditionObs; ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2, 2.2, 2.4, 2.6, 2.8, 3, 3.5, 4, 5, 6, 8, 10}, "pt axis for histograms"}; @@ -252,7 +261,7 @@ struct FlowTask { ConfigurableAxis axisDCAxy{"axisDCAxy", {200, -1, 1}, "DCA_{xy} (cm)"}; Filter collisionFilter = (nabs(aod::collision::posZ) < cfgCutVertex) && (aod::cent::centFT0C > cfgCentFT0CMin) && (aod::cent::centFT0C < cfgCentFT0CMax); - Filter trackFilter = ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPtMin) && (aod::track::pt < cfgCutPtMax); + Filter trackFilter = ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == static_cast(true))) && (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPtMin) && (aod::track::pt < cfgCutPtMax); using FilteredCollisions = soa::Filtered>; using FilteredTracks = soa::Filtered>; // Filter for MCcollisions @@ -287,7 +296,7 @@ struct FlowTask { std::vector corrconfigs; GFWCorrConfigs gfwConfigs; std::vector corrconfigsPtVn; - TAxis* fPtAxis; + TAxis* fPtAxis = nullptr; TRandom3* fRndm = new TRandom3(0); std::vector> etagapsPtPt; std::vector>> bootstrapArray; @@ -324,15 +333,15 @@ struct FlowTask { double mMinSeconds{-1.}; std::unordered_map gHadronicRate; ctpRateFetcher mRateFetcher; - TH2* gCurrentHadronicRate; + TH2* gCurrentHadronicRate = nullptr; RCTFlagsChecker rctChecker{"CBT"}; // phi-EP correction std::vector funcEff; - TH1D* hFindPtBin; - TF1* funcV2; - TF1* funcV3; - TF1* funcV4; + TH1D* hFindPtBin = nullptr; + TF1* funcV2 = nullptr; + TF1* funcV3 = nullptr; + TF1* funcV4 = nullptr; void init(InitContext const&) { @@ -439,6 +448,13 @@ struct FlowTask { registry.add("hPtA_afterDptCut", "p_{T} distribution", {HistType::kTH1D, {axisPt}}); registry.add("hPtB_afterDptCut", "p_{T} distribution", {HistType::kTH1D, {axisPt}}); } + if (!cfgAdditionObs.cfgNewPtContainer && cfgAdditionObs.cfgManualCk) { + registry.add("ptpt_1sub1_1sub3_2pc", "", {HistType::kTProfile, {axisIndependent}}); + registry.add("pt_1sub1_1pc", "", {HistType::kTProfile, {axisIndependent}}); + registry.add("pt_1sub1_2pc", "", {HistType::kTProfile, {axisIndependent}}); + registry.add("pt_1sub3_1pc", "", {HistType::kTProfile, {axisIndependent}}); + registry.add("pt_1sub3_2pc", "", {HistType::kTProfile, {axisIndependent}}); + } if (doprocessMCGen) { registry.add("MCGen/MChPhi", "#phi distribution", {HistType::kTH1D, {axisPhi}}); registry.add("MCGen/MChEta", "#eta distribution", {HistType::kTH1D, {axisEta}}); @@ -659,14 +675,42 @@ struct FlowTask { // Mask 1: vn-[pT], 3: vn-[pT^2], 7: vn-[pT^3], 15: vn-[pT^4] gfwConfigs.SetpTCorrMasks(cfgUserPtVnCorrConfig->GetpTCorrMasks()); gfwConfigs.Print(); - fFCpt->setUseCentralMoments(cfgUseCentralMoments); - fFCpt->setUseGapMethod(true); if (!cfgUserIO.cfgUserPtVnEvWeightEnabled) fFCpt->setEventWeight(EventWeight::UnityWeight); else fFCpt->setEventWeight(EventWeight::TupleWeight); - fFCpt->initialise(axisIndependent, cfgMpar, gfwConfigs, cfgNbootstrap); - if (cfgEtaGapPtPtEnabled) { + if (cfgAdditionObs.cfgNewPtContainer) { + cfgAdditionObs.npcRegions.SetNames(cfgAdditionObs.cfgNpcRegions->GetNames()); + cfgAdditionObs.npcRegions.SetEtaMin(cfgAdditionObs.cfgNpcRegions->GetEtaMin()); + cfgAdditionObs.npcRegions.SetEtaMax(cfgAdditionObs.cfgNpcRegions->GetEtaMax()); + cfgAdditionObs.npcRegions.SetpTDifs(cfgAdditionObs.cfgNpcRegions->GetpTDifs()); + cfgAdditionObs.npcRegions.SetBitmasks(cfgAdditionObs.cfgNpcRegions->GetBitmasks()); + cfgAdditionObs.npcPtVnCorrConfig.SetCorrs(cfgAdditionObs.cfgNpcPtVnCorrConfig->GetCorrs()); + cfgAdditionObs.npcPtVnCorrConfig.SetHeads(cfgAdditionObs.cfgNpcPtVnCorrConfig->GetHeads()); + cfgAdditionObs.npcPtVnCorrConfig.SetpTDifs(cfgAdditionObs.cfgNpcPtVnCorrConfig->GetpTDifs()); + cfgAdditionObs.npcPtVnCorrConfig.SetpTCorrMasks(cfgAdditionObs.cfgNpcPtVnCorrConfig->GetpTCorrMasks()); + cfgAdditionObs.npcRegions.Print(); + cfgAdditionObs.npcPtVnCorrConfig.Print(); + fFCpt->initialise(axisIndependent, cfgAdditionObs.npcRegions, cfgAdditionObs.npcPtVnCorrConfig, cfgNbootstrap); + + for (int region = 0; region < cfgAdditionObs.npcRegions.GetSize(); ++region) { + cfgAdditionObs.npcGfw.AddRegion(cfgAdditionObs.npcRegions.GetNames()[region], cfgAdditionObs.npcRegions.GetEtaMin()[region], + cfgAdditionObs.npcRegions.GetEtaMax()[region], 1, + cfgAdditionObs.npcRegions.GetBitmasks()[region]); + } + std::vector npcGFWCorr = cfgAdditionObs.cfgNpcGFWCorr; + if (!npcGFWCorr.empty()) { + for (uint i = 0; i < npcGFWCorr.size(); i++) { + cfgAdditionObs.npcGfw.GetCorrelatorConfig(npcGFWCorr.at(i).c_str()); + } + } + cfgAdditionObs.npcGfw.CreateRegions(); + } else { + fFCpt->setUseCentralMoments(cfgUseCentralMoments); + fFCpt->setUseGapMethod(true); + fFCpt->initialise(axisIndependent, cfgMpar, gfwConfigs, cfgNbootstrap); + } + if (!cfgAdditionObs.cfgNewPtContainer && cfgEtaGapPtPtEnabled) { for (int i = 0; i < 4; ++i) { // o2-linter: disable=magic-number (maximum of 4 subevents) if (cfgPtPtGaps->getData()[i][0] < -1. || cfgPtPtGaps->getData()[i][1] < -1.) continue; @@ -748,31 +792,44 @@ struct FlowTask { funcV4->SetParameters(0.008845, 0.000259668, -3.24435e-06, 4.54837e-08, -6.01825e-10); } - if (cfgTrackCuts->getData()[kDCAxyNSigma][kTrCutObs]) { + readMatrix(cfgTrackCuts->getData(), trackCuts); + if (trackCuts[kDCAxyNSigma][kTrCutObs]) { cfgFuncParas.fPtDepDCAxy = new TF1("ptDepDCAxy", Form("[0]*%s", cfgFuncParas.cfgDCAxyFunc->c_str()), 0.001, 1000); - cfgFuncParas.fPtDepDCAxy->SetParameter(0, cfgTrackCuts->getData()[kDCAxyNSigma][kTrCutObs]); - LOGF(info, "DCAxy pt-dependence function: %s", Form("%0.1f * %s", cfgTrackCuts->getData()[kDCAxyNSigma][kTrCutObs], cfgFuncParas.cfgDCAxyFunc->c_str())); + cfgFuncParas.fPtDepDCAxy->SetParameter(0, trackCuts[kDCAxyNSigma][kTrCutObs]); + LOGF(info, "DCAxy pt-dependence function: %s", Form("%0.1f * %s", trackCuts[kDCAxyNSigma][kTrCutObs], cfgFuncParas.cfgDCAxyFunc->c_str())); } - if (cfgTrackCuts->getData()[kDCAxyNSigma][kTrCutNch]) { + if (trackCuts[kDCAxyNSigma][kTrCutNch]) { cfgFuncParas.fPtDepDCAxyForNch = new TF1("ptDepDCAxyForNch", Form("[0]*%s", cfgFuncParas.cfgDCAxyFunc->c_str()), 0.001, 1000); - cfgFuncParas.fPtDepDCAxyForNch->SetParameter(0, cfgTrackCuts->getData()[kDCAxyNSigma][kTrCutNch]); - LOGF(info, "DCAxy pt-dependence function for Nch: %s", Form("%0.1f * %s", cfgTrackCuts->getData()[kDCAxyNSigma][kTrCutNch], cfgFuncParas.cfgDCAxyFunc->c_str())); + cfgFuncParas.fPtDepDCAxyForNch->SetParameter(0, trackCuts[kDCAxyNSigma][kTrCutNch]); + LOGF(info, "DCAxy pt-dependence function for Nch: %s", Form("%0.1f * %s", trackCuts[kDCAxyNSigma][kTrCutNch], cfgFuncParas.cfgDCAxyFunc->c_str())); } - if (cfgTrackCuts->getData()[kDCAzNSigma][kTrCutObs]) { + if (trackCuts[kDCAzNSigma][kTrCutObs]) { cfgFuncParas.fPtDepDCAz = new TF1("ptDepDCAz", Form("[0]*%s", cfgFuncParas.cfgDCAzFunc->c_str()), 0.001, 1000); - cfgFuncParas.fPtDepDCAz->SetParameter(0, cfgTrackCuts->getData()[kDCAzNSigma][kTrCutObs]); - LOGF(info, "DCAz pt-dependence function: %s", Form("%0.1f * %s", cfgTrackCuts->getData()[kDCAzNSigma][kTrCutObs], cfgFuncParas.cfgDCAzFunc->c_str())); + cfgFuncParas.fPtDepDCAz->SetParameter(0, trackCuts[kDCAzNSigma][kTrCutObs]); + LOGF(info, "DCAz pt-dependence function: %s", Form("%0.1f * %s", trackCuts[kDCAzNSigma][kTrCutObs], cfgFuncParas.cfgDCAzFunc->c_str())); } - if (cfgTrackCuts->getData()[kDCAzNSigma][kTrCutNch]) { + if (trackCuts[kDCAzNSigma][kTrCutNch]) { cfgFuncParas.fPtDepDCAzForNch = new TF1("ptDepDCAzForNch", Form("[0]*%s", cfgFuncParas.cfgDCAzFunc->c_str()), 0.001, 1000); - cfgFuncParas.fPtDepDCAzForNch->SetParameter(0, cfgTrackCuts->getData()[kDCAzNSigma][kTrCutNch]); - LOGF(info, "DCAz pt-dependence function for Nch: %s", Form("%0.1f * %s", cfgTrackCuts->getData()[kDCAzNSigma][kTrCutNch], cfgFuncParas.cfgDCAzFunc->c_str())); + cfgFuncParas.fPtDepDCAzForNch->SetParameter(0, trackCuts[kDCAzNSigma][kTrCutNch]); + LOGF(info, "DCAz pt-dependence function for Nch: %s", Form("%0.1f * %s", trackCuts[kDCAzNSigma][kTrCutNch], cfgFuncParas.cfgDCAzFunc->c_str())); } if (!cfgEvSelRCTflags.value.empty()) { rctChecker.init(cfgEvSelRCTflags.value.c_str()); // override initialzation } } + template + void readMatrix(Array2D const& mat, P& array) + { + for (auto i = 0; i < static_cast(mat.rows); ++i) { + for (auto j = 0; j < static_cast(mat.cols); ++j) { + array[i][j] = mat(i, j); + } + } + + return; + } + void createOutputObjectsForRun(int runNumber) { const AxisSpec axisPhi{60, 0.0, constants::math::TwoPI, "#varphi"}; @@ -910,30 +967,30 @@ struct FlowTask { if (cfgUserIO.cfgAcceptance.value.empty() == false) { mAcceptance = ccdb->getForTimeStamp(cfgUserIO.cfgAcceptance, timestamp); if (mAcceptance) - LOGF(info, "Loaded acceptance weights from %s (%p)", cfgUserIO.cfgAcceptance.value.c_str(), (void*)mAcceptance); + LOGF(info, "Loaded acceptance weights from %s (%p)", cfgUserIO.cfgAcceptance.value.c_str(), static_cast(mAcceptance)); else - LOGF(warning, "Could not load acceptance weights from %s (%p)", cfgUserIO.cfgAcceptance.value.c_str(), (void*)mAcceptance); + LOGF(warning, "Could not load acceptance weights from %s (%p)", cfgUserIO.cfgAcceptance.value.c_str(), static_cast(mAcceptance)); } if (cfgUserIO.cfgEfficiency.value.empty() == false) { mEfficiency = ccdb->getForTimeStamp(cfgUserIO.cfgEfficiency, timestamp); if (mEfficiency == nullptr) { LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgUserIO.cfgEfficiency.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgUserIO.cfgEfficiency.value.c_str(), (void*)mEfficiency); + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgUserIO.cfgEfficiency.value.c_str(), static_cast(mEfficiency)); } if (cfgUserIO.cfgEfficiencyForNch.value.empty() == false) { mEfficiencyForNch = ccdb->getForTimeStamp(cfgUserIO.cfgEfficiencyForNch, timestamp); if (mEfficiencyForNch == nullptr) { LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgUserIO.cfgEfficiencyForNch.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgUserIO.cfgEfficiencyForNch.value.c_str(), (void*)mEfficiencyForNch); + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgUserIO.cfgEfficiencyForNch.value.c_str(), static_cast(mEfficiencyForNch)); } if (cfgAdditionObs.cfgDptDisEnable && cfgAdditionObs.cfgDptDishEvAvgMeanPt.value.empty() == false) { cfgAdditionObs.hEvAvgMeanPt = ccdb->getForTimeStamp(cfgAdditionObs.cfgDptDishEvAvgMeanPt, timestamp); if (cfgAdditionObs.hEvAvgMeanPt == nullptr) { LOGF(fatal, "Could not load mean pT histogram from %s", cfgAdditionObs.cfgDptDishEvAvgMeanPt.value.c_str()); } - LOGF(info, "Loaded mean pT histogram from %s (%p)", cfgAdditionObs.cfgDptDishEvAvgMeanPt.value.c_str(), (void*)cfgAdditionObs.hEvAvgMeanPt); + LOGF(info, "Loaded mean pT histogram from %s (%p)", cfgAdditionObs.cfgDptDishEvAvgMeanPt.value.c_str(), static_cast(cfgAdditionObs.hEvAvgMeanPt)); } if (cfgAdditionObs.cfgDptDisEnable && cfgAdditionObs.cfgDptDisSelectionSwitch > kNoDptCut) { if (cfgAdditionObs.cfgDptDisCutLow.value.empty() == false) { @@ -941,14 +998,14 @@ struct FlowTask { if (cfgAdditionObs.fDptDisCutLow == nullptr) { LOGF(fatal, "Could not load dptDis low cut histogram from %s", cfgAdditionObs.cfgDptDisCutLow.value.c_str()); } - LOGF(info, "Loaded dptDis low cut histogram from %s (%p)", cfgAdditionObs.cfgDptDisCutLow.value.c_str(), (void*)cfgAdditionObs.fDptDisCutLow); + LOGF(info, "Loaded dptDis low cut histogram from %s (%p)", cfgAdditionObs.cfgDptDisCutLow.value.c_str(), static_cast(cfgAdditionObs.fDptDisCutLow)); } if (cfgAdditionObs.cfgDptDisCutHigh.value.empty() == false) { cfgAdditionObs.fDptDisCutHigh = ccdb->getForTimeStamp(cfgAdditionObs.cfgDptDisCutHigh, timestamp); if (cfgAdditionObs.fDptDisCutHigh == nullptr) { LOGF(fatal, "Could not load dptDis high cut histogram from %s", cfgAdditionObs.cfgDptDisCutHigh.value.c_str()); } - LOGF(info, "Loaded dptDis high cut histogram from %s (%p)", cfgAdditionObs.cfgDptDisCutHigh.value.c_str(), (void*)cfgAdditionObs.fDptDisCutHigh); + LOGF(info, "Loaded dptDis high cut histogram from %s (%p)", cfgAdditionObs.cfgDptDisCutHigh.value.c_str(), static_cast(cfgAdditionObs.fDptDisCutHigh)); } } correctionsLoaded = true; @@ -1111,21 +1168,21 @@ struct FlowTask { template bool trackSelected(const TTrack& track) { - if (cfgTrackCuts->getData()[kDCAxyNSigma][kTrCutObs] && (std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxy->Eval(track.pt()))) + if (trackCuts[kDCAxyNSigma][kTrCutObs] && cfgFuncParas.fPtDepDCAxy != nullptr && (std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxy->Eval(track.pt()))) return false; - if (cfgTrackCuts->getData()[kDCAzNSigma][kTrCutObs] && (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAz->Eval(track.pt()))) + if (trackCuts[kDCAzNSigma][kTrCutObs] && cfgFuncParas.fPtDepDCAz != nullptr && (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAz->Eval(track.pt()))) return false; - return ((track.tpcNClsFound() >= cfgTrackCuts->getData()[kTPCclu][kTrCutObs]) && (track.tpcNClsCrossedRows() >= cfgTrackCuts->getData()[kTPCCrossedRows][kTrCutObs]) && (track.itsNCls() >= cfgTrackCuts->getData()[kITSclu][kTrCutObs]) && (track.tpcChi2NCl() < cfgTrackCuts->getData()[kChi2prTPCcls][kTrCutObs]) && (std::fabs(track.dcaZ()) < cfgTrackCuts->getData()[kDCAz][kTrCutObs])); + return ((track.tpcNClsFound() >= trackCuts[kTPCclu][kTrCutObs]) && (track.tpcNClsCrossedRows() >= trackCuts[kTPCCrossedRows][kTrCutObs]) && (track.itsNCls() >= trackCuts[kITSclu][kTrCutObs]) && (track.tpcChi2NCl() < trackCuts[kChi2prTPCcls][kTrCutObs]) && (std::fabs(track.dcaZ()) < trackCuts[kDCAz][kTrCutObs])); } template bool trackSelectedForNch(const TTrack& track) { - if (cfgTrackCuts->getData()[kDCAxyNSigma][kTrCutNch] && (std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxyForNch->Eval(track.pt()))) + if (trackCuts[kDCAxyNSigma][kTrCutNch] && cfgFuncParas.fPtDepDCAxyForNch != nullptr && (std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxyForNch->Eval(track.pt()))) return false; - if (cfgTrackCuts->getData()[kDCAzNSigma][kTrCutNch] && (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAzForNch->Eval(track.pt()))) + if (trackCuts[kDCAzNSigma][kTrCutNch] && cfgFuncParas.fPtDepDCAzForNch != nullptr && (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAzForNch->Eval(track.pt()))) return false; - return ((track.tpcNClsFound() >= cfgTrackCuts->getData()[kTPCclu][kTrCutNch]) && (track.tpcNClsCrossedRows() >= cfgTrackCuts->getData()[kTPCCrossedRows][kTrCutNch]) && (track.itsNCls() >= cfgTrackCuts->getData()[kITSclu][kTrCutNch]) && (track.tpcChi2NCl() < cfgTrackCuts->getData()[kChi2prTPCcls][kTrCutNch]) && (std::fabs(track.dcaZ()) < cfgTrackCuts->getData()[kDCAz][kTrCutNch])); + return ((track.tpcNClsFound() >= trackCuts[kTPCclu][kTrCutNch]) && (track.tpcNClsCrossedRows() >= trackCuts[kTPCCrossedRows][kTrCutNch]) && (track.itsNCls() >= trackCuts[kITSclu][kTrCutNch]) && (track.tpcChi2NCl() < trackCuts[kChi2prTPCcls][kTrCutNch]) && (std::fabs(track.dcaZ()) < trackCuts[kDCAz][kTrCutNch])); } template @@ -1256,6 +1313,9 @@ struct FlowTask { fGFW->Clear(); fFCpt->clearVector(); cfgAdditionObs.nPt.clear(); + if (cfgAdditionObs.cfgNewPtContainer) { + cfgAdditionObs.npcGfw.Clear(); + } if (cfgGetInteractionRate) { initHadronicRate(bc); double hadronicRate = mRateFetcher.fetch(ccdb.service, bc.timestamp(), mRunNumber, "ZNC hadronic") * 1.e-3; // @@ -1289,6 +1349,10 @@ struct FlowTask { double weffEventWithinGap08 = 0., weffEventSquareWithinGap08 = 0.; double sumPtsquareWsquareWithinGap08 = 0., sumPtWsquareWithinGap08 = 0.; double nTracksCorrected = 0; + double mpt_sub1 = 0; + double mpt_sub1_weffEv = 0; + double mpt_sub3 = 0; + double mpt_sub3_weffEv = 0; int magnetfield = 0; float independent = cent; float nTracksUncorrected = 0; @@ -1405,6 +1469,16 @@ struct FlowTask { weffEventWithinGap08 += weff; weffEventSquareWithinGap08 += weff * weff; } + if (!cfgAdditionObs.cfgNewPtContainer && cfgAdditionObs.cfgManualCk) { + if (track.eta() >= etagapsPtPt[0].first && track.eta() <= etagapsPtPt[0].second) { + mpt_sub1 += weff * track.pt(); + mpt_sub1_weffEv += weff; + } + if (track.eta() >= etagapsPtPt[2].first && track.eta() <= etagapsPtPt[2].second) { + mpt_sub3 += weff * track.pt(); + mpt_sub3_weffEv += weff; + } + } } if (cfgUserIO.cfgConsistentEventFlag && consistentEventVector.size() == 6) { // o2-linter: disable=magic-number (size match) acceptedTracks.nFull += 1; @@ -1421,10 +1495,23 @@ struct FlowTask { fGFW->Fill(track.eta(), fPtAxis->FindBin(track.pt()) - 1, track.phi(), wacc * weff, 2); if (withinPtPOI && withinPtRef) fGFW->Fill(track.eta(), fPtAxis->FindBin(track.pt()) - 1, track.phi(), wacc * weff, 4); - if (cfgUsePtRef && withinPtRef) - fillPtSums(track, weff); - if (!cfgUsePtRef && withinPtPOI) - fillPtSums(track, weff); + if (cfgAdditionObs.cfgNewPtContainer) { + if (cfgUsePtRef && withinPtRef) + fFCpt->fill(track.eta(), weff, track.pt(), 1); + if (!cfgUsePtRef && withinPtPOI) + fFCpt->fill(track.eta(), weff, track.pt(), 1); + if (withinPtRef) + cfgAdditionObs.npcGfw.Fill(track.eta(), fPtAxis->FindBin(track.pt()) - 1, track.phi(), wacc * weff, 1); + if (withinPtPOI) + cfgAdditionObs.npcGfw.Fill(track.eta(), fPtAxis->FindBin(track.pt()) - 1, track.phi(), wacc * weff, 2); + if (withinPtPOI && withinPtRef) + cfgAdditionObs.npcGfw.Fill(track.eta(), fPtAxis->FindBin(track.pt()) - 1, track.phi(), wacc * weff, 4); + } else { + if (cfgUsePtRef && withinPtRef) + fillPtSums(track, weff); + if (!cfgUsePtRef && withinPtPOI) + fillPtSums(track, weff); + } } registry.fill(HIST("hTrackCorrection2d"), nTracksUncorrected, nTracksCorrected); if (cfgUseNch && cfgUseNchCorrected) { @@ -1477,13 +1564,12 @@ struct FlowTask { if (weffEvent) { registry.fill(HIST("hMeanPt"), independent, ptSum / weffEvent, weffEvent); } - if (weffEventWithinGap08) + if (weffEventWithinGap08) { registry.fill(HIST("hMeanPtWithinGap08"), independent, ptSum_Gap08 / weffEventWithinGap08, cfgUserIO.cfgUserPtVnEvWeightEnabled ? weffEventWithinGap08 : 1.); - if (weffEventWithinGap08) bootstrapArray[sampleIndex][kMeanPtWithinGap08]->Fill(independent, ptSum_Gap08 / weffEventWithinGap08, 1.0); - // c22_gap8 * pt_withGap8 - if (weffEventWithinGap08) + // c22_gap8 * pt_withGap8 fillpTvnProfile(corrconfigs.at(7), ptSum_Gap08, weffEventWithinGap08, HIST("c22_gap08_Weff"), HIST("c22_gap08_trackMeanPt"), independent); + } // PtVariance if (weffEventDiffWithGap08) { registry.fill(HIST("PtVariance_partA_WithinGap08"), independent, @@ -1494,6 +1580,25 @@ struct FlowTask { weffEventDiffWithGap08); } + if (!cfgAdditionObs.cfgNewPtContainer && cfgAdditionObs.cfgManualCk) { + // 2pc event weight: + double ev2pc = mpt_sub1_weffEv * mpt_sub3_weffEv; + // manual pt-pt variance + if (mpt_sub1_weffEv) { + mpt_sub1 = mpt_sub1 / mpt_sub1_weffEv; + registry.fill(HIST("pt_1sub1_1pc"), independent, mpt_sub1, cfgUserIO.cfgUserPtVnEvWeightEnabled ? mpt_sub1_weffEv : 1.); + } + if (mpt_sub3_weffEv) { + mpt_sub3 = mpt_sub3 / mpt_sub3_weffEv; + registry.fill(HIST("pt_1sub3_1pc"), independent, mpt_sub3, cfgUserIO.cfgUserPtVnEvWeightEnabled ? mpt_sub3_weffEv : 1.); + } + if (mpt_sub1_weffEv && mpt_sub3_weffEv) { + registry.fill(HIST("ptpt_1sub1_1sub3_2pc"), independent, mpt_sub1 * mpt_sub3, cfgUserIO.cfgUserPtVnEvWeightEnabled ? ev2pc : 1.); + registry.fill(HIST("pt_1sub1_2pc"), independent, mpt_sub1, cfgUserIO.cfgUserPtVnEvWeightEnabled ? ev2pc : 1.); + registry.fill(HIST("pt_1sub3_2pc"), independent, mpt_sub3, cfgUserIO.cfgUserPtVnEvWeightEnabled ? ev2pc : 1.); + } + } + // v02 if (cfgAdditionObs.cfgV02Enabled && weffEventFrac > 0) { for (auto ipt = 0; ipt < fPtAxis->GetNbins(); ipt++) { @@ -1508,7 +1613,11 @@ struct FlowTask { fillFC(corrconfigs.at(l_ind), independent, lRandom); } // Filling pt Container - fillPtContainers(independent, lRandom); + if (cfgAdditionObs.cfgNewPtContainer) { + fFCpt->fillPtProfiles(cfgAdditionObs.npcGfw, independent, lRandom); + } else { + fillPtContainers(independent, lRandom); + } } PROCESS_SWITCH(FlowTask, processData, "Process analysis for non-derived data", true); @@ -1569,9 +1678,8 @@ struct FlowTask { ptSum_Gap08 += mcParticle.pt(); count_Gap08 += 1.; } - } - if (withinPtRef) fGFW->Fill(mcParticle.eta(), fPtAxis->FindBin(mcParticle.pt()) - 1, mcParticle.phi(), 1., 1); + } if (withinPtPOI) fGFW->Fill(mcParticle.eta(), fPtAxis->FindBin(mcParticle.pt()) - 1, mcParticle.phi(), 1., 2); if (withinPtPOI && withinPtRef) diff --git a/PWGCF/Flow/Tasks/flowTransmutation.cxx b/PWGCF/Flow/Tasks/flowTransmutation.cxx new file mode 100644 index 00000000000..fa0fe8210f4 --- /dev/null +++ b/PWGCF/Flow/Tasks/flowTransmutation.cxx @@ -0,0 +1,636 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file flowTransmutation.cxx +/// \author Vojtech Machacek (vojtech.machacek@cern.ch) +/// \since Oct/05/2026 +/// \brief Task to measure flow observables with cumulants method in transmuted events. + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::aod::rctsel; +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; // NOLINT(bugprone-macro-parentheses) +static constexpr std::array, 7> LongArrayFloat = {{{{2.5, 2.5}}, {{50.0, 50.0}}, {{70.0, 70.0}}, {{5.0, 5.0}}, {{7.0, 7.0}}, {{7.0, 7.0}}, {{0., 0.}}}}; +template +auto readMatrix(Array2D const& mat, P& array) +{ + for (auto i = 0; i < static_cast(mat.rows); ++i) { + for (auto j = 0; j < static_cast(mat.cols); ++j) { + array[i][j] = mat(i, j); + } + } + return; +} +struct FlowTransmutation { + struct : ConfigurableGroup{ + O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FV0A; 4:NTPV; 5:NGlobal; 6:MFT") + O2_DEFINE_CONFIGURABLE(cfgCutVertex, float, 10.0f, "Accepted z-vertex range") + O2_DEFINE_CONFIGURABLE(cfgEvSelRCTflags, std::string, "CBT", "keep empty to disable, usage: 'CBT', 'CBT_hadronPID' ") + O2_DEFINE_CONFIGURABLE(cfgEvSelkIsGoodITSLayersFlag, int, 0, "0: Disabled; 1: kIsGoodITSLayersAll; 2: kIsGoodITSLayer0123; 3: kIsGoodITSLayer3") + O2_DEFINE_CONFIGURABLE(cfgEvSelUsage, bool, true, "Use event selection cuts: (0/1)") + O2_DEFINE_CONFIGURABLE(cfgEvSel8, bool, true, "rejects collisions which are not selected by sel8") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoSameBunchPileup, bool, true, "rejects collisions which are associated with the same found-by-T0 bunch crossing") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoITSROFrameBorder, bool, true, "reject events at ITS ROF border") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoTimeFrameBorder, bool, true, "reject events at TF border") + O2_DEFINE_CONFIGURABLE(cfgEvSelkIsGoodZvtxFT0vsPV, bool, true, "removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference, use this cut at low multiplicities with caution") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoCollInTimeRangeStandard, bool, true, "no collisions in specified time range") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoCollInRofStandard, bool, true, "no other collisions in this Readout Frame with per-collision multiplicity above threshold") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoHighMultCollInPrevRof, bool, true, "veto an event if FT0C amplitude in previous ITS ROF is above threshold")} cfgEventSelection; + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgEvSelMultCorrelation, bool, true, "Multiplicity correlation cut") + O2_DEFINE_CONFIGURABLE(cfgGlobalMultCentCutEnabled, bool, true, "Enable global multiplicity vs centrality cut") + O2_DEFINE_CONFIGURABLE(cfgGlobalMultCentLowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for global multiplicity vs centrality lower cut"); + O2_DEFINE_CONFIGURABLE(cfgGlobalMultCentHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 10.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for global multiplicity vs centrality upper cut"); + Configurable> cfgGlobalMultCentCutPars{"cfgGlobalMultCentCutPars", std::vector{177.409, -5.06256, 0.0695392, -0.000539089, 0.00000177447, 27.3677, -0.427405, 0.00452115, -0.0000382143, 1.13518e-7}, "Global multiplicity vs centrality cut values"}; + O2_DEFINE_CONFIGURABLE(cfgGlobalMultPVMultCutEnabled, bool, true, "Enable global multiplicity vs PV multiplicity cut"); + O2_DEFINE_CONFIGURABLE(cfgGlobalMultPVMultLowCutFunction, std::string, "[0]+[1]*x - 5.*([2]+[3]*x)", "Functional for global multiplicity vs PV multiplicity correlation lower cut"); + O2_DEFINE_CONFIGURABLE(cfgGlobalMultPVMultHighCutFunction, std::string, "[0]+[1]*x + 5.*([2]+[3]*x)", "Functional for global multiplicity vs PV multiplicity correlation upper cut"); + Configurable> cfgGlobalMultPVMultCutPars{"cfgGlobalMultPVMultCutPars", std::vector{-0.0893236, 0.721511, 2.05725, 0.0244317}, "global multiplicity vs PV multiplicity cut parameter values"}; + O2_DEFINE_CONFIGURABLE(cfgGlobalMultV0AMultCutEnabled, bool, true, "Enable global multiplicity vs V0A multiplicity cut"); + O2_DEFINE_CONFIGURABLE(cfgGlobalMultV0AMultLowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for global multiplicity vs V0A multiplicity correlation lower cut"); + O2_DEFINE_CONFIGURABLE(cfgGlobalMultV0AMultHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 4.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for global multiplicity vs V0A multiplicity correlation upper cut"); + Configurable> cfgGlobalMultV0AMultCutPars{"cfgGlobalMultV0AMultCutPars", std::vector{485.385, 191.561, 0.296174, -0.00282594, 0.00000497112, 370.073, 102.096, -1.00597, 0.00405269, -0.00000520521}, "Global multiplicity vs V0A multiplicity cut parameter values"}; + O2_DEFINE_CONFIGURABLE(cfgPVMultCentCutEnabled, bool, true, "Enable primary vertex multiplicity vs centrality cut"); + O2_DEFINE_CONFIGURABLE(cfgPVMultCentLowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for primary vertex multiplicity vs centrality upper cut"); + O2_DEFINE_CONFIGURABLE(cfgPVMultCentHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 10.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for primary vertex multiplicity vs centrality lower cut"); + Configurable> cfgPVMultCentCutCutPars{"cfgPVMultCentCutPars", std::vector{245.882, -6.90255, 0.0930817, -0.000710842, 0.00000231095, 35.9647, -0.530632, 0.00513954, -0.0000411738, 1.10106e-7}, "Primary vertex multiplicity vs centrality cut parameter values"}; + } cfgMultiplicityCorrelationCut; + O2_DEFINE_CONFIGURABLE(cfgNUAWeightsRunByRun, bool, true, "Fill NUA weights run by run") + O2_DEFINE_CONFIGURABLE(cfgGetNumberOfCorr, bool, false, "Get histograms with number of correlations") + Configurable> cfgFirstFillRunsList{"cfgFirstFillRunsList", std::vector{}, "First run of the fill for time-dependent analysis, keep empty to disable"}; + O2_DEFINE_CONFIGURABLE(cfgCutPtMax, float, 10.0f, "Maximal pT for all tracks") + O2_DEFINE_CONFIGURABLE(cfgCutPtMin, float, 0.2f, "Minimal pT for all tracks") + O2_DEFINE_CONFIGURABLE(cfgCutPtPOIMax, float, 3.0f, "Maximal pT for poi tracks") + O2_DEFINE_CONFIGURABLE(cfgCutPtPOIMin, float, 0.2f, "Minimal pT for poi tracks") + O2_DEFINE_CONFIGURABLE(cfgCutEta, float, 0.8f, "Eta range for tracks") + O2_DEFINE_CONFIGURABLE(cfgCutDCAxy, std::string, "(0.0026+0.005/(x^1.01))", "function for pT dependent DCAxy cut, keep empty for const. cut based on DCAxy (Nsigma)") + O2_DEFINE_CONFIGURABLE(cfgCutDCAz, std::string, "(0.0026+0.005/(x^1.01))", "function for pT dependent DCAz cut, keep empty for const. cut based on DCAz (Nsigma)") + Configurable> cfgRemovedRunList{"cfgRemovedRunList", std::vector{-1}, "excluded run numbers"}; + Configurable> cfgTrackCuts{"cfgTrackCuts", {LongArrayFloat.front().data(), 7, 2, {"chi2 per TPCcls", "TPC cluster", "TPC crossed rows", "ITS cluster", "DCAxy (Nsigma)", "DCAz (Nsigma)", "DCAz (cm) override"}, {"Nch", "Observable"}}, "separate Nch and observable track selections"}; + ConfigurableAxis binningNch{"binningNch", {500, 0, 500}, "binning for N_{ch} histograms"}; + ConfigurableAxis binningTime{"binningTime", {12, 0, 12}, "binning for time-dependent histograms transmutation QA"}; + ConfigurableAxis binningPt{"binningPt", {VARIABLE_WIDTH, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2, 2.2, 2.4, 2.6, 2.8, 3, 3.5, 4, 5, 6, 8, 10}, "binning for pT histograms"}; + HistogramRegistry registryQA{"registryQA"}; + HistogramRegistry registryCorr{"registryCorr"}; + Filter collisionFilter = (nabs(aod::collision::posZ) < cfgEventSelection.cfgCutVertex); + Filter trackFilter = ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPtMin) && (aod::track::pt < cfgCutPtMax); + Service ccdb{}; + RCTFlagsChecker rctChecker{"CBT"}; + enum TrackSelectionGroup { + TrkSelNch = 0, + TrkSelObs, + TrkCounter + }; + enum TrackCut { + Chi2prTPCcls = 0, // max chi2 per TPC clusters + TPCclu, // minimum TPC found clusters + TPCCrossedRows, // minimum TPC crossed rows + ITSclu, // minimum ITS found clusters + DCAxyNsigma, // cut on number of sigma deviations from expected DCA in the transverse direction, nsigma=7 is the same with global track; set cfgCutDCAxy to "1" to change the cut to a fixed one + DCAzNsigma, // cut on number of sigma deviations from expected DCA in the transverse direction, nsigma=7 is the same with global track; set cfgCutDCAxy to "1" to change the cut to a fixed one + DCAz, // constant DCAz cut, set to 0 to override with pT dependent one + Count_TrackCut + }; + enum CentEstimators { + CentFT0C = 0, + CentFT0CVariant1, + CentFT0M, + CentFV0A, + CentNTPV, + CentNGlobal, + CentMFT, + // Count the total number of enum + Count_CentEstimators + }; + enum GoodITSLayersFlag { + Disabled = 0, + ITSLayersAll, + ITSLayer0123, + ITSLayer3, + Count_ITSLayersFlag + }; + enum EventSelectionCuts { + FilteredEvent = 1, + Sel8, + RCTflags, + IsGoodITSLayers, + NoSameBunchPileup, + NoITSROFrameBorder, + NoTimeFrameBorder, + IsGoodZvtxFT0vsPV, + NoCollInTimeRangeStandard, + NoCollInRofStandard, + NoHighMultCollInPrevRof, + MultCorrelation, + BadRunsRemoved + }; + enum EtaGapsSettings { + NoGap = -1, + Gap00, + Gap02, + Gap04, + Gap06, + Gap08, + Gap10, + Gap12, + Gap14, + EtaGapCounter + }; + enum QAFillTime { + BeforeSel, + AfterSel + }; + struct EventCut { + bool enabled; + int histBin; + int flag; // just store the enum + }; + struct TrackRegion { + float forward; + float backward; + float gap; + }; + static constexpr std::array FillTimeName = {"beforeEventSelQA/", "afterEventSelQA/"}; + static constexpr std::array FillTrackName = {"cutNch/", "cutObs/"}; + std::array eventSelectionCuts{}; + std::array, 7> trackSel{}; + TF1* fGlobalMultPVMultLowCut = nullptr; + TF1* fGlobalMultPVMultHighCut = nullptr; + TF1* fGlobalMultV0AMultLowCut = nullptr; + TF1* fGlobalMultV0AMultHighCut = nullptr; + TF1* fGlobalMultCentLowCut = nullptr; + TF1* fGlobalMultCentHighCut = nullptr; + TF1* fPVMultCentLowCut = nullptr; + TF1* fPVMultCentHighCut = nullptr; + TH1D* hEff = nullptr; + std::array fDCAzCut{}; + std::array fDCAxyCut{}; + std::vector runNumbers; + std::map> th3sPerRun; + int firstRunOfFill = -1; + int getCorrectITSLayersFlag(int flag) + { + switch (flag) { + case ITSLayersAll: + return o2::aod::evsel::kIsGoodITSLayersAll; + case ITSLayer0123: + return o2::aod::evsel::kIsGoodITSLayer0123; + case ITSLayer3: + return o2::aod::evsel::kIsGoodITSLayer3; + default: + return 0; // Disabled + } + } + void init(InitContext const&) + { + AxisSpec axisVertex{40, -20, 20, "Vtxz (cm)"}; + AxisSpec axisPhi{60, 0.0, constants::math::TwoPI, "#varphi"}; + AxisSpec axisEta{40, -1., 1., "#eta"}; + AxisSpec axisCent{100, 0., 100., "Centrality"}; + AxisSpec axisT0C = {1000, 0, 50000, "N_{ch} (T0C)"}; + AxisSpec axisT0A = {1800, 0, 180000, "N_{ch} (T0A)"}; + AxisSpec axisV0A = {1800, 0, 180000, "N_{ch} (V0A)"}; + AxisSpec axisNch = {binningNch, "N_{ch}"}; + AxisSpec axisPt = {binningPt, "p_{T} (GeV/c)"}; + registryQA.add("cutNch/hTrackPhi", "Track #varphi distribution;#varphi;Count", {HistType::kTH1D, {axisPhi}}); + registryQA.add("cutNch/hTrackEta", "Track #eta distribution;#eta;Count", {HistType::kTH1D, {axisEta}}); + registryQA.add("cutNch/hPt", "p_{T} distribution for reconstructed particles in a given pT range;p_{T};Counts", {HistType::kTH1D, {axisPt}}); + registryQA.add("cutNch/hChi2prTPCcls", "#chi^{2}/cluster for the TPC track segment;#chi^{2}/cluster;Counts", {HistType::kTH1D, {{100, 0., 5.}}}); + registryQA.add("cutNch/hChi2prITScls", "#chi^{2}/cluster for the ITS track;#chi^{2}/cluster;Counts", {HistType::kTH1D, {{100, 0., 50.}}}); + registryQA.add("cutNch/hnTPCClu", "Number of found TPC clusters", {HistType::kTH1D, {{100, 40, 180}}}); + registryQA.add("cutNch/hnITSClu", "Number of found ITS clusters", {HistType::kTH1D, {{100, 0, 20}}}); + registryQA.add("cutNch/hnTPCCrossedRow", "Number of crossed TPC Rows", {HistType::kTH1D, {{100, 40, 180}}}); + registryQA.add("cutNch/hDCAz", "DCAz after cuts; DCAz (cm); p_{T}", {HistType::kTH2D, {{200, -1., 1.}, {200, 0, 5}}}); + registryQA.add("cutNch/hDCAxy", "DCAxy after cuts; DCAxy (cm); p_{T}", {HistType::kTH2D, {{200, -1., 1.}, {200, 0, 5}}}); + registryQA.add("cutNch/hPhiEtaVtxZ", ";#varphi;#eta;v_{z}", {HistType::kTH3D, {axisPhi, {32, -0.8, 0.8}, {40, -10, 10}}}); + registryQA.addClone("cutNch/", "cutObs/"); + registryQA.add("hVtxZ", "Vertex Z distribution;Vtxz (cm);Count", {HistType::kTH1D, {axisVertex}}); + registryQA.add("hCentrality", "Centrality distribution;Centrality;Count", {HistType::kTH1D, {axisCent}}); + registryQA.add("hEventCount", "Number of Event;; Count", {HistType::kTH1D, {{13, 0.5, 13.5}}}); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(FilteredEvent, "Filtered event"); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(Sel8, "after sel8"); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(RCTflags, "RCTflags"); + std::array itsLayersFlag = {"ITSLayers-Disabled", "ITSLayersAll", "ITSLayers0123", "ITSLayer3"}; + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(IsGoodITSLayers, Form("kIsGood%s", itsLayersFlag[cfgEventSelection.cfgEvSelkIsGoodITSLayersFlag].c_str())); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(NoSameBunchPileup, "kNoSameBunchPileup"); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(NoITSROFrameBorder, "kNoITSROFrameBorder"); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(NoTimeFrameBorder, "kNoTimeFrameBorder"); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(IsGoodZvtxFT0vsPV, "kIsGoodZvtxFT0vsPV"); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(NoCollInTimeRangeStandard, "kNoCollInTimeRangeStandard"); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(NoCollInRofStandard, "kNoCollInRofStandard"); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(NoHighMultCollInPrevRof, "kNoHighMultCollInPrevRof"); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(MultCorrelation, "MultCorrelation"); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(BadRunsRemoved, "BadRunsRemoved"); + registryQA.add("beforeEventSelQA/globalTracks_centT0C", "before event selection;FT0C centrality (%);mulplicity global tracks", {HistType::kTH2D, {axisCent, axisNch}}); + registryQA.add("beforeEventSelQA/PVTracks_centT0C", "before event selection;FT0C centrality (%);mulplicity PV tracks", {HistType::kTH2D, {axisCent, axisNch}}); + registryQA.add("beforeEventSelQA/globalTracks_PVTracks", "before event selection;mulplicity PV tracks;mulplicity global tracks", {HistType::kTH2D, {axisNch, axisNch}}); + registryQA.add("beforeEventSelQA/globalTracks_multT0A", "before event selection;mulplicity T0A;mulplicity global tracks", {HistType::kTH2D, {axisT0A, axisNch}}); + registryQA.add("beforeEventSelQA/globalTracks_multV0A", "before event selection;mulplicity V0A;mulplicity global tracks", {HistType::kTH2D, {axisT0A, axisNch}}); + registryQA.add("beforeEventSelQA/multV0A_multT0A", "before event selection;mulplicity T0A;mulplicity V0A", {HistType::kTH2D, {axisT0A, axisT0A}}); + registryQA.add("beforeEventSelQA/multT0C_centT0C", "before event selection;FT0C centrality (%);mulplicity T0C", {HistType::kTH2D, {axisCent, axisT0C}}); + if (cfgEventSelection.cfgEvSelUsage) { + registryQA.addClone("beforeEventSelQA/", "afterEventSelQA/"); + } + eventSelectionCuts = { + EventCut({.enabled = (cfgEventSelection.cfgEvSelkIsGoodITSLayersFlag != Disabled), .histBin = IsGoodITSLayers, .flag = getCorrectITSLayersFlag(cfgEventSelection.cfgEvSelkIsGoodITSLayersFlag)}), + EventCut({.enabled = cfgEventSelection.cfgEvSelkNoSameBunchPileup, .histBin = NoSameBunchPileup, .flag = o2::aod::evsel::kNoSameBunchPileup}), + EventCut({.enabled = cfgEventSelection.cfgEvSelkNoITSROFrameBorder, .histBin = NoITSROFrameBorder, .flag = o2::aod::evsel::kNoITSROFrameBorder}), + EventCut({.enabled = cfgEventSelection.cfgEvSelkNoTimeFrameBorder, .histBin = NoTimeFrameBorder, .flag = o2::aod::evsel::kNoTimeFrameBorder}), + EventCut({.enabled = cfgEventSelection.cfgEvSelkIsGoodZvtxFT0vsPV, .histBin = IsGoodZvtxFT0vsPV, .flag = o2::aod::evsel::kIsGoodZvtxFT0vsPV}), + EventCut({.enabled = cfgEventSelection.cfgEvSelkNoCollInTimeRangeStandard, .histBin = NoCollInTimeRangeStandard, .flag = o2::aod::evsel::kNoCollInTimeRangeStandard}), + EventCut({.enabled = cfgEventSelection.cfgEvSelkNoCollInRofStandard, .histBin = NoCollInRofStandard, .flag = o2::aod::evsel::kNoCollInRofStandard}), + EventCut({.enabled = cfgEventSelection.cfgEvSelkNoHighMultCollInPrevRof, .histBin = NoHighMultCollInPrevRof, .flag = o2::aod::evsel::kNoHighMultCollInPrevRof})}; + if (cfgEventSelection.cfgEvSelUsage) { + LOGF(info, "Event selection cuts are enabled"); + LOGF(info, "Sel8 is %senabled", (cfgEventSelection.cfgEvSel8) ? "" : "not "); + if (!cfgEventSelection.cfgEvSelRCTflags.value.empty()) { + rctChecker.init(cfgEventSelection.cfgEvSelRCTflags.value); + LOGF(info, "RCT is %senabled", (!cfgEventSelection.cfgEvSelRCTflags.value.empty()) ? "" : "not "); + LOGF(info, "RCT selection flag: %s", cfgEventSelection.cfgEvSelRCTflags.value.c_str()); + } + for (const auto& cut : eventSelectionCuts) { + LOGF(info, "Flag %d is %senabled", cut.histBin, (cut.enabled) ? "" : "not "); + } + if (cfgMultiplicityCorrelationCut.cfgEvSelMultCorrelation) { + LOGF(info, "Multiplicity correlation cuts are enabled"); + if (cfgMultiplicityCorrelationCut.cfgGlobalMultPVMultCutEnabled) { + LOGF(info, "Global multiplicity vs PV multiplicity cut is enabled: %f", axisNch.binEdges.back()); + fGlobalMultPVMultHighCut = new TF1("fGlobalMultPVMultHighCut", cfgMultiplicityCorrelationCut.cfgGlobalMultPVMultHighCutFunction->c_str(), 0, axisNch.binEdges.back()); + fGlobalMultPVMultHighCut->SetParameters(cfgMultiplicityCorrelationCut.cfgGlobalMultPVMultCutPars.value.data()); + fGlobalMultPVMultLowCut = new TF1("fGlobalMultPVMultLowCut", cfgMultiplicityCorrelationCut.cfgGlobalMultPVMultLowCutFunction->c_str(), 0, axisNch.binEdges.back()); + fGlobalMultPVMultLowCut->SetParameters(cfgMultiplicityCorrelationCut.cfgGlobalMultPVMultCutPars.value.data()); + LOGF(info, "Global multiplicity vs PV multiplicity Upper Cut: %s", cfgMultiplicityCorrelationCut.cfgGlobalMultPVMultHighCutFunction->c_str()); + LOGF(info, "Global multiplicity vs PV multiplicity Lower Cut: %s", cfgMultiplicityCorrelationCut.cfgGlobalMultPVMultLowCutFunction->c_str()); + LOGF(info, "Global multiplicity vs PV multiplicity cut parameters:"); + for (std::size_t i = 0; i < cfgMultiplicityCorrelationCut.cfgGlobalMultPVMultCutPars.value.size(); ++i) { + LOGF(info, "fGlobalMultPVMultLowCut par %zu = %g", i, fGlobalMultPVMultLowCut->GetParameter(i)); + LOGF(info, "fGlobalMultPVMultHighCut par %zu = %g", i, fGlobalMultPVMultHighCut->GetParameter(i)); + } + } + if (cfgMultiplicityCorrelationCut.cfgGlobalMultV0AMultCutEnabled) { + LOGF(info, Form("Global multiplicity vs V0A multiplicity cut is enabled: %f ", axisV0A.binEdges.back())); + + fGlobalMultV0AMultHighCut = new TF1("fGlobalMultV0AMultHighCut", cfgMultiplicityCorrelationCut.cfgGlobalMultV0AMultHighCutFunction->c_str(), 0, axisV0A.binEdges.back()); + fGlobalMultV0AMultHighCut->SetParameters(cfgMultiplicityCorrelationCut.cfgGlobalMultV0AMultCutPars.value.data()); + fGlobalMultV0AMultLowCut = new TF1("fGlobalMultV0AMultLowCut", cfgMultiplicityCorrelationCut.cfgGlobalMultV0AMultLowCutFunction->c_str(), 0, axisV0A.binEdges.back()); + fGlobalMultV0AMultLowCut->SetParameters(cfgMultiplicityCorrelationCut.cfgGlobalMultV0AMultCutPars.value.data()); + LOGF(info, "Global multiplicity vs V0A multiplicity Upper Cut: %s", cfgMultiplicityCorrelationCut.cfgGlobalMultV0AMultHighCutFunction->c_str()); + LOGF(info, "Global multiplicity vs V0A multiplicity Lower Cut: %s", cfgMultiplicityCorrelationCut.cfgGlobalMultV0AMultLowCutFunction->c_str()); + LOGF(info, "Global multiplicity vs V0A multiplicity cut parameters:"); + for (std::size_t i = 0; i < cfgMultiplicityCorrelationCut.cfgGlobalMultV0AMultCutPars.value.size(); ++i) { + LOGF(info, "fGlobalMultV0AMultLowCut par %zu = %g", i, fGlobalMultV0AMultLowCut->GetParameter(i)); + LOGF(info, "fGlobalMultV0AMultHighCut par %zu = %g", i, fGlobalMultV0AMultHighCut->GetParameter(i)); + } + } + if (cfgMultiplicityCorrelationCut.cfgGlobalMultCentCutEnabled) { + LOGF(info, "Global multiplicity vs centrality cut is enabled"); + fGlobalMultCentHighCut = new TF1("fGlobalMultCentHighCut", cfgMultiplicityCorrelationCut.cfgGlobalMultCentHighCutFunction->c_str(), 0, 100); + fGlobalMultCentHighCut->SetParameters(cfgMultiplicityCorrelationCut.cfgGlobalMultCentCutPars.value.data()); + fGlobalMultCentLowCut = new TF1("fGlobalMultCentLowCut", cfgMultiplicityCorrelationCut.cfgGlobalMultCentLowCutFunction->c_str(), 0, 100); + fGlobalMultCentLowCut->SetParameters(cfgMultiplicityCorrelationCut.cfgGlobalMultCentCutPars.value.data()); + LOGF(info, "Global multiplicity vs centrality Upper Cut: %s", cfgMultiplicityCorrelationCut.cfgGlobalMultCentHighCutFunction->c_str()); + LOGF(info, "Global multiplicity vs centrality Lower Cut: %s", cfgMultiplicityCorrelationCut.cfgGlobalMultCentLowCutFunction->c_str()); + LOGF(info, "Global multiplicity vs centrality cut parameters:"); + for (std::size_t i = 0; i < cfgMultiplicityCorrelationCut.cfgGlobalMultCentCutPars.value.size(); ++i) { + LOGF(info, "fGlobalMultCentLowCut par %zu = %g", i, fGlobalMultCentLowCut->GetParameter(i)); + LOGF(info, "fGlobalMultCentHighCut par %zu = %g", i, fGlobalMultCentHighCut->GetParameter(i)); + } + } + if (cfgMultiplicityCorrelationCut.cfgPVMultCentCutEnabled) { + LOGF(info, "PV multiplicity vs centrality cut is enabled"); + fPVMultCentHighCut = new TF1("fPVMultCentHighCut", cfgMultiplicityCorrelationCut.cfgPVMultCentHighCutFunction->c_str(), 0, 100); + fPVMultCentHighCut->SetParameters(cfgMultiplicityCorrelationCut.cfgPVMultCentCutCutPars.value.data()); + fPVMultCentLowCut = new TF1("fPVMultCentLowCut", cfgMultiplicityCorrelationCut.cfgPVMultCentLowCutFunction->c_str(), 0, 100); + fPVMultCentLowCut->SetParameters(cfgMultiplicityCorrelationCut.cfgPVMultCentCutCutPars.value.data()); + LOGF(info, "PV multiplicity vs centrality Upper Cut: %s", cfgMultiplicityCorrelationCut.cfgPVMultCentHighCutFunction->c_str()); + LOGF(info, "PV multiplicity vs centrality Lower Cut: %s", cfgMultiplicityCorrelationCut.cfgPVMultCentLowCutFunction->c_str()); + LOGF(info, "PV multiplicity vs centrality cut parameters:"); + for (std::size_t i = 0; i < cfgMultiplicityCorrelationCut.cfgPVMultCentCutCutPars.value.size(); ++i) { + LOGF(info, "fPVMultCentLowCut par %zu = %g", i, fPVMultCentLowCut->GetParameter(i)); + LOGF(info, "fPVMultCentHighCut par %zu = %g", i, fPVMultCentHighCut->GetParameter(i)); + } + } + } + } else { + LOGF(warning, "Event selection cuts are disabled"); + } + readMatrix(cfgTrackCuts->getData(), trackSel); + if (trackSel[DCAz][TrkSelNch] == 0) { + fDCAzCut[TrkSelNch] = new TF1("pT-DCAzNch", Form("[0]*%s", cfgCutDCAz->c_str()), 0.001, 100); + fDCAzCut[TrkSelNch]->SetParameter(0, trackSel[DCAzNsigma][TrkSelNch]); + LOGF(info, Form("pT-dependent DCAz cut has been selcted: [0]*%s", cfgCutDCAz->c_str())); + LOGF(info, Form("pT-dependent DCAz cut has been selcted: %f*%s", fDCAzCut[TrkSelNch]->GetParameter(0), cfgCutDCAz->c_str())); + } + if (trackSel[DCAz][TrkSelObs] == 0) { + fDCAzCut[TrkSelObs] = new TF1("pT-DCAzObs", Form("[0]*%s", cfgCutDCAz->c_str()), 0.001, 100); + fDCAzCut[TrkSelObs]->SetParameter(0, trackSel[DCAzNsigma][TrkSelObs]); + } + fDCAxyCut[TrkSelNch] = new TF1("pT-DCAxyNch", Form("[0]*%s", cfgCutDCAxy->c_str()), 0.001, 100); + fDCAxyCut[TrkSelNch]->SetParameter(0, trackSel[DCAxyNsigma][TrkSelNch]); + fDCAxyCut[TrkSelObs] = new TF1("pT-DCAxyObs", Form("[0]*%s", cfgCutDCAxy->c_str()), 0.001, 100); + fDCAxyCut[TrkSelObs]->SetParameter(0, trackSel[DCAxyNsigma][TrkSelObs]); + LOGF(info, Form("pT-dependent DCAxy cut for Nch : %f*%s", fDCAxyCut[TrkSelObs]->GetParameter(0), cfgCutDCAxy->c_str())); + std::array etaGapsFlag = {"2-sub |#Delta#eta|>0", "2-sub |#Delta#eta|>0.2", "2-sub |#Delta#eta|>0.4", "2-sub |#Delta#eta|>0.6", "2-sub |#Delta#eta|>0.8", "2-sub |#Delta#eta|>1.0", "2-sub |#Delta#eta|>1.2", "2-sub |#Delta#eta|>1.4"}; + if (cfgGetNumberOfCorr) { + registryCorr.add("hTwoParticleCorrelations", "2 particle correlations; Nch;", {HistType::kTH2F, {axisNch, {8, -0.5, 7.5}}}); + registryCorr.add("hFourParticleCorrelations", "4 particle correlations; Nch;", {HistType::kTH2F, {axisNch, {8, -0.5, 7.5}}}); + registryCorr.add("hSixParticleCorrelations", "6 particle correlations; Nch;", {HistType::kTH2F, {axisNch, {8, -0.5, 7.5}}}); + registryCorr.add("hEightParticleCorrelations", "8 particle correlations; Nch;", {HistType::kTH2F, {axisNch, {8, -0.5, 7.5}}}); + for (uint iSet = 0; iSet < static_cast(EtaGapCounter); iSet++) { + registryCorr.get(HIST("hTwoParticleCorrelations"))->GetYaxis()->SetBinLabel(iSet + 1, etaGapsFlag[iSet].c_str()); + registryCorr.get(HIST("hFourParticleCorrelations"))->GetYaxis()->SetBinLabel(iSet + 1, etaGapsFlag[iSet].c_str()); + registryCorr.get(HIST("hSixParticleCorrelations"))->GetYaxis()->SetBinLabel(iSet + 1, etaGapsFlag[iSet].c_str()); + registryCorr.get(HIST("hEightParticleCorrelations"))->GetYaxis()->SetBinLabel(iSet + 1, etaGapsFlag[iSet].c_str()); + } + } + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setCaching(true); + auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + ccdb->setCreatedNotAfter(now); + if (!cfgFirstFillRunsList.value.empty()) { + registryQA.add("multT0A_multT0C", "FT0A-C multiplicity correlation;N_{ch}(FT0A);N_{ch}(FT0C);t[h]", {HistType::kTH3D, {axisT0A, axisT0C, binningTime}}); + firstRunOfFill = *std::min_element(cfgFirstFillRunsList.value.begin(), cfgFirstFillRunsList.value.end()); + } + } + template + float getCentrality(const TCollision& collision) + { + switch (cfgEventSelection.cfgCentEstimator) { + case CentFT0C: + return collision.centFT0C(); + case CentFT0CVariant1: + return collision.centFT0CVariant1(); + case CentFT0M: + return collision.centFT0M(); + case CentFV0A: + return collision.centFV0A(); + case CentNTPV: + return collision.centNTPV(); + case CentNGlobal: + return collision.centNGlobal(); + case CentMFT: + return collision.centMFT(); + default: + return collision.centFT0C(); + } + } + template + bool selectMultiplicityCorrelation(const TCollision& collision, const int multTrk, const float& centrality) + { + auto multNTracksPV = collision.multNTracksPV(); + if (cfgMultiplicityCorrelationCut.cfgGlobalMultPVMultCutEnabled) { + if (multTrk > fGlobalMultPVMultHighCut->Eval(multNTracksPV) || multTrk < fGlobalMultPVMultLowCut->Eval(multNTracksPV)) { + return false; + } + } + if (cfgMultiplicityCorrelationCut.cfgGlobalMultV0AMultCutEnabled) { + if (collision.multFV0A() > fGlobalMultV0AMultHighCut->Eval(multTrk) || collision.multFV0A() < fGlobalMultV0AMultLowCut->Eval(multTrk)) { + return false; + } + } + if (cfgMultiplicityCorrelationCut.cfgGlobalMultCentCutEnabled) { + if (multTrk > fGlobalMultCentHighCut->Eval(centrality) || multTrk < fGlobalMultCentLowCut->Eval(centrality)) { + return false; + } + } + if (cfgMultiplicityCorrelationCut.cfgPVMultCentCutEnabled) { + if (multNTracksPV > fPVMultCentHighCut->Eval(centrality) || multNTracksPV < fPVMultCentLowCut->Eval(centrality)) { + return false; + } + } + return true; + } + template + bool eventSelected(const TCollision& collision, const int multTrk, const float centrality, const int runNumber) + { + if (!cfgEventSelection.cfgEvSelUsage) { + return true; + } + if (cfgEventSelection.cfgEvSel8) { + if (!collision.sel8()) { + return false; + } + registryQA.fill(HIST("hEventCount"), Sel8); + } + if (!cfgEventSelection.cfgEvSelRCTflags.value.empty()) { + if (!rctChecker(collision)) { + return false; + } + registryQA.fill(HIST("hEventCount"), RCTflags); + } + for (auto& cut : eventSelectionCuts) { + if (!cut.enabled) { + continue; + } + if (!collision.selection_bit(cut.flag)) { + return false; + } + registryQA.fill(HIST("hEventCount"), cut.histBin); + } + if (cfgMultiplicityCorrelationCut.cfgEvSelMultCorrelation) { + if (!selectMultiplicityCorrelation(collision, multTrk, centrality)) { + return false; + } + registryQA.fill(HIST("hEventCount"), MultCorrelation); + } + if (std::find(cfgRemovedRunList.value.begin(), cfgRemovedRunList.value.end(), runNumber) != cfgRemovedRunList.value.end()) { + return false; + } + registryQA.fill(HIST("hEventCount"), BadRunsRemoved); + return true; + } + template + bool trackSelected(const TTracks& track) + { + if (trackSel[DCAz][sg] == 0 && std::fabs(track.dcaZ()) > fDCAzCut[sg]->Eval(track.pt())) { + return false; + } + if (trackSel[DCAz][sg] != 0 && std::fabs(track.dcaZ()) > trackSel[DCAz][sg]) { + return false; + } + return ((track.tpcChi2NCl() < trackSel[Chi2prTPCcls][sg]) && (track.tpcNClsFound() >= trackSel[TPCclu][sg]) && (track.itsNCls() >= trackSel[ITSclu][sg]) && (track.tpcNClsCrossedRows() >= trackSel[TPCCrossedRows][sg]) && (std::fabs(track.dcaXY()) < fDCAxyCut[sg]->Eval(track.pt()))); + } + template + inline void fillEventQA(const TCollision& collision, const TTracks& tracks) + { + registryQA.fill(HIST(FillTimeName[ft]) + HIST("globalTracks_centT0C"), collision.centFT0C(), tracks.size()); + registryQA.fill(HIST(FillTimeName[ft]) + HIST("PVTracks_centT0C"), collision.centFT0C(), collision.multNTracksPV()); + registryQA.fill(HIST(FillTimeName[ft]) + HIST("globalTracks_PVTracks"), collision.multNTracksPV(), tracks.size()); + registryQA.fill(HIST(FillTimeName[ft]) + HIST("globalTracks_multT0A"), collision.multFT0A(), tracks.size()); + registryQA.fill(HIST(FillTimeName[ft]) + HIST("globalTracks_multV0A"), collision.multFV0A(), tracks.size()); + registryQA.fill(HIST(FillTimeName[ft]) + HIST("multV0A_multT0A"), collision.multFT0A(), collision.multFV0A()); + registryQA.fill(HIST(FillTimeName[ft]) + HIST("multT0C_centT0C"), collision.centFT0C(), collision.multFT0C()); + } + template + inline void fillTrackQA(const TTracks& track, const float& vtxz) + { + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hTrackPhi"), track.phi()); + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hTrackEta"), track.eta()); + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hPhiEtaVtxZ"), track.phi(), track.eta(), vtxz); + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hPt"), track.pt()); + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hChi2prTPCcls"), track.tpcChi2NCl()); + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hChi2prITScls"), track.itsChi2NCl()); + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hnTPCClu"), track.tpcNClsFound()); + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hnITSClu"), track.itsNCls()); + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hnTPCCrossedRow"), track.tpcNClsCrossedRows()); + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hDCAz"), track.dcaZ(), track.pt()); + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hDCAxy"), track.dcaXY(), track.pt()); + } + template + inline void fillCorrProfile(const TTrack& track, std::array& trackRegion) + { + float eta = track.eta(); + for (uint iRegion = 0; iRegion < static_cast(EtaGapCounter); iRegion++) { + if (std::fabs(eta) > trackRegion[iRegion].gap) { + if (eta > 0) { + trackRegion[iRegion].forward++; + } else { + trackRegion[iRegion].backward++; + } + } + } + } + double getTimeSinceStartOfFill(uint64_t timestamp, int firstRun) + { + auto runDuration = ccdb->getRunDuration(firstRun); + uint64_t tsSOF = runDuration.first; + uint64_t diff = timestamp - tsSOF; + return static_cast(diff) / 3600000.0; + } + using FilteredTracks = soa::Filtered>; + using FilteredCollisions = soa::Filtered>; + void process(FilteredCollisions::iterator const& collision, aod::BCsWithTimestamps const&, FilteredTracks const& tracks) + { + auto bc = collision.bc_as(); + int run = bc.runNumber(); + int nch = 0; + std::array trackCorr{TrackRegion{.forward = 0, .backward = 0, .gap = 0.}, TrackRegion{.forward = 0, .backward = 0, .gap = 0.1}, TrackRegion{.forward = 0, .backward = 0, .gap = 0.2}, TrackRegion{.forward = 0, .backward = 0, .gap = 0.3}, TrackRegion{.forward = 0, .backward = 0, .gap = 0.4}, TrackRegion{.forward = 0, .backward = 0, .gap = 0.5}, TrackRegion{.forward = 0, .backward = 0, .gap = 0.6}, TrackRegion{.forward = 0, .backward = 0, .gap = 0.7}}; + registryQA.fill(HIST("hEventCount"), FilteredEvent); + if (tracks.size() < 1) { + return; + } + fillEventQA(collision, tracks); + const float cent = getCentrality(collision); + const float vtxz = collision.posZ(); + if (!eventSelected(collision, tracks.size(), cent, run)) { + return; + } + if (cfgEventSelection.cfgEvSelUsage) { + fillEventQA(collision, tracks); + } + if (!cfgFirstFillRunsList.value.empty()) { + if (std::find(cfgFirstFillRunsList.value.begin(), cfgFirstFillRunsList.value.end(), run) != cfgFirstFillRunsList.value.end() && firstRunOfFill != run) { + firstRunOfFill = run; + } + registryQA.fill(HIST("multT0A_multT0C"), collision.multFT0A(), collision.multFT0C(), getTimeSinceStartOfFill(bc.timestamp(), firstRunOfFill)); + } + registryQA.fill(HIST("hVtxZ"), vtxz); + registryQA.fill(HIST("hCentrality"), cent); + if (std::find(runNumbers.begin(), runNumbers.end(), run) == runNumbers.end()) { + if (cfgNUAWeightsRunByRun) { + std::shared_ptr histNUA = registryQA.add(Form("NUA/hPhiEtaVtxZ_%i", run), "", {HistType::kTH3D, {{60, 0.0, constants::math::TwoPI}, {32, -0.8, 0.8}, {40, -10, 10}}}); + th3sPerRun.insert(std::make_pair(run, histNUA)); + } + runNumbers.push_back(run); + } + for (const auto& track : tracks) { + + if (trackSelected(track)) { + fillTrackQA(track, vtxz); + } + if (!trackSelected(track)) { + continue; + } + fillTrackQA(track, vtxz); + if (cfgGetNumberOfCorr) { + fillCorrProfile(track, trackCorr); + + nch++; + } + if (cfgNUAWeightsRunByRun) { + th3sPerRun[run]->Fill(track.phi(), track.eta(), vtxz); + } + } + float binContent = 0; + float nCorrelations = 0; + if (cfgGetNumberOfCorr) { + for (uint iRegion = 0; iRegion < static_cast(EtaGapCounter); iRegion++) { + if (trackCorr[iRegion].forward > 0 && trackCorr[iRegion].backward > 0) { + binContent = registryCorr.get(HIST("hTwoParticleCorrelations"))->GetBinContent(nch, iRegion + 1); + nCorrelations = trackCorr[iRegion].forward * trackCorr[iRegion].backward; + registryCorr.get(HIST("hTwoParticleCorrelations"))->SetBinContent(nch, iRegion + 1, binContent + nCorrelations); + } + if (trackCorr[iRegion].forward > 1 && trackCorr[iRegion].backward > 1) { + binContent = registryCorr.get(HIST("hFourParticleCorrelations"))->GetBinContent(nch, iRegion + 1); + nCorrelations = trackCorr[iRegion].forward * (trackCorr[iRegion].forward - 1) * trackCorr[iRegion].backward * (trackCorr[iRegion].backward - 1) / 4.; + registryCorr.get(HIST("hFourParticleCorrelations"))->SetBinContent(nch, iRegion + 1, binContent + nCorrelations); + } + if (trackCorr[iRegion].forward > 2 && trackCorr[iRegion].backward > 2) { + binContent = registryCorr.get(HIST("hSixParticleCorrelations"))->GetBinContent(nch, iRegion + 1); + nCorrelations = trackCorr[iRegion].forward * (trackCorr[iRegion].forward - 1) * (trackCorr[iRegion].forward - 2) * trackCorr[iRegion].backward * (trackCorr[iRegion].backward - 1) * (trackCorr[iRegion].backward - 2) / 36.; + registryCorr.get(HIST("hSixParticleCorrelations"))->SetBinContent(nch, iRegion + 1, binContent + nCorrelations); + } + if (trackCorr[iRegion].forward > 3 && trackCorr[iRegion].backward > 3) { + binContent = registryCorr.get(HIST("hEightParticleCorrelations"))->GetBinContent(nch, iRegion + 1); + nCorrelations = trackCorr[iRegion].forward * (trackCorr[iRegion].forward - 1) * (trackCorr[iRegion].forward - 2) * (trackCorr[iRegion].forward - 3) * trackCorr[iRegion].backward * (trackCorr[iRegion].backward - 1) * (trackCorr[iRegion].backward - 2) * (trackCorr[iRegion].backward - 3) / 576; + registryCorr.get(HIST("hEightParticleCorrelations"))->SetBinContent(nch, iRegion + 1, binContent + nCorrelations); + } + } + } + } +}; +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGCF/Flow/Tasks/flowZdcTask.cxx b/PWGCF/Flow/Tasks/flowZdcTask.cxx index 21cacbd92ea..0186d851235 100644 --- a/PWGCF/Flow/Tasks/flowZdcTask.cxx +++ b/PWGCF/Flow/Tasks/flowZdcTask.cxx @@ -220,8 +220,6 @@ struct FlowZdcTask { histos.add("ZNAVsFT0M", ";T0A+T0C (#times 1/100);ZNA Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0M}, {nBinsZDC, -0.5, maxZn}}}); histos.add("ZNCVsFT0C", ";T0C (#times 1/100);ZNC Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0}, {nBinsZDC, -0.5, maxZn}}}); histos.add("ZNCVsFT0M", ";T0A+T0C (#times 1/100);ZNC Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0M}, {nBinsZDC, -0.5, maxZn}}}); - histos.add("ZPAZNAVsFT0M", ";T0A+T0C (#times 1/100);ZPA Amplitude;ZNA Amplitude;", kTH3F, {{{nBinsAmpFT0M, 0., maxAmpFT0M}, {nBinsZP, -0.5, maxZp}, {nBinsZN, -0.5, maxZn}}}); - histos.add("ZPCZNCVsFT0M", ";T0A+T0C (#times 1/100);ZPC Amplitude;ZNC Amplitude;", kTH3F, {{{nBinsAmpFT0M, 0., maxAmpFT0M}, {nBinsZP, -0.5, maxZp}, {nBinsZN, -0.5, maxZn}}}); histos.add("ZPAVsFT0C", ";T0C (#times 1/100);ZPA Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0}, {nBinsZDC, -0.5, maxZp}}}); histos.add("ZPAVsFT0M", ";T0A+T0C (#times 1/100);ZPA Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0M}, {nBinsZDC, -0.5, maxZp}}}); histos.add("ZPCVsFT0C", ";T0C (#times 1/100);ZPC Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0}, {nBinsZDC, -0.5, maxZp}}}); diff --git a/PWGCF/Flow/Tasks/pidFlowPtCorr.cxx b/PWGCF/Flow/Tasks/pidFlowPtCorr.cxx index c7d9c1a80dd..5189ecd236d 100644 --- a/PWGCF/Flow/Tasks/pidFlowPtCorr.cxx +++ b/PWGCF/Flow/Tasks/pidFlowPtCorr.cxx @@ -21,7 +21,13 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/TriggerAliases.h" #include "Common/CCDB/ctpRateFetcher.h" +#include "Common/Core/CollisionAssociation.h" +#include "Common/Core/TableHelper.h" +#include "Common/Core/TrackSelection.h" +#include "Common/Core/TrackSelectionDefaults.h" +#include "Common/Core/trackUtilities.h" #include "Common/DataModel/Centrality.h" +#include "Common/DataModel/CollisionAssociationTables.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseITS.h" @@ -30,12 +36,18 @@ #include "Common/DataModel/TrackSelectionTables.h" #include +#include #include +#include +#include +#include +#include #include #include #include #include #include +#include #include #include #include @@ -46,6 +58,7 @@ #include #include +#include #include #include #include @@ -53,10 +66,11 @@ #include #include #include +#include #include #include -#include +#include #include #include @@ -64,14 +78,52 @@ #include #include #include +#include +#include #include #include +#include #include using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; +namespace o2::aod +{ +namespace pidflowtimetrack +{ +DECLARE_SOA_INDEX_COLUMN(Collision, collision); +DECLARE_SOA_INDEX_COLUMN(Track, track); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(DcaXY, dcaXY, float); +DECLARE_SOA_COLUMN(DcaZ, dcaZ, float); +DECLARE_SOA_COLUMN(HasITS, hasITS, bool); +DECLARE_SOA_COLUMN(HasTPC, hasTPC, bool); +DECLARE_SOA_COLUMN(ItsNCls, itsNCls, uint8_t); +DECLARE_SOA_COLUMN(ItsChi2NCl, itsChi2NCl, float); +DECLARE_SOA_COLUMN(TpcNClsCrossedRows, tpcNClsCrossedRows, int); +DECLARE_SOA_COLUMN(TpcNClsFound, tpcNClsFound, int); +DECLARE_SOA_COLUMN(TpcChi2NCl, tpcChi2NCl, float); +// Raw public-associator count, before PV locking or closest-target selection. +DECLARE_SOA_COLUMN(NumCompatibleCollisions, numCompatibleCollisions, unsigned int); +} // namespace pidflowtimetrack + +// At most one accepted target per source track, ordered by collision. +// Kinematics/DCA belong to the target vertex; detector quality belongs to the source. +// Baseline selection is applied here; analysis quality/pT cuts stay in the consumers. +DECLARE_SOA_TABLE(PidFlowTimeTracks, "AOD", "PFTIMETRACK", + o2::soa::Index<>, pidflowtimetrack::CollisionId, pidflowtimetrack::TrackId, + pidflowtimetrack::Pt, pidflowtimetrack::Eta, pidflowtimetrack::Phi, + pidflowtimetrack::DcaXY, pidflowtimetrack::DcaZ, + pidflowtimetrack::HasITS, pidflowtimetrack::HasTPC, + pidflowtimetrack::ItsNCls, pidflowtimetrack::ItsChi2NCl, + pidflowtimetrack::TpcNClsCrossedRows, pidflowtimetrack::TpcNClsFound, + pidflowtimetrack::TpcChi2NCl, pidflowtimetrack::NumCompatibleCollisions); +} // namespace o2::aod + namespace { // Keep the corrconfigs vector layout in one named index table. Namespace @@ -85,7 +137,8 @@ enum : std::size_t { kC24 = 1, kC22Full = 2, kC32Gap = 3, - kC34Gap = 4 + kC34Gap = 4, + kC24Gap = 52 }; } // namespace charged @@ -103,7 +156,8 @@ enum : std::size_t { kC22Pure = 29, kC32Pure = 32, kC22FullA = 35, - kC22FullB = 36 + kC22FullB = 36, + kC24PIDChargedGap = 48 }; } // namespace pion @@ -121,7 +175,8 @@ enum : std::size_t { kC22Pure = 30, kC32Pure = 33, kC22FullA = 37, - kC22FullB = 38 + kC22FullB = 38, + kC24PIDChargedGap = 49 }; } // namespace kaon @@ -139,7 +194,8 @@ enum : std::size_t { kC22Pure = 31, kC32Pure = 34, kC22FullA = 39, - kC22FullB = 40 + kC22FullB = 40, + kC24PIDChargedGap = 50 }; } // namespace proton @@ -154,12 +210,13 @@ enum : std::size_t { kC24B = 44, kC22Pure = 45, kC22FullA = 46, - kC22FullB = 47 + kC22FullB = 47, + kC24PIDChargedGap = 51 }; } // namespace unidentified enum : std::size_t { - kCount = 48 + kCount = 53 }; } // namespace corrConfigIndex @@ -206,16 +263,469 @@ enum : int { }; } // namespace unidentified } // namespace gfwBitMask + +// Only target-dependent kinematics change; detector quality and MC labels stay +// on the source row. This view deliberately has no PID-table dependency. +template +struct TimeAssociatedChargedTrack : Track { + explicit TimeAssociatedChargedTrack(Track const& source) + : Track(source), targetPt(source.pt()), targetEta(source.eta()), targetPhi(source.phi()), targetDCAxy(source.dcaXY()), targetDCAz(source.dcaZ()) + { + } + float targetPt, targetEta, targetPhi, targetDCAxy, targetDCAz; + [[nodiscard]] float pt() const { return targetPt; } + [[nodiscard]] float eta() const { return targetEta; } + [[nodiscard]] float phi() const { return targetPhi; } + [[nodiscard]] float dcaXY() const { return targetDCAxy; } + [[nodiscard]] float dcaZ() const { return targetDCAz; } +}; +template +T timeAssociationOption(InitContext& context, std::string const& task, std::string const& option) +{ + T value{}; + if (!o2::common::core::getTaskOptionValue(context, task, option, value, false)) { + LOGF(fatal, "Charged time association needs option %s from producer %s", option.c_str(), task.c_str()); + } + return value; +} + } // namespace +// Shared charged-only producer: public TrackAssoc supplies time compatibility; +// lock PV contributors to their original collision and select the closest-time +// candidate for other tracks, then evaluate the target for flow, NUA and NUE. +struct PidFlowChargedTimeProducer { + Produces timeTracks; + // Original-vertex DCA/eta must not preselect candidates for another vertex. + using SourceTracks = soa::Join; + Service ccdb{}; + HistogramRegistry registry{"registry"}; + OutputObj chargedTimeAssociationConfig{TNamed("ChargedTimeAssociationConfig", "disabled")}; + TrackSelection timeGlobalSelection; + TrackSelection timeSDDSelection; + bool timeIsRun3 = false; + int timeFieldRun = -1; + uint64_t timeFieldTimestamp = 0; + float timeL3Current = 0.f; + float timeDipoleCurrent = 0.f; + bool timeUniformField = false; + bool cfgTimeAssociationQA = true; + int timeUsePVAssociation = aod::track_association::PVContrReassocOpt::OnlySameBc; + int timeMaxPvContributorsForLowMultReassoc = 10; + float timeNSigma = 4.f; + float timeMargin = 0.f; + float cutEta = 0.8f; + float cutVertex = 10.f; + + void init(InitContext& context) + { + if (doprocessDummy.value == doprocessTimeTracks.value) { + LOGF(fatal, "Enable exactly one of processDummy and processTimeTracks in pid-flow-charged-time-producer"); + } + const bool needed = timeAssociationOption(context, "pid-flow-pt-corr", "processDataTimeAssociation") || + timeAssociationOption(context, "pid-flow-pt-corr", "processChargedDifferentialTimeAssociation") || + timeAssociationOption(context, "pid-flow-pt-corr", "fillCorrectionGraphTimeAssociation") || + timeAssociationOption(context, "pid-flow-pt-corr", "processMCTimeAssociation") || + timeAssociationOption(context, "pid-flow-pt-corr", "processTimeAssociationComparison"); + if (needed != doprocessTimeTracks.value) { + LOGF(fatal, "Enable processTimeTracks (and disable processDummy) exactly when a charged time consumer is enabled"); + } + if (!doprocessTimeTracks) { + return; + } + cfgTimeAssociationQA = timeAssociationOption(context, "pid-flow-pt-corr", "cfgTimeAssociationQA"); + timeUsePVAssociation = timeAssociationOption(context, "track-to-collision-association", "usePVAssociation"); + timeMaxPvContributorsForLowMultReassoc = timeAssociationOption(context, "track-to-collision-association", "maxPvContributorsForLowMultReassoc"); + timeNSigma = timeAssociationOption(context, "track-to-collision-association", "nSigmaForTimeCompat"); + timeMargin = timeAssociationOption(context, "track-to-collision-association", "timeMargin"); + cutEta = timeAssociationOption(context, "pid-flow-pt-corr", "trkQualityOpts.cfgCutEta"); + cutVertex = timeAssociationOption(context, "pid-flow-pt-corr", "cfgCutVertex"); + ccdb->setURL(timeAssociationOption(context, "pid-flow-pt-corr", "cfgurl")); + ccdb->setCaching(true); + ccdb->setCreatedNotAfter(timeAssociationOption(context, "pid-flow-pt-corr", "cfgnolaterthan")); + if (!timeAssociationOption(context, "track-to-collision-association", "processAssocWithTime") || + timeAssociationOption(context, "track-to-collision-association", "processStandardAssoc") || + timeAssociationOption(context, "track-to-collision-association", "setTrackSelections") != 0) { + LOGF(fatal, "Use track-to-collision-association: processAssocWithTime=true, processStandardAssoc=false, setTrackSelections=0"); + } + + // Inherit the effective upstream selection rather than introducing a + // second set of global-track cuts with different hidden DCA defaults. + timeIsRun3 = timeAssociationOption(context, "track-selection", "isRun3"); + const int matching = timeAssociationOption(context, "track-selection", "itsMatching"); + const int dcaSetup = timeAssociationOption(context, "track-selection", "dcaSetup"); + const bool compatibilityIU = timeAssociationOption(context, "track-selection", "compatibilityIU"); + if (timeIsRun3) { + using Matching = TrackSelection::GlobalTrackRun3ITSMatching; + const std::array modes{Matching::Run3ITSibAny, Matching::Run3ITSibAny, Matching::Run3ITSallAny, Matching::Run3ITSall7Layers, Matching::Run3ITSibFirst}; + if (matching < 0 || matching >= static_cast(modes.size())) { + LOGF(fatal, "Unsupported track-selection itsMatching=%d", matching); + } + timeGlobalSelection = getGlobalTrackSelectionRun3ITSMatch(modes[matching], dcaSetup); + timeGlobalSelection.SetTrackType(compatibilityIU ? aod::track::TrackTypeEnum::TrackIU : aod::track::TrackTypeEnum::Track); + } else { + if (matching != 0) { + LOGF(fatal, "Run 2 time association requires track-selection itsMatching=0"); + } + timeGlobalSelection = getGlobalTrackSelection(); + } + timeSDDSelection = getGlobalTrackSelectionSDD(); + const auto ptMin = timeAssociationOption(context, "track-selection", "ptMin"); + const auto ptMax = timeAssociationOption(context, "track-selection", "ptMax"); + const auto etaMin = timeAssociationOption(context, "track-selection", "etaMin"); + const auto etaMax = timeAssociationOption(context, "track-selection", "etaMax"); + for (auto const& selection : {&timeGlobalSelection, &timeSDDSelection}) { + selection->SetPtRange(ptMin, ptMax); + selection->SetEtaRange(etaMin, etaMax); + } + std::string metadata = "association=TrackAssoc;table=PidFlowTimeTracks;charged-only;PID=none;local-density=none;source=Tracks;propagation=target-DCA,USEMatCorrNONE;PV=locked-original,not-refitted;nonPV=minimum-absolute-BC-corrected-deltaTime;tie=lowest-collisionId;selection=before-cuts,no-fallback;candidate-count=raw-TrackAssoc"; + metadata += ";isRun3=" + std::to_string(static_cast(timeIsRun3)) + ";itsMatching=" + std::to_string(matching) + ";dcaSetup=" + std::to_string(dcaSetup) + ";compatibilityIU=" + std::to_string(static_cast(compatibilityIU)); + metadata += ";pt=" + std::to_string(ptMin) + "," + std::to_string(ptMax) + ";eta=" + std::to_string(etaMin) + "," + std::to_string(etaMax); + metadata += ";producer.nSigmaForTimeCompat=" + std::to_string(timeAssociationOption(context, "track-to-collision-association", "nSigmaForTimeCompat")); + metadata += ";producer.timeMargin=" + std::to_string(timeAssociationOption(context, "track-to-collision-association", "timeMargin")); + metadata += ";producer.bcWindowForOneSigma=" + std::to_string(timeAssociationOption(context, "track-to-collision-association", "bcWindowForOneSigma")); + metadata += ";producer.usePVAssociation=" + std::to_string(timeUsePVAssociation); + metadata += ";producer.maxPvContributorsForLowMultReassoc=" + std::to_string(timeMaxPvContributorsForLowMultReassoc); + metadata += ";producer.includeUnassigned=" + std::to_string(static_cast(timeAssociationOption(context, "track-to-collision-association", "includeUnassigned"))); + chargedTimeAssociationConfig->SetTitle(metadata.c_str()); + LOG(info) << "Charged time association: " << metadata; + if (!cfgTimeAssociationQA) { + return; + } + registry.add("timeAssociation/candidatesPerTrack", "Time-compatible collisions per source track before event/track cuts;N_{coll}^{comp};Track count", {HistType::kTH1D, {{101, -0.5, 100.5}}}); + registry.add("timeAssociation/candidatesPerTrackSourceBaseline", "Source global/SDD + finite kinematics/DCA + eta only;N_{coll}^{comp};Track count", {HistType::kTH1D, {{101, -0.5, 100.5}}}); + registry.add("timeAssociation/candidatesVsSourceType", "All source tracks;N_{coll}^{comp};Source type", {HistType::kTH2D, {{101, -0.5, 100.5}, {3, -0.5, 2.5}}}); + registry.add("timeAssociation/candidatesVsAmbiguousBC", "AmbiguousTracks BC status (also for assigned tracks);N_{coll}^{comp};Ambiguous BC status", {HistType::kTH2D, {{101, -0.5, 100.5}, {3, -0.5, 2.5}}}); + registry.add("timeAssociation/candidatesVsRawTimeStatus", "Stored track time/resolution before PV substitution;N_{coll}^{comp};Raw time status", {HistType::kTH2D, {{101, -0.5, 100.5}, {4, -0.5, 3.5}}}); + registry.add("timeAssociation/candidatesVsOriginalTimeTest", "Fine time test at original collision; excludes coarse BC/search checks;N_{coll}^{comp};Original time test", {HistType::kTH2D, {{101, -0.5, 100.5}, {6, -0.5, 5.5}}}); + registry.add("timeAssociation/candidatesVsAcceptedTargets", "Raw candidates vs accepted PV-locked/closest-time target after producer cuts;N_{coll}^{comp} (raw TrackAssoc);N accepted targets (0 or 1)", {HistType::kTH2D, {{101, -0.5, 100.5}, {101, -0.5, 100.5}}}); + registry.add("timeAssociation/zeroCandidatePtEta", "Zero-candidate tracks with finite source pT/eta;Source p_{T} (GeV/c);Source #eta", {HistType::kTH2D, {{200, 0., 20.}, {100, -5., 5.}}}); + const auto setLabels = [](TAxis* axis, auto const& names) { + for (std::size_t bin = 0; bin < names.size(); ++bin) { + axis->SetBinLabel(bin + 1, names[bin]); + } + }; + setLabels(registry.get(HIST("timeAssociation/candidatesVsSourceType"))->GetYaxis(), + std::array{"no original collision", "assigned non-PV", "assigned PV contributor"}); + setLabels(registry.get(HIST("timeAssociation/candidatesVsAmbiguousBC"))->GetYaxis(), + std::array{"no ambiguous row", "invalid/empty BC slice", "valid BC slice"}); + setLabels(registry.get(HIST("timeAssociation/candidatesVsRawTimeStatus"))->GetYaxis(), + std::array{"finite time, res > 0", "finite time, res = 0", "finite time, res < 0", "nonfinite time or res"}); + setLabels(registry.get(HIST("timeAssociation/candidatesVsOriginalTimeTest"))->GetYaxis(), + std::array{"no original collision", "invalid effective time/res", "fails fine time test", "passes, original pair absent", "passes, original pair present", "invalid original collision index"}); + registry.add("timeAssociation/pairStatus", "Raw pairs and selected targets (target stages after vertex cut; stages overlap);stage;count", {HistType::kTH1D, {{10, 0, 10}}}); + const std::array labels{"producer pair", "duplicate pair", "same collision", "different collision", "unassigned source", "propagation failed", "baseline rejected", "baseline accepted", "PV locked target", "non-PV closest-time target"}; + for (std::size_t bin = 0; bin < labels.size(); ++bin) { + registry.get(HIST("timeAssociation/pairStatus"))->GetXaxis()->SetBinLabel(bin + 1, labels[bin]); + } + } + + template + void setChargedTimeField(GRP const* grp) + { + auto* field = dynamic_cast(TGeoGlobalMagField::Instance()->GetField()); + if (!field) { + o2::base::Propagator::initFieldFromGRP(grp); + } else if (!field->TestBit(o2::field::MagneticField::kOverrideGRP) && + (timeFieldRun < 0 || timeL3Current != grp->getL3Current() || timeDipoleCurrent != grp->getDipoleCurrent() || timeUniformField != grp->getFieldUniformity())) { + // Keep the field object alive: Propagator can already hold its pointer. + field->rescaleField(grp->getL3Current(), grp->getDipoleCurrent(), grp->getFieldUniformity()); + } + o2::base::Propagator::Instance()->updateField(); + timeL3Current = grp->getL3Current(); + timeDipoleCurrent = grp->getDipoleCurrent(); + timeUniformField = grp->getFieldUniformity(); + } + + void prepareChargedTimeField(aod::BCsWithTimestamps::iterator const& bc) + { + if (timeFieldRun == bc.runNumber() && timeFieldTimestamp == bc.timestamp()) { + return; + } + if (timeIsRun3) { + auto* grp = ccdb->getForTimeStamp("GLO/Config/GRPMagField", bc.timestamp()); + if (!grp) { + LOGF(fatal, "Missing GRPMagField for charged time association"); + } + setChargedTimeField(grp); + } else { + auto* grp = ccdb->getForTimeStamp("GLO/GRP/GRP", bc.timestamp()); + if (!grp) { + LOGF(fatal, "Missing GRPObject for charged time association"); + } + setChargedTimeField(grp); + } + timeFieldRun = bc.runNumber(); + timeFieldTimestamp = bc.timestamp(); + } + + template + std::vector chargedTimeMultiplicity(aod::TrackAssoc const& pairs, TTracks const& tracks, + aod::Collisions const& collisions, aod::BCsWithTimestamps const& bcs, aod::AmbiguousTracks const& ambiguousTracks) + { + std::vector counts(tracks.size(), 0); + std::vector lastCollision(tracks.size(), -1); + std::vector hasOriginalPair(cfgTimeAssociationQA ? tracks.size() : 0, false); + for (auto const& pair : pairs) { + const int64_t index = pair.trackId() - tracks.offset(); + if (index < 0 || index >= static_cast(tracks.size())) { + LOGF(fatal, "TrackAssoc track index is outside the full source track table"); + } + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/pairStatus"), 0.5); + if (lastCollision[index] == pair.collisionId()) { + registry.fill(HIST("timeAssociation/pairStatus"), 1.5); + } + } + // TrackAssoc is grouped by collision. Count distinct target collisions, + // including those that will later fail event or target-track cuts. + if (lastCollision[index] != pair.collisionId()) { + ++counts[index]; + lastCollision[index] = pair.collisionId(); + } + if (cfgTimeAssociationQA && pair.collisionId() == tracks.rawIteratorAt(index).collisionId()) { + hasOriginalPair[index] = true; + } + } + if (!cfgTimeAssociationQA) { + return counts; + } + // Cache the first ambiguous row, matching the upstream lookup. Do not + // dereference its BC slice until its endpoints have been checked. + std::vector ambiguousBCStatus(tracks.size(), 0); + for (auto const& ambiguous : ambiguousTracks) { + const int64_t index = ambiguous.trackId() - tracks.offset(); + if (index < 0 || index >= static_cast(tracks.size()) || ambiguousBCStatus[index] != 0) { + continue; + } + auto const& ids = ambiguous.bcIds(); + const bool valid = ambiguous.has_bc() && ids[0] >= 0 && ids[1] >= ids[0] && ids[1] < bcs.size(); + ambiguousBCStatus[index] = valid ? 2 : 1; + } + // One unweighted entry per source track, including zero candidates, before + // ownership resolution or any target event/track selection. + for (auto const& track : tracks) { + const int64_t index = track.globalIndex() - tracks.offset(); + const auto count = counts[index]; + registry.fill(HIST("timeAssociation/candidatesPerTrack"), count); + const int sourceType = !track.has_collision() ? 0 : (track.isPVContributor() ? 2 : 1); + registry.fill(HIST("timeAssociation/candidatesVsSourceType"), count, sourceType); + registry.fill(HIST("timeAssociation/candidatesVsAmbiguousBC"), count, ambiguousBCStatus[index]); + const bool finiteTime = std::isfinite(track.trackTime()) && std::isfinite(track.trackTimeRes()); + const int rawTimeStatus = !finiteTime ? 3 : (track.trackTimeRes() > 0.f ? 0 : (track.trackTimeRes() == 0.f ? 1 : 2)); + registry.fill(HIST("timeAssociation/candidatesVsRawTimeStatus"), count, rawTimeStatus); + const bool finite = std::isfinite(track.pt()) && std::isfinite(track.eta()) && std::isfinite(track.phi()) && std::isfinite(track.dcaXY()) && std::isfinite(track.dcaZ()); + if (finite && (track.isGlobalTrack() || track.isGlobalTrackSDD()) && std::fabs(track.eta()) < cutEta) { + // Diagnostic at the source vertex only; not the final flow selection. + registry.fill(HIST("timeAssociation/candidatesPerTrackSourceBaseline"), count); + } + if (count == 0 && std::isfinite(track.pt()) && std::isfinite(track.eta())) { + registry.fill(HIST("timeAssociation/zeroCandidatePtEta"), track.pt(), track.eta()); + } + int originalTimeStatus = 0; + if (track.has_collision()) { + const int64_t collisionIndex = track.collisionId() - collisions.offset(); + originalTimeStatus = 5; + if (collisionIndex >= 0 && collisionIndex < collisions.size()) { + auto collision = collisions.rawIteratorAt(collisionIndex); + // Match the public associator's PV-time substitution, including mode 2. + const bool usePVTime = track.isPVContributor() && + (timeUsePVAssociation == aod::track_association::PVContrReassocOpt::OnlySameBc || + (timeUsePVAssociation == aod::track_association::PVContrReassocOpt::SameBcAndLowMult && + collision.numContrib() > timeMaxPvContributorsForLowMultReassoc)); + const float trackTime = usePVTime ? collision.collisionTime() : track.trackTime(); + const float trackRes = usePVTime ? o2::constants::lhc::LHCBunchSpacingNS : track.trackTimeRes(); + const float collisionRes = collision.collisionTimeRes(); + originalTimeStatus = 1; + if (std::isfinite(trackTime) && std::isfinite(trackRes) && trackRes >= 0.f && std::isfinite(collision.collisionTime()) && std::isfinite(collisionRes) && collisionRes >= 0.f) { + float threshold = trackRes; + if (!usePVTime) { + threshold = TESTBIT(track.flags(), aod::track::TrackTimeResIsRange) + ? trackRes + timeNSigma * std::sqrt(collisionRes * collisionRes) + timeMargin + : timeNSigma * std::sqrt(collisionRes * collisionRes + trackRes * trackRes) + timeMargin; + } + // Same BC as the original collision, hence bcOffset = 0. This + // checks only fine time compatibility, not the upstream BC cache + // or iterator window. A pass with no pair needs upstream study. + originalTimeStatus = std::fabs(trackTime - collision.collisionTime()) < threshold ? (hasOriginalPair[index] ? 4 : 3) : 2; + } + } + } + registry.fill(HIST("timeAssociation/candidatesVsOriginalTimeTest"), count, originalTimeStatus); + } + return counts; + } + + template + std::vector> selectChargedTimeTargets(aod::TrackAssoc const& pairs, TTracks const& tracks, + aod::Collisions const& collisions, aod::BCsWithTimestamps const& bcs, aod::AmbiguousTracks const& ambiguousTracks) + { + std::vector targetIndices(tracks.size(), -1); + std::vector referenceBC(tracks.size(), -1); + std::vector bestDistance(tracks.size(), std::numeric_limits::infinity()); + const auto globalBC = [&bcs](int64_t id) -> int64_t { + const int64_t index = id - bcs.offset(); + if (index < 0 || index >= static_cast(bcs.size())) { + return -1; + } + return static_cast(bcs.rawIteratorAt(index).globalBC()); + }; + for (auto const& track : tracks) { + const int64_t index = track.globalIndex() - tracks.offset(); + const int64_t original = track.collisionId() - collisions.offset(); + if (!track.has_collision() || original < 0 || original >= static_cast(collisions.size())) { + continue; + } + if (track.isPVContributor()) { + // A PV contributor defines this vertex. Restore its original pair even + // when the public time associator supplied zero candidates. + targetIndices[index] = original; + } else { + referenceBC[index] = globalBC(collisions.rawIteratorAt(original).bcId()); + } + } + // Unassigned tracks use the first BC of their first ambiguous row, exactly + // as in the public associator; never replace an invalid first row by another. + std::vector seenAmbiguous(tracks.size(), false); + for (auto const& ambiguous : ambiguousTracks) { + const int64_t index = ambiguous.trackId() - tracks.offset(); + if (index < 0 || index >= static_cast(tracks.size()) || seenAmbiguous[index]) { + continue; + } + seenAmbiguous[index] = true; + auto const& ids = ambiguous.bcIds(); + if (!tracks.rawIteratorAt(index).has_collision() && ambiguous.has_bc() && ids[1] >= ids[0] && globalBC(ids[1]) >= 0) { + referenceBC[index] = globalBC(ids[0]); + } + } + for (auto const& pair : pairs) { + const int64_t index = pair.trackId() - tracks.offset(); + const int64_t target = pair.collisionId() - collisions.offset(); + if (index < 0 || index >= static_cast(tracks.size()) || target < 0 || target >= static_cast(collisions.size())) { + LOGF(fatal, "TrackAssoc index is outside the full source track/collision tables"); + } + auto track = tracks.rawIteratorAt(index); + if (track.isPVContributor() || referenceBC[index] < 0) { + continue; + } + auto collision = collisions.rawIteratorAt(target); + const int64_t collisionBC = globalBC(collision.bcId()); + if (collisionBC < 0) { + continue; + } + // Compare in a common BC reference. Subtract the integer BCs before the + // floating-point conversion to retain precision for large global BCs. + const double deltaTime = static_cast(track.trackTime()) - collision.collisionTime() + + static_cast(referenceBC[index] - collisionBC) * o2::constants::lhc::LHCBunchSpacingNS; + const double distance = std::abs(deltaTime); + if (std::isfinite(distance) && (distance < bestDistance[index] || (distance == bestDistance[index] && target < targetIndices[index]))) { + targetIndices[index] = target; + bestDistance[index] = distance; + } + } + // Resolve ownership before any event/vertex, propagation or quality cuts. + // A rejected best target has no fallback; each source can appear only once. + std::vector> selected(collisions.size()); + for (std::size_t index = 0; index < targetIndices.size(); ++index) { + if (targetIndices[index] >= 0) { + selected[targetIndices[index]].push_back(index); + } + } + return selected; + } + + template + void fillChargedTimeTracks(TCollision const& collision, std::vector const& selected, TTracks const& tracks, std::vector const& counts, std::vector& acceptedCounts) + { + bool fieldReady = false; + for (const auto index : selected) { + auto source = tracks.rawIteratorAt(index); + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/pairStatus"), source.isPVContributor() ? 8.5 : 9.5); + } + TimeAssociatedChargedTrack candidate(source); + const bool sameCollision = source.collisionId() == collision.globalIndex(); + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/pairStatus"), sameCollision ? 2.5 : 3.5); + } + if (cfgTimeAssociationQA && !source.has_collision()) { + registry.fill(HIST("timeAssociation/pairStatus"), 4.5); + } + if (!sameCollision) { + if (!fieldReady) { + prepareChargedTimeField(collision.template bc_as()); + fieldReady = true; + } + auto parameters = getTrackPar(source); + std::array dca{}; + if (!o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, parameters, 2.f, o2::base::Propagator::MatCorrType::USEMatCorrNONE, &dca)) { + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/pairStatus"), 5.5); + } + continue; + } + candidate.targetPt = parameters.getPt(); + candidate.targetEta = parameters.getEta(); + candidate.targetPhi = parameters.getPhi(); + candidate.targetDCAxy = dca[0]; + candidate.targetDCAz = dca[1]; + } + const bool finite = std::isfinite(candidate.pt()) && std::isfinite(candidate.eta()) && std::isfinite(candidate.phi()) && std::isfinite(candidate.dcaXY()) && std::isfinite(candidate.dcaZ()); + const bool baseline = finite && (sameCollision ? (source.isGlobalTrack() || source.isGlobalTrackSDD()) : (timeGlobalSelection.IsSelected(candidate) || (!timeIsRun3 && timeSDDSelection.IsSelected(candidate)))); + if (!baseline || std::fabs(candidate.eta()) >= cutEta) { + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/pairStatus"), 6.5); + } + continue; + } + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/pairStatus"), 7.5); + ++acceptedCounts[index]; + } + timeTracks(collision.globalIndex(), source.globalIndex(), + candidate.pt(), candidate.eta(), candidate.phi(), candidate.dcaXY(), candidate.dcaZ(), + source.hasITS(), source.hasTPC(), source.itsNCls(), source.itsChi2NCl(), + source.tpcNClsCrossedRows(), source.tpcNClsFound(), source.tpcChi2NCl(), counts[index]); + } + } + + void processTimeTracks(aod::Collisions const& collisions, aod::BCsWithTimestamps const& bcs, + aod::TrackAssoc const& pairs, SourceTracks const& sources, aod::AmbiguousTracks const& ambiguousTracks) + { + // Count all producer candidates before vertex, event or target-track cuts. + auto counts = chargedTimeMultiplicity(pairs, sources, collisions, bcs, ambiguousTracks); + auto selected = selectChargedTimeTargets(pairs, sources, collisions, bcs, ambiguousTracks); + std::vector acceptedCounts(cfgTimeAssociationQA ? sources.size() : 0, 0); + for (auto const& collision : collisions) { + if (std::fabs(collision.posZ()) < cutVertex) { + fillChargedTimeTracks(collision, selected[collision.globalIndex() - collisions.offset()], sources, counts, acceptedCounts); + } + } + if (cfgTimeAssociationQA) { + for (std::size_t index = 0; index < counts.size(); ++index) { + registry.fill(HIST("timeAssociation/candidatesVsAcceptedTargets"), counts[index], acceptedCounts[index]); + } + } + } + PROCESS_SWITCH(PidFlowChargedTimeProducer, processTimeTracks, "Build shared charged time-associated track table; no PID", false); + + void processDummy(aod::Collisions const&) {} + PROCESS_SWITCH(PidFlowChargedTimeProducer, processDummy, "Idle entry for original collisionId analysis", true); +}; + struct PidFlowPtCorr { // configurable double minVal4Float = 1e-3; Configurable cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted z-vertex range"}; Configurable cfgCutChi2prTPCcls{"cfgCutChi2prTPCcls", 2.5, "Chi2 per TPC clusters"}; - Configurable cfgDeltaPhiLocDen{"cfgDeltaPhiLocDen", 3, "Number of delta phi for local density, 200 bins in 2 pi"}; Configurable cfgCentEstimator{"cfgCentEstimator", 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FV0A; 4:NTPV; 5:NGlobal; 6:MFT"}; + Configurable cfgUseMultiplicity{"cfgUseMultiplicity", false, "Use raw PV-contributor count in |eta| < 0.8 instead of centrality for output axes and NUE lookup; configure axisMultiplicity and disable cfgDoMultPVCut"}; + Configurable cfgTimeAssociationQA{"cfgTimeAssociationQA", true, "Create and fill time-association producer, comparison and MC QA; keep event counters and flow/NUA/NUE outputs"}; + ConfigurableAxis cfgaxisAssociationNch{"cfgaxisAssociationNch", {601, -5., 6005.}, "Association QA Nch (default bin width: 10 tracks; includes zero)"}; + ConfigurableAxis cfgaxisAssociationChangedNch{"cfgaxisAssociationChangedNch", {1001, -0.5, 1000.5}, "Association QA lost/gained track count"}; + ConfigurableAxis cfgaxisAssociationDeltaNch{"cfgaxisAssociationDeltaNch", {4001, -2000.5, 2000.5}, "Association QA Nch(time) minus Nch(original)"}; + ConfigurableAxis cfgaxisAssociationOccupancy{"cfgaxisAssociationOccupancy", {200, -0.5, 19999.5}, "Association QA track occupancy (-1 goes to underflow)"}; struct : ConfigurableGroup { std::string prefix = "trkQualityOpts"; @@ -264,8 +774,6 @@ struct PidFlowPtCorr { Configurable cfgDCAxy{"cfgDCAxy", "(0.0026+0.005/(x^1.01))", "Functional form of pt-dependent DCAxy cut"}; } dcaCutOpts; - Configurable> cfgNSigmapid{"cfgNSigmapid", (std::vector{3, 3, 3, 9, 9, 9, 9, 9, 9}), "tpc, tof and its NSigma for Pion Proton Kaon"}; - struct : ConfigurableGroup { std::string prefix = "correctionPathOpts"; Configurable> cfgAcceptancePath{"cfgAcceptancePath", (std::vector{"Users/f/fcui/NUA/NUAREFPartical", "Users/f/fcui/NUA/NUAK0s", "Users/f/fcui/NUA/NUALambda", "Users/f/fcui/NUA/NUAXi", "Users/f/fcui/NUA/NUAOmega"}), "CCDB path to acceptance object"}; @@ -279,17 +787,23 @@ struct PidFlowPtCorr { Configurable cfgEtaGap{"cfgEtaGap", 0.4, "Eta-gap half-width for flow particles; 0.4 selects (-etaMax,-0.4) and (0.4,etaMax)"}; Configurable cfgFlowNbootstrap{"cfgFlowNbootstrap", 30, "Number of subsamples for bootstrap"}; + struct : ConfigurableGroup { + std::string prefix = "chargedDifferentialOpts"; + Configurable cfgRefPtMin{"cfgRefPtMin", 0.2f, "Charged differential v2{2}: reference pT lower bound (GeV/c), independent of integrated flow"}; + Configurable cfgRefPtMax{"cfgRefPtMax", 3.0f, "Charged differential v2{2}: reference pT upper bound (GeV/c), independent of integrated flow"}; + ConfigurableAxis cfgaxisPt{"cfgaxisPt", {VARIABLE_WIDTH, 0.2, 0.3, 0.4, 0.5, 0.6, 0.8, 1.0, 1.2, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0, 6.0, 8.0, 10.0}, "Charged differential v2{2}: POI pT binning and acceptance [first edge, last edge) in GeV/c"}; + } chargedDifferentialOpts; + // switch struct : ConfigurableGroup { std::string prefix = "switchsOpts"; - Configurable cfgDoLocDenCorr{"cfgDoLocDenCorr", false, "do local density corr"}; Configurable cfgOutputrunbyrun{"cfgOutputrunbyrun", false, "OPEN IF USE FUNCTION(fillcorrectiongraph) Fill and output NUA weights run by run"}; Configurable cfgOutPutMC{"cfgOutPutMC", false, "Fill MC graphs, note that if the processMCgen is open,this MUST be open"}; Configurable cfgOutputQA{"cfgOutputQA", false, "OPEN IF USE FUNCTION(detectorPidQa) do QA"}; Configurable cfgDebugMyCode{"cfgDebugMyCode", false, "output some graph for code debug"}; Configurable cfgOutPutPtSpectra{"cfgOutPutPtSpectra", false, "output pt spectra for data, MC and RECO"}; Configurable cfgCheck2MethodDiff{"cfgCheck2MethodDiff", false, "check difference between v2' && v2''"}; - Configurable cfgClosureTest{"cfgClosureTest", 0, "choose (val) percent particle from charged to pass Pion PID selection"}; + Configurable cfgClosureTest{"cfgClosureTest", 0, "Nonzero enables extra pion mean-pT weight QA profiles"}; Configurable cfgOutPutMC1D{"cfgOutPutMC1D", true, "Fill MC graphs, note that if the processMCgen is open,this MUST be open"}; Configurable cfgAddPidResponseMatrixHistograms{"cfgAddPidResponseMatrixHistograms", false, "Add PID response matrix histograms; enable together with processPidResponseMatrix"}; Configurable cfgAddC22DeltaPtHistograms{"cfgAddC22DeltaPtHistograms", false, "Add histograms for processDataC22DeltaPt; enable together with processDataC22DeltaPt"}; @@ -308,37 +822,17 @@ struct PidFlowPtCorr { Configurable cfgUseNUAWithPt{"cfgUseNUAWithPt", false, "false: use original GFWWeights (phi,eta,vz) NUA; true: use THnSparse (phi,eta,vz,pt) NUA loaded from cfgAcceptancePathWithPt"}; } switchsOpts; - /** - * @brief cfg for PID pt range - * @details default datas are from run2, note that separate pi-k and k-p needs to use difference pt range - */ - // separate pi and k + // TOF momentum ranges used by circle PID. struct : ConfigurableGroup { std::string prefix = "pidPtRangeOpts"; - Configurable cfgPtMin4ITSPiKa{"cfgPtMin4ITSPiKa", 0.2, "pt min for ITS to separate pi and k"}; - Configurable cfgPtMax4ITSPiKa{"cfgPtMax4ITSPiKa", 0.8, "pt max for ITS to separate pi and k"}; Configurable cfgPtMin4TOFPiKa{"cfgPtMin4TOFPiKa", 0.5, "pt min for TOF to separate pi and k"}; Configurable cfgPtMax4TOFPiKa{"cfgPtMax4TOFPiKa", 3.0, "pt max for TOF to separate pi and k"}; - Configurable cfgPtMin4TPCPiKa{"cfgPtMin4TPCPiKa", 0.2, "pt min for TPC to separate pi and k"}; - Configurable cfgPtMax4TPCPiKa{"cfgPtMax4TPCPiKa", 0.6, "pt max for TPC to separate pi and k"}; - // end separate pi and k - - // separate k-p - Configurable cfgPtMin4ITSKaPr{"cfgPtMin4ITSKaPr", 0.4, "pt min for ITS to separate k and p"}; - Configurable cfgPtMax4ITSKaPr{"cfgPtMax4ITSKaPr", 1.4, "pt max for ITS to separate k and p"}; Configurable cfgPtMin4TOFKaPr{"cfgPtMin4TOFKaPr", 0.6, "pt min for TOF to separate k and p"}; Configurable cfgPtMax4TOFKaPr{"cfgPtMax4TOFKaPr", 3.0, "pt max for TOF to separate k and p"}; - Configurable cfgPtMin4TPCKaPr{"cfgPtMin4TPCKaPr", 0.2, "pt min for TPC to separate k and p"}; - Configurable cfgPtMax4TPCKaPr{"cfgPtMax4TPCKaPr", 1.0, "pt max for TPC to separate k and p"}; } pidPtRangeOpts; - // end separate k-p - // end cfg for PID pt range struct : ConfigurableGroup { std::string prefix = "circleCutOpts"; - // Switch to enable/disable circular cut PID - Configurable cfgUseCircleCutPID{"cfgUseCircleCutPID", true, "Enable circular cut PID method"}; - // TOF pT threshold: above this pt, TOF signal is required Configurable cfgCircleCutTofPtCut{"cfgCircleCutTofPtCut", 0.5, "TOF pT threshold for requiring TOF signal"}; @@ -347,12 +841,19 @@ struct PidFlowPtCorr { Configurable cfgCircleCutSigmaKa{"cfgCircleCutSigmaKa", 4.0, "Circular cut radius squared for Kaon (TPC+TOF)"}; Configurable cfgCircleCutSigmaPr{"cfgCircleCutSigmaPr", 4.0, "Circular cut radius squared for Proton (TPC+TOF)"}; - // TPC-only fallback cut values (|nsigma| < cut) - Configurable cfgCircleCutTPCPi{"cfgCircleCutTPCPi", 2.0, "TPC-only nsigma cut for Pion"}; - Configurable cfgCircleCutTPCKa{"cfgCircleCutTPCKa", 2.0, "TPC-only nsigma cut for Kaon"}; - Configurable cfgCircleCutTPCPr{"cfgCircleCutTPCPr", 2.0, "TPC-only nsigma cut for Proton"}; + // TPC fallback cuts, combined with ITS below cfgCircleCutITSPtMax. + Configurable cfgCircleCutTPCPi{"cfgCircleCutTPCPi", 2.0, "TPC fallback nsigma cut for Pion"}; + Configurable cfgCircleCutTPCKa{"cfgCircleCutTPCKa", 2.0, "TPC fallback nsigma cut for Kaon"}; + Configurable cfgCircleCutTPCPr{"cfgCircleCutTPCPr", 2.0, "TPC fallback nsigma cut for Proton"}; + Configurable cfgCircleCutITSPtMax{"cfgCircleCutITSPtMax", 0.5, "Require ITS PID below this pT (GeV/c); 0 disables"}; + Configurable cfgCircleCutITSPi{"cfgCircleCutITSPi", 2.0, "Low-pT ITS absolute nsigma cut for Pion"}; + Configurable cfgCircleCutITSKa{"cfgCircleCutITSKa", 2.0, "Low-pT ITS absolute nsigma cut for Kaon"}; + Configurable cfgCircleCutITSPr{"cfgCircleCutITSPr", 2.0, "Low-pT ITS absolute nsigma cut for Proton"}; } circleCutOpts; + Configurable cfgTPCPIDClustersMinFunction{"cfgTPCPIDClustersMinFunction", "0", "Minimum TPC PID clusters as a ROOT formula; x = tpcNClsFound(), numeric coefficients, 0 imposes no lower bound"}; + std::unique_ptr fTPCPIDClustersMin; + struct : ConfigurableGroup { std::string prefix = "particleAbundanceOpts"; ConfigurableAxis cfgaxisAbundancePi{"cfgaxisAbundancePi", {100, 0, 1100}, "axis for Abundance Pi"}; @@ -369,22 +870,23 @@ struct PidFlowPtCorr { ConfigurableAxis cfgaxisPt{"cfgaxisPt", {VARIABLE_WIDTH, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00, 1.10, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.20, 2.40, 2.60, 2.80, 3.00, 3.50, 4.00, 4.50, 5.00, 5.50, 6.00, 10.0}, "pt (GeV)"}; ConfigurableAxis cfgaxisTPCDedx{"cfgaxisTPCDedx", {500, 0.0f, 1000.0f}, "d#it{E}/d#it{x} axis for detector-PID QA"}; ConfigurableAxis cfgaxisTOFDelta{"cfgaxisTOFDelta", {200, -1000.0f, 1000.0f}, "TOF #it{t} - #it{t}_{ev} - #it{t}_{exp} axis for detector-PID QA"}; + ConfigurableAxis cfgaxisTOFTime{"cfgaxisTOFTime", {10000, 0.0f, 2.0e6f}, "TOF #it{t} - #it{t}_{ev} (ps) axis for detector-PID QA"}; + ConfigurableAxis cfgaxisTPCPIDClusters{"cfgaxisTPCPIDClusters", {201, -0.5, 200.5}, "TPC clusters used for PID"}; + ConfigurableAxis cfgaxisPIDNSigma{"cfgaxisPIDNSigma", {240, -12., 12.}, "Signed TPC n-sigma axis for PID-quality QA"}; + ConfigurableAxis cfgaxisPIDMomentum{"cfgaxisPIDMomentum", {1000, 0., 10.}, "Total momentum for ITS cluster-size QA (GeV/c)"}; + ConfigurableAxis cfgaxisITSNSigma{"cfgaxisITSNSigma", {161, -12.075, 12.075}, "Signed ITS n-sigma; zero at bin center"}; + ConfigurableAxis cfgaxisITSPt{"cfgaxisITSPt", {300, 0., 3.}, "Fine pT axis for ITS response QA (GeV/c)"}; + ConfigurableAxis cfgaxisITSClusterSize{"cfgaxisITSClusterSize", {101, -0.075, 15.075}, "Truncated mean ITS cluster size divided by cosh(eta)"}; ConfigurableAxis cfgaxisMeanPt{"cfgaxisMeanPt", {300, 0, 3}, "pt (GeV)"}; ConfigurableAxis cfgaxisNch{"cfgaxisNch", {3000, 0.5, 3000.5}, "Nch"}; - ConfigurableAxis cfgaxisLocalDensity{"cfgaxisLocalDensity", {200, 0, 600}, "local density"}; ConfigurableAxis cfgaxisRun{"cfgaxisRun", {7, 0, 7}, "axis of runs in the data"}; - Configurable> cfgTrackDensityP0{"cfgTrackDensityP0", std::vector{0.7217476707, 0.7384792571, 0.7542625668, 0.7640680200, 0.7701951667, 0.7755299053, 0.7805901710, 0.7849446786, 0.7957356586, 0.8113039262, 0.8211968966, 0.8280558878, 0.8329342135}, "parameter 0 for track density efficiency correction"}; - Configurable> cfgTrackDensityP1{"cfgTrackDensityP1", std::vector{-2.169488e-05, -2.191913e-05, -2.295484e-05, -2.556538e-05, -2.754463e-05, -2.816832e-05, -2.846502e-05, -2.843857e-05, -2.705974e-05, -2.477018e-05, -2.321730e-05, -2.203315e-05, -2.109474e-05}, "parameter 1 for track density efficiency correction"}; - Configurable> cfgTrackDensityV2P{"cfgTrackDensityV2P", std::vector{0.0186111, 0.00351907, -4.38264e-05, 1.35383e-07, -3.96266e-10}, "parameter of v2(cent) for track density efficiency correction"}; - Configurable> cfgTrackDensityV3P{"cfgTrackDensityV3P", std::vector{0.0174056, 0.000703329, -1.45044e-05, 1.91991e-07, -1.62137e-09}, "parameter of v2(cent) for track density efficiency correction"}; - Configurable> cfgTrackDensityV4P{"cfgTrackDensityV4P", std::vector{0.008845, 0.000259668, -3.24435e-06, 4.54837e-08, -6.01825e-10}, "parameter of v2(cent) for track density efficiency correction"}; struct : ConfigurableGroup { std::string prefix = "meanptC22GraphOpts"; ConfigurableAxis cfgaxisBootstrap{"cfgaxisBootstrap", {30, 0, 30}, "cfgaxisBootstrap"}; } meanptC22GraphOpts; - ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 80, 90}, "Centrality (%)"}; + ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 80, 90}, "Centrality (%) or raw PV multiplicity when cfgUseMultiplicity is enabled"}; // configurable @@ -398,10 +900,12 @@ struct PidFlowPtCorr { using TracksPIDForDetectorPidQA = soa::Join; // data tracks filter using AodTracks = soa::Filtered>; - using AodTracksForDetectorPidQA = soa::Filtered>; + using AodTracksForDetectorPidQA = soa::Filtered>; // data collisions filter using AodCollisions = soa::Filtered>; + using ChargedTimeCollisionsMC = soa::Filtered>; + // MC Filter mccollisionFilter = nabs(aod::mccollision::posZ) < cfgCutVertex; using FilteredMcCollisions = soa::Filtered; @@ -418,6 +922,8 @@ struct PidFlowPtCorr { // others Preslice perCollision = aod::track::collisionId; + Preslice timeTracksPerCollision = aod::pidflowtimetrack::collisionId; + Preslice particlesPerMCCollision = aod::mcparticle::mcCollisionId; // Connect to ccdb Service ccdb{}; @@ -430,22 +936,35 @@ struct PidFlowPtCorr { // val used for bootstrap TRandom3* fRndm = new TRandom3(0); OutputObj fFCCh{FlowContainer("FlowContainerCharged")}; + OutputObj fFCChDifferential{FlowContainer("FlowContainerChargedDifferential")}; OutputObj fFCPi{FlowContainer("FlowContainerPi")}; OutputObj fFCKa{FlowContainer("FlowContainerKa")}; OutputObj fFCPr{FlowContainer("FlowContainerPr")}; OutputObj fFCUnidentified{FlowContainer("FlowContainerUnidentified")}; OutputObj fFCPidPtCorr{FlowContainer("FlowContainerPidPtCorr")}; + bool timeAcceptanceLoaded = false; + bool timeEfficiencyLoaded = false; // end val used for bootstrap // define global variables GFW* fGFW = new GFW(); // GFW class used from main src std::vector corrconfigs; + // Independent reference pT range and Q-vectors for charged differential v2{2}. + // Group runtime state to stay below the GCC DPL limit of 100 task members. + struct { + std::unique_ptr gfw; + std::unique_ptr ptAxis; + std::vector configs; + } chargedDifferential; + enum ChargedDifferentialMask { + kDifferentialReference = 1, + kDifferentialPOI = 2 + }; std::vector cfgAcceptance; std::vector cfgAcceptanceWithPt; std::vector cfgMultPVCutPara; - std::vector cfgNSigma; std::vector runNumbers; enum MyParticleType { @@ -509,15 +1028,20 @@ struct PidFlowPtCorr { funcProcessPidResponseMatrix, funcProcessDataC22DeltaPt, funcProcessPidPtCorrelations, + funcProcessDataTimeAssociation, + funcFillCorrectionGraphTimeAssociation, + funcProcessMCTimeAssociation, + funcProcessTimeAssociationComparison, + funcProcessChargedDifferentialTimeAssociation, funcNumber }; enum PidResponseBin { kPidRespPion = 0, - kPidRespKaon, - kPidRespProton, - kPidRespOther, - kPidRespNTrueBins, + kPidRespKaon = 1, + kPidRespProton = 2, + kPidRespOther = 3, + kPidRespNTrueBins = 4, kPidRespUnidentified = kPidRespOther, kPidRespNRecoBins = kPidRespNTrueBins }; @@ -535,34 +1059,224 @@ struct PidFlowPtCorr { TF1* fT0AV0AMean = nullptr; TF1* fT0AV0ASigma = nullptr; - // Declare the pt, mult and phi Axis; + // Index order is pion, kaon, proton; stage 0 is before PID, stage 1 is + // after the current unique PID assignment. The registry owns the objects. + struct PidQualityQAHists { + std::array tpcPIDClustersVsPt{}; + std::array tpcNSigmaVsPIDClusters{}; + std::array itsNSigmaVsPt{}; + std::array tpcVsITSNSigma{}; + TH2* itsClusterSizeVsP = nullptr; + TH2* itsClusterSizeVsPt = nullptr; + TProfile* meanITSClusterSizeVsP = nullptr; + TProfile* meanITSClusterSizeVsPt = nullptr; + }; + std::array pidQualityQAHists{}; - std::vector funcEff; - TH1D* hFindPtBin = nullptr; - TF1* funcV2 = nullptr; - TF1* funcV3 = nullptr; - TF1* funcV4 = nullptr; + const char* getActivityAxisTitle() const + { + return cfgUseMultiplicity.value ? "N_{tracks}^{PV} (|#eta| < 0.8)" : "Centrality (%)"; + } - // hists for QA runbyrun - struct qaHist { - TH1* hPhiRunByRunBefore = nullptr; - TH1* hPhiRunByRunAfter = nullptr; + void initPidQualityQA() + { + const AxisSpec ptAxis{cfgaxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec itsPtAxis{cfgaxisITSPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec etaAxis{cfgaxisEta, "#eta"}; + const AxisSpec pAxis{cfgaxisPIDMomentum, "#it{p} (GeV/#it{c})"}; + const AxisSpec centAxis{axisMultiplicity, getActivityAxisTitle()}; + const AxisSpec clusterAxis{cfgaxisTPCPIDClusters, "TPC clusters used for PID"}; + const AxisSpec foundClusterAxis{cfgaxisTPCPIDClusters, "TPC found clusters"}; + const AxisSpec tpcNSigmaAxis{cfgaxisPIDNSigma, "n#sigma_{TPC}"}; + const AxisSpec itsNSigmaAxis{cfgaxisITSNSigma, "n#sigma_{ITS}"}; + const AxisSpec itsSizeAxis{cfgaxisITSClusterSize, "#LTITS cluster size#GT_{trunc}/cosh(#eta)"}; + registry.add("DetectorPidQA/PIDQuality/tpcPIDClustersAll", "Before PID;#it{p}_{T} (GeV/#it{c});TPC clusters used for PID", {HistType::kTH2F, {ptAxis, clusterAxis}}); + registry.add("DetectorPidQA/PIDQuality/tpcPIDClustersUnidentified", "PID failed or ambiguous;#it{p}_{T} (GeV/#it{c});TPC clusters used for PID", {HistType::kTH2F, {ptAxis, clusterAxis}}); + registry.add("DetectorPidQA/PIDQuality/tpcPIDClustersVsFound", "Before PID;TPC found clusters;TPC clusters used for PID", {HistType::kTH2F, {foundClusterAxis, clusterAxis}}); + registry.add("DetectorPidQA/PIDQuality/itsClusterSizeAll", "Before PID;#it{p} (GeV/#it{c});#LTITS cluster size#GT_{trunc}/cosh(#eta)", {HistType::kTH2F, {pAxis, itsSizeAxis}}); + registry.add("DetectorPidQA/PIDQuality/itsClusterSizeAllVsPt", "Before PID;#it{p}_{T} (GeV/#it{c});#LTITS cluster size#GT_{trunc}/cosh(#eta)", {HistType::kTH2F, {itsPtAxis, itsSizeAxis}}); + registry.add("DetectorPidQA/PIDQuality/itsClusterSizeVsPEta", "Before PID: ITS response versus momentum and eta", {HistType::kTHnSparseF, {pAxis, etaAxis, itsSizeAxis}}); + registry.add("DetectorPidQA/PIDQuality/itsResponseStatus", "ITS QA validity;status;tracks", {HistType::kTH1D, {{4, 0., 4.}}}); + auto* statusAxis = registry.get(HIST("DetectorPidQA/PIDQuality/itsResponseStatus"))->GetXaxis(); + statusAxis->SetBinLabel(1, "Fewer than 2 cluster sizes"); + statusAxis->SetBinLabel(2, "Invalid momentum or eta"); + statusAxis->SetBinLabel(3, "Nonfinite ITS response"); + statusAxis->SetBinLabel(4, "All ITS hypotheses finite"); + + constexpr std::array species{"Pi", "Ka", "Pr"}; + constexpr std::array stages{"BeforePID", "AfterPID"}; + std::array expectedITSResponse{}; + for (std::size_t i = 0; i < species.size(); ++i) { + auto& h = pidQualityQAHists[i]; + for (std::size_t stage = 0; stage < stages.size(); ++stage) { + const std::string path = std::string("DetectorPidQA/PIDQuality/") + stages[stage] + "/" + species[i] + "/"; + h.tpcPIDClustersVsPt[stage] = registry.add(path + "tpcPIDClustersVsPt", "TPC PID cluster count", HistType::kTH2F, {ptAxis, clusterAxis}).get(); + h.tpcNSigmaVsPIDClusters[stage] = registry.add(path + "tpcNSigmaVsPIDClusters", "TPC PID response versus PID cluster count", HistType::kTHnSparseF, {ptAxis, centAxis, clusterAxis, tpcNSigmaAxis}).get(); + h.itsNSigmaVsPt[stage] = registry.add(path + "itsNSigmaVsPt", "ITS response for this mass hypothesis", HistType::kTH2F, {itsPtAxis, itsNSigmaAxis}).get(); + h.tpcVsITSNSigma[stage] = registry.add(path + "tpcVsITSNSigma", "TPC versus ITS response for this mass hypothesis", HistType::kTHnSparseF, {ptAxis, centAxis, tpcNSigmaAxis, itsNSigmaAxis}).get(); + } + const std::string path = std::string("DetectorPidQA/PIDQuality/AfterPID/") + species[i] + "/"; + h.itsClusterSizeVsP = registry.add(path + "itsClusterSizeVsP", "ITS cluster size after current PID", HistType::kTH2F, {pAxis, itsSizeAxis}).get(); + h.itsClusterSizeVsPt = registry.add(path + "itsClusterSizeVsPt", "ITS cluster size after current PID", HistType::kTH2F, {itsPtAxis, itsSizeAxis}).get(); + h.meanITSClusterSizeVsP = registry.add(path + "meanITSClusterSizeVsP", "Measured mean ITS cluster size after current PID;#it{p} (GeV/#it{c});#LTITS signal#GT", HistType::kTProfile, {pAxis}).get(); + h.meanITSClusterSizeVsPt = registry.add(path + "meanITSClusterSizeVsPt", "Measured mean ITS cluster size after current PID;#it{p}_{T} (GeV/#it{c});#LTITS signal#GT", HistType::kTProfile, {itsPtAxis}).get(); + const std::string expectedPath = std::string("DetectorPidQA/PIDQuality/Expected/") + species[i] + "/itsClusterSizeVsP"; + expectedITSResponse[i] = registry.add(expectedPath, "ITS expected mean (model, not measured data)", HistType::kTProfile, {pAxis}).get(); + } + // Identical axes and response parameters preserve model values on merge. + // Entries count model samples per worker, not tracks; do not normalize. + // Sample bin centers; the response is defined only at positive momentum. + for (int bin = 1; bin <= expectedITSResponse[0]->GetNbinsX(); ++bin) { + const float p = expectedITSResponse[0]->GetXaxis()->GetBinCenter(bin); + if (p <= 0.f) { + continue; + } + const std::array expected{ + o2::aod::ITSResponse::expSignal(p), + o2::aod::ITSResponse::expSignal(p), + o2::aod::ITSResponse::expSignal(p)}; + for (std::size_t i = 0; i < expected.size(); ++i) { + if (std::isfinite(expected[i])) { + expectedITSResponse[i]->Fill(p, expected[i]); + } + } + } + } - TH2* hITSnclsVsPhi = nullptr; - TH2* hITSChi2VsPhi = nullptr; + template + void fillPidQualityQA(TrackObject const& track, float cent, int pid) + { + // Called only by detectorPidQA, once per quality-selected track. All + // counts have unit weight; PID decisions come from getPidConfigurable. + const float pt = track.pt(); + const int nPIDClusters = track.tpcNClsPID(); + registry.fill(HIST("DetectorPidQA/PIDQuality/tpcPIDClustersAll"), pt, nPIDClusters); + registry.fill(HIST("DetectorPidQA/PIDQuality/tpcPIDClustersVsFound"), track.tpcNClsFound(), nPIDClusters); + if (pid < 0) { + registry.fill(HIST("DetectorPidQA/PIDQuality/tpcPIDClustersUnidentified"), pt, nPIDClusters); + } - qaHist(TH1* hPhiRunByRunBefore, TH1* hPhiRunByRunAfter, TH2* hITSnclsVsPhi, TH2* hITSChi2VsPhi) - : hPhiRunByRunBefore(hPhiRunByRunBefore), hPhiRunByRunAfter(hPhiRunByRunAfter), hITSnclsVsPhi(hITSnclsVsPhi), hITSChi2VsPhi(hITSChi2VsPhi) - { + constexpr std::array species{MyParticleType::kPion, MyParticleType::kKaon, MyParticleType::kProton}; + const std::array tpcNSigma{track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; + for (std::size_t i = 0; i < species.size(); ++i) { + if (!isWithinPOIPtRange(species[i], pt)) { + continue; + } + for (std::size_t stage = 0; stage < 2; ++stage) { + if (stage == 1 && pid != species[i]) { + continue; + } + auto& h = pidQualityQAHists[i]; + h.tpcPIDClustersVsPt[stage]->Fill(pt, nPIDClusters); + if (std::isfinite(tpcNSigma[i])) { + const std::array values{pt, cent, static_cast(nPIDClusters), tpcNSigma[i]}; + h.tpcNSigmaVsPIDClusters[stage]->Fill(values.data()); + } + } } - }; - std::vector qaHistVector; - // end hists for QA runbyrun + + // averageClusterSize removes the largest cluster and divides by N-1. + const uint32_t clusterSizes = track.itsClusterSizes(); + int nSizeLayers = 0; + for (int layer = 0; layer < 7; ++layer) { + nSizeLayers += ((clusterSizes >> (4 * layer)) & 0xf) != 0; + } + if (!track.hasITS() || nSizeLayers < 2) { + registry.fill(HIST("DetectorPidQA/PIDQuality/itsResponseStatus"), 0.5); + return; + } + const float momentum = track.p(); + if (!std::isfinite(momentum) || momentum <= 0.f || !std::isfinite(track.eta())) { + registry.fill(HIST("DetectorPidQA/PIDQuality/itsResponseStatus"), 1.5); + return; + } + const float clusterSize = o2::aod::ITSResponse::averageClusterSize(clusterSizes) / std::cosh(track.eta()); + // Reuse the response used by the low-pT ITS PID; do not change + // its global parameters or initialize a different data/MC response here. + const std::array itsNSigma{ + o2::aod::ITSResponse::nSigmaITS(track), + o2::aod::ITSResponse::nSigmaITS(track), + o2::aod::ITSResponse::nSigmaITS(track)}; + const bool allFinite = std::isfinite(clusterSize) && std::all_of(itsNSigma.begin(), itsNSigma.end(), [](float value) { return std::isfinite(value); }); + registry.fill(HIST("DetectorPidQA/PIDQuality/itsResponseStatus"), allFinite ? 3.5 : 2.5); + if (std::isfinite(clusterSize)) { + registry.fill(HIST("DetectorPidQA/PIDQuality/itsClusterSizeAll"), momentum, clusterSize); + registry.fill(HIST("DetectorPidQA/PIDQuality/itsClusterSizeAllVsPt"), pt, clusterSize); + registry.fill(HIST("DetectorPidQA/PIDQuality/itsClusterSizeVsPEta"), momentum, track.eta(), clusterSize); + } + for (std::size_t i = 0; i < species.size(); ++i) { + if (!isWithinPOIPtRange(species[i], pt)) { + continue; + } + auto& h = pidQualityQAHists[i]; + if (pid == species[i] && std::isfinite(clusterSize)) { + h.itsClusterSizeVsP->Fill(momentum, clusterSize); + h.itsClusterSizeVsPt->Fill(pt, clusterSize); + h.meanITSClusterSizeVsP->Fill(momentum, clusterSize); + h.meanITSClusterSizeVsPt->Fill(pt, clusterSize); + } + if (!std::isfinite(itsNSigma[i])) { + continue; + } + for (std::size_t stage = 0; stage < 2; ++stage) { + if (stage == 1 && pid != species[i]) { + continue; + } + h.itsNSigmaVsPt[stage]->Fill(pt, itsNSigma[i]); + if (std::isfinite(tpcNSigma[i])) { + const std::array values{pt, cent, tpcNSigma[i], itsNSigma[i]}; + h.tpcVsITSNSigma[stage]->Fill(values.data()); + } + } + } + } // others - void init(InitContext const&) // Initialization + void initChargedTimeAssociation(InitContext& context) + { + if (doprocessData || dofillCorrectionGraph || doprocessReco || doprocessSim || doprocessReco1D || doprocessSim1D || + doprocessDataC22DeltaPt || doprocessPidPtCorrelations || doprocessMCClosure || doprocessQA || dodetectorPidQA || doprocessPidResponseMatrix) { + LOGF(fatal, "Charged time mode: explicitly disable all original process switches to avoid mixed association/PID samples"); + } + if (!switchsOpts.cfgOutputCharged || switchsOpts.cfgOutputPion || switchsOpts.cfgOutputKaon || switchsOpts.cfgOutputProton || switchsOpts.cfgOutputUnidentified) { + LOGF(fatal, "Charged time mode requires cfgOutputCharged=true and all four PID/unidentified output switches=false"); + } + if (dofillCorrectionGraphTimeAssociation && !switchsOpts.cfgOutputrunbyrun) { + LOGF(fatal, "fillCorrectionGraphTimeAssociation requires switchsOpts.cfgOutputrunbyrun=true"); + } + if (doprocessMCTimeAssociation && !switchsOpts.cfgOutPutMC) { + LOGF(fatal, "processMCTimeAssociation requires switchsOpts.cfgOutPutMC=true"); + } + if (!timeAssociationOption(context, "pid-flow-charged-time-producer", "processTimeTracks") || + timeAssociationOption(context, "pid-flow-charged-time-producer", "processDummy")) { + LOGF(fatal, "Charged time mode requires pid-flow-charged-time-producer: processTimeTracks=true, processDummy=false"); + } + } + + void init(InitContext& context) // Initialization { + const bool timeMode = doprocessDataTimeAssociation || doprocessChargedDifferentialTimeAssociation || dofillCorrectionGraphTimeAssociation || doprocessMCTimeAssociation || doprocessTimeAssociationComparison; + if (timeMode) { + initChargedTimeAssociation(context); + } + const AxisSpec activityAxis{axisMultiplicity, getActivityAxisTitle()}; + // FlowContainer's AxisSpec overload expects a full array of bin edges. + // Use its uniform-axis overload for fixed-width axisMultiplicity settings. + const auto initializeFlowContainer = [&](auto& container, TObjArray* profiles) { + if (activityAxis.nBins.has_value()) { + container->Initialize(profiles, activityAxis.nBins.value(), activityAxis.binEdges.front(), activityAxis.binEdges.back(), cfgFlowNbootstrap); + } else { + container->Initialize(profiles, activityAxis, cfgFlowNbootstrap); + } + container->GetProfile()->GetXaxis()->SetTitle(getActivityAxisTitle()); + if (auto* subprofiles = container->GetSubProfiles()) { + for (auto* profile : *subprofiles) { + static_cast(profile)->GetXaxis()->SetTitle(getActivityAxisTitle()); + } + } + }; + // init and add lots of graphs according to switch ccdb->setURL(cfgurl.value); ccdb->setCaching(true); @@ -571,9 +1285,15 @@ struct PidFlowPtCorr { cfgAcceptance = correctionPathOpts.cfgAcceptancePath.value; cfgAcceptanceWithPt = correctionPathOpts.cfgAcceptancePathWithPt.value; - cfgNSigma = cfgNSigmapid; cfgMultPVCutPara = evtSeleOpts.cfgMultPVCut; + if (!timeMode) { + fTPCPIDClustersMin = std::make_unique("pidFlowPtCorrTPCPIDClustersMin", cfgTPCPIDClustersMinFunction.value.c_str(), 0., 256.); + if (!fTPCPIDClustersMin->IsValid() || fTPCPIDClustersMin->GetNpar() != 0) { + LOGF(fatal, "Invalid cfgTPCPIDClustersMinFunction: use a ROOT formula in x = tpcNClsFound() with numeric coefficients and no free parameters"); + } + } + // Set the pt, mult and phi Axis; if (dcaCutOpts.cfgDCAxyNSigma > 0) { @@ -599,8 +1319,9 @@ struct PidFlowPtCorr { registry.add("hVtxZ", "", {HistType::kTH1D, {cfgaxisVertex}}); registry.add("hMult", "", {HistType::kTH1D, {cfgaxisNch}}); registry.add("hMultTPC", "", {HistType::kTH1D, {cfgaxisNch}}); - std::string hCentTitle = "Centrality distribution, Estimator " + std::to_string(cfgCentEstimator); - registry.add("hCent", hCentTitle.c_str(), {HistType::kTH1D, {{90, 0, 90}}}); + std::string hCentTitle = cfgUseMultiplicity.value ? "PV multiplicity distribution" : "Centrality distribution, Estimator " + std::to_string(cfgCentEstimator); + const AxisSpec hCentAxis = cfgUseMultiplicity.value ? activityAxis : AxisSpec{90, 0., 90., getActivityAxisTitle()}; + registry.add("hCent", hCentTitle.c_str(), {HistType::kTH1D, {hCentAxis}}); registry.add("hPt", "", {HistType::kTH1D, {cfgaxisPt}}); registry.add("hPtCorr", "", {HistType::kTH1D, {cfgaxisPt}}); registry.add("hPtPi", "", {HistType::kTH1D, {cfgaxisPt}}); @@ -609,68 +1330,49 @@ struct PidFlowPtCorr { registry.add("hPtCorrKa", "", {HistType::kTH1D, {cfgaxisPt}}); registry.add("hPtPr", "", {HistType::kTH1D, {cfgaxisPt}}); registry.add("hPtCorrPr", "", {HistType::kTH1D, {cfgaxisPt}}); - registry.add("hNTracksPVvsCentrality", "", {HistType::kTH2D, {{5000, 0, 5000}, axisMultiplicity}}); + registry.add("hNTracksPVvsCentrality", "", {HistType::kTH2D, {{5000, 0, 5000}, activityAxis}}); registry.add("hNchUnCorrectedVSNchCorrected", "", {HistType::kTH2D, {cfgaxisNch, cfgaxisNch}}); runNumbers = cfgRunNumbers; // Minimal detector-PID performance QA. if (switchsOpts.cfgOutputQA.value) { + initPidQualityQA(); registry.add("DetectorPidQA/TPC/dEdxPi", "TPC d#it{E}/d#it{x} (pion)", {HistType::kTH2F, {cfgaxisPt, cfgaxisTPCDedx}}); registry.add("DetectorPidQA/TPC/dEdxKa", "TPC d#it{E}/d#it{x} (kaon)", {HistType::kTH2F, {cfgaxisPt, cfgaxisTPCDedx}}); registry.add("DetectorPidQA/TPC/dEdxPr", "TPC d#it{E}/d#it{x} (proton)", {HistType::kTH2F, {cfgaxisPt, cfgaxisTPCDedx}}); + registry.add("DetectorPidQA/TPC/dEdxAll", "TPC d#it{E}/d#it{x} (all particles)", {HistType::kTH2F, {cfgaxisPt, cfgaxisTPCDedx}}); registry.add("DetectorPidQA/TOF/deltaPi", "TOF #it{t} - #it{t}_{ev} - #it{t}_{exp} (pion)", {HistType::kTH2F, {cfgaxisPt, cfgaxisTOFDelta}}); registry.add("DetectorPidQA/TOF/deltaKa", "TOF #it{t} - #it{t}_{ev} - #it{t}_{exp} (kaon)", {HistType::kTH2F, {cfgaxisPt, cfgaxisTOFDelta}}); registry.add("DetectorPidQA/TOF/deltaPr", "TOF #it{t} - #it{t}_{ev} - #it{t}_{exp} (proton)", {HistType::kTH2F, {cfgaxisPt, cfgaxisTOFDelta}}); - - // run by run QA hists - /** - * @brief create QA hist for each run - * @note include: - * 1. hPhi before collision cut - * 2. hPhi after collision cut - * > graph below are after collision cut < - * 3. ITS ncls vs Phi - * 4. ITS chi2 vs Phi - * @param runNumbers - */ - for (const auto& oneRun : runNumbers) { - TH1* hPhiRunByRunBefore = registry.add(Form("RunByRunQA%d/hPhiBefore", oneRun), "", {HistType::kTH1D, {cfgaxisPhi}}).get(); - TH1* hPhiRunByRunAfter = registry.add(Form("RunByRunQA%d/hPhiAfter", oneRun), "", {HistType::kTH1D, {cfgaxisPhi}}).get(); - - TH2* hITSnclsVsPhi = registry.add(Form("RunByRunQA%d/hITSnclsVsPhi", oneRun), "", {HistType::kTH2D, {cfgaxisPhi, {8, 0, 8}}}).get(); - TH2* hITSChi2VsPhi = registry.add(Form("RunByRunQA%d/hITSChi2VsPhi", oneRun), "", {HistType::kTH2D, {cfgaxisPhi, {100, 0, 10}}}).get(); - - qaHistVector.emplace_back(qaHist(hPhiRunByRunBefore, hPhiRunByRunAfter, hITSnclsVsPhi, hITSChi2VsPhi)); - } - // end run by run QA hists + registry.add("DetectorPidQA/TOF/tofTimeAll", "TOF #it{t} - #it{t}_{ev} (all particles)", {HistType::kTH2F, {cfgaxisPt, cfgaxisTOFTime}}); } // cfgoutputqa if (switchsOpts.cfgOutPutMC.value) { // hist for NUE correction - registry.add("correction/hPtCentMcRec", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("correction/hPtCentMcGen", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); + registry.add("correction/hPtCentMcRec", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcGen", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); // ITS only // hist for Pi eff - registry.add("correction/hPtCentMcRecPi", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("correction/hPtCentMcGenPi", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); + registry.add("correction/hPtCentMcRecPi", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcGenPi", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); // hist for Ka eff - registry.add("correction/hPtCentMcRecKa", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("correction/hPtCentMcGenKa", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); + registry.add("correction/hPtCentMcRecKa", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcGenKa", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); // hist for Pr eff - registry.add("correction/hPtCentMcRecPr", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("correction/hPtCentMcGenPr", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); + registry.add("correction/hPtCentMcRecPr", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcGenPr", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); // Charged-track reconstruction efficiency split by MC true species. // Detector PID is deliberately not used for these numerators. - registry.add("correction/hPtCentMcRecTruePi", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("correction/hPtCentMcRecTrueKa", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("correction/hPtCentMcRecTruePr", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("correction/hPtCentMcRecTrueOther", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("correction/hPtCentMcGenTruePi", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("correction/hPtCentMcGenTrueKa", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("correction/hPtCentMcGenTruePr", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("correction/hPtCentMcGenTrueOther", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); + registry.add("correction/hPtCentMcRecTruePi", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcRecTrueKa", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcRecTruePr", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcRecTrueOther", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcGenTruePi", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcGenTrueKa", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcGenTruePr", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcGenTrueOther", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); } // cfgoutputMC if (switchsOpts.cfgOutPutMC1D.value) { @@ -686,10 +1388,10 @@ struct PidFlowPtCorr { } if (switchsOpts.cfgOutPutPtSpectra.value) { - registry.add("ptSpectra/hPtCentData", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("ptSpectra/hCentEventCountData", "", {HistType::kTH1D, {axisMultiplicity}}); - registry.add("ptSpectra/hCentEventCountMcGen", "", {HistType::kTH1D, {axisMultiplicity}}); - registry.add("ptSpectra/hCentEventCountMcRec", "", {HistType::kTH1D, {axisMultiplicity}}); + registry.add("ptSpectra/hPtCentData", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("ptSpectra/hCentEventCountData", "", {HistType::kTH1D, {activityAxis}}); + registry.add("ptSpectra/hCentEventCountMcGen", "", {HistType::kTH1D, {activityAxis}}); + registry.add("ptSpectra/hCentEventCountMcRec", "", {HistType::kTH1D, {activityAxis}}); registry.add("c22PrimeVsc22/Pi", "", {HistType::kTH2D, {{100, 0., 0.01}, {100, 0., 0.01}}}); registry.add("c22PrimeVsc22/Ka", "", {HistType::kTH2D, {{100, 0., 0.01}, {100, 0., 0.01}}}); @@ -719,10 +1421,10 @@ struct PidFlowPtCorr { registry.add("abundance/hNumOfPrEventCount", "", {HistType::kTH1D, {particleAbundanceOpts.cfgaxisAbundancePr}}); } if (particleAbundanceOpts.cfgOutputAbundanceYields.value) { - registry.add("abundance/hPtCentDataPi", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("abundance/hPtCentDataKa", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("abundance/hPtCentDataPr", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("abundance/hPtCentDataUnidentified", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); + registry.add("abundance/hPtCentDataPi", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("abundance/hPtCentDataKa", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("abundance/hPtCentDataPr", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("abundance/hPtCentDataUnidentified", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); } // set bin label for hEventCount @@ -752,6 +1454,34 @@ struct PidFlowPtCorr { registry.addClone("hEventCount/processData", "hEventCount/processSim"); // processPidResponseMatrix registry.addClone("hEventCount/processData", "hEventCount/processPidResponseMatrix"); + if (timeMode) { + auto originalCounter = registry.get(HIST("hEventCount/processData")); + for (auto const& name : {"hEventCount/processDataTimeAssociation", "hEventCount/fillCorrectionGraphTimeAssociation", "hEventCount/processMCTimeAssociation", "hEventCount/processChargedDifferentialTimeAssociation"}) { + auto counter = registry.add(name, "Target collisions;selection;collisions", {HistType::kTH1D, {{15, 0, 15}}}); + for (int bin = 1; bin <= originalCounter->GetNbinsX(); ++bin) { + counter->GetXaxis()->SetBinLabel(bin, originalCounter->GetXaxis()->GetBinLabel(bin)); + } + counter->GetXaxis()->SetBinLabel(15, "has associated baseline track"); + } + if (doprocessMCTimeAssociation && cfgTimeAssociationQA) { + registry.add("timeAssociation/mcEventStatus", "MC event sample;stage;target collisions", {HistType::kTH1D, {{4, 0, 4}}}); + const std::array mcLabels{"target collision", "has MC label", "one reco per MC in vertex acceptance", "accepted for reco and gen"}; + for (std::size_t bin = 0; bin < mcLabels.size(); ++bin) { + registry.get(HIST("timeAssociation/mcEventStatus"))->GetXaxis()->SetBinLabel(bin + 1, mcLabels[bin]); + } + registry.add("timeAssociation/mcTrackStatus", "Selected charged tracks;truth category;tracks", {HistType::kTH1D, {{4, 0, 4}}}); + const std::array mcLabelsTrack{"no MC particle", "other MC collision", "same MC outside primary truth sample", "matched primary numerator"}; + for (std::size_t bin = 0; bin < mcLabelsTrack.size(); ++bin) { + registry.get(HIST("timeAssociation/mcTrackStatus"))->GetXaxis()->SetBinLabel(bin + 1, mcLabelsTrack[bin]); + } + registry.add("timeAssociation/hPtCentMcSelected", "All selected reassociated tracks;p_{T};centrality", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); + registry.add("timeAssociation/hPtCentMcOtherCollision", "Tracks from a different true event;p_{T};centrality", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); + } + } + if (doprocessTimeAssociationComparison && cfgTimeAssociationQA) { + registry.addClone("hEventCount/processData", "hEventCount/processTimeAssociationComparison"); + initTimeAssociationComparisonQA(); + } if (switchsOpts.cfgAddPidPtCorrelationProfiles.value) { registry.addClone("hEventCount/processData", "hEventCount/processPidPtCorrelations"); } @@ -762,24 +1492,24 @@ struct PidFlowPtCorr { // Store the ensemble mean pT required for the offline delta[pT]/<[pT]> // transformation. The profile weight is the NUE-corrected particle count. if (switchsOpts.cfgOutputCharged.value) { - registry.add("c22DeltaPt/c22dmeanptCharged", "Charged c_{2}{2} versus charged event [p_{T}];Centrality (%);[p_{T}]_{ch} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", {HistType::kTProfile3D, {axisMultiplicity, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("c22DeltaPt/hMeanPtCharged", "Charged mean p_{T};Centrality (%);Bootstrap subsample;[p_{T}]_{ch} (GeV/#it{c})", {HistType::kTProfile2D, {axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/c22dmeanptCharged", Form("Charged c_{2}{2} versus charged event [p_{T}];%s;[p_{T}]_{ch} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/hMeanPtCharged", Form("Charged mean p_{T};%s;Bootstrap subsample;[p_{T}]_{ch} (GeV/#it{c})", getActivityAxisTitle()), {HistType::kTProfile2D, {activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); } if (switchsOpts.cfgOutputPion.value) { - registry.add("c22DeltaPt/hMeanPtPion", "Pion mean p_{T};Centrality (%);Bootstrap subsample;[p_{T}]_{#pi} (GeV/#it{c})", {HistType::kTProfile2D, {axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("c22DeltaPt/c22dmeanptPionRefRef", "Ref-ref c_{2}{2} versus pion event [p_{T}];Centrality (%);[p_{T}]_{#pi} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", {HistType::kTProfile3D, {axisMultiplicity, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/hMeanPtPion", Form("Pion mean p_{T};%s;Bootstrap subsample;[p_{T}]_{#pi} (GeV/#it{c})", getActivityAxisTitle()), {HistType::kTProfile2D, {activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/c22dmeanptPionRefRef", Form("Ref-ref c_{2}{2} versus pion event [p_{T}];%s;[p_{T}]_{#pi} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); } if (switchsOpts.cfgOutputKaon.value) { - registry.add("c22DeltaPt/hMeanPtKaon", "Kaon mean p_{T};Centrality (%);Bootstrap subsample;[p_{T}]_{K} (GeV/#it{c})", {HistType::kTProfile2D, {axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("c22DeltaPt/c22dmeanptKaonRefRef", "Ref-ref c_{2}{2} versus kaon event [p_{T}];Centrality (%);[p_{T}]_{K} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", {HistType::kTProfile3D, {axisMultiplicity, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/hMeanPtKaon", Form("Kaon mean p_{T};%s;Bootstrap subsample;[p_{T}]_{K} (GeV/#it{c})", getActivityAxisTitle()), {HistType::kTProfile2D, {activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/c22dmeanptKaonRefRef", Form("Ref-ref c_{2}{2} versus kaon event [p_{T}];%s;[p_{T}]_{K} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); } if (switchsOpts.cfgOutputProton.value) { - registry.add("c22DeltaPt/hMeanPtProton", "Proton mean p_{T};Centrality (%);Bootstrap subsample;[p_{T}]_{p} (GeV/#it{c})", {HistType::kTProfile2D, {axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("c22DeltaPt/c22dmeanptProtonRefRef", "Ref-ref c_{2}{2} versus proton event [p_{T}];Centrality (%);[p_{T}]_{p} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", {HistType::kTProfile3D, {axisMultiplicity, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/hMeanPtProton", Form("Proton mean p_{T};%s;Bootstrap subsample;[p_{T}]_{p} (GeV/#it{c})", getActivityAxisTitle()), {HistType::kTProfile2D, {activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/c22dmeanptProtonRefRef", Form("Ref-ref c_{2}{2} versus proton event [p_{T}];%s;[p_{T}]_{p} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); } if (switchsOpts.cfgOutputUnidentified.value) { - registry.add("c22DeltaPt/hMeanPtUnidentified", "Unidentified mean p_{T};Centrality (%);Bootstrap subsample;[p_{T}]_{unidentified} (GeV/#it{c})", {HistType::kTProfile2D, {axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("c22DeltaPt/c22dmeanptUnidentifiedRefRef", "Ref-ref c_{2}{2} versus unidentified event [p_{T}];Centrality (%);[p_{T}]_{unidentified} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", {HistType::kTProfile3D, {axisMultiplicity, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/hMeanPtUnidentified", Form("Unidentified mean p_{T};%s;Bootstrap subsample;[p_{T}]_{unidentified} (GeV/#it{c})", getActivityAxisTitle()), {HistType::kTProfile2D, {activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/c22dmeanptUnidentifiedRefRef", Form("Ref-ref c_{2}{2} versus unidentified event [p_{T}];%s;[p_{T}]_{unidentified} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); } // Ref-ref is required in either PID mode: Pure/ref-ref or POI-ref/ref-ref. @@ -788,30 +1518,30 @@ struct PidFlowPtCorr { // profiles are neither allocated nor filled. if (switchsOpts.cfgC22DeltaPtUsePure.value) { if (switchsOpts.cfgOutputPion.value) { - registry.add("c22DeltaPt/c22dmeanptPionPure", "Pion POI-POI c_{2}{2} versus pion event [p_{T}];Centrality (%);[p_{T}]_{#pi} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", {HistType::kTProfile3D, {axisMultiplicity, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/c22dmeanptPionPure", Form("Pion POI-POI c_{2}{2} versus pion event [p_{T}];%s;[p_{T}]_{#pi} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); } if (switchsOpts.cfgOutputKaon.value) { - registry.add("c22DeltaPt/c22dmeanptKaonPure", "Kaon POI-POI c_{2}{2} versus kaon event [p_{T}];Centrality (%);[p_{T}]_{K} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", {HistType::kTProfile3D, {axisMultiplicity, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/c22dmeanptKaonPure", Form("Kaon POI-POI c_{2}{2} versus kaon event [p_{T}];%s;[p_{T}]_{K} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); } if (switchsOpts.cfgOutputProton.value) { - registry.add("c22DeltaPt/c22dmeanptProtonPure", "Proton POI-POI c_{2}{2} versus proton event [p_{T}];Centrality (%);[p_{T}]_{p} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", {HistType::kTProfile3D, {axisMultiplicity, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/c22dmeanptProtonPure", Form("Proton POI-POI c_{2}{2} versus proton event [p_{T}];%s;[p_{T}]_{p} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); } if (switchsOpts.cfgOutputUnidentified.value) { - registry.add("c22DeltaPt/c22dmeanptUnidentifiedPure", "Unidentified POI-POI c_{2}{2} versus unidentified event [p_{T}];Centrality (%);[p_{T}]_{unidentified} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", {HistType::kTProfile3D, {axisMultiplicity, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/c22dmeanptUnidentifiedPure", Form("Unidentified POI-POI c_{2}{2} versus unidentified event [p_{T}];%s;[p_{T}]_{unidentified} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); } } else { // For each PID species, compare POI-ref and ref-ref using the same PID mean-pT axis. if (switchsOpts.cfgOutputPion.value) { - registry.add("c22DeltaPt/c22dmeanptPionPOIRef", "Pion POI-ref c_{2}{2} versus pion event [p_{T}];Centrality (%);[p_{T}]_{#pi} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", {HistType::kTProfile3D, {axisMultiplicity, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/c22dmeanptPionPOIRef", Form("Pion POI-ref c_{2}{2} versus pion event [p_{T}];%s;[p_{T}]_{#pi} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); } if (switchsOpts.cfgOutputKaon.value) { - registry.add("c22DeltaPt/c22dmeanptKaonPOIRef", "Kaon POI-ref c_{2}{2} versus kaon event [p_{T}];Centrality (%);[p_{T}]_{K} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", {HistType::kTProfile3D, {axisMultiplicity, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/c22dmeanptKaonPOIRef", Form("Kaon POI-ref c_{2}{2} versus kaon event [p_{T}];%s;[p_{T}]_{K} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); } if (switchsOpts.cfgOutputProton.value) { - registry.add("c22DeltaPt/c22dmeanptProtonPOIRef", "Proton POI-ref c_{2}{2} versus proton event [p_{T}];Centrality (%);[p_{T}]_{p} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", {HistType::kTProfile3D, {axisMultiplicity, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/c22dmeanptProtonPOIRef", Form("Proton POI-ref c_{2}{2} versus proton event [p_{T}];%s;[p_{T}]_{p} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); } if (switchsOpts.cfgOutputUnidentified.value) { - registry.add("c22DeltaPt/c22dmeanptUnidentifiedPOIRef", "Unidentified POI-ref c_{2}{2} versus unidentified event [p_{T}];Centrality (%);[p_{T}]_{unidentified} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", {HistType::kTProfile3D, {axisMultiplicity, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/c22dmeanptUnidentifiedPOIRef", Form("Unidentified POI-ref c_{2}{2} versus unidentified event [p_{T}];%s;[p_{T}]_{unidentified} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); } } } @@ -821,7 +1551,7 @@ struct PidFlowPtCorr { if (switchsOpts.cfgAddPidResponseMatrixHistograms.value) { registry.add("pidResponseMatrix/hPidResponseMatrix", "PID response matrix;true PID;reconstructed PID", {HistType::kTH2D, {{4, -0.5, 3.5}, {4, -0.5, 3.5}}}); - registry.add("pidResponseMatrix/hPidResponseMatrixRecPtCent", "PID response matrix vs reconstructed pT and centrality;true PID;reconstructed PID;p_{T}^{rec} (GeV/#it{c});Centrality (%)", {HistType::kTHnSparseF, {{4, -0.5, 3.5}, {4, -0.5, 3.5}, cfgaxisPt, axisMultiplicity}}); + registry.add("pidResponseMatrix/hPidResponseMatrixRecPtCent", Form("PID response matrix vs reconstructed pT and event activity;true PID;reconstructed PID;p_{T}^{rec} (GeV/#it{c});%s", getActivityAxisTitle()), {HistType::kTHnSparseF, {{4, -0.5, 3.5}, {4, -0.5, 3.5}, cfgaxisPt, activityAxis}}); auto setPidResponseLabels = [](TAxis* axis, bool includeUnidentified) { axis->SetBinLabel(kPidRespPion + 1, "Pion"); @@ -868,38 +1598,88 @@ struct PidFlowPtCorr { oba4Ch->Add(new TNamed("hMeanPtWeightFull", "hMeanPtWeightFull")); // end fill TObjArray for charged + // Clone the shared profiles before appending the charged-only SC baseline. + auto* oba4PID = dynamic_cast(oba4Ch->Clone()); + oba4Ch->Add(new TNamed("c24Gap", "c24Gap")); + // init fFCCh if (switchsOpts.cfgOutputCharged.value) { fFCCh->SetName("FlowContainerCharged"); - fFCCh->Initialize(oba4Ch, axisMultiplicity, cfgFlowNbootstrap); + initializeFlowContainer(fFCCh, oba4Ch); } // end init fFCCh + if (doprocessChargedDifferentialTimeAssociation) { + const double refPtMin = chargedDifferentialOpts.cfgRefPtMin.value; + const double refPtMax = chargedDifferentialOpts.cfgRefPtMax.value; + const double etaMax = trkQualityOpts.cfgCutEta.value; + const double etaGap = cfgEtaGap.value; + if (!std::isfinite(refPtMin) || !std::isfinite(refPtMax) || refPtMin < 0. || refPtMax <= refPtMin || + !std::isfinite(etaMax) || !std::isfinite(etaGap) || etaGap < 0. || etaGap >= etaMax) { + LOGF(fatal, "Charged differential v2{2} requires 0 <= cfgRefPtMin < cfgRefPtMax and 0 <= cfgEtaGap < cfgCutEta"); + } + const AxisSpec ptAxis{chargedDifferentialOpts.cfgaxisPt, "#it{p}_{T} (GeV/#it{c})"}; + if (ptAxis.binEdges.size() < 2 || ptAxis.binEdges.front() < 0. || + std::any_of(ptAxis.binEdges.begin(), ptAxis.binEdges.end(), [](double edge) { return !std::isfinite(edge); }) || + std::adjacent_find(ptAxis.binEdges.begin(), ptAxis.binEdges.end(), [](double left, double right) { return left >= right; }) != ptAxis.binEdges.end() || + (ptAxis.nBins.has_value() && ptAxis.nBins.value() <= 0)) { + LOGF(fatal, "chargedDifferentialOpts.cfgaxisPt must define a finite, nonnegative, strictly increasing pT axis with at least one bin"); + } + if (ptAxis.nBins.has_value()) { + chargedDifferential.ptAxis = std::make_unique(ptAxis.nBins.value(), ptAxis.binEdges.front(), ptAxis.binEdges.back()); + } else { + chargedDifferential.ptAxis = std::make_unique(static_cast(ptAxis.binEdges.size()) - 1, ptAxis.binEdges.data()); + } + chargedDifferential.ptAxis->SetTitle("#it{p}_{T} (GeV/#it{c})"); + const int ptBins = chargedDifferential.ptAxis->GetNbins(); + chargedDifferential.gfw = std::make_unique(); + chargedDifferential.gfw->AddRegion("refN", -etaMax, -etaGap, 1, kDifferentialReference); + chargedDifferential.gfw->AddRegion("refP", etaGap, etaMax, 1, kDifferentialReference); + chargedDifferential.gfw->AddRegion("poiN", -etaMax, -etaGap, ptBins, kDifferentialPOI); + chargedDifferential.gfw->AddRegion("poiP", etaGap, etaMax, ptBins, kDifferentialPOI); + chargedDifferential.configs.push_back(chargedDifferential.gfw->GetCorrelatorConfig("refN {2} refP {-2}", "ChGap22", false)); + // Disjoint eta sides remove self-pairs even when POI and reference pT overlap. + // Both orientations contribute to the same profile with their own pair weight. + chargedDifferential.configs.push_back(chargedDifferential.gfw->GetCorrelatorConfig("poiN {2} refP {-2}", "ChGap22", true)); + chargedDifferential.configs.push_back(chargedDifferential.gfw->GetCorrelatorConfig("poiP {2} refN {-2}", "ChGap22", true)); + chargedDifferential.gfw->CreateRegions(); + + TObjArray differentialProfiles; + differentialProfiles.SetOwner(true); + differentialProfiles.Add(new TNamed("ChGap22", "Reference-reference <2>, independent differential reference pT range")); + for (int bin = 1; bin <= ptBins; ++bin) { + differentialProfiles.Add(new TNamed(Form("ChGap22_pt_%d", bin), "Charged POI-reference <2> versus pT")); + } + fFCChDifferential->SetXAxis(chargedDifferential.ptAxis.get()); + initializeFlowContainer(fFCChDifferential, &differentialProfiles); + } + // init fFCPID // note that need to add c22pure and c32pure - auto* oba4PID = dynamic_cast(oba4Ch->Clone()); oba4PID->Add(new TNamed("c22pure", "c22pure")); oba4PID->Add(new TNamed("c32pure", "c32pure")); oba4PID->Add(new TNamed("covV2PtPID", "covV2PtPID")); oba4PID->Add(new TNamed("c22TrackWeightPID", "c22TrackWeightPID")); oba4PID->Add(new TNamed("hMeanPtWeightCharged", "hMeanPtWeightCharged")); + // Raw mixed <4> for SC/NSC; reuse PID c22pure and charged c22 as <2>. + oba4PID->Add(new TNamed("c24PIDChargedGap", "c24PIDChargedGap")); if (switchsOpts.cfgOutputPion.value) { fFCPi->SetName("FlowContainerPi"); - fFCPi->Initialize(oba4PID, axisMultiplicity, cfgFlowNbootstrap); + initializeFlowContainer(fFCPi, oba4PID); } if (switchsOpts.cfgOutputKaon.value) { fFCKa->SetName("FlowContainerKa"); - fFCKa->Initialize(oba4PID, axisMultiplicity, cfgFlowNbootstrap); + initializeFlowContainer(fFCKa, oba4PID); } if (switchsOpts.cfgOutputProton.value) { fFCPr->SetName("FlowContainerPr"); - fFCPr->Initialize(oba4PID, axisMultiplicity, cfgFlowNbootstrap); + initializeFlowContainer(fFCPr, oba4PID); } if (switchsOpts.cfgOutputUnidentified.value) { fFCUnidentified->SetName("FlowContainerUnidentified"); - fFCUnidentified->Initialize(oba4PID, axisMultiplicity, cfgFlowNbootstrap); + initializeFlowContainer(fFCUnidentified, oba4PID); } // end init fFCPID @@ -924,43 +1704,43 @@ struct PidFlowPtCorr { oba4PidPtCorr->Add(new TNamed("ptKaInKaPrPairs", "ptKaInKaPrPairs")); oba4PidPtCorr->Add(new TNamed("ptPrInKaPrPairs", "ptPrInKaPrPairs")); fFCPidPtCorr->SetName("FlowContainerPidPtCorr"); - fFCPidPtCorr->Initialize(oba4PidPtCorr, axisMultiplicity, cfgFlowNbootstrap); + initializeFlowContainer(fFCPidPtCorr, oba4PidPtCorr); } if (switchsOpts.cfgAddMeanPtCentNbsHistograms.value) { // TProfile3D axes: event mean pT, centrality, and bootstrap subsample. if (switchsOpts.cfgOutputCharged.value) { - registry.add("meanptCentNbs/hCharged", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hChargedMeanpt", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hCharged", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hChargedMeanpt", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); } if (switchsOpts.cfgOutputPion.value) { - registry.add("meanptCentNbs/hChargedPionWithNpair", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hChargedPionFull", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hPion", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hPionMeanpt", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hChargedPionWithNpair", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hChargedPionFull", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hPion", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hPionMeanpt", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); if (switchsOpts.cfgClosureTest.value != 0) { - registry.add("meanptCentNbs/hPionMeanptWeightC22pure", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hPionMeanptWeightMeanpt", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hPionMeanptWeightC22prime", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hPionMeanptWeightC22pure", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hPionMeanptWeightMeanpt", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hPionMeanptWeightC22prime", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); } } if (switchsOpts.cfgOutputKaon.value) { - registry.add("meanptCentNbs/hChargedKaonWithNpair", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hChargedKaonFull", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hKaon", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hKaonMeanpt", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hChargedKaonWithNpair", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hChargedKaonFull", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hKaon", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hKaonMeanpt", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); } if (switchsOpts.cfgOutputProton.value) { - registry.add("meanptCentNbs/hChargedProtonWithNpair", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hChargedProtonFull", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hProton", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hProtonMeanpt", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hChargedProtonWithNpair", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hChargedProtonFull", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hProton", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hProtonMeanpt", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); } if (switchsOpts.cfgOutputUnidentified.value) { - registry.add("meanptCentNbs/hChargedUnidentifiedWithNpair", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hChargedUnidentifiedFull", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hUnidentified", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hUnidentifiedMeanpt", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hChargedUnidentifiedWithNpair", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hChargedUnidentifiedFull", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hUnidentified", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hUnidentifiedMeanpt", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); } } @@ -979,10 +1759,10 @@ struct PidFlowPtCorr { // end track QA /// @note evetn QA - registry.add("hProcessQA/centVsMult", "cent Vs Mult;Centrality T0C;mulplicity global tracks", {HistType::kTH2D, {axisMultiplicity, cfgaxisNch}}); + registry.add("hProcessQA/centVsMult", Form("Event activity versus input track count;%s;Input track count", getActivityAxisTitle()), {HistType::kTH2D, {activityAxis, cfgaxisNch}}); registry.add("hProcessQA/IR", "", {HistType::kTH1D, {{100, 0, 100}}}); registry.add("hProcessQA/Occupacy", "", {HistType::kTH1D, {{1000, 0, 10000}}}); - registry.add("hProcessQA/occupancyVsCent", "Track occupancy versus centrality;Centrality (%);Track occupancy in time range", {HistType::kTH2D, {axisMultiplicity, {1000, 0, 10000}}}); + registry.add("hProcessQA/occupancyVsCent", Form("Track occupancy versus event activity;%s;Track occupancy in time range", getActivityAxisTitle()), {HistType::kTH2D, {activityAxis, {1000, 0, 10000}}}); // end evetn QA } // end init QA plot for processQA @@ -1006,6 +1786,8 @@ struct PidFlowPtCorr { fGFW->AddRegion("olPiN", -etaMax, 0, 1, gfwBitMask::pion::kOverlap); fGFW->AddRegion("olPiP", 0, etaMax, 1, gfwBitMask::pion::kOverlap); + fGFW->AddRegion("olPiN08", -etaMax, -etaGap, 1, gfwBitMask::pion::kOverlap); + fGFW->AddRegion("olPiP08", etaGap, etaMax, 1, gfwBitMask::pion::kOverlap); // Kaon POI and overlap regions. fGFW->AddRegion("poiKaN08", -etaMax, -etaGap, 1, gfwBitMask::kaon::kPOI); @@ -1015,6 +1797,8 @@ struct PidFlowPtCorr { fGFW->AddRegion("olKaN", -etaMax, 0, 1, gfwBitMask::kaon::kOverlap); fGFW->AddRegion("olKaP", 0, etaMax, 1, gfwBitMask::kaon::kOverlap); + fGFW->AddRegion("olKaN08", -etaMax, -etaGap, 1, gfwBitMask::kaon::kOverlap); + fGFW->AddRegion("olKaP08", etaGap, etaMax, 1, gfwBitMask::kaon::kOverlap); // Proton POI and overlap regions. fGFW->AddRegion("poiPrN08", -etaMax, -etaGap, 1, gfwBitMask::proton::kPOI); @@ -1024,6 +1808,8 @@ struct PidFlowPtCorr { fGFW->AddRegion("olPrN", -etaMax, 0, 1, gfwBitMask::proton::kOverlap); fGFW->AddRegion("olPrP", 0, etaMax, 1, gfwBitMask::proton::kOverlap); + fGFW->AddRegion("olPrN08", -etaMax, -etaGap, 1, gfwBitMask::proton::kOverlap); + fGFW->AddRegion("olPrP08", etaGap, etaMax, 1, gfwBitMask::proton::kOverlap); // Tracks for which the PID selection returns -1. They use the charged // pT range and corrections, but have independent POI and overlap masks. @@ -1033,6 +1819,8 @@ struct PidFlowPtCorr { fGFW->AddRegion("poiUnidentifiedP", 0, etaMax, 1, gfwBitMask::unidentified::kPOI); fGFW->AddRegion("olUnidentifiedN", -etaMax, 0, 1, gfwBitMask::unidentified::kOverlap); fGFW->AddRegion("olUnidentifiedP", 0, etaMax, 1, gfwBitMask::unidentified::kOverlap); + fGFW->AddRegion("olUnidentifiedN08", -etaMax, -etaGap, 1, gfwBitMask::unidentified::kOverlap); + fGFW->AddRegion("olUnidentifiedP08", etaGap, etaMax, 1, gfwBitMask::unidentified::kOverlap); // end data region add // pushback @@ -1090,6 +1878,16 @@ struct PidFlowPtCorr { corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiUnidentifiedN reffull | olUnidentifiedN {2 -2}", "UnidentifiedFull22a", false)); corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiUnidentifiedP reffull | olUnidentifiedP {2 -2}", "UnidentifiedFull22b", false)); // 47 + // One POI and one reference on each eta side, with distinct tracks. + // Each overlap region has the same eta range as its POI/reference pair. + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiPiN08 refN08 | olPiN08 {2 2} poiPiP08 refP08 | olPiP08 {-2 -2}", "PionPIDChargedGap24", false)); // 48 + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiKaN08 refN08 | olKaN08 {2 2} poiKaP08 refP08 | olKaP08 {-2 -2}", "KaonPIDChargedGap24", false)); + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiPrN08 refN08 | olPrN08 {2 2} poiPrP08 refP08 | olPrP08 {-2 -2}", "ProtonPIDChargedGap24", false)); + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiUnidentifiedN08 refN08 | olUnidentifiedN08 {2 2} poiUnidentifiedP08 refP08 | olUnidentifiedP08 {-2 -2}", "UnidentifiedPIDChargedGap24", false)); // 51 + + // Charged-charged SC baseline: two distinct reference tracks per eta side. + corrconfigs.push_back(fGFW->GetCorrelatorConfig("refN08 {2 2} refP08 {-2 -2}", "Ref08Gap24", false)); // 52 + if (corrconfigs.size() != corrConfigIndex::kCount) { LOGF(fatal, "Correlation index table expects %zu configurations, but %zu were created", static_cast(corrConfigIndex::kCount), corrconfigs.size()); } @@ -1106,167 +1904,10 @@ struct PidFlowPtCorr { fT0AV0AMean->SetParameters(-1601.0581, 9.417652e-01); fT0AV0ASigma = new TF1("fT0AV0ASigma", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 200000); fT0AV0ASigma->SetParameters(463.4144, 6.796509e-02, -9.097136e-07, 7.971088e-12, -2.600581e-17); - - std::vector pTEffBins = {0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.4, 1.8, 2.2, 2.6, 3.0}; - hFindPtBin = new TH1D("hFindPtBin", "hFindPtBin", pTEffBins.size() - 1, pTEffBins.data()); - funcEff.resize(pTEffBins.size() - 1); - - std::vector f1p0 = cfgTrackDensityP0; - std::vector f1p1 = cfgTrackDensityP1; - for (uint ifunc = 0; ifunc < pTEffBins.size() - 1; ifunc++) { - funcEff[ifunc] = new TF1(Form("funcEff%i", ifunc), "[0]+[1]*x", 0, 3000); - funcEff[ifunc]->SetParameters(f1p0[ifunc], f1p1[ifunc]); - } - std::vector v2para = cfgTrackDensityV2P; - std::vector v3para = cfgTrackDensityV3P; - std::vector v4para = cfgTrackDensityV4P; - funcV2 = new TF1("funcV2", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 100); - funcV2->SetParameters(v2para[0], v2para[1], v2para[2], v2para[3], v2para[4]); - funcV3 = new TF1("funcV3", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 100); - funcV3->SetParameters(v3para[0], v3para[1], v3para[2], v3para[3], v3para[4]); - funcV4 = new TF1("funcV4", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 100); - funcV4->SetParameters(v4para[0], v4para[1], v4para[2], v4para[3], v4para[4]); } // pid utils function - /** - * @brief Identify whether the input track is a Pion - * - * @param track Input track object to be identified - * @return true The track is identified as Pion - * @return false The track is NOT identified as Pion - * @note The result is the logical AND of valid detector Sigma checks. - * If the track pt is out of a detector's valid range, that detector's check is skipped. - */ - template - bool isPion(TrackObject const& track) - { - if (switchsOpts.cfgClosureTest.value != 0) { - float rnd4test = fRndm->Rndm() * 100; - if (rnd4test < switchsOpts.cfgClosureTest.value) { - return true; - } - } // closure test - - bool resultPion = true; - - // Declare ITSResponse object internally to get ITS Sigma - o2::aod::ITSResponse itsResponse; - // Extract sigma values and pt from track - const float itsSigma = std::fabs(itsResponse.nSigmaITS(track)); - const float tofSigma = std::fabs(track.tofNSigmaPi()); - const float tpcSigma = std::fabs(track.tpcNSigmaPi()); - const float pt = track.pt(); - - // ITS detector check (pi-k separation pt range) - if (pt > pidPtRangeOpts.cfgPtMin4ITSPiKa.value && pt < pidPtRangeOpts.cfgPtMax4ITSPiKa.value) { - resultPion &= (itsSigma < cfgNSigma[6]); - } - // end ITS - - // TOF detector check (pi-k separation pt range) - if (pt > pidPtRangeOpts.cfgPtMin4TOFPiKa.value && pt < pidPtRangeOpts.cfgPtMax4TOFPiKa.value) { - resultPion &= (tofSigma < cfgNSigma[3]); - } - // end TOF - - // TPC detector check (pi-k separation pt range) - if (pt > pidPtRangeOpts.cfgPtMin4TPCPiKa.value && pt < pidPtRangeOpts.cfgPtMax4TPCPiKa.value) { - resultPion &= (tpcSigma < cfgNSigma[0]); - } - // end TPC - - return resultPion; - } - - /** - * @brief Identify whether the input track is a Proton - * - * @param track Input track object to be identified - * @return true The track is identified as Proton - * @return false The track is NOT identified as Proton - * @note The result is the logical AND of valid detector Sigma checks. - * If the track pt is out of a detector's valid range, that detector's check is skipped. - */ - template - bool isProton(TrackObject const& track) - { - bool resultProton = true; - - // Declare ITSResponse object internally to get ITS Sigma - o2::aod::ITSResponse itsResponse; - // Extract sigma values and pt from track - const float itsSigma = std::fabs(itsResponse.nSigmaITS(track)); - const float tofSigma = std::fabs(track.tofNSigmaPr()); - const float tpcSigma = std::fabs(track.tpcNSigmaPr()); - const float pt = track.pt(); - - // ITS detector check (k-p separation pt range) - if (pt > pidPtRangeOpts.cfgPtMin4ITSKaPr.value && pt < pidPtRangeOpts.cfgPtMax4ITSKaPr.value) { - resultProton &= (itsSigma < cfgNSigma[7]); - } - // end ITS - - // TOF detector check (k-p separation pt range) - if (pt > pidPtRangeOpts.cfgPtMin4TOFKaPr.value && pt < pidPtRangeOpts.cfgPtMax4TOFKaPr.value) { - resultProton &= (tofSigma < cfgNSigma[4]); - } - // end TOF - - // TPC detector check (k-p separation pt range) - if (pt > pidPtRangeOpts.cfgPtMin4TPCKaPr.value && pt < pidPtRangeOpts.cfgPtMax4TPCKaPr.value) { - resultProton &= (tpcSigma < cfgNSigma[1]); - } - // end TPC - - return resultProton; - } - - /** - * @brief Identify whether the input track is a Kaon (separate from Pion and Proton) - * - * @param track Input track object (aod::Track) to be identified - * @return true The track is identified as Kaon - * @return false The track is NOT identified as Kaon - * @note The result is the logical AND of valid detector Sigma checks. - * Only pt range that overlaps with both pi-k and k-p separation ranges is checked. - * If the track pt is out of the overlapping range, that detector's check is skipped. - */ - template - bool isKaon(TrackObject const& track) - { - bool resultKaon = true; - - // Declare ITSResponse object internally to get ITS Sigma - o2::aod::ITSResponse itsResponse; - // Extract sigma values and pt from track - const float itsSigma = std::fabs(itsResponse.nSigmaITS(track)); - const float tofSigma = std::fabs(track.tofNSigmaKa()); - const float tpcSigma = std::fabs(track.tpcNSigmaKa()); - const float pt = track.pt(); - - // ITS detector check (overlap of pi-k and k-p separation pt ranges) - if (pt > pidPtRangeOpts.cfgPtMin4ITSKaPr.value && pt > pidPtRangeOpts.cfgPtMin4ITSPiKa.value && pt < pidPtRangeOpts.cfgPtMax4ITSKaPr.value && pt < pidPtRangeOpts.cfgPtMax4ITSPiKa.value) { - resultKaon &= (itsSigma < cfgNSigma[8]); - } - // end ITS - - // TOF detector check (overlap of pi-k and k-p separation pt ranges) - if (pt > pidPtRangeOpts.cfgPtMin4TOFKaPr.value && pt > pidPtRangeOpts.cfgPtMin4TOFPiKa.value && pt < pidPtRangeOpts.cfgPtMax4TOFKaPr.value && pt < pidPtRangeOpts.cfgPtMax4TOFPiKa.value) { - resultKaon &= (tofSigma < cfgNSigma[5]); - } - // end TOF - - // TPC detector check (overlap of pi-k and k-p separation pt ranges) - if (pt > pidPtRangeOpts.cfgPtMin4TPCKaPr.value && pt > pidPtRangeOpts.cfgPtMin4TPCPiKa.value && pt < pidPtRangeOpts.cfgPtMax4TPCKaPr.value && pt < pidPtRangeOpts.cfgPtMax4TPCPiKa.value) { - resultKaon &= (tpcSigma < cfgNSigma[2]); - } - // end TPC - - return resultKaon; - } - template int getPidWithCircleCut(TrackObject const& track) { @@ -1293,7 +1934,7 @@ struct PidFlowPtCorr { if (pt > ptMin && pt < ptMax) { isPionBool = std::hypot(tpcNsigma, tofNsigma) < circleCutOpts.cfgCircleCutSigmaPi.value; } else { - // Fallback: TPC only cut + // TPC fallback; the low-pT ITS requirement is applied below. isPionBool = std::fabs(tpcNsigma) < circleCutOpts.cfgCircleCutTPCPi.value; } } @@ -1335,6 +1976,24 @@ struct PidFlowPtCorr { } } + // Apply ITS to every low-pT candidate before resolving ambiguities. + if (pt < circleCutOpts.cfgCircleCutITSPtMax.value) { + const uint32_t clusterSizes = track.itsClusterSizes(); + int nSizeLayers = 0; + for (int layer = 0; layer < 7; ++layer) { + nSizeLayers += ((clusterSizes >> (4 * layer)) & 0xf) != 0; + } + if (!track.hasITS() || nSizeLayers < 2 || !std::isfinite(track.p()) || track.p() <= 0.f || !std::isfinite(track.eta())) { + return -1; + } + const auto passesITS = [](float nsigma, float cut) { + return std::isfinite(nsigma) && std::fabs(nsigma) < cut; + }; + isPionBool = isPionBool && passesITS(o2::aod::ITSResponse::nSigmaITS(track), circleCutOpts.cfgCircleCutITSPi.value); + isKaonBool = isKaonBool && passesITS(o2::aod::ITSResponse::nSigmaITS(track), circleCutOpts.cfgCircleCutITSKa.value); + isProtonBool = isProtonBool && passesITS(o2::aod::ITSResponse::nSigmaITS(track), circleCutOpts.cfgCircleCutITSPr.value); + } + // ------------------------------ // Ambiguity rejection // ------------------------------ @@ -1359,7 +2018,7 @@ struct PidFlowPtCorr { } /** - * @brief Get the Pid, combine square cut && circle cut + * @brief Apply the TPC PID cluster cut, then circle PID with low-pT ITS * * @tparam TrackObject * @param track @@ -1368,28 +2027,13 @@ struct PidFlowPtCorr { template int getPidConfigurable(TrackObject const& track) { - int pid = -1; - if (circleCutOpts.cfgUseCircleCutPID.value) { - // Use circular cut (already handles ambiguity rejection) - pid = getPidWithCircleCut(track); - } else { - // Use normal cut (with manual ambiguity rejection) - bool isPi = isPion(track); - bool isKa = isKaon(track); - bool isPr = isProton(track); - int nCandidates = isPi + isKa + isPr; - if (nCandidates == 1) { - if (isPi) { - pid = MyParticleType::kPion; - } else if (isKa) { - pid = MyParticleType::kKaon; - } else if (isPr) { - pid = MyParticleType::kProton; - } - } - // else: pid remains -1 (ambiguous or none) + // PID quality changes the species assignment, not charged track selection. + const double minPIDClusters = fTPCPIDClustersMin->Eval(track.tpcNClsFound()); + if (!std::isfinite(minPIDClusters) || track.tpcNClsPID() < minPIDClusters) { + return -1; } - return pid; + + return getPidWithCircleCut(track); } bool isWithinRefPtRange(float pt) @@ -2314,6 +2958,21 @@ struct PidFlowPtCorr { case MyFunctionName::funcProcessPidPtCorrelations: registry.fill(HIST("hEventCount/processPidPtCorrelations"), position); break; + case MyFunctionName::funcProcessDataTimeAssociation: + registry.fill(HIST("hEventCount/processDataTimeAssociation"), position); + break; + case MyFunctionName::funcFillCorrectionGraphTimeAssociation: + registry.fill(HIST("hEventCount/fillCorrectionGraphTimeAssociation"), position); + break; + case MyFunctionName::funcProcessMCTimeAssociation: + registry.fill(HIST("hEventCount/processMCTimeAssociation"), position); + break; + case MyFunctionName::funcProcessTimeAssociationComparison: + registry.fill(HIST("hEventCount/processTimeAssociationComparison"), position); + break; + case MyFunctionName::funcProcessChargedDifferentialTimeAssociation: + registry.fill(HIST("hEventCount/processChargedDifferentialTimeAssociation"), position); + break; default: // LOGF(warning, "could not find event count graph"); @@ -2322,13 +2981,22 @@ struct PidFlowPtCorr { } /** - * @brief get centrality from the configured estimator - * @note mirrors flowTask.cxx's getCentrality(), allowing this task to - * switch between different Run 3 centrality estimators via - * cfgCentEstimator instead of always using FT0C. + * @brief Get the event activity used for output binning and NUE lookup. + * @note Multiplicity mode uses raw PV contributors in |eta| < 0.8. + * It requires matching multiplicity axes and NUE maps; the caller + * must disable the centrality-calibrated cfgDoMultPVCut. */ template float getCentrality(TCollision const& collision) + { + if (cfgUseMultiplicity.value) { + return collision.multNTracksPV(); + } + return getPercentileCentrality(collision); + } + + template + float getPercentileCentrality(TCollision const& collision) { float cent = -1; switch (cfgCentEstimator) { @@ -2440,7 +3108,10 @@ struct PidFlowPtCorr { return false; } - registry.fill(HIST("hNTracksPVvsCentrality"), multNTracksPV, centrality); + // The comparison has its own counter; do not double-fill physics event QA. + if (funcName != funcProcessTimeAssociationComparison) { + registry.fill(HIST("hNTracksPVvsCentrality"), multNTracksPV, centrality); + } if (evtSeleOpts.cfgDoMultPVCut.value) { if (multNTracksPV < fMultPVCutLow->Eval(centrality)) { return false; @@ -2465,7 +3136,9 @@ struct PidFlowPtCorr { } fillEventCountHelper(funcName, 11.5); - registry.fill(HIST("hInteractionRate"), interactionRate); + if (funcName != funcProcessTimeAssociationComparison) { + registry.fill(HIST("hInteractionRate"), interactionRate); + } if (evtSeleOpts.cfgCutminIR.value >= 0 && interactionRate > 0 && interactionRate < evtSeleOpts.cfgCutminIR.value) { return false; } @@ -2492,6 +3165,53 @@ struct PidFlowPtCorr { // main functions + void loadChargedTimeCorrections(uint64_t timestamp, bool acceptance = true) + { + // Preserve the existing one/four-path convention, but load only charged. + constexpr std::size_t FullCorrectionSpecies = 4; + if (!timeEfficiencyLoaded) { + auto const& paths = correctionPathOpts.cfgEfficiencyPath.value; + if (paths.size() == 1 || paths.size() == FullCorrectionSpecies) { + auto* efficiency = ccdb->getForTimeStamp(paths.front(), timestamp); + if (!efficiency) { + LOGF(fatal, "Missing charged time-association efficiency: %s", paths.front().c_str()); + } + mEfficiency.push_back(efficiency); + } + timeEfficiencyLoaded = true; + } + if (acceptance && !timeAcceptanceLoaded) { + if (switchsOpts.cfgUseNUAWithPt) { + if (cfgAcceptanceWithPt.size() == 1 || cfgAcceptanceWithPt.size() == FullCorrectionSpecies) { + auto* weights = ccdb->getForTimeStamp(cfgAcceptanceWithPt.front(), timestamp); + if (!weights) { + LOGF(fatal, "Missing charged time-association NUA: %s", cfgAcceptanceWithPt.front().c_str()); + } + mAcceptanceWithPt.push_back(weights); + } + } else if (cfgAcceptance.size() == 1 || cfgAcceptance.size() == FullCorrectionSpecies) { + auto* weights = ccdb->getForTimeStamp(cfgAcceptance.front(), timestamp); + if (!weights) { + LOGF(fatal, "Missing charged time-association NUA: %s", cfgAcceptance.front().c_str()); + } + mAcceptance.push_back(weights); + } + timeAcceptanceLoaded = true; + } + } + + template + bool chargedTimeEventSelected(TCollision const& collision, MyFunctionName name) + { + fillEventCountHelper(name, 0.5); + if (!collision.sel8()) { + return false; + } + fillEventCountHelper(name, 1.5); + auto bc = collision.template bc_as(); + return eventSelected(collision, getCentrality(collision), getInteractionRate(bc.timestamp(), bc.runNumber()), name); + } + void processData(AodCollisions::iterator const& collision, aod::BCsWithTimestamps const&, AodTracks const& tracks) { // sub region, init @@ -2535,33 +3255,6 @@ struct PidFlowPtCorr { registry.fill(HIST("hCent"), cent); // end correction - double psi2Est = 0, psi3Est = 0, psi4Est = 0; - float wEPeff = 1; - double v2 = 0, v3 = 0, v4 = 0; - if (switchsOpts.cfgDoLocDenCorr.value) { - double q2x = 0, q2y = 0; - double q3x = 0, q3y = 0; - double q4x = 0, q4y = 0; - for (const auto& track : tracks) { - // pt cut - bool withinPtRef = isWithinRefPtRange(track.pt()); - if (withinPtRef) { - q2x += std::cos(2 * track.phi()); - q2y += std::sin(2 * track.phi()); - q3x += std::cos(3 * track.phi()); - q3y += std::sin(3 * track.phi()); - q4x += std::cos(4 * track.phi()); - q4y += std::sin(4 * track.phi()); - } - } - psi2Est = std::atan2(q2y, q2x) / 2.; - psi3Est = std::atan2(q3y, q3x) / 3.; - psi4Est = std::atan2(q4y, q4x) / 4.; - v2 = funcV2->Eval(cent); - v3 = funcV3->Eval(cent); - v4 = funcV4->Eval(cent); - } // cfgDoLocDenCorr - double ptSum = 0; double ptSumw2 = 0; double nch = 0; @@ -2688,22 +3381,6 @@ struct PidFlowPtCorr { setParticleNUEWeight(weff, track, cent); // end do NUE && NUA - if (switchsOpts.cfgDoLocDenCorr.value) { - bool withinPtRef = isWithinRefPtRange(track.pt()); - if (withinPtRef) { - double fphi = v2 * std::cos(2 * (track.phi() - psi2Est)) + v3 * std::cos(3 * (track.phi() - psi3Est)) + v4 * std::cos(4 * (track.phi() - psi4Est)); - fphi = (1 + 2 * fphi); - int pTBinForEff = hFindPtBin->FindBin(track.pt()); - if (pTBinForEff >= 1 && pTBinForEff <= hFindPtBin->GetNbinsX()) { - wEPeff = funcEff[pTBinForEff - 1]->Eval(fphi * tracks.size()); - if (wEPeff > 0.) { - wEPeff = 1. / wEPeff; - weff *= wEPeff; - } - } - } - } // cfgDoLocDenCorr - // The reference and POI pT ranges are applied independently below. if (!trackSelectedForFlow(track)) { continue; @@ -2755,7 +3432,7 @@ struct PidFlowPtCorr { } if (pid == -1 && withinPtRefGlobal) { // Unidentified tracks have no PID-specific correction. Reuse exactly - // the charged NUA/NUE (including the optional local-density factor). + // the charged NUA/NUE weights. fGFW->Fill(track.eta(), 0, track.phi(), wacc * weff, gfwBitMask::unidentified::kPOI); fGFW->Fill(track.eta(), 0, track.phi(), wacc * weff, gfwBitMask::unidentified::kOverlap, wacc * weff); } else if (isWithinPOIPtRange(pid, track.pt())) { @@ -2767,7 +3444,10 @@ struct PidFlowPtCorr { // Fill GFW and counters using the same species-specific POI pT range. if (pid == MyParticleType::kPion) { fGFW->Fill(track.eta(), 0, track.phi(), waccPid * weffPid, gfwBitMask::pion::kPOI); - fGFW->Fill(track.eta(), 0, track.phi(), waccPid * weffPid, gfwBitMask::pion::kOverlap, wacc * weff); + // Only tracks in both pT selections contribute to POI-ref overlap. + if (withinPtRefGlobal) { + fGFW->Fill(track.eta(), 0, track.phi(), waccPid * weffPid, gfwBitMask::pion::kOverlap, wacc * weff); + } registry.fill(HIST("hPhiPi"), track.phi()); registry.fill(HIST("hPhicorrPi"), track.phi(), waccPid); registry.fill(HIST("hPhiCorrNUANUEPi"), track.phi(), waccPid * weffPid); @@ -2776,7 +3456,9 @@ struct PidFlowPtCorr { numOfPi++; } else if (pid == MyParticleType::kKaon) { fGFW->Fill(track.eta(), 0, track.phi(), waccPid * weffPid, gfwBitMask::kaon::kPOI); - fGFW->Fill(track.eta(), 0, track.phi(), waccPid * weffPid, gfwBitMask::kaon::kOverlap, wacc * weff); + if (withinPtRefGlobal) { + fGFW->Fill(track.eta(), 0, track.phi(), waccPid * weffPid, gfwBitMask::kaon::kOverlap, wacc * weff); + } registry.fill(HIST("hPhiKa"), track.phi()); registry.fill(HIST("hPhicorrKa"), track.phi(), waccPid); registry.fill(HIST("hPhiCorrNUANUEKa"), track.phi(), waccPid * weffPid); @@ -2785,7 +3467,9 @@ struct PidFlowPtCorr { numOfKa++; } else if (pid == MyParticleType::kProton) { fGFW->Fill(track.eta(), 0, track.phi(), waccPid * weffPid, gfwBitMask::proton::kPOI); - fGFW->Fill(track.eta(), 0, track.phi(), waccPid * weffPid, gfwBitMask::proton::kOverlap, wacc * weff); + if (withinPtRefGlobal) { + fGFW->Fill(track.eta(), 0, track.phi(), waccPid * weffPid, gfwBitMask::proton::kOverlap, wacc * weff); + } registry.fill(HIST("hPhiPr"), track.phi()); registry.fill(HIST("hPhicorrPr"), track.phi(), waccPid); registry.fill(HIST("hPhiCorrNUANUEPr"), track.phi(), waccPid * weffPid); @@ -2813,6 +3497,8 @@ struct PidFlowPtCorr { if (nch > 0) { fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Gap), cent, rndm, "c22"); fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC24), cent, rndm, "c24"); + // Raw gap <4> for SC(ch,ch); reuse c22 for both two-particle factors. + fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC24Gap), cent, rndm, "c24Gap"); fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Full), cent, rndm, "c22Full"); fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC32Gap), cent, rndm, "c32"); fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC34Gap), cent, rndm, "c34"); @@ -2854,6 +3540,14 @@ struct PidFlowPtCorr { bool filledPr = fillFC(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC22Pure), cent, rndm, "c22pure"); bool filledUnidentified = fillFC(MyParticleType::kUnidentified, corrconfigs.at(corrConfigIndex::unidentified::kC22Pure), cent, rndm, "c22pure"); + // SC/NSC components only: the mixed raw <4> uses its GFW tuple weight + // and the same bootstrap draw as the existing c22pure and charged c22. + // Existing <2> profiles retain their own valid-event samples/weights. + fillFC(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC24PIDChargedGap), cent, rndm, "c24PIDChargedGap"); + fillFC(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC24PIDChargedGap), cent, rndm, "c24PIDChargedGap"); + fillFC(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC24PIDChargedGap), cent, rndm, "c24PIDChargedGap"); + fillFC(MyParticleType::kUnidentified, corrconfigs.at(corrConfigIndex::unidentified::kC24PIDChargedGap), cent, rndm, "c24PIDChargedGap"); + fillFC(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC32Pure), cent, rndm, "c32pure"); fillFC(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC32Pure), cent, rndm, "c32pure"); fillFC(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC32Pure), cent, rndm, "c32pure"); @@ -3077,6 +3771,418 @@ struct PidFlowPtCorr { } PROCESS_SWITCH(PidFlowPtCorr, processData, "", true); + void initTimeAssociationComparisonQA() + { + constexpr const char* selection = "Common selected events, final charged selection, unit weights"; + registry.add("associationComparison/hEventAvailability", "Common events including empty track sets;Centrality (%);Track availability", {HistType::kTH2D, {axisMultiplicity, {4, -0.5, 3.5}}}); + registry.add("associationComparison/hNchOriginalVsTime", "Final charged multiplicity;N_{ch} original;N_{ch} time;Event count", {HistType::kTH2D, {cfgaxisAssociationNch, cfgaxisAssociationNch}}); + registry.add("associationComparison/hLostVsGained", "Track membership changes;Lost tracks;Gained tracks;Event count", {HistType::kTH2D, {cfgaxisAssociationChangedNch, cfgaxisAssociationChangedNch}}); + registry.add("associationComparison/hDeltaNchVsCent", "Multiplicity change;Centrality (%);N_{ch}^{time}-N_{ch}^{original};Event count", {HistType::kTH2D, {axisMultiplicity, cfgaxisAssociationDeltaNch}}); + registry.add("associationComparison/hDeltaNchVsOccupancy", "Multiplicity change;Track occupancy;N_{ch}^{time}-N_{ch}^{original};Event count", {HistType::kTH2D, {cfgaxisAssociationOccupancy, cfgaxisAssociationDeltaNch}}); + registry.add("associationComparison/hSetChangeVsCent", "Nonempty union only;Centrality (%);(lost+gained)/(common+lost+gained);Event count", {HistType::kTH2D, {axisMultiplicity, {101, -0.005, 1.005}}}); + registry.add("associationComparison/hSetChangeVsOccupancy", "Nonempty union only;Track occupancy;(lost+gained)/(common+lost+gained);Event count", {HistType::kTH2D, {cfgaxisAssociationOccupancy, {101, -0.005, 1.005}}}); + registry.add("associationComparison/pSetChangeVsCent", "Event mean, nonempty union only;Centrality (%);Mean set-change fraction", {HistType::kTProfile, {axisMultiplicity}}); + registry.add("associationComparison/pSetChangeVsOccupancy", "Event mean, nonempty union only;Track occupancy;Mean set-change fraction", {HistType::kTProfile, {cfgaxisAssociationOccupancy}}); + registry.add("associationComparison/pLostFractionVsCent", "Event mean, original Nch > 0;Centrality (%);Mean lost/original fraction", {HistType::kTProfile, {axisMultiplicity}}); + registry.add("associationComparison/pLostFractionVsOccupancy", "Event mean, original Nch > 0;Track occupancy;Mean lost/original fraction", {HistType::kTProfile, {cfgaxisAssociationOccupancy}}); + registry.add("associationComparison/pGainedFractionVsCent", "Event mean, time Nch > 0;Centrality (%);Mean gained/time fraction", {HistType::kTProfile, {axisMultiplicity}}); + registry.add("associationComparison/pGainedFractionVsOccupancy", "Event mean, time Nch > 0;Track occupancy;Mean gained/time fraction", {HistType::kTProfile, {cfgaxisAssociationOccupancy}}); + // Category projections provide original/time denominators for lost/gained + // spectra ratios. Entries count track-target pairs, not distinct sources. + registry.add("associationComparison/hPtByCategory", "Final tracks per target collision;p_{T} (GeV/c);Track category;Track-target count", {HistType::kTH2D, {cfgaxisPt, {5, -0.5, 4.5}}}); + registry.add("associationComparison/hEtaByCategory", "Final tracks per target collision;#eta;Track category;Track-target count", {HistType::kTH2D, {cfgaxisEta, {5, -0.5, 4.5}}}); + registry.add("associationComparison/hPhiByCategory", "Final tracks per target collision;#varphi;Track category;Track-target count", {HistType::kTH2D, {cfgaxisPhi, {5, -0.5, 4.5}}}); + registry.add("associationComparison/hFinalCollisionsOriginal", "All source tracks once per dataframe;N selected collisions (original);Source track count", {HistType::kTH1D, {{2, -0.5, 1.5}}}); + registry.add("associationComparison/hFinalCollisionsTime", "All source tracks once per dataframe;N selected collisions (time);Source track count", {HistType::kTH1D, {{101, -0.5, 100.5}}}); + registry.add("associationComparison/hFinalCollisionsOriginalVsTime", "All source tracks once per dataframe;N selected collisions (original);N selected collisions (time);Source track count", {HistType::kTH2D, {{2, -0.5, 1.5}, {101, -0.5, 100.5}}}); + const auto setLabels = [](TAxis* axis, auto const& labels) { + for (std::size_t index = 0; index < labels.size(); ++index) { + axis->SetBinLabel(index + 1, labels[index]); + } + }; + setLabels(registry.get(HIST("associationComparison/hEventAvailability"))->GetYaxis(), + std::array{"both empty", "original only", "time only", "both nonempty"}); + const std::array categories{"original", "time", "common", "lost", "gained"}; + setLabels(registry.get(HIST("associationComparison/hPtByCategory"))->GetYaxis(), categories); + setLabels(registry.get(HIST("associationComparison/hEtaByCategory"))->GetYaxis(), categories); + setLabels(registry.get(HIST("associationComparison/hPhiByCategory"))->GetYaxis(), categories); + LOG(info) << "Association comparison: " << selection << "; original=collisionId, time=PidFlowTimeTracks; PV=locked-original, nonPV=closest-time"; + } + + // Evaluate both associations in the same event loop without PID or correction + // weights. Keep the full source table to include zero-use tracks in usage QA. + void processTimeAssociationComparison(AodCollisions const& collisions, aod::BCsWithTimestamps const&, + PidFlowChargedTimeProducer::SourceTracks const& sources, aod::PidFlowTimeTracks const& associatedTracks) + { + if (!cfgTimeAssociationQA) { + return; + } + std::vector originalUses(sources.size(), 0), timeUses(sources.size(), 0); + const auto sourceIndex = [&](int64_t trackId) { + const int64_t index = trackId - sources.offset(); + if (index < 0 || index >= sources.size()) { + LOGF(fatal, "Association comparison track index is outside the full source track table"); + } + return index; + }; + const auto fillKinematics = [&](auto const& track, int category) { + registry.fill(HIST("associationComparison/hPtByCategory"), track.pt(), category); + registry.fill(HIST("associationComparison/hEtaByCategory"), track.eta(), category); + registry.fill(HIST("associationComparison/hPhiByCategory"), track.phi(), category); + }; + for (auto const& collision : collisions) { + // No track-presence requirement: both methods share this event sample. + if (!chargedTimeEventSelected(collision, funcProcessTimeAssociationComparison)) { + continue; + } + auto originalTracks = sources.sliceBy(perCollision, collision.globalIndex()); + auto timeTracks = associatedTracks.sliceBy(timeTracksPerCollision, collision.globalIndex()); + std::unordered_set originalIds, timeIds; + for (auto const& track : originalTracks) { + // Reproduce processData's framework filter and final charged cuts, + // without requiring or evaluating any PID tables. + if (!(track.isGlobalTrack() || track.isGlobalTrackSDD() != 0) || !(std::fabs(track.eta()) < trkQualityOpts.cfgCutEta.value) || + !trackSelectedForFlow(track) || !isWithinRefPtRange(track.pt())) { + continue; + } + if (originalIds.insert(track.globalIndex()).second) { + ++originalUses[sourceIndex(track.globalIndex())]; + fillKinematics(track, 0); + } + } + unsigned int common = 0; + for (auto const& track : timeTracks) { + if (!trackSelectedForFlow(track) || !isWithinRefPtRange(track.pt()) || !timeIds.insert(track.trackId()).second) { + continue; + } + ++timeUses[sourceIndex(track.trackId())]; + fillKinematics(track, 1); + if (originalIds.contains(track.trackId())) { + ++common; + fillKinematics(track, 2); + } else { + fillKinematics(track, 4); + } + } + for (auto const& track : originalTracks) { + if (originalIds.contains(track.globalIndex()) && !timeIds.contains(track.globalIndex())) { + fillKinematics(track, 3); + } + } + const auto nOriginal = originalIds.size(); + const auto nTime = timeIds.size(); + const auto lost = nOriginal - common; + const auto gained = nTime - common; + const auto unionSize = common + lost + gained; + const double cent = getCentrality(collision); + const auto occupancy = collision.trackOccupancyInTimeRange(); + const double deltaNch = static_cast(nTime) - static_cast(nOriginal); + const int availability = (nOriginal > 0 ? 1 : 0) + (nTime > 0 ? 2 : 0); + registry.fill(HIST("associationComparison/hEventAvailability"), cent, availability); + registry.fill(HIST("associationComparison/hNchOriginalVsTime"), nOriginal, nTime); + registry.fill(HIST("associationComparison/hLostVsGained"), lost, gained); + registry.fill(HIST("associationComparison/hDeltaNchVsCent"), cent, deltaNch); + registry.fill(HIST("associationComparison/hDeltaNchVsOccupancy"), occupancy, deltaNch); + if (unionSize > 0) { + const double changeFraction = static_cast(lost + gained) / unionSize; + registry.fill(HIST("associationComparison/hSetChangeVsCent"), cent, changeFraction); + registry.fill(HIST("associationComparison/hSetChangeVsOccupancy"), occupancy, changeFraction); + registry.fill(HIST("associationComparison/pSetChangeVsCent"), cent, changeFraction); + registry.fill(HIST("associationComparison/pSetChangeVsOccupancy"), occupancy, changeFraction); + } + if (nOriginal > 0) { + const double lostFraction = static_cast(lost) / nOriginal; + registry.fill(HIST("associationComparison/pLostFractionVsCent"), cent, lostFraction); + registry.fill(HIST("associationComparison/pLostFractionVsOccupancy"), occupancy, lostFraction); + } + if (nTime > 0) { + const double gainedFraction = static_cast(gained) / nTime; + registry.fill(HIST("associationComparison/pGainedFractionVsCent"), cent, gainedFraction); + registry.fill(HIST("associationComparison/pGainedFractionVsOccupancy"), occupancy, gainedFraction); + } + } + for (std::size_t index = 0; index < originalUses.size(); ++index) { + registry.fill(HIST("associationComparison/hFinalCollisionsOriginal"), originalUses[index]); + registry.fill(HIST("associationComparison/hFinalCollisionsTime"), timeUses[index]); + registry.fill(HIST("associationComparison/hFinalCollisionsOriginalVsTime"), originalUses[index], timeUses[index]); + } + } + PROCESS_SWITCH(PidFlowPtCorr, processTimeAssociationComparison, "Compare original collisionId and time-associated final charged tracks; no PID/NUA/NUE", false); + + // Independent charged-only counterpart of processData. All moment/profile + // definitions below retain the original charged weights and bootstrap draw. + void processDataTimeAssociation(AodCollisions::iterator const& collision, aod::BCsWithTimestamps const&, aod::PidFlowTimeTracks const& tracks) + { + if (!chargedTimeEventSelected(collision, funcProcessDataTimeAssociation)) { + return; + } + if (tracks.size() == 0) { + return; + } + fillEventCountHelper(funcProcessDataTimeAssociation, 14.5); + const double cent = getCentrality(collision); + const float vtxz = collision.posZ(); + const float rndm = fRndm->Rndm(); + auto bc = collision.bc_as(); + loadChargedTimeCorrections(bc.timestamp()); + fGFW->Clear(); + registry.fill(HIST("hVtxZ"), vtxz); + registry.fill(HIST("hMult"), tracks.size()); + registry.fill(HIST("hMultTPC"), collision.multTPC()); + registry.fill(HIST("hCent"), cent); + if (switchsOpts.cfgOutPutPtSpectra) { + registry.fill(HIST("ptSpectra/hCentEventCountData"), cent); + } + + double ptSum = 0., ptSumw2 = 0., nch = 0., nchSquare = 0., ptSquareSum = 0.; + double nchPassedSelection = 0., nchCorrectedPassed = 0.; + for (auto const& track : tracks) { + if (!trackSelectedForFlow(track) || !isWithinRefPtRange(track.pt())) { + continue; + } + float weff = 1.f; + setParticleNUEWeight(weff, track, cent); + nchPassedSelection += 1.; + nchCorrectedPassed += weff; + if (std::fabs(track.eta()) < trkQualityOpts.cfgRangeEta.value) { + nch += weff; + nchSquare += weff * weff; + ptSum += weff * track.pt(); + ptSumw2 += weff * weff * track.pt(); + ptSquareSum += weff * weff * track.pt() * track.pt(); + } + } + for (auto const& track : tracks) { + if (!trackSelectedForFlow(track) || !isWithinRefPtRange(track.pt())) { + continue; + } + float weff = 1.f, wacc = 1.f; + setParticleNUEWeight(weff, track, cent); + setParticleNUAWeight(wacc, track, vtxz); + registry.fill(HIST("hPhi"), track.phi()); + registry.fill(HIST("hPhicorr"), track.phi(), wacc); + registry.fill(HIST("hPhiCorrNUANUE"), track.phi(), wacc * weff); + registry.fill(HIST("hEta"), track.eta()); + registry.fill(HIST("hPt"), track.pt()); + registry.fill(HIST("hPtCorr"), track.pt(), weff); + if (switchsOpts.cfgOutPutPtSpectra) { + registry.fill(HIST("ptSpectra/hPtCentData"), track.pt(), cent); + } + fGFW->Fill(track.eta(), 0, track.phi(), wacc * weff, gfwBitMask::charged::kReference); + } + registry.fill(HIST("hNchUnCorrectedVSNchCorrected"), nchPassedSelection, nchCorrectedPassed); + if (nch <= 0.) { + return; + } + fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Gap), cent, rndm, "c22"); + fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC24), cent, rndm, "c24"); + fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC24Gap), cent, rndm, "c24Gap"); + fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Full), cent, rndm, "c22Full"); + fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC32Gap), cent, rndm, "c32"); + fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC34Gap), cent, rndm, "c34"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Gap), cent, rndm, nch, nch, "c22TrackWeight"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Gap), cent, rndm, nch, nch, "c22TrackWeightOne", true); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC24), cent, rndm, nch, nch, "c24TrackWeight"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Full), cent, rndm, nch, nch, "c22FullTrackWeight"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC32Gap), cent, rndm, nch, nch, "c32TrackWeight"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC34Gap), cent, rndm, nch, nch, "c34TrackWeight"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Gap), cent, rndm, ptSum, nch, "covV2Pt"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Gap), cent, rndm, ptSum, nch, "covV2PtWeightOne", true); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC32Gap), cent, rndm, ptSum, nch, "covV3Pt"); + fillFC4PtC22(cent, ptSum, nch, rndm); + fFCCh->FillProfile("hMeanPt", cent, ptSum / nch, nch, rndm); + fFCCh->FillProfile("hMeanPtWeightOne", cent, ptSum / nch, 1., rndm); + const double nchDiff = nch * nch - nchSquare; + if (nchDiff > minVal4Float) { + fFCCh->FillProfile("ptSquareAve", cent, (ptSum * ptSum - ptSquareSum) / nchDiff, nchDiff, rndm); + fFCCh->FillProfile("ptSquareAveWeightOne", cent, (ptSum * ptSum - ptSquareSum) / nchDiff, 1., rndm); + fFCCh->FillProfile("ptAve", cent, (nch * ptSum - ptSumw2) / nchDiff, nchDiff, rndm); + fFCCh->FillProfile("ptAveWeightOne", cent, (nch * ptSum - ptSumw2) / nchDiff, 1., rndm); + } + if (switchsOpts.cfgAddC22DeltaPtHistograms) { + registry.fill(HIST("c22DeltaPt/hMeanPtCharged"), cent, rndm * cfgFlowNbootstrap, ptSum / nch, nch); + fillProfilePOIvnpt(corrconfigs.at(corrConfigIndex::charged::kC22Gap), HIST("c22DeltaPt/c22dmeanptCharged"), cent, ptSum, nch, rndm); + } + } + PROCESS_SWITCH(PidFlowPtCorr, processDataTimeAssociation, "Charged flow and mean-pT using time-based TrackAssoc; no PID", false); + + // Store raw ensemble correlators, not event-by-event normalized v2. + // FlowContainer: SetIDName("ChGap"), then GetVN2VsPt(2, centMin, centMax). + void processChargedDifferentialTimeAssociation(AodCollisions::iterator const& collision, aod::BCsWithTimestamps const&, aod::PidFlowTimeTracks const& tracks) + { + if (!chargedTimeEventSelected(collision, funcProcessChargedDifferentialTimeAssociation) || tracks.size() == 0) { + return; + } + fillEventCountHelper(funcProcessChargedDifferentialTimeAssociation, 14.5); + const double cent = getCentrality(collision); + const double rndm = fRndm->Rndm(); + auto bc = collision.bc_as(); + loadChargedTimeCorrections(bc.timestamp()); + chargedDifferential.gfw->Clear(); + const int ptBins = chargedDifferential.ptAxis->GetNbins(); + for (auto const& track : tracks) { + if (!trackSelectedForFlow(track)) { + continue; + } + const double pt = track.pt(); + const bool isReference = pt > chargedDifferentialOpts.cfgRefPtMin.value && pt < chargedDifferentialOpts.cfgRefPtMax.value; + const int ptBin = chargedDifferential.ptAxis->FindFixBin(pt) - 1; + const bool isPOI = ptBin >= 0 && ptBin < ptBins; + if (!isReference && !isPOI) { + continue; + } + float weff = 1.f, wacc = 1.f; + setParticleNUEWeight(weff, track, cent); + setParticleNUAWeight(wacc, track, collision.posZ()); + const double weight = static_cast(weff) * wacc; + if (isReference) { + chargedDifferential.gfw->Fill(track.eta(), 0, track.phi(), weight, kDifferentialReference); + } + if (isPOI) { + chargedDifferential.gfw->Fill(track.eta(), ptBin, track.phi(), weight, kDifferentialPOI); + } + } + for (auto const& config : chargedDifferential.configs) { + const int bins = config.pTDif ? ptBins : 1; + for (int bin = 0; bin < bins; ++bin) { + const double pairs = chargedDifferential.gfw->Calculate(config, bin, true).real(); + if (!std::isfinite(pairs) || pairs <= 0.) { + continue; + } + const double correlator = chargedDifferential.gfw->Calculate(config, bin, false).real() / pairs; + if (std::isfinite(correlator)) { + const std::string name = config.pTDif ? config.Head + "_pt_" + std::to_string(bin + 1) : config.Head; + fFCChDifferential->FillProfile(name.c_str(), cent, correlator, pairs, rndm); + } + } + } + } + PROCESS_SWITCH(PidFlowPtCorr, processChargedDifferentialTimeAssociation, "Charged differential v2{2}(pT) using time-based TrackAssoc; independent reference pT and POI bins, no PID", false); + + void fillCorrectionGraphTimeAssociation(AodCollisions::iterator const& collision, aod::BCsWithTimestamps const&, aod::PidFlowTimeTracks const& tracks) + { + if (!chargedTimeEventSelected(collision, funcFillCorrectionGraphTimeAssociation)) { + return; + } + if (tracks.size() == 0) { + return; + } + fillEventCountHelper(funcFillCorrectionGraphTimeAssociation, 14.5); + auto bc = collision.bc_as(); + auto run = std::find(runNumbers.begin(), runNumbers.end(), bc.runNumber()); + if (run == runNumbers.end()) { + return; + } + const int runIndex = std::distance(runNumbers.begin(), run); + const double cent = getCentrality(collision); + if (switchsOpts.cfgUseNUAWithPt) { + loadChargedTimeCorrections(bc.timestamp(), false); + } + for (auto const& track : tracks) { + if (!trackSelectedForFlow(track) || !isWithinRefPtRange(track.pt())) { + continue; + } + float weff = 1.f; + if (switchsOpts.cfgUseNUAWithPt) { + setParticleNUEWeight(weff, track, cent); + } + registry.fill(HIST("correction/hRunNumberPhiEtaVertex"), runIndex, track.phi(), track.eta(), collision.posZ(), track.pt(), weff); + } + } + PROCESS_SWITCH(PidFlowPtCorr, fillCorrectionGraphTimeAssociation, "Charged NUA source using time-based TrackAssoc; no PID", false); + + // Fill reco numerator and generated denominator together, so they use the + // very same accepted target event. A different true collision is contamination, + // not successful reconstruction of a particle in this denominator. + void processMCTimeAssociation(ChargedTimeCollisionsMC const& collisions, aod::BCsWithTimestamps const&, aod::PidFlowTimeTracks const& associatedTracks, + aod::McCollisions const& mcCollisions, aod::McParticles const& particles, aod::McTrackLabels const& labels) + { + std::vector recoPerMC(mcCollisions.size(), 0); + for (auto const& collision : collisions) { + if (collision.has_mcCollision()) { + ++recoPerMC[collision.mcCollisionId() - mcCollisions.offset()]; + } + } + for (auto const& collision : collisions) { + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/mcEventStatus"), 0.5); + } + if (!collision.has_mcCollision()) { + continue; + } + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/mcEventStatus"), 1.5); + } + const int64_t mcIndex = collision.mcCollisionId() - mcCollisions.offset(); + auto mcCollision = mcCollisions.rawIteratorAt(mcIndex); + if (recoPerMC[mcIndex] != 1 || std::fabs(mcCollision.posZ()) >= cfgCutVertex.value) { + continue; + } + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/mcEventStatus"), 2.5); + } + if (!chargedTimeEventSelected(collision, funcProcessMCTimeAssociation)) { + continue; + } + auto tracks = associatedTracks.sliceBy(timeTracksPerCollision, collision.globalIndex()); + if (tracks.size() == 0) { + continue; + } + fillEventCountHelper(funcProcessMCTimeAssociation, 14.5); + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/mcEventStatus"), 3.5); + } + const double cent = getCentrality(collision); + if (switchsOpts.cfgOutPutPtSpectra) { + registry.fill(HIST("ptSpectra/hCentEventCountMcRec"), cent); + registry.fill(HIST("ptSpectra/hCentEventCountMcGen"), cent); + } + for (auto const& particle : particles.sliceBy(particlesPerMCCollision, collision.mcCollisionId())) { + if (particle.isPhysicalPrimary() && particleSelected(particle)) { + registry.fill(HIST("correction/hPtCentMcGen"), particle.pt(), cent); + } + } + for (auto const& track : tracks) { + if (!trackSelectedForFlow(track) || !isWithinRefPtRange(track.pt())) { + continue; + } + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/hPtCentMcSelected"), track.pt(), cent); + } + const int64_t labelIndex = track.trackId() - labels.offset(); + if (labelIndex < 0 || labelIndex >= labels.size()) { + LOGF(fatal, "Time-associated source track index is outside McTrackLabels"); + } + auto source = labels.rawIteratorAt(labelIndex); + if (!source.has_mcParticle()) { + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/mcTrackStatus"), 0.5); + } + continue; + } + auto particle = source.mcParticle_as(); + if (particle.mcCollisionId() != collision.mcCollisionId()) { + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/mcTrackStatus"), 1.5); + registry.fill(HIST("timeAssociation/hPtCentMcOtherCollision"), track.pt(), cent); + } + continue; + } + if (!particle.isPhysicalPrimary() || !particleSelected(particle)) { + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/mcTrackStatus"), 2.5); + } + continue; + } + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/mcTrackStatus"), 3.5); + } + registry.fill(HIST("correction/hPtCentMcRec"), track.pt(), cent); + } + } + } + PROCESS_SWITCH(PidFlowPtCorr, processMCTimeAssociation, "Charged NUE reco/gen with common time-association event sample; no detector PID", false); + /** * @brief Fill charged, identified-particle, and unidentified-particle c22 versus event mean pT. * @note Each profile uses the mean pT of the same particle sample on its pT axis: @@ -3114,42 +4220,8 @@ struct PidFlowPtCorr { loadCorrections(bc.timestamp()); - /// @note local density corr const float vtxz = collision.posZ(); - double psi2Est = 0.; - double psi3Est = 0.; - double psi4Est = 0.; - double v2 = 0.; - double v3 = 0.; - double v4 = 0.; - if (switchsOpts.cfgDoLocDenCorr.value) { - double q2x = 0.; - double q2y = 0.; - double q3x = 0.; - double q3y = 0.; - double q4x = 0.; - double q4y = 0.; - for (const auto& track : tracks) { - if (!isWithinRefPtRange(track.pt())) { - continue; - } - q2x += std::cos(2. * track.phi()); - q2y += std::sin(2. * track.phi()); - q3x += std::cos(3. * track.phi()); - q3y += std::sin(3. * track.phi()); - q4x += std::cos(4. * track.phi()); - q4y += std::sin(4. * track.phi()); - } - psi2Est = std::atan2(q2y, q2x) / 2.; - psi3Est = std::atan2(q3y, q3x) / 3.; - psi4Est = std::atan2(q4y, q4x) / 4.; - v2 = funcV2->Eval(cent); - v3 = funcV3->Eval(cent); - v4 = funcV4->Eval(cent); - } - // end local density corr - // Event-wise mean-pT numerators and NUE-corrected particle counts. Charged // particles use the reference pT range; each PID species uses its own POI pT range. double ptSum = 0.; @@ -3218,18 +4290,6 @@ struct PidFlowPtCorr { const bool withinPtRef = isWithinRefPtRange(track.pt()); if (withinPtRef) { - if (switchsOpts.cfgDoLocDenCorr.value) { - const double fphi = 1. + 2. * (v2 * std::cos(2. * (track.phi() - psi2Est)) + - v3 * std::cos(3. * (track.phi() - psi3Est)) + - v4 * std::cos(4. * (track.phi() - psi4Est))); - const int ptBinForEff = hFindPtBin->FindBin(track.pt()); - if (ptBinForEff >= 1 && ptBinForEff <= hFindPtBin->GetNbinsX()) { - const double localDensityEff = funcEff[ptBinForEff - 1]->Eval(fphi * tracks.size()); - if (localDensityEff > 0.) { - weff /= localDensityEff; - } - } - } /// @note passed selection: global cut + pt cut fGFW->Fill(track.eta(), 0, track.phi(), wacc * weff, gfwBitMask::charged::kReference); } @@ -3442,7 +4502,7 @@ struct PidFlowPtCorr { /** * @brief Run the flow calculation on generated MC particles for a closure test. - * @note Reconstructed collisions are only used to obtain the centrality. The selection is + * @note Reconstructed collisions only provide centrality or raw PV multiplicity. The selection is * deliberately truth-level: MC vertex filter, physical-primary particles, kinematics, * and PDG PID. No data event, track-quality, detector-PID, NUA, or NUE cuts are applied. */ @@ -3922,7 +4982,7 @@ struct PidFlowPtCorr { PROCESS_SWITCH(PidFlowPtCorr, processQA, "", true); /** - * @brief Minimal PID-performance QA for TPC dE/dx and TOF time-of-flight. + * @brief PID-performance QA for TPC, TOF, PID clusters and ITS response. * @note open switch outputQA if use it * * @param collision @@ -3941,25 +5001,6 @@ struct PidFlowPtCorr { double interactionRate = getInteractionRate(bc.timestamp(), runNumber); // end init - // loop the vector, find the place to put - int matchedPosition = -1; - for (uint64_t idxPosition = 0; idxPosition < runNumbers.size(); idxPosition++) { - if (runNumbers[idxPosition] == runNumber) { - matchedPosition = idxPosition; - break; - } - } - if (matchedPosition == -1) { - return; - } - // end find place to put run data - - // fill some QA graph before collision cut - for (const auto& track : tracks) { - qaHistVector[matchedPosition].hPhiRunByRunBefore->Fill(track.phi()); - } - // end fill some QA graph before collision cut - // collision cut registry.fill(HIST("hEventCount/detectorPidQA"), 0.5); if (nTot < 1) { @@ -3977,42 +5018,46 @@ struct PidFlowPtCorr { // loadCorrections(bc.timestamp()); // end cut and correction - // fill some QA graph after collision cut - for (const auto& track : tracks) { - qaHistVector[matchedPosition].hPhiRunByRunAfter->Fill(track.phi()); - qaHistVector[matchedPosition].hITSnclsVsPhi->Fill(track.phi(), track.itsNCls()); - qaHistVector[matchedPosition].hITSChi2VsPhi->Fill(track.phi(), track.itsChi2NCl()); - } - // end fill some QA graph after collision cut - // start filling graphs for (const auto& track : tracks) { - // Use the same track-quality, PID and species pT selection as processData. + // Use the same track-quality selection as processData, but do not + // require a PID hypothesis for the all-particle QA histograms. if (!trackSelectedForFlow(track)) { continue; } + const float pt = track.pt(); + if (std::abs(track.sign()) == 1) { + registry.fill(HIST("DetectorPidQA/TPC/dEdxAll"), pt, track.tpcSignal()); + if (track.hasTOF()) { + registry.fill(HIST("DetectorPidQA/TOF/tofTimeAll"), pt, track.tofSignal() - track.tofEvTime()); + } + } + const int pid = getPidConfigurable(track); - if (!isWithinPOIPtRange(pid, track.pt())) { + fillPidQualityQA(track, cent, pid); + // The existing species-dependent TPC/TOF histograms require the current + // unique PID assignment and its corresponding POI pT range. + if (!isWithinPOIPtRange(pid, pt)) { continue; } switch (pid) { case MyParticleType::kPion: - registry.fill(HIST("DetectorPidQA/TPC/dEdxPi"), track.tpcInnerParam(), track.tpcSignal()); + registry.fill(HIST("DetectorPidQA/TPC/dEdxPi"), pt, track.tpcSignal()); if (track.hasTOF()) { - registry.fill(HIST("DetectorPidQA/TOF/deltaPi"), track.p(), o2::aod::pidutils::tofExpSignalDiff(track)); + registry.fill(HIST("DetectorPidQA/TOF/deltaPi"), pt, o2::aod::pidutils::tofExpSignalDiff(track)); } break; case MyParticleType::kKaon: - registry.fill(HIST("DetectorPidQA/TPC/dEdxKa"), track.tpcInnerParam(), track.tpcSignal()); + registry.fill(HIST("DetectorPidQA/TPC/dEdxKa"), pt, track.tpcSignal()); if (track.hasTOF()) { - registry.fill(HIST("DetectorPidQA/TOF/deltaKa"), track.p(), o2::aod::pidutils::tofExpSignalDiff(track)); + registry.fill(HIST("DetectorPidQA/TOF/deltaKa"), pt, o2::aod::pidutils::tofExpSignalDiff(track)); } break; case MyParticleType::kProton: - registry.fill(HIST("DetectorPidQA/TPC/dEdxPr"), track.tpcInnerParam(), track.tpcSignal()); + registry.fill(HIST("DetectorPidQA/TPC/dEdxPr"), pt, track.tpcSignal()); if (track.hasTOF()) { - registry.fill(HIST("DetectorPidQA/TOF/deltaPr"), track.p(), o2::aod::pidutils::tofExpSignalDiff(track)); + registry.fill(HIST("DetectorPidQA/TOF/deltaPr"), pt, o2::aod::pidutils::tofExpSignalDiff(track)); } break; default: @@ -4368,5 +5413,6 @@ struct PidFlowPtCorr { WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ + adaptAnalysisTask(cfgc), adaptAnalysisTask(cfgc)}; } diff --git a/PWGCF/GenericFramework/Core/CMakeLists.txt b/PWGCF/GenericFramework/Core/CMakeLists.txt old mode 100755 new mode 100644 diff --git a/PWGCF/GenericFramework/Core/FlowContainer.cxx b/PWGCF/GenericFramework/Core/FlowContainer.cxx index c51fe3ece8a..4b94468bde6 100644 --- a/PWGCF/GenericFramework/Core/FlowContainer.cxx +++ b/PWGCF/GenericFramework/Core/FlowContainer.cxx @@ -415,6 +415,10 @@ TProfile* FlowContainer::GetCorrXXVsMulti(const char* order, int l_pti) } delete rethist; } + if (!retSubset) { + LOGF(error, "Correlation profile is null"); + return nullptr; + } if (fMultiRebin > 0) { TString temp_name(retSubset->GetName()); TProfile* tempprof = dynamic_cast(retSubset->Clone("tempProfile")); @@ -525,9 +529,9 @@ TH1D* FlowContainer::GetCN2VsX(int n, bool onPt, double arg1, double arg2) corrN2 = GetHistCorrXXVsMulti(Form("%i2", n), static_cast(arg1)); corrN2->SetName(Form("Corr_%s", corrN2->GetName())); TH1D* rethist = GetCN2(corrN2); - TString* nam = new TString(corrN2->GetName()); + const TString nam(corrN2->GetName()); delete corrN2; - rethist->SetName(nam->Data()); + rethist->SetName(nam.Data()); if (onPt) { int bins = fProf->GetXaxis()->FindBin(arg1); int bins2 = fProf->GetXaxis()->FindBin(arg2); @@ -543,10 +547,10 @@ TH1D* FlowContainer::GetCN2VsX(int n, bool onPt, double arg1, double arg2) TH1D* FlowContainer::GetVN2VsX(int n, bool onPt, double arg1, double arg2) { TH1D* corrh = GetCN2VsX(n, onPt, arg1, arg2); - TString* nam = new TString(corrh->GetName()); + const TString nam(corrh->GetName()); TH1D* rethist = GetVN2(corrh); delete corrh; - rethist->SetName(nam->Data()); + rethist->SetName(nam.Data()); if (onPt) { int bins = fProf->GetXaxis()->FindBin(arg1); int bins2 = fProf->GetXaxis()->FindBin(arg2); @@ -556,7 +560,6 @@ TH1D* FlowContainer::GetVN2VsX(int n, bool onPt, double arg1, double arg2) } else { rethist->SetTitle(Form(";#it{N}_{tr};v_{%i}{2}", n)); } - delete nam; return rethist; } @@ -706,10 +709,10 @@ TH1D* FlowContainer::GetCN4VsX(int n, bool onPt, double arg1, double arg2) corrN4->SetName(Form("Corr_%s", corrN4->GetName())); } TH1D* rethist = GetCN4(corrN4, corrN2); - TString* nam = new TString(corrN4->GetName()); + const TString nam(corrN4->GetName()); delete corrN2; delete corrN4; - rethist->SetName(nam->Data()); + rethist->SetName(nam.Data()); if (onPt) { int bins = fProf->GetXaxis()->FindBin(arg1); int bins2 = fProf->GetXaxis()->FindBin(arg2); @@ -780,9 +783,9 @@ TH1D* FlowContainer::GetVN4VsX(int n, bool onPt, double arg1, double arg2) { TH1D* temph = GetCN4VsX(n, onPt, arg1, arg2); TH1D* rethist = GetVN4(temph); - TString* nam = new TString(temph->GetName()); + const TString nam(temph->GetName()); delete temph; - rethist->SetName(nam->Data()); + rethist->SetName(nam.Data()); if (onPt) { int bins = fProf->GetXaxis()->FindBin(arg1); int bins2 = fProf->GetXaxis()->FindBin(arg2); @@ -812,9 +815,9 @@ TH1D* FlowContainer::GetCN6VsX(int n, bool onPt, double arg1, double arg2) TH1D* rethist = GetCN6(corrN6, corrN4, corrN2); delete corrN2; delete corrN4; - TString* nam = new TString(corrN6->GetName()); + const TString nam(corrN6->GetName()); delete corrN6; - rethist->SetName(nam->Data()); + rethist->SetName(nam.Data()); if (onPt) { int bins = fProf->GetXaxis()->FindBin(arg1); int bins2 = fProf->GetXaxis()->FindBin(arg2); @@ -830,9 +833,9 @@ TH1D* FlowContainer::GetVN6VsX(int n, bool onPt, double arg1, double arg2) { TH1D* temph = GetCN6VsX(n, onPt, arg1, arg2); TH1D* rethist = GetVN6(temph); - TString* nam = new TString(temph->GetName()); + const TString nam(temph->GetName()); delete temph; - rethist->SetName(nam->Data()); + rethist->SetName(nam.Data()); if (onPt) { int bins = fProf->GetXaxis()->FindBin(arg1); int bins2 = fProf->GetXaxis()->FindBin(arg2); @@ -866,9 +869,9 @@ TH1D* FlowContainer::GetCN8VsX(int n, bool onPt, double arg1, double arg2) delete corrN2; delete corrN4; delete corrN6; - TString* nam = new TString(corrN8->GetName()); + const TString nam(corrN8->GetName()); delete corrN8; - rethist->SetName(nam->Data()); + rethist->SetName(nam.Data()); if (onPt) { int bins = fProf->GetXaxis()->FindBin(arg1); int bins2 = fProf->GetXaxis()->FindBin(arg2); @@ -884,9 +887,9 @@ TH1D* FlowContainer::GetVN8VsX(int n, bool onPt, double arg1, double arg2) { TH1D* temph = GetCN8VsX(n, onPt, arg1, arg2); TH1D* rethist = GetVN8(temph); - TString* nam = new TString(temph->GetName()); + const TString nam(temph->GetName()); delete temph; - rethist->SetName(nam->Data()); + rethist->SetName(nam.Data()); if (onPt) { int bins = fProf->GetXaxis()->FindBin(arg1); int bins2 = fProf->GetXaxis()->FindBin(arg2); @@ -928,7 +931,7 @@ TProfile* FlowContainer::GetRefFlowProfile(const char* order, double m1, double if (nStopBin < nStartBin) nStopBin = fProf->GetXaxis()->GetNbins(); int nBins = nStopBin - nStartBin + 1; - double* l_bins = new double[nBins + 1]; + std::vector l_bins(nBins + 1); for (int i = 0; i <= nBins; i++) l_bins[i] = i; TProfile* retpf = 0; @@ -939,7 +942,7 @@ TProfile* FlowContainer::GetRefFlowProfile(const char* order, double m1, double while (fIDName.Tokenize(l_name, l_pos)) { l_name.Append(order); int ybin = fProf->GetYaxis()->FindBin(l_name.Data()); - TProfile* tempprof = rhSubset->GetSubset(kTRUE, "tempprof", ybin, ybin, nBins, l_bins); + TProfile* tempprof = rhSubset->GetSubset(kTRUE, "tempprof", ybin, ybin, nBins, l_bins.data()); if (!retpf) retpf = dynamic_cast(tempprof->Clone("RefFlowProf")); else @@ -1237,5 +1240,5 @@ double* FlowContainer::GetMultiRebin(int& nbins) double* retBins = new double[fMultiRebin + 1]; for (int i = 0; i <= nbins; i++) retBins[i] = fMultiRebinEdges[i]; - return fMultiRebinEdges; + return retBins; } diff --git a/PWGCF/GenericFramework/Core/FlowPtContainer.cxx b/PWGCF/GenericFramework/Core/FlowPtContainer.cxx index d2d5a1740f6..d76b8a6bb39 100644 --- a/PWGCF/GenericFramework/Core/FlowPtContainer.cxx +++ b/PWGCF/GenericFramework/Core/FlowPtContainer.cxx @@ -16,6 +16,7 @@ #include "FlowPtContainer.h" #include "PWGCF/GenericFramework/Core/BootstrapProfile.h" +#include "PWGCF/GenericFramework/Core/GFW.h" #include "PWGCF/GenericFramework/Core/GFWConfig.h" #include @@ -31,16 +32,154 @@ #include #include +#include +#include #include #include #include #include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include #include #include using namespace o2::analysis::genericframework; using namespace o2::analysis::genericframework::eventweight; +using o2::analysis::genericframework::flowptcontainer::PtCorrConfig; +using o2::analysis::genericframework::flowptcontainer::PtMomentConfig; + +namespace +{ +constexpr std::size_t TwoParticleOrder = 2; +constexpr int ParityDivisor = 2; + +bool parseInteger(std::string_view token, int& value) +{ + if (!token.empty() && token.front() == '+') { + token.remove_prefix(1); + } + const auto result = std::from_chars(token.data(), token.data() + token.size(), value); + return !token.empty() && result.ec == std::errc{} && result.ptr == token.data() + token.size(); +} + +std::optional>> replacePtTerms(std::string& expression, const std::string& head) +{ + std::vector> isPt; + size_t cursor = 0; + while (true) { + auto openBrace = expression.find('{', cursor); + if (openBrace == std::string::npos) { + break; + } + if (openBrace > 0 && std::isspace(static_cast(expression[openBrace - 1])) == 0) { + expression.insert(openBrace++, 1, ' '); + } + const auto closeBrace = expression.find('}', openBrace + 1); + if (closeBrace == std::string::npos || expression.find('{', openBrace + 1) < closeBrace) { + LOGF(error, "Invalid pT correlation %s: unmatched brace", head.c_str()); + return std::nullopt; + } + + std::string terms = expression.substr(openBrace + 1, closeBrace - openBrace - 1); + std::replace(terms.begin(), terms.end(), ',', ' '); + std::replace(terms.begin(), terms.end(), ';', ' '); + std::istringstream stream(terms); + std::string replacement; + std::string token; + std::vector groupIsPt; + while (stream >> token) { + int harmonic = 0; + const bool tokenIsPt = token == "pt"; + if (!tokenIsPt && !parseInteger(token, harmonic)) { + LOGF(error, "Invalid pT correlation %s: unknown observable %s", head.c_str(), token.c_str()); + return std::nullopt; + } + if (!replacement.empty()) { + replacement += ' '; + } + replacement += tokenIsPt ? "0" : token; + groupIsPt.push_back(tokenIsPt); + } + if (groupIsPt.empty()) { + LOGF(error, "Invalid pT correlation %s: empty observable group", head.c_str()); + return std::nullopt; + } + + expression.replace(openBrace + 1, closeBrace - openBrace - 1, replacement); + cursor = openBrace + replacement.size() + 2; + isPt.push_back(std::move(groupIsPt)); + } + + if (isPt.empty()) { + LOGF(error, "Invalid pT correlation %s: no observable groups", head.c_str()); + return std::nullopt; + } + return isPt; +} + +std::string makeFullTopologyName(const std::string& head, std::uint64_t activeFactors, std::uint64_t allFactors, bool hasFlow, int ptOrder) +{ + if (activeFactors == allFactors) { + return head; + } + + std::string name = head + "_full"; + if (hasFlow && (activeFactors & std::uint64_t{1}) != 0) { + name += "_flow"; + } + const int ptOffset = hasFlow ? 1 : 0; + for (int i = 0; i < ptOrder; ++i) { + if ((activeFactors & (std::uint64_t{1} << (ptOffset + i))) != 0) { + name += "_pt" + std::to_string(i + 1); + } + } + return name; +} + +double binomialCoefficient(int n, int k) +{ + k = std::min(k, n - k); + double result = 1.; + for (int i = 1; i <= k; ++i) { + result *= static_cast(n - k + i) / i; + } + return result; +} + +std::string makePostprocessName(std::string_view observable, const char* head, int ind) +{ + return std::string{head} + "_" + std::string{observable} + "_" + std::to_string(ind); +} + +bool sameMomentConfigs(const std::vector& first, const std::vector& second) +{ + if (first.size() != second.size()) { + return false; + } + for (size_t config = 0; config < first.size(); ++config) { + if (first[config].head != second[config].head || first[config].groups.size() != second[config].groups.size()) { + return false; + } + for (size_t group = 0; group < first[config].groups.size(); ++group) { + const auto& lhs = first[config].groups[group]; + const auto& rhs = second[config].groups[group]; + if (lhs.region != rhs.region || lhs.order != rhs.order || lhs.meanProfile != rhs.meanProfile) { + return false; + } + } + } + return true; +} +} // namespace FlowPtContainer::FlowPtContainer() : fCMTermList(nullptr), fCorrList(nullptr), @@ -54,7 +193,9 @@ FlowPtContainer::FlowPtContainer() : fCMTermList(nullptr), fillCounter(0), fEventWeight(EventWeight::UnityWeight), fUseCentralMoments(true), - fUseGap(false) + fUseGap(false), + fPtCentralMomentList(nullptr), + fPtCumulantList(nullptr) { } FlowPtContainer::~FlowPtContainer() @@ -66,6 +207,8 @@ FlowPtContainer::~FlowPtContainer() delete fSubCMList; delete fCumulantList; delete fCentralMomentList; + delete fPtCentralMomentList; + delete fPtCumulantList; }; FlowPtContainer::FlowPtContainer(const char* name) : TNamed(name, name), fCMTermList(nullptr), @@ -80,7 +223,9 @@ FlowPtContainer::FlowPtContainer(const char* name) : TNamed(name, name), fillCounter(0), fEventWeight(EventWeight::UnityWeight), fUseCentralMoments(true), - fUseGap(false) + fUseGap(false), + fPtCentralMomentList(nullptr), + fPtCumulantList(nullptr) { } FlowPtContainer::FlowPtContainer(const char* name, const char* title) : TNamed(name, title), @@ -96,8 +241,349 @@ FlowPtContainer::FlowPtContainer(const char* name, const char* title) : TNamed(n fillCounter(0), fEventWeight(EventWeight::UnityWeight), fUseCentralMoments(true), - fUseGap(false) + fUseGap(false), + fPtCentralMomentList(nullptr), + fPtCumulantList(nullptr) +{ +} +void FlowPtContainer::initialise(const o2::framework::AxisSpec& axis, const GFWRegions& regions, const GFWCorrConfigs& configs, const int& nsub) { + const auto numberOfRegions = regions.GetNames().size(); + const auto numberOfConfigs = configs.GetCorrs().size(); + if (regions.GetEtaMin().size() != numberOfRegions || regions.GetEtaMax().size() != numberOfRegions || + regions.GetpTDifs().size() != numberOfRegions || regions.GetBitmasks().size() != numberOfRegions || + configs.GetHeads().size() != numberOfConfigs || configs.GetpTDifs().size() != numberOfConfigs) { + LOGF(error, "Cannot initialize %s: GFW configuration vectors have different sizes", GetName()); + return; + } + if (nsub < 0) { + LOGF(error, "Cannot initialize %s: number of subsamples cannot be negative", GetName()); + return; + } + + std::vector multiBins = axis.binEdges; + int numberOfBins = axis.nBins.value_or(0); + const bool isVariableBinning = numberOfBins <= 0; + if (isVariableBinning) { + numberOfBins = multiBins.size() - 1; + } + const size_t expectedEdges = isVariableBinning ? static_cast(numberOfBins + 1) : 2u; + if (numberOfBins <= 0 || multiBins.size() != expectedEdges) { + LOGF(error, "Cannot initialize %s: invalid multiplicity axis", GetName()); + return; + } + + GFW parser; + for (size_t i = 0; i < numberOfRegions; ++i) { + parser.AddRegion(regions.GetNames()[i], regions.GetEtaMin()[i], regions.GetEtaMax()[i], regions.GetpTDifs()[i] != 0 ? 2 : 1, regions.GetBitmasks()[i]); + } + if (parser.fRegions.size() != numberOfRegions) { + LOGF(error, "Cannot initialize %s: invalid GFW region", GetName()); + return; + } + + struct ParsedPtRequest { + GFW::CorrConfig config; + std::vector> isPt; + std::vector> factors; + std::vector> ptTerms; + int ptOrder{}; + bool hasFlow{}; + std::uint64_t allFactors{}; + }; + + std::vector requests; + requests.reserve(numberOfConfigs); + std::unordered_set correlationHeads; + std::vector regionMaxOrders(numberOfRegions, 0); + int maxPtOrder = 0; + for (size_t i = 0; i < numberOfConfigs; ++i) { + if (configs.GetpTDifs()[i] != 0) { + LOGF(error, "Invalid pT correlation %s: differential configurations are not supported", configs.GetHeads()[i].c_str()); + return; + } + std::string expression = configs.GetCorrs()[i]; + auto isPt = replacePtTerms(expression, configs.GetHeads()[i]); + if (!isPt) { + return; + } + auto config = parser.GetCorrelatorConfig(expression, configs.GetHeads()[i], configs.GetpTDifs()[i] != 0); + if (config.Regs.size() != isPt->size() || config.Hars.size() != isPt->size() || config.Overlap.size() != isPt->size() || config.ptInd.size() != isPt->size()) { + LOGF(error, "Invalid pT correlation %s", config.Head.c_str()); + return; + } + if (std::any_of(config.ptInd.begin(), config.ptInd.end(), [](int ptIndex) { return ptIndex >= 0; })) { + LOGF(error, "Invalid pT correlation %s: explicit pT-bin indices are not supported", config.Head.c_str()); + return; + } + int ptOrder = 0; + bool hasFlow = false; + std::vector> ptTerms; + std::vector> factors(isPt->size()); + for (size_t group = 0; group < isPt->size(); ++group) { + if (config.Regs[group].empty() || config.Hars[group].size() != isPt->at(group).size()) { + LOGF(error, "Invalid pT correlation %s", config.Head.c_str()); + return; + } + const bool groupHasPt = std::any_of(isPt->at(group).begin(), isPt->at(group).end(), [](bool value) { return value; }); + const bool groupHasFlow = std::any_of(isPt->at(group).begin(), isPt->at(group).end(), [](bool value) { return !value; }); + if (groupHasPt && groupHasFlow) { + LOGF(error, "Invalid pT correlation %s: mixed flow and pt terms in one group are not supported", config.Head.c_str()); + return; + } + if (groupHasPt && (config.Regs[group].size() != 1 || config.Overlap[group] >= 0)) { + LOGF(error, "Invalid pT correlation %s: pt groups must contain exactly one region without an explicit overlap", config.Head.c_str()); + return; + } + factors[group].resize(isPt->at(group).size()); + for (size_t term = 0; term < isPt->at(group).size(); ++term) { + if (isPt->at(group)[term]) { + ptTerms.emplace_back(group, term); + ++ptOrder; + } else { + hasFlow = true; + } + } + } + if (ptOrder == 0) { + LOGF(error, "Invalid pT correlation %s: configuration does not contain pt", config.Head.c_str()); + return; + } + if (config.Head.empty() || !correlationHeads.insert(config.Head).second) { + LOGF(error, "Cannot initialize %s: correlation heads must be non-empty and unique", GetName()); + return; + } + + const int factorCount = ptOrder + (hasFlow ? 1 : 0); + if (factorCount >= std::numeric_limits::digits) { + LOGF(error, "Invalid pT correlation %s: too many observable factors", config.Head.c_str()); + return; + } + const int ptOffset = hasFlow ? 1 : 0; + for (int ptIndex = 0; ptIndex < ptOrder; ++ptIndex) { + const auto [group, term] = ptTerms[ptIndex]; + factors[group][term] = ptOffset + ptIndex; + } + if (hasFlow) { + for (size_t group = 0; group < isPt->size(); ++group) { + if (!isPt->at(group).front()) { + std::fill(factors[group].begin(), factors[group].end(), 0); + } + } + } + const std::uint64_t allFactors = (std::uint64_t{1} << factorCount) - 1; + for (size_t group = 0; group < isPt->size(); ++group) { + if (!isPt->at(group).front()) { + continue; + } + const int order = isPt->at(group).size(); + for (const int& region : config.Regs[group]) { + regionMaxOrders[region] = std::max(regionMaxOrders[region], order); + } + if (config.Overlap[group] >= 0) { + regionMaxOrders[config.Overlap[group]] = std::max(regionMaxOrders[config.Overlap[group]], order); + } + } + + maxPtOrder = std::max(maxPtOrder, ptOrder); + requests.push_back({.config = std::move(config), + .isPt = std::move(*isPt), + .factors = std::move(factors), + .ptTerms = std::move(ptTerms), + .ptOrder = ptOrder, + .hasFlow = hasFlow, + .allFactors = allFactors}); + } + + struct NaturalMean { + int region{}; + int ptIndex{}; + bool pTDif{}; + std::string profile; + }; + std::vector naturalMeans; + auto getPtRegion = [](const ParsedPtRequest& request, int ptIndex) { + const auto group = request.ptTerms[ptIndex].first; + return request.config.Regs[group].front(); + }; + auto findNaturalMean = [&](const ParsedPtRequest& request, int ptIndex) { + const auto group = request.ptTerms[ptIndex].first; + const int region = getPtRegion(request, ptIndex); + return std::find_if(naturalMeans.begin(), naturalMeans.end(), [&](const auto& mean) { + return mean.region == region && mean.ptIndex == request.config.ptInd[group] && mean.pTDif == request.config.pTDif; + }); + }; + + // Prefer explicitly requested one-particle observables as the shared native means, + // irrespective of their order in the configuration list. + for (const auto& request : requests) { + if (request.hasFlow || request.ptOrder != 1 || findNaturalMean(request, 0) != naturalMeans.end()) { + continue; + } + const auto group = request.ptTerms.front().first; + naturalMeans.push_back({.region = getPtRegion(request, 0), + .ptIndex = request.config.ptInd[group], + .pTDif = request.config.pTDif, + .profile = request.config.Head}); + } + + std::vector parsedConfigs; + parsedConfigs.reserve(requests.size()); + std::vector momentConfigs; + momentConfigs.reserve(requests.size()); + for (const auto& request : requests) { + const int ptOffset = request.hasFlow ? 1 : 0; + PtCorrConfig parsed{ + .head = request.config.Head, + .allFactors = request.allFactors, + .hasFlow = request.hasFlow, + .profiles = {}, + .meanProfiles = std::vector(request.ptOrder)}; + + // Factors in one single-region pT group are exchangeable. Their full-topology + // terms therefore depend only on how many factors are active. + std::vector> factorClasses; + if (request.hasFlow) { + factorClasses.push_back({0}); + } + for (size_t group = 0; group < request.isPt.size(); ++group) { + if (!request.isPt[group].front()) { + continue; + } + factorClasses.push_back(request.factors[group]); + } + + std::vector activeMasks{0}; + for (const auto& factors : factorClasses) { + const size_t previousSize = activeMasks.size(); + for (size_t i = 0; i < previousSize; ++i) { + std::uint64_t addedFactors = 0; + for (const int& factor : factors) { + addedFactors |= std::uint64_t{1} << factor; + activeMasks.push_back(activeMasks[i] | addedFactors); + } + } + } + std::sort(activeMasks.begin(), activeMasks.end()); + parsed.profiles.reserve(activeMasks.size() + request.ptOrder); + + // Inactive factors stay in the tuple as harmonic-zero multiplicity insertions, + // so every subset retains the event weight of the full correlation. + auto addFullTopologyProfile = [&](std::uint64_t activeFactors) { + auto profileConfig = request.config; + if (request.hasFlow && (activeFactors & std::uint64_t{1}) == 0) { + for (size_t group = 0; group < request.isPt.size(); ++group) { + if (!request.isPt[group].front()) { + std::fill(profileConfig.Hars[group].begin(), profileConfig.Hars[group].end(), 0); + } + } + } + profileConfig.Head = makeFullTopologyName(request.config.Head, activeFactors, request.allFactors, request.hasFlow, request.ptOrder); + parsed.profiles.push_back({.config = std::move(profileConfig), + .isPt = request.isPt, + .factors = request.factors, + .activeFactors = activeFactors, + .usesFullTopology = true}); + }; + + addFullTopologyProfile(request.allFactors); + for (const auto& activeFactors : activeMasks) { + if (activeFactors != 0 && activeFactors != request.allFactors) { + addFullTopologyProfile(activeFactors); + } + } + + // Each distinct pT source has one naturally weighted mean. Explicit one-particle + // requests provide that mean directly; otherwise the first consumer books it. + for (int ptIndex = 0; ptIndex < request.ptOrder; ++ptIndex) { + const std::uint64_t factor = std::uint64_t{1} << (ptOffset + ptIndex); + if (factor == request.allFactors) { + parsed.meanProfiles[ptIndex] = request.config.Head; + continue; + } + auto existingMean = findNaturalMean(request, ptIndex); + if (existingMean != naturalMeans.end()) { + parsed.meanProfiles[ptIndex] = existingMean->profile; + continue; + } + + const auto group = request.ptTerms[ptIndex].first; + GFW::CorrConfig meanConfig; + meanConfig.Regs = {{getPtRegion(request, ptIndex)}}; + meanConfig.Hars = {{0}}; + meanConfig.Overlap = {-1}; + meanConfig.ptInd = {request.config.ptInd[group]}; + meanConfig.pTDif = request.config.pTDif; + meanConfig.Head = request.config.Head + "_mean_pt" + std::to_string(ptIndex + 1); + parsed.meanProfiles[ptIndex] = meanConfig.Head; + naturalMeans.push_back({.region = meanConfig.Regs.front().front(), + .ptIndex = meanConfig.ptInd.front(), + .pTDif = meanConfig.pTDif, + .profile = meanConfig.Head}); + parsed.profiles.push_back({.config = std::move(meanConfig), + .isPt = {{true}}, + .factors = {{ptOffset + ptIndex}}, + .activeFactors = factor, + .usesFullTopology = false}); + } + + if (!request.hasFlow) { + PtMomentConfig momentConfig{ + .head = request.config.Head, + .groups = {}}; + momentConfig.groups.reserve(request.isPt.size()); + for (size_t group = 0; group < request.isPt.size(); ++group) { + const int firstFactor = request.factors[group].front(); + const int region = request.config.Regs[group].front(); + momentConfig.groups.push_back({.region = regions.GetNames()[region], + .order = static_cast(request.factors[group].size()), + .meanProfile = parsed.meanProfiles[firstFactor]}); + } + momentConfigs.push_back(std::move(momentConfig)); + } + + parsedConfigs.push_back(std::move(parsed)); + } + + auto* profiles = new TList(); + profiles->SetOwner(kTRUE); + std::unordered_set profileNames; + for (const auto& parsed : parsedConfigs) { + for (const auto& profile : parsed.profiles) { + const auto& head = profile.config.Head; + if (!profileNames.insert(head).second) { + LOGF(error, "Cannot initialize %s: generated profile name %s is not unique", GetName(), head.c_str()); + delete profiles; + return; + } + if (isVariableBinning) { + profiles->Add(new BootstrapProfile(head.c_str(), head.c_str(), numberOfBins, multiBins.data())); + } else { + profiles->Add(new BootstrapProfile(head.c_str(), head.c_str(), numberOfBins, multiBins.front(), multiBins.back())); + } + } + } + if (nsub > 0) { + for (int i = 0; i < profiles->GetEntries(); ++i) { + dynamic_cast(profiles->At(i))->InitializeSubsamples(nsub); + } + } + + delete fCorrList; + fCorrList = profiles; + fPtRegions = regions; + fPtCorrConfigs = std::move(parsedConfigs); + fPtMomentConfigs = std::move(momentConfigs); + clearPtPostprocessLists(); + fPtRegionMoments.clear(); + fPtRegionMoments.reserve(regionMaxOrders.size()); + for (const int& order : regionMaxOrders) { + fPtRegionMoments.push_back({.maxOrder = order, + .values = std::vector((order + 1) * (order + 1), 0.)}); + } + mpar = maxPtOrder; + nSubevents = static_cast(numberOfRegions); + LOGF(info, "Container %s initialized with %zu pT correlations, %i profiles and %i subsamples", GetName(), numberOfConfigs, profiles->GetEntries(), nsub); } void FlowPtContainer::initialise(const o2::framework::AxisSpec& axis, const int& maxOrder, const GFWCorrConfigs& configs, const int& nsub) { @@ -618,6 +1104,94 @@ void FlowPtContainer::fill(const double& w, const double& pt) sumP[i] += std::pow(w, i % (mpar + 1)) * std::pow(pt, i / (mpar + 1)); } } +void FlowPtContainer::fill(const double& eta, const double& w, const double& pt, int mask) +{ + int maxOrder = 0; + for (const auto& moments : fPtRegionMoments) { + maxOrder = std::max(maxOrder, moments.maxOrder); + } + if (maxOrder == 0) { + return; + } + + std::vector weightPowers(maxOrder + 1, 1.); + std::vector ptPowers(maxOrder + 1, 1.); + for (int power = 1; power <= maxOrder; ++power) { + weightPowers[power] = weightPowers[power - 1] * w; + ptPowers[power] = ptPowers[power - 1] * pt; + } + + for (size_t region = 0; region < fPtRegionMoments.size(); ++region) { + auto& moments = fPtRegionMoments[region]; + const bool inEtaRange = fPtRegions.GetEtaMin()[region] < eta && eta < fPtRegions.GetEtaMax()[region]; + const bool maskMatches = (fPtRegions.GetBitmasks()[region] & mask) != 0; + if (moments.maxOrder == 0 || !inEtaRange || !maskMatches) { + continue; + } + for (int weightPower = 1; weightPower <= moments.maxOrder; ++weightPower) { + for (int ptPower = 0; ptPower <= weightPower; ++ptPower) { + moments.values[weightPower * (moments.maxOrder + 1) + ptPower] += weightPowers[weightPower] * ptPowers[ptPower]; + } + } + } +} +void FlowPtContainer::clearPtRegionMoments() +{ + for (auto& moments : fPtRegionMoments) { // o2-linter: disable=const-ref-in-for-loop (contents are reset) + std::fill(moments.values.begin(), moments.values.end(), 0.); + } +} +void FlowPtContainer::clearPtPostprocessLists() +{ + delete fPtCentralMomentList; + fPtCentralMomentList = nullptr; + delete fPtCumulantList; + fPtCumulantList = nullptr; +} +double FlowPtContainer::getPtMoment(int region, int weightPower, int ptPower) const +{ + if (region < 0 || region >= static_cast(fPtRegionMoments.size())) { + return 0.; + } + const auto& moments = fPtRegionMoments[region]; + if (weightPower < 1 || weightPower > moments.maxOrder || ptPower < 0 || ptPower > weightPower) { + return 0.; + } + return moments.values[weightPower * (moments.maxOrder + 1) + ptPower]; +} +double FlowPtContainer::recursivePtCorrelation(int poi, int ref, int overlap, std::vector& weightPowers, std::vector& ptPowers) const +{ + if (weightPowers.empty() || weightPowers.size() != ptPowers.size()) { + return 0.; + } + if (weightPowers.front() != 1 && overlap >= 0) { + poi = overlap; + } + if (weightPowers.size() == 1) { + return getPtMoment(poi, weightPowers.front(), ptPowers.front()); + } + if (weightPowers.size() == TwoParticleOrder) { + const double product = getPtMoment(poi, weightPowers[0], ptPowers[0]) * getPtMoment(ref, weightPowers[1], ptPowers[1]); + const double selfCorrelation = overlap >= 0 ? getPtMoment(overlap, weightPowers[0] + weightPowers[1], ptPowers[0] + ptPowers[1]) : 0.; + return product - selfCorrelation; + } + + const int lastWeightPower = weightPowers.back(); + const int lastPtPower = ptPowers.back(); + weightPowers.pop_back(); + ptPowers.pop_back(); + double result = recursivePtCorrelation(poi, ref, overlap, weightPowers, ptPowers) * getPtMoment(ref, lastWeightPower, lastPtPower); + for (int term = static_cast(weightPowers.size()) - 1; term >= 0; --term) { + weightPowers[term] += lastWeightPower; + ptPowers[term] += lastPtPower; + result -= recursivePtCorrelation(poi, ref, overlap, weightPowers, ptPowers); + weightPowers[term] -= lastWeightPower; + ptPowers[term] -= lastPtPower; + } + weightPowers.push_back(lastWeightPower); + ptPowers.push_back(lastPtPower); + return result; +} void FlowPtContainer::fillSub(const double& w, const double& pt, int subIndex) { for (size_t i = 0; i < insub[subIndex].size(); ++i) { @@ -689,6 +1263,57 @@ void FlowPtContainer::fillPtProfiles(const double& centmult, const double& rn) } } } +void FlowPtContainer::fillPtProfiles(GFW& gfw, const double& centmult, const double& rn) +{ + if (!fCorrList) { + return; + } + clearPtPostprocessLists(); + for (const auto& correlation : fPtCorrConfigs) { + for (const auto& profileConfig : correlation.profiles) { + std::complex numerator{1., 0.}; + double denominator = 1.; + for (size_t group = 0; group < profileConfig.config.Regs.size(); ++group) { + if (profileConfig.isPt[group].front()) { + const int poi = profileConfig.config.Regs[group].front(); + const int ref = profileConfig.config.Regs[group].size() > 1 ? profileConfig.config.Regs[group][1] : poi; + int overlap = profileConfig.config.Overlap[group]; + if (overlap < 0 && poi == ref) { + overlap = ref; + } + std::vector weightPowers(profileConfig.isPt[group].size(), 1); + std::vector ptPowers; + ptPowers.reserve(profileConfig.isPt[group].size()); + for (const int& factor : profileConfig.factors[group]) { + ptPowers.push_back((profileConfig.activeFactors & (std::uint64_t{1} << factor)) != 0 ? 1 : 0); + } + auto denominatorPtPowers = std::vector(ptPowers.size(), 0); + denominator *= recursivePtCorrelation(poi, ref, overlap, weightPowers, denominatorPtPowers); + numerator *= recursivePtCorrelation(poi, ref, overlap, weightPowers, ptPowers); + } else { + GFW::CorrConfig groupConfig; + groupConfig.Regs = {profileConfig.config.Regs[group]}; + groupConfig.Hars = {profileConfig.config.Hars[group]}; + groupConfig.Overlap = {profileConfig.config.Overlap[group]}; + groupConfig.ptInd = {profileConfig.config.ptInd[group]}; + groupConfig.pTDif = profileConfig.config.pTDif; + numerator *= gfw.Calculate(groupConfig, 0, false); + denominator *= gfw.Calculate(groupConfig, 0, true).real(); + } + } + if (!std::isfinite(numerator.real()) || !std::isfinite(numerator.imag()) || !std::isfinite(denominator) || denominator <= 0.) { + continue; + } + auto* profile = dynamic_cast(fCorrList->FindObject(profileConfig.config.Head.c_str())); + if (!profile) { + LOGF(error, "pT profile %s has not been booked", profileConfig.config.Head.c_str()); + continue; + } + const double eventWeight = fEventWeight == EventWeight::UnityWeight ? 1. : denominator; + profile->FillProfile(centmult, numerator.real() / denominator, eventWeight, rn); + } + } +} bool FlowPtContainer::addPtProfile(const char* name, int observableOrder) { if (!fCorrList || !name || (name[0] == 0) || observableOrder < 1 || observableOrder > mpar) { @@ -1748,6 +2373,7 @@ double FlowPtContainer::orderedAddition(std::vector vec) } void FlowPtContainer::rebinMulti(int nbins) { + clearPtPostprocessLists(); if (fCMTermList) { for (int i = 0; i < fCMTermList->GetEntries(); i++) { dynamic_cast(fCMTermList->At(i))->RebinMulti(nbins); @@ -1776,6 +2402,7 @@ void FlowPtContainer::rebinMulti(int nbins) } void FlowPtContainer::rebinMulti(int nbins, double* binedges) { + clearPtPostprocessLists(); if (fCMTermList) { for (int i = 0; i < fCMTermList->GetEntries(); i++) { dynamic_cast(fCMTermList->At(i))->RebinMulti(nbins, binedges); @@ -1806,6 +2433,295 @@ TH1* FlowPtContainer::getCorrHist(int ind, int m) { return dynamic_cast(fCorrList->FindObject(Form("mpt%i", m)))->getHist(ind); } +TH1* FlowPtContainer::getPtCentralMomentHist(const char* head, int ind) +{ + if (!head || head[0] == 0 || !fCorrList) { + return nullptr; + } + + const std::string resultName = makePostprocessName("ptcentral", head, ind); + if (fPtCentralMomentList) { + if (auto* result = dynamic_cast(fPtCentralMomentList->FindObject(resultName.c_str()))) { + return result; + } + } + + const auto config = std::find_if(fPtMomentConfigs.begin(), fPtMomentConfigs.end(), [&](const auto& candidate) { return candidate.head == head; }); + if (config == fPtMomentConfigs.end()) { + LOGF(error, "Cannot calculate pT central moment %s: no pure-pT postprocessing configuration was stored", head); + return nullptr; + } + + int order = 0; + for (const auto& group : config->groups) { + order += group.order; + } + if (order < 1 || order >= std::numeric_limits::digits) { + LOGF(error, "Cannot calculate pT central moment %s: invalid order %i", head, order); + return nullptr; + } + + auto getProfileHist = [&](const std::string& name) -> std::unique_ptr { + auto* profile = dynamic_cast(fCorrList->FindObject(name.c_str())); + if (!profile) { + LOGF(error, "Cannot calculate pT central moment %s: profile %s is missing", head, name.c_str()); + return nullptr; + } + std::unique_ptr histogram{profile->getHist(ind)}; + if (histogram) { + histogram->SetDirectory(nullptr); + } + return histogram; + }; + + auto shape = getProfileHist(config->head); + if (!shape) { + return nullptr; + } + auto* result = dynamic_cast(shape->Clone(resultName.c_str())); + result->SetDirectory(nullptr); + result->Reset(); + + const std::uint64_t allFactors = (std::uint64_t{1} << order) - 1; + std::vector activeCounts(config->groups.size(), 0); + bool finished = false; + while (!finished) { + int activeOrder = 0; + int factorOffset = 0; + double coefficient = 1.; + std::uint64_t activeFactors = 0; + std::map meanPowers; + for (size_t groupIndex = 0; groupIndex < config->groups.size(); ++groupIndex) { + const auto& group = config->groups[groupIndex]; + const int active = activeCounts[groupIndex]; + activeOrder += active; + coefficient *= binomialCoefficient(group.order, active); + for (int factor = 0; factor < active; ++factor) { + activeFactors |= std::uint64_t{1} << (factorOffset + factor); + } + if (active < group.order) { + meanPowers[group.meanProfile] += group.order - active; + } + factorOffset += group.order; + } + + std::unique_ptr term; + if (activeOrder == 0) { + term.reset(dynamic_cast(shape->Clone())); + term->SetDirectory(nullptr); + term->Reset(); + for (int bin = 1; bin <= term->GetNbinsX(); ++bin) { + if (shape->GetBinContent(bin) != 0. || shape->GetBinError(bin) != 0.) { + term->SetBinContent(bin, 1.); + } + } + } else { + term = getProfileHist(makeFullTopologyName(config->head, activeFactors, allFactors, false, order)); + if (!term) { + delete result; + return nullptr; + } + term->SetDirectory(nullptr); + } + + for (const auto& [meanName, power] : meanPowers) { + auto mean = getProfileHist(meanName); + if (!mean) { + delete result; + return nullptr; + } + mean->SetDirectory(nullptr); + if (power > 1) { + for (int bin = 0; bin <= mean->GetNbinsX() + 1; ++bin) { + const double value = mean->GetBinContent(bin); + const double error = mean->GetBinError(bin); + mean->SetBinContent(bin, std::pow(value, power)); + mean->SetBinError(bin, std::abs(power * std::pow(value, power - 1) * error)); + } + } + term->Multiply(mean.get()); + } + + if ((order - activeOrder) % ParityDivisor != 0) { + coefficient = -coefficient; + } + result->Add(term.get(), coefficient); + + size_t groupIndex = 0; + for (; groupIndex < activeCounts.size(); ++groupIndex) { + if (++activeCounts[groupIndex] <= config->groups[groupIndex].order) { + break; + } + activeCounts[groupIndex] = 0; + } + finished = groupIndex == activeCounts.size(); + } + + if (!fPtCentralMomentList) { + fPtCentralMomentList = new TList(); + fPtCentralMomentList->SetOwner(kTRUE); + } + fPtCentralMomentList->Add(result); + return result; +} +TH1* FlowPtContainer::getPtCumulantHist(const char* head, int ind) +{ + if (!head || head[0] == 0 || !fCorrList) { + return nullptr; + } + + const std::string resultName = makePostprocessName("ptcumulant", head, ind); + if (fPtCumulantList) { + if (auto* result = dynamic_cast(fPtCumulantList->FindObject(resultName.c_str()))) { + return result; + } + } + + const auto config = std::find_if(fPtMomentConfigs.begin(), fPtMomentConfigs.end(), [&](const auto& candidate) { return candidate.head == head; }); + if (config == fPtMomentConfigs.end()) { + LOGF(error, "Cannot calculate pT cumulant %s: no pure-pT postprocessing configuration was stored", head); + return nullptr; + } + + std::vector factorGroups; + for (size_t groupIndex = 0; groupIndex < config->groups.size(); ++groupIndex) { + factorGroups.insert(factorGroups.end(), config->groups[groupIndex].order, groupIndex); + } + const int order = factorGroups.size(); + if (order < 1) { + return nullptr; + } + + if (order == 1) { + auto* profile = dynamic_cast(fCorrList->FindObject(config->groups.front().meanProfile.c_str())); + if (!profile) { + LOGF(error, "Cannot calculate pT cumulant %s: profile %s is missing", head, config->groups.front().meanProfile.c_str()); + return nullptr; + } + auto* result = profile->getHist(ind); + if (!result) { + return nullptr; + } + result->SetName(resultName.c_str()); + result->SetDirectory(nullptr); + if (!fPtCumulantList) { + fPtCumulantList = new TList(); + fPtCumulantList->SetOwner(kTRUE); + } + fPtCumulantList->Add(result); + return result; + } + + auto* centralMoment = getPtCentralMomentHist(head, ind); + if (!centralMoment) { + return nullptr; + } + auto* result = dynamic_cast(centralMoment->Clone(resultName.c_str())); + result->SetDirectory(nullptr); + result->Reset(); + + using Topology = std::vector>; + auto topology = [](const PtMomentConfig& candidate, const std::vector* counts = nullptr) { + Topology value; + for (size_t groupIndex = 0; groupIndex < candidate.groups.size(); ++groupIndex) { + const int count = counts ? counts->at(groupIndex) : candidate.groups[groupIndex].order; + if (count > 0) { + value.emplace_back(candidate.groups[groupIndex].region, count); + } + } + std::sort(value.begin(), value.end()); + return value; + }; + auto findMomentConfig = [&](const std::vector& counts) { + const auto required = topology(*config, &counts); + return std::find_if(fPtMomentConfigs.begin(), fPtMomentConfigs.end(), [&](const auto& candidate) { return topology(candidate) == required; }); + }; + auto describeTopology = [&](const std::vector& counts) { + std::string description; + for (const auto& [region, count] : topology(*config, &counts)) { + if (!description.empty()) { + description += " "; + } + description += region + "{" + std::to_string(count) + "pt}"; + } + return description; + }; + + bool failed = false; + std::vector blockForFactor(order, 0); + std::function addPartitions = [&](int factor, int highestBlock) { + if (failed) { + return; + } + if (factor < order) { + for (int block = 0; block <= highestBlock + 1; ++block) { + blockForFactor[factor] = block; + addPartitions(factor + 1, std::max(highestBlock, block)); + } + return; + } + + const int numberOfBlocks = highestBlock + 1; + std::vector> blockCounts(numberOfBlocks, std::vector(config->groups.size(), 0)); + std::vector blockSizes(numberOfBlocks, 0); + for (int factorIndex = 0; factorIndex < order; ++factorIndex) { + const int block = blockForFactor[factorIndex]; + ++blockCounts[block][factorGroups[factorIndex]]; + ++blockSizes[block]; + } + if (std::any_of(blockSizes.begin(), blockSizes.end(), [](int size) { return size == 1; })) { + return; + } + + std::unique_ptr term; + for (int block = 0; block < numberOfBlocks; ++block) { + const PtMomentConfig* blockConfig = &*config; + if (numberOfBlocks > 1) { + const auto found = findMomentConfig(blockCounts[block]); + if (found == fPtMomentConfigs.end()) { + LOGF(error, "Cannot calculate pT cumulant %s: missing lower-order correlation %s", head, describeTopology(blockCounts[block]).c_str()); + failed = true; + return; + } + blockConfig = &*found; + } + auto* blockMoment = getPtCentralMomentHist(blockConfig->head.c_str(), ind); + if (!blockMoment) { + failed = true; + return; + } + if (!term) { + term.reset(dynamic_cast(blockMoment->Clone())); + term->SetDirectory(nullptr); + } else { + term->Multiply(blockMoment); + } + } + + double coefficient = 1.; + for (int i = 2; i < numberOfBlocks; ++i) { + coefficient *= i; + } + if (numberOfBlocks % ParityDivisor == 0) { + coefficient = -coefficient; + } + result->Add(term.get(), coefficient); + }; + + blockForFactor.front() = 0; + addPartitions(1, 0); + if (failed) { + delete result; + return nullptr; + } + + if (!fPtCumulantList) { + fPtCumulantList = new TList(); + fPtCumulantList->SetOwner(kTRUE); + } + fPtCumulantList->Add(result); + return result; +} TH1* FlowPtContainer::getCentralMomentHist(int ind, int m) { if (!fCentralMomentList) { @@ -1897,27 +2813,41 @@ void FlowPtContainer::calculateCumulantHists(std::vector inh, int ind) } Long64_t FlowPtContainer::Merge(TCollection* collist) // o2-linter: disable=name/function-variable (Keep name consistent with ROOT streamer merging) { - if (!fCorrList || !fCMTermList) { - return 0; - } + delete fCumulantList; + fCumulantList = nullptr; + delete fCentralMomentList; + fCentralMomentList = nullptr; + clearPtPostprocessLists(); + Long64_t nmerged = 0; TIter allPTC(collist); FlowPtContainer* lPTC = nullptr; while ((lPTC = dynamic_cast(allPTC()))) { + if (!lPTC->fPtMomentConfigs.empty()) { + if (fPtMomentConfigs.empty()) { + fPtMomentConfigs = lPTC->fPtMomentConfigs; + } else if (!sameMomentConfigs(fPtMomentConfigs, lPTC->fPtMomentConfigs)) { + LOGF(error, "Cannot merge FlowPtContainer %s: pT postprocessing configurations differ", GetName()); + continue; + } + } else if (!fPtMomentConfigs.empty()) { + LOGF(error, "Cannot merge FlowPtContainer %s: incoming pT postprocessing configuration is missing", GetName()); + continue; + } + TList* tCMTerm = lPTC->fCMTermList; TList* tCorr = lPTC->fCorrList; TList* tCov = lPTC->fCovList; TList* tSub = lPTC->fSubList; TList* tSubCM = lPTC->fSubCMList; - TList* tCum = lPTC->fCumulantList; - TList* tCM = lPTC->fCentralMomentList; + bool merged = false; if (tCMTerm) { if (!fCMTermList) { fCMTermList = dynamic_cast(tCMTerm->Clone()); } else { mergeBSLists(fCMTermList, tCMTerm); } - nmerged++; + merged = true; } if (tCorr) { if (!fCorrList) { @@ -1925,6 +2855,7 @@ Long64_t FlowPtContainer::Merge(TCollection* collist) // o2-linter: disable=name } else { mergeBSLists(fCorrList, tCorr); } + merged = true; } if (tCov) { if (!fCovList) { @@ -1932,20 +2863,7 @@ Long64_t FlowPtContainer::Merge(TCollection* collist) // o2-linter: disable=name } else { mergeBSLists(fCovList, tCov); } - } - if (tCum) { - if (!fCumulantList) { - fCumulantList = dynamic_cast(tCum->Clone()); - } else { - mergeBSLists(fCumulantList, tCum); - } - } - if (tCM) { - if (!fCentralMomentList) { - fCentralMomentList = dynamic_cast(tCM->Clone()); - } else { - mergeBSLists(fCentralMomentList, tCM); - } + merged = true; } if (tSub) { if (!fSubList) { @@ -1953,6 +2871,7 @@ Long64_t FlowPtContainer::Merge(TCollection* collist) // o2-linter: disable=name } else { mergeBSLists(fSubList, tSub); } + merged = true; } if (tSubCM) { if (!fSubCMList) { @@ -1960,6 +2879,10 @@ Long64_t FlowPtContainer::Merge(TCollection* collist) // o2-linter: disable=name } else { mergeBSLists(fSubCMList, tSubCM); } + merged = true; + } + if (merged) { + ++nmerged; } } return nmerged; diff --git a/PWGCF/GenericFramework/Core/FlowPtContainer.h b/PWGCF/GenericFramework/Core/FlowPtContainer.h index 96ada820053..6bd33833f7e 100644 --- a/PWGCF/GenericFramework/Core/FlowPtContainer.h +++ b/PWGCF/GenericFramework/Core/FlowPtContainer.h @@ -16,6 +16,7 @@ #ifndef PWGCF_GENERICFRAMEWORK_CORE_FLOWPTCONTAINER_H_ #define PWGCF_GENERICFRAMEWORK_CORE_FLOWPTCONTAINER_H_ +#include "GFW.h" #include "GFWConfig.h" #include @@ -31,6 +32,7 @@ #include #include #include +#include #include #include @@ -42,6 +44,43 @@ enum EventWeight { }; }; +namespace o2::analysis::genericframework::flowptcontainer +{ +struct PtProfileConfig { + GFW::CorrConfig config; + std::vector> isPt; + std::vector> factors; + std::uint64_t activeFactors{}; // Flow, when present, is bit 0; pT terms follow in expression order. + bool usesFullTopology{}; +}; + +struct PtCorrConfig { + std::string head; + std::uint64_t allFactors{}; + bool hasFlow{}; + std::vector profiles; + std::vector meanProfiles; +}; + +struct PtRegionMoments { + int maxOrder{}; + std::vector values; +}; + +struct PtMomentGroup { + std::string region; + int order{}; + std::string meanProfile; + ClassDefNV(PtMomentGroup, 1); +}; + +struct PtMomentConfig { + std::string head; + std::vector groups; + ClassDefNV(PtMomentConfig, 1); +}; +} // namespace o2::analysis::genericframework::flowptcontainer + class FlowPtContainer : public TNamed { public: @@ -50,6 +89,7 @@ class FlowPtContainer : public TNamed explicit FlowPtContainer(const char* name); ~FlowPtContainer() override; FlowPtContainer(const char* name, const char* title); + void initialise(const o2::framework::AxisSpec& axis, const o2::analysis::genericframework::GFWRegions& regions, const o2::analysis::genericframework::GFWCorrConfigs& configs, const int& nsub = 10); void initialise(const o2::framework::AxisSpec& axis, const int& maxOrder, const o2::analysis::genericframework::GFWCorrConfigs& configs, const int& nsub = 10); void initialise(int nbinsx, double* xbins, const int& maxOrder, const o2::analysis::genericframework::GFWCorrConfigs& configs, const int& nsub = 10); void initialise(int nbinsx, double xlow, double xhigh, const int& maxOrder, const o2::analysis::genericframework::GFWCorrConfigs& configs, const int& nsub = 10); @@ -58,6 +98,7 @@ class FlowPtContainer : public TNamed void initialiseSubevent(int nbinsx, double* xbins, const int& maxOrder, const int& nsubev = 2, const int& nsub = 10); void initialiseSubevent(int nbinsx, double xlow, double xhigh, const int& maxOrder, const int& nsubev = 2, const int& nsub = 10); void fill(const double& w, const double& pt); + void fill(const double& eta, const double& w, const double& pt, int mask); void fillSub(const double& w, const double& pt, int subIndex); void fillSub1(const double& w, const double& pt) { fillSub(w, pt, 0); } void fillSub2(const double& w, const double& pt) { fillSub(w, pt, nSubevents - 1); } @@ -68,6 +109,7 @@ class FlowPtContainer : public TNamed void calculateSubeventCorrelations(); void calculateCMTerms(); void fillPtProfiles(const double& centmult, const double& rn); + void fillPtProfiles(GFW& gfw, const double& centmult, const double& rn); // Book and fill a separate pT observable with a weight calculated by the task. // The task can supply a separate event weight for the same observable. bool addPtProfile(const char* name, int observableOrder); @@ -133,6 +175,9 @@ class FlowPtContainer : public TNamed TH1* getCentralMomentHist(int ind, int m); TH1* getCumulantHist(int ind, int m); TH1* getCorrHist(int ind, int m); + // Postprocess a pure-pT configuration by head. ind = -1 selects the main profile. + TH1* getPtCentralMomentHist(const char* head, int ind = -1); + TH1* getPtCumulantHist(const char* head, int ind = -1); int getMpar() { return mpar; } Long64_t Merge(TCollection* collist); double orderedAddition(std::vector vec); @@ -157,6 +202,7 @@ class FlowPtContainer : public TNamed arr.resize(3 * 3 * 5 * 5, {0.0, 0.0}); warr.clear(); warr.resize(3 * 3 * 5 * 5, 0.0); + clearPtRegionMoments(); }; TList* fCMTermList; @@ -229,10 +275,20 @@ class FlowPtContainer : public TNamed private: static const int centralMomentMaxOrder = 4; - std::vector> subevents; + o2::analysis::genericframework::GFWRegions fPtRegions; //! + std::vector fPtCorrConfigs; //! + std::vector fPtRegionMoments; //! + std::vector fPtMomentConfigs; + TList* fPtCentralMomentList; //! + TList* fPtCumulantList; //! + std::vector> subevents; //! + void clearPtPostprocessLists(); + void clearPtRegionMoments(); + [[nodiscard]] double getPtMoment(int region, int weightPower, int ptPower) const; + double recursivePtCorrelation(int poi, int ref, int overlap, std::vector& weightPowers, std::vector& ptPowers) const; void getSubevents(int k, int n, std::vector& current, std::vector>& subevents); static constexpr std::array FactorialArray = {1., 1., 2., 6., 24., 120., 720., 5040., 40320.}; static constexpr std::array SignArray = {1, -1, 1, -1, 1, -1, 1, -1, 1}; - ClassDef(FlowPtContainer, 2); + ClassDefOverride(FlowPtContainer, 3); }; #endif // PWGCF_GENERICFRAMEWORK_CORE_FLOWPTCONTAINER_H_ diff --git a/PWGCF/GenericFramework/Core/GFW.cxx b/PWGCF/GenericFramework/Core/GFW.cxx index 3faf5142531..9a4918af00c 100644 --- a/PWGCF/GenericFramework/Core/GFW.cxx +++ b/PWGCF/GenericFramework/Core/GFW.cxx @@ -9,12 +9,18 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +/// \file GFW.cxx +/// \brief Initialize regions and calculate n-particle correlations +/// \author Emil Gorm Nielsen, NBI, emil.gorm.nielsen@cern.ch + #include "GFW.h" #include "PWGCF/GenericFramework/Core/GFWPowerArray.h" +#include + #include -#include +#include #include #include #include @@ -22,10 +28,20 @@ using std::complex; using std::pair; using std::string; -using std::vector; + +namespace +{ +constexpr std::size_t TwoParticleOrder = 2; +constexpr std::size_t ThreeParticleOrder = 3; +} // namespace GFW::GFW() : fInitialized(false) {} +void GFW::Region::PrintStructure() +{ + LOGF(info, "%s: eta [%f.. %f].", rName.c_str(), EtaMin, EtaMax); +} + GFW::~GFW() { for (auto pItr = fCumulants.begin(); pItr != fCumulants.end(); ++pItr) @@ -34,15 +50,15 @@ GFW::~GFW() void GFW::AddRegion(const string& refName, double lEtaMin, double lEtaMax, int lNpT, int BitMask) { if (lNpT < 1) { - printf("Number of pT bins cannot be less than 1! Not adding anything.\n"); + LOGF(warning, "Number of pT bins cannot be less than 1! Not adding anything."); return; } if (lEtaMin >= lEtaMax) { - printf("Eta min. cannot be more than eta max! Not adding...\n"); + LOGF(warning, "Eta min. cannot be more than eta max! Not adding..."); return; } if (refName == "") { - printf("Region must have a name!\n"); + LOGF(warning, "Region must have a name!"); return; } Region lOneRegion; @@ -56,26 +72,33 @@ void GFW::AddRegion(const string& refName, double lEtaMin, double lEtaMax, int l lOneRegion.BitMask = BitMask; // Bit mask AddRegion(lOneRegion); }; -void GFW::AddRegion(const string& refName, const vector& lNparVec, double lEtaMin, double lEtaMax, int lNpT, int BitMask) +void GFW::AddRegion(const string& refName, const std::vector& lNparVec, double lEtaMin, double lEtaMax, int lNpT, int BitMask) { - AddRegion(std::move(refName), lEtaMin, lEtaMax, lNpT, BitMask); + const auto oldSize = fRegions.size(); + AddRegion(refName, lEtaMin, lEtaMax, lNpT, BitMask); + if (fRegions.size() == oldSize) + return; (fRegions.end() - 1)->Nhar = static_cast(lNparVec.size()); (fRegions.end() - 1)->NparVec = lNparVec; (fRegions.end() - 1)->powsDefined = true; }; -void GFW::AddRegion(string refName, int lNhar, int lNpar, double lEtaMin, double lEtaMax, int lNpT, int BitMask) +void GFW::AddRegion(const string& refName, int lNhar, int lNpar, double lEtaMin, double lEtaMax, int lNpT, int BitMask) { - vector tVec = {}; + std::vector tVec = {}; + if (lNhar > 0) + tVec.reserve(lNhar); for (int i = 0; i < lNhar; i++) tVec.push_back(lNpar); - AddRegion(std::move(refName), tVec, lEtaMin, lEtaMax, lNpT, BitMask); + AddRegion(refName, tVec, lEtaMin, lEtaMax, lNpT, BitMask); }; -void GFW::AddRegion(string refName, int lNhar, int* lNparVec, double lEtaMin, double lEtaMax, int lNpT, int BitMask) +void GFW::AddRegion(const string& refName, int lNhar, int* lNparVec, double lEtaMin, double lEtaMax, int lNpT, int BitMask) { - vector tVec = {}; + std::vector tVec = {}; + if (lNhar > 0) + tVec.reserve(lNhar); for (int i = 0; i < lNhar; i++) tVec.push_back(lNparVec[i]); - AddRegion(std::move(refName), tVec, lEtaMin, lEtaMax, lNpT, BitMask); + AddRegion(refName, tVec, lEtaMin, lEtaMax, lNpT, BitMask); }; int GFW::CreateRegions() { @@ -84,14 +107,13 @@ int GFW::CreateRegions() fCumulants.clear(); InitializePowerArrays(); if (fRegions.size() < 1) { - printf("No regions set. Skipping...\n"); + LOGF(warning, "No regions set. Skipping..."); return 0; } int nRegions = 0; for (auto pItr = fRegions.begin(); pItr != fRegions.end(); pItr++) { - GFWCumulant* lCumulant = new GFWCumulant(); - lCumulant->CreateComplexVectorArrayVarPower(pItr->Nhar, pItr->NparVec, pItr->NpT); - fCumulants.push_back(*lCumulant); + fCumulants.emplace_back(); + fCumulants.back().CreateComplexVectorArrayVarPower(pItr->Nhar, pItr->NparVec, pItr->NpT); ++nRegions; } if (nRegions) @@ -114,22 +136,25 @@ complex GFW::TwoRec(int n1, int n2, int p1, int p2, int ptbin, GFWCumula complex formula = part1 * part2 - part3; return formula; }; -complex GFW::RecursiveCorr(GFWCumulant* qpoi, GFWCumulant* qref, GFWCumulant* qol, int ptbin, vector& hars) +complex GFW::RecursiveCorr(GFWCumulant* qpoi, GFWCumulant* qref, GFWCumulant* qol, int ptbin, std::vector& hars) { - vector pows; + std::vector pows; + pows.reserve(hars.size()); for (int i = 0; i < static_cast(hars.size()); i++) pows.push_back(1); return RecursiveCorr(qpoi, qref, qol, ptbin, hars, pows); }; -complex GFW::RecursiveCorr(GFWCumulant* qpoi, GFWCumulant* qref, GFWCumulant* qol, int ptbin, vector& hars, vector& pows) +complex GFW::RecursiveCorr(GFWCumulant* qpoi, GFWCumulant* qref, GFWCumulant* qol, int ptbin, std::vector& hars, std::vector& pows) { + if (hars.empty() || pows.empty()) + return complex(0, 0); if ((pows.at(0) != 1) && qol) qpoi = qol; // if the power of POI is not unity, then always use overlap (if defined). // Only valid for 1 particle of interest though! - if (hars.size() < 2) + if (hars.size() < TwoParticleOrder) return qpoi->Vec(hars.at(0), pows.at(0), ptbin); - if (hars.size() < 3) + if (hars.size() < ThreeParticleOrder) return TwoRec(hars.at(0), hars.at(1), pows.at(0), pows.at(1), ptbin, qpoi, qref, qol); int harlast = hars.at(hars.size() - 1); int powlast = pows.at(pows.size() - 1); @@ -141,7 +166,7 @@ complex GFW::RecursiveCorr(GFWCumulant* qpoi, GFWCumulant* qref, GFWCumu for (int i = harSize - 1; i >= 0; i--) { // checking if current configuration is a permutation of the next one. // Need to have more than 2 harmonics though, otherwise it doesn't make sense. - if (i > 2) { // only makes sense when we have more than two harmonics remaining + if (i > static_cast(TwoParticleOrder)) { // only makes sense when we have more than two harmonics remaining if (hars.at(i) == hars.at(i - 1) && pows.at(i) == pows.at(i - 1)) { // if it is a permutation, then increase degeneracy and continue; lDegeneracy++; continue; @@ -181,30 +206,28 @@ GFW::CorrConfig GFW::GetCorrelatorConfig(string config, string head, bool ptdif) // Then make sure we don't have any double-spaces: while (s_index(config, " ") > -1) s_replace_all(config, " ", " "); - vector regs; - vector hars; int sz1 = 0; int szend = 0; string ts, ts2; CorrConfig ReturnConfig; // Fetch region descriptor if (!s_tokenize(config, ts, szend, "{")) { - printf("Could not find any harmonics!\n"); + LOGF(warning, "Could not find any harmonics!"); return ReturnConfig; } szend = 0; int counter = 0; while (s_tokenize(config, ts, szend, "{")) { counter++; - ReturnConfig.Regs.push_back(vector{}); - ReturnConfig.Hars.push_back(vector{}); + ReturnConfig.Regs.push_back(std::vector{}); + ReturnConfig.Hars.push_back(std::vector{}); ReturnConfig.Overlap.push_back(-1); // initially, assume no overlap // Check if there's a particular pT bin I should be using here. If so, store it (otherwise, it's bin 0) int ptbin = -1; int sz2 = 0; if (s_contains(ts, "(")) { if (!s_contains(ts, ")")) { - printf("Missing \")\" in the configurator. Returning...\n"); + LOGF(warning, "Missing \")\" in the configurator. Returning..."); return ReturnConfig; } sz2 = s_index(ts, "("); @@ -231,7 +254,7 @@ GFW::CorrConfig GFW::GetCorrelatorConfig(string config, string head, bool ptdif) if (ts2 == " " || ts2 == "") continue; if (ind < 0) { - printf("Could not find region named %s!\n", ts2.c_str()); + LOGF(warning, "Could not find region named %s!", ts2.c_str()); break; } if (!isOverlap) @@ -253,7 +276,7 @@ GFW::CorrConfig GFW::GetCorrelatorConfig(string config, string head, bool ptdif) return ReturnConfig; }; -complex GFW::Calculate(int poi, int ref, vector hars, int ptbin) +complex GFW::Calculate(int poi, int ref, std::vector hars, int ptbin) { GFWCumulant* qref = &fCumulants.at(ref); GFWCumulant* qpoi = &fCumulants.at(poi); @@ -305,13 +328,13 @@ complex GFW::Calculate(CorrConfig corconf, int ptbin, bool SetHarmsToZer } return retval; }; -vector>> GFW::GetHarmonicsSingleConfig(const CorrConfig& incfg) +std::vector>> GFW::GetHarmonicsSingleConfig(const CorrConfig& incfg) { - vector>> retPair; + std::vector>> retPair; for (int iR = 0; iR < static_cast(incfg.Regs.size()); iR++) { if (static_cast(incfg.Regs[iR].size()) > 1) { - retPair.push_back(make_pair(incfg.Regs[iR][0], vector{incfg.Hars[iR][0]})); // If we have a PoI, then it comes with the first harmonic - retPair.push_back(make_pair(incfg.Regs[iR][1], incfg.Hars[iR])); // Then the second is ref. with full harmonics + retPair.push_back(make_pair(incfg.Regs[iR][0], std::vector{incfg.Hars[iR][0]})); // If we have a PoI, then it comes with the first harmonic + retPair.push_back(make_pair(incfg.Regs[iR][1], incfg.Hars[iR])); // Then the second is ref. with full harmonics } else { retPair.push_back(make_pair(incfg.Regs[iR][0], incfg.Hars[iR])); // Otherwise, it's only ref with all harmonics } @@ -322,7 +345,7 @@ vector>> GFW::GetHarmonicsSingleConfig(const CorrConfig& i }; void GFW::InitializePowerArrays() { - vector>> harSets(static_cast(fRegions.size())); + std::vector>> harSets(static_cast(fRegions.size())); for (const CorrConfig& lConf : fListOfCFGs) { auto HarPerReg = GetHarmonicsSingleConfig(lConf); for (const auto& oneHar : HarPerReg) @@ -332,13 +355,13 @@ void GFW::InitializePowerArrays() for (int i = 0; i < static_cast(harSets.size()); i++) { if (fRegions[i].powsDefined) continue; // Only do if powers have not been externally defined - vector powerArray = GFWPowerArray::GetPowerArray(harSets[i]); + std::vector powerArray = GFWPowerArray::GetPowerArray(harSets[i]); fRegions[i].Nhar = static_cast(powerArray.size()); fRegions[i].NparVec = powerArray; fRegions[i].powsDefined = true; } }; -complex GFW::Calculate(int poi, vector hars) +complex GFW::Calculate(int poi, std::vector hars) { GFWCumulant* qpoi = &fCumulants.at(poi); return RecursiveCorr(qpoi, qpoi, qpoi, 0, hars); @@ -351,13 +374,14 @@ int GFW::FindRegionByName(const string& refName) return -1; }; // String processing: -int GFW::s_index(string& instr, const string& pattern, const int& spos) +int GFW::s_index(const string& instr, const string& pattern, const int& spos) { - return instr.find(pattern, spos); + const auto position = instr.find(pattern, spos); + return position == string::npos ? -1 : static_cast(position); }; -bool GFW::s_contains(string& instr, const string& pattern) +bool GFW::s_contains(const string& instr, const string& pattern) { - return (s_index(instr, pattern) > -1); + return instr.find(pattern) != string::npos; }; void GFW::s_replace(string& instr, const string& pattern1, const string& pattern2, const int& spos) { @@ -374,7 +398,7 @@ void GFW::s_replace_all(string& instr, const string& pattern1, const string& pat lpos = s_index(instr, pattern1, lpos); } }; -bool GFW::s_tokenize(string& instr, string& subs, int& spos, const string& delim) +bool GFW::s_tokenize(const string& instr, string& subs, int& spos, const string& delim) { if (spos < 0 || spos >= static_cast(instr.size())) { spos = -1; diff --git a/PWGCF/GenericFramework/Core/GFW.h b/PWGCF/GenericFramework/Core/GFW.h index 481f5ce6bff..288ea0ed634 100644 --- a/PWGCF/GenericFramework/Core/GFW.h +++ b/PWGCF/GenericFramework/Core/GFW.h @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file GFW.h/.cxx +/// \file GFW.h /// \brief Class steers the initialization and calculation of n-particle correlations. Uses recursive function, all terms are calculated only once. /// \author Emil Gorm Nielsen (ack. V. Vislavicius), NBI, emil.gorm.nielsen@cern.ch @@ -19,7 +19,6 @@ #include "GFWCumulant.h" #include -#include #include #include #include @@ -28,7 +27,8 @@ class GFW { public: struct Region { - int Nhar, NpT; + int Nhar = 0; + int NpT = 0; std::vector NparVec{}; double EtaMin = -999; double EtaMax = -999; @@ -39,7 +39,7 @@ class GFW { return EtaMin < a.EtaMin; }; - void PrintStructure() { printf("%s: eta [%f.. %f].", rName.c_str(), EtaMin, EtaMax); } + void PrintStructure(); }; struct CorrConfig { std::vector> Regs{}; @@ -50,13 +50,15 @@ class GFW std::string Head = ""; }; GFW(); + GFW(const GFW&) = delete; + GFW& operator=(const GFW&) = delete; ~GFW(); std::vector fRegions; std::vector fCumulants; void AddRegion(const std::string& refName, double lEtaMin, double lEtaMax, int lNpT, int BitMask); void AddRegion(const std::string& refName, const std::vector& lNparVec, double lEtaMin, double lEtaMax, int lNpT, int BitMask); // Legacy - void AddRegion(std::string refName, int lNhar, int lNpar, double lEtaMin, double lEtaMax, int lNpT, int BitMask); // Legacy support, all powers are the same - void AddRegion(std::string refName, int lNhar, int* lNparVec, double lEtaMin, double lEtaMax, int lNpT, int BitMask); // Legacy support, array instead of a vector + void AddRegion(const std::string& refName, int lNhar, int lNpar, double lEtaMin, double lEtaMax, int lNpT, int BitMask); // Legacy support, all powers are the same + void AddRegion(const std::string& refName, int lNhar, int* lNparVec, double lEtaMin, double lEtaMax, int lNpT, int BitMask); // Legacy support, array instead of a vector int CreateRegions(); void Fill(double eta, int ptin, double phi, double weight, int mask, double secondWeight = -1); void Clear(); @@ -79,10 +81,10 @@ class GFW std::complex Calculate(int poi, int ref, std::vector hars, int ptbin = 0); // For differential, need POI and reference std::complex Calculate(int poi, std::vector hars); // For integrated case // Operations on strings. Equivalent to TString operations, but one to rid of root dependence - int s_index(std::string& instr, const std::string& pattern, const int& spos = 0); - bool s_contains(std::string& instr, const std::string& pattern); + int s_index(const std::string& instr, const std::string& pattern, const int& spos = 0); + bool s_contains(const std::string& instr, const std::string& pattern); void s_replace(std::string& instr, const std::string& pattern1, const std::string& pattern2, const int& spos = 0); void s_replace_all(std::string& instr, const std::string& pattern1, const std::string& pattern2); - bool s_tokenize(std::string& instr, std::string& substr, int& spos, const std::string& delim); + bool s_tokenize(const std::string& instr, std::string& substr, int& spos, const std::string& delim); }; #endif // PWGCF_GENERICFRAMEWORK_CORE_GFW_H_ diff --git a/PWGCF/GenericFramework/Core/GFWPowerArray.cxx b/PWGCF/GenericFramework/Core/GFWPowerArray.cxx index d8c988b111e..5ebd708f38f 100644 --- a/PWGCF/GenericFramework/Core/GFWPowerArray.cxx +++ b/PWGCF/GenericFramework/Core/GFWPowerArray.cxx @@ -9,108 +9,119 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +/// \file GFWPowerArray.cxx +/// \brief Compute required powers of Q-vectors for harmonic sets +/// \author Emil Gorm Nielsen, NBI, emil.gorm.nielsen@cern.ch + #include "GFWPowerArray.h" +#include + +#include #include -#include +#include #include +#include +#include #include -#include #include -using std::string; -using std::vector; - int GFWPowerArray::getHighestHarmonic(const HarSet& inhar) { // Highest possible harmonic: sum of same-sign harmonics - int maxPos = 0, maxNeg = 0; - for (int val : inhar) + std::int64_t maxPos = 0, maxNeg = 0; + for (const int& val : inhar) { if (val > 0) maxPos += val; else - maxNeg += abs(val); - return maxPos > maxNeg ? maxPos : maxNeg; + maxNeg += std::abs(static_cast(val)); + if (maxPos >= std::numeric_limits::max() || maxNeg >= std::numeric_limits::max()) + throw std::overflow_error("Harmonic sum exceeds the supported range"); + } + return static_cast(std::max(maxPos, maxNeg)); }; -HarSet GFWPowerArray::TrimVec(HarSet hars, int ind) +HarSet GFWPowerArray::trimVec(const HarSet& hars, int ind) { - HarSet retVec = std::move(hars); + HarSet retVec = hars; retVec.erase(retVec.begin() + ind); return retVec; }; -HarSet GFWPowerArray::AddConstant(HarSet hars, int offset) +HarSet GFWPowerArray::addConstant(const HarSet& hars, int offset) { - HarSet retVec = std::move(hars); - for (int& val : retVec) + HarSet retVec = hars; + for (int& val : retVec) // o2-linter: disable=const-ref-in-for-loop (updates each harmonic) val += offset; return retVec; }; -void GFWPowerArray::FlushVectorToMaster(HarSet& masterVector, HarSet& comVec, const int& MaxPower) +void GFWPowerArray::flushVectorToMaster(HarSet& masterVector, const HarSet& comVec, int maxPower) { - int nPartLoc = MaxPower - comVec.size() + 1; - for (auto& val : comVec) { - int absVal = abs(val); + int nPartLoc = maxPower - static_cast(comVec.size()) + 1; + for (const int& val : comVec) { + int absVal = std::abs(val); if (masterVector.at(absVal) < nPartLoc) { masterVector.at(absVal) = nPartLoc; } } }; -void GFWPowerArray::RecursiveFunction(HarSet& masterVector, HarSet hars, int offset, const int& MaxPower) +void GFWPowerArray::recursiveFunction(HarSet& masterVector, const HarSet& hars, int offset, int maxPower, std::size_t startIndex) { - HarSet compVec = AddConstant(hars, offset); - FlushVectorToMaster(masterVector, compVec, MaxPower); - for (size_t i = 0; i < hars.size(); i++) - RecursiveFunction(masterVector, TrimVec(hars, i), offset + hars.at(i), MaxPower); - ; + HarSet compVec = addConstant(hars, offset); + flushVectorToMaster(masterVector, compVec, maxPower); + for (std::size_t i = startIndex; i < hars.size(); i++) + recursiveFunction(masterVector, trimVec(hars, static_cast(i)), offset + hars.at(i), maxPower, i); }; -void GFWPowerArray::PrintVector(const HarSet& singleSet) +void GFWPowerArray::printVector(const HarSet& singleSet) { - int vcSize = static_cast(singleSet.size()); - if (!vcSize) - printf("Vector is empty!\n"); - printf("{%i", singleSet[0]); - for (int i = 1; i < vcSize; i++) - printf(", %i", singleSet[i]); - printf("}\n"); + if (singleSet.empty()) { + LOGF(info, "Vector is empty!"); + return; + } + std::string output = "{" + std::to_string(singleSet[0]); + for (std::size_t i = 1; i < singleSet.size(); i++) + output += ", " + std::to_string(singleSet[i]); + output += "}"; + LOGF(info, "%s", output.c_str()); } -HarSet GFWPowerArray::GetPowerArray(const vector& inHarmonics) +HarSet GFWPowerArray::GetPowerArray(const std::vector& inHarmonics) // o2-linter: disable=name/function-variable (preserve existing public API) { // First, find maximum number of particle correlations ( = max power) and maximum (sum of) harmonics - int MaxHar = 0; - int nMaxPart = 0; + int maxHar = 0; + int maxPart = 0; for (const HarSet& singleSet : inHarmonics) { int harSum = getHighestHarmonic(singleSet); - MaxHar = harSum > MaxHar ? harSum : MaxHar; + maxHar = harSum > maxHar ? harSum : maxHar; } - // Make a vector with MaxHar+1 entries (entry 0 for sum=0) - HarSet retVec = HarSet(MaxHar + 1); + // Make a vector with maxHar+1 entries (entry 0 for sum=0) + HarSet retVec = HarSet(maxHar + 1); // Then loop over all combinations and calculate max powers for (const HarSet& singleSet : inHarmonics) { + if (singleSet.size() >= static_cast(std::numeric_limits::max())) + throw std::overflow_error("Harmonic set exceeds the supported particle count"); int lNPart = static_cast(singleSet.size()); // Total number of particles correlated - RecursiveFunction(retVec, singleSet, 0, lNPart); + recursiveFunction(retVec, singleSet, 0, lNPart, 0); // Harmonic sum = 0 is a special case. In principle all 0 cases with non-zero harmonics are captured by the function above, but to calculate normalization, we set all harmonics to 0. This means that sum=0 power is the max number of harmonics/particles being correlated - nMaxPart = (lNPart > nMaxPart) ? lNPart : nMaxPart; + maxPart = (lNPart > maxPart) ? lNPart : maxPart; } // Override the sum=0 power with the number of correlated particles - if (retVec[0] < nMaxPart) - retVec[0] = nMaxPart; + if (retVec[0] < maxPart) + retVec[0] = maxPart; // Need an extra power ( = 0) for all non-zero powers - for (int& val : retVec) + for (int& val : retVec) // o2-linter: disable=const-ref-in-for-loop (increments the required powers) if (val != 0) val++; return retVec; }; -void GFWPowerArray::PowerArrayTest() +void GFWPowerArray::PowerArrayTest() // o2-linter: disable=name/function-variable (preserve existing public API) { - vector AllHars = { + std::vector allHars = { HarSet{2}, HarSet{3}, HarSet{2, 2}, HarSet{3, 3}}; - printf("Input harmonics are:\n"); - for (const HarSet& inSet : AllHars) - PrintVector(inSet); - printf("The configuration of powers must then be:\n"); - auto vc = GetPowerArray(AllHars); - PrintVector(vc); + LOGF(info, "Input harmonics are:"); + for (const HarSet& inSet : allHars) + printVector(inSet); + LOGF(info, "The configuration of powers must then be:"); + auto vc = GetPowerArray(allHars); + printVector(vc); }; diff --git a/PWGCF/GenericFramework/Core/GFWPowerArray.h b/PWGCF/GenericFramework/Core/GFWPowerArray.h index 7f83a72a3b7..cce3945d3f5 100644 --- a/PWGCF/GenericFramework/Core/GFWPowerArray.h +++ b/PWGCF/GenericFramework/Core/GFWPowerArray.h @@ -9,29 +9,29 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file GFWPowerArray.h/.cxx +/// \file GFWPowerArray.h /// \brief Class to compute necessary powers of Q-vectors based on input correlations /// \author Emil Gorm Nielsen, NBI, emil.gorm.nielsen@cern.ch #ifndef PWGCF_GENERICFRAMEWORK_CORE_GFWPOWERARRAY_H_ #define PWGCF_GENERICFRAMEWORK_CORE_GFWPOWERARRAY_H_ -#include +#include #include typedef std::vector HarSet; class GFWPowerArray { public: - static HarSet GetPowerArray(const std::vector& inHarmonics); - static void PowerArrayTest(); + static HarSet GetPowerArray(const std::vector& inHarmonics); // o2-linter: disable=name/function-variable (preserve existing public API) + static void PowerArrayTest(); // o2-linter: disable=name/function-variable (preserve existing public API) private: static int getHighestHarmonic(const HarSet& inhar); - static HarSet TrimVec(HarSet hars, int ind); - static HarSet AddConstant(HarSet hars, int offset); - static void FlushVectorToMaster(HarSet& masterVector, HarSet& comVec, const int& MaxPower); - static void RecursiveFunction(HarSet& masterVector, HarSet hars, int offset, const int& MaxPower); - static void PrintVector(const HarSet& singleSet); + static HarSet trimVec(const HarSet& hars, int ind); + static HarSet addConstant(const HarSet& hars, int offset); + static void flushVectorToMaster(HarSet& masterVector, const HarSet& comVec, int maxPower); + static void recursiveFunction(HarSet& masterVector, const HarSet& hars, int offset, int maxPower, std::size_t startIndex); + static void printVector(const HarSet& singleSet); }; #endif // PWGCF_GENERICFRAMEWORK_CORE_GFWPOWERARRAY_H_ diff --git a/PWGCF/GenericFramework/Core/GFWWeights.cxx b/PWGCF/GenericFramework/Core/GFWWeights.cxx index 7373995c3e2..b707e252764 100644 --- a/PWGCF/GenericFramework/Core/GFWWeights.cxx +++ b/PWGCF/GenericFramework/Core/GFWWeights.cxx @@ -9,6 +9,10 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +/// \file GFWWeights.cxx +/// \brief Implementation of corrections for the Generic Framework +/// \author Emil Gorm Nielsen, NBI, emil.gorm.nielsen@cern.ch + #include "GFWWeights.h" #include @@ -25,7 +29,49 @@ #include +#include #include +#include + +namespace +{ +constexpr int MCGenType = 2; + +TObjArray* cloneArray(TObjArray* source) +{ + if (!source) + return 0; + TObjArray* result = new TObjArray(); + result->SetName(source->GetName()); + result->SetOwner(kTRUE); + for (int i = 0; i < source->GetEntries(); i++) { + if (!source->At(i)) + continue; + TH3D* hist = reinterpret_cast(source->At(i)->Clone()); + hist->SetDirectory(0); + result->AddAt(hist, i); + } + return result; +} + +TH3D* cloneTH3D(TH3D* source) +{ + if (!source) + return 0; + TH3D* result = reinterpret_cast(source->Clone()); + result->SetDirectory(0); + return result; +} + +TH1D* cloneTH1D(TH1D* source) +{ + if (!source) + return 0; + TH1D* result = reinterpret_cast(source->Clone()); + result->SetDirectory(0); + return result; +} +} // namespace GFWWeights::GFWWeights() : TNamed("", ""), fDataFilled(kFALSE), @@ -49,6 +95,42 @@ GFWWeights::GFWWeights(const char* name) : TNamed(name, name), fAccInt(0), fNbinsPt(0), fbinsPt(0) {} +GFWWeights::GFWWeights(const GFWWeights& other) : TNamed(other), + fDataFilled(other.fDataFilled), + fMCFilled(other.fMCFilled), + fW_data(cloneArray(other.fW_data)), + fW_mcrec(cloneArray(other.fW_mcrec)), + fW_mcgen(cloneArray(other.fW_mcgen)), + fEffInt(cloneTH3D(other.fEffInt)), + fIntEff(cloneTH1D(other.fIntEff)), + fAccInt(cloneTH3D(other.fAccInt)), + fNbinsPt(other.fNbinsPt), + fbinsPt(0) +{ + if (other.fbinsPt) { + fbinsPt = new double[fNbinsPt + 1]; + for (int i = 0; i <= fNbinsPt; i++) + fbinsPt[i] = other.fbinsPt[i]; + } +} +GFWWeights& GFWWeights::operator=(const GFWWeights& other) +{ + if (this == &other) + return *this; + GFWWeights copy(other); + TNamed::operator=(other); + std::swap(fDataFilled, copy.fDataFilled); + std::swap(fMCFilled, copy.fMCFilled); + std::swap(fW_data, copy.fW_data); + std::swap(fW_mcrec, copy.fW_mcrec); + std::swap(fW_mcgen, copy.fW_mcgen); + std::swap(fEffInt, copy.fEffInt); + std::swap(fIntEff, copy.fIntEff); + std::swap(fAccInt, copy.fAccInt); + std::swap(fNbinsPt, copy.fNbinsPt); + std::swap(fbinsPt, copy.fbinsPt); + return *this; +} GFWWeights::~GFWWeights() { delete fW_data; @@ -78,14 +160,25 @@ void GFWWeights::init(bool AddData, bool AddMC) fbinsPt[1] = 1e6; } if (AddData) { + delete fAccInt; + fAccInt = 0; + delete fW_data; fW_data = new TObjArray(); fW_data->SetName("GFWWeights_Data"); fW_data->SetOwner(kTRUE); const char* tnd = getBinName(0, 0, Form("data_%s", this->GetName())); - fW_data->Add(new TH3D(tnd, ";#varphi;#eta;v_{z}", 60, 0, TMath::TwoPi(), 64, -1.6, 1.6, 40, -10, 10)); + TH3D* hist = new TH3D(tnd, ";#varphi;#eta;v_{z}", 60, 0, TMath::TwoPi(), 64, -1.6, 1.6, 40, -10, 10); // o2-linter: disable=external-pi (ROOT histogram axis uses double-precision two pi) + hist->SetDirectory(0); + fW_data->Add(hist); fDataFilled = kTRUE; } if (AddMC) { + delete fEffInt; + delete fIntEff; + fEffInt = 0; + fIntEff = 0; + delete fW_mcrec; + delete fW_mcgen; fW_mcrec = new TObjArray(); fW_mcrec->SetName("GFWWeights_MCRec"); fW_mcgen = new TObjArray(); @@ -94,8 +187,12 @@ void GFWWeights::init(bool AddData, bool AddMC) fW_mcgen->SetOwner(kTRUE); const char* tnr = getBinName(0, 0, "mcrec"); // all integrated over cent. anyway const char* tng = getBinName(0, 0, "mcgen"); // all integrated over cent. anyway - fW_mcrec->Add(new TH3D(tnr, ";#it{p}_{T};#eta;v_{z}", fNbinsPt, 0, 20, 64, -1.6, 1.6, 40, -10, 10)); - fW_mcgen->Add(new TH3D(tng, ";#it{p}_{T};#eta;v_{z}", fNbinsPt, 0, 20, 64, -1.6, 1.6, 40, -10, 10)); + TH3D* rec = new TH3D(tnr, ";#it{p}_{T};#eta;v_{z}", fNbinsPt, 0, 20, 64, -1.6, 1.6, 40, -10, 10); + TH3D* gen = new TH3D(tng, ";#it{p}_{T};#eta;v_{z}", fNbinsPt, 0, 20, 64, -1.6, 1.6, 40, -10, 10); + rec->SetDirectory(0); + gen->SetDirectory(0); + fW_mcrec->Add(rec); + fW_mcgen->Add(gen); reinterpret_cast(fW_mcrec->At(fW_mcrec->GetEntries() - 1))->GetXaxis()->Set(fNbinsPt, fbinsPt); reinterpret_cast(fW_mcgen->At(fW_mcgen->GetEntries() - 1))->GetXaxis()->Set(fNbinsPt, fbinsPt); fMCFilled = kTRUE; @@ -114,7 +211,7 @@ void GFWWeights::fill(double phi, double eta, double vz, double pt, double /*cen tar = fW_mcrec; pf = "mcrec"; } - if (htype == 2) { + if (htype == MCGenType) { tar = fW_mcgen; pf = "mcgen"; } @@ -122,8 +219,11 @@ void GFWWeights::fill(double phi, double eta, double vz, double pt, double /*cen return; TH3D* th3 = reinterpret_cast(tar->FindObject(getBinName(0, 0, pf))); // pT bin 0, V0M bin 0, since all integrated if (!th3) { - if (!htype) - tar->Add(new TH3D(getBinName(0, 0, pf), ";#varphi;#eta;v_{z}", 60, 0, TMath::TwoPi(), 64, -1.6, 1.6, 40, -10, 10)); // 0,0 since all integrated + if (!htype) { + TH3D* hist = new TH3D(getBinName(0, 0, pf), ";#varphi;#eta;v_{z}", 60, 0, TMath::TwoPi(), 64, -1.6, 1.6, 40, -10, 10); // o2-linter: disable=external-pi (ROOT histogram axis uses double-precision two pi) + hist->SetDirectory(0); + tar->Add(hist); + } th3 = reinterpret_cast(tar->At(tar->GetEntries() - 1)); } th3->Fill(htype ? pt : phi, eta, vz, weight); @@ -134,13 +234,13 @@ double GFWWeights::getWeight(double phi, double eta, double vz, double pt, doubl const char* pf = ""; if (htype == 0) { tar = fW_data; - pf = "data"; + pf = Form("data_%s", this->GetName()); } if (htype == 1) { tar = fW_mcrec; pf = "mcrec"; } - if (htype == 2) { + if (htype == MCGenType) { tar = fW_mcgen; pf = "mcgen"; } @@ -161,6 +261,8 @@ double GFWWeights::getNUA(double phi, double eta, double vz) { if (!fAccInt) createNUA(); + if (!fAccInt) + return 1; int xind = fAccInt->GetXaxis()->FindBin(phi); int etaind = fAccInt->GetYaxis()->FindBin(eta); int vzind = fAccInt->GetZaxis()->FindBin(vz); @@ -173,6 +275,8 @@ double GFWWeights::getNUE(double pt, double eta, double vz) { if (!fEffInt) createNUE(); + if (!fEffInt) + return 1; int xind = fEffInt->GetXaxis()->FindBin(pt); int etaind = fEffInt->GetYaxis()->FindBin(eta); int vzind = fEffInt->GetZaxis()->FindBin(vz); @@ -212,7 +316,7 @@ void GFWWeights::mcToEfficiency() }; void GFWWeights::rebinNUA(int nX, int nY, int nZ) { - if (fW_data->GetEntries() < 1) + if (!fW_data || fW_data->GetEntries() < 1) return; for (int i = 0; i < fW_data->GetEntries(); i++) { reinterpret_cast(fW_data->At(i))->RebinX(nX); @@ -227,44 +331,47 @@ void GFWWeights::createNUA(bool IntegrateOverCentAndPt) return; } TH1D* h1; - if (fW_data->GetEntries() < 1) + if (!fW_data || fW_data->GetEntries() < 1) return; - if (IntegrateOverCentAndPt) { - if (fAccInt) - delete fAccInt; - fAccInt = reinterpret_cast(fW_data->At(0)->Clone("IntegratedAcceptance")); - fAccInt->Sumw2(); - for (int etai = 1; etai <= fAccInt->GetNbinsY(); etai++) { - fAccInt->GetYaxis()->SetRange(etai, etai); + if (fAccInt) + delete fAccInt; + fAccInt = reinterpret_cast(fW_data->At(0)->Clone("IntegratedAcceptance")); + fAccInt->SetDirectory(0); + fAccInt->Sumw2(); + for (int etai = 1; etai <= fAccInt->GetNbinsY(); etai++) { + fAccInt->GetYaxis()->SetRange(etai, etai); + if (fAccInt->Integral() < 1) + continue; + for (int vzi = 1; vzi <= fAccInt->GetNbinsZ(); vzi++) { + fAccInt->GetZaxis()->SetRange(vzi, vzi); if (fAccInt->Integral() < 1) continue; - for (int vzi = 1; vzi <= fAccInt->GetNbinsZ(); vzi++) { - fAccInt->GetZaxis()->SetRange(vzi, vzi); - if (fAccInt->Integral() < 1) - continue; - h1 = reinterpret_cast(fAccInt->Project3D("x")); - double maxv = h1->GetMaximum(); - for (int phii = 1; phii <= h1->GetNbinsX(); phii++) { - fAccInt->SetBinContent(phii, etai, vzi, fAccInt->GetBinContent(phii, etai, vzi) / maxv); - fAccInt->SetBinError(phii, etai, vzi, fAccInt->GetBinError(phii, etai, vzi) / maxv); - } - delete h1; + h1 = reinterpret_cast(fAccInt->Project3D("x")); + double maxv = h1->GetMaximum(); + for (int phii = 1; phii <= h1->GetNbinsX(); phii++) { + fAccInt->SetBinContent(phii, etai, vzi, fAccInt->GetBinContent(phii, etai, vzi) / maxv); + fAccInt->SetBinError(phii, etai, vzi, fAccInt->GetBinError(phii, etai, vzi) / maxv); } - fAccInt->GetZaxis()->SetRange(1, fAccInt->GetNbinsZ()); + delete h1; } - fAccInt->GetYaxis()->SetRange(1, fAccInt->GetNbinsY()); - return; + fAccInt->GetZaxis()->SetRange(1, fAccInt->GetNbinsZ()); } + fAccInt->GetYaxis()->SetRange(1, fAccInt->GetNbinsY()); }; TH1D* GFWWeights::getdNdPhi() { + if (!fW_data || !fW_data->GetEntries()) + return 0; TH3D* temph = reinterpret_cast(fW_data->At(0)->Clone("tempH3")); TH1D* reth = reinterpret_cast(temph->Project3D("x")); reth->SetName("RetHist"); + reth->SetDirectory(0); delete temph; double max = reth->GetMaximum(); - if (max == 0) + if (max == 0) { + delete reth; return 0; + } for (int phi = 1; phi <= reth->GetNbinsX(); phi++) { if (reth->GetBinContent(phi) == 0) continue; @@ -281,32 +388,40 @@ void GFWWeights::createNUE(bool IntegrateOverCentrality) } TH3D* num = 0; TH3D* den = 0; - if (fW_mcrec->GetEntries() < 1 || fW_mcgen->GetEntries() < 1) + if (!fW_mcrec || !fW_mcgen || fW_mcrec->GetEntries() < 1 || fW_mcgen->GetEntries() < 1) return; - if (IntegrateOverCentrality) { - num = reinterpret_cast(fW_mcrec->At(0)); - den = reinterpret_cast(fW_mcgen->At(0)); - num->Sumw2(); - den->Sumw2(); - num->RebinY(2); - den->RebinY(2); - num->RebinZ(5); - den->RebinZ(5); - fEffInt = reinterpret_cast(num->Clone("Efficiency_Integrated")); - fEffInt->Divide(den); - return; - } + num = reinterpret_cast(fW_mcrec->At(0)); + den = reinterpret_cast(fW_mcgen->At(0)); + num->Sumw2(); + den->Sumw2(); + num->RebinY(2); + den->RebinY(2); + num->RebinZ(5); + den->RebinZ(5); + delete fEffInt; + fEffInt = reinterpret_cast(num->Clone("Efficiency_Integrated")); + fEffInt->SetDirectory(0); + fEffInt->Divide(den); }; void GFWWeights::readAndMerge(const TString& filelinks, const TString& listName, bool addData, bool addRec, bool addGen) { FILE* flist = fopen(filelinks.Data(), "r"); + if (!flist) { + LOGF(warning, "Could not open file list %s!\n", filelinks.Data()); + return; + } char str[150]; int nFiles = 0; - while (fscanf(flist, "%s\n", str) == 1) + while (fscanf(flist, "%149s", str) == 1) nFiles++; - rewind(flist); + if (fseek(flist, 0, SEEK_SET) != 0) { + LOGF(warning, "Could not rewind file list %s!\n", filelinks.Data()); + fclose(flist); + return; + } if (nFiles == 0) { LOGF(info, "No files to read!\n"); + fclose(flist); return; } if (!fW_data && addData) { @@ -326,19 +441,31 @@ void GFWWeights::readAndMerge(const TString& filelinks, const TString& listName, } TFile* tf = 0; for (int i = 0; i < nFiles; i++) { - auto retVal = fscanf(flist, "%s\n", str); - (void)retVal; + if (fscanf(flist, "%149s", str) != 1) + break; tf = new TFile(str, "READ"); if (tf->IsZombie()) { LOGF(warning, "Could not open file %s!\n", str); tf->Close(); + delete tf; + continue; + } + TObject* obj = tf->Get(listName.Data()); + TList* tl = dynamic_cast(obj); + if (!tl) { + LOGF(warning, "Could not fetch list %s from %s\n", listName.Data(), str); + delete obj; + tf->Close(); + delete tf; continue; } - TList* tl = reinterpret_cast(tf->Get(listName.Data())); - GFWWeights* tw = reinterpret_cast(tl->FindObject(this->GetName())); + GFWWeights* tw = dynamic_cast(tl->FindObject(this->GetName())); if (!tw) { LOGF(warning, "Could not fetch weights object from %s\n", str); + tl->Delete(); + delete tl; tf->Close(); + delete tf; continue; } if (addData) @@ -347,9 +474,14 @@ void GFWWeights::readAndMerge(const TString& filelinks, const TString& listName, addArray(fW_mcrec, tw->getRecArray()); if (addGen) addArray(fW_mcgen, tw->getGenArray()); - tf->Close(); + tl->Remove(tw); delete tw; + tl->Delete(); + delete tl; + tf->Close(); + delete tf; } + fclose(flist); }; void GFWWeights::addArray(TObjArray* targ, TObjArray* sour) { @@ -373,15 +505,20 @@ void GFWWeights::overwriteNUA() { if (!fAccInt) createNUA(); + if (!fAccInt || !fW_data || !fW_data->GetEntries()) + return; TString ts(fW_data->At(0)->GetName()); TH3D* trash = reinterpret_cast(fW_data->RemoveAt(0)); delete trash; - fW_data->Add(reinterpret_cast(fAccInt->Clone(ts.Data()))); + TH3D* hist = reinterpret_cast(fAccInt->Clone(ts.Data())); + hist->SetDirectory(0); + fW_data->AddAt(hist, 0); delete fAccInt; + fAccInt = 0; } -Long64_t GFWWeights::Merge(TCollection* collist) +Long64_t GFWWeights::Merge(TCollection* collist) // o2-linter: disable=root/entity,name/function-variable (ROOT Merge requires this signature) { - Long64_t nmerged = 0; + Long64_t nmerged = 0; // o2-linter: disable=root/entity (matches ROOT Merge return type) if (!fW_data) { fW_data = new TObjArray(); fW_data->SetName("Weights_Data"); @@ -397,12 +534,12 @@ Long64_t GFWWeights::Merge(TCollection* collist) fW_mcgen->SetName("Weights_MCGen"); fW_mcgen->SetOwner(kTRUE); } - GFWWeights* l_w = 0; - TIter all_w(collist); - while ((l_w = (reinterpret_cast(all_w())))) { - addArray(fW_data, l_w->getDataArray()); - addArray(fW_mcrec, l_w->getRecArray()); - addArray(fW_mcgen, l_w->getGenArray()); + GFWWeights* weight = 0; + TIter allWeights(collist); + while ((weight = (reinterpret_cast(allWeights())))) { + addArray(fW_data, weight->getDataArray()); + addArray(fW_mcrec, weight->getRecArray()); + addArray(fW_mcgen, weight->getGenArray()); nmerged++; } return nmerged; @@ -433,8 +570,10 @@ TH1D* GFWWeights::getIntegratedEfficiencyHist() den->Add(reinterpret_cast(fW_mcgen->At(i))); TH1D* num1d = reinterpret_cast(num->Project3D("x")); num1d->SetName("retHist"); + num1d->SetDirectory(0); num1d->Sumw2(); TH1D* den1d = reinterpret_cast(den->Project3D("x")); + den1d->SetDirectory(0); den1d->Sumw2(); num1d->Divide(den1d); delete num; @@ -462,6 +601,8 @@ double GFWWeights::getIntegratedEfficiency(double pt) } TH1D* GFWWeights::getEfficiency(double etamin, double etamax, double vzmin, double vzmax) { + if (!fW_mcrec || !fW_mcgen || !fW_mcrec->GetEntries() || !fW_mcgen->GetEntries()) + return 0; TH3D* num = reinterpret_cast(fW_mcrec->At(0)->Clone("Numerator")); for (int i = 1; i < fW_mcrec->GetEntries(); i++) num->Add(reinterpret_cast(fW_mcrec->At(i))); @@ -478,6 +619,8 @@ TH1D* GFWWeights::getEfficiency(double etamin, double etamax, double vzmin, doub den->GetZaxis()->SetRange(vz1, vz2); TH1D* num1d = reinterpret_cast(num->Project3D("x")); TH1D* den1d = reinterpret_cast(den->Project3D("x")); + num1d->SetDirectory(0); + den1d->SetDirectory(0); delete num; delete den; num1d->Sumw2(); @@ -498,15 +641,21 @@ void GFWWeights::mergeWeights(GFWWeights* other) } void GFWWeights::setTH3D(TH3D* th3d) { + if (!th3d) + return; if (!fW_data) { fW_data = new TObjArray(); fW_data->SetName("GFWWeights_Data"); fW_data->SetOwner(kTRUE); - fW_data->Add(th3d); + TH3D* hist = reinterpret_cast(th3d->Clone()); + hist->SetDirectory(0); + fW_data->Add(hist); return; } TString ts(fW_data->At(0)->GetName()); TH3D* trash = reinterpret_cast(fW_data->RemoveAt(0)); delete trash; - fW_data->Add(reinterpret_cast(th3d->Clone(ts.Data()))); + TH3D* hist = reinterpret_cast(th3d->Clone(ts.Data())); + hist->SetDirectory(0); + fW_data->AddAt(hist, 0); } diff --git a/PWGCF/GenericFramework/Core/GFWWeights.h b/PWGCF/GenericFramework/Core/GFWWeights.h index bcec7463647..bf255a731e5 100644 --- a/PWGCF/GenericFramework/Core/GFWWeights.h +++ b/PWGCF/GenericFramework/Core/GFWWeights.h @@ -31,6 +31,8 @@ class GFWWeights : public TNamed public: GFWWeights(); explicit GFWWeights(const char* name); + GFWWeights(const GFWWeights& other); + GFWWeights& operator=(const GFWWeights& other); ~GFWWeights(); void init(bool AddData = kTRUE, bool AddM = kTRUE); void fill(double phi, double eta, double vz, double pt, double cent, int htype, double weight = 1); // htype: 0 for data, 1 for mc rec, 2 for mc gen @@ -53,25 +55,25 @@ class GFWWeights : public TNamed void setMCFilled(bool newval) { fMCFilled = newval; } void readAndMerge(const TString& filelinks, const TString& listName = "OutputList", bool addData = kTRUE, bool addRec = kTRUE, bool addGen = kTRUE); void setPtBins(int Nbins, double* bins); - Long64_t Merge(TCollection* collist); + Long64_t Merge(TCollection* collist); // o2-linter: disable=root/entity,name/function-variable (ROOT Merge requires this signature) void rebinNUA(int nX = 1, int nY = 2, int nZ = 5); void overwriteNUA(); TH1D* getdNdPhi(); TH1D* getEfficiency(double etamin, double etamax, double vzmin, double vzmax); void mergeWeights(GFWWeights* other); - void setTH3D(TH3D* th3d); + void setTH3D(TH3D* th3d); // Stores a clone of the input histogram private: bool fDataFilled; bool fMCFilled; - TObjArray* fW_data; - TObjArray* fW_mcrec; - TObjArray* fW_mcgen; - TH3D* fEffInt; //! - TH1D* fIntEff; //! - TH3D* fAccInt; //! - int fNbinsPt; //! do not store - double* fbinsPt; //! do not store + TObjArray* fW_data; // o2-linter: disable=name/function-variable (persisted ROOT member name) + TObjArray* fW_mcrec; // o2-linter: disable=name/function-variable (persisted ROOT member name) + TObjArray* fW_mcgen; // o2-linter: disable=name/function-variable (persisted ROOT member name) + TH3D* fEffInt; //! + TH1D* fIntEff; //! + TH3D* fAccInt; //! + int fNbinsPt; //! do not store + double* fbinsPt; //! do not store void addArray(TObjArray* targ, TObjArray* sour); const char* getBinName(double /*ptv*/, double /*v0mv*/, const char* pf = "") { diff --git a/PWGCF/GenericFramework/Core/GenericFrameworkLinkDef.h b/PWGCF/GenericFramework/Core/GenericFrameworkLinkDef.h index 3fea9523064..a9a3da8c05e 100644 --- a/PWGCF/GenericFramework/Core/GenericFrameworkLinkDef.h +++ b/PWGCF/GenericFramework/Core/GenericFrameworkLinkDef.h @@ -9,8 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef PWGCF_GENERICFRAMEWORK_CORE_GENERICFRAMEWORKLINKDEF_H_ -#define PWGCF_GENERICFRAMEWORK_CORE_GENERICFRAMEWORKLINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -28,5 +27,7 @@ #pragma link C++ class o2::analysis::genericframework::GFWBinningCuts + ; #pragma link C++ class o2::analysis::genericframework::GFWRegions + ; #pragma link C++ class o2::analysis::genericframework::GFWCorrConfigs + ; - -#endif // PWGCF_GENERICFRAMEWORK_CORE_GENERICFRAMEWORKLINKDEF_H_ +#pragma link C++ class o2::analysis::genericframework::flowptcontainer::PtMomentGroup + ; +#pragma link C++ class o2::analysis::genericframework::flowptcontainer::PtMomentConfig + ; +#pragma link C++ class std::vector < o2::analysis::genericframework::flowptcontainer::PtMomentGroup> + ; +#pragma link C++ class std::vector < o2::analysis::genericframework::flowptcontainer::PtMomentConfig> + ; diff --git a/PWGCF/GenericFramework/DataModel/GfwDerived.h b/PWGCF/GenericFramework/DataModel/GfwDerived.h new file mode 100644 index 00000000000..9fc0edf7f5f --- /dev/null +++ b/PWGCF/GenericFramework/DataModel/GfwDerived.h @@ -0,0 +1,146 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file GfwDerived.h +/// \brief Derived collision and track tables for the GFW v02 flow analysis. +/// +/// Selection columns are bit masks. A later analysis keeps a row when every bit +/// set in its mask is also set on the row (`ncheckbit` / `(value & mask) == mask`). +/// PID is stored only as bits. The nominal, tight, and loose windows are fixed +/// at production. +/// +/// \author Maxim Virta, AU, maxim.virta@cern.ch + +#ifndef PWGCF_GENERICFRAMEWORK_DATAMODEL_GFWDERIVED_H_ +#define PWGCF_GENERICFRAMEWORK_DATAMODEL_GFWDERIVED_H_ + +#include +#include + +#include + +#include + +namespace o2::aod +{ +namespace gfwcollision +{ +DECLARE_SOA_COLUMN(Centrality, centrality, float); //! Centrality estimator chosen at production +DECLARE_SOA_COLUMN(Multiplicity, multiplicity, float); //! Global-track multiplicity, multNTracksGlobal +DECLARE_SOA_COLUMN(EventSel, eventSel, uint16_t); //! Event-selection bitmask, see EventSelection + +/// One bit per event-level decision. Bits record that the decision passed. +/// sel8 is required by the producer and is not stored here. +enum EventSelection : uint16_t { + kNoSameBunchPileup = BIT(0), + kIsGoodZvtxFT0vsPV = BIT(1), + kNoCollInTimeRangeStandard = BIT(2), + kNoCollInRofStandard = BIT(3), + kNoHighMultCollInPrevRof = BIT(4), + kIsVertexITSTPC = BIT(5), + kIsGoodITSLayersAll = BIT(6), + kNoTimeFrameBorder = BIT(7), + kNoITSROFrameBorder = BIT(8), + kOccupancy = BIT(9), //! track occupancy is within the production threshold + kNotTVXinTRD = BIT(10), //! collision is not a TRD-triggered TVX event + kGoodVertex = BIT(11), //! PV contributor and z-resolution quality + kMultCorr = BIT(12) //! multiplicity-correlation cut +}; + +inline constexpr uint16_t DefaultEventMask = 0; +} // namespace gfwcollision + +DECLARE_SOA_TABLE(GfwCollisions, "AOD", "GFWCOLLISION", //! + o2::soa::Index<>, + bc::RunNumber, + collision::PosZ, + gfwcollision::Centrality, + gfwcollision::Multiplicity, + timestamp::Timestamp, + gfwcollision::EventSel); +using GfwCollision = GfwCollisions::iterator; + +namespace gfwtrack +{ +DECLARE_SOA_INDEX_COLUMN(GfwCollision, gfwCollision); //! Parent derived collision +DECLARE_SOA_COLUMN(Pt, pt, float); //! pT (GeV/c) +DECLARE_SOA_COLUMN(Eta, eta, float); //! Pseudorapidity +DECLARE_SOA_COLUMN(Phi, phi, float); //! Azimuthal angle +DECLARE_SOA_COLUMN(HasTOF, hasTOF, bool); //! Track has a TOF match +DECLARE_SOA_COLUMN(TrackSel, trackSel, uint32_t); //! Track and PID bitmask, see TrackSelection + +/// Track-quality bits occupy 0-13. TPC PID bits occupy 14-22 and TOF PID bits occupy 23-31. +/// Global-track, ITS-cluster, and ITS-chi2 requirements are applied by the producer and are not stored. +/// DCA bits are |DCA| <= nSigma times a pT-dependent shape. +/// A PID bit is set when low * scale < nSigma < up * scale. +/// Above the TOF pT cut, every PID bit stays unset when the track has no TOF match. +/// Within each detector, PidWindowBits covers π/K/p, followed by the tight window and then the loose window. +enum TrackSelection : uint32_t { + kDCAxy = BIT(0), //! |DCAxy| within the nominal nSigma times f_xy(pT) + kDCAxyTight = BIT(1), + kDCAxyLoose = BIT(2), + kDCAz = BIT(3), //! |DCAz| within the nominal nSigma times f_z(pT) + kDCAzTight = BIT(4), + kDCAzLoose = BIT(5), // DCA bit selections + + kTPCXrows = BIT(6), + kTPCXrowsTight = BIT(7), + kTPCXrowsLoose = BIT(8), + kTPCCls = BIT(9), + kTPCClsTight = BIT(10), + kTPCClsLoose = BIT(11), + kChi2TPC = BIT(12), + kChi2TPCLoose = BIT(13), // TPC bit selections + + // TPC PID selections: nominal, tight, then loose + kTpcPi = BIT(14), + kTpcKa = BIT(15), + kTpcPr = BIT(16), + kTpcPiTight = BIT(17), + kTpcKaTight = BIT(18), + kTpcPrTight = BIT(19), + kTpcPiLoose = BIT(20), + kTpcKaLoose = BIT(21), + kTpcPrLoose = BIT(22), + // TOF PID selections: nominal, tight, then loose + kTofPi = BIT(23), + kTofKa = BIT(24), + kTofPr = BIT(25), + kTofPiTight = BIT(26), + kTofKaTight = BIT(27), + kTofPrTight = BIT(28), + kTofPiLoose = BIT(29), + kTofKaLoose = BIT(30), + kTofPrLoose = BIT(31) +}; + +/// Quality bits corresponding to the default flowGfwV02 track filter plus trackSelected(). +inline constexpr uint32_t NominalMask = static_cast(kDCAxy | kDCAz | kTPCXrows | kTPCCls | kChi2TPC); +/// Loosest quality bits. Production can optionally require these while still leaving the nominal cuts free. +inline constexpr uint32_t LooseSkimMask = static_cast(kDCAxyLoose | kDCAzLoose | kTPCXrowsLoose | kTPCClsLoose | kChi2TPCLoose); +/// Tracks that enter the multiplicity-correlation count. Global track and ITS chi2 are already required by the producer. +inline constexpr uint32_t MultCorrTrackMask = static_cast(kDCAz | kChi2TPC); + +inline constexpr int PidWindowBits = 3; +inline constexpr int PidWindowNominal = 0; +inline constexpr int PidWindowTight = 1; +inline constexpr int PidWindowLoose = 2; +} // namespace gfwtrack + +DECLARE_SOA_TABLE(GfwTracks, "AOD", "GFWTRACK", //! + o2::soa::Index<>, + gfwtrack::GfwCollisionId, + gfwtrack::Pt, gfwtrack::Eta, gfwtrack::Phi, + gfwtrack::HasTOF, gfwtrack::TrackSel); +using GfwTrack = GfwTracks::iterator; +} // namespace o2::aod + +#endif // PWGCF_GENERICFRAMEWORK_DATAMODEL_GFWDERIVED_H_ diff --git a/PWGCF/GenericFramework/TableProducer/CMakeLists.txt b/PWGCF/GenericFramework/TableProducer/CMakeLists.txt index bbfd7adac2b..ab012ec889e 100644 --- a/PWGCF/GenericFramework/TableProducer/CMakeLists.txt +++ b/PWGCF/GenericFramework/TableProducer/CMakeLists.txt @@ -8,3 +8,8 @@ # 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. + +o2physics_add_dpl_workflow(flow-gfw-derived-producer + SOURCES flowGfwDerivedProducer.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGCF/GenericFramework/TableProducer/flowGfwDerivedProducer.cxx b/PWGCF/GenericFramework/TableProducer/flowGfwDerivedProducer.cxx new file mode 100644 index 00000000000..1bfed843783 --- /dev/null +++ b/PWGCF/GenericFramework/TableProducer/flowGfwDerivedProducer.cxx @@ -0,0 +1,443 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file flowGfwDerivedProducer.cxx +/// \brief Produce the GFW derived dataset used by flowGfwV02. +/// +/// Keeps sel8 collisions and global tracks that pass the ITS cluster and ITS chi2 +/// cuts. Stores the event mask and the combined track and PID bitmask. PID bits +/// stay unset above the TOF pT cut when the track has no TOF match. Run it after +/// event selection, track selection, multiplicity, centrality, and the TPC/TOF PID +/// helpers (pid-tpc, pid-tof-full, pid-tof-beta). +/// +/// \author Maxim Virta, NBI, maxim.virta@cern.ch + +#include "PWGCF/GenericFramework/DataModel/GfwDerived.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/TriggerAliases.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +namespace +{ +constexpr float VertexResMax = 0.25f; +constexpr int VertexContribMin = 20; +constexpr float DcaShapeMinPt = 0.001f; +constexpr float DcaShapeMaxPt = 100.f; +constexpr float DcaZReferenceNsigma = 7.f; +constexpr float NsigmaScaleNominal = 1.f; +constexpr float NsigmaScaleTight = 2.f / 3.f; +constexpr float NsigmaScaleLoose = 4.f / 3.f; +constexpr uint32_t NsigmaRows = 6; +constexpr uint32_t NsigmaCols = 3; +constexpr int NsigmaUpPi = 0; +constexpr int NsigmaUpKa = 1; +constexpr int NsigmaUpPr = 2; +constexpr int NsigmaLowPi = 3; +constexpr int NsigmaLowKa = 4; +constexpr int NsigmaLowPr = 5; +constexpr int NsigmaColTpc = 0; +constexpr int NsigmaColTof = 1; +constexpr std::array, NsigmaRows> NsigmaDefault = {{{{1.1f, 2.1f, 3.1f}}, {{1.2f, 2.2f, 3.2f}}, {{1.3f, 2.3f, 3.3f}}, {{-1.1f, -2.1f, -3.1f}}, {{-1.2f, -2.2f, -3.2f}}, {{-1.3f, -2.3f, -3.3f}}}}; +} // namespace + +struct FlowGfwDerivedProducer { + using CollisionsWithInfo = soa::Join; + using TracksWithPid = soa::Join; + + enum CentEstimator : int { + CentFT0C = 0, + CentFT0CVariant1, + CentFT0M, + CentFV0A, + CentNTPV, + CentNGlobal, + CentMFT + }; + + Configurable cfgCutVertex{"cfgCutVertex", 10.f, "Maximum |z| of collisions written to the derived dataset (cm)"}; + Configurable cfgPtMin{"cfgPtMin", 0.2f, "Minimum track pT written to the derived dataset (GeV/c)"}; + Configurable cfgPtMax{"cfgPtMax", 10.f, "Maximum track pT written to the derived dataset (GeV/c)"}; + Configurable cfgEtaMax{"cfgEtaMax", 0.8f, "Maximum |eta| of tracks written to the derived dataset"}; + Configurable cfgSkipEmptyEvents{"cfgSkipEmptyEvents", true, "Do not write collisions that keep no tracks"}; + Configurable cfgRequireLooseTrackBits{"cfgRequireLooseTrackBits", false, "Keep only tracks that pass the loose DCA and TPC-crossed-rows bits"}; + Configurable cfgCentEstimator{"cfgCentEstimator", 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FV0A; 4:NTPV; 5:NGlobal; 6:MFT"}; + Configurable cfgOccupancyMax{"cfgOccupancyMax", 2000, "Maximum track occupancy for the occupancy bit; negative disables the cut"}; + + Configurable cfgDCAxyNSigma{"cfgDCAxyNSigma", 7.f, "Nominal pT-dependent DCAxy nSigma. Non-positive disables the nominal bit"}; + Configurable cfgDCAxyNSigmaTight{"cfgDCAxyNSigmaTight", 5.f, "Tight pT-dependent DCAxy nSigma"}; + Configurable cfgDCAxyNSigmaLoose{"cfgDCAxyNSigmaLoose", 10.f, "Loose pT-dependent DCAxy nSigma"}; + Configurable cfgDCAxy{"cfgDCAxy", "(0.0026+0.005/(x^1.01))", "1-sigma shape of the pT-dependent DCAxy cut; a bit passes when |DCAxy| <= nSigma*shape(pT)"}; + Configurable cfgDCAzNSigma{"cfgDCAzNSigma", 7.f, "Nominal DCAz nSigma relative to the 7-sigma pT shape. Non-positive disables the nominal bit"}; + Configurable cfgDCAzNSigmaTight{"cfgDCAzNSigmaTight", 5.f, "Tight DCAz nSigma"}; + Configurable cfgDCAzNSigmaLoose{"cfgDCAzNSigmaLoose", 10.f, "Loose DCAz nSigma"}; + Configurable cfgDCAz{"cfgDCAz", "(0.0105+0.0350/(x^1.1))", "7-sigma pT-dependent DCAz shape; a bit passes when |DCAz| <= (nSigma/7)*shape(pT)"}; + Configurable cfgNTPCXrows{"cfgNTPCXrows", 70.f, "Nominal minimum number of TPC crossed rows"}; + Configurable cfgNTPCXrowsTight{"cfgNTPCXrowsTight", 80.f, "Tight minimum number of TPC crossed rows"}; + Configurable cfgNTPCXrowsLoose{"cfgNTPCXrowsLoose", 60.f, "Loose minimum number of TPC crossed rows"}; + Configurable cfgNTPCCls{"cfgNTPCCls", 50.f, "Nominal minimum number of found TPC clusters"}; + Configurable cfgNTPCClsTight{"cfgNTPCClsTight", 60.f, "Tight minimum number of found TPC clusters"}; + Configurable cfgNTPCClsLoose{"cfgNTPCClsLoose", 40.f, "Loose minimum number of found TPC clusters"}; + Configurable cfgMinNITSCls{"cfgMinNITSCls", 5.f, "Minimum number of ITS clusters required to keep a track"}; + Configurable cfgChi2PrTPCCls{"cfgChi2PrTPCCls", 2.5f, "Nominal maximum chi2 per TPC cluster"}; + Configurable cfgChi2PrTPCClsLoose{"cfgChi2PrTPCClsLoose", 4.f, "Loose maximum chi2 per TPC cluster"}; + Configurable cfgChi2PrITSCls{"cfgChi2PrITSCls", 36.f, "Maximum chi2 per ITS cluster required to keep a track"}; + + Configurable> cfgMultGlobalCutPars{"cfgMultGlobalCutPars", std::vector{2272.16, -76.6932, 1.01204, -0.00631545, 1.59868e-05, 136.336, -4.97006, 0.121199, -0.0015921, 7.66197e-06}, "Global vs FT0C multiplicity cut parameters"}; + Configurable> cfgMultPVCutPars{"cfgMultPVCutPars", std::vector{3074.43, -106.192, 1.46176, -0.00968364, 2.61923e-05, 182.128, -7.43492, 0.193901, -0.00256715, 1.22594e-05}, "PV vs FT0C multiplicity cut parameters"}; + Configurable> cfgMultGlobalPVCutPars{"cfgMultGlobalPVCutPars", std::vector{-0.223013, 0.715849, 0.664242, 0.0829653, -0.000503733, 1.21185e-06}, "Global vs PV multiplicity cut parameters"}; + Configurable cfgMultCorrHighCutFunction{"cfgMultCorrHighCutFunction", "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Upper multiplicity-correlation cut"}; + Configurable cfgMultCorrLowCutFunction{"cfgMultCorrLowCutFunction", "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Lower multiplicity-correlation cut"}; + Configurable cfgMultGlobalPVCorrCutFunction{"cfgMultGlobalPVCorrCutFunction", "[0] + [1]*x + 3*([2] + [3]*x + [4]*x*x + [5]*x*x*x)", "Global vs PV multiplicity-correlation cut"}; + Configurable cfgTofPtCut{"cfgTofPtCut", 0.5f, "Above this pT, PID bits stay unset when the track has no TOF match"}; + Configurable> nSigmas{"nSigmas", {NsigmaDefault.front().data(), NsigmaRows, NsigmaCols, {"UpCut_pi", "UpCut_ka", "UpCut_pr", "LowCut_pi", "LowCut_ka", "LowCut_pr"}, {"TPC", "TOF", "ITS"}}, "Nominal nSigma windows for TPC, TOF, and ITS. ITS is accepted so the analysis JSON can be reused and is not stored as a bit"}; + Configurable cfgNsigmaScaleTight{"cfgNsigmaScaleTight", static_cast(NsigmaScaleTight), "Scale applied to the nominal nSigma bounds for the tight PID bits"}; + Configurable cfgNsigmaScaleLoose{"cfgNsigmaScaleLoose", static_cast(NsigmaScaleLoose), "Scale applied to the nominal nSigma bounds for the loose PID bits"}; + + Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; + Filter trackFilter = (aod::track::pt > cfgPtMin) && (aod::track::pt < cfgPtMax) && (nabs(aod::track::eta) < cfgEtaMax); + + Produces outputCollisions; + Produces outputTracks; + + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; + std::unique_ptr fDcaXyShape; + std::unique_ptr fDcaZShape; + std::array tpcNsigmaCut{}; + std::array tofNsigmaCut{}; + std::unique_ptr fMultPVCutLow; + std::unique_ptr fMultPVCutHigh; + std::unique_ptr fMultCutLow; + std::unique_ptr fMultCutHigh; + std::unique_ptr fMultPVGlobalCutHigh; + + void init(InitContext const&) + { + const AxisSpec axisCounter{1, 0., 1., ""}; + registry.add("events", "Written derived collisions", HistType::kTH1F, {axisCounter}); + registry.add("tracks", "Written derived tracks", HistType::kTH1F, {axisCounter}); + + const std::string dcaXyFormula = "[0]*" + cfgDCAxy.value; + fDcaXyShape = std::make_unique("gfwDerivedDcaXy", dcaXyFormula.c_str(), DcaShapeMinPt, DcaShapeMaxPt); + fDcaXyShape->SetParameter(0, 1.f); + fDcaZShape = std::make_unique("gfwDerivedDcaZ", cfgDCAz.value.c_str(), DcaShapeMinPt, DcaShapeMaxPt); + + const auto nsigma = nSigmas->getData(); + for (uint32_t row = 0; row < NsigmaRows; ++row) { + tpcNsigmaCut[row] = nsigma(row, NsigmaColTpc); + tofNsigmaCut[row] = nsigma(row, NsigmaColTof); + } + + constexpr double MultCorrMaxCent = 100.; + constexpr double MultCorrMaxNch = 3000.; + fMultPVCutLow = std::make_unique("gfwDerivedMultPVCutLow", cfgMultCorrLowCutFunction.value.c_str(), 0., MultCorrMaxCent); + fMultPVCutLow->SetParameters(cfgMultPVCutPars.value.data()); + fMultPVCutHigh = std::make_unique("gfwDerivedMultPVCutHigh", cfgMultCorrHighCutFunction.value.c_str(), 0., MultCorrMaxCent); + fMultPVCutHigh->SetParameters(cfgMultPVCutPars.value.data()); + fMultCutLow = std::make_unique("gfwDerivedMultCutLow", cfgMultCorrLowCutFunction.value.c_str(), 0., MultCorrMaxCent); + fMultCutLow->SetParameters(cfgMultGlobalCutPars.value.data()); + fMultCutHigh = std::make_unique("gfwDerivedMultCutHigh", cfgMultCorrHighCutFunction.value.c_str(), 0., MultCorrMaxCent); + fMultCutHigh->SetParameters(cfgMultGlobalCutPars.value.data()); + fMultPVGlobalCutHigh = std::make_unique("gfwDerivedMultPVGlobalCutHigh", cfgMultGlobalPVCorrCutFunction.value.c_str(), 0., MultCorrMaxNch); + fMultPVGlobalCutHigh->SetParameters(cfgMultGlobalPVCutPars.value.data()); + } + + template + float centrality(TCollision const& collision) const + { + switch (cfgCentEstimator.value) { + case CentFT0C: + return collision.centFT0C(); + case CentFT0CVariant1: + return collision.centFT0CVariant1(); + case CentFT0M: + return collision.centFT0M(); + case CentFV0A: + return collision.centFV0A(); + case CentNTPV: + return collision.centNTPV(); + case CentNGlobal: + return collision.centNGlobal(); + case CentMFT: + return collision.centMFT(); + default: + return collision.centFT0C(); + } + } + + template + bool vertexQuality(TCollision const& collision) const + { + if (collision.numContrib() <= 1) { + return false; + } + const float zRes = std::sqrt(collision.covZZ()); + return !(zRes > VertexResMax && collision.numContrib() < VertexContribMin); + } + + template + bool multiplicityCorrelated(TCollision const& collision, float cent, float multTrk) const + { + const float multPV = collision.multNTracksPV(); + if (multPV < fMultPVCutLow->Eval(cent) || multPV > fMultPVCutHigh->Eval(cent)) { + return false; + } + if (multTrk < fMultCutLow->Eval(cent) || multTrk > fMultCutHigh->Eval(cent)) { + return false; + } + return multTrk <= fMultPVGlobalCutHigh->Eval(multPV); + } + + template + uint16_t packEventSel(TCollision const& collision, float cent, float multTrk) const + { + uint16_t bits = 0; + if (collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + bits |= aod::gfwcollision::kNoSameBunchPileup; + } + if (collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { + bits |= aod::gfwcollision::kIsGoodZvtxFT0vsPV; + } + if (collision.selection_bit(aod::evsel::kNoCollInTimeRangeStandard)) { + bits |= aod::gfwcollision::kNoCollInTimeRangeStandard; + } + if (collision.selection_bit(aod::evsel::kNoCollInRofStandard)) { + bits |= aod::gfwcollision::kNoCollInRofStandard; + } + if (collision.selection_bit(aod::evsel::kNoHighMultCollInPrevRof)) { + bits |= aod::gfwcollision::kNoHighMultCollInPrevRof; + } + if (collision.selection_bit(aod::evsel::kIsVertexITSTPC)) { + bits |= aod::gfwcollision::kIsVertexITSTPC; + } + if (collision.selection_bit(aod::evsel::kIsGoodITSLayersAll)) { + bits |= aod::gfwcollision::kIsGoodITSLayersAll; + } + if (collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + bits |= aod::gfwcollision::kNoTimeFrameBorder; + } + if (collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + bits |= aod::gfwcollision::kNoITSROFrameBorder; + } + if (cfgOccupancyMax.value < 0 || collision.trackOccupancyInTimeRange() <= cfgOccupancyMax.value) { + bits |= aod::gfwcollision::kOccupancy; + } + if (!collision.alias_bit(kTVXinTRD)) { + bits |= aod::gfwcollision::kNotTVXinTRD; + } + if (vertexQuality(collision)) { + bits |= aod::gfwcollision::kGoodVertex; + } + if (multiplicityCorrelated(collision, cent, multTrk)) { + bits |= aod::gfwcollision::kMultCorr; + } + return bits; + } + + template + bool withinDcaXy(TTrack const& track, float nSigma) const + { + return std::abs(track.dcaXY()) <= nSigma * fDcaXyShape->Eval(track.pt()); + } + + template + bool withinDcaZ(TTrack const& track, float nSigma) const + { + return std::abs(track.dcaZ()) <= (nSigma / DcaZReferenceNsigma) * fDcaZShape->Eval(track.pt()); + } + + template + bool passProducerTrackCuts(TTrack const& track) const + { + const bool global = track.isGlobalTrack() || track.isGlobalTrackSDD() != 0; + return global && track.itsNCls() >= cfgMinNITSCls.value && track.itsChi2NCl() <= cfgChi2PrITSCls.value; + } + + template + uint32_t packTrackSel(TTrack const& track) const + { + uint32_t bits = 0; + if (cfgDCAxyNSigma.value <= 0.f || withinDcaXy(track, cfgDCAxyNSigma.value)) { + bits |= aod::gfwtrack::kDCAxy; + } + if (withinDcaXy(track, cfgDCAxyNSigmaTight.value)) { + bits |= aod::gfwtrack::kDCAxyTight; + } + if (withinDcaXy(track, cfgDCAxyNSigmaLoose.value)) { + bits |= aod::gfwtrack::kDCAxyLoose; + } + if (cfgDCAzNSigma.value <= 0.f || withinDcaZ(track, cfgDCAzNSigma.value)) { + bits |= aod::gfwtrack::kDCAz; + } + if (withinDcaZ(track, cfgDCAzNSigmaTight.value)) { + bits |= aod::gfwtrack::kDCAzTight; + } + if (withinDcaZ(track, cfgDCAzNSigmaLoose.value)) { + bits |= aod::gfwtrack::kDCAzLoose; + } + if (track.tpcNClsCrossedRows() >= cfgNTPCXrows.value) { + bits |= aod::gfwtrack::kTPCXrows; + } + if (track.tpcNClsCrossedRows() >= cfgNTPCXrowsTight.value) { + bits |= aod::gfwtrack::kTPCXrowsTight; + } + if (track.tpcNClsCrossedRows() >= cfgNTPCXrowsLoose.value) { + bits |= aod::gfwtrack::kTPCXrowsLoose; + } + if (track.tpcNClsFound() >= cfgNTPCCls.value) { + bits |= aod::gfwtrack::kTPCCls; + } + if (track.tpcNClsFound() >= cfgNTPCClsTight.value) { + bits |= aod::gfwtrack::kTPCClsTight; + } + if (track.tpcNClsFound() >= cfgNTPCClsLoose.value) { + bits |= aod::gfwtrack::kTPCClsLoose; + } + if (track.tpcChi2NCl() <= cfgChi2PrTPCCls.value) { + bits |= aod::gfwtrack::kChi2TPC; + } + if (track.tpcChi2NCl() <= cfgChi2PrTPCClsLoose.value) { + bits |= aod::gfwtrack::kChi2TPCLoose; + } + return bits; + } + + static bool insideNsigma(float nsigma, float up, float low, float scale) + { + return nsigma < up * scale && nsigma > low * scale; + } + + template + uint32_t packPidWindow(TTrack const& track, float scale) const + { + if (track.pt() > cfgTofPtCut.value && !track.hasTOF()) { + return 0; + } + uint32_t bits = 0; + if (insideNsigma(track.tpcNSigmaPi(), tpcNsigmaCut[NsigmaUpPi], tpcNsigmaCut[NsigmaLowPi], scale)) { + bits |= aod::gfwtrack::kTpcPi; + } + if (insideNsigma(track.tpcNSigmaKa(), tpcNsigmaCut[NsigmaUpKa], tpcNsigmaCut[NsigmaLowKa], scale)) { + bits |= aod::gfwtrack::kTpcKa; + } + if (insideNsigma(track.tpcNSigmaPr(), tpcNsigmaCut[NsigmaUpPr], tpcNsigmaCut[NsigmaLowPr], scale)) { + bits |= aod::gfwtrack::kTpcPr; + } + if (insideNsigma(track.tofNSigmaPi(), tofNsigmaCut[NsigmaUpPi], tofNsigmaCut[NsigmaLowPi], scale)) { + bits |= aod::gfwtrack::kTofPi; + } + if (insideNsigma(track.tofNSigmaKa(), tofNsigmaCut[NsigmaUpKa], tofNsigmaCut[NsigmaLowKa], scale)) { + bits |= aod::gfwtrack::kTofKa; + } + if (insideNsigma(track.tofNSigmaPr(), tofNsigmaCut[NsigmaUpPr], tofNsigmaCut[NsigmaLowPr], scale)) { + bits |= aod::gfwtrack::kTofPr; + } + return bits; + } + + template + uint32_t packPidSel(TTrack const& track) const + { + const uint32_t nominal = packPidWindow(track, NsigmaScaleNominal); + const uint32_t tight = packPidWindow(track, cfgNsigmaScaleTight.value); + const uint32_t loose = packPidWindow(track, cfgNsigmaScaleLoose.value); + // Nominal TPC bits start at bit 14 and nominal TOF bits at bit 23. Shifting by one window moves each detector into its tight or loose block. + return nominal | (tight << (aod::gfwtrack::PidWindowTight * aod::gfwtrack::PidWindowBits)) | (loose << (aod::gfwtrack::PidWindowLoose * aod::gfwtrack::PidWindowBits)); + } + + template + void fillTrack(TTrack const& track, uint32_t bits) + { + outputTracks(outputCollisions.lastIndex(), track.pt(), track.eta(), track.phi(), + track.hasTOF(), bits | packPidSel(track)); + registry.fill(HIST("tracks"), 0.5); + } + + void process(soa::Filtered::iterator const& collision, aod::BCsWithTimestamps const&, soa::Filtered const& tracks) + { + if (!collision.sel8()) { + return; + } + + int nKept = 0; + int nForMultCorr = 0; + for (const auto& track : tracks) { + if (!passProducerTrackCuts(track)) { + continue; + } + const uint32_t bits = packTrackSel(track); + const bool passLoose = !cfgRequireLooseTrackBits || ((bits & aod::gfwtrack::LooseSkimMask) == aod::gfwtrack::LooseSkimMask); + if (!passLoose) { + continue; + } + ++nKept; + if ((bits & aod::gfwtrack::MultCorrTrackMask) == aod::gfwtrack::MultCorrTrackMask) { + ++nForMultCorr; + } + } + if (nKept == 0 && cfgSkipEmptyEvents) { + return; + } + + const auto bc = collision.bc_as(); + const float cent = centrality(collision); + outputCollisions(bc.runNumber(), collision.posZ(), cent, static_cast(collision.multNTracksGlobal()), bc.timestamp(), packEventSel(collision, cent, static_cast(nForMultCorr))); + registry.fill(HIST("events"), 0.5); + + for (const auto& track : tracks) { + if (!passProducerTrackCuts(track)) { + continue; + } + const uint32_t bits = packTrackSel(track); + const bool passLoose = !cfgRequireLooseTrackBits || ((bits & aod::gfwtrack::LooseSkimMask) == aod::gfwtrack::LooseSkimMask); + if (!passLoose) { + continue; + } + fillTrack(track, bits); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + }; +} diff --git a/PWGCF/GenericFramework/Tasks/CMakeLists.txt b/PWGCF/GenericFramework/Tasks/CMakeLists.txt index 73a9f7a5396..90b65bdba93 100644 --- a/PWGCF/GenericFramework/Tasks/CMakeLists.txt +++ b/PWGCF/GenericFramework/Tasks/CMakeLists.txt @@ -28,3 +28,8 @@ o2physics_add_dpl_workflow(flow-gfw-nonflow SOURCES flowGfwNonflow.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::GFWCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(energy-flow-gfw + SOURCES energyFlowGfw.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCCDB O2Physics::AnalysisCore O2Physics::GFWCore + COMPONENT_NAME Analysis) diff --git a/PWGCF/GenericFramework/Tasks/energyFlowGfw.cxx b/PWGCF/GenericFramework/Tasks/energyFlowGfw.cxx new file mode 100644 index 00000000000..41fdfa963ec --- /dev/null +++ b/PWGCF/GenericFramework/Tasks/energyFlowGfw.cxx @@ -0,0 +1,1033 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file enegyFlowGfw.cxx +/// \brief Charged-particle transverse-energy correlators in Xe-Xe and Pb-Pb collisions +/// \author Emil Gorm Dahlbæk Nielsen + +#include "PWGCF/GenericFramework/Core/FlowContainer.h" +#include "PWGCF/GenericFramework/Core/GFW.h" +#include "PWGCF/GenericFramework/Core/GFWConfig.h" +#include "PWGCF/GenericFramework/Core/GFWWeights.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/TriggerAliases.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::analysis::genericframework; + +struct EnergyFlowGfw { + enum EnergyWeightMode : int { + PtWeight = 0, + MtWeight, + FullEtWeight + }; + + enum ParticleId : int { + Unidentified = 0, + Pion, + Kaon, + Proton + }; + + enum ActivityAxisMode : int { + CentralityAxis = 0, + NchAxis, + ZdcSpectatorAxis + }; + + enum AodSelectionStep : uint8_t { + AllAodEvents = 0, + TriggerSelected, + CentralitySelected, + HasValidZdc, + SpectatorSelected + }; + + enum ZdcQaStep : uint8_t { + MatchedZdc = 0, + FiniteCommonEnergy, + NonnegativeCommonEnergy, + HasZnTiming, + HasZpTiming, + HasAllTiming + }; + + enum EfficiencyStep : uint8_t { + EfficiencyGenerated = 0, + EfficiencyReconstructed, + EfficiencyStepCount + }; + + enum EfficiencySpecies : uint8_t { + EfficiencyCharged = 0, + EfficiencyPion, + EfficiencyKaon, + EfficiencyProton, + EfficiencySpeciesCount + }; + + struct : ConfigurableGroup { + Configurable vertexZ{"vertexZ", 10.f, "Accepted |z-vertex| (cm)"}; + Configurable> ptCuts{"ptCuts", {0.2f, 5.f}, "Track pT cuts (GeV/c)"}; + Configurable etaMax{"etaMax", 0.8f, "Maximum absolute track pseudorapidity"}; + } cuts; + + struct : ConfigurableGroup { + Configurable weightMode{"weightMode", FullEtWeight, "Energy weight: 0=pT, 1=mT, 2=E/cosh(eta)"}; + Configurable nSigmaCut{"nSigmaCut", 3.f, "TPC or combined TPC+TOF PID selection in n-sigma"}; + Configurable tofPtCut{"tofPtCut", 0.5f, "Require TOF PID above this pT (GeV/c)"}; + Configurable strictPid{"strictPid", true, "Mark tracks compatible with multiple species as unidentified"}; + Configurable unidentifiedMass{"unidentifiedMass", o2::constants::physics::MassPionCharged, "Mass assigned to unidentified charged tracks (GeV/c^2)"}; + } energy; + + Configurable cfgRegions{"cfgRegions", {{"refA", "refB"}, {-0.8, 0.4}, {-0.4, 0.8}, {0, 0}, {1, 1}}, "GFW region definitions"}; + Configurable cfgCorrConfig{"cfgCorrConfig", {{"refA {0}", "refB {0}", "refA {0} refB {0}", "refA {1} refB {-1}", "refA {2} refB {-2}", "refA {3} refB {-3}", "refA {4} refB {-4}", "refA {5} refB {-5}", "refA {6} refB {-6}"}, {"F0A", "F0B", "F0AF0B", "EECGap12", "EECGap22", "EECGap32", "EECGap42", "EECGap52", "EECGap62"}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}}, "GFW correlations to calculate"}; + Configurable cfgBootstrapSamples{"cfgBootstrapSamples", 10, "Number of FlowContainer bootstrap subsamples; zero disables bootstrapping"}; + Configurable cfgActivityAxis{"cfgActivityAxis", CentralityAxis, "FlowContainer x-axis: 0=centrality, 1=number of filtered global tracks, 2=corrected ZDC spectator estimate"}; + Configurable cfgMaxCentrality{"cfgMaxCentrality", 100.f, "Maximum accepted centrality percentile"}; + Configurable cfgFillEfficiency{"fillEfficiency", false, "Fill MC tracking efficiency; ignored by data processes"}; + Configurable cfgFillAcceptance{"fillAcceptance", false, "Fill GFW acceptance weights instead of running the reconstructed analysis"}; + Configurable cfgUseRun2EventSelectionWithoutZdc{"cfgUseRun2EventSelectionWithoutZdc", false, "For Run 2 data, require kINT7 and every CCDB-enabled barrel sel7 criterion except the ZDC beam-beam criteria; retain the ZDC beam-gas vetoes"}; + + Configurable cfgSpectatorEnergyPerNucleon{"cfgSpectatorEnergyPerNucleon", 1.f, "ZDC energy corresponding to one spectator nucleon"}; + Configurable cfgMaxTotalSpectators{"cfgMaxTotalSpectators", 500.f, "Maximum accepted spectator count from ZNA+ZNC+ZPA+ZPC"}; + Configurable cfgZdcResponseZNA{"cfgZdcResponseZNA", 1.f, "ZNA efficiency/acceptance response factor alpha; corrected energy is E_ZNA/alpha_ZNA"}; + Configurable cfgZdcResponseZNC{"cfgZdcResponseZNC", 1.f, "ZNC efficiency/acceptance response factor alpha; corrected energy is E_ZNC/alpha_ZNC"}; + Configurable cfgZdcResponseZPA{"cfgZdcResponseZPA", 1.f, "ZPA efficiency/acceptance response factor alpha; corrected energy is E_ZPA/alpha_ZPA"}; + Configurable cfgZdcResponseZPC{"cfgZdcResponseZPC", 1.f, "ZPC efficiency/acceptance response factor alpha; corrected energy is E_ZPC/alpha_ZPC"}; + Configurable cfgZdcMaxAbsTimeForQa{"cfgZdcMaxAbsTimeForQa", 100.f, "Maximum absolute ZDC time considered available in QA; excludes missing-time sentinels"}; + + struct : ConfigurableGroup { + Configurable cfgEfficiencyPath{"cfgEfficiencyPath", "", "CCDB path to efficiency object"}; + Configurable cfgUsePIDEfficiencies{"cfgUsePIDEfficiencies", false, "Use separate charged, pion, kaon, and proton efficiencies"}; + } cfgEfficiency; + Configurable acceptancePath{"acceptancePath", "", "CCDB path to GFW acceptance weights"}; + Configurable acceptanceRunByRun{"acceptanceRunByRun", false, "Load acceptance weights from the RunByRun subdirectory"}; + + ConfigurableAxis axisVertex{"axisVertex", {10, -10., 10.}, "z-vertex (cm)"}; + ConfigurableAxis axisCentrality{"axisCentrality", {VARIABLE_WIDTH, 0., 5., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100.1}, "centrality"}; + ConfigurableAxis axisNGlobalTracks{"axisNGlobalTracks", {500, 0., 5000.}, "number of filtered global tracks"}; + ConfigurableAxis axisPt{"axisPt", {100, 0., 5.}, "pT (GeV/c)"}; + ConfigurableAxis axisEnergy{"axisEnergy", {200, 0., 20.}, "transverse-energy weight (GeV)"}; + ConfigurableAxis axisTotalSpectators{"axisTotalSpectators", {500, 0., 500.}, "total spectator count"}; + ConfigurableAxis axisSpectatorActivity{"axisSpectatorActivity", {500, 0., 500.}, "spectator count used by the FlowContainer"}; + ConfigurableAxis axisZdcNeutronEnergy{"axisZdcNeutronEnergy", {410, -2000., 80000.}, "ZDC neutron common energy"}; + ConfigurableAxis axisZdcProtonEnergy{"axisZdcProtonEnergy", {310, -1000., 30000.}, "ZDC proton common energy"}; + ConfigurableAxis axisZdcTotalEnergy{"axisZdcTotalEnergy", {410, -5000., 200000.}, "summed ZDC common energy"}; + ConfigurableAxis axisZdcZemEnergy{"axisZdcZemEnergy", {210, -100., 2000.}, "summed ZEM energy"}; + ConfigurableAxis axisZdcTime{"axisZdcTime", {600, -15., 15.}, "ZDC time (ns)"}; + ConfigurableAxis axisZdcTimeCombination{"axisZdcTimeCombination", {400, -20., 20.}, "ZDC time sum or difference (ns)"}; + ConfigurableAxis axisZdcTrackMultiplicity{"axisZdcTrackMultiplicity", {400, 0., 4000.}, "number of filtered global tracks"}; + + Filter collisionFilter = nabs(aod::collision::posZ) < cuts.vertexZ; + Filter trackFilter = (nabs(aod::track::eta) < cuts.etaMax) && (aod::track::pt > cuts.ptCuts->first) && (aod::track::pt < cuts.ptCuts->second) && + ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == static_cast(true))); + + using CollisionsRun2 = soa::Filtered>; + using CollisionsRun2MC = soa::Join; + using CollisionsRun3 = soa::Filtered>; + using CollisionsRun3MC = soa::Join; + using Tracks = soa::Filtered>; + using TracksMC = soa::Filtered>; + using BCsRun2 = soa::Join; + using BCsRun3 = soa::Join; + + Preslice tracksPerCollision = aod::track::collisionId; + + OutputObj flowContainer{"FlowContainer"}; + OutputObj acceptanceWeights{GFWWeights("weights")}; + HistogramRegistry registry{"registry"}; + Service ccdb{}; + Service pdgDB{}; + + std::unique_ptr gfw{std::make_unique()}; + std::unique_ptr random{std::make_unique(0)}; + std::vector corrConfigs; + std::vector zeroHarmonicConfigs; + int gfwMask = 0; + + TH1* efficiency = nullptr; + std::vector pidEfficiencies; + bool efficiencyLoaded = false; + GFWWeights* acceptance = nullptr; + int acceptanceRunNumber = -1; + + void init(InitContext const&) + { + const int enabledProcesses = static_cast(doprocessRun2) + static_cast(doprocessRun2MC) + static_cast(doprocessRun3) + static_cast(doprocessRun3MC); + if (enabledProcesses > 1) { + LOGF(fatal, "Enable only one input process: Run 2 data, Run 2 MC, Run 3 data, or Run 3 MC"); + } + if (cuts.ptCuts->first >= cuts.ptCuts->second || cuts.etaMax <= 0.f) { + LOGF(fatal, "Require ptMin < ptMax and etaMax > 0"); + } + if (energy.weightMode < PtWeight || energy.weightMode > FullEtWeight) { + LOGF(fatal, "weightMode must be 0 (pT), 1 (mT), or 2 (full ET)"); + } + if (energy.nSigmaCut <= 0.f || energy.tofPtCut < 0.f || energy.unidentifiedMass < 0.f) { + LOGF(fatal, "PID n-sigma and mass settings must be non-negative, with nSigmaCut > 0"); + } + if (cfgBootstrapSamples < 0) { + LOGF(fatal, "cfgBootstrapSamples must not be negative"); + } + if (cfgActivityAxis < CentralityAxis || cfgActivityAxis > ZdcSpectatorAxis) { + LOGF(fatal, "cfgActivityAxis must be 0 (centrality), 1 (filtered global tracks), or 2 (corrected ZDC spectators)"); + } + if (!std::isfinite(cfgMaxCentrality.value) || cfgMaxCentrality < 0.f || cfgSpectatorEnergyPerNucleon <= 0.f || cfgMaxTotalSpectators < 0.f || cfgZdcResponseZNA <= 0.f || cfgZdcResponseZNC <= 0.f || cfgZdcResponseZPA <= 0.f || cfgZdcResponseZPC <= 0.f || cfgZdcMaxAbsTimeForQa <= 0.f) { + LOGF(fatal, "The maximum centrality must be finite and non-negative, the spectator energy scale, ZDC response factors, and ZDC QA time range must be positive, and the spectator threshold non-negative"); + } + if (cfgMaxCentrality > 100.f) { + LOGF(warning, "cfgMaxCentrality is %.1f (> 100); dummy centrality values will be accepted. Use the Nch or spectator activity axis when calibrated centrality is unavailable", cfgMaxCentrality.value); + } + + const int numberOfRegions = cfgRegions->GetSize(); + if (numberOfRegions <= 0 || cfgCorrConfig->GetSize() <= 0) { + LOGF(fatal, "GFW regions and correlations must be non-empty and internally consistent"); + } + + float largestRegionEta = 0.f; + for (int index = 0; index < numberOfRegions; ++index) { + const float etaMin = cfgRegions->GetEtaMin()[index]; + const float etaMax = cfgRegions->GetEtaMax()[index]; + if (!std::isfinite(etaMin) || !std::isfinite(etaMax) || etaMin >= etaMax) { + LOGF(fatal, "GFW region %s must have finite etaMin < etaMax", cfgRegions->GetNames()[index].c_str()); + } + if (cfgRegions->GetpTDifs()[index] != 0 || cfgRegions->GetBitmasks()[index] == 0) { + LOGF(fatal, "This task supports only pT-integrated regions with non-zero bit masks"); + } + largestRegionEta = std::max({largestRegionEta, std::abs(etaMin), std::abs(etaMax)}); + gfwMask |= cfgRegions->GetBitmasks()[index]; + gfw->AddRegion(cfgRegions->GetNames()[index], etaMin, etaMax, 1, cfgRegions->GetBitmasks()[index]); + } + if (largestRegionEta > cuts.etaMax) { + LOGF(fatal, "All GFW regions must be contained within the selected track eta range"); + } + + auto profileNames = std::make_unique(); + profileNames->SetOwner(true); + corrConfigs.reserve(cfgCorrConfig->GetSize()); + zeroHarmonicConfigs.reserve(cfgCorrConfig->GetSize()); + for (int index = 0; index < cfgCorrConfig->GetSize(); ++index) { + if (cfgCorrConfig->GetpTDifs()[index] != 0) { + LOGF(fatal, "This task supports only pT-integrated GFW correlations"); + } + auto config = gfw->GetCorrelatorConfig(cfgCorrConfig->GetCorrs()[index], cfgCorrConfig->GetHeads()[index], false); + if (config.Regs.empty() || config.Head.empty()) { + LOGF(fatal, "Invalid GFW correlation configuration at index %d", index); + } + const bool zeroHarmonic = std::all_of(config.Hars.begin(), config.Hars.end(), [](const auto& harmonics) { + return std::all_of(harmonics.begin(), harmonics.end(), [](int harmonic) { return harmonic == 0; }); + }); + zeroHarmonicConfigs.push_back(zeroHarmonic); + if (!zeroHarmonic) { + profileNames->Add(new TNamed(config.Head.c_str(), config.Head.c_str())); + } + const std::string rawName = config.Head + "_raw"; + profileNames->Add(new TNamed(rawName.c_str(), rawName.c_str())); + corrConfigs.push_back(std::move(config)); + } + gfw->CreateRegions(); + flowContainer.setObject(new FlowContainer("FlowContainer")); + const AxisSpec activityAxis = [this]() { + if (cfgActivityAxis.value == NchAxis) { + return AxisSpec{axisNGlobalTracks, "N_{global tracks}"}; + } + if (cfgActivityAxis.value == ZdcSpectatorAxis) { + return AxisSpec{axisSpectatorActivity, "N_{spectators}"}; + } + return AxisSpec{axisCentrality, "centrality (%)"}; + }(); + if (activityAxis.nBins.has_value()) { + flowContainer->Initialize(profileNames.get(), activityAxis.nBins.value(), activityAxis.binEdges.front(), activityAxis.binEdges.back(), cfgBootstrapSamples); + } else { + flowContainer->Initialize(profileNames.get(), activityAxis, cfgBootstrapSamples); + } + if (cfgFillAcceptance) { + acceptanceWeights->init(true, false); + } + + const AxisSpec etaAxis{32, -cuts.etaMax.value, cuts.etaMax.value, "#eta"}; + registry.add("event/centrality", "Accepted events;centrality;events", HistType::kTH1F, {axisCentrality}); + registry.add("event/activity", "Accepted events;event activity;events", HistType::kTH1F, {activityAxis}); + registry.add("event/vertex", "Accepted events;z-vtx (cm);events", HistType::kTH1F, {axisVertex}); + registry.add("event/aodSelection", "AOD event selection;selection;events", HistType::kTH1F, {{5, -0.5, 4.5}}); + registry.add("event/totalSpectators", "ZDC spectator estimate;N_{spectators};events", HistType::kTH1F, {axisTotalSpectators}); + registry.add("track/ptEta", "Selected tracks;p_{T} (GeV/c);#eta", HistType::kTH2F, {axisPt, etaAxis}); + registry.add("track/energy", "Energy entering the GFW;E_{T} (GeV);tracks", HistType::kTH1F, {axisEnergy}); + registry.add("track/pid", "PID mass assignment;species;tracks", HistType::kTH1F, {{4, -0.5, 3.5}}); + + if (cfgFillEfficiency && (doprocessRun2MC || doprocessRun3MC)) { + const AxisSpec efficiencySpeciesAxis{EfficiencySpeciesCount, -0.5, static_cast(EfficiencySpeciesCount) - 0.5, "particle"}; + const AxisSpec efficiencyStepAxis{EfficiencyStepCount, -0.5, static_cast(EfficiencyStepCount) - 0.5, "generated/reconstructed"}; + registry.add("Efficiency/efficiencyHist", "MC tracking efficiency;#it{p}_{T}^{MC};centrality (%);particle;step", HistType::kTHnSparseF, {axisPt, axisCentrality, efficiencySpeciesAxis, efficiencyStepAxis}); + auto efficiencyHistogram = registry.get(HIST("Efficiency/efficiencyHist")); + efficiencyHistogram->GetAxis(2)->SetBinLabel(EfficiencyCharged + 1, "charged"); + efficiencyHistogram->GetAxis(2)->SetBinLabel(EfficiencyPion + 1, "pion"); + efficiencyHistogram->GetAxis(2)->SetBinLabel(EfficiencyKaon + 1, "kaon"); + efficiencyHistogram->GetAxis(2)->SetBinLabel(EfficiencyProton + 1, "proton"); + efficiencyHistogram->GetAxis(3)->SetBinLabel(EfficiencyGenerated + 1, "generated"); + efficiencyHistogram->GetAxis(3)->SetBinLabel(EfficiencyReconstructed + 1, "reconstructed"); + } + + registry.add("zdc/qaSelection", "ZDC QA availability;condition;events", HistType::kTH1F, {{6, -0.5, 5.5}}); + registry.add("zdc/energyZNA", "Triggered events with matched ZDC;E_{ZNA};events", HistType::kTH1F, {axisZdcNeutronEnergy}); + registry.add("zdc/energyZNC", "Triggered events with matched ZDC;E_{ZNC};events", HistType::kTH1F, {axisZdcNeutronEnergy}); + registry.add("zdc/energyZPA", "Triggered events with matched ZDC;E_{ZPA};events", HistType::kTH1F, {axisZdcProtonEnergy}); + registry.add("zdc/energyZPC", "Triggered events with matched ZDC;E_{ZPC};events", HistType::kTH1F, {axisZdcProtonEnergy}); + registry.add("zdc/energyZEM", "Triggered events with matched ZDC;E_{ZEM1}+E_{ZEM2};events", HistType::kTH1F, {axisZdcZemEnergy}); + registry.add("zdc/timeZNA", "Triggered events with matched ZDC;t_{ZNA} (ns);events", HistType::kTH1F, {axisZdcTime}); + registry.add("zdc/timeZNC", "Triggered events with matched ZDC;t_{ZNC} (ns);events", HistType::kTH1F, {axisZdcTime}); + registry.add("zdc/timeZPA", "Triggered events with matched ZDC;t_{ZPA} (ns);events", HistType::kTH1F, {axisZdcTime}); + registry.add("zdc/timeZPC", "Triggered events with matched ZDC;t_{ZPC} (ns);events", HistType::kTH1F, {axisZdcTime}); + registry.add("zdc/timeZnSumVsDiff", "Triggered events with ZNA and ZNC timing;t_{ZNA}-t_{ZNC} (ns);t_{ZNA}+t_{ZNC} (ns)", HistType::kTH2F, {axisZdcTimeCombination, axisZdcTimeCombination}); + registry.add("zdc/energyZnaVsZnc", "Triggered events with finite ZN energies;E_{ZNC};E_{ZNA}", HistType::kTH2F, {axisZdcNeutronEnergy, axisZdcNeutronEnergy}); + registry.add("zdc/energyZpaVsZpc", "Triggered events with finite ZP energies;E_{ZPC};E_{ZPA}", HistType::kTH2F, {axisZdcProtonEnergy, axisZdcProtonEnergy}); + registry.add("zdc/energyZnVsZem", "Triggered events with finite ZN and ZEM energies;E_{ZEM1}+E_{ZEM2};E_{ZNA}+E_{ZNC}", HistType::kTH2F, {axisZdcZemEnergy, axisZdcTotalEnergy}); + registry.add("zdc/energyZnaVsTime", "Triggered events with matched ZDC;t_{ZNA} (ns);E_{ZNA}", HistType::kTH2F, {axisZdcTime, axisZdcNeutronEnergy}); + registry.add("zdc/energyZncVsTime", "Triggered events with matched ZDC;t_{ZNC} (ns);E_{ZNC}", HistType::kTH2F, {axisZdcTime, axisZdcNeutronEnergy}); + registry.add("zdc/energyZpaVsTime", "Triggered events with matched ZDC;t_{ZPA} (ns);E_{ZPA}", HistType::kTH2F, {axisZdcTime, axisZdcProtonEnergy}); + registry.add("zdc/energyZpcVsTime", "Triggered events with matched ZDC;t_{ZPC} (ns);E_{ZPC}", HistType::kTH2F, {axisZdcTime, axisZdcProtonEnergy}); + registry.add("zdc/commonVsSectorZNA", "Triggered events with finite ZNA energies;E_{ZNA}^{common};#Sigma E_{ZNA}^{sectors}", HistType::kTH2F, {axisZdcNeutronEnergy, axisZdcNeutronEnergy}); + registry.add("zdc/commonVsSectorZNC", "Triggered events with finite ZNC energies;E_{ZNC}^{common};#Sigma E_{ZNC}^{sectors}", HistType::kTH2F, {axisZdcNeutronEnergy, axisZdcNeutronEnergy}); + registry.add("zdc/commonVsSectorZPA", "Triggered events with finite ZPA energies;E_{ZPA}^{common};#Sigma E_{ZPA}^{sectors}", HistType::kTH2F, {axisZdcProtonEnergy, axisZdcProtonEnergy}); + registry.add("zdc/commonVsSectorZPC", "Triggered events with finite ZPC energies;E_{ZPC}^{common};#Sigma E_{ZPC}^{sectors}", HistType::kTH2F, {axisZdcProtonEnergy, axisZdcProtonEnergy}); + registry.add("zdc/energyZnVsNGlobalTracks", "Triggered events with finite ZN energies;N_{global tracks};E_{ZNA}+E_{ZNC}", HistType::kTH2F, {axisZdcTrackMultiplicity, axisZdcTotalEnergy}); + registry.add("zdc/energyZpVsNGlobalTracks", "Triggered events with finite ZP energies;N_{global tracks};E_{ZPA}+E_{ZPC}", HistType::kTH2F, {axisZdcTrackMultiplicity, axisZdcTotalEnergy}); + registry.add("zdc/energyTotalVsNGlobalTracks", "Triggered events with finite common energies;N_{global tracks};#Sigma E_{ZDC}^{common}", HistType::kTH2F, {axisZdcTrackMultiplicity, axisZdcTotalEnergy}); + registry.add("zdc/spectatorEstimateVsNGlobalTracks", "Triggered events with finite common energies;N_{global tracks};N_{spectators} estimate", HistType::kTH2F, {axisZdcTrackMultiplicity, axisTotalSpectators}); + registry.add("zdc/energyZnVsCentrality", "Triggered events with finite ZN energies;centrality (%);E_{ZNA}+E_{ZNC}", HistType::kTH2F, {axisCentrality, axisZdcTotalEnergy}); + registry.add("zdc/energyZpVsCentrality", "Triggered events with finite ZP energies;centrality (%);E_{ZPA}+E_{ZPC}", HistType::kTH2F, {axisCentrality, axisZdcTotalEnergy}); + registry.add("zdc/energyTotalVsCentrality", "Triggered events with finite common energies;centrality (%);#Sigma E_{ZDC}^{common}", HistType::kTH2F, {axisCentrality, axisZdcTotalEnergy}); + registry.add("zdc/spectatorEstimateVsCentrality", "Triggered events with finite common energies;centrality (%);N_{spectators} estimate", HistType::kTH2F, {axisCentrality, axisTotalSpectators}); + registry.add("zdc/meanZNAByNGlobalTracks", "Mean ZNA response;N_{global tracks};#LT E_{ZNA} #GT", HistType::kTProfile, {axisZdcTrackMultiplicity}); + registry.add("zdc/meanZNCByNGlobalTracks", "Mean ZNC response;N_{global tracks};#LT E_{ZNC} #GT", HistType::kTProfile, {axisZdcTrackMultiplicity}); + registry.add("zdc/meanZPAByNGlobalTracks", "Mean ZPA response;N_{global tracks};#LT E_{ZPA} #GT", HistType::kTProfile, {axisZdcTrackMultiplicity}); + registry.add("zdc/meanZPCByNGlobalTracks", "Mean ZPC response;N_{global tracks};#LT E_{ZPC} #GT", HistType::kTProfile, {axisZdcTrackMultiplicity}); + registry.add("zdc/meanZnByNGlobalTracks", "Mean neutron-ZDC response;N_{global tracks};#LT E_{ZNA}+E_{ZNC} #GT", HistType::kTProfile, {axisZdcTrackMultiplicity}); + registry.add("zdc/meanZpByNGlobalTracks", "Mean proton-ZDC response;N_{global tracks};#LT E_{ZPA}+E_{ZPC} #GT", HistType::kTProfile, {axisZdcTrackMultiplicity}); + registry.add("zdc/meanTotalByNGlobalTracks", "Mean total ZDC response;N_{global tracks};#LT #Sigma E_{ZDC}^{common} #GT", HistType::kTProfile, {axisZdcTrackMultiplicity}); + registry.add("zdc/meanZNAByCentrality", "Mean ZNA response;centrality (%);#LT E_{ZNA} #GT", HistType::kTProfile, {axisCentrality}); + registry.add("zdc/meanZNCByCentrality", "Mean ZNC response;centrality (%);#LT E_{ZNC} #GT", HistType::kTProfile, {axisCentrality}); + registry.add("zdc/meanZPAByCentrality", "Mean ZPA response;centrality (%);#LT E_{ZPA} #GT", HistType::kTProfile, {axisCentrality}); + registry.add("zdc/meanZPCByCentrality", "Mean ZPC response;centrality (%);#LT E_{ZPC} #GT", HistType::kTProfile, {axisCentrality}); + registry.add("zdc/meanZnByCentrality", "Mean neutron-ZDC response;centrality (%);#LT E_{ZNA}+E_{ZNC} #GT", HistType::kTProfile, {axisCentrality}); + registry.add("zdc/meanZpByCentrality", "Mean proton-ZDC response;centrality (%);#LT E_{ZPA}+E_{ZPC} #GT", HistType::kTProfile, {axisCentrality}); + registry.add("zdc/meanTotalByCentrality", "Mean total ZDC response;centrality (%);#LT #Sigma E_{ZDC}^{common} #GT", HistType::kTProfile, {axisCentrality}); + + auto selectionHistogram = registry.get(HIST("event/aodSelection")); + selectionHistogram->GetXaxis()->SetBinLabel(AllAodEvents + 1, "all"); + selectionHistogram->GetXaxis()->SetBinLabel(TriggerSelected + 1, "event selection"); + selectionHistogram->GetXaxis()->SetBinLabel(CentralitySelected + 1, "centrality threshold"); + selectionHistogram->GetXaxis()->SetBinLabel(HasValidZdc + 1, "valid ZDC"); + selectionHistogram->GetXaxis()->SetBinLabel(SpectatorSelected + 1, "spectator threshold"); + auto zdcQaHistogram = registry.get(HIST("zdc/qaSelection")); + zdcQaHistogram->GetXaxis()->SetBinLabel(MatchedZdc + 1, "matched ZDC"); + zdcQaHistogram->GetXaxis()->SetBinLabel(FiniteCommonEnergy + 1, "finite common energies"); + zdcQaHistogram->GetXaxis()->SetBinLabel(NonnegativeCommonEnergy + 1, "nonnegative common energies"); + zdcQaHistogram->GetXaxis()->SetBinLabel(HasZnTiming + 1, "ZNA and ZNC timing"); + zdcQaHistogram->GetXaxis()->SetBinLabel(HasZpTiming + 1, "ZPA and ZPC timing"); + zdcQaHistogram->GetXaxis()->SetBinLabel(HasAllTiming + 1, "all timing"); + auto pidHistogram = registry.get(HIST("track/pid")); + pidHistogram->GetXaxis()->SetBinLabel(Unidentified + 1, "unidentified"); + pidHistogram->GetXaxis()->SetBinLabel(Pion + 1, "pion"); + pidHistogram->GetXaxis()->SetBinLabel(Kaon + 1, "kaon"); + pidHistogram->GetXaxis()->SetBinLabel(Proton + 1, "proton"); + + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + const auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + ccdb->setCreatedNotAfter(now); + } + + template + float totalSpectators(TZdc const& zdc) const + { + const std::array energies{zdc.energyCommonZNA(), zdc.energyCommonZNC(), zdc.energyCommonZPA(), zdc.energyCommonZPC()}; + const std::array responseFactors{cfgZdcResponseZNA.value, cfgZdcResponseZNC.value, cfgZdcResponseZPA.value, cfgZdcResponseZPC.value}; + float totalEnergy = 0.f; + for (int index = 0; index < 4; ++index) { + const float value = energies[index]; + if (!std::isfinite(value)) { + return -1.f; + } + totalEnergy += value / responseFactors[index]; + } + const float spectators = totalEnergy / cfgSpectatorEnergyPerNucleon.value; + // Preserve signed pedestal fluctuations in the sum, then enforce the physical boundary. + return std::isfinite(spectators) ? std::max(0.f, spectators) : -1.f; + } + + template + void fillZdcQa(TZdc const& zdc, float numberOfGlobalTracks, float centrality) + { + registry.fill(HIST("zdc/qaSelection"), MatchedZdc); + + const std::array commonEnergies{zdc.energyCommonZNA(), zdc.energyCommonZNC(), zdc.energyCommonZPA(), zdc.energyCommonZPC()}; + const std::array times{zdc.timeZNA(), zdc.timeZNC(), zdc.timeZPA(), zdc.timeZPC()}; + const bool validCentrality = std::isfinite(centrality) && centrality >= 0.f && centrality <= 100.f; + const bool allFinite = std::all_of(commonEnergies.begin(), commonEnergies.end(), [](float value) { return std::isfinite(value); }); + const bool allNonnegative = allFinite && std::all_of(commonEnergies.begin(), commonEnergies.end(), [](float value) { return value >= 0.f; }); + if (allFinite) { + registry.fill(HIST("zdc/qaSelection"), FiniteCommonEnergy); + } + if (allNonnegative) { + registry.fill(HIST("zdc/qaSelection"), NonnegativeCommonEnergy); + } + + const auto hasTiming = [this](float value) { return std::isfinite(value) && std::abs(value) < cfgZdcMaxAbsTimeForQa.value; }; + const bool hasZnTiming = hasTiming(times[0]) && hasTiming(times[1]); + const bool hasZpTiming = hasTiming(times[2]) && hasTiming(times[3]); + if (hasZnTiming) { + registry.fill(HIST("zdc/qaSelection"), HasZnTiming); + registry.fill(HIST("zdc/timeZnSumVsDiff"), times[0] - times[1], times[0] + times[1]); + } + if (hasZpTiming) { + registry.fill(HIST("zdc/qaSelection"), HasZpTiming); + } + if (hasZnTiming && hasZpTiming) { + registry.fill(HIST("zdc/qaSelection"), HasAllTiming); + } + + if (std::isfinite(commonEnergies[0])) { + registry.fill(HIST("zdc/energyZNA"), commonEnergies[0]); + registry.fill(HIST("zdc/meanZNAByNGlobalTracks"), numberOfGlobalTracks, commonEnergies[0]); + if (validCentrality) { + registry.fill(HIST("zdc/meanZNAByCentrality"), centrality, commonEnergies[0]); + } + } + if (std::isfinite(commonEnergies[1])) { + registry.fill(HIST("zdc/energyZNC"), commonEnergies[1]); + registry.fill(HIST("zdc/meanZNCByNGlobalTracks"), numberOfGlobalTracks, commonEnergies[1]); + if (validCentrality) { + registry.fill(HIST("zdc/meanZNCByCentrality"), centrality, commonEnergies[1]); + } + } + if (std::isfinite(commonEnergies[2])) { + registry.fill(HIST("zdc/energyZPA"), commonEnergies[2]); + registry.fill(HIST("zdc/meanZPAByNGlobalTracks"), numberOfGlobalTracks, commonEnergies[2]); + if (validCentrality) { + registry.fill(HIST("zdc/meanZPAByCentrality"), centrality, commonEnergies[2]); + } + } + if (std::isfinite(commonEnergies[3])) { + registry.fill(HIST("zdc/energyZPC"), commonEnergies[3]); + registry.fill(HIST("zdc/meanZPCByNGlobalTracks"), numberOfGlobalTracks, commonEnergies[3]); + if (validCentrality) { + registry.fill(HIST("zdc/meanZPCByCentrality"), centrality, commonEnergies[3]); + } + } + + if (hasTiming(times[0])) { + registry.fill(HIST("zdc/timeZNA"), times[0]); + } + if (hasTiming(times[1])) { + registry.fill(HIST("zdc/timeZNC"), times[1]); + } + if (hasTiming(times[2])) { + registry.fill(HIST("zdc/timeZPA"), times[2]); + } + if (hasTiming(times[3])) { + registry.fill(HIST("zdc/timeZPC"), times[3]); + } + if (hasTiming(times[0]) && std::isfinite(commonEnergies[0])) { + registry.fill(HIST("zdc/energyZnaVsTime"), times[0], commonEnergies[0]); + } + if (hasTiming(times[1]) && std::isfinite(commonEnergies[1])) { + registry.fill(HIST("zdc/energyZncVsTime"), times[1], commonEnergies[1]); + } + if (hasTiming(times[2]) && std::isfinite(commonEnergies[2])) { + registry.fill(HIST("zdc/energyZpaVsTime"), times[2], commonEnergies[2]); + } + if (hasTiming(times[3]) && std::isfinite(commonEnergies[3])) { + registry.fill(HIST("zdc/energyZpcVsTime"), times[3], commonEnergies[3]); + } + + const auto sumSectors = [](const auto& sectors) { + float sum = 0.f; + for (const float value : sectors) { + if (!std::isfinite(value)) { + return std::numeric_limits::quiet_NaN(); + } + sum += value; + } + return sum; + }; + const std::array sectorSums{sumSectors(zdc.energySectorZNA()), sumSectors(zdc.energySectorZNC()), sumSectors(zdc.energySectorZPA()), sumSectors(zdc.energySectorZPC())}; + if (std::isfinite(commonEnergies[0]) && std::isfinite(sectorSums[0])) { + registry.fill(HIST("zdc/commonVsSectorZNA"), commonEnergies[0], sectorSums[0]); + } + if (std::isfinite(commonEnergies[1]) && std::isfinite(sectorSums[1])) { + registry.fill(HIST("zdc/commonVsSectorZNC"), commonEnergies[1], sectorSums[1]); + } + if (std::isfinite(commonEnergies[2]) && std::isfinite(sectorSums[2])) { + registry.fill(HIST("zdc/commonVsSectorZPA"), commonEnergies[2], sectorSums[2]); + } + if (std::isfinite(commonEnergies[3]) && std::isfinite(sectorSums[3])) { + registry.fill(HIST("zdc/commonVsSectorZPC"), commonEnergies[3], sectorSums[3]); + } + + const float zem1 = zdc.energyZEM1(); + const float zem2 = zdc.energyZEM2(); + const bool finiteZem = std::isfinite(zem1) && std::isfinite(zem2); + const float zemSum = finiteZem ? zem1 + zem2 : std::numeric_limits::quiet_NaN(); + if (finiteZem) { + registry.fill(HIST("zdc/energyZEM"), zemSum); + } + if (std::isfinite(commonEnergies[0]) && std::isfinite(commonEnergies[1])) { + const float znSum = commonEnergies[0] + commonEnergies[1]; + registry.fill(HIST("zdc/energyZnaVsZnc"), commonEnergies[1], commonEnergies[0]); + registry.fill(HIST("zdc/energyZnVsNGlobalTracks"), numberOfGlobalTracks, znSum); + registry.fill(HIST("zdc/meanZnByNGlobalTracks"), numberOfGlobalTracks, znSum); + if (validCentrality) { + registry.fill(HIST("zdc/energyZnVsCentrality"), centrality, znSum); + registry.fill(HIST("zdc/meanZnByCentrality"), centrality, znSum); + } + if (finiteZem) { + registry.fill(HIST("zdc/energyZnVsZem"), zemSum, znSum); + } + } + if (std::isfinite(commonEnergies[2]) && std::isfinite(commonEnergies[3])) { + const float zpSum = commonEnergies[2] + commonEnergies[3]; + registry.fill(HIST("zdc/energyZpaVsZpc"), commonEnergies[3], commonEnergies[2]); + registry.fill(HIST("zdc/energyZpVsNGlobalTracks"), numberOfGlobalTracks, zpSum); + registry.fill(HIST("zdc/meanZpByNGlobalTracks"), numberOfGlobalTracks, zpSum); + if (validCentrality) { + registry.fill(HIST("zdc/energyZpVsCentrality"), centrality, zpSum); + registry.fill(HIST("zdc/meanZpByCentrality"), centrality, zpSum); + } + } + if (allFinite) { + const float totalEnergy = commonEnergies[0] + commonEnergies[1] + commonEnergies[2] + commonEnergies[3]; + registry.fill(HIST("zdc/energyTotalVsNGlobalTracks"), numberOfGlobalTracks, totalEnergy); + registry.fill(HIST("zdc/meanTotalByNGlobalTracks"), numberOfGlobalTracks, totalEnergy); + if (validCentrality) { + registry.fill(HIST("zdc/energyTotalVsCentrality"), centrality, totalEnergy); + registry.fill(HIST("zdc/meanTotalByCentrality"), centrality, totalEnergy); + } + const float spectators = totalSpectators(zdc); + if (spectators >= 0.f) { + registry.fill(HIST("zdc/spectatorEstimateVsNGlobalTracks"), numberOfGlobalTracks, spectators); + if (validCentrality) { + registry.fill(HIST("zdc/spectatorEstimateVsCentrality"), centrality, spectators); + } + } + } + } + + template + bool eventSelected(TCollision const& collision, bool triggerSelected, float numberOfGlobalTracks, float centrality, float& spectators) + { + spectators = -1.f; + registry.fill(HIST("event/aodSelection"), AllAodEvents); + if (!triggerSelected) { + return false; + } + registry.fill(HIST("event/aodSelection"), TriggerSelected); + + if (!collision.has_foundBC()) { + return false; + } + const auto& bc = collision.template foundBC_as(); + if (!bc.has_zdc()) { + return false; + } + const auto& zdc = bc.zdc(); + fillZdcQa(zdc, numberOfGlobalTracks, centrality); + + if (!std::isfinite(centrality) || centrality < 0.f || centrality > cfgMaxCentrality) { + return false; + } + registry.fill(HIST("event/aodSelection"), CentralitySelected); + + spectators = totalSpectators(zdc); + if (spectators < 0.f) { + return false; + } + registry.fill(HIST("event/aodSelection"), HasValidZdc); + registry.fill(HIST("event/totalSpectators"), spectators); + if (spectators > cfgMaxTotalSpectators) { + return false; + } + registry.fill(HIST("event/aodSelection"), SpectatorSelected); + return true; + } + + static bool isZdcBeamBeamSelectionFlag(int flag) + { + return flag == aod::evsel::kIsBBZNA || flag == aod::evsel::kIsBBZNC || flag == aod::evsel::kIsBBZAC; + } + + template + bool run2EventSelected(TCollision const& collision) + { + if (!collision.alias_bit(kINT7)) { + return false; + } + if (!cfgUseRun2EventSelectionWithoutZdc) { + return collision.sel7(); + } + if (!collision.has_foundBC()) { + return false; + } + + const auto& bc = collision.template foundBC_as(); + auto* selectionParameters = ccdb->getForTimeStamp("EventSelection/EventSelectionParams", bc.timestamp()); + if (!selectionParameters) { + LOGF(fatal, "Could not load the Run 2 event-selection parameters"); + } + const bool* enabledSelections = selectionParameters->getSelection(0); + for (int flag = 0; flag < aod::evsel::kNsel; ++flag) { + if (isZdcBeamBeamSelectionFlag(flag)) { + continue; + } + if (enabledSelections[flag] && !collision.selection_bit(flag)) { + return false; + } + } + return true; + } + + void loadEfficiency(uint64_t timestamp) + { + if (efficiencyLoaded) { + return; + } + if (cfgEfficiency.cfgEfficiencyPath.value.empty()) { + efficiencyLoaded = true; + return; + } + + if (!cfgEfficiency.cfgUsePIDEfficiencies) { + efficiency = ccdb->getForTimeStamp(cfgEfficiency.cfgEfficiencyPath.value, timestamp); + if (!efficiency) { + LOGF(fatal, "Could not load efficiency histogram from %s", cfgEfficiency.cfgEfficiencyPath.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s", cfgEfficiency.cfgEfficiencyPath.value.c_str()); + } else { + static constexpr std::array EfficiencySpeciesNames{"ch", "pi", "ka", "pr"}; + pidEfficiencies.reserve(EfficiencySpeciesNames.size()); + for (const auto& speciesName : EfficiencySpeciesNames) { + const std::string path = cfgEfficiency.cfgEfficiencyPath.value + "/" + speciesName; + pidEfficiencies.push_back(ccdb->getForTimeStamp(path, timestamp)); + if (!pidEfficiencies.back()) { + LOGF(fatal, "Could not load PID efficiency histogram from %s", path.c_str()); + } + LOGF(info, "Loaded PID efficiency histogram from %s", path.c_str()); + } + } + efficiencyLoaded = true; + } + + void loadAcceptance(uint64_t timestamp, int runNumber) + { + if (acceptancePath.value.empty()) { + acceptance = nullptr; + return; + } + if (acceptance && (!acceptanceRunByRun || acceptanceRunNumber == runNumber)) { + return; + } + std::string path = acceptancePath.value; + if (acceptanceRunByRun) { + if (path.back() != '/') { + path += '/'; + } + path += "RunByRun/"; + } + acceptance = ccdb->getForTimeStamp(path, timestamp); + if (!acceptance || !acceptance->isDataFilled()) { + LOGF(fatal, "Could not load populated GFW acceptance weights from %s", path.c_str()); + } + acceptanceRunNumber = runNumber; + } + + double getEfficiency(float pt, int pid) const + { + TH1* efficiencyHistogram = efficiency; + if (cfgEfficiency.cfgUsePIDEfficiencies.value) { + if (pidEfficiencies.empty()) { + return 1.; + } + efficiencyHistogram = pidEfficiencies[pid]; + } else if (!efficiencyHistogram) { + return 1.; + } + + const double value = efficiencyHistogram->GetBinContent(efficiencyHistogram->FindBin(pt)); + return value == 0. ? -1. : 1. / value; + } + + template + int particleId(TTrack const& track) const + { + const std::array tpc{track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; + std::array selected = tpc; + if (track.pt() > energy.tofPtCut.value) { + if (!track.hasTOF()) { + return Unidentified; + } + selected = {std::hypot(tpc[0], track.tofNSigmaPi()), + std::hypot(tpc[1], track.tofNSigmaKa()), + std::hypot(tpc[2], track.tofNSigmaPr())}; + } + + int bestSpecies = Unidentified; + int compatibleSpecies = 0; + float bestNSigma = energy.nSigmaCut.value; + for (int species = 0; species < 3; ++species) { + const float absoluteNSigma = std::abs(selected[species]); + if (absoluteNSigma >= energy.nSigmaCut.value) { + continue; + } + ++compatibleSpecies; + if (absoluteNSigma < bestNSigma) { + bestNSigma = absoluteNSigma; + bestSpecies = species + 1; + } + } + if (energy.strictPid.value && compatibleSpecies > 1) { + return Unidentified; + } + return bestSpecies; + } + + double massForParticle(int pid) const + { + switch (pid) { + case Pion: + return o2::constants::physics::MassPionCharged; + case Kaon: + return o2::constants::physics::MassKPlus; + case Proton: + return o2::constants::physics::MassProton; + default: + return energy.unidentifiedMass.value; + } + } + + template + double transverseEnergy(TTrack const& track, int pid) const + { + if (energy.weightMode.value == PtWeight) { + return track.pt(); + } + const double mass = massForParticle(pid); + if (energy.weightMode.value == MtWeight) { + return std::hypot(track.pt(), mass); + } + return std::hypot(track.pt(), mass / std::cosh(track.eta())); + } + + template + bool isEfficiencyParticle(TParticle const& particle) const + { + if (!particle.isPhysicalPrimary() || std::abs(particle.eta()) >= cuts.etaMax.value || particle.pt() <= cuts.ptCuts.value.first || particle.pt() >= cuts.ptCuts.value.second) { + return false; + } + const auto* pdgParticle = pdgDB->GetParticle(particle.pdgCode()); + return pdgParticle && pdgParticle->Charge() != 0.; + } + + template + int efficiencySpecies(TParticle const& particle) const + { + const int absolutePdg = std::abs(particle.pdgCode()); + if (absolutePdg == PDG_t::kPiPlus) { + return EfficiencyPion; + } + if (absolutePdg == PDG_t::kKPlus) { + return EfficiencyKaon; + } + if (absolutePdg == PDG_t::kProton) { + return EfficiencyProton; + } + return EfficiencyCharged; + } + + template + void fillEfficiencyParticle(TParticle const& particle, float centrality, EfficiencyStep step) + { + if (!isEfficiencyParticle(particle)) { + return; + } + registry.fill(HIST("Efficiency/efficiencyHist"), particle.pt(), centrality, EfficiencyCharged, step); + const int species = efficiencySpecies(particle); + if (species != EfficiencyCharged) { + registry.fill(HIST("Efficiency/efficiencyHist"), particle.pt(), centrality, species, step); + } + } + + template + void fillTrackingEfficiency(int mcCollisionIndex, TParticles const& particles, TTracks const& tracks, float centrality) + { + for (const auto& particle : particles) { + if (particle.mcCollisionId() != mcCollisionIndex) { + continue; + } + fillEfficiencyParticle(particle, centrality, EfficiencyGenerated); + } + for (const auto& track : tracks) { + if (!track.has_mcParticle()) { + continue; + } + const auto& particle = track.mcParticle(); + if (particle.mcCollisionId() != mcCollisionIndex) { + continue; + } + fillEfficiencyParticle(particle, centrality, EfficiencyReconstructed); + } + } + + template + void fillAcceptanceWeights(TCollision const& collision, TTracks const& tracks, float centrality) + { + for (const auto& track : tracks) { + acceptanceWeights->fill(track.phi(), track.eta(), collision.posZ(), track.pt(), centrality, 0); + } + } + + template + void processCollision(TCollision const& collision, TTracks const& tracks, float centrality, float spectators) + { + float activity = centrality; + if (cfgActivityAxis.value == NchAxis) { + activity = static_cast(tracks.size()); + } else if (cfgActivityAxis.value == ZdcSpectatorAxis) { + activity = spectators; + } + if (!std::isfinite(activity)) { + return; + } + + gfw->Clear(); + for (const auto& track : tracks) { + const bool needsParticleId = energy.weightMode.value != PtWeight || cfgEfficiency.cfgUsePIDEfficiencies; + const int pid = needsParticleId ? particleId(track) : Unidentified; + const double et = transverseEnergy(track, pid); + const double weff = getEfficiency(track.pt(), pid); + const double wacc = acceptance ? acceptance->getNUA(track.phi(), track.eta(), collision.posZ()) : 1.; + if (!std::isfinite(et) || !std::isfinite(weff) || !std::isfinite(wacc) || et <= 0. || weff <= 0. || wacc <= 0.) { + continue; + } + gfw->Fill(track.eta(), 0, track.phi(), et * weff * wacc, gfwMask); + registry.fill(HIST("track/ptEta"), track.pt(), track.eta()); + registry.fill(HIST("track/energy"), et); + if (energy.weightMode.value != PtWeight) { + registry.fill(HIST("track/pid"), pid); + } + } + + registry.fill(HIST("event/centrality"), centrality); + registry.fill(HIST("event/activity"), activity); + registry.fill(HIST("event/vertex"), collision.posZ()); + fillFlowContainer(activity); + } + + void fillFlowContainer(float activity) + { + const double randomNumber = random->Rndm(); + for (std::size_t index = 0; index < corrConfigs.size(); ++index) { + const auto& config = corrConfigs[index]; + if (zeroHarmonicConfigs[index]) { + const double raw = gfw->Calculate(config, 0, false).real(); + if (std::isfinite(raw) && raw > 0.) { + const std::string rawName = config.Head + "_raw"; + flowContainer->FillProfile(rawName.c_str(), activity, raw, 1., randomNumber); + } + continue; + } + + const double denominator = gfw->Calculate(config, 0, true).real(); + if (!std::isfinite(denominator) || denominator <= 0.) { + continue; + } + const double raw = gfw->Calculate(config, 0, false).real(); + if (!std::isfinite(raw)) { + continue; + } + const std::string rawName = config.Head + "_raw"; + flowContainer->FillProfile(rawName.c_str(), activity, raw, 1., randomNumber); + flowContainer->FillProfile(config.Head.c_str(), activity, raw / denominator, denominator, randomNumber); + } + } + + void processRun2(CollisionsRun2::iterator const& collision, Tracks const& tracks, BCsRun2 const&, aod::Zdcs const&) + { + float spectators = -1.f; + if (!eventSelected(collision, run2EventSelected(collision), static_cast(tracks.size()), collision.centRun2V0M(), spectators)) { + return; + } + if (cfgFillAcceptance) { + fillAcceptanceWeights(collision, tracks, collision.centRun2V0M()); + return; + } + const auto& bc = collision.foundBC_as(); + loadEfficiency(bc.timestamp()); + loadAcceptance(bc.timestamp(), bc.runNumber()); + processCollision(collision, tracks, collision.centRun2V0M(), spectators); + } + PROCESS_SWITCH(EnergyFlowGfw, processRun2, "Process Run 2 AOD data", false); + + void processRun2MC(aod::McCollisions::iterator const& mcCollision, + soa::SmallGroups const& collisions, + BCsRun2 const&, + aod::Zdcs const&, + TracksMC const& tracks, + aod::McParticles const& mcParticles) + { + if (collisions.size() != 1) { + return; + } + + for (const auto& collision : collisions) { + if (std::abs(collision.posZ()) >= cuts.vertexZ.value) { + return; + } + const auto groupedTracks = tracks.sliceBy(tracksPerCollision, collision.globalIndex()); + float spectators = -1.f; + if (!eventSelected(collision, collision.alias_bit(kINT7) && collision.sel7(), static_cast(groupedTracks.size()), collision.centRun2V0M(), spectators)) { + return; + } + + bool filledCorrections = false; + if (cfgFillEfficiency) { + fillTrackingEfficiency(mcCollision.globalIndex(), mcParticles, groupedTracks, collision.centRun2V0M()); + filledCorrections = true; + } + if (cfgFillAcceptance) { + fillAcceptanceWeights(collision, groupedTracks, collision.centRun2V0M()); + filledCorrections = true; + } + if (filledCorrections) { + return; + } + + const auto& bc = collision.foundBC_as(); + loadEfficiency(bc.timestamp()); + loadAcceptance(bc.timestamp(), bc.runNumber()); + processCollision(collision, groupedTracks, collision.centRun2V0M(), spectators); + return; + } + } + PROCESS_SWITCH(EnergyFlowGfw, processRun2MC, "Process matched Run 2 MC for efficiency, acceptance, or reconstructed closure", false); + + void processRun3(CollisionsRun3::iterator const& collision, Tracks const& tracks, BCsRun3 const&, aod::Zdcs const&) + { + float spectators = -1.f; + if (!eventSelected(collision, collision.sel8(), static_cast(tracks.size()), collision.centFT0C(), spectators)) { + return; + } + if (cfgFillAcceptance) { + fillAcceptanceWeights(collision, tracks, collision.centFT0C()); + return; + } + const auto& bc = collision.foundBC_as(); + loadEfficiency(bc.timestamp()); + loadAcceptance(bc.timestamp(), bc.runNumber()); + processCollision(collision, tracks, collision.centFT0C(), spectators); + } + PROCESS_SWITCH(EnergyFlowGfw, processRun3, "Process Run 3 AOD data", true); + + void processRun3MC(aod::McCollisions::iterator const& mcCollision, + soa::SmallGroups const& collisions, + BCsRun3 const&, + aod::Zdcs const&, + TracksMC const& tracks, + aod::McParticles const& mcParticles) + { + if (collisions.size() != 1) { + return; + } + + for (const auto& collision : collisions) { + if (std::abs(collision.posZ()) >= cuts.vertexZ.value) { + return; + } + const auto groupedTracks = tracks.sliceBy(tracksPerCollision, collision.globalIndex()); + float spectators = -1.f; + if (!eventSelected(collision, collision.sel8(), static_cast(groupedTracks.size()), collision.centFT0C(), spectators)) { + return; + } + + bool filledCorrections = false; + if (cfgFillEfficiency) { + fillTrackingEfficiency(mcCollision.globalIndex(), mcParticles, groupedTracks, collision.centFT0C()); + filledCorrections = true; + } + if (cfgFillAcceptance) { + fillAcceptanceWeights(collision, groupedTracks, collision.centFT0C()); + filledCorrections = true; + } + if (filledCorrections) { + return; + } + + const auto& bc = collision.foundBC_as(); + loadEfficiency(bc.timestamp()); + loadAcceptance(bc.timestamp(), bc.runNumber()); + processCollision(collision, groupedTracks, collision.centFT0C(), spectators); + return; + } + } + PROCESS_SWITCH(EnergyFlowGfw, processRun3MC, "Process matched Run 3 MC for efficiency, acceptance, or reconstructed closure", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{adaptAnalysisTask(context)}; +} diff --git a/PWGCF/GenericFramework/Tasks/flowGfwV02.cxx b/PWGCF/GenericFramework/Tasks/flowGfwV02.cxx index db3767664e9..90ae8e00762 100644 --- a/PWGCF/GenericFramework/Tasks/flowGfwV02.cxx +++ b/PWGCF/GenericFramework/Tasks/flowGfwV02.cxx @@ -17,9 +17,11 @@ #include "PWGCF/GenericFramework/Core/GFW.h" #include "PWGCF/GenericFramework/Core/GFWConfig.h" #include "PWGCF/GenericFramework/Core/GFWWeights.h" +#include "PWGCF/GenericFramework/DataModel/GfwDerived.h" #include "PWGCF/JCorran/DataModel/JCatalyst.h" #include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/TriggerAliases.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" @@ -74,6 +76,8 @@ using namespace analysis::genericframework; #define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, (DEFAULT), (HELP)}; // NOLINT(bugprone-macro-parentheses) static constexpr std::array, 20> LongArrayFloat = {{{{1.1, 2.1, 3.1}}, {{1.2, 2.2, 3.2}}, {{1.3, 2.3, 3.3}}, {{-1.1, -2.1, -3.1}}, {{-1.2, -2.2, -3.2}}, {{-1.3, -2.3, -3.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}, {{1.3, 1.3, 1.3}}, {{-1.1, -1.1, -1.1}}, {{-1.2, -1.2, -1.2}}, {{-1.3, -1.3, -1.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}, {{1.3, 1.3, 1.3}}, {{-1.1, -1.1, -1.1}}, {{-1.2, -1.2, -1.2}}, {{-1.3, -1.3, -1.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}}}; +// Rows: UpCut_pi, UpCut_ka, UpCut_pr, LowCut_pi, LowCut_ka, LowCut_pr. Columns: TPC, TOF, ITS. +static constexpr std::array, 6> HighPurityNsigma = {{{{1.5f, 0.f, 0.f}}, {{0.f, 1.5f, 0.f}}, {{0.f, 1.5f, 0.f}}, {{0.f, -1.5f, 0.f}}, {{-1.5f, 0.f, 0.f}}, {{-1.5f, 0.f, 0.f}}}}; template auto projectMatrix(Array2D const& mat, std::array& array1, std::array& array2, std::array& array3) @@ -124,6 +128,8 @@ struct FlowGfwV02 { O2_DEFINE_CONFIGURABLE(cfgFixedMultMax, int, 3000, "Maximum for fixed nch range"); O2_DEFINE_CONFIGURABLE(cfgTofPtCut, float, 0.5f, "Minimum pt to use TOF N-sigma") O2_DEFINE_CONFIGURABLE(cfgUseItsPID, bool, true, "Use ITS PID for particle identification") + O2_DEFINE_CONFIGURABLE(cfgUseHighPurityPID, bool, false, "Use high purity PID for particle identification") + O2_DEFINE_CONFIGURABLE(cfgHighPurityPtMin, float, 3.f, "Minimum pt (GeV/c) for high purity PID") O2_DEFINE_CONFIGURABLE(cfgGetNsigmaQA, bool, true, "Get QA histograms for selection of pions, kaons, and protons") O2_DEFINE_CONFIGURABLE(cfgGetdEdx, bool, true, "Get dEdx histograms for pions, kaons, and protons") O2_DEFINE_CONFIGURABLE(cfgUseMultiplicityFlowWeights, bool, true, "Enable or disable the use of multiplicity-based event weighting"); @@ -197,8 +203,9 @@ struct FlowGfwV02 { Configurable cfgGFWBinning{"cfgGFWBinning", {40, 16, 72, 300, 0, 3000, 0.2, 10.0, 0.2, 5.0, {0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.25, 1.5, 1.75, 2, 2.25, 2.5, 2.75, 3, 3.25, 3.5, 3.75, 4, 4.5, 5}, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90}}, "Configuration for binning"}; Configurable cfgRegions{"cfgRegions", {{"refN", "refP", "refFull", "refMid", "piP", "kaP", "prP"}, {-0.8, 0.5, -0.8, -0.4, 0.5, 0.5, 0.5}, {-0.5, 0.8, 0.8, 0.4, 0.8, 0.8, 0.8}, {0, 0, 0, 0, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1}}, "Configurations for GFW regions"}; - Configurable cfgCorrConfig{"cfgCorrConfig", {{"refP {2} refN {-2}", "piP {2} refN {-2}", "kaP {2} refN {-2}", "prP {2} refN {-2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}"}, {"ChGap22", "PiGap22", "KaGap22", "PrGap22", "ChFull22", "nchCh", "nchPi", "nchKa", "nchPr", "v02ptCh", "v02ptPi", "v02ptKa", "v02ptPr"}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {15, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, "Configurations for each correlation to calculate"}; + Configurable cfgCorrConfig{"cfgCorrConfig", {{"refP {2} refN {-2}", "refP {2 2} refN {-2 -2}", "piP {2} refN {-2}", "kaP {2} refN {-2}", "prP {2} refN {-2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}"}, {"ChGap22", "ChGap24", "PiGap22", "KaGap22", "PrGap22", "ChFull22", "nchCh", "nchPi", "nchKa", "nchPr", "v02ptCh", "v02ptPi", "v02ptKa", "v02ptPr"}, {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {15, 15, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, "Configurations for each correlation to calculate"}; Configurable> nSigmas{"nSigmas", {LongArrayFloat.front().data(), 6, 3, {"UpCut_pi", "UpCut_ka", "UpCut_pr", "LowCut_pi", "LowCut_ka", "LowCut_pr"}, {"TPC", "TOF", "ITS"}}, "Labeled array for n-sigma values for TPC, TOF, ITS for pions, kaons, protons (positive and negative)"}; + Configurable> nSigmasHighPurity{"nSigmasHighPurity", {HighPurityNsigma.front().data(), 6, 3, {"UpCut_pi", "UpCut_ka", "UpCut_pr", "LowCut_pi", "LowCut_ka", "LowCut_pr"}, {"TPC", "TOF", "ITS"}}, "High purity n-sigma windows for TPC, TOF, ITS for pions, kaons, and protons"}; struct : ConfigurableGroup { Configurable cfgZvtxMax{"cfgZvtxMax", 10.0f, "Maximum primary vertex cut applied for the events."}; @@ -208,8 +215,14 @@ struct FlowGfwV02 { // // Filters to be applied to the received data. // // The analysis assumes the data has been subjected to a QA of its selection, // // and thus only the final distributions of the data for analysis are saved. + // Derived-data track mask. Every listed track bit is required; see gfwtrack::TrackSelection. + // The event mask is built from cfgEventCutFlags when a GFW derived process is enabled. + Configurable> cfgBitmaskSelections{"cfgBitmaskSelections", {0, 3, 6, 9, 12}, "Enabled TrackSelection bit indices for GFW derived data. DCA 0-5, TPC track 6-13, TPC PID 14-22, TOF PID 23-31. The default is the nominal quality selection"}; + O2_DEFINE_CONFIGURABLE(cfgPidWindow, int, static_cast(o2::aod::gfwtrack::PidWindowNominal), "PID nSigma window for GFW derived data: 0 nominal, 1 tight, 2 loose"); + o2::framework::expressions::Filter collFilter = (nabs(aod::collision::posZ) < cfgEventCuts.cfgZvtxMax); o2::framework::expressions::Filter trackFilter = (aod::track::pt > cfgTrackCuts.cfgPtMin) && (aod::track::pt < cfgTrackCuts.cfgPtMax) && nabs(aod::track::eta) < cfgTrackCuts.cfgEtaMax && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (aod::track::itsChi2NCl < cfgTrackCuts.cfgChi2PrITSCls) && (aod::track::tpcChi2NCl < cfgTrackCuts.cfgChi2PrTPCCls) && nabs(aod::track::dcaZ) < cfgTrackCuts.cfgDCAz; + o2::framework::expressions::Filter gfwTrackFilter = (aod::gfwtrack::pt > cfgTrackCuts.cfgPtMin) && (aod::gfwtrack::pt < cfgTrackCuts.cfgPtMax) && (nabs(aod::gfwtrack::eta) < cfgTrackCuts.cfgEtaMax); // Connect to ccdb Service ccdb{}; @@ -270,6 +283,9 @@ struct FlowGfwV02 { std::array tofNsigmaCut{}; std::array itsNsigmaCut{}; std::array tpcNsigmaCut{}; + std::array highPurityTofNsigmaCut{}; + std::array highPurityItsNsigmaCut{}; + std::array highPurityTpcNsigmaCut{}; std::array, 4> hPtMid{}; std::array, 4> hPtForward{}; std::array, 4> hPtBackward{}; @@ -284,6 +300,8 @@ struct FlowGfwV02 { std::unique_ptr fMultPVGlobalCutHigh; std::unique_ptr fPtDepDCAxy; + uint32_t trackBitMask = o2::aod::gfwtrack::NominalMask; + uint16_t eventBitMask = o2::aod::gfwcollision::DefaultEventMask; using GFWTracks = soa::Filtered>; @@ -303,10 +321,67 @@ struct FlowGfwV02 { IndProtonLow }; + // Required gfwcollision::EventSel bits for derived data, taken from the event-cut configurables. + uint16_t eventSelectionMask() + { + uint16_t mask = 0; + if (cfgEventCutFlags.cfgNoSameBunchPileupCut) { + mask |= o2::aod::gfwcollision::kNoSameBunchPileup; + } + if (cfgEventCutFlags.cfgIsGoodZvtxFT0vsPV) { + mask |= o2::aod::gfwcollision::kIsGoodZvtxFT0vsPV; + } + if (cfgEventCutFlags.cfgNoCollInTimeRangeStandard) { + mask |= o2::aod::gfwcollision::kNoCollInTimeRangeStandard; + } + if (cfgEventCutFlags.cfgNoCollInRofStandard) { + mask |= o2::aod::gfwcollision::kNoCollInRofStandard; + } + if (cfgEventCutFlags.cfgNoHighMultCollInPrevRof) { + mask |= o2::aod::gfwcollision::kNoHighMultCollInPrevRof; + } + if (cfgEventCutFlags.cfgIsVertexITSTPC) { + mask |= o2::aod::gfwcollision::kIsVertexITSTPC; + } + if (cfgEventCutFlags.cfgIsGoodITSLayersAll) { + mask |= o2::aod::gfwcollision::kIsGoodITSLayersAll; + } + if (cfgEventCutFlags.cfgNoTimeFrameBorder) { + mask |= o2::aod::gfwcollision::kNoTimeFrameBorder; + } + if (cfgEventCutFlags.cfgNoITSROFrameBorder) { + mask |= o2::aod::gfwcollision::kNoITSROFrameBorder; + } + if (cfgEventCutFlags.cfgTVXinTRD) { + mask |= o2::aod::gfwcollision::kNotTVXinTRD; + } + if (cfgTrackCuts.cfgOccupancySelection >= 0) { + mask |= o2::aod::gfwcollision::kOccupancy; + } + if (cfgUseAdditionalEventCut) { + mask |= o2::aod::gfwcollision::kGoodVertex; + if (cfgMultCut) { + mask |= o2::aod::gfwcollision::kMultCorr; + } + } + return mask; + } + void init(InitContext const&) { + trackBitMask = 0; + for (const int bit : cfgBitmaskSelections.value) { + if (bit >= 0 && bit < 32) { + trackBitMask |= static_cast(1u << bit); + } + } + if (doprocessDerived || doprocessDerivedCorrected) { + eventBitMask = eventSelectionMask(); + LOGF(info, "GFW derived event-selection mask: %u", static_cast(eventBitMask)); + } projectMatrix(nSigmas->getData(), pidStates.tpcNsigmaCut, pidStates.tofNsigmaCut, pidStates.itsNsigmaCut); + projectMatrix(nSigmasHighPurity->getData(), pidStates.highPurityTpcNsigmaCut, pidStates.highPurityTofNsigmaCut, pidStates.highPurityItsNsigmaCut); if (cfgGetNsigmaQA) { if (cfgUseItsPID) { @@ -439,12 +514,14 @@ struct FlowGfwV02 { registry.add("analysis/charged/ptAB", "", {HistType::kTProfile2D, {bsAxis, centAxis}}); registry.add("analysis/charged/ptC", "", {HistType::kTProfile2D, {bsAxis, centAxis}}); registry.add("analysis/charged/v22", "", {HistType::kTProfile2D, {bsAxis, centAxis}}); + registry.add("analysis/charged/v24", "", {HistType::kTProfile2D, {bsAxis, centAxis}}); if (cfgUseMultiplicityFracWeights) { registry.add("analysis/charged/ptA2pc", "", {HistType::kTProfile2D, {bsAxis, centAxis}}); registry.add("analysis/charged/ptB2pc", "", {HistType::kTProfile2D, {bsAxis, centAxis}}); registry.add("analysis/charged/ptC3pc", "", {HistType::kTProfile2D, {bsAxis, centAxis}}); registry.add("analysis/charged/v223pc", "", {HistType::kTProfile2D, {bsAxis, centAxis}}); + registry.add("analysis/charged/v224pc", "", {HistType::kTProfile2D, {bsAxis, centAxis}}); } ccdb->setURL("http://alice-ccdb.cern.ch"); @@ -594,35 +671,78 @@ struct FlowGfwV02 { template int getNsigmaPID(TTrack const& track) { - // Computing Nsigma arrays for pion, kaon, and protons - std::array nSigmaTPC = {track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; - std::array nSigmaTOF = {track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr()}; - std::array nSigmaITS = {pidStates.itsResponse.nSigmaITS(track), pidStates.itsResponse.nSigmaITS(track), pidStates.itsResponse.nSigmaITS(track)}; int pid = -1; // -1 = not identified, 1 = pion, 2 = kaon, 3 = proton - - std::array nSigmaToUse = cfgUseItsPID ? nSigmaITS : nSigmaTPC; // Choose which nSigma to use: TPC or ITS - std::array detectorNsigmaCut = cfgUseItsPID ? pidStates.itsNsigmaCut : pidStates.tpcNsigmaCut; // Choose which nSigma to use: TPC or ITS - bool isPion = false, isKaon = false, isProton = false; - bool isDetectedPion = nSigmaToUse[IndPionUp] < detectorNsigmaCut[IndPionUp] && nSigmaToUse[IndPionUp] > detectorNsigmaCut[IndPionLow]; - bool isDetectedKaon = nSigmaToUse[IndKaonUp] < detectorNsigmaCut[IndKaonUp] && nSigmaToUse[IndKaonUp] > detectorNsigmaCut[IndKaonLow]; - bool isDetectedProton = nSigmaToUse[IndProtonUp] < detectorNsigmaCut[IndProtonUp] && nSigmaToUse[IndProtonUp] > detectorNsigmaCut[IndProtonLow]; - - bool isTofPion = nSigmaTOF[IndPionUp] < pidStates.tofNsigmaCut[IndPionUp] && nSigmaTOF[IndPionUp] > pidStates.tofNsigmaCut[IndPionLow]; - bool isTofKaon = nSigmaTOF[IndKaonUp] < pidStates.tofNsigmaCut[IndKaonUp] && nSigmaTOF[IndKaonUp] > pidStates.tofNsigmaCut[IndKaonLow]; - bool isTofProton = nSigmaTOF[IndProtonUp] < pidStates.tofNsigmaCut[IndProtonUp] && nSigmaTOF[IndProtonUp] > pidStates.tofNsigmaCut[IndProtonLow]; - - if (track.pt() > cfgTofPtCut && !track.hasTOF()) { + bool isDetectedPion = false; + bool isDetectedKaon = false; + bool isDetectedProton = false; + bool isTofPion = false; + bool isTofKaon = false; + bool isTofProton = false; + if constexpr (requires { track.trackSel(); }) { + const int window = cfgPidWindow; + const int shift = (window == aod::gfwtrack::PidWindowTight || window == aod::gfwtrack::PidWindowLoose) ? window * aod::gfwtrack::PidWindowBits : aod::gfwtrack::PidWindowNominal; + const uint32_t trackSel = track.trackSel(); + isDetectedPion = (trackSel & (static_cast(aod::gfwtrack::kTpcPi) << shift)) != 0; + isDetectedKaon = (trackSel & (static_cast(aod::gfwtrack::kTpcKa) << shift)) != 0; + isDetectedProton = (trackSel & (static_cast(aod::gfwtrack::kTpcPr) << shift)) != 0; + isTofPion = (trackSel & (static_cast(aod::gfwtrack::kTofPi) << shift)) != 0; + isTofKaon = (trackSel & (static_cast(aod::gfwtrack::kTofKa) << shift)) != 0; + isTofProton = (trackSel & (static_cast(aod::gfwtrack::kTofPr) << shift)) != 0; + } else if constexpr (requires { track.tpcNSigmaPi(); track.tofNSigmaPi(); }) { + const std::array nSigmaTPC = {track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; + const std::array nSigmaTOF = {track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr()}; + constexpr bool HasItsResponse = requires { + track.itsClusterSizes(); + track.p(); + }; + if (cfgUseHighPurityPID && track.pt() > cfgHighPurityPtMin) { + const bool isHighPurityPion = nSigmaTPC[IndPionUp] < pidStates.highPurityTpcNsigmaCut[IndPionUp] && nSigmaTPC[IndPionUp] > pidStates.highPurityTpcNsigmaCut[IndPionLow] && nSigmaTOF[IndPionUp] < pidStates.highPurityTofNsigmaCut[IndPionUp] && nSigmaTOF[IndPionUp] > pidStates.highPurityTofNsigmaCut[IndPionLow]; + const bool isHighPurityKaon = nSigmaTPC[IndKaonUp] < pidStates.highPurityTpcNsigmaCut[IndKaonUp] && nSigmaTPC[IndKaonUp] > pidStates.highPurityTpcNsigmaCut[IndKaonLow] && nSigmaTOF[IndKaonUp] < pidStates.highPurityTofNsigmaCut[IndKaonUp] && nSigmaTOF[IndKaonUp] > pidStates.highPurityTofNsigmaCut[IndKaonLow]; + const bool isHighPurityProton = nSigmaTPC[IndProtonUp] < pidStates.highPurityTpcNsigmaCut[IndProtonUp] && nSigmaTPC[IndProtonUp] > pidStates.highPurityTpcNsigmaCut[IndProtonLow] && nSigmaTOF[IndProtonUp] < pidStates.highPurityTofNsigmaCut[IndProtonUp] && nSigmaTOF[IndProtonUp] > pidStates.highPurityTofNsigmaCut[IndProtonLow]; + if (isHighPurityPion) { + return PidPions; + } + if (isHighPurityKaon) { + return PidKaons; + } + if (isHighPurityProton) { + return PidProtons; + } + return -1; + } + std::array nSigmaITS{}; + if constexpr (HasItsResponse) { + nSigmaITS = {pidStates.itsResponse.nSigmaITS(track), pidStates.itsResponse.nSigmaITS(track), pidStates.itsResponse.nSigmaITS(track)}; + } + const bool useItsPid = cfgUseItsPID && HasItsResponse; + const std::array nSigmaToUse = useItsPid ? nSigmaITS : nSigmaTPC; + const std::array detectorNsigmaCut = useItsPid ? pidStates.itsNsigmaCut : pidStates.tpcNsigmaCut; + isDetectedPion = nSigmaToUse[IndPionUp] < detectorNsigmaCut[IndPionUp] && nSigmaToUse[IndPionUp] > detectorNsigmaCut[IndPionLow]; + isDetectedKaon = nSigmaToUse[IndKaonUp] < detectorNsigmaCut[IndKaonUp] && nSigmaToUse[IndKaonUp] > detectorNsigmaCut[IndKaonLow]; + isDetectedProton = nSigmaToUse[IndProtonUp] < detectorNsigmaCut[IndProtonUp] && nSigmaToUse[IndProtonUp] > detectorNsigmaCut[IndProtonLow]; + isTofPion = nSigmaTOF[IndPionUp] < pidStates.tofNsigmaCut[IndPionUp] && nSigmaTOF[IndPionUp] > pidStates.tofNsigmaCut[IndPionLow]; + isTofKaon = nSigmaTOF[IndKaonUp] < pidStates.tofNsigmaCut[IndKaonUp] && nSigmaTOF[IndKaonUp] > pidStates.tofNsigmaCut[IndKaonLow]; + isTofProton = nSigmaTOF[IndProtonUp] < pidStates.tofNsigmaCut[IndProtonUp] && nSigmaTOF[IndProtonUp] > pidStates.tofNsigmaCut[IndProtonLow]; + } else { return -1; } - if (track.pt() > cfgTofPtCut && track.hasTOF()) { - isPion = isTofPion && isDetectedPion; - isKaon = isTofKaon && isDetectedKaon; - isProton = isTofProton && isDetectedProton; + + if constexpr (requires { track.hasTOF(); }) { + if (track.pt() > cfgTofPtCut && !track.hasTOF()) { + return -1; + } + if (track.pt() > cfgTofPtCut && track.hasTOF()) { + isPion = isTofPion && isDetectedPion; + isKaon = isTofKaon && isDetectedKaon; + isProton = isTofProton && isDetectedProton; + } else { + isPion = isDetectedPion; + isKaon = isDetectedKaon; + isProton = isDetectedProton; + } } else { - isPion = isDetectedPion; - isKaon = isDetectedKaon; - isProton = isDetectedProton; + return -1; } if ((isPion && isKaon) || (isPion && isProton) || (isKaon && isProton)) { @@ -686,12 +806,15 @@ struct FlowGfwV02 { cfg.correctionsLoaded = true; } + // JWeights, filled by jflucWeightsLoader from a local file or CCDB, stores the same + // correction used by flowJSPCAnalysis: acceptance is 1/weightNUA and efficiency is weightEff. template double getJTrackAcceptance(TTrack const& track) { double wacc = 1; if constexpr (requires { track.weightNUA(); }) { - wacc = 1. / track.weightNUA(); + const double nua = track.weightNUA(); + wacc = (nua == 0.) ? 1. : 1. / nua; } return wacc; } @@ -709,31 +832,43 @@ struct FlowGfwV02 { template double getAcceptance(TTrack const& track, const double& vtxz) { - double wacc = 1; - if (cfg.mAcceptance) { - wacc = cfg.mAcceptance->getNUA(track.phi(), track.eta(), vtxz); + if constexpr (requires { track.weightNUA(); }) { + return getJTrackAcceptance(track); + } else { + double wacc = 1; + if (cfg.mAcceptance) { + wacc = cfg.mAcceptance->getNUA(track.phi(), track.eta(), vtxz); + } + return wacc; } - return wacc; } template double getEfficiency(TTrack const& track, const int& pid = PidCharged) { - double eff = 1.; - if (cfg.mEfficiency[pid]) { - eff = cfg.mEfficiency[pid]->GetBinContent(cfg.mEfficiency[pid]->FindBin(track.pt())); - } - if (eff == 0) { - return -1.; + if constexpr (requires { track.weightEff(); }) { + const double eff = getJTrackEfficiency(track); + if (eff == 0.) { + return -1.; + } + return eff; + } else { + double eff = 1.; + if (cfg.mEfficiency[pid]) { + eff = cfg.mEfficiency[pid]->GetBinContent(cfg.mEfficiency[pid]->FindBin(track.pt())); + } + if (eff == 0) { + return -1.; + } + return 1. / eff; } - return 1. / eff; } template bool eventSelected(TCollision const& collision, const int& multTrk, const float& centrality) { if (cfgEventCutFlags.cfgTVXinTRD) { - if (collision.alias_bit(kTVXinTRD)) { + if (collision.alias_bit(::kTVXinTRD)) { // TRD triggered // "CMTVX-B-NOPF-TRD,minbias_TVX" return 0; @@ -869,8 +1004,8 @@ struct FlowGfwV02 { template void fillOutputContainers(const float& centmult) { - constexpr double threshold = 1.01; - constexpr double minDnxAB = 1e-8; // skip events with vanishing V22 weight + constexpr double Threshold = 1.01; + constexpr double MinDnxAB = 1e-8; // skip events with vanishing V22 weight int bootstrap = fRndm->Integer(gfwMemberCache.nBootstrap); // Calculate V02 @@ -878,74 +1013,84 @@ struct FlowGfwV02 { double v22npt = 0; double ptMeanMid = pidStates.hPtMid[PidCharged]->GetMean(); double ptFractionMid = 0.; - double dnxAB = fGFW->Calculate(corrconfigs.at(0), 0, kTRUE).real(); // V22 weight for AB - auto valAB = fGFW->Calculate(corrconfigs.at(0), 0, kFALSE).real() / dnxAB; - if (std::abs(valAB) > threshold || std::isnan(valAB) || std::isinf(valAB) || dnxAB < minDnxAB) { - return; - } - double v22pt = valAB * ptMeanMid; - double WeightAB = (cfgUseMultiplicityFlowWeights) ? dnxAB : 1.0; - double WeightC = 1.0; - // Calculate V02 for each particle type - for (int pid = 0; pid < PidTotal; pid++) { - int normIndex = (cfgNormalizeByCharged) ? PidCharged : pid; - if (!(pidStates.hPtMid[normIndex]->Integral() > 0)) { - continue; // Mid pT distribution is not defined + double dnxAB = fGFW->Calculate(corrconfigs.at(0), 0, kTRUE).real(); // V22 weight for AB + double dnxABAB = fGFW->Calculate(corrconfigs.at(1), 0, kTRUE).real(); // V22 weight for ABAB + + // Check if the V22 weight is valid + const bool validAB = dnxAB >= MinDnxAB; + const bool validABAB = dnxABAB >= MinDnxAB; + const double valAB = validAB ? fGFW->Calculate(corrconfigs.at(0), 0, kFALSE).real() / dnxAB : 0.; + const double valABAB = validABAB ? fGFW->Calculate(corrconfigs.at(1), 0, kFALSE).real() / dnxABAB : 0.; + const bool acceptAB = validAB && std::isfinite(valAB) && std::abs(valAB) <= Threshold; + const bool acceptABAB = validABAB && std::isfinite(valABAB) && std::abs(valABAB) <= Threshold; + if (acceptAB && acceptABAB) { + double v22pt = valAB * ptMeanMid; + double weightAB = (cfgUseMultiplicityFlowWeights) ? dnxAB : 1.0; + double weightABAB = (cfgUseMultiplicityFlowWeights) ? dnxABAB : 1.0; + double weightC = 1.0; + // Calculate V02 for each particle type + for (int pid = 0; pid < PidTotal; pid++) { + int normIndex = (cfgNormalizeByCharged) ? PidCharged : pid; + if (!(pidStates.hPtMid[normIndex]->Integral() > 0)) { + continue; // Mid pT distribution is not defined + } + if (cfgUseMultiplicityFracWeights) { + weightC = pidStates.hPtMid[PidCharged]->Integral(); // Mean pt/npt C weight + } + for (int i = 1; i <= fSecondAxis->GetNbins(); i++) { + ptFractionMid = pidStates.hPtMid[pid]->GetBinContent(i) / pidStates.hPtMid[normIndex]->Integral(); + v22npt = valAB * ptFractionMid; + + switch (pid) { + case PidCharged: + registry.fill(HIST("analysis/charged/v22npt"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v22npt, weightC * weightAB); + registry.fill(HIST("analysis/charged/nchC"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC); + if (cfgUseMultiplicityFracWeights) { + registry.fill(HIST("analysis/charged/nchC3pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC * weightAB); + } + break; + case PidPions: + registry.fill(HIST("analysis/pion/v22npt"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v22npt, weightC * weightAB); + registry.fill(HIST("analysis/pion/nchC"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC); + if (cfgUseMultiplicityFracWeights) { + registry.fill(HIST("analysis/pion/nchC3pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC * weightAB); + } + break; + case PidKaons: + registry.fill(HIST("analysis/kaon/v22npt"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v22npt, weightC * weightAB); + registry.fill(HIST("analysis/kaon/nchC"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC); + if (cfgUseMultiplicityFracWeights) { + registry.fill(HIST("analysis/kaon/nchC3pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC * weightAB); + } + break; + case PidProtons: + registry.fill(HIST("analysis/proton/v22npt"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v22npt, weightC * weightAB); + registry.fill(HIST("analysis/proton/nchC"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC); + if (cfgUseMultiplicityFracWeights) { + registry.fill(HIST("analysis/proton/nchC3pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC * weightAB); + } + break; + default: + break; + } + } } + + // Calculate NCv22pt if (cfgUseMultiplicityFracWeights) { - WeightC = pidStates.hPtMid[PidCharged]->Integral(); // Mean pt/npt C weight + weightC = pidStates.hPtMid[PidCharged]->Integral(); } - for (int i = 1; i <= fSecondAxis->GetNbins(); i++) { - ptFractionMid = pidStates.hPtMid[pid]->GetBinContent(i) / pidStates.hPtMid[normIndex]->Integral(); - v22npt = valAB * ptFractionMid; - switch (pid) { - case PidCharged: - registry.fill(HIST("analysis/charged/v22npt"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v22npt, WeightC * WeightAB); - registry.fill(HIST("analysis/charged/nchC"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, WeightC); - if (cfgUseMultiplicityFracWeights) { - registry.fill(HIST("analysis/charged/nchC3pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, WeightC * WeightAB); - } - break; - case PidPions: - registry.fill(HIST("analysis/pion/v22npt"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v22npt, WeightC * WeightAB); - registry.fill(HIST("analysis/pion/nchC"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, WeightC); - if (cfgUseMultiplicityFracWeights) { - registry.fill(HIST("analysis/pion/nchC3pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, WeightC * WeightAB); - } - break; - case PidKaons: - registry.fill(HIST("analysis/kaon/v22npt"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v22npt, WeightC * WeightAB); - registry.fill(HIST("analysis/kaon/nchC"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, WeightC); - if (cfgUseMultiplicityFracWeights) { - registry.fill(HIST("analysis/kaon/nchC3pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, WeightC * WeightAB); - } - break; - case PidProtons: - registry.fill(HIST("analysis/proton/v22npt"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v22npt, WeightC * WeightAB); - registry.fill(HIST("analysis/proton/nchC"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, WeightC); - if (cfgUseMultiplicityFracWeights) { - registry.fill(HIST("analysis/proton/nchC3pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, WeightC * WeightAB); - } - break; - default: - break; - } + registry.fill(HIST("analysis/charged/v22pt"), bootstrap, centmult, v22pt, weightC * weightAB); + registry.fill(HIST("analysis/charged/v22"), bootstrap, centmult, valAB, weightAB); + registry.fill(HIST("analysis/charged/v24"), bootstrap, centmult, valABAB, weightABAB); + registry.fill(HIST("analysis/charged/ptC"), bootstrap, centmult, ptMeanMid, weightC); + if (cfgUseMultiplicityFracWeights) { + registry.fill(HIST("analysis/charged/v223pc"), bootstrap, centmult, valAB, weightC * weightAB); + registry.fill(HIST("analysis/charged/v224pc"), bootstrap, centmult, valAB, weightABAB); + registry.fill(HIST("analysis/charged/ptC3pc"), bootstrap, centmult, ptMeanMid, weightC * weightAB); } } - - // Calculate NCv22pt - if (cfgUseMultiplicityFracWeights) { - WeightC = pidStates.hPtMid[PidCharged]->Integral(); - } - - registry.fill(HIST("analysis/charged/v22pt"), bootstrap, centmult, v22pt, WeightC * WeightAB); - registry.fill(HIST("analysis/charged/v22"), bootstrap, centmult, valAB, WeightAB); - registry.fill(HIST("analysis/charged/ptC"), bootstrap, centmult, ptMeanMid, WeightC); - if (cfgUseMultiplicityFracWeights) { - registry.fill(HIST("analysis/charged/v223pc"), bootstrap, centmult, valAB, WeightC * WeightAB); - registry.fill(HIST("analysis/charged/ptC3pc"), bootstrap, centmult, ptMeanMid, WeightC * WeightAB); - } } // End of V02 // Calculate V0 @@ -956,16 +1101,16 @@ struct FlowGfwV02 { double ptMeanBackward = pidStates.hPtBackward[PidCharged]->GetMean(); double ptFractionForward = 0.; double ptFractionBackward = 0.; - double WeightA = 1.0; - double WeightB = 1.0; + double weightA = 1.0; + double weightB = 1.0; for (int pid = 0; pid < PidTotal; pid++) { int normIndex = (cfgNormalizeByCharged) ? PidCharged : pid; if (!(pidStates.hPtForward[normIndex]->Integral() > 0) || !(pidStates.hPtBackward[normIndex]->Integral() > 0)) { continue; // Forward or backward pT distribution is not defined } if (cfgUseMultiplicityFracWeights) { - WeightA = pidStates.hPtForward[PidCharged]->Integral(); - WeightB = pidStates.hPtBackward[PidCharged]->Integral(); + weightA = pidStates.hPtForward[PidCharged]->Integral(); + weightB = pidStates.hPtBackward[PidCharged]->Integral(); } for (int i = 1; i <= fSecondAxis->GetNbins(); i++) { ptFractionForward = pidStates.hPtForward[pid]->GetBinContent(i) / pidStates.hPtForward[normIndex]->Integral(); @@ -974,43 +1119,43 @@ struct FlowGfwV02 { v0corrBA = ptFractionBackward * ptMeanForward; switch (pid) { case PidCharged: - registry.fill(HIST("analysis/charged/v0AB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrAB, WeightA * WeightB); - registry.fill(HIST("analysis/charged/v0BA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrBA, WeightA * WeightB); - registry.fill(HIST("analysis/charged/nchA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, WeightA); - registry.fill(HIST("analysis/charged/nchB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, WeightB); + registry.fill(HIST("analysis/charged/v0AB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrAB, weightA * weightB); + registry.fill(HIST("analysis/charged/v0BA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrBA, weightA * weightB); + registry.fill(HIST("analysis/charged/nchA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, weightA); + registry.fill(HIST("analysis/charged/nchB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, weightB); if (cfgUseMultiplicityFracWeights) { - registry.fill(HIST("analysis/charged/nchA2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, WeightA * WeightB); - registry.fill(HIST("analysis/charged/nchB2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, WeightA * WeightB); + registry.fill(HIST("analysis/charged/nchA2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, weightA * weightB); + registry.fill(HIST("analysis/charged/nchB2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, weightA * weightB); } break; case PidPions: - registry.fill(HIST("analysis/pion/v0AB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrAB, WeightA * WeightB); - registry.fill(HIST("analysis/pion/v0BA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrBA, WeightA * WeightB); - registry.fill(HIST("analysis/pion/nchA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, WeightA); - registry.fill(HIST("analysis/pion/nchB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, WeightB); + registry.fill(HIST("analysis/pion/v0AB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrAB, weightA * weightB); + registry.fill(HIST("analysis/pion/v0BA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrBA, weightA * weightB); + registry.fill(HIST("analysis/pion/nchA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, weightA); + registry.fill(HIST("analysis/pion/nchB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, weightB); if (cfgUseMultiplicityFracWeights) { - registry.fill(HIST("analysis/pion/nchA2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, WeightA * WeightB); - registry.fill(HIST("analysis/pion/nchB2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, WeightA * WeightB); + registry.fill(HIST("analysis/pion/nchA2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, weightA * weightB); + registry.fill(HIST("analysis/pion/nchB2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, weightA * weightB); } break; case PidKaons: - registry.fill(HIST("analysis/kaon/v0AB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrAB, WeightA * WeightB); - registry.fill(HIST("analysis/kaon/v0BA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrBA, WeightA * WeightB); - registry.fill(HIST("analysis/kaon/nchA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, WeightA); - registry.fill(HIST("analysis/kaon/nchB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, WeightB); + registry.fill(HIST("analysis/kaon/v0AB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrAB, weightA * weightB); + registry.fill(HIST("analysis/kaon/v0BA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrBA, weightA * weightB); + registry.fill(HIST("analysis/kaon/nchA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, weightA); + registry.fill(HIST("analysis/kaon/nchB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, weightB); if (cfgUseMultiplicityFracWeights) { - registry.fill(HIST("analysis/kaon/nchA2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, WeightA * WeightB); - registry.fill(HIST("analysis/kaon/nchB2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, WeightA * WeightB); + registry.fill(HIST("analysis/kaon/nchA2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, weightA * weightB); + registry.fill(HIST("analysis/kaon/nchB2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, weightA * weightB); } break; case PidProtons: - registry.fill(HIST("analysis/proton/v0AB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrAB, WeightA * WeightB); - registry.fill(HIST("analysis/proton/v0BA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrBA, WeightA * WeightB); - registry.fill(HIST("analysis/proton/nchA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, WeightA); - registry.fill(HIST("analysis/proton/nchB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, WeightB); + registry.fill(HIST("analysis/proton/v0AB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrAB, weightA * weightB); + registry.fill(HIST("analysis/proton/v0BA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrBA, weightA * weightB); + registry.fill(HIST("analysis/proton/nchA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, weightA); + registry.fill(HIST("analysis/proton/nchB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, weightB); if (cfgUseMultiplicityFracWeights) { - registry.fill(HIST("analysis/proton/nchA2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, WeightA * WeightB); - registry.fill(HIST("analysis/proton/nchB2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, WeightA * WeightB); + registry.fill(HIST("analysis/proton/nchA2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, weightA * weightB); + registry.fill(HIST("analysis/proton/nchB2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, weightA * weightB); } break; default: @@ -1021,16 +1166,16 @@ struct FlowGfwV02 { // Calculate PID independent parts of V0 if (cfgUseMultiplicityFracWeights) { - WeightA = pidStates.hPtForward[PidCharged]->Integral(); - WeightB = pidStates.hPtBackward[PidCharged]->Integral(); + weightA = pidStates.hPtForward[PidCharged]->Integral(); + weightB = pidStates.hPtBackward[PidCharged]->Integral(); } - registry.fill(HIST("analysis/charged/ptA"), bootstrap, centmult, ptMeanForward, WeightA); - registry.fill(HIST("analysis/charged/ptB"), bootstrap, centmult, ptMeanBackward, WeightB); + registry.fill(HIST("analysis/charged/ptA"), bootstrap, centmult, ptMeanForward, weightA); + registry.fill(HIST("analysis/charged/ptB"), bootstrap, centmult, ptMeanBackward, weightB); if (cfgUseMultiplicityFracWeights) { - registry.fill(HIST("analysis/charged/ptA2pc"), bootstrap, centmult, ptMeanForward, WeightA * WeightB); - registry.fill(HIST("analysis/charged/ptB2pc"), bootstrap, centmult, ptMeanBackward, WeightA * WeightB); + registry.fill(HIST("analysis/charged/ptA2pc"), bootstrap, centmult, ptMeanForward, weightA * weightB); + registry.fill(HIST("analysis/charged/ptB2pc"), bootstrap, centmult, ptMeanBackward, weightA * weightB); } - registry.fill(HIST("analysis/charged/ptAB"), bootstrap, centmult, ptMeanForward * ptMeanBackward, WeightA * WeightB); + registry.fill(HIST("analysis/charged/ptAB"), bootstrap, centmult, ptMeanForward * ptMeanBackward, weightA * weightB); } // End of V0 } // End of Filling @@ -1155,10 +1300,17 @@ struct FlowGfwV02 { template bool trackSelected(TTrack const& track) { - if (cfgTrackCuts.cfgDCAxyNSigma && (std::fabs(track.dcaXY()) > fPtDepDCAxy->Eval(track.pt()))) { - return false; + if constexpr (requires { track.trackSel(); }) { + const uint32_t mask = trackBitMask; + return (track.trackSel() & mask) == mask; + } else if constexpr (requires { track.dcaXY(); track.tpcNClsCrossedRows(); track.tpcNClsFound(); track.itsNCls(); }) { + if (cfgTrackCuts.cfgDCAxyNSigma && (std::fabs(track.dcaXY()) > fPtDepDCAxy->Eval(track.pt()))) { + return false; + } + return ((track.tpcNClsCrossedRows() >= cfgTrackCuts.cfgNTPCXrows) && (track.tpcNClsFound() >= cfgTrackCuts.cfgNTPCCls) && (track.itsNCls() >= cfgTrackCuts.cfgMinNITSCls)); + } else { + return true; } - return ((track.tpcNClsCrossedRows() >= cfgTrackCuts.cfgNTPCXrows) && (track.tpcNClsFound() >= cfgTrackCuts.cfgNTPCCls) && (track.itsNCls() >= cfgTrackCuts.cfgMinNITSCls)); } template @@ -1248,65 +1400,55 @@ struct FlowGfwV02 { } template - inline void fillPidQA(TTrack const& track, const int& pid) + void fillPidDedxQA(TTrack const& track, const int& pid) { - // Fill Nsigma QA - if (!cfgUseItsPID) { - if (ft == kBefore) { - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TofTpcNsigma_pions"), track.tpcNSigmaPi(), track.tofNSigmaPi(), track.pt()); - if (cfgGetdEdx) { - double tpcExpSignalPi = track.tpcSignal() - (track.tpcNSigmaPi() * track.tpcExpSigmaPi()); - - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TpcdEdx_ptwise_pions"), track.pt(), track.tpcSignal(), track.tofNSigmaPi()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpTpcdEdx_ptwise_pions"), track.pt(), tpcExpSignalPi, track.tofNSigmaPi()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpSigma_ptwise_pions"), track.pt(), track.tpcExpSigmaPi(), track.tofNSigmaPi()); - } - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TofTpcNsigma_kaons"), track.tpcNSigmaKa(), track.tofNSigmaKa(), track.pt()); - if (cfgGetdEdx) { - double tpcExpSignalKa = track.tpcSignal() - (track.tpcNSigmaKa() * track.tpcExpSigmaKa()); - - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TpcdEdx_ptwise_kaons"), track.pt(), track.tpcSignal(), track.tofNSigmaKa()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpTpcdEdx_ptwise_kaons"), track.pt(), tpcExpSignalKa, track.tofNSigmaKa()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpSigma_ptwise_kaons"), track.pt(), track.tpcExpSigmaKa(), track.tofNSigmaKa()); - } - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TofTpcNsigma_protons"), track.tpcNSigmaPr(), track.tofNSigmaPr(), track.pt()); - if (cfgGetdEdx) { - double tpcExpSignalPr = track.tpcSignal() - (track.tpcNSigmaPr() * track.tpcExpSigmaPr()); + if constexpr (requires { track.tpcSignal(); track.tpcExpSigmaPi(); track.tpcExpSigmaKa(); track.tpcExpSigmaPr(); }) { + if (!cfgGetdEdx) { + return; + } + if (ft == kBefore || pid == PidPions) { + const double tpcExpSignalPi = track.tpcSignal() - (track.tpcNSigmaPi() * track.tpcExpSigmaPi()); + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TpcdEdx_ptwise_pions"), track.pt(), track.tpcSignal(), track.tofNSigmaPi()); + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpTpcdEdx_ptwise_pions"), track.pt(), tpcExpSignalPi, track.tofNSigmaPi()); + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpSigma_ptwise_pions"), track.pt(), track.tpcExpSigmaPi(), track.tofNSigmaPi()); + } + if (ft == kBefore || pid == PidKaons) { + const double tpcExpSignalKa = track.tpcSignal() - (track.tpcNSigmaKa() * track.tpcExpSigmaKa()); + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TpcdEdx_ptwise_kaons"), track.pt(), track.tpcSignal(), track.tofNSigmaKa()); + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpTpcdEdx_ptwise_kaons"), track.pt(), tpcExpSignalKa, track.tofNSigmaKa()); + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpSigma_ptwise_kaons"), track.pt(), track.tpcExpSigmaKa(), track.tofNSigmaKa()); + } + if (ft == kBefore || pid == PidProtons) { + const double tpcExpSignalPr = track.tpcSignal() - (track.tpcNSigmaPr() * track.tpcExpSigmaPr()); + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TpcdEdx_ptwise_protons"), track.pt(), track.tpcSignal(), track.tofNSigmaPr()); + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpTpcdEdx_ptwise_protons"), track.pt(), tpcExpSignalPr, track.tofNSigmaPr()); + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpSigma_ptwise_protons"), track.pt(), track.tpcExpSigmaPr(), track.tofNSigmaPr()); + } + } + } - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TpcdEdx_ptwise_protons"), track.pt(), track.tpcSignal(), track.tofNSigmaPr()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpTpcdEdx_ptwise_protons"), track.pt(), tpcExpSignalPr, track.tofNSigmaPr()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpSigma_ptwise_protons"), track.pt(), track.tpcExpSigmaPr(), track.tofNSigmaPr()); - } - } else if (ft == kAfter) { - if (pid == PidPions) { + template + inline void fillPidQA(TTrack const& track, const int& pid) + { + // Fill Nsigma QA. The derived tables store PID bits and have no nSigma columns. + if constexpr (requires { track.tpcNSigmaPi(); track.tofNSigmaPi(); }) { + if (!cfgUseItsPID) { + if (ft == kBefore) { registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TofTpcNsigma_pions"), track.tpcNSigmaPi(), track.tofNSigmaPi(), track.pt()); - if (cfgGetdEdx) { - double tpcExpSignalPi = track.tpcSignal() - (track.tpcNSigmaPi() * track.tpcExpSigmaPi()); - - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TpcdEdx_ptwise_pions"), track.pt(), track.tpcSignal(), track.tofNSigmaPi()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpTpcdEdx_ptwise_pions"), track.pt(), tpcExpSignalPi, track.tofNSigmaPi()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpSigma_ptwise_pions"), track.pt(), track.tpcExpSigmaPi(), track.tofNSigmaPi()); - } - } - if (pid == PidKaons) { registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TofTpcNsigma_kaons"), track.tpcNSigmaKa(), track.tofNSigmaKa(), track.pt()); - if (cfgGetdEdx) { - double tpcExpSignalKa = track.tpcSignal() - (track.tpcNSigmaKa() * track.tpcExpSigmaKa()); - - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TpcdEdx_ptwise_kaons"), track.pt(), track.tpcSignal(), track.tofNSigmaKa()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpTpcdEdx_ptwise_kaons"), track.pt(), tpcExpSignalKa, track.tofNSigmaKa()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpSigma_ptwise_kaons"), track.pt(), track.tpcExpSigmaKa(), track.tofNSigmaKa()); - } - } - if (pid == PidProtons) { registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TofTpcNsigma_protons"), track.tpcNSigmaPr(), track.tofNSigmaPr(), track.pt()); - if (cfgGetdEdx) { - double tpcExpSignalPr = track.tpcSignal() - (track.tpcNSigmaPr() * track.tpcExpSigmaPr()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TpcdEdx_ptwise_protons"), track.pt(), track.tpcSignal(), track.tofNSigmaPr()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpTpcdEdx_ptwise_protons"), track.pt(), tpcExpSignalPr, track.tofNSigmaPr()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpSigma_ptwise_protons"), track.pt(), track.tpcExpSigmaPr(), track.tofNSigmaPr()); + } else if (ft == kAfter) { + if (pid == PidPions) { + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TofTpcNsigma_pions"), track.tpcNSigmaPi(), track.tofNSigmaPi(), track.pt()); + } + if (pid == PidKaons) { + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TofTpcNsigma_kaons"), track.tpcNSigmaKa(), track.tofNSigmaKa(), track.pt()); + } + if (pid == PidProtons) { + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TofTpcNsigma_protons"), track.tpcNSigmaPr(), track.tofNSigmaPr(), track.pt()); } } + fillPidDedxQA(track, pid); } } } @@ -1317,13 +1459,17 @@ struct FlowGfwV02 { double wacc = getAcceptance(track, vtxz); registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("phi_eta_vtxZ"), track.phi(), track.eta(), vtxz, (ft == kAfter) ? wacc : 1.0); - registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("pt_dcaXY_dcaZ"), track.pt(), track.dcaXY(), track.dcaZ()); + if constexpr (requires { track.dcaXY(); track.dcaZ(); }) { + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("pt_dcaXY_dcaZ"), track.pt(), track.dcaXY(), track.dcaZ()); + } - registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("chi2prTPCcls"), track.tpcChi2NCl()); - registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("chi2prITScls"), track.itsChi2NCl()); - registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("nTPCClusters"), track.tpcNClsFound()); - registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("nITSClusters"), track.itsNCls()); - registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("nTPCCrossedRows"), track.tpcNClsCrossedRows()); + if constexpr (requires { track.tpcChi2NCl(); track.itsChi2NCl(); track.tpcNClsFound(); track.itsNCls(); track.tpcNClsCrossedRows(); }) { + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("chi2prTPCcls"), track.tpcChi2NCl()); + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("chi2prITScls"), track.itsChi2NCl()); + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("nTPCClusters"), track.tpcNClsFound()); + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("nITSClusters"), track.itsNCls()); + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("nTPCCrossedRows"), track.tpcNClsCrossedRows()); + } if (ft == kAfter) { registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("pt_ref"), track.pt()); @@ -1413,9 +1559,54 @@ struct FlowGfwV02 { const XAxis xaxis{.centrality = collision.multiplicity(), .multiplicity = tracks.size()}; registry.fill(HIST("eventQA/after/centrality"), xaxis.centrality); registry.fill(HIST("eventQA/after/multiplicity"), xaxis.multiplicity); - // processCollision(collision, tracks, xaxis); + processCollision(collision, tracks, xaxis); + } + PROCESS_SWITCH(FlowGfwV02, processCFDerivedCorrected, "Process CF derived data with JWeights acceptance and efficiency corrections", false); + + void processDerived(aod::GfwCollision const& collision, soa::Filtered const& tracks) + { + int run = collision.runNumber(); + if (run != lastRun) { + lastRun = run; + LOGF(info, "run = %d", run); + } + loadCorrections(run); + + if ((collision.eventSel() & eventBitMask) != eventBitMask) { + return; + } + if (std::abs(collision.posZ()) > cfgEventCuts.cfgZvtxMax) { + return; + } + + const XAxis xaxis{.centrality = static_cast(cfgCentralityFactor * collision.centrality()), .multiplicity = static_cast(collision.multiplicity())}; + registry.fill(HIST("eventQA/after/centrality"), xaxis.centrality); + registry.fill(HIST("eventQA/after/multiplicity"), xaxis.multiplicity); + processCollision(collision, tracks, xaxis); + } + PROCESS_SWITCH(FlowGfwV02, processDerived, "Process analysis for GFW derived data", false); + + void processDerivedCorrected(aod::GfwCollision const& collision, soa::Filtered> const& tracks) + { + int run = collision.runNumber(); + if (run != lastRun) { + lastRun = run; + LOGF(info, "run = %d", run); + } + + if ((collision.eventSel() & eventBitMask) != eventBitMask) { + return; + } + if (std::abs(collision.posZ()) > cfgEventCuts.cfgZvtxMax) { + return; + } + + const XAxis xaxis{.centrality = static_cast(cfgCentralityFactor * collision.centrality()), .multiplicity = static_cast(collision.multiplicity())}; + registry.fill(HIST("eventQA/after/centrality"), xaxis.centrality); + registry.fill(HIST("eventQA/after/multiplicity"), xaxis.multiplicity); + processCollision(collision, tracks, xaxis); } - PROCESS_SWITCH(FlowGfwV02, processCFDerivedCorrected, "Process analysis for CF derived data with corrections", false); + PROCESS_SWITCH(FlowGfwV02, processDerivedCorrected, "Process GFW derived data with JWeights acceptance and efficiency corrections", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGCF/JCorran/Core/FlowJSPCAnalysis.cxx b/PWGCF/JCorran/Core/FlowJSPCAnalysis.cxx index 0c1927d9579..3533e4c4987 100644 --- a/PWGCF/JCorran/Core/FlowJSPCAnalysis.cxx +++ b/PWGCF/JCorran/Core/FlowJSPCAnalysis.cxx @@ -119,11 +119,25 @@ void FlowJSPCAnalysis::calculateCorrelators(const int fCentBin) // Histogram filling fillHistograms(fCentBin, j, correlationNum, correlationDenom, weightCorrelationNum, weightCorrelationDenom); + } - correlationNum = 0.; - weightCorrelationNum = 0.; - correlationDenom = 0.; - weightCorrelationDenom = 0.; + // Effective multiplicity N_m (arXiv:2606.10258 Eq. IV.2 / manuscript eq:Neff): + // 1/N_m^{m-1} = sum_i M_m(N_i) N_i^{-(m-1)} / sum_i M_m(N_i), + // with M_m = N(N-1)...(N-m+1) = fCorrelDenoms[m-1] and N = Re(Q_{0,1}). + // TProfile stores _comb; invert offline as N_m = content^{-1/(m-1)}. + // 3SPC → fN3 (m=3); 4SPC → fN4 (m=4). + if (mHistRegistry && qvecs) { + const double nSel = qvecs->QvectorQC[0][1].Re(); + const float centX = static_cast(fCentBin) + 0.5f; + if (nSel > 0.0 && std::isfinite(nSel)) { + if (mWhichSPC == 0 && fCorrelDenoms[2] > 0.0) { + const double invN3Pow = 1.0 / (nSel * nSel); // N^{-(3-1)} + mHistRegistry->fill(HIST("fN3"), centX, invN3Pow, fCorrelDenoms[2]); + } else if (mWhichSPC == 1 && fCorrelDenoms[3] > 0.0) { + const double invN4Pow = 1.0 / (nSel * nSel * nSel); // N^{-(4-1)} + mHistRegistry->fill(HIST("fN4"), centX, invN4Pow, fCorrelDenoms[3]); + } + } } } diff --git a/PWGCF/JCorran/Core/FlowJSPCAnalysis.h b/PWGCF/JCorran/Core/FlowJSPCAnalysis.h index 59a677dccd7..425c78ee9d0 100644 --- a/PWGCF/JCorran/Core/FlowJSPCAnalysis.h +++ b/PWGCF/JCorran/Core/FlowJSPCAnalysis.h @@ -29,6 +29,7 @@ #include +#include #include #include #include @@ -42,6 +43,27 @@ class FlowJSPCAnalysis int getCentBin(float cValue); using JQVectorsT = JQVectors; + /// Fill-grid size for Q_{n,p}: (v8 * nPartDen)+1 harmonics, nPartDen+1 powers. + static constexpr uint32_t Nh3p = 49; // 3-particle SPC, 6-particle denominator + static constexpr uint32_t Nk3p = 7; + static constexpr uint32_t Nh4p = 65; // 4-particle SPC, 8-particle denominator + static constexpr uint32_t Nk4p = 9; + static constexpr uint32_t NhFull = 113; + static constexpr uint32_t NkFull = 15; + void qVectorGrid(int whichSPC, uint32_t& nhUse, uint32_t& nkUse) + { + mWhichSPC = whichSPC; + if (whichSPC == 0) { + nhUse = Nh3p; + nkUse = Nk3p; + } else if (whichSPC == 1) { + nhUse = Nh4p; + nkUse = Nk4p; + } else { + nhUse = NhFull; + nkUse = NkFull; + } + } inline void setQvectors(const JQVectorsT* _qvecs) { qvecs = _qvecs; } void correlation(int c_nPart, int c_nHarmo, int* harmo, double* correlData); void calculateCorrelators(const int fCentBin); @@ -57,6 +79,10 @@ class FlowJSPCAnalysis return; } mHistRegistry->add("FullCentrality", "FullCentrality", o2::framework::HistType::kTH1D, {{100, 0., 100.}}, true); + // Stores _comb = 1/N_m^{m-1} (arXiv:2606.10258 Eq. IV.2). + // Invert offline: N_m = content^{-1/(m-1)}. Weight is M_m. + mHistRegistry->add("fN3", "1/N_{3}^{2};centrality class;1/N_{3}^{2}", {o2::framework::HistType::kTProfile, {{9, 0., 9.}}}, true); + mHistRegistry->add("fN4", "1/N_{4}^{3};centrality class;1/N_{4}^{3}", {o2::framework::HistType::kTProfile, {{9, 0., 9.}}}, true); mHistRegistry->add("Centrality_0/fResults", "Numerators and denominators", {o2::framework::HistType::kTProfile, {{24, 0., 24.}}}, true); mHistRegistry->add("Centrality_0/fCovResults", "Covariance N*D", {o2::framework::HistType::kTProfile, {{48, 0., 48.}}}, true); mHistRegistry->add("Centrality_0/phiBefore", "Phi before", {o2::framework::HistType::kTH1D, {{100, 0., o2::constants::math::TwoPI}}}, true); @@ -67,13 +93,13 @@ class FlowJSPCAnalysis } } - void setCorrSet(int obsInd, int harmo[8]) + void setCorrSet(int obsInd, int const harmo[8]) { for (int i = 0; i < 8; i++) { fHarmosArray[obsInd][i] = harmo[i]; } } - void setFullCorrSet(int harmo[12][8]) + void setFullCorrSet(int const harmo[12][8]) { memcpy(fHarmosArray, harmo, sizeof(int) * 12 * 8); } @@ -92,13 +118,14 @@ class FlowJSPCAnalysis private: const int mNqHarmos = 113; ///< Highest harmo for Q(n,p): (v8*14part)+1. const int mNqPowers = 15; ///< Max power for Q(n,p): 14part+1. - const JQVectorsT* qvecs; + const JQVectorsT* qvecs = nullptr; o2::framework::HistogramRegistry* mHistRegistry = nullptr; - int fHarmosArray[12][8]; + int fHarmosArray[12][8] = {{0}}; - double fCorrelDenoms[14]; + double fCorrelDenoms[14] = {0}; + int mWhichSPC = 0; ClassDefNV(FlowJSPCAnalysis, 1); }; diff --git a/PWGCF/JCorran/Core/FlowJSPCObservables.h b/PWGCF/JCorran/Core/FlowJSPCObservables.h index 440d70c8bf2..e5db30a94e7 100644 --- a/PWGCF/JCorran/Core/FlowJSPCObservables.h +++ b/PWGCF/JCorran/Core/FlowJSPCObservables.h @@ -38,42 +38,53 @@ class FlowJSPCObservables switch (index) { case 0: { LOGF(info, "Computing three harmonic SPC"); - int harmonicArray01[maxNrComb][8] = { - {3, 6, -3, -3, 0, 0, 0, 0}, - {3, 4, -2, -2, 0, 0, 0, 0}, + // fResults slot j: num at 2j+0.5, denom at 2j+1.5. arXiv:2606.10258 nonflow refs in unused slots. + int const harmonicArray01[maxNrComb][8] = { + {3, 6, -3, -3, 0, 0, 0, 0}, // 0: C633 = + {3, 4, -2, -2, 0, 0, 0, 0}, // 1: C422 = {3, 8, -4, -4, 0, 0, 0, 0}, - {3, 2, 4, -6, 0, 0, 0, 0}, - {3, 2, 3, -5, 0, 0, 0, 0}, + {3, 2, 4, -6, 0, 0, 0, 0}, // 3: C246 = + {3, 2, 3, -5, 0, 0, 0, 0}, // 4: C235 = {3, 3, 4, -7, 0, 0, 0, 0}, // These are three harmonic SPC!! {3, 2, 5, -7, 0, 0, 0, 0}, // These are three harmonic SPC!! {3, 3, 5, -8, 0, 0, 0, 0}, // These are three harmonic SPC!! - {0, 6, -2, -2, -2, 0, 0, 0}, - {0, 2, -3, -4, 5, 0, 0, 0}, - {0, 2, -3, -3, 4, 0, 0, 0}, - {0, 3, 3, -2, -2, -2, 0, 0}}; + // {0, 6, -2, -2, -2, 0, 0, 0}, + // {0, 2, -3, -4, 5, 0, 0, 0}, + // {0, 2, -3, -3, 4, 0, 0, 0}, + // {0, 3, 3, -2, -2, -2, 0, 0}, + {3, 1, 1, -2, 0, 0, 0, 0}, // 8: C112 = , Eqs. (IV.7), (IV.18) + {3, 1, 2, -3, 0, 0, 0, 0}, // 9: C123 = , Eqs. (IV.8), (IV.18) + {0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0}}; memcpy(harmonicArray, harmonicArray01, sizeof(int) * maxNrComb * 8); } break; case 1: { LOGF(info, "Computing four harmonic SPC"); - int harmonicArray02[maxNrComb][8] = { - {4, 6, -2, -2, -2, 0, 0, 0}, + // fResults slot j: num at 2j+0.5, denom at 2j+1.5. arXiv:2606.10258: c1{4}=<<4>>-2<<2>>^2 after averaging, Eq. (IV.6). + int const harmonicArray02[maxNrComb][8] = { + {4, 6, -2, -2, -2, 0, 0, 0}, // 0: C6222 = {4, 2, -3, -4, 5, 0, 0, 0}, {4, 2, -3, -3, 4, 0, 0, 0}, {4, 2, 2, 3, -7, 0, 0, 0}, // These are three harmonic SPC!! {4, 2, 2, 4, -8, 0, 0, 0}, // These are three harmonic SPC!! {4, 2, 7, -4, -5, 0, 0, 0}, {4, 3, -4, -4, 5, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0}, + // {0, 0, 0, 0, 0, 0, 0, 0}, + // {0, 0, 0, 0, 0, 0, 0, 0}, + // {0, 0, 0, 0, 0, 0, 0, 0}, + // {0, 0, 0, 0, 0, 0, 0, 0}, + // {0, 0, 0, 0, 0, 0, 0, 0}, + {4, 1, 1, -1, -1, 0, 0, 0}, // 7: <<4>>_{1,1,-1,-1} for c1{4} + {2, 1, -1, 0, 0, 0, 0, 0}, // 8: <<2>>_{1,-1} = + {3, 1, 1, -2, 0, 0, 0, 0}, // 9: C112 on the 4-particle sample (mixed-order Eq. (IV.19)) {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}}; memcpy(harmonicArray, harmonicArray02, sizeof(int) * maxNrComb * 8); } break; case 2: { LOGF(info, "Computing five and six harmonic SPC"); - int harmonicArray03[maxNrComb][8] = { + int const harmonicArray03[maxNrComb][8] = { {5, 3, 3, -2, -2, -2, 0, 0}, {5, 2, 2, -3, 4, -5, 0, 0}, {5, 2, 3, 3, -4, -4, 0, 0}, @@ -90,7 +101,7 @@ class FlowJSPCObservables } break; case 3: { LOGF(info, "Computing slected five harmonic SPC"); - int harmonicArray04[maxNrComb][8] = { + int const harmonicArray04[maxNrComb][8] = { {5, 3, 3, -2, -2, -2, 0, 0}, {0, 2, 2, -3, 4, -5, 0, 0}, {5, 2, 3, 3, -4, -4, 0, 0}, diff --git a/PWGCF/JCorran/Core/JCORRANLinkDef.h b/PWGCF/JCorran/Core/JCORRANLinkDef.h index d0e9bef308d..e48231cbdf1 100644 --- a/PWGCF/JCorran/Core/JCORRANLinkDef.h +++ b/PWGCF/JCorran/Core/JCORRANLinkDef.h @@ -9,8 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef PWGCF_JCORRAN_CORE_JCORRANLINKDEF_H_ -#define PWGCF_JCORRAN_CORE_JCORRANLINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -22,5 +21,3 @@ #pragma link C++ class FlowJSPCAnalysis + ; #pragma link C++ class FlowJSPCObservables + ; #pragma link C++ class JEPFlowAnalysis + ; - -#endif // PWGCF_JCORRAN_CORE_JCORRANLINKDEF_H_ diff --git a/PWGCF/JCorran/Core/JQVectors.h b/PWGCF/JCorran/Core/JQVectors.h index d0ba705c32f..aadb821fc2b 100644 --- a/PWGCF/JCorran/Core/JQVectors.h +++ b/PWGCF/JCorran/Core/JQVectors.h @@ -19,6 +19,8 @@ #include +#include +#include #include #include @@ -52,11 +54,16 @@ class JQVectors : public std::conditional_t, JQ using hasInvMass = decltype(std::declval().invMass()); template - inline void Calculate(JInputClass& inputInst, float etamin, float etamax, float massMin = 0.0f, float massMax = 999.9f) + inline void Calculate(JInputClass& inputInst, float etamin, float etamax, float massMin = 0.0f, float massMax = 999.9f, + uint32_t nhUse = nh, uint32_t nkUse = nk) { + // nhUse/nkUse limit the filled (harmonic, power) grid. Defaults keep the full template size. + const uint32_t nH = std::min(nhUse, nh); + const uint32_t nK = std::min(nkUse, nk); + // calculate Q-vector for QC method ( no subgroup ) - for (UInt_t ih = 0; ih < nh; ++ih) { - for (UInt_t ik = 0; ik < nk; ++ik) { + for (UInt_t ih = 0; ih < nH; ++ih) { + for (UInt_t ik = 0; ik < nK; ++ik) { QvectorQC[ih][ik] = Q(0, 0); if constexpr (gap) { for (UInt_t isub = 0; isub < 2; ++isub) @@ -64,7 +71,7 @@ class JQVectors : public std::conditional_t, JQ } } } - for (auto& track : inputInst) { + for (auto const& track : inputInst) { if (track.eta() < -etamax || track.eta() > etamax) continue; using JInputClassIter = typename JInputClass::iterator; @@ -74,10 +81,15 @@ class JQVectors : public std::conditional_t, JQ } UInt_t isub = (UInt_t)(track.eta() > 0.0); - for (UInt_t ih = 0; ih < nh; ++ih) { + const Double_t phi = track.phi(); + const Double_t c1 = TMath::Cos(phi); + const Double_t s1 = TMath::Sin(phi); + Double_t cn = 1.0; // cos(ih * phi), ih = 0 + Double_t sn = 0.0; // sin(ih * phi) + for (UInt_t ih = 0; ih < nH; ++ih) { Double_t tf = 1.0; - for (UInt_t ik = 0; ik < nk; ++ik) { - Q q(tf * TMath::Cos(ih * track.phi()), tf * TMath::Sin(ih * track.phi())); + for (UInt_t ik = 0; ik < nK; ++ik) { + Q q(tf * cn, tf * sn); QvectorQC[ih][ik] += q; if constexpr (gap) { @@ -90,6 +102,10 @@ class JQVectors : public std::conditional_t, JQ if constexpr (std::experimental::is_detected::value) tf *= track.weightEff(); } + const Double_t cnNext = cn * c1 - sn * s1; + const Double_t snNext = cn * s1 + sn * c1; + cn = cnNext; + sn = snNext; } } } diff --git a/PWGCF/JCorran/Tasks/CMakeLists.txt b/PWGCF/JCorran/Tasks/CMakeLists.txt index 465083509d0..1cd594f8f19 100644 --- a/PWGCF/JCorran/Tasks/CMakeLists.txt +++ b/PWGCF/JCorran/Tasks/CMakeLists.txt @@ -39,6 +39,16 @@ o2physics_add_dpl_workflow(epdzeroflow-analysis PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(j-v02-dmeson-analysis + SOURCES jV02DmesonAnalysis.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore O2Physics::JCorran + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(j-v02-dmeson-toy + SOURCES jV02DmesonToy.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(j-fluc-efficiency-task SOURCES jFlucEfficiencyTask.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::JCorran diff --git a/PWGCF/JCorran/Tasks/flowJSPCAnalysis.cxx b/PWGCF/JCorran/Tasks/flowJSPCAnalysis.cxx index 6892ab58658..cfe7f931a39 100644 --- a/PWGCF/JCorran/Tasks/flowJSPCAnalysis.cxx +++ b/PWGCF/JCorran/Tasks/flowJSPCAnalysis.cxx @@ -109,6 +109,8 @@ struct flowJSPCAnalysis { std::unique_ptr multCutFormula; std::array multCutFormulaParamIndex; + uint32_t mNhUse = FlowJSPCAnalysis::NhFull; + uint32_t mNkUse = FlowJSPCAnalysis::NkFull; void init(InitContext const&) { @@ -118,6 +120,8 @@ struct flowJSPCAnalysis { spcObservables.setSPCObservables(cfgWhichSPC); spcAnalysis.setFullCorrSet(spcObservables.harmonicArray); + spcAnalysis.qVectorGrid(cfgWhichSPC, mNhUse, mNkUse); + LOGF(info, "Q-vector fill grid: nh=%u nk=%u (cfgWhichSPC=%d)", mNhUse, mNkUse, cfgWhichSPC.value); histManager.setHistRegistryQA(&qaHistRegistry); histManager.setDebugLog(false); @@ -186,7 +190,7 @@ struct flowJSPCAnalysis { if (cfgFillQA) histManager.fillEventQA<1>(collision, cBin, cent, nTracks); - jqvecs.Calculate(tracks, 0.0, cfgTrackCuts.cfgEtaMax); + jqvecs.Calculate(tracks, 0.0, cfgTrackCuts.cfgEtaMax, 0.0f, 999.9f, mNhUse, mNkUse); spcAnalysis.setQvectors(&jqvecs); spcAnalysis.calculateCorrelators(cBin); } diff --git a/PWGCF/JCorran/Tasks/jV02DmesonAnalysis.cxx b/PWGCF/JCorran/Tasks/jV02DmesonAnalysis.cxx new file mode 100644 index 00000000000..e93b7ec3f7a --- /dev/null +++ b/PWGCF/JCorran/Tasks/jV02DmesonAnalysis.cxx @@ -0,0 +1,561 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file jV02DmesonAnalysis.cxx +/// \brief D0 v02(pT) on the JCorran derived tables +/// \author Dong Jo Kim (djkim@jyu.fi) +/// +/// v02(pT) = _c / (_c ). +/// X and Y are the corrected Q2 of two forward detectors (default FT0A x FT0C) +/// from the combined Qvectors table, with the same indexing as jEPDzeroFlowAnalysis. +/// Z_D(pT) = N_D(pT) / N_ch. N_D comes from CF2ProngTracks (D0 and D0bar). +/// N_ch comes from CFTracks. With JWeights (jflucWeightsLoader, hadron NUE only), +/// sum 1/epsilon_h into N_ch for hadrons and optionally for D0 denominators +/// (processReferenceCorrected / processD0HadronNch). Full D0 candidate efficiency +/// (J2ProngWeights, processD0Corrected) is not wired in the default train yet. +/// Both derived tables are indexed to CFCollisions, +/// so the original collision is matched through CFCollRefs (one row per kept +/// collision, written by filter-correlations with cfgTransientTables). +/// The D azimuth is not part of the Q-vector. +/// Do not divide by an extra resolution: it cancels in the ratio. +/// +/// Definition: arXiv:2506.18690, Appendix A. + +#include "PWGCF/DataModel/CorrelationsDerived.h" +#include "PWGCF/JCorran/DataModel/JCatalyst.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Qvectors.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +struct JV02DmesonAnalysis { + enum Detector { + kFT0C = 0, + kFT0A, + kFT0M, + kFV0A, + kTPCpos, + kTPCneg, + kTPCall + }; + static constexpr int NDetectors = 7; + static constexpr int NCorrections = 4; + static constexpr int FullyCorrected = 3; + static constexpr int StepAccepted = 5; + static constexpr float InvalidQvector = 900.f; + + using MyCollisions = soa::Join; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + Configurable cfgCentEst{"cfgCentEst", "FT0C", "Centrality estimator: FT0C or FT0M"}; + Configurable cfgRefAName{"cfgRefAName", "FT0A", "Reference detector A: FT0A, FT0C, FT0M, FV0A, TPCpos, TPCneg, TPCall"}; + Configurable cfgRefBName{"cfgRefBName", "FT0C", "Reference detector B"}; + Configurable cfgQvecMode{"cfgQvecMode", 0, "Harmonic slot in qvector-table cfgnMods. 0 is v2 when cfgnMods starts at 2"}; + Configurable cfgNSubsample{"cfgNSubsample", 10, "Number of bootstrap subsamples"}; + Configurable cfgSeed{"cfgSeed", 1, "Seed for the subsample index"}; + Configurable cfgMaxOccupancy{"cfgMaxOccupancy", 999999, "Maximum track occupancy"}; + Configurable cfgMinOccupancy{"cfgMinOccupancy", 0, "Minimum track occupancy"}; + Configurable cfgVtxZ{"cfgVtxZ", 10.f, "Maximum |z_vtx| (cm)"}; + Configurable cfgCentMin{"cfgCentMin", 0.f, "Minimum centrality"}; + Configurable cfgCentMax{"cfgCentMax", 80.f, "Maximum centrality"}; + Configurable cfgEtaMax{"cfgEtaMax", 0.8f, "Maximum |eta| of CF tracks used for N_ch"}; + Configurable cfgPtMinCh{"cfgPtMinCh", 0.2f, "Minimum charged-track pT (GeV/c)"}; + Configurable cfgPtMaxCh{"cfgPtMaxCh", 3.f, "Maximum charged-track pT (GeV/c)"}; + Configurable cfgPtMinD{"cfgPtMinD", 1.f, "Minimum D0 pT (GeV/c)"}; + Configurable cfgPtMaxD{"cfgPtMaxD", 24.f, "Maximum D0 pT (GeV/c)"}; + Configurable cfgMinNch{"cfgMinNch", 10.f, "Minimum N_ch"}; + Configurable cfgMinSumAmpl{"cfgMinSumAmpl", 1e-4f, "Minimum Q-vector amplitude"}; + Configurable cfgRequireSel8{"cfgRequireSel8", true, "Require sel8"}; + Configurable cfgCutNoSameBunchPileup{"cfgCutNoSameBunchPileup", true, "Require kNoSameBunchPileup"}; + Configurable cfgCutGoodZvtxFT0vsPV{"cfgCutGoodZvtxFT0vsPV", true, "Require kIsGoodZvtxFT0vsPV"}; + Configurable cfgCutNoTimeFrameBorder{"cfgCutNoTimeFrameBorder", true, "Require kNoTimeFrameBorder"}; + Configurable cfgCutNoITSROFrameBorder{"cfgCutNoITSROFrameBorder", true, "Require kNoITSROFrameBorder"}; + Configurable cfgRejectDaughters{"cfgRejectDaughters", false, "Drop D0 daughter CF tracks from N_ch"}; + Configurable cfgWeightEffIsEfficiency{"cfgWeightEffIsEfficiency", true, "JWeights.weightEff is epsilon (multiply counts by 1/epsilon). Set false if the table already stores 1/epsilon."}; + + ConfigurableAxis cfgAxisCent{"cfgAxisCent", {8, 0., 80.}, "Centrality axis"}; + ConfigurableAxis cfgAxisPtD{"cfgAxisPtD", {VARIABLE_WIDTH, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 8.f, 12.f, 16.f, 24.f}, "D0 pT axis"}; + ConfigurableAxis cfgAxisPtHad{"cfgAxisPtHad", {VARIABLE_WIDTH, 0.2f, 0.4f, 0.6f, 0.8f, 1.f, 1.2f, 1.5f, 2.f, 2.5f, 3.f}, "Charged-hadron pT axis"}; + ConfigurableAxis cfgAxisMass{"cfgAxisMass", {70, 1.70, 2.05}, "D0 invariant-mass axis"}; + + int mRefA = kFT0A; + int mRefB = kFT0C; + int mNSubsample = 1; + Preslice mPerCFTrack = aod::cftrack::cfCollisionId; + Preslice mPerCFCand = aod::cftrack::cfCollisionId; + const TAxis* mHadPtAxis = nullptr; + std::vector mHadCounts; + std::mt19937 mRng{1}; + std::uniform_int_distribution mSampleDist; + + template + using hasWeightEff = decltype(std::declval().weightEff()); + + template + float inverseEfficiencyWeight(const TTrack& track) const + { + if constexpr (std::experimental::is_detected::value) { + const float w = track.weightEff(); + if (w <= 0.f || !std::isfinite(w)) { + return 0.f; + } + return cfgWeightEffIsEfficiency.value ? 1.f / w : w; + } + return 1.f; + } + + int detectorId(const std::string& name) const + { + if (name == "FT0C") { + return kFT0C; + } + if (name == "FT0A") { + return kFT0A; + } + if (name == "FT0M") { + return kFT0M; + } + if (name == "FV0A") { + return kFV0A; + } + if (name == "TPCpos") { + return kTPCpos; + } + if (name == "TPCneg") { + return kTPCneg; + } + if (name == "TPCall") { + return kTPCall; + } + return -1; + } + + void init(InitContext const&) + { + mRefA = detectorId(cfgRefAName.value); + mRefB = detectorId(cfgRefBName.value); + if (mRefA < 0 || mRefB < 0 || mRefA == mRefB) { + LOGF(fatal, "cfgRefAName and cfgRefBName must be two different detectors"); + } + mNSubsample = cfgNSubsample > 0 ? cfgNSubsample.value : 1; + mRng.seed(static_cast(cfgSeed)); + mSampleDist = std::uniform_int_distribution(0, mNSubsample - 1); + + const AxisSpec subsampleAxis{mNSubsample, 0., static_cast(mNSubsample), "subsample"}; + const AxisSpec qaAxis{6, 0.5, 6.5, "1 seen; 2 sel8; 3 vertex; 4 quality; 5 centrality; 6 accepted"}; + + if (doprocessReference || doprocessReferenceCorrected) { + histos.add("hadron/hEventQa", "1 seen; 2 sel8; 3 vertex; 4 quality; 5 centrality; 6 accepted", {HistType::kTH1F, {qaAxis}}); + histos.add("hadron/hEvents", "Selected events;centrality (%);events", {HistType::kTH1F, {cfgAxisCent}}); + histos.add("hadron/Prof_XY", "Bulk #LTXY#GT;centrality (%);#LTXY#GT", {HistType::kTProfile, {cfgAxisCent}}); + histos.add("hadron/Prof_Nch", "N_{ch};centrality (%);#LTN_{ch}#GT", {HistType::kTProfile, {cfgAxisCent}}); + histos.add("hadron/Prof_Z", "Z = n(p_{T})/N_{ch};centrality (%);p_{T} (GeV/c);#LTZ#GT", {HistType::kTProfile2D, {cfgAxisCent, cfgAxisPtHad}}); + histos.add("hadron/Prof_XYZ", "XY Z;centrality (%);p_{T} (GeV/c);#LTXYZ#GT", {HistType::kTProfile2D, {cfgAxisCent, cfgAxisPtHad}}); + histos.add("hadron/Prof_XY_sub", "Bulk #LTXY#GT per subsample;subsample;centrality (%);#LTXY#GT", {HistType::kTProfile2D, {subsampleAxis, cfgAxisCent}}); + histos.add("hadron/Prof_Z_sub", "Z per subsample;subsample;centrality (%);p_{T} (GeV/c);#LTZ#GT", {HistType::kTProfile3D, {subsampleAxis, cfgAxisCent, cfgAxisPtHad}}); + histos.add("hadron/Prof_XYZ_sub", "XYZ per subsample;subsample;centrality (%);p_{T} (GeV/c);#LTXYZ#GT", {HistType::kTProfile3D, {subsampleAxis, cfgAxisCent, cfgAxisPtHad}}); + mHadPtAxis = histos.get(HIST("hadron/Prof_Z"))->GetYaxis(); + mHadCounts.assign(mHadPtAxis->GetNbins(), 0.f); + } + if (doprocessReference && doprocessReferenceCorrected) { + LOGF(fatal, "Enable only one of processReference and processReferenceCorrected"); + } + if (doprocessD0 + doprocessD0HadronNch + doprocessD0Corrected > 1) { + LOGF(fatal, "Enable only one of processD0, processD0HadronNch, and processD0Corrected"); + } + if (doprocessD0 || doprocessD0HadronNch || doprocessD0Corrected) { + histos.add("D0/hEventQa", "1 seen; 2 sel8; 3 vertex; 4 quality; 5 centrality; 6 accepted", {HistType::kTH1F, {qaAxis}}); + histos.add("D0/hEvents", "Selected events;centrality (%);events", {HistType::kTH1F, {cfgAxisCent}}); + histos.add("D0/Prof_XY", "Bulk #LTXY#GT;centrality (%);#LTXY#GT", {HistType::kTProfile, {cfgAxisCent}}); + histos.add("D0/Prof_Nch", "N_{ch};centrality (%);#LTN_{ch}#GT", {HistType::kTProfile, {cfgAxisCent}}); + histos.add("D0/hMassRaw", "Weight 1/N_{ch};centrality (%);p_{T} (GeV/c);m (GeV/c^{2})", {HistType::kTH3F, {cfgAxisCent, cfgAxisPtD, cfgAxisMass}}, true); + histos.add("D0/hMassCorr", "Weight XY/N_{ch};centrality (%);p_{T} (GeV/c);m (GeV/c^{2})", {HistType::kTH3F, {cfgAxisCent, cfgAxisPtD, cfgAxisMass}}, true); + histos.add("D0/hMassRawSub", "Weight 1/N_{ch};subsample;centrality (%);p_{T} (GeV/c);m (GeV/c^{2})", {HistType::kTHnSparseF, {subsampleAxis, cfgAxisCent, cfgAxisPtD, cfgAxisMass}}, true); + histos.add("D0/hMassCorrSub", "Weight XY/N_{ch};subsample;centrality (%);p_{T} (GeV/c);m (GeV/c^{2})", {HistType::kTHnSparseF, {subsampleAxis, cfgAxisCent, cfgAxisPtD, cfgAxisMass}}, true); + } + } + + template + float centralityOf(const TCollision& collision) + { + if (cfgCentEst.value == "FT0M") { + return collision.centFT0M(); + } + return collision.centFT0C(); + } + + template + int passedEventStep(const TCollision& collision, float& centrality) + { + if (cfgRequireSel8 && !collision.sel8()) { + return 1; + } + if (std::abs(collision.posZ()) > cfgVtxZ) { + return 2; + } + const bool rejected = + (cfgCutNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) || + (cfgCutGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) || + (cfgCutNoTimeFrameBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) || + (cfgCutNoITSROFrameBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) || + collision.trackOccupancyInTimeRange() > cfgMaxOccupancy || + collision.trackOccupancyInTimeRange() < cfgMinOccupancy; + if (rejected) { + return 3; + } + centrality = centralityOf(collision); + if (centrality < cfgCentMin || centrality > cfgCentMax) { + return 4; + } + return 5; + } + + template + bool referenceProduct(const TCollision& collision, float& xy) + { + const auto& re = collision.qvecRe(); + const auto& im = collision.qvecIm(); + const auto& amp = collision.qvecAmp(); + const int idxA = cfgQvecMode * NDetectors * NCorrections + mRefA * NCorrections + FullyCorrected; + const int idxB = cfgQvecMode * NDetectors * NCorrections + mRefB * NCorrections + FullyCorrected; + const int ampA = cfgQvecMode * NDetectors + mRefA; + const int ampB = cfgQvecMode * NDetectors + mRefB; + if (idxA >= static_cast(re.size()) || idxB >= static_cast(im.size()) || ampA >= static_cast(amp.size()) || ampB >= static_cast(amp.size())) { + return false; + } + if (!(amp[ampA] > cfgMinSumAmpl) || !(amp[ampB] > cfgMinSumAmpl)) { + return false; + } + if (re[idxA] > InvalidQvector || re[idxB] > InvalidQvector) { + return false; + } + xy = re[idxA] * re[idxB] + im[idxA] * im[idxB]; + return std::isfinite(xy); + } + + template + float countCharged(const TTracks& tracks, const std::unordered_set& daughters, bool differential) + { + float nch = 0.f; + if (differential) { + std::fill(mHadCounts.begin(), mHadCounts.end(), 0.f); + } + for (const auto& track : tracks) { + const float pt = track.pt(); + if (track.sign() == 0 || pt < cfgPtMinCh || pt >= cfgPtMaxCh || std::abs(track.eta()) > cfgEtaMax) { + continue; + } + if (cfgRejectDaughters && daughters.contains(static_cast(track.globalIndex()))) { + continue; + } + float w = 1.f; + if constexpr (kApplyEff) { + w = inverseEfficiencyWeight(track); + if (w <= 0.f) { + continue; + } + } + nch += w; + if (differential && mHadPtAxis != nullptr) { + const int bin = mHadPtAxis->FindBin(pt); + if (bin >= 1 && bin <= mHadPtAxis->GetNbins()) { + mHadCounts[bin - 1] += w; + } + } + } + return nch; + } + + static bool isD0Decay(uint8_t decay) + { + return decay == aod::cf2prongtrack::D0ToPiK || decay == aod::cf2prongtrack::D0barToKPi || decay == aod::cf2prongtrack::D0barToKPiExclusive; + } + + template + static int cfCollisionIndex(const TRefs& refs) + { + if (refs.size() != 1) { + return -1; + } + return static_cast(refs.begin().globalIndex()); + } + + void processReference(MyCollisions::iterator const& collision, aod::BCs const&, aod::CFCollisions const&, aod::CFCollRefs const& refs, aod::CFTracks const& tracks) + { + float cent = -1.f; + float xy = 0.f; + const int step = passedEventStep(collision, cent); + for (int qaBin = 1; qaBin <= step; ++qaBin) { + histos.fill(HIST("hadron/hEventQa"), static_cast(qaBin)); + } + const int cfId = cfCollisionIndex(refs); + if (step < StepAccepted || cfId < 0 || !referenceProduct(collision, xy)) { + return; + } + const std::unordered_set noDaughters; + const float nch = countCharged(tracks.sliceBy(mPerCFTrack, cfId), noDaughters, true); + if (nch < cfgMinNch) { + return; + } + histos.fill(HIST("hadron/hEventQa"), 6.f); + const int sample = mSampleDist(mRng); + histos.fill(HIST("hadron/hEvents"), cent); + histos.fill(HIST("hadron/Prof_XY"), cent, xy); + histos.fill(HIST("hadron/Prof_Nch"), cent, nch); + histos.fill(HIST("hadron/Prof_XY_sub"), static_cast(sample), cent, xy); + const int nBins = mHadPtAxis->GetNbins(); + for (int ibin = 1; ibin <= nBins; ++ibin) { + const auto pt = static_cast(mHadPtAxis->GetBinCenter(ibin)); + const float z = mHadCounts[ibin - 1] / nch; + histos.fill(HIST("hadron/Prof_Z"), cent, pt, z); + histos.fill(HIST("hadron/Prof_XYZ"), cent, pt, xy * z); + histos.fill(HIST("hadron/Prof_Z_sub"), static_cast(sample), cent, pt, z); + histos.fill(HIST("hadron/Prof_XYZ_sub"), static_cast(sample), cent, pt, xy * z); + } + } + PROCESS_SWITCH(JV02DmesonAnalysis, processReference, "Charged-hadron v02(pT) closure", true); + + void processReferenceCorrected(MyCollisions::iterator const& collision, aod::BCs const&, aod::CFCollisions const&, aod::CFCollRefs const& refs, soa::Join const& tracks) + { + float cent = -1.f; + float xy = 0.f; + const int step = passedEventStep(collision, cent); + for (int qaBin = 1; qaBin <= step; ++qaBin) { + histos.fill(HIST("hadron/hEventQa"), static_cast(qaBin)); + } + const int cfId = cfCollisionIndex(refs); + if (step < StepAccepted || cfId < 0 || !referenceProduct(collision, xy)) { + return; + } + const std::unordered_set noDaughters; + const float nch = countCharged, true>(tracks.sliceBy(mPerCFTrack, cfId), noDaughters, true); + if (nch < cfgMinNch) { + return; + } + histos.fill(HIST("hadron/hEventQa"), 6.f); + const int sample = mSampleDist(mRng); + histos.fill(HIST("hadron/hEvents"), cent); + histos.fill(HIST("hadron/Prof_XY"), cent, xy); + histos.fill(HIST("hadron/Prof_Nch"), cent, nch); + histos.fill(HIST("hadron/Prof_XY_sub"), static_cast(sample), cent, xy); + const int nBins = mHadPtAxis->GetNbins(); + for (int ibin = 1; ibin <= nBins; ++ibin) { + const auto pt = static_cast(mHadPtAxis->GetBinCenter(ibin)); + const float z = mHadCounts[ibin - 1] / nch; + histos.fill(HIST("hadron/Prof_Z"), cent, pt, z); + histos.fill(HIST("hadron/Prof_XYZ"), cent, pt, xy * z); + histos.fill(HIST("hadron/Prof_Z_sub"), static_cast(sample), cent, pt, z); + histos.fill(HIST("hadron/Prof_XYZ_sub"), static_cast(sample), cent, pt, xy * z); + } + } + PROCESS_SWITCH(JV02DmesonAnalysis, processReferenceCorrected, "Charged-hadron v02 with JWeights efficiency", false); + + void processD0(MyCollisions::iterator const& collision, aod::BCs const&, aod::CFCollisions const&, aod::CFCollRefs const& refs, aod::CF2ProngTracks const& candidates, aod::CFTracks const& tracks) + { + float cent = -1.f; + float xy = 0.f; + const int step = passedEventStep(collision, cent); + for (int qaBin = 1; qaBin <= step; ++qaBin) { + histos.fill(HIST("D0/hEventQa"), static_cast(qaBin)); + } + const int cfId = cfCollisionIndex(refs); + if (step < StepAccepted || cfId < 0 || !referenceProduct(collision, xy)) { + return; + } + auto eventCandidates = candidates.sliceBy(mPerCFCand, cfId); + auto eventTracks = tracks.sliceBy(mPerCFTrack, cfId); + + std::unordered_set daughters; + if (cfgRejectDaughters) { + for (const auto& cand : eventCandidates) { + if (!isD0Decay(cand.decay())) { + continue; + } + daughters.insert(static_cast(cand.cfTrackProng0Id())); + daughters.insert(static_cast(cand.cfTrackProng1Id())); + } + } + const float nch = countCharged(eventTracks, daughters, false); + if (nch < cfgMinNch) { + return; + } + histos.fill(HIST("D0/hEventQa"), 6.f); + const int sample = mSampleDist(mRng); + const auto sampleCoord = static_cast(sample); + histos.fill(HIST("D0/hEvents"), cent); + histos.fill(HIST("D0/Prof_XY"), cent, xy); + histos.fill(HIST("D0/Prof_Nch"), cent, nch); + + const float wRaw = 1.f / nch; + const float wCorr = xy / nch; + for (const auto& cand : eventCandidates) { + if (!isD0Decay(cand.decay())) { + continue; + } + const float pt = cand.pt(); + if (pt < cfgPtMinD || pt >= cfgPtMaxD) { + continue; + } + const float mass = cand.invMass(); + histos.fill(HIST("D0/hMassRaw"), cent, pt, mass, wRaw); + histos.fill(HIST("D0/hMassCorr"), cent, pt, mass, wCorr); + histos.fill(HIST("D0/hMassRawSub"), sampleCoord, cent, pt, mass, wRaw); + histos.fill(HIST("D0/hMassCorrSub"), sampleCoord, cent, pt, mass, wCorr); + } + } + PROCESS_SWITCH(JV02DmesonAnalysis, processD0, "D0 and D0bar v02(pT) from CF2ProngTracks", true); + + void processD0HadronNch(MyCollisions::iterator const& collision, aod::BCs const&, aod::CFCollisions const&, aod::CFCollRefs const& refs, aod::CF2ProngTracks const& candidates, soa::Join const& tracks) + { + float cent = -1.f; + float xy = 0.f; + const int step = passedEventStep(collision, cent); + for (int qaBin = 1; qaBin <= step; ++qaBin) { + histos.fill(HIST("D0/hEventQa"), static_cast(qaBin)); + } + const int cfId = cfCollisionIndex(refs); + if (step < StepAccepted || cfId < 0 || !referenceProduct(collision, xy)) { + return; + } + auto eventCandidates = candidates.sliceBy(mPerCFCand, cfId); + auto eventTracks = tracks.sliceBy(mPerCFTrack, cfId); + + std::unordered_set daughters; + if (cfgRejectDaughters) { + for (const auto& cand : eventCandidates) { + if (!isD0Decay(cand.decay())) { + continue; + } + daughters.insert(static_cast(cand.cfTrackProng0Id())); + daughters.insert(static_cast(cand.cfTrackProng1Id())); + } + } + const float nch = countCharged, true>(eventTracks, daughters, false); + if (nch < cfgMinNch) { + return; + } + histos.fill(HIST("D0/hEventQa"), 6.f); + const int sample = mSampleDist(mRng); + const auto sampleCoord = static_cast(sample); + histos.fill(HIST("D0/hEvents"), cent); + histos.fill(HIST("D0/Prof_XY"), cent, xy); + histos.fill(HIST("D0/Prof_Nch"), cent, nch); + + const float wRaw = 1.f / nch; + const float wCorr = xy / nch; + for (const auto& cand : eventCandidates) { + if (!isD0Decay(cand.decay())) { + continue; + } + const float pt = cand.pt(); + if (pt < cfgPtMinD || pt >= cfgPtMaxD) { + continue; + } + const float mass = cand.invMass(); + histos.fill(HIST("D0/hMassRaw"), cent, pt, mass, wRaw); + histos.fill(HIST("D0/hMassCorr"), cent, pt, mass, wCorr); + histos.fill(HIST("D0/hMassRawSub"), sampleCoord, cent, pt, mass, wRaw); + histos.fill(HIST("D0/hMassCorrSub"), sampleCoord, cent, pt, mass, wCorr); + } + } + PROCESS_SWITCH(JV02DmesonAnalysis, processD0HadronNch, "D0 v02 with epsilon_h-weighted N_ch only (no D0 efficiency)", false); + + void processD0Corrected(MyCollisions::iterator const& collision, aod::BCs const&, aod::CFCollisions const&, aod::CFCollRefs const& refs, + soa::Join const& candidates, soa::Join const& tracks) + { + float cent = -1.f; + float xy = 0.f; + const int step = passedEventStep(collision, cent); + for (int qaBin = 1; qaBin <= step; ++qaBin) { + histos.fill(HIST("D0/hEventQa"), static_cast(qaBin)); + } + const int cfId = cfCollisionIndex(refs); + if (step < StepAccepted || cfId < 0 || !referenceProduct(collision, xy)) { + return; + } + auto eventCandidates = candidates.sliceBy(mPerCFCand, cfId); + auto eventTracks = tracks.sliceBy(mPerCFTrack, cfId); + + std::unordered_set daughters; + if (cfgRejectDaughters) { + for (const auto& cand : eventCandidates) { + if (!isD0Decay(cand.decay())) { + continue; + } + daughters.insert(static_cast(cand.cfTrackProng0Id())); + daughters.insert(static_cast(cand.cfTrackProng1Id())); + } + } + const float nch = countCharged, true>(eventTracks, daughters, false); + if (nch < cfgMinNch) { + return; + } + histos.fill(HIST("D0/hEventQa"), 6.f); + const int sample = mSampleDist(mRng); + const auto sampleCoord = static_cast(sample); + histos.fill(HIST("D0/hEvents"), cent); + histos.fill(HIST("D0/Prof_XY"), cent, xy); + histos.fill(HIST("D0/Prof_Nch"), cent, nch); + + for (const auto& cand : eventCandidates) { + if (!isD0Decay(cand.decay())) { + continue; + } + const float pt = cand.pt(); + if (pt < cfgPtMinD || pt >= cfgPtMaxD) { + continue; + } + const float wInvD = inverseEfficiencyWeight(cand); + if (wInvD <= 0.f) { + continue; + } + const float wRaw = wInvD / nch; + const float wCorr = xy * wInvD / nch; + const float mass = cand.invMass(); + histos.fill(HIST("D0/hMassRaw"), cent, pt, mass, wRaw); + histos.fill(HIST("D0/hMassCorr"), cent, pt, mass, wCorr); + histos.fill(HIST("D0/hMassRawSub"), sampleCoord, cent, pt, mass, wRaw); + histos.fill(HIST("D0/hMassCorrSub"), sampleCoord, cent, pt, mass, wCorr); + } + } + PROCESS_SWITCH(JV02DmesonAnalysis, processD0Corrected, "D0 v02 with JWeights and J2ProngWeights efficiency", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGCF/JCorran/Tasks/jV02DmesonToy.cxx b/PWGCF/JCorran/Tasks/jV02DmesonToy.cxx new file mode 100644 index 00000000000..e4d3f614849 --- /dev/null +++ b/PWGCF/JCorran/Tasks/jV02DmesonToy.cxx @@ -0,0 +1,160 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file jV02DmesonToy.cxx +/// \brief One timeframe of synthetic collisions for JV02DmesonAnalysis. +/// \author Dong Jo Kim (djkim@jyu.fi) +/// +/// 200 events at centrality 25. N_ch = 20. +/// Even events: XY = 0.08, 6 tracks in pT [1.0, 1.2), 4 D0 at mass 1.864 and pT 3.5. +/// Odd events: XY = 0.02, 2 tracks in pT [1.0, 1.2), 1 D0 at the same mass and pT. +/// The remaining charged tracks sit at pT 0.3. +/// Expected charged-hadron v02 in the 1.1 GeV/c bin is 0.3. +/// Expected D0 v02, with an empty sideband, is 0.36. + +#include "PWGCF/DataModel/CorrelationsDerived.h" +#include "PWGCF/JCorran/DataModel/JCatalyst.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Qvectors.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +struct JV02DmesonToy { + Produces bcs; + Produces collisions; + Produces evsels; + Produces centsC; + Produces centsM; + Produces qvectors; + Produces cfCollisions; + Produces cfRefs; + Produces cfTracks; + Produces cfCandidates; + Produces jWeights; + + void fill() + { + constexpr int NEvents = 200; + constexpr int NDetectors = 7; + constexpr int NCorrections = 4; + constexpr int FullyCorrected = 3; + constexpr int Ft0C = 0; + constexpr int Ft0A = 1; + constexpr int EventPair = 2; + constexpr float Centrality = 25.f; + constexpr float Nch = 20.f; + const uint64_t selection = + (uint64_t{1} << aod::evsel::kNoSameBunchPileup) | + (uint64_t{1} << aod::evsel::kIsGoodZvtxFT0vsPV) | + (uint64_t{1} << aod::evsel::kNoTimeFrameBorder) | + (uint64_t{1} << aod::evsel::kNoITSROFrameBorder); + + std::vector re(NDetectors * NCorrections, 0.f); + std::vector im(NDetectors * NCorrections, 0.f); + std::vector amp(NDetectors, 0.f); + amp[Ft0A] = 1.f; + amp[Ft0C] = 1.f; + + for (int ievt = 0; ievt < NEvents; ++ievt) { + const bool high = ievt % EventPair == 0; + const float xy = high ? 0.08f : 0.02f; + const float q = std::sqrt(xy); + re[Ft0C * NCorrections + FullyCorrected] = q; + re[Ft0A * NCorrections + FullyCorrected] = q; + + bcs(544116, static_cast(ievt), uint64_t{0}, uint64_t{0}); + collisions(ievt, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, uint16_t{0}, 1.f, uint16_t{20}, 0.f, 0.f); + evsels(uint32_t{0}, selection, uint32_t{0}, false, true, ievt, -1, -1, -1, -1, 100, 0.f, 0.f); + centsC(Centrality); + centsM(Centrality); + qvectors(Centrality, true, re, im, amp); + cfCollisions(544116, 0.f, Nch, uint64_t{0}); + cfRefs(ievt); + + const int nSignalTracks = high ? 6 : 2; + for (int itrk = 0; itrk < static_cast(Nch); ++itrk) { + const float pt = itrk < nSignalTracks ? 1.1f : 0.3f; + const int8_t sign = itrk % EventPair == 0 ? int8_t{1} : int8_t{-1}; + cfTracks(ievt, pt, 0.f, 0.f, sign, uint8_t{1}); + // Closure for hadron efficiency: signal pT bin eps=0.5, rest eps=1 (v02 unchanged if all eps equal). + const float eff = pt > 1.f ? 0.5f : 1.f; + jWeights(1.f, eff); + } + const int nD0 = high ? 4 : 1; + for (int icand = 0; icand < nD0; ++icand) { + cfCandidates(ievt, 0, 1, 3.5f, 0.f, 0.f, static_cast(o2::constants::physics::MassD0), static_cast(aod::cf2prongtrack::D0ToPiK)); + } + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + DataProcessorSpec{ + .name = "jv02-dmeson-toy", + .inputs = {}, + .outputs = { + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputSpec{"TFN", "TFNumber"}, + OutputSpec{"TFF", "TFFilename"}, + }, + .algorithm = AlgorithmSpec{adaptStateless([](ProcessingContext& pc) { + JV02DmesonToy toy; + toy.bcs.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.collisions.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.evsels.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.centsC.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.centsM.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.qvectors.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.cfCollisions.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.cfRefs.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.cfTracks.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.cfCandidates.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.jWeights.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.fill(); + pc.outputs().make(Output{"TFN", "TFNumber"}) = 1; + pc.outputs().make(Output{"TFF", "TFFilename"}) = std::string{"toy"}; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + })}, + }, + }; +} diff --git a/PWGCF/JCorran/Tasks/jflucWeightsLoader.cxx b/PWGCF/JCorran/Tasks/jflucWeightsLoader.cxx index 02088eab148..bbc5d528faf 100644 --- a/PWGCF/JCorran/Tasks/jflucWeightsLoader.cxx +++ b/PWGCF/JCorran/Tasks/jflucWeightsLoader.cxx @@ -16,6 +16,7 @@ /// The weights are loaded from the local files or CCDB and stored in the JWeights table. #include "PWGCF/DataModel/CorrelationsDerived.h" +#include "PWGCF/GenericFramework/DataModel/GfwDerived.h" #include "PWGCF/JCorran/DataModel/JCatalyst.h" #include @@ -116,14 +117,16 @@ struct JflucWeightsLoader { void init(InitContext const&) { - if (!doprocessLoadWeights && !doprocessLoadWeightsCF && !doprocessJNUACreatorCFDerived) { + if (!doprocessLoadWeights && !doprocessLoadWeightsCF && !doprocessLoadWeightsGFW && !doprocessJNUACreatorCFDerived) { return; } - if (doprocessLoadWeights && doprocessLoadWeightsCF) - LOGF(fatal, "Only one of JTracks or CFTracks processing can be enabled at a time."); + const int nWeightProcesses = static_cast(doprocessLoadWeights) + static_cast(doprocessLoadWeightsCF) + static_cast(doprocessLoadWeightsGFW); + if (nWeightProcesses > 1) { + LOGF(fatal, "Only one of JTracks, CFTracks, or GFWTracks weight loading can be enabled at a time."); + } - if (doprocessLoadWeights || doprocessLoadWeightsCF) { + if (doprocessLoadWeights || doprocessLoadWeightsCF || doprocessLoadWeightsGFW) { // NUA corrections from local file or CCDB if (cfgPathPhiWeights.value.substr(0, 8) == "local://") { LOGF(info, "Using NUA corrections locally from: %s", cfgPathPhiWeights.value.substr(8).c_str()); @@ -172,7 +175,7 @@ struct JflucWeightsLoader { ccdb->setFatalWhenNull(false); } } - } // doprocessLoadWeights || doprocessLoadWeightsCF + } // doprocessLoadWeights || doprocessLoadWeightsCF || doprocessLoadWeightsGFW if (doprocessJNUACreatorCFDerived) { qaHistRegistry.add("trackType", "trackType;trackType;counts", HistType::kTH1F, {{65, -0.5, 64.5}}); @@ -276,6 +279,12 @@ struct JflucWeightsLoader { } PROCESS_SWITCH(JflucWeightsLoader, processLoadWeightsCF, "Load weights histograms for CF derived data table", true); + void processLoadWeightsGFW(aod::GfwCollision const& collision, aod::GfwTracks const& tracks) + { + loadWeights(output, collision, tracks); + } + PROCESS_SWITCH(JflucWeightsLoader, processLoadWeightsGFW, "Load weights histograms for GFW derived data table", false); + Produces output2p; void processLoadWeightsCF2Prong(aod::CFCollision const& collision, aod::CF2ProngTracks const& tracks2p) { diff --git a/PWGCF/MultiparticleCorrelations/Tasks/CMakeLists.txt b/PWGCF/MultiparticleCorrelations/Tasks/CMakeLists.txt index a20000206b2..32c916c2275 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/CMakeLists.txt +++ b/PWGCF/MultiparticleCorrelations/Tasks/CMakeLists.txt @@ -38,3 +38,8 @@ o2physics_add_dpl_workflow(multiparticle-correlations-mei SOURCES multiparticleCorrelationsMei.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(multiparticle-correlations-dm + SOURCES multiparticleCorrelationsDm.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore + COMPONENT_NAME Analysis) diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiparticle-correlations-ar.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiparticle-correlations-ar.cxx index 2814c562225..411c1ec8426 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/multiparticle-correlations-ar.cxx +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiparticle-correlations-ar.cxx @@ -86,7 +86,7 @@ enum CorEventDep { kCENDEP, kLAST_CorEventDep }; -const std::string CorEventDepNames[kLAST_CorEventDep] = { +const std::array CorEventDepNames{ "[kINTEGRATED]", "[kMULDEP]", "[kCENDEP]"}; @@ -101,7 +101,7 @@ enum CorTrackDep { kETADEP, kLAST_CorTrackDep }; -const std::string CorTrackDepNames[kLAST_CorTrackDep] = { +const std::array CorTrackDepNames{ "[kPTDEP]", "[kETADEP]"}; std::vector> CorTrackDepDefaultBins = { @@ -121,7 +121,7 @@ enum EventVariable { kMULTPC, kLAST_EventVariable }; -static constexpr std::string_view EventVariableNames[kLAST_EventVariable] = { +static constexpr std::array EventVariableNames{ "EventVariable_VertexX", "EventVariable_VertexY", "EventVariable_VertexZ", @@ -201,10 +201,10 @@ std::vector> TrackVariableDefaultCuts = { {0., 6., 1.}}; // kITSCLUSTERS // common info string for all configurables -const std::string BinningSuffix = std::string("-Binning"); -const std::string CutSuffix = std::string("-Cut"); -const std::string CorrelatorHistPrefix = std::string("CorDepBinning_"); -const std::vector CutInfo = {std::string("Low"), std::string("High"), std::string("Option")}; +static constexpr std::string BinningSuffix{"-Binning"}; +static constexpr std::string CutSuffix{"-Cut"}; +static constexpr std::string CorrelatorHistPrefix{"CorDepBinning_"}; +static const std::vector cutInfo{"Low", "High", "Option"}; const int MaxHarmonic = 10; const int MaxPower = 10; @@ -259,7 +259,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgEventCutVX{ std::string(AR::EventVariableNames[AR::kVX]) + AR::CutSuffix, - {AR::EventVariableDefaultCuts.at(AR::kVX).data(), 3, AR::CutInfo}, + {AR::EventVariableDefaultCuts.at(AR::kVX).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgEventAxisVY{ std::string(AR::EventVariableNames[AR::kVY]) + AR::BinningSuffix, @@ -267,7 +267,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgEventCutVY{ std::string(AR::EventVariableNames[AR::kVY]) + AR::CutSuffix, - {AR::EventVariableDefaultCuts.at(AR::kVY).data(), 3, AR::CutInfo}, + {AR::EventVariableDefaultCuts.at(AR::kVY).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgEventAxisVZ{ std::string(AR::EventVariableNames[AR::kVZ]) + AR::BinningSuffix, @@ -275,7 +275,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgEventCutVZ{ std::string(AR::EventVariableNames[AR::kVZ]) + AR::CutSuffix, - {AR::EventVariableDefaultCuts.at(AR::kVZ).data(), 3, AR::CutInfo}, + {AR::EventVariableDefaultCuts.at(AR::kVZ).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgEventAxisVABS{ std::string(AR::EventVariableNames[AR::kVABS]) + AR::BinningSuffix, @@ -283,7 +283,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgEventCutVABS{ std::string(AR::EventVariableNames[AR::kVABS]) + AR::CutSuffix, - {AR::EventVariableDefaultCuts.at(AR::kVABS).data(), 3, AR::CutInfo}, + {AR::EventVariableDefaultCuts.at(AR::kVABS).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgEventAxisCEN{ std::string(AR::EventVariableNames[AR::kCEN]) + AR::BinningSuffix, @@ -291,7 +291,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgEventCutCEN{ std::string(AR::EventVariableNames[AR::kCEN]) + AR::CutSuffix, - {AR::EventVariableDefaultCuts.at(AR::kCEN).data(), 3, AR::CutInfo}, + {AR::EventVariableDefaultCuts.at(AR::kCEN).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgEventAxisMULQ{ std::string(AR::EventVariableNames[AR::kMULQ]) + AR::BinningSuffix, @@ -299,7 +299,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgEventCutMULQ{ std::string(AR::EventVariableNames[AR::kMULQ]) + AR::CutSuffix, - {AR::EventVariableDefaultCuts.at(AR::kMULQ).data(), 3, AR::CutInfo}, + {AR::EventVariableDefaultCuts.at(AR::kMULQ).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgEventAxisMULW{ std::string(AR::EventVariableNames[AR::kMULW]) + AR::BinningSuffix, @@ -307,7 +307,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgEventCutMULW{ std::string(AR::EventVariableNames[AR::kMULW]) + AR::CutSuffix, - {AR::EventVariableDefaultCuts.at(AR::kMULW).data(), 3, AR::CutInfo}, + {AR::EventVariableDefaultCuts.at(AR::kMULW).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgEventAxisMULNC{ std::string(AR::EventVariableNames[AR::kMULNC]) + AR::BinningSuffix, @@ -315,7 +315,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgEventCutMULNC{ std::string(AR::EventVariableNames[AR::kMULNC]) + AR::CutSuffix, - {AR::EventVariableDefaultCuts.at(AR::kMULNC).data(), 3, AR::CutInfo}, + {AR::EventVariableDefaultCuts.at(AR::kMULNC).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgEventAxisMULTPC{ std::string(AR::EventVariableNames[AR::kMULTPC]) + AR::BinningSuffix, @@ -323,7 +323,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgEventCutMULTPC{ std::string(AR::EventVariableNames[AR::kMULTPC]) + AR::CutSuffix, - {AR::EventVariableDefaultCuts.at(AR::kMULTPC).data(), 3, AR::CutInfo}, + {AR::EventVariableDefaultCuts.at(AR::kMULTPC).data(), 3, AR::cutInfo}, ""}; std::vector cfgEventBinning = { cfgEventAxisVX, @@ -343,7 +343,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgTrackCutPT{ std::string(AR::TrackVariableNames[AR::kPT]) + AR::CutSuffix, - {AR::TrackVariableDefaultCuts.at(AR::kPT).data(), 3, AR::CutInfo}, + {AR::TrackVariableDefaultCuts.at(AR::kPT).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgTrackAxisPHI{ std::string(AR::TrackVariableNames[AR::kPHI]) + AR::BinningSuffix, @@ -351,7 +351,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgTrackCutPHI{ std::string(AR::TrackVariableNames[AR::kPHI]) + AR::CutSuffix, - {AR::TrackVariableDefaultCuts.at(AR::kPHI).data(), 3, AR::CutInfo}, + {AR::TrackVariableDefaultCuts.at(AR::kPHI).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgTrackAxisETA{ std::string(AR::TrackVariableNames[AR::kETA]) + AR::BinningSuffix, @@ -359,7 +359,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgTrackCutETA{ std::string(AR::TrackVariableNames[AR::kETA]) + AR::CutSuffix, - {AR::TrackVariableDefaultCuts.at(AR::kETA).data(), 3, AR::CutInfo}, + {AR::TrackVariableDefaultCuts.at(AR::kETA).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgTrackAxisCHARGE{ std::string(AR::TrackVariableNames[AR::kCHARGE]) + AR::BinningSuffix, @@ -367,7 +367,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgTrackCutCHARGE{ std::string(AR::TrackVariableNames[AR::kCHARGE]) + AR::CutSuffix, - {AR::TrackVariableDefaultCuts.at(AR::kCHARGE).data(), 3, AR::CutInfo}, + {AR::TrackVariableDefaultCuts.at(AR::kCHARGE).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgTrackAxisDCAZ{ std::string(AR::TrackVariableNames[AR::kDCAZ]) + AR::BinningSuffix, @@ -375,7 +375,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgTrackCutDCAZ{ std::string(AR::TrackVariableNames[AR::kDCAZ]) + AR::CutSuffix, - {AR::TrackVariableDefaultCuts.at(AR::kDCAZ).data(), 3, AR::CutInfo}, + {AR::TrackVariableDefaultCuts.at(AR::kDCAZ).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgTrackAxisDCAXY{ std::string(AR::TrackVariableNames[AR::kDCAXY]) + AR::BinningSuffix, @@ -383,7 +383,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgTrackCutDCAXY{ std::string(AR::TrackVariableNames[AR::kDCAXY]) + AR::CutSuffix, - {AR::TrackVariableDefaultCuts.at(AR::kDCAXY).data(), 3, AR::CutInfo}, + {AR::TrackVariableDefaultCuts.at(AR::kDCAXY).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgTrackAxisTPCCLUSTERS{ std::string(AR::TrackVariableNames[AR::kTPCCLUSTERS]) + AR::BinningSuffix, @@ -391,7 +391,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgTrackCutTPCCLUSTERS{ std::string(AR::TrackVariableNames[AR::kTPCCLUSTERS]) + AR::CutSuffix, - {AR::TrackVariableDefaultCuts.at(AR::kTPCCLUSTERS).data(), 3, AR::CutInfo}, + {AR::TrackVariableDefaultCuts.at(AR::kTPCCLUSTERS).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgTrackAxisTPCCROSSEDROWS{ std::string(AR::TrackVariableNames[AR::kTPCCROSSEDROWS]) + AR::BinningSuffix, @@ -399,7 +399,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgTrackCutTPCCROSSEDROWS{ std::string(AR::TrackVariableNames[AR::kTPCCROSSEDROWS]) + AR::CutSuffix, - {AR::TrackVariableDefaultCuts.at(AR::kTPCCROSSEDROWS).data(), 3, AR::CutInfo}, + {AR::TrackVariableDefaultCuts.at(AR::kTPCCROSSEDROWS).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgTrackAxisTPCCHI2{ std::string(AR::TrackVariableNames[AR::kTPCCHI2]) + AR::BinningSuffix, @@ -407,7 +407,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgTrackCutTPCCHI2{ std::string(AR::TrackVariableNames[AR::kTPCCHI2]) + AR::CutSuffix, - {AR::TrackVariableDefaultCuts.at(AR::kTPCCHI2).data(), 3, AR::CutInfo}, + {AR::TrackVariableDefaultCuts.at(AR::kTPCCHI2).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgTrackAxisITSCLUSTERS{ std::string(AR::TrackVariableNames[AR::kITSCLUSTERS]) + AR::BinningSuffix, @@ -415,7 +415,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgTrackCutITSCLUSTERS{ std::string(AR::TrackVariableNames[AR::kITSCLUSTERS]) + AR::CutSuffix, - {AR::TrackVariableDefaultCuts.at(AR::kITSCLUSTERS).data(), 3, AR::CutInfo}, + {AR::TrackVariableDefaultCuts.at(AR::kITSCLUSTERS).data(), 3, AR::cutInfo}, ""}; std::vector cfgTrackBinning = { cfgTrackAxisPT, diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsDm.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsDm.cxx new file mode 100644 index 00000000000..587248abc75 --- /dev/null +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsDm.cxx @@ -0,0 +1,1057 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file multiparticleCorrelationsDm.cxx +/// \brief Task to calculate multiparticle correlations and related observables +/// \author molnard@tcd.ie + +// O2: + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" // needed for aod::TracksDCA table + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// *) ROOT and C++ include's: + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +// Definitions of join tables for Run 3 analysis: +using EventSelection = soa::Join; +using CollisionRec = soa::Join::iterator; // use in json "isMC": "true" for "event-selection-task" +using CollisionRecSim = soa::Join::iterator; +using CollisionSim = aod::McCollision; +using TracksRec = soa::Join; +using TrackRec = soa::Join::iterator; +using TracksRecSim = soa::Join; // + use in json "isMC" : "true" +using TrackRecSim = soa::Join::iterator; +using TracksSim = aod::McParticles; +using TrackSim = aod::McParticles::iterator; + +// *) Define enums: +enum ERecSim { ERec = 0, + ESim, + ERecAndSim }; + +enum EProcess { + EProcessRec = 0, // Run 3, only reconstructed + EProcessRecSim, // Run 3, both reconstructed and simulated + EProcessSim, // Run 3, only simulated + EProcess_N +}; + +enum ECentralityEstimator { + ECentFT0C = 0, // default + ECentFT0M, + ECentFT0A, + ECentralityEstimator_N +}; + +enum EReferenceMultiplicityEstimator { + ERefMultTPC = 0, + ERefMultFT0C, + ERefMultFT0M, + EReferenceMultiplicityEstimator_N +}; + +enum EEventHistograms { // this is for cuts + EMultiplicity = 0, + EReferenceMultiplicity, + ECentrality, + EImpactParameter, // only simulated + EVertexX, + EVertexY, + EVertexZ, + EEventHistograms_N +}; + +enum EParticleHistograms { // this is for cuts + EPt = 0, + EPhi, + EParticleHistograms_N +}; + +enum EBeforeAfter { + EBefore = 0, + EAfter, + EBeforeAfter_N +}; + +// *) Main task: +struct MultiparticleCorrelationsDm { // this name is used in lower-case format to name the TDirectoryFile in AnalysisResults.root + + // *) Base TList to hold all output objects: + TString sBaseListName = "Default list name"; // yes, I declare it separately, because I need it also later in BailOut() function + OutputObj fBaseList{sBaseListName.Data(), + OutputObjHandlingPolicy::AnalysisObject, + OutputObjSourceType::OutputObjSource}; + + // *) CCDB: + // Service ccdb; // support for offline callibration data base, not needed for the time being... + // to be able to use ccdb object in the analysis task + Service ccdb{}; + + // *) Define configurables: + + Configurable cfDryRun{"cfDryRun", false, "book all histos and run without filling and calculating anything"}; + Configurable cfFillQA{"cfFillQA", false, "book and fill QA histograms (only for development and validation)"}; // switch for QA histograms + + // Define cuts: + Configurable cfUseVertexZCut{"cfUseVertexZCut", true, "apply the vertex Z event cut"}; + Configurable cfUsePtCut{"cfUsePtCut", true, "apply the pt particle cut"}; + Configurable cfUseTimeRange{"cfUseTimeRange", true, "rejects events with overlapping collisions"}; + Configurable cfUseRof{"cfUseRof", true, "removes events containing multiple collisions in the same readout frame"}; + Configurable cfUsePileUp{"cfUsePileUp", true, "apply pile up cuts"}; + Configurable cfUseITSTPC{"cfUseITSTPC", true, "use reconstructed from the ITS (Inner Tracking System) and TPC (Time Porjection Chamber) tracks"}; + Configurable cfUseITSLayers{"cfUseITSLayers", true, "use the ITS layers cut"}; + Configurable cfUseZFT0vsPV{"cfUseZFT0vsPV", true, "use this cut"}; + Configurable cfUsePrevRof{"cfUsePrevRof", true, " use this cut"}; + + Configurable cfCentralityEstimator{"cfCentralityEstimator", "FT0C", "centrality estimator: FT0C, FT0M, FT0A"}; // centrality estimator with valid values: FT0C (default) + Configurable cfReferenceMultiplicityEstimator{"cfReferenceMultiplicityEstimator", "FT0C", "reference multiplicity estimator: TPC, FT0C, FT0M"}; // ref. multiplicity estimator with valid values: TPC (default) FT0C Ft0M + + // sigma_inel in fm^2 (1 b = 100 fm^2): Pb+Pb at 5.36 TeV = 7.71 b = 771 fm^2 (PH2278 Lecture 2, slide 33), change for other system or energy + Configurable cfSigmaInel{"cfSigmaInel", 771., "sigma_inel in fm^2 (1 b = 100 fm^2), Pb+Pb 5.36 TeV = 771"}; + + Configurable> cfPtBins{"cfPtBins", {1000, 0., 100.}, "nPtBins, ptMin, ptMax"}; + Configurable> cfPhiBins{"cfPhiBins", {360, 0., o2::constants::math::TwoPI}, "nPhiBins, phiMin, phiMax"}; + Configurable> cfMultiplicityBins{"cfMultiplicityBins", {5000, 0., 5000.}, "nMultBins, multMin, multMax"}; + Configurable> cfRefmultiplicityBins{"cfRefmultiplicityBins", {5000, 0., 5000.}, "nRefMultBins, refMultMin, refMultMax"}; // reference multiplicity + Configurable> cfImpactparameterBins{"cfImpactparameterBins", {200, 0., 20.}, "nBBins, bMin, bMax (fm)"}; // impact parameter + Configurable> cfCentralityBins{"cfCentralityBins", {100, 0., 100.}, "nCentBins, centMin, centMax"}; + Configurable> cfVertexXBins{"cfVertexXBins", {200, -1., 1.}, "nvxBins, vxMin, vxMax"}; + Configurable> cfVertexYBins{"cfVertexYBins", {200, -1, 1.}, "nvyBins, vyMin, vyMax"}; + Configurable> cfVertexZBins{"cfVertexZBins", {300, -15., 15.}, "nvzBins, vzMin, vzMax"}; + Configurable> cfNContribBins{"cfNContribBins", {500, 0., 5000.}, "nContribBins, contribMin, contribMax"}; + Configurable> cfVertexZ{"cfVertexZ", {-10., 10.}, "{min, max} [cm]"}; + Configurable> cfPt{"cfPt", {0.2, 5.0}, "{min, max} [GeV]"}; + Configurable cfUseEtaCut{"cfUseEtaCut", true, "apply the eta particle cut (pseudorapidity"}; + Configurable> cfEta{"cfEta", {-0.8, 0.8}, "eta range: {min, max}, with convention: min <= eta < max"}; + + // for weights (efficiency corrections) + Configurable cfUsePhiWeights{"cfUsePhiWeights", false, "use phi weights from the file in cfFileWithWeights"}; + Configurable cfUsePtWeights{"cfUsePtWeights", false, "use pt weights from the file in cfFileWithWeights"}; + Configurable cfUseEtaWeights{"cfUseEtaWeights", false, "use eta weights from the file in cfFileWithWeights"}; + Configurable cfFileWithWeights{"cfFileWithWeights", "", "path to external ROOT file with weights; on Hyperloop only CCDB paths (/alice-ccdb.cern.ch/...) work"}; + + Configurable cfVerbose{"cfVerbose", false, "print run number, centrality and vertex for each collision"}; + + // *) Define and initialize all data members to be called in the main process* functions: + // **) Task configuration: + struct TaskConfiguration { + std::array fProcess{}; // see enum EProcess + bool fDryRun = false; // book all histos and run without filling and calculating anything + bool fFillQA = false; // book and fill QA histos + bool fUseVertexZCut = true; + bool fUsePtCut = true; + bool fUseTimeRange = true; + bool fUseRof = true; + bool fUsePileUp = true; + bool fUseITSTPC = true; + bool fUseITSLayers = true; + bool fUseZFT0vsPV = true; + bool fUsePrevRof = true; + + int fCentralityEstimator = ECentFT0C; + int fReferenceMultiplicityEstimator = ERefMultTPC; + + float fSigmaInel = 771.; // sigma inel [fm^2] + std::array fVertexZ = {-10., 10.}; // {min, max} [cm] + std::array fPt = {0.2, 5.0}; // {min, max} [GeV] + bool fUseEtaCut = true; + std::array fEta = {-0.8, 0.8}; // {min, max} + int fCurrentRunNumber = -1; // run number of the weights currently in use + bool fUsePhiWeights = false; // load phi weights + bool fUsePtWeights = false; // load pt weights + bool fUseEtaWeights = false; // load eta weights + bool fVerbose = false; // print the values / collision + + } tc; // you have to prepend "tc." for all objects name in this group later in the code + + // **) Particle histograms: + struct ParticleHistograms { + TList* fParticleHistogramsList = nullptr; //!, 2>, EParticleHistograms_N> fParticleHistograms{}; // [type][rec, sim][before, after] + + } pc; // you have to prepend "pc." for all objects name in this group later in the code + + // **) Event histograms: + struct EventHistograms { + TList* fEventHistogramsList = nullptr; + + std::array, 2>, EEventHistograms_N> fEventHistograms{}; // [type][rec, sim][before, after] + + } ec; // you have to prepend "ec." for all objects name in this group later in the code + + // **) QA histograms (booked and filled only if cfFillQA is true): + + struct QAHistograms { + TList* fQAHistogramsList = nullptr; + TH2F* fHistCentralityRecVsSim = nullptr; // x = centrality sim (from impact parameter), y = centrality rec + std::array fHistRefMultVsNContrib{}; // x = reference multiplicity, y = number of vertex contributors [before, after] + } qa; // you have to prepend "qa." for all objects name in this group later in the code + + // **) Correlations: + struct Correlations { + TList* fCorrelationsList = nullptr; + TProfile* fProfTwo = nullptr; // <<2>> = v2{2}^2 vs. centrality (rec) + } cc; + + // **) Histogram with weights from an external file + TH1F* histPhiWeights = nullptr; // phi weights for the current run + TH1F* histPtWeights = nullptr; // pt weights for the current run + TH1F* histEtaWeights = nullptr; // eta weights for the current run + + // *) Define all member functions to be called in the main process* functions: + + // returns centrality from the estimator selected in json + template + float getCentrality(T const& collision) + { + switch (tc.fCentralityEstimator) { + case ECentFT0C: + return collision.centFT0C(); + case ECentFT0M: + return collision.centFT0M(); + case ECentFT0A: + return collision.centFT0A(); + default: + LOGF(fatal, "Unknown centrality estimator"); + return -1.; + } + } + + // returns reference multiplicity from the estimator selected in json + template + float getReferenceMultiplicity(T const& collision) + { + switch (tc.fReferenceMultiplicityEstimator) { + case ERefMultTPC: + return collision.multTPC(); + case ERefMultFT0C: + return collision.multFT0C(); + case ERefMultFT0M: + return collision.multFT0M(); + default: + LOGF(fatal, "Unknown reference multiplicity estimator"); + return -1.; + } + } + + // centrality from impact parameter: slide 33 PH2278 Lecture 2 + float centralityFromImpactParameter(float b) { return 100. * o2::constants::math::PI * b * b / tc.fSigmaInel; } + + // ______________________ weights _________________________ + + TObject* getObjectFromList(TList* list, const char* objectName) + { + // Last update: 20261018 + + // Get TObject pointer from TList, even if it's in some nested TList. + // Foreseen to be used to fetch histograms or profiles from files directly. + + // Some ideas taken from TCollection::ls() + + // If you have added histograms directly to files (without TList's), then you can fetch them directly with + // file->Get("hist-name"). + + // Example usage: TH1D *hist = (TH1D*) getObjectFromList("some-valid-TList-pointer","some-object-name"); + + // Insanity checks: + if (!list) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + if (!objectName) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + if (0 == list->GetEntries()) { + return nullptr; + } + + // The object is in the current base list: + TObject* objectFinal = list->FindObject(objectName); // the final object I am after + if (objectFinal) { + return objectFinal; + } + + // Otherwise, search for the object recursively in the nested lists: + TObject* objectIter = nullptr; // iterator object in the loop below + TIter next(list); + while ((objectIter = next())) // double round braces are to silence the warnings + { + if (TString(objectIter->ClassName()).EqualTo("TList")) { + objectFinal = getObjectFromList(dynamic_cast(objectIter), objectName); + if (objectFinal) { + return objectFinal; + } + } + } // while(objectIter = next()) + + return nullptr; + + } // TObject* getObjectFromList(...) + TH1F* getHistogramWithWeights(const char* filePath, const char* runNumber, const char* histName) + { + // Last update: 20261018 + + // *) Return value: + TH1F* hist = nullptr; + TList* baseList = nullptr; // base top-level list in the TFile, e.g. named "ccdb_object" + TList* listWithRuns = nullptr; // nested list with run-wise TList's holding run-specific weights + + // *) Determine from filePath if the file is on a local machine, or in home dir AliEn, or in CCDB: + + // Algorithm: a) If filePath begins with "/alice/cern.ch/" then it's in the home dir AliEn; + // b) If filePath begins with "/alice-ccdb.cern.ch/" then it's in CCDB. Therefore, files in AliEn and CCDB must be specified with abs path; + // c) For local files both absolute and relative paths are just fine. + + bool bFileIsInAliEn = false; + bool bFileIsInCCDB = false; + + if (TString(filePath).BeginsWith("/alice/cern.ch/")) { + bFileIsInAliEn = true; // file is in AliEn + } else if (TString(filePath).BeginsWith("/alice-ccdb.cern.ch/")) { + bFileIsInCCDB = true; // file is in CCDB + } + + if (bFileIsInAliEn) { + + // File you want to access is in your home directory in AliEn. + // In this branch, there is a special code to support file access in that case. + + const TGrid* alien = TGrid::Connect("alien", gSystem->Getenv("USER"), "", ""); + // add #include for it to work + + if (!alien) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + TFile* weightsFile = TFile::Open(Form("alien://%s", filePath), "READ"); // yes, ROOT can open a file transparently, even if it's sitting in AliEn, with this specific syntax + if (!weightsFile) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + weightsFile->GetObject("ccdb_object", baseList); + if (!baseList) { + // weightsFile->ls(); + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + + // Finally, from the top-level TList "ccdb_object", get the desired nested TList. + // The technical problem here is that it can be nested at any level, + // to overcome that there is a helper utility function TObject* getObjectFromList(...), + // whose implementation is discussed and provided separately. + listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumber)); + if (!listWithRuns) { + TString runNumberWithLeadingZeroes = "000"; + runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number + listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); + if (!listWithRuns) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + } + + // OK, we got the desired run-specific TList with efficiency corrections for AliEn case, + // after that we can use the common code for all 3 cases (local, AliEn, CCDB), + // that common code is further below. + + } else if (bFileIsInCCDB) { + + // File you want to access is in your home directory in CCDB. + // In this branch, there is a special code to support file access in that case. + + // Remember that here I do not access the file; instead, I directly access the object in that file. + // My home dir in CCDB: https://alice-ccdb.cern.ch/browse/Users/a/abilandz/ => adapt for your case + ccdb->setURL("http://alice-ccdb.cern.ch"); // to be able to use the "ccdb" object in your analysis task + baseList = dynamic_cast(ccdb->get(TString(filePath).ReplaceAll("/alice-ccdb.cern.ch/", "").Data())); + + if (!baseList) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + + listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumber)); + if (!listWithRuns) { + TString runNumberWithLeadingZeroes = "000"; + runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number + listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); + if (!listWithRuns) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + } + + // OK, we got the desired run-specific TList with efficiency corrections for CCDB, + // after that we can use the common code for all 3 cases (local, AliEn, CCDB), + // that common code is further below. + + } else { + + // File you want to access is on your local computer. + // In this branch, there is a special code to support file access in that case. + + // Check if the external ROOT file exists at the specified path: + if (gSystem->AccessPathName(filePath, kFileExists)) { + LOGF(info, "\033[1;33m if(gSystem->AccessPathName(filePath,kFileExists)), filePath = %s \033[0m", filePath); + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + + TFile* weightsFile = TFile::Open(filePath, "READ"); + if (!weightsFile) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + + weightsFile->GetObject("ccdb_object", baseList); + + if (!baseList) { + // weightsFile->ls(); + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + + listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumber)); + if (!listWithRuns) { + TString runNumberWithLeadingZeroes = "000"; + runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number + listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); + if (!listWithRuns) { + // baseList->ls(); + LOGF(fatal, "\033[1;31m%s at line %d : this crash can happen if in the output file there is no list with weights for the current run number = %s\033[0m", __FUNCTION__, __LINE__, runNumber); // changed runNumber instead of tc.fRunNumber.Data() + } + } + + // OK, we got the desired run-specific TList with efficiency corrections for local case, + // after that we can use the common code for all 3 cases (local, AliEn, CCDB), + // that common code is further below. + + } // end of else + + // The common code for all three cases, where from "listWithRuns" you fetch the desired histogram with efficiency corrections: + // listWithRuns->ls(); + + // get pointer to "hist" from "listWithRuns" + hist = dynamic_cast(getObjectFromList(listWithRuns, histName)); + if (!hist) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + + hist->SetDirectory(nullptr); // remove ownerhip from an external file + TH1F* histClone = dynamic_cast(hist->Clone()); // yes, I have to clone here + + if (!bFileIsInCCDB) { // not for CCDB, there the CCDB manager owns the object and deletes it itself + delete baseList; // release back the memory + } + + return histClone; + + } // end of TH1F* getHistogramWithWeights(...) + + // load one kind of weights for this run (or reuse it if already loaded), and save it in the output + TH1F* loadWeights(const char* nameInFile, const char* nameInOutput, int runNumber) + { + TString name = Form("%s_%d", nameInOutput, runNumber); + TH1F* hist = dynamic_cast(fBaseList->FindObject(name.Data())); + if (!hist) { + hist = getHistogramWithWeights(cfFileWithWeights.value.c_str(), Form("%d", runNumber), nameInFile); + hist->SetName(name.Data()); // e.g. histPhiWeights_544116 + hist->SetTitle(cfFileWithWeights.value.c_str()); // book-keeping: title = file path + fBaseList->Add(hist); // saved in AnalysisResults.root + } + return hist; + } + + // weight of one particle: product of all enabled weights, 1 if none is enabled + float getParticleWeight(float phi, float pt, float eta) + { + float weight = 1.; + if (tc.fUsePhiWeights && histPhiWeights) { + weight *= histPhiWeights->GetBinContent(histPhiWeights->FindBin(phi)); + } + if (tc.fUsePtWeights && histPtWeights) { + weight *= histPtWeights->GetBinContent(histPtWeights->FindBin(pt)); + } + if (tc.fUseEtaWeights && histEtaWeights) { + weight *= histEtaWeights->GetBinContent(histEtaWeights->FindBin(eta)); + } + return weight; + } + + //__ EVENT CUTS __ + template + bool eventCuts(T1 const& collision) + { + + // *) Vertex Z cut + if (tc.fUseVertexZCut) { + if (!(tc.fVertexZ[0] <= collision.posZ() && collision.posZ() < tc.fVertexZ[1])) { + return false; // if at least one event cut is not satisfied + } + } + // *) No collision in time range cut + if (tc.fUseTimeRange) { + if (!collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + return false; // if at least one event cut is not satisfied + } + } + + // *) No collision in the same readout frame cut + if (tc.fUseRof) { + if (!collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + return false; // if at least one event cut is not satisfied + } + } + + // *) No same-bunch pile-up cut + if (tc.fUsePileUp) { + if (!collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return false; // if at least one event cut is not satisfied + } + } + + // *) Vertex from ITS and TPC tracks cut + if (tc.fUseITSTPC) { + if (!collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + return false; // if at least one event cut is not satisfied + } + } + + // *) All ITS layers good cut + if (tc.fUseITSLayers) { + if (!collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + return false; // if at least one event cut is not satisfied + } + } + + // *) z vertex from FT0 and from tracks agree cut + if (tc.fUseZFT0vsPV) { + if (!collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; // if at least one event cut is not satisfied + } + } + + // *) No high-multiplicity collision in previous readout frame cut + if (tc.fUsePrevRof) { + if (!collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + return false; // if at least one event cut is not satisfied + } + } + + return true; // all event cuts passed + } + + //__ PARTICLE CUTS __ + template + bool particleCuts(T const& track) + { + + // *) pt cut + if (tc.fUsePtCut) { + if (!(tc.fPt[0] <= track.pt() && track.pt() < tc.fPt[1])) { + return false; // if at least one event cut is not satisfied + } + } + + // *) eta cut + if (tc.fUseEtaCut) { + if (!(tc.fEta[0] <= track.eta() && track.eta() < tc.fEta[1])) { + return false; // if at least one particle cut is not satisfied + } + } + + return true; // all particle cuts passed + } + + //__ Fill all event histograms __ #ba = EBefore or EAfter + template + void fillEventHistograms(T1 const& collision, T2 const& tracks, EBeforeAfter ba) + { + + if constexpr (rs == ERec || rs == ERecAndSim) { + + float centRec = getCentrality(collision); + + ec.fEventHistograms[EMultiplicity][ERec][ba]->Fill(tracks.size()); // defined as total number of available particles + ec.fEventHistograms[EReferenceMultiplicity][ERec][ba]->Fill(getReferenceMultiplicity(collision)); + ec.fEventHistograms[ECentrality][ERec][ba]->Fill(centRec); + ec.fEventHistograms[EVertexX][ERec][ba]->Fill(collision.posX()); + ec.fEventHistograms[EVertexY][ERec][ba]->Fill(collision.posY()); + ec.fEventHistograms[EVertexZ][ERec][ba]->Fill(collision.posZ()); + + // QA: reference multiplicity vs. number of contributors + if (tc.fFillQA) { + qa.fHistRefMultVsNContrib[ba]->Fill(getReferenceMultiplicity(collision), collision.numContrib()); + } + + if constexpr (rs == ERecAndSim) { + if (!collision.has_mcCollision()) { + LOGF(debug, " No MC collision for this collision, skip simulated part..."); + return; + } + auto mcCollision = collision.mcCollision(); + + ec.fEventHistograms[EMultiplicity][ESim][ba]->Fill(tracks.size()); + ec.fEventHistograms[EVertexX][ESim][ba]->Fill(mcCollision.posX()); + ec.fEventHistograms[EVertexY][ESim][ba]->Fill(mcCollision.posY()); + ec.fEventHistograms[EVertexZ][ESim][ba]->Fill(mcCollision.posZ()); + + // impact parameter and centrality calculated from MC + float b = mcCollision.impactParameter(); + if (b > 0.) { // only if the impact parameter is stored in the MC + float centSim = centralityFromImpactParameter(b); + ec.fEventHistograms[EImpactParameter][ESim][ba]->Fill(b); + ec.fEventHistograms[ECentrality][ESim][ba]->Fill(centSim); + + // QA centrality sim vs centrality rec after the cuts + if (tc.fFillQA && ba == EAfter) { + qa.fHistCentralityRecVsSim->Fill(centSim, centRec); + } // if (tc.fFillQA && ba == EAfter) + + } // if (b > 0.) + + } // if constexpr (rs == ERecAndSim) + + } // if constexpr (rs == ERec || rs == ERecAndSim) + + } // fillEventHistograms + + template + void steer(T1 const& collision, T2 const& tracks) + { + + // Dry run: + if (tc.fDryRun) { + return; + } + + // weights are run-specific: load them again when the run number changes + int runNumber = collision.bc().runNumber(); + if (runNumber != tc.fCurrentRunNumber) { + if (tc.fUsePhiWeights) { + histPhiWeights = loadWeights("histWithEfficiencyCorrections_1", "histPhiWeights", runNumber); + } + if (tc.fUsePtWeights) { + histPtWeights = loadWeights("histWithEfficiencyCorrections_2", "histPtWeights", runNumber); + } + if (tc.fUseEtaWeights) { + histEtaWeights = loadWeights("histWithEfficiencyCorrections_3", "histEtaWeights", runNumber); + } + tc.fCurrentRunNumber = runNumber; + } + + // Print info for each collision, only if switched on in the config: + if (tc.fVerbose) { + LOGF(info, "Run number: %d", collision.bc().runNumber()); + LOGF(info, "Centrality: %f", getCentrality(collision)); + LOGF(info, "Vertex X position: %f", collision.posX()); + } + + // Fill event histograms before event cuts + fillEventHistograms(collision, tracks, EBefore); + + // Event cuts: leave this collision if it doesn't pass + if (!eventCuts(collision)) { + return; + } + + // Main loop over particles: + // Q-vector of harmonic 2 and sums of weights, for <2> of this event + double q2Re = 0., q2Im = 0., sumW = 0., sumW2 = 0.; + + auto track = tracks.iteratorAt(0); // set the type and scope from one instance + for (int64_t i = 0; i < tracks.size(); i++) { + + track = tracks.iteratorAt(i); + + // Print track azimuthal angle: + // LOGF(info, "Track azimuthal angle: %f", track.phi()); + + // Fill reconstructed ...: + if constexpr (rs == ERec || rs == ERecAndSim) { + // Fill tracks before cuts: + + pc.fParticleHistograms[EPt][ERec][EBefore]->Fill(track.pt()); + pc.fParticleHistograms[EPhi][ERec][EBefore]->Fill(track.phi()); + + // ... and corresponding MC truth simulated (before cuts): + // See https://github.com/AliceO2Group/O2Physics/blob/master/Tutorials/src/mcHistograms.cxx + // See https://aliceo2group.github.io/analysis-framework/docs/datamodel/ao2dTables.html#montecarlo + + if constexpr (rs == ERecAndSim) { + if (track.has_mcParticle()) { + auto mcparticle = track.mcParticle(); // corresponding MC truth simulated particle + pc.fParticleHistograms[EPt][ESim][EBefore]->Fill(mcparticle.pt()); + pc.fParticleHistograms[EPhi][ESim][EBefore]->Fill(mcparticle.phi()); + } else { + LOGF(debug, " No MC particle for this track..."); + } + } + + // Particle cuts: skip this track, not the whole event + if (!particleCuts(track)) { + continue; + } + + // Fill tracks after cuts: + + pc.fParticleHistograms[EPt][ERec][EAfter]->Fill(track.pt()); + pc.fParticleHistograms[EPhi][ERec][EAfter]->Fill(track.phi()); + + // add this particle to the Q-vector + const double w = getParticleWeight(track.phi(), track.pt(), track.eta()); + q2Re += w * std::cos(2. * track.phi()); + q2Im += w * std::sin(2. * track.phi()); + sumW += w; + sumW2 += w * w; + + if constexpr (rs == ERecAndSim) { + if (!track.has_mcParticle()) { + continue; + } + + auto mcparticle = track.mcParticle(); // corresponding MC truth simulated particles + pc.fParticleHistograms[EPt][ESim][EAfter]->Fill(mcparticle.pt()); + pc.fParticleHistograms[EPhi][ESim][EAfter]->Fill(mcparticle.phi()); + + } // end of if constexpr (rs == ERecAndSim) { + + } // if constexpr (rs == ERec || rs == ERecAndSim) { + + } // end of for (int64_t i = 0; i < tracks.size(); i++) { + + // Fill event histograms after event and particle cuts + // <2> of this event, filled vs. centrality with event weight sumW^2 - sumW2 (= M(M-1) for unit weights) + if constexpr (rs == ERec || rs == ERecAndSim) { + const double denominator = sumW * sumW - sumW2; + if (denominator > 0.) { + const double two = (q2Re * q2Re + q2Im * q2Im - sumW2) / denominator; + cc.fProfTwo->Fill(getCentrality(collision), two, denominator); + } + } + + fillEventHistograms(collision, tracks, EAfter); + + } // end of template void steer(T1 const& collision, T2 const& tracks) { + + // *) Initialize and book all objects: + void init(InitContext&) + { + + // ... code to book and initialize all analysis objects ... + + // *) Set automatically what to process, from an implicit variable "doprocessSomeProcessName" within a PROCESS_SWITCH clause: + tc.fProcess[EProcessRec] = doprocessRec; + tc.fProcess[EProcessRecSim] = doprocessRecSim; + tc.fProcess[EProcessSim] = doprocessSim; + + // *) Configure your task using configurables in the json file: + tc.fDryRun = cfDryRun; + tc.fFillQA = cfFillQA; + tc.fVerbose = cfVerbose; + tc.fSigmaInel = cfSigmaInel; + + tc.fUsePhiWeights = cfUsePhiWeights; + tc.fUsePtWeights = cfUsePtWeights; + tc.fUseEtaWeights = cfUseEtaWeights; + if ((tc.fUsePhiWeights || tc.fUsePtWeights || tc.fUseEtaWeights) && cfFileWithWeights.value.empty()) { + LOGF(fatal, "weights are switched on, but cfFileWithWeights is empty"); + } + + // vertex Z cut copy the swtich and range + tc.fUseVertexZCut = cfUseVertexZCut; + std::vector lVzCut = cfVertexZ.value; + + tc.fVertexZ[0] = lVzCut[0]; + tc.fVertexZ[1] = lVzCut[1]; + + // pt cut copy switch and range + tc.fUsePtCut = cfUsePtCut; + std::vector lPtCut = cfPt.value; + + tc.fPt[0] = lPtCut[0]; + tc.fPt[1] = lPtCut[1]; + + // eta cut copy switch and range + tc.fUseEtaCut = cfUseEtaCut; + std::vector lEtaCut = cfEta.value; + + tc.fEta[0] = lEtaCut[0]; + tc.fEta[1] = lEtaCut[1]; + + // technical event cuts: copy the switches + tc.fUseTimeRange = cfUseTimeRange; + tc.fUseRof = cfUseRof; + tc.fUsePileUp = cfUsePileUp; + tc.fUseITSTPC = cfUseITSTPC; + tc.fUseITSLayers = cfUseITSLayers; + tc.fUseZFT0vsPV = cfUseZFT0vsPV; + tc.fUsePrevRof = cfUsePrevRof; + + // translate the centrality estimator string into the enum + if (cfCentralityEstimator.value == "FT0C") { + tc.fCentralityEstimator = ECentFT0C; + } else if (cfCentralityEstimator.value == "FT0M") { + tc.fCentralityEstimator = ECentFT0M; + } else if (cfCentralityEstimator.value == "FT0A") { + tc.fCentralityEstimator = ECentFT0A; + } else { + LOGF(fatal, "Unknown centrality estimator in json: %s", cfCentralityEstimator.value.c_str()); + } + + // same for the reference multiplicity estimator + if (cfReferenceMultiplicityEstimator.value == "TPC") { + tc.fReferenceMultiplicityEstimator = ERefMultTPC; + } else if (cfReferenceMultiplicityEstimator.value == "FT0C") { + tc.fReferenceMultiplicityEstimator = ERefMultFT0C; + } else if (cfReferenceMultiplicityEstimator.value == "FT0M") { + tc.fReferenceMultiplicityEstimator = ERefMultFT0M; + } else { + LOGF(fatal, "Unknown reference multiplicity estimator in json: %s", cfReferenceMultiplicityEstimator.value.c_str()); + } + + // *) Book base list: + TList* temp = new TList(); + temp->SetOwner(true); + fBaseList.setObject(temp); + + // *) Book and nest all other TLists: + pc.fParticleHistogramsList = new TList(); + pc.fParticleHistogramsList->SetName("ParticleHistograms"); + pc.fParticleHistogramsList->SetOwner(true); + fBaseList->Add(pc.fParticleHistogramsList); // any nested TList in the base TList appears as a subdir in the output ROOT file + + ec.fEventHistogramsList = new TList(); + ec.fEventHistogramsList->SetName("EventHistograms"); + ec.fEventHistogramsList->SetOwner(true); + fBaseList->Add(ec.fEventHistogramsList); // any nested TList in the base TList appears as a subdir in the output ROOT file + + // correlations: <<2>> vs. centrality, in the centrality classes of the reference paper + cc.fCorrelationsList = new TList(); + cc.fCorrelationsList->SetName("Correlations"); + cc.fCorrelationsList->SetOwner(true); + fBaseList->Add(cc.fCorrelationsList); + const std::array centBoundaries = {0., 5., 10., 20., 30., 40., 50., 60., 70., 80.}; + cc.fProfTwo = new TProfile("fProfTwo", "<<2>> = v_{2}{2}^{2} vs. centrality", centBoundaries.size() - 1, centBoundaries.data()); + cc.fProfTwo->GetXaxis()->SetTitle("centrality (%)"); + cc.fProfTwo->GetYaxis()->SetTitle("#LT#LT2#GT#GT"); + cc.fProfTwo->Sumw2(); + cc.fCorrelationsList->Add(cc.fProfTwo); + + if (tc.fFillQA) { // only if QA switched on + qa.fQAHistogramsList = new TList(); + qa.fQAHistogramsList->SetName("QA"); + qa.fQAHistogramsList->SetOwner(true); + fBaseList->Add(qa.fQAHistogramsList); + } + + // *) Book (pt distribution and azimuthal-angle histograms) + event quantities with binning defined through configurables in the json file: + std::vector lPtBins = cfPtBins.value; // define local array and initialize it from an array set in the configurables + int nBins = static_cast(lPtBins[0]); + float min = lPtBins[1]; + float max = lPtBins[2]; + + std::vector lPhiBins = cfPhiBins.value; // azimuthal angle + int nPhiBins = static_cast(lPhiBins[0]); + float phiMin = lPhiBins[1]; + float phiMax = lPhiBins[2]; + + std::vector lMultBins = cfMultiplicityBins.value; // multiplicity + int nMultBins = static_cast(lMultBins[0]); + float multMin = lMultBins[1]; + float multMax = lMultBins[2]; + + std::vector lRefmultBins = cfRefmultiplicityBins.value; // reference multiplicity + int nRefMultBins = static_cast(lRefmultBins[0]); + float refMultMin = lRefmultBins[1]; + float refMultMax = lRefmultBins[2]; + + std::vector lCentBins = cfCentralityBins.value; // centrality + int nCentBins = static_cast(lCentBins[0]); + float centMin = lCentBins[1]; + float centMax = lCentBins[2]; + + std::vector lBBins = cfImpactparameterBins.value; // impact parameter + int nBBins = static_cast(lBBins[0]); + float bMin = lBBins[1]; + float bMax = lBBins[2]; + + std::vector lVxBins = cfVertexXBins.value; // vertex X + int nvxBins = static_cast(lVxBins[0]); + float vxMin = lVxBins[1]; + float vxMax = lVxBins[2]; + + std::vector lVyBins = cfVertexYBins.value; // vertex y + int nvyBins = static_cast(lVyBins[0]); + float vyMin = lVyBins[1]; + float vyMax = lVyBins[2]; + + std::vector lVzBins = cfVertexZBins.value; // vertex z + int nvzBins = static_cast(lVzBins[0]); + float vzMin = lVzBins[1]; + float vzMax = lVzBins[2]; + + // histogram booked before and after cuts + std::array baNames = {"before", "after"}; + // book rec histograms only if a process with reconstructed data runs, sim histograms only if one with simulated data runs + const bool bookRec = doprocessRec || doprocessRecSim; + const bool bookSim = doprocessRecSim || doprocessSim; + for (int ba = 0; ba < EBeforeAfter_N; ba++) { + + if (bookRec) { + // par + pc.fParticleHistograms[EPt][ERec][ba] = new TH1F(Form("fHistPt[ERec][%s]", baNames[ba]), Form("pt distribution for reconstructed particles (%s cuts)", baNames[ba]), nBins, min, max); + pc.fParticleHistograms[EPt][ERec][ba]->GetXaxis()->SetTitle("p_{T}"); + pc.fParticleHistogramsList->Add(pc.fParticleHistograms[EPt][ERec][ba]); + + pc.fParticleHistograms[EPhi][ERec][ba] = new TH1F(Form("fHistPhi[ERec][%s]", baNames[ba]), Form("Azimuthal-angle distribution for reconstructed particles (%s cuts)", baNames[ba]), nPhiBins, phiMin, phiMax); + pc.fParticleHistograms[EPhi][ERec][ba]->GetXaxis()->SetTitle("#varphi (rad)"); + pc.fParticleHistogramsList->Add(pc.fParticleHistograms[EPhi][ERec][ba]); + // ev + ec.fEventHistograms[EMultiplicity][ERec][ba] = new TH1F(Form("fHistMultiplicity[ERec][%s]", baNames[ba]), Form("Multiplicity for reconstructed particles (%s cuts)", baNames[ba]), nMultBins, multMin, multMax); + ec.fEventHistograms[EMultiplicity][ERec][ba]->GetXaxis()->SetTitle("total number of available particles"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EMultiplicity][ERec][ba]); + + // reference multiplicity (only reconstructed) + ec.fEventHistograms[EReferenceMultiplicity][ERec][ba] = new TH1F(Form("fHistReferenceMultiplicity[ERec][%s]", baNames[ba]), Form("Reference multiplicity, reconstructed (%s cuts)", baNames[ba]), nRefMultBins, refMultMin, refMultMax); + ec.fEventHistograms[EReferenceMultiplicity][ERec][ba]->GetXaxis()->SetTitle("reference multiplicity"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EReferenceMultiplicity][ERec][ba]); + + ec.fEventHistograms[ECentrality][ERec][ba] = new TH1F(Form("fHistCentrality[ERec][%s]", baNames[ba]), Form("Centrality for reconstructed particles (%s cuts)", baNames[ba]), nCentBins, centMin, centMax); + ec.fEventHistograms[ECentrality][ERec][ba]->GetXaxis()->SetTitle("centrality (%)"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[ECentrality][ERec][ba]); + + ec.fEventHistograms[EVertexX][ERec][ba] = new TH1F(Form("fHistVertexX[ERec][%s]", baNames[ba]), Form("Vertex X for reconstructed particles (%s cuts)", baNames[ba]), nvxBins, vxMin, vxMax); + ec.fEventHistograms[EVertexX][ERec][ba]->GetXaxis()->SetTitle("vertex X"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EVertexX][ERec][ba]); + + ec.fEventHistograms[EVertexY][ERec][ba] = new TH1F(Form("fHistVertexY[ERec][%s]", baNames[ba]), Form("Vertex Y for reconstructed particles (%s cuts)", baNames[ba]), nvyBins, vyMin, vyMax); + ec.fEventHistograms[EVertexY][ERec][ba]->GetXaxis()->SetTitle("vertex Y"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EVertexY][ERec][ba]); + + ec.fEventHistograms[EVertexZ][ERec][ba] = new TH1F(Form("fHistVertexZ[ERec][%s]", baNames[ba]), Form("Vertex Z for reconstructed particles (%s cuts)", baNames[ba]), nvzBins, vzMin, vzMax); + ec.fEventHistograms[EVertexZ][ERec][ba]->GetXaxis()->SetTitle("vertex Z"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EVertexZ][ERec][ba]); + } + if (bookSim) { + pc.fParticleHistograms[EPt][ESim][ba] = new TH1F(Form("fHistPt[ESim][%s]", baNames[ba]), Form("pt distribution for simulated particles (%s cuts)", baNames[ba]), nBins, min, max); + pc.fParticleHistograms[EPt][ESim][ba]->GetXaxis()->SetTitle("p_{T}"); + pc.fParticleHistogramsList->Add(pc.fParticleHistograms[EPt][ESim][ba]); + + pc.fParticleHistograms[EPhi][ESim][ba] = new TH1F(Form("fHistPhi[ESim][%s]", baNames[ba]), Form("Azimuthal-angle distribution for simulated particles (%s cuts)", baNames[ba]), nPhiBins, phiMin, phiMax); + pc.fParticleHistograms[EPhi][ESim][ba]->GetXaxis()->SetTitle("#varphi (rad)"); + pc.fParticleHistogramsList->Add(pc.fParticleHistograms[EPhi][ESim][ba]); + // ev + ec.fEventHistograms[EMultiplicity][ESim][ba] = new TH1F(Form("fHistMultiplicity[ESim][%s]", baNames[ba]), Form("Multiplicity for simulated particles (%s cuts)", baNames[ba]), nMultBins, multMin, multMax); + ec.fEventHistograms[EMultiplicity][ESim][ba]->GetXaxis()->SetTitle("total number of available particles"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EMultiplicity][ESim][ba]); + // sim from impact parameter + ec.fEventHistograms[ECentrality][ESim][ba] = new TH1F(Form("fHistCentrality[ESim][%s]", baNames[ba]), Form("Centrality for simulated particles, from impact parameter (%s cuts)", baNames[ba]), nCentBins, centMin, centMax); + ec.fEventHistograms[ECentrality][ESim][ba]->GetXaxis()->SetTitle("centrality (%)"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[ECentrality][ESim][ba]); + // impact parameter, only simulated + ec.fEventHistograms[EImpactParameter][ESim][ba] = new TH1F(Form("fHistImpactParameter[ESim][%s]", baNames[ba]), Form("Impact parameter, simulated (%s cuts)", baNames[ba]), nBBins, bMin, bMax); + ec.fEventHistograms[EImpactParameter][ESim][ba]->GetXaxis()->SetTitle("b (fm)"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EImpactParameter][ESim][ba]); + + ec.fEventHistograms[EVertexX][ESim][ba] = new TH1F(Form("fHistVertexX[ESim][%s]", baNames[ba]), Form("Vertex X for simulated particles (%s cuts)", baNames[ba]), nvxBins, vxMin, vxMax); + ec.fEventHistograms[EVertexX][ESim][ba]->GetXaxis()->SetTitle("vertex X"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EVertexX][ESim][ba]); + + ec.fEventHistograms[EVertexY][ESim][ba] = new TH1F(Form("fHistVertexY[ESim][%s]", baNames[ba]), Form("Vertex Y for simulated particles (%s cuts)", baNames[ba]), nvyBins, vyMin, vyMax); + ec.fEventHistograms[EVertexY][ESim][ba]->GetXaxis()->SetTitle("vertex Y"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EVertexY][ESim][ba]); + + ec.fEventHistograms[EVertexZ][ESim][ba] = new TH1F(Form("fHistVertexZ[ESim][%s]", baNames[ba]), Form("Vertex Z for simulated particles (%s cuts)", baNames[ba]), nvzBins, vzMin, vzMax); + ec.fEventHistograms[EVertexZ][ESim][ba]->GetXaxis()->SetTitle("vertex Z"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EVertexZ][ESim][ba]); + } + } // end of for (int ba = 0; ba < 2; ba++) + + // QA histograms, booked only if cfFillQA is true + if (tc.fFillQA) { + qa.fHistCentralityRecVsSim = new TH2F("fHistCentralityRecVsSim", "Centrality: reconstructed vs. simulated", nCentBins, centMin, centMax, nCentBins, centMin, centMax); + qa.fHistCentralityRecVsSim->GetXaxis()->SetTitle("centrality simulated (%)"); + qa.fHistCentralityRecVsSim->GetYaxis()->SetTitle("centrality reconstructed (%)"); + qa.fQAHistogramsList->Add(qa.fHistCentralityRecVsSim); + + // reference multiplicity vs. number of contributors, before and after cuts + std::vector lContribBins = cfNContribBins.value; + for (int ba = 0; ba < EBeforeAfter_N; ba++) { + qa.fHistRefMultVsNContrib[ba] = new TH2F(Form("fHistRefMultVsNContrib[%s]", baNames[ba]), Form("reference multiplicity vs. number of contributors (%s cuts)", baNames[ba]), nRefMultBins, refMultMin, refMultMax, static_cast(lContribBins[0]), lContribBins[1], lContribBins[2]); + qa.fHistRefMultVsNContrib[ba]->GetXaxis()->SetTitle("reference multiplicity"); + qa.fHistRefMultVsNContrib[ba]->GetYaxis()->SetTitle("number of contributors"); + qa.fQAHistogramsList->Add(qa.fHistRefMultVsNContrib[ba]); + } + } + + } // end of void init(InitContext&) { + + // A) Process only reconstructed data: + void processRec(CollisionRec const& collision, aod::BCs const&, TracksRec const& tracks) + { + // ... + + // *) steer all analysis steps: + steer(collision, tracks); + } + PROCESS_SWITCH(MultiparticleCorrelationsDm, processRec, "process only reconstructed data", true); // yes, keep always one process switch "true", so that there is default running version + + // ------------------------------------------- + + // B) Process both reconstructed and corresponding MC truth simulated data: + void processRecSim(CollisionRecSim const& collision, aod::BCs const&, TracksRecSim const& tracks, aod::McParticles const&, aod::McCollisions const&) + { + steer(collision, tracks); + } + PROCESS_SWITCH(MultiparticleCorrelationsDm, processRecSim, "process both reconstructed and corresponding MC truth simulated data", false); + + // ------------------------------------------- + + // C) Process only simulated data: + void processSim(CollisionSim const& /*collision*/, aod::BCs const&, TracksSim const& /*tracks*/) + { + // steer(collision, tracks); // TBI 20241105 not ready yet, but I do not really need this one urgently, since RecSim is working, and I need that one for efficiencies... + } + PROCESS_SWITCH(MultiparticleCorrelationsDm, processSim, "process only simulated data", false); + +}; // struct MultiparticleCorrelationsDm { + +// *) The final touch: +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + }; +} // WorkflowSpec... diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx index 9fb3bed9a13..4d4ce5047f1 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx @@ -13,6 +13,7 @@ /// \brief Multiparticle correlation in O2 Framework /// \author yuanjun.mei@cern.ch +#include "Common/CCDB/EventSelectionParams.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" @@ -31,6 +32,7 @@ #include #include +#include #include #include #include @@ -38,7 +40,9 @@ #include #include #include +#include #include +#include #include #include @@ -46,14 +50,13 @@ #include #include -#include +#include #include #include using namespace o2; using namespace o2::framework; using namespace o2::constants; -using namespace std; // Definitions of join tables for Run 3 analysis: using EventSelection = soa::Join; @@ -68,6 +71,25 @@ using TracksSim = aod::McParticles; using TrackSim = aod::McParticles::iterator; // *) Define enums: +enum ERecSim { + eRec = 0, + eSim, + eRecAndSim, + eRecSim_N +}; + +enum EProcess { + eProcessRec = 0, + eProcessRecSim, + eProcessSim, + eProcess_N +}; + +enum ERealMC { + eReal = 0, + eMC +}; + enum ECentralityEstimator { eFT0C = 0, eFT0M, @@ -76,6 +98,12 @@ enum ECentralityEstimator { eCentralityEstimator_N }; +enum EOccupancyEstimator { + eOccuTrack = 0, + eOccuFT0C, + eOccupancyEstimator_N +}; + enum EMultiplicityTables { eMultTPC = 0, eMultFV0M, @@ -85,29 +113,57 @@ enum EMultiplicityTables { eMultiplicityTables_N }; -enum ERecSim { - eRec = 0, - eSim, - eRecAndSim, - eRecSim_N +enum EEventCuts { + eSel8Cut = 0, + eIsGoodITSLayersAll, + eNoCollInTimeRangeStandard, + eNoCollInRofStandard, + eIsGoodZvtxFT0vsPV, + eNoHighMultCollInPrevRof, + eNoSameBunchPileUp, + eIsVertexITSTPC, + eCentCut, + eVertexZCut, + eNContribCut, + eOccupancyCut, + eCorrCentCut, + eMultNContribCut, + EEventCuts_N }; -enum ECuts { +enum EParticleCuts { + ePtCut = 0, + eEtaCut, + eChargeCut, + eTPCNumClusterCut, + eDCAxyCut, + eDCAzCut, + EParticleCuts_N +}; + +enum EBeforeAfterCuts { eBefore = 0, eAfter, - eCuts_N + eBeforeAfterCuts_N }; -enum ERealMC { - eReal = 0, - eMC +enum EUpperLowerBounds { + eUpper = 0, + eLower, + eUpperLowerBounds_N }; -enum EProcess { - eProcessRec = 0, - eProcessRecSim, - eProcessSim, - eProcess_N +enum EEventHistograms { + eHistCentrality = 0, + eHistMult, + eHistRefMult, + eHistVertexX, + eHistVertexY, + eHistVertexZ, + eHistImpactParameter, + eHistNumContrib, + eHistOccupancy, + eEventHistograms_N }; enum EParticleHistograms { @@ -118,15 +174,11 @@ enum EParticleHistograms { eParticleHistograms_N }; -enum EEventHistograms { - eHistCentrality = 0, - eHistMultiplicity, - eHistVertexX, - eHistVertexY, - eHistVertexZ, - eHistImpactParameter, - eHistReferenceMultiplicity, - eEventHistograms_N +enum EWeightsHistograms { + eWPhiRec = 0, + eWPhiSim, + eWPt, + eWeightsHistograms_N }; enum EMiscHistograms { @@ -134,134 +186,206 @@ enum EMiscHistograms { eMiscHistograms_N }; -enum EExternalHistograms { - ePhi = 0, - ePt, - eExternalHistograms_N +enum EObservables { + eV22 = 0, + eV22Gap, + eV24, + eObservables_N }; -enum EObservables { - eProfTwo = 0, - eObservablesHist_N +enum EIntValHarmonics { + eIV2 = 0, + eIV3, + eIV4, + eIntValHarmonics_N }; // *) Lists of names: -static constexpr std::array CentralityEstimatorNames = { - "FT0C", - "FT0M", - "FV0A", - "NTPV"}; - -static constexpr std::array MultiplicityTablesNames = { - "multTPC", - "multFV0M", - "multFT0C", - "multFT0M", - "multNTracksPV"}; - -static constexpr std::array WeightsNames = { - "ePhi", - "ePt"}; - -static constexpr std::array CutsNames = { - "eBefore", - "eAfter"}; +static constexpr std::array CentralityEstimatorNames = {"FT0C", "FT0M", "FV0A", "NTPV"}; +static constexpr std::array MultiplicityTablesNames = {"multTPC", "multFV0M", "multFT0C", "multFT0M", "multNTracksPV"}; +static constexpr std::array OccupancyEstimatorNames = {"track", "FT0C"}; + +static constexpr std::array ParticleHistTitles = {"p_{T}", "#varphi", "#eta", "Particle charge"}; +static constexpr std::array ParticleHistNames = {"eHistPt", "eHistPhi", "eHistEta", "eHistCharge"}; + +static constexpr std::array EventHistTitles = {"Centrality", "Multiplicity (trak.size())", "Reference Multiplicity", "Vertex X", "Vertex Y", "Vertex Z", "Impact Parameter", "Number of Contributor", "Occupancy"}; +static constexpr std::array EventHistNames = {"eHistCentrality", "eHistMult", "eHistRefMult", "eHistVertexX", "eHistVertexY", "eHistVertexZ", "eHistImpactParameter", "eHistNumContrib", "eHistOccupancy"}; + +static constexpr std::array WeightsNames = {"eWPhiRec", "eWPhiSim", "eWPt"}; + +static constexpr std::array CutsNames = {"eBefore", "eAfter"}; +static constexpr std::array CutsTitles = {"before", "after"}; + +static constexpr std::array ObservablesNames = {"eV22", "eV22Gap", "eV24"}; +static constexpr std::array ObservablesTitles = {"v_{2}^{2}", "v_{2}^{2} with eta gap", "v_{2}^{4}"}; + +static constexpr std::array IntValNames = {"eIV2", "eIV3", "eIV4"}; +static constexpr std::array IntValTitles = {"v_{2}^{2}", "v_{3}^{2}", "v_{4}^{2}"}; // *) Main task: struct MultiparticleCorrelationsMei // this name is used in lower-case format to name the TDirectoryFile in AnalysisResults.root { // *) Base TList to hold all output objects: TString sBaseListName = "Default list name"; // yes, I declare it separately, because I need it also later in BailOut() function - OutputObj fBaseList{sBaseListName.Data(), - OutputObjHandlingPolicy::AnalysisObject, - OutputObjSourceType::OutputObjSource}; + OutputObj fBaseList{sBaseListName.Data(), OutputObjHandlingPolicy::AnalysisObject, OutputObjSourceType::OutputObjSource}; // *) CCDB: - Service ccdb{}; // support for offline callibration data base, not needed for the time being... + Service ccdb{}; // support for offline callibration data base Service pdg{}; // *) Define configurables: - Configurable centralityEstimator{"centralityEstimator", 0, "centrality estimator: 0=FT0C, 1=FT0M, 2=FV0A, 3=NTPV"}; - Configurable multiplicityTables{"multiplicityTables", 0, "multiplicity tables: 0=multTPC, 1=multFV0M, 2=multFT0C, 3=multFT0M, 4=multNTracksPV"}; Configurable cfDryRun{"cfDryRun", false, "book all histos and run without filling and calculating anything"}; + Configurable cfCentralityEstimator{"cfCentralityEstimator", 0, "centrality estimator: 0=FT0C, 1=FT0M, 2=FV0A, 3=NTPV"}; + Configurable cfMultiplicityTables{"cfMultiplicityTables", 2, "multiplicity tables: 0=multTPC, 1=multFV0M, 2=multFT0C, 3=multFT0M, 4=multNTracksPV"}; + Configurable cfOccupancyEstimator{"cfOccupancyEstimator", 0, "occupancy estimator: 0=trackOccupancyInTimeRange, 1=ft0cOccupancyInTimeRange"}; - // *) external root files + // *) External root files Configurable cfExternalFileSwitch{"cfExternalFileSwitch", false, "choose to include external root files or not"}; - Configurable cfFileWithWeights{"cfFileWithWeights", "/alice-ccdb.cern.ch/Users/m/mei/O2challenge-", "path to external ROOT file which holds all particle weights"}; - - // *) binnings - Configurable cfALICECentBinSwitch{"cfALICECentBinSwitch", true, "switch on or off to use ALICE default binning"}; - Configurable> cfCentBins{"cfCentBins", {100, 0., 100.}, "nCentBins, centMin, centMax"}; - Configurable> cfMultBins{"cfMultBins", {400, 0., 40000.}, "Multiplicity bins: nMultBins, multMin, multMax"}; - Configurable> cfMultBinsRef{"cfMultBinsRef", {400, 0., 40000.}, "Reference mult bins: nMultBins, multMin, multMax"}; - Configurable> cfVxBins{"cfVxBins", {300, -0.04, 0.04}, "Vertex X hist: nVxBins, vxMin, vxMax"}; - Configurable> cfVyBins{"cfVyBins", {300, -0.01, 0.01}, "Vertex Y hist: nVyBins, vyMin, vyMax"}; - Configurable> cfVzBins{"cfVzBins", {300, -20., 20.}, "Vertex Z hist: nVzBins, vzMin, vzMax"}; + Configurable cfFileWithWeights{"cfFileWithWeights", "/alice-ccdb.cern.ch/Users/m/mei/thesis-", "path to external ROOT file which holds all particle weights"}; + + // *) Binnings + Configurable> cfCentBins{"cfCentBins", {100, 0., 100.}, "Centrality bins: nCentBins, centMin, centMax"}; + Configurable> cfMultBins{"cfMultBins", {300, 0., 30000.}, "Multiplicity bins: nMultBins, multMin, multMax"}; + Configurable> cfRefMultBins{"cfRefMultBins", {400, 0., 25000.}, "Reference mult bins: nMultBins, multMin, multMax"}; + Configurable> cfNumContribBins{"cfNumContribBins", {400, 0., 7000.}, "Number of contributors bins: nBinsContrib, contribMin, contribMax"}; + Configurable> cfVxBins{"cfVxBins", {500, -0.04, 0.04}, "Vertex X hist: nVxBins, vxMin, vxMax"}; + Configurable> cfVyBins{"cfVyBins", {500, -0.015, 0.015}, "Vertex Y hist: nVyBins, vyMin, vyMax"}; + Configurable> cfVzBins{"cfVzBins", {500, -20., 20.}, "Vertex Z hist: nVzBins, vzMin, vzMax"}; Configurable> cfIpBins{"cfIpBins", {100, 0., 20.}, "Impact parameters hist (MC only): nIPBins, ipMin, ipMax"}; + Configurable> cfOccuBins{"cfOccuBins", {300, 0., 10000.}, "Occupancy hist (Rec only): nOccuBins, occuMin, occuMax"}; - Configurable> cfPtBins{"cfPtBins", {2000, 0., 5.}, "nPtBins, ptMin, ptMax"}; + Configurable> cfPtBins{"cfPtBins", {4000, 0., 10.}, "nPtBins, ptMin, ptMax"}; Configurable> cfPhiBins{"cfPhiBins", {180, 0., math::TwoPI}, "nPhiBins, phiMin, phiMax"}; Configurable> cfEtaBins{"cfEtaBins", {800, -3., 3.}, "nEtaBins, etaMin, etaMax"}; + Configurable> cfQASelTrackBins{"cfQASelTrackBins", {200, 0., 15000., 400, 0., 6000.}, "bins for hist of selected tracks of QA: {nQASelTrackBinsBefore, minBefore, MaxBefore, nQASelTrackBinsAfter, minAfter, MaxAfter}"}; + // *) Cuts - // event level cuts Configurable cfMasterCutSwitch{"cfMasterCutSwitch", true, "switch on or off all cuts"}; - Configurable cfEventCutSwitch{"cfEventCutSwitch", true, "switch to apply vertex z position cut"}; - Configurable> cfVertexZCutRange{"cfVertexZCutRange", {-10, 10.}, "vertex z position range: {min, max}[cm], with convention: min <= Vz < max"}; - // particle level cuts - Configurable cfPtCutSwitch{"cfPtCutSwitch", true, "switch to apply pt cut"}; - Configurable> cfPtCutRange{"cfPtCutRange", {0.2, 5.}, "pt cut range: {min, max}, with convention: min <= pt < max"}; - Configurable cfEtaCutSwitch{"cfEtaCutSwitch", true, "switch to apply pt cut"}; - Configurable> cfEtaCutRange{"cfEtaCutRange", {-0.8, 0.8}, "eta cut range: {min, max}, with convention: min <= eta < max"}; - Configurable cfChargeCutSwitch{"cfChargeCutSwitch", true, "switch to apply charge cut (cut neutral particle out)"}; + // event level cuts + Configurable> cfEventCutSwitch{"cfEventCutSwitch", {"1Sel8", "1IsGoodITSLayersAll", "1NoCollInTimeRangeStandard", "1NoCollInRofStandard", "1IsGoodZvtxFT0vsPV", "1NoHighMultCollInPrevRof", "1NoSameBunchPileUp", "1IsVertexITSTPC", "1Cent", "1VertexZ", "1NContrib", "1Occupancy", "1CorrCent", "1MultNContribCut"}, "event cuts switch, on and off by the first number before name"}; + Configurable> cfVertexZCutRange{"cfVertexZCutRange", {-10., 10.}, "vertex z position range: {min, max}[cm], with convention: min <= Vz <= max"}; + Configurable> cfCentCutRange{"cfCentCutRange", {0., 80.}, "centrality range: {min, max}, with convention: min <= cent <= max"}; + Configurable cfNContribCutInf{"cfNContribCutInf", 2, "Cuts on number of tracks used for the vertex, only numContrib > cfNContribCutInf survives"}; + Configurable cfOccupancyCutSup{"cfOccupancyCutSup", 6000, "Cuts on number of tracks used for the vertex, only occupancy < cfOccupancyCutSup survives"}; + Configurable> cfCorrCent{"cfCorrCent", {0, 2}, "choose the pair of centrality estimators for cut: 0=FT0C, 1=FT0M, 2=FV0A, 3=NTPV"}; + Configurable cfCorrCentCutRatio{"cfCorrCentCutRatio", 0.5, "correlation cut on centrality, only 2*|cent1 - cent2|/(cent1 + cent2) < cfCorrCentCutRatio survives"}; + Configurable> cfMultNContribBound{"cfMultNContribBound", {"0.160 * x + 153.563", "0.095 * x + -157.476"}, "geometric cuts on correlation of mult vs. nContrib switch, convention {upper bound, lower bound}"}; - // *) misc - Configurable sigmaInel{"sigmaInel", 7.71, "inelastic cross section in mb"}; - Configurable qualityAssuranceSwitch{"qualityAssuranceSwitch", false, "quality assurance switch"}; + // particle level cuts + Configurable> cfParticleCutSwitch{"cfParticleCutSwitch", {"1Pt", "1Eta", "1Charge", "1TPCNumCluster", "1DCAxy", "1DCAz"}, "particle cuts switch, on and off by the first number before name"}; + Configurable> cfPtCutRange{"cfPtCutRange", {0.2, 5.}, "pt cut range: {min, max}[GeV/c], with convention: min <= pt <= max"}; + Configurable> cfEtaCutRange{"cfEtaCutRange", {-0.8, 0.8}, "eta cut range: {min, max}, with convention: min <= eta <= max"}; + Configurable> cfTPCNumClusterCutRange{"cfTPCNumClusterCutRange", {70, 160}, "range of cut on the number of clusters associated with a reconstructed track: {min, max}, with convention: min <= TPCNcls <= max"}; + Configurable> cfDCAxyCutRange{"cfDCAxyCutRange", {-3.2, 3.2}, "range of cut on signed transverse distance of closest approach between the reconstructed track and the primary interaction vertex in x-y plane: {min, max}, with convention: min <= dcaXY <= max"}; + Configurable> cfDCAzCutRange{"cfDCAzCutRange", {-2.4, 2.4}, "range of cut on signed transverse distance of closest approach between the reconstructed track and the primary interaction vertex in z direction: {min, max}, with convention: min <= dcaZ <= max"}; + + // *) Misc + Configurable cfEtaGap{"cfEtaGap", 1.0, "eta gap for two particle correlation"}; + Configurable cfSigmaInel{"cfSigmaInel", 7.71, "inelastic cross section in mb"}; + Configurable cfQualityAssuranceSwitch{"cfQualityAssuranceSwitch", false, "quality assurance switch"}; + Configurable cfRunMessageSwitch{"cfRunMessageSwitch", false, "run message switch"}; + Configurable cfInternalValidationSwitch{"cfInternalValidationSwitch", false, "internal validation switch"}; + Configurable> cfIVHarmonics{"cfIVHarmonics", {0.06, 0.07, 0.08}, "harmonics for internal validation: {v_2, v_3, v_4, ...}"}; + Configurable cfRNGSeed{"cfRNGSeed", 0, "seed for random number generator"}; + Configurable> cfQASelTrackCentRange{"cfQASelTrackCentRange", {60., 70.}, "centrality range for hist of selected tracks for QA"}; // *) Define and initialize all data members to be called in the main process* functions: // **) Task configuration: struct TaskConfiguration { + float fSigmaInel = 7.71; + float fEtaGap = 1.0; + float fCorrCentCutRatio = 0.5; + + std::vector fEventCutSwitch; + std::vector fVertexZCutRange = {-10., 10.}; + std::vector fCentCutRange = {0., 80.}; + std::vector fCorrCent = {0, 2}; + std::vector fMultNContribBound; + std::vector fParticleCutSwitch; + std::vector fPtCutRange = {0.2, 5.}; + std::vector fEtaCutRange = {-0.8, 0.8}; + std::vector fTPCNumClusterCutRange = {70, 160}; + std::vector fDCAxyCutRange = {-3.2, 3.2}; + std::vector fDCAzCutRange = {-2.4, 2.4}; + + std::vector fIVHarmonics; + std::vector fQASelTrackCentRange; + + std::string fFileWithWeights = "/alice-ccdb.cern.ch/Users/m/mei/thesis-"; + + int fCentralityEstimator = 0; + int fMultiplicityTables = 0; + int fOccupancyEstimator = 0; + int fNContribCutInf = 2; + int fOccupancyCutSup = 6000; + int fRNGSeed = 0; + + bool fDryRun = false; // book all histos and run without filling and calculating anything + bool fExternalFileSwitch = false; + bool fMasterCutSwitch = true; + bool fQualityAssuranceSwitch = false; + bool fRunMessageSwitch = false; + bool fInternalValidationSwitch = false; + std::array fProcess{false}; // Set what to process. See enum EProcess for full description. Set via implicit variables within a PROCESS_SWITCH clause. - bool fDryRun = false; // book all histos and run without filling and calculating anything - } tc; // you have to prepend "tc." for all objects name in this group later in the code + } tc; - // **) Particle histograms: struct ParticleHistograms { - TList* fParticleHistList = nullptr; //!, 2>, eParticleHistograms_N> fParticleHist{}; + TList* fParticleHistList = nullptr; + std::array, 2>, eParticleHistograms_N> fParticleHist{}; } pc; - // *) Event histograms: struct EventHistograms { TList* fEventHistList = nullptr; - std::array, 2>, eEventHistograms_N> fEventHist{}; //! [ type - see enum EEventHistograms ][reco,sim][before, after event cuts] + std::array, 2>, eEventHistograms_N> fEventHist{}; //! [ type - see enum EEventHistograms ][reco,sim][before, after event cuts] } ec; - struct MiscHistograms { - TList* fMiscHistList = nullptr; - TH1D* fMiscHistRunNumber = nullptr; - } misc; - - // *) External histograms: struct ExternalHistograms { TList* fExternalHistogramsList = nullptr; - std::array, eExternalHistograms_N> fWeights{}; //! [type][before, after cuts] + std::array fWeights{}; } ex; struct Observables { TList* fObservablesList = nullptr; - std::array, 2> fProfTwo{}; //! [reco,sim][before, after event cuts] + std::array, eObservables_N> fProfCorr{}; //! [type][reco,sim] } obs; - // *) Quality assurance histograms: struct QualityAssurance { - TList* fQualityAssuranceList = nullptr; //! fHistCentralityRecSim{}; + std::array fHistMultNContrib{}; + std::array fHistCorrCent{}; + std::array fHistSelectedTrack{}; } qa; - // *) functions + struct MiscHistograms { + TList* fMiscHistList = nullptr; + TProfile* fMiscHistRunNumber = nullptr; + std::array fMiscEventCutsCounter{}; //! [reco,sim] + std::array fMiscParticleCutsCounter{}; //! [reco,sim] + } misc; + + struct GeometricCuts { // eUpperLowerBounds_N + std::array fMultNContribBounds{}; + } geoCuts; + + struct InternalValidation { + TRandom3* fRandGen = nullptr; + TF1* fIntValFun = nullptr; + TList* fIntValList = nullptr; + std::array fProfIntVal{}; + } intval; + + struct RunTimeInfo { + bool isFirstCollision = true; // this is used to ensure that weights hist are booked once and only once, otherwise, a crash + int collisionCounter = 0; + int thisRunNumber = 0; + static constexpr int RunMessagePeriod = 100; + } rti; + + // *) functions and templates std::vector> initQVectorsTable(int maxCorrelator, const std::vector& harmonic) { int sum = 0; @@ -275,18 +399,29 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } template - void updateQVectorsTable(std::vector>& QVectorsTable, T1 phi, T1 weight = T1(1)) + void updateQVectorsTable(std::vector>& QVectorsTable, T1 const& phi, T1 const& weight) { - const int maxHarmonic = QVectorsTable.size(); - const int maxPower = QVectorsTable.empty() ? 0 : QVectorsTable[0].size(); - for (int h = 0; h < maxHarmonic; ++h) { - for (int p = 0; p < maxPower; ++p) { - const auto wp = std::pow(weight, p); + for (std::size_t h = 0; h < QVectorsTable.size(); ++h) { + for (std::size_t p = 0; p < QVectorsTable[h].size(); ++p) { + const auto wp = std::pow(weight, static_cast(p)); QVectorsTable[h][p] += TComplex(wp * std::cos(h * phi), wp * std::sin(h * phi)); } } } + template + void updateVecForGap(std::vector const& harmonic, std::vector& qVecAB, std::vector& weightAB, T1 const& phi, T1 const& eta, T1 const& weight) + { + if (eta < -tc.fEtaGap / 2.f) { + qVecAB[0] += TComplex(weight * std::cos(harmonic[0] * phi), weight * std::sin(harmonic[0] * phi)); + weightAB[0] += weight; + } + if (eta > tc.fEtaGap / 2.f) { + qVecAB[1] += TComplex(weight * std::cos(harmonic[1] * phi), weight * std::sin(harmonic[1] * phi)); + weightAB[1] += weight; + } + } + std::vector two(const std::vector>& QVectorsTable, const std::vector& harmonic) { int n1 = harmonic[0], n2 = harmonic[1]; @@ -299,39 +434,37 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to auto corr = [&](int n1, int n2) -> TComplex { return q(n1, 1) * q(n2, 1) - q(n1 + n2, 2); }; - TComplex n = corr(n1, n2); TComplex d = corr(0, 0); return {n, d}; } - std::vector recursion(int m, const std::vector>& Qvector, std::vector& harmonic, int mult = 1, int skip = 0) + std::vector recursion(int m, const std::vector>& qVector, std::vector& harmonic, int mult = 1, int skip = 0) { // Calculate multi-particle correlators by using recursion (an improved faster version) originally developed by // Kristjan Gulbrandsen (gulbrand@nbi.dk). auto q = [&](int n, int p) -> TComplex { if (n >= 0) { - return Qvector[n][p]; + return qVector[n][p]; } - return TComplex::Conjugate(Qvector[-n][p]); + return TComplex::Conjugate(qVector[-n][p]); }; std::vector c = {q(harmonic[m - 1], mult), q(0, mult)}; if ((m - 1) == 0) { return c; } - std::vector temp = recursion(m - 1, Qvector, harmonic); + std::vector temp = recursion(m - 1, qVector, harmonic); c[0] *= temp[0]; c[1] *= temp[1]; if ((m - 1) == skip) { return c; } - int counter1 = 0; int hhold = harmonic[counter1]; harmonic[counter1] = harmonic[m - 2]; harmonic[m - 2] = hhold + harmonic[m - 1]; - std::vector c2 = recursion(m - 1, Qvector, harmonic, mult + 1, m - 2); + std::vector c2 = recursion(m - 1, qVector, harmonic, mult + 1, m - 2); int counter2 = m - 3; while (counter2 >= skip) { harmonic[m - 2] = harmonic[counter1]; @@ -340,14 +473,13 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to hhold = harmonic[counter1]; harmonic[counter1] = harmonic[m - 2]; harmonic[m - 2] = hhold + harmonic[m - 1]; - temp = recursion(m - 1, Qvector, harmonic, mult + 1, counter2); + temp = recursion(m - 1, qVector, harmonic, mult + 1, counter2); c2[0] += temp[0]; c2[1] += temp[1]; --counter2; } harmonic[m - 2] = harmonic[counter1]; harmonic[counter1] = hhold; - if (mult == 1) { return {c[0] - c2[0], c[1] - c2[1]}; } @@ -361,8 +493,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to TObject* getObjectFromList(TList* list, const char* objectName) { - // Get TObject pointer from TList, even if it's in some nested TList. Foreseen - // to be used to fetch histograms or profiles from files directly. + // Get TObject pointer from TList, even if it's in some nested TList. Foreseen to be used to fetch histograms or profiles from files directly. // Some ideas taken from TCollection::ls() // If you have added histograms directly to files (without TList's), then you can fetch them directly with // file->Get("hist-name"). @@ -402,10 +533,11 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to return nullptr; } // TObject* getObjectFromList(TList *list, char *objectName) - TH1F* getHistogramWithWeights(const char* filePath, const char* runNumber, const char* histName) + template + void getHistogramWithWeights(const char* filePath, const char* runNumber) { // *) Return value: - TH1F* hist = nullptr; + std::array histW{}; TList* baseList = nullptr; // base top-level list in the TFile, e.g. named "ccdb_object" TList* listWithRuns = nullptr; // nested list with run-wise TList's holding run-specific weights @@ -415,7 +547,6 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to bool bFileIsInCCDB = false; std::string path(filePath); - if (path.starts_with("/alice/cern.ch/")) { bFileIsInAliEn = true; } else if (path.starts_with("/alice-ccdb.cern.ch/")) { @@ -433,44 +564,10 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } weightsFile->GetObject("ccdb_object", baseList); - if (!baseList) { - LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); - } - - // Finally, from the top-level TList, get the desired nested TList => the technical problem here is that it can be nested at any level, for that there is a helper utility function getObjectFromList(...) , see its implementation further below - listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumber)); - if (!listWithRuns) { - TString runNumberWithLeadingZeroes = "000"; - runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number - listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); - if (!listWithRuns) { - LOGF(error, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); - return nullptr; - } - } - - // OK, we got the desired TList with efficiency corrections, after that we can use the common code for all 3 cases (local, AliEn, CCDB, that common code is below) } else if (bFileIsInCCDB) { - // File you want to access is in your home dir in CCDB: - // Remember that here I do not access the file; instead, I directly access the object in that file. + // File you want to access is in your home dir in CCDB: Remember that here I do not access the file; instead, I directly access the object in that file. ccdb->setURL("https://alice-ccdb.cern.ch"); baseList = dynamic_cast(ccdb->get(TString(filePath).ReplaceAll("/alice-ccdb.cern.ch/", "").Data())); - if (!baseList) { - LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); - } - - listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumber)); - if (!listWithRuns) { - TString runNumberWithLeadingZeroes = "000"; - runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number - listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); - if (!listWithRuns) { - LOGF(error, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); - return nullptr; - } - } - - // OK, we got the desired TList with efficiency corrections, after that we can use the common code for all 3 cases (local, AliEn, CCDB, that common code is below) } else { // this is the local case: // Check if the external ROOT file exists at the specified path: @@ -478,57 +575,57 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to LOGF(info, "\033[1;33m if(gSystem->AccessPathName(filePath,kFileExists)), filePath = %s \033[0m", filePath); LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } - TFile* weightsFile = TFile::Open(filePath, "READ"); if (!weightsFile) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m can't open file", __FUNCTION__, __LINE__); } weightsFile->GetObject("ccdb_object", baseList); - if (!baseList) { - LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); - } - - listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumber)); - if (!listWithRuns) { - TString runNumberWithLeadingZeroes = "000"; - runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number - listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); - if (!listWithRuns) { - LOGF(error, "\033[1;31m%s at line %d : this crash can happen if in the output file there is no list with weights for the current run number = %s\033[0m", __FUNCTION__, __LINE__, runNumber); - return nullptr; - } - } } // Here comes the common code for all three cases, where from "listWithRuns" you fetch the desired histogram with efficiency corrections: - if (!listWithRuns) { - LOGF(fatal, "\033[1;31m%s: listWithRuns is null for run %s\033[0m", __FUNCTION__, runNumber); - return nullptr; + if (!baseList) { + LOGF(fatal, "\033[1;31m%s at line %d for case %s\033[0m", __FUNCTION__, __LINE__, filePath); } - - hist = dynamic_cast(listWithRuns->FindObject(histName)); - if (!hist) { - LOGF(info, "%s: histogram 'hist' not found in run list", __FUNCTION__); - return nullptr; + listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumber)); + if (!listWithRuns) { + TString runNumberWithLeadingZeroes = "000"; + runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number + listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); + if (!listWithRuns) { + LOGF(warning, "\033[1;31m%s at line %d : this crash can happen if in the output file there is no list with weights for the current run number = %s\033[0m", __FUNCTION__, __LINE__, runNumber); + return; + } } - auto histClone = dynamic_cast(hist->Clone()); - if (!histClone) { - LOGF(error, "%s: histogram 'histClone' failed to be cloned", __FUNCTION__); - return nullptr; + for (int i = 0; i < eWeightsHistograms_N; ++i) { + if constexpr (rs == eRec) { + if (i == eWPhiSim) { + continue; + } + } + histW[i] = dynamic_cast(listWithRuns->FindObject(Form("[%s]", WeightsNames[i]))); + if (!histW[i]) { + LOGF(warning, "%s: histogram '%s' not found in run list with run number %s", __FUNCTION__, WeightsNames[i], runNumber); + continue; + } + histW[i]->SetDirectory(nullptr); + ex.fWeights[i] = dynamic_cast(histW[i]->Clone()); + if (!ex.fWeights[i]) { + LOGF(warning, "%s: failed to clone histogram '%s' with run number %s", __FUNCTION__, WeightsNames[i], runNumber); + continue; + } + ex.fWeights[i]->SetStats(false); + ex.fWeights[i]->SetTitle(filePath); + ex.fWeights[i]->SetDirectory(nullptr); + ex.fExternalHistogramsList->Add(ex.fWeights[i]); } - histClone->SetDirectory(nullptr); - - delete baseList; - - return histClone; - } // end of TH1F* getHistogramWithWeights(const char* filePath, const char* runNumber, const char* histName) + } // end of void getHistogramWithWeights(const char* filePath, const char* runNumber) // templates template - float chooseCent(T1 const& collision) + float chooseCent(T1 const& collision, int const& whichEstimator) { - switch (centralityEstimator) { + switch (whichEstimator) { case eFT0C: return static_cast(collision.centFT0C()); case eFT0M: @@ -544,9 +641,9 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } template - float chooseMult(T1 const& collision) + float chooseMult(T1 const& collision, int const& whichTable) { - switch (multiplicityTables) { + switch (whichTable) { case eMultTPC: return static_cast(collision.multTPC()); case eMultFV0M: @@ -563,242 +660,345 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } } + template + float chooseOccu(T1 const& collision, int const& whichEstimator) + { + switch (whichEstimator) { + case eOccuTrack: + return static_cast(collision.trackOccupancyInTimeRange()); + case eOccuFT0C: + return static_cast(collision.ft0cOccupancyInTimeRange()); + default: + LOG(warning) << "Unknown occupancy estimator. Using track as default."; + return static_cast(collision.trackOccupancyInTimeRange()); + } + } + template bool eventCuts(T1 const& collision) { - if constexpr (rs == eRec || rs == eRecAndSim) { - if (cfEventCutSwitch) // event level cuts for Rec - { - if (rm == eReal) { - if (collision.posZ() > cfVertexZCutRange.value[1] || collision.posZ() < cfVertexZCutRange.value[0]) { - return false; - } // vertex z cut + if constexpr ((rs == eSim || rs == eRecAndSim) && rm == eMC) { + if (!collision.has_mcCollision()) { + return false; + } + } + bool doNotPassCut = false; + for (int i = 0; i < EEventCuts_N; ++i) { + switch (i) { + default: + doNotPassCut = false; + break; + case eSel8Cut: + doNotPassCut = tc.fEventCutSwitch[eSel8Cut].starts_with("1") && !collision.sel8(); + break; + case eNoCollInTimeRangeStandard: + doNotPassCut = tc.fEventCutSwitch[eNoCollInTimeRangeStandard].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard); + break; + case eNoCollInRofStandard: + doNotPassCut = tc.fEventCutSwitch[eNoCollInRofStandard].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard); + break; + case eNoSameBunchPileUp: + doNotPassCut = tc.fEventCutSwitch[eNoSameBunchPileUp].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup); + break; + case eIsVertexITSTPC: + doNotPassCut = tc.fEventCutSwitch[eIsVertexITSTPC].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC); + break; + case eIsGoodITSLayersAll: + doNotPassCut = tc.fEventCutSwitch[eIsGoodITSLayersAll].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll); + break; + case eIsGoodZvtxFT0vsPV: + doNotPassCut = tc.fEventCutSwitch[eIsGoodZvtxFT0vsPV].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV); + break; + case eNoHighMultCollInPrevRof: + doNotPassCut = tc.fEventCutSwitch[eNoHighMultCollInPrevRof].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof); + break; + case eNContribCut: + doNotPassCut = tc.fEventCutSwitch[eNContribCut].starts_with("1") && (collision.numContrib() < tc.fNContribCutInf); + break; + case eOccupancyCut: { + auto thisOccu = chooseOccu(collision, tc.fOccupancyEstimator); + doNotPassCut = tc.fEventCutSwitch[eOccupancyCut].starts_with("1") && (thisOccu > tc.fOccupancyCutSup); + break; } - if constexpr (rs == eRecAndSim) // event level cuts for Sim - { - if (rm == eMC) { - if (!collision.has_mcCollision()) { - return false; - } - auto thisMCCollision = collision.mcCollision(); // corresponding MC truth simulated particle - if (thisMCCollision.posZ() > cfVertexZCutRange.value[1] || thisMCCollision.posZ() < cfVertexZCutRange.value[0]) { - return false; - } // vertex z cut + case eVertexZCut: + if constexpr ((rs == eRec || rs == eRecAndSim) && rm == eReal) { + doNotPassCut = tc.fEventCutSwitch[eVertexZCut].starts_with("1") && (collision.posZ() > tc.fVertexZCutRange[1] || collision.posZ() < tc.fVertexZCutRange[0]); + } + if constexpr ((rs == eSim || rs == eRecAndSim) && rm == eMC) { + doNotPassCut = tc.fEventCutSwitch[eVertexZCut].starts_with("1") && (collision.mcCollision().posZ() > tc.fVertexZCutRange[1] || collision.mcCollision().posZ() < tc.fVertexZCutRange[0]); + } + break; + case eCentCut: + if constexpr ((rs == eRec || rs == eRecAndSim) && rm == eReal) { + auto thisCent = chooseCent(collision, tc.fCentralityEstimator); + doNotPassCut = tc.fEventCutSwitch[eCentCut].starts_with("1") && (thisCent > tc.fCentCutRange[1] || thisCent < tc.fCentCutRange[0]); + } + if constexpr ((rs == eSim || rs == eRecAndSim) && rm == eMC) { + auto impactParameter = collision.mcCollision().impactParameter(); + auto centralityMC = math::PI * impactParameter * impactParameter / tc.fSigmaInel; + doNotPassCut = tc.fEventCutSwitch[eCentCut].starts_with("1") && (centralityMC > tc.fCentCutRange[1] || centralityMC < tc.fCentCutRange[0]); } + break; + case eCorrCentCut: { + auto cent1 = chooseCent(collision, tc.fCorrCent[0]); + auto cent2 = chooseCent(collision, tc.fCorrCent[1]); + doNotPassCut = tc.fEventCutSwitch[eCorrCentCut].starts_with("1") && (2 * std::fabsf(cent1 - cent2) / (cent1 + cent2) > tc.fCorrCentCutRatio); + break; } + case eMultNContribCut: { + auto thisRefMult = chooseMult(collision, tc.fMultiplicityTables); + auto thisNContrib = collision.numContrib(); + auto upper = geoCuts.fMultNContribBounds[eUpper]->Eval(thisRefMult); + auto lower = geoCuts.fMultNContribBounds[eLower]->Eval(thisRefMult); + doNotPassCut = tc.fEventCutSwitch[eMultNContribCut].starts_with("1") && (thisNContrib < lower || thisNContrib > upper); + break; + } + } // end of switch (i) { + + if (doNotPassCut) { + if constexpr (rm == eReal) { + misc.fMiscEventCutsCounter[eRec]->Fill(i + 0.5, 1); + } + if constexpr (rm == eMC) { + misc.fMiscEventCutsCounter[eSim]->Fill(i + 0.5, 1); + } + return false; } - } + } // end of for (int i = 0; i < EEventCuts_N; ++i) { return true; - } + } // end of bool eventCuts(T1 const& collision) - template + template void eventHistFill(T1 const& collision, T2 const& tracks) { - if constexpr (rs == eRec || rs == eRecAndSim) { - // Fill reconstructed-level event histograms - if (rm == eReal) { - auto thisCent = chooseCent(collision); - auto thisRefMult = chooseMult(collision); - int multiplicityRec = static_cast(tracks.size()); - if constexpr (cuts == eBefore) { - ec.fEventHist[eHistMultiplicity][eRec][eBefore]->Fill(multiplicityRec); - ec.fEventHist[eHistCentrality][eRec][eBefore]->Fill(thisCent); - ec.fEventHist[eHistReferenceMultiplicity][eRec][eBefore]->Fill(thisRefMult); - ec.fEventHist[eHistVertexX][eRec][eBefore]->Fill(collision.posX()); - ec.fEventHist[eHistVertexY][eRec][eBefore]->Fill(collision.posY()); - ec.fEventHist[eHistVertexZ][eRec][eBefore]->Fill(collision.posZ()); - } - if constexpr (cuts == eAfter) { - ec.fEventHist[eHistMultiplicity][eRec][eAfter]->Fill(multiplicityRec); - ec.fEventHist[eHistCentrality][eRec][eAfter]->Fill(thisCent); - ec.fEventHist[eHistReferenceMultiplicity][eRec][eAfter]->Fill(thisRefMult); - ec.fEventHist[eHistVertexX][eRec][eAfter]->Fill(collision.posX()); - ec.fEventHist[eHistVertexY][eRec][eAfter]->Fill(collision.posY()); - ec.fEventHist[eHistVertexZ][eRec][eAfter]->Fill(collision.posZ()); - } + if constexpr ((rs == eRec || rs == eRecAndSim) && rm == eReal) { // Fill reconstructed-level event histograms + auto thisCent = chooseCent(collision, tc.fCentralityEstimator); + auto thisRefMult = chooseMult(collision, tc.fMultiplicityTables); + auto thisOccu = chooseOccu(collision, tc.fOccupancyEstimator); + int multiplicityRec = static_cast(tracks.size()); + if constexpr (cuts == eBefore) { + ec.fEventHist[eHistMult][eRec][eBefore]->Fill(multiplicityRec); + ec.fEventHist[eHistCentrality][eRec][eBefore]->Fill(thisCent); + ec.fEventHist[eHistRefMult][eRec][eBefore]->Fill(thisRefMult); + ec.fEventHist[eHistVertexX][eRec][eBefore]->Fill(collision.posX()); + ec.fEventHist[eHistVertexY][eRec][eBefore]->Fill(collision.posY()); + ec.fEventHist[eHistVertexZ][eRec][eBefore]->Fill(collision.posZ()); + ec.fEventHist[eHistNumContrib][eRec][eBefore]->Fill(collision.numContrib()); + ec.fEventHist[eHistOccupancy][eRec][eBefore]->Fill(thisOccu); } - - // Fill MC simulated-level event histograms if both reconstructed and simulated data are processed - if constexpr (rs == eRecAndSim) { - if (!collision.has_mcCollision()) { - // LOGF(warning, "No MC collision for this collision, skip..."); - return; - } - if (rm == eMC) { - auto thisMCCollision = collision.mcCollision(); // corresponding MC truth simulated particle - int multiplicitySim = static_cast(tracks.size()); - auto impactParameter = thisMCCollision.impactParameter(); - auto centralityMC = math::PI * impactParameter * impactParameter / sigmaInel; // centrality for sim derived from impact parameter - if constexpr (cuts == eBefore) { - ec.fEventHist[eHistMultiplicity][eSim][eBefore]->Fill(multiplicitySim); - ec.fEventHist[eHistCentrality][eSim][eBefore]->Fill(centralityMC); - ec.fEventHist[eHistImpactParameter][eSim][eBefore]->Fill(impactParameter); - ec.fEventHist[eHistVertexX][eSim][eBefore]->Fill(thisMCCollision.posX()); - ec.fEventHist[eHistVertexY][eSim][eBefore]->Fill(thisMCCollision.posY()); - ec.fEventHist[eHistVertexZ][eSim][eBefore]->Fill(thisMCCollision.posZ()); - } - - if constexpr (cuts == eAfter) { - ec.fEventHist[eHistMultiplicity][eSim][eAfter]->Fill(multiplicitySim); - ec.fEventHist[eHistCentrality][eSim][eAfter]->Fill(centralityMC); - ec.fEventHist[eHistImpactParameter][eSim][eAfter]->Fill(impactParameter); - ec.fEventHist[eHistVertexX][eSim][eAfter]->Fill(thisMCCollision.posX()); - ec.fEventHist[eHistVertexY][eSim][eAfter]->Fill(thisMCCollision.posY()); - ec.fEventHist[eHistVertexZ][eSim][eAfter]->Fill(thisMCCollision.posZ()); - } - } // end of if (rm == eMC) { + if constexpr (cuts == eAfter) { + ec.fEventHist[eHistMult][eRec][eAfter]->Fill(multiplicityRec); + ec.fEventHist[eHistCentrality][eRec][eAfter]->Fill(thisCent); + ec.fEventHist[eHistRefMult][eRec][eAfter]->Fill(thisRefMult); + ec.fEventHist[eHistVertexX][eRec][eAfter]->Fill(collision.posX()); + ec.fEventHist[eHistVertexY][eRec][eAfter]->Fill(collision.posY()); + ec.fEventHist[eHistVertexZ][eRec][eAfter]->Fill(collision.posZ()); + ec.fEventHist[eHistNumContrib][eRec][eAfter]->Fill(collision.numContrib()); + ec.fEventHist[eHistOccupancy][eRec][eAfter]->Fill(thisOccu); } } - } + + // Fill MC simulated-level event histograms if both reconstructed and simulated data are processed + if constexpr ((rs == eRecAndSim || rs == eSim) && rm == eMC) { + if (!collision.has_mcCollision()) { + return; + } + auto thisMCCollision = collision.mcCollision(); + int multiplicitySim = static_cast(tracks.size()); + auto impactParameter = thisMCCollision.impactParameter(); + auto centralityMC = math::PI * impactParameter * impactParameter / tc.fSigmaInel; // centrality for sim derived from impact parameter + if constexpr (cuts == eBefore) { + ec.fEventHist[eHistMult][eSim][eBefore]->Fill(multiplicitySim); + ec.fEventHist[eHistCentrality][eSim][eBefore]->Fill(centralityMC); + ec.fEventHist[eHistImpactParameter][eSim][eBefore]->Fill(impactParameter); + ec.fEventHist[eHistVertexX][eSim][eBefore]->Fill(thisMCCollision.posX()); + ec.fEventHist[eHistVertexY][eSim][eBefore]->Fill(thisMCCollision.posY()); + ec.fEventHist[eHistVertexZ][eSim][eBefore]->Fill(thisMCCollision.posZ()); + } + if constexpr (cuts == eAfter) { + ec.fEventHist[eHistMult][eSim][eAfter]->Fill(multiplicitySim); + ec.fEventHist[eHistCentrality][eSim][eAfter]->Fill(centralityMC); + ec.fEventHist[eHistImpactParameter][eSim][eAfter]->Fill(impactParameter); + ec.fEventHist[eHistVertexX][eSim][eAfter]->Fill(thisMCCollision.posX()); + ec.fEventHist[eHistVertexY][eSim][eAfter]->Fill(thisMCCollision.posY()); + ec.fEventHist[eHistVertexZ][eSim][eAfter]->Fill(thisMCCollision.posZ()); + } + } // end of if constexpr (rs == eRecAndSim && rm == eMC) { + } // end of void eventHistFill(T1 const& collision, T2 const& tracks) template bool particleCuts(T const& track) { - if constexpr (rs == eRec || rs == eRecAndSim) { - if (cfPtCutSwitch) // pt cuts for Rec - { - if (rm == eReal) { - if (track.pt() < cfPtCutRange.value[0] || track.pt() > cfPtCutRange.value[1]) { - return false; - } + bool doNotPassCut = false; + for (int i = 0; i < EParticleCuts_N; ++i) { + if constexpr ((rs == eRec || rs == eRecAndSim) && rm == eReal) { + switch (i) { + default: + doNotPassCut = false; + break; + case ePtCut: + doNotPassCut = tc.fParticleCutSwitch[ePtCut].starts_with("1") && (track.pt() < tc.fPtCutRange[0] || track.pt() > tc.fPtCutRange[1]); + break; + case eEtaCut: + doNotPassCut = tc.fParticleCutSwitch[eEtaCut].starts_with("1") && (track.eta() < tc.fEtaCutRange[0] || track.eta() > tc.fEtaCutRange[1]); + break; + case eChargeCut: + doNotPassCut = tc.fParticleCutSwitch[eChargeCut].starts_with("1") && (track.sign() != 1 && track.sign() != -1); + break; + case eTPCNumClusterCut: + doNotPassCut = tc.fParticleCutSwitch[eTPCNumClusterCut].starts_with("1") && (track.tpcNClsFound() < tc.fTPCNumClusterCutRange[0] || track.tpcNClsFound() > tc.fTPCNumClusterCutRange[1]); + break; + case eDCAxyCut: + doNotPassCut = tc.fParticleCutSwitch[eDCAxyCut].starts_with("1") && (track.dcaXY() < tc.fDCAxyCutRange[0] || track.dcaXY() > tc.fDCAxyCutRange[1]); + break; + case eDCAzCut: + doNotPassCut = tc.fParticleCutSwitch[eDCAzCut].starts_with("1") && (track.dcaZ() < tc.fDCAzCutRange[0] || track.dcaZ() > tc.fDCAzCutRange[1]); + break; } - if constexpr (rs == eRecAndSim) // pt cuts for Sim - { - if (rm == eMC) { - if (!track.has_mcParticle()) { - return false; - } - auto mcParticle = track.mcParticle(); - if (mcParticle.pt() < cfPtCutRange.value[0] || mcParticle.pt() > cfPtCutRange.value[1]) { - return false; - } - } // end of if (rm == eMC) { + } // end of if constexpr ((rs == eRec || rs == eRecAndSim) && rm == eReal) { + + if constexpr ((rs == eSim || rs == eRecAndSim) && rm == eMC) { + if (!track.has_mcParticle()) { + return false; } - } - if (cfEtaCutSwitch) // eta cuts for Rec - { - if (rm == eReal) { - if (track.eta() < cfEtaCutRange.value[0] || track.eta() > cfEtaCutRange.value[1]) { - return false; - } + auto thisMCParticle = track.mcParticle(); + switch (i) { + default: + doNotPassCut = false; + break; + case ePtCut: + doNotPassCut = tc.fParticleCutSwitch[ePtCut].starts_with("1") && (thisMCParticle.pt() < tc.fPtCutRange[0] || thisMCParticle.pt() > tc.fPtCutRange[1]); + break; + case eEtaCut: + doNotPassCut = tc.fParticleCutSwitch[eEtaCut].starts_with("1") && (thisMCParticle.eta() < tc.fEtaCutRange[0] || thisMCParticle.eta() > tc.fEtaCutRange[1]); + break; + case eChargeCut: + if (tc.fParticleCutSwitch[eChargeCut].starts_with("1")) { + TParticlePDG* particlePDG = pdg->GetParticle(thisMCParticle.pdgCode()); + if (particlePDG) { + const int chargeMCUnit = 3; + auto chargeMC = particlePDG->Charge() / chargeMCUnit; + if (chargeMC != 1 && chargeMC != -1) { + doNotPassCut = true; + } + } else { + doNotPassCut = true; + } + } // end of if (tc.fParticleCutSwitch[eChargeCut]) { + break; + case eTPCNumClusterCut: + doNotPassCut = tc.fParticleCutSwitch[eTPCNumClusterCut].starts_with("1") && (track.tpcNClsFound() < tc.fTPCNumClusterCutRange[0] || track.tpcNClsFound() > tc.fTPCNumClusterCutRange[1]); + break; + case eDCAxyCut: + doNotPassCut = tc.fParticleCutSwitch[eDCAxyCut].starts_with("1") && (track.dcaXY() < tc.fDCAxyCutRange[0] || track.dcaXY() > tc.fDCAxyCutRange[1]); + break; + case eDCAzCut: + doNotPassCut = tc.fParticleCutSwitch[eDCAzCut].starts_with("1") && (track.dcaZ() < tc.fDCAzCutRange[0] || track.dcaZ() > tc.fDCAzCutRange[1]); + break; } - if constexpr (rs == eRecAndSim) // eta cuts for Sim - { - if (rm == eMC) { - if (!track.has_mcParticle()) { - return false; - } - auto mcParticle = track.mcParticle(); - if (mcParticle.eta() < cfEtaCutRange.value[0] || mcParticle.eta() > cfEtaCutRange.value[1]) { - return false; - } - } // end of if (rm == eMC) { + } // end of if constexpr ((rs == eSim || rs == eRecAndSim) && rm == eMC) { + + if (doNotPassCut) { + if constexpr (rm == eReal) { + misc.fMiscParticleCutsCounter[eRec]->Fill(i + 0.5, 1); } - } - if (cfChargeCutSwitch) // charge cuts for Rec - { - if (rm == eReal) { - if (track.sign() == 0) { - return false; - } + if constexpr (rm == eMC) { + misc.fMiscParticleCutsCounter[eSim]->Fill(i + 0.5, 1); } - if constexpr (rs == eRecAndSim) // charge cuts for Sim - { - if (rm == eMC) { - if (!track.has_mcParticle()) { - return false; - } - auto mcParticle = track.mcParticle(); - auto chargeMC = pdg->GetParticle(mcParticle.pdgCode())->Charge(); - if (chargeMC == 0) { - return false; - } - } // end of if (rm == eMC) { - } - } // end of if (cfChargeCutSwitch) // charge cuts for Rec - } // end of if constexpr (rs == eRec || rs == eRecAndSim) { + return false; + } + } // end of for (int i = 0; i < EParticleCuts_N; ++i) { return true; - } + } // end of bool particleCuts(T const& track) - template + template void particleHistFill(T1 const& track) { - if constexpr (rs == eRec || rs == eRecAndSim) { - if (rm == eReal) { - if constexpr (cuts == eBefore) { - pc.fParticleHist[eHistPt][eRec][eBefore]->Fill(track.pt()); - pc.fParticleHist[eHistPhi][eRec][eBefore]->Fill(track.phi()); - pc.fParticleHist[eHistEta][eRec][eBefore]->Fill(track.eta()); - pc.fParticleHist[eHistCharge][eRec][eBefore]->Fill(track.sign()); - } + if constexpr ((rs == eRec || rs == eRecAndSim) && rm == eReal) { + if constexpr (cuts == eBefore) { + pc.fParticleHist[eHistPt][eRec][eBefore]->Fill(track.pt()); + pc.fParticleHist[eHistPhi][eRec][eBefore]->Fill(track.phi()); + pc.fParticleHist[eHistEta][eRec][eBefore]->Fill(track.eta()); + pc.fParticleHist[eHistCharge][eRec][eBefore]->Fill(track.sign()); + } + if constexpr (cuts == eAfter) { + pc.fParticleHist[eHistPt][eRec][eAfter]->Fill(track.pt()); + pc.fParticleHist[eHistPhi][eRec][eAfter]->Fill(track.phi()); + pc.fParticleHist[eHistEta][eRec][eAfter]->Fill(track.eta()); + pc.fParticleHist[eHistCharge][eRec][eAfter]->Fill(track.sign()); + } + } - if constexpr (cuts == eAfter) { - pc.fParticleHist[eHistPt][eRec][eAfter]->Fill(track.pt()); - pc.fParticleHist[eHistPhi][eRec][eAfter]->Fill(track.phi()); - pc.fParticleHist[eHistEta][eRec][eAfter]->Fill(track.eta()); - pc.fParticleHist[eHistCharge][eRec][eAfter]->Fill(track.sign()); + if constexpr ((rs == eSim || rs == eRecAndSim) && rm == eMC) { + if (!track.has_mcParticle()) { + return; + } + auto thisMCParticle = track.mcParticle(); + TParticlePDG* particlePDG = pdg->GetParticle(thisMCParticle.pdgCode()); + float chargeMC = 0.f; + const int chargeMCUnit = 3; + if (particlePDG) { + chargeMC = particlePDG->Charge(); + chargeMC /= chargeMCUnit; + } + if constexpr (cuts == eBefore) { + pc.fParticleHist[eHistPt][eSim][eBefore]->Fill(thisMCParticle.pt()); + pc.fParticleHist[eHistPhi][eSim][eBefore]->Fill(thisMCParticle.phi()); + pc.fParticleHist[eHistEta][eSim][eBefore]->Fill(thisMCParticle.eta()); + if (particlePDG) { + pc.fParticleHist[eHistCharge][eSim][eBefore]->Fill(chargeMC); } } - - if constexpr (rs == eRecAndSim) { - if (rm == eMC) { - if (!track.has_mcParticle()) { - // LOGF(warning, " No MC particle for this track, skip..."); - return; - } - auto mcParticle = track.mcParticle(); - auto chargeMC = pdg->GetParticle(mcParticle.pdgCode())->Charge(); - const int chargeMCUnit = 3; - chargeMC /= chargeMCUnit; - if constexpr (cuts == eBefore) { - pc.fParticleHist[eHistPt][eSim][eBefore]->Fill(mcParticle.pt()); - pc.fParticleHist[eHistPhi][eSim][eBefore]->Fill(mcParticle.phi()); - pc.fParticleHist[eHistEta][eSim][eBefore]->Fill(mcParticle.eta()); - pc.fParticleHist[eHistCharge][eSim][eBefore]->Fill(chargeMC); - } - - if constexpr (cuts == eAfter) { - pc.fParticleHist[eHistPt][eSim][eAfter]->Fill(mcParticle.pt()); - pc.fParticleHist[eHistPhi][eSim][eAfter]->Fill(mcParticle.phi()); - pc.fParticleHist[eHistEta][eSim][eAfter]->Fill(mcParticle.eta()); - pc.fParticleHist[eHistCharge][eSim][eAfter]->Fill(chargeMC); - } - } // end of if (rm == eMC) { + if constexpr (cuts == eAfter) { + pc.fParticleHist[eHistPt][eSim][eAfter]->Fill(thisMCParticle.pt()); + pc.fParticleHist[eHistPhi][eSim][eAfter]->Fill(thisMCParticle.phi()); + pc.fParticleHist[eHistEta][eSim][eAfter]->Fill(thisMCParticle.eta()); + if (particlePDG) { + pc.fParticleHist[eHistCharge][eSim][eAfter]->Fill(chargeMC); + } } - } - } + } // end of if constexpr (rs == eRecAndSim && rm == eMC) { + } // end of void particleHistFill(T1 const& track) - void loadWeights(int runNumber) + template + void qaFill(T1 const& collision, T2 const& tracks) { - for (int i = 0; i < eExternalHistograms_N; ++i) { - for (int j = 0; j < eCuts_N; ++j) { - ex.fWeights[i][j] = - getHistogramWithWeights(cfFileWithWeights.value.c_str(), Form("%d", runNumber), Form("[%s][%s]", WeightsNames[i], CutsNames[j])); - ex.fExternalHistogramsList->Add(ex.fWeights[i][j]); + auto thisCent = chooseCent(collision, tc.fCentralityEstimator); + auto thisRefMult = chooseMult(collision, tc.fMultiplicityTables); + if constexpr (rm == eReal) { + auto cent1 = chooseCent(collision, tc.fCorrCent[0]); + auto cent2 = chooseCent(collision, tc.fCorrCent[1]); + if constexpr (cuts == eBefore) { + qa.fHistMultNContrib[eBefore]->Fill(thisRefMult, collision.numContrib()); + qa.fHistCorrCent[eBefore]->Fill(cent1, cent2); + if (thisCent > tc.fQASelTrackCentRange[0] && thisCent < tc.fQASelTrackCentRange[1]) { + qa.fHistSelectedTrack[eBefore]->Fill(tracks.size()); + } + } + if constexpr (cuts == eAfter) { + qa.fHistMultNContrib[eAfter]->Fill(thisRefMult, collision.numContrib()); + qa.fHistCorrCent[eAfter]->Fill(cent1, cent2); } } - } - template - void qaFill(T1 const& collision) - { - auto thisCent = chooseCent(collision); - if constexpr (rs == eRecAndSim || rs == eSim) { + if constexpr (rs == eRecAndSim && rm == eMC) { if (!collision.has_mcCollision()) { return; } auto thisMCCollision = collision.mcCollision(); auto impactParameter = thisMCCollision.impactParameter(); - auto centralityMC = math::PI * impactParameter * impactParameter / sigmaInel; // centrality for sim derived from impact parameter - qa.fHistCentralityRecSim->Fill(thisCent, centralityMC); + auto centralityMC = math::PI * impactParameter * impactParameter / tc.fSigmaInel; + if constexpr (cuts == eBefore) { + qa.fHistCentralityRecSim[eBefore]->Fill(thisCent, centralityMC); + } + if constexpr (cuts == eAfter) { + qa.fHistCentralityRecSim[eAfter]->Fill(thisCent, centralityMC); + } } } - // *) Misc: - bool isFirstCollision = true; // this is used to ensure that weights hist are booked once and only once, otherwise, a crash - // *) Define all member functions to be called in the main process* functions: template void steer(T1 const& collision, T2 const& tracks) @@ -807,107 +1007,227 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if (tc.fDryRun) { return; } - int thisRunNumber = collision.bc().runNumber(); - - if (isFirstCollision) { - // Get run number - misc.fMiscHistRunNumber->SetTitle(Form("%d", thisRunNumber)); - misc.fMiscHistRunNumber->SetBinContent(1, thisRunNumber); - // Get weights - if (cfExternalFileSwitch) { - loadWeights(thisRunNumber); + auto thisCollCentReal = chooseCent(collision, tc.fCentralityEstimator); + + if (rti.isFirstCollision) { + rti.thisRunNumber = collision.bc().runNumber(); + misc.fMiscHistRunNumber->SetTitle(Form("%d", rti.thisRunNumber)); // Get run number + misc.fMiscHistRunNumber->Fill(0.5, rti.thisRunNumber); + if (tc.fExternalFileSwitch) { + getHistogramWithWeights(tc.fFileWithWeights.c_str(), Form("%d", rti.thisRunNumber)); } } - // Fill Quality Assurance - if (qualityAssuranceSwitch) { - qaFill(collision); + if (tc.fRunMessageSwitch) { + if (rti.collisionCounter % rti.RunMessagePeriod == 0) { + LOGF(info, "Successfully running, run number is %d", rti.thisRunNumber); + rti.collisionCounter = 0; + } + ++rti.collisionCounter; } - const bool passesEventCutsReal = eventCuts(collision); - const bool passesEventCutsMC = eventCuts(collision); + // Fill Quality Assurance before cuts + if (tc.fQualityAssuranceSwitch) { + qaFill(collision, tracks); + qaFill(collision, tracks); + } - // Fill Event Hist + // Fill Event Hist before cuts eventHistFill(collision, tracks); eventHistFill(collision, tracks); - if (cfMasterCutSwitch) { - if (passesEventCutsReal) { - eventHistFill(collision, tracks); - } - if (passesEventCutsMC) { - eventHistFill(collision, tracks); + bool passEventCutsReal = eventCuts(collision); + // bool passEventCutsMC = eventCuts(collision); // this is currently useless + + // Fill event hist and qa hist after cuts + if (tc.fMasterCutSwitch && passEventCutsReal) { + eventHistFill(collision, tracks); + eventHistFill(collision, tracks); + if (tc.fQualityAssuranceSwitch) { + qaFill(collision, tracks); + qaFill(collision, tracks); } } - std::vector n2 = {-2, 2}; - auto qVectorsTableBeforeCutsReal = initQVectorsTable(2, n2); - auto qVectorsTableAfterCutsReal = initQVectorsTable(2, n2); + std::vector harmonic22 = {-2, 2}; + std::vector harmonic24 = {-2, -2, 2, 2}; + + auto qVecTableReal = initQVectorsTable(4, harmonic24); + auto qVecTableMC = initQVectorsTable(4, harmonic24); + auto qVecTableIV = initQVectorsTable(4, harmonic24); + + std::vector qVecABReal(2, TComplex(0., 0.)); + std::vector qVecABMC(2, TComplex(0., 0.)); + std::vector weightABReal(2, 0.f); + std::vector weightABMC(2, 0.f); + + if (tc.fInternalValidationSwitch) { + double psi = intval.fRandGen->Uniform(0, math::TwoPI); + intval.fIntValFun->SetParameter(0, psi); + } // Main loop over particles: - auto track = tracks.iteratorAt(0); // set the type and scope from one instance - for (int64_t i = 0; i < tracks.size(); i++) { - track = tracks.iteratorAt(i); - const float thisPhi = track.phi(); - const float thisPt = track.pt(); - float wPhiBefore = 1.f; - float wPhiAfter = 1.f; - float wPtBefore = 1.f; - float wPtAfter = 1.f; - if (cfExternalFileSwitch && ex.fWeights[ePhi][eBefore]) { - wPhiBefore = ex.fWeights[ePhi][eBefore]->GetBinContent(ex.fWeights[ePhi][eBefore]->FindBin(thisPhi)); - wPhiAfter = ex.fWeights[ePhi][eAfter]->GetBinContent(ex.fWeights[ePhi][eAfter]->FindBin(thisPhi)); - wPtBefore = ex.fWeights[ePt][eBefore]->GetBinContent(ex.fWeights[ePt][eBefore]->FindBin(thisPt)); - wPtAfter = ex.fWeights[ePt][eAfter]->GetBinContent(ex.fWeights[ePt][eAfter]->FindBin(thisPt)); + int nPassedTrack = 0; + for (auto const& track : tracks) { + float thisPhiRec = track.phi(); + float thisPt = track.pt(); + + if (tc.fInternalValidationSwitch && passEventCutsReal) { + const float thisPhiIV = intval.fIntValFun->GetRandom(); + updateQVectorsTable(qVecTableIV, thisPhiIV, 1.f); } - particleHistFill(track); - particleHistFill(track); - updateQVectorsTable(qVectorsTableBeforeCutsReal, thisPhi, wPhiBefore * wPtBefore); + std::array thisPhiAndPt = {thisPhiRec, 0.f, thisPt}; // {thisPhiRec, thisPhiSim, thisPt} + std::array thisWeights = {1.f, 1.f, 1.f}; // {wPhiRec, wPhiSim, wPt} - if (cfMasterCutSwitch) { - if (passesEventCutsReal && particleCuts(track)) { - particleHistFill(track); - updateQVectorsTable(qVectorsTableAfterCutsReal, thisPhi, wPhiAfter * wPtAfter); + particleHistFill(track); + if (tc.fMasterCutSwitch && passEventCutsReal && particleCuts(track)) { + particleHistFill(track); + ++nPassedTrack; + if (tc.fExternalFileSwitch) { + for (int k = 0; k < eWeightsHistograms_N; ++k) { + if (k != eWPhiSim && ex.fWeights[k]) { + thisWeights[k] = ex.fWeights[k]->GetBinContent(ex.fWeights[k]->FindBin(thisPhiAndPt[k])); + } + } } - if (passesEventCutsMC && particleCuts(track)) { + + float thisWeightReal = thisWeights[eWPhiRec] * thisWeights[eWPt]; + updateQVectorsTable(qVecTableReal, thisPhiRec, thisWeightReal); + auto thisEtaRec = track.eta(); + updateVecForGap(harmonic22, qVecABReal, weightABReal, thisPhiRec, thisEtaRec, thisWeightReal); + } + + if constexpr (rs == eRecAndSim) { + particleHistFill(track); + if (tc.fMasterCutSwitch && passEventCutsReal && particleCuts(track)) { particleHistFill(track); + + auto thisPhiSim = track.mcParticle().phi(); + auto thisEtaSim = track.mcParticle().eta(); + thisPhiAndPt[eWPhiSim] = thisPhiSim; + if (tc.fExternalFileSwitch && ex.fWeights[eWPhiSim]) { + thisWeights[eWPhiSim] = ex.fWeights[eWPhiSim]->GetBinContent(ex.fWeights[eWPhiSim]->FindBin(thisPhiSim)); + } + + float thisWeightSim = thisWeights[eWPhiSim] * thisWeights[eWPt]; + updateQVectorsTable(qVecTableMC, thisPhiSim, thisWeightSim); + updateVecForGap(harmonic22, qVecABMC, weightABMC, thisPhiSim, thisEtaSim, thisWeightSim); } + } // end of if constexpr (rs == eRecAndSim) { + } // end of for (auto const& track : tracks) { + if (tc.fMasterCutSwitch && passEventCutsReal && tc.fQualityAssuranceSwitch) { + qa.fHistSelectedTrack[eAfter]->Fill(nPassedTrack); + } + + if (tc.fMasterCutSwitch && passEventCutsReal) { + std::vector result22(2, TComplex(0., 0.)); + std::vector result24(2, TComplex(0., 0.)); + std::vector result22Gap(2, 0.f); + result22 = two(qVecTableReal, harmonic22); + if (noneZeroDenom(result22)) { + obs.fProfCorr[eV22][eRec]->Fill(thisCollCentReal, (result22[0] / result22[1].Re()).Re()); + } + result24 = recursion(4, qVecTableReal, harmonic24); + if (noneZeroDenom(result24)) { + obs.fProfCorr[eV24][eRec]->Fill(thisCollCentReal, (result24[0] / result24[1].Re()).Re()); } - } // end of for (int64_t i = 0; i < tracks.size(); i++) { - std::vector resultMultCorr(2, TComplex(0., 0.)); - resultMultCorr = two(qVectorsTableBeforeCutsReal, n2); - if (noneZeroDenom(resultMultCorr)) { - obs.fProfTwo[eRec][eBefore]->Fill(0.5, (resultMultCorr[0] / resultMultCorr[1].Re()).Re() / (1e-2)); - } - if (cfMasterCutSwitch && passesEventCutsReal) { - resultMultCorr = two(qVectorsTableAfterCutsReal, n2); - if (noneZeroDenom(resultMultCorr)) { - obs.fProfTwo[eRec][eAfter]->Fill(0.5, (resultMultCorr[0] / resultMultCorr[1].Re()).Re() / (1e-2)); + result22Gap = {static_cast((qVecABReal[0] * qVecABReal[1]).Re()), weightABReal[0] * weightABReal[1]}; + if (result22Gap[1] != 0) { + obs.fProfCorr[eV22Gap][eRec]->Fill(thisCollCentReal, result22Gap[0] / result22Gap[1]); } - } - // Now the first collision ends - isFirstCollision = false; + if constexpr (rs == eSim || rs == eRecAndSim) { + auto impactParam = collision.mcCollision().impactParameter(); + auto thisCollCentMC = math::PI * impactParam * impactParam / tc.fSigmaInel; + + result22 = two(qVecTableMC, harmonic22); + if (noneZeroDenom(result22)) { + obs.fProfCorr[eV22][eSim]->Fill(thisCollCentMC, (result22[0] / result22[1].Re()).Re()); + } + result24 = recursion(4, qVecTableMC, harmonic24); + if (noneZeroDenom(result24)) { + obs.fProfCorr[eV24][eSim]->Fill(thisCollCentMC, (result24[0] / result24[1].Re()).Re()); + } + + result22Gap = {static_cast((qVecABMC[0] * qVecABMC[1]).Re()), weightABMC[0] * weightABMC[1]}; + if (result22Gap[1] != 0) { + obs.fProfCorr[eV22Gap][eSim]->Fill(thisCollCentMC, result22Gap[0] / result22Gap[1]); + } + } // end of if constexpr (rs == eRecAndSim) { + + if (tc.fInternalValidationSwitch) { + std::vector> harmonicIV = {{-2, 2}, {-3, 3}, {-4, 4}}; + for (int iv = 0; iv < eIntValHarmonics_N; ++iv) { + std::vector result = two(qVecTableIV, harmonicIV[iv]); + if (noneZeroDenom(result)) { + intval.fProfIntVal[iv]->Fill(thisCollCentReal, (result[0] / result[1].Re()).Re()); + } + } + } // end of if (tc.fInternalValidationSwitch) { + } // end of if (tc.fMasterCutSwitch) { + + rti.isFirstCollision = false; // Now the first collision ends } // end of template void steer(T1 const& collision, T2 const& tracks) { // *) Initialize and book all objects: void init(InitContext&) { - // *) Set colors - const int afterCutFillColor = kGreen - 10; - const int afterCutLineColor = kGreen; - const int beforeCutFillColor = kRed - 10; - const int beforeCutLineColor = kRed; - // *) Set automatically what to process, from an implicit variable "doprocessSomEProcessName" within a PROCESS_SWITCH clause: tc.fProcess[eProcessRec] = doprocessRec; tc.fProcess[eProcessRecSim] = doprocessRecSim; tc.fProcess[eProcessSim] = doprocessSim; + // *) Set runtime info + rti.isFirstCollision = true; + rti.collisionCounter = 0; + rti.thisRunNumber = 0; + + // *) Set colors + std::vector fillColor(eBeforeAfterCuts_N); + fillColor[eBefore] = kRed - 10; + fillColor[eAfter] = kGreen - 10; + + std::vector lineColor(eBeforeAfterCuts_N); + lineColor[eBefore] = kRed; + lineColor[eAfter] = kGreen; + // *) Configure your task using configurables in the json file: tc.fDryRun = cfDryRun; + tc.fCentralityEstimator = cfCentralityEstimator; + tc.fMultiplicityTables = cfMultiplicityTables; + tc.fOccupancyEstimator = cfOccupancyEstimator; + + tc.fExternalFileSwitch = cfExternalFileSwitch; + tc.fFileWithWeights = cfFileWithWeights; + + tc.fMasterCutSwitch = cfMasterCutSwitch; + + tc.fEventCutSwitch = cfEventCutSwitch.value; + tc.fVertexZCutRange = cfVertexZCutRange.value; + tc.fCentCutRange = cfCentCutRange.value; + tc.fNContribCutInf = cfNContribCutInf; + tc.fOccupancyCutSup = cfOccupancyCutSup; + tc.fCorrCent = cfCorrCent.value; + tc.fCorrCentCutRatio = cfCorrCentCutRatio; + tc.fMultNContribBound = cfMultNContribBound.value; + + tc.fParticleCutSwitch = cfParticleCutSwitch.value; + tc.fPtCutRange = cfPtCutRange.value; + tc.fEtaCutRange = cfEtaCutRange.value; + tc.fTPCNumClusterCutRange = cfTPCNumClusterCutRange.value; + tc.fDCAxyCutRange = cfDCAxyCutRange.value; + tc.fDCAzCutRange = cfDCAzCutRange.value; + + tc.fEtaGap = cfEtaGap; + tc.fSigmaInel = cfSigmaInel; + tc.fRNGSeed = cfRNGSeed; + tc.fQualityAssuranceSwitch = cfQualityAssuranceSwitch; + tc.fRunMessageSwitch = cfRunMessageSwitch; + tc.fInternalValidationSwitch = cfInternalValidationSwitch; + tc.fIVHarmonics = cfIVHarmonics.value; + tc.fQASelTrackCentRange = cfQASelTrackCentRange.value; // *) Book base list: auto* temp = new TList(); @@ -915,8 +1235,8 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to fBaseList.setObject(temp); // *) Book and nest all other TLists: - if (cfExternalFileSwitch) { - // *) Book External Hist List + // *) Book External Hist List + if (tc.fExternalFileSwitch) { ex.fExternalHistogramsList = new TList(); ex.fExternalHistogramsList->SetName("ExternalHistograms"); ex.fExternalHistogramsList->SetOwner(true); @@ -929,280 +1249,147 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to misc.fMiscHistList->SetOwner(true); fBaseList->Add(misc.fMiscHistList); - misc.fMiscHistRunNumber = new TH1D("eRunNumber", "to be set", 1, 0, 1); + misc.fMiscHistRunNumber = new TProfile("[eRunNumber]", "to be set", 1, 0, 1); + misc.fMiscHistRunNumber->SetStats(false); + misc.fMiscHistRunNumber->GetXaxis()->SetBinLabel(1, "RunNumber"); misc.fMiscHistList->Add(misc.fMiscHistRunNumber); - // *) Book particle TLists: - pc.fParticleHistList = new TList(); - pc.fParticleHistList->SetName("ParticleHistograms"); - pc.fParticleHistList->SetOwner(true); - fBaseList->Add(pc.fParticleHistList); // any nested TList in the base TList appears as a subdir in the output ROOT file - - // *) Book pt and phi distribution with binning defined through configurables in the json file: - std::vector lPtBins = cfPtBins.value; - const int nBinsPt = static_cast(lPtBins[0]); - const float minPt = lPtBins[1]; - const float maxPt = lPtBins[2]; - - std::vector lPhiBins = cfPhiBins.value; - const int nBinsPhi = static_cast(lPhiBins[0]); - const float minPhi = lPhiBins[1]; - const float maxPhi = lPhiBins[2]; - - std::vector lEtaBins = cfEtaBins.value; - const int nBinsEta = static_cast(lEtaBins[0]); - const float minEta = lEtaBins[1]; - const float maxEta = lEtaBins[2]; - - const int nBinsCharge = 5; - const float minCharge = -2.5; - const float maxCharge = 2.5; - if (doprocessRec || doprocessRecSim) { - pc.fParticleHist[eHistPt][eRec][eBefore] = new TH1F("[eHistPt][eRec][eBefore]", "p_{T} distribution for reconstructed particles before cuts", nBinsPt, minPt, maxPt); - pc.fParticleHist[eHistPt][eRec][eBefore]->GetXaxis()->SetTitle("p_{T}"); - - pc.fParticleHist[eHistPhi][eRec][eBefore] = new TH1F("[eHistPhi][eRec][eBefore]", "#phi distribution for reconstructed particles before cuts", nBinsPhi, minPhi, maxPhi); - pc.fParticleHist[eHistPhi][eRec][eBefore]->GetXaxis()->SetTitle("#varphi"); - - pc.fParticleHist[eHistEta][eRec][eBefore] = new TH1F("[eHistEta][eRec][eBefore]", "#eta distribution for reconstructed particles before cuts", nBinsEta, minEta, maxEta); - pc.fParticleHist[eHistEta][eRec][eBefore]->GetXaxis()->SetTitle("#eta"); - - pc.fParticleHist[eHistCharge][eRec][eBefore] = new TH1F("[eHistCharge][eRec][eBefore]", "charge distribution for reconstructed particles before cuts", nBinsCharge, minCharge, maxCharge); - pc.fParticleHist[eHistCharge][eRec][eBefore]->GetXaxis()->SetTitle("Particle charge"); - - for (int i = 0; i < eParticleHistograms_N; ++i) { - pc.fParticleHist[i][eRec][eBefore]->SetColors(beforeCutLineColor, -1, beforeCutFillColor); - pc.fParticleHistList->Add(pc.fParticleHist[i][eRec][eBefore]); + misc.fMiscEventCutsCounter[eRec] = new TH1D("[eEventCutsCounter][eRec]", "Event Level Cuts Counter for Reconstructed Data (Sequential)", EEventCuts_N, 0, EEventCuts_N); + misc.fMiscEventCutsCounter[eRec]->SetStats(false); + for (int i = 1; i <= EEventCuts_N; ++i) { + misc.fMiscEventCutsCounter[eRec]->GetXaxis()->SetBinLabel(i, tc.fEventCutSwitch[i - 1].c_str()); } + misc.fMiscHistList->Add(misc.fMiscEventCutsCounter[eRec]); - if (cfMasterCutSwitch) { - pc.fParticleHist[eHistPt][eRec][eAfter] = new TH1F("[eHistPt][eRec][eAfter]", "p_{T} distribution for reconstructed particles after cuts", nBinsPt, minPt, maxPt); - pc.fParticleHist[eHistPt][eRec][eAfter]->GetXaxis()->SetTitle("p_{T}"); - - pc.fParticleHist[eHistPhi][eRec][eAfter] = new TH1F("[eHistPhi][eRec][eAfter]", "#phi distribution for reconstructed particles after cuts", nBinsPhi, minPhi, maxPhi); - pc.fParticleHist[eHistPhi][eRec][eAfter]->GetXaxis()->SetTitle("#varphi"); - - pc.fParticleHist[eHistEta][eRec][eAfter] = new TH1F("[eHistEta][eRec][eAfter]", "#eta distribution for reconstructed particles after cuts", nBinsEta, minEta, maxEta); - pc.fParticleHist[eHistEta][eRec][eAfter]->GetXaxis()->SetTitle("#eta"); - - pc.fParticleHist[eHistCharge][eRec][eAfter] = new TH1F("[eHistCharge][eRec][eAfter]", "charge distribution for reconstructed particles after cuts", nBinsCharge, minCharge, maxCharge); - pc.fParticleHist[eHistCharge][eRec][eAfter]->GetXaxis()->SetTitle("Particle charge"); - - for (int i = 0; i < eParticleHistograms_N; ++i) { - pc.fParticleHist[i][eRec][eAfter]->SetColors(afterCutLineColor, -1, afterCutFillColor); - pc.fParticleHistList->Add(pc.fParticleHist[i][eRec][eAfter]); - } + misc.fMiscParticleCutsCounter[eRec] = new TH1D("[eParticleCutsCounter][eRec]", "Particle Level Cuts Counter for Reconstructed Data (Sequential)", EParticleCuts_N, 0, EParticleCuts_N); + misc.fMiscParticleCutsCounter[eRec]->SetStats(false); + for (int i = 1; i <= EParticleCuts_N; ++i) { + misc.fMiscParticleCutsCounter[eRec]->GetXaxis()->SetBinLabel(i, tc.fParticleCutSwitch[i - 1].c_str()); } + misc.fMiscHistList->Add(misc.fMiscParticleCutsCounter[eRec]); } if (doprocessSim || doprocessRecSim) { - pc.fParticleHist[eHistPt][eSim][eBefore] = new TH1F("[eHistPt][eSim][eBefore]", "p_{T} distribution for simulated particles before cuts", nBinsPt, minPt, maxPt); - pc.fParticleHist[eHistPt][eSim][eBefore]->GetXaxis()->SetTitle("p_{T}"); - - pc.fParticleHist[eHistPhi][eSim][eBefore] = new TH1F("[eHistPhi][eSim][eBefore]", "#phi distribution for simulated particles before cuts", nBinsPhi, minPhi, maxPhi); - pc.fParticleHist[eHistPhi][eSim][eBefore]->GetXaxis()->SetTitle("#varphi"); - - pc.fParticleHist[eHistEta][eSim][eBefore] = new TH1F("[eHistEta][eSim][eBefore]", "#eta distribution for simulated particles before cuts", nBinsEta, minEta, maxEta); - pc.fParticleHist[eHistEta][eSim][eBefore]->GetXaxis()->SetTitle("#eta"); - - pc.fParticleHist[eHistCharge][eSim][eBefore] = new TH1F("[eHistCharge][eSim][eBefore]", "charge distribution for simulated particles before cuts", nBinsCharge, minCharge, maxCharge); - pc.fParticleHist[eHistCharge][eSim][eBefore]->GetXaxis()->SetTitle("Particle charge"); + misc.fMiscEventCutsCounter[eSim] = new TH1D("[eEventCutsCounter][eSim]", "Event Level Cuts Counter for Simulated Data (Sequential)", EEventCuts_N, 0, EEventCuts_N); + misc.fMiscEventCutsCounter[eSim]->SetStats(false); + for (int i = 1; i <= EEventCuts_N; ++i) { + misc.fMiscEventCutsCounter[eSim]->GetXaxis()->SetBinLabel(i, tc.fEventCutSwitch[i - 1].c_str()); + } + misc.fMiscHistList->Add(misc.fMiscEventCutsCounter[eSim]); - for (int i = 0; i < eParticleHistograms_N; ++i) { - pc.fParticleHist[i][eSim][eBefore]->SetColors(beforeCutLineColor, -1, beforeCutFillColor); - pc.fParticleHistList->Add(pc.fParticleHist[i][eSim][eBefore]); + misc.fMiscParticleCutsCounter[eSim] = new TH1D("[eParticleCutsCounter][eSim]", "Particle Level Cuts Counter for Simulated Data (Sequential)", EParticleCuts_N, 0, EParticleCuts_N); + misc.fMiscParticleCutsCounter[eSim]->SetStats(false); + for (int i = 1; i <= EParticleCuts_N; ++i) { + misc.fMiscParticleCutsCounter[eSim]->GetXaxis()->SetBinLabel(i, tc.fParticleCutSwitch[i - 1].c_str()); } + misc.fMiscHistList->Add(misc.fMiscParticleCutsCounter[eSim]); + } - if (cfMasterCutSwitch) { - pc.fParticleHist[eHistPt][eSim][eAfter] = new TH1F("[eHistPt][eSim][eAfter]", "p_{T} distribution for simulated particles after cuts", nBinsPt, minPt, maxPt); - pc.fParticleHist[eHistPt][eSim][eAfter]->GetXaxis()->SetTitle("p_{T}"); + // *) Book particle TLists: + pc.fParticleHistList = new TList(); + pc.fParticleHistList->SetName("ParticleHistograms"); + pc.fParticleHistList->SetOwner(true); + fBaseList->Add(pc.fParticleHistList); // any nested TList in the base TList appears as a subdir in the output ROOT file - pc.fParticleHist[eHistPhi][eSim][eAfter] = new TH1F("[eHistPhi][eSim][eAfter]", "#phi distribution for simulated particles after cuts", nBinsPhi, minPhi, maxPhi); - pc.fParticleHist[eHistPhi][eSim][eAfter]->GetXaxis()->SetTitle("#varphi"); + std::vector> lParticleHistBins(eParticleHistograms_N); - pc.fParticleHist[eHistEta][eSim][eAfter] = new TH1F("[eHistEta][eSim][eAfter]", "#eta distribution for simulated particles after cuts", nBinsEta, minEta, maxEta); - pc.fParticleHist[eHistEta][eSim][eAfter]->GetXaxis()->SetTitle("#eta"); + lParticleHistBins[eHistPt] = cfPtBins.value; + lParticleHistBins[eHistPhi] = cfPhiBins.value; + lParticleHistBins[eHistEta] = cfEtaBins.value; + lParticleHistBins[eHistCharge] = {5, -2.5, 2.5}; - pc.fParticleHist[eHistCharge][eSim][eAfter] = new TH1F("[eHistCharge][eSim][eAfter]", "charge distribution for simulated particles after cuts", nBinsCharge, minCharge, maxCharge); - pc.fParticleHist[eHistCharge][eSim][eAfter]->GetXaxis()->SetTitle("Particle charge"); + if (doprocessRec || doprocessRecSim) { + for (int i = 0; i < eParticleHistograms_N; ++i) { + const auto& bins = lParticleHistBins[i]; + for (int ba = 0; ba < eBeforeAfterCuts_N; ++ba) { + if (!tc.fMasterCutSwitch && ba == eAfter) { + continue; + } + pc.fParticleHist[i][eRec][ba] = new TH1F(Form("[%s][eRec][%s]", ParticleHistNames[i], CutsNames[ba]), Form("%s Distribution for Reconstructed Particles %s Cuts", ParticleHistTitles[i], CutsTitles[ba]), static_cast(bins[0]), bins[1], bins[2]); + pc.fParticleHist[i][eRec][ba]->GetXaxis()->SetTitle(Form("%s", ParticleHistTitles[i])); + pc.fParticleHist[i][eRec][ba]->SetColors(lineColor[ba], -1, fillColor[ba]); + pc.fParticleHistList->Add(pc.fParticleHist[i][eRec][ba]); + } // end of for (int ba = 0; ba < eBeforeAfterCuts_N; ++ba) { + } + } // end of if (doprocessRec || doprocessRecSim) { - for (int i = 0; i < eParticleHistograms_N; ++i) { - pc.fParticleHist[i][eSim][eAfter]->SetColors(afterCutLineColor, -1, afterCutFillColor); - pc.fParticleHistList->Add(pc.fParticleHist[i][eSim][eAfter]); - } + if (doprocessSim || doprocessRecSim) { + for (int i = 0; i < eParticleHistograms_N; ++i) { + const auto& bins = lParticleHistBins[i]; + for (int ba = 0; ba < eBeforeAfterCuts_N; ++ba) { + if (!tc.fMasterCutSwitch && ba == eAfter) { + continue; + } + pc.fParticleHist[i][eSim][ba] = new TH1F(Form("[%s][eSim][%s]", ParticleHistNames[i], CutsNames[ba]), Form("%s Distribution for Simulated Particles %s Cuts", ParticleHistTitles[i], CutsTitles[ba]), static_cast(bins[0]), bins[1], bins[2]); + pc.fParticleHist[i][eSim][ba]->GetXaxis()->SetTitle(Form("%s", ParticleHistTitles[i])); + pc.fParticleHist[i][eSim][ba]->SetColors(lineColor[ba], -1, fillColor[ba]); + pc.fParticleHistList->Add(pc.fParticleHist[i][eSim][ba]); + } // end of for (int ba = 0; ba < eBeforeAfterCuts_N; ++ba) { } - } + } // end of if (doprocessSim || doprocessRecSim) { - // Book event-level histograms + // *) Book event-level histograms ec.fEventHistList = new TList(); ec.fEventHistList->SetName("EventHistograms"); ec.fEventHistList->SetOwner(true); fBaseList->Add(ec.fEventHistList); - float defaultBoundaries[] = {0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}; - const int nDefaultBins = sizeof(defaultBoundaries) / sizeof(defaultBoundaries[0]) - 1; - - std::vector lCent = cfCentBins.value; - const int nBinsCent = static_cast(lCent[0]); - const float minCent = lCent[1]; - const float maxCent = lCent[2]; - - std::vector lMult = cfMultBins.value; - const int nBinsMult = static_cast(lMult[0]); - const float minMult = lMult[1]; - const float maxMult = lMult[2]; - - std::vector lMultRef = cfMultBinsRef.value; - const int nBinsMultRef = static_cast(lMultRef[0]); - const float minMultRef = lMultRef[1]; - const float maxMultRef = lMultRef[2]; - - std::vector lVx = cfVxBins.value; - const int nBinsVx = static_cast(lVx[0]); - const float minVx = lVx[1]; - const float maxVx = lVx[2]; + std::vector> lEventHistBins(eEventHistograms_N); - std::vector lVy = cfVyBins.value; - const int nBinsVy = static_cast(lVy[0]); - const float minVy = lVy[1]; - const float maxVy = lVy[2]; - - std::vector lVz = cfVzBins.value; - const int nBinsVz = static_cast(lVz[0]); - const float minVz = lVz[1]; - const float maxVz = lVz[2]; - - std::vector lIp = cfIpBins.value; - const int nBinsIp = static_cast(lIp[0]); - const float minIp = lIp[1]; - const float maxIp = lIp[2]; - - // eEventHistograms_N + lEventHistBins[eHistCentrality] = cfCentBins.value; + lEventHistBins[eHistMult] = cfMultBins.value; + lEventHistBins[eHistRefMult] = cfRefMultBins.value; + lEventHistBins[eHistVertexX] = cfVxBins.value; + lEventHistBins[eHistVertexY] = cfVyBins.value; + lEventHistBins[eHistVertexZ] = cfVzBins.value; + lEventHistBins[eHistImpactParameter] = cfIpBins.value; + lEventHistBins[eHistNumContrib] = cfNumContribBins.value; + lEventHistBins[eHistOccupancy] = cfOccuBins.value; if (doprocessRec || doprocessRecSim) { - if (cfALICECentBinSwitch) { - ec.fEventHist[eHistCentrality][eRec][eBefore] = new TH1F("[eHistCentrality][eRec][eBefore]", "Centrality (reconstructed) before cuts", nDefaultBins, defaultBoundaries); - } else { - ec.fEventHist[eHistCentrality][eRec][eBefore] = new TH1F("[eHistCentrality][eRec][eBefore]", "Centrality (reconstructed) before cuts", nBinsCent, minCent, maxCent); - } - ec.fEventHist[eHistCentrality][eRec][eBefore]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[centralityEstimator])); - - ec.fEventHist[eHistMultiplicity][eRec][eBefore] = new TH1F("[eHistMultiplicity][eRec][eBefore]", "Multiplicity (reconstructed) before cuts", nBinsMult, minMult, maxMult); - ec.fEventHist[eHistMultiplicity][eRec][eBefore]->GetXaxis()->SetTitle("Multiplicity"); - - ec.fEventHist[eHistReferenceMultiplicity][eRec][eBefore] = new TH1F("[eHistReferenceMultiplicity][eRec][eBefore]", "Reference Multiplicity before cuts", nBinsMultRef, minMultRef, maxMultRef); - ec.fEventHist[eHistReferenceMultiplicity][eRec][eBefore]->GetXaxis()->SetTitle(Form("Reference Multiplicity (%s)", MultiplicityTablesNames[multiplicityTables])); - - ec.fEventHist[eHistVertexX][eRec][eBefore] = new TH1F("[eHistVertexX][eRec][eBefore]", "Vertex X (reconstructed) before cuts", nBinsVx, minVx, maxVx); - ec.fEventHist[eHistVertexX][eRec][eBefore]->GetXaxis()->SetTitle("Vertex X"); - - ec.fEventHist[eHistVertexY][eRec][eBefore] = new TH1F("[eHistVertexY][eRec][eBefore]", "Vertex Y (reconstructed) before cuts", nBinsVy, minVy, maxVy); - ec.fEventHist[eHistVertexY][eRec][eBefore]->GetXaxis()->SetTitle("Vertex Y"); - - ec.fEventHist[eHistVertexZ][eRec][eBefore] = new TH1F("[eHistVertexZ][eRec][eBefore]", "Vertex Z (reconstructed) before cuts", nBinsVz, minVz, maxVz); - ec.fEventHist[eHistVertexZ][eRec][eBefore]->GetXaxis()->SetTitle("Vertex Z"); - for (int i = 0; i < eEventHistograms_N; ++i) { - if (i != eHistImpactParameter) { - ec.fEventHist[i][eRec][eBefore]->SetColors(beforeCutLineColor, -1, beforeCutFillColor); - ec.fEventHistList->Add(ec.fEventHist[i][eRec][eBefore]); + if (i == eHistImpactParameter) { // no impact parameter for Rec + continue; } - } - - if (cfMasterCutSwitch) { - if (cfALICECentBinSwitch) { - ec.fEventHist[eHistCentrality][eRec][eAfter] = new TH1F("[eHistCentrality][eRec][eAfter]", "Centrality (reconstructed) after cuts", nDefaultBins, defaultBoundaries); - } else { - ec.fEventHist[eHistCentrality][eRec][eAfter] = new TH1F("[eHistCentrality][eRec][eAfter]", "Centrality (reconstructed) after cuts", nBinsCent, minCent, maxCent); - } - ec.fEventHist[eHistCentrality][eRec][eAfter]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[centralityEstimator])); - - ec.fEventHist[eHistMultiplicity][eRec][eAfter] = new TH1F("[eHistMultiplicity][eRec][eAfter]", "Multiplicity (reconstructed) after cuts", nBinsMult, minMult, maxMult); - ec.fEventHist[eHistMultiplicity][eRec][eAfter]->GetXaxis()->SetTitle("Multiplicity"); - - ec.fEventHist[eHistReferenceMultiplicity][eRec][eAfter] = new TH1F("[eHistReferenceMultiplicity][eRec][eAfter]", "Reference Multiplicity after cuts", nBinsMultRef, minMultRef, maxMultRef); - ec.fEventHist[eHistReferenceMultiplicity][eRec][eAfter]->GetXaxis()->SetTitle(Form("Reference Multiplicity (%s)", MultiplicityTablesNames[multiplicityTables])); - - ec.fEventHist[eHistVertexX][eRec][eAfter] = new TH1F("[eHistVertexX][eRec][eAfter]", "Vertex X (reconstructed) after cuts", nBinsVx, minVx, maxVx); - ec.fEventHist[eHistVertexX][eRec][eAfter]->GetXaxis()->SetTitle("Vertex X"); - - ec.fEventHist[eHistVertexY][eRec][eAfter] = new TH1F("[eHistVertexY][eRec][eAfter]", "Vertex Y (reconstructed) after cuts", nBinsVy, minVy, maxVy); - ec.fEventHist[eHistVertexY][eRec][eAfter]->GetXaxis()->SetTitle("Vertex Y"); - - ec.fEventHist[eHistVertexZ][eRec][eAfter] = new TH1F("[eHistVertexZ][eRec][eAfter]", "Vertex Z (reconstructed) after cuts", nBinsVz, minVz, maxVz); - ec.fEventHist[eHistVertexZ][eRec][eAfter]->GetXaxis()->SetTitle("Vertex Z"); - - for (int i = 0; i < eEventHistograms_N; ++i) { - if (i != eHistImpactParameter) { - ec.fEventHist[i][eRec][eAfter]->SetColors(afterCutLineColor, -1, afterCutFillColor); - ec.fEventHistList->Add(ec.fEventHist[i][eRec][eAfter]); + const auto& bins = lEventHistBins[i]; + for (int ba = 0; ba < eBeforeAfterCuts_N; ++ba) { + if (!tc.fMasterCutSwitch && ba == eAfter) { + continue; } - } - } - } + ec.fEventHist[i][eRec][ba] = new TH1F(Form("[%s][eRec][%s]", EventHistNames[i], CutsNames[ba]), Form("%s (Reconstructed) %s Cuts", EventHistTitles[i], CutsTitles[ba]), static_cast(bins[0]), bins[1], bins[2]); + ec.fEventHist[i][eRec][ba]->GetXaxis()->SetTitle(Form("%s", EventHistTitles[i])); + ec.fEventHist[i][eRec][ba]->SetColors(lineColor[ba], -1, fillColor[ba]); + ec.fEventHistList->Add(ec.fEventHist[i][eRec][ba]); + } // end of for (int ba = 0; ba < eBeforeAfterCuts_N; ++ba) { + } // end of for (int i = 0; i < eEventHistograms_N; ++i) { + ec.fEventHist[eHistCentrality][eRec][eBefore]->GetXaxis()->SetTitle(Form("%s (%s)", EventHistTitles[eHistCentrality], CentralityEstimatorNames[tc.fCentralityEstimator])); + ec.fEventHist[eHistCentrality][eRec][eAfter]->GetXaxis()->SetTitle(Form("%s (%s)", EventHistTitles[eHistCentrality], CentralityEstimatorNames[tc.fCentralityEstimator])); + ec.fEventHist[eHistRefMult][eRec][eBefore]->GetXaxis()->SetTitle(Form("%s (%s)", EventHistTitles[eHistRefMult], MultiplicityTablesNames[tc.fMultiplicityTables])); + ec.fEventHist[eHistRefMult][eRec][eAfter]->GetXaxis()->SetTitle(Form("%s (%s)", EventHistTitles[eHistRefMult], MultiplicityTablesNames[tc.fMultiplicityTables])); + ec.fEventHist[eHistOccupancy][eRec][eBefore]->GetXaxis()->SetTitle(Form("%s (%s)", EventHistTitles[eHistRefMult], OccupancyEstimatorNames[tc.fOccupancyEstimator])); + ec.fEventHist[eHistOccupancy][eRec][eAfter]->GetXaxis()->SetTitle(Form("%s (%s)", EventHistTitles[eHistRefMult], OccupancyEstimatorNames[tc.fOccupancyEstimator])); + } // end of if (doprocessRec || doprocessRecSim) { if (doprocessSim || doprocessRecSim) { - if (cfALICECentBinSwitch) { - ec.fEventHist[eHistCentrality][eSim][eBefore] = new TH1F("[eHistCentrality][eSim][eBefore]", "Centrality (simulated) before cuts", nDefaultBins, defaultBoundaries); - } else { - ec.fEventHist[eHistCentrality][eSim][eBefore] = new TH1F("[eHistCentrality][eSim][eBefore]", "Centrality (simulated) before cuts", nBinsCent, minCent, maxCent); - } - ec.fEventHist[eHistCentrality][eSim][eBefore]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[centralityEstimator])); - - ec.fEventHist[eHistMultiplicity][eSim][eBefore] = new TH1F("[eHistMultiplicity][eSim][eBefore]", "Multiplicity (simulated) before cuts", nBinsMult, minMult, maxMult); - ec.fEventHist[eHistMultiplicity][eSim][eBefore]->GetXaxis()->SetTitle("Multiplicity"); - - ec.fEventHist[eHistVertexX][eSim][eBefore] = new TH1F("[eHistVertexX][eSim][eBefore]", "Vertex X (simulated) before cuts", nBinsVx, minVx, maxVx); - ec.fEventHist[eHistVertexX][eSim][eBefore]->GetXaxis()->SetTitle("Vertex X"); - - ec.fEventHist[eHistVertexY][eSim][eBefore] = new TH1F("[eHistVertexY][eSim][eBefore]", "Vertex Y (simulated) before cuts", nBinsVy, minVy, maxVy); - ec.fEventHist[eHistVertexY][eSim][eBefore]->GetXaxis()->SetTitle("Vertex Y"); - - ec.fEventHist[eHistVertexZ][eSim][eBefore] = new TH1F("[eHistVertexZ][eSim][eBefore]", "Vertex Z (simulated) before cuts", nBinsVz, minVz, maxVz); - ec.fEventHist[eHistVertexZ][eSim][eBefore]->GetXaxis()->SetTitle("Vertex Z"); - - ec.fEventHist[eHistImpactParameter][eSim][eBefore] = new TH1F("[eHistImpactParameter][eSim][eBefore]", "Impact Parameter (simulated) before cuts", nBinsIp, minIp, maxIp); - ec.fEventHist[eHistImpactParameter][eSim][eBefore]->GetXaxis()->SetTitle("Impact Parameter"); - for (int i = 0; i < eEventHistograms_N; ++i) { - if (i != eHistReferenceMultiplicity) { - ec.fEventHist[i][eSim][eBefore]->SetColors(beforeCutLineColor, -1, beforeCutFillColor); - ec.fEventHistList->Add(ec.fEventHist[i][eSim][eBefore]); - } - } - - if (cfMasterCutSwitch) { - if (cfALICECentBinSwitch) { - ec.fEventHist[eHistCentrality][eSim][eAfter] = new TH1F("[eHistCentrality][eSim][eAfter]", "Centrality (simulated) after cuts", nDefaultBins, defaultBoundaries); - } else { - ec.fEventHist[eHistCentrality][eSim][eAfter] = new TH1F("[eHistCentrality][eSim][eAfter]", "Centrality (simulated) after cuts", nBinsCent, minCent, maxCent); + if (i == eHistRefMult || i == eHistNumContrib || i == eHistOccupancy) { // no RefMult for Sim + continue; } - ec.fEventHist[eHistCentrality][eSim][eAfter]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[centralityEstimator])); - - ec.fEventHist[eHistMultiplicity][eSim][eAfter] = new TH1F("[eHistMultiplicity][eSim][eAfter]", "Multiplicity (simulated) after cuts", nBinsMult, minMult, maxMult); - ec.fEventHist[eHistMultiplicity][eSim][eAfter]->GetXaxis()->SetTitle("Multiplicity"); - - ec.fEventHist[eHistVertexX][eSim][eAfter] = new TH1F("[eHistVertexX][eSim][eAfter]", "Vertex X (simulated) after cuts", nBinsVx, minVx, maxVx); - ec.fEventHist[eHistVertexX][eSim][eAfter]->GetXaxis()->SetTitle("Vertex X"); - - ec.fEventHist[eHistVertexY][eSim][eAfter] = new TH1F("[eHistVertexY][eSim][eAfter]", "Vertex Y (simulated) after cuts", nBinsVy, minVy, maxVy); - ec.fEventHist[eHistVertexY][eSim][eAfter]->GetXaxis()->SetTitle("Vertex Y"); - - ec.fEventHist[eHistVertexZ][eSim][eAfter] = new TH1F("[eHistVertexZ][eSim][eAfter]", "Vertex Z (simulated) after cuts", nBinsVz, minVz, maxVz); - ec.fEventHist[eHistVertexZ][eSim][eAfter]->GetXaxis()->SetTitle("Vertex Z"); - - ec.fEventHist[eHistImpactParameter][eSim][eAfter] = new TH1F("[eHistImpactParameter][eSim][eAfter]", "Impact Parameter (simulated) after cuts", nBinsIp, minIp, maxIp); - ec.fEventHist[eHistImpactParameter][eSim][eAfter]->GetXaxis()->SetTitle("Impact Parameter"); - - for (int i = 0; i < eEventHistograms_N; ++i) { - if (i != eHistReferenceMultiplicity) { - ec.fEventHist[i][eSim][eAfter]->SetColors(afterCutLineColor, -1, afterCutFillColor); - ec.fEventHistList->Add(ec.fEventHist[i][eSim][eAfter]); + const auto& bins = lEventHistBins[i]; + for (int ba = 0; ba < eBeforeAfterCuts_N; ++ba) { + if (!tc.fMasterCutSwitch && ba == eAfter) { + continue; } - } - } - } + ec.fEventHist[i][eSim][ba] = new TH1F(Form("[%s][eSim][%s]", EventHistNames[i], CutsNames[ba]), Form("%s (Simulated) %s Cuts", EventHistTitles[i], CutsTitles[ba]), static_cast(bins[0]), bins[1], bins[2]); + ec.fEventHist[i][eSim][ba]->GetXaxis()->SetTitle(Form("%s", EventHistTitles[i])); + ec.fEventHist[i][eSim][ba]->SetColors(lineColor[ba], -1, fillColor[ba]); + ec.fEventHistList->Add(ec.fEventHist[i][eSim][ba]); + } // end of for (int ba = 0; ba < eBeforeAfterCuts_N; ++ba) { + } // end of for (int i = 0; i < eEventHistograms_N; ++i) { + ec.fEventHist[eHistCentrality][eSim][eBefore]->GetXaxis()->SetTitle(Form("%s (calculated from I.P.)", EventHistTitles[eHistCentrality])); + ec.fEventHist[eHistCentrality][eSim][eAfter]->GetXaxis()->SetTitle(Form("%s (calculated from I.P.)", EventHistTitles[eHistCentrality])); + } // end of if (doprocessSim || doprocessRecSim) { // *) Book observales TLists: obs.fObservablesList = new TList(); @@ -1210,30 +1397,104 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to obs.fObservablesList->SetOwner(true); fBaseList->Add(obs.fObservablesList); + const std::vector defaultCentBoundaries = {0.f, 5.f, 10.f, 20.f, 30.f, 40.f, 50.f, 60.f, 70.f, 80.f}; + const int nDefaultCentBins = defaultCentBoundaries.size() - 1; + if (doprocessRec || doprocessRecSim) { - obs.fProfTwo[eRec][eBefore] = new TProfile("obs.fProfTwo[eRec][eBefore]", "obs.fProfTwo[eRec][eBefore]", 1, 0., 1); - obs.fProfTwo[eRec][eBefore]->GetYaxis()->SetTitle("#LT#LTk#GT#GT / 10^{-k}"); - obs.fObservablesList->Add(obs.fProfTwo[eRec][eBefore]); - - if (cfMasterCutSwitch) { - obs.fProfTwo[eRec][eAfter] = new TProfile("obs.fProfTwo[eRec][eAfter]", "obs.fProfTwo[eRec][eAfter]", 1, 0., 1); - obs.fProfTwo[eRec][eAfter]->GetYaxis()->SetTitle("#LT#LTk#GT#GT / 10^{-k}"); - obs.fObservablesList->Add(obs.fProfTwo[eRec][eAfter]); + for (int i = 0; i < eObservables_N; ++i) { + obs.fProfCorr[i][eRec] = new TProfile(Form("[%s][eRec]", ObservablesNames[i]), Form("%s for Reconstructed Data after Cuts", ObservablesTitles[i]), nDefaultCentBins, defaultCentBoundaries.data()); + obs.fProfCorr[i][eRec]->SetStats(false); + obs.fProfCorr[i][eRec]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[tc.fCentralityEstimator])); + obs.fProfCorr[i][eRec]->GetYaxis()->SetTitle(ObservablesTitles[i]); + obs.fProfCorr[i][eRec]->Sumw2(); + obs.fObservablesList->Add(obs.fProfCorr[i][eRec]); + } + } + + if (doprocessSim || doprocessRecSim) { + for (int i = 0; i < eObservables_N; ++i) { + obs.fProfCorr[i][eSim] = new TProfile(Form("[%s][eSim]", ObservablesNames[i]), Form("%s for Simulated Data after Cuts", ObservablesTitles[i]), nDefaultCentBins, defaultCentBoundaries.data()); + obs.fProfCorr[i][eSim]->SetStats(false); + obs.fProfCorr[i][eSim]->GetXaxis()->SetTitle("Centrality (calculated from I.P.)"); + obs.fProfCorr[i][eSim]->GetYaxis()->SetTitle(ObservablesTitles[i]); + obs.fProfCorr[i][eSim]->Sumw2(); + obs.fObservablesList->Add(obs.fProfCorr[i][eSim]); } } // *) Book and QA TLists: - if (qualityAssuranceSwitch && doprocessRecSim) { + if (tc.fQualityAssuranceSwitch) { qa.fQualityAssuranceList = new TList(); qa.fQualityAssuranceList->SetName("QualityAssurance"); qa.fQualityAssuranceList->SetOwner(true); fBaseList->Add(qa.fQualityAssuranceList); - qa.fHistCentralityRecSim = new TH2F("fHistCentralityRecSim", "Centrality Rec vs Sim", nBinsCent, minCent, maxCent, nBinsCent, minCent, maxCent); - qa.fHistCentralityRecSim->GetXaxis()->SetTitle("Centrality (reconstructed)"); - qa.fHistCentralityRecSim->GetYaxis()->SetTitle("Centrality (simulated)"); - qa.fQualityAssuranceList->Add(qa.fHistCentralityRecSim); + if (doprocessRec || doprocessRecSim) { + const auto& nContribBins = lEventHistBins[eHistNumContrib]; + const auto& refMultBins = lEventHistBins[eHistRefMult]; + for (int i = 0; i < eBeforeAfterCuts_N; ++i) { + qa.fHistMultNContrib[i] = new TH2F(Form("fHistMultNContrib[%s]", CutsNames[i]), Form("refMult vs. nContributors %s Cuts", CutsNames[i]), static_cast(refMultBins[0]), refMultBins[1], refMultBins[2], static_cast(nContribBins[0]), nContribBins[1], nContribBins[2]); + qa.fHistMultNContrib[i]->GetXaxis()->SetTitle(Form("Reference Multiplicity (%s)", MultiplicityTablesNames[tc.fMultiplicityTables])); + qa.fHistMultNContrib[i]->GetYaxis()->SetTitle("Number of contributors"); + qa.fHistMultNContrib[i]->SetStats(false); + qa.fQualityAssuranceList->Add(qa.fHistMultNContrib[i]); + } + + const auto& centBins = lEventHistBins[eHistCentrality]; + for (int i = 0; i < eBeforeAfterCuts_N; ++i) { + qa.fHistCorrCent[i] = new TH2F(Form("fHistCorrCent[%s]", CutsNames[i]), Form("Centrality (%s) vs. Centrality (%s) %s Cuts", CentralityEstimatorNames[tc.fCorrCent[0]], CentralityEstimatorNames[tc.fCorrCent[1]], CutsNames[i]), static_cast(centBins[0]), centBins[1], centBins[2], static_cast(centBins[0]), centBins[1], centBins[2]); + qa.fHistCorrCent[i]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[tc.fCorrCent[0]])); + qa.fHistCorrCent[i]->GetYaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[tc.fCorrCent[1]])); + qa.fHistCorrCent[i]->SetStats(false); + qa.fQualityAssuranceList->Add(qa.fHistCorrCent[i]); + } + + std::vector lQASelTrackBins = cfQASelTrackBins.value; + for (int i = 0; i < eBeforeAfterCuts_N; ++i) { + qa.fHistSelectedTrack[i] = new TH1F(Form("fHistSelectedTrack[%s]", CutsNames[i]), Form("Selected Tracks in %.1f-%.1f%% %s cuts", tc.fQASelTrackCentRange[0], tc.fQASelTrackCentRange[1], CutsNames[i]), static_cast(lQASelTrackBins[0 + i * 3]), lQASelTrackBins[1 + i * 3], lQASelTrackBins[2 + i * 3]); + qa.fHistSelectedTrack[i]->GetXaxis()->SetTitle("Number of selected tracks"); + qa.fHistSelectedTrack[i]->SetStats(false); + qa.fQualityAssuranceList->Add(qa.fHistSelectedTrack[i]); + } + } // end of if (doprocessRec || doprocessRecSim) { + + if (doprocessRecSim) { + const auto& centBins = lEventHistBins[eHistCentrality]; + for (int i = 0; i < eBeforeAfterCuts_N; ++i) { + qa.fHistCentralityRecSim[i] = new TH2F(Form("fHistCentralityRecSim[%s]", CutsNames[i]), Form("Centrality Rec vs Sim %s Cuts", CutsNames[i]), static_cast(centBins[0]), centBins[1], centBins[2], static_cast(centBins[0]), centBins[1], centBins[2]); + qa.fHistCentralityRecSim[i]->GetXaxis()->SetTitle("Centrality (reconstructed)"); + qa.fHistCentralityRecSim[i]->GetYaxis()->SetTitle("Centrality (simulated)"); + qa.fHistCentralityRecSim[i]->SetStats(false); + qa.fQualityAssuranceList->Add(qa.fHistCentralityRecSim[i]); + } + } // end of if (doprocessRecSim) { + } // end of if (tc.fQualityAssuranceSwitch) { + + // *) Book InternalValidation TLists: + if (tc.fInternalValidationSwitch) { + intval.fRandGen = new TRandom3(tc.fRNGSeed); + intval.fIntValFun = new TF1("fIntValFun", "1 + 2*0.04*cos(1*(x - [0])) + 2*[1]*cos(2*(x - [0])) + 2*[2]*cos(3*(x - [0])) + 2*[3]*cos(4*(x - [0]))", 0., math::TwoPI); + for (std::size_t i = 1; i <= tc.fIVHarmonics.size(); ++i) { + intval.fIntValFun->SetParameter(i, tc.fIVHarmonics[i - 1]); + } + + intval.fIntValList = new TList(); + intval.fIntValList->SetName("InternalValidation"); + intval.fIntValList->SetOwner(true); + fBaseList->Add(intval.fIntValList); + + for (int i = 0; i < eIntValHarmonics_N; ++i) { + intval.fProfIntVal[i] = new TProfile(Form("[%s]", IntValNames[i]), Form("Internal Validation for %s with True Value %f", IntValTitles[i], std::pow(tc.fIVHarmonics[i], 2)), nDefaultCentBins, defaultCentBoundaries.data()); + intval.fProfIntVal[i]->SetStats(false); + intval.fProfIntVal[i]->GetXaxis()->SetTitle("Centrality (calculated from I.P.)"); + intval.fProfIntVal[i]->GetYaxis()->SetTitle(IntValTitles[i]); + intval.fProfIntVal[i]->Sumw2(); + intval.fIntValList->Add(intval.fProfIntVal[i]); + } } + + geoCuts.fMultNContribBounds[eUpper] = new TF1("fMultNContribBounds[eUpper]", tc.fMultNContribBound[eUpper].c_str(), lEventHistBins[eHistRefMult][1], lEventHistBins[eHistRefMult][2]); + geoCuts.fMultNContribBounds[eLower] = new TF1("fMultNContribBounds[eLower]", tc.fMultNContribBound[eLower].c_str(), lEventHistBins[eHistRefMult][1], lEventHistBins[eHistRefMult][2]); } // end of void init(InitContext&) { // A) Process only reconstructed data: @@ -1263,7 +1524,5 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to // *) The final touch: WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { - return WorkflowSpec{ - adaptAnalysisTask(cfgc), - }; + return WorkflowSpec{adaptAnalysisTask(cfgc)}; } // WorkflowSpec... diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCumulants.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCumulants.cxx index ff637d76aa7..0a27a5ef7b4 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCumulants.cxx +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCumulants.cxx @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -111,13 +112,19 @@ static constexpr std::array EventHistNames = { enum EnParticleHistograms { ePt, + eEta, ePhi, + eDCAXY, + eDCAZ, eParticleHistograms_N }; static constexpr std::array ParticleHistNames = { "Pt", - "Phi"}; + "Eta", + "Phi", + "DCAXY", + "DCAZ"}; enum EnQAHistograms { eQACent, @@ -197,9 +204,11 @@ static constexpr std::array BeforeAfterNames = "After"}; static constexpr int NumHarmonics = 3; // n = 2, 3, 4 -static constexpr int NumTwoPCorrBins = NumHarmonics; // , , , in this case 3 -static constexpr int NumFourPCorrBins = (NumHarmonics * (NumHarmonics + 1)) / 2; // , , , , , , with , in this case 6 -static constexpr int NumSixPCorrBins = (NumHarmonics * (NumHarmonics - 1) * (NumHarmonics - 2)) / 6; // , without , in this case 1 +static constexpr int NumTwoPCorrBins = NumHarmonics + 1; // <<2>>_n = , , , , in this case 3 +static constexpr int NumThreePCorrBins = 3; // <<3>>_{n1, n2, n3}, (n1, n2, n3) = (2, -1, -1), (3, -2, -1), (4, -2, -2), in this case 3 +static constexpr int NumFourPCorrBins = (NumHarmonics * (NumHarmonics + 1)) / 2; // <<4>>_{n1, n1, n2, n2} = , , , , , , in this case 6 +static constexpr int NumSixPCorrBins = (NumHarmonics * (NumHarmonics - 1) * (NumHarmonics - 2)) / 6; // <<6>>_{n1, n1, n2, n2, n3, n3} = , in this case 1 +static constexpr std::array, NumThreePCorrBins> ThreePHarmonicIndex{{{2, 1, 1}, {3, 2, 1}, {4, 2, 2}}}; static constexpr std::array, NumFourPCorrBins> FourPHarmonicIndex{{{2, 2}, {2, 3}, {2, 4}, {3, 3}, {3, 4}, {4, 4}}}; static constexpr int NumEtaGap = 3; @@ -230,6 +239,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam Configurable cfCentCutSwitch{"cfCentCutSwitch", true, "centrality cut switch"}; Configurable cfNumContribCutSwitch{"cfNumContribCutSwitch", true, "NContribution cut switch"}; Configurable cfNumTrackCutSwitch{"cfNumTrackCutSwitch", true, "NTrack cut switch"}; + Configurable cfOccupancyCutSwitch{"cfOccupancyCutSwitch", true, "occupancy cut switch"}; Configurable cfCentCorrCutSwitch{"cfCentCorrCutSwitch", true, "centrality correlation outlier cut switch"}; Configurable cfMultCorrCutSwitch{"cfMultCorrCutSwitch", true, "multiplicity correlation outlier cut switch"}; Configurable cfNumContribMultCorrCutSwitch{"cfNumContribMultCorrCutSwitch", true, "NContribution vs. multiplicity correlation outlier cut switch"}; @@ -248,6 +258,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam Configurable> cfCentCut{"cfCentCut", {10., 20.}, "centrality range: {min, max}[%]"}; Configurable> cfNumContribCut{"cfNumContribCut", {0, 3000.}, "NContribution range: {min, max}"}; Configurable> cfNumTrackCut{"cfNumTrackCut", {0, 3000.}, "NTrack range: {min, max}"}; + Configurable> cfOccupancyCut{"cfOccupancyCut", {0, 6000.}, "Occupancy range: {min, max}"}; Configurable> cfCentABCorrCut{"cfCentABCorrCut", {1.4, 300., 1.4, 300.}, "parameters of limits in FT0C vs. FT0M centrality 2D histograms, cx+d < y < ax+b: {a,b,c,d}"}; Configurable> cfCentACCorrCut{"cfCentACCorrCut", {1.4, 300., 1.4, 300.}, "parameters of limits in FT0C vs. FT0M centrality 2D histograms, cx+d < y < ax+b: {a,b,c,d}"}; Configurable> cfCentBCCorrCut{"cfCentBCCorrCut", {1.4, 300., 1.4, 300.}, "parameters of limits in FT0C vs. FT0M centrality 2D histograms, cx+d < y < ax+b: {a,b,c,d}"}; @@ -271,7 +282,11 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam // *) Bins Configurable> cfPtBins{"cfPtBins", {1000, 0., 100.}, "nPtBins, ptMin, ptMax"}; + Configurable> cfEtaBins{"cfEtaBins", {1000, -1., 1.}, "nEtaBins, EtaMin, EtaMax"}; Configurable> cfPhiBins{"cfPhiBins", {1000, 0., o2::constants::math::TwoPI}, "nPhiBins, phiMin, phiMax"}; + Configurable> cfDCAXYBins{"cfDCAXYBins", {1000, -5., 5.}, "nDCAXYBins, DCAXYMin, DCAXYMax"}; + Configurable> cfDCAZBins{"cfDCAZBins", {1000, -5., 5.}, "nDCAZBins, DCAZMin, DCAZMax"}; + Configurable> cfCentBins{"cfCentBins", {100, 0., 100.}, "nCenBins, cenMin, cenMax"}; Configurable> cfFT0CMultBins{"cfFT0CMultBins", {100, 0., 60000.}, "nMultBins, MultMin, MultMax"}; Configurable> cfFT0MMultBins{"cfFT0MMultBins", {100, 0., 250000.}, "nMultBins, MultMin, MultMax"}; @@ -281,9 +296,12 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam Configurable> cfVerZBins{"cfVerZBins", {100, -50., 50.}, "nVerZBins, VerZMin, VerZMax"}; Configurable> cfNumContribBins{"cfNumContribBins", {100, 0., 5000.}, "nNumContribBins, NumContribMin, NumContribMax"}; Configurable> cfNumTrackBins{"cfNumTrackBins", {100, 0., 5000.}, "nNumTrackBins, NumTrackMin, NumTrackMax"}; + Configurable> cfTwoParticleCorrBins{"cfTwoParticleCorrBins", {100, 0., 2e-3}, "nTwoParticleCorrBins, TwoParticleCorrMin, TwoParticleCorrMax"}; + Configurable> cfThreeParticleCorrBins{"cfThreeParticleCorrBins", {100, 0., 2e-4}, "nThreeParticleCorrBins, ThreeParticleCorrMin, ThreeParticleCorrMax"}; Configurable> cfFourParticleCorrBins{"cfFourParticleCorrBins", {100, 0., 1e-7}, "nFourParticleCorrBins, FourParticleCorrMin, FourParticleCorrMax"}; Configurable> cfSixParticleCorrBins{"cfSixParticleCorrBins", {100, 0., 1e-11}, "nSixParticleCorrBins, SixParticleCorrMin, SixParticleCorrMax"}; + Configurable> cfNumTrackTotalBins{"cfNumTrackTotalBins", {100, 0, 3000}, "nNumTrackTotalBins, NumTrackTotalMin, NumTrackTotalMax"}; // *) Define and initialize all data members to be called in the main process* functions: // **) Task configuration: @@ -302,6 +320,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam bool fCentCutSwitch = true; bool fNumContribCutSwitch = true; bool fNumTrackCutSwitch = true; + bool fOccupancyCutSwitch = true; bool fCentCorrCutSwitch = true; bool fMultCorrCutSwitch = true; bool fNumContribMultCorrCutSwitch = true; @@ -318,6 +337,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam std::vector fCentCut = {10., 20.}; std::vector fNumContribCut = {0, 3000.}; std::vector fNumTrackCut = {0, 350.}; + std::vector fOccupancyCut = {0, 6000.}; std::vector>> fCentAllCorrCut = {{{0., 0., 0., 0.}, {0., 0., 0., 0.}, {0., 0., 0., 0.}}, {{0., 0., 0., 0.}, {0., 0., 0., 0.}, {0., 0., 0., 0.}}, {{0., 0., 0., 0.}, {0., 0., 0., 0.}, {0., 0., 0., 0.}}}; std::vector>> fMultAllCorrCut = {{{0., 0., 0., 0.}, {0., 0., 0., 0.}, {0., 0., 0., 0.}}, {{0., 0., 0., 0.}, {0., 0., 0., 0.}, {0., 0., 0., 0.}}, {{0., 0., 0., 0.}, {0., 0., 0., 0.}, {0., 0., 0., 0.}}}; std::vector fNumContribMultCorrCut = {0.024, -600., 0.035, 100.}; @@ -331,7 +351,10 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam std::vector fEtaGap = {0.4, 0.8, 1.0}; std::vector fPtBins = {1000, 0., 100.}; + std::vector fEtaBins = {100, -1., 1.}; std::vector fPhiBins = {1000, 0., o2::constants::math::TwoPI}; + std::vector fDCAXYBins = {100, -5., 5.}; + std::vector fDCAZBins = {100, -5., 5.}; std::vector fCentBins = {100, 0., 100.}; std::vector fMultBins = {100, 0, 5000}; @@ -345,8 +368,10 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam std::vector fNumContribBins = {100, 0, 5000}; std::vector fNumTrackBins = {100, 0, 5000}; std::vector fTwoParticleCorrBins = {100, 0, 2e-3}; + std::vector fThreeParticleCorrBins = {100, 0., 2e-4}; std::vector fFourParticleCorrBins = {100, 0, 1e-7}; std::vector fSixParticleCorrBins = {100, 0, 1e-11}; + std::vector fNumTrackTotalBins = {100, 0, 3000}; std::string fFileWithWeights = "/scratch3/go52dab/O2tutorial/analysis_code/weights.root"; @@ -417,13 +442,18 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam TList* fMultiparticleCorrelationBackupList = nullptr; std::map fMultiparticleCorrelationByRunMap; std::array fTwoParticleCorrelationProfiles{}; // [cut] + std::array fThreeParticleCorrelationProfiles{}; std::array fFourParticleCorrelationProfiles{}; std::array fSixParticleCorrelationProfiles{}; std::array, NumEtaGap> fTwoParticleCorrelationGapProfiles{}; std::array, eBeforeAfter_N>, eBeforeAfter_N> fTwoParticleCorrelationHistograms{}; // [cut][event weight][n] + std::array, eBeforeAfter_N>, eBeforeAfter_N> fThreeParticleCorrelationHistograms{}; std::array, eBeforeAfter_N>, eBeforeAfter_N> fFourParticleCorrelationHistograms{}; std::array, eBeforeAfter_N>, eBeforeAfter_N> fSixParticleCorrelationHistograms{}; std::array, NumEtaGap> fTwoParticleCorrelationGapHistograms{}; + std::array fTwoParticleCorrelationNumTrackProfiles{}; + std::array fThreeParticleCorrelationNumTrackProfiles{}; + std::array fFourParticleCorrelationNumTrackProfiles{}; } mc; struct EventByEventQuantities { @@ -434,12 +464,15 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam float fCentralitySim = 0.; float fImpactParameter = 0.; float fNumContrib = 0.; - std::array, eBeforeAfter_N> fTwoParticleCorrelationEbye{}; // <<2>> [before, after][, , ] + std::array, eBeforeAfter_N> fTwoParticleCorrelationEbye{}; // <<2>> [before, after][, ,, ] std::array, eBeforeAfter_N> fTwoParticleCorrelationMinEbye{}; std::array, eBeforeAfter_N> fTwoParticleCorrelationMaxEbye{}; std::array, eBeforeAfter_N> fFourParticleCorrelationEbye{}; // <<4>> [before, after][, , , , , ] std::array, eBeforeAfter_N> fFourParticleCorrelationMinEbye{}; std::array, eBeforeAfter_N> fFourParticleCorrelationMaxEbye{}; + std::array, eBeforeAfter_N> fThreeParticleCorrelationEbye{}; // <<3>> [before, after][] + std::array, eBeforeAfter_N> fThreeParticleCorrelationMinEbye{}; + std::array, eBeforeAfter_N> fThreeParticleCorrelationMaxEbye{}; std::array, eBeforeAfter_N> fSixParticleCorrelationEbye{}; // <<6>> [before, after][] std::array, eBeforeAfter_N> fSixParticleCorrelationMinEbye{}; std::array, eBeforeAfter_N> fSixParticleCorrelationMaxEbye{}; @@ -455,6 +488,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam bool bSel8Cut = true; bool bCentCut = true; bool bNumContribCut = true; + bool bNumOccupancyCut = true; bool bCentCorrCut = true; bool bMultCorrCut = true; bool bNumContribMultCorrCut = true; @@ -481,6 +515,9 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam // *) NumContribCut bNumContribCut = ebye.fNumContrib < tc.fNumContribCut[1] && ebye.fNumContrib > tc.fNumContribCut[0]; + // *) NumOccupancyCut + bNumOccupancyCut = collision.trackOccupancyInTimeRange() < tc.fOccupancyCut[1] && + collision.trackOccupancyInTimeRange() > tc.fOccupancyCut[0]; // *) CentCorrCut float iCent = 0.; float jCent = 0.; @@ -538,6 +575,9 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam if (tc.fNumContribCutSwitch) { pass &= bNumContribCut; } + if (tc.fOccupancyCutSwitch) { + pass &= bNumOccupancyCut; + } if (tc.fCentCorrCutSwitch) { pass &= bCentCorrCut; } @@ -641,7 +681,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam return TComplex::Conjugate(mcc.fQvectorAfter[-n][p]); } - // TComplex mccTwo(int n1, int n2, EnBeforeAfter eba) + // TComplex mccFour(int n1, int n2, EnBeforeAfter eba) // { // return mccQ(n1, 1, eba) * mccQ(n2, 1, eba) - mccQ(n1 + n2, 2, eba); // } @@ -978,12 +1018,19 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam for (int i = 0; i < eBeforeAfter_N; i++) { // before/after cut mc.fTwoParticleCorrelationProfiles[i] = nullptr; + mc.fThreeParticleCorrelationProfiles[i] = nullptr; mc.fFourParticleCorrelationProfiles[i] = nullptr; mc.fSixParticleCorrelationProfiles[i] = nullptr; - mc.fTwoParticleCorrelationGapProfiles[i].fill(nullptr); + for (int k = 0; k < NumEtaGap; k++) { + mc.fTwoParticleCorrelationGapProfiles[k][i] = nullptr; + } + mc.fTwoParticleCorrelationNumTrackProfiles[i] = nullptr; + mc.fThreeParticleCorrelationNumTrackProfiles[i] = nullptr; + mc.fFourParticleCorrelationNumTrackProfiles[i] = nullptr; for (int j = 0; j < eBeforeAfter_N; j++) { // before/after weight mc.fTwoParticleCorrelationHistograms[i][j].fill(nullptr); + mc.fThreeParticleCorrelationHistograms[i][j].fill(nullptr); mc.fFourParticleCorrelationHistograms[i][j].fill(nullptr); mc.fSixParticleCorrelationHistograms[i][j].fill(nullptr); } @@ -993,24 +1040,33 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam for (int i = 0; i < eBeforeAfter_N; i++) { // before/after cut mc.fTwoParticleCorrelationProfiles[i] = new TProfile(Form("prof2%sCut", BeforeAfterNames[i + 2]), Form("2-p correlation %s cut", BeforeAfterNames[i]), NumTwoPCorrBins, 0., 1.); mc.fTwoParticleCorrelationProfiles[i]->Sumw2(); + mc.fTwoParticleCorrelationNumTrackProfiles[i] = new TProfile2D(Form("prof2NumTrack%sCut", BeforeAfterNames[i + 2]), Form("2-p correlation vs. nTracks %s cut", BeforeAfterNames[i]), NumTwoPCorrBins, 0., 1., tc.fNumTrackTotalBins[0], tc.fNumTrackTotalBins[1], tc.fNumTrackTotalBins[2]); + // mc.fTwoParticleCorrelationNumTrackProfiles[i]->Sumw2(); for (int n = 0; n < NumEtaGap; n++) { mc.fTwoParticleCorrelationGapProfiles[n][i] = new TProfile(Form("prof2%sCutGapped%.1f", BeforeAfterNames[i + 2], tc.fEtaGap[n]), Form("2-p correlation %s cut, %.1f gapped", BeforeAfterNames[i], tc.fEtaGap[n]), NumTwoPCorrBins, 0., 1.); mc.fTwoParticleCorrelationGapProfiles[n][i]->Sumw2(); } - for (int k = 0; k < NumTwoPCorrBins; k++) { // v2, v3, v4 - mc.fTwoParticleCorrelationProfiles[i]->GetXaxis()->SetBinLabel(k + 1, Form("", k + 2)); + for (int k = 0; k < NumTwoPCorrBins; k++) { // v1, v2, v3, v4 + mc.fTwoParticleCorrelationProfiles[i]->GetXaxis()->SetBinLabel(k + 1, Form("", k + 1)); + mc.fTwoParticleCorrelationNumTrackProfiles[i]->GetXaxis()->SetBinLabel(k + 1, Form("", k + 1)); + for (int m = 0; m < tc.fNumTrackTotalBins[0]; m++) { + int width = (tc.fNumTrackTotalBins[2] - tc.fNumTrackTotalBins[1]) / tc.fNumTrackTotalBins[0]; + int lbound = m * width; + int ubound = (m + 1) * width; + mc.fTwoParticleCorrelationNumTrackProfiles[i]->GetYaxis()->SetBinLabel(m + 1, Form("%d-%d", lbound, ubound)); + } for (int n = 0; n < NumEtaGap; n++) { - mc.fTwoParticleCorrelationGapProfiles[n][i]->GetXaxis()->SetBinLabel(k + 1, Form("", k + 2)); + mc.fTwoParticleCorrelationGapProfiles[n][i]->GetXaxis()->SetBinLabel(k + 1, Form("", k + 1)); if (static_cast(i)) { - mc.fTwoParticleCorrelationGapHistograms[n][k] = new TH1D(Form("hist2v%dAfterCutWithGap%.1f", k + 2, tc.fEtaGap[n]), Form("2-p correlation v%d^2 after cut with gap %.1f", k + 2, tc.fEtaGap[n]), static_cast(tc.fTwoParticleCorrBins[0]), tc.fTwoParticleCorrBins[1], tc.fTwoParticleCorrBins[2]); + mc.fTwoParticleCorrelationGapHistograms[n][k] = new TH1D(Form("hist2v%dAfterCutWithGap%.1f", k + 1, tc.fEtaGap[n]), Form("2-p correlation v%d^2 after cut with gap %.1f", k + 1, tc.fEtaGap[n]), static_cast(tc.fTwoParticleCorrBins[0]), tc.fTwoParticleCorrBins[1], tc.fTwoParticleCorrBins[2]); mc.fTwoParticleCorrelationGapHistograms[n][k]->Sumw2(); mc.fTwoParticleCorrelationGapHistograms[n][k]->SetOption("HIST"); } } for (int j = 0; j < eBeforeAfter_N; j++) { // before/after weight - mc.fTwoParticleCorrelationHistograms[i][j][k] = new TH1D(Form("hist2v%d%sCut%sWeight", k + 2, BeforeAfterNames[i + 2], BeforeAfterNames[j + 2]), Form("2-p correlation v%d^2 %s cut %s weight", k + 2, BeforeAfterNames[i], BeforeAfterNames[j]), static_cast(tc.fTwoParticleCorrBins[0]), tc.fTwoParticleCorrBins[1], tc.fTwoParticleCorrBins[2]); + mc.fTwoParticleCorrelationHistograms[i][j][k] = new TH1D(Form("hist2v%d%sCut%sWeight", k + 1, BeforeAfterNames[i + 2], BeforeAfterNames[j + 2]), Form("2-p correlation v%d^2 %s cut %s weight", k + 1, BeforeAfterNames[i], BeforeAfterNames[j]), static_cast(tc.fTwoParticleCorrBins[0]), tc.fTwoParticleCorrBins[1], tc.fTwoParticleCorrBins[2]); if (static_cast(j)) { mc.fTwoParticleCorrelationHistograms[i][j][k]->Sumw2(); mc.fTwoParticleCorrelationHistograms[i][j][k]->SetOption("HIST"); @@ -1024,18 +1080,58 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam } } mc.fMultiparticleCorrelationByRunMap[ebye.fRunNumber]->Add(mc.fTwoParticleCorrelationProfiles[i]); + mc.fMultiparticleCorrelationByRunMap[ebye.fRunNumber]->Add(mc.fTwoParticleCorrelationNumTrackProfiles[i]); for (int n = 0; n < NumEtaGap; n++) { mc.fMultiparticleCorrelationByRunMap[ebye.fRunNumber]->Add(mc.fTwoParticleCorrelationGapProfiles[n][i]); } } + // Define 3p profiles and histograms: + for (int i = 0; i < eBeforeAfter_N; i++) { // before/after cut + mc.fThreeParticleCorrelationProfiles[i] = new TProfile(Form("prof3%sCut", BeforeAfterNames[i + 2]), Form("3-p correlation %s cut", BeforeAfterNames[i]), NumThreePCorrBins, 0., 1.); + mc.fThreeParticleCorrelationProfiles[i]->Sumw2(); + mc.fThreeParticleCorrelationNumTrackProfiles[i] = new TProfile2D(Form("prof3NumTrack%sCut", BeforeAfterNames[i + 2]), Form("3-p correlation vs. nTracks %s cut", BeforeAfterNames[i]), NumThreePCorrBins, 0., 1., tc.fNumTrackTotalBins[0], tc.fNumTrackTotalBins[1], tc.fNumTrackTotalBins[2]); + // mc.fThreeParticleCorrelationNumTrackProfiles[i]->Sumw2(); + + for (int k = 0; k < NumThreePCorrBins; k++) { + mc.fThreeParticleCorrelationProfiles[i]->GetXaxis()->SetBinLabel(k + 1, Form("", ThreePHarmonicIndex[k][0], ThreePHarmonicIndex[k][1], ThreePHarmonicIndex[k][2])); + mc.fThreeParticleCorrelationNumTrackProfiles[i]->GetXaxis()->SetBinLabel(k + 1, Form("", ThreePHarmonicIndex[k][0], ThreePHarmonicIndex[k][1], ThreePHarmonicIndex[k][2])); + for (int m = 0; m < tc.fNumTrackTotalBins[0]; m++) { + int width = (tc.fNumTrackTotalBins[2] - tc.fNumTrackTotalBins[1]) / tc.fNumTrackTotalBins[0]; + int lbound = m * width; + int ubound = (m + 1) * width; + mc.fThreeParticleCorrelationNumTrackProfiles[i]->GetYaxis()->SetBinLabel(m + 1, Form("%d-%d", lbound, ubound)); + } + + for (int j = 0; j < eBeforeAfter_N; j++) { // before/after weight + mc.fThreeParticleCorrelationHistograms[i][j][k] = new TH1D(Form("hist3v%dv%dv%d%sCut%sWeight", ThreePHarmonicIndex[k][0], ThreePHarmonicIndex[k][1], ThreePHarmonicIndex[k][2], BeforeAfterNames[i + 2], BeforeAfterNames[j + 2]), Form("3-p correlation v%dv%dv%d %s cut %s weight", ThreePHarmonicIndex[k][0], ThreePHarmonicIndex[k][1], ThreePHarmonicIndex[k][2], BeforeAfterNames[i], BeforeAfterNames[j]), static_cast(tc.fThreeParticleCorrBins[0]), tc.fThreeParticleCorrBins[1], tc.fThreeParticleCorrBins[2]); + if (static_cast(j)) { + mc.fThreeParticleCorrelationHistograms[i][j][k]->Sumw2(); + mc.fThreeParticleCorrelationHistograms[i][j][k]->SetOption("HIST"); + } + mc.fMultiparticleCorrelationBackupList->Add(mc.fThreeParticleCorrelationHistograms[i][j][k]); + } + } + mc.fMultiparticleCorrelationByRunMap[ebye.fRunNumber]->Add(mc.fThreeParticleCorrelationProfiles[i]); + mc.fMultiparticleCorrelationByRunMap[ebye.fRunNumber]->Add(mc.fThreeParticleCorrelationNumTrackProfiles[i]); + } + // Define 4p profiles and histograms: for (int i = 0; i < eBeforeAfter_N; i++) { // before/after cut mc.fFourParticleCorrelationProfiles[i] = new TProfile(Form("prof4%sCut", BeforeAfterNames[i + 2]), Form("4-p correlation %s cut", BeforeAfterNames[i]), NumFourPCorrBins, 0., 1.); mc.fFourParticleCorrelationProfiles[i]->Sumw2(); + mc.fFourParticleCorrelationNumTrackProfiles[i] = new TProfile2D(Form("prof4NumTrack%sCut", BeforeAfterNames[i + 2]), Form("4-p correlation vs. nTracks %s cut", BeforeAfterNames[i]), NumFourPCorrBins, 0., 1., tc.fNumTrackTotalBins[0], tc.fNumTrackTotalBins[1], tc.fNumTrackTotalBins[2]); + // mc.fFourParticleCorrelationNumTrackProfiles[i]->Sumw2(); for (int k = 0; k < NumFourPCorrBins; k++) { // v2v2, v2v3, v2v4, v3v3, v3v4, v4v4 mc.fFourParticleCorrelationProfiles[i]->GetXaxis()->SetBinLabel(k + 1, Form("", FourPHarmonicIndex[k][0], FourPHarmonicIndex[k][1])); + mc.fFourParticleCorrelationNumTrackProfiles[i]->GetXaxis()->SetBinLabel(k + 1, Form("", FourPHarmonicIndex[k][0], FourPHarmonicIndex[k][1])); + for (int m = 0; m < tc.fNumTrackTotalBins[0]; m++) { + int width = (tc.fNumTrackTotalBins[2] - tc.fNumTrackTotalBins[1]) / tc.fNumTrackTotalBins[0]; + int lbound = m * width; + int ubound = (m + 1) * width; + mc.fFourParticleCorrelationNumTrackProfiles[i]->GetYaxis()->SetBinLabel(m + 1, Form("%d-%d", lbound, ubound)); + } for (int j = 0; j < eBeforeAfter_N; j++) { // before/after weight mc.fFourParticleCorrelationHistograms[i][j][k] = new TH1D(Form("hist4v%dv%d%sCut%sWeight", FourPHarmonicIndex[k][0], FourPHarmonicIndex[k][1], BeforeAfterNames[i + 2], BeforeAfterNames[j + 2]), Form("4-p correlation v%d^2v%d^2 %s cut %s weight", FourPHarmonicIndex[k][0], FourPHarmonicIndex[k][1], BeforeAfterNames[i], BeforeAfterNames[j]), static_cast(tc.fFourParticleCorrBins[0]), tc.fFourParticleCorrBins[1], tc.fFourParticleCorrBins[2]); @@ -1047,6 +1143,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam } } mc.fMultiparticleCorrelationByRunMap[ebye.fRunNumber]->Add(mc.fFourParticleCorrelationProfiles[i]); + mc.fMultiparticleCorrelationByRunMap[ebye.fRunNumber]->Add(mc.fFourParticleCorrelationNumTrackProfiles[i]); } // Define 6p profiles and histograms: @@ -1295,7 +1392,10 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam // Fill track histograms before cut: pc.fParticleHistograms[ePt][eRec][eBefore]->Fill(track.pt()); + pc.fParticleHistograms[eEta][eRec][eBefore]->Fill(track.eta()); pc.fParticleHistograms[ePhi][eRec][eBefore]->Fill(track.phi()); + pc.fParticleHistograms[eDCAXY][eRec][eBefore]->Fill(track.dcaXY()); + pc.fParticleHistograms[eDCAZ][eRec][eBefore]->Fill(track.dcaZ()); // Calculating Q-vector before cut: dEta = track.eta(); @@ -1343,13 +1443,16 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam if (ctParticleCuts(track)) { - // Fill phi/pt real histogram with this run number: + // Fill phi (real rec), pt (MC rec) histogram with this run number: wt.fPhiByRunMap.at(ebye.fRunNumber)->Fill(track.phi()); wt.fPtRealByRunMap.at(ebye.fRunNumber)->Fill(track.pt()); // Fill particle histograms after cut: pc.fParticleHistograms[ePt][eRec][eAfter]->Fill(track.pt()); + pc.fParticleHistograms[eEta][eRec][eAfter]->Fill(track.eta()); pc.fParticleHistograms[ePhi][eRec][eAfter]->Fill(track.phi()); + pc.fParticleHistograms[eDCAXY][eRec][eAfter]->Fill(track.dcaXY()); + pc.fParticleHistograms[eDCAZ][eRec][eAfter]->Fill(track.dcaZ()); // Calculating Q-vector after cut: for (int h = 0; h < mcc.MaxHarmonic; h++) { @@ -1397,21 +1500,15 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam // Fill MC particle histograms before cut: pc.fParticleHistograms[ePt][eSim][eBefore]->Fill(mcparticle.pt()); + pc.fParticleHistograms[eEta][eSim][eBefore]->Fill(mcparticle.eta()); pc.fParticleHistograms[ePhi][eSim][eBefore]->Fill(mcparticle.phi()); if (ctParticleCuts(mcparticle)) { - // Fill pt MC sim histogram with this run number: - double diffPt = std::abs(track.pt() - mcparticle.pt()); - double diffPtMax = (tc.fPtBins[2] - tc.fPtBins[1]) / tc.fPtBins[0]; - if (diffPt < diffPtMax) { - wt.fPtMCByRunMap.at(ebye.fRunNumber)->Fill(mcparticle.pt()); - } else { - if (tc.fPrintSwitch) { - LOGF(info, "|RecPt - SimPt| = %e > %e", diffPt, diffPtMax); - } - } + // Fill pt (MC sim) histogram with this run number: + wt.fPtMCByRunMap.at(ebye.fRunNumber)->Fill(mcparticle.pt()); // Fill MC particle histograms after cut: pc.fParticleHistograms[ePt][eSim][eAfter]->Fill(mcparticle.pt()); + pc.fParticleHistograms[eEta][eSim][eAfter]->Fill(mcparticle.eta()); pc.fParticleHistograms[ePhi][eSim][eAfter]->Fill(mcparticle.phi()); } } @@ -1422,13 +1519,17 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam // Fill real event histograms (NumTrack) before cut: ev.fEventHistograms[eNumTrack][eRec][eBefore]->Fill(nTracksBefore); ev.fEventHistograms[eNumTrack][eRec][eAfter]->Fill(nTracksAfter); - bool bNumTrackCut = nTracksAfter > tc.fNumTrackCut[0] && nTracksAfter < tc.fNumTrackCut[1]; + + bool bNumTrackCut = true; + if (tc.fNumTrackCutSwitch) { + bNumTrackCut = nTracksAfter > tc.fNumTrackCut[0] && nTracksAfter < tc.fNumTrackCut[1]; + } // 2-p correlations: for (int i = 0; i < NumTwoPCorrBins; i++) { int k = 2; - mcc.h1 = -(i + 2); - mcc.h2 = i + 2; + mcc.h1 = -(i + 1); + mcc.h2 = i + 1; std::array harmonicsTwoNum = {mcc.h1, mcc.h2}; std::array harmonicsTwoDen = {0, 0}; @@ -1442,15 +1543,16 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam mc.fTwoParticleCorrelationProfiles[eBefore]->Fill(mc.fTwoParticleCorrelationProfiles[eBefore]->GetXaxis()->GetBinCenter(i + 1), ebye.fTwoParticleCorrelationEbye[eBefore][i], wTwoRecursionBefore); mc.fTwoParticleCorrelationHistograms[eBefore][eBefore][i]->Fill(ebye.fTwoParticleCorrelationEbye[eBefore][i]); mc.fTwoParticleCorrelationHistograms[eBefore][eAfter][i]->Fill(ebye.fTwoParticleCorrelationEbye[eBefore][i], wTwoRecursionBefore); + mc.fTwoParticleCorrelationNumTrackProfiles[eBefore]->Fill(mc.fTwoParticleCorrelationNumTrackProfiles[eBefore]->GetXaxis()->GetBinCenter(i + 1), nTracksBefore, ebye.fTwoParticleCorrelationEbye[eBefore][i]); // Find max and min: if (ebye.fTwoParticleCorrelationEbye[eBefore][i] > ebye.fTwoParticleCorrelationMaxEbye[eBefore][i]) { ebye.fTwoParticleCorrelationMaxEbye[eBefore][i] = ebye.fTwoParticleCorrelationEbye[eBefore][i]; - LOGF(info, "max v%d^2 so far = %e", i + 2, ebye.fTwoParticleCorrelationEbye[eBefore][i]); + LOGF(info, "max v%d^2 so far = %e", i + 1, ebye.fTwoParticleCorrelationEbye[eBefore][i]); } if (ebye.fTwoParticleCorrelationEbye[eBefore][i] < ebye.fTwoParticleCorrelationMinEbye[eBefore][i]) { ebye.fTwoParticleCorrelationMinEbye[eBefore][i] = ebye.fTwoParticleCorrelationEbye[eBefore][i]; - LOGF(info, "min v%d^2 so far = %e", i + 2, ebye.fTwoParticleCorrelationEbye[eBefore][i]); + LOGF(info, "min v%d^2 so far = %e", i + 1, ebye.fTwoParticleCorrelationEbye[eBefore][i]); } } else { if (tc.fPrintSwitch) { @@ -1468,15 +1570,16 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam mc.fTwoParticleCorrelationProfiles[eAfter]->Fill(mc.fTwoParticleCorrelationProfiles[eAfter]->GetXaxis()->GetBinCenter(i + 1), ebye.fTwoParticleCorrelationEbye[eAfter][i], wTwoRecursionAfter); mc.fTwoParticleCorrelationHistograms[eAfter][eBefore][i]->Fill(ebye.fTwoParticleCorrelationEbye[eAfter][i]); mc.fTwoParticleCorrelationHistograms[eAfter][eAfter][i]->Fill(ebye.fTwoParticleCorrelationEbye[eAfter][i], wTwoRecursionAfter); + mc.fTwoParticleCorrelationNumTrackProfiles[eAfter]->Fill(mc.fTwoParticleCorrelationNumTrackProfiles[eAfter]->GetXaxis()->GetBinCenter(i + 1), nTracksAfter, ebye.fTwoParticleCorrelationEbye[eAfter][i]); // Find max and min: if (ebye.fTwoParticleCorrelationEbye[eAfter][i] > ebye.fTwoParticleCorrelationMaxEbye[eAfter][i]) { ebye.fTwoParticleCorrelationMaxEbye[eAfter][i] = ebye.fTwoParticleCorrelationEbye[eAfter][i]; - LOGF(info, "max v%d^2 so far = %e", i + 2, ebye.fTwoParticleCorrelationEbye[eAfter][i]); + LOGF(info, "max v%d^2 so far = %e", i + 1, ebye.fTwoParticleCorrelationEbye[eAfter][i]); } if (ebye.fTwoParticleCorrelationEbye[eAfter][i] < ebye.fTwoParticleCorrelationMinEbye[eAfter][i]) { ebye.fTwoParticleCorrelationMinEbye[eAfter][i] = ebye.fTwoParticleCorrelationEbye[eAfter][i]; - LOGF(info, "min v%d^2 so far = %e", i + 2, ebye.fTwoParticleCorrelationEbye[eAfter][i]); + LOGF(info, "min v%d^2 so far = %e", i + 1, ebye.fTwoParticleCorrelationEbye[eAfter][i]); } } else { if (tc.fPrintSwitch) { @@ -1519,6 +1622,70 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam } } + // 3-p correlations: + for (int i = 0; i < NumThreePCorrBins; i++) { + int k = 3; + mcc.h1 = ThreePHarmonicIndex[i][0]; + mcc.h2 = -ThreePHarmonicIndex[i][1]; + mcc.h3 = -ThreePHarmonicIndex[i][2]; + std::array harmonicsThreeNum = {mcc.h1, mcc.h2, mcc.h3}; + std::array harmonicsThreeDen = {0, 0, 0}; + + // Before cut: + TComplex threeRecursionBefore = mccRecursion(3, harmonicsThreeNum, eBefore) / mccRecursion(3, harmonicsThreeDen, eBefore).Re(); + double wThreeRecursionBefore = mccRecursion(3, harmonicsThreeDen, eBefore).Re(); + ebye.fThreeParticleCorrelationEbye[eBefore][i] = threeRecursionBefore.Re(); + + if (nTracksBefore > k - 1 && wThreeRecursionBefore > 0.) { + // Fill vn1vn2vn3 profile and histogram: + mc.fThreeParticleCorrelationProfiles[eBefore]->Fill(mc.fThreeParticleCorrelationProfiles[eBefore]->GetXaxis()->GetBinCenter(i + 1), ebye.fThreeParticleCorrelationEbye[eBefore][i], wThreeRecursionBefore); + mc.fThreeParticleCorrelationHistograms[eBefore][eBefore][i]->Fill(ebye.fThreeParticleCorrelationEbye[eBefore][i]); + mc.fThreeParticleCorrelationHistograms[eBefore][eAfter][i]->Fill(ebye.fThreeParticleCorrelationEbye[eBefore][i], wThreeRecursionBefore); + mc.fThreeParticleCorrelationNumTrackProfiles[eBefore]->Fill(mc.fThreeParticleCorrelationNumTrackProfiles[eBefore]->GetXaxis()->GetBinCenter(i + 1), nTracksBefore, ebye.fThreeParticleCorrelationEbye[eBefore][i]); + + // Find max and min: + if (ebye.fThreeParticleCorrelationEbye[eBefore][i] > ebye.fThreeParticleCorrelationMaxEbye[eBefore][i]) { + ebye.fThreeParticleCorrelationMaxEbye[eBefore][i] = ebye.fThreeParticleCorrelationEbye[eBefore][i]; + LOGF(info, "max v%dv%dv%d so far = %e", mcc.h1, mcc.h2, mcc.h3, ebye.fThreeParticleCorrelationEbye[eBefore][i]); + } + if (ebye.fThreeParticleCorrelationEbye[eBefore][i] < ebye.fThreeParticleCorrelationMinEbye[eBefore][i]) { + ebye.fThreeParticleCorrelationMinEbye[eBefore][i] = ebye.fThreeParticleCorrelationEbye[eBefore][i]; + LOGF(info, "min v%dv%dv%d so far = %e", mcc.h1, mcc.h2, mcc.h3, ebye.fThreeParticleCorrelationEbye[eBefore][i]); + } + } else { + if (tc.fPrintSwitch) { + LOGF(warning, "cent=%f, nTracksBefore = %d, wThreeRecursionBefore = %e", rlCollisionCent, nTracksBefore, wThreeRecursionBefore); + } + } + + // After cut: + TComplex threeRecursionAfter = mccRecursion(3, harmonicsThreeNum, eAfter) / mccRecursion(3, harmonicsThreeDen, eAfter).Re(); + double wThreeRecursionAfter = mccRecursion(3, harmonicsThreeDen, eAfter).Re(); + ebye.fThreeParticleCorrelationEbye[eAfter][i] = threeRecursionAfter.Re(); + + if (nTracksAfter > k - 1 && wThreeRecursionAfter > 0. && bNumTrackCut) { + // Fill vn1vn2vn3 profile and histogram: + mc.fThreeParticleCorrelationProfiles[eAfter]->Fill(mc.fThreeParticleCorrelationProfiles[eAfter]->GetXaxis()->GetBinCenter(i + 1), ebye.fThreeParticleCorrelationEbye[eAfter][i], wThreeRecursionAfter); + mc.fThreeParticleCorrelationHistograms[eAfter][eBefore][i]->Fill(ebye.fThreeParticleCorrelationEbye[eAfter][i]); + mc.fThreeParticleCorrelationHistograms[eAfter][eAfter][i]->Fill(ebye.fThreeParticleCorrelationEbye[eAfter][i], wThreeRecursionAfter); + mc.fThreeParticleCorrelationNumTrackProfiles[eAfter]->Fill(mc.fThreeParticleCorrelationNumTrackProfiles[eAfter]->GetXaxis()->GetBinCenter(i + 1), nTracksAfter, ebye.fThreeParticleCorrelationEbye[eAfter][i]); + + // Find max and min: + if (ebye.fThreeParticleCorrelationEbye[eAfter][i] > ebye.fThreeParticleCorrelationMaxEbye[eAfter][i]) { + ebye.fThreeParticleCorrelationMaxEbye[eAfter][i] = ebye.fThreeParticleCorrelationEbye[eAfter][i]; + LOGF(info, "max v%dv%dv%d so far = %e", mcc.h1, mcc.h2, mcc.h3, ebye.fThreeParticleCorrelationEbye[eAfter][i]); + } + if (ebye.fThreeParticleCorrelationEbye[eAfter][i] < ebye.fThreeParticleCorrelationMinEbye[eAfter][i]) { + ebye.fThreeParticleCorrelationMinEbye[eAfter][i] = ebye.fThreeParticleCorrelationEbye[eAfter][i]; + LOGF(info, "min v%dv%dv%d so far = %e", mcc.h1, mcc.h2, mcc.h3, ebye.fThreeParticleCorrelationEbye[eAfter][i]); + } + } else { + if (tc.fPrintSwitch) { + LOGF(warning, "cent=%f, nTracksAfter = %d, wThreeRecursionAfter = %e", rlCollisionCent, nTracksAfter, wThreeRecursionAfter); + } + } + } + // 4-p correlations: for (int i = 0; i < NumFourPCorrBins; i++) { int k = 4; @@ -1539,6 +1706,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam mc.fFourParticleCorrelationProfiles[eBefore]->Fill(mc.fFourParticleCorrelationProfiles[eBefore]->GetXaxis()->GetBinCenter(i + 1), ebye.fFourParticleCorrelationEbye[eBefore][i], wFourRecursionBefore); mc.fFourParticleCorrelationHistograms[eBefore][eBefore][i]->Fill(ebye.fFourParticleCorrelationEbye[eBefore][i]); mc.fFourParticleCorrelationHistograms[eBefore][eAfter][i]->Fill(ebye.fFourParticleCorrelationEbye[eBefore][i], wFourRecursionBefore); + mc.fFourParticleCorrelationNumTrackProfiles[eBefore]->Fill(mc.fFourParticleCorrelationNumTrackProfiles[eBefore]->GetXaxis()->GetBinCenter(i + 1), nTracksBefore, ebye.fFourParticleCorrelationEbye[eBefore][i]); // Find max and min: if (ebye.fFourParticleCorrelationEbye[eBefore][i] > ebye.fFourParticleCorrelationMaxEbye[eBefore][i]) { @@ -1560,12 +1728,12 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam double wFourRecursionAfter = mccRecursion(4, harmonicsFourDen, eAfter).Re(); ebye.fFourParticleCorrelationEbye[eAfter][i] = fourRecursionAfter.Re(); - // LOGF(info, "nTracksAfter = %d", nTracksAfter); if (nTracksAfter > k - 1 && wFourRecursionAfter > 0. && bNumTrackCut) { // Fill vn^2vm^2 profile and histogram: mc.fFourParticleCorrelationProfiles[eAfter]->Fill(mc.fFourParticleCorrelationProfiles[eAfter]->GetXaxis()->GetBinCenter(i + 1), ebye.fFourParticleCorrelationEbye[eAfter][i], wFourRecursionAfter); mc.fFourParticleCorrelationHistograms[eAfter][eBefore][i]->Fill(ebye.fFourParticleCorrelationEbye[eAfter][i]); mc.fFourParticleCorrelationHistograms[eAfter][eAfter][i]->Fill(ebye.fFourParticleCorrelationEbye[eAfter][i], wFourRecursionAfter); + mc.fFourParticleCorrelationNumTrackProfiles[eAfter]->Fill(mc.fFourParticleCorrelationNumTrackProfiles[eAfter]->GetXaxis()->GetBinCenter(i + 1), nTracksAfter, ebye.fFourParticleCorrelationEbye[eAfter][i]); // Find max and min: if (ebye.fFourParticleCorrelationEbye[eAfter][i] > ebye.fFourParticleCorrelationMaxEbye[eAfter][i]) { @@ -1649,8 +1817,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam } } - template - void bookParticleHistograms(T1 const& lPcBins) + void bookParticleHistograms(int histType, const std::vector>& lPcBins) { const auto& lPtBins = lPcBins[histType]; // define local array and initialize it from an array set in the configurables int nBinsPt = static_cast(lPtBins[0]); @@ -1678,8 +1845,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam } } - template - void bookEventHistograms(T1 const& lEvBins) + void bookEventHistograms(int histType, const std::vector>& lEvBins) { const auto& lCentBins = lEvBins[histType]; // define local array and initialize it from an array set in the configurables int nBinsCent = static_cast(lCentBins[0]); @@ -1693,10 +1859,10 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam std::string nameRecfull; std::string nameSimfull; - if constexpr (histType == eCent) { + if (histType == eCent) { nameRecfull = Form("%s %s distribution for reconstructed events", tc.fCentEstm.c_str(), EventHistNames[histType]); nameSimfull = Form("%s %s distribution for simulated events", tc.fCentEstm.c_str(), EventHistNames[histType]); - } else if constexpr (histType == eMult) { + } else if (histType == eMult) { nameRecfull = Form("%s %s distribution for reconstructed events", tc.fMultEstm.c_str(), EventHistNames[histType]); nameSimfull = Form("%s %s distribution for simulated events", tc.fMultEstm.c_str(), EventHistNames[histType]); } else { @@ -1711,7 +1877,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam } if (doprocessSim || doprocessRecSim) { - if constexpr (histType != eNumContrib && histType != eMult) { + if (histType != eNumContrib && histType != eMult) { ev.fEventHistograms[histType][eSim][ba] = new TH1F(nameSim.c_str(), nameSimfull.c_str(), nBinsCent, minCent, maxCent); ev.fEventHistograms[histType][eSim][ba]->GetXaxis()->SetTitle(EventHistNames[histType]); ev.fEventHistogramsList->Add(ev.fEventHistograms[histType][eSim][ba]); @@ -1720,8 +1886,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam } } - template - void bookQAHistograms(T1 const& lQABins) + void bookQAHistograms(int histType, const std::vector>& lQABins) { int nBinsCentX = 0; float minCentX = 0.; @@ -1753,15 +1918,15 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam std::string name = Form("fHist%s[%s cut]", EventHistNames[histType], BeforeAfterNames[ba]); std::string namefull; - if constexpr (histType == eCent) { + if (histType == eCent) { namefull = Form("Quality assurance of %s %s", tc.fCentEstm.c_str(), EventHistNames[histType]); - } else if constexpr (histType == eMult) { + } else if (histType == eMult) { namefull = Form("Quality assurance of %s %s vs. NContributors", tc.fMultEstm.c_str(), EventHistNames[histType]); } else { namefull = Form("Quality assurance of %s", EventHistNames[histType]); } - if constexpr (histType == eMult) { + if (histType == eMult) { qa.fQAHistograms[histType][ba] = new TH2F(name.c_str(), namefull.c_str(), nBinsCentX, minCentX, maxCentX, nBinsCentY, minCentY, maxCentY); qa.fQAHistograms[histType][ba]->GetYaxis()->SetTitle("NContributors"); qa.fQAHistograms[histType][ba]->GetXaxis()->SetTitle("Reference multiplicity"); @@ -1774,24 +1939,22 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam } } - template - void bookCorrHistograms(T1 const& lCrBins) + void bookCorrHistograms(int histType, const std::vector>& lCrBins) { // (mult)(cent)(NContribMult) offset - constexpr int BoundOffset = histType == eCorrCent ? eCentABBound : histType == eCorrMult ? eMultABBound - : eNumContribMultBound; - + int boundOffset = histType == eCorrCent ? eCentABBound : histType == eCorrMult ? eMultABBound + : eNumContribMultBound; // (mult)(cent)(NContribMult) book functions: - if constexpr (histType == eCorrCent || histType == eCorrMult) { + if (histType == eCorrCent || histType == eCorrMult) { for (int i = 0; i < eEstmCorr_N; i++) { - int indexBound = BoundOffset + i; + int indexBound = boundOffset + i; cr.fCorrBounds[indexBound][0] = new TF1(Form("fCorrUpperBound%s%s", EstmCorrName[i], CorrHistNames[histType]), "[0] * x + [1]"); cr.fCorrBounds[indexBound][1] = new TF1(Form("fCorrLowerBound%s%s", EstmCorrName[i], CorrHistNames[histType]), "[0] * x + [1]"); cr.fCorrBounds[indexBound][0]->SetTitle(Form("%s %s upper bound", EstmCorrName[i], CorrHistNames[histType])); cr.fCorrBounds[indexBound][1]->SetTitle(Form("%s %s lower bound", EstmCorrName[i], CorrHistNames[histType])); } - } else if constexpr (histType == eCorrNumContribMult) { - int indexBound = BoundOffset; + } else if (histType == eCorrNumContribMult) { + int indexBound = boundOffset; cr.fCorrBounds[indexBound][0] = new TF1(Form("fCorrUpperBound%s", CorrHistNames[histType]), "[0] * x + [1]"); cr.fCorrBounds[indexBound][1] = new TF1(Form("fCorrLowerBound%s", CorrHistNames[histType]), "[0] * x + [1]"); cr.fCorrBounds[indexBound][0]->SetTitle(Form("%s upper bound", CorrHistNames[histType])); @@ -1815,13 +1978,13 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam float maxNumContrib = 0.; // (NContribMult) set functions and add to list: - if constexpr (histType == eCorrNumContribMult) { + if (histType == eCorrNumContribMult) { - int indexBins = static_cast(histType) + static_cast(eMultEstm_N); + int indexBins = histType + static_cast(eMultEstm_N); // (NContribMult) get bins: const auto& lMultXBins = lCrBins[indexBins]; - nBinsXMult = static_cast(lMultXBins[0]); + // nBinsXMult = static_cast(lMultXBins[0]); minXMult = lMultXBins[1]; maxXMult = lMultXBins[2]; const auto& lNumContribBins = lCrBins[indexBins - 1]; @@ -1830,7 +1993,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam maxNumContrib = lNumContribBins[2]; // (NContribMult) set functions and add to list: - int indexBound = BoundOffset; + int indexBound = boundOffset; cr.fCorrBounds[indexBound][0]->SetRange(minXMult, maxXMult); cr.fCorrBounds[indexBound][1]->SetRange(minXMult, maxXMult); cr.fCorrBounds[indexBound][0]->SetMinimum(0); @@ -1844,15 +2007,15 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam } // (mult)(cent) Book histograms, set functions and add to list: - if constexpr (histType != eCorrNumContribMult) { + if (histType != eCorrNumContribMult) { for (int ba = 0; ba < eBeforeAfter_N; ba++) { std::string name; std::string namefull = Form("%s correlation %s cut", CorrHistNames[histType], BeforeAfterNames[ba]); int nEstm = 0; - if constexpr (histType == eCorrCent) { + if (histType == eCorrCent) { nEstm = eCentEstm_N; - } else if constexpr (histType == eCorrMult) { + } else if (histType == eCorrMult) { nEstm = eMultEstm_N; } @@ -1861,17 +2024,17 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam for (int i = 0; i < nEstm; i++) { std::string titleX; - if constexpr (histType == eCorrCent) { + if (histType == eCorrCent) { titleX = Form("%s %s", CentEstmNames[i], CorrHistNames[histType]); - } else if constexpr (histType == eCorrMult) { + } else if (histType == eCorrMult) { titleX = Form("%s %s", MultEstmNames[i], CorrHistNames[histType]); } for (int j = i + 1; j < nEstm; j++) { std::string titleY; - if constexpr (histType == eCorrCent) { + if (histType == eCorrCent) { name = Form("fHist%s[%s vs. %s][%s cut]", CorrHistNames[histType], CentEstmNames[i], CentEstmNames[j], BeforeAfterNames[ba]); titleY = Form("%s %s", CentEstmNames[j], CorrHistNames[histType]); @@ -1887,7 +2050,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam // (cent) set functions and add to list: if (static_cast(ba)) { - int indexBound = BoundOffset + nComb; + int indexBound = boundOffset + nComb; cr.fCorrBounds[indexBound][0]->SetRange(minXCent, maxXCent); cr.fCorrBounds[indexBound][1]->SetRange(minXCent, maxXCent); cr.fCorrBounds[indexBound][0]->SetParameters(tc.fCentAllCorrCut[i][j][0], tc.fCentAllCorrCut[i][j][1]); @@ -1903,7 +2066,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam // (cent) book corr 2D histogram: cr.fCorrHistograms[histType][i][j][ba] = new TH2F(name.c_str(), namefull.c_str(), nBinsXCent, minXCent, maxXCent, nBinsYCent, minYCent, maxYCent); - } else if constexpr (histType == eCorrMult) { + } else if (histType == eCorrMult) { name = Form("fHist%s[%s vs. %s][%s cut]", CorrHistNames[histType], MultEstmNames[i], MultEstmNames[j], BeforeAfterNames[ba]); titleY = Form("%s %s", MultEstmNames[j], CorrHistNames[histType]); @@ -1919,7 +2082,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam // (mult) set functions and add to list: if (static_cast(ba)) { - int indexBound = BoundOffset + nComb; + int indexBound = boundOffset + nComb; cr.fCorrBounds[indexBound][0]->SetRange(minXMult, maxXMult); cr.fCorrBounds[indexBound][1]->SetRange(minXMult, maxXMult); cr.fCorrBounds[indexBound][0]->SetParameters(tc.fMultAllCorrCut[i][j][0], tc.fMultAllCorrCut[i][j][1]); @@ -1971,6 +2134,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam tc.fCentCutSwitch = cfCentCutSwitch; tc.fNumContribCutSwitch = cfNumContribCutSwitch; tc.fNumTrackCutSwitch = cfNumTrackCutSwitch; + tc.fOccupancyCutSwitch = cfOccupancyCutSwitch; tc.fCentCorrCutSwitch = cfCentCorrCutSwitch; tc.fMultCorrCutSwitch = cfMultCorrCutSwitch; tc.fNumContribMultCorrCutSwitch = cfNumContribMultCorrCutSwitch; @@ -1987,6 +2151,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam tc.fCentCut = cfCentCut; tc.fNumContribCut = cfNumContribCut; tc.fNumTrackCut = cfNumTrackCut; + tc.fOccupancyCut = cfOccupancyCut; tc.fCentAllCorrCut[0][1] = cfCentABCorrCut; tc.fCentAllCorrCut[0][2] = cfCentACCorrCut; tc.fCentAllCorrCut[1][2] = cfCentBCCorrCut; @@ -2005,7 +2170,10 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam tc.fEtaGap = cfEtaGap; tc.fPtBins = cfPtBins; + tc.fEtaBins = cfEtaBins; tc.fPhiBins = cfPhiBins; + tc.fDCAXYBins = cfDCAXYBins; + tc.fDCAZBins = cfDCAZBins; tc.fCentBins = cfCentBins; tc.fFT0CMultBins = cfFT0CMultBins; @@ -2018,8 +2186,10 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam tc.fNumContribBins = cfNumContribBins; tc.fNumTrackBins = cfNumTrackBins; tc.fTwoParticleCorrBins = cfTwoParticleCorrBins; + tc.fThreeParticleCorrBins = cfThreeParticleCorrBins; tc.fFourParticleCorrBins = cfFourParticleCorrBins; tc.fSixParticleCorrBins = cfSixParticleCorrBins; + tc.fNumTrackTotalBins = cfNumTrackTotalBins; tc.fPrintSwitch = cfPrintSwitch; tc.fFileWithWeights = cfFileWithWeights; @@ -2073,25 +2243,23 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam mc.fMultiparticleCorrelationProfilesList->SetOwner(kTRUE); fBaseList->Add(mc.fMultiparticleCorrelationProfilesList); - std::vector> lPcBins = {tc.fPtBins, tc.fPhiBins}; + std::vector> lPcBins = {tc.fPtBins, tc.fEtaBins, tc.fPhiBins, tc.fDCAXYBins, tc.fDCAZBins}; std::vector> lEvBins = {tc.fCentBins, tc.fMultBins, tc.fVerXBins, tc.fVerYBins, tc.fVerZBins, tc.fNumContribBins, tc.fNumTrackBins}; std::vector> lQABins = {tc.fCentBins, tc.fMultBins, tc.fNumContribBins}; std::vector> lCrBins = {tc.fCentBins, tc.fFT0CMultBins, tc.fFT0MMultBins, tc.fFV0AMultBins, tc.fNumContribBins, tc.fMultBins}; - bookParticleHistograms(lPcBins); - bookParticleHistograms(lPcBins); - bookEventHistograms(lEvBins); - bookEventHistograms(lEvBins); - bookEventHistograms(lEvBins); - bookEventHistograms(lEvBins); - bookEventHistograms(lEvBins); - bookEventHistograms(lEvBins); - bookEventHistograms(lEvBins); - bookQAHistograms(lQABins); - bookQAHistograms(lQABins); - bookCorrHistograms(lCrBins); - bookCorrHistograms(lCrBins); - bookCorrHistograms(lCrBins); + for (int i = 0; i < eParticleHistograms_N; i++) { + bookParticleHistograms(i, lPcBins); + } + for (int i = 0; i < eEventHistograms_N; i++) { + bookEventHistograms(i, lEvBins); + } + for (int i = 0; i < eQAHistograms_N; i++) { + bookQAHistograms(i, lQABins); + } + for (int i = 0; i < eCorrHistograms_N; i++) { + bookCorrHistograms(i, lCrBins); + } wt.fDummyPhiWeightHistogram = new TH1F("fDummyPhiWeightHistogram", "Dummy phi weight histogram", static_cast(tc.fPhiBins[0]), tc.fPhiBins[1], tc.fPhiBins[2]); for (int i = 1; i <= wt.fDummyPhiWeightHistogram->GetNbinsX(); i++) { diff --git a/PWGCF/MultiparticleCorrelations/Tasks/threeParticleCorrelations.cxx b/PWGCF/MultiparticleCorrelations/Tasks/threeParticleCorrelations.cxx index d1aca1a5aab..b78325f9d01 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/threeParticleCorrelations.cxx +++ b/PWGCF/MultiparticleCorrelations/Tasks/threeParticleCorrelations.cxx @@ -182,8 +182,8 @@ struct ThreeParticleCorrelations { using BinningType = ColumnBinningPolicy; using BinningTypeMC = ColumnBinningPolicy; - BinningType collBinning{{confCentBins, confZvtxBins}, true}; - BinningTypeMC collBinningMC{{confCentBins, confZvtxBins}, true}; + BinningType collBinning{{confCentBins, confZvtxBins}}; + BinningTypeMC collBinningMC{{confCentBins, confZvtxBins}}; Pair pairData{collBinning, 5, -1, &cache}; SameKindPair pairMC{collBinningMC, 5, -1, &cache}; diff --git a/PWGCF/TableProducer/CMakeLists.txt b/PWGCF/TableProducer/CMakeLists.txt index 6438d45a5fe..7d893f3a156 100644 --- a/PWGCF/TableProducer/CMakeLists.txt +++ b/PWGCF/TableProducer/CMakeLists.txt @@ -11,7 +11,7 @@ o2physics_add_dpl_workflow(filter-correlations SOURCES filterCorrelations.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(filter-correlations-2prong diff --git a/PWGCF/TableProducer/filter2Prong.cxx b/PWGCF/TableProducer/filter2Prong.cxx index bff2a81ee2c..e33fc8cae64 100644 --- a/PWGCF/TableProducer/filter2Prong.cxx +++ b/PWGCF/TableProducer/filter2Prong.cxx @@ -179,7 +179,6 @@ struct Filter2Prong { ConfigurableAxis axisVertexMix{"axisVertexMix", {7, -7, 7}, "vertex axis for phi event mixing"}; ConfigurableAxis axisMultiplicityMix{"axisMultiplicityMix", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 100.1}, "multiplicity axis for phi event mixing"}; - HfHelper hfHelper; Produces output2ProngTracks; Produces output2ProngTrackmls; @@ -246,7 +245,7 @@ struct Filter2Prong { // look-up the collision id if ((c.hfflag() & (1 << aod::hf_cand_2prong::DecayType::D0ToPiK)) == 0) continue; - if (cfgYMax >= 0.0f && std::abs(hfHelper.yD0(c)) > cfgYMax) + if (cfgYMax >= 0.0f && std::abs(HfHelper::yD0(c)) > cfgYMax) continue; if constexpr (std::experimental::is_detected::value) { if (std::abs(c.flagMcMatchRec()) != o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) @@ -255,7 +254,7 @@ struct Filter2Prong { if (c.isSelD0() > 0) { output2ProngTracks(cfcollisions.begin().globalIndex(), - prongCFId[0], prongCFId[1], c.pt(), c.eta(), c.phi(), hfHelper.invMassD0ToPiK(c), aod::cf2prongtrack::D0ToPiK); + prongCFId[0], prongCFId[1], c.pt(), c.eta(), c.phi(), HfHelper::invMassD0ToPiK(c), aod::cf2prongtrack::D0ToPiK); if constexpr (std::experimental::is_detected::value) { mlvecd.clear(); for (const float val : c.mlProbD0()) @@ -269,7 +268,7 @@ struct Filter2Prong { if (c.isSelD0bar() > 0) { output2ProngTracks(cfcollisions.begin().globalIndex(), - prongCFId[0], prongCFId[1], c.pt(), c.eta(), c.phi(), hfHelper.invMassD0barToKPi(c), + prongCFId[0], prongCFId[1], c.pt(), c.eta(), c.phi(), HfHelper::invMassD0barToKPi(c), c.isSelD0() > 0 ? aod::cf2prongtrack::D0barToKPi : aod::cf2prongtrack::D0barToKPiExclusive); if constexpr (std::experimental::is_detected::value) { mlvecd.clear(); @@ -830,7 +829,7 @@ struct Filter2Prong { }; using BinningTypeDerived = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertexMix, axisMultiplicityMix}, true}; + BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertexMix, axisMultiplicityMix}}; auto tracksTuple = std::make_tuple(cftracks, cftracks); using TA = std::tuple_element<0, decltype(tracksTuple)>::type; using TB = std::tuple_element - 1, decltype(tracksTuple)>::type; diff --git a/PWGCF/TableProducer/filterCorrelations.cxx b/PWGCF/TableProducer/filterCorrelations.cxx index 8f246c14653..7b91bbfe308 100644 --- a/PWGCF/TableProducer/filterCorrelations.cxx +++ b/PWGCF/TableProducer/filterCorrelations.cxx @@ -15,6 +15,7 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/TriggerAliases.h" #include "Common/Core/TableHelper.h" +#include "Common/Core/Zorro.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" @@ -92,7 +93,7 @@ struct FilterCF { O2_DEFINE_CONFIGURABLE(cfgCutMCPt, float, 0.5f, "Minimal pT for particles"); O2_DEFINE_CONFIGURABLE(cfgCutMCEta, float, 0.8f, "Eta range for particles"); O2_DEFINE_CONFIGURABLE(cfgVerbosity, int, 1, "Verbosity level (0 = major, 1 = per collision)"); - O2_DEFINE_CONFIGURABLE(cfgTrigger, int, 7, "Trigger choice: (0 = none, 7 = sel7, 8 = sel8, 9 = sel8 + kNoSameBunchPileup + kIsGoodZvtxFT0vsPV, 10 = sel8 before April, 2024, 11 = sel8 for MC, 12 = sel8 with low occupancy cut, 13 = sel8 + kNoSameBunchPileup + kIsGoodITSLayersAll -- for OO/NeNe) "); + O2_DEFINE_CONFIGURABLE(cfgTrigger, int, 7, "Trigger choice: (0 = none, 7 = sel7, 8 = sel8, 9 = sel8 + kNoSameBunchPileup + kIsGoodZvtxFT0vsPV, 10 = sel8 before April, 2024, 11 = sel8 for MC, 12 = sel8 with low occupancy cut, 13 = sel8 + kNoSameBunchPileup + kIsGoodITSLayersAll -- for OO/NeNe, 14 = fHighTrackMult (pp), 15 = fHighMultFv0 (pp), 16 = fHighTrackMult || fHighMultFv0 (pp))"); O2_DEFINE_CONFIGURABLE(cfgMinOcc, int, 0, "minimum occupancy selection"); O2_DEFINE_CONFIGURABLE(cfgMaxOcc, int, 3000, "maximum occupancy selection"); O2_DEFINE_CONFIGURABLE(cfgCollisionFlags, uint16_t, aod::collision::CollisionFlagsRun2::Run2VertexerTracks, "Request collision flags if non-zero (0 = off, 1 = Run2VertexerTracks)"); @@ -135,6 +136,9 @@ struct FilterCF { HistogramRegistry registrytrackQA{"TrackQA", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + Zorro zorro; + int zorroRun = 0; + Produces outputCollisions; Produces outputCollisionsExtra; Produces outputTracks; @@ -194,12 +198,13 @@ struct FilterCF { return; } localMultiplicityEfficiency.reset(dynamic_cast(efficiency->Clone())); - } else { - ccdb->setURL("http://alice-ccdb.cern.ch"); - ccdb->setCaching(true); - ccdb->setLocalObjectValidityChecking(); } } + if ((cfgTrigger >= 14 && cfgTrigger <= 16) || (!cfgEfficiencyMultiplicity.value.empty() && cfgLocalEfficiency == 0)) { // o2-linter: disable=magic-number (documented legacy trigger-selection code) + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + } if (doprocessTrackQA) { registrytrackQA.add("zvtx", "Z Vertex position; posz (cm); Events", HistType::kTH1F, {{100, -12, 12}}); registrytrackQA.add("eta", "eta distribution; eta; arb. units", HistType::kTH1F, {{100, -2, 2}}); @@ -249,6 +254,26 @@ struct FilterCF { if (cfgTrigger == 13) { // relevant for pO/OO/NeNe, recommended by Physics Board on 27.01.2026; o2-linter: disable=magic-number (documented legacy trigger-selection code) return isMultSelected && collision.sel8() && collision.selection_bit(aod::evsel::kNoSameBunchPileup) && collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV); } + + // Zorro-based trigger selection + if (cfgTrigger >= 14 && cfgTrigger <= 16) { // o2-linter: disable=magic-number (documented legacy trigger-selection code) + auto bc = collision.template bc_as(); + if (zorroRun != bc.runNumber()) { + std::string zorroMask; + if (cfgTrigger == 14) { // High-multiplicity pp trigger based on tracks (PWGMM/multFilter.cxx); o2-linter: disable=magic-number (documented legacy trigger-selection code) + zorroMask = "fHighTrackMult"; + } + if (cfgTrigger == 15) { // High-multiplicity pp trigger based on FV0 amplitude (PWGMM/multFilter.cxx); o2-linter: disable=magic-number (documented legacy trigger-selection code) + zorroMask = "fHighMultFv0"; + } + if (cfgTrigger == 16) { // High-multiplicity pp trigger based on tracks or FV0 amplitude (PWGMM/multFilter.cxx); o2-linter: disable=magic-number (documented legacy trigger-selection code) + zorroMask = "fHighTrackMult,fHighMultFv0"; + } + zorro.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), zorroMask); + zorroRun = bc.runNumber(); + } + return isMultSelected && zorro.isSelected(bc.globalBC(), 0); // Set BC threshold to zero to not admit nearby low-multiplicity collisions + } return false; } @@ -412,6 +437,9 @@ struct FilterCF { template using HasMultTables = decltype(std::declval().multNTracksPV()); + template + using HasNMPI = decltype(std::declval().nMPI()); + /// \brief Templetized process data for a given collision and its associated tracks /// \param collision The collision object containing information about the collision /// \param tracks The collection of tracks associated with the collision @@ -488,7 +516,7 @@ struct FilterCF { } PROCESS_SWITCH(FilterCF, processDataMults, "Process data with multiplicity sets", false); - void processTrackQA(soa::Filtered>::iterator const& collision, soa::Filtered> const& tracks) + void processTrackQA(soa::Filtered>::iterator const& collision, aod::BCsWithTimestamps const&, soa::Filtered> const& tracks) { if (!keepCollision(collision)) { return; @@ -609,7 +637,9 @@ struct FilterCF { } } outputMcCollisions(mcCollision.posZ(), multiplicity); - outputMcCollisionExtras(mcCollision.nMPI()); + if constexpr (std::experimental::is_detected::value) { + outputMcCollisionExtras(mcCollision.nMPI()); + } } // PASS 2 on collisions: store collisions and tracks @@ -682,7 +712,7 @@ struct FilterCF { Preslice perMcCollision = aod::mcparticle::mcCollisionId; Preslice perCollision = aod::track::collisionId; using McCollisionsWithHepMC = soa::Join; - void processMC(McCollisionsWithHepMC const& mcCollisions, aod::McParticles const& allParticles, + void processMC(aod::McCollisions const& mcCollisions, aod::McParticles const& allParticles, soa::Join const& allCollisions, soa::Filtered> const& tracks, aod::BCsWithTimestamps const& bcs) @@ -691,8 +721,17 @@ struct FilterCF { } PROCESS_SWITCH(FilterCF, processMC, "Process MC", false); + void processMCWithHepMC(McCollisionsWithHepMC const& mcCollisions, aod::McParticles const& allParticles, + soa::Join const& allCollisions, + soa::Filtered> const& tracks, + aod::BCsWithTimestamps const& bcs) + { + processMCT(mcCollisions, allParticles, allCollisions, tracks, bcs); + } + PROCESS_SWITCH(FilterCF, processMCWithHepMC, "Process MC with HepMC cross-section (nMPI)", false); + // NOTE not filtering collisions here because in that case there can be tracks referring to MC particles which are not part of the selected MC collisions - void processMCPid(McCollisionsWithHepMC const& mcCollisions, aod::McParticles const& allParticles, + void processMCPid(aod::McCollisions const& mcCollisions, aod::McParticles const& allParticles, soa::Join const& allCollisions, soa::Filtered> const& tracks, aod::BCsWithTimestamps const& bcs) @@ -701,7 +740,16 @@ struct FilterCF { } PROCESS_SWITCH(FilterCF, processMCPid, "Process MC with PID", false); - void processMCMults(McCollisionsWithHepMC const& mcCollisions, aod::McParticles const& allParticles, + void processMCPidWithHepMC(McCollisionsWithHepMC const& mcCollisions, aod::McParticles const& allParticles, + soa::Join const& allCollisions, + soa::Filtered> const& tracks, + aod::BCsWithTimestamps const& bcs) + { + processMCT(mcCollisions, allParticles, allCollisions, tracks, bcs); + } + PROCESS_SWITCH(FilterCF, processMCPidWithHepMC, "Process MC with PID and HepMC cross-section (nMPI)", false); + + void processMCMults(aod::McCollisions const& mcCollisions, aod::McParticles const& allParticles, soa::Join const& allCollisions, soa::Filtered> const& tracks, aod::BCsWithTimestamps const& bcs) @@ -711,7 +759,18 @@ struct FilterCF { PROCESS_SWITCH(FilterCF, processMCMults, "Process MC with multiplicity sets", false); - void processMCGen(McCollisionsWithHepMC::iterator const& mcCollision, aod::McParticles const& particles) + void processMCMultsWithHepMC(McCollisionsWithHepMC const& mcCollisions, aod::McParticles const& allParticles, + soa::Join const& allCollisions, + soa::Filtered> const& tracks, + aod::BCsWithTimestamps const& bcs) + { + processMCT(mcCollisions, allParticles, allCollisions, tracks, bcs); + } + PROCESS_SWITCH(FilterCF, processMCMultsWithHepMC, "Process MC with multiplicity sets and HepMC cross-section (nMPI)", false); + + /// \brief Templated MCGen processing; stores nMPI only when the MC collision carries the HepMC cross-section table + template + void processMCGenT(const TMC& mcCollision, aod::McParticles const& particles) { float multiplicity = 0.0f; for (const auto& particle : particles) { @@ -731,9 +790,22 @@ struct FilterCF { sign, particle.pdgCode(), particle.flags()); } outputMcCollisions(mcCollision.posZ(), multiplicity); - outputMcCollisionExtras(mcCollision.nMPI()); + if constexpr (std::experimental::is_detected::value) { + outputMcCollisionExtras(mcCollision.nMPI()); + } + } + + void processMCGen(aod::McCollisions::iterator const& mcCollision, aod::McParticles const& particles) + { + processMCGenT(mcCollision, particles); } PROCESS_SWITCH(FilterCF, processMCGen, "Process MCGen", false); + + void processMCGenWithHepMC(McCollisionsWithHepMC::iterator const& mcCollision, aod::McParticles const& particles) + { + processMCGenT(mcCollision, particles); + } + PROCESS_SWITCH(FilterCF, processMCGenWithHepMC, "Process MCGen with HepMC cross-section (nMPI)", false); }; struct MultiplicitySelector { diff --git a/PWGCF/Tasks/correlations.cxx b/PWGCF/Tasks/correlations.cxx index 46d372b88ae..0a5c1d4ed6b 100644 --- a/PWGCF/Tasks/correlations.cxx +++ b/PWGCF/Tasks/correlations.cxx @@ -61,6 +61,7 @@ #include #include #include +#include #include #include #include @@ -105,7 +106,7 @@ struct CorrelationTask { O2_DEFINE_CONFIGURABLE(cfgTwoTrackCut, float, -1, "Two track cut: -1 = off; >0 otherwise distance value (suggested: 0.02)"); O2_DEFINE_CONFIGURABLE(cfgTwoTrackCutMinRadius, float, 0.8f, "Two track cut: radius in m from which two track cuts are applied"); O2_DEFINE_CONFIGURABLE(cfgLocalEfficiency, int, 0, "0 = OFF and 1 = ON for local efficiency"); - O2_DEFINE_CONFIGURABLE(cfgDropStepRECO, bool, false, "choice to drop step RECO if efficiency correction is used") + O2_DEFINE_CONFIGURABLE(cfgDropStepRECO, bool, false, "choice to drop step RECO if efficiency correction is used"); O2_DEFINE_CONFIGURABLE(cfgCentBinsForMC, int, 0, "0 = OFF and 1 = ON for data like multiplicity/centrality bins for MC steps"); O2_DEFINE_CONFIGURABLE(cfgRequireRecoCollision, int, 1, "0 = all generated collisions; 1 = only generated collisions with exactly 1 reconstructed collision; 2 = select reconstructed collision with largest number of contributors per generated collision") O2_DEFINE_CONFIGURABLE(cfgTrackBitMask, uint16_t, 0, "BitMask for track selection systematics; refer to the enum TrackSelectionCuts in filtering task"); @@ -118,6 +119,7 @@ struct CorrelationTask { O2_DEFINE_CONFIGURABLE(cfgEfficiencyTrigger, std::string, "", "CCDB path to efficiency object for trigger particles") O2_DEFINE_CONFIGURABLE(cfgEfficiencyAssociated, std::string, "", "CCDB path to efficiency object for associated particles") O2_DEFINE_CONFIGURABLE(cfgUseRun3MagField, bool, false, "false: Run 2 GRPObject; true: Run 3 GRPMagField") + O2_DEFINE_CONFIGURABLE(cfgMultWeight, std::string, "", "Multiplicity/Centrality weight") O2_DEFINE_CONFIGURABLE(cfgNoMixedEvents, int, 5, "Number of mixed events per event") O2_DEFINE_CONFIGURABLE(cfgRejectMixedPhiProngEvents, bool, true, "Reject associated hadrons from either mixed-phi prong event") @@ -132,6 +134,8 @@ struct CorrelationTask { O2_DEFINE_CONFIGURABLE(cfgPtDepMLbkg, std::vector, {}, "pT interval for ML training") O2_DEFINE_CONFIGURABLE(cfgPtCentDepMLbkgSel, std::vector, {}, "Bkg ML selection") O2_DEFINE_CONFIGURABLE(cfgPtCentDepMLpromptSel, std::vector, {}, "Prompt ML selection") + O2_DEFINE_CONFIGURABLE(cfgPtCentDepMLnonpromptSel, std::vector, {}, "Non-prompt ML selection (maximum cut on the non-prompt score)") + O2_DEFINE_CONFIGURABLE(cfgMcClassSel, int, -1, "MC class for 2-prong efficiency: -1=auto(promptSel empty?all:prompt), 0=all, 1=prompt, 2=non-prompt") std::unordered_map cfTrackToCFCollision; @@ -183,6 +187,7 @@ struct CorrelationTask { bool mPairCuts = false; THn* mEfficiencyTrigger = nullptr; THn* mEfficiencyAssociated = nullptr; + TH1* mMultWeight = nullptr; bool efficiencyLoaded = false; } cfg; @@ -220,6 +225,14 @@ struct CorrelationTask { if (!cfgPtCentDepMLpromptSel->empty() && cfgPtCentDepMLpromptSel->size() != cfgPtDepMLbkg->size()) { LOGF(fatal, "cfgPtDepMLbkg and cfgPtCentDepMLpromptSel must be same size."); } + if (!cfgPtCentDepMLnonpromptSel->empty() && cfgPtCentDepMLnonpromptSel->size() != cfgPtDepMLbkg->size()) + LOGF(fatal, "cfgPtDepMLbkg and cfgPtCentDepMLnonpromptSel must be same size."); + if (cfgPtCentDepMLpromptSel->size() > 0) + LOGF(info, "Prompt selection scores in use."); + else + LOGF(info, "Using inclusive selections."); + if (cfgPtCentDepMLnonpromptSel->size() > 0) + LOGF(info, "Non-prompt selection scores in use."); } registry.add("yields", "multiplicity/centrality vs pT vs eta", {HistType::kTH3F, {{100, 0, 100, "/multiplicity/centrality"}, {40, 0, 20, "p_{T}"}, {100, -2, 2, "#eta"}}}); registry.add("etaphi", "multiplicity/centrality vs eta vs phi", {HistType::kTH3F, {{100, 0, 100, "multiplicity/centrality"}, {100, -2, 2, "#eta"}, {200, 0, o2::constants::math::TwoPI, "#varphi"}}}); @@ -229,6 +242,7 @@ struct CorrelationTask { const AxisSpec& a = AxisSpec(axisInvMass); AxisSpec axisSpecMass = {1000, a.binEdges[0], a.binEdges[a.getNbins()]}; registry.add("invMass", "2-prong invariant mass (GeV/c^2)", {HistType::kTHnSparseF, {axisSpecMass, axisPtTrigger, axisMultiplicity, axisVertex}}); + registry.add("invMassCorrected", "2-prong invariant mass with efficiency correction (GeV/c^2)", {HistType::kTHnSparseF, {axisSpecMass, axisPtTrigger, axisMultiplicity, axisVertex}}); if (doprocessSame2Prong2Prong || doprocessSame2Prong2ProngML) { registry.add("invMassTwoPart", "2D 2-prong invariant mass (GeV/c^2)", {HistType::kTHnSparseF, {axisSpecMass, axisSpecMass, axisPtTrigger, axisPtAssoc, axisMultiplicity}}); registry.add("invMassTwoPartDPhi", "2D 2-prong invariant mass (GeV/c^2)", {HistType::kTHnSparseF, {axisSpecMass, axisSpecMass, axisPtTrigger, axisPtAssoc, axisDeltaPhi}}); @@ -395,12 +409,16 @@ struct CorrelationTask { return (track.decay() != aod::cf2prongtrack::D0ToPiK || (track.mlProbD0()[0] < cfgPtCentDepMLbkgSel->at(idx) && // not background (cfgPtCentDepMLpromptSel->empty() || - track.mlProbD0()[1] > cfgPtCentDepMLpromptSel->at(idx)))) && // prompt + track.mlProbD0()[1] > cfgPtCentDepMLpromptSel->at(idx)) && // prompt + (cfgPtCentDepMLnonpromptSel->empty() || + track.mlProbD0()[2] < cfgPtCentDepMLnonpromptSel->at(idx)))) && // not non-prompt ((track.decay() != aod::cf2prongtrack::D0barToKPi && track.decay() != aod::cf2prongtrack::D0barToKPiExclusive) || (track.mlProbD0bar()[0] < cfgPtCentDepMLbkgSel->at(idx) && // not background (cfgPtCentDepMLpromptSel->empty() || - track.mlProbD0bar()[1] > cfgPtCentDepMLpromptSel->at(idx)))); // prompt + track.mlProbD0bar()[1] > cfgPtCentDepMLpromptSel->at(idx)) && // prompt + (cfgPtCentDepMLnonpromptSel->empty() || + track.mlProbD0bar()[2] < cfgPtCentDepMLnonpromptSel->at(idx)))); // not non-prompt } template @@ -428,6 +446,8 @@ struct CorrelationTask { using HasInvMass = decltype(std::declval().invMass()); template using HasPDGCode = decltype(std::declval().pdgCode()); + template + using HasTrackType = decltype(std::declval().trackType()); template void fillQA(const TCollision& collision, float multiplicity, float posZ, const TTracks1& tracks1, const TTracks2& tracks2) @@ -442,7 +462,15 @@ struct CorrelationTask { continue; } } + float triggerWeight = 1.0f; + if (cfg.mMultWeight) { + triggerWeight *= cfg.mMultWeight->GetBinContent(cfg.mMultWeight->FindBin(multiplicity)); + } + if (cfg.mEfficiencyTrigger) { + triggerWeight *= getEfficiencyCorrection(cfg.mEfficiencyTrigger, track1.eta(), track1.pt(), multiplicity, collision.posZ()); + } registry.fill(HIST("invMass"), track1.invMass(), track1.pt(), multiplicity, posZ); + registry.fill(HIST("invMassCorrected"), track1.invMass(), track1.pt(), multiplicity, posZ, triggerWeight); for (const auto& track2 : tracks2) { if constexpr (std::experimental::is_detected::value && std::experimental::is_detected::value) { if (doprocessSame2Prong2Prong || doprocessMixed2Prong2Prong || doprocessSame2Prong2ProngML || doprocessMixed2Prong2ProngML) { @@ -628,6 +656,9 @@ struct CorrelationTask { } } + static std::set> duplicates; // workaround for old derived datasets involving 2prongs when cfgDecayParticleMask = 0 + duplicates.clear(); + for (const auto& track1 : tracks1) { // LOGF(info, "Track %f | %f | %f %d %d", track1.eta(), track1.phi(), track1.pt(), track1.isGlobalTrack(), track1.isGlobalTrackSDD()); @@ -637,6 +668,15 @@ struct CorrelationTask { } } + if constexpr (std::experimental::is_detected::value) { + if (cfgDecayParticleMask == 0) { + auto a = std::make_tuple(track1.cfTrackProng0Id(), track1.cfTrackProng1Id()); // workaround allows for use of D0bar in old derived datasets + if (duplicates.contains(a)) + continue; + duplicates.insert(a); + } + } + // sign check and PDG code special cases if constexpr (std::experimental::is_detected::value) { // If the MC trigger particle is on the trigger PDG code list, we will accept them regardless of their charge. @@ -691,6 +731,9 @@ struct CorrelationTask { float triggerWeight = eventWeight; if constexpr (step == CorrelationContainer::kCFStepCorrected) { + if (cfg.mMultWeight) { + triggerWeight *= cfg.mMultWeight->GetBinContent(cfg.mMultWeight->FindBin(multiplicity)); + } if (cfg.mEfficiencyTrigger) { triggerWeight *= getEfficiencyCorrection(cfg.mEfficiencyTrigger, track1.eta(), track1.pt(), multiplicity, posZ); } @@ -777,6 +820,7 @@ struct CorrelationTask { // If decay attributes are found for the second track/particle, we assume 2p-2p correlation if constexpr (std::experimental::is_detected::value) { if ((((track2.mcDecay()) != aod::cf2prongtrack::D0ToPiK) && ((track2.mcDecay()) != aod::cf2prongtrack::D0barToKPiExclusive)) || (!cfgPtCentDepMLpromptSel->empty() && (track2.decay() & aod::cf2prongmcpart::Prompt) == 0)) { + continue; } } else if constexpr (std::experimental::is_detected::value) { @@ -929,6 +973,11 @@ struct CorrelationTask { } LOGF(info, "Loaded efficiency histogram for associated particles from %s (%p)", cfgEfficiencyAssociated.value.c_str(), static_cast(cfg.mEfficiencyAssociated)); } + if (cfgMultWeight.value.empty() == false) { + TFile* pf = TFile::Open(cfgMultWeight.value.c_str(), "READ"); + cfg.mMultWeight = reinterpret_cast(pf->Get("hWeight")); + LOGF(info, "Loaded multiplicity weight histogram for associated particles from %s", cfgMultWeight.value.c_str()); + } cfg.efficiencyLoaded = true; } @@ -970,7 +1019,7 @@ struct CorrelationTask { void processSameDerivedT(CollType const& collision, TTracks1 const& tracks1, TTracks2 const& tracks2) { using BinningTypeDerived = ColumnBinningPolicy; - BinningTypeDerived configurableBinningDerived{{axisVertex, axisMultiplicity}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + BinningTypeDerived configurableBinningDerived{{axisVertex, axisMultiplicity}}; if (cfgVerbosity > 0) { LOGF(info, "processSameDerivedT: Tracks for collision: %d/%d | Vertex: %.1f | Multiplicity/Centrality: %.1f", tracks1.size(), tracks2.size(), collision.posZ(), collision.multiplicity()); } @@ -998,6 +1047,7 @@ struct CorrelationTask { same->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); fillCorrelations(same, tracks1, tracks2, multiplicity, collision.posZ(), field, 1.0f); } + if (hasEfficiency) { same->fillEvent(multiplicity, CorrelationContainer::kCFStepCorrected); fillCorrelations(same, tracks1, tracks2, multiplicity, collision.posZ(), field, 1.0f); @@ -1049,7 +1099,7 @@ struct CorrelationTask { // NOTE legacy function for O2 integration tests. Full version needs derived data // Strictly upper categorised collisions, for cfgNoMixedEvents combinations per bin, skipping those in entry -1 - BinningTypeAOD configurableBinning{{axisVertex, axisMultiplicity}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + BinningTypeAOD configurableBinning{{axisVertex, axisMultiplicity}}; auto tracksTuple = std::make_tuple(tracks); SameKindPair pairs{configurableBinning, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1102,7 +1152,7 @@ struct CorrelationTask { }; using BinningTypeDerived = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertex, axisMultiplicity}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertex, axisMultiplicity}}; // Strictly upper categorised collisions, for cfgNoMixedEvents combinations per bin, skipping those in entry -1 auto tracksTuple = std::make_tuple(std::forward(tracks)...); using TA = std::tuple_element<0, decltype(tracksTuple)>::type; @@ -1363,6 +1413,19 @@ struct CorrelationTask { } // Primaries p2indexCache.clear(); + std::set signalMC; // mc global index cache + std::set reflectionMC; // mc global index cache + // Resolve the MC class to use for the efficiency denominator/numerator. + // cfgMcClassSel: -1=auto (promptSel empty -> all, else prompt), 0=all, 1=prompt, 2=non-prompt. + int mcClass = cfgMcClassSel.value; + if (mcClass < 0) { + mcClass = cfgPtCentDepMLpromptSel->empty() ? 0 : 1; + } + auto mcClassSel = [&](const auto& mp) -> bool { + bool isP = (mp.decay() & aod::cf2prongmcpart::Prompt) != 0; + bool isNP = (mp.decay() & aod::cf2prongmcpart::NonPrompt) != 0; + return (mcClass == 0) || (mcClass == 1 && isP) || (mcClass == 2 && isNP); + }; for (const auto& mcParticle : mcParticles) { if (std::find(cfgMcTriggerPDGs->begin(), cfgMcTriggerPDGs->end(), mcParticle.pdgCode()) != cfgMcTriggerPDGs->end()) { if ((mcParticle.mcDecay() != aod::cf2prongtrack::D0ToPiK) && (mcParticle.mcDecay() != aod::cf2prongtrack::D0barToKPiExclusive)) { @@ -1372,9 +1435,8 @@ struct CorrelationTask { continue; // daughters not found } if constexpr (!reflectionSpec) { - if (cfgPtCentDepMLpromptSel->empty() || (mcParticle.decay() & aod::cf2prongmcpart::Prompt) != 0) { + if (mcClassSel(mcParticle)) same->getTrackHistEfficiency()->Fill(CorrelationContainer::MC, mcParticle.eta(), mcParticle.pt(), 4, multiplicity, mcCollision.posZ()); - } } p2indexCache.push_back(mcParticle.globalIndex()); } @@ -1409,17 +1471,18 @@ struct CorrelationTask { return false; } const auto& mcParticle = mcParticles.iteratorAt(*m - mcParticles.begin().globalIndex()); + // check MC class and return true if not the target class (wont be a fake) + if (!mcClassSel(mcParticle)) + return true; // a valid candidate but not the target class. Return true so we won't register a fake. if constexpr (!reflectionSpec) { - if (!cfgPtCentDepMLpromptSel->empty() && (mcParticle.decay() & aod::cf2prongmcpart::Prompt) == 0) { - return true; // a valid candidate but not a prompt - } same->getTrackHistEfficiency()->Fill(CorrelationContainer::RecoPrimaries, mcParticle.eta(), mcParticle.pt(), 4, multiplicity, mcCollision.posZ()); } else { - if ((mcParticle.mcDecay() == aod::cf2prongtrack::D0barToKPiExclusive && (p2track.decay() == aod::cf2prongtrack::D0barToKPiExclusive || p2track.decay() == aod::cf2prongtrack::D0barToKPi)) || - (mcParticle.mcDecay() == aod::cf2prongtrack::D0ToPiK && p2track.decay() == aod::cf2prongtrack::D0ToPiK)) { + if (!signalMC.contains(mcParticle.globalIndex()) && ((mcParticle.mcDecay() == aod::cf2prongtrack::D0barToKPiExclusive && (p2track.decay() == aod::cf2prongtrack::D0barToKPiExclusive || p2track.decay() == aod::cf2prongtrack::D0barToKPi)) || (mcParticle.mcDecay() == aod::cf2prongtrack::D0ToPiK && p2track.decay() == aod::cf2prongtrack::D0ToPiK))) { registry.fill(HIST("invMassSignal"), p2track.invMass(), p2track.pt(), multiplicity); - } else { // one particle may be filled into both histograms through duplicates + signalMC.insert(mcParticle.globalIndex()); + } else if (!reflectionMC.contains(mcParticle.globalIndex()) && !signalMC.contains(mcParticle.globalIndex()) && ((mcParticle.mcDecay() == aod::cf2prongtrack::D0barToKPiExclusive && p2track.decay() == aod::cf2prongtrack::D0ToPiK) || (mcParticle.mcDecay() == aod::cf2prongtrack::D0ToPiK && (p2track.decay() == aod::cf2prongtrack::D0barToKPi || p2track.decay() == aod::cf2prongtrack::D0barToKPiExclusive)))) { registry.fill(HIST("invMassReflected"), p2track.invMass(), p2track.pt(), multiplicity); + reflectionMC.insert(mcParticle.globalIndex()); } } return true; @@ -1544,7 +1607,7 @@ struct CorrelationTask { }; using BinningTypeMCDerived = FlexibleBinningPolicy, aod::mccollision::PosZ, decltype(getMultiplicity)>; - BinningTypeMCDerived configurableBinning{{getMultiplicity}, {axisVertex, axisMultiplicity}, true}; + BinningTypeMCDerived configurableBinning{{getMultiplicity}, {axisVertex, axisMultiplicity}}; // Strictly upper categorised collisions, for cfgNoMixedEvents combinations per bin, skipping those in entry -1 auto tuple = std::make_tuple(std::forward(particles)...); diff --git a/PWGCF/Tasks/dptDptCorrelations.cxx b/PWGCF/Tasks/dptDptCorrelations.cxx index 99602575f1d..9f7713c221c 100644 --- a/PWGCF/Tasks/dptDptCorrelations.cxx +++ b/PWGCF/Tasks/dptDptCorrelations.cxx @@ -1489,7 +1489,7 @@ struct DptDptCorrelations { std::vector multBinsEdges{VARIABLE_WIDTH, 0.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0, 100.1f}; SliceCache cache; using BinningZVtxMultRec = ColumnBinningPolicy; - BinningZVtxMultRec bindingOnVtxAndMultRec{{vtxBinsEdges, multBinsEdges}, true}; // true is for 'ignore overflows' (true by default) + BinningZVtxMultRec bindingOnVtxAndMultRec{{vtxBinsEdges, multBinsEdges}}; static constexpr int KNoOfLoggingCombinations = 10; void processRecLevelMixed(soa::Filtered const& collisions, aod::BCsWithTimestamps const&, soa::Filtered const& tracks) @@ -1569,7 +1569,7 @@ struct DptDptCorrelations { PROCESS_SWITCH(DptDptCorrelations, processRecLevelMixedNotStored, "Process reco level mixed events correlations for not stored derived data", false); using BinningZVtxMultGen = ColumnBinningPolicy; - BinningZVtxMultGen bindingOnVtxAndMultGen{{vtxBinsEdges, multBinsEdges}, true}; // true is for 'ignore overflows' (true by default) + BinningZVtxMultGen bindingOnVtxAndMultGen{{vtxBinsEdges, multBinsEdges}}; void processGenLevelMixed(soa::Filtered const& collisions, soa::Filtered const& tracks) { diff --git a/PWGCF/Tutorial/CFTutorialTask5.cxx b/PWGCF/Tutorial/CFTutorialTask5.cxx index 065fd3da043..6e5d78cdf1c 100644 --- a/PWGCF/Tutorial/CFTutorialTask5.cxx +++ b/PWGCF/Tutorial/CFTutorialTask5.cxx @@ -150,7 +150,7 @@ struct CFTutorialTask5 { void processMixed(MyFilteredCollisions const& colls, MyFilteredTracks const&) { - BinningType colBinning{{ConfVtxBins, ConfMultBins}, true}; + BinningType colBinning{{ConfVtxBins, ConfMultBins}}; for (auto& [collision1, collision2] : soa::selfCombinations(colBinning, 5, -1, colls, colls)) { auto groupPositive = positive->sliceByCached(aod::track::collisionId, collision1.globalIndex(), cache); auto groupNegative = negative->sliceByCached(aod::track::collisionId, collision2.globalIndex(), cache); @@ -175,7 +175,7 @@ struct CFTutorialTask5 { void processMixedEventInterface(MyFilteredCollisions& colls, MyFilteredTracks& tracks) { auto tracksTuple = std::make_tuple(tracks); - BinningType colBinning{{ConfVtxBins, ConfMultBins}, true}; + BinningType colBinning{{ConfVtxBins, ConfMultBins}}; SameKindPair pair{colBinning, 5, -1, colls, tracksTuple, &cache}; for (auto& [c1, tracks1, c2, tracks2] : pair) { Partition groupPositive = aod::track::signed1Pt > ConfChargeCut; diff --git a/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.cxx b/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.cxx index 3d8090a56c1..3181b07d8b5 100644 --- a/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.cxx +++ b/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.cxx @@ -474,8 +474,6 @@ void EventSelectionFilterAndAnalysis::StoreArmedMask() } mOptArmedMask.push_back(optMask); optMask = 0UL; - if (mTriggerSelection != nullptr) { - } if (mZVertex != nullptr) { armedBrick(mZVertex); } diff --git a/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.h b/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.h index 5030f011be7..28ca101cbf4 100644 --- a/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.h +++ b/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.h @@ -84,6 +84,7 @@ class EventSelectionFilterAndAnalysis : public SelectionFilterAndAnalysis private: struct ComplexBrickHelper { + virtual ~ComplexBrickHelper() = default; CutBrick* mBrick = nullptr; int mDefaultEstimatorIndex = -1; std::vector mAlternateEstimatorIndex = std::vector{}; @@ -95,15 +96,15 @@ class EventSelectionFilterAndAnalysis : public SelectionFilterAndAnalysis void armedBrick(uint64_t&, uint64_t&, uint64_t&, int&); }; struct MultiplicityBrick : public ComplexBrickHelper { - virtual void initialize(); + void initialize() override; std::vector GetMultiplicities(); int GetArmedIndex() { return mBrick->getArmedIndex(); } - virtual bool Filter(uint64_t& mask, int& bit, CutBrick* brick, int index); + bool Filter(uint64_t& mask, int& bit, CutBrick* brick, int index) override; }; struct PileUpRejBrick : public ComplexBrickHelper { - virtual void initialize(); + void initialize() override; std::vector mIndepVar = std::vector{}; - virtual bool Filter(uint64_t& mask, int& bit, CutBrick* brick, int index); + bool Filter(uint64_t& mask, int& bit, CutBrick* brick, int index) override; }; static bool filterBrickValue(uint64_t& mask, int& bit, CutBrick* brick, float value); @@ -111,7 +112,7 @@ class EventSelectionFilterAndAnalysis : public SelectionFilterAndAnalysis template void StoreMultiplicities(CollisionToFilter const&, AssociatedTracks const&); int CalculateMaskLength() override; - virtual void StoreArmedMask() override; + void StoreArmedMask() override; std::vector*> mBFieldSelection; //! the magnetic field selection cuts MultiplicityBrick* mMultiplicityClasses; //! the multiplicity classes cuts @@ -119,7 +120,7 @@ class EventSelectionFilterAndAnalysis : public SelectionFilterAndAnalysis CutBrick* mZVertex; //! the z vertex selection cuts PileUpRejBrick* mPileUpRejection; //! the pile-up rejection criteria - ClassDef(EventSelectionFilterAndAnalysis, 1) + ClassDefOverride(EventSelectionFilterAndAnalysis, 1) }; /// \brief Stores the different multiplicities needed for proper collision filtering @@ -141,7 +142,7 @@ inline void EventSelectionFilterAndAnalysis::StoreMultiplicities(CollisionToFilt /* we require tracks with pT > 0.15 and |eta| < 0.8 and additionally the TPCout flag */ /* for the time being there is not TPCout flag, we just consider hasTPC */ for (auto track : tracks) { - if (track.pt() > 0.15 and abs(track.eta()) < 0.8 and track.hasTPC()) { + if (track.pt() > 0.15 and std::abs(track.eta()) < 0.8 and track.hasTPC()) { ntracksTPCout++; } } @@ -231,8 +232,6 @@ inline uint64_t EventSelectionFilterAndAnalysis::Filter(CollisionToFilter const& bool acc = mMultiplicityClasses->ComplexBrickHelper::Filter(selectedMask, bit); acceptcollision = acceptcollision && acc; } - if (mTriggerSelection != nullptr) { - } if (mZVertex != nullptr) { bool acc = filterBrickValue(selectedMask, bit, mZVertex, col.posZ()); acceptcollision = acceptcollision && acc; diff --git a/PWGCF/TwoParticleCorrelations/Core/PIDSelectionFilterAndAnalysis.h b/PWGCF/TwoParticleCorrelations/Core/PIDSelectionFilterAndAnalysis.h index 9c6d4d5ead0..7cf25ff5d6a 100644 --- a/PWGCF/TwoParticleCorrelations/Core/PIDSelectionFilterAndAnalysis.h +++ b/PWGCF/TwoParticleCorrelations/Core/PIDSelectionFilterAndAnalysis.h @@ -98,7 +98,7 @@ class PIDSelectionFilterAndAnalysis : public SelectionFilterAndAnalysis PIDSelectionFilterAndAnalysis(); PIDSelectionFilterAndAnalysis(const TString&, selmodes); PIDSelectionFilterAndAnalysis(const PIDSelectionConfigurable& pidsel, selmodes mode); - virtual ~PIDSelectionFilterAndAnalysis() override; + ~PIDSelectionFilterAndAnalysis() override; void SetPTOF(float ptof) { mPTOF = ptof; } void SetRequireTOF(bool requiretof = false) { mRequireTOF = requiretof; } @@ -133,8 +133,8 @@ class PIDSelectionFilterAndAnalysis : public SelectionFilterAndAnalysis private: void ConstructCutFromString(const TString&); - virtual int CalculateMaskLength() override; - virtual void StoreArmedMask() override; + int CalculateMaskLength() override; + void StoreArmedMask() override; float mPTOF = 0.8f; ///< the p threshold for cheking TOF information bool mRequireTOF = false; ///< is TOF required @@ -145,7 +145,7 @@ class PIDSelectionFilterAndAnalysis : public SelectionFilterAndAnalysis std::vector*> mCloseNsigmasTOF; std::vector*> mBayesProbability; - ClassDef(PIDSelectionFilterAndAnalysis, 1); + ClassDefOverride(PIDSelectionFilterAndAnalysis, 1); }; /// \brief Fills the filter cuts mask diff --git a/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.cxx b/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.cxx index ffd29c96571..56563c682ef 100644 --- a/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.cxx +++ b/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.cxx @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -1188,19 +1189,18 @@ bool TrackSelectionBrick::FulfillsITSHitRequirements(uint8_t itsClusterMap) return true; } -const std::string TrackSelectionBrick::mCutNames[static_cast(TrackSelectionBrick::TrackCuts::kNCuts)] = - { - "TrackType", - "TPCNCls", - "TPCCrossedRowsOverNCls", - "TPCRefit", - "ITSNCls", - "ITSChi2NDF", - "ITSRefit", - "ITSHits", - "GoldenChi2", - "DCAxy", - "DCAz"}; +const std::array(TrackSelectionBrick::TrackCuts::kNCuts)> TrackSelectionBrick::mCutNames{ + "TrackType", + "TPCNCls", + "TPCCrossedRowsOverNCls", + "TPCRefit", + "ITSNCls", + "ITSChi2NDF", + "ITSRefit", + "ITSHits", + "GoldenChi2", + "DCAxy", + "DCAz"}; /// \brief Returns wether the cut brick is incorporated in the selection chain /// \return true if the cut brick is incorporated diff --git a/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.h b/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.h index 8c3ca4f2094..aa79964d65a 100644 --- a/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.h +++ b/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.h @@ -23,6 +23,7 @@ #include +#include #include #include #include @@ -46,7 +47,7 @@ class CutBrick : public TNamed CutBrick(); CutBrick(const char*, const char*); CutBrick(const CutBrick&) = delete; - virtual ~CutBrick() override = default; + ~CutBrick() override = default; CutBrick& operator=(const CutBrick&) = delete; public: @@ -98,7 +99,7 @@ class CutBrick : public TNamed BrickStatus mState = kPASSIVE; BrickMode mMode = kUNSELECTED; - ClassDef(CutBrick, 1); + ClassDefOverride(CutBrick, 1); }; /// \class CutBrickLimit @@ -110,14 +111,14 @@ class CutBrickLimit : public CutBrick public: CutBrickLimit(); CutBrickLimit(const char*, const TValueToFilter&); - CutBrickLimit(const TString&); - virtual ~CutBrickLimit() override = default; + explicit CutBrickLimit(const TString&); + ~CutBrickLimit() override = default; CutBrickLimit(const CutBrickLimit&) = delete; CutBrickLimit& operator=(const CutBrickLimit&) = delete; - virtual std::vector IsArmed() override; - virtual std::vector Filter(const TValueToFilter&) override; - virtual int Length() override { return 1; } + std::vector IsArmed() override; + std::vector Filter(const TValueToFilter&) override; + int Length() override { return 1; } private: void ConstructCutFromString(const TString&); @@ -154,13 +155,13 @@ class CutBrickFnLimit : public CutBrickLimit public: CutBrickFnLimit(); CutBrickFnLimit(const char*, const TF1&); - CutBrickFnLimit(const TString&); - virtual ~CutBrickFnLimit() override = default; + explicit CutBrickFnLimit(const TString&); + ~CutBrickFnLimit() override = default; CutBrickFnLimit(const CutBrickFnLimit&) = delete; CutBrickFnLimit& operator=(const CutBrickFnLimit&) = delete; /// sets the value of the limit according the passed variable value - virtual void setIndependentFnVar(float x) override + void setIndependentFnVar(float x) override { this->mLimit = TValueToFilter(mFunction.Eval(x)); } @@ -181,14 +182,14 @@ class CutBrickThreshold : public CutBrick public: CutBrickThreshold(); CutBrickThreshold(const char*, const TValueToFilter&); - CutBrickThreshold(const TString&); - virtual ~CutBrickThreshold() override = default; + explicit CutBrickThreshold(const TString&); + ~CutBrickThreshold() override = default; CutBrickThreshold(const CutBrickThreshold&) = delete; CutBrickThreshold& operator=(const CutBrickThreshold&) = delete; - virtual std::vector IsArmed() override; - virtual std::vector Filter(const TValueToFilter&) override; - virtual int Length() override { return 1; } + std::vector IsArmed() override; + std::vector Filter(const TValueToFilter&) override; + int Length() override { return 1; } private: void ConstructCutFromString(const TString&); @@ -225,13 +226,13 @@ class CutBrickFnThreshold : public CutBrickThreshold public: CutBrickFnThreshold(); CutBrickFnThreshold(const char*, const TF1&); - CutBrickFnThreshold(const TString&); - virtual ~CutBrickFnThreshold() override = default; + explicit CutBrickFnThreshold(const TString&); + ~CutBrickFnThreshold() override = default; CutBrickFnThreshold(const CutBrickFnThreshold&) = delete; CutBrickFnThreshold& operator=(const CutBrickFnThreshold&) = delete; /// sets the value of the threshold according the passed variable value - virtual void setIndependentFnVar(float x) override + void setIndependentFnVar(float x) override { this->mThreshold = TValueToFilter(mFunction.Eval(x)); } @@ -252,14 +253,14 @@ class CutBrickRange : public CutBrick public: CutBrickRange(); CutBrickRange(const char*, const TValueToFilter&, const TValueToFilter&); - CutBrickRange(const TString&); - virtual ~CutBrickRange() override = default; + explicit CutBrickRange(const TString&); + ~CutBrickRange() override = default; CutBrickRange(const CutBrickRange&) = delete; CutBrickRange& operator=(const CutBrickRange&) = delete; - virtual std::vector IsArmed() override; - virtual std::vector Filter(const TValueToFilter&) override; - virtual int Length() override { return 1; } + std::vector IsArmed() override; + std::vector Filter(const TValueToFilter&) override; + int Length() override { return 1; } private: void ConstructCutFromString(const TString&); @@ -297,13 +298,13 @@ class CutBrickFnRange : public CutBrickRange public: CutBrickFnRange(); CutBrickFnRange(const char*, const TF1&, const TF1&); - CutBrickFnRange(const TString&); - virtual ~CutBrickFnRange() override = default; + explicit CutBrickFnRange(const TString&); + ~CutBrickFnRange() override = default; CutBrickFnRange(const CutBrickFnRange&) = delete; CutBrickFnRange& operator=(const CutBrickFnRange&) = delete; /// sets the value of the limits according the passed variable value - virtual void setIndependentFnVar(float x) override + void setIndependentFnVar(float x) override { this->mLow = TValueToFilter(mLowFunction.Eval(x)); this->mUp = TValueToFilter(mUpFunction.Eval(x)); @@ -326,14 +327,14 @@ class CutBrickExtToRange : public CutBrick public: CutBrickExtToRange(); CutBrickExtToRange(const char*, const TValueToFilter&, const TValueToFilter&); - CutBrickExtToRange(const TString&); - virtual ~CutBrickExtToRange() override = default; + explicit CutBrickExtToRange(const TString&); + ~CutBrickExtToRange() override = default; CutBrickExtToRange(const CutBrickExtToRange&) = delete; CutBrickExtToRange& operator=(const CutBrickExtToRange&) = delete; - virtual std::vector IsArmed() override; - virtual std::vector Filter(const TValueToFilter&) override; - virtual int Length() override { return 1; } + std::vector IsArmed() override; + std::vector Filter(const TValueToFilter&) override; + int Length() override { return 1; } private: void ConstructCutFromString(const TString&); @@ -371,13 +372,13 @@ class CutBrickFnExtToRange : public CutBrickExtToRange public: CutBrickFnExtToRange(); CutBrickFnExtToRange(const char*, const TF1&, const TF1&); - CutBrickFnExtToRange(const TString&); - virtual ~CutBrickFnExtToRange() override = default; + explicit CutBrickFnExtToRange(const TString&); + ~CutBrickFnExtToRange() override = default; CutBrickFnExtToRange(const CutBrickFnExtToRange&) = delete; CutBrickFnExtToRange& operator=(const CutBrickFnExtToRange&) = delete; /// sets the value of the limits according the passed variable value - virtual void setIndependentFnVar(float x) override + void setIndependentFnVar(float x) override { this->mLow = TValueToFilter(mLowFunction.Eval(x)); this->mUp = TValueToFilter(mUpFunction.Eval(x)); @@ -402,17 +403,17 @@ class CutBrickSelectorMultipleRanges : public CutBrick public: CutBrickSelectorMultipleRanges(); CutBrickSelectorMultipleRanges(const char*, const std::vector&); - CutBrickSelectorMultipleRanges(const TString&); - virtual ~CutBrickSelectorMultipleRanges() override = default; + explicit CutBrickSelectorMultipleRanges(const TString&); + ~CutBrickSelectorMultipleRanges() override = default; CutBrickSelectorMultipleRanges(const CutBrickSelectorMultipleRanges&) = delete; CutBrickSelectorMultipleRanges& operator=(const CutBrickSelectorMultipleRanges&) = delete; - virtual std::vector IsArmed() override; - virtual std::vector Filter(const TValueToFilter&) override; + std::vector IsArmed() override; + std::vector Filter(const TValueToFilter&) override; /// Return the length needed to code the brick status /// The length is in brick units. The actual length is implementation dependent /// \returns Brick length in units of bricks - virtual int Length() override { return mActive.size(); } + int Length() override { return mActive.size(); } private: void ConstructCutFromString(const TString&); @@ -432,8 +433,8 @@ class CutWithVariations : public CutBrick /// Default constructor CutWithVariations(); CutWithVariations(const char*, const char*, bool); - CutWithVariations(const TString&); - virtual ~CutWithVariations() override = default; + explicit CutWithVariations(const TString&); + ~CutWithVariations() override = default; CutWithVariations(const CutWithVariations&) = delete; CutWithVariations& operator=(const CutWithVariations&) = delete; @@ -441,10 +442,10 @@ class CutWithVariations : public CutBrick bool AddVariationBrick(CutBrick* brick); TList& getDefaultBricks() { return mDefaultBricks; } TList& getVariantBricks() { return mVariationBricks; } - virtual std::vector IsArmed() override; - virtual std::vector Filter(const TValueToFilter&) override; - virtual int Length() override; - virtual int getArmedIndex() override; + std::vector IsArmed() override; + std::vector Filter(const TValueToFilter&) override; + int Length() override; + int getArmedIndex() override; private: void ConstructCutFromString(const TString&); @@ -452,7 +453,7 @@ class CutWithVariations : public CutBrick bool mAllowSeveralDefaults; ///< true if allows to store several cut default values TList mDefaultBricks; ///< the list with the cut default values bricks TList mVariationBricks; ///< the list with the cut variation values bricks - ClassDef(CutWithVariations, 1); + ClassDefOverride(CutWithVariations, 1); }; /// \class SpecialCutBrick @@ -467,7 +468,7 @@ class SpecialCutBrick : public TNamed SpecialCutBrick(); SpecialCutBrick(const char*, const char*); SpecialCutBrick(const SpecialCutBrick&) = delete; - virtual ~SpecialCutBrick() override = default; + ~SpecialCutBrick() override = default; SpecialCutBrick& operator=(const SpecialCutBrick&) = delete; public: @@ -503,15 +504,15 @@ class SpecialCutBrick : public TNamed BrickStatus mState = kPASSIVE; BrickMode mMode = kUNSELECTED; - ClassDef(SpecialCutBrick, 1); + ClassDefOverride(SpecialCutBrick, 1); }; class TrackSelectionBrick : public SpecialCutBrick { public: TrackSelectionBrick() = default; - TrackSelectionBrick(const TString&); - virtual ~TrackSelectionBrick() override = default; + explicit TrackSelectionBrick(const TString&); + ~TrackSelectionBrick() override = default; enum class TrackCuts : int { kTrackType = 0, @@ -530,9 +531,9 @@ class TrackSelectionBrick : public SpecialCutBrick kNCuts }; - static const std::string mCutNames[static_cast(TrackCuts::kNCuts)]; + static const std::array(TrackSelectionBrick::TrackCuts::kNCuts)> mCutNames; - virtual std::vector IsArmed() override; + std::vector IsArmed() override; template bool Filter(TrackToFilter const& track) { @@ -547,8 +548,8 @@ class TrackSelectionBrick : public SpecialCutBrick ((mRequireTPCRefit) ? (track.flags() & o2::aod::track::TPCrefit) : true) && ((mRequireGoldenChi2) ? (track.flags() & o2::aod::track::GoldenChi2) : true) && FulfillsITSHitRequirements(track.itsClusterMap()) && - (mCheckMaxDcaXY && (abs(track.dcaXY()) <= ((mMaxDcaXYPtDep) ? mMaxDcaXYPtDep(track.pt()) : mMaxDcaXY))) && - (mCheckMaxDcaZ && (abs(track.dcaZ()) <= mMaxDcaZ))) { + (mCheckMaxDcaXY && (std::abs(track.dcaXY()) <= ((mMaxDcaXYPtDep) ? mMaxDcaXYPtDep(track.pt()) : mMaxDcaXY))) && + (mCheckMaxDcaZ && (std::abs(track.dcaZ()) <= mMaxDcaZ))) { this->mState = this->kACTIVE; return true; } else { @@ -626,7 +627,7 @@ class TrackSelectionBrick : public SpecialCutBrick // vector of ITS requirements (minNRequiredHits in specific requiredLayers) std::vector>> mRequiredITSHits{}; - ClassDef(TrackSelectionBrick, 1); + ClassDefOverride(TrackSelectionBrick, 1); }; } // namespace PWGCF diff --git a/PWGCF/TwoParticleCorrelations/Core/TwoPartCorrLinkDef.h b/PWGCF/TwoParticleCorrelations/Core/TwoPartCorrLinkDef.h index 3bb61740aaa..3b3bddb5118 100644 --- a/PWGCF/TwoParticleCorrelations/Core/TwoPartCorrLinkDef.h +++ b/PWGCF/TwoParticleCorrelations/Core/TwoPartCorrLinkDef.h @@ -8,8 +8,8 @@ // 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. -#ifndef PWGCF_TWOPARTICLECORRELATIONS_CORE_TWOPARTCORRLINKDEF_H_ -#define PWGCF_TWOPARTICLECORRELATIONS_CORE_TWOPARTCORRLINKDEF_H_ + +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -51,5 +51,3 @@ #pragma link C++ class o2::analysis::PWGCF::EventSelectionFilterAndAnalysis + ; #pragma link C++ class o2::analysis::PWGCF::FilterAndAnalysisFramework + ; #pragma link C++ class DihadronContainer + ; - -#endif // PWGCF_TWOPARTICLECORRELATIONS_CORE_TWOPARTCORRLINKDEF_H_ diff --git a/PWGCF/TwoParticleCorrelations/DataModel/LambdaSpinPolarizationDerived.h b/PWGCF/TwoParticleCorrelations/DataModel/LambdaSpinPolarizationDerived.h new file mode 100644 index 00000000000..8941e813244 --- /dev/null +++ b/PWGCF/TwoParticleCorrelations/DataModel/LambdaSpinPolarizationDerived.h @@ -0,0 +1,110 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file LambdaSpinPolarizationDerived.h +/// +/// \brief Derived tables of Lambda candidates with loose selections, input of lambdaSpinPolarization +/// \author Subhadeep Roy + +#ifndef PWGCF_TWOPARTICLECORRELATIONS_DATAMODEL_LAMBDASPINPOLARIZATIONDERIVED_H_ +#define PWGCF_TWOPARTICLECORRELATIONS_DATAMODEL_LAMBDASPINPOLARIZATIONDERIVED_H_ + +#include +#include + +#include + +namespace o2::aod +{ +namespace lambdaloosecollision +{ +DECLARE_SOA_COLUMN(Cent, cent, float); +DECLARE_SOA_COLUMN(Mult, mult, float); +DECLARE_SOA_COLUMN(PosX, posX, float); +DECLARE_SOA_COLUMN(PosY, posY, float); +DECLARE_SOA_COLUMN(PosZ, posZ, float); +DECLARE_SOA_COLUMN(TimeStamp, timeStamp, uint64_t); +} // namespace lambdaloosecollision + +DECLARE_SOA_TABLE(LambdaLooseCols, "AOD", "LAMBDALOOSECOL", + o2::soa::Index<>, + lambdaloosecollision::Cent, + lambdaloosecollision::Mult, + lambdaloosecollision::PosX, + lambdaloosecollision::PosY, + lambdaloosecollision::PosZ, + lambdaloosecollision::TimeStamp); +using LambdaLooseCol = LambdaLooseCols::iterator; + +namespace lambdaloosetrack +{ +DECLARE_SOA_INDEX_COLUMN(LambdaLooseCol, lambdaLooseCol); +DECLARE_SOA_COLUMN(Px, px, float); +DECLARE_SOA_COLUMN(Py, py, float); +DECLARE_SOA_COLUMN(Pz, pz, float); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Rap, rap, float); +DECLARE_SOA_COLUMN(Mass, mass, float); +DECLARE_SOA_COLUMN(MassOther, massOther, float); +DECLARE_SOA_COLUMN(PrPx, prPx, float); +DECLARE_SOA_COLUMN(PrPy, prPy, float); +DECLARE_SOA_COLUMN(PrPz, prPz, float); +DECLARE_SOA_COLUMN(PosTrackId, posTrackId, int32_t); +DECLARE_SOA_COLUMN(NegTrackId, negTrackId, int32_t); +DECLARE_SOA_COLUMN(V0Type, v0Type, int8_t); +DECLARE_SOA_COLUMN(CosPA, cosPA, float); +DECLARE_SOA_COLUMN(DcaDau, dcaDau, float); +DECLARE_SOA_COLUMN(DcaV0ToPV, dcaV0ToPV, float); +DECLARE_SOA_COLUMN(CTau, cTau, float); +DECLARE_SOA_COLUMN(MK0Short, mK0Short, float); +DECLARE_SOA_COLUMN(PosNSigmaPr, posNSigmaPr, float); +DECLARE_SOA_COLUMN(PosNSigmaPi, posNSigmaPi, float); +DECLARE_SOA_COLUMN(NegNSigmaPr, negNSigmaPr, float); +DECLARE_SOA_COLUMN(NegNSigmaPi, negNSigmaPi, float); +DECLARE_SOA_COLUMN(PosTpcCrossedRows, posTpcCrossedRows, int16_t); +DECLARE_SOA_COLUMN(NegTpcCrossedRows, negTpcCrossedRows, int16_t); +} // namespace lambdaloosetrack + +DECLARE_SOA_TABLE(LambdaLooseTracks, "AOD", "LAMBDALOOSETRKS", + o2::soa::Index<>, + lambdaloosetrack::LambdaLooseColId, + lambdaloosetrack::Px, + lambdaloosetrack::Py, + lambdaloosetrack::Pz, + lambdaloosetrack::Pt, + lambdaloosetrack::Eta, + lambdaloosetrack::Phi, + lambdaloosetrack::Rap, + lambdaloosetrack::Mass, + lambdaloosetrack::MassOther, + lambdaloosetrack::PrPx, + lambdaloosetrack::PrPy, + lambdaloosetrack::PrPz, + lambdaloosetrack::PosTrackId, + lambdaloosetrack::NegTrackId, + lambdaloosetrack::V0Type, + lambdaloosetrack::CosPA, + lambdaloosetrack::DcaDau, + lambdaloosetrack::DcaV0ToPV, + lambdaloosetrack::CTau, + lambdaloosetrack::MK0Short, + lambdaloosetrack::PosNSigmaPr, + lambdaloosetrack::PosNSigmaPi, + lambdaloosetrack::NegNSigmaPr, + lambdaloosetrack::NegNSigmaPi, + lambdaloosetrack::PosTpcCrossedRows, + lambdaloosetrack::NegTpcCrossedRows); +using LambdaLooseTrack = LambdaLooseTracks::iterator; +} // namespace o2::aod + +#endif // PWGCF_TWOPARTICLECORRELATIONS_DATAMODEL_LAMBDASPINPOLARIZATIONDERIVED_H_ diff --git a/PWGCF/TwoParticleCorrelations/DataModel/LongRangeDerived.h b/PWGCF/TwoParticleCorrelations/DataModel/LongRangeDerived.h index a6c9e2286ce..7435a99ecfb 100644 --- a/PWGCF/TwoParticleCorrelations/DataModel/LongRangeDerived.h +++ b/PWGCF/TwoParticleCorrelations/DataModel/LongRangeDerived.h @@ -18,8 +18,6 @@ #ifndef PWGCF_TWOPARTICLECORRELATIONS_DATAMODEL_LONGRANGEDERIVED_H_ #define PWGCF_TWOPARTICLECORRELATIONS_DATAMODEL_LONGRANGEDERIVED_H_ -#include "PWGMM/Mult/DataModel/bestCollisionTable.h" - #include "Common/DataModel/Multiplicity.h" #include @@ -76,14 +74,6 @@ DECLARE_SOA_TABLE(LRFt0cMcTracks, "AOD", "LRFT0CMCTRACK", lrcorrmctrktable::Phi); using LRFt0cMcTrack = LRFt0cMcTracks::iterator; -DECLARE_SOA_TABLE(LRMftMcTracks, "AOD", "LRMFTMCTRACK", - o2::soa::Index<>, - lrcorrmctrktable::LRMcCollisionId, - lrcorrmctrktable::Pt, - lrcorrmctrktable::Eta, - lrcorrmctrktable::Phi); -using LRMftMcTrack = LRMftMcTracks::iterator; - namespace lrcorrcolltable { DECLARE_SOA_INDEX_COLUMN(LRMcCollision, lrMcCollision); @@ -221,20 +211,6 @@ DECLARE_SOA_TABLE(LRV0Tracks, "AOD", "LRV0TRACK", lrcorrtrktable::V0Type); using LRV0Track = LRV0Tracks::iterator; -DECLARE_SOA_TABLE(LRMftTracks, "AOD", "LRMFTTRACK", - o2::soa::Index<>, - lrcorrtrktable::LRCollisionId, - lrcorrtrktable::AmbDegree, - lrcorrtrktable::Pt, - lrcorrtrktable::Eta, - lrcorrtrktable::Phi, - fwdtrack::NClusters, - fwdtrack::BestDCAXY, - fwdtrack::BestDCAZ, - lrcorrtrktable::IsCA, - lrcorrtrktable::IsReassigned); -using LRMftTrack = LRMftTracks::iterator; - DECLARE_SOA_TABLE(UpcLRMidTracks, "AOD", "UPCLRMIDTRACK", o2::soa::Index<>, lrcorrtrktable::UpcLRCollisionId, @@ -280,19 +256,6 @@ DECLARE_SOA_TABLE(UpcLRV0Tracks, "AOD", "UPCLRV0TRACK", lrcorrtrktable::V0Type); using UpcLRV0Track = UpcLRV0Tracks::iterator; -DECLARE_SOA_TABLE(UpcLRMftTracks, "AOD", "UPCLRMFTTRACK", - o2::soa::Index<>, - lrcorrtrktable::UpcLRCollisionId, - lrcorrtrktable::AmbDegree, - lrcorrtrktable::Pt, - lrcorrtrktable::Eta, - lrcorrtrktable::Phi, - fwdtrack::NClusters, - fwdtrack::BestDCAXY, - fwdtrack::BestDCAZ, - lrcorrtrktable::IsCA, - lrcorrtrktable::IsReassigned); -using UpcLRMftTrack = UpcLRMftTracks::iterator; } // namespace o2::aod #endif // PWGCF_TWOPARTICLECORRELATIONS_DATAMODEL_LONGRANGEDERIVED_H_ diff --git a/PWGCF/TwoParticleCorrelations/TableProducer/CMakeLists.txt b/PWGCF/TwoParticleCorrelations/TableProducer/CMakeLists.txt index 0669a351d1e..919a0c205e9 100644 --- a/PWGCF/TwoParticleCorrelations/TableProducer/CMakeLists.txt +++ b/PWGCF/TwoParticleCorrelations/TableProducer/CMakeLists.txt @@ -33,3 +33,8 @@ o2physics_add_dpl_workflow(longrange-maker SOURCES longrangeMaker.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::TwoPartCorrCore O2Physics::SGCutParHolder COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(lambda-spin-polarization-loose-producer + SOURCES lambdaSpinPolarizationLooseProducer.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGCF/TwoParticleCorrelations/TableProducer/lambdaSpinPolarizationLooseProducer.cxx b/PWGCF/TwoParticleCorrelations/TableProducer/lambdaSpinPolarizationLooseProducer.cxx new file mode 100644 index 00000000000..dd5ed9595b4 --- /dev/null +++ b/PWGCF/TwoParticleCorrelations/TableProducer/lambdaSpinPolarizationLooseProducer.cxx @@ -0,0 +1,482 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file lambdaSpinPolarizationLooseProducer.cxx +/// \brief Lambda candidates with loose selections, written as derived data for lambdaSpinPolarization +/// \author Subhadeep Roy + +#include "PWGCF/TwoParticleCorrelations/DataModel/LambdaSpinPolarizationDerived.h" +#include "PWGLF/DataModel/LFStrangenessTables.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/CollisionAssociationTables.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::constants::physics; + +enum LooseEventCutFlow { kLeAll = 1, + kLeTrigger, + kLeTvx, + kLeTFBorder, + kLeItsRofBorder, + kLeItsTpcVtx, + kLeNoSameBunchPileup, + kLeGoodZvtxFT0vsPV, + kLeGoodItsLayers, + kLeCentrality, + kLeVz, + kLeOneV0, + kLeTwoV0, + kLeWritten, + kNLooseEventCutFlow }; + +enum LooseV0Count { kLvAll = 1, + kLvV0Type, + kLvAmbiguousTracks, + kLvKShortRejected, + kLvKinematics, + kLvTopology, + kLvNoHypothesis, + kLvLambdaRow, + kLvAntiLambdaRow, + kLvBothRows, + kNLooseV0Count }; + +enum LooseHypothesis { kLooseLambda = 0, + kLooseAntiLambda, + kNLooseHypotheses }; + +static constexpr std::size_t NV0ForPair = 2; + +enum LooseCentEstimator { kLooseCentFT0M = 0, + kLooseCentFT0C }; + +struct LambdaSpinPolarizationLooseProducer { + + Produces lambdaLooseCollisionTable; + Produces lambdaLooseTrackTable; + + Configurable cCentEstimator{"cCentEstimator", 0, "Centrality Estimator: 0=FT0M, 1=FT0C"}; + Configurable cMinZVtx{"cMinZVtx", -10.0, "Min VtxZ (cm)"}; + Configurable cMaxZVtx{"cMaxZVtx", 10.0, "Max VtxZ (cm)"}; + Configurable cMinMult{"cMinMult", 0.0, "Min centrality percentile"}; + Configurable cMaxMult{"cMaxMult", 100.0, "Max centrality percentile"}; + Configurable cSel8Trig{"cSel8Trig", true, "Sel8 (T0A+T0C) Run3"}; + Configurable cTriggerTvxSel{"cTriggerTvxSel", false, "TVX Trigger Selection"}; + Configurable cTFBorder{"cTFBorder", false, "Timeframe Border Selection"}; + Configurable cNoItsROBorder{"cNoItsROBorder", false, "No ITSRO Border Cut"}; + Configurable cItsTpcVtx{"cItsTpcVtx", false, "ITS+TPC Vertex Selection"}; + Configurable cPileupReject{"cPileupReject", false, "Pileup rejection"}; + Configurable cZVtxTimeDiff{"cZVtxTimeDiff", false, "z-vtx time diff selection"}; + Configurable cIsGoodITSLayers{"cIsGoodITSLayers", false, "Good ITS Layers All"}; + + Configurable cTrackMinPt{"cTrackMinPt", 0.15, "p_{T} minimum"}; + Configurable cTrackMaxPt{"cTrackMaxPt", 999.0, "p_{T} maximum"}; + Configurable cTrackEtaCut{"cTrackEtaCut", 0.8, "Pseudorapidity cut"}; + Configurable cMinTpcCrossedRows{"cMinTpcCrossedRows", 50, "TPC Min Crossed Rows (loosest of the variations)"}; + Configurable cMinTpcCROverCls{"cMinTpcCROverCls", -999, "TPC Min CR/Findable Cls"}; + Configurable cMaxTpcSharedClusters{"cMaxTpcSharedClusters", 999, "TPC Max Shared Clusters"}; + Configurable cMaxChi2Tpc{"cMaxChi2Tpc", 999, "Max TPC Chi2/ndf"}; + Configurable cTpcNsigmaCut{"cTpcNsigmaCut", 5.0, "TPC nSigma PID cut (loosest of the variations)"}; + Configurable cRemoveAmbiguousTracks{"cRemoveAmbiguousTracks", false, "Remove Ambiguous Tracks"}; + + Configurable cMinDcaProtonToPV{"cMinDcaProtonToPV", 0.05, "Min proton DCA to PV (cm)"}; + Configurable cMinDcaPionToPV{"cMinDcaPionToPV", 0.1, "Min pion DCA to PV (cm)"}; + Configurable cMinV0DcaDaughters{"cMinV0DcaDaughters", 0., "Min DCA between V0 daughters"}; + Configurable cMaxV0DcaDaughters{"cMaxV0DcaDaughters", 1.4, "Max DCA between V0 daughters"}; + Configurable cMinDcaV0ToPV{"cMinDcaV0ToPV", 0.0, "Min DCA V0 to PV"}; + Configurable cMaxDcaV0ToPV{"cMaxDcaV0ToPV", 0.2, "Max DCA V0 to PV (loosest of the variations)"}; + Configurable cMinV0TransRadius{"cMinV0TransRadius", 0.5, "Min V0 decay radius (cm)"}; + Configurable cMaxV0TransRadius{"cMaxV0TransRadius", 999.0, "Max V0 decay radius (cm)"}; + Configurable cMinV0CTau{"cMinV0CTau", 0.0, "Min cTau (cm)"}; + Configurable cMaxV0CTau{"cMaxV0CTau", 36.0, "Max cTau (cm) (loosest of the variations)"}; + Configurable cMinV0CosPA{"cMinV0CosPA", 0.98, "Min V0 cos(PA) (loosest of the variations)"}; + Configurable cKshortRejMassWindow{"cKshortRejMassWindow", 0.001, "K0s mass rejection window (narrowest of the variations)"}; + Configurable cKshortRejFlag{"cKshortRejFlag", true, "K0s mass rejection flag"}; + + Configurable cMinV0Mass{"cMinV0Mass", 1.09, "V0 Mass Min"}; + Configurable cMaxV0Mass{"cMaxV0Mass", 1.14, "V0 Mass Max"}; + Configurable cMinV0Pt{"cMinV0Pt", 0.6, "Minimum V0 pT"}; + Configurable cMaxV0Pt{"cMaxV0Pt", 3.0, "Maximum V0 pT"}; + Configurable cMaxV0Rap{"cMaxV0Rap", 0.5, "|rap| cut"}; + Configurable cDoEtaAnalysis{"cDoEtaAnalysis", false, "Do Eta Analysis"}; + Configurable cV0TypeSelFlag{"cV0TypeSelFlag", true, "V0 Type Selection Flag"}; + Configurable cV0TypeSelection{"cV0TypeSelection", 1, "V0 Type Selection"}; + + Configurable cMinV0PerEvent{"cMinV0PerEvent", 2, "Collisions with fewer V0s passing the loose selection are not written"}; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + struct LooseCandidate { + float px, py, pz, pt, eta, phi, rap, mass, massOther, prPx, prPy, prPz; + int32_t posTrackId, negTrackId; + int8_t v0Type; + float cosPA, dcaDau, dcaV0ToPV, cTau, mK0Short; + float posNSigmaPr, posNSigmaPi, negNSigmaPr, negNSigmaPi; + int16_t posTpcCrossedRows, negTpcCrossedRows; + }; + std::vector candidates; + + float cent = 0., mult = 0.; + + void init(InitContext const&) + { + if (cMinV0PerEvent.value < 1) { + LOGF(fatal, "cMinV0PerEvent must be at least 1, it is %d", cMinV0PerEvent.value); + } + const AxisSpec axisV0Mass(200, 1.08, 1.18, "M_{p#pi} (GeV/#it{c}^{2})"); + const AxisSpec axisV0Pt(100., 0., 10., "p_{T} (GeV/#it{c})"); + const AxisSpec axisNV0(20, -0.5, 19.5, "V0s written per collision"); + + auto hCutFlow = histos.add("Events/hEventCutFlow", "event cut flow;;collisions", kTH1D, {{kNLooseEventCutFlow - 1, 0.5, static_cast(kNLooseEventCutFlow) - 0.5}}); + hCutFlow->GetXaxis()->SetBinLabel(kLeAll, "all"); + hCutFlow->GetXaxis()->SetBinLabel(kLeTrigger, "trigger (sel8)"); + hCutFlow->GetXaxis()->SetBinLabel(kLeTvx, "TVX"); + hCutFlow->GetXaxis()->SetBinLabel(kLeTFBorder, "no TF border"); + hCutFlow->GetXaxis()->SetBinLabel(kLeItsRofBorder, "no ITS ROF border"); + hCutFlow->GetXaxis()->SetBinLabel(kLeItsTpcVtx, "ITS-TPC vertex"); + hCutFlow->GetXaxis()->SetBinLabel(kLeNoSameBunchPileup, "no same-bunch pileup"); + hCutFlow->GetXaxis()->SetBinLabel(kLeGoodZvtxFT0vsPV, "good z_{vtx} FT0 vs PV"); + hCutFlow->GetXaxis()->SetBinLabel(kLeGoodItsLayers, "good ITS layers"); + hCutFlow->GetXaxis()->SetBinLabel(kLeCentrality, "centrality range"); + hCutFlow->GetXaxis()->SetBinLabel(kLeVz, "V_{z} range"); + hCutFlow->GetXaxis()->SetBinLabel(kLeOneV0, "#geq 1 V0 with a loose candidate"); + hCutFlow->GetXaxis()->SetBinLabel(kLeTwoV0, "#geq 2 V0s with a loose candidate"); + hCutFlow->GetXaxis()->SetBinLabel(kLeWritten, "written"); + + auto hV0 = histos.add("Tracks/hV0Count", "V0s;;V0s", kTH1D, {{kNLooseV0Count - 1, 0.5, static_cast(kNLooseV0Count) - 0.5}}); + hV0->GetXaxis()->SetBinLabel(kLvAll, "all"); + hV0->GetXaxis()->SetBinLabel(kLvV0Type, "rejected: V0 type"); + hV0->GetXaxis()->SetBinLabel(kLvAmbiguousTracks, "rejected: ambiguous tracks"); + hV0->GetXaxis()->SetBinLabel(kLvKShortRejected, "rejected: K^{0}_{S} mass"); + hV0->GetXaxis()->SetBinLabel(kLvKinematics, "rejected: p_{T} / y"); + hV0->GetXaxis()->SetBinLabel(kLvTopology, "rejected: topology"); + hV0->GetXaxis()->SetBinLabel(kLvNoHypothesis, "rejected: no hypothesis"); + hV0->GetXaxis()->SetBinLabel(kLvLambdaRow, "#Lambda candidate"); + hV0->GetXaxis()->SetBinLabel(kLvAntiLambdaRow, "#bar{#Lambda} candidate"); + hV0->GetXaxis()->SetBinLabel(kLvBothRows, "both candidates"); + + histos.add("Tracks/hLambdaMassVsPt", "#Lambda candidates", kTH2F, {axisV0Mass, axisV0Pt}); + histos.add("Tracks/hAntiLambdaMassVsPt", "#bar{#Lambda} candidates", kTH2F, {axisV0Mass, axisV0Pt}); + histos.add("Events/hNV0Written", "written collisions", kTH1F, {axisNV0}); + } + + template + bool selCollision(C const& col) + { + histos.fill(HIST("Events/hEventCutFlow"), kLeAll); + if (cCentEstimator == kLooseCentFT0M) { + cent = col.centFT0M(); + } else if (cCentEstimator == kLooseCentFT0C) { + cent = col.centFT0C(); + } + if (cSel8Trig && !col.sel8()) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeTrigger); + + if (cTriggerTvxSel && !col.selection_bit(aod::evsel::kIsTriggerTVX)) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeTvx); + if (cTFBorder && !col.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeTFBorder); + if (cNoItsROBorder && !col.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeItsRofBorder); + if (cItsTpcVtx && !col.selection_bit(aod::evsel::kIsVertexITSTPC)) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeItsTpcVtx); + if (cPileupReject && !col.selection_bit(aod::evsel::kNoSameBunchPileup)) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeNoSameBunchPileup); + if (cZVtxTimeDiff && !col.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeGoodZvtxFT0vsPV); + if (cIsGoodITSLayers && !col.selection_bit(aod::evsel::kIsGoodITSLayersAll)) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeGoodItsLayers); + + if (cent <= cMinMult || cent >= cMaxMult) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeCentrality); + if (col.posZ() <= cMinZVtx || col.posZ() >= cMaxZVtx) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeVz); + + mult = col.multNTracksPV(); + return true; + } + + bool kinCutSelection(float const& pt, float const& rap, float const& ptMin, float const& ptMax, float const& rapMax) + { + return pt > ptMin && pt < ptMax && rap < rapMax; + } + + template + bool selTrack(T const& track) + { + if (!kinCutSelection(track.pt(), std::abs(track.eta()), cTrackMinPt, cTrackMaxPt, cTrackEtaCut)) { + return false; + } + if (track.tpcNClsCrossedRows() <= cMinTpcCrossedRows) { + return false; + } + if (track.tpcCrossedRowsOverFindableCls() < cMinTpcCROverCls) { + return false; + } + if (track.tpcNClsShared() > cMaxTpcSharedClusters) { + return false; + } + if (track.tpcChi2NCl() > cMaxChi2Tpc) { + return false; + } + return true; + } + + template + bool selDaughterTracks(V const& v0, T const&, LooseHypothesis const& v0Type) + { + auto posTrack = v0.template posTrack_as(); + auto negTrack = v0.template negTrack_as(); + if (!selTrack(posTrack) || !selTrack(negTrack)) { + return false; + } + + float dcaProton = 0., dcaPion = 0.; + if (v0Type == kLooseLambda) { + dcaProton = std::abs(v0.dcapostopv()); + dcaPion = std::abs(v0.dcanegtopv()); + } else if (v0Type == kLooseAntiLambda) { + dcaPion = std::abs(v0.dcapostopv()); + dcaProton = std::abs(v0.dcanegtopv()); + } + if (dcaProton < cMinDcaProtonToPV || dcaPion < cMinDcaPionToPV) { + return false; + } + return true; + } + + template + bool topoCutSelection(C const& col, V const& v0, T const&) + { + if (v0.dcaV0daughters() <= cMinV0DcaDaughters || v0.dcaV0daughters() >= cMaxV0DcaDaughters) { + return false; + } + if (v0.dcav0topv() <= cMinDcaV0ToPV || v0.dcav0topv() >= cMaxDcaV0ToPV) { + return false; + } + if (v0.v0radius() <= cMinV0TransRadius || v0.v0radius() >= cMaxV0TransRadius) { + return false; + } + + float ctau = v0.distovertotmom(col.posX(), col.posY(), col.posZ()) * MassLambda0; + if (ctau <= cMinV0CTau || ctau >= cMaxV0CTau) { + return false; + } + if (v0.v0cosPA() <= cMinV0CosPA) { + return false; + } + return true; + } + + template + bool selLambdaDauWithTpcPid(T const& postrack, T const& negtrack) + { + float tpcNSigmaPr = 0., tpcNSigmaPi = 0.; + if constexpr (part == kLooseLambda) { + tpcNSigmaPr = postrack.tpcNSigmaPr(); + tpcNSigmaPi = negtrack.tpcNSigmaPi(); + } else { + tpcNSigmaPr = negtrack.tpcNSigmaPr(); + tpcNSigmaPi = postrack.tpcNSigmaPi(); + } + return (std::abs(tpcNSigmaPr) < cTpcNsigmaCut && std::abs(tpcNSigmaPi) < cTpcNsigmaCut); + } + + template + bool hasAmbiguousDaughters(V const& v0, T const&) + { + auto posTrack = v0.template posTrack_as(); + auto negTrack = v0.template negTrack_as(); + auto posCC = posTrack.compatibleCollIds(); + auto negCC = negTrack.compatibleCollIds(); + if (posCC.size() > 1 || negCC.size() > 1) { + return true; + } + if ((posCC.size() != 0 && posCC[0] != posTrack.collisionId()) || + (negCC.size() != 0 && negCC[0] != negTrack.collisionId())) { + return true; + } + return false; + } + + using CollisionsRun3 = soa::Join; + using Tracks = soa::Join; + + void processData(CollisionsRun3::iterator const& collision, aod::BCsWithTimestamps const&, aod::V0Datas const& V0s, Tracks const& tracks) + { + if (!selCollision(collision)) { + return; + } + + candidates.clear(); + std::size_t nV0 = 0; + for (auto const& v0 : V0s) { + histos.fill(HIST("Tracks/hV0Count"), kLvAll); + if (cV0TypeSelFlag && v0.v0Type() != cV0TypeSelection) { + histos.fill(HIST("Tracks/hV0Count"), kLvV0Type); + continue; + } + if (cRemoveAmbiguousTracks && hasAmbiguousDaughters(v0, tracks)) { + histos.fill(HIST("Tracks/hV0Count"), kLvAmbiguousTracks); + continue; + } + if (cKshortRejFlag && (std::abs(v0.mK0Short() - MassK0Short) <= cKshortRejMassWindow)) { + histos.fill(HIST("Tracks/hV0Count"), kLvKShortRejected); + continue; + } + const float rap = cDoEtaAnalysis ? std::abs(v0.eta()) : std::abs(v0.yLambda()); + if (!kinCutSelection(v0.pt(), rap, cMinV0Pt, cMaxV0Pt, cMaxV0Rap)) { + histos.fill(HIST("Tracks/hV0Count"), kLvKinematics); + continue; + } + if (!topoCutSelection(collision, v0, tracks)) { + histos.fill(HIST("Tracks/hV0Count"), kLvTopology); + continue; + } + + auto postrack = v0.posTrack_as(); + auto negtrack = v0.negTrack_as(); + const std::array masses = {v0.mLambda(), v0.mAntiLambda()}; + const std::array pid = {selLambdaDauWithTpcPid(postrack, negtrack), selLambdaDauWithTpcPid(postrack, negtrack)}; + const float ctau = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * MassLambda0; + + std::size_t nRows = 0; + for (auto const& hyp : {kLooseLambda, kLooseAntiLambda}) { + const bool inMassWindow = masses[hyp] > cMinV0Mass && masses[hyp] < cMaxV0Mass; + if (!inMassWindow || !pid[hyp]) { + continue; + } + if (!selDaughterTracks(v0, tracks, hyp)) { + continue; + } + const bool isLambda = hyp == kLooseLambda; + candidates.push_back(LooseCandidate{.px = v0.px(), + .py = v0.py(), + .pz = v0.pz(), + .pt = v0.pt(), + .eta = v0.eta(), + .phi = v0.phi(), + .rap = v0.yLambda(), + .mass = masses[hyp], + .massOther = masses[isLambda ? kLooseAntiLambda : kLooseLambda], + .prPx = isLambda ? v0.pxpos() : v0.pxneg(), + .prPy = isLambda ? v0.pypos() : v0.pyneg(), + .prPz = isLambda ? v0.pzpos() : v0.pzneg(), + .posTrackId = static_cast(postrack.index()), + .negTrackId = static_cast(negtrack.index()), + .v0Type = static_cast(hyp), + .cosPA = v0.v0cosPA(), + .dcaDau = v0.dcaV0daughters(), + .dcaV0ToPV = v0.dcav0topv(), + .cTau = ctau, + .mK0Short = v0.mK0Short(), + .posNSigmaPr = postrack.tpcNSigmaPr(), + .posNSigmaPi = postrack.tpcNSigmaPi(), + .negNSigmaPr = negtrack.tpcNSigmaPr(), + .negNSigmaPi = negtrack.tpcNSigmaPi(), + .posTpcCrossedRows = postrack.tpcNClsCrossedRows(), + .negTpcCrossedRows = negtrack.tpcNClsCrossedRows()}); + if (isLambda) { + histos.fill(HIST("Tracks/hV0Count"), kLvLambdaRow); + histos.fill(HIST("Tracks/hLambdaMassVsPt"), masses[hyp], v0.pt()); + } else { + histos.fill(HIST("Tracks/hV0Count"), kLvAntiLambdaRow); + histos.fill(HIST("Tracks/hAntiLambdaMassVsPt"), masses[hyp], v0.pt()); + } + ++nRows; + } + if (nRows == 0) { + histos.fill(HIST("Tracks/hV0Count"), kLvNoHypothesis); + continue; + } + if (nRows == kNLooseHypotheses) { + histos.fill(HIST("Tracks/hV0Count"), kLvBothRows); + } + ++nV0; + } + + if (nV0 >= 1) { + histos.fill(HIST("Events/hEventCutFlow"), kLeOneV0); + } + if (nV0 >= NV0ForPair) { + histos.fill(HIST("Events/hEventCutFlow"), kLeTwoV0); + } + if (std::cmp_less(nV0, cMinV0PerEvent.value)) { + return; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeWritten); + histos.fill(HIST("Events/hNV0Written"), nV0); + + auto bc = collision.bc_as(); + lambdaLooseCollisionTable(cent, mult, collision.posX(), collision.posY(), collision.posZ(), bc.timestamp()); + for (auto const& c : candidates) { + lambdaLooseTrackTable(lambdaLooseCollisionTable.lastIndex(), c.px, c.py, c.pz, c.pt, c.eta, c.phi, c.rap, c.mass, c.massOther, c.prPx, c.prPy, c.prPz, + c.posTrackId, c.negTrackId, c.v0Type, c.cosPA, c.dcaDau, c.dcaV0ToPV, c.cTau, c.mK0Short, + c.posNSigmaPr, c.posNSigmaPi, c.negNSigmaPr, c.negNSigmaPi, c.posTpcCrossedRows, c.negTpcCrossedRows); + } + } + PROCESS_SWITCH(LambdaSpinPolarizationLooseProducer, processData, "Run 3 data (or MC reconstructed as data)", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGCF/TwoParticleCorrelations/TableProducer/longrangeMaker.cxx b/PWGCF/TwoParticleCorrelations/TableProducer/longrangeMaker.cxx index b49e5a6f554..c31496536fd 100644 --- a/PWGCF/TwoParticleCorrelations/TableProducer/longrangeMaker.cxx +++ b/PWGCF/TwoParticleCorrelations/TableProducer/longrangeMaker.cxx @@ -18,7 +18,6 @@ #include "PWGCF/Core/CorrelationContainer.h" #include "PWGCF/TwoParticleCorrelations/DataModel/LongRangeDerived.h" #include "PWGLF/DataModel/LFStrangenessTables.h" -#include "PWGMM/Mult/DataModel/bestCollisionTable.h" #include "PWGUD/Core/SGCutParHolder.h" #include "PWGUD/Core/SGSelector.h" #include "PWGUD/Core/UDHelpers.h" @@ -53,16 +52,13 @@ #include #include #include -#include #include #include #include -#include #include -#include #include #include #include @@ -79,7 +75,6 @@ using namespace o2::aod::fwdtrack; using namespace o2::aod::evsel; using namespace o2::constants::math; -auto static constexpr CintZero = 0; auto static constexpr KminFt0cCell = 96; auto static constexpr TotFt0Channels = 208; auto static constexpr MinFt0aDeadChannelOuter = 60; @@ -93,8 +88,7 @@ auto static constexpr MinFt0cMirrorChannelOuter = 144; auto static constexpr MaxFt0cMirrorChannelOuter = 147; auto static constexpr MinFt0cOuterRing = 144; -AxisSpec axisEvent{20, 0.5, 20.5, "#Event", "EventAxis"}; -AxisSpec axisTrackSel{10, 0.5, 10.5, "#Track", "TrackAxis"}; +const AxisSpec axisEvent{20, 0.5, 20.5, "#Event", "EventAxis"}; auto static constexpr KminCharge = 3.0f; enum KindOfV0 { @@ -153,20 +147,6 @@ struct LongrangeMaker { Configurable cfgEffccdbPath{"cfgEffccdbPath", "Users/a/abmodak/Efficiency/OO/default", "Browse track eff object from CCDB"}; } cfgtrksel; - struct : ConfigurableGroup { - std::string prefix = "MftTrackSelection_group"; - Configurable cfgUseChi2Cut{"cfgUseChi2Cut", false, "Use condition on MFT track: chi2/Nclusters"}; - Configurable useMftPtCut{"useMftPtCut", true, "Choose to apply MFT track pT cut"}; - Configurable cfgMftCluster{"cfgMftCluster", 5, "cut on MFT Cluster"}; - Configurable cfgMftEtaMax{"cfgMftEtaMax", -2.4f, "Maximum MFT eta cut"}; - Configurable cfgMftEtaMin{"cfgMftEtaMin", -3.6f, "Minimum MFT eta cut"}; - Configurable cfgMftDcaxy{"cfgMftDcaxy", 2.0f, "cut on DCA xy for MFT tracks"}; - Configurable cfgMftDcaz{"cfgMftDcaz", 2.0f, "cut on DCA z for MFT tracks"}; - Configurable cfgMftPtCutMin{"cfgMftPtCutMin", 0.f, "minimum accepted MFT track pT"}; - Configurable cfgMftPtCutMax{"cfgMftPtCutMax", 10.f, "maximum accepted MFT track pT"}; - Configurable cfgMaxChi2NCl{"cfgMaxChi2NCl", 1.f, "Cut on MFT track: chi2/Nclusters"}; - } cfgmfttrksel; - struct : ConfigurableGroup { std::string prefix = "FitTrackSelection_group"; Configurable cfgFt0aEtaMax{"cfgFt0aEtaMax", 4.9f, "Maximum FT0A eta cut"}; @@ -215,7 +195,6 @@ struct LongrangeMaker { std::string prefix = "ConfigAxis_group"; ConfigurableAxis axisAmplitude{"axisAmplitude", {5000, 0, 10000}, "FT0 amplitude"}; ConfigurableAxis axisChannel{"axisChannel", {208, 0, 208}, "FT0 channel"}; - ConfigurableAxis axisMFTAmbDegree{"axisMFTAmbDegree", {50, -0.5, 49.5}, "Track Ambiguity axis"}; ConfigurableAxis axisEta = {"axisEta", {100, -5, 5}, "eta axis"}; ConfigurableAxis axisPhi{"axisPhi", {72, 0, TwoPI}, "#phi axis"}; @@ -246,8 +225,8 @@ struct LongrangeMaker { Configurable cfgTofPidPtCut{"cfgTofPidPtCut", 0.3f, "Minimum pt to use TOF N-sigma"}; Configurable isUseItsPid{"isUseItsPid", false, "Use ITS PID for particle identification"}; - Service ccdb; - Service pdg; + Service ccdb{}; + Service pdg{}; o2::ccdb::CcdbApi ccdbApi; o2::ft0::Geometry ft0Det; std::vector* offsetFT0{nullptr}; @@ -262,7 +241,7 @@ struct LongrangeMaker { // Create instance of the selector class which runs the gap selection algorithm SGSelector sgSelector; // Create instance of cut holder class to contain the user defined cuts - SGCutParHolder cfgSgCuts = SGCutParHolder(); + SGCutParHolder cfgSgCuts; Configurable sgCuts{"sgCuts", {}, "SG event cuts"}; Configurable cfgGapSide{"cfgGapSide", 2, "cut on UPC events"}; @@ -303,25 +282,6 @@ struct LongrangeMaker { histos.add("hSelectionResult", "hSelectionResult", kTH1I, {{5, -0.5, 4.5}}); - histos.add("hMftTrkSel", "hMftTrkSel", kTH1D, {axisTrackSel}, false); - auto hMftTrkStat = histos.get(HIST("hMftTrkSel")); - auto* xMftTrk = hMftTrkStat->GetXaxis(); - xMftTrk->SetBinLabel(1, "All"); - xMftTrk->SetBinLabel(2, "Cluster selection"); - xMftTrk->SetBinLabel(3, "Chi2/NCluster selection"); - xMftTrk->SetBinLabel(4, "eta selection"); - xMftTrk->SetBinLabel(5, "pT selection"); - xMftTrk->SetBinLabel(6, "CA track selection"); - - histos.add("hMftBestTrkSel", "hMftBestTrkSel", kTH1D, {axisTrackSel}, false); - auto hMftBestTrkStat = histos.get(HIST("hMftBestTrkSel")); - auto* xMftBestTrk = hMftBestTrkStat->GetXaxis(); - xMftBestTrk->SetBinLabel(1, "All"); - xMftBestTrk->SetBinLabel(2, "BestSelCollID selection"); - xMftBestTrk->SetBinLabel(3, "Orphan track selection"); - xMftBestTrk->SetBinLabel(4, "DCAxy selection"); - xMftBestTrk->SetBinLabel(5, "DCAz selection"); - histos.add("FT0A_Amp", "FT0A_Amp", kTH1D, {cfgAxis.axisAmplitude}); histos.add("FT0A_Amp_gaincorrected", "FT0A_Amp_gaincorrected", kTH1D, {cfgAxis.axisAmplitude}); histos.add("FT0A_Channel_vs_Amp", "FT0A_Channel_vs_Amp", kTH2D, {cfgAxis.axisChannel, cfgAxis.axisAmplitude}); @@ -355,9 +315,9 @@ struct LongrangeMaker { TrackSelection::GlobalTrackRun3DCAxyCut::Default); myTrackFilter.SetPtRange(cfgtrksel.cfgPtCutMin, cfgtrksel.cfgPtCutMax); myTrackFilter.SetEtaRange(-cfgtrksel.cfgEtaCut, cfgtrksel.cfgEtaCut); - myTrackFilter.SetMinNCrossedRowsTPC(cfgtrksel.minNCrossedRowsTPC); + myTrackFilter.SetMinNCrossedRowsTPC(static_cast(cfgtrksel.minNCrossedRowsTPC)); myTrackFilter.SetMinNCrossedRowsOverFindableClustersTPC(cfgtrksel.minNCrossedRowsOverFindableClustersTPC); - myTrackFilter.SetMinNClustersTPC(cfgtrksel.minTPCNClsFound); + myTrackFilter.SetMinNClustersTPC(static_cast(cfgtrksel.minTPCNClsFound)); myTrackFilter.SetMaxChi2PerClusterTPC(cfgtrksel.maxChi2PerClusterTPC); myTrackFilter.SetMaxChi2PerClusterITS(cfgtrksel.maxChi2PerClusterITS); myTrackFilter.SetMaxDcaZ(cfgtrksel.maxDcaZ); @@ -368,21 +328,19 @@ struct LongrangeMaker { itsNsigmaCut = itsNsigmaPidCut; tpcNsigmaCut = tpcNsigmaPidCut; - cfgSgCuts = (SGCutParHolder)sgCuts; + cfgSgCuts = SGCutParHolder(sgCuts); } Produces lrmccollision; Produces lrmidmctracks; Produces lrft0amctracks; Produces lrft0cmctracks; - Produces lrmftmctracks; Produces lrcollision; Produces lrcollisionMcLabel; Produces lrmidtracks; Produces lrft0atracks; Produces lrft0ctracks; - Produces lrmfttracks; Produces lrv0tracks; Produces upclrcollision; @@ -392,7 +350,6 @@ struct LongrangeMaker { Produces upclrmidtracks; Produces upclrft0atracks; Produces upclrft0ctracks; - Produces upclrmfttracks; Produces upclrv0tracks; Filter fTracksEta = nabs(aod::track::eta) < cfgtrksel.cfgEtaCut; @@ -400,13 +357,12 @@ struct LongrangeMaker { using CollTable = soa::Join; using TrksTable = soa::Filtered>; - using MftTrkTable = aod::MFTTracks; using BCs = soa::Join; - std::vector ft0gainvalues{}; - void processData(CollTable::iterator const& col, TrksTable const& tracks, - aod::FT0s const&, MftTrkTable const&, - soa::SmallGroups const& reassoMftTracks, + std::vector ft0gainvalues; + void processData(CollTable::iterator const& col, + TrksTable const& tracks, + aod::FT0s const&, aod::V0Datas const& V0s, aod::BCsWithTimestamps const&) { if (!isEventSelected(col)) { @@ -443,10 +399,12 @@ struct LongrangeMaker { // track loop for (const auto& track : tracks) { - if (!track.isGlobalTrack()) + if (!track.isGlobalTrack()) { continue; - if (!myTrackFilter.IsSelected(track)) + } + if (!myTrackFilter.IsSelected(track)) { continue; + } auto pid = getTrackPID(track); lrmidtracks(lrcollision.lastIndex(), track.tpcNClsFound(), @@ -549,28 +507,6 @@ struct LongrangeMaker { } } - // mft tracks - for (const auto& reassoMftTrack : reassoMftTracks) { - if (!isMftBestTrackSelected(reassoMftTrack)) - continue; - auto itrack = reassoMftTrack.template mfttrack_as(); - if (!isMftTrackSelected(itrack)) { - continue; - } - auto phi = itrack.phi(); - o2::math_utils::bringTo02Pi(phi); - lrmfttracks(lrcollision.lastIndex(), - reassoMftTrack.ambDegree(), - itrack.pt(), - itrack.eta(), - phi, - itrack.nClusters(), - reassoMftTrack.bestDCAXY(), - reassoMftTrack.bestDCAZ(), - itrack.isCA(), - itrack.collisionId() != reassoMftTrack.bestCollisionId()); - } - // v0 loop for (const auto& v0 : V0s) { if (!isSelectV0Track(v0)) { // Quality selection for V0 prongs @@ -625,9 +561,7 @@ struct LongrangeMaker { void processUpc(CollTable::iterator const& col, BCs const& bcs, TrksTable const& tracks, aod::Zdcs const&, aod::FV0As const& fv0as, aod::FT0s const& ft0s, - aod::FDDs const& fdds, MftTrkTable const&, - soa::SmallGroups const& reassoMftTracks, - aod::V0Datas const& V0s) + aod::FDDs const& fdds, aod::V0Datas const& V0s) { if (!isEventSelected(col)) { return; @@ -647,9 +581,10 @@ struct LongrangeMaker { histos.fill(HIST("hSelectionResult"), isSGEvent.value); if (issgevent <= cfgGapSide) { - if (cfgSgCuts.minRgtrwTOF()) { - if (udhelpers::rPVtrwTOF(tracks, col.numContrib()) < cfgSgCuts.minRgtrwTOF()) + if (cfgSgCuts.minRgtrwTOF() != 0.0f) { + if (udhelpers::rPVtrwTOF(tracks, col.numContrib()) < cfgSgCuts.minRgtrwTOF()) { return; + } } upchelpers::FITInfo fitInfo{}; @@ -666,10 +601,12 @@ struct LongrangeMaker { // track loop for (const auto& track : tracks) { - if (!track.isGlobalTrack()) + if (!track.isGlobalTrack()) { continue; - if (!myTrackFilter.IsSelected(track)) + } + if (!myTrackFilter.IsSelected(track)) { continue; + } auto pid = getTrackPID(track); upclrmidtracks(upclrcollision.lastIndex(), track.tpcNClsFound(), @@ -729,27 +666,6 @@ struct LongrangeMaker { } } - for (const auto& reassoMftTrack : reassoMftTracks) { - if (!isMftBestTrackSelected(reassoMftTrack)) - continue; - auto itrack = reassoMftTrack.template mfttrack_as(); - if (!isMftTrackSelected(itrack)) { - continue; - } - auto phi = itrack.phi(); - o2::math_utils::bringTo02Pi(phi); - upclrmfttracks(upclrcollision.lastIndex(), - reassoMftTrack.ambDegree(), - itrack.pt(), - itrack.eta(), - phi, - itrack.nClusters(), - reassoMftTrack.bestDCAXY(), - reassoMftTrack.bestDCAZ(), - itrack.isCA(), - itrack.collisionId() != reassoMftTrack.bestCollisionId()); - } - // v0 loop for (const auto& v0 : V0s) { if (!isSelectV0Track(v0)) { // Quality selection for V0 prongs @@ -805,18 +721,20 @@ struct LongrangeMaker { using ColMCTrueTable = soa::Join; using ColMCRecTable = soa::SmallGroups>; using TrksMCRecTable = soa::Filtered>; - using MftTrkMCRecTable = soa::Join; Preslice perColMidtrack = aod::track::collisionId; - PresliceUnsorted perColMftTrack = aod::fwdtrack::bestCollisionId; - void processMCRec(ColMCTrueTable::iterator const& mcCollision, ColMCRecTable const& RecCols, TrksMCRecTable const& RecTracks, - aod::FT0s const&, MftTrkMCRecTable const&, aod::BestCollisionsFwd3d const& reassoMftTracks, - aod::McParticles const& mcparticles, aod::BCsWithTimestamps const&) + void processMCRec(ColMCTrueTable::iterator const& mcCollision, + ColMCRecTable const& RecCols, + TrksMCRecTable const& RecTracks, + aod::FT0s const&, + aod::McParticles const& mcparticles, + aod::BCsWithTimestamps const&) { auto multiplicity = 0; for (const auto& particle : mcparticles) { - if (!isGenPartSelected(particle) || std::abs(particle.eta()) > cfgtrksel.cfgEtaCut || particle.pt() < cfgtrksel.cfgPtCutMinForMult || particle.pt() > cfgtrksel.cfgPtCutMaxForMult) + if (!isGenPartSelected(particle) || std::abs(particle.eta()) > cfgtrksel.cfgEtaCut || particle.pt() < cfgtrksel.cfgPtCutMinForMult || particle.pt() > cfgtrksel.cfgPtCutMaxForMult) { continue; + } multiplicity++; } lrmccollision(mcCollision.posZ(), multiplicity, mcCollision.multMCFT0A(), mcCollision.multMCFT0C()); @@ -854,15 +772,19 @@ struct LongrangeMaker { // track loop for (const auto& track : recTracksPart) { - if (!track.isGlobalTrack()) + if (!track.isGlobalTrack()) { continue; - if (!myTrackFilter.IsSelected(track)) + } + if (!myTrackFilter.IsSelected(track)) { continue; - if (!track.has_mcParticle()) + } + if (!track.has_mcParticle()) { continue; + } auto mcpart = track.mcParticle(); - if (RecCol.mcCollisionId() != mcpart.mcCollisionId()) + if (RecCol.mcCollisionId() != mcpart.mcCollisionId()) { continue; + } auto pid = getTrackPID(track); lrmidtracks(lrcollision.lastIndex(), track.tpcNClsFound(), @@ -959,43 +881,21 @@ struct LongrangeMaker { } } - auto recMftTracksPart = reassoMftTracks.sliceBy(perColMftTrack, RecCol.globalIndex()); - for (const auto& reassoMftTrack : recMftTracksPart) { - if (!isMftBestTrackSelected(reassoMftTrack)) - continue; - auto itrack = reassoMftTrack.template mfttrack_as(); - if (!isMftTrackSelected(itrack)) { - continue; - } - auto phi = itrack.phi(); - o2::math_utils::bringTo02Pi(phi); - lrmfttracks(lrcollision.lastIndex(), - reassoMftTrack.ambDegree(), - itrack.pt(), - itrack.eta(), - phi, - itrack.nClusters(), - reassoMftTrack.bestDCAXY(), - reassoMftTrack.bestDCAZ(), - itrack.isCA(), - itrack.collisionId() != reassoMftTrack.bestCollisionId()); - } - for (const auto& particle : mcparticles) { if (!isGenPartSelected(particle)) { continue; } // Fill midrapidity tracks - if (std::abs(particle.eta()) < cfgtrksel.cfgEtaCut && particle.pt() > cfgtrksel.cfgPtCutMin && particle.pt() < cfgtrksel.cfgPtCutMax) + if (std::abs(particle.eta()) < cfgtrksel.cfgEtaCut && particle.pt() > cfgtrksel.cfgPtCutMin && particle.pt() < cfgtrksel.cfgPtCutMax) { lrmidmctracks(lrmccollision.lastIndex(), particle.pt(), particle.eta(), particle.phi(), particle.pdgCode(), particle.flags()); + } // Fill FT0 tracks - if (cfgfittrksel.cfgFt0aEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfgFt0aEtaMax) + if (cfgfittrksel.cfgFt0aEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfgFt0aEtaMax) { lrft0amctracks(lrmccollision.lastIndex(), particle.pt(), particle.eta(), particle.phi()); - if (cfgfittrksel.cfgFt0cEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfgFt0cEtaMax) + } + if (cfgfittrksel.cfgFt0cEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfgFt0cEtaMax) { lrft0cmctracks(lrmccollision.lastIndex(), particle.pt(), particle.eta(), particle.phi()); - // Fill MFT tracks - if (cfgmfttrksel.cfgMftEtaMin < particle.eta() && particle.eta() < cfgmfttrksel.cfgMftEtaMax && particle.pt() > cfgmfttrksel.cfgMftPtCutMin && particle.pt() < cfgmfttrksel.cfgMftPtCutMax) - lrmftmctracks(lrmccollision.lastIndex(), particle.pt(), particle.eta(), particle.phi()); + } } } } @@ -1006,14 +906,18 @@ struct LongrangeMaker { auto multMCFT0A = 0; auto multMCFT0C = 0; for (const auto& particle : mcparticles) { - if (!isGenPartSelected(particle)) + if (!isGenPartSelected(particle)) { continue; - if (std::abs(particle.eta()) < cfgtrksel.cfgEtaCut && particle.pt() > cfgtrksel.cfgPtCutMinForMult && particle.pt() < cfgtrksel.cfgPtCutMaxForMult) + } + if (std::abs(particle.eta()) < cfgtrksel.cfgEtaCut && particle.pt() > cfgtrksel.cfgPtCutMinForMult && particle.pt() < cfgtrksel.cfgPtCutMaxForMult) { multiplicity++; - if (cfgfittrksel.cfgFt0cEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfgFt0cEtaMax) + } + if (cfgfittrksel.cfgFt0cEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfgFt0cEtaMax) { multMCFT0C++; - if (cfgfittrksel.cfgFt0aEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfgFt0aEtaMax) + } + if (cfgfittrksel.cfgFt0aEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfgFt0aEtaMax) { multMCFT0A++; + } } lrmccollision(mcCollision.posZ(), multiplicity, multMCFT0A, multMCFT0C); @@ -1022,16 +926,16 @@ struct LongrangeMaker { continue; } // Fill midrapidity tracks - if (std::abs(particle.eta()) < cfgtrksel.cfgEtaCut && particle.pt() > cfgtrksel.cfgPtCutMin && particle.pt() < cfgtrksel.cfgPtCutMax) + if (std::abs(particle.eta()) < cfgtrksel.cfgEtaCut && particle.pt() > cfgtrksel.cfgPtCutMin && particle.pt() < cfgtrksel.cfgPtCutMax) { lrmidmctracks(lrmccollision.lastIndex(), particle.pt(), particle.eta(), particle.phi(), particle.pdgCode(), particle.flags()); + } // Fill FT0 tracks - if (cfgfittrksel.cfgFt0aEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfgFt0aEtaMax) + if (cfgfittrksel.cfgFt0aEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfgFt0aEtaMax) { lrft0amctracks(lrmccollision.lastIndex(), particle.pt(), particle.eta(), particle.phi()); - if (cfgfittrksel.cfgFt0cEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfgFt0cEtaMax) + } + if (cfgfittrksel.cfgFt0cEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfgFt0cEtaMax) { lrft0cmctracks(lrmccollision.lastIndex(), particle.pt(), particle.eta(), particle.phi()); - // Fill MFT tracks - if (cfgmfttrksel.cfgMftEtaMin < particle.eta() && particle.eta() < cfgmfttrksel.cfgMftEtaMax && particle.pt() > cfgmfttrksel.cfgMftPtCutMin && particle.pt() < cfgmfttrksel.cfgMftPtCutMax) - lrmftmctracks(lrmccollision.lastIndex(), particle.pt(), particle.eta(), particle.phi()); + } } } @@ -1046,9 +950,9 @@ struct LongrangeMaker { multiplicity = countNTracks(tracks, col.posZ()); } - bool checkK0s = (cfgv0trksel.cfgQAV0Mask & 1); - bool checkLam = (cfgv0trksel.cfgQAV0Mask & 2); - bool checkALam = (cfgv0trksel.cfgQAV0Mask & 4); + bool checkK0s = (cfgv0trksel.cfgQAV0Mask & 1) != 0; + bool checkLam = (cfgv0trksel.cfgQAV0Mask & 2) != 0; + bool checkALam = (cfgv0trksel.cfgQAV0Mask & 4) != 0; for (const auto& v0 : V0s) { if (!isSelectV0Track(v0)) { @@ -1202,16 +1106,19 @@ struct LongrangeMaker { { float nTrk = 0.f; for (const auto& track : tracks) { - if (!track.isGlobalTrack()) + if (!track.isGlobalTrack()) { continue; - if (!myTrackFilter.IsSelected(track)) + } + if (!myTrackFilter.IsSelected(track)) { continue; + } if (track.pt() < cfgtrksel.cfgPtCutMinForMult || track.pt() > cfgtrksel.cfgPtCutMaxForMult) { continue; } float trkeff = 1.0f; - if (cfgtrksel.applyEffCorr) + if (cfgtrksel.applyEffCorr) { trkeff = getTrkEffCorr(vz, track.eta(), track.pt()); + } nTrk += trkeff; } return nTrk; @@ -1245,7 +1152,9 @@ struct LongrangeMaker { std::array nSigmaITS = {itsResponse.nSigmaITS(track), itsResponse.nSigmaITS(track), itsResponse.nSigmaITS(track)}; std::array nSigmaToUse = isUseItsPid ? nSigmaITS : nSigmaTPC; // Choose which nSigma to use: TPC or ITS std::vector detectorNsigmaCut = isUseItsPid ? itsNsigmaCut : tpcNsigmaCut; // Choose which nSigma to use: TPC or ITS - bool isPion, isKaon, isProton; + bool isPion = false; + bool isKaon = false; + bool isProton = false; bool isDetectedPion = nSigmaToUse[0] < detectorNsigmaCut[0] && nSigmaToUse[0] > detectorNsigmaCut[0 + 3]; bool isDetectedKaon = nSigmaToUse[1] < detectorNsigmaCut[1] && nSigmaToUse[1] > detectorNsigmaCut[1 + 3]; bool isDetectedProton = nSigmaToUse[2] < detectorNsigmaCut[2] && nSigmaToUse[2] > detectorNsigmaCut[2 + 3]; @@ -1256,7 +1165,8 @@ struct LongrangeMaker { if (track.pt() > cfgTofPidPtCut && !track.hasTOF()) { return aod::lrcorrtrktable::kNoPid; - } else if (track.pt() > cfgTofPidPtCut && track.hasTOF()) { + } + if (track.pt() > cfgTofPidPtCut && track.hasTOF()) { isPion = isTofPion && isDetectedPion; isKaon = isTofKaon && isDetectedKaon; isProton = isTofProton && isDetectedProton; @@ -1272,31 +1182,33 @@ struct LongrangeMaker { if (isPion) { return aod::lrcorrtrktable::kSpPion; - } else if (isKaon) { + } + if (isKaon) { return aod::lrcorrtrktable::kSpKaon; - } else if (isProton) { + } + if (isProton) { return aod::lrcorrtrktable::kSpProton; - } else { - return aod::lrcorrtrktable::kNoPid; // no particle satisfies the criteria } + return aod::lrcorrtrktable::kNoPid; // no particle satisfies the criteria } int getFt0aDeadChannelId(int channelId) { - if (channelId >= MinFt0aMirrorChannelOuter && channelId <= MaxFt0aMirrorChannelOuter) + if (channelId >= MinFt0aMirrorChannelOuter && channelId <= MaxFt0aMirrorChannelOuter) { return MinFt0aDeadChannelOuter + (channelId - MinFt0aMirrorChannelOuter); - else - return -1; + } + return -1; } int getFt0cDeadChannelId(int channelId) { - if (channelId >= MinFt0cMirrorChannelOuter && channelId <= MaxFt0cMirrorChannelOuter) + if (channelId >= MinFt0cMirrorChannelOuter && channelId <= MaxFt0cMirrorChannelOuter) { return MinFt0cDeadChannelOuter + (channelId - MinFt0cMirrorChannelOuter); - else if (channelId == Ft0cMirrorChannelInner) + } + if (channelId == Ft0cMirrorChannelInner) { return Ft0cDeadChannelInner; - else - return -1; + } + return -1; } double getPhiFT0(uint chno, int i) @@ -1313,85 +1225,14 @@ struct LongrangeMaker { auto x = chPos.X() + (*offsetFT0)[i].getX(); auto y = chPos.Y() + (*offsetFT0)[i].getY(); auto z = chPos.Z() + (*offsetFT0)[i].getZ(); - if (chno >= KminFt0cCell) + if (chno >= KminFt0cCell) { z = -z; + } auto r = std::sqrt(x * x + y * y); auto theta = std::atan2(r, z); return -std::log(std::tan(0.5 * theta)); } - template - bool isMftTrackSelected(CheckMftTrack const& track) - { - if constexpr (fillHis) { - histos.fill(HIST("hMftTrkSel"), 1); - } - if (track.nClusters() < cfgmfttrksel.cfgMftCluster) { - return false; - } - if constexpr (fillHis) { - histos.fill(HIST("hMftTrkSel"), 2); - } - if (cfgmfttrksel.cfgUseChi2Cut) { - float nclMft = std::max(2.0f * track.nClusters() - 5.0f, 1.0f); - float mftChi2NCl = track.chi2() / nclMft; - if (mftChi2NCl > cfgmfttrksel.cfgMaxChi2NCl) - return false; - } - if constexpr (fillHis) { - histos.fill(HIST("hMftTrkSel"), 3); - } - if (track.eta() > cfgmfttrksel.cfgMftEtaMax || track.eta() < cfgmfttrksel.cfgMftEtaMin) { - return false; - } - if constexpr (fillHis) { - histos.fill(HIST("hMftTrkSel"), 4); - } - if (cfgmfttrksel.useMftPtCut && (track.pt() < cfgmfttrksel.cfgMftPtCutMin || track.pt() > cfgmfttrksel.cfgMftPtCutMax)) { - return false; - } - if constexpr (fillHis) { - histos.fill(HIST("hMftTrkSel"), 5); - } - if constexpr (fillHis) { - histos.fill(HIST("hMftTrkSel"), 6); - } - return true; - } - - template - bool isMftBestTrackSelected(CheckMftTrack const& mfttrack) - { - if constexpr (fillHis) { - histos.fill(HIST("hMftBestTrkSel"), 1); - } - if (mfttrack.bestCollisionId() < CintZero) { - return false; - } - if constexpr (fillHis) { - histos.fill(HIST("hMftBestTrkSel"), 2); - } - if (mfttrack.ambDegree() == CintZero) { - return false; - } - if constexpr (fillHis) { - histos.fill(HIST("hMftBestTrkSel"), 3); - } - if (std::abs(mfttrack.bestDCAXY()) >= cfgmfttrksel.cfgMftDcaxy) { - return false; - } - if constexpr (fillHis) { - histos.fill(HIST("hMftBestTrkSel"), 4); - } - if (std::abs(mfttrack.bestDCAZ()) >= cfgmfttrksel.cfgMftDcaz) { - return false; - } - if constexpr (fillHis) { - histos.fill(HIST("hMftBestTrkSel"), 5); - } - return true; - } - template bool isSelectV0Track(const T1& v0) { @@ -1520,7 +1361,7 @@ struct LongrangeMaker { if (fLoadTrkEffCorr) { return; } - if (cfgtrksel.cfgEffccdbPath.value.empty() == false) { + if (!cfgtrksel.cfgEffccdbPath.value.empty()) { hTrkEff = ccdb->getForTimeStamp(cfgtrksel.cfgEffccdbPath, timestamp); if (hTrkEff == nullptr) { LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgtrksel.cfgEffccdbPath.value.c_str()); @@ -1552,8 +1393,8 @@ struct LongrangeMaker { PROCESS_SWITCH(LongrangeMaker, processV0QA, "process V0 QA histograms", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(context)}; } diff --git a/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt b/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt index d38f6e57f9b..83f52b206e0 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt +++ b/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt @@ -45,7 +45,7 @@ o2physics_add_dpl_workflow(dpt-dpt-per-run-extra-qc o2physics_add_dpl_workflow(corr-reso SOURCES corrReso.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore O2Physics::TwoPartCorrCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(neutron-proton-corr-zdc @@ -60,7 +60,7 @@ o2physics_add_dpl_workflow(di-hadron-cor o2physics_add_dpl_workflow(pid-di-hadron SOURCES pidDiHadron.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore O2Physics::AnalysisCCDB O2Physics::GFWCore + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore O2Physics::TwoPartCorrCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(longrange-correlation @@ -78,6 +78,11 @@ o2physics_add_dpl_workflow(long-range-dihadron-cor PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore O2Physics::AnalysisCCDB O2Physics::GFWCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(mft-correlations + SOURCES mftcorrelations.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(two-particle-correlation-pp SOURCES twoParticleCorrelationPp.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGCFCore @@ -137,3 +142,23 @@ o2physics_add_dpl_workflow(lambda-or-anti-lambda-producer-with-spin SOURCES lambdaOrAntiLambdaProducerWithSpin.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(fox-wolfram-correlation + SOURCES FoxWolframCorrelation.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(two-particle-correlations-mult-spher + SOURCES twoParticleCorrelationsMultSpher.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(hparticle-correlation + SOURCES hParticleCorrelation.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(pid-long-range + SOURCES pidLongRange.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore O2Physics::TwoPartCorrCore O2Physics::AnalysisCCDB O2Physics::GFWCore + COMPONENT_NAME Analysis) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx b/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx new file mode 100644 index 00000000000..7cd6f5fdf87 --- /dev/null +++ b/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx @@ -0,0 +1,1301 @@ +// Copyright 2020-2022 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file FoxWolframCorrelation.cxx +/// \author Andreicovici Iulian Florin +/// \brief RECO-level event shape analysis using Fox-Wolfram Moments for O+O collisions. +/// Classifies events by topology (triangular), fills correlation histograms, +/// and produces custom SOA tables for mixed-event background estimation. +/// \since 06 Nov 2026 + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::aod::track; +using namespace o2::soa; + +// Define compact event and track tables for mixed-event processing. +namespace o2::aod +{ +namespace collision +{ +DECLARE_SOA_COLUMN(Multip, multip, int); +} + +DECLARE_SOA_TABLE(TriangleCollisions, "AOD", "TRIANGLECOLLISION", + o2::soa::Index<>, + collision::PosZ, + collision::Multip); + +using TriangleCollision = TriangleCollisions::iterator; + +namespace track_tri +{ +DECLARE_SOA_INDEX_COLUMN(TriangleCollision, triangleCollision); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +} // namespace track_tri + +DECLARE_SOA_TABLE(TriangleTracks, "AOD", "TRITRACK", + o2::soa::Index<>, + track_tri::TriangleCollisionId, + track_tri::Pt, track_tri::Eta, track_tri::Phi); + +} // namespace o2::aod + +// Wrap the azimuthal difference to the correlation interval. +double computeDeltaPhi(double phi1, double phi2) +{ + return RecoDecay::constrainAngle(phi1 - phi2, -o2::constants::math::PIHalf); +} + +// Name the available pair-selection modes. +enum LeadAssocMode : int { // 3 distinct modes + kAllPairs = 0, + kpTLeadConstrain = 1, + kpTLeadFree = 2 +}; + +// Name the available event-topology selections. +enum TopologyMode : int { + kAllEvSetNom = 0, + kTriEvSetNom = 1, + kIsoEvSetNom = 2, + kTriEvSet1p = 3, + kTriEvSet2p = 4, + kTriEvSet3p = 5, + kTriEvSet1m = 6, + kTriEvSet2m = 7, + kTriEvSet3m = 8 +}; + +template +// Apply the same pair-selection rules to signal and background. +bool passesLeadAssoc(int mode, bool foundPart, Track const& track1, Track const& track2, + TLead const& triggTrk, TLead const& triggTrkPart) +{ + if (mode == kAllPairs) { // all i-j pairs + return (track1.pt() < 3.f) && (track2.pt() < 3.f); + } + if (mode == kpTLeadConstrain) { // L-A: Leading pt in (2,3), Assoc pt < 2 + return foundPart && (track1.globalIndex() == triggTrkPart.globalIndex()) && (track2.pt() < 2.f); + } + if (mode == kpTLeadFree) { // L-A + return (track1.globalIndex() == triggTrk.globalIndex()) && (track2.pt() < track1.pt()); + } + + return false; +} + +template +// Find the overall leading track and the leading track in the selected momentum window. +auto findLeadingPart(T const& tracks) +{ + int leadingAllId = 0, leadingPartId = 0; + int i = 0; + + double ptMaxAll = -1., ptMaxPart = -1.; + bool foundPart = false; + + for (const auto& track : tracks) { + // pT Leading (no constraint) + if (track.pt() > ptMaxAll) { + ptMaxAll = track.pt(); + leadingAllId = i; + } + + // pT Leading in (2,3) + if (track.pt() > 2.f && track.pt() < 3.f && track.pt() > ptMaxPart) { + ptMaxPart = track.pt(); + leadingPartId = i; + foundPart = true; + } + i++; + } + + return std::make_tuple(tracks.iteratorAt(leadingAllId), tracks.iteratorAt(leadingPartId), foundPart); +} + +// Store selected tracks and collisions across data frames. +using TrackTuple = std::tuple; +using CollisionTuple = std::tuple>; + +// Define the reconstructed collision and track views. +using RecCollisions = soa::Join; +using CollisionRecTable = RecCollisions::iterator; +using RecTracks = soa::Join; + +// Select events, compute moments, and fill same-event observables. +struct FoxWolframCorrelation { + + SliceCache cache; + Preslice perCollision = aod::track::collisionId; + + Produces triCollisions; + Produces triTracks; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + //// Separate the 120 folded spectra to stay below the registry capacity //// + HistogramRegistry foldedHistos{"foldedHistos", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; + + // Configure histogram axes, event selection, and topology. + Configurable vtxRange{"vtxRange", 10.0f, "Vertex Z range to consider"}; + Configurable etaRange{"etaRange", 0.8f, "eta range to consider"}; + Configurable nSelEv{"nSelEv", 200, "no. of mixing Ev."}; + Configurable doSameEvent{"doSameEvent", true, "Fill same-event TRI correlations"}; + Configurable doFWMCorrelations{"doFWMCorrelations", false, "Fill H_i-H_j corr. hist"}; + Configurable topologyMode{"topologyMode", kTriEvSetNom, + "SelOpt: 0 AllEv, 1 TriNom, 2 IsoNom, 3 TriSet1p, 4 TriSet2p, 5 TriSet3p, 6 TriSet1m, 7 TriSet2m, 8 TriSet3m"}; + + // Choose the pair selection for same-event correlations. + Configurable leadAssocMode{"leadAssocMode", 2, "0: ALL i-j pairs (pT<3); 1: pTLead in (2,3) & pTAssoc < 2; 2: pTAssoc < pTLead"}; + + Configurable isVtxRange{"isVtxRange", true, "isVtxRange"}; + Configurable isSel8{"isSel8", true, "isSel8"}; + Configurable isNoSameBunchPileup{"isNoSameBunchPileup", true, "isNoSameBunchPileup"}; + Configurable isGoodZvtxFT0vsPV{"isGoodZvtxFT0vsPV", true, "isGoodZvtxFT0vsPV"}; + Configurable isVertexITSTPC{"isVertexITSTPC", false, "isVertexITSTPC"}; + Configurable isVertexTOFmatched{"isVertexTOFmatched", false, "isVertexTOFmatched"}; + Configurable kIsGoodITSLayer0123{"kIsGoodITSLayer0123", false, "kIsGoodITSLayer0123"}; + Configurable isNoCollInTimeRangeNarrow{"isNoCollInTimeRangeNarrow", false, "isNoCollInTimeRangeNarrow"}; + + //// Configurable FWM limits: H1, H2, H3, H4, H5, H6, H7, H8 //// + Configurable> kTriFWMSetNom{"kTriFWMSetNom", {0.05, 0.30, 0.040, 0.190, 0.020, 0.150, 0.016, 0.120}, "kTriFWMSetNom"}; + + Configurable> kTriFWMSet1p{"kTriFWMSet1p", {0.0525, 0.315, 0.0380, 0.1995, 0.0190, 0.1575, 0.0152, 0.1260}, "kTriFWMSet1p"}; + Configurable> kTriFWMSet2p{"kTriFWMSet2p", {0.0550, 0.330, 0.0360, 0.2090, 0.0180, 0.1650, 0.0144, 0.1320}, "kTriFWMSet2p"}; + Configurable> kTriFWMSet3p{"kTriFWMSet3p", {0.0575, 0.345, 0.0340, 0.2185, 0.0170, 0.1725, 0.0136, 0.1380}, "kTriFWMSet3p"}; + + Configurable> kTriFWMSet1m{"kTriFWMSet1m", {0.0475, 0.2850, 0.0420, 0.1805, 0.0210, 0.1425, 0.0168, 0.1140}, "kTriFWMSet1m"}; + Configurable> kTriFWMSet2m{"kTriFWMSet2m", {0.0450, 0.2700, 0.0440, 0.1710, 0.0220, 0.1350, 0.0176, 0.1080}, "kTriFWMSet2m"}; + Configurable> kTriFWMSet3m{"kTriFWMSet3m", {0.0425, 0.2550, 0.0460, 0.1615, 0.0230, 0.1275, 0.0184, 0.1020}, "kTriFWMSet3m"}; + + //// Named TRI selection indices //// + enum TriSelection { + kTriNominal, + kTriSet1p, + kTriSet2p, + kTriSet3p, + kTriSet1m, + kTriSet2m, + kTriSet3m, + kNTriSel + }; + using TriFlags = std::array; + + ConfigurableAxis cfgAxisPt{"cfgAxisPt", {VARIABLE_WIDTH, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 8.0, 9.0, 10.0}, "p_{T} Bins"}; + ConfigurableAxis cfgAxisMultip{"cfgAxisMultip", {351, -0.5, 350.5}, "Multiplicity Bins"}; + + Configurable> isoFWMnominal{"isoFWMnominal", {0.02, 0.27, 0.02, 0.16, 0.02, 0.12, 0.02, 0.10}, "isoFWMnominal"}; + + // Multiplicity bins: bin i is [multLower[i], multUpper[i]] (inclusive limits). + // The first three entries are the broad ranges (RANGE1-3: FWM_BIN1-3, lead-assoc, mixing buffers), + // the remaining ones are the finer intervals (BIN4...). Bins may overlap. Max 10 bins. + // The same limits must be given to FoxWolframCorrelationMixing. + Configurable> multLower{"multLower", {40, 86, 141, 40, 61, 81, 101, 121, 141, 161}, "lower multiplicity limit of each bin (inclusive)"}; + Configurable> multUpper{"multUpper", {85, 140, 350, 60, 80, 100, 120, 140, 160, 350}, "upper multiplicity limit of each bin (inclusive)"}; + + static constexpr int MaxMultBins = 10; + int nMultBins = 0; // set in init() from multLower/multUpper + + static constexpr std::array binNames = { + "BIN1", "BIN2", "BIN3", "BIN4", "BIN5", + "BIN6", "BIN7", "BIN8", "BIN9", "BIN10"}; + + //// Class-level names for compile-time HIST lookups in nested loops //// + static constexpr std::array nearNames = { + "nearPtD0_", "nearPtD1_", "nearPtD2_", + "nearPtD3_", "nearPtD4_", "nearPtD5_"}; + + static constexpr std::array awayNames = { + "awayPtD0_", "awayPtD1_", "awayPtD2_", + "awayPtD3_", "awayPtD4_", "awayPtD5_"}; + + static constexpr std::array allLRNames = { + "allLRNear", "allLRValley", "allLRAway"}; + + static constexpr std::array allSRNames = { + "allSRNear", "allSRValley", "allSRAway"}; + + static constexpr std::array triLRNames = { + "triLRPeak1", "triLRPeak2", "triLRPeak3", "triLRValleys"}; + + static constexpr std::array triSRNames = { + "triSRPeak1", "triSRPeak2", "triSRPeak3", "triSRValleys"}; + + // Map each unique pair of Fox-Wolfram moments to its histogram. + static constexpr std::array fwmRangeNames = {"FWM_BIN1/", "FWM_BIN2/", "FWM_BIN3/"}; + static constexpr std::array fwmHNames = {"H1", "H2", "H3", "H4", "H5", "H6", "H7", "H8"}; + static constexpr std::array fwmMultHNames = {"Multip_H1", "Multip_H2", "Multip_H3", "Multip_H4", "Multip_H5", "Multip_H6", "Multip_H7", "Multip_H8"}; + + static constexpr std::array fwmCorrelationNames = {"H1_H2", "H1_H3", "H1_H4", "H1_H5", "H1_H6", "H1_H7", "H1_H8", "H2_H3", "H2_H4", "H2_H5", "H2_H6", "H2_H7", "H2_H8", "H3_H4", "H3_H5", "H3_H6", "H3_H7", "H3_H8", "H4_H5", "H4_H6", "H4_H7", "H4_H8", "H5_H6", "H5_H7", "H5_H8", "H6_H7", "H6_H8", "H7_H8"}; + static constexpr std::array fwmCorrelationFirst = {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 6}; + static constexpr std::array fwmCorrelationSecond = {1, 2, 3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 7, 3, 4, 5, 6, 7, 4, 5, 6, 7, 5, 6, 7, 6, 7, 7}; + + // Register event, track, moment, and correlation histograms. + void init(InitContext const&) + { + // Reject unsupported topology modes at startup. + if (topologyMode.value < kAllEvSetNom || topologyMode.value > kTriEvSet3m) { + LOGF(fatal, "Invalid topologyMode=%d. Allowed values: 0 ALL, 1 TRI nominal, 2 ISO, 3 TRI 1p, 4 TRI 2p, 5 TRI 3p, 6 TRI 1m, 7 TRI 2m, 8 TRI 3m", topologyMode.value); + } + + // Validate the configurable multiplicity bins. + if (multLower.value.size() != multUpper.value.size() || multLower.value.empty() || multLower.value.size() > static_cast(MaxMultBins)) { + LOGF(fatal, "multLower/multUpper must have the same size, between 1 and %d entries", MaxMultBins); + } + nMultBins = static_cast(multLower.value.size()); + if (nMultBins < 3) { + LOGF(fatal, "At least three broad multiplicity ranges are required"); + } + for (int i = 0; i < nMultBins; ++i) { + if (multLower.value[i] > multUpper.value[i]) { + LOGF(fatal, "Multiplicity bin %d has multLower (%d) > multUpper (%d)", i + 1, multLower.value[i], multUpper.value[i]); + } + } + + // Define axes shared by the registered histograms. + const AxisSpec axispT{cfgAxisPt, "p_{T} (GeV/c)"}; + const AxisSpec axisMultip{cfgAxisMultip, "Multiplicity"}; + const AxisSpec axisDeltaEta{32, -1.6, +1.6, "#Delta#eta"}; + const AxisSpec axisDeltaPhi{72, -constants::math::PIHalf, +3 * constants::math::PIHalf, "#Delta#phi"}; + const AxisSpec axisPhi{100, 0, constants::math::TwoPI, "#phi"}; + const AxisSpec axisEta{40, -0.8, 0.8, "#eta"}; + const AxisSpec axisHlrec{50, 0, 1, "H_{l}"}; + const AxisSpec axisHlfine{100, 0, 1, "H_{l}"}; + const AxisSpec axisEvent{10, 0.5, 10.5, "", "EventAxis"}; + const AxisSpec axisRecoil{100, 0., 1, "Recoil"}; + + // Label same-event pair counts by multiplicity interval. + histos.add("SamePairs/SamePairsNoLA", "SamePairsNoLA", kTH1D, {axisEvent}, false); + auto* samePairsNoLA = histos.get(HIST("SamePairs/SamePairsNoLA"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + samePairsNoLA->SetBinLabel(i + 1, i < 3 ? fmt::format("samePairsLA_RANGE{}", i + 1).c_str() : fmt::format("samePairsLA_BIN{}", i + 1).c_str()); + } + + // ALL Ev per Sel. BIN + histos.add("Events_Details/allEvSetNom", "SetAllNominal", kTH1D, {axisEvent}, false); + auto* allEvSetNom = histos.get(HIST("Events_Details/allEvSetNom"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + allEvSetNom->SetBinLabel(i + 1, i < 3 ? fmt::format("RANGE{}", i + 1).c_str() : fmt::format("BIN{}", i + 1).c_str()); + } + + // ISO Ev per Sel. BIN + histos.add("Events_Details/eventsNoISO", "SetIsoNominal", kTH1D, {axisEvent}, false); + auto* eventsNoISO = histos.get(HIST("Events_Details/eventsNoISO"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + eventsNoISO->SetBinLabel(i + 1, i < 3 ? fmt::format("RANGE{}", i + 1).c_str() : fmt::format("BIN{}", i + 1).c_str()); + } + + // TRI Ev per Sel. BIN (NOMINAL) + histos.add("Events_Details/triEvSetNom", "SetTriNominal", kTH1D, {axisEvent}, false); + auto* triEvSetNom = histos.get(HIST("Events_Details/triEvSetNom"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + triEvSetNom->SetBinLabel(i + 1, i < 3 ? fmt::format("RANGE{}", i + 1).c_str() : fmt::format("BIN{}", i + 1).c_str()); + } + + // TRI Ev per Sel. BIN (SET1PLUS) + histos.add("Events_Details/triEvSet1p", "Set1TriPlus", kTH1D, {axisEvent}, false); + auto* triEvSet1p = histos.get(HIST("Events_Details/triEvSet1p"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + triEvSet1p->SetBinLabel(i + 1, i < 3 ? fmt::format("RANGE{}", i + 1).c_str() : fmt::format("BIN{}", i + 1).c_str()); + } + + // TRI Ev per Sel. BIN (SET2PLUS) + histos.add("Events_Details/triEvSet2p", "Set2TriPlus", kTH1D, {axisEvent}, false); + auto* triEvSet2p = histos.get(HIST("Events_Details/triEvSet2p"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + triEvSet2p->SetBinLabel(i + 1, i < 3 ? fmt::format("RANGE{}", i + 1).c_str() : fmt::format("BIN{}", i + 1).c_str()); + } + + // TRI Ev per Sel. BIN (SET3PLUS) + histos.add("Events_Details/triEvSet3p", "Set3TriPlus", kTH1D, {axisEvent}, false); + auto* triEvSet3p = histos.get(HIST("Events_Details/triEvSet3p"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + triEvSet3p->SetBinLabel(i + 1, i < 3 ? fmt::format("RANGE{}", i + 1).c_str() : fmt::format("BIN{}", i + 1).c_str()); + } + + // TRI Ev per Sel. BIN (SET1MINUS) + histos.add("Events_Details/triEvSet1m", "Set1TriMinus", kTH1D, {axisEvent}, false); + auto* triEvSet1m = histos.get(HIST("Events_Details/triEvSet1m"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + triEvSet1m->SetBinLabel(i + 1, i < 3 ? fmt::format("RANGE{}", i + 1).c_str() : fmt::format("BIN{}", i + 1).c_str()); + } + + // TRI Ev per Sel. BIN (SET2MINUS) + histos.add("Events_Details/triEvSet2m", "Set2TriMinus", kTH1D, {axisEvent}, false); + auto* triEvSet2m = histos.get(HIST("Events_Details/triEvSet2m"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + triEvSet2m->SetBinLabel(i + 1, i < 3 ? fmt::format("RANGE{}", i + 1).c_str() : fmt::format("BIN{}", i + 1).c_str()); + } + + // TRI Ev per Sel. BIN (SET3MINUS) + histos.add("Events_Details/triEvSet3m", "Set3TriMinus", kTH1D, {axisEvent}, false); + auto* triEvSet3m = histos.get(HIST("Events_Details/triEvSet3m"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + triEvSet3m->SetBinLabel(i + 1, i < 3 ? fmt::format("RANGE{}", i + 1).c_str() : fmt::format("BIN{}", i + 1).c_str()); + } + + // Register inclusive multiplicity, track spectra, and recoil histograms. + histos.add("Multip/multipAllEv", "multipAllEv; trks; Events", kTH1D, {axisMultip}); + histos.add("Multip/multipTriEv", "multipTriEv; trks; Events", kTH1D, {axisMultip}); + + histos.add("Multip/nTrkTriEvS1p", "nTrkTriEvS1p", kTH1D, {axisMultip}); + histos.add("Multip/nTrkTriEvS2p", "nTrkTriEvS2p", kTH1D, {axisMultip}); + histos.add("Multip/nTrkTriEvS3p", "nTrkTriEvS3p", kTH1D, {axisMultip}); + histos.add("Multip/nTrkTriEvS1m", "nTrkTriEvS1m", kTH1D, {axisMultip}); + histos.add("Multip/nTrkTriEvS2m", "nTrkTriEvS2m", kTH1D, {axisMultip}); + histos.add("Multip/nTrkTriEvS3m", "nTrkTriEvS3m", kTH1D, {axisMultip}); + + // Assoc. pT spectra + histos.add("pTSpectra/pTAssocAllEv", "pTAssocAllEv", kTH1D, {axispT}); + histos.add("pTSpectra/pTAssocTriEv", "pTAssocTriEv", kTH1D, {axispT}); + histos.add("pTSpectra/meanpTAssocMultipAllEv", "meanpTAssocMultipAllEv", kTProfile, {axisMultip}); + histos.add("pTSpectra/meanpTAssocMultipTriEv", "meanpTAssocMultipTriEv", kTProfile, {axisMultip}); + + // Lead. pT spectra + histos.add("pTSpectra/pTLeadAllEv", "pTLeadAllEv", kTH1D, {axispT}); + histos.add("pTSpectra/pTLeadTriEv", "pTLeadTriEv", kTH1D, {axispT}); + histos.add("pTSpectra/ptLeadMultipAllEv", "ptLeadMultipAllEv", kTH2D, {axisMultip, axispT}); + histos.add("pTSpectra/ptLeadMultipTriEv", "ptLeadMultipTriEv", kTH2D, {axisMultip, axispT}); + histos.add("pTSpectra/meanptLeadMultipAllEv", "meanptLeadMultipAllEv", kTProfile, {axisMultip}); + histos.add("pTSpectra/meanptLeadMultipTriEv", "meanptLeadMultipTriEv", kTProfile, {axisMultip}); + + histos.add("pTSpectra/RecoilAllEv", "RecoilAllEv", kTH2D, {axisMultip, axisRecoil}); + histos.add("pTSpectra/RecoilTriEv", "RecoilTriEv", kTH2D, {axisMultip, axisRecoil}); + + // Register track spectra for each multiplicity interval. + for (int i = 1; i <= nMultBins; i++) { + + // eta-phi and pt-phi + histos.add(fmt::format("Spectra/etaphiAllEv_BIN{}", i).c_str(), fmt::format("etaphiAllEv_BIN{}", i).c_str(), kTH2D, {axisPhi, axisEta}); + histos.add(fmt::format("Spectra/ptphiAllEv_BIN{}", i).c_str(), fmt::format("ptphiAllEv_BIN{}", i).c_str(), kTH2D, {axisPhi, axispT}); + + histos.add(fmt::format("Spectra/etaphiTriEv_BIN{}", i).c_str(), fmt::format("etaphiTriEv_BIN{}", i).c_str(), kTH2D, {axisPhi, axisEta}); + histos.add(fmt::format("Spectra/ptphiTriEv_BIN{}", i).c_str(), fmt::format("ptphiTriEv_BIN{}", i).c_str(), kTH2D, {axisPhi, axispT}); + + //// Six near spectra and six combined 120/240-degree spectra per multiplicity bin //// + for (int angularBin = 0; angularBin < 6; ++angularBin) { + foldedHistos.add(fmt::format("triEvFoldedLR/nearPtD{}_BIN{}", angularBin, i).c_str(), fmt::format("LR near: {}-{} deg from 0", 10 * angularBin, 10 * (angularBin + 1)).c_str(), kTH1D, {axispT}); + foldedHistos.add(fmt::format("triEvFoldedLR/awayPtD{}_BIN{}", angularBin, i).c_str(), fmt::format("LR away: {}-{} deg from 120 and 240", 10 * angularBin, 10 * (angularBin + 1)).c_str(), kTH1D, {axispT}); + } + + //// ALL: pT spectra (TH1D) and mean associated pT versus multiplicity (TProfile) //// + histos.add(fmt::format("allEvPkVall/allLRNearPt_BIN{}", i).c_str(), fmt::format("allLRNearPt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("allEvPkVall/allLRNearPtMultip_BIN{}", i).c_str(), fmt::format("allLRNearPtMultip_BIN{}", i).c_str(), kTProfile, {axisMultip}); + + histos.add(fmt::format("allEvPkVall/allSRNearPt_BIN{}", i).c_str(), fmt::format("allSRNearPt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("allEvPkVall/allSRNearPtMultip_BIN{}", i).c_str(), fmt::format("allSRNearPtMultip_BIN{}", i).c_str(), kTProfile, {axisMultip}); + + histos.add(fmt::format("allEvPkVall/allLRValleyPt_BIN{}", i).c_str(), fmt::format("allLRValleyPt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("allEvPkVall/allLRValleyPtMultip_BIN{}", i).c_str(), fmt::format("allLRValleyPtMultip_BIN{}", i).c_str(), kTProfile, {axisMultip}); + histos.add(fmt::format("allEvPkVall/allSRValleyPt_BIN{}", i).c_str(), fmt::format("allSRValleyPt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("allEvPkVall/allSRValleyPtMultip_BIN{}", i).c_str(), fmt::format("allSRValleyPtMultip_BIN{}", i).c_str(), kTProfile, {axisMultip}); + + histos.add(fmt::format("allEvPkVall/allLRAwayPt_BIN{}", i).c_str(), fmt::format("allLRAwayPt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("allEvPkVall/allLRAwayPtMultip_BIN{}", i).c_str(), fmt::format("allLRAwayPtMultip_BIN{}", i).c_str(), kTProfile, {axisMultip}); + histos.add(fmt::format("allEvPkVall/allSRAwayPt_BIN{}", i).c_str(), fmt::format("allSRAwayPt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("allEvPkVall/allSRAwayPtMultip_BIN{}", i).c_str(), fmt::format("allSRAwayPtMultip_BIN{}", i).c_str(), kTProfile, {axisMultip}); + + //// TRI: three peaks and two valleys combined into one spectrum/profile //// + histos.add(fmt::format("triEvPkVall/triLRPeak1Pt_BIN{}", i).c_str(), fmt::format("triLRPeak1Pt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("triEvPkVall/triLRPeak1PtMultip_BIN{}", i).c_str(), fmt::format("triLRPeak1PtMultip_BIN{}", i).c_str(), kTProfile, {axisMultip}); + histos.add(fmt::format("triEvPkVall/triSRPeak1Pt_BIN{}", i).c_str(), fmt::format("triSRPeak1Pt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("triEvPkVall/triSRPeak1PtMultip_BIN{}", i).c_str(), fmt::format("triSRPeak1PtMultip_BIN{}", i).c_str(), kTProfile, {axisMultip}); + + histos.add(fmt::format("triEvPkVall/triLRPeak2Pt_BIN{}", i).c_str(), fmt::format("triLRPeak2Pt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("triEvPkVall/triLRPeak2PtMultip_BIN{}", i).c_str(), fmt::format("triLRPeak2PtMultip_BIN{}", i).c_str(), kTProfile, {axisMultip}); + histos.add(fmt::format("triEvPkVall/triSRPeak2Pt_BIN{}", i).c_str(), fmt::format("triSRPeak2Pt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("triEvPkVall/triSRPeak2PtMultip_BIN{}", i).c_str(), fmt::format("triSRPeak2PtMultip_BIN{}", i).c_str(), kTProfile, {axisMultip}); + + histos.add(fmt::format("triEvPkVall/triLRPeak3Pt_BIN{}", i).c_str(), fmt::format("triLRPeak3Pt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("triEvPkVall/triLRPeak3PtMultip_BIN{}", i).c_str(), fmt::format("triLRPeak3PtMultip_BIN{}", i).c_str(), kTProfile, {axisMultip}); + histos.add(fmt::format("triEvPkVall/triSRPeak3Pt_BIN{}", i).c_str(), fmt::format("triSRPeak3Pt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("triEvPkVall/triSRPeak3PtMultip_BIN{}", i).c_str(), fmt::format("triSRPeak3PtMultip_BIN{}", i).c_str(), kTProfile, {axisMultip}); + + histos.add(fmt::format("triEvPkVall/triLRValleysPt_BIN{}", i).c_str(), fmt::format("triLRValleysPt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("triEvPkVall/triLRValleysPtMultip_BIN{}", i).c_str(), fmt::format("triLRValleysPtMultip_BIN{}", i).c_str(), kTProfile, {axisMultip}); + histos.add(fmt::format("triEvPkVall/triSRValleysPt_BIN{}", i).c_str(), fmt::format("triSRValleysPt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("triEvPkVall/triSRValleysPtMultip_BIN{}", i).c_str(), fmt::format("triSRValleysPtMultip_BIN{}", i).c_str(), kTProfile, {axisMultip}); + + // Same Even Fct + histos.add(fmt::format("Corelations/sTRI_BIN{}", i).c_str(), fmt::format("sTRI_BIN{}", i).c_str(), kTH2D, {axisDeltaPhi, axisDeltaEta}); + histos.add(fmt::format("Corelations/sProjTRI_BIN{}", i).c_str(), fmt::format("sProjTRI_BIN{}", i).c_str(), kTH1D, {axisDeltaPhi}); + } + + // Register moment distributions in the broad ranges. + const std::array bins = {1, 2, 3}; + for (int j = 1; j <= 8; j++) { + for (const auto& i : bins) { + histos.add(fmt::format("FWM_BIN{}/H{}", i, j).c_str(), fmt::format("H_{{{}}}^{{rec}}", j).c_str(), kTH1F, {axisHlrec}); + histos.add(fmt::format("FWM_BIN{}/Multip_H{}", i, j).c_str(), fmt::format("H_{{{}}} vs Multip", j).c_str(), kTH2D, {axisMultip, axisHlfine}); + } + } + + // Register optional pairwise moment correlations. + if (doFWMCorrelations) { + + for (size_t k = 0; k < fwmCorrelationNames.size(); ++k) { + + const int first = fwmCorrelationFirst[k] + 1; + const int second = fwmCorrelationSecond[k] + 1; + + histos.add(fmt::format("FWM_BIN1/{}", fwmCorrelationNames[k]).c_str(), fmt::format("H_{{{}}} vs H_{{{}}}", first, second).c_str(), kTH2D, {axisHlfine, axisHlfine}); + histos.add(fmt::format("FWM_BIN2/{}", fwmCorrelationNames[k]).c_str(), fmt::format("H_{{{}}} vs H_{{{}}}", first, second).c_str(), kTH2D, {axisHlfine, axisHlfine}); + histos.add(fmt::format("FWM_BIN3/{}", fwmCorrelationNames[k]).c_str(), fmt::format("H_{{{}}} vs H_{{{}}}", first, second).c_str(), kTH2D, {axisHlfine, axisHlfine}); + } + } + } + + // Apply reconstructed collision quality requirements. + template + bool isEventSelected(const TCollision& RecCollision) + { + if (isVtxRange && std::abs(RecCollision.posZ()) >= vtxRange) { + return false; + } + + if (isSel8 && !RecCollision.sel8()) { + return false; + } + + if (isNoSameBunchPileup && !RecCollision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + return false; + } + + if (isGoodZvtxFT0vsPV && !RecCollision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + + if (isVertexITSTPC && !RecCollision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + return false; + } + + if (isVertexTOFmatched && !RecCollision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) { + return false; + } + + if (kIsGoodITSLayer0123 && !RecCollision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { + return false; + } + + if (isNoCollInTimeRangeNarrow && !RecCollision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeNarrow)) { + return false; + } + + return true; + } + + // Compute normalized Fox-Wolfram moments through order eight. + template + std::array computeFWM(T const& tracks) + { + std::array hSN{}; + std::array hS{}; + + // Accumulate transverse-momentum-weighted contributions from track pairs. + for (const auto& [t1, t2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks, tracks))) { + + double c = std::cos(t1.phi() - t2.phi()); + double w = t1.pt() * t2.pt(); + double p0 = 1.0, p1 = c; + + hSN[0] += w; + hSN[1] += w * p1; + + // Evaluate higher Legendre orders with the recurrence relation. + for (int l = 2; l < 9; ++l) { + + double p = ((2 * l - 1) * c * p1 - (l - 1) * p0) / l; + + hSN[l] += w * p; + p0 = p1; + p1 = p; + } + } + + // Normalize each moment by the zeroth moment. + for (int l = 0; l < 9; ++l) { + hS[l] = hSN[l] / hSN[0]; + } + + return hS; + } + + // Calculate normalized transverse recoil. + template + double computeRecoil(const Tracks& tracks) + { + double sumPx = 0.0; + double sumPy = 0.0; + double sumPt = 0.0; + + // Sum track momenta to form the transverse recoil. + for (const auto& track : tracks) { + sumPx += track.px(); + sumPy += track.py(); + sumPt += track.pt(); + } + + if (sumPt == 0.0) { + return 0.0; + } + + return std::sqrt(sumPx * sumPx + sumPy * sumPy) / sumPt; + } + + // Fill inclusive and topology-selected observables in one multiplicity interval. + template + void fillHistoMult(int binIndex, int minMult, int maxMult, + bool isSelectedTopology, const TriFlags& triFlags, bool isIsoNominal, + const T& tracks) + { + if (tracks.size() < minMult || tracks.size() > maxMult) { + return; + } + + // Count all events and each topology selection in this interval. + histos.fill(HIST("Events_Details/allEvSetNom"), binIndex); + + if (isIsoNominal) { + histos.fill(HIST("Events_Details/eventsNoISO"), binIndex); + } + + if (triFlags[kTriNominal]) { + histos.fill(HIST("Events_Details/triEvSetNom"), binIndex); + } + + // The remaining filters + if (triFlags[kTriSet1p]) { + histos.fill(HIST("Events_Details/triEvSet1p"), binIndex); + } + + if (triFlags[kTriSet2p]) { + histos.fill(HIST("Events_Details/triEvSet2p"), binIndex); + } + + if (triFlags[kTriSet3p]) { + histos.fill(HIST("Events_Details/triEvSet3p"), binIndex); + } + + if (triFlags[kTriSet1m]) { + histos.fill(HIST("Events_Details/triEvSet1m"), binIndex); + } + + if (triFlags[kTriSet2m]) { + histos.fill(HIST("Events_Details/triEvSet2m"), binIndex); + } + + if (triFlags[kTriSet3m]) { + histos.fill(HIST("Events_Details/triEvSet3m"), binIndex); + } + + // Fill inclusive spectra and selected-topology spectra for this interval. + static_for<0, 9>([&](auto i) { + constexpr int idx = i.value; + + if (binIndex == idx + 1) { + + for (const auto& track : tracks) { + + histos.fill(HIST("Spectra/etaphiAllEv_") + HIST(binNames[idx]), track.phi(), track.eta()); + histos.fill(HIST("Spectra/ptphiAllEv_") + HIST(binNames[idx]), track.phi(), track.pt()); + + if (!isSelectedTopology) { + continue; + } // TRI Ev. + + histos.fill(HIST("Spectra/etaphiTriEv_") + HIST(binNames[idx]), track.phi(), track.eta()); + histos.fill(HIST("Spectra/ptphiTriEv_") + HIST(binNames[idx]), track.phi(), track.pt()); + } + } + }); // per Multip Bin + } + + //// Helper for one multiplicity bin (Idx is a template parameter, not a captured lambda variable) //// + //// Avoids the GCC 14 ICE in tsubst_expr caused by nested generic static_for lambdas //// + template + void fillFoldedBin(double deltaPhi, double pt) + { + const double degToRad = constants::math::PI / 180.; + + //// Adjacent windows include the lower edge and exclude the upper edge //// + static_for<0, 5>([&](auto j) { + constexpr int angularBin = j.value; + constexpr double lo = 10. * angularBin; + constexpr double hi = 10. * (angularBin + 1); + + //// Combine the two sides of the near peak at zero //// + const bool inNear = (deltaPhi > -hi * degToRad && deltaPhi < -lo * degToRad) || (deltaPhi > lo * degToRad && deltaPhi < hi * degToRad); + + if (inNear) { + foldedHistos.fill(HIST("triEvFoldedLR/") + HIST(nearNames[angularBin]) + HIST(binNames[Idx]), pt); + } + + //// Combine both sides of 120 and 240 degrees //// + //// The final window maps angles above 270 degrees to negative deltaPhi //// + const bool inAway = (deltaPhi > (120. - hi) * degToRad && deltaPhi < (120. - lo) * degToRad) || (deltaPhi > (120. + lo) * degToRad && deltaPhi < (120. + hi) * degToRad) || + (deltaPhi > (240. - hi) * degToRad && deltaPhi < (240. - lo) * degToRad) || (deltaPhi > (240. + lo) * degToRad && deltaPhi < (240. + hi) * degToRad) || + (deltaPhi > (240. + lo - 360.) * degToRad && deltaPhi < (240. + hi - 360.) * degToRad); + + if (inAway) { + foldedHistos.fill(HIST("triEvFoldedLR/") + HIST(awayNames[angularBin]) + HIST(binNames[Idx]), pt); + } + }); + } + + //// TRI LR pT spectra in six symmetric angular intervals //// + template + void fillSameEventFoldedLR(int binIndex, + Track const& associatedTrack, + double deltaPhi, + double deltaEta, + bool isLeadAssocPair) + { + //// Event topology is already selected by the caller //// + const bool isAnyTriMode = topologyMode.value == kTriEvSetNom || + topologyMode.value == kTriEvSet1p || + topologyMode.value == kTriEvSet2p || + topologyMode.value == kTriEvSet3p || + topologyMode.value == kTriEvSet1m || + topologyMode.value == kTriEvSet2m || + topologyMode.value == kTriEvSet3m; + + if (!isAnyTriMode || leadAssocMode.value != kpTLeadFree || !isLeadAssocPair) { + return; + } + + //// Keep only 1.25 < |deltaEta| < 1.60 //// + const double absEta = std::abs(deltaEta); + if (absEta <= 1.25 || absEta >= 1.60) { + return; + } + + //// Fill only the requested multiplicity interval //// + static_for<0, 9>([&](auto i) { + constexpr int idx = i.value; + + if (binIndex != idx + 1) { + return; + } + + fillFoldedBin(deltaPhi, associatedTrack.pt()); + }); + } + + //// Helper for one multiplicity bin (Idx is a template parameter, not a captured lambda variable) //// + template + void fillPtBin(bool isAll, bool isTri, + const std::array& allWindows, const std::array& triWindows, + bool inLR, bool inSR, int nTracks, double pt) + { + //// Fill ALL spectra and profiles with one entry per accepted pair //// + if (isAll) { + static_for<0, 2>([&](auto j) { + constexpr int region = j.value; + + if (!allWindows[region]) { + return; + } + + if (inLR) { + histos.fill(HIST("allEvPkVall/") + HIST(allLRNames[region]) + HIST("Pt_") + HIST(binNames[Idx]), pt); + histos.fill(HIST("allEvPkVall/") + HIST(allLRNames[region]) + HIST("PtMultip_") + HIST(binNames[Idx]), nTracks, pt); + } + + if (inSR) { + histos.fill(HIST("allEvPkVall/") + HIST(allSRNames[region]) + HIST("Pt_") + HIST(binNames[Idx]), pt); + histos.fill(HIST("allEvPkVall/") + HIST(allSRNames[region]) + HIST("PtMultip_") + HIST(binNames[Idx]), nTracks, pt); + } + }); + } + + //// Fill TRI spectra and profiles with one entry per accepted pair //// + if (isTri) { + static_for<0, 3>([&](auto j) { + constexpr int region = j.value; + + if (!triWindows[region]) { + return; + } + + if (inLR) { + histos.fill(HIST("triEvPkVall/") + HIST(triLRNames[region]) + HIST("Pt_") + HIST(binNames[Idx]), pt); + histos.fill(HIST("triEvPkVall/") + HIST(triLRNames[region]) + HIST("PtMultip_") + HIST(binNames[Idx]), nTracks, pt); + } + + if (inSR) { + histos.fill(HIST("triEvPkVall/") + HIST(triSRNames[region]) + HIST("Pt_") + HIST(binNames[Idx]), pt); + histos.fill(HIST("triEvPkVall/") + HIST(triSRNames[region]) + HIST("PtMultip_") + HIST(binNames[Idx]), nTracks, pt); + } + }); + } + } + + //// Fixed-window pT spectra and mean-pT profiles for ALL and TRI //// + template + void fillSameEventPtHist(int binIndex, + int nTracks, + Track const& associatedTrack, + double deltaPhi, + double deltaEta, + bool isLeadAssocPair) + { + if (!isLeadAssocPair || leadAssocMode.value != kpTLeadFree) { + return; + } + + //// Associated pT and LR/SR pair selections //// + const double pt = associatedTrack.pt(); + const double absEta = std::abs(deltaEta); + const double degToRad = constants::math::PI / 180.; + + const bool inLR = absEta > 1.25 && absEta < 1.60; + const bool inSR = absEta < 0.35; + + //// Preserve the original open angular windows //// + const auto inPhi = [&](double lo, double hi) { + return deltaPhi > lo * degToRad && deltaPhi < hi * degToRad; + }; + + //// Event topology //// + const bool isAll = topologyMode.value == kAllEvSetNom; + + const bool isTri = topologyMode.value == kTriEvSetNom || + topologyMode.value == kTriEvSet1p || + topologyMode.value == kTriEvSet2p || + topologyMode.value == kTriEvSet3p || + topologyMode.value == kTriEvSet1m || + topologyMode.value == kTriEvSet2m || + topologyMode.value == kTriEvSet3m; + + //// ALL: near, between, away //// + const std::array allWindows = { + inPhi(-15., 15.), inPhi(75., 105.), inPhi(165., 195.)}; + + //// TRI: peaks at 0, 120, 240 degrees and the two combined valleys //// + const std::array triWindows = { + inPhi(-15., 15.), inPhi(105., 135.), inPhi(225., 255.), inPhi(45., 75.) || inPhi(165., 195.)}; + + //// Fill only the requested multiplicity interval //// + static_for<0, 9>([&](auto i) { + constexpr int idx = i.value; + + if (binIndex != idx + 1) { + return; + } + + fillPtBin(isAll, isTri, allWindows, triWindows, inLR, inSR, nTracks, pt); + }); + } + + //// Same-event pair counts and angular correlations only //// + template + void fillSameEventHist(int binIndex, + Track const& /*associatedTrack*/, + double deltaPhi, + double deltaEta, + bool isLeadAssocPair) + { + + static_for<0, 9>([&](auto i) { + constexpr int idx = i.value; + + if (binIndex != idx + 1) { + return; + } + + if (!isLeadAssocPair) { + return; + } + + //// Count the pair and fill its original angular coordinates //// + histos.fill(HIST("SamePairs/SamePairsNoLA"), binIndex); + histos.fill(HIST("Corelations/sProjTRI_") + HIST(binNames[idx]), deltaPhi); + histos.fill(HIST("Corelations/sTRI_") + HIST(binNames[idx]), deltaPhi, deltaEta); + }); + } + + /// Helper template for FWMs, FWMs vs Multip, FWMs i-j corr. + template + void fillFWMRange(const std::array& H, int nTrk) + { + // H[0] = H0 (normalizare), H[1..8] = H1..H8 + static_for<0, 7>([&](auto j) { + constexpr int jj = j.value; + histos.fill(HIST(fwmRangeNames[R]) + HIST(fwmHNames[jj]), H[jj + 1]); + histos.fill(HIST(fwmRangeNames[R]) + HIST(fwmMultHNames[jj]), nTrk, H[jj + 1]); + }); + + if (doFWMCorrelations) { + static_for<0, 27>([&](auto k) { + constexpr int kk = k.value; + histos.fill(HIST(fwmRangeNames[R]) + HIST(fwmCorrelationNames[kk]), H[fwmCorrelationFirst[kk] + 1], H[fwmCorrelationSecond[kk] + 1]); + }); + } + } + + std::array, 3> triTuples; + + Filter itsTpcTracks = requireGlobalTrackInFilter(); + using GlobalTracks = soa::Filtered; + + // Process reconstructed events and buffer selected tracks for mixing. + void process(CollisionRecTable const& collision, + GlobalTracks const& tracks) + { + + // Stop before analysis when the collision fails quality cuts. + if (!isEventSelected(collision)) { + return; + } + + if (tracks.size() == 0) { + return; + } + + // Determine track multiplicity, recoil, and leading tracks. + int nGlobalTracks = tracks.size(); + auto nRecoil = computeRecoil(tracks); + + auto [triggTrk, triggTrkPart, foundPart] = findLeadingPart(tracks); + + // Assign the event to broad and fine multiplicity intervals. + std::array inBin{}; + for (int i = 0; i < nMultBins; ++i) { + inBin[i] = (nGlobalTracks >= multLower.value[i] && nGlobalTracks <= multUpper.value[i]); + } + const bool multRANGE1 = inBin[0]; + const bool multRANGE2 = inBin[1]; + const bool multRANGE3 = inBin[2]; + + // Compute event moments and evaluate topology selections. + auto fwH = computeFWM(tracks); + auto [H0, H1, H2, H3, H4, H5, H6, H7, H8] = fwH; + + // FWMs, FWMs vs Multip, FWMs i-j corr. + if (multRANGE1) { + fillFWMRange<0>(fwH, nGlobalTracks); + } + + if (multRANGE2) { + fillFWMRange<1>(fwH, nGlobalTracks); + } + + if (multRANGE3) { + fillFWMRange<2>(fwH, nGlobalTracks); + } + + // Apply the nominal triangular moment limits + const bool isTriSetNom = (H1 < kTriFWMSetNom.value[0]) && (H2 < kTriFWMSetNom.value[1]) && (H3 > kTriFWMSetNom.value[2]) && (H4 < kTriFWMSetNom.value[3]) && (H5 > kTriFWMSetNom.value[4]) && (H6 < kTriFWMSetNom.value[5]) && (H7 > kTriFWMSetNom.value[6]) && (H8 < kTriFWMSetNom.value[7]); + + // Apply three progressively looser triangular selections + const bool isTriSet1p = (H1 < kTriFWMSet1p.value[0]) && (H2 < kTriFWMSet1p.value[1]) && (H3 > kTriFWMSet1p.value[2]) && (H4 < kTriFWMSet1p.value[3]) && (H5 > kTriFWMSet1p.value[4]) && (H6 < kTriFWMSet1p.value[5]) && (H7 > kTriFWMSet1p.value[6]) && (H8 < kTriFWMSet1p.value[7]); + const bool isTriSet2p = (H1 < kTriFWMSet2p.value[0]) && (H2 < kTriFWMSet2p.value[1]) && (H3 > kTriFWMSet2p.value[2]) && (H4 < kTriFWMSet2p.value[3]) && (H5 > kTriFWMSet2p.value[4]) && (H6 < kTriFWMSet2p.value[5]) && (H7 > kTriFWMSet2p.value[6]) && (H8 < kTriFWMSet2p.value[7]); + const bool isTriSet3p = (H1 < kTriFWMSet3p.value[0]) && (H2 < kTriFWMSet3p.value[1]) && (H3 > kTriFWMSet3p.value[2]) && (H4 < kTriFWMSet3p.value[3]) && (H5 > kTriFWMSet3p.value[4]) && (H6 < kTriFWMSet3p.value[5]) && (H7 > kTriFWMSet3p.value[6]) && (H8 < kTriFWMSet3p.value[7]); + + // Apply three progressively tighter triangular selections + const bool isTriSet1m = (H1 < kTriFWMSet1m.value[0]) && (H2 < kTriFWMSet1m.value[1]) && (H3 > kTriFWMSet1m.value[2]) && (H4 < kTriFWMSet1m.value[3]) && (H5 > kTriFWMSet1m.value[4]) && (H6 < kTriFWMSet1m.value[5]) && (H7 > kTriFWMSet1m.value[6]) && (H8 < kTriFWMSet1m.value[7]); + const bool isTriSet2m = (H1 < kTriFWMSet2m.value[0]) && (H2 < kTriFWMSet2m.value[1]) && (H3 > kTriFWMSet2m.value[2]) && (H4 < kTriFWMSet2m.value[3]) && (H5 > kTriFWMSet2m.value[4]) && (H6 < kTriFWMSet2m.value[5]) && (H7 > kTriFWMSet2m.value[6]) && (H8 < kTriFWMSet2m.value[7]); + const bool isTriSet3m = (H1 < kTriFWMSet3m.value[0]) && (H2 < kTriFWMSet3m.value[1]) && (H3 > kTriFWMSet3m.value[2]) && (H4 < kTriFWMSet3m.value[3]) && (H5 > kTriFWMSet3m.value[4]) && (H6 < kTriFWMSet3m.value[5]) && (H7 > kTriFWMSet3m.value[6]) && (H8 < kTriFWMSet3m.value[7]); + + const TriFlags triFlags = {isTriSetNom, isTriSet1p, isTriSet2p, isTriSet3p, isTriSet1m, isTriSet2m, isTriSet3m}; + + // Apply the configurable isotropic moment limits. + const bool isIsoNominal = (H1 < isoFWMnominal.value[0]) && (H2 < isoFWMnominal.value[1]) && (H3 < isoFWMnominal.value[2]) && (H4 < isoFWMnominal.value[3]) && (H5 < isoFWMnominal.value[4]) && (H6 < isoFWMnominal.value[5]) && (H7 < isoFWMnominal.value[6]) && (H8 < isoFWMnominal.value[7]); + + // Select all, triangular, or isotropic events for correlations. + const bool isSelectedTopology = + (topologyMode.value == kAllEvSetNom) || + (topologyMode.value == kTriEvSetNom && isTriSetNom) || + (topologyMode.value == kIsoEvSetNom && isIsoNominal) || + (topologyMode.value == kTriEvSet1p && isTriSet1p) || + (topologyMode.value == kTriEvSet2p && isTriSet2p) || + (topologyMode.value == kTriEvSet3p && isTriSet3p) || + (topologyMode.value == kTriEvSet1m && isTriSet1m) || + (topologyMode.value == kTriEvSet2m && isTriSet2m) || + (topologyMode.value == kTriEvSet3m && isTriSet3m); + + // TRI Ev. Multip (the remaining filters) - p == permissive and m == restrictive + if (triFlags[kTriSet1p]) { + histos.fill(HIST("Multip/nTrkTriEvS1p"), nGlobalTracks); + } + + if (triFlags[kTriSet2p]) { + histos.fill(HIST("Multip/nTrkTriEvS2p"), nGlobalTracks); + } + + if (triFlags[kTriSet3p]) { + histos.fill(HIST("Multip/nTrkTriEvS3p"), nGlobalTracks); + } + + if (triFlags[kTriSet1m]) { + histos.fill(HIST("Multip/nTrkTriEvS1m"), nGlobalTracks); + } + if (triFlags[kTriSet2m]) { + histos.fill(HIST("Multip/nTrkTriEvS2m"), nGlobalTracks); + } + if (triFlags[kTriSet3m]) { + histos.fill(HIST("Multip/nTrkTriEvS3m"), nGlobalTracks); + } + + // Fill inclusive event multiplicity, recoil and average pt lead. + + histos.fill(HIST("Multip/multipAllEv"), nGlobalTracks); + histos.fill(HIST("pTSpectra/RecoilAllEv"), nGlobalTracks, nRecoil); + histos.fill(HIST("pTSpectra/pTLeadAllEv"), triggTrk.pt()); + histos.fill(HIST("pTSpectra/ptLeadMultipAllEv"), nGlobalTracks, triggTrk.pt()); + histos.fill(HIST("pTSpectra/meanptLeadMultipAllEv"), nGlobalTracks, triggTrk.pt()); + + //// Inclusive spectra for the configured selected topology //// + if (isSelectedTopology) { + histos.fill(HIST("Multip/multipTriEv"), nGlobalTracks); + histos.fill(HIST("pTSpectra/RecoilTriEv"), nGlobalTracks, nRecoil); + histos.fill(HIST("pTSpectra/pTLeadTriEv"), triggTrk.pt()); + histos.fill(HIST("pTSpectra/ptLeadMultipTriEv"), nGlobalTracks, triggTrk.pt()); + histos.fill(HIST("pTSpectra/meanptLeadMultipTriEv"), nGlobalTracks, triggTrk.pt()); + } + + // Fill inclusive and topology-selected leading-track spectra. + for (const auto& track : tracks) { + + if (track.globalIndex() == triggTrk.globalIndex()) { + continue; + } // just the assoc. particles + + histos.fill(HIST("pTSpectra/pTAssocAllEv"), track.pt()); + histos.fill(HIST("pTSpectra/meanpTAssocMultipAllEv"), nGlobalTracks, track.pt()); + + if (!isSelectedTopology) { + continue; + } + + histos.fill(HIST("pTSpectra/pTAssocTriEv"), track.pt()); + histos.fill(HIST("pTSpectra/meanpTAssocMultipTriEv"), nGlobalTracks, track.pt()); + } + + // Build same-event angular correlations for the selected topology. + if (doSameEvent && isSelectedTopology) { + + // Form distinct track pairs within the selected event. + for (const auto& [track1, track2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks, tracks))) { + + if (track1.globalIndex() == track2.globalIndex()) { + continue; + } + + // Calculate pair separations and classify azimuthal windows. + const double deltaPhi = computeDeltaPhi(track1.phi(), track2.phi()); + const double deltaEta = track1.eta() - track2.eta(); + + // Apply the configured pair selection to each interval. + const bool isLeadAssocPair = passesLeadAssoc(leadAssocMode.value, foundPart, track1, track2, triggTrk, triggTrkPart); + + for (int i = 0; i < nMultBins; ++i) { + if (inBin[i]) { + //// Angular correlations for the selected topology //// + fillSameEventHist(i + 1, track2, deltaPhi, deltaEta, isLeadAssocPair); + //// Fixed-window spectra and mean-pT profiles //// + fillSameEventPtHist(i + 1, nGlobalTracks, track2, deltaPhi, deltaEta, isLeadAssocPair); + //// TRI LR spectra in symmetric 10-degree windows //// + fillSameEventFoldedLR(i + 1, track2, deltaPhi, deltaEta, isLeadAssocPair); + } + } + } + } + + // Map the event to a broad range for mixed-event buffering. + int range = -1; + + if (multRANGE1) { + range = 0; + } else if (multRANGE2) { + range = 1; + } else if (multRANGE3) { + range = 2; + } + + // Buffer up to the requested number of selected events per range. + if (isSelectedTopology && range >= 0 && triTuples[range].size() < static_cast(nSelEv)) { + + std::vector tracksTRI; + tracksTRI.reserve(nGlobalTracks); + + for (const auto& track : tracks) { + tracksTRI.emplace_back(track.pt(), track.eta(), track.phi()); + } + + triTuples[range].emplace_back(collision.posZ(), nGlobalTracks, std::move(tracksTRI)); + } + + // Fill event counts and spectra for all matching multiplicity intervals. + for (int i = 0; i < nMultBins; ++i) { + fillHistoMult(i + 1, multLower.value[i], multUpper.value[i], isSelectedTopology, triFlags, isIsoNominal, tracks); + } + } + + int countDF = 0; + + // Write buffered events to the tables consumed by the mixing task. + void processFILL(aod::Collisions const& /*collisions*/, aod::Tracks const& /*tracks*/) + { + // Track data frames and report buffered event counts. + countDF++; + + // LOGF(info, "Input data - ALL Collisions %d, Tracks %d -> DF no %d", collisions.size(), tracks.size(), countDF); + + for (int range = 0; range < 3; ++range) { + + auto& events = triTuples[range]; + // LOGF(info, "Selected events buffered in RANGE%d: %zu/%d", range + 1, events.size(), static_cast(nSelEv)); + + // Wait until a range reaches its event quota. + if (events.size() < static_cast(nSelEv)) { + continue; + } + + // LOGF(info, "-> FILL TABLES FOR RANGE%d <-", range + 1); + + // Write each buffered collision and its tracks to the output tables. + for (const auto& col : events) { + + float posZ = std::get<0>(col); + int multip = std::get<1>(col); + triCollisions(posZ, multip); + + for (const auto& track : std::get<2>(col)) { + triTracks(triCollisions.lastIndex(), std::get<0>(track), std::get<1>(track), std::get<2>(track)); + } + } + + // Release events after their tables are filled. + events.clear(); + } + } + + PROCESS_SWITCH(FoxWolframCorrelation, processFILL, "Fill the triTables", true); +}; + +// Build mixed-event backgrounds from the selected event tables. +struct FoxWolframCorrelationMixing { + + SliceCache cache_bin1, cache_bin2, cache_bin3, cache_bin4, cache_bin5, cache_bin6, cache_bin7, cache_bin8, cache_bin9, cache_bin10; + Preslice triCol = aod::track_tri::triangleCollisionId; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + static constexpr std::array mixedBinNames = { + "BIN1", "BIN2", "BIN3", "BIN4", "BIN5", "BIN6", "BIN7", "BIN8", "BIN9", "BIN10"}; + + Configurable nBin{"nBin", 100, "No of bis per axis"}; + ConfigurableAxis axisVtx{"axisVtx", {VARIABLE_WIDTH, -10, 10}, "vertex axis for mixed event histograms"}; + + // Use half-integer edges for inclusive integer multiplicity bins. + ConfigurableAxis axisMult1{"axisMult1", {VARIABLE_WIDTH, 39.5, 85.5}, "axisMult1"}; + ConfigurableAxis axisMult2{"axisMult2", {VARIABLE_WIDTH, 85.5, 140.5}, "axisMult2"}; + ConfigurableAxis axisMult3{"axisMult3", {VARIABLE_WIDTH, 140.5, 350.5}, "axisMult3"}; + ConfigurableAxis axisMult4{"axisMult4", {VARIABLE_WIDTH, 39.5, 60.5}, "axisMult4"}; + ConfigurableAxis axisMult5{"axisMult5", {VARIABLE_WIDTH, 60.5, 80.5}, "axisMult5"}; + ConfigurableAxis axisMult6{"axisMult6", {VARIABLE_WIDTH, 80.5, 100.5}, "axisMult6"}; + ConfigurableAxis axisMult7{"axisMult7", {VARIABLE_WIDTH, 100.5, 120.5}, "axisMult7"}; + ConfigurableAxis axisMult8{"axisMult8", {VARIABLE_WIDTH, 120.5, 140.5}, "axisMult8"}; + ConfigurableAxis axisMult9{"axisMult9", {VARIABLE_WIDTH, 140.5, 160.5}, "axisMult9"}; + ConfigurableAxis axisMult10{"axisMult10", {VARIABLE_WIDTH, 160.5, 350.5}, "axisMult10"}; + + Configurable doMixedEvent{"doMixedEvent", true, "Fill mixed-event TRI correlations"}; + Configurable nMixedEvents{"nMixedEvents", 5, "Number of mixed events per trigger event"}; + + // Same multiplicity limits as in FoxWolframCorrelation (inclusive, same values in both tasks). + // They set how many mixing pools are active and are checked against axisMult1..10. + Configurable> multLower{"multLower", {40, 86, 141, 40, 61, 81, 101, 121, 141, 161}, "lower multip. limits (inclusive)"}; + Configurable> multUpper{"multUpper", {85, 140, 350, 60, 80, 100, 120, 140, 160, 350}, "upper multip. limits (inclusive)"}; + + static constexpr int MaxMultBins = 10; + int nMultBins = 0; // set in init() from multLower/multUpper + + // Choose the pair selection for mixed-event correlations. + Configurable leadAssocMode{"leadAssocMode", 2, "0: ALL i-j pairs (pT<3); 1: pTLead in (2,3) & pTAssoc < 2; 2: pTAssoc < pTLead"}; + + // Register mixed-event pair counts and angular distributions. + void init(InitContext const&) + { + // Validate the configurable multiplicity bins. + if (multLower.value.size() != multUpper.value.size() || multLower.value.empty() || multLower.value.size() > static_cast(MaxMultBins)) { + LOGF(fatal, "multLower/multUpper must have the same size, between 1 and %d entries", MaxMultBins); + } + nMultBins = static_cast(multLower.value.size()); + + // The binning policies are fixed objects, so check that each axisMultK matches [multLower-0.5, multUpper+0.5]. + const std::array, MaxMultBins> axisEdges = { + axisMult1.value, axisMult2.value, axisMult3.value, axisMult4.value, axisMult5.value, + axisMult6.value, axisMult7.value, axisMult8.value, axisMult9.value, axisMult10.value}; + + //// Each mixing axis must cover its configured integer multiplicity interval //// + for (int i = 0; i < nMultBins; ++i) { + const auto& edges = axisEdges[i]; + const double lo = multLower.value[i] - 0.5; + const double hi = multUpper.value[i] + 0.5; + if (multLower.value[i] > multUpper.value[i] || edges.size() < 2 || + std::abs(edges[edges.size() - 2] - lo) > 1.e-6 || + std::abs(edges.back() - hi) > 1.e-6) { + LOGF(fatal, "axisMult%d must match multiplicity limits [%.1f, %.1f]", i + 1, lo, hi); + } + } + + pools = {&pairBIN1, &pairBIN2, &pairBIN3, &pairBIN4, &pairBIN5, + &pairBIN6, &pairBIN7, &pairBIN8, &pairBIN9, &pairBIN10}; + + const AxisSpec axisDeltaEta{32, -1.6, +1.6, "#Delta#eta"}; + const AxisSpec axisDeltaPhi{72, -constants::math::PIHalf, +3 * constants::math::PIHalf, "#Delta#phi"}; + const AxisSpec axisEvent{10, 0.5, 10.5, "#Multip. Bin", "EventAxis"}; + + // Label mixed-event pair counts by multiplicity interval. + histos.add("MixingPairs/MixingPairsNoLA", "MixingPairsNoLA", kTH1D, {axisEvent}, false); + auto* mixingPairsNoLA = histos.get(HIST("MixingPairs/MixingPairsNoLA"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + mixingPairsNoLA->SetBinLabel(i + 1, i < 3 ? fmt::format("mixPairsLA_RANGE{}", i + 1).c_str() : fmt::format("mixPairsLA_BIN{}", i + 1).c_str()); + } + + // Register mixed-event correlation histograms for all intervals. + for (int i = 1; i <= nMultBins; i++) { + histos.add(fmt::format("Corelations/bTRI_BIN{}", i).c_str(), fmt::format("bTRI_BIN{}", i).c_str(), kTH2D, {axisDeltaPhi, axisDeltaEta}); + histos.add(fmt::format("Corelations/bProjTRI_BIN{}", i).c_str(), fmt::format("bProjTRI_BIN{}", i).c_str(), kTH1D, {axisDeltaPhi}); + } + } + + // Mix events in vertex and multiplicity intervals. + using BinningType = ColumnBinningPolicy; + + BinningType binningOnPosMultip1{{axisVtx, axisMult1}}; + BinningType binningOnPosMultip2{{axisVtx, axisMult2}}; + BinningType binningOnPosMultip3{{axisVtx, axisMult3}}; + BinningType binningOnPosMultip4{{axisVtx, axisMult4}}; + BinningType binningOnPosMultip5{{axisVtx, axisMult5}}; + BinningType binningOnPosMultip6{{axisVtx, axisMult6}}; + BinningType binningOnPosMultip7{{axisVtx, axisMult7}}; + BinningType binningOnPosMultip8{{axisVtx, axisMult8}}; + BinningType binningOnPosMultip9{{axisVtx, axisMult9}}; + BinningType binningOnPosMultip10{{axisVtx, axisMult10}}; + + // Create independent mixing pools for the ten multiplicity intervals. + SameKindPair pairBIN1{binningOnPosMultip1, nMixedEvents, -1, &cache_bin1}; + SameKindPair pairBIN2{binningOnPosMultip2, nMixedEvents, -1, &cache_bin2}; + SameKindPair pairBIN3{binningOnPosMultip3, nMixedEvents, -1, &cache_bin3}; + SameKindPair pairBIN4{binningOnPosMultip4, nMixedEvents, -1, &cache_bin4}; + SameKindPair pairBIN5{binningOnPosMultip5, nMixedEvents, -1, &cache_bin5}; + SameKindPair pairBIN6{binningOnPosMultip6, nMixedEvents, -1, &cache_bin6}; + SameKindPair pairBIN7{binningOnPosMultip7, nMixedEvents, -1, &cache_bin7}; + SameKindPair pairBIN8{binningOnPosMultip8, nMixedEvents, -1, &cache_bin8}; + SameKindPair pairBIN9{binningOnPosMultip9, nMixedEvents, -1, &cache_bin9}; + SameKindPair pairBIN10{binningOnPosMultip10, nMixedEvents, -1, &cache_bin10}; + + // Pointers to the pools so they can be processed in a loop (filled in init()). + using MixPair = SameKindPair; + std::array pools{}; + + // Fill mixed-event distributions for a selected multiplicity interval. + template + void fillMixedEventBin(PairType& pair, int binIndex) + { + // Mix populated event pairs within the selected pool. + for (const auto& [c1, tracks1, c2, tracks2] : pair) { + if (tracks1.size() == 0 || tracks2.size() == 0) { + continue; + } + + // Find leading tracks in the first mixed event. + auto [triggTrk, triggTrkPart, foundPart] = findLeadingPart(tracks1); + + // LOGF(debug, "Mix TRI Ev (BIN%d): (%d, %d), z=(%.3f, %.3f), mult=(%d, %d)", + // binIndex, c1.globalIndex(), c2.globalIndex(), c1.posZ(), c2.posZ(), tracks1.size(), tracks2.size()); + + // Select tracks from different events using the configured pair rules. + for (const auto& [track1, track2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + + if (!passesLeadAssoc(leadAssocMode.value, foundPart, track1, track2, triggTrk, triggTrkPart)) { + continue; + } + + const double deltaPhi = computeDeltaPhi(track1.phi(), track2.phi()); + const double deltaEta = track1.eta() - track2.eta(); + + // Count accepted pairs in the corresponding multiplicity interval. + histos.fill(HIST("MixingPairs/MixingPairsNoLA"), binIndex); + static_for<0, 9>([&](auto i) { + constexpr int idx = i.value; + if (binIndex == idx + 1) { + histos.fill(HIST("Corelations/bProjTRI_") + HIST(mixedBinNames[idx]), deltaPhi); + histos.fill(HIST("Corelations/bTRI_") + HIST(mixedBinNames[idx]), deltaPhi, deltaEta); + } + }); + } + } + } + + // Run mixed-event pairing for all multiplicity intervals. + void process(aod::TriangleCollisions const& collisions, + aod::TriangleTracks const& tracks) + { + // Skip mixing when disabled or when the input tables are empty. + if (!doMixedEvent || collisions.size() == 0 || tracks.size() == 0) { + return; + } + + // Process the active independent mixing pools. + for (int i = 0; i < nMultBins; ++i) { + fillMixedEventBin(*pools[i], i + 1); + } + } +}; + +// Expose the event analysis and mixed-event tasks as one workflow. +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + }; +} diff --git a/PWGCF/TwoParticleCorrelations/Tasks/cascDiHadronCorr.cxx b/PWGCF/TwoParticleCorrelations/Tasks/cascDiHadronCorr.cxx index 7aa30d4f202..d97d425e6b3 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/cascDiHadronCorr.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/cascDiHadronCorr.cxx @@ -83,7 +83,7 @@ struct CascDiHadronCorr { O2_DEFINE_CONFIGURABLE(cfgCutCentMin, float, 0.0f, "Minimum centrality for collision") O2_DEFINE_CONFIGURABLE(cfgCutCentMax, float, 100.0f, "Maximum centrality for collision") O2_DEFINE_CONFIGURABLE(cfgMixEventNumMin, int, 1, "Minimum number of events to mix") - O2_DEFINE_CONFIGURABLE(cfgRadiusLow, float, 0.8, "Low radius for merging cut") + O2_DEFINE_CONFIGURABLE(cfgRadiusLow, double, 0.8, "Low radius for merging cut") O2_DEFINE_CONFIGURABLE(cfgRadiusHigh, float, 2.5, "High radius for merging cut") O2_DEFINE_CONFIGURABLE(cfgSampleSize, double, 10, "Sample size for mixed event") O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FT0A") @@ -104,6 +104,7 @@ struct CascDiHadronCorr { O2_DEFINE_CONFIGURABLE(cfgNSigmapid, std::vector, (std::vector{3, 3, 3}), "tpc NSigma for Pion Proton Kaon") O2_DEFINE_CONFIGURABLE(cfgOutputXi, bool, true, "Output Xi-charged correlation") O2_DEFINE_CONFIGURABLE(cfgOutputOmega, bool, false, "Output Omega-charged correlation") + O2_DEFINE_CONFIGURABLE(cfgOutputCascQA, bool, true, "Output QA plots for cascades and daughters") struct : ConfigurableGroup { std::string prefix = "cfgEvSel"; O2_DEFINE_CONFIGURABLE(cfgUseAdditionalEventCut, bool, false, "Use additional event cut on mult correlations") @@ -118,6 +119,8 @@ struct CascDiHadronCorr { O2_DEFINE_CONFIGURABLE(cfgEvSelMultCorrelation, bool, true, "Multiplicity correlation cut") O2_DEFINE_CONFIGURABLE(cfgEvSelV0AT0ACut, bool, true, "V0A T0A 5 sigma cut") O2_DEFINE_CONFIGURABLE(cfgEvSelOccupancy, bool, true, "Occupancy cut") + O2_DEFINE_CONFIGURABLE(cfgEvSelHaveXi, bool, false, "reject the collisions without any Xi reconstracted") + O2_DEFINE_CONFIGURABLE(cfgEvSelHaveOmega, bool, false, "reject the collisions without any Xi reconstracted") } cfgEvSel; struct : ConfigurableGroup { O2_DEFINE_CONFIGURABLE(cfgMultCentHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 10.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); @@ -174,6 +177,7 @@ struct CascDiHadronCorr { O2_DEFINE_CONFIGURABLE(cfgMinITSNCls, int, -1, "check minimum number of ITS clusters") O2_DEFINE_CONFIGURABLE(cfgTPCNCls, int, 0, "check minimum number of TPC hits") O2_DEFINE_CONFIGURABLE(cfgTPCCrossedRows, int, 0, "check minimum number of TPC crossed rows") + O2_DEFINE_CONFIGURABLE(cfgCutChi2prTPCcls, float, 2.5, "max chi2 per TPC clusters for cascades daughters") } trkQualityOpts; SliceCache cache; @@ -404,6 +408,56 @@ struct CascDiHadronCorr { registry.add("MCRecPri", "MCRecPri", {HistType::kTH1D, {axisPtEfficiency}}); } + if (cfgOutputCascQA) { + registry.add("QAhisto/hqaTPCNClsbefore", "", {HistType::kTH1D, {{100, 0, 200}}}); + registry.add("QAhisto/hqaTPCCrossedRowsbefore", "", {HistType::kTH1D, {{100, 0, 200}}}); + registry.add("QAhisto/hqaChi2prTPCclsbefore", "", {HistType::kTH1D, {{100, 0, 10}}}); + registry.add("QAhisto/hqaTPCNClsafter", "", {HistType::kTH1D, {{100, 0, 200}}}); + registry.add("QAhisto/hqaTPCCrossedRowsafter", "", {HistType::kTH1D, {{100, 0, 200}}}); + registry.add("QAhisto/hqaChi2prTPCclsafter", "", {HistType::kTH1D, {{100, 0, 10}}}); + + registry.add("QAhisto/Xi/hqaCascRadiusbefore", "", {HistType::kTH1D, {{1000, 0, 100}}}); + registry.add("QAhisto/Xi/hqaCascRadiusafter", "", {HistType::kTH1D, {{1000, 0, 100}}}); + registry.add("QAhisto/Xi/hqaCasccosPAbefore", "", {HistType::kTH1D, {{1000, 0.95, 1}}}); + registry.add("QAhisto/Xi/hqaCasccosPAafter", "", {HistType::kTH1D, {{1000, 0.95, 1}}}); + registry.add("QAhisto/Xi/hqaCascV0cosPAbefore", "", {HistType::kTH1D, {{1000, 0.95, 1}}}); + registry.add("QAhisto/Xi/hqaCascV0cosPAafter", "", {HistType::kTH1D, {{1000, 0.95, 1}}}); + registry.add("QAhisto/Xi/hqadcaCascV0toPVbefore", "", {HistType::kTH1D, {{1000, 0, 10}}}); + registry.add("QAhisto/Xi/hqadcaCascV0toPVafter", "", {HistType::kTH1D, {{1000, 0, 10}}}); + registry.add("QAhisto/Xi/hqadcaCascBachtoPVbefore", "", {HistType::kTH1D, {{1000, -10, 10}}}); + registry.add("QAhisto/Xi/hqadcaCascBachtoPVafter", "", {HistType::kTH1D, {{1000, -10, 10}}}); + registry.add("QAhisto/Xi/hqadcaCascLaprtoPVbefore", "", {HistType::kTH1D, {{1000, -10, 10}}}); + registry.add("QAhisto/Xi/hqadcaCascLaprtoPVafter", "", {HistType::kTH1D, {{1000, -10, 10}}}); + registry.add("QAhisto/Xi/hqadcaCascLapitoPVbefore", "", {HistType::kTH1D, {{1000, -10, 10}}}); + registry.add("QAhisto/Xi/hqadcaCascLapitoPVafter", "", {HistType::kTH1D, {{1000, -10, 10}}}); + registry.add("QAhisto/Xi/hqadcaCascdaubefore", "", {HistType::kTH1D, {{100, 0, 1}}}); + registry.add("QAhisto/Xi/hqadcaCascdauafter", "", {HistType::kTH1D, {{100, 0, 1}}}); + registry.add("QAhisto/Xi/hqadcaCascV0daubefore", "", {HistType::kTH1D, {{100, 0, 1}}}); + registry.add("QAhisto/Xi/hqadcaCascV0dauafter", "", {HistType::kTH1D, {{100, 0, 1}}}); + registry.add("QAhisto/Xi/hqaLambdaMassbefore", "", {HistType::kTH1D, {{160, 1.08, 1.16}}}); + registry.add("QAhisto/Xi/hqaLambdaMassafter", "", {HistType::kTH1D, {{160, 1.08, 1.16}}}); + + registry.add("QAhisto/Omega/hqaCascRadiusbefore", "", {HistType::kTH1D, {{1000, 0, 100}}}); + registry.add("QAhisto/Omega/hqaCascRadiusafter", "", {HistType::kTH1D, {{1000, 0, 100}}}); + registry.add("QAhisto/Omega/hqaCasccosPAbefore", "", {HistType::kTH1D, {{1000, 0.95, 1}}}); + registry.add("QAhisto/Omega/hqaCasccosPAafter", "", {HistType::kTH1D, {{1000, 0.95, 1}}}); + registry.add("QAhisto/Omega/hqaCascV0cosPAbefore", "", {HistType::kTH1D, {{1000, 0.95, 1}}}); + registry.add("QAhisto/Omega/hqaCascV0cosPAafter", "", {HistType::kTH1D, {{1000, 0.95, 1}}}); + registry.add("QAhisto/Omega/hqadcaCascV0toPVbefore", "", {HistType::kTH1D, {{1000, 0, 10}}}); + registry.add("QAhisto/Omega/hqadcaCascV0toPVafter", "", {HistType::kTH1D, {{1000, 0, 10}}}); + registry.add("QAhisto/Omega/hqadcaCascBachtoPVbefore", "", {HistType::kTH1D, {{1000, -10, 10}}}); + registry.add("QAhisto/Omega/hqadcaCascBachtoPVafter", "", {HistType::kTH1D, {{1000, -10, 10}}}); + registry.add("QAhisto/Omega/hqadcaCascLaprtoPVbefore", "", {HistType::kTH1D, {{1000, -10, 10}}}); + registry.add("QAhisto/Omega/hqadcaCascLaprtoPVafter", "", {HistType::kTH1D, {{1000, -10, 10}}}); + registry.add("QAhisto/Omega/hqadcaCascLapitoPVbefore", "", {HistType::kTH1D, {{1000, -10, 10}}}); + registry.add("QAhisto/Omega/hqadcaCascLapitoPVafter", "", {HistType::kTH1D, {{1000, -10, 10}}}); + registry.add("QAhisto/Omega/hqadcaCascdaubefore", "", {HistType::kTH1D, {{100, 0, 1}}}); + registry.add("QAhisto/Omega/hqadcaCascdauafter", "", {HistType::kTH1D, {{100, 0, 1}}}); + registry.add("QAhisto/Omega/hqadcaCascV0daubefore", "", {HistType::kTH1D, {{100, 0, 1}}}); + registry.add("QAhisto/Omega/hqadcaCascV0dauafter", "", {HistType::kTH1D, {{100, 0, 1}}}); + registry.add("QAhisto/Omega/hqaLambdaMassbefore", "", {HistType::kTH1D, {{160, 1.08, 1.16}}}); + registry.add("QAhisto/Omega/hqaLambdaMassafter", "", {HistType::kTH1D, {{160, 1.08, 1.16}}}); + } LOGF(info, "Initializing correlation container"); std::vector corrAxis = {{axisSample, "Sample"}, {axisVertex, "z-vtx (cm)"}, @@ -512,6 +566,18 @@ struct CascDiHadronCorr { if (std::fabs(negdau.eta()) > trkQualityOpts.cfgCutEta.value) return false; + if (cfgOutputCascQA) { + registry.fill(HIST("QAhisto/hqaTPCNClsbefore"), bachelor.tpcNClsFound()); + registry.fill(HIST("QAhisto/hqaTPCCrossedRowsbefore"), bachelor.tpcNClsCrossedRows()); + registry.fill(HIST("QAhisto/hqaChi2prTPCclsbefore"), bachelor.tpcChi2NCl()); + registry.fill(HIST("QAhisto/hqaTPCNClsbefore"), posdau.tpcNClsFound()); + registry.fill(HIST("QAhisto/hqaTPCCrossedRowsbefore"), posdau.tpcNClsCrossedRows()); + registry.fill(HIST("QAhisto/hqaChi2prTPCclsbefore"), posdau.tpcChi2NCl()); + registry.fill(HIST("QAhisto/hqaTPCNClsbefore"), negdau.tpcNClsFound()); + registry.fill(HIST("QAhisto/hqaTPCCrossedRowsbefore"), negdau.tpcNClsCrossedRows()); + registry.fill(HIST("QAhisto/hqaChi2prTPCclsbefore"), negdau.tpcChi2NCl()); + } + // track quality check if (bachelor.itsNCls() <= trkQualityOpts.cfgMinITSNCls.value) return false; @@ -538,8 +604,63 @@ struct CascDiHadronCorr { return false; if (negdau.tpcNClsCrossedRows() <= trkQualityOpts.cfgTPCCrossedRows.value) return false; + if (bachelor.tpcChi2NCl() >= trkQualityOpts.cfgCutChi2prTPCcls.value) + return false; + if (posdau.tpcChi2NCl() >= trkQualityOpts.cfgCutChi2prTPCcls.value) + return false; + if (negdau.tpcChi2NCl() >= trkQualityOpts.cfgCutChi2prTPCcls.value) + return false; - if (cfgOutputXi) { + if (cfgOutputCascQA) { + registry.fill(HIST("QAhisto/hqaTPCNClsafter"), bachelor.tpcNClsFound()); + registry.fill(HIST("QAhisto/hqaTPCCrossedRowsafter"), bachelor.tpcNClsCrossedRows()); + registry.fill(HIST("QAhisto/hqaChi2prTPCclsafter"), bachelor.tpcChi2NCl()); + registry.fill(HIST("QAhisto/hqaTPCNClsafter"), posdau.tpcNClsFound()); + registry.fill(HIST("QAhisto/hqaTPCCrossedRowsafter"), posdau.tpcNClsCrossedRows()); + registry.fill(HIST("QAhisto/hqaChi2prTPCclsafter"), posdau.tpcChi2NCl()); + registry.fill(HIST("QAhisto/hqaTPCNClsafter"), negdau.tpcNClsFound()); + registry.fill(HIST("QAhisto/hqaTPCCrossedRowsafter"), negdau.tpcNClsCrossedRows()); + registry.fill(HIST("QAhisto/hqaChi2prTPCclsafter"), negdau.tpcChi2NCl()); + } + + if (cfgOutputCascQA) { + if (cfgOutputXi) { + registry.fill(HIST("QAhisto/Xi/hqaCascRadiusbefore"), casc.cascradius()); + registry.fill(HIST("QAhisto/Xi/hqaCasccosPAbefore"), casc.casccosPA(posX, posY, posZ)); + registry.fill(HIST("QAhisto/Xi/hqaCascV0cosPAbefore"), casc.v0cosPA(posX, posY, posZ)); + registry.fill(HIST("QAhisto/Xi/hqadcaCascV0toPVbefore"), casc.dcav0topv(posX, posY, posZ)); + registry.fill(HIST("QAhisto/Xi/hqadcaCascBachtoPVbefore"), casc.dcabachtopv()); + registry.fill(HIST("QAhisto/Xi/hqadcaCascdaubefore"), casc.dcacascdaughters()); + registry.fill(HIST("QAhisto/Xi/hqadcaCascV0daubefore"), casc.dcaV0daughters()); + registry.fill(HIST("QAhisto/Xi/hqaLambdaMassbefore"), casc.mLambda()); + if (casc.sign() > 0) { + registry.fill(HIST("QAhisto/Xi/hqadcaCascLaprtoPVbefore"), casc.dcapostopv()); + registry.fill(HIST("QAhisto/Xi/hqadcaCascLapitoPVbefore"), casc.dcanegtopv()); + } else { + registry.fill(HIST("QAhisto/Xi/hqadcaCascLaprtoPVbefore"), casc.dcanegtopv()); + registry.fill(HIST("QAhisto/Xi/hqadcaCascLapitoPVbefore"), casc.dcapostopv()); + } + } + if (cfgOutputOmega) { + registry.fill(HIST("QAhisto/Omega/hqaCascRadiusbefore"), casc.cascradius()); + registry.fill(HIST("QAhisto/Omega/hqaCasccosPAbefore"), casc.casccosPA(posX, posY, posZ)); + registry.fill(HIST("QAhisto/Omega/hqaCascV0cosPAbefore"), casc.v0cosPA(posX, posY, posZ)); + registry.fill(HIST("QAhisto/Omega/hqadcaCascV0toPVbefore"), casc.dcav0topv(posX, posY, posZ)); + registry.fill(HIST("QAhisto/Omega/hqadcaCascBachtoPVbefore"), casc.dcabachtopv()); + registry.fill(HIST("QAhisto/Omega/hqadcaCascdaubefore"), casc.dcacascdaughters()); + registry.fill(HIST("QAhisto/Omega/hqadcaCascV0daubefore"), casc.dcaV0daughters()); + registry.fill(HIST("QAhisto/Omega/hqaLambdaMassbefore"), casc.mLambda()); + if (casc.sign() > 0) { + registry.fill(HIST("QAhisto/Omega/hqadcaCascLaprtoPVbefore"), casc.dcapostopv()); + registry.fill(HIST("QAhisto/Omega/hqadcaCascLapitoPVbefore"), casc.dcanegtopv()); + } else { + registry.fill(HIST("QAhisto/Omega/hqadcaCascLaprtoPVbefore"), casc.dcanegtopv()); + registry.fill(HIST("QAhisto/Omega/hqadcaCascLapitoPVbefore"), casc.dcapostopv()); + } + } + } + + if (cfgOutputXi || cfgEvSel.cfgEvSelHaveXi.value) { if (casc.mXi() > massup || casc.mXi() < masslow) return false; if (casc.sign() < 0) { @@ -582,7 +703,7 @@ struct CascDiHadronCorr { if (std::fabs(casc.mLambda() - o2::constants::physics::MassLambda0) > cascBuilderOpts.cfgcasc_mlambdawindow.value) return false; } - if (cfgOutputOmega) { + if (cfgOutputOmega || cfgEvSel.cfgEvSelHaveOmega.value) { if (casc.mOmega() > massup || casc.mOmega() < masslow) return false; if (casc.sign() < 0) { @@ -625,6 +746,43 @@ struct CascDiHadronCorr { if (std::fabs(casc.mLambda() - o2::constants::physics::MassLambda0) > cascBuilderOpts.cfgcasc_mlambdawindow.value) return false; } + + if (cfgOutputCascQA) { + if (cfgOutputXi) { + registry.fill(HIST("QAhisto/Xi/hqaCascRadiusafter"), casc.cascradius()); + registry.fill(HIST("QAhisto/Xi/hqaCasccosPAafter"), casc.casccosPA(posX, posY, posZ)); + registry.fill(HIST("QAhisto/Xi/hqaCascV0cosPAafter"), casc.v0cosPA(posX, posY, posZ)); + registry.fill(HIST("QAhisto/Xi/hqadcaCascV0toPVafter"), casc.dcav0topv(posX, posY, posZ)); + registry.fill(HIST("QAhisto/Xi/hqadcaCascBachtoPVafter"), casc.dcabachtopv()); + registry.fill(HIST("QAhisto/Xi/hqadcaCascdauafter"), casc.dcacascdaughters()); + registry.fill(HIST("QAhisto/Xi/hqadcaCascV0dauafter"), casc.dcaV0daughters()); + registry.fill(HIST("QAhisto/Xi/hqaLambdaMassafter"), casc.mLambda()); + if (casc.sign() > 0) { + registry.fill(HIST("QAhisto/Xi/hqadcaCascLaprtoPVafter"), casc.dcapostopv()); + registry.fill(HIST("QAhisto/Xi/hqadcaCascLapitoPVafter"), casc.dcanegtopv()); + } else { + registry.fill(HIST("QAhisto/Xi/hqadcaCascLaprtoPVafter"), casc.dcanegtopv()); + registry.fill(HIST("QAhisto/Xi/hqadcaCascLapitoPVafter"), casc.dcapostopv()); + } + } + if (cfgOutputOmega) { + registry.fill(HIST("QAhisto/Omega/hqaCascRadiusafter"), casc.cascradius()); + registry.fill(HIST("QAhisto/Omega/hqaCasccosPAafter"), casc.casccosPA(posX, posY, posZ)); + registry.fill(HIST("QAhisto/Omega/hqaCascV0cosPAafter"), casc.v0cosPA(posX, posY, posZ)); + registry.fill(HIST("QAhisto/Omega/hqadcaCascV0toPVafter"), casc.dcav0topv(posX, posY, posZ)); + registry.fill(HIST("QAhisto/Omega/hqadcaCascBachtoPVafter"), casc.dcabachtopv()); + registry.fill(HIST("QAhisto/Omega/hqadcaCascdauafter"), casc.dcacascdaughters()); + registry.fill(HIST("QAhisto/Omega/hqadcaCascV0dauafter"), casc.dcaV0daughters()); + registry.fill(HIST("QAhisto/Omega/hqaLambdaMassafter"), casc.mLambda()); + if (casc.sign() > 0) { + registry.fill(HIST("QAhisto/Omega/hqadcaCascLaprtoPVafter"), casc.dcapostopv()); + registry.fill(HIST("QAhisto/Omega/hqadcaCascLapitoPVafter"), casc.dcanegtopv()); + } else { + registry.fill(HIST("QAhisto/Omega/hqadcaCascLaprtoPVafter"), casc.dcanegtopv()); + registry.fill(HIST("QAhisto/Omega/hqadcaCascLapitoPVafter"), casc.dcapostopv()); + } + } + } return true; } @@ -1410,6 +1568,18 @@ struct CascDiHadronCorr { } if (cfgEvSel.cfgUseAdditionalEventCut.value && !eventSelected(collision, tracks.size(), cent, true)) return; + + if (cfgEvSel.cfgEvSelHaveXi.value || cfgEvSel.cfgEvSelHaveOmega.value) { + bool haveCasc = false; + for (const auto& cascade : cascades) { + if (cascSelected(cascade, collision.posX(), collision.posY(), collision.posZ())) { + haveCasc = true; + break; + } + } + if (!haveCasc) + return; + } loadCorrection(bc.timestamp()); if (!cfgCentTableUnavailable) { getCentralityWeight(weightCent, cent); @@ -1453,7 +1623,7 @@ struct CascDiHadronCorr { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1514,7 +1684,7 @@ struct CascDiHadronCorr { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(cascades, tracks); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1563,7 +1733,7 @@ struct CascDiHadronCorr { PROCESS_SWITCH(CascDiHadronCorr, processMixedCasc, "Process mixed events", true); // the process for filling the mixed events by buffer - void processMixedBuffer(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks) + void processMixedBuffer(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks, aod::CascDatas const& cascades, DaughterTracks const&) { ValidCollision currentCollision; int nBinsMult = 0; @@ -1597,6 +1767,18 @@ struct CascDiHadronCorr { if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent < cfgCutCentMin || cent >= cfgCutCentMax)) return; + if (cfgEvSel.cfgEvSelHaveXi.value || cfgEvSel.cfgEvSelHaveOmega.value) { + bool haveCasc = false; + for (const auto& cascade : cascades) { + if (cascSelected(cascade, collision.posX(), collision.posY(), collision.posZ())) { + haveCasc = true; + break; + } + } + if (!haveCasc) + return; + } + float weightCent = 1.0f; if (!cfgCentTableUnavailable) getCentralityWeight(weightCent, cent); @@ -1650,6 +1832,18 @@ struct CascDiHadronCorr { if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent < cfgCutCentMin || cent >= cfgCutCentMax)) return; + if (cfgEvSel.cfgEvSelHaveXi.value || cfgEvSel.cfgEvSelHaveOmega.value) { + bool haveCasc = false; + for (const auto& cascade : cascades) { + if (cascSelected(cascade, collision.posX(), collision.posY(), collision.posZ())) { + haveCasc = true; + break; + } + } + if (!haveCasc) + return; + } + float weightCent = 1.0f; if (!cfgCentTableUnavailable) getCentralityWeight(weightCent, cent); @@ -1834,7 +2028,7 @@ struct CascDiHadronCorr { using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1890,7 +2084,7 @@ struct CascDiHadronCorr { using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1977,7 +2171,7 @@ struct CascDiHadronCorr { using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip diff --git a/PWGCF/TwoParticleCorrelations/Tasks/corrFit.cxx b/PWGCF/TwoParticleCorrelations/Tasks/corrFit.cxx index 27eb0d6fdf5..cc82c070425 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/corrFit.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/corrFit.cxx @@ -56,6 +56,7 @@ #include #include #include +#include #include #include @@ -66,6 +67,19 @@ using namespace o2::aod::rctsel; using namespace constants::math; #define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, (DEFAULT), (HELP)}; // NOLINT(bugprone-macro-parentheses) + +template +auto readMatrix(Array2D const& mat, P& array) +{ + for (auto i = 0; i < static_cast(mat.rows); ++i) { + for (auto j = 0; j < static_cast(mat.cols); ++j) { + array[i][j] = mat(i, j); + } + } + + return; +} + static constexpr std::array, 20> LongArrayFloat = {{{{1.1, 2.1, 3.1}}, {{1.2, 2.2, 3.2}}, {{1.3, 2.3, 3.3}}, {{-1.1, -2.1, -3.1}}, {{-1.2, -2.2, -3.2}}, {{-1.3, -2.3, -3.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}, {{1.3, 1.3, 1.3}}, {{-1.1, -1.1, -1.1}}, {{-1.2, -1.2, -1.2}}, {{-1.3, -1.3, -1.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}, {{1.3, 1.3, 1.3}}, {{-1.1, -1.1, -1.1}}, {{-1.2, -1.2, -1.2}}, {{-1.3, -1.3, -1.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}}}; static constexpr std::array, 20> LongArrayInt = {{{{1, 2, 3}}, {{1, 2, 3}}, {{1, 2, 3}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}, {{1, 1, 1}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}, {{1, 1, 1}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}}}; @@ -73,34 +87,37 @@ struct CorrFit { o2::aod::ITSResponse itsResponse; Service ccdb{}; - O2_DEFINE_CONFIGURABLE(cfgUseAdditionalEventCut, bool, true, "Use additional event cut on mult correlations") - O2_DEFINE_CONFIGURABLE(cfgZVtxCut, float, 10.0f, "Accepted z-vertex range") O2_DEFINE_CONFIGURABLE(cfgUseTransverseMomentum, bool, false, "Use transverse momentum for correlation container") O2_DEFINE_CONFIGURABLE(cfgQaCheck, bool, true, "Enable QA histograms for event selection") O2_DEFINE_CONFIGURABLE(cfgStrictTrackCounter, bool, false, "Strict track counter for multiplicity correlation cut, counts only tracks that pass all cuts and are used in the correlation") - O2_DEFINE_CONFIGURABLE(cfgRefpTt, bool, false, "Apply upper pT cut on reference tracks") - O2_DEFINE_CONFIGURABLE(cfgRefpTMax, float, 3.0f, "maximum pT for reference tracks if cfgRefpTt is true") - O2_DEFINE_CONFIGURABLE(cfgMinMultForCorrelations, int, 0, "minimum multiplicity for correlations") - O2_DEFINE_CONFIGURABLE(cfgMaxMultForCorrelations, int, 20, "maximum multiplicity for correlations") - O2_DEFINE_CONFIGURABLE(cfgRefMultiplicity, bool, false, "Use multiplicity of reference tracks for multiplicity correlation cut instead of Nch") + O2_DEFINE_CONFIGURABLE(cfgUseRefpTCut, bool, false, "Apply upper pT cut on reference tracks") + O2_DEFINE_CONFIGURABLE(cfgRefpTMax, float, 3.0f, "maximum pT for reference tracks if cfgUseRefpTCut is true") + O2_DEFINE_CONFIGURABLE(cfgRefpTMin, float, 0.2f, "minimum pT for reference tracks if cfgUseRefpTCut is true") + Configurable> cfgRunRemoveList{"cfgRunRemoveList", std::vector{-1}, "excluded run numbers"}; O2_DEFINE_CONFIGURABLE(cfgEvSelRCTflags, std::string, "", "keep empty to disable, usage: 'CentralBarrelTracking (CBT)', 'CBT_hadronPID' ") - struct : ConfigurableGroup{ - O2_DEFINE_CONFIGURABLE(cfgPtCutMin, float, 0.2f, "minimum accepted track pT") - O2_DEFINE_CONFIGURABLE(cfgPtCutMax, float, 10.0f, "maximum accepted track pT") - O2_DEFINE_CONFIGURABLE(cfgEtaCut, float, 0.8f, "Eta cut") - O2_DEFINE_CONFIGURABLE(cfgCutChi2prTPCcls, float, 2.5f, "max chi2 per TPC clusters") - O2_DEFINE_CONFIGURABLE(cfgCutTPCclu, float, 50.0f, "minimum TPC clusters") - O2_DEFINE_CONFIGURABLE(cfgCutTPCCrossedRows, float, 70.0f, "minimum TPC crossed rows") - O2_DEFINE_CONFIGURABLE(cfgCutITSclu, float, 5.0f, "minimum ITS clusters") - O2_DEFINE_CONFIGURABLE(cfgCutDCAz, float, 2.0f, "max DCA to vertex z")} cfgTrackCuts; - - struct : ConfigurableGroup{ - O2_DEFINE_CONFIGURABLE(cfgCutOccupancyHigh, int, 2000, "High cut on TPC occupancy") - O2_DEFINE_CONFIGURABLE(cfgCutOccupancyLow, int, 0, "Low cut on TPC occupancy")} cfgEventSelection; + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgGlobalPtMin, float, 0.2f, "minimum accepted track pT") + O2_DEFINE_CONFIGURABLE(cfgGlobalPtMax, float, 10.0f, "maximum accepted track pT") + O2_DEFINE_CONFIGURABLE(cfgGlobalEta, float, 0.8f, "Eta cut") + O2_DEFINE_CONFIGURABLE(cfgGlobalChi2prTPCcls, float, 2.5, "Chi2 per TPC clusters") + O2_DEFINE_CONFIGURABLE(cfgGlobalDCAz, float, 2.0f, "DCAz range for tracks") + Configurable> cfgTrackCutsDCAxy{"cfgTrackCutsDCAxy", std::vector{"(0.0026+0.005/(x^1.01))", "(0.0026+0.005/(x^1.01))"}, "Functional form of pt-dependent DCAxy cut"}; + Configurable> cfgTrackCutsDCAz{"cfgTrackCutsDCAz", std::vector{"", ""}, "Functional form of pt-dependent DCAz cut"}; + Configurable> cfgTrackCuts{"cfgTrackCuts", {LongArrayFloat.front().data(), 10, 2, {"PtMin", "PtMax", "EtaCut", "Chi2PrTpcCls", "TpcCluster", "TpcCrossedRows", "ItsCluster", "DCAz", "DCAzNsigma", "DCAxyNsigma"}, {"General", "SelNch"}}, "Labeled array for track selections"}; + } cfgTrkSel; - Configurable> cfgUseEventCuts{"cfgUseEventCuts", {LongArrayInt.front().data(), 14, 1, {"Filtered Events", "Sel8", "kNoTimeFrameBorder", "kNoITSROFrameBorder", "kNoSameBunchPileup", "kIsGoodZvtxFT0vsPV", "kNoCollInTimeRangeStandard", "kIsGoodITSLayersAll", "kIsGoodITSLayer0123", "kNoCollInRofStandard", "kNoHighMultCollInPrevRof", "Occupancy", "Multcorrelation", "T0AV0ACut"}, {"EvCuts"}}, "Labeled array (int) for various cuts on resonances"}; + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgGlobalCutVertex, float, 10.0f, "Accepted z-vertex range") + O2_DEFINE_CONFIGURABLE(cfgCutOccupancyHigh, int, 2000, "High cut on TPC occupancy") + O2_DEFINE_CONFIGURABLE(cfgCutOccupancyLow, int, 0, "Low cut on TPC occupancy") + O2_DEFINE_CONFIGURABLE(cfgUseAdditionalEventCut, bool, true, "Use additional event cut on mult correlations") + O2_DEFINE_CONFIGURABLE(cfgMinMultForCorrelations, int, 0, "minimum multiplicity for correlations") + O2_DEFINE_CONFIGURABLE(cfgMaxMultForCorrelations, int, 20, "maximum multiplicity for correlations") + O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FT0A") + Configurable> cfgUseEventCuts{"cfgUseEventCuts", {LongArrayInt.front().data(), 14, 1, {"Filtered Events", "Sel8", "kNoTimeFrameBorder", "kNoITSROFrameBorder", "kNoSameBunchPileup", "kIsGoodZvtxFT0vsPV", "kNoCollInTimeRangeStandard", "kIsGoodITSLayersAll", "kIsGoodITSLayer0123", "kNoCollInRofStandard", "kNoHighMultCollInPrevRof", "Occupancy", "Multcorrelation", "T0AV0ACut"}, {"EvCuts"}}, "Labeled array (int) for various cuts on resonances"}; + } cfgEvSel; O2_DEFINE_CONFIGURABLE(cfgMinMixEventNum, int, 5, "Minimum number of events to mix") O2_DEFINE_CONFIGURABLE(cfgMergingCut, float, 0.02, "Merging cut on track merge") @@ -114,7 +131,6 @@ struct CorrFit { O2_DEFINE_CONFIGURABLE(cfgLocalEfficiency, bool, false, "Use local efficiency object") O2_DEFINE_CONFIGURABLE(cfgLocalEfficiencyNch, bool, false, "Use local multiplicity dependent efficiency object"); O2_DEFINE_CONFIGURABLE(cfgUseEventWeights, bool, false, "Use event weights for mixed event") - O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FT0A") struct : ConfigurableGroup { O2_DEFINE_CONFIGURABLE(cfgMultCentHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 10.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); @@ -135,16 +151,20 @@ struct CorrFit { std::vector multPVT0CCutPars; std::vector multGlobalPVCutPars; std::vector multMultV0ACutPars; - TF1* fMultPVT0CCutLow = nullptr; - TF1* fMultPVT0CCutHigh = nullptr; - TF1* fMultT0CCutLow = nullptr; - TF1* fMultT0CCutHigh = nullptr; - TF1* fMultGlobalPVCutLow = nullptr; - TF1* fMultGlobalPVCutHigh = nullptr; - TF1* fMultMultV0ACutLow = nullptr; - TF1* fMultMultV0ACutHigh = nullptr; - TF1* fT0AV0AMean = nullptr; - TF1* fT0AV0ASigma = nullptr; + std::unique_ptr fMultPVT0CCutLow = nullptr; + std::unique_ptr fMultPVT0CCutHigh = nullptr; + std::unique_ptr fMultT0CCutLow = nullptr; + std::unique_ptr fMultT0CCutHigh = nullptr; + std::unique_ptr fMultGlobalPVCutLow = nullptr; + std::unique_ptr fMultGlobalPVCutHigh = nullptr; + std::unique_ptr fMultMultV0ACutLow = nullptr; + std::unique_ptr fMultMultV0ACutHigh = nullptr; + std::unique_ptr fT0AV0AMean = nullptr; + std::unique_ptr fT0AV0ASigma = nullptr; + std::unique_ptr fPtDepDCAxy = nullptr; + std::unique_ptr fPtDepDCAxyForNch = nullptr; + std::unique_ptr fPtDepDCAz = nullptr; + std::unique_ptr fPtDepDCAzForNch = nullptr; O2_DEFINE_CONFIGURABLE(cfgV0AT0Acut, int, 5, "V0AT0A cut") } cfgFuncParas; @@ -200,8 +220,8 @@ struct CorrFit { AxisSpec axisFit{cfgaxisFITamp, "fit amplitude"}; AxisSpec axisChID = {220, 0, 220}; - Filter collisionFilter = (nabs(aod::collision::posZ) < cfgZVtxCut); - Filter trackFilter = (nabs(aod::track::eta) < cfgTrackCuts.cfgEtaCut) && (cfgTrackCuts.cfgPtCutMin < aod::track::pt) && (cfgTrackCuts.cfgPtCutMax > aod::track::pt) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)); + Filter collisionFilter = (nabs(aod::collision::posZ) < cfgEvSel.cfgGlobalCutVertex); + Filter trackFilter = (nabs(aod::track::eta) < cfgTrkSel.cfgGlobalEta) && (aod::track::pt > cfgTrkSel.cfgGlobalPtMin) && (aod::track::pt < cfgTrkSel.cfgGlobalPtMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (nabs(aod::track::dcaZ) < cfgTrkSel.cfgGlobalDCAz) && (aod::track::tpcChi2NCl < cfgTrkSel.cfgGlobalChi2prTPCcls); using FilteredCollisions = soa::Filtered>; using FilteredTracks = soa::Filtered>; @@ -229,9 +249,12 @@ struct CorrFit { OutputObj mixedTPC{"mixedEvent_TPC"}; HistogramRegistry registry{"registry"}; + std::array, 10> trackCuts{}; + std::array, 14> eventCuts{}; + std::array, 6> nSigmaVals{}; // define global variables - TRandom3* gRandom = new TRandom3(); + TRandom3 fRandom{0}; enum EventType { SameEvent = 1, @@ -239,63 +262,87 @@ struct CorrFit { }; enum FITIndex { - kFT0A = 0, - kFT0C = 1 + IndexFT0A = 0, + IndexFT0C = 1 }; enum PIDIndex { - kCharged = 0, - kPions, - kKaons, - kProtons, - kK0, - kLambda, - kPhi + UseCharged = 0, + UsePions, + UseKaons, + UseProtons, + UseK0, + UseLambda, + UsePhi }; enum PiKpArrayIndex { - iPionUp = 0, - iKaonUp, - iProtonUp, - iPionLow, - iKaonLow, - iProtonLow + IndexPionUp = 0, + IndexKaonUp, + IndexProtonUp, + IndexPionLow, + IndexKaonLow, + IndexProtonLow }; enum DetectorType { - kTPC = 0, - kTOF, - kITS + UseTPC = 0, + UseTof, + UseITS + }; + enum Stage { + Before = 0, + After }; enum EventCutTypes { - kFilteredEvents = 0, - kAfterSel8, - kUseNoTimeFrameBorder, - kUseNoITSROFrameBorder, - kUseNoSameBunchPileup, - kUseGoodZvtxFT0vsPV, - kUseNoCollInTimeRangeStandard, - kUseGoodITSLayersAll, - kUseGoodITSLayer0123, - kUseNoCollInRofStandard, - kUseNoHighMultCollInPrevRof, - kUseOccupancy, - kUseMultCorrCut, - kUseT0AV0ACut, - kNEventCuts + FilteredEvents = 0, + AfterSel8, + UseNoTimeFrameBorder, + UseNoITSROFrameBorder, + UseNoSameBunchPileup, + UseGoodZvtxFT0vsPV, + UseNoCollInTimeRangeStandard, + UseGoodITSLayersAll, + UseGoodITSLayer0123, + UseNoCollInRofStandard, + UseNoHighMultCollInPrevRof, + UseOccupancy, + UseMultCorrCut, + UseT0AV0ACut, + HaveFT0Cut, + NumEventCuts }; enum EventCutType { - kEvCut1 = 0, - kNEvCutTypes = 1 + EvCut1 = 0, + NumEvCutTypes = 1 + }; + + enum TrackCuts { + TrkCutPtMin = 0, + TrkCutPtMax, + TrkCutEtaCut, + TrkCutChi2PrTpcCls, + TrkCutTpcCluster, + TrkCutTpcCrossedRows, + TrkCutItsCluster, + TrkCutDCAz, + TrkCutDCAzNsigma, + TrkCutDCAxyNsigma + }; + + enum TrackCutGroup { + UseGenTrkCuts = 0, + UseNchSelCuts, + NumTrackCutTypes }; enum CentEstimators { - kCentFT0C = 0, - kCentFT0CVariant1, - kCentFT0M, - kCentFV0A, + UseCentFT0C = 0, + UseCentFT0CVariant1, + UseCentFT0M, + UseCentFV0A, // Count the total number of enum - kCount_CentEstimators + NumCentEstimators }; RCTFlagsChecker rctChecker{"CBT"}; @@ -309,77 +356,102 @@ struct CorrFit { ccdb->setCreatedNotAfter(now); LOGF(info, "Starting init"); - + readMatrix(cfgTrkSel.cfgTrackCuts->getData(), trackCuts); + readMatrix(cfgEvSel.cfgUseEventCuts->getData(), eventCuts); + readMatrix(cfgPIDConfigs.nSigmas->getData(), nSigmaVals); if (doprocessSameFt0aFt0c || doprocessSameTpcFt0a || doprocessSameTpcFt0c || doprocessSameTPC) { registry.add("hEventCountRct", "Number of Event;; Count", {HistType::kTH1D, {{2, 0, 2}}}); registry.get(HIST("hEventCountRct"))->GetXaxis()->SetBinLabel(1, "rct fail"); registry.get(HIST("hEventCountRct"))->GetXaxis()->SetBinLabel(2, "rct pass"); registry.add("hEventCount", "Number of Event;; Count", {HistType::kTH1D, {{14, -0.5, 13.5}}}); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kFilteredEvents + 1, "Filtered events"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kAfterSel8 + 1, "After sel8"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoTimeFrameBorder + 1, "kNoTimeFrameBorder"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoITSROFrameBorder + 1, "kNoITSROFrameBorder"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoSameBunchPileup + 1, "kNoSameBunchPileup"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseGoodITSLayer0123 + 1, "kIsGoodITSLayer0123"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoCollInRofStandard + 1, "kNoCollInRofStandard"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoHighMultCollInPrevRof + 1, "kNoHighMultCollInPrevRof"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseOccupancy + 1, "Occupancy Cut"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseMultCorrCut + 1, "MultCorrelation Cut"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseT0AV0ACut + 1, "T0AV0A cut"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(FilteredEvents + 1, "Filtered events"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(AfterSel8 + 1, "After sel8"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoTimeFrameBorder + 1, "kNoTimeFrameBorder"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoITSROFrameBorder + 1, "kNoITSROFrameBorder"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoSameBunchPileup + 1, "kNoSameBunchPileup"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodITSLayer0123 + 1, "kIsGoodITSLayer0123"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoCollInRofStandard + 1, "kNoCollInRofStandard"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoHighMultCollInPrevRof + 1, "kNoHighMultCollInPrevRof"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseOccupancy + 1, "Occupancy Cut"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseMultCorrCut + 1, "MultCorrelation Cut"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseT0AV0ACut + 1, "T0AV0A cut"); } if ((doprocessSameFt0aFt0c || doprocessSameTpcFt0a || doprocessSameTpcFt0c || doprocessSameTPC) && cfgQaCheck) { registry.add("hPassedEventSelection", "Number of Event;; Count", {HistType::kTH1D, {{12, -0.5, 11.5}}}); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kFilteredEvents + 1, "Filtered events"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kAfterSel8 + 1, "After sel8"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseNoTimeFrameBorder + 1, "kNoTimeFrameBorder"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseNoITSROFrameBorder + 1, "kNoITSROFrameBorder"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseNoSameBunchPileup + 1, "kNoSameBunchPileup"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseGoodITSLayer0123 + 1, "kIsGoodITSLayer0123"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseNoCollInRofStandard + 1, "kNoCollInRofStandard"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseNoHighMultCollInPrevRof + 1, "kNoHighMultCollInPrevRof"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseOccupancy + 1, "Occupancy Cut"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(FilteredEvents + 1, "Filtered events"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(AfterSel8 + 1, "After sel8"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoTimeFrameBorder + 1, "kNoTimeFrameBorder"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoITSROFrameBorder + 1, "kNoITSROFrameBorder"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoSameBunchPileup + 1, "kNoSameBunchPileup"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseGoodITSLayer0123 + 1, "kIsGoodITSLayer0123"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoCollInRofStandard + 1, "kNoCollInRofStandard"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoHighMultCollInPrevRof + 1, "kNoHighMultCollInPrevRof"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseOccupancy + 1, "Occupancy Cut"); } // Multiplicity correlation cuts - if (cfgUseEventCuts->getData()[kUseMultCorrCut][kEvCut1] != 0) { + if (eventCuts[UseMultCorrCut][EvCut1] != 0) { cfgFuncParas.multT0CCutPars = cfgFuncParas.cfgMultT0CCutPars; cfgFuncParas.multPVT0CCutPars = cfgFuncParas.cfgMultPVT0CCutPars; cfgFuncParas.multGlobalPVCutPars = cfgFuncParas.cfgMultGlobalPVCutPars; cfgFuncParas.multMultV0ACutPars = cfgFuncParas.cfgMultMultV0ACutPars; - cfgFuncParas.fMultPVT0CCutLow = new TF1("fMultPVT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); + + cfgFuncParas.fMultPVT0CCutLow = std::make_unique("fMultPVT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); cfgFuncParas.fMultPVT0CCutLow->SetParameters(cfgFuncParas.multPVT0CCutPars.data()); - cfgFuncParas.fMultPVT0CCutHigh = new TF1("fMultPVT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultPVT0CCutHigh = std::make_unique("fMultPVT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); cfgFuncParas.fMultPVT0CCutHigh->SetParameters(cfgFuncParas.multPVT0CCutPars.data()); - cfgFuncParas.fMultT0CCutLow = new TF1("fMultT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultT0CCutLow = std::make_unique("fMultT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); cfgFuncParas.fMultT0CCutLow->SetParameters(cfgFuncParas.multT0CCutPars.data()); - cfgFuncParas.fMultT0CCutHigh = new TF1("fMultT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultT0CCutHigh = std::make_unique("fMultT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); cfgFuncParas.fMultT0CCutHigh->SetParameters(cfgFuncParas.multT0CCutPars.data()); - cfgFuncParas.fMultGlobalPVCutLow = new TF1("fMultGlobalPVCutLow", cfgFuncParas.cfgMultMultPVLowCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultGlobalPVCutLow = std::make_unique("fMultGlobalPVCutLow", cfgFuncParas.cfgMultMultPVLowCutFunction->c_str(), 0, 4000); cfgFuncParas.fMultGlobalPVCutLow->SetParameters(cfgFuncParas.multGlobalPVCutPars.data()); - cfgFuncParas.fMultGlobalPVCutHigh = new TF1("fMultGlobalPVCutHigh", cfgFuncParas.cfgMultMultPVHighCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultGlobalPVCutHigh = std::make_unique("fMultGlobalPVCutHigh", cfgFuncParas.cfgMultMultPVHighCutFunction->c_str(), 0, 4000); cfgFuncParas.fMultGlobalPVCutHigh->SetParameters(cfgFuncParas.multGlobalPVCutPars.data()); - cfgFuncParas.fMultMultV0ACutLow = new TF1("fMultMultV0ACutLow", cfgFuncParas.cfgMultMultV0ALowCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultMultV0ACutLow = std::make_unique("fMultMultV0ACutLow", cfgFuncParas.cfgMultMultV0ALowCutFunction->c_str(), 0, 4000); cfgFuncParas.fMultMultV0ACutLow->SetParameters(cfgFuncParas.multMultV0ACutPars.data()); - cfgFuncParas.fMultMultV0ACutHigh = new TF1("fMultMultV0ACutHigh", cfgFuncParas.cfgMultMultV0AHighCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultMultV0ACutHigh = std::make_unique("fMultMultV0ACutHigh", cfgFuncParas.cfgMultMultV0AHighCutFunction->c_str(), 0, 4000); cfgFuncParas.fMultMultV0ACutHigh->SetParameters(cfgFuncParas.multMultV0ACutPars.data()); } - if (cfgUseEventCuts->getData()[kUseT0AV0ACut][kEvCut1] != 0) { - cfgFuncParas.fT0AV0AMean = new TF1("fT0AV0AMean", "[0]+[1]*x", 0, 200000); + if (eventCuts[UseT0AV0ACut][EvCut1] != 0) { + cfgFuncParas.fT0AV0AMean = std::make_unique("fT0AV0AMean", "[0]+[1]*x", 0, 200000); cfgFuncParas.fT0AV0AMean->SetParameters(-1601.0581, 9.417652e-01); - cfgFuncParas.fT0AV0ASigma = new TF1("fT0AV0ASigma", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 200000); + cfgFuncParas.fT0AV0ASigma = std::make_unique("fT0AV0ASigma", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 200000); cfgFuncParas.fT0AV0ASigma->SetParameters(463.4144, 6.796509e-02, -9.097136e-07, 7.971088e-12, -2.600581e-17); } + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].empty()) { + cfgFuncParas.fPtDepDCAxy = std::make_unique("ptDepDCAxy", cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAxy->SetParameter(0, trackCuts[TrkCutDCAxyNsigma][UseGenTrkCuts]); + LOGF(info, "DCAxy pt-dependence function: %0.1f * %s", trackCuts[TrkCutDCAxyNsigma][UseGenTrkCuts], cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].c_str()); + } + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].empty()) { + cfgFuncParas.fPtDepDCAxyForNch = std::make_unique("ptDepDCAxyForNch", cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAxyForNch->SetParameter(0, trackCuts[TrkCutDCAxyNsigma][UseNchSelCuts]); + LOGF(info, "DCAxy pt-dependence function for Nch: %0.1f * %s", trackCuts[TrkCutDCAxyNsigma][UseNchSelCuts], cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].c_str()); + } + + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].empty()) { + cfgFuncParas.fPtDepDCAz = std::make_unique("ptDepDCAz", cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAz->SetParameter(0, trackCuts[TrkCutDCAzNsigma][UseGenTrkCuts]); + LOGF(info, "DCAz pt-dependence function: %0.1f * %s", trackCuts[TrkCutDCAzNsigma][UseGenTrkCuts], cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].c_str()); + } + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].empty()) { + cfgFuncParas.fPtDepDCAzForNch = std::make_unique("ptDepDCAzForNch", cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAzForNch->SetParameter(0, trackCuts[TrkCutDCAzNsigma][UseNchSelCuts]); + LOGF(info, "DCAz pt-dependence function for Nch: %0.1f * %s", trackCuts[TrkCutDCAzNsigma][UseNchSelCuts], cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].c_str()); + } + if (doprocessSameTPC || doprocessSameFt0aFt0c || doprocessSameTpcFt0a || doprocessSameTpcFt0c) { registry.add("Phi", "Phi", {HistType::kTH1D, {axisPhi}}); registry.add("Eta", "Eta", {HistType::kTH1D, {axisEta}}); @@ -426,24 +498,24 @@ struct CorrFit { registry.add("deltaEta_deltaPhi_mixed_TPC_FT0A", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0a}}); registry.add("Assoc_amp_same_TPC_FT0A", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); registry.add("Assoc_amp_mixed_TPC_FT0A", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); - registry.add("Trig_hist_TPC_FT0A", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger}}}); + registry.add("Trig_hist_TPC_FT0A", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger, axisMult}}}); } if (doprocessSameTpcFt0c) { registry.add("deltaEta_deltaPhi_same_TPC_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0c}}); // check to see the delta eta and delta phi distribution registry.add("deltaEta_deltaPhi_mixed_TPC_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0c}}); registry.add("Assoc_amp_same_TPC_FT0C", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); registry.add("Assoc_amp_mixed_TPC_FT0C", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); - registry.add("Trig_hist_TPC_FT0C", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger}}}); + registry.add("Trig_hist_TPC_FT0C", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger, axisMult}}}); } if (doprocessSameFt0aFt0c) { registry.add("deltaEta_deltaPhi_same_FT0A_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaFt0aFt0c}}); // check to see the delta eta and delta phi distribution registry.add("deltaEta_deltaPhi_mixed_FT0A_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaFt0aFt0c}}); - registry.add("Trig_hist_FT0A_FT0C", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger}}}); + registry.add("Trig_hist_FT0A_FT0C", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger, axisMult}}}); } if (doprocessSameTPC) { registry.add("deltaEta_deltaPhi_same_TPC", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcTpc}}); // check to see the delta eta and delta phi distribution registry.add("deltaEta_deltaPhi_mixed_TPC", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcTpc}}); - registry.add("Trig_hist_TPC", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger}}}); + registry.add("Trig_hist_TPC", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger, axisMult}}}); } registry.add("eventcount", "bin", {HistType::kTH1F, {{4, 0, 4, "bin"}}}); // histogram to see how many events are in the same and mixed event @@ -511,20 +583,20 @@ struct CorrFit { } template - float getCentrality(TCollision const& collision) + double getCentrality(TCollision const& collision) { - float cent = 0.0; - switch (cfgCentEstimator) { - case kCentFT0C: + double cent = 0.0; + switch (cfgEvSel.cfgCentEstimator) { + case UseCentFT0C: cent = collision.centFT0C(); break; - case kCentFT0CVariant1: + case UseCentFT0CVariant1: cent = collision.centFT0CVariant1(); break; - case kCentFT0M: + case UseCentFT0M: cent = collision.centFT0M(); break; - case kCentFV0A: + case UseCentFV0A: cent = collision.centFV0A(); break; default: @@ -537,183 +609,206 @@ struct CorrFit { bool eventRct(TCollision const& collision, const bool fillCounter) { if (!rctChecker(collision)) { - if (fillCounter) + if (fillCounter) { registry.fill(HIST("hEventCountRct"), 0.5); + } - return 0; + return false; } - if (fillCounter) + if (fillCounter) { registry.fill(HIST("hEventCountRct"), 1.5); + } - return 1; + return true; } template bool eventSelected(TCollision const& collision, const int mult, const bool fillCounter) { - if (cfgUseEventCuts->getData()[kUseNoTimeFrameBorder][kEvCut1] && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { - return 0; + if (eventCuts[UseNoTimeFrameBorder][EvCut1] && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + if (fillCounter && eventCuts[UseNoTimeFrameBorder][EvCut1]) { + registry.fill(HIST("hEventCount"), UseNoTimeFrameBorder); } - if (fillCounter && cfgUseEventCuts->getData()[kUseNoTimeFrameBorder][kEvCut1]) - registry.fill(HIST("hEventCount"), kUseNoTimeFrameBorder); - if (cfgUseEventCuts->getData()[kUseNoITSROFrameBorder][kEvCut1] && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { - return 0; + if (eventCuts[UseNoITSROFrameBorder][EvCut1] && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + if (fillCounter && eventCuts[UseNoITSROFrameBorder][EvCut1]) { + registry.fill(HIST("hEventCount"), UseNoITSROFrameBorder); } - if (fillCounter && cfgUseEventCuts->getData()[kUseNoITSROFrameBorder][kEvCut1]) - registry.fill(HIST("hEventCount"), kUseNoITSROFrameBorder); - if (cfgUseEventCuts->getData()[kUseNoSameBunchPileup][kEvCut1] && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + if (eventCuts[UseNoSameBunchPileup][EvCut1] && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { // rejects collisions which are associated with the same "found-by-T0" bunch crossing // https://indico.cern.ch/event/1396220/#1-event-selection-with-its-rof - return 0; + return false; + } + if (fillCounter && eventCuts[UseNoSameBunchPileup][EvCut1]) { + registry.fill(HIST("hEventCount"), UseNoSameBunchPileup); } - if (fillCounter && cfgUseEventCuts->getData()[kUseNoSameBunchPileup][kEvCut1]) - registry.fill(HIST("hEventCount"), kUseNoSameBunchPileup); - if (cfgUseEventCuts->getData()[kUseGoodZvtxFT0vsPV][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + if (eventCuts[UseGoodZvtxFT0vsPV][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference // use this cut at low multiplicities with caution - return 0; + return false; + } + if (fillCounter && eventCuts[UseGoodZvtxFT0vsPV][EvCut1]) { + registry.fill(HIST("hEventCount"), UseGoodZvtxFT0vsPV); } - if (fillCounter && cfgUseEventCuts->getData()[kUseGoodZvtxFT0vsPV][kEvCut1]) - registry.fill(HIST("hEventCount"), kUseGoodZvtxFT0vsPV); - if (cfgUseEventCuts->getData()[kUseNoCollInTimeRangeStandard][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + if (eventCuts[UseNoCollInTimeRangeStandard][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { // no collisions in specified time range - return 0; + return false; } - if (fillCounter && cfgUseEventCuts->getData()[kUseNoCollInTimeRangeStandard][kEvCut1]) - registry.fill(HIST("hEventCount"), kUseNoCollInTimeRangeStandard); + if (fillCounter && eventCuts[UseNoCollInTimeRangeStandard][EvCut1]) { + registry.fill(HIST("hEventCount"), UseNoCollInTimeRangeStandard); + } - if (cfgUseEventCuts->getData()[kUseGoodITSLayersAll][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + if (eventCuts[UseGoodITSLayersAll][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { // from Jan 9 2025 AOT meeting // cut time intervals with dead ITS staves - return 0; + return false; } - if (fillCounter && cfgUseEventCuts->getData()[kUseGoodITSLayersAll][kEvCut1]) - registry.fill(HIST("hEventCount"), kUseGoodITSLayersAll); + if (fillCounter && eventCuts[UseGoodITSLayersAll][EvCut1]) { + registry.fill(HIST("hEventCount"), UseGoodITSLayersAll); + } - if (cfgUseEventCuts->getData()[kUseGoodITSLayer0123][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { - return 0; + if (eventCuts[UseGoodITSLayer0123][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { + return false; + } + if (fillCounter && eventCuts[UseGoodITSLayer0123][EvCut1]) { + registry.fill(HIST("hEventCount"), UseGoodITSLayer0123); } - if (fillCounter && cfgUseEventCuts->getData()[kUseGoodITSLayer0123][kEvCut1]) - registry.fill(HIST("hEventCount"), kUseGoodITSLayer0123); - if (cfgUseEventCuts->getData()[kUseNoCollInRofStandard][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + if (eventCuts[UseNoCollInRofStandard][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { // no other collisions in this Readout Frame with per-collision multiplicity above threshold - return 0; + return false; } - if (fillCounter && cfgUseEventCuts->getData()[kUseNoCollInRofStandard][kEvCut1]) - registry.fill(HIST("hEventCount"), kUseNoCollInRofStandard); + if (fillCounter && eventCuts[UseNoCollInRofStandard][EvCut1]) { + registry.fill(HIST("hEventCount"), UseNoCollInRofStandard); + } - if (cfgUseEventCuts->getData()[kUseNoHighMultCollInPrevRof][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + if (eventCuts[UseNoHighMultCollInPrevRof][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { // veto an event if FT0C amplitude in previous ITS ROF is above threshold - return 0; + return false; + } + if (fillCounter && eventCuts[UseNoHighMultCollInPrevRof][EvCut1]) { + registry.fill(HIST("hEventCount"), UseNoHighMultCollInPrevRof); } - if (fillCounter && cfgUseEventCuts->getData()[kUseNoHighMultCollInPrevRof][kEvCut1]) - registry.fill(HIST("hEventCount"), kUseNoHighMultCollInPrevRof); auto multNTracksPV = collision.multNTracksPV(); auto occupancy = collision.trackOccupancyInTimeRange(); - if (cfgUseEventCuts->getData()[kUseOccupancy][kEvCut1] && (occupancy < cfgEventSelection.cfgCutOccupancyLow || occupancy > cfgEventSelection.cfgCutOccupancyHigh)) { - return 0; + if (eventCuts[UseOccupancy][EvCut1] && (occupancy < cfgEvSel.cfgCutOccupancyLow || occupancy > cfgEvSel.cfgCutOccupancyHigh)) { + return false; + } + if (fillCounter && eventCuts[UseOccupancy][EvCut1]) { + registry.fill(HIST("hEventCount"), UseOccupancy); } - if (fillCounter && cfgUseEventCuts->getData()[kUseOccupancy][kEvCut1]) - registry.fill(HIST("hEventCount"), kUseOccupancy); - if (cfgUseEventCuts->getData()[kUseMultCorrCut][kEvCut1]) { + if (eventCuts[UseMultCorrCut][EvCut1]) { float cent = getCentrality(collision); if (cfgFuncParas.cfgMultPVT0CCutEnabled) { - if (multNTracksPV < cfgFuncParas.fMultPVT0CCutLow->Eval(cent)) - return 0; - if (multNTracksPV > cfgFuncParas.fMultPVT0CCutHigh->Eval(cent)) - return 0; + if (multNTracksPV < cfgFuncParas.fMultPVT0CCutLow->Eval(cent)) { + return false; + } + if (multNTracksPV > cfgFuncParas.fMultPVT0CCutHigh->Eval(cent)) { + return false; + } } if (cfgFuncParas.cfgMultT0CCutEnabled) { - if (mult < cfgFuncParas.fMultT0CCutLow->Eval(cent)) - return 0; - if (mult > cfgFuncParas.fMultT0CCutHigh->Eval(cent)) - return 0; + if (mult < cfgFuncParas.fMultT0CCutLow->Eval(cent)) { + return false; + } + if (mult > cfgFuncParas.fMultT0CCutHigh->Eval(cent)) { + return false; + } } if (cfgFuncParas.cfgMultGlobalPVCutEnabled) { - if (mult < cfgFuncParas.fMultGlobalPVCutLow->Eval(multNTracksPV)) - return 0; - if (mult > cfgFuncParas.fMultGlobalPVCutHigh->Eval(multNTracksPV)) - return 0; + if (mult < cfgFuncParas.fMultGlobalPVCutLow->Eval(multNTracksPV)) { + return false; + } + if (mult > cfgFuncParas.fMultGlobalPVCutHigh->Eval(multNTracksPV)) { + return false; + } } if (cfgFuncParas.cfgMultMultV0ACutEnabled) { - if (collision.multFV0A() < cfgFuncParas.fMultMultV0ACutLow->Eval(mult)) - return 0; - if (collision.multFV0A() > cfgFuncParas.fMultMultV0ACutHigh->Eval(mult)) - return 0; + if (collision.multFV0A() < cfgFuncParas.fMultMultV0ACutLow->Eval(mult)) { + return false; + } + if (collision.multFV0A() > cfgFuncParas.fMultMultV0ACutHigh->Eval(mult)) { + return false; + } } } - if (fillCounter && cfgUseEventCuts->getData()[kUseMultCorrCut][kEvCut1]) - registry.fill(HIST("hEventCount"), kUseMultCorrCut); + if (fillCounter && eventCuts[UseMultCorrCut][EvCut1]) { + registry.fill(HIST("hEventCount"), UseMultCorrCut); + } // V0A T0A 5 sigma cut - if (cfgUseEventCuts->getData()[kUseT0AV0ACut][kEvCut1] && (std::fabs(collision.multFV0A() - cfgFuncParas.fT0AV0AMean->Eval(collision.multFT0A())) > cfgFuncParas.cfgV0AT0Acut * cfgFuncParas.fT0AV0ASigma->Eval(collision.multFT0A()))) - return 0; - if (fillCounter && cfgUseEventCuts->getData()[kUseT0AV0ACut][kEvCut1]) - registry.fill(HIST("hEventCount"), kUseT0AV0ACut); + if (eventCuts[UseT0AV0ACut][EvCut1] && (std::fabs(collision.multFV0A() - cfgFuncParas.fT0AV0AMean->Eval(collision.multFT0A())) > cfgFuncParas.cfgV0AT0Acut * cfgFuncParas.fT0AV0ASigma->Eval(collision.multFT0A()))) { + return false; + } + if (fillCounter && eventCuts[UseT0AV0ACut][EvCut1]) { + registry.fill(HIST("hEventCount"), UseT0AV0ACut); + } - return 1; + return true; } template void eventSelectedIndividually(TCollision const& collision) { - registry.fill(HIST("hPassedEventSelection"), kFilteredEvents); + registry.fill(HIST("hPassedEventSelection"), FilteredEvents); if (collision.sel8()) { - registry.fill(HIST("hPassedEventSelection"), kAfterSel8); + registry.fill(HIST("hPassedEventSelection"), AfterSel8); } if (collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { - registry.fill(HIST("hPassedEventSelection"), kUseNoTimeFrameBorder); + registry.fill(HIST("hPassedEventSelection"), UseNoTimeFrameBorder); } if (collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { - registry.fill(HIST("hPassedEventSelection"), kUseNoITSROFrameBorder); + registry.fill(HIST("hPassedEventSelection"), UseNoITSROFrameBorder); } if (collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { - registry.fill(HIST("hPassedEventSelection"), kUseNoSameBunchPileup); + registry.fill(HIST("hPassedEventSelection"), UseNoSameBunchPileup); } if (collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { - registry.fill(HIST("hPassedEventSelection"), kUseGoodZvtxFT0vsPV); + registry.fill(HIST("hPassedEventSelection"), UseGoodZvtxFT0vsPV); } if (collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { - registry.fill(HIST("hPassedEventSelection"), kUseNoCollInTimeRangeStandard); + registry.fill(HIST("hPassedEventSelection"), UseNoCollInTimeRangeStandard); } if (collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { - registry.fill(HIST("hPassedEventSelection"), kUseGoodITSLayersAll); + registry.fill(HIST("hPassedEventSelection"), UseGoodITSLayersAll); } if (collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { - registry.fill(HIST("hPassedEventSelection"), kUseGoodITSLayer0123); + registry.fill(HIST("hPassedEventSelection"), UseGoodITSLayer0123); } if (collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { - registry.fill(HIST("hPassedEventSelection"), kUseNoCollInRofStandard); + registry.fill(HIST("hPassedEventSelection"), UseNoCollInRofStandard); } if (collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { - registry.fill(HIST("hPassedEventSelection"), kUseNoHighMultCollInPrevRof); + registry.fill(HIST("hPassedEventSelection"), UseNoHighMultCollInPrevRof); } auto occupancy = collision.trackOccupancyInTimeRange(); - if (cfgUseEventCuts->getData()[kUseOccupancy][kEvCut1] && (occupancy < cfgEventSelection.cfgCutOccupancyLow || occupancy > cfgEventSelection.cfgCutOccupancyHigh)) { - registry.fill(HIST("hPassedEventSelection"), kUseOccupancy); + if (eventCuts[UseOccupancy][EvCut1] && (occupancy > cfgEvSel.cfgCutOccupancyLow && occupancy < cfgEvSel.cfgCutOccupancyHigh)) { + registry.fill(HIST("hPassedEventSelection"), UseOccupancy); } } @@ -759,7 +854,39 @@ struct CorrFit { template bool trackSelected(TTrack const& track) { - return ((track.tpcNClsFound() >= cfgTrackCuts.cfgCutTPCclu) && (track.tpcNClsCrossedRows() >= cfgTrackCuts.cfgCutTPCCrossedRows) && (track.itsNCls() >= cfgTrackCuts.cfgCutITSclu) && (track.tpcChi2NCl() < cfgTrackCuts.cfgCutChi2prTPCcls) && (track.dcaZ() < cfgTrackCuts.cfgCutDCAz)); + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].empty() && std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxy->Eval(track.pt())) { + return false; + } + + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].empty()) { + if (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAz->Eval(track.pt())) { + return false; + } + } else { + if (std::fabs(track.dcaZ()) > trackCuts[TrkCutDCAz][UseGenTrkCuts]) { + return false; + } + } + + return ((std::abs(track.eta()) < trackCuts[TrkCutEtaCut][UseGenTrkCuts]) && (track.tpcChi2NCl() < trackCuts[TrkCutChi2PrTpcCls][UseGenTrkCuts]) && (track.tpcNClsFound() >= trackCuts[TrkCutTpcCluster][UseGenTrkCuts]) && (track.tpcNClsCrossedRows() >= trackCuts[TrkCutTpcCrossedRows][UseGenTrkCuts]) && (track.itsNCls() >= trackCuts[TrkCutItsCluster][UseGenTrkCuts])); + } + + template + bool trackSelectedForNch(TTrack const& track) + { + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].empty() && (std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxyForNch->Eval(track.pt()))) { + return false; + } + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].empty()) { + if (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAzForNch->Eval(track.pt())) { + return false; + } + } else { + if (std::fabs(track.dcaZ()) > trackCuts[TrkCutDCAz][UseNchSelCuts]) { + return false; + } + } + return ((track.pt() > trackCuts[TrkCutPtMin][UseNchSelCuts]) && (track.pt() < trackCuts[TrkCutPtMax][UseNchSelCuts]) && (std::abs(track.eta()) < trackCuts[TrkCutEtaCut][UseNchSelCuts]) && (track.tpcChi2NCl() < trackCuts[TrkCutChi2PrTpcCls][UseNchSelCuts]) && (track.tpcNClsFound() >= trackCuts[TrkCutTpcCluster][UseNchSelCuts]) && (track.tpcNClsCrossedRows() >= trackCuts[TrkCutTpcCrossedRows][UseNchSelCuts]) && (track.itsNCls() >= trackCuts[TrkCutItsCluster][UseNchSelCuts])); } template @@ -778,10 +905,12 @@ struct CorrFit { float dPhiStar = phi1 - phi2 - charge1 * fbSign * std::asin(0.075 * radius / pt1) + charge2 * fbSign * std::asin(0.075 * radius / pt2); - if (dPhiStar > constants::math::PI) + if (dPhiStar > constants::math::PI) { dPhiStar = constants::math::TwoPI - dPhiStar; - if (dPhiStar < -constants::math::PI) + } + if (dPhiStar < -constants::math::PI) { dPhiStar = -constants::math::TwoPI - dPhiStar; + } return dPhiStar; } @@ -814,21 +943,23 @@ struct CorrFit { template void getChannel(TFT0s const& ft0, std::size_t const& iCh, int& id, float& ampl, int fitType, int system) { - if (fitType == kFT0C) { + if (fitType == IndexFT0C) { id = ft0.channelC()[iCh]; id = id + Ft0IndexA; ampl = ft0.amplitudeC()[iCh]; - if (system == SameEvent) + if (system == SameEvent) { registry.fill(HIST("FT0Amp"), id, ampl); + } ampl = ampl / cstFT0RelGain[id]; if (system == SameEvent) { registry.fill(HIST("FT0AmpCorrect"), id, ampl); } - } else if (fitType == kFT0A) { + } else if (fitType == IndexFT0A) { id = ft0.channelA()[iCh]; ampl = ft0.amplitudeA()[iCh]; - if (system == SameEvent) + if (system == SameEvent) { registry.fill(HIST("FT0Amp"), id, ampl); + } ampl = ampl / cstFT0RelGain[id]; if (system == SameEvent) { registry.fill(HIST("FT0AmpCorrect"), id, ampl); @@ -848,18 +979,26 @@ struct CorrFit { int pid = -1; // -1 = not identified, 1 = pion, 2 = kaon, 3 = proton std::array nSigmaToUse = cfgPIDConfigs.cfgUseItsPID ? nSigmaITS : nSigmaTPC; // Choose which nSigma to use: TPC or ITS - int kIndexDetector = cfgPIDConfigs.cfgUseItsPID ? kITS : kTPC; // Choose which nSigma to use: TPC or ITS + int useIndexDetector = UseTPC; + useIndexDetector = cfgPIDConfigs.cfgUseItsPID ? UseITS : UseTPC; // Choose which nSigma to use: TPC or ITS bool isPion = false; bool isKaon = false; bool isProton = false; - bool isDetectedPion = nSigmaToUse[iPionUp] < cfgPIDConfigs.nSigmas->getData()[iPionUp][kIndexDetector] && nSigmaToUse[iPionUp] > cfgPIDConfigs.nSigmas->getData()[iPionLow][kIndexDetector]; - bool isDetectedKaon = nSigmaToUse[iKaonUp] < cfgPIDConfigs.nSigmas->getData()[iKaonUp][kIndexDetector] && nSigmaToUse[iKaonUp] > cfgPIDConfigs.nSigmas->getData()[iKaonLow][kIndexDetector]; - bool isDetectedProton = nSigmaToUse[iProtonUp] < cfgPIDConfigs.nSigmas->getData()[iProtonUp][kIndexDetector] && nSigmaToUse[iProtonUp] > cfgPIDConfigs.nSigmas->getData()[iProtonLow][kIndexDetector]; + bool isDetectedPion = false; + bool isDetectedKaon = false; + bool isDetectedProton = false; + bool isTofPion = false; + bool isTofKaon = false; + bool isTofProton = false; + + isDetectedPion = nSigmaToUse[IndexPionUp] < nSigmaVals[IndexPionUp][useIndexDetector] && nSigmaToUse[IndexPionUp] > nSigmaVals[IndexPionLow][useIndexDetector]; + isDetectedKaon = nSigmaToUse[IndexKaonUp] < nSigmaVals[IndexKaonUp][useIndexDetector] && nSigmaToUse[IndexKaonUp] > nSigmaVals[IndexKaonLow][useIndexDetector]; + isDetectedProton = nSigmaToUse[IndexProtonUp] < nSigmaVals[IndexProtonUp][useIndexDetector] && nSigmaToUse[IndexProtonUp] > nSigmaVals[IndexProtonLow][useIndexDetector]; - bool isTofPion = nSigmaTOF[iPionUp] < cfgPIDConfigs.nSigmas->getData()[iPionUp][kTOF] && nSigmaTOF[iPionUp] > cfgPIDConfigs.nSigmas->getData()[iPionLow][kTOF]; - bool isTofKaon = nSigmaTOF[iKaonUp] < cfgPIDConfigs.nSigmas->getData()[iKaonUp][kTOF] && nSigmaTOF[iKaonUp] > cfgPIDConfigs.nSigmas->getData()[iKaonLow][kTOF]; - bool isTofProton = nSigmaTOF[iProtonUp] < cfgPIDConfigs.nSigmas->getData()[iProtonUp][kTOF] && nSigmaTOF[iProtonUp] > cfgPIDConfigs.nSigmas->getData()[iProtonLow][kTOF]; + isTofPion = nSigmaTOF[IndexPionUp] < nSigmaVals[IndexPionUp][UseTof] && nSigmaTOF[IndexPionUp] > nSigmaVals[IndexPionLow][UseTof]; + isTofKaon = nSigmaTOF[IndexKaonUp] < nSigmaVals[IndexKaonUp][UseTof] && nSigmaTOF[IndexKaonUp] > nSigmaVals[IndexKaonLow][UseTof]; + isTofProton = nSigmaTOF[IndexProtonUp] < nSigmaVals[IndexProtonUp][UseTof] && nSigmaTOF[IndexProtonUp] > nSigmaVals[IndexProtonLow][UseTof]; if (track.pt() > cfgPIDConfigs.cfgTofPtCut && !track.hasTOF()) { return -1; @@ -878,21 +1017,25 @@ struct CorrFit { return -1; // more than one particle satisfy the criteria } - if (fillYields) - registry.fill(HIST("hParticleCounts"), kCharged); + if (fillYields) { + registry.fill(HIST("hParticleCounts"), UseCharged); + } if (isPion) { - pid = kPions; - if (fillYields) - registry.fill(HIST("hParticleCounts"), kPions); + pid = UsePions; + if (fillYields) { + registry.fill(HIST("hParticleCounts"), UsePions); + } } else if (isKaon) { - pid = kKaons; - if (fillYields) - registry.fill(HIST("hParticleCounts"), kKaons); + pid = UseKaons; + if (fillYields) { + registry.fill(HIST("hParticleCounts"), UseKaons); + } } else if (isProton) { - pid = kProtons; - if (fillYields) - registry.fill(HIST("hParticleCounts"), kProtons); + pid = UseProtons; + if (fillYields) { + registry.fill(HIST("hParticleCounts"), UseProtons); + } } else { return -1; // no particle satisfies the criteria } @@ -945,15 +1088,14 @@ struct CorrFit { { float effNch = 1.; if (mEfficiencyNch) { - int ptBin = mEfficiencyNch->FindBin(pt); effNch = mEfficiencyNch->GetBinContent(ptBin); - } else { effNch = 1.0; } - if (effNch == 0) + if (effNch == 0) { return false; + } weightNch = 1. / effNch; return true; } @@ -962,17 +1104,16 @@ struct CorrFit { { float eff = 1.; if (mEfficiency) { - int etaBin = mEfficiency->GetXaxis()->FindBin(eta); // use the eta bin corresponding to eta=0 for the trigger particle efficiency int ptBin = mEfficiency->GetYaxis()->FindBin(pt); int vertexBin = mEfficiency->GetZaxis()->FindBin(vertex); // use the vertex bin corresponding to z=0 for the trigger particle efficiency eff = mEfficiency->GetBinContent(etaBin, ptBin, vertexBin); - } else { eff = 1.0; } - if (eff == 0) + if (eff == 0) { return false; + } weight = 1. / eff; return true; } @@ -998,10 +1139,8 @@ struct CorrFit { double nTracksCorrected = 0; float weightNch = 1.0f; for (auto const& track : tracks) { - - if (cfgRefMultiplicity) { - if (track.pt() > cfgRefpTMax) - continue; + if (!trackSelectedForNch(track)) { + continue; } if (!getEfficiencyCorrectionNch(weightNch, track.pt())) { @@ -1025,8 +1164,9 @@ struct CorrFit { if (!trackSelected(track1)) { continue; } - if (cfgPIDConfigs.cfgPIDParticle && getNsigmaPID(track1, true) != cfgPIDConfigs.cfgPIDParticle) + if (cfgPIDConfigs.cfgPIDParticle && getNsigmaPID(track1, true) != cfgPIDConfigs.cfgPIDParticle) { continue; // if PID is selected, check if the track has the right PID + } if (!getEfficiencyCorrection(weff1, track1.eta(), track1.pt(), zvtx)) { continue; @@ -1045,10 +1185,11 @@ struct CorrFit { void fillNsigmaBeforeCut(const TTrack& track1, int pid) // function to fill the QA before Nsigma selection { switch (pid) { - case kPions: // For Pions + case UsePions: // For Pions if (!cfgPIDConfigs.cfgUseItsPID) { - if (cfgPIDConfigs.cfgGetNsigmaQA) + if (cfgPIDConfigs.cfgGetNsigmaQA) { registry.fill(HIST("TofTpcNsigma_before"), track1.tpcNSigmaPi(), track1.tofNSigmaPi(), track1.pt()); + } if (cfgPIDConfigs.cfgGetdEdx) { double tpcExpSignalPi = track1.tpcSignal() - (track1.tpcNSigmaPi() * track1.tpcExpSigmaPi()); @@ -1057,13 +1198,15 @@ struct CorrFit { registry.fill(HIST("ExpSigma_ptwise_beforeCut"), track1.pt(), track1.tpcExpSigmaPi(), track1.tofNSigmaPi()); } } - if (cfgPIDConfigs.cfgGetNsigmaQA && cfgPIDConfigs.cfgUseItsPID) + if (cfgPIDConfigs.cfgGetNsigmaQA && cfgPIDConfigs.cfgUseItsPID) { registry.fill(HIST("TofItsNsigma_before"), itsResponse.nSigmaITS(track1), track1.tofNSigmaPi(), track1.pt()); + } break; - case kKaons: // For Kaons + case UseKaons: // For Kaons if (!cfgPIDConfigs.cfgUseItsPID) { - if (cfgPIDConfigs.cfgGetNsigmaQA) + if (cfgPIDConfigs.cfgGetNsigmaQA) { registry.fill(HIST("TofTpcNsigma_before"), track1.tpcNSigmaKa(), track1.tofNSigmaKa(), track1.pt()); + } if (cfgPIDConfigs.cfgGetdEdx) { double tpcExpSignalKa = track1.tpcSignal() - (track1.tpcNSigmaKa() * track1.tpcExpSigmaKa()); @@ -1072,13 +1215,16 @@ struct CorrFit { registry.fill(HIST("ExpSigma_ptwise_beforeCut"), track1.pt(), track1.tpcExpSigmaKa(), track1.tofNSigmaKa()); } } - if (cfgPIDConfigs.cfgGetNsigmaQA && cfgPIDConfigs.cfgUseItsPID) + if (cfgPIDConfigs.cfgGetNsigmaQA && cfgPIDConfigs.cfgUseItsPID) { registry.fill(HIST("TofItsNsigma_before"), itsResponse.nSigmaITS(track1), track1.tofNSigmaKa(), track1.pt()); + } break; - case kProtons: // For Protons + + case UseProtons: // For Protons if (!cfgPIDConfigs.cfgUseItsPID) { - if (cfgPIDConfigs.cfgGetNsigmaQA) + if (cfgPIDConfigs.cfgGetNsigmaQA) { registry.fill(HIST("TofTpcNsigma_before"), track1.tpcNSigmaPr(), track1.tofNSigmaPr(), track1.pt()); + } if (cfgPIDConfigs.cfgGetdEdx) { double tpcExpSignalPr = track1.tpcSignal() - (track1.tpcNSigmaPr() * track1.tpcExpSigmaPr()); @@ -1087,8 +1233,9 @@ struct CorrFit { registry.fill(HIST("ExpSigma_ptwise_beforeCut"), track1.pt(), track1.tpcExpSigmaPr(), track1.tofNSigmaPr()); } } - if (cfgPIDConfigs.cfgGetNsigmaQA && cfgPIDConfigs.cfgUseItsPID) + if (cfgPIDConfigs.cfgGetNsigmaQA && cfgPIDConfigs.cfgUseItsPID) { registry.fill(HIST("TofItsNsigma_before"), itsResponse.nSigmaITS(track1), track1.tofNSigmaPr(), track1.pt()); + } break; default: LOGF(info, "PID %d not recognized, skipping nSigma QA filling", pid); @@ -1100,7 +1247,7 @@ struct CorrFit { void fillNsigmaAfterCut(const TTrack& track1, int pid) // function to fill the QA after Nsigma selection { switch (pid) { - case kPions: // For Pions + case UsePions: // For Pions if (!cfgPIDConfigs.cfgUseItsPID) { if (cfgPIDConfigs.cfgGetdEdx) { double tpcExpSignalPi = track1.tpcSignal() - (track1.tpcNSigmaPi() * track1.tpcExpSigmaPi()); @@ -1109,13 +1256,15 @@ struct CorrFit { registry.fill(HIST("ExpTpcdEdx_ptwise_afterCut"), track1.pt(), tpcExpSignalPi, track1.tofNSigmaPi()); registry.fill(HIST("ExpSigma_ptwise_afterCut"), track1.pt(), track1.tpcExpSigmaPi(), track1.tofNSigmaPi()); } - if (cfgPIDConfigs.cfgGetNsigmaQA) + if (cfgPIDConfigs.cfgGetNsigmaQA) { registry.fill(HIST("TofTpcNsigma_after"), track1.tpcNSigmaPi(), track1.tofNSigmaPi(), track1.pt()); + } } - if (cfgPIDConfigs.cfgUseItsPID) + if (cfgPIDConfigs.cfgUseItsPID) { registry.fill(HIST("TofItsNsigma_after"), itsResponse.nSigmaITS(track1), track1.tofNSigmaPi(), track1.pt()); + } break; - case kKaons: // For Kaons + case UseKaons: // For Kaons if (!cfgPIDConfigs.cfgUseItsPID) { if (cfgPIDConfigs.cfgGetdEdx) { double tpcExpSignalKa = track1.tpcSignal() - (track1.tpcNSigmaKa() * track1.tpcExpSigmaKa()); @@ -1124,13 +1273,15 @@ struct CorrFit { registry.fill(HIST("ExpTpcdEdx_ptwise_afterCut"), track1.pt(), tpcExpSignalKa, track1.tofNSigmaKa()); registry.fill(HIST("ExpSigma_ptwise_afterCut"), track1.pt(), track1.tpcExpSigmaKa(), track1.tofNSigmaKa()); } - if (cfgPIDConfigs.cfgGetNsigmaQA) + if (cfgPIDConfigs.cfgGetNsigmaQA) { registry.fill(HIST("TofTpcNsigma_after"), track1.tpcNSigmaKa(), track1.tofNSigmaKa(), track1.pt()); + } } - if (cfgPIDConfigs.cfgUseItsPID) + if (cfgPIDConfigs.cfgUseItsPID) { registry.fill(HIST("TofItsNsigma_after"), itsResponse.nSigmaITS(track1), track1.tofNSigmaKa(), track1.pt()); + } break; - case kProtons: // For Protons + case UseProtons: // For Protons if (!cfgPIDConfigs.cfgUseItsPID) { if (cfgPIDConfigs.cfgGetdEdx) { double tpcExpSignalPr = track1.tpcSignal() - (track1.tpcNSigmaPr() * track1.tpcExpSigmaPr()); @@ -1139,14 +1290,16 @@ struct CorrFit { registry.fill(HIST("ExpTpcdEdx_ptwise_afterCut"), track1.pt(), tpcExpSignalPr, track1.tofNSigmaPr()); registry.fill(HIST("ExpSigma_ptwise_afterCut"), track1.pt(), track1.tpcExpSigmaPr(), track1.tofNSigmaPr()); } - if (cfgPIDConfigs.cfgGetNsigmaQA) + if (cfgPIDConfigs.cfgGetNsigmaQA) { registry.fill(HIST("TofTpcNsigma_after"), track1.tpcNSigmaPr(), track1.tofNSigmaPr(), track1.pt()); + } } - if (cfgPIDConfigs.cfgUseItsPID && cfgPIDConfigs.cfgGetNsigmaQA) + if (cfgPIDConfigs.cfgUseItsPID && cfgPIDConfigs.cfgGetNsigmaQA) { registry.fill(HIST("TofItsNsigma_after"), itsResponse.nSigmaITS(track1), track1.tofNSigmaPr(), track1.pt()); + } break; default: - LOGF(info, "PID %d not recognized, skipping nSigma QA filling", pid); + LOGF(info, "PID %d not recognized, skipping nSigma QA filling, breaking", pid); break; } // end of switch } @@ -1154,40 +1307,43 @@ struct CorrFit { template void fillCorrelationsTPCFT0(const TTracks& tracks1, TFT0s const& ft0, float posZ, int system, double multiplicity, int corType, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms { - - int fSampleIndex = static_cast(gRandom->Uniform(0.0, cfgSampleSize)); + auto fSampleIndex = static_cast(fRandom.Uniform(0.0, cfgSampleSize)); float triggerWeight = 1.0f; // loop over all tracks for (auto const& track1 : tracks1) { - if (!trackSelected(track1)) + if (!trackSelected(track1)) { continue; - - if (cfgPIDConfigs.cfgPIDParticle > kCharged) + } + if (cfgPIDConfigs.cfgPIDParticle > UseCharged) { fillNsigmaBeforeCut(track1, cfgPIDConfigs.cfgPIDParticle); + } - if (cfgPIDConfigs.cfgPIDParticle && getNsigmaPID(track1, false) != cfgPIDConfigs.cfgPIDParticle) + if (cfgPIDConfigs.cfgPIDParticle && getNsigmaPID(track1, false) != cfgPIDConfigs.cfgPIDParticle) { continue; // if PID is selected, check if the track has the right PID + } - if (cfgPIDConfigs.cfgPIDParticle > kCharged) + if (cfgPIDConfigs.cfgPIDParticle > UseCharged) { fillNsigmaAfterCut(track1, cfgPIDConfigs.cfgPIDParticle); + } - if (!getEfficiencyCorrection(triggerWeight, track1.pt(), track1.eta(), posZ)) + if (!getEfficiencyCorrection(triggerWeight, track1.pt(), track1.eta(), posZ)) { continue; + } if (system == SameEvent) { - if (corType == kFT0C) { - registry.fill(HIST("Trig_hist_TPC_FT0C"), fSampleIndex, posZ, track1.pt(), eventWeight * triggerWeight); - } else if (corType == kFT0A) { - registry.fill(HIST("Trig_hist_TPC_FT0A"), fSampleIndex, posZ, track1.pt(), eventWeight * triggerWeight); + if (corType == IndexFT0C) { + registry.fill(HIST("Trig_hist_TPC_FT0C"), fSampleIndex, posZ, track1.pt(), multiplicity, eventWeight * triggerWeight); + } else if (corType == IndexFT0A) { + registry.fill(HIST("Trig_hist_TPC_FT0A"), fSampleIndex, posZ, track1.pt(), multiplicity, eventWeight * triggerWeight); } } std::size_t channelSize = 0; - if (corType == kFT0C) { + if (corType == IndexFT0C) { channelSize = ft0.channelC().size(); - } else if (corType == kFT0A) { + } else if (corType == IndexFT0A) { channelSize = ft0.channelA().size(); } else { LOGF(fatal, "Cor Index %d out of range", corType); @@ -1204,13 +1360,13 @@ struct CorrFit { float deltaEta = track1.eta() - eta; // fill the right sparse and histograms if (system == SameEvent) { - if (corType == kFT0A) { + if (corType == IndexFT0A) { if (cfgQaCheck) { registry.fill(HIST("Assoc_amp_same_TPC_FT0A"), chanelid, ampl); registry.fill(HIST("deltaEta_deltaPhi_same_TPC_FT0A"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); } sameTpcFt0a->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), multiplicity, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); - } else if (corType == kFT0C) { + } else if (corType == IndexFT0C) { if (cfgQaCheck) { registry.fill(HIST("Assoc_amp_same_TPC_FT0C"), chanelid, ampl); registry.fill(HIST("deltaEta_deltaPhi_same_TPC_FT0C"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); @@ -1218,13 +1374,13 @@ struct CorrFit { sameTpcFt0c->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), multiplicity, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); } } else if (system == MixedEvent) { - if (corType == kFT0A) { + if (corType == IndexFT0A) { if (cfgQaCheck) { registry.fill(HIST("Assoc_amp_mixed_TPC_FT0A"), chanelid, ampl); registry.fill(HIST("deltaEta_deltaPhi_mixed_TPC_FT0A"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); } mixedTpcFt0a->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), multiplicity, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); - } else if (corType == kFT0C) { + } else if (corType == IndexFT0C) { if (cfgQaCheck) { registry.fill(HIST("Assoc_amp_mixed_TPC_FT0C"), chanelid, ampl); registry.fill(HIST("deltaEta_deltaPhi_mixed_TPC_FT0C"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); @@ -1239,7 +1395,7 @@ struct CorrFit { template void fillCorrelationsFT0AFT0C(TFT0s const& ft0Col1, TFT0s const& ft0Col2, float posZ, int system, double multiplicity, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms { - int fSampleIndex = static_cast(gRandom->Uniform(0.0, cfgSampleSize)); + auto fSampleIndex = static_cast(fRandom.Uniform(0.0, cfgSampleSize)); float triggerWeight = 1.0f; std::size_t channelASize = ft0Col1.channelA().size(); @@ -1249,20 +1405,20 @@ struct CorrFit { int chanelAid = 0; float amplA = 0.; - getChannel(ft0Col1, iChA, chanelAid, amplA, kFT0A, system); - auto phiA = getPhiFT0(chanelAid, kFT0A); - auto etaA = getEtaFT0(chanelAid, kFT0A); + getChannel(ft0Col1, iChA, chanelAid, amplA, IndexFT0A, system); + auto phiA = getPhiFT0(chanelAid, IndexFT0A); + auto etaA = getEtaFT0(chanelAid, IndexFT0A); if (system == SameEvent) { - registry.fill(HIST("Trig_hist_FT0A_FT0C"), fSampleIndex, posZ, 0.5, eventWeight * amplA); + registry.fill(HIST("Trig_hist_FT0A_FT0C"), fSampleIndex, posZ, 0.5, multiplicity, eventWeight * amplA); } for (std::size_t iChC = 0; iChC < channelCSize; iChC++) { int chanelCid = 0; float amplC = 0.; - getChannel(ft0Col2, iChC, chanelCid, amplC, kFT0C, system); - auto phiC = getPhiFT0(chanelCid, kFT0C); - auto etaC = getEtaFT0(chanelCid, kFT0C); + getChannel(ft0Col2, iChC, chanelCid, amplC, IndexFT0C, system); + auto phiC = getPhiFT0(chanelCid, IndexFT0C); + auto etaC = getEtaFT0(chanelCid, IndexFT0C); float deltaPhi = RecoDecay::constrainAngle(phiA - phiC, -PIHalf); float deltaEta = etaA - etaC; @@ -1283,10 +1439,10 @@ struct CorrFit { } template - void fillCorrelations(const TTracks& tracks1, const TTracksAssoc& tracks2, float posZ, int system, double multiplicity, int magneticField) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + void fillCorrelations(const TTracks& tracks1, const TTracksAssoc& tracks2, float posZ, int system, double multiplicity, int magneticField, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms { - int fSampleIndex = static_cast(gRandom->Uniform(0.0, cfgSampleSize)); + auto fSampleIndex = static_cast(fRandom.Uniform(0.0, cfgSampleSize)); float triggerWeight = 1.0f; @@ -1295,41 +1451,49 @@ struct CorrFit { // loop over all tracks for (auto const& track1 : tracks1) { - if (!trackSelected(track1)) + if (!trackSelected(track1)) { continue; + } // Fill Nsigma QA - if (cfgPIDConfigs.cfgPIDParticle > kCharged) + if (cfgPIDConfigs.cfgPIDParticle > UseCharged) { fillNsigmaBeforeCut(track1, cfgPIDConfigs.cfgPIDParticle); + } - if (cfgPIDConfigs.cfgPIDParticle && getNsigmaPID(track1, false) != cfgPIDConfigs.cfgPIDParticle) + if (cfgPIDConfigs.cfgPIDParticle && getNsigmaPID(track1, false) != cfgPIDConfigs.cfgPIDParticle) { continue; // if PID is selected, check if the track has the right PID + } - if (cfgPIDConfigs.cfgPIDParticle > kCharged) + if (cfgPIDConfigs.cfgPIDParticle > UseCharged) { fillNsigmaAfterCut(track1, cfgPIDConfigs.cfgPIDParticle); + } - if (!getEfficiencyCorrectionNch(triggerWeight, track1.pt())) + if (!getEfficiencyCorrectionNch(triggerWeight, track1.pt())) { continue; + } if (system == SameEvent) { - registry.fill(HIST("Trig_hist_TPC"), fSampleIndex, posZ, track1.pt(), triggerWeight); + registry.fill(HIST("Trig_hist_TPC"), fSampleIndex, posZ, track1.pt(), multiplicity, eventWeight * triggerWeight); } for (auto const& track2 : tracks2) { - if (!trackSelected(track2)) + if (!trackSelected(track2)) { continue; + } - if (!getEfficiencyCorrectionNch(associateWeight, track2.pt())) + if (!getEfficiencyCorrectionNch(associateWeight, track2.pt())) { continue; + } - if (cfgRefpTt) { - if (track2.pt() > cfgRefpTMax) { + if (cfgUseRefpTCut) { + if (track2.pt() > cfgRefpTMax || track2.pt() < cfgRefpTMin) { continue; } } - if (track1.pt() <= track2.pt()) + if (track1.pt() <= track2.pt()) { continue; // skip if the trigger pt is less than the associate pt + } float deltaPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi(), -PIHalf); float deltaEta = track1.eta() - track2.eta(); @@ -1355,8 +1519,9 @@ struct CorrFit { break; } } - if (bIsBelow) + if (bIsBelow) { continue; + } } } @@ -1381,8 +1546,8 @@ struct CorrFit { bool isGoodRun(int runNumber) { - for (const auto& ExcludedRun : cfgRunRemoveList.value) { - if (runNumber == ExcludedRun) { + for (const auto& excludedRun : cfgRunRemoveList.value) { + if (runNumber == excludedRun) { return false; } } @@ -1396,25 +1561,30 @@ struct CorrFit { eventSelectedIndividually(collision); } - if (!collision.sel8()) + if (!collision.sel8()) { return; + } - if (!eventRct(collision, true)) + if (!eventRct(collision, true)) { return; + } auto bc = collision.bc_as(); int currentRunNumber = bc.runNumber(); if (!cfgRunRemoveList.value.empty()) { - if (!isGoodRun(currentRunNumber)) // Rejects runs if bad run number + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number return; + } } - if (cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), true)) + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), true)) { return; + } - if (!collision.has_foundFT0()) + if (!collision.has_foundFT0()) { return; + } loadAlignParam(bc.timestamp()); loadGain(bc); @@ -1427,7 +1597,7 @@ struct CorrFit { fillYield(collision, tracks); - double multiplicity = static_cast(tracks.size()); + auto multiplicity = static_cast(tracks.size()); if (cfgQaCheck) { registry.fill(HIST("Nch"), multiplicity); @@ -1441,12 +1611,12 @@ struct CorrFit { registry.fill(HIST("Nch_corrected"), multiplicity); } - if (multiplicity > cfgMaxMultForCorrelations || multiplicity < cfgMinMultForCorrelations) { + if (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations) { return; } const auto& ft0 = collision.foundFT0(); - fillCorrelationsTPCFT0(tracks, ft0, collision.posZ(), SameEvent, multiplicity, kFT0A, eventWeight); + fillCorrelationsTPCFT0(tracks, ft0, collision.posZ(), SameEvent, multiplicity, IndexFT0A, eventWeight); } PROCESS_SWITCH(CorrFit, processSameTpcFt0a, "Process same event for TPC-FT0 correlation", false); @@ -1461,52 +1631,58 @@ struct CorrFit { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto it = pairs.begin(); it != pairs.end(); it++) { auto& [collision1, tracks1, collision2, tracks2] = *it; - if (!collision1.sel8() || !collision2.sel8()) + if (!collision1.sel8() || !collision2.sel8()) { continue; + } - if (!eventRct(collision1, false) || !eventRct(collision2, false)) + if (!eventRct(collision1, false) || !eventRct(collision2, false)) { continue; + } - if (cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), false)) + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), false)) { continue; - if (cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), false)) + } + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), false)) { continue; + } - if (!(collision1.has_foundFT0() && collision2.has_foundFT0())) + if (!(collision1.has_foundFT0() && collision2.has_foundFT0())) { continue; + } registry.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin auto bc = collision1.bc_as(); int currentRunNumber = bc.runNumber(); if (!cfgRunRemoveList.value.empty()) { - if (!isGoodRun(currentRunNumber)) // Rejects runs if bad run number + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number continue; + } } loadAlignParam(bc.timestamp()); loadCorrection(bc.timestamp()); float eventWeight = 1.0f; - double multiplicity = static_cast(tracks.size()); + auto multiplicity = static_cast(tracks1.size()); if (cfgStrictTrackCounter) { trackCounter(tracks1, multiplicity); } - if (multiplicity > cfgMaxMultForCorrelations || multiplicity < cfgMinMultForCorrelations) { + if (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations) { continue; } const auto& ft0 = collision2.foundFT0(); - fillCorrelationsTPCFT0(tracks1, ft0, collision1.posZ(), MixedEvent, multiplicity, kFT0A, eventWeight); + fillCorrelationsTPCFT0(tracks1, ft0, collision1.posZ(), MixedEvent, multiplicity, IndexFT0A, eventWeight); } } PROCESS_SWITCH(CorrFit, processMixedTpcFt0a, "Process mixed events for TPC-FT0A correlation", false); @@ -1518,23 +1694,28 @@ struct CorrFit { eventSelectedIndividually(collision); } - if (!collision.sel8()) + if (!collision.sel8()) { return; + } - if (!eventRct(collision, true)) + if (!eventRct(collision, true)) { return; + } auto bc = collision.bc_as(); int currentRunNumber = bc.runNumber(); if (!cfgRunRemoveList.value.empty()) { - if (!isGoodRun(currentRunNumber)) // Rejects runs if bad run number + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number return; + } } - if (cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), true)) + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), true)) { return; - if (!collision.has_foundFT0()) + } + if (!collision.has_foundFT0()) { return; + } loadAlignParam(bc.timestamp()); loadGain(bc); loadCorrection(bc.timestamp()); @@ -1545,7 +1726,7 @@ struct CorrFit { const auto& ft0 = collision.foundFT0(); - double multiplicity = static_cast(tracks.size()); + auto multiplicity = static_cast(tracks.size()); if (cfgQaCheck) { registry.fill(HIST("Nch"), multiplicity); @@ -1559,11 +1740,11 @@ struct CorrFit { registry.fill(HIST("Nch_corrected"), multiplicity); } - if (multiplicity > cfgMaxMultForCorrelations || multiplicity < cfgMinMultForCorrelations) { + if (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations) { return; } - fillCorrelationsTPCFT0(tracks, ft0, collision.posZ(), SameEvent, multiplicity, kFT0C, 1.0f); + fillCorrelationsTPCFT0(tracks, ft0, collision.posZ(), SameEvent, multiplicity, IndexFT0C, 1.0f); } PROCESS_SWITCH(CorrFit, processSameTpcFt0c, "Process same event for TPC-FT0C correlation", false); @@ -1578,50 +1759,55 @@ struct CorrFit { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto it = pairs.begin(); it != pairs.end(); it++) { auto& [collision1, tracks1, collision2, tracks2] = *it; - if (!collision1.sel8() || !collision2.sel8()) + if (!collision1.sel8() || !collision2.sel8()) { continue; + } - if (!eventRct(collision1, false) || !eventRct(collision2, false)) + if (!eventRct(collision1, false) || !eventRct(collision2, false)) { continue; + } - if (cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), false)) + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), false)) { continue; - - if (cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), false)) + } + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), false)) { continue; + } - if (!(collision1.has_foundFT0() && collision2.has_foundFT0())) + if (!(collision1.has_foundFT0() && collision2.has_foundFT0())) { continue; + } registry.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin auto bc = collision1.bc_as(); int currentRunNumber = bc.runNumber(); if (!cfgRunRemoveList.value.empty()) { - if (!isGoodRun(currentRunNumber)) // Rejects runs if bad run number + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number continue; + } } loadAlignParam(bc.timestamp()); loadCorrection(bc.timestamp()); float eventWeight = 1.0f; const auto& ft0 = collision2.foundFT0(); - double multiplicity = static_cast(tracks.size()); + auto multiplicity = static_cast(tracks1.size()); if (cfgStrictTrackCounter) { trackCounter(tracks1, multiplicity); } - if (multiplicity > cfgMaxMultForCorrelations || multiplicity < cfgMinMultForCorrelations) { + if (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations) { continue; } - fillCorrelationsTPCFT0(tracks1, ft0, collision1.posZ(), MixedEvent, multiplicity, kFT0C, eventWeight); + fillCorrelationsTPCFT0(tracks1, ft0, collision1.posZ(), MixedEvent, multiplicity, IndexFT0C, eventWeight); } } PROCESS_SWITCH(CorrFit, processMixedTpcFt0c, "Process mixed events for TPC-FT0C correlation", false); @@ -1633,24 +1819,29 @@ struct CorrFit { eventSelectedIndividually(collision); } - if (!collision.sel8()) + if (!collision.sel8()) { return; + } - if (!eventRct(collision, true)) + if (!eventRct(collision, true)) { return; + } auto bc = collision.bc_as(); int currentRunNumber = bc.runNumber(); if (!cfgRunRemoveList.value.empty()) { - if (!isGoodRun(currentRunNumber)) // Rejects runs if bad run number + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number return; + } } - if (cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), true)) + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), true)) { return; + } - if (!collision.has_foundFT0()) + if (!collision.has_foundFT0()) { return; + } loadAlignParam(bc.timestamp()); loadGain(bc); @@ -1663,7 +1854,7 @@ struct CorrFit { registry.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin - double multiplicity = static_cast(tracks.size()); + auto multiplicity = static_cast(tracks.size()); if (cfgQaCheck) { registry.fill(HIST("Nch"), multiplicity); @@ -1677,7 +1868,7 @@ struct CorrFit { registry.fill(HIST("Nch_corrected"), multiplicity); } - if (multiplicity > cfgMaxMultForCorrelations || multiplicity < cfgMinMultForCorrelations) { + if (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations) { return; } @@ -1696,7 +1887,7 @@ struct CorrFit { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1704,25 +1895,31 @@ struct CorrFit { auto& [collision1, tracks1, collision2, tracks2] = *it; // should have the same event to TPC-FT0A/C correlations - if (!collision1.sel8() || !collision2.sel8()) + if (!collision1.sel8() || !collision2.sel8()) { continue; + } - if (!eventRct(collision1, false) || !eventRct(collision2, false)) + if (!eventRct(collision1, false) || !eventRct(collision2, false)) { continue; + } - if (cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), false)) + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), false)) { continue; - if (cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), false)) + } + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), false)) { continue; + } - if (!(collision1.has_foundFT0() && collision2.has_foundFT0())) + if (!(collision1.has_foundFT0() && collision2.has_foundFT0())) { continue; + } auto bc = collision1.bc_as(); int currentRunNumber = bc.runNumber(); if (!cfgRunRemoveList.value.empty()) { - if (!isGoodRun(currentRunNumber)) // Rejects runs if bad run number + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number continue; + } } loadAlignParam(bc.timestamp()); loadCorrection(bc.timestamp()); @@ -1731,13 +1928,13 @@ struct CorrFit { const auto& ft0Col1 = collision1.foundFT0(); const auto& ft0Col2 = collision2.foundFT0(); - double multiplicity = static_cast(tracks.size()); + auto multiplicity = static_cast(tracks1.size()); if (cfgStrictTrackCounter) { trackCounter(tracks1, multiplicity); } - if (multiplicity > cfgMaxMultForCorrelations || multiplicity < cfgMinMultForCorrelations) { + if (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations) { continue; } @@ -1754,28 +1951,34 @@ struct CorrFit { if (cfgQaCheck) { eventSelectedIndividually(collision); } - if (!eventRct(collision, true)) + if (!eventRct(collision, true)) { return; + } - if (!collision.sel8()) + if (!collision.sel8()) { return; + } auto bc = collision.bc_as(); int currentRunNumber = bc.runNumber(); if (!cfgRunRemoveList.value.empty()) { - if (!isGoodRun(currentRunNumber)) // Rejects runs if bad run number + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number return; + } } - if (cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), true)) + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), true)) { return; + } + + registry.fill(HIST("zVtx"), collision.posZ()); registry.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin loadCorrection(bc.timestamp()); fillYield(collision, tracks); - double multiplicity = static_cast(tracks.size()); + auto multiplicity = static_cast(tracks.size()); if (cfgQaCheck) { registry.fill(HIST("Nch"), multiplicity); @@ -1789,11 +1992,11 @@ struct CorrFit { registry.fill(HIST("Nch_corrected"), multiplicity); } - if (multiplicity > cfgMaxMultForCorrelations || multiplicity < cfgMinMultForCorrelations) { + if (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations) { return; } - fillCorrelations(tracks, tracks, collision.posZ(), SameEvent, multiplicity, getMagneticField(bc.timestamp())); + fillCorrelations(tracks, tracks, collision.posZ(), SameEvent, multiplicity, getMagneticField(bc.timestamp()), 1.0f); } PROCESS_SWITCH(CorrFit, processSameTPC, "Process same event for TPC-TPC correlation", false); @@ -1808,46 +2011,51 @@ struct CorrFit { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto it = pairs.begin(); it != pairs.end(); it++) { auto& [collision1, tracks1, collision2, tracks2] = *it; - if (!eventRct(collision1, false) || !eventRct(collision2, false)) + if (!eventRct(collision1, false) || !eventRct(collision2, false)) { continue; + } - if (!collision1.sel8() || !collision2.sel8()) + if (!collision1.sel8() || !collision2.sel8()) { continue; + } - if (cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), false)) + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), false)) { continue; - if (cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), false)) + } + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), false)) { continue; + } registry.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin auto bc = collision1.bc_as(); int currentRunNumber = bc.runNumber(); if (!cfgRunRemoveList.value.empty()) { - if (!isGoodRun(currentRunNumber)) // Rejects runs if bad run number + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number continue; + } } loadCorrection(bc.timestamp()); - double multiplicity = static_cast(tracks.size()); + auto multiplicity = static_cast(tracks1.size()); if (cfgStrictTrackCounter) { trackCounter(tracks1, multiplicity); } - if (multiplicity > cfgMaxMultForCorrelations || multiplicity < cfgMinMultForCorrelations) { + if (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations) { continue; } - fillCorrelations(tracks1, tracks2, collision1.posZ(), MixedEvent, multiplicity, getMagneticField(collision1.bc_as().timestamp())); + fillCorrelations(tracks1, tracks2, collision1.posZ(), MixedEvent, multiplicity, getMagneticField(collision1.bc_as().timestamp()), 1.0f); } } PROCESS_SWITCH(CorrFit, processMixedTPC, "Process mixed events for TPC-TPC correlation", false); diff --git a/PWGCF/TwoParticleCorrelations/Tasks/corrReso.cxx b/PWGCF/TwoParticleCorrelations/Tasks/corrReso.cxx index 338aa2406bf..f3185668771 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/corrReso.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/corrReso.cxx @@ -13,7 +13,7 @@ /// \brief Ultra long range correlation using forward FIT detectors and TPC, with focus on resonances /// \author Thor Jensen (thor.kjaersgaard.jensen@cern.ch) -#include "PWGCF/Core/CorrelationContainer.h" +#include "PWGCF/TwoParticleCorrelations/Core/DihadronContainer.h" #include "PWGLF/DataModel/LFStrangenessTables.h" #include "Common/CCDB/EventSelectionParams.h" @@ -57,6 +57,7 @@ #include #include #include +#include #include #include @@ -87,121 +88,131 @@ struct CorrReso { o2::aod::ITSResponse itsResponse; Service ccdb{}; - O2_DEFINE_CONFIGURABLE(cfgUseAdditionalEventCut, bool, true, "Use additional event cut on mult correlations") - O2_DEFINE_CONFIGURABLE(cfgZVtxCut, float, 10.0f, "Accepted z-vertex range") - O2_DEFINE_CONFIGURABLE(cfgQaCheck, bool, true, "Enable QA histograms for event selection") - O2_DEFINE_CONFIGURABLE(cfgStrictTrackCounter, bool, false, "Strict track counter for multiplicity correlation cut, counts only tracks that pass all cuts and are used in the correlation") - O2_DEFINE_CONFIGURABLE(cfgMinMultForCorrelations, int, 0, "minimum multiplicity for correlations") - O2_DEFINE_CONFIGURABLE(cfgMaxMultForCorrelations, int, 20, "maximum multiplicity for correlations") - Configurable> cfgRunRemoveList{"cfgRunRemoveList", std::vector{-1}, "excluded run numbers"}; - O2_DEFINE_CONFIGURABLE(cfgEvSelRCTflags, std::string, "", "keep empty to disable, usage: 'CentralBarrelTracking (CBT)', 'CBT_hadronPID' ") - - struct : ConfigurableGroup{ - O2_DEFINE_CONFIGURABLE(cfgPtCutMin, float, 0.2f, "minimum accepted track pT") - O2_DEFINE_CONFIGURABLE(cfgPtCutMax, float, 10.0f, "maximum accepted track pT") - O2_DEFINE_CONFIGURABLE(cfgEtaCut, float, 0.8f, "Eta cut") - O2_DEFINE_CONFIGURABLE(cfgCutChi2prTPCcls, float, 2.5f, "max chi2 per TPC clusters") - O2_DEFINE_CONFIGURABLE(cfgCutTPCclu, float, 50.0f, "minimum TPC clusters") - O2_DEFINE_CONFIGURABLE(cfgCutTPCCrossedRows, float, 70.0f, "minimum TPC crossed rows") - O2_DEFINE_CONFIGURABLE(cfgCutITSclu, float, 5.0f, "minimum ITS clusters") - O2_DEFINE_CONFIGURABLE(cfgCutDCAz, float, 2.0f, "max DCA to vertex z")} cfgTrackCuts; + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgQABasic, bool, true, "Enable QA histograms for event and track selection") + O2_DEFINE_CONFIGURABLE(cfgStrictTrackCounter, bool, false, "Strict track counter for multiplicity correlation cut, counts only tracks that pass all cuts and are used in the correlation") + O2_DEFINE_CONFIGURABLE(cfgSampleSize, int, 10, "Sample size for mixed event") + O2_DEFINE_CONFIGURABLE(cfgAnalyzeTPCFT0A, bool, true, "Switch for doing TPC-FT0A correlations") + O2_DEFINE_CONFIGURABLE(cfgAnalyzeTPCFT0C, bool, true, "Switch for doing TPC-FT0C correlations") + O2_DEFINE_CONFIGURABLE(cfgMinMixEventNum, int, 5, "Minimum number of events to mix") + Configurable> cfgRunRemoveList{"cfgRunRemoveList", std::vector{-1}, "excluded run numbers"}; + Configurable cfgGainEqPath{"cfgGainEqPath", "Analysis/EventPlane/GainEq", "CCDB path for gain equalization constants"}; + Configurable cfgCorrLevel{"cfgCorrLevel", 0, "calibration step: 0 = no corr, 1 = gain corr"}; + } cfgGeneral; struct : ConfigurableGroup{ - O2_DEFINE_CONFIGURABLE(cfgCutOccupancyHigh, int, 2000, "High cut on TPC occupancy") - O2_DEFINE_CONFIGURABLE(cfgCutOccupancyLow, int, 0, "Low cut on TPC occupancy")} cfgEventSelection; + O2_DEFINE_CONFIGURABLE(cfgEfficiency, std::string, "", "CCDB path to efficiency object") + O2_DEFINE_CONFIGURABLE(cfgEfficiencyNch, std::string, "", "CCDB path to multiplicity dependent efficiency object") + O2_DEFINE_CONFIGURABLE(cfgUseEventWeights, bool, false, "Use event weights for mixed event") + O2_DEFINE_CONFIGURABLE(cfgCentralityWeight, std::string, "", "CCDB path to centrality weight object") + O2_DEFINE_CONFIGURABLE(cfgLocalEfficiency, bool, false, "Use local efficiency object") + O2_DEFINE_CONFIGURABLE(cfgLocalEfficiencyNch, bool, false, "Use local multiplicity dependent efficiency object")} cfgEffWeights; - Configurable> cfgUseEventCuts{"cfgUseEventCuts", {LongArrayInt.front().data(), 14, 1, {"Filtered Events", "Sel8", "kNoTimeFrameBorder", "kNoITSROFrameBorder", "kNoSameBunchPileup", "kIsGoodZvtxFT0vsPV", "kNoCollInTimeRangeStandard", "kIsGoodITSLayersAll", "kIsGoodITSLayer0123", "kNoCollInRofStandard", "kNoHighMultCollInPrevRof", "Occupancy", "Multcorrelation", "T0AV0ACut"}, {"EvCuts"}}, "Labeled array (int) for various cuts on resonances"}; + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgGlobalPtMin, float, 0.2f, "minimum accepted track pT") + O2_DEFINE_CONFIGURABLE(cfgGlobalPtMax, float, 10.0f, "maximum accepted track pT") + O2_DEFINE_CONFIGURABLE(cfgGlobalEta, float, 0.8f, "Eta cut") + O2_DEFINE_CONFIGURABLE(cfgGlobalChi2prTPCcls, float, 2.5f, "Maximum chi2 per TPC clusters") + O2_DEFINE_CONFIGURABLE(cfgGlobalDCAz, float, 2.0f, "Maximum DCA to vertex z") + Configurable> cfgTrackCutsDCAxy{"cfgTrackCutsDCAxy", std::vector{"[0]*(0.0026+0.005/(x^1.01))", "[0]*(0.0026+0.005/(x^1.01))"}, "Functional form of pt-dependent DCAxy cut"}; + Configurable> cfgTrackCutsDCAz{"cfgTrackCutsDCAz", std::vector{"", ""}, "Functional form of pt-dependent DCAz cut"}; + Configurable> cfgTrackCuts{"cfgTrackCuts", {LongArrayFloat.front().data(), 10, 2, {"PtMin", "PtMax", "EtaCut", "Chi2PrTpcCls", "TpcCluster", "TpcCrossedRows", "ItsCluster", "DCAz", "DCAzNsigma", "DCAxyNsigma"}, {"General", "SelNch"}}, "Labeled array for track selections"}; + } cfgTrkSel; - O2_DEFINE_CONFIGURABLE(cfgMinMixEventNum, int, 5, "Minimum number of events to mix") - O2_DEFINE_CONFIGURABLE(cfgSampleSize, double, 10, "Sample size for mixed event") - O2_DEFINE_CONFIGURABLE(cfgEfficiency, std::string, "", "CCDB path to efficiency object") - O2_DEFINE_CONFIGURABLE(cfgEfficiencyNch, std::string, "", "CCDB path to multiplicity dependent efficiency object") - O2_DEFINE_CONFIGURABLE(cfgCentralityWeight, std::string, "", "CCDB path to centrality weight object") - O2_DEFINE_CONFIGURABLE(cfgLocalEfficiency, bool, false, "Use local efficiency object") - O2_DEFINE_CONFIGURABLE(cfgLocalEfficiencyNch, bool, false, "Use local multiplicity dependent efficiency object"); - O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FT0A") + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgGlobalCutVertex, float, 10.0f, "Accepted z-vertex range") + O2_DEFINE_CONFIGURABLE(cfgCutOccupancyHigh, int, 2000, "High cut on TPC occupancy") + O2_DEFINE_CONFIGURABLE(cfgCutOccupancyLow, int, 0, "Low cut on TPC occupancy") + O2_DEFINE_CONFIGURABLE(cfgUseAdditionalEventCut, bool, true, "Use additional event cut on mult correlations") + O2_DEFINE_CONFIGURABLE(cfgMinMultForCorrelations, int, 0, "minimum multiplicity for correlations") + O2_DEFINE_CONFIGURABLE(cfgMaxMultForCorrelations, int, 20, "maximum multiplicity for correlations") + O2_DEFINE_CONFIGURABLE(cfgSelCollByNch, bool, true, "Select collisions by Nch or centrality") + O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FV0A") + O2_DEFINE_CONFIGURABLE(cfgMinCentForCorrelations, int, 0, "minimum centrality for correlations") + O2_DEFINE_CONFIGURABLE(cfgMaxCentForCorrelations, int, 20, "maximum centrality for correlations") + O2_DEFINE_CONFIGURABLE(cfgUseCol2ForMixedEvSel, bool, true, "Use Mult/Cent cut on collision 2 during mixed event") + O2_DEFINE_CONFIGURABLE(cfgEvSelRCTflags, std::string, "", "keep empty to disable, usage: 'CentralBarrelTracking (CBT)', 'CBT_hadronPID' ") + Configurable> cfgUseEventCuts{"cfgUseEventCuts", {LongArrayInt.front().data(), 14, 1, {"Filtered Events", "Sel8", "kNoTimeFrameBorder", "kNoITSROFrameBorder", "kNoSameBunchPileup", "kIsGoodZvtxFT0vsPV", "kNoCollInTimeRangeStandard", "kIsGoodITSLayersAll", "kIsGoodITSLayer0123", "kNoCollInRofStandard", "kNoHighMultCollInPrevRof", "Occupancy", "Multcorrelation", "T0AV0ACut"}, {"EvCuts"}}, "Labeled array (int) for various cuts on resonances"}; + } cfgEvSel; struct : ConfigurableGroup { - O2_DEFINE_CONFIGURABLE(cfgMultCentHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 10.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); - O2_DEFINE_CONFIGURABLE(cfgMultCentLowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultCentHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 10.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut") + O2_DEFINE_CONFIGURABLE(cfgMultCentLowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut") O2_DEFINE_CONFIGURABLE(cfgMultT0CCutEnabled, bool, false, "Enable Global multiplicity vs T0C centrality cut") Configurable> cfgMultT0CCutPars{"cfgMultT0CCutPars", std::vector{143.04, -4.58368, 0.0766055, -0.000727796, 2.86153e-06, 23.3108, -0.36304, 0.00437706, -4.717e-05, 1.98332e-07}, "Global multiplicity vs T0C centrality cut parameter values"}; O2_DEFINE_CONFIGURABLE(cfgMultPVT0CCutEnabled, bool, false, "Enable PV multiplicity vs T0C centrality cut") Configurable> cfgMultPVT0CCutPars{"cfgMultPVT0CCutPars", std::vector{195.357, -6.15194, 0.101313, -0.000955828, 3.74793e-06, 30.0326, -0.43322, 0.00476265, -5.11206e-05, 2.13613e-07}, "PV multiplicity vs T0C centrality cut parameter values"}; - O2_DEFINE_CONFIGURABLE(cfgMultMultPVHighCutFunction, std::string, "[0]+[1]*x + 5.*([2]+[3]*x)", "Functional for multiplicity correlation cut"); - O2_DEFINE_CONFIGURABLE(cfgMultMultPVLowCutFunction, std::string, "[0]+[1]*x - 5.*([2]+[3]*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultMultPVHighCutFunction, std::string, "[0]+[1]*x + 5.*([2]+[3]*x)", "Functional for multiplicity correlation cut") + O2_DEFINE_CONFIGURABLE(cfgMultMultPVLowCutFunction, std::string, "[0]+[1]*x - 5.*([2]+[3]*x)", "Functional for multiplicity correlation cut") O2_DEFINE_CONFIGURABLE(cfgMultGlobalPVCutEnabled, bool, false, "Enable global multiplicity vs PV multiplicity cut") Configurable> cfgMultGlobalPVCutPars{"cfgMultGlobalPVCutPars", std::vector{-0.140809, 0.734344, 2.77495, 0.0165935}, "PV multiplicity vs T0C centrality cut parameter values"}; - O2_DEFINE_CONFIGURABLE(cfgMultMultV0AHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 4.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); - O2_DEFINE_CONFIGURABLE(cfgMultMultV0ALowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultMultV0AHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 4.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut") + O2_DEFINE_CONFIGURABLE(cfgMultMultV0ALowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut") O2_DEFINE_CONFIGURABLE(cfgMultMultV0ACutEnabled, bool, false, "Enable global multiplicity vs V0A multiplicity cut") Configurable> cfgMultMultV0ACutPars{"cfgMultMultV0ACutPars", std::vector{534.893, 184.344, 0.423539, -0.00331436, 5.34622e-06, 871.239, 53.3735, -0.203528, 0.000122758, 5.41027e-07}, "Global multiplicity vs V0A multiplicity cut parameter values"}; std::vector multT0CCutPars; std::vector multPVT0CCutPars; std::vector multGlobalPVCutPars; std::vector multMultV0ACutPars; - TF1* fMultPVT0CCutLow = nullptr; - TF1* fMultPVT0CCutHigh = nullptr; - TF1* fMultT0CCutLow = nullptr; - TF1* fMultT0CCutHigh = nullptr; - TF1* fMultGlobalPVCutLow = nullptr; - TF1* fMultGlobalPVCutHigh = nullptr; - TF1* fMultMultV0ACutLow = nullptr; - TF1* fMultMultV0ACutHigh = nullptr; - TF1* fT0AV0AMean = nullptr; - TF1* fT0AV0ASigma = nullptr; + std::unique_ptr fMultPVT0CCutLow = nullptr; + std::unique_ptr fMultPVT0CCutHigh = nullptr; + std::unique_ptr fMultT0CCutLow = nullptr; + std::unique_ptr fMultT0CCutHigh = nullptr; + std::unique_ptr fMultGlobalPVCutLow = nullptr; + std::unique_ptr fMultGlobalPVCutHigh = nullptr; + std::unique_ptr fMultMultV0ACutLow = nullptr; + std::unique_ptr fMultMultV0ACutHigh = nullptr; + std::unique_ptr fT0AV0AMean = nullptr; + std::unique_ptr fT0AV0ASigma = nullptr; + std::unique_ptr fPtDepDCAxy = nullptr; + std::unique_ptr fPtDepDCAxyForNch = nullptr; + std::unique_ptr fPtDepDCAz = nullptr; + std::unique_ptr fPtDepDCAzForNch = nullptr; O2_DEFINE_CONFIGURABLE(cfgV0AT0Acut, int, 5, "V0AT0A cut") } cfgFuncParas; struct : ConfigurableGroup { - O2_DEFINE_CONFIGURABLE(cfgUseOnlyTPC, bool, true, "Use only TPC PID for daughter selection") - O2_DEFINE_CONFIGURABLE(cfgPIDUseRejection, bool, true, "True: use exclusion exclusion criteria for PID determination, false: don't use exclusion") - O2_DEFINE_CONFIGURABLE(cfgTpcCut, float, 3.0f, "TPC N-sigma cut for pions, kaons, protons") - O2_DEFINE_CONFIGURABLE(cfgPIDParticle, int, 0, "4 = kshort, 5 = lambda, 6 = phi, 0 for no PID") - O2_DEFINE_CONFIGURABLE(cfgUseItsPID, bool, true, "Use ITS PID for particle identification") O2_DEFINE_CONFIGURABLE(cfgTofPtCut, float, 0.4f, "Minimum pt to use TOF N-sigma") + O2_DEFINE_CONFIGURABLE(cfgUseItsPID, bool, false, "Use ITS PID for particle identification") + O2_DEFINE_CONFIGURABLE(cfgPIDUseRejection, bool, true, "True: use exclusion exclusion criteria for PID determination, false: don't use exclusion") Configurable> nSigmas{"nSigmas", {LongArrayFloat.front().data(), 6, 3, {"UpCut_pi", "UpCut_ka", "UpCut_pr", "LowCut_pi", "LowCut_ka", "LowCut_pr"}, {"TPC", "TOF", "ITS"}}, "Labeled array for n-sigma values for TPC, TOF, ITS for pions, kaons, protons (positive and negative)"}; - Configurable> cfgResoCuts{"cfgResoCuts", {LongArrayFloat.front().data(), 13, 3, {"cos_PAs", "massMin", "massMax", "PosTrackPt", "NegTrackPt", "DCAPosToPVMin", "DCANegToPVMin", "Lifetime", "RadiusMin", "RadiusMax", "Rapidity", "ArmPodMinVal", "DCABetDaug"}, {"K0", "Lambda", "Phi"}}, "Labeled array (float) for various cuts on resonances"}; - Configurable> cfgResoSwitches{"cfgResoSwitches", {LongArrayInt.front().data(), 6, 3, {"UseCosPA", "NMassBins", "UseDCABetDaug", "UseProperLifetime", "UseV0Radius", "UseArmPodCut"}, {"K0", "Lambda", "Phi"}}, "Labeled array (int) for various cuts on resonances"}; + O2_DEFINE_CONFIGURABLE(cfgUseOnlyTPC, bool, true, "Use only TPC PID for daughter selection") + O2_DEFINE_CONFIGURABLE(cfgTpcCut, float, 3.0f, "TPC N-sigma cut for pions, kaons, protons") + O2_DEFINE_CONFIGURABLE(cfgPIDParticle, int, 0, "4 = kshort, 5 = lambda, 0 for no PID") + Configurable> cfgResoCuts{"cfgResoCuts", {LongArrayFloat.front().data(), 15, 2, {"cos_PAs", "massMin", "massMax", "PosTrackPt", "NegTrackPt", "DCAPosToPVMin", "DCANegToPVMin", "Lifetime", "RadiusMin", "RadiusMax", "RapidityMin", "RapidityMax", "ArmPodMinVal", "DCABetDaug", "CompMassRej"}, {"K0", "Lambda"}}, "Labeled array (float) for various cuts on resonances"}; + Configurable> cfgResoSwitches{"cfgResoSwitches", {LongArrayInt.front().data(), 7, 2, {"UseCosPA", "NMassBins", "UseDCABetDaug", "UseProperLifetime", "UseV0Radius", "UseArmPodCut", "UseCompMassRej"}, {"K0", "Lambda"}}, "Labeled array (int) for various cuts on resonances"}; } cfgPIDConfigs; - Configurable cfgCutFV0{"cfgCutFV0", 50., "FV0A threshold"}; - Configurable cfgCutFT0A{"cfgCutFT0A", 150., "FT0A threshold"}; - Configurable cfgCutFT0C{"cfgCutFT0C", 50., "FT0C threshold"}; - Configurable cfgCutZDC{"cfgCutZDC", 10., "ZDC threshold"}; - SliceCache cache; + // Axes for general QA ConfigurableAxis axisVertex{"axisVertex", {10, -10, 10}, "vertex axis for histograms"}; - ConfigurableAxis axisMult{"axisMult", {10, 0, 100}, "multiplicity axis for histograms"}; + ConfigurableAxis axisMult{"axisMult", {10, 0, 100}, "multiplicity axis for QA histograms"}; + ConfigurableAxis axisCent{"axisCent", {100, 0, 100}, "centrality axis for QA histograms"}; ConfigurableAxis axisEta{"axisEta", {40, -1., 1.}, "eta axis for histograms"}; ConfigurableAxis axisPhi{"axisPhi", {72, 0.0, constants::math::TwoPI}, "phi axis for histograms"}; ConfigurableAxis axisPtFiner{"axisPtFiner", {98, 0.2, 10.0}, "pt axis for histograms"}; + + // Axes for Correlations + ConfigurableAxis axisMultForCorr{"axisMultForCorr", {VARIABLE_WIDTH, 0, 5, 10, 20, 50, 100, 150, 200, 300, 500}, "multiplicity axis for correlation histograms"}; + ConfigurableAxis axisCentForCorr{"axisCentForCorr", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, "centrality axis for correlation histograms"}; ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {72, -PIHalf, PIHalf * 3}, "delta phi axis for histograms"}; ConfigurableAxis axisDeltaEtaTpcFt0a{"axisDeltaEtaTpcFt0a", {32, -5.8, -2.6}, "delta eta axis, -5.8~-2.6 for TPC-FT0A,"}; ConfigurableAxis axisDeltaEtaTpcFt0c{"axisDeltaEtaTpcFt0c", {32, 1.2, 4.2}, "delta eta axis, 1.2~4.2 for TPC-FT0C"}; - ConfigurableAxis axisPtTrigger{"axisPtTrigger", {VARIABLE_WIDTH, 0.2, 0.5, 1, 1.5, 2, 3, 4, 6, 10}, "pt trigger axis for histograms"}; ConfigurableAxis axisVtxMix{"axisVtxMix", {VARIABLE_WIDTH, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, "vertex axis for mixed event histograms"}; ConfigurableAxis axisMultMix{"axisMultMix", {VARIABLE_WIDTH, 0, 10, 20, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 260}, "multiplicity / centrality axis for mixed event histograms"}; - ConfigurableAxis axisSample{"axisSample", {cfgSampleSize, 0, cfgSampleSize}, "sample axis for histograms"}; - - ConfigurableAxis axisNsigmaTPC{"axisNsigmaTPC", {80, -5, 5}, "nsigmaTPC axis"}; - ConfigurableAxis axisNsigmaTOF{"axisNsigmaTOF", {80, -5, 5}, "nsigmaTOF axis"}; + ConfigurableAxis axisPtTrigger{"axisPtTrigger", {VARIABLE_WIDTH, 0.2, 0.5, 1, 1.5, 2, 3, 4, 6, 10}, "pt trigger axis for histograms"}; - ConfigurableAxis axisVertexEfficiency{"axisVertexEfficiency", {10, -10, 10}, "vertex axis for efficiency histograms"}; - ConfigurableAxis axisEtaEfficiency{"axisEtaEfficiency", {20, -1.0, 1.0}, "eta axis for efficiency histograms"}; - ConfigurableAxis axisPtEfficiency{"axisPtEfficiency", {VARIABLE_WIDTH, 0.2, 0.5, 1, 1.5, 2, 3, 4, 6, 10}, "pt axis for efficiency histograms"}; - ConfigurableAxis axisAmplitudeFt0a{"axisAmplitudeFt0a", {5000, 0, 1000}, "FT0A amplitude"}; + // Axes for long-range correlations QA + ConfigurableAxis axisAmplitudeFt0{"axisAmplitudeFt0", {5000, 0, 1000}, "FT0A amplitude"}; ConfigurableAxis axisChannelFt0aAxis{"axisChannelFt0aAxis", {96, 0.0, 96.0}, "FT0A channel"}; + ConfigurableAxis axisChannelFt0cAxis{"axisChannelFt0cAxis", {96, 0.0, 96.0}, "FT0C channel"}; - Configurable cfgGainEqPath{"cfgGainEqPath", "Analysis/EventPlane/GainEq", "CCDB path for gain equalization constants"}; - Configurable cfgCorrLevel{"cfgCorrLevel", 0, "calibration step: 0 = no corr, 1 = gain corr"}; - ConfigurableAxis cfgaxisFITamp{"cfgaxisFITamp", {1000, 0, 5000}, ""}; - AxisSpec axisFit{cfgaxisFITamp, "fit amplitude"}; - AxisSpec axisChID = {220, 0, 220}; - - Filter collisionFilter = (nabs(aod::collision::posZ) < cfgZVtxCut); - Filter trackFilter = (nabs(aod::track::eta) < cfgTrackCuts.cfgEtaCut) && (cfgTrackCuts.cfgPtCutMin < aod::track::pt) && (cfgTrackCuts.cfgPtCutMax > aod::track::pt) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)); + // Axes for PID QA + ConfigurableAxis axisNsigmaTPC{"axisNsigmaTPC", {80, -5, 5}, "nsigmaTPC axis"}; + ConfigurableAxis axisNsigmaTOF{"axisNsigmaTOF", {80, -5, 5}, "nsigmaTOF axis"}; + Filter collisionFilter = (nabs(aod::collision::posZ) < cfgEvSel.cfgGlobalCutVertex); + Filter trackFilter = (nabs(aod::track::eta) < cfgTrkSel.cfgGlobalEta) && (aod::track::pt > cfgTrkSel.cfgGlobalPtMin) && (aod::track::pt < cfgTrkSel.cfgGlobalPtMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (nabs(aod::track::dcaZ) < cfgTrkSel.cfgGlobalDCAz) && (aod::track::tpcChi2NCl < cfgTrkSel.cfgGlobalChi2prTPCcls); using FilteredCollisions = soa::Filtered>; using FilteredTracks = soa::Filtered>; @@ -220,21 +231,22 @@ struct CorrReso { bool correctionsLoaded = false; // Define the outputs - OutputObj sameTpcFt0a{"sameEvent_TPC_FT0A"}; - OutputObj mixedTpcFt0a{"mixedEvent_TPC_FT0A"}; - OutputObj sameTpcFt0c{"sameEvent_TPC_FT0C"}; - OutputObj mixedTpcFt0c{"mixedEvent_TPC_FT0C"}; + OutputObj sameTpcFt0a{"sameEvent_TPC_FT0A"}; + OutputObj mixedTpcFt0a{"mixedEvent_TPC_FT0A"}; + OutputObj sameTpcFt0c{"sameEvent_TPC_FT0C"}; + OutputObj mixedTpcFt0c{"mixedEvent_TPC_FT0C"}; - HistogramRegistry registry{"registry"}; + HistogramRegistry histos{"histos"}; // For Labelled array value containers - std::array, 13> resoCutVals{}; - std::array, 6> resoSwitchVals{}; + std::array, 10> trackCuts{}; std::array, 14> eventCuts{}; std::array, 6> nSigmaVals{}; + std::array, 15> resoCutVals{}; + std::array, 7> resoSwitchVals{}; // define global variables - TRandom3* gRandom = new TRandom3(); + TRandom3 fRandom{0}; enum EventType { SameEvent = 1, @@ -242,101 +254,142 @@ struct CorrReso { }; enum FITIndex { - kFT0A = 0, - kFT0C = 1 + IndexFT0A = 0, + IndexFT0C = 1 }; enum PIDIndex { - kCharged = 0, - kPions, - kKaons, - kProtons, - kK0, - kLambda, - kPhi + UseCharged = 0, + UsePions, + UseKaons, + UseProtons, + UseK0, + UseLambda, }; enum PiKpArrayIndex { - iPionUp = 0, - iKaonUp, - iProtonUp, - iPionLow, - iKaonLow, - iProtonLow - }; - enum ResoArrayIndex { - iK0 = 0, - iLambda = 1, - iPhi = 2, - NResoParticles = 3 - }; - enum ResoParticleCuts { - kCosPA = 0, - kMassMin, - kMassMax, - kPosTrackPt, - kNegTrackPt, - kDCAPosToPVMin, - kDCANegToPVMin, - kLifeTime, - kRadiusMin, - kRadiusMax, - kRapidity, - kArmPodMinVal, - kDCABetDaug, - kNParticleCuts - }; - enum ResoParticleSwitches { - kUseCosPA = 0, - kMassBins, - kUseDCABetDaug, - kUseProperLifetime, - kUseV0Radius, - kUseArmPodCut, - kNParticleSwitches + IndexPionUp = 0, + IndexKaonUp, + IndexProtonUp, + IndexPionLow, + IndexKaonLow, + IndexProtonLow }; enum DetectorType { - kTPC = 0, - kTOF, - kITS + UseTPC = 0, + UseTOF, + UseITS }; enum EventCutTypes { - kFilteredEvents = 0, - kAfterSel8, - kUseNoTimeFrameBorder, - kUseNoITSROFrameBorder, - kUseNoSameBunchPileup, - kUseGoodZvtxFT0vsPV, - kUseNoCollInTimeRangeStandard, - kUseGoodITSLayersAll, - kUseGoodITSLayer0123, - kUseNoCollInRofStandard, - kUseNoHighMultCollInPrevRof, - kUseOccupancy, - kUseMultCorrCut, - kUseT0AV0ACut, - kNEventCuts + FilteredEvents = 0, + AfterSel8, + UseNoTimeFrameBorder, + UseNoITSROFrameBorder, + UseNoSameBunchPileup, + UseGoodZvtxFT0vsPV, + UseNoCollInTimeRangeStandard, + UseGoodITSLayersAll, + UseGoodITSLayer0123, + UseNoCollInRofStandard, + UseNoHighMultCollInPrevRof, + UseOccupancy, + UseMultCorrCut, + UseT0AV0ACut, + HaveFT0Cut, + NumEventCuts }; enum EventCutType { - kEvCut1 = 0, - kNEvCutTypes = 1 + EvCut1 = 0, + NumEvCutTypes = 1 + }; + + enum TrackCuts { + TrkCutPtMin = 0, + TrkCutPtMax, + TrkCutEtaCut, + TrkCutChi2PrTpcCls, + TrkCutTpcCluster, + TrkCutTpcCrossedRows, + TrkCutItsCluster, + TrkCutDCAz, + TrkCutDCAzNsigma, + TrkCutDCAxyNsigma + }; + + enum TrackCutGroup { + UseGenTrkCuts = 0, + UseNchSelCuts, + NumTrackCutTypes + }; + enum ResoArrayIndex { + IndexK0 = 0, + IndexLambda = 1, + IndexPhi = 2, + NumResoParticles = 3 + }; + enum ResoParticleCuts { + CutCosPA = 0, + CutMassMin, + CutMassMax, + CutPosTrackPt, + CutNegTrackPt, + CutDCAPosToPVMin, + CutDCANegToPVMin, + CutLifeTime, + CutRadiusMin, + CutRadiusMax, + CutRapidityMin, + CutRapidityMax, + CutArmPodMinVal, + CutDCABetDaug, + CutCompMassRej, + NumParticleCuts + }; + enum ResoParticleSwitches { + UseCosPA = 0, + UseMassBins, + UseDCABetDaug, + UseProperLifetime, + UseV0Radius, + UseArmPodCut, + UseCompMassRej, + NumParticleSwitches + }; + + enum ResoSelectionFlags { + FillCandidate = 0, + FillDaughterPt, + FillMassCut, + FillRapidityCut, + FillDCAtoPV, + FillDCAxDaughters, + FillV0Radius, + FillCosPA, + FillProperLifetime, + FillArmPodCut, + FillCompetingMass, + FillDaughterTrackSelected, }; enum CentEstimators { - kCentFT0C = 0, - kCentFT0CVariant1, - kCentFT0M, - kCentFV0A, + UseCentFT0C = 0, + UseCentFT0CVariant1, + UseCentFT0M, + UseCentFV0A, // Count the total number of enum - kCount_CentEstimators + NumCentEstimators }; RCTFlagsChecker rctChecker{"CBT"}; void init(InitContext&) { + // ---------------------------------------------------------------------------------------------------------------------- + // The way the code is initialized, TPC-FT0 correlations are done together + // ---------------------------------------------------------------------------------------------------------------------- + ccdb->setURL("http://alice-ccdb.cern.ch"); ccdb->setCaching(true); auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); @@ -344,249 +397,286 @@ struct CorrReso { LOGF(info, "Starting init"); - if (cfgPIDConfigs.cfgPIDParticle < kK0) { + readMatrix(cfgTrkSel.cfgTrackCuts->getData(), trackCuts); + readMatrix(cfgEvSel.cfgUseEventCuts->getData(), eventCuts); + readMatrix(cfgPIDConfigs.nSigmas->getData(), nSigmaVals); + readMatrix(cfgPIDConfigs.cfgResoCuts->getData(), resoCutVals); + readMatrix(cfgPIDConfigs.cfgResoSwitches->getData(), resoSwitchVals); + + if (cfgPIDConfigs.cfgPIDParticle < UseK0) { LOGF(fatal, "The input number does not correspond to any particle"); return; } - readMatrix(cfgPIDConfigs.cfgResoCuts->getData(), resoCutVals); - readMatrix(cfgPIDConfigs.cfgResoSwitches->getData(), resoSwitchVals); - readMatrix(cfgUseEventCuts->getData(), eventCuts); - readMatrix(cfgPIDConfigs.nSigmas->getData(), nSigmaVals); - // Creating mass axis depending on particle - 4 = kshort, 5 = lambda, 6 = phi AxisSpec axisInvMass = {10, 0, 1, "mass"}; - if (cfgPIDConfigs.cfgPIDParticle == kK0) { - const auto bins = resoSwitchVals[kMassBins][iK0]; - const auto min = resoCutVals[kMassMin][iK0]; - const auto max = resoCutVals[kMassMax][iK0]; + if (cfgPIDConfigs.cfgPIDParticle == UseK0) { + const auto bins = resoSwitchVals[UseMassBins][IndexK0]; + const auto min = resoCutVals[CutMassMin][IndexK0]; + const auto max = resoCutVals[CutMassMax][IndexK0]; axisInvMass = {bins, min, max, "M_{#pi^{+}#pi^{-}} (GeV/c^{2})"}; } - if (cfgPIDConfigs.cfgPIDParticle == kLambda) { - const auto bins = resoSwitchVals[kMassBins][iLambda]; - const auto min = resoCutVals[kMassMin][iLambda]; - const auto max = resoCutVals[kMassMax][iLambda]; + if (cfgPIDConfigs.cfgPIDParticle == UseLambda) { + const auto bins = resoSwitchVals[UseMassBins][IndexLambda]; + const auto min = resoCutVals[CutMassMin][IndexLambda]; + const auto max = resoCutVals[CutMassMax][IndexLambda]; axisInvMass = {bins, min, max, "M_{p#pi} (GeV/c^{2})"}; } - if (cfgPIDConfigs.cfgPIDParticle == kPhi) { - const auto bins = resoSwitchVals[kMassBins][iPhi]; - const auto min = resoCutVals[kMassMin][iPhi]; - const auto max = resoCutVals[kMassMax][iPhi]; - - axisInvMass = {bins, min, max, "M_{K^{+}K^{-}} (GeV/c^{2})"}; - } - - if (doprocessSameTpcFt0a || doprocessSameTpcFt0c) { - if (cfgPIDConfigs.cfgPIDParticle == kK0) { // For K0 - registry.add("PiPlusTPC_K0", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTPC}}}); - registry.add("PiMinusTPC_K0", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTPC}}}); - registry.add("PiPlusTOF_K0", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTOF}}}); - registry.add("PiMinusTOF_K0", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTOF}}}); - registry.add("hK0Phi", "", {HistType::kTH1D, {axisPhi}}); - registry.add("hK0Eta", "", {HistType::kTH1D, {axisEta}}); - - registry.add("hK0Count", "Number of K0;; Count", {HistType::kTH1D, {{11, 0, 11}}}); - registry.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(1, "K0 candidates"); - registry.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(2, "Daughter pt"); - registry.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(3, "Mass cut"); - registry.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(4, "Rapidity cut"); - registry.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(5, "DCA to PV"); - registry.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(6, "DCA between daughters"); - registry.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(7, "V0radius"); - registry.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(8, "CosPA"); - registry.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(9, "Proper lifetime"); - registry.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(10, "ArmenterosPod"); - registry.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(11, "Daughter track selection"); - } - if (cfgPIDConfigs.cfgPIDParticle == kLambda) { // For Lambda - registry.add("PrPlusTPC_La", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTPC}}}); - registry.add("PiMinusTPC_La", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTPC}}}); - registry.add("PrPlusTOF_La", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTOF}}}); - registry.add("PiMinusTOF_La", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTOF}}}); - - registry.add("PrMinusTPC_Al", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTPC}}}); - registry.add("PiPlusTPC_Al", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTPC}}}); - registry.add("PrMinusTOF_Al", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTOF}}}); - registry.add("PiPlusTOF_Al", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTOF}}}); - - registry.add("hLambdaPhi", "", {HistType::kTH1D, {axisPhi}}); - registry.add("hLambdaEta", "", {HistType::kTH1D, {axisEta}}); - - registry.add("hLambdaCount", "Number of Lambda;; Count", {HistType::kTH1D, {{10, 0, 10}}}); - registry.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(1, "Lambda candidates"); - registry.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(2, "Daughter pt"); - registry.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(3, "Mass cut"); - registry.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(4, "Rapidity cut"); - registry.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(5, "DCA to PV"); - registry.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(6, "DCA between daughters"); - registry.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(7, "V0radius"); - registry.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(8, "CosPA"); - registry.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(9, "Proper lifetime"); - registry.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(10, "Daughter track selection"); - } - } - if (doprocessSameTpcFt0a || doprocessSameTpcFt0c) { - registry.add("hEventCountRct", "Number of Event;; Count", {HistType::kTH1D, {{2, 0, 2}}}); - registry.get(HIST("hEventCountRct"))->GetXaxis()->SetBinLabel(1, "rct fail"); - registry.get(HIST("hEventCountRct"))->GetXaxis()->SetBinLabel(2, "rct pass"); - registry.add("hEventCount", "Number of Event;; Count", {HistType::kTH1D, {{14, -0.5, 13.5}}}); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kFilteredEvents + 1, "Filtered events"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kAfterSel8 + 1, "After sel8"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoTimeFrameBorder + 1, "kNoTimeFrameBorder"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoITSROFrameBorder + 1, "kNoITSROFrameBorder"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoSameBunchPileup + 1, "kNoSameBunchPileup"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseGoodITSLayer0123 + 1, "kIsGoodITSLayer0123"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoCollInRofStandard + 1, "kNoCollInRofStandard"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoHighMultCollInPrevRof + 1, "kNoHighMultCollInPrevRof"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseOccupancy + 1, "Occupancy Cut"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseMultCorrCut + 1, "MultCorrelation Cut"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseT0AV0ACut + 1, "T0AV0A cut"); - } - - if ((doprocessSameTpcFt0a || doprocessSameTpcFt0c) && cfgQaCheck) { - registry.add("hPassedEventSelection", "Number of Event;; Count", {HistType::kTH1D, {{12, -0.5, 11.5}}}); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kFilteredEvents + 1, "Filtered events"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kAfterSel8 + 1, "After sel8"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseNoTimeFrameBorder + 1, "kNoTimeFrameBorder"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseNoITSROFrameBorder + 1, "kNoITSROFrameBorder"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseNoSameBunchPileup + 1, "kNoSameBunchPileup"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseGoodITSLayer0123 + 1, "kIsGoodITSLayer0123"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseNoCollInRofStandard + 1, "kNoCollInRofStandard"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseNoHighMultCollInPrevRof + 1, "kNoHighMultCollInPrevRof"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseOccupancy + 1, "Occupancy Cut"); + + // Set the sample axis + auto maxSample = static_cast(cfgGeneral.cfgSampleSize); + AxisSpec axisSample{cfgGeneral.cfgSampleSize, 0, maxSample, "Sample"}; + + // Choose if it is Nch selection or Centrality selection + AxisSpec axisEventClass = {axisMultForCorr, "N_{ch}"}; + + if (!cfgEvSel.cfgSelCollByNch) { + axisEventClass = {axisCentForCorr, "Centrality"}; + } + + if (cfgPIDConfigs.cfgPIDParticle == UseK0) { // For K0 + histos.add("PiPlusTPC_K0", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTPC}}}); + histos.add("PiMinusTPC_K0", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTPC}}}); + histos.add("PiPlusTOF_K0", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTOF}}}); + histos.add("PiMinusTOF_K0", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTOF}}}); + histos.add("hK0Phi", "", {HistType::kTH1D, {axisPhi}}); + histos.add("hK0Rap", "", {HistType::kTH1D, {axisEta}}); + histos.add("hK0Eta", "", {HistType::kTH1D, {axisEta}}); + + histos.add("hK0Count", "Number of K0;; Count", {HistType::kTH1D, {{12, -0.5, 11.5}}}); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillCandidate + 1, "K0 candidates"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillDaughterPt + 1, "Daughter pt"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillMassCut + 1, "Mass cut"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillRapidityCut + 1, "Rapidity cut"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillDCAtoPV + 1, "DCA to PV"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillDCAxDaughters + 1, "DCA between daughters"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillV0Radius + 1, "V0radius"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillCosPA + 1, "CosPA"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillProperLifetime + 1, "Proper lifetime"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillArmPodCut + 1, "ArmenterosPod"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillCompetingMass + 1, "CompetingMassRejection"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillDaughterTrackSelected + 1, "Daughter track selection"); + } + + if (cfgPIDConfigs.cfgPIDParticle == UseLambda) { // For Lambda + histos.add("PrPlusTPC_La", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTPC}}}); + histos.add("PiMinusTPC_La", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTPC}}}); + histos.add("PrPlusTOF_La", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTOF}}}); + histos.add("PiMinusTOF_La", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTOF}}}); + + histos.add("PrMinusTPC_Al", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTPC}}}); + histos.add("PiPlusTPC_Al", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTPC}}}); + histos.add("PrMinusTOF_Al", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTOF}}}); + histos.add("PiPlusTOF_Al", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTOF}}}); + + histos.add("hLambdaPhi", "", {HistType::kTH1D, {axisPhi}}); + histos.add("hLambdaRap", "", {HistType::kTH1D, {axisEta}}); + histos.add("hLambdaEta", "", {HistType::kTH1D, {axisEta}}); + + histos.add("hLambdaCount", "Number of Lambda;; Count", {HistType::kTH1D, {{12, -0.5, 11.5}}}); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillCandidate + 1, "Lambda candidates"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillDaughterPt + 1, "Daughter pt"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillMassCut + 1, "Mass cut"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillRapidityCut + 1, "Rapidity cut"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillDCAtoPV + 1, "DCA to PV"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillDCAxDaughters + 1, "DCA between daughters"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillV0Radius + 1, "V0radius"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillCosPA + 1, "CosPA"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillProperLifetime + 1, "Proper lifetime"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillArmPodCut + 1, "ArmenterosPod"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillCompetingMass + 1, "CompetingMassRejection"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillDaughterTrackSelected + 1, "Daughter track selection"); + } + + histos.add("hEventCountRct", "Number of Event;; Count", {HistType::kTH1D, {{2, 0, 2}}}); + histos.get(HIST("hEventCountRct"))->GetXaxis()->SetBinLabel(1, "rct fail"); + histos.get(HIST("hEventCountRct"))->GetXaxis()->SetBinLabel(2, "rct pass"); + histos.add("hEventCount", "Number of Event;; Count", {HistType::kTH1D, {{15, -0.5, 14.5}}}); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(FilteredEvents + 1, "Filtered events"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(AfterSel8 + 1, "After sel8"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoTimeFrameBorder + 1, "kNoTimeFrameBorder"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoITSROFrameBorder + 1, "kNoITSROFrameBorder"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoSameBunchPileup + 1, "kNoSameBunchPileup"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodITSLayer0123 + 1, "kIsGoodITSLayer0123"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoCollInRofStandard + 1, "kNoCollInRofStandard"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoHighMultCollInPrevRof + 1, "kNoHighMultCollInPrevRof"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseOccupancy + 1, "Occupancy Cut"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseMultCorrCut + 1, "MultCorrelation Cut"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseT0AV0ACut + 1, "T0AV0A cut"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(HaveFT0Cut + 1, "Event has FT0 cut"); + + if (cfgGeneral.cfgQABasic) { + histos.add("hPassedEventSelection", "Number of Event;; Count", {HistType::kTH1D, {{12, -0.5, 11.5}}}); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(FilteredEvents + 1, "Filtered events"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(AfterSel8 + 1, "After sel8"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoTimeFrameBorder + 1, "kNoTimeFrameBorder"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoITSROFrameBorder + 1, "kNoITSROFrameBorder"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoSameBunchPileup + 1, "kNoSameBunchPileup"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseGoodITSLayer0123 + 1, "kIsGoodITSLayer0123"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoCollInRofStandard + 1, "kNoCollInRofStandard"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoHighMultCollInPrevRof + 1, "kNoHighMultCollInPrevRof"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseOccupancy + 1, "Occupancy Cut"); } // Multiplicity correlation cuts - if (eventCuts[kUseMultCorrCut][kEvCut1] != 0) { + if (eventCuts[UseMultCorrCut][EvCut1] != 0) { cfgFuncParas.multT0CCutPars = cfgFuncParas.cfgMultT0CCutPars; cfgFuncParas.multPVT0CCutPars = cfgFuncParas.cfgMultPVT0CCutPars; cfgFuncParas.multGlobalPVCutPars = cfgFuncParas.cfgMultGlobalPVCutPars; cfgFuncParas.multMultV0ACutPars = cfgFuncParas.cfgMultMultV0ACutPars; - cfgFuncParas.fMultPVT0CCutLow = new TF1("fMultPVT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); + + cfgFuncParas.fMultPVT0CCutLow = std::make_unique("fMultPVT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); cfgFuncParas.fMultPVT0CCutLow->SetParameters(cfgFuncParas.multPVT0CCutPars.data()); - cfgFuncParas.fMultPVT0CCutHigh = new TF1("fMultPVT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultPVT0CCutHigh = std::make_unique("fMultPVT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); cfgFuncParas.fMultPVT0CCutHigh->SetParameters(cfgFuncParas.multPVT0CCutPars.data()); - cfgFuncParas.fMultT0CCutLow = new TF1("fMultT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultT0CCutLow = std::make_unique("fMultT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); cfgFuncParas.fMultT0CCutLow->SetParameters(cfgFuncParas.multT0CCutPars.data()); - cfgFuncParas.fMultT0CCutHigh = new TF1("fMultT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultT0CCutHigh = std::make_unique("fMultT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); cfgFuncParas.fMultT0CCutHigh->SetParameters(cfgFuncParas.multT0CCutPars.data()); - cfgFuncParas.fMultGlobalPVCutLow = new TF1("fMultGlobalPVCutLow", cfgFuncParas.cfgMultMultPVLowCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultGlobalPVCutLow = std::make_unique("fMultGlobalPVCutLow", cfgFuncParas.cfgMultMultPVLowCutFunction->c_str(), 0, 4000); cfgFuncParas.fMultGlobalPVCutLow->SetParameters(cfgFuncParas.multGlobalPVCutPars.data()); - cfgFuncParas.fMultGlobalPVCutHigh = new TF1("fMultGlobalPVCutHigh", cfgFuncParas.cfgMultMultPVHighCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultGlobalPVCutHigh = std::make_unique("fMultGlobalPVCutHigh", cfgFuncParas.cfgMultMultPVHighCutFunction->c_str(), 0, 4000); cfgFuncParas.fMultGlobalPVCutHigh->SetParameters(cfgFuncParas.multGlobalPVCutPars.data()); - cfgFuncParas.fMultMultV0ACutLow = new TF1("fMultMultV0ACutLow", cfgFuncParas.cfgMultMultV0ALowCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultMultV0ACutLow = std::make_unique("fMultMultV0ACutLow", cfgFuncParas.cfgMultMultV0ALowCutFunction->c_str(), 0, 4000); cfgFuncParas.fMultMultV0ACutLow->SetParameters(cfgFuncParas.multMultV0ACutPars.data()); - cfgFuncParas.fMultMultV0ACutHigh = new TF1("fMultMultV0ACutHigh", cfgFuncParas.cfgMultMultV0AHighCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultMultV0ACutHigh = std::make_unique("fMultMultV0ACutHigh", cfgFuncParas.cfgMultMultV0AHighCutFunction->c_str(), 0, 4000); cfgFuncParas.fMultMultV0ACutHigh->SetParameters(cfgFuncParas.multMultV0ACutPars.data()); } - if (eventCuts[kUseT0AV0ACut][kEvCut1] != 0) { - cfgFuncParas.fT0AV0AMean = new TF1("fT0AV0AMean", "[0]+[1]*x", 0, 200000); + if (eventCuts[UseT0AV0ACut][EvCut1] != 0) { + cfgFuncParas.fT0AV0AMean = std::make_unique("fT0AV0AMean", "[0]+[1]*x", 0, 200000); cfgFuncParas.fT0AV0AMean->SetParameters(-1601.0581, 9.417652e-01); - cfgFuncParas.fT0AV0ASigma = new TF1("fT0AV0ASigma", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 200000); + cfgFuncParas.fT0AV0ASigma = std::make_unique("fT0AV0ASigma", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 200000); cfgFuncParas.fT0AV0ASigma->SetParameters(463.4144, 6.796509e-02, -9.097136e-07, 7.971088e-12, -2.600581e-17); } - if (doprocessSameTpcFt0a || doprocessSameTpcFt0c) { - registry.add("Phi", "Phi", {HistType::kTH1D, {axisPhi}}); - registry.add("Eta", "Eta", {HistType::kTH1D, {axisEta}}); - registry.add("EtaCorrected", "EtaCorrected", {HistType::kTH1D, {axisEta}}); - registry.add("pTFiner", "pTFiner", {HistType::kTH1D, {axisPtFiner}}); - registry.add("pTFinerCorrected", "pTFinerCorrected", {HistType::kTH1D, {axisPtFiner}}); - registry.add("Nch", "N_{ch}", {HistType::kTH1D, {axisMult}}); - registry.add("zVtx", "zVtx", {HistType::kTH1D, {axisVertex}}); - - registry.add("FT0Amp", "", {HistType::kTH2F, {axisChID, axisFit}}); - registry.add("FT0AmpCorrect", "", {HistType::kTH2F, {axisChID, axisFit}}); - } // end of single particle distribution histograms + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].empty()) { + cfgFuncParas.fPtDepDCAxy = std::make_unique("ptDepDCAxy", cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAxy->SetParameter(0, trackCuts[TrkCutDCAxyNsigma][UseGenTrkCuts]); + LOGF(info, "DCAxy pt-dependence function: %0.1f * %s", trackCuts[TrkCutDCAxyNsigma][UseGenTrkCuts], cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].c_str()); + } + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].empty()) { + cfgFuncParas.fPtDepDCAxyForNch = std::make_unique("ptDepDCAxyForNch", cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAxyForNch->SetParameter(0, trackCuts[TrkCutDCAxyNsigma][UseNchSelCuts]); + LOGF(info, "DCAxy pt-dependence function for Nch: %0.1f * %s", trackCuts[TrkCutDCAxyNsigma][UseNchSelCuts], cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].c_str()); + } - if (cfgQaCheck) { - registry.add("Nch_corrected", "N_{ch} corrected", {HistType::kTH1D, {axisMult}}); + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].empty()) { + cfgFuncParas.fPtDepDCAz = std::make_unique("ptDepDCAz", cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAz->SetParameter(0, trackCuts[TrkCutDCAzNsigma][UseGenTrkCuts]); + LOGF(info, "DCAz pt-dependence function: %0.1f * %s", trackCuts[TrkCutDCAzNsigma][UseGenTrkCuts], cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].c_str()); } + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].empty()) { + cfgFuncParas.fPtDepDCAzForNch = std::make_unique("ptDepDCAzForNch", cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAzForNch->SetParameter(0, trackCuts[TrkCutDCAzNsigma][UseNchSelCuts]); + LOGF(info, "DCAz pt-dependence function for Nch: %0.1f * %s", trackCuts[TrkCutDCAzNsigma][UseNchSelCuts], cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].c_str()); + } + + if (doprocessSameTpcFt0) { + histos.add("zVtx", "zVtx", {HistType::kTH1D, {axisVertex}}); + histos.add("Nch", "N_{ch}", {HistType::kTH1D, {axisMult}}); + histos.add("Centrality", "Centrality", {HistType::kTH1D, {axisCent}}); - if (doprocessSameTpcFt0a) { - registry.add("deltaEta_deltaPhi_same_TPC_FT0A", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0a}}); // check to see the delta eta and delta phi distribution - registry.add("deltaEta_deltaPhi_mixed_TPC_FT0A", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0a}}); - registry.add("Assoc_amp_same_TPC_FT0A", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); - registry.add("Assoc_amp_mixed_TPC_FT0A", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); - registry.add("Trig_hist_TPC_FT0A", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger, axisInvMass}}}); + if (cfgGeneral.cfgStrictTrackCounter) { + histos.add("Nch_corrected", "N_{ch} corrected", {HistType::kTH1D, {axisMult}}); + } + if (!cfgEvSel.cfgSelCollByNch) { + histos.add("CentralityWeighted", "Centrality (weighted)", {HistType::kTH1D, {axisCent}}); + } + + if (cfgGeneral.cfgQABasic) { + histos.add("Phi", "Phi", {HistType::kTH1D, {axisPhi}}); + histos.add("Eta", "Eta", {HistType::kTH1D, {axisEta}}); + histos.add("EtaCorrected", "EtaCorrected", {HistType::kTH1D, {axisEta}}); + histos.add("pTFiner", "pTFiner", {HistType::kTH1D, {axisPtFiner}}); + histos.add("pTFinerCorrected", "pTFinerCorrected", {HistType::kTH1D, {axisPtFiner}}); + } + + if (cfgGeneral.cfgAnalyzeTPCFT0A) { + histos.add("deltaEta_deltaPhi_same_TPC_FT0A", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0a}}); // check to see the delta eta and delta phi distribution + histos.add("Assoc_amp_same_TPC_FT0A", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0}}); + histos.add("Trig_hist_TPC_FT0A", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisEventClass, axisPtTrigger, axisInvMass}}}); + } + if (cfgGeneral.cfgAnalyzeTPCFT0C) { + histos.add("deltaEta_deltaPhi_same_TPC_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0c}}); // check to see the delta eta and delta phi distribution + histos.add("Assoc_amp_same_TPC_FT0C", "", {HistType::kTH2D, {axisChannelFt0cAxis, axisAmplitudeFt0}}); + histos.add("Trig_hist_TPC_FT0C", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisEventClass, axisPtTrigger, axisInvMass}}}); + } } - if (doprocessSameTpcFt0c) { - registry.add("deltaEta_deltaPhi_same_TPC_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0c}}); // check to see the delta eta and delta phi distribution - registry.add("deltaEta_deltaPhi_mixed_TPC_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0c}}); - registry.add("Assoc_amp_same_TPC_FT0C", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); - registry.add("Assoc_amp_mixed_TPC_FT0C", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); - registry.add("Trig_hist_TPC_FT0C", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger, axisInvMass}}}); + + if (doprocessMixedTpcFt0) { + if (cfgGeneral.cfgAnalyzeTPCFT0A) { + histos.add("deltaEta_deltaPhi_mixed_TPC_FT0A", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0a}}); + histos.add("Assoc_amp_mixed_TPC_FT0A", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0}}); + } + if (cfgGeneral.cfgAnalyzeTPCFT0C) { + histos.add("deltaEta_deltaPhi_mixed_TPC_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0c}}); + histos.add("Assoc_amp_mixed_TPC_FT0C", "", {HistType::kTH2D, {axisChannelFt0cAxis, axisAmplitudeFt0}}); + } } - registry.add("eventcount", "bin", {HistType::kTH1F, {{4, 0, 4, "bin"}}}); // histogram to see how many events are in the same and mixed event + histos.add("eventcount", "bin", {HistType::kTH1F, {{4, 0, 4, "bin"}}}); // histogram to see how many events are in the same and mixed event LOGF(info, "Initializing correlation container"); - // Initialize Nch-related histograms and containers - - std::vector corrAxisTpcFt0a = {{axisSample, "Sample"}, + // Initialize containers + std::vector corrAxisTpcFt0a = {{axisSample}, {axisVertex, "z-vtx (cm)"}, + {axisEventClass}, {axisPtTrigger, "p_{T} (GeV/c)"}, {axisInvMass}, {axisDeltaPhi, "#Delta#varphi (rad)"}, {axisDeltaEtaTpcFt0a, "#Delta#eta"}}; - std::vector corrAxisTpcFt0c = {{axisSample, "Sample"}, + std::vector corrAxisTpcFt0c = {{axisSample}, {axisVertex, "z-vtx (cm)"}, + {axisEventClass}, {axisPtTrigger, "p_{T} (GeV/c)"}, {axisInvMass}, {axisDeltaPhi, "#Delta#varphi (rad)"}, {axisDeltaEtaTpcFt0c, "#Delta#eta"}}; - std::vector effAxis = { - {axisEtaEfficiency, "#eta"}, - {axisPtEfficiency, "p_{T} (GeV/c)"}, - {axisVertexEfficiency, "z-vtx (cm)"}, - }; - std::vector userAxis; - - if (doprocessSameTpcFt0a) { - sameTpcFt0a.setObject(new CorrelationContainer("sameEvent_TPC_FT0A", "sameEvent_TPC_FT0A", corrAxisTpcFt0a, effAxis, userAxis)); - mixedTpcFt0a.setObject(new CorrelationContainer("mixedEvent_TPC_FT0A", "mixedEvent_TPC_FT0A", corrAxisTpcFt0a, effAxis, userAxis)); + if (cfgGeneral.cfgAnalyzeTPCFT0A) { + sameTpcFt0a.setObject(new DihadronContainer("sameEvent_TPC_FT0A", "sameEvent_TPC_FT0A", corrAxisTpcFt0a)); + mixedTpcFt0a.setObject(new DihadronContainer("mixedEvent_TPC_FT0A", "mixedEvent_TPC_FT0A", corrAxisTpcFt0a)); } - if (doprocessSameTpcFt0c) { - sameTpcFt0c.setObject(new CorrelationContainer("sameEvent_TPC_FT0C", "sameEvent_TPC_FT0C", corrAxisTpcFt0c, effAxis, userAxis)); - mixedTpcFt0c.setObject(new CorrelationContainer("mixedEvent_TPC_FT0C", "mixedEvent_TPC_FT0C", corrAxisTpcFt0c, effAxis, userAxis)); + if (cfgGeneral.cfgAnalyzeTPCFT0C) { + sameTpcFt0c.setObject(new DihadronContainer("sameEvent_TPC_FT0C", "sameEvent_TPC_FT0C", corrAxisTpcFt0c)); + mixedTpcFt0c.setObject(new DihadronContainer("mixedEvent_TPC_FT0C", "mixedEvent_TPC_FT0C", corrAxisTpcFt0c)); } - if (!cfgEvSelRCTflags.value.empty()) { - rctChecker.init(cfgEvSelRCTflags.value); // override initialzation + if (!cfgEvSel.cfgEvSelRCTflags.value.empty()) { + rctChecker.init(cfgEvSel.cfgEvSelRCTflags.value); // override initialzation } LOGF(info, "End of init"); } template - float getCentrality(TCollision const& collision) + double getCentrality(TCollision const& collision) { - float cent = 0.0; - switch (cfgCentEstimator) { - case kCentFT0C: + double cent = 0.0; + switch (cfgEvSel.cfgCentEstimator) { + case UseCentFT0C: cent = collision.centFT0C(); break; - case kCentFT0CVariant1: + case UseCentFT0CVariant1: cent = collision.centFT0CVariant1(); break; - case kCentFT0M: + case UseCentFT0M: cent = collision.centFT0M(); break; - case kCentFV0A: + case UseCentFV0A: cent = collision.centFV0A(); break; default: @@ -598,195 +688,217 @@ struct CorrReso { template bool eventRct(TCollision const& collision, const bool fillCounter) { + if (cfgEvSel.cfgEvSelRCTflags.value.empty()) { + return true; + } if (!rctChecker(collision)) { if (fillCounter) { - registry.fill(HIST("hEventCountRct"), 0.5); + histos.fill(HIST("hEventCountRct"), 0.5); } - return 0; + return false; } if (fillCounter) { - registry.fill(HIST("hEventCountRct"), 1.5); + histos.fill(HIST("hEventCountRct"), 1.5); } - return 1; + return true; } template - bool eventSelected(TCollision const& collision, const int mult, const bool fillCounter) + bool eventSelected(TCollision const& collision, const int mult, const double cent, const bool fillCounter) { - if (eventCuts[kUseNoTimeFrameBorder][kEvCut1] && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { - return 0; + if (fillCounter) { + histos.fill(HIST("hEventCount"), FilteredEvents); + } + if (!collision.sel8()) { + return false; + } + if (fillCounter) { + histos.fill(HIST("hEventCount"), AfterSel8); + } + + if (eventCuts[UseNoTimeFrameBorder][EvCut1] && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; } - if (fillCounter && eventCuts[kUseNoTimeFrameBorder][kEvCut1]) { - registry.fill(HIST("hEventCount"), kUseNoTimeFrameBorder); + if (fillCounter && eventCuts[UseNoTimeFrameBorder][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoTimeFrameBorder); } - if (eventCuts[kUseNoITSROFrameBorder][kEvCut1] && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { - return 0; + + if (eventCuts[UseNoITSROFrameBorder][EvCut1] && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; } - if (fillCounter && eventCuts[kUseNoITSROFrameBorder][kEvCut1]) { - registry.fill(HIST("hEventCount"), kUseNoITSROFrameBorder); + if (fillCounter && eventCuts[UseNoITSROFrameBorder][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoITSROFrameBorder); } - if (eventCuts[kUseNoSameBunchPileup][kEvCut1] && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + + if (eventCuts[UseNoSameBunchPileup][EvCut1] && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { // rejects collisions which are associated with the same "found-by-T0" bunch crossing // https://indico.cern.ch/event/1396220/#1-event-selection-with-its-rof - return 0; + return false; } - if (fillCounter && eventCuts[kUseNoSameBunchPileup][kEvCut1]) { - registry.fill(HIST("hEventCount"), kUseNoSameBunchPileup); + if (fillCounter && eventCuts[UseNoSameBunchPileup][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoSameBunchPileup); } - if (eventCuts[kUseGoodZvtxFT0vsPV][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + + if (eventCuts[UseGoodZvtxFT0vsPV][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference // use this cut at low multiplicities with caution - return 0; + return false; } - if (fillCounter && eventCuts[kUseGoodZvtxFT0vsPV][kEvCut1]) { - registry.fill(HIST("hEventCount"), kUseGoodZvtxFT0vsPV); + if (fillCounter && eventCuts[UseGoodZvtxFT0vsPV][EvCut1]) { + histos.fill(HIST("hEventCount"), UseGoodZvtxFT0vsPV); } - if (eventCuts[kUseNoCollInTimeRangeStandard][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + + if (eventCuts[UseNoCollInTimeRangeStandard][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { // no collisions in specified time range - return 0; + return false; } - if (fillCounter && eventCuts[kUseNoCollInTimeRangeStandard][kEvCut1]) { - registry.fill(HIST("hEventCount"), kUseNoCollInTimeRangeStandard); + if (fillCounter && eventCuts[UseNoCollInTimeRangeStandard][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoCollInTimeRangeStandard); } - if (eventCuts[kUseGoodITSLayersAll][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + + if (eventCuts[UseGoodITSLayersAll][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { // from Jan 9 2025 AOT meeting // cut time intervals with dead ITS staves - return 0; + return false; } - if (fillCounter && eventCuts[kUseGoodITSLayersAll][kEvCut1]) { - registry.fill(HIST("hEventCount"), kUseGoodITSLayersAll); + if (fillCounter && eventCuts[UseGoodITSLayersAll][EvCut1]) { + histos.fill(HIST("hEventCount"), UseGoodITSLayersAll); } - if (eventCuts[kUseGoodITSLayer0123][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { - return 0; + + if (eventCuts[UseGoodITSLayer0123][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { + return false; } - if (fillCounter && eventCuts[kUseGoodITSLayer0123][kEvCut1]) { - registry.fill(HIST("hEventCount"), kUseGoodITSLayer0123); + if (fillCounter && eventCuts[UseGoodITSLayer0123][EvCut1]) { + histos.fill(HIST("hEventCount"), UseGoodITSLayer0123); } - if (eventCuts[kUseNoCollInRofStandard][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + + if (eventCuts[UseNoCollInRofStandard][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { // no other collisions in this Readout Frame with per-collision multiplicity above threshold - return 0; + return false; } - if (fillCounter && eventCuts[kUseNoCollInRofStandard][kEvCut1]) { - registry.fill(HIST("hEventCount"), kUseNoCollInRofStandard); + if (fillCounter && eventCuts[UseNoCollInRofStandard][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoCollInRofStandard); } - if (eventCuts[kUseNoHighMultCollInPrevRof][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + + if (eventCuts[UseNoHighMultCollInPrevRof][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { // veto an event if FT0C amplitude in previous ITS ROF is above threshold - return 0; + return false; } - if (fillCounter && eventCuts[kUseNoHighMultCollInPrevRof][kEvCut1]) { - registry.fill(HIST("hEventCount"), kUseNoHighMultCollInPrevRof); + if (fillCounter && eventCuts[UseNoHighMultCollInPrevRof][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoHighMultCollInPrevRof); } + auto multNTracksPV = collision.multNTracksPV(); auto occupancy = collision.trackOccupancyInTimeRange(); - if (eventCuts[kUseOccupancy][kEvCut1] && (occupancy < cfgEventSelection.cfgCutOccupancyLow || occupancy > cfgEventSelection.cfgCutOccupancyHigh)) { - return 0; + if (eventCuts[UseOccupancy][EvCut1] && (occupancy < cfgEvSel.cfgCutOccupancyLow || occupancy > cfgEvSel.cfgCutOccupancyHigh)) { + return false; } - if (fillCounter && eventCuts[kUseOccupancy][kEvCut1]) { - registry.fill(HIST("hEventCount"), kUseOccupancy); + if (fillCounter && eventCuts[UseOccupancy][EvCut1]) { + histos.fill(HIST("hEventCount"), UseOccupancy); } - if (eventCuts[kUseMultCorrCut][kEvCut1]) { - float cent = getCentrality(collision); + + if (eventCuts[UseMultCorrCut][EvCut1]) { if (cfgFuncParas.cfgMultPVT0CCutEnabled) { if (multNTracksPV < cfgFuncParas.fMultPVT0CCutLow->Eval(cent)) { - return 0; + return false; } if (multNTracksPV > cfgFuncParas.fMultPVT0CCutHigh->Eval(cent)) { - return 0; + return false; } } if (cfgFuncParas.cfgMultT0CCutEnabled) { if (mult < cfgFuncParas.fMultT0CCutLow->Eval(cent)) { - return 0; + return false; } if (mult > cfgFuncParas.fMultT0CCutHigh->Eval(cent)) { - return 0; + return false; } } if (cfgFuncParas.cfgMultGlobalPVCutEnabled) { if (mult < cfgFuncParas.fMultGlobalPVCutLow->Eval(multNTracksPV)) { - return 0; + return false; } if (mult > cfgFuncParas.fMultGlobalPVCutHigh->Eval(multNTracksPV)) { - return 0; + return false; } } if (cfgFuncParas.cfgMultMultV0ACutEnabled) { if (collision.multFV0A() < cfgFuncParas.fMultMultV0ACutLow->Eval(mult)) { - return 0; + return false; } if (collision.multFV0A() > cfgFuncParas.fMultMultV0ACutHigh->Eval(mult)) { - return 0; + return false; } } } - if (fillCounter && eventCuts[kUseMultCorrCut][kEvCut1]) { - registry.fill(HIST("hEventCount"), kUseMultCorrCut); + if (fillCounter && eventCuts[UseMultCorrCut][EvCut1]) { + histos.fill(HIST("hEventCount"), UseMultCorrCut); } // V0A T0A 5 sigma cut - if (eventCuts[kUseT0AV0ACut][kEvCut1] && (std::fabs(collision.multFV0A() - cfgFuncParas.fT0AV0AMean->Eval(collision.multFT0A())) > cfgFuncParas.cfgV0AT0Acut * cfgFuncParas.fT0AV0ASigma->Eval(collision.multFT0A()))) { - return 0; + if (eventCuts[UseT0AV0ACut][EvCut1] && (std::fabs(collision.multFV0A() - cfgFuncParas.fT0AV0AMean->Eval(collision.multFT0A())) > cfgFuncParas.cfgV0AT0Acut * cfgFuncParas.fT0AV0ASigma->Eval(collision.multFT0A()))) { + return false; } - if (fillCounter && eventCuts[kUseT0AV0ACut][kEvCut1]) { - registry.fill(HIST("hEventCount"), kUseT0AV0ACut); + if (fillCounter && eventCuts[UseT0AV0ACut][EvCut1]) { + histos.fill(HIST("hEventCount"), UseT0AV0ACut); } - return 1; + return true; } template void eventSelectedIndividually(TCollision const& collision) { - registry.fill(HIST("hPassedEventSelection"), kFilteredEvents); + histos.fill(HIST("hPassedEventSelection"), FilteredEvents); if (collision.sel8()) { - registry.fill(HIST("hPassedEventSelection"), kAfterSel8); + histos.fill(HIST("hPassedEventSelection"), AfterSel8); } if (collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { - registry.fill(HIST("hPassedEventSelection"), kUseNoTimeFrameBorder); + histos.fill(HIST("hPassedEventSelection"), UseNoTimeFrameBorder); } if (collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { - registry.fill(HIST("hPassedEventSelection"), kUseNoITSROFrameBorder); + histos.fill(HIST("hPassedEventSelection"), UseNoITSROFrameBorder); } if (collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { - registry.fill(HIST("hPassedEventSelection"), kUseNoSameBunchPileup); + histos.fill(HIST("hPassedEventSelection"), UseNoSameBunchPileup); } if (collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { - registry.fill(HIST("hPassedEventSelection"), kUseGoodZvtxFT0vsPV); + histos.fill(HIST("hPassedEventSelection"), UseGoodZvtxFT0vsPV); } if (collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { - registry.fill(HIST("hPassedEventSelection"), kUseNoCollInTimeRangeStandard); + histos.fill(HIST("hPassedEventSelection"), UseNoCollInTimeRangeStandard); } if (collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { - registry.fill(HIST("hPassedEventSelection"), kUseGoodITSLayersAll); + histos.fill(HIST("hPassedEventSelection"), UseGoodITSLayersAll); } if (collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { - registry.fill(HIST("hPassedEventSelection"), kUseGoodITSLayer0123); + histos.fill(HIST("hPassedEventSelection"), UseGoodITSLayer0123); } if (collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { - registry.fill(HIST("hPassedEventSelection"), kUseNoCollInRofStandard); + histos.fill(HIST("hPassedEventSelection"), UseNoCollInRofStandard); } if (collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { - registry.fill(HIST("hPassedEventSelection"), kUseNoHighMultCollInPrevRof); + histos.fill(HIST("hPassedEventSelection"), UseNoHighMultCollInPrevRof); } auto occupancy = collision.trackOccupancyInTimeRange(); - if (eventCuts[kUseOccupancy][kEvCut1] && (occupancy > cfgEventSelection.cfgCutOccupancyLow && occupancy < cfgEventSelection.cfgCutOccupancyHigh)) { - registry.fill(HIST("hPassedEventSelection"), kUseOccupancy); + if (eventCuts[UseOccupancy][EvCut1] && (occupancy > cfgEvSel.cfgCutOccupancyLow && occupancy < cfgEvSel.cfgCutOccupancyHigh)) { + histos.fill(HIST("hPassedEventSelection"), UseOccupancy); } } @@ -832,7 +944,39 @@ struct CorrReso { template bool trackSelected(TTrack const& track) { - return ((track.tpcNClsFound() >= cfgTrackCuts.cfgCutTPCclu) && (track.tpcNClsCrossedRows() >= cfgTrackCuts.cfgCutTPCCrossedRows) && (track.itsNCls() >= cfgTrackCuts.cfgCutITSclu) && (track.tpcChi2NCl() < cfgTrackCuts.cfgCutChi2prTPCcls) && (std::abs(track.dcaZ()) < cfgTrackCuts.cfgCutDCAz)); + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].empty() && std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxy->Eval(track.pt())) { + return false; + } + + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].empty()) { + if (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAz->Eval(track.pt())) { + return false; + } + } else { + if (std::fabs(track.dcaZ()) > trackCuts[TrkCutDCAz][UseGenTrkCuts]) { + return false; + } + } + + return ((track.pt() > trackCuts[TrkCutPtMin][UseGenTrkCuts]) && (track.pt() < trackCuts[TrkCutPtMax][UseGenTrkCuts]) && (std::abs(track.eta()) < trackCuts[TrkCutEtaCut][UseGenTrkCuts]) && (track.tpcChi2NCl() < trackCuts[TrkCutChi2PrTpcCls][UseGenTrkCuts]) && (track.tpcNClsFound() >= trackCuts[TrkCutTpcCluster][UseGenTrkCuts]) && (track.tpcNClsCrossedRows() >= trackCuts[TrkCutTpcCrossedRows][UseGenTrkCuts]) && (track.itsNCls() >= trackCuts[TrkCutItsCluster][UseGenTrkCuts])); + } + + template + bool trackSelectedForNch(TTrack const& track) + { + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].empty() && (std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxyForNch->Eval(track.pt()))) { + return false; + } + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].empty()) { + if (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAzForNch->Eval(track.pt())) { + return false; + } + } else { + if (std::fabs(track.dcaZ()) > trackCuts[TrkCutDCAz][UseNchSelCuts]) { + return false; + } + } + return ((track.pt() > trackCuts[TrkCutPtMin][UseNchSelCuts]) && (track.pt() < trackCuts[TrkCutPtMax][UseNchSelCuts]) && (std::abs(track.eta()) < trackCuts[TrkCutEtaCut][UseNchSelCuts]) && (track.tpcChi2NCl() < trackCuts[TrkCutChi2PrTpcCls][UseNchSelCuts]) && (track.tpcNClsFound() >= trackCuts[TrkCutTpcCluster][UseNchSelCuts]) && (track.tpcNClsCrossedRows() >= trackCuts[TrkCutTpcCrossedRows][UseNchSelCuts]) && (track.itsNCls() >= trackCuts[TrkCutItsCluster][UseNchSelCuts])); } void loadGain(aod::BCsWithTimestamps::iterator const& bc) @@ -842,12 +986,12 @@ struct CorrReso { auto timestamp = bc.timestamp(); constexpr int ChannelsFT0 = 208; - if (cfgCorrLevel == 0) { + if (cfgGeneral.cfgCorrLevel == 0) { for (auto i{0u}; i < ChannelsFT0; i++) { cstFT0RelGain.push_back(1.); } } else { - fullPath = cfgGainEqPath; + fullPath = cfgGeneral.cfgGainEqPath; fullPath += "/FT0"; const auto objft0Gain = ccdb->getForTimeStamp>(fullPath, timestamp); if (!objft0Gain) { @@ -861,29 +1005,17 @@ struct CorrReso { } template - void getChannel(TFT0s const& ft0, std::size_t const& iCh, int& id, float& ampl, int fitType, int system) + void getChannel(TFT0s const& ft0, std::size_t const& iCh, int& id, float& ampl, int fitType) { - if (fitType == kFT0C) { + if (fitType == IndexFT0C) { id = ft0.channelC()[iCh]; id = id + Ft0IndexA; ampl = ft0.amplitudeC()[iCh]; - if (system == SameEvent) { - registry.fill(HIST("FT0Amp"), id, ampl); - } ampl = ampl / cstFT0RelGain[id]; - if (system == SameEvent) { - registry.fill(HIST("FT0AmpCorrect"), id, ampl); - } - } else if (fitType == kFT0A) { + } else if (fitType == IndexFT0A) { id = ft0.channelA()[iCh]; ampl = ft0.amplitudeA()[iCh]; - if (system == SameEvent) { - registry.fill(HIST("FT0Amp"), id, ampl); - } ampl = ampl / cstFT0RelGain[id]; - if (system == SameEvent) { - registry.fill(HIST("FT0AmpCorrect"), id, ampl); - } } else { LOGF(fatal, "Cor Index %d out of range", fitType); } @@ -899,18 +1031,26 @@ struct CorrReso { int pid = -1; // -1 = not identified, 1 = pion, 2 = kaon, 3 = proton std::array nSigmaToUse = cfgPIDConfigs.cfgUseItsPID ? nSigmaITS : nSigmaTPC; // Choose which nSigma to use: TPC or ITS - int kIndexDetector = cfgPIDConfigs.cfgUseItsPID ? kITS : kTPC; // Choose which nSigma to use: TPC or ITS + int useIndexDetector = UseTPC; + useIndexDetector = cfgPIDConfigs.cfgUseItsPID ? UseITS : UseTPC; // Choose which nSigma to use: TPC or ITS bool isPion = false; bool isKaon = false; bool isProton = false; - bool isDetectedPion = nSigmaToUse[iPionUp] < nSigmaVals[iPionUp][kIndexDetector] && nSigmaToUse[iPionUp] > nSigmaVals[iPionLow][kIndexDetector]; - bool isDetectedKaon = nSigmaToUse[iKaonUp] < nSigmaVals[iKaonUp][kIndexDetector] && nSigmaToUse[iKaonUp] > nSigmaVals[iKaonLow][kIndexDetector]; - bool isDetectedProton = nSigmaToUse[iProtonUp] < nSigmaVals[iProtonUp][kIndexDetector] && nSigmaToUse[iProtonUp] > nSigmaVals[iProtonLow][kIndexDetector]; + bool isDetectedPion = false; + bool isDetectedKaon = false; + bool isDetectedProton = false; + bool isTofPion = false; + bool isTofKaon = false; + bool isTofProton = false; + + isDetectedPion = nSigmaToUse[IndexPionUp] < nSigmaVals[IndexPionUp][useIndexDetector] && nSigmaToUse[IndexPionUp] > nSigmaVals[IndexPionLow][useIndexDetector]; + isDetectedKaon = nSigmaToUse[IndexKaonUp] < nSigmaVals[IndexKaonUp][useIndexDetector] && nSigmaToUse[IndexKaonUp] > nSigmaVals[IndexKaonLow][useIndexDetector]; + isDetectedProton = nSigmaToUse[IndexProtonUp] < nSigmaVals[IndexProtonUp][useIndexDetector] && nSigmaToUse[IndexProtonUp] > nSigmaVals[IndexProtonLow][useIndexDetector]; - bool isTofPion = nSigmaTOF[iPionUp] < nSigmaVals[iPionUp][kTOF] && nSigmaTOF[iPionUp] > nSigmaVals[iPionLow][kTOF]; - bool isTofKaon = nSigmaTOF[iKaonUp] < nSigmaVals[iKaonUp][kTOF] && nSigmaTOF[iKaonUp] > nSigmaVals[iKaonLow][kTOF]; - bool isTofProton = nSigmaTOF[iProtonUp] < nSigmaVals[iProtonUp][kTOF] && nSigmaTOF[iProtonUp] > nSigmaVals[iProtonLow][kTOF]; + isTofPion = nSigmaTOF[IndexPionUp] < nSigmaVals[IndexPionUp][UseTOF] && nSigmaTOF[IndexPionUp] > nSigmaVals[IndexPionLow][UseTOF]; + isTofKaon = nSigmaTOF[IndexKaonUp] < nSigmaVals[IndexKaonUp][UseTOF] && nSigmaTOF[IndexKaonUp] > nSigmaVals[IndexKaonLow][UseTOF]; + isTofProton = nSigmaTOF[IndexProtonUp] < nSigmaVals[IndexProtonUp][UseTOF] && nSigmaTOF[IndexProtonUp] > nSigmaVals[IndexProtonLow][UseTOF]; if (track.pt() > cfgPIDConfigs.cfgTofPtCut && !track.hasTOF()) { return -1; @@ -930,11 +1070,11 @@ struct CorrReso { } if (isPion) { - pid = kPions; + pid = UsePions; } else if (isKaon) { - pid = kKaons; + pid = UseKaons; } else if (isProton) { - pid = kProtons; + pid = UseProtons; } else { return -1; // no particle satisfies the criteria } @@ -945,30 +1085,27 @@ struct CorrReso { template bool selectionV0Daughter(TTrack const& track, int pid) { - if (!(track.itsNCls() >= cfgTrackCuts.cfgCutITSclu)) { + if (!(track.itsNCls() >= trackCuts[TrkCutItsCluster][UseGenTrkCuts])) { return false; } if (!track.hasTPC()) { return false; } - if (!(track.tpcNClsFound() >= cfgTrackCuts.cfgCutTPCclu)) { + if (!(track.tpcNClsFound() >= trackCuts[TrkCutTpcCluster][UseGenTrkCuts])) { return false; } - if (!(track.tpcNClsCrossedRows() >= cfgTrackCuts.cfgCutTPCCrossedRows)) { - return false; - } - if (!(std::abs(track.dcaZ()) < cfgTrackCuts.cfgCutDCAz)) { + if (!(track.tpcNClsCrossedRows() >= trackCuts[TrkCutTpcCrossedRows][UseGenTrkCuts])) { return false; } if (cfgPIDConfigs.cfgUseOnlyTPC) { - if (pid == kPions && std::abs(track.tpcNSigmaPi()) > cfgPIDConfigs.cfgTpcCut) { + if (pid == UsePions && std::abs(track.tpcNSigmaPi()) > cfgPIDConfigs.cfgTpcCut) { return false; } - if (pid == kKaons && std::abs(track.tpcNSigmaKa()) > cfgPIDConfigs.cfgTpcCut) { + if (pid == UseKaons && std::abs(track.tpcNSigmaKa()) > cfgPIDConfigs.cfgTpcCut) { return false; } - if (pid == kProtons && std::abs(track.tpcNSigmaPr()) > cfgPIDConfigs.cfgTpcCut) { + if (pid == UseProtons && std::abs(track.tpcNSigmaPr()) > cfgPIDConfigs.cfgTpcCut) { return false; } } else { @@ -987,38 +1124,40 @@ struct CorrReso { if (correctionsLoaded) { return; } - if (!cfgEfficiency.value.empty()) { - if (cfgLocalEfficiency) { - TFile* fEfficiencyTrigger = TFile::Open(cfgEfficiency.value.c_str(), "READ"); + if (!cfgEffWeights.cfgEfficiency.value.empty()) { + if (cfgEffWeights.cfgLocalEfficiency) { + TFile* fEfficiencyTrigger = nullptr; + fEfficiencyTrigger = TFile::Open(cfgEffWeights.cfgEfficiency.value.c_str(), "READ"); mEfficiency = dynamic_cast(fEfficiencyTrigger->Get("ccdb_object")); } else { - mEfficiency = ccdb->getForTimeStamp(cfgEfficiency, timestamp); + mEfficiency = ccdb->getForTimeStamp(cfgEffWeights.cfgEfficiency, timestamp); } if (mEfficiency == nullptr) { - LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEfficiency.value.c_str()); + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEffWeights.cfgEfficiency.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEfficiency.value.c_str(), static_cast(mEfficiency)); + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEffWeights.cfgEfficiency.value.c_str(), static_cast(mEfficiency)); } - if (!cfgEfficiencyNch.value.empty()) { - if (cfgLocalEfficiencyNch) { - TFile* fEfficiencyTrigger = TFile::Open(cfgEfficiencyNch.value.c_str(), "READ"); + if (!cfgEffWeights.cfgEfficiencyNch.value.empty()) { + if (cfgEffWeights.cfgLocalEfficiencyNch) { + TFile* fEfficiencyTrigger = nullptr; + fEfficiencyTrigger = TFile::Open(cfgEffWeights.cfgEfficiencyNch.value.c_str(), "READ"); mEfficiencyNch = dynamic_cast(fEfficiencyTrigger->Get("ccdb_object")); } else { - mEfficiencyNch = ccdb->getForTimeStamp(cfgEfficiencyNch, timestamp); + mEfficiencyNch = ccdb->getForTimeStamp(cfgEffWeights.cfgEfficiencyNch, timestamp); } if (!mEfficiencyNch) { - LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEfficiencyNch.value.c_str()); + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEffWeights.cfgEfficiencyNch.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEfficiencyNch.value.c_str(), static_cast(mEfficiencyNch)); + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEffWeights.cfgEfficiencyNch.value.c_str(), static_cast(mEfficiencyNch)); } - if (!cfgCentralityWeight.value.empty()) { - mCentralityWeight = ccdb->getForTimeStamp(cfgCentralityWeight, timestamp); + if (!cfgEffWeights.cfgCentralityWeight.value.empty()) { + mCentralityWeight = ccdb->getForTimeStamp(cfgEffWeights.cfgCentralityWeight, timestamp); if (mCentralityWeight == nullptr) { - LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgCentralityWeight.value.c_str()); + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEffWeights.cfgCentralityWeight.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgCentralityWeight.value.c_str(), static_cast(mCentralityWeight)); + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEffWeights.cfgCentralityWeight.value.c_str(), static_cast(mCentralityWeight)); } correctionsLoaded = true; } @@ -1027,10 +1166,8 @@ struct CorrReso { { float effNch = 1.; if (mEfficiencyNch) { - int ptBin = mEfficiencyNch->FindBin(pt); effNch = mEfficiencyNch->GetBinContent(ptBin); - } else { effNch = 1.0; } @@ -1045,12 +1182,10 @@ struct CorrReso { { float eff = 1.; if (mEfficiency) { - int etaBin = mEfficiency->GetXaxis()->FindBin(eta); // use the eta bin corresponding to eta=0 for the trigger particle efficiency int ptBin = mEfficiency->GetYaxis()->FindBin(pt); int vertexBin = mEfficiency->GetZaxis()->FindBin(vertex); // use the vertex bin corresponding to z=0 for the trigger particle efficiency eff = mEfficiency->GetBinContent(etaBin, ptBin, vertexBin); - } else { eff = 1.0; } @@ -1061,17 +1196,34 @@ struct CorrReso { return true; } + bool getCentralityWeight(float& weightCent, const double centrality) + { + float weight = 1.; + if (mCentralityWeight) { + weight = mCentralityWeight->GetBinContent(mCentralityWeight->FindBin(centrality)); + } else { + weight = 1.0; + } + if (weight == 0) { + return false; + } + weightCent = weight; + return true; + } + template - void trackCounter(const TTracks& tracks, double& multiplicity) // function to count the number of tracks in the event and fill the histogram + void nchCounter(const TTracks& tracks, double& multiplicity) // function to count the number of tracks in the event and fill the histogram { double nTracksCorrected = 0; float weightNch = 1.0f; for (auto const& track : tracks) { + if (!trackSelectedForNch(track)) { + continue; + } if (!getEfficiencyCorrectionNch(weightNch, track.pt())) { continue; } - nTracksCorrected += weightNch; } multiplicity = nTracksCorrected; @@ -1094,16 +1246,16 @@ struct CorrReso { continue; } - registry.fill(HIST("Phi"), RecoDecay::constrainAngle(track1.phi(), 0.0)); - registry.fill(HIST("Eta"), track1.eta()); - registry.fill(HIST("EtaCorrected"), track1.eta(), weff1); - registry.fill(HIST("pTFiner"), track1.pt()); - registry.fill(HIST("pTFinerCorrected"), track1.pt(), weff1); + histos.fill(HIST("Phi"), RecoDecay::constrainAngle(track1.phi(), 0.0)); + histos.fill(HIST("Eta"), track1.eta()); + histos.fill(HIST("EtaCorrected"), track1.eta(), weff1); + histos.fill(HIST("pTFiner"), track1.pt()); + histos.fill(HIST("pTFinerCorrected"), track1.pt(), weff1); } } template - bool isSelectedK0(V0 const& candidate, float posZ, float posY, float posX) + bool isSelectedK0(V0 const& candidate, float posZ, float posY, float posX, int system, int corType) { double mk0 = candidate.mK0Short(); @@ -1111,68 +1263,103 @@ struct CorrReso { auto postrack = candidate.template posTrack_as(); auto negtrack = candidate.template negTrack_as(); - registry.fill(HIST("hK0Count"), 0.5); - if (postrack.pt() < resoCutVals[kPosTrackPt][iK0] || negtrack.pt() < resoCutVals[kNegTrackPt][iK0]) { + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillCandidate); + } + if (postrack.pt() < resoCutVals[CutPosTrackPt][IndexK0] || negtrack.pt() < resoCutVals[CutNegTrackPt][IndexK0]) { return false; } - registry.fill(HIST("hK0Count"), 1.5); - if (mk0 < resoCutVals[kMassMin][iK0] || mk0 > resoCutVals[kMassMax][iK0]) { + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillDaughterPt); + } + if (mk0 < resoCutVals[CutMassMin][IndexK0] || mk0 > resoCutVals[CutMassMax][IndexK0]) { return false; } - registry.fill(HIST("hK0Count"), 2.5); + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillMassCut); + } // Rapidity correction - if (std::abs(candidate.yK0Short()) > resoCutVals[kRapidity][iK0]) { + if (candidate.yK0Short() > resoCutVals[CutRapidityMax][IndexK0] || candidate.yK0Short() < resoCutVals[CutRapidityMin][IndexK0]) { return false; } - registry.fill(HIST("hK0Count"), 3.5); + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillRapidityCut); + } // DCA cuts for K0short - if (std::abs(candidate.dcapostopv()) < resoCutVals[kDCAPosToPVMin][iK0] || std::abs(candidate.dcanegtopv()) < resoCutVals[kDCANegToPVMin][iK0]) { + if (std::abs(candidate.dcapostopv()) < resoCutVals[CutDCAPosToPVMin][IndexK0] || std::abs(candidate.dcanegtopv()) < resoCutVals[CutDCANegToPVMin][IndexK0]) { return false; } - registry.fill(HIST("hK0Count"), 4.5); - if (resoSwitchVals[kUseDCABetDaug][iK0] && std::abs(candidate.dcaV0daughters()) > resoCutVals[kDCABetDaug][iK0]) { + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillDCAtoPV); + } + if (resoSwitchVals[UseDCABetDaug][IndexK0] && std::abs(candidate.dcaV0daughters()) > resoCutVals[CutDCABetDaug][IndexK0]) { return false; } - registry.fill(HIST("hK0Count"), 5.5); + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillDCAxDaughters); + } // v0 radius cuts - if (resoSwitchVals[kUseV0Radius][iK0] && (candidate.v0radius() < resoCutVals[kRadiusMin][iK0] || candidate.v0radius() > resoCutVals[kRadiusMax][iK0])) { + if (resoSwitchVals[UseV0Radius][IndexK0] && (candidate.v0radius() < resoCutVals[CutRadiusMin][IndexK0] || candidate.v0radius() > resoCutVals[CutRadiusMax][IndexK0])) { return false; } - registry.fill(HIST("hK0Count"), 6.5); + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillV0Radius); + } // cosine pointing angle cuts - if (resoSwitchVals[kUseCosPA][iK0] && candidate.v0cosPA() < resoCutVals[kCosPA][iK0]) { + if (resoSwitchVals[UseCosPA][IndexK0] && candidate.v0cosPA() < resoCutVals[CutCosPA][IndexK0]) { return false; } - registry.fill(HIST("hK0Count"), 7.5); + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillCosPA); + } // Proper lifetime float cTauK0 = candidate.distovertotmom(posX, posY, posZ) * massK0Short; - if (resoSwitchVals[kUseProperLifetime][iK0] && cTauK0 > resoCutVals[kLifeTime][iK0]) { + if (resoSwitchVals[UseProperLifetime][IndexK0] && cTauK0 > resoCutVals[CutLifeTime][IndexK0]) { return false; } - registry.fill(HIST("hK0Count"), 8.5); + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillProperLifetime); + } // ArmenterosPodolanskiCut - if (resoSwitchVals[kUseArmPodCut][iK0] && (candidate.qtarm() / std::abs(candidate.alpha())) < resoCutVals[kArmPodMinVal][iK0]) { + if (resoSwitchVals[UseArmPodCut][IndexK0] && (candidate.qtarm() / std::abs(candidate.alpha())) < resoCutVals[CutArmPodMinVal][IndexK0]) { return false; } - registry.fill(HIST("hK0Count"), 9.5); + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillArmPodCut); + } + + if (resoSwitchVals[UseCompMassRej][IndexK0]) { + if (std::abs(candidate.mLambda() - o2::constants::physics::MassLambda) < resoCutVals[CutCompMassRej][IndexK0]) { + return false; + } + if (std::abs(candidate.mAntiLambda() - o2::constants::physics::MassLambda) < resoCutVals[CutCompMassRej][IndexK0]) { + return false; + } + } + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillCompetingMass); + } + // Selection on V0 daughters - if (!selectionV0Daughter(postrack, kPions) || !selectionV0Daughter(negtrack, kPions)) { + if (!selectionV0Daughter(postrack, UsePions) || !selectionV0Daughter(negtrack, UsePions)) { return false; } - registry.fill(HIST("hK0Count"), 10.5); - - registry.fill(HIST("hK0Phi"), candidate.phi()); - registry.fill(HIST("hK0Eta"), candidate.eta()); - registry.fill(HIST("PiPlusTPC_K0"), postrack.pt(), postrack.tpcNSigmaPi()); - registry.fill(HIST("PiPlusTOF_K0"), postrack.pt(), postrack.tofNSigmaPi()); - registry.fill(HIST("PiMinusTPC_K0"), negtrack.pt(), negtrack.tpcNSigmaPi()); - registry.fill(HIST("PiMinusTOF_K0"), negtrack.pt(), negtrack.tofNSigmaPi()); + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillDaughterTrackSelected); + histos.fill(HIST("hK0Phi"), candidate.phi()); + histos.fill(HIST("hK0Rap"), candidate.yK0Short()); + histos.fill(HIST("hK0Eta"), candidate.eta()); + histos.fill(HIST("PiPlusTPC_K0"), postrack.pt(), postrack.tpcNSigmaPi()); + histos.fill(HIST("PiPlusTOF_K0"), postrack.pt(), postrack.tofNSigmaPi()); + histos.fill(HIST("PiMinusTPC_K0"), negtrack.pt(), negtrack.tpcNSigmaPi()); + histos.fill(HIST("PiMinusTOF_K0"), negtrack.pt(), negtrack.tofNSigmaPi()); + } return true; } template - bool isSelectedLambda(V0 const& candidate, float posZ, float posY, float posX) + bool isSelectedLambda(V0 const& candidate, float posZ, float posY, float posX, int system, int corType) { bool isL = false; // Is lambda candidate bool isAL = false; // Is anti-lambda candidate @@ -1184,98 +1371,135 @@ struct CorrReso { auto postrack = candidate.template posTrack_as(); auto negtrack = candidate.template negTrack_as(); - registry.fill(HIST("hLambdaCount"), 0.5); - if (postrack.pt() < resoCutVals[kPosTrackPt][iLambda] || negtrack.pt() < resoCutVals[kNegTrackPt][iLambda]) { + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillCandidate); + } + if (postrack.pt() < resoCutVals[CutPosTrackPt][IndexLambda] || negtrack.pt() < resoCutVals[CutNegTrackPt][IndexLambda]) { return false; } - registry.fill(HIST("hLambdaCount"), 1.5); - if (mlambda > resoCutVals[kMassMin][iLambda] && mlambda < resoCutVals[kMassMax][iLambda]) { + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillDaughterPt); + } + if (mlambda > resoCutVals[CutMassMin][IndexLambda] && mlambda < resoCutVals[CutMassMax][IndexLambda]) { isL = true; } - if (mantilambda > resoCutVals[kMassMin][iLambda] && mantilambda < resoCutVals[kMassMax][iLambda]) { + if (mantilambda > resoCutVals[CutMassMin][IndexLambda] && mantilambda < resoCutVals[CutMassMax][IndexLambda]) { isAL = true; } if (!isL && !isAL) { return false; } - registry.fill(HIST("hLambdaCount"), 2.5); + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillMassCut); + } // Rapidity correction - if (std::abs(candidate.yLambda()) > resoCutVals[kRapidity][iLambda]) { + if (candidate.yLambda() > resoCutVals[CutRapidityMax][IndexLambda] || candidate.yLambda() < resoCutVals[CutRapidityMin][IndexLambda]) { return false; } - registry.fill(HIST("hLambdaCount"), 3.5); + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillRapidityCut); + } // DCA cuts for lambda and antilambda - if (isL && (std::abs(candidate.dcapostopv()) < resoCutVals[kDCAPosToPVMin][iLambda] || std::abs(candidate.dcanegtopv()) < resoCutVals[kDCANegToPVMin][iLambda])) { + if (isL && (std::abs(candidate.dcapostopv()) < resoCutVals[CutDCAPosToPVMin][IndexLambda] || std::abs(candidate.dcanegtopv()) < resoCutVals[CutDCANegToPVMin][IndexLambda])) { isL = false; } - if (isAL && (std::abs(candidate.dcapostopv()) < resoCutVals[kDCANegToPVMin][iLambda] || std::abs(candidate.dcanegtopv()) < resoCutVals[kDCAPosToPVMin][iLambda])) { + if (isAL && (std::abs(candidate.dcapostopv()) < resoCutVals[CutDCANegToPVMin][IndexLambda] || std::abs(candidate.dcanegtopv()) < resoCutVals[CutDCAPosToPVMin][IndexLambda])) { isAL = false; } if (!isL && !isAL) { return false; } - registry.fill(HIST("hLambdaCount"), 4.5); - if (resoSwitchVals[kUseDCABetDaug][iLambda] && std::abs(candidate.dcaV0daughters()) > resoCutVals[kDCABetDaug][iLambda]) { + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillDCAtoPV); + } + if (resoSwitchVals[UseDCABetDaug][IndexLambda] && std::abs(candidate.dcaV0daughters()) > resoCutVals[CutDCABetDaug][IndexLambda]) { return false; } - registry.fill(HIST("hLambdaCount"), 5.5); + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillDCAxDaughters); + } // v0 radius cuts - if (resoSwitchVals[kUseV0Radius][iLambda] && (candidate.v0radius() < resoCutVals[kRadiusMin][iLambda] || candidate.v0radius() > resoCutVals[kRadiusMax][iLambda])) { + if (resoSwitchVals[UseV0Radius][IndexLambda] && (candidate.v0radius() < resoCutVals[CutRadiusMin][IndexLambda] || candidate.v0radius() > resoCutVals[CutRadiusMax][IndexLambda])) { return false; } - registry.fill(HIST("hLambdaCount"), 6.5); + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillV0Radius); + } // cosine pointing angle cuts - if (resoSwitchVals[kUseCosPA][iLambda] && candidate.v0cosPA() < resoCutVals[kCosPA][iLambda]) { + if (resoSwitchVals[UseCosPA][IndexLambda] && candidate.v0cosPA() < resoCutVals[CutCosPA][IndexLambda]) { return false; } - registry.fill(HIST("hLambdaCount"), 7.5); + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillCosPA); + } // Proper lifetime float cTauLambda = candidate.distovertotmom(posX, posY, posZ) * massLambda; - if (resoSwitchVals[kUseProperLifetime][iLambda] && cTauLambda > resoCutVals[kLifeTime][iLambda]) { + if (resoSwitchVals[UseProperLifetime][IndexLambda] && cTauLambda > resoCutVals[CutLifeTime][IndexLambda]) { return false; } - registry.fill(HIST("hLambdaCount"), 8.5); + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillProperLifetime); + } + + // ArmenterosPodolanskiCut + if (resoSwitchVals[UseArmPodCut][IndexLambda] && (candidate.qtarm() / std::abs(candidate.alpha())) < resoCutVals[CutArmPodMinVal][IndexLambda]) { + return false; + } + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillArmPodCut); + } + + if (resoSwitchVals[UseCompMassRej][IndexLambda]) { + if (std::abs(candidate.mK0Short() - o2::constants::physics::MassK0Short) < resoCutVals[CutCompMassRej][IndexLambda]) { + return false; + } + } + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillCompetingMass); + } if (isL) { - if (!selectionV0Daughter(postrack, kProtons) || !selectionV0Daughter(negtrack, kPions)) { + if (!selectionV0Daughter(postrack, UseProtons) || !selectionV0Daughter(negtrack, UsePions)) { isL = false; } } if (isAL) { - if (!selectionV0Daughter(postrack, kPions) || !selectionV0Daughter(negtrack, kProtons)) { + if (!selectionV0Daughter(postrack, UsePions) || !selectionV0Daughter(negtrack, UseProtons)) { isAL = false; } } if (!isL && !isAL) { return false; } - registry.fill(HIST("hLambdaCount"), 9.5); - - registry.fill(HIST("hLambdaPhi"), candidate.phi()); - registry.fill(HIST("hLambdaEta"), candidate.eta()); - if (isL) { - registry.fill(HIST("PrPlusTPC_La"), postrack.pt(), postrack.tpcNSigmaPr()); - registry.fill(HIST("PrPlusTOF_La"), postrack.pt(), postrack.tofNSigmaPr()); - registry.fill(HIST("PiMinusTPC_La"), negtrack.pt(), negtrack.tpcNSigmaPi()); - registry.fill(HIST("PiMinusTOF_La"), negtrack.pt(), negtrack.tofNSigmaPi()); + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillDaughterTrackSelected); + histos.fill(HIST("hLambdaPhi"), candidate.phi()); + histos.fill(HIST("hLambdaEta"), candidate.eta()); + histos.fill(HIST("hLambdaRap"), candidate.yLambda()); } - if (isAL) { - registry.fill(HIST("PrMinusTPC_Al"), negtrack.pt(), negtrack.tpcNSigmaPr()); - registry.fill(HIST("PrMinusTOF_Al"), negtrack.pt(), negtrack.tofNSigmaPr()); - registry.fill(HIST("PiPlusTPC_Al"), postrack.pt(), postrack.tpcNSigmaPi()); - registry.fill(HIST("PiPlusTOF_Al"), postrack.pt(), postrack.tofNSigmaPi()); + if (isL && system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("PrPlusTPC_La"), postrack.pt(), postrack.tpcNSigmaPr()); + histos.fill(HIST("PrPlusTOF_La"), postrack.pt(), postrack.tofNSigmaPr()); + histos.fill(HIST("PiMinusTPC_La"), negtrack.pt(), negtrack.tpcNSigmaPi()); + histos.fill(HIST("PiMinusTOF_La"), negtrack.pt(), negtrack.tofNSigmaPi()); + } + if (isAL && system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("PrMinusTPC_Al"), negtrack.pt(), negtrack.tpcNSigmaPr()); + histos.fill(HIST("PrMinusTOF_Al"), negtrack.pt(), negtrack.tofNSigmaPr()); + histos.fill(HIST("PiPlusTPC_Al"), postrack.pt(), postrack.tpcNSigmaPi()); + histos.fill(HIST("PiPlusTOF_Al"), postrack.pt(), postrack.tofNSigmaPi()); } return true; } - template - void fillCorrelationsTPCFT0(const TV0Tracks& tracks1, TFT0s const& ft0, float posZ, float posY, float posX, int system, int corType, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + template + void fillCorrelationsTPCFT0(const TV0Tracks& tracks1, TFT0s const& ft0, float posZ, float posY, float posX, int system, double eventClass, int corType, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms { - int fSampleIndex = static_cast(gRandom->Uniform(0.0, cfgSampleSize)); + int fSampleIndex = static_cast(fRandom.Uniform(0.0, cfgGeneral.cfgSampleSize)); float triggerWeight = 1.0f; // loop over all tracks @@ -1283,16 +1507,16 @@ struct CorrReso { double resoMass = -1; // 4 = kshort, 5 = lambda, 6 = phi - if (cfgPIDConfigs.cfgPIDParticle == kK0) { - if (!isSelectedK0(track1, posZ, posY, posX)) { + if (cfgPIDConfigs.cfgPIDParticle == UseK0) { + if (!isSelectedK0(track1, posZ, posY, posX, system, corType)) { continue; // Reject if called for K0 but V0 is not K0 } resoMass = track1.mK0Short(); } - if (cfgPIDConfigs.cfgPIDParticle == kLambda) { - if (!isSelectedLambda(track1, posZ, posY, posX)) { + if (cfgPIDConfigs.cfgPIDParticle == UseLambda) { + if (!isSelectedLambda(track1, posZ, posY, posX, system, corType)) { continue; // Reject if called for Lambda but V0 is not lambda } @@ -1300,17 +1524,17 @@ struct CorrReso { } if (system == SameEvent) { - if (corType == kFT0C) { - registry.fill(HIST("Trig_hist_TPC_FT0C"), fSampleIndex, posZ, track1.pt(), resoMass, eventWeight * triggerWeight); - } else if (corType == kFT0A) { - registry.fill(HIST("Trig_hist_TPC_FT0A"), fSampleIndex, posZ, track1.pt(), resoMass, eventWeight * triggerWeight); + if (corType == IndexFT0C) { + histos.fill(HIST("Trig_hist_TPC_FT0C"), fSampleIndex, posZ, eventClass, track1.pt(), resoMass, eventWeight * triggerWeight); + } else if (corType == IndexFT0A) { + histos.fill(HIST("Trig_hist_TPC_FT0A"), fSampleIndex, posZ, eventClass, track1.pt(), resoMass, eventWeight * triggerWeight); } } std::size_t channelSize = 0; - if (corType == kFT0C) { + if (corType == IndexFT0C) { channelSize = ft0.channelC().size(); - } else if (corType == kFT0A) { + } else if (corType == IndexFT0A) { channelSize = ft0.channelA().size(); } else { LOGF(fatal, "Cor Index %d out of range", corType); @@ -1318,7 +1542,7 @@ struct CorrReso { for (std::size_t iCh = 0; iCh < channelSize; iCh++) { int chanelid = 0; float ampl = 0.; - getChannel(ft0, iCh, chanelid, ampl, corType, system); + getChannel(ft0, iCh, chanelid, ampl, corType); auto phi = getPhiFT0(chanelid, corType); auto eta = getEtaFT0(chanelid, corType); @@ -1327,32 +1551,24 @@ struct CorrReso { float deltaEta = track1.eta() - eta; // fill the right sparse and histograms if (system == SameEvent) { - if (corType == kFT0A) { - if (cfgQaCheck) { - registry.fill(HIST("Assoc_amp_same_TPC_FT0A"), chanelid, ampl); - registry.fill(HIST("deltaEta_deltaPhi_same_TPC_FT0A"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); - } - sameTpcFt0a->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), resoMass, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); - } else if (corType == kFT0C) { - if (cfgQaCheck) { - registry.fill(HIST("Assoc_amp_same_TPC_FT0C"), chanelid, ampl); - registry.fill(HIST("deltaEta_deltaPhi_same_TPC_FT0C"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); - } - sameTpcFt0c->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), resoMass, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + if (corType == IndexFT0A) { + histos.fill(HIST("Assoc_amp_same_TPC_FT0A"), chanelid, ampl); + histos.fill(HIST("deltaEta_deltaPhi_same_TPC_FT0A"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + sameTpcFt0a->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), resoMass, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } else if (corType == IndexFT0C) { + histos.fill(HIST("Assoc_amp_same_TPC_FT0C"), chanelid, ampl); + histos.fill(HIST("deltaEta_deltaPhi_same_TPC_FT0C"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + sameTpcFt0c->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), resoMass, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); } } else if (system == MixedEvent) { - if (corType == kFT0A) { - if (cfgQaCheck) { - registry.fill(HIST("Assoc_amp_mixed_TPC_FT0A"), chanelid, ampl); - registry.fill(HIST("deltaEta_deltaPhi_mixed_TPC_FT0A"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); - } - mixedTpcFt0a->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), resoMass, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); - } else if (corType == kFT0C) { - if (cfgQaCheck) { - registry.fill(HIST("Assoc_amp_mixed_TPC_FT0C"), chanelid, ampl); - registry.fill(HIST("deltaEta_deltaPhi_mixed_TPC_FT0C"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); - } - mixedTpcFt0c->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), resoMass, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + if (corType == IndexFT0A) { + histos.fill(HIST("Assoc_amp_mixed_TPC_FT0A"), chanelid, ampl); + histos.fill(HIST("deltaEta_deltaPhi_mixed_TPC_FT0A"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + mixedTpcFt0a->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), resoMass, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } else if (corType == IndexFT0C) { + histos.fill(HIST("Assoc_amp_mixed_TPC_FT0C"), chanelid, ampl); + histos.fill(HIST("deltaEta_deltaPhi_mixed_TPC_FT0C"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + mixedTpcFt0c->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), resoMass, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); } } } @@ -1361,8 +1577,8 @@ struct CorrReso { bool isGoodRun(int runNumber) { - for (const auto& ExcludedRun : cfgRunRemoveList.value) { - if (runNumber == ExcludedRun) { + for (const auto& excludedRun : cfgGeneral.cfgRunRemoveList.value) { + if (runNumber == excludedRun) { return false; } } @@ -1370,24 +1586,15 @@ struct CorrReso { return true; } - double massKaPlus = o2::constants::physics::MassKPlus; // same as MassKMinus double massLambda = o2::constants::physics::MassLambda; // same as MassLambda0 and MassLambda0Bar double massK0Short = o2::constants::physics::MassK0Short; // same as o2::constants::physics::MassK0 and o2::constants::physics::MassK0Bar - void processSameTpcFt0a(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&, aod::V0Datas const& V0s) + void processSameTpcFt0(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&, V0TrackCandidate const& V0s) { - registry.fill(HIST("hEventCount"), kFilteredEvents); - - if (cfgQaCheck) { + if (cfgGeneral.cfgQABasic) { eventSelectedIndividually(collision); } - if (!collision.sel8()) { - return; - } - - registry.fill(HIST("hEventCount"), kAfterSel8); - if (!eventRct(collision, true)) { return; } @@ -1395,12 +1602,14 @@ struct CorrReso { auto bc = collision.bc_as(); int currentRunNumber = bc.runNumber(); - if (!cfgRunRemoveList.value.empty()) { - if (!isGoodRun(currentRunNumber)) // Rejects runs if bad run number + if (!cfgGeneral.cfgRunRemoveList.value.empty()) { + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number return; + } } - if (cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), true)) { + auto cent = getCentrality(collision); + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), cent, true)) { return; } @@ -1408,41 +1617,56 @@ struct CorrReso { return; } + histos.fill(HIST("hEventCount"), HaveFT0Cut); + loadAlignParam(bc.timestamp()); loadGain(bc); loadCorrection(bc.timestamp()); - float eventWeight = 1.0f; - - registry.fill(HIST("zVtx"), collision.posZ()); - - registry.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin + float weightCent = 1.0f; - fillYield(collision, tracks); + histos.fill(HIST("Centrality"), cent); + if (!cfgEvSel.cfgSelCollByNch) { + if (!getCentralityWeight(weightCent, cent)) { + return; // or continue in a loop + } + histos.fill(HIST("CentralityWeighted"), cent, weightCent); + } - double multiplicity = static_cast(tracks.size()); + histos.fill(HIST("zVtx"), collision.posZ()); - if (cfgQaCheck) { - registry.fill(HIST("Nch"), multiplicity); + if (cfgGeneral.cfgQABasic) { + fillYield(collision, tracks); } - if (cfgStrictTrackCounter) { - trackCounter(tracks, multiplicity); - } + auto multiplicity = static_cast(tracks.size()); + histos.fill(HIST("Nch"), multiplicity); - if (cfgQaCheck) { - registry.fill(HIST("Nch_corrected"), multiplicity); + if (cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks, multiplicity); + histos.fill(HIST("Nch_corrected"), multiplicity); } - if (multiplicity > cfgMaxMultForCorrelations || multiplicity < cfgMinMultForCorrelations) { + if (cfgEvSel.cfgSelCollByNch && (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations)) { + return; + } + if (!cfgEvSel.cfgSelCollByNch && (cent > cfgEvSel.cfgMaxCentForCorrelations || cent < cfgEvSel.cfgMinCentForCorrelations)) { return; } + histos.fill(HIST("eventcount"), SameEvent); + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity : cent; + const auto& ft0 = collision.foundFT0(); - fillCorrelationsTPCFT0(V0s, ft0, collision.posZ(), collision.posY(), collision.posX(), SameEvent, kFT0A, eventWeight); + if (cfgGeneral.cfgAnalyzeTPCFT0A) { + fillCorrelationsTPCFT0(V0s, ft0, collision.posZ(), collision.posY(), collision.posX(), SameEvent, eventClass, IndexFT0A, weightCent); + } + if (cfgGeneral.cfgAnalyzeTPCFT0C) { + fillCorrelationsTPCFT0(V0s, ft0, collision.posZ(), collision.posY(), collision.posX(), SameEvent, eventClass, IndexFT0C, weightCent); + } } - PROCESS_SWITCH(CorrReso, processSameTpcFt0a, "Process same event for TPC-FT0 correlation", false); + PROCESS_SWITCH(CorrReso, processSameTpcFt0, "Process same event for TPC-FT0 correlation", false); - void processMixedTpcFt0a(FilteredCollisions const& collisions, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&, aod::V0Datas const& V0s) + void processMixedTpcFt0(FilteredCollisions const& collisions, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&, V0TrackCandidate const& V0s) { auto getTracksSize = [&tracks, this](FilteredCollisions::iterator const& collision) { auto associatedTracks = tracks.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); @@ -1455,11 +1679,11 @@ struct CorrReso { MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; auto tracksTuple = std::make_tuple(V0s, tracks); - Pair pairs{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto it = pairs.begin(); it != pairs.end(); it++) { auto& [collision1, v0s1, collision2, tracks2] = *it; - if (!collision1.sel8() || !collision2.sel8()) { + if (!collision1.sel8() || !collision2.sel8()) { // Doing this at the top of mixed events loop to reduce computation (This cut is also there inside eventSelected) continue; } @@ -1469,10 +1693,13 @@ struct CorrReso { auto tracks1 = tracks.sliceByCached(o2::aod::track::collisionId, collision1.globalIndex(), this->cache); - if (cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), false)) { + auto cent1 = getCentrality(collision1); + auto cent2 = getCentrality(collision2); + + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), cent1, false)) { continue; } - if (cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), false)) { + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), cent2, false)) { continue; } @@ -1480,11 +1707,9 @@ struct CorrReso { continue; } - registry.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin - auto bc = collision1.bc_as(); int currentRunNumber = bc.runNumber(); - if (!cfgRunRemoveList.value.empty()) { + if (!cfgGeneral.cfgRunRemoveList.value.empty()) { if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number continue; } @@ -1492,154 +1717,55 @@ struct CorrReso { loadAlignParam(bc.timestamp()); loadCorrection(bc.timestamp()); - float eventWeight = 1.0f; - double multiplicity = static_cast(tracks1.size()); + auto multiplicity1 = static_cast(tracks1.size()); + auto multiplicity2 = static_cast(tracks2.size()); - if (cfgStrictTrackCounter) { - trackCounter(tracks1, multiplicity); + if (cfgEvSel.cfgSelCollByNch && cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks1, multiplicity1); + if (cfgEvSel.cfgUseCol2ForMixedEvSel) { + nchCounter(tracks2, multiplicity2); + } } - if (multiplicity > cfgMaxMultForCorrelations || multiplicity < cfgMinMultForCorrelations) { + if (cfgEvSel.cfgSelCollByNch && (multiplicity1 > cfgEvSel.cfgMaxMultForCorrelations || multiplicity1 < cfgEvSel.cfgMinMultForCorrelations)) { continue; } - - const auto& ft0 = collision2.foundFT0(); - fillCorrelationsTPCFT0(v0s1, ft0, collision1.posZ(), collision1.posY(), collision1.posX(), MixedEvent, kFT0A, eventWeight); - } - } - PROCESS_SWITCH(CorrReso, processMixedTpcFt0a, "Process mixed events for TPC-FT0A correlation", false); - - void processSameTpcFt0c(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&, aod::V0Datas const& V0s) - { - registry.fill(HIST("hEventCount"), kFilteredEvents); - - if (cfgQaCheck) { - eventSelectedIndividually(collision); - } - - if (!collision.sel8()) { - return; - } - - registry.fill(HIST("hEventCount"), kAfterSel8); - - if (!eventRct(collision, true)) { - return; - } - - auto bc = collision.bc_as(); - int currentRunNumber = bc.runNumber(); - if (!cfgRunRemoveList.value.empty()) { - if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number - return; - } - } - - if (cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), true)) { - return; - } - if (!collision.has_foundFT0()) { - return; - } - loadAlignParam(bc.timestamp()); - loadGain(bc); - loadCorrection(bc.timestamp()); - - registry.fill(HIST("zVtx"), collision.posZ()); - - registry.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin - - const auto& ft0 = collision.foundFT0(); - - double multiplicity = static_cast(tracks.size()); - - if (cfgQaCheck) { - registry.fill(HIST("Nch"), multiplicity); - } - - if (cfgStrictTrackCounter) { - trackCounter(tracks, multiplicity); - } - - if (cfgQaCheck) { - registry.fill(HIST("Nch_corrected"), multiplicity); - } - - if (multiplicity > cfgMaxMultForCorrelations || multiplicity < cfgMinMultForCorrelations) { - return; - } - - fillCorrelationsTPCFT0(V0s, ft0, collision.posZ(), collision.posY(), collision.posX(), SameEvent, kFT0C, 1.0f); - } - PROCESS_SWITCH(CorrReso, processSameTpcFt0c, "Process same event for TPC-FT0C correlation", false); - - void processMixedTpcFt0c(FilteredCollisions const& collisions, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&, aod::V0Datas const& V0s) - { - - auto getTracksSize = [&tracks, this](FilteredCollisions::iterator const& collision) { - auto associatedTracks = tracks.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); - auto mult = associatedTracks.size(); - return mult; - }; - - using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; - - auto tracksTuple = std::make_tuple(V0s, tracks); - Pair pairs{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip - for (auto it = pairs.begin(); it != pairs.end(); it++) { - auto& [collision1, v0s1, collision2, tracks2] = *it; - if (!collision1.sel8() || !collision2.sel8()) { + if (cfgEvSel.cfgSelCollByNch && cfgEvSel.cfgUseCol2ForMixedEvSel && (multiplicity2 > cfgEvSel.cfgMaxMultForCorrelations || multiplicity2 < cfgEvSel.cfgMinMultForCorrelations)) { continue; } - - if (!eventRct(collision1, false) || !eventRct(collision2, false)) { + if (!cfgEvSel.cfgSelCollByNch && (cent1 > cfgEvSel.cfgMaxCentForCorrelations || cent1 < cfgEvSel.cfgMinCentForCorrelations)) { continue; } - - auto tracks1 = tracks.sliceByCached(o2::aod::track::collisionId, collision1.globalIndex(), this->cache); - - if (cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), false)) { + if (!cfgEvSel.cfgSelCollByNch && cfgEvSel.cfgUseCol2ForMixedEvSel && (cent2 > cfgEvSel.cfgMaxCentForCorrelations || cent2 < cfgEvSel.cfgMinCentForCorrelations)) { continue; } - if (cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), false)) { - continue; - } + histos.fill(HIST("eventcount"), MixedEvent); + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity1 : cent1; - if (!(collision1.has_foundFT0() && collision2.has_foundFT0())) { - continue; - } + float eventWeight = 1.0f; - registry.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin - auto bc = collision1.bc_as(); - int currentRunNumber = bc.runNumber(); - if (!cfgRunRemoveList.value.empty()) { - if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number - continue; + if (cfgEffWeights.cfgUseEventWeights) { + eventWeight = 1.0f / it.currentWindowNeighbours(); + } + float weightCent = 1.0f; + if (!cfgEvSel.cfgSelCollByNch) { + if (!getCentralityWeight(weightCent, cent1)) { + continue; // or continue in a loop } } - loadAlignParam(bc.timestamp()); - loadCorrection(bc.timestamp()); - float eventWeight = 1.0f; const auto& ft0 = collision2.foundFT0(); - double multiplicity = static_cast(tracks1.size()); - - if (cfgStrictTrackCounter) { - trackCounter(tracks1, multiplicity); + if (cfgGeneral.cfgAnalyzeTPCFT0A) { + fillCorrelationsTPCFT0(v0s1, ft0, collision1.posZ(), collision1.posY(), collision1.posX(), MixedEvent, eventClass, IndexFT0A, eventWeight * weightCent); } - - if (multiplicity > cfgMaxMultForCorrelations || multiplicity < cfgMinMultForCorrelations) { - continue; + if (cfgGeneral.cfgAnalyzeTPCFT0C) { + fillCorrelationsTPCFT0(v0s1, ft0, collision1.posZ(), collision1.posY(), collision1.posX(), MixedEvent, eventClass, IndexFT0C, eventWeight * weightCent); } - - fillCorrelationsTPCFT0(v0s1, ft0, collision1.posZ(), collision1.posY(), collision1.posX(), MixedEvent, kFT0C, eventWeight); } } - PROCESS_SWITCH(CorrReso, processMixedTpcFt0c, "Process mixed events for TPC-FT0C correlation", false); + PROCESS_SWITCH(CorrReso, processMixedTpcFt0, "Process mixed events for TPC-FT0 correlations", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/diHadronCor.cxx b/PWGCF/TwoParticleCorrelations/Tasks/diHadronCor.cxx index 54e9e909adf..61dfa8787c3 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/diHadronCor.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/diHadronCor.cxx @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -947,7 +948,7 @@ struct DiHadronCor { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1107,7 +1108,7 @@ struct DiHadronCor { using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1194,7 +1195,7 @@ struct DiHadronCor { using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip diff --git a/PWGCF/TwoParticleCorrelations/Tasks/etaDihadron.cxx b/PWGCF/TwoParticleCorrelations/Tasks/etaDihadron.cxx index ea041d44f09..24cf76f210a 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/etaDihadron.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/etaDihadron.cxx @@ -932,7 +932,7 @@ struct EtaDihadron { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1090,7 +1090,7 @@ struct EtaDihadron { using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1174,7 +1174,7 @@ struct EtaDihadron { using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip diff --git a/PWGCF/TwoParticleCorrelations/Tasks/flowDecorrelation.cxx b/PWGCF/TwoParticleCorrelations/Tasks/flowDecorrelation.cxx index 7ab6ca2f262..9a2251ed1f5 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/flowDecorrelation.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/flowDecorrelation.cxx @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -1192,7 +1193,7 @@ struct FlowDecorrelation { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1297,7 +1298,7 @@ struct FlowDecorrelation { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1399,7 +1400,7 @@ struct FlowDecorrelation { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; @@ -1551,7 +1552,7 @@ struct FlowDecorrelation { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1664,7 +1665,7 @@ struct FlowDecorrelation { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1837,7 +1838,7 @@ struct FlowDecorrelation { using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1952,7 +1953,7 @@ struct FlowDecorrelation { using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip diff --git a/PWGCF/TwoParticleCorrelations/Tasks/hParticleCorrelation.cxx b/PWGCF/TwoParticleCorrelations/Tasks/hParticleCorrelation.cxx new file mode 100644 index 00000000000..9370e407843 --- /dev/null +++ b/PWGCF/TwoParticleCorrelations/Tasks/hParticleCorrelation.cxx @@ -0,0 +1,6730 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file hParticleCorrelation.cxx +/// \brief Task for same- and mixed-event azimuthal angular correlations of trigger hadrons with charged hadrons, identified particles, and resonances +/// +/// \author Rahul Verma (rahul.verma@iitb.ac.in) :: Durgesh Bhatt (durgesh.bhatt@cern.ch) :: Sadhana Dash (sadhana@phy.iitb.ac.in) + +#include "PWGLF/DataModel/mcCentrality.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::constants::physics; // for constants + +namespace +{ +constexpr float ProtonMass = o2::constants::physics::MassProton; +constexpr float KaonMass = o2::constants::physics::MassKaonCharged; +constexpr float LambdaMass = o2::constants::physics::MassLambda0; +constexpr float XiMass = o2::constants::physics::MassXiMinus; +constexpr float OmegaMass = o2::constants::physics::MassOmegaMinus; +constexpr float Lambda1520Mass = o2::constants::physics::MassLambda1520; +constexpr float MinAbsCharge = 1e-3f; +constexpr float InvalidCentrality = -999.0f; +const std::array kProtonKaonMasses{ProtonMass, KaonMass}; +} // namespace + +namespace lambda1520_analysis +{ +static constexpr int PdgProton{2212}; +static constexpr int PdgKaon{321}; +static constexpr int PdgLambda0{3122}; +static constexpr int PdgXiMinus{3312}; +static constexpr int PdgOmegaMinus{3334}; + +enum CentEstimator : int { + kFT0M = 0, + kFT0A = 1, + kFT0C = 2 +}; + +} // namespace lambda1520_analysis + +namespace o2::aod +{ +namespace resonancecndt +{ +DECLARE_SOA_INDEX_COLUMN(Collision, collision); //! Candidate to collision +DECLARE_SOA_INDEX_COLUMN_FULL(PosTrack, posTrack, int, Tracks, "_Pos"); //! Positive track +DECLARE_SOA_INDEX_COLUMN_FULL(NegTrack, negTrack, int, Tracks, "_Neg"); //! Negative track + +// Candidate kinematics +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); + +DECLARE_SOA_COLUMN(Px, px, float); +DECLARE_SOA_COLUMN(Py, py, float); +DECLARE_SOA_COLUMN(Pz, pz, float); + +// Invariant-mass hypotheses +DECLARE_SOA_COLUMN(MPhi1020, mPhi1020, float); +DECLARE_SOA_COLUMN(MKStar892, mKStar892, float); +DECLARE_SOA_COLUMN(MKStar892Bar, mKStar892Bar, float); +DECLARE_SOA_COLUMN(MLambda1520, mLambda1520, float); +DECLARE_SOA_COLUMN(MLambda1520Bar, mLambda1520Bar, float); + +// Mass-region tags +DECLARE_SOA_COLUMN(Phi1020Tag, phi1020Tag, uint8_t); +DECLARE_SOA_COLUMN(KStar892Tag, kStar892Tag, uint8_t); +DECLARE_SOA_COLUMN(KStar892BarTag, kStar892BarTag, uint8_t); +DECLARE_SOA_COLUMN(Lambda1520Tag, lambda1520Tag, uint8_t); +DECLARE_SOA_COLUMN(Lambda1520BarTag, lambda1520BarTag, uint8_t); + +} // namespace resonancecndt + +DECLARE_SOA_TABLE(ResonanceCndts, "AOD", "RESONCNDTS", o2::soa::Index<>, + o2::aod::resonancecndt::CollisionId, + o2::aod::resonancecndt::PosTrackId, + o2::aod::resonancecndt::NegTrackId, + + o2::aod::resonancecndt::Pt, + o2::aod::resonancecndt::Eta, + o2::aod::resonancecndt::Phi, + + o2::aod::resonancecndt::Px, + o2::aod::resonancecndt::Py, + o2::aod::resonancecndt::Pz, + + o2::aod::resonancecndt::MPhi1020, + o2::aod::resonancecndt::MKStar892, + o2::aod::resonancecndt::MKStar892Bar, + o2::aod::resonancecndt::MLambda1520, + o2::aod::resonancecndt::MLambda1520Bar, + + o2::aod::resonancecndt::Phi1020Tag, + o2::aod::resonancecndt::KStar892Tag, + o2::aod::resonancecndt::KStar892BarTag, + o2::aod::resonancecndt::Lambda1520Tag, + o2::aod::resonancecndt::Lambda1520BarTag); + +//______________________________________________________________________________ +// Derived correlation tables +//______________________________________________________________________________ + +namespace hpcorrcollision +{ +DECLARE_SOA_COLUMN(Centrality, centrality, float); //! Value of the mixing estimator chosen in the same-event task +DECLARE_SOA_COLUMN(CorrMask, corrMask, uint64_t); //! Mixing pools this collision belongs to (CorrPresenceMask bits) +} // namespace hpcorrcollision + +DECLARE_SOA_TABLE(HPCorrColls, "AOD", "HPCORRCOLL", o2::soa::Index<>, + o2::aod::collision::PosZ, + hpcorrcollision::Centrality, + hpcorrcollision::CorrMask); + +//______________________________________________________________________________ +// Trigger tracks +namespace hpcorrtrigger +{ +DECLARE_SOA_INDEX_COLUMN(HPCorrColl, hpCorrColl); // o2-linter: disable=name/o2-column (Keep the derived collision-table relation) +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +} // namespace hpcorrtrigger + +DECLARE_SOA_TABLE(HPCorrTriggers, "AOD", "HPCORRTRIG", o2::soa::Index<>, + hpcorrtrigger::HPCorrCollId, + hpcorrtrigger::Pt, + hpcorrtrigger::Eta, + hpcorrtrigger::Phi); + +//______________________________________________________________________________ +// Associated particles: tracks, identified hadrons and resonance candidates +namespace hpcorrassoc +{ +DECLARE_SOA_INDEX_COLUMN(HPCorrColl, hpCorrColl); // o2-linter: disable=name/o2-column (Keep the derived collision-table relation) +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Sign, sign, int8_t); //! Tracks: +1 (h+, pi+, K+, p) / -1 (h-, pi-, K-, pbar). Resonances: 0 (species, incl. anti-K*0 / anti-Lambda(1520), is in UsedMask) +DECLARE_SOA_COLUMN(UsedMask, usedMask, uint64_t); //! CorrPresenceMask bits of every correlation this particle was used in: 0-7 tracks (h, pi, K, p x low/high pT), 18-47 resonances (species x pT x mass window) +} // namespace hpcorrassoc + +DECLARE_SOA_TABLE(HPCorrAssocs, "AOD", "HPCORRASSOC", o2::soa::Index<>, + hpcorrassoc::HPCorrCollId, + hpcorrassoc::Pt, + hpcorrassoc::Eta, + hpcorrassoc::Phi, + hpcorrassoc::Sign, + hpcorrassoc::UsedMask); +} // namespace o2::aod + +// Delta phi in [-pi/2, 3pi/2) +float computeDeltaPhi(float particlePhi, float leadPhi) +{ + return RecoDecay::constrainAngle(particlePhi - leadPhi, -o2::constants::math::PIHalf); +} + +template +float computeRapidity(const T& track, const float mass) +{ + const float energy = RecoDecay::e(track.px(), track.py(), track.pz(), mass); + return 0.5f * std::log((energy + track.pz()) / (energy - track.pz())); +} + +template +inline T getCfg(const auto& cfgLabelledArray, int32_t row, int32_t col) +{ + double val = cfgLabelledArray->get(row, col); // Always stored as double + + if constexpr (std::is_same_v) { + return val != 0.0; // will return anything nonzero as true + } else if constexpr (std::is_same_v) { + // cppcheck-suppress suspiciousFloatingPointCast + return static_cast(val); + } else if constexpr (std::is_same_v) { + return val; + } else if constexpr (std::is_same_v) { + return static_cast(val); + } else { + static_assert( + std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v, + "getCfg() only supports T = bool, float, double or int"); + } +} + +int binarySearchVector(const int64_t Key, const std::vector& List, int low, int high) // v2 : list by reference +{ + while (low <= high) { + int mid = low + (high - low) / 2; + if (Key == List[mid]) { + return mid; + } + + if (Key > List[mid]) { + low = mid + 1; // If Key is greater, ignore left half, update the low + } else { + high = mid - 1; + } // If Key is smaller, ignore right half, update the high + } + return -1; // Element is not present +} + +// do a fastest search in an sorted array. ==> Binary Search in an array. +template +bool checkTrackInList(const T& track, const std::vector& ParticleList) // v2 : list by reference +{ + return static_cast(binarySearchVector(track.globalIndex(), ParticleList, 0, ParticleList.size() - 1) != -1); +} + +enum CollisionRejectionTag { + kCollAccepted = 0, + + // Basic event selection + kCollRejectSel8, + kCollRejectVertexZ, + kCollRejectTriggerTVX, + + // TF / ROF borders + kCollRejectITSROFrameBorder, + kCollRejectTimeFrameBorder, + + // Vertex quality + kCollRejectVertexITSTPC, + kCollRejectGoodZvtxFT0vsPV, + kCollRejectVertexTOFmatched, + kCollRejectVertexTRDmatched, + kCollRejectGoodITSLayersAll, + + // Pileup / neighbouring collisions + kCollRejectSameBunchPileup, + kCollRejectCollInTimeRangeStandard, + kCollRejectCollInTimeRangeStrict, + kCollRejectCollInTimeRangeNarrow, + kCollRejectCollInRofStandard, + kCollRejectCollInRofStrict, + kCollRejectHighMultCollInPrevRof, + + // INEL classes + kCollRejectINELgt0, + kCollRejectINELgt1, + + // Multiplicity + kCollRejectMultiplicityLow, + kCollRejectMultiplicityHigh, + + // Centrality + kCollRejectCentralityLow, + kCollRejectCentralityHigh, + + // Occupancy + kCollRejectOccupancyLow, + kCollRejectOccupancyHigh, + + // Interaction rate + kCollRejectInteractionRateLow, + kCollRejectInteractionRateHigh, + + // RCT + kCollRejectRCT, + kCollRejectCorrelationRCT, + + kNCollisionRejectionTags +}; + +enum TrackRejectionTag { + kTrackAccepted = 0, + + // Kinematics + kTrackRejectPt, + kTrackRejectEta, + kTrackRejectMomentum, + kTrackRejectCharge, + + // Standard O2 track selections + kTrackRejectQualityTrack, + kTrackRejectQualityTrackITS, + kTrackRejectQualityTrackTPC, + kTrackRejectPrimaryTrack, + kTrackRejectInAcceptanceTrack, + kTrackRejectGlobalTrack, + kTrackRejectGlobalTrackWoDCA, + kTrackRejectGlobalTrackWoPtEta, + kTrackRejectGlobalTrackWoTPCCluster, + kTrackRejectGlobalTrackWoDCATPCCluster, + kTrackRejectPVContributor, + + // Detector presence + kTrackRejectITS, + kTrackRejectTPC, + kTrackRejectTOF, + kTrackRejectTRD, + + // TPC quality + kTrackRejectTPCNClsFound, + kTrackRejectTPCCrossedRows, + kTrackRejectTPCCrossedRowsOverFindable, + kTrackRejectTPCFoundOverFindable, + kTrackRejectTPCFractionShared, + kTrackRejectTPCChi2, + + // ITS quality + kTrackRejectITSNCls, + kTrackRejectITSNClsInnerBarrel, + kTrackRejectITSChi2, + + // DCA + kTrackRejectFixedDCAxy, + kTrackRejectFixedDCAz, + kTrackRejectPtDependentDCAxy, + kTrackRejectPtDependentDCAz, + + kNTrackRejectionTags +}; + +enum QAStageEnum { + kBeforeSelection = 0, + kAfterSelFunction, + kAfterSelection, + kNQAStages +}; + +static constexpr std::array QAStageDire = { + "BeforeSelection/", + "AfterSelFunction/", + "AfterSelection/"}; + +enum EventQABin { + kEventAllFiltered = 0, + kEventPassedSelCollision, + kEventPassedMinFilteredTracks, + kEventPassedMinSelectedTracks, + kNEventQABins +}; + +enum TrackQABin { + kTrackAllFiltered = 0, + kTrackPassedSelectionTrack, + kNTrackQABins +}; + +enum DataFrameQABin { + kDFWithZeroFilteredColls = 0, + kNDataFrameQABins +}; + +enum EventTypeEnum { + kSameEvent = 0, + kMixedEvent, + kNEventTypes +}; + +static constexpr std::array EventTypeDire = { + "SE/", + "ME/"}; + +enum PairTypeEnum { + kHH = 0, + kHId, + kHPhi, + kHKStar, + kHKStarBar, + kHLambda, + kHLambdaBar, + kPhiPhi, + kNPairTypes +}; + +static constexpr std::array PairTypeDire = { + "hh/", + "hIdentified/", + "hPhi/", + "hKStar/", + "hKStarBar/", + "hLambda/", + "hLambdaBar/", + "PhiPhi/"}; + +enum AnalysisTypeEnum { + kQA = 0, + kCorr, + kNAnalysisTypes +}; + +static constexpr std::array AnalysisTypeDire = { + "QA/", + "Corr/"}; + +enum CorrRoleEnum { + kTrigger = 0, + kAssocLowPt, + kAssocHighPt, + kNCorrRoles +}; + +static constexpr std::array CorrRoleDire = { + "Trigger/", + "AssocLowPt/", + "AssocHighPt/"}; + +enum PrimVtxMassRegionTag : uint8_t { + kMassOutside = 0, + kMassPeak = 1, + kMassLSB = 2, + kMassRSB = 3, + kMassNone = 4, + kNMassRegions = 5 +}; + +static constexpr std::array MassRegionDire = { + "Outside/", + "Peak/", + "LSB/", + "RSB/", + ""}; + +enum DauTypeEnum { + kPosDau = 0, + kNegDau, + kNDauTypes +}; + +static constexpr std::array DauTypeDire = { + "PosDau/", + "NegDau/"}; + +// template +// uint8_t getMassRegionTag(T mass, T lsbLow, T lsbUp, T peakLow, T peakUp, T rsbLow, T rsbUp) +uint8_t getMassRegionTag(float mass, float lsbLow, float lsbUp, float peakLow, float peakUp, float rsbLow, float rsbUp) +{ + if (lsbLow < mass && mass < lsbUp) { + return kMassLSB; + } + if (peakLow < mass && mass < peakUp) { + return kMassPeak; + } + if (rsbLow < mass && mass < rsbUp) { + return kMassRSB; + } + return kMassOutside; +} + +enum CorrCountTypeEnum { + kCountHH = 0, + kCountHPi, + kCountHKa, + kCountHPr, + kCountHPhi, + kCountHKStar, + kCountHKStarBar, + kCountHLambda, + kCountHLambdaBar, + kNCorrCountTypes +}; + +enum CorrCountPtEnum { + kCountLowPt = 0, + kCountHighPt, + kNCorrCountPt +}; + +static constexpr int NCorrCountChannels = static_cast(kNCorrCountTypes) * static_cast(kNCorrCountPt); +static constexpr std::array CorrCountTypeName = { + "hh", + "hPi", + "hKa", + "hPr", + "hPhi", + "hKStar", + "hKStarBar", + "hLambda", + "hLambdaBar"}; + +using CorrCountArray = std::array, kNCorrCountTypes>; +enum ResoCorrCountTypeEnum { + kResoCountHPhi = 0, + kResoCountHKStar, + kResoCountHKStarBar, + kResoCountHLambda, + kResoCountHLambdaBar, + kNResoCorrCountTypes +}; + +using ResoRegionCountArray = std::array, kNCorrCountPt>, kNResoCorrCountTypes>; + +struct MixingBinStatus { + uint64_t nCollisions = 0; // nCollisions = total selected collisions entering this mixing bin in this dataframe + CorrCountArray nCorrCollisions{}; // nCorrCollisions[hPhi][LowPt] = how many COLLISIONS in this mixing bin had >=1 hPhi-low correlation +}; + +enum PidEnum { + kPi = 0, + kKa, + kPr, + kEl, + kMu, + kDe, + kNPid +}; + +static constexpr std::array PidTypeDire = { + "Pi/", + "Ka/", + "Pr/", + "El/", + "Mu/", + "De/"}; + +enum CutSettingEnum { + kThrPforTOF = 0, + kIdCutTypeLowP, + kNSigmaTPCLowP, + kNSigmaTOFLowP, + kNSigmaRadLowP, + kIdCutTypeHighP, + kNSigmaTPCHighP, + kNSigmaTOFHighP, + kNSigmaRadHighP, + kDoVetoOthers, + kDoRelativeTPCcheck, + kDoRelativeTOFcheck, + kDoRelativeTPCTOFcheck, + kNCutSettings +}; + +enum VetoSettingEnum { + kDoVetoTPC = 0, + kDoVetoTOF, + kVetoTPC, + kVetoTOF, + kNVetoSettings +}; + +enum IdentificationType { + kTPCidentified = 0, + kTPCTOFidentified, + kUnidentified, + kNIdentificationTypes +}; + +enum TpcTofCutType { + kCircularCut = 0, + kRectangularCut, + kEllipsoidalCut, + kNCutTypes +}; + +enum MixingEstimatorEnum { + kMixCentFT0C = 0, + kMixCentFT0M, + kMixCentFT0A, + kMixCentFV0A, + kNMixingEstimators +}; + +enum CorrPresenceMask : uint64_t { + kMaskHHLowPt = 1ULL << 0, + kMaskHHHighPt = 1ULL << 1, + kMaskHPiLowPt = 1ULL << 2, + kMaskHPiHighPt = 1ULL << 3, + kMaskHKaLowPt = 1ULL << 4, + kMaskHKaHighPt = 1ULL << 5, + kMaskHPrLowPt = 1ULL << 6, + kMaskHPrHighPt = 1ULL << 7, + + kMaskHPhiLowPt = 1ULL << 8, + kMaskHPhiHighPt = 1ULL << 9, + kMaskHKStarLowPt = 1ULL << 10, + kMaskHKStarHighPt = 1ULL << 11, + kMaskHKStarBarLowPt = 1ULL << 12, + kMaskHKStarBarHighPt = 1ULL << 13, + kMaskHLambdaLowPt = 1ULL << 14, + kMaskHLambdaHighPt = 1ULL << 15, + kMaskHLambdaBarLowPt = 1ULL << 16, + kMaskHLambdaBarHighPt = 1ULL << 17, + + kMaskHPhiLowPtPeak = 1ULL << 18, + kMaskHPhiLowPtLSB = 1ULL << 19, + kMaskHPhiLowPtRSB = 1ULL << 20, + kMaskHPhiHighPtPeak = 1ULL << 21, + kMaskHPhiHighPtLSB = 1ULL << 22, + kMaskHPhiHighPtRSB = 1ULL << 23, + + kMaskHKStarLowPtPeak = 1ULL << 24, + kMaskHKStarLowPtLSB = 1ULL << 25, + kMaskHKStarLowPtRSB = 1ULL << 26, + kMaskHKStarHighPtPeak = 1ULL << 27, + kMaskHKStarHighPtLSB = 1ULL << 28, + kMaskHKStarHighPtRSB = 1ULL << 29, + + kMaskHKStarBarLowPtPeak = 1ULL << 30, + kMaskHKStarBarLowPtLSB = 1ULL << 31, + kMaskHKStarBarLowPtRSB = 1ULL << 32, + kMaskHKStarBarHighPtPeak = 1ULL << 33, + kMaskHKStarBarHighPtLSB = 1ULL << 34, + kMaskHKStarBarHighPtRSB = 1ULL << 35, + + kMaskHLambdaLowPtPeak = 1ULL << 36, + kMaskHLambdaLowPtLSB = 1ULL << 37, + kMaskHLambdaLowPtRSB = 1ULL << 38, + kMaskHLambdaHighPtPeak = 1ULL << 39, + kMaskHLambdaHighPtLSB = 1ULL << 40, + kMaskHLambdaHighPtRSB = 1ULL << 41, + + kMaskHLambdaBarLowPtPeak = 1ULL << 42, + kMaskHLambdaBarLowPtLSB = 1ULL << 43, + kMaskHLambdaBarLowPtRSB = 1ULL << 44, + kMaskHLambdaBarHighPtPeak = 1ULL << 45, + kMaskHLambdaBarHighPtLSB = 1ULL << 46, + kMaskHLambdaBarHighPtRSB = 1ULL << 47, + + kMaskAll = (1ULL << 48) - 1ULL +}; + +constexpr uint64_t getCorrMaskBit(int corrType, int ptType) +{ + return 1ULL << (corrType * kNCorrCountPt + ptType); +} + +constexpr uint64_t getResoRegionMaskBit(int resoType, int ptType, int massRegion) +{ + if (massRegion < kMassPeak || massRegion > kMassRSB) { + return 0ULL; + } + return 1ULL << (18 + resoType * 6 + ptType * 3 + (massRegion - kMassPeak)); +} + +// Bits 18-47 (kMaskHPhiLowPtPeak ... kMaskHLambdaBarHighPtRSB): resonance (species x pT x mass-window) pools +static constexpr uint64_t MaskResoRegions = static_cast(kMaskAll) & ~((1ULL << 18) - 1ULL); + +static constexpr std::array, kNPid> DefaultPIDcheckValues = {{ + {0.7, 0, 3.0, 3.0, 9.0, 0, 3.0, 3.0, 9.0, 0, 1, 0, 1}, // Pi + {0.5, 0, 3.0, 3.0, 9.0, 0, 3.0, 3.0, 9.0, 0, 1, 0, 1}, // Ka + {0.8, 0, 3.0, 3.0, 9.0, 0, 3.0, 3.0, 9.0, 0, 1, 0, 1}, // Pr + {0.4, 0, 3.0, 3.0, 9.0, 0, 3.0, 3.0, 9.0, 0, 1, 0, 1}, // El + {0.4, 0, 3.0, 3.0, 9.0, 0, 3.0, 3.0, 9.0, 0, 1, 0, 1}, // Mu + {0.4, 0, 3.0, 3.0, 9.0, 0, 3.0, 3.0, 9.0, 0, 1, 0, 1} // De +}}; + +static constexpr std::array, kNPid> DefaultPidVetoValues = {{ + {1, 1, 3.0, 3.0}, // Pi + {1, 1, 3.0, 3.0}, // Ka + {1, 1, 3.0, 3.0}, // Pr + {0, 0, 3.0, 3.0}, // El + {0, 0, 3.0, 3.0}, // Mu + {0, 0, 3.0, 3.0} // De +}}; + +//______________________________________________________________________________________________________________ +// Common functions independent of configurables used across the tasks + +template +bool relativeIdOthersTPC(const T& track) +{ + float distSpecies = 1.e6f; + + if constexpr (pidMode == kPi) { + distSpecies = std::fabs(track.tpcNSigmaPi()); + } else if constexpr (pidMode == kKa) { + distSpecies = std::fabs(track.tpcNSigmaKa()); + } else if constexpr (pidMode == kPr) { + distSpecies = std::fabs(track.tpcNSigmaPr()); + } else { + // FIX ME! : Relative check will be updated in future, currently implemented for pion, kaon and proton only + // else if constexpr (pidMode == kEl) { + // distSpecies = std::fabs(track.tpcNSigmaEl()); + // } else if constexpr (pidMode == kMu) { + // distSpecies = std::fabs(track.tpcNSigmaMu()); + // } else if constexpr (pidMode == kDe) { + // distSpecies = std::fabs(track.tpcNSigmaDe()); + // } + + return true; + } + + if constexpr (pidMode != kPi) { + if (std::fabs(track.tpcNSigmaPi()) <= distSpecies) { + return false; + } + } + + if constexpr (pidMode != kKa) { + if (std::fabs(track.tpcNSigmaKa()) <= distSpecies) { + return false; + } + } + + if constexpr (pidMode != kPr) { + if (std::fabs(track.tpcNSigmaPr()) <= distSpecies) { + return false; + } + } + + // FIX ME! : Relative check will be updated in future, currently implemented for pion, kaon and proton only + // if constexpr (pidMode != kEl) + // if (std::fabs(track.tpcNSigmaEl()) <= distSpecies) + // return false; + + // if constexpr (pidMode != kMu) + // if (std::fabs(track.tpcNSigmaMu()) <= distSpecies) + // return false; + + // if constexpr (pidMode != kDe) + // if (std::fabs(track.tpcNSigmaDe()) <= distSpecies) + // return false; + + return true; +} + +template +bool relativeIdOthersTOF(const T& track) +{ + float distSpecies = 1.e6f; + + if constexpr (pidMode == kPi) { + distSpecies = std::fabs(track.tofNSigmaPi()); + } else if constexpr (pidMode == kKa) { + distSpecies = std::fabs(track.tofNSigmaKa()); + } else if constexpr (pidMode == kPr) { + distSpecies = std::fabs(track.tofNSigmaPr()); + + // FIX ME! : Relative check will be updated in future, currently implemented for pion, kaon and proton only + // } else if constexpr (pidMode == kEl) { + // distSpecies = std::fabs(track.tofNSigmaEl()); + // } else if constexpr (pidMode == kMu) { + // distSpecies = std::fabs(track.tofNSigmaMu()); + // } else if constexpr (pidMode == kDe) { + // distSpecies = std::fabs(track.tofNSigmaDe()); + } else { + return true; + } + + if constexpr (pidMode != kPi) { + if (std::fabs(track.tofNSigmaPi()) <= distSpecies) { + return false; + } + } + + if constexpr (pidMode != kKa) { + if (std::fabs(track.tofNSigmaKa()) <= distSpecies) { + return false; + } + } + + if constexpr (pidMode != kPr) { + if (std::fabs(track.tofNSigmaPr()) <= distSpecies) { + return false; + } + } + // FIX ME! : Relative check will be updated in future, currently implemented for pion, kaon and proton only + // if constexpr (pidMode != kEl) + // if (std::fabs(track.tofNSigmaEl()) <= distSpecies) + // return false; + + // if constexpr (pidMode != kMu) + // if (std::fabs(track.tofNSigmaMu()) <= distSpecies) + // return false; + + // if constexpr (pidMode != kDe) + // if (std::fabs(track.tofNSigmaDe()) <= distSpecies) + // return false; + + return true; +} + +template +bool relativeIdOthersTPCTOF(const T& track) +{ + float distSpeciesSq = 1.e6f; + + if constexpr (pidMode == kPi) { + distSpeciesSq = track.tpcNSigmaPi() * track.tpcNSigmaPi() + track.tofNSigmaPi() * track.tofNSigmaPi(); + } else if constexpr (pidMode == kKa) { + distSpeciesSq = track.tpcNSigmaKa() * track.tpcNSigmaKa() + track.tofNSigmaKa() * track.tofNSigmaKa(); + } else if constexpr (pidMode == kPr) { + distSpeciesSq = track.tpcNSigmaPr() * track.tpcNSigmaPr() + track.tofNSigmaPr() * track.tofNSigmaPr(); + // FIX ME! : Relative check will be updated in future, currently implemented for pion, kaon and proton only + // } else if constexpr (pidMode == kEl) { + // distSpeciesSq = track.tpcNSigmaEl() * track.tpcNSigmaEl() + track.tofNSigmaEl() * track.tofNSigmaEl(); + // } else if constexpr (pidMode == kMu) { + // distSpeciesSq = track.tpcNSigmaMu() * track.tpcNSigmaMu() + track.tofNSigmaMu() * track.tofNSigmaMu(); + // } else if constexpr (pidMode == kDe) { + // distSpeciesSq = track.tpcNSigmaDe() * track.tpcNSigmaDe() + track.tofNSigmaDe() * track.tofNSigmaDe(); + } else { + return true; + } + + if constexpr (pidMode != kPi) { + const float distSq = track.tpcNSigmaPi() * track.tpcNSigmaPi() + track.tofNSigmaPi() * track.tofNSigmaPi(); + if (distSq <= distSpeciesSq) { + return false; + } + } + + if constexpr (pidMode != kKa) { + const float distSq = track.tpcNSigmaKa() * track.tpcNSigmaKa() + track.tofNSigmaKa() * track.tofNSigmaKa(); + if (distSq <= distSpeciesSq) { + return false; + } + } + + if constexpr (pidMode != kPr) { + const float distSq = track.tpcNSigmaPr() * track.tpcNSigmaPr() + track.tofNSigmaPr() * track.tofNSigmaPr(); + if (distSq <= distSpeciesSq) { + return false; + } + } + // FIX ME! : Relative check will be updated in future, currently implemented for pion, kaon and proton only + // if constexpr (pidMode != kEl) { + // const float distSq = track.tpcNSigmaEl() * track.tpcNSigmaEl() + track.tofNSigmaEl() * track.tofNSigmaEl(); + // if (distSq <= distSpeciesSq) + // return false; + // } + + // if constexpr (pidMode != kMu) { + // const float distSq = track.tpcNSigmaMu() * track.tpcNSigmaMu() + track.tofNSigmaMu() * track.tofNSigmaMu(); + // if (distSq <= distSpeciesSq) + // return false; + // } + + // if constexpr (pidMode != kDe) { + // const float distSq = track.tpcNSigmaDe() * track.tpcNSigmaDe() + track.tofNSigmaDe() * track.tofNSigmaDe(); + // if (distSq <= distSpeciesSq) + // return false; + // } + + return true; +} + +template +bool selIdRectangularCut(const T& track, const float& nSigmaTPC, const float& nSigmaTOF) +{ + if constexpr (pidMode == kPi) { + return std::fabs(track.tpcNSigmaPi()) < nSigmaTPC && std::fabs(track.tofNSigmaPi()) < nSigmaTOF; + } else if constexpr (pidMode == kKa) { + return std::fabs(track.tpcNSigmaKa()) < nSigmaTPC && std::fabs(track.tofNSigmaKa()) < nSigmaTOF; + } else if constexpr (pidMode == kPr) { + return std::fabs(track.tpcNSigmaPr()) < nSigmaTPC && std::fabs(track.tofNSigmaPr()) < nSigmaTOF; + } else if constexpr (pidMode == kEl) { + return std::fabs(track.tpcNSigmaEl()) < nSigmaTPC && std::fabs(track.tofNSigmaEl()) < nSigmaTOF; + } else if constexpr (pidMode == kMu) { + return std::fabs(track.tpcNSigmaMu()) < nSigmaTPC && std::fabs(track.tofNSigmaMu()) < nSigmaTOF; + } else if constexpr (pidMode == kDe) { + return std::fabs(track.tpcNSigmaDe()) < nSigmaTPC && std::fabs(track.tofNSigmaDe()) < nSigmaTOF; + } else { + return false; + } +} + +template +bool selIdEllipsoidalCut(const T& track, const float& nSigmaTPC, const float& nSigmaTOF) +{ + if constexpr (pidMode == kPi) { + float tpc = track.tpcNSigmaPi() / nSigmaTPC; + float tof = track.tofNSigmaPi() / nSigmaTOF; + return (tpc * tpc + tof * tof) < 1.0f; + } else if constexpr (pidMode == kKa) { + float tpc = track.tpcNSigmaKa() / nSigmaTPC; + float tof = track.tofNSigmaKa() / nSigmaTOF; + return (tpc * tpc + tof * tof) < 1.0f; + } else if constexpr (pidMode == kPr) { + float tpc = track.tpcNSigmaPr() / nSigmaTPC; + float tof = track.tofNSigmaPr() / nSigmaTOF; + return (tpc * tpc + tof * tof) < 1.0f; + } else if constexpr (pidMode == kEl) { + float tpc = track.tpcNSigmaEl() / nSigmaTPC; + float tof = track.tofNSigmaEl() / nSigmaTOF; + return (tpc * tpc + tof * tof) < 1.0f; + } else if constexpr (pidMode == kMu) { + float tpc = track.tpcNSigmaMu() / nSigmaTPC; + float tof = track.tofNSigmaMu() / nSigmaTOF; + return (tpc * tpc + tof * tof) < 1.0f; + } else if constexpr (pidMode == kDe) { + float tpc = track.tpcNSigmaDe() / nSigmaTPC; + float tof = track.tofNSigmaDe() / nSigmaTOF; + return (tpc * tpc + tof * tof) < 1.0f; + } else { + return false; + } +} + +// Circular cut : (tpcNSigma^2 + tofNSigma^2) < nSigmaSquaredRad ; for 3 sigma, nSigmaSquaredRad = 9.0 +template +constexpr bool selIdCircularCut(const T& track, const float& nSigmaSquaredRad) +{ + float tpc = 0.f; + float tof = 0.f; + + if constexpr (pidMode == kPi) { + tpc = track.tpcNSigmaPi(); + tof = track.tofNSigmaPi(); + } else if constexpr (pidMode == kKa) { + tpc = track.tpcNSigmaKa(); + tof = track.tofNSigmaKa(); + } else if constexpr (pidMode == kPr) { + tpc = track.tpcNSigmaPr(); + tof = track.tofNSigmaPr(); + } else if constexpr (pidMode == kEl) { + tpc = track.tpcNSigmaEl(); + tof = track.tofNSigmaEl(); + } else if constexpr (pidMode == kMu) { + tpc = track.tpcNSigmaMu(); + tof = track.tofNSigmaMu(); + } else if constexpr (pidMode == kDe) { + tpc = track.tpcNSigmaDe(); + tof = track.tofNSigmaDe(); + } else { + return false; // unknown pidMode + } + return (tpc * tpc + tof * tof) < nSigmaSquaredRad; +} + +template +bool applyVetoOthersTPC(const T& track, const std::array& doVetoTPC, const std::array& vetoTPC) +{ + if constexpr (pidMode != kPi) { + if (doVetoTPC[kPi] && std::fabs(track.tpcNSigmaPi()) < vetoTPC[kPi]) { + return false; + } + } + + if constexpr (pidMode != kKa) { + if (doVetoTPC[kKa] && std::fabs(track.tpcNSigmaKa()) < vetoTPC[kKa]) { + return false; + } + } + + if constexpr (pidMode != kPr) { + if (doVetoTPC[kPr] && std::fabs(track.tpcNSigmaPr()) < vetoTPC[kPr]) { + return false; + } + } + + if constexpr (pidMode != kEl) { + if (doVetoTPC[kEl] && std::fabs(track.tpcNSigmaEl()) < vetoTPC[kEl]) { + return false; + } + } + + if constexpr (pidMode != kMu) { + if (doVetoTPC[kMu] && std::fabs(track.tpcNSigmaMu()) < vetoTPC[kMu]) { + return false; + } + } + + if constexpr (pidMode != kDe) { + if (doVetoTPC[kDe] && std::fabs(track.tpcNSigmaDe()) < vetoTPC[kDe]) { + return false; + } + } + + return true; +} + +template +bool applyVetoOthersTOF(const T& track, const std::array& doVetoTOF, const std::array& vetoTOF) +{ + if constexpr (pidMode != kPi) { + if (doVetoTOF[kPi] && std::fabs(track.tofNSigmaPi()) < vetoTOF[kPi]) { + return false; + } + } + + if constexpr (pidMode != kKa) { + if (doVetoTOF[kKa] && std::fabs(track.tofNSigmaKa()) < vetoTOF[kKa]) { + return false; + } + } + + if constexpr (pidMode != kPr) { + if (doVetoTOF[kPr] && std::fabs(track.tofNSigmaPr()) < vetoTOF[kPr]) { + return false; + } + } + + if constexpr (pidMode != kEl) { + if (doVetoTOF[kEl] && std::fabs(track.tofNSigmaEl()) < vetoTOF[kEl]) { + return false; + } + } + + if constexpr (pidMode != kMu) { + if (doVetoTOF[kMu] && std::fabs(track.tofNSigmaMu()) < vetoTOF[kMu]) { + return false; + } + } + + if constexpr (pidMode != kDe) { + if (doVetoTOF[kDe] && std::fabs(track.tofNSigmaDe()) < vetoTOF[kDe]) { + return false; + } + } + + return true; +} + +// Common Helper functions are over. +//__________________________________________________________________________________________________________________________ + +//__________________________________________________________________________________________________________________________ +// Common Histograms booking functions - to be used inside +template +void addBasicTrackQAHistos(H& histReg, const std::string& basePath, const AxisSpec& axisP, const AxisSpec& axisPt, const AxisSpec& axisEta, const AxisSpec& axisPhi, const AxisSpec& axisDcaXY, const AxisSpec& axisDcaZ, const AxisSpec& axisSign) +{ + histReg.add((basePath + "P").c_str(), "p;p (GeV/c);Counts", kTH1F, {axisP}); + histReg.add((basePath + "Pt").c_str(), "p_{T};p_{T} (GeV/c);Counts", kTH1F, {axisPt}); + histReg.add((basePath + "Eta").c_str(), "#eta;#eta;Counts", kTH1F, {axisEta}); + histReg.add((basePath + "Phi").c_str(), "#varphi;#varphi;Counts", kTH1F, {axisPhi}); + histReg.add((basePath + "DCAxy").c_str(), "DCA_{xy};DCA_{xy} (cm);Counts", kTH1F, {axisDcaXY}); + histReg.add((basePath + "DCAz").c_str(), "DCA_{z};DCA_{z} (cm);Counts", kTH1F, {axisDcaZ}); + histReg.add((basePath + "Sign").c_str(), "Sign;Sign;Counts", kTH1F, {axisSign}); + histReg.add((basePath + "DCAxyVsPt").c_str(), "DCA_{xy} vs p_{T};p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {axisPt, axisDcaXY}); + histReg.add((basePath + "DCAzVsPt").c_str(), "DCA_{z} vs p_{T};p_{T} (GeV/c);DCA_{z} (cm)", kTH2F, {axisPt, axisDcaZ}); +} + +template +void addIdentifiedQAHistos(H& histReg, const std::string& basePath, const AxisSpec& axisP, const AxisSpec& axisRapidity, const AxisSpec& axisTPCSignal, const AxisSpec& axisTOFBeta, const AxisSpec& axisTPCNSigma, const AxisSpec& axisTOFNSigma, const AxisSpec& axisIdMethod) +{ + histReg.add((basePath + "Rapidity").c_str(), "Rapidity;y;Counts", HistType::kTH1F, {axisRapidity}); + histReg.add((basePath + "TPCSignalVsP").c_str(), "TPC signal vs p;p (GeV/c);TPC signal", HistType::kTH2F, {axisP, axisTPCSignal}); + histReg.add((basePath + "TOFBetaVsP").c_str(), "TOF #beta vs p;p (GeV/c);#beta", HistType::kTH2F, {axisP, axisTOFBeta}); + histReg.add((basePath + "TPCNSigmaVsP").c_str(), "TPC n#sigma vs p;p (GeV/c);n#sigma_{TPC}", HistType::kTH2F, {axisP, axisTPCNSigma}); + histReg.add((basePath + "TOFNSigmaVsP").c_str(), "TOF n#sigma vs p;p (GeV/c);n#sigma_{TOF}", HistType::kTH2F, {axisP, axisTOFNSigma}); + histReg.add((basePath + "TPCNSigmaVsTOFNSigma").c_str(), "TPC n#sigma vs TOF n#sigma;n#sigma_{TPC};n#sigma_{TOF}", HistType::kTH2F, {axisTPCNSigma, axisTOFNSigma}); + histReg.add((basePath + "IdMethodVsP").c_str(), "Identification method vs p;p (GeV/c);ID method", HistType::kTH2F, {axisP, axisIdMethod}); +} + +// Common Histograms booking functions are over +//__________________________________________________________________________________________________________________________ + +struct HParticleCorrelation1ResonanceProducer { + Produces resonanceCndts; + HistogramRegistry resonanceQAPlots{"resonanceQAPlots", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + + static constexpr float ResonancePidNSigmaMax = 3.5f; + + Configurable a0000EnableStruct{"a0000EnableStruct", true, "enables filling of declared registries"}; + struct : ConfigurableGroup { + Configurable c0001printDebugMessages{"c0001printDebugMessages", false, "Print debug messages"}; + } cfgDebug; + + struct : ConfigurableGroup { + Configurable c0002requireSel8{"c0002requireSel8", true, "Require sel8 event selection"}; + Configurable c0003cutZvertex{"c0003cutZvertex", 10.0f, "Maximum |z_{vtx}| (cm)"}; + } cfgEvent; + + struct : ConfigurableGroup { + Configurable c0004ptMin{"c0004ptMin", 0.15f, "Minimum track pT (GeV/c)"}; + Configurable c0005ptMax{"c0005ptMax", 100.0f, "Maximum track pT (GeV/c)"}; + Configurable c0006etaMax{"c0006etaMax", 0.8f, "Maximum |eta|"}; + + Configurable c0007useFixedDCAxy{"c0007useFixedDCAxy", false, "Apply fixed DCAxy cut"}; + Configurable c0008dcaXYMax{"c0008dcaXYMax", 0.1f, "Maximum |DCAxy| (cm)"}; + + Configurable c0009useFixedDCAz{"c0009useFixedDCAz", false, "Apply fixed DCAz cut"}; + Configurable c0010dcaZMax{"c0010dcaZMax", 0.2f, "Maximum |DCAz| (cm)"}; + } cfgTrackCuts; + + struct : ConfigurableGroup { + Configurable c0011doPhi1020{"c0011doPhi1020", true, "Build Phi(1020) candidates"}; + Configurable c0012doKStar892{"c0012doKStar892", true, "Build K*(892)0 candidates"}; + Configurable c0013doKStar892Bar{"c0013doKStar892Bar", true, "Build anti-K*(892)0 candidates"}; + Configurable c0014doLambda1520{"c0014doLambda1520", true, "Build Lambda(1520) candidates"}; + Configurable c0015doLambda1520Bar{"c0015doLambda1520Bar", true, "Build anti-Lambda(1520) candidates"}; + } cfgResonances; + + struct : ConfigurableGroup { + Configurable c0016phi1020PeakLow{"c0016phi1020PeakLow", 1.013f, "Phi(1020) peak lower mass"}; + Configurable c0017phi1020PeakUp{"c0017phi1020PeakUp", 1.026f, "Phi(1020) peak upper mass"}; + Configurable c0018phi1020LSBLow{"c0018phi1020LSBLow", 0.995f, "Phi(1020) LSB lower mass"}; + Configurable c0019phi1020LSBUp{"c0019phi1020LSBUp", 1.005f, "Phi(1020) LSB upper mass"}; + Configurable c0020phi1020RSBLow{"c0020phi1020RSBLow", 1.040f, "Phi(1020) RSB lower mass"}; + Configurable c0021phi1020RSBUp{"c0021phi1020RSBUp", 1.060f, "Phi(1020) RSB upper mass"}; + } cfgPhi1020Mass; + + struct : ConfigurableGroup { + Configurable c0022kstar892PeakLow{"c0022kstar892PeakLow", 0.846f, "K*(892)0 peak lower mass"}; + Configurable c0023kstar892PeakUp{"c0023kstar892PeakUp", 0.946f, "K*(892)0 peak upper mass"}; + Configurable c0024kstar892LSBLow{"c0024kstar892LSBLow", 0.700f, "K*(892)0 LSB lower mass"}; + Configurable c0025kstar892LSBUp{"c0025kstar892LSBUp", 0.780f, "K*(892)0 LSB upper mass"}; + Configurable c0026kstar892RSBLow{"c0026kstar892RSBLow", 1.010f, "K*(892)0 RSB lower mass"}; + Configurable c0027kstar892RSBUp{"c0027kstar892RSBUp", 1.090f, "K*(892)0 RSB upper mass"}; + } cfgKstar892Mass; + + struct : ConfigurableGroup { + Configurable c0028lambda1520PeakLow{"c0028lambda1520PeakLow", 1.500f, "Lambda(1520) peak lower mass"}; + Configurable c0029lambda1520PeakUp{"c0029lambda1520PeakUp", 1.540f, "Lambda(1520) peak upper mass"}; + Configurable c0030lambda1520LSBLow{"c0030lambda1520LSBLow", 1.440f, "Lambda(1520) LSB lower mass"}; + Configurable c0031lambda1520LSBUp{"c0031lambda1520LSBUp", 1.480f, "Lambda(1520) LSB upper mass"}; + Configurable c0032lambda1520RSBLow{"c0032lambda1520RSBLow", 1.560f, "Lambda(1520) RSB lower mass"}; + Configurable c0033lambda1520RSBUp{"c0033lambda1520RSBUp", 1.600f, "Lambda(1520) RSB upper mass"}; + } cfgLambda1520Mass; + + void init(InitContext const&) + { + if (!a0000EnableStruct) { + return; + } + const AxisSpec axisPhi1020Mass = {300, 0.98f, 1.08f, "m_{K^{+}K^{-}} (GeV/#it{c}^{2})"}; + const AxisSpec axisKStar892Mass = {300, 0.65f, 1.15f, "m_{K#pi} (GeV/#it{c}^{2})"}; + const AxisSpec axisLambda1520Mass = {300, 1.40f, 1.65f, "m_{pK} (GeV/#it{c}^{2})"}; + + resonanceQAPlots.add("mPhi1020", "#phi(1020);m_{K^{+}K^{-}} (GeV/#it{c}^{2});Counts", kTH1F, {axisPhi1020Mass}); + resonanceQAPlots.add("mKStar892", "K^{*}(892)^{0};m_{K^{+}#pi^{-}} (GeV/#it{c}^{2});Counts", kTH1F, {axisKStar892Mass}); + resonanceQAPlots.add("mKStar892Bar", "#bar{K}^{*}(892)^{0};m_{#pi^{+}K^{-}} (GeV/#it{c}^{2});Counts", kTH1F, {axisKStar892Mass}); + resonanceQAPlots.add("mLambda1520", "#Lambda(1520);m_{pK^{-}} (GeV/#it{c}^{2});Counts", kTH1F, {axisLambda1520Mass}); + resonanceQAPlots.add("mLambda1520Bar", "#bar{#Lambda}(1520);m_{K^{+}#bar{p}} (GeV/#it{c}^{2});Counts", kTH1F, {axisLambda1520Mass}); + } + + template + bool selPion(const T& track) + { + if (std::abs(track.tpcNSigmaPi()) < ResonancePidNSigmaMax) { + return true; + } + if (track.hasTOF()) { + if (std::abs(track.tofNSigmaPi()) < ResonancePidNSigmaMax) { + return true; + } + } + return false; + } + + template + bool selKaon(const T& track) + { + if (std::abs(track.tpcNSigmaKa()) < ResonancePidNSigmaMax) { + return true; + } + if (track.hasTOF()) { + if (std::abs(track.tofNSigmaKa()) < ResonancePidNSigmaMax) { + return true; + } + } + return false; + } + + template + bool selProton(const T& track) + { + if (std::abs(track.tpcNSigmaPr()) < ResonancePidNSigmaMax) { + return true; + } + if (track.hasTOF()) { + if (std::abs(track.tofNSigmaPr()) < ResonancePidNSigmaMax) { + return true; + } + } + return false; + } + + // Event Filter + Filter eventFilter = (!cfgEvent.c0002requireSel8) || (o2::aod::evsel::sel8 == true); + Filter posZFilter = (nabs(o2::aod::collision::posZ) < cfgEvent.c0003cutZvertex); + + // Track Filter + Filter ptFilter = (o2::aod::track::pt > cfgTrackCuts.c0004ptMin) && (o2::aod::track::pt < cfgTrackCuts.c0005ptMax); + Filter etaFilter = (nabs(o2::aod::track::eta) < cfgTrackCuts.c0006etaMax); + Filter dcaFilter = ((!cfgTrackCuts.c0007useFixedDCAxy) || (nabs(o2::aod::track::dcaXY) < cfgTrackCuts.c0008dcaXYMax)) && + ((!cfgTrackCuts.c0009useFixedDCAz) || (nabs(o2::aod::track::dcaZ) < cfgTrackCuts.c0010dcaZMax)); + using MyFilteredCollisions = soa::Filtered>; + using MyFilteredTracks = soa::Filtered>; + + Preslice tracksPerCollisionPreslice = o2::aod::track::collisionId; + SliceCache cache; + Partition posTracks = aod::track::signed1Pt > 0.0f; + Partition negTracks = aod::track::signed1Pt < 0.0f; + + int dfNumber = 0; + // void processNothing(aod::HPCorrColls const&) + void processNothing(aod::Origins const& origins) + { + if (cfgDebug.c0001printDebugMessages) { + LOG(info) << "DEBUG :: Process Nothing :: df_" << dfNumber << " :: origins = " << origins.size(); + } + // Intentionally empty. + // Keeps the task alive when running purely on derived data. + } + PROCESS_SWITCH(HParticleCorrelation1ResonanceProducer, processNothing, "Dummy process for derived-data analysis", true); + + void processSameEvent(MyFilteredCollisions const& collisions, MyFilteredTracks const& fullTracks, o2::aod::Origins const& /*Origins*/) + { + dfNumber++; + if (cfgDebug.c0001printDebugMessages) { + LOG(info) << "DEBUG :: df_" << dfNumber << " :: SE :: collisions = " << collisions.size() << " :: fullTracks = " << fullTracks.size(); + } + + float ePosPi = 0.0f; + float ePosKa = 0.0f; + float ePosPr = 0.0f; + + float eNegPi = 0.0f; + float eNegKa = 0.0f; + float eNegPr = 0.0f; + + float pt = 0.0f; + float eta = 0.0f; + float phi = 0.0f; + float px = 0.0f; + float py = 0.0f; + float pz = 0.0f; + float p = 0.0f; + + float mPhi1020 = -1.0f; + float mKStar892 = -1.0f; + float mKStar892Bar = -1.0f; + float mLambda1520 = -1.0f; + float mLambda1520Bar = -1.0f; + + uint8_t phi1020Tag = kMassOutside; + uint8_t kStar892Tag = kMassOutside; + uint8_t kStar892BarTag = kMassOutside; + uint8_t lambda1520Tag = kMassOutside; + uint8_t lambda1520BarTag = kMassOutside; + + bool posIsPi = false, posIsKa = false, posIsPr = false, negIsPi = false, negIsKa = false, negIsPr = false; + bool fillTable = false; + + for (const auto& collision : collisions) { + auto posTracksPerColl = posTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + auto negTracksPerColl = negTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + + for (const auto& posTrack : posTracksPerColl) { + posIsPi = selPion(posTrack); + posIsKa = selKaon(posTrack); + posIsPr = selProton(posTrack); + if (!(posIsPi || posIsKa || posIsPr)) { + continue; + } + // Precompute energies for fast speed + ePosPi = RecoDecay::e(posTrack.px(), posTrack.py(), posTrack.pz(), MassPiPlus); + ePosKa = RecoDecay::e(posTrack.px(), posTrack.py(), posTrack.pz(), MassKPlus); + ePosPr = RecoDecay::e(posTrack.px(), posTrack.py(), posTrack.pz(), MassProton); + for (const auto& negTrack : negTracksPerColl) { + negIsPi = selPion(negTrack); + negIsKa = selKaon(negTrack); + negIsPr = selProton(negTrack); + if (!(negIsPi || negIsKa || negIsPr)) { + continue; + } + + eNegPi = RecoDecay::e(negTrack.px(), negTrack.py(), negTrack.pz(), MassPiPlus); + eNegKa = RecoDecay::e(negTrack.px(), negTrack.py(), negTrack.pz(), MassKPlus); + eNegPr = RecoDecay::e(negTrack.px(), negTrack.py(), negTrack.pz(), MassProton); + + px = posTrack.px() + negTrack.px(); + py = posTrack.py() + negTrack.py(); + pz = posTrack.pz() + negTrack.pz(); + p = RecoDecay::p(px, py, pz); // definition = std::sqrt(px*px+py*py+pz*pz); + + fillTable = false; + + mPhi1020 = -1.0f; + mKStar892 = -1.0f; + mKStar892Bar = -1.0f; + mLambda1520 = -1.0f; + mLambda1520Bar = -1.0f; + + phi1020Tag = kMassOutside; + kStar892Tag = kMassOutside; + kStar892BarTag = kMassOutside; + lambda1520Tag = kMassOutside; + lambda1520BarTag = kMassOutside; + + // phi(1020) -> K+ + K- + if (cfgResonances.c0011doPhi1020 && posIsKa && negIsKa) { + mPhi1020 = RecoDecay::m(p, ePosKa + eNegKa); + phi1020Tag = getMassRegionTag(mPhi1020, cfgPhi1020Mass.c0018phi1020LSBLow, cfgPhi1020Mass.c0019phi1020LSBUp, cfgPhi1020Mass.c0016phi1020PeakLow, cfgPhi1020Mass.c0017phi1020PeakUp, cfgPhi1020Mass.c0020phi1020RSBLow, cfgPhi1020Mass.c0021phi1020RSBUp); + if (phi1020Tag != kMassOutside) { + fillTable = true; + } + resonanceQAPlots.fill(HIST("mPhi1020"), mPhi1020); + } + + // K(892)* -> K+ + pi- + if (cfgResonances.c0012doKStar892 && posIsKa && negIsPi) { + mKStar892 = RecoDecay::m(p, ePosKa + eNegPi); + kStar892Tag = getMassRegionTag(mKStar892, cfgKstar892Mass.c0024kstar892LSBLow, cfgKstar892Mass.c0025kstar892LSBUp, cfgKstar892Mass.c0022kstar892PeakLow, cfgKstar892Mass.c0023kstar892PeakUp, cfgKstar892Mass.c0026kstar892RSBLow, cfgKstar892Mass.c0027kstar892RSBUp); + if (kStar892Tag != kMassOutside) { + fillTable = true; + } + resonanceQAPlots.fill(HIST("mKStar892"), mKStar892); + } + + // K(892)*Bar -> K- + pi+ + if (cfgResonances.c0013doKStar892Bar && posIsPi && negIsKa) { + mKStar892Bar = RecoDecay::m(p, ePosPi + eNegKa); + kStar892BarTag = getMassRegionTag(mKStar892Bar, cfgKstar892Mass.c0024kstar892LSBLow, cfgKstar892Mass.c0025kstar892LSBUp, cfgKstar892Mass.c0022kstar892PeakLow, cfgKstar892Mass.c0023kstar892PeakUp, cfgKstar892Mass.c0026kstar892RSBLow, cfgKstar892Mass.c0027kstar892RSBUp); + if (kStar892BarTag != kMassOutside) { + fillTable = true; + } + resonanceQAPlots.fill(HIST("mKStar892Bar"), mKStar892Bar); + } + + // Λ(1520) -> P+ + Ka- + if (cfgResonances.c0014doLambda1520 && posIsPr && negIsKa) { + mLambda1520 = RecoDecay::m(p, ePosPr + eNegKa); + lambda1520Tag = getMassRegionTag(mLambda1520, cfgLambda1520Mass.c0030lambda1520LSBLow, cfgLambda1520Mass.c0031lambda1520LSBUp, cfgLambda1520Mass.c0028lambda1520PeakLow, cfgLambda1520Mass.c0029lambda1520PeakUp, cfgLambda1520Mass.c0032lambda1520RSBLow, cfgLambda1520Mass.c0033lambda1520RSBUp); + if (lambda1520Tag != kMassOutside) { + fillTable = true; + } + resonanceQAPlots.fill(HIST("mLambda1520"), mLambda1520); + } + + // Λ(1520)Bar -> PBar + Ka+ + if (cfgResonances.c0015doLambda1520Bar && posIsKa && negIsPr) { + mLambda1520Bar = RecoDecay::m(p, ePosKa + eNegPr); + lambda1520BarTag = getMassRegionTag(mLambda1520Bar, cfgLambda1520Mass.c0030lambda1520LSBLow, cfgLambda1520Mass.c0031lambda1520LSBUp, cfgLambda1520Mass.c0028lambda1520PeakLow, cfgLambda1520Mass.c0029lambda1520PeakUp, cfgLambda1520Mass.c0032lambda1520RSBLow, cfgLambda1520Mass.c0033lambda1520RSBUp); + if (lambda1520BarTag != kMassOutside) { + fillTable = true; + } + resonanceQAPlots.fill(HIST("mLambda1520Bar"), mLambda1520Bar); + } + + if (!fillTable) { + continue; + } + pt = RecoDecay::pt(px, py); // definition = std::sqrt(px*px+py*py); + eta = RecoDecay::eta(std::array{px, py, pz}); // definition = 0.5 * std::log((p + pz) / (p - pz + 1e-12)); // 1e-12 protect divide-by-zero + phi = RecoDecay::phi(px, py); // definition = std::atan2(py, px); + + resonanceCndts(collision.globalIndex(), posTrack.globalIndex(), negTrack.globalIndex(), + pt, eta, phi, px, py, pz, + mPhi1020, mKStar892, mKStar892Bar, mLambda1520, mLambda1520Bar, + phi1020Tag, kStar892Tag, kStar892BarTag, lambda1520Tag, lambda1520BarTag); + } // Neg Tracks + } // Pos Tracks + } // collision Loop is over + } + PROCESS_SWITCH(HParticleCorrelation1ResonanceProducer, processSameEvent, "Process Same event", true); +}; + +struct HParticleCorrelation2SameEvent { + + Produces derivedCollisions; + Produces derivedTriggers; + Produces derivedAssocs; + + // Hisogram redistry: + HistogramRegistry seEventQA{"seEventQA", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry seTrackQA{"seTrackQA", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hhCorrelation{"hhCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hIdCorrelation{"hIdCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hPhiCorrelation{"hPhiCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hKStarCorrelation{"hKStarCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hKStarBarCorrelation{"hKStarBarCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hLambdaCorrelation{"hLambdaCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hLambdaBarCorrelation{"hLambdaBarCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry resonanceMassSpectra{"resonanceMassSpectra", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry mixingQA{"mixingQA", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + + Configurable a0000EnableStruct{"a0000EnableStruct", true, "enables filling of declared registries"}; + + struct : ConfigurableGroup { + Configurable c1001printDebugMessages{"c1001printDebugMessages", false, "Print debug messages"}; + } cfgDebug; + + struct : ConfigurableGroup { + // Basic event selection + Configurable c1002cutZvertex{"c1002cutZvertex", 10.f, "Maximum |z_{vtx}| (cm)"}; // Imp : All + Configurable c1003requireSel8{"c1003requireSel8", true, "Require sel8 event selection"}; // Imp : All + Configurable c1004requireTriggerTVX{"c1004requireTriggerTVX", false, "Require kIsTriggerTVX"}; + + Configurable c1005minNFilteredTracks{"c1005minNFilteredTracks", 3, "Minimum number of filtered tracks required per collision"}; + Configurable c1006minNSelectedTracks{"c1006minNSelectedTracks", 3, "Minimum number of selected tracks required per collision"}; + + // Time-frame / ROF border selections + Configurable c1007requireNoITSROFrameBorder{"c1007requireNoITSROFrameBorder", false, "Require kNoITSROFrameBorder"}; + Configurable c1008requireNoTimeFrameBorder{"c1008requireNoTimeFrameBorder", false, "Require kNoTimeFrameBorder"}; + + // Vertex quality + Configurable c1009requireVertexITSTPC{"c1009requireVertexITSTPC", true, "Require kIsVertexITSTPC"}; // Imp : Light Ions + pp + Configurable c1010requireGoodZvtxFT0vsPV{"c1010requireGoodZvtxFT0vsPV", false, "Require kIsGoodZvtxFT0vsPV"}; // Imp : Light Ions + Configurable c1011requireVertexTOFmatched{"c1011requireVertexTOFmatched", false, "Require kIsVertexTOFmatched"}; + Configurable c1012requireVertexTRDmatched{"c1012requireVertexTRDmatched", false, "Require kIsVertexTRDmatched"}; + Configurable c1013requireGoodITSLayersAll{"c1013requireGoodITSLayersAll", false, "Require kIsGoodITSLayersAll"}; // Imp : Light Ions + + // Pileup / neighbouring-collision rejection + Configurable c1014requireNoSameBunchPileup{"c1014requireNoSameBunchPileup", true, "Require kNoSameBunchPileup"}; // Imp : Light Ions + pp + Configurable c1015requireNoCollInTimeRangeStandard{"c1015requireNoCollInTimeRangeStandard", false, "Require kNoCollInTimeRangeStandard"}; + Configurable c1016requireNoCollInTimeRangeStrict{"c1016requireNoCollInTimeRangeStrict", false, "Require kNoCollInTimeRangeStrict"}; + Configurable c1017requireNoCollInTimeRangeNarrow{"c1017requireNoCollInTimeRangeNarrow", false, "Require kNoCollInTimeRangeNarrow"}; + Configurable c1018requireNoCollInRofStandard{"c1018requireNoCollInRofStandard", false, "Require kNoCollInRofStandard"}; + Configurable c1019requireNoCollInRofStrict{"c1019requireNoCollInRofStrict", false, "Require kNoCollInRofStrict"}; + Configurable c1020requireNoHighMultCollInPrevRof{"c1020requireNoHighMultCollInPrevRof", false, "Require kNoHighMultCollInPrevRof"}; + + // INEL event classes + Configurable c1021requireINELgt0{"c1021requireINELgt0", false, "Require INEL > 0"}; + Configurable c1022requireINELgt1{"c1022requireINELgt1", false, "Require INEL > 1"}; + + // Multiplicity selection + Configurable c1023useMultiplicitySelection{"c1023useMultiplicitySelection", false, "Apply multiplicity selection"}; + Configurable c1024multiplicityEstimator{"c1024multiplicityEstimator", 0, "0: multNTracksPV, 1: numContrib, 2: multFT0C, 3: multFT0M"}; + Configurable c1025minMultiplicity{"c1025minMultiplicity", 0.f, "Minimum multiplicity"}; + Configurable c1026maxMultiplicity{"c1026maxMultiplicity", 1.e9f, "Maximum multiplicity"}; + + // Centrality selection + Configurable c1027useCentralitySelection{"c1027useCentralitySelection", false, "Apply centrality selection"}; + Configurable c1028centralityEstimator{"c1028centralityEstimator", 0, "0: centFT0C, 1: centFT0M, 2: centFT0A, 3: centFV0A"}; + Configurable c1029minCentrality{"c1029minCentrality", 0.f, "Minimum centrality percentile"}; + Configurable c1030maxCentrality{"c1030maxCentrality", 100.f, "Maximum centrality percentile"}; + + // Occupancy selection + Configurable c1031useOccupancySelection{"c1031useOccupancySelection", false, "Apply event occupancy selection"}; + Configurable c1032useFT0CbasedOccupancy{"c1032useFT0CbasedOccupancy", false, "Use FT0C occupancy instead of track occupancy"}; + Configurable c1033minOccupancy{"c1033minOccupancy", -1.f, "Minimum occupancy"}; + Configurable c1034maxOccupancy{"c1034maxOccupancy", 1.e9f, "Maximum occupancy"}; + + // Interaction-rate selection + Configurable c1035useInteractionRateSelection{"c1035useInteractionRateSelection", false, "Apply interaction-rate selection"}; + Configurable c1036minInteractionRate{"c1036minInteractionRate", -1.f, "Minimum interaction rate"}; + Configurable c1037maxInteractionRate{"c1037maxInteractionRate", 1.e9f, "Maximum interaction rate"}; + + // RCT / detector-quality selection + Configurable c1038requireRCTFlagChecker{"c1038requireRCTFlagChecker", false, "Apply Run Condition Table event-quality selection"}; + Configurable c1039requireCorrelationAnalysisRCTFlagChecker{"c1039requireCorrelationAnalysisRCTFlagChecker", false, "Apply correlation-analysis RCT selection"}; + Configurable c1040rctFlagCheckerLabel{"c1040rctFlagCheckerLabel", "CBT_muon_global", "RCT flag checker label"}; + Configurable c1041rctCheckZDC{"c1041rctCheckZDC", false, "Include ZDC in RCT detector-quality check"}; + Configurable c1042rctTreatLimitedAcceptanceAsBad{"c1042rctTreatLimitedAcceptanceAsBad", false, "Treat limited detector acceptance as bad"}; + } cfgEvent; + + struct : ConfigurableGroup { + // Kinematics + Configurable c1043ptMin{"c1043ptMin", 0.2f, "Minimum track pT (GeV/c)"}; + Configurable c1044ptMax{"c1044ptMax", 1.e10f, "Maximum track pT (GeV/c)"}; + Configurable c1045etaMax{"c1045etaMax", 0.8f, "Maximum |eta|"}; + + // Standard O2 track-selection flags + Configurable c1046requireGlobalTrack{"c1046requireGlobalTrack", false, "Require isGlobalTrack()"}; + Configurable c1047requireGlobalTrackWoDCA{"c1047requireGlobalTrackWoDCA", false, "Require isGlobalTrackWoDCA()"}; + Configurable c1048requirePVContributor{"c1048requirePVContributor", false, "Require track to be a PV contributor"}; + + // Detector matching / presence + Configurable c1049requireITS{"c1049requireITS", false, "Require ITS information"}; + Configurable c1050requireTPC{"c1050requireTPC", false, "Require TPC information"}; + Configurable c1051requireTOF{"c1051requireTOF", false, "Require TOF information"}; + Configurable c1052requireTRD{"c1052requireTRD", false, "Require TRD information"}; + + // TPC quality + Configurable c1053tpcNClsFoundMin{"c1053tpcNClsFoundMin", 0, "Minimum number of found TPC clusters"}; + Configurable c1054tpcNClsCrossedRowsMin{"c1054tpcNClsCrossedRowsMin", 80, "Minimum number of TPC crossed rows"}; + Configurable c1055tpcCrossedRowsOverFindableMin{"c1055tpcCrossedRowsOverFindableMin", 0.f, "Minimum TPC crossed rows / findable clusters"}; + Configurable c1056tpcFoundOverFindableMin{"c1056tpcFoundOverFindableMin", 0.f, "Minimum TPC found / findable clusters"}; + Configurable c1057tpcFractionSharedMax{"c1057tpcFractionSharedMax", 1.f, "Maximum fraction of shared TPC clusters"}; + Configurable c1058tpcChi2NClMin{"c1058tpcChi2NClMin", 0.f, "Minimum TPC chi2 per cluster"}; + Configurable c1059tpcChi2NClMax{"c1059tpcChi2NClMax", 1.e10f, "Maximum TPC chi2 per cluster"}; + + // ITS quality + Configurable c1060itsNClsMin{"c1060itsNClsMin", 0, "Minimum number of ITS clusters"}; + Configurable c1061itsNClsMax{"c1061itsNClsMax", 7, "Maximum number of ITS clusters"}; + Configurable c1062itsNClsInnerBarrelMin{"c1062itsNClsInnerBarrelMin", 0, "Minimum number of ITS inner-barrel clusters"}; + Configurable c1063itsChi2NClMin{"c1063itsChi2NClMin", 0.f, "Minimum ITS chi2 per cluster"}; + Configurable c1064itsChi2NClMax{"c1064itsChi2NClMax", 1.e10f, "Maximum ITS chi2 per cluster"}; + + // Fixed DCA + Configurable c1065useFixedDCAxy{"c1065useFixedDCAxy", true, "Apply fixed |DCAxy| cut"}; + Configurable c1066dcaXYMax{"c1066dcaXYMax", 0.1f, "Maximum fixed |DCAxy| (cm)"}; + Configurable c1067useFixedDCAz{"c1067useFixedDCAz", true, "Apply fixed |DCAz| cut"}; + Configurable c1068dcaZMax{"c1068dcaZMax", 0.2f, "Maximum fixed |DCAz| (cm)"}; + + // pT-dependent DCAxy: + // |DCAxy| < A + B * pT^C + Configurable c1069usePtDependentDCAxy{"c1069usePtDependentDCAxy", false, "Apply pT-dependent DCAxy cut"}; + Configurable c1070dcaXYPtA{"c1070dcaXYPtA", 0.0105f, "A coefficient of pT-dependent DCAxy cut"}; + Configurable c1071dcaXYPtB{"c1071dcaXYPtB", 0.035f, "B coefficient of pT-dependent DCAxy cut"}; + Configurable c1072dcaXYPtC{"c1072dcaXYPtC", 1.1f, "Power C of pT-dependent DCAxy cut"}; + + // pT-dependent DCAz: + // |DCAz| < A + B * pT^C + Configurable c1073usePtDependentDCAz{"c1073usePtDependentDCAz", false, "Apply pT-dependent DCAz cut"}; + Configurable c1074dcaZPtA{"c1074dcaZPtA", 0.1f, "A coefficient of pT-dependent DCAz cut"}; + Configurable c1075dcaZPtB{"c1075dcaZPtB", 0.0f, "B coefficient of pT-dependent DCAz cut"}; + Configurable c1076dcaZPtC{"c1076dcaZPtC", 0.0f, "Power C of pT-dependent DCAz cut"}; + } cfgTrackCuts; + + struct : ConfigurableGroup { + Configurable c1077triggerPtLow{"c1077triggerPtLow", 4.0f, "Minimum pT for trigger tracks"}; + Configurable c1078triggerPtHigh{"c1078triggerPtHigh", 8.0f, "Maximum pT for trigger tracks"}; + Configurable c1079assocPtLowMin{"c1079assocPtLowMin", 0.0f, "Minimum pT for low-pT associated tracks"}; + Configurable c1080assocPtLowMax{"c1080assocPtLowMax", 2.0f, "Maximum pT for low-pT associated tracks"}; + Configurable c1081assocPtHighMin{"c1081assocPtHighMin", 2.0f, "Minimum pT for high-pT associated tracks"}; + Configurable c1082assocPtHighMax{"c1082assocPtHighMax", 4.0f, "Maximum pT for high-pT associated tracks"}; + } cfgPartitions; + + struct : ConfigurableGroup { + Configurable c1083resoRapidityMin{"c1083resoRapidityMin", -0.5f, "Minimum rapidity for resonance candidates"}; + Configurable c1084resoRapidityMax{"c1084resoRapidityMax", 0.5f, "Maximum rapidity for resonance candidates"}; + + Configurable c1085phiPtLowMin{"c1085phiPtLowMin", 0.0f, "Minimum pT for low-pT Phi(1020)"}; + Configurable c1086phiPtLowMax{"c1086phiPtLowMax", 2.0f, "Maximum pT for low-pT Phi(1020)"}; + Configurable c1087phiPtHighMin{"c1087phiPtHighMin", 2.0f, "Minimum pT for high-pT Phi(1020)"}; + Configurable c1088phiPtHighMax{"c1088phiPtHighMax", 4.0f, "Maximum pT for high-pT Phi(1020)"}; + + Configurable c1089kstarPtLowMin{"c1089kstarPtLowMin", 0.0f, "Minimum pT for low-pT K*(892)0"}; + Configurable c1090kstarPtLowMax{"c1090kstarPtLowMax", 2.0f, "Maximum pT for low-pT K*(892)0"}; + Configurable c1091kstarPtHighMin{"c1091kstarPtHighMin", 2.0f, "Minimum pT for high-pT K*(892)0"}; + Configurable c1092kstarPtHighMax{"c1092kstarPtHighMax", 4.0f, "Maximum pT for high-pT K*(892)0"}; + + Configurable c1093kstarBarPtLowMin{"c1093kstarBarPtLowMin", 0.0f, "Minimum pT for low-pT anti-K*(892)0"}; + Configurable c1094kstarBarPtLowMax{"c1094kstarBarPtLowMax", 2.0f, "Maximum pT for low-pT anti-K*(892)0"}; + Configurable c1095kstarBarPtHighMin{"c1095kstarBarPtHighMin", 2.0f, "Minimum pT for high-pT anti-K*(892)0"}; + Configurable c1096kstarBarPtHighMax{"c1096kstarBarPtHighMax", 4.0f, "Maximum pT for high-pT anti-K*(892)0"}; + + Configurable c1097lambda1520PtLowMin{"c1097lambda1520PtLowMin", 0.0f, "Minimum pT for low-pT Lambda(1520)"}; + Configurable c1098lambda1520PtLowMax{"c1098lambda1520PtLowMax", 2.0f, "Maximum pT for low-pT Lambda(1520)"}; + Configurable c1099lambda1520PtHighMin{"c1099lambda1520PtHighMin", 2.0f, "Minimum pT for high-pT Lambda(1520)"}; + Configurable c1100lambda1520PtHighMax{"c1100lambda1520PtHighMax", 4.0f, "Maximum pT for high-pT Lambda(1520)"}; + + Configurable c1101lambda1520BarPtLowMin{"c1101lambda1520BarPtLowMin", 0.0f, "Minimum pT for low-pT anti-Lambda(1520)"}; + Configurable c1102lambda1520BarPtLowMax{"c1102lambda1520BarPtLowMax", 2.0f, "Maximum pT for low-pT anti-Lambda(1520)"}; + Configurable c1103lambda1520BarPtHighMin{"c1103lambda1520BarPtHighMin", 2.0f, "Minimum pT for high-pT anti-Lambda(1520)"}; + Configurable c1104lambda1520BarPtHighMax{"c1104lambda1520BarPtHighMax", 4.0f, "Maximum pT for high-pT anti-Lambda(1520)"}; + } cfgResPartitions; + + struct : ConfigurableGroup { + ConfigurableAxis c1105axisDeltaPhi{"c1105axisDeltaPhi", {80, -2.0f, 6.0f}, "#Delta#varphi"}; + ConfigurableAxis c1106axisDeltaEta{"c1106axisDeltaEta", {84, -2.1f, 2.1f}, "#Delta#eta"}; + + ConfigurableAxis c1107axisCorrSparseTriggerPt{"c1107axisCorrSparseTriggerPt", {8, 4.0f, 8.0f}, "#it{p}_{T}^{trig} (GeV/#it{c})"}; + ConfigurableAxis c1108axisCorrSparseAssocPt{"c1108axisCorrSparseAssocPt", {8, 0.0f, 4.0f}, "#it{p}_{T}^{assoc} (GeV/#it{c})"}; + ConfigurableAxis c1109axisCorrSparseDeltaPhi{"c1109axisCorrSparseDeltaPhi", {32, -2.0f, 6.0f}, "#Delta#varphi"}; + ConfigurableAxis c1110axisCorrSparseDeltaEta{"c1110axisCorrSparseDeltaEta", {24, -2.1f, 2.1f}, "#Delta#eta"}; + ConfigurableAxis c1111axisCorrCentrality{"c1111axisCorrCentrality", {VARIABLE_WIDTH, 0.0, 1.0, 5.0, 10.0, 20.0, 30.0, 40.0, 50.0, 70.0, 100.0}, "Centrality (%) axis of the correlation histograms (estimator chosen by c1138mixingEstimator); keep equal to c2008"}; + } cfgAxis; + + Configurable c1112fillDauQAOnce{"c1112fillDauQAOnce", false, "Fill each resonance daughter in QA only once per event"}; + Configurable c1113rejectResoWithAnyTriggerDaughter{"c1113rejectResoWithAnyTriggerDaughter", false, "Reject resonance if either daughter belongs to the event trigger population"}; + Configurable c1114requireSelectedTriggerForInvariantMass{"c1114requireSelectedTriggerForInvariantMass", true, "Require at least one selected trigger track before filling resonance invariant-mass spectra"}; + + struct : ConfigurableGroup { + Configurable c1115phi1020PeakLow{"c1115phi1020PeakLow", 1.013f, "Phi(1020) peak lower mass"}; + Configurable c1116phi1020PeakUp{"c1116phi1020PeakUp", 1.026f, "Phi(1020) peak upper mass"}; + Configurable c1117phi1020LSBLow{"c1117phi1020LSBLow", 0.995f, "Phi(1020) LSB lower mass"}; + Configurable c1118phi1020LSBUp{"c1118phi1020LSBUp", 1.005f, "Phi(1020) LSB upper mass"}; + Configurable c1119phi1020RSBLow{"c1119phi1020RSBLow", 1.040f, "Phi(1020) RSB lower mass"}; + Configurable c1120phi1020RSBUp{"c1120phi1020RSBUp", 1.060f, "Phi(1020) RSB upper mass"}; + } cfgPhi1020CorrMass; + + struct : ConfigurableGroup { + Configurable c1121kStar892PeakLow{"c1121kStar892PeakLow", 0.846f, "K*(892)0 correlation peak lower mass"}; + Configurable c1122kStar892PeakUp{"c1122kStar892PeakUp", 0.946f, "K*(892)0 correlation peak upper mass"}; + Configurable c1123kStar892LSBLow{"c1123kStar892LSBLow", 0.700f, "K*(892)0 correlation LSB lower mass"}; + Configurable c1124kStar892LSBUp{"c1124kStar892LSBUp", 0.780f, "K*(892)0 correlation LSB upper mass"}; + Configurable c1125kStar892RSBLow{"c1125kStar892RSBLow", 1.010f, "K*(892)0 correlation RSB lower mass"}; + Configurable c1126kStar892RSBUp{"c1126kStar892RSBUp", 1.090f, "K*(892)0 correlation RSB upper mass"}; + } cfgKStar892CorrMass; + + struct : ConfigurableGroup { + Configurable c1127lambda1520PeakLow{"c1127lambda1520PeakLow", 1.500f, "Lambda(1520) correlation peak lower mass"}; + Configurable c1128lambda1520PeakUp{"c1128lambda1520PeakUp", 1.540f, "Lambda(1520) correlation peak upper mass"}; + Configurable c1129lambda1520LSBLow{"c1129lambda1520LSBLow", 1.440f, "Lambda(1520) correlation LSB lower mass"}; + Configurable c1130lambda1520LSBUp{"c1130lambda1520LSBUp", 1.480f, "Lambda(1520) correlation LSB upper mass"}; + Configurable c1131lambda1520RSBLow{"c1131lambda1520RSBLow", 1.560f, "Lambda(1520) correlation RSB lower mass"}; + Configurable c1132lambda1520RSBUp{"c1132lambda1520RSBUp", 1.600f, "Lambda(1520) correlation RSB upper mass"}; + } cfgLambda1520CorrMass; + + struct : ConfigurableGroup { + Configurable c1133pairMask{"c1133pairMask", 0ULL, "Correlation mask to store; 0 = any non-zero correlation"}; + Configurable c1134mixingBin{"c1134mixingBin", -1, "Mixing bin to store; -1 = all valid mixing bins"}; + Configurable c1135requireAllPairBits{"c1135requireAllPairBits", false, "Require all requested pair-mask bits instead of any requested bit"}; + Configurable c1136resetGlobalCountersPerDF{"c1136resetGlobalCountersPerDF", false, "Reset derived-data global counters at the beginning of every dataframe"}; + } cfgDerivedData; + + struct : ConfigurableGroup { + Configurable c1137nEvtMixing{"c1137nEvtMixing", 5, "Number of events to mix"}; + Configurable c1138mixingEstimator{"c1138mixingEstimator", kMixCentFT0C, "Mixing estimator: 0=FT0C, 1=FT0M, 2=FT0A, 3=FV0A"}; + ConfigurableAxis c1139axisVtxMixing{"c1139axisVtxMixing", {VARIABLE_WIDTH, -10.0, -8.0, -6.0, -4.0, -2.0, 0.0, 2.0, 4.0, 6.0, 8.0, 10.0}, "Mixing bins - z vertex"}; + ConfigurableAxis c1140axisCentMixing{"c1140axisCentMixing", {VARIABLE_WIDTH, -1.0, 20.0, 50.0, 80.0, 101.0}, "Mixing bins - centrality"}; + ConfigurableAxis c1141axisMixingOccupancy{"c1141axisMixingOccupancy", {101, -0.5, 100.5}, "Number of collisions in mixing pool"}; + } cfgMixing; + + using BinningTypeVtxZFT0C = ColumnBinningPolicy; + using BinningTypeVtxZFT0M = ColumnBinningPolicy; + using BinningTypeVtxZFT0A = ColumnBinningPolicy; + using BinningTypeVtxZFV0A = ColumnBinningPolicy; + + BinningTypeVtxZFT0C colBinningFT0C{{cfgMixing.c1139axisVtxMixing, cfgMixing.c1140axisCentMixing}, true}; + BinningTypeVtxZFT0M colBinningFT0M{{cfgMixing.c1139axisVtxMixing, cfgMixing.c1140axisCentMixing}, true}; + BinningTypeVtxZFT0A colBinningFT0A{{cfgMixing.c1139axisVtxMixing, cfgMixing.c1140axisCentMixing}, true}; + BinningTypeVtxZFV0A colBinningFV0A{{cfgMixing.c1139axisVtxMixing, cfgMixing.c1140axisCentMixing}, true}; + + int nMixBins = 0; + + template + int getMixingBin(const C& collision) + { + switch (cfgMixing.c1138mixingEstimator) { + case kMixCentFT0C: + return colBinningFT0C.getBin({collision.posZ(), collision.centFT0C()}); + case kMixCentFT0M: + return colBinningFT0M.getBin({collision.posZ(), collision.centFT0M()}); + case kMixCentFT0A: + return colBinningFT0A.getBin({collision.posZ(), collision.centFT0A()}); + case kMixCentFV0A: + return colBinningFV0A.getBin({collision.posZ(), collision.centFV0A()}); + default: + return -1; + } + } + + template + float getMixingEstimatorValue(const C& collision) + { + switch (cfgMixing.c1138mixingEstimator) { + case kMixCentFT0C: + return collision.centFT0C(); + case kMixCentFT0M: + return collision.centFT0M(); + case kMixCentFT0A: + return collision.centFT0A(); + case kMixCentFV0A: + return collision.centFV0A(); + default: + return -1.0f; + } + } + + struct : ConfigurableGroup { + Configurable> c1142pidConfigSetting{"c1142pidConfigSetting", {DefaultPIDcheckValues[0].data(), kNPid, kNCutSettings, {"Pi", "Ka", "Pr", "El", "Mu", "De"}, {"ThrPforTOF", "IdCutTypeLowP", "NSigmaTPCLowP", "NSigmaTOFLowP", "NSigmaRadLowP", "IdCutTypeHighP", "NSigmaTPCHighP", "NSigmaTOFHighP", "NSigmaRadHighP", "doVetoOthers", "doRelativeTPCCheck", "doRelativeTOFcheck", "doRelativeTPCTOFcheck"}}, "Cut values for particle identification"}; + Configurable> c1143pidVetoSetting{"c1143pidVetoSetting", {DefaultPidVetoValues[0].data(), kNPid, kNVetoSettings, {"Pi", "Ka", "Pr", "El", "Mu", "De"}, {"doVetoTPC", "doVetoTOF", "vetoTPC", "vetoTOF"}}, "Veto cuts for particle identification"}; + Configurable c1144cfgId07CheckTofBeta{"c1144cfgId07CheckTofBeta", false, "Require beta > 0 for reliable TOF"}; + } cfgIdCut; + + void init(InitContext const&) + { + if (!a0000EnableStruct) { + return; + } + + if (cfgDebug.c1001printDebugMessages) { + LOGF(info, "Starting init"); + } + + if ((static_cast(cfgDerivedData.c1133pairMask) & ~(0xFFULL | MaskResoRegions)) != 0ULL) { + LOG(fatal) << "c1133pairMask = " << static_cast(cfgDerivedData.c1133pairMask) << " contains bits that are not mixing pools; allowed are bits 0-7 (h-h, h-pi, h-K, h-p) and 18-47 (resonance Peak/LSB/RSB)"; + } + // Axes + AxisSpec axisVertexZ = {30, -15., 15., "vrtx_{Z} [cm]"}; + AxisSpec axisCentFT0C = {1200, -10.0, 110.0, "centFT0C(percentile)"}; + AxisSpec axisMult = {150, -1.0, 149.0}; + + const AxisSpec axisEventVertexZ = {60, -15.0f, 15.0f, "z_{vtx} (cm)"}; + const AxisSpec axisEventCentrality = {102, -1.0f, 101.0f, "Centrality (%)"}; + const AxisSpec axisMixingBinStatus = {2, -0.5f, 1.5f, "Mixing-bin status"}; + + const AxisSpec axisP = {200, 0.0f, 10.0f, "#it{p} (GeV/#it{c})"}; + const AxisSpec axisPt = {200, 0.0f, 10.0f, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisTPCInnerParam = {200, 0.0f, 10.0f, "#it{p}_{tpcInnerParam} (GeV/#it{c})"}; + const AxisSpec axisTOFExpMom = {200, 0.0f, 10.0f, "#it{p}_{tofExpMom} (GeV/#it{c})"}; + + const AxisSpec axisEta = {100, -5.0f, 5.0f, "#eta"}; + const AxisSpec axisPhi = {110, -1.0f, 10.0f, "#phi (radians)"}; + const AxisSpec axisRapidity = {200, -5.0f, 5.0f, "Rapidity (y)"}; + const AxisSpec axisDcaXY = {100, -5.0f, 5.0f, "dcaXY"}; + const AxisSpec axisDcaZ = {100, -5.0f, 5.0f, "dcaZ"}; + const AxisSpec axisDcaXYwide = {2000, -100.0f, 100.0f, "dcaXY"}; + const AxisSpec axisDcaZwide = {2000, -100.0f, 100.0f, "dcaZ"}; + const AxisSpec axisSign = {10, -5.0f, 5.0f, "track.sign"}; + + const AxisSpec axisTPCSignal = {100, -1.0f, 1000.0f, "tpcSignal"}; + const AxisSpec axisTOFBeta = {40, -2.0f, 2.0f, "tofBeta"}; + + const AxisSpec axisTPCSignalFine = {10010, -1.0f, 1000.0f, "tpcSignal"}; + const AxisSpec axisTOFBetaFine = {400, -2.0f, 2.0f, "tofBeta"}; + + const AxisSpec axisTPCNSigma = {200, -10.0f, 10.0f, "n#sigma_{TPC}"}; + const AxisSpec axisTOFNSigma = {200, -10.0f, 10.0f, "n#sigma_{TOF}"}; + const AxisSpec axisIdMethod = {2, -0.5f, 1.5f, "ID method (0=TPC, 1=TPC+TOF)"}; + + AxisSpec axisDeltaPhiSpec{cfgAxis.c1105axisDeltaPhi, "#Delta#varphi"}; + AxisSpec axisDeltaEtaSpec{cfgAxis.c1106axisDeltaEta, "#Delta#eta"}; + + AxisSpec axisCorrSparseTriggerPt{cfgAxis.c1107axisCorrSparseTriggerPt, "#it{p}_{T}^{trig} (GeV/#it{c})"}; + AxisSpec axisCorrSparseAssocPt{cfgAxis.c1108axisCorrSparseAssocPt, "#it{p}_{T}^{assoc} (GeV/#it{c})"}; + AxisSpec axisCorrSparseDeltaPhi{cfgAxis.c1109axisCorrSparseDeltaPhi, "#Delta#varphi"}; + AxisSpec axisCorrSparseDeltaEta{cfgAxis.c1110axisCorrSparseDeltaEta, "#Delta#eta"}; + AxisSpec axisCorrCentrality{cfgAxis.c1111axisCorrCentrality, "Centrality (%)"}; + + const AxisSpec axisPhi1020Mass = {300, 0.98f, 1.08f, "m_{K^{+}K^{-}} (GeV/#it{c}^{2})"}; + const AxisSpec axisKStar892Mass = {300, 0.65f, 1.15f, "m_{K#pi} (GeV/#it{c}^{2})"}; + const AxisSpec axisLambda1520Mass = {300, 1.40f, 1.65f, "m_{pK} (GeV/#it{c}^{2})"}; + const AxisSpec axisResoMassCent = {102, -1.0f, 101.0f, "Centrality (%)"}; + + AxisSpec axisVtxMixSpec{cfgMixing.c1139axisVtxMixing, "z_{vtx} (cm)"}; + AxisSpec axisCentMixSpec{cfgMixing.c1140axisCentMixing, "Centrality (%)"}; + AxisSpec axisPoolOccupancy{cfgMixing.c1141axisMixingOccupancy, "N eligible collisions"}; + nMixBins = axisVtxMixSpec.getNbins() * axisCentMixSpec.getNbins(); + + // Re-build the binnings now that the configurables are set (as data members they hold the default axes) + colBinningFT0C = BinningTypeVtxZFT0C{{cfgMixing.c1139axisVtxMixing, cfgMixing.c1140axisCentMixing}, true}; + colBinningFT0M = BinningTypeVtxZFT0M{{cfgMixing.c1139axisVtxMixing, cfgMixing.c1140axisCentMixing}, true}; + colBinningFT0A = BinningTypeVtxZFT0A{{cfgMixing.c1139axisVtxMixing, cfgMixing.c1140axisCentMixing}, true}; + colBinningFV0A = BinningTypeVtxZFV0A{{cfgMixing.c1139axisVtxMixing, cfgMixing.c1140axisCentMixing}, true}; + + const AxisSpec axisMixBin{nMixBins, -0.5, static_cast(nMixBins) - 0.5, "Mixing bin"}; + const AxisSpec axisCorrChannel{NCorrCountChannels, -0.5, static_cast(NCorrCountChannels) - 0.5, "Correlation channel"}; + const AxisSpec axisReadyBins{nMixBins + 1, -0.5, static_cast(nMixBins) + 0.5, "N ready mixing bins"}; + + const int nEventQABins = static_cast(kNEventQABins); + const int nTrackQABins = static_cast(kNTrackQABins); + const int nDataFrameQABins = static_cast(kNDataFrameQABins); + const int nCollisionRejectionBins = static_cast(kNCollisionRejectionTags); + const int nTrackRejectionBins = static_cast(kNTrackRejectionTags); + + auto addEventQAHistos = [&](auto& histReg, const std::string& basePath) { + histReg.add((basePath + "VertexZ").c_str(), "Collision vertex;z_{vtx} (cm);Counts", HistType::kTH1F, {axisEventVertexZ}); + + histReg.add((basePath + "CentFT0C").c_str(), "FT0C centrality;Centrality (%);Counts", HistType::kTH1F, {axisEventCentrality}); + histReg.add((basePath + "CentFT0M").c_str(), "FT0M centrality;Centrality (%);Counts", HistType::kTH1F, {axisEventCentrality}); + histReg.add((basePath + "CentFT0A").c_str(), "FT0A centrality;Centrality (%);Counts", HistType::kTH1F, {axisEventCentrality}); + histReg.add((basePath + "CentFV0A").c_str(), "FV0A centrality;Centrality (%);Counts", HistType::kTH1F, {axisEventCentrality}); + + histReg.add((basePath + "VtxZVsCentFT0C").c_str(), "Vertex z vs FT0C centrality;z_{vtx} (cm);Centrality (%)", HistType::kTH2F, {axisEventVertexZ, axisEventCentrality}); + histReg.add((basePath + "VtxZVsCentFT0M").c_str(), "Vertex z vs FT0M centrality;z_{vtx} (cm);Centrality (%)", HistType::kTH2F, {axisEventVertexZ, axisEventCentrality}); + histReg.add((basePath + "VtxZVsCentFT0A").c_str(), "Vertex z vs FT0A centrality;z_{vtx} (cm);Centrality (%)", HistType::kTH2F, {axisEventVertexZ, axisEventCentrality}); + histReg.add((basePath + "VtxZVsCentFV0A").c_str(), "Vertex z vs FV0A centrality;z_{vtx} (cm);Centrality (%)", HistType::kTH2F, {axisEventVertexZ, axisEventCentrality}); + + histReg.add((basePath + "CentFT0CVsCentFT0M").c_str(), "FT0C vs FT0M centrality;FT0C (%);FT0M (%)", HistType::kTH2F, {axisEventCentrality, axisEventCentrality}); + histReg.add((basePath + "CentFT0CVsCentFT0A").c_str(), "FT0C vs FT0A centrality;FT0C (%);FT0A (%)", HistType::kTH2F, {axisEventCentrality, axisEventCentrality}); + histReg.add((basePath + "CentFT0MVsCentFT0A").c_str(), "FT0M vs FT0A centrality;FT0M (%);FT0A (%)", HistType::kTH2F, {axisEventCentrality, axisEventCentrality}); + + histReg.add((basePath + "CentFT0CVsCentFV0A").c_str(), "FT0C vs FV0A centrality;FT0C (%);FV0A (%)", HistType::kTH2F, {axisEventCentrality, axisEventCentrality}); + histReg.add((basePath + "CentFT0MVsCentFV0A").c_str(), "FT0M vs FV0A centrality;FT0M (%);FV0A (%)", HistType::kTH2F, {axisEventCentrality, axisEventCentrality}); + histReg.add((basePath + "CentFT0AVsCentFV0A").c_str(), "FT0A vs FV0A centrality;FT0A (%);FV0A (%)", HistType::kTH2F, {axisEventCentrality, axisEventCentrality}); + + histReg.add((basePath + "MixingEstimator").c_str(), "Centrality estimator used for mixing;Centrality (%);Counts", HistType::kTH1F, {axisEventCentrality}); + histReg.add((basePath + "VtxZVsMixingEstimator").c_str(), "Physical mixing-bin map;z_{vtx} (cm);Mixing centrality (%)", HistType::kTH2F, {axisVtxMixSpec, axisCentMixSpec}); + histReg.add((basePath + "MixingBin").c_str(), "Mixing-bin index;Mixing bin;Counts", HistType::kTH1F, {axisMixBin}); + histReg.add((basePath + "MixingBinStatus").c_str(), "Mixing-bin validity;Status;Counts", HistType::kTH1F, {axisMixingBinStatus}); + }; + + addEventQAHistos(seEventQA, "SE/Events/BeforeSelection/"); + seEventQA.addClone("SE/Events/BeforeSelection/", "SE/Events/AfterSelFunction/"); + seEventQA.addClone("SE/Events/BeforeSelection/", "SE/Events/AfterSelection/"); + + auto hMixStatusBefore = seEventQA.get(HIST("SE/Events/BeforeSelection/MixingBinStatus")); + hMixStatusBefore->GetXaxis()->SetBinLabel(1, "Invalid"); + hMixStatusBefore->GetXaxis()->SetBinLabel(2, "Valid"); + + auto hMixStatusAfter = seEventQA.get(HIST("SE/Events/AfterSelection/MixingBinStatus")); + hMixStatusAfter->GetXaxis()->SetBinLabel(1, "Invalid"); + hMixStatusAfter->GetXaxis()->SetBinLabel(2, "Valid"); + + seEventQA.add("SE/Events/EventSelection", "Event selection QA", HistType::kTH1F, {{nEventQABins, -0.5, static_cast(nEventQABins) - 0.5}}); + seTrackQA.add("SE/Tracks/TrackSelection", "Track selection QA", HistType::kTH1F, {{nTrackQABins, -0.5, static_cast(nTrackQABins) - 0.5}}); + seEventQA.add("SE/Events/DataFrameQA", "Dataframe QA", HistType::kTH1F, {{nDataFrameQABins, -0.5, static_cast(nDataFrameQABins) - 0.5}}); + seEventQA.add("SE/Events/CollisionRejection", "Collision rejection reason", HistType::kTH1F, {{nCollisionRejectionBins, -0.5, static_cast(nCollisionRejectionBins) - 0.5}}); + seTrackQA.add("SE/Tracks/TrackRejection", "Track rejection reason", HistType::kTH1F, {{nTrackRejectionBins, -0.5, static_cast(nTrackRejectionBins) - 0.5}}); + + auto hEventQA = seEventQA.get(HIST("SE/Events/EventSelection")); + hEventQA->GetXaxis()->SetBinLabel(static_cast(kEventAllFiltered) + 1, "Filtered collision"); + hEventQA->GetXaxis()->SetBinLabel(static_cast(kEventPassedSelCollision) + 1, "Passed selCollision"); + hEventQA->GetXaxis()->SetBinLabel(static_cast(kEventPassedMinFilteredTracks) + 1, "Passed min filtered tracks"); + hEventQA->GetXaxis()->SetBinLabel(static_cast(kEventPassedMinSelectedTracks) + 1, "Passed min selected tracks"); + + auto hTrackQA = seTrackQA.get(HIST("SE/Tracks/TrackSelection")); + hTrackQA->GetXaxis()->SetBinLabel(static_cast(kTrackAllFiltered) + 1, "Filtered track"); + hTrackQA->GetXaxis()->SetBinLabel(static_cast(kTrackPassedSelectionTrack) + 1, "Passed selectionTrack"); + + auto hDataFrameQA = seEventQA.get(HIST("SE/Events/DataFrameQA")); + hDataFrameQA->GetXaxis()->SetBinLabel(static_cast(kDFWithZeroFilteredColls) + 1, "Zero filtered collisions"); + + auto hCollRej = seEventQA.get(HIST("SE/Events/CollisionRejection")); + hCollRej->GetXaxis()->SetBinLabel(kCollAccepted + 1, "Accepted"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectSel8 + 1, "sel8"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectVertexZ + 1, "Vertex Z"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectTriggerTVX + 1, "TVX"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectITSROFrameBorder + 1, "ITS ROF border"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectTimeFrameBorder + 1, "TF border"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectVertexITSTPC + 1, "ITS-TPC vertex"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectGoodZvtxFT0vsPV + 1, "FT0/PV z"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectVertexTOFmatched + 1, "TOF vertex"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectVertexTRDmatched + 1, "TRD vertex"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectGoodITSLayersAll + 1, "Good ITS layers"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectSameBunchPileup + 1, "Same bunch pileup"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectCollInTimeRangeStandard + 1, "Time range std"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectCollInTimeRangeStrict + 1, "Time range strict"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectCollInTimeRangeNarrow + 1, "Time range narrow"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectCollInRofStandard + 1, "ROF std"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectCollInRofStrict + 1, "ROF strict"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectHighMultCollInPrevRof + 1, "High mult prev ROF"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectINELgt0 + 1, "INEL > 0"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectINELgt1 + 1, "INEL > 1"); + + auto hTrackRej = seTrackQA.get(HIST("SE/Tracks/TrackRejection")); + + hTrackRej->GetXaxis()->SetBinLabel(kTrackAccepted + 1, "Accepted"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectPt + 1, "pT"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectEta + 1, "Eta"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectMomentum + 1, "Momentum"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectCharge + 1, "Charge"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectQualityTrack + 1, "Quality track"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectQualityTrackITS + 1, "Quality ITS"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectQualityTrackTPC + 1, "Quality TPC"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectPrimaryTrack + 1, "Primary track"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectInAcceptanceTrack + 1, "Acceptance track"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectGlobalTrack + 1, "Global track"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectGlobalTrackWoDCA + 1, "Global wo DCA"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectGlobalTrackWoPtEta + 1, "Global wo pT eta"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectGlobalTrackWoTPCCluster + 1, "Global wo TPC cluster"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectGlobalTrackWoDCATPCCluster + 1, "Global wo DCA/TPC"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectPVContributor + 1, "PV contributor"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectITS + 1, "No ITS"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectTPC + 1, "No TPC"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectTOF + 1, "No TOF"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectTRD + 1, "No TRD"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectTPCNClsFound + 1, "TPC NCls found"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectTPCCrossedRows + 1, "TPC crossed rows"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectTPCCrossedRowsOverFindable + 1, "TPC crossed/findable"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectTPCFoundOverFindable + 1, "TPC found/findable"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectTPCFractionShared + 1, "TPC shared fraction"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectTPCChi2 + 1, "TPC chi2"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectITSNCls + 1, "ITS NCls"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectITSNClsInnerBarrel + 1, "ITS IB NCls"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectITSChi2 + 1, "ITS chi2"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectFixedDCAxy + 1, "Fixed DCAxy"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectFixedDCAz + 1, "Fixed DCAz"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectPtDependentDCAxy + 1, "pT-dep DCAxy"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectPtDependentDCAz + 1, "pT-dep DCAz"); + + //______________________________________________________________________________ + // Correlation histogram booking helpers + auto addTrackQAHistos = [&](auto& histReg, const std::string& basePath) { + addBasicTrackQAHistos(histReg, basePath, axisP, axisPt, axisEta, axisPhi, axisDcaXY, axisDcaZ, axisSign); + histReg.add((basePath + "TPCSignalVsTPCInnerParam").c_str(), "TPC signal vs p_{TPC inner};p_{TPC inner} (GeV/c);TPC signal", kTH2F, {axisTPCInnerParam, axisTPCSignal}); + histReg.add((basePath + "TPCSignalVsP").c_str(), "TPC signal vs p;p (GeV/c);TPC signal", kTH2F, {axisP, axisTPCSignal}); + histReg.add((basePath + "TOFBetaVsTOFExpMom").c_str(), "TOF #beta vs p_{TOF};p_{TOF} (GeV/c);#beta", kTH2F, {axisTOFExpMom, axisTOFBeta}); + histReg.add((basePath + "TOFBetaVsP").c_str(), "TOF #beta vs p;p (GeV/c);#beta", kTH2F, {axisP, axisTOFBeta}); + }; + + auto addTriggerPtVsCentrality = [&](auto& histReg, const std::string& basePath) { + histReg.add((basePath + "PtVsCentrality").c_str(), "Selected triggers;#it{p}_{T}^{trig} (GeV/#it{c});Centrality (%)", HistType::kTH2D, {axisCorrSparseTriggerPt, axisCorrCentrality}); + }; + + auto addFullTrackPIDQAHistos = [axisP, axisTPCNSigma, axisTOFNSigma](auto& histReg, const std::string& basePath) { + histReg.add((basePath + "TPCNSigmaVsP").c_str(), "TPC n#sigma vs p;p (GeV/c);n#sigma_{TPC}", HistType::kTH2F, {axisP, axisTPCNSigma}); + histReg.add((basePath + "TOFNSigmaVsP").c_str(), "TOF n#sigma vs p;p (GeV/c);n#sigma_{TOF}", HistType::kTH2F, {axisP, axisTOFNSigma}); + histReg.add((basePath + "TPCNSigmaVsTOFNSigma").c_str(), "TPC n#sigma vs TOF n#sigma;n#sigma_{TPC};n#sigma_{TOF}", HistType::kTH2F, {axisTPCNSigma, axisTOFNSigma}); + }; + + auto addFullTrackQAHistos = [&](auto& histReg, const std::string& basePath) { + addTrackQAHistos(histReg, basePath); + + histReg.add((basePath + "TPCInnerParam").c_str(), "TPC inner momentum;p_{TPC inner} (GeV/c);Counts", HistType::kTH1F, {axisTPCInnerParam}); + histReg.add((basePath + "TOFExpMom").c_str(), "TOF expected momentum;p_{TOF} (GeV/c);Counts", HistType::kTH1F, {axisTOFExpMom}); + + histReg.add((basePath + "DCAxyVsP").c_str(), "DCA_{xy} vs p;p (GeV/c);DCA_{xy} (cm)", HistType::kTH2F, {axisP, axisDcaXY}); + histReg.add((basePath + "DCAxyVsTPCInnerParam").c_str(), "DCA_{xy} vs p_{TPC inner};p_{TPC inner} (GeV/c);DCA_{xy} (cm)", HistType::kTH2F, {axisTPCInnerParam, axisDcaXY}); + histReg.add((basePath + "DCAxyVsTOFExpMom").c_str(), "DCA_{xy} vs p_{TOF};p_{TOF} (GeV/c);DCA_{xy} (cm)", HistType::kTH2F, {axisTOFExpMom, axisDcaXY}); + + histReg.add((basePath + "DCAzVsP").c_str(), "DCA_{z} vs p;p (GeV/c);DCA_{z} (cm)", HistType::kTH2F, {axisP, axisDcaZ}); + histReg.add((basePath + "DCAzVsTPCInnerParam").c_str(), "DCA_{z} vs p_{TPC inner};p_{TPC inner} (GeV/c);DCA_{z} (cm)", HistType::kTH2F, {axisTPCInnerParam, axisDcaZ}); + histReg.add((basePath + "DCAzVsTOFExpMom").c_str(), "DCA_{z} vs p_{TOF};p_{TOF} (GeV/c);DCA_{z} (cm)", HistType::kTH2F, {axisTOFExpMom, axisDcaZ}); + + histReg.add((basePath + "PVsPt").c_str(), "p vs p_{T};p (GeV/c);p_{T} (GeV/c)", HistType::kTH2F, {axisP, axisPt}); + histReg.add((basePath + "PVsTPCInnerParam").c_str(), "p vs p_{TPC inner};p (GeV/c);p_{TPC inner} (GeV/c)", HistType::kTH2F, {axisP, axisTPCInnerParam}); + histReg.add((basePath + "PVsTOFExpMom").c_str(), "p vs p_{TOF};p (GeV/c);p_{TOF} (GeV/c)", HistType::kTH2F, {axisP, axisTOFExpMom}); + + histReg.add((basePath + "TPCSignalVsTOFExpMom").c_str(), "TPC signal vs p_{TOF};p_{TOF} (GeV/c);TPC signal", HistType::kTH2F, {axisTOFExpMom, axisTPCSignal}); + histReg.add((basePath + "TOFBetaVsTPCInnerParam").c_str(), "TOF #beta vs p_{TPC inner};p_{TPC inner} (GeV/c);#beta", HistType::kTH2F, {axisTPCInnerParam, axisTOFBeta}); + + addFullTrackPIDQAHistos(histReg, basePath + "PID/Pi/"); + histReg.addClone(basePath + "PID/Pi/", basePath + "PID/Ka/"); + histReg.addClone(basePath + "PID/Pi/", basePath + "PID/Pr/"); + histReg.addClone(basePath + "PID/Pi/", basePath + "PID/El/"); + histReg.addClone(basePath + "PID/Pi/", basePath + "PID/Mu/"); + histReg.addClone(basePath + "PID/Pi/", basePath + "PID/De/"); + }; + addFullTrackQAHistos(seTrackQA, "SE/Tracks/BeforeSelection/"); + seTrackQA.addClone("SE/Tracks/BeforeSelection/", "SE/Tracks/AfterSelection/"); + + auto addCorrHistos = [&](auto& histReg, const std::string& basePath) { + histReg.add((basePath + "DeltaPhi").c_str(), "Trigger-associate;#Delta#varphi;Pairs", HistType::kTH1F, {axisDeltaPhiSpec}); + histReg.add((basePath + "DeltaEta").c_str(), "Trigger-associate;#Delta#eta;Pairs", HistType::kTH1F, {axisDeltaEtaSpec}); + histReg.add((basePath + "DeltaPhiDeltaEta").c_str(), "Trigger-associate;#Delta#varphi;#Delta#eta;Centrality (%)", HistType::kTH3F, {axisDeltaPhiSpec, axisDeltaEtaSpec, axisCorrCentrality}); + histReg.add((basePath + "CorrSparse").c_str(), "Correlation sparse;p_{T}^{trig};p_{T}^{assoc};#Delta#varphi;#Delta#eta;Centrality (%)", HistType::kTHnSparseF, {axisCorrSparseTriggerPt, axisCorrSparseAssocPt, axisCorrSparseDeltaPhi, axisCorrSparseDeltaEta, axisCorrCentrality}); + }; + + auto addResoMassCorrHistos = [&](auto& histReg, const std::string& basePath, const AxisSpec& massAxis) { + histReg.add((basePath + "DeltaPhiVsInvMass").c_str(), "#Delta#varphi vs invariant mass;Invariant mass (GeV/c^{2});#Delta#varphi;Centrality (%)", HistType::kTH3F, {massAxis, axisDeltaPhiSpec, axisCorrCentrality}); + histReg.add((basePath + "DeltaEtaVsInvMass").c_str(), "#Delta#eta vs invariant mass;Invariant mass (GeV/c^{2});#Delta#eta;Centrality (%)", HistType::kTH3F, {massAxis, axisDeltaEtaSpec, axisCorrCentrality}); + }; + + auto addResoAssocQAHistos = [&](auto& histReg, const std::string& basePath, const AxisSpec& massAxis, const std::string& massHistTitle) { + histReg.add((basePath + "P").c_str(), "Resonance;p (GeV/c);Counts", HistType::kTH1F, {axisP}); + histReg.add((basePath + "Pt").c_str(), "Resonance;p_{T} (GeV/c);Counts", HistType::kTH1F, {axisPt}); + histReg.add((basePath + "Eta").c_str(), "Resonance;#eta;Counts", HistType::kTH1F, {axisEta}); + histReg.add((basePath + "Phi").c_str(), "Resonance;#varphi;Counts", HistType::kTH1F, {axisPhi}); + histReg.add((basePath + "Rapidity").c_str(), "Resonance;y;Counts", HistType::kTH1F, {axisRapidity}); + histReg.add((basePath + "InvMass").c_str(), massHistTitle.c_str(), HistType::kTH1F, {massAxis}); + histReg.add((basePath + "InvMassVsPt").c_str(), "Invariant mass vs p_{T};p_{T} (GeV/c);Invariant mass (GeV/c^{2})", HistType::kTH2F, {axisPt, massAxis}); + }; + + auto addResoDauQAHistos = [&](auto& histReg, const std::string& basePath) { + addBasicTrackQAHistos(histReg, basePath, axisP, axisPt, axisEta, axisPhi, axisDcaXY, axisDcaZ, axisSign); + addIdentifiedQAHistos(histReg, basePath, axisP, axisRapidity, axisTPCSignal, axisTOFBeta, axisTPCNSigma, axisTOFNSigma, axisIdMethod); + }; + + // h-h ______________________________________________________________________________ + addTrackQAHistos(hhCorrelation, "SE/hh/QA/Trigger/"); + hhCorrelation.addClone("SE/hh/QA/Trigger/", "SE/hh/QA/AssocLowPt/"); + hhCorrelation.addClone("SE/hh/QA/Trigger/", "SE/hh/QA/AssocHighPt/"); + addTriggerPtVsCentrality(hhCorrelation, "SE/hh/QA/Trigger/"); + addCorrHistos(hhCorrelation, "SE/hh/Corr/AssocLowPt/"); + hhCorrelation.addClone("SE/hh/Corr/AssocLowPt/", "SE/hh/Corr/AssocHighPt/"); + + // h-Id ______________________________________________________________________________ + auto registerIdentifiedHistograms = [&](auto& histReg, const std::string& pairName) { + const std::string base = "SE/" + pairName + "/"; + + addTrackQAHistos(histReg, base + "QA/Trigger/"); + addTriggerPtVsCentrality(histReg, base + "QA/Trigger/"); + addBasicTrackQAHistos(histReg, base + "QA/AssocLowPt/Pi/", axisP, axisPt, axisEta, axisPhi, axisDcaXY, axisDcaZ, axisSign); + addIdentifiedQAHistos(histReg, base + "QA/AssocLowPt/Pi/", axisP, axisRapidity, axisTPCSignal, axisTOFBeta, axisTPCNSigma, axisTOFNSigma, axisIdMethod); + histReg.addClone(base + "QA/AssocLowPt/Pi/", base + "QA/AssocLowPt/Ka/"); + histReg.addClone(base + "QA/AssocLowPt/Pi/", base + "QA/AssocLowPt/Pr/"); + histReg.addClone(base + "QA/AssocLowPt/", base + "QA/AssocHighPt/"); + + addCorrHistos(histReg, base + "Corr/AssocLowPt/Pi/"); + histReg.addClone(base + "Corr/AssocLowPt/Pi/", base + "Corr/AssocLowPt/Ka/"); + histReg.addClone(base + "Corr/AssocLowPt/Pi/", base + "Corr/AssocLowPt/Pr/"); + histReg.addClone(base + "Corr/AssocLowPt/", base + "Corr/AssocHighPt/"); + }; + registerIdentifiedHistograms(hIdCorrelation, "hIdentified"); + + // h-Resonance ______________________________________________________________________________ + auto registerResonanceHistograms = [&](auto& histReg, const std::string& pairName, const AxisSpec& massAxis, const std::string& massHistTitle) { + const std::string base = "SE/" + pairName + "/"; + const std::string qaPeak = base + "QA/AssocLowPt/Peak/"; + + addTrackQAHistos(histReg, base + "QA/Trigger/"); + addTriggerPtVsCentrality(histReg, base + "QA/Trigger/"); + addResoAssocQAHistos(histReg, qaPeak + "Reso/", massAxis, massHistTitle); + addResoDauQAHistos(histReg, qaPeak + "PosDau/"); + histReg.addClone(qaPeak + "PosDau/", qaPeak + "NegDau/"); + histReg.addClone(base + "QA/AssocLowPt/Peak/", base + "QA/AssocLowPt/LSB/"); + histReg.addClone(base + "QA/AssocLowPt/Peak/", base + "QA/AssocLowPt/RSB/"); + histReg.addClone(base + "QA/AssocLowPt/", base + "QA/AssocHighPt/"); + + addCorrHistos(histReg, base + "Corr/AssocLowPt/Peak/"); + histReg.addClone(base + "Corr/AssocLowPt/Peak/", base + "Corr/AssocLowPt/LSB/"); + histReg.addClone(base + "Corr/AssocLowPt/Peak/", base + "Corr/AssocLowPt/RSB/"); + addResoMassCorrHistos(histReg, base + "Corr/AssocLowPt/MassDependent/", massAxis); + histReg.addClone(base + "Corr/AssocLowPt/", base + "Corr/AssocHighPt/"); + }; + + registerResonanceHistograms(hPhiCorrelation, "hPhi", axisPhi1020Mass, "#phi(1020);m_{K^{+}K^{-}} (GeV/#it{c}^{2});Counts"); + registerResonanceHistograms(hKStarCorrelation, "hKStar", axisKStar892Mass, "K^{*}(892)^{0};m_{K^{+}#pi^{-}} (GeV/#it{c}^{2});Counts"); + registerResonanceHistograms(hKStarBarCorrelation, "hKStarBar", axisKStar892Mass, "#bar{K}^{*}(892)^{0};m_{#pi^{+}K^{-}} (GeV/#it{c}^{2});Counts"); + registerResonanceHistograms(hLambdaCorrelation, "hLambda", axisLambda1520Mass, "#Lambda(1520);m_{pK^{-}} (GeV/#it{c}^{2});Counts"); + registerResonanceHistograms(hLambdaBarCorrelation, "hLambdaBar", axisLambda1520Mass, "#bar{#Lambda}(1520);m_{K^{+}#bar{p}} (GeV/#it{c}^{2});Counts"); + + auto addInvariantMassSpectra = [&](const std::string& species, const AxisSpec& massAxis) { + const std::string seBase = "SE/InvariantMass/" + species + "/"; + const std::string meBase = "ME/InvariantMass/" + species + "/"; + + resonanceMassSpectra.add((seBase + "US").c_str(), "Unlike-sign;m_{inv} (GeV/#it{c}^{2});p_{T} (GeV/#it{c});Centrality (%)", kTHnSparseF, {massAxis, axisPt, axisResoMassCent}); + resonanceMassSpectra.add((seBase + "LS").c_str(), "Like-sign;m_{inv} (GeV/#it{c}^{2});p_{T} (GeV/#it{c});Centrality (%)", kTHnSparseF, {massAxis, axisPt, axisResoMassCent}); + resonanceMassSpectra.add((meBase + "US").c_str(), "Mixed-event;m_{inv} (GeV/#it{c}^{2});p_{T} (GeV/#it{c});Centrality (%)", kTHnSparseF, {massAxis, axisPt, axisResoMassCent}); + }; + + addInvariantMassSpectra("Phi", axisPhi1020Mass); + addInvariantMassSpectra("KStar", axisKStar892Mass); + addInvariantMassSpectra("KStarBar", axisKStar892Mass); + addInvariantMassSpectra("Lambda1520", axisLambda1520Mass); + addInvariantMassSpectra("Lambda1520Bar", axisLambda1520Mass); + + //______________________________________________________________________________ + // Event-mixing QA + mixingQA.add("Mixing/DataFramesProcessed", "Processed dataframes;Dataframe;Count", HistType::kTH1D, {{1, -0.5, 0.5}}); + mixingQA.add("Mixing/AllCollisionsPerBin", "Selected collisions per mixing bin;Mixing bin;Collisions", HistType::kTH1D, {axisMixBin}); + mixingQA.add("Mixing/AllCollisionsZCent", "Selected collisions in physical mixing pools;z_{vtx} (cm);Centrality (%)", HistType::kTH2D, {axisVtxMixSpec, axisCentMixSpec}); + mixingQA.add("Mixing/EligibleCollisionsPerBin", "Eligible collisions per mixing pool;Mixing bin;Correlation channel", HistType::kTH2D, {axisMixBin, axisCorrChannel}); + mixingQA.add("Mixing/EligibleCollisionsZCentChannel", "Eligible collisions in physical mixing pools;z_{vtx} (cm);Centrality (%);Correlation channel", HistType::kTH3D, {axisVtxMixSpec, axisCentMixSpec, axisCorrChannel}); + mixingQA.add("Mixing/PerDF/AllPoolOccupancy", "Mixing-pool occupancy per dataframe;Mixing bin;N collisions", HistType::kTH2D, {axisMixBin, axisPoolOccupancy}); + mixingQA.add("Mixing/PerDF/EligiblePoolOccupancy", "Eligible pool occupancy per dataframe;Mixing bin;Correlation channel;N eligible collisions", HistType::kTH3D, {axisMixBin, axisCorrChannel, axisPoolOccupancy}); + mixingQA.add("Mixing/PerDF/PoolOccupancyDistribution", "Distribution of mixing-pool occupancies;Correlation channel;N eligible collisions", HistType::kTH2D, {axisCorrChannel, axisPoolOccupancy}); + mixingQA.add("Mixing/PerDF/ReadyPoolMap", "Dataframes where pool was ready;Mixing bin;Correlation channel", HistType::kTH2D, {axisMixBin, axisCorrChannel}); + mixingQA.add("Mixing/PerDF/NReadyMixBins", "Number of ready mixing bins per dataframe;Correlation channel;N ready mixing bins", HistType::kTH2D, {axisCorrChannel, axisReadyBins}); + + auto setCorrChannelLabels = [](TAxis* axis) { + for (int iCorr = 0; iCorr < kNCorrCountTypes; ++iCorr) { + axis->SetBinLabel(iCorr * kNCorrCountPt + kCountLowPt + 1, (std::string(CorrCountTypeName[iCorr]) + "_LowPt").c_str()); + axis->SetBinLabel(iCorr * kNCorrCountPt + kCountHighPt + 1, (std::string(CorrCountTypeName[iCorr]) + "_HighPt").c_str()); + } + }; + setCorrChannelLabels(mixingQA.get(HIST("Mixing/EligibleCollisionsPerBin"))->GetYaxis()); + setCorrChannelLabels(mixingQA.get(HIST("Mixing/EligibleCollisionsZCentChannel"))->GetZaxis()); + setCorrChannelLabels(mixingQA.get(HIST("Mixing/PerDF/EligiblePoolOccupancy"))->GetYaxis()); + setCorrChannelLabels(mixingQA.get(HIST("Mixing/PerDF/PoolOccupancyDistribution"))->GetXaxis()); + setCorrChannelLabels(mixingQA.get(HIST("Mixing/PerDF/ReadyPoolMap"))->GetYaxis()); + setCorrChannelLabels(mixingQA.get(HIST("Mixing/PerDF/NReadyMixBins"))->GetXaxis()); + + mixingQA.add("DerivedData/RowsWritten", "Rows written to the derived tables;Table;Rows", HistType::kTH1D, {{4, -0.5, 3.5}}); + mixingQA.get(HIST("DerivedData/RowsWritten"))->GetXaxis()->SetBinLabel(1, "Collisions"); + mixingQA.get(HIST("DerivedData/RowsWritten"))->GetXaxis()->SetBinLabel(2, "Triggers"); + mixingQA.get(HIST("DerivedData/RowsWritten"))->GetXaxis()->SetBinLabel(3, "TrackAssocs"); + mixingQA.get(HIST("DerivedData/RowsWritten"))->GetXaxis()->SetBinLabel(4, "ResoAssocs"); + + if (cfgDebug.c1001printDebugMessages) { + LOGF(info, "Finishing init"); + } + } // Init function is over. + + //_______________________________ Particle Identification _______________________________ + // p-dependent identification + // p < ThrPforTOF : TPC+TOF (circular cut) if TOF is reliable, otherwise TPC only + // p >= ThrPforTOF : TPC+TOF (circular cut), TOF is required + // Default cuts are 3 sigma (see DefaultPIDcheckValues). + + // If vetoIdOthers = true; it passed all veto checks + // If vetoIdOthers = false; it failed veto check with some other particle + template + bool vetoIdOthersTPC(const T& track) + { + // Static is only run once, ever. + static const std::array doVetoTPC = { + getCfg(cfgIdCut.c1143pidVetoSetting, kPi, kDoVetoTPC), + getCfg(cfgIdCut.c1143pidVetoSetting, kKa, kDoVetoTPC), + getCfg(cfgIdCut.c1143pidVetoSetting, kPr, kDoVetoTPC), + getCfg(cfgIdCut.c1143pidVetoSetting, kEl, kDoVetoTPC), + getCfg(cfgIdCut.c1143pidVetoSetting, kMu, kDoVetoTPC), + getCfg(cfgIdCut.c1143pidVetoSetting, kDe, kDoVetoTPC)}; + + static const std::array vetoTPC = { + getCfg(cfgIdCut.c1143pidVetoSetting, kPi, kVetoTPC), + getCfg(cfgIdCut.c1143pidVetoSetting, kKa, kVetoTPC), + getCfg(cfgIdCut.c1143pidVetoSetting, kPr, kVetoTPC), + getCfg(cfgIdCut.c1143pidVetoSetting, kEl, kVetoTPC), + getCfg(cfgIdCut.c1143pidVetoSetting, kMu, kVetoTPC), + getCfg(cfgIdCut.c1143pidVetoSetting, kDe, kVetoTPC)}; + + return applyVetoOthersTPC(track, doVetoTPC, vetoTPC); + } + + template + bool vetoIdOthersTOF(const T& track) + { + // Only computed once + static const std::array doVetoTOF = { + getCfg(cfgIdCut.c1143pidVetoSetting, kPi, kDoVetoTOF), + getCfg(cfgIdCut.c1143pidVetoSetting, kKa, kDoVetoTOF), + getCfg(cfgIdCut.c1143pidVetoSetting, kPr, kDoVetoTOF), + getCfg(cfgIdCut.c1143pidVetoSetting, kEl, kDoVetoTOF), + getCfg(cfgIdCut.c1143pidVetoSetting, kMu, kDoVetoTOF), + getCfg(cfgIdCut.c1143pidVetoSetting, kDe, kDoVetoTOF)}; + + static const std::array vetoTOF = { + getCfg(cfgIdCut.c1143pidVetoSetting, kPi, kVetoTOF), + getCfg(cfgIdCut.c1143pidVetoSetting, kKa, kVetoTOF), + getCfg(cfgIdCut.c1143pidVetoSetting, kPr, kVetoTOF), + getCfg(cfgIdCut.c1143pidVetoSetting, kEl, kVetoTOF), + getCfg(cfgIdCut.c1143pidVetoSetting, kMu, kVetoTOF), + getCfg(cfgIdCut.c1143pidVetoSetting, kDe, kVetoTOF)}; + + return applyVetoOthersTOF(track, doVetoTOF, vetoTOF); + } + + template + bool vetoIdOthersTPCTOF(const T& track) + { + // If either veto fails, reject + return vetoIdOthersTPC(track) && vetoIdOthersTOF(track); + } + + // Check if TOF is reliable + template + inline bool checkReliableTOF(const T& track) + { + // which check makes the information of TOF relaiable? should track.beta() be checked? e.g.: + if (cfgIdCut.c1144cfgId07CheckTofBeta) { + return (track.hasTOF() && track.beta() > 0.0f); + } + return track.hasTOF(); + } + + template + bool idTPC(const T& track, const float& nSigmaTPC, float& nSigmaIdDistSq) + { + if constexpr (pidMode == kPi) { + nSigmaIdDistSq = track.tpcNSigmaPi() * track.tpcNSigmaPi(); + } else if constexpr (pidMode == kKa) { + nSigmaIdDistSq = track.tpcNSigmaKa() * track.tpcNSigmaKa(); + } else if constexpr (pidMode == kPr) { + nSigmaIdDistSq = track.tpcNSigmaPr() * track.tpcNSigmaPr(); + } else if constexpr (pidMode == kEl) { + nSigmaIdDistSq = track.tpcNSigmaEl() * track.tpcNSigmaEl(); + } else if constexpr (pidMode == kMu) { + nSigmaIdDistSq = track.tpcNSigmaMu() * track.tpcNSigmaMu(); + } else if constexpr (pidMode == kDe) { + nSigmaIdDistSq = track.tpcNSigmaDe() * track.tpcNSigmaDe(); + } else { + nSigmaIdDistSq = 1000000; + } + + static const bool doVetoOthers = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kDoVetoOthers); + if (doVetoOthers) { + if (!vetoIdOthersTPC(track)) { + // If vetoIdOthers = true; it passed all veto checks + // If vetoIdOthers = false; it failed veto check with some other particle + return false; + } + } + + static const bool doRelativeTPCcheck = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kDoRelativeTPCcheck); + if (doRelativeTPCcheck) { + if (!relativeIdOthersTPC(track)) { + // If relativeIdOthersTPC = true; particle has stronger nSigma compared to others + // If relativeIdOthersTPC = false; some particle has stronger nSigma compared to it + return false; + } + } + + if constexpr (pidMode == kPi) { + return std::fabs(track.tpcNSigmaPi()) < nSigmaTPC; + } else if constexpr (pidMode == kKa) { + return std::fabs(track.tpcNSigmaKa()) < nSigmaTPC; + } else if constexpr (pidMode == kPr) { + return std::fabs(track.tpcNSigmaPr()) < nSigmaTPC; + } else if constexpr (pidMode == kEl) { + return std::fabs(track.tpcNSigmaEl()) < nSigmaTPC; + } else if constexpr (pidMode == kMu) { + return std::fabs(track.tpcNSigmaMu()) < nSigmaTPC; + } else if constexpr (pidMode == kDe) { + return std::fabs(track.tpcNSigmaDe()) < nSigmaTPC; + } else { + return false; + } + } + + template + bool idTPCTOF(const T& track, const int& pidCutType, const float& nSigmaTPC, const float& nSigmaTOF, const float& nSigmaSquaredRad, float& nSigmaIdDistSq) + { + if constexpr (pidMode == kPi) { + nSigmaIdDistSq = track.tpcNSigmaPi() * track.tpcNSigmaPi() + track.tofNSigmaPi() * track.tofNSigmaPi(); + } else if constexpr (pidMode == kKa) { + nSigmaIdDistSq = track.tpcNSigmaKa() * track.tpcNSigmaKa() + track.tofNSigmaKa() * track.tofNSigmaKa(); + } else if constexpr (pidMode == kPr) { + nSigmaIdDistSq = track.tpcNSigmaPr() * track.tpcNSigmaPr() + track.tofNSigmaPr() * track.tofNSigmaPr(); + } else if constexpr (pidMode == kEl) { + nSigmaIdDistSq = track.tpcNSigmaEl() * track.tpcNSigmaEl() + track.tofNSigmaEl() * track.tofNSigmaEl(); + } else if constexpr (pidMode == kMu) { + nSigmaIdDistSq = track.tpcNSigmaMu() * track.tpcNSigmaMu() + track.tofNSigmaMu() * track.tofNSigmaMu(); + } else if constexpr (pidMode == kDe) { + nSigmaIdDistSq = track.tpcNSigmaDe() * track.tpcNSigmaDe() + track.tofNSigmaDe() * track.tofNSigmaDe(); + } else { + nSigmaIdDistSq = 1000000; + } + static const bool doVetoOthers = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kDoVetoOthers); + if (doVetoOthers) { + if (!vetoIdOthersTPCTOF(track)) { + // If vetoIdOthers = true; it passed all veto checks + // If vetoIdOthers = false; it failed veto check with some other particle + return false; + } + } + + static const bool doRelativeTOFcheck = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kDoRelativeTOFcheck); + if (doRelativeTOFcheck) { + if (!relativeIdOthersTOF(track)) { + // If relativeIdOthersTOF = true; particle has stronger nSigma compared to others + // If relativeIdOthersTOF = false; some particle has stronger nSigma compared to it + return false; + } + } + + static const bool doRelativeTPCTOFcheck = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kDoRelativeTPCTOFcheck); + if (doRelativeTPCTOFcheck) { + if (!relativeIdOthersTPCTOF(track)) { + // If relativeIdOthersTPCTOF = true; particle has stronger nSigma compared to others + // If relativeIdOthersTPCTOF = false; some particle has stronger nSigma compared to it + return false; + } + } + + switch (pidCutType) { + case kCircularCut: + return selIdCircularCut(track, nSigmaSquaredRad); + case kRectangularCut: + return selIdRectangularCut(track, nSigmaTPC, nSigmaTOF); + case kEllipsoidalCut: + return selIdEllipsoidalCut(track, nSigmaTPC, nSigmaTOF); + default: + return false; + } + } + + template + bool selPdependent(const T& track, int& IdMethod, float& nSigmaIdDistSq) + { + // Static cache inside function - initialized once on first call + static const auto thrPforTOF = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kThrPforTOF); + static const auto idCutTypeLowP = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kIdCutTypeLowP); + static const auto nSigmaTPCLowP = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kNSigmaTPCLowP); + static const auto nSigmaTOFLowP = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kNSigmaTOFLowP); + static const auto nSigmaRadLowP = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kNSigmaRadLowP); + static const auto idCutTypeHighP = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kIdCutTypeHighP); + static const auto nSigmaTPCHighP = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kNSigmaTPCHighP); + static const auto nSigmaTOFHighP = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kNSigmaTOFHighP); + static const auto nSigmaRadHighP = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kNSigmaRadHighP); + + if (track.p() < thrPforTOF) { + if (checkReliableTOF(track)) { + if (idTPCTOF(track, idCutTypeLowP, nSigmaTPCLowP, nSigmaTOFLowP, nSigmaRadLowP, nSigmaIdDistSq)) { + IdMethod = kTPCTOFidentified; + return true; + } + } else { + if (idTPC(track, nSigmaTPCLowP, nSigmaIdDistSq)) { + IdMethod = kTPCidentified; + return true; + } + } + } else { + if (checkReliableTOF(track)) { + if (idTPCTOF(track, idCutTypeHighP, nSigmaTPCHighP, nSigmaTOFHighP, nSigmaRadHighP, nSigmaIdDistSq)) { + IdMethod = kTPCTOFidentified; + return true; + } + } + } + return false; + } + + //______________________________Identification Functions________________________________________________________________ + // Pion + template + bool selPion(const T& track, int& IdMethod, float& nSigmaIdDistSq) + { + return selPdependent(track, IdMethod, nSigmaIdDistSq); + } + + // Kaon + template + bool selKaon(const T& track, int& IdMethod, float& nSigmaIdDistSq) + { + return selPdependent(track, IdMethod, nSigmaIdDistSq); + } + + // Proton + template + bool selProton(const T& track, int& IdMethod, float& nSigmaIdDistSq) + { + return selPdependent(track, IdMethod, nSigmaIdDistSq); + } + + // Electron + template + bool selElectron(const T& track, int& IdMethod, float& nSigmaIdDistSq) + { + return selPdependent(track, IdMethod, nSigmaIdDistSq); + } + + // Muon + template + bool selMuon(const T& track, int& IdMethod, float& nSigmaIdDistSq) + { + return selPdependent(track, IdMethod, nSigmaIdDistSq); + } + + // Deuteron + template + bool selDeuteron(const T& track, int& IdMethod, float& nSigmaIdDistSq) + { + return selPdependent(track, IdMethod, nSigmaIdDistSq); + } + + // Two-argument versions + template + bool selPion(const T& track, int& IdMethod) + { + float nSigmaIdDistSq = -1.0f; + return selPion(track, IdMethod, nSigmaIdDistSq); + } + + template + bool selKaon(const T& track, int& IdMethod) + { + float nSigmaIdDistSq = -1.0f; + return selKaon(track, IdMethod, nSigmaIdDistSq); + } + + template + bool selProton(const T& track, int& IdMethod) + { + float nSigmaIdDistSq = -1.0f; + return selProton(track, IdMethod, nSigmaIdDistSq); + } + + template + bool selElectron(const T& track, int& IdMethod) + { + float nSigmaIdDistSq = -1.0f; + return selElectron(track, IdMethod, nSigmaIdDistSq); + } + + template + bool selMuon(const T& track, int& IdMethod) + { + float nSigmaIdDistSq = -1.0f; + return selMuon(track, IdMethod, nSigmaIdDistSq); + } + + template + bool selDeuteron(const T& track, int& IdMethod) + { + float nSigmaIdDistSq = -1.0f; + return selDeuteron(track, IdMethod, nSigmaIdDistSq); + } + // + + template + CollisionRejectionTag selCollision(T const& collision) + { + // Basic event selection + if (cfgEvent.c1003requireSel8 && !collision.sel8()) { + return kCollRejectSel8; + } + + if (std::abs(collision.posZ()) >= cfgEvent.c1002cutZvertex) { + return kCollRejectVertexZ; + } + + if (cfgEvent.c1004requireTriggerTVX && !collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { + return kCollRejectTriggerTVX; + } + + // TF / ITS-ROF borders + if (cfgEvent.c1007requireNoITSROFrameBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + return kCollRejectITSROFrameBorder; + } + + if (cfgEvent.c1008requireNoTimeFrameBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + return kCollRejectTimeFrameBorder; + } + + // Vertex quality + if (cfgEvent.c1009requireVertexITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + return kCollRejectVertexITSTPC; + } + + if (cfgEvent.c1010requireGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return kCollRejectGoodZvtxFT0vsPV; + } + + if (cfgEvent.c1011requireVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) { + return kCollRejectVertexTOFmatched; + } + + if (cfgEvent.c1012requireVertexTRDmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTRDmatched)) { + return kCollRejectVertexTRDmatched; + } + + if (cfgEvent.c1013requireGoodITSLayersAll && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + return kCollRejectGoodITSLayersAll; + } + + // Pileup / neighbouring-collision rejection + if (cfgEvent.c1014requireNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return kCollRejectSameBunchPileup; + } + + if (cfgEvent.c1015requireNoCollInTimeRangeStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + return kCollRejectCollInTimeRangeStandard; + } + + if (cfgEvent.c1016requireNoCollInTimeRangeStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStrict)) { + return kCollRejectCollInTimeRangeStrict; + } + + if (cfgEvent.c1017requireNoCollInTimeRangeNarrow && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeNarrow)) { + return kCollRejectCollInTimeRangeNarrow; + } + + if (cfgEvent.c1018requireNoCollInRofStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + return kCollRejectCollInRofStandard; + } + + if (cfgEvent.c1019requireNoCollInRofStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStrict)) { + return kCollRejectCollInRofStrict; + } + + if (cfgEvent.c1020requireNoHighMultCollInPrevRof && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + return kCollRejectHighMultCollInPrevRof; + } + + // INEL classes + if (cfgEvent.c1021requireINELgt0 && !collision.isInelGt0()) { + return kCollRejectINELgt0; + } + + if (cfgEvent.c1022requireINELgt1 && !collision.isInelGt1()) { + return kCollRejectINELgt1; + } + + return kCollAccepted; + } // selCollision is Over. + + template + TrackRejectionTag selectionTrack(T const& track) + { + + // Filter cuts again: resonance daughters are reached through an index into the full track table, + // which does not re-apply the Filter (the resonance producer uses looser cuts). + if (track.pt() <= cfgTrackCuts.c1043ptMin || track.pt() >= cfgTrackCuts.c1044ptMax) { + return kTrackRejectPt; + } + if (std::abs(track.eta()) >= cfgTrackCuts.c1045etaMax) { + return kTrackRejectEta; + } + if (cfgTrackCuts.c1065useFixedDCAxy && std::abs(track.dcaXY()) >= cfgTrackCuts.c1066dcaXYMax) { + return kTrackRejectFixedDCAxy; + } + if (cfgTrackCuts.c1067useFixedDCAz && std::abs(track.dcaZ()) >= cfgTrackCuts.c1068dcaZMax) { + return kTrackRejectFixedDCAz; + } + // Filter cuts are over + + // Standard O2 flags + if (cfgTrackCuts.c1046requireGlobalTrack && !track.isGlobalTrack()) { + return kTrackRejectGlobalTrack; + } + + if (cfgTrackCuts.c1047requireGlobalTrackWoDCA && !track.isGlobalTrackWoDCA()) { + return kTrackRejectGlobalTrackWoDCA; + } + + if (cfgTrackCuts.c1048requirePVContributor && !track.isPVContributor()) { + return kTrackRejectPVContributor; + } + + // Detector presence + if (cfgTrackCuts.c1049requireITS && !track.hasITS()) { + return kTrackRejectITS; + } + + if (cfgTrackCuts.c1050requireTPC && !track.hasTPC()) { + return kTrackRejectTPC; + } + + if (cfgTrackCuts.c1051requireTOF && !track.hasTOF()) { + return kTrackRejectTOF; + } + + if (cfgTrackCuts.c1052requireTRD && !track.hasTRD()) { + return kTrackRejectTRD; + } + + // TPC quality + if (track.tpcNClsFound() < cfgTrackCuts.c1053tpcNClsFoundMin) { + return kTrackRejectTPCNClsFound; + } + + if (track.tpcNClsCrossedRows() < cfgTrackCuts.c1054tpcNClsCrossedRowsMin) { + return kTrackRejectTPCCrossedRows; + } + + if (track.tpcCrossedRowsOverFindableCls() < cfgTrackCuts.c1055tpcCrossedRowsOverFindableMin) { + return kTrackRejectTPCCrossedRowsOverFindable; + } + + if (track.tpcFoundOverFindableCls() < cfgTrackCuts.c1056tpcFoundOverFindableMin) { + return kTrackRejectTPCFoundOverFindable; + } + + if (track.tpcFractionSharedCls() > cfgTrackCuts.c1057tpcFractionSharedMax) { + return kTrackRejectTPCFractionShared; + } + + if (track.tpcChi2NCl() < cfgTrackCuts.c1058tpcChi2NClMin || track.tpcChi2NCl() > cfgTrackCuts.c1059tpcChi2NClMax) { + return kTrackRejectTPCChi2; + } + + // ITS quality + if (track.itsNCls() < cfgTrackCuts.c1060itsNClsMin || track.itsNCls() > cfgTrackCuts.c1061itsNClsMax) { + return kTrackRejectITSNCls; + } + + if (track.itsNClsInnerBarrel() < cfgTrackCuts.c1062itsNClsInnerBarrelMin) { + return kTrackRejectITSNClsInnerBarrel; + } + + if (track.itsChi2NCl() < cfgTrackCuts.c1063itsChi2NClMin || track.itsChi2NCl() > cfgTrackCuts.c1064itsChi2NClMax) { + return kTrackRejectITSChi2; + } + + // pT-dependent DCA + if (cfgTrackCuts.c1069usePtDependentDCAxy) { + const float dcaXYMax = cfgTrackCuts.c1070dcaXYPtA + cfgTrackCuts.c1071dcaXYPtB / std::pow(track.pt(), cfgTrackCuts.c1072dcaXYPtC); + if (std::abs(track.dcaXY()) > dcaXYMax) { + return kTrackRejectPtDependentDCAxy; + } + } + + if (cfgTrackCuts.c1073usePtDependentDCAz) { + const float dcaZMax = cfgTrackCuts.c1074dcaZPtA; + if (std::abs(track.dcaZ()) > dcaZMax) { + return kTrackRejectPtDependentDCAz; + } + } + + return kTrackAccepted; + } + + template + void fillEventQA(const C& collision) + { + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("VertexZ"), collision.posZ()); + + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("CentFT0C"), collision.centFT0C()); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("CentFT0M"), collision.centFT0M()); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("CentFT0A"), collision.centFT0A()); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("CentFV0A"), collision.centFV0A()); + + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("VtxZVsCentFT0C"), collision.posZ(), collision.centFT0C()); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("VtxZVsCentFT0M"), collision.posZ(), collision.centFT0M()); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("VtxZVsCentFT0A"), collision.posZ(), collision.centFT0A()); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("VtxZVsCentFV0A"), collision.posZ(), collision.centFV0A()); + + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("CentFT0CVsCentFT0M"), collision.centFT0C(), collision.centFT0M()); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("CentFT0CVsCentFT0A"), collision.centFT0C(), collision.centFT0A()); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("CentFT0MVsCentFT0A"), collision.centFT0M(), collision.centFT0A()); + + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("CentFT0CVsCentFV0A"), collision.centFT0C(), collision.centFV0A()); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("CentFT0MVsCentFV0A"), collision.centFT0M(), collision.centFV0A()); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("CentFT0AVsCentFV0A"), collision.centFT0A(), collision.centFV0A()); + + const float mixingEstimatorValue = getMixingEstimatorValue(collision); + const int mixingBin = getMixingBin(collision); + const bool validMixingBin = mixingBin >= 0 && mixingBin < nMixBins; + + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("MixingEstimator"), mixingEstimatorValue); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("VtxZVsMixingEstimator"), collision.posZ(), mixingEstimatorValue); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("MixingBinStatus"), validMixingBin ? 1.0f : 0.0f); + + if (validMixingBin) { + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("MixingBin"), mixingBin); + } + } + + template + void fillFullTrackPIDQA(const T& track) + { + float tpcNSigma = 0.0f; + float tofNSigma = 0.0f; + + if constexpr (pidType == kPi) { + tpcNSigma = track.tpcNSigmaPi(); + tofNSigma = track.tofNSigmaPi(); + } else if constexpr (pidType == kKa) { + tpcNSigma = track.tpcNSigmaKa(); + tofNSigma = track.tofNSigmaKa(); + } else if constexpr (pidType == kPr) { + tpcNSigma = track.tpcNSigmaPr(); + tofNSigma = track.tofNSigmaPr(); + } else if constexpr (pidType == kEl) { + tpcNSigma = track.tpcNSigmaEl(); + tofNSigma = track.tofNSigmaEl(); + } else if constexpr (pidType == kMu) { + tpcNSigma = track.tpcNSigmaMu(); + tofNSigma = track.tofNSigmaMu(); + } else if constexpr (pidType == kDe) { + tpcNSigma = track.tpcNSigmaDe(); + tofNSigma = track.tofNSigmaDe(); + } + + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("PID/") + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsP"), track.p(), tpcNSigma); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("PID/") + HIST(PidTypeDire[pidType]) + HIST("TOFNSigmaVsP"), track.p(), tofNSigma); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("PID/") + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsTOFNSigma"), tpcNSigma, tofNSigma); + } + + template + void fillFullTrackQA(const T& track) + { + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("P"), track.p()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("Pt"), track.pt()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("TPCInnerParam"), track.tpcInnerParam()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("TOFExpMom"), track.tofExpMom()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("Eta"), track.eta()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("Phi"), track.phi()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("DCAxy"), track.dcaXY()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("DCAz"), track.dcaZ()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("Sign"), track.sign()); + + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("DCAxyVsP"), track.p(), track.dcaXY()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("DCAxyVsPt"), track.pt(), track.dcaXY()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("DCAxyVsTPCInnerParam"), track.tpcInnerParam(), track.dcaXY()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("DCAxyVsTOFExpMom"), track.tofExpMom(), track.dcaXY()); + + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("DCAzVsP"), track.p(), track.dcaZ()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("DCAzVsPt"), track.pt(), track.dcaZ()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("DCAzVsTPCInnerParam"), track.tpcInnerParam(), track.dcaZ()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("DCAzVsTOFExpMom"), track.tofExpMom(), track.dcaZ()); + + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("PVsPt"), track.p(), track.pt()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("PVsTPCInnerParam"), track.p(), track.tpcInnerParam()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("PVsTOFExpMom"), track.p(), track.tofExpMom()); + + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("TPCSignalVsP"), track.p(), track.tpcSignal()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("TPCSignalVsTPCInnerParam"), track.tpcInnerParam(), track.tpcSignal()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("TPCSignalVsTOFExpMom"), track.tofExpMom(), track.tpcSignal()); + + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("TOFBetaVsP"), track.p(), track.beta()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("TOFBetaVsTPCInnerParam"), track.tpcInnerParam(), track.beta()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("TOFBetaVsTOFExpMom"), track.tofExpMom(), track.beta()); + + fillFullTrackPIDQA(track); + fillFullTrackPIDQA(track); + fillFullTrackPIDQA(track); + fillFullTrackPIDQA(track); + fillFullTrackPIDQA(track); + fillFullTrackPIDQA(track); + } + + template + void fillCorrParticleQA(H& histReg, const T& track) + { + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("P"), track.p()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("Pt"), track.pt()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("Eta"), track.eta()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("Phi"), track.phi()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DCAxy"), track.dcaXY()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DCAz"), track.dcaZ()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("Sign"), track.sign()); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DCAxyVsPt"), track.pt(), track.dcaXY()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DCAzVsPt"), track.pt(), track.dcaZ()); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("TPCSignalVsTPCInnerParam"), track.tpcInnerParam(), track.tpcSignal()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("TPCSignalVsP"), track.p(), track.tpcSignal()); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("TOFBetaVsTOFExpMom"), track.tofExpMom(), track.beta()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("TOFBetaVsP"), track.p(), track.beta()); + } + + template + void fillIdentifiedCorrelation(H& histReg, const T& trigger, const A& associate, const int idMethod) + { + const float dPhi = computeDeltaPhi(associate.phi(), trigger.phi()); + const float dEta = associate.eta() - trigger.eta(); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("P"), associate.p()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Pt"), associate.pt()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Eta"), associate.eta()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Phi"), associate.phi()); + if constexpr (pidType == kPi) { + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Rapidity"), computeRapidity(associate, MassPiPlus)); + } else if constexpr (pidType == kKa) { + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Rapidity"), computeRapidity(associate, MassKPlus)); + } else if constexpr (pidType == kPr) { + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Rapidity"), computeRapidity(associate, MassProton)); + } + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DCAxy"), associate.dcaXY()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DCAz"), associate.dcaZ()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Sign"), associate.sign()); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DCAxyVsPt"), associate.pt(), associate.dcaXY()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DCAzVsPt"), associate.pt(), associate.dcaZ()); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCSignalVsP"), associate.p(), associate.tpcSignal()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TOFBetaVsP"), associate.p(), associate.beta()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("IdMethodVsP"), associate.p(), idMethod); + + if constexpr (pidType == kPi) { + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsP"), associate.p(), associate.tpcNSigmaPi()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TOFNSigmaVsP"), associate.p(), associate.tofNSigmaPi()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsTOFNSigma"), associate.tpcNSigmaPi(), associate.tofNSigmaPi()); + } else if constexpr (pidType == kKa) { + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsP"), associate.p(), associate.tpcNSigmaKa()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TOFNSigmaVsP"), associate.p(), associate.tofNSigmaKa()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsTOFNSigma"), associate.tpcNSigmaKa(), associate.tofNSigmaKa()); + } else if constexpr (pidType == kPr) { + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsP"), associate.p(), associate.tpcNSigmaPr()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TOFNSigmaVsP"), associate.p(), associate.tofNSigmaPr()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsTOFNSigma"), associate.tpcNSigmaPr(), associate.tofNSigmaPr()); + } + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DeltaPhi"), dPhi); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DeltaEta"), dEta); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DeltaPhiDeltaEta"), dPhi, dEta, currentCentrality); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("CorrSparse"), trigger.pt(), associate.pt(), dPhi, dEta, currentCentrality); + } + + //______________________________________________________________________________ + // Resonance candidate QA + template + void fillResoAssocQA(H& histReg, const A& associate, const float invMass) + { + const float p = std::sqrt(associate.px() * associate.px() + associate.py() * associate.py() + associate.pz() * associate.pz()); + const float energy = std::sqrt(p * p + invMass * invMass); + const float rapidity = 0.5f * std::log((energy + associate.pz()) / (energy - associate.pz())); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("P"), p); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("Pt"), associate.pt()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("Eta"), associate.eta()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("Phi"), associate.phi()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("Rapidity"), rapidity); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("InvMass"), invMass); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("InvMassVsPt"), associate.pt(), invMass); + } + + //______________________________________________________________________________ + // Resonance daughter QA + template + void fillResoDauQA(H& histReg, const T& track, const int idMethod) + { + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("P"), track.p()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("Pt"), track.pt()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("Eta"), track.eta()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("Phi"), track.phi()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("DCAxy"), track.dcaXY()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("DCAz"), track.dcaZ()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("Sign"), track.sign()); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("DCAxyVsPt"), track.pt(), track.dcaXY()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("DCAzVsPt"), track.pt(), track.dcaZ()); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("TPCSignalVsP"), track.p(), track.tpcSignal()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("TOFBetaVsP"), track.p(), track.beta()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("IdMethodVsP"), track.p(), idMethod); + + float tpcNSigma = 0.0f; + float tofNSigma = 0.0f; + float massHypothesis = 0.0f; + + if constexpr (pairType == kHPhi || (pairType == kHKStar && dauType == kPosDau) || (pairType == kHKStarBar && dauType == kNegDau) || + (pairType == kHLambda && dauType == kNegDau) || (pairType == kHLambdaBar && dauType == kPosDau)) { + tpcNSigma = track.tpcNSigmaKa(); + tofNSigma = track.tofNSigmaKa(); + massHypothesis = MassKPlus; + } else if constexpr ((pairType == kHKStar && dauType == kNegDau) || (pairType == kHKStarBar && dauType == kPosDau)) { + tpcNSigma = track.tpcNSigmaPi(); + tofNSigma = track.tofNSigmaPi(); + massHypothesis = MassPiPlus; + } else if constexpr ((pairType == kHLambda && dauType == kPosDau) || (pairType == kHLambdaBar && dauType == kNegDau)) { + tpcNSigma = track.tpcNSigmaPr(); + tofNSigma = track.tofNSigmaPr(); + massHypothesis = MassProton; + } + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("Rapidity"), computeRapidity(track, massHypothesis)); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("TPCNSigmaVsP"), track.p(), tpcNSigma); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("TOFNSigmaVsP"), track.p(), tofNSigma); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("TPCNSigmaVsTOFNSigma"), tpcNSigma, tofNSigma); + } + + //______________________________________________________________________________ + // Generic h-h correlation + template + void fillTrigAssociateCorrelation(H& histReg, const T& trigger, const A& associate) + { + const float dPhi = computeDeltaPhi(associate.phi(), trigger.phi()); + const float dEta = associate.eta() - trigger.eta(); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DeltaPhi"), dPhi); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DeltaEta"), dEta); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DeltaPhiDeltaEta"), dPhi, dEta, currentCentrality); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("CorrSparse"), trigger.pt(), associate.pt(), dPhi, dEta, currentCentrality); + } + + //______________________________________________________________________________ + // Resonance correlation + template + void fillResoCorrelation(H& histReg, const T& trigger, const A& associate) + { + const float dPhi = computeDeltaPhi(associate.phi(), trigger.phi()); + const float dEta = associate.eta() - trigger.eta(); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("DeltaPhi"), dPhi); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("DeltaEta"), dEta); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("DeltaPhiDeltaEta"), dPhi, dEta, currentCentrality); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("CorrSparse"), trigger.pt(), associate.pt(), dPhi, dEta, currentCentrality); + } + + // Resonance correlation as a function of invariant mass + template + void fillResoMassCorrelation(H& histReg, const T& trigger, const A& associate, const float invMass) + { + const float dPhi = computeDeltaPhi(associate.phi(), trigger.phi()); + const float dEta = associate.eta() - trigger.eta(); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST("MassDependent/") + HIST("DeltaPhiVsInvMass"), invMass, dPhi, currentCentrality); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST("MassDependent/") + HIST("DeltaEtaVsInvMass"), invMass, dEta, currentCentrality); + } + + template + void fillResoRegion(H& histReg, const T& trigger, const A& associate, const P& posDauTrack, const N& negDauTrack, const float invMass, const int posDauIdMethod, const int negDauIdMethod) + { + fillResoAssocQA(histReg, associate, invMass); + fillResoDauQA(histReg, posDauTrack, posDauIdMethod); + fillResoDauQA(histReg, negDauTrack, negDauIdMethod); + fillResoCorrelation(histReg, trigger, associate); + } + + template + bool selectResonanceDaughters(const P& posDauTrack, const N& negDauTrack, int& posDauIdMethod, int& negDauIdMethod) + { + // Phi -> K+ K- ----------------------- + // K(892)* -> K+ + pi- ----------------------- + // K(892)*Bar -> K- + pi+ ----------------------- + // Λ(1520) -> P+ + Ka- ----------------------- + // Λ(1520)Bar -> PBar + Ka+ ----------------------- + + posDauIdMethod = kUnidentified; + negDauIdMethod = kUnidentified; + if constexpr (pairType == kHPhi) { + return selKaon(posDauTrack, posDauIdMethod) && selKaon(negDauTrack, negDauIdMethod); + } else if constexpr (pairType == kHKStar) { + return selKaon(posDauTrack, posDauIdMethod) && selPion(negDauTrack, negDauIdMethod); + } else if constexpr (pairType == kHKStarBar) { + return selPion(posDauTrack, posDauIdMethod) && selKaon(negDauTrack, negDauIdMethod); + } else if constexpr (pairType == kHLambda) { + return selProton(posDauTrack, posDauIdMethod) && selKaon(negDauTrack, negDauIdMethod); + } else if constexpr (pairType == kHLambdaBar) { + return selKaon(posDauTrack, posDauIdMethod) && selProton(negDauTrack, negDauIdMethod); + } + return false; + } + + template + uint8_t getResonanceMassRegion(const A& associate, float& invMass) + { + if constexpr (pairType == kHPhi) { + invMass = associate.mPhi1020(); + return getMassRegionTag(invMass, cfgPhi1020CorrMass.c1117phi1020LSBLow, cfgPhi1020CorrMass.c1118phi1020LSBUp, cfgPhi1020CorrMass.c1115phi1020PeakLow, cfgPhi1020CorrMass.c1116phi1020PeakUp, cfgPhi1020CorrMass.c1119phi1020RSBLow, cfgPhi1020CorrMass.c1120phi1020RSBUp); + } else if constexpr (pairType == kHKStar) { + invMass = associate.mKStar892(); + return getMassRegionTag(invMass, cfgKStar892CorrMass.c1123kStar892LSBLow, cfgKStar892CorrMass.c1124kStar892LSBUp, cfgKStar892CorrMass.c1121kStar892PeakLow, cfgKStar892CorrMass.c1122kStar892PeakUp, cfgKStar892CorrMass.c1125kStar892RSBLow, cfgKStar892CorrMass.c1126kStar892RSBUp); + } else if constexpr (pairType == kHKStarBar) { + invMass = associate.mKStar892Bar(); + return getMassRegionTag(invMass, cfgKStar892CorrMass.c1123kStar892LSBLow, cfgKStar892CorrMass.c1124kStar892LSBUp, cfgKStar892CorrMass.c1121kStar892PeakLow, cfgKStar892CorrMass.c1122kStar892PeakUp, cfgKStar892CorrMass.c1125kStar892RSBLow, cfgKStar892CorrMass.c1126kStar892RSBUp); + } else if constexpr (pairType == kHLambda) { + invMass = associate.mLambda1520(); + return getMassRegionTag(invMass, cfgLambda1520CorrMass.c1129lambda1520LSBLow, cfgLambda1520CorrMass.c1130lambda1520LSBUp, cfgLambda1520CorrMass.c1127lambda1520PeakLow, cfgLambda1520CorrMass.c1128lambda1520PeakUp, cfgLambda1520CorrMass.c1131lambda1520RSBLow, cfgLambda1520CorrMass.c1132lambda1520RSBUp); + } else if constexpr (pairType == kHLambdaBar) { + invMass = associate.mLambda1520Bar(); + return getMassRegionTag(invMass, cfgLambda1520CorrMass.c1129lambda1520LSBLow, cfgLambda1520CorrMass.c1130lambda1520LSBUp, cfgLambda1520CorrMass.c1127lambda1520PeakLow, cfgLambda1520CorrMass.c1128lambda1520PeakUp, cfgLambda1520CorrMass.c1131lambda1520RSBLow, cfgLambda1520CorrMass.c1132lambda1520RSBUp); + } + + invMass = -1.0f; + return kMassOutside; + } + + template + void incrementCorrCount(CorrCountArray& counts) + { + constexpr int PtType = roleType == kAssocLowPt ? kCountLowPt : kCountHighPt; + + if constexpr (pairType == kHH) { + ++counts[kCountHH][PtType]; + } else if constexpr (pairType == kHId && pidType == kPi) { + ++counts[kCountHPi][PtType]; + } else if constexpr (pairType == kHId && pidType == kKa) { + ++counts[kCountHKa][PtType]; + } else if constexpr (pairType == kHId && pidType == kPr) { + ++counts[kCountHPr][PtType]; + } else if constexpr (pairType == kHPhi) { + ++counts[kCountHPhi][PtType]; + } else if constexpr (pairType == kHKStar) { + ++counts[kCountHKStar][PtType]; + } else if constexpr (pairType == kHKStarBar) { + ++counts[kCountHKStarBar][PtType]; + } else if constexpr (pairType == kHLambda) { + ++counts[kCountHLambda][PtType]; + } else if constexpr (pairType == kHLambdaBar) { + ++counts[kCountHLambdaBar][PtType]; + } + } + + template + void incrementResoRegionCount(ResoRegionCountArray& counts, uint8_t massRegion) + { + constexpr int PtType = roleType == kAssocLowPt ? kCountLowPt : kCountHighPt; + + if (massRegion != kMassPeak && massRegion != kMassLSB && massRegion != kMassRSB) { + return; + } + + if constexpr (pairType == kHPhi) { + ++counts[kResoCountHPhi][PtType][massRegion]; + } else if constexpr (pairType == kHKStar) { + ++counts[kResoCountHKStar][PtType][massRegion]; + } else if constexpr (pairType == kHKStarBar) { + ++counts[kResoCountHKStarBar][PtType][massRegion]; + } else if constexpr (pairType == kHLambda) { + ++counts[kResoCountHLambda][PtType][massRegion]; + } else if constexpr (pairType == kHLambdaBar) { + ++counts[kResoCountHLambdaBar][PtType][massRegion]; + } + } + + template + float getInvariantMass(const T1& dau1, const T2& dau2) + { + const float p = RecoDecay::p(dau1.px() + dau2.px(), dau1.py() + dau2.py(), dau1.pz() + dau2.pz()); + + if constexpr (pairType == kHPhi) { + return RecoDecay::m(p, RecoDecay::e(dau1.px(), dau1.py(), dau1.pz(), MassKPlus) + RecoDecay::e(dau2.px(), dau2.py(), dau2.pz(), MassKPlus)); + } else if constexpr (pairType == kHKStar) { + return RecoDecay::m(p, RecoDecay::e(dau1.px(), dau1.py(), dau1.pz(), MassKPlus) + RecoDecay::e(dau2.px(), dau2.py(), dau2.pz(), MassPiPlus)); + } else if constexpr (pairType == kHKStarBar) { + return RecoDecay::m(p, RecoDecay::e(dau1.px(), dau1.py(), dau1.pz(), MassPiPlus) + RecoDecay::e(dau2.px(), dau2.py(), dau2.pz(), MassKPlus)); + } else if constexpr (pairType == kHLambda) { + return RecoDecay::m(p, RecoDecay::e(dau1.px(), dau1.py(), dau1.pz(), MassProton) + RecoDecay::e(dau2.px(), dau2.py(), dau2.pz(), MassKPlus)); + } else if constexpr (pairType == kHLambdaBar) { + return RecoDecay::m(p, RecoDecay::e(dau1.px(), dau1.py(), dau1.pz(), MassKPlus) + RecoDecay::e(dau2.px(), dau2.py(), dau2.pz(), MassProton)); + } + + return -1.0f; + } + + template + bool fillInvariantMassPair(const C& collision, const T1& dau1, const T2& dau2) + { + int dau1IdMethod = kUnidentified; + int dau2IdMethod = kUnidentified; + + if (!selectResonanceDaughters(dau1, dau2, dau1IdMethod, dau2IdMethod)) { + return false; + } + + const float mass = getInvariantMass(dau1, dau2); + const float pt = RecoDecay::pt(dau1.px() + dau2.px(), dau1.py() + dau2.py()); + const float pz = dau1.pz() + dau2.pz(); + const float energy = RecoDecay::e(dau1.px() + dau2.px(), dau1.py() + dau2.py(), pz, mass); + const float rapidity = 0.5f * std::log((energy + pz) / (energy - pz)); + if (rapidity < cfgResPartitions.c1083resoRapidityMin || rapidity > cfgResPartitions.c1084resoRapidityMax) { + return false; + } + + float centrality = -1.0f; + switch (cfgEvent.c1028centralityEstimator) { + case 0: + centrality = collision.centFT0C(); + break; + case 1: + centrality = collision.centFT0M(); + break; + case 2: + centrality = collision.centFT0A(); + break; + case 3: + centrality = collision.centFV0A(); + break; + default: + return false; + } + + if constexpr (pairType == kHPhi && !isLikeSign) { + resonanceMassSpectra.fill(HIST("SE/InvariantMass/Phi/US"), mass, pt, centrality); + } else if constexpr (pairType == kHPhi && isLikeSign) { + resonanceMassSpectra.fill(HIST("SE/InvariantMass/Phi/LS"), mass, pt, centrality); + } else if constexpr (pairType == kHKStar && !isLikeSign) { + resonanceMassSpectra.fill(HIST("SE/InvariantMass/KStar/US"), mass, pt, centrality); + } else if constexpr (pairType == kHKStar && isLikeSign) { + resonanceMassSpectra.fill(HIST("SE/InvariantMass/KStar/LS"), mass, pt, centrality); + } else if constexpr (pairType == kHKStarBar && !isLikeSign) { + resonanceMassSpectra.fill(HIST("SE/InvariantMass/KStarBar/US"), mass, pt, centrality); + } else if constexpr (pairType == kHKStarBar && isLikeSign) { + resonanceMassSpectra.fill(HIST("SE/InvariantMass/KStarBar/LS"), mass, pt, centrality); + } else if constexpr (pairType == kHLambda && !isLikeSign) { + resonanceMassSpectra.fill(HIST("SE/InvariantMass/Lambda1520/US"), mass, pt, centrality); + } else if constexpr (pairType == kHLambda && isLikeSign) { + resonanceMassSpectra.fill(HIST("SE/InvariantMass/Lambda1520/LS"), mass, pt, centrality); + } else if constexpr (pairType == kHLambdaBar && !isLikeSign) { + resonanceMassSpectra.fill(HIST("SE/InvariantMass/Lambda1520Bar/US"), mass, pt, centrality); + } else if constexpr (pairType == kHLambdaBar && isLikeSign) { + resonanceMassSpectra.fill(HIST("SE/InvariantMass/Lambda1520Bar/LS"), mass, pt, centrality); + } + + return true; + } + + template + void fillUnlikeSignInvariantMass(const C& collision, const P& posTracksPerColl, const N& negTracksPerColl) + { + for (const auto& posTrack : posTracksPerColl) { + if (selectionTrack(posTrack) != kTrackAccepted) { + continue; + } + + for (const auto& negTrack : negTracksPerColl) { + if (selectionTrack(negTrack) != kTrackAccepted) { + continue; + } + + fillInvariantMassPair(collision, posTrack, negTrack); + fillInvariantMassPair(collision, posTrack, negTrack); + fillInvariantMassPair(collision, posTrack, negTrack); + fillInvariantMassPair(collision, posTrack, negTrack); + fillInvariantMassPair(collision, posTrack, negTrack); + } + } + } + + template + void fillLikeSignInvariantMass(const C& collision, const T& tracksPerColl) + { + for (const auto& track1 : tracksPerColl) { + if (selectionTrack(track1) != kTrackAccepted) { + continue; + } + + for (const auto& track2 : tracksPerColl) { + if (track2.globalIndex() <= track1.globalIndex()) { + continue; + } + if (selectionTrack(track2) != kTrackAccepted) { + continue; + } + + fillInvariantMassPair(collision, track1, track2); + + if constexpr (positiveSign) { + if (!fillInvariantMassPair(collision, track1, track2)) { + fillInvariantMassPair(collision, track2, track1); + } + if (!fillInvariantMassPair(collision, track1, track2)) { + fillInvariantMassPair(collision, track2, track1); + } + } else { + if (!fillInvariantMassPair(collision, track1, track2)) { + fillInvariantMassPair(collision, track2, track1); + } + if (!fillInvariantMassPair(collision, track1, track2)) { + fillInvariantMassPair(collision, track2, track1); + } + } + } + } + } + + uint64_t getStoredCorrMask(uint64_t corrMask, int mixingBin) + { + if (mixingBin < 0 || mixingBin >= nMixBins) { + return 0ULL; + } + + if (cfgDerivedData.c1134mixingBin >= 0 && mixingBin != cfgDerivedData.c1134mixingBin) { + return 0ULL; + } + + const uint64_t requestedMask = cfgDerivedData.c1133pairMask; + if (requestedMask == 0ULL) { + return corrMask; + } + + if (cfgDerivedData.c1135requireAllPairBits && (corrMask & requestedMask) != requestedMask) { + return 0ULL; + } + + // Keeping only the requested pools, so tracks / resonances of other channels are not written. + // Resonance pools are the Peak/LSB/RSB bits (kMaskHPhiLowPtPeak ... kMaskHLambdaBarHighPtRSB). + return corrMask & requestedMask; + } + + //______________________________________________________________________________ + // Tracks : bit 0-7 = pools h, pi, K, p x low/high pT (same bit numbers as CorrPresenceMask), + // bit 15 = used as trigger + // Resonances : bit r = CorrPresenceMask pool bit (18 + r), r = species * 6 + pT class * 3 + (mass window - 1) + // Setting a bit again (e.g. for another trigger) changes nothing, so there are no duplicates, + // and no sorting or searching is needed. Re-sized per collision; the vectors keep their capacity. + static constexpr uint16_t TriggerUsedFlag = 1 << 15; + std::vector trackUsedFlags; + std::vector resoUsedFlags; + int64_t firstTrackGI = 0; + int64_t firstResoGI = 0; + float currentCentrality = -1.0f; // mixing-estimator value of the current collision, used as centrality axis of the correlation histograms + + template + void markTrackUsed(const T& track, const uint16_t flag) + { + const int64_t i = track.globalIndex() - firstTrackGI; + if (i >= 0 && i < static_cast(trackUsedFlags.size())) { + trackUsedFlags[i] |= flag; + } + } + + template + void markResoUsed(const R& resonance, const uint32_t flag) + { + const int64_t i = resonance.globalIndex() - firstResoGI; + if (i >= 0 && i < static_cast(resoUsedFlags.size())) { + resoUsedFlags[i] |= flag; + } + } + + // Particle (+1) / antiparticle (-1) / self-conjugate (0) flag from the associate type + static int8_t getResoSign(const int assocType) + { + if (assocType == kCountHKStar || assocType == kCountHLambda) { + return 1; + } + if (assocType == kCountHKStarBar || assocType == kCountHLambdaBar) { + return -1; + } + return 0; // phi(1020) + } + + // 01-Start-obtaining trigger-LowPtAssociates correlation + template + void executeTrigAssocCorrelation(H& histReg, const T& trigger, const A& associates, const auto& triggerGIList, CorrCountArray& corrCountPerColl, ResoRegionCountArray& resoRegionCountsPerColl) + { + constexpr int PtType = roleType == kAssocLowPt ? kCountLowPt : kCountHighPt; + int associateSelTag = kTrackAccepted; + int assocPosDauSelTag = kTrackAccepted; + int assocNegDauSelTag = kTrackAccepted; + int posDauIdMethod = kUnidentified; + int negDauIdMethod = kUnidentified; + int pionIdMethod = kUnidentified; + int kaonIdMethod = kUnidentified; + int protonIdMethod = kUnidentified; + + for (const auto& associate : associates) { + //__________________________________________________________________________ + // Hadron-hadron and hadron-Identified correlation + if constexpr (pairType == kHH || pairType == kHId) { + associateSelTag = selectionTrack(associate); + if (associateSelTag != kTrackAccepted) { + continue; + } + + if constexpr (pairType == kHH) { + fillCorrParticleQA(histReg, associate); + fillTrigAssociateCorrelation(histReg, trigger, associate); + incrementCorrCount(corrCountPerColl); + markTrackUsed(associate, static_cast(getCorrMaskBit(kCountHH, PtType))); + } + + if constexpr (pairType == kHId) { + pionIdMethod = kUnidentified; + kaonIdMethod = kUnidentified; + protonIdMethod = kUnidentified; + + if (selPion(associate, pionIdMethod)) { + fillIdentifiedCorrelation(histReg, trigger, associate, pionIdMethod); + incrementCorrCount(corrCountPerColl); + markTrackUsed(associate, static_cast(getCorrMaskBit(kCountHPi, PtType))); + } + if (selKaon(associate, kaonIdMethod)) { + fillIdentifiedCorrelation(histReg, trigger, associate, kaonIdMethod); + incrementCorrCount(corrCountPerColl); + markTrackUsed(associate, static_cast(getCorrMaskBit(kCountHKa, PtType))); + } + if (selProton(associate, protonIdMethod)) { + fillIdentifiedCorrelation(histReg, trigger, associate, protonIdMethod); + incrementCorrCount(corrCountPerColl); + markTrackUsed(associate, static_cast(getCorrMaskBit(kCountHPr, PtType))); + } + } + } + + //__________________________________________________________________________ + // Hadron-Resonance correlation + if constexpr (pairType == kHPhi || pairType == kHKStar || pairType == kHKStarBar || pairType == kHLambda || pairType == kHLambdaBar) { + const auto& posDauTrack = associate.template posTrack_as(); + const auto& negDauTrack = associate.template negTrack_as(); + + // Track Selection ------------------------ + assocPosDauSelTag = selectionTrack(posDauTrack); + if (assocPosDauSelTag != kTrackAccepted) { + continue; + } + + assocNegDauSelTag = selectionTrack(negDauTrack); + if (assocNegDauSelTag != kTrackAccepted) { + continue; + } + + if (!selectResonanceDaughters(posDauTrack, negDauTrack, posDauIdMethod, negDauIdMethod)) { + continue; + } + + float invMass = -1.0f; + const uint8_t massRegion = getResonanceMassRegion(associate, invMass); + const float rapidity = computeRapidity(associate, invMass); + if (rapidity < cfgResPartitions.c1083resoRapidityMin || rapidity > cfgResPartitions.c1084resoRapidityMax) { + continue; + } + + // Trigger-resonance daughter overlap -----expensive check ---- + if (c1113rejectResoWithAnyTriggerDaughter) { + // Strict mode: reject the whole resonance if either daughter belongs to the event trigger population. + if (checkTrackInList(posDauTrack, triggerGIList) || checkTrackInList(negDauTrack, triggerGIList)) { + continue; + } + } else { + // Standard pair-wise autocorrelation removal: reject only if the CURRENT trigger is a daughter. + if (trigger.globalIndex() == posDauTrack.globalIndex() || trigger.globalIndex() == negDauTrack.globalIndex()) { + continue; + } + } + + fillResoMassCorrelation(histReg, trigger, associate, invMass); + + if (massRegion == kMassOutside) { + continue; + } + + switch (massRegion) { + case kMassPeak: + fillResoRegion(histReg, trigger, associate, posDauTrack, negDauTrack, invMass, posDauIdMethod, negDauIdMethod); + break; + case kMassLSB: + fillResoRegion(histReg, trigger, associate, posDauTrack, negDauTrack, invMass, posDauIdMethod, negDauIdMethod); + break; + case kMassRSB: + fillResoRegion(histReg, trigger, associate, posDauTrack, negDauTrack, invMass, posDauIdMethod, negDauIdMethod); + break; + default: + continue; + } + + incrementCorrCount(corrCountPerColl); + incrementResoRegionCount(resoRegionCountsPerColl, massRegion); + markResoUsed(associate, static_cast(getResoRegionMaskBit(pairType - kHPhi, PtType, massRegion) >> 18)); + } + } // associate particle loop is over + } // function definition is over + + template + void executeCorrelation(H& histReg, const T& triggers, const A& associatesLowPt, const B& associatesHighPt, CorrCountArray& corrCountPerColl, ResoRegionCountArray& resoRegionCountsPerColl) + { + // Trigger Selection + // int nTrigger = 0; + int triggerSelTag = kTrackAccepted; + + // Option 1 : Trigger tracks wont be accepted as daughter track of reconstructed resonance. + // Option 2 : + std::vector triggerGIList; + std::vector posDauGIListPeak; + std::vector negDauGIListPeak; + std::vector posDauGIListLSB; + std::vector negDauGIListLSB; + std::vector posDauGIListRSSB; + std::vector negDauGIListRSSB; + + if (c1113rejectResoWithAnyTriggerDaughter) { + for (const auto& trigger : triggers) { + if (selectionTrack(trigger) != kTrackAccepted) { + continue; + } + triggerGIList.push_back(trigger.globalIndex()); + } + } + + for (const auto& trigger : triggers) { + triggerSelTag = selectionTrack(trigger); + if (triggerSelTag != kTrackAccepted) { + continue; + } + // fillTrigQA + fillCorrParticleQA(histReg, trigger); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[kTrigger]) + HIST("PtVsCentrality"), trigger.pt(), currentCentrality); + markTrackUsed(trigger, TriggerUsedFlag); + // ++nTrigger; + + // triggerTrackIndexList.push_back(triggerTrack.globalIndex()); + executeTrigAssocCorrelation(histReg, trigger, associatesLowPt, triggerGIList, corrCountPerColl, resoRegionCountsPerColl); + executeTrigAssocCorrelation(histReg, trigger, associatesHighPt, triggerGIList, corrCountPerColl, resoRegionCountsPerColl); + } // trigger particle loop + } // execute Correlation Function is over + + // Event Filter + Filter eventFilter = (!cfgEvent.c1003requireSel8) || (o2::aod::evsel::sel8 == true); + Filter posZFilter = (nabs(o2::aod::collision::posZ) < cfgEvent.c1002cutZvertex); + + // Track Filter + Filter ptFilter = (o2::aod::track::pt > cfgTrackCuts.c1043ptMin) && (o2::aod::track::pt < cfgTrackCuts.c1044ptMax); + Filter etaFilter = (nabs(o2::aod::track::eta) < cfgTrackCuts.c1045etaMax); + Filter dcaFilter = ((!cfgTrackCuts.c1065useFixedDCAxy) || (nabs(o2::aod::track::dcaXY) < cfgTrackCuts.c1066dcaXYMax)) && + ((!cfgTrackCuts.c1067useFixedDCAz) || (nabs(o2::aod::track::dcaZ) < cfgTrackCuts.c1068dcaZMax)); + using MyFilteredCollisions = soa::Filtered>; // , + // using MyFilteredTracks = soa::Filtered>; + using MyFilteredTracks = soa::Filtered>; + // For manual sliceBy + Preslice tracksPerCollisionPreslice = o2::aod::track::collisionId; + Preslice resoPerCollisionPreslice = aod::resonancecndt::collisionId; // registers RESONCNDTS/fIndexCollisions in the SliceCache + + // // definition of partitions + SliceCache cache; + Partition triggerTracks = cfgPartitions.c1077triggerPtLow < aod::track::pt && aod::track::pt < cfgPartitions.c1078triggerPtHigh; + Partition posTracks = aod::track::signed1Pt > 0.0f; + Partition negTracks = aod::track::signed1Pt < 0.0f; + + Partition assocTracksLowPt = cfgPartitions.c1079assocPtLowMin < aod::track::pt && aod::track::pt < cfgPartitions.c1080assocPtLowMax; + Partition assocTracksHighPt = cfgPartitions.c1081assocPtHighMin < aod::track::pt && aod::track::pt < cfgPartitions.c1082assocPtHighMax; + + Partition assocPhiLowPt = (cfgResPartitions.c1085phiPtLowMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.c1086phiPtLowMax) && (aod::resonancecndt::phi1020Tag != static_cast(kMassOutside)); + Partition assocPhiHighPt = (cfgResPartitions.c1087phiPtHighMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.c1088phiPtHighMax) && (aod::resonancecndt::phi1020Tag != static_cast(kMassOutside)); + + Partition assocKStarLowPt = (cfgResPartitions.c1089kstarPtLowMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.c1090kstarPtLowMax) && (aod::resonancecndt::kStar892Tag != static_cast(kMassOutside)); + Partition assocKStarHighPt = (cfgResPartitions.c1091kstarPtHighMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.c1092kstarPtHighMax) && (aod::resonancecndt::kStar892Tag != static_cast(kMassOutside)); + + Partition assocKStarBarLowPt = (cfgResPartitions.c1093kstarBarPtLowMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.c1094kstarBarPtLowMax) && (aod::resonancecndt::kStar892BarTag != static_cast(kMassOutside)); + Partition assocKStarBarHighPt = (cfgResPartitions.c1095kstarBarPtHighMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.c1096kstarBarPtHighMax) && (aod::resonancecndt::kStar892BarTag != static_cast(kMassOutside)); + + Partition assocLambda1520LowPt = (cfgResPartitions.c1097lambda1520PtLowMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.c1098lambda1520PtLowMax) && (aod::resonancecndt::lambda1520Tag != static_cast(kMassOutside)); + Partition assocLambda1520HighPt = (cfgResPartitions.c1099lambda1520PtHighMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.c1100lambda1520PtHighMax) && (aod::resonancecndt::lambda1520Tag != static_cast(kMassOutside)); + + Partition assocLambda1520BarLowPt = (cfgResPartitions.c1101lambda1520BarPtLowMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.c1102lambda1520BarPtLowMax) && (aod::resonancecndt::lambda1520BarTag != static_cast(kMassOutside)); + Partition assocLambda1520BarHighPt = (cfgResPartitions.c1103lambda1520BarPtHighMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.c1104lambda1520BarPtHighMax) && (aod::resonancecndt::lambda1520BarTag != static_cast(kMassOutside)); + + int dfNumber = 0; + int64_t iCollGlobalCount = -1; + + void processNothing(aod::Origins const& origins) + { + if (cfgDebug.c1001printDebugMessages) { + LOG(info) << "DEBUG :: Process Nothing :: df_" << dfNumber << " :: origins = " << origins.size(); + } + // Intentionally empty. + // Keeps the task alive when running purely on derived data. + } + PROCESS_SWITCH(HParticleCorrelation2SameEvent, processNothing, "Dummy process for derived-data analysis", true); + + void processSameEvent(MyFilteredCollisions const& collisions, MyFilteredTracks const& fullTracks, aod::ResonanceCndts const& resonanceCndts, o2::aod::Origins const& Origins, aod::BCsWithTimestamps const&) + { + dfNumber++; + mixingQA.fill(HIST("Mixing/DataFramesProcessed"), 0.0); + std::vector mixingBinStatusPerDF(nMixBins); + uint64_t dataframeID = 0; + + if (cfgDerivedData.c1136resetGlobalCountersPerDF) { + iCollGlobalCount = -1; + } + + if (Origins.size() == 0) { + dataframeID = std::numeric_limits::max() - static_cast(dfNumber); + LOG(warn) << "Origins table is empty. Using synthetic dataframeID = " << dataframeID; + } else { + dataframeID = Origins.iteratorAt(0).dataframeID(); + } + + if (cfgDebug.c1001printDebugMessages) { + LOG(info) << "DEBUG :: df_" << dfNumber << " :: Origins = " << Origins.size() << " :: dataframeID = " << dataframeID; + } + + assocPhiLowPt.bindExternalIndices(&fullTracks); + assocPhiHighPt.bindExternalIndices(&fullTracks); + assocKStarLowPt.bindExternalIndices(&fullTracks); + assocKStarHighPt.bindExternalIndices(&fullTracks); + assocKStarBarLowPt.bindExternalIndices(&fullTracks); + assocKStarBarHighPt.bindExternalIndices(&fullTracks); + assocLambda1520LowPt.bindExternalIndices(&fullTracks); + assocLambda1520HighPt.bindExternalIndices(&fullTracks); + assocLambda1520BarLowPt.bindExternalIndices(&fullTracks); + assocLambda1520BarHighPt.bindExternalIndices(&fullTracks); + + if (collisions.size() == 0) { + seEventQA.fill(HIST("SE/Events/DataFrameQA"), kDFWithZeroFilteredColls); + + if (cfgDebug.c1001printDebugMessages) { + LOG(info) << "DEBUG :: df_" << dfNumber << " :: SE :: collisions = 0 :: No filtered collisions found in this dataframe"; + } + } else if (cfgDebug.c1001printDebugMessages) { + auto bc = collisions.iteratorAt(0).bc_as(); + int currentRunNumber = bc.runNumber(); + + LOG(info) << "DEBUG :: df_" << dfNumber << " :: SE :: currentRunNumber = " << currentRunNumber << " :: " << collisions.iteratorAt(0).bc_as().runNumber(); + LOG(info) << "DEBUG :: df_" << dfNumber << " :: SE :: collisions = " << collisions.size() << " :: fullTracks = " << fullTracks.size() << " :: resonanceCndts = " << resonanceCndts.size(); + } + + int nTrack = 0; + CollisionRejectionTag collisionTag = kCollAccepted; + TrackRejectionTag trackTag = kTrackAccepted; + + for (const auto& collision : collisions) { // CollisionLoop-Start + seEventQA.fill(HIST("SE/Events/EventSelection"), static_cast(kEventAllFiltered)); + fillEventQA(collision); + + collisionTag = selCollision(collision); + seEventQA.fill(HIST("SE/Events/CollisionRejection"), static_cast(collisionTag)); + if (collisionTag != kCollAccepted) { + continue; + } + seEventQA.fill(HIST("SE/Events/EventSelection"), static_cast(kEventPassedSelCollision)); + fillEventQA(collision); + const auto tracksPerCollision = fullTracks.sliceBy(tracksPerCollisionPreslice, collision.globalIndex()); + if (tracksPerCollision.size() < cfgEvent.c1005minNFilteredTracks) { + continue; + } + seEventQA.fill(HIST("SE/Events/EventSelection"), static_cast(kEventPassedMinFilteredTracks)); + + nTrack = 0; + for (const auto& track : tracksPerCollision) { + seTrackQA.fill(HIST("SE/Tracks/TrackSelection"), static_cast(kTrackAllFiltered)); + fillFullTrackQA(track); + + trackTag = selectionTrack(track); + seTrackQA.fill(HIST("SE/Tracks/TrackRejection"), static_cast(trackTag)); + + if (trackTag != kTrackAccepted) { + continue; + } + + seTrackQA.fill(HIST("SE/Tracks/TrackSelection"), static_cast(kTrackPassedSelectionTrack)); + fillFullTrackQA(track); + nTrack++; + } // track loop is over. + + if (nTrack < cfgEvent.c1006minNSelectedTracks) { + continue; + } + + const auto resonancesPerCollision = resonanceCndts.sliceBy(resoPerCollisionPreslice, collision.globalIndex()); + trackUsedFlags.clear(); + resoUsedFlags.clear(); + if (tracksPerCollision.size() > 0) { + firstTrackGI = tracksPerCollision.begin().globalIndex(); + trackUsedFlags.assign(tracksPerCollision.iteratorAt(tracksPerCollision.size() - 1).globalIndex() - firstTrackGI + 1, 0); + } + if (resonancesPerCollision.size() > 0) { + firstResoGI = resonancesPerCollision.begin().globalIndex(); + resoUsedFlags.assign(resonancesPerCollision.iteratorAt(resonancesPerCollision.size() - 1).globalIndex() - firstResoGI + 1, 0); + } + + fillEventQA(collision); + CorrCountArray corrCountsPerColl{}; + ResoRegionCountArray resoRegionCountsPerColl{}; + + seEventQA.fill(HIST("SE/Events/EventSelection"), static_cast(kEventPassedMinSelectedTracks)); + + // Get the Partitions + auto triggerTracksPerColl = triggerTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + + auto assocTrksPerCollLowPt = assocTracksLowPt->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + auto assocTrksPerCollHighPt = assocTracksHighPt->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + + auto assocPhiPerCollLowPt = assocPhiLowPt->sliceByCached(aod::resonancecndt::collisionId, collision.globalIndex(), cache); + auto assocPhiPerCollHighPt = assocPhiHighPt->sliceByCached(aod::resonancecndt::collisionId, collision.globalIndex(), cache); + + auto assocKStarPerCollLowPt = assocKStarLowPt->sliceByCached(aod::resonancecndt::collisionId, collision.globalIndex(), cache); + auto assocKStarPerCollHighPt = assocKStarHighPt->sliceByCached(aod::resonancecndt::collisionId, collision.globalIndex(), cache); + + auto assocKStarBarPerCollLowPt = assocKStarBarLowPt->sliceByCached(aod::resonancecndt::collisionId, collision.globalIndex(), cache); + auto assocKStarBarPerCollHighPt = assocKStarBarHighPt->sliceByCached(aod::resonancecndt::collisionId, collision.globalIndex(), cache); + + auto assocLambdaPerCollLowPt = assocLambda1520LowPt->sliceByCached(aod::resonancecndt::collisionId, collision.globalIndex(), cache); + auto assocLambdaPerCollHighPt = assocLambda1520HighPt->sliceByCached(aod::resonancecndt::collisionId, collision.globalIndex(), cache); + + auto assocLambdaBarPerCollLowPt = assocLambda1520BarLowPt->sliceByCached(aod::resonancecndt::collisionId, collision.globalIndex(), cache); + auto assocLambdaBarPerCollHighPt = assocLambda1520BarHighPt->sliceByCached(aod::resonancecndt::collisionId, collision.globalIndex(), cache); + + currentCentrality = getMixingEstimatorValue(collision); + executeCorrelation(hhCorrelation, triggerTracksPerColl, assocTrksPerCollLowPt, assocTrksPerCollHighPt, corrCountsPerColl, resoRegionCountsPerColl); + executeCorrelation(hIdCorrelation, triggerTracksPerColl, assocTrksPerCollLowPt, assocTrksPerCollHighPt, corrCountsPerColl, resoRegionCountsPerColl); + executeCorrelation(hPhiCorrelation, triggerTracksPerColl, assocPhiPerCollLowPt, assocPhiPerCollHighPt, corrCountsPerColl, resoRegionCountsPerColl); + executeCorrelation(hKStarCorrelation, triggerTracksPerColl, assocKStarPerCollLowPt, assocKStarPerCollHighPt, corrCountsPerColl, resoRegionCountsPerColl); + executeCorrelation(hKStarBarCorrelation, triggerTracksPerColl, assocKStarBarPerCollLowPt, assocKStarBarPerCollHighPt, corrCountsPerColl, resoRegionCountsPerColl); + executeCorrelation(hLambdaCorrelation, triggerTracksPerColl, assocLambdaPerCollLowPt, assocLambdaPerCollHighPt, corrCountsPerColl, resoRegionCountsPerColl); + executeCorrelation(hLambdaBarCorrelation, triggerTracksPerColl, assocLambdaBarPerCollLowPt, assocLambdaBarPerCollHighPt, corrCountsPerColl, resoRegionCountsPerColl); + + // Unlike Sign Signal + // LikeSign Signal + bool hasSelectedTrigger = false; + if (c1114requireSelectedTriggerForInvariantMass) { + for (const auto& trigger : triggerTracksPerColl) { + if (selectionTrack(trigger) == kTrackAccepted) { + hasSelectedTrigger = true; + break; + } + } + } + + if (!c1114requireSelectedTriggerForInvariantMass || hasSelectedTrigger) { + auto posTracksPerColl = posTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + auto negTracksPerColl = negTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + + fillUnlikeSignInvariantMass(collision, posTracksPerColl, negTracksPerColl); + fillLikeSignInvariantMass(collision, posTracksPerColl); + fillLikeSignInvariantMass(collision, negTracksPerColl); + } + + //______________________________________________________________________________ + // Event-mixing information for this collision + uint64_t corrPresenceMask = 0ULL; + const int mixingBin = getMixingBin(collision); + const float mixingEstimatorValue = getMixingEstimatorValue(collision); + const bool validMixingBin = mixingBin >= 0 && mixingBin < nMixBins; + + if (validMixingBin) { + ++mixingBinStatusPerDF[mixingBin].nCollisions; + mixingQA.fill(HIST("Mixing/AllCollisionsPerBin"), mixingBin); + mixingQA.fill(HIST("Mixing/AllCollisionsZCent"), collision.posZ(), mixingEstimatorValue); + } + + for (int iCorr = 0; iCorr < kNCorrCountTypes; ++iCorr) { + for (int iPt = 0; iPt < kNCorrCountPt; ++iPt) { + if (corrCountsPerColl[iCorr][iPt] == 0) { + continue; + } + + const int corrChannel = iCorr * kNCorrCountPt + iPt; + corrPresenceMask |= getCorrMaskBit(iCorr, iPt); + + if (validMixingBin) { + ++mixingBinStatusPerDF[mixingBin].nCorrCollisions[iCorr][iPt]; + mixingQA.fill(HIST("Mixing/EligibleCollisionsPerBin"), mixingBin, corrChannel); + mixingQA.fill(HIST("Mixing/EligibleCollisionsZCentChannel"), collision.posZ(), mixingEstimatorValue, corrChannel); + } + } + } + + for (int iReso = 0; iReso < kNResoCorrCountTypes; ++iReso) { + for (int iPt = 0; iPt < kNCorrCountPt; ++iPt) { + for (int iMassRegion = kMassPeak; iMassRegion <= kMassRSB; ++iMassRegion) { + if (resoRegionCountsPerColl[iReso][iPt][iMassRegion] == 0) { + continue; + } + corrPresenceMask |= getResoRegionMaskBit(iReso, iPt, iMassRegion); + } + } + } + + int nTriggersWritten = 0; + int nTrackAssocsWritten = 0; + int nResoAssocsWritten = 0; + //______________________________________________________________________________ + // Derived tables: only the triggers and associates marked as used, and only for the stored pools. + // Every particle is written once; its UsedMask holds all correlations it was used in. + const uint64_t storedMask = getStoredCorrMask(corrPresenceMask, mixingBin); + if (storedMask != 0ULL) { + derivedCollisions(collision.posZ(), mixingEstimatorValue, storedMask); + iCollGlobalCount++; + + // Tracks: triggers, and h / pi / K / p associates (track pools = CorrPresenceMask bits 0-7) + for (const auto& track : tracksPerCollision) { + const int64_t i = track.globalIndex() - firstTrackGI; + if (i < 0 || i >= static_cast(trackUsedFlags.size()) || trackUsedFlags[i] == 0) { + continue; + } + const uint16_t flags = trackUsedFlags[i]; + if ((flags & TriggerUsedFlag) != 0u) { + nTriggersWritten++; + derivedTriggers(iCollGlobalCount, track.pt(), track.eta(), track.phi()); + } + const uint64_t usedMask = static_cast(flags & 0xFFu) & storedMask; + if (usedMask != 0ULL) { + nTrackAssocsWritten++; + derivedAssocs(iCollGlobalCount, track.pt(), track.eta(), track.phi(), static_cast(track.sign() > 0 ? 1 : -1), usedMask); + } + } + + // Resonance candidates (resonance pools = CorrPresenceMask bits 18-47; flag bit r = pool bit 18 + r) + for (const auto& resonance : resonancesPerCollision) { + const int64_t i = resonance.globalIndex() - firstResoGI; + if (i < 0 || i >= static_cast(resoUsedFlags.size()) || resoUsedFlags[i] == 0) { + continue; + } + const uint64_t usedMask = (static_cast(resoUsedFlags[i]) << 18) & storedMask; + if (usedMask != 0ULL) { + nResoAssocsWritten++; + derivedAssocs(iCollGlobalCount, resonance.pt(), resonance.eta(), resonance.phi(), static_cast(0), usedMask); + } + } + + mixingQA.fill(HIST("DerivedData/RowsWritten"), 0.0); + mixingQA.fill(HIST("DerivedData/RowsWritten"), 1.0, nTriggersWritten); + mixingQA.fill(HIST("DerivedData/RowsWritten"), 2.0, nTrackAssocsWritten); + mixingQA.fill(HIST("DerivedData/RowsWritten"), 3.0, nResoAssocsWritten); + } // Derived data creation block is over. + } // collision loop - Over. + + //______________________________________________________________________________ + // Dataframe-level mixing-pool QA + std::array nReadyMixBins{}; + + for (int iMixBin = 0; iMixBin < nMixBins; ++iMixBin) { + const auto& binStatus = mixingBinStatusPerDF[iMixBin]; + mixingQA.fill(HIST("Mixing/PerDF/AllPoolOccupancy"), iMixBin, binStatus.nCollisions); + + for (int iCorr = 0; iCorr < kNCorrCountTypes; ++iCorr) { + for (int iPt = 0; iPt < kNCorrCountPt; ++iPt) { + const int corrChannel = iCorr * kNCorrCountPt + iPt; + const uint64_t nEligibleCollisions = binStatus.nCorrCollisions[iCorr][iPt]; + + mixingQA.fill(HIST("Mixing/PerDF/EligiblePoolOccupancy"), iMixBin, corrChannel, nEligibleCollisions); + mixingQA.fill(HIST("Mixing/PerDF/PoolOccupancyDistribution"), corrChannel, nEligibleCollisions); + + if (nEligibleCollisions >= static_cast(cfgMixing.c1137nEvtMixing)) { + ++nReadyMixBins[corrChannel]; + mixingQA.fill(HIST("Mixing/PerDF/ReadyPoolMap"), iMixBin, corrChannel); + } + } + } + } + + for (int corrChannel = 0; corrChannel < NCorrCountChannels; ++corrChannel) { + mixingQA.fill(HIST("Mixing/PerDF/NReadyMixBins"), corrChannel, nReadyMixBins[corrChannel]); + } + } + PROCESS_SWITCH(HParticleCorrelation2SameEvent, processSameEvent, "Process Same event", true); +}; + +struct HParticleCorrelation3MixedEvent { + + HistogramRegistry hhCorrelation{"hhCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hIdCorrelation{"hIdCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hPhiCorrelation{"hPhiCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hKStarCorrelation{"hKStarCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hKStarBarCorrelation{"hKStarBarCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hLambdaCorrelation{"hLambdaCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hLambdaBarCorrelation{"hLambdaBarCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry mixingOperationQA{"mixingOperationQA", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + + // Mixing channels: 8 track channels (h-h, h-pi, h-K, h-p x low/high pT) followed by + // 30 resonance channels (5 species x low/high pT x Peak/LSB/RSB) + static constexpr int NMassWindows = 3; + static constexpr int NTrackMixChannels = static_cast(kCountHPr + 1) * static_cast(kNCorrCountPt); + static constexpr int NMixOperationChannels = NTrackMixChannels + static_cast(kNResoCorrCountTypes) * static_cast(kNCorrCountPt) * NMassWindows; + + static constexpr std::array MixOperationChannelName = { + "hh_LowPt", "hh_HighPt", "hPi_LowPt", "hPi_HighPt", "hKa_LowPt", "hKa_HighPt", "hPr_LowPt", "hPr_HighPt", + "hPhi_LowPt_Peak", "hPhi_LowPt_LSB", "hPhi_LowPt_RSB", "hPhi_HighPt_Peak", "hPhi_HighPt_LSB", "hPhi_HighPt_RSB", + "hKStar_LowPt_Peak", "hKStar_LowPt_LSB", "hKStar_LowPt_RSB", "hKStar_HighPt_Peak", "hKStar_HighPt_LSB", "hKStar_HighPt_RSB", + "hKStarBar_LowPt_Peak", "hKStarBar_LowPt_LSB", "hKStarBar_LowPt_RSB", "hKStarBar_HighPt_Peak", "hKStarBar_HighPt_LSB", "hKStarBar_HighPt_RSB", + "hLambda_LowPt_Peak", "hLambda_LowPt_LSB", "hLambda_LowPt_RSB", "hLambda_HighPt_Peak", "hLambda_HighPt_LSB", "hLambda_HighPt_RSB", + "hLambdaBar_LowPt_Peak", "hLambdaBar_LowPt_LSB", "hLambdaBar_LowPt_RSB", "hLambdaBar_HighPt_Peak", "hLambdaBar_HighPt_LSB", "hLambdaBar_HighPt_RSB"}; + + struct : ConfigurableGroup { + Configurable c2001printDebugMessages{"c2001printDebugMessages", false, "Print debug messages"}; + } cfgDebug; + + struct : ConfigurableGroup { + ConfigurableAxis c2002axisDeltaPhi{"c2002axisDeltaPhi", {80, -2.0f, 6.0f}, "#Delta#varphi"}; + ConfigurableAxis c2003axisDeltaEta{"c2003axisDeltaEta", {84, -2.1f, 2.1f}, "#Delta#eta"}; + ConfigurableAxis c2004axisCorrSparseTriggerPt{"c2004axisCorrSparseTriggerPt", {8, 4.0f, 8.0f}, "#it{p}_{T}^{trig} (GeV/#it{c})"}; + ConfigurableAxis c2005axisCorrSparseAssocPt{"c2005axisCorrSparseAssocPt", {8, 0.0f, 4.0f}, "#it{p}_{T}^{assoc} (GeV/#it{c})"}; + ConfigurableAxis c2006axisCorrSparseDeltaPhi{"c2006axisCorrSparseDeltaPhi", {32, -2.0f, 6.0f}, "#Delta#varphi"}; + ConfigurableAxis c2007axisCorrSparseDeltaEta{"c2007axisCorrSparseDeltaEta", {24, -2.1f, 2.1f}, "#Delta#eta"}; + ConfigurableAxis c2008axisCorrCentrality{"c2008axisCorrCentrality", {VARIABLE_WIDTH, 0.0, 1.0, 5.0, 10.0, 20.0, 30.0, 40.0, 50.0, 70.0, 100.0}, "Centrality (%) axis of the correlation histograms (estimator chosen by c1138mixingEstimator)"}; + } cfgAxis; + + // Selections (track cuts, PID, pT classes, mass windows, rapidity, estimator) are applied in the same-event + // task and stored as tags. Only the mixing itself is configured here; keep these axes equal to c1139/c1140. + struct : ConfigurableGroup { + Configurable c2009nEvtMixing{"c2009nEvtMixing", 5, "Number of partner events mixed with each trigger event"}; + ConfigurableAxis c2010axisVtxMixing{"c2010axisVtxMixing", {VARIABLE_WIDTH, -10.0, -8.0, -6.0, -4.0, -2.0, 0.0, 2.0, 4.0, 6.0, 8.0, 10.0}, "Mixing bins - z vertex"}; + ConfigurableAxis c2011axisCentMixing{"c2011axisCentMixing", {VARIABLE_WIDTH, -1.0, 20.0, 50.0, 80.0, 101.0}, "Mixing bins - centrality (estimator chosen by c1138mixingEstimator in the same-event task)"}; + } cfgMixing; + + Configurable a0000EnableStruct{"a0000EnableStruct", true, "enables filling of declared registries"}; + + // Re-built in init() with the configured axes (as a data member it holds the default axes) + using BinningTypeMixing = ColumnBinningPolicy; + BinningTypeMixing colBinning{{cfgMixing.c2010axisVtxMixing, cfgMixing.c2011axisCentMixing}, true}; + int nMixBins = 0; + + void init(InitContext const&) + { + if (!a0000EnableStruct) { + return; + } + + if (cfgDebug.c2001printDebugMessages) { + LOGF(info, "Starting init"); + } + + const AxisSpec axisPt = {200, 0.0f, 10.0f, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisEta = {100, -5.0f, 5.0f, "#eta"}; + const AxisSpec axisPhi = {110, -1.0f, 10.0f, "#phi (radians)"}; + const AxisSpec axisSign = {3, -1.5f, 1.5f, "Sign (tracks: charge, resonances: 0)"}; + + AxisSpec axisDeltaPhiSpec{cfgAxis.c2002axisDeltaPhi, "#Delta#varphi"}; + AxisSpec axisDeltaEtaSpec{cfgAxis.c2003axisDeltaEta, "#Delta#eta"}; + AxisSpec axisCorrSparseTriggerPt{cfgAxis.c2004axisCorrSparseTriggerPt, "#it{p}_{T}^{trig} (GeV/#it{c})"}; + AxisSpec axisCorrSparseAssocPt{cfgAxis.c2005axisCorrSparseAssocPt, "#it{p}_{T}^{assoc} (GeV/#it{c})"}; + AxisSpec axisCorrSparseDeltaPhi{cfgAxis.c2006axisCorrSparseDeltaPhi, "#Delta#varphi"}; + AxisSpec axisCorrSparseDeltaEta{cfgAxis.c2007axisCorrSparseDeltaEta, "#Delta#eta"}; + AxisSpec axisCorrCentrality{cfgAxis.c2008axisCorrCentrality, "Centrality (%)"}; + + auto addAssocQAHistos = [&](auto& histReg, const std::string& basePath) { + histReg.add((basePath + "Pt").c_str(), "Associate;p_{T} (GeV/c);Counts", HistType::kTH1F, {axisPt}); + histReg.add((basePath + "Eta").c_str(), "Associate;#eta;Counts", HistType::kTH1F, {axisEta}); + histReg.add((basePath + "Phi").c_str(), "Associate;#varphi;Counts", HistType::kTH1F, {axisPhi}); + histReg.add((basePath + "Sign").c_str(), "Associate;Sign;Counts", HistType::kTH1F, {axisSign}); + }; + + auto addCorrHistos = [&](auto& histReg, const std::string& basePath) { + histReg.add((basePath + "DeltaPhi").c_str(), "Trigger-associate;#Delta#varphi;Pairs", HistType::kTH1F, {axisDeltaPhiSpec}); + histReg.add((basePath + "DeltaEta").c_str(), "Trigger-associate;#Delta#eta;Pairs", HistType::kTH1F, {axisDeltaEtaSpec}); + histReg.add((basePath + "DeltaPhiDeltaEta").c_str(), "Trigger-associate;#Delta#varphi;#Delta#eta;Centrality (%)", HistType::kTH3F, {axisDeltaPhiSpec, axisDeltaEtaSpec, axisCorrCentrality}); + histReg.add((basePath + "CorrSparse").c_str(), "Correlation sparse;p_{T}^{trig};p_{T}^{assoc};#Delta#varphi;#Delta#eta;Centrality (%)", HistType::kTHnSparseF, {axisCorrSparseTriggerPt, axisCorrSparseAssocPt, axisCorrSparseDeltaPhi, axisCorrSparseDeltaEta, axisCorrCentrality}); + }; + + // Histogram paths mirror the same-event task (SE/... -> ME/...) + // h-h + addAssocQAHistos(hhCorrelation, "ME/hh/QA/AssocLowPt/"); + hhCorrelation.addClone("ME/hh/QA/AssocLowPt/", "ME/hh/QA/AssocHighPt/"); + addCorrHistos(hhCorrelation, "ME/hh/Corr/AssocLowPt/"); + hhCorrelation.addClone("ME/hh/Corr/AssocLowPt/", "ME/hh/Corr/AssocHighPt/"); + + // h-identified + addAssocQAHistos(hIdCorrelation, "ME/hIdentified/QA/AssocLowPt/Pi/"); + hIdCorrelation.addClone("ME/hIdentified/QA/AssocLowPt/Pi/", "ME/hIdentified/QA/AssocLowPt/Ka/"); + hIdCorrelation.addClone("ME/hIdentified/QA/AssocLowPt/Pi/", "ME/hIdentified/QA/AssocLowPt/Pr/"); + hIdCorrelation.addClone("ME/hIdentified/QA/AssocLowPt/", "ME/hIdentified/QA/AssocHighPt/"); + addCorrHistos(hIdCorrelation, "ME/hIdentified/Corr/AssocLowPt/Pi/"); + hIdCorrelation.addClone("ME/hIdentified/Corr/AssocLowPt/Pi/", "ME/hIdentified/Corr/AssocLowPt/Ka/"); + hIdCorrelation.addClone("ME/hIdentified/Corr/AssocLowPt/Pi/", "ME/hIdentified/Corr/AssocLowPt/Pr/"); + hIdCorrelation.addClone("ME/hIdentified/Corr/AssocLowPt/", "ME/hIdentified/Corr/AssocHighPt/"); + + // h-resonance + auto registerResonanceHistograms = [&](auto& histReg, const std::string& pairName) { + const std::string base = "ME/" + pairName + "/"; + addAssocQAHistos(histReg, base + "QA/AssocLowPt/Peak/Reso/"); + histReg.addClone(base + "QA/AssocLowPt/Peak/", base + "QA/AssocLowPt/LSB/"); + histReg.addClone(base + "QA/AssocLowPt/Peak/", base + "QA/AssocLowPt/RSB/"); + histReg.addClone(base + "QA/AssocLowPt/", base + "QA/AssocHighPt/"); + addCorrHistos(histReg, base + "Corr/AssocLowPt/Peak/"); + histReg.addClone(base + "Corr/AssocLowPt/Peak/", base + "Corr/AssocLowPt/LSB/"); + histReg.addClone(base + "Corr/AssocLowPt/Peak/", base + "Corr/AssocLowPt/RSB/"); + histReg.addClone(base + "Corr/AssocLowPt/", base + "Corr/AssocHighPt/"); + }; + registerResonanceHistograms(hPhiCorrelation, "hPhi"); + registerResonanceHistograms(hKStarCorrelation, "hKStar"); + registerResonanceHistograms(hKStarBarCorrelation, "hKStarBar"); + registerResonanceHistograms(hLambdaCorrelation, "hLambda"); + registerResonanceHistograms(hLambdaBarCorrelation, "hLambdaBar"); + + //______________________________________________________________________________ + // Mixing binning (re-built here with the configured axes) and mixing QA + AxisSpec axisVtxMix{cfgMixing.c2010axisVtxMixing, "z_{vtx} (cm)"}; + AxisSpec axisCentMix{cfgMixing.c2011axisCentMixing, "Centrality (%)"}; + nMixBins = axisVtxMix.getNbins() * axisCentMix.getNbins(); + colBinning = BinningTypeMixing{{cfgMixing.c2010axisVtxMixing, cfgMixing.c2011axisCentMixing}, true}; + + const AxisSpec axisMixBin = {nMixBins, -0.5, static_cast(nMixBins) - 0.5, "Mixing bin"}; + const AxisSpec axisMixChannel = {NMixOperationChannels, -0.5, static_cast(NMixOperationChannels) - 0.5, "Mixing channel"}; + const AxisSpec axisMixPartners = {static_cast(cfgMixing.c2009nEvtMixing) + 1, -0.5, static_cast(cfgMixing.c2009nEvtMixing) + 0.5, "N mixed partner events"}; + const AxisSpec axisCollPerDF = {200, 0.0, 20000.0, "Derived collisions per dataframe"}; + const AxisSpec axisMixVtxZQA = {100, -10.0f, 10.0f, "z_{vtx} (cm)"}; + const AxisSpec axisMixDeltaVtxZQA = {100, -20.0f, 20.0f, "#Delta z_{vtx} (cm)"}; + const AxisSpec axisMixCentralityQA = {102, -1.0f, 101.0f, "Centrality (%)"}; + const AxisSpec axisMixDeltaCentralityQA = {204, -102.0f, 102.0f, "#Delta Centrality (%)"}; + + mixingOperationQA.add("ME/Mixing/DataFramesProcessed", "Processed dataframes;Dataframe;Count", HistType::kTH1D, {{1, -0.5, 0.5}}); + mixingOperationQA.add("ME/Mixing/CollisionsPerDF", "Derived collisions per (merged) dataframe;N collisions;Dataframes", HistType::kTH1D, {axisCollPerDF}); + mixingOperationQA.add("ME/Mixing/CollisionsPerBinChannel", "Collisions in mixing pools;Mixing bin;Mixing channel", HistType::kTH2D, {axisMixBin, axisMixChannel}); + mixingOperationQA.add("ME/Mixing/CollisionPairsPerChannel", "Mixed collision pairs;Mixing channel;Mixed collision pairs", HistType::kTH1D, {axisMixChannel}); + mixingOperationQA.add("ME/Mixing/CollisionPairsPerBinChannel", "Mixed collision pairs;Mixing bin;Mixing channel", HistType::kTH2D, {axisMixBin, axisMixChannel}); + mixingOperationQA.add("ME/Mixing/PartnersPerTriggerEvent", "Mixed partners per trigger event;Mixing channel;N partner events", HistType::kTH2D, {axisMixChannel, axisMixPartners}); + mixingOperationQA.add("ME/Mixing/PartnersPerTriggerEventVsBin", "Mixed partners per trigger event;Mixing bin;Mixing channel;N partner events", HistType::kTH3D, {axisMixBin, axisMixChannel, axisMixPartners}); + mixingOperationQA.add("ME/Mixing/VtxZ1VsVtxZ2", "Mixed-event vertex positions;z_{vtx}^{trig} (cm);z_{vtx}^{assoc} (cm)", HistType::kTH2F, {axisMixVtxZQA, axisMixVtxZQA}); + mixingOperationQA.add("ME/Mixing/DeltaVtxZVsChannel", "Mixed-event #Delta z_{vtx};Mixing channel;z_{vtx}^{trig}-z_{vtx}^{assoc} (cm)", HistType::kTH2F, {axisMixChannel, axisMixDeltaVtxZQA}); + mixingOperationQA.add("ME/Mixing/Centrality1VsCentrality2", "Mixing estimator of mixed events;Estimator^{trig} (%);Estimator^{assoc} (%)", HistType::kTH2F, {axisMixCentralityQA, axisMixCentralityQA}); + mixingOperationQA.add("ME/Mixing/DeltaCentralityVsChannel", "Mixing-estimator difference;Mixing channel;Estimator^{trig}-Estimator^{assoc} (%)", HistType::kTH2F, {axisMixChannel, axisMixDeltaCentralityQA}); + + auto setMixOperationChannelLabels = [](TAxis* axis) { + for (int iChannel = 0; iChannel < NMixOperationChannels; ++iChannel) { + axis->SetBinLabel(iChannel + 1, MixOperationChannelName[iChannel].data()); + } + }; + setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/CollisionsPerBinChannel"))->GetYaxis()); + setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/CollisionPairsPerChannel"))->GetXaxis()); + setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/CollisionPairsPerBinChannel"))->GetYaxis()); + setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/PartnersPerTriggerEvent"))->GetXaxis()); + setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/PartnersPerTriggerEventVsBin"))->GetYaxis()); + setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/DeltaVtxZVsChannel"))->GetXaxis()); + setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/DeltaCentralityVsChannel"))->GetXaxis()); + + if (cfgDebug.c2001printDebugMessages) { + LOGF(info, "Finishing init"); + } + } + + SliceCache cache; + Preslice triggersPerCollPreslice = aod::hpcorrtrigger::hpCorrCollId; + Preslice assocsPerCollPreslice = aod::hpcorrassoc::hpCorrCollId; // registers HPCORRASSOC/fIndexHPCorrColls in the SliceCache + + //______________________________________________________________________________ + // Associate partitions: one per mixing channel. Every stored particle carries UsedMask = all correlations + // h-h, h-pi, h-K, h-p + Partition assocHHLowPt = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHHLowPt)); + Partition assocHHHighPt = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHHHighPt)); + Partition assocHPiLowPt = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHPiLowPt)); + Partition assocHPiHighPt = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHPiHighPt)); + Partition assocHKaLowPt = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKaLowPt)); + Partition assocHKaHighPt = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKaHighPt)); + Partition assocHPrLowPt = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHPrLowPt)); + Partition assocHPrHighPt = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHPrHighPt)); + + // h-Phi + Partition assocHPhiLowPtPeak = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHPhiLowPtPeak)); + Partition assocHPhiLowPtLSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHPhiLowPtLSB)); + Partition assocHPhiLowPtRSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHPhiLowPtRSB)); + Partition assocHPhiHighPtPeak = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHPhiHighPtPeak)); + Partition assocHPhiHighPtLSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHPhiHighPtLSB)); + Partition assocHPhiHighPtRSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHPhiHighPtRSB)); + + // h-KStar + Partition assocHKStarLowPtPeak = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarLowPtPeak)); + Partition assocHKStarLowPtLSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarLowPtLSB)); + Partition assocHKStarLowPtRSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarLowPtRSB)); + Partition assocHKStarHighPtPeak = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarHighPtPeak)); + Partition assocHKStarHighPtLSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarHighPtLSB)); + Partition assocHKStarHighPtRSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarHighPtRSB)); + + // h-KStarBar + Partition assocHKStarBarLowPtPeak = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarBarLowPtPeak)); + Partition assocHKStarBarLowPtLSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarBarLowPtLSB)); + Partition assocHKStarBarLowPtRSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarBarLowPtRSB)); + Partition assocHKStarBarHighPtPeak = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarBarHighPtPeak)); + Partition assocHKStarBarHighPtLSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarBarHighPtLSB)); + Partition assocHKStarBarHighPtRSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarBarHighPtRSB)); + + // h-Lambda + Partition assocHLambdaLowPtPeak = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaLowPtPeak)); + Partition assocHLambdaLowPtLSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaLowPtLSB)); + Partition assocHLambdaLowPtRSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaLowPtRSB)); + Partition assocHLambdaHighPtPeak = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaHighPtPeak)); + Partition assocHLambdaHighPtLSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaHighPtLSB)); + Partition assocHLambdaHighPtRSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaHighPtRSB)); + + // h-LambdaBar + Partition assocHLambdaBarLowPtPeak = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaBarLowPtPeak)); + Partition assocHLambdaBarLowPtLSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaBarLowPtLSB)); + Partition assocHLambdaBarLowPtRSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaBarLowPtRSB)); + Partition assocHLambdaBarHighPtPeak = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaBarHighPtPeak)); + Partition assocHLambdaBarHighPtLSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaBarHighPtLSB)); + Partition assocHLambdaBarHighPtRSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaBarHighPtRSB)); + + //______________________________________________________________________________ + // Collision pools: one per mixing channel. A collision is in the pool of a channel if its CorrMask has the + // channel bit, i.e. it had at least one trigger-associate pair of this channel in the same event. + // h-h, h-pi, h-K, h-p + Partition collHHLowPt = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHHLowPt)); + Partition collHHHighPt = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHHHighPt)); + Partition collHPiLowPt = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHPiLowPt)); + Partition collHPiHighPt = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHPiHighPt)); + Partition collHKaLowPt = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKaLowPt)); + Partition collHKaHighPt = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKaHighPt)); + Partition collHPrLowPt = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHPrLowPt)); + Partition collHPrHighPt = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHPrHighPt)); + + // h-Phi + Partition collHPhiLowPtPeak = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHPhiLowPtPeak)); + Partition collHPhiLowPtLSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHPhiLowPtLSB)); + Partition collHPhiLowPtRSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHPhiLowPtRSB)); + Partition collHPhiHighPtPeak = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHPhiHighPtPeak)); + Partition collHPhiHighPtLSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHPhiHighPtLSB)); + Partition collHPhiHighPtRSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHPhiHighPtRSB)); + + // h-KStar + Partition collHKStarLowPtPeak = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarLowPtPeak)); + Partition collHKStarLowPtLSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarLowPtLSB)); + Partition collHKStarLowPtRSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarLowPtRSB)); + Partition collHKStarHighPtPeak = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarHighPtPeak)); + Partition collHKStarHighPtLSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarHighPtLSB)); + Partition collHKStarHighPtRSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarHighPtRSB)); + + // h-KStarBar + Partition collHKStarBarLowPtPeak = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarBarLowPtPeak)); + Partition collHKStarBarLowPtLSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarBarLowPtLSB)); + Partition collHKStarBarLowPtRSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarBarLowPtRSB)); + Partition collHKStarBarHighPtPeak = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarBarHighPtPeak)); + Partition collHKStarBarHighPtLSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarBarHighPtLSB)); + Partition collHKStarBarHighPtRSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarBarHighPtRSB)); + + // h-Lambda + Partition collHLambdaLowPtPeak = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaLowPtPeak)); + Partition collHLambdaLowPtLSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaLowPtLSB)); + Partition collHLambdaLowPtRSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaLowPtRSB)); + Partition collHLambdaHighPtPeak = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaHighPtPeak)); + Partition collHLambdaHighPtLSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaHighPtLSB)); + Partition collHLambdaHighPtRSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaHighPtRSB)); + + // h-LambdaBar + Partition collHLambdaBarLowPtPeak = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaBarLowPtPeak)); + Partition collHLambdaBarLowPtLSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaBarLowPtLSB)); + Partition collHLambdaBarLowPtRSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaBarLowPtRSB)); + Partition collHLambdaBarHighPtPeak = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaBarHighPtPeak)); + Partition collHLambdaBarHighPtLSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaBarHighPtLSB)); + Partition collHLambdaBarHighPtRSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaBarHighPtRSB)); + + // Number of mixed partners of every collision as trigger event (QA only, re-used by all channels) + std::vector nPartnersPerColl; + + //______________________________________________________________________________ + // Channel bookkeeping: PairTypeEnum (+ PidEnum for identified hadrons) -> CorrCountTypeEnum, + // which gives the CorrPresenceMask bit and the mixing-QA channel number + template + static constexpr int getCorrType() + { + if constexpr (pairType == kHH) { + return kCountHH; + } else if constexpr (pairType == kHId) { + return pidType == kPi ? kCountHPi : (pidType == kKa ? kCountHKa : kCountHPr); + } else if constexpr (pairType == kHPhi) { + return kCountHPhi; + } else if constexpr (pairType == kHKStar) { + return kCountHKStar; + } else if constexpr (pairType == kHKStarBar) { + return kCountHKStarBar; + } else if constexpr (pairType == kHLambda) { + return kCountHLambda; + } else { + return kCountHLambdaBar; + } + } + + //______________________________________________________________________________ + // Histogram filling for one mixed trigger-associate pair + template + void fillMixedPair(H& histReg, const T& trigger, const A& associate, const float centrality) + { + const float dPhi = computeDeltaPhi(associate.phi(), trigger.phi()); + const float dEta = associate.eta() - trigger.eta(); + + if constexpr (pairType == kHH) { + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHH]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST("Pt"), associate.pt()); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHH]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST("Eta"), associate.eta()); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHH]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST("Phi"), associate.phi()); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHH]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST("Sign"), associate.sign()); + + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHH]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST("DeltaPhi"), dPhi); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHH]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST("DeltaEta"), dEta); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHH]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST("DeltaPhiDeltaEta"), dPhi, dEta, centrality); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHH]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST("CorrSparse"), trigger.pt(), associate.pt(), dPhi, dEta, centrality); + } else if constexpr (pairType == kHId) { + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Pt"), associate.pt()); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Eta"), associate.eta()); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Phi"), associate.phi()); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Sign"), associate.sign()); + + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DeltaPhi"), dPhi); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DeltaEta"), dEta); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DeltaPhiDeltaEta"), dPhi, dEta, centrality); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("CorrSparse"), trigger.pt(), associate.pt(), dPhi, dEta, centrality); + } else { + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/Pt"), associate.pt()); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/Eta"), associate.eta()); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/Phi"), associate.phi()); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/Sign"), associate.sign()); + + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("DeltaPhi"), dPhi); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("DeltaEta"), dEta); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("DeltaPhiDeltaEta"), dPhi, dEta, centrality); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("CorrSparse"), trigger.pt(), associate.pt(), dPhi, dEta, centrality); + } + } + + template + void fillMixingPairQA(const int mixChannel, const int mixBin, const C& collision1, const C& collision2) + { + mixingOperationQA.fill(HIST("ME/Mixing/CollisionPairsPerChannel"), mixChannel); + mixingOperationQA.fill(HIST("ME/Mixing/CollisionPairsPerBinChannel"), mixBin, mixChannel); + mixingOperationQA.fill(HIST("ME/Mixing/VtxZ1VsVtxZ2"), collision1.posZ(), collision2.posZ()); + mixingOperationQA.fill(HIST("ME/Mixing/DeltaVtxZVsChannel"), mixChannel, collision1.posZ() - collision2.posZ()); + mixingOperationQA.fill(HIST("ME/Mixing/Centrality1VsCentrality2"), collision1.centrality(), collision2.centrality()); + mixingOperationQA.fill(HIST("ME/Mixing/DeltaCentralityVsChannel"), mixChannel, collision1.centrality() - collision2.centrality()); + } + + //______________________________________________________________________________ + // Mix one channel with selfCombinations. + // collisionPool : collisions whose CorrMask has the channel bit (partition on HPCorrColls) + // assocPartition : associates whose UsedMask has the channel bit (partition on HPCorrAssocs) + // selfCombinations groups the pool by mixing bin (z-vertex x centrality) and pairs every collision with the + // next c2009nEvtMixing collisions of the same bin. Each pair comes once, collision1 before collision2: + // the triggers are taken from collision1 and the associates from collision2. + template + void mixChannel(H& histReg, P& collisionPool, A& assocPartition, aod::HPCorrTriggers const& triggers, const int64_t nCollisions) + { + constexpr int PtType = roleType == kAssocLowPt ? kCountLowPt : kCountHighPt; + constexpr int CorrType = getCorrType(); + constexpr bool IsReso = CorrType >= kCountHPhi; + constexpr int MixChannel = IsReso ? NTrackMixChannels + (CorrType - kCountHPhi) * kNCorrCountPt * NMassWindows + PtType * NMassWindows + (massRegion - kMassPeak) + : CorrType * kNCorrCountPt + PtType; + static_assert(MixChannel >= 0 && MixChannel < NMixOperationChannels, "Invalid mixing channel"); + + nPartnersPerColl.assign(nCollisions, 0); + + for (const auto& [collision1, collision2] : selfCombinations(colBinning, cfgMixing.c2009nEvtMixing, -1, collisionPool, collisionPool)) { + const int mixBin1 = colBinning.getBin({collision1.posZ(), collision1.centrality()}); + const int mixBin2 = colBinning.getBin({collision2.posZ(), collision2.centrality()}); + if (mixBin1 != mixBin2) { + LOG(fatal) << "EVENT MIXING ERROR :: collisions from different mixing bins :: " << mixBin1 << " :: " << mixBin2; + } + + nPartnersPerColl[collision1.globalIndex()]++; + fillMixingPairQA(MixChannel, mixBin1, collision1, collision2); + + const auto triggers1 = triggers.sliceBy(triggersPerCollPreslice, collision1.globalIndex()); + const auto associates2 = assocPartition->sliceByCached(aod::hpcorrassoc::hpCorrCollId, collision2.globalIndex(), cache); + + for (const auto& trigger : triggers1) { + for (const auto& associate : associates2) { + fillMixedPair(histReg, trigger, associate, collision1.centrality()); + } + } + } + + // Pool QA: every collision of the pool and the number of partners it was mixed with as trigger event + for (const auto& collision : collisionPool) { + const int mixBin = colBinning.getBin({collision.posZ(), collision.centrality()}); + if (mixBin < 0) { + continue; + } + const int nPartners = nPartnersPerColl[collision.globalIndex()]; + mixingOperationQA.fill(HIST("ME/Mixing/CollisionsPerBinChannel"), mixBin, MixChannel); + mixingOperationQA.fill(HIST("ME/Mixing/PartnersPerTriggerEvent"), MixChannel, nPartners); + mixingOperationQA.fill(HIST("ME/Mixing/PartnersPerTriggerEventVsBin"), mixBin, MixChannel, nPartners); + } + } + + int dfNumber = 0; + void processNothing(aod::Origins const& origins) + { + if (cfgDebug.c2001printDebugMessages) { + LOG(info) << "DEBUG :: Process Nothing :: df_" << dfNumber << " :: origins = " << origins.size(); + } + // Intentionally empty. Keeps the task alive when the mixing is not run. + } + PROCESS_SWITCH(HParticleCorrelation3MixedEvent, processNothing, "Dummy process for analysis", true); + + void processMixEventInDeriveData(aod::HPCorrColls const& collisions, aod::HPCorrTriggers const& triggers, aod::HPCorrAssocs const& assocs) + { + dfNumber++; + const int64_t nCollisions = collisions.size(); + mixingOperationQA.fill(HIST("ME/Mixing/DataFramesProcessed"), 0.0); + mixingOperationQA.fill(HIST("ME/Mixing/CollisionsPerDF"), nCollisions); + + if (cfgDebug.c2001printDebugMessages) { + LOG(info) << "DEBUG :: df_" << dfNumber << " :: collisions = " << nCollisions << " :: triggers = " << triggers.size() << " :: assocs = " << assocs.size(); + } + + // Index sanity check: every trigger / associate must point to a collision of this (merged) dataframe + int64_t nBadIndex = 0; + for (const auto& trigger : triggers) { + if (trigger.hpCorrCollId() < 0 || trigger.hpCorrCollId() >= nCollisions) { + ++nBadIndex; + } + } + for (const auto& assoc : assocs) { + if (assoc.hpCorrCollId() < 0 || assoc.hpCorrCollId() >= nCollisions) { + ++nBadIndex; + } + } + if (nBadIndex > 0) { + LOG(fatal) << "DERIVED DATA ERROR :: df_" << dfNumber << " :: " << nBadIndex << " trigger/associate rows point outside the collision table"; + } + + // h-h + mixChannel(hhCorrelation, collHHLowPt, assocHHLowPt, triggers, nCollisions); + mixChannel(hhCorrelation, collHHHighPt, assocHHHighPt, triggers, nCollisions); + + // h-pi, h-K, h-p + mixChannel(hIdCorrelation, collHPiLowPt, assocHPiLowPt, triggers, nCollisions); + mixChannel(hIdCorrelation, collHPiHighPt, assocHPiHighPt, triggers, nCollisions); + mixChannel(hIdCorrelation, collHKaLowPt, assocHKaLowPt, triggers, nCollisions); + mixChannel(hIdCorrelation, collHKaHighPt, assocHKaHighPt, triggers, nCollisions); + mixChannel(hIdCorrelation, collHPrLowPt, assocHPrLowPt, triggers, nCollisions); + mixChannel(hIdCorrelation, collHPrHighPt, assocHPrHighPt, triggers, nCollisions); + + // h-Phi + mixChannel(hPhiCorrelation, collHPhiLowPtPeak, assocHPhiLowPtPeak, triggers, nCollisions); + mixChannel(hPhiCorrelation, collHPhiLowPtLSB, assocHPhiLowPtLSB, triggers, nCollisions); + mixChannel(hPhiCorrelation, collHPhiLowPtRSB, assocHPhiLowPtRSB, triggers, nCollisions); + mixChannel(hPhiCorrelation, collHPhiHighPtPeak, assocHPhiHighPtPeak, triggers, nCollisions); + mixChannel(hPhiCorrelation, collHPhiHighPtLSB, assocHPhiHighPtLSB, triggers, nCollisions); + mixChannel(hPhiCorrelation, collHPhiHighPtRSB, assocHPhiHighPtRSB, triggers, nCollisions); + + // h-KStar + mixChannel(hKStarCorrelation, collHKStarLowPtPeak, assocHKStarLowPtPeak, triggers, nCollisions); + mixChannel(hKStarCorrelation, collHKStarLowPtLSB, assocHKStarLowPtLSB, triggers, nCollisions); + mixChannel(hKStarCorrelation, collHKStarLowPtRSB, assocHKStarLowPtRSB, triggers, nCollisions); + mixChannel(hKStarCorrelation, collHKStarHighPtPeak, assocHKStarHighPtPeak, triggers, nCollisions); + mixChannel(hKStarCorrelation, collHKStarHighPtLSB, assocHKStarHighPtLSB, triggers, nCollisions); + mixChannel(hKStarCorrelation, collHKStarHighPtRSB, assocHKStarHighPtRSB, triggers, nCollisions); + + // h-KStarBar + mixChannel(hKStarBarCorrelation, collHKStarBarLowPtPeak, assocHKStarBarLowPtPeak, triggers, nCollisions); + mixChannel(hKStarBarCorrelation, collHKStarBarLowPtLSB, assocHKStarBarLowPtLSB, triggers, nCollisions); + mixChannel(hKStarBarCorrelation, collHKStarBarLowPtRSB, assocHKStarBarLowPtRSB, triggers, nCollisions); + mixChannel(hKStarBarCorrelation, collHKStarBarHighPtPeak, assocHKStarBarHighPtPeak, triggers, nCollisions); + mixChannel(hKStarBarCorrelation, collHKStarBarHighPtLSB, assocHKStarBarHighPtLSB, triggers, nCollisions); + mixChannel(hKStarBarCorrelation, collHKStarBarHighPtRSB, assocHKStarBarHighPtRSB, triggers, nCollisions); + + // h-Lambda + mixChannel(hLambdaCorrelation, collHLambdaLowPtPeak, assocHLambdaLowPtPeak, triggers, nCollisions); + mixChannel(hLambdaCorrelation, collHLambdaLowPtLSB, assocHLambdaLowPtLSB, triggers, nCollisions); + mixChannel(hLambdaCorrelation, collHLambdaLowPtRSB, assocHLambdaLowPtRSB, triggers, nCollisions); + mixChannel(hLambdaCorrelation, collHLambdaHighPtPeak, assocHLambdaHighPtPeak, triggers, nCollisions); + mixChannel(hLambdaCorrelation, collHLambdaHighPtLSB, assocHLambdaHighPtLSB, triggers, nCollisions); + mixChannel(hLambdaCorrelation, collHLambdaHighPtRSB, assocHLambdaHighPtRSB, triggers, nCollisions); + + // h-LambdaBar + mixChannel(hLambdaBarCorrelation, collHLambdaBarLowPtPeak, assocHLambdaBarLowPtPeak, triggers, nCollisions); + mixChannel(hLambdaBarCorrelation, collHLambdaBarLowPtLSB, assocHLambdaBarLowPtLSB, triggers, nCollisions); + mixChannel(hLambdaBarCorrelation, collHLambdaBarLowPtRSB, assocHLambdaBarLowPtRSB, triggers, nCollisions); + mixChannel(hLambdaBarCorrelation, collHLambdaBarHighPtPeak, assocHLambdaBarHighPtPeak, triggers, nCollisions); + mixChannel(hLambdaBarCorrelation, collHLambdaBarHighPtLSB, assocHLambdaBarHighPtLSB, triggers, nCollisions); + mixChannel(hLambdaBarCorrelation, collHLambdaBarHighPtRSB, assocHLambdaBarHighPtRSB, triggers, nCollisions); + } + PROCESS_SWITCH(HParticleCorrelation3MixedEvent, processMixEventInDeriveData, "Mixed-event correlations from the minimal derived data", true); +}; + +struct HParticleCorrelation4Block4 { + + SliceCache cache; + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + Service pdgDB{}; + + Configurable a0000EnableStruct{"a0000EnableStruct", true, "enables filling of declared registries"}; + Configurable a0001PrintMessages{"a0001PrintMessages", false, "enables printing of debug messages"}; + + struct : ConfigurableGroup { + Configurable cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted |z-vertex| range [cm]"}; + Configurable cfgUseNoITSROFrameBorderCut{"cfgUseNoITSROFrameBorderCut", false, "Apply kNoITSROFrameBorder selection"}; + Configurable cfgUseSel8{"cfgUseSel8", false, "Apply sel8 selection"}; + Configurable cfgRequireRecoINELgt0{"cfgRequireRecoINELgt0", true, "Require reconstructed INEL>0 event class (applied in ALL processes)"}; + Configurable cfgUseNoSameBunchPileupCut{"cfgUseNoSameBunchPileupCut", false, "Apply kNoSameBunchPileup event selection"}; + Configurable cfgUseGoodZvtxFT0vsPVCut{"cfgUseGoodZvtxFT0vsPVCut", false, "Apply kIsGoodZvtxFT0vsPV event selection"}; + Configurable cfgUseGoodITSLayersAll{"cfgUseGoodITSLayersAll", false, "Apply kIsGoodITSLayersAll event selection"}; + Configurable applyOccupancyInTimeRangeCut{"applyOccupancyInTimeRangeCut", false, "Apply occupancy-in-time-range cut"}; + Configurable cfgOccupancyMin{"cfgOccupancyMin", 0, "Minimum track occupancy in time range"}; + Configurable cfgOccupancyMax{"cfgOccupancyMax", 9999, "Maximum track occupancy in time range"}; + Configurable cfgCentralityEstimator{"cfgCentralityEstimator", 0, "Centrality estimator: 0=FT0M 1=FT0A 2=FT0C"}; + Configurable cfgUseMCTruthCentrality{"cfgUseMCTruthCentrality", false, "Use MC truth centrality for generated Lambda(1520) histograms (otherwise the centrality of the accepted reconstructed collision is used when there is one)"}; + Configurable cfgApplyMCTruthINELgt0{"cfgApplyMCTruthINELgt0", true, "Apply truth-level INEL>0 selection to MC generated events (event factor, signal loss, generated QA)"}; + } evSel; + + struct : ConfigurableGroup { + Configurable cfgCutPt{"cfgCutPt", 0.15f, "Minimum pT of daughter track [GeV/c]"}; + Configurable cfgCutEta{"cfgCutEta", 0.8f, "Maximum |eta| of daughter track"}; + Configurable cfgMinY{"cfgMinY", -0.5f, "Minimum rapidity of the Lambda(1520) (reconstructed, generated and signal-loss particles)"}; + Configurable cfgMaxY{"cfgMaxY", 0.5f, "Maximum rapidity of the Lambda(1520) (reconstructed, generated and signal-loss particles)"}; + Configurable cfgMinTPCCrossedRows{"cfgMinTPCCrossedRows", 70, "Minimum TPC crossed pad rows"}; + Configurable cfgMinTPCClusters{"cfgMinTPCClusters", 120, "Minimum TPC clusters found"}; + Configurable cfgMinITSClusters{"cfgMinITSClusters", 5, "Minimum ITS clusters"}; + Configurable cfgMaxTPCSharedClusters{"cfgMaxTPCSharedClusters", 0, "Maximum TPC shared clusters"}; + Configurable cfgMinCrossedRowsOverFindable{"cfgMinCrossedRowsOverFindable", 0.8f, "Minimum ratio crossedRows/findableClusters"}; + Configurable cfgMaxTPCChi2NCl{"cfgMaxTPCChi2NCl", 4.0f, "Maximum TPC chi2/NCl"}; + Configurable cfgMaxITSChi2NCl{"cfgMaxITSChi2NCl", 36.0f, "Maximum ITS chi2/NCl"}; + Configurable applyTPCCrossedRowsCut{"applyTPCCrossedRowsCut", true, "Enable/disable minimum TPC crossed rows cut"}; + Configurable applyTPCFoundClusterCut{"applyTPCFoundClusterCut", false, "Enable/disable minimum TPC clusters found cut"}; + Configurable applyITSClusterCut{"applyITSClusterCut", false, "Enable/disable minimum ITS clusters cut"}; + Configurable applyTPCSharedClusterCut{"applyTPCSharedClusterCut", false, "Enable/disable maximum TPC shared clusters cut"}; + Configurable applyTPCCrossedRowsOverFindableCut{"applyTPCCrossedRowsOverFindableCut", false, "Enable/disable crossed rows / findable clusters cut"}; + Configurable applyTPCChi2NClCut{"applyTPCChi2NClCut", false, "Enable/disable maximum TPC chi2/NCl cut"}; + Configurable applyITSChi2NClCut{"applyITSChi2NClCut", false, "Enable/disable maximum ITS chi2/NCl cut"}; + Configurable requirePrimaryTrack{"requirePrimaryTrack", true, "Require isPrimaryTrack flag"}; + Configurable requireGlobalTrackWoDCA{"requireGlobalTrackWoDCA", true, "Require isGlobalTrackWoDCA flag"}; + Configurable requireGlobalTrack{"requireGlobalTrack", false, "Require isGlobalTrack flag"}; + Configurable requirePVContributor{"requirePVContributor", false, "Require PV-contributor flag"}; + } trackCuts; + + struct : ConfigurableGroup { + Configurable cfgCutDCAz{"cfgCutDCAz", 0.1f, "Maximum |DCAz| for all tracks [cm]"}; + Configurable> protonDCAPtBinEdges{"protonDCAPtBinEdges", {0.0f, 1000.f}, "Proton pT bin edges for DCAxy cut [GeV/c]"}; + Configurable> protonMaxDCAxyPerPtBin{"protonMaxDCAxyPerPtBin", {0.1f}, "Max |DCAxy| for proton per pT bin [cm]"}; + Configurable> kaonDCAPtBinEdges{"kaonDCAPtBinEdges", {0.0f, 1000.f}, "Kaon pT bin edges for DCAxy cut [GeV/c]"}; + Configurable> kaonMaxDCAxyPerPtBin{"kaonMaxDCAxyPerPtBin", {0.1f}, "Max |DCAxy| for kaon per pT bin [cm]"}; + Configurable isApplyPtDepDCAxyCut{"isApplyPtDepDCAxyCut", false, "Additionally apply the continuous pT-dependent DCAxy cut"}; + Configurable dcaXYConst{"dcaXYConst", 0.004f, "Constant term of pT-dependent DCAxy cut [cm] (7-sigma: 0.004)"}; + Configurable dcaXYPtCoeff{"dcaXYPtCoeff", 0.013f, "pT-dependent coefficient of DCAxy cut [cm] (7-sigma: 0.013)"}; + Configurable dcaXYPtExponent{"dcaXYPtExponent", 1.0f, "pT exponent of DCAxy cut"}; + } dcaCuts; + + struct : ConfigurableGroup { + Configurable useTPCOnlyPIDAtAllMomenta{"useTPCOnlyPIDAtAllMomenta", false, "Bypass TOF: TPC-only PID (|nSigma| < max TPC nSigma) at all momenta"}; + Configurable applyKaonPionRejection{"applyKaonPionRejection", false, "Reject pion-like kaon candidates (no TOF, pT window, small pion nSigma)"}; + Configurable pionRejectionMinPt{"pionRejectionMinPt", 1.0f, "Pion rejection: minimum pT [GeV/c]"}; + Configurable pionRejectionMaxPt{"pionRejectionMaxPt", 2.0f, "Pion rejection: maximum pT [GeV/c]"}; + Configurable pionRejectionMaxTPCNSigma{"pionRejectionMaxTPCNSigma", 2.0f, "Pion rejection: |TPC nSigma pion| below this is rejected"}; + } pidShared; + + struct : ConfigurableGroup { + Configurable requireTOFForProton{"requireTOFForProton", false, "Require TOF signal for proton candidates"}; + Configurable maxTPCNSigmaProton{"maxTPCNSigmaProton", 2.0f, "Maximum |TPC nSigma| (used only with useTPCOnlyPIDAtAllMomenta)"}; + Configurable protonThresholdMomentum{"protonThresholdMomentum", 0.9f, "Proton momentum p [GeV/c] for tracks WITHOUT TOF: below it TPC-only PID, at/above it the track is rejected (tracks with TOF always use the TPC+TOF cut)"}; + Configurable> protonTPCPIDMomentumBins{"protonTPCPIDMomentumBins", {0.f, 0.9f}, "momentum edges p for TPC-only proton PID [GeV/c]"}; + Configurable> protonTPCNSigmaCutPerBin{"protonTPCNSigmaCutPerBin", {2.f}, "Max |TPC nSigma| per momentum bin, TPC-only proton PID"}; + Configurable> protonCombinedPIDMomentumBins{"protonCombinedPIDMomentumBins", {0.f, 999.f}, "momentum edges p for circular TPC+TOF proton PID [GeV/c]"}; + Configurable> protonCombinedNSigmaCutPerBin{"protonCombinedNSigmaCutPerBin", {2.f}, "Max combined nSigma radius per momentum bin, circular cut"}; + } protonPID; + + struct : ConfigurableGroup { + Configurable requireTOFForKaon{"requireTOFForKaon", false, "Require TOF signal for kaon candidates"}; + Configurable maxTPCNSigmaKaon{"maxTPCNSigmaKaon", 2.0f, "Maximum |TPC nSigma| (used only with useTPCOnlyPIDAtAllMomenta)"}; + Configurable kaonThresholdMomentum{"kaonThresholdMomentum", 0.5f, "Kaon momentum p [GeV/c] for tracks WITHOUT TOF: below it TPC-only PID, at/above it the track is rejected (tracks with TOF always use the TPC+TOF cut)"}; + Configurable> kaonTPCPIDMomentumBins{"kaonTPCPIDMomentumBins", {0.f, 0.5f}, "momentum edges p for TPC-only kaon PID [GeV/c]"}; + Configurable> kaonTPCNSigmaCutPerBin{"kaonTPCNSigmaCutPerBin", {2.f}, "Max |TPC nSigma| per momentum bin, TPC-only kaon PID"}; + Configurable> kaonCombinedPIDMomentumBins{"kaonCombinedPIDMomentumBins", {0.f, 999.f}, "momentum edges p for circular TPC+TOF kaon PID [GeV/c]"}; + Configurable> kaonCombinedNSigmaCutPerBin{"kaonCombinedNSigmaCutPerBin", {2.f}, "Max combined nSigma radius per momentum bin, circular cut"}; + } kaonPID; + + struct : ConfigurableGroup { + Configurable applyDaughterDeltaEtaPhiCuts{"applyDaughterDeltaEtaPhiCuts", false, "Apply |deltaEta| and |deltaPhi| cuts between daughters"}; + Configurable maxDaughterDeltaEta{"maxDaughterDeltaEta", 0.7f, "Maximum |deltaEta| between daughters"}; + Configurable maxDaughterDeltaPhi{"maxDaughterDeltaPhi", 1.5f, "Maximum |deltaPhi| between daughters"}; + Configurable applyDeepAngleCut{"applyDeepAngleCut", false, "Apply minimum opening-angle cut (removes split-track background)"}; + Configurable deepAngleCutValue{"deepAngleCutValue", 0.04f, "Minimum opening angle between proton and kaon [rad]"}; + Configurable applyOpeningAngleVeto{"applyOpeningAngleVeto", false, "Reject pairs whose opening angle lies INSIDE [veto min, veto max]"}; + Configurable openingAngleVetoMin{"openingAngleVetoMin", 1.4f, "Opening-angle veto window: lower edge [rad]"}; + Configurable openingAngleVetoMax{"openingAngleVetoMax", 2.4f, "Opening-angle veto window: upper edge [rad]"}; + Configurable applyPairPtMassUpperLimits{"applyPairPtMassUpperLimits", false, "Exclude pairs with pT or mass above the limits below (overflow bins)"}; + Configurable cfgMaxPairPt{"cfgMaxPairPt", 10.0f, "Maximum pair pT [GeV/c]"}; + Configurable cfgMaxPairMass{"cfgMaxPairMass", 3.0f, "Maximum pair mass [GeV/c^2]"}; + } pairCuts; + + struct : ConfigurableGroup { + Configurable cfgNoMixedEvents{"cfgNoMixedEvents", 10, "Number of mixed events per signal event"}; + Configurable cfgFillMixLikeSign{"cfgFillMixLikeSign", false, "Also fill like-sign pairs in mixed events"}; + } mixingCfg; + + struct : ConfigurableGroup { + Configurable enableRotationalBackground{"enableRotationalBackground", false, "Compute rotational background (unlike-sign pairs only)"}; + Configurable numberOfRotations{"numberOfRotations", 10, "Number of rotations per pair (evenly spaced, weight 1/N)"}; + Configurable rotationAngleWindow{"rotationAngleWindow", 10.f, "Rotation angles within PI +/- PI/rotationAngleWindow"}; + Configurable rotateProtonInsteadOfKaon{"rotateProtonInsteadOfKaon", true, "Rotate the proton phi (false = rotate the kaon phi)"}; + } rotBkg; + + struct : ConfigurableGroup { + Configurable cfgLambda1520PdgCode{"cfgLambda1520PdgCode", static_cast(o2::constants::physics::Pdg::kLambda1520_Py), "PDG code of the Lambda(1520) in the MC sample (change it for injected samples that use a different code). Sign is ignored: +code = Lambda(1520), -code = anti-Lambda(1520)"}; + Configurable applyGeneratedEtaCut{"applyGeneratedEtaCut", false, "Apply |eta| cut to generated particles and to truth-matched reconstructed pairs"}; + Configurable generatedEtaMax{"generatedEtaMax", 0.5f, "MC |eta| cut"}; + Configurable applyGeneratedDaughterEtaCut{"applyGeneratedDaughterEtaCut", true, "Require both generated daughters within |eta| < trackCuts.cfgCutEta"}; + Configurable applyMtScaleWeight{"applyMtScaleWeight", false, "Weight mT-scaled signal-loss histograms by mtScaledPt/pt"}; + Configurable useGeneratedMultiplicityCentrality{"useGeneratedMultiplicityCentrality", false, "Derive MC centrality from generated multiplicity"}; + Configurable> generatedMultiplicityLowerEdges{"generatedMultiplicityLowerEdges", std::vector{500, 300, 200, 120, 80, 50, 30, 10, 0}, "Generated multiplicity lower cuts"}; + Configurable> centralityPerGeneratedMultiplicityClass{"centralityPerGeneratedMultiplicityClass", std::vector{2.5f, 7.5f, 15.0f, 25.0f, 35.0f, 45.0f, 55.0f, 75.0f, 95.0f}, "Centrality bin centres"}; + Configurable generatedMultiplicityEtaMax{"generatedMultiplicityEtaMax", 0.5f, "Max eta for generated mid-rapidity multiplicity"}; + Configurable generatedMultiplicityEtaMin{"generatedMultiplicityEtaMin", -0.5f, "Min eta for generated mid-rapidity multiplicity"}; + } mcCfg; + + struct : ConfigurableGroup { + Configurable cfgFillTrackQA{"cfgFillTrackQA", true, "Fill track QA histograms (before/after selection)"}; + Configurable cfgFillPairQA{"cfgFillPairQA", false, "Fill pair QA histograms (opening angle, deltaEta, deltaPhi)"}; + Configurable cfgFillMultQA{"cfgFillMultQA", false, "Fill centrality vs track multiplicity correlation"}; + Configurable cfgFillMixQA{"cfgFillMixQA", false, "Fill mixed-event pool QA histograms"}; + } qa; + + struct : ConfigurableGroup { + Configurable referenceFrameDaughter{"referenceFrameDaughter", 0, "Reference daughter for cos(theta*): 0 = proton, 1 = kaon"}; + Configurable activateHelicityFrame{"activateHelicityFrame", false, "Compute cos(theta*) in the Helicity frame (quantization axis = Lambda(1520) flight direction)"}; + Configurable activateCollinsSoperFrame{"activateCollinsSoperFrame", false, "Compute cos(theta*) in the Collins-Soper frame (beam bisector)"}; + Configurable activateProductionFrame{"activateProductionFrame", false, "Compute cos(theta*) wrt the production-plane normal"}; + Configurable activateBeamAxisFrame{"activateBeamAxisFrame", false, "Compute cos(theta*) in the beam-axis (Gottfried-Jackson) frame"}; + Configurable activateRandomFrame{"activateRandomFrame", false, "Compute cos(theta*) wrt a random axis (null/control test, should be flat)"}; + Configurable fillRotatedBackgroundPolarization{"fillRotatedBackgroundPolarization", false, "Also fill rotated-background polarization histograms"}; + Configurable fillMixedEventPolarization{"fillMixedEventPolarization", false, "Also fill mixed-event-background polarization histograms"}; + Configurable beamEnergy{"beamEnergy", 13600.0f, "sqrt(s) beam energy in GeV (used for Collins-Soper/beam-axis/production frames)"}; + ConfigurableAxis cosThetaBins{"cosThetaBins", {20, -1.0f, 1.0f}, "cos(theta*) binning"}; + } spinAlignment; + + struct : ConfigurableGroup { + ConfigurableAxis cfgPtAxis{"cfgPtAxis", {VARIABLE_WIDTH, 0.0, 0.1, 0.12, 0.14, 0.16, 0.18, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1.0, 1.1, 1.2, 1.25, 1.3, 1.4, 1.5, 1.6, 1.7, 1.75, 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 3.0, 3.1, 3.2, 3.3, 3.4, 3.6, 3.7, 3.8, 3.9, 4.0, 4.1, 4.2, 4.5, 4.6, 4.8, 4.9, 5.0, 5.5, 5.6, 6.0, 6.4, 6.5, 7.0, 7.2, 8.0, 9.0, 9.5, 9.6, 10.0, 11.0, 11.5, 12.0, 13.0, 14.0, 14.4, 15.0, 16.0, 18.0, 19.2, 20.0}, "#it{p}_{T} (GeV/#it{c})"}; + ConfigurableAxis cfgMassAxis{"cfgMassAxis", {70, 1.3f, 2.0f}, "Invariant mass (GeV/#it{c}^{2})"}; + ConfigurableAxis cfgCentAxis{"cfgCentAxis", {VARIABLE_WIDTH, 0.f, 5.f, 10.f, 20.f, 30.f, 40.f, 50.f, 60.f, 70.f, 80.f, 90.f, 100.f}, "Centrality (%)"}; + ConfigurableAxis cfgRapAxis{"cfgRapAxis", {20, -1.0, 1.0}, "Rapidity y"}; + ConfigurableAxis cfgVtxAxis{"cfgVtxAxis", {VARIABLE_WIDTH, -10.0f, -8.0f, -6.0f, -4.0f, -2.0f, 0.0f, 2.0f, 4.0f, 6.0f, 8.0f, 10.0f}, "Vertex z [cm] (histograms and event-mixing bins)"}; + } axes; + + struct DaughterTrackCandidate { + float px = 0.f, py = 0.f, pz = 0.f; + float pt = 0.f, eta = 0.f, phi = 0.f; + float mom = 0.f; + float dcaXY = 0.f, dcaZ = 0.f; + int sign = 0; + bool hasTOF = false; + float tpcNSigmaPr = 0.f, tpcNSigmaKa = 0.f; + float tofNSigmaPr = -999.f, tofNSigmaKa = -999.f; + int64_t globalIndex = 0; + bool hasMcLabel = false; + int mcPdgCode = 0; + int64_t mcLambda1520MotherIndex = -1; + int mcLambda1520MotherPdgCode = 0; + }; + + struct SpeciesPIDSettings { + std::vector tpcOnlyBins, tpcOnlyCuts; + std::vector combinedBins, combinedCuts; + float maxTPCNSigma = 2.f; + float thresholdMomentum = 0.f; + }; + + SpeciesPIDSettings mProtonPIDCuts; + SpeciesPIDSettings mKaonPIDCuts; + std::vector mProtonDCAPtEdges; + std::vector mProtonMaxDCAxy; + std::vector mKaonDCAPtEdges; + std::vector mKaonMaxDCAxy; + int mLambda1520AbsPdgCode = 0; + std::vector mProtonCandidates; + std::vector mKaonCandidates; + + float mBeam1Energy = 0.f; + std::array mBeam1Momentum{}; + float mBeam2Energy = 0.f; + std::array mBeam2Momentum{}; + std::mt19937 mRandomEngine{std::random_device{}()}; + std::uniform_real_distribution mUniform01{0., 1.}; + + template + static void setHistogramBinLabels(HistPtr const& hist, std::vector const& labels) + { + for (size_t i = 0; i < labels.size(); ++i) { + hist->GetXaxis()->SetBinLabel(static_cast(i) + 1, labels[i]); + } + } + + using BinningTypeFT0M = ColumnBinningPolicy; + using BinningTypeFT0A = ColumnBinningPolicy; + using BinningTypeFT0C = ColumnBinningPolicy; + + BinningTypeFT0M binningFT0M{{axes.cfgVtxAxis, axes.cfgCentAxis}}; + BinningTypeFT0A binningFT0A{{axes.cfgVtxAxis, axes.cfgCentAxis}}; + BinningTypeFT0C binningFT0C{{axes.cfgVtxAxis, axes.cfgCentAxis}}; + + void init(InitContext const&) + { + if (!a0000EnableStruct) { + return; + } + + // Re-build the mixing binnings now that the configurables are set (as data members they hold the default axes) + binningFT0M = BinningTypeFT0M{{axes.cfgVtxAxis, axes.cfgCentAxis}}; + binningFT0A = BinningTypeFT0A{{axes.cfgVtxAxis, axes.cfgCentAxis}}; + binningFT0C = BinningTypeFT0C{{axes.cfgVtxAxis, axes.cfgCentAxis}}; + + auto checkEdgesMatchCuts = [](auto const& cuts, auto const& bins, const char* label) { + const auto& cutsVec = cuts.value; + const auto& binsVec = bins.value; + if (cutsVec.size() != binsVec.size() - 1) { + LOG(fatal) << "PID bin-size mismatch for: " << label + << " (cuts=" << cutsVec.size() + << ", bins=" << binsVec.size() << ")"; + } + }; + checkEdgesMatchCuts(protonPID.protonTPCNSigmaCutPerBin, protonPID.protonTPCPIDMomentumBins, "proton TPC-only PID"); + checkEdgesMatchCuts(protonPID.protonCombinedNSigmaCutPerBin, protonPID.protonCombinedPIDMomentumBins, "proton combined PID"); + checkEdgesMatchCuts(kaonPID.kaonTPCNSigmaCutPerBin, kaonPID.kaonTPCPIDMomentumBins, "kaon TPC-only PID"); + checkEdgesMatchCuts(kaonPID.kaonCombinedNSigmaCutPerBin, kaonPID.kaonCombinedPIDMomentumBins, "kaon combined PID"); + checkEdgesMatchCuts(dcaCuts.protonMaxDCAxyPerPtBin, dcaCuts.protonDCAPtBinEdges, "proton DCAxy pT bins"); + checkEdgesMatchCuts(dcaCuts.kaonMaxDCAxyPerPtBin, dcaCuts.kaonDCAPtBinEdges, "kaon DCAxy pT bins"); + if (mcCfg.useGeneratedMultiplicityCentrality && + mcCfg.generatedMultiplicityLowerEdges.value.size() != mcCfg.centralityPerGeneratedMultiplicityClass.value.size()) { + LOG(fatal) << "generatedMultiplicityLowerEdges and centralityPerGeneratedMultiplicityClass must have the same size"; + } + + mLambda1520AbsPdgCode = std::abs(mcCfg.cfgLambda1520PdgCode.value); + if (mLambda1520AbsPdgCode == 0) { + LOG(fatal) << "cfgLambda1520PdgCode must be non-zero"; + } + if (doprocessMC || doprocessMCGen || doprocessSignalLoss) { + LOG(info) << "Lambda(1520) PDG code used for MC truth: " << mLambda1520AbsPdgCode; + } + + mProtonDCAPtEdges = dcaCuts.protonDCAPtBinEdges.value; + mProtonMaxDCAxy = dcaCuts.protonMaxDCAxyPerPtBin.value; + mKaonDCAPtEdges = dcaCuts.kaonDCAPtBinEdges.value; + mKaonMaxDCAxy = dcaCuts.kaonMaxDCAxyPerPtBin.value; + + mProtonPIDCuts.tpcOnlyBins = protonPID.protonTPCPIDMomentumBins.value; + mProtonPIDCuts.tpcOnlyCuts = protonPID.protonTPCNSigmaCutPerBin.value; + mProtonPIDCuts.combinedBins = protonPID.protonCombinedPIDMomentumBins.value; + mProtonPIDCuts.combinedCuts = protonPID.protonCombinedNSigmaCutPerBin.value; + mProtonPIDCuts.maxTPCNSigma = protonPID.maxTPCNSigmaProton; + mProtonPIDCuts.thresholdMomentum = protonPID.protonThresholdMomentum; + + mKaonPIDCuts.tpcOnlyBins = kaonPID.kaonTPCPIDMomentumBins.value; + mKaonPIDCuts.tpcOnlyCuts = kaonPID.kaonTPCNSigmaCutPerBin.value; + mKaonPIDCuts.combinedBins = kaonPID.kaonCombinedPIDMomentumBins.value; + mKaonPIDCuts.combinedCuts = kaonPID.kaonCombinedNSigmaCutPerBin.value; + mKaonPIDCuts.maxTPCNSigma = kaonPID.maxTPCNSigmaKaon; + mKaonPIDCuts.thresholdMomentum = kaonPID.kaonThresholdMomentum; + + auto checkTPCOnlyMomentumCoverage = [](std::vector const& edges, float threshold, const char* label) { + if (threshold > 0.f && (edges.empty() || edges.front() > 0.f || edges.back() < threshold)) { + LOG(warning) << label << ": TPC-only momentum bins do not cover [0, " << threshold + << ") GeV/c - tracks WITHOUT TOF in the uncovered range are rejected"; + } + }; + if (!pidShared.useTPCOnlyPIDAtAllMomenta) { + checkTPCOnlyMomentumCoverage(mProtonPIDCuts.tpcOnlyBins, mProtonPIDCuts.thresholdMomentum, "proton PID"); + checkTPCOnlyMomentumCoverage(mKaonPIDCuts.tpcOnlyBins, mKaonPIDCuts.thresholdMomentum, "kaon PID"); + } + + { + const auto sqrtS = static_cast(spinAlignment.beamEnergy); + double beamMomentum = std::sqrt(sqrtS * sqrtS / 4. - static_cast(ProtonMass) * static_cast(ProtonMass)); + mBeam1Energy = static_cast(sqrtS * 0.5); + mBeam1Momentum = {0.f, 0.f, static_cast(-beamMomentum)}; + mBeam2Energy = static_cast(sqrtS * 0.5); + mBeam2Momentum = {0.f, 0.f, static_cast(beamMomentum)}; + } + + { + const int nActiveFrames = static_cast(spinAlignment.activateHelicityFrame) + + static_cast(spinAlignment.activateCollinsSoperFrame) + + static_cast(spinAlignment.activateProductionFrame) + + static_cast(spinAlignment.activateBeamAxisFrame) + + static_cast(spinAlignment.activateRandomFrame); + if (nActiveFrames > 1) { + LOG(fatal) << "Multiple polarization frames activated simultaneously. " + << "Please activate only ONE frame at a time to prevent data mixing in the cos(theta*) THnSparse."; + } + } + + const AxisSpec ptAxis{axes.cfgPtAxis, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec massAxis{axes.cfgMassAxis, "M_{inv} (GeV/#it{c}^{2})"}; + const AxisSpec centAxis{axes.cfgCentAxis, "Centrality (%)"}; + const AxisSpec rapAxis{axes.cfgRapAxis, "Rapidity y"}; + const AxisSpec vtxAxis{axes.cfgVtxAxis, "Vertex z [cm]"}; + const AxisSpec nSigmaTPCaxis{100, -5., 5., "n#sigma^{TPC}"}; + const AxisSpec nSigmaTOFaxis{100, -5., 5., "n#sigma^{TOF}"}; + const AxisSpec momentumAxis{100, 0., 10., "p (GeV/#it{c})"}; + const AxisSpec ptForPIDAxis{100, 0., 10., "p_{T} (GeV/#it{c})"}; + const AxisSpec dcaXYaxis{200, -0.2f, 0.2f, "DCA_{xy} (cm)"}; + const AxisSpec dcaZaxis{200, -1.2f, 1.2f, "DCA_{z} (cm)"}; + const AxisSpec tpcClusAxis{200, 0, 200, "N_{clusters}^{TPC}"}; + const AxisSpec tpcRowsAxis{200, 0, 200, "Crossed rows^{TPC}"}; + const AxisSpec tpcSignalAxis{500, 0, 500, "TPC signal (a.u.)"}; + const AxisSpec occupancyAxis{200, 0, 10000, "Track occupancy [-40, 100] ns"}; + const AxisSpec etaAxis{150, -1.5, 1.5, "#eta"}; + const AxisSpec phiAxis{72, 0., o2::constants::math::TwoPI, "#varphi (rad)"}; + const AxisSpec openAngleAxis{100, 0., o2::constants::math::PI, "Opening angle (rad)"}; + const AxisSpec deltaEtaAxis{160, 0., 1.6, "|#Delta#eta|"}; + const AxisSpec deltaPhiAxis{100, 0., o2::constants::math::PI, "|#Delta#varphi| (rad)"}; + const std::vector eventCutLabels{"All", "Vz", "kNoITSROFrameBorder", "sel8", "INEL>0", "Occupancy", "NoSameBunchPileup", "GoodZvtxFT0vsPV", "GoodITSLayersAll", "Accepted"}; + + if (doprocessSameEvent) { + histos.add("EventSelection/SameEvent/hCutFlow", "Data event cut flow", kTH1F, {{10, -0.5f, 9.5f}}); + setHistogramBinLabels(histos.get(HIST("EventSelection/SameEvent/hCutFlow")), eventCutLabels); + + histos.add("EventQA/SameEvent/hVertexZ", "Vertex z; z (cm)", kTH1F, {{400, -20., 20.}}); + histos.add("EventQA/SameEvent/hPrimaryVertexContributors", "PV contributors; N", kTH1F, {{2001, -0.5f, 2000.5f}}); + histos.add("EventQA/SameEvent/hCentralityPercent", "Centrality", kTH1F, {centAxis}); + histos.add("EventQA/SameEvent/hTrackOccupancy", "Occupancy in time range", kTH1F, {occupancyAxis}); + histos.add("EventQA/SameEvent/hCentralityVsVertexZ", "Centrality vs vertex z", kTH2F, {vtxAxis, centAxis}); + histos.add("EventQA/SameEvent/hCentralityVsTrackOccupancy", "Centrality vs occupancy", kTH2F, {occupancyAxis, centAxis}); + + if (qa.cfgFillMultQA) { + histos.add("EventQA/SameEvent/hCentralityVsFilteredTracksVsPVTracks", "Centrality vs filtered tracks vs PV tracks", kTHnSparseF, {{110, 0, 110, "Centrality"}, {500, 0, 5000, "Filtered tracks"}, {500, 0, 5000, "PV tracks"}}); + } + + if (qa.cfgFillTrackQA) { + histos.add("TrackQA/BeforeSelection/Proton/hTPCNSigmaVsMomentum", "TPC n#sigma proton vs p (before PID)", kTH2F, {momentumAxis, nSigmaTPCaxis}); + histos.add("TrackQA/BeforeSelection/Proton/hTOFNSigmaVsMomentum", "TOF n#sigma proton vs p (before PID)", kTH2F, {momentumAxis, nSigmaTOFaxis}); + histos.add("TrackQA/BeforeSelection/Proton/hTOFNSigmaVsTPCNSigma", "TOF vs TPC n#sigma proton (before PID)", kTH2F, {nSigmaTPCaxis, nSigmaTOFaxis}); + histos.add("TrackQA/BeforeSelection/Kaon/hTPCNSigmaVsMomentum", "TPC n#sigma kaon vs p (before PID)", kTH2F, {momentumAxis, nSigmaTPCaxis}); + histos.add("TrackQA/BeforeSelection/Kaon/hTOFNSigmaVsMomentum", "TOF n#sigma kaon vs p (before PID)", kTH2F, {momentumAxis, nSigmaTOFaxis}); + histos.add("TrackQA/BeforeSelection/Kaon/hTOFNSigmaVsTPCNSigma", "TOF vs TPC n#sigma kaon (before PID)", kTH2F, {nSigmaTPCaxis, nSigmaTOFaxis}); + + histos.add("TrackQA/BeforeSelection/AllTracks/hEta", "Reco dN/d#eta; #eta; Tracks", kTH1F, {{50, -1.0, 1.0}}); + histos.add("TrackQA/BeforeSelection/AllTracks/hPt", "Reco pT; p_{T} (GeV/c); Tracks", kTH1F, {ptForPIDAxis}); + histos.add("TrackQA/BeforeSelection/AllTracks/hPhi", "Reco #varphi; #varphi (rad); Tracks", kTH1F, {phiAxis}); + histos.add("TrackQA/BeforeSelection/AllTracks/hEtaPhi", "Reco #eta vs #varphi; #eta; #varphi", kTH2F, {etaAxis, phiAxis}); + + histos.add("TrackQA/AfterSelection/Proton/hDCAxyVsPt", "Proton DCA_{xy} vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, dcaXYaxis}); + histos.add("TrackQA/AfterSelection/Proton/hDCAzVsPt", "Proton DCA_{z} vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, dcaZaxis}); + histos.add("TrackQA/AfterSelection/Proton/hTPCNSigmaVsMomentum", "Proton TPC n#sigma vs p (after cuts)", kTH2F, {momentumAxis, nSigmaTPCaxis}); + histos.add("TrackQA/AfterSelection/Proton/hTPCNSigmaVsPt", "Proton TPC n#sigma vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("TrackQA/AfterSelection/Proton/hTPCNSigmaVsCentrality", "Proton TPC n#sigma vs centrality", kTH2F, {centAxis, nSigmaTPCaxis}); + histos.add("TrackQA/AfterSelection/Proton/hTOFNSigmaVsMomentum", "Proton TOF n#sigma vs p (after cuts)", kTH2F, {momentumAxis, nSigmaTOFaxis}); + histos.add("TrackQA/AfterSelection/Proton/hTOFNSigmaVsPt", "Proton TOF n#sigma vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, nSigmaTOFaxis}); + histos.add("TrackQA/AfterSelection/Proton/hTOFNSigmaVsCentrality", "Proton TOF n#sigma vs centrality", kTH2F, {centAxis, nSigmaTOFaxis}); + histos.add("TrackQA/AfterSelection/Proton/hTOFNSigmaVsTPCNSigma", "Proton TOF vs TPC n#sigma (after cuts)", kTH2F, {nSigmaTPCaxis, nSigmaTOFaxis}); + histos.add("TrackQA/AfterSelection/Proton/hTPCNSigmaPionHypothesisVsPt", "Proton track: TPC n#sigma pion contamination", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("TrackQA/AfterSelection/Proton/hTPCNSigmaKaonHypothesisVsPt", "Proton track: TPC n#sigma kaon contamination", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("TrackQA/AfterSelection/Proton/hTPCdEdxVsMomentum", "Proton TPC signal vs p (after cuts)", kTH2F, {momentumAxis, tpcSignalAxis}); + histos.add("TrackQA/AfterSelection/Proton/hTPCClustersVsPhi", "Proton TPC clusters vs #varphi", kTH2F, {tpcClusAxis, phiAxis}); + histos.add("TrackQA/AfterSelection/Proton/hTPCCrossedRowsVsPt", "Proton TPC crossed rows vs p_{T}", kTH2F, {ptForPIDAxis, tpcRowsAxis}); + histos.add("TrackQA/AfterSelection/Proton/hTPCFoundClustersVsPt", "Proton TPC clusters found vs p_{T}", kTH2F, {ptForPIDAxis, tpcClusAxis}); + histos.add("TrackQA/AfterSelection/Proton/hPt", "Proton p_{T}; p_{T} (GeV/c); Tracks", kTH1F, {ptForPIDAxis}); + histos.add("TrackQA/AfterSelection/Proton/hEta", "Proton #eta; #eta; Tracks", kTH1F, {etaAxis}); + + histos.add("TrackQA/AfterSelection/Kaon/hDCAxyVsPt", "Kaon DCA_{xy} vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, dcaXYaxis}); + histos.add("TrackQA/AfterSelection/Kaon/hDCAzVsPt", "Kaon DCA_{z} vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, dcaZaxis}); + histos.add("TrackQA/AfterSelection/Kaon/hTPCNSigmaVsMomentum", "Kaon TPC n#sigma vs p (after cuts)", kTH2F, {momentumAxis, nSigmaTPCaxis}); + histos.add("TrackQA/AfterSelection/Kaon/hTPCNSigmaVsPt", "Kaon TPC n#sigma vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("TrackQA/AfterSelection/Kaon/hTPCNSigmaVsCentrality", "Kaon TPC n#sigma vs centrality", kTH2F, {centAxis, nSigmaTPCaxis}); + histos.add("TrackQA/AfterSelection/Kaon/hTOFNSigmaVsMomentum", "Kaon TOF n#sigma vs p (after cuts)", kTH2F, {momentumAxis, nSigmaTOFaxis}); + histos.add("TrackQA/AfterSelection/Kaon/hTOFNSigmaVsPt", "Kaon TOF n#sigma vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, nSigmaTOFaxis}); + histos.add("TrackQA/AfterSelection/Kaon/hTOFNSigmaVsCentrality", "Kaon TOF n#sigma vs centrality", kTH2F, {centAxis, nSigmaTOFaxis}); + histos.add("TrackQA/AfterSelection/Kaon/hTOFNSigmaVsTPCNSigma", "Kaon TOF vs TPC n#sigma (after cuts)", kTH2F, {nSigmaTPCaxis, nSigmaTOFaxis}); + histos.add("TrackQA/AfterSelection/Kaon/hTPCNSigmaPionHypothesisVsPt", "Kaon track: TPC n#sigma pion contamination", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("TrackQA/AfterSelection/Kaon/hTPCNSigmaProtonHypothesisVsPt", "Kaon track: TPC n#sigma proton contamination", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("TrackQA/AfterSelection/Kaon/hTPCdEdxVsMomentum", "Kaon TPC signal vs p (after cuts)", kTH2F, {momentumAxis, tpcSignalAxis}); + histos.add("TrackQA/AfterSelection/Kaon/hTPCClustersVsPhi", "Kaon TPC clusters vs #varphi", kTH2F, {tpcClusAxis, phiAxis}); + histos.add("TrackQA/AfterSelection/Kaon/hTPCCrossedRowsVsPt", "Kaon TPC crossed rows vs p_{T}", kTH2F, {ptForPIDAxis, tpcRowsAxis}); + histos.add("TrackQA/AfterSelection/Kaon/hTPCFoundClustersVsPt", "Kaon TPC clusters found vs p_{T}", kTH2F, {ptForPIDAxis, tpcClusAxis}); + histos.add("TrackQA/AfterSelection/Kaon/hPt", "Kaon p_{T}; p_{T} (GeV/c); Tracks", kTH1F, {ptForPIDAxis}); + histos.add("TrackQA/AfterSelection/Kaon/hEta", "Kaon #eta; #eta; Tracks", kTH1F, {etaAxis}); + } + + if (qa.cfgFillPairQA) { + histos.add("PairQA/hOpeningAngleBeforePairCuts", "Opening angle (before pair cuts)", kTH1F, {openAngleAxis}); + histos.add("PairQA/hOpeningAngleAfterPairCuts", "Opening angle (after pair cuts)", kTH1F, {openAngleAxis}); + histos.add("PairQA/hDeltaEtaBeforePairCuts", "|#Delta#eta| (before pair cuts)", kTH1F, {deltaEtaAxis}); + histos.add("PairQA/hDeltaEtaAfterPairCuts", "|#Delta#eta| (after pair cuts)", kTH1F, {deltaEtaAxis}); + histos.add("PairQA/hDeltaPhiBeforePairCuts", "|#Delta#varphi| (before pair cuts)", kTH1F, {deltaPhiAxis}); + histos.add("PairQA/hDeltaPhiAfterPairCuts", "|#Delta#varphi| (after pair cuts)", kTH1F, {deltaPhiAxis}); + } + + if (!isPolarizationActive()) { + histos.add("Lambda1520/SameEvent/hUnlikeSignLambda1520", "THnSparse #Lambda(1520) same-event", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("Lambda1520/SameEvent/hUnlikeSignAntiLambda1520", "THnSparse #bar{#Lambda}(1520) same-event", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("Lambda1520/SameEvent/hLikeSignPlusPlus", "THnSparse like-sign p K^{+}", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("Lambda1520/SameEvent/hLikeSignMinusMinus", "THnSparse like-sign #bar{p} K^{-}", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + + if (rotBkg.enableRotationalBackground) { + histos.add("Lambda1520/SameEvent/hRotatedBackgroundLambda1520", "THnSparse #Lambda(1520) rotational background", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("Lambda1520/SameEvent/hRotatedBackgroundAntiLambda1520", "THnSparse #bar{#Lambda}(1520) rotational background", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.get(HIST("Lambda1520/SameEvent/hRotatedBackgroundLambda1520"))->Sumw2(); + histos.get(HIST("Lambda1520/SameEvent/hRotatedBackgroundAntiLambda1520"))->Sumw2(); + } + } + + if (isPolarizationActive()) { + const AxisSpec cosThetaAxis{spinAlignment.cosThetaBins, "cos#theta*"}; + histos.add("SpinAlignment/SameEvent/hCosThetaStarLambda1520", "cos#theta* #Lambda(1520) unlike-sign", kTHnSparseF, {massAxis, ptAxis, centAxis, cosThetaAxis}); + histos.add("SpinAlignment/SameEvent/hCosThetaStarAntiLambda1520", "cos#theta* #bar{#Lambda}(1520) unlike-sign", kTHnSparseF, {massAxis, ptAxis, centAxis, cosThetaAxis}); + histos.add("SpinAlignment/SameEvent/hCosThetaStarLikeSignPP", "cos#theta* like-sign p K^{+}", kTHnSparseF, {massAxis, ptAxis, centAxis, cosThetaAxis}); + histos.add("SpinAlignment/SameEvent/hCosThetaStarLikeSignMM", "cos#theta* like-sign #bar{p} K^{-}", kTHnSparseF, {massAxis, ptAxis, centAxis, cosThetaAxis}); + + if (spinAlignment.fillRotatedBackgroundPolarization && rotBkg.enableRotationalBackground) { + histos.add("SpinAlignment/SameEvent/hCosThetaStarRotatedLambda1520", "cos#theta* rotated bkg #Lambda(1520)", kTHnSparseF, {massAxis, ptAxis, centAxis, cosThetaAxis}); + histos.add("SpinAlignment/SameEvent/hCosThetaStarRotatedAntiLambda1520", "cos#theta* rotated bkg #bar{#Lambda}(1520)", kTHnSparseF, {massAxis, ptAxis, centAxis, cosThetaAxis}); + } + } + } + + if (doprocessMixedEvent) { + if (!isPolarizationActive()) { + histos.add("Lambda1520/MixedEvent/hMixedEventLambda1520", "THnSparse #Lambda(1520) event mixing", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("Lambda1520/MixedEvent/hMixedEventAntiLambda1520", "THnSparse #bar{#Lambda}(1520) event mixing", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + + if (mixingCfg.cfgFillMixLikeSign) { + histos.add("Lambda1520/MixedEvent/hMixedEventLikeSignPlusPlus", "THnSparse like-sign p K^{+} event mixing", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("Lambda1520/MixedEvent/hMixedEventLikeSignMinusMinus", "THnSparse like-sign #bar{p} K^{-} event mixing", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + } + + if (qa.cfgFillMixQA) { + histos.add("MixingQA/hMixedEventPoolVertexZ", "Mixed event pool: vertex z; z (cm); Pairs", kTH1F, {vtxAxis}); + histos.add("MixingQA/hMixedEventPoolCentrality", "Mixed event pool: centrality; Centrality (%); Pairs", kTH1F, {centAxis}); + histos.add("MixingQA/hMixedEventPoolVertexZVsCentrality", "Mixed event pool: vertex z vs centrality", kTH2F, {vtxAxis, centAxis}); + histos.add("MixingQA/hMixedEventCollisionPairs", "Number of mixed-event collision pairs processed", kTH1F, {{1, 0.5f, 1.5f}}); + } + } + + if (isPolarizationActive() && spinAlignment.fillMixedEventPolarization) { + const AxisSpec cosThetaAxis{spinAlignment.cosThetaBins, "cos#theta*"}; + histos.add("SpinAlignment/MixedEvent/hCosThetaStarLambda1520", "cos#theta* mixed-event #Lambda(1520)", kTHnSparseF, {massAxis, ptAxis, centAxis, cosThetaAxis}); + histos.add("SpinAlignment/MixedEvent/hCosThetaStarAntiLambda1520", "cos#theta* mixed-event #bar{#Lambda}(1520)", kTHnSparseF, {massAxis, ptAxis, centAxis, cosThetaAxis}); + histos.add("SpinAlignment/MixedEvent/hCosThetaStarLikeSignPP", "cos#theta* mixed-event like-sign p K^{+}", kTHnSparseF, {massAxis, ptAxis, centAxis, cosThetaAxis}); + histos.add("SpinAlignment/MixedEvent/hCosThetaStarLikeSignMM", "cos#theta* mixed-event like-sign #bar{p} K^{-}", kTHnSparseF, {massAxis, ptAxis, centAxis, cosThetaAxis}); + } + } + + if (doprocessMC) { + histos.add("EventSelection/MCReco/hCutFlow", "MC reconstructed event cut flow", kTH1F, {{10, -0.5f, 9.5f}}); + setHistogramBinLabels(histos.get(HIST("EventSelection/MCReco/hCutFlow")), eventCutLabels); + + histos.add("EventQA/MCReco/hSelectedRecoEvents", "Reconstructed INEL>0 events (Nrec, MC reco)", kTH1F, {centAxis}); + histos.add("EventQA/MCReco/hVertexZ", "MC reco vertex z; z (cm)", kTH1F, {{400, -20., 20.}}); + histos.add("EventQA/MCReco/hPrimaryVertexContributors", "MC reco PV contributors; N", kTH1F, {{2001, -0.5f, 2000.5f}}); + + histos.add("Lambda1520/MCReco/hAllPairsLambda1520", "THnSparse #Lambda(1520) MC reconstructed (pre-truth-matching)", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("Lambda1520/MCReco/hAllPairsAntiLambda1520", "THnSparse #bar{#Lambda}(1520) MC reconstructed (pre-truth-matching)", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + + histos.add("Lambda1520/MCReco/hTruthMatchedLambda1520", "THnSparse #Lambda(1520) MC reconstructed (truth-matched)", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("Lambda1520/MCReco/hTruthMatchedAntiLambda1520", "THnSparse #bar{#Lambda}(1520) MC reconstructed (truth-matched)", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + + histos.add("MCRecoQA/hRecoProtonPtFromLambda1520", "Rec proton from #Lambda(1520)", kTH1F, {ptAxis}); + histos.add("MCRecoQA/hRecoProtonPtFromAntiLambda1520", "Rec proton from #bar{#Lambda}(1520)", kTH1F, {ptAxis}); + histos.add("MCRecoQA/hRecoKaonPtFromLambda1520", "Rec kaon from #Lambda(1520)", kTH1F, {ptAxis}); + histos.add("MCRecoQA/hRecoKaonPtFromAntiLambda1520", "Rec kaon from #bar{#Lambda}(1520)", kTH1F, {ptAxis}); + + histos.add("TrackQA/MCReco/AllTracks/hEta", "MC Reco dN/d#eta; #eta; Tracks", kTH1F, {{50, -1.0, 1.0}}); + histos.add("TrackQA/MCReco/AllTracks/hPt", "MC Reco pT; p_{T} (GeV/c); Tracks", kTH1F, {ptForPIDAxis}); + histos.add("TrackQA/MCReco/AllTracks/hPhi", "MC Reco #varphi; #varphi (rad); Tracks", kTH1F, {phiAxis}); + histos.add("TrackQA/MCReco/AllTracks/hEtaPhi", "MC Reco #eta vs #varphi; #eta; #varphi", kTH2F, {etaAxis, phiAxis}); + histos.add("PairQA/MCReco/hTruthMatchedRapidityVsPt", "Truth-matched reconstructed #Lambda(1520): y vs p_{T}", kTH2F, {ptAxis, rapAxis}); + + if (qa.cfgFillTrackQA) { + histos.add("TrackQA/MCReco/BeforeSelection/Proton/hTPCNSigmaVsMomentum", "MC reco: TPC n#sigma proton vs p (before PID)", kTH2F, {momentumAxis, nSigmaTPCaxis}); + histos.add("TrackQA/MCReco/BeforeSelection/Proton/hTOFNSigmaVsMomentum", "MC reco: TOF n#sigma proton vs p (before PID)", kTH2F, {momentumAxis, nSigmaTOFaxis}); + histos.add("TrackQA/MCReco/BeforeSelection/Proton/hTOFNSigmaVsTPCNSigma", "MC reco: TOF vs TPC n#sigma proton (before PID)", kTH2F, {nSigmaTPCaxis, nSigmaTOFaxis}); + histos.add("TrackQA/MCReco/BeforeSelection/Kaon/hTPCNSigmaVsMomentum", "MC reco: TPC n#sigma kaon vs p (before PID)", kTH2F, {momentumAxis, nSigmaTPCaxis}); + histos.add("TrackQA/MCReco/BeforeSelection/Kaon/hTOFNSigmaVsMomentum", "MC reco: TOF n#sigma kaon vs p (before PID)", kTH2F, {momentumAxis, nSigmaTOFaxis}); + histos.add("TrackQA/MCReco/BeforeSelection/Kaon/hTOFNSigmaVsTPCNSigma", "MC reco: TOF vs TPC n#sigma kaon (before PID)", kTH2F, {nSigmaTPCaxis, nSigmaTOFaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hDCAxyVsPt", "MC reco: Proton DCA_{xy} vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, dcaXYaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hDCAzVsPt", "MC reco: Proton DCA_{z} vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, dcaZaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hTPCNSigmaVsMomentum", "MC reco: Proton TPC n#sigma vs p (after cuts)", kTH2F, {momentumAxis, nSigmaTPCaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hTPCNSigmaVsPt", "MC reco: Proton TPC n#sigma vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hTPCNSigmaVsCentrality", "MC reco: Proton TPC n#sigma vs centrality", kTH2F, {centAxis, nSigmaTPCaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hTOFNSigmaVsMomentum", "MC reco: Proton TOF n#sigma vs p (after cuts)", kTH2F, {momentumAxis, nSigmaTOFaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hTOFNSigmaVsPt", "MC reco: Proton TOF n#sigma vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, nSigmaTOFaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hTOFNSigmaVsCentrality", "MC reco: Proton TOF n#sigma vs centrality", kTH2F, {centAxis, nSigmaTOFaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hTOFNSigmaVsTPCNSigma", "MC reco: Proton TOF vs TPC n#sigma (after cuts)", kTH2F, {nSigmaTPCaxis, nSigmaTOFaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hTPCNSigmaPionHypothesisVsPt", "MC reco: Proton track: TPC n#sigma pion contamination", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hTPCNSigmaKaonHypothesisVsPt", "MC reco: Proton track: TPC n#sigma kaon contamination", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hTPCdEdxVsMomentum", "MC reco: Proton TPC signal vs p (after cuts)", kTH2F, {momentumAxis, tpcSignalAxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hTPCClustersVsPhi", "MC reco: Proton TPC clusters vs #varphi", kTH2F, {tpcClusAxis, phiAxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hTPCCrossedRowsVsPt", "MC reco: Proton TPC crossed rows vs p_{T}", kTH2F, {ptForPIDAxis, tpcRowsAxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hTPCFoundClustersVsPt", "MC reco: Proton TPC clusters found vs p_{T}", kTH2F, {ptForPIDAxis, tpcClusAxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hPt", "MC reco: Proton p_{T}; p_{T} (GeV/c); Tracks", kTH1F, {ptForPIDAxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hEta", "MC reco: Proton #eta; #eta; Tracks", kTH1F, {etaAxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hDCAxyVsPt", "MC reco: Kaon DCA_{xy} vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, dcaXYaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hDCAzVsPt", "MC reco: Kaon DCA_{z} vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, dcaZaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hTPCNSigmaVsMomentum", "MC reco: Kaon TPC n#sigma vs p (after cuts)", kTH2F, {momentumAxis, nSigmaTPCaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hTPCNSigmaVsPt", "MC reco: Kaon TPC n#sigma vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hTPCNSigmaVsCentrality", "MC reco: Kaon TPC n#sigma vs centrality", kTH2F, {centAxis, nSigmaTPCaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hTOFNSigmaVsMomentum", "MC reco: Kaon TOF n#sigma vs p (after cuts)", kTH2F, {momentumAxis, nSigmaTOFaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hTOFNSigmaVsPt", "MC reco: Kaon TOF n#sigma vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, nSigmaTOFaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hTOFNSigmaVsCentrality", "MC reco: Kaon TOF n#sigma vs centrality", kTH2F, {centAxis, nSigmaTOFaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hTOFNSigmaVsTPCNSigma", "MC reco: Kaon TOF vs TPC n#sigma (after cuts)", kTH2F, {nSigmaTPCaxis, nSigmaTOFaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hTPCNSigmaPionHypothesisVsPt", "MC reco: Kaon track: TPC n#sigma pion contamination", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hTPCNSigmaProtonHypothesisVsPt", "MC reco: Kaon track: TPC n#sigma proton contamination", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hTPCdEdxVsMomentum", "MC reco: Kaon TPC signal vs p (after cuts)", kTH2F, {momentumAxis, tpcSignalAxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hTPCClustersVsPhi", "MC reco: Kaon TPC clusters vs #varphi", kTH2F, {tpcClusAxis, phiAxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hTPCCrossedRowsVsPt", "MC reco: Kaon TPC crossed rows vs p_{T}", kTH2F, {ptForPIDAxis, tpcRowsAxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hTPCFoundClustersVsPt", "MC reco: Kaon TPC clusters found vs p_{T}", kTH2F, {ptForPIDAxis, tpcClusAxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hPt", "MC reco: Kaon p_{T}; p_{T} (GeV/c); Tracks", kTH1F, {ptForPIDAxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hEta", "MC reco: Kaon #eta; #eta; Tracks", kTH1F, {etaAxis}); + } + + histos.add("TrackQA/MCReco/TruthMatchedProton/hDCAxyVsPt", "Truth-matched proton DCA_{xy} vs p_{T}", kTH2F, {ptForPIDAxis, dcaXYaxis}); + histos.add("TrackQA/MCReco/TruthMatchedProton/hDCAzVsPt", "Truth-matched proton DCA_{z} vs p_{T}", kTH2F, {ptForPIDAxis, dcaZaxis}); + histos.add("TrackQA/MCReco/TruthMatchedProton/hTPCNSigmaVsPt", "Truth-matched proton TPC n#sigma vs p_{T}", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("TrackQA/MCReco/TruthMatchedProton/hTOFNSigmaVsPt", "Truth-matched proton TOF n#sigma vs p_{T}", kTH2F, {ptForPIDAxis, nSigmaTOFaxis}); + histos.add("TrackQA/MCReco/TruthMatchedKaon/hDCAxyVsPt", "Truth-matched kaon DCA_{xy} vs p_{T}", kTH2F, {ptForPIDAxis, dcaXYaxis}); + histos.add("TrackQA/MCReco/TruthMatchedKaon/hDCAzVsPt", "Truth-matched kaon DCA_{z} vs p_{T}", kTH2F, {ptForPIDAxis, dcaZaxis}); + histos.add("TrackQA/MCReco/TruthMatchedKaon/hTPCNSigmaVsPt", "Truth-matched kaon TPC n#sigma vs p_{T}", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("TrackQA/MCReco/TruthMatchedKaon/hTOFNSigmaVsPt", "Truth-matched kaon TOF n#sigma vs p_{T}", kTH2F, {ptForPIDAxis, nSigmaTOFaxis}); + + histos.add("MCRecoQA/hGeneratedToReconstructedCutFlow", "Generated-to-reconstructed #Lambda(1520) cut flow (efficiency QA)", kTH1F, {{19, -0.5f, 18.5f}}); + setHistogramBinLabels(histos.get(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow")), + {"Generated Lambda(1520) (event accepted)", "Lambda(1520) -> p + K", "Rapidity accepted", + "Proton acceptance", "Kaon acceptance", "Both daughters acceptance", + "Proton reconstructed", "Kaon reconstructed", "Both daughters reconstructed", + "Proton track quality", "Kaon track quality", "Both track quality", + "Proton PID", "Kaon PID", "Both PID", + "Proton DCA", "Kaon DCA", "Both DCA", + "Pair cuts (final reconstructed)"}); + + histos.add("MCRecoQA/hTruthMatchingCutFlow", "MC truth-matching cut flow (efficiency QA)", kTH1F, {{6, -0.5f, 5.5f}}); + setHistogramBinLabels(histos.get(HIST("MCRecoQA/hTruthMatchingCutFlow")), + {"Reco pair", "Proton has MC label", "Kaon has MC label", "Proton is proton", "Kaon is kaon", "Common Lambda(1520) mother"}); + + if (isPolarizationActive()) { + const AxisSpec cosThetaAxis{spinAlignment.cosThetaBins, "cos#theta*"}; + histos.add("SpinAlignment/MCReco/hCosThetaStarTruthMatchedLambda1520", "cos#theta* truth-matched #Lambda(1520)", kTHnSparseF, {massAxis, ptAxis, centAxis, cosThetaAxis}); + histos.add("SpinAlignment/MCReco/hCosThetaStarTruthMatchedAntiLambda1520", "cos#theta* truth-matched #bar{#Lambda}(1520)", kTHnSparseF, {massAxis, ptAxis, centAxis, cosThetaAxis}); + } + + const AxisSpec massResolutionAxis{200, -0.05f, 0.05f, "M_{reco} - M_{gen} (GeV/c^{2})"}; + histos.add("Lambda1520/MCReco/hMassResolutionLambda1520", "(M_{reco} - M_{gen}) truth-matched #Lambda(1520)", kTHnSparseF, {massResolutionAxis, ptAxis, centAxis}); + histos.add("Lambda1520/MCReco/hMassResolutionAntiLambda1520", "(M_{reco} - M_{gen}) truth-matched #bar{#Lambda}(1520)", kTHnSparseF, {massResolutionAxis, ptAxis, centAxis}); + if (isPolarizationActive()) { + const AxisSpec cosThetaAxisRes{spinAlignment.cosThetaBins, "cos#theta*"}; + histos.add("SpinAlignment/MCReco/hMassResolutionCosThetaLambda1520", "(M_{reco} - M_{gen}) vs cos#theta* #Lambda(1520)", kTHnSparseF, {massResolutionAxis, ptAxis, centAxis, cosThetaAxisRes}); + histos.add("SpinAlignment/MCReco/hMassResolutionCosThetaAntiLambda1520", "(M_{reco} - M_{gen}) vs cos#theta* #bar{#Lambda}(1520)", kTHnSparseF, {massResolutionAxis, ptAxis, centAxis, cosThetaAxisRes}); + } + } + + if (doprocessMCEventMixing) { + histos.add("Lambda1520/MCRecoMixedEvent/hMixedEventLambda1520", "THnSparse #Lambda(1520) MC reconstructed event mixing", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("Lambda1520/MCRecoMixedEvent/hMixedEventAntiLambda1520", "THnSparse #bar{#Lambda}(1520) MC reconstructed event mixing", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("MixingQA/MCReco/hMixedEventCollisionPairs", "Number of MC mixed-event collision pairs processed", kTH1F, {{1, 0.5f, 1.5f}}); + histos.add("MixingQA/MCReco/hMixedEventCollisionPairsVsCentrality", "MC mixed-event collision pairs vs centrality", kTH1F, {centAxis}); + } + + if (doprocessMCGen) { + histos.add("EventSelection/MCGen/hCutFlow", "MC generated event cut flow", kTH1F, {{6, -0.5f, 5.5f}}); + setHistogramBinLabels(histos.get(HIST("EventSelection/MCGen/hCutFlow")), + {"All", "Vz", "INEL>0", "Centrality", "Final generated event", "Associated reco event accepted"}); + + histos.add("Lambda1520/MCGen/hGeneratedLambda1520", "THnSparse #Lambda(1520) generated", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("Lambda1520/MCGen/hGeneratedAntiLambda1520", "THnSparse #bar{#Lambda}(1520) generated", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + + histos.add("Lambda1520/MCGen/hGeneratedLambda1520InAcceptedEvents", "THnSparse #Lambda(1520) generated, event-accepted", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("Lambda1520/MCGen/hGeneratedAntiLambda1520InAcceptedEvents", "THnSparse #bar{#Lambda}(1520) generated, event-accepted", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + + histos.add("GeneratedQA/hProtonDaughterPtFromLambda1520", "Gen proton from #Lambda(1520)", kTH1F, {ptAxis}); + histos.add("GeneratedQA/hProtonDaughterPtFromAntiLambda1520", "Gen proton from #bar{#Lambda}(1520)", kTH1F, {ptAxis}); + histos.add("GeneratedQA/hKaonDaughterPtFromLambda1520", "Gen kaon from #Lambda(1520)", kTH1F, {ptAxis}); + histos.add("GeneratedQA/hKaonDaughterPtFromAntiLambda1520", "Gen kaon from #bar{#Lambda}(1520)", kTH1F, {ptAxis}); + histos.add("GeneratedQA/hLambda1520GeneratedCutFlow", "Generated #Lambda(1520) cut flow", kTH1F, {{8, -0.5f, 7.5f}}); + setHistogramBinLabels(histos.get(HIST("GeneratedQA/hLambda1520GeneratedCutFlow")), + {"All particles", "producedByGenerator", "Lambda(1520) PDG", "Decay p + K", "Rapidity", "Daughter eta", "Eta", "Final"}); + histos.add("GeneratedQA/hEtaVsPtBeforeCuts", "Generated #Lambda(1520) #eta-p_{T} (before kinematic cuts)", kTH2F, {etaAxis, ptAxis}); + histos.add("GeneratedQA/hPhiVsRapidityBeforeCuts", "Generated #Lambda(1520) #varphi-y (before kinematic cuts)", kTH2F, {phiAxis, rapAxis}); + histos.add("GeneratedQA/hEtaVsPtAfterCuts", "Generated #Lambda(1520) #eta-p_{T} (after kinematic cuts)", kTH2F, {etaAxis, ptAxis}); + histos.add("GeneratedQA/hPhiVsRapidityAfterCuts", "Generated #Lambda(1520) #varphi-y (after kinematic cuts)", kTH2F, {phiAxis, rapAxis}); + histos.add("GeneratedQA/hRapidityVsPtFinal", "Generated #Lambda(1520) y vs p_{T} (final)", kTH2F, {ptAxis, rapAxis}); + } + + if (doprocessMCGenPol) { + if (!isPolarizationActive()) { + LOG(fatal) << "processMCGenPol requires at least one polarization frame to be active"; + } + const AxisSpec cosThetaAxisGenPol{spinAlignment.cosThetaBins, "cos#theta*"}; + histos.add("SpinAlignment/MCGenPol/hCosThetaStarGeneratedLambda1520", "cos#theta* generated #Lambda(1520) (pol process)", kTHnSparseF, {massAxis, ptAxis, centAxis, cosThetaAxisGenPol}); + histos.add("SpinAlignment/MCGenPol/hCosThetaStarGeneratedAntiLambda1520", "cos#theta* generated #bar{#Lambda}(1520) (pol process)", kTHnSparseF, {massAxis, ptAxis, centAxis, cosThetaAxisGenPol}); + histos.add("SpinAlignment/MCGenPol/hCosThetaStarGeneratedLambda1520InAcceptedEvents", "cos#theta* generated #Lambda(1520), event-accepted (pol process)", kTHnSparseF, {massAxis, ptAxis, centAxis, cosThetaAxisGenPol}); + histos.add("SpinAlignment/MCGenPol/hCosThetaStarGeneratedAntiLambda1520InAcceptedEvents", "cos#theta* generated #bar{#Lambda}(1520), event-accepted (pol process)", kTHnSparseF, {massAxis, ptAxis, centAxis, cosThetaAxisGenPol}); + } + + if (doprocessEventFactor) { + histos.add("EventLoss/hAcceptedRecoEventsOfGeneratedInelGt0", "Accepted reconstructed events of generated INEL>0 events (Nrec, MC-truth centrality)", kTH1F, {centAxis}); + histos.add("EventLoss/hGeneratedEventsVzAndInelGt0", "Generated events passing truth |Zvtx| + truth INEL>0 cuts", kTH1F, {centAxis}); + histos.add("EventLoss/hGeneratedEventsAcceptedTruthCentrality", "Generated events with >=1 accepted reconstructed collision (truth centrality)", kTH1F, {centAxis}); + histos.add("EventLoss/hGeneratedEventsAcceptedRecoCentrality", "Generated events with >=1 accepted reconstructed collision (reconstructed centrality)", kTH1F, {centAxis}); + histos.add("EventLoss/hRecoCollisionsPerGeneratedCollision", "Number of reconstructed collisions per generated collision", kTH1F, {{21, -0.5f, 20.5f}}); + histos.add("EventSelection/EventLoss/hGeneratedEventCutFlow", "Event Loss / Event Splitting: generated-event cut flow", kTH1F, {{6, -0.5f, 5.5f}}); + setHistogramBinLabels(histos.get(HIST("EventSelection/EventLoss/hGeneratedEventCutFlow")), + {"All", "Vz", "INEL>0", "HasRecoColl", "RecoCollAccepted", "EventAccepted"}); + } + + if (doprocessSignalLoss) { + histos.add("SignalLoss/Lambda1520/hPt_denominator", "Generated #Lambda(1520) (den)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/AntiLambda1520/hPt_denominator", "Generated #bar{#Lambda}(1520) (den)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/Lambda1520/hPt_numerator", "Generated #Lambda(1520) (num)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/AntiLambda1520/hPt_numerator", "Generated #bar{#Lambda}(1520) (num)", kTH2F, {ptAxis, centAxis}); + + histos.add("SignalLoss/Kaon/hTruePt_denominator", "Kaon, true pT (den)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/Kaon/hMtScaledPt_denominator", "Kaon, mT-scaled pT (den)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/Kaon/hTruePt_numerator", "Kaon, true pT (num)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/Kaon/hMtScaledPt_numerator", "Kaon, mT-scaled pT (num)", kTH2F, {ptAxis, centAxis}); + + histos.add("SignalLoss/Proton/hTruePt_denominator", "Proton, true pT (den)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/Proton/hMtScaledPt_denominator", "Proton, mT-scaled pT (den)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/Proton/hTruePt_numerator", "Proton, true pT (num)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/Proton/hMtScaledPt_numerator", "Proton, mT-scaled pT (num)", kTH2F, {ptAxis, centAxis}); + + histos.add("SignalLoss/Lambda0/hTruePt_denominator", "Lambda, true pT (den)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/Lambda0/hMtScaledPt_denominator", "Lambda, mT-scaled pT (den)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/Lambda0/hTruePt_numerator", "Lambda, true pT (num)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/Lambda0/hMtScaledPt_numerator", "Lambda, mT-scaled pT (num)", kTH2F, {ptAxis, centAxis}); + + histos.add("SignalLoss/Xi/hTruePt_denominator", "Xi, true pT (den)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/Xi/hMtScaledPt_denominator", "Xi, mT-scaled pT (den)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/Xi/hTruePt_numerator", "Xi, true pT (num)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/Xi/hMtScaledPt_numerator", "Xi, mT-scaled pT (num)", kTH2F, {ptAxis, centAxis}); + + histos.add("SignalLoss/Omega/hTruePt_denominator", "Omega, true pT (den)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/Omega/hMtScaledPt_denominator", "Omega, mT-scaled pT (den)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/Omega/hTruePt_numerator", "Omega, true pT (num)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/Omega/hMtScaledPt_numerator", "Omega, mT-scaled pT (num)", kTH2F, {ptAxis, centAxis}); + } + } + + template + float getCentrality(CollisionType const& collision) + { + switch (evSel.cfgCentralityEstimator) { + case lambda1520_analysis::kFT0M: + return collision.centFT0M(); + case lambda1520_analysis::kFT0A: + return collision.centFT0A(); + case lambda1520_analysis::kFT0C: + return collision.centFT0C(); + default: + return collision.centFT0M(); + } + } + + template + bool isRecoInelGt0(CollisionType const& collision) + { + return collision.isInelGt0(); + } + + template + bool isGeneratedChargedPrimary(McParticleType const& mcPart) + { + if (!mcPart.isPhysicalPrimary()) { + return false; + } + auto* pdgParticle = pdgDB->GetParticle(mcPart.pdgCode()); + if (!pdgParticle) { + return false; + } + return std::abs(pdgParticle->Charge()) > MinAbsCharge; + } + + template + bool isTruthInelGt0(McParticleTableType const& mcParticlesInCollision) + { + for (auto const& mcPart : mcParticlesInCollision) { + if (std::abs(mcPart.eta()) < 1.f && isGeneratedChargedPrimary(mcPart)) { + return true; + } + } + return false; + } + + template + int countGeneratedChargedPrimaries(McPartsT const& partsThisMc) + { + int nChargedPrimaries = 0; + for (auto const& part : partsThisMc) { + if (!part.isPhysicalPrimary()) { + continue; + } + if (part.eta() > mcCfg.generatedMultiplicityEtaMax || part.eta() < mcCfg.generatedMultiplicityEtaMin) { + continue; + } + auto* pdgParticle = pdgDB->GetParticle(part.pdgCode()); + if (!pdgParticle || std::abs(pdgParticle->Charge()) < MinAbsCharge) { + continue; + } + ++nChargedPrimaries; + } + return nChargedPrimaries; + } + + float getCentralityFromGeneratedMultiplicity(int nChargedPrimaries) + { + const auto& cuts = mcCfg.generatedMultiplicityLowerEdges.value; + const auto& centres = mcCfg.centralityPerGeneratedMultiplicityClass.value; + for (size_t i = 0; i < cuts.size(); ++i) { + if (nChargedPrimaries >= cuts[i]) { + return centres[i]; + } + } + return InvalidCentrality; + } + + template + float getTruthCentrality(McCollisionType const& mcCollision, McPartsT const& partsThisMc) + { + if (mcCfg.useGeneratedMultiplicityCentrality) { + return getCentralityFromGeneratedMultiplicity(countGeneratedChargedPrimaries(partsThisMc)); + } + return mcCollision.centFT0M(); + } + + struct NoCutFlowTag { + }; + struct DataCutFlowTag { + }; + struct McRecoCutFlowTag { + }; + + template + void fillEventSelectionCutFlowBin(float bin) + { + if constexpr (std::is_same_v) { + histos.fill(HIST("EventSelection/SameEvent/hCutFlow"), bin); + } else if constexpr (std::is_same_v) { + histos.fill(HIST("EventSelection/MCReco/hCutFlow"), bin); + } + } + + template + bool passesEventSelectionImpl(CollisionType const& collision) + { + fillEventSelectionCutFlowBin(0.f); + + if (std::abs(collision.posZ()) > evSel.cfgCutVertex) { + return false; + } + fillEventSelectionCutFlowBin(1.f); + + if (evSel.cfgUseNoITSROFrameBorderCut && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + return false; + } + fillEventSelectionCutFlowBin(2.f); + + if (evSel.cfgUseSel8 && !collision.sel8()) { + return false; + } + fillEventSelectionCutFlowBin(3.f); + + if (evSel.cfgRequireRecoINELgt0 && !isRecoInelGt0(collision)) { + return false; + } + fillEventSelectionCutFlowBin(4.f); + + if (evSel.applyOccupancyInTimeRangeCut) { + const int trackOccupancy = collision.trackOccupancyInTimeRange(); + if (trackOccupancy < evSel.cfgOccupancyMin || trackOccupancy > evSel.cfgOccupancyMax) { + return false; + } + } + fillEventSelectionCutFlowBin(5.f); + + if (evSel.cfgUseNoSameBunchPileupCut && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return false; + } + fillEventSelectionCutFlowBin(6.f); + + if (evSel.cfgUseGoodZvtxFT0vsPVCut && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + fillEventSelectionCutFlowBin(7.f); + + if (evSel.cfgUseGoodITSLayersAll && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + return false; + } + fillEventSelectionCutFlowBin(8.f); + + fillEventSelectionCutFlowBin(9.f); + return true; + } + + template + bool passesEventSelection(CollisionType const& collision) + { + return passesEventSelectionImpl(collision); + } + + template + bool passesBasicTrackSelection(TrackType const& track) + { + if (trackCuts.applyTPCCrossedRowsCut && + track.tpcNClsCrossedRows() < trackCuts.cfgMinTPCCrossedRows) { + return false; + } + if (trackCuts.applyTPCFoundClusterCut && + track.tpcNClsFound() < trackCuts.cfgMinTPCClusters) { + return false; + } + if (trackCuts.applyITSClusterCut && + track.itsNCls() < trackCuts.cfgMinITSClusters) { + return false; + } + if (trackCuts.applyTPCSharedClusterCut && + track.tpcNClsShared() > trackCuts.cfgMaxTPCSharedClusters) { + return false; + } + if (trackCuts.applyTPCCrossedRowsOverFindableCut && + track.tpcNClsCrossedRows() < trackCuts.cfgMinCrossedRowsOverFindable * track.tpcNClsFindable()) { + return false; + } + if (trackCuts.applyTPCChi2NClCut && + track.tpcChi2NCl() > trackCuts.cfgMaxTPCChi2NCl) { + return false; + } + if (trackCuts.applyITSChi2NClCut && + track.itsChi2NCl() > trackCuts.cfgMaxITSChi2NCl) { + return false; + } + if (trackCuts.requirePrimaryTrack && !track.isPrimaryTrack()) { + return false; + } + if (trackCuts.requireGlobalTrackWoDCA && !track.isGlobalTrackWoDCA()) { + return false; + } + if (trackCuts.requireGlobalTrack && !track.isGlobalTrack()) { + return false; + } + if (trackCuts.requirePVContributor && !track.isPVContributor()) { + return false; + } + return true; + } + + template + bool passesDCAxyDCAzSelection(TrackType const& track, std::vector const& ptEdges, std::vector const& maxDCAxyPerBin) + { + if (std::abs(track.dcaZ()) >= dcaCuts.cfgCutDCAz) { + return false; + } + const float pt = track.pt(); + const float dcaXY = std::abs(track.dcaXY()); + const int bin = findBinIndex(ptEdges, pt); + if (bin < 0 || dcaXY >= maxDCAxyPerBin[bin]) { + return false; + } + if (dcaCuts.isApplyPtDepDCAxyCut) { + const float dcaConst = dcaCuts.dcaXYConst; + const float dcaCoeff = dcaCuts.dcaXYPtCoeff; + const float dcaExp = dcaCuts.dcaXYPtExponent; + const float maxPtDepDCAxy = dcaConst + dcaCoeff / std::pow(pt, dcaExp); + if (dcaXY > maxPtDepDCAxy) { + return false; + } + } + return true; + } + + template + bool passesProtonDCASelection(TrackType const& track) + { + return passesDCAxyDCAzSelection(track, mProtonDCAPtEdges, mProtonMaxDCAxy); + } + + template + bool passesKaonDCASelection(TrackType const& track) + { + return passesDCAxyDCAzSelection(track, mKaonDCAPtEdges, mKaonMaxDCAxy); + } + + static int findBinIndex(std::vector const& edges, float x) + { + for (size_t i = 0; i + 1 < edges.size(); ++i) { + if (x >= edges[i] && x < edges[i + 1]) { + return static_cast(i); + } + } + return -1; + } + + bool passesMomentumDependentPID(float p, bool hasTOF, float tpcNSigma, float tofNSigma, SpeciesPIDSettings const& cuts) + { + const float absTPC = std::abs(tpcNSigma); + if (pidShared.useTPCOnlyPIDAtAllMomenta) { + return absTPC < cuts.maxTPCNSigma; + } + if (!hasTOF) { + if (p >= cuts.thresholdMomentum) { + return false; + } + const int bin = findBinIndex(cuts.tpcOnlyBins, p); + return bin >= 0 && absTPC < cuts.tpcOnlyCuts[bin]; + } + const float absTOF = std::abs(tofNSigma); + const int bin = findBinIndex(cuts.combinedBins, p); + return bin >= 0 && (absTPC * absTPC + absTOF * absTOF) < cuts.combinedCuts[bin] * cuts.combinedCuts[bin]; + } + + template + bool passesProtonPID(TrackType const& track, float totalMomentum) + { + if (protonPID.requireTOFForProton && !track.hasTOF()) { + return false; + } + const bool hasTOF = track.hasTOF(); + const float tofNSigma = hasTOF ? track.tofNSigmaPr() : -999.f; + return passesMomentumDependentPID(totalMomentum, hasTOF, track.tpcNSigmaPr(), tofNSigma, mProtonPIDCuts); + } + + template + bool isPionContaminatedKaonCandidate(TrackType const& track) + { + return track.pt() > pidShared.pionRejectionMinPt && + track.pt() < pidShared.pionRejectionMaxPt && + !track.hasTOF() && + std::abs(track.tpcNSigmaPi()) < pidShared.pionRejectionMaxTPCNSigma; + } + + template + bool passesKaonPID(TrackType const& track, float totalMomentum) + { + if (kaonPID.requireTOFForKaon && !track.hasTOF()) { + return false; + } + if (pidShared.applyKaonPionRejection && isPionContaminatedKaonCandidate(track)) { + return false; + } + const bool hasTOF = track.hasTOF(); + const float tofNSigma = hasTOF ? track.tofNSigmaKa() : -999.f; + return passesMomentumDependentPID(totalMomentum, hasTOF, track.tpcNSigmaKa(), tofNSigma, mKaonPIDCuts); + } + + template + DaughterTrackCandidate makeTrackCandidate(TrackType const& track) + { + DaughterTrackCandidate cand{}; + cand.px = track.px(); + cand.py = track.py(); + cand.pz = track.pz(); + cand.pt = track.pt(); + cand.eta = track.eta(); + cand.phi = track.phi(); + cand.mom = RecoDecay::p(track.px(), track.py(), track.pz()); + cand.dcaXY = track.dcaXY(); + cand.dcaZ = track.dcaZ(); + cand.sign = track.sign(); + cand.hasTOF = track.hasTOF(); + cand.tpcNSigmaPr = track.tpcNSigmaPr(); + cand.tpcNSigmaKa = track.tpcNSigmaKa(); + if (cand.hasTOF) { + cand.tofNSigmaPr = track.tofNSigmaPr(); + cand.tofNSigmaKa = track.tofNSigmaKa(); + } + cand.globalIndex = track.globalIndex(); + return cand; + } + + template + void attachMCTruthToCandidate(DaughterTrackCandidate& cand, TrackType const& track) + { + if (!track.has_mcParticle()) { + return; + } + auto const mcPart = track.mcParticle(); + cand.hasMcLabel = true; + cand.mcPdgCode = mcPart.pdgCode(); + if (!mcPart.has_mothers()) { + return; + } + for (auto const& mother : mcPart.template mothers_as()) { + if (std::abs(mother.pdgCode()) == mLambda1520AbsPdgCode) { + cand.mcLambda1520MotherIndex = mother.globalIndex(); + cand.mcLambda1520MotherPdgCode = mother.pdgCode(); + break; + } + } + } + + template + void buildProtonKaonCandidatePools(TrackCollection const& tracks, std::vector& protons, std::vector& kaons) + { + protons.clear(); + kaons.clear(); + for (auto const& track : tracks) { + if (!passesBasicTrackSelection(track)) { + continue; + } + const float mom = RecoDecay::p(track.px(), track.py(), track.pz()); + const bool isProton = passesProtonPID(track, mom) && passesProtonDCASelection(track); + const bool isKaon = passesKaonPID(track, mom) && passesKaonDCASelection(track); + if (!isProton && !isKaon) { + continue; + } + DaughterTrackCandidate cand = makeTrackCandidate(track); + if constexpr (IsMC) { + attachMCTruthToCandidate(cand, track); + } + if (isProton) { + protons.push_back(cand); + } + if (isKaon) { + kaons.push_back(cand); + } + } + } + + void computeDaughterPairGeometry(DaughterTrackCandidate const& pr, DaughterTrackCandidate const& ka, float& deltaEta, float& deltaPhi, float& openingAngle) + { + deltaEta = std::abs(pr.eta - ka.eta); + deltaPhi = std::abs(pr.phi - ka.phi); + if (deltaPhi > o2::constants::math::PI) { + deltaPhi = o2::constants::math::TwoPI - deltaPhi; + } + const float dot = pr.px * ka.px + pr.py * ka.py + pr.pz * ka.pz; + openingAngle = std::acos(std::clamp(dot / (pr.mom * ka.mom), -1.f, 1.f)); + } + + bool passesDaughterAngularCuts(DaughterTrackCandidate const& pr, DaughterTrackCandidate const& ka) + { + if (!pairCuts.applyDaughterDeltaEtaPhiCuts && !pairCuts.applyDeepAngleCut && !pairCuts.applyOpeningAngleVeto) { + return true; + } + float deltaEta = 0.f, deltaPhi = 0.f, openingAngle = 0.f; + computeDaughterPairGeometry(pr, ka, deltaEta, deltaPhi, openingAngle); + if (pairCuts.applyDaughterDeltaEtaPhiCuts) { + const float maxDaughterDeltaEtaValue = pairCuts.maxDaughterDeltaEta; + const float maxDaughterDeltaPhiValue = pairCuts.maxDaughterDeltaPhi; + if (deltaEta >= maxDaughterDeltaEtaValue || deltaPhi >= maxDaughterDeltaPhiValue) { + return false; + } + } + if (pairCuts.applyDeepAngleCut) { + const float minOpeningAngle = pairCuts.deepAngleCutValue; + if (openingAngle < minOpeningAngle) { + return false; + } + } + if (pairCuts.applyOpeningAngleVeto) { + const float openingAngleVetoLow = pairCuts.openingAngleVetoMin; + const float openingAngleVetoHigh = pairCuts.openingAngleVetoMax; + if (openingAngle > openingAngleVetoLow && openingAngle < openingAngleVetoHigh) { + return false; + } + } + return true; + } + + bool buildProtonKaonPair(DaughterTrackCandidate const& pr, DaughterTrackCandidate const& ka, float& pairMass, float& pairPt, float& pairY) + { + if (!passesDaughterAngularCuts(pr, ka)) { + return false; + } + const std::array, 2> momenta = { + std::array{pr.px, pr.py, pr.pz}, + std::array{ka.px, ka.py, ka.pz}}; + pairMass = RecoDecay::m(momenta, kProtonKaonMasses); + pairPt = RecoDecay::pt(std::array{pr.px + ka.px, pr.py + ka.py}); + pairY = RecoDecay::y(std::array{pr.px + ka.px, pr.py + ka.py, pr.pz + ka.pz}, pairMass); + + const float minRapidity = trackCuts.cfgMinY; + const float maxRapidity = trackCuts.cfgMaxY; + if (pairY < minRapidity || pairY > maxRapidity) { + return false; + } + if (pairCuts.applyPairPtMassUpperLimits) { + const float maxPairPt = pairCuts.cfgMaxPairPt; + const float maxPairMass = pairCuts.cfgMaxPairMass; + if (pairPt >= maxPairPt || pairMass >= maxPairMass) { + return false; + } + } + return true; + } + + void fillPairQABefore(DaughterTrackCandidate const& pr, DaughterTrackCandidate const& ka) + { + float deltaEta = 0.f, deltaPhi = 0.f, openingAngle = 0.f; + computeDaughterPairGeometry(pr, ka, deltaEta, deltaPhi, openingAngle); + histos.fill(HIST("PairQA/hOpeningAngleBeforePairCuts"), openingAngle); + histos.fill(HIST("PairQA/hDeltaEtaBeforePairCuts"), deltaEta); + histos.fill(HIST("PairQA/hDeltaPhiBeforePairCuts"), deltaPhi); + } + + void fillPairQAAfter(DaughterTrackCandidate const& pr, DaughterTrackCandidate const& ka) + { + float deltaEta = 0.f, deltaPhi = 0.f, openingAngle = 0.f; + computeDaughterPairGeometry(pr, ka, deltaEta, deltaPhi, openingAngle); + histos.fill(HIST("PairQA/hOpeningAngleAfterPairCuts"), openingAngle); + histos.fill(HIST("PairQA/hDeltaEtaAfterPairCuts"), deltaEta); + histos.fill(HIST("PairQA/hDeltaPhiAfterPairCuts"), deltaPhi); + } + + static float vecDot(const std::array& a, const std::array& b) + { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; + } + + static std::array vecSub(const std::array& a, const std::array& b) + { + return {a[0] - b[0], a[1] - b[1], a[2] - b[2]}; + } + + static std::array vecCross(const std::array& a, const std::array& b) + { + return {a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]}; + } + + static constexpr float KVecUnitEpsilon = 1e-9f; + static std::array vecUnit(const std::array& a) + { + float mag = std::sqrt(vecDot(a, a)); + if (mag < KVecUnitEpsilon) { + return {0.f, 0.f, 0.f}; + } + return {a[0] / mag, a[1] / mag, a[2] / mag}; + } + + static constexpr float KBoostMomentumEpsilon = 1e-6f; + static std::array boostToRestFrame(float motherE, const std::array& motherP, + float dE, const std::array& dP) + { + float pMag2 = vecDot(motherP, motherP); + float pMag = std::sqrt(pMag2); + if (pMag < KBoostMomentumEpsilon) { + return dP; + } + + std::array nHat = {motherP[0] / pMag, motherP[1] / pMag, motherP[2] / pMag}; + float beta = pMag / motherE; + float motherMass2 = motherE * motherE - pMag2; + float gamma = motherMass2 > 0.f ? motherE / std::sqrt(motherMass2) : 1.f; + + float pParallel = vecDot(dP, nHat); + std::array pPerp = {dP[0] - pParallel * nHat[0], dP[1] - pParallel * nHat[1], dP[2] - pParallel * nHat[2]}; + float pParallelBoosted = gamma * (pParallel - beta * dE); + + return {pPerp[0] + pParallelBoosted * nHat[0], pPerp[1] + pParallelBoosted * nHat[1], pPerp[2] + pParallelBoosted * nHat[2]}; + } + + enum class PolarizationBackgroundMode { + Signal, + LikeSign, + Rotated, + Mixed, + LikeSignMixed, + TruthMatched + }; + + bool isPolarizationActive() const + { + return spinAlignment.activateHelicityFrame.value || + spinAlignment.activateCollinsSoperFrame.value || + spinAlignment.activateProductionFrame.value || + spinAlignment.activateBeamAxisFrame.value || + spinAlignment.activateRandomFrame.value; + } + + template + void fillCosThetaStarHistograms(float candidateMass, float candidatePt, + const std::array& motherMom, float motherE, + const std::array& protonMom, float protonE, + const std::array& kaonMom, float kaonE, + float centrality, bool isParticle) + { + if (!isPolarizationActive()) { + return; + } + + std::array protonCM = boostToRestFrame(motherE, motherMom, protonE, protonMom); + std::array kaonCM = boostToRestFrame(motherE, motherMom, kaonE, kaonMom); + std::array refCM = vecUnit(spinAlignment.referenceFrameDaughter == 0 ? protonCM : kaonCM); + + auto fillFrame = [&](float cosTheta) { + if constexpr (Mode == PolarizationBackgroundMode::Signal) { + if (isParticle) { + histos.fill(HIST("SpinAlignment/SameEvent/hCosThetaStarLambda1520"), candidateMass, candidatePt, centrality, cosTheta); + } else { + histos.fill(HIST("SpinAlignment/SameEvent/hCosThetaStarAntiLambda1520"), candidateMass, candidatePt, centrality, cosTheta); + } + } else if constexpr (Mode == PolarizationBackgroundMode::LikeSign) { + if (isParticle) { + histos.fill(HIST("SpinAlignment/SameEvent/hCosThetaStarLikeSignPP"), candidateMass, candidatePt, centrality, cosTheta); + } else { + histos.fill(HIST("SpinAlignment/SameEvent/hCosThetaStarLikeSignMM"), candidateMass, candidatePt, centrality, cosTheta); + } + } else if constexpr (Mode == PolarizationBackgroundMode::Rotated) { + if (isParticle) { + histos.fill(HIST("SpinAlignment/SameEvent/hCosThetaStarRotatedLambda1520"), candidateMass, candidatePt, centrality, cosTheta); + } else { + histos.fill(HIST("SpinAlignment/SameEvent/hCosThetaStarRotatedAntiLambda1520"), candidateMass, candidatePt, centrality, cosTheta); + } + } else if constexpr (Mode == PolarizationBackgroundMode::Mixed) { + if (isParticle) { + histos.fill(HIST("SpinAlignment/MixedEvent/hCosThetaStarLambda1520"), candidateMass, candidatePt, centrality, cosTheta); + } else { + histos.fill(HIST("SpinAlignment/MixedEvent/hCosThetaStarAntiLambda1520"), candidateMass, candidatePt, centrality, cosTheta); + } + } else if constexpr (Mode == PolarizationBackgroundMode::LikeSignMixed) { + if (isParticle) { + histos.fill(HIST("SpinAlignment/MixedEvent/hCosThetaStarLikeSignPP"), candidateMass, candidatePt, centrality, cosTheta); + } else { + histos.fill(HIST("SpinAlignment/MixedEvent/hCosThetaStarLikeSignMM"), candidateMass, candidatePt, centrality, cosTheta); + } + } else if constexpr (Mode == PolarizationBackgroundMode::TruthMatched) { + if (isParticle) { + histos.fill(HIST("SpinAlignment/MCReco/hCosThetaStarTruthMatchedLambda1520"), candidateMass, candidatePt, centrality, cosTheta); + } else { + histos.fill(HIST("SpinAlignment/MCReco/hCosThetaStarTruthMatchedAntiLambda1520"), candidateMass, candidatePt, centrality, cosTheta); + } + } + }; + + if (spinAlignment.activateHelicityFrame) { + std::array zAxisHE = vecUnit(motherMom); + fillFrame(vecDot(refCM, zAxisHE)); + } + + if (spinAlignment.activateCollinsSoperFrame || spinAlignment.activateProductionFrame || spinAlignment.activateBeamAxisFrame) { + std::array beam1CM = vecUnit(boostToRestFrame(motherE, motherMom, mBeam1Energy, mBeam1Momentum)); + std::array beam2CM = vecUnit(boostToRestFrame(motherE, motherMom, mBeam2Energy, mBeam2Momentum)); + + if (spinAlignment.activateCollinsSoperFrame) { + std::array zAxisCS = vecUnit(vecSub(beam1CM, beam2CM)); + fillFrame(vecDot(refCM, zAxisCS)); + } + if (spinAlignment.activateProductionFrame) { + std::array motherDir = vecUnit(motherMom); + std::array yAxisPR = vecUnit(vecCross(beam1CM, motherDir)); + fillFrame(vecDot(refCM, yAxisPR)); + } + if (spinAlignment.activateBeamAxisFrame) { + fillFrame(vecDot(refCM, beam1CM)); + } + } + + if (spinAlignment.activateRandomFrame) { + double randCos = mUniform01(mRandomEngine) * 2. - 1.; + double randPhi = mUniform01(mRandomEngine) * o2::constants::math::TwoPI; + double randSin = std::sqrt(1. - randCos * randCos); + std::array zAxisRD = {static_cast(randSin * std::cos(randPhi)), + static_cast(randSin * std::sin(randPhi)), + static_cast(randCos)}; + fillFrame(vecDot(refCM, zAxisRD)); + } + } + + void fillLambda1520HistogramSameEvent(int protonSign, int kaonSign, float pairPt, float pairMass, float centrality, float rapidity) + { + if (isPolarizationActive()) { + return; + } + if (protonSign * kaonSign < 0) { + if (protonSign > 0) { + histos.fill(HIST("Lambda1520/SameEvent/hUnlikeSignLambda1520"), pairMass, pairPt, centrality, rapidity); + } else { + histos.fill(HIST("Lambda1520/SameEvent/hUnlikeSignAntiLambda1520"), pairMass, pairPt, centrality, rapidity); + } + } else { + if (protonSign > 0) { + histos.fill(HIST("Lambda1520/SameEvent/hLikeSignPlusPlus"), pairMass, pairPt, centrality, rapidity); + } else { + histos.fill(HIST("Lambda1520/SameEvent/hLikeSignMinusMinus"), pairMass, pairPt, centrality, rapidity); + } + } + } + + void fillLambda1520HistogramMixedEvent(int protonSign, int kaonSign, float pairPt, float pairMass, float centrality, float rapidity) + { + if (isPolarizationActive()) { + return; + } + if (protonSign * kaonSign < 0) { + if (protonSign > 0) { + histos.fill(HIST("Lambda1520/MixedEvent/hMixedEventLambda1520"), pairMass, pairPt, centrality, rapidity); + } else { + histos.fill(HIST("Lambda1520/MixedEvent/hMixedEventAntiLambda1520"), pairMass, pairPt, centrality, rapidity); + } + } else if (mixingCfg.cfgFillMixLikeSign) { + if (protonSign > 0) { + histos.fill(HIST("Lambda1520/MixedEvent/hMixedEventLikeSignPlusPlus"), pairMass, pairPt, centrality, rapidity); + } else { + histos.fill(HIST("Lambda1520/MixedEvent/hMixedEventLikeSignMinusMinus"), pairMass, pairPt, centrality, rapidity); + } + } + } + + void fillLambda1520HistogramMCRecoAllPairs(int protonSign, float pairPt, float pairMass, float centrality, float rapidity) + { + if (protonSign > 0) { + histos.fill(HIST("Lambda1520/MCReco/hAllPairsLambda1520"), pairMass, pairPt, centrality, rapidity); + } else { + histos.fill(HIST("Lambda1520/MCReco/hAllPairsAntiLambda1520"), pairMass, pairPt, centrality, rapidity); + } + } + + void fillLambda1520HistogramTruthMatched(int motherPdg, float pairPt, float pairMass, float centrality, float rapidity) + { + if (motherPdg > 0) { + histos.fill(HIST("Lambda1520/MCReco/hTruthMatchedLambda1520"), pairMass, pairPt, centrality, rapidity); + } else { + histos.fill(HIST("Lambda1520/MCReco/hTruthMatchedAntiLambda1520"), pairMass, pairPt, centrality, rapidity); + } + } + + void fillLambda1520HistogramMCRecoMixedEvent(int protonSign, int kaonSign, float pairPt, float pairMass, float centrality, float rapidity) + { + if (protonSign * kaonSign >= 0) { + return; + } + if (protonSign > 0) { + histos.fill(HIST("Lambda1520/MCRecoMixedEvent/hMixedEventLambda1520"), pairMass, pairPt, centrality, rapidity); + } else { + histos.fill(HIST("Lambda1520/MCRecoMixedEvent/hMixedEventAntiLambda1520"), pairMass, pairPt, centrality, rapidity); + } + } + + void fillRotatedPairBackground(DaughterTrackCandidate const& pr, DaughterTrackCandidate const& ka, float centrality) + { + const int nRot = rotBkg.numberOfRotations; + if (nRot <= 0) { + return; + } + const float weight = 1.f / static_cast(nRot); + const float rotationHalfWindow = o2::constants::math::PI / static_cast(rotBkg.rotationAngleWindow); + const bool rotateProtonInsteadOfKaon = rotBkg.rotateProtonInsteadOfKaon; + const DaughterTrackCandidate& rotated = rotateProtonInsteadOfKaon ? pr : ka; + + for (int iRot = 0; iRot < nRot; ++iRot) { + float rotationAngle = o2::constants::math::PI; + if (nRot > 1) { + rotationAngle = (o2::constants::math::PI - rotationHalfWindow) + + static_cast(iRot) * (2.f * rotationHalfWindow / static_cast(nRot - 1)); + } + const float rotatedPhi = RecoDecay::constrainAngle(rotated.phi + rotationAngle, 0.f); + const float rotatedPx = rotated.pt * std::cos(rotatedPhi); + const float rotatedPy = rotated.pt * std::sin(rotatedPhi); + + const std::array momPr = rotateProtonInsteadOfKaon ? std::array{rotatedPx, rotatedPy, pr.pz} + : std::array{pr.px, pr.py, pr.pz}; + const std::array momKa = rotateProtonInsteadOfKaon ? std::array{ka.px, ka.py, ka.pz} + : std::array{rotatedPx, rotatedPy, ka.pz}; + const std::array, 2> rotMomenta = {momPr, momKa}; + + const float rotatedMass = RecoDecay::m(rotMomenta, kProtonKaonMasses); + const float pairPxSum = momPr[0] + momKa[0]; + const float pairPySum = momPr[1] + momKa[1]; + const float pairPzSum = momPr[2] + momKa[2]; + const float rotatedPt = RecoDecay::pt(std::array{pairPxSum, pairPySum}); + const float rotatedRapidity = RecoDecay::y(std::array{pairPxSum, pairPySum, pairPzSum}, rotatedMass); + + const float minRapidity = trackCuts.cfgMinY; + const float maxRapidity = trackCuts.cfgMaxY; + if (rotatedRapidity < minRapidity || rotatedRapidity > maxRapidity) { + continue; + } + if (pairCuts.applyPairPtMassUpperLimits) { + const float maxPairPt = pairCuts.cfgMaxPairPt; + const float maxPairMass = pairCuts.cfgMaxPairMass; + if (rotatedPt >= maxPairPt || rotatedMass >= maxPairMass) { + continue; + } + } + + if (!isPolarizationActive()) { + if (pr.sign > 0) { + histos.fill(HIST("Lambda1520/SameEvent/hRotatedBackgroundLambda1520"), rotatedMass, rotatedPt, centrality, rotatedRapidity, weight); + } else { + histos.fill(HIST("Lambda1520/SameEvent/hRotatedBackgroundAntiLambda1520"), rotatedMass, rotatedPt, centrality, rotatedRapidity, weight); + } + } + + if (isPolarizationActive() && spinAlignment.fillRotatedBackgroundPolarization) { + const float protonE = std::sqrt(momPr[0] * momPr[0] + momPr[1] * momPr[1] + momPr[2] * momPr[2] + ProtonMass * ProtonMass); + const float kaonE = std::sqrt(momKa[0] * momKa[0] + momKa[1] * momKa[1] + momKa[2] * momKa[2] + KaonMass * KaonMass); + const std::array rotMotherMom = {pairPxSum, pairPySum, pairPzSum}; + const float rotMotherE = protonE + kaonE; + fillCosThetaStarHistograms( + rotatedMass, rotatedPt, rotMotherMom, rotMotherE, momPr, protonE, momKa, kaonE, centrality, pr.sign > 0); + } + } + } + + template + void fillInvariantMassFromCandidatePools( + std::vector const& protonPool, + std::vector const& kaonPool, + float centrality, + bool fillPairQA = false) + { + for (auto const& pr : protonPool) { + for (auto const& ka : kaonPool) { + + if constexpr (!IsMixed) { + if (pr.globalIndex == ka.globalIndex) { + continue; + } + if (fillPairQA) { + fillPairQABefore(pr, ka); + } + } + + float pairMass = 0.f, pairPt = 0.f, pairY = 0.f; + if (!buildProtonKaonPair(pr, ka, pairMass, pairPt, pairY)) { + continue; + } + + if constexpr (!IsMixed) { + if (fillPairQA) { + fillPairQAAfter(pr, ka); + } + } + + if constexpr (IsMixed) { + if constexpr (IsMC) { + fillLambda1520HistogramMCRecoMixedEvent(pr.sign, ka.sign, pairPt, pairMass, centrality, pairY); + } else { + fillLambda1520HistogramMixedEvent(pr.sign, ka.sign, pairPt, pairMass, centrality, pairY); + + if (isPolarizationActive() && spinAlignment.fillMixedEventPolarization) { + const float protonE = std::sqrt(pr.px * pr.px + pr.py * pr.py + pr.pz * pr.pz + ProtonMass * ProtonMass); + const float kaonE = std::sqrt(ka.px * ka.px + ka.py * ka.py + ka.pz * ka.pz + KaonMass * KaonMass); + const std::array motherMom = {pr.px + ka.px, pr.py + ka.py, pr.pz + ka.pz}; + const float motherE = protonE + kaonE; + const std::array protonMom = {pr.px, pr.py, pr.pz}; + const std::array kaonMom = {ka.px, ka.py, ka.pz}; + + if (pr.sign * ka.sign < 0) { + fillCosThetaStarHistograms( + pairMass, pairPt, motherMom, motherE, protonMom, protonE, kaonMom, kaonE, centrality, pr.sign > 0); + } else if (mixingCfg.cfgFillMixLikeSign) { + fillCosThetaStarHistograms( + pairMass, pairPt, motherMom, motherE, protonMom, protonE, kaonMom, kaonE, centrality, pr.sign > 0); + } + } + } + } else { + fillLambda1520HistogramSameEvent(pr.sign, ka.sign, pairPt, pairMass, centrality, pairY); + + if (isPolarizationActive()) { + const float protonE = std::sqrt(pr.px * pr.px + pr.py * pr.py + pr.pz * pr.pz + ProtonMass * ProtonMass); + const float kaonE = std::sqrt(ka.px * ka.px + ka.py * ka.py + ka.pz * ka.pz + KaonMass * KaonMass); + const std::array motherMom = {pr.px + ka.px, pr.py + ka.py, pr.pz + ka.pz}; + const float motherE = protonE + kaonE; + const std::array protonMom = {pr.px, pr.py, pr.pz}; + const std::array kaonMom = {ka.px, ka.py, ka.pz}; + + if (pr.sign * ka.sign < 0) { + fillCosThetaStarHistograms( + pairMass, pairPt, motherMom, motherE, protonMom, protonE, kaonMom, kaonE, centrality, pr.sign > 0); + } else { + fillCosThetaStarHistograms( + pairMass, pairPt, motherMom, motherE, protonMom, protonE, kaonMom, kaonE, centrality, pr.sign > 0); + } + } + + if (rotBkg.enableRotationalBackground && pr.sign * ka.sign < 0) { + fillRotatedPairBackground(pr, ka, centrality); + } + } + } + } + } + + template + void fillSelectedProtonQA(TrackType const& track, float totalMomentum, float centrality) + { + const float pt = track.pt(); + const float mom = totalMomentum; + const float tpcNSigPr = track.tpcNSigmaPr(); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hDCAxyVsPt"), pt, track.dcaXY()); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hDCAzVsPt"), pt, track.dcaZ()); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hTPCNSigmaVsMomentum"), mom, tpcNSigPr); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hTPCNSigmaVsPt"), pt, tpcNSigPr); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hTPCNSigmaVsCentrality"), centrality, tpcNSigPr); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hTPCNSigmaPionHypothesisVsPt"), pt, track.tpcNSigmaPi()); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hTPCNSigmaKaonHypothesisVsPt"), pt, track.tpcNSigmaKa()); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hTPCdEdxVsMomentum"), track.tpcInnerParam(), track.tpcSignal()); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hTPCClustersVsPhi"), track.tpcNClsFound(), track.phi()); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hTPCCrossedRowsVsPt"), pt, track.tpcNClsCrossedRows()); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hTPCFoundClustersVsPt"), pt, track.tpcNClsFound()); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hPt"), pt); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hEta"), track.eta()); + if (track.hasTOF()) { + const float tofNSigPr = track.tofNSigmaPr(); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hTOFNSigmaVsMomentum"), mom, tofNSigPr); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hTOFNSigmaVsPt"), pt, tofNSigPr); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hTOFNSigmaVsCentrality"), centrality, tofNSigPr); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hTOFNSigmaVsTPCNSigma"), tpcNSigPr, tofNSigPr); + } + } + + template + void fillSelectedKaonQA(TrackType const& track, float totalMomentum, float centrality) + { + const float pt = track.pt(); + const float mom = totalMomentum; + const float tpcNSigKa = track.tpcNSigmaKa(); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hDCAxyVsPt"), pt, track.dcaXY()); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hDCAzVsPt"), pt, track.dcaZ()); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hTPCNSigmaVsMomentum"), mom, tpcNSigKa); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hTPCNSigmaVsPt"), pt, tpcNSigKa); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hTPCNSigmaVsCentrality"), centrality, tpcNSigKa); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hTPCNSigmaPionHypothesisVsPt"), pt, track.tpcNSigmaPi()); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hTPCNSigmaProtonHypothesisVsPt"), pt, track.tpcNSigmaPr()); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hTPCdEdxVsMomentum"), track.tpcInnerParam(), track.tpcSignal()); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hTPCClustersVsPhi"), track.tpcNClsFound(), track.phi()); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hTPCCrossedRowsVsPt"), pt, track.tpcNClsCrossedRows()); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hTPCFoundClustersVsPt"), pt, track.tpcNClsFound()); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hPt"), pt); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hEta"), track.eta()); + if (track.hasTOF()) { + const float tofNSigKa = track.tofNSigmaKa(); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hTOFNSigmaVsMomentum"), mom, tofNSigKa); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hTOFNSigmaVsPt"), pt, tofNSigKa); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hTOFNSigmaVsCentrality"), centrality, tofNSigKa); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hTOFNSigmaVsTPCNSigma"), tpcNSigKa, tofNSigKa); + } + } + + template + void fillSelectedProtonQAMC(TrackType const& track, float totalMomentum, float centrality) + { + const float pt = track.pt(); + const float mom = totalMomentum; + const float tpcNSigPr = track.tpcNSigmaPr(); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hDCAxyVsPt"), pt, track.dcaXY()); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hDCAzVsPt"), pt, track.dcaZ()); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hTPCNSigmaVsMomentum"), mom, tpcNSigPr); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hTPCNSigmaVsPt"), pt, tpcNSigPr); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hTPCNSigmaVsCentrality"), centrality, tpcNSigPr); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hTPCNSigmaPionHypothesisVsPt"), pt, track.tpcNSigmaPi()); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hTPCNSigmaKaonHypothesisVsPt"), pt, track.tpcNSigmaKa()); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hTPCdEdxVsMomentum"), track.tpcInnerParam(), track.tpcSignal()); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hTPCClustersVsPhi"), track.tpcNClsFound(), track.phi()); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hTPCCrossedRowsVsPt"), pt, track.tpcNClsCrossedRows()); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hTPCFoundClustersVsPt"), pt, track.tpcNClsFound()); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hPt"), pt); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hEta"), track.eta()); + if (track.hasTOF()) { + const float tofNSigPr = track.tofNSigmaPr(); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hTOFNSigmaVsMomentum"), mom, tofNSigPr); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hTOFNSigmaVsPt"), pt, tofNSigPr); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hTOFNSigmaVsCentrality"), centrality, tofNSigPr); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hTOFNSigmaVsTPCNSigma"), tpcNSigPr, tofNSigPr); + } + } + + template + void fillSelectedKaonQAMC(TrackType const& track, float totalMomentum, float centrality) + { + const float pt = track.pt(); + const float mom = totalMomentum; + const float tpcNSigKa = track.tpcNSigmaKa(); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hDCAxyVsPt"), pt, track.dcaXY()); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hDCAzVsPt"), pt, track.dcaZ()); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hTPCNSigmaVsMomentum"), mom, tpcNSigKa); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hTPCNSigmaVsPt"), pt, tpcNSigKa); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hTPCNSigmaVsCentrality"), centrality, tpcNSigKa); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hTPCNSigmaPionHypothesisVsPt"), pt, track.tpcNSigmaPi()); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hTPCNSigmaProtonHypothesisVsPt"), pt, track.tpcNSigmaPr()); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hTPCdEdxVsMomentum"), track.tpcInnerParam(), track.tpcSignal()); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hTPCClustersVsPhi"), track.tpcNClsFound(), track.phi()); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hTPCCrossedRowsVsPt"), pt, track.tpcNClsCrossedRows()); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hTPCFoundClustersVsPt"), pt, track.tpcNClsFound()); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hPt"), pt); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hEta"), track.eta()); + if (track.hasTOF()) { + const float tofNSigKa = track.tofNSigmaKa(); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hTOFNSigmaVsMomentum"), mom, tofNSigKa); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hTOFNSigmaVsPt"), pt, tofNSigKa); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hTOFNSigmaVsCentrality"), centrality, tofNSigKa); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hTOFNSigmaVsTPCNSigma"), tpcNSigKa, tofNSigKa); + } + } + + template + bool findProtonKaonDaughters(McPart const& mother, McPart& proton, McPart& kaon) + { + if (!mother.has_daughters()) { + return false; + } + bool hasProtonDaughter = false, hasKaonDaughter = false; + for (auto const& d : mother.template daughters_as()) { + const int absPdgCode = std::abs(d.pdgCode()); + if (absPdgCode == lambda1520_analysis::PdgProton) { + hasProtonDaughter = true; + proton = d; + } else if (absPdgCode == lambda1520_analysis::PdgKaon) { + hasKaonDaughter = true; + kaon = d; + } + } + return hasProtonDaughter && hasKaonDaughter; + } + + struct GeneratedDaughterMatch { + bool found = false; + bool trackQuality = false; + bool pid = false; + bool dca = false; + DaughterTrackCandidate cand{}; + }; + + template + GeneratedDaughterMatch findRecoTrackOfGeneratedDaughter(TracksSlice const& tracksSlice, McPart const& mcDaughter, bool isProton) + { + GeneratedDaughterMatch result; + for (auto const& trk : tracksSlice) { + if (!trk.has_mcParticle() || trk.mcParticle().globalIndex() != mcDaughter.globalIndex()) { + continue; + } + result.found = true; + result.trackQuality = passesBasicTrackSelection(trk); + const float mom = RecoDecay::p(trk.px(), trk.py(), trk.pz()); + if (isProton) { + result.pid = passesProtonPID(trk, mom); + result.dca = passesProtonDCASelection(trk); + } else { + result.pid = passesKaonPID(trk, mom); + result.dca = passesKaonDCASelection(trk); + } + result.cand = makeTrackCandidate(trk); + break; + } + return result; + } + + template + void fillGeneratedToReconstructedCutFlow(TracksSlice const& tracksOfCollision, aod::McParticles const& mcParticles, int64_t mcCollisionId) + { + auto genParts = mcParticles.sliceBy(perMcCollision, mcCollisionId); + for (auto const& gen : genParts) { + if (!gen.producedByGenerator() || std::abs(gen.pdgCode()) != mLambda1520AbsPdgCode) { + continue; + } + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 0.f); + + auto genProton = gen; + auto genKaon = gen; + if (!findProtonKaonDaughters(gen, genProton, genKaon)) { + continue; + } + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 1.f); + + const float minRapidity = trackCuts.cfgMinY; + const float maxRapidity = trackCuts.cfgMaxY; + if (gen.y() < minRapidity || gen.y() > maxRapidity) { + continue; + } + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 2.f); + + const float maxTrackEta = trackCuts.cfgCutEta; + const float minTrackPt = trackCuts.cfgCutPt; + const bool protonInAcceptance = std::abs(genProton.eta()) < maxTrackEta && genProton.pt() > minTrackPt; + const bool kaonInAcceptance = std::abs(genKaon.eta()) < maxTrackEta && genKaon.pt() > minTrackPt; + if (protonInAcceptance) { + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 3.f); + } + if (kaonInAcceptance) { + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 4.f); + } + if (!protonInAcceptance || !kaonInAcceptance) { + continue; + } + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 5.f); + + const auto protonMatch = findRecoTrackOfGeneratedDaughter(tracksOfCollision, genProton, true); + const auto kaonMatch = findRecoTrackOfGeneratedDaughter(tracksOfCollision, genKaon, false); + if (protonMatch.found) { + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 6.f); + } + if (kaonMatch.found) { + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 7.f); + } + if (!protonMatch.found || !kaonMatch.found) { + continue; + } + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 8.f); + + if (protonMatch.trackQuality) { + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 9.f); + } + if (kaonMatch.trackQuality) { + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 10.f); + } + if (!protonMatch.trackQuality || !kaonMatch.trackQuality) { + continue; + } + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 11.f); + + if (protonMatch.pid) { + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 12.f); + } + if (kaonMatch.pid) { + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 13.f); + } + if (!protonMatch.pid || !kaonMatch.pid) { + continue; + } + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 14.f); + + if (protonMatch.dca) { + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 15.f); + } + if (kaonMatch.dca) { + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 16.f); + } + if (!protonMatch.dca || !kaonMatch.dca) { + continue; + } + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 17.f); + + float pairMass = 0.f, pairPt = 0.f, pairY = 0.f; + if (buildProtonKaonPair(protonMatch.cand, kaonMatch.cand, pairMass, pairPt, pairY)) { + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 18.f); + } + } + } + + static float computeMtScaledPt(float pt, float referenceMass) + { + const float mtScaledPtSquared = pt * pt + referenceMass * referenceMass - Lambda1520Mass * Lambda1520Mass; + return (mtScaledPtSquared > 0.f) ? std::sqrt(mtScaledPtSquared) : -1.f; + } + + template + void fillSignalLossProxySpecies(McPartType const& part, float centrality) + { + const int absPdgCode = std::abs(part.pdgCode()); + const float pt = part.pt(); + if (pt <= 0.f) { + return; + } + + if (absPdgCode == lambda1520_analysis::PdgKaon) { + const float mtScaledPt = computeMtScaledPt(pt, KaonMass); + const float weight = (mcCfg.applyMtScaleWeight && mtScaledPt > 0.f) ? mtScaledPt / pt : 1.f; + if constexpr (IsNumerator) { + histos.fill(HIST("SignalLoss/Kaon/hTruePt_numerator"), pt, centrality); + if (mtScaledPt > 0.f) { + histos.fill(HIST("SignalLoss/Kaon/hMtScaledPt_numerator"), mtScaledPt, centrality, weight); + } + } else { + histos.fill(HIST("SignalLoss/Kaon/hTruePt_denominator"), pt, centrality); + if (mtScaledPt > 0.f) { + histos.fill(HIST("SignalLoss/Kaon/hMtScaledPt_denominator"), mtScaledPt, centrality, weight); + } + } + } else if (absPdgCode == lambda1520_analysis::PdgProton) { + const float mtScaledPt = computeMtScaledPt(pt, ProtonMass); + const float weight = (mcCfg.applyMtScaleWeight && mtScaledPt > 0.f) ? mtScaledPt / pt : 1.f; + if constexpr (IsNumerator) { + histos.fill(HIST("SignalLoss/Proton/hTruePt_numerator"), pt, centrality); + if (mtScaledPt > 0.f) { + histos.fill(HIST("SignalLoss/Proton/hMtScaledPt_numerator"), mtScaledPt, centrality, weight); + } + } else { + histos.fill(HIST("SignalLoss/Proton/hTruePt_denominator"), pt, centrality); + if (mtScaledPt > 0.f) { + histos.fill(HIST("SignalLoss/Proton/hMtScaledPt_denominator"), mtScaledPt, centrality, weight); + } + } + } else if (absPdgCode == lambda1520_analysis::PdgLambda0) { + const float mtScaledPt = computeMtScaledPt(pt, LambdaMass); + const float weight = (mcCfg.applyMtScaleWeight && mtScaledPt > 0.f) ? mtScaledPt / pt : 1.f; + if constexpr (IsNumerator) { + histos.fill(HIST("SignalLoss/Lambda0/hTruePt_numerator"), pt, centrality); + if (mtScaledPt > 0.f) { + histos.fill(HIST("SignalLoss/Lambda0/hMtScaledPt_numerator"), mtScaledPt, centrality, weight); + } + } else { + histos.fill(HIST("SignalLoss/Lambda0/hTruePt_denominator"), pt, centrality); + if (mtScaledPt > 0.f) { + histos.fill(HIST("SignalLoss/Lambda0/hMtScaledPt_denominator"), mtScaledPt, centrality, weight); + } + } + } else if (absPdgCode == lambda1520_analysis::PdgXiMinus) { + const float mtScaledPt = computeMtScaledPt(pt, XiMass); + const float weight = (mcCfg.applyMtScaleWeight && mtScaledPt > 0.f) ? mtScaledPt / pt : 1.f; + if constexpr (IsNumerator) { + histos.fill(HIST("SignalLoss/Xi/hTruePt_numerator"), pt, centrality); + if (mtScaledPt > 0.f) { + histos.fill(HIST("SignalLoss/Xi/hMtScaledPt_numerator"), mtScaledPt, centrality, weight); + } + } else { + histos.fill(HIST("SignalLoss/Xi/hTruePt_denominator"), pt, centrality); + if (mtScaledPt > 0.f) { + histos.fill(HIST("SignalLoss/Xi/hMtScaledPt_denominator"), mtScaledPt, centrality, weight); + } + } + } else if (absPdgCode == lambda1520_analysis::PdgOmegaMinus) { + const float mtScaledPt = computeMtScaledPt(pt, OmegaMass); + const float weight = (mcCfg.applyMtScaleWeight && mtScaledPt > 0.f) ? mtScaledPt / pt : 1.f; + if constexpr (IsNumerator) { + histos.fill(HIST("SignalLoss/Omega/hTruePt_numerator"), pt, centrality); + if (mtScaledPt > 0.f) { + histos.fill(HIST("SignalLoss/Omega/hMtScaledPt_numerator"), mtScaledPt, centrality, weight); + } + } else { + histos.fill(HIST("SignalLoss/Omega/hTruePt_denominator"), pt, centrality); + if (mtScaledPt > 0.f) { + histos.fill(HIST("SignalLoss/Omega/hMtScaledPt_denominator"), mtScaledPt, centrality, weight); + } + } + } + } + + Filter acceptanceFilter = (nabs(aod::track::eta) < trackCuts.cfgCutEta && nabs(aod::track::pt) > trackCuts.cfgCutPt); + + using EventCandidates = soa::Join; + using TrackBase = soa::Join; + using TrackCandidates = soa::Filtered; + using TrackCandidatesMC = soa::Filtered>; + using EventCandidatesMC = soa::Join; + using McCollisionsCent = soa::Join; + + Preslice perCol = aod::track::collisionId; + Preslice perColMC = aod::track::collisionId; + Preslice perMcCollision = aod::mcparticle::mcCollisionId; + + SameKindPair pairFT0M{binningFT0M, mixingCfg.cfgNoMixedEvents, -1, &cache}; + SameKindPair pairFT0A{binningFT0A, mixingCfg.cfgNoMixedEvents, -1, &cache}; + SameKindPair pairFT0C{binningFT0C, mixingCfg.cfgNoMixedEvents, -1, &cache}; + + void processNothing(aod::Origins const& origins) + { + if (a0001PrintMessages) { + LOG(info) << "DEBUG :: Process Nothing :: origins = " << origins.size(); + } + // Intentionally empty. Keeps the task alive when the struct is not supposed to run. + } + PROCESS_SWITCH(HParticleCorrelation4Block4, processNothing, "Dummy process for analysis", true); + + void processSameEvent(EventCandidates const& collisions, + TrackCandidates const& tracks, + aod::BCs const&) + { + for (auto const& collision : collisions) { + if (!passesEventSelectionImpl(collision)) { + continue; + } + const float centrality = getCentrality(collision); + const int occupancy = collision.trackOccupancyInTimeRange(); + + histos.fill(HIST("EventQA/SameEvent/hVertexZ"), collision.posZ()); + histos.fill(HIST("EventQA/SameEvent/hPrimaryVertexContributors"), collision.numContrib()); + histos.fill(HIST("EventQA/SameEvent/hCentralityPercent"), centrality); + histos.fill(HIST("EventQA/SameEvent/hTrackOccupancy"), occupancy); + histos.fill(HIST("EventQA/SameEvent/hCentralityVsVertexZ"), collision.posZ(), centrality); + histos.fill(HIST("EventQA/SameEvent/hCentralityVsTrackOccupancy"), occupancy, centrality); + + const int64_t collisionIndex = collision.globalIndex(); + auto tracksOfCollision = tracks.sliceBy(perCol, collisionIndex); + tracksOfCollision.bindExternalIndices(&tracks); + + if (qa.cfgFillMultQA) { + histos.fill(HIST("EventQA/SameEvent/hCentralityVsFilteredTracksVsPVTracks"), centrality, tracksOfCollision.size(), collision.multNTracksPV()); + } + + if (qa.cfgFillTrackQA) { + for (auto const& track : tracksOfCollision) { + const float mom = RecoDecay::p(track.px(), track.py(), track.pz()); + histos.fill(HIST("TrackQA/BeforeSelection/Proton/hTPCNSigmaVsMomentum"), mom, track.tpcNSigmaPr()); + histos.fill(HIST("TrackQA/BeforeSelection/Kaon/hTPCNSigmaVsMomentum"), mom, track.tpcNSigmaKa()); + if (track.hasTOF()) { + histos.fill(HIST("TrackQA/BeforeSelection/Proton/hTOFNSigmaVsMomentum"), mom, track.tofNSigmaPr()); + histos.fill(HIST("TrackQA/BeforeSelection/Proton/hTOFNSigmaVsTPCNSigma"), track.tpcNSigmaPr(), track.tofNSigmaPr()); + histos.fill(HIST("TrackQA/BeforeSelection/Kaon/hTOFNSigmaVsMomentum"), mom, track.tofNSigmaKa()); + histos.fill(HIST("TrackQA/BeforeSelection/Kaon/hTOFNSigmaVsTPCNSigma"), track.tpcNSigmaKa(), track.tofNSigmaKa()); + } + + histos.fill(HIST("TrackQA/BeforeSelection/AllTracks/hEta"), track.eta()); + histos.fill(HIST("TrackQA/BeforeSelection/AllTracks/hPt"), track.pt()); + histos.fill(HIST("TrackQA/BeforeSelection/AllTracks/hPhi"), track.phi()); + histos.fill(HIST("TrackQA/BeforeSelection/AllTracks/hEtaPhi"), track.eta(), track.phi()); + + if (!passesBasicTrackSelection(track)) { + continue; + } + if (passesProtonPID(track, mom) && passesProtonDCASelection(track)) { + fillSelectedProtonQA(track, mom, centrality); + } + if (passesKaonPID(track, mom) && passesKaonDCASelection(track)) { + fillSelectedKaonQA(track, mom, centrality); + } + } + } + + buildProtonKaonCandidatePools(tracksOfCollision, mProtonCandidates, mKaonCandidates); + fillInvariantMassFromCandidatePools(mProtonCandidates, mKaonCandidates, centrality, qa.cfgFillPairQA); + } + } + PROCESS_SWITCH(HParticleCorrelation4Block4, processSameEvent, "Process same event (data)", true); + + template + void loopOverMixedEventPairs(PairType& mixingPair) + { + for (auto const& [c1, tracks1, c2, tracks2] : mixingPair) { + if (!passesEventSelection(c1) || !passesEventSelection(c2)) { + continue; + } + const float centrality = getCentrality(c1); + + if constexpr (IsMC) { + histos.fill(HIST("MixingQA/MCReco/hMixedEventCollisionPairs"), 1.f); + histos.fill(HIST("MixingQA/MCReco/hMixedEventCollisionPairsVsCentrality"), centrality); + } else { + if (qa.cfgFillMixQA) { + histos.fill(HIST("MixingQA/hMixedEventPoolVertexZ"), c1.posZ()); + histos.fill(HIST("MixingQA/hMixedEventPoolCentrality"), centrality); + histos.fill(HIST("MixingQA/hMixedEventPoolVertexZVsCentrality"), c1.posZ(), centrality); + histos.fill(HIST("MixingQA/hMixedEventCollisionPairs"), 1.f); + } + } + + std::vector protonPool1, kaonPool1, protonPool2, kaonPool2; + buildProtonKaonCandidatePools(tracks1, protonPool1, kaonPool1); + buildProtonKaonCandidatePools(tracks2, protonPool2, kaonPool2); + fillInvariantMassFromCandidatePools(protonPool1, kaonPool2, centrality); + fillInvariantMassFromCandidatePools(protonPool2, kaonPool1, centrality); + } + } + + template + void runMixedEventForSelectedEstimator() + { + switch (evSel.cfgCentralityEstimator) { + case lambda1520_analysis::kFT0M: + loopOverMixedEventPairs(pairFT0M); + break; + case lambda1520_analysis::kFT0A: + loopOverMixedEventPairs(pairFT0A); + break; + case lambda1520_analysis::kFT0C: + loopOverMixedEventPairs(pairFT0C); + break; + default: + loopOverMixedEventPairs(pairFT0M); + break; + } + } + + void processMixedEvent(EventCandidates const&, TrackCandidates const&) + { + runMixedEventForSelectedEstimator(); + } + PROCESS_SWITCH(HParticleCorrelation4Block4, processMixedEvent, "Process mixed event (data)", true); + + void processMCEventMixing(EventCandidates const&, TrackCandidates const&) + { + runMixedEventForSelectedEstimator(); + } + PROCESS_SWITCH(HParticleCorrelation4Block4, processMCEventMixing, "Process MC reconstructed event mixing (reuses the mixing pairs; no truth matching)", false); + + void processMC(EventCandidatesMC const& collisions, + aod::BCs const&, + TrackCandidatesMC const& tracks, + aod::McParticles const& mcParticles) + { + std::unordered_set mcCollisionsWithEfficiencyQA; + + for (auto const& collision : collisions) { + if (!passesEventSelectionImpl(collision)) { + continue; + } + if (!collision.has_mcCollision()) { + continue; + } + const float centrality = getCentrality(collision); + + histos.fill(HIST("EventQA/MCReco/hSelectedRecoEvents"), centrality); + histos.fill(HIST("EventQA/MCReco/hVertexZ"), collision.posZ()); + histos.fill(HIST("EventQA/MCReco/hPrimaryVertexContributors"), collision.numContrib()); + + const int64_t collisionIndex = collision.globalIndex(); + auto tracksOfCollision = tracks.sliceBy(perColMC, collisionIndex); + tracksOfCollision.bindExternalIndices(&tracks); + tracksOfCollision.bindExternalIndices(&mcParticles); + + for (auto const& track : tracksOfCollision) { + histos.fill(HIST("TrackQA/MCReco/AllTracks/hEta"), track.eta()); + histos.fill(HIST("TrackQA/MCReco/AllTracks/hPt"), track.pt()); + histos.fill(HIST("TrackQA/MCReco/AllTracks/hPhi"), track.phi()); + histos.fill(HIST("TrackQA/MCReco/AllTracks/hEtaPhi"), track.eta(), track.phi()); + + if (qa.cfgFillTrackQA) { + const float mom = RecoDecay::p(track.px(), track.py(), track.pz()); + histos.fill(HIST("TrackQA/MCReco/BeforeSelection/Proton/hTPCNSigmaVsMomentum"), mom, track.tpcNSigmaPr()); + histos.fill(HIST("TrackQA/MCReco/BeforeSelection/Kaon/hTPCNSigmaVsMomentum"), mom, track.tpcNSigmaKa()); + if (track.hasTOF()) { + histos.fill(HIST("TrackQA/MCReco/BeforeSelection/Proton/hTOFNSigmaVsMomentum"), mom, track.tofNSigmaPr()); + histos.fill(HIST("TrackQA/MCReco/BeforeSelection/Proton/hTOFNSigmaVsTPCNSigma"), track.tpcNSigmaPr(), track.tofNSigmaPr()); + histos.fill(HIST("TrackQA/MCReco/BeforeSelection/Kaon/hTOFNSigmaVsMomentum"), mom, track.tofNSigmaKa()); + histos.fill(HIST("TrackQA/MCReco/BeforeSelection/Kaon/hTOFNSigmaVsTPCNSigma"), track.tpcNSigmaKa(), track.tofNSigmaKa()); + } + if (passesBasicTrackSelection(track)) { + if (passesProtonPID(track, mom) && passesProtonDCASelection(track)) { + fillSelectedProtonQAMC(track, mom, centrality); + } + if (passesKaonPID(track, mom) && passesKaonDCASelection(track)) { + fillSelectedKaonQAMC(track, mom, centrality); + } + } + } + } + + buildProtonKaonCandidatePools(tracksOfCollision, mProtonCandidates, mKaonCandidates); + + for (auto const& pr : mProtonCandidates) { + if (pr.mcLambda1520MotherIndex >= 0 && std::abs(pr.mcPdgCode) == lambda1520_analysis::PdgProton) { + if (pr.mcLambda1520MotherPdgCode > 0) { + histos.fill(HIST("MCRecoQA/hRecoProtonPtFromLambda1520"), pr.pt); + } else { + histos.fill(HIST("MCRecoQA/hRecoProtonPtFromAntiLambda1520"), pr.pt); + } + } + } + for (auto const& ka : mKaonCandidates) { + if (ka.mcLambda1520MotherIndex >= 0 && std::abs(ka.mcPdgCode) == lambda1520_analysis::PdgKaon) { + if (ka.mcLambda1520MotherPdgCode > 0) { + histos.fill(HIST("MCRecoQA/hRecoKaonPtFromLambda1520"), ka.pt); + } else { + histos.fill(HIST("MCRecoQA/hRecoKaonPtFromAntiLambda1520"), ka.pt); + } + } + } + + for (auto const& pr : mProtonCandidates) { + for (auto const& ka : mKaonCandidates) { + if (pr.globalIndex == ka.globalIndex) { + continue; + } + if (pr.sign * ka.sign >= 0) { + continue; + } + float pairMass = 0.f, pairPt = 0.f, pairY = 0.f; + if (!buildProtonKaonPair(pr, ka, pairMass, pairPt, pairY)) { + continue; + } + + fillLambda1520HistogramMCRecoAllPairs(pr.sign, pairPt, pairMass, centrality, pairY); + + histos.fill(HIST("MCRecoQA/hTruthMatchingCutFlow"), 0.f); + if (pr.hasMcLabel) { + histos.fill(HIST("MCRecoQA/hTruthMatchingCutFlow"), 1.f); + } + if (ka.hasMcLabel) { + histos.fill(HIST("MCRecoQA/hTruthMatchingCutFlow"), 2.f); + } + if (!pr.hasMcLabel || !ka.hasMcLabel) { + continue; + } + if (std::abs(pr.mcPdgCode) != lambda1520_analysis::PdgProton) { + continue; + } + histos.fill(HIST("MCRecoQA/hTruthMatchingCutFlow"), 3.f); + if (std::abs(ka.mcPdgCode) != lambda1520_analysis::PdgKaon) { + continue; + } + histos.fill(HIST("MCRecoQA/hTruthMatchingCutFlow"), 4.f); + if (pr.mcLambda1520MotherIndex < 0 || pr.mcLambda1520MotherIndex != ka.mcLambda1520MotherIndex) { + continue; + } + histos.fill(HIST("MCRecoQA/hTruthMatchingCutFlow"), 5.f); + + if (mcCfg.applyGeneratedEtaCut) { + const float pairEta = RecoDecay::eta(std::array{pr.px + ka.px, pr.py + ka.py, pr.pz + ka.pz}); + if (std::abs(pairEta) > mcCfg.generatedEtaMax) { + continue; + } + } + + fillLambda1520HistogramTruthMatched(pr.mcLambda1520MotherPdgCode, pairPt, pairMass, centrality, pairY); + + { + auto const mcMother = mcParticles.iteratorAt(pr.mcLambda1520MotherIndex); + const float genE = mcMother.e(); + const float genPx = mcMother.px(); + const float genPy = mcMother.py(); + const float genPz = mcMother.pz(); + const float genMass2 = genE * genE - genPx * genPx - genPy * genPy - genPz * genPz; + const float genMass = std::sqrt(std::max(0.f, genMass2)); + const float massResolution = pairMass - genMass; + if (pr.mcLambda1520MotherPdgCode > 0) { + histos.fill(HIST("Lambda1520/MCReco/hMassResolutionLambda1520"), massResolution, pairPt, centrality); + } else { + histos.fill(HIST("Lambda1520/MCReco/hMassResolutionAntiLambda1520"), massResolution, pairPt, centrality); + } + } + + if (isPolarizationActive()) { + const float protonE = std::sqrt(pr.px * pr.px + pr.py * pr.py + pr.pz * pr.pz + ProtonMass * ProtonMass); + const float kaonE = std::sqrt(ka.px * ka.px + ka.py * ka.py + ka.pz * ka.pz + KaonMass * KaonMass); + const std::array motherMom = {pr.px + ka.px, pr.py + ka.py, pr.pz + ka.pz}; + const float motherE = protonE + kaonE; + const std::array protonMom = {pr.px, pr.py, pr.pz}; + const std::array kaonMom = {ka.px, ka.py, ka.pz}; + fillCosThetaStarHistograms( + pairMass, pairPt, motherMom, motherE, protonMom, protonE, kaonMom, kaonE, centrality, pr.mcLambda1520MotherPdgCode > 0); + + { + auto const mcMother = mcParticles.iteratorAt(pr.mcLambda1520MotherIndex); + const float genE2 = mcMother.e(); + const float genPx2 = mcMother.px(); + const float genPy2 = mcMother.py(); + const float genPz2 = mcMother.pz(); + const float genM2 = genE2 * genE2 - genPx2 * genPx2 - genPy2 * genPy2 - genPz2 * genPz2; + const float genMassVal = std::sqrt(std::max(0.f, genM2)); + const float massRes = pairMass - genMassVal; + + std::array protonCMRes = boostToRestFrame(motherE, motherMom, protonE, protonMom); + std::array kaonCMRes = boostToRestFrame(motherE, motherMom, kaonE, kaonMom); + std::array refCMRes = vecUnit(spinAlignment.referenceFrameDaughter == 0 ? protonCMRes : kaonCMRes); + float cosThetaRes = 0.f; + if (spinAlignment.activateHelicityFrame) { + cosThetaRes = vecDot(refCMRes, vecUnit(motherMom)); + } else if (spinAlignment.activateCollinsSoperFrame || spinAlignment.activateProductionFrame || spinAlignment.activateBeamAxisFrame) { + std::array beam1CMRes = vecUnit(boostToRestFrame(motherE, motherMom, mBeam1Energy, mBeam1Momentum)); + std::array beam2CMRes = vecUnit(boostToRestFrame(motherE, motherMom, mBeam2Energy, mBeam2Momentum)); + if (spinAlignment.activateCollinsSoperFrame) { + cosThetaRes = vecDot(refCMRes, vecUnit(vecSub(beam1CMRes, beam2CMRes))); + } else if (spinAlignment.activateProductionFrame) { + cosThetaRes = vecDot(refCMRes, vecUnit(vecCross(beam1CMRes, vecUnit(motherMom)))); + } else { + cosThetaRes = vecDot(refCMRes, beam1CMRes); + } + } else if (spinAlignment.activateRandomFrame) { + double rCos = mUniform01(mRandomEngine) * 2. - 1.; + double rPhi = mUniform01(mRandomEngine) * o2::constants::math::TwoPI; + double rSin = std::sqrt(1. - rCos * rCos); + std::array zRD = {static_cast(rSin * std::cos(rPhi)), + static_cast(rSin * std::sin(rPhi)), + static_cast(rCos)}; + cosThetaRes = vecDot(refCMRes, zRD); + } + if (pr.mcLambda1520MotherPdgCode > 0) { + histos.fill(HIST("SpinAlignment/MCReco/hMassResolutionCosThetaLambda1520"), massRes, pairPt, centrality, cosThetaRes); + } else { + histos.fill(HIST("SpinAlignment/MCReco/hMassResolutionCosThetaAntiLambda1520"), massRes, pairPt, centrality, cosThetaRes); + } + } + } + + histos.fill(HIST("PairQA/MCReco/hTruthMatchedRapidityVsPt"), pairPt, pairY); + histos.fill(HIST("TrackQA/MCReco/TruthMatchedProton/hDCAxyVsPt"), pr.pt, pr.dcaXY); + histos.fill(HIST("TrackQA/MCReco/TruthMatchedProton/hDCAzVsPt"), pr.pt, pr.dcaZ); + histos.fill(HIST("TrackQA/MCReco/TruthMatchedProton/hTPCNSigmaVsPt"), pr.pt, pr.tpcNSigmaPr); + if (pr.hasTOF) { + histos.fill(HIST("TrackQA/MCReco/TruthMatchedProton/hTOFNSigmaVsPt"), pr.pt, pr.tofNSigmaPr); + } + histos.fill(HIST("TrackQA/MCReco/TruthMatchedKaon/hDCAxyVsPt"), ka.pt, ka.dcaXY); + histos.fill(HIST("TrackQA/MCReco/TruthMatchedKaon/hDCAzVsPt"), ka.pt, ka.dcaZ); + histos.fill(HIST("TrackQA/MCReco/TruthMatchedKaon/hTPCNSigmaVsPt"), ka.pt, ka.tpcNSigmaKa); + if (ka.hasTOF) { + histos.fill(HIST("TrackQA/MCReco/TruthMatchedKaon/hTOFNSigmaVsPt"), ka.pt, ka.tofNSigmaKa); + } + } + } + + if (mcCollisionsWithEfficiencyQA.insert(collision.mcCollisionId()).second) { + fillGeneratedToReconstructedCutFlow(tracksOfCollision, mcParticles, collision.mcCollisionId()); + } + } + } + PROCESS_SWITCH(HParticleCorrelation4Block4, processMC, "Process MC reconstructed (truth matching)", false); + + void processMCGen(McCollisionsCent::iterator const& mcCollision, + aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions) + { + histos.fill(HIST("EventSelection/MCGen/hCutFlow"), 0.f); + + if (std::abs(mcCollision.posZ()) > evSel.cfgCutVertex) { + return; + } + histos.fill(HIST("EventSelection/MCGen/hCutFlow"), 1.f); + + if (evSel.cfgApplyMCTruthINELgt0 && !isTruthInelGt0(mcParticles)) { + return; + } + histos.fill(HIST("EventSelection/MCGen/hCutFlow"), 2.f); + + float genCentrality = getTruthCentrality(mcCollision, mcParticles); + if (genCentrality == InvalidCentrality) { + return; + } + histos.fill(HIST("EventSelection/MCGen/hCutFlow"), 3.f); + histos.fill(HIST("EventSelection/MCGen/hCutFlow"), 4.f); + + bool hasAcceptedReco = false; + for (auto const& collision : collisions) { + if (passesEventSelection(collision)) { + hasAcceptedReco = true; + if (!evSel.cfgUseMCTruthCentrality) { + genCentrality = getCentrality(collision); + } + break; + } + } + if (hasAcceptedReco) { + histos.fill(HIST("EventSelection/MCGen/hCutFlow"), 5.f); + } + + for (auto const& mcParticle : mcParticles) { + histos.fill(HIST("GeneratedQA/hLambda1520GeneratedCutFlow"), 0.f); + + if (!mcParticle.producedByGenerator()) { + continue; + } + histos.fill(HIST("GeneratedQA/hLambda1520GeneratedCutFlow"), 1.f); + + const int pdg = mcParticle.pdgCode(); + if (std::abs(pdg) != mLambda1520AbsPdgCode) { + continue; + } + histos.fill(HIST("GeneratedQA/hLambda1520GeneratedCutFlow"), 2.f); + + auto genProton = mcParticle; + auto genKaon = mcParticle; + if (!findProtonKaonDaughters(mcParticle, genProton, genKaon)) { + continue; + } + histos.fill(HIST("GeneratedQA/hLambda1520GeneratedCutFlow"), 3.f); + + const float genPt = mcParticle.pt(); + const float genY = mcParticle.y(); + histos.fill(HIST("GeneratedQA/hEtaVsPtBeforeCuts"), mcParticle.eta(), genPt); + histos.fill(HIST("GeneratedQA/hPhiVsRapidityBeforeCuts"), mcParticle.phi(), genY); + + if (genY < trackCuts.cfgMinY || genY > trackCuts.cfgMaxY) { + continue; + } + histos.fill(HIST("GeneratedQA/hLambda1520GeneratedCutFlow"), 4.f); + + const float maxTrackEta = trackCuts.cfgCutEta; + if (mcCfg.applyGeneratedDaughterEtaCut && (std::abs(genProton.eta()) >= maxTrackEta || std::abs(genKaon.eta()) >= maxTrackEta)) { + continue; + } + histos.fill(HIST("GeneratedQA/hLambda1520GeneratedCutFlow"), 5.f); + + if (mcCfg.applyGeneratedEtaCut && std::abs(mcParticle.eta()) > mcCfg.generatedEtaMax) { + continue; + } + histos.fill(HIST("GeneratedQA/hLambda1520GeneratedCutFlow"), 6.f); + histos.fill(HIST("GeneratedQA/hLambda1520GeneratedCutFlow"), 7.f); + + histos.fill(HIST("GeneratedQA/hEtaVsPtAfterCuts"), mcParticle.eta(), genPt); + histos.fill(HIST("GeneratedQA/hPhiVsRapidityAfterCuts"), mcParticle.phi(), genY); + histos.fill(HIST("GeneratedQA/hRapidityVsPtFinal"), genPt, genY); + + const float motherEnergy = mcParticle.e(); + const float motherPx = mcParticle.px(); + const float motherPy = mcParticle.py(); + const float motherPz = mcParticle.pz(); + const float mass2 = motherEnergy * motherEnergy - motherPx * motherPx - motherPy * motherPy - motherPz * motherPz; + const float genMass = std::sqrt(std::max(0.f, mass2)); + const float ptPr = genProton.pt(); + const float ptKa = genKaon.pt(); + + if (pdg > 0) { + histos.fill(HIST("Lambda1520/MCGen/hGeneratedLambda1520"), genMass, genPt, genCentrality, genY); + histos.fill(HIST("GeneratedQA/hProtonDaughterPtFromLambda1520"), ptPr); + histos.fill(HIST("GeneratedQA/hKaonDaughterPtFromLambda1520"), ptKa); + } else { + histos.fill(HIST("Lambda1520/MCGen/hGeneratedAntiLambda1520"), genMass, genPt, genCentrality, genY); + histos.fill(HIST("GeneratedQA/hProtonDaughterPtFromAntiLambda1520"), ptPr); + histos.fill(HIST("GeneratedQA/hKaonDaughterPtFromAntiLambda1520"), ptKa); + } + + if (hasAcceptedReco) { + if (pdg > 0) { + histos.fill(HIST("Lambda1520/MCGen/hGeneratedLambda1520InAcceptedEvents"), genMass, genPt, genCentrality, genY); + } else { + histos.fill(HIST("Lambda1520/MCGen/hGeneratedAntiLambda1520InAcceptedEvents"), genMass, genPt, genCentrality, genY); + } + } + } + } + PROCESS_SWITCH(HParticleCorrelation4Block4, processMCGen, "Process MC truth: generated-level Lambda(1520) spectra and QA", false); + + void processMCGenPol(McCollisionsCent::iterator const& mcCollision, + aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions) + { + if (std::abs(mcCollision.posZ()) > evSel.cfgCutVertex) { + return; + } + if (evSel.cfgApplyMCTruthINELgt0 && !isTruthInelGt0(mcParticles)) { + return; + } + float genCentrality = getTruthCentrality(mcCollision, mcParticles); + if (genCentrality == InvalidCentrality) { + return; + } + + bool hasAcceptedReco = false; + for (auto const& collision : collisions) { + if (passesEventSelection(collision)) { + hasAcceptedReco = true; + if (!evSel.cfgUseMCTruthCentrality) { + genCentrality = getCentrality(collision); + } + break; + } + } + + for (auto const& mcParticle : mcParticles) { + if (!mcParticle.producedByGenerator()) { + continue; + } + const int pdg = mcParticle.pdgCode(); + if (std::abs(pdg) != mLambda1520AbsPdgCode) { + continue; + } + auto genProton = mcParticle; + auto genKaon = mcParticle; + if (!findProtonKaonDaughters(mcParticle, genProton, genKaon)) { + continue; + } + + const float genPt = mcParticle.pt(); + const float genY = mcParticle.y(); + if (genY < trackCuts.cfgMinY || genY > trackCuts.cfgMaxY) { + continue; + } + + const float maxTrackEta = trackCuts.cfgCutEta; + if (mcCfg.applyGeneratedDaughterEtaCut && (std::abs(genProton.eta()) >= maxTrackEta || std::abs(genKaon.eta()) >= maxTrackEta)) { + continue; + } + if (mcCfg.applyGeneratedEtaCut && std::abs(mcParticle.eta()) > mcCfg.generatedEtaMax) { + continue; + } + + const float motherEnergy = mcParticle.e(); + const float motherPx = mcParticle.px(); + const float motherPy = mcParticle.py(); + const float motherPz = mcParticle.pz(); + const float mass2 = motherEnergy * motherEnergy - motherPx * motherPx - motherPy * motherPy - motherPz * motherPz; + const float genMass = std::sqrt(std::max(0.f, mass2)); + + const std::array motherMom = {motherPx, motherPy, motherPz}; + const std::array protonMom = {genProton.px(), genProton.py(), genProton.pz()}; + const std::array kaonMom = {genKaon.px(), genKaon.py(), genKaon.pz()}; + const float protonE = genProton.e(); + const float kaonE = genKaon.e(); + + std::array protonCM = boostToRestFrame(motherEnergy, motherMom, protonE, protonMom); + std::array kaonCM = boostToRestFrame(motherEnergy, motherMom, kaonE, kaonMom); + std::array refCM = vecUnit(spinAlignment.referenceFrameDaughter == 0 ? protonCM : kaonCM); + + float cosTheta = 0.f; + if (spinAlignment.activateHelicityFrame) { + cosTheta = vecDot(refCM, vecUnit(motherMom)); + } else if (spinAlignment.activateCollinsSoperFrame || spinAlignment.activateProductionFrame || spinAlignment.activateBeamAxisFrame) { + std::array beam1CM = vecUnit(boostToRestFrame(motherEnergy, motherMom, mBeam1Energy, mBeam1Momentum)); + std::array beam2CM = vecUnit(boostToRestFrame(motherEnergy, motherMom, mBeam2Energy, mBeam2Momentum)); + if (spinAlignment.activateCollinsSoperFrame) { + cosTheta = vecDot(refCM, vecUnit(vecSub(beam1CM, beam2CM))); + } else if (spinAlignment.activateProductionFrame) { + cosTheta = vecDot(refCM, vecUnit(vecCross(beam1CM, vecUnit(motherMom)))); + } else { + cosTheta = vecDot(refCM, beam1CM); + } + } else if (spinAlignment.activateRandomFrame) { + double rCos = mUniform01(mRandomEngine) * 2. - 1.; + double rPhi = mUniform01(mRandomEngine) * o2::constants::math::TwoPI; + double rSin = std::sqrt(1. - rCos * rCos); + std::array zRD = {static_cast(rSin * std::cos(rPhi)), + static_cast(rSin * std::sin(rPhi)), + static_cast(rCos)}; + cosTheta = vecDot(refCM, zRD); + } + + if (pdg > 0) { + histos.fill(HIST("SpinAlignment/MCGenPol/hCosThetaStarGeneratedLambda1520"), genMass, genPt, genCentrality, cosTheta); + } else { + histos.fill(HIST("SpinAlignment/MCGenPol/hCosThetaStarGeneratedAntiLambda1520"), genMass, genPt, genCentrality, cosTheta); + } + + if (hasAcceptedReco) { + if (pdg > 0) { + histos.fill(HIST("SpinAlignment/MCGenPol/hCosThetaStarGeneratedLambda1520InAcceptedEvents"), genMass, genPt, genCentrality, cosTheta); + } else { + histos.fill(HIST("SpinAlignment/MCGenPol/hCosThetaStarGeneratedAntiLambda1520InAcceptedEvents"), genMass, genPt, genCentrality, cosTheta); + } + } + } + } + PROCESS_SWITCH(HParticleCorrelation4Block4, processMCGenPol, "Process MC truth: generated-level Lambda(1520) polarization cos(theta*) with same cuts as processMCGen", false); + + void processEventFactor(EventCandidatesMC const& collisions, + McCollisionsCent const& mcCollisions, + aod::McParticles const& mcParticles) + { + std::unordered_map nRecoCollisionsPerMc; + std::unordered_map acceptedMcCollisionRecoCent; + for (auto const& collision : collisions) { + if (!collision.has_mcCollision()) { + continue; + } + ++nRecoCollisionsPerMc[collision.mcCollisionId()]; + if (!passesEventSelection(collision)) { + continue; + } + acceptedMcCollisionRecoCent.emplace(collision.mcCollisionId(), getCentrality(collision)); + + const auto& mcCollision = collision.mcCollision_as(); + auto mcParticlesOfRecoCollision = mcParticles.sliceBy(perMcCollision, mcCollision.globalIndex()); + if (evSel.cfgApplyMCTruthINELgt0 && !isTruthInelGt0(mcParticlesOfRecoCollision)) { + continue; + } + const float truthCentrality = getTruthCentrality(mcCollision, mcParticlesOfRecoCollision); + if (truthCentrality == InvalidCentrality) { + continue; + } + histos.fill(HIST("EventLoss/hAcceptedRecoEventsOfGeneratedInelGt0"), truthCentrality); + } + + for (auto const& mcCollision : mcCollisions) { + histos.fill(HIST("EventSelection/EventLoss/hGeneratedEventCutFlow"), 0.f); + + auto mcParticlesOfGenCollision = mcParticles.sliceBy(perMcCollision, mcCollision.globalIndex()); + const bool isInsideVertexRange = std::abs(mcCollision.posZ()) <= evSel.cfgCutVertex; + const bool truthInelGt0 = isTruthInelGt0(mcParticlesOfGenCollision); + const float truthCentrality = getTruthCentrality(mcCollision, mcParticlesOfGenCollision); + + if (!isInsideVertexRange) { + continue; + } + histos.fill(HIST("EventSelection/EventLoss/hGeneratedEventCutFlow"), 1.f); + + if (evSel.cfgApplyMCTruthINELgt0 && !truthInelGt0) { + continue; + } + histos.fill(HIST("EventSelection/EventLoss/hGeneratedEventCutFlow"), 2.f); + + if (truthCentrality == InvalidCentrality) { + continue; + } + histos.fill(HIST("EventLoss/hGeneratedEventsVzAndInelGt0"), truthCentrality); + + const auto recoCollisionCountEntry = nRecoCollisionsPerMc.find(mcCollision.globalIndex()); + const int nRecoTotal = (recoCollisionCountEntry != nRecoCollisionsPerMc.end()) ? recoCollisionCountEntry->second : 0; + histos.fill(HIST("EventLoss/hRecoCollisionsPerGeneratedCollision"), nRecoTotal); + + if (nRecoTotal > 0) { + histos.fill(HIST("EventSelection/EventLoss/hGeneratedEventCutFlow"), 3.f); + } + + const auto acceptedRecoCentralityEntry = acceptedMcCollisionRecoCent.find(mcCollision.globalIndex()); + if (acceptedRecoCentralityEntry != acceptedMcCollisionRecoCent.end()) { + histos.fill(HIST("EventSelection/EventLoss/hGeneratedEventCutFlow"), 4.f); + histos.fill(HIST("EventSelection/EventLoss/hGeneratedEventCutFlow"), 5.f); + histos.fill(HIST("EventLoss/hGeneratedEventsAcceptedTruthCentrality"), truthCentrality); + histos.fill(HIST("EventLoss/hGeneratedEventsAcceptedRecoCentrality"), acceptedRecoCentralityEntry->second); + } + } + } + PROCESS_SWITCH(HParticleCorrelation4Block4, processEventFactor, "Process Event Loss / Event Splitting factors", false); + + void processSignalLoss(McCollisionsCent::iterator const& mcCollision, + aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions) + { + if (std::abs(mcCollision.posZ()) > evSel.cfgCutVertex) { + return; + } + if (evSel.cfgApplyMCTruthINELgt0 && !isTruthInelGt0(mcParticles)) { + return; + } + const float centrality = getTruthCentrality(mcCollision, mcParticles); + if (centrality == InvalidCentrality) { + return; + } + + bool hasAcceptedReco = false; + for (auto const& collision : collisions) { + if (passesEventSelection(collision)) { + hasAcceptedReco = true; + break; + } + } + + const float minRapidity = trackCuts.cfgMinY; + const float maxRapidity = trackCuts.cfgMaxY; + const float maxAbsGeneratedEta = mcCfg.generatedEtaMax; + + for (auto const& part : mcParticles) { + const int absPdgCode = std::abs(part.pdgCode()); + const bool isLambda1520 = absPdgCode == mLambda1520AbsPdgCode; + const bool isProxySpecies = absPdgCode == lambda1520_analysis::PdgKaon || + absPdgCode == lambda1520_analysis::PdgProton || + absPdgCode == lambda1520_analysis::PdgLambda0 || + absPdgCode == lambda1520_analysis::PdgXiMinus || + absPdgCode == lambda1520_analysis::PdgOmegaMinus; + if (!isLambda1520 && !isProxySpecies) { + continue; + } + + if (part.y() < minRapidity || part.y() > maxRapidity) { + continue; + } + if (mcCfg.applyGeneratedEtaCut && std::abs(part.eta()) > maxAbsGeneratedEta) { + continue; + } + + if (isLambda1520) { + if (!part.producedByGenerator()) { + continue; + } + auto genProton = part; + auto genKaon = part; + if (!findProtonKaonDaughters(part, genProton, genKaon)) { + continue; + } + if (part.pdgCode() > 0) { + histos.fill(HIST("SignalLoss/Lambda1520/hPt_denominator"), part.pt(), centrality); + if (hasAcceptedReco) { + histos.fill(HIST("SignalLoss/Lambda1520/hPt_numerator"), part.pt(), centrality); + } + } else { + histos.fill(HIST("SignalLoss/AntiLambda1520/hPt_denominator"), part.pt(), centrality); + if (hasAcceptedReco) { + histos.fill(HIST("SignalLoss/AntiLambda1520/hPt_numerator"), part.pt(), centrality); + } + } + continue; + } + + if (!part.isPhysicalPrimary()) { + continue; + } + fillSignalLossProxySpecies(part, centrality); + if (hasAcceptedReco) { + fillSignalLossProxySpecies(part, centrality); + } + } + } + PROCESS_SWITCH(HParticleCorrelation4Block4, processSignalLoss, "Process signal loss (K, p, Lambda, Xi, Omega proxies and Lambda(1520))", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{ + adaptAnalysisTask(context), + adaptAnalysisTask(context), + adaptAnalysisTask(context), + adaptAnalysisTask(context), + }; +} diff --git a/PWGCF/TwoParticleCorrelations/Tasks/lambdaOrAntiLambdaProducerWithSpin.cxx b/PWGCF/TwoParticleCorrelations/Tasks/lambdaOrAntiLambdaProducerWithSpin.cxx index fb9decce085..72a7a8cca45 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/lambdaOrAntiLambdaProducerWithSpin.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/lambdaOrAntiLambdaProducerWithSpin.cxx @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -123,6 +124,21 @@ DECLARE_SOA_COLUMN(NegTrackId, negTrackId, int64_t); DECLARE_SOA_COLUMN(Alpha, alpha, float); DECLARE_SOA_COLUMN(QtArm, qtArm, float); +// Compatible with the K0Short mass window; not a truth label +DECLARE_SOA_COLUMN(IsKShortCompatible, isKShortCompatible, bool); + +// Positive daughter: TPC momentum, dE/dx signal, and PID +DECLARE_SOA_COLUMN(PosTPCMomentum, posTPCMomentum, float); +DECLARE_SOA_COLUMN(PosTPCSignal, posTPCSignal, float); +DECLARE_SOA_COLUMN(PosTPCNSigmaPr, posTPCNSigmaPr, float); +DECLARE_SOA_COLUMN(PosTPCNSigmaPi, posTPCNSigmaPi, float); + +// Negative daughter: TPC momentum, dE/dx signal, and PID +DECLARE_SOA_COLUMN(NegTPCMomentum, negTPCMomentum, float); +DECLARE_SOA_COLUMN(NegTPCSignal, negTPCSignal, float); +DECLARE_SOA_COLUMN(NegTPCNSigmaPr, negTPCNSigmaPr, float); +DECLARE_SOA_COLUMN(NegTPCNSigmaPi, negTPCNSigmaPi, float); + } // namespace lambdaorantilambda DECLARE_SOA_TABLE(LambdaOrAntiLambdas, @@ -176,7 +192,19 @@ DECLARE_SOA_TABLE(LambdaOrAntiLambdas, lambdaorantilambda::NegTrackId, lambdaorantilambda::Alpha, - lambdaorantilambda::QtArm); + lambdaorantilambda::QtArm, + + lambdaorantilambda::IsKShortCompatible, + + lambdaorantilambda::PosTPCMomentum, + lambdaorantilambda::PosTPCSignal, + lambdaorantilambda::PosTPCNSigmaPr, + lambdaorantilambda::PosTPCNSigmaPi, + + lambdaorantilambda::NegTPCMomentum, + lambdaorantilambda::NegTPCSignal, + lambdaorantilambda::NegTPCNSigmaPr, + lambdaorantilambda::NegTPCNSigmaPi); namespace lambdahyperon { @@ -210,6 +238,18 @@ DECLARE_SOA_COLUMN(PionDcaToPV, pionDcaToPV, float); DECLARE_SOA_COLUMN(V0Radius, v0Radius, float); DECLARE_SOA_COLUMN(Alpha, alpha, float); DECLARE_SOA_COLUMN(QtArm, qtArm, float); + +// Positive daughter +DECLARE_SOA_COLUMN(PosTPCMomentum, posTPCMomentum, float); +DECLARE_SOA_COLUMN(PosTPCSignal, posTPCSignal, float); +DECLARE_SOA_COLUMN(PosTPCNSigmaPr, posTPCNSigmaPr, float); +DECLARE_SOA_COLUMN(PosTPCNSigmaPi, posTPCNSigmaPi, float); + +// Negative daughter +DECLARE_SOA_COLUMN(NegTPCMomentum, negTPCMomentum, float); +DECLARE_SOA_COLUMN(NegTPCSignal, negTPCSignal, float); +DECLARE_SOA_COLUMN(NegTPCNSigmaPr, negTPCNSigmaPr, float); +DECLARE_SOA_COLUMN(NegTPCNSigmaPi, negTPCNSigmaPi, float); } // namespace lambdahyperon DECLARE_SOA_TABLE(Lambdas, "AOD", "LAMBDAS", o2::soa::Index<>, @@ -222,7 +262,11 @@ DECLARE_SOA_TABLE(Lambdas, "AOD", "LAMBDAS", o2::soa::Index<>, lambdahyperon::PosTrackId, lambdahyperon::NegTrackId, lambdahyperon::CosPA, lambdahyperon::DcaDau, lambdahyperon::DcaV0ToPV, lambdahyperon::ProtonDcaToPV, lambdahyperon::PionDcaToPV, - lambdahyperon::V0Radius, lambdahyperon::Alpha, lambdahyperon::QtArm); + lambdahyperon::V0Radius, lambdahyperon::Alpha, lambdahyperon::QtArm, + lambdahyperon::PosTPCMomentum, lambdahyperon::PosTPCSignal, + lambdahyperon::PosTPCNSigmaPr, lambdahyperon::PosTPCNSigmaPi, + lambdahyperon::NegTPCMomentum, lambdahyperon::NegTPCSignal, + lambdahyperon::NegTPCNSigmaPr, lambdahyperon::NegTPCNSigmaPi); DECLARE_SOA_TABLE(AntiLambdas, "AOD", "ANTILAMBDAS", o2::soa::Index<>, lambdahyperon::CollisionId, @@ -234,7 +278,11 @@ DECLARE_SOA_TABLE(AntiLambdas, "AOD", "ANTILAMBDAS", o2::soa::Index<>, lambdahyperon::PosTrackId, lambdahyperon::NegTrackId, lambdahyperon::CosPA, lambdahyperon::DcaDau, lambdahyperon::DcaV0ToPV, lambdahyperon::ProtonDcaToPV, lambdahyperon::PionDcaToPV, - lambdahyperon::V0Radius, lambdahyperon::Alpha, lambdahyperon::QtArm); + lambdahyperon::V0Radius, lambdahyperon::Alpha, lambdahyperon::QtArm, + lambdahyperon::PosTPCMomentum, lambdahyperon::PosTPCSignal, + lambdahyperon::PosTPCNSigmaPr, lambdahyperon::PosTPCNSigmaPi, + lambdahyperon::NegTPCMomentum, lambdahyperon::NegTPCSignal, + lambdahyperon::NegTPCNSigmaPr, lambdahyperon::NegTPCNSigmaPi); // ===================================================================== // MC-reconstructed Lambda/anti-Lambda candidates @@ -305,6 +353,9 @@ DECLARE_SOA_COLUMN(PieDcaToPV, pieDcaToPV, float); DECLARE_SOA_COLUMN(PieTPCCrossedRows, pieTPCCrossedRows, int); DECLARE_SOA_COLUMN(PieTPCNSigma, pieTPCNSigma, float); +// KShort Mass Campatability +DECLARE_SOA_COLUMN(IsKShortCompatible, isKShortCompatible, bool); + } // namespace mcrecolambda DECLARE_SOA_TABLE(McRecoLambdaCandidates, @@ -365,7 +416,8 @@ DECLARE_SOA_TABLE(McRecoLambdaCandidates, mcrecolambda::PiePhi, mcrecolambda::PieDcaToPV, mcrecolambda::PieTPCCrossedRows, - mcrecolambda::PieTPCNSigma); + mcrecolambda::PieTPCNSigma, + mcrecolambda::IsKShortCompatible); // ===================================================================== // MC-generated Lambda/anti-Lambda candidates @@ -409,6 +461,7 @@ struct LambdaOrAntiLambdaProducerWithSpin { Configurable minTPCCrossedRows{"minTPCCrossedRows", 70, "Minimum number of TPC crossed rows"}; Configurable maxTPCNSigma{"maxTPCNSigma", 3.0f, "Maximum absolute TPC PID n-sigma"}; + Configurable kShortRejectionWindow{"kShortRejectionWindow", 0.001f, "K0Short mass rejection half-window (GeV/c2)"}; ConfigurableAxis axisInvariantMass{"axisInvariantMass", {100, 1.08f, 1.2f}, "Invariant-mass axis"}; ConfigurableAxis axisLambdaAntiLambdaPt{"axisLambdaAntiLambdaPt", {200, 0.f, 10.f}, "LambdaAntiLambda transverse-momentum axis"}; @@ -466,6 +519,16 @@ struct LambdaOrAntiLambdaProducerWithSpin { rLambdaOrAntiLambda.add("Pion/hPhi", "Pion azimuth;#varphi;entries", HistType::kTH1F, {{72, 0.f, o2::constants::math::TwoPI}}); rLambdaOrAntiLambda.add("Pion/hDcaToPV", "Pion DCA to PV;DCA (cm);entries", HistType::kTH1F, {{200, 0.f, 10.f}}); + // Positive daughter: measured signal and both PID hypotheses + rLambdaOrAntiLambda.add("Positive/hTPCdEdxVsP", "Positive daughter;p_{TPC} (GeV/c);TPC dE/dx (arb. units)", HistType::kTH2F, {{200, 0.f, 10.f}, {500, 0.f, 1000.f}}); + rLambdaOrAntiLambda.add("Positive/hTPCNSigmaPrVsP", "Positive daughter, proton hypothesis;p_{TPC} (GeV/c);TPC n#sigma_{p}", HistType::kTH2F, {{200, 0.f, 10.f}, {200, -10.f, 10.f}}); + rLambdaOrAntiLambda.add("Positive/hTPCNSigmaPiVsP", "Positive daughter, pion hypothesis;p_{TPC} (GeV/c);TPC n#sigma_{#pi}", HistType::kTH2F, {{200, 0.f, 10.f}, {200, -10.f, 10.f}}); + + // Negative daughter: measured signal and both PID hypotheses + rLambdaOrAntiLambda.add("Negative/hTPCdEdxVsP", "Negative daughter;p_{TPC} (GeV/c);TPC dE/dx (arb. units)", HistType::kTH2F, {{200, 0.f, 10.f}, {500, 0.f, 1000.f}}); + rLambdaOrAntiLambda.add("Negative/hTPCNSigmaPrVsP", "Negative daughter, antiproton hypothesis;p_{TPC} (GeV/c);TPC n#sigma_{#bar{p}}", HistType::kTH2F, {{200, 0.f, 10.f}, {200, -10.f, 10.f}}); + rLambdaOrAntiLambda.add("Negative/hTPCNSigmaPiVsP", "Negative daughter, pion hypothesis;p_{TPC} (GeV/c);TPC n#sigma_{#pi}", HistType::kTH2F, {{200, 0.f, 10.f}, {200, -10.f, 10.f}}); + // Armenteros-Podolanski rLambdaOrAntiLambda.add("Armenteros/hAlpha", "Armenteros #alpha;#alpha;entries", HistType::kTH1F, {{200, -1.f, 1.f}}); rLambdaOrAntiLambda.add("Armenteros/hQtArm", "Armenteros q_{T};q_{T} (GeV/c);entries", HistType::kTH1F, {{200, 0.f, 0.4f}}); @@ -494,6 +557,9 @@ struct LambdaOrAntiLambdaProducerWithSpin { continue; } + const bool isKShortCompatible = + std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) <= kShortRejectionWindow.value; + const bool isLambda = std::abs(posTrack.tpcNSigmaPr()) < maxTPCNSigma && std::abs(negTrack.tpcNSigmaPi()) < maxTPCNSigma; @@ -575,7 +641,22 @@ struct LambdaOrAntiLambdaProducerWithSpin { // Armenteros-Podolanski v0.alpha(), - v0.qtarm()); + v0.qtarm(), + + // K0 Mass Comaptablity test + isKShortCompatible, + + // Positive daughter: measured signal and both PID hypotheses + posTrack.tpcInnerParam(), + posTrack.tpcSignal(), + posTrack.tpcNSigmaPr(), + posTrack.tpcNSigmaPi(), + + // Negative daughter: measured signal and both PID hypotheses + negTrack.tpcInnerParam(), + negTrack.tpcSignal(), + negTrack.tpcNSigmaPr(), + negTrack.tpcNSigmaPi()); rLambdaOrAntiLambda.fill(HIST("Event/hCentrality"), collision.centFT0M()); @@ -619,6 +700,16 @@ struct LambdaOrAntiLambdaProducerWithSpin { rLambdaOrAntiLambda.fill(HIST("Armenteros/hAlpha"), v0.alpha()); rLambdaOrAntiLambda.fill(HIST("Armenteros/hQtArm"), v0.qtarm()); rLambdaOrAntiLambda.fill(HIST("Armenteros/hAlphaVsQt"), v0.alpha(), v0.qtarm()); + + // Positive daughter: before PID selection + rLambdaOrAntiLambda.fill(HIST("Positive/hTPCdEdxVsP"), posTrack.tpcInnerParam(), posTrack.tpcSignal()); + rLambdaOrAntiLambda.fill(HIST("Positive/hTPCNSigmaPrVsP"), posTrack.tpcInnerParam(), posTrack.tpcNSigmaPr()); + rLambdaOrAntiLambda.fill(HIST("Positive/hTPCNSigmaPiVsP"), posTrack.tpcInnerParam(), posTrack.tpcNSigmaPi()); + + // Negative daughter: before PID selection + rLambdaOrAntiLambda.fill(HIST("Negative/hTPCdEdxVsP"), negTrack.tpcInnerParam(), negTrack.tpcSignal()); + rLambdaOrAntiLambda.fill(HIST("Negative/hTPCNSigmaPrVsP"), negTrack.tpcInnerParam(), negTrack.tpcNSigmaPr()); + rLambdaOrAntiLambda.fill(HIST("Negative/hTPCNSigmaPiVsP"), negTrack.tpcInnerParam(), negTrack.tpcNSigmaPi()); } } void processDummy(aod::Collisions const&) {} @@ -680,6 +771,16 @@ struct LambdaAntiLambdaSelector { rSelected.add((prefix + "/Armenteros/hAlpha").c_str(), "Armenteros #alpha;#alpha;entries", HistType::kTH1F, {{200, -1.f, 1.f}}); rSelected.add((prefix + "/Armenteros/hQtArm").c_str(), "Armenteros q_{T};q_{T} (GeV/c);entries", HistType::kTH1F, {{200, 0.f, 0.4f}}); rSelected.add((prefix + "/Armenteros/hAlphaVsQt").c_str(), "Armenteros-Podolanski;#alpha;q_{T} (GeV/c)", HistType::kTH2F, {{200, -1.f, 1.f}, {200, 0.f, 0.4f}}); + + // Positive daughter + rSelected.add((prefix + "/Positive/hTPCdEdxVsP").c_str(), "Positive daughter;p_{TPC} (GeV/c);TPC dE/dx (arb. units)", HistType::kTH2F, {{200, 0.f, 10.f}, {500, 0.f, 1000.f}}); + rSelected.add((prefix + "/Positive/hTPCNSigmaPrVsP").c_str(), "Positive daughter, proton hypothesis;p_{TPC} (GeV/c);TPC n#sigma_{p}", HistType::kTH2F, {{200, 0.f, 10.f}, {200, -10.f, 10.f}}); + rSelected.add((prefix + "/Positive/hTPCNSigmaPiVsP").c_str(), "Positive daughter, pion hypothesis;p_{TPC} (GeV/c);TPC n#sigma_{#pi}", HistType::kTH2F, {{200, 0.f, 10.f}, {200, -10.f, 10.f}}); + + // Negative daughter + rSelected.add((prefix + "/Negative/hTPCdEdxVsP").c_str(), "Negative daughter;p_{TPC} (GeV/c);TPC dE/dx (arb. units)", HistType::kTH2F, {{200, 0.f, 10.f}, {500, 0.f, 1000.f}}); + rSelected.add((prefix + "/Negative/hTPCNSigmaPrVsP").c_str(), "Negative daughter, antiproton hypothesis;p_{TPC} (GeV/c);TPC n#sigma_{#bar{p}}", HistType::kTH2F, {{200, 0.f, 10.f}, {200, -10.f, 10.f}}); + rSelected.add((prefix + "/Negative/hTPCNSigmaPiVsP").c_str(), "Negative daughter, pion hypothesis;p_{TPC} (GeV/c);TPC n#sigma_{#pi}", HistType::kTH2F, {{200, 0.f, 10.f}, {200, -10.f, 10.f}}); } void init(InitContext const&) @@ -720,6 +821,14 @@ struct LambdaAntiLambdaSelector { rSelected.fill(HIST("Lambda/Armenteros/hAlpha"), candidate.alpha()); rSelected.fill(HIST("Lambda/Armenteros/hQtArm"), candidate.qtArm()); rSelected.fill(HIST("Lambda/Armenteros/hAlphaVsQt"), candidate.alpha(), candidate.qtArm()); + + rSelected.fill(HIST("Lambda/Positive/hTPCdEdxVsP"), candidate.posTPCMomentum(), candidate.posTPCSignal()); + rSelected.fill(HIST("Lambda/Positive/hTPCNSigmaPrVsP"), candidate.posTPCMomentum(), candidate.posTPCNSigmaPr()); + rSelected.fill(HIST("Lambda/Positive/hTPCNSigmaPiVsP"), candidate.posTPCMomentum(), candidate.posTPCNSigmaPi()); + + rSelected.fill(HIST("Lambda/Negative/hTPCdEdxVsP"), candidate.negTPCMomentum(), candidate.negTPCSignal()); + rSelected.fill(HIST("Lambda/Negative/hTPCNSigmaPrVsP"), candidate.negTPCMomentum(), candidate.negTPCNSigmaPr()); + rSelected.fill(HIST("Lambda/Negative/hTPCNSigmaPiVsP"), candidate.negTPCMomentum(), candidate.negTPCNSigmaPi()); } else { rSelected.fill(HIST("AntiLambda/Event/hCentrality"), candidate.centrality()); rSelected.fill(HIST("AntiLambda/Event/hMultiplicity"), candidate.multiplicity()); @@ -748,6 +857,14 @@ struct LambdaAntiLambdaSelector { rSelected.fill(HIST("AntiLambda/Armenteros/hAlpha"), candidate.alpha()); rSelected.fill(HIST("AntiLambda/Armenteros/hQtArm"), candidate.qtArm()); rSelected.fill(HIST("AntiLambda/Armenteros/hAlphaVsQt"), candidate.alpha(), candidate.qtArm()); + + rSelected.fill(HIST("AntiLambda/Positive/hTPCdEdxVsP"), candidate.posTPCMomentum(), candidate.posTPCSignal()); + rSelected.fill(HIST("AntiLambda/Positive/hTPCNSigmaPrVsP"), candidate.posTPCMomentum(), candidate.posTPCNSigmaPr()); + rSelected.fill(HIST("AntiLambda/Positive/hTPCNSigmaPiVsP"), candidate.posTPCMomentum(), candidate.posTPCNSigmaPi()); + + rSelected.fill(HIST("AntiLambda/Negative/hTPCdEdxVsP"), candidate.negTPCMomentum(), candidate.negTPCSignal()); + rSelected.fill(HIST("AntiLambda/Negative/hTPCNSigmaPrVsP"), candidate.negTPCMomentum(), candidate.negTPCNSigmaPr()); + rSelected.fill(HIST("AntiLambda/Negative/hTPCNSigmaPiVsP"), candidate.negTPCMomentum(), candidate.negTPCNSigmaPi()); } } // ================================================================ @@ -845,7 +962,7 @@ struct LambdaAntiLambdaSelector { Configurable minLambdaPt{"minLambdaPt", 0.5f, "Minimum Lambda pT (GeV/c)"}; - Configurable maxLambdaPt{"maxLambdaPt", 4.5f, "Maximum Lambda pT (GeV/c)"}; + Configurable maxLambdaPt{"maxLambdaPt", 5.0f, "Maximum Lambda pT (GeV/c)"}; Configurable maxLambdaRapidity{"maxLambdaRapidity", 0.5f, "Maximum absolute Lambda rapidity"}; @@ -860,6 +977,12 @@ struct LambdaAntiLambdaSelector { aod::lambdaorantilambda::pt < maxLambdaPt&& nabs(aod::lambdaorantilambda::rapidity) < maxLambdaRapidity; + // ===================================================================== + // Applying KShort Mass Compatablilty Filtere + // ===================================================================== + + Filter kShortRejectionFilter = + aod::lambdaorantilambda::isKShortCompatible == false; // All compatible filters above are combined with logical AND. using SelectedLambdaOrAntiLambdas = soa::Filtered; @@ -916,7 +1039,17 @@ struct LambdaAntiLambdaSelector { candidate.v0Radius(), candidate.alpha(), - candidate.qtArm()); + candidate.qtArm(), + + candidate.posTPCMomentum(), + candidate.posTPCSignal(), + candidate.posTPCNSigmaPr(), + candidate.posTPCNSigmaPi(), + + candidate.negTPCMomentum(), + candidate.negTPCSignal(), + candidate.negTPCNSigmaPr(), + candidate.negTPCNSigmaPi()); } // ============================================================ @@ -959,7 +1092,17 @@ struct LambdaAntiLambdaSelector { candidate.v0Radius(), candidate.alpha(), - candidate.qtArm()); + candidate.qtArm(), + + candidate.posTPCMomentum(), + candidate.posTPCSignal(), + candidate.posTPCNSigmaPr(), + candidate.posTPCNSigmaPi(), + + candidate.negTPCMomentum(), + candidate.negTPCSignal(), + candidate.negTPCNSigmaPr(), + candidate.negTPCNSigmaPi()); } } void processDummy(aod::Collisions const&) {} @@ -979,6 +1122,7 @@ struct LambdaAntiLambdaMcRecoTableProducer { // ================================================================ Configurable maxTPCNSigma{"maxTPCNSigma", 3.f, "Maximum TPC PID n-sigma"}; + Configurable kShortRejectionWindow{"kShortRejectionWindow", 0.001f, "K0Short mass rejection half-window (GeV/c2)"}; // ================================================================ // Lambda/anti-Lambda invariant-mass selection @@ -1009,6 +1153,9 @@ struct LambdaAntiLambdaMcRecoTableProducer { auto posTrack = v0.posTrack_as(); auto negTrack = v0.negTrack_as(); + const bool isKShortCompatible = + std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) <= kShortRejectionWindow.value; + const bool isLambda = std::abs(posTrack.tpcNSigmaPr()) < maxTPCNSigma && std::abs(negTrack.tpcNSigmaPi()) < maxTPCNSigma; @@ -1140,7 +1287,8 @@ struct LambdaAntiLambdaMcRecoTableProducer { pionTrack.phi(), pionDcaToPV, static_cast(pionTrack.tpcNClsCrossedRows()), - pionTrack.tpcNSigmaPi()); + pionTrack.tpcNSigmaPi(), + isKShortCompatible); } } @@ -1228,7 +1376,7 @@ struct LambdaAntiLambdaEfficiencyPlots { // ================================================================ Configurable minLambdaPt{"minLambdaPt", 0.5f, "Minimum Lambda pT"}; - Configurable maxLambdaPt{"maxLambdaPt", 4.5f, "Maximum Lambda pT"}; + Configurable maxLambdaPt{"maxLambdaPt", 5.0f, "Maximum Lambda pT"}; Configurable maxLambdaRapidity{"maxLambdaRapidity", 0.5f, "Maximum absolute Lambda rapidity"}; Filter mcRecoKinematicFilter = aod::mcrecolambda::recoPt > minLambdaPt&& @@ -1247,6 +1395,13 @@ struct LambdaAntiLambdaEfficiencyPlots { nabs(aod::mcrecolambda::proEta) < maxDaughterEta&& nabs(aod::mcrecolambda::pieEta) < maxDaughterEta&& aod::mcrecolambda::proTPCCrossedRows >= minTPCCrossedRows&& aod::mcrecolambda::pieTPCCrossedRows >= minTPCCrossedRows; + Configurable minLambdaMass{"minLambdaMass", 1.11f, "Minimum Lambda mass (GeV/c2)"}; + Configurable maxLambdaMass{"maxLambdaMass", 1.12f, "Maximum Lambda mass (GeV/c2)"}; + + Filter mcRecoMassFilter = aod::mcrecolambda::massLambda >= minLambdaMass && aod::mcrecolambda::massLambda <= maxLambdaMass; + + Filter mcRecoKShortRejectionFilter = aod::mcrecolambda::isKShortCompatible == false; + void addEfficiencyPlotsHistograms(std::string const& particle) { const std::string recoDirectory = particle + "/Reco/"; @@ -1375,14 +1530,18 @@ struct LambdaAntiLambdaSelectionCutFlow { // Lambda kinematics Configurable minLambdaPt{"minLambdaPt", 0.5f, "Minimum Lambda pT"}; - Configurable maxLambdaPt{"maxLambdaPt", 4.5f, "Maximum Lambda pT"}; + Configurable maxLambdaPt{"maxLambdaPt", 5.0f, "Maximum Lambda pT"}; Configurable maxLambdaRapidity{"maxLambdaRapidity", 0.5f, "Maximum absolute Lambda rapidity"}; + // Lambda Mass Cuts + Configurable minLambdaMass{"minLambdaMass", 1.11f, "Minimum Lambda mass (GeV/c2)"}; + Configurable maxLambdaMass{"maxLambdaMass", 1.12f, "Maximum Lambda mass (GeV/c2)"}; + void init(InitContext const&) { AxisSpec eventCutAxis{3, -0.5, 2.5, "Event-selection stage"}; - AxisSpec lambdaCutAxis{8, -0.5, 7.5, "Candidate-selection stage"}; + AxisSpec lambdaCutAxis{10, -0.5, 9.5, "Candidate-selection stage"}; cutFlowRegistry.add("Event/hCutFlow", "Event cut flow;Selection;Collisions", HistType::kTH1D, {eventCutAxis}); cutFlowRegistry.add("Lambda/hCutFlow", "Lambda cut flow;Selection;Candidates", HistType::kTH1D, {lambdaCutAxis}); @@ -1407,6 +1566,8 @@ struct LambdaAntiLambdaSelectionCutFlow { histogram->GetXaxis()->SetBinLabel(6, "TPC crossed rows"); histogram->GetXaxis()->SetBinLabel(7, "V0 topology"); histogram->GetXaxis()->SetBinLabel(8, "Lambda kinematics"); + histogram->GetXaxis()->SetBinLabel(9, "K0Short rejection"); + histogram->GetXaxis()->SetBinLabel(10, "Lambda mass window"); } } @@ -1487,6 +1648,17 @@ struct LambdaAntiLambdaSelectionCutFlow { continue; } fillStage(7.f); + + if (candidate.isKShortCompatible()) { + continue; + } + fillStage(8.f); + + if (candidate.massLambda() < minLambdaMass.value || + candidate.massLambda() > maxLambdaMass.value) { + continue; + } + fillStage(9.f); } } void processDummy(aod::Collisions const&) {} @@ -1520,11 +1692,17 @@ struct LambdaAntiLambdaPairAnalysis { // Mixed-Event Compatablity variables Configurable mixedEventMaxDeltaMultiplicity{"mixedEventMaxDeltaMultiplicity", 10.f, "Maximum multiplicity difference between mixed events"}; + Configurable maxMixedEvents{"maxMixedEvents", 5, "Maximum multiplicity-compatible collision combinations with selected candidates in both collisions per dataframe"}; // Short-range pairs Configurable sameEventShortRangePairMaxDeltaRapidity{"sameEventShortRangePairMaxDeltaRapidity", 0.5f, "ShortRangePai Maximum absolute rapidity difference for same-event pairs"}; Configurable sameEventShortRangePairMaxDeltaPhi{"sameEventShortRangePairMaxDeltaPhi", 1.047f, "ShortRangePai Maximum absolute wrapped phi difference for same-event pairs"}; // Pi/3 =1.047 + // For JackKnife Method of Error estemation + Configurable jackknife{"jackknife", false, "Enable leave-one-group-out jackknife"}; + Configurable jackknifeExcludedGroup{"jackknifeExcludedGroup", 0, "Group to exclude: 0 to 9"}; + static constexpr int NJackknifeGroups = 10; + void addHistograms(TString const& candidate1, TString const& candidate2) { //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Same-Event~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1726,6 +1904,9 @@ struct LambdaAntiLambdaPairAnalysis { addHistograms("Lambda", "AntiLambda"); addHistograms("Lambda", "Lambda"); addHistograms("AntiLambda", "AntiLambda"); + if (jackknife.value && (jackknifeExcludedGroup.value < 0 || jackknifeExcludedGroup.value >= NJackknifeGroups)) { + LOGF(fatal, "jackknifeExcludedGroup must be between 0 and 9"); + } } ROOT::Math::PxPyPzMVector daughterInParentRestFrame(float parentLambdaPx, float parentLambdaPy, float parentLambdaPz, float parentLambdaMass, float daughterProtonPx, float daughterProtonPy, float daughterProtonPz, float daughterProtonMass) @@ -2258,11 +2439,14 @@ struct LambdaAntiLambdaPairAnalysis { Preslice lambdasPerCollision = aod::lambdahyperon::collisionId; Preslice antiLambdasPerCollision = aod::lambdahyperon::collisionId; - void processData(CollisionsWithMultiplicity const& collisions, FilteredLambdas const& lambdas, FilteredAntiLambdas const& antiLambdas) + void processSameEventData(CollisionsWithMultiplicity const& collisions, FilteredLambdas const& lambdas, FilteredAntiLambdas const& antiLambdas) { static constexpr int MinimumSameSpeciesCandidates = 2; for (auto const& collision : collisions) { + if (jackknife.value && collision.globalIndex() % NJackknifeGroups == jackknifeExcludedGroup.value) { + continue; + } const auto lambdasThisCollision = lambdas.sliceBy(lambdasPerCollision, collision.globalIndex()); const auto antiLambdasThisCollision = antiLambdas.sliceBy(antiLambdasPerCollision, collision.globalIndex()); @@ -2282,15 +2466,23 @@ struct LambdaAntiLambdaPairAnalysis { fillAntiLambdaAntiLambdaSameEvent(antiLambdasThisCollision); } } + } - // ============================================================ - // Mixed events - // ============================================================ + void processMixedEventData(CollisionsWithMultiplicity const& collisions, FilteredLambdas const& lambdas, FilteredAntiLambdas const& antiLambdas) + { + int mixedEventCounter = 0; for (auto const& [collision1, collision2] : combinations(CombinationsStrictlyUpperIndexPolicy(collisions, collisions))) { + if (jackknife.value && (collision1.globalIndex() % NJackknifeGroups == jackknifeExcludedGroup.value || collision2.globalIndex() % NJackknifeGroups == jackknifeExcludedGroup.value)) { + continue; + } const float deltaMultiplicity = std::abs(static_cast(collision1.multNTracksPV()) - static_cast(collision2.multNTracksPV())); + if (mixedEventCounter >= maxMixedEvents) { + break; + } + if (deltaMultiplicity > mixedEventMaxDeltaMultiplicity) { continue; } @@ -2301,6 +2493,12 @@ struct LambdaAntiLambdaPairAnalysis { const auto antiLambdas1 = antiLambdas.sliceBy(antiLambdasPerCollision, collision1.globalIndex()); const auto antiLambdas2 = antiLambdas.sliceBy(antiLambdasPerCollision, collision2.globalIndex()); + // Empty collisions must not consume the mixing allowance. + if ((lambdas1.size() == 0 && antiLambdas1.size() == 0) || + (lambdas2.size() == 0 && antiLambdas2.size() == 0)) { + continue; + } + // Lambda–anti-Lambda requires at least one of each. if (lambdas1.size() > 0 && antiLambdas2.size() > 0) { fillLambdaAntiLambdaMixedEvent(lambdas1, antiLambdas2); @@ -2316,33 +2514,18 @@ struct LambdaAntiLambdaPairAnalysis { if (antiLambdas1.size() > 0 && antiLambdas2.size() > 0) { fillAntiLambdaAntiLambdaMixedEvent(antiLambdas1, antiLambdas2); } + + ++mixedEventCounter; } } + void processDummy(aod::Collisions const&) {} - PROCESS_SWITCH(LambdaAntiLambdaPairAnalysis, processData, "Run candidate production", true); + PROCESS_SWITCH(LambdaAntiLambdaPairAnalysis, processSameEventData, "Run same-event pair analysis", true); + PROCESS_SWITCH(LambdaAntiLambdaPairAnalysis, processMixedEventData, "Run mixed-event pair analysis", false); PROCESS_SWITCH(LambdaAntiLambdaPairAnalysis, processDummy, "Skip candidate production", false); }; -/*WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - const bool isMC = cfgc.options().get("isMC"); - - WorkflowSpec workflow; - - if (isMC) { - workflow.push_back(adaptAnalysisTask(cfgc)); - workflow.push_back(adaptAnalysisTask(cfgc)); - workflow.push_back(adaptAnalysisTask(cfgc)); - } else { - workflow.push_back(adaptAnalysisTask(cfgc)); - workflow.push_back(adaptAnalysisTask(cfgc)); - workflow.push_back(adaptAnalysisTask(cfgc)); - } - - return workflow; -}*/ - WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ diff --git a/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx b/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx index bb09321931a..f076baf3e3c 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx @@ -24,10 +24,12 @@ #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" +#include #include #include #include #include +#include #include #include #include @@ -38,38 +40,55 @@ #include #include +#include +#include #include #include +#include +#include +#include +#include #include #include #include +#include #include #include #include #include #include #include +#include #include +#include #include using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; -using MyTracks = soa::Join; +// MC-labelled versions used by processMCEfficiency and processMCRecBF. +using MyTracksMC = soa::Join; +using MyV0sMC = soa::Join; + +namespace +{ +float displayDeltaPhi(float dPhi) +{ + return RecoDecay::constrainAngle(dPhi, -o2::constants::math::PI / 2.0f); +} +} // namespace // Per-centrality-bin histogram set. Defined at file scope (NOT inside the task struct), // otherwise O2's struct reflection in adaptAnalysisTask fails to compile. struct CentRho2Set { - // eta space (only booked if cFillEtaSpace && cFillCentEtaSpace) - std::shared_ptr Lp, LAp, ALp, ALAp, pp, pAp, App, ApAp, LL, LAL, ALL, ALAL; // y space std::shared_ptr Lp_y, LAp_y, ALp_y, ALAp_y, pp_y, pAp_y, App_y, ApAp_y, LL_y, LAL_y, ALL_y, ALAL_y; - // rho1 eta - std::shared_ptr rho1_Proton, rho1_AntiProton, rho1_Lambda, rho1_AntiLambda; // rho1 y std::shared_ptr rho1_Proton_y, rho1_AntiProton_y, rho1_Lambda_y, rho1_AntiLambda_y; // event counter @@ -78,7 +97,6 @@ struct CentRho2Set { struct LambdaProtonBalanceFunction { HistogramRegistry registryLambda{"Lambda_invMass", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - HistogramRegistry registryLambdaExtended{"Lambda_invMassExtended", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry registryRho{"rho1ANDrho2_LP", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry registryCent{"rho1ANDrho2_LP_CentralityBased", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry registryOther{"Other_Hists", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; @@ -90,120 +108,122 @@ struct LambdaProtonBalanceFunction { // Folder: ProtonVetoQA/ // Contains per-event distributions of N_before, N_after, N_removed, fraction. HistogramRegistry registryProtonVetoQA{"ProtonVetoQA", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - - // pT bins 0.6 → 3.6 in steps of 0.2 (used for per-bin rho2 / mass histograms) - std::vector ptEdges; - - // ════════════════════════════════════════════════════════════════════════ - // SECTION A — Invariant mass histograms - // ════════════════════════════════════════════════════════════════════════ - std::vector> hMassLambdaPtBins; - std::vector> hMassAntiLambdaPtBins; - std::shared_ptr hMassLambdaMerged; - std::shared_ptr hMassAntiLambdaMerged; + // MC efficiency maps (Gen*/Rec* TH3: x=cent, y=pT, z=y), filled only by processMCEfficiency. Folder: Efficiency/ + HistogramRegistry registryEff{"Efficiency", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; // ════════════════════════════════════════════════════════════════════════ - // SECTION A2— Extended invariant-mass histograms (y × pT grid) - // hMassLambdaExtended[iY][iPt] → Lambda_invMassExtended/Lambda/ - // hMassAntiLambdaExtended[iY][iPt] → Lambda_invMassExtended/AntiLambda/ - // Rapidity bins (kExtNyBins = 2): [-0.8,-0.7), [-0.7,-0.6] - // pT bins (kExtNptBins = 25): [0.0,0.1), [0.1,0.2), ..., [2.4,2.5] + // TaskState — ordinary C++ runtime/implementation state.. + // Grouped into ONE nested object so that the number of direct data members of + // this task struct stays well below the O2 StructToTuple / structured-binding + // reflection limit. Contains NO O2 Framework objects (no Configurable, + // ConfigurableAxis, Filter, HistogramRegistry): those must remain top-level. // ════════════════════════════════════════════════════════════════════════ - static constexpr int kExtNyBins = 2; - static constexpr int kExtNptBins = 25; - static constexpr float kExtYMin = -0.8f; - static constexpr float kExtYMax = -0.6f; - static constexpr float kExtYStep = 0.1f; - static constexpr float kExtPtMin = 0.0f; - static constexpr float kExtPtMax = 2.5f; - static constexpr float kExtPtStep = 0.1f; - - std::vector>> hMassLambdaExtended; - std::vector>> hMassAntiLambdaExtended; + struct TaskState { + // pT bins 0.6 → 3.6 in steps of 0.2 (used for per-bin pT-spectra / mass histograms) + std::vector ptEdges; + + // ════════════════════════════════════════════════════════════════════════ + // SECTION A — Invariant mass histograms + // ════════════════════════════════════════════════════════════════════════ + std::vector> hMassLambdaPtBins; + std::vector> hMassAntiLambdaPtBins; + std::shared_ptr hMassLambdaMerged; + std::shared_ptr hMassAntiLambdaMerged; + + // ════════════════════════════════════════════════════════════════════════ + // SECTION C — rho2 rapidity-space histograms, filled with unrolledIndexY(y, phi). + // Naming: hRho2__y_AllPt. + // ════════════════════════════════════════════════════════════════════════ + // Lambda–proton family (y) + std::shared_ptr hRho2_Lp_y_AllPt; + std::shared_ptr hRho2_LAp_y_AllPt; + std::shared_ptr hRho2_ALp_y_AllPt; + std::shared_ptr hRho2_ALAp_y_AllPt; + // Proton–proton family (y) + std::shared_ptr hRho2_pp_y_AllPt; + std::shared_ptr hRho2_pAp_y_AllPt; + std::shared_ptr hRho2_App_y_AllPt; + std::shared_ptr hRho2_ApAp_y_AllPt; + // Lambda–Lambda family (y) + std::shared_ptr hRho2_LL_y_AllPt; + std::shared_ptr hRho2_LAL_y_AllPt; + std::shared_ptr hRho2_ALL_y_AllPt; + std::shared_ptr hRho2_ALAL_y_AllPt; + + // SECTION E — centrality-differential storage (struct CentRho2Set is defined above the task) + std::vector centSets; + std::vector centEdgesLocal; + + // ════════════════════════════════════════════════════════════════════════ + // SECTION D — pT-spectra and pair-count monitoring histograms + // ════════════════════════════════════════════════════════════════════════ + std::vector> hPtPrimProton_Lambda; + std::vector> hPtPrimAntiProton_Lambda; + std::vector> hPtPrimProton_AntiLambda; + std::vector> hPtPrimAntiProton_AntiLambda; + + std::shared_ptr hPtSelectedPrimProton_AllPt; + std::shared_ptr hPtSelectedPrimAntiProton_AllPt; + std::shared_ptr hPtSelLambda_AllPt; + std::shared_ptr hPtSelAntiLambda_AllPt; + + // ── PID-check histograms (ProtonCounts_byPID_Check) ─────────────────── + std::shared_ptr h_TPC; + std::shared_ptr h_TPCandTOF; + std::shared_ptr h_TPCorTOF; + // ───────────────────────────────────────────────────────────────────── - // ════════════════════════════════════════════════════════════════════════ - // SECTION B — rho2 eta-space histograms - // Naming: hRho2_ = per-pT-bin vector - // hRho2__pT015toMaxDefined = merged (full pT range as - // set by the pT cuts: protons 0.5-3.6, Lambdas 0.6-3.6 GeV/c; - // "015" is a legacy name and does NOT reflect the actual lower cut) - // ════════════════════════════════════════════════════════════════════════ - // Lambda–proton family - std::shared_ptr hRho2_Lp_pT015toMaxDefined; - std::shared_ptr hRho2_LAp_pT015toMaxDefined; - std::shared_ptr hRho2_ALp_pT015toMaxDefined; - std::shared_ptr hRho2_ALAp_pT015toMaxDefined; - // Proton–proton family - std::shared_ptr hRho2_pp_pT015toMaxDefined; - std::shared_ptr hRho2_pAp_pT015toMaxDefined; - std::shared_ptr hRho2_App_pT015toMaxDefined; - std::shared_ptr hRho2_ApAp_pT015toMaxDefined; - // Lambda–Lambda family - std::shared_ptr hRho2_LL_pT015toMaxDefined; - std::shared_ptr hRho2_LAL_pT015toMaxDefined; - std::shared_ptr hRho2_ALL_pT015toMaxDefined; - std::shared_ptr hRho2_ALAL_pT015toMaxDefined; + // ── Run-level accumulators for proton-veto importance QA ───────────── + // These are summed over all processed events and printed in endOfStream(). + // They quantify how much Lambda feed-down was removed from the proton + // sample before constructing future Balance Functions. + int64_t runTotalProtonBeforeVeto = 0; ///< sum of N_before_veto over all events + int64_t runTotalProtonRemovedVeto = 0; ///< sum of N_removed_by_veto over all events + int64_t runTotalAntiPBeforeVeto = 0; ///< antiproton: sum of N_before_veto + int64_t runTotalAntiPRemovedVeto = 0; ///< antiproton: sum of N_removed_by_veto + + // Runtime copies populated from Configurables in init(). + // Not static, not constexpr — values are known only after O2 delivers the config. + int mRhoPhiBins = 0; + int mRhoYBins = 0; + int mRhoUnrolledBinsY = 0; // mRhoYBins * mRhoPhiBins + float mRhoYMin = 0.f; + float mRhoYMax = 0.f; + // Centrality range of the analysis = first and last edge of cCentBins (set in init()). + float mCentMin = 0.f; + float mCentMax = 0.f; + + // 1/efficiency maps (TH3: x=cent, y=pt, z=y); loaded once in init() when cApplyEffCorr is true + TH3* invEffL = nullptr; + TH3* invEffAL = nullptr; + TH3* invEffP = nullptr; + TH3* invEffAP = nullptr; + }; - // ════════════════════════════════════════════════════════════════════════ - // SECTION C — rho2 rapidity-space histograms - // Same channels as Section B but filled with unrolledIndexY(y, phi) - // Naming: hRho2__y / hRho2__y_pT015toMaxDefined - // ════════════════════════════════════════════════════════════════════════ - // Lambda–proton family (y) - std::shared_ptr hRho2_Lp_y_pT015toMaxDefined; - std::shared_ptr hRho2_LAp_y_pT015toMaxDefined; - std::shared_ptr hRho2_ALp_y_pT015toMaxDefined; - std::shared_ptr hRho2_ALAp_y_pT015toMaxDefined; - // Proton–proton family (y) - std::shared_ptr hRho2_pp_y_pT015toMaxDefined; - std::shared_ptr hRho2_pAp_y_pT015toMaxDefined; - std::shared_ptr hRho2_App_y_pT015toMaxDefined; - std::shared_ptr hRho2_ApAp_y_pT015toMaxDefined; - // Lambda–Lambda family (y) - std::shared_ptr hRho2_LL_y_pT015toMaxDefined; - std::shared_ptr hRho2_LAL_y_pT015toMaxDefined; - std::shared_ptr hRho2_ALL_y_pT015toMaxDefined; - std::shared_ptr hRho2_ALAL_y_pT015toMaxDefined; - - // SECTION E — centrality-differential storage (struct CentRho2Set is defined above the task) - std::vector centSets; - std::vector centEdgesLocal; + TaskState state; - // ════════════════════════════════════════════════════════════════════════ - // SECTION D — pT-spectra and pair-count monitoring histograms - // ════════════════════════════════════════════════════════════════════════ - std::vector> hPtPrimProton_Lambda; - std::vector> hPtPrimAntiProton_Lambda; - std::vector> hPtPrimProton_AntiLambda; - std::vector> hPtPrimAntiProton_AntiLambda; - - std::shared_ptr hPtSelectedPrimProton_pT015toMaxDefined; - std::shared_ptr hPtSelectedPrimAntiProton_pT015toMaxDefined; - std::shared_ptr hPtSelLambda_pT015toMaxDefined; - std::shared_ptr hPtSelAntiLambda_pT015toMaxDefined; - - // ── PID-check histograms (ProtonCounts_byPID_Check) ─────────────────── - std::shared_ptr h_TPC; - std::shared_ptr h_TPCandTOF; - std::shared_ptr h_TPCorTOF; - // ───────────────────────────────────────────────────────────────────── + Service ccdb{}; - // ── Run-level accumulators for proton-veto importance QA ───────────── - // These are summed over all processed events and printed in endOfStream(). - // They quantify how much Lambda feed-down was removed from the proton - // sample before constructing future Balance Functions. - int64_t runTotalProtonBeforeVeto = 0; ///< sum of N_before_veto over all events - int64_t runTotalProtonRemovedVeto = 0; ///< sum of N_removed_by_veto over all events - int64_t runTotalAntiPBeforeVeto = 0; ///< antiproton: sum of N_before_veto - int64_t runTotalAntiPRemovedVeto = 0; ///< antiproton: sum of N_removed_by_veto // ───────────────────────────────────────────────────────────────────── // ── Configurables ───────────────────────────────────────────────────── Configurable cPPandV0ZVertexCut{"cPPandV0ZVertexCut", 10.0f, "Accepted z-vertex range (cm)"}; - Configurable cFillEtaSpace{"cFillEtaSpace", false, "Fill eta-space rho1/rho2 histograms"}; - Configurable cUseQinvCut{"cUseQinvCut", true, "Apply q_inv > 0.01 GeV/c pair cut (false = no q_inv rejection)"}; + Configurable cPileupReject{"cPileupReject", false, "Reject same-bunch pileup (kNoSameBunchPileup)"}; + Configurable cZVtxTimeDiff{"cZVtxTimeDiff", false, "Require FT0 vs PV z-vertex agreement (kIsGoodZvtxFT0vsPV)"}; + Configurable cIsGoodITSLayers{"cIsGoodITSLayers", false, "Require all ITS layers good (kIsGoodITSLayersAll)"}; + Configurable cUseQinvCut{"cUseQinvCut", false, "Apply q_inv > cQinvCutValue pair cut (false = no q_inv rejection)"}; + Configurable cQinvCutValue{"cQinvCutValue", 0.001f, "q_inv cut value (GeV/c)"}; + Configurable cQinvQABins{"cQinvQABins", 1000, "Number of bins for QA q_inv histograms"}; + Configurable cQinvQAMax{"cQinvQAMax", 1.0f, "Max q_inv for QA histograms (GeV/c)"}; Configurable cFillCentHists{"cFillCentHists", true, "Fill centrality-differential rho1/rho2 histograms in separate rho1ANDrho2_LP_CentralityBased folder"}; + Configurable cUseFT0C{"cUseFT0C", false, "Use FT0C instead of FT0M as centrality estimator"}; + ConfigurableAxis cCentBins{"cCentBins", {VARIABLE_WIDTH, 0.f, 5.f, 10.f, 20.f, 40.f, 60.f, 80.f}, "Centrality bins (%)"}; + Configurable cApplyEffCorr{"cApplyEffCorr", false, "Weight rho1/rho2 by 1/efficiency"}; + Configurable cEffFile{"cEffFile", "", "ROOT file with hInvEff_Lambda, hInvEff_AntiLambda, hInvEff_Proton, hInvEff_AntiProton (TH3: x=cent, y=pt, z=y). Used only if cEffCcdbPath is empty"}; + Configurable cEffCcdbPath{"cEffCcdbPath", "", "CCDB path of a TList holding hInvEff_Lambda, hInvEff_AntiLambda, hInvEff_Proton, hInvEff_AntiProton; if non-empty it is used instead of cEffFile"}; + Configurable cCcdbUrl{"cCcdbUrl", "http://alice-ccdb.cern.ch", "CCDB URL"}; + Configurable cEffCcdbTimestamp{"cEffCcdbTimestamp", 1700000000000, "CCDB timestamp (ms) used to fetch the efficiency maps (must lie inside the object validity)"}; // Track quality cuts Configurable pProtonTPCMinRows{"pProtonTPCMinRows", 70, "Minimum TPC crossed rows"}; @@ -216,13 +236,13 @@ struct LambdaProtonBalanceFunction { Configurable pProtonMaxDCAxy{"pProtonMaxDCAxy", 0.02f, "Max |DCAxy| (cm)"}; // tight cut for cleanest primary-proton sample Configurable pProtonMaxDCAz{"pProtonMaxDCAz", 0.1f, "Max |DCAz| (cm)"}; - Configurable pProtonTPCTOFSwitchP{"pProtonTPCTOFSwitchP", 0.700000238f, "p threshold (GeV/c) above which TOF is used if available"}; + Configurable pProtonTPCTOFSwitchPt{"pProtonTPCTOFSwitchPt", 0.7f, "pT threshold (GeV/c) above which TOF is used if available"}; // Configurable cPPMinTofBeta{"cPPMinTofBeta", 0.4f, "Min TOF beta for proton selection (TOF branch only)"}; //BETACUTCommented // Configurable cPPMaxTofBeta{"cPPMaxTofBeta", 0.96f, "Max TOF beta for proton selection (TOF branch only)"}; //BETACUTCommented // V0 topology cuts + V0 DCA Cuts Configurable cV0MaxDcaDaughters{"cV0MaxDcaDaughters", 1.0f, "DCA V0 daughters < (cm)"}; - Configurable lambdaV0DaughterProtonMinDCAToPV{"lambdaV0DaughterProtonMinDCAToPV", 0.01f, "DCA proton daughter to PV > (cm)"}; + Configurable lambdaV0DaughterProtonMinDCAToPV{"lambdaV0DaughterProtonMinDCAToPV", 0.05f, "DCA proton daughter to PV > (cm)"}; Configurable lambdaV0DaughterPionMinDCAToPV{"lambdaV0DaughterPionMinDCAToPV", 0.10f, "DCA pion daughter to PV > (cm)"}; Configurable lambdaV0MinCosPA{"lambdaV0MinCosPA", 0.995, "V0 CosPA >"}; Configurable lambdaV0MinDecayRadius{"lambdaV0MinDecayRadius", 0.5f, "V0 radius > (cm)"}; @@ -234,6 +254,13 @@ struct LambdaProtonBalanceFunction { Configurable lambdaV0DaughterProtonTPCNsigma{"lambdaV0DaughterProtonTPCNsigma", 2.0f, "lambdaV0DaughterProtonTPCNsigma"}; Configurable pProtonTOFNsigma{"pProtonTOFNsigma", 2.0f, "pProtonTOFNsigma"}; + // Contaminant veto for primary (anti)protons: reject track if compatible with pi/K/e + Configurable cUseContaminantVeto{"cUseContaminantVeto", true, "Reject primary (anti)protons compatible with pi/K/e hypotheses"}; + Configurable cVetoUseTOF{"cVetoUseTOF", true, "Also apply TOF veto whenever the track has TOF"}; + Configurable pProtonVetoPionNsigma{"pProtonVetoPionNsigma", 2.0f, "Reject if |nSigma_pi| < this"}; + Configurable pProtonVetoKaonNsigma{"pProtonVetoKaonNsigma", 2.0f, "Reject if |nSigma_K| < this"}; + Configurable pProtonVetoElectronNsigma{"pProtonVetoElectronNsigma", 2.0f, "Reject if |nSigma_e| < this"}; + // // V0 daughter beta cuts (particle-dependent) //BETACUTCommented // Configurable cV0protonDauMinBeta{"cV0protonDauMinBeta", 0.4f, "Min TOF beta for (anti)proton V0 daughter"}; //BETACUTCommented // Configurable cV0protonDauMaxBeta{"cV0protonDauMaxBeta", 0.98f, "Max TOF beta for (anti)proton V0 daughter"}; //BETACUTCommented @@ -241,12 +268,10 @@ struct LambdaProtonBalanceFunction { // ───────────────────────────────────────────────────────────────────── // Proton Kinematic Cuts (from strange9) - Configurable pProtonMinP{"pProtonMinP", 0.5f, "Min p for primary protons (GeV/c)"}; - Configurable pProtonMaxP{"pProtonMaxP", 3.6f, "Max p for primary protons (GeV/c)"}; Configurable pProtonMinPt{"pProtonMinPt", 0.5f, "Min pT for primary protons (GeV/c)"}; Configurable pProtonMaxPt{"pProtonMaxPt", 3.6f, "Max pT for primary protons (GeV/c)"}; - Configurable pProtonMaxY{"pProtonMaxY", 0.5f, "Max |y| for primary protons"}; + Configurable pProtonMaxY{"pProtonMaxY", 0.49f, "Max |y| for primary protons"}; // Lambda Kinematic Cuts (from strange9) Configurable lambdaV0MinPt{"lambdaV0MinPt", 0.60f, "Min pT for Lambdas"}; @@ -263,43 +288,24 @@ struct LambdaProtonBalanceFunction { Configurable cV0DauMinPt{"cV0DauMinPt", 0.1f, "Daughter pT minimum"}; Configurable cV0DauMaxEta{"cV0DauMaxEta", 0.8f, "Daughter |eta| cut"}; Configurable cV0DauMinTpcCrossedRows{"cV0DauMinTpcCrossedRows", 70, "Daughter TPC min crossed rows"}; - // Rapidity-specific constants matched to pProtonMaxY's default value (0.5). - // Note: Since these are compile-time constexpr, the axis will NOT auto-update - // if pProtonMaxY is changed via configurable at runtime. The axis just needs to - // stay >= the cut, not exactly equal. - - // Eta - static constexpr int kRhoEtaBins = 40; - static constexpr float kRhoMin = -0.5f; - static constexpr float kRhoMax = 0.5f; - - // Rapidity - static constexpr int kRhoYBins = 10; - static constexpr float kRhoYMin = -0.5f; - static constexpr float kRhoYMax = 0.5f; - - static constexpr int kRhoPhiBins = 72; - static constexpr int kRhoUnrolledBins = kRhoEtaBins * kRhoPhiBins; - static constexpr int kRhoUnrolledBinsY = kRhoYBins * kRhoPhiBins; + // ── Rho1/Rho2 grid — configurable at runtime (Hyperloop-friendly) ──────── + // These replace the former static constexpr k* constants. Default values + // reproduce the original hardcoded grid (y: 10×72). + Configurable cRhoPhiBins{"cRhoPhiBins", 36, "Number of phi bins for rho1/rho2"}; + Configurable cRhoYBins{"cRhoYBins", 12, "Number of y bins for rho1/rho2"}; + Configurable cRhoYMin{"cRhoYMin", -0.5f, "Min y for rho1/rho2"}; + Configurable cRhoYMax{"cRhoYMax", 0.5f, "Max y for rho1/rho2"}; + // ───────────────────────────────────────────────────────────────────────── void buildPtBins() { - ptEdges.clear(); + state.ptEdges.clear(); static constexpr int kNPtEdges = 16; // 0.6, 0.8, ..., 3.6 GeV/c (15 bins) static constexpr float kPtEdgeMin = 0.6f; static constexpr float kPtEdgeStep = 0.2f; for (int i = 0; i < kNPtEdges; ++i) { - ptEdges.push_back(kPtEdgeMin + kPtEdgeStep * static_cast(i)); - } - } - - int etaBinIndex(float eta) const - { - if (eta < kRhoMin || eta >= kRhoMax) { - return -1; + state.ptEdges.push_back(kPtEdgeMin + kPtEdgeStep * static_cast(i)); } - const float binWidth = (kRhoMax - kRhoMin) / kRhoEtaBins; - return static_cast((eta - kRhoMin) / binWidth); } int phiBinIndex(float phi) const @@ -309,27 +315,17 @@ struct LambdaProtonBalanceFunction { if (phi < phiMin || phi >= phiMax) { return -1; } - const float binWidth = (phiMax - phiMin) / kRhoPhiBins; + const float binWidth = (phiMax - phiMin) / state.mRhoPhiBins; return static_cast((phi - phiMin) / binWidth); } - int unrolledIndex(float eta, float phi) const - { - const int iEta = etaBinIndex(eta); - const int iPhi = phiBinIndex(phi); - if (iEta < 0 || iPhi < 0) { - return -1; - } - return iEta * kRhoPhiBins + iPhi; - } - int yBinIndex(float y) const { - if (y < kRhoYMin || y >= kRhoYMax) { + if (y < state.mRhoYMin || y >= state.mRhoYMax) { return -1; } - const float binWidth = (kRhoYMax - kRhoYMin) / kRhoYBins; - return static_cast((y - kRhoYMin) / binWidth); + const float binWidth = (state.mRhoYMax - state.mRhoYMin) / state.mRhoYBins; + return static_cast((y - state.mRhoYMin) / binWidth); } int unrolledIndexY(float y, float phi) const @@ -339,27 +335,19 @@ struct LambdaProtonBalanceFunction { if (iY < 0 || iPhi < 0) { return -1; } - return iY * kRhoPhiBins + iPhi; + return iY * state.mRhoPhiBins + iPhi; } // Convenience wrappers with particle-specific masses static constexpr float kMassProton = o2::constants::physics::MassProton; static constexpr float kMassLambda = o2::constants::physics::MassLambda; - // Femtoscopic q_inv cut: pairs with q_inv <= kQinvCutLP are rejected from all rho2 histograms (pp, pAp, App, ApAp, LP-family, LL-family): - static constexpr float kQinvCutLP = 0.01f; - template float protonRapidity(T const& track) const { return RecoDecay::y(std::array{track.px(), track.py(), track.pz()}, kMassProton); } - float displayDeltaPhi(float dPhi) const - { - return RecoDecay::constrainAngle(dPhi, -o2::constants::math::PI / 2.0f); - } - // k* = |p*| of one particle in the pair rest frame, from four-momenta. float computeKstar(float px1, float py1, float pz1, float m1, float px2, float py2, float pz2, float m2) const @@ -401,14 +389,44 @@ struct LambdaProtonBalanceFunction { // true => pair is rejected by the q_inv cut (never true when the cut is switched off) bool failsQinvCut(float qinv) const { - return cUseQinvCut.value && (qinv <= kQinvCutLP); + return cUseQinvCut.value && (qinv <= cQinvCutValue.value); + } + + // QA-only: true => pair would survive the q_inv cut, regardless of the cUseQinvCut master switch. + // Used for ALL "After_qinvCut" QA histograms (PP, pAp, App, ApAp, LP-family, LL-family) so they always show the mathematical effect of the cut. + // Physics logic must keep using failsQinvCut(). + bool passesQinvQA(float qinv) const { return qinv > cQinvCutValue.value; } + + // Returns the active centrality estimator's percentile for this collision, + // selected at runtime via cUseFT0C (false = FT0M, true = FT0C). + template + float getCentrality(TCollision const& col) const + { + return cUseFT0C.value ? col.centFT0C() : col.centFT0M(); + } + + // Extra event selection switches (all default false = no extra cut). + // sel8 and |posZ| are NOT handled here: they stay as the Filters of processData and as the explicit check in processMCEfficiency. + template + bool passesExtraEventSelection(TCollision const& col) const + { + if (cPileupReject.value && !col.selection_bit(aod::evsel::kNoSameBunchPileup)) { + return false; + } + if (cZVtxTimeDiff.value && !col.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + if (cIsGoodITSLayers.value && !col.selection_bit(aod::evsel::kIsGoodITSLayersAll)) { + return false; + } + return true; } // Returns index of centrality bin for cent, or -1 if outside all bins. int centBinIndex(float cent) const { - for (size_t i = 0; i + 1 < centEdgesLocal.size(); ++i) { - if (cent >= centEdgesLocal[i] && cent < centEdgesLocal[i + 1]) { + for (size_t i = 0; i + 1 < state.centEdgesLocal.size(); ++i) { + if (cent >= state.centEdgesLocal[i] && cent < state.centEdgesLocal[i + 1]) { return static_cast(i); } } @@ -416,11 +434,22 @@ struct LambdaProtonBalanceFunction { } // Fill helper: fills h (if valid) at (a,b) only when both indices are in range. - static void fillIdx(std::shared_ptr const& h, int a, int b) + static void fillIdx(std::shared_ptr const& h, int a, int b, float w = 1.f) { if (h && a >= 0 && b >= 0) { - h->Fill(a, b); + h->Fill(a + 0.5f, b + 0.5f, w); + } + } + + // Efficiency weight w = 1/eff from the TH3 map (x=cent, y=pt, z=y). 1 when correction is off. + // (TH3* non-const: TH1::FindBin is a non-const method.) + float getEffWeight(TH3* h, float cent, float pt, float y) const + { + if (!cApplyEffCorr.value || !h) { + return 1.f; } + const float v = h->GetBinContent(h->FindBin(cent, pt, y)); + return v > 0.f ? v : 0.f; // 0 means: particle is dropped (no efficiency info) } // ───────────────────────────────────────────────────────────────────── @@ -428,13 +457,13 @@ struct LambdaProtonBalanceFunction { template bool passesPrimProtonPid(TTrack const& trk) const { - const float pTPC = trk.tpcInnerParam(); // NOTE: pTPC, not trk.p() — matches reference exactly + const float pt = trk.pt(); const float nsPr = trk.tpcNSigmaPr(); if (std::abs(nsPr) >= pProtonTPCNsigma.value) { return false; } - if (pTPC < pProtonTPCTOFSwitchP.value) { + if (pt < pProtonTPCTOFSwitchPt.value) { return true; } if (!trk.hasTOF()) { @@ -442,6 +471,60 @@ struct LambdaProtonBalanceFunction { } return std::abs(trk.tofNSigmaPr()) < pProtonTOFNsigma.value; } + + // Bitmask of contaminant hypotheses that REJECT the track (bit0 = pi, bit1 = K, bit2 = e). + // A hypothesis rejects only if the track is inside its veto window AND closer to it than to the proton. + // Detector: TOF at/above the TPC/TOF switch pT when TOF is available, TPC otherwise. + template + int contaminantVetoMask(TTrack const& trk, bool& usedTOF) const + { + usedTOF = cVetoUseTOF.value && trk.hasTOF() && trk.pt() >= pProtonTPCTOFSwitchPt.value; + const float nsP = std::abs(usedTOF ? trk.tofNSigmaPr() : trk.tpcNSigmaPr()); + const float nsPi = std::abs(usedTOF ? trk.tofNSigmaPi() : trk.tpcNSigmaPi()); + const float nsK = std::abs(usedTOF ? trk.tofNSigmaKa() : trk.tpcNSigmaKa()); + const float nsE = std::abs(usedTOF ? trk.tofNSigmaEl() : trk.tpcNSigmaEl()); + int mask = 0; + if (nsPi < pProtonVetoPionNsigma.value && nsPi < nsP) { + mask |= 1; + } + if (nsK < pProtonVetoKaonNsigma.value && nsK < nsP) { + mask |= 2; + } + if (nsE < pProtonVetoElectronNsigma.value && nsE < nsP) { + mask |= 4; + } + return mask; + } + + // true => track survives the pi/K/e contaminant veto + template + bool passesContaminantVeto(TTrack const& trk) const + { + if (!cUseContaminantVeto.value) { + return true; + } + bool usedTOF = false; + return contaminantVetoMask(trk, usedTOF) == 0; + } + + // 2D cutflow vs pT only (used directly for the veto-reason bins 16-21) + template + void fillCutFlow2D(float stage, TTrack const& trk) + { + if (trk.sign() > 0) { + registryQaDetector.fill(HIST("Selection/TOF_Matching/h2f_CutFlow_vs_pt_Proton"), trk.pt(), stage); + } else { + registryQaDetector.fill(HIST("Selection/TOF_Matching/h2f_CutFlow_vs_pt_AntiProton"), trk.pt(), stage); + } + } + + // 1D hCutFlow + 2D cutflow (sequential stages 1-15) + template + void fillCutFlow(float stage, TTrack const& trk) + { + registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), stage); + fillCutFlow2D(stage, trk); + } // ───────────────────────────────────────────────────────────────────── // ══════════════════════════════════════════════════════════════════════════ @@ -452,7 +535,7 @@ struct LambdaProtonBalanceFunction { enum class ProtonQAStage : int { RawAfterTrackSel = 0, ///< after track-quality selection (TrackSelection table), before DCA cuts and PID TpcPid = 1, ///< after DCA + TPC PID cut passes (all momenta) - TpcTofPid = 2, ///< after DCA + full PID: TPC everywhere, plus TOF required only above pProtonTPCTOFSwitchP + TpcTofPid = 2, ///< after DCA + full PID: TPC everywhere, plus TOF required only above pProtonTPCTOFSwitchPt FinalSelected = 3 ///< entered selectedPrimProtons / selectedPrimAntiProtons }; @@ -707,6 +790,8 @@ struct LambdaProtonBalanceFunction { const float pt = trk.pt(); const float beta = trk.beta(); const float nsTOF = trk.tofNSigmaPr(); + const float nsTPC = trk.tpcNSigmaPr(); + const float radius = std::sqrt(nsTPC * nsTPC + nsTOF * nsTOF); if (stage == ProtonQAStage::RawAfterTrackSel) { if constexpr (IsProton) { registryQaDetector.fill(HIST("Proton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_beta_vs_p"), p, beta); @@ -717,6 +802,8 @@ struct LambdaProtonBalanceFunction { registryQaDetector.fill(HIST("Proton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_nsigmaPr_vs_pTPC"), pTPC, nsTOF); registryQaDetector.fill(HIST("Proton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_nsigmaPr_vs_pt"), pt, nsTOF); registryQaDetector.fill(HIST("Proton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_nsigmaPr_vs_pTOF"), pTOF, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_nsigmaTPC_vs_nsigmaTOF"), nsTPC, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/01_RAW_AfterTrackSel/h1f_circular_radius"), radius); } else { registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_beta_vs_p"), p, beta); registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_beta_vs_pTPC"), pTPC, beta); @@ -726,6 +813,8 @@ struct LambdaProtonBalanceFunction { registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_nsigmaPr_vs_pTPC"), pTPC, nsTOF); registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_nsigmaPr_vs_pt"), pt, nsTOF); registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_nsigmaPr_vs_pTOF"), pTOF, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_nsigmaTPC_vs_nsigmaTOF"), nsTPC, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/01_RAW_AfterTrackSel/h1f_circular_radius"), radius); } } else if (stage == ProtonQAStage::TpcPid) { if constexpr (IsProton) { @@ -737,6 +826,8 @@ struct LambdaProtonBalanceFunction { registryQaDetector.fill(HIST("Proton/04_TOF_QA/02_TPC_PID/h2f_nsigmaPr_vs_pTPC"), pTPC, nsTOF); registryQaDetector.fill(HIST("Proton/04_TOF_QA/02_TPC_PID/h2f_nsigmaPr_vs_pt"), pt, nsTOF); registryQaDetector.fill(HIST("Proton/04_TOF_QA/02_TPC_PID/h2f_nsigmaPr_vs_pTOF"), pTOF, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/02_TPC_PID/h2f_nsigmaTPC_vs_nsigmaTOF"), nsTPC, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/02_TPC_PID/h1f_circular_radius"), radius); } else { registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/02_TPC_PID/h2f_beta_vs_p"), p, beta); registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/02_TPC_PID/h2f_beta_vs_pTPC"), pTPC, beta); @@ -746,6 +837,8 @@ struct LambdaProtonBalanceFunction { registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/02_TPC_PID/h2f_nsigmaPr_vs_pTPC"), pTPC, nsTOF); registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/02_TPC_PID/h2f_nsigmaPr_vs_pt"), pt, nsTOF); registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/02_TPC_PID/h2f_nsigmaPr_vs_pTOF"), pTOF, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/02_TPC_PID/h2f_nsigmaTPC_vs_nsigmaTOF"), nsTPC, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/02_TPC_PID/h1f_circular_radius"), radius); } } else if (stage == ProtonQAStage::TpcTofPid) { if constexpr (IsProton) { @@ -757,6 +850,8 @@ struct LambdaProtonBalanceFunction { registryQaDetector.fill(HIST("Proton/04_TOF_QA/03_TPC_TOF_PID/h2f_nsigmaPr_vs_pTPC"), pTPC, nsTOF); registryQaDetector.fill(HIST("Proton/04_TOF_QA/03_TPC_TOF_PID/h2f_nsigmaPr_vs_pt"), pt, nsTOF); registryQaDetector.fill(HIST("Proton/04_TOF_QA/03_TPC_TOF_PID/h2f_nsigmaPr_vs_pTOF"), pTOF, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/03_TPC_TOF_PID/h2f_nsigmaTPC_vs_nsigmaTOF"), nsTPC, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/03_TPC_TOF_PID/h1f_circular_radius"), radius); } else { registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/03_TPC_TOF_PID/h2f_beta_vs_p"), p, beta); registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/03_TPC_TOF_PID/h2f_beta_vs_pTPC"), pTPC, beta); @@ -766,6 +861,8 @@ struct LambdaProtonBalanceFunction { registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/03_TPC_TOF_PID/h2f_nsigmaPr_vs_pTPC"), pTPC, nsTOF); registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/03_TPC_TOF_PID/h2f_nsigmaPr_vs_pt"), pt, nsTOF); registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/03_TPC_TOF_PID/h2f_nsigmaPr_vs_pTOF"), pTOF, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/03_TPC_TOF_PID/h2f_nsigmaTPC_vs_nsigmaTOF"), nsTPC, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/03_TPC_TOF_PID/h1f_circular_radius"), radius); } } else { // FinalSelected if constexpr (IsProton) { @@ -777,6 +874,8 @@ struct LambdaProtonBalanceFunction { registryQaDetector.fill(HIST("Proton/04_TOF_QA/04_Final_SelectedProton/h2f_nsigmaPr_vs_pTPC"), pTPC, nsTOF); registryQaDetector.fill(HIST("Proton/04_TOF_QA/04_Final_SelectedProton/h2f_nsigmaPr_vs_pt"), pt, nsTOF); registryQaDetector.fill(HIST("Proton/04_TOF_QA/04_Final_SelectedProton/h2f_nsigmaPr_vs_pTOF"), pTOF, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/04_Final_SelectedProton/h2f_nsigmaTPC_vs_nsigmaTOF"), nsTPC, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/04_Final_SelectedProton/h1f_circular_radius"), radius); } else { registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/04_Final_SelectedProton/h2f_beta_vs_p"), p, beta); registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/04_Final_SelectedProton/h2f_beta_vs_pTPC"), pTPC, beta); @@ -786,6 +885,8 @@ struct LambdaProtonBalanceFunction { registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/04_Final_SelectedProton/h2f_nsigmaPr_vs_pTPC"), pTPC, nsTOF); registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/04_Final_SelectedProton/h2f_nsigmaPr_vs_pt"), pt, nsTOF); registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/04_Final_SelectedProton/h2f_nsigmaPr_vs_pTOF"), pTOF, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/04_Final_SelectedProton/h2f_nsigmaTPC_vs_nsigmaTOF"), nsTPC, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/04_Final_SelectedProton/h1f_circular_radius"), radius); } } } @@ -1128,11 +1229,10 @@ struct LambdaProtonBalanceFunction { // Per-particle cache used by the pair loops (avoids recomputing per pair). struct PartInfo { - int64_t gid; - float px, py, pz, E; - float eta, phi, y, pt; - int idxEta; - int idxY; + int64_t gid = 0; + float px = 0.f, py = 0.f, pz = 0.f, E = 0.f; + float phi = 0.f, y = 0.f, pt = 0.f; + int idxY = -1; }; template @@ -1144,17 +1244,76 @@ struct LambdaProtonBalanceFunction { p.py = t.py(); p.pz = t.pz(); p.E = std::sqrt(p.px * p.px + p.py * p.py + p.pz * p.pz + kMassProton * kMassProton); - p.eta = t.eta(); p.phi = t.phi(); p.pt = t.pt(); p.y = protonRapidity(t); - p.idxEta = unrolledIndex(p.eta, p.phi); p.idxY = unrolledIndexY(p.y, p.phi); return p; } + template + PartInfo makeMcInfo(TMc const& mc, float mass) const + { + PartInfo p; + p.gid = mc.globalIndex(); + p.px = mc.px(); + p.py = mc.py(); + p.pz = mc.pz(); + p.E = std::sqrt(p.px * p.px + p.py * p.py + p.pz * p.pz + mass * mass); + p.pt = mc.pt(); + p.y = RecoDecay::y(std::array{p.px, p.py, p.pz}, mass); + p.phi = mc.phi(); + // Keep the reco [0, 2pi) convention even if an input truth phi needs wrapping. + if (p.phi < 0.f || p.phi >= o2::constants::math::TwoPI) { + p.phi = RecoDecay::constrainAngle(p.phi, 0.f); + } + p.idxY = unrolledIndexY(p.y, p.phi); + return p; + } + + // One ordered-pair traversal for all twelve generated channels. The returned + // count follows reco: after qinv selection, before the rho-grid validity check. + int64_t fillGeneratedPairs(std::vector const& listA, + std::vector const& listB, bool sameSpecies, + std::shared_ptr const& integrated, + std::shared_ptr const& centrality) const + { + int64_t count = 0; + for (auto const& a : listA) { + for (auto const& b : listB) { + if (sameSpecies && a.gid == b.gid) { + continue; + } + if (failsQinvCut(computeQinv(a.px, a.py, a.pz, a.E, b.px, b.py, b.pz, b.E))) { + continue; + } + ++count; + // Generated particles always have unit weight, with no efficiency lookup. + fillIdx(integrated, a.idxY, b.idxY, 1.f); + fillIdx(centrality, a.idxY, b.idxY, 1.f); + } + } + return count; + } + void init(InitContext const&) { + if (static_cast(doprocessData.value) + static_cast(doprocessMCRecBF.value) + + static_cast(doprocessMCGenBF.value) > + 1) { + LOGF(fatal, "BF modes share histograms: enable only one of processData, processMCRecBF, processMCGenBF. processData defaults to true; explicitly set processData=false for matched-event BF runs."); + } + if (doprocessMCGenBF.value && cApplyEffCorr.value) { + LOGF(fatal, "processMCGenBF requires cApplyEffCorr=false: generated weights must be exactly one."); + } + // ── Populate rho1/rho2 grid members from Configurables ────────────────── + state.mRhoPhiBins = cRhoPhiBins.value; + state.mRhoYBins = cRhoYBins.value; + state.mRhoYMin = cRhoYMin.value; + state.mRhoYMax = cRhoYMax.value; + state.mRhoUnrolledBinsY = state.mRhoYBins * state.mRhoPhiBins; + // ──────────────────────────────────────────────────────────────────────── + buildPtBins(); // ── Common axis definitions ────────────────────────────────────────── @@ -1175,13 +1334,13 @@ struct LambdaProtonBalanceFunction { AxisSpec dcaAxis = {100, -1.0f, 1.0f, "DCAxy (cm)"}; AxisSpec dcaZAxis = {100, -1.0f, 1.0f, "DCAz (cm)"}; AxisSpec dcaAxisWide = {200, 0.0f, 5.0f, "DCA (cm)"}; - AxisSpec multAxis = {100, 0.0f, 100.0f, "FT0M Percentile (%)"}; - AxisSpec etaAxis = {kRhoEtaBins, kRhoMin, kRhoMax, "#eta"}; - AxisSpec phiAxis = {kRhoPhiBins, 0.0f, o2::constants::math::TwoPI, "#varphi"}; - AxisSpec unrolledAxis = {kRhoUnrolledBins, 0.0f, static_cast(kRhoUnrolledBins), "index(#eta,#varphi)"}; - AxisSpec unrolledAxisY = {kRhoUnrolledBinsY, 0.0f, static_cast(kRhoUnrolledBinsY), "index(y,#varphi)"}; - AxisSpec rapidityAxis = {kRhoYBins, kRhoYMin, kRhoYMax, "y"}; - AxisSpec yAxis = {kRhoYBins, kRhoYMin, kRhoYMax, "y"}; + const std::string centLabel = cUseFT0C.value ? "FT0C" : "FT0M"; + const std::string centAxisTitle = centLabel + " Percentile (%)"; + AxisSpec multAxis = {100, 0.0f, 100.0f, centAxisTitle}; + AxisSpec phiAxis = {state.mRhoPhiBins, 0.0f, o2::constants::math::TwoPI, "#varphi"}; + AxisSpec unrolledAxisY = {state.mRhoUnrolledBinsY, 0.0f, static_cast(state.mRhoUnrolledBinsY), "index(y,#varphi)"}; + AxisSpec rapidityAxis = {state.mRhoYBins, state.mRhoYMin, state.mRhoYMax, "y"}; + AxisSpec yAxis = {state.mRhoYBins, state.mRhoYMin, state.mRhoYMax, "y"}; // pT axis for ProtonCounts_byPID_Check: 0.15 → 6.0, 118 bins of 0.05 width AxisSpec pidCheckPtAxis = {118, 0.15f, 6.05f, "#it{p}_{T} (GeV/#it{c})"}; @@ -1189,11 +1348,36 @@ struct LambdaProtonBalanceFunction { // ── Other / event-level histograms ─────────────────────────────────── registryOther.add("hVertexZRec", "hVertexZRec", {HistType::kTH1F, {vertexZAxis}}); - registryOther.add("hFT0MPercentile", "hFT0MPercentile", {HistType::kTH1F, {multAxis}}); + registryOther.add("hFT0MPercentile", ("Centrality percentile (" + centLabel + ")").c_str(), {HistType::kTH1F, {multAxis}}); + // ── MC efficiency maps (processMCEfficiency): x = centrality (cCentBins), y = pT, z = y (same range/bins as rho1) ── + { + const AxisSpec axCent(cCentBins, "centrality (%)"); + const AxisSpec axPt{60, 0.0, 6.0, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axY{cRhoYBins.value, cRhoYMin.value, cRhoYMax.value, "y"}; + for (const auto& name : {"GenLambda", "GenAntiLambda", "GenProton", "GenAntiProton", + "RecLambda", "RecAntiLambda", "RecProton", "RecAntiProton"}) { + registryEff.add(name, name, {HistType::kTH3F, {axCent, axPt, axY}}); + } + } + registryOther.add("hEffWeightProton", "Efficiency weight 1/#epsilon, protons", {HistType::kTH1F, {{200, 0.0f, 20.0f}}}); + registryOther.add("hEffWeightLambda", "Efficiency weight 1/#epsilon, #Lambda", {HistType::kTH1F, {{200, 0.0f, 20.0f}}}); + registryOther.add("hEffWeightZeroCount", "Particles with efficiency weight 0 (1=p, 2=#bar{p}, 3=#Lambda, 4=#bar{#Lambda})", {HistType::kTH1F, {{4, 0.5f, 4.5f}}}); registryOther.add("hNSelectedPrimProtons", "Number of selected primary protons per event", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); registryOther.add("hNSelectedPrimAntiProtons", "Number of selected primary antiprotons per event", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); registryOther.add("hNSelLambda", "Number of selected Lambda candidates per event", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); registryOther.add("hNSelAntiLambda", "Number of selected AntiLambda candidates per event", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); + registryOther.add("hN_Lambda_reco1", "Stage 1: selected reconstructed Lambda per event (unweighted);N;Events", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); + registryOther.add("hN_AntiLambda_reco1", "Stage 1: selected reconstructed AntiLambda per event (unweighted);N;Events", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); + registryOther.add("hN_Proton_reco1", "Stage 1: selected reconstructed Proton per event (unweighted);N;Events", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); + registryOther.add("hN_AntiProton_reco1", "Stage 1: selected reconstructed AntiProton per event (unweighted);N;Events", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); + registryOther.add("hN_Lambda_PrimSec_reco", "Stage 2: primary and secondary truth-matched Lambda per event (unweighted);N;Events", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); + registryOther.add("hN_AntiLambda_PrimSec_reco", "Stage 2: primary and secondary truth-matched AntiLambda per event (unweighted);N;Events", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); + registryOther.add("hN_Proton_PrimSec_reco", "Stage 2: primary and secondary truth-matched Proton per event (unweighted);N;Events", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); + registryOther.add("hN_AntiProton_PrimSec_reco", "Stage 2: primary and secondary truth-matched AntiProton per event (unweighted);N;Events", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); + registryOther.add("hN_Lambda_prim_reco", "Stage 3: physical-primary truth-matched Lambda per event (unweighted);N;Events", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); + registryOther.add("hN_AntiLambda_prim_reco", "Stage 3: physical-primary truth-matched AntiLambda per event (unweighted);N;Events", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); + registryOther.add("hN_Proton_prim_reco", "Stage 3: physical-primary truth-matched Proton per event (unweighted);N;Events", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); + registryOther.add("hN_AntiProton_prim_reco", "Stage 3: physical-primary truth-matched AntiProton per event (unweighted);N;Events", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); // ── V0-daughter veto QA counters ───────────────────────────────────── // Proton veto counters registryOther.add("hNPrimProtonBeforeVeto", "Proton candidates before V0-daughter veto;N;Events", {HistType::kTH1F, {{200, 0.0f, 200.0f}}}); @@ -1213,6 +1397,27 @@ struct LambdaProtonBalanceFunction { registryOther.add("hNPairs_AntiLambda_PrimProton", "#bar{#Lambda}-p pairs per event (after q_{inv} cut);N_{pairs};Events", {HistType::kTH1F, {{200, 0.0f, 200.0f}}}); registryOther.add("hNPairs_AntiLambda_PrimAntiProton", "#bar{#Lambda}-#bar{p} pairs per event (after q_{inv} cut);N_{pairs};Events", {HistType::kTH1F, {{200, 0.0f, 200.0f}}}); + // ── Shared-daughter (union) cleaning diagnostics for Lambda / AntiLambda ── + registryOther.add("h1f_nLambda_beforeClean", "N_{#Lambda} per event before shared-daughter cleaning;N;Events", {HistType::kTH1F, {{100, -0.5f, 99.5f}}}); + registryOther.add("h1f_nLambda_rejectedClean", "N_{#Lambda} rejected by shared-daughter cleaning per event;N;Events", {HistType::kTH1F, {{100, -0.5f, 99.5f}}}); + registryOther.add("h1f_nLambda_afterClean", "N_{#Lambda} per event after shared-daughter cleaning;N;Events", {HistType::kTH1F, {{100, -0.5f, 99.5f}}}); + registryOther.add("h1f_nAntiLambda_beforeClean", "N_{#bar{#Lambda}} per event before shared-daughter cleaning;N;Events", {HistType::kTH1F, {{100, -0.5f, 99.5f}}}); + registryOther.add("h1f_nAntiLambda_rejectedClean", "N_{#bar{#Lambda}} rejected by shared-daughter cleaning per event;N;Events", {HistType::kTH1F, {{100, -0.5f, 99.5f}}}); + registryOther.add("h1f_nAntiLambda_afterClean", "N_{#bar{#Lambda}} per event after shared-daughter cleaning;N;Events", {HistType::kTH1F, {{100, -0.5f, 99.5f}}}); + registryOther.add("hSharedDauCleaningSums", "Shared-daughter cleaning: sums over events;Quantity;Sum", {HistType::kTH1D, {{9, 0.5f, 9.5f}}}); + { + auto hSD = registryOther.get(HIST("hSharedDauCleaningSums")); + hSD->GetXaxis()->SetBinLabel(1, "Sum N_L raw"); + hSD->GetXaxis()->SetBinLabel(2, "Sum N_L rej"); + hSD->GetXaxis()->SetBinLabel(3, "Sum N_L clean"); + hSD->GetXaxis()->SetBinLabel(4, "Sum N_AL raw"); + hSD->GetXaxis()->SetBinLabel(5, "Sum N_AL rej"); + hSD->GetXaxis()->SetBinLabel(6, "Sum N_AL clean"); + hSD->GetXaxis()->SetBinLabel(7, "Sum N_L*(N_L-1)"); + hSD->GetXaxis()->SetBinLabel(8, "Sum N_AL*(N_AL-1)"); + hSD->GetXaxis()->SetBinLabel(9, "Sum N_L*N_AL"); + } + // ══════════════════════════════════════════════════════════════════════════ // NEW STAGED QA_Detec REGISTRATION // ══════════════════════════════════════════════════════════════════════════ @@ -1223,7 +1428,7 @@ struct LambdaProtonBalanceFunction { AxisSpec axisCosPA_zoom = {200, 0.99f, 1.0f, "Cos(PA)"}; AxisSpec axisDecayR = {100, 0.0f, 50.0f, "cm"}; AxisSpec axisCtau = {100, 0.0f, 50.0f, "ctau (cm)"}; - AxisSpec axisCutFlow = {14, 0.5f, 14.5f, "Cut stage"}; + AxisSpec axisCutFlow = {15, 0.5f, 15.5f, "Cut stage"}; auto addProtonQAForStage = [&](const char* species, const char* stage) { // Kinematics @@ -1260,6 +1465,14 @@ struct LambdaProtonBalanceFunction { registryQaDetector.add(Form("%s/04_TOF_QA/%s/h2f_nsigmaPr_vs_pTPC", species, stage), "TOF n#sigma_{p} vs p_{TPC};p_{TPC} (GeV/c);n#sigma", {HistType::kTH2F, {axisPTPC, nsigmaAxis}}); registryQaDetector.add(Form("%s/04_TOF_QA/%s/h2f_nsigmaPr_vs_pt", species, stage), "TOF n#sigma_{p} vs p_{T};p_{T} (GeV/c);n#sigma", {HistType::kTH2F, {axisPtProton, nsigmaAxis}}); registryQaDetector.add(Form("%s/04_TOF_QA/%s/h2f_nsigmaPr_vs_pTOF", species, stage), "TOF n#sigma_{p} vs p_{TOF};p_{TOF} (GeV/c);n#sigma", {HistType::kTH2F, {axisPTOF, nsigmaAxis}}); + + // --- 2D TPC vs TOF n-sigma and 1D circular radius (competitive-veto vs circle comparison) --- + registryQaDetector.add(Form("%s/04_TOF_QA/%s/h2f_nsigmaTPC_vs_nsigmaTOF", species, stage), + "TPC vs TOF n#sigma_{p};n#sigma^{TPC}_{p};n#sigma^{TOF}_{p}", + {HistType::kTH2F, {nsigmaAxis, nsigmaAxis}}); + registryQaDetector.add(Form("%s/04_TOF_QA/%s/h1f_circular_radius", species, stage), + "Circular PID Radius;R = #sqrt{(n#sigma^{TPC}_{p})^{2} + (n#sigma^{TOF}_{p})^{2}};Counts", + {HistType::kTH1F, {{100, 0.0f, 10.0f, "R"}}}); }; static constexpr std::array localProtonStages = { @@ -1376,8 +1589,43 @@ struct LambdaProtonBalanceFunction { hCF->GetXaxis()->SetBinLabel(10, "|y| window"); hCF->GetXaxis()->SetBinLabel(11, "DCA xy,z"); hCF->GetXaxis()->SetBinLabel(12, "PID (TPC/TOF)"); - hCF->GetXaxis()->SetBinLabel(13, "V0-daughter veto"); - hCF->GetXaxis()->SetBinLabel(14, "Selected"); + hCF->GetXaxis()->SetBinLabel(13, "pi/K/e veto"); + hCF->GetXaxis()->SetBinLabel(14, "V0-daughter veto"); + hCF->GetXaxis()->SetBinLabel(15, "Selected"); + } + + // ── V0 (Lambda / AntiLambda) cutflow histograms ───────────────────────── + // Bins 1-3 are filled for every V0 under BOTH hypotheses (PID hypothesis not yet known). + // From bin 4 on, each V0 is counted only in the cutflow of the hypothesis chosen by TPC PID. + // Bin 10 = survivors of shared-daughter cleaning (the sample entering rho1 / rho2). Bin 11 is spare. + registryQaDetector.add("Lambda/hCutFlow", "#Lambda candidate cutflow;Stage;Counts", {HistType::kTH1F, {{11, 0.5f, 11.5f}}}); + registryQaDetector.add("AntiLambda/hCutFlow", "#bar{#Lambda} candidate cutflow;Stage;Counts", {HistType::kTH1F, {{11, 0.5f, 11.5f}}}); + { + const std::array v0CfLabels = {"All V0s", "V0 Type", "Daughter Kinematics", "Unambiguous TPC PID", "Rapidity", + "pT Acceptance", "Topology & PV DCA", "cTau & K0s Rej", "Mass Window", "Shared-Dau Cleaned"}; + auto hCFL = registryQaDetector.get(HIST("Lambda/hCutFlow")); + auto hCFAL = registryQaDetector.get(HIST("AntiLambda/hCutFlow")); + for (int ib = 0; ib < static_cast(v0CfLabels.size()); ++ib) { + hCFL->GetXaxis()->SetBinLabel(ib + 1, v0CfLabels[ib]); + hCFAL->GetXaxis()->SetBinLabel(ib + 1, v0CfLabels[ib]); + } + } + + // 2D cutflow vs pT, separate for protons and antiprotons. + // y bins 1-15 = sequential stages (same as hCutFlow); 16-21 = veto REASONS (not sequential, a track can fill several) + registryQaDetector.add("Selection/TOF_Matching/h2f_CutFlow_vs_pt_Proton", "Proton cutflow vs p_{T};p_{T} (GeV/c);Stage", {HistType::kTH2F, {axisPtProton, {21, 0.5f, 21.5f}}}); + registryQaDetector.add("Selection/TOF_Matching/h2f_CutFlow_vs_pt_AntiProton", "AntiProton cutflow vs p_{T};p_{T} (GeV/c);Stage", {HistType::kTH2F, {axisPtProton, {21, 0.5f, 21.5f}}}); + { + const std::array cfLabels2D = {"All tracks", "hasTPC", "TPC crossed rows", "TPC rows/findable", "TPC chi2", "ITS Ncls", "ITS chi2", + "TPC signal", "p_TPC & pT window", "|y| window", "DCA xy,z", "PID (TPC/TOF)", "pi/K/e veto", + "V0-daughter veto", "Selected", "veto: TPC pi", "veto: TPC K", "veto: TPC e", + "veto: TOF pi", "veto: TOF K", "veto: TOF e"}; + auto hCFp = registryQaDetector.get(HIST("Selection/TOF_Matching/h2f_CutFlow_vs_pt_Proton")); + auto hCFa = registryQaDetector.get(HIST("Selection/TOF_Matching/h2f_CutFlow_vs_pt_AntiProton")); + for (int ib = 0; ib < static_cast(cfLabels2D.size()); ++ib) { + hCFp->GetYaxis()->SetBinLabel(ib + 1, cfLabels2D[ib]); + hCFa->GetYaxis()->SetBinLabel(ib + 1, cfLabels2D[ib]); + } } // ══════════════════════════════════════════════════════════════════════════ @@ -1386,13 +1634,13 @@ struct LambdaProtonBalanceFunction { // -- Event cutflow -- registryOther.add("hEventCutflow", "Event Cutflow;Step;Counts", - {HistType::kTH1F, {{4, 0.5f, 4.5f}}}); + {HistType::kTH1F, {{5, 0.5f, 5.5f}}}); { auto hEC = registryOther.get(HIST("hEventCutflow")); hEC->GetXaxis()->SetBinLabel(1, "sel8 + |z|GetXaxis()->SetBinLabel(2, "numContrib >= 1"); - hEC->GetXaxis()->SetBinLabel(3, "centFT0M < 80"); hEC->GetXaxis()->SetBinLabel(4, "INEL > 0"); + hEC->GetXaxis()->SetBinLabel(5, "extra event selection (pileup/zvtx/ITS flags)"); } // ── ProtonVetoQA histograms ─────────────────────────────────────────── @@ -1423,7 +1671,6 @@ struct LambdaProtonBalanceFunction { "Proton V0-daughter veto QA: fraction_{removed} = N_{removed}/N_{before} per event;fraction_{removed};Events", {HistType::kTH1F, {{110, 0.0f, 1.1f}}}); // ───────────────────────────────────────────────────────────────────── - // ───────────────────────────────────────────────────────────────────── // ── Item 5: Event-level pileup rejection QA ─────────────────────────── // hPileupFlags: each bin corresponds to a different event-selection bit. @@ -1443,10 +1690,10 @@ struct LambdaProtonBalanceFunction { hPU->GetXaxis()->SetBinLabel(6, "NoSBPU&&GoodZvtx&&ITSTPC"); } registryOther.add("h2f_NTracks_vs_Cent", - "N_{tracks}(|#eta|<0.8, all TracksIU, no quality cut) vs FT0M cent;FT0M (%);N_{tracks}(|#eta|<0.8)", + ("N_{tracks}(|#eta|<0.8, all TracksIU, no quality cut) vs " + centLabel + ";" + centLabel + " (%);N_{tracks}(|#eta|<0.8)").c_str(), {HistType::kTH2F, {multAxis, {200, 0.0f, 200.0f, "N_{tracks}"}}}); registryOther.add("h2f_NumContrib_vs_Cent", - "N_{contribs} vs FT0M percentile;FT0M (%);N_{contribs}", + ("N_{contribs} vs " + centLabel + " percentile;" + centLabel + " (%);N_{contribs}").c_str(), {HistType::kTH2F, {multAxis, {200, 0.0f, 200.0f, "N_{contribs}"}}}); // ───────────────────────────────────────────────────────────────────── @@ -1469,11 +1716,6 @@ struct LambdaProtonBalanceFunction { // ── Rho histograms ─────────────────────────────────────────────────── registryRho.add("hEventCounter", "Events", {HistType::kTH1F, {{1, 0.0, 1.0}}}); - // eta-based rho1 - registryRho.add("h2_rho1_Proton", "#rho_{1}(p) in (#eta,#varphi) (after V0-daughter veto)", {HistType::kTH2F, {etaAxis, phiAxis}}); - registryRho.add("h2_rho1_AntiProton", "#rho_{1}(#bar{p}) in (#eta,#varphi) (after V0-daughter veto)", {HistType::kTH2F, {etaAxis, phiAxis}}); - registryRho.add("h2_rho1_Lambda", "#rho_{1}(#Lambda) in (#eta,#varphi)", {HistType::kTH2F, {etaAxis, phiAxis}}); - registryRho.add("h2_rho1_AntiLambda", "#rho_{1}(#bar{#Lambda}) in (#eta,#varphi)", {HistType::kTH2F, {etaAxis, phiAxis}}); // rapidity-based rho1 registryRho.add("h2_rho1_Proton_y", "#rho_{1}(p) in (y,#varphi) (after V0-daughter veto)", {HistType::kTH2F, {yAxis, phiAxis}}); registryRho.add("h2_rho1_AntiProton_y", "#rho_{1}(#bar{p}) in (y,#varphi) (after V0-daughter veto)", {HistType::kTH2F, {yAxis, phiAxis}}); @@ -1615,63 +1857,139 @@ struct LambdaProtonBalanceFunction { // ───────────────────────────────────────────────────────────────────── // ── QA3: SplitTrackQA — q_inv and shared-cluster fraction ───────────── + float qinvMax = cQinvQAMax.value; + int qinvBins = cQinvQABins.value; + constexpr float MinQinvQABinWidth = 0.001f; + if (qinvMax / qinvBins < MinQinvQABinWidth) { + qinvBins = static_cast(qinvMax / MinQinvQABinWidth); + } + // "Before_qinvCut": filled for every accepted pair, prior to the q_inv - // cut (kQinvCutLP), so they show the complete original q_inv distributions. + // cut (kQinvCut), so they show the complete original q_inv distributions. registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_qinv_PP_Before_qinvCut", "Split-track QA: q_{inv} for p-p pairs, before q_{inv} cut;q_{inv} (GeV/c);Counts", - {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_qinv_LP_Before_qinvCut", "Split-track QA: q_{inv} for #Lambda/#bar{#Lambda}-p/#bar{p} pairs (all four LP combos), before q_{inv} cut;q_{inv} (GeV/c);Counts", - {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_qinv_LL_Before_qinvCut", "Split-track QA: q_{inv} for #Lambda/#bar{#Lambda}-#Lambda/#bar{#Lambda} pairs (all four LL combos), before q_{inv} cut;q_{inv} (GeV/c);Counts", - {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); // "After_qinvCut": filled only for pairs surviving the q_inv cut - // (q_inv > kQinvCutLP), applied uniformly to all pair types (PP, pAp, App, ApAp, LP-family, LL-family). + // (q_inv > kQinvCut), applied uniformly to all pair types (PP, pAp, App, ApAp, LP-family, LL-family). registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_qinv_PP_After_qinvCut", "Split-track QA: q_{inv} for p-p pairs, after q_{inv} cut (q_{inv} > 0.01 GeV/c);q_{inv} (GeV/c);Counts", - {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_qinv_LP_After_qinvCut", "Split-track QA: q_{inv} for #Lambda/#bar{#Lambda}-p/#bar{p} pairs (all four LP combos), after q_{inv} cut (q_{inv} > 0.01 GeV/c);q_{inv} (GeV/c);Counts", - {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_qinv_LL_After_qinvCut", "Split-track QA: q_{inv} for #Lambda/#bar{#Lambda}-#Lambda/#bar{#Lambda} pairs (all four LL combos), after q_{inv} cut (q_{inv} > 0.01 GeV/c);q_{inv} (GeV/c);Counts", - {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); // q_inv Before/After QA for p-pbar, pbar-p and pbar-pbar pairs registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_qinv_pAp_Before_qinvCut", "Split-track QA: q_{inv} for p-#bar{p} pairs, before q_{inv} cut;q_{inv} (GeV/c);Counts", - {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_qinv_pAp_After_qinvCut", "Split-track QA: q_{inv} for p-#bar{p} pairs, after q_{inv} cut (q_{inv} > 0.01 GeV/c);q_{inv} (GeV/c);Counts", - {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_qinv_App_Before_qinvCut", "Split-track QA: q_{inv} for #bar{p}-p pairs, before q_{inv} cut;q_{inv} (GeV/c);Counts", - {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_qinv_App_After_qinvCut", "Split-track QA: q_{inv} for #bar{p}-p pairs, after q_{inv} cut (q_{inv} > 0.01 GeV/c);q_{inv} (GeV/c);Counts", - {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_qinv_ApAp_Before_qinvCut", "Split-track QA: q_{inv} for #bar{p}-#bar{p} pairs, before q_{inv} cut;q_{inv} (GeV/c);Counts", - {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_qinv_ApAp_After_qinvCut", "Split-track QA: q_{inv} for #bar{p}-#bar{p} pairs, after q_{inv} cut (q_{inv} > 0.01 GeV/c);q_{inv} (GeV/c);Counts", - {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); - registryCorrelationQA.add("QA3/Kstar/h1f_kstar_Lp", "k* of #Lambda-p pairs (after q_{inv} cut);k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); - registryCorrelationQA.add("QA3/Kstar/h1f_kstar_LAp", "k* of #Lambda-#bar{p} pairs (after q_{inv} cut);k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); - registryCorrelationQA.add("QA3/Kstar/h1f_kstar_ALp", "k* of #bar{#Lambda}-p pairs (after q_{inv} cut);k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); - registryCorrelationQA.add("QA3/Kstar/h1f_kstar_ALAp", "k* of #bar{#Lambda}-#bar{p} pairs (after q_{inv} cut);k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); + registryCorrelationQA.add("QA3/Kstar/h1f_kstar_Lp_BeforeQinvCut", "k* of #Lambda-p pairs before q_{inv} cut;k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); + registryCorrelationQA.add("QA3/Kstar/h1f_kstar_Lp", "k* of #Lambda-p pairs after q_{inv} cut;k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); + registryCorrelationQA.add("QA3/Kstar/h1f_kstar_LAp_BeforeQinvCut", "k* of #Lambda-#bar{p} pairs before q_{inv} cut;k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); + registryCorrelationQA.add("QA3/Kstar/h1f_kstar_LAp", "k* of #Lambda-#bar{p} pairs after q_{inv} cut;k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); + registryCorrelationQA.add("QA3/Kstar/h1f_kstar_ALp_BeforeQinvCut", "k* of #bar{#Lambda}-p pairs before q_{inv} cut;k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); + registryCorrelationQA.add("QA3/Kstar/h1f_kstar_ALp", "k* of #bar{#Lambda}-p pairs after q_{inv} cut;k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); + registryCorrelationQA.add("QA3/Kstar/h1f_kstar_ALAp_BeforeQinvCut", "k* of #bar{#Lambda}-#bar{p} pairs before q_{inv} cut;k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); + registryCorrelationQA.add("QA3/Kstar/h1f_kstar_ALAp", "k* of #bar{#Lambda}-#bar{p} pairs after q_{inv} cut;k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); + + // ── QA3: 3D q_inv vs ΔΦ vs Δη and q_inv vs ΔΦ vs ΔY ───────────────── + // Filled BEFORE the q_inv cut so the full distribution is visible. + // Nomenclature: "Lp" = Lambda(V0) vs primary proton + // "Lp_dau" = Lambda daughter proton vs primary proton + // "LAp"/"LAp_dau" = Lambda (V0/dau) vs primary antiproton + // "ALp"/"ALp_dau" = AntiLambda (V0/dau) vs primary proton + // "ALAp"/"ALAp_dau" = AntiLambda (V0/dau) vs primary antiproton + { + AxisSpec axisQinv3D = {100, 0.0f, 2.0f, "q_{inv} (GeV/c)"}; + AxisSpec axisDPhi3D = {64, -o2::constants::math::PI / 2.0f, 3.0f * o2::constants::math::PI / 2.0f, "#Delta#varphi"}; + AxisSpec axisDEta3D = {40, -2.0f, 2.0f, "#Delta#eta"}; + AxisSpec axisDY3D = {40, -2.0f, 2.0f, "#Deltay"}; + // ── eta space ───────────────────────────────────────────────────── + registryCorrelationQA.add("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_Lp", + "q_{inv} vs #Delta#varphi vs #Delta#eta: #Lambda (V0) - p;q_{inv} (GeV/c);#Delta#varphi;#Delta#eta", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDEta3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_Lp_dau", + "q_{inv} vs #Delta#varphi vs #Delta#eta: #Lambda dau p - p;q_{inv} (GeV/c);#Delta#varphi;#Delta#eta", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDEta3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_LAp", + "q_{inv} vs #Delta#varphi vs #Delta#eta: #Lambda (V0) - #bar{p};q_{inv} (GeV/c);#Delta#varphi;#Delta#eta", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDEta3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_LAp_dau", + "q_{inv} vs #Delta#varphi vs #Delta#eta: #Lambda dau p - #bar{p};q_{inv} (GeV/c);#Delta#varphi;#Delta#eta", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDEta3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_ALp", + "q_{inv} vs #Delta#varphi vs #Delta#eta: #bar{#Lambda} (V0) - p;q_{inv} (GeV/c);#Delta#varphi;#Delta#eta", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDEta3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_ALp_dau", + "q_{inv} vs #Delta#varphi vs #Delta#eta: #bar{#Lambda} dau #bar{p} - p;q_{inv} (GeV/c);#Delta#varphi;#Delta#eta", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDEta3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_ALAp", + "q_{inv} vs #Delta#varphi vs #Delta#eta: #bar{#Lambda} (V0) - #bar{p};q_{inv} (GeV/c);#Delta#varphi;#Delta#eta", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDEta3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_ALAp_dau", + "q_{inv} vs #Delta#varphi vs #Delta#eta: #bar{#Lambda} dau #bar{p} - #bar{p};q_{inv} (GeV/c);#Delta#varphi;#Delta#eta", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDEta3D}}); + // ── y space ─────────────────────────────────────────────────────── + registryCorrelationQA.add("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_Lp", + "q_{inv} vs #Delta#varphi vs #Deltay: #Lambda (V0) - p;q_{inv} (GeV/c);#Delta#varphi;#Deltay", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDY3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_Lp_dau", + "q_{inv} vs #Delta#varphi vs #Deltay: #Lambda dau p - p;q_{inv} (GeV/c);#Delta#varphi;#Deltay", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDY3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_LAp", + "q_{inv} vs #Delta#varphi vs #Deltay: #Lambda (V0) - #bar{p};q_{inv} (GeV/c);#Delta#varphi;#Deltay", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDY3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_LAp_dau", + "q_{inv} vs #Delta#varphi vs #Deltay: #Lambda dau p - #bar{p};q_{inv} (GeV/c);#Delta#varphi;#Deltay", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDY3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_ALp", + "q_{inv} vs #Delta#varphi vs #Deltay: #bar{#Lambda} (V0) - p;q_{inv} (GeV/c);#Delta#varphi;#Deltay", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDY3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_ALp_dau", + "q_{inv} vs #Delta#varphi vs #Deltay: #bar{#Lambda} dau #bar{p} - p;q_{inv} (GeV/c);#Delta#varphi;#Deltay", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDY3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_ALAp", + "q_{inv} vs #Delta#varphi vs #Deltay: #bar{#Lambda} (V0) - #bar{p};q_{inv} (GeV/c);#Delta#varphi;#Deltay", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDY3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_ALAp_dau", + "q_{inv} vs #Delta#varphi vs #Deltay: #bar{#Lambda} dau #bar{p} - #bar{p};q_{inv} (GeV/c);#Delta#varphi;#Deltay", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDY3D}}); + } + // ───────────────────────────────────────────────────────────────────── registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_sharedClsFraction_t1", "Split-track QA: TPC shared-cluster fraction, track 1 (p-p: first proton; #Lambda-p: V0 daughter) in close pairs;Fraction;Counts", @@ -1680,171 +1998,125 @@ struct LambdaProtonBalanceFunction { "QA3/SplitTrackQA/h1f_sharedClsFraction_t2", "Split-track QA: TPC shared-cluster fraction, track 2 (p-p: second proton; #Lambda-p: primary (anti)proton) in close pairs;Fraction;Counts", {HistType::kTH1F, {{50, 0.0f, 1.0f}}}); + registryCorrelationQA.add( + "h2_qinv_dPhi_Lp", + "Lambda-Proton pairs;#Delta#varphi;q_{inv} (GeV/c)", + {HistType::kTH2F, {{72, -static_cast(TMath::Pi()), static_cast(TMath::Pi())}, {100, 0.0f, 0.10f}}}); // ───────────────────────────────────────────────────────────────────── // ── ProtonCounts_byPID_Check histograms (protons only, sign-guarded in process) ── - h_TPC = + state.h_TPC = registryProtonPidCheck.add("h_TPC", "Proton candidates: TPC PID only (no TOF condition);#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1F, {pidCheckPtAxis}}); - h_TPCandTOF = + state.h_TPCandTOF = registryProtonPidCheck.add("h_TPCandTOF", "Proton candidates: TPC AND TOF both required;#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1F, {pidCheckPtAxis}}); - h_TPCorTOF = + state.h_TPCorTOF = registryProtonPidCheck.add("h_TPCorTOF", "Proton candidates: TPC required; if TOF present at any p, TOF n#sigma also required (differs from final selection, which requires TOF only above p_{TPC} switch);#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1F, {pidCheckPtAxis}}); // ───────────────────────────────────────────────────────────────────── - // ── Merged / pT015toMaxDefined histograms ─────────────────────────────────── - hRho2_Lp_pT015toMaxDefined = - registryRho.add("h2_rho2_Lp_pT015toMaxDefined", - "#rho_{2}(#Lambda, p) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #Lambda (trigger);index(#eta,#varphi) of p (associate)", - {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - hRho2_LAp_pT015toMaxDefined = - registryRho.add("h2_rho2_LAp_pT015toMaxDefined", - "#rho_{2}(#Lambda, #bar{p}) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #Lambda (trigger);index(#eta,#varphi) of #bar{p} (associate)", - {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - hRho2_ALp_pT015toMaxDefined = - registryRho.add("h2_rho2_ALp_pT015toMaxDefined", - "#rho_{2}(#bar{#Lambda}, p) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #bar{#Lambda} (trigger);index(#eta,#varphi) of p (associate)", - {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - hRho2_ALAp_pT015toMaxDefined = - registryRho.add("h2_rho2_ALAp_pT015toMaxDefined", - "#rho_{2}(#bar{#Lambda}, #bar{p}) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #bar{#Lambda} (trigger);index(#eta,#varphi) of #bar{p} (associate)", - {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - hRho2_pp_pT015toMaxDefined = - registryRho.add("h2_rho2_pp_pT015toMaxDefined", - "#rho_{2}(p, p) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of p (trigger);index(#eta,#varphi) of p (associate)", - {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - hRho2_pAp_pT015toMaxDefined = - registryRho.add("h2_rho2_pAp_pT015toMaxDefined", - "#rho_{2}(p, #bar{p}) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of p (trigger);index(#eta,#varphi) of #bar{p} (associate)", - {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - hRho2_App_pT015toMaxDefined = - registryRho.add("h2_rho2_App_pT015toMaxDefined", - "#rho_{2}(#bar{p}, p) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #bar{p} (trigger);index(#eta,#varphi) of p (associate)", - {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - hRho2_ApAp_pT015toMaxDefined = - registryRho.add("h2_rho2_ApAp_pT015toMaxDefined", - "#rho_{2}(#bar{p}, #bar{p}) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #bar{p} (trigger);index(#eta,#varphi) of #bar{p} (associate)", - {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - hRho2_LL_pT015toMaxDefined = - registryRho.add("h2_rho2_LL_pT015toMaxDefined", - "#rho_{2}(#Lambda, #Lambda) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #Lambda (trigger);index(#eta,#varphi) of #Lambda (associate)", - {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - hRho2_LAL_pT015toMaxDefined = - registryRho.add("h2_rho2_LAL_pT015toMaxDefined", - "#rho_{2}(#Lambda, #bar{#Lambda}) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #Lambda (trigger);index(#eta,#varphi) of #bar{#Lambda} (associate)", - {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - hRho2_ALL_pT015toMaxDefined = - registryRho.add("h2_rho2_ALL_pT015toMaxDefined", - "#rho_{2}(#bar{#Lambda}, #Lambda) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #bar{#Lambda} (trigger);index(#eta,#varphi) of #Lambda (associate)", - {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - hRho2_ALAL_pT015toMaxDefined = - registryRho.add("h2_rho2_ALAL_pT015toMaxDefined", - "#rho_{2}(#bar{#Lambda}, #bar{#Lambda}) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #bar{#Lambda} (trigger);index(#eta,#varphi) of #bar{#Lambda} (associate)", - {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - + // ── Merged / AllPt histograms ─────────────────────────────────── // ── Rapidity merged rho2 histograms ───────────────────────────────────── - // NOTE: these must use unrolledAxisY (kRhoYBins*kRhoPhiBins bins), - // NOT the eta-space unrolledAxis (kRhoEtaBins*kRhoPhiBins bins). - // GetUnrolledIndexY() below produces indices in [0, kRhoUnrolledBinsY), + // NOTE: these must use unrolledAxisY (mRhoYBins*mRhoPhiBins bins), + // GetUnrolledIndexY() below produces indices in [0, mRhoUnrolledBinsY), // so the booked axis must match that range or downstream R2/BF code that // expects y-space-sized histograms will see a dimension mismatch. - hRho2_Lp_y_pT015toMaxDefined = - registryRho.add("h2_rho2_Lp_y_pT015toMaxDefined", "#rho_{2}(#Lambda, p) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #Lambda (trigger);index(y,#varphi) of p (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); - hRho2_LAp_y_pT015toMaxDefined = - registryRho.add("h2_rho2_LAp_y_pT015toMaxDefined", "#rho_{2}(#Lambda, #bar{p}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #Lambda (trigger);index(y,#varphi) of #bar{p} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); - hRho2_ALp_y_pT015toMaxDefined = - registryRho.add("h2_rho2_ALp_y_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda}, p) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{#Lambda} (trigger);index(y,#varphi) of p (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); - hRho2_ALAp_y_pT015toMaxDefined = - registryRho.add("h2_rho2_ALAp_y_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda}, #bar{p}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{#Lambda} (trigger);index(y,#varphi) of #bar{p} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); - hRho2_pp_y_pT015toMaxDefined = - registryRho.add("h2_rho2_pp_y_pT015toMaxDefined", "#rho_{2}(p, p) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of p (trigger);index(y,#varphi) of p (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); - hRho2_pAp_y_pT015toMaxDefined = - registryRho.add("h2_rho2_pAp_y_pT015toMaxDefined", "#rho_{2}(p, #bar{p}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of p (trigger);index(y,#varphi) of #bar{p} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); - hRho2_App_y_pT015toMaxDefined = - registryRho.add("h2_rho2_App_y_pT015toMaxDefined", "#rho_{2}(#bar{p}, p) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{p} (trigger);index(y,#varphi) of p (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); - hRho2_ApAp_y_pT015toMaxDefined = - registryRho.add("h2_rho2_ApAp_y_pT015toMaxDefined", "#rho_{2}(#bar{p}, #bar{p}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{p} (trigger);index(y,#varphi) of #bar{p} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); - hRho2_LL_y_pT015toMaxDefined = - registryRho.add("h2_rho2_LL_y_pT015toMaxDefined", "#rho_{2}(#Lambda, #Lambda) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #Lambda (trigger);index(y,#varphi) of #Lambda (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); - hRho2_LAL_y_pT015toMaxDefined = - registryRho.add("h2_rho2_LAL_y_pT015toMaxDefined", "#rho_{2}(#Lambda, #bar{#Lambda}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #Lambda (trigger);index(y,#varphi) of #bar{#Lambda} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); - hRho2_ALL_y_pT015toMaxDefined = - registryRho.add("h2_rho2_ALL_y_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda}, #Lambda) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{#Lambda} (trigger);index(y,#varphi) of #Lambda (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); - hRho2_ALAL_y_pT015toMaxDefined = - registryRho.add("h2_rho2_ALAL_y_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda}, #bar{#Lambda}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{#Lambda} (trigger);index(y,#varphi) of #bar{#Lambda} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_Lp_y_AllPt = + registryRho.add("h2_rho2_Lp_y_AllPt", "#rho_{2}(#Lambda, p) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #Lambda (trigger);index(y,#varphi) of p (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_LAp_y_AllPt = + registryRho.add("h2_rho2_LAp_y_AllPt", "#rho_{2}(#Lambda, #bar{p}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #Lambda (trigger);index(y,#varphi) of #bar{p} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_ALp_y_AllPt = + registryRho.add("h2_rho2_ALp_y_AllPt", "#rho_{2}(#bar{#Lambda}, p) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{#Lambda} (trigger);index(y,#varphi) of p (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_ALAp_y_AllPt = + registryRho.add("h2_rho2_ALAp_y_AllPt", "#rho_{2}(#bar{#Lambda}, #bar{p}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{#Lambda} (trigger);index(y,#varphi) of #bar{p} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_pp_y_AllPt = + registryRho.add("h2_rho2_pp_y_AllPt", "#rho_{2}(p, p) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of p (trigger);index(y,#varphi) of p (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_pAp_y_AllPt = + registryRho.add("h2_rho2_pAp_y_AllPt", "#rho_{2}(p, #bar{p}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of p (trigger);index(y,#varphi) of #bar{p} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_App_y_AllPt = + registryRho.add("h2_rho2_App_y_AllPt", "#rho_{2}(#bar{p}, p) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{p} (trigger);index(y,#varphi) of p (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_ApAp_y_AllPt = + registryRho.add("h2_rho2_ApAp_y_AllPt", "#rho_{2}(#bar{p}, #bar{p}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{p} (trigger);index(y,#varphi) of #bar{p} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_LL_y_AllPt = + registryRho.add("h2_rho2_LL_y_AllPt", "#rho_{2}(#Lambda, #Lambda) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #Lambda (trigger);index(y,#varphi) of #Lambda (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_LAL_y_AllPt = + registryRho.add("h2_rho2_LAL_y_AllPt", "#rho_{2}(#Lambda, #bar{#Lambda}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #Lambda (trigger);index(y,#varphi) of #bar{#Lambda} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_ALL_y_AllPt = + registryRho.add("h2_rho2_ALL_y_AllPt", "#rho_{2}(#bar{#Lambda}, #Lambda) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{#Lambda} (trigger);index(y,#varphi) of #Lambda (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_ALAL_y_AllPt = + registryRho.add("h2_rho2_ALAL_y_AllPt", "#rho_{2}(#bar{#Lambda}, #bar{#Lambda}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{#Lambda} (trigger);index(y,#varphi) of #bar{#Lambda} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); // ───────────────────────────────────────────────────────────────────── - hPtSelectedPrimProton_pT015toMaxDefined = - registryOther.add("hPtSelectedPrimProton_pT015toMaxDefined", + state.hPtSelectedPrimProton_AllPt = + registryOther.add("hPtSelectedPrimProton_AllPt", "Selected primary proton p_{T} spectrum (integrated, 0.5-3.6 GeV/#it{c});#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1F, {ptAxis}}); - hPtSelectedPrimAntiProton_pT015toMaxDefined = - registryOther.add("hPtSelectedPrimAntiProton_pT015toMaxDefined", + state.hPtSelectedPrimAntiProton_AllPt = + registryOther.add("hPtSelectedPrimAntiProton_AllPt", "Selected primary antiproton p_{T} spectrum (integrated, 0.5-3.6 GeV/#it{c});#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1F, {ptAxis}}); - hPtSelLambda_pT015toMaxDefined = - registryOther.add("hPtSelLambda_pT015toMaxDefined", + state.hPtSelLambda_AllPt = + registryOther.add("hPtSelLambda_AllPt", "Selected #Lambda p_{T} spectrum (integrated, 0.6-3.6 GeV/#it{c});#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1F, {ptAxis}}); - hPtSelAntiLambda_pT015toMaxDefined = - registryOther.add("hPtSelAntiLambda_pT015toMaxDefined", + state.hPtSelAntiLambda_AllPt = + registryOther.add("hPtSelAntiLambda_AllPt", "Selected #bar{#Lambda} p_{T} spectrum (integrated, 0.6-3.6 GeV/#it{c});#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1F, {ptAxis}}); - hMassLambdaMerged = - registryLambda.add("hMassLambdaNoMassCut_pT015toMaxDefined", + state.hMassLambdaMerged = + registryLambda.add("hMassLambdaNoMassCut_AllPt", "#Lambda mass (all selections except #Lambda mass window), integrated #it{p}_{T} 0.6-3.6 GeV/#it{c};#it{M}_{inv} [GeV/#it{c}^{2}];Counts", {HistType::kTH1F, {lambdaMassAxis}}); - hMassAntiLambdaMerged = - registryLambda.add("hMassAntiLambdaNoMassCut_pT015toMaxDefined", + state.hMassAntiLambdaMerged = + registryLambda.add("hMassAntiLambdaNoMassCut_AllPt", "#bar{#Lambda} mass (all selections except #Lambda mass window), integrated #it{p}_{T} 0.6-3.6 GeV/#it{c};#it{M}_{inv} [GeV/#it{c}^{2}];Counts", {HistType::kTH1F, {lambdaMassAxis}}); // ───────────────────────────────────────────────────────────────────── - // ── Per-pT-bin histograms (Lambda pT bins 0.6 → 6.0) ───────────────── - hMassLambdaPtBins.clear(); - hMassAntiLambdaPtBins.clear(); - hPtPrimProton_Lambda.clear(); - hPtPrimAntiProton_Lambda.clear(); - hPtPrimProton_AntiLambda.clear(); - hPtPrimAntiProton_AntiLambda.clear(); + // ── Per-pT-bin histograms (Lambda pT bins 0.6 → 3.6) ───────────────── + state.hMassLambdaPtBins.clear(); + state.hMassAntiLambdaPtBins.clear(); + state.hPtPrimProton_Lambda.clear(); + state.hPtPrimAntiProton_Lambda.clear(); + state.hPtPrimProton_AntiLambda.clear(); + state.hPtPrimAntiProton_AntiLambda.clear(); - for (size_t i = 0; i < ptEdges.size() - 1; ++i) { - const float ptLo = ptEdges[i]; - const float ptHi = ptEdges[i + 1]; + for (size_t i = 0; i < state.ptEdges.size() - 1; ++i) { + const float ptLo = state.ptEdges[i]; + const float ptHi = state.ptEdges[i + 1]; - hPtPrimProton_Lambda.push_back( + state.hPtPrimProton_Lambda.push_back( registryOther.add( Form("hPtPrimProton_Lambda_ptBin%02zu", i), Form("Proton #it{p}_{T} in #Lambda-p pairs, #Lambda #it{p}_{T} in [%.1f, %.1f) GeV/#it{c};#it{p}_{T}^{p} (GeV/#it{c});Counts", ptLo, ptHi), {HistType::kTH1F, {ptAxis}})); - hPtPrimAntiProton_Lambda.push_back( + state.hPtPrimAntiProton_Lambda.push_back( registryOther.add( Form("hPtPrimAntiProton_Lambda_ptBin%02zu", i), Form("Antiproton #it{p}_{T} in #Lambda-#bar{p} pairs, #Lambda #it{p}_{T} in [%.1f, %.1f) GeV/#it{c};#it{p}_{T}^{#bar{p}} (GeV/#it{c});Counts", ptLo, ptHi), {HistType::kTH1F, {ptAxis}})); - hPtPrimProton_AntiLambda.push_back( + state.hPtPrimProton_AntiLambda.push_back( registryOther.add( Form("hPtPrimProton_AntiLambda_ptBin%02zu", i), Form("Proton #it{p}_{T} in #bar{#Lambda}-p pairs, #bar{#Lambda} #it{p}_{T} in [%.1f, %.1f) GeV/#it{c};#it{p}_{T}^{p} (GeV/#it{c});Counts", ptLo, ptHi), {HistType::kTH1F, {ptAxis}})); - hPtPrimAntiProton_AntiLambda.push_back( + state.hPtPrimAntiProton_AntiLambda.push_back( registryOther.add( Form("hPtPrimAntiProton_AntiLambda_ptBin%02zu", i), Form("Antiproton #it{p}_{T} in #bar{#Lambda}-#bar{p} pairs, #bar{#Lambda} #it{p}_{T} in [%.1f, %.1f) GeV/#it{c};#it{p}_{T}^{#bar{p}} (GeV/#it{c});Counts", ptLo, ptHi), {HistType::kTH1F, {ptAxis}})); - hMassLambdaPtBins.push_back( + state.hMassLambdaPtBins.push_back( registryLambda.add( Form("hMassLambdaNoMassCut_ptBin%02zu", i), Form("#Lambda mass (all selections except #Lambda mass window), %.1f #leq #it{p}_{T} < %.1f GeV/#it{c};#it{M}_{inv} [GeV/#it{c}^{2}];Counts", ptLo, ptHi), {HistType::kTH1F, {lambdaMassAxis}})); - hMassAntiLambdaPtBins.push_back( + state.hMassAntiLambdaPtBins.push_back( registryLambda.add( Form("hMassAntiLambdaNoMassCut_ptBin%02zu", i), Form("#bar{#Lambda} mass (all selections except #Lambda mass window), %.1f #leq #it{p}_{T} < %.1f GeV/#it{c};#it{M}_{inv} [GeV/#it{c}^{2}];Counts", ptLo, ptHi), @@ -1852,68 +2124,45 @@ struct LambdaProtonBalanceFunction { } // ───────────────────────────────────────────────────────────────────── - // ── Extended y × pT invariant-mass histograms ───────────────────────── - // Rapidity bins: 2 bins from -0.8 to -0.6 in steps of 0.1 - // pT bins: 25 bins from 0.0 to 2.5 in steps of 0.1 - // Folder structure: - // Lambda_invMassExtended/Lambda/hMassLambda_yBinXX_ptBinYY - // Lambda_invMassExtended/AntiLambda/hMassAntiLambda_yBinXX_ptBinYY - hMassLambdaExtended.clear(); - hMassAntiLambdaExtended.clear(); - hMassLambdaExtended.resize(kExtNyBins); - hMassAntiLambdaExtended.resize(kExtNyBins); - - for (int iY = 0; iY < kExtNyBins; ++iY) { - const float yLow = kExtYMin + iY * kExtYStep; - const float yHigh = yLow + kExtYStep; - // Last rapidity bin [-0.7,-0.6] has inclusive upper edge in filling logic - const bool lastYBin = (iY == kExtNyBins - 1); - const char* yUpBracket = lastYBin ? "<=" : "<"; - - hMassLambdaExtended[iY].resize(kExtNptBins); - hMassAntiLambdaExtended[iY].resize(kExtNptBins); - - for (int iPt = 0; iPt < kExtNptBins; ++iPt) { - const float ptLow = kExtPtMin + iPt * kExtPtStep; - const float ptHigh = ptLow + kExtPtStep; - // Last pT bin [2.4,2.5] has inclusive upper edge in filling logic - const bool lastPtBin = (iPt == kExtNptBins - 1); - const char* ptUpBracket = lastPtBin ? "<=" : "<"; - - // Lambda - hMassLambdaExtended[iY][iPt] = - registryLambdaExtended.add( - Form("Lambda/hMassLambda_yBin%02d_ptBin%02d", iY, iPt), - Form("#Lambda mass (no mass-window cut) | %.1f #leq y %s %.1f | %.1f #leq #it{p}_{T} %s %.1f GeV/#it{c};#it{M}_{inv} [GeV/#it{c}^{2}];Counts", - yLow, yUpBracket, yHigh, ptLow, ptUpBracket, ptHigh), - {HistType::kTH1F, {lambdaMassAxis}}); - - // AntiLambda - hMassAntiLambdaExtended[iY][iPt] = - registryLambdaExtended.add( - Form("AntiLambda/hMassAntiLambda_yBin%02d_ptBin%02d", iY, iPt), - Form("#bar{#Lambda} mass (no mass-window cut) | %.1f #leq y %s %.1f | %.1f #leq #it{p}_{T} %s %.1f GeV/#it{c};#it{M}_{inv} [GeV/#it{c}^{2}];Counts", - yLow, yUpBracket, yHigh, ptLow, ptUpBracket, ptHigh), - {HistType::kTH1F, {lambdaMassAxis}}); - } - } - // ───────────────────────────────────────────────────────────────────── - // ══════════════════════════════════════════════════════════════════════ // Centrality-differential histograms → top-level folder "rho1ANDrho2_LP_CentralityBased" // ══════════════════════════════════════════════════════════════════════ - centSets.clear(); - centEdgesLocal = {0.f, 5.f, 10.f, 20.f, 40.f, 60.f, 80.f}; + state.centSets.clear(); + // Single source of truth: centrality class edges come from the cCentBins ConfigurableAxis. + // Edges are read back from a temporary ROOT histogram built from the AxisSpec. + { + const AxisSpec centCfgAxis(cCentBins, "centrality"); + HistogramRegistry tmpReg{"tmpCentEdges", {}, OutputObjHandlingPolicy::AnalysisObject}; + auto hTmp = tmpReg.add("hTmp", "", {HistType::kTH1F, {centCfgAxis}}); + state.centEdgesLocal.clear(); + const TAxis* ax = hTmp->GetXaxis(); + for (int i = 1; i <= ax->GetNbins(); ++i) { + state.centEdgesLocal.push_back(static_cast(ax->GetBinLowEdge(i))); + } + state.centEdgesLocal.push_back(static_cast(ax->GetBinUpEdge(ax->GetNbins()))); + } + constexpr size_t MinCentralityEdges = 2; + if (state.centEdgesLocal.size() < MinCentralityEdges) { + LOGF(fatal, "cCentBins needs at least 2 edges"); + } + state.mCentMin = state.centEdgesLocal.front(); + state.mCentMax = state.centEdgesLocal.back(); + registryOther.get(HIST("hEventCutflow"))->GetXaxis()->SetBinLabel(3, Form("%s in [%.0f, %.0f)", cUseFT0C.value ? "centFT0C" : "centFT0M", state.mCentMin, state.mCentMax)); + for (const float& e : state.centEdgesLocal) { + if (e != std::floor(e)) { + LOGF(fatal, "cCentBins edges must be integers (folder names use integer edges)"); + } + } if (cFillCentHists.value) { - const size_t nCent = centEdgesLocal.size() - 1; - centSets.resize(nCent); + const size_t nCent = state.centEdgesLocal.size() - 1; + state.centSets.resize(nCent); for (size_t ic = 0; ic < nCent; ++ic) { - const float cLo = centEdgesLocal[ic]; - const float cHi = centEdgesLocal[ic + 1]; + const float cLo = state.centEdgesLocal[ic]; + const float cHi = state.centEdgesLocal[ic + 1]; const std::string dir = Form("Cent%02d_%02d", static_cast(cLo), static_cast(cHi)); - const std::string ctag = Form("FT0M %.0f-%.0f%%", cLo, cHi); - auto& S = centSets[ic]; + const std::string ctag = Form("%s %.0f-%.0f%%", cUseFT0C.value ? "FT0C" : "FT0M", cLo, cHi); + auto& S = state.centSets[ic]; S.hEvents = registryCent.add((dir + "/hEventCounter").c_str(), ("Events, " + ctag).c_str(), @@ -1929,42 +2178,155 @@ struct LambdaProtonBalanceFunction { auto addY = [&](std::shared_ptr& h, const char* name, const char* title) { h = registryCent.add((dir + "/" + name).c_str(), (std::string(title) + ", " + ctag).c_str(), {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); }; - addY(S.Lp_y, "h2_rho2_Lp_y_pT015toMaxDefined", "#rho_{2}(#Lambda,p) (y,#varphi)"); - addY(S.LAp_y, "h2_rho2_LAp_y_pT015toMaxDefined", "#rho_{2}(#Lambda,#bar{p}) (y,#varphi)"); - addY(S.ALp_y, "h2_rho2_ALp_y_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda},p) (y,#varphi)"); - addY(S.ALAp_y, "h2_rho2_ALAp_y_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda},#bar{p}) (y,#varphi)"); - addY(S.pp_y, "h2_rho2_pp_y_pT015toMaxDefined", "#rho_{2}(p,p) (y,#varphi)"); - addY(S.pAp_y, "h2_rho2_pAp_y_pT015toMaxDefined", "#rho_{2}(p,#bar{p}) (y,#varphi)"); - addY(S.App_y, "h2_rho2_App_y_pT015toMaxDefined", "#rho_{2}(#bar{p},p) (y,#varphi)"); - addY(S.ApAp_y, "h2_rho2_ApAp_y_pT015toMaxDefined", "#rho_{2}(#bar{p},#bar{p}) (y,#varphi)"); - addY(S.LL_y, "h2_rho2_LL_y_pT015toMaxDefined", "#rho_{2}(#Lambda,#Lambda) (y,#varphi)"); - addY(S.LAL_y, "h2_rho2_LAL_y_pT015toMaxDefined", "#rho_{2}(#Lambda,#bar{#Lambda}) (y,#varphi)"); - addY(S.ALL_y, "h2_rho2_ALL_y_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda},#Lambda) (y,#varphi)"); - addY(S.ALAL_y, "h2_rho2_ALAL_y_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda},#bar{#Lambda}) (y,#varphi)"); - - // ── eta space (only if BOTH switches on: memory heavy) ── - if (cFillEtaSpace.value) { - S.rho1_Proton = registryCent.add((dir + "/h2_rho1_Proton").c_str(), ("#rho_{1}(p) in (#eta,#varphi), " + ctag).c_str(), {HistType::kTH2F, {etaAxis, phiAxis}}); - S.rho1_AntiProton = registryCent.add((dir + "/h2_rho1_AntiProton").c_str(), ("#rho_{1}(#bar{p}) in (#eta,#varphi), " + ctag).c_str(), {HistType::kTH2F, {etaAxis, phiAxis}}); - S.rho1_Lambda = registryCent.add((dir + "/h2_rho1_Lambda").c_str(), ("#rho_{1}(#Lambda) in (#eta,#varphi), " + ctag).c_str(), {HistType::kTH2F, {etaAxis, phiAxis}}); - S.rho1_AntiLambda = registryCent.add((dir + "/h2_rho1_AntiLambda").c_str(), ("#rho_{1}(#bar{#Lambda}) in (#eta,#varphi), " + ctag).c_str(), {HistType::kTH2F, {etaAxis, phiAxis}}); - - auto addEta = [&](std::shared_ptr& h, const char* name, const char* title) { - h = registryCent.add((dir + "/" + name).c_str(), (std::string(title) + ", " + ctag).c_str(), {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - }; - addEta(S.Lp, "h2_rho2_Lp_pT015toMaxDefined", "#rho_{2}(#Lambda,p) (#eta,#varphi)"); - addEta(S.LAp, "h2_rho2_LAp_pT015toMaxDefined", "#rho_{2}(#Lambda,#bar{p}) (#eta,#varphi)"); - addEta(S.ALp, "h2_rho2_ALp_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda},p) (#eta,#varphi)"); - addEta(S.ALAp, "h2_rho2_ALAp_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda},#bar{p}) (#eta,#varphi)"); - addEta(S.pp, "h2_rho2_pp_pT015toMaxDefined", "#rho_{2}(p,p) (#eta,#varphi)"); - addEta(S.pAp, "h2_rho2_pAp_pT015toMaxDefined", "#rho_{2}(p,#bar{p}) (#eta,#varphi)"); - addEta(S.App, "h2_rho2_App_pT015toMaxDefined", "#rho_{2}(#bar{p},p) (#eta,#varphi)"); - addEta(S.ApAp, "h2_rho2_ApAp_pT015toMaxDefined", "#rho_{2}(#bar{p},#bar{p}) (#eta,#varphi)"); - addEta(S.LL, "h2_rho2_LL_pT015toMaxDefined", "#rho_{2}(#Lambda,#Lambda) (#eta,#varphi)"); - addEta(S.LAL, "h2_rho2_LAL_pT015toMaxDefined", "#rho_{2}(#Lambda,#bar{#Lambda}) (#eta,#varphi)"); - addEta(S.ALL, "h2_rho2_ALL_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda},#Lambda) (#eta,#varphi)"); - addEta(S.ALAL, "h2_rho2_ALAL_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda},#bar{#Lambda}) (#eta,#varphi)"); - } + addY(S.Lp_y, "h2_rho2_Lp_y_AllPt", "#rho_{2}(#Lambda,p) (y,#varphi)"); + addY(S.LAp_y, "h2_rho2_LAp_y_AllPt", "#rho_{2}(#Lambda,#bar{p}) (y,#varphi)"); + addY(S.ALp_y, "h2_rho2_ALp_y_AllPt", "#rho_{2}(#bar{#Lambda},p) (y,#varphi)"); + addY(S.ALAp_y, "h2_rho2_ALAp_y_AllPt", "#rho_{2}(#bar{#Lambda},#bar{p}) (y,#varphi)"); + addY(S.pp_y, "h2_rho2_pp_y_AllPt", "#rho_{2}(p,p) (y,#varphi)"); + addY(S.pAp_y, "h2_rho2_pAp_y_AllPt", "#rho_{2}(p,#bar{p}) (y,#varphi)"); + addY(S.App_y, "h2_rho2_App_y_AllPt", "#rho_{2}(#bar{p},p) (y,#varphi)"); + addY(S.ApAp_y, "h2_rho2_ApAp_y_AllPt", "#rho_{2}(#bar{p},#bar{p}) (y,#varphi)"); + addY(S.LL_y, "h2_rho2_LL_y_AllPt", "#rho_{2}(#Lambda,#Lambda) (y,#varphi)"); + addY(S.LAL_y, "h2_rho2_LAL_y_AllPt", "#rho_{2}(#Lambda,#bar{#Lambda}) (y,#varphi)"); + addY(S.ALL_y, "h2_rho2_ALL_y_AllPt", "#rho_{2}(#bar{#Lambda},#Lambda) (y,#varphi)"); + addY(S.ALAL_y, "h2_rho2_ALAL_y_AllPt", "#rho_{2}(#bar{#Lambda},#bar{#Lambda}) (y,#varphi)"); + } + } + + // Matched-event guards and generated-population diagnostic (no extra task members). + auto hMatched = registryOther.add("hMatchedEventQA", "Matched-event BF guards;Stage;Counts", + {HistType::kTH1F, {{7, 0.5f, 7.5f}}}); + const std::array matchedLabels = {"MC collisions seen", "reco-collision count != 1", "MC-label mismatch", + "failed sel8/|z|", "failed numContrib/centrality/INEL/extra selection", + "accepted", "McParticle MC-collision mismatch (must be 0)"}; + for (int i = 0; i < static_cast(matchedLabels.size()); ++i) { + hMatched->GetXaxis()->SetBinLabel(i + 1, matchedLabels[i]); + } + auto hDaughters = registryOther.add("hGenLambdaDaughters", "Primary generated Lambdas in accepted events, before acceptance;Population;Particles", + {HistType::kTH1F, {{4, 0.5f, 4.5f}}}); + const std::array daughterLabels = {"primary Lambda with daughters", "primary Lambda without daughters", + "primary AntiLambda with daughters", "primary AntiLambda without daughters"}; + for (int i = 0; i < static_cast(daughterLabels.size()); ++i) { + hDaughters->GetXaxis()->SetBinLabel(i + 1, daughterLabels[i]); + } + const std::string mode = doprocessData.value ? "processData" : doprocessMCRecBF.value ? "processMCRecBF" + : doprocessMCGenBF.value ? "processMCGenBF" + : "none"; + std::vector configLabels = { + "activeBFMode=" + mode, + "cUseFT0C=" + std::to_string(static_cast(cUseFT0C.value)), + "cApplyEffCorr=" + std::to_string(static_cast(cApplyEffCorr.value)), + "cUseQinvCut=" + std::to_string(static_cast(cUseQinvCut.value)), + "cQinvCutValue=" + std::to_string(cQinvCutValue.value), + "cPileupReject=" + std::to_string(static_cast(cPileupReject.value)), + "cZVtxTimeDiff=" + std::to_string(static_cast(cZVtxTimeDiff.value)), + "cIsGoodITSLayers=" + std::to_string(static_cast(cIsGoodITSLayers.value)), + "cPPandV0ZVertexCut=" + std::to_string(cPPandV0ZVertexCut.value), + "cRhoYBins=" + std::to_string(cRhoYBins.value), + "cRhoPhiBins=" + std::to_string(cRhoPhiBins.value), + "cRhoYMin=" + std::to_string(cRhoYMin.value), + "cRhoYMax=" + std::to_string(cRhoYMax.value), + "mCentMin=" + std::to_string(state.mCentMin), + "mCentMax=" + std::to_string(state.mCentMax), + "cFillCentHists=" + std::to_string(static_cast(cFillCentHists.value))}; + std::string centBinsLabel = "cCentBins="; + for (const float& edge : state.centEdgesLocal) { + if (centBinsLabel != "cCentBins=") { + centBinsLabel += ","; + } + centBinsLabel += std::to_string(edge); + } + configLabels.push_back(centBinsLabel); + const int nConfig = static_cast(configLabels.size()); + auto hConfig = registryOther.add("hRunConfig", "BF configuration;Setting;Accepted events", + {HistType::kTH1F, {{nConfig, 0.5f, nConfig + 0.5f}}}); + for (int i = 0; i < nConfig; ++i) { + hConfig->GetXaxis()->SetBinLabel(i + 1, configLabels[i].c_str()); + } + + // ── Efficiency correction: load 1/eff maps once ────────────────────── + if (cApplyEffCorr.value) { + if (!cEffCcdbPath.value.empty()) { + // ── load from CCDB (TList of TH3) ── + ccdb->setURL(cCcdbUrl.value); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + auto* effList = ccdb->getForTimeStamp(cEffCcdbPath.value, cEffCcdbTimestamp.value); + if (!effList) { + LOGF(fatal, "Cannot get efficiency TList from CCDB path %s (timestamp %" PRId64 ")", cEffCcdbPath.value.c_str(), static_cast(cEffCcdbTimestamp.value)); + } + auto loadMap = [&](const char* name) -> TH3* { + auto* obj = effList->FindObject(name); + if (!obj) { + LOGF(fatal, "Missing efficiency map %s in CCDB object", name); + } + TH3* h = dynamic_cast(obj->Clone()); + if (!h) { + LOGF(fatal, "Object %s in CCDB could not be cast to TH3*", name); + } + h->SetDirectory(nullptr); + return h; + }; + state.invEffL = loadMap("hInvEff_Lambda"); + state.invEffAL = loadMap("hInvEff_AntiLambda"); + state.invEffP = loadMap("hInvEff_Proton"); + state.invEffAP = loadMap("hInvEff_AntiProton"); + } else { + // ── load from a ROOT file (fallback / local tests) ── + TFile* fEff = TFile::Open(cEffFile.value.c_str(), "READ"); + if (!fEff || fEff->IsZombie()) { + LOGF(fatal, "Cannot open efficiency file %s", cEffFile.value.c_str()); + } + auto loadMap = [&](const char* name) -> TH3* { + TH3* h = dynamic_cast(fEff->Get(name)); + if (!h) { + LOGF(fatal, "Missing efficiency map %s", name); + } + h->SetDirectory(nullptr); + return h; + }; + state.invEffL = loadMap("hInvEff_Lambda"); + state.invEffAL = loadMap("hInvEff_AntiLambda"); + state.invEffP = loadMap("hInvEff_Proton"); + state.invEffAP = loadMap("hInvEff_AntiProton"); + fEff->Close(); + } + + // Sumw2 for all weighted fills (rho1, and every rho2 channel: Lp family, pp family, LL family) + registryRho.get(HIST("h2_rho1_Proton_y"))->Sumw2(); + registryRho.get(HIST("h2_rho1_AntiProton_y"))->Sumw2(); + registryRho.get(HIST("h2_rho1_Lambda_y"))->Sumw2(); + registryRho.get(HIST("h2_rho1_AntiLambda_y"))->Sumw2(); + state.hRho2_Lp_y_AllPt->Sumw2(); + state.hRho2_LAp_y_AllPt->Sumw2(); + state.hRho2_ALp_y_AllPt->Sumw2(); + state.hRho2_ALAp_y_AllPt->Sumw2(); + state.hRho2_pp_y_AllPt->Sumw2(); + state.hRho2_pAp_y_AllPt->Sumw2(); + state.hRho2_App_y_AllPt->Sumw2(); + state.hRho2_ApAp_y_AllPt->Sumw2(); + state.hRho2_LL_y_AllPt->Sumw2(); + state.hRho2_LAL_y_AllPt->Sumw2(); + state.hRho2_ALL_y_AllPt->Sumw2(); + state.hRho2_ALAL_y_AllPt->Sumw2(); + for (const auto& S : state.centSets) { + S.rho1_Proton_y->Sumw2(); + S.rho1_AntiProton_y->Sumw2(); + S.rho1_Lambda_y->Sumw2(); + S.rho1_AntiLambda_y->Sumw2(); + S.Lp_y->Sumw2(); + S.LAp_y->Sumw2(); + S.ALp_y->Sumw2(); + S.ALAp_y->Sumw2(); + S.pp_y->Sumw2(); + S.pAp_y->Sumw2(); + S.App_y->Sumw2(); + S.ApAp_y->Sumw2(); + S.LL_y->Sumw2(); + S.LAL_y->Sumw2(); + S.ALL_y->Sumw2(); + S.ALAL_y->Sumw2(); } } } @@ -1992,70 +2354,74 @@ struct LambdaProtonBalanceFunction { } // ───────────────────────────────────────────────────────────────────── - void process(soa::Filtered>::iterator const& collision, - aod::V0Datas const& V0s, - MyTracks const& tracks) + // Shared BF event stages. Entry follows sel8 and the vertex cut in every mode. + template + bool selectBFEvent(TCollision const& collision, TTracks const& tracks, float& cent, int& nTracksINEL) { - // ── Ordered-pair convention ──────────────────────────────────────────── - // pp : trigger=proton1, assoc=proton2 (self-pairs rejected) - // pAp : trigger=proton, assoc=antiproton - // App : trigger=antiproton,assoc=proton - // ApAp : trigger=antiproton1,assoc=antiproton2 (self-pairs rejected) - // Lp : trigger=Lambda, assoc=proton (daughter-track overlap rejected) - // LAp : trigger=Lambda, assoc=antiproton - // ALp : trigger=AntiLambda,assoc=proton - // ALAp : trigger=AntiLambda,assoc=antiproton - // LL/LAL/ALL/ALAL: both V0s from respective selected lists (self-pairs rejected) - // - // Each channel has both eta-space (unrolledIndex) and - // rapidity-space (unrolledIndexY) versions filled in the same loop; the - // eta-space fills are gated by cFillEtaSpace. - // ───────────────────────────────────────────────────────────────────── - - // ── Event cutflow bin 1 — enters process (after sel8 + posZ filters) ── + // ── Event cutflow bin 1 — enters processData (after sel8 + posZ filters) ── registryOther.fill(HIST("hEventCutflow"), 1.0f); if (collision.numContrib() < 1) { - return; + return false; } // ── Event cutflow bin 2 — after numContrib >= 1 ────────────────────── registryOther.fill(HIST("hEventCutflow"), 2.0f); - static constexpr float kMaxCentFT0M = 80.0f; - if (collision.centFT0M() > kMaxCentFT0M) { - return; + cent = getCentrality(collision); + if (cent < state.mCentMin || cent >= state.mCentMax) { + return false; } - // ── Event cutflow bin 3 — after centFT0M < 80 ──────────────────────── + // ── Event cutflow bin 3 — after centrality in [first, last edge of cCentBins) ── registryOther.fill(HIST("hEventCutflow"), 3.0f); // INEL > 0: require at least one charged track in |eta| < 0.8 - int nTracksINEL = 0; static constexpr float kMaxInelEta = 0.8f; + nTracksINEL = 0; for (auto const& trk : tracks) { if (std::abs(trk.eta()) < kMaxInelEta) { ++nTracksINEL; } } if (nTracksINEL < 1) { - return; + return false; } // ── Event cutflow bin 4 — after INEL > 0 ───────────────────────────── registryOther.fill(HIST("hEventCutflow"), 4.0f); + if (!passesExtraEventSelection(collision)) { + return false; + } + // ── Event cutflow bin 5 — after extra event selection (pileup / zvtx / ITS flags) ── + registryOther.fill(HIST("hEventCutflow"), 5.0f); + + return true; + } + + void fillRunConfig() + { + auto hConfig = registryOther.get(HIST("hRunConfig")); + for (int bin = 1; bin <= hConfig->GetNbinsX(); ++bin) { + hConfig->Fill(static_cast(bin)); + } + } + + template + CentRho2Set* bookBFEvent(TCollision const& collision, float cent, int nTracksINEL) + { registryRho.fill(HIST("hEventCounter"), 0.5); // ── Centrality bin for the centrality-differential histograms ───────── CentRho2Set* cs = nullptr; if (cFillCentHists.value) { - const int iCent = centBinIndex(collision.centFT0M()); - if (iCent >= 0 && iCent < static_cast(centSets.size())) { - cs = ¢Sets[iCent]; + const int iCent = centBinIndex(cent); + if (iCent >= 0 && iCent < static_cast(state.centSets.size())) { + cs = &state.centSets[iCent]; cs->hEvents->Fill(0.5); } } registryOther.fill(HIST("hVertexZRec"), collision.posZ()); - registryOther.fill(HIST("hFT0MPercentile"), collision.centFT0M()); + registryOther.fill(HIST("hFT0MPercentile"), cent); // ── Item 5: Event-level pileup rejection metrics ────────────────────── // Fill hPileupFlags: one bin per evsel bit, filled if the bit is set. @@ -2079,46 +2445,39 @@ struct LambdaProtonBalanceFunction { collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { registryOther.fill(HIST("hPileupFlags"), 6.0f); } - // N_{tracks} and N_{contribs} vs FT0M centrality — filled once per event - { - const float cent = collision.centFT0M(); - registryOther.fill(HIST("h2f_NTracks_vs_Cent"), cent, static_cast(nTracksINEL)); - registryOther.fill(HIST("h2f_NumContrib_vs_Cent"), cent, static_cast(collision.numContrib())); - } + // N_{tracks} and N_{contribs} vs centrality — filled once per event + registryOther.fill(HIST("h2f_NTracks_vs_Cent"), cent, static_cast(nTracksINEL)); + registryOther.fill(HIST("h2f_NumContrib_vs_Cent"), cent, static_cast(collision.numContrib())); // ───────────────────────────────────────────────────────────────────── - // ====== Step 1: Initialise containers ====================================== - std::unordered_set v0DaughterTrackIds; - v0DaughterTrackIds.reserve(64); - std::vector selectedPrimProtons; - std::vector selectedPrimAntiProtons; - std::vector selectedLambdas; - std::vector selectedAntiLambdas; - - int nSelLambda = 0; - int nSelAntiLambda = 0; - int nPairs_Lambda_PrimProton = 0; - int nPairs_Lambda_PrimAntiProton = 0; - int nPairs_AntiLambda_PrimProton = 0; - int nPairs_AntiLambda_PrimAntiProton = 0; - - // ── Veto QA event counters (filled once per event) ─────────────────── - int nProtonBeforeVeto = 0; - int nProtonRemovedVeto = 0; - int nAntiPBeforeVeto = 0; - int nAntiPRemovedVeto = 0; - // ───────────────────────────────────────────────────────────────────── + fillRunConfig(); + return cs; + } + // Shared reconstructed selection: truth labels never participate in acceptance. + // QA calls stay at their original selection stages for the BF instantiation. + template + void selectRecoV0s(TCollision const& collision, TV0s const& V0s, + std::vector& selectedLambdas, + std::vector& selectedAntiLambdas, + std::unordered_set& v0DaughterTrackIds) + { // ====== Step 2: V0 validation loop (BEFORE track loop) ================= // Validates each V0, builds the veto set from validated daughters only, - // fills single-particle histograms, and populates selectedLambdas/AntiLambdas. - // Pairing with protons happens in Step 3 (after the track loop). + // fills QA only when requested, and populates selectedLambdas/AntiLambdas. + // BF single-particle fills and pairing remain in runRecoAnalysis(). constexpr float mLambdaPDG = o2::constants::physics::MassLambda; constexpr float mK0PDG = o2::constants::physics::MassK0Short; for (const auto& v0 : V0s) { - const auto& v0PosDauCached = v0.posTrack_as(); - const auto& v0NegDauCached = v0.negTrack_as(); + const auto& v0PosDauCached = v0.template posTrack_as(); + const auto& v0NegDauCached = v0.template negTrack_as(); + + // V0 cutflow bin 1: all V0s (both hypotheses) + if constexpr (fillQA) { + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 1.0f); + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 1.0f); + } // ── TRULY RAW QA Fills — before any V0 selection cut ───────────── // Placed here at the very top of the loop: before v0Type, daughter @@ -2127,7 +2486,7 @@ struct LambdaProtonBalanceFunction { // We double-fill under both the Lambda hypothesis (pos=proton, neg=pion) // and the AntiLambda hypothesis (pos=pion, neg=proton) since no PID // decision has been made yet. This gives the true combinatorial view. - { + if constexpr (fillQA) { const auto& rawPosDau = v0PosDauCached; const auto& rawNegDau = v0NegDauCached; const float rawCtau = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * mLambdaPDG; @@ -2153,6 +2512,11 @@ struct LambdaProtonBalanceFunction { if (v0.v0Type() != cV0TypeSelection.value) { continue; } + // V0 cutflow bin 2: V0 type (both hypotheses) + if constexpr (fillQA) { + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 2.0f); + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 2.0f); + } // ───────────────────────────────────────────────────────────────── const auto& v0PosDau = v0PosDauCached; @@ -2165,6 +2529,11 @@ struct LambdaProtonBalanceFunction { v0NegDau.tpcNClsCrossedRows() <= cV0DauMinTpcCrossedRows.value) { continue; } + // V0 cutflow bin 3: daughter kinematics (both hypotheses) + if constexpr (fillQA) { + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 3.0f); + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 3.0f); + } // ───────────────────────────────────────────────────────────────── // ── PID hypothesis ──────────────────────────────────────────────── @@ -2179,6 +2548,16 @@ struct LambdaProtonBalanceFunction { if (v0DauPID_AsLambda == v0DauPID_AsAntiLambda) { continue; } + // V0 cutflow bin 4: unambiguous TPC PID — from here on, fill only the chosen hypothesis + if (v0DauPID_AsLambda) { + if constexpr (fillQA) { + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 4.0f); + } + } else { + if constexpr (fillQA) { + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 4.0f); + } + } // ───────────────────────────────────────────────────────────────── // ── TPC shared clusters cut on daughter tracks ──────────────────── @@ -2199,6 +2578,16 @@ struct LambdaProtonBalanceFunction { if (v0DauPID_AsAntiLambda && std::abs(v0.rapidity(2)) > lambdaV0MaxY.value) { continue; } + // V0 cutflow bin 5: rapidity + if (v0DauPID_AsLambda) { + if constexpr (fillQA) { + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 5.0f); + } + } else { + if constexpr (fillQA) { + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 5.0f); + } + } // ───────────────────────────────────────────────────────────────── // ── Eta cuts ────────────────────────────────────────────────────── @@ -2213,6 +2602,16 @@ struct LambdaProtonBalanceFunction { (v0.pt() < lambdaV0MinPt.value || v0.pt() > lambdaV0MaxPt.value)) { continue; } + // V0 cutflow bin 6: pT acceptance + if (v0DauPID_AsLambda) { + if constexpr (fillQA) { + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 6.0f); + } + } else { + if constexpr (fillQA) { + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 6.0f); + } + } // ───────────────────────────────────────────────────────────────── // ── DCA of daughters to PV ──────────────────────────────────────── @@ -2221,10 +2620,8 @@ struct LambdaProtonBalanceFunction { const float dcaPion = v0DauPID_AsLambda ? std::abs(v0.dcanegtopv()) : std::abs(v0.dcapostopv()); - // ── Lifetime (needed for Final QA fills and CTau cut below) ────────── + // ── Lifetime (needed for the CTau cut below; Final QA recomputes ctau/bgctau on the cleaned sample) ── const float ctau = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * mLambdaPDG; - // FIX-3: compute lab decay length (gamma*c*tau) = ctau * |p| / m - const float bgctau = ctau * v0.p() / mLambdaPDG; // ── (Raw QA fills moved to top of V0 loop — see TRULY RAW block above) ─ @@ -2239,6 +2636,16 @@ struct LambdaProtonBalanceFunction { dcaPion < lambdaV0DaughterPionMinDCAToPV.value) { continue; } + // V0 cutflow bin 7: topology (passTopoCuts) and daughter DCA-to-PV + if (v0DauPID_AsLambda) { + if constexpr (fillQA) { + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 7.0f); + } + } else { + if constexpr (fillQA) { + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 7.0f); + } + } // ───────────────────────────────────────────────────────────────── if (ctau > lambdaV0MaxCTau.value) { @@ -2248,6 +2655,16 @@ struct LambdaProtonBalanceFunction { if (std::abs(v0.mK0Short() - mK0PDG) <= lambdaV0KShortRejectMassWindow.value) { continue; } + // V0 cutflow bin 8: cTau and K0s rejection + if (v0DauPID_AsLambda) { + if constexpr (fillQA) { + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 8.0f); + } + } else { + if constexpr (fillQA) { + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 8.0f); + } + } // ───────────────────────────────────────────────────────────────── // ── Mass window selection: |m - mPDG| < lambdaV0MassWindow ───────── @@ -2255,102 +2672,66 @@ struct LambdaProtonBalanceFunction { const bool selAntiLambda = v0DauPID_AsAntiLambda && (std::abs(v0.mAntiLambda() - mLambdaPDG) < lambdaV0MassWindow.value); // ── Item 2: K0S mass fill after K0S rejection + after Lambda/AntiLambda selection ── if (selLambda) { - registryQaDetector.fill(HIST("Lambda/InvariantMass/h1f_mK0Short_afterLambdaSel"), v0.mK0Short()); + if constexpr (fillQA) { + registryQaDetector.fill(HIST("Lambda/InvariantMass/h1f_mK0Short_afterLambdaSel"), v0.mK0Short()); + } } if (selAntiLambda) { - registryQaDetector.fill(HIST("AntiLambda/InvariantMass/h1f_mK0Short_afterLambdaSel"), v0.mK0Short()); - } - // ───────────────────────────────────────────────────────────────── - - // ── pT bin index for per-bin histograms ────────────────────────── - const float pt = v0.pt(); - // VS Code: commented out unused variable. - // const float eta = v0.eta(); - // VS Code: commented out unused variable. - // const float pz = pt * std::sinh(eta); - const float y = v0DauPID_AsLambda ? v0.rapidity(1) : v0.rapidity(2); - int bin = -1; - for (size_t i = 0; i < ptEdges.size() - 1; ++i) { - if (pt >= ptEdges[i] && pt < ptEdges[i + 1]) { - bin = static_cast(i); - break; + if constexpr (fillQA) { + registryQaDetector.fill(HIST("AntiLambda/InvariantMass/h1f_mK0Short_afterLambdaSel"), v0.mK0Short()); } } // ───────────────────────────────────────────────────────────────── - // ── NoMassCut QA fills (PID + topo only) ───────────────────────── - // Note: fillLambdaQA/fillDaughterQA for 01_Raw were moved earlier in the - // loop (before the daughter DCA-to-PV cut) to give a true pre-cut view. - if (v0DauPID_AsLambda) { - if (bin >= 0) { - hMassLambdaPtBins[bin]->Fill(v0.mLambda()); - } - hMassLambdaMerged->Fill(v0.mLambda()); - registryQaDetector.fill(HIST("Lambda/InvariantMass/NoMassCut/h2f_mass_vs_pt"), pt, v0.mLambda()); - // ── Item 2: mass vs eta/phi acceptance uniformity ── - registryQaDetector.fill(HIST("Lambda/InvariantMass/h2f_mass_vs_eta"), v0.eta(), v0.mLambda()); - registryQaDetector.fill(HIST("Lambda/InvariantMass/h2f_mass_vs_phi"), v0.phi(), v0.mLambda()); - // ────────────────────────────────────────────────── - if (selLambda) { - registryQaDetector.fill(HIST("Lambda/InvariantMass/MassCut/h2f_mass_vs_pt"), pt, v0.mLambda()); - } - - // ── Extended y × pT Lambda invariant-mass fill ──────────────────── - // Use the same rapidity variable `y` computed above - // and the same pT variable `pt` = v0.pt() already in scope. - // Accept only -0.8 <= y <= -0.6 and 0.0 <= pT <= 2.5 GeV/c. - if (y >= kExtYMin && y <= kExtYMax && pt >= kExtPtMin && pt <= kExtPtMax) { - // Determine rapidity bin index - int iYext = static_cast((y - kExtYMin) / kExtYStep); - if (iYext >= kExtNyBins) { - iYext = kExtNyBins - 1; - } // clamp upper edge of last bin - // Determine pT bin index - int iPtext = static_cast((pt - kExtPtMin) / kExtPtStep); - if (iPtext >= kExtNptBins) { - iPtext = kExtNptBins - 1; - } // clamp upper edge of last bin - if (iYext >= 0 && iPtext >= 0) { - hMassLambdaExtended[iYext][iPtext]->Fill(v0.mLambda()); + if constexpr (fillQA) { + // ── pT bin index for per-bin histograms ────────────────────────── + const float pt = v0.pt(); + // VS Code: commented out unused variable. + // const float eta = v0.eta(); + // VS Code: commented out unused variable. + // const float pz = pt * std::sinh(eta); + int bin = -1; + for (size_t i = 0; i < state.ptEdges.size() - 1; ++i) { + if (pt >= state.ptEdges[i] && pt < state.ptEdges[i + 1]) { + bin = static_cast(i); + break; } } // ───────────────────────────────────────────────────────────────── - } - if (v0DauPID_AsAntiLambda) { - if (bin >= 0) { - hMassAntiLambdaPtBins[bin]->Fill(v0.mAntiLambda()); - } - hMassAntiLambdaMerged->Fill(v0.mAntiLambda()); - registryQaDetector.fill(HIST("AntiLambda/InvariantMass/NoMassCut/h2f_mass_vs_pt"), pt, v0.mAntiLambda()); - // ── Item 2: mass vs eta/phi acceptance uniformity ── - registryQaDetector.fill(HIST("AntiLambda/InvariantMass/h2f_mass_vs_eta"), v0.eta(), v0.mAntiLambda()); - registryQaDetector.fill(HIST("AntiLambda/InvariantMass/h2f_mass_vs_phi"), v0.phi(), v0.mAntiLambda()); - // ────────────────────────────────────────────────── - if (selAntiLambda) { - registryQaDetector.fill(HIST("AntiLambda/InvariantMass/MassCut/h2f_mass_vs_pt"), pt, v0.mAntiLambda()); - } - - // ── Extended y × pT AntiLambda invariant-mass fill ──────────────── - // Use the same rapidity variable `y` computed above - // and the same pT variable `pt` = v0.pt() already in scope. - // Accept only -0.8 <= y <= -0.6 and 0.0 <= pT <= 2.5 GeV/c. - if (y >= kExtYMin && y <= kExtYMax && pt >= kExtPtMin && pt <= kExtPtMax) { - int iYext = static_cast((y - kExtYMin) / kExtYStep); - if (iYext >= kExtNyBins) { - iYext = kExtNyBins - 1; - } // clamp upper edge of last bin - int iPtext = static_cast((pt - kExtPtMin) / kExtPtStep); - if (iPtext >= kExtNptBins) { - iPtext = kExtNptBins - 1; - } // clamp upper edge of last bin - if (iYext >= 0 && iPtext >= 0) { - hMassAntiLambdaExtended[iYext][iPtext]->Fill(v0.mAntiLambda()); + + // ── NoMassCut QA fills (PID + topo only) ───────────────────────── + // Note: fillLambdaQA/fillDaughterQA for 01_Raw were moved earlier in the + // loop (before the daughter DCA-to-PV cut) to give a true pre-cut view. + if (v0DauPID_AsLambda) { + if (bin >= 0) { + state.hMassLambdaPtBins[bin]->Fill(v0.mLambda()); + } + state.hMassLambdaMerged->Fill(v0.mLambda()); + registryQaDetector.fill(HIST("Lambda/InvariantMass/NoMassCut/h2f_mass_vs_pt"), pt, v0.mLambda()); + // ── Item 2: mass vs eta/phi acceptance uniformity ── + registryQaDetector.fill(HIST("Lambda/InvariantMass/h2f_mass_vs_eta"), v0.eta(), v0.mLambda()); + registryQaDetector.fill(HIST("Lambda/InvariantMass/h2f_mass_vs_phi"), v0.phi(), v0.mLambda()); + // ────────────────────────────────────────────────── + if (selLambda) { + registryQaDetector.fill(HIST("Lambda/InvariantMass/MassCut/h2f_mass_vs_pt"), pt, v0.mLambda()); + } + } + if (v0DauPID_AsAntiLambda) { + if (bin >= 0) { + state.hMassAntiLambdaPtBins[bin]->Fill(v0.mAntiLambda()); + } + state.hMassAntiLambdaMerged->Fill(v0.mAntiLambda()); + registryQaDetector.fill(HIST("AntiLambda/InvariantMass/NoMassCut/h2f_mass_vs_pt"), pt, v0.mAntiLambda()); + // ── Item 2: mass vs eta/phi acceptance uniformity ── + registryQaDetector.fill(HIST("AntiLambda/InvariantMass/h2f_mass_vs_eta"), v0.eta(), v0.mAntiLambda()); + registryQaDetector.fill(HIST("AntiLambda/InvariantMass/h2f_mass_vs_phi"), v0.phi(), v0.mAntiLambda()); + // ────────────────────────────────────────────────── + if (selAntiLambda) { + registryQaDetector.fill(HIST("AntiLambda/InvariantMass/MassCut/h2f_mass_vs_pt"), pt, v0.mAntiLambda()); } } // ───────────────────────────────────────────────────────────────── } - // ───────────────────────────────────────────────────────────────── - // // ── Daughter beta window veto (after NoMassCut fills, before Final) ─── //BETACUTCommented // // Raw and NoMassCut histograms above are filled for ALL topology-passing //BETACUTCommented // // candidates regardless of daughter beta. The veto here prevents V0s with //BETACUTCommented @@ -2379,124 +2760,192 @@ struct LambdaProtonBalanceFunction { // ── Step 3: Selected Lambda — populate veto set + rho1 + pT histos ─ if (selLambda) { - ++nSelLambda; + // V0 cutflow bin 9: mass window (pre-cleaning) + if constexpr (fillQA) { + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 9.0f); + } selectedLambdas.push_back(v0); v0DaughterTrackIds.insert(v0.posTrackId()); v0DaughterTrackIds.insert(v0.negTrackId()); - fillLambdaQA(LambdaQAStage::Final, v0, ctau, bgctau); - fillDaughterQA(DaughterQAStage::Final, v0PosDau); - fillDaughterQA(DaughterQAStage::Final, v0NegDau); - - if (cFillEtaSpace.value) { - registryRho.fill(HIST("h2_rho1_Lambda"), v0.eta(), v0.phi()); - } - registryRho.fill(HIST("h2_rho1_Lambda_y"), v0.rapidity(1), v0.phi()); - if (cs) { - cs->rho1_Lambda_y->Fill(v0.rapidity(1), v0.phi()); - if (cs->rho1_Lambda) { - cs->rho1_Lambda->Fill(v0.eta(), v0.phi()); - } - } - registryOther.fill(HIST("hPtSelLambda"), pt); - hPtSelLambda_pT015toMaxDefined->Fill(pt); + // Final QA, rho1 / pT spectra and nSelLambda are filled after shared-daughter cleaning (below). } if (selAntiLambda) { - ++nSelAntiLambda; + // V0 cutflow bin 9: mass window (pre-cleaning) + if constexpr (fillQA) { + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 9.0f); + } selectedAntiLambdas.push_back(v0); v0DaughterTrackIds.insert(v0.posTrackId()); v0DaughterTrackIds.insert(v0.negTrackId()); - fillLambdaQA(LambdaQAStage::Final, v0, ctau, bgctau); - fillDaughterQA(DaughterQAStage::Final, v0NegDau); - fillDaughterQA(DaughterQAStage::Final, v0PosDau); - - if (cFillEtaSpace.value) { - registryRho.fill(HIST("h2_rho1_AntiLambda"), v0.eta(), v0.phi()); - } - registryRho.fill(HIST("h2_rho1_AntiLambda_y"), v0.rapidity(2), v0.phi()); - if (cs) { - cs->rho1_AntiLambda_y->Fill(v0.rapidity(2), v0.phi()); - if (cs->rho1_AntiLambda) { - cs->rho1_AntiLambda->Fill(v0.eta(), v0.phi()); - } - } - registryOther.fill(HIST("hPtSelAntiLambda"), pt); - hPtSelAntiLambda_pT015toMaxDefined->Fill(pt); + // Final QA, rho1 / pT spectra and nSelAntiLambda are filled after shared-daughter cleaning (below). } // ───────────────────────────────────────────────────────────────── } // ====== End of Step 2+3 V0 validation loop ============================ - // ── Veto QA event counters (filled once per event) ─────────────────── + // ====== Shared-daughter cleaning (symmetric, over the Lambda ∪ AntiLambda union) ====== + // Two candidates sharing a positive OR a negative daughter track are BOTH rejected. + // v0DaughterTrackIds is intentionally left untouched (conservative primary veto). + [[maybe_unused]] const int nRawLambda = static_cast(selectedLambdas.size()); + [[maybe_unused]] const int nRawAntiLambda = static_cast(selectedAntiLambdas.size()); + + std::unordered_map posIdCount; + std::unordered_map negIdCount; + for (const auto& v : selectedLambdas) { + ++posIdCount[v.posTrackId()]; + ++negIdCount[v.negTrackId()]; + } + for (const auto& v : selectedAntiLambdas) { + ++posIdCount[v.posTrackId()]; + ++negIdCount[v.negTrackId()]; + } + + std::vector cleanLambdas; + std::vector cleanAntiLambdas; + for (const auto& v : selectedLambdas) { + if (posIdCount[v.posTrackId()] == 1 && negIdCount[v.negTrackId()] == 1) { + cleanLambdas.push_back(v); + } + } + for (const auto& v : selectedAntiLambdas) { + if (posIdCount[v.posTrackId()] == 1 && negIdCount[v.negTrackId()] == 1) { + cleanAntiLambdas.push_back(v); + } + } + selectedLambdas = std::move(cleanLambdas); + selectedAntiLambdas = std::move(cleanAntiLambdas); + + // V0 cutflow bin 10: survivors of shared-daughter cleaning + for ([[maybe_unused]] const auto& v : selectedLambdas) { + if constexpr (fillQA) { + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 10.0f); + } + } + for ([[maybe_unused]] const auto& v : selectedAntiLambdas) { + if constexpr (fillQA) { + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 10.0f); + } + } + + [[maybe_unused]] const int nSelLambda = static_cast(selectedLambdas.size()); + [[maybe_unused]] const int nSelAntiLambda = static_cast(selectedAntiLambdas.size()); + + // Cleaning diagnostics + if constexpr (fillQA) { + registryOther.fill(HIST("h1f_nLambda_beforeClean"), static_cast(nRawLambda)); + registryOther.fill(HIST("h1f_nLambda_rejectedClean"), static_cast(nRawLambda - nSelLambda)); + registryOther.fill(HIST("h1f_nLambda_afterClean"), static_cast(nSelLambda)); + registryOther.fill(HIST("h1f_nAntiLambda_beforeClean"), static_cast(nRawAntiLambda)); + registryOther.fill(HIST("h1f_nAntiLambda_rejectedClean"), static_cast(nRawAntiLambda - nSelAntiLambda)); + registryOther.fill(HIST("h1f_nAntiLambda_afterClean"), static_cast(nSelAntiLambda)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 1.0f, static_cast(nRawLambda)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 2.0f, static_cast(nRawLambda - nSelLambda)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 3.0f, static_cast(nSelLambda)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 4.0f, static_cast(nRawAntiLambda)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 5.0f, static_cast(nRawAntiLambda - nSelAntiLambda)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 6.0f, static_cast(nSelAntiLambda)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 7.0f, static_cast(nSelLambda) * static_cast(nSelLambda - 1)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 8.0f, static_cast(nSelAntiLambda) * static_cast(nSelAntiLambda - 1)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 9.0f, static_cast(nSelLambda) * static_cast(nSelAntiLambda)); + } + } + + template + void selectRecoProtons(TTracks const& tracks, + std::unordered_set const& v0DaughterTrackIds, + std::vector& selectedPrimProtons, + std::vector& selectedPrimAntiProtons) + { + // Local veto counters retain the BF counting convention. + int nProtonBeforeVeto = 0; + int nProtonRemovedVeto = 0; + int nAntiPBeforeVeto = 0; + int nAntiPRemovedVeto = 0; for (auto const& trk : tracks) { - // ── RAW Stage QA Fills (truly before any selection — kinematics, detector quality, - // TPC, and TOF all filled here before any cut fires) ─────────────────────────── - if (trk.sign() > 0) { - fillProtonKinematics(ProtonQAStage::RawAfterTrackSel, trk); - fillProtonDetectorQuality(ProtonQAStage::RawAfterTrackSel, trk); - if (trk.hasTPC()) { - fillProtonTpcQA(ProtonQAStage::RawAfterTrackSel, trk); - } - if (trk.hasTOF()) { - fillProtonTofQA(ProtonQAStage::RawAfterTrackSel, trk); - } - } else { - fillProtonKinematics(ProtonQAStage::RawAfterTrackSel, trk); - fillProtonDetectorQuality(ProtonQAStage::RawAfterTrackSel, trk); - if (trk.hasTPC()) { - fillProtonTpcQA(ProtonQAStage::RawAfterTrackSel, trk); - } - if (trk.hasTOF()) { - fillProtonTofQA(ProtonQAStage::RawAfterTrackSel, trk); + if constexpr (fillQA) { + // ── RAW Stage QA Fills (truly before any selection — kinematics, detector quality, + // TPC, and TOF all filled here before any cut fires) ─────────────────────────── + if (trk.sign() > 0) { + fillProtonKinematics(ProtonQAStage::RawAfterTrackSel, trk); + fillProtonDetectorQuality(ProtonQAStage::RawAfterTrackSel, trk); + if (trk.hasTPC()) { + fillProtonTpcQA(ProtonQAStage::RawAfterTrackSel, trk); + } + if (trk.hasTOF()) { + fillProtonTofQA(ProtonQAStage::RawAfterTrackSel, trk); + } + } else { + fillProtonKinematics(ProtonQAStage::RawAfterTrackSel, trk); + fillProtonDetectorQuality(ProtonQAStage::RawAfterTrackSel, trk); + if (trk.hasTPC()) { + fillProtonTpcQA(ProtonQAStage::RawAfterTrackSel, trk); + } + if (trk.hasTOF()) { + fillProtonTofQA(ProtonQAStage::RawAfterTrackSel, trk); + } } + // ───────────────────────────────────────────────────────────────── } - // ───────────────────────────────────────────────────────────────── - // ── Track quality cuts ──────────────────────────────────────────── - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 1.0f); + if constexpr (fillQA) { + fillCutFlow(1.0f, trk); + } if (!trk.hasTPC()) { continue; } - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 2.0f); + if constexpr (fillQA) { + fillCutFlow(2.0f, trk); + } if (trk.tpcNClsCrossedRows() < pProtonTPCMinRows.value) { continue; } - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 3.0f); + if constexpr (fillQA) { + fillCutFlow(3.0f, trk); + } if (trk.tpcCrossedRowsOverFindableCls() < pProtonTPCMinRowsOverFindable.value) { continue; } - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 4.0f); + if constexpr (fillQA) { + fillCutFlow(4.0f, trk); + } if (trk.tpcChi2NCl() > pProtonTPCMaxChi2PerCluster.value) { continue; } - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 5.0f); + if constexpr (fillQA) { + fillCutFlow(5.0f, trk); + } if (trk.itsNCls() < pProtonITSMinClusters.value) { continue; } - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 6.0f); + if constexpr (fillQA) { + fillCutFlow(6.0f, trk); + } if (trk.itsChi2NCl() > pProtonITSMaxChi2PerCluster.value) { continue; } - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 7.0f); + if constexpr (fillQA) { + fillCutFlow(7.0f, trk); + } if (trk.tpcSignal() < cPPMinTpcSignal.value) { continue; } - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 8.0f); + if constexpr (fillQA) { + fillCutFlow(8.0f, trk); + } // ───────────────────────────────────────────────────────────────── - // ── p_TPC and pT acceptance cuts ───────────────────────────────────────────── - const float pTPC = trk.tpcInnerParam(); - if (pTPC < pProtonMinP.value || pTPC > pProtonMaxP.value) { - continue; - } + // ── pT acceptance cut ───────────────────────────────────────────── if (trk.pt() < pProtonMinPt.value || trk.pt() > pProtonMaxPt.value) { continue; } - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 9.0f); + if constexpr (fillQA) { + fillCutFlow(9.0f, trk); + } // ───────────────────────────────────────────────────────────────── // ── Proton kinematic acceptance ─────────────────────────────────── @@ -2506,7 +2955,9 @@ struct LambdaProtonBalanceFunction { if (std::abs(y) > pProtonMaxY.value) { continue; } - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 10.0f); + if constexpr (fillQA) { + fillCutFlow(10.0f, trk); + } // ───────────────────────────────────────────────────────────────── // ── DCA cuts ───────────────────────────────────────────────────── const bool passDCA = (std::abs(trk.dcaXY()) < pProtonMaxDCAxy.value) && @@ -2514,102 +2965,126 @@ struct LambdaProtonBalanceFunction { if (!passDCA) { continue; } - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 11.0f); + if constexpr (fillQA) { + fillCutFlow(11.0f, trk); + } // ───────────────────────────────────────────────────────────────── - // FIX-4: ProtonCounts_byPID_Check — protons only (sign > 0 guard added) - if (trk.sign() > 0) { - const float pt = trk.pt(); - const float nsTPC = std::abs(trk.tpcNSigmaPr()); + if constexpr (fillQA) { + // FIX-4: ProtonCounts_byPID_Check — protons only (sign > 0 guard added) + if (trk.sign() > 0) { + const float pt = trk.pt(); + const float nsTPC = std::abs(trk.tpcNSigmaPr()); - if (nsTPC < pProtonTPCNsigma.value) { - h_TPC->Fill(pt); - } - if (trk.hasTOF() && nsTPC < pProtonTPCNsigma.value && - std::abs(trk.tofNSigmaPr()) < pProtonTOFNsigma.value) { - h_TPCandTOF->Fill(pt); - } - if (nsTPC < pProtonTPCNsigma.value) { - if (!trk.hasTOF() || std::abs(trk.tofNSigmaPr()) < pProtonTOFNsigma.value) { - h_TPCorTOF->Fill(pt); + if (nsTPC < pProtonTPCNsigma.value) { + state.h_TPC->Fill(pt); + } + if (trk.hasTOF() && nsTPC < pProtonTPCNsigma.value && + std::abs(trk.tofNSigmaPr()) < pProtonTOFNsigma.value) { + state.h_TPCandTOF->Fill(pt); + } + if (nsTPC < pProtonTPCNsigma.value) { + if (!trk.hasTOF() || std::abs(trk.tofNSigmaPr()) < pProtonTOFNsigma.value) { + state.h_TPCorTOF->Fill(pt); + } } } - } - // ───────────────────────────────────────────────────────────────── + // ───────────────────────────────────────────────────────────────── - // TPC PID pass QA fill (intermediate stage, before full selection) - if (std::abs(trk.tpcNSigmaPr()) < pProtonTPCNsigma.value) { - if (trk.sign() > 0) { - fillProtonKinematics(ProtonQAStage::TpcPid, trk); - fillProtonDetectorQuality(ProtonQAStage::TpcPid, trk); - fillProtonTpcQA(ProtonQAStage::TpcPid, trk); - if (trk.hasTOF()) { - fillProtonTofQA(ProtonQAStage::TpcPid, trk); - } - } else { - fillProtonKinematics(ProtonQAStage::TpcPid, trk); - fillProtonDetectorQuality(ProtonQAStage::TpcPid, trk); - fillProtonTpcQA(ProtonQAStage::TpcPid, trk); - if (trk.hasTOF()) { - fillProtonTofQA(ProtonQAStage::TpcPid, trk); + // TPC PID pass QA fill (intermediate stage, before full selection) + if (std::abs(trk.tpcNSigmaPr()) < pProtonTPCNsigma.value) { + if (trk.sign() > 0) { + fillProtonKinematics(ProtonQAStage::TpcPid, trk); + fillProtonDetectorQuality(ProtonQAStage::TpcPid, trk); + fillProtonTpcQA(ProtonQAStage::TpcPid, trk); + if (trk.hasTOF()) { + fillProtonTofQA(ProtonQAStage::TpcPid, trk); + } + } else { + fillProtonKinematics(ProtonQAStage::TpcPid, trk); + fillProtonDetectorQuality(ProtonQAStage::TpcPid, trk); + fillProtonTpcQA(ProtonQAStage::TpcPid, trk); + if (trk.hasTOF()) { + fillProtonTofQA(ProtonQAStage::TpcPid, trk); + } } } - } - // TPC+TOF PID pass QA fill (intermediate stage, before full selection) - if (std::abs(trk.tpcNSigmaPr()) < pProtonTPCNsigma.value && trk.hasTOF() && std::abs(trk.tofNSigmaPr()) < pProtonTOFNsigma.value) { - if (trk.sign() > 0) { - fillProtonKinematics(ProtonQAStage::TpcTofPid, trk); - fillProtonDetectorQuality(ProtonQAStage::TpcTofPid, trk); - fillProtonTpcQA(ProtonQAStage::TpcTofPid, trk); - fillProtonTofQA(ProtonQAStage::TpcTofPid, trk); - } else { - fillProtonKinematics(ProtonQAStage::TpcTofPid, trk); - fillProtonDetectorQuality(ProtonQAStage::TpcTofPid, trk); - fillProtonTpcQA(ProtonQAStage::TpcTofPid, trk); - fillProtonTofQA(ProtonQAStage::TpcTofPid, trk); + // TPC+TOF PID pass QA fill (intermediate stage, before full selection) + if (std::abs(trk.tpcNSigmaPr()) < pProtonTPCNsigma.value && trk.hasTOF() && std::abs(trk.tofNSigmaPr()) < pProtonTOFNsigma.value) { + if (trk.sign() > 0) { + fillProtonKinematics(ProtonQAStage::TpcTofPid, trk); + fillProtonDetectorQuality(ProtonQAStage::TpcTofPid, trk); + fillProtonTpcQA(ProtonQAStage::TpcTofPid, trk); + fillProtonTofQA(ProtonQAStage::TpcTofPid, trk); + } else { + fillProtonKinematics(ProtonQAStage::TpcTofPid, trk); + fillProtonDetectorQuality(ProtonQAStage::TpcTofPid, trk); + fillProtonTpcQA(ProtonQAStage::TpcTofPid, trk); + fillProtonTofQA(ProtonQAStage::TpcTofPid, trk); + } } } - // ── Main PID selection ──────────────────────────────────────────── if (!passesPrimProtonPid(trk)) { continue; } - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 12.0f); - - // TOF Matching Fraction check on selected protons - registryQaDetector.fill(HIST("Selection/TOF_Matching/hTOFMatchedFractionVsPt"), trk.pt(), trk.hasTOF() ? 1.0f : 0.0f); - // ── Item 4: 2D TOF-matching efficiency map for primary protons ────── - registryQaDetector.fill(HIST("Selection/TOF_Matching/h2f_TOFMatchedFractionVsEtaPhi"), - trk.eta(), trk.phi(), trk.hasTOF() ? 1.0f : 0.0f); - // ───────────────────────────────────────────────────────────────── + if constexpr (fillQA) { + fillCutFlow(12.0f, trk); + } - // ── PID dEdx monitoring ─────────────────────────────────────────── - // vs total-p: unchanged — intentional total-momentum view - // vs pTPC (tpcInnerParam): added alongside for proton-selection QA comparison - if (trk.sign() > 0) { - registryPid.fill(HIST("Proton/hTPCdEdxVsP_selected"), trk.p(), trk.tpcSignal()); - registryPid.fill(HIST("Proton/hTPCdEdxVsPTPC_selected"), trk.tpcInnerParam(), trk.tpcSignal()); - if (trk.hasTOF()) { - registryPid.fill(HIST("Proton/hTPCdEdxVsP_TPCTOF"), trk.p(), trk.tpcSignal()); - registryPid.fill(HIST("Proton/hTPCdEdxVsPTPC_TPCTOF"), trk.tpcInnerParam(), trk.tpcSignal()); - } else { - registryPid.fill(HIST("Proton/hTPCdEdxVsP_TPConly"), trk.p(), trk.tpcSignal()); - registryPid.fill(HIST("Proton/hTPCdEdxVsPTPC_TPConly"), trk.tpcInnerParam(), trk.tpcSignal()); + // pi/K/e contaminant veto + if (!passesContaminantVeto(trk)) { + bool vetoUsedTOF = false; + const int vetoMask = contaminantVetoMask(trk, vetoUsedTOF); + constexpr int NumContaminantSpecies = 3; + for (int ib = 0; ib < NumContaminantSpecies; ++ib) { + if (vetoMask & (1 << ib)) { + if constexpr (fillQA) { + fillCutFlow2D((vetoUsedTOF ? 19.0f : 16.0f) + ib, trk); + } + } } - } else { - registryPid.fill(HIST("AntiProton/hTPCdEdxVsP_selected"), trk.p(), trk.tpcSignal()); - registryPid.fill(HIST("AntiProton/hTPCdEdxVsPTPC_selected"), trk.tpcInnerParam(), trk.tpcSignal()); - if (trk.hasTOF()) { - registryPid.fill(HIST("AntiProton/hTPCdEdxVsP_TPCTOF"), trk.p(), trk.tpcSignal()); - registryPid.fill(HIST("AntiProton/hTPCdEdxVsPTPC_TPCTOF"), trk.tpcInnerParam(), trk.tpcSignal()); + continue; + } + if constexpr (fillQA) { + fillCutFlow(13.0f, trk); + } + + if constexpr (fillQA) { + // TOF Matching Fraction check on selected protons + registryQaDetector.fill(HIST("Selection/TOF_Matching/hTOFMatchedFractionVsPt"), trk.pt(), trk.hasTOF() ? 1.0f : 0.0f); + // ── Item 4: 2D TOF-matching efficiency map for primary protons ────── + registryQaDetector.fill(HIST("Selection/TOF_Matching/h2f_TOFMatchedFractionVsEtaPhi"), + trk.eta(), trk.phi(), trk.hasTOF() ? 1.0f : 0.0f); + // ───────────────────────────────────────────────────────────────── + + // ── PID dEdx monitoring ─────────────────────────────────────────── + // vs total-p: unchanged — intentional total-momentum view + // vs pTPC (tpcInnerParam): added alongside for proton-selection QA comparison + if (trk.sign() > 0) { + registryPid.fill(HIST("Proton/hTPCdEdxVsP_selected"), trk.p(), trk.tpcSignal()); + registryPid.fill(HIST("Proton/hTPCdEdxVsPTPC_selected"), trk.tpcInnerParam(), trk.tpcSignal()); + if (trk.hasTOF()) { + registryPid.fill(HIST("Proton/hTPCdEdxVsP_TPCTOF"), trk.p(), trk.tpcSignal()); + registryPid.fill(HIST("Proton/hTPCdEdxVsPTPC_TPCTOF"), trk.tpcInnerParam(), trk.tpcSignal()); + } else { + registryPid.fill(HIST("Proton/hTPCdEdxVsP_TPConly"), trk.p(), trk.tpcSignal()); + registryPid.fill(HIST("Proton/hTPCdEdxVsPTPC_TPConly"), trk.tpcInnerParam(), trk.tpcSignal()); + } } else { - registryPid.fill(HIST("AntiProton/hTPCdEdxVsP_TPConly"), trk.p(), trk.tpcSignal()); - registryPid.fill(HIST("AntiProton/hTPCdEdxVsPTPC_TPConly"), trk.tpcInnerParam(), trk.tpcSignal()); + registryPid.fill(HIST("AntiProton/hTPCdEdxVsP_selected"), trk.p(), trk.tpcSignal()); + registryPid.fill(HIST("AntiProton/hTPCdEdxVsPTPC_selected"), trk.tpcInnerParam(), trk.tpcSignal()); + if (trk.hasTOF()) { + registryPid.fill(HIST("AntiProton/hTPCdEdxVsP_TPCTOF"), trk.p(), trk.tpcSignal()); + registryPid.fill(HIST("AntiProton/hTPCdEdxVsPTPC_TPCTOF"), trk.tpcInnerParam(), trk.tpcSignal()); + } else { + registryPid.fill(HIST("AntiProton/hTPCdEdxVsP_TPConly"), trk.p(), trk.tpcSignal()); + registryPid.fill(HIST("AntiProton/hTPCdEdxVsPTPC_TPConly"), trk.tpcInnerParam(), trk.tpcSignal()); + } } + // ───────────────────────────────────────────────────────────────── } - // ───────────────────────────────────────────────────────────────── - // ── V0-daughter veto: reject tracks shared with any V0 daughter ─── // Count before veto, check veto set, then classify or skip. // This MUST appear before push_back so that every pp histogram @@ -2630,51 +3105,61 @@ struct LambdaProtonBalanceFunction { } continue; } - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 13.0f); + if constexpr (fillQA) { + fillCutFlow(14.0f, trk); + } // ───────────────────────────────────────────────────────────────── // ── Classify into proton / antiproton lists ─────────────────────── - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 14.0f); + if constexpr (fillQA) { + fillCutFlow(15.0f, trk); + } // VS Code: commented out unused variables. // const float pz = trk.pt() * std::sinh(trk.eta()); // const float eta = trk.eta(); if (trk.sign() > 0) { selectedPrimProtons.push_back(trk); - fillProtonKinematics(ProtonQAStage::FinalSelected, trk); - fillProtonDetectorQuality(ProtonQAStage::FinalSelected, trk); - fillProtonTpcQA(ProtonQAStage::FinalSelected, trk); - // TOF QA only when track was accepted through the TPC+TOF branch: - // p > PProtonTPC_TOFSwitchP guarantees hasTOF() and nSigmaTOF passed (beta window cut removed). //BETACUTCommented - if (pTPC >= pProtonTPCTOFSwitchP.value) { - fillProtonTofQA(ProtonQAStage::FinalSelected, trk); - } + if constexpr (fillQA) { + fillProtonKinematics(ProtonQAStage::FinalSelected, trk); + fillProtonDetectorQuality(ProtonQAStage::FinalSelected, trk); + fillProtonTpcQA(ProtonQAStage::FinalSelected, trk); + // TOF QA only when track was accepted through the TPC+TOF branch: + // pt > pProtonTPCTOFSwitchPt guarantees hasTOF() and nSigmaTOF passed (beta window cut removed). //BETACUTCommented + if (trk.pt() >= pProtonTPCTOFSwitchPt.value) { + fillProtonTofQA(ProtonQAStage::FinalSelected, trk); + } - registryOther.fill(HIST("hPtSelectedPrimProton"), trk.pt()); - hPtSelectedPrimProton_pT015toMaxDefined->Fill(trk.pt()); + registryOther.fill(HIST("hPtSelectedPrimProton"), trk.pt()); + state.hPtSelectedPrimProton_AllPt->Fill(trk.pt()); + } } else { selectedPrimAntiProtons.push_back(trk); - fillProtonKinematics(ProtonQAStage::FinalSelected, trk); - fillProtonDetectorQuality(ProtonQAStage::FinalSelected, trk); - fillProtonTpcQA(ProtonQAStage::FinalSelected, trk); - // TOF QA only when track was accepted through the TPC+TOF branch: - // p > pProtonTPCTOFSwitchP guarantees hasTOF() and nSigmaTOF passed (beta window cut removed). //BETACUTCommented - if (pTPC >= pProtonTPCTOFSwitchP.value) { - fillProtonTofQA(ProtonQAStage::FinalSelected, trk); - } + if constexpr (fillQA) { + fillProtonKinematics(ProtonQAStage::FinalSelected, trk); + fillProtonDetectorQuality(ProtonQAStage::FinalSelected, trk); + fillProtonTpcQA(ProtonQAStage::FinalSelected, trk); + // TOF QA only when track was accepted through the TPC+TOF branch: + // pt > pProtonTPCTOFSwitchPt guarantees hasTOF() and nSigmaTOF passed (beta window cut removed). //BETACUTCommented + if (trk.pt() >= pProtonTPCTOFSwitchPt.value) { + fillProtonTofQA(ProtonQAStage::FinalSelected, trk); + } - registryOther.fill(HIST("hPtSelectedPrimAntiProton"), trk.pt()); - hPtSelectedPrimAntiProton_pT015toMaxDefined->Fill(trk.pt()); + registryOther.fill(HIST("hPtSelectedPrimAntiProton"), trk.pt()); + state.hPtSelectedPrimAntiProton_AllPt->Fill(trk.pt()); + } } // ───────────────────────────────────────────────────────────────── } // ── Fill veto QA histograms (once per event, after the track loop) ── - registryOther.fill(HIST("hNPrimProtonBeforeVeto"), static_cast(nProtonBeforeVeto)); - registryOther.fill(HIST("hNPrimProtonRemovedByVeto"), static_cast(nProtonRemovedVeto)); - registryOther.fill(HIST("hNPrimProtonAfterVeto"), static_cast(nProtonBeforeVeto - nProtonRemovedVeto)); - registryOther.fill(HIST("hNPrimAntiProtonBeforeVeto"), static_cast(nAntiPBeforeVeto)); - registryOther.fill(HIST("hNPrimAntiProtonRemovedByVeto"), static_cast(nAntiPRemovedVeto)); - registryOther.fill(HIST("hNPrimAntiProtonAfterVeto"), static_cast(nAntiPBeforeVeto - nAntiPRemovedVeto)); + if constexpr (fillQA) { + registryOther.fill(HIST("hNPrimProtonBeforeVeto"), static_cast(nProtonBeforeVeto)); + registryOther.fill(HIST("hNPrimProtonRemovedByVeto"), static_cast(nProtonRemovedVeto)); + registryOther.fill(HIST("hNPrimProtonAfterVeto"), static_cast(nProtonBeforeVeto - nProtonRemovedVeto)); + registryOther.fill(HIST("hNPrimAntiProtonBeforeVeto"), static_cast(nAntiPBeforeVeto)); + registryOther.fill(HIST("hNPrimAntiProtonRemovedByVeto"), static_cast(nAntiPRemovedVeto)); + registryOther.fill(HIST("hNPrimAntiProtonAfterVeto"), static_cast(nAntiPBeforeVeto - nAntiPRemovedVeto)); + } // ───────────────────────────────────────────────────────────────────── // ── ProtonVetoQA: fill four dedicated per-event histograms ─────────── @@ -2684,7 +3169,7 @@ struct LambdaProtonBalanceFunction { // N_after_veto = nProtonBeforeVeto - nProtonRemovedVeto // = size of selectedPrimProtons, used by rho1(p), rho2(pp), rho2(p pbar) // fraction = N_removed / N_before (0 when N_before = 0, avoids divide-by-zero) - { + if constexpr (fillQA) { const int nBefore = nProtonBeforeVeto; const int nRemoved = nProtonRemovedVeto; const int nAfter = nBefore - nRemoved; @@ -2696,38 +3181,252 @@ struct LambdaProtonBalanceFunction { registryProtonVetoQA.fill(HIST("h1f_nAfterVeto"), static_cast(nAfter)); registryProtonVetoQA.fill(HIST("h1f_fractionRemoved"), fraction); // Accumulate run-level totals for endOfStream() summary. - runTotalProtonBeforeVeto += nBefore; - runTotalProtonRemovedVeto += nRemoved; - runTotalAntiPBeforeVeto += nAntiPBeforeVeto; - runTotalAntiPRemovedVeto += nAntiPRemovedVeto; + state.runTotalProtonBeforeVeto += nBefore; + state.runTotalProtonRemovedVeto += nRemoved; + state.runTotalAntiPBeforeVeto += nAntiPBeforeVeto; + state.runTotalAntiPRemovedVeto += nAntiPRemovedVeto; } // ───────────────────────────────────────────────────────────────────── + } - // ── Single-particle density for protons (eta and y) ────────────────── - for (auto const& primProton : selectedPrimProtons) { - if (cFillEtaSpace.value) { - registryRho.fill(HIST("h2_rho1_Proton"), primProton.eta(), primProton.phi()); + void fillStage23Counts(int64_t /*mcCollisionId*/, + std::vector const& /*selectedLambdas*/, + std::vector const& /*selectedAntiLambdas*/, + std::vector const& /*selectedPrimProtons*/, + std::vector const& /*selectedPrimAntiProtons*/) + { + } + + template + void fillStage23Counts(int64_t mcCollisionId, + std::vector const& selectedLambdas, + std::vector const& selectedAntiLambdas, + std::vector const& selectedPrimProtons, + std::vector const& selectedPrimAntiProtons, + [[maybe_unused]] float cent = 0.f) + { + // Stage 2 and Stage 3 counting; the selected reconstructed containers remain unchanged. + // Efficiency mode fills only Rec* at the exact Stage 3 acceptance point, + // with reconstructed kinematics and unit weight, without filling BF counters. + int nLambdaPrimSec = 0; + int nAntiLambdaPrimSec = 0; + int nProtonPrimSec = 0; + int nAntiProtonPrimSec = 0; + int nLambdaPrim = 0; + int nAntiLambdaPrim = 0; + int nProtonPrim = 0; + int nAntiProtonPrim = 0; + + for (const auto& candidate : selectedLambdas) { + if (!candidate.has_mcParticle()) { + continue; + } + const auto* truth = static_cast(candidate).getCurrent(); + const int64_t id = candidate.mcParticleId(); + if (!usableMCIndex(truth, id)) { + continue; + } + const auto mc = truth->rawIteratorAt(id - truth->offset()); + if (mc.mcCollisionId() != mcCollisionId) { + continue; + } + if (mc.pdgCode() != PDG_t::kLambda0) { + continue; + } + ++nLambdaPrimSec; + if (mc.isPhysicalPrimary()) { + ++nLambdaPrim; + if constexpr (fillEfficiency) { + registryEff.fill(HIST("RecLambda"), cent, candidate.pt(), candidate.rapidity(1)); + } + } + } + + for (const auto& candidate : selectedAntiLambdas) { + if (!candidate.has_mcParticle()) { + continue; } + const auto* truth = static_cast(candidate).getCurrent(); + const int64_t id = candidate.mcParticleId(); + if (!usableMCIndex(truth, id)) { + continue; + } + const auto mc = truth->rawIteratorAt(id - truth->offset()); + if (mc.mcCollisionId() != mcCollisionId) { + continue; + } + if (mc.pdgCode() != PDG_t::kLambda0Bar) { + continue; + } + ++nAntiLambdaPrimSec; + if (mc.isPhysicalPrimary()) { + ++nAntiLambdaPrim; + if constexpr (fillEfficiency) { + registryEff.fill(HIST("RecAntiLambda"), cent, candidate.pt(), candidate.rapidity(2)); + } + } + } + + for (const auto& candidate : selectedPrimProtons) { + if (!candidate.has_mcParticle()) { + continue; + } + const auto* truth = static_cast(candidate).getCurrent(); + const int64_t id = candidate.mcParticleId(); + if (!usableMCIndex(truth, id)) { + continue; + } + const auto mc = truth->rawIteratorAt(id - truth->offset()); + if (mc.mcCollisionId() != mcCollisionId) { + continue; + } + if (mc.pdgCode() != PDG_t::kProton) { + continue; + } + ++nProtonPrimSec; + if (mc.isPhysicalPrimary()) { + ++nProtonPrim; + if constexpr (fillEfficiency) { + registryEff.fill(HIST("RecProton"), cent, candidate.pt(), protonRapidity(candidate)); + } + } + } + + for (const auto& candidate : selectedPrimAntiProtons) { + if (!candidate.has_mcParticle()) { + continue; + } + const auto* truth = static_cast(candidate).getCurrent(); + const int64_t id = candidate.mcParticleId(); + if (!usableMCIndex(truth, id)) { + continue; + } + const auto mc = truth->rawIteratorAt(id - truth->offset()); + if (mc.mcCollisionId() != mcCollisionId) { + continue; + } + if (mc.pdgCode() != PDG_t::kProtonBar) { + continue; + } + ++nAntiProtonPrimSec; + if (mc.isPhysicalPrimary()) { + ++nAntiProtonPrim; + if constexpr (fillEfficiency) { + registryEff.fill(HIST("RecAntiProton"), cent, candidate.pt(), protonRapidity(candidate)); + } + } + } + + if constexpr (!fillEfficiency) { + registryOther.fill(HIST("hN_Lambda_PrimSec_reco"), static_cast(nLambdaPrimSec)); + registryOther.fill(HIST("hN_AntiLambda_PrimSec_reco"), static_cast(nAntiLambdaPrimSec)); + registryOther.fill(HIST("hN_Proton_PrimSec_reco"), static_cast(nProtonPrimSec)); + registryOther.fill(HIST("hN_AntiProton_PrimSec_reco"), static_cast(nAntiProtonPrimSec)); + registryOther.fill(HIST("hN_Lambda_prim_reco"), static_cast(nLambdaPrim)); + registryOther.fill(HIST("hN_AntiLambda_prim_reco"), static_cast(nAntiLambdaPrim)); + registryOther.fill(HIST("hN_Proton_prim_reco"), static_cast(nProtonPrim)); + registryOther.fill(HIST("hN_AntiProton_prim_reco"), static_cast(nAntiProtonPrim)); + } + } + + // Reconstructed candidates are compared with generated primary particles; + // residual background and feed-down can prevent full closure. + template + void runRecoAnalysis(TCollision const& collision, TV0s const& V0s, + TTracks const& tracks, float cent, CentRho2Set* cs, [[maybe_unused]] int64_t mcCollisionId = -1) + { + // ====== Step 1: Initialise containers ====================================== + std::unordered_set v0DaughterTrackIds; + v0DaughterTrackIds.reserve(64); + std::vector selectedPrimProtons; + std::vector selectedPrimAntiProtons; + std::vector selectedLambdas; + std::vector selectedAntiLambdas; + + int nSelLambda = 0; + int nSelAntiLambda = 0; + int nPairs_Lambda_PrimProton = 0; + int nPairs_Lambda_PrimAntiProton = 0; + int nPairs_AntiLambda_PrimProton = 0; + int nPairs_AntiLambda_PrimAntiProton = 0; + + selectRecoV0s(collision, V0s, selectedLambdas, selectedAntiLambdas, v0DaughterTrackIds); + nSelLambda = static_cast(selectedLambdas.size()); + nSelAntiLambda = static_cast(selectedAntiLambdas.size()); + constexpr float mLambdaPDG = o2::constants::physics::MassLambda; + + // rho1 and pT spectra from the cleaned sample (same sample as rho2) + for (const auto& v0 : selectedLambdas) { + // Final QA on the cleaned sample (same sample as rho1 / rho2) + { + const float ctauFinal = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * mLambdaPDG; + const float bgctauFinal = ctauFinal * v0.p() / mLambdaPDG; + const auto& dauPos = v0.template posTrack_as(); + const auto& dauNeg = v0.template negTrack_as(); + fillLambdaQA(LambdaQAStage::Final, v0, ctauFinal, bgctauFinal); + fillDaughterQA(DaughterQAStage::Final, dauPos); + fillDaughterQA(DaughterQAStage::Final, dauNeg); + } + const float wL = getEffWeight(state.invEffL, cent, v0.pt(), v0.rapidity(1)); + registryRho.fill(HIST("h2_rho1_Lambda_y"), v0.rapidity(1), v0.phi(), wL); + if (cs) { + cs->rho1_Lambda_y->Fill(v0.rapidity(1), v0.phi(), wL); + } + registryOther.fill(HIST("hEffWeightLambda"), wL); + if (wL == 0.f) { + registryOther.fill(HIST("hEffWeightZeroCount"), 3.f); + } + registryOther.fill(HIST("hPtSelLambda"), v0.pt()); + state.hPtSelLambda_AllPt->Fill(v0.pt()); + } + for (const auto& v0 : selectedAntiLambdas) { + // Final QA on the cleaned sample (same sample as rho1 / rho2) + { + const float ctauFinal = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * mLambdaPDG; + const float bgctauFinal = ctauFinal * v0.p() / mLambdaPDG; + const auto& dauPos = v0.template posTrack_as(); + const auto& dauNeg = v0.template negTrack_as(); + fillLambdaQA(LambdaQAStage::Final, v0, ctauFinal, bgctauFinal); + fillDaughterQA(DaughterQAStage::Final, dauNeg); + fillDaughterQA(DaughterQAStage::Final, dauPos); + } + const float wAL = getEffWeight(state.invEffAL, cent, v0.pt(), v0.rapidity(2)); + registryRho.fill(HIST("h2_rho1_AntiLambda_y"), v0.rapidity(2), v0.phi(), wAL); + if (cs) { + cs->rho1_AntiLambda_y->Fill(v0.rapidity(2), v0.phi(), wAL); + } + if (wAL == 0.f) { + registryOther.fill(HIST("hEffWeightZeroCount"), 4.f); + } + registryOther.fill(HIST("hPtSelAntiLambda"), v0.pt()); + state.hPtSelAntiLambda_AllPt->Fill(v0.pt()); + } + // ====== End of shared-daughter cleaning =============================== + + selectRecoProtons(tracks, v0DaughterTrackIds, selectedPrimProtons, selectedPrimAntiProtons); + + // ── Single-particle density for protons (y) ────────────────── + for (auto const& primProton : selectedPrimProtons) { const float yp = protonRapidity(primProton); - registryRho.fill(HIST("h2_rho1_Proton_y"), yp, primProton.phi()); + const float wP = getEffWeight(state.invEffP, cent, primProton.pt(), yp); + registryRho.fill(HIST("h2_rho1_Proton_y"), yp, primProton.phi(), wP); if (cs) { - cs->rho1_Proton_y->Fill(yp, primProton.phi()); - if (cs->rho1_Proton) { - cs->rho1_Proton->Fill(primProton.eta(), primProton.phi()); - } + cs->rho1_Proton_y->Fill(yp, primProton.phi(), wP); + } + registryOther.fill(HIST("hEffWeightProton"), wP); + if (wP == 0.f) { + registryOther.fill(HIST("hEffWeightZeroCount"), 1.f); } } for (auto const& primAntiProton : selectedPrimAntiProtons) { - if (cFillEtaSpace.value) { - registryRho.fill(HIST("h2_rho1_AntiProton"), primAntiProton.eta(), primAntiProton.phi()); - } const float yap = protonRapidity(primAntiProton); - registryRho.fill(HIST("h2_rho1_AntiProton_y"), yap, primAntiProton.phi()); + const float wAP = getEffWeight(state.invEffAP, cent, primAntiProton.pt(), yap); + registryRho.fill(HIST("h2_rho1_AntiProton_y"), yap, primAntiProton.phi(), wAP); if (cs) { - cs->rho1_AntiProton_y->Fill(yap, primAntiProton.phi()); - if (cs->rho1_AntiProton) { - cs->rho1_AntiProton->Fill(primAntiProton.eta(), primAntiProton.phi()); - } + cs->rho1_AntiProton_y->Fill(yap, primAntiProton.phi(), wAP); + } + if (wAP == 0.f) { + registryOther.fill(HIST("hEffWeightZeroCount"), 2.f); } } // ───────────────────────────────────────────────────────────────────── @@ -2747,7 +3446,7 @@ struct LambdaProtonBalanceFunction { antiProtonInfo.push_back(makeProtonInfo(t)); } - // ── pp rho2 fills (eta and y) ─────────────────────────────────────────── + // ── pp rho2 fills (y) ─────────────────────────────────────────────────── for (auto const& p1 : selectedPrimProtons) { const float y1_pp = protonRapidity(p1); for (auto const& p2 : selectedPrimProtons) { @@ -2775,7 +3474,7 @@ struct LambdaProtonBalanceFunction { const float E2 = std::sqrt(p2mag * p2mag + kMassProton * kMassProton); const float qinvPP = computeQinv(p1.px(), p1.py(), p1.pz(), E1, p2.px(), p2.py(), p2.pz(), E2); registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_PP_Before_qinvCut"), qinvPP); - if (!failsQinvCut(qinvPP)) { + if (passesQinvQA(qinvPP)) { registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_PP_After_qinvCut"), qinvPP); } } @@ -2807,21 +3506,16 @@ struct LambdaProtonBalanceFunction { } registryCorrelationQA.fill(HIST("QA3/PairCleaning/h1f_pairCleaning"), 4); } - const int idx1 = unrolledIndex(p1.eta(), p1.phi()); - const int idx2 = unrolledIndex(p2.eta(), p2.phi()); const float y2_pp = protonRapidity(p2); const int idxY1 = unrolledIndexY(y1_pp, p1.phi()); const int idxY2 = unrolledIndexY(y2_pp, p2.phi()); - // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCutLP are rejected above) - if (cFillEtaSpace.value && idx1 >= 0 && idx2 >= 0) { - hRho2_pp_pT015toMaxDefined->Fill(idx1, idx2); - } + // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) + const float wPair = getEffWeight(state.invEffP, cent, p1.pt(), y1_pp) * getEffWeight(state.invEffP, cent, p2.pt(), y2_pp); if (idxY1 >= 0 && idxY2 >= 0) { - hRho2_pp_y_pT015toMaxDefined->Fill(idxY1, idxY2); + state.hRho2_pp_y_AllPt->Fill(idxY1 + 0.5f, idxY2 + 0.5f, wPair); } if (cs) { - fillIdx(cs->pp, idx1, idx2); - fillIdx(cs->pp_y, idxY1, idxY2); + fillIdx(cs->pp_y, idxY1, idxY2, wPair); } } } @@ -2829,19 +3523,18 @@ struct LambdaProtonBalanceFunction { for (auto const& b : antiProtonInfo) { const float qinvPAp = computeQinv(a.px, a.py, a.pz, a.E, b.px, b.py, b.pz, b.E); registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_pAp_Before_qinvCut"), qinvPAp); + if (passesQinvQA(qinvPAp)) { + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_pAp_After_qinvCut"), qinvPAp); + } if (failsQinvCut(qinvPAp)) { continue; } - registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_pAp_After_qinvCut"), qinvPAp); - if (cFillEtaSpace.value && a.idxEta >= 0 && b.idxEta >= 0) { - hRho2_pAp_pT015toMaxDefined->Fill(a.idxEta, b.idxEta); - } + const float wPair = getEffWeight(state.invEffP, cent, a.pt, a.y) * getEffWeight(state.invEffAP, cent, b.pt, b.y); if (a.idxY >= 0 && b.idxY >= 0) { - hRho2_pAp_y_pT015toMaxDefined->Fill(a.idxY, b.idxY); + state.hRho2_pAp_y_AllPt->Fill(a.idxY + 0.5f, b.idxY + 0.5f, wPair); } if (cs) { - fillIdx(cs->pAp, a.idxEta, b.idxEta); - fillIdx(cs->pAp_y, a.idxY, b.idxY); + fillIdx(cs->pAp_y, a.idxY, b.idxY, wPair); } } } @@ -2849,19 +3542,18 @@ struct LambdaProtonBalanceFunction { for (auto const& b : protonInfo) { const float qinvApp = computeQinv(a.px, a.py, a.pz, a.E, b.px, b.py, b.pz, b.E); registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_App_Before_qinvCut"), qinvApp); + if (passesQinvQA(qinvApp)) { + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_App_After_qinvCut"), qinvApp); + } if (failsQinvCut(qinvApp)) { continue; } - registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_App_After_qinvCut"), qinvApp); - if (cFillEtaSpace.value && a.idxEta >= 0 && b.idxEta >= 0) { - hRho2_App_pT015toMaxDefined->Fill(a.idxEta, b.idxEta); - } + const float wPair = getEffWeight(state.invEffAP, cent, a.pt, a.y) * getEffWeight(state.invEffP, cent, b.pt, b.y); if (a.idxY >= 0 && b.idxY >= 0) { - hRho2_App_y_pT015toMaxDefined->Fill(a.idxY, b.idxY); + state.hRho2_App_y_AllPt->Fill(a.idxY + 0.5f, b.idxY + 0.5f, wPair); } if (cs) { - fillIdx(cs->App, a.idxEta, b.idxEta); - fillIdx(cs->App_y, a.idxY, b.idxY); + fillIdx(cs->App_y, a.idxY, b.idxY, wPair); } } } @@ -2872,19 +3564,18 @@ struct LambdaProtonBalanceFunction { } const float qinvApAp = computeQinv(a.px, a.py, a.pz, a.E, b.px, b.py, b.pz, b.E); registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_ApAp_Before_qinvCut"), qinvApAp); + if (passesQinvQA(qinvApAp)) { + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_ApAp_After_qinvCut"), qinvApAp); + } if (failsQinvCut(qinvApAp)) { continue; } - registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_ApAp_After_qinvCut"), qinvApAp); - if (cFillEtaSpace.value && a.idxEta >= 0 && b.idxEta >= 0) { - hRho2_ApAp_pT015toMaxDefined->Fill(a.idxEta, b.idxEta); - } + const float wPair = getEffWeight(state.invEffAP, cent, a.pt, a.y) * getEffWeight(state.invEffAP, cent, b.pt, b.y); if (a.idxY >= 0 && b.idxY >= 0) { - hRho2_ApAp_y_pT015toMaxDefined->Fill(a.idxY, b.idxY); + state.hRho2_ApAp_y_AllPt->Fill(a.idxY + 0.5f, b.idxY + 0.5f, wPair); } if (cs) { - fillIdx(cs->ApAp, a.idxEta, b.idxEta); - fillIdx(cs->ApAp_y, a.idxY, b.idxY); + fillIdx(cs->ApAp_y, a.idxY, b.idxY, wPair); } } } @@ -2895,11 +3586,11 @@ struct LambdaProtonBalanceFunction { // now fully populated. Veto set was built from validated daughters only. for (const auto& v0 : selectedLambdas) { - const auto& v0PosDau = v0.posTrack_as(); + const auto& v0PosDau = v0.template posTrack_as(); const float pt = v0.pt(); int bin = -1; - for (size_t i = 0; i < ptEdges.size() - 1; ++i) { - if (pt >= ptEdges[i] && pt < ptEdges[i + 1]) { + for (size_t i = 0; i < state.ptEdges.size() - 1; ++i) { + if (pt >= state.ptEdges[i] && pt < state.ptEdges[i + 1]) { bin = static_cast(i); break; } @@ -2920,9 +3611,40 @@ struct LambdaProtonBalanceFunction { registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_Before_qinvCut"), qinvLP); } // QA3/SplitTrackQA: q_inv Before and After cut - if (!failsQinvCut(qinvLP)) { + if (passesQinvQA(qinvLP)) { registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_After_qinvCut"), qinvLP); } + // QA3/QinvVsAngles: 3D fills for Lambda(V0)-primProton and Lambda dau proton-primProton + { + constexpr float PIHalf3D = o2::constants::math::PI / 2.0f; + // Angular variables w.r.t. Lambda (V0) + const float dPhi_V0_p = RecoDecay::constrainAngle((v0.phi() - primProton.phi()), -PIHalf3D); + const float dEta_V0_p = v0.eta() - primProton.eta(); + const float yLambda = v0.rapidity(1); + const float yPrimP = protonRapidity(primProton); + const float dY_V0_p = yLambda - yPrimP; + // q_inv using Lambda (V0) four-momentum (kMassLambda hypothesis) + const float v0pmag_3d = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); + const float Ev0_3d = std::sqrt(v0pmag_3d * v0pmag_3d + kMassLambda * kMassLambda); + const float pprmag_3d = std::sqrt(primProton.px() * primProton.px() + primProton.py() * primProton.py() + primProton.pz() * primProton.pz()); + const float Eppr_3d = std::sqrt(pprmag_3d * pprmag_3d + kMassProton * kMassProton); + const float qinvLP_V0 = computeQinv(v0.px(), v0.py(), v0.pz(), Ev0_3d, primProton.px(), primProton.py(), primProton.pz(), Eppr_3d); + // q_inv using Lambda daughter proton (kMassProton hypothesis), only for *_dau histograms + const float dauPmag_3d = std::sqrt(v0PosDau.px() * v0PosDau.px() + v0PosDau.py() * v0PosDau.py() + v0PosDau.pz() * v0PosDau.pz()); + const float Edau_3d = std::sqrt(dauPmag_3d * dauPmag_3d + kMassProton * kMassProton); + const float qinvLP_dau = computeQinv(v0PosDau.px(), v0PosDau.py(), v0PosDau.pz(), Edau_3d, primProton.px(), primProton.py(), primProton.pz(), Eppr_3d); + // Angular variables w.r.t. Lambda daughter proton + const float dPhi_dau_p = RecoDecay::constrainAngle((v0PosDau.phi() - primProton.phi()), -PIHalf3D); + const float dEta_dau_p = v0PosDau.eta() - primProton.eta(); + const float yDauP = protonRapidity(v0PosDau); + const float dY_dau_p = yDauP - yPrimP; + // eta space + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_Lp"), qinvLP_V0, dPhi_V0_p, dEta_V0_p); + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_Lp_dau"), qinvLP_dau, dPhi_dau_p, dEta_dau_p); + // y space + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_Lp"), qinvLP_V0, dPhi_V0_p, dY_V0_p); + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_Lp_dau"), qinvLP_dau, dPhi_dau_p, dY_dau_p); + } // QA3/SplitTrackQA: close-pair block for daughter-overlap diagnosed split tracks { constexpr float PIHalf = o2::constants::math::PI / 2.0f; @@ -2938,33 +3660,33 @@ struct LambdaProtonBalanceFunction { registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_sharedClsFraction_t2"), primProton.tpcFractionSharedCls()); } } - const int idxLambda = unrolledIndex(v0.eta(), v0.phi()); - const int idxPrimP = unrolledIndex(primProton.eta(), primProton.phi()); const float yL = v0.rapidity(1); const float yP = protonRapidity(primProton); const int idxYL = unrolledIndexY(yL, v0.phi()); const int idxYP = unrolledIndexY(yP, primProton.phi()); - // Physics pair correlations (rho2 / pT / pair-count): q_inv cut applied (pairs with q_inv <= kQinvCutLP are rejected above) + registryCorrelationQA.fill(HIST("QA3/Kstar/h1f_kstar_Lp_BeforeQinvCut"), + computeKstar(v0.px(), v0.py(), v0.pz(), kMassLambda, + primProton.px(), primProton.py(), primProton.pz(), kMassProton)); + // q_inv vs DeltaPhi diagnostic (filled BEFORE applying q_inv rejection) + const float dPhi_Lp = RecoDecay::constrainAngle(primProton.phi() - v0.phi(), -o2::constants::math::PI); + registryCorrelationQA.fill(HIST("h2_qinv_dPhi_Lp"), dPhi_Lp, qinvLP); + + // Physics pair correlations (rho2 / pT / pair-count): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) if (failsQinvCut(qinvLP)) { continue; } registryCorrelationQA.fill(HIST("QA3/Kstar/h1f_kstar_Lp"), computeKstar(v0.px(), v0.py(), v0.pz(), kMassLambda, primProton.px(), primProton.py(), primProton.pz(), kMassProton)); - if (idxLambda >= 0 && idxPrimP >= 0) { - if (bin >= 0) { - hPtPrimProton_Lambda[bin]->Fill(primProton.pt()); - } - if (cFillEtaSpace.value) { - hRho2_Lp_pT015toMaxDefined->Fill(idxLambda, idxPrimP); - } + if (idxYL >= 0 && idxYP >= 0 && bin >= 0) { + state.hPtPrimProton_Lambda[bin]->Fill(primProton.pt()); } + const float wPair = getEffWeight(state.invEffL, cent, v0.pt(), yL) * getEffWeight(state.invEffP, cent, primProton.pt(), yP); if (idxYL >= 0 && idxYP >= 0) { - hRho2_Lp_y_pT015toMaxDefined->Fill(idxYL, idxYP); + state.hRho2_Lp_y_AllPt->Fill(idxYL + 0.5f, idxYP + 0.5f, wPair); } if (cs) { - fillIdx(cs->Lp, idxLambda, idxPrimP); - fillIdx(cs->Lp_y, idxYL, idxYP); + fillIdx(cs->Lp_y, idxYL, idxYP, wPair); } ++nPairs_Lambda_PrimProton; } @@ -2975,17 +3697,48 @@ struct LambdaProtonBalanceFunction { // QA3/SplitTrackQA: q_inv for every accepted Lambda-antiproton pair float qinvLAp = 0.0f; { - const float v0pmag2 = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); + const float v0pmag = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); const float paprmag = std::sqrt(primAntiProton.px() * primAntiProton.px() + primAntiProton.py() * primAntiProton.py() + primAntiProton.pz() * primAntiProton.pz()); - const float Ev02 = std::sqrt(v0pmag2 * v0pmag2 + kMassLambda * kMassLambda); + const float Ev0 = std::sqrt(v0pmag * v0pmag + kMassLambda * kMassLambda); const float Epapr = std::sqrt(paprmag * paprmag + kMassProton * kMassProton); - qinvLAp = computeQinv(v0.px(), v0.py(), v0.pz(), Ev02, primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), Epapr); + qinvLAp = computeQinv(v0.px(), v0.py(), v0.pz(), Ev0, primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), Epapr); registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_Before_qinvCut"), qinvLAp); } // QA3/SplitTrackQA: q_inv Before and After cut - if (!failsQinvCut(qinvLAp)) { + if (passesQinvQA(qinvLAp)) { registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_After_qinvCut"), qinvLAp); } + // QA3/QinvVsAngles: 3D fills for Lambda(V0)-primAntiProton and Lambda dau proton-primAntiProton + { + constexpr float PIHalf3D = o2::constants::math::PI / 2.0f; + // Angular variables w.r.t. Lambda (V0) + const float dPhi_V0_ap = RecoDecay::constrainAngle((v0.phi() - primAntiProton.phi()), -PIHalf3D); + const float dEta_V0_ap = v0.eta() - primAntiProton.eta(); + const float yLambdaL = v0.rapidity(1); + const float yPrimAp = protonRapidity(primAntiProton); + const float dY_V0_ap = yLambdaL - yPrimAp; + // q_inv using Lambda (V0) four-momentum (kMassLambda hypothesis) + const float v0pmag_3dL = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); + const float Ev0_3dL = std::sqrt(v0pmag_3dL * v0pmag_3dL + kMassLambda * kMassLambda); + const float paprmag_3dL = std::sqrt(primAntiProton.px() * primAntiProton.px() + primAntiProton.py() * primAntiProton.py() + primAntiProton.pz() * primAntiProton.pz()); + const float Epapr_3dL = std::sqrt(paprmag_3dL * paprmag_3dL + kMassProton * kMassProton); + const float qinvLAp_V0 = computeQinv(v0.px(), v0.py(), v0.pz(), Ev0_3dL, primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), Epapr_3dL); + // q_inv using Lambda daughter proton (kMassProton hypothesis), only for *_dau histograms + const float dauPmag_3dL = std::sqrt(v0PosDau.px() * v0PosDau.px() + v0PosDau.py() * v0PosDau.py() + v0PosDau.pz() * v0PosDau.pz()); + const float Edau_3dL = std::sqrt(dauPmag_3dL * dauPmag_3dL + kMassProton * kMassProton); + const float qinvLAp_dau = computeQinv(v0PosDau.px(), v0PosDau.py(), v0PosDau.pz(), Edau_3dL, primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), Epapr_3dL); + // Angular variables w.r.t. Lambda daughter proton + const float dPhi_dau_ap = RecoDecay::constrainAngle((v0PosDau.phi() - primAntiProton.phi()), -PIHalf3D); + const float dEta_dau_ap = v0PosDau.eta() - primAntiProton.eta(); + const float yDauPL = protonRapidity(v0PosDau); + const float dY_dau_ap = yDauPL - yPrimAp; + // eta space + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_LAp"), qinvLAp_V0, dPhi_V0_ap, dEta_V0_ap); + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_LAp_dau"), qinvLAp_dau, dPhi_dau_ap, dEta_dau_ap); + // y space + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_LAp"), qinvLAp_V0, dPhi_V0_ap, dY_V0_ap); + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_LAp_dau"), qinvLAp_dau, dPhi_dau_ap, dY_dau_ap); + } // QA3/RawPairDensity: Lambda-antiproton → same LP histogram { constexpr float PIHalf = o2::constants::math::PI / 2.0f; @@ -2994,33 +3747,29 @@ struct LambdaProtonBalanceFunction { v0.eta() - primAntiProton.eta(), RecoDecay::constrainAngle((v0.phi() - primAntiProton.phi()), -PIHalf)); } - const int idxLambda = unrolledIndex(v0.eta(), v0.phi()); - const int idxPrimPbar = unrolledIndex(primAntiProton.eta(), primAntiProton.phi()); const float yL2 = v0.rapidity(1); const float yAp = protonRapidity(primAntiProton); const int idxYL2 = unrolledIndexY(yL2, v0.phi()); const int idxYAp = unrolledIndexY(yAp, primAntiProton.phi()); - // Physics pair correlations (rho2 / pT / pair-count): q_inv cut applied (pairs with q_inv <= kQinvCutLP are rejected above) + registryCorrelationQA.fill(HIST("QA3/Kstar/h1f_kstar_LAp_BeforeQinvCut"), + computeKstar(v0.px(), v0.py(), v0.pz(), kMassLambda, + primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), kMassProton)); + // Physics pair correlations (rho2 / pT / pair-count): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) if (failsQinvCut(qinvLAp)) { continue; } registryCorrelationQA.fill(HIST("QA3/Kstar/h1f_kstar_LAp"), computeKstar(v0.px(), v0.py(), v0.pz(), kMassLambda, primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), kMassProton)); - if (idxLambda >= 0 && idxPrimPbar >= 0) { - if (bin >= 0) { - hPtPrimAntiProton_Lambda[bin]->Fill(primAntiProton.pt()); - } - if (cFillEtaSpace.value) { - hRho2_LAp_pT015toMaxDefined->Fill(idxLambda, idxPrimPbar); - } + if (idxYL2 >= 0 && idxYAp >= 0 && bin >= 0) { + state.hPtPrimAntiProton_Lambda[bin]->Fill(primAntiProton.pt()); } + const float wPair = getEffWeight(state.invEffL, cent, v0.pt(), yL2) * getEffWeight(state.invEffAP, cent, primAntiProton.pt(), yAp); if (idxYL2 >= 0 && idxYAp >= 0) { - hRho2_LAp_y_pT015toMaxDefined->Fill(idxYL2, idxYAp); + state.hRho2_LAp_y_AllPt->Fill(idxYL2 + 0.5f, idxYAp + 0.5f, wPair); } if (cs) { - fillIdx(cs->LAp, idxLambda, idxPrimPbar); - fillIdx(cs->LAp_y, idxYL2, idxYAp); + fillIdx(cs->LAp_y, idxYL2, idxYAp, wPair); } ++nPairs_Lambda_PrimAntiProton; } @@ -3086,11 +3835,11 @@ struct LambdaProtonBalanceFunction { } for (const auto& v0 : selectedAntiLambdas) { - const auto& v0NegDau = v0.negTrack_as(); + const auto& v0NegDau = v0.template negTrack_as(); const float pt = v0.pt(); int bin = -1; - for (size_t i = 0; i < ptEdges.size() - 1; ++i) { - if (pt >= ptEdges[i] && pt < ptEdges[i + 1]) { + for (size_t i = 0; i < state.ptEdges.size() - 1; ++i) { + if (pt >= state.ptEdges[i] && pt < state.ptEdges[i + 1]) { bin = static_cast(i); break; } @@ -3103,17 +3852,48 @@ struct LambdaProtonBalanceFunction { // QA3/SplitTrackQA: q_inv for every accepted AntiLambda-proton pair float qinvALp = 0.0f; { - const float alpmag = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); + const float v0pmag = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); const float pprmag3 = std::sqrt(primProton.px() * primProton.px() + primProton.py() * primProton.py() + primProton.pz() * primProton.pz()); - const float EalL = std::sqrt(alpmag * alpmag + kMassLambda * kMassLambda); + const float Ev0 = std::sqrt(v0pmag * v0pmag + kMassLambda * kMassLambda); const float Eppr3 = std::sqrt(pprmag3 * pprmag3 + kMassProton * kMassProton); - qinvALp = computeQinv(v0.px(), v0.py(), v0.pz(), EalL, primProton.px(), primProton.py(), primProton.pz(), Eppr3); + qinvALp = computeQinv(v0.px(), v0.py(), v0.pz(), Ev0, primProton.px(), primProton.py(), primProton.pz(), Eppr3); registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_Before_qinvCut"), qinvALp); } // QA3/SplitTrackQA: q_inv Before and After cut - if (!failsQinvCut(qinvALp)) { + if (passesQinvQA(qinvALp)) { registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_After_qinvCut"), qinvALp); } + // QA3/QinvVsAngles: 3D fills for AntiLambda(V0)-primProton and AntiLambda dau antiproton-primProton + { + constexpr float PIHalf3D = o2::constants::math::PI / 2.0f; + // Angular variables w.r.t. AntiLambda (V0) + const float dPhi_AL_p = RecoDecay::constrainAngle((v0.phi() - primProton.phi()), -PIHalf3D); + const float dEta_AL_p = v0.eta() - primProton.eta(); + const float yAL = v0.rapidity(2); + const float yPrimP3 = protonRapidity(primProton); + const float dY_AL_p = yAL - yPrimP3; + // q_inv using AntiLambda (V0) four-momentum (kMassLambda hypothesis) + const float almag_3d = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); + const float EalL_3d = std::sqrt(almag_3d * almag_3d + kMassLambda * kMassLambda); + const float pprmag3_3d = std::sqrt(primProton.px() * primProton.px() + primProton.py() * primProton.py() + primProton.pz() * primProton.pz()); + const float Eppr3_3d = std::sqrt(pprmag3_3d * pprmag3_3d + kMassProton * kMassProton); + const float qinvALp_V0 = computeQinv(v0.px(), v0.py(), v0.pz(), EalL_3d, primProton.px(), primProton.py(), primProton.pz(), Eppr3_3d); + // q_inv using AntiLambda daughter antiproton (kMassProton hypothesis), only for *_dau histograms + const float dauPmagAL_3d = std::sqrt(v0NegDau.px() * v0NegDau.px() + v0NegDau.py() * v0NegDau.py() + v0NegDau.pz() * v0NegDau.pz()); + const float EdauAL_3d = std::sqrt(dauPmagAL_3d * dauPmagAL_3d + kMassProton * kMassProton); + const float qinvALp_dau = computeQinv(v0NegDau.px(), v0NegDau.py(), v0NegDau.pz(), EdauAL_3d, primProton.px(), primProton.py(), primProton.pz(), Eppr3_3d); + // Angular variables w.r.t. AntiLambda daughter antiproton + const float dPhi_dauAL_p = RecoDecay::constrainAngle((v0NegDau.phi() - primProton.phi()), -PIHalf3D); + const float dEta_dauAL_p = v0NegDau.eta() - primProton.eta(); + const float yDauAL = protonRapidity(v0NegDau); + const float dY_dauAL_p = yDauAL - yPrimP3; + // eta space + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_ALp"), qinvALp_V0, dPhi_AL_p, dEta_AL_p); + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_ALp_dau"), qinvALp_dau, dPhi_dauAL_p, dEta_dauAL_p); + // y space + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_ALp"), qinvALp_V0, dPhi_AL_p, dY_AL_p); + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_ALp_dau"), qinvALp_dau, dPhi_dauAL_p, dY_dauAL_p); + } // QA3/RawPairDensity: AntiLambda-proton → same LP histogram { constexpr float PIHalf = o2::constants::math::PI / 2.0f; @@ -3122,33 +3902,29 @@ struct LambdaProtonBalanceFunction { v0.eta() - primProton.eta(), RecoDecay::constrainAngle((v0.phi() - primProton.phi()), -PIHalf)); } - const int idxAL = unrolledIndex(v0.eta(), v0.phi()); - const int idxPrimP = unrolledIndex(primProton.eta(), primProton.phi()); const float yAL = v0.rapidity(2); const float yP2 = protonRapidity(primProton); const int idxYAL = unrolledIndexY(yAL, v0.phi()); const int idxYP2 = unrolledIndexY(yP2, primProton.phi()); - // Physics pair correlations (rho2 / pT / pair-count): q_inv cut applied (pairs with q_inv <= kQinvCutLP are rejected above) + registryCorrelationQA.fill(HIST("QA3/Kstar/h1f_kstar_ALp_BeforeQinvCut"), + computeKstar(v0.px(), v0.py(), v0.pz(), kMassLambda, + primProton.px(), primProton.py(), primProton.pz(), kMassProton)); + // Physics pair correlations (rho2 / pT / pair-count): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) if (failsQinvCut(qinvALp)) { continue; } registryCorrelationQA.fill(HIST("QA3/Kstar/h1f_kstar_ALp"), computeKstar(v0.px(), v0.py(), v0.pz(), kMassLambda, primProton.px(), primProton.py(), primProton.pz(), kMassProton)); - if (idxAL >= 0 && idxPrimP >= 0) { - if (bin >= 0) { - hPtPrimProton_AntiLambda[bin]->Fill(primProton.pt()); - } - if (cFillEtaSpace.value) { - hRho2_ALp_pT015toMaxDefined->Fill(idxAL, idxPrimP); - } + if (idxYAL >= 0 && idxYP2 >= 0 && bin >= 0) { + state.hPtPrimProton_AntiLambda[bin]->Fill(primProton.pt()); } + const float wPair = getEffWeight(state.invEffAL, cent, v0.pt(), yAL) * getEffWeight(state.invEffP, cent, primProton.pt(), yP2); if (idxYAL >= 0 && idxYP2 >= 0) { - hRho2_ALp_y_pT015toMaxDefined->Fill(idxYAL, idxYP2); + state.hRho2_ALp_y_AllPt->Fill(idxYAL + 0.5f, idxYP2 + 0.5f, wPair); } if (cs) { - fillIdx(cs->ALp, idxAL, idxPrimP); - fillIdx(cs->ALp_y, idxYAL, idxYP2); + fillIdx(cs->ALp_y, idxYAL, idxYP2, wPair); } ++nPairs_AntiLambda_PrimProton; } @@ -3159,17 +3935,48 @@ struct LambdaProtonBalanceFunction { // QA3/SplitTrackQA: q_inv for every accepted AntiLambda-antiproton pair float qinvALAp = 0.0f; { - const float alpmag2 = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); + const float v0pmag = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); const float paprmag2 = std::sqrt(primAntiProton.px() * primAntiProton.px() + primAntiProton.py() * primAntiProton.py() + primAntiProton.pz() * primAntiProton.pz()); - const float EalL2 = std::sqrt(alpmag2 * alpmag2 + kMassLambda * kMassLambda); + const float Ev0 = std::sqrt(v0pmag * v0pmag + kMassLambda * kMassLambda); const float Epapr2 = std::sqrt(paprmag2 * paprmag2 + kMassProton * kMassProton); - qinvALAp = computeQinv(v0.px(), v0.py(), v0.pz(), EalL2, primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), Epapr2); + qinvALAp = computeQinv(v0.px(), v0.py(), v0.pz(), Ev0, primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), Epapr2); registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_Before_qinvCut"), qinvALAp); } // QA3/SplitTrackQA: q_inv Before and After cut - if (!failsQinvCut(qinvALAp)) { + if (passesQinvQA(qinvALAp)) { registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_After_qinvCut"), qinvALAp); } + // QA3/QinvVsAngles: 3D fills for AntiLambda(V0)-primAntiProton and AntiLambda dau antiproton-primAntiProton + { + constexpr float PIHalf3D = o2::constants::math::PI / 2.0f; + // Angular variables w.r.t. AntiLambda (V0) + const float dPhi_AL_ap = RecoDecay::constrainAngle((v0.phi() - primAntiProton.phi()), -PIHalf3D); + const float dEta_AL_ap = v0.eta() - primAntiProton.eta(); + const float yAL2 = v0.rapidity(2); + const float yPrimAp2 = protonRapidity(primAntiProton); + const float dY_AL_ap = yAL2 - yPrimAp2; + // q_inv using AntiLambda (V0) four-momentum (kMassLambda hypothesis) + const float almag2_3d = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); + const float EalL2_3d = std::sqrt(almag2_3d * almag2_3d + kMassLambda * kMassLambda); + const float paprmag2_3d = std::sqrt(primAntiProton.px() * primAntiProton.px() + primAntiProton.py() * primAntiProton.py() + primAntiProton.pz() * primAntiProton.pz()); + const float Epapr2_3d = std::sqrt(paprmag2_3d * paprmag2_3d + kMassProton * kMassProton); + const float qinvALAp_V0 = computeQinv(v0.px(), v0.py(), v0.pz(), EalL2_3d, primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), Epapr2_3d); + // q_inv using AntiLambda daughter antiproton (kMassProton hypothesis), only for *_dau histograms + const float dauPmagAL2_3d = std::sqrt(v0NegDau.px() * v0NegDau.px() + v0NegDau.py() * v0NegDau.py() + v0NegDau.pz() * v0NegDau.pz()); + const float EdauAL2_3d = std::sqrt(dauPmagAL2_3d * dauPmagAL2_3d + kMassProton * kMassProton); + const float qinvALAp_dau = computeQinv(v0NegDau.px(), v0NegDau.py(), v0NegDau.pz(), EdauAL2_3d, primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), Epapr2_3d); + // Angular variables w.r.t. AntiLambda daughter antiproton + const float dPhi_dauAL_ap = RecoDecay::constrainAngle((v0NegDau.phi() - primAntiProton.phi()), -PIHalf3D); + const float dEta_dauAL_ap = v0NegDau.eta() - primAntiProton.eta(); + const float yDauAL2 = protonRapidity(v0NegDau); + const float dY_dauAL_ap = yDauAL2 - yPrimAp2; + // eta space + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_ALAp"), qinvALAp_V0, dPhi_AL_ap, dEta_AL_ap); + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_ALAp_dau"), qinvALAp_dau, dPhi_dauAL_ap, dEta_dauAL_ap); + // y space + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_ALAp"), qinvALAp_V0, dPhi_AL_ap, dY_AL_ap); + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_ALAp_dau"), qinvALAp_dau, dPhi_dauAL_ap, dY_dauAL_ap); + } // QA3/SplitTrackQA: close-pair block for daughter-overlap diagnosed split tracks { constexpr float PIHalf = o2::constants::math::PI / 2.0f; @@ -3185,33 +3992,29 @@ struct LambdaProtonBalanceFunction { registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_sharedClsFraction_t2"), primAntiProton.tpcFractionSharedCls()); } } - const int idxAL2 = unrolledIndex(v0.eta(), v0.phi()); - const int idxPrimPb = unrolledIndex(primAntiProton.eta(), primAntiProton.phi()); const float yAL2 = v0.rapidity(2); const float yAp2 = protonRapidity(primAntiProton); const int idxYAL2 = unrolledIndexY(yAL2, v0.phi()); const int idxYAp2 = unrolledIndexY(yAp2, primAntiProton.phi()); - // Physics pair correlations (rho2 / pT / pair-count): q_inv cut applied (pairs with q_inv <= kQinvCutLP are rejected above) + registryCorrelationQA.fill(HIST("QA3/Kstar/h1f_kstar_ALAp_BeforeQinvCut"), + computeKstar(v0.px(), v0.py(), v0.pz(), kMassLambda, + primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), kMassProton)); + // Physics pair correlations (rho2 / pT / pair-count): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) if (failsQinvCut(qinvALAp)) { continue; } registryCorrelationQA.fill(HIST("QA3/Kstar/h1f_kstar_ALAp"), computeKstar(v0.px(), v0.py(), v0.pz(), kMassLambda, primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), kMassProton)); - if (idxAL2 >= 0 && idxPrimPb >= 0) { - if (bin >= 0) { - hPtPrimAntiProton_AntiLambda[bin]->Fill(primAntiProton.pt()); - } - if (cFillEtaSpace.value) { - hRho2_ALAp_pT015toMaxDefined->Fill(idxAL2, idxPrimPb); - } + if (idxYAL2 >= 0 && idxYAp2 >= 0 && bin >= 0) { + state.hPtPrimAntiProton_AntiLambda[bin]->Fill(primAntiProton.pt()); } + const float wPair = getEffWeight(state.invEffAL, cent, v0.pt(), yAL2) * getEffWeight(state.invEffAP, cent, primAntiProton.pt(), yAp2); if (idxYAL2 >= 0 && idxYAp2 >= 0) { - hRho2_ALAp_y_pT015toMaxDefined->Fill(idxYAL2, idxYAp2); + state.hRho2_ALAp_y_AllPt->Fill(idxYAL2 + 0.5f, idxYAp2 + 0.5f, wPair); } if (cs) { - fillIdx(cs->ALAp, idxAL2, idxPrimPb); - fillIdx(cs->ALAp_y, idxYAL2, idxYAp2); + fillIdx(cs->ALAp_y, idxYAL2, idxYAp2, wPair); } ++nPairs_AntiLambda_PrimAntiProton; } @@ -3311,25 +4114,22 @@ struct LambdaProtonBalanceFunction { const float Ev2 = std::sqrt(v2pmag * v2pmag + kMassLambda * kMassLambda); const float qinvLL = computeQinv(v1.px(), v1.py(), v1.pz(), Ev1, v2.px(), v2.py(), v2.pz(), Ev2); registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_Before_qinvCut"), qinvLL); + if (passesQinvQA(qinvLL)) { + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_After_qinvCut"), qinvLL); + } if (failsQinvCut(qinvLL)) { continue; } - registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_After_qinvCut"), qinvLL); } - const int idx1 = unrolledIndex(v1.eta(), v1.phi()); - const int idx2 = unrolledIndex(v2.eta(), v2.phi()); const int idxY1 = unrolledIndexY(yv1L, v1.phi()); const int idxY2 = unrolledIndexY(v2.rapidity(1), v2.phi()); - // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCutLP are rejected above) - if (cFillEtaSpace.value && idx1 >= 0 && idx2 >= 0) { - hRho2_LL_pT015toMaxDefined->Fill(idx1, idx2); - } + // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) + const float wPair = getEffWeight(state.invEffL, cent, v1.pt(), yv1L) * getEffWeight(state.invEffL, cent, v2.pt(), v2.rapidity(1)); if (idxY1 >= 0 && idxY2 >= 0) { - hRho2_LL_y_pT015toMaxDefined->Fill(idxY1, idxY2); + state.hRho2_LL_y_AllPt->Fill(idxY1 + 0.5f, idxY2 + 0.5f, wPair); } if (cs) { - fillIdx(cs->LL, idx1, idx2); - fillIdx(cs->LL_y, idxY1, idxY2); + fillIdx(cs->LL_y, idxY1, idxY2, wPair); } } } @@ -3365,25 +4165,22 @@ struct LambdaProtonBalanceFunction { const float Ev2L = std::sqrt(v2pmagL * v2pmagL + kMassLambda * kMassLambda); const float qinvLAL = computeQinv(v1.px(), v1.py(), v1.pz(), Ev1L, v2.px(), v2.py(), v2.pz(), Ev2L); registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_Before_qinvCut"), qinvLAL); + if (passesQinvQA(qinvLAL)) { + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_After_qinvCut"), qinvLAL); + } if (failsQinvCut(qinvLAL)) { continue; } - registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_After_qinvCut"), qinvLAL); } - const int idx1 = unrolledIndex(v1.eta(), v1.phi()); - const int idx2 = unrolledIndex(v2.eta(), v2.phi()); const int idxY1 = unrolledIndexY(yv1LAL, v1.phi()); const int idxY2 = unrolledIndexY(v2.rapidity(2), v2.phi()); - // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCutLP are rejected above) - if (cFillEtaSpace.value && idx1 >= 0 && idx2 >= 0) { - hRho2_LAL_pT015toMaxDefined->Fill(idx1, idx2); - } + // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) + const float wPair = getEffWeight(state.invEffL, cent, v1.pt(), yv1LAL) * getEffWeight(state.invEffAL, cent, v2.pt(), v2.rapidity(2)); if (idxY1 >= 0 && idxY2 >= 0) { - hRho2_LAL_y_pT015toMaxDefined->Fill(idxY1, idxY2); + state.hRho2_LAL_y_AllPt->Fill(idxY1 + 0.5f, idxY2 + 0.5f, wPair); } if (cs) { - fillIdx(cs->LAL, idx1, idx2); - fillIdx(cs->LAL_y, idxY1, idxY2); + fillIdx(cs->LAL_y, idxY1, idxY2, wPair); } } } @@ -3419,25 +4216,22 @@ struct LambdaProtonBalanceFunction { const float Ev2AL = std::sqrt(v2pmagAL * v2pmagAL + kMassLambda * kMassLambda); const float qinvALL = computeQinv(v1.px(), v1.py(), v1.pz(), Ev1AL, v2.px(), v2.py(), v2.pz(), Ev2AL); registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_Before_qinvCut"), qinvALL); + if (passesQinvQA(qinvALL)) { + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_After_qinvCut"), qinvALL); + } if (failsQinvCut(qinvALL)) { continue; } - registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_After_qinvCut"), qinvALL); } - const int idx1 = unrolledIndex(v1.eta(), v1.phi()); - const int idx2 = unrolledIndex(v2.eta(), v2.phi()); const int idxY1 = unrolledIndexY(yv1ALL, v1.phi()); const int idxY2 = unrolledIndexY(v2.rapidity(1), v2.phi()); - // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCutLP are rejected above) - if (cFillEtaSpace.value && idx1 >= 0 && idx2 >= 0) { - hRho2_ALL_pT015toMaxDefined->Fill(idx1, idx2); - } + // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) + const float wPair = getEffWeight(state.invEffAL, cent, v1.pt(), yv1ALL) * getEffWeight(state.invEffL, cent, v2.pt(), v2.rapidity(1)); if (idxY1 >= 0 && idxY2 >= 0) { - hRho2_ALL_y_pT015toMaxDefined->Fill(idxY1, idxY2); + state.hRho2_ALL_y_AllPt->Fill(idxY1 + 0.5f, idxY2 + 0.5f, wPair); } if (cs) { - fillIdx(cs->ALL, idx1, idx2); - fillIdx(cs->ALL_y, idxY1, idxY2); + fillIdx(cs->ALL_y, idxY1, idxY2, wPair); } } } @@ -3476,25 +4270,22 @@ struct LambdaProtonBalanceFunction { const float Ev2AL2 = std::sqrt(v2pmagAL2 * v2pmagAL2 + kMassLambda * kMassLambda); const float qinvALAL = computeQinv(v1.px(), v1.py(), v1.pz(), Ev1AL2, v2.px(), v2.py(), v2.pz(), Ev2AL2); registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_Before_qinvCut"), qinvALAL); + if (passesQinvQA(qinvALAL)) { + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_After_qinvCut"), qinvALAL); + } if (failsQinvCut(qinvALAL)) { continue; } - registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_After_qinvCut"), qinvALAL); } - const int idx1 = unrolledIndex(v1.eta(), v1.phi()); - const int idx2 = unrolledIndex(v2.eta(), v2.phi()); const int idxY1 = unrolledIndexY(yv1ALAL, v1.phi()); const int idxY2 = unrolledIndexY(v2.rapidity(2), v2.phi()); - // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCutLP are rejected above) - if (cFillEtaSpace.value && idx1 >= 0 && idx2 >= 0) { - hRho2_ALAL_pT015toMaxDefined->Fill(idx1, idx2); - } + // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) + const float wPair = getEffWeight(state.invEffAL, cent, v1.pt(), yv1ALAL) * getEffWeight(state.invEffAL, cent, v2.pt(), v2.rapidity(2)); if (idxY1 >= 0 && idxY2 >= 0) { - hRho2_ALAL_y_pT015toMaxDefined->Fill(idxY1, idxY2); + state.hRho2_ALAL_y_AllPt->Fill(idxY1 + 0.5f, idxY2 + 0.5f, wPair); } if (cs) { - fillIdx(cs->ALAL, idx1, idx2); - fillIdx(cs->ALAL_y, idxY1, idxY2); + fillIdx(cs->ALAL_y, idxY1, idxY2, wPair); } } } @@ -3506,14 +4297,374 @@ struct LambdaProtonBalanceFunction { registryOther.fill(HIST("hNPairs_Lambda_PrimAntiProton"), static_cast(nPairs_Lambda_PrimAntiProton)); registryOther.fill(HIST("hNPairs_AntiLambda_PrimProton"), static_cast(nPairs_AntiLambda_PrimProton)); registryOther.fill(HIST("hNPairs_AntiLambda_PrimAntiProton"), static_cast(nPairs_AntiLambda_PrimAntiProton)); + + // Stage 1 counting. + registryOther.fill(HIST("hN_Lambda_reco1"), static_cast(selectedLambdas.size())); + registryOther.fill(HIST("hN_AntiLambda_reco1"), static_cast(selectedAntiLambdas.size())); + registryOther.fill(HIST("hN_Proton_reco1"), static_cast(selectedPrimProtons.size())); + registryOther.fill(HIST("hN_AntiProton_reco1"), static_cast(selectedPrimAntiProtons.size())); + fillStage23Counts(mcCollisionId, selectedLambdas, selectedAntiLambdas, selectedPrimProtons, selectedPrimAntiProtons); } + void processData(soa::Filtered>::iterator const& collision, + aod::V0Datas const& V0s, + MyTracks const& tracks) + { + // ── Ordered-pair convention ──────────────────────────────────────────── + // pp : trigger=proton1, assoc=proton2 (self-pairs rejected) + // pAp : trigger=proton, assoc=antiproton + // App : trigger=antiproton,assoc=proton + // ApAp : trigger=antiproton1,assoc=antiproton2 (self-pairs rejected) + // Lp : trigger=Lambda, assoc=proton (daughter-track overlap rejected) + // LAp : trigger=Lambda, assoc=antiproton + // ALp : trigger=AntiLambda,assoc=proton + // ALAp : trigger=AntiLambda,assoc=antiproton + // LL/LAL/ALL/ALAL: both V0s from respective selected lists (self-pairs rejected) + // + // Every channel fills y-space only (unrolledIndexY). + // ───────────────────────────────────────────────────────────────────── + + float cent = 0.f; + int nTracksINEL = 0; + if (!selectBFEvent(collision, tracks, cent, nTracksINEL)) { + return; + } + auto* cs = bookBFEvent(collision, cent, nTracksINEL); + runRecoAnalysis(collision, V0s, tracks, cent, cs); + } + PROCESS_SWITCH(LambdaProtonBalanceFunction, processData, "Process for Data", true); + + // ══════════════════════════════════════════════════════════════════════════ + // MC efficiency maps: Gen* from MC particles, Rec* from the reconstructed selection. + // Gen* use MC-truth pT and y (proton mass for p/pbar, Lambda mass for Lambda/AntiLambda). + // Rec* use RECONSTRUCTED pT and y, i.e. the same variables getEffWeight() queries in processData(). + // The loop is driven by MC collisions (exactly one reconstructed collision required) to avoid + // split-vertex double counting of generated particles. + // ══════════════════════════════════════════════════════════════════════════ + Preslice v0sPerCollision = aod::v0data::collisionId; + Preslice tracksPerCollision = aod::track::collisionId; + + template + static float mcRapidity(TMc const& mc, float mass) + { + return RecoDecay::y(std::array{mc.px(), mc.py(), mc.pz()}, mass); + } + // Generator-level acceptance = same pT range and |y| cut as the reco selection of the species. + bool inLambdaAcc(float pt, float y) const + { + return pt >= lambdaV0MinPt.value && pt <= lambdaV0MaxPt.value && std::abs(y) <= lambdaV0MaxY.value; + } + bool inProtonAcc(float pt, float y) const + { + return pt >= pProtonMinPt.value && pt <= pProtonMaxPt.value && std::abs(y) <= pProtonMaxY.value; + } + + void processMCEfficiency(aod::McCollisions::iterator const& mcCollision, + soa::SmallGroups> const& collisions, + MyV0sMC const& allV0s, + MyTracksMC const& allTracks, + aod::McParticles const& mcParticles) + { + // ── 1) Event association + selection (soa::Filtered does not apply to SmallGroups) ── + // Exactly one reconstructed collision per MC collision: removes split-vertex double counting. + if (collisions.size() != 1) { + return; + } + auto collision = collisions.begin(); + if (!collision.has_mcCollision() || collision.mcCollisionId() != mcCollision.globalIndex()) { + return; + } + // Same event selection as the Filters of processData(): sel8 + |posZ| cut + if (!collision.sel8() || !(std::abs(collision.posZ()) < cPPandV0ZVertexCut.value)) { + return; + } + // Slice V0s and tracks to this reconstructed collision + auto V0s = allV0s.sliceBy(v0sPerCollision, collision.globalIndex()); + auto tracks = allTracks.sliceBy(tracksPerCollision, collision.globalIndex()); + + if (collision.numContrib() < 1) { + return; + } + const float cent = getCentrality(collision); // FT0C or FT0M according to cUseFT0C + if (cent < state.mCentMin || cent >= state.mCentMax) { + return; + } + // INEL > 0 (same as processData()): at least one charged track in |eta| < 0.8 + { + constexpr float MaxInelEta = 0.8f; + int nTracksINEL = 0; + for (auto const& trk : tracks) { + if (std::abs(trk.eta()) < MaxInelEta) { + ++nTracksINEL; + } + } + if (nTracksINEL < 1) { + return; + } + } + + // Same extra event selection as processData(). It MUST be identical, otherwise the efficiency does not match the data. + if (!passesExtraEventSelection(collision)) { + return; + } + + // ── 2) GENERATED: physical primaries of this MC collision (mcParticles is auto-sliced to mcCollision; + // Lambda/AntiLambda require daughter records; no decay-channel requirement or V0-daughter veto) ── + for (auto const& mc : mcParticles) { + if (mc.mcCollisionId() != mcCollision.globalIndex() || !mc.isPhysicalPrimary()) { + continue; + } + const int pdg = mc.pdgCode(); + if (pdg == PDG_t::kLambda0 || pdg == PDG_t::kLambda0Bar) { + if (!mc.has_daughters()) { + continue; + } + const float pt = mc.pt(); + const float y = mcRapidity(mc, kMassLambda); + if (!inLambdaAcc(pt, y)) { + continue; + } + if (pdg == PDG_t::kLambda0) { + registryEff.fill(HIST("GenLambda"), cent, pt, y); + } else { + registryEff.fill(HIST("GenAntiLambda"), cent, pt, y); + } + } else if (pdg == PDG_t::kProton || pdg == PDG_t::kProtonBar) { + const float pt = mc.pt(); + const float y = mcRapidity(mc, kMassProton); + if (!inProtonAcc(pt, y)) { + continue; + } + if (pdg == PDG_t::kProton) { + registryEff.fill(HIST("GenProton"), cent, pt, y); + } else { + registryEff.fill(HIST("GenAntiProton"), cent, pt, y); + } + } + } + + // Use the identical reconstructed selection and cleaning as processMCRecBF, + // including the conservative daughter veto built before shared-daughter cleaning. + std::unordered_set v0DaughterTrackIds; + std::vector selectedLambdas; + std::vector selectedAntiLambdas; + std::vector selectedPrimProtons; + std::vector selectedPrimAntiProtons; + selectRecoV0s(collision, V0s, selectedLambdas, selectedAntiLambdas, v0DaughterTrackIds); + selectRecoProtons(tracks, v0DaughterTrackIds, selectedPrimProtons, selectedPrimAntiProtons); + + // RecSpecies is the unweighted Stage 3 count in reconstructed (cent, pT, y). + // GenSpecies above uses truth (pT, y) in the same accepted event; their binwise + // ratio includes migration. Rec/Gen division is performed by external map macros. + fillStage23Counts(mcCollision.globalIndex(), selectedLambdas, selectedAntiLambdas, + selectedPrimProtons, selectedPrimAntiProtons, cent); + } + PROCESS_SWITCH(LambdaProtonBalanceFunction, processMCEfficiency, "Produce MC single-particle efficiency inputs", false); + // ───────────────────────────────────────────────────────────────────── + + // ══ Matched-event BF selection (shared by reconstructed and generated modes) ══ + using MatchedCollisions = soa::Join; + Preslice v0sPerCollisionData = aod::v0data::collisionId; + Preslice tracksPerCollisionData = aod::track::collisionId; + + // This reference is conditional on accepted reconstructed events and excludes + // MC collisions with missing or split reconstructed collisions. + // Select once for BF and composition; only BF books BF event histograms. + template + void withMatchedEvent(aod::McCollisions::iterator const& mcCollision, + soa::SmallGroups const& collisions, + TTracks const& allTracks, TPreslice const& trackPreslice, TAnalysis&& analyze) + { + registryOther.fill(HIST("hMatchedEventQA"), 1.f); + if (collisions.size() != 1) { + registryOther.fill(HIST("hMatchedEventQA"), 2.f); + return; + } + auto collision = collisions.begin(); + if (!collision.has_mcCollision() || collision.mcCollisionId() != mcCollision.globalIndex()) { + registryOther.fill(HIST("hMatchedEventQA"), 3.f); + return; + } + // SmallGroups do not inherit processData's framework Filters. + if (!collision.sel8() || !(std::abs(collision.posZ()) < cPPandV0ZVertexCut.value)) { + registryOther.fill(HIST("hMatchedEventQA"), 4.f); + return; + } + auto tracks = allTracks.sliceBy(trackPreslice, collision.globalIndex()); + float cent = 0.f; + int nTracksINEL = 0; + if (!selectBFEvent(collision, tracks, cent, nTracksINEL)) { + registryOther.fill(HIST("hMatchedEventQA"), 5.f); + return; + } + CentRho2Set* cs = nullptr; + if constexpr (fillBF) { + cs = bookBFEvent(collision, cent, nTracksINEL); + } + registryOther.fill(HIST("hMatchedEventQA"), 6.f); + std::forward(analyze)(collision, tracks, cent, cs); + } + + template + void withMatchedBFEvent(aod::McCollisions::iterator const& mcCollision, + soa::SmallGroups const& collisions, + MyTracks const& allTracks, TAnalysis&& analyze) + { + withMatchedEvent(mcCollision, collisions, allTracks, tracksPerCollisionData, + std::forward(analyze)); + } + + // ══ Reconstructed BF in matched selected MC events ══ + void processMCRecBF(aod::McCollisions::iterator const& mcCollision, + soa::SmallGroups const& collisions, + MyV0sMC const& allV0s, + MyTracksMC const& allTracks, + aod::McParticles const& /*mcParticles*/) + { + withMatchedEvent(mcCollision, collisions, allTracks, tracksPerCollision, + [&](auto const& collision, MyTracksMC const& tracks, float cent, CentRho2Set* cs) { + auto V0s = allV0s.sliceBy(v0sPerCollision, collision.globalIndex()); + runRecoAnalysis(collision, V0s, tracks, cent, cs, mcCollision.globalIndex()); + }); + } + PROCESS_SWITCH(LambdaProtonBalanceFunction, processMCRecBF, + "Reconstructed correlations in matched selected MC events", false); + + // ══ Generated BF in the identical matched selected MC events ══ + void processMCGenBF(aod::McCollisions::iterator const& mcCollision, + soa::SmallGroups const& collisions, + MyTracks const& allTracks, + aod::McParticles const& mcParticles) + { + withMatchedBFEvent(mcCollision, collisions, allTracks, + [&](auto const&, MyTracks const&, float, CentRho2Set* cs) { + // mcParticles is auto-sliced by the MC collision; lists live only for this event. + std::vector lambdas, antiLambdas, protons, antiProtons; + for (auto const& mc : mcParticles) { + if (mc.mcCollisionId() != mcCollision.globalIndex()) { + registryOther.fill(HIST("hMatchedEventQA"), 7.f); + continue; + } + if (!mc.isPhysicalPrimary()) { + continue; + } + const int pdg = mc.pdgCode(); + const bool isLambda = pdg == PDG_t::kLambda0 || pdg == PDG_t::kLambda0Bar; + const bool isProton = pdg == PDG_t::kProton || pdg == PDG_t::kProtonBar; + if (!isLambda && !isProton) { + continue; + } + if (isLambda) { + // Count primary Lambdas before acceptance, in accepted events. + registryOther.fill(HIST("hGenLambdaDaughters"), + (pdg == PDG_t::kLambda0 ? 1.f : 3.f) + (mc.has_daughters() ? 0.f : 1.f)); + if (!mc.has_daughters()) { + continue; + } + } + auto p = makeMcInfo(mc, isLambda ? kMassLambda : kMassProton); + // Inclusive acceptance edges match reconstructed selection, not the efficiency helpers. + const float minPt = isLambda ? lambdaV0MinPt.value : pProtonMinPt.value; + const float maxPt = isLambda ? lambdaV0MaxPt.value : pProtonMaxPt.value; + const float maxY = isLambda ? lambdaV0MaxY.value : pProtonMaxY.value; + if (p.pt < minPt || p.pt > maxPt || std::abs(p.y) > maxY) { + continue; + } + // No decay-channel/daughter-acceptance requirement and no proton daughter veto. + if (pdg == PDG_t::kLambda0) { + lambdas.push_back(p); + } else if (pdg == PDG_t::kLambda0Bar) { + antiLambdas.push_back(p); + } else if (pdg == PDG_t::kProton) { + protons.push_back(p); + } else { + antiProtons.push_back(p); + } + } + for (auto const& p : lambdas) { + registryRho.fill(HIST("h2_rho1_Lambda_y"), p.y, p.phi, 1.f); + if (cs) { + cs->rho1_Lambda_y->Fill(p.y, p.phi, 1.f); + } + registryOther.fill(HIST("hPtSelLambda"), p.pt); + state.hPtSelLambda_AllPt->Fill(p.pt); + } + registryOther.fill(HIST("hNSelLambda"), static_cast(lambdas.size())); + for (auto const& p : antiLambdas) { + registryRho.fill(HIST("h2_rho1_AntiLambda_y"), p.y, p.phi, 1.f); + if (cs) { + cs->rho1_AntiLambda_y->Fill(p.y, p.phi, 1.f); + } + registryOther.fill(HIST("hPtSelAntiLambda"), p.pt); + state.hPtSelAntiLambda_AllPt->Fill(p.pt); + } + registryOther.fill(HIST("hNSelAntiLambda"), static_cast(antiLambdas.size())); + for (auto const& p : protons) { + registryRho.fill(HIST("h2_rho1_Proton_y"), p.y, p.phi, 1.f); + if (cs) { + cs->rho1_Proton_y->Fill(p.y, p.phi, 1.f); + } + registryOther.fill(HIST("hPtSelectedPrimProton"), p.pt); + state.hPtSelectedPrimProton_AllPt->Fill(p.pt); + } + registryOther.fill(HIST("hNSelectedPrimProtons"), static_cast(protons.size())); + for (auto const& p : antiProtons) { + registryRho.fill(HIST("h2_rho1_AntiProton_y"), p.y, p.phi, 1.f); + if (cs) { + cs->rho1_AntiProton_y->Fill(p.y, p.phi, 1.f); + } + registryOther.fill(HIST("hPtSelectedPrimAntiProton"), p.pt); + state.hPtSelectedPrimAntiProton_AllPt->Fill(p.pt); + } + registryOther.fill(HIST("hNSelectedPrimAntiProtons"), static_cast(antiProtons.size())); + const auto nLp = fillGeneratedPairs(lambdas, protons, false, + state.hRho2_Lp_y_AllPt, cs ? cs->Lp_y : nullptr); + registryOther.fill(HIST("hNPairs_Lambda_PrimProton"), static_cast(nLp)); + const auto nLAp = fillGeneratedPairs(lambdas, antiProtons, false, + state.hRho2_LAp_y_AllPt, cs ? cs->LAp_y : nullptr); + registryOther.fill(HIST("hNPairs_Lambda_PrimAntiProton"), static_cast(nLAp)); + const auto nALp = fillGeneratedPairs(antiLambdas, protons, false, + state.hRho2_ALp_y_AllPt, cs ? cs->ALp_y : nullptr); + registryOther.fill(HIST("hNPairs_AntiLambda_PrimProton"), static_cast(nALp)); + const auto nALAp = fillGeneratedPairs(antiLambdas, antiProtons, false, + state.hRho2_ALAp_y_AllPt, cs ? cs->ALAp_y : nullptr); + registryOther.fill(HIST("hNPairs_AntiLambda_PrimAntiProton"), static_cast(nALAp)); + fillGeneratedPairs(protons, protons, true, + state.hRho2_pp_y_AllPt, cs ? cs->pp_y : nullptr); + fillGeneratedPairs(protons, antiProtons, false, + state.hRho2_pAp_y_AllPt, cs ? cs->pAp_y : nullptr); + fillGeneratedPairs(antiProtons, protons, false, + state.hRho2_App_y_AllPt, cs ? cs->App_y : nullptr); + fillGeneratedPairs(antiProtons, antiProtons, true, + state.hRho2_ApAp_y_AllPt, cs ? cs->ApAp_y : nullptr); + fillGeneratedPairs(lambdas, lambdas, true, + state.hRho2_LL_y_AllPt, cs ? cs->LL_y : nullptr); + fillGeneratedPairs(lambdas, antiLambdas, false, + state.hRho2_LAL_y_AllPt, cs ? cs->LAL_y : nullptr); + fillGeneratedPairs(antiLambdas, lambdas, false, + state.hRho2_ALL_y_AllPt, cs ? cs->ALL_y : nullptr); + fillGeneratedPairs(antiLambdas, antiLambdas, true, + state.hRho2_ALAL_y_AllPt, cs ? cs->ALAL_y : nullptr); + }); + } + PROCESS_SWITCH(LambdaProtonBalanceFunction, processMCGenBF, + "Generated correlations in matched selected MC events", false); + + static bool usableMCIndex(aod::McParticles const* table, int64_t id) + { + // has_mcParticle() tests only id >= 0 in ASoA; also check the bound table range. + return table != nullptr && id >= 0 && id >= static_cast(table->offset()) && + id - static_cast(table->offset()) < table->size(); + } + // ── Run-level summary printed once at the end of the analysis job ───── // Called automatically by the O2 framework after all events are processed. // Prints totals accumulated in runTotalProtonBeforeVeto / runTotalProtonRemovedVeto. void endOfStream(EndOfStreamContext const&) { - const int64_t totalBefore = runTotalProtonBeforeVeto; - const int64_t totalRemoved = runTotalProtonRemovedVeto; + const int64_t totalBefore = state.runTotalProtonBeforeVeto; + const int64_t totalRemoved = state.runTotalProtonRemovedVeto; const int64_t totalAfter = totalBefore - totalRemoved; const double fracRemoved = (totalBefore > 0) ? static_cast(totalRemoved) / static_cast(totalBefore) @@ -3525,11 +4676,11 @@ struct LambdaProtonBalanceFunction { LOG(info) << "Total proton candidates after veto : " << totalAfter; LOG(info) << "Total removed by veto : " << totalRemoved; LOG(info) << Form("Fraction removed : %.6f", fracRemoved); - const double fracRemovedAp = (runTotalAntiPBeforeVeto > 0) - ? static_cast(runTotalAntiPRemovedVeto) / static_cast(runTotalAntiPBeforeVeto) + const double fracRemovedAp = (state.runTotalAntiPBeforeVeto > 0) + ? static_cast(state.runTotalAntiPRemovedVeto) / static_cast(state.runTotalAntiPBeforeVeto) : 0.0; - LOG(info) << "Total antiproton candidates before veto: " << runTotalAntiPBeforeVeto; - LOG(info) << "Total antiproton removed by veto : " << runTotalAntiPRemovedVeto; + LOG(info) << "Total antiproton candidates before veto: " << state.runTotalAntiPBeforeVeto; + LOG(info) << "Total antiproton removed by veto : " << state.runTotalAntiPRemovedVeto; LOG(info) << Form("Antiproton fraction removed : %.6f", fracRemovedAp); LOG(info) << "==============================================="; LOG(info) << ""; diff --git a/PWGCF/TwoParticleCorrelations/Tasks/lambdaR2Correlation.cxx b/PWGCF/TwoParticleCorrelations/Tasks/lambdaR2Correlation.cxx index bcf8852121d..4a5bf4ce66a 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/lambdaR2Correlation.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/lambdaR2Correlation.cxx @@ -128,11 +128,15 @@ namespace lambdatrackext DECLARE_SOA_COLUMN(LambdaSharingDaughter, lambdaSharingDaughter, bool); DECLARE_SOA_COLUMN(LambdaSharingDauIds, lambdaSharingDauIds, std::vector); DECLARE_SOA_COLUMN(TrueLambdaFlag, trueLambdaFlag, bool); +DECLARE_SOA_COLUMN(CloseLambdaTrackIds, closeLambdaTrackIds, std::vector); +DECLARE_SOA_COLUMN(CloseLambdaFlag, closeLambdaFlag, bool); } // namespace lambdatrackext DECLARE_SOA_TABLE(LambdaTracksExt, "AOD", "LAMBDATRACKSEXT", lambdatrackext::LambdaSharingDaughter, lambdatrackext::LambdaSharingDauIds, - lambdatrackext::TrueLambdaFlag); + lambdatrackext::TrueLambdaFlag, + lambdatrackext::CloseLambdaTrackIds, + lambdatrackext::CloseLambdaFlag); using LambdaTrackExt = LambdaTracksExt::iterator; @@ -160,6 +164,11 @@ using LambdaMcGenTrack = LambdaMcGenTracks::iterator; enum CollisionLabels { kTotColBeforeHasMcCollision = 1, kTotCol, + kPassVzCut, + kPassSel8, + kPassPileUpRej, + kPassGoodZVtx, + kPassGoodITSLayerAll, kPassSelCol }; @@ -182,7 +191,8 @@ enum EffCorrType { enum CentEstType { kCentFT0M = 0, - kCentFT0C + kCentFT0C, + kCentFV0A }; enum ParticleType { @@ -222,7 +232,7 @@ struct LambdaTableProducer { ConfigurableAxis cCentBins{"cCentBins", {VARIABLE_WIDTH, 0.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.f, 60.0f, 70.0f, 80.0f, 90.0f, 100.f}, "Variable Centrality Bins"}; // Collisions - Configurable cCentEstimator{"cCentEstimator", 1, "Centrality Estimator : 0-FT0M, 1-FT0C"}; + Configurable cCentEstimator{"cCentEstimator", 0, "Centrality Estimator : 0-FT0M, 1-FT0C, 2-FV0A"}; Configurable cZVtxCut{"cZVtxCut", 10.0, "Z-Vtx cut"}; Configurable cSel8Trig{"cSel8Trig", true, "Sel8 (T0A + T0C) Selection Run3"}; Configurable cPileupReject{"cPileupReject", true, "Pileup rejection"}; @@ -266,9 +276,19 @@ struct LambdaTableProducer { Configurable cSelPrimaryParticle{"cSelPrimaryParticle", false, "Select primary particle"}; Configurable cSelTrueParticle{"cSelTrueParticle", false, "Select MC PDG matched particle"}; + // MC Generator + Configurable cFwdMult{"cFwdMult", 500, "Fwd multiplicity axis config"}; + Configurable cFT0AEtaMin{"cFT0AEtaMin", 3.5, "FT0A Eta Min"}; + Configurable cFT0AEtaMax{"cFT0AEtaMax", 4.9, "FT0A Eta Max"}; + Configurable cFT0CEtaMin{"cFT0CEtaMin", -3.3, "FT0C Eta Min"}; + Configurable cFT0CEtaMax{"cFT0CEtaMax", -2.1, "FT0C Eta Max"}; + Configurable cTPCEtaMin{"cTPCEtaMin", -1., "TPC Eta Min"}; + Configurable cTPCEtaMax{"cTPCEtaMax", 1., "TPC Eta Max"}; + // CCDB Configurable cUrlCCDB{"cUrlCCDB", "http://alice-ccdb.cern.ch", "ALICE CCDB URL"}; Configurable cPathCCDBRecoEff{"cPathCCDBRecoEff", "Users/y/ypatley/LBF/PP/RecoEfficiency", "Path for ccdb-object for reco efficiency"}; + Configurable cPathCCDBMcCent{"cPathCCDBMcCent", "Users/y/ypatley/LBF/PP/McCent/MultCent", "Path for ccdb-object for MC Gen multiplicity centrality calibration"}; Configurable nolaterthan{"nolaterthan", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; // Initialize CCDB Service @@ -290,14 +310,23 @@ struct LambdaTableProducer { // Initialize Global Variables float cent = 0.; TList* ccdbObjRecoEff = nullptr; - TList* ccdbObjMatchEff = nullptr; + TList* ccdbObjMcCent = nullptr; + TH1F* hMcFwdMult = nullptr; static constexpr auto SubDir = std::array{"QA/Lambda/", "QA/AntiLambda/"}; void init(InitContext const&) { - // initialize axis specifications - const AxisSpec axisCols(5, 0.5, 5.5, ""); + // CCDB + // Set CCDB url + ccdb->setURL(cUrlCCDB.value); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setCreatedNotAfter(nolaterthan.value); + + // Initialize axis specifications + const AxisSpec axisCols(10, 0.5, 10.5, ""); const AxisSpec axisTrks(15, 0.5, 15.5, ""); + const AxisSpec axisFwdMult(cFwdMult, 0, cFwdMult, "N_{ch}^{FWD}"); const AxisSpec axisEffChecks(3, 0, 3, ""); const AxisSpec axisCent(100, 0, 100, "Centrality(%)"); const AxisSpec axisVarCent(cCentBins, "FT0C%"); @@ -310,6 +339,7 @@ struct LambdaTableProducer { const AxisSpec axisV0Rap(48, -1.2, 1.2, "y"); const AxisSpec axisV0Eta(48, -1.2, 1.2, "#eta"); const AxisSpec axisV0Phi(36, 0., TwoPI, "#phi (rad)"); + const AxisSpec axisK0SMass(200, 0.4, 0.6, "M_{#pi#pi} (GeV/#it{c}^{2})"); const AxisSpec axisRadius(5000, 0, 5, "r(cm)"); const AxisSpec axisCosPA(100, 0.99, 1.0, "cos(#theta_{PA})"); @@ -339,6 +369,8 @@ struct LambdaTableProducer { histos.add("Tracks/h1f_effcorr_info", "Efficiency correction info", kTH1F, {axisEffChecks}); histos.add("Tracks/h2f_armpod_before_sel", "Armentros-Podolanski Plot", kTH2F, {axisAlpha, axisQtarm}); histos.add("Tracks/h2f_armpod_after_sel", "Armentros-Podolanski Plot", kTH2F, {axisAlpha, axisQtarm}); + histos.add("Tracks/h1f_mass_k0s", "M_{#pi#pi}", kTH1F, {axisK0SMass}); + histos.add("Tracks/h1f_mass_rejk0s", "M_{#pi#pi}", kTH1F, {axisK0SMass}); // QA Lambda histos.add("QA/Lambda/h3f_centmasspt", "Invariant Mass", kTH3F, {axisCent, axisV0Pt, axisV0Mass}); @@ -360,14 +392,14 @@ struct LambdaTableProducer { histos.add("QA/Lambda/h2f_neg_prong_tpc_nsigma_pi_vs_p", "TPC n#sigma Neg Prong", kTH2F, {axisMomPID, axisTrackNsigma}); // Dca analysis histogram - histos.add("QA/Lambda/DCA/h3f_Dca_vs_pT_cent", "DCA", kTH3F, {axisCent, axisV0Pt, axisDcaLambda}); + histos.add("QA/Lambda/DCA/h2f_Dca_vs_pT_cent", "DCA", kTH2F, {axisV0Pt, axisDcaLambda}); // MC Generated Histograms if (doprocessMCRecoGen || doprocessMCReco) { // DCA analysis - histos.add("QA/Lambda/DCA/h3f_Prm_Dca_vs_pT_cent", "Primary DCA", kTH3F, {axisCent, axisV0Pt, axisDcaLambda}); - histos.add("QA/Lambda/DCA/h3f_Scd_Dca_vs_pT_cent", "Weak Decay DCA", kTH3F, {axisCent, axisV0Pt, axisDcaLambda}); - histos.add("QA/Lambda/DCA/h3f_Mat_Dca_vs_pT_cent", "Material DCA", kTH3F, {axisCent, axisV0Pt, axisDcaLambda}); + histos.add("QA/Lambda/DCA/h2f_Prm_Dca_vs_pT_cent", "Primary DCA", kTH2F, {axisV0Pt, axisDcaLambda}); + histos.add("QA/Lambda/DCA/h2f_Scd_Dca_vs_pT_cent", "Weak Decay DCA", kTH2F, {axisV0Pt, axisDcaLambda}); + histos.add("QA/Lambda/DCA/h2f_Mat_Dca_vs_pT_cent", "Material DCA", kTH2F, {axisV0Pt, axisDcaLambda}); // McGen Histos histos.add("McGen/h1f_collision_recgen", "# of Reco Collision Associated to One Mc Generator Collision", kTH1F, {axisMult}); @@ -392,6 +424,13 @@ struct LambdaTableProducer { // Set bin labels histos.get(HIST("Events/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kTotCol, "kTotCol"); + histos.get(HIST("Events/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kPassVzCut, "kPassVzCut"); + histos.get(HIST("Events/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kPassSel8, "kPassSel8"); + histos.get(HIST("Events/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kPassPileUpRej, "kPassPileUpRej"); + histos.get(HIST("Events/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kPassGoodZVtx, "kPassGoodZVtx"); + histos.get(HIST("Events/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kPassGoodITSLayerAll, "kPassGoodITSLayerAll"); + histos.get(HIST("Events/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kTotCol, "kTotCol"); + histos.get(HIST("Events/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kPassSelCol, "kPassSelCol"); histos.get(HIST("Tracks/h1f_lambda_info"))->GetXaxis()->SetBinLabel(LambdaLabels::kAllV0Tracks, "kAllV0Tracks"); histos.get(HIST("Tracks/h1f_lambda_info"))->GetXaxis()->SetBinLabel(LambdaLabels::kPassK0ShortMassRej, "kPassK0ShortMassRej"); @@ -400,14 +439,17 @@ struct LambdaTableProducer { histos.get(HIST("Tracks/h1f_lambda_info"))->GetXaxis()->SetBinLabel(LambdaLabels::kPassV0TopoSel, "kPassV0TopoSel"); histos.get(HIST("Tracks/h1f_lambda_info"))->GetXaxis()->SetBinLabel(LambdaLabels::kAllSelPassed, "kAllSelPassed"); + // MC generator histograms + if (doprocessMCGen) { + histos.add("Events/h1f_fwdmult", "Fwd multiplicity", kTH1F, {axisFwdMult}); + + // MC Gen Mult-Cent Calibration + ccdbObjMcCent = ccdb->getForTimeStamp(cPathCCDBMcCent.value, nolaterthan.value); + hMcFwdMult = dynamic_cast(ccdbObjMcCent->FindObject("hFwdMult")); + } + // Load correction factor if (cGetCorrectionFlag) { - // Set CCDB url - ccdb->setURL(cUrlCCDB.value); - ccdb->setCaching(true); - ccdb->setLocalObjectValidityChecking(); - ccdb->setCreatedNotAfter(nolaterthan.value); - // Get CCDB object ccdbObjRecoEff = ccdb->getForTimeStamp(cPathCCDBRecoEff.value, nolaterthan.value); @@ -464,33 +506,40 @@ struct LambdaTableProducer { if (std::abs(col.posZ()) >= cZVtxCut) { return false; } + histos.fill(HIST("Events/h1f_collisions_info"), kPassVzCut); // Run 3 Min-Bias Trigger if (cSel8Trig && !col.sel8()) { return false; } + histos.fill(HIST("Events/h1f_collisions_info"), kPassSel8); // Centrality estimator if (cCentEstimator == kCentFT0M) { cent = col.centFT0M(); } else if (cCentEstimator == kCentFT0C) { cent = col.centFT0C(); + } else if (cCentEstimator == kCentFV0A) { + cent = col.centFV0A(); } // Pileup rejection if (cPileupReject && !col.selection_bit(aod::evsel::kNoSameBunchPileup)) { return false; } + histos.fill(HIST("Events/h1f_collisions_info"), kPassPileUpRej); // Zvtx from FT0 time difference if (cZVtxTimeDiff && !col.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { return false; } + histos.fill(HIST("Events/h1f_collisions_info"), kPassGoodZVtx); // Hit all layers in ITS if (cIsGoodITSLayers && !col.selection_bit(aod::evsel::kIsGoodITSLayersAll)) { return false; } + histos.fill(HIST("Events/h1f_collisions_info"), kPassGoodITSLayerAll); return true; } @@ -585,6 +634,7 @@ struct LambdaTableProducer { } histos.fill(HIST("Tracks/h1f_lambda_info"), kPassV0TopoSel); + histos.fill(HIST("Tracks/h1f_mass_k0s"), v0.mK0Short()); // K0s mass rejection if (std::abs(v0.mK0Short() - MassK0Short) <= cKshortRejMassWindow) { @@ -659,6 +709,55 @@ struct LambdaTableProducer { return effCorrFact; } + // MC generator multiplicity estimator and MB event selection + template + bool multEstimator(P const& particles, int& fwdMult) + { + int multFT0A = 0, multFT0C = 0, multTPC = 0; + + // Select charged particles + for (auto const& particle : particles) { + // Primary + if (!particle.isPhysicalPrimary()) { + continue; + } + + // FT0C + if (particle.eta() > cFT0CEtaMin && particle.eta() < cFT0CEtaMax) { + ++multFT0C; + } + + // FT0A + if (particle.eta() > cFT0AEtaMin && particle.eta() < cFT0AEtaMax) { + ++multFT0A; + } + + // Central barrel (ITS + TPC) + if (particle.eta() > cTPCEtaMin && particle.eta() < cTPCEtaMax) { + ++multTPC; + } + } + + int multFT0M = multFT0A + multFT0C; + fwdMult = multFT0M; + + // Minimum bias trigger (1 track in A side, 1 track in C side, 1 track in central barrel) + return (multFT0A > 0 && multFT0C > 0 && multTPC > 0); + } + + // MC generator centrality + float getMcCentrality(int mult) + { + float totalArea = hMcFwdMult->Integral(); + float area = 0.; + int nx = hMcFwdMult->GetNbinsX(); + int bin = hMcFwdMult->GetXaxis()->FindBin(mult + 0.5); + for (int i = bin; i <= nx; ++i) { + area += hMcFwdMult->GetBinContent(i); + } + return (area / totalArea) * 100.; + } + // Lambda QA template void fillLambdaQAHistos(C const& col, V const& v0, T const&) @@ -670,6 +769,7 @@ struct LambdaTableProducer { // Decay length float ctau = v0.distovertotmom(col.posX(), col.posY(), col.posZ()) * MassLambda0; + histos.fill(HIST("Tracks/h1f_mass_rejk0s"), v0.mK0Short()); histos.fill(HIST(SubDir[part]) + HIST("h2f_qt_vs_alpha"), v0.alpha(), v0.qtarm()); histos.fill(HIST(SubDir[part]) + HIST("h1f_dca_V0_daughters"), v0.dcaV0daughters()); histos.fill(HIST(SubDir[part]) + HIST("h1f_dca_pos_to_PV"), v0.dcapostopv()); @@ -693,17 +793,17 @@ struct LambdaTableProducer { void getDcaHist(T const& track, float const& dca) { // Fill histograms - histos.fill(HIST(SubDir[part]) + HIST("DCA/h3f_Dca_vs_pT_cent"), cent, track.pt(), dca); + histos.fill(HIST(SubDir[part]) + HIST("DCA/h2f_Dca_vs_pT_cent"), track.pt(), dca); if constexpr (dmc == kMC) { auto mcpart = track.mcParticle(); if (mcpart.isPhysicalPrimary()) { - histos.fill(HIST(SubDir[part]) + HIST("DCA/h3f_Prm_Dca_vs_pT_cent"), cent, track.pt(), dca); + histos.fill(HIST(SubDir[part]) + HIST("DCA/h2f_Prm_Dca_vs_pT_cent"), track.pt(), dca); } else { if (mcpart.getProcess() == cWeakDecayProcess) { // Weak decay - histos.fill(HIST(SubDir[part]) + HIST("DCA/h3f_Scd_Dca_vs_pT_cent"), cent, track.pt(), dca); + histos.fill(HIST(SubDir[part]) + HIST("DCA/h2f_Scd_Dca_vs_pT_cent"), track.pt(), dca); } else { // Material - histos.fill(HIST(SubDir[part]) + HIST("DCA/h3f_Mat_Dca_vs_pT_cent"), cent, track.pt(), dca); + histos.fill(HIST(SubDir[part]) + HIST("DCA/h2f_Mat_Dca_vs_pT_cent"), track.pt(), dca); } } } @@ -796,7 +896,7 @@ struct LambdaTableProducer { // Fill Lambda/AntiLambda Table lambdaTrackTable(lambdaCollisionTable.lastIndex(), v0.pt(), v0.eta(), v0.phi(), v0.yLambda(), v0.px(), v0.py(), v0.pz(), lambdaMass, v0.template posTrack_as().index(), v0.template negTrack_as().index(), posTrackKin, negTrackKin, - (int8_t)partType, lambdaCorrFact); + static_cast(partType), lambdaCorrFact); } } @@ -851,7 +951,7 @@ struct LambdaTableProducer { } // Fill table lambdaMCGenTrackTable(lambdaMCGenCollisionTable.lastIndex(), mcpart.pt(), mcpart.eta(), mcpart.phi(), mcpart.y(), mcpart.px(), mcpart.py(), mcpart.pz(), RecoDecay::m(mcpart.p(), mcpart.e()), - daughterIDs[0], daughterIDs[1], (int8_t)partType, 1.); + daughterIDs[0], daughterIDs[1], static_cast(partType), 1.); } } @@ -882,7 +982,7 @@ struct LambdaTableProducer { // BC, Collision, tracks and V0s using BCsRun3 = soa::Join; - using Collisions = soa::Join; + using Collisions = soa::Join; using Tracks = soa::Join; using TracksMC = soa::Join; using McV0Tracks = soa::Join; @@ -905,11 +1005,7 @@ struct LambdaTableProducer { void processMCReco(soa::Join::iterator const& collision, aod::McCollisions const&, BCsRun3 const& bc, McV0Tracks const& V0s, TracksMC const& tracks, aod::McParticles const&) { - // check collision - if (!selCollision(collision)) { - return; - } - fillLambdaRecoTables(collision, bc, V0s, tracks); + fillLambdaRecoTables(collision, bc, V0s, tracks); } PROCESS_SWITCH(LambdaTableProducer, processMCReco, "Process for McReco DATA", false); @@ -923,6 +1019,55 @@ struct LambdaTableProducer { } PROCESS_SWITCH(LambdaTableProducer, processMCRecoGen, "Process for MC RecoGen", false); + + void processMCGen(aod::McCollisions::iterator const& mcCollision, aod::McParticles const& mcParticles) + { + // Multiplicity estimation + int fwdMult = 0; + bool eventTrig = multEstimator(mcParticles, fwdMult); + + // MB event selection + if (!eventTrig) { + return; + } + + // Fill fwd multiplicity histogram + histos.fill(HIST("Events/h1f_fwdmult"), fwdMult); + + // Get centrality + cent = getMcCentrality(fwdMult); + + // Fill McGen Collision Table + lambdaMCGenCollisionTable(cent, mcCollision.posX(), mcCollision.posY(), mcCollision.posZ()); + + // initialize track objects + ParticleType partType = kLambda; + + // Loop over MC particles + for (auto const& mcpart : mcParticles) { + // Check for Primary Lambda/Anti-Lambda + if (mcpart.isPhysicalPrimary() && mcpart.pdgCode() == kLambda0) { + partType = kLambda; + } else if (mcpart.isPhysicalPrimary() && mcpart.pdgCode() == kLambda0Bar) { + partType = kAntiLambda; + } else { + continue; + } + + // Kinematic selection + if (mcpart.pt() <= cLambdaMinPt || mcpart.pt() >= cLambdaMaxPt || std::abs(mcpart.y()) >= cLambdaRapCut) { + continue; + } + + histos.fill(HIST("Tracks/h1f_lambda_info"), kGenTotAccLambda); + + // Fill table + lambdaMCGenTrackTable(lambdaMCGenCollisionTable.lastIndex(), mcpart.pt(), mcpart.eta(), mcpart.phi(), mcpart.y(), mcpart.px(), mcpart.py(), mcpart.pz(), RecoDecay::m(mcpart.p(), mcpart.e()), + -1, -1, static_cast(partType), 1.); + } + } + + PROCESS_SWITCH(LambdaTableProducer, processMCGen, "Process for MC Gen", false); }; struct LambdaTracksExtProducer { @@ -932,10 +1077,16 @@ struct LambdaTracksExtProducer { // Configurables Configurable cAcceptAllLambda{"cAcceptAllLambda", false, "Accept all Lambda"}; Configurable cRejAllLambdaShaDau{"cRejAllLambdaShaDau", true, "Reject all Lambda sharing daughters"}; + Configurable cDEtaCut{"cDEtaCut", 0.02, "DEta cut"}; + Configurable cDPhiStarCut{"cDPhiStarCut", 0.02, "DPhiStar cut"}; // Histogram Registry. HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + // Objects + float magField = 0.; + std::array tpcRadii = {0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4}; + void init(InitContext const&) { // Axis Specifications @@ -953,18 +1104,69 @@ struct LambdaTracksExtProducer { histos.add("h2d_n2_etaphi_LaM_LaM", "#rho_{2}^{SharePair}", kTH2D, {axisDEta, axisDPhi}); } + template + bool checkClosePair(A const& v1, A const& v2, int charge1, int charge2) + { + // DPhiStar average over different TPC radius + float dphistar = 0., n = 0.; + for (auto const& radius : tpcRadii) { + float arg1 = 0.15 * magField * radius / v1[0]; + float arg2 = 0.15 * magField * radius / v2[0]; + if (std::abs(arg1) < 1.0 && std::abs(arg2) < 1.0) { + dphistar += v1[2] - v2[2] - (charge1 * std::abs(std::asin(arg1))) + (charge2 * std::abs(std::asin(arg2))); + ++n; + } + } + + // Inf check + if (n == 0) { + return false; + } + + // DPhiStar + dphistar = RecoDecay::constrainAngle(dphistar / n, -PIHalf); + + // DEta + float deta = v1[1] - v2[1]; + + // Return + return (std::abs(deta) < cDEtaCut && std::abs(dphistar) < cDPhiStarCut); + } + + template + bool isClosePair(V const& l1, V const& l2) + { + // Assign kinematics + std::array posTrackKinLambda1 = {l1.posTrackKin()[0], l1.posTrackKin()[1], l1.posTrackKin()[2]}; + std::array negTrackKinLambda1 = {l1.negTrackKin()[0], l1.negTrackKin()[1], l1.negTrackKin()[2]}; + std::array posTrackKinLambda2 = {l2.posTrackKin()[0], l2.posTrackKin()[1], l2.posTrackKin()[2]}; + std::array negTrackKinLambda2 = {l2.negTrackKin()[0], l2.negTrackKin()[1], l2.negTrackKin()[2]}; + + // Check close pair + bool retFlag = checkClosePair(posTrackKinLambda1, posTrackKinLambda2, 1, 1) && checkClosePair(negTrackKinLambda1, negTrackKinLambda2, -1, -1); + + // Pair reject + return retFlag; + } + void processDummy(aod::LambdaCollisions::iterator const&) {} PROCESS_SWITCH(LambdaTracksExtProducer, processDummy, "Dummy Process", true); - void processLambdaTrackExt(aod::LambdaCollisions::iterator const&, aod::LambdaTracks const& tracks) + void processLambdaTrackExt(aod::LambdaCollisions::iterator const& collision, aod::LambdaTracks const& tracks) { int nTotLambda = 0, nTotAntiLambda = 0, nSelLambda = 0, nSelAntiLambda = 0; + magField = collision.bField(); for (auto const& lambda : tracks) { + // Shared daughter objects bool lambdaSharingDauFlag = false, trueLambdaFlag = false; std::vector vSharedDauLambdaIndex; + // Index of lambda track close to current lambda + bool isCloseLambdaFlag = false; + std::vector vCloseLambdaTrackIndex; + if (lambda.partType() == kLambda) { ++nTotLambda; } else if (lambda.partType() == kAntiLambda) { @@ -972,12 +1174,12 @@ struct LambdaTracksExtProducer { } for (auto const& track : tracks) { - // check lambda index (don't analyze same lambda track !!!) + // Check lambda index (don't analyze same lambda track !!!) if (lambda.index() == track.index()) { continue; } - // check if lambda shares daughters with any other track + // Check if lambda shares daughters with any other lambda track if (lambda.posTrackId() == track.posTrackId() || lambda.negTrackId() == track.negTrackId()) { vSharedDauLambdaIndex.push_back(track.index()); lambdaSharingDauFlag = true; @@ -991,6 +1193,13 @@ struct LambdaTracksExtProducer { histos.fill(HIST("h2d_n2_etaphi_LaM_LaM"), lambda.eta() - track.eta(), RecoDecay::constrainAngle((lambda.phi() - track.phi()), -PIHalf)); } } + + // Check if this lambda is close to lambda track + // Keep the index of this lambda track + if (isClosePair(lambda, track)) { + isCloseLambdaFlag = true; + vCloseLambdaTrackIndex.push_back(track.index()); + } } // Accept / Reject @@ -1006,7 +1215,7 @@ struct LambdaTracksExtProducer { } // Fill LambdaTrackExt table - lambdaTrackExtTable(lambdaSharingDauFlag, vSharedDauLambdaIndex, trueLambdaFlag); + lambdaTrackExtTable(lambdaSharingDauFlag, vSharedDauLambdaIndex, trueLambdaFlag, vCloseLambdaTrackIndex, isCloseLambdaFlag); } // fill multiplicity histograms @@ -1045,14 +1254,8 @@ struct LambdaR2Correlation { Configurable cAnaPairs{"cAnaPairs", false, "Analyze Pairs Flag"}; Configurable cAnaEff{"cAnaEff", false, "Analyze efficiency flag"}; - // Lambda Kaon femtoscopic correction - Configurable cApplyFemtoSel{"cApplyFemtoSel", false, "Femto qinv selection"}; - Configurable cFemtoCut{"cFemtoCut", 0.1, "Femto qinv cut"}; - - // Lambda Kaon two-track cuts - Configurable cApplyTwoTrackCut{"cApplyTwoTrackCut", false, "Flag for two track cut"}; - Configurable cDEtaCut{"cDEtaCut", 0.02, "DEta cut"}; - Configurable cDPhiStarCut{"cDPhiStarCut", 0.02, "DPhiStar cut"}; + // Lambda close pair rejection + Configurable cRejCloseLambda{"cRejCloseLambda", false, "Flag close Lambda rej"}; // Centrality Axis ConfigurableAxis cCentBins{"cCentBins", {VARIABLE_WIDTH, 0.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.f, 60.0f, 70.0f, 80.0f, 90.0f, 100.f}, "Variable Mult-Bins"}; @@ -1072,8 +1275,10 @@ struct LambdaR2Correlation { float phibinwidth = 0.; float q = 0., e = 0., qinv = 0.; float cent = 0., posZ = 0.; - float magField = 0.; - std::array tpcRadii = {0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4}; + + static constexpr auto SubDirRecGen = std::array{"Reco/", "McGen/"}; + static constexpr auto SubDirHist = std::array{"LaP", "LaM"}; + static constexpr auto SubDirPairHist = std::array{"LaP_LaM", "LaP_LaP", "LaM_LaM"}; void init(InitContext const&) { @@ -1108,11 +1313,17 @@ struct LambdaR2Correlation { histos.add("Event/Reco/h1f_collision_posz", "V_{Z} Distribution", kTH1F, {axisPosZ}); histos.add("Event/Reco/h1f_ft0m_mult_percentile", "FT0M (%)", kTH1F, {axisCent}); - // Two track cut - histos.add("QA/TwoTrackCut/Before/h2d_n2_detadphi", "#rho_{2}", kTH2D, {axisDEta, axisDPhi}); - histos.add("QA/TwoTrackCut/After/h2d_n2_detadphi", "#rho_{2}", kTH2D, {axisDEta, axisDPhi}); - histos.add("QA/FemtoCut/Before/h2d_n2_detadphi", "#rho_{2}", kTH2D, {axisDEta, axisDPhi}); - histos.add("QA/FemtoCut/After/h2d_n2_detadphi", "#rho_{2}", kTH2D, {axisDEta, axisDPhi}); + // Close pair checks + histos.add("QA/CloseLambda/Before/h2d_n1_centpt_LaP", "#rho_{1}", kTH2D, {axisCent, axisPtLambda}); + histos.add("QA/CloseLambda/Before/h2d_n1_centpt_LaM", "#rho_{1}", kTH2D, {axisCent, axisPtLambda}); + histos.add("QA/CloseLambda/Before/h2d_n2_detadphi_LaP_LaM", "#rho_{2}", kTH2D, {axisDEta, axisDPhi}); + histos.add("QA/CloseLambda/Before/h2d_n2_detadphi_LaP_LaP", "#rho_{2}", kTH2D, {axisDEta, axisDPhi}); + histos.add("QA/CloseLambda/Before/h2d_n2_detadphi_LaM_LaM", "#rho_{2}", kTH2D, {axisDEta, axisDPhi}); + histos.add("QA/CloseLambda/After/h2d_n1_centpt_LaP", "#rho_{1}", kTH2D, {axisCent, axisPtLambda}); + histos.add("QA/CloseLambda/After/h2d_n1_centpt_LaM", "#rho_{1}", kTH2D, {axisCent, axisPtLambda}); + histos.add("QA/CloseLambda/After/h2d_n2_detadphi_LaP_LaM", "#rho_{2}", kTH2D, {axisDEta, axisDPhi}); + histos.add("QA/CloseLambda/After/h2d_n2_detadphi_LaP_LaP", "#rho_{2}", kTH2D, {axisDEta, axisDPhi}); + histos.add("QA/CloseLambda/After/h2d_n2_detadphi_LaM_LaM", "#rho_{2}", kTH2D, {axisDEta, axisDPhi}); // Efficiency Histograms // Single Particle Efficiencies @@ -1150,86 +1361,9 @@ struct LambdaR2Correlation { } } - template - bool checkClosePair(A const& v1, A const& v2, int charge1, int charge2) - { - // DPhiStar average over different TPC radius - float dphistar = 0., n = 0.; - for (auto const& radius : tpcRadii) { - float arg1 = 0.15 * magField * radius / v1[0]; - float arg2 = 0.15 * magField * radius / v2[0]; - if (std::abs(arg1) < 1.0 && std::abs(arg2) < 1.0) { - dphistar += v1[2] - v2[2] - (charge1 * std::abs(std::asin(arg1))) + (charge2 * std::abs(std::asin(arg2))); - ++n; - } - } - - // Inf check - if (n == 0) { - return false; - } - - // DPhiStar - dphistar = RecoDecay::constrainAngle(dphistar / n, -PIHalf); - - // DEta - float deta = v1[1] - v2[1]; - - // Return - return (std::abs(deta) < cDEtaCut && std::abs(dphistar) < cDPhiStarCut); - } - - template - bool isClosePair(V const& l1, V const& l2) - { - // Before - histos.fill(HIST("QA/TwoTrackCut/Before/h2d_n2_detadphi"), l1.eta() - l2.eta(), RecoDecay::constrainAngle(l1.phi() - l2.phi(), -PIHalf)); - - // Assign kinematics - std::array posTrackKinLambda1 = {l1.posTrackKin()[0], l1.posTrackKin()[1], l1.posTrackKin()[2]}; - std::array negTrackKinLambda1 = {l1.negTrackKin()[0], l1.negTrackKin()[1], l1.negTrackKin()[2]}; - std::array posTrackKinLambda2 = {l2.posTrackKin()[0], l2.posTrackKin()[1], l2.posTrackKin()[2]}; - std::array negTrackKinLambda2 = {l2.negTrackKin()[0], l2.negTrackKin()[1], l2.negTrackKin()[2]}; - - // Check close pair - bool retFlag = checkClosePair(posTrackKinLambda1, posTrackKinLambda2, 1, 1) && checkClosePair(negTrackKinLambda1, negTrackKinLambda2, -1, -1); - - // Fill QA - if (!retFlag) { // Pair accept - histos.fill(HIST("QA/TwoTrackCut/After/h2d_n2_detadphi"), l1.eta() - l2.eta(), RecoDecay::constrainAngle(l1.phi() - l2.phi(), -PIHalf)); - } - - // Pair reject - return retFlag; - } - - template - bool isCloseQinv(V const& p1, T const& p2) - { - // Before - histos.fill(HIST("QA/FemtoCut/Before/h2d_n2_detadphi"), p1.eta() - p2.eta(), RecoDecay::constrainAngle(p1.phi() - p2.phi(), -PIHalf)); - - // Calculate qinv - q = RecoDecay::p((p1.px() - p2.px()), (p1.py() - p2.py()), (p1.pz() - p2.pz())); - e = RecoDecay::e(p1.px(), p1.py(), p1.pz(), MassLambda0) - RecoDecay::e(p2.px(), p2.py(), p2.pz(), MassKaonCharged); - qinv = std::sqrt(-RecoDecay::m2(q, e)); - - // Pair accept - if (qinv > cFemtoCut) { - histos.fill(HIST("QA/FemtoCut/After/h2d_n2_detadphi"), p1.eta() - p2.eta(), RecoDecay::constrainAngle(p1.phi() - p2.phi(), -PIHalf)); - return false; - } - - // Pair reject - return true; - } - template void fillPairHistos(T1& p1, T2& p2) { - static constexpr auto SubDirRecGen = std::array{"Reco/", "McGen/"}; - static constexpr auto SubDirHist = std::array{"LaP_LaM", "LaP_LaP", "LaM_LaM"}; - const auto rapbin1 = static_cast((p1.rap() - kminrap) / rapbinwidth); const auto rapbin2 = static_cast((p2.rap() - kminrap) / rapbinwidth); @@ -1243,17 +1377,25 @@ struct LambdaR2Correlation { const int rapphix = rapbin1 * nphibins + phibin1; const int rapphiy = rapbin2 * nphibins + phibin2; - histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("h3f_n2_rapphi_") + HIST(SubDirHist[part_pair]), cent, rapphix + 0.5, rapphiy + 0.5, corfac); + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("h3f_n2_rapphi_") + HIST(SubDirPairHist[part_pair]), cent, rapphix + 0.5, rapphiy + 0.5, corfac); } } template void analyzeSingles(T const& tracks) { - static constexpr auto SubDirRecGen = std::array{"Reco/", "McGen/"}; - static constexpr auto SubDirHist = std::array{"LaP", "LaM"}; - for (auto const& track : tracks) { + if constexpr (rec_gen == kRec) { + // Close pair lambda + histos.fill(HIST("QA/CloseLambda/Before/h2d_n1_centpt_") + HIST(SubDirHist[part]), cent, track.pt()); + if (track.closeLambdaFlag()) { + histos.fill(HIST("QA/CloseLambda/After/h2d_n1_centpt_") + HIST(SubDirHist[part]), cent, track.pt()); + if (cRejCloseLambda) { + continue; + } + } + } + // Efficiency Plots if (cAnaEff) { histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("Efficiency/h2f_n1_centpt_") + HIST(SubDirHist[part]), cent, track.pt()); @@ -1275,19 +1417,21 @@ struct LambdaR2Correlation { { for (auto const& trk_1 : trks_1) { for (auto const& trk_2 : trks_2) { - // check for same index for Lambda-Lambda / AntiLambda-AntiLambda + // Check for same index for Lambda-Lambda / AntiLambda-AntiLambda if (same && ((trk_1.index() == trk_2.index()))) { continue; } - // Lambda close pair rejection + // Close pair if constexpr (rec_gen == kRec) { - // Close pair - if (cApplyTwoTrackCut && isClosePair(trk_1, trk_2)) { - continue; + // Close pair histograms + histos.fill(HIST("QA/CloseLambda/Before/h2d_n2_detadphi_") + HIST(SubDirPairHist[partpair]), trk_1.eta() - trk_2.eta(), RecoDecay::constrainAngle(trk_1.phi() - trk_2.phi(), -PIHalf)); + if (trk_1.closeLambdaFlag() && trk_2.closeLambdaFlag()) { + histos.fill(HIST("QA/CloseLambda/After/h2d_n2_detadphi_") + HIST(SubDirPairHist[partpair]), trk_1.eta() - trk_2.eta(), RecoDecay::constrainAngle(trk_1.phi() - trk_2.phi(), -PIHalf)); } - // Femto selection - if (cApplyFemtoSel && isCloseQinv(trk_1, trk_2)) { + + // Close pair lambda rejection + if (cRejCloseLambda && (trk_1.closeLambdaFlag() || trk_2.closeLambdaFlag())) { continue; } } @@ -1302,8 +1446,8 @@ struct LambdaR2Correlation { using LambdaTracks = soa::Join; SliceCache cache; - Partition partLambdaTracks = (aod::lambdatrack::partType == (int8_t)kLambda) && (aod::lambdatrackext::trueLambdaFlag == true); - Partition partAntiLambdaTracks = (aod::lambdatrack::partType == (int8_t)kAntiLambda) && (aod::lambdatrackext::trueLambdaFlag == true); + Partition partLambdaTracks = (aod::lambdatrack::partType == static_cast(kLambda)) && (aod::lambdatrackext::trueLambdaFlag == true); + Partition partAntiLambdaTracks = (aod::lambdatrack::partType == static_cast(kAntiLambda)) && (aod::lambdatrackext::trueLambdaFlag == true); void processDummy(aod::LambdaCollisions::iterator const&) {} @@ -1316,7 +1460,6 @@ struct LambdaR2Correlation { posZ = collision.posZ(); cent = collision.cent(); - magField = collision.bField(); auto lambdaTracks = partLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); auto antiLambdaTracks = partAntiLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); @@ -1338,8 +1481,8 @@ struct LambdaR2Correlation { using LambdaMcGenTracks = aod::LambdaMcGenTracks; SliceCache cachemc; - Partition partMcLambdaTracks = (aod::lambdatrack::partType == (int8_t)kLambda); - Partition partMcAntiLambdaTracks = (aod::lambdatrack::partType == (int8_t)kAntiLambda); + Partition partMcLambdaTracks = (aod::lambdatrack::partType == static_cast(kLambda)); + Partition partMcAntiLambdaTracks = (aod::lambdatrack::partType == static_cast(kAntiLambda)); void processMCGen(LambdaMcGenCollisions::iterator const& mcgencol, LambdaMcGenTracks const&) { diff --git a/PWGCF/TwoParticleCorrelations/Tasks/lambdaSpinPolarization.cxx b/PWGCF/TwoParticleCorrelations/Tasks/lambdaSpinPolarization.cxx index f5d26c6fbaa..0d0f413c951 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/lambdaSpinPolarization.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/lambdaSpinPolarization.cxx @@ -14,6 +14,7 @@ ///\author Subhadeep Roy /// \author Yash Patley , +#include "PWGCF/TwoParticleCorrelations/DataModel/LambdaSpinPolarizationDerived.h" #include "PWGLF/DataModel/LFStrangenessTables.h" #include "Common/CCDB/EventSelectionParams.h" @@ -30,11 +31,9 @@ #include #include #include -#include #include #include #include -#include #include #include #include @@ -43,18 +42,27 @@ #include #include +#include #include +#include #include +#include #include #include #include +#include #include +#include #include #include #include +#include +#include #include #include +#include +#include #include using namespace o2; @@ -70,7 +78,6 @@ namespace lambdacollision DECLARE_SOA_COLUMN(Cent, cent, float); DECLARE_SOA_COLUMN(Mult, mult, float); DECLARE_SOA_COLUMN(TimeStamp, timeStamp, uint64_t); -// DECALRE_SOA_ } // namespace lambdacollision DECLARE_SOA_TABLE(LambdaCollisions, "AOD", "LAMBDACOLS", o2::soa::Index<>, @@ -124,15 +131,10 @@ using LambdaTrack = LambdaTracks::iterator; namespace lambdatrackext { DECLARE_SOA_COLUMN(LambdaSharingDaughter, lambdaSharingDaughter, bool); -DECLARE_SOA_COLUMN(LambdaSharingDauIds, lambdaSharingDauIds, - std::vector); -DECLARE_SOA_COLUMN(TrueLambdaFlag, trueLambdaFlag, bool); } // namespace lambdatrackext DECLARE_SOA_TABLE(LambdaTracksExt, "AOD", "LAMBDATRACKSEXT", - lambdatrackext::LambdaSharingDaughter, - lambdatrackext::LambdaSharingDauIds, - lambdatrackext::TrueLambdaFlag); + lambdatrackext::LambdaSharingDaughter); using LambdaTrackExt = LambdaTracksExt::iterator; namespace lambdamcgentrack @@ -172,21 +174,23 @@ using LambdaMixEventMcGenCollision = LambdaMixEventMcGenCollisions::iterator; namespace lambdamixeventtracks { -// DECLARE_SOA_INDEX_COLUMN(LambdaMixEventCollision, lambdaMixEventCollision); DECLARE_SOA_COLUMN(LambdaMixEventCollisionIdx, lambdaMixEventCollisionIdx, int); DECLARE_SOA_COLUMN(LambdaMixEventTrackIdx, lambdaMixEventTrackIdx, int); DECLARE_SOA_COLUMN(LambdaMixEventTimeStamp, lambdaMixEventTimeStamp, uint64_t); +DECLARE_SOA_COLUMN(LambdaMixEventPosTrackIdx, lambdaMixEventPosTrackIdx, int); +DECLARE_SOA_COLUMN(LambdaMixEventNegTrackIdx, lambdaMixEventNegTrackIdx, int); } // namespace lambdamixeventtracks DECLARE_SOA_TABLE(LambdaMixEventTracks, "AOD", "LAMBDAMIXTRKS", o2::soa::Index<>, - // lambdamixeventtracks::LambdaMixEventCollisionId, lambdamixeventtracks::LambdaMixEventCollisionIdx, lambdamixeventtracks::LambdaMixEventTrackIdx, lambdatrack::Px, lambdatrack::Py, lambdatrack::Pz, lambdatrack::Mass, lambdatrack::PrPx, lambdatrack::PrPy, lambdatrack::PrPz, lambdatrack::V0Type, - lambdamixeventtracks::LambdaMixEventTimeStamp); + lambdamixeventtracks::LambdaMixEventTimeStamp, + lambdamixeventtracks::LambdaMixEventPosTrackIdx, + lambdamixeventtracks::LambdaMixEventNegTrackIdx); using LambdaMixEventTrack = LambdaMixEventTracks::iterator; namespace lambdamixeventmcgentracks @@ -207,6 +211,21 @@ enum CollisionLabels { kTotColBeforeHasMcCollision = 1, kTotCol, kPassSelCol }; +enum EventCutFlow { kEvAll = 1, + kEvTrigger, + kEvTvx, + kEvTFBorder, + kEvItsRofBorder, + kEvItsTpcVtx, + kEvNoSameBunchPileup, + kEvGoodZvtxFT0vsPV, + kEvGoodItsLayers, + kEvCentrality, + kEvVz, + kEvOneV0, + kEvTwoV0, + kNEventCutFlow }; + enum TrackLabels { kTracksBeforeHasMcParticle = 1, kAllV0Tracks, @@ -245,23 +264,38 @@ enum RunType { kRun3 = 0, enum ParticleType { kLambda = 0, kAntiLambda }; -enum ParticlePairType { - - kLambdaAntiLambda = 0, - kAntiLambdaLambda = 1, - kLambdaLambda = 2, - kAntiLambdaAntiLambda = 3, - - kLambdaSBAntiLambda = 4, - kAntiLambdaSBLambda = 5, - kLambdaSBLambda = 6, - kAntiLambdaSBAntiLambda = 7, - - kLambdaSBSBAntiLambda = 8, - kAntiLambdaSBSBLambda = 9, - kLambdaSBSBLambda = 10, - kAntiLambdaSBSBAntiLambda = 11 -}; +enum ParticlePairType { kLambdaAntiLambda = 0, + kAntiLambdaLambda, + kLambdaLambda, + kAntiLambdaAntiLambda }; + +enum PairTier { kSameEvent = 0, + kSameEventInMixing, + kMixedEvent, + kMcGenSameEvent, + kMcGenSameEventInMixing, + kMcGenMixedEvent, + kSameEventSharedDau, + kSameEventInMixingSharedDau }; + +enum SharedDauRule { kSharedDauMaxSet = 0, + kSharedDauPairVeto, + kSharedDauDropAll }; + +enum PairEventCount { kPeAll = 1, + kPeOneCand, + kPeTwoCand, + kPeUnlikeSign, + kPeLambdaLambda, + kPeAntiLambdaAntiLambda, + kNPairEventCount }; + +enum MEEventStatus { kMEEventSeen = 1, + kMEEventOutsidePools, + kMEEventWarmUp, + kMEEventMixed, + kMEEventDonated, + kNMEEventStatus }; enum ShareDauLambda { kUniqueLambda = 0, kLambdaShareDau }; @@ -284,9 +318,10 @@ enum PrmScdPairType { kPP = 0, kSP, kSS }; -// Number of daughters of the two-body decay Lambda -> p + pi static constexpr std::size_t NDaughtersTwoBody = 2; +static constexpr std::size_t NCandidatesForPair = 2; + struct LambdaTableProducer { Produces lambdaCollisionTable; @@ -294,8 +329,7 @@ struct LambdaTableProducer { Produces lambdaMCGenCollisionTable; Produces lambdaMCGenTrackTable; - Configurable cCentEstimator{"cCentEstimator", 0, - "Centrality Estimator: 0=FT0M, 1=FT0C"}; + Configurable cCentEstimator{"cCentEstimator", 0, "Centrality Estimator: 0=FT0M, 1=FT0C"}; Configurable cMinZVtx{"cMinZVtx", -10.0, "Min VtxZ (cm)"}; Configurable cMaxZVtx{"cMaxZVtx", 10.0, "Max VtxZ (cm)"}; Configurable cMinMult{"cMinMult", 0.0, "Min centrality percentile"}; @@ -303,130 +337,83 @@ struct LambdaTableProducer { Configurable cSel8Trig{"cSel8Trig", true, "Sel8 (T0A+T0C) Run3"}; Configurable cInt7Trig{"cInt7Trig", false, "kINT7 MB Trigger"}; Configurable cSel7Trig{"cSel7Trig", false, "Sel7 (V0A+V0C) Run2"}; - Configurable cTriggerTvxSel{"cTriggerTvxSel", false, - "TVX Trigger Selection"}; - Configurable cTFBorder{"cTFBorder", false, - "Timeframe Border Selection"}; - Configurable cNoItsROBorder{"cNoItsROBorder", false, - "No ITSRO Border Cut"}; - Configurable cItsTpcVtx{"cItsTpcVtx", false, - "ITS+TPC Vertex Selection"}; + Configurable cTriggerTvxSel{"cTriggerTvxSel", false, "TVX Trigger Selection"}; + Configurable cTFBorder{"cTFBorder", false, "Timeframe Border Selection"}; + Configurable cNoItsROBorder{"cNoItsROBorder", false, "No ITSRO Border Cut"}; + Configurable cItsTpcVtx{"cItsTpcVtx", false, "ITS+TPC Vertex Selection"}; Configurable cPileupReject{"cPileupReject", false, "Pileup rejection"}; - Configurable cZVtxTimeDiff{"cZVtxTimeDiff", false, - "z-vtx time diff selection"}; - Configurable cIsGoodITSLayers{"cIsGoodITSLayers", false, - "Good ITS Layers All"}; + Configurable cZVtxTimeDiff{"cZVtxTimeDiff", false, "z-vtx time diff selection"}; + Configurable cIsGoodITSLayers{"cIsGoodITSLayers", false, "Good ITS Layers All"}; - // Tracks Configurable cTrackMinPt{"cTrackMinPt", 0.15, "p_{T} minimum"}; Configurable cTrackMaxPt{"cTrackMaxPt", 999.0, "p_{T} maximum"}; Configurable cTrackEtaCut{"cTrackEtaCut", 0.8, "Pseudorapidity cut"}; - Configurable cMinTpcCrossedRows{"cMinTpcCrossedRows", 70, - "TPC Min Crossed Rows"}; - Configurable cMinTpcCROverCls{"cMinTpcCROverCls", 0.8, - "TPC Min CR/Findable Cls"}; - Configurable cMaxTpcSharedClusters{"cMaxTpcSharedClusters", 0.4, - "TPC Max Shared Clusters"}; - Configurable cMaxChi2Tpc{"cMaxChi2Tpc", 4, "Max TPC Chi2/ndf"}; - Configurable cTpcNsigmaCut{"cTpcNsigmaCut", 3.0, - "TPC nSigma PID cut"}; - Configurable cRemoveAmbiguousTracks{"cRemoveAmbiguousTracks", false, - "Remove Ambiguous Tracks"}; - - Configurable cMinDcaProtonToPV{"cMinDcaProtonToPV", 0.02, - "Min proton DCA to PV (cm)"}; - Configurable cMinDcaPionToPV{"cMinDcaPionToPV", 0.06, - "Min pion DCA to PV (cm)"}; - Configurable cMinV0DcaDaughters{"cMinV0DcaDaughters", 0., - "Min DCA between V0 daughters"}; - Configurable cMaxV0DcaDaughters{"cMaxV0DcaDaughters", 1., - "Max DCA between V0 daughters"}; + Configurable cMinTpcCrossedRows{"cMinTpcCrossedRows", 70, "TPC Min Crossed Rows"}; + Configurable cMinTpcCROverCls{"cMinTpcCROverCls", -999, "TPC Min CR/Findable Cls"}; + Configurable cMaxTpcSharedClusters{"cMaxTpcSharedClusters", 999, "TPC Max Shared Clusters"}; + Configurable cMaxChi2Tpc{"cMaxChi2Tpc", 999, "Max TPC Chi2/ndf"}; + Configurable cTpcNsigmaCut{"cTpcNsigmaCut", 5.0, "TPC nSigma PID cut"}; + Configurable cRemoveAmbiguousTracks{"cRemoveAmbiguousTracks", false, "Remove Ambiguous Tracks"}; + + Configurable cMinDcaProtonToPV{"cMinDcaProtonToPV", 0.05, "Min proton DCA to PV (cm)"}; + Configurable cMinDcaPionToPV{"cMinDcaPionToPV", 0.1, "Min pion DCA to PV (cm)"}; + Configurable cMinV0DcaDaughters{"cMinV0DcaDaughters", 0., "Min DCA between V0 daughters"}; + Configurable cMaxV0DcaDaughters{"cMaxV0DcaDaughters", 1.4, "Max DCA between V0 daughters"}; Configurable cMinDcaV0ToPV{"cMinDcaV0ToPV", 0.0, "Min DCA V0 to PV"}; - Configurable cMaxDcaV0ToPV{"cMaxDcaV0ToPV", 999.0, - "Max DCA V0 to PV"}; - Configurable cMinV0TransRadius{"cMinV0TransRadius", 0.5, - "Min V0 decay radius (cm)"}; - Configurable cMaxV0TransRadius{"cMaxV0TransRadius", 999.0, - "Max V0 decay radius (cm)"}; + Configurable cMaxDcaV0ToPV{"cMaxDcaV0ToPV", 0.1, "Max DCA V0 to PV"}; + Configurable cMinV0TransRadius{"cMinV0TransRadius", 0.5, "Min V0 decay radius (cm)"}; + Configurable cMaxV0TransRadius{"cMaxV0TransRadius", 999.0, "Max V0 decay radius (cm)"}; Configurable cMinV0CTau{"cMinV0CTau", 0.0, "Min cTau (cm)"}; Configurable cMaxV0CTau{"cMaxV0CTau", 30.0, "Max cTau (cm)"}; Configurable cMinV0CosPA{"cMinV0CosPA", 0.995, "Min V0 cos(PA)"}; - Configurable cKshortRejMassWindow{"cKshortRejMassWindow", 0.01, - "K0s mass rejection window"}; - Configurable cKshortRejFlag{"cKshortRejFlag", true, - "K0s mass rejection flag"}; - - // V0s kinmatic acceptance - Configurable cMinV0Mass{"cMinV0Mass", 1.10, "V0 Mass Min"}; - Configurable cMaxV0Mass{"cMaxV0Mass", 1.12, "V0 Mass Max"}; - Configurable cMinV0Pt{"cMinV0Pt", 0.8, "Minimum V0 pT"}; - Configurable cMaxV0Pt{"cMaxV0Pt", 4.2, "Minimum V0 pT"}; + Configurable cKshortRejMassWindow{"cKshortRejMassWindow", 0.001, "K0s mass rejection window"}; + Configurable cKshortRejFlag{"cKshortRejFlag", true, "K0s mass rejection flag"}; + + Configurable cMinV0Mass{"cMinV0Mass", 1.09, "V0 Mass Min"}; + Configurable cMaxV0Mass{"cMaxV0Mass", 1.14, "V0 Mass Max"}; + Configurable cMinV0Pt{"cMinV0Pt", 0.6, "Minimum V0 pT"}; + Configurable cMaxV0Pt{"cMaxV0Pt", 3.0, "Maximum V0 pT"}; Configurable cMaxV0Rap{"cMaxV0Rap", 0.5, "|rap| cut"}; Configurable cDoEtaAnalysis{"cDoEtaAnalysis", false, "Do Eta Analysis"}; - Configurable cV0TypeSelFlag{"cV0TypeSelFlag", false, - "V0 Type Selection Flag"}; - Configurable cV0TypeSelection{"cV0TypeSelection", 1, - "V0 Type Selection"}; + Configurable cV0TypeSelFlag{"cV0TypeSelFlag", true, "V0 Type Selection Flag"}; + Configurable cV0TypeSelection{"cV0TypeSelection", 1, "V0 Type Selection"}; - // V0s MC Configurable cHasMcFlag{"cHasMcFlag", true, "Has Mc Tag"}; - Configurable cSelectTrueLambda{"cSelectTrueLambda", true, - "Select True Lambda"}; - Configurable cSelMCPSV0{"cSelMCPSV0", true, - "Select Primary/Secondary V0"}; - Configurable cCheckRecoDauFlag{"cCheckRecoDauFlag", true, - "Check for reco daughter PID"}; - Configurable cGenPrimaryLambda{"cGenPrimaryLambda", true, - "Primary Generated Lambda"}; - Configurable cGenSecondaryLambda{"cGenSecondaryLambda", false, - "Secondary Generated Lambda"}; - Configurable cGenDecayChannel{"cGenDecayChannel", true, - "Gen Level Decay Channel Flag"}; + Configurable cSelectTrueLambda{"cSelectTrueLambda", false, "Select True Lambda"}; + Configurable cSelMCPSV0{"cSelMCPSV0", false, "Select Primary/Secondary V0"}; + Configurable cCheckRecoDauFlag{"cCheckRecoDauFlag", true, "Check for reco daughter PID"}; + Configurable cGenPrimaryLambda{"cGenPrimaryLambda", true, "Primary Generated Lambda"}; + Configurable cGenSecondaryLambda{"cGenSecondaryLambda", false, "Secondary Generated Lambda"}; + Configurable cGenDecayChannel{"cGenDecayChannel", false, "Gen Level Decay Channel Flag"}; Configurable cRecoMomResoFlag{"cRecoMomResoFlag", false, "Check effect of momentum space smearing on balance function"}; - // Efficiency Correction - Configurable cCorrectionFlag{"cCorrectionFlag", false, - "Correction Flag"}; - Configurable cGetEffFact{"cGetEffFact", false, - "Get Efficiency Factor Flag"}; - Configurable cGetPrimFrac{"cGetPrimFrac", false, - "Get Primary Fraction Flag"}; - Configurable cCorrFactHist{"cCorrFactHist", 0, - "Efficiency Factor Histogram"}; - Configurable cPrimFracHist{"cPrimFracHist", 0, - "Primary Fraction Histogram"}; - - // CCDB - Configurable cUrlCCDB{"cUrlCCDB", "http://ccdb-test.cern.ch:8080", "url of ccdb"}; - Configurable cPathCCDB{"cPathCCDB", "Users/y/ypatley/lambda_corr_fact", "Path for ccdb-object"}; - - // Initialize CCDB Service + Configurable cCorrectionFlag{"cCorrectionFlag", false, "Correction Flag"}; + Configurable cGetEffFact{"cGetEffFact", true, "Get Efficiency Factor Flag"}; + Configurable cGetPrimFrac{"cGetPrimFrac", false, "Get Primary Fraction Flag"}; + Configurable cCorrFactHist{"cCorrFactHist", 0, "Efficiency Factor Histogram"}; + Configurable cPrimFracHist{"cPrimFracHist", 0, "Primary Fraction Histogram"}; + + Configurable cUrlCCDB{"cUrlCCDB", "http://alice-ccdb.cern.ch", "url of ccdb"}; + Configurable cPathCCDB{"cPathCCDB", "Users/y/ypatley/SpinCorr/RecoEfficiency", "Path for ccdb-object"}; + Service ccdb{}; - // Histogram Registry. - HistogramRegistry histos{ - "histos", - {}, - OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - // initialize corr_factor objects std::vector> vCorrFactStrings = { {"hEffVsPtCentLambda", "hEffVsPtCentAntiLambda"}, {"hEffVsPtYCentLambda", "hEffVsPtYCentAntiLambda"}, {"hEffVsPtEtaCentLambda", "hEffVsPtEtaCentAntiLambda"}}; - // initialize corr_factor objects std::vector> vPrimFracStrings = { {"hPrimFracVsPtCentLambda", "hPrimFracVsPtCentAntiLambda"}, {"hPrimFracVsPtYCentLambda", "hPrimFracVsPtYCentAntiLambda"}, {"hPrimFracVsPtEtaCentLambda", "hPrimFracVsPtEtaCentAntiLambda"}}; - // Initialize Global Variables float cent = 0., mult = 0.; void init(InitContext const&) { - // Set CCDB url ccdb->setURL(cUrlCCDB.value); ccdb->setCaching(true); @@ -437,7 +424,7 @@ struct LambdaTableProducer { const AxisSpec axisVz(220, -11, 11, "V_{z} (cm)"); const AxisSpec axisPID(8000, -4000, 4000, "PdgCode"); - const AxisSpec axisV0Mass(120, 1.08, 1.20, "M_{p#pi} (GeV/#it{c}^{2})"); + const AxisSpec axisV0Mass(200, 1.08, 1.18, "M_{p#pi} (GeV/#it{c}^{2})"); const AxisSpec axisV0Pt(100., 0., 10., "p_{T} (GeV/#it{c})"); const AxisSpec axisV0Rap(48, -1.2, 1.2, "y"); const AxisSpec axisV0Eta(48, -1.2, 1.2, "#eta"); @@ -458,102 +445,77 @@ struct LambdaTableProducer { const AxisSpec axisNsigma(401, -10.025, 10.025, "n#sigma"); const AxisSpec axisdEdx(360, 20, 200, "#frac{dE}{dx}"); - // Create Histograms. - // Event histograms - histos.add("Events/h1f_collisions_info", "# of Collisions", kTH1F, - {axisCols}); - histos.add("Events/h1f_collision_posZ", "V_{z}-distribution", kTH1F, - {axisVz}); + histos.add("Events/h1f_collisions_info", "# of Collisions", kTH1F, {axisCols}); + histos.add("Events/h1f_collision_posZ", "V_{z}-distribution", kTH1F, {axisVz}); + histos.add("Events/h1f_collision_cent", "Centrality of the selected collisions", kTH1F, {axisCent}); + auto hCutFlow = histos.add("Events/hEventCutFlow", "event cut flow;;collisions", kTH1D, {{kNEventCutFlow - 1, 0.5, static_cast(kNEventCutFlow) - 0.5}}); + hCutFlow->GetXaxis()->SetBinLabel(kEvAll, "all"); + hCutFlow->GetXaxis()->SetBinLabel(kEvTrigger, "trigger (sel8)"); + hCutFlow->GetXaxis()->SetBinLabel(kEvTvx, "TVX"); + hCutFlow->GetXaxis()->SetBinLabel(kEvTFBorder, "no TF border"); + hCutFlow->GetXaxis()->SetBinLabel(kEvItsRofBorder, "no ITS ROF border"); + hCutFlow->GetXaxis()->SetBinLabel(kEvItsTpcVtx, "ITS-TPC vertex"); + hCutFlow->GetXaxis()->SetBinLabel(kEvNoSameBunchPileup, "no same-bunch pileup"); + hCutFlow->GetXaxis()->SetBinLabel(kEvGoodZvtxFT0vsPV, "good z_{vtx} FT0 vs PV"); + hCutFlow->GetXaxis()->SetBinLabel(kEvGoodItsLayers, "good ITS layers"); + hCutFlow->GetXaxis()->SetBinLabel(kEvCentrality, "centrality range"); + hCutFlow->GetXaxis()->SetBinLabel(kEvVz, "V_{z} range"); + hCutFlow->GetXaxis()->SetBinLabel(kEvOneV0, "#geq 1 selected #Lambda/#bar{#Lambda}"); + hCutFlow->GetXaxis()->SetBinLabel(kEvTwoV0, "#geq 2 selected #Lambda/#bar{#Lambda}"); histos.add("Tracks/h1f_tracks_info", "# of tracks", kTH1F, {axisTrks}); - histos.add("Tracks/h2f_armpod_before_sel", "Armenteros-Podolanski (before)", - kTH2F, {axisAlpha, axisQtarm}); - histos.add("Tracks/h2f_armpod_after_sel", "Armenteros-Podolanski (after)", - kTH2F, {axisAlpha, axisQtarm}); - histos.add("Tracks/h1f_lambda_pt_vs_invm", "p_{T} vs M_{#Lambda}", kTH2F, - {axisV0Mass, axisV0Pt}); - histos.add("Tracks/h1f_antilambda_pt_vs_invm", "p_{T} vs M_{#bar{#Lambda}}", - kTH2F, {axisV0Mass, axisV0Pt}); - - histos.add("QA/Lambda/h2f_qt_vs_alpha", "Armenteros-Podolanski", kTH2F, - {axisAlpha, axisQtarm}); - histos.add("QA/Lambda/h1f_dca_V0_daughters", "DCA V0 daughters", kTH1F, - {axisDcaDau}); - histos.add("QA/Lambda/h1f_dca_pos_to_PV", "DCA pos-prong to PV", kTH1F, - {axisDcaProngPV}); - histos.add("QA/Lambda/h1f_dca_neg_to_PV", "DCA neg-prong to PV", kTH1F, - {axisDcaProngPV}); - histos.add("QA/Lambda/h1f_dca_V0_to_PV", "DCA V0 to PV", kTH1F, - {axisDcaV0PV}); - histos.add("QA/Lambda/h1f_V0_cospa", "cos(#theta_{PA})", kTH1F, - {axisCosPA}); - histos.add("QA/Lambda/h1f_V0_radius", "V0 decay radius", kTH1F, - {axisRadius}); + histos.add("Tracks/h2f_armpod_before_sel", "Armenteros-Podolanski (before)", kTH2F, {axisAlpha, axisQtarm}); + histos.add("Tracks/h2f_armpod_after_sel", "Armenteros-Podolanski (after)", kTH2F, {axisAlpha, axisQtarm}); + histos.add("Tracks/h1f_lambda_pt_vs_invm", "p_{T} vs M_{#Lambda}", kTH2F, {axisV0Mass, axisV0Pt}); + histos.add("Tracks/h1f_antilambda_pt_vs_invm", "p_{T} vs M_{#bar{#Lambda}}", kTH2F, {axisV0Mass, axisV0Pt}); + + histos.add("QA/Lambda/h2f_qt_vs_alpha", "Armenteros-Podolanski", kTH2F, {axisAlpha, axisQtarm}); + histos.add("QA/Lambda/h1f_dca_V0_daughters", "DCA V0 daughters", kTH1F, {axisDcaDau}); + histos.add("QA/Lambda/h1f_dca_pos_to_PV", "DCA pos-prong to PV", kTH1F, {axisDcaProngPV}); + histos.add("QA/Lambda/h1f_dca_neg_to_PV", "DCA neg-prong to PV", kTH1F, {axisDcaProngPV}); + histos.add("QA/Lambda/h1f_dca_V0_to_PV", "DCA V0 to PV", kTH1F, {axisDcaV0PV}); + histos.add("QA/Lambda/h1f_V0_cospa", "cos(#theta_{PA})", kTH1F, {axisCosPA}); + histos.add("QA/Lambda/h1f_V0_radius", "V0 decay radius", kTH1F, {axisRadius}); histos.add("QA/Lambda/h1f_V0_ctau", "c#tau", kTH1F, {axisCTau}); histos.add("QA/Lambda/h1f_V0_gctau", "#gammac#tau", kTH1F, {axisGCTau}); - histos.add("QA/Lambda/h1f_pos_prong_pt", "Pos-prong p_{T}", kTH1F, - {axisTrackPt}); - histos.add("QA/Lambda/h1f_neg_prong_pt", "Neg-prong p_{T}", kTH1F, - {axisTrackPt}); - histos.add("QA/Lambda/h1f_pos_prong_eta", "Pos-prong #eta", kTH1F, - {axisV0Eta}); - histos.add("QA/Lambda/h1f_neg_prong_eta", "Neg-prong #eta", kTH1F, - {axisV0Eta}); - histos.add("QA/Lambda/h1f_pos_prong_phi", "Pos-prong #phi", kTH1F, - {axisV0Phi}); - histos.add("QA/Lambda/h1f_neg_prong_phi", "Neg-prong #phi", kTH1F, - {axisV0Phi}); - histos.add("QA/Lambda/h2f_pos_prong_dcaXY_vs_pt", "DCA vs p_{T}", kTH2F, - {axisTrackPt, axisTrackDCA}); - histos.add("QA/Lambda/h2f_neg_prong_dcaXY_vs_pt", "DCA vs p_{T}", kTH2F, - {axisTrackPt, axisTrackDCA}); - histos.add("QA/Lambda/h2f_pos_prong_dEdx_vs_p", "TPC dE/dx pos", kTH2F, - {axisMomPID, axisdEdx}); - histos.add("QA/Lambda/h2f_neg_prong_dEdx_vs_p", "TPC dE/dx neg", kTH2F, - {axisMomPID, axisdEdx}); - histos.add("QA/Lambda/h2f_pos_prong_tpc_nsigma_pr_vs_p", - "TPC n#sigma_{p} pos", kTH2F, {axisMomPID, axisNsigma}); - histos.add("QA/Lambda/h2f_neg_prong_tpc_nsigma_pr_vs_p", - "TPC n#sigma_{p} neg", kTH2F, {axisMomPID, axisNsigma}); - histos.add("QA/Lambda/h2f_pos_prong_tpc_nsigma_pi_vs_p", - "TPC n#sigma_{#pi} pos", kTH2F, {axisMomPID, axisNsigma}); - histos.add("QA/Lambda/h2f_neg_prong_tpc_nsigma_pi_vs_p", - "TPC n#sigma_{#pi} neg", kTH2F, {axisMomPID, axisNsigma}); + histos.add("QA/Lambda/h1f_pos_prong_pt", "Pos-prong p_{T}", kTH1F, {axisTrackPt}); + histos.add("QA/Lambda/h1f_neg_prong_pt", "Neg-prong p_{T}", kTH1F, {axisTrackPt}); + histos.add("QA/Lambda/h1f_pos_prong_eta", "Pos-prong #eta", kTH1F, {axisV0Eta}); + histos.add("QA/Lambda/h1f_neg_prong_eta", "Neg-prong #eta", kTH1F, {axisV0Eta}); + histos.add("QA/Lambda/h1f_pos_prong_phi", "Pos-prong #phi", kTH1F, {axisV0Phi}); + histos.add("QA/Lambda/h1f_neg_prong_phi", "Neg-prong #phi", kTH1F, {axisV0Phi}); + histos.add("QA/Lambda/h2f_pos_prong_dcaXY_vs_pt", "DCA vs p_{T}", kTH2F, {axisTrackPt, axisTrackDCA}); + histos.add("QA/Lambda/h2f_neg_prong_dcaXY_vs_pt", "DCA vs p_{T}", kTH2F, {axisTrackPt, axisTrackDCA}); + histos.add("QA/Lambda/h2f_pos_prong_dEdx_vs_p", "TPC dE/dx pos", kTH2F, {axisMomPID, axisdEdx}); + histos.add("QA/Lambda/h2f_neg_prong_dEdx_vs_p", "TPC dE/dx neg", kTH2F, {axisMomPID, axisdEdx}); + histos.add("QA/Lambda/h2f_pos_prong_tpc_nsigma_pr_vs_p", "TPC n#sigma_{p} pos", kTH2F, {axisMomPID, axisNsigma}); + histos.add("QA/Lambda/h2f_neg_prong_tpc_nsigma_pr_vs_p", "TPC n#sigma_{p} neg", kTH2F, {axisMomPID, axisNsigma}); + histos.add("QA/Lambda/h2f_pos_prong_tpc_nsigma_pi_vs_p", "TPC n#sigma_{#pi} pos", kTH2F, {axisMomPID, axisNsigma}); + histos.add("QA/Lambda/h2f_neg_prong_tpc_nsigma_pi_vs_p", "TPC n#sigma_{#pi} neg", kTH2F, {axisMomPID, axisNsigma}); histos.add("McRec/Lambda/hPt", "p_{T}", kTH1F, {axisV0Pt}); histos.add("McRec/Lambda/hEta", "#eta", kTH1F, {axisV0Eta}); histos.add("McRec/Lambda/hRap", "y", kTH1F, {axisV0Rap}); histos.add("McRec/Lambda/hPhi", "#phi", kTH1F, {axisV0Phi}); - // QA Anti-Lambda histos.addClone("QA/Lambda/", "QA/AntiLambda/"); histos.addClone("McRec/Lambda/", "McRec/AntiLambda/"); if (doprocessMCRun3) { - histos.add("Tracks/h2f_tracks_pid_before_sel", "PIDs before sel", kTH2F, - {axisPID, axisV0Pt}); - histos.add("Tracks/h2f_tracks_pid_after_sel", "PIDs after sel", kTH2F, - {axisPID, axisV0Pt}); - histos.add("Tracks/h2f_lambda_mothers_pdg", "Lambda mothers", kTH2F, - {axisPID, axisV0Pt}); - - histos.add("McGen/h1f_collision_recgen", "RecGen collisions", kTH1F, - {axisMult}); - histos.add("McGen/h1f_collisions_info", "Collisions info", kTH1F, - {axisCols}); - histos.add("McGen/h2f_collision_posZ", "V_{z} rec vs gen", kTH2F, - {axisVz, axisVz}); - histos.add("McGen/h2f_collision_cent", "Centrality rec vs gen", kTH2F, - {axisCent, axisCent}); - histos.add("McGen/h1f_lambda_daughter_PDG", "Lambda dau PDG", kTH1F, - {axisPID}); - histos.add("McGen/h1f_antilambda_daughter_PDG", "AntiLambda dau PDG", - kTH1F, {axisPID}); + histos.add("Tracks/h2f_tracks_pid_before_sel", "PIDs before sel", kTH2F, {axisPID, axisV0Pt}); + histos.add("Tracks/h2f_tracks_pid_after_sel", "PIDs after sel", kTH2F, {axisPID, axisV0Pt}); + histos.add("Tracks/h2f_lambda_mothers_pdg", "Lambda mothers", kTH2F, {axisPID, axisV0Pt}); + + histos.add("McGen/h1f_collision_recgen", "RecGen collisions", kTH1F, {axisMult}); + histos.add("McGen/h1f_collisions_info", "Collisions info", kTH1F, {axisCols}); + histos.add("McGen/h2f_collision_posZ", "V_{z} rec vs gen", kTH2F, {axisVz, axisVz}); + histos.add("McGen/h2f_collision_cent", "Centrality rec vs gen", kTH2F, {axisCent, axisCent}); + histos.add("McGen/h1f_lambda_daughter_PDG", "Lambda dau PDG", kTH1F, {axisPID}); + histos.add("McGen/h1f_antilambda_daughter_PDG", "AntiLambda dau PDG", kTH1F, {axisPID}); histos.addClone("McRec/", "McGen/"); - histos.add("McGen/Lambda/Proton/hPt", "Proton p_{T}", kTH1F, - {axisTrackPt}); + histos.add("McGen/Lambda/Proton/hPt", "Proton p_{T}", kTH1F, {axisTrackPt}); histos.add("McGen/Lambda/Proton/hEta", "Proton #eta", kTH1F, {axisV0Eta}); histos.add("McGen/Lambda/Proton/hRap", "Proton y", kTH1F, {axisV0Rap}); histos.add("McGen/Lambda/Proton/hPhi", "Proton #phi", kTH1F, {axisV0Phi}); @@ -561,120 +523,45 @@ struct LambdaTableProducer { histos.addClone("McGen/Lambda/Proton/", "McGen/AntiLambda/Proton/"); histos.addClone("McGen/Lambda/Pion/", "McGen/AntiLambda/Pion/"); - // set bin lables specific to MC - histos.get(HIST("Events/h1f_collisions_info")) - ->GetXaxis() - ->SetBinLabel(CollisionLabels::kTotColBeforeHasMcCollision, - "kTotColBeforeHasMcCollision"); - histos.get(HIST("McGen/h1f_collisions_info")) - ->GetXaxis() - ->SetBinLabel(CollisionLabels::kTotCol, "kTotCol"); - histos.get(HIST("McGen/h1f_collisions_info")) - ->GetXaxis() - ->SetBinLabel(CollisionLabels::kPassSelCol, "kPassSelCol"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kTracksBeforeHasMcParticle, - "kTracksBeforeHasMcParticle"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kPrimaryLambda, "kPrimaryLambda"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kSecondaryLambda, "kSecondaryLambda"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kLambdaDauNotMcParticle, - "kLambdaDauNotMcParticle"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kLambdaNotPrPiMinus, - "kLambdaNotPrPiMinus"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kAntiLambdaNotAntiPrPiPlus, - "kAntiLambdaNotAntiPrPiPlus"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kPassTrueLambdaSel, "kPassTrueLambdaSel"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kGenTotAccLambda, "kGenTotAccLambda"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kGenLambdaNoDau, "kGenLambdaNoDau"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kGenLambdaToPrPi, "kGenLambdaToPrPi"); - } - - // set bin labels - histos.get(HIST("Events/h1f_collisions_info")) - ->GetXaxis() - ->SetBinLabel(CollisionLabels::kTotCol, "kTotCol"); - histos.get(HIST("Events/h1f_collisions_info")) - ->GetXaxis() - ->SetBinLabel(CollisionLabels::kPassSelCol, "kPassSelCol"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kAllV0Tracks, "kAllV0Tracks"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kV0KShortMassRej, "kV0KShortMassRej"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kNotLambdaNotAntiLambda, - "kNotLambdaNotAntiLambda"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kV0IsBothLambdaAntiLambda, - "kV0IsBothLambdaAntiLambda"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kNotLambdaAfterSel, "kNotLambdaAfterSel"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kV0IsLambdaOrAntiLambda, - "kV0IsLambdaOrAntiLambda"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kPassV0DauTrackSel, "kPassV0DauTrackSel"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kPassV0KinCuts, "kPassV0KinCuts"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kPassV0TopoSel, "kPassV0TopoSel"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kAllSelPassed, "kAllSelPassed"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kEffCorrPtCent, "kEffCorrPtCent"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kEffCorrPtRapCent, "kEffCorrPtRapCent"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kNoEffCorr, "kNoEffCorr"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kPFCorrPtCent, "kPFCorrPtCent"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kPFCorrPtRapCent, "kPFCorrPtRapCent"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kNoPFCorr, "kNoPFCorr"); + histos.get(HIST("Events/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kTotColBeforeHasMcCollision, "kTotColBeforeHasMcCollision"); + histos.get(HIST("McGen/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kTotCol, "kTotCol"); + histos.get(HIST("McGen/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kPassSelCol, "kPassSelCol"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kTracksBeforeHasMcParticle, "kTracksBeforeHasMcParticle"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kPrimaryLambda, "kPrimaryLambda"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kSecondaryLambda, "kSecondaryLambda"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kLambdaDauNotMcParticle, "kLambdaDauNotMcParticle"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kLambdaNotPrPiMinus, "kLambdaNotPrPiMinus"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kAntiLambdaNotAntiPrPiPlus, "kAntiLambdaNotAntiPrPiPlus"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kPassTrueLambdaSel, "kPassTrueLambdaSel"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kGenTotAccLambda, "kGenTotAccLambda"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kGenLambdaNoDau, "kGenLambdaNoDau"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kGenLambdaToPrPi, "kGenLambdaToPrPi"); + } + + histos.get(HIST("Events/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kTotCol, "kTotCol"); + histos.get(HIST("Events/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kPassSelCol, "kPassSelCol"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kAllV0Tracks, "kAllV0Tracks"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kV0KShortMassRej, "kV0KShortMassRej"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kNotLambdaNotAntiLambda, "kNotLambdaNotAntiLambda"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kV0IsBothLambdaAntiLambda, "kV0IsBothLambdaAntiLambda"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kNotLambdaAfterSel, "kNotLambdaAfterSel"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kV0IsLambdaOrAntiLambda, "kV0IsLambdaOrAntiLambda"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kPassV0DauTrackSel, "kPassV0DauTrackSel"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kPassV0KinCuts, "kPassV0KinCuts"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kPassV0TopoSel, "kPassV0TopoSel"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kAllSelPassed, "kAllSelPassed"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kEffCorrPtCent, "kEffCorrPtCent"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kEffCorrPtRapCent, "kEffCorrPtRapCent"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kNoEffCorr, "kNoEffCorr"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kPFCorrPtCent, "kPFCorrPtCent"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kPFCorrPtRapCent, "kPFCorrPtRapCent"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kNoPFCorr, "kNoPFCorr"); } template bool selCollision(C const& col) { - if (col.posZ() <= cMinZVtx || col.posZ() >= cMaxZVtx) { - return false; - } - + histos.fill(HIST("Events/hEventCutFlow"), kEvAll); if constexpr (run == kRun3) { if (cCentEstimator == kCentFT0M) { cent = col.centFT0M(); @@ -693,50 +580,59 @@ struct LambdaTableProducer { return false; } } + histos.fill(HIST("Events/hEventCutFlow"), kEvTrigger); - if (cent <= cMinMult || cent >= cMaxMult) { - return false; - } if (cTriggerTvxSel && !col.selection_bit(aod::evsel::kIsTriggerTVX)) { return false; } + histos.fill(HIST("Events/hEventCutFlow"), kEvTvx); if (cTFBorder && !col.selection_bit(aod::evsel::kNoTimeFrameBorder)) { return false; } + histos.fill(HIST("Events/hEventCutFlow"), kEvTFBorder); if (cNoItsROBorder && !col.selection_bit(aod::evsel::kNoITSROFrameBorder)) { return false; } + histos.fill(HIST("Events/hEventCutFlow"), kEvItsRofBorder); if (cItsTpcVtx && !col.selection_bit(aod::evsel::kIsVertexITSTPC)) { return false; } + histos.fill(HIST("Events/hEventCutFlow"), kEvItsTpcVtx); if (cPileupReject && !col.selection_bit(aod::evsel::kNoSameBunchPileup)) { return false; } + histos.fill(HIST("Events/hEventCutFlow"), kEvNoSameBunchPileup); if (cZVtxTimeDiff && !col.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { return false; } - if (cIsGoodITSLayers && - !col.selection_bit(aod::evsel::kIsGoodITSLayersAll)) { + histos.fill(HIST("Events/hEventCutFlow"), kEvGoodZvtxFT0vsPV); + if (cIsGoodITSLayers && !col.selection_bit(aod::evsel::kIsGoodITSLayersAll)) { return false; } + histos.fill(HIST("Events/hEventCutFlow"), kEvGoodItsLayers); + + if (cent <= cMinMult || cent >= cMaxMult) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kEvCentrality); + if (col.posZ() <= cMinZVtx || col.posZ() >= cMaxZVtx) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kEvVz); mult = col.multNTracksPV(); return true; } - // Kinematic Selection - bool kinCutSelection(float const& pt, float const& rap, float const& ptMin, - float const& ptMax, float const& rapMax) + bool kinCutSelection(float const& pt, float const& rap, float const& ptMin, float const& ptMax, float const& rapMax) { return pt > ptMin && pt < ptMax && rap < rapMax; } - // Track Selection template bool selTrack(T const& track) { - if (!kinCutSelection(track.pt(), std::abs(track.eta()), cTrackMinPt, - cTrackMaxPt, cTrackEtaCut)) { + if (!kinCutSelection(track.pt(), std::abs(track.eta()), cTrackMinPt, cTrackMaxPt, cTrackEtaCut)) { return false; } if (track.tpcNClsCrossedRows() <= cMinTpcCrossedRows) { @@ -754,7 +650,6 @@ struct LambdaTableProducer { return true; } - // Daughter Track Selection template bool selDaughterTracks(V const& v0, T const&, ParticleType const& v0Type) { @@ -764,8 +659,6 @@ struct LambdaTableProducer { return false; } - // Apply DCA Selection on Daughter Tracks Based on Lambda/AntiLambda - // daughters float dcaProton = 0., dcaPion = 0.; if (v0Type == kLambda) { dcaProton = std::abs(v0.dcapostopv()); @@ -783,20 +676,17 @@ struct LambdaTableProducer { template bool topoCutSelection(C const& col, V const& v0, T const&) { - if (v0.dcaV0daughters() <= cMinV0DcaDaughters || - v0.dcaV0daughters() >= cMaxV0DcaDaughters) { + if (v0.dcaV0daughters() <= cMinV0DcaDaughters || v0.dcaV0daughters() >= cMaxV0DcaDaughters) { return false; } if (v0.dcav0topv() <= cMinDcaV0ToPV || v0.dcav0topv() >= cMaxDcaV0ToPV) { return false; } - if (v0.v0radius() <= cMinV0TransRadius || - v0.v0radius() >= cMaxV0TransRadius) { + if (v0.v0radius() <= cMinV0TransRadius || v0.v0radius() >= cMaxV0TransRadius) { return false; } - float ctau = - v0.distovertotmom(col.posX(), col.posY(), col.posZ()) * MassLambda0; + float ctau = v0.distovertotmom(col.posX(), col.posY(), col.posZ()) * MassLambda0; if (ctau <= cMinV0CTau || ctau >= cMaxV0CTau) { return false; } @@ -817,34 +707,27 @@ struct LambdaTableProducer { tpcNSigmaPr = negtrack.tpcNSigmaPr(); tpcNSigmaPi = postrack.tpcNSigmaPi(); } - return (std::abs(tpcNSigmaPr) < cTpcNsigmaCut && - std::abs(tpcNSigmaPi) < cTpcNsigmaCut); + return (std::abs(tpcNSigmaPr) < cTpcNsigmaCut && std::abs(tpcNSigmaPi) < cTpcNsigmaCut); } template bool selLambdaMassWindow(V const& v0, T const&, ParticleType& v0type) { - // Kshort mass rejection hypothesis - if (cKshortRejFlag && - (std::abs(v0.mK0Short() - MassK0Short) <= cKshortRejMassWindow)) { + if (cKshortRejFlag && (std::abs(v0.mK0Short() - MassK0Short) <= cKshortRejMassWindow)) { histos.fill(HIST("Tracks/h1f_tracks_info"), kV0KShortMassRej); return false; } - // initialize daughter tracks auto postrack = v0.template posTrack_as(); auto negtrack = v0.template negTrack_as(); - // initialize selection flags bool lambdaFlag = false, antiLambdaFlag = false; - if ((v0.mLambda() > cMinV0Mass && v0.mLambda() < cMaxV0Mass) && - selLambdaDauWithTpcPid(postrack, negtrack)) { + if ((v0.mLambda() > cMinV0Mass && v0.mLambda() < cMaxV0Mass) && selLambdaDauWithTpcPid(postrack, negtrack)) { lambdaFlag = true; v0type = kLambda; } - if ((v0.mAntiLambda() > cMinV0Mass && v0.mAntiLambda() < cMaxV0Mass) && - selLambdaDauWithTpcPid(postrack, negtrack)) { + if ((v0.mAntiLambda() > cMinV0Mass && v0.mAntiLambda() < cMaxV0Mass) && selLambdaDauWithTpcPid(postrack, negtrack)) { antiLambdaFlag = true; v0type = kAntiLambda; } @@ -861,8 +744,7 @@ struct LambdaTableProducer { } template - bool selV0Particle(C const& col, V const& v0, T const& tracks, - ParticleType& v0Type) + bool selV0Particle(C const& col, V const& v0, T const& tracks, ParticleType& v0Type) { if (!selLambdaMassWindow(v0, tracks, v0Type)) { return false; @@ -885,7 +767,6 @@ struct LambdaTableProducer { } histos.fill(HIST("Tracks/h1f_tracks_info"), kPassV0TopoSel); - // All Selection Criterion Passed return true; } @@ -960,7 +841,6 @@ struct LambdaTableProducer { return 1.; } - // Get from CCDB auto ccdbObj = ccdb->getForTimeStamp(cPathCCDB.value, 1); if (!ccdbObj) { LOGF(warning, "CCDB OBJECT NOT FOUND"); @@ -970,7 +850,6 @@ struct LambdaTableProducer { float effCorrFact = 1., primFrac = 1.; float rap = (cDoEtaAnalysis) ? v0.eta() : v0.yLambda(); - // Get Efficiency Factor if (cGetEffFact) { auto* objEff = ccdbObj->FindObject( Form("%s", vCorrFactStrings[cCorrFactHist][part].c_str())); @@ -1021,26 +900,20 @@ struct LambdaTableProducer { template void fillLambdaQAHistos(C const& col, V const& v0, T const&) { - static constexpr std::array SubDir = { - "QA/Lambda/", "QA/AntiLambda/"}; + static constexpr std::array SubDir = {"QA/Lambda/", "QA/AntiLambda/"}; auto postrack = v0.template posTrack_as(); auto negtrack = v0.template negTrack_as(); float mass = (part == kLambda) ? v0.mLambda() : v0.mAntiLambda(); float e = RecoDecay::e(v0.px(), v0.py(), v0.pz(), mass); float gamma = e / mass; - float ctau = - v0.distovertotmom(col.posX(), col.posY(), col.posZ()) * MassLambda0; + float ctau = v0.distovertotmom(col.posX(), col.posY(), col.posZ()) * MassLambda0; float gctau = ctau * gamma; - histos.fill(HIST(SubDir[part]) + HIST("h2f_qt_vs_alpha"), v0.alpha(), - v0.qtarm()); - histos.fill(HIST(SubDir[part]) + HIST("h1f_dca_V0_daughters"), - v0.dcaV0daughters()); - histos.fill(HIST(SubDir[part]) + HIST("h1f_dca_pos_to_PV"), - v0.dcapostopv()); - histos.fill(HIST(SubDir[part]) + HIST("h1f_dca_neg_to_PV"), - v0.dcanegtopv()); + histos.fill(HIST(SubDir[part]) + HIST("h2f_qt_vs_alpha"), v0.alpha(), v0.qtarm()); + histos.fill(HIST(SubDir[part]) + HIST("h1f_dca_V0_daughters"), v0.dcaV0daughters()); + histos.fill(HIST(SubDir[part]) + HIST("h1f_dca_pos_to_PV"), v0.dcapostopv()); + histos.fill(HIST(SubDir[part]) + HIST("h1f_dca_neg_to_PV"), v0.dcanegtopv()); histos.fill(HIST(SubDir[part]) + HIST("h1f_dca_V0_to_PV"), v0.dcav0topv()); histos.fill(HIST(SubDir[part]) + HIST("h1f_V0_cospa"), v0.v0cosPA()); histos.fill(HIST(SubDir[part]) + HIST("h1f_V0_radius"), v0.v0radius()); @@ -1052,45 +925,31 @@ struct LambdaTableProducer { histos.fill(HIST(SubDir[part]) + HIST("h1f_neg_prong_eta"), negtrack.eta()); histos.fill(HIST(SubDir[part]) + HIST("h1f_pos_prong_phi"), postrack.phi()); histos.fill(HIST(SubDir[part]) + HIST("h1f_neg_prong_phi"), negtrack.phi()); - histos.fill(HIST(SubDir[part]) + HIST("h2f_pos_prong_dcaXY_vs_pt"), - postrack.pt(), postrack.dcaXY()); - histos.fill(HIST(SubDir[part]) + HIST("h2f_neg_prong_dcaXY_vs_pt"), - negtrack.pt(), negtrack.dcaXY()); - histos.fill(HIST(SubDir[part]) + HIST("h2f_pos_prong_dEdx_vs_p"), - postrack.tpcInnerParam(), postrack.tpcSignal()); - histos.fill(HIST(SubDir[part]) + HIST("h2f_neg_prong_dEdx_vs_p"), - negtrack.tpcInnerParam(), negtrack.tpcSignal()); - histos.fill(HIST(SubDir[part]) + HIST("h2f_pos_prong_tpc_nsigma_pr_vs_p"), - postrack.tpcInnerParam(), postrack.tpcNSigmaPr()); - histos.fill(HIST(SubDir[part]) + HIST("h2f_neg_prong_tpc_nsigma_pr_vs_p"), - negtrack.tpcInnerParam(), negtrack.tpcNSigmaPr()); - histos.fill(HIST(SubDir[part]) + HIST("h2f_pos_prong_tpc_nsigma_pi_vs_p"), - postrack.tpcInnerParam(), postrack.tpcNSigmaPi()); - histos.fill(HIST(SubDir[part]) + HIST("h2f_neg_prong_tpc_nsigma_pi_vs_p"), - negtrack.tpcInnerParam(), negtrack.tpcNSigmaPi()); + histos.fill(HIST(SubDir[part]) + HIST("h2f_pos_prong_dcaXY_vs_pt"), postrack.pt(), postrack.dcaXY()); + histos.fill(HIST(SubDir[part]) + HIST("h2f_neg_prong_dcaXY_vs_pt"), negtrack.pt(), negtrack.dcaXY()); + histos.fill(HIST(SubDir[part]) + HIST("h2f_pos_prong_dEdx_vs_p"), postrack.tpcInnerParam(), postrack.tpcSignal()); + histos.fill(HIST(SubDir[part]) + HIST("h2f_neg_prong_dEdx_vs_p"), negtrack.tpcInnerParam(), negtrack.tpcSignal()); + histos.fill(HIST(SubDir[part]) + HIST("h2f_pos_prong_tpc_nsigma_pr_vs_p"), postrack.tpcInnerParam(), postrack.tpcNSigmaPr()); + histos.fill(HIST(SubDir[part]) + HIST("h2f_neg_prong_tpc_nsigma_pr_vs_p"), negtrack.tpcInnerParam(), negtrack.tpcNSigmaPr()); + histos.fill(HIST(SubDir[part]) + HIST("h2f_pos_prong_tpc_nsigma_pi_vs_p"), postrack.tpcInnerParam(), postrack.tpcNSigmaPi()); + histos.fill(HIST(SubDir[part]) + HIST("h2f_neg_prong_tpc_nsigma_pi_vs_p"), negtrack.tpcInnerParam(), negtrack.tpcNSigmaPi()); } template - void fillKinematicHists(float const& pt, float const& eta, float const& y, - float const& phi) + void fillKinematicHists(float const& pt, float const& eta, float const& y, float const& phi) { - static constexpr std::array SubDirRG = {"McRec/", - "McGen/"}; - static constexpr std::array SubDirPart = { - "Lambda/", "AntiLambda/"}; + static constexpr std::array SubDirRG = {"McRec/", "McGen/"}; + static constexpr std::array SubDirPart = {"Lambda/", "AntiLambda/"}; histos.fill(HIST(SubDirRG[rg]) + HIST(SubDirPart[part]) + HIST("hPt"), pt); - histos.fill(HIST(SubDirRG[rg]) + HIST(SubDirPart[part]) + HIST("hEta"), - eta); + histos.fill(HIST(SubDirRG[rg]) + HIST(SubDirPart[part]) + HIST("hEta"), eta); histos.fill(HIST(SubDirRG[rg]) + HIST(SubDirPart[part]) + HIST("hRap"), y); - histos.fill(HIST(SubDirRG[rg]) + HIST(SubDirPart[part]) + HIST("hPhi"), - phi); + histos.fill(HIST(SubDirRG[rg]) + HIST(SubDirPart[part]) + HIST("hPhi"), phi); } template - void fillLambdaRecoTables(C const& collision, B const& bc, V const& v0tracks, - T const& tracks) + void fillLambdaRecoTables(C const& collision, B const& bc, V const& v0tracks, T const& tracks) { histos.fill(HIST("Events/h1f_collisions_info"), kTotCol); @@ -1102,17 +961,16 @@ struct LambdaTableProducer { histos.fill(HIST("Events/h1f_collisions_info"), kPassSelCol); histos.fill(HIST("Events/h1f_collision_posZ"), collision.posZ()); + histos.fill(HIST("Events/h1f_collision_cent"), cent); - // Fill Collision Table - lambdaCollisionTable(cent, mult, collision.posX(), collision.posY(), - collision.posZ(), bc.timestamp()); + lambdaCollisionTable(cent, mult, collision.posX(), collision.posY(), collision.posZ(), bc.timestamp()); - // initialize v0track objects ParticleType v0Type = kLambda; PrmScdType v0PrmScdType = kPrimary; float mass = 0., corr_fact = 1.; float prPx = 0., prPy = 0., prPz = 0.; float pt = 0., eta = 0., rap = 0., phi = 0.; + std::size_t nWritten = 0; for (auto const& v0 : v0tracks) { if constexpr (dmc == kMC) { @@ -1147,8 +1005,7 @@ struct LambdaTableProducer { phi = v0.phi(); if constexpr (dmc == kMC) { - histos.fill(HIST("Tracks/h2f_tracks_pid_before_sel"), - v0.mcParticle().pdgCode(), v0.pt()); + histos.fill(HIST("Tracks/h2f_tracks_pid_before_sel"), v0.mcParticle().pdgCode(), v0.pt()); if (cSelMCPSV0) { v0PrmScdType = isPrimaryV0(v0); } @@ -1159,8 +1016,7 @@ struct LambdaTableProducer { fillLambdaMothers(v0, tracks); } histos.fill(HIST("Tracks/h1f_tracks_info"), kPassTrueLambdaSel); - histos.fill(HIST("Tracks/h2f_tracks_pid_after_sel"), - v0.mcParticle().pdgCode(), v0.pt()); + histos.fill(HIST("Tracks/h2f_tracks_pid_after_sel"), v0.mcParticle().pdgCode(), v0.pt()); if (cRecoMomResoFlag) { auto mc = v0.template mcParticle_as(); pt = mc.pt(); @@ -1168,16 +1024,14 @@ struct LambdaTableProducer { rap = mc.y(); phi = mc.phi(); float y = cDoEtaAnalysis ? eta : rap; - if (!kinCutSelection(pt, std::abs(y), cMinV0Pt, cMaxV0Pt, - cMaxV0Rap)) { + if (!kinCutSelection(pt, std::abs(y), cMinV0Pt, cMaxV0Pt, cMaxV0Rap)) { continue; } } } histos.fill(HIST("Tracks/h2f_armpod_after_sel"), v0.alpha(), v0.qtarm()); - corr_fact = (v0Type == kLambda) ? getCorrectionFactors(v0) - : getCorrectionFactors(v0); + corr_fact = (v0Type == kLambda) ? getCorrectionFactors(v0) : getCorrectionFactors(v0); if (v0Type == kLambda) { prPx = v0.pxpos(); @@ -1185,33 +1039,39 @@ struct LambdaTableProducer { prPz = v0.pzpos(); histos.fill(HIST("Tracks/h1f_lambda_pt_vs_invm"), mass, v0.pt()); fillLambdaQAHistos(collision, v0, tracks); - fillKinematicHists(v0.pt(), v0.eta(), v0.yLambda(), - v0.phi()); + fillKinematicHists(v0.pt(), v0.eta(), v0.yLambda(), v0.phi()); } else { prPx = v0.pxneg(); prPy = v0.pyneg(); prPz = v0.pzneg(); histos.fill(HIST("Tracks/h1f_antilambda_pt_vs_invm"), mass, v0.pt()); fillLambdaQAHistos(collision, v0, tracks); - fillKinematicHists(v0.pt(), v0.eta(), v0.yLambda(), - v0.phi()); + fillKinematicHists(v0.pt(), v0.eta(), v0.yLambda(), v0.phi()); } lambdaTrackTable(lambdaCollisionTable.lastIndex(), v0.px(), v0.py(), v0.pz(), pt, eta, phi, rap, mass, prPx, prPy, prPz, v0.template posTrack_as().index(), - v0.template negTrack_as().index(), v0.v0cosPA(), - v0.dcaV0daughters(), (int8_t)v0Type, v0PrmScdType, + v0.template negTrack_as().index(), + v0.v0cosPA(), + v0.dcaV0daughters(), (int8_t)v0Type, + v0PrmScdType, corr_fact); + ++nWritten; + } + + if (nWritten >= 1) { + histos.fill(HIST("Events/hEventCutFlow"), kEvOneV0); + } + if (nWritten >= NCandidatesForPair) { + histos.fill(HIST("Events/hEventCutFlow"), kEvTwoV0); } } template - void fillLambdaMcGenTables(B const& bc, C const& mcCollision, - M const& mcParticles) + void fillLambdaMcGenTables(B const& bc, C const& mcCollision, M const& mcParticles) { - lambdaMCGenCollisionTable(cent, mult, mcCollision.posX(), - mcCollision.posY(), mcCollision.posZ(), + lambdaMCGenCollisionTable(cent, mult, mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), bc.timestamp()); ParticleType v0Type = kLambda; @@ -1293,15 +1153,11 @@ struct LambdaTableProducer { histos.fill(HIST("McGen/Lambda/Pion/hEta"), vDauEta[piIdx]); histos.fill(HIST("McGen/Lambda/Pion/hRap"), vDauRap[piIdx]); histos.fill(HIST("McGen/Lambda/Pion/hPhi"), vDauPhi[piIdx]); - fillKinematicHists(mcpart.pt(), mcpart.eta(), mcpart.y(), - mcpart.phi()); + fillKinematicHists(mcpart.pt(), mcpart.eta(), mcpart.y(), mcpart.phi()); } else { - histos.fill(HIST("McGen/h1f_antilambda_daughter_PDG"), - daughterPDGs[prIdx]); - histos.fill(HIST("McGen/h1f_antilambda_daughter_PDG"), - daughterPDGs[piIdx]); - histos.fill(HIST("McGen/h1f_antilambda_daughter_PDG"), - mcpart.pdgCode()); + histos.fill(HIST("McGen/h1f_antilambda_daughter_PDG"), daughterPDGs[prIdx]); + histos.fill(HIST("McGen/h1f_antilambda_daughter_PDG"), daughterPDGs[piIdx]); + histos.fill(HIST("McGen/h1f_antilambda_daughter_PDG"), mcpart.pdgCode()); histos.fill(HIST("McGen/AntiLambda/Proton/hPt"), vDauPt[prIdx]); histos.fill(HIST("McGen/AntiLambda/Proton/hEta"), vDauEta[prIdx]); histos.fill(HIST("McGen/AntiLambda/Proton/hRap"), vDauRap[prIdx]); @@ -1310,8 +1166,7 @@ struct LambdaTableProducer { histos.fill(HIST("McGen/AntiLambda/Pion/hEta"), vDauEta[piIdx]); histos.fill(HIST("McGen/AntiLambda/Pion/hRap"), vDauRap[piIdx]); histos.fill(HIST("McGen/AntiLambda/Pion/hPhi"), vDauPhi[piIdx]); - fillKinematicHists(mcpart.pt(), mcpart.eta(), - mcpart.y(), mcpart.phi()); + fillKinematicHists(mcpart.pt(), mcpart.eta(), mcpart.y(), mcpart.phi()); } lambdaMCGenTrackTable( @@ -1323,10 +1178,8 @@ struct LambdaTableProducer { } } - template - void analyzeMcRecoGen(M const& mcCollision, C const& collisions, B const&, - V const& V0s, T const& tracks, P const& mcParticles) + template + void analyzeMcRecoGen(M const& mcCollision, C const& collisions, B const&, V const& V0s, T const& tracks, P const& mcParticles) { int nRecCols = collisions.size(); if (nRecCols != 0) { @@ -1356,32 +1209,25 @@ struct LambdaTableProducer { } SliceCache cache; - Preslice> perCollision = - aod::v0data::collisionId; + Preslice> perCollision = aod::v0data::collisionId; - using CollisionsRun3 = soa::Join; - using CollisionsRun2 = - soa::Join; + using CollisionsRun3 = soa::Join; + using CollisionsRun2 = soa::Join; using Tracks = soa::Join; - using TracksRun2 = - soa::Join; + using TracksRun2 = soa::Join; using TracksMC = soa::Join; using TracksMCRun2 = soa::Join; using McV0Tracks = soa::Join; - void processDataRun3(CollisionsRun3::iterator const& collision, - aod::BCsWithTimestamps const&, aod::V0Datas const& V0s, - Tracks const& tracks) + void processDataRun3(CollisionsRun3::iterator const& collision, aod::BCsWithTimestamps const&, aod::V0Datas const& V0s, Tracks const& tracks) { auto bc = collision.bc_as(); fillLambdaRecoTables(collision, bc, V0s, tracks); } - PROCESS_SWITCH(LambdaTableProducer, processDataRun3, "Process for Run3 DATA", - true); + PROCESS_SWITCH(LambdaTableProducer, processDataRun3, "Process for Run3 DATA", true); void processMCRun3( aod::McCollisions::iterator const& mcCollision, @@ -1389,38 +1235,127 @@ struct LambdaTableProducer { aod::BCsWithTimestamps const& bcts, McV0Tracks const& V0s, TracksMC const& tracks, aod::McParticles const& mcParticles) { - analyzeMcRecoGen(mcCollision, collisions, bcts, V0s, tracks, - mcParticles); + analyzeMcRecoGen(mcCollision, collisions, bcts, V0s, tracks, mcParticles); } - PROCESS_SWITCH(LambdaTableProducer, processMCRun3, - "Process for Run3 MC RecoGen", false); + PROCESS_SWITCH(LambdaTableProducer, processMCRun3, "Process for Run3 MC RecoGen", false); + + template + bool selLooseCandidate(T const& cand) + { + if (cKshortRejFlag && (std::abs(cand.mK0Short() - MassK0Short) <= cKshortRejMassWindow)) { + histos.fill(HIST("Tracks/h1f_tracks_info"), kV0KShortMassRej); + return false; + } + + auto passMassAndPid = [&](float mass, float tpcNSigmaPr, float tpcNSigmaPi) { + return (mass > cMinV0Mass && mass < cMaxV0Mass) && (std::abs(tpcNSigmaPr) < cTpcNsigmaCut && std::abs(tpcNSigmaPi) < cTpcNsigmaCut); + }; + const bool isLambda = cand.v0Type() == kLambda; + const bool ownFlag = isLambda ? passMassAndPid(cand.mass(), cand.posNSigmaPr(), cand.negNSigmaPi()) : passMassAndPid(cand.mass(), cand.negNSigmaPr(), cand.posNSigmaPi()); + const bool otherFlag = isLambda ? passMassAndPid(cand.massOther(), cand.negNSigmaPr(), cand.posNSigmaPi()) : passMassAndPid(cand.massOther(), cand.posNSigmaPr(), cand.negNSigmaPi()); + if (!ownFlag) { + histos.fill(HIST("Tracks/h1f_tracks_info"), kNotLambdaNotAntiLambda); + return false; + } + if (otherFlag) { + histos.fill(HIST("Tracks/h1f_tracks_info"), kV0IsBothLambdaAntiLambda); + return false; + } + histos.fill(HIST("Tracks/h1f_tracks_info"), kV0IsLambdaOrAntiLambda); + + if (cand.posTpcCrossedRows() <= cMinTpcCrossedRows || cand.negTpcCrossedRows() <= cMinTpcCrossedRows) { + return false; + } + histos.fill(HIST("Tracks/h1f_tracks_info"), kPassV0DauTrackSel); + + float rap = cDoEtaAnalysis ? std::abs(cand.eta()) : std::abs(cand.rap()); + if (!kinCutSelection(cand.pt(), rap, cMinV0Pt, cMaxV0Pt, cMaxV0Rap)) { + return false; + } + histos.fill(HIST("Tracks/h1f_tracks_info"), kPassV0KinCuts); + + if (cand.dcaDau() <= cMinV0DcaDaughters || cand.dcaDau() >= cMaxV0DcaDaughters) { + return false; + } + if (cand.dcaV0ToPV() <= cMinDcaV0ToPV || cand.dcaV0ToPV() >= cMaxDcaV0ToPV) { + return false; + } + if (cand.cTau() <= cMinV0CTau || cand.cTau() >= cMaxV0CTau) { + return false; + } + if (cand.cosPA() <= cMinV0CosPA) { + return false; + } + histos.fill(HIST("Tracks/h1f_tracks_info"), kPassV0TopoSel); + return true; + } + + void processDerivedData(aod::LambdaLooseCols::iterator const& collision, aod::LambdaLooseTracks const& looseTracks) + { + histos.fill(HIST("Events/h1f_collisions_info"), kTotCol); + cent = collision.cent(); + mult = collision.mult(); + if (cent <= cMinMult || cent >= cMaxMult) { + return; + } + histos.fill(HIST("Events/hEventCutFlow"), kEvCentrality); + if (collision.posZ() <= cMinZVtx || collision.posZ() >= cMaxZVtx) { + return; + } + histos.fill(HIST("Events/hEventCutFlow"), kEvVz); + + histos.fill(HIST("Events/h1f_collisions_info"), kPassSelCol); + histos.fill(HIST("Events/h1f_collision_posZ"), collision.posZ()); + histos.fill(HIST("Events/h1f_collision_cent"), cent); + + lambdaCollisionTable(cent, mult, collision.posX(), collision.posY(), collision.posZ(), collision.timeStamp()); + + std::size_t nWritten = 0; + for (auto const& cand : looseTracks) { + histos.fill(HIST("Tracks/h1f_tracks_info"), kAllV0Tracks); + if (!selLooseCandidate(cand)) { + continue; + } + histos.fill(HIST("Tracks/h1f_tracks_info"), kAllSelPassed); + + if (cand.v0Type() == kLambda) { + histos.fill(HIST("Tracks/h1f_lambda_pt_vs_invm"), cand.mass(), cand.pt()); + fillKinematicHists(cand.pt(), cand.eta(), cand.rap(), cand.phi()); + } else { + histos.fill(HIST("Tracks/h1f_antilambda_pt_vs_invm"), cand.mass(), cand.pt()); + fillKinematicHists(cand.pt(), cand.eta(), cand.rap(), cand.phi()); + } + + lambdaTrackTable(lambdaCollisionTable.lastIndex(), cand.px(), cand.py(), + cand.pz(), cand.pt(), cand.eta(), cand.phi(), cand.rap(), cand.mass(), + cand.prPx(), cand.prPy(), cand.prPz(), + static_cast(cand.posTrackId()), + static_cast(cand.negTrackId()), + cand.cosPA(), cand.dcaDau(), cand.v0Type(), + static_cast(kPrimary), 1.f); + ++nWritten; + } + + if (nWritten >= 1) { + histos.fill(HIST("Events/hEventCutFlow"), kEvOneV0); + } + if (nWritten >= NCandidatesForPair) { + histos.fill(HIST("Events/hEventCutFlow"), kEvTwoV0); + } + } + PROCESS_SWITCH(LambdaTableProducer, processDerivedData, "Process the derived data of lambda-spin-polarization-loose-producer", false); }; struct LambdaTracksExtProducer { Produces lambdaTrackExtTable; - Configurable cAcceptAllLambda{"cAcceptAllLambda", false, - "Accept all lambda (ignore sharing)"}; - Configurable cRejAllLambdaShaDau{"cRejAllLambdaShaDau", true, - "Reject lambda sharing daughters"}; - Configurable cSelLambdaMassPdg{"cSelLambdaMassPdg", false, - "Select lambda closest to PDG mass"}; - Configurable cSelLambdaTScore{"cSelLambdaTScore", false, - "Select lambda by t-score"}; - Configurable cA{"cA", 0.6, "t-score weight: |mass - PDGmass|"}; - Configurable cB{"cB", 0.6, "t-score weight: DCA daughters"}; - Configurable cC{"cC", 0.6, "t-score weight: |cosPA - 1|"}; - - HistogramRegistry histos{ - "histos", - {}, - OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; void init(InitContext const&) { const AxisSpec axisMult(10, 0, 10); - const AxisSpec axisMass(120, 1.08, 1.20, "M_{p#pi} (GeV/#it{c}^{2})"); + const AxisSpec axisMass(200, 1.08, 1.18, "M_{p#pi} (GeV/#it{c}^{2})"); const AxisSpec axisCPA(100, 0.995, 1.0, "cos(#theta_{PA})"); const AxisSpec axisDcaDau(75, 0., 1.5, "Daughter DCA (#sigma)"); const AxisSpec axisDEta(320, -1.6, 1.6, "#Delta#eta"); @@ -1428,58 +1363,41 @@ struct LambdaTracksExtProducer { histos.add("h1i_totlambda_mult", "Multiplicity", kTH1I, {axisMult}); histos.add("h1i_totantilambda_mult", "Multiplicity", kTH1I, {axisMult}); - histos.add("h1i_lambda_mult", "Multiplicity", kTH1I, {axisMult}); - histos.add("h1i_antilambda_mult", "Multiplicity", kTH1I, {axisMult}); - histos.add("h2d_n2_etaphi_LaP_LaM", "#rho_{2}^{Share} #Lambda#bar{#Lambda}", - kTH2D, {axisDEta, axisDPhi}); - histos.add("h2d_n2_etaphi_LaM_LaP", "#rho_{2}^{Share} #bar{#Lambda}#Lambda", - kTH2D, {axisDEta, axisDPhi}); - histos.add("h2d_n2_etaphi_LaP_LaP", "#rho_{2}^{Share} #Lambda#Lambda", - kTH2D, {axisDEta, axisDPhi}); - histos.add("h2d_n2_etaphi_LaM_LaM", - "#rho_{2}^{Share} #bar{#Lambda}#bar{#Lambda}", kTH2D, - {axisDEta, axisDPhi}); + histos.add("h2d_n2_etaphi_LaP_LaM", "#rho_{2}^{Share} #Lambda#bar{#Lambda}", kTH2D, {axisDEta, axisDPhi}); + histos.add("h2d_n2_etaphi_LaM_LaP", "#rho_{2}^{Share} #bar{#Lambda}#Lambda", kTH2D, {axisDEta, axisDPhi}); + histos.add("h2d_n2_etaphi_LaP_LaP", "#rho_{2}^{Share} #Lambda#Lambda", kTH2D, {axisDEta, axisDPhi}); + histos.add("h2d_n2_etaphi_LaM_LaM", "#rho_{2}^{Share} #bar{#Lambda}#bar{#Lambda}", kTH2D, {axisDEta, axisDPhi}); histos.add("Reco/h1f_lambda_invmass", "M_{#Lambda}", kTH1F, {axisMass}); histos.add("Reco/h1f_lambda_cospa", "cos(PA)", kTH1F, {axisCPA}); histos.add("Reco/h1f_lambda_dcadau", "DCA daughters", kTH1F, {axisDcaDau}); - histos.add("Reco/h1f_antilambda_invmass", "M_{#bar{#Lambda}}", kTH1F, - {axisMass}); + histos.add("Reco/h1f_antilambda_invmass", "M_{#bar{#Lambda}}", kTH1F, {axisMass}); histos.add("Reco/h1f_antilambda_cospa", "cos(PA)", kTH1F, {axisCPA}); - histos.add("Reco/h1f_antilambda_dcadau", "DCA daughters", kTH1F, - {axisDcaDau}); + histos.add("Reco/h1f_antilambda_dcadau", "DCA daughters", kTH1F, {axisDcaDau}); histos.addClone("Reco/", "SharingDau/"); } template void fillHistos(T const& track) { - static constexpr std::array SubDir = {"Reco/", - "SharingDau/"}; + static constexpr std::array SubDir = {"Reco/", "SharingDau/"}; if (track.v0Type() == kLambda) { histos.fill(HIST(SubDir[sd]) + HIST("h1f_lambda_invmass"), track.mass()); histos.fill(HIST(SubDir[sd]) + HIST("h1f_lambda_dcadau"), track.dcaDau()); histos.fill(HIST(SubDir[sd]) + HIST("h1f_lambda_cospa"), track.cosPA()); } else { - histos.fill(HIST(SubDir[sd]) + HIST("h1f_antilambda_invmass"), - track.mass()); - histos.fill(HIST(SubDir[sd]) + HIST("h1f_antilambda_dcadau"), - track.dcaDau()); - histos.fill(HIST(SubDir[sd]) + HIST("h1f_antilambda_cospa"), - track.cosPA()); + histos.fill(HIST(SubDir[sd]) + HIST("h1f_antilambda_invmass"), track.mass()); + histos.fill(HIST(SubDir[sd]) + HIST("h1f_antilambda_dcadau"), track.dcaDau()); + histos.fill(HIST(SubDir[sd]) + HIST("h1f_antilambda_cospa"), track.cosPA()); } } - void process(aod::LambdaCollisions::iterator const&, - aod::LambdaTracks const& tracks) + void process(aod::LambdaCollisions::iterator const&, aod::LambdaTracks const& tracks) { - int nTotLambda = 0, nTotAntiLambda = 0, nSelLambda = 0, nSelAntiLambda = 0; + int nTotLambda = 0, nTotAntiLambda = 0; for (auto const& lambda : tracks) { - bool lambdaMinDeltaMassFlag = true, lambdaMinTScoreFlag = true; - bool lambdaSharingDauFlag = false, trueLambdaFlag = false; - std::vector vSharedDauLambdaIndex; - float tLambda = 0., tTrack = 0.; + bool lambdaSharingDauFlag = false; if (lambda.v0Type() == kLambda) { ++nTotLambda; @@ -1487,52 +1405,22 @@ struct LambdaTracksExtProducer { ++nTotAntiLambda; } - tLambda = (cA * std::abs(lambda.mass() - MassLambda0)) + - (cB * lambda.dcaDau()) + (cC * std::abs(lambda.cosPA() - 1.)); - for (auto const& track : tracks) { if (lambda.index() == track.index()) { continue; } - if (lambda.posTrackId() == track.posTrackId() || - lambda.negTrackId() == track.negTrackId()) { - vSharedDauLambdaIndex.push_back(track.index()); + if (lambda.posTrackId() == track.posTrackId() || lambda.negTrackId() == track.negTrackId()) { lambdaSharingDauFlag = true; if (lambda.v0Type() == kLambda && track.v0Type() == kAntiLambda) { - histos.fill(HIST("h2d_n2_etaphi_LaP_LaM"), - lambda.eta() - track.eta(), - RecoDecay::constrainAngle((lambda.phi() - track.phi()), - -PIHalf)); - } else if (lambda.v0Type() == kAntiLambda && - track.v0Type() == kLambda) { - histos.fill(HIST("h2d_n2_etaphi_LaM_LaP"), - lambda.eta() - track.eta(), - RecoDecay::constrainAngle((lambda.phi() - track.phi()), - -PIHalf)); + histos.fill(HIST("h2d_n2_etaphi_LaP_LaM"), lambda.eta() - track.eta(), RecoDecay::constrainAngle((lambda.phi() - track.phi()), -PIHalf)); + } else if (lambda.v0Type() == kAntiLambda && track.v0Type() == kLambda) { + histos.fill(HIST("h2d_n2_etaphi_LaM_LaP"), lambda.eta() - track.eta(), RecoDecay::constrainAngle((lambda.phi() - track.phi()), -PIHalf)); } else if (lambda.v0Type() == kLambda && track.v0Type() == kLambda) { - histos.fill(HIST("h2d_n2_etaphi_LaP_LaP"), - lambda.eta() - track.eta(), - RecoDecay::constrainAngle((lambda.phi() - track.phi()), - -PIHalf)); - } else if (lambda.v0Type() == kAntiLambda && - track.v0Type() == kAntiLambda) { - histos.fill(HIST("h2d_n2_etaphi_LaM_LaM"), - lambda.eta() - track.eta(), - RecoDecay::constrainAngle((lambda.phi() - track.phi()), - -PIHalf)); - } - - if (std::abs(lambda.mass() - MassLambda0) > - std::abs(track.mass() - MassLambda0)) { - lambdaMinDeltaMassFlag = false; - } - - tTrack = (cA * std::abs(track.mass() - MassLambda0)) + - (cB * track.dcaDau()) + (cC * std::abs(track.cosPA() - 1.)); - if (tLambda > tTrack) { - lambdaMinTScoreFlag = false; + histos.fill(HIST("h2d_n2_etaphi_LaP_LaP"), lambda.eta() - track.eta(), RecoDecay::constrainAngle((lambda.phi() - track.phi()), -PIHalf)); + } else if (lambda.v0Type() == kAntiLambda && track.v0Type() == kAntiLambda) { + histos.fill(HIST("h2d_n2_etaphi_LaM_LaM"), lambda.eta() - track.eta(), RecoDecay::constrainAngle((lambda.phi() - track.phi()), -PIHalf)); } } } @@ -1543,22 +1431,7 @@ struct LambdaTracksExtProducer { fillHistos(lambda); } - if (cAcceptAllLambda || (cRejAllLambdaShaDau && !lambdaSharingDauFlag) || - (cSelLambdaMassPdg && lambdaMinDeltaMassFlag) || - (cSelLambdaTScore && lambdaMinTScoreFlag)) { - trueLambdaFlag = true; - } - - if (trueLambdaFlag) { - if (lambda.v0Type() == kLambda) { - ++nSelLambda; - } else if (lambda.v0Type() == kAntiLambda) { - ++nSelAntiLambda; - } - } - - lambdaTrackExtTable(lambdaSharingDauFlag, vSharedDauLambdaIndex, - trueLambdaFlag); + lambdaTrackExtTable(lambdaSharingDauFlag); } if (nTotLambda != 0) { @@ -1567,309 +1440,465 @@ struct LambdaTracksExtProducer { if (nTotAntiLambda != 0) { histos.fill(HIST("h1i_totantilambda_mult"), nTotAntiLambda); } - if (nSelLambda != 0) { - histos.fill(HIST("h1i_lambda_mult"), nSelLambda); - } - if (nSelAntiLambda != 0) { - histos.fill(HIST("h1i_antilambda_mult"), nSelAntiLambda); - } } }; struct LambdaSpinPolarization { - // Table producer Produces lambdaMixEvtCol; Produces lambdaMixEvtTrk; Produces lambdaMixEvtMGCol; Produces lambdaMixEvtMGTrk; - Configurable cNPtBins{"cNPtBins", 30, "N pT bins"}; - Configurable cMinPt{"cMinPt", 0.5f, "pT min (GeV/c)"}; - Configurable cMaxPt{"cMaxPt", 4.5f, "pT max (GeV/c)"}; - Configurable cNRapBins{"cNRapBins", 10, "N rapidity bins"}; - Configurable cMinRap{"cMinRap", -0.5f, "Rapidity min"}; - Configurable cMaxRap{"cMaxRap", 0.5f, "Rapidity max"}; - Configurable cNPhiBins{"cNPhiBins", 36, "N phi bins"}; + Configurable cMassAccMin{"cMassAccMin", 1.0956f, "Accepted mass min (GeV/c2); candidates outside the window are not paired"}; + Configurable cMassAccMax{"cMassAccMax", 1.1356f, "Accepted mass max (GeV/c2); candidates outside the window are not paired"}; + Configurable cMassHistMin{"cMassHistMin", 1.0806f, "Mass axes min (GeV/c2), below cMassAccMin"}; + Configurable cMassHistMax{"cMassHistMax", 1.1806f, "Mass axes max (GeV/c2), above cMassAccMax"}; + Configurable cNMassBins{"cNMassBins", 200, "Mass axes N bins"}; + ConfigurableAxis axisDeltaR{"axisDeltaR", {VARIABLE_WIDTH, 0.0, 0.4, 0.8, 1.2, 1.8, 2.4, 3.1, 3.5}, "DeltaR bins (last bin: outside the analysis windows)"}; Configurable cNBinsCosTS{"cNBinsCosTS", 10, "N costheta* bins"}; - Configurable cNBinsDeltaR{"cNBinsDeltaR", 20, "N DeltaR bins"}; - - Configurable cMassHistMin{"cMassHistMin", 1.08f, - "Mass histogram min (GeV/c2)"}; - Configurable cMassHistMax{"cMassHistMax", 1.20f, - "Mass histogram max (GeV/c2)"}; - Configurable cNMassBins{"cNMassBins", 120, "Mass histogram N bins"}; - - Configurable cSigMinLambda{"cSigMinLambda", 1.108f, - "Signal region min (GeV/c2)"}; - Configurable cSigMaxLambda{"cSigMaxLambda", 1.123f, - "Signal region max (GeV/c2)"}; - Configurable cSbLeftMin{"cSbLeftMin", 1.080f, - "Left sideband min (GeV/c2)"}; - Configurable cSbLeftMax{"cSbLeftMax", 1.100f, - "Left sideband max (GeV/c2)"}; - Configurable cSbRightMin{"cSbRightMin", 1.135f, - "Right sideband min (GeV/c2)"}; - Configurable cSbRightMax{"cSbRightMax", 1.155f, - "Right sideband max (GeV/c2)"}; - - Configurable cInvBoostFlag{"cInvBoostFlag", true, "Inverse boost flag"}; - Configurable cDoAtlasMethod{"cDoAtlasMethod", false, - "Fill pair-boost (ATLAS) histograms"}; - Configurable cDoStarMethod{"cDoStarMethod", true, - "Fill lab-boost (STAR) histograms"}; - Configurable mixingParameter{"mixingParameter", 5, "ME pool depth"}; - Configurable cMEMode{"cMEMode", 1, - "ME mode: 0=standard, 1=kinematicConstrained"}; - - ConfigurableAxis cMultBins{"cMultBins", {VARIABLE_WIDTH, 0.f, 10.f, 30.f, 50.f, 80.f, 100.f}, "Multiplicity bins"}; - ConfigurableAxis axisCentME{"axisCentME", {VARIABLE_WIDTH, 0, 10, 30, 50, 100}, "ME centrality bins"}; - ConfigurableAxis axisVtxZME{"axisVtxZME", {VARIABLE_WIDTH, -7, -3, 0, 3, 7}, "ME vtxZ bins"}; + Configurable cSharedDauRule{"cSharedDauRule", 0, "Candidates sharing a daughter track: 0 = keep the largest set without shared tracks per cluster (ties: closest to the PDG mass), 1 = veto only the pairs sharing a track, 2 = drop every candidate sharing a track"}; + Configurable cMinCandPerEvent{"cMinCandPerEvent", 2, "Collisions with fewer reconstructed Lambda + AntiLambda candidates are skipped by the pair, mixing and derived-table processes (the event QA counts all collisions)"}; + + Configurable cMEPoolDepth{"cMEPoolDepth", 100000, "Rolling ME pool: depth in donor events per (centrality, Vz) bin"}; + Configurable cMEPoolMinEvents{"cMEPoolMinEvents", 20, "Rolling ME pool: events a pool must hold before its bin is mixed (warm-up)"}; + Configurable cMEPoolMinCand{"cMEPoolMinCand", 2, "Rolling ME pool: only events with at least this many accepted candidates donate"}; + ConfigurableAxis axisCentME{"axisCentME", {VARIABLE_WIDTH, 0, 10, 20, 40, 100}, "ME pool centrality bins"}; + ConfigurableAxis axisVtxZME{"axisVtxZME", {VARIABLE_WIDTH, -10., -7., -4., -2., 0, 2., 4., 7., 10.}, "ME pool vtxZ bins"}; Configurable cMaxDeltaPt{"cMaxDeltaPt", 0.1f, "Kinematic ME: max |deltaPt(SE)-deltaPt(ME)| (GeV/c)"}; - Configurable cMaxDeltaPhi{"cMaxDeltaPhi", 0.1f, "Kinematic ME: max |deltaPhi(SE)-deltaPhi(ME)| (rad)"}; - Configurable cMaxDeltaRap{"cMaxDeltaRap", 0.1f, "Kinematic ME: max |deltaRap(SE)-deltaRap(ME)|"}; + Configurable cMaxDeltaPhi{"cMaxDeltaPhi", 0.02f, "Kinematic ME: max |deltaPhi(SE)-deltaPhi(ME)| (rad)"}; + Configurable cMaxDeltaRap{"cMaxDeltaRap", 0.03f, "Kinematic ME: max |deltaRap(SE)-deltaRap(ME)|"}; + Configurable cFillSEInMixing{"cFillSEInMixing", true, "Kinematic ME: fill every same-event pair the mixing processes, matched or not (SEproc)"}; + + Configurable cFillKinWSpectra{"cFillKinWSpectra", true, "Kinematic ME: fill the leg-2 spectra of SEproc and ME (weight-map input, closure)"}; + Configurable cMinPt{"cMinPt", 0.6f, "Leg spectra and ME pool grid: pT min (GeV/c)"}; + Configurable cMaxPt{"cMaxPt", 3.0f, "Leg spectra and ME pool grid: pT max (GeV/c)"}; + Configurable cMinRap{"cMinRap", -0.5f, "Leg spectra and ME pool grid: rapidity min"}; + Configurable cMaxRap{"cMaxRap", 0.5f, "Leg spectra and ME pool grid: rapidity max"}; + Configurable cNKinWPtBins{"cNKinWPtBins", 0, "Leg spectra: pT bins (0: no wider than cMaxDeltaPt)"}; + Configurable cNKinWRapBins{"cNKinWRapBins", 0, "Leg spectra: rapidity bins (0: no wider than cMaxDeltaRap)"}; + Configurable cNKinWPhiBins{"cNKinWPhiBins", 0, "Leg spectra: phi bins (0: no wider than cMaxDeltaPhi)"}; + Configurable cApplyKinWeights{"cApplyKinWeights", false, "Kinematic ME: weight the leg-2 replacements with the maps from CCDB"}; + Configurable cKinWeightCcdbUrl{"cKinWeightCcdbUrl", "http://alice-ccdb.cern.ch", "Kinematic weights: CCDB url"}; + Configurable cKinWeightCcdbPath{"cKinWeightCcdbPath", "", "Kinematic weights: CCDB path of a TList with hKinW_ (axes of KinW/*/hLeg2_)"}; + Configurable cKinWeightCcdbTimestamp{"cKinWeightCcdbTimestamp", -1, "Kinematic weights: CCDB timestamp (-1: timestamp of the first mixed collision)"}; + + Configurable cFillClosePairQA{"cFillClosePairQA", true, "Close-pair QA of the same-event pairs: SE (processDataReco) and SEproc (processDataRecoMixed)"}; + Configurable cClosePairMassWindow{"cClosePairMassWindow", 0.004f, "Close-pair QA: both legs within |m - m_Lambda| < this (GeV/c2); <= 0: all pairs"}; + ConfigurableAxis axisClosePair{"axisClosePair", {100, -0.15, 0.15}, "Close-pair QA: bins of the daughter Delta eta, Delta y and Delta phi"}; + Configurable cFillSharedDauQA{"cFillSharedDauQA", true, "Shared-daughter QA: SE and SEproc pairs with a candidate sharing a daughter track with another candidate, and the cluster sizes"}; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + Service ccdb{}; - HistogramRegistry histos{ - "histos", - {}, - OutputObjHandlingPolicy::AnalysisObject}; + uint64_t pairOrderSeed = 0; + std::array, 4> kinWeightMaps{}; + std::vector legAxesRef; + bool kinWeightsLoaded = false; + + struct SharedDauCand { + int64_t globalIdx; + int64_t posId; + int64_t negId; + float dMass; + }; + static constexpr std::size_t MaxExactCluster = 16; + static constexpr int NoSharedTrackIdx = -1; + std::vector sdCands; + std::vector sdConflicted; + std::vector sdDropped; - float cent = 0.; struct PoolTrack { - float _px, _py, _pz, _pt, _rap, _phi, _mass; - float _prPx, _prPy, _prPz; - [[nodiscard]] float px() const { return _px; } - [[nodiscard]] float py() const { return _py; } - [[nodiscard]] float pz() const { return _pz; } - [[nodiscard]] float pt() const { return _pt; } - [[nodiscard]] float rap() const { return _rap; } - [[nodiscard]] float phi() const { return _phi; } - [[nodiscard]] float mass() const { return _mass; } - [[nodiscard]] float prPx() const { return _prPx; } - [[nodiscard]] float prPy() const { return _prPy; } - [[nodiscard]] float prPz() const { return _prPz; } + float mPx, mPy, mPz, mPt, mRap, mPhi, mMass; + float mPrPx, mPrPy, mPrPz; + int8_t mSpecies; + [[nodiscard]] float px() const { return mPx; } + [[nodiscard]] float py() const { return mPy; } + [[nodiscard]] float pz() const { return mPz; } + [[nodiscard]] float pt() const { return mPt; } + [[nodiscard]] float rap() const { return mRap; } + [[nodiscard]] float phi() const { return mPhi; } + [[nodiscard]] float mass() const { return mMass; } + [[nodiscard]] float prPx() const { return mPrPx; } + [[nodiscard]] float prPy() const { return mPrPy; } + [[nodiscard]] float prPz() const { return mPrPz; } + [[nodiscard]] int8_t species() const { return mSpecies; } }; template PoolTrack toPoolTrack(T const& trk) { - return PoolTrack{trk.px(), trk.py(), trk.pz(), trk.pt(), trk.rap(), - trk.phi(), trk.mass(), trk.prPx(), trk.prPy(), trk.prPz()}; + return PoolTrack{trk.px(), trk.py(), trk.pz(), trk.pt(), trk.rap(), trk.phi(), trk.mass(), trk.prPx(), trk.prPy(), trk.prPz(), static_cast(trk.v0Type())}; } - void init(InitContext const&) + class RollingPool { - const AxisSpec axisCheck(1, 0, 1, ""); - const AxisSpec axisCent(cMultBins, "FT0M (%)"); + public: + struct Grid { + float ptMin = 0.f; + float rapMin = 0.f; + float cellPt = 1.f; + float cellRap = 1.f; + float cellPhi = TwoPI; + int nPt = 1; + int nRap = 1; + int nPhi = 1; + }; + + RollingPool() = default; + RollingPool(Grid const& g, int depth) : grid(g), maxEvents(depth) {} + + [[nodiscard]] std::size_t nEvents() const { return nPerEvent.size(); } + + void push(std::vector const& event) + { + for (auto const& cand : event) { + const int64_t seq = seqBase + static_cast(slots.size()); + slots.push_back(Slot{.cand = cand, .next = NoSeq}); + auto [it, isNew] = cells[cand.species()].try_emplace(cellKey(cand), Cell{.head = seq, .tail = seq}); + if (!isNew) { + slot(it->second.tail).next = seq; + it->second.tail = seq; + } + } + nPerEvent.push_back(static_cast(event.size())); + while (static_cast(nPerEvent.size()) > maxEvents) { + const int n = nPerEvent.front(); + for (int i = 0; i < n; ++i) { + retireOldest(); + } + nPerEvent.pop_front(); + } + } + + template + void findMatches(PoolTrack const& leg, F const& isMatch, std::vector& out) const + { + out.clear(); + int iPt = 0, iRap = 0, iPhi = 0; + cellCoords(leg, iPt, iRap, iPhi); + std::array phiCells{}; + int nPhiCells = 0; + for (int d = -1; d <= 1; ++d) { + const int k = ((iPhi + d) % grid.nPhi + grid.nPhi) % grid.nPhi; + if (std::find(phiCells.begin(), phiCells.begin() + nPhiCells, k) == phiCells.begin() + nPhiCells) { + phiCells[nPhiCells++] = k; + } + } + auto const& speciesCells = cells[leg.species()]; + for (int jPt = std::max(iPt - 1, 0); jPt <= std::min(iPt + 1, grid.nPt - 1); ++jPt) { + for (int jRap = std::max(iRap - 1, 0); jRap <= std::min(iRap + 1, grid.nRap - 1); ++jRap) { + for (int ip = 0; ip < nPhiCells; ++ip) { + const auto it = speciesCells.find(key(jPt, jRap, phiCells[ip])); + if (it == speciesCells.end()) { + continue; + } + for (int64_t seq = it->second.head; seq != NoSeq;) { + Slot const& sl = slot(seq); + if (isMatch(sl.cand)) { + out.push_back(&sl.cand); + } + seq = sl.next; + } + } + } + } + } + + private: + static constexpr int64_t NoSeq = -1; + struct Slot { + PoolTrack cand; + int64_t next; + }; + struct Cell { + int64_t head; + int64_t tail; + }; + + Slot& slot(int64_t seq) { return slots[static_cast(seq - seqBase)]; } + [[nodiscard]] Slot const& slot(int64_t seq) const { return slots[static_cast(seq - seqBase)]; } + + void retireOldest() + { + Slot const& old = slots.front(); + auto& speciesCells = cells[old.cand.species()]; + auto it = speciesCells.find(cellKey(old.cand)); + if (it == speciesCells.end() || it->second.head != seqBase) { + LOGF(fatal, "RollingPool: grid index out of step with the candidate queue"); + return; + } + if (old.next == NoSeq) { + speciesCells.erase(it); + } else { + it->second.head = old.next; + } + slots.pop_front(); + ++seqBase; + } + + [[nodiscard]] int64_t key(int iPt, int iRap, int iPhi) const + { + return (static_cast(iPt) * grid.nRap + iRap) * grid.nPhi + iPhi; + } - const AxisSpec axisMass(cNMassBins, cMassHistMin, cMassHistMax, - "M_{#Lambda} (GeV/#it{c}^{2})"); - const AxisSpec axisPt(cNPtBins, cMinPt, cMaxPt, "p_{T} (GeV/#it{c})"); - const AxisSpec axisDRap(2 * cNRapBins, cMinRap - cMaxRap, cMaxRap - cMinRap, - "#Deltay"); - const AxisSpec axisDPhi(cNPhiBins, -PI, PI, "#Delta#varphi"); + void cellCoords(PoolTrack const& c, int& iPt, int& iRap, int& iPhi) const + { + iPt = std::clamp(static_cast(std::floor((c.pt() - grid.ptMin) / grid.cellPt)), 0, grid.nPt - 1); + iRap = std::clamp(static_cast(std::floor((c.rap() - grid.rapMin) / grid.cellRap)), 0, grid.nRap - 1); + iPhi = std::clamp(static_cast(std::floor(RecoDecay::constrainAngle(c.phi(), 0.f) / grid.cellPhi)), 0, grid.nPhi - 1); + } + + [[nodiscard]] int64_t cellKey(PoolTrack const& c) const + { + int iPt = 0, iRap = 0, iPhi = 0; + cellCoords(c, iPt, iRap, iPhi); + return key(iPt, iRap, iPhi); + } + + Grid grid{}; + int maxEvents = 0; + std::deque slots; + std::deque nPerEvent; + int64_t seqBase = 0; + std::array, 2> cells{}; + }; + + std::vector mePools; + std::vector mePoolsGen; + TAxis mePoolAxisCent; + TAxis mePoolAxisVz; + + void init(InitContext const&) + { + const AxisSpec axisM1(cNMassBins, cMassHistMin, cMassHistMax, "M_{1} (GeV/#it{c}^{2})"); + const AxisSpec axisM2(cNMassBins, cMassHistMin, cMassHistMax, "M_{2} (GeV/#it{c}^{2})"); + const AxisSpec axisDR(axisDeltaR, "#DeltaR"); const AxisSpec axisCosTS(cNBinsCosTS, -1, 1, "cos(#theta*)"); - const AxisSpec axisDR(cNBinsDeltaR, 0, 3.5, "#DeltaR"); - const AxisSpec axisPosZ(200, -10, 10, "V_{z} (cm)"); - const AxisSpec axisMult(10, 0, 10, "N_{#Lambda}"); + const std::vector pairAxes = {axisM1, axisM2, axisDR, axisCosTS}; + + auto nBins = [](int configured, float range, float window) { + if (configured > 0) { + return configured; + } + return window > 0.f ? static_cast(std::ceil(range / window)) : 1; + }; + const AxisSpec axisLegPt(nBins(cNKinWPtBins, cMaxPt - cMinPt, cMaxDeltaPt), cMinPt, cMaxPt, "p_{T} (GeV/#it{c})"); + const AxisSpec axisLegRap(nBins(cNKinWRapBins, cMaxRap - cMinRap, cMaxDeltaRap), cMinRap, cMaxRap, "y"); + const AxisSpec axisLegPhi(nBins(cNKinWPhiBins, TwoPI, cMaxDeltaPhi), 0., TwoPI, "#varphi (rad)"); + const std::vector legAxes = {axisLegPt, axisLegRap, axisLegPhi}; + legAxesRef.clear(); + for (auto const& spec : legAxes) { + const std::vector edges = axisEdges(spec); + legAxesRef.emplace_back(static_cast(edges.size()) - 1, edges.data()); + } + + const bool massAxesContainWindow = cMassHistMin.value < cMassAccMin.value && cMassAccMax.value < cMassHistMax.value; + if (!massAxesContainWindow) { + LOGF(fatal, "The mass axes [%.4f, %.4f] must be wider than the accepted mass window [%.4f, %.4f]", cMassHistMin.value, cMassHistMax.value, cMassAccMin.value, cMassAccMax.value); + } + for (auto const& edge : {cMassAccMin.value, cMassAccMax.value}) { + if (!isOnMassBinEdge(edge)) { + LOGF(warning, "Accepted mass %.5f is not a bin edge of the mass axes [%.4f, %.4f] / %d", edge, cMassHistMin.value, cMassHistMax.value, cNMassBins.value); + } + } + if (cSharedDauRule.value < kSharedDauMaxSet || cSharedDauRule.value > kSharedDauDropAll) { + LOGF(fatal, "cSharedDauRule must be %d (largest set without shared tracks), %d (veto pairs sharing a track) or %d (drop every candidate sharing a track)", static_cast(kSharedDauMaxSet), static_cast(kSharedDauPairVeto), static_cast(kSharedDauDropAll)); + } + if (cMinCandPerEvent.value < 1) { + LOGF(fatal, "cMinCandPerEvent must be at least 1, it is %d", cMinCandPerEvent.value); + } + + if (doprocessDataRecoMixed || doprocessMcGenMixed) { + const std::vector centEdges = axisEdges(AxisSpec(axisCentME)); + const std::vector vzEdges = axisEdges(AxisSpec(axisVtxZME)); + mePoolAxisCent.Set(static_cast(centEdges.size()) - 1, centEdges.data()); + mePoolAxisVz.Set(static_cast(vzEdges.size()) - 1, vzEdges.data()); + + static constexpr std::array PoolSizeSteps = {1, 2, 5}; + static constexpr int64_t PoolSizeDecade = 10; + const int64_t depth = std::max(1, cMEPoolDepth.value); + std::vector poolSizeEdges = {0.}; + for (int64_t decade = 1; decade < depth; decade *= PoolSizeDecade) { + for (auto const& step : PoolSizeSteps) { + if (step * decade < depth) { + poolSizeEdges.push_back(static_cast(step * decade)); + } + } + } + poolSizeEdges.push_back(static_cast(depth)); + const AxisSpec axisPoolSize(poolSizeEdges, "events in the pool"); + + std::vector mixQaDirs; + if (doprocessDataRecoMixed) { + mixQaDirs.emplace_back("QA/ME/"); + } + if (doprocessMcGenMixed) { + mixQaDirs.emplace_back("McGen/QA/ME/"); + } + for (auto const& dir : mixQaDirs) { + histos.add((dir + "hMixedCentVz").c_str(), "events mixed;cent (%);V_{z} (cm)", kTH2F, {axisCentME, axisVtxZME}); + auto hStatus = histos.add((dir + "hEventStatus").c_str(), "rolling-pool mixing;;collisions", kTH1D, {{kNMEEventStatus - 1, 0.5, static_cast(kNMEEventStatus) - 0.5}}); + hStatus->GetXaxis()->SetBinLabel(kMEEventSeen, "seen"); + hStatus->GetXaxis()->SetBinLabel(kMEEventOutsidePools, "outside the pool bins"); + hStatus->GetXaxis()->SetBinLabel(kMEEventWarmUp, "pairs, pool in warm-up"); + hStatus->GetXaxis()->SetBinLabel(kMEEventMixed, "mixed"); + hStatus->GetXaxis()->SetBinLabel(kMEEventDonated, "donated to the pool"); + histos.add((dir + "hPoolEventsAtMixing").c_str(), "pool size when a collision is mixed;events in the pool;collisions", kTH1F, {axisPoolSize}); + histos.add((dir + "hMatchesLeg2").c_str(), "matches per replaced leg 2;N matches;pair type", kTH2F, {{100, -0.5, 99.5}, {4, -0.5, 3.5}}); + histos.add((dir + "hDeltaRShift").c_str(), "ME pairs are filed at their own #DeltaR;#DeltaR_{seed};#DeltaR_{mixed pair} - #DeltaR_{seed}", kTH2F, {{35, 0., 3.5}, {100, -0.2, 0.2}}); + histos.add((dir + "hLambdaMultVsCent").c_str(), "ME #Lambda mult;cent;N", kTH2D, {axisCentME, {50, 0, 50}}); + histos.add((dir + "hAntiLambdaMultVsCent").c_str(), "ME #bar{#Lambda} mult;cent;N", kTH2D, {axisCentME, {50, 0, 50}}); + } - histos.add("QA/ME/hPoolCentVz", "ME pool;cent (%);V_{z}", kTH2F, - {axisCentME, axisVtxZME}); - histos.add("QA/ME/hLambdaMultVsCent", "ME #Lambda mult;cent;N", kTH2F, - {axisCentME, {50, 0, 50}}); - histos.add("QA/ME/hAntiLambdaMultVsCent", "ME #bar{#Lambda} mult;cent;N", - kTH2F, {axisCentME, {50, 0, 50}}); - - const std::vector massSparseAxes = { - axisCent, axisMass, axisMass, axisPt, axisPt, - axisDRap, axisDPhi, axisDR, axisCosTS}; - - histos.add("SE/Reco/Star/h2f_n2_mass_LaPLaM", - "M_{inv}: #Lambda#bar{#Lambda} inclusive", kTHnSparseF, - massSparseAxes); - histos.add("SE/Reco/Star/h2f_n2_mass_LaMLaP", - "M_{inv}: #bar{#Lambda}#Lambda inclusive", kTHnSparseF, - massSparseAxes); - histos.add("SE/Reco/Star/h2f_n2_mass_LaPLaP", - "M_{inv}: #Lambda#Lambda inclusive", kTHnSparseF, - massSparseAxes); - histos.add("SE/Reco/Star/h2f_n2_mass_LaMLaM", - "M_{inv}: #bar{#Lambda}#bar{#Lambda} inclusive", kTHnSparseF, - massSparseAxes); - - histos.add("SE/RecoBkgSigSB/Star/h2f_n2_mass_LaPLaM", - "M_{inv}: #Lambda#bar{#Lambda} Sig#timesSB", kTHnSparseF, - massSparseAxes); - histos.add("SE/RecoBkgSigSB/Star/h2f_n2_mass_LaMLaP", - "M_{inv}: #bar{#Lambda}#Lambda Sig#timesSB", kTHnSparseF, - massSparseAxes); - histos.add("SE/RecoBkgSigSB/Star/h2f_n2_mass_LaPLaP", - "M_{inv}: #Lambda#Lambda Sig#timesSB", kTHnSparseF, - massSparseAxes); - histos.add("SE/RecoBkgSigSB/Star/h2f_n2_mass_LaMLaM", - "M_{inv}: #bar{#Lambda}#bar{#Lambda} Sig#timesSB", kTHnSparseF, - massSparseAxes); - - histos.add("SE/RecoBkgSBSB/Star/h2f_n2_mass_LaPLaM", - "M_{inv}: #Lambda#bar{#Lambda} SB#timesSB", kTHnSparseF, - massSparseAxes); - histos.add("SE/RecoBkgSBSB/Star/h2f_n2_mass_LaMLaP", - "M_{inv}: #bar{#Lambda}#Lambda SB#timesSB", kTHnSparseF, - massSparseAxes); - histos.add("SE/RecoBkgSBSB/Star/h2f_n2_mass_LaPLaP", - "M_{inv}: #Lambda#Lambda SB#timesSB", kTHnSparseF, - massSparseAxes); - histos.add("SE/RecoBkgSBSB/Star/h2f_n2_mass_LaMLaM", - "M_{inv}: #bar{#Lambda}#bar{#Lambda} SB#timesSB", kTHnSparseF, - massSparseAxes); - - histos.addClone("SE/Reco/Star/", "SE/Reco/Atlas/"); - histos.addClone("SE/RecoBkgSigSB/Star/", "SE/RecoBkgSigSB/Atlas/"); - histos.addClone("SE/RecoBkgSBSB/Star/", "SE/RecoBkgSBSB/Atlas/"); - - histos.addClone("SE/Reco/", "ME/Reco/"); - histos.addClone("SE/RecoBkgSigSB/", "ME/RecoBkgSigSB/"); - histos.addClone("SE/RecoBkgSBSB/", "ME/RecoBkgSBSB/"); - - histos.add("SE/RecoCorr/Star/h2f_n2_dltaR_LaPLaM", - "#rho_{2} #Lambda#bar{#Lambda} [Star]", kTHnSparseF, - {axisCent, axisDR, axisCosTS}); - histos.add("SE/RecoCorr/Star/h2f_n2_dltaR_LaMLaP", - "#rho_{2} #bar{#Lambda}#Lambda [Star]", kTHnSparseF, - {axisCent, axisDR, axisCosTS}); - histos.add("SE/RecoCorr/Star/h2f_n2_dltaR_LaPLaP", - "#rho_{2} #Lambda#Lambda [Star]", kTHnSparseF, - {axisCent, axisDR, axisCosTS}); - histos.add("SE/RecoCorr/Star/h2f_n2_dltaR_LaMLaM", - "#rho_{2} #bar{#Lambda}#bar{#Lambda} [Star]", kTHnSparseF, - {axisCent, axisDR, axisCosTS}); - - histos.add("SE/RecoCorr/Star/h2f_n2_ctheta_LaPLaM", - "#rho_{2} #Lambda#bar{#Lambda} [Star]", kTHnSparseF, - {axisCent, axisDRap, axisDPhi, axisCosTS}); - histos.add("SE/RecoCorr/Star/h2f_n2_ctheta_LaMLaP", - "#rho_{2} #bar{#Lambda}#Lambda [Star]", kTHnSparseF, - {axisCent, axisDRap, axisDPhi, axisCosTS}); - histos.add("SE/RecoCorr/Star/h2f_n2_ctheta_LaPLaP", - "#rho_{2} #Lambda#Lambda [Star]", kTHnSparseF, - {axisCent, axisDRap, axisDPhi, axisCosTS}); - histos.add("SE/RecoCorr/Star/h2f_n2_ctheta_LaMLaM", - "#rho_{2} #bar{#Lambda}#bar{#Lambda} [Star]", kTHnSparseF, - {axisCent, axisDRap, axisDPhi, axisCosTS}); - - histos.add("SE/RecoCorr/Atlas/h2f_n2_dltaR_LaPLaM", - "#rho_{2} #Lambda#bar{#Lambda} [Atlas]", kTHnSparseF, - {axisCent, axisDR, axisCosTS}); - histos.add("SE/RecoCorr/Atlas/h2f_n2_dltaR_LaMLaP", - "#rho_{2} #bar{#Lambda}#Lambda [Atlas]", kTHnSparseF, - {axisCent, axisDR, axisCosTS}); - histos.add("SE/RecoCorr/Atlas/h2f_n2_dltaR_LaPLaP", - "#rho_{2} #Lambda#Lambda [Atlas]", kTHnSparseF, - {axisCent, axisDR, axisCosTS}); - histos.add("SE/RecoCorr/Atlas/h2f_n2_dltaR_LaMLaM", - "#rho_{2} #bar{#Lambda}#bar{#Lambda} [Atlas]", kTHnSparseF, - {axisCent, axisDR, axisCosTS}); - - histos.add("SE/RecoCorr/Atlas/h2f_n2_ctheta_LaPLaM", - "#rho_{2} #Lambda#bar{#Lambda} [Atlas]", kTHnSparseF, - {axisCent, axisDRap, axisDPhi, axisCosTS}); - histos.add("SE/RecoCorr/Atlas/h2f_n2_ctheta_LaMLaP", - "#rho_{2} #bar{#Lambda}#Lambda [Atlas]", kTHnSparseF, - {axisCent, axisDRap, axisDPhi, axisCosTS}); - histos.add("SE/RecoCorr/Atlas/h2f_n2_ctheta_LaPLaP", - "#rho_{2} #Lambda#Lambda [Atlas]", kTHnSparseF, - {axisCent, axisDRap, axisDPhi, axisCosTS}); - histos.add("SE/RecoCorr/Atlas/h2f_n2_ctheta_LaMLaM", - "#rho_{2} #bar{#Lambda}#bar{#Lambda} [Atlas]", kTHnSparseF, - {axisCent, axisDRap, axisDPhi, axisCosTS}); - - histos.add("ME/RecoCorr/Star/h2f_n2_dltaR_LaPLaM", - "#rho_{2} #Lambda#bar{#Lambda} [Star]", kTHnSparseF, - {axisCent, axisDR, axisCosTS}); - histos.add("ME/RecoCorr/Star/h2f_n2_dltaR_LaMLaP", - "#rho_{2} #bar{#Lambda}#Lambda [Star]", kTHnSparseF, - {axisCent, axisDR, axisCosTS}); - histos.add("ME/RecoCorr/Star/h2f_n2_dltaR_LaPLaP", - "#rho_{2} #Lambda#Lambda [Star]", kTHnSparseF, - {axisCent, axisDR, axisCosTS}); - histos.add("ME/RecoCorr/Star/h2f_n2_dltaR_LaMLaM", - "#rho_{2} #bar{#Lambda}#bar{#Lambda} [Star]", kTHnSparseF, - {axisCent, axisDR, axisCosTS}); - - histos.add("ME/RecoCorr/Star/h2f_n2_ctheta_LaPLaM", - "#rho_{2} #Lambda#bar{#Lambda} [Star]", kTHnSparseF, - {axisCent, axisDRap, axisDPhi, axisCosTS}); - histos.add("ME/RecoCorr/Star/h2f_n2_ctheta_LaMLaP", - "#rho_{2} #bar{#Lambda}#Lambda [Star]", kTHnSparseF, - {axisCent, axisDRap, axisDPhi, axisCosTS}); - histos.add("ME/RecoCorr/Star/h2f_n2_ctheta_LaPLaP", - "#rho_{2} #Lambda#Lambda [Star]", kTHnSparseF, - {axisCent, axisDRap, axisDPhi, axisCosTS}); - histos.add("ME/RecoCorr/Star/h2f_n2_ctheta_LaMLaM", - "#rho_{2} #bar{#Lambda}#bar{#Lambda} [Star]", kTHnSparseF, - {axisCent, axisDRap, axisDPhi, axisCosTS}); - - histos.add("ME/RecoCorr/Atlas/h2f_n2_dltaR_LaPLaM", - "#rho_{2} #Lambda#bar{#Lambda} [Atlas]", kTHnSparseF, - {axisCent, axisDR, axisCosTS}); - histos.add("ME/RecoCorr/Atlas/h2f_n2_dltaR_LaMLaP", - "#rho_{2} #bar{#Lambda}#Lambda [Atlas]", kTHnSparseF, - {axisCent, axisDR, axisCosTS}); - histos.add("ME/RecoCorr/Atlas/h2f_n2_dltaR_LaPLaP", - "#rho_{2} #Lambda#Lambda [Atlas]", kTHnSparseF, - {axisCent, axisDR, axisCosTS}); - histos.add("ME/RecoCorr/Atlas/h2f_n2_dltaR_LaMLaM", - "#rho_{2} #bar{#Lambda}#bar{#Lambda} [Atlas]", kTHnSparseF, - {axisCent, axisDR, axisCosTS}); - - histos.add("ME/RecoCorr/Atlas/h2f_n2_ctheta_LaPLaM", - "#rho_{2} #Lambda#bar{#Lambda} [Atlas]", kTHnSparseF, - {axisCent, axisDRap, axisDPhi, axisCosTS}); - histos.add("ME/RecoCorr/Atlas/h2f_n2_ctheta_LaMLaP", - "#rho_{2} #bar{#Lambda}#Lambda [Atlas]", kTHnSparseF, - {axisCent, axisDRap, axisDPhi, axisCosTS}); - histos.add("ME/RecoCorr/Atlas/h2f_n2_ctheta_LaPLaP", - "#rho_{2} #Lambda#Lambda [Atlas]", kTHnSparseF, - {axisCent, axisDRap, axisDPhi, axisCosTS}); - histos.add("ME/RecoCorr/Atlas/h2f_n2_ctheta_LaMLaM", - "#rho_{2} #bar{#Lambda}#bar{#Lambda} [Atlas]", kTHnSparseF, - {axisCent, axisDRap, axisDPhi, axisCosTS}); - - histos.addClone("SE/RecoCorr/Star/", "SE/RecoCorrBkgSigSB/Star/"); - histos.addClone("SE/RecoCorr/Atlas/", "SE/RecoCorrBkgSigSB/Atlas/"); - histos.addClone("SE/RecoCorr/Star/", "SE/RecoCorrBkgSBSB/Star/"); - histos.addClone("SE/RecoCorr/Atlas/", "SE/RecoCorrBkgSBSB/Atlas/"); - - histos.addClone("ME/RecoCorr/Star/", "ME/RecoCorrBkgSigSB/Star/"); - histos.addClone("ME/RecoCorr/Atlas/", "ME/RecoCorrBkgSigSB/Atlas/"); - histos.addClone("ME/RecoCorr/Star/", "ME/RecoCorrBkgSBSB/Star/"); - histos.addClone("ME/RecoCorr/Atlas/", "ME/RecoCorrBkgSBSB/Atlas/"); - } - - bool isSignal(float m) const - { - return (m >= cSigMinLambda.value && m <= cSigMaxLambda.value); - } - bool isSideband(float m) const - { - return ((m >= cSbLeftMin.value && m <= cSbLeftMax.value) || - (m >= cSbRightMin.value && m <= cSbRightMax.value)); - } - bool isInclusive(float m) const - { - return (m >= cMassHistMin.value && m <= cMassHistMax.value); - } - - void getBoostVector(std::array const& p, std::array& v, - bool inverseBoostFlag = true) + if (cMaxDeltaPt.value <= 0.f || cMaxDeltaRap.value <= 0.f || cMaxDeltaPhi.value <= 0.f) { + LOGF(fatal, "Rolling ME pool: the matching windows cMaxDeltaPt, cMaxDeltaRap and cMaxDeltaPhi must be positive"); + } + RollingPool::Grid grid; + grid.ptMin = cMinPt.value; + grid.rapMin = cMinRap.value; + grid.cellPt = cMaxDeltaPt.value; + grid.cellRap = cMaxDeltaRap.value; + grid.nPt = std::max(1, static_cast(std::ceil((cMaxPt.value - cMinPt.value) / grid.cellPt))); + grid.nRap = std::max(1, static_cast(std::ceil((cMaxRap.value - cMinRap.value) / grid.cellRap))); + grid.nPhi = std::max(1, static_cast(std::floor(TwoPI / cMaxDeltaPhi.value))); + grid.cellPhi = TwoPI / static_cast(grid.nPhi); + const auto nPools = static_cast(mePoolAxisCent.GetNbins()) * static_cast(mePoolAxisVz.GetNbins()); + if (doprocessDataRecoMixed) { + mePools.assign(nPools, RollingPool(grid, cMEPoolDepth.value)); + } + if (doprocessMcGenMixed) { + mePoolsGen.assign(nPools, RollingPool(grid, cMEPoolDepth.value)); + } + LOGF(info, "Rolling ME pools: %d (centrality) x %d (Vz) bins, depth %d donor events, grid %d x %d x %d (pT, y, phi)", + mePoolAxisCent.GetNbins(), mePoolAxisVz.GetNbins(), cMEPoolDepth.value, grid.nPt, grid.nRap, grid.nPhi); + } + + std::vector evDirs; + if (doprocessDataReco || doprocessDataRecoMixed) { + evDirs.emplace_back(""); + } + if (doprocessMcGen || doprocessMcGenMixed) { + evDirs.emplace_back("McGen/"); + } + const AxisSpec axisCandMass(cNMassBins, cMassHistMin, cMassHistMax, "M_{p#pi} (GeV/#it{c}^{2})"); + for (auto const& dir : evDirs) { + auto hCount = histos.add((dir + "Events/hEventCount").c_str(), "collisions of the pair analysis;;collisions", kTH1D, {{kNPairEventCount - 1, 0.5, static_cast(kNPairEventCount) - 0.5}}); + hCount->GetXaxis()->SetBinLabel(kPeAll, "all"); + hCount->GetXaxis()->SetBinLabel(kPeOneCand, "#geq 1 accepted #Lambda/#bar{#Lambda}"); + hCount->GetXaxis()->SetBinLabel(kPeTwoCand, "#geq 2 accepted (enter pairs)"); + hCount->GetXaxis()->SetBinLabel(kPeUnlikeSign, "#geq 1 #Lambda#bar{#Lambda} pair"); + hCount->GetXaxis()->SetBinLabel(kPeLambdaLambda, "#geq 1 #Lambda#Lambda pair"); + hCount->GetXaxis()->SetBinLabel(kPeAntiLambdaAntiLambda, "#geq 1 #bar{#Lambda}#bar{#Lambda} pair"); + histos.add((dir + "Events/hNCandAccepted").c_str(), "accepted candidates per collision;N_{#Lambda};N_{#bar{#Lambda}}", kTH2D, {{21, -0.5, 20.5}, {21, -0.5, 20.5}}); + histos.add((dir + "Events/hCentPairEvents").c_str(), "collisions with a pair;cent (%);collisions", kTH1D, {{100, 0., 100.}}); + for (auto const& species : {"Lambda/", "AntiLambda/"}) { + const std::string cand = dir + "QA/PairCand/" + species; + histos.add((cand + "hMassVsPt").c_str(), "accepted candidates of collisions with a pair", kTH2F, {axisCandMass, axisLegPt}); + histos.add((cand + "hRapVsPhi").c_str(), "accepted candidates of collisions with a pair", kTH2F, {axisLegRap, axisLegPhi}); + } + } + + static constexpr std::array PairTags = {"LaPLaM", "LaMLaP", "LaPLaP", "LaMLaM"}; + for (auto const& tag : PairTags) { + const std::string pair{tag}; + if (doprocessDataReco) { + histos.add(("SE/hPair_" + pair).c_str(), ("SE " + pair).c_str(), kTHnSparseF, pairAxes); + } + if (doprocessDataRecoMixed) { + histos.add(("ME/hPair_" + pair).c_str(), ("ME " + pair).c_str(), kTHnSparseD, pairAxes, true); + } + if (doprocessDataRecoMixed && cFillSEInMixing) { + histos.add(("SEproc/hPair_" + pair).c_str(), ("SE processed by the mixing " + pair).c_str(), kTHnSparseF, pairAxes); + } + if (doprocessDataRecoMixed && cFillKinWSpectra) { + histos.add(("KinW/SEproc/hLeg2_" + pair).c_str(), ("leg 2 of SEproc " + pair).c_str(), kTHnSparseD, legAxes); + histos.add(("KinW/ME/hLeg2_" + pair).c_str(), ("candidates replacing leg 2 " + pair).c_str(), kTHnSparseD, legAxes, true); + } + if (cFillSharedDauQA && doprocessDataReco) { + histos.add(("QA/SharedDau/SE/hPair_" + pair).c_str(), ("SE pairs with a candidate sharing a daughter " + pair).c_str(), kTHnSparseF, pairAxes); + } + if (cFillSharedDauQA && doprocessDataRecoMixed) { + histos.add(("QA/SharedDau/SEproc/hPair_" + pair).c_str(), ("SEproc pairs with a candidate sharing a daughter " + pair).c_str(), kTHnSparseF, pairAxes); + } + if (doprocessMcGen) { + histos.add(("McGen/SE/hPair_" + pair).c_str(), ("MC generated SE " + pair).c_str(), kTHnSparseF, pairAxes); + } + if (doprocessMcGenMixed) { + histos.add(("McGen/ME/hPair_" + pair).c_str(), ("MC generated ME " + pair).c_str(), kTHnSparseD, pairAxes, true); + } + if (doprocessMcGenMixed && cFillSEInMixing) { + histos.add(("McGen/SEproc/hPair_" + pair).c_str(), ("MC generated SE processed by the mixing " + pair).c_str(), kTHnSparseF, pairAxes); + } + } + if (cFillSharedDauQA && (doprocessDataReco || doprocessDataRecoMixed)) { + histos.add("QA/SharedDau/hClusterSize", "clusters of candidates sharing daughter tracks;candidates in the cluster;clusters", kTH1F, {{20, 0.5, 20.5}}); + } + + if (cFillClosePairQA && (doprocessDataReco || doprocessDataRecoMixed)) { + const AxisSpec axisCPdEta(axisClosePair, "#Delta#eta"); + const AxisSpec axisCPdY(axisClosePair, "#Deltay"); + const AxisSpec axisCPdPhi(axisClosePair, "#Delta#varphi (rad)"); + std::vector> cpDirs; + if (doprocessDataReco) { + cpDirs.emplace_back("QA/ClosePair/", ""); + } + if (doprocessDataRecoMixed) { + cpDirs.emplace_back("QA/ClosePairSEproc/", "SEproc, "); + } + for (auto const& [dir, tag] : cpDirs) { + for (auto const& cls : {"LamLam", "ALamALam", "UnlikeSign"}) { + for (auto const& combo : {"pp", "pipi", "p1pi2", "pi1p2"}) { + const std::string name = dir + cls + "/h_" + combo; + const std::string title = tag + cls + ", daughters " + combo; + histos.add((name + "_dEta").c_str(), title.c_str(), kTH3F, {axisDR, axisCPdEta, axisCPdPhi}); + histos.add((name + "_dY").c_str(), title.c_str(), kTH3F, {axisDR, axisCPdY, axisCPdPhi}); + } + } + } + } + + if (cApplyKinWeights) { + if (cKinWeightCcdbPath.value.empty()) { + LOGF(fatal, "cApplyKinWeights is set but cKinWeightCcdbPath is empty"); + } + ccdb->setURL(cKinWeightCcdbUrl.value); + ccdb->setCaching(true); + ccdb->setFatalWhenNull(false); + } + } + + bool isAcceptedMass(float m) const + { + return m >= cMassAccMin.value && m < cMassAccMax.value; + } + + static std::vector axisEdges(AxisSpec const& axis) + { + if (!axis.nBins.has_value()) { + return axis.binEdges; + } + const int n = axis.nBins.value(); + std::vector edges(n + 1); + for (int i = 0; i <= n; ++i) { + edges[i] = axis.binEdges[0] + (axis.binEdges[1] - axis.binEdges[0]) * i / n; + } + return edges; + } + + static constexpr double MassEdgeTolerance = 1e-3; + [[nodiscard]] bool isOnMassBinEdge(double m) const + { + const double width = (static_cast(cMassHistMax.value) - cMassHistMin.value) / cNMassBins.value; + const double k = (m - cMassHistMin.value) / width; + return std::abs(k - std::round(k)) < MassEdgeTolerance; + } + + void getBoostVector(std::array const& p, std::array& v) { int n = p.size(); for (int i = 0; i < n - 1; ++i) { - if (inverseBoostFlag) { - v[i] = -p[i] / RecoDecay::e(p[0], p[1], p[2], p[3]); - } else { - v[i] = p[i] / RecoDecay::e(p[0], p[1], p[2], p[3]); - } + v[i] = -p[i] / RecoDecay::e(p[0], p[1], p[2], p[3]); } } @@ -1885,9 +1914,7 @@ struct LambdaSpinPolarization { p[2] = p[2] + gam2 * bp * b[2] + gam * b[2] * e; } - // cos of the opening angle between the two proton three-momenta. - static float cosOpeningAngle(std::array const& a, - std::array const& b) + static float cosOpeningAngle(std::array const& a, std::array const& b) { std::array n1 = {a[0], a[1], a[2]}, n2 = {b[0], b[1], b[2]}; return RecoDecay::dotProd(n1, n2) / @@ -1895,591 +1922,727 @@ struct LambdaSpinPolarization { RecoDecay::sqrtSumOfSquares(n2[0], n2[1], n2[2])); } - float cosThetaStarAtlas(std::array const& l1, - std::array const& l2, - std::array const& pr1, - std::array const& pr2) + float cosThetaStarAtlas(std::array const& l1, std::array const& l2, std::array const& pr1, std::array const& pr2) { auto l1a = l1; auto l2a = l2; auto pr1a = pr1; auto pr2a = pr2; - const float mPair = - RecoDecay::m(std::array{std::array{l1a[0], l1a[1], l1a[2]}, - std::array{l2a[0], l2a[1], l2a[2]}}, - std::array{l1a[3], l2a[3]}); - std::array llpair = {l1a[0] + l2a[0], l1a[1] + l2a[1], - l1a[2] + l2a[2], mPair}; + const float mPair = RecoDecay::m(std::array{std::array{l1a[0], l1a[1], l1a[2]}, std::array{l2a[0], l2a[1], l2a[2]}}, std::array{l1a[3], l2a[3]}); + std::array llpair = {l1a[0] + l2a[0], l1a[1] + l2a[1], l1a[2] + l2a[2], mPair}; std::array vPair{}; - getBoostVector(llpair, vPair, cInvBoostFlag); + getBoostVector(llpair, vPair); boost(l1a, vPair); boost(l2a, vPair); boost(pr1a, vPair); boost(pr2a, vPair); std::array v1p{}, v2p{}; - getBoostVector(l1a, v1p, cInvBoostFlag); - getBoostVector(l2a, v2p, cInvBoostFlag); + getBoostVector(l1a, v1p); + getBoostVector(l2a, v2p); boost(pr1a, v1p); boost(pr2a, v2p); return cosOpeningAngle(pr1a, pr2a); } - float cosThetaStarStar(std::array const& l1, - std::array const& l2, - std::array const& pr1, - std::array const& pr2) + static float deltaR(PoolTrack const& p1, PoolTrack const& p2) { - auto pr1s = pr1; - auto pr2s = pr2; - std::array v1lab{}, v2lab{}; - getBoostVector(l1, v1lab, cInvBoostFlag); - getBoostVector(l2, v2lab, cInvBoostFlag); - boost(pr1s, v1lab); - boost(pr2s, v2lab); - return cosOpeningAngle(pr1s, pr2s); + const float drap = p1.rap() - p2.rap(); + const float dphi = RecoDecay::constrainAngle(p1.phi() - p2.phi(), -PI); + return std::sqrt(drap * drap + dphi * dphi); } - template - void fillPairHistos(U const& p1, U const& p2) + template + void fillPair(PoolTrack const& p1, PoolTrack const& p2, float dR, float w) { - static constexpr std::array SubDir = { - "LaPLaM", "LaMLaP", "LaPLaP", "LaMLaM"}; + static constexpr std::array TierDir = {"SE/hPair_", "SEproc/hPair_", "ME/hPair_", + "McGen/SE/hPair_", "McGen/SEproc/hPair_", "McGen/ME/hPair_", + "QA/SharedDau/SE/hPair_", "QA/SharedDau/SEproc/hPair_"}; + static constexpr std::array PairTag = {"LaPLaM", "LaMLaP", "LaPLaP", "LaMLaM"}; - constexpr bool IsSigSB = - (part_pair == kLambdaSBAntiLambda || part_pair == kAntiLambdaSBLambda || - part_pair == kLambdaSBLambda || part_pair == kAntiLambdaSBAntiLambda); - constexpr bool IsSBSB = - (part_pair == kLambdaSBSBAntiLambda || - part_pair == kAntiLambdaSBSBLambda || part_pair == kLambdaSBSBLambda || - part_pair == kAntiLambdaSBSBAntiLambda); + std::array l1 = {p1.px(), p1.py(), p1.pz(), p1.mass()}; + std::array l2 = {p2.px(), p2.py(), p2.pz(), p2.mass()}; + std::array pr1 = {p1.prPx(), p1.prPy(), p1.prPz(), MassProton}; + std::array pr2 = {p2.prPx(), p2.prPy(), p2.prPz(), MassProton}; - constexpr int Idx = IsSigSB ? static_cast(part_pair) - 4 - : IsSBSB ? static_cast(part_pair) - 8 - : static_cast(part_pair); + const float ctheta = cosThetaStarAtlas(l1, l2, pr1, pr2); + histos.fill(HIST(TierDir[tier]) + HIST(PairTag[part_pair]), p1.mass(), p2.mass(), dR, ctheta, w); + } - float drap = p1.rap() - p2.rap(); - float dphi = RecoDecay::constrainAngle(p1.phi() - p2.phi(), -PI); - float dR = std::sqrt(drap * drap + dphi * dphi); + template + void fillLeg2Spectrum(PoolTrack const& leg, float w) + { + static constexpr std::array Dir = {"KinW/SEproc/hLeg2_", "KinW/ME/hLeg2_"}; + static constexpr std::array PairTag = {"LaPLaM", "LaMLaP", "LaPLaP", "LaMLaM"}; + histos.fill(HIST(Dir[IsME]) + HIST(PairTag[part_pair]), leg.pt(), leg.rap(), leg.phi(), w); + } - std::array l1 = {p1.px(), p1.py(), p1.pz(), MassLambda0}; - std::array l2 = {p2.px(), p2.py(), p2.pz(), MassLambda0}; - std::array pr1 = {p1.prPx(), p1.prPy(), p1.prPz(), MassProton}; - std::array pr2 = {p2.prPx(), p2.prPy(), p2.prPz(), MassProton}; + template + void fillDaughterSeparation(std::array const& a, double ma, std::array const& b, double mb, float dR) + { + static_assert(tier == kSameEvent || tier == kSameEventInMixing, "close-pair QA is filled for SE and SEproc pairs only"); + static constexpr std::array TierDir = {"QA/ClosePair/", "QA/ClosePairSEproc/"}; + static constexpr std::array ClsDir = {"LamLam/", "ALamALam/", "UnlikeSign/"}; + static constexpr std::array ComboName = {"h_pp_", "h_pipi_", "h_p1pi2_", "h_pi1p2_"}; + const float dEta = std::asinh(a[2] / std::hypot(a[0], a[1])) - std::asinh(b[2] / std::hypot(b[0], b[1])); + const float dY = RecoDecay::y(a, ma) - RecoDecay::y(b, mb); + const float dPhi = RecoDecay::constrainAngle(std::atan2(a[1], a[0]) - std::atan2(b[1], b[0]), -PI); + histos.fill(HIST(TierDir[tier]) + HIST(ClsDir[Cls]) + HIST(ComboName[Combo]) + HIST("dEta"), dR, dEta, dPhi); + histos.fill(HIST(TierDir[tier]) + HIST(ClsDir[Cls]) + HIST(ComboName[Combo]) + HIST("dY"), dR, dY, dPhi); + } - if (cDoAtlasMethod) { - float ctheta = cosThetaStarAtlas(l1, l2, pr1, pr2); - if constexpr (!IsSigSB && !IsSBSB) { - histos.fill(HIST("SE/Reco/Atlas/h2f_n2_mass_") + HIST(SubDir[Idx]), - cent, p1.mass(), p2.mass(), p1.pt(), p2.pt(), drap, dphi, - dR, ctheta); - histos.fill(HIST("SE/RecoCorr/Atlas/h2f_n2_ctheta_") + - HIST(SubDir[Idx]), - cent, drap, dphi, ctheta); - histos.fill(HIST("SE/RecoCorr/Atlas/h2f_n2_dltaR_") + HIST(SubDir[Idx]), - cent, dR, ctheta); - } else if constexpr (IsSigSB) { - histos.fill(HIST("SE/RecoBkgSigSB/Atlas/h2f_n2_mass_") + - HIST(SubDir[Idx]), - cent, p1.mass(), p2.mass(), p1.pt(), p2.pt(), drap, dphi, - dR, ctheta); - histos.fill(HIST("SE/RecoCorrBkgSigSB/Atlas/h2f_n2_ctheta_") + - HIST(SubDir[Idx]), - cent, drap, dphi, ctheta); - histos.fill(HIST("SE/RecoCorrBkgSigSB/Atlas/h2f_n2_dltaR_") + - HIST(SubDir[Idx]), - cent, dR, ctheta); - } else { - histos.fill( - HIST("SE/RecoBkgSBSB/Atlas/h2f_n2_mass_") + HIST(SubDir[Idx]), cent, - p1.mass(), p2.mass(), p1.pt(), p2.pt(), drap, dphi, dR, ctheta); - histos.fill(HIST("SE/RecoCorrBkgSBSB/Atlas/h2f_n2_ctheta_") + - HIST(SubDir[Idx]), - cent, drap, dphi, ctheta); - histos.fill(HIST("SE/RecoCorrBkgSBSB/Atlas/h2f_n2_dltaR_") + - HIST(SubDir[Idx]), - cent, dR, ctheta); - } - } - - if (cDoStarMethod) { - float ctheta = cosThetaStarStar(l1, l2, pr1, pr2); - if constexpr (!IsSigSB && !IsSBSB) { - histos.fill(HIST("SE/Reco/Star/h2f_n2_mass_") + HIST(SubDir[Idx]), cent, - p1.mass(), p2.mass(), p1.pt(), p2.pt(), drap, dphi, dR, - ctheta); - histos.fill(HIST("SE/RecoCorr/Star/h2f_n2_ctheta_") + HIST(SubDir[Idx]), - cent, drap, dphi, ctheta); - histos.fill(HIST("SE/RecoCorr/Star/h2f_n2_dltaR_") + HIST(SubDir[Idx]), - cent, dR, ctheta); - } else if constexpr (IsSigSB) { - histos.fill( - HIST("SE/RecoBkgSigSB/Star/h2f_n2_mass_") + HIST(SubDir[Idx]), cent, - p1.mass(), p2.mass(), p1.pt(), p2.pt(), drap, dphi, dR, ctheta); - histos.fill(HIST("SE/RecoCorrBkgSigSB/Star/h2f_n2_ctheta_") + - HIST(SubDir[Idx]), - cent, drap, dphi, ctheta); - histos.fill(HIST("SE/RecoCorrBkgSigSB/Star/h2f_n2_dltaR_") + - HIST(SubDir[Idx]), - cent, dR, ctheta); - } else { - histos.fill( - HIST("SE/RecoBkgSBSB/Star/h2f_n2_mass_") + HIST(SubDir[Idx]), cent, - p1.mass(), p2.mass(), p1.pt(), p2.pt(), drap, dphi, dR, ctheta); - histos.fill(HIST("SE/RecoCorrBkgSBSB/Star/h2f_n2_ctheta_") + - HIST(SubDir[Idx]), - cent, drap, dphi, ctheta); - histos.fill(HIST("SE/RecoCorrBkgSBSB/Star/h2f_n2_dltaR_") + - HIST(SubDir[Idx]), - cent, dR, ctheta); - } - } + template + void fillClosePairQA(PoolTrack const& l1, PoolTrack const& l2, float dR) + { + constexpr int Cls = part_pair == kLambdaLambda ? 0 : (part_pair == kAntiLambdaAntiLambda ? 1 : 2); + const std::array pr1 = {l1.prPx(), l1.prPy(), l1.prPz()}; + const std::array pr2 = {l2.prPx(), l2.prPy(), l2.prPz()}; + const std::array pi1 = {l1.px() - l1.prPx(), l1.py() - l1.prPy(), l1.pz() - l1.prPz()}; + const std::array pi2 = {l2.px() - l2.prPx(), l2.py() - l2.prPy(), l2.pz() - l2.prPz()}; + fillDaughterSeparation(pr1, MassProton, pr2, MassProton, dR); + fillDaughterSeparation(pi1, MassPionCharged, pi2, MassPionCharged, dR); + fillDaughterSeparation(pr1, MassProton, pi2, MassPionCharged, dR); + fillDaughterSeparation(pi1, MassPionCharged, pr2, MassProton, dR); } + bool isClosePairQAMass(float m) const + { + return cClosePairMassWindow.value <= 0.f || std::abs(m - MassLambda0) < cClosePairMassWindow.value; + } + + bool isKinematicMatch(PoolTrack const& cand, PoolTrack const& leg) const + { + return std::abs(cand.pt() - leg.pt()) < cMaxDeltaPt.value && + std::abs(cand.rap() - leg.rap()) < cMaxDeltaRap.value && + std::abs(RecoDecay::constrainAngle(cand.phi() - leg.phi(), -PI)) < cMaxDeltaPhi.value; + } + + static constexpr std::size_t NLegAxes = 3; template - void fillPairHistosWeighted(PoolTrack const& p1, PoolTrack const& p2, - float w) - { - static constexpr std::array SubDir = { - "LaPLaM", "LaMLaP", "LaPLaP", "LaMLaM"}; - - constexpr bool IsSigSB = - (part_pair == kLambdaSBAntiLambda || part_pair == kAntiLambdaSBLambda || - part_pair == kLambdaSBLambda || part_pair == kAntiLambdaSBAntiLambda); - constexpr bool IsSBSB = - (part_pair == kLambdaSBSBAntiLambda || - part_pair == kAntiLambdaSBSBLambda || part_pair == kLambdaSBSBLambda || - part_pair == kAntiLambdaSBSBAntiLambda); - - constexpr int Idx = IsSigSB ? static_cast(part_pair) - 4 - : IsSBSB ? static_cast(part_pair) - 8 - : static_cast(part_pair); - - float drap = p1.rap() - p2.rap(); - float dphi = RecoDecay::constrainAngle(p1.phi() - p2.phi(), -PI); - float dR = std::sqrt(drap * drap + dphi * dphi); - - std::array l1 = {p1.px(), p1.py(), p1.pz(), MassLambda0}; - std::array l2 = {p2.px(), p2.py(), p2.pz(), MassLambda0}; - std::array pr1 = {p1.prPx(), p1.prPy(), p1.prPz(), MassProton}; - std::array pr2 = {p2.prPx(), p2.prPy(), p2.prPz(), MassProton}; + float kinWeight(PoolTrack const& cand) const + { + THnBase const* map = kinWeightMaps[part_pair].get(); + if (!map) { + return 1.f; + } + const std::array x = {cand.pt(), cand.rap(), cand.phi()}; + std::array idx{}; + for (std::size_t i = 0; i < NLegAxes; ++i) { + TAxis const* axis = map->GetAxis(i); + idx[i] = axis->FindFixBin(x[i]); + if (idx[i] < 1 || idx[i] > axis->GetNbins()) { + return 1.f; + } + } + const int64_t bin = map->GetBin(idx.data()); + if (bin < 0) { + return 1.f; + } + const double w = map->GetBinContent(bin); + return w > 0. ? static_cast(w) : 1.f; + } - if (cDoAtlasMethod) { - float ctheta = cosThetaStarAtlas(l1, l2, pr1, pr2); - - if constexpr (!IsSigSB && !IsSBSB) { - histos.fill(HIST("ME/Reco/Atlas/h2f_n2_mass_") + HIST(SubDir[Idx]), - cent, p1.mass(), p2.mass(), p1.pt(), p2.pt(), drap, dphi, - dR, ctheta, w); - histos.fill(HIST("ME/RecoCorr/Atlas/h2f_n2_ctheta_") + - HIST(SubDir[Idx]), - cent, drap, dphi, ctheta, w); - histos.fill(HIST("ME/RecoCorr/Atlas/h2f_n2_dltaR_") + HIST(SubDir[Idx]), - cent, dR, ctheta, w); - } else if constexpr (IsSigSB) { - histos.fill(HIST("ME/RecoBkgSigSB/Atlas/h2f_n2_mass_") + - HIST(SubDir[Idx]), - cent, p1.mass(), p2.mass(), p1.pt(), p2.pt(), drap, dphi, - dR, ctheta, w); - histos.fill(HIST("ME/RecoCorrBkgSigSB/Atlas/h2f_n2_ctheta_") + - HIST(SubDir[Idx]), - cent, drap, dphi, ctheta, w); - histos.fill(HIST("ME/RecoCorrBkgSigSB/Atlas/h2f_n2_dltaR_") + - HIST(SubDir[Idx]), - cent, dR, ctheta, w); - } else { - histos.fill( - HIST("ME/RecoBkgSBSB/Atlas/h2f_n2_mass_") + HIST(SubDir[Idx]), cent, - p1.mass(), p2.mass(), p1.pt(), p2.pt(), drap, dphi, dR, ctheta, w); - histos.fill(HIST("ME/RecoCorrBkgSBSB/Atlas/h2f_n2_ctheta_") + - HIST(SubDir[Idx]), - cent, drap, dphi, ctheta, w); - histos.fill(HIST("ME/RecoCorrBkgSBSB/Atlas/h2f_n2_dltaR_") + - HIST(SubDir[Idx]), - cent, dR, ctheta, w); - } - } - - if (cDoStarMethod) { - float ctheta = cosThetaStarStar(l1, l2, pr1, pr2); - - if constexpr (!IsSigSB && !IsSBSB) { - histos.fill(HIST("ME/Reco/Star/h2f_n2_mass_") + HIST(SubDir[Idx]), cent, - p1.mass(), p2.mass(), p1.pt(), p2.pt(), drap, dphi, dR, - ctheta, w); - histos.fill(HIST("ME/RecoCorr/Star/h2f_n2_ctheta_") + HIST(SubDir[Idx]), - cent, drap, dphi, ctheta, w); - histos.fill(HIST("ME/RecoCorr/Star/h2f_n2_dltaR_") + HIST(SubDir[Idx]), - cent, dR, ctheta, w); - } else if constexpr (IsSigSB) { - histos.fill( - HIST("ME/RecoBkgSigSB/Star/h2f_n2_mass_") + HIST(SubDir[Idx]), cent, - p1.mass(), p2.mass(), p1.pt(), p2.pt(), drap, dphi, dR, ctheta, w); - histos.fill(HIST("ME/RecoCorrBkgSigSB/Star/h2f_n2_ctheta_") + - HIST(SubDir[Idx]), - cent, drap, dphi, ctheta, w); - histos.fill(HIST("ME/RecoCorrBkgSigSB/Star/h2f_n2_dltaR_") + - HIST(SubDir[Idx]), - cent, dR, ctheta, w); - } else { - histos.fill( - HIST("ME/RecoBkgSBSB/Star/h2f_n2_mass_") + HIST(SubDir[Idx]), cent, - p1.mass(), p2.mass(), p1.pt(), p2.pt(), drap, dphi, dR, ctheta, w); - histos.fill(HIST("ME/RecoCorrBkgSBSB/Star/h2f_n2_ctheta_") + - HIST(SubDir[Idx]), - cent, drap, dphi, ctheta, w); - histos.fill(HIST("ME/RecoCorrBkgSBSB/Star/h2f_n2_dltaR_") + - HIST(SubDir[Idx]), - cent, dR, ctheta, w); + static constexpr double AxisEdgeTolerance = 1e-6; + [[nodiscard]] bool hasLegAxes(THnBase const& map) const + { + if (map.GetNdimensions() != static_cast(legAxesRef.size())) { + return false; + } + for (std::size_t i = 0; i < legAxesRef.size(); ++i) { + TAxis const* axis = map.GetAxis(i); + TAxis const& ref = legAxesRef[i]; + if (axis->GetNbins() != ref.GetNbins()) { + return false; + } + for (int b = 1; b <= ref.GetNbins() + 1; ++b) { + if (std::abs(axis->GetBinLowEdge(b) - ref.GetBinLowEdge(b)) > AxisEdgeTolerance) { + return false; + } } } + return true; } - template - void analyzePairsWithMassWindow(T const& trks_1, T const& trks_2) + void loadKinWeights(uint64_t collisionTimestamp) { - for (auto const& trk_1 : trks_1) { - if (!isInclusive(trk_1.mass())) { - continue; + static constexpr std::array PairTag = {"LaPLaM", "LaMLaP", "LaPLaP", "LaMLaM"}; + const int64_t ts = cKinWeightCcdbTimestamp.value >= 0 ? cKinWeightCcdbTimestamp.value : static_cast(collisionTimestamp); + auto* list = ccdb->getForTimeStamp(cKinWeightCcdbPath.value, ts); + if (!list) { + LOGP(fatal, "Kinematic weights: no object at {} for timestamp {}", cKinWeightCcdbPath.value, ts); + return; + } + auto const* guard = dynamic_cast(list->FindObject("kinWeightGuardStatus")); + if (guard != nullptr && !TString(guard->GetTitle()).BeginsWith("OK")) { + LOGF(fatal, "Kinematic weights at %s are flagged by their builder: %s", cKinWeightCcdbPath.value.c_str(), guard->GetTitle()); + return; + } + for (std::size_t i = 0; i < PairTag.size(); ++i) { + const std::string name = "hKinW_" + std::string(PairTag[i]); + auto* map = dynamic_cast(list->FindObject(name.c_str())); + if (!map || !hasLegAxes(*map)) { + LOGF(fatal, "Kinematic weights: %s is missing, or its axes differ from KinW/*/hLeg2_%s (pT, y, phi) of this configuration", name.c_str(), std::string(PairTag[i]).c_str()); + return; } - bool t1sig = isSignal(trk_1.mass()); - bool t1sb = isSideband(trk_1.mass()); + kinWeightMaps[i].reset(dynamic_cast(map->Clone())); + } + auto const* form = dynamic_cast(list->FindObject("kinWeightForm")); + kinWeightsLoaded = true; + LOGF(info, "Kinematic weights loaded from %s (form: %s)", cKinWeightCcdbPath.value.c_str(), form ? form->GetTitle() : "not stated"); + } - for (auto const& trk_2 : trks_2) { - if constexpr (samelambda) { - if (trk_1.index() == trk_2.index()) { + static uint64_t splitMix(uint64_t x) + { + x += 0x9E3779B97F4A7C15ULL; + x = (x ^ (x >> 30)) * 0xBF58476D1CE4E5B9ULL; + x = (x ^ (x >> 27)) * 0x94D049BB133111EBULL; + return x ^ (x >> 31); + } + + template + void setPairOrderSeed(C const& col) + { + const uint64_t vz = std::bit_cast(col.posZ()); + pairOrderSeed = splitMix(((vz << 32) | static_cast(col.globalIndex())) ^ (col.timeStamp() * 0xD6E8FEB86659FD93ULL)); + } + + template + bool isCountedOrder(T const& trk_1, T const& trk_2) const + { + const int64_t g1 = trk_1.globalIndex(), g2 = trk_2.globalIndex(); + if (g1 == g2) { + return false; + } + const bool firstIsLo = g1 < g2; + const uint64_t lo = static_cast(firstIsLo ? g1 : g2); + const uint64_t hi = static_cast(firstIsLo ? g2 : g1); + const bool swapped = splitMix(pairOrderSeed ^ ((lo << 32) | hi)) & 1ULL; + return firstIsLo != swapped; + } + + template + static bool sharesDaughter(T1 const& a, T2 const& b) + { + return a.posTrackId() == b.posTrackId() || a.negTrackId() == b.negTrackId(); + } + + [[nodiscard]] bool sharesDaughterCand(std::size_t a, std::size_t b) const + { + return a != b && (sdCands[a].posId == sdCands[b].posId || sdCands[a].negId == sdCands[b].negId); + } + + [[nodiscard]] bool isDropped(int64_t globalIdx) const + { + return std::binary_search(sdDropped.begin(), sdDropped.end(), globalIdx); + } + + [[nodiscard]] bool isConflicted(int64_t globalIdx) const + { + return std::binary_search(sdConflicted.begin(), sdConflicted.end(), globalIdx); + } + + template + [[nodiscard]] bool isKept(T const& trk) const + { + return !Reco || !isDropped(trk.globalIndex()); + } + + template + [[nodiscard]] bool isUsable(T const& trk) const + { + return isAcceptedMass(trk.mass()) && isKept(trk); + } + + template + [[nodiscard]] bool hasMinCandidates(T const& lTrks, T const& alTrks) const + { + return std::cmp_greater_equal(lTrks.size() + alTrks.size(), cMinCandPerEvent.value); + } + + void selectLargestFreeSet(std::vector const& members, std::vector& keep) const + { + const std::size_t s = members.size(); + keep.assign(s, 0); + if (s <= MaxExactCluster) { + std::array conf{}; + for (std::size_t a = 0; a < s; ++a) { + for (std::size_t b = 0; b < s; ++b) { + if (sharesDaughterCand(members[a], members[b])) { + conf[a] |= static_cast(1) << b; + } + } + } + uint32_t best = 0; + int bestN = -1; + float bestScore = 0.f; + const uint32_t nMasks = static_cast(1) << s; + for (uint32_t mask = 1; mask < nMasks; ++mask) { + bool isFree = true; + int nIn = 0; + float score = 0.f; + for (std::size_t a = 0; a < s && isFree; ++a) { + if (((mask >> a) & 1U) == 0U) { continue; } + if ((conf[a] & mask) != 0U) { + isFree = false; + continue; + } + ++nIn; + score += sdCands[members[a]].dMass; } - if (!isInclusive(trk_2.mass())) { - continue; + if (isFree && (nIn > bestN || (nIn == bestN && score < bestScore))) { + best = mask; + bestN = nIn; + bestScore = score; + } + } + for (std::size_t a = 0; a < s; ++a) { + keep[a] = static_cast((best >> a) & 1U); + } + return; + } + std::vector order(s); + for (std::size_t a = 0; a < s; ++a) { + order[a] = a; + } + std::sort(order.begin(), order.end(), [this, &members](std::size_t x, std::size_t y) { + SharedDauCand const& cx = sdCands[members[x]]; + SharedDauCand const& cy = sdCands[members[y]]; + return cx.dMass < cy.dMass || (cx.dMass == cy.dMass && cx.globalIdx < cy.globalIdx); + }); + for (auto const& a : order) { + bool isFree = true; + for (std::size_t b = 0; b < s && isFree; ++b) { + if (keep[b] != 0 && sharesDaughterCand(members[a], members[b])) { + isFree = false; } - bool t2sig = isSignal(trk_2.mass()); - bool t2sb = isSideband(trk_2.mass()); + } + if (isFree) { + keep[a] = 1; + } + } + } - fillPairHistos(trk_1, trk_2); - if ((t1sig && t2sb) || (t1sb && t2sig)) { - fillPairHistos(trk_1, trk_2); + template + void resolveSharedDaughters(T const& lTrks, T const& alTrks, bool fillQA) + { + sdCands.clear(); + sdConflicted.clear(); + sdDropped.clear(); + auto collect = [this](auto const& trks) { + for (auto const& trk : trks) { + if (trk.lambdaSharingDaughter()) { + sdCands.push_back(SharedDauCand{trk.globalIndex(), trk.posTrackId(), trk.negTrackId(), static_cast(std::abs(trk.mass() - MassLambda0))}); } - if (t1sb && t2sb) { - fillPairHistos(trk_1, trk_2); + } + }; + collect(lTrks); + collect(alTrks); + const std::size_t n = sdCands.size(); + if (n == 0) { + return; + } + std::sort(sdCands.begin(), sdCands.end(), [](SharedDauCand const& a, SharedDauCand const& b) { return a.globalIdx < b.globalIdx; }); + std::vector cluster(n, -1); + std::vector members; + std::vector stack; + std::vector keep; + int nClusters = 0; + for (std::size_t seed = 0; seed < n; ++seed) { + if (cluster[seed] >= 0) { + continue; + } + members.clear(); + stack.assign(1, seed); + cluster[seed] = nClusters; + while (!stack.empty()) { + const std::size_t a = stack.back(); + stack.pop_back(); + members.push_back(a); + for (std::size_t b = 0; b < n; ++b) { + if (cluster[b] < 0 && sharesDaughterCand(a, b)) { + cluster[b] = nClusters; + stack.push_back(b); + } + } + } + ++nClusters; + std::sort(members.begin(), members.end()); + if (fillQA && cFillSharedDauQA) { + histos.fill(HIST("QA/SharedDau/hClusterSize"), members.size()); + } + if (members.size() < NCandidatesForPair) { + continue; + } + for (auto const& a : members) { + sdConflicted.push_back(sdCands[a].globalIdx); + } + if (cSharedDauRule.value == kSharedDauPairVeto) { + continue; + } + if (cSharedDauRule.value == kSharedDauDropAll) { + keep.assign(members.size(), 0); + } else { + selectLargestFreeSet(members, keep); + } + for (std::size_t a = 0; a < members.size(); ++a) { + if (keep[a] == 0) { + sdDropped.push_back(sdCands[members[a]].globalIdx); } } } + std::sort(sdConflicted.begin(), sdConflicted.end()); + std::sort(sdDropped.begin(), sdDropped.end()); } - static constexpr int MEModeStandard = 0; - static constexpr int MEModeKinematic = 1; - - template - void analyzePairsME(T const& trks_1, T const& trks_2) + template + void analyzePairsSE(T const& trks_1, T const& trks_2) { - + constexpr bool Reco = tier == kSameEvent; for (auto const& trk_1 : trks_1) { - if (!isInclusive(trk_1.mass())) { + if (!isUsable(trk_1)) { continue; } - const bool t1sig = isSignal(trk_1.mass()); - const bool t1sb = isSideband(trk_1.mass()); - PoolTrack p1 = toPoolTrack(trk_1); + const PoolTrack p1 = toPoolTrack(trk_1); for (auto const& trk_2 : trks_2) { - if constexpr (samelambda) { - if (trk_1.index() == trk_2.index()) { - continue; - } - } - if (!isInclusive(trk_2.mass())) { + if (!isCountedOrder(trk_1, trk_2) || !isUsable(trk_2)) { continue; } - const bool t2sig = isSignal(trk_2.mass()); - const bool t2sb = isSideband(trk_2.mass()); - PoolTrack p2 = toPoolTrack(trk_2); - - fillPairHistosWeighted(p1, p2, 1.0f); - - if ((t1sig && t2sb) || (t1sb && t2sig)) { - fillPairHistosWeighted(p1, p2, 1.0f); + if constexpr (Reco) { + if (sharesDaughter(trk_1, trk_2)) { + continue; + } } - - if (t1sb && t2sb) { - fillPairHistosWeighted(p1, p2, 1.0f); + const PoolTrack p2 = toPoolTrack(trk_2); + const float dR = deltaR(p1, p2); + fillPair(p1, p2, dR, 1.f); + if constexpr (Reco) { + if (cFillClosePairQA && isClosePairQAMass(p1.mass()) && isClosePairQAMass(p2.mass())) { + fillClosePairQA(p1, p2, dR); + } + if (cFillSharedDauQA && (isConflicted(trk_1.globalIndex()) || isConflicted(trk_2.globalIndex()))) { + fillPair(p1, p2, dR, 1.f); + } } } } } - template - void analyzePairsMEKinematic(T const& se_trks_1, T const& se_trks_2, - T const& me_pool_1, T const& me_pool_2) + template + void fillEventQA(float centrality, T const& lTrks, T const& alTrks) { - std::vector meVec1, meVec2; - meVec1.reserve(me_pool_1.size()); - meVec2.reserve(me_pool_2.size()); - for (auto const& me : me_pool_1) { - if (isInclusive(me.mass())) { - meVec1.push_back(toPoolTrack(me)); + static constexpr std::array EvDir = {"Events/", "McGen/Events/"}; + std::size_t nLambda = 0, nAntiLambda = 0; + for (auto const& trk : lTrks) { + if (isUsable(trk)) { + ++nLambda; } } - for (auto const& me : me_pool_2) { - if (isInclusive(me.mass())) { - meVec2.push_back(toPoolTrack(me)); + for (auto const& trk : alTrks) { + if (isUsable(trk)) { + ++nAntiLambda; } } - - if (meVec1.empty() && meVec2.empty()) { + histos.fill(HIST(EvDir[Gen]) + HIST("hEventCount"), kPeAll); + histos.fill(HIST(EvDir[Gen]) + HIST("hNCandAccepted"), nLambda, nAntiLambda); + if (nLambda + nAntiLambda >= 1) { + histos.fill(HIST(EvDir[Gen]) + HIST("hEventCount"), kPeOneCand); + } + if (nLambda + nAntiLambda < NCandidatesForPair) { return; } + histos.fill(HIST(EvDir[Gen]) + HIST("hEventCount"), kPeTwoCand); + histos.fill(HIST(EvDir[Gen]) + HIST("hCentPairEvents"), centrality); + if (nLambda >= 1 && nAntiLambda >= 1) { + histos.fill(HIST(EvDir[Gen]) + HIST("hEventCount"), kPeUnlikeSign); + } + if (nLambda >= NCandidatesForPair) { + histos.fill(HIST(EvDir[Gen]) + HIST("hEventCount"), kPeLambdaLambda); + } + if (nAntiLambda >= NCandidatesForPair) { + histos.fill(HIST(EvDir[Gen]) + HIST("hEventCount"), kPeAntiLambdaAntiLambda); + } + for (auto const& trk : lTrks) { + if (isUsable(trk)) { + fillPairCandQA(trk); + } + } + for (auto const& trk : alTrks) { + if (isUsable(trk)) { + fillPairCandQA(trk); + } + } + } + template + void fillPairCandQA(T const& trk) + { + static constexpr std::array CandDir = {"QA/PairCand/", "McGen/QA/PairCand/"}; + static constexpr std::array Species = {"Lambda/", "AntiLambda/"}; + histos.fill(HIST(CandDir[Gen]) + HIST(Species[part]) + HIST("hMassVsPt"), trk.mass(), trk.pt()); + histos.fill(HIST(CandDir[Gen]) + HIST(Species[part]) + HIST("hRapVsPhi"), trk.rap(), trk.phi()); + } + + [[nodiscard]] int poolBin(float centrality, float vz) const + { + const int iCent = mePoolAxisCent.FindFixBin(centrality); + const int iVz = mePoolAxisVz.FindFixBin(vz); + if (iCent < 1 || iCent > mePoolAxisCent.GetNbins() || iVz < 1 || iVz > mePoolAxisVz.GetNbins()) { + return -1; + } + return (iCent - 1) * mePoolAxisVz.GetNbins() + (iVz - 1); + } + + template + void analyzePairsMEKinematic(T const& se_trks_1, T const& se_trks_2, RollingPool const& pool) + { + static constexpr std::array MixQaDir = {"QA/ME/", "McGen/QA/ME/"}; + constexpr PairTier SeTier = Gen ? kMcGenSameEventInMixing : kSameEventInMixing; + constexpr PairTier MeTier = Gen ? kMcGenMixedEvent : kMixedEvent; + std::vector matches; for (auto const& trk1 : se_trks_1) { - if (!isInclusive(trk1.mass())) { + if (!isUsable(trk1)) { continue; } - const bool se1sig = isSignal(trk1.mass()); - const bool se1sb = isSideband(trk1.mass()); - PoolTrack p1 = toPoolTrack(trk1); + const PoolTrack p1 = toPoolTrack(trk1); for (auto const& trk2 : se_trks_2) { - if constexpr (samelambda) { - if (trk1.index() == trk2.index()) { + if (!isCountedOrder(trk1, trk2) || !isUsable(trk2)) { + continue; + } + if constexpr (!Gen) { + if (sharesDaughter(trk1, trk2)) { continue; } } - if (!isInclusive(trk2.mass())) { - continue; + const PoolTrack p2 = toPoolTrack(trk2); + const float dRSeed = deltaR(p1, p2); + + if (cFillSEInMixing) { + fillPair(p1, p2, dRSeed, 1.f); } - const bool se2sig = isSignal(trk2.mass()); - const bool se2sb = isSideband(trk2.mass()); - PoolTrack p2 = toPoolTrack(trk2); - - { - std::vector matchA; - for (auto const& meP : meVec2) { - if (std::abs(meP.pt() - p2.pt()) < cMaxDeltaPt && - std::abs(meP.rap() - p2.rap()) < cMaxDeltaRap && - std::abs(RecoDecay::constrainAngle(meP.phi() - p2.phi(), -PI)) < - cMaxDeltaPhi) { - matchA.push_back(meP); - } + if constexpr (!Gen) { + if (cFillClosePairQA && isClosePairQAMass(p1.mass()) && isClosePairQAMass(p2.mass())) { + fillClosePairQA(p1, p2, dRSeed); } - if (!matchA.empty()) { - const float wA = 1.0f / static_cast(matchA.size()); - for (auto const& meP2 : matchA) { - const bool me2sig = isSignal(meP2.mass()); - const bool me2sb = isSideband(meP2.mass()); - - fillPairHistosWeighted(p1, meP2, wA); - - if ((se1sig && me2sb) || (se1sb && me2sig)) { - fillPairHistosWeighted(p1, meP2, wA); - } - - if (se1sb && me2sb) { - fillPairHistosWeighted(p1, meP2, wA); - } - } + if (cFillSharedDauQA && (isConflicted(trk1.globalIndex()) || isConflicted(trk2.globalIndex()))) { + fillPair(p1, p2, dRSeed, 1.f); + } + if (cFillKinWSpectra) { + fillLeg2Spectrum(p2, 1.f); } } - { - std::vector matchB; - for (auto const& meP : meVec1) { - if (std::abs(meP.pt() - p1.pt()) < cMaxDeltaPt && - std::abs(meP.rap() - p1.rap()) < cMaxDeltaRap && - std::abs(RecoDecay::constrainAngle(meP.phi() - p1.phi(), -PI)) < - cMaxDeltaPhi) { - matchB.push_back(meP); + pool.findMatches(p2, [this, &p2](PoolTrack const& cand) { return isKinematicMatch(cand, p2); }, matches); + histos.fill(HIST(MixQaDir[Gen]) + HIST("hMatchesLeg2"), matches.size(), part_pair); + for (auto const& meP2 : matches) { + float kinW = 1.f; + if constexpr (!Gen) { + kinW = kinWeight(*meP2); + } + const float w = kinW / static_cast(matches.size()); + const float dRMixed = deltaR(p1, *meP2); + fillPair(p1, *meP2, dRMixed, w); + histos.fill(HIST(MixQaDir[Gen]) + HIST("hDeltaRShift"), dRSeed, dRMixed - dRSeed, w); + if constexpr (!Gen) { + if (cFillKinWSpectra) { + fillLeg2Spectrum(*meP2, w); } } - if (!matchB.empty()) { - const float wB = 1.0f / static_cast(matchB.size()); - for (auto const& meP1 : matchB) { - const bool me1sig = isSignal(meP1.mass()); - const bool me1sb = isSideband(meP1.mass()); + } + } + } + } - fillPairHistosWeighted(meP1, p2, wB); + template + void mixCollision(C const& col, T const& lTrks, T const& alTrks, std::vector& pools, std::vector& eventCands) + { + static constexpr std::array MixQaDir = {"QA/ME/", "McGen/QA/ME/"}; + histos.fill(HIST(MixQaDir[Gen]) + HIST("hEventStatus"), kMEEventSeen); + const int bin = poolBin(col.cent(), col.posZ()); + if (bin < 0) { + histos.fill(HIST(MixQaDir[Gen]) + HIST("hEventStatus"), kMEEventOutsidePools); + return; + } + std::size_t nLambda = 0, nAntiLambda = 0; + for (auto const& trk : lTrks) { + if (isKept(trk)) { + ++nLambda; + } + } + for (auto const& trk : alTrks) { + if (isKept(trk)) { + ++nAntiLambda; + } + } + histos.fill(HIST(MixQaDir[Gen]) + HIST("hLambdaMultVsCent"), col.cent(), nLambda); + histos.fill(HIST(MixQaDir[Gen]) + HIST("hAntiLambdaMultVsCent"), col.cent(), nAntiLambda); - if ((me1sig && se2sb) || (me1sb && se2sig)) { - fillPairHistosWeighted(meP1, p2, wB); - } + eventCands.clear(); + for (auto const& trk : lTrks) { + if (isUsable(trk)) { + eventCands.push_back(toPoolTrack(trk)); + } + } + for (auto const& trk : alTrks) { + if (isUsable(trk)) { + eventCands.push_back(toPoolTrack(trk)); + } + } - if (me1sb && se2sb) { - fillPairHistosWeighted(meP1, p2, wB); - } - } + RollingPool& pool = pools[bin]; + if (eventCands.size() >= NCandidatesForPair) { + if (static_cast(pool.nEvents()) >= cMEPoolMinEvents.value) { + if constexpr (!Gen) { + if (cApplyKinWeights && !kinWeightsLoaded) { + loadKinWeights(col.timeStamp()); } } + setPairOrderSeed(col); + histos.fill(HIST(MixQaDir[Gen]) + HIST("hEventStatus"), kMEEventMixed); + histos.fill(HIST(MixQaDir[Gen]) + HIST("hMixedCentVz"), col.cent(), col.posZ()); + histos.fill(HIST(MixQaDir[Gen]) + HIST("hPoolEventsAtMixing"), pool.nEvents()); + analyzePairsMEKinematic(lTrks, alTrks, pool); + analyzePairsMEKinematic(alTrks, lTrks, pool); + analyzePairsMEKinematic(lTrks, lTrks, pool); + analyzePairsMEKinematic(alTrks, alTrks, pool); + } else { + histos.fill(HIST(MixQaDir[Gen]) + HIST("hEventStatus"), kMEEventWarmUp); + } + } + if (static_cast(eventCands.size()) >= cMEPoolMinCand.value) { + pool.push(eventCands); + histos.fill(HIST(MixQaDir[Gen]) + HIST("hEventStatus"), kMEEventDonated); + } + } - } // trk2 - } // trk1 - } // analyzePairsMEKinematic - - // ========================================================================= - // O2 framework declarations - // ========================================================================= using LambdaCollisions = aod::LambdaCollisions; using LambdaMcGenCollisions = aod::LambdaMcGenCollisions; using LambdaTracks = soa::Join; - Preslice perCollisionLambda = - aod::lambdatrack::lambdaCollisionId; + Preslice perCollisionLambda = aod::lambdatrack::lambdaCollisionId; SliceCache cache; - Partition partLambdaTracks = - (aod::lambdatrack::v0Type == (int8_t)kLambda) && - (aod::lambdatrackext::trueLambdaFlag == true) && - (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); + Partition partLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kLambda) && (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); - Partition partAntiLambdaTracks = - (aod::lambdatrack::v0Type == (int8_t)kAntiLambda) && - (aod::lambdatrackext::trueLambdaFlag == true) && - (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); + Partition partAntiLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kAntiLambda) && (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); SliceCache cachemc; - Partition partMcLambdaTracks = - (aod::lambdatrack::v0Type == (int8_t)kLambda) && - (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); + Partition partMcLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kLambda) && (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); - Partition partMcAntiLambdaTracks = - (aod::lambdatrack::v0Type == (int8_t)kAntiLambda) && - (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); + Partition partMcAntiLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kAntiLambda) && (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); void processDummy(LambdaCollisions::iterator const&) {} PROCESS_SWITCH(LambdaSpinPolarization, processDummy, "Dummy", false); - void processDataReco(LambdaCollisions::iterator const& collision, - LambdaTracks const&) + void processDataReco(LambdaCollisions::iterator const& collision, LambdaTracks const&) { - cent = collision.cent(); - auto lTrks = partLambdaTracks->sliceByCached( - aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); - auto alTrks = partAntiLambdaTracks->sliceByCached( - aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); - - analyzePairsWithMassWindow(lTrks, alTrks); - analyzePairsWithMassWindow(alTrks, lTrks); - analyzePairsWithMassWindow(lTrks, lTrks); - analyzePairsWithMassWindow(alTrks, alTrks); - } - PROCESS_SWITCH(LambdaSpinPolarization, processDataReco, - "SE only (data/MCReco)", false); - - struct GetMultiplicity { - float operator()(auto const& col) const { return col.cent(); } - }; - using MixedBinning = - FlexibleBinningPolicy, - o2::aod::collision::PosZ, GetMultiplicity>; - MixedBinning binningOnVtxAndMult{ - {GetMultiplicity{}}, - {axisVtxZME, axisCentME}, - true}; - - void processDataRecoMixed(LambdaCollisions const& col, LambdaTracks const&) - { - for (auto const& [col1, col2] : soa::selfCombinations( - binningOnVtxAndMult, mixingParameter, -1, col, col)) { - if (col1.globalIndex() == col2.globalIndex()) { + setPairOrderSeed(collision); + auto lTrks = partLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); + auto alTrks = partAntiLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); + resolveSharedDaughters(lTrks, alTrks, !doprocessDataRecoMixed); + if (!doprocessDataRecoMixed) { + fillEventQA(collision.cent(), lTrks, alTrks); + } + if (!hasMinCandidates(lTrks, alTrks)) { + return; + } + + analyzePairsSE(lTrks, alTrks); + analyzePairsSE(alTrks, lTrks); + analyzePairsSE(lTrks, lTrks); + analyzePairsSE(alTrks, alTrks); + } + PROCESS_SWITCH(LambdaSpinPolarization, processDataReco, "SE only (data/MCReco)", false); + + void processDataRecoMixed(LambdaCollisions const& cols, LambdaTracks const&) + { + std::vector eventCands; + for (auto const& col : cols) { + auto lTrks = partLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, col.globalIndex(), cache); + auto alTrks = partAntiLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, col.globalIndex(), cache); + resolveSharedDaughters(lTrks, alTrks, true); + fillEventQA(col.cent(), lTrks, alTrks); + if (!hasMinCandidates(lTrks, alTrks)) { continue; } - cent = col1.cent(); - histos.fill(HIST("QA/ME/hPoolCentVz"), col1.cent(), col1.posZ()); - - auto lTrks1 = partLambdaTracks->sliceByCached( - aod::lambdatrack::lambdaCollisionId, col1.globalIndex(), cache); - auto lTrks2 = partLambdaTracks->sliceByCached( - aod::lambdatrack::lambdaCollisionId, col2.globalIndex(), cache); - auto alTrks1 = partAntiLambdaTracks->sliceByCached( - aod::lambdatrack::lambdaCollisionId, col1.globalIndex(), cache); - auto alTrks2 = partAntiLambdaTracks->sliceByCached( - aod::lambdatrack::lambdaCollisionId, col2.globalIndex(), cache); - - histos.fill(HIST("QA/ME/hLambdaMultVsCent"), col1.cent(), lTrks1.size()); - histos.fill(HIST("QA/ME/hAntiLambdaMultVsCent"), col1.cent(), - alTrks1.size()); - - if (cMEMode == MEModeStandard) { - analyzePairsME(lTrks1, alTrks2); - analyzePairsME(alTrks1, lTrks2); - analyzePairsME(lTrks1, lTrks2); - analyzePairsME(alTrks1, alTrks2); - - } else if (cMEMode == MEModeKinematic) { - analyzePairsMEKinematic(lTrks1, alTrks1, - lTrks2, alTrks2); - analyzePairsMEKinematic(alTrks1, lTrks1, - alTrks2, lTrks2); - analyzePairsMEKinematic(lTrks1, lTrks1, lTrks2, - lTrks2); - analyzePairsMEKinematic( - alTrks1, alTrks1, alTrks2, alTrks2); - } - } - } - PROCESS_SWITCH(LambdaSpinPolarization, processDataRecoMixed, - "ME (modes 0=standard, 1=kinematicConstrained)", false); - - void processDataRecoMixEvent(LambdaCollisions::iterator const& collision, - LambdaTracks const&) - { - auto lTrks = partLambdaTracks->sliceByCached( - aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); - auto alTrks = partAntiLambdaTracks->sliceByCached( - aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); - - if (lTrks.size() == 0 && alTrks.size() == 0) { + mixCollision(col, lTrks, alTrks, mePools, eventCands); + } + } + PROCESS_SWITCH(LambdaSpinPolarization, processDataRecoMixed, "ME: kinematic mixing against rolling pools that cross DataFrames", false); + + void processMcGen(LambdaMcGenCollisions::iterator const& collision, aod::LambdaMcGenTracks const&) + { + setPairOrderSeed(collision); + auto lTrks = partMcLambdaTracks->sliceByCached(aod::lambdamcgentrack::lambdaMcGenCollisionId, collision.globalIndex(), cachemc); + auto alTrks = partMcAntiLambdaTracks->sliceByCached(aod::lambdamcgentrack::lambdaMcGenCollisionId, collision.globalIndex(), cachemc); + if (!doprocessMcGenMixed) { + fillEventQA(collision.cent(), lTrks, alTrks); + } + if (!hasMinCandidates(lTrks, alTrks)) { return; } - lambdaMixEvtCol(collision.index(), collision.cent(), collision.posZ(), - collision.timeStamp()); + analyzePairsSE(lTrks, alTrks); + analyzePairsSE(alTrks, lTrks); + analyzePairsSE(lTrks, lTrks); + analyzePairsSE(alTrks, alTrks); + } + PROCESS_SWITCH(LambdaSpinPolarization, processMcGen, "MC generated: SE pairs (McGen/SE), as processDataReco", false); - for (auto const& track : lTrks) { + void processMcGenMixed(LambdaMcGenCollisions const& cols, aod::LambdaMcGenTracks const&) + { + std::vector eventCands; + for (auto const& col : cols) { + auto lTrks = partMcLambdaTracks->sliceByCached(aod::lambdamcgentrack::lambdaMcGenCollisionId, col.globalIndex(), cachemc); + auto alTrks = partMcAntiLambdaTracks->sliceByCached(aod::lambdamcgentrack::lambdaMcGenCollisionId, col.globalIndex(), cachemc); + fillEventQA(col.cent(), lTrks, alTrks); + if (!hasMinCandidates(lTrks, alTrks)) { + continue; + } + mixCollision(col, lTrks, alTrks, mePoolsGen, eventCands); + } + } + PROCESS_SWITCH(LambdaSpinPolarization, processMcGenMixed, "MC generated: kinematic mixing against rolling pools, as processDataRecoMixed", false); + + void processDataRecoMixEvent(LambdaCollisions::iterator const& collision, LambdaTracks const&) + { + auto lTrks = partLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); + auto alTrks = partAntiLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); + + if (!hasMinCandidates(lTrks, alTrks)) { + return; + } + + lambdaMixEvtCol(collision.index(), collision.cent(), collision.posZ(), collision.timeStamp()); + + auto fillMixEventTrack = [&](auto const& track) { + const bool shared = track.lambdaSharingDaughter(); lambdaMixEvtTrk(collision.index(), track.globalIndex(), track.px(), track.py(), track.pz(), track.mass(), track.prPx(), track.prPy(), track.prPz(), track.v0Type(), - collision.timeStamp()); + collision.timeStamp(), + shared ? static_cast(track.posTrackId()) : NoSharedTrackIdx, + shared ? static_cast(track.negTrackId()) : NoSharedTrackIdx); + }; + for (auto const& track : lTrks) { + fillMixEventTrack(track); } for (auto const& track : alTrks) { - lambdaMixEvtTrk(collision.index(), track.globalIndex(), track.px(), - track.py(), track.pz(), track.mass(), track.prPx(), - track.prPy(), track.prPz(), track.v0Type(), - collision.timeStamp()); + fillMixEventTrack(track); } } - PROCESS_SWITCH(LambdaSpinPolarization, processDataRecoMixEvent, - "Mix-event table filling", false); + PROCESS_SWITCH(LambdaSpinPolarization, processDataRecoMixEvent, "Mix-event table filling", false); - void processMcGenMixEvent(LambdaMcGenCollisions::iterator const& collision, - aod::LambdaMcGenTracks const&) + void processMcGenMixEvent(LambdaMcGenCollisions::iterator const& collision, aod::LambdaMcGenTracks const&) { - auto lTrks = partMcLambdaTracks->sliceByCached( - aod::lambdamcgentrack::lambdaMcGenCollisionId, collision.globalIndex(), - cachemc); - auto alTrks = partMcAntiLambdaTracks->sliceByCached( - aod::lambdamcgentrack::lambdaMcGenCollisionId, collision.globalIndex(), - cachemc); + auto lTrks = partMcLambdaTracks->sliceByCached(aod::lambdamcgentrack::lambdaMcGenCollisionId, collision.globalIndex(), cachemc); + auto alTrks = partMcAntiLambdaTracks->sliceByCached(aod::lambdamcgentrack::lambdaMcGenCollisionId, collision.globalIndex(), cachemc); - if (lTrks.size() == 0 && alTrks.size() == 0) { + if (!hasMinCandidates(lTrks, alTrks)) { return; } @@ -2499,8 +2662,7 @@ struct LambdaSpinPolarization { track.v0Type(), collision.timeStamp()); } } - PROCESS_SWITCH(LambdaSpinPolarization, processMcGenMixEvent, - "Mix-event McGen table filling", false); + PROCESS_SWITCH(LambdaSpinPolarization, processMcGenMixEvent, "Mix-event McGen table filling", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { diff --git a/PWGCF/TwoParticleCorrelations/Tasks/longRangeDihadronCor.cxx b/PWGCF/TwoParticleCorrelations/Tasks/longRangeDihadronCor.cxx index 5baea7d44b4..8f66d3bfb27 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/longRangeDihadronCor.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/longRangeDihadronCor.cxx @@ -1592,7 +1592,7 @@ struct LongRangeDihadronCor { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1746,7 +1746,7 @@ struct LongRangeDihadronCor { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1857,7 +1857,7 @@ struct LongRangeDihadronCor { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -2098,7 +2098,7 @@ struct LongRangeDihadronCor { using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -2168,7 +2168,7 @@ struct LongRangeDihadronCor { using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -2367,7 +2367,7 @@ struct LongRangeDihadronCor { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -2443,7 +2443,7 @@ struct LongRangeDihadronCor { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip diff --git a/PWGCF/TwoParticleCorrelations/Tasks/longrangeCorrelation.cxx b/PWGCF/TwoParticleCorrelations/Tasks/longrangeCorrelation.cxx index ccb73d62d14..40c09df3d8e 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/longrangeCorrelation.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/longrangeCorrelation.cxx @@ -867,7 +867,7 @@ struct LongrangeCorrelation { }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxZME, axisMultME}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxZME, axisMultME}}; for (auto const& [col1, col2] : soa::selfCombinations(binningOnVtxAndMult, mixingParameter, -1, col, col)) { if (!isEventSelected(col1) || !isEventSelected(col2)) { continue; @@ -902,7 +902,7 @@ struct LongrangeCorrelation { }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxZME, axisMultME}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxZME, axisMultME}}; for (auto const& [col1, col2] : soa::selfCombinations(binningOnVtxAndMult, mixingParameter, -1, col, col)) { if (!isEventSelected(col1) || !isEventSelected(col2)) { continue; @@ -937,7 +937,7 @@ struct LongrangeCorrelation { }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxZME, axisMultME}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxZME, axisMultME}}; auto tracksTuple = std::make_tuple(tracks, mfttracks); Pair pairs{binningOnVtxAndMult, mixingParameter, -1, col, tracksTuple, &cache}; for (auto const& [col1, tracks1, col2, tracks2] : pairs) { @@ -967,7 +967,7 @@ struct LongrangeCorrelation { }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxZME, axisMultME}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxZME, axisMultME}}; for (auto const& [col1, col2] : soa::selfCombinations(binningOnVtxAndMult, mixingParameter, -1, col, col)) { if (!isEventSelected(col1) || !isEventSelected(col2)) { continue; @@ -1002,7 +1002,7 @@ struct LongrangeCorrelation { }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxZME, axisMultME}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxZME, axisMultME}}; for (auto const& [col1, col2] : soa::selfCombinations(binningOnVtxAndMult, mixingParameter, -1, col, col)) { if (!isEventSelected(col1) || !isEventSelected(col2)) { continue; diff --git a/PWGCF/TwoParticleCorrelations/Tasks/longrangecorrDerived.cxx b/PWGCF/TwoParticleCorrelations/Tasks/longrangecorrDerived.cxx index 54014f8f0e7..22eeaebfdf6 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/longrangecorrDerived.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/longrangecorrDerived.cxx @@ -17,6 +17,7 @@ #include "PWGCF/Core/CorrelationContainer.h" #include "PWGCF/TwoParticleCorrelations/DataModel/LongRangeDerived.h" +#include "PWGLF/DataModel/LFStrangenessTables.h" #include "PWGUD/Core/SGSelector.h" #include "Common/CCDB/EventSelectionParams.h" @@ -31,6 +32,7 @@ #include #include +#include #include #include #include @@ -91,6 +93,8 @@ struct LongrangecorrDerived { Configurable cfgEtaCut{"cfgEtaCut", 0.8f, "Eta range to consider"}; Configurable cfgPtCutMin{"cfgPtCutMin", 0.2f, "minimum accepted track pT"}; Configurable cfgPtCutMax{"cfgPtCutMax", 10.0f, "maximum accepted track pT"}; + Configurable cfgPtCutMinForMult{"cfgPtCutMinForMult", 0.2f, "minimum track pT for fallback multiplicity classification"}; + Configurable cfgPtCutMaxForMult{"cfgPtCutMaxForMult", 3.0f, "maximum track pT for fallback multiplicity classification"}; Configurable cfgTpcMinNclsFound{"cfgTpcMinNclsFound", 50.0f, ""}; Configurable cfgTpcMinNCrossedRows{"cfgTpcMinNCrossedRows", 70.0f, ""}; Configurable cfgTpcMaxChi2PerCluster{"cfgTpcMaxChi2PerCluster", 4.0f, ""}; @@ -100,13 +104,6 @@ struct LongrangecorrDerived { Configurable cfgEffccdbPath{"cfgEffccdbPath", "Users/a/abmodak/Efficiency/OO/default", "Browse track eff object from CCDB"}; Configurable cfgAccccdbPath{"cfgAccccdbPath", "Users/a/abmodak/Acceptance/OO/default", "Browse track eff object from CCDB"}; - Configurable cfgMftCluster{"cfgMftCluster", 5, "cut on MFT Cluster"}; - Configurable cfgMftDcaxy{"cfgMftDcaxy", 2.0f, "cut on DCA xy for MFT tracks"}; - Configurable cfgMftDcaz{"cfgMftDcaz", 2.0f, "cut on DCA z for MFT tracks"}; - Configurable cfgRejectAmbTrk{"cfgRejectAmbTrk", false, "Condition to reject Ambiguous tracks"}; - Configurable cfgRejectNonAmbTrk{"cfgRejectNonAmbTrk", false, "Condition to reject Non-Ambiguous tracks"}; - Configurable cfgRequireCA{"cfgRequireCA", false, "Use Cellular Automaton track-finding algorithm"}; - Configurable cfgRequireLTF{"cfgRequireLTF", false, "Use LTF track-finding algorithm"}; Configurable cfgRequireFt0aOuterRing{"cfgRequireFt0aOuterRing", false, "Consider FT0A Outer Ring"}; Configurable cfgRequireFt0aInnerRing{"cfgRequireFt0aInnerRing", false, "Consider FT0A Inner Ring"}; Configurable cfgRequireFt0cOuterRing{"cfgRequireFt0cOuterRing", false, "Consider FT0C Outer Ring"}; @@ -115,13 +112,37 @@ struct LongrangecorrDerived { Configurable cfgTofPidPtCut{"cfgTofPidPtCut", 0.4f, "Minimum pt to use TOF N-sigma"}; Configurable cfgPidNsigmaMax{"cfgPidNsigmaMax", 1.5f, "Maximum n-sigma for PID"}; Configurable cfgPidNsigmaMin{"cfgPidNsigmaMin", -1.5f, "Minimum n-sigma for PID"}; - Configurable cfgCleanNsigmaTpcMax{"cfgCleanNsigmaTpcMax", 1.5f, "Max TPC n-sigma for clean templates"}; - Configurable cfgCleanNsigmaTpcMin{"cfgCleanNsigmaTpcMin", -1.5f, "Min TPC n-sigma for clean templates"}; + Configurable cfgCleanNsigmaTpcMax{"cfgCleanNsigmaTpcMax", 10.0f, "Max TPC n-sigma for clean templates (wide to avoid dE/dx truncation)"}; + Configurable cfgCleanNsigmaTpcMin{"cfgCleanNsigmaTpcMin", -10.0f, "Min TPC n-sigma for clean templates (wide to avoid dE/dx truncation)"}; Configurable cfgCleanNsigmaTofMax{"cfgCleanNsigmaTofMax", 1.5f, "Max TOF n-sigma for clean templates"}; Configurable cfgCleanNsigmaTofMin{"cfgCleanNsigmaTofMin", -1.5f, "Min TOF n-sigma for clean templates"}; + Configurable cfgCleanTofVetoNsigma{"cfgCleanTofVetoNsigma", 3.0f, "Veto other species within this |nSigmaTOF| for track-level TOF-clean templates"}; Configurable cfgGetNsigmaQA{"cfgGetNsigmaQA", true, "Get QA histograms for PID selection"}; Configurable cfgGetdEdx{"cfgGetdEdx", true, "Get dEdx histograms for TPC signal"}; - + Configurable cfgGetAllSpeciesQA{"cfgGetAllSpeciesQA", true, "Fill unified all-species dE/dx and expected sigma histograms"}; + Configurable cfgGetV0CleanSample{"cfgGetV0CleanSample", true, "Fill clean V0 daughter (K0s -> pi pi, Lambda -> p pi) dE/dx templates"}; + Configurable cfgFillAfterCutWithV0{"cfgFillAfterCutWithV0", true, "Fill TpcdEdx_ptwise_afterCut and TofTpcNsigma_after for pi and p using V0 clean daughters"}; + Configurable cfgRequireTofForV0AfterCut{"cfgRequireTofForV0AfterCut", true, "Require hasTOF and |nSigmaTOF| < cfgCleanNsigmaTofMax when filling afterCut from V0s"}; + Configurable cfgIncludeLambdaPiInCleanPi{"cfgIncludeLambdaPiInCleanPi", true, "Also include clean pions from Lambda -> p pi in TpcdEdx_ptwise_afterCut for pion wagon"}; + Configurable cfgAllowFallbackMultInPIDQA{"cfgAllowFallbackMultInPIDQA", false, "If true and LRCollisions is empty, use fallback multiplicity in processPIDQA"}; + Configurable cfgRequireTofInMCdEdx{"cfgRequireTofInMCdEdx", true, "Require hasTOF and |nSigmaTOF| < 1.5 in MC_TpcdEdx_ptwise"}; + + // V0 topological cuts for clean daughter template extraction (tag-and-probe without TPC cut on probe daughter) + Configurable cfgCleanV0MinCosPA{"cfgCleanV0MinCosPA", 0.995f, "Min cos(PA) for clean V0 daughters"}; + Configurable cfgCleanV0MaxDcaDau{"cfgCleanV0MaxDcaDau", 0.8f, "Max DCA between V0 daughters (cm)"}; + Configurable cfgCleanV0MinRadius{"cfgCleanV0MinRadius", 1.2f, "Min V0 2D decay radius (cm)"}; + Configurable cfgCleanV0MaxRadius{"cfgCleanV0MaxRadius", 30.0f, "Max V0 2D decay radius (cm)"}; + Configurable cfgCleanK0sMassWin{"cfgCleanK0sMassWin", 0.008f, "Tight K0s mass window around PDG mass (GeV/c^2)"}; + Configurable cfgCleanK0sMinArmQt{"cfgCleanK0sMinArmQt", 0.2f, "Min Armenteros qT/|alpha| for clean K0s"}; + Configurable cfgCleanK0sMaxCtau{"cfgCleanK0sMaxCtau", 20.0f, "Max proper lifetime c*tau for clean K0s (cm)"}; + Configurable cfgCleanK0sMinDcaToPv{"cfgCleanK0sMinDcaToPv", 0.1f, "Min pion DCA to PV for clean K0s (cm)"}; + Configurable cfgCleanLambdaMassWin{"cfgCleanLambdaMassWin", 0.005f, "Tight Lambda mass window around PDG mass (GeV/c^2)"}; + Configurable cfgCleanLambdaRejK0sWin{"cfgCleanLambdaRejK0sWin", 0.012f, "Competing K0s mass rejection window for clean Lambda (GeV/c^2)"}; + Configurable cfgCleanLambdaMinArmQt{"cfgCleanLambdaMinArmQt", 0.02f, "Min Armenteros qT for clean Lambda to reject photon conversions (GeV/c)"}; + Configurable cfgCleanLambdaMaxCtau{"cfgCleanLambdaMaxCtau", 30.0f, "Max proper lifetime c*tau for clean Lambda (cm)"}; + Configurable cfgCleanLambdaMinDcaPr{"cfgCleanLambdaMinDcaPr", 0.07f, "Min proton DCA to PV for clean Lambda (cm)"}; + Configurable cfgCleanLambdaMinDcaPi{"cfgCleanLambdaMinDcaPi", 0.2f, "Min pion DCA to PV for clean Lambda (cm)"}; + Configurable cfgCleanV0TagTpcNsigma{"cfgCleanV0TagTpcNsigma", 3.0f, "Max |nSigmaTPC| on companion tag daughter"}; } cfgSel; struct : ConfigurableGroup { @@ -132,7 +153,7 @@ struct LongrangecorrDerived { ConfigurableAxis axisPhi{"axisPhi", {96, 0, TwoPI}, "#phi axis"}; ConfigurableAxis axisEtaTrig{"axisEtaTrig", {40, -1., 1.}, "#eta trig axis"}; ConfigurableAxis axisPtTrigger{"axisPtTrigger", {VARIABLE_WIDTH, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 10.0}, "pt trigger axis for histograms"}; - ConfigurableAxis axisPtQA{"axisPtQA", {50, 0.0, 8.0}, "pt axis for PID QA histograms"}; + ConfigurableAxis axisPtQA{"axisPtQA", {80, 0.0, 8.0}, "pt axis for PID QA histograms"}; ConfigurableAxis axisVtxZ{"axisVtxZ", {40, -20, 20}, "vertex axis"}; ConfigurableAxis axisEtaAssoc{"axisEtaAssoc", {96, 3.5, 4.9}, "#eta assoc axis"}; @@ -151,11 +172,6 @@ struct LongrangecorrDerived { ConfigurableAxis axisTPCChi2NCl{"axisTPCChi2NCl", {20, 0.0, 20.0}, "TPC Chi2/NCl axis"}; ConfigurableAxis axisTPCdcaZ{"axisTPCdcaZ", {200, -10.0, 10.0}, "TPC dcaZ axis"}; - ConfigurableAxis axisMFTAmbDegree{"axisMFTAmbDegree", {50, -0.5, 49.5}, "Track Ambiguity axis"}; - ConfigurableAxis axisMFTNClusters{"axisMFTNClusters", {200, -0.5, 199.5}, "MFT Cluster axis"}; - ConfigurableAxis axisMFTbestDCAXY{"axisMFTbestDCAXY", {200, -10.0, 10.0}, "MFT dcaXY axis"}; - ConfigurableAxis axisMFTbestDCAZ{"axisMFTbestDCAZ", {200, -10.0, 10.0}, "MFT dcaZ axis"}; - ConfigurableAxis axisVertexEfficiency{"axisVertexEfficiency", {10, -10, 10}, "vertex axis for efficiency histograms"}; ConfigurableAxis axisEtaEfficiency{"axisEtaEfficiency", {20, -1.0, 1.0}, "eta axis for efficiency histograms"}; ConfigurableAxis axisPtEfficiency{"axisPtEfficiency", {1, 0.5, 8.0}, "pt axis for efficiency histograms"}; @@ -163,6 +179,7 @@ struct LongrangecorrDerived { ConfigurableAxis axisNsigmaTPC{"axisNsigmaTPC", {80, -5, 5}, "nsigmaTPC axis"}; ConfigurableAxis axisNsigmaTOF{"axisNsigmaTOF", {80, -5, 5}, "nsigmaTOF axis"}; ConfigurableAxis axisTpcSignal{"axisTpcSignal", {250, 0, 250}, "dEdx axis for TPC"}; + ConfigurableAxis axisTpcExpSigma{"axisTpcExpSigma", {200, 0, 20}, "Expected sigma axis for TPC dE/dx"}; ConfigurableAxis axisMcPt{"axisMcPt", {100, 0.2, 10.0}, "p_{T} (GeV/c)"}; ConfigurableAxis axisMcEta{"axisMcEta", {100, -0.8, 0.8}, "#eta"}; @@ -185,38 +202,32 @@ struct LongrangecorrDerived { using CollsTable = aod::LRCollisions; using TrksTable = aod::LRMidTracks; - using MftTrksTable = aod::LRMftTracks; using Ft0aTrksTable = aod::LRFt0aTracks; using Ft0cTrksTable = aod::LRFt0cTracks; using V0TrksTable = aod::LRV0Tracks; using McCollsTable = aod::LRMcCollisions; using McTrksTable = aod::LRMidMcTracks; - using McMftTrksTable = aod::LRMftMcTracks; using McFt0aTrksTable = aod::LRFt0aMcTracks; using McFt0cTrksTable = aod::LRFt0cMcTracks; using UpcCollsTable = soa::Join; using TrksUpcTable = aod::UpcLRMidTracks; - using MftTrksUpcTable = aod::UpcLRMftTracks; using Ft0aTrksUpcTable = aod::UpcLRFt0aTracks; using Ft0cTrksUpcTable = aod::UpcLRFt0cTracks; using V0TrksUpcTable = aod::UpcLRV0Tracks; Preslice perColTpc = aod::lrcorrtrktable::lrCollisionId; - Preslice perColMft = aod::lrcorrtrktable::lrCollisionId; Preslice perColFt0a = aod::lrcorrtrktable::lrCollisionId; Preslice perColFt0c = aod::lrcorrtrktable::lrCollisionId; Preslice perColV0 = aod::lrcorrtrktable::lrCollisionId; Preslice perUpcColTpc = aod::lrcorrtrktable::upcLRCollisionId; - Preslice perUpcColMft = aod::lrcorrtrktable::upcLRCollisionId; Preslice perUpcColFt0a = aod::lrcorrtrktable::upcLRCollisionId; Preslice perUpcColFt0c = aod::lrcorrtrktable::upcLRCollisionId; Preslice perUpcColV0 = aod::lrcorrtrktable::upcLRCollisionId; Preslice perMcColTpc = aod::lrcorrmctrktable::lrMcCollisionId; - Preslice perMcColMft = aod::lrcorrmctrktable::lrMcCollisionId; Preslice perMcColFt0a = aod::lrcorrmctrktable::lrMcCollisionId; Preslice perMcColFt0c = aod::lrcorrmctrktable::lrMcCollisionId; @@ -271,13 +282,6 @@ struct LongrangecorrDerived { histos.add("TPCNClsCrossedRows", "TPCNClsCrossedRows", kTH1D, {cfgAxis.axisTPCNClsCrossedRows}); histos.add("TPCChi2NCl", "TPCChi2NCl", kTH1D, {cfgAxis.axisTPCChi2NCl}); histos.add("TPCdcaZ", "TPCdcaZ", kTH1D, {cfgAxis.axisTPCdcaZ}); - - histos.add("MFTNClusters", "MFTNClusters", kTH1D, {cfgAxis.axisMFTNClusters}); - histos.add("MFTbestDCAXY", "MFTbestDCAXY", kTH1D, {cfgAxis.axisMFTbestDCAXY}); - histos.add("MFTbestDCAZ", "MFTbestDCAZ", kTH1D, {cfgAxis.axisMFTbestDCAZ}); - - histos.add("ReassignedMFTtrackAmbDegree", "ReassignedMFTtrackAmbDegree", kTH1D, {cfgAxis.axisMFTAmbDegree}); - histos.add("AssignedMFTtrackAmbDegree", "AssignedMFTtrackAmbDegree", kTH1D, {cfgAxis.axisMFTAmbDegree}); } if (doprocessTPCtrackEff) { @@ -290,7 +294,12 @@ struct LongrangecorrDerived { } if (cfgSel.cfgGetdEdx) { - histos.add("MC_TpcdEdx_ptwise", "MC True TPC dE/dx;Particle Species;p_{T} (GeV/c);TPC dE/dx", kTHnSparseD, {{3, 0.5, 3.5}, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal}); + histos.add("MC_TpcdEdx_ptwise", "MC True TPC dE/dx;Particle Species;p_{T} (GeV/c);TPC dE/dx", + kTHnSparseD, {{3, 0.5, 3.5}, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal}); + histos.add("MC_TpcdEdx_TofNsigma_ptwise", "MC True TPC dE/dx vs TOF n#sigma;Particle Species;p_{T} (GeV/c);TPC dE/dx;n#sigma_{TOF}", + kTHnSparseD, {{3, 0.5, 3.5}, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal, cfgAxis.axisNsigmaTOF}); + histos.add("MC_ExpSigma_ptwise", "MC Expected TPC #sigma;Particle Species;p_{T} (GeV/c);#sigma_{exp}^{TPC};n#sigma_{TOF}", + kTHnSparseD, {{3, 0.5, 3.5}, cfgAxis.axisPtQA, cfgAxis.axisTpcExpSigma, cfgAxis.axisNsigmaTOF}); } } @@ -304,36 +313,62 @@ struct LongrangecorrDerived { myTrackFilter.SetMaxDcaZ(cfgSel.cfgTpcMaxDcaZ); myTrackFilter.print(); - if (cfgSel.cfgGetNsigmaQA && (cfgSel.cfgPidMask == KPidMaskPion || cfgSel.cfgPidMask == KPidMaskKaon || cfgSel.cfgPidMask == KPidMaskProton)) { + if (doprocessPIDQA) { + const AxisSpec axisEvtClass = cfgSel.isUseCentEst ? AxisSpec{cfgAxis.axisCentrality, "Centrality (%)"} + : AxisSpec{cfgAxis.axisMultiplicity, "Multiplicity (N_{ch})"}; + const AxisSpec axisSpecies{3, 0.5, 3.5, "Particle Species (1=#pi, 2=K, 3=p)"}; + const AxisSpec axisV0CleanType{3, 0.5, 3.5, "V0 Clean Daughter (1=#pi from K_{S}^{0}, 2=p from #Lambda, 3=#pi from #Lambda)"}; - if (cfgSel.isUseCentEst) { // CENTRALITY MODE - histos.add("TofTpcNsigma_before", "TPC vs TOF n#sigma Before Cuts;Centrality (%);p_{T} (GeV/c);n#sigma_{TPC};n#sigma_{TOF}", - kTHnSparseD, {cfgAxis.axisCentrality, cfgAxis.axisPtQA, cfgAxis.axisNsigmaTPC, cfgAxis.axisNsigmaTOF}); + histos.add("hPIDQA_EventCount", "PIDQA Event Matching Counter;;Events", kTH1D, {{4, 0.5, 4.5}}); + histos.get(HIST("hPIDQA_EventCount"))->GetXaxis()->SetBinLabel(1, "Raw collisions"); + histos.get(HIST("hPIDQA_EventCount"))->GetXaxis()->SetBinLabel(2, "Pass basic EvSel"); + histos.get(HIST("hPIDQA_EventCount"))->GetXaxis()->SetBinLabel(3, "Matched to LRCollisions"); + histos.get(HIST("hPIDQA_EventCount"))->GetXaxis()->SetBinLabel(4, "Unmatched (skipped)"); - histos.add("TofTpcNsigma_after", "TPC vs TOF n#sigma After Cuts;Centrality (%);p_{T} (GeV/c);n#sigma_{TPC};n#sigma_{TOF}", - kTHnSparseD, {cfgAxis.axisCentrality, cfgAxis.axisPtQA, cfgAxis.axisNsigmaTPC, cfgAxis.axisNsigmaTOF}); + if (cfgSel.cfgGetNsigmaQA && (cfgSel.cfgPidMask == KPidMaskPion || cfgSel.cfgPidMask == KPidMaskKaon || cfgSel.cfgPidMask == KPidMaskProton)) { + histos.add("TofTpcNsigma_before", "TPC vs TOF n#sigma Before Cuts;Event Class;p_{T} (GeV/c);n#sigma_{TPC};n#sigma_{TOF}", + kTHnSparseD, {axisEvtClass, cfgAxis.axisPtQA, cfgAxis.axisNsigmaTPC, cfgAxis.axisNsigmaTOF}); + + histos.add("TofTpcNsigma_after", "TPC vs TOF n#sigma After Cuts;Event Class;p_{T} (GeV/c);n#sigma_{TPC};n#sigma_{TOF}", + kTHnSparseD, {axisEvtClass, cfgAxis.axisPtQA, cfgAxis.axisNsigmaTPC, cfgAxis.axisNsigmaTOF}); if (cfgSel.cfgGetdEdx) { - histos.add("TpcdEdx_ptwise", "TPC dE/dx Before Cuts;Centrality (%);p_{T} (GeV/c);TPC dE/dx;n#sigma_{TOF}", - kTHnSparseD, {cfgAxis.axisCentrality, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal, cfgAxis.axisNsigmaTOF}); + histos.add("TpcdEdx_ptwise", "TPC dE/dx Before Cuts;Event Class;p_{T} (GeV/c);TPC dE/dx;n#sigma_{TOF}", + kTHnSparseD, {axisEvtClass, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal, cfgAxis.axisNsigmaTOF}); + + histos.add("TpcdEdx_ptwise_afterCut", "TPC dE/dx After Clean TOF Cuts;Event Class;p_{T} (GeV/c);TPC dE/dx;n#sigma_{TOF}", + kTHnSparseD, {axisEvtClass, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal, cfgAxis.axisNsigmaTOF}); + + histos.add("ExpTpcdEdx_ptwise", "Expected TPC dE/dx;Event Class;p_{T} (GeV/c);Expected TPC dE/dx;n#sigma_{TOF}", + kTHnSparseD, {axisEvtClass, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal, cfgAxis.axisNsigmaTOF}); - histos.add("TpcdEdx_ptwise_afterCut", "TPC dE/dx After Cuts;Centrality (%);p_{T} (GeV/c);TPC dE/dx;n#sigma_{TOF}", - kTHnSparseD, {cfgAxis.axisCentrality, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal, cfgAxis.axisNsigmaTOF}); + histos.add("ExpSigma_ptwise", "Expected TPC #sigma;Event Class;p_{T} (GeV/c);#sigma_{exp}^{TPC};n#sigma_{TOF}", + kTHnSparseD, {axisEvtClass, cfgAxis.axisPtQA, cfgAxis.axisTpcExpSigma, cfgAxis.axisNsigmaTOF}); } - } else { // MULTIPLICITY MODE - histos.add("TofTpcNsigma_before", "TPC vs TOF n#sigma Before Cuts;Multiplicity (N_{ch});p_{T} (GeV/c);n#sigma_{TPC};n#sigma_{TOF}", - kTHnSparseD, {cfgAxis.axisMultiplicity, cfgAxis.axisPtQA, cfgAxis.axisNsigmaTPC, cfgAxis.axisNsigmaTOF}); + } - histos.add("TofTpcNsigma_after", "TPC vs TOF n#sigma After Cuts;Multiplicity (N_{ch});p_{T} (GeV/c);n#sigma_{TPC};n#sigma_{TOF}", - kTHnSparseD, {cfgAxis.axisMultiplicity, cfgAxis.axisPtQA, cfgAxis.axisNsigmaTPC, cfgAxis.axisNsigmaTOF}); + if (cfgSel.cfgGetAllSpeciesQA && cfgSel.cfgGetdEdx) { + histos.add("TpcdEdx_allSpecies_ptwise", "All-Species TPC dE/dx;Species (1=#pi,2=K,3=p);Event Class;p_{T} (GeV/c);TPC dE/dx;n#sigma_{TOF}", + kTHnSparseD, {axisSpecies, axisEvtClass, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal, cfgAxis.axisNsigmaTOF}); - if (cfgSel.cfgGetdEdx) { - histos.add("TpcdEdx_ptwise", "TPC dE/dx Before Cuts;Multiplicity (N_{ch});p_{T} (GeV/c);TPC dE/dx;n#sigma_{TOF}", - kTHnSparseD, {cfgAxis.axisMultiplicity, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal, cfgAxis.axisNsigmaTOF}); + histos.add("TpcdEdx_allSpecies_afterCut", "All-Species Clean TOF TPC dE/dx;Species (1=#pi,2=K,3=p);Event Class;p_{T} (GeV/c);TPC dE/dx;n#sigma_{TOF}", + kTHnSparseD, {axisSpecies, axisEvtClass, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal, cfgAxis.axisNsigmaTOF}); - histos.add("TpcdEdx_ptwise_afterCut", "TPC dE/dx After Cuts;Multiplicity (N_{ch});p_{T} (GeV/c);TPC dE/dx;n#sigma_{TOF}", - kTHnSparseD, {cfgAxis.axisMultiplicity, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal, cfgAxis.axisNsigmaTOF}); - } + histos.add("ExpSigma_allSpecies_ptwise", "All-Species Expected TPC #sigma;Species (1=#pi,2=K,3=p);Event Class;p_{T} (GeV/c);#sigma_{exp}^{TPC};n#sigma_{TOF}", + kTHnSparseD, {axisSpecies, axisEvtClass, cfgAxis.axisPtQA, cfgAxis.axisTpcExpSigma, cfgAxis.axisNsigmaTOF}); + } + + if (cfgSel.cfgGetV0CleanSample && cfgSel.cfgGetdEdx) { + histos.add("V0_TpcdEdx_ptwise", "Clean V0 Daughter TPC dE/dx;Daughter Type (1=#pi(K_{S}^{0}), 2=p(#Lambda), 3=#pi(#Lambda));Event Class;p_{T} (GeV/c);TPC dE/dx;n#sigma_{TOF}", + kTHnSparseD, {axisV0CleanType, axisEvtClass, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal, cfgAxis.axisNsigmaTOF}); + + histos.add("V0_ExpSigma_ptwise", "Clean V0 Daughter Expected TPC #sigma;Daughter Type (1=#pi(K_{S}^{0}), 2=p(#Lambda), 3=#pi(#Lambda));Event Class;p_{T} (GeV/c);#sigma_{exp}^{TPC};n#sigma_{TOF}", + kTHnSparseD, {axisV0CleanType, axisEvtClass, cfgAxis.axisPtQA, cfgAxis.axisTpcExpSigma, cfgAxis.axisNsigmaTOF}); + + histos.add("hCleanK0s_MassVsPt", "Selected Clean K_{S}^{0};p_{T} (GeV/c);M(#pi^{+}#pi^{-}) (GeV/c^{2})", + kTH2D, {cfgAxis.axisPtQA, {100, 0.45, 0.55}}); + histos.add("hCleanLambda_MassVsPt", "Selected Clean #Lambda/#bar{#Lambda};p_{T} (GeV/c);M(p#pi) (GeV/c^{2})", + kTH2D, {cfgAxis.axisPtQA, {100, 1.09, 1.14}}); } } } @@ -343,7 +378,7 @@ struct LongrangecorrDerived { if (fLoadTrkEffCorr) { return; } - if (cfgSel.cfgEffccdbPath.value.empty() == false) { + if (!cfgSel.cfgEffccdbPath.value.empty()) { hTrkEff = ccdb->getForTimeStamp(cfgSel.cfgEffccdbPath, timestamp); if (hTrkEff == nullptr) { LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgSel.cfgEffccdbPath.value.c_str()); @@ -351,7 +386,7 @@ struct LongrangecorrDerived { LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgSel.cfgEffccdbPath.value.c_str(), static_cast(hTrkEff)); } - if (cfgSel.cfgAccccdbPath.value.empty() == false) { + if (!cfgSel.cfgAccccdbPath.value.empty()) { hTrkAcc = ccdb->getForTimeStamp(cfgSel.cfgAccccdbPath, timestamp); if (hTrkAcc == nullptr) { LOGF(fatal, "Could not load NUA histogram for trigger particles from %s", cfgSel.cfgAccccdbPath.value.c_str()); @@ -396,30 +431,18 @@ struct LongrangecorrDerived { bool isTrackSelected(TTrack const& track) { if constexpr (requires { track.tpcNClsFound(); }) { - if (track.tpcNClsFound() < cfgSel.cfgTpcMinNclsFound) + if (track.tpcNClsFound() < cfgSel.cfgTpcMinNclsFound) { return false; - if (track.tpcNClsCrossedRows() < cfgSel.cfgTpcMinNCrossedRows) - return false; - if (track.tpcChi2NCl() > cfgSel.cfgTpcMaxChi2PerCluster) - return false; - if (std::abs(track.dcaZ()) > cfgSel.cfgTpcMaxDcaZ) - return false; - return true; - } else if constexpr (requires { track.nClusters(); }) { - if (track.nClusters() < cfgSel.cfgMftCluster) - return false; - if (std::abs(track.bestDCAXY()) >= cfgSel.cfgMftDcaxy) - return false; - if (std::abs(track.bestDCAZ()) >= cfgSel.cfgMftDcaz) - return false; - if (cfgSel.cfgRejectAmbTrk && track.ambDegree() > 1) - return false; - if (cfgSel.cfgRejectNonAmbTrk && track.ambDegree() == 1) + } + if (track.tpcNClsCrossedRows() < cfgSel.cfgTpcMinNCrossedRows) { return false; - if (cfgSel.cfgRequireCA && !track.isCA()) + } + if (track.tpcChi2NCl() > cfgSel.cfgTpcMaxChi2PerCluster) { return false; - if (cfgSel.cfgRequireLTF && track.isCA()) + } + if (std::abs(track.dcaZ()) > cfgSel.cfgTpcMaxDcaZ) { return false; + } return true; } else { return true; @@ -460,16 +483,6 @@ struct LongrangecorrDerived { histos.fill(HIST("TPCChi2NCl"), track.tpcChi2NCl()); histos.fill(HIST("TPCdcaZ"), track.dcaZ()); } - if constexpr (requires { track.nClusters(); }) { - histos.fill(HIST("MFTNClusters"), track.nClusters()); - histos.fill(HIST("MFTbestDCAXY"), track.bestDCAXY()); - histos.fill(HIST("MFTbestDCAZ"), track.bestDCAZ()); - if (track.isReassigned()) { - histos.fill(HIST("ReassignedMFTtrackAmbDegree"), track.ambDegree()); - } else { - histos.fill(HIST("AssignedMFTtrackAmbDegree"), track.ambDegree()); - } - } } template @@ -482,16 +495,6 @@ struct LongrangecorrDerived { histos.fill(HIST("Assoc_amp"), assoAmpl); histos.fill(HIST("Channel_vs_Assoc_amp"), track.channelID(), assoAmpl); } - if constexpr (requires { track.nClusters(); }) { - histos.fill(HIST("MFTNClusters"), track.nClusters()); - histos.fill(HIST("MFTbestDCAXY"), track.bestDCAXY()); - histos.fill(HIST("MFTbestDCAZ"), track.bestDCAZ()); - if (track.isReassigned()) { - histos.fill(HIST("ReassignedMFTtrackAmbDegree"), track.ambDegree()); - } else { - histos.fill(HIST("AssignedMFTtrackAmbDegree"), track.ambDegree()); - } - } } template @@ -504,8 +507,9 @@ struct LongrangecorrDerived { if constexpr (fillHist) { histos.fill(HIST("hTrueGapSide"), truegapSide); } - if (truegapSide != cfgGapSideCut) + if (truegapSide != cfgGapSideCut) { return false; + } if constexpr (fillHist) { histos.fill(HIST("hTrueGapSide_AfterSel"), truegapSide); } @@ -521,22 +525,27 @@ struct LongrangecorrDerived { auto trkeff = 1.0f; auto trkAcc = 1.0f; - if (!isTrackSelected(triggerTrack)) + if (!isTrackSelected(triggerTrack)) { continue; + } if constexpr (requires { triggerTrack.trackType(); }) { - if (cfgSel.cfgPidMask != 0 && (cfgSel.cfgPidMask & (1u << static_cast(triggerTrack.trackType()))) == 0u) + if (cfgSel.cfgPidMask != 0 && (cfgSel.cfgPidMask & (1u << static_cast(triggerTrack.trackType()))) == 0u) { continue; + } } else if constexpr (requires { triggerTrack.v0Type(); }) { - if (cfgSel.cfgV0Mask != 0 && (cfgSel.cfgV0Mask & (1u << static_cast(triggerTrack.v0Type()))) == 0u) + if (cfgSel.cfgV0Mask != 0 && (cfgSel.cfgV0Mask & (1u << static_cast(triggerTrack.v0Type()))) == 0u) { continue; + } } if constexpr (requires { triggerTrack.channelID(); }) { - if (cfgSel.cfgRequireFt0aOuterRing && !triggerTrack.isTrackFT0Outer()) + if (cfgSel.cfgRequireFt0aOuterRing && !triggerTrack.isTrackFT0Outer()) { continue; - if (cfgSel.cfgRequireFt0aInnerRing && triggerTrack.isTrackFT0Outer()) + } + if (cfgSel.cfgRequireFt0aInnerRing && triggerTrack.isTrackFT0Outer()) { continue; + } trigAmpl = triggerTrack.amplitude(); } else { trigAmpl = 1.0; @@ -569,14 +578,17 @@ struct LongrangecorrDerived { for (auto const& assoTrack : assocs) { auto assoAmpl = 1.0f; - if (!isTrackSelected(assoTrack)) + if (!isTrackSelected(assoTrack)) { continue; + } if constexpr (requires { assoTrack.channelID(); }) { - if (cfgSel.cfgRequireFt0cOuterRing && !assoTrack.isTrackFT0Outer()) + if (cfgSel.cfgRequireFt0cOuterRing && !assoTrack.isTrackFT0Outer()) { continue; - if (cfgSel.cfgRequireFt0cInnerRing && assoTrack.isTrackFT0Outer()) + } + if (cfgSel.cfgRequireFt0cInnerRing && assoTrack.isTrackFT0Outer()) { continue; + } assoAmpl = assoTrack.amplitude(); } else { assoAmpl = 1.0f; @@ -611,10 +623,11 @@ struct LongrangecorrDerived { fillCollQA(col); auto multiplicity = 1.0f; if constexpr (requires { col.centrality(); }) { - if (cfgSel.isUseCentEst) + if (cfgSel.isUseCentEst) { multiplicity = col.centrality(); - else + } else { multiplicity = col.multiplicity(); + } } else { multiplicity = col.multiplicity(); } @@ -638,10 +651,11 @@ struct LongrangecorrDerived { } auto multiplicity = 1.0f; if constexpr (requires { col.centrality(); }) { - if (cfgSel.isUseCentEst) + if (cfgSel.isUseCentEst) { multiplicity = col.centrality(); - else + } else { multiplicity = col.multiplicity(); + } } else { multiplicity = col.multiplicity(); } @@ -690,10 +704,11 @@ struct LongrangecorrDerived { auto multiplicity = mccollision.multiplicity(); if (cfgSel.isUseDataLikeMult > 0) { for (const auto& collision : collisions) { - if (cfgSel.isUseCentEst) + if (cfgSel.isUseCentEst) { multiplicity = collision.centrality(); - else + } else { multiplicity = collision.multiplicity(); + } } } fillCorrHist(same, triggers, assocs, false, mccollision.posZ(), multiplicity, 1.0); @@ -705,15 +720,17 @@ struct LongrangecorrDerived { bool useMCMultiplicity = (cfgSel.isUseDataLikeMult == 0); auto getMultiplicity = [&collisions, &useMCMultiplicity, this](auto& col) { - if (useMCMultiplicity) + if (useMCMultiplicity) { return col.multiplicity(); + } auto groupedCollisions = collisions.sliceByCached(aod::lrcorrcolltable::lrMcCollisionId, col.globalIndex(), this->cache); - if (groupedCollisions.size() == 0) + if (groupedCollisions.size() == 0) { return -1.0f; - if (cfgSel.isUseCentEst) + } + if (cfgSel.isUseCentEst) { return groupedCollisions.begin().centrality(); - else - return groupedCollisions.begin().multiplicity(); + } + return groupedCollisions.begin().multiplicity(); }; using MixedBinning = FlexibleBinningPolicy, aod::mccollision::PosZ, decltype(getMultiplicity)>; @@ -772,16 +789,6 @@ struct LongrangecorrDerived { processSame(col, tracks, ft0cs); } - void processTpcmftSE(CollsTable::iterator const& col, TrksTable const& tracks, MftTrksTable const& mfts) - { - processSame(col, tracks, mfts); - } - - void processMftft0aSE(CollsTable::iterator const& col, MftTrksTable const& mfts, Ft0aTrksTable const& ft0as) - { - processSame(col, mfts, ft0as); - } - void processV0ft0aSE(CollsTable::iterator const& col, V0TrksTable const& tracks, Ft0aTrksTable const& ft0as) { processSame(col, tracks, ft0as); @@ -797,11 +804,6 @@ struct LongrangecorrDerived { processSame(col, v0s, tracks); } - void processV0mftSE(CollsTable::iterator const& col, V0TrksTable const& tracks, MftTrksTable const& mfts) - { - processSame(col, tracks, mfts); - } - void processFt0aft0cSE(CollsTable::iterator const& col, Ft0aTrksTable const& ft0as, Ft0cTrksTable const& ft0cs) { processSame(col, ft0as, ft0cs); @@ -817,16 +819,6 @@ struct LongrangecorrDerived { processMixed(cols, tracks, ft0cs); } - void processTpcmftME(CollsTable const& cols, TrksTable const& tracks, MftTrksTable const& mfts) - { - processMixed(cols, tracks, mfts); - } - - void processMftft0aME(CollsTable const& cols, MftTrksTable const& mfts, Ft0aTrksTable const& ft0as) - { - processMixed(cols, mfts, ft0as); - } - void processV0ft0aME(CollsTable const& cols, V0TrksTable const& tracks, Ft0aTrksTable const& ft0as) { processMixed(cols, tracks, ft0as); @@ -842,11 +834,6 @@ struct LongrangecorrDerived { processMixed(cols, v0s, tracks); } - void processV0mftME(CollsTable const& cols, V0TrksTable const& tracks, MftTrksTable const& mfts) - { - processMixed(cols, tracks, mfts); - } - void processFt0aft0cME(CollsTable const& cols, Ft0aTrksTable const& ft0as, Ft0cTrksTable const& ft0cs) { processMixed(cols, ft0as, ft0cs); @@ -868,22 +855,6 @@ struct LongrangecorrDerived { processSame(col, tracks, ft0cs); } - void processUpcTpcmftSE(UpcCollsTable::iterator const& col, TrksUpcTable const& tracks, MftTrksUpcTable const& mfts) - { - if (!isUpcEventSelected(col)) { - return; - } - processSame(col, tracks, mfts); - } - - void processUpcMftft0aSE(UpcCollsTable::iterator const& col, MftTrksUpcTable const& mfts, Ft0aTrksUpcTable const& ft0as) - { - if (!isUpcEventSelected(col)) { - return; - } - processSame(col, mfts, ft0as); - } - void processUpcV0ft0aSE(UpcCollsTable::iterator const& col, V0TrksUpcTable const& tracks, Ft0aTrksUpcTable const& ft0as) { if (!isUpcEventSelected(col)) { @@ -897,14 +868,6 @@ struct LongrangecorrDerived { processSame(col, tracks, ft0cs); } - void processUpcV0mftSE(UpcCollsTable::iterator const& col, V0TrksUpcTable const& tracks, MftTrksUpcTable const& mfts) - { - if (!isUpcEventSelected(col)) { - return; - } - processSame(col, tracks, mfts); - } - void processUpcTpcft0aME(UpcCollsTable const& cols, TrksUpcTable const& tracks, Ft0aTrksUpcTable const& ft0as) { processMixed(cols, tracks, ft0as); @@ -915,16 +878,6 @@ struct LongrangecorrDerived { processMixed(cols, tracks, ft0cs); } - void processUpcTpcmftME(UpcCollsTable const& cols, TrksUpcTable const& tracks, MftTrksUpcTable const& mfts) - { - processMixed(cols, tracks, mfts); - } - - void processUpcMftft0aME(UpcCollsTable const& cols, MftTrksUpcTable const& mfts, Ft0aTrksUpcTable const& ft0as) - { - processMixed(cols, mfts, ft0as); - } - void processUpcV0ft0aME(UpcCollsTable const& cols, V0TrksUpcTable const& tracks, Ft0aTrksUpcTable const& ft0as) { processMixed(cols, tracks, ft0as); @@ -935,11 +888,6 @@ struct LongrangecorrDerived { processMixed(cols, tracks, ft0cs); } - void processUpcV0mftME(UpcCollsTable const& cols, V0TrksUpcTable const& tracks, MftTrksUpcTable const& mfts) - { - processMixed(cols, tracks, mfts); - } - void processMcTpcft0aSE(McCollsTable::iterator const& mccollision, soa::SmallGroups const& collisions, McTrksTable const& tracks, McFt0aTrksTable const& ft0as) { processMcSame(mccollision, collisions, tracks, ft0as); @@ -950,16 +898,6 @@ struct LongrangecorrDerived { processMcSame(mccollision, collisions, tracks, ft0cs); } - void processMcTpcmftSE(McCollsTable::iterator const& mccollision, soa::SmallGroups const& collisions, McTrksTable const& tracks, McMftTrksTable const& mfts) - { - processMcSame(mccollision, collisions, tracks, mfts); - } - - void processMcMftft0aSE(McCollsTable::iterator const& mccollision, soa::SmallGroups const& collisions, McMftTrksTable const& mfts, McFt0aTrksTable const& ft0as) - { - processMcSame(mccollision, collisions, mfts, ft0as); - } - void processMcFt0aft0cSE(McCollsTable::iterator const& mccollision, soa::SmallGroups const& collisions, McFt0aTrksTable const& ft0as, McFt0cTrksTable const& ft0cs) { processMcSame(mccollision, collisions, ft0as, ft0cs); @@ -975,16 +913,6 @@ struct LongrangecorrDerived { processMcMixed(mccollisions, collisions, tracks, ft0cs); } - void processMcTpcmftME(McCollsTable const& mccollisions, aod::LRCollisionsWithLabel const& collisions, McTrksTable const& tracks, McMftTrksTable const& mfts) - { - processMcMixed(mccollisions, collisions, tracks, mfts); - } - - void processMcMftft0aME(McCollsTable const& mccollisions, aod::LRCollisionsWithLabel const& collisions, McMftTrksTable const& mfts, McFt0aTrksTable const& ft0as) - { - processMcMixed(mccollisions, collisions, mfts, ft0as); - } - void processMcFt0aft0cME(McCollsTable const& mccollisions, aod::LRCollisionsWithLabel const& collisions, McFt0aTrksTable const& ft0as, McFt0cTrksTable const& ft0cs) { processMcMixed(mccollisions, collisions, ft0as, ft0cs); @@ -1000,16 +928,6 @@ struct LongrangecorrDerived { processMcGenSame(mccollision, tracks, ft0cs); } - void processMcGenTpcmftSE(McCollsTable::iterator const& mccollision, McTrksTable const& tracks, McMftTrksTable const& mfts) - { - processMcGenSame(mccollision, tracks, mfts); - } - - void processMcGenMftft0aSE(McCollsTable::iterator const& mccollision, McMftTrksTable const& mfts, McFt0aTrksTable const& ft0as) - { - processMcGenSame(mccollision, mfts, ft0as); - } - void processMcGenV0ft0aSE(McCollsTable::iterator const& mccollision, McTrksTable const& tracks, McFt0aTrksTable const& ft0as) { processMcGenSame(mccollision, tracks, ft0as); @@ -1035,16 +953,6 @@ struct LongrangecorrDerived { processMcGenMixed(mccollisions, tracks, ft0cs); } - void processMcGenTpcmftME(McCollsTable const& mccollisions, McTrksTable const& tracks, McMftTrksTable const& mfts) - { - processMcGenMixed(mccollisions, tracks, mfts); - } - - void processMcGenMftft0aME(McCollsTable const& mccollisions, McMftTrksTable const& mfts, McFt0aTrksTable const& ft0as) - { - processMcGenMixed(mccollisions, mfts, ft0as); - } - void processMcGenV0ft0aME(McCollsTable const& mccollisions, McTrksTable const& tracks, McFt0aTrksTable const& ft0as) { processMcGenMixed(mccollisions, tracks, ft0as); @@ -1068,29 +976,37 @@ struct LongrangecorrDerived { template bool isEventSelected(CheckCol const& col) { - if (!col.sel8()) + if (!col.sel8()) { return false; - if (!col.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) + } + if (!col.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { return false; - if (!col.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) + } + if (!col.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { return false; - if (!col.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) + } + if (!col.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { return false; - if (std::abs(col.posZ()) >= cfgSel.cfgVtxCut) + } + if (std::abs(col.posZ()) >= cfgSel.cfgVtxCut) { return false; + } return true; } template bool isGenPartSelected(CheckGenPart const& particle) { - if (!particle.isPhysicalPrimary() || !particle.producedByGenerator()) + if (!particle.isPhysicalPrimary() || !particle.producedByGenerator()) { return false; + } auto pdgParticle = pdg->GetParticle(particle.pdgCode()); - if (pdgParticle == nullptr) + if (pdgParticle == nullptr) { return false; - if (std::abs(pdgParticle->Charge()) < KminCharge) + } + if (std::abs(pdgParticle->Charge()) < KminCharge) { return false; + } return true; } @@ -1102,18 +1018,21 @@ struct LongrangecorrDerived { } bool atLeastOne = false; for (const auto& RecCol : RecCols) { - if (!isEventSelected(RecCol)) + if (!isEventSelected(RecCol)) { continue; - if (RecCol.globalIndex() != mcCollision.bestCollisionIndex()) + } + if (RecCol.globalIndex() != mcCollision.bestCollisionIndex()) { continue; + } atLeastOne = true; } for (const auto& particle : mcparticles) { if (!isGenPartSelected(particle) || std::abs(particle.eta()) > cfgSel.cfgEtaCut || particle.pt() < cfgSel.cfgPtCutMin || - particle.pt() > cfgSel.cfgPtCutMax) + particle.pt() > cfgSel.cfgPtCutMax) { continue; + } if (atLeastOne) { histos.fill(HIST("hGenMCdndpt"), mcCollision.posZ(), particle.eta(), particle.pt()); @@ -1132,37 +1051,60 @@ struct LongrangecorrDerived { } for (const auto& RecCol : RecCols) { - if (!isEventSelected(RecCol)) + if (!isEventSelected(RecCol)) { continue; - if (RecCol.globalIndex() != mcCollision.bestCollisionIndex()) + } + if (RecCol.globalIndex() != mcCollision.bestCollisionIndex()) { continue; + } auto recTracksPart = RecTracks.sliceBy(perColMidtrack, RecCol.globalIndex()); for (const auto& track : recTracksPart) { - if (!track.isGlobalTrack()) + if (!track.isGlobalTrack()) { continue; - if (!myTrackFilter.IsSelected(track)) + } + if (!myTrackFilter.IsSelected(track)) { continue; - if (!track.has_mcParticle()) + } + if (!track.has_mcParticle()) { continue; + } auto particle = track.mcParticle(); - if (RecCol.mcCollisionId() != particle.mcCollisionId()) + if (RecCol.mcCollisionId() != particle.mcCollisionId()) { continue; + } if (particle.isPhysicalPrimary()) { histos.fill(HIST("hRecMCdndpt"), mcCollision.posZ(), particle.eta(), particle.pt()); if (cfgSel.cfgGetdEdx) { int pdgIdx = 0; + float tofNsigMC = -999.0f; + float expSigmaMC = 0.0f; auto absPdg = std::abs(particle.pdgCode()); - if (absPdg == PDG_t::kPiPlus) + if (absPdg == PDG_t::kPiPlus) { pdgIdx = 1; // o2-linter: disable=pdg/explicit-code (histogram species index) - else if (absPdg == PDG_t::kKPlus) + tofNsigMC = track.tofNSigmaPi(); + expSigmaMC = track.tpcExpSigmaPi(); + } else if (absPdg == PDG_t::kKPlus) { pdgIdx = 2; // o2-linter: disable=pdg/explicit-code (histogram species index) - else if (absPdg == PDG_t::kProton) + tofNsigMC = track.tofNSigmaKa(); + expSigmaMC = track.tpcExpSigmaKa(); + } else if (absPdg == PDG_t::kProton) { pdgIdx = 3; // o2-linter: disable=pdg/explicit-code (histogram species index) + tofNsigMC = track.tofNSigmaPr(); + expSigmaMC = track.tpcExpSigmaPr(); + } if (pdgIdx > 0) { - histos.fill(HIST("MC_TpcdEdx_ptwise"), static_cast(pdgIdx), track.pt(), track.tpcSignal()); + if (track.hasTOF()) { + histos.fill(HIST("MC_TpcdEdx_TofNsigma_ptwise"), static_cast(pdgIdx), track.pt(), track.tpcSignal(), tofNsigMC); + histos.fill(HIST("MC_ExpSigma_ptwise"), static_cast(pdgIdx), track.pt(), expSigmaMC, tofNsigMC); + } + bool passTofForMC = !cfgSel.cfgRequireTofInMCdEdx || + (track.hasTOF() && tofNsigMC > cfgSel.cfgCleanNsigmaTofMin && tofNsigMC < cfgSel.cfgCleanNsigmaTofMax); + if (passTofForMC) { + histos.fill(HIST("MC_TpcdEdx_ptwise"), static_cast(pdgIdx), track.pt(), track.tpcSignal()); + } } } @@ -1180,7 +1122,7 @@ struct LongrangecorrDerived { isPion = isTofPion && isTpcPion; isKaon = isTofKaon && isTpcKaon; isProton = isTofProton && isTpcProton; - } else if (!(track.pt() > cfgSel.cfgTofPidPtCut && !track.hasTOF())) { + } else if (track.pt() <= cfgSel.cfgTofPidPtCut || track.hasTOF()) { isPion = isTpcPion; isKaon = isTpcKaon; isProton = isTpcProton; @@ -1209,82 +1151,269 @@ struct LongrangecorrDerived { using CollStandardTable = soa::Join; using TrksStandardTable = soa::Join; - void processPIDQA(CollsTable const& lrCollisions, TrksStandardTable const& standardTracks) + template + float countFallbackMultiplicity(TTracks const& tracks, float vz) { - for (auto const& col : lrCollisions) { - - if (std::abs(col.posZ()) >= cfgSel.cfgVtxCut) { + float nTrk = 0.0f; + for (auto const& trk : tracks) { + if (!trk.isGlobalTrack()) { continue; } + if (!myTrackFilter.IsSelected(trk)) { + continue; + } + if (trk.pt() < cfgSel.cfgPtCutMinForMult || trk.pt() > cfgSel.cfgPtCutMaxForMult) { + continue; + } + nTrk += getTrkEffCorr(vz, trk.eta(), trk.pt()); + } + return nTrk; + } - // EXTRACT CENTRALITY/MULTIPLICITY - float multiplicity = 1.0f; - if constexpr (requires { col.centrality(); }) { - if (cfgSel.isUseCentEst) - multiplicity = col.centrality(); - else - multiplicity = col.multiplicity(); + template + void fillCleanV0Daughters(TCollision const& rawCol, TV0 const& v0, TTrack const& posTrack, TTrack const& negTrack, float multiplicity) + { + if (!posTrack.hasTPC() || !negTrack.hasTPC()) { + return; + } + if (posTrack.tpcNClsCrossedRows() < cfgSel.cfgTpcMinNCrossedRows || negTrack.tpcNClsCrossedRows() < cfgSel.cfgTpcMinNCrossedRows) { + return; + } + if (std::abs(v0.positiveeta()) > cfgSel.cfgEtaCut || std::abs(v0.negativeeta()) > cfgSel.cfgEtaCut) { + return; + } + if (v0.v0cosPA() < cfgSel.cfgCleanV0MinCosPA) { + return; + } + if (v0.dcaV0daughters() > cfgSel.cfgCleanV0MaxDcaDau) { + return; + } + if (v0.v0radius() < cfgSel.cfgCleanV0MinRadius || v0.v0radius() > cfgSel.cfgCleanV0MaxRadius) { + return; + } + + float distOverP = v0.distovertotmom(rawCol.posX(), rawCol.posY(), rawCol.posZ()); + + auto fillV0Probe = [&](double v0CleanType, TTrack const& probeTrack, float tpcNsig, float tofNsigReal, float expSigma, bool isPionProbe, bool isProtonProbe) { + if (!probeTrack.isGlobalTrack() || !myTrackFilter.IsSelected(probeTrack)) { + return; + } + + const float tofVal = probeTrack.hasTOF() ? tofNsigReal : -999.0f; + + if (cfgSel.cfgGetV0CleanSample && cfgSel.cfgGetdEdx) { + histos.fill(HIST("V0_TpcdEdx_ptwise"), v0CleanType, multiplicity, probeTrack.pt(), probeTrack.tpcSignal(), tofVal); + histos.fill(HIST("V0_ExpSigma_ptwise"), v0CleanType, multiplicity, probeTrack.pt(), expSigma, tofVal); + } + + if (cfgSel.cfgFillAfterCutWithV0 && cfgSel.cfgGetNsigmaQA) { + const bool passTofForAfterCut = !cfgSel.cfgRequireTofForV0AfterCut || + (probeTrack.hasTOF() && tofNsigReal > cfgSel.cfgCleanNsigmaTofMin && tofNsigReal < cfgSel.cfgCleanNsigmaTofMax); + const bool matchWagon = (((cfgSel.cfgPidMask & KPidMaskPion) != 0) && isPionProbe) || + (((cfgSel.cfgPidMask & KPidMaskProton) != 0) && isProtonProbe); + if (matchWagon && passTofForAfterCut) { + histos.fill(HIST("TofTpcNsigma_after"), multiplicity, probeTrack.pt(), tpcNsig, tofVal); + if (cfgSel.cfgGetdEdx) { + histos.fill(HIST("TpcdEdx_ptwise_afterCut"), multiplicity, probeTrack.pt(), probeTrack.tpcSignal(), tofVal); + } + } + } + }; + + float ctauK0s = distOverP * o2::constants::physics::MassK0; + bool isK0sTopo = (std::abs(v0.mK0Short() - o2::constants::physics::MassK0) < cfgSel.cfgCleanK0sMassWin) && + (v0.qtarm() >= cfgSel.cfgCleanK0sMinArmQt * std::abs(v0.alpha())) && + (std::abs(ctauK0s) <= cfgSel.cfgCleanK0sMaxCtau) && + (std::abs(v0.dcapostopv()) >= cfgSel.cfgCleanK0sMinDcaToPv) && + (std::abs(v0.dcanegtopv()) >= cfgSel.cfgCleanK0sMinDcaToPv); + + if (isK0sTopo) { + if (cfgSel.cfgGetV0CleanSample && cfgSel.cfgGetdEdx) { + histos.fill(HIST("hCleanK0s_MassVsPt"), v0.pt(), v0.mK0Short()); + } + if (std::abs(negTrack.tpcNSigmaPi()) < cfgSel.cfgCleanV0TagTpcNsigma) { + fillV0Probe(1.0, posTrack, posTrack.tpcNSigmaPi(), posTrack.tofNSigmaPi(), posTrack.tpcExpSigmaPi(), true, false); + } + if (std::abs(posTrack.tpcNSigmaPi()) < cfgSel.cfgCleanV0TagTpcNsigma) { + fillV0Probe(1.0, negTrack, negTrack.tpcNSigmaPi(), negTrack.tofNSigmaPi(), negTrack.tpcExpSigmaPi(), true, false); + } + } + + float ctauLambda = distOverP * o2::constants::physics::MassLambda; + bool passK0sRej = std::abs(v0.mK0Short() - o2::constants::physics::MassK0) >= cfgSel.cfgCleanLambdaRejK0sWin; + bool passLambdaArm = v0.qtarm() >= cfgSel.cfgCleanLambdaMinArmQt; + bool passLambdaCtau = std::abs(ctauLambda) <= cfgSel.cfgCleanLambdaMaxCtau; + + if (passK0sRej && passLambdaArm && passLambdaCtau) { + bool isLambdaTopo = (std::abs(v0.mLambda() - o2::constants::physics::MassLambda) < cfgSel.cfgCleanLambdaMassWin) && + (std::abs(v0.dcapostopv()) >= cfgSel.cfgCleanLambdaMinDcaPr) && + (std::abs(v0.dcanegtopv()) >= cfgSel.cfgCleanLambdaMinDcaPi); + if (isLambdaTopo) { + if (cfgSel.cfgGetV0CleanSample && cfgSel.cfgGetdEdx) { + histos.fill(HIST("hCleanLambda_MassVsPt"), v0.pt(), v0.mLambda()); + } + if (std::abs(negTrack.tpcNSigmaPi()) < cfgSel.cfgCleanV0TagTpcNsigma) { + fillV0Probe(2.0, posTrack, posTrack.tpcNSigmaPr(), posTrack.tofNSigmaPr(), posTrack.tpcExpSigmaPr(), false, true); + } + if (std::abs(posTrack.tpcNSigmaPr()) < cfgSel.cfgCleanV0TagTpcNsigma) { + fillV0Probe(3.0, negTrack, negTrack.tpcNSigmaPi(), negTrack.tofNSigmaPi(), negTrack.tpcExpSigmaPi(), cfgSel.cfgIncludeLambdaPiInCleanPi, false); + } + } + + bool isAntiLambdaTopo = (std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda) < cfgSel.cfgCleanLambdaMassWin) && + (std::abs(v0.dcapostopv()) >= cfgSel.cfgCleanLambdaMinDcaPi) && + (std::abs(v0.dcanegtopv()) >= cfgSel.cfgCleanLambdaMinDcaPr); + if (isAntiLambdaTopo) { + if (cfgSel.cfgGetV0CleanSample && cfgSel.cfgGetdEdx) { + histos.fill(HIST("hCleanLambda_MassVsPt"), v0.pt(), v0.mAntiLambda()); + } + if (std::abs(posTrack.tpcNSigmaPi()) < cfgSel.cfgCleanV0TagTpcNsigma) { + fillV0Probe(2.0, negTrack, negTrack.tpcNSigmaPr(), negTrack.tofNSigmaPr(), negTrack.tpcExpSigmaPr(), false, true); + } + if (std::abs(negTrack.tpcNSigmaPr()) < cfgSel.cfgCleanV0TagTpcNsigma) { + fillV0Probe(3.0, posTrack, posTrack.tpcNSigmaPi(), posTrack.tofNSigmaPi(), posTrack.tpcExpSigmaPi(), cfgSel.cfgIncludeLambdaPiInCleanPi, false); + } + } + } + } + + void processPIDQA(CollStandardTable const& rawCollisions, CollsTable const& lrCollisions, + TrksStandardTable const& standardTracks, aod::V0Datas const& V0s) + { + int64_t lrIdx = 0; + const int64_t nLrCols = lrCollisions.size(); + + for (auto const& rawCol : rawCollisions) { + histos.fill(HIST("hPIDQA_EventCount"), 1.0); + + bool matched = false; + float multiplicity = -1.0f; + if (nLrCols > 0 && lrIdx < nLrCols) { + if (std::abs(lrCollisions.iteratorAt(lrIdx).posZ() - rawCol.posZ()) <= 1e-5f) { + auto const& lrCol = lrCollisions.iteratorAt(lrIdx); + multiplicity = cfgSel.isUseCentEst ? lrCol.centrality() : lrCol.multiplicity(); + lrIdx++; + matched = true; + } + } + + if (!isEventSelected(rawCol)) { + continue; + } + histos.fill(HIST("hPIDQA_EventCount"), 2.0); + + if (matched) { + histos.fill(HIST("hPIDQA_EventCount"), 3.0); + } else if (nLrCols == 0 && cfgSel.cfgAllowFallbackMultInPIDQA) { + auto tracksForMult = standardTracks.sliceByCached(aod::track::collisionId, rawCol.globalIndex(), this->cache); + multiplicity = countFallbackMultiplicity(tracksForMult, rawCol.posZ()); + histos.fill(HIST("hPIDQA_EventCount"), 3.0); } else { - multiplicity = col.multiplicity(); + histos.fill(HIST("hPIDQA_EventCount"), 4.0); + continue; } - auto tracks = standardTracks.sliceByCached(aod::track::collisionId, col.globalIndex(), this->cache); + auto tracks = standardTracks.sliceByCached(aod::track::collisionId, rawCol.globalIndex(), this->cache); for (auto const& track : tracks) { - if (!track.isGlobalTrack()) + if (!track.isGlobalTrack()) { continue; - if (!myTrackFilter.IsSelected(track)) + } + if (!myTrackFilter.IsSelected(track)) { continue; - - if (!track.hasTOF()) + } + if (!track.hasTOF()) { continue; + } - float tpcNsig = 0.0f, tofNsig = 0.0f; - if (cfgSel.cfgPidMask & KPidMaskPion) { - tpcNsig = track.tpcNSigmaPi(); - tofNsig = track.tofNSigmaPi(); - } else if (cfgSel.cfgPidMask & KPidMaskKaon) { - tpcNsig = track.tpcNSigmaKa(); - tofNsig = track.tofNSigmaKa(); - } else if (cfgSel.cfgPidMask & KPidMaskProton) { - tpcNsig = track.tpcNSigmaPr(); - tofNsig = track.tofNSigmaPr(); + const float tpcNsigPi = track.tpcNSigmaPi(), tofNsigPi = track.tofNSigmaPi(), expSigPi = track.tpcExpSigmaPi(); + const float tpcNsigKa = track.tpcNSigmaKa(), tofNsigKa = track.tofNSigmaKa(), expSigKa = track.tpcExpSigmaKa(); + const float tpcNsigPr = track.tpcNSigmaPr(), tofNsigPr = track.tofNSigmaPr(), expSigPr = track.tpcExpSigmaPr(); + + const bool isTpcCleanPi = (tpcNsigPi > cfgSel.cfgCleanNsigmaTpcMin && tpcNsigPi < cfgSel.cfgCleanNsigmaTpcMax); + const bool isTpcCleanKa = (tpcNsigKa > cfgSel.cfgCleanNsigmaTpcMin && tpcNsigKa < cfgSel.cfgCleanNsigmaTpcMax); + const bool isTpcCleanPr = (tpcNsigPr > cfgSel.cfgCleanNsigmaTpcMin && tpcNsigPr < cfgSel.cfgCleanNsigmaTpcMax); + + const bool isTofCleanPi = (tofNsigPi > cfgSel.cfgCleanNsigmaTofMin && tofNsigPi < cfgSel.cfgCleanNsigmaTofMax) && + (std::abs(tofNsigKa) > cfgSel.cfgCleanTofVetoNsigma) && + (std::abs(tofNsigPr) > cfgSel.cfgCleanTofVetoNsigma); + const bool isTofCleanKa = (tofNsigKa > cfgSel.cfgCleanNsigmaTofMin && tofNsigKa < cfgSel.cfgCleanNsigmaTofMax) && + (std::abs(tofNsigPi) > cfgSel.cfgCleanTofVetoNsigma) && + (std::abs(tofNsigPr) > cfgSel.cfgCleanTofVetoNsigma); + const bool isTofCleanPr = (tofNsigPr > cfgSel.cfgCleanNsigmaTofMin && tofNsigPr < cfgSel.cfgCleanNsigmaTofMax) && + (std::abs(tofNsigPi) > cfgSel.cfgCleanTofVetoNsigma) && + (std::abs(tofNsigKa) > cfgSel.cfgCleanTofVetoNsigma); + + const bool isCleanPi = isTofCleanPi && isTpcCleanPi; + const bool isCleanKa = isTofCleanKa && isTpcCleanKa; + const bool isCleanPr = isTofCleanPr && isTpcCleanPr; + + if (cfgSel.cfgGetAllSpeciesQA && cfgSel.cfgGetdEdx) { + histos.fill(HIST("TpcdEdx_allSpecies_ptwise"), 1.0, multiplicity, track.pt(), track.tpcSignal(), tofNsigPi); + histos.fill(HIST("TpcdEdx_allSpecies_ptwise"), 2.0, multiplicity, track.pt(), track.tpcSignal(), tofNsigKa); + histos.fill(HIST("TpcdEdx_allSpecies_ptwise"), 3.0, multiplicity, track.pt(), track.tpcSignal(), tofNsigPr); + + histos.fill(HIST("ExpSigma_allSpecies_ptwise"), 1.0, multiplicity, track.pt(), expSigPi, tofNsigPi); + histos.fill(HIST("ExpSigma_allSpecies_ptwise"), 2.0, multiplicity, track.pt(), expSigKa, tofNsigKa); + histos.fill(HIST("ExpSigma_allSpecies_ptwise"), 3.0, multiplicity, track.pt(), expSigPr, tofNsigPr); + + if (isCleanPi) { + histos.fill(HIST("TpcdEdx_allSpecies_afterCut"), 1.0, multiplicity, track.pt(), track.tpcSignal(), tofNsigPi); + } + if (isCleanKa) { + histos.fill(HIST("TpcdEdx_allSpecies_afterCut"), 2.0, multiplicity, track.pt(), track.tpcSignal(), tofNsigKa); + } + if (isCleanPr) { + histos.fill(HIST("TpcdEdx_allSpecies_afterCut"), 3.0, multiplicity, track.pt(), track.tpcSignal(), tofNsigPr); + } } - // FILL BEFORE CUTS if (cfgSel.cfgGetNsigmaQA && (cfgSel.cfgPidMask == KPidMaskPion || cfgSel.cfgPidMask == KPidMaskKaon || cfgSel.cfgPidMask == KPidMaskProton)) { + float tpcNsig = 0.0f, tofNsig = 0.0f, expSig = 0.0f; + bool isTargetClean = false; + + if ((cfgSel.cfgPidMask & KPidMaskPion) != 0) { + tpcNsig = tpcNsigPi; + tofNsig = tofNsigPi; + expSig = expSigPi; + isTargetClean = isCleanPi; + } else if ((cfgSel.cfgPidMask & KPidMaskKaon) != 0) { + tpcNsig = tpcNsigKa; + tofNsig = tofNsigKa; + expSig = expSigKa; + isTargetClean = isCleanKa; + } else if ((cfgSel.cfgPidMask & KPidMaskProton) != 0) { + tpcNsig = tpcNsigPr; + tofNsig = tofNsigPr; + expSig = expSigPr; + isTargetClean = isCleanPr; + } + + const float expSignal = track.tpcSignal() - (tpcNsig * expSig); + histos.fill(HIST("TofTpcNsigma_before"), multiplicity, track.pt(), tpcNsig, tofNsig); if (cfgSel.cfgGetdEdx) { histos.fill(HIST("TpcdEdx_ptwise"), multiplicity, track.pt(), track.tpcSignal(), tofNsig); + histos.fill(HIST("ExpTpcdEdx_ptwise"), multiplicity, track.pt(), expSignal, tofNsig); + histos.fill(HIST("ExpSigma_ptwise"), multiplicity, track.pt(), expSig, tofNsig); } - } - - bool isTpcPion = (track.tpcNSigmaPi() > cfgSel.cfgCleanNsigmaTpcMin && track.tpcNSigmaPi() < cfgSel.cfgCleanNsigmaTpcMax); - bool isTpcKaon = (track.tpcNSigmaKa() > cfgSel.cfgCleanNsigmaTpcMin && track.tpcNSigmaKa() < cfgSel.cfgCleanNsigmaTpcMax); - bool isTpcProton = (track.tpcNSigmaPr() > cfgSel.cfgCleanNsigmaTpcMin && track.tpcNSigmaPr() < cfgSel.cfgCleanNsigmaTpcMax); - - bool isTofPion = (track.tofNSigmaPi() > cfgSel.cfgCleanNsigmaTofMin && track.tofNSigmaPi() < cfgSel.cfgCleanNsigmaTofMax); - bool isTofKaon = (track.tofNSigmaKa() > cfgSel.cfgCleanNsigmaTofMin && track.tofNSigmaKa() < cfgSel.cfgCleanNsigmaTofMax); - bool isTofProton = (track.tofNSigmaPr() > cfgSel.cfgCleanNsigmaTofMin && track.tofNSigmaPr() < cfgSel.cfgCleanNsigmaTofMax); - - bool isPion = isTofPion && isTpcPion; - bool isKaon = isTofKaon && isTpcKaon; - bool isProton = isTofProton && isTpcProton; - if ((isPion && isKaon) || (isPion && isProton) || (isKaon && isProton)) { - isPion = isKaon = isProton = false; + const bool useTrackCleanHere = !cfgSel.cfgFillAfterCutWithV0 || (cfgSel.cfgPidMask == KPidMaskKaon); + if (useTrackCleanHere && isTargetClean) { + histos.fill(HIST("TofTpcNsigma_after"), multiplicity, track.pt(), tpcNsig, tofNsig); + if (cfgSel.cfgGetdEdx) { + histos.fill(HIST("TpcdEdx_ptwise_afterCut"), multiplicity, track.pt(), track.tpcSignal(), tofNsig); + } + } } + } - bool isTargetParticle = ((cfgSel.cfgPidMask & KPidMaskPion) && isPion) || - ((cfgSel.cfgPidMask & KPidMaskKaon) && isKaon) || - ((cfgSel.cfgPidMask & KPidMaskProton) && isProton); - - // FILL AFTER CUTS - if (cfgSel.cfgGetNsigmaQA && isTargetParticle) { - histos.fill(HIST("TofTpcNsigma_after"), multiplicity, track.pt(), tpcNsig, tofNsig); - if (cfgSel.cfgGetdEdx) { - histos.fill(HIST("TpcdEdx_ptwise_afterCut"), multiplicity, track.pt(), track.tpcSignal(), tofNsig); - } + if ((cfgSel.cfgGetV0CleanSample || cfgSel.cfgFillAfterCutWithV0) && cfgSel.cfgGetdEdx) { + auto v0sThisCol = V0s.sliceByCached(aod::v0data::collisionId, rawCol.globalIndex(), this->cache); + for (auto const& v0 : v0sThisCol) { + auto const& posTrack = v0.template posTrack_as(); + auto const& negTrack = v0.template negTrack_as(); + fillCleanV0Daughters(rawCol, v0, posTrack, negTrack, multiplicity); } } } @@ -1294,52 +1423,32 @@ struct LongrangecorrDerived { PROCESS_SWITCH(LongrangecorrDerived, processTpcft0aME, "mixed event TPC vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processTpcft0cSE, "same event TPC vs FT0C", false); PROCESS_SWITCH(LongrangecorrDerived, processTpcft0cME, "mixed event TPC vs FT0C", false); - PROCESS_SWITCH(LongrangecorrDerived, processTpcmftSE, "same event TPC vs MFT", false); - PROCESS_SWITCH(LongrangecorrDerived, processTpcmftME, "mixed event TPC vs MFT", false); - PROCESS_SWITCH(LongrangecorrDerived, processMftft0aSE, "same event MFT vs FT0A", false); - PROCESS_SWITCH(LongrangecorrDerived, processMftft0aME, "mixed event MFT vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processV0ft0aSE, "same event V0 vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processV0ft0aME, "mixed event V0 vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processV0ft0cSE, "same event V0 vs FT0C", false); PROCESS_SWITCH(LongrangecorrDerived, processV0ft0cME, "mixed event V0 vs FT0C", false); PROCESS_SWITCH(LongrangecorrDerived, processV0tpcSE, "same event V0 vs TPC", false); PROCESS_SWITCH(LongrangecorrDerived, processV0tpcME, "mixed event V0 vs TPC", false); - PROCESS_SWITCH(LongrangecorrDerived, processV0mftSE, "same event V0 vs MFT", false); - PROCESS_SWITCH(LongrangecorrDerived, processV0mftME, "mixed event V0 vs MFT", false); PROCESS_SWITCH(LongrangecorrDerived, processFt0aft0cSE, "same event FT0A vs FT0C", false); PROCESS_SWITCH(LongrangecorrDerived, processFt0aft0cME, "mixed event FT0A vs FT0C", false); PROCESS_SWITCH(LongrangecorrDerived, processUpcTpcft0aSE, "same UPC event TPC vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processUpcTpcft0aME, "mixed UPC event TPC vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processUpcTpcft0cSE, "same UPC event TPC vs FT0C", false); PROCESS_SWITCH(LongrangecorrDerived, processUpcTpcft0cME, "mixed UPC event TPC vs FT0C", false); - PROCESS_SWITCH(LongrangecorrDerived, processUpcTpcmftSE, "same UPC event TPC vs MFT", false); - PROCESS_SWITCH(LongrangecorrDerived, processUpcTpcmftME, "mixed UPC event TPC vs MFT", false); - PROCESS_SWITCH(LongrangecorrDerived, processUpcMftft0aSE, "same UPC event MFT vs FT0A", false); - PROCESS_SWITCH(LongrangecorrDerived, processUpcMftft0aME, "mixed UPC event MFT vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processUpcV0ft0aSE, "same UPC event V0 vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processUpcV0ft0aME, "mixed UPC event V0 vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processUpcV0ft0cSE, "same UPC event V0 vs FT0C", false); PROCESS_SWITCH(LongrangecorrDerived, processUpcV0ft0cME, "mixed UPC event V0 vs FT0C", false); - PROCESS_SWITCH(LongrangecorrDerived, processUpcV0mftSE, "same UPC event V0 vs MFT", false); - PROCESS_SWITCH(LongrangecorrDerived, processUpcV0mftME, "mixed UPC event V0 vs MFT", false); PROCESS_SWITCH(LongrangecorrDerived, processMcTpcft0aSE, "same MC event TPC vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processMcTpcft0aME, "mixed MC event TPC vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processMcTpcft0cSE, "same MC event TPC vs FT0C", false); PROCESS_SWITCH(LongrangecorrDerived, processMcTpcft0cME, "mixed MC event TPC vs FT0C", false); - PROCESS_SWITCH(LongrangecorrDerived, processMcTpcmftSE, "same MC event TPC vs MFT", false); - PROCESS_SWITCH(LongrangecorrDerived, processMcTpcmftME, "mixed MC event TPC vs MFT", false); - PROCESS_SWITCH(LongrangecorrDerived, processMcMftft0aSE, "same MC event MFT vs FT0A", false); - PROCESS_SWITCH(LongrangecorrDerived, processMcMftft0aME, "mixed MC event MFT vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processMcFt0aft0cSE, "same MC event FT0A vs FT0C", false); PROCESS_SWITCH(LongrangecorrDerived, processMcFt0aft0cME, "mixed MC event FT0A vs FT0C", false); PROCESS_SWITCH(LongrangecorrDerived, processMcGenTpcft0aSE, "same MC gen event TPC vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processMcGenTpcft0aME, "mixed MC gen event TPC vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processMcGenTpcft0cSE, "same MC gen event TPC vs FT0C", false); PROCESS_SWITCH(LongrangecorrDerived, processMcGenTpcft0cME, "mixed MC gen event TPC vs FT0C", false); - PROCESS_SWITCH(LongrangecorrDerived, processMcGenTpcmftSE, "same MC gen event TPC vs MFT", false); - PROCESS_SWITCH(LongrangecorrDerived, processMcGenTpcmftME, "mixed MC gen event TPC vs MFT", false); - PROCESS_SWITCH(LongrangecorrDerived, processMcGenMftft0aSE, "same MC gen event MFT vs FT0A", false); - PROCESS_SWITCH(LongrangecorrDerived, processMcGenMftft0aME, "mixed MC gen event MFT vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processMcGenV0ft0aSE, "same MC gen event V0 vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processMcGenV0ft0aME, "mixed MC gen event V0 vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processMcGenV0ft0cSE, "same MC gen event V0 vs FT0C", false); @@ -1350,8 +1459,8 @@ struct LongrangecorrDerived { PROCESS_SWITCH(LongrangecorrDerived, processPIDQA, "process PID QA on standard AODs", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(context)}; } diff --git a/PWGCF/TwoParticleCorrelations/Tasks/mftcorrelations.cxx b/PWGCF/TwoParticleCorrelations/Tasks/mftcorrelations.cxx new file mode 100644 index 00000000000..e34bd64f3a9 --- /dev/null +++ b/PWGCF/TwoParticleCorrelations/Tasks/mftcorrelations.cxx @@ -0,0 +1,1449 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file mftcorrelations.cxx +/// \brief task for two-particle (hadron-hadron) angular correlations built from MFT tracks + +/// \author Mintu Haldar + +#include "PWGCF/Core/CorrelationContainer.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // Pair<>, SameKindPair<> +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace constants::math; + +// Bitmask describing which multiplicity/centrality estimators feed the "multCorrelations" QA +// histogram and, when set, the outlier-rejection formula. +namespace o2::aod +{ +namespace mftmultset +{ +DECLARE_SOA_COLUMN(Multiplicities, multiplicities, std::vector); //! List of auxiliary multiplicities +enum MultiplicityEstimators : uint16_t { + CentFT0C = 1 << 0, + MultFV0A = 1 << 1, + MultNTracksPV = 1 << 2, + MultNTracksGlobal = 1 << 3, + CentFT0M = 1 << 4 +}; +inline constexpr uint32_t NMultiplicityEstimators = __builtin_ctz(CentFT0M) + 1; +} // namespace mftmultset + +DECLARE_SOA_TABLE(MFTMultSets, "AOD", "MFTMULTSET", mftmultset::Multiplicities); //! Auxilary multiplicity set table +using MFTMultSet = MFTMultSets::iterator; +} // namespace o2::aod + +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; + +struct Mftcorrelations { + SliceCache cache; + + // ------------------------------------------------------------------ Configurables + O2_DEFINE_CONFIGURABLE(cfgCutVertex, float, 10.0f, "Accepted z-vertex range") + + // Global acceptance + O2_DEFINE_CONFIGURABLE(cfgGlobalTrackEtaCut, float, 0.8f, "Eta cut for global (central-barrel) tracks used as MFT-Global trigger") + O2_DEFINE_CONFIGURABLE(cfgGlobalTrackPtMin, float, 0.15f, "Minimal pT for global tracks") + O2_DEFINE_CONFIGURABLE(cfgGlobalTrackPtMax, float, 10.0f, "Maximal pT for global tracks") + + // MFT acceptance is roughly -3.6 < eta < -2.5; keep as configurables since it is tag/setup dependent. + O2_DEFINE_CONFIGURABLE(cfgMFTCutEtaMin, float, -3.6f, "Minimal eta for MFT tracks") + O2_DEFINE_CONFIGURABLE(cfgMFTCutEtaMax, float, -2.5f, "Maximal eta for MFT tracks") + O2_DEFINE_CONFIGURABLE(cfgMFTCutPtMin, float, 0.5f, "Minimal pT for MFT tracks") + O2_DEFINE_CONFIGURABLE(cfgMFTCutPtMax, float, 20.0f, "Maximal pT for MFT tracks") + O2_DEFINE_CONFIGURABLE(cfgMFTMinNClusters, int, 5, "Minimal number of MFT clusters on track") + + O2_DEFINE_CONFIGURABLE(cfgPtOrder, int, 1, "Only consider pairs for which pT,1 < pT,2 (0 = OFF, 1 = ON)") + O2_DEFINE_CONFIGURABLE(cfgTriggerCharge, int, 0, "Select on charge of trigger particle: 0 = all; 1 = positive; -1 = negative") + O2_DEFINE_CONFIGURABLE(cfgAssociatedCharge, int, 0, "Select on charge of associated particle: 0 = all; 1 = positive; -1 = negative") + O2_DEFINE_CONFIGURABLE(cfgPairCharge, int, 0, "Select on charge of the pair: 0 = all; 1 = like sign; -1 = unlike sign") + + O2_DEFINE_CONFIGURABLE(cfgLocalEfficiency, int, 0, "0 = OFF and 1 = ON: read efficiency THn from a local ROOT file instead of CCDB") + O2_DEFINE_CONFIGURABLE(cfgDropStepRECO, bool, false, "drop step RECO if efficiency correction is used") + O2_DEFINE_CONFIGURABLE(cfgEfficiencyTrigger, std::string, "", "CCDB path (or local file if cfgLocalEfficiency=1) to efficiency object for trigger particles") + O2_DEFINE_CONFIGURABLE(cfgEfficiencyAssociated, std::string, "", "CCDB path (or local file if cfgLocalEfficiency=1) to efficiency object for associated particles") + // FIX #6d: efficiency for central-barrel (global) tracks, used in MFT-Global (trigger) and Global-Global channels. + O2_DEFINE_CONFIGURABLE(cfgEfficiencyGlobal, std::string, "", "CCDB path (or local file if cfgLocalEfficiency=1) to efficiency object for central-barrel (global) tracks") + + O2_DEFINE_CONFIGURABLE(cfgMultCorrelationsMask, uint16_t, 0, "Bitmask of multiplicity/centrality estimators to fill in multCorrelations QA / use for outlier rejection (see mftmultset enum)") + O2_DEFINE_CONFIGURABLE(cfgMultCutFormula, std::string, "", "Multiplicity outlier-rejection formula. A result > 0 accepts the event. Parameters: [cFT0C], [mFV0A], [mPV], [mGlob], [cFT0M]") + + O2_DEFINE_CONFIGURABLE(cfgNoMixedEvents, int, 5, "Number of mixed events per event") + O2_DEFINE_CONFIGURABLE(cfgVerbosity, int, 1, "Verbosity level (0 = major, 1 = per collision)") + + // ------------------------------------------------------------------ Phi weight + // NOTE: keep OFF (empty file) when the efficiency object already has a phi axis, otherwise phi is corrected twice. + O2_DEFINE_CONFIGURABLE(cfgPhiWeightFile, std::string, "", "Local ROOT file with the phi-weight TH1 (empty = disabled)") + O2_DEFINE_CONFIGURABLE(cfgPhiWeightName, std::string, "", "Name of the phi-weight TH1 object inside cfgPhiWeightFile") + + // ------------------------------------------------------------------ Axes + ConfigurableAxis axisVertex{"axisVertex", {10, -10, 10}, "vertex axis for histograms"}; + ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {72, -PIHalf, PIHalf * 3}, "delta phi axis for histograms"}; + ConfigurableAxis axisDeltaEta{"axisDeltaEta", {40, -2.2, 2.2}, "delta eta axis for histograms (MFT-MFT so somewhat narrower than TPC-TPC)"}; + ConfigurableAxis axisPtTrigger{"axisPtTrigger", {VARIABLE_WIDTH, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.5, 2.0, 3.0}, "pt trigger axis for histograms"}; + ConfigurableAxis axisPtAssoc{"axisPtAssoc", {VARIABLE_WIDTH, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.5, 2.0}, "pt associated axis for histograms"}; + ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 100.1}, "multiplicity / centrality axis for histograms"}; + + ConfigurableAxis axisVertexEfficiency{"axisVertexEfficiency", {10, -10, 10}, "vertex axis for efficiency histograms"}; + ConfigurableAxis axisEtaEfficiency{"axisEtaEfficiency", {20, -3.6, -2.5}, "eta axis for efficiency histograms (MFT acceptance)"}; + ConfigurableAxis axisPtEfficiency{"axisPtEfficiency", {VARIABLE_WIDTH, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.8, 1.0, 1.5, 2.0, 3.0}, "pt axis for efficiency histograms"}; + ConfigurableAxis axisPhiEfficiency{"axisPhiEfficiency", {36, 0, TwoPI}, "phi axis for efficiency histograms"}; + + ConfigurableAxis axisEtaEfficiencyGlobal{"axisEtaEfficiencyGlobal", {16, -0.8, 0.8}, "eta axis for efficiency histograms (global tracks)"}; + ConfigurableAxis axisPtEfficiencyGlobal{"axisPtEfficiencyGlobal", {VARIABLE_WIDTH, 0.0, 0.15, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.25, 1.5, 1.75, 2.0, 2.5, 3.0, 4.0, 5.0, 6.0, 8.0, 10.0, 20.0}, "pt axis for efficiency histograms (global tracks)"}; + + ConfigurableAxis axisMultCorrCent{"axisMultCorrCent", {100, 0, 100}, "multiplicity correlation axis for centralities"}; + ConfigurableAxis axisMultCorrV0{"axisMultCorrV0", {1000, 0, 100000}, "multiplicity correlation axis for V0 multiplicities"}; + ConfigurableAxis axisMultCorrMult{"axisMultCorrMult", {1000, 0, 1000}, "multiplicity correlation axis for track multiplicities"}; + + // ------------------------------------------------------------------ QA Axes + ConfigurableAxis axisMultQA{"axisMultQA", {100, 0, 100}, "multiplicity/centrality axis for QA histograms"}; + ConfigurableAxis axisPtQA{"axisPtQA", {VARIABLE_WIDTH, 0.0, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 8.0, 10.0, 15.0, 20.0}, "pT axis for QA histograms (yields)"}; + ConfigurableAxis axisEtaQA{"axisEtaQA", {50, -4, -2}, "eta axis for QA histograms"}; + ConfigurableAxis axisPhiQA{"axisPhiQA", {200, 0, TwoPI}, "phi axis for QA histograms"}; + + // Global MFT + ConfigurableAxis axisEtaGlobalQA{"axisEtaGlobalQA", {40, -1, 1}, "eta axis for global-track QA histograms"}; + ConfigurableAxis axisDeltaEtaMftGlobal{"axisDeltaEtaMftGlobal", {60, 1.0, 5.0}, "delta eta axis for MFT-Global correlations (deltaEta = etaGlobal - etaMFT is POSITIVE, roughly 1.7-4.4); override via JSON if needed"}; + // FIX #4: delta-eta axis for the global-global (central-barrel hadron-hadron) channel. + ConfigurableAxis axisDeltaEtaGlobal{"axisDeltaEtaGlobal", {32, -1.6, 1.6}, "delta eta axis for global-global (central-barrel hadron-hadron) correlations"}; + + // ------------------------------------------------------------------ Event selection + Filter collisionZVtxFilter = nabs(aod::collision::posZ) < cfgCutVertex; + + // ------------------------------------------------------------------ MFT track selection + Filter mftTrackFilter = (aod::fwdtrack::eta > cfgMFTCutEtaMin) && (aod::fwdtrack::eta < cfgMFTCutEtaMax) && + (aod::fwdtrack::pt > cfgMFTCutPtMin) && (aod::fwdtrack::pt < cfgMFTCutPtMax); + + // ------------------------------------------------------------------ Global track selection + Filter globalTrackFilter = (nabs(aod::track::eta) < cfgGlobalTrackEtaCut) && + (aod::track::pt > cfgGlobalTrackPtMin) && (aod::track::pt < cfgGlobalTrackPtMax) && + requireGlobalTrackInFilter(); + + // ------------------------------------------------------------------ Output + OutputObj same{"sameEvent"}; + OutputObj mixed{"mixedEvent"}; + + OutputObj sameMftGlobal{"sameEventMftGlobal"}; + OutputObj mixedMftGlobal{"mixedEventMftGlobal"}; + + // FIX #4: global-global (central-barrel hadron-hadron) output objects. + OutputObj sameGlobal{"sameEventGlobalGlobal"}; + OutputObj mixedGlobal{"mixedEventGlobalGlobal"}; + + HistogramRegistry registry{"registry"}; + + Service ccdb; + Service pdg; + + struct Config { + THn* mEfficiencyTrigger = nullptr; + THn* mEfficiencyAssociated = nullptr; + THn* mEfficiencyGlobal = nullptr; // FIX #6d + TH1* mPhiWeight = nullptr; + bool efficiencyLoaded = false; + } cfg; + + std::vector efficiencyAssociatedCache; + std::vector phiWeightCache; + std::vector phiWeightCache1; + std::unique_ptr multCutFormula; + std::array multCutFormulaParamIndex{}; + + // Collisions are expected to already carry the joined centrality/multiplicity estimator tables. + using ExtendedCollisions = soa::Join; + using FilteredCollisions = soa::Filtered; + using FilteredMFTTracks = soa::Filtered; + using FilteredGlobalTracks = soa::Filtered>; + + // For MFT MC Efficiency + // NOTE SmallGroups includes soa::Filtered always + using McEffCollisions = soa::SmallGroups>; + using McEffMFTTracks = soa::Filtered>; + + // For Global MC Efficiency + using McEffGlobalTracks = soa::Filtered>; + + void init(o2::framework::InitContext&) + { + registry.add("yields", "multiplicity/centrality vs pT vs eta", {HistType::kTH3F, {{axisMultQA, "multiplicity/centrality"}, {axisPtQA, "p_{T}"}, {axisEtaQA, "#eta"}}}); + registry.add("etaphi", "multiplicity/centrality vs eta vs phi", {HistType::kTH3F, {{axisMultQA, "multiplicity/centrality"}, {axisEtaQA, "#eta"}, {axisPhiQA, "#varphi"}}}); + // FIX #6b: efficiency-corrected QA histograms (weight = efficiency correction factor) + registry.add("yields_corrected", "efficiency-corrected multiplicity/centrality vs pT vs eta", {HistType::kTH3F, {{axisMultQA, "multiplicity/centrality"}, {axisPtQA, "p_{T}"}, {axisEtaQA, "#eta"}}}); + registry.add("etaphi_corrected", "efficiency-corrected multiplicity/centrality vs eta vs phi", {HistType::kTH3F, {{axisMultQA, "multiplicity/centrality"}, {axisEtaQA, "#eta"}, {axisPhiQA, "#varphi"}}}); + registry.add("multiplicity", "event multiplicity", {HistType::kTH1F, {{axisMultQA, "multiplicity/centrality"}}}); + registry.add("nClusters", "MFT track number of clusters", {HistType::kTH1F, {{12, -0.5, 11.5, "n clusters"}}}); + registry.add("multiplicity_raw", "event multiplicity (no cuts)", {HistType::kTH1F, {{axisMultQA, "multiplicity/centrality"}}}); + + // MC Efficiency + registry.add("MCEff/hNRecoColl", "N reco collisions per MC collision", {HistType::kTH1F, {{6, -0.5, 5.5}}}); + registry.add("MCEff/hCutFlow", "1=all MC coll, 2=size==1, 3=sel8", {HistType::kTH1F, {{3, 0.5, 3.5}}}); + + registry.add("MCEffGlobal/hNRecoColl", "N reco collisions per MC collision (global)", {HistType::kTH1F, {{6, -0.5, 5.5}}}); + registry.add("MCEffGlobal/hCutFlow", "1=all MC coll, 2=size==1, 3=sel8", {HistType::kTH1F, {{3, 0.5, 3.5}}}); + + if (cfgMultCorrelationsMask != 0) { + std::vector multAxes; + if (cfgMultCorrelationsMask & aod::mftmultset::CentFT0C) + multAxes.emplace_back(axisMultCorrCent, "FT0C centrality"); + if (cfgMultCorrelationsMask & aod::mftmultset::MultFV0A) + multAxes.emplace_back(axisMultCorrV0, "V0A multiplicity"); + if (cfgMultCorrelationsMask & aod::mftmultset::MultNTracksPV) + multAxes.emplace_back(axisMultCorrMult, "Nch PV"); + if (cfgMultCorrelationsMask & aod::mftmultset::MultNTracksGlobal) + multAxes.emplace_back(axisMultCorrMult, "Nch Global"); + if (cfgMultCorrelationsMask & aod::mftmultset::CentFT0M) + multAxes.emplace_back(axisMultCorrCent, "FT0M centrality"); + registry.add("multCorrelations", "Multiplicity correlations", {HistType::kTHnSparseF, multAxes}); + + // FIX #3 + if (!(cfgMultCorrelationsMask & aod::mftmultset::CentFT0C)) { + LOGF(warning, + "cfgMultCorrelationsMask=%d does not include CentFT0C. Mixed-event binning follows " + "getMultiplicity(), so make sure the efficiency objects were produced with the same estimator.", + (int)cfgMultCorrelationsMask); + } + } + + const int maxMixBin = AxisSpec(axisMultiplicity).getNbins() * AxisSpec(axisVertex).getNbins(); + registry.add("eventcount_same", "bin", {HistType::kTH1F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}}}); + registry.add("eventcount_mixed", "bin", {HistType::kTH1F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}}}); + registry.add("trackcount_same", "bin", {HistType::kTH2F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}, {50, -0.5, 49.5}}}); + registry.add("trackcount_mixed", "bin", {HistType::kTH3F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}, {50, -0.5, 49.5}, {50, -0.5, 49.5}}}); + + // FIX #1 + registry.add("eventcount_mftglobal_same", "bin", {HistType::kTH1F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}}}); + registry.add("eventcount_mftglobal_mixed", "bin", {HistType::kTH1F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}}}); + registry.add("trackcount_mftglobal_same", "bin", {HistType::kTH3F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}, {50, -0.5, 49.5}, {50, -0.5, 49.5}}}); + registry.add("trackcount_mftglobal_mixed", "bin", {HistType::kTH3F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}, {50, -0.5, 49.5}, {50, -0.5, 49.5}}}); + + if (!cfgMultCutFormula.value.empty()) { + multCutFormula = std::make_unique("multCutFormula", cfgMultCutFormula.value.c_str()); + std::fill_n(multCutFormulaParamIndex.begin(), std::size(multCutFormulaParamIndex), ~0u); + std::array pars = {"cFT0C", "mFV0A", "mPV", "mGlob", "cFT0M"}; + for (uint32_t i = 0, n = multCutFormula->GetNpar(); i < n; ++i) { + auto m = std::find(pars.begin(), pars.end(), multCutFormula->GetParName(i)); + if (m == pars.end()) { + LOGF(warning, "Unknown parameter in cfgMultCutFormula: %s", multCutFormula->GetParName(i)); + continue; + } + multCutFormulaParamIndex[std::distance(pars.begin(), m)] = i; + } + } + + registry.add("mftmft/SE/deltaEta_deltaPhi", "same event #Delta#eta-#Delta#varphi (MFT-MFT)", {HistType::kTH2F, {{axisDeltaPhi, "#Delta#varphi"}, {axisDeltaEta, "#Delta#eta"}}}); + registry.add("mftmft/ME/deltaEta_deltaPhi", "mixed event #Delta#eta-#Delta#varphi (MFT-MFT)", {HistType::kTH2F, {{axisDeltaPhi, "#Delta#varphi"}, {axisDeltaEta, "#Delta#eta"}}}); + + registry.add("mftGlobal/SE/Trig_etavsphi", "trigger (global) eta vs phi", {HistType::kTH2F, {{axisPhiQA, "#varphi"}, {axisEtaGlobalQA, "#eta"}}}); + registry.add("mftGlobal/SE/Trig_pt", "trigger (global) pT", {HistType::kTH1F, {{axisPtTrigger, "p_{T}"}}}); + registry.add("mftGlobal/SE/Assoc_etavsphi", "associated (MFT) eta vs phi", {HistType::kTH2F, {{axisPhiQA, "#varphi"}, {axisEtaQA, "#eta"}}}); + registry.add("mftGlobal/SE/Assoc_pt", "associated (MFT) pT", {HistType::kTH1F, {{axisPtAssoc, "p_{T}"}}}); + registry.add("mftGlobal/SE/deltaEta_deltaPhi", "same event #Delta#eta-#Delta#varphi", {HistType::kTH2F, {{axisDeltaPhi, "#Delta#varphi"}, {axisDeltaEtaMftGlobal, "#Delta#eta"}}}); + registry.add("mftGlobal/ME/deltaEta_deltaPhi", "mixed event #Delta#eta-#Delta#varphi", {HistType::kTH2F, {{axisDeltaPhi, "#Delta#varphi"}, {axisDeltaEtaMftGlobal, "#Delta#eta"}}}); + + // FIX #4: QA histograms for the global-global channel. + registry.add("eventcount_global_same", "bin", {HistType::kTH1F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}}}); + registry.add("eventcount_global_mixed", "bin", {HistType::kTH1F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}}}); + registry.add("trackcount_global_same", "bin", {HistType::kTH2F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}, {50, -0.5, 49.5}}}); + registry.add("trackcount_global_mixed", "bin", {HistType::kTH3F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}, {50, -0.5, 49.5}, {50, -0.5, 49.5}}}); + + registry.add("global/SE/etavsphi", "global track eta vs phi", {HistType::kTH2F, {{axisPhiQA, "#varphi"}, {axisEtaGlobalQA, "#eta"}}}); + registry.add("global/SE/pt", "global track pT", {HistType::kTH1F, {{axisPtQA, "p_{T}"}}}); + registry.add("global/SE/deltaEta_deltaPhi", "same event #Delta#eta-#Delta#varphi (global-global)", {HistType::kTH2F, {{axisDeltaPhi, "#Delta#varphi"}, {axisDeltaEtaGlobal, "#Delta#eta"}}}); + registry.add("global/ME/deltaEta_deltaPhi", "mixed event #Delta#eta-#Delta#varphi (global-global)", {HistType::kTH2F, {{axisDeltaPhi, "#Delta#varphi"}, {axisDeltaEtaGlobal, "#Delta#eta"}}}); + + std::vector corrAxis = {{axisDeltaEta, "#Delta#eta"}, + {axisPtAssoc, "p_{T} (GeV/c)"}, + {axisPtTrigger, "p_{T} (GeV/c)"}, + {axisMultiplicity, "multiplicity / centrality"}, + {axisDeltaPhi, "#Delta#varphi (rad)"}, + {axisVertex, "z-vtx (cm)"}}; + std::vector effAxis = {{axisEtaEfficiency, "#eta"}, + {axisPtEfficiency, "p_{T} (GeV/c)"}, + {axisVertexEfficiency, "z-vtx (cm)"}, + {axisPhiEfficiency, "#varphi (rad)"}}; + + std::vector effAxisGlobal = {{axisEtaEfficiencyGlobal, "#eta"}, + {axisPtEfficiencyGlobal, "p_{T} (GeV/c)"}, + {axisVertexEfficiency, "z-vtx (cm)"}, + {axisPhiEfficiency, "#varphi (rad)"}}; + + std::vector userAxis; // no extra axis needed for hadron-hadron + + std::vector corrAxisMftGlobal = {{axisDeltaEtaMftGlobal, "#Delta#eta"}, + {axisPtAssoc, "p_{T} (GeV/c)"}, + {axisPtTrigger, "p_{T} (GeV/c)"}, + {axisMultiplicity, "multiplicity / centrality"}, + {axisDeltaPhi, "#Delta#varphi (rad)"}, + {axisVertex, "z-vtx (cm)"}}; + + // FIX #4 + std::vector corrAxisGlobal = {{axisDeltaEtaGlobal, "#Delta#eta"}, + {axisPtAssoc, "p_{T} (GeV/c)"}, + {axisPtTrigger, "p_{T} (GeV/c)"}, + {axisMultiplicity, "multiplicity / centrality"}, + {axisDeltaPhi, "#Delta#varphi (rad)"}, + {axisVertex, "z-vtx (cm)"}}; + + sameMftGlobal.setObject(new CorrelationContainer("sameEventMftGlobal", "sameEventMftGlobal", corrAxisMftGlobal, effAxis, userAxis)); + mixedMftGlobal.setObject(new CorrelationContainer("mixedEventMftGlobal", "mixedEventMftGlobal", corrAxisMftGlobal, effAxis, userAxis)); + + same.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxis, effAxis, userAxis)); + mixed.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxis, effAxis, userAxis)); + + same->setTrackEtaCut(cfgMFTCutEtaMax); // upper bound of |eta| cut used internally by CorrelationContainer + + sameGlobal.setObject(new CorrelationContainer("sameEventGlobalGlobal", "sameEventGlobalGlobal", corrAxisGlobal, effAxisGlobal, userAxis)); + mixedGlobal.setObject(new CorrelationContainer("mixedEventGlobalGlobal", "mixedEventGlobalGlobal", corrAxisGlobal, effAxisGlobal, userAxis)); + sameGlobal->setTrackEtaCut(cfgGlobalTrackEtaCut); + + if (!cfgEfficiencyAssociated.value.empty()) + efficiencyAssociatedCache.reserve(512); + + // FIX #5: load the phi weight once at init(). + loadPhiWeight(); + + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + ccdb->setCreatedNotAfter(now); + } + + // ------------------------------------------------------------------ helpers + + int getMagneticField(uint64_t timestamp) + { + static o2::parameters::GRPObject* grpo = nullptr; + if (grpo == nullptr) { + grpo = ccdb->getForTimeStamp("GLO/GRP/GRP", timestamp); + if (grpo == nullptr) { + LOGF(fatal, "GRP object not found for timestamp %llu", timestamp); + return 0; + } + } + return grpo->getNominalL3Field(); + } + + void loadEfficiency(uint64_t timestamp) + { + if (cfg.efficiencyLoaded) { + return; + } + if (!cfgEfficiencyTrigger.value.empty()) { + if (cfgLocalEfficiency > 0) { + TFile* f = TFile::Open(cfgEfficiencyTrigger.value.c_str(), "READ"); + cfg.mEfficiencyTrigger = dynamic_cast(f->Get("ccdb_object")); + } else { + cfg.mEfficiencyTrigger = ccdb->getForTimeStamp>(cfgEfficiencyTrigger, timestamp); + } + if (cfg.mEfficiencyTrigger == nullptr) { + LOGF(fatal, "Could not load trigger efficiency from %s", cfgEfficiencyTrigger.value.c_str()); + } + LOGF(info, "Loaded MFT trigger efficiency from %s (%p)", cfgEfficiencyTrigger.value.c_str(), static_cast(cfg.mEfficiencyTrigger)); + } + if (!cfgEfficiencyAssociated.value.empty()) { + if (cfgLocalEfficiency > 0) { + TFile* f = TFile::Open(cfgEfficiencyAssociated.value.c_str(), "READ"); + cfg.mEfficiencyAssociated = dynamic_cast(f->Get("ccdb_object")); + } else { + cfg.mEfficiencyAssociated = ccdb->getForTimeStamp>(cfgEfficiencyAssociated, timestamp); + } + if (cfg.mEfficiencyAssociated == nullptr) { + LOGF(fatal, "Could not load associated efficiency from %s", cfgEfficiencyAssociated.value.c_str()); + } + LOGF(info, "Loaded MFT associated efficiency from %s (%p)", cfgEfficiencyAssociated.value.c_str(), static_cast(cfg.mEfficiencyAssociated)); + } + // FIX #6d + if (!cfgEfficiencyGlobal.value.empty()) { + if (cfgLocalEfficiency > 0) { + TFile* f = TFile::Open(cfgEfficiencyGlobal.value.c_str(), "READ"); + cfg.mEfficiencyGlobal = dynamic_cast(f->Get("ccdb_object")); + } else { + cfg.mEfficiencyGlobal = ccdb->getForTimeStamp>(cfgEfficiencyGlobal, timestamp); + } + if (cfg.mEfficiencyGlobal == nullptr) { + LOGF(fatal, "Could not load global-track efficiency from %s", cfgEfficiencyGlobal.value.c_str()); + } + LOGF(info, "Loaded global-track efficiency from %s (%p)", cfgEfficiencyGlobal.value.c_str(), static_cast(cfg.mEfficiencyGlobal)); + } + cfg.efficiencyLoaded = true; + } + + // MFT phi is in [-pi,pi], McParticle/global-track phi in [0,2pi). Use [0,2pi) everywhere. + static float wrapPhi(float phi) + { + return RecoDecay::constrainAngle(phi, 0.f); + } + + // FIX #6a: expected axis order of the efficiency object: (eta, pt, multiplicity, posZ [, phi]). + // - phi is wrapped to [0, 2pi) (MFT track.phi() is in [-pi, pi]) + // - 4-dim and 5-dim objects are supported + // - bins outside the axis range (under/overflow) return 0 = track outside efficiency acceptance + double getEfficiencyCorrection(THn* eff, float eta, float pt, float multiplicity, float posZ, float phi) + { + if (eff == nullptr) { + return 1.0; + } + constexpr int NDimWithoutPhi = 4; // eta, pt, mult, z + constexpr int NDimWithPhi = 5; // eta, pt, mult, z, phi + const int nDim = eff->GetNdimensions(); + if (nDim != NDimWithoutPhi && nDim != NDimWithPhi) { + LOGF(fatal, "Efficiency object has %d dimensions, expected %d (eta,pt,mult,z) or %d (+phi)", nDim, NDimWithoutPhi, NDimWithPhi); + } + const double vals[NDimWithPhi] = {eta, pt, multiplicity, posZ, static_cast(RecoDecay::constrainAngle(phi, 0.f))}; + int bins[NDimWithPhi]; + for (int i = 0; i < nDim; ++i) { + TAxis* ax = eff->GetAxis(i); + bins[i] = ax->FindBin(vals[i]); + if (bins[i] < 1 || bins[i] > ax->GetNbins()) { + return 0.0; + } + } + return eff->GetBinContent(bins); + } + + template + using HasSign = decltype(std::declval().sign()); + template + using HasPdgCode = decltype(std::declval().pdgCode()); + template + using HasIsPhysicalPrimary = decltype(std::declval().isPhysicalPrimary()); + + // Retrieve the multiplicity/centrality estimator selected by cfgMultCorrelationsMask. + // Falls back to FT0C centrality if no mask bit is set. + template + float getMultiplicity(TCollision const& collision) + { + if (cfgMultCorrelationsMask == 0) { + return collision.centFT0C(); + } + if (cfgMultCorrelationsMask & aod::mftmultset::CentFT0C) + return collision.centFT0C(); + if (cfgMultCorrelationsMask & aod::mftmultset::CentFT0M) + return collision.centFT0M(); + return collision.centFT0C(); + } + + template + bool passOutlier(TCollision const& collision) + { + if (cfgMultCutFormula.value.empty() || multCutFormula == nullptr) + return true; + if (multCutFormulaParamIndex[0] != ~0u) + multCutFormula->SetParameter(multCutFormulaParamIndex[0], collision.centFT0C()); + if (multCutFormulaParamIndex[1] != ~0u) + multCutFormula->SetParameter(multCutFormulaParamIndex[1], collision.multFV0A()); + if (multCutFormulaParamIndex[2] != ~0u) + multCutFormula->SetParameter(multCutFormulaParamIndex[2], collision.multNTracksPV()); + if (multCutFormulaParamIndex[3] != ~0u) + multCutFormula->SetParameter(multCutFormulaParamIndex[3], collision.multNTracksPV()); // substitute a "global" estimator if/when available + if (multCutFormulaParamIndex[4] != ~0u) + multCutFormula->SetParameter(multCutFormulaParamIndex[4], collision.centFT0M()); + return multCutFormula->Eval() > 0.0f; + } + + template + void fillMultCorrelations(TCollision const& collision) + { + if (cfgMultCorrelationsMask == 0) + return; + std::vector v; + if (cfgMultCorrelationsMask & aod::mftmultset::CentFT0C) + v.push_back(collision.centFT0C()); + if (cfgMultCorrelationsMask & aod::mftmultset::MultFV0A) + v.push_back(collision.multFV0A()); + if (cfgMultCorrelationsMask & aod::mftmultset::MultNTracksPV) + v.push_back(collision.multNTracksPV()); + if (cfgMultCorrelationsMask & aod::mftmultset::MultNTracksGlobal) + v.push_back(collision.multNTracksPV()); // substitute a "global" estimator if/when available + if (cfgMultCorrelationsMask & aod::mftmultset::CentFT0M) + v.push_back(collision.centFT0M()); + registry.get(HIST("multCorrelations")).get()->Fill(v.data()); + } + + void loadPhiWeight() + { + if (cfg.mPhiWeight != nullptr || cfgPhiWeightFile.value.empty()) { + return; // already loaded, or feature disabled + } + TFile* f = TFile::Open(cfgPhiWeightFile.value.c_str(), "READ"); + if (!f || f->IsZombie()) { + LOGF(fatal, "Could not open phi weight file %s", cfgPhiWeightFile.value.c_str()); + return; + } + cfg.mPhiWeight = dynamic_cast(f->Get(cfgPhiWeightName.value.c_str())); + if (cfg.mPhiWeight) { + cfg.mPhiWeight->SetDirectory(nullptr); // detach so it survives f->Close() + } else { + LOGF(fatal, "Could not find object %s in %s", cfgPhiWeightName.value.c_str(), cfgPhiWeightFile.value.c_str()); + } + f->Close(); + LOGF(info, "Loaded phi weight '%s' from %s (%p)", cfgPhiWeightName.value.c_str(), cfgPhiWeightFile.value.c_str(), static_cast(cfg.mPhiWeight)); + } + + // Looks up a bin-content-based weight from a 1-D phi map. + // Returns 0 if the histogram is missing so the >0 check upstream skips it cleanly. + float getPhiWeight(TH1* hPhiWeight, float phi) + { + if (!hPhiWeight) { + return 0.f; + } + int binX = hPhiWeight->GetXaxis()->FindBin(wrapPhi(phi)); + return hPhiWeight->GetBinContent(binX); + } + + // ------------------------------------------------------------------ QA + // FIX #6b: "yields"/"etaphi" are RAW. "yields_corrected"/"etaphi_corrected" are weighted with the + // efficiency correction factor (associated-particle efficiency object). + template + void fillQA(TCollision const& collision, float multiplicity, TTracks const& tracks) + { + registry.fill(HIST("multiplicity"), multiplicity); + fillMultCorrelations(collision); + const float posZ = collision.posZ(); + + for (const auto& track : tracks) { + if (track.nClusters() < cfgMFTMinNClusters) { + continue; + } + registry.fill(HIST("yields"), multiplicity, track.pt(), track.eta()); + + float phiWeight = 1.f; + if (cfg.mPhiWeight) { + float phiWgt = getPhiWeight(cfg.mPhiWeight, track.phi()); + phiWeight = (phiWgt > 0) ? 1.f / phiWgt : 1.f; + } + registry.fill(HIST("etaphi"), multiplicity, track.eta(), wrapPhi(track.phi()), phiWeight); + registry.fill(HIST("nClusters"), track.nClusters()); + + float effWeight = 1.f; + if (cfg.mEfficiencyAssociated) { + effWeight = getEfficiencyCorrection(cfg.mEfficiencyAssociated, track.eta(), track.pt(), multiplicity, posZ, track.phi()); + } + registry.fill(HIST("yields_corrected"), multiplicity, track.pt(), track.eta(), effWeight); + registry.fill(HIST("etaphi_corrected"), multiplicity, track.eta(), wrapPhi(track.phi()), effWeight); + } + } + + // ------------------------------------------------------------------ event-level acceptance + template + bool acceptCollision(TCollision const& collision) + { + if (!collision.sel8()) { + return false; + } + if (!passOutlier(collision)) { + return false; + } + return true; + } + + // ------------------------------------------------------------------ correlation filling + // Hadron-hadron (MFT-MFT). tracks1 = trigger collection, tracks2 = associated collection. + // Same-event: called as fillCorrelations(..., tracks, tracks, ...) -> self-correlation with index check. + // Mixed-event: called as fillCorrelations(..., tracks1, tracks2, ...) from two different collisions. + + template + void fillCorrelations(TTarget target, TTracks1 const& tracks1, TTracks2 const& tracks2, float multiplicity, bool mixing, float posZ, float eventWeight) + { + // ---------------------------------------------------------------- per-call caches + // Indexed by plain position in the loop (NOT filteredIndex()), and built for BOTH + // collections. This is what makes the mixed event correct: tracks1 and tracks2 have the same + // C++ type in SameKindPair, so a type-based "sameCollection" is true in ME as well. + if constexpr (step == CorrelationContainer::kCFStepCorrected) { + efficiencyAssociatedCache.clear(); + if (cfg.mEfficiencyAssociated) { + efficiencyAssociatedCache.reserve(tracks2.size()); + for (const auto& track : tracks2) { + efficiencyAssociatedCache.push_back(getEfficiencyCorrection(cfg.mEfficiencyAssociated, track.eta(), track.pt(), multiplicity, posZ, track.phi())); + } + } + } + + phiWeightCache.clear(); // associated side (tracks2) + phiWeightCache1.clear(); // trigger side (tracks1) + if (cfg.mPhiWeight) { + phiWeightCache.reserve(tracks2.size()); + for (const auto& track : tracks2) { + phiWeightCache.push_back(getPhiWeight(cfg.mPhiWeight, track.phi())); + } + phiWeightCache1.reserve(tracks1.size()); + for (const auto& track : tracks1) { + phiWeightCache1.push_back(getPhiWeight(cfg.mPhiWeight, track.phi())); + } + } + + // ---------------------------------------------------------------- trigger loop + int i1 = -1; + for (const auto& track1 : tracks1) { + ++i1; // must stay before any 'continue' + if (track1.nClusters() < cfgMFTMinNClusters) { + continue; + } + + if constexpr (std::experimental::is_detected::value) { + if constexpr (std::experimental::is_detected::value) { + if (!track1.isPhysicalPrimary()) + continue; + } + } else if constexpr (std::experimental::is_detected::value) { + if (cfgTriggerCharge != 0 && cfgTriggerCharge * track1.sign() < 0) + continue; + } + + float triggerWeight = eventWeight; + if constexpr (step == CorrelationContainer::kCFStepCorrected) { + if (cfg.mEfficiencyTrigger) { + triggerWeight *= getEfficiencyCorrection(cfg.mEfficiencyTrigger, track1.eta(), track1.pt(), multiplicity, posZ, track1.phi()); + } + } + // phi weight of the trigger: applied BEFORE the trigger histogram is filled, so the + // per-trigger normalisation and the pair weights stay consistent. + if (cfg.mPhiWeight) { + const float w1 = phiWeightCache1[i1]; + if (w1 > 0.f) { + triggerWeight *= 1.f / w1; + } + } + target->getTriggerHist()->Fill(step, track1.pt(), multiplicity, posZ, triggerWeight); + + // ---------------------------------------------------------------- associated loop + int i2 = -1; + for (const auto& track2 : tracks2) { + ++i2; // must stay before any 'continue' + + if (!mixing && track1.globalIndex() == track2.globalIndex()) { + continue; // no self-correlation (same event only) + } + if (track2.nClusters() < cfgMFTMinNClusters) { + continue; + } + + if constexpr (std::experimental::is_detected::value) { + if (cfgAssociatedCharge != 0 && cfgAssociatedCharge * track2.sign() < 0) + continue; + } + if constexpr (std::experimental::is_detected::value && + std::experimental::is_detected::value) { + if (cfgPairCharge != 0 && cfgPairCharge * track1.sign() * track2.sign() < 0) + continue; + } + + if (cfgPtOrder != 0 && track2.pt() >= track1.pt()) { + continue; + } + + float associatedWeight = triggerWeight; // already contains event, trigger eff, trigger phi weight + if constexpr (step == CorrelationContainer::kCFStepCorrected) { + if (cfg.mEfficiencyAssociated) { + associatedWeight *= efficiencyAssociatedCache[i2]; + } + } + if (cfg.mPhiWeight) { + const float w2 = phiWeightCache[i2]; + if (w2 > 0.f) { + associatedWeight *= 1.f / w2; + } + } + + const float deltaEta = track1.eta() - track2.eta(); + const float deltaPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi(), -o2::constants::math::PIHalf); + + if (mixing) { + registry.fill(HIST("mftmft/ME/deltaEta_deltaPhi"), deltaPhi, deltaEta); + } else { + registry.fill(HIST("mftmft/SE/deltaEta_deltaPhi"), deltaPhi, deltaEta); + } + + target->getPairHist()->Fill(step, deltaEta, track2.pt(), track1.pt(), multiplicity, deltaPhi, posZ, associatedWeight); + } + } + } + // FIX #2 + FIX #6d: MFT-Global. Trigger = central-barrel track (cfg.mEfficiencyGlobal), + // associated = MFT track (cfg.mEfficiencyAssociated). + template + void fillCorrMftGlobal(TTarget target, TTriggers const& triggers, TMFTs const& mftTracks, bool mixing, float posZ, float multiplicity, float eventWeight) + { + constexpr bool TriggerHasSign = std::experimental::is_detected::value; + constexpr bool AssocHasSign = std::experimental::is_detected::value; + + if (!mixing) { + for (const auto& assocTrack : mftTracks) { + if (assocTrack.nClusters() < cfgMFTMinNClusters) { + continue; + } + registry.fill(HIST("mftGlobal/SE/Assoc_etavsphi"), assocTrack.phi(), assocTrack.eta()); + registry.fill(HIST("mftGlobal/SE/Assoc_pt"), assocTrack.pt()); + } + } + + for (const auto& triggerTrack : triggers) { + if constexpr (TriggerHasSign) { + if (cfgTriggerCharge != 0 && cfgTriggerCharge * triggerTrack.sign() < 0) + continue; + } + + float triggerWeight = eventWeight; + if constexpr (step == CorrelationContainer::kCFStepCorrected) { + if (cfg.mEfficiencyGlobal) { + triggerWeight *= getEfficiencyCorrection(cfg.mEfficiencyGlobal, triggerTrack.eta(), triggerTrack.pt(), multiplicity, posZ, triggerTrack.phi()); + } + } + if (!mixing) { + registry.fill(HIST("mftGlobal/SE/Trig_etavsphi"), triggerTrack.phi(), triggerTrack.eta()); + registry.fill(HIST("mftGlobal/SE/Trig_pt"), triggerTrack.pt()); + } + target->getTriggerHist()->Fill(step, triggerTrack.pt(), multiplicity, posZ, triggerWeight); + + for (const auto& assocTrack : mftTracks) { + if (assocTrack.nClusters() < cfgMFTMinNClusters) { + continue; + } + if constexpr (AssocHasSign) { + if (cfgAssociatedCharge != 0 && cfgAssociatedCharge * assocTrack.sign() < 0) + continue; + } + if constexpr (TriggerHasSign && AssocHasSign) { + if (cfgPairCharge != 0 && cfgPairCharge * triggerTrack.sign() * assocTrack.sign() < 0) + continue; + } + float associatedWeight = triggerWeight; + if constexpr (step == CorrelationContainer::kCFStepCorrected) { + if (cfg.mEfficiencyAssociated) { + associatedWeight *= getEfficiencyCorrection(cfg.mEfficiencyAssociated, assocTrack.eta(), assocTrack.pt(), multiplicity, posZ, assocTrack.phi()); + } + } + + float deltaPhi = RecoDecay::constrainAngle(triggerTrack.phi() - assocTrack.phi(), -o2::constants::math::PIHalf); + float deltaEta = triggerTrack.eta() - assocTrack.eta(); + if (mixing) { + registry.fill(HIST("mftGlobal/ME/deltaEta_deltaPhi"), deltaPhi, deltaEta); + } else { + registry.fill(HIST("mftGlobal/SE/deltaEta_deltaPhi"), deltaPhi, deltaEta); + } + target->getPairHist()->Fill(step, deltaEta, assocTrack.pt(), triggerTrack.pt(), multiplicity, deltaPhi, posZ, associatedWeight); + } + } + } + + // FIX #4 + FIX #6d: Global-Global (central-barrel hadron-hadron). Both tracks use cfg.mEfficiencyGlobal. + // No nClusters() cut: global tracks are quality-selected via FilteredGlobalTracks. + template + void fillCorrGlobalGlobal(TTarget target, TTracks1 const& tracks1, TTracks2 const& tracks2, bool mixing, float posZ, float multiplicity, float eventWeight) + { + constexpr bool Track1HasSign = std::experimental::is_detected::value; + constexpr bool Track2HasSign = std::experimental::is_detected::value; + + for (const auto& track1 : tracks1) { + if constexpr (Track1HasSign) { + if (cfgTriggerCharge != 0 && cfgTriggerCharge * track1.sign() < 0) + continue; + } + + float triggerWeight = eventWeight; + if constexpr (step == CorrelationContainer::kCFStepCorrected) { + if (cfg.mEfficiencyGlobal) { + triggerWeight *= getEfficiencyCorrection(cfg.mEfficiencyGlobal, track1.eta(), track1.pt(), multiplicity, posZ, track1.phi()); + } + } + if (!mixing) { + registry.fill(HIST("global/SE/etavsphi"), track1.phi(), track1.eta()); + registry.fill(HIST("global/SE/pt"), track1.pt()); + } + target->getTriggerHist()->Fill(step, track1.pt(), multiplicity, posZ, triggerWeight); + + for (const auto& track2 : tracks2) { + if constexpr (std::is_same::value) { + if (track1.globalIndex() == track2.globalIndex()) { + continue; // no self-correlation + } + } + + if constexpr (Track2HasSign) { + if (cfgAssociatedCharge != 0 && cfgAssociatedCharge * track2.sign() < 0) + continue; + } + if constexpr (Track1HasSign && Track2HasSign) { + if (cfgPairCharge != 0 && cfgPairCharge * track1.sign() * track2.sign() < 0) + continue; + } + if (cfgPtOrder != 0 && track2.pt() >= track1.pt()) { + continue; + } + + float associatedWeight = triggerWeight; + if constexpr (step == CorrelationContainer::kCFStepCorrected) { + if (cfg.mEfficiencyGlobal) { + associatedWeight *= getEfficiencyCorrection(cfg.mEfficiencyGlobal, track2.eta(), track2.pt(), multiplicity, posZ, track2.phi()); + } + } + + float deltaPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi(), -o2::constants::math::PIHalf); + float deltaEta = track1.eta() - track2.eta(); + if (mixing) { + registry.fill(HIST("global/ME/deltaEta_deltaPhi"), deltaPhi, deltaEta); + } else { + registry.fill(HIST("global/SE/deltaEta_deltaPhi"), deltaPhi, deltaEta); + } + target->getPairHist()->Fill(step, deltaEta, track2.pt(), track1.pt(), multiplicity, deltaPhi, posZ, associatedWeight); + } + } + } + + // ------------------------------------------------------------------ SAME EVENT (data) + void processSameMFT(FilteredCollisions::iterator const& collision, aod::BCsWithTimestamps const&, FilteredMFTTracks const& tracks) + { + registry.fill(HIST("multiplicity_raw"), collision.centFT0C()); // unconditional, no cuts + if (!acceptCollision(collision)) { + return; + } + + loadEfficiency(collision.template bc_as().timestamp()); + const float multiplicity = getMultiplicity(collision); + + using BinningType = ColumnBinningPolicy; + BinningType binning{{axisVertex, axisMultiplicity}, true}; + int bin = binning.getBin({collision.posZ(), multiplicity}); + + if (cfgVerbosity > 0) { + LOGF(info, "processSameMFT: %d MFT tracks | Vertex: %.2f | Mult: %.2f", tracks.size(), collision.posZ(), multiplicity); + } + + registry.fill(HIST("eventcount_same"), bin); + registry.fill(HIST("trackcount_same"), bin, tracks.size()); + fillQA(collision, multiplicity, tracks); + + const bool hasEfficiency = (cfg.mEfficiencyAssociated != nullptr || cfg.mEfficiencyTrigger != nullptr); + const bool fillReco = !(cfgDropStepRECO && hasEfficiency); + + same->fillEvent(multiplicity, CorrelationContainer::kCFStepAll); + if (fillReco) { + same->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrelations(same, tracks, tracks, multiplicity, false, collision.posZ(), 1.0f); + } + if (hasEfficiency) { + same->fillEvent(multiplicity, CorrelationContainer::kCFStepCorrected); + fillCorrelations(same, tracks, tracks, multiplicity, false, collision.posZ(), 1.0f); + } + } + PROCESS_SWITCH(Mftcorrelations, processSameMFT, "Process same event MFT-MFT correlations", true); + + void processMftGlobalSE(FilteredCollisions::iterator const& collision, FilteredGlobalTracks const& globalTracks, FilteredMFTTracks const& mftTracks, aod::BCsWithTimestamps const&) + { + if (!acceptCollision(collision)) { + return; + } + loadEfficiency(collision.template bc_as().timestamp()); + const float multiplicity = getMultiplicity(collision); + + using MixingBinningType = ColumnBinningPolicy; + MixingBinningType binning{{axisVertex, axisMultiplicity}, true}; + int bin = binning.getBin({collision.posZ(), multiplicity}); + registry.fill(HIST("eventcount_mftglobal_same"), bin); + registry.fill(HIST("trackcount_mftglobal_same"), bin, globalTracks.size(), mftTracks.size()); + + // FIX #6d: trigger = global track, associated = MFT track + const bool hasEfficiency = (cfg.mEfficiencyAssociated != nullptr || cfg.mEfficiencyGlobal != nullptr); + const bool fillReco = !(cfgDropStepRECO && hasEfficiency); + + sameMftGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepAll); + if (fillReco) { + sameMftGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrMftGlobal(sameMftGlobal, globalTracks, mftTracks, false, collision.posZ(), multiplicity, 1.0f); + } + if (hasEfficiency) { + sameMftGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepCorrected); + fillCorrMftGlobal(sameMftGlobal, globalTracks, mftTracks, false, collision.posZ(), multiplicity, 1.0f); + } + } + PROCESS_SWITCH(Mftcorrelations, processMftGlobalSE, "Process same event MFT-vs-global-track correlations", false); + + // FIX #4: Same-event global-global (central-barrel hadron-hadron) correlations. + void processGlobalGlobalSE(FilteredCollisions::iterator const& collision, FilteredGlobalTracks const& tracks, aod::BCsWithTimestamps const&) + { + if (!acceptCollision(collision)) { + return; + } + loadEfficiency(collision.template bc_as().timestamp()); + const float multiplicity = getMultiplicity(collision); + + using MixingBinningType = ColumnBinningPolicy; + MixingBinningType binning{{axisVertex, axisMultiplicity}, true}; + int bin = binning.getBin({collision.posZ(), multiplicity}); + + if (cfgVerbosity > 0) { + LOGF(info, "processGlobalGlobalSE: %d global tracks | Vertex: %.2f | Mult: %.2f", tracks.size(), collision.posZ(), multiplicity); + } + + registry.fill(HIST("eventcount_global_same"), bin); + registry.fill(HIST("trackcount_global_same"), bin, tracks.size()); + + // FIX #6d + const bool hasEfficiency = (cfg.mEfficiencyGlobal != nullptr); + const bool fillReco = !(cfgDropStepRECO && hasEfficiency); + + sameGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepAll); + if (fillReco) { + sameGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrGlobalGlobal(sameGlobal, tracks, tracks, false, collision.posZ(), multiplicity, 1.0f); + } + if (hasEfficiency) { + sameGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepCorrected); + fillCorrGlobalGlobal(sameGlobal, tracks, tracks, false, collision.posZ(), multiplicity, 1.0f); + } + } + PROCESS_SWITCH(Mftcorrelations, processGlobalGlobalSE, "Process same event global-global (central-barrel) correlations", false); + + void processSameMFTMultSet(FilteredCollisions::iterator const& collision, aod::BCsWithTimestamps const& bcs, FilteredMFTTracks const& tracks) + { + if (cfgMultCorrelationsMask == 0) { + LOGF(fatal, "cfgMultCorrelationsMask can not be 0 when the MultSet process function is in use."); + } + processSameMFT(collision, bcs, tracks); + } + PROCESS_SWITCH(Mftcorrelations, processSameMFTMultSet, "Process same event MFT-MFT correlations with multi-estimator QA", false); + + // ------------------------------------------------------------------ MIXED EVENT (data) + void processMixedMFT(FilteredCollisions const& collisions, FilteredMFTTracks const& tracks, aod::BCsWithTimestamps const&) + { + // Bin mixed events on the SAME multiplicity/centrality estimator that getMultiplicity() returns + // (driven by cfgMultCorrelationsMask), instead of a hardcoded CentFT0C column. This keeps the + // mixing pool synchronized with whatever estimator is actually filled into the CorrelationContainer, + // and lets you switch estimators (e.g. FT0C -> FT0M) purely from the JSON config. + auto getMult = [this](auto const& c) { return getMultiplicity(c); }; + using BinningType = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMult)>; + BinningType binning{{getMult}, {axisVertex, axisMultiplicity}, true}; + + auto tracksTuple = std::make_tuple(tracks); + SameKindPair pairs{binning, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; + + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracks1, collision2, tracks2] = *it; + + if (!acceptCollision(collision1) || !collision2.sel8()) { + continue; + } + + const float multiplicity = getMultiplicity(collision1); + int bin = binning.getBin(std::tuple(collision1.posZ(), multiplicity)); + float eventWeight = 1.0f / it.currentWindowNeighbours(); + + if (cfgVerbosity > 0) { + LOGF(info, "processMixedMFT: bin %d | pair (%d,%d) | Vertex %.2f/%.2f | Mult %.2f", bin, collision1.globalIndex(), collision2.globalIndex(), collision1.posZ(), collision2.posZ(), multiplicity); + } + + if (it.isNewWindow()) { + loadEfficiency(collision1.template bc_as().timestamp()); + mixed->fillEvent(multiplicity, CorrelationContainer::kCFStepAll); + } + + registry.fill(HIST("eventcount_mixed"), bin); + registry.fill(HIST("trackcount_mixed"), bin, tracks1.size(), tracks2.size()); + + const bool hasEfficiency = (cfg.mEfficiencyAssociated != nullptr || cfg.mEfficiencyTrigger != nullptr); + const bool fillReco = !(cfgDropStepRECO && hasEfficiency); + + if (it.isNewWindow() && fillReco) { + mixed->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + } + if (it.isNewWindow() && hasEfficiency) { + mixed->fillEvent(multiplicity, CorrelationContainer::kCFStepCorrected); + } + + if (fillReco) { + fillCorrelations(mixed, tracks1, tracks2, multiplicity, true, collision1.posZ(), eventWeight); + } + if (hasEfficiency) { + fillCorrelations(mixed, tracks1, tracks2, multiplicity, true, collision1.posZ(), eventWeight); + } + } + } + PROCESS_SWITCH(Mftcorrelations, processMixedMFT, "Process mixed event MFT-MFT correlations", false); + + void processMftGlobalME(FilteredCollisions const& collisions, FilteredGlobalTracks const& globalTracks, FilteredMFTTracks const& mftTracks, aod::BCsWithTimestamps const&) + { + // Same fix as processMixedMFT: bin on getMultiplicity() (driven by cfgMultCorrelationsMask) + // instead of a hardcoded CentFT0C column, so the mixing pool matches whatever estimator is + // actually filled into the CorrelationContainer. + auto getMult = [this](auto const& c) { return getMultiplicity(c); }; + using MixingBinningType = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMult)>; + MixingBinningType binning{{getMult}, {axisVertex, axisMultiplicity}, true}; + + auto tracksTuple = std::make_tuple(globalTracks, mftTracks); + Pair pairs{binning, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; + + int pairCounter = 0; + int acceptedPairCounter = 0; + int newWindowCounter = 0; + + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, globalTracks1, collision2, mftTracks2] = *it; + pairCounter++; + + if (!acceptCollision(collision1) || !collision2.sel8()) { + continue; + } + acceptedPairCounter++; + + const float multiplicity = getMultiplicity(collision1); + float eventWeight = 1.0f / it.currentWindowNeighbours(); + + const bool hasEfficiency = (cfg.mEfficiencyAssociated != nullptr || cfg.mEfficiencyGlobal != nullptr); + const bool fillReco = !(cfgDropStepRECO && hasEfficiency); + + int bin = binning.getBin(std::tuple(collision1.posZ(), multiplicity)); + + if (it.isNewWindow()) { + newWindowCounter++; + loadEfficiency(collision1.template bc_as().timestamp()); + mixedMftGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepAll); + if (fillReco) { + mixedMftGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + } + if (hasEfficiency) { + mixedMftGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepCorrected); + } + registry.fill(HIST("eventcount_mftglobal_mixed"), bin); + } + registry.fill(HIST("trackcount_mftglobal_mixed"), bin, globalTracks1.size(), mftTracks2.size()); + + if (fillReco) { + fillCorrMftGlobal(mixedMftGlobal, globalTracks1, mftTracks2, true, collision1.posZ(), multiplicity, eventWeight); + } + if (hasEfficiency) { + fillCorrMftGlobal(mixedMftGlobal, globalTracks1, mftTracks2, true, collision1.posZ(), multiplicity, eventWeight); + } + } + + if (cfgVerbosity > 0) { + LOGF(info, "processMftGlobalME: collisions %d | pairs iterated %d | accepted %d | new windows %d", + collisions.size(), pairCounter, acceptedPairCounter, newWindowCounter); + } + } + PROCESS_SWITCH(Mftcorrelations, processMftGlobalME, "Process mixed event MFT-vs-global-track correlations", false); + + // FIX #4: Mixed-event global-global (central-barrel hadron-hadron) correlations. + void processGlobalGlobalME(FilteredCollisions const& collisions, FilteredGlobalTracks const& tracks, aod::BCsWithTimestamps const&) + { + auto getMult = [this](auto const& c) { return getMultiplicity(c); }; + using MixingBinningType = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMult)>; + MixingBinningType binning{{getMult}, {axisVertex, axisMultiplicity}, true}; + + auto tracksTuple = std::make_tuple(tracks); + SameKindPair pairs{binning, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; + + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracks1, collision2, tracks2] = *it; + + if (!acceptCollision(collision1) || !collision2.sel8()) { + continue; + } + + const float multiplicity = getMultiplicity(collision1); + int bin = binning.getBin(std::tuple(collision1.posZ(), multiplicity)); + float eventWeight = 1.0f / it.currentWindowNeighbours(); + + if (cfgVerbosity > 0) { + LOGF(info, "processGlobalGlobalME: bin %d | pair (%d,%d) | Vertex %.2f/%.2f | Mult %.2f", bin, collision1.globalIndex(), collision2.globalIndex(), collision1.posZ(), collision2.posZ(), multiplicity); + } + + const bool hasEfficiency = (cfg.mEfficiencyGlobal != nullptr); + const bool fillReco = !(cfgDropStepRECO && hasEfficiency); + + if (it.isNewWindow()) { + loadEfficiency(collision1.template bc_as().timestamp()); + mixedGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepAll); + if (fillReco) { + mixedGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + } + if (hasEfficiency) { + mixedGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepCorrected); + } + registry.fill(HIST("eventcount_global_mixed"), bin); + } + registry.fill(HIST("trackcount_global_mixed"), bin, tracks1.size(), tracks2.size()); + + if (fillReco) { + fillCorrGlobalGlobal(mixedGlobal, tracks1, tracks2, true, collision1.posZ(), multiplicity, eventWeight); + } + if (hasEfficiency) { + fillCorrGlobalGlobal(mixedGlobal, tracks1, tracks2, true, collision1.posZ(), multiplicity, eventWeight); + } + } + } + PROCESS_SWITCH(Mftcorrelations, processGlobalGlobalME, "Process mixed event global-global (central-barrel) correlations", false); + + void processMixedMFTMultSet(FilteredCollisions const& collisions, FilteredMFTTracks const& tracks, aod::BCsWithTimestamps const& bcs) + { + if (cfgMultCorrelationsMask == 0) { + LOGF(fatal, "cfgMultCorrelationsMask can not be 0 when the MultSet process function is in use."); + } + processMixedMFT(collisions, tracks, bcs); + } + PROCESS_SWITCH(Mftcorrelations, processMixedMFTMultSet, "Process mixed event MFT-MFT correlations with multi-estimator QA", false); + + template + bool isChargedPrimary(TParticle const& p) + { + if (!p.isPhysicalPrimary()) { + return false; + } + auto* entry = pdg->GetParticle(p.pdgCode()); + return entry != nullptr && entry->Charge() != 0; + } + + // ------------------------------------------------------------------ MC efficiency extraction + Preslice perCollisionMFT = aod::fwdtrack::collisionId; + + void processMCEfficiencyMFT(aod::McCollisions::iterator const& mcCollision, + aod::McParticles const& mcParticles, + McEffCollisions const& collisions, + McEffMFTTracks const& tracks) + { + registry.fill(HIST("MCEff/hNRecoColl"), collisions.size()); + registry.fill(HIST("MCEff/hCutFlow"), 1); + if (collisions.size() != 1) { + return; + } + registry.fill(HIST("MCEff/hCutFlow"), 2); + if (cfgVerbosity > 0) { + LOGF(info, "processMCEfficiencyMFT: ENTER | mcCollision=%lld | nMcParticles=%d | nCollisions=%d | nTracks=%d", + mcCollision.globalIndex(), mcParticles.size(), collisions.size(), tracks.size()); + } + float multiplicity = -1; + bool passSel8 = false; + int64_t collGlobalIndex = -1; + for (const auto& collision : collisions) { + if (!collision.sel8()) { + continue; + } + registry.fill(HIST("MCEff/hCutFlow"), 3); + passSel8 = true; + multiplicity = getMultiplicity(collision); // driven by cfgMultCorrelationsMask + collGlobalIndex = collision.globalIndex(); + } + if (!passSel8) { + return; + } + + for (const auto& mcParticle : mcParticles) { + if (!isChargedPrimary(mcParticle) || mcParticle.eta() < cfgMFTCutEtaMin || mcParticle.eta() > cfgMFTCutEtaMax || mcParticle.pt() < cfgMFTCutPtMin || mcParticle.pt() > cfgMFTCutPtMax) { + continue; + } + same->getTrackHistEfficiency()->Fill(CorrelationContainer::MC, mcParticle.eta(), mcParticle.pt(), 0, multiplicity, mcCollision.posZ(), wrapPhi(mcParticle.phi())); + } + + auto groupedTracks = tracks.sliceBy(perCollisionMFT, collGlobalIndex); + + std::unordered_set countedPrimaries; + std::unordered_set countedAll; + + for (const auto& track : groupedTracks) { + if (track.nClusters() < cfgMFTMinNClusters) { + continue; + } + if (track.has_mcParticle()) { + const auto& mcParticle = track.mcParticle(); + const int64_t idx = mcParticle.globalIndex(); + + if (mcParticle.isPhysicalPrimary() && countedPrimaries.insert(idx).second) { + same->getTrackHistEfficiency()->Fill(CorrelationContainer::RecoPrimaries, + mcParticle.eta(), mcParticle.pt(), 0, multiplicity, mcCollision.posZ(), wrapPhi(mcParticle.phi())); + } + if (countedAll.insert(idx).second) { + same->getTrackHistEfficiency()->Fill(CorrelationContainer::RecoAll, + mcParticle.eta(), mcParticle.pt(), 0, multiplicity, mcCollision.posZ(), wrapPhi(mcParticle.phi())); + } + } else { + // FIX #6e: wrap phi to [0,2pi) like the MC-particle phi used for the other steps + same->getTrackHistEfficiency()->Fill(CorrelationContainer::Fake, + track.eta(), track.pt(), 0, multiplicity, mcCollision.posZ(), + RecoDecay::constrainAngle(track.phi(), 0.f)); + } + } + } + PROCESS_SWITCH(Mftcorrelations, processMCEfficiencyMFT, "MC: Extract MFT tracking efficiency", false); + + //-------------------------------------------------------------------MC Efficiency Extraction for Global + + Preslice perCollisionGlobal = aod::track::collisionId; + + void processMCEfficiencyGlobal(aod::McCollisions::iterator const& mcCollision, + aod::McParticles const& mcParticles, + McEffCollisions const& collisions, + McEffGlobalTracks const& tracks) + { + registry.fill(HIST("MCEffGlobal/hNRecoColl"), collisions.size()); + registry.fill(HIST("MCEffGlobal/hCutFlow"), 1); + if (collisions.size() != 1) { // exactly one reconstructed collision per MC collision (as in the MFT efficiency) + return; + } + registry.fill(HIST("MCEffGlobal/hCutFlow"), 2); + if (cfgVerbosity > 0) { + LOGF(info, "processMCEfficiencyGlobal: mcCollision=%lld | nMcParticles=%d | nCollisions=%d | nTracks=%d", + mcCollision.globalIndex(), mcParticles.size(), collisions.size(), tracks.size()); + } + + float multiplicity = -1; + bool passSel8 = false; + int64_t collGlobalIndex = -1; + for (const auto& collision : collisions) { + if (!collision.sel8()) { + continue; + } + registry.fill(HIST("MCEffGlobal/hCutFlow"), 3); + passSel8 = true; + multiplicity = getMultiplicity(collision); // same estimator as in data (cfgMultCorrelationsMask) + collGlobalIndex = collision.globalIndex(); + } + if (!passSel8) { + return; + } + + // Generated: charged physical primaries in the global-track acceptance + for (const auto& mcParticle : mcParticles) { + if (!isChargedPrimary(mcParticle) || + std::abs(mcParticle.eta()) >= cfgGlobalTrackEtaCut || + mcParticle.pt() < cfgGlobalTrackPtMin || mcParticle.pt() > cfgGlobalTrackPtMax) { + continue; + } + sameGlobal->getTrackHistEfficiency()->Fill(CorrelationContainer::MC, mcParticle.eta(), mcParticle.pt(), 0, multiplicity, mcCollision.posZ(), mcParticle.phi()); + } + + // Reconstructed: global tracks of the selected reco collision (globalTrackFilter already applied) + auto groupedTracks = tracks.sliceBy(perCollisionGlobal, collGlobalIndex); + + std::unordered_set countedPrimaries; // count each MC particle once (split tracks) + std::unordered_set countedAll; + + for (const auto& track : groupedTracks) { + if (track.has_mcParticle()) { + const auto& mcParticle = track.mcParticle(); + const int64_t idx = mcParticle.globalIndex(); + + if (mcParticle.isPhysicalPrimary() && countedPrimaries.insert(idx).second) { + sameGlobal->getTrackHistEfficiency()->Fill(CorrelationContainer::RecoPrimaries, + mcParticle.eta(), mcParticle.pt(), 0, multiplicity, mcCollision.posZ(), mcParticle.phi()); + } + if (countedAll.insert(idx).second) { + sameGlobal->getTrackHistEfficiency()->Fill(CorrelationContainer::RecoAll, + mcParticle.eta(), mcParticle.pt(), 0, multiplicity, mcCollision.posZ(), mcParticle.phi()); + } + } else { + // fake track + sameGlobal->getTrackHistEfficiency()->Fill(CorrelationContainer::Fake, + track.eta(), track.pt(), 0, multiplicity, mcCollision.posZ(), + RecoDecay::constrainAngle(track.phi(), 0.f)); + } + } + } + PROCESS_SWITCH(Mftcorrelations, processMCEfficiencyGlobal, "MC: Extract central-barrel (global track) tracking efficiency", false); + + // ------------------------------------------------------------------ MC generator-level correlations (truth reference / closure test) + using McRecoCollisions = soa::Join; + + // -1, 0, +1 from the PDG database charge (0 = neutral or unknown code) + template + int getMcCharge(TParticle const& p) + { + auto* entry = pdg->GetParticle(p.pdgCode()); + if (entry == nullptr) { + return 0; + } + const double q = entry->Charge(); + return (q > 0) - (q < 0); + } + + // cheap cuts first, PDG lookup last + template + bool acceptMftTruth(TParticle const& p) + { + return p.isPhysicalPrimary() && p.eta() > cfgMFTCutEtaMin && p.eta() < cfgMFTCutEtaMax && + p.pt() > cfgMFTCutPtMin && p.pt() < cfgMFTCutPtMax && getMcCharge(p) != 0; + } + + template + bool acceptGlobalTruth(TParticle const& p) + { + return p.isPhysicalPrimary() && std::abs(p.eta()) < cfgGlobalTrackEtaCut && + p.pt() > cfgGlobalTrackPtMin && p.pt() < cfgGlobalTrackPtMax && getMcCharge(p) != 0; + } + + template + std::vector collectAccepted(TParticles const& particles, TSelector accept) + { + std::vector idx; + idx.reserve(64); + for (const auto& p : particles) { + if (accept(p)) { + idx.push_back(p.globalIndex()); + } + } + return idx; + } + + template + void fillTruthPairs(TTarget& target, TParticles1 const& particles1, TParticles2 const& particles2, + std::vector const& acc1, std::vector const& acc2, + float multiplicity, float posZ, float weight, bool sameCollection) + { + for (const auto& i1 : acc1) { + const auto& p1 = particles1.iteratorAt(i1 - particles1.begin().globalIndex()); + target->getTriggerHist()->Fill(CorrelationContainer::kCFStepAll, p1.pt(), multiplicity, posZ, weight); + for (const auto& i2 : acc2) { + if (sameCollection && i1 == i2) { + continue; + } + const auto& p2 = particles2.iteratorAt(i2 - particles2.begin().globalIndex()); + if (cfgPtOrder != 0 && p2.pt() >= p1.pt()) { + continue; + } + float deltaPhi = RecoDecay::constrainAngle(p1.phi() - p2.phi(), -o2::constants::math::PIHalf); + target->getPairHist()->Fill(CorrelationContainer::kCFStepAll, p1.eta() - p2.eta(), p2.pt(), p1.pt(), multiplicity, deltaPhi, posZ, weight); + } + } + } + + // ---------------- same event + template + void fillTruthSame(TTarget& target, aod::McCollisions::iterator const& mcCollision, aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions, TSelector accept) + { + if (collisions.size() != 1) { // same reco-collision requirement as processMCEfficiencyMFT + return; + } + auto collision = collisions.begin(); + if (!collision.sel8()) { + return; + } + const float multiplicity = getMultiplicity(collision); + const auto acc = collectAccepted(mcParticles, accept); + + target->fillEvent(multiplicity, CorrelationContainer::kCFStepAll); + fillTruthPairs(target, mcParticles, mcParticles, acc, acc, multiplicity, mcCollision.posZ(), 1.0f, true); + } + + void processMCSameMFT(aod::McCollisions::iterator const& mcCollision, aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions) + { + fillTruthSame(same, mcCollision, mcParticles, collisions, [this](auto const& p) { return acceptMftTruth(p); }); + } + PROCESS_SWITCH(Mftcorrelations, processMCSameMFT, "Process MC-truth same event MFT-acceptance correlations (charged primaries)", false); + + void processMCSameGlobal(aod::McCollisions::iterator const& mcCollision, aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions) + { + fillTruthSame(sameGlobal, mcCollision, mcParticles, collisions, [this](auto const& p) { return acceptGlobalTruth(p); }); + } + PROCESS_SWITCH(Mftcorrelations, processMCSameGlobal, "Process MC-truth same event global-acceptance correlations (charged primaries)", false); + + // ---------------- mixed event + PresliceUnsorted perMcCollision = aod::mccollisionlabel::mcCollisionId; + + template + void fillTruthMixed(TTarget& target, aod::McCollisions const& mcCollisions, aod::McParticles const& mcParticles, + McRecoCollisions const& collisions, TSelector accept) + { + // multiplicity of an MC collision = that of its single, sel8-passing reco collision; otherwise -1 (skipped by the binning) + auto getMult = [&collisions, this](auto const& mcCol) { + auto grouped = collisions.sliceBy(perMcCollision, mcCol.globalIndex()); + if (grouped.size() != 1) { + return -1.0f; + } + auto c = grouped.begin(); + if (!c.sel8()) { + return -1.0f; + } + return static_cast(getMultiplicity(c)); + }; + + using BinningTypeMC = FlexibleBinningPolicy, aod::mccollision::PosZ, decltype(getMult)>; + BinningTypeMC binning{{getMult}, {axisVertex, axisMultiplicity}, true}; + auto tuple = std::make_tuple(mcParticles); + SameKindPair pairs{binning, cfgNoMixedEvents, -1, mcCollisions, tuple, &cache}; + + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [mcCollision1, particles1, mcCollision2, particles2] = *it; + const float eventWeight = 1.0f / it.currentWindowNeighbours(); + const float multiplicity = getMult(mcCollision1); + + if (it.isNewWindow()) { + target->fillEvent(multiplicity, CorrelationContainer::kCFStepAll); + } + const auto acc1 = collectAccepted(particles1, accept); + const auto acc2 = collectAccepted(particles2, accept); // once per pair, not once per trigger + fillTruthPairs(target, particles1, particles2, acc1, acc2, multiplicity, mcCollision1.posZ(), eventWeight, false); + } + } + + void processMCMixedMFT(aod::McCollisions const& mcCollisions, aod::McParticles const& mcParticles, McRecoCollisions const& collisions) + { + fillTruthMixed(mixed, mcCollisions, mcParticles, collisions, [this](auto const& p) { return acceptMftTruth(p); }); + } + PROCESS_SWITCH(Mftcorrelations, processMCMixedMFT, "Process MC-truth mixed event MFT-acceptance correlations (charged primaries)", false); + + void processMCMixedGlobal(aod::McCollisions const& mcCollisions, aod::McParticles const& mcParticles, McRecoCollisions const& collisions) + { + fillTruthMixed(mixedGlobal, mcCollisions, mcParticles, collisions, [this](auto const& p) { return acceptGlobalTruth(p); }); + } + PROCESS_SWITCH(Mftcorrelations, processMCMixedGlobal, "Process MC-truth mixed event global-acceptance correlations (charged primaries)", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGCF/TwoParticleCorrelations/Tasks/nucleibalance.cxx b/PWGCF/TwoParticleCorrelations/Tasks/nucleibalance.cxx index dc2fc35780c..640352fff2f 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/nucleibalance.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/nucleibalance.cxx @@ -857,7 +857,7 @@ struct Nucleibalance { const auto multiplicity = eventMultiplicityFT0MOrFallback(collision, collision.multiplicity()); using BinningTypeDerived = ColumnBinningPolicy; - BinningTypeDerived configurableBinningDerived{{axisVertex, axisMultiplicity}, true}; + BinningTypeDerived configurableBinningDerived{{axisVertex, axisMultiplicity}}; int bin = configurableBinningDerived.getBin({collision.posZ(), multiplicity}); registry.fill(HIST("eventcount_same"), bin); registry.fill(HIST("trackcount_same"), bin, tracks.size()); @@ -1256,7 +1256,7 @@ struct Nucleibalance { void processSameDerivedT(CollType const& collision, TTracks1 const& tracks1, TTracks2 const& tracks2) { using BinningTypeDerived = ColumnBinningPolicy; - BinningTypeDerived configurableBinningDerived{{axisVertex, axisMultiplicity}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + BinningTypeDerived configurableBinningDerived{{axisVertex, axisMultiplicity}}; if (cfgVerbosity > 0) { LOGF(info, "processSameDerivedT: Tracks for collision: %d/%d | Vertex: %.1f | Multiplicity/Centrality: %.1f", tracks1.size(), tracks2.size(), collision.posZ(), collision.multiplicity()); } @@ -1305,7 +1305,7 @@ struct Nucleibalance { }; using BinningTypeDerived = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertex, axisMultiplicity}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertex, axisMultiplicity}}; // Strictly upper categorised collisions, for cfgNoMixedEvents combinations per bin, skipping those in entry -1 auto tracksTuple = std::make_tuple(std::forward(tracks)...); using TA = std::tuple_element<0, decltype(tracksTuple)>::type; @@ -1369,7 +1369,7 @@ struct Nucleibalance { }; using BinningTypeDerived = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertex, axisMultiplicity}, true}; + BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertex, axisMultiplicity}}; auto tracksTuple = std::make_tuple(std::forward(tracks)...); using TA = std::tuple_element<0, decltype(tracksTuple)>::type; @@ -1602,7 +1602,7 @@ struct Nucleibalance { }; using BinningTypeMCDerived = FlexibleBinningPolicy, aod::mccollision::PosZ, decltype(getMultiplicity)>; - BinningTypeMCDerived configurableBinning{{getMultiplicity}, {axisVertex, axisMultiplicity}, true}; + BinningTypeMCDerived configurableBinning{{getMultiplicity}, {axisVertex, axisMultiplicity}}; // Strictly upper categorised collisions, for cfgNoMixedEvents combinations per bin, skipping those in entry -1 auto tuple = std::make_tuple(std::forward(particles)...); diff --git a/PWGCF/TwoParticleCorrelations/Tasks/pidDiHadron.cxx b/PWGCF/TwoParticleCorrelations/Tasks/pidDiHadron.cxx index 40ce655fb4e..064272be3d0 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/pidDiHadron.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/pidDiHadron.cxx @@ -14,11 +14,10 @@ /// \author Preet Bhanjan Pati (preet.bhanjan.pati@cern.ch), Zhiyong Lu (zhiyong.lu@cern.ch) /// \since July/29/2025 -#include "PWGCF/Core/CorrelationContainer.h" +#include "PWGCF/TwoParticleCorrelations/Core/DihadronContainer.h" #include "PWGLF/DataModel/LFStrangenessTables.h" #include "Common/CCDB/EventSelectionParams.h" -#include "Common/CCDB/TriggerAliases.h" #include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" @@ -51,14 +50,12 @@ #include #include #include -#include - -#include #include #include #include #include +#include #include #include @@ -68,13 +65,13 @@ using namespace o2::framework::expressions; using namespace constants::math; // define the filtered collisions and tracks -#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, (DEFAULT), (HELP)}; // NOLINT(bugprone-macro-parentheses) template -auto readMatrix(Array2D const& mat, P& array, int rowStart, int rowEnd, int colStart, int colEnd) +auto readMatrix(Array2D const& mat, P& array) { - for (auto i = rowStart; i < rowEnd; ++i) { - for (auto j = colStart; j < colEnd; ++j) { + for (auto i = 0; i < static_cast(mat.rows); ++i) { + for (auto j = 0; j < static_cast(mat.cols); ++j) { array[i][j] = mat(i, j); } } @@ -82,57 +79,83 @@ auto readMatrix(Array2D const& mat, P& array, int rowStart, int rowEnd, int c return; } -static constexpr float LongArrayFloat[3][20] = {{1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2}, {2.1, 2.2, 2.3, -2.1, -2.2, -2.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2}, {3.1, 3.2, 3.3, -3.1, -3.2, -3.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2}}; -static constexpr int LongArrayInt[3][20] = {{1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1}, {2, 2, 2, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1}, {3, 3, 3, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1}}; +static constexpr std::array, 20> LongArrayFloat = {{{{1.1, 2.1, 3.1}}, {{1.2, 2.2, 3.2}}, {{1.3, 2.3, 3.3}}, {{-1.1, -2.1, -3.1}}, {{-1.2, -2.2, -3.2}}, {{-1.3, -2.3, -3.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}, {{1.3, 1.3, 1.3}}, {{-1.1, -1.1, -1.1}}, {{-1.2, -1.2, -1.2}}, {{-1.3, -1.3, -1.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}, {{1.3, 1.3, 1.3}}, {{-1.1, -1.1, -1.1}}, {{-1.2, -1.2, -1.2}}, {{-1.3, -1.3, -1.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}}}; +static constexpr std::array, 20> LongArrayInt = {{{{1, 2, 3}}, {{1, 2, 3}}, {{1, 2, 3}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}, {{1, 1, 1}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}, {{1, 1, 1}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}}}; struct PidDiHadron { o2::aod::ITSResponse itsResponse; - Service ccdb; - - O2_DEFINE_CONFIGURABLE(cfgCutVertex, float, 10.0f, "Accepted z-vertex range") - O2_DEFINE_CONFIGURABLE(cfgCutPtPOIMin, float, 0.2f, "minimum accepted track pT") - O2_DEFINE_CONFIGURABLE(cfgCutPtPOIMax, float, 10.0f, "maximum accepted track pT") - O2_DEFINE_CONFIGURABLE(cfgCutPtMin, float, 0.2f, "Minimal pT for ref tracks") - O2_DEFINE_CONFIGURABLE(cfgCutPtMax, float, 3.0f, "Maximal pT for ref tracks") - O2_DEFINE_CONFIGURABLE(cfgCutEta, float, 0.8f, "Eta cut") - O2_DEFINE_CONFIGURABLE(cfgCutChi2prTPCcls, float, 2.5f, "max chi2 per TPC clusters") - O2_DEFINE_CONFIGURABLE(cfgTpcCluster, float, 50.0f, "minimum TPC clusters") - O2_DEFINE_CONFIGURABLE(cfgTpcCrossRows, float, 70.0f, "minimum TPC crossed rows") - O2_DEFINE_CONFIGURABLE(cfgTofPtCut, float, 0.5f, "Minimum pt to use TOF N-sigma") - O2_DEFINE_CONFIGURABLE(cfgTpcCut, float, 3.0f, "TPC N-sigma cut for pions, kaons, protons") - O2_DEFINE_CONFIGURABLE(cfgITScluster, float, 5.0f, "minimum ITS clusters") - O2_DEFINE_CONFIGURABLE(cfgCutOccupancyMax, int, 2000, "Minimum occupancy cut") - O2_DEFINE_CONFIGURABLE(cfgCutOccupancyMin, int, 0, "Maximum occupancy cut") - O2_DEFINE_CONFIGURABLE(cfgFakeKaonCut, float, 0.1f, "Maximum difference in measured momentum and TPC inner ring momentum of particle") - O2_DEFINE_CONFIGURABLE(cfgCutDCAz, float, 2.0f, "max DCA to vertex z") - O2_DEFINE_CONFIGURABLE(cfgCutMerging, float, 0.0, "Merging cut on track merge") - O2_DEFINE_CONFIGURABLE(cfgSelCollByNch, bool, true, "Select collisions by Nch or centrality") - O2_DEFINE_CONFIGURABLE(cfgCutMultMin, int, 0, "Minimum multiplicity for collision") - O2_DEFINE_CONFIGURABLE(cfgCutMultMax, int, 10, "Maximum multiplicity for collision") - O2_DEFINE_CONFIGURABLE(cfgCutCentMin, float, 60.0f, "Minimum centrality for collision") - O2_DEFINE_CONFIGURABLE(cfgCutCentMax, float, 80.0f, "Maximum centrality for collision") - O2_DEFINE_CONFIGURABLE(cfgMixEventNumMin, int, 5, "Minimum number of events to mix") - O2_DEFINE_CONFIGURABLE(cfgRadiusLow, float, 0.8, "Low radius for merging cut") - O2_DEFINE_CONFIGURABLE(cfgRadiusHigh, float, 2.5, "High radius for merging cut") - O2_DEFINE_CONFIGURABLE(cfgSampleSize, double, 10, "Sample size for mixed event") - O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FT0A") - O2_DEFINE_CONFIGURABLE(cfgCentTableUnavailable, bool, false, "if a dataset does not provide centrality information") - O2_DEFINE_CONFIGURABLE(cfgUseAdditionalEventCut, bool, false, "Use additional event cut on mult correlations") - O2_DEFINE_CONFIGURABLE(cfgV0AT0Acut, int, 5, "V0AT0A cut") - O2_DEFINE_CONFIGURABLE(cfgEfficiency, std::string, "", "CCDB path to efficiency object") - O2_DEFINE_CONFIGURABLE(cfgCentralityWeight, std::string, "", "CCDB path to centrality weight object") - O2_DEFINE_CONFIGURABLE(cfgLocalEfficiency, bool, false, "Use local efficiency object") - O2_DEFINE_CONFIGURABLE(cfgVerbosity, bool, false, "Verbose output") - O2_DEFINE_CONFIGURABLE(cfgUseEventWeights, bool, false, "Use event weights for mixed event") - O2_DEFINE_CONFIGURABLE(cfgUsePtOrder, bool, false, "enable trigger pT < associated pT cut") - O2_DEFINE_CONFIGURABLE(cfgUsePtOrderInMixEvent, bool, false, "enable trigger pT < associated pT cut in mixed event") - O2_DEFINE_CONFIGURABLE(cfgUseItsPID, bool, true, "Use ITS PID for particle identification") - O2_DEFINE_CONFIGURABLE(cfgUseOnlyTPC, bool, true, "Use only TPC PID for daughter selection") - O2_DEFINE_CONFIGURABLE(cfgPIDParticle, int, 0, "1 = pion, 2 = kaon, 3 = proton, 4 = kshort, 5 = lambda, 6 = phi, 0 for no PID") - O2_DEFINE_CONFIGURABLE(cfgGetNsigmaQA, bool, true, "Get QA histograms for selection of pions, kaons, and protons") - O2_DEFINE_CONFIGURABLE(cfgGetdEdx, bool, true, "Get dEdx histograms for pions, kaons, and protons") - O2_DEFINE_CONFIGURABLE(cfgUseAntiLambda, bool, true, "Use AntiLambda candidates for analysis") - O2_DEFINE_CONFIGURABLE(cfgPIDUseRejection, bool, true, "True: use exclusion exclusion criteria for PID determination, false: don't use exclusion") + Service ccdb{}; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgQABasic, bool, true, "Enable QA histograms for event and track selection") + O2_DEFINE_CONFIGURABLE(cfgStrictTrackCounter, bool, false, "Strict track counter for multiplicity correlation cut, counts only tracks that pass all cuts and are used in the correlation") + O2_DEFINE_CONFIGURABLE(cfgSampleSize, int, 10, "Sample size for mixed event") + O2_DEFINE_CONFIGURABLE(cfgAsscPtMin, float, 0.2f, "Minimal pT for ref tracks") + O2_DEFINE_CONFIGURABLE(cfgAsscPtMax, float, 3.0f, "Maximal pT for ref tracks") + O2_DEFINE_CONFIGURABLE(cfgCutMerging, float, 0.0, "Merging cut on track merge") + O2_DEFINE_CONFIGURABLE(cfgUseCutMerging, bool, false, "use two track cut"); + O2_DEFINE_CONFIGURABLE(cfgMixEventNumMin, int, 5, "Minimum number of events to mix") + O2_DEFINE_CONFIGURABLE(cfgRadiusLow, float, 0.8, "Low radius for merging cut") + O2_DEFINE_CONFIGURABLE(cfgRadiusHigh, float, 2.5, "High radius for merging cut") + O2_DEFINE_CONFIGURABLE(cfgUsePtOrder, bool, false, "enable trigger pT < associated pT cut") + O2_DEFINE_CONFIGURABLE(cfgUsePtOrderInMixEvent, bool, false, "enable trigger pT < associated pT cut in mixed event") + } cfgGeneral; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgEfficiency, std::string, "", "CCDB path to efficiency object") + O2_DEFINE_CONFIGURABLE(cfgEfficiencyNch, std::string, "", "CCDB path to multiplicity dependent efficiency object") + O2_DEFINE_CONFIGURABLE(cfgUseEventWeights, bool, false, "Use event weights for mixed event") + O2_DEFINE_CONFIGURABLE(cfgCentralityWeight, std::string, "", "CCDB path to centrality weight object") + O2_DEFINE_CONFIGURABLE(cfgLocalEfficiency, bool, false, "Use local efficiency object") + O2_DEFINE_CONFIGURABLE(cfgLocalEfficiencyNch, bool, false, "Use local multiplicity dependent efficiency object"); + O2_DEFINE_CONFIGURABLE(cfgUseTrigWeight, bool, false, "enable trigger efficiency correction") + } cfgEffWeights; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgGlobalCutVertex, float, 10.0f, "Accepted z-vertex range") + O2_DEFINE_CONFIGURABLE(cfgCutOccupancyMax, int, 2000, "Maximum occupancy cut") + O2_DEFINE_CONFIGURABLE(cfgCutOccupancyMin, int, 0, "Minimum occupancy cut") + O2_DEFINE_CONFIGURABLE(cfgUseAdditionalEventCut, bool, true, "Use additional event cut on mult correlations") + O2_DEFINE_CONFIGURABLE(cfgCutMultMin, int, 0, "Minimum multiplicity for collision") + O2_DEFINE_CONFIGURABLE(cfgCutMultMax, int, 10, "Maximum multiplicity for collision") + O2_DEFINE_CONFIGURABLE(cfgSelCollByNch, bool, true, "Select collisions by Nch or centrality") + O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FV0A") + O2_DEFINE_CONFIGURABLE(cfgCentTableUnavailable, bool, false, "if a dataset does not provide centrality information") + O2_DEFINE_CONFIGURABLE(cfgCutCentMin, float, 60.0f, "Minimum centrality for collision") + O2_DEFINE_CONFIGURABLE(cfgCutCentMax, float, 80.0f, "Maximum centrality for collision") + O2_DEFINE_CONFIGURABLE(cfgUseCol2ForMixedEvSel, bool, true, "Use Mult/Cent cut on collision 2 during mixed event"); + Configurable> cfgUseEventCuts{"cfgUseEventCuts", {LongArrayInt.front().data(), 14, 1, {"Filtered Events", "Sel8", "kNoTimeFrameBorder", "kNoITSROFrameBorder", "kNoSameBunchPileup", "kIsGoodZvtxFT0vsPV", "kNoCollInTimeRangeStandard", "kIsGoodITSLayersAll", "kIsGoodITSLayer0123", "kNoCollInRofStandard", "kNoHighMultCollInPrevRof", "Occupancy", "Multcorrelation", "T0AV0ACut"}, {"EvCuts"}}, "Labeled array (int) for various cuts on resonances"}; + } cfgEvSel; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgGlobalPtMin, float, 0.2f, "minimum accepted track pT") + O2_DEFINE_CONFIGURABLE(cfgGlobalPtMax, float, 10.0f, "maximum accepted track pT") + O2_DEFINE_CONFIGURABLE(cfgGlobalEta, float, 0.8f, "Eta cut") + O2_DEFINE_CONFIGURABLE(cfgGlobalChi2prTPCcls, float, 2.5f, "Maximum chi2 per TPC clusters") + O2_DEFINE_CONFIGURABLE(cfgGlobalDCAz, float, 2.0f, "Maximum DCA to vertex z") + Configurable> cfgTrackCutsDCAxy{"cfgTrackCutsDCAxy", std::vector{"[0]*(0.0026+0.005/(x^1.01))", "[0]*(0.0026+0.005/(x^1.01))"}, "Functional form of pt-dependent DCAxy cut"}; + Configurable> cfgTrackCutsDCAz{"cfgTrackCutsDCAz", std::vector{"", ""}, "Functional form of pt-dependent DCAz cut"}; + Configurable> cfgTrackCuts{"cfgTrackCuts", {LongArrayFloat.front().data(), 10, 2, {"PtMin", "PtMax", "EtaCut", "Chi2PrTpcCls", "TpcCluster", "TpcCrossedRows", "ItsCluster", "DCAz", "DCAzNsigma", "DCAxyNsigma"}, {"General", "SelNch"}}, "Labeled array for track selections"}; + O2_DEFINE_CONFIGURABLE(cfgMCTrigIsPhysPrim, bool, false, "Switch for using only physical primary trigger particles in MC correlations"); + O2_DEFINE_CONFIGURABLE(cfgMCAsscIsPhysPrim, bool, false, "Switch for using only physical primary associated particles in MC correlations"); + } cfgTrkSel; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgQAdEdx, bool, false, "Get dEdx histograms for pions, kaons, and protons") + O2_DEFINE_CONFIGURABLE(cfgQANsigma, bool, false, "Get QA histograms for selection of pions, kaons, and protons") + O2_DEFINE_CONFIGURABLE(cfgPIDTofPtCut, float, 0.4f, "Minimum pt to use TOF N-sigma") + O2_DEFINE_CONFIGURABLE(cfgPIDUseIts, bool, false, "Use ITS PID for particle identification") + O2_DEFINE_CONFIGURABLE(cfgPIDUseRejection, bool, true, "True: use exclusion exclusion criteria for PID determination, false: don't use exclusion") + O2_DEFINE_CONFIGURABLE(cfgPIDOnlyTpcCut, float, 3.0f, "TPC N-sigma cut for pions, kaons, protons") + O2_DEFINE_CONFIGURABLE(cfgPIDUseOnlyTpc, bool, true, "Use only TPC PID for daughter selection") + O2_DEFINE_CONFIGURABLE(cfgPIDParticle, int, 0, "1 = pion, 2 = kaon, 3 = proton, 4 = kshort, 5 = lambda, 0 for no PID") + O2_DEFINE_CONFIGURABLE(cfgPIDUseReso, bool, false, "Switch to turn on resonance analysis") + O2_DEFINE_CONFIGURABLE(cfgPIDNclsPIDCut, int, 70, "Number of clusters used for PID selection") + O2_DEFINE_CONFIGURABLE(cfgPIDUseNclsPID, bool, false, "Switch for using cfgPIDNclsPIDCut") + Configurable> cfgPIDnSigmas{"cfgPIDnSigmas", {LongArrayFloat.front().data(), 6, 3, {"UpCut_pi", "UpCut_ka", "UpCut_pr", "LowCut_pi", "LowCut_ka", "LowCut_pr"}, {"TPC", "TOF", "ITS"}}, "Labeled array for n-sigma values for TPC, TOF, ITS for pions, kaons, protons (positive and negative)"}; + Configurable> cfgPIDResoCuts{"cfgPIDResoCuts", {LongArrayFloat.front().data(), 15, 2, {"cos_PAs", "massMin", "massMax", "PosTrackPt", "NegTrackPt", "DCAPosToPVMin", "DCANegToPVMin", "Lifetime", "RadiusMin", "RadiusMax", "RapidityMin", "RapidityMax", "ArmPodMinVal", "DCABetDaug", "CompMassRej"}, {"K0", "Lambda"}}, "Labeled array (float) for various cuts on resonances"}; + Configurable> cfgPIDResoSwitches{"cfgPIDResoSwitches", {LongArrayInt.front().data(), 7, 2, {"UseCosPA", "NMassBins", "UseDCABetDaug", "UseProperLifetime", "UseV0Radius", "UseArmPodCut", "UseCompMassRej"}, {"K0", "Lambda"}}, "Labeled array (int) for various cuts on resonances"}; + } cfgPIDConfigs; struct : ConfigurableGroup { O2_DEFINE_CONFIGURABLE(cfgMultCentHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 10.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); @@ -149,208 +172,229 @@ struct PidDiHadron { O2_DEFINE_CONFIGURABLE(cfgMultMultV0ALowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); O2_DEFINE_CONFIGURABLE(cfgMultMultV0ACutEnabled, bool, false, "Enable global multiplicity vs V0A multiplicity cut") Configurable> cfgMultMultV0ACutPars{"cfgMultMultV0ACutPars", std::vector{534.893, 184.344, 0.423539, -0.00331436, 5.34622e-06, 871.239, 53.3735, -0.203528, 0.000122758, 5.41027e-07}, "Global multiplicity vs V0A multiplicity cut parameter values"}; - O2_DEFINE_CONFIGURABLE(cfgDCAxyNSigma, float, 7, "Cut on number of sigma deviations from expected DCA in the transverse direction"); - O2_DEFINE_CONFIGURABLE(cfgDCAxy, std::string, "(0.0026+0.005/(x^1.01))", "Functional form of pt-dependent DCAxy cut"); std::vector multT0CCutPars; std::vector multPVT0CCutPars; std::vector multGlobalPVCutPars; std::vector multMultV0ACutPars; - TF1* fMultPVT0CCutLow = nullptr; - TF1* fMultPVT0CCutHigh = nullptr; - TF1* fMultT0CCutLow = nullptr; - TF1* fMultT0CCutHigh = nullptr; - TF1* fMultGlobalPVCutLow = nullptr; - TF1* fMultGlobalPVCutHigh = nullptr; - TF1* fMultMultV0ACutLow = nullptr; - TF1* fMultMultV0ACutHigh = nullptr; - TF1* fT0AV0AMean = nullptr; - TF1* fT0AV0ASigma = nullptr; - TF1* fPtDepDCAxy = nullptr; + std::unique_ptr fMultPVT0CCutLow = nullptr; + std::unique_ptr fMultPVT0CCutHigh = nullptr; + std::unique_ptr fMultT0CCutLow = nullptr; + std::unique_ptr fMultT0CCutHigh = nullptr; + std::unique_ptr fMultGlobalPVCutLow = nullptr; + std::unique_ptr fMultGlobalPVCutHigh = nullptr; + std::unique_ptr fMultMultV0ACutLow = nullptr; + std::unique_ptr fMultMultV0ACutHigh = nullptr; + std::unique_ptr fT0AV0AMean = nullptr; + std::unique_ptr fT0AV0ASigma = nullptr; + std::unique_ptr fPtDepDCAxy = nullptr; + std::unique_ptr fPtDepDCAxyForNch = nullptr; + std::unique_ptr fPtDepDCAz = nullptr; + std::unique_ptr fPtDepDCAzForNch = nullptr; + O2_DEFINE_CONFIGURABLE(cfgV0AT0Acut, int, 5, "V0AT0A cut") } cfgFuncParas; SliceCache cache; + // Axes for general QA ConfigurableAxis axisVertex{"axisVertex", {10, -10, 10}, "vertex axis for histograms"}; - ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 10, 20, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 260}, "multiplicity axis for histograms"}; - ConfigurableAxis axisCentrality{"axisCentrality", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, "centrality axis for histograms"}; - ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.2, 0.5, 1, 1.5, 2, 3, 4, 6, 10}, "pt axis for histograms"}; + ConfigurableAxis axisMultiplicity{"axisMultiplicity", {100, 0, 1000}, "multiplicity axis for histograms"}; + ConfigurableAxis axisCentrality{"axisCentrality", {100, 0, 100}, "centrality axis for histograms"}; + ConfigurableAxis axisPt{"axisPt", {98, 0.2, 10.0}, "pt axis for histograms"}; + + // Axes for Correlations + ConfigurableAxis axisMultForCorr{"axisMultForCorr", {VARIABLE_WIDTH, 0, 5, 10, 20, 50, 100, 150, 200, 300, 500}, "multiplicity axis for correlation histograms"}; + ConfigurableAxis axisCentForCorr{"axisCentForCorr", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, "centrality axis for correlation histograms"}; ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {72, -PIHalf, PIHalf * 3}, "delta phi axis for histograms"}; ConfigurableAxis axisDeltaEta{"axisDeltaEta", {48, -2.4, 2.4}, "delta eta axis for histograms"}; ConfigurableAxis axisPtTrigger{"axisPtTrigger", {VARIABLE_WIDTH, 0.2, 0.5, 1, 1.5, 2, 3, 4, 6, 10}, "pt trigger axis for histograms"}; - ConfigurableAxis axisPtAssoc{"axisPtAssoc", {VARIABLE_WIDTH, 0.2, 0.5, 1, 1.5, 2, 3, 4, 6, 10}, "pt associated axis for histograms"}; + ConfigurableAxis axisParticle{"axisParticle", {4, 0, 4}, "particle axis for correlation container"}; ConfigurableAxis axisVtxMix{"axisVtxMix", {VARIABLE_WIDTH, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, "vertex axis for mixed event histograms"}; ConfigurableAxis axisMultMix{"axisMultMix", {VARIABLE_WIDTH, 0, 10, 20, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 260}, "multiplicity / centrality axis for mixed event histograms"}; - ConfigurableAxis axisSample{"axisSample", {cfgSampleSize, 0, cfgSampleSize}, "sample axis for histograms"}; + + // Axes for PID QA ConfigurableAxis axisNsigmaTPC{"axisNsigmaTPC", {80, -5, 5}, "nsigmaTPC axis"}; ConfigurableAxis axisNsigmaTOF{"axisNsigmaTOF", {80, -5, 5}, "nsigmaTOF axis"}; ConfigurableAxis axisNsigmaITS{"axisNsigmaITS", {80, -5, 5}, "nsigmaITS axis"}; ConfigurableAxis axisTpcSignal{"axisTpcSignal", {250, 0, 250}, "dEdx axis for TPC"}; ConfigurableAxis axisSigma{"axisSigma", {200, 0, 20}, "sigma axis for TPC"}; - Configurable> cfgUseEventCuts{"cfgUseEventCuts", {LongArrayInt[0], 15, 1, {"Filtered Events", "Sel8", "kNoTimeFrameBorder", "kNoITSROFrameBorder", "kNoSameBunchPileup", "kIsGoodZvtxFT0vsPV", "kNoCollInTimeRangeStandard", "kIsGoodITSLayersAll", "kNoCollInRofStandard", "kNoHighMultCollInPrevRof", "Occupancy", "Multcorrelation", "T0AV0ACut", "kIsVertexITSTPC", "kTVXinTRD"}, {"EvCuts"}}, "Labeled array (int) for various cuts on resonances"}; - Configurable> nSigmas{"nSigmas", {LongArrayFloat[0], 6, 3, {"UpCut_pi", "UpCut_ka", "UpCut_pr", "LowCut_pi", "LowCut_ka", "LowCut_pr"}, {"TPC", "TOF", "ITS"}}, "Labeled array for n-sigma values for TPC, TOF, ITS for pions, kaons, protons (positive and negative)"}; - Configurable> resonanceCuts{"resonanceCuts", {LongArrayFloat[0], 12, 3, {"cos_PAs", "massMin", "massMax", "PosTrackPt", "NegTrackPt", "DCAPosToPVMin", "DCANegToPVMin", "Lifetime", "RadiusMin", "RadiusMax", "Rapidity", "ArmPodMinVal"}, {"K0", "Lambda", "Phi"}}, "Labeled array (float) for various cuts on resonances"}; - Configurable> resonanceSwitches{"resonanceSwitches", {LongArrayInt[0], 6, 3, {"UseCosPA", "NMassBins", "DCABetDaug", "UseProperLifetime", "UseV0Radius", "UseArmPodCut"}, {"K0", "Lambda", "Phi"}}, "Labeled array (int) for various cuts on resonances"}; - - ConfigurableAxis axisVertexEfficiency{"axisVertexEfficiency", {1, 0, 1}, "vertex axis for efficiency histograms"}; - ConfigurableAxis axisEtaEfficiency{"axisEtaEfficiency", {1, 0, 1}, "eta axis for efficiency histograms"}; - ConfigurableAxis axisPtEfficiency{"axisPtEfficiency", {1, 0, 1}, "pt axis for efficiency histograms"}; - // make the filters and cuts. - Filter collisionFilter = (nabs(aod::collision::posZ) < cfgCutVertex); - Filter trackFilter = (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPtPOIMin) && (aod::track::pt < cfgCutPtPOIMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (aod::track::tpcChi2NCl < cfgCutChi2prTPCcls) && (nabs(aod::track::dcaZ) < cfgCutDCAz); + Filter collisionFilter = (nabs(aod::collision::posZ) < cfgEvSel.cfgGlobalCutVertex); + Filter mcCollisionFilter = (nabs(aod::mccollision::posZ) < cfgEvSel.cfgGlobalCutVertex); + Filter trackFilter = (nabs(aod::track::eta) < cfgTrkSel.cfgGlobalEta) && (aod::track::pt > cfgTrkSel.cfgGlobalPtMin) && (aod::track::pt < cfgTrkSel.cfgGlobalPtMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (aod::track::tpcChi2NCl < cfgTrkSel.cfgGlobalChi2prTPCcls) && (nabs(aod::track::dcaZ) < cfgTrkSel.cfgGlobalDCAz); + Filter mcTrackFilter = (nabs(aod::mcparticle::eta) < cfgTrkSel.cfgGlobalEta) && (aod::mcparticle::pt > cfgTrkSel.cfgGlobalPtMin) && (aod::mcparticle::pt < cfgTrkSel.cfgGlobalPtMax); using FilteredCollisions = soa::Filtered>; using FilteredTracks = soa::Filtered>; - using FilteredMcTracks = soa::Filtered>; + using FilteredTracksMcLabels = soa::Filtered>; using V0TrackCandidate = aod::V0Datas; + using FilteredMcCollisions = soa::Filtered; + using FilteredMcTracks = soa::Filtered; + using SmallGroupMcCollisions = soa::SmallGroups>; + + Partition asscTracks = (aod::track::pt > cfgGeneral.cfgAsscPtMin) && (aod::track::pt < cfgGeneral.cfgAsscPtMax); + Partition asscTracksMC = (aod::mcparticle::pt > cfgGeneral.cfgAsscPtMin) && (aod::mcparticle::pt < cfgGeneral.cfgAsscPtMax); - Preslice perCollision = aod::track::collisionId; - Preslice perCollisionV0 = aod::v0::collisionId; + PresliceUnsorted collisionPerMCCollision = aod::mccollisionlabel::mcCollisionId; // Corrections TH3D* mEfficiency = nullptr; + TH1D* mEfficiencyNch = nullptr; TH1D* mCentralityWeight = nullptr; bool correctionsLoaded = false; // Define the outputs - OutputObj same{"sameEvent"}; - OutputObj mixed{"mixedEvent"}; - OutputObj sameReso{"sameEventReso"}; - OutputObj mixedReso{"mixedEventReso"}; + OutputObj same{"sameEvent"}; + OutputObj mixed{"mixedEvent"}; + OutputObj sameReso{"sameEventReso"}; + OutputObj mixedReso{"mixedEventReso"}; HistogramRegistry histos{"histos"}; // define global variables - TRandom3* gRandom = new TRandom3(); + TRandom3 fRandom{0}; enum PIDIndex { - kCharged = 0, - kPions, - kKaons, - kProtons, - kK0, - kLambda, - kPhi + UseCharged = 0, + UsePions, + UseKaons, + UseProtons, + UseK0, + UseLambda }; enum PiKpArrayIndex { - iPionUp = 0, - iKaonUp, - iProtonUp, - iPionLow, - iKaonLow, - iProtonLow + IndexPionUp = 0, + IndexKaonUp, + IndexProtonUp, + IndexPionLow, + IndexKaonLow, + IndexProtonLow }; enum ResoArrayIndex { - iK0 = 0, - iLambda = 1, - iPhi = 2, - NResoParticles = 3 + IndexK0 = 0, + IndexLambda, + NumResoParticles }; enum ResoParticleCuts { - kCosPA = 0, - kMassMin, - kMassMax, - kPosTrackPt, - kNegTrackPt, - kDCAPosToPVMin, - kDCANegToPVMin, - kLifeTime, - kRadiusMin, - kRadiusMax, - kRapidity, - kArmPodMinVal, - kNParticleCuts + CutCosPA = 0, + CutMassMin, + CutMassMax, + CutPosTrackPt, + CutNegTrackPt, + CutDCAPosToPVMin, + CutDCANegToPVMin, + CutLifeTime, + CutRadiusMin, + CutRadiusMax, + CutRapidityMin, + CutRapidityMax, + CutArmPodMinVal, + CutDCABetDaug, + CutCompMassRej, + NumParticleCuts }; enum ResoParticleSwitches { - kUseCosPA = 0, - kMassBins, - kDCABetDaug, - kUseProperLifetime, - kUseV0Radius, - kUseArmPodCut, - kNParticleSwitches + UseCosPA = 0, + UseMassBins, + UseDCABetDaug, + UseProperLifetime, + UseV0Radius, + UseArmPodCut, + UseCompMassRej, + NumParticleSwitches }; + + enum ResoSelectionFlags { + FillCandidate = 0, + FillDaughterPt, + FillMassCut, + FillRapidityCut, + FillDCAtoPV, + FillDCAxDaughters, + FillV0Radius, + FillCosPA, + FillProperLifetime, + FillArmPodCut, + FillCompetingMass, + FillDaughterTrackSelected, + }; + enum EventCutTypes { - kFilteredEvents = 0, - kAfterSel8, - kUseNoTimeFrameBorder, - kUseNoITSROFrameBorder, - kUseNoSameBunchPileup, - kUseGoodZvtxFT0vsPV, - kUseNoCollInTimeRangeStandard, - kUseGoodITSLayersAll, - kUseNoCollInRofStandard, - kUseNoHighMultCollInPrevRof, - kUseOccupancy, - kUseMultCorrCut, - kUseT0AV0ACut, - kUseVertexITSTPC, - kUseTVXinTRD, - kNEventCuts + FilteredEvents = 0, + AfterSel8, + UseNoTimeFrameBorder, + UseNoITSROFrameBorder, + UseNoSameBunchPileup, + UseGoodZvtxFT0vsPV, + UseNoCollInTimeRangeStandard, + UseGoodITSLayersAll, + UseGoodITSLayer0123, + UseNoCollInRofStandard, + UseNoHighMultCollInPrevRof, + UseOccupancy, + UseMultCorrCut, + UseT0AV0ACut, + NumEventCuts + }; + enum TrackCuts { + TrkCutPtMin = 0, + TrkCutPtMax, + TrkCutEtaCut, + TrkCutChi2PrTpcCls, + TrkCutTpcCluster, + TrkCutTpcCrossedRows, + TrkCutItsCluster, + TrkCutDCAz, + TrkCutDCAzNsigma, + TrkCutDCAxyNsigma + }; + + enum TrackCutGroup { + UseGenTrkCuts = 0, + UseNchSelCuts, + NumTrackCutTypes }; enum CentEstimators { - kCentFT0C = 0, - kCentFT0CVariant1, - kCentFT0M, - kCentFV0A, - // Count the total number of enum - kCount_CentEstimators + UseCentFT0C = 0, + UseCentFT0CVariant1, + UseCentFT0M, + UseCentFV0A, + NumCentEstimators }; enum EventType { SameEvent = 1, MixedEvent = 3 }; enum DetectorType { - kTPC = 0, - kTOF, - kITS + UseTPC = 0, + UseTOF, + UseITS }; enum EventCutType { - kEvCut1 = 0, - kNEvCutTypes = 1 + EvCut1 = 0, + NumEvCutTypes = 1 + }; + enum Stage { + Before = 0, + After }; - std::array, 15> eventCuts; - std::array, 12> resoCutVals; - std::array, 7> resoSwitchVals; - std::array tofNsigmaCut; - std::array itsNsigmaCut; - std::array tpcNsigmaCut; - + std::array, 10> trackCuts{}; + std::array, 14> eventCuts{}; + std::array, 6> nSigmaVals{}; + std::array, 15> resoCutVals{}; + std::array, 7> resoSwitchVals{}; // persistent caches std::vector efficiencyAssociatedCache; + std::vector useAssociatedCache; void init(InitContext&) { - readMatrix(resonanceCuts->getData(), resoCutVals, kCosPA, kNParticleCuts, iK0, NResoParticles); - readMatrix(resonanceSwitches->getData(), resoSwitchVals, kUseCosPA, kNParticleSwitches, iK0, NResoParticles); - readMatrix(cfgUseEventCuts->getData(), eventCuts, kFilteredEvents, kNEventCuts, kEvCut1, kNEvCutTypes); - - tpcNsigmaCut[iPionUp] = nSigmas->getData()[iPionUp][kTPC]; - tpcNsigmaCut[iKaonUp] = nSigmas->getData()[iKaonUp][kTPC]; - tpcNsigmaCut[iProtonUp] = nSigmas->getData()[iProtonUp][kTPC]; - tpcNsigmaCut[iPionLow] = nSigmas->getData()[iPionLow][kTPC]; - tpcNsigmaCut[iKaonLow] = nSigmas->getData()[iKaonLow][kTPC]; - tpcNsigmaCut[iProtonLow] = nSigmas->getData()[iProtonLow][kTPC]; - - tofNsigmaCut[iPionUp] = nSigmas->getData()[iPionUp][kTOF]; - tofNsigmaCut[iKaonUp] = nSigmas->getData()[iKaonUp][kTOF]; - tofNsigmaCut[iProtonUp] = nSigmas->getData()[iProtonUp][kTOF]; - tofNsigmaCut[iPionLow] = nSigmas->getData()[iPionLow][kTOF]; - tofNsigmaCut[iKaonLow] = nSigmas->getData()[iKaonLow][kTOF]; - tofNsigmaCut[iProtonLow] = nSigmas->getData()[iProtonLow][kTOF]; - - itsNsigmaCut[iPionUp] = nSigmas->getData()[iPionUp][kITS]; - itsNsigmaCut[iKaonUp] = nSigmas->getData()[iKaonUp][kITS]; - itsNsigmaCut[iProtonUp] = nSigmas->getData()[iProtonUp][kITS]; - itsNsigmaCut[iPionLow] = nSigmas->getData()[iPionLow][kITS]; - itsNsigmaCut[iKaonLow] = nSigmas->getData()[iKaonLow][kITS]; - itsNsigmaCut[iProtonLow] = nSigmas->getData()[iProtonLow][kITS]; - - AxisSpec axisK0Mass = {resoSwitchVals[kMassBins][iK0], resoCutVals[kMassMin][iK0], resoCutVals[kMassMax][iK0]}; - AxisSpec axisLambdaMass = {resoSwitchVals[kMassBins][iLambda], resoCutVals[kMassMin][iLambda], resoCutVals[kMassMax][iLambda]}; - AxisSpec axisPhiMass = {resoSwitchVals[kMassBins][iPhi], resoCutVals[kMassMin][iPhi], resoCutVals[kMassMax][iPhi]}; - - if (cfgCentTableUnavailable && !cfgSelCollByNch) { + readMatrix(cfgTrkSel.cfgTrackCuts->getData(), trackCuts); + readMatrix(cfgEvSel.cfgUseEventCuts->getData(), eventCuts); + readMatrix(cfgPIDConfigs.cfgPIDnSigmas->getData(), nSigmaVals); + readMatrix(cfgPIDConfigs.cfgPIDResoCuts->getData(), resoCutVals); + readMatrix(cfgPIDConfigs.cfgPIDResoSwitches->getData(), resoSwitchVals); + + if (cfgEvSel.cfgCentTableUnavailable && !cfgEvSel.cfgSelCollByNch) { LOGF(fatal, "Centrality table is unavailable, cannot select collisions by centrality"); } const AxisSpec axisPhi{72, 0.0, constants::math::TwoPI, "#varphi"}; @@ -363,238 +407,280 @@ struct PidDiHadron { LOGF(info, "Starting init"); - // Creating mass axis depending on particle - 4 = kshort, 5 = lambda, 6 = phi - AxisSpec massAxisReso = {10, 0, 1, "mass"}; - if (cfgPIDParticle == kK0) - massAxisReso = {resoSwitchVals[kMassBins][iK0], resoCutVals[kMassMin][iK0], resoCutVals[kMassMax][iK0], "M_{#pi^{+}#pi^{-}} (GeV/c^{2})"}; - if (cfgPIDParticle == kLambda) - massAxisReso = {resoSwitchVals[kMassBins][iLambda], resoCutVals[kMassMin][iLambda], resoCutVals[kMassMax][iLambda], "M_{p#pi^{-}} (GeV/c^{2})"}; - if (cfgPIDParticle == kPhi) - massAxisReso = {resoSwitchVals[kMassBins][iPhi], resoCutVals[kMassMin][iPhi], resoCutVals[kMassMax][iPhi], "M_{K^{+}K^{-}} (GeV/c^{2})"}; + // Creating mass axis depending on particle - 4 = kshort, 5 = lambda + AxisSpec axisInvMass = {10, 0, 1, "mass"}; + if (cfgPIDConfigs.cfgPIDParticle == UseK0) { + const auto bins = resoSwitchVals[UseMassBins][IndexK0]; + const auto min = resoCutVals[CutMassMin][IndexK0]; + const auto max = resoCutVals[CutMassMax][IndexK0]; + + axisInvMass = {bins, min, max, "M_{#pi^{+}#pi^{-}} (GeV/c^{2})"}; + } + if (cfgPIDConfigs.cfgPIDParticle == UseLambda) { + const auto bins = resoSwitchVals[UseMassBins][IndexLambda]; + const auto min = resoCutVals[CutMassMin][IndexLambda]; + const auto max = resoCutVals[CutMassMax][IndexLambda]; + + axisInvMass = {bins, min, max, "M_{p#pi} (GeV/c^{2})"}; + } + + // Set the sample axis + auto maxSample = static_cast(cfgGeneral.cfgSampleSize); + AxisSpec axisSample{cfgGeneral.cfgSampleSize, 0, maxSample, "Sample"}; + + // Choose if it is Nch selection or Centrality selection + AxisSpec axisEventClass = {axisMultForCorr, "N_{ch}"}; + + if (!cfgEvSel.cfgSelCollByNch) { + axisEventClass = {axisCentForCorr, "Centrality"}; + } // Event Counter - if ((doprocessSame || doprocessSameReso || doprocessMC) && cfgUseAdditionalEventCut) { + if ((doprocessSame || doprocessSameReso || doprocessMCPID) && cfgEvSel.cfgUseAdditionalEventCut) { histos.add("hEventCount", "Number of Events;; Count", {HistType::kTH1D, {{15, -0.5, 14.5}}}); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kFilteredEvents + 1, "Filtered event"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kAfterSel8 + 1, "After sel8"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoTimeFrameBorder + 1, "kNoTimeFrameBorder"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoITSROFrameBorder + 1, "kNoITSROFrameBorder"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoSameBunchPileup + 1, "kNoSameBunchPileup"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoCollInRofStandard + 1, "kNoCollInRofStandard"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoHighMultCollInPrevRof + 1, "kNoHighMultCollInPrevRof"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseOccupancy + 1, "Occupancy Cut"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseMultCorrCut + 1, "MultCorrelation Cut"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseT0AV0ACut + 1, "T0AV0A cut"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseVertexITSTPC + 1, "kIsVertexITSTPC"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseTVXinTRD + 1, "kTVXinTRD"); - } - - if (cfgPIDParticle == kK0) { // For K0 - histos.add("PiPlusTPC_K0", "", {HistType::kTH2D, {{axisPt, axisNsigmaTPC}}}); - histos.add("PiMinusTPC_K0", "", {HistType::kTH2D, {{axisPt, axisNsigmaTPC}}}); - histos.add("PiPlusTOF_K0", "", {HistType::kTH2D, {{axisPt, axisNsigmaTOF}}}); - histos.add("PiMinusTOF_K0", "", {HistType::kTH2D, {{axisPt, axisNsigmaTOF}}}); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(FilteredEvents + 1, "Filtered events"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(AfterSel8 + 1, "After sel8"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoTimeFrameBorder + 1, "kNoTimeFrameBorder"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoITSROFrameBorder + 1, "kNoITSROFrameBorder"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoSameBunchPileup + 1, "kNoSameBunchPileup"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodITSLayer0123 + 1, "kIsGoodITSLayer0123"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoCollInRofStandard + 1, "kNoCollInRofStandard"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoHighMultCollInPrevRof + 1, "kNoHighMultCollInPrevRof"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseOccupancy + 1, "Occupancy Cut"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseMultCorrCut + 1, "MultCorrelation Cut"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseT0AV0ACut + 1, "T0AV0A cut"); + } + + if (cfgPIDConfigs.cfgPIDUseReso && cfgPIDConfigs.cfgPIDParticle == UseK0) { // For K0 + histos.add("PiPlusTPC_K0", "", {HistType::kTH2D, {{axisPtTrigger, axisNsigmaTPC}}}); + histos.add("PiMinusTPC_K0", "", {HistType::kTH2D, {{axisPtTrigger, axisNsigmaTPC}}}); + histos.add("PiPlusTOF_K0", "", {HistType::kTH2D, {{axisPtTrigger, axisNsigmaTOF}}}); + histos.add("PiMinusTOF_K0", "", {HistType::kTH2D, {{axisPtTrigger, axisNsigmaTOF}}}); histos.add("hK0Phi", "", {HistType::kTH1D, {axisPhi}}); histos.add("hK0Eta", "", {HistType::kTH1D, {axisEta}}); - histos.add("hK0Count", "Number of K0;; Count", {HistType::kTH1D, {{11, 0, 11}}}); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(1, "K0 candidates"); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(2, "Daughter pt"); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(3, "Mass cut"); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(4, "Rapidity cut"); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(5, "DCA to PV"); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(6, "DCA between daughters"); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(7, "V0radius"); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(8, "CosPA"); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(9, "Proper lifetime"); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(10, "ArmenterosPod"); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(11, "Daughter track selection"); - } - if (cfgPIDParticle == kLambda) { // For Lambda - histos.add("PrPlusTPC_L", "", {HistType::kTH2D, {{axisPt, axisNsigmaTPC}}}); - histos.add("PiMinusTPC_L", "", {HistType::kTH2D, {{axisPt, axisNsigmaTPC}}}); - histos.add("PrPlusTOF_L", "", {HistType::kTH2D, {{axisPt, axisNsigmaTOF}}}); - histos.add("PiMinusTOF_L", "", {HistType::kTH2D, {{axisPt, axisNsigmaTOF}}}); + histos.add("hK0Count", "Number of K0;; Count", {HistType::kTH1D, {{12, -0.5, 11.5}}}); + histos.get(HIST("hK0Count"))->GetYaxis()->SetTitle("Counts"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillCandidate + 1, "K0 candidates"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillDaughterPt + 1, "Daughter pt"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillMassCut + 1, "Mass cut"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillRapidityCut + 1, "Rapidity cut"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillDCAtoPV + 1, "DCA to PV"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillDCAxDaughters + 1, "DCA between daughters"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillV0Radius + 1, "V0radius"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillCosPA + 1, "CosPA"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillProperLifetime + 1, "Proper lifetime"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillArmPodCut + 1, "ArmenterosPod"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillCompetingMass + 1, "CompetingMassRejection"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillDaughterTrackSelected + 1, "Daughter track selection"); + } + if (cfgPIDConfigs.cfgPIDUseReso && cfgPIDConfigs.cfgPIDParticle == UseLambda) { // For Lambda + histos.add("PrPlusTPC_L", "", {HistType::kTH2D, {{axisPtTrigger, axisNsigmaTPC}}}); + histos.add("PiMinusTPC_L", "", {HistType::kTH2D, {{axisPtTrigger, axisNsigmaTPC}}}); + histos.add("PrPlusTOF_L", "", {HistType::kTH2D, {{axisPtTrigger, axisNsigmaTOF}}}); + histos.add("PiMinusTOF_L", "", {HistType::kTH2D, {{axisPtTrigger, axisNsigmaTOF}}}); histos.add("hLambdaPhi", "", {HistType::kTH1D, {axisPhi}}); histos.add("hLambdaEta", "", {HistType::kTH1D, {axisEta}}); - histos.add("hLambdaCount", "Number of Lambda;; Count", {HistType::kTH1D, {{10, 0, 10}}}); - histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(1, "Lambda candidates"); - histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(2, "Daughter pt"); - histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(3, "Mass cut"); - histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(4, "Rapidity cut"); - histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(5, "DCA to PV"); - histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(6, "DCA between daughters"); - histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(7, "V0radius"); - histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(8, "CosPA"); - histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(9, "Proper lifetime"); - histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(10, "Daughter track selection"); - } - if (cfgPIDParticle == kPhi) { // For Phi - histos.add("KaPlusTPC", "", {HistType::kTH2D, {{axisPt, axisNsigmaTPC}}}); - histos.add("KaMinusTPC", "", {HistType::kTH2D, {{axisPt, axisNsigmaTPC}}}); - histos.add("KaPlusTOF", "", {HistType::kTH2D, {{axisPt, axisNsigmaTOF}}}); - histos.add("KaMinusTOF", "", {HistType::kTH2D, {{axisPt, axisNsigmaTOF}}}); - histos.add("hPhiPhi", "", {HistType::kTH1D, {axisPhi}}); - histos.add("hPhiEta", "", {HistType::kTH1D, {axisEta}}); - histos.add("hPhiMass_sparse", "", {HistType::kTHnSparseD, {{axisPhiMass, axisPt, axisMultiplicity}}}); - - histos.add("hPhiCount", "Number of Phi;; Count", {HistType::kTH1D, {{5, 0, 5}}}); - histos.get(HIST("hPhiCount"))->GetXaxis()->SetBinLabel(1, "Phi candidates"); - histos.get(HIST("hPhiCount"))->GetXaxis()->SetBinLabel(2, "Daughter track selection"); - histos.get(HIST("hPhiCount"))->GetXaxis()->SetBinLabel(3, "Fake Kaon"); - histos.get(HIST("hPhiCount"))->GetXaxis()->SetBinLabel(4, "CosPA"); - histos.get(HIST("hPhiCount"))->GetXaxis()->SetBinLabel(5, "Rapidity cut"); + histos.add("hLambdaCount", "Number of Lambda;; Count", {HistType::kTH1D, {{12, -0.5, 11.5}}}); + histos.get(HIST("hLambdaCount"))->GetYaxis()->SetTitle("Counts"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillCandidate + 1, "Lambda candidates"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillDaughterPt + 1, "Daughter pt"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillMassCut + 1, "Mass cut"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillRapidityCut + 1, "Rapidity cut"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillDCAtoPV + 1, "DCA to PV"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillDCAxDaughters + 1, "DCA between daughters"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillV0Radius + 1, "V0radius"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillCosPA + 1, "CosPA"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillProperLifetime + 1, "Proper lifetime"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillArmPodCut + 1, "ArmenterosPod"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillCompetingMass + 1, "CompetingMassRejection"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillDaughterTrackSelected + 1, "Daughter track selection"); } // Multiplicity correlation cuts - if (eventCuts[kUseMultCorrCut][kEvCut1]) { + if (eventCuts[UseMultCorrCut][EvCut1] != 0) { cfgFuncParas.multT0CCutPars = cfgFuncParas.cfgMultT0CCutPars; cfgFuncParas.multPVT0CCutPars = cfgFuncParas.cfgMultPVT0CCutPars; cfgFuncParas.multGlobalPVCutPars = cfgFuncParas.cfgMultGlobalPVCutPars; cfgFuncParas.multMultV0ACutPars = cfgFuncParas.cfgMultMultV0ACutPars; - cfgFuncParas.fMultPVT0CCutLow = new TF1("fMultPVT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); - cfgFuncParas.fMultPVT0CCutLow->SetParameters(&(cfgFuncParas.multPVT0CCutPars[0])); - cfgFuncParas.fMultPVT0CCutHigh = new TF1("fMultPVT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); - cfgFuncParas.fMultPVT0CCutHigh->SetParameters(&(cfgFuncParas.multPVT0CCutPars[0])); - - cfgFuncParas.fMultT0CCutLow = new TF1("fMultT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); - cfgFuncParas.fMultT0CCutLow->SetParameters(&(cfgFuncParas.multT0CCutPars[0])); - cfgFuncParas.fMultT0CCutHigh = new TF1("fMultT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); - cfgFuncParas.fMultT0CCutHigh->SetParameters(&(cfgFuncParas.multT0CCutPars[0])); - - cfgFuncParas.fMultGlobalPVCutLow = new TF1("fMultGlobalPVCutLow", cfgFuncParas.cfgMultMultPVLowCutFunction->c_str(), 0, 4000); - cfgFuncParas.fMultGlobalPVCutLow->SetParameters(&(cfgFuncParas.multGlobalPVCutPars[0])); - cfgFuncParas.fMultGlobalPVCutHigh = new TF1("fMultGlobalPVCutHigh", cfgFuncParas.cfgMultMultPVHighCutFunction->c_str(), 0, 4000); - cfgFuncParas.fMultGlobalPVCutHigh->SetParameters(&(cfgFuncParas.multGlobalPVCutPars[0])); - - cfgFuncParas.fMultMultV0ACutLow = new TF1("fMultMultV0ACutLow", cfgFuncParas.cfgMultMultV0ALowCutFunction->c_str(), 0, 4000); - cfgFuncParas.fMultMultV0ACutLow->SetParameters(&(cfgFuncParas.multMultV0ACutPars[0])); - cfgFuncParas.fMultMultV0ACutHigh = new TF1("fMultMultV0ACutHigh", cfgFuncParas.cfgMultMultV0AHighCutFunction->c_str(), 0, 4000); - cfgFuncParas.fMultMultV0ACutHigh->SetParameters(&(cfgFuncParas.multMultV0ACutPars[0])); - } - if (eventCuts[kUseT0AV0ACut][kEvCut1]) { - cfgFuncParas.fT0AV0AMean = new TF1("fT0AV0AMean", "[0]+[1]*x", 0, 200000); + + cfgFuncParas.fMultPVT0CCutLow = std::make_unique("fMultPVT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultPVT0CCutLow->SetParameters(cfgFuncParas.multPVT0CCutPars.data()); + cfgFuncParas.fMultPVT0CCutHigh = std::make_unique("fMultPVT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultPVT0CCutHigh->SetParameters(cfgFuncParas.multPVT0CCutPars.data()); + + cfgFuncParas.fMultT0CCutLow = std::make_unique("fMultT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultT0CCutLow->SetParameters(cfgFuncParas.multT0CCutPars.data()); + cfgFuncParas.fMultT0CCutHigh = std::make_unique("fMultT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultT0CCutHigh->SetParameters(cfgFuncParas.multT0CCutPars.data()); + + cfgFuncParas.fMultGlobalPVCutLow = std::make_unique("fMultGlobalPVCutLow", cfgFuncParas.cfgMultMultPVLowCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultGlobalPVCutLow->SetParameters(cfgFuncParas.multGlobalPVCutPars.data()); + cfgFuncParas.fMultGlobalPVCutHigh = std::make_unique("fMultGlobalPVCutHigh", cfgFuncParas.cfgMultMultPVHighCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultGlobalPVCutHigh->SetParameters(cfgFuncParas.multGlobalPVCutPars.data()); + + cfgFuncParas.fMultMultV0ACutLow = std::make_unique("fMultMultV0ACutLow", cfgFuncParas.cfgMultMultV0ALowCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultMultV0ACutLow->SetParameters(cfgFuncParas.multMultV0ACutPars.data()); + cfgFuncParas.fMultMultV0ACutHigh = std::make_unique("fMultMultV0ACutHigh", cfgFuncParas.cfgMultMultV0AHighCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultMultV0ACutHigh->SetParameters(cfgFuncParas.multMultV0ACutPars.data()); + } + if (eventCuts[UseT0AV0ACut][EvCut1] != 0) { + cfgFuncParas.fT0AV0AMean = std::make_unique("fT0AV0AMean", "[0]+[1]*x", 0, 200000); cfgFuncParas.fT0AV0AMean->SetParameters(-1601.0581, 9.417652e-01); - cfgFuncParas.fT0AV0ASigma = new TF1("fT0AV0ASigma", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 200000); + cfgFuncParas.fT0AV0ASigma = std::make_unique("fT0AV0ASigma", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 200000); cfgFuncParas.fT0AV0ASigma->SetParameters(463.4144, 6.796509e-02, -9.097136e-07, 7.971088e-12, -2.600581e-17); } - cfgFuncParas.fPtDepDCAxy = new TF1("ptDepDCAxy", Form("[0]*%s", cfgFuncParas.cfgDCAxy->c_str()), 0.001, 100); - cfgFuncParas.fPtDepDCAxy->SetParameter(0, cfgFuncParas.cfgDCAxyNSigma); - LOGF(info, "DCAxy pt-dependence function: %s", Form("[0]*%s", cfgFuncParas.cfgDCAxy->c_str())); + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].empty()) { + cfgFuncParas.fPtDepDCAxy = std::make_unique("ptDepDCAxy", cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAxy->SetParameter(0, trackCuts[TrkCutDCAxyNsigma][UseGenTrkCuts]); + LOGF(info, "DCAxy pt-dependence function: %0.1f * %s", trackCuts[TrkCutDCAxyNsigma][UseGenTrkCuts], cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].c_str()); + } + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].empty()) { + cfgFuncParas.fPtDepDCAxyForNch = std::make_unique("ptDepDCAxyForNch", cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAxyForNch->SetParameter(0, trackCuts[TrkCutDCAxyNsigma][UseNchSelCuts]); + LOGF(info, "DCAxy pt-dependence function for Nch: %0.1f * %s", trackCuts[TrkCutDCAxyNsigma][UseNchSelCuts], cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].c_str()); + } + + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].empty()) { + cfgFuncParas.fPtDepDCAz = std::make_unique("ptDepDCAz", cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAz->SetParameter(0, trackCuts[TrkCutDCAzNsigma][UseGenTrkCuts]); + LOGF(info, "DCAz pt-dependence function: %0.1f * %s", trackCuts[TrkCutDCAzNsigma][UseGenTrkCuts], cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].c_str()); + } + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].empty()) { + cfgFuncParas.fPtDepDCAzForNch = std::make_unique("ptDepDCAzForNch", cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAzForNch->SetParameter(0, trackCuts[TrkCutDCAzNsigma][UseNchSelCuts]); + LOGF(info, "DCAz pt-dependence function for Nch: %0.1f * %s", trackCuts[TrkCutDCAzNsigma][UseNchSelCuts], cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].c_str()); + } + + std::string hCentTitle = "Centrality distribution, Estimator " + std::to_string(cfgEvSel.cfgCentEstimator); - std::string hCentTitle = "Centrality distribution, Estimator " + std::to_string(cfgCentEstimator); // Make histograms to check the distributions after cuts if (doprocessSame || doprocessSameReso) { - histos.add("deltaEta_deltaPhi_same", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEta}}); // check to see the delta eta and delta phi distribution histos.add("Nch", "N_{ch}", {HistType::kTH1D, {axisMultiplicity}}); - histos.add("Nch_used", "N_{ch}", {HistType::kTH1D, {axisMultiplicity}}); // histogram to see how many events are in the same and mixed event + histos.add("Nch_corrected", "N_{ch}", {HistType::kTH1D, {axisMultiplicity}}); histos.add("Centrality", hCentTitle.c_str(), {HistType::kTH1D, {axisCentrality}}); histos.add("CentralityWeighted", hCentTitle.c_str(), {HistType::kTH1D, {{100, 0, 100}}}); - histos.add("Centrality_used", hCentTitle.c_str(), {HistType::kTH1D, {axisCentrality}}); // histogram to see how many events are in the same and mixed event histos.add("zVtx", "zVtx", {HistType::kTH1D, {axisVertex}}); - histos.add("zVtx_used", "zVtx_used", {HistType::kTH1D, {axisVertex}}); - - if (cfgPIDParticle == kCharged || cfgPIDParticle == kPions || cfgPIDParticle == kKaons || cfgPIDParticle == kProtons) { - histos.add("Phi", "Phi", {HistType::kTH1D, {axisPhi}}); - histos.add("Eta", "Eta", {HistType::kTH1D, {axisEta}}); - histos.add("EtaCorrected", "EtaCorrected", {HistType::kTH1D, {axisEta}}); - histos.add("pT", "pT", {HistType::kTH1D, {axisPtTrigger}}); - histos.add("pTCorrected", "pTCorrected", {HistType::kTH1D, {axisPtTrigger}}); - histos.add("Trig_hist", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger}}}); - - if (cfgGetNsigmaQA) { - if (!cfgUseItsPID) { - histos.add("TofTpcNsigma_before", "", {HistType::kTHnSparseD, {{axisNsigmaTPC, axisNsigmaTOF, axisPt}}}); - histos.add("TofTpcNsigma_after", "", {HistType::kTHnSparseD, {{axisNsigmaTPC, axisNsigmaTOF, axisPt}}}); - - if (cfgGetdEdx) { - histos.add("TpcdEdx_ptwise", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("ExpTpcdEdx_ptwise", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("ExpSigma_ptwise", "", {HistType::kTHnSparseD, {{axisPt, axisSigma, axisNsigmaTOF}}}); - - histos.add("TpcdEdx_ptwise_afterCut", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("ExpTpcdEdx_ptwise_afterCut", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("ExpSigma_ptwise_afterCut", "", {HistType::kTHnSparseD, {{axisPt, axisSigma, axisNsigmaTOF}}}); - } - } // TPC-TOF PID QA hists - if (cfgUseItsPID) { - histos.add("TofItsNsigma_before", "", {HistType::kTHnSparseD, {{axisNsigmaITS, axisNsigmaTOF, axisPt}}}); - histos.add("TofItsNsigma_after", "", {HistType::kTHnSparseD, {{axisNsigmaITS, axisNsigmaTOF, axisPt}}}); - } // ITS-TOF PID QA hists - } // end of PID QA hists + histos.add("deltaEta_deltaPhi_same", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEta}}); // check to see the delta eta and delta phi distribution + } + + if (doprocessSame) { + if (cfgGeneral.cfgQABasic) { + histos.add("Phi", "Phi", {HistType::kTH2D, {axisPhi, axisParticle}}); + histos.add("Eta", "Eta", {HistType::kTH2D, {axisEta, axisParticle}}); + histos.add("EtaCorrected", "EtaCorrected", {HistType::kTH2D, {axisEta, axisParticle}}); + histos.add("pTFiner", "pTFiner", {HistType::kTHnSparseF, {axisPt, axisParticle, axisEventClass}}); + histos.add("pTFinerCorrected", "pTFinerCorrected", {HistType::kTHnSparseF, {axisPt, axisParticle, axisEventClass}}); } - if (cfgPIDParticle == kK0 || cfgPIDParticle == kLambda || cfgPIDParticle == kPhi) { - histos.add("Trig_histReso", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger, massAxisReso}}}); + if (cfgPIDConfigs.cfgQANsigma) { + if (!cfgPIDConfigs.cfgPIDUseIts) { // TPC-TOF PID QA hists + histos.add("TofTpcNsigma_beforeCut", "", {HistType::kTHnSparseD, {{axisNsigmaTPC, axisNsigmaTOF, axisPtTrigger, axisParticle}}}); + histos.add("TofTpcNsigma_afterCut", "", {HistType::kTHnSparseD, {{axisNsigmaTPC, axisNsigmaTOF, axisPtTrigger, axisParticle}}}); + } + if (cfgPIDConfigs.cfgPIDUseIts) { // ITS-TOF PID QA hists + histos.add("TofItsNsigma_beforeCut", "", {HistType::kTHnSparseD, {{axisNsigmaITS, axisNsigmaTOF, axisPtTrigger, axisParticle}}}); + histos.add("TofItsNsigma_afterCut", "", {HistType::kTHnSparseD, {{axisNsigmaITS, axisNsigmaTOF, axisPtTrigger, axisParticle}}}); + } + } // end of PID nsigma QA hists + if (cfgPIDConfigs.cfgQAdEdx) { + histos.add("TpcdEdx_ptwise_beforeCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF, axisParticle}}}); + histos.add("ExpTpcdEdx_ptwise_beforeCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF, axisParticle}}}); + histos.add("ExpSigma_ptwise_beforeCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisSigma, axisNsigmaTOF, axisParticle}}}); + + histos.add("TpcdEdx_ptwise_afterCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF, axisParticle}}}); + histos.add("ExpTpcdEdx_ptwise_afterCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF, axisParticle}}}); + histos.add("ExpSigma_ptwise_afterCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisSigma, axisNsigmaTOF, axisParticle}}}); + } // end of PID dEdx QA hists + + // Trigger histogram + histos.add("Trig_hist", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisEventClass, axisPtTrigger, axisParticle}}}); + } + + if (doprocessSameMC) { + histos.add("Nch", "N_{ch}", {HistType::kTH1D, {axisMultiplicity}}); + histos.add("Centrality", hCentTitle.c_str(), {HistType::kTH1D, {axisCentrality}}); + histos.add("zVtx", "zVtx", {HistType::kTH1D, {axisVertex}}); + histos.add("deltaEta_deltaPhi_same", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEta}}); // check to see the delta eta and delta phi distribution + + if (cfgGeneral.cfgQABasic) { + histos.add("Phi", "Phi", {HistType::kTH2D, {axisPhi, axisParticle}}); + histos.add("Eta", "Eta", {HistType::kTH2D, {axisEta, axisParticle}}); + histos.add("pTFiner", "pTFiner", {HistType::kTHnSparseF, {axisPt, axisParticle, axisEventClass}}); } + histos.add("Trig_hist", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisEventClass, axisPtTrigger, axisParticle}}}); } - if (doprocessMixed || doprocessMixedReso) { + + if (doprocessSameReso) { + if (cfgPIDConfigs.cfgPIDParticle == UseK0 || cfgPIDConfigs.cfgPIDParticle == UseLambda) { + histos.add("Trig_histReso", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisEventClass, axisPtTrigger, axisInvMass}}}); + } + } + + if (doprocessMixed || doprocessMixedReso || doprocessMixedMC) { histos.add("deltaEta_deltaPhi_mixed", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEta}}); } - if (doprocessMC) { - histos.add("hNsigmaPionTruePositives", "hNsigmaPionTruePositives", {HistType::kTH1D, {axisPt}}); // Fraction of particles that are pions and selected as pions - histos.add("hNsigmaKaonTruePositives", "hNsigmaKaonTruePositives", {HistType::kTH1D, {axisPt}}); // Fraction of particles that are kaons and selected as kaons - histos.add("hNsigmaProtonTruePositives", "hNsigmaProtonTruePositives", {HistType::kTH1D, {axisPt}}); // Fraction of particles that are protons and selected as protons - histos.add("hNsigmaPionSelected", "hNsigmaPionSelected", {HistType::kTH1D, {axisPt}}); - histos.add("hNsigmaKaonSelected", "hNsigmaKaonSelected", {HistType::kTH1D, {axisPt}}); - histos.add("hNsigmaProtonSelected", "hNsigmaProtonSelected", {HistType::kTH1D, {axisPt}}); + if (doprocessMCPID) { + histos.add("hNsigmaPionTruePositives", "hNsigmaPionTruePositives", {HistType::kTH1D, {axisPtTrigger}}); // Fraction of particles that are pions and selected as pions + histos.add("hNsigmaKaonTruePositives", "hNsigmaKaonTruePositives", {HistType::kTH1D, {axisPtTrigger}}); // Fraction of particles that are kaons and selected as kaons + histos.add("hNsigmaProtonTruePositives", "hNsigmaProtonTruePositives", {HistType::kTH1D, {axisPtTrigger}}); // Fraction of particles that are protons and selected as protons + + histos.add("hNsigmaPionSelected", "hNsigmaPionSelected", {HistType::kTH1D, {axisPtTrigger}}); + histos.add("hNsigmaKaonSelected", "hNsigmaKaonSelected", {HistType::kTH1D, {axisPtTrigger}}); + histos.add("hNsigmaProtonSelected", "hNsigmaProtonSelected", {HistType::kTH1D, {axisPtTrigger}}); - histos.add("hNsigmaPionTrue", "hNsigmaPionTrue", {HistType::kTH1D, {axisPt}}); // All true pions from MC - histos.add("hNsigmaKaonTrue", "hNsigmaKaonTrue", {HistType::kTH1D, {axisPt}}); // All true kaons from MC - histos.add("hNsigmaProtonTrue", "hNsigmaProtonTrue", {HistType::kTH1D, {axisPt}}); // All true protons from MC + histos.add("hNsigmaPionTrue", "hNsigmaPionTrue", {HistType::kTH1D, {axisPtTrigger}}); // All true pions from MC + histos.add("hNsigmaKaonTrue", "hNsigmaKaonTrue", {HistType::kTH1D, {axisPtTrigger}}); // All true kaons from MC + histos.add("hNsigmaProtonTrue", "hNsigmaProtonTrue", {HistType::kTH1D, {axisPtTrigger}}); // All true protons from MC - histos.add("TpcdEdx_ptwise_pi", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("TpcdEdx_ptwise_ka", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("TpcdEdx_ptwise_pr", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); + histos.add("TpcdEdx_ptwise_pi", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); + histos.add("TpcdEdx_ptwise_ka", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); + histos.add("TpcdEdx_ptwise_pr", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("ExpTpcdEdx_ptwise_pi", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("ExpTpcdEdx_ptwise_ka", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("ExpTpcdEdx_ptwise_pr", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); + histos.add("ExpTpcdEdx_ptwise_pi", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); + histos.add("ExpTpcdEdx_ptwise_ka", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); + histos.add("ExpTpcdEdx_ptwise_pr", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("TpcdEdx_ptwise_pi_physprim", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("TpcdEdx_ptwise_ka_physprim", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("TpcdEdx_ptwise_pr_physprim", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); + histos.add("TpcdEdx_ptwise_pi_physprim", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); + histos.add("TpcdEdx_ptwise_ka_physprim", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); + histos.add("TpcdEdx_ptwise_pr_physprim", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("TpcdEdx_ptwise_all_physprim", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); + histos.add("TpcdEdx_ptwise_all_physprim", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); } histos.add("eventcount", "bin", {HistType::kTH1F, {{4, 0, 4, "bin"}}}); // histogram to see how many events are in the same and mixed event LOGF(info, "Initializing correlation container"); - std::vector corrAxis = {{axisSample, "Sample"}, + std::vector corrAxis = {{axisSample}, {axisVertex, "z-vtx (cm)"}, + {axisEventClass}, {axisPtTrigger, "p_{T} (GeV/c)"}, - {axisPtAssoc, "p_{T} (GeV/c)"}, + {axisParticle, "Particle, 0 = charged, 1 = pion, 2 = kaon, 3 = proton"}, {axisDeltaPhi, "#Delta#varphi (rad)"}, {axisDeltaEta, "#Delta#eta"}}; - std::vector corrAxisReso = {{axisSample, "Sample"}, + std::vector corrAxisReso = {{axisSample}, {axisVertex, "z-vtx (cm)"}, + {axisEventClass}, {axisPtTrigger, "p_{T} (GeV/c)"}, - {massAxisReso}, + {axisInvMass}, {axisDeltaPhi, "#Delta#varphi (rad)"}, {axisDeltaEta, "#Delta#eta"}}; - std::vector effAxis = { - {axisEtaEfficiency, "#eta"}, - {axisPtEfficiency, "p_{T} (GeV/c)"}, - {axisVertexEfficiency, "z-vtx (cm)"}, - }; - std::vector userAxis; - if (cfgPIDParticle == kCharged || cfgPIDParticle == kPions || cfgPIDParticle == kKaons || cfgPIDParticle == kProtons) { - same.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxis, effAxis, userAxis)); - mixed.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxis, effAxis, userAxis)); + if (!cfgPIDConfigs.cfgPIDUseReso) { + same.setObject(new DihadronContainer("sameEvent", "sameEvent", corrAxis)); + mixed.setObject(new DihadronContainer("mixedEvent", "mixedEvent", corrAxis)); } - if (cfgPIDParticle == kK0 || cfgPIDParticle == kLambda || cfgPIDParticle == kPhi) { - sameReso.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxisReso, effAxis, userAxis)); - mixedReso.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxisReso, effAxis, userAxis)); + if (cfgPIDConfigs.cfgPIDUseReso && (cfgPIDConfigs.cfgPIDParticle == UseK0 || cfgPIDConfigs.cfgPIDParticle == UseLambda)) { + sameReso.setObject(new DihadronContainer("sameEvent", "sameEvent", corrAxisReso)); + mixedReso.setObject(new DihadronContainer("mixedEvent", "mixedEvent", corrAxisReso)); } LOGF(info, "End of init"); } @@ -617,18 +703,18 @@ struct PidDiHadron { template float getCentrality(TCollision const& collision) { - float cent; - switch (cfgCentEstimator) { - case kCentFT0C: + float cent = 0.0; + switch (cfgEvSel.cfgCentEstimator) { + case UseCentFT0C: cent = collision.centFT0C(); break; - case kCentFT0CVariant1: + case UseCentFT0CVariant1: cent = collision.centFT0CVariant1(); break; - case kCentFT0M: + case UseCentFT0M: cent = collision.centFT0M(); break; - case kCentFV0A: + case UseCentFV0A: cent = collision.centFV0A(); break; default: @@ -638,37 +724,75 @@ struct PidDiHadron { } template - bool trackSelected(const TTrack& track) + bool trackSelected(TTrack const& track) { - if (cfgFuncParas.cfgDCAxyNSigma && (std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxy->Eval(track.pt()))) + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].empty() && std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxy->Eval(track.pt())) { return false; - return ((track.tpcNClsFound() >= cfgTpcCluster) && (track.tpcNClsCrossedRows() >= cfgTpcCrossRows) && (track.itsNCls() >= cfgITScluster)); + } + + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].empty()) { + if (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAz->Eval(track.pt())) { + return false; + } + } else { + if (std::fabs(track.dcaZ()) > trackCuts[TrkCutDCAz][UseGenTrkCuts]) { + return false; + } + } + + return ((track.pt() > trackCuts[TrkCutPtMin][UseGenTrkCuts]) && (track.pt() < trackCuts[TrkCutPtMax][UseGenTrkCuts]) && (std::abs(track.eta()) < trackCuts[TrkCutEtaCut][UseGenTrkCuts]) && (track.tpcChi2NCl() < trackCuts[TrkCutChi2PrTpcCls][UseGenTrkCuts]) && (track.tpcNClsFound() >= trackCuts[TrkCutTpcCluster][UseGenTrkCuts]) && (track.tpcNClsCrossedRows() >= trackCuts[TrkCutTpcCrossedRows][UseGenTrkCuts]) && (track.itsNCls() >= trackCuts[TrkCutItsCluster][UseGenTrkCuts])); + } + + template + bool trackSelectedForNch(TTrack const& track) + { + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].empty() && (std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxyForNch->Eval(track.pt()))) { + return false; + } + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].empty()) { + if (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAzForNch->Eval(track.pt())) { + return false; + } + } else { + if (std::fabs(track.dcaZ()) > trackCuts[TrkCutDCAz][UseNchSelCuts]) { + return false; + } + } + return ((track.pt() > trackCuts[TrkCutPtMin][UseNchSelCuts]) && (track.pt() < trackCuts[TrkCutPtMax][UseNchSelCuts]) && (std::abs(track.eta()) < trackCuts[TrkCutEtaCut][UseNchSelCuts]) && (track.tpcChi2NCl() < trackCuts[TrkCutChi2PrTpcCls][UseNchSelCuts]) && (track.tpcNClsFound() >= trackCuts[TrkCutTpcCluster][UseNchSelCuts]) && (track.tpcNClsCrossedRows() >= trackCuts[TrkCutTpcCrossedRows][UseNchSelCuts]) && (track.itsNCls() >= trackCuts[TrkCutItsCluster][UseNchSelCuts])); } template bool selectionV0Daughter(TTrack const& track, int pid) { - if (!(track.itsNCls() > cfgITScluster)) - return 0; - if (!track.hasTPC()) + if (!(track.itsNCls() >= trackCuts[TrkCutItsCluster][UseGenTrkCuts])) { return false; - if (track.tpcNClsFound() < cfgTpcCluster) + } + if (!track.hasTPC()) { return false; - if (!(track.tpcNClsCrossedRows() > cfgTpcCrossRows)) - return 0; + } + if (!(track.tpcNClsFound() >= trackCuts[TrkCutTpcCluster][UseGenTrkCuts])) { + return false; + } + if (!(track.tpcNClsCrossedRows() >= trackCuts[TrkCutTpcCrossedRows][UseGenTrkCuts])) { + return false; + } - if (cfgUseOnlyTPC) { - if (pid == kPions && std::abs(track.tpcNSigmaPi()) > cfgTpcCut) + if (cfgPIDConfigs.cfgPIDUseOnlyTpc) { + if (pid == UsePions && std::abs(track.tpcNSigmaPi()) > cfgPIDConfigs.cfgPIDOnlyTpcCut) { return false; - if (pid == kKaons && std::abs(track.tpcNSigmaKa()) > cfgTpcCut) + } + if (pid == UseKaons && std::abs(track.tpcNSigmaKa()) > cfgPIDConfigs.cfgPIDOnlyTpcCut) { return false; - if (pid == kProtons && std::abs(track.tpcNSigmaPr()) > cfgTpcCut) + } + if (pid == UseProtons && std::abs(track.tpcNSigmaPr()) > cfgPIDConfigs.cfgPIDOnlyTpcCut) { return false; + } } else { int partIndex = getNsigmaPID(track); int pidIndex = partIndex; // 1 = pion, 2 = kaon, 3 = proton - if (pidIndex != pid) + if (pidIndex != pid) { return false; + } } return true; @@ -677,27 +801,42 @@ struct PidDiHadron { template int getNsigmaPID(const TTrack& track) { + if (cfgPIDConfigs.cfgPIDUseNclsPID && track.tpcNClsPID() < cfgPIDConfigs.cfgPIDNclsPIDCut) { + return -1; + } + // Computing Nsigma arrays for pion, kaon, and protons std::array nSigmaTPC = {track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; std::array nSigmaTOF = {track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr()}; std::array nSigmaITS = {itsResponse.nSigmaITS(track), itsResponse.nSigmaITS(track), itsResponse.nSigmaITS(track)}; int pid = -1; // -1 = not identified, 1 = pion, 2 = kaon, 3 = proton - std::array nSigmaToUse = cfgUseItsPID ? nSigmaITS : nSigmaTPC; // Choose which nSigma to use: TPC or ITS - std::array detectorNsigmaCut = cfgUseItsPID ? itsNsigmaCut : tpcNsigmaCut; // Choose which nSigma to use: TPC or ITS - - bool isPion, isKaon, isProton; - bool isDetectedPion = nSigmaToUse[iPionUp] < detectorNsigmaCut[iPionUp] && nSigmaToUse[iPionUp] > detectorNsigmaCut[iPionLow]; - bool isDetectedKaon = nSigmaToUse[iKaonUp] < detectorNsigmaCut[iKaonUp] && nSigmaToUse[iKaonUp] > detectorNsigmaCut[iKaonLow]; - bool isDetectedProton = nSigmaToUse[iProtonUp] < detectorNsigmaCut[iProtonUp] && nSigmaToUse[iProtonUp] > detectorNsigmaCut[iProtonLow]; - - bool isTofPion = nSigmaTOF[iPionUp] < tofNsigmaCut[iPionUp] && nSigmaTOF[iPionUp] > tofNsigmaCut[iPionLow]; - bool isTofKaon = nSigmaTOF[iKaonUp] < tofNsigmaCut[iKaonUp] && nSigmaTOF[iKaonUp] > tofNsigmaCut[iKaonLow]; - bool isTofProton = nSigmaTOF[iProtonUp] < tofNsigmaCut[iProtonUp] && nSigmaTOF[iProtonUp] > tofNsigmaCut[iProtonLow]; - - if (track.pt() > cfgTofPtCut && !track.hasTOF()) { + std::array nSigmaToUse = cfgPIDConfigs.cfgPIDUseIts ? nSigmaITS : nSigmaTPC; // Choose which nSigma to use: TPC or ITS + int useIndexDetector = UseTPC; + useIndexDetector = cfgPIDConfigs.cfgPIDUseIts ? UseITS : UseTPC; // Choose which nSigma to use: TPC or ITS + + bool isPion = false; + bool isKaon = false; + bool isProton = false; + bool isDetectedPion = false; + bool isDetectedKaon = false; + bool isDetectedProton = false; + bool isTofPion = false; + bool isTofKaon = false; + bool isTofProton = false; + + isDetectedPion = nSigmaToUse[IndexPionUp] < nSigmaVals[IndexPionUp][useIndexDetector] && nSigmaToUse[IndexPionUp] > nSigmaVals[IndexPionLow][useIndexDetector]; + isDetectedKaon = nSigmaToUse[IndexKaonUp] < nSigmaVals[IndexKaonUp][useIndexDetector] && nSigmaToUse[IndexKaonUp] > nSigmaVals[IndexKaonLow][useIndexDetector]; + isDetectedProton = nSigmaToUse[IndexProtonUp] < nSigmaVals[IndexProtonUp][useIndexDetector] && nSigmaToUse[IndexProtonUp] > nSigmaVals[IndexProtonLow][useIndexDetector]; + + isTofPion = nSigmaTOF[IndexPionUp] < nSigmaVals[IndexPionUp][UseTOF] && nSigmaTOF[IndexPionUp] > nSigmaVals[IndexPionLow][UseTOF]; + isTofKaon = nSigmaTOF[IndexKaonUp] < nSigmaVals[IndexKaonUp][UseTOF] && nSigmaTOF[IndexKaonUp] > nSigmaVals[IndexKaonLow][UseTOF]; + isTofProton = nSigmaTOF[IndexProtonUp] < nSigmaVals[IndexProtonUp][UseTOF] && nSigmaTOF[IndexProtonUp] > nSigmaVals[IndexProtonLow][UseTOF]; + + if (track.pt() > cfgPIDConfigs.cfgPIDTofPtCut && !track.hasTOF()) { return -1; - } else if (track.pt() > cfgTofPtCut && track.hasTOF()) { + } + if (track.pt() > cfgPIDConfigs.cfgPIDTofPtCut && track.hasTOF()) { isPion = isTofPion && isDetectedPion; isKaon = isTofKaon && isDetectedKaon; isProton = isTofProton && isDetectedProton; @@ -707,16 +846,16 @@ struct PidDiHadron { isProton = isDetectedProton; } - if (cfgPIDUseRejection && ((isPion && isKaon) || (isPion && isProton) || (isKaon && isProton))) { + if (cfgPIDConfigs.cfgPIDUseRejection && ((isPion && isKaon) || (isPion && isProton) || (isKaon && isProton))) { return -1; // more than one particle satisfy the criteria } if (isPion) { - pid = kPions; + pid = UsePions; } else if (isKaon) { - pid = kKaons; + pid = UseKaons; } else if (isProton) { - pid = kProtons; + pid = UseProtons; } else { return -1; // no particle satisfies the criteria } @@ -729,29 +868,60 @@ struct PidDiHadron { if (correctionsLoaded) { return; } - if (cfgEfficiency.value.empty() == false) { - if (cfgLocalEfficiency > 0) { - TFile* fEfficiencyTrigger = TFile::Open(cfgEfficiency.value.c_str(), "READ"); - mEfficiency = reinterpret_cast(fEfficiencyTrigger->Get("ccdb_object")); + if (!cfgEffWeights.cfgEfficiency.value.empty()) { + if (cfgEffWeights.cfgLocalEfficiency) { + TFile* fEfficiencyTrigger = nullptr; + fEfficiencyTrigger = TFile::Open(cfgEffWeights.cfgEfficiency.value.c_str(), "READ"); + mEfficiency = dynamic_cast(fEfficiencyTrigger->Get("ccdb_object")); + } else { - mEfficiency = ccdb->getForTimeStamp(cfgEfficiency, timestamp); + mEfficiency = ccdb->getForTimeStamp(cfgEffWeights.cfgEfficiency, timestamp); } if (mEfficiency == nullptr) { - LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEfficiency.value.c_str()); + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEffWeights.cfgEfficiency.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEfficiency.value.c_str(), (void*)mEfficiency); + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEffWeights.cfgEfficiency.value.c_str(), static_cast(mEfficiency)); } + if (!cfgEffWeights.cfgEfficiencyNch.value.empty()) { + if (cfgEffWeights.cfgLocalEfficiencyNch) { + TFile* fEfficiencyTrigger = nullptr; + fEfficiencyTrigger = TFile::Open(cfgEffWeights.cfgEfficiencyNch.value.c_str(), "READ"); + mEfficiencyNch = dynamic_cast(fEfficiencyTrigger->Get("ccdb_object")); - if (cfgCentralityWeight.value.empty() == false) { - mCentralityWeight = ccdb->getForTimeStamp(cfgCentralityWeight, timestamp); + } else { + mEfficiencyNch = ccdb->getForTimeStamp(cfgEffWeights.cfgEfficiencyNch, timestamp); + } + if (!mEfficiencyNch) { + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEffWeights.cfgEfficiencyNch.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEffWeights.cfgEfficiencyNch.value.c_str(), static_cast(mEfficiencyNch)); + } + if (!cfgEffWeights.cfgCentralityWeight.value.empty()) { + mCentralityWeight = ccdb->getForTimeStamp(cfgEffWeights.cfgCentralityWeight, timestamp); if (mCentralityWeight == nullptr) { - LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgCentralityWeight.value.c_str()); + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEffWeights.cfgCentralityWeight.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgCentralityWeight.value.c_str(), (void*)mCentralityWeight); + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEffWeights.cfgCentralityWeight.value.c_str(), static_cast(mCentralityWeight)); } correctionsLoaded = true; } + bool getEfficiencyCorrectionNch(float& weightNch, float pt) + { + float effNch = 1.; + if (mEfficiencyNch) { + int ptBin = mEfficiencyNch->FindBin(pt); + effNch = mEfficiencyNch->GetBinContent(ptBin); + } else { + effNch = 1.0; + } + if (effNch == 0) { + return false; + } + weightNch = 1. / effNch; + return true; + } + bool getEfficiencyCorrection(float& weight_nue, float eta, float pt, float posZ) { float eff = 1.; @@ -763,93 +933,135 @@ struct PidDiHadron { } else { eff = 1.0; } - if (eff == 0) + if (eff == 0) { return false; + } weight_nue = 1. / eff; return true; } - bool getCentralityWeight(float& weightCent, const float centrality) + bool getCentralityWeight(float& weightCent, const double centrality) { float weight = 1.; - if (mCentralityWeight) + if (mCentralityWeight) { weight = mCentralityWeight->GetBinContent(mCentralityWeight->FindBin(centrality)); - else + } else { weight = 1.0; - if (weight == 0) + } + if (weight == 0) { return false; + } weightCent = weight; return true; } - // fill multiple histograms - template - void fillYield(const TCollision& collision, const TTracks& tracks) // function to fill the yield and etaphi histograms. + template + void nchCounter(const TTracks& tracks, double& multiplicity) // function to count the number of tracks in the event and fill the histogram { - float weff1 = 1; - float vtxz = collision.posZ(); - for (auto const& track1 : tracks) { - if (!trackSelected(track1)) + double nTracksCorrected = 0; + float weightNch = 1.0f; + for (auto const& track : tracks) { + if (!trackSelectedForNch(track)) { continue; - if (cfgPIDParticle && getNsigmaPID(track1) != cfgPIDParticle) - continue; // if PID is selected, check if the track has the right PID - if (!getEfficiencyCorrection(weff1, track1.eta(), track1.pt(), vtxz)) + } + + if (!getEfficiencyCorrectionNch(weightNch, track.pt())) { continue; - histos.fill(HIST("Phi"), RecoDecay::constrainAngle(track1.phi(), 0.0)); - histos.fill(HIST("Eta"), track1.eta()); - histos.fill(HIST("EtaCorrected"), track1.eta(), weff1); - histos.fill(HIST("pT"), track1.pt()); - histos.fill(HIST("pTCorrected"), track1.pt(), weff1); + } + nTracksCorrected += weightNch; } + multiplicity = nTracksCorrected; } template - void fillNsigmaAfterCut(const TTrack& track1, Int_t pid) // function to fill the QA after Nsigma selection + void fillTrackQA(TTrack const& track, int pid, double eventClass, Stage stage, float weff) // function to fill the QA after Nsigma selection { - switch (pid) { - case 1: // For Pions - if (!cfgUseItsPID) { - if (cfgGetdEdx) { - double tpcExpSignalPi = track1.tpcSignal() - (track1.tpcNSigmaPi() * track1.tpcExpSigmaPi()); - - histos.fill(HIST("TpcdEdx_ptwise_afterCut"), track1.pt(), track1.tpcSignal(), track1.tofNSigmaPi()); - histos.fill(HIST("ExpTpcdEdx_ptwise_afterCut"), track1.pt(), tpcExpSignalPi, track1.tofNSigmaPi()); - histos.fill(HIST("ExpSigma_ptwise_afterCut"), track1.pt(), track1.tpcExpSigmaPi(), track1.tofNSigmaPi()); + if (cfgGeneral.cfgQABasic) { + if (stage == Before && pid == UseCharged) { + histos.fill(HIST("Phi"), RecoDecay::constrainAngle(track.phi(), 0.0), UseCharged); + histos.fill(HIST("Eta"), track.eta(), UseCharged); + histos.fill(HIST("EtaCorrected"), track.eta(), UseCharged, weff); + histos.fill(HIST("pTFiner"), track.pt(), UseCharged, eventClass); + histos.fill(HIST("pTFinerCorrected"), track.pt(), UseCharged, eventClass, weff); + + } else if (stage == After && pid > UseCharged) { + histos.fill(HIST("Phi"), RecoDecay::constrainAngle(track.phi(), 0.0), pid); + histos.fill(HIST("Eta"), track.eta(), pid); + histos.fill(HIST("EtaCorrected"), track.eta(), pid, weff); + histos.fill(HIST("pTFiner"), track.pt(), pid, eventClass); + histos.fill(HIST("pTFinerCorrected"), track.pt(), pid, eventClass, weff); + } + } + + if (pid > UseCharged && (cfgPIDConfigs.cfgQANsigma || cfgPIDConfigs.cfgQAdEdx)) { + const bool useITS = cfgPIDConfigs.cfgPIDUseIts; + double tpcNSigma = 0.0; + double tpcExpSigma = 0.0; + double tofNSigma = 0.0; + double itsNSigma = 0.0; + switch (pid) { + case UsePions: // For Pions + tofNSigma = track.tofNSigmaPi(); + if (!useITS) { + tpcNSigma = track.tpcNSigmaPi(); + tpcExpSigma = track.tpcExpSigmaPi(); + } else { + itsNSigma = itsResponse.nSigmaITS(track); } - histos.fill(HIST("TofTpcNsigma_after"), track1.tpcNSigmaPi(), track1.tofNSigmaPi(), track1.pt()); - } - if (cfgUseItsPID) - histos.fill(HIST("TofItsNsigma_after"), itsResponse.nSigmaITS(track1), track1.tofNSigmaPi(), track1.pt()); - break; - case 2: // For Kaons - if (!cfgUseItsPID) { - if (cfgGetdEdx) { - double tpcExpSignalKa = track1.tpcSignal() - (track1.tpcNSigmaKa() * track1.tpcExpSigmaKa()); - - histos.fill(HIST("TpcdEdx_ptwise_afterCut"), track1.pt(), track1.tpcSignal(), track1.tofNSigmaKa()); - histos.fill(HIST("ExpTpcdEdx_ptwise_afterCut"), track1.pt(), tpcExpSignalKa, track1.tofNSigmaKa()); - histos.fill(HIST("ExpSigma_ptwise_afterCut"), track1.pt(), track1.tpcExpSigmaKa(), track1.tofNSigmaKa()); + break; + case UseKaons: // For Kaons + tofNSigma = track.tofNSigmaKa(); + if (!useITS) { + tpcNSigma = track.tpcNSigmaKa(); + tpcExpSigma = track.tpcExpSigmaKa(); + } else { + itsNSigma = itsResponse.nSigmaITS(track); } - histos.fill(HIST("TofTpcNsigma_after"), track1.tpcNSigmaKa(), track1.tofNSigmaKa(), track1.pt()); - } - if (cfgUseItsPID) - histos.fill(HIST("TofItsNsigma_after"), itsResponse.nSigmaITS(track1), track1.tofNSigmaKa(), track1.pt()); - break; - case 3: // For Protons - if (!cfgUseItsPID) { - if (cfgGetdEdx) { - double tpcExpSignalPr = track1.tpcSignal() - (track1.tpcNSigmaPr() * track1.tpcExpSigmaPr()); - - histos.fill(HIST("TpcdEdx_ptwise_afterCut"), track1.pt(), track1.tpcSignal(), track1.tofNSigmaPr()); - histos.fill(HIST("ExpTpcdEdx_ptwise_afterCut"), track1.pt(), tpcExpSignalPr, track1.tofNSigmaPr()); - histos.fill(HIST("ExpSigma_ptwise_afterCut"), track1.pt(), track1.tpcExpSigmaPr(), track1.tofNSigmaPr()); + break; + + case UseProtons: // For Protons + tofNSigma = track.tofNSigmaPr(); + if (!useITS) { + tpcNSigma = track.tpcNSigmaPr(); + tpcExpSigma = track.tpcExpSigmaPr(); + } else { + itsNSigma = itsResponse.nSigmaITS(track); + } + break; + default: + return; + } + + if (cfgPIDConfigs.cfgQANsigma) { + if (stage == Before) { + if (!useITS) { + histos.fill(HIST("TofTpcNsigma_beforeCut"), tpcNSigma, tofNSigma, track.pt(), pid); + } else { + histos.fill(HIST("TofItsNsigma_beforeCut"), itsNSigma, tofNSigma, track.pt(), pid); + } + } else if (stage == After) { + if (!useITS) { + histos.fill(HIST("TofTpcNsigma_afterCut"), tpcNSigma, tofNSigma, track.pt(), pid); + } else { + histos.fill(HIST("TofItsNsigma_afterCut"), itsNSigma, tofNSigma, track.pt(), pid); } - histos.fill(HIST("TofTpcNsigma_after"), track1.tpcNSigmaPr(), track1.tofNSigmaPr(), track1.pt()); } - if (cfgUseItsPID) - histos.fill(HIST("TofItsNsigma_after"), itsResponse.nSigmaITS(track1), track1.tofNSigmaPr(), track1.pt()); - break; - } // end of switch + } + + if (cfgPIDConfigs.cfgQAdEdx) { + double tpcExpSignal = track.tpcSignal() - (tpcNSigma * tpcExpSigma); + + if (stage == Before) { + histos.fill(HIST("TpcdEdx_ptwise_beforeCut"), track.pt(), track.tpcSignal(), tofNSigma, pid); + histos.fill(HIST("ExpTpcdEdx_ptwise_beforeCut"), track.pt(), tpcExpSignal, tofNSigma, pid); + histos.fill(HIST("ExpSigma_ptwise_beforeCut"), track.pt(), tpcExpSigma, tofNSigma, pid); + } else if (stage == After) { + histos.fill(HIST("TpcdEdx_ptwise_afterCut"), track.pt(), track.tpcSignal(), tofNSigma, pid); + histos.fill(HIST("ExpTpcdEdx_ptwise_afterCut"), track.pt(), tpcExpSignal, tofNSigma, pid); + histos.fill(HIST("ExpSigma_ptwise_afterCut"), track.pt(), tpcExpSigma, tofNSigma, pid); + } + } + } // if not a charged particle } float getDPhiStar(float charge1, float charge2, float phi1, float phi2, float pt1, float pt2, float radius, int magField) @@ -858,246 +1070,337 @@ struct PidDiHadron { float dPhiStar = phi1 - phi2 - charge1 * fbSign * std::asin(0.075 * radius / pt1) + charge2 * fbSign * std::asin(0.075 * radius / pt2); - if (dPhiStar > constants::math::PI) + if (dPhiStar > constants::math::PI) { dPhiStar = constants::math::TwoPI - dPhiStar; - if (dPhiStar < -constants::math::PI) + } + if (dPhiStar < -constants::math::PI) { dPhiStar = -constants::math::TwoPI - dPhiStar; + } return dPhiStar; } - template - void fillCorrelations(const TTracks& tracks1, const TTracksAssoc& tracks2, float posZ, int system, int magneticField, float cent, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + template + void fillCorrelations(const TTracks& triggers, const TTracksAssoc& assocs, float posZ, int system, int magneticField, double eventClass, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms { - // Cache efficiency for particles (too many FindBin lookups) - if (mEfficiency) { - efficiencyAssociatedCache.clear(); - efficiencyAssociatedCache.reserve(tracks2.size()); - for (const auto& track2 : tracks2) { - float weff = 1.; - getEfficiencyCorrection(weff, track2.eta(), track2.pt(), posZ); - efficiencyAssociatedCache.push_back(weff); + const int kNullIndex = -44; + efficiencyAssociatedCache.clear(); + efficiencyAssociatedCache.reserve(assocs.size()); + useAssociatedCache.clear(); + useAssociatedCache.reserve(assocs.size()); + + for (const auto& assc : assocs) { // loading efficiencies for associated particles and simultaneously applying track selection + if (trackSelected(assc)) { + useAssociatedCache.push_back(1); + if (mEfficiency) { + float weff = 1.; + getEfficiencyCorrection(weff, assc.eta(), assc.pt(), posZ); + efficiencyAssociatedCache.push_back(weff); + } + } else { + useAssociatedCache.push_back(0); + if (mEfficiency) { + efficiencyAssociatedCache.push_back(kNullIndex); + } } - } - - if (system == SameEvent) { - if (!cfgCentTableUnavailable) - histos.fill(HIST("Centrality_used"), cent); - histos.fill(HIST("Nch_used"), tracks1.size()); - } + } // end of efficieny requirement - int fSampleIndex = gRandom->Uniform(0, cfgSampleSize); + int fSampleIndex = static_cast(fRandom.Uniform(0.0, cfgGeneral.cfgSampleSize)); float triggerWeight = 1.0f; float associatedWeight = 1.0f; // loop over all tracks - for (auto const& track1 : tracks1) { + for (auto const& trig : triggers) { - if (!trackSelected(track1)) + if (!trackSelected(trig)) { continue; - - // Fill Nsigma QA - if (cfgGetNsigmaQA && !cfgUseItsPID) { - if (cfgPIDParticle == kPions) { - histos.fill(HIST("TofTpcNsigma_before"), track1.tpcNSigmaPi(), track1.tofNSigmaPi(), track1.pt()); - if (cfgGetdEdx) { - double tpcExpSignalPi = track1.tpcSignal() - (track1.tpcNSigmaPi() * track1.tpcExpSigmaPi()); - - histos.fill(HIST("TpcdEdx_ptwise"), track1.pt(), track1.tpcSignal(), track1.tofNSigmaPi()); - histos.fill(HIST("ExpTpcdEdx_ptwise"), track1.pt(), tpcExpSignalPi, track1.tofNSigmaPi()); - histos.fill(HIST("ExpSigma_ptwise"), track1.pt(), track1.tpcExpSigmaPi(), track1.tofNSigmaPi()); - } - } - if (cfgPIDParticle == kKaons) { - histos.fill(HIST("TofTpcNsigma_before"), track1.tpcNSigmaKa(), track1.tofNSigmaKa(), track1.pt()); - if (cfgGetdEdx) { - double tpcExpSignalKa = track1.tpcSignal() - (track1.tpcNSigmaKa() * track1.tpcExpSigmaKa()); - - histos.fill(HIST("TpcdEdx_ptwise"), track1.pt(), track1.tpcSignal(), track1.tofNSigmaKa()); - histos.fill(HIST("ExpTpcdEdx_ptwise"), track1.pt(), tpcExpSignalKa, track1.tofNSigmaKa()); - histos.fill(HIST("ExpSigma_ptwise"), track1.pt(), track1.tpcExpSigmaKa(), track1.tofNSigmaKa()); - } - } - if (cfgPIDParticle == kProtons) { - histos.fill(HIST("TofTpcNsigma_before"), track1.tpcNSigmaPr(), track1.tofNSigmaPr(), track1.pt()); - if (cfgGetdEdx) { - double tpcExpSignalPr = track1.tpcSignal() - (track1.tpcNSigmaPr() * track1.tpcExpSigmaPr()); - - histos.fill(HIST("TpcdEdx_ptwise"), track1.pt(), track1.tpcSignal(), track1.tofNSigmaPr()); - histos.fill(HIST("ExpTpcdEdx_ptwise"), track1.pt(), tpcExpSignalPr, track1.tofNSigmaPr()); - histos.fill(HIST("ExpSigma_ptwise"), track1.pt(), track1.tpcExpSigmaPr(), track1.tofNSigmaPr()); - } - } } - if (cfgGetNsigmaQA && cfgUseItsPID) { - if (cfgPIDParticle == kPions) - histos.fill(HIST("TofItsNsigma_before"), itsResponse.nSigmaITS(track1), track1.tofNSigmaPi(), track1.pt()); - if (cfgPIDParticle == kKaons) - histos.fill(HIST("TofItsNsigma_before"), itsResponse.nSigmaITS(track1), track1.tofNSigmaKa(), track1.pt()); - if (cfgPIDParticle == kProtons) - histos.fill(HIST("TofItsNsigma_before"), itsResponse.nSigmaITS(track1), track1.tofNSigmaPr(), track1.pt()); + + if (cfgEffWeights.cfgUseTrigWeight && !getEfficiencyCorrection(triggerWeight, trig.eta(), trig.pt(), posZ)) { + continue; } - if (cfgPIDParticle && getNsigmaPID(track1) != cfgPIDParticle) - continue; // if PID is selected, check if the track has the right PID + if (system == SameEvent) { + fillTrackQA(trig, UseCharged, eventClass, Before, triggerWeight); + fillTrackQA(trig, UsePions, eventClass, Before, triggerWeight); + fillTrackQA(trig, UseKaons, eventClass, Before, triggerWeight); + fillTrackQA(trig, UseProtons, eventClass, Before, triggerWeight); + } - if (cfgGetNsigmaQA) - fillNsigmaAfterCut(track1, cfgPIDParticle); + int pidIndex = getNsigmaPID(trig); - if (!getEfficiencyCorrection(triggerWeight, track1.eta(), track1.pt(), posZ)) - continue; if (system == SameEvent) { - histos.fill(HIST("Trig_hist"), fSampleIndex, posZ, track1.pt(), eventWeight * triggerWeight); + histos.fill(HIST("Trig_hist"), fSampleIndex, posZ, eventClass, trig.pt(), UseCharged, eventWeight * triggerWeight); + if (pidIndex > UseCharged) { + fillTrackQA(trig, pidIndex, eventClass, After, triggerWeight); + histos.fill(HIST("Trig_hist"), fSampleIndex, posZ, eventClass, trig.pt(), pidIndex, eventWeight * triggerWeight); + } } - for (auto const& track2 : tracks2) { + for (auto const& assc : assocs) { - if (!trackSelected(track2)) + if (useAssociatedCache[assc.filteredIndex()] == 0) { // rejecting associated tracks continue; - if (mEfficiency) { - associatedWeight = efficiencyAssociatedCache[track2.filteredIndex()]; } - if (!cfgUsePtOrder && track1.globalIndex() == track2.globalIndex()) + if (mEfficiency && efficiencyAssociatedCache[assc.filteredIndex()] != kNullIndex) { // loading associated efficiency + associatedWeight = efficiencyAssociatedCache[assc.filteredIndex()]; + } + + if (!cfgGeneral.cfgUsePtOrder && trig.globalIndex() == assc.globalIndex()) { continue; // For pt-differential correlations, skip if the trigger and associate are the same track - if (cfgUsePtOrder && system == SameEvent && track1.pt() <= track2.pt()) + } + if (cfgGeneral.cfgUsePtOrder && system == SameEvent && trig.pt() <= assc.pt()) { continue; // Without pt-differential correlations, skip if the trigger pt is less than the associate pt - if (cfgUsePtOrder && system == MixedEvent && cfgUsePtOrderInMixEvent && track1.pt() <= track2.pt()) + } + if (cfgGeneral.cfgUsePtOrder && system == MixedEvent && cfgGeneral.cfgUsePtOrderInMixEvent && trig.pt() <= assc.pt()) { continue; // For pt-differential correlations in mixed events, skip if the trigger pt is less than the associate pt + } - float deltaPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi(), -PIHalf); - float deltaEta = track1.eta() - track2.eta(); + float deltaPhi = RecoDecay::constrainAngle(trig.phi() - assc.phi(), -PIHalf); + float deltaEta = trig.eta() - assc.eta(); - if (std::abs(deltaEta) < cfgCutMerging) { + if (std::abs(deltaEta) < cfgGeneral.cfgCutMerging && cfgGeneral.cfgUseCutMerging) { - double dPhiStarHigh = getDPhiStar(track1.sign(), track2.sign(), track1.phi(), track2.phi(), track1.pt(), track2.pt(), cfgRadiusHigh, magneticField); - double dPhiStarLow = getDPhiStar(track1.sign(), track2.sign(), track1.phi(), track2.phi(), track1.pt(), track2.pt(), cfgRadiusLow, magneticField); + double dPhiStarHigh = getDPhiStar(trig.sign(), assc.sign(), trig.phi(), assc.phi(), trig.pt(), assc.pt(), cfgGeneral.cfgRadiusHigh, magneticField); + double dPhiStarLow = getDPhiStar(trig.sign(), assc.sign(), trig.phi(), assc.phi(), trig.pt(), assc.pt(), cfgGeneral.cfgRadiusLow, magneticField); - const double kLimit = 3.0 * cfgCutMerging; + const double kLimit = 3.0 * cfgGeneral.cfgCutMerging; bool bIsBelow = false; if (std::abs(dPhiStarLow) < kLimit || std::abs(dPhiStarHigh) < kLimit || dPhiStarLow * dPhiStarHigh < 0) { - for (double rad(cfgRadiusLow); rad < cfgRadiusHigh; rad += 0.01) { - double dPhiStar = getDPhiStar(track1.sign(), track2.sign(), track1.phi(), track2.phi(), track1.pt(), track2.pt(), rad, magneticField); + const double step = 0.01; + const double low = cfgGeneral.cfgRadiusLow; + const double high = cfgGeneral.cfgRadiusHigh; + const auto nSteps = static_cast(std::ceil((high - low) / step)); + + for (int i = 0; i < nSteps; ++i) { + const double rad = low + i * step; + double dPhiStar = getDPhiStar(trig.sign(), assc.sign(), trig.phi(), assc.phi(), trig.pt(), assc.pt(), rad, magneticField); if (std::abs(dPhiStar) < kLimit) { bIsBelow = true; break; } } - if (bIsBelow) + if (bIsBelow) { continue; + } } - } + } // End of merging cut condition // fill the right sparse and histograms if (system == SameEvent) { - - same->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track2.pt(), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + same->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, trig.pt(), UseCharged, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + if (pidIndex > UseCharged) { + same->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, trig.pt(), pidIndex, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + } histos.fill(HIST("deltaEta_deltaPhi_same"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); } else if (system == MixedEvent) { - - mixed->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track2.pt(), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + mixed->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, trig.pt(), UseCharged, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + if (pidIndex > UseCharged) { + mixed->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, trig.pt(), pidIndex, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + } histos.fill(HIST("deltaEta_deltaPhi_mixed"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); } } } } - template - void fillCorrelationsReso(const TV0Tracks& tracks1, const TTracksAssoc& tracks2, float posZ, float posY, float posX, int system, int magneticField, float cent, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + template + void fillCorrelationsReso(const TV0Tracks& triggers, const TTracksAssoc& assocs, float posZ, float posY, float posX, int system, int magneticField, double eventClass, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms { - // Cache efficiency for particles (too many FindBin lookups) - if (mEfficiency) { - efficiencyAssociatedCache.clear(); - efficiencyAssociatedCache.reserve(tracks2.size()); - for (const auto& track2 : tracks2) { - float weff = 1.; - getEfficiencyCorrection(weff, track2.eta(), track2.pt(), posZ); - efficiencyAssociatedCache.push_back(weff); + const int kNullIndex = -44; + efficiencyAssociatedCache.clear(); + efficiencyAssociatedCache.reserve(assocs.size()); + useAssociatedCache.clear(); + useAssociatedCache.reserve(assocs.size()); + + for (const auto& assc : assocs) { // loading efficiencies for associated particles and simultaneously applying track selection + if (trackSelected(assc)) { + useAssociatedCache.push_back(1); + if (mEfficiency) { + float weff = 1.; + getEfficiencyCorrection(weff, assc.eta(), assc.pt(), posZ); + efficiencyAssociatedCache.push_back(weff); + } + } else { + useAssociatedCache.push_back(0); + if (mEfficiency) { + efficiencyAssociatedCache.push_back(kNullIndex); + } } - } + } // end of efficieny requirement - if (system == SameEvent) { - if (!cfgCentTableUnavailable) - histos.fill(HIST("Centrality_used"), cent); - histos.fill(HIST("Nch_used"), tracks2.size()); // Taking Nch from tracks2 since tracks1 are V0s - } - - int fSampleIndex = gRandom->Uniform(0, cfgSampleSize); + int fSampleIndex = static_cast(fRandom.Uniform(0.0, cfgGeneral.cfgSampleSize)); float triggerWeight = 1.0f; float associatedWeight = 1.0f; // loop over all tracks - for (auto const& track1 : tracks1) { + for (auto const& trig : triggers) { double resoMass = -1; - // 4 = kshort, 5 = lambda, 6 = phi - if (cfgPIDParticle == kK0) { - if (!isSelectedK0(track1, posZ, posY, posX)) + // 4 = kshort, 5 = lambda + if (cfgPIDConfigs.cfgPIDParticle == UseK0) { + if (!isSelectedK0(trig, posZ, posY, posX, system)) { continue; // Reject if called for K0 but V0 is not K0 + } - resoMass = track1.mK0Short(); + resoMass = trig.mK0Short(); } - if (cfgPIDParticle == kLambda) { - if (!isSelectedLambda(track1, posZ, posY, posX)) + if (cfgPIDConfigs.cfgPIDParticle == UseLambda) { + if (!isSelectedLambda(trig, posZ, posY, posX, system)) { continue; // Reject if called for Lambda but V0 is not lambda - - resoMass = track1.mLambda(); + } + resoMass = trig.mLambda(); } if (system == SameEvent) { - histos.fill(HIST("Trig_histReso"), fSampleIndex, posZ, track1.pt(), resoMass, eventWeight * triggerWeight); + histos.fill(HIST("Trig_histReso"), fSampleIndex, posZ, eventClass, trig.pt(), resoMass, eventWeight * triggerWeight); } - for (auto const& track2 : tracks2) { + for (auto const& assc : assocs) { - if (!trackSelected(track2)) - continue; - if (track2.pt() < cfgCutPtMin || track2.pt() > cfgCutPtMax) // Select associated particles in the pt range 0.2 - 3.0 GeV/c - continue; - if (mEfficiency) { - associatedWeight = efficiencyAssociatedCache[track2.filteredIndex()]; + if (useAssociatedCache[assc.filteredIndex()] == 0) { + continue; // rejecting associated tracks } - if (cfgUsePtOrder && system == SameEvent && track1.pt() <= track2.pt()) + if (mEfficiency && efficiencyAssociatedCache[assc.filteredIndex()] != kNullIndex) { + associatedWeight = efficiencyAssociatedCache[assc.filteredIndex()]; // loading associated efficiency + } + + if (cfgGeneral.cfgUsePtOrder && system == SameEvent && trig.pt() <= assc.pt()) { continue; // Without pt-differential correlations, skip if the trigger pt is less than the associate pt - if (cfgUsePtOrder && system == MixedEvent && cfgUsePtOrderInMixEvent && track1.pt() <= track2.pt()) + } + if (cfgGeneral.cfgUsePtOrder && system == MixedEvent && cfgGeneral.cfgUsePtOrderInMixEvent && trig.pt() <= assc.pt()) { continue; // For pt-differential correlations in mixed events, skip if the trigger pt is less than the associate pt + } - float deltaPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi(), -PIHalf); - float deltaEta = track1.eta() - track2.eta(); + float deltaPhi = RecoDecay::constrainAngle(trig.phi() - assc.phi(), -PIHalf); + float deltaEta = trig.eta() - assc.eta(); - if (std::abs(deltaEta) < cfgCutMerging) { + if (std::abs(deltaEta) < cfgGeneral.cfgCutMerging && cfgGeneral.cfgUseCutMerging) { - double dPhiStarHigh = getDPhiStar(0, track2.sign(), track1.phi(), track2.phi(), track1.pt(), track2.pt(), cfgRadiusHigh, magneticField); - double dPhiStarLow = getDPhiStar(0, track2.sign(), track1.phi(), track2.phi(), track1.pt(), track2.pt(), cfgRadiusLow, magneticField); + double dPhiStarHigh = getDPhiStar(0, assc.sign(), trig.phi(), assc.phi(), trig.pt(), assc.pt(), cfgGeneral.cfgRadiusHigh, magneticField); + double dPhiStarLow = getDPhiStar(0, assc.sign(), trig.phi(), assc.phi(), trig.pt(), assc.pt(), cfgGeneral.cfgRadiusLow, magneticField); - const double kLimit = 3.0 * cfgCutMerging; + const double kLimit = 3.0 * cfgGeneral.cfgCutMerging; bool bIsBelow = false; if (std::abs(dPhiStarLow) < kLimit || std::abs(dPhiStarHigh) < kLimit || dPhiStarLow * dPhiStarHigh < 0) { - for (double rad(cfgRadiusLow); rad < cfgRadiusHigh; rad += 0.01) { - double dPhiStar = getDPhiStar(0, track2.sign(), track1.phi(), track2.phi(), track1.pt(), track2.pt(), rad, magneticField); + const double step = 0.01; + const double low = cfgGeneral.cfgRadiusLow; + const double high = cfgGeneral.cfgRadiusHigh; + const auto nSteps = static_cast(std::ceil((high - low) / step)); + + for (int i = 0; i < nSteps; ++i) { + const double rad = low + i * step; + double dPhiStar = getDPhiStar(0, assc.sign(), trig.phi(), assc.phi(), trig.pt(), assc.pt(), rad, magneticField); if (std::abs(dPhiStar) < kLimit) { bIsBelow = true; break; } } - if (bIsBelow) + if (bIsBelow) { continue; + } } } // fill the right sparse and histograms if (system == SameEvent) { - sameReso->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), resoMass, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + sameReso->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, trig.pt(), resoMass, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + histos.fill(HIST("deltaEta_deltaPhi_same"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + } else if (system == MixedEvent) { + mixedReso->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, trig.pt(), resoMass, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + histos.fill(HIST("deltaEta_deltaPhi_mixed"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + } + } + } + } + + template + void fillCorrelationsMC(const TTracks& triggers, const TTracksAssoc& assocs, float posZ, int system, double eventClass, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms for MC truth + { + int fSampleIndex = static_cast(fRandom.Uniform(0.0, cfgGeneral.cfgSampleSize)); + + float triggerWeight = 1.0f; + float associatedWeight = 1.0f; + + // loop over all tracks + for (auto const& trig : triggers) { + + if (cfgTrkSel.cfgMCTrigIsPhysPrim && !trig.isPhysicalPrimary()) { + continue; + } + + auto getPIDcode = [](int x) { + int pid = -1; + switch (x) { + case PDG_t::kPiPlus: + pid = UsePions; + break; + case PDG_t::kKPlus: + pid = UseKaons; + break; + case PDG_t::kProton: + pid = UseProtons; + break; + default: + return pid; + } // end of switch + return pid; + }; + + int pidIndex = getPIDcode(trig.pdgCode()); + + if (system == SameEvent) { + histos.fill(HIST("Phi"), RecoDecay::constrainAngle(trig.phi(), 0.0), UseCharged); + histos.fill(HIST("Eta"), trig.eta(), UseCharged); + histos.fill(HIST("pTFiner"), trig.pt(), UseCharged, eventClass); + histos.fill(HIST("Trig_hist"), fSampleIndex, posZ, eventClass, trig.pt(), UseCharged, eventWeight * triggerWeight); + if (pidIndex > UseCharged) { + histos.fill(HIST("Phi"), RecoDecay::constrainAngle(trig.phi(), 0.0), pidIndex); + histos.fill(HIST("Eta"), trig.eta(), pidIndex); + histos.fill(HIST("pTFiner"), trig.pt(), pidIndex, eventClass); + histos.fill(HIST("Trig_hist"), fSampleIndex, posZ, eventClass, trig.pt(), pidIndex, eventWeight * triggerWeight); + } + } + + for (auto const& assc : assocs) { + + if (cfgTrkSel.cfgMCAsscIsPhysPrim && !assc.isPhysicalPrimary()) { + continue; + } + + if (!cfgGeneral.cfgUsePtOrder && trig.globalIndex() == assc.globalIndex()) { + continue; // For pt-differential correlations, skip if the trigger and associate are the same track + } + if (cfgGeneral.cfgUsePtOrder && system == SameEvent && trig.pt() <= assc.pt()) { + continue; // Without pt-differential correlations, skip if the trigger pt is less than the associate pt + } + if (cfgGeneral.cfgUsePtOrder && system == MixedEvent && cfgGeneral.cfgUsePtOrderInMixEvent && trig.pt() <= assc.pt()) { + continue; // For pt-differential correlations in mixed events, skip if the trigger pt is less than the associate pt + } + + float deltaPhi = RecoDecay::constrainAngle(trig.phi() - assc.phi(), -PIHalf); + float deltaEta = trig.eta() - assc.eta(); + + // fill the right sparse and histograms + if (system == SameEvent) { + same->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, trig.pt(), UseCharged, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + if (pidIndex > UseCharged) { + same->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, trig.pt(), pidIndex, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + } histos.fill(HIST("deltaEta_deltaPhi_same"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); } else if (system == MixedEvent) { - mixedReso->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), resoMass, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + mixed->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, trig.pt(), UseCharged, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + if (pidIndex > UseCharged) { + mixed->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, trig.pt(), pidIndex, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + } histos.fill(HIST("deltaEta_deltaPhi_mixed"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); } } @@ -1105,144 +1408,165 @@ struct PidDiHadron { } template - bool selectionEvent(const TCollision& collision, const int mult, const float cent, const bool fillCounter) + bool selectionEvent(TCollision const& collision, const int mult, const double cent, const bool fillCounter) { - if (fillCounter) - histos.fill(HIST("hEventCount"), kFilteredEvents); + if (fillCounter) { + histos.fill(HIST("hEventCount"), FilteredEvents); + } if (!collision.sel8()) { - return 0; + return false; + } + if (fillCounter) { + histos.fill(HIST("hEventCount"), AfterSel8); } - if (fillCounter) - histos.fill(HIST("hEventCount"), kAfterSel8); - if (eventCuts[kUseNoTimeFrameBorder][kEvCut1] && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { - return 0; + if (eventCuts[UseNoTimeFrameBorder][EvCut1] && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + if (fillCounter && eventCuts[UseNoTimeFrameBorder][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoTimeFrameBorder); } - if (fillCounter && eventCuts[kUseNoTimeFrameBorder][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseNoTimeFrameBorder); - if (eventCuts[kUseNoITSROFrameBorder][kEvCut1] && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { - return 0; + if (eventCuts[UseNoITSROFrameBorder][EvCut1] && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + if (fillCounter && eventCuts[UseNoITSROFrameBorder][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoITSROFrameBorder); } - if (fillCounter && eventCuts[kUseNoITSROFrameBorder][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseNoITSROFrameBorder); - if (eventCuts[kUseNoSameBunchPileup][kEvCut1] && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + if (eventCuts[UseNoSameBunchPileup][EvCut1] && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { // rejects collisions which are associated with the same "found-by-T0" bunch crossing // https://indico.cern.ch/event/1396220/#1-event-selection-with-its-rof - return 0; + return false; + } + if (fillCounter && eventCuts[UseNoSameBunchPileup][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoSameBunchPileup); } - if (fillCounter && eventCuts[kUseNoSameBunchPileup][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseNoSameBunchPileup); - if (eventCuts[kUseGoodZvtxFT0vsPV][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + if (eventCuts[UseGoodZvtxFT0vsPV][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference // use this cut at low multiplicities with caution - return 0; + return false; + } + if (fillCounter && eventCuts[UseGoodZvtxFT0vsPV][EvCut1]) { + histos.fill(HIST("hEventCount"), UseGoodZvtxFT0vsPV); } - if (fillCounter && eventCuts[kUseGoodZvtxFT0vsPV][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseGoodZvtxFT0vsPV); - if (eventCuts[kUseNoCollInTimeRangeStandard][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + if (eventCuts[UseNoCollInTimeRangeStandard][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { // no collisions in specified time range - return 0; + return false; + } + + if (fillCounter && eventCuts[UseNoCollInTimeRangeStandard][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoCollInTimeRangeStandard); } - if (fillCounter && eventCuts[kUseNoCollInTimeRangeStandard][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseNoCollInTimeRangeStandard); - if (eventCuts[kUseGoodITSLayersAll][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + if (eventCuts[UseGoodITSLayersAll][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { // from Jan 9 2025 AOT meeting // cut time intervals with dead ITS staves - return 0; + return false; + } + + if (fillCounter && eventCuts[UseGoodITSLayersAll][EvCut1]) { + histos.fill(HIST("hEventCount"), UseGoodITSLayersAll); + } + + if (eventCuts[UseGoodITSLayer0123][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { + return false; + } + if (fillCounter && eventCuts[UseGoodITSLayer0123][EvCut1]) { + histos.fill(HIST("hEventCount"), UseGoodITSLayer0123); } - if (fillCounter && eventCuts[kUseGoodITSLayersAll][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseGoodITSLayersAll); - if (eventCuts[kUseNoCollInRofStandard][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + if (eventCuts[UseNoCollInRofStandard][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { // no other collisions in this Readout Frame with per-collision multiplicity above threshold - return 0; + return false; } - if (fillCounter && eventCuts[kUseNoCollInRofStandard][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseNoCollInRofStandard); - if (eventCuts[kUseNoHighMultCollInPrevRof][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + if (fillCounter && eventCuts[UseNoCollInRofStandard][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoCollInRofStandard); + } + + if (eventCuts[UseNoHighMultCollInPrevRof][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { // veto an event if FT0C amplitude in previous ITS ROF is above threshold - return 0; + return false; + } + if (fillCounter && eventCuts[UseNoHighMultCollInPrevRof][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoHighMultCollInPrevRof); } - if (fillCounter && eventCuts[kUseNoHighMultCollInPrevRof][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseNoHighMultCollInPrevRof); auto multNTracksPV = collision.multNTracksPV(); auto occupancy = collision.trackOccupancyInTimeRange(); - if (eventCuts[kUseOccupancy][kEvCut1] && (occupancy < cfgCutOccupancyMin || occupancy > cfgCutOccupancyMax)) { - return 0; + if (eventCuts[UseOccupancy][EvCut1] && (occupancy < cfgEvSel.cfgCutOccupancyMin || occupancy > cfgEvSel.cfgCutOccupancyMax)) { + return false; + } + if (fillCounter && eventCuts[UseOccupancy][EvCut1]) { + histos.fill(HIST("hEventCount"), UseOccupancy); } - if (fillCounter && eventCuts[kUseOccupancy][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseOccupancy); - if (eventCuts[kUseMultCorrCut][kEvCut1]) { - if (cfgFuncParas.cfgMultPVT0CCutEnabled && !cfgCentTableUnavailable) { - if (multNTracksPV < cfgFuncParas.fMultPVT0CCutLow->Eval(cent)) - return 0; - if (multNTracksPV > cfgFuncParas.fMultPVT0CCutHigh->Eval(cent)) - return 0; + if (eventCuts[UseMultCorrCut][EvCut1]) { + if (cfgFuncParas.cfgMultPVT0CCutEnabled) { + if (multNTracksPV < cfgFuncParas.fMultPVT0CCutLow->Eval(cent)) { + return false; + } + if (multNTracksPV > cfgFuncParas.fMultPVT0CCutHigh->Eval(cent)) { + return false; + } } - if (cfgFuncParas.cfgMultT0CCutEnabled && !cfgCentTableUnavailable) { - if (mult < cfgFuncParas.fMultT0CCutLow->Eval(cent)) - return 0; - if (mult > cfgFuncParas.fMultT0CCutHigh->Eval(cent)) - return 0; + if (cfgFuncParas.cfgMultT0CCutEnabled) { + if (mult < cfgFuncParas.fMultT0CCutLow->Eval(cent)) { + return false; + } + if (mult > cfgFuncParas.fMultT0CCutHigh->Eval(cent)) { + return false; + } } if (cfgFuncParas.cfgMultGlobalPVCutEnabled) { - if (mult < cfgFuncParas.fMultGlobalPVCutLow->Eval(multNTracksPV)) - return 0; - if (mult > cfgFuncParas.fMultGlobalPVCutHigh->Eval(multNTracksPV)) - return 0; + if (mult < cfgFuncParas.fMultGlobalPVCutLow->Eval(multNTracksPV)) { + return false; + } + if (mult > cfgFuncParas.fMultGlobalPVCutHigh->Eval(multNTracksPV)) { + return false; + } } if (cfgFuncParas.cfgMultMultV0ACutEnabled) { - if (collision.multFV0A() < cfgFuncParas.fMultMultV0ACutLow->Eval(mult)) - return 0; - if (collision.multFV0A() > cfgFuncParas.fMultMultV0ACutHigh->Eval(mult)) - return 0; + if (collision.multFV0A() < cfgFuncParas.fMultMultV0ACutLow->Eval(mult)) { + return false; + } + if (collision.multFV0A() > cfgFuncParas.fMultMultV0ACutHigh->Eval(mult)) { + return false; + } } } - if (fillCounter && eventCuts[kUseMultCorrCut][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseMultCorrCut); + if (fillCounter && eventCuts[UseMultCorrCut][EvCut1]) { + histos.fill(HIST("hEventCount"), UseMultCorrCut); + } // V0A T0A 5 sigma cut - if (eventCuts[kUseT0AV0ACut][kEvCut1] && (std::fabs(collision.multFV0A() - cfgFuncParas.fT0AV0AMean->Eval(collision.multFT0A())) > cfgV0AT0Acut * cfgFuncParas.fT0AV0ASigma->Eval(collision.multFT0A()))) - return 0; - if (fillCounter && eventCuts[kUseT0AV0ACut][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseT0AV0ACut); - - if (eventCuts[kUseVertexITSTPC][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) - return 0; - if (fillCounter && eventCuts[kUseVertexITSTPC][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseVertexITSTPC); - - if (eventCuts[kUseTVXinTRD][kEvCut1] && collision.alias_bit(kTVXinTRD)) { - return 0; + if (eventCuts[UseT0AV0ACut][EvCut1] && (std::fabs(collision.multFV0A() - cfgFuncParas.fT0AV0AMean->Eval(collision.multFT0A())) > cfgFuncParas.cfgV0AT0Acut * cfgFuncParas.fT0AV0ASigma->Eval(collision.multFT0A()))) { + return false; + } + if (fillCounter && eventCuts[UseT0AV0ACut][EvCut1]) { + histos.fill(HIST("hEventCount"), UseT0AV0ACut); } - if (fillCounter && eventCuts[kUseTVXinTRD][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseTVXinTRD); - return 1; + return true; } void processSame(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks, aod::BCsWithTimestamps const&) { auto bc = collision.bc_as(); - float cent = -1.; float weightCent = 1.0f; - if (!cfgCentTableUnavailable) - cent = getCentrality(collision); - if (cfgUseAdditionalEventCut && !selectionEvent(collision, tracks.size(), cent, true)) + float cent = cfgEvSel.cfgCentTableUnavailable ? -1.f : getCentrality(collision); + + if (cfgEvSel.cfgUseAdditionalEventCut && !selectionEvent(collision, tracks.size(), cent, true)) { return; + } loadCorrection(bc.timestamp()); - if (!cfgCentTableUnavailable) { + if (!cfgEvSel.cfgCentTableUnavailable) { getCentralityWeight(weightCent, cent); histos.fill(HIST("Centrality"), cent); histos.fill(HIST("CentralityWeighted"), cent, weightCent); @@ -1250,24 +1574,32 @@ struct PidDiHadron { histos.fill(HIST("Nch"), tracks.size()); histos.fill(HIST("zVtx"), collision.posZ()); - if (cfgSelCollByNch && (tracks.size() < cfgCutMultMin || tracks.size() >= cfgCutMultMax)) { + auto multiplicity = static_cast(tracks.size()); + + if (cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks, multiplicity); + } + histos.fill(HIST("Nch_corrected"), multiplicity); + + if (cfgEvSel.cfgSelCollByNch && (multiplicity < cfgEvSel.cfgCutMultMin || multiplicity >= cfgEvSel.cfgCutMultMax)) { return; } - if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent < cfgCutCentMin || cent >= cfgCutCentMax)) { + if (!cfgEvSel.cfgSelCollByNch && !cfgEvSel.cfgCentTableUnavailable && (cent < cfgEvSel.cfgCutCentMin || cent >= cfgEvSel.cfgCutCentMax)) { return; } histos.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin - fillYield(collision, tracks); - same->fillEvent(tracks.size(), CorrelationContainer::kCFStepReconstructed); - fillCorrelations(tracks, tracks, collision.posZ(), SameEvent, getMagneticField(bc.timestamp()), cent, weightCent); + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity : cent; + + auto assocs = asscTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + + fillCorrelations(tracks, assocs, collision.posZ(), SameEvent, getMagneticField(bc.timestamp()), eventClass, weightCent); } PROCESS_SWITCH(PidDiHadron, processSame, "Process same event", true); // the process for filling the mixed events void processMixed(FilteredCollisions const& collisions, FilteredTracks const& tracks, aod::BCsWithTimestamps const&) { - auto getTracksSize = [&tracks, this](FilteredCollisions::iterator const& collision) { auto associatedTracks = tracks.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); auto mult = associatedTracks.size(); @@ -1276,56 +1608,74 @@ struct PidDiHadron { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); - Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto it = pairs.begin(); it != pairs.end(); it++) { auto& [collision1, tracks1, collision2, tracks2] = *it; - if (!collision1.sel8() || !collision2.sel8()) + if (!collision1.sel8() || !collision2.sel8()) { continue; + } - if (cfgSelCollByNch && (tracks1.size() < cfgCutMultMin || tracks1.size() >= cfgCutMultMax)) - continue; + float cent1 = cfgEvSel.cfgCentTableUnavailable ? -1.f : getCentrality(collision1); + float cent2 = cfgEvSel.cfgCentTableUnavailable ? -1.f : getCentrality(collision2); - if (cfgSelCollByNch && (tracks2.size() < cfgCutMultMin || tracks2.size() >= cfgCutMultMax)) + if (cfgEvSel.cfgUseAdditionalEventCut && !selectionEvent(collision1, tracks1.size(), cent1, false)) { continue; - - float cent1 = -1; - float cent2 = -1; - if (!cfgCentTableUnavailable) { - cent1 = getCentrality(collision1); - cent2 = getCentrality(collision2); } - if (cfgUseAdditionalEventCut && !selectionEvent(collision1, tracks1.size(), cent1, false)) + if (cfgEvSel.cfgUseAdditionalEventCut && !selectionEvent(collision2, tracks2.size(), cent2, false)) { continue; - if (cfgUseAdditionalEventCut && !selectionEvent(collision2, tracks2.size(), cent2, false)) + } + + if (!cfgEvSel.cfgSelCollByNch && !cfgEvSel.cfgCentTableUnavailable && (cent1 < cfgEvSel.cfgCutCentMin || cent1 >= cfgEvSel.cfgCutCentMax)) { continue; + } - if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent1 < cfgCutCentMin || cent1 >= cfgCutCentMax)) + if (!cfgEvSel.cfgSelCollByNch && !cfgEvSel.cfgCentTableUnavailable && (cent2 < cfgEvSel.cfgCutCentMin || cent2 >= cfgEvSel.cfgCutCentMax)) { continue; + } + + auto multiplicity1 = static_cast(tracks1.size()); + auto multiplicity2 = static_cast(tracks2.size()); - if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent2 < cfgCutCentMin || cent2 >= cfgCutCentMax)) + if (cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks1, multiplicity1); + if (cfgEvSel.cfgUseCol2ForMixedEvSel) { + nchCounter(tracks2, multiplicity2); + } + } + + if (cfgEvSel.cfgSelCollByNch && (multiplicity1 < cfgEvSel.cfgCutMultMin || multiplicity1 >= cfgEvSel.cfgCutMultMax)) { continue; + } + + if (cfgEvSel.cfgSelCollByNch && (multiplicity2 < cfgEvSel.cfgCutMultMin || multiplicity2 >= cfgEvSel.cfgCutMultMax)) { + continue; + } - histos.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin auto bc = collision1.bc_as(); loadCorrection(bc.timestamp()); float eventWeight = 1.0f; - if (cfgUseEventWeights) { + if (cfgEffWeights.cfgUseEventWeights) { eventWeight = 1.0f / it.currentWindowNeighbours(); } + + histos.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity1 : cent1; float weightCent = 1.0f; - if (!cfgCentTableUnavailable) + if (!cfgEvSel.cfgCentTableUnavailable) { getCentralityWeight(weightCent, cent1); + } - fillCorrelations(tracks1, tracks2, collision1.posZ(), MixedEvent, getMagneticField(bc.timestamp()), cent1, eventWeight * weightCent); + auto assocs = asscTracks->sliceByCached(aod::track::collisionId, collision2.globalIndex(), cache); + fillCorrelations(tracks1, assocs, collision1.posZ(), MixedEvent, getMagneticField(bc.timestamp()), eventClass, eventWeight * weightCent); } } PROCESS_SWITCH(PidDiHadron, processMixed, "Process mixed events", true); template - bool isSelectedK0(V0 const& candidate, float posZ, float posY, float posX) + bool isSelectedK0(V0 const& candidate, float posZ, float posY, float posX, int system) { double mk0 = candidate.mK0Short(); @@ -1333,58 +1683,103 @@ struct PidDiHadron { auto postrack = candidate.template posTrack_as(); auto negtrack = candidate.template negTrack_as(); - histos.fill(HIST("hK0Count"), 0.5); - if (postrack.pt() < resoCutVals[kPosTrackPt][iK0] || negtrack.pt() < resoCutVals[kNegTrackPt][iK0]) + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillCandidate); + } + if (postrack.pt() < resoCutVals[CutPosTrackPt][IndexK0] || negtrack.pt() < resoCutVals[CutNegTrackPt][IndexK0]) { return false; - histos.fill(HIST("hK0Count"), 1.5); - if (mk0 < resoCutVals[kMassMin][iK0] && mk0 > resoCutVals[kMassMax][iK0]) + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillDaughterPt); + } + if (mk0 < resoCutVals[CutMassMin][IndexK0] || mk0 > resoCutVals[CutMassMax][IndexK0]) { return false; - histos.fill(HIST("hK0Count"), 2.5); + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillMassCut); + } // Rapidity correction - if (candidate.yK0Short() > resoCutVals[kRapidity][iK0]) + if (candidate.yK0Short() > resoCutVals[CutRapidityMax][IndexK0] || candidate.yK0Short() < resoCutVals[CutRapidityMin][IndexK0]) { return false; - histos.fill(HIST("hK0Count"), 3.5); + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillRapidityCut); + } // DCA cuts for K0short - if (std::abs(candidate.dcapostopv()) < resoCutVals[kDCAPosToPVMin][iK0] || std::abs(candidate.dcanegtopv()) < resoCutVals[kDCANegToPVMin][iK0]) + if (std::abs(candidate.dcapostopv()) < resoCutVals[CutDCAPosToPVMin][IndexK0] || std::abs(candidate.dcanegtopv()) < resoCutVals[CutDCANegToPVMin][IndexK0]) { return false; - histos.fill(HIST("hK0Count"), 4.5); - if (std::abs(candidate.dcaV0daughters()) > resoSwitchVals[kDCABetDaug][iK0]) + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillDCAtoPV); + } + if (resoSwitchVals[UseDCABetDaug][IndexK0] && std::abs(candidate.dcaV0daughters()) > resoCutVals[CutDCABetDaug][IndexK0]) { return false; - histos.fill(HIST("hK0Count"), 5.5); + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillDCAxDaughters); + } // v0 radius cuts - if (resoSwitchVals[kUseV0Radius][iK0] && (candidate.v0radius() < resoCutVals[kRadiusMin][iK0] || candidate.v0radius() > resoCutVals[kRadiusMax][iK0])) + if (resoSwitchVals[UseV0Radius][IndexK0] && (candidate.v0radius() < resoCutVals[CutRadiusMin][IndexK0] || candidate.v0radius() > resoCutVals[CutRadiusMax][IndexK0])) { return false; - histos.fill(HIST("hK0Count"), 6.5); + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillV0Radius); + } // cosine pointing angle cuts - if (candidate.v0cosPA() < resoCutVals[kCosPA][iK0]) + if (resoSwitchVals[UseCosPA][IndexK0] && candidate.v0cosPA() < resoCutVals[CutCosPA][IndexK0]) { return false; - histos.fill(HIST("hK0Count"), 7.5); + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillCosPA); + } // Proper lifetime float cTauK0 = candidate.distovertotmom(posX, posY, posZ) * massK0Short; - if (resoSwitchVals[kUseProperLifetime][iK0] && std::abs(cTauK0) > resoCutVals[kLifeTime][iK0]) + if (resoSwitchVals[UseProperLifetime][IndexK0] && cTauK0 > resoCutVals[CutLifeTime][IndexK0]) { return false; - histos.fill(HIST("hK0Count"), 8.5); + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillProperLifetime); + } // ArmenterosPodolanskiCut - if (resoSwitchVals[kUseArmPodCut][iK0] && (candidate.qtarm() / std::abs(candidate.alpha())) < resoCutVals[kArmPodMinVal][iK0]) + if (resoSwitchVals[UseArmPodCut][IndexK0] && (candidate.qtarm() / std::abs(candidate.alpha())) < resoCutVals[CutArmPodMinVal][IndexK0]) { return false; - histos.fill(HIST("hK0Count"), 9.5); + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillArmPodCut); + } + + if (resoSwitchVals[UseCompMassRej][IndexK0]) { + if (std::abs(candidate.mLambda() - o2::constants::physics::MassLambda) < resoCutVals[CutCompMassRej][IndexK0]) { + return false; + } + if (std::abs(candidate.mAntiLambda() - o2::constants::physics::MassLambda) < resoCutVals[CutCompMassRej][IndexK0]) { + return false; + } + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillCompetingMass); + } + // Selection on V0 daughters - if (!selectionV0Daughter(postrack, kPions) || !selectionV0Daughter(negtrack, kPions)) + if (!selectionV0Daughter(postrack, UsePions) || !selectionV0Daughter(negtrack, UsePions)) { return false; - histos.fill(HIST("hK0Count"), 10.5); - - histos.fill(HIST("hK0Phi"), candidate.phi()); - histos.fill(HIST("hK0Eta"), candidate.eta()); - histos.fill(HIST("PiPlusTPC_K0"), postrack.pt(), postrack.tpcNSigmaPi()); - histos.fill(HIST("PiPlusTOF_K0"), postrack.pt(), postrack.tofNSigmaPi()); - histos.fill(HIST("PiMinusTPC_K0"), negtrack.pt(), negtrack.tpcNSigmaPi()); - histos.fill(HIST("PiMinusTOF_K0"), negtrack.pt(), negtrack.tofNSigmaPi()); + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillDaughterTrackSelected); + histos.fill(HIST("hK0Phi"), candidate.phi()); + histos.fill(HIST("hK0Rap"), candidate.yK0Short()); + histos.fill(HIST("hK0Eta"), candidate.eta()); + histos.fill(HIST("PiPlusTPC_K0"), postrack.pt(), postrack.tpcNSigmaPi()); + histos.fill(HIST("PiPlusTOF_K0"), postrack.pt(), postrack.tofNSigmaPi()); + histos.fill(HIST("PiMinusTPC_K0"), negtrack.pt(), negtrack.tpcNSigmaPi()); + histos.fill(HIST("PiMinusTOF_K0"), negtrack.pt(), negtrack.tofNSigmaPi()); + } return true; } template - bool isSelectedLambda(V0 const& candidate, float posZ, float posY, float posX) + bool isSelectedLambda(V0 const& candidate, float posZ, float posY, float posX, int system) { bool isL = false; // Is lambda candidate bool isAL = false; // Is anti-lambda candidate @@ -1396,71 +1791,127 @@ struct PidDiHadron { auto postrack = candidate.template posTrack_as(); auto negtrack = candidate.template negTrack_as(); - histos.fill(HIST("hLambdaCount"), 0.5); - if (postrack.pt() < resoCutVals[kPosTrackPt][iLambda] || negtrack.pt() < resoCutVals[kNegTrackPt][iLambda]) + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillCandidate); + } + if (postrack.pt() < resoCutVals[CutPosTrackPt][IndexLambda] || negtrack.pt() < resoCutVals[CutNegTrackPt][IndexLambda]) { return false; - - histos.fill(HIST("hLambdaCount"), 1.5); - if (mlambda > resoCutVals[kMassMin][iLambda] && mlambda < resoCutVals[kMassMax][iLambda]) + } + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillDaughterPt); + } + if (mlambda > resoCutVals[CutMassMin][IndexLambda] && mlambda < resoCutVals[CutMassMax][IndexLambda]) { isL = true; - if (mantilambda > resoCutVals[kMassMin][iLambda] && mantilambda < resoCutVals[kMassMax][iLambda]) + } + if (mantilambda > resoCutVals[CutMassMin][IndexLambda] && mantilambda < resoCutVals[CutMassMax][IndexLambda]) { isAL = true; + } if (!isL && !isAL) { return false; } - histos.fill(HIST("hLambdaCount"), 2.5); + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillMassCut); + } // Rapidity correction - if (candidate.yLambda() > resoCutVals[kRapidity][iLambda]) + if (candidate.yLambda() > resoCutVals[CutRapidityMax][IndexLambda] || candidate.yLambda() < resoCutVals[CutRapidityMin][IndexLambda]) { return false; - histos.fill(HIST("hLambdaCount"), 3.5); + } + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillRapidityCut); + } + // DCA cuts for lambda and antilambda - if (isL) { - if (std::abs(candidate.dcapostopv()) < resoCutVals[kDCAPosToPVMin][iLambda] || std::abs(candidate.dcanegtopv()) < resoCutVals[kDCANegToPVMin][iLambda]) - return false; + if (isL && (std::abs(candidate.dcapostopv()) < resoCutVals[CutDCAPosToPVMin][IndexLambda] || std::abs(candidate.dcanegtopv()) < resoCutVals[CutDCANegToPVMin][IndexLambda])) { + isL = false; } - if (isAL) { - if (std::abs(candidate.dcapostopv()) < resoCutVals[kDCANegToPVMin][iLambda] || std::abs(candidate.dcanegtopv()) < resoCutVals[kDCAPosToPVMin][iLambda]) - return false; + if (isAL && (std::abs(candidate.dcapostopv()) < resoCutVals[CutDCANegToPVMin][IndexLambda] || std::abs(candidate.dcanegtopv()) < resoCutVals[CutDCAPosToPVMin][IndexLambda])) { + isAL = false; } - histos.fill(HIST("hLambdaCount"), 4.5); - if (std::abs(candidate.dcaV0daughters()) > resoSwitchVals[kDCABetDaug][iLambda]) + if (!isL && !isAL) { return false; - histos.fill(HIST("hLambdaCount"), 5.5); + } + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillDCAtoPV); + } + if (resoSwitchVals[UseDCABetDaug][IndexLambda] && std::abs(candidate.dcaV0daughters()) > resoCutVals[CutDCABetDaug][IndexLambda]) { + return false; + } + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillDCAxDaughters); + } // v0 radius cuts - if (resoSwitchVals[kUseV0Radius][iLambda] && (candidate.v0radius() < resoCutVals[kRadiusMin][iLambda] || candidate.v0radius() > resoCutVals[kRadiusMax][iLambda])) + if (resoSwitchVals[UseV0Radius][IndexLambda] && (candidate.v0radius() < resoCutVals[CutRadiusMin][IndexLambda] || candidate.v0radius() > resoCutVals[CutRadiusMax][IndexLambda])) { return false; - histos.fill(HIST("hLambdaCount"), 6.5); + } + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillV0Radius); + } // cosine pointing angle cuts - if (candidate.v0cosPA() < resoCutVals[kCosPA][iLambda]) + if (resoSwitchVals[UseCosPA][IndexLambda] && candidate.v0cosPA() < resoCutVals[CutCosPA][IndexLambda]) { return false; - histos.fill(HIST("hLambdaCount"), 7.5); + } + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillCosPA); + } // Proper lifetime float cTauLambda = candidate.distovertotmom(posX, posY, posZ) * massLambda; - if (resoSwitchVals[kUseProperLifetime][iLambda] && cTauLambda > resoCutVals[kLifeTime][iLambda]) + if (resoSwitchVals[UseProperLifetime][IndexLambda] && cTauLambda > resoCutVals[CutLifeTime][IndexLambda]) { return false; - histos.fill(HIST("hLambdaCount"), 8.5); - if (isL) { - if (!selectionV0Daughter(postrack, kProtons) || !selectionV0Daughter(negtrack, kPions)) - return false; } - if (isAL) { - if (!selectionV0Daughter(postrack, kPions) || !selectionV0Daughter(negtrack, kProtons)) - return false; + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillProperLifetime); } - histos.fill(HIST("hLambdaCount"), 9.5); - if (!cfgUseAntiLambda && isAL) { // Reject the track if it is antilambda + // ArmenterosPodolanskiCut + if (resoSwitchVals[UseArmPodCut][IndexLambda] && (candidate.qtarm() / std::abs(candidate.alpha())) < resoCutVals[CutArmPodMinVal][IndexLambda]) { return false; } + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillArmPodCut); + } - histos.fill(HIST("hLambdaPhi"), candidate.phi()); - histos.fill(HIST("hLambdaEta"), candidate.eta()); - histos.fill(HIST("PrPlusTPC_L"), postrack.pt(), postrack.tpcNSigmaPr()); - histos.fill(HIST("PrPlusTOF_L"), postrack.pt(), postrack.tofNSigmaPr()); - histos.fill(HIST("PiMinusTPC_L"), negtrack.pt(), negtrack.tpcNSigmaPi()); - histos.fill(HIST("PiMinusTOF_L"), negtrack.pt(), negtrack.tofNSigmaPi()); + if (resoSwitchVals[UseCompMassRej][IndexLambda]) { + if (std::abs(candidate.mK0Short() - o2::constants::physics::MassK0Short) < resoCutVals[CutCompMassRej][IndexLambda]) { + return false; + } + } + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillCompetingMass); + } + + if (isL) { + if (!selectionV0Daughter(postrack, UseProtons) || !selectionV0Daughter(negtrack, UsePions)) { + isL = false; + } + } + if (isAL) { + if (!selectionV0Daughter(postrack, UsePions) || !selectionV0Daughter(negtrack, UseProtons)) { + isAL = false; + } + } + if (!isL && !isAL) { + return false; + } + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillDaughterTrackSelected); + histos.fill(HIST("hLambdaPhi"), candidate.phi()); + histos.fill(HIST("hLambdaEta"), candidate.eta()); + histos.fill(HIST("hLambdaRap"), candidate.yLambda()); + } + if (isL && system == SameEvent) { + histos.fill(HIST("PrPlusTPC_La"), postrack.pt(), postrack.tpcNSigmaPr()); + histos.fill(HIST("PrPlusTOF_La"), postrack.pt(), postrack.tofNSigmaPr()); + histos.fill(HIST("PiMinusTPC_La"), negtrack.pt(), negtrack.tpcNSigmaPi()); + histos.fill(HIST("PiMinusTOF_La"), negtrack.pt(), negtrack.tofNSigmaPi()); + } + if (isAL && system == SameEvent) { + histos.fill(HIST("PrMinusTPC_Al"), negtrack.pt(), negtrack.tpcNSigmaPr()); + histos.fill(HIST("PrMinusTOF_Al"), negtrack.pt(), negtrack.tofNSigmaPr()); + histos.fill(HIST("PiPlusTPC_Al"), postrack.pt(), postrack.tpcNSigmaPi()); + histos.fill(HIST("PiPlusTOF_Al"), postrack.pt(), postrack.tofNSigmaPi()); + } return true; } @@ -1472,15 +1923,15 @@ struct PidDiHadron { void processSameReso(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks, aod::BCsWithTimestamps const&, aod::V0Datas const& V0s) { auto bc = collision.bc_as(); - float cent = -1.; float weightCent = 1.0f; - if (!cfgCentTableUnavailable) - cent = getCentrality(collision); - if (cfgUseAdditionalEventCut && !selectionEvent(collision, tracks.size(), cent, true)) + float cent = cfgEvSel.cfgCentTableUnavailable ? -1.f : getCentrality(collision); + + if (cfgEvSel.cfgUseAdditionalEventCut && !selectionEvent(collision, tracks.size(), cent, true)) { return; + } loadCorrection(bc.timestamp()); - if (!cfgCentTableUnavailable) { + if (!cfgEvSel.cfgCentTableUnavailable) { getCentralityWeight(weightCent, cent); histos.fill(HIST("Centrality"), cent); histos.fill(HIST("CentralityWeighted"), cent, weightCent); @@ -1488,17 +1939,28 @@ struct PidDiHadron { histos.fill(HIST("Nch"), tracks.size()); histos.fill(HIST("zVtx"), collision.posZ()); - if (cfgSelCollByNch && (tracks.size() < cfgCutMultMin || tracks.size() >= cfgCutMultMax)) { + auto multiplicity = static_cast(tracks.size()); + + if (cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks, multiplicity); + } + + histos.fill(HIST("Nch_corrected"), multiplicity); + + if (cfgEvSel.cfgSelCollByNch && (multiplicity < cfgEvSel.cfgCutMultMin || multiplicity >= cfgEvSel.cfgCutMultMax)) { return; } - if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent < cfgCutCentMin || cent >= cfgCutCentMax)) { + if (!cfgEvSel.cfgSelCollByNch && !cfgEvSel.cfgCentTableUnavailable && (cent < cfgEvSel.cfgCutCentMin || cent >= cfgEvSel.cfgCutCentMax)) { return; } histos.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin - sameReso->fillEvent(tracks.size(), CorrelationContainer::kCFStepReconstructed); - fillCorrelationsReso(V0s, tracks, collision.posZ(), collision.posY(), collision.posX(), SameEvent, getMagneticField(bc.timestamp()), cent, weightCent); + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity : cent; + + auto assocs = asscTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + + fillCorrelationsReso(V0s, assocs, collision.posZ(), collision.posY(), collision.posX(), SameEvent, getMagneticField(bc.timestamp()), eventClass, weightCent); } PROCESS_SWITCH(PidDiHadron, processSameReso, "Process same event for resonances", true); @@ -1512,106 +1974,231 @@ struct PidDiHadron { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(V0s, tracks); - Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto it = pairs.begin(); it != pairs.end(); it++) { auto& [collision1, v0s1, collision2, tracks2] = *it; - if (!collision1.sel8() || !collision2.sel8()) + if (!collision1.sel8() || !collision2.sel8()) { continue; + } auto tracks1 = tracks.sliceByCached(o2::aod::track::collisionId, collision1.globalIndex(), this->cache); - if (cfgSelCollByNch && (tracks1.size() < cfgCutMultMin || tracks1.size() >= cfgCutMultMax)) - continue; - if (cfgSelCollByNch && (tracks2.size() < cfgCutMultMin || tracks2.size() >= cfgCutMultMax)) - continue; + float cent2 = cfgEvSel.cfgCentTableUnavailable ? -1.f : getCentrality(collision2); + float cent1 = cfgEvSel.cfgCentTableUnavailable ? -1.f : getCentrality(collision1); - float cent1 = -1; - float cent2 = -1; - if (!cfgCentTableUnavailable) { - cent1 = getCentrality(collision1); - cent2 = getCentrality(collision2); + if (cfgEvSel.cfgUseAdditionalEventCut && !selectionEvent(collision1, tracks1.size(), cent1, false)) { + continue; } - if (cfgUseAdditionalEventCut && !selectionEvent(collision1, tracks1.size(), cent1, false)) + if (cfgEvSel.cfgUseAdditionalEventCut && !selectionEvent(collision2, tracks2.size(), cent2, false)) { continue; - if (cfgUseAdditionalEventCut && !selectionEvent(collision2, tracks2.size(), cent2, false)) + } + + if (!cfgEvSel.cfgSelCollByNch && !cfgEvSel.cfgCentTableUnavailable && (cent1 < cfgEvSel.cfgCutCentMin || cent1 >= cfgEvSel.cfgCutCentMax)) { continue; + } - if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent1 < cfgCutCentMin || cent1 >= cfgCutCentMax)) + if (!cfgEvSel.cfgSelCollByNch && !cfgEvSel.cfgCentTableUnavailable && (cent2 < cfgEvSel.cfgCutCentMin || cent2 >= cfgEvSel.cfgCutCentMax)) { continue; + } + + auto multiplicity1 = static_cast(tracks1.size()); + auto multiplicity2 = static_cast(tracks2.size()); + + if (cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks1, multiplicity1); + + if (cfgEvSel.cfgUseCol2ForMixedEvSel) { + nchCounter(tracks2, multiplicity2); + } + } - if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent2 < cfgCutCentMin || cent2 >= cfgCutCentMax)) + if (cfgEvSel.cfgSelCollByNch && (multiplicity1 < cfgEvSel.cfgCutMultMin || multiplicity1 >= cfgEvSel.cfgCutMultMax)) { continue; + } + + if (cfgEvSel.cfgSelCollByNch && (multiplicity2 < cfgEvSel.cfgCutMultMin || multiplicity2 >= cfgEvSel.cfgCutMultMax)) { + continue; + } - histos.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin auto bc = collision1.bc_as(); loadCorrection(bc.timestamp()); float eventWeight = 1.0f; - if (cfgUseEventWeights) { + if (cfgEffWeights.cfgUseEventWeights) { eventWeight = 1.0f / it.currentWindowNeighbours(); } + + histos.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity1 : cent1; float weightCent = 1.0f; - if (!cfgCentTableUnavailable) + if (!cfgEvSel.cfgCentTableUnavailable) { getCentralityWeight(weightCent, cent1); + } + + auto assocs = asscTracks->sliceByCached(aod::track::collisionId, collision2.globalIndex(), cache); - fillCorrelationsReso(v0s1, tracks2, collision1.posZ(), collision1.posY(), collision1.posX(), MixedEvent, getMagneticField(bc.timestamp()), cent1, eventWeight * weightCent); + fillCorrelationsReso(v0s1, assocs, collision1.posZ(), collision1.posY(), collision1.posX(), MixedEvent, getMagneticField(bc.timestamp()), eventClass, eventWeight * weightCent); } } PROCESS_SWITCH(PidDiHadron, processMixedReso, "Process mixed events", true); - void processMC(FilteredCollisions::iterator const& collision, FilteredMcTracks const& tracksmc, aod::BCsWithTimestamps const&, aod::McParticles const&) + void processSameMC(FilteredMcCollisions::iterator const& mcCollision, FilteredMcTracks const& mcTracks, SmallGroupMcCollisions const& collisions) + { + float cent = -1.f; + if (!cfgEvSel.cfgCentTableUnavailable) { + for (const auto& collision : collisions) { + cent = getCentrality(collision); + } + histos.fill(HIST("Centrality"), cent); + } + auto multiplicity = static_cast(mcTracks.size()); + histos.fill(HIST("Nch"), multiplicity); + histos.fill(HIST("zVtx"), mcCollision.posZ()); + + if (cfgEvSel.cfgSelCollByNch && (multiplicity < cfgEvSel.cfgCutMultMin || multiplicity >= cfgEvSel.cfgCutMultMax)) { + return; + } + if (!cfgEvSel.cfgSelCollByNch && !cfgEvSel.cfgCentTableUnavailable && (cent < cfgEvSel.cfgCutCentMin || cent >= cfgEvSel.cfgCutCentMax)) { + return; + } + histos.fill(HIST("eventcount"), SameEvent); + + float eventWeight = 1.0f; + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity : cent; + + auto mcAssocs = asscTracksMC->sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); + + fillCorrelationsMC(mcTracks, mcAssocs, mcCollision.posZ(), SameEvent, eventClass, eventWeight); + + } // end of processSameMC + PROCESS_SWITCH(PidDiHadron, processSameMC, "Process Monte Carlo for same event correlations", true); + + void processMixedMC(FilteredMcCollisions const& mcCollisions, FilteredMcTracks const& mcTracks, SmallGroupMcCollisions const& collisions) + { + auto getTracksSize = [&mcTracks, this](FilteredMcCollisions::iterator const& mcCollision) { + auto associatedTracks = mcTracks.sliceByCached(o2::aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), this->cache); + auto mult = associatedTracks.size(); + return mult; + }; + + using MixedBinning = FlexibleBinningPolicy, aod::mccollision::PosZ, decltype(getTracksSize)>; + + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; + + auto tracksTuple = std::make_tuple(mcTracks, mcTracks); + Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [mcCollision1, mcTracks1, mcCollision2, mcTracks2] = *it; + + auto groupedCollisions1 = collisions.sliceBy(collisionPerMCCollision, mcCollision1.globalIndex()); + auto groupedCollisions2 = collisions.sliceBy(collisionPerMCCollision, mcCollision2.globalIndex()); + + float cent1 = -1.f; + float cent2 = -1.f; + + if (!cfgEvSel.cfgCentTableUnavailable) { + for (const auto& collision : groupedCollisions1) { + cent1 = getCentrality(collision); + } + for (const auto& collision : groupedCollisions2) { + cent2 = getCentrality(collision); + } + } + + if (!cfgEvSel.cfgSelCollByNch && !cfgEvSel.cfgCentTableUnavailable && (cent1 < cfgEvSel.cfgCutCentMin || cent1 >= cfgEvSel.cfgCutCentMax)) { + continue; + } + + if (!cfgEvSel.cfgSelCollByNch && !cfgEvSel.cfgCentTableUnavailable && (cent2 < cfgEvSel.cfgCutCentMin || cent2 >= cfgEvSel.cfgCutCentMax)) { + continue; + } + + auto multiplicity1 = static_cast(mcTracks1.size()); + auto multiplicity2 = static_cast(mcTracks2.size()); + + if (cfgEvSel.cfgSelCollByNch && (multiplicity1 < cfgEvSel.cfgCutMultMin || multiplicity1 >= cfgEvSel.cfgCutMultMax)) { + continue; + } + + if (cfgEvSel.cfgSelCollByNch && (multiplicity2 < cfgEvSel.cfgCutMultMin || multiplicity2 >= cfgEvSel.cfgCutMultMax)) { + continue; + } + + histos.fill(HIST("eventcount"), MixedEvent); + + float eventWeight = (cfgEffWeights.cfgUseEventWeights) ? (1.0f / it.currentWindowNeighbours()) : 1.0f; + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity1 : cent1; + + auto mcAssocs = asscTracksMC->sliceByCached(aod::mcparticle::mcCollisionId, mcCollision2.globalIndex(), cache); + + fillCorrelationsMC(mcTracks1, mcAssocs, mcCollision1.posZ(), MixedEvent, eventClass, eventWeight); + } + } + PROCESS_SWITCH(PidDiHadron, processMixedMC, "Process Monte Carlo for mixed event correlations", true); + + void processMCPID(FilteredCollisions::iterator const& collision, FilteredTracksMcLabels const& tracksmc, aod::BCsWithTimestamps const&, aod::McParticles const&) { float cent = -1.; - if (!cfgCentTableUnavailable) + if (!cfgEvSel.cfgCentTableUnavailable) { cent = getCentrality(collision); - if (cfgUseAdditionalEventCut && !selectionEvent(collision, tracksmc.size(), cent, true)) + } + if (cfgEvSel.cfgUseAdditionalEventCut && !selectionEvent(collision, tracksmc.size(), cent, true)) { return; + } - if (cfgSelCollByNch && (tracksmc.size() < cfgCutMultMin || tracksmc.size() >= cfgCutMultMax)) { + if (cfgEvSel.cfgSelCollByNch && (tracksmc.size() < cfgEvSel.cfgCutMultMin || tracksmc.size() >= cfgEvSel.cfgCutMultMax)) { return; } - if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent < cfgCutCentMin || cent >= cfgCutCentMax)) { + if (!cfgEvSel.cfgSelCollByNch && !cfgEvSel.cfgCentTableUnavailable && (cent < cfgEvSel.cfgCutCentMin || cent >= cfgEvSel.cfgCutCentMax)) { return; } // loop over all tracks for (auto const& track : tracksmc) { - if (!trackSelected(track)) + if (!trackSelected(track)) { continue; + } int pidIndex = getNsigmaPID(track); // Fill Counts for selection through Nsigma cuts - if (pidIndex == kPions) + if (pidIndex == UsePions) { histos.fill(HIST("hNsigmaPionSelected"), track.pt()); - if (pidIndex == kKaons) + } + if (pidIndex == UseKaons) { histos.fill(HIST("hNsigmaKaonSelected"), track.pt()); - if (pidIndex == kProtons) + } + if (pidIndex == UseProtons) { histos.fill(HIST("hNsigmaProtonSelected"), track.pt()); + } if (track.mcParticle().isPhysicalPrimary()) { - if (cfgPIDParticle == kPions) + if (cfgPIDConfigs.cfgPIDParticle == UsePions) { histos.fill(HIST("TpcdEdx_ptwise_all_physprim"), track.pt(), track.tpcSignal(), track.tofNSigmaPi()); - if (cfgPIDParticle == kKaons) + } + if (cfgPIDConfigs.cfgPIDParticle == UseKaons) { histos.fill(HIST("TpcdEdx_ptwise_all_physprim"), track.pt(), track.tpcSignal(), track.tofNSigmaKa()); - if (cfgPIDParticle == kProtons) + } + if (cfgPIDConfigs.cfgPIDParticle == UseProtons) { histos.fill(HIST("TpcdEdx_ptwise_all_physprim"), track.pt(), track.tpcSignal(), track.tofNSigmaPr()); + } } // Check the PDG code for the particles (MC truth) and match with analysed Nsigma PID if (std::abs(track.mcParticle().pdgCode()) == PDG_t::kPiPlus) { histos.fill(HIST("hNsigmaPionTrue"), track.pt()); histos.fill(HIST("TpcdEdx_ptwise_pi"), track.pt(), track.tpcSignal(), track.tofNSigmaPi()); - if (track.mcParticle().isPhysicalPrimary()) + if (track.mcParticle().isPhysicalPrimary()) { histos.fill(HIST("TpcdEdx_ptwise_pi_physprim"), track.pt(), track.tpcSignal(), track.tofNSigmaPi()); + } double tpcExpSignalPi = track.tpcSignal() - (track.tpcNSigmaPi() * track.tpcExpSigmaPi()); histos.fill(HIST("ExpTpcdEdx_ptwise_pi"), track.pt(), tpcExpSignalPi, track.tofNSigmaPi()); - if (pidIndex == kPions) { + if (pidIndex == UsePions) { histos.fill(HIST("hNsigmaPionTruePositives"), track.pt()); } } // Pion condition @@ -1619,13 +2206,14 @@ struct PidDiHadron { if (std::abs(track.mcParticle().pdgCode()) == PDG_t::kKPlus) { histos.fill(HIST("hNsigmaKaonTrue"), track.pt()); histos.fill(HIST("TpcdEdx_ptwise_ka"), track.pt(), track.tpcSignal(), track.tofNSigmaKa()); - if (track.mcParticle().isPhysicalPrimary()) + if (track.mcParticle().isPhysicalPrimary()) { histos.fill(HIST("TpcdEdx_ptwise_ka_physprim"), track.pt(), track.tpcSignal(), track.tofNSigmaKa()); + } double tpcExpSignalKa = track.tpcSignal() - (track.tpcNSigmaKa() * track.tpcExpSigmaKa()); histos.fill(HIST("ExpTpcdEdx_ptwise_ka"), track.pt(), tpcExpSignalKa, track.tofNSigmaKa()); - if (pidIndex == kKaons) { + if (pidIndex == UseKaons) { histos.fill(HIST("hNsigmaKaonTruePositives"), track.pt()); } } // Kaon condition @@ -1633,19 +2221,20 @@ struct PidDiHadron { if (std::abs(track.mcParticle().pdgCode()) == PDG_t::kProton) { histos.fill(HIST("hNsigmaProtonTrue"), track.pt()); histos.fill(HIST("TpcdEdx_ptwise_pr"), track.pt(), track.tpcSignal(), track.tofNSigmaPr()); - if (track.mcParticle().isPhysicalPrimary()) + if (track.mcParticle().isPhysicalPrimary()) { histos.fill(HIST("TpcdEdx_ptwise_pr_physprim"), track.pt(), track.tpcSignal(), track.tofNSigmaPr()); + } double tpcExpSignalPr = track.tpcSignal() - (track.tpcNSigmaPr() * track.tpcExpSigmaPr()); histos.fill(HIST("ExpTpcdEdx_ptwise_pr"), track.pt(), tpcExpSignalPr, track.tofNSigmaPr()); - if (pidIndex == kProtons) { + if (pidIndex == UseProtons) { histos.fill(HIST("hNsigmaProtonTruePositives"), track.pt()); } } // Proton condition } // end of tracks MC loop - } // end of process MC - PROCESS_SWITCH(PidDiHadron, processMC, "Process Monte Carlo", true); + } // end of process MCPID + PROCESS_SWITCH(PidDiHadron, processMCPID, "Process Monte Carlo for PID", true); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/pidLongRange.cxx b/PWGCF/TwoParticleCorrelations/Tasks/pidLongRange.cxx new file mode 100644 index 00000000000..555d4f8de81 --- /dev/null +++ b/PWGCF/TwoParticleCorrelations/Tasks/pidLongRange.cxx @@ -0,0 +1,1915 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file pidLongRange.cxx +/// \brief Identified particles long-range correlations using forward FIT detectors and TPC, with focus on particle +/// \author Thor Jensen (thor.kjaersgaard.jensen@cern.ch), Preet Bhanjan Pati (preet.bhanjan.pati@cern.ch) + +#include "PWGCF/TwoParticleCorrelations/Core/DihadronContainer.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseITS.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::aod::rctsel; +using namespace constants::math; + +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, (DEFAULT), (HELP)}; // NOLINT(bugprone-macro-parentheses) + +template +auto readMatrix(Array2D const& mat, P& array) +{ + for (auto i = 0; i < static_cast(mat.rows); ++i) { + for (auto j = 0; j < static_cast(mat.cols); ++j) { + array[i][j] = mat(i, j); + } + } + + return; +} + +static constexpr std::array, 20> LongArrayFloat = {{{{1.1, 2.1, 3.1}}, {{1.2, 2.2, 3.2}}, {{1.3, 2.3, 3.3}}, {{-1.1, -2.1, -3.1}}, {{-1.2, -2.2, -3.2}}, {{-1.3, -2.3, -3.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}, {{1.3, 1.3, 1.3}}, {{-1.1, -1.1, -1.1}}, {{-1.2, -1.2, -1.2}}, {{-1.3, -1.3, -1.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}, {{1.3, 1.3, 1.3}}, {{-1.1, -1.1, -1.1}}, {{-1.2, -1.2, -1.2}}, {{-1.3, -1.3, -1.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}}}; +static constexpr std::array, 20> LongArrayInt = {{{{1, 2, 3}}, {{1, 2, 3}}, {{1, 2, 3}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}, {{1, 1, 1}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}, {{1, 1, 1}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}}}; + +struct PidLongRange { + o2::aod::ITSResponse itsResponse; + Service ccdb{}; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgQABasic, bool, true, "Enable QA histograms for event and track selection") + O2_DEFINE_CONFIGURABLE(cfgStrictTrackCounter, bool, false, "Strict track counter for multiplicity correlation cut, counts only tracks that pass all cuts and are used in the correlation") + O2_DEFINE_CONFIGURABLE(cfgSampleSize, int, 10, "Sample size for mixed event") + O2_DEFINE_CONFIGURABLE(cfgAnalyzeTPCFT0A, bool, true, "Switch for doing TPC-FT0A correlations") + O2_DEFINE_CONFIGURABLE(cfgAnalyzeTPCFT0C, bool, true, "Switch for doing TPC-FT0C correlations") + O2_DEFINE_CONFIGURABLE(cfgMinMixEventNum, int, 5, "Minimum number of events to mix") + Configurable> cfgRunRemoveList{"cfgRunRemoveList", std::vector{-1}, "excluded run numbers"}; + Configurable cfgGainEqPath{"cfgGainEqPath", "Analysis/EventPlane/GainEq", "CCDB path for gain equalization constants"}; + Configurable cfgCorrLevel{"cfgCorrLevel", 0, "calibration step: 0 = no corr, 1 = gain corr"}; + } cfgGeneral; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgEfficiency, std::string, "", "CCDB path to efficiency object") + O2_DEFINE_CONFIGURABLE(cfgEfficiencyNch, std::string, "", "CCDB path to multiplicity dependent efficiency object") + O2_DEFINE_CONFIGURABLE(cfgUseEventWeights, bool, false, "Use event weights for mixed event") + O2_DEFINE_CONFIGURABLE(cfgCentralityWeight, std::string, "", "CCDB path to centrality weight object") + O2_DEFINE_CONFIGURABLE(cfgLocalEfficiency, bool, false, "Use local efficiency object") + O2_DEFINE_CONFIGURABLE(cfgLocalEfficiencyNch, bool, false, "Use local multiplicity dependent efficiency object"); + } cfgEffWeights; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgGlobalPtMin, float, 0.2f, "minimum accepted track pT") + O2_DEFINE_CONFIGURABLE(cfgGlobalPtMax, float, 10.0f, "maximum accepted track pT") + O2_DEFINE_CONFIGURABLE(cfgGlobalEta, float, 0.8f, "Eta cut") + O2_DEFINE_CONFIGURABLE(cfgGlobalChi2prTPCcls, float, 2.5f, "Maximum chi2 per TPC clusters") + O2_DEFINE_CONFIGURABLE(cfgGlobalDCAz, float, 2.0f, "Maximum DCA to vertex z") + Configurable> cfgTrackCutsDCAxy{"cfgTrackCutsDCAxy", std::vector{"[0]*(0.0026+0.005/(x^1.01))", "[0]*(0.0026+0.005/(x^1.01))"}, "Functional form of pt-dependent DCAxy cut"}; + Configurable> cfgTrackCutsDCAz{"cfgTrackCutsDCAz", std::vector{"", ""}, "Functional form of pt-dependent DCAz cut"}; + Configurable> cfgTrackCuts{"cfgTrackCuts", {LongArrayFloat.front().data(), 10, 2, {"PtMin", "PtMax", "EtaCut", "Chi2PrTpcCls", "TpcCluster", "TpcCrossedRows", "ItsCluster", "DCAz", "DCAzNsigma", "DCAxyNsigma"}, {"General", "SelNch"}}, "Labeled array for track selections"}; + } cfgTrkSel; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgGlobalCutVertex, float, 10.0f, "Accepted z-vertex range") + O2_DEFINE_CONFIGURABLE(cfgCutOccupancyHigh, int, 2000, "High cut on TPC occupancy") + O2_DEFINE_CONFIGURABLE(cfgCutOccupancyLow, int, 0, "Low cut on TPC occupancy") + O2_DEFINE_CONFIGURABLE(cfgUseAdditionalEventCut, bool, true, "Use additional event cut on mult correlations") + O2_DEFINE_CONFIGURABLE(cfgMinMultForCorrelations, int, 0, "minimum multiplicity for correlations") + O2_DEFINE_CONFIGURABLE(cfgMaxMultForCorrelations, int, 20, "maximum multiplicity for correlations") + O2_DEFINE_CONFIGURABLE(cfgSelCollByNch, bool, true, "Select collisions by Nch or centrality") + O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FT0A") + O2_DEFINE_CONFIGURABLE(cfgMinCentForCorrelations, int, 0, "minimum centrality for correlations") + O2_DEFINE_CONFIGURABLE(cfgMaxCentForCorrelations, int, 20, "maximum centrality for correlations") + O2_DEFINE_CONFIGURABLE(cfgUseCol2ForMixedEvSel, bool, true, "Use Mult/Cent cut on collision 2 during mixed event"); + O2_DEFINE_CONFIGURABLE(cfgEvSelRCTflags, std::string, "", "keep empty to disable, usage: 'CentralBarrelTracking (CBT)', 'CBT_hadronPID' ") + Configurable> cfgUseEventCuts{"cfgUseEventCuts", {LongArrayInt.front().data(), 14, 1, {"Filtered Events", "Sel8", "kNoTimeFrameBorder", "kNoITSROFrameBorder", "kNoSameBunchPileup", "kIsGoodZvtxFT0vsPV", "kNoCollInTimeRangeStandard", "kIsGoodITSLayersAll", "kIsGoodITSLayer0123", "kNoCollInRofStandard", "kNoHighMultCollInPrevRof", "Occupancy", "Multcorrelation", "T0AV0ACut"}, {"EvCuts"}}, "Labeled array (int) for various cuts on resonances"}; + } cfgEvSel; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgMultCentHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 10.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultCentLowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultT0CCutEnabled, bool, false, "Enable Global multiplicity vs T0C centrality cut") + Configurable> cfgMultT0CCutPars{"cfgMultT0CCutPars", std::vector{143.04, -4.58368, 0.0766055, -0.000727796, 2.86153e-06, 23.3108, -0.36304, 0.00437706, -4.717e-05, 1.98332e-07}, "Global multiplicity vs T0C centrality cut parameter values"}; + O2_DEFINE_CONFIGURABLE(cfgMultPVT0CCutEnabled, bool, false, "Enable PV multiplicity vs T0C centrality cut") + Configurable> cfgMultPVT0CCutPars{"cfgMultPVT0CCutPars", std::vector{195.357, -6.15194, 0.101313, -0.000955828, 3.74793e-06, 30.0326, -0.43322, 0.00476265, -5.11206e-05, 2.13613e-07}, "PV multiplicity vs T0C centrality cut parameter values"}; + O2_DEFINE_CONFIGURABLE(cfgMultMultPVHighCutFunction, std::string, "[0]+[1]*x + 5.*([2]+[3]*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultMultPVLowCutFunction, std::string, "[0]+[1]*x - 5.*([2]+[3]*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultGlobalPVCutEnabled, bool, false, "Enable global multiplicity vs PV multiplicity cut") + Configurable> cfgMultGlobalPVCutPars{"cfgMultGlobalPVCutPars", std::vector{-0.140809, 0.734344, 2.77495, 0.0165935}, "PV multiplicity vs T0C centrality cut parameter values"}; + O2_DEFINE_CONFIGURABLE(cfgMultMultV0AHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 4.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultMultV0ALowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultMultV0ACutEnabled, bool, false, "Enable global multiplicity vs V0A multiplicity cut") + Configurable> cfgMultMultV0ACutPars{"cfgMultMultV0ACutPars", std::vector{534.893, 184.344, 0.423539, -0.00331436, 5.34622e-06, 871.239, 53.3735, -0.203528, 0.000122758, 5.41027e-07}, "Global multiplicity vs V0A multiplicity cut parameter values"}; + std::vector multT0CCutPars; + std::vector multPVT0CCutPars; + std::vector multGlobalPVCutPars; + std::vector multMultV0ACutPars; + std::unique_ptr fMultPVT0CCutLow = nullptr; + std::unique_ptr fMultPVT0CCutHigh = nullptr; + std::unique_ptr fMultT0CCutLow = nullptr; + std::unique_ptr fMultT0CCutHigh = nullptr; + std::unique_ptr fMultGlobalPVCutLow = nullptr; + std::unique_ptr fMultGlobalPVCutHigh = nullptr; + std::unique_ptr fMultMultV0ACutLow = nullptr; + std::unique_ptr fMultMultV0ACutHigh = nullptr; + std::unique_ptr fT0AV0AMean = nullptr; + std::unique_ptr fT0AV0ASigma = nullptr; + std::unique_ptr fPtDepDCAxy = nullptr; + std::unique_ptr fPtDepDCAxyForNch = nullptr; + std::unique_ptr fPtDepDCAz = nullptr; + std::unique_ptr fPtDepDCAzForNch = nullptr; + O2_DEFINE_CONFIGURABLE(cfgV0AT0Acut, int, 5, "V0AT0A cut") + } cfgFuncParas; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgTofPtCut, float, 0.4f, "Minimum pt to use TOF N-sigma") + O2_DEFINE_CONFIGURABLE(cfgUseItsPID, bool, false, "Use ITS PID for particle identification") + O2_DEFINE_CONFIGURABLE(cfgQANsigma, bool, false, "Get QA histograms for selection of pions, kaons, and protons") + O2_DEFINE_CONFIGURABLE(cfgQAdEdx, bool, false, "Get dEdx histograms for pions, kaons, and protons") + O2_DEFINE_CONFIGURABLE(cfgPIDUseRejection, bool, true, "True: use exclusion exclusion criteria for PID determination, false: don't use exclusion") + Configurable> nSigmas{"nSigmas", {LongArrayFloat.front().data(), 6, 3, {"UpCut_pi", "UpCut_ka", "UpCut_pr", "LowCut_pi", "LowCut_ka", "LowCut_pr"}, {"TPC", "TOF", "ITS"}}, "Labeled array for n-sigma values for TPC, TOF, ITS for pions, kaons, protons (positive and negative)"}; + } cfgPIDConfigs; + + SliceCache cache; + + // Axes for general QA + ConfigurableAxis axisVertex{"axisVertex", {10, -10, 10}, "vertex axis for histograms"}; + ConfigurableAxis axisMult{"axisMult", {10, 0, 100}, "multiplicity axis for QA histograms"}; + ConfigurableAxis axisCent{"axisCent", {100, 0, 100}, "centrality axis for QA histograms"}; + ConfigurableAxis axisEta{"axisEta", {40, -1., 1.}, "eta axis for histograms"}; + ConfigurableAxis axisPhi{"axisPhi", {72, 0.0, constants::math::TwoPI}, "phi axis for histograms"}; + ConfigurableAxis axisPtFiner{"axisPtFiner", {98, 0.2, 10.0}, "pt axis for histograms"}; + ConfigurableAxis axisDCAz{"axisDCAz", {200, -2, 2}, "DCA_{z} (cm)"}; + ConfigurableAxis axisDCAxy{"axisDCAxy", {200, -1, 1}, "DCA_{xy} (cm)"}; + + // Axes for Correlations + ConfigurableAxis axisMultForCorr{"axisMultForCorr", {VARIABLE_WIDTH, 0, 5, 10, 20, 50, 100, 150, 200, 300, 500}, "multiplicity axis for correlation histograms"}; + ConfigurableAxis axisCentForCorr{"axisCentForCorr", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, "centrality axis for correlation histograms"}; + ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {72, -PIHalf, PIHalf * 3}, "delta phi axis for histograms"}; + ConfigurableAxis axisDeltaEtaTpcFt0a{"axisDeltaEtaTpcFt0a", {32, -5.8, -2.6}, "delta eta axis, -5.8~-2.6 for TPC-FT0A,"}; + ConfigurableAxis axisDeltaEtaTpcFt0c{"axisDeltaEtaTpcFt0c", {32, 1.2, 4.2}, "delta eta axis, 1.2~4.2 for TPC-FT0C"}; + ConfigurableAxis axisDeltaEtaFt0aFt0c{"axisDeltaEtaFt0aFt0c", {32, -1.5, 3.0}, "delta eta axis"}; + ConfigurableAxis axisVtxMix{"axisVtxMix", {VARIABLE_WIDTH, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, "vertex axis for mixed event histograms"}; + ConfigurableAxis axisMultMix{"axisMultMix", {VARIABLE_WIDTH, 0, 10, 20, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 260}, "multiplicity / centrality axis for mixed event histograms"}; + ConfigurableAxis axisPtTrigger{"axisPtTrigger", {VARIABLE_WIDTH, 0.2, 0.5, 1, 1.5, 2, 3, 4, 6, 10}, "pt trigger axis for histograms"}; + ConfigurableAxis axisParticle{"axisParticle", {4, 0, 4}, "particle axis for correlation container"}; + ConfigurableAxis axisTpcNcls{"axisTpcNcls", {500, 0, 500}, "number of TPC clusters used for PID"}; + + // Axes for long-range correlations QA + ConfigurableAxis axisAmplitudeFt0{"axisAmplitudeFt0", {5000, 0, 1000}, "FT0A amplitude"}; + ConfigurableAxis axisChannelFt0aAxis{"axisChannelFt0aAxis", {96, 0.0, 96.0}, "FT0A channel"}; + ConfigurableAxis axisChannelFt0cAxis{"axisChannelFt0cAxis", {96, 0.0, 96.0}, "FT0C channel"}; + ConfigurableAxis cfgFITamp{"cfgFITamp", {1000, 0, 5000}, "FT0 amplitude"}; + + // Axes for PID QA + ConfigurableAxis axisNsigmaTPC{"axisNsigmaTPC", {80, -5, 5}, "nsigmaTPC axis"}; + ConfigurableAxis axisNsigmaTOF{"axisNsigmaTOF", {80, -5, 5}, "nsigmaTOF axis"}; + ConfigurableAxis axisNsigmaITS{"axisNsigmaITS", {80, -5, 5}, "nsigmaITS axis"}; + ConfigurableAxis axisTpcSignal{"axisTpcSignal", {250, 0, 250}, "dEdx axis for TPC"}; + ConfigurableAxis axisSigma{"axisSigma", {200, 0, 20}, "sigma axis for TPC"}; + + Filter collisionFilter = (nabs(aod::collision::posZ) < cfgEvSel.cfgGlobalCutVertex); + Filter trackFilter = (nabs(aod::track::eta) < cfgTrkSel.cfgGlobalEta) && (aod::track::pt > cfgTrkSel.cfgGlobalPtMin) && (aod::track::pt < cfgTrkSel.cfgGlobalPtMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (nabs(aod::track::dcaZ) < cfgTrkSel.cfgGlobalDCAz) && (aod::track::tpcChi2NCl < cfgTrkSel.cfgGlobalChi2prTPCcls); + + using FilteredCollisions = soa::Filtered>; + using FilteredTracks = soa::Filtered>; + + // FT0 geometry + o2::ft0::Geometry ft0Det; + static constexpr uint64_t Ft0IndexA = 96; + std::vector* offsetFT0 = nullptr; + std::vector cstFT0RelGain; + + // Corrections + TH3D* mEfficiency = nullptr; + TH1D* mEfficiencyNch = nullptr; + TH1D* mCentralityWeight = nullptr; + bool correctionsLoaded = false; + + // Define the outputs + OutputObj sameTpcFt0a{"sameEvent_TPC_FT0A"}; + OutputObj mixedTpcFt0a{"mixedEvent_TPC_FT0A"}; + OutputObj sameTpcFt0c{"sameEvent_TPC_FT0C"}; + OutputObj mixedTpcFt0c{"mixedEvent_TPC_FT0C"}; + OutputObj sameFt0aFt0c{"sameEvent_FT0A_FT0C"}; + OutputObj mixedFt0aFt0c{"mixedEvent_FT0A_FT0C"}; + + HistogramRegistry histos{"histos"}; + + // For Labelled array value containers + std::array, 10> trackCuts{}; + std::array, 14> eventCuts{}; + std::array, 6> nSigmaVals{}; + + // define global variables + TRandom3 fRandom{0}; + + enum EventType { + SameEvent = 1, + MixedEvent = 3 + }; + + enum FITIndex { + IndexFT0A = 0, + IndexFT0C = 1 + }; + + enum PIDIndex { + UseCharged = 0, + UsePions, + UseKaons, + UseProtons, + UseK0, + UseLambda, + UsePhi + }; + enum PiKpArrayIndex { + IndexPionUp = 0, + IndexKaonUp, + IndexProtonUp, + IndexPionLow, + IndexKaonLow, + IndexProtonLow + }; + enum DetectorType { + UseTPC = 0, + UseTOF, + UseITS + }; + enum Stage { + Before = 0, + After + }; + + enum EventCutTypes { + FilteredEvents = 0, + AfterSel8, + UseNoTimeFrameBorder, + UseNoITSROFrameBorder, + UseNoSameBunchPileup, + UseGoodZvtxFT0vsPV, + UseNoCollInTimeRangeStandard, + UseGoodITSLayersAll, + UseGoodITSLayer0123, + UseNoCollInRofStandard, + UseNoHighMultCollInPrevRof, + UseOccupancy, + UseMultCorrCut, + UseT0AV0ACut, + HaveFT0Cut, + NumEventCuts + }; + + enum EventCutType { + EvCut1 = 0, + NumEvCutTypes = 1 + }; + + enum TrackCuts { + TrkCutPtMin = 0, + TrkCutPtMax, + TrkCutEtaCut, + TrkCutChi2PrTpcCls, + TrkCutTpcCluster, + TrkCutTpcCrossedRows, + TrkCutItsCluster, + TrkCutDCAz, + TrkCutDCAzNsigma, + TrkCutDCAxyNsigma + }; + + enum TrackCutGroup { + UseGenTrkCuts = 0, + UseNchSelCuts, + NumTrackCutTypes + }; + + enum CentEstimators { + UseCentFT0C = 0, + UseCentFT0CVariant1, + UseCentFT0M, + UseCentFV0A, + // Count the total number of enum + NumCentEstimators + }; + + RCTFlagsChecker rctChecker{"CBT"}; + + void init(InitContext&) + { + // ---------------------------------------------------------------------------------------------------------------------- + // The way the code is initialized, TPC-FT0 correlations are done together and FT0A-FT0C correlations are done separately + // ---------------------------------------------------------------------------------------------------------------------- + + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setCaching(true); + auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + ccdb->setCreatedNotAfter(now); + + LOGF(info, "Starting init"); + + readMatrix(cfgTrkSel.cfgTrackCuts->getData(), trackCuts); + readMatrix(cfgEvSel.cfgUseEventCuts->getData(), eventCuts); + readMatrix(cfgPIDConfigs.nSigmas->getData(), nSigmaVals); + + if (doprocessSameFt0aFt0c || doprocessSameTpcFt0 || doprocessQA) { + histos.add("hEventCountRct", "Number of Event;; Count", {HistType::kTH1D, {{2, 0, 2}}}); + histos.get(HIST("hEventCountRct"))->GetXaxis()->SetBinLabel(1, "rct fail"); + histos.get(HIST("hEventCountRct"))->GetXaxis()->SetBinLabel(2, "rct pass"); + histos.add("hEventCount", "Number of Event;; Count", {HistType::kTH1D, {{15, -0.5, 14.5}}}); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(FilteredEvents + 1, "Filtered events"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(AfterSel8 + 1, "After sel8"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoTimeFrameBorder + 1, "kNoTimeFrameBorder"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoITSROFrameBorder + 1, "kNoITSROFrameBorder"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoSameBunchPileup + 1, "kNoSameBunchPileup"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodITSLayer0123 + 1, "kIsGoodITSLayer0123"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoCollInRofStandard + 1, "kNoCollInRofStandard"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoHighMultCollInPrevRof + 1, "kNoHighMultCollInPrevRof"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseOccupancy + 1, "Occupancy Cut"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseMultCorrCut + 1, "MultCorrelation Cut"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseT0AV0ACut + 1, "T0AV0A cut"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(HaveFT0Cut + 1, "Event has FT0 cut"); + } + + if ((doprocessSameFt0aFt0c || doprocessSameTpcFt0 || doprocessQA) && cfgGeneral.cfgQABasic) { + histos.add("hPassedEventSelection", "Number of Event;; Count", {HistType::kTH1D, {{12, -0.5, 11.5}}}); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(FilteredEvents + 1, "Filtered events"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(AfterSel8 + 1, "After sel8"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoTimeFrameBorder + 1, "kNoTimeFrameBorder"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoITSROFrameBorder + 1, "kNoITSROFrameBorder"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoSameBunchPileup + 1, "kNoSameBunchPileup"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseGoodITSLayer0123 + 1, "kIsGoodITSLayer0123"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoCollInRofStandard + 1, "kNoCollInRofStandard"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoHighMultCollInPrevRof + 1, "kNoHighMultCollInPrevRof"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseOccupancy + 1, "Occupancy Cut"); + } + + // Multiplicity correlation cuts + if (eventCuts[UseMultCorrCut][EvCut1] != 0) { + cfgFuncParas.multT0CCutPars = cfgFuncParas.cfgMultT0CCutPars; + cfgFuncParas.multPVT0CCutPars = cfgFuncParas.cfgMultPVT0CCutPars; + cfgFuncParas.multGlobalPVCutPars = cfgFuncParas.cfgMultGlobalPVCutPars; + cfgFuncParas.multMultV0ACutPars = cfgFuncParas.cfgMultMultV0ACutPars; + + cfgFuncParas.fMultPVT0CCutLow = std::make_unique("fMultPVT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultPVT0CCutLow->SetParameters(cfgFuncParas.multPVT0CCutPars.data()); + cfgFuncParas.fMultPVT0CCutHigh = std::make_unique("fMultPVT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultPVT0CCutHigh->SetParameters(cfgFuncParas.multPVT0CCutPars.data()); + + cfgFuncParas.fMultT0CCutLow = std::make_unique("fMultT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultT0CCutLow->SetParameters(cfgFuncParas.multT0CCutPars.data()); + cfgFuncParas.fMultT0CCutHigh = std::make_unique("fMultT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultT0CCutHigh->SetParameters(cfgFuncParas.multT0CCutPars.data()); + + cfgFuncParas.fMultGlobalPVCutLow = std::make_unique("fMultGlobalPVCutLow", cfgFuncParas.cfgMultMultPVLowCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultGlobalPVCutLow->SetParameters(cfgFuncParas.multGlobalPVCutPars.data()); + cfgFuncParas.fMultGlobalPVCutHigh = std::make_unique("fMultGlobalPVCutHigh", cfgFuncParas.cfgMultMultPVHighCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultGlobalPVCutHigh->SetParameters(cfgFuncParas.multGlobalPVCutPars.data()); + + cfgFuncParas.fMultMultV0ACutLow = std::make_unique("fMultMultV0ACutLow", cfgFuncParas.cfgMultMultV0ALowCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultMultV0ACutLow->SetParameters(cfgFuncParas.multMultV0ACutPars.data()); + cfgFuncParas.fMultMultV0ACutHigh = std::make_unique("fMultMultV0ACutHigh", cfgFuncParas.cfgMultMultV0AHighCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultMultV0ACutHigh->SetParameters(cfgFuncParas.multMultV0ACutPars.data()); + } + if (eventCuts[UseT0AV0ACut][EvCut1] != 0) { + cfgFuncParas.fT0AV0AMean = std::make_unique("fT0AV0AMean", "[0]+[1]*x", 0, 200000); + cfgFuncParas.fT0AV0AMean->SetParameters(-1601.0581, 9.417652e-01); + cfgFuncParas.fT0AV0ASigma = std::make_unique("fT0AV0ASigma", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 200000); + cfgFuncParas.fT0AV0ASigma->SetParameters(463.4144, 6.796509e-02, -9.097136e-07, 7.971088e-12, -2.600581e-17); + } + + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].empty()) { + cfgFuncParas.fPtDepDCAxy = std::make_unique("ptDepDCAxy", cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAxy->SetParameter(0, trackCuts[TrkCutDCAxyNsigma][UseGenTrkCuts]); + LOGF(info, "DCAxy pt-dependence function: %0.1f * %s", trackCuts[TrkCutDCAxyNsigma][UseGenTrkCuts], cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].c_str()); + } + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].empty()) { + cfgFuncParas.fPtDepDCAxyForNch = std::make_unique("ptDepDCAxyForNch", cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAxyForNch->SetParameter(0, trackCuts[TrkCutDCAxyNsigma][UseNchSelCuts]); + LOGF(info, "DCAxy pt-dependence function for Nch: %0.1f * %s", trackCuts[TrkCutDCAxyNsigma][UseNchSelCuts], cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].c_str()); + } + + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].empty()) { + cfgFuncParas.fPtDepDCAz = std::make_unique("ptDepDCAz", cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAz->SetParameter(0, trackCuts[TrkCutDCAzNsigma][UseGenTrkCuts]); + LOGF(info, "DCAz pt-dependence function: %0.1f * %s", trackCuts[TrkCutDCAzNsigma][UseGenTrkCuts], cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].c_str()); + } + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].empty()) { + cfgFuncParas.fPtDepDCAzForNch = std::make_unique("ptDepDCAzForNch", cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAzForNch->SetParameter(0, trackCuts[TrkCutDCAzNsigma][UseNchSelCuts]); + LOGF(info, "DCAz pt-dependence function for Nch: %0.1f * %s", trackCuts[TrkCutDCAzNsigma][UseNchSelCuts], cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].c_str()); + } + + const AxisSpec axisT0C{70, 0, 70000, "N_{ch} (T0C)"}; + const AxisSpec axisT0A{200, 0, 200000, "N_{ch} (T0A)"}; + const AxisSpec axisChi2{100, 0., 10.}; + const AxisSpec axisChID = {220, 0, 220}; + + auto maxSample = static_cast(cfgGeneral.cfgSampleSize); + AxisSpec axisSample{cfgGeneral.cfgSampleSize, 0, maxSample, "Sample"}; + + // Choose if it is Nch selection or Centrality selection + AxisSpec axisEventClass = {axisMultForCorr, "N_{ch}"}; + + if (!cfgEvSel.cfgSelCollByNch) { + axisEventClass = {axisCentForCorr, "Centrality"}; + } + + if (doprocessQA) { + histos.add("h_globalTracks_centT0C_before", "before cut;Centrality T0C;mulplicity global tracks", {HistType::kTH2D, {axisCent, axisMult}}); + histos.add("h_PVTracks_centT0C_before", "before cut;Centrality T0C;mulplicity PV tracks", {HistType::kTH2D, {axisCent, axisMult}}); + histos.add("h_globalTracks_PVTracks_before", "before cut;mulplicity PV tracks;mulplicity global tracks", {HistType::kTH2D, {axisMult, axisMult}}); + histos.add("h_globalTracks_multT0A_before", "before cut;mulplicity T0A;mulplicity global tracks", {HistType::kTH2D, {axisT0A, axisMult}}); + histos.add("h_globalTracks_multV0A_before", "before cut;mulplicity V0A;mulplicity global tracks", {HistType::kTH2D, {axisT0A, axisMult}}); + histos.add("h_multV0A_multT0A_before", "before cut;mulplicity T0A;mulplicity V0A", {HistType::kTH2D, {axisT0A, axisT0A}}); + histos.add("h_multT0C_centT0C_before", "before cut;Centrality T0C;mulplicity T0C", {HistType::kTH2D, {axisCent, axisT0C}}); + + histos.add("h_globalTracks_centT0C_after", "after cut;Centrality T0C;mulplicity global tracks", {HistType::kTH2D, {axisCent, axisMult}}); + histos.add("h_PVTracks_centT0C_after", "after cut;Centrality T0C;mulplicity PV tracks", {HistType::kTH2D, {axisCent, axisMult}}); + histos.add("h_globalTracks_PVTracks_after", "after cut;mulplicity PV tracks;mulplicity global tracks", {HistType::kTH2D, {axisMult, axisMult}}); + histos.add("h_globalTracks_multT0A_after", "after cut;mulplicity T0A;mulplicity global tracks", {HistType::kTH2D, {axisT0A, axisMult}}); + histos.add("h_globalTracks_multV0A_after", "after cut;mulplicity V0A;mulplicity global tracks", {HistType::kTH2D, {axisT0A, axisMult}}); + histos.add("h_multV0A_multT0A_after", "after cut;mulplicity T0A;mulplicity V0A", {HistType::kTH2D, {axisT0A, axisT0A}}); + histos.add("h_multT0C_centT0C_after", "after cut;Centrality T0C;mulplicity T0C", {HistType::kTH2D, {axisCent, axisT0C}}); + histos.add("h_centFT0M_centFT0C", "after cut;Centrality T0C;Centrality T0M", {HistType::kTH2D, {axisCent, axisCent}}); + histos.add("h_centFV0A_centFT0C", "after cut;Centrality T0C;Centrality V0A", {HistType::kTH2D, {axisCent, axisCent}}); + + // Add track cuts table + histos.add("hDCAz_before", "DCAz before cuts; DCAz (cm); Pt", {HistType::kTH2D, {axisDCAz, axisPtFiner}}); + histos.add("hDCAxy_before", "DCAxy before cuts; DCAxy (cm); Pt", {HistType::kTH2D, {axisDCAxy, axisPtFiner}}); + histos.add("hTPCNclsPID_before", "hTPCNclsPID_before", {HistType::kTH1D, {axisTpcNcls}}); + histos.add("hTPCNclsFound_before", "hTPCNclsFound_before", {HistType::kTH1D, {axisTpcNcls}}); + histos.add("hTPCCrossedRows_before", "hTPCCrossedRows_before", {HistType::kTH1D, {axisTpcNcls}}); + + histos.add("hDCAz_after", "DCAz after cuts; DCAz (cm); Pt", {HistType::kTH3D, {axisDCAz, axisPtFiner, axisParticle}}); + histos.add("hDCAxy_after", "DCAxy after cuts; DCAxy (cm); Pt", {HistType::kTH3D, {axisDCAxy, axisPtFiner, axisParticle}}); + histos.add("hTPCNclsPID_after", "hTPCNclsPID_after", {HistType::kTH1D, {axisTpcNcls}}); + histos.add("hTPCNclsFound_after", "hTPCNclsFound_after", {HistType::kTH1D, {axisTpcNcls}}); + histos.add("hTPCCrossedRows_after", "hTPCCrossedRows_after", {HistType::kTH1D, {axisTpcNcls}}); + + histos.add("hChi2prTPCcls", "#chi^{2}/cluster for the TPC track segment", {HistType::kTH1D, {axisChi2}}); + histos.add("hChi2prITScls", "#chi^{2}/cluster for the ITS track", {HistType::kTH1D, {axisChi2}}); + histos.add("hITSNclsFound", "Number of found ITS clusters", {HistType::kTH1D, {axisTpcNcls}}); + } + + if (doprocessSameFt0aFt0c || doprocessSameTpcFt0 || doprocessQA) { + histos.add("zVtx", "zVtx", {HistType::kTH1D, {axisVertex}}); + histos.add("Nch", "N_{ch}", {HistType::kTH1D, {axisMult}}); + histos.add("Nch_corrected", "N_{ch} corrected", {HistType::kTH1D, {axisMult}}); + histos.add("Centrality", "Centrality", {HistType::kTH1D, {axisCent}}); + histos.add("CentralityWeighted", "Centrality (weighted)", {HistType::kTH1D, {axisCent}}); + } + + if (doprocessSameTpcFt0 || doprocessQA) { + if (cfgGeneral.cfgQABasic) { + histos.add("Phi", "Phi", {HistType::kTH2D, {axisPhi, axisParticle}}); + histos.add("Eta", "Eta", {HistType::kTH2D, {axisEta, axisParticle}}); + histos.add("EtaCorrected", "EtaCorrected", {HistType::kTH2D, {axisEta, axisParticle}}); + histos.add("pTFiner", "pTFiner", {HistType::kTHnSparseF, {axisPtFiner, axisParticle, axisEventClass}}); + histos.add("pTFinerCorrected", "pTFinerCorrected", {HistType::kTHnSparseF, {axisPtFiner, axisParticle, axisEventClass}}); + } + // PID nSigma histograms + if (cfgPIDConfigs.cfgQANsigma) { + if (!cfgPIDConfigs.cfgUseItsPID) { + histos.add("TofTpcNsigma_beforeCut", "", {HistType::kTHnSparseD, {{axisNsigmaTPC, axisNsigmaTOF, axisPtTrigger, axisParticle}}}); + histos.add("TofTpcNsigma_afterCut", "", {HistType::kTHnSparseD, {{axisNsigmaTPC, axisNsigmaTOF, axisPtTrigger, axisParticle}}}); + } // TPC-TOF PID QA hists + if (cfgPIDConfigs.cfgUseItsPID) { + histos.add("TofItsNsigma_beforeCut", "", {HistType::kTHnSparseD, {{axisNsigmaITS, axisNsigmaTOF, axisPtTrigger, axisParticle}}}); + histos.add("TofItsNsigma_afterCut", "", {HistType::kTHnSparseD, {{axisNsigmaITS, axisNsigmaTOF, axisPtTrigger, axisParticle}}}); + } // ITS-TOF PID QA hists + } // end of PID QA hists + + // PID dEdx histograms + if (cfgPIDConfigs.cfgQAdEdx) { + histos.add("TpcdEdx_ptwise_beforeCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF, axisParticle}}}); + histos.add("ExpTpcdEdx_ptwise_beforeCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF, axisParticle}}}); + histos.add("ExpSigma_ptwise_beforeCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisSigma, axisNsigmaTOF, axisParticle}}}); + + histos.add("TpcdEdx_ptwise_afterCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF, axisParticle}}}); + histos.add("ExpTpcdEdx_ptwise_afterCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF, axisParticle}}}); + histos.add("ExpSigma_ptwise_afterCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisSigma, axisNsigmaTOF, axisParticle}}}); + } + } + if (doprocessSameTpcFt0) { // QA plots are based on TPC tracks, so they are only included in TPC-FT0A process and not in TPC-FT0C process + // TPC-FT0A correlation histograms + if (cfgGeneral.cfgAnalyzeTPCFT0A) { + histos.add("deltaEta_deltaPhi_same_TPC_FT0A", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0a}}); // check to see the delta eta and delta phi distribution + histos.add("deltaEta_deltaPhi_mixed_TPC_FT0A", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0a}}); + histos.add("Assoc_amp_same_TPC_FT0A", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0}}); + histos.add("Assoc_amp_mixed_TPC_FT0A", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0}}); + histos.add("Trig_hist_TPC_FT0A", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisEventClass, axisPtTrigger, axisParticle}}}); + } + // TPC-FT0C correlation histograms + if (cfgGeneral.cfgAnalyzeTPCFT0C) { + histos.add("deltaEta_deltaPhi_same_TPC_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0c}}); // check to see the delta eta and delta phi distribution + histos.add("deltaEta_deltaPhi_mixed_TPC_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0c}}); + histos.add("Assoc_amp_same_TPC_FT0C", "", {HistType::kTH2D, {axisChannelFt0cAxis, axisAmplitudeFt0}}); + histos.add("Assoc_amp_mixed_TPC_FT0C", "", {HistType::kTH2D, {axisChannelFt0cAxis, axisAmplitudeFt0}}); + histos.add("Trig_hist_TPC_FT0C", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisEventClass, axisPtTrigger, axisParticle}}}); + } + + histos.add("FT0Amp", "", {HistType::kTH2F, {axisChID, cfgFITamp}}); + histos.add("FT0AmpCorrect", "", {HistType::kTH2F, {axisChID, cfgFITamp}}); + } + + if (doprocessSameFt0aFt0c) { + histos.add("Phi_FT0A_FT0C", "Phi_FT0A_FT0C", {HistType::kTH1D, {axisPhi}}); + histos.add("Eta_FT0A_FT0C", "Eta_FT0A_FT0C", {HistType::kTH1D, {axisEta}}); + histos.add("EtaCorrected_FT0A_FT0C", "EtaCorrected_FT0A_FT0C", {HistType::kTH1D, {axisEta}}); + histos.add("pTFiner_FT0A_FT0C", "pTFiner_FT0A_FT0C", {HistType::kTH1D, {axisPtFiner}}); + histos.add("pTFinerCorrected_FT0A_FT0C", "pTFinerCorrected_FT0A_FT0C", {HistType::kTH1D, {axisPtFiner}}); + histos.add("deltaEta_deltaPhi_same_FT0A_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaFt0aFt0c}}); // check to see the delta eta and delta phi distribution + histos.add("deltaEta_deltaPhi_mixed_FT0A_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaFt0aFt0c}}); + histos.add("Trig_hist_FT0A_FT0C", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisEventClass}}}); + + histos.add("FT0Amp", "", {HistType::kTH2F, {axisChID, cfgFITamp}}); + histos.add("FT0AmpCorrect", "", {HistType::kTH2F, {axisChID, cfgFITamp}}); + } + + histos.add("eventcount", "bin", {HistType::kTH1F, {{4, 0, 4, "bin"}}}); // histogram to see how many events are in the same and mixed event + histos.add("hTrackCorrection2d", "Correlation table for number of tracks table; uncorrected track; corrected track", {HistType::kTH2D, {axisMult, axisMult}}); + + LOGF(info, "Initializing correlation container"); + + // Initialize containers + std::vector corrAxisTpcFt0a = {{axisSample}, + {axisVertex, "z-vtx (cm)"}, + {axisEventClass}, + {axisPtTrigger, "p_{T} (GeV/c)"}, + {axisParticle, "Particle, 0 = charged, 1 = pion, 2 = kaon, 3 = proton"}, + {axisDeltaPhi, "#Delta#varphi (rad)"}, + {axisDeltaEtaTpcFt0a, "#Delta#eta"}}; + + std::vector corrAxisTpcFt0c = {{axisSample}, + {axisVertex, "z-vtx (cm)"}, + {axisEventClass}, + {axisPtTrigger, "p_{T} (GeV/c)"}, + {axisParticle, "Particle, 0 = charged, 1 = pion, 2 = kaon, 3 = proton"}, + {axisDeltaPhi, "#Delta#varphi (rad)"}, + {axisDeltaEtaTpcFt0c, "#Delta#eta"}}; + + std::vector corrAxisFt0aFt0c = {{axisSample}, + {axisVertex, "z-vtx (cm)"}, + {axisEventClass}, + {axisDeltaPhi, "#Delta#varphi (rad)"}, + {axisDeltaEtaFt0aFt0c, "#Delta#eta"}}; + + if (doprocessSameTpcFt0) { + if (cfgGeneral.cfgAnalyzeTPCFT0A) { + sameTpcFt0a.setObject(new DihadronContainer("sameEvent_TPC_FT0A", "sameEvent_TPC_FT0A", corrAxisTpcFt0a)); + mixedTpcFt0a.setObject(new DihadronContainer("mixedEvent_TPC_FT0A", "mixedEvent_TPC_FT0A", corrAxisTpcFt0a)); + } + if (cfgGeneral.cfgAnalyzeTPCFT0C) { + sameTpcFt0c.setObject(new DihadronContainer("sameEvent_TPC_FT0C", "sameEvent_TPC_FT0C", corrAxisTpcFt0c)); + mixedTpcFt0c.setObject(new DihadronContainer("mixedEvent_TPC_FT0C", "mixedEvent_TPC_FT0C", corrAxisTpcFt0c)); + } + } + + if (doprocessSameFt0aFt0c) { + sameFt0aFt0c.setObject(new DihadronContainer("sameEvent_FT0A_FT0C", "sameEvent_FT0A_FT0C", corrAxisFt0aFt0c)); + mixedFt0aFt0c.setObject(new DihadronContainer("mixedEvent_FT0A_FT0C", "mixedEvent_FT0A_FT0C", corrAxisFt0aFt0c)); + } + + if (!cfgEvSel.cfgEvSelRCTflags.value.empty()) { + rctChecker.init(cfgEvSel.cfgEvSelRCTflags.value); // override initialzation + } + + LOGF(info, "End of init"); + } + + template + double getCentrality(TCollision const& collision) + { + double cent = 0.0; + switch (cfgEvSel.cfgCentEstimator) { + case UseCentFT0C: + cent = collision.centFT0C(); + break; + case UseCentFT0CVariant1: + cent = collision.centFT0CVariant1(); + break; + case UseCentFT0M: + cent = collision.centFT0M(); + break; + case UseCentFV0A: + cent = collision.centFV0A(); + break; + default: + cent = collision.centFT0C(); + } + return cent; + } + + template + bool eventRct(TCollision const& collision, const bool fillCounter) + { + if (!rctChecker(collision)) { + if (fillCounter) { + histos.fill(HIST("hEventCountRct"), 0.5); + } + + return false; + } + if (fillCounter) { + histos.fill(HIST("hEventCountRct"), 1.5); + } + + return true; + } + + template + bool eventSelected(TCollision const& collision, const int mult, const double cent, const bool fillCounter) + { + if (fillCounter) { + histos.fill(HIST("hEventCount"), FilteredEvents); + } + if (!collision.sel8()) { + return false; + } + if (fillCounter) { + histos.fill(HIST("hEventCount"), AfterSel8); + } + + if (eventCuts[UseNoTimeFrameBorder][EvCut1] && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + if (fillCounter && eventCuts[UseNoTimeFrameBorder][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoTimeFrameBorder); + } + + if (eventCuts[UseNoITSROFrameBorder][EvCut1] && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + if (fillCounter && eventCuts[UseNoITSROFrameBorder][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoITSROFrameBorder); + } + + if (eventCuts[UseNoSameBunchPileup][EvCut1] && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + // rejects collisions which are associated with the same "found-by-T0" bunch crossing + // https://indico.cern.ch/event/1396220/#1-event-selection-with-its-rof + return false; + } + if (fillCounter && eventCuts[UseNoSameBunchPileup][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoSameBunchPileup); + } + + if (eventCuts[UseGoodZvtxFT0vsPV][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference + // use this cut at low multiplicities with caution + return false; + } + if (fillCounter && eventCuts[UseGoodZvtxFT0vsPV][EvCut1]) { + histos.fill(HIST("hEventCount"), UseGoodZvtxFT0vsPV); + } + + if (eventCuts[UseNoCollInTimeRangeStandard][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + // no collisions in specified time range + return false; + } + + if (fillCounter && eventCuts[UseNoCollInTimeRangeStandard][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoCollInTimeRangeStandard); + } + + if (eventCuts[UseGoodITSLayersAll][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + // from Jan 9 2025 AOT meeting + // cut time intervals with dead ITS staves + return false; + } + + if (fillCounter && eventCuts[UseGoodITSLayersAll][EvCut1]) { + histos.fill(HIST("hEventCount"), UseGoodITSLayersAll); + } + + if (eventCuts[UseGoodITSLayer0123][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { + return false; + } + if (fillCounter && eventCuts[UseGoodITSLayer0123][EvCut1]) { + histos.fill(HIST("hEventCount"), UseGoodITSLayer0123); + } + + if (eventCuts[UseNoCollInRofStandard][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + // no other collisions in this Readout Frame with per-collision multiplicity above threshold + return false; + } + + if (fillCounter && eventCuts[UseNoCollInRofStandard][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoCollInRofStandard); + } + + if (eventCuts[UseNoHighMultCollInPrevRof][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + // veto an event if FT0C amplitude in previous ITS ROF is above threshold + return false; + } + if (fillCounter && eventCuts[UseNoHighMultCollInPrevRof][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoHighMultCollInPrevRof); + } + + auto multNTracksPV = collision.multNTracksPV(); + auto occupancy = collision.trackOccupancyInTimeRange(); + + if (eventCuts[UseOccupancy][EvCut1] && (occupancy < cfgEvSel.cfgCutOccupancyLow || occupancy > cfgEvSel.cfgCutOccupancyHigh)) { + return false; + } + if (fillCounter && eventCuts[UseOccupancy][EvCut1]) { + histos.fill(HIST("hEventCount"), UseOccupancy); + } + + if (eventCuts[UseMultCorrCut][EvCut1]) { + if (cfgFuncParas.cfgMultPVT0CCutEnabled) { + if (multNTracksPV < cfgFuncParas.fMultPVT0CCutLow->Eval(cent)) { + return false; + } + if (multNTracksPV > cfgFuncParas.fMultPVT0CCutHigh->Eval(cent)) { + return false; + } + } + if (cfgFuncParas.cfgMultT0CCutEnabled) { + if (mult < cfgFuncParas.fMultT0CCutLow->Eval(cent)) { + return false; + } + if (mult > cfgFuncParas.fMultT0CCutHigh->Eval(cent)) { + return false; + } + } + if (cfgFuncParas.cfgMultGlobalPVCutEnabled) { + if (mult < cfgFuncParas.fMultGlobalPVCutLow->Eval(multNTracksPV)) { + return false; + } + if (mult > cfgFuncParas.fMultGlobalPVCutHigh->Eval(multNTracksPV)) { + return false; + } + } + if (cfgFuncParas.cfgMultMultV0ACutEnabled) { + if (collision.multFV0A() < cfgFuncParas.fMultMultV0ACutLow->Eval(mult)) { + return false; + } + if (collision.multFV0A() > cfgFuncParas.fMultMultV0ACutHigh->Eval(mult)) { + return false; + } + } + } + + if (fillCounter && eventCuts[UseMultCorrCut][EvCut1]) { + histos.fill(HIST("hEventCount"), UseMultCorrCut); + } + + // V0A T0A 5 sigma cut + if (eventCuts[UseT0AV0ACut][EvCut1] && (std::fabs(collision.multFV0A() - cfgFuncParas.fT0AV0AMean->Eval(collision.multFT0A())) > cfgFuncParas.cfgV0AT0Acut * cfgFuncParas.fT0AV0ASigma->Eval(collision.multFT0A()))) { + return false; + } + if (fillCounter && eventCuts[UseT0AV0ACut][EvCut1]) { + histos.fill(HIST("hEventCount"), UseT0AV0ACut); + } + + return true; + } + + template + void eventSelectedIndividually(TCollision const& collision) + { + histos.fill(HIST("hPassedEventSelection"), FilteredEvents); + + if (collision.sel8()) { + histos.fill(HIST("hPassedEventSelection"), AfterSel8); + } + + if (collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + histos.fill(HIST("hPassedEventSelection"), UseNoTimeFrameBorder); + } + + if (collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + histos.fill(HIST("hPassedEventSelection"), UseNoITSROFrameBorder); + } + + if (collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + histos.fill(HIST("hPassedEventSelection"), UseNoSameBunchPileup); + } + + if (collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + histos.fill(HIST("hPassedEventSelection"), UseGoodZvtxFT0vsPV); + } + + if (collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + histos.fill(HIST("hPassedEventSelection"), UseNoCollInTimeRangeStandard); + } + + if (collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + histos.fill(HIST("hPassedEventSelection"), UseGoodITSLayersAll); + } + + if (collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { + histos.fill(HIST("hPassedEventSelection"), UseGoodITSLayer0123); + } + + if (collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + histos.fill(HIST("hPassedEventSelection"), UseNoCollInRofStandard); + } + + if (collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + histos.fill(HIST("hPassedEventSelection"), UseNoHighMultCollInPrevRof); + } + + auto occupancy = collision.trackOccupancyInTimeRange(); + if (eventCuts[UseOccupancy][EvCut1] && (occupancy > cfgEvSel.cfgCutOccupancyLow && occupancy < cfgEvSel.cfgCutOccupancyHigh)) { + histos.fill(HIST("hPassedEventSelection"), UseOccupancy); + } + } + + double getPhiFT0(uint64_t chno, int i) + { + // offsetFT0[0]: FT0A, offsetFT0[1]: FT0C + if (i > 1 || i < 0) { + LOGF(fatal, "kFIT Index %d out of range", i); + } + + ft0Det.calculateChannelCenter(); + auto chPos = ft0Det.getChannelCenter(chno); + return RecoDecay::phi(chPos.X() + (*offsetFT0)[i].getX(), chPos.Y() + (*offsetFT0)[i].getY()); + } + + double getEtaFT0(uint64_t chno, int i) + { + // offsetFT0[0]: FT0A, offsetFT0[1]: FT0C + if (i > 1 || i < 0) { + LOGF(fatal, "kFIT Index %d out of range", i); + } + ft0Det.calculateChannelCenter(); + auto chPos = ft0Det.getChannelCenter(chno); + auto x = chPos.X() + (*offsetFT0)[i].getX(); + auto y = chPos.Y() + (*offsetFT0)[i].getY(); + auto z = chPos.Z() + (*offsetFT0)[i].getZ(); + if (chno >= Ft0IndexA) { + z = -z; + } + auto r = std::sqrt(x * x + y * y); + auto theta = std::atan2(r, z); + return -std::log(std::tan(0.5 * theta)); + } + + void loadAlignParam(uint64_t timestamp) + { + offsetFT0 = ccdb->getForTimeStamp>("FT0/Calib/Align", timestamp); + if (offsetFT0 == nullptr) { + LOGF(fatal, "Could not load FT0/Calib/Align for timestamp %d", timestamp); + } + } + + template + bool trackSelected(TTrack const& track) + { + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].empty() && std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxy->Eval(track.pt())) { + return false; + } + + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].empty()) { + if (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAz->Eval(track.pt())) { + return false; + } + } else { + if (std::fabs(track.dcaZ()) > trackCuts[TrkCutDCAz][UseGenTrkCuts]) { + return false; + } + } + + return ((track.pt() > trackCuts[TrkCutPtMin][UseGenTrkCuts]) && (track.pt() < trackCuts[TrkCutPtMax][UseGenTrkCuts]) && (std::abs(track.eta()) < trackCuts[TrkCutEtaCut][UseGenTrkCuts]) && (track.tpcChi2NCl() < trackCuts[TrkCutChi2PrTpcCls][UseGenTrkCuts]) && (track.tpcNClsFound() >= trackCuts[TrkCutTpcCluster][UseGenTrkCuts]) && (track.tpcNClsCrossedRows() >= trackCuts[TrkCutTpcCrossedRows][UseGenTrkCuts]) && (track.itsNCls() >= trackCuts[TrkCutItsCluster][UseGenTrkCuts])); + } + + template + bool trackSelectedForNch(TTrack const& track) + { + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].empty() && (std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxyForNch->Eval(track.pt()))) { + return false; + } + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].empty()) { + if (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAzForNch->Eval(track.pt())) { + return false; + } + } else { + if (std::fabs(track.dcaZ()) > trackCuts[TrkCutDCAz][UseNchSelCuts]) { + return false; + } + } + return ((track.pt() > trackCuts[TrkCutPtMin][UseNchSelCuts]) && (track.pt() < trackCuts[TrkCutPtMax][UseNchSelCuts]) && (std::abs(track.eta()) < trackCuts[TrkCutEtaCut][UseNchSelCuts]) && (track.tpcChi2NCl() < trackCuts[TrkCutChi2PrTpcCls][UseNchSelCuts]) && (track.tpcNClsFound() >= trackCuts[TrkCutTpcCluster][UseNchSelCuts]) && (track.tpcNClsCrossedRows() >= trackCuts[TrkCutTpcCrossedRows][UseNchSelCuts]) && (track.itsNCls() >= trackCuts[TrkCutItsCluster][UseNchSelCuts])); + } + + void loadGain(aod::BCsWithTimestamps::iterator const& bc) + { + cstFT0RelGain.clear(); + std::string fullPath; + + auto timestamp = bc.timestamp(); + constexpr int ChannelsFT0 = 208; + if (cfgGeneral.cfgCorrLevel == 0) { + for (auto i{0u}; i < ChannelsFT0; i++) { + cstFT0RelGain.push_back(1.); + } + } else { + fullPath = cfgGeneral.cfgGainEqPath; + fullPath += "/FT0"; + const auto objft0Gain = ccdb->getForTimeStamp>(fullPath, timestamp); + if (!objft0Gain) { + for (auto i{0u}; i < ChannelsFT0; i++) { + cstFT0RelGain.push_back(1.); + } + } else { + cstFT0RelGain = *(objft0Gain); + } + } + } + + template + void getChannel(TFT0s const& ft0, std::size_t const& iCh, int& id, float& ampl, int fitType, int system) + { + if (fitType == IndexFT0C) { + id = ft0.channelC()[iCh]; + id = id + Ft0IndexA; + ampl = ft0.amplitudeC()[iCh]; + if (system == SameEvent) { + histos.fill(HIST("FT0Amp"), id, ampl); + } + ampl = ampl / cstFT0RelGain[id]; + if (system == SameEvent) { + histos.fill(HIST("FT0AmpCorrect"), id, ampl); + } + } else if (fitType == IndexFT0A) { + id = ft0.channelA()[iCh]; + ampl = ft0.amplitudeA()[iCh]; + if (system == SameEvent) { + histos.fill(HIST("FT0Amp"), id, ampl); + } + ampl = ampl / cstFT0RelGain[id]; + if (system == SameEvent) { + histos.fill(HIST("FT0AmpCorrect"), id, ampl); + } + } else { + LOGF(fatal, "Cor Index %d out of range", fitType); + } + } + + template + int getNsigmaPID(const TTrack& track) + { + // Computing Nsigma arrays for pion, kaon, and protons + std::array nSigmaTPC = {track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; + std::array nSigmaTOF = {track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr()}; + std::array nSigmaITS = {itsResponse.nSigmaITS(track), itsResponse.nSigmaITS(track), itsResponse.nSigmaITS(track)}; + int pid = -1; // -1 = not identified, 1 = pion, 2 = kaon, 3 = proton + + std::array nSigmaToUse = cfgPIDConfigs.cfgUseItsPID ? nSigmaITS : nSigmaTPC; // Choose which nSigma to use: TPC or ITS + int useIndexDetector = UseTPC; + useIndexDetector = cfgPIDConfigs.cfgUseItsPID ? UseITS : UseTPC; // Choose which nSigma to use: TPC or ITS + + bool isPion = false; + bool isKaon = false; + bool isProton = false; + bool isDetectedPion = false; + bool isDetectedKaon = false; + bool isDetectedProton = false; + bool isTofPion = false; + bool isTofKaon = false; + bool isTofProton = false; + + isDetectedPion = nSigmaToUse[IndexPionUp] < nSigmaVals[IndexPionUp][useIndexDetector] && nSigmaToUse[IndexPionUp] > nSigmaVals[IndexPionLow][useIndexDetector]; + isDetectedKaon = nSigmaToUse[IndexKaonUp] < nSigmaVals[IndexKaonUp][useIndexDetector] && nSigmaToUse[IndexKaonUp] > nSigmaVals[IndexKaonLow][useIndexDetector]; + isDetectedProton = nSigmaToUse[IndexProtonUp] < nSigmaVals[IndexProtonUp][useIndexDetector] && nSigmaToUse[IndexProtonUp] > nSigmaVals[IndexProtonLow][useIndexDetector]; + + isTofPion = nSigmaTOF[IndexPionUp] < nSigmaVals[IndexPionUp][UseTOF] && nSigmaTOF[IndexPionUp] > nSigmaVals[IndexPionLow][UseTOF]; + isTofKaon = nSigmaTOF[IndexKaonUp] < nSigmaVals[IndexKaonUp][UseTOF] && nSigmaTOF[IndexKaonUp] > nSigmaVals[IndexKaonLow][UseTOF]; + isTofProton = nSigmaTOF[IndexProtonUp] < nSigmaVals[IndexProtonUp][UseTOF] && nSigmaTOF[IndexProtonUp] > nSigmaVals[IndexProtonLow][UseTOF]; + + if (track.pt() > cfgPIDConfigs.cfgTofPtCut && !track.hasTOF()) { + return -1; + } + if (track.pt() > cfgPIDConfigs.cfgTofPtCut && track.hasTOF()) { + isPion = isTofPion && isDetectedPion; + isKaon = isTofKaon && isDetectedKaon; + isProton = isTofProton && isDetectedProton; + } else { + isPion = isDetectedPion; + isKaon = isDetectedKaon; + isProton = isDetectedProton; + } + + if (cfgPIDConfigs.cfgPIDUseRejection && ((isPion && isKaon) || (isPion && isProton) || (isKaon && isProton))) { + return -1; // more than one particle satisfy the criteria + } + + if (isPion) { + pid = UsePions; + } else if (isKaon) { + pid = UseKaons; + } else if (isProton) { + pid = UseProtons; + } else { + return -1; // no particle satisfies the criteria + } + + return pid; // -1 = not identified, 1 = pion, 2 = kaon, 3 = proton + } + + void loadCorrection(uint64_t timestamp) + { + if (correctionsLoaded) { + return; + } + if (!cfgEffWeights.cfgEfficiency.value.empty()) { + if (cfgEffWeights.cfgLocalEfficiency) { + TFile* fEfficiencyTrigger = nullptr; + fEfficiencyTrigger = TFile::Open(cfgEffWeights.cfgEfficiency.value.c_str(), "READ"); + mEfficiency = dynamic_cast(fEfficiencyTrigger->Get("ccdb_object")); + + } else { + mEfficiency = ccdb->getForTimeStamp(cfgEffWeights.cfgEfficiency, timestamp); + } + if (mEfficiency == nullptr) { + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEffWeights.cfgEfficiency.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEffWeights.cfgEfficiency.value.c_str(), static_cast(mEfficiency)); + } + if (!cfgEffWeights.cfgEfficiencyNch.value.empty()) { + if (cfgEffWeights.cfgLocalEfficiencyNch) { + TFile* fEfficiencyTrigger = nullptr; + fEfficiencyTrigger = TFile::Open(cfgEffWeights.cfgEfficiencyNch.value.c_str(), "READ"); + mEfficiencyNch = dynamic_cast(fEfficiencyTrigger->Get("ccdb_object")); + + } else { + mEfficiencyNch = ccdb->getForTimeStamp(cfgEffWeights.cfgEfficiencyNch, timestamp); + } + if (!mEfficiencyNch) { + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEffWeights.cfgEfficiencyNch.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEffWeights.cfgEfficiencyNch.value.c_str(), static_cast(mEfficiencyNch)); + } + if (!cfgEffWeights.cfgCentralityWeight.value.empty()) { + mCentralityWeight = ccdb->getForTimeStamp(cfgEffWeights.cfgCentralityWeight, timestamp); + if (mCentralityWeight == nullptr) { + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEffWeights.cfgCentralityWeight.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEffWeights.cfgCentralityWeight.value.c_str(), static_cast(mCentralityWeight)); + } + correctionsLoaded = true; + } + + bool getEfficiencyCorrectionNch(float& weightNch, float pt) + { + float effNch = 1.; + if (mEfficiencyNch) { + int ptBin = mEfficiencyNch->FindBin(pt); + effNch = mEfficiencyNch->GetBinContent(ptBin); + } else { + effNch = 1.0; + } + if (effNch == 0) { + return false; + } + weightNch = 1. / effNch; + return true; + } + + bool getEfficiencyCorrection(float& weight, float pt, float eta, float vertex) + { + float eff = 1.; + if (mEfficiency) { + int etaBin = mEfficiency->GetXaxis()->FindBin(eta); // use the eta bin corresponding to eta=0 for the trigger particle efficiency + int ptBin = mEfficiency->GetYaxis()->FindBin(pt); + int vertexBin = mEfficiency->GetZaxis()->FindBin(vertex); // use the vertex bin corresponding to z=0 for the trigger particle efficiency + eff = mEfficiency->GetBinContent(etaBin, ptBin, vertexBin); + } else { + eff = 1.0; + } + if (eff == 0) { + return false; + } + weight = 1. / eff; + return true; + } + + bool getCentralityWeight(float& weightCent, const double centrality) + { + float weight = 1.; + if (mCentralityWeight) { + weight = mCentralityWeight->GetBinContent(mCentralityWeight->FindBin(centrality)); + } else { + weight = 1.0; + } + if (weight == 0) { + return false; + } + weightCent = weight; + return true; + } + + template + void nchCounter(const TTracks& tracks, double& multiplicity, bool fillHistogram) // function to count the number of tracks in the event and fill the histogram + { + double nTracksCorrected = 0; + double nTracksUncorrected = 0; + float weightNch = 1.0f; + for (auto const& track : tracks) { + if (!trackSelectedForNch(track)) { + continue; + } + + if (!getEfficiencyCorrectionNch(weightNch, track.pt())) { + continue; + } + + nTracksUncorrected += 1.0; + nTracksCorrected += weightNch; + } + if (fillHistogram) { + histos.fill(HIST("hTrackCorrection2d"), nTracksUncorrected, nTracksCorrected); + } + multiplicity = nTracksCorrected; + } + + template + void fillYieldFt0aFt0c(const TCollision& collision, const TTracks& tracks) // function to fill the yield and etaphi histograms. + { + + float weff1 = 1.0; + float zvtx = collision.posZ(); + + for (auto const& track1 : tracks) { + + if (!trackSelected(track1)) { + continue; + } + + if (!getEfficiencyCorrection(weff1, track1.pt(), track1.eta(), zvtx)) { + continue; + } + + histos.fill(HIST("Phi_FT0A_FT0C"), RecoDecay::constrainAngle(track1.phi(), 0.0)); + histos.fill(HIST("Eta_FT0A_FT0C"), track1.eta()); + histos.fill(HIST("EtaCorrected_FT0A_FT0C"), track1.eta(), weff1); + histos.fill(HIST("pTFiner_FT0A_FT0C"), track1.pt()); + histos.fill(HIST("pTFinerCorrected_FT0A_FT0C"), track1.pt(), weff1); + } + } + + template + void fillTrackQA(TTrack const& track, int pid, double eventClass, Stage stage, float weff) // function to fill the QA after Nsigma selection + { + if (cfgGeneral.cfgQABasic) { + if (stage == Before && pid == UseCharged) { + histos.fill(HIST("Phi"), RecoDecay::constrainAngle(track.phi(), 0.0), UseCharged); + histos.fill(HIST("Eta"), track.eta(), UseCharged); + histos.fill(HIST("EtaCorrected"), track.eta(), UseCharged, weff); + histos.fill(HIST("pTFiner"), track.pt(), UseCharged, eventClass); + histos.fill(HIST("pTFinerCorrected"), track.pt(), UseCharged, eventClass, weff); + + } else if (stage == After && pid > UseCharged) { + histos.fill(HIST("Phi"), RecoDecay::constrainAngle(track.phi(), 0.0), pid); + histos.fill(HIST("Eta"), track.eta(), pid); + histos.fill(HIST("EtaCorrected"), track.eta(), pid, weff); + histos.fill(HIST("pTFiner"), track.pt(), pid, eventClass); + histos.fill(HIST("pTFinerCorrected"), track.pt(), pid, eventClass, weff); + } + } + + if (pid > UseCharged) { + const bool useITS = cfgPIDConfigs.cfgUseItsPID; + double tpcNSigma = 0.0; + double tpcExpSigma = 0.0; + double tofNSigma = 0.0; + double itsNSigma = 0.0; + switch (pid) { + case UsePions: // For Pions + tofNSigma = track.tofNSigmaPi(); + if (!useITS) { + tpcNSigma = track.tpcNSigmaPi(); + tpcExpSigma = track.tpcExpSigmaPi(); + } else { + itsNSigma = itsResponse.nSigmaITS(track); + } + break; + case UseKaons: // For Kaons + tofNSigma = track.tofNSigmaKa(); + if (!useITS) { + tpcNSigma = track.tpcNSigmaKa(); + tpcExpSigma = track.tpcExpSigmaKa(); + } else { + itsNSigma = itsResponse.nSigmaITS(track); + } + break; + + case UseProtons: // For Protons + tofNSigma = track.tofNSigmaPr(); + if (!useITS) { + tpcNSigma = track.tpcNSigmaPr(); + tpcExpSigma = track.tpcExpSigmaPr(); + } else { + itsNSigma = itsResponse.nSigmaITS(track); + } + break; + default: + return; + } + + if (stage == Before) { + if (!useITS) { + histos.fill(HIST("TofTpcNsigma_beforeCut"), tpcNSigma, tofNSigma, track.pt(), pid); + } else { + histos.fill(HIST("TofItsNsigma_beforeCut"), itsNSigma, tofNSigma, track.pt(), pid); + } + } else if (stage == After) { + if (!useITS) { + histos.fill(HIST("TofTpcNsigma_afterCut"), tpcNSigma, tofNSigma, track.pt(), pid); + } else { + histos.fill(HIST("TofItsNsigma_afterCut"), itsNSigma, tofNSigma, track.pt(), pid); + } + } + + if (cfgPIDConfigs.cfgQAdEdx && !useITS) { + double tpcExpSignal = track.tpcSignal() - (tpcNSigma * tpcExpSigma); + + if (stage == Before) { + histos.fill(HIST("TpcdEdx_ptwise_beforeCut"), track.pt(), track.tpcSignal(), tofNSigma, pid); + histos.fill(HIST("ExpTpcdEdx_ptwise_beforeCut"), track.pt(), tpcExpSignal, tofNSigma, pid); + histos.fill(HIST("ExpSigma_ptwise_beforeCut"), track.pt(), tpcExpSigma, tofNSigma, pid); + } else if (stage == After) { + histos.fill(HIST("TpcdEdx_ptwise_afterCut"), track.pt(), track.tpcSignal(), tofNSigma, pid); + histos.fill(HIST("ExpTpcdEdx_ptwise_afterCut"), track.pt(), tpcExpSignal, tofNSigma, pid); + histos.fill(HIST("ExpSigma_ptwise_afterCut"), track.pt(), tpcExpSigma, tofNSigma, pid); + } + } + } // if not a charged particle + } + + template + void fillCorrelationsTPCFT0(const TTracks& tracks1, TFT0s const& ft0, float posZ, int system, double eventClass, int corType, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + { + int fSampleIndex = static_cast(fRandom.Uniform(0.0, cfgGeneral.cfgSampleSize)); + + float triggerWeight = 1.0f; + // loop over all tracks + for (auto const& track1 : tracks1) { + + if (!trackSelected(track1)) { + continue; + } + if (!getEfficiencyCorrection(triggerWeight, track1.pt(), track1.eta(), posZ)) { // Keep efficiency string empty to disable, efficiency is not used for tpc-ft0 correlations + continue; + } + + if (system == SameEvent) { + fillTrackQA(track1, UseCharged, eventClass, Before, triggerWeight); + fillTrackQA(track1, UsePions, eventClass, Before, triggerWeight); + fillTrackQA(track1, UseKaons, eventClass, Before, triggerWeight); + fillTrackQA(track1, UseProtons, eventClass, Before, triggerWeight); + } + + int pidIndex = getNsigmaPID(track1); + + if (system == SameEvent) { + if (pidIndex > UseCharged) { + fillTrackQA(track1, pidIndex, eventClass, After, triggerWeight); + } + if (corType == IndexFT0C) { + histos.fill(HIST("Trig_hist_TPC_FT0C"), fSampleIndex, posZ, eventClass, track1.pt(), UseCharged, eventWeight * triggerWeight); + if (pidIndex > UseCharged) { + histos.fill(HIST("Trig_hist_TPC_FT0C"), fSampleIndex, posZ, eventClass, track1.pt(), pidIndex, eventWeight * triggerWeight); + } + } else if (corType == IndexFT0A) { + histos.fill(HIST("Trig_hist_TPC_FT0A"), fSampleIndex, posZ, eventClass, track1.pt(), UseCharged, eventWeight * triggerWeight); + if (pidIndex > UseCharged) { + histos.fill(HIST("Trig_hist_TPC_FT0A"), fSampleIndex, posZ, eventClass, track1.pt(), pidIndex, eventWeight * triggerWeight); + } + } + } + + std::size_t channelSize = 0; + if (corType == IndexFT0C) { + channelSize = ft0.channelC().size(); + } else if (corType == IndexFT0A) { + channelSize = ft0.channelA().size(); + } else { + LOGF(fatal, "Cor Index %d out of range", corType); + } + for (std::size_t iCh = 0; iCh < channelSize; iCh++) { + int chanelid = 0; + float ampl = 0.; + getChannel(ft0, iCh, chanelid, ampl, corType, system); + + auto phi = getPhiFT0(chanelid, corType); + auto eta = getEtaFT0(chanelid, corType); + + float deltaPhi = RecoDecay::constrainAngle(track1.phi() - phi, -PIHalf); + float deltaEta = track1.eta() - eta; + // fill the right sparse and histograms + if (system == SameEvent) { + if (corType == IndexFT0A) { + if (cfgGeneral.cfgQABasic) { + histos.fill(HIST("Assoc_amp_same_TPC_FT0A"), chanelid, ampl); + histos.fill(HIST("deltaEta_deltaPhi_same_TPC_FT0A"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } + sameTpcFt0a->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), UseCharged, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + if (pidIndex > UseCharged) { + sameTpcFt0a->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), pidIndex, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } + } else if (corType == IndexFT0C) { + if (cfgGeneral.cfgQABasic) { + histos.fill(HIST("Assoc_amp_same_TPC_FT0C"), chanelid, ampl); + histos.fill(HIST("deltaEta_deltaPhi_same_TPC_FT0C"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } + sameTpcFt0c->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), UseCharged, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + if (pidIndex > UseCharged) { + sameTpcFt0c->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), pidIndex, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } + } + } else if (system == MixedEvent) { + if (corType == IndexFT0A) { + if (cfgGeneral.cfgQABasic) { + histos.fill(HIST("Assoc_amp_mixed_TPC_FT0A"), chanelid, ampl); + histos.fill(HIST("deltaEta_deltaPhi_mixed_TPC_FT0A"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } + mixedTpcFt0a->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), UseCharged, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + if (pidIndex > UseCharged) { + mixedTpcFt0a->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), pidIndex, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } + } else if (corType == IndexFT0C) { + if (cfgGeneral.cfgQABasic) { + histos.fill(HIST("Assoc_amp_mixed_TPC_FT0C"), chanelid, ampl); + histos.fill(HIST("deltaEta_deltaPhi_mixed_TPC_FT0C"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } + mixedTpcFt0c->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), UseCharged, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + if (pidIndex > UseCharged) { + mixedTpcFt0c->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), pidIndex, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } + } + } + } + } + } + + template + void fillCorrelationsFT0AFT0C(TFT0s const& ft0Col1, TFT0s const& ft0Col2, float posZ, int system, double eventClass, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + { + int fSampleIndex = static_cast(fRandom.Uniform(0.0, cfgGeneral.cfgSampleSize)); + + float triggerWeight = 1.0f; + std::size_t channelASize = ft0Col1.channelA().size(); + std::size_t channelCSize = ft0Col2.channelC().size(); + // loop over all tracks + for (std::size_t iChA = 0; iChA < channelASize; iChA++) { + + int chanelAid = 0; + float amplA = 0.; + getChannel(ft0Col1, iChA, chanelAid, amplA, IndexFT0A, system); + auto phiA = getPhiFT0(chanelAid, IndexFT0A); + auto etaA = getEtaFT0(chanelAid, IndexFT0A); + + if (system == SameEvent) { + histos.fill(HIST("Trig_hist_FT0A_FT0C"), fSampleIndex, posZ, eventClass, eventWeight * amplA); + } + + for (std::size_t iChC = 0; iChC < channelCSize; iChC++) { + int chanelCid = 0; + float amplC = 0.; + getChannel(ft0Col2, iChC, chanelCid, amplC, IndexFT0C, system); + auto phiC = getPhiFT0(chanelCid, IndexFT0C); + auto etaC = getEtaFT0(chanelCid, IndexFT0C); + float deltaPhi = RecoDecay::constrainAngle(phiA - phiC, -PIHalf); + float deltaEta = etaA - etaC; + + // fill the right sparse and histograms + if (system == SameEvent) { + if (cfgGeneral.cfgQABasic) { + histos.fill(HIST("deltaEta_deltaPhi_same_FT0A_FT0C"), deltaPhi, deltaEta, amplA * amplC * eventWeight * triggerWeight); + } + sameFt0aFt0c->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, deltaPhi, deltaEta, amplA * amplC * eventWeight * triggerWeight); + } else if (system == MixedEvent) { + if (cfgGeneral.cfgQABasic) { + histos.fill(HIST("deltaEta_deltaPhi_mixed_FT0A_FT0C"), deltaPhi, deltaEta, amplA * amplC * eventWeight * triggerWeight); + } + mixedFt0aFt0c->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, deltaPhi, deltaEta, amplA * amplC * eventWeight * triggerWeight); + } + } + } + } + + bool isGoodRun(int runNumber) + { + for (const auto& excludedRun : cfgGeneral.cfgRunRemoveList.value) { + if (runNumber == excludedRun) { + return false; + } + } + + return true; + } + + void processSameTpcFt0(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&) + { + if (cfgGeneral.cfgQABasic) { + eventSelectedIndividually(collision); + } + + if (!eventRct(collision, true)) { + return; + } + + auto bc = collision.bc_as(); + + int currentRunNumber = bc.runNumber(); + if (!cfgGeneral.cfgRunRemoveList.value.empty()) { + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number + return; + } + } + + auto cent = getCentrality(collision); + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), cent, true)) { + return; + } + + if (!collision.has_foundFT0()) { + return; + } + + histos.fill(HIST("hEventCount"), HaveFT0Cut); + + loadAlignParam(bc.timestamp()); + loadGain(bc); + loadCorrection(bc.timestamp()); + float weightCent = 1.0f; + + if (!getCentralityWeight(weightCent, cent)) { + return; // or continue in a loop + } + + histos.fill(HIST("Centrality"), cent); + histos.fill(HIST("CentralityWeighted"), cent, weightCent); + + histos.fill(HIST("zVtx"), collision.posZ()); + + auto multiplicity = static_cast(tracks.size()); + + if (cfgGeneral.cfgQABasic) { + histos.fill(HIST("Nch"), multiplicity); + } + + if (cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks, multiplicity, false); + } + + if (cfgGeneral.cfgQABasic) { + histos.fill(HIST("Nch_corrected"), multiplicity); + } + + if (cfgEvSel.cfgSelCollByNch && (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations)) { + return; + } + if (!cfgEvSel.cfgSelCollByNch && (cent > cfgEvSel.cfgMaxCentForCorrelations || cent < cfgEvSel.cfgMinCentForCorrelations)) { + return; + } + + histos.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin + + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity : cent; + + const auto& ft0 = collision.foundFT0(); + if (cfgGeneral.cfgAnalyzeTPCFT0A) { + fillCorrelationsTPCFT0(tracks, ft0, collision.posZ(), SameEvent, eventClass, IndexFT0A, weightCent); + } + if (cfgGeneral.cfgAnalyzeTPCFT0C) { + fillCorrelationsTPCFT0(tracks, ft0, collision.posZ(), SameEvent, eventClass, IndexFT0C, weightCent); + } + } + PROCESS_SWITCH(PidLongRange, processSameTpcFt0, "Process same event for TPC-FT0 correlation", false); + + void processMixedTpcFt0(FilteredCollisions const& collisions, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&) + { + auto getTracksSize = [&tracks, this](FilteredCollisions::iterator const& collision) { + auto associatedTracks = tracks.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); + auto mult = associatedTracks.size(); + return mult; + }; + + using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; + + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; + + auto tracksTuple = std::make_tuple(tracks, tracks); + Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracks1, collision2, tracks2] = *it; + + if (!collision1.sel8() || !collision2.sel8()) { // Doing this at the top of mixed events loop to reduce computation (This cut is also there inside eventSelected) + continue; + } + + if (!eventRct(collision1, false) || !eventRct(collision2, false)) { + continue; + } + + auto cent1 = getCentrality(collision1); + auto cent2 = getCentrality(collision2); + + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), cent1, false)) { + continue; + } + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), cent2, false)) { + continue; + } + + if (!(collision1.has_foundFT0() && collision2.has_foundFT0())) { + continue; + } + + auto bc = collision1.bc_as(); + int currentRunNumber = bc.runNumber(); + if (!cfgGeneral.cfgRunRemoveList.value.empty()) { + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number + continue; + } + } + + loadAlignParam(bc.timestamp()); + loadCorrection(bc.timestamp()); + + auto multiplicity1 = static_cast(tracks1.size()); + auto multiplicity2 = static_cast(tracks2.size()); + + if (cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks1, multiplicity1, false); + if (cfgEvSel.cfgUseCol2ForMixedEvSel) { + nchCounter(tracks2, multiplicity2, false); + } + } + + if (cfgEvSel.cfgSelCollByNch && (multiplicity1 > cfgEvSel.cfgMaxMultForCorrelations || multiplicity1 < cfgEvSel.cfgMinMultForCorrelations)) { + continue; + } + if (cfgEvSel.cfgSelCollByNch && cfgEvSel.cfgUseCol2ForMixedEvSel && (multiplicity2 > cfgEvSel.cfgMaxMultForCorrelations || multiplicity2 < cfgEvSel.cfgMinMultForCorrelations)) { + continue; + } + if (!cfgEvSel.cfgSelCollByNch && (cent1 > cfgEvSel.cfgMaxCentForCorrelations || cent1 < cfgEvSel.cfgMinCentForCorrelations)) { + continue; + } + if (!cfgEvSel.cfgSelCollByNch && cfgEvSel.cfgUseCol2ForMixedEvSel && (cent2 > cfgEvSel.cfgMaxCentForCorrelations || cent2 < cfgEvSel.cfgMinCentForCorrelations)) { + continue; + } + + histos.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity1 : cent1; + + float eventWeight = 1.0f; + + if (cfgEffWeights.cfgUseEventWeights) { + eventWeight = 1.0f / it.currentWindowNeighbours(); + } + float weightCent = 1.0f; + getCentralityWeight(weightCent, cent1); + + const auto& ft0 = collision2.foundFT0(); + if (cfgGeneral.cfgAnalyzeTPCFT0A) { + fillCorrelationsTPCFT0(tracks1, ft0, collision1.posZ(), MixedEvent, eventClass, IndexFT0A, eventWeight * weightCent); + } + if (cfgGeneral.cfgAnalyzeTPCFT0C) { + fillCorrelationsTPCFT0(tracks1, ft0, collision1.posZ(), MixedEvent, eventClass, IndexFT0C, eventWeight * weightCent); + } + } + } + PROCESS_SWITCH(PidLongRange, processMixedTpcFt0, "Process mixed events for TPC-FT0A correlation", false); + + void processSameFt0aFt0c(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&) + { + + if (cfgGeneral.cfgQABasic) { + eventSelectedIndividually(collision); + } + + if (!eventRct(collision, true)) { + return; + } + + auto bc = collision.bc_as(); + int currentRunNumber = bc.runNumber(); + if (!cfgGeneral.cfgRunRemoveList.value.empty()) { + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number + return; + } + } + + auto cent = getCentrality(collision); + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), cent, true)) { + return; + } + + if (!collision.has_foundFT0()) { + return; + } + histos.fill(HIST("hEventCount"), HaveFT0Cut); + + loadAlignParam(bc.timestamp()); + loadGain(bc); + loadCorrection(bc.timestamp()); + float weightCent = 1.0f; + + getCentralityWeight(weightCent, cent); + histos.fill(HIST("Centrality"), cent); + histos.fill(HIST("CentralityWeighted"), cent, weightCent); + + histos.fill(HIST("zVtx"), collision.posZ()); + + fillYieldFt0aFt0c(collision, tracks); + + auto multiplicity = static_cast(tracks.size()); + + if (cfgGeneral.cfgQABasic) { + histos.fill(HIST("Nch"), multiplicity); + } + + if (cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks, multiplicity, false); + } + + if (cfgGeneral.cfgQABasic) { + histos.fill(HIST("Nch_corrected"), multiplicity); + } + + if (cfgEvSel.cfgSelCollByNch && (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations)) { + return; + } + if (!cfgEvSel.cfgSelCollByNch && (cent > cfgEvSel.cfgMaxCentForCorrelations || cent < cfgEvSel.cfgMinCentForCorrelations)) { + return; + } + + histos.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin + + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity : cent; + + const auto& ft0 = collision.foundFT0(); + + fillCorrelationsFT0AFT0C(ft0, ft0, collision.posZ(), SameEvent, eventClass, weightCent); + } + PROCESS_SWITCH(PidLongRange, processSameFt0aFt0c, "Process same event for FT0A-FT0C correlation", true); + + void processMixedFt0aFt0c(FilteredCollisions const& collisions, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&) + { + + auto getTracksSize = [&tracks, this](FilteredCollisions::iterator const& collision) { + auto associatedTracks = tracks.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); + auto mult = associatedTracks.size(); + return mult; + }; + + using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; + + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; + + auto tracksTuple = std::make_tuple(tracks, tracks); + Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracks1, collision2, tracks2] = *it; + + // should have the same event to TPC-FT0A/C correlations + if (!collision1.sel8() || !collision2.sel8()) { + continue; + } + + if (!eventRct(collision1, false) || !eventRct(collision2, false)) { + continue; + } + + auto cent1 = getCentrality(collision1); + auto cent2 = getCentrality(collision2); + + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), cent1, false)) { + continue; + } + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), cent2, false)) { + continue; + } + + if (!(collision1.has_foundFT0() && collision2.has_foundFT0())) { + continue; + } + + auto bc = collision1.bc_as(); + int currentRunNumber = bc.runNumber(); + if (!cfgGeneral.cfgRunRemoveList.value.empty()) { + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number + continue; + } + } + loadAlignParam(bc.timestamp()); + loadCorrection(bc.timestamp()); + + auto multiplicity1 = static_cast(tracks1.size()); + auto multiplicity2 = static_cast(tracks2.size()); + + if (cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks1, multiplicity1, false); + if (cfgEvSel.cfgUseCol2ForMixedEvSel) { + nchCounter(tracks2, multiplicity2, false); + } + } + + if (cfgEvSel.cfgSelCollByNch && (multiplicity1 > cfgEvSel.cfgMaxMultForCorrelations || multiplicity1 < cfgEvSel.cfgMinMultForCorrelations)) { + continue; + } + if (cfgEvSel.cfgSelCollByNch && cfgEvSel.cfgUseCol2ForMixedEvSel && (multiplicity2 > cfgEvSel.cfgMaxMultForCorrelations || multiplicity2 < cfgEvSel.cfgMinMultForCorrelations)) { + continue; + } + if (!cfgEvSel.cfgSelCollByNch && (cent1 > cfgEvSel.cfgMaxCentForCorrelations || cent1 < cfgEvSel.cfgMinCentForCorrelations)) { + continue; + } + if (!cfgEvSel.cfgSelCollByNch && cfgEvSel.cfgUseCol2ForMixedEvSel && (cent2 > cfgEvSel.cfgMaxCentForCorrelations || cent2 < cfgEvSel.cfgMinCentForCorrelations)) { + continue; + } + + histos.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin + + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity1 : cent1; + + float eventWeight = 1.0f; + + if (cfgEffWeights.cfgUseEventWeights) { + eventWeight = 1.0f / it.currentWindowNeighbours(); + } + float weightCent = 1.0f; + getCentralityWeight(weightCent, cent1); + + const auto& ft0Col1 = collision1.foundFT0(); + const auto& ft0Col2 = collision2.foundFT0(); + + fillCorrelationsFT0AFT0C(ft0Col1, ft0Col2, collision1.posZ(), MixedEvent, eventClass, eventWeight * weightCent); + } + } + PROCESS_SWITCH(PidLongRange, processMixedFt0aFt0c, "Process mixed events for FT0A-FT0C correlation", true); + + void processQA(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&) + { + eventSelectedIndividually(collision); + + if (!eventRct(collision, true)) { + return; + } + + auto bc = collision.bc_as(); + int currentRunNumber = bc.runNumber(); + if (!cfgGeneral.cfgRunRemoveList.value.empty()) { + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number + return; + } + } + + histos.fill(HIST("h_globalTracks_centT0C_before"), collision.centFT0C(), tracks.size()); + histos.fill(HIST("h_PVTracks_centT0C_before"), collision.centFT0C(), collision.multNTracksPV()); + histos.fill(HIST("h_globalTracks_PVTracks_before"), collision.multNTracksPV(), tracks.size()); + histos.fill(HIST("h_globalTracks_multT0A_before"), collision.multFT0A(), tracks.size()); + histos.fill(HIST("h_globalTracks_multV0A_before"), collision.multFV0A(), tracks.size()); + histos.fill(HIST("h_multV0A_multT0A_before"), collision.multFT0A(), collision.multFV0A()); + histos.fill(HIST("h_multT0C_centT0C_before"), collision.centFT0C(), collision.multFT0C()); + + auto cent = getCentrality(collision); + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), cent, true)) { + return; + } + + histos.fill(HIST("h_globalTracks_centT0C_after"), collision.centFT0C(), tracks.size()); + histos.fill(HIST("h_PVTracks_centT0C_after"), collision.centFT0C(), collision.multNTracksPV()); + histos.fill(HIST("h_globalTracks_PVTracks_after"), collision.multNTracksPV(), tracks.size()); + histos.fill(HIST("h_globalTracks_multT0A_after"), collision.multFT0A(), tracks.size()); + histos.fill(HIST("h_globalTracks_multV0A_after"), collision.multFV0A(), tracks.size()); + histos.fill(HIST("h_multV0A_multT0A_after"), collision.multFT0A(), collision.multFV0A()); + histos.fill(HIST("h_multT0C_centT0C_after"), collision.centFT0C(), collision.multFT0C()); + histos.fill(HIST("h_centFT0M_centFT0C"), collision.centFT0C(), collision.centFT0M()); + histos.fill(HIST("h_centFV0A_centFT0C"), collision.centFT0C(), collision.centFV0A()); + + if (!collision.has_foundFT0()) { + return; + } + histos.fill(HIST("hEventCount"), HaveFT0Cut); + + loadAlignParam(bc.timestamp()); + loadGain(bc); + loadCorrection(bc.timestamp()); + float weightCent = 1.0f; + + getCentralityWeight(weightCent, cent); + histos.fill(HIST("Centrality"), cent); + histos.fill(HIST("CentralityWeighted"), cent, weightCent); + + histos.fill(HIST("zVtx"), collision.posZ()); + + auto multiplicity = static_cast(tracks.size()); + + histos.fill(HIST("Nch"), multiplicity); + + if (cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks, multiplicity, true); + } + + histos.fill(HIST("Nch_corrected"), multiplicity); + + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity : cent; + + float triggerWeight = 1.0f; + // loop over all tracks + for (auto const& track : tracks) { + + histos.fill(HIST("hDCAz_before"), track.dcaZ(), track.pt()); + histos.fill(HIST("hDCAxy_before"), track.dcaXY(), track.pt()); + histos.fill(HIST("hTPCNclsPID_before"), track.tpcNClsPID()); + histos.fill(HIST("hTPCNclsFound_before"), track.tpcNClsFound()); + histos.fill(HIST("hTPCCrossedRows_before"), track.tpcNClsCrossedRows()); + + if (!trackSelected(track)) { + continue; + } + + histos.fill(HIST("hTPCNclsPID_after"), track.tpcNClsPID()); + histos.fill(HIST("hTPCNclsFound_after"), track.tpcNClsFound()); + histos.fill(HIST("hTPCCrossedRows_after"), track.tpcNClsCrossedRows()); + histos.fill(HIST("hChi2prTPCcls"), track.tpcChi2NCl()); + histos.fill(HIST("hChi2prITScls"), track.itsChi2NCl()); + histos.fill(HIST("hITSNclsFound"), track.itsNCls()); + + histos.fill(HIST("hDCAz_after"), track.dcaZ(), track.pt(), UseCharged); + histos.fill(HIST("hDCAxy_after"), track.dcaXY(), track.pt(), UseCharged); + + fillTrackQA(track, UseCharged, eventClass, Before, triggerWeight); + fillTrackQA(track, UsePions, eventClass, Before, triggerWeight); + fillTrackQA(track, UseKaons, eventClass, Before, triggerWeight); + fillTrackQA(track, UseProtons, eventClass, Before, triggerWeight); + + int pidIndex = getNsigmaPID(track); + + if (pidIndex > UseCharged) { + fillTrackQA(track, pidIndex, eventClass, After, triggerWeight); + histos.fill(HIST("hDCAz_after"), track.dcaZ(), track.pt(), pidIndex); + histos.fill(HIST("hDCAxy_after"), track.dcaXY(), track.pt(), pidIndex); + } + + } // end of track loop + } + PROCESS_SWITCH(PidLongRange, processQA, "Process for storing the QA for events and tracks", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + }; +} diff --git a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationPp.cxx b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationPp.cxx index 5a94ee3811a..73b8b8eecfc 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationPp.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationPp.cxx @@ -360,7 +360,7 @@ struct TwoParticleCorrelationPp { void processMixed(FullCollisions const& collisions, FullTracks const& tracks) { - BinningType bindingOnVtx{{vtxBinsEdges}, true}; + BinningType bindingOnVtx{{vtxBinsEdges}}; auto tracksTuple = std::make_tuple(tracks); SameKindPair> cfgPairCut{"cfgPairCut", {CfgPairCutDefaults.front().data(), 5, {"Photon", "K0", "Lambda", "Phi", "Rho"}}, "Pair cuts on various particles"}; - Configurable cfgEfficiencyTrigger{"cfgEfficiencyTrigger", "", "CCDB path to efficiency object for trigger particles"}; - Configurable cfgEfficiencyAssociated{"cfgEfficiencyAssociated", "", "CCDB path to efficiency object for associated particles"}; - Configurable cfgAcceptance{"cfgAcceptance", "", "CCDB path to the GFW acceptance object"}; - Configurable cfgFillAcceptanceWeights{"cfgFillAcceptanceWeights", false, "Fill acceptance maps instead of physics outputs"}; - Configurable cfgAcceptanceRunByRun{"cfgAcceptanceRunByRun", false, "Fill or load run-by-run acceptance weights"}; - Configurable cfgAcceptancePhiBins{"cfgAcceptancePhiBins", int{DefaultAcceptancePhiBins}, "Number of phi bins in acceptance maps"}; - Configurable cfgAcceptanceEtaBins{"cfgAcceptanceEtaBins", int{DefaultAcceptanceEtaBins}, "Number of eta bins in acceptance maps"}; + struct : ConfigurableGroup { + Configurable cfgEfficiencyTrigger{"cfgEfficiencyTrigger", "", "CCDB path to efficiency object for trigger particles"}; + Configurable cfgEfficiencyAssociated{"cfgEfficiencyAssociated", "", "CCDB path to efficiency object for associated particles"}; + Configurable cfgAcceptance{"cfgAcceptance", "", "CCDB path to the GFW acceptance object"}; + Configurable cfgFillAcceptanceWeights{"cfgFillAcceptanceWeights", false, "Fill acceptance maps instead of physics outputs"}; + Configurable cfgAcceptanceRunByRun{"cfgAcceptanceRunByRun", false, "Fill or load run-by-run acceptance weights"}; + Configurable cfgAcceptancePhiBins{"cfgAcceptancePhiBins", int{DefaultAcceptancePhiBins}, "Number of phi bins in acceptance maps"}; + Configurable cfgAcceptanceEtaBins{"cfgAcceptanceEtaBins", int{DefaultAcceptanceEtaBins}, "Number of eta bins in acceptance maps"}; + } cfgCorrections; + Configurable cfgNuncSeedsTemplateFile{"cfgNuncSeedsTemplateFile", "", "Local ROOT file containing ensembleYieldTemplates"}; Configurable cfgNuncSeedsTemplate{"cfgNuncSeedsTemplate", "", "CCDB path to the ensemble-yield template ccdb_object"}; Configurable cfgMinPairAcceptance{"cfgMinPairAcceptance", 0.05f, "Minimum pair acceptance used by the event seed estimator"}; @@ -156,6 +159,7 @@ struct TwoParticleCorrelationsMpi { ConfigurableAxis axisPtTrigger{"axisPtTrigger", {VARIABLE_WIDTH, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 10.0}, "pt trigger axis for histograms"}; ConfigurableAxis axisPtAssoc{"axisPtAssoc", {VARIABLE_WIDTH, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0}, "pt associated axis for histograms"}; ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 100.1}, "multiplicity / centrality axis for histograms"}; + ConfigurableAxis axisMultiplicityQA{"axisMultiplicityQA", {250, 0, 250}, "fine multiplicity / centrality axis for control histograms"}; ConfigurableAxis axisVertexEfficiency{"axisVertexEfficiency", {10, -10, 10}, "vertex axis for efficiency histograms"}; ConfigurableAxis axisEtaEfficiency{"axisEtaEfficiency", {20, -1.0, 1.0}, "eta axis for efficiency histograms"}; @@ -289,6 +293,12 @@ struct TwoParticleCorrelationsMpi { float weight; }; + struct DetectorClosureResponseCoordinates { + float trueMultiplicity; + float rawMultiplicity; + float correctedMultiplicity; + }; + std::vector yieldTemplates; std::vector> pairAcceptanceMaps; std::vector> pairAcceptanceEtaVertexMaps; @@ -327,10 +337,11 @@ struct TwoParticleCorrelationsMpi { Service pdg{}; using AodCollisions = soa::Filtered>; - using AodTracks = soa::Filtered>; + using AodTracks = soa::Filtered>; using DerivedCollisions = soa::Filtered; using DerivedCollisionsCorrected = soa::Filtered; + using DerivedMCClosureCollisions = soa::Join; using DerivedCollisionsMultSet = soa::Filtered>; using DerivedCollisionsMultSetCorrected = soa::Filtered>; using DerivedTracks = soa::Filtered; @@ -351,26 +362,38 @@ struct TwoParticleCorrelationsMpi { if (enabledDerivedMixedProcesses > 1) { LOGF(fatal, "Only one reconstructed derived mixed-event process can be enabled"); } - if (doprocessMCSameDerived && enabledDerivedSameProcesses > 0) { - LOGF(fatal, "processMCSameDerived is mutually exclusive with the reconstructed derived same-event processes because it also fills those outputs"); + if ((doprocessMCSameDerived || doprocessMCDetectorClosure) && enabledDerivedSameProcesses > 0) { + LOGF(fatal, "The MC generated/reconstructed same-event processes are mutually exclusive with the reconstructed derived same-event processes because they also fill those outputs"); + } + if (doprocessMCSameDerived && doprocessMCDetectorClosure) { + LOGF(fatal, "processMCSameDerived and processMCDetectorClosure are mutually exclusive because both fill generated and reconstructed same-event outputs"); } - if (doprocessSameGenMC && doprocessMCSameDerived) { - LOGF(fatal, "processSameGenMC and processMCSameDerived are mutually exclusive because both fill the generated same-event outputs"); + if (doprocessSameGenMC && (doprocessMCSameDerived || doprocessMCDetectorClosure)) { + LOGF(fatal, "processSameGenMC is mutually exclusive with processMCSameDerived and processMCDetectorClosure because they fill the same generated outputs"); + } + if (doprocessMCDetectorClosure && cfgGeneral.cfgCentBinsForMC != 0) { + LOGF(fatal, "processMCDetectorClosure requires cfgCentBinsForMC=0 so generated and reconstructed outputs are both filled versus true charged multiplicity"); + } + if (doprocessMCDetectorClosure && cfgUserAxis == NMPIAxis) { + LOGF(fatal, "processMCDetectorClosure does not support cfgUserAxis=3 (true N MPI); use the true charged-multiplicity axis for detector closure"); + } + if (doprocessMCDetectorClosure && cfgCorrections.cfgFillAcceptanceWeights) { + LOGF(fatal, "processMCDetectorClosure cannot be used to produce acceptance weights"); } if (cfgUserAxis == NMPIAxis && (!(doprocessSameGenMC || doprocessMCSameDerived) || doprocessSameAOD || enabledDerivedSameProcesses > 0 || doprocessMixedAOD || enabledDerivedMixedProcesses > 0 || doprocessMCMixedDerived || - cfgGeneral.cfgCentBinsForMC != 0 || cfgFillAcceptanceWeights)) { + cfgGeneral.cfgCentBinsForMC != 0 || cfgCorrections.cfgFillAcceptanceWeights)) { LOGF(fatal, "cfgUserAxis=3 supports generated MC same events only: enable processSameGenMC or processMCSameDerived and disable all other same/mixed processes"); } - if (cfgFillAcceptanceWeights && !cfgAcceptance.value.empty()) { + if (cfgCorrections.cfgFillAcceptanceWeights && !cfgCorrections.cfgAcceptance.value.empty()) { LOGF(fatal, "cfgFillAcceptanceWeights and cfgAcceptance are mutually exclusive: produce and apply acceptance weights in separate jobs"); } - if (cfgFillAcceptanceWeights && !(doprocessSameDerived || doprocessSameDerivedCorrected || doprocessSameDerivedMultSet || doprocessSameDerivedMultSetCorrected || doprocessMCSameDerived)) { + if (cfgCorrections.cfgFillAcceptanceWeights && !(doprocessSameDerived || doprocessSameDerivedCorrected || doprocessSameDerivedMultSet || doprocessSameDerivedMultSetCorrected || doprocessMCSameDerived)) { LOGF(fatal, "cfgFillAcceptanceWeights requires a reconstructed derived same-event process"); } - if (cfgAcceptancePhiBins < 1 || cfgAcceptanceEtaBins < 1) { + if (cfgCorrections.cfgAcceptancePhiBins < 1 || cfgCorrections.cfgAcceptanceEtaBins < 1) { LOGF(fatal, "Acceptance-map bin counts must be positive"); } if (!cfgNuncSeedsTemplateFile.value.empty() && !cfgNuncSeedsTemplate.value.empty()) { @@ -421,8 +444,8 @@ struct TwoParticleCorrelationsMpi { eventSeedEstimatorEnabled ? "enabled" : "disabled", cfgNuncSeedsTemplateFile.value.c_str(), cfgNuncSeedsTemplate.value.c_str()); - registry.add("yields", "multiplicity/centrality vs pT vs eta", {HistType::kTH3F, {{100, 0, 100, "/multiplicity/centrality"}, {40, 0, 20, "p_{T}"}, {100, -2, 2, "#eta"}}}); - registry.add("etaphi", "multiplicity/centrality vs eta vs phi", {HistType::kTH3F, {{100, 0, 100, "multiplicity/centrality"}, {100, -2, 2, "#eta"}, {200, 0, o2::constants::math::TwoPI, "#varphi"}}}); + registry.add("yields", "multiplicity/centrality vs pT vs eta", {HistType::kTH3F, {{axisMultiplicityQA, "/multiplicity/centrality"}, {40, 0, 20, "p_{T}"}, {100, -2, 2, "#eta"}}}); + registry.add("etaphi", "multiplicity/centrality vs eta vs phi", {HistType::kTH3F, {{axisMultiplicityQA, "multiplicity/centrality"}, {100, -2, 2, "#eta"}, {200, 0, o2::constants::math::TwoPI, "#varphi"}}}); if (doprocessSameDerivedMultSet || doprocessSameDerivedMultSetCorrected) { if (cfgMultCorrelationsMask == 0) { LOGF(fatal, "cfgMultCorrelationsMask can not be 0 when MultSet process functions are in use."); @@ -445,16 +468,32 @@ struct TwoParticleCorrelationsMpi { } registry.add("multCorrelations", "Multiplicity correlations", {HistType::kTHnSparseF, multAxes}); } - registry.add("multiplicity", "event multiplicity", {HistType::kTH1F, {{100, 0, 100, "/multiplicity/centrality"}}}); + registry.add("multiplicity", "event multiplicity", {HistType::kTH1F, {{axisMultiplicityQA, "/multiplicity/centrality"}}}); + if (doprocessMCDetectorClosure) { + registry.add("detectorClosure/rawMultiplicityVsTrue", "raw reconstructed versus true charged multiplicity;N_{ch}^{true};N_{ch}^{raw,reco}", {HistType::kTH2F, {axisMultiplicity, axisMultiplicity}}); + registry.add("detectorClosure/correctedMultiplicityVsTrue", "corrected reconstructed versus true charged multiplicity;N_{ch}^{true};N_{ch}^{corr,reco}", {HistType::kTH2F, {axisMultiplicity, axisMultiplicity}}); + registry.add("detectorClosure/triggerResponseRaw", "efficiency-weighted trigger response versus raw reconstructed multiplicity", {HistType::kTHnSparseF, {{axisMultiplicity, "N_{ch}^{true}"}, {axisMultiplicity, "N_{ch}^{raw,reco}"}, {axisPtTrigger, "p_{T,trig}"}}}); + registry.add("detectorClosure/triggerResponseCorrected", "efficiency-weighted trigger response versus corrected reconstructed multiplicity", {HistType::kTHnSparseF, {{axisMultiplicity, "N_{ch}^{true}"}, {axisMultiplicity, "N_{ch}^{corr,reco}"}, {axisPtTrigger, "p_{T,trig}"}}}); + registry.add("detectorClosure/pairResponseRaw", "efficiency-weighted pair response versus raw reconstructed multiplicity", {HistType::kTHnSparseF, {{axisMultiplicity, "N_{ch}^{true}"}, {axisMultiplicity, "N_{ch}^{raw,reco}"}, {axisDeltaEta, "#Delta#eta"}, {axisPtAssoc, "p_{T,assoc}"}, {axisPtTrigger, "p_{T,trig}"}, {axisDeltaPhi, "#Delta#varphi"}}}); + registry.add("detectorClosure/pairResponseCorrected", "efficiency-weighted pair response versus corrected reconstructed multiplicity", {HistType::kTHnSparseF, {{axisMultiplicity, "N_{ch}^{true}"}, {axisMultiplicity, "N_{ch}^{corr,reco}"}, {axisDeltaEta, "#Delta#eta"}, {axisPtAssoc, "p_{T,assoc}"}, {axisPtTrigger, "p_{T,trig}"}, {axisDeltaPhi, "#Delta#varphi"}}}); + registry.add("detectorClosure/profileCorrectedMultiplicityVsTrue", "mean corrected reconstructed multiplicity versus true multiplicity;N_{ch}^{true};#LT N_{ch}^{corr,reco} #GT", {HistType::kTProfile, {axisMultiplicity}}); + registry.add("detectorClosure/profileTrueMultiplicityVsCorrected", "mean true multiplicity versus corrected reconstructed multiplicity;N_{ch}^{corr,reco};#LT N_{ch}^{true} #GT", {HistType::kTProfile, {axisMultiplicity}}); + registry.add("detectorClosure/recoCollisionsPerMCCollision", "accepted reconstructed collisions per MC collision;N_{coll}^{reco};events", {HistType::kTH1F, {{11, -0.5, 10.5}}}); + registry.add("detectorClosure/status", "detector-closure event status;status;events", {HistType::kTH1F, {{3, -0.5, 2.5}}}); + auto* detectorClosureStatus = registry.get(HIST("detectorClosure/status")).get(); + detectorClosureStatus->GetXaxis()->SetBinLabel(1, "no reconstructed collision"); + detectorClosureStatus->GetXaxis()->SetBinLabel(2, "one best collision"); + detectorClosureStatus->GetXaxis()->SetBinLabel(3, "invalid best-collision count"); + } if (eventSeedEstimatorEnabled) { - registry.add("eventSeedEstimator", "event-level template estimator", {HistType::kTHnSparseF, {{100, 0, 100, "multiplicity"}, {100, -0.5, 99.5, "N_{trig}"}, {200, 0, 20, "Y_{near}"}, {200, 0, 20, "Y_{away}"}, {200, 0, 100, "N_{uncorrelated seeds}"}}}); + registry.add("eventSeedEstimator", "event-level template estimator", {HistType::kTHnSparseF, {{axisMultiplicityQA, "multiplicity"}, {100, -0.5, 99.5, "N_{trig}"}, {200, 0, 20, "Y_{near}"}, {200, 0, 20, "Y_{away}"}, {200, 0, 100, "N_{uncorrelated seeds}"}}}); registry.add("eventSeedPairProbabilities", "summed pair probabilities", {HistType::kTH3F, {{200, 0, 200, "#Sigma P_{baseline}"}, {200, 0, 200, "#Sigma P_{near}"}, {200, 0, 200, "#Sigma P_{away}"}}}); registry.add("eventSeedEstimatorStatus", "event-estimator status", {HistType::kTH1F, {{5, -0.5, 4.5, "status"}}}); - registry.add("eventSeedTemplateCoverage", "template coverage vs multiplicity", {HistType::kTH2F, {{100, 0, 100, "multiplicity"}, {102, -0.01, 1.01, "matched/candidate pairs"}}}); - registry.add("eventSeedEstimateVsMultiplicity", "event-level seed estimate vs multiplicity", {HistType::kTH2F, {{100, 0, 100, "multiplicity"}, {200, 0, 100, "N_{uncorrelated seeds}"}}}); + registry.add("eventSeedTemplateCoverage", "template coverage vs multiplicity", {HistType::kTH2F, {{axisMultiplicityQA, "multiplicity"}, {102, -0.01, 1.01, "matched/candidate pairs"}}}); + registry.add("eventSeedEstimateVsMultiplicity", "event-level seed estimate vs multiplicity", {HistType::kTH2F, {{axisMultiplicityQA, "multiplicity"}, {200, 0, 100, "N_{uncorrelated seeds}"}}}); registry.add("eventSeedFixedProbabilityVsMultiplicity", "fixed-probability event Seed versus multiplicity;multiplicity;N_{seed}^{probability sum}", {HistType::kTH2F, {axisMultiplicity, {1000, 0, 100}}}); - registry.add("eventNearYieldVsMultiplicity", "event-level near yield vs multiplicity", {HistType::kTH2F, {{100, 0, 100, "multiplicity"}, {200, 0, 20, "Y_{near}"}}}); - registry.add("eventAwayYieldVsMultiplicity", "event-level away yield vs multiplicity", {HistType::kTH2F, {{100, 0, 100, "multiplicity"}, {200, 0, 20, "Y_{away}"}}}); + registry.add("eventNearYieldVsMultiplicity", "event-level near yield vs multiplicity", {HistType::kTH2F, {{axisMultiplicityQA, "multiplicity"}, {200, 0, 20, "Y_{near}"}}}); + registry.add("eventAwayYieldVsMultiplicity", "event-level away yield vs multiplicity", {HistType::kTH2F, {{axisMultiplicityQA, "multiplicity"}, {200, 0, 20, "Y_{away}"}}}); registry.add("profileEventNTriggers", "mean event trigger count", {HistType::kTProfile, {axisMultiplicity}}); registry.add("profileEventNearPairs", "mean summed near-pair probability", {HistType::kTProfile, {axisMultiplicity}}); registry.add("profileEventAwayPairs", "mean summed away-pair probability", {HistType::kTProfile, {axisMultiplicity}}); @@ -531,9 +570,9 @@ struct TwoParticleCorrelationsMpi { registry.add("eventcount_mixed", "bin", {HistType::kTH1F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}}}); registry.add("trackcount_same", "bin", {HistType::kTH2F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}, {10, -0.5, 9.5}}}); registry.add("trackcount_mixed", "bin", {HistType::kTH3F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}, {10, -0.5, 9.5}, {10, -0.5, 9.5}}}); - if (cfgFillAcceptanceWeights && !cfgAcceptanceRunByRun) { - AxisSpec phiAxis{cfgAcceptancePhiBins, 0., o2::constants::math::TwoPI, "#varphi"}; - AxisSpec etaAxis{cfgAcceptanceEtaBins, -cfgGeneral.cfgCutEta.value, cfgGeneral.cfgCutEta.value, "#eta"}; + if (cfgCorrections.cfgFillAcceptanceWeights && !cfgCorrections.cfgAcceptanceRunByRun) { + AxisSpec phiAxis{cfgCorrections.cfgAcceptancePhiBins, 0., o2::constants::math::TwoPI, "#varphi"}; + AxisSpec etaAxis{cfgCorrections.cfgAcceptanceEtaBins, -cfgGeneral.cfgCutEta.value, cfgGeneral.cfgCutEta.value, "#eta"}; mAcceptanceWeights = registry.add("phi_eta_vtxz_ref", "Reference-track acceptance;#varphi;#eta;z_{vtx} (cm)", {HistType::kTH3D, {phiAxis, etaAxis, axisVertex}}); } @@ -606,7 +645,7 @@ struct TwoParticleCorrelationsMpi { same->setTrackEtaCut(cfgGeneral.cfgCutEta); mixed->setTrackEtaCut(cfgGeneral.cfgCutEta); - if (!cfgEfficiencyAssociated.value.empty()) { + if (!cfgCorrections.cfgEfficiencyAssociated.value.empty()) { efficiencyAssociatedCache.reserve(512); } @@ -655,7 +694,7 @@ struct TwoParticleCorrelationsMpi { ++nPtPtSubevents; } - if (!cfgFillAcceptanceWeights && (doprocessMCSameDerived || doprocessSameDerived || doprocessSameDerivedCorrected || doprocessSameDerivedMultSet || doprocessSameDerivedMultSetCorrected)) { + if (!cfgCorrections.cfgFillAcceptanceWeights && (doprocessMCSameDerived || doprocessMCDetectorClosure || doprocessSameDerived || doprocessSameDerivedCorrected || doprocessSameDerivedMultSet || doprocessSameDerivedMultSetCorrected)) { auto recoProfiles = std::make_unique(); addConfigObjectsToObjArray(recoProfiles.get(), mCorrConfigs, cfgUserAxis == EventSeedAxis && eventClassifierPercentileAxisEnabled); fFC.setObject(new FlowContainer("FlowContainer")); @@ -669,7 +708,7 @@ struct TwoParticleCorrelationsMpi { fFCpt->initialise(axisMultiplicity, MaxPtCorrelationOrder, cfgCorrConfig.value, FlowNBootstrap); fFCpt->initialiseSubevent(axisMultiplicity, MaxPtCorrelationOrder, nPtPtSubevents, FlowNBootstrap); } - if (!cfgFillAcceptanceWeights && (doprocessMCSameDerived || doprocessSameGenMC)) { + if (!cfgCorrections.cfgFillAcceptanceWeights && (doprocessMCSameDerived || doprocessMCDetectorClosure || doprocessSameGenMC)) { auto generatedProfiles = std::make_unique(); addConfigObjectsToObjArray(generatedProfiles.get(), mCorrConfigs, cfgUserAxis == NMPIAxis && eventClassifierPercentileAxisEnabled); fFCGen.setObject(new FlowContainer("FlowContainer_gen")); @@ -862,8 +901,8 @@ struct TwoParticleCorrelationsMpi { return existing->second; } - AxisSpec phiAxis{cfgAcceptancePhiBins, 0., o2::constants::math::TwoPI, "#varphi"}; - AxisSpec etaAxis{cfgAcceptanceEtaBins, -cfgGeneral.cfgCutEta.value, cfgGeneral.cfgCutEta.value, "#eta"}; + AxisSpec phiAxis{cfgCorrections.cfgAcceptancePhiBins, 0., o2::constants::math::TwoPI, "#varphi"}; + AxisSpec etaAxis{cfgCorrections.cfgAcceptanceEtaBins, -cfgGeneral.cfgCutEta.value, cfgGeneral.cfgCutEta.value, "#eta"}; const std::string histogramName = Form("%d/phi_eta_vtxz_ref", runNumber); const auto histogram = registry.add(histogramName.c_str(), "Reference-track acceptance;#varphi;#eta;z_{vtx} (cm)", {HistType::kTH3D, {phiAxis, etaAxis, axisVertex}}); mAcceptanceWeightsByRun.emplace(runNumber, histogram); @@ -876,7 +915,7 @@ struct TwoParticleCorrelationsMpi { if (track.pt() <= fPtAxis->GetXmin() || track.pt() >= fPtAxis->GetXmax()) { return; } - const auto histogram = cfgAcceptanceRunByRun ? getAcceptanceWeightsForRun(runNumber) : mAcceptanceWeights; + const auto histogram = cfgCorrections.cfgAcceptanceRunByRun ? getAcceptanceWeightsForRun(runNumber) : mAcceptanceWeights; histogram->Fill(track.phi(), track.eta(), posZ); } @@ -1745,11 +1784,12 @@ struct TwoParticleCorrelationsMpi { } template - void fillCorrelations(TTarget target, TTracks1& tracks1, TTracks2& tracks2, float multiplicity, float posZ, int magField, float eventWeight, EventSeedEstimate* seedEstimate = nullptr, std::vector* pendingTriggerFills = nullptr, std::vector* pendingPairFills = nullptr, double userAxisValue = -1.0, bool fillEstimatorAcceptanceQA = true, bool fillLoopQA = true) + void fillCorrelations(TTarget target, TTracks1& tracks1, TTracks2& tracks2, float multiplicity, float posZ, int magField, float eventWeight, EventSeedEstimate* seedEstimate = nullptr, std::vector* pendingTriggerFills = nullptr, std::vector* pendingPairFills = nullptr, double userAxisValue = -1.0, bool fillEstimatorAcceptanceQA = true, bool fillLoopQA = true, float efficiencyMultiplicity = -1.f, const DetectorClosureResponseCoordinates* detectorClosureResponse = nullptr) { if (multiplicity < 0.f) { return; } + const float efficiencyLookupMultiplicity = efficiencyMultiplicity >= 0.f ? efficiencyMultiplicity : multiplicity; // Cache efficiency for particles (too many FindBin lookups) if constexpr (step == CorrelationContainer::kCFStepCorrected) { @@ -1757,7 +1797,7 @@ struct TwoParticleCorrelationsMpi { efficiencyAssociatedCache.clear(); efficiencyAssociatedCache.reserve(tracks2.size()); for (const auto& track : tracks2) { - efficiencyAssociatedCache.push_back(getEfficiencyCorrection(cfg.mEfficiencyAssociated, track.eta(), track.pt(), multiplicity, posZ)); + efficiencyAssociatedCache.push_back(getEfficiencyCorrection(cfg.mEfficiencyAssociated, track.eta(), track.pt(), efficiencyLookupMultiplicity, posZ)); } } } @@ -1819,7 +1859,11 @@ struct TwoParticleCorrelationsMpi { float triggerWeight = eventWeight; if constexpr (step == CorrelationContainer::kCFStepCorrected) { if (cfg.mEfficiencyTrigger) { - triggerWeight *= getEfficiencyCorrection(cfg.mEfficiencyTrigger, track1.eta(), track1.pt(), multiplicity, posZ); + triggerWeight *= getEfficiencyCorrection(cfg.mEfficiencyTrigger, track1.eta(), track1.pt(), efficiencyLookupMultiplicity, posZ); + } + if (detectorClosureResponse) { + registry.fill(HIST("detectorClosure/triggerResponseRaw"), detectorClosureResponse->trueMultiplicity, detectorClosureResponse->rawMultiplicity, track1.pt(), triggerWeight); + registry.fill(HIST("detectorClosure/triggerResponseCorrected"), detectorClosureResponse->trueMultiplicity, detectorClosureResponse->correctedMultiplicity, track1.pt(), triggerWeight); } } @@ -1945,6 +1989,13 @@ struct TwoParticleCorrelationsMpi { float deltaPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi(), -o2::constants::math::PIHalf); const float deltaEta = track1.eta() - track2.eta(); + if constexpr (step == CorrelationContainer::kCFStepCorrected) { + if (detectorClosureResponse) { + registry.fill(HIST("detectorClosure/pairResponseRaw"), detectorClosureResponse->trueMultiplicity, detectorClosureResponse->rawMultiplicity, deltaEta, track2.pt(), track1.pt(), deltaPhi, associatedWeight); + registry.fill(HIST("detectorClosure/pairResponseCorrected"), detectorClosureResponse->trueMultiplicity, detectorClosureResponse->correctedMultiplicity, deltaEta, track2.pt(), track1.pt(), deltaPhi, associatedWeight); + } + } + if (triggerHasTemplate) { ++seedEstimate->nCandidatePairs; if (const auto* yieldTemplate = findYieldTemplate(multiplicity, track1.pt(), track2.pt())) { @@ -1983,46 +2034,46 @@ struct TwoParticleCorrelationsMpi { if (cfg.efficiencyLoaded) { return; } - if (!cfgEfficiencyTrigger.value.empty()) { + if (!cfgCorrections.cfgEfficiencyTrigger.value.empty()) { if (cfgGeneral.cfgLocalEfficiency > 0) { - TFile* fEfficiencyTrigger = TFile::Open(cfgEfficiencyTrigger.value.c_str(), "READ"); + TFile* fEfficiencyTrigger = TFile::Open(cfgCorrections.cfgEfficiencyTrigger.value.c_str(), "READ"); cfg.mEfficiencyTrigger = dynamic_cast(fEfficiencyTrigger->Get("ccdb_object")); } else { - cfg.mEfficiencyTrigger = ccdb->getForTimeStamp>(cfgEfficiencyTrigger, timestamp); + cfg.mEfficiencyTrigger = ccdb->getForTimeStamp>(cfgCorrections.cfgEfficiencyTrigger, timestamp); } if (cfg.mEfficiencyTrigger == nullptr) { - LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEfficiencyTrigger.value.c_str()); + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgCorrections.cfgEfficiencyTrigger.value.c_str()); } - LOGF(info, "Loaded efficiency histogram for trigger particles from %s", cfgEfficiencyTrigger.value.c_str()); + LOGF(info, "Loaded efficiency histogram for trigger particles from %s", cfgCorrections.cfgEfficiencyTrigger.value.c_str()); } - if (!cfgEfficiencyAssociated.value.empty()) { + if (!cfgCorrections.cfgEfficiencyAssociated.value.empty()) { if (cfgGeneral.cfgLocalEfficiency > 0) { - TFile* fEfficiencyAssociated = TFile::Open(cfgEfficiencyAssociated.value.c_str(), "READ"); + TFile* fEfficiencyAssociated = TFile::Open(cfgCorrections.cfgEfficiencyAssociated.value.c_str(), "READ"); cfg.mEfficiencyAssociated = dynamic_cast(fEfficiencyAssociated->Get("ccdb_object")); } else { - cfg.mEfficiencyAssociated = ccdb->getForTimeStamp>(cfgEfficiencyAssociated, timestamp); + cfg.mEfficiencyAssociated = ccdb->getForTimeStamp>(cfgCorrections.cfgEfficiencyAssociated, timestamp); } if (cfg.mEfficiencyAssociated == nullptr) { - LOGF(fatal, "Could not load efficiency histogram for associated particles from %s", cfgEfficiencyAssociated.value.c_str()); + LOGF(fatal, "Could not load efficiency histogram for associated particles from %s", cfgCorrections.cfgEfficiencyAssociated.value.c_str()); } - LOGF(info, "Loaded efficiency histogram for associated particles from %s", cfgEfficiencyAssociated.value.c_str()); + LOGF(info, "Loaded efficiency histogram for associated particles from %s", cfgCorrections.cfgEfficiencyAssociated.value.c_str()); } cfg.efficiencyLoaded = true; } void loadAcceptance(const uint64_t timestamp, const int runNumber) { - if (cfgFillAcceptanceWeights || cfgAcceptance.value.empty()) { + if (cfgCorrections.cfgFillAcceptanceWeights || cfgCorrections.cfgAcceptance.value.empty()) { cfg.mAcceptance = nullptr; cfg.acceptanceLoaded = true; return; } - if (cfg.acceptanceLoaded && (!cfgAcceptanceRunByRun || cfg.acceptanceRunNumber == runNumber)) { + if (cfg.acceptanceLoaded && (!cfgCorrections.cfgAcceptanceRunByRun || cfg.acceptanceRunNumber == runNumber)) { return; } - std::string path = cfgAcceptance.value; - if (cfgAcceptanceRunByRun) { + std::string path = cfgCorrections.cfgAcceptance.value; + if (cfgCorrections.cfgAcceptanceRunByRun) { if (path.back() != '/') { path += '/'; } @@ -2099,7 +2150,7 @@ struct TwoParticleCorrelationsMpi { soa::Filtered const& mcParticles, soa::SmallGroups const& collisions) { - if (cfgFillAcceptanceWeights) { + if (cfgCorrections.cfgFillAcceptanceWeights) { return; } if (cfgUserAxis == NMPIAxis && mcCollision.nMPI() < 0) { @@ -2157,18 +2208,20 @@ struct TwoParticleCorrelationsMpi { PROCESS_SWITCH(TwoParticleCorrelationsMpi, processSameGenMC, "Process generated MC events from derived data and validate against the stored HepMC N MPI", false); template - void processSameDerivedT(CollType const& collision, TTracks1 const& tracks1, TTracks2 const& tracks2, const int* trueNMPI = nullptr) + void processSameDerivedT(CollType const& collision, TTracks1 const& tracks1, TTracks2 const& tracks2, const int* trueNMPI = nullptr, const float* outputMultiplicityOverride = nullptr, const float* efficiencyMultiplicityOverride = nullptr, const DetectorClosureResponseCoordinates* detectorClosureResponse = nullptr) { auto getMultiplicity = [](const auto& col) { return getAnalysisMultiplicity(col); }; using BinningTypeDerived = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertex, axisMultiplicity}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. - const auto multiplicity = getMultiplicity(collision); + BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertex, axisMultiplicity}}; // underflows and overflows get bin -1 + const float collisionMultiplicity = getMultiplicity(collision); + const float multiplicity = outputMultiplicityOverride ? *outputMultiplicityOverride : collisionMultiplicity; + const float efficiencyMultiplicity = efficiencyMultiplicityOverride ? *efficiencyMultiplicityOverride : collisionMultiplicity; if (cfgVerbosity > 0) { LOGF(info, "processSameDerivedT: Tracks for collision: %d/%d | Vertex: %.1f | Multiplicity/Centrality: %.1f", tracks1.size(), tracks2.size(), collision.posZ(), multiplicity); } - if (cfgFillAcceptanceWeights) { + if (cfgCorrections.cfgFillAcceptanceWeights) { for (const auto& track : tracks2) { fillAcceptanceWeights(track, collision.posZ(), collision.runNumber()); } @@ -2186,8 +2239,8 @@ struct TwoParticleCorrelationsMpi { fGFW->Clear(); fFCpt->clearVector(); for (const auto& track : tracks2) { - fillPtSums(track, multiplicity, collision.posZ()); - fillGFW(track, multiplicity, collision.posZ()); + fillPtSums(track, efficiencyMultiplicity, collision.posZ()); + fillGFW(track, efficiencyMultiplicity, collision.posZ()); } int bin = configurableBinningDerived.getBin(std::tuple(collision.posZ(), multiplicity)); @@ -2216,14 +2269,14 @@ struct TwoParticleCorrelationsMpi { flushSeedAxisFills(same, pendingTriggerFills, pendingPairFills, seedEstimate.nuncSeeds()); } else if (hasEfficiency) { fillContainerEvent(same, multiplicity, CorrelationContainer::kCFStepCorrected); - fillCorrelations(same, tracks1, tracks2, multiplicity, collision.posZ(), field, 1.0f, &seedEstimate, &pendingTriggerFills, &pendingPairFills); + fillCorrelations(same, tracks1, tracks2, multiplicity, collision.posZ(), field, 1.0f, &seedEstimate, &pendingTriggerFills, &pendingPairFills, -1.0, true, true, efficiencyMultiplicity, detectorClosureResponse); finalizeEventSeedEstimate(seedEstimate); flushSeedAxisFills(same, pendingTriggerFills, pendingPairFills, seedEstimate.nuncSeeds()); } if (fillReco && hasEfficiency) { fillContainerEvent(same, multiplicity, CorrelationContainer::kCFStepCorrected); - fillCorrelations(same, tracks1, tracks2, multiplicity, collision.posZ(), field, 1.0f, nullptr, nullptr, nullptr, getEventSeedUserAxisValue(multiplicity, seedEstimate.nuncSeeds())); + fillCorrelations(same, tracks1, tracks2, multiplicity, collision.posZ(), field, 1.0f, nullptr, nullptr, nullptr, getEventSeedUserAxisValue(multiplicity, seedEstimate.nuncSeeds()), true, true, efficiencyMultiplicity, detectorClosureResponse); } const auto seedClass = getEventSeedClass(multiplicity, seedEstimate.nuncSeeds()); fillOutputContainers(multiplicity, fRndm->Rndm(), seedClass); @@ -2240,7 +2293,7 @@ struct TwoParticleCorrelationsMpi { } if (hasEfficiency) { fillContainerEvent(same, multiplicity, CorrelationContainer::kCFStepCorrected); - fillCorrelations(same, tracks1, tracks2, multiplicity, collision.posZ(), field, 1.0f, fillReco ? nullptr : &seedEstimate); + fillCorrelations(same, tracks1, tracks2, multiplicity, collision.posZ(), field, 1.0f, fillReco ? nullptr : &seedEstimate, nullptr, nullptr, -1.0, true, true, efficiencyMultiplicity, detectorClosureResponse); } finalizeEventSeedEstimate(seedEstimate); fillOutputContainers(multiplicity, fRndm->Rndm()); @@ -2286,7 +2339,7 @@ struct TwoParticleCorrelationsMpi { // NOTE legacy function for O2 integration tests. Full version needs derived data // Strictly upper categorised collisions, for cfgNumMixedEvents combinations per bin, skipping those in entry -1 - BinningTypeAOD configurableBinning{{axisVertex, axisMultiplicity}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + BinningTypeAOD configurableBinning{{axisVertex, axisMultiplicity}}; auto tracksTuple = std::make_tuple(tracks); SameKindPair pairs{configurableBinning, cfgNumMixedEvents, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -2345,7 +2398,7 @@ struct TwoParticleCorrelationsMpi { }; using BinningTypeDerived = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertex, axisMultiplicity}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertex, axisMultiplicity}}; // Strictly upper categorised collisions, for cfgNumMixedEvents combinations per bin, skipping those in entry -1 auto tracksTuple = std::make_tuple(std::forward(tracks)...); using TA = std::tuple_element<0, decltype(tracksTuple)>::type; @@ -2430,10 +2483,10 @@ struct TwoParticleCorrelationsMpi { } PROCESS_SWITCH(TwoParticleCorrelationsMpi, processMixedDerivedMultSetCorrected, "Process mixed events on derived data with corrected multiplicity and multiplicity sets", false); - template - void processMCSameDerivedT(McCollision const& mcCollision, Particles1 const& mcParticles1, Particles2 const& mcParticles2, soa::SmallGroups const& collisions) + template + void processMCSameDerivedT(McCollision const& mcCollision, Particles1 const& mcParticles1, Particles2 const& mcParticles2, Collisions const& collisions) { - if (cfgFillAcceptanceWeights) { + if (cfgCorrections.cfgFillAcceptanceWeights) { return; } if (cfgUserAxis == NMPIAxis && mcCollision.nMPI() < 0) { @@ -2456,7 +2509,7 @@ struct TwoParticleCorrelationsMpi { ? getTrueNMPIUserAxisValue(multiplicity, mcCollision.nMPI()) : -1.0; - if (!(doprocessMCSameDerived || doprocessSameDerived || doprocessSameDerivedCorrected || doprocessSameDerivedMultSet || doprocessSameDerivedMultSetCorrected)) { + if (!(doprocessMCSameDerived || doprocessMCDetectorClosure || doprocessSameDerived || doprocessSameDerivedCorrected || doprocessSameDerivedMultSet || doprocessSameDerivedMultSetCorrected)) { if constexpr (std::experimental::is_detected::value) { fillQA(mcCollision, multiplicity, mcCollision.posZ(), mcParticles1, mcParticles2); } else { @@ -2526,6 +2579,61 @@ struct TwoParticleCorrelationsMpi { } PROCESS_SWITCH(TwoParticleCorrelationsMpi, processMCSameDerived, "Process generated and reconstructed MC same events on derived data and validate against HepMC N MPI", false); + void processMCDetectorClosure(soa::Filtered::iterator const& mcCollision, + soa::Filtered const& mcParticles, + soa::SmallGroups const& collisions, + soa::Filtered const& tracks) + { + registry.fill(HIST("detectorClosure/recoCollisionsPerMCCollision"), static_cast(collisions.size())); + + int bestRecoCollisionCount = 0; + for (const auto& collision : collisions) { + if (collision.bestRecoCollision()) { + ++bestRecoCollisionCount; + } + } + + if (collisions.size() == 0) { + registry.fill(HIST("detectorClosure/status"), 0.0); + } else if (bestRecoCollisionCount == 1) { + registry.fill(HIST("detectorClosure/status"), 1.0); + } else { + registry.fill(HIST("detectorClosure/status"), 2.0); + LOGF(fatal, "processMCDetectorClosure expected exactly one best reconstructed collision for MC collision %d, found %d among %d reconstructed collisions", + mcCollision.globalIndex(), bestRecoCollisionCount, collisions.size()); + } + + // Fill generated steps once. kCFStepVertex is filled only when at least one + // selected reconstructed collision is associated with this MC collision. + processMCSameDerivedT(mcCollision, mcParticles, mcParticles, collisions); + + if (bestRecoCollisionCount == 0) { + return; + } + + const float trueMultiplicity = mcCollision.multiplicity(); + const int trueNMPI = mcCollision.nMPI(); + for (const auto& collision : collisions) { + if (!collision.bestRecoCollision()) { + continue; + } + + const float rawMultiplicity = collision.multiplicity(); + const float correctedMultiplicity = collision.multiplicityCorrected(); + registry.fill(HIST("detectorClosure/rawMultiplicityVsTrue"), trueMultiplicity, rawMultiplicity); + registry.fill(HIST("detectorClosure/correctedMultiplicityVsTrue"), trueMultiplicity, correctedMultiplicity); + registry.fill(HIST("detectorClosure/profileCorrectedMultiplicityVsTrue"), trueMultiplicity, correctedMultiplicity); + registry.fill(HIST("detectorClosure/profileTrueMultiplicityVsCorrected"), correctedMultiplicity, trueMultiplicity); + + auto collisionTracks = tracks.sliceBy(derivedTracksPerCollision, collision.globalIndex()); + const DetectorClosureResponseCoordinates responseCoordinates{trueMultiplicity, rawMultiplicity, correctedMultiplicity}; + // Physics histograms use true multiplicity, while efficiency weights use + // the reconstructed multiplicity coordinate for which the map was made. + processSameDerivedT(collision, collisionTracks, collisionTracks, &trueNMPI, &trueMultiplicity, &rawMultiplicity, &responseCoordinates); + } + } + PROCESS_SWITCH(TwoParticleCorrelationsMpi, processMCDetectorClosure, "Fill generated, raw reconstructed, and efficiency-corrected MC correlations versus true charged multiplicity", false); + PresliceUnsorted collisionPerMCCollision = aod::cfcollision::cfMcCollisionId; template void processMCMixedDerivedT(soa::Filtered const& mcCollisions, soa::Filtered const& collisions, ParticleTypes&&... particles) @@ -2544,7 +2652,7 @@ struct TwoParticleCorrelationsMpi { }; using BinningTypeMCDerived = FlexibleBinningPolicy, aod::mccollision::PosZ, decltype(getMultiplicity)>; - BinningTypeMCDerived configurableBinning{{getMultiplicity}, {axisVertex, axisMultiplicity}, true}; + BinningTypeMCDerived configurableBinning{{getMultiplicity}, {axisVertex, axisMultiplicity}}; // Strictly upper categorised collisions, for cfgNumMixedEvents combinations per bin, skipping those in entry -1 auto tuple = std::make_tuple(std::forward(particles)...); diff --git a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx new file mode 100644 index 00000000000..8284aa29488 --- /dev/null +++ b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx @@ -0,0 +1,1262 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file twoParticleCorrelationsMultSpher.cxx +/// \brief Two-particle angular correlations in pp collisions at 13 TeV. +/// Structured in parallel with the AliRoot AliMESppColTask analysis. +/// MC truth level: McCollision + McParticles. +/// Includes: multiplicity classes, transverse sphericity, PID (MC: PDG code), +/// event mixing with 4 separate pools per sphericity class, +/// THnSparse for multi-dimensional analysis + TH2D SE/ME for +/// direct normalisation (both per-trigger and per-pair). +/// +/// Pool structure (separate for MC generated and Reco reconstructed data): +/// eventPoolsMC[sphClass][zvtxBin][multBin] +/// eventPoolsReco[sphClass][zvtxBin][multBin] +/// sphClass 0 : S_T in (0.0, 0.3] — jetty +/// sphClass 1 : S_T in (0.3, 0.6] — intermediate +/// sphClass 2 : S_T in (0.6, 1.0] — isotropic +/// sphClass 3 : S_T > 0.0 — all events (no sphericity cut) +/// +/// \author Madalina Tarzila + +#include "PWGHF/Utils/utilsAnalysis.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +// ============================================================ +// PID species +// ============================================================ +enum PidSpecies { PidUnidentified = 0, + PidPion, + PidKaon, + PidProton, + NPidSpecies }; +static constexpr std::array PidNames{"unid", "pion", "kaon", "proton"}; + +// ============================================================ +// Sphericity classes +// ============================================================ +static constexpr int NSphClasses = 4; +static constexpr std::array SphMax{0.3, 0.6, 1.0, 1.0}; +static constexpr std::array SphLabels{"jetty", "intermediate", "isotropic", "all"}; + +// Half a bin: puts integer-valued axes (indices, cut-flow steps) at bin centres. +static constexpr double HalfBin = 0.5; + +// ============================================================ +// Cut-flow steps (bin numbers of the evSel_* and track cut-flow histograms) +// ============================================================ +enum EventCut { EventCutRead = 1, + EventCutZvtx, + EventCutMultBin, + EventCutZvtxBin, + EventCutLeadPt, + EventCutSphericity, + EventCutSphClass, + EventCutSeFilled, + NEventCuts = EventCutSeFilled }; +enum TrackCut { TrackCutSeen = 1, + TrackCutGlobal, + TrackCutDcaXY, + TrackCutDcaZ, + TrackCutEta, + TrackCutPt, + NTrackCuts = TrackCutPt }; + +// ============================================================ +struct TwoParticleCorrelationsMultSpher { + + // ---------------------------------------------------------------- + // Configurables + // ---------------------------------------------------------------- + Configurable nBinsPt{"nBinsPt", 100, "N bins in pT histograms [0-10 GeV/c]"}; + Configurable nBinsPt2{"nBinsPt2", 50, "N bins in pT study histograms [0-5 GeV/c]"}; + Configurable mixPoolSize{"mixPoolSize", 200, "Max events per mixing pool cell"}; + Configurable minAssocPt{"minAssocPt", 0.2, "Min pT for track selection (GeV/c)"}; + Configurable minLeadPt{"minLeadPt", 1.0, "Min pT for leading (trigger) particle (GeV/c)"}; + Configurable maxLeadPt{"maxLeadPt", 2.0, "Max pT for leading (trigger) particle (GeV/c)"}; + Configurable minAssocCorr{"minAssocCorr", 1.0, "Min pT for associated particle in correlations (GeV/c)"}; + Configurable maxAssocCorr{"maxAssocCorr", 2.0, "Max pT for associated particle in correlations (GeV/c)"}; + Configurable maxEta{"maxEta", 0.8, "Max |eta| for track selection"}; + Configurable maxZvtx{"maxZvtx", 10.0, "Max |z_vtx| (cm)"}; + // |y| cut applied ONLY to PID-identified tracks (pion/kaon/proton) -- + // independent of maxEta, which applies to all-species tracks (hSE/hME). + // Rapidity is well-defined only for massive/identified particles; at + // pT=0.5 GeV/c, proton y_max~0.48, so |y|<0.5 keeps full acceptance for + // that species. Reference: ALICE 2511.10399 (arXiv, Nov 2025), Sec. 4 -- + // "tracks reconstructed within |eta|<0.8, with the additional rapidity + // cut of |y|<0.5". + Configurable maxRapPID{"maxRapPID", 0.5, "Max |y| for PID-identified tracks (pion/kaon/proton), independent of maxEta"}; + + // Reco-specific configurables + Configurable maxDcaXY{"maxDcaXY", 2.0, "Max DCA XY (cm) for reco tracks"}; + Configurable maxDcaZ{"maxDcaZ", 2.0, "Max DCA Z (cm) for reco tracks"}; + // Combined TPC+TOF PID criterion per ALICE 2511.10399 Sec. 4: two + // different thresholds -- selection at 2 sigma, ambiguity rejection + // at 3 sigma. + Configurable maxNSigmaPID{"maxNSigmaPID", 2.0, "Max combined N_sigma,PID for species selection (ALICE 2511.10399)"}; + Configurable maxNSigmaPIDAmbig{"maxNSigmaPIDAmbig", 3.0, "N_sigma,PID threshold for ambiguity rejection (>1 species below this -> PidUnidentified)"}; + + // Thresholds of the legacy PID gating (independent 3-sigma cuts on TPC + // and TOF, no combined quadrature, no pT-dependent TOF gating), kept so + // that it can be switched on with useLegacyNSigmaGating without removing + // the ALICE 2511.10399 criterion. + Configurable maxNSigmaTPC{"maxNSigmaTPC", 3.0, "[legacy] Max |N_sigma| TPC for PID, used only when useLegacyNSigmaGating=true"}; + Configurable maxNSigmaTOF{"maxNSigmaTOF", 3.0, "[legacy] Max |N_sigma| TOF for PID, used only when useLegacyNSigmaGating=true"}; + + // Three independently toggleable flags to compare the legacy PID + // methodology (independent 3-sigma TPC/TOF cuts, minimum-chi2 species + // choice, no rapidity cut) with the ALICE 2511.10399-based one. Each + // isolates one component, so any combination of the 8 can be run to + // see which change drives a statistics difference. The gating and + // selection defaults reproduce the 2511.10399 behaviour; setting all + // three to the legacy side reproduces the legacy pidFromNSigma. + // + // applyRapidityCutPID defaults to false: the |y|<0.5 cut only discarded + // already-correctly-identified tracks between |y|=0.5 and the |eta|<0.8 + // acceptance, so dropping it raises both yield and purity at the same + // time. Gating and selection stay at the 2511.10399 values, because + // relaxing the gating trades purity for statistics. + Configurable useLegacyNSigmaGating{"useLegacyNSigmaGating", false, "false=pT-gated combined N_sigma,PID (2511.10399); true=independent TPC/TOF 3-sigma AND-gate, TOF always required when available (legacy)"}; + Configurable useLegacyPIDSelection{"useLegacyPIDSelection", false, "false=reject if >1 species below ambiguity threshold; true=pick minimum-chi2 species among those passing the gate, no rejection (legacy)"}; + Service pdg{}; + + Configurable applyRapidityCutPID{"applyRapidityCutPID", false, "false=skip the |y| ZvtxEdges{-10., -5., -2.5, 2.5, 5., 10.}; + static constexpr std::array MultEdges{1, 10, 15, 20, 30, 46, 81}; + + static std::string multLabel(int im) + { + return std::to_string(MultEdges[im]) + "-" + std::to_string(MultEdges[im + 1] - 1); + } + + // ---------------------------------------------------------------- + // Mixing pools + // [sphClass][zvtxBin][multBin] — "all" (existing) + // [pidIdx][sphClass][zvtxBin][multBin] — per PID species + // pidIdx: 0=pion, 1=kaon, 2=proton + // ---------------------------------------------------------------- + static constexpr int NPidCorr = 3; // pion, kaon, proton + static constexpr std::array PidCorrNames{"pion", "kaon", "proton"}; + // PDG codes corresponding to pidIdx + static int pidCorrPDG(int idx) + { + if (idx == 0) { + return PDG_t::kPiPlus; + } + if (idx == 1) { + return PDG_t::kKPlus; + } + return PDG_t::kProton; + } + /// Index of a pion/kaon/proton in the NPidCorr-sized arrays, or -1 for any other species. + static int pidSpeciesToIdx(PidSpecies pid) + { + if (pid < PidPion || pid > PidProton) { + return -1; + } + return pid - PidPion; + } + + using EventPool = std::deque>; + // Pool "all" — separate per MC (generated) and Reco (reconstructed) + std::array, NZvtxBins>, NSphClasses> eventPoolsMC; + std::array, NZvtxBins>, NSphClasses> eventPoolsReco; + // Pool per PID species (like-sign) — separate for MC and Reco + std::array, NZvtxBins>, NSphClasses>, NPidCorr> eventPoolsPidMC; + std::array, NZvtxBins>, NSphClasses>, NPidCorr> eventPoolsPidReco; + + // ---------------------------------------------------------------- + // Histogram registry + handles + // ---------------------------------------------------------------- + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // SE/ME TH2D "all": [sphClass][multBin] — MC and Reco separate + std::array, NMultBins>, NSphClasses> hSeMC{}; + std::array, NMultBins>, NSphClasses> hMeMC{}; + std::array, NMultBins>, NSphClasses> hSeReco{}; + std::array, NMultBins>, NSphClasses> hMeReco{}; + + // SE/ME TH2D like-sign per species: [pidIdx][sphClass][multBin] — MC and Reco separate + std::array, NMultBins>, NSphClasses>, NPidCorr> hSePidMC{}; + std::array, NMultBins>, NSphClasses>, NPidCorr> hMePidMC{}; + std::array, NMultBins>, NSphClasses>, NPidCorr> hSePidReco{}; + std::array, NMultBins>, NSphClasses>, NPidCorr> hMePidReco{}; + + // Trigger counter: axes = mult bin (x) x sphClass (y) — MC and Reco separate + std::shared_ptr hNtrigMC; + std::shared_ptr hNtrigReco; + std::array, NPidCorr> hNtrigPidMC{}; + std::array, NPidCorr> hNtrigPidReco{}; + + // Per-mult sphericity — MC and Reco separate + std::array, NMultBins> hSphMultMC{}; + std::array, NMultBins> hSphMultReco{}; + + // ---------------------------------------------------------------- + // [DEBUG, TEMPORARY] Track-cut counter for processReco diagnostics. + // Bins: 1=tracks seen, 2=passed isGlobalTrackWoDCA, 3=passed DCAxy, + // 4=passed DCAz, 5=passed eta, 6=passed pt (selected). + // Remove once the empty-Reco-histograms issue is understood/fixed. + // ---------------------------------------------------------------- + std::shared_ptr hTrackCutDebugReco; + + // ---------------------------------------------------------------- + // PID confusion matrix (true species from PDG vs. tagged species from + // pidFromNSigma applied to the SAME reconstructed track, per pT bin). + // Quick contamination estimate, reported as a systematic; it does NOT + // correct anything. Filled only by processMCConfusion (needs both MC + // truth and reconstructed nSigma together, unlike + // processMC/processReco which only ever see one or the other). + // ---------------------------------------------------------------- + std::shared_ptr hPidConfusionMC; + + // ---------------------------------------------------------------- + // Helpers + // ---------------------------------------------------------------- + + static PidSpecies pidFromPDG(int pdg) + { + switch (std::abs(pdg)) { + case PDG_t::kPiPlus: + return PidPion; + case PDG_t::kKPlus: + return PidKaon; + case PDG_t::kProton: + return PidProton; + default: + return PidUnidentified; + } + } + + /// PID from TPC(+TOF) nSigma for reconstructed tracks. Two independently + /// toggleable behaviors (see the useLegacyNSigmaGating and + /// useLegacyPIDSelection Configurables), so that the legacy and the + /// ALICE 2511.10399-based methodologies can be compared: + /// + /// legacyGating (gate = "does this species hypothesis pass at all"): + /// false (default): pT>0.5 && TOF available -> combined + /// N_sigma,PID = sqrt(nsTPC^2+nsTOF^2) < maxSel (2 sigma, per ALICE + /// 2511.10399 Sec. 4); else TPC-only. + /// true: legacy style -- |nsTPC| < maxTPC AND (if TOF available) |nsTOF| < maxTOF, + /// independently (no quadrature sum for the gate itself), TOF + /// required whenever available regardless of pT. + /// + /// legacySelection (what to do with hypotheses that pass the gate): + /// false (default): reject the track outright (PidUnidentified) if MORE + /// THAN ONE hypothesis also falls below the looser ambiguity + /// threshold maxAmbig (3 sigma) -- this comparison always uses the + /// combined-quadrature N_sigma,PID, regardless of legacyGating, so + /// the two flags stay independently meaningful. + /// true: legacy style -- among hypotheses passing the gate, + /// pick the one with minimum chi2 = nsTPC^2+nsTOF^2 (or nsTPC^2 + /// alone if no TOF); no rejection for multiple passing hypotheses. + /// + /// The two flags combine independently (e.g. legacy gating + new + /// selection is a valid, meaningful hybrid) -- this is deliberate, so + /// the statistics impact of each component can be isolated rather than + /// only comparing "old vs new" as one indivisible block. + template + PidSpecies pidFromNSigma(const T& track, double maxSel, double maxAmbig, + double maxTPC, double maxTOF, + bool legacyGating, bool legacySelection) const + { + struct Hypo { + PidSpecies pid = PidUnidentified; + double nsTPC = 0.; + double nsTOF = 0.; + }; + const std::array hypos{{{PidPion, track.tpcNSigmaPi(), track.tofNSigmaPi()}, + {PidKaon, track.tpcNSigmaKa(), track.tofNSigmaKa()}, + {PidProton, track.tpcNSigmaPr(), track.tofNSigmaPr()}}}; + + // TOF unavailable -> nSigma returns -999; check with any species + bool hasTOF = (std::fabs(track.tofNSigmaPi()) < MaxValidNSigma); + bool useCombined = (track.pt() > MinPtCombinedPid) && hasTOF; // only feeds the new-style gate + + auto passesGate = [&](const Hypo& h) { + if (legacyGating) { + return (std::fabs(h.nsTPC) < maxTPC) && (!hasTOF || std::fabs(h.nsTOF) < maxTOF); + } + double nsPID = useCombined + ? std::sqrt(h.nsTPC * h.nsTPC + h.nsTOF * h.nsTOF) + : std::fabs(h.nsTPC); + return nsPID < maxSel; + }; + + if (legacySelection) { + // Best-fit (min chi2) among hypotheses passing the gate -- no + // ambiguity rejection (legacy style). + PidSpecies best = PidUnidentified; + double bestChi2 = std::numeric_limits::max(); + for (const auto& h : hypos) { + if (!passesGate(h)) { + continue; + } + double chi2 = h.nsTPC * h.nsTPC + (hasTOF ? h.nsTOF * h.nsTOF : 0.0); + if (chi2 < bestChi2) { + bestChi2 = chi2; + best = h.pid; + } + } + return best; + } + + // Ambiguity-rejection selection (current default style). + int nBelowAmbig = 0; + PidSpecies selected = PidUnidentified; + for (const auto& h : hypos) { + if (!passesGate(h)) { + continue; + } + double nsPID = useCombined + ? std::sqrt(h.nsTPC * h.nsTPC + h.nsTOF * h.nsTOF) + : std::fabs(h.nsTPC); + if (nsPID < maxAmbig) { + ++nBelowAmbig; + } + selected = h.pid; + } + if (nBelowAmbig > 1) { + return PidUnidentified; // ambiguous, reject + } + return selected; + } + + /// Returns PDG mass for identified species, 0 for unidentified. + static double pidMass(PidSpecies pid) + { + switch (pid) { + case PidPion: + return o2::constants::physics::MassPiPlus; + case PidKaon: + return o2::constants::physics::MassKPlus; + case PidProton: + return o2::constants::physics::MassProton; + default: + return 0.; + } + } + + // Returns specific sph class (0,1,2) or -1 if unphysical + // Note: class 3 ("all") is handled separately — it is always filled + // when sph > 0, without needing a specific bin lookup. + static int sphClass(double sph) + { + if (sph <= 0.) { + return -1; + } + for (int ic = 0; ic < NSphClasses - 1; ++ic) { + if (sph <= SphMax[ic]) { + return ic; + } + } + return -1; + } + + static double computeSphericity(const std::vector& tracks) + { + if (tracks.size() < MinTracksSphericity) { + return -1.0; + } + double sxx = 0., sxy = 0., syy = 0.; + for (const auto& t : tracks) { + double px = t.pt * std::cos(t.phi); + double py = t.pt * std::sin(t.phi); + sxx += px * px; + sxy += px * py; + syy += py * py; + } + double tr = sxx + syy; + double disc = tr * tr - 4.0 * (sxx * syy - sxy * sxy); + if (disc < 0.) { + disc = 0.; + } + double lambdaMin = (tr - std::sqrt(disc)) / 2.0; + if (tr < Epsilon) { + return -1.0; + } + return std::clamp(2.0 * lambdaMin / tr, 0.0, 1.0); + } + + /// Compute rapidity from pT, eta and particle mass. + /// y = 0.5 * ln((energy + pz) / (energy - pz)) + /// For unidentified particles (mass <= 0), returns eta. + static double computeRapidity(double pt, double eta, double mass) + { + if (mass <= 0.) { + return eta; // massless / unidentified -> y = eta + } + double pz = pt * std::sinh(eta); + double energy = std::sqrt(mass * mass + pt * pt + pz * pz); + if (energy <= std::fabs(pz) + Epsilon) { + return eta; // safety + } + return 0.5 * std::log((energy + pz) / (energy - pz)); + } + + // ---------------------------------------------------------------- + // init + // ---------------------------------------------------------------- + void init(InitContext const&) + { + const AxisSpec axisZvtx{100, -15., 15., "z_{vtx} (cm)"}; + const AxisSpec axisMult{100, 0., 180., "N_{ch}"}; + const AxisSpec axisEta{30, -1.5, 1.5, "#eta"}; + const AxisSpec axisPhi{60, 0., o2::constants::math::TwoPI, "#phi"}; + const AxisSpec axisDphi{30, -o2::constants::math::PIHalf, o2::constants::math::PI + o2::constants::math::PIHalf, "#Delta#phi"}; + const AxisSpec axisDeta{32, -1.6, 1.6, "#Delta#eta"}; + // Reserved for a possible rapidity-differentiated analysis -- unused + // while the PID correlations run pseudorapidity-only. + [[maybe_unused]] const AxisSpec axisDy{32, -1.6, 1.6, "#Delta y"}; + const AxisSpec axisPt{nBinsPt, 0., 10., "p_{T} (GeV/c)"}; + const AxisSpec axisSph{100, 0., 1., "S_{T}"}; + const AxisSpec axisPt2{nBinsPt2, 0., 5., "p_{T} (GeV/c)"}; + + // ---------------------------------------------------------------- + // Common lambda: books a complete set of histograms for one + // "channel" (MC or Reco), with all names suffixed accordingly. + // Takes references to all channel-specific members. + // ---------------------------------------------------------------- + auto bookChannel = [&](const char* suf, + std::array, NMultBins>, NSphClasses>& hSE, + std::array, NMultBins>, NSphClasses>& hME, + std::array, NMultBins>, NSphClasses>, NPidCorr>& hSEpid, + std::array, NMultBins>, NSphClasses>, NPidCorr>& hMEpid, + std::shared_ptr& hNtrig, + std::array, NPidCorr>& hNtrigPID, + std::array, NMultBins>& hSphMult) { + // QA + auto hQA = histos.add(Form("evSel_%s", suf), Form("Event selection (%s)", suf), HistType::kTH1D, {{NEventCuts, HalfBin, static_cast(NEventCuts) + HalfBin}}); + hQA->GetXaxis()->SetBinLabel(EventCutRead, "Events read"); + hQA->GetXaxis()->SetBinLabel(EventCutZvtx, "|z_{vtx}|<10"); + hQA->GetXaxis()->SetBinLabel(EventCutMultBin, "multBin OK"); + hQA->GetXaxis()->SetBinLabel(EventCutZvtxBin, "zvtxBin OK"); + hQA->GetXaxis()->SetBinLabel(EventCutLeadPt, "leadPt #in [min,max]"); + hQA->GetXaxis()->SetBinLabel(EventCutSphericity, "S_{T} valid"); + hQA->GetXaxis()->SetBinLabel(EventCutSphClass, "sphClass OK"); + hQA->GetXaxis()->SetBinLabel(EventCutSeFilled, "SE filled"); + + histos.add(Form("Zvertex_%s", suf), Form("z_{vtx} (%s);z_{vtx} (cm);events", suf), kTH1D, {axisZvtx}); + histos.add(Form("multHist_%s", suf), Form("N_{ch} (%s);N_{ch};events", suf), kTH1D, {axisMult}); + histos.add(Form("etaHistogram_%s", suf), Form("#eta (%s);#eta;tracks", suf), kTH1D, {axisEta}); + histos.add(Form("phiHistogram_%s", suf), Form("#phi (%s);#phi;tracks", suf), kTH1D, {axisPhi}); + histos.add(Form("ptHistogram_%s", suf), Form("p_{T} (%s);p_{T} (GeV/c);tracks", suf), kTH1D, {axisPt}); + histos.add(Form("ptLeadHistogram_%s", suf), Form("p_{T} lead (%s);p_{T} (GeV/c);events", suf), kTH1D, {axisPt}); + histos.add(Form("ptAssocHistogram_%s", suf), Form("p_{T} assoc (%s);p_{T} (GeV/c);pairs", suf), kTH1D, {axisPt}); + histos.add(Form("sphericity_%s", suf), Form("S_{T} (inclusive, %s);S_{T};events", suf), kTH1D, {axisSph}); + histos.add(Form("sphericity_beforeCuts_%s", suf), Form("S_{T} (all evts with >=3 tracks, %s);S_{T};events", suf), kTH1D, {axisSph}); + + // Per-mult sphericity + for (int im = 0; im < NMultBins; ++im) { + hSphMult[im] = histos.add( + Form("sphericity_mult%d_%s", im, suf), + Form("S_T [%s] (%s);S_{T};events", multLabel(im).c_str(), suf), + HistType::kTH1D, {axisSph}); + } + + // Trigger counter TH2: x = multBin, y = sphClass + hNtrig = histos.add( + Form("hNtrig%s", suf), + Form("N_{trig} (%s);mult bin;sph class", suf), + HistType::kTH2D, + {{NMultBins, -HalfBin, static_cast(NMultBins) - HalfBin}, + {NSphClasses, -HalfBin, static_cast(NSphClasses) - HalfBin}}); + for (int im = 0; im < NMultBins; ++im) { + hNtrig->GetXaxis()->SetBinLabel(im + 1, multLabel(im).c_str()); + } + for (int ic = 0; ic < NSphClasses; ++ic) { + hNtrig->GetYaxis()->SetBinLabel(ic + 1, SphLabels[ic]); + } + + // SE/ME TH2D: [sphClass][multBin] + for (int ic = 0; ic < NSphClasses; ++ic) { + for (int im = 0; im < NMultBins; ++im) { + hSE[ic][im] = histos.add( + Form("hSE%s_sph%d_mult%d", suf, ic, im), + Form("SE [%s][%s] (%s);#Delta#phi;#Delta#eta", SphLabels[ic], multLabel(im).c_str(), suf), + HistType::kTH2D, {axisDphi, axisDeta}); + hME[ic][im] = histos.add( + Form("hME%s_sph%d_mult%d", suf, ic, im), + Form("ME [%s][%s] (%s);#Delta#phi;#Delta#eta", SphLabels[ic], multLabel(im).c_str(), suf), + HistType::kTH2D, {axisDphi, axisDeta}); + } + } + + // ---- SE/ME like-sign per PID species: [pidIdx][sphClass][multBin] --- + // PID correlations use Delta-eta, same as the inclusive hSE/hME + // histograms, NOT Delta-y. The rapidity infrastructure + // (computeRapidity(), the per-track y field, axisDy) is kept for a + // possible rapidity-differentiated analysis and is deliberately + // unused for now. + for (int ip = 0; ip < NPidCorr; ++ip) { + for (int ic = 0; ic < NSphClasses; ++ic) { + for (int im = 0; im < NMultBins; ++im) { + hSEpid[ip][ic][im] = histos.add( + Form("hSE%s_pid%d_sph%d_mult%d", suf, ip, ic, im), + Form("SE %s--%s [%s][%s] (%s);#Delta#varphi;#Delta#eta", + PidCorrNames[ip], PidCorrNames[ip], SphLabels[ic], multLabel(im).c_str(), suf), + HistType::kTH2D, {axisDphi, axisDeta}); + hMEpid[ip][ic][im] = histos.add( + Form("hME%s_pid%d_sph%d_mult%d", suf, ip, ic, im), + Form("ME %s--%s [%s][%s] (%s);#Delta#varphi;#Delta#eta", + PidCorrNames[ip], PidCorrNames[ip], SphLabels[ic], multLabel(im).c_str(), suf), + HistType::kTH2D, {axisDphi, axisDeta}); + } + } + // Trigger counter per PID species + hNtrigPID[ip] = histos.add( + Form("hNtrig%s_pid%d", suf, ip), + Form("N_{trig} %s (%s);mult bin;sph class", PidCorrNames[ip], suf), + HistType::kTH2D, + {{NMultBins, -HalfBin, static_cast(NMultBins) - HalfBin}, + {NSphClasses, -HalfBin, static_cast(NSphClasses) - HalfBin}}); + for (int im = 0; im < NMultBins; ++im) { + hNtrigPID[ip]->GetXaxis()->SetBinLabel(im + 1, multLabel(im).c_str()); + } + for (int ic = 0; ic < NSphClasses; ++ic) { + hNtrigPID[ip]->GetYaxis()->SetBinLabel(ic + 1, SphLabels[ic]); + } + } + }; + + // Book histograms for MC (generated) channel + bookChannel("MC", + hSeMC, hMeMC, hSePidMC, hMePidMC, + hNtrigMC, hNtrigPidMC, hSphMultMC); + + // Book histograms for Reco (reconstructed) channel + bookChannel("Reco", + hSeReco, hMeReco, hSePidReco, hMePidReco, + hNtrigReco, hNtrigPidReco, hSphMultReco); + + // [DEBUG, TEMPORARY] track-cut diagnostic counter for processReco + hTrackCutDebugReco = histos.add( + "hTrackCutDebug_Reco", + "Track cut flow (Reco, DEBUG);cut;tracks", + HistType::kTH1D, {{NTrackCuts, HalfBin, static_cast(NTrackCuts) + HalfBin}}); + hTrackCutDebugReco->GetXaxis()->SetBinLabel(TrackCutSeen, "seen"); + hTrackCutDebugReco->GetXaxis()->SetBinLabel(TrackCutGlobal, "passed isGlobalTrackWoDCA"); + hTrackCutDebugReco->GetXaxis()->SetBinLabel(TrackCutDcaXY, "passed DCAxy"); + hTrackCutDebugReco->GetXaxis()->SetBinLabel(TrackCutDcaZ, "passed DCAz"); + hTrackCutDebugReco->GetXaxis()->SetBinLabel(TrackCutEta, "passed eta"); + hTrackCutDebugReco->GetXaxis()->SetBinLabel(TrackCutPt, "passed pt (selected)"); + + // PID confusion matrix (contamination estimate) -- + // axes: pT, true species (PDG), tagged species (pidFromNSigma). + // Species axis order matches PidSpecies: 0=unid,1=pion,2=kaon,3=proton. + const AxisSpec axisPidSpecies{static_cast(NPidSpecies), -HalfBin, static_cast(NPidSpecies) - HalfBin, "species"}; + hPidConfusionMC = histos.add( + "hPidConfusion_MC", + "True vs. tagged PID species (MC);p_{T} (GeV/c);true species;tagged species", + HistType::kTH3D, {axisPt2, axisPidSpecies, axisPidSpecies}); + for (int ip = 0; ip < static_cast(NPidSpecies); ++ip) { + hPidConfusionMC->GetYaxis()->SetBinLabel(ip + 1, PidNames[ip]); + hPidConfusionMC->GetZaxis()->SetBinLabel(ip + 1, PidNames[ip]); + } + } + + // ---------------------------------------------------------------- + // processMC + // ---------------------------------------------------------------- + void processMC(aod::McCollision const& mcCollision, + aod::McParticles const& mcParticles) + { + histos.fill(HIST("evSel_MC"), EventCutRead); + + const double zvtx = mcCollision.posZ(); + if (std::fabs(zvtx) > maxZvtx) { + return; + } + histos.fill(HIST("evSel_MC"), EventCutZvtx); + histos.fill(HIST("Zvertex_MC"), zvtx); + + // ---- one-pass: build selTracks + find leading --------------- + std::vector selTracks; + selTracks.reserve(MultEdges.back() - 1); + int leadIdx = -1; + double pTlead = -1., phiLead = 0., etaLead = 0.; + + for (const auto& p : mcParticles) { + if (!p.isPhysicalPrimary()) { + continue; + } + auto* pdgPtr = pdg->GetParticle(p.pdgCode()); + if (!pdgPtr || std::fabs(pdgPtr->Charge()) < MinCharge) { + continue; + } + if (std::fabs(p.eta()) > maxEta) { + continue; + } + if (p.pt() < minAssocPt) { + continue; + } + + double mass = pdgPtr->Mass(); + double rap = computeRapidity(p.pt(), p.eta(), mass); + + // |y| < maxRapPID cut applies ONLY to PID-identified species + // (pion/kaon/proton) -- the all-species channel (hSE/hME) keeps the + // track regardless, gated only by |eta| < maxEta above. Clean way to + // do this: keep pdg for all-species bookkeeping, but zero it (-> + // PidUnidentified via pidFromPDG) when the rapidity cut fails, so the + // track contributes to hSE/hME but not hSEpid/hMEpid. + int pdgForPID = p.pdgCode(); + if (applyRapidityCutPID && pidFromPDG(pdgForPID) != PidUnidentified && std::fabs(rap) > maxRapPID) { + pdgForPID = 0; + } + + int idx = static_cast(selTracks.size()); + selTracks.push_back({static_cast(p.phi()), static_cast(p.eta()), static_cast(p.pt()), pdgForPID}); + + histos.fill(HIST("etaHistogram_MC"), p.eta()); + histos.fill(HIST("phiHistogram_MC"), p.phi()); + histos.fill(HIST("ptHistogram_MC"), p.pt()); + + if (p.pt() > pTlead) { + pTlead = p.pt(); + phiLead = p.phi(); + etaLead = p.eta(); + leadIdx = idx; + } + } + + const int nch = static_cast(selTracks.size()); + histos.fill(HIST("multHist_MC"), nch); + + const int mBin = o2::analysis::findBin(&MultEdges, nch); + if (mBin < 0) { + return; + } + histos.fill(HIST("evSel_MC"), EventCutMultBin); + + const int zBin = o2::analysis::findBin(&ZvtxEdges, zvtx); + if (zBin < 0) { + return; + } + histos.fill(HIST("evSel_MC"), EventCutZvtxBin); + + // ---- leading particle pT cut [minLeadPt, maxLeadPt] ----------- + if (leadIdx < 0) { + return; + } + if (pTlead < minLeadPt || pTlead > maxLeadPt) { + return; + } + histos.fill(HIST("evSel_MC"), EventCutLeadPt); + histos.fill(HIST("ptLeadHistogram_MC"), pTlead); + + // ---- sphericity --------------------------------------------- + const double sph = computeSphericity(selTracks); + // Fill sphericity_beforeCuts here — no additional cuts applied yet + if (sph >= 0.) { + histos.fill(HIST("sphericity_beforeCuts_MC"), sph); + } + if (sph < 0.) { + return; // fewer than 3 tracks + } + histos.fill(HIST("evSel_MC"), EventCutSphericity); + histos.fill(HIST("sphericity_MC"), sph); + hSphMultMC[mBin]->Fill(sph); + + const int sphCls = sphClass(sph); // 0,1,2 or -1 + if (sphCls < 0) { + return; + } + histos.fill(HIST("evSel_MC"), EventCutSphClass); + + const PidSpecies pidLead = pidFromPDG(selTracks[leadIdx].pdg); + + // ---- trigger counter ---------------------------------------- + hNtrigMC->Fill(static_cast(mBin), static_cast(sphCls)); + hNtrigMC->Fill(static_cast(mBin), static_cast((NSphClasses - 1))); // "all" + + // ================================================================ + // SAME-EVENT correlations + // ================================================================ + bool seHasPairs = false; + for (int ia = 0; ia < nch; ++ia) { + if (ia == leadIdx) { + continue; + } + const auto& assoc = selTracks[ia]; + if (assoc.pt >= pTlead) { + continue; // associated pT < leading pT + } + if (assoc.pt < minAssocCorr || assoc.pt > maxAssocCorr) { + continue; + } + + histos.fill(HIST("ptAssocHistogram_MC"), assoc.pt); + + const PidSpecies pidAssoc = pidFromPDG(assoc.pdg); + + const double dphi = RecoDecay::constrainAngle(phiLead - assoc.phi, -o2::constants::math::PIHalf); + const double deta = etaLead - assoc.eta; + + // TH2D SE: specific sph class (uses Delta-eta) + hSeMC[sphCls][mBin]->Fill(dphi, deta); + // TH2D SE: "all" class (uses Delta-eta) + hSeMC[NSphClasses - 1][mBin]->Fill(dphi, deta); + + // SE like-sign: fill only if trigger and associated are the same species + // Uses Delta-eta (pseudorapidity-only regime) + int pidLeadIdx = pidSpeciesToIdx(pidLead); + int pidAssocIdx = pidSpeciesToIdx(pidAssoc); + if (pidLeadIdx >= 0 && pidLeadIdx == pidAssocIdx) { + hSePidMC[pidLeadIdx][sphCls][mBin]->Fill(dphi, deta); + hSePidMC[pidLeadIdx][NSphClasses - 1][mBin]->Fill(dphi, deta); + } + + seHasPairs = true; + } + if (seHasPairs) { + histos.fill(HIST("evSel_MC"), EventCutSeFilled); + } + + // ---- hNtrigMC per PID species leading -------------------------- + int pidLeadIdx = pidSpeciesToIdx(pidLead); + if (pidLeadIdx >= 0) { + hNtrigPidMC[pidLeadIdx]->Fill(static_cast(mBin), static_cast(sphCls)); + hNtrigPidMC[pidLeadIdx]->Fill(static_cast(mBin), static_cast((NSphClasses - 1))); + } + + // ================================================================ + // Update pools AFTER SE + // ================================================================ + // Pool "all" — specific sph + { + auto& pool = eventPoolsMC[sphCls][zBin][mBin]; + pool.push_back(selTracks); + if (static_cast(pool.size()) > mixPoolSize) { + pool.pop_front(); + } + } + // Pool "all" — all sph + { + auto& poolAll = eventPoolsMC[NSphClasses - 1][zBin][mBin]; + poolAll.push_back(selTracks); + if (static_cast(poolAll.size()) > mixPoolSize) { + poolAll.pop_front(); + } + } + + // Pool per PID species — contains only the tracks of that species + for (int ip = 0; ip < NPidCorr; ++ip) { + // Build a vector with only the tracks of species ip + std::vector pidTracks; + for (const auto& t : selTracks) { + if (pidSpeciesToIdx(pidFromPDG(t.pdg)) == ip) { + pidTracks.push_back(t); + } + } + if (pidTracks.empty()) { + continue; + } + // Specific sph pool + { + auto& pool = eventPoolsPidMC[ip][sphCls][zBin][mBin]; + pool.push_back(pidTracks); + if (static_cast(pool.size()) > mixPoolSize) { + pool.pop_front(); + } + } + // "all" sph pool + { + auto& pool = eventPoolsPidMC[ip][NSphClasses - 1][zBin][mBin]; + pool.push_back(pidTracks); + if (static_cast(pool.size()) > mixPoolSize) { + pool.pop_front(); + } + } + } + + // ================================================================ + // MIXED-EVENT "all" + // ================================================================ + auto doMixing = [&](int poolIdx) { + const auto& pool = eventPoolsMC[poolIdx][zBin][mBin]; + const int nEvts = static_cast(pool.size()) - 1; + for (int ie = 0; ie < nEvts; ++ie) { + for (const auto& tr : pool[ie]) { + if (tr.pt >= pTlead) { + continue; + } + if (tr.pt < minAssocCorr || tr.pt > maxAssocCorr) { + continue; + } + const double dphi = RecoDecay::constrainAngle(phiLead - tr.phi, -o2::constants::math::PIHalf); + const double deta = etaLead - tr.eta; + hMeMC[poolIdx][mBin]->Fill(dphi, deta); + } + } + }; + + doMixing(sphCls); + doMixing(NSphClasses - 1); + + // ================================================================ + // MIXED-EVENT like-sign per PID species + // Trigger = leading track of species pidLeadIdx + // Associated = tracks of the same species from the PID pool + // Uses Delta-eta (pseudorapidity-only regime) + // ================================================================ + if (pidLeadIdx >= 0) { + auto doMixingPID = [&](int poolIdx) { + const auto& pool = eventPoolsPidMC[pidLeadIdx][poolIdx][zBin][mBin]; + const int nEvts = static_cast(pool.size()) - 1; + for (int ie = 0; ie < nEvts; ++ie) { + for (const auto& tr : pool[ie]) { + if (tr.pt >= pTlead) { + continue; + } + if (tr.pt < minAssocCorr || tr.pt > maxAssocCorr) { + continue; + } + const double dphi = RecoDecay::constrainAngle(phiLead - tr.phi, -o2::constants::math::PIHalf); + const double detaPID = etaLead - tr.eta; + hMePidMC[pidLeadIdx][poolIdx][mBin]->Fill(dphi, detaPID); + } + } + }; + doMixingPID(sphCls); + doMixingPID(NSphClasses - 1); + } + } + PROCESS_SWITCH(TwoParticleCorrelationsMultSpher, processMC, "Process MC truth", true); + + // ================================================================ + // processReco — reconstructed data (Collision + Tracks) + // ================================================================ + using ColReco = soa::Join; + using TrackReco = soa::Join; + + void processReco(ColReco::iterator const& col, + TrackReco const& tracks) + { + histos.fill(HIST("evSel_Reco"), EventCutRead); + + // ---- event selection ---- + if (!col.sel8()) { + return; + } + const double zvtx = col.posZ(); + if (std::fabs(zvtx) > maxZvtx) { + return; + } + histos.fill(HIST("evSel_Reco"), EventCutZvtx); + histos.fill(HIST("Zvertex_Reco"), zvtx); + + // ---- one-pass: build selTracks + find leading --------------- + std::vector selTracks; + selTracks.reserve(MultEdges.back() - 1); + int leadIdx = -1; + double pTlead = -1., phiLead = 0., etaLead = 0.; + + for (const auto& track : tracks) { + hTrackCutDebugReco->Fill(TrackCutSeen); // seen + + // Quality selection: global track without DCA (apply DCA separately) + if (!track.isGlobalTrackWoDCA()) { + continue; + } + hTrackCutDebugReco->Fill(TrackCutGlobal); // passed isGlobalTrackWoDCA + + // DCA cuts + if (std::fabs(track.dcaXY()) > maxDcaXY) { + continue; + } + hTrackCutDebugReco->Fill(TrackCutDcaXY); // passed DCAxy + if (std::fabs(track.dcaZ()) > maxDcaZ) { + continue; + } + hTrackCutDebugReco->Fill(TrackCutDcaZ); // passed DCAz + + // Kinematic cuts (same as MC) + if (std::fabs(track.eta()) > maxEta) { + continue; + } + hTrackCutDebugReco->Fill(TrackCutEta); // passed eta + if (track.pt() < minAssocPt) { + continue; + } + hTrackCutDebugReco->Fill(TrackCutPt); // passed pt (selected) + + // PID from nSigma (combined TPC+TOF, ALICE 2511.10399, unless the + // legacy flags below switch to legacy gating/selection) + PidSpecies pid = pidFromNSigma(track, maxNSigmaPID, maxNSigmaPIDAmbig, + maxNSigmaTPC, maxNSigmaTOF, + useLegacyNSigmaGating, useLegacyPIDSelection); + double mass = pidMass(pid); + double rap = computeRapidity(track.pt(), track.eta(), mass); + + // |y| < maxRapPID cut applies ONLY to PID-identified species -- see + // matching comment in processMC. Track stays in selTracks either way + // (all-species channel unaffected); only the PID assignment is reset. + if (applyRapidityCutPID && pid != PidUnidentified && std::fabs(rap) > maxRapPID) { + pid = PidUnidentified; + } + + // pdg = 0 for reco (no MC truth); store sign for future like/unlike-sign + int idx = static_cast(selTracks.size()); + selTracks.push_back({static_cast(track.phi()), static_cast(track.eta()), static_cast(track.pt()), 0}); + // Store PID info in pdg field as signed pidIdx for later use: + // sign(track.sign()) * (211 for pion, 321 for kaon, 2212 for proton, 0 for unid) + const int pidIdx = pidSpeciesToIdx(pid); + const int pdgLike = pidIdx >= 0 ? pidCorrPDG(pidIdx) : 0; + selTracks.back().pdg = track.sign() * pdgLike; + + histos.fill(HIST("etaHistogram_Reco"), track.eta()); + histos.fill(HIST("phiHistogram_Reco"), track.phi()); + histos.fill(HIST("ptHistogram_Reco"), track.pt()); + + if (track.pt() > pTlead) { + pTlead = track.pt(); + phiLead = track.phi(); + etaLead = track.eta(); + leadIdx = idx; + } + } + + const int nch = static_cast(selTracks.size()); + histos.fill(HIST("multHist_Reco"), nch); + + const int mBin = o2::analysis::findBin(&MultEdges, nch); + if (mBin < 0) { + return; + } + histos.fill(HIST("evSel_Reco"), EventCutMultBin); + + const int zBin = o2::analysis::findBin(&ZvtxEdges, zvtx); + if (zBin < 0) { + return; + } + histos.fill(HIST("evSel_Reco"), EventCutZvtxBin); + + // ---- leading particle pT cut [minLeadPt, maxLeadPt] ----------- + if (leadIdx < 0) { + return; + } + if (pTlead < minLeadPt || pTlead > maxLeadPt) { + return; + } + histos.fill(HIST("evSel_Reco"), EventCutLeadPt); + histos.fill(HIST("ptLeadHistogram_Reco"), pTlead); + + // ---- sphericity --------------------------------------------- + const double sph = computeSphericity(selTracks); + if (sph >= 0.) { + histos.fill(HIST("sphericity_beforeCuts_Reco"), sph); + } + if (sph < 0.) { + return; + } + histos.fill(HIST("evSel_Reco"), EventCutSphericity); + histos.fill(HIST("sphericity_Reco"), sph); + hSphMultReco[mBin]->Fill(sph); + + const int sphCls = sphClass(sph); + if (sphCls < 0) { + return; + } + histos.fill(HIST("evSel_Reco"), EventCutSphClass); + + const PidSpecies pidLead = pidFromPDG(selTracks[leadIdx].pdg); + + // ---- trigger counter ---------------------------------------- + hNtrigReco->Fill(static_cast(mBin), static_cast(sphCls)); + hNtrigReco->Fill(static_cast(mBin), static_cast((NSphClasses - 1))); + + // ================================================================ + // SAME-EVENT correlations (identical logic to processMC) + // ================================================================ + bool seHasPairs = false; + for (int ia = 0; ia < nch; ++ia) { + if (ia == leadIdx) { + continue; + } + const auto& assoc = selTracks[ia]; + if (assoc.pt >= pTlead) { + continue; + } + if (assoc.pt < minAssocCorr || assoc.pt > maxAssocCorr) { + continue; + } + + histos.fill(HIST("ptAssocHistogram_Reco"), assoc.pt); + + const PidSpecies pidAssoc = pidFromPDG(assoc.pdg); + + const double dphi = RecoDecay::constrainAngle(phiLead - assoc.phi, -o2::constants::math::PIHalf); + const double deta = etaLead - assoc.eta; + + // All-species SE (Delta-eta) + hSeReco[sphCls][mBin]->Fill(dphi, deta); + hSeReco[NSphClasses - 1][mBin]->Fill(dphi, deta); + + // PID SE — same species, Delta-eta (pseudorapidity-only regime) + int pidLeadIdx = pidSpeciesToIdx(pidLead); + int pidAssocIdx = pidSpeciesToIdx(pidAssoc); + if (pidLeadIdx >= 0 && pidLeadIdx == pidAssocIdx) { + hSePidReco[pidLeadIdx][sphCls][mBin]->Fill(dphi, deta); + hSePidReco[pidLeadIdx][NSphClasses - 1][mBin]->Fill(dphi, deta); + } + + seHasPairs = true; + } + if (seHasPairs) { + histos.fill(HIST("evSel_Reco"), EventCutSeFilled); + } + + // ---- hNtrigReco per PID species leading -------------------------- + int pidLeadIdx = pidSpeciesToIdx(pidLead); + if (pidLeadIdx >= 0) { + hNtrigPidReco[pidLeadIdx]->Fill(static_cast(mBin), static_cast(sphCls)); + hNtrigPidReco[pidLeadIdx]->Fill(static_cast(mBin), static_cast((NSphClasses - 1))); + } + + // ================================================================ + // Update pools AFTER SE (identical to processMC) + // ================================================================ + { + auto& pool = eventPoolsReco[sphCls][zBin][mBin]; + pool.push_back(selTracks); + if (static_cast(pool.size()) > mixPoolSize) { + pool.pop_front(); + } + } + { + auto& poolAll = eventPoolsReco[NSphClasses - 1][zBin][mBin]; + poolAll.push_back(selTracks); + if (static_cast(poolAll.size()) > mixPoolSize) { + poolAll.pop_front(); + } + } + + for (int ip = 0; ip < NPidCorr; ++ip) { + std::vector pidTracks; + for (const auto& t : selTracks) { + if (pidSpeciesToIdx(pidFromPDG(t.pdg)) == ip) { + pidTracks.push_back(t); + } + } + if (pidTracks.empty()) { + continue; + } + { + auto& pool = eventPoolsPidReco[ip][sphCls][zBin][mBin]; + pool.push_back(pidTracks); + if (static_cast(pool.size()) > mixPoolSize) { + pool.pop_front(); + } + } + { + auto& pool = eventPoolsPidReco[ip][NSphClasses - 1][zBin][mBin]; + pool.push_back(pidTracks); + if (static_cast(pool.size()) > mixPoolSize) { + pool.pop_front(); + } + } + } + + // ================================================================ + // MIXED-EVENT "all" (Delta-eta) + // ================================================================ + auto doMixing = [&](int poolIdx) { + const auto& pool = eventPoolsReco[poolIdx][zBin][mBin]; + const int nEvts = static_cast(pool.size()) - 1; + for (int ie = 0; ie < nEvts; ++ie) { + for (const auto& tr : pool[ie]) { + if (tr.pt >= pTlead) { + continue; + } + if (tr.pt < minAssocCorr || tr.pt > maxAssocCorr) { + continue; + } + const double dphi = RecoDecay::constrainAngle(phiLead - tr.phi, -o2::constants::math::PIHalf); + const double deta = etaLead - tr.eta; + hMeReco[poolIdx][mBin]->Fill(dphi, deta); + } + } + }; + + doMixing(sphCls); + doMixing(NSphClasses - 1); + + // ================================================================ + // MIXED-EVENT PID (Delta-eta, pseudorapidity-only regime) + // ================================================================ + if (pidLeadIdx >= 0) { + auto doMixingPID = [&](int poolIdx) { + const auto& pool = eventPoolsPidReco[pidLeadIdx][poolIdx][zBin][mBin]; + const int nEvts = static_cast(pool.size()) - 1; + for (int ie = 0; ie < nEvts; ++ie) { + for (const auto& tr : pool[ie]) { + if (tr.pt >= pTlead) { + continue; + } + if (tr.pt < minAssocCorr || tr.pt > maxAssocCorr) { + continue; + } + const double dphi = RecoDecay::constrainAngle(phiLead - tr.phi, -o2::constants::math::PIHalf); + const double detaPID = etaLead - tr.eta; + hMePidReco[pidLeadIdx][poolIdx][mBin]->Fill(dphi, detaPID); + } + } + }; + doMixingPID(sphCls); + doMixingPID(NSphClasses - 1); + } + } + PROCESS_SWITCH(TwoParticleCorrelationsMultSpher, processReco, "Process reconstructed data", false); + + // ---------------------------------------------------------------- + // processMCConfusion -- PID contamination estimate. Fills hPidConfusionMC (pT, true species, tagged + // species) on MC only -- needs BOTH the truth PDG (via McTrackLabels) + // AND the reconstructed track's nSigma values on the SAME track, + // which neither processMC (truth only) nor processReco (no truth + // link) has access to, hence a dedicated process function. Reports + // contamination as a systematic; does NOT correct anything (no + // unfolding is implemented). Off by default, like the other MC-only diagnostics. + // ---------------------------------------------------------------- + using TrackRecoMC = soa::Join; + + void processMCConfusion(ColReco::iterator const& col, + TrackRecoMC const& tracks, + aod::McParticles const&) + { + if (!col.sel8()) { + return; + } + if (std::fabs(col.posZ()) > maxZvtx) { + return; + } + + for (const auto& track : tracks) { + if (!track.has_mcParticle()) { + continue; + } + auto mcPart = track.mcParticle(); + if (!mcPart.isPhysicalPrimary()) { + continue; + } + auto* pdgPtr = pdg->GetParticle(mcPart.pdgCode()); + if (!pdgPtr || std::fabs(pdgPtr->Charge()) < MinCharge) { + continue; + } + + // Same track-level cuts as processReco, so the matrix reflects the + // actual analyzed track population, not an unfiltered sample. + if (std::fabs(track.dcaXY()) > maxDcaXY) { + continue; + } + if (std::fabs(track.dcaZ()) > maxDcaZ) { + continue; + } + if (std::fabs(track.eta()) > maxEta) { + continue; + } + if (track.pt() < minAssocPt) { + continue; + } + + PidSpecies truePid = pidFromPDG(mcPart.pdgCode()); + PidSpecies taggedPid = pidFromNSigma(track, maxNSigmaPID, maxNSigmaPIDAmbig, + maxNSigmaTPC, maxNSigmaTOF, + useLegacyNSigmaGating, useLegacyPIDSelection); + + // Same rapidity cut as processReco applies to its "tagged" PID -- + // without this, applyRapidityCutPID=false would silently have zero + // effect on this matrix even though it changes the processReco yields. + if (applyRapidityCutPID && taggedPid != PidUnidentified) { + double tagMass = pidMass(taggedPid); + double tagRap = computeRapidity(track.pt(), track.eta(), tagMass); + if (std::fabs(tagRap) > maxRapPID) { + taggedPid = PidUnidentified; + } + } + + histos.fill(HIST("hPidConfusion_MC"), track.pt(), static_cast(truePid), static_cast(taggedPid)); + } + } + PROCESS_SWITCH(TwoParticleCorrelationsMultSpher, processMCConfusion, "Rapid PID contamination estimate: true (PDG) vs tagged (nSigma) species confusion matrix, MC only", false); + +}; // end struct + +// ---------------------------------------------------------------- +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGDQ/Core/AnalysisCompositeCut.h b/PWGDQ/Core/AnalysisCompositeCut.h index 7e78faebfd6..a9482c3cb91 100644 --- a/PWGDQ/Core/AnalysisCompositeCut.h +++ b/PWGDQ/Core/AnalysisCompositeCut.h @@ -27,7 +27,7 @@ class AnalysisCompositeCut : public AnalysisCut { public: - AnalysisCompositeCut(bool useAND = kTRUE); + explicit AnalysisCompositeCut(bool useAND = kTRUE); AnalysisCompositeCut(const char* name, const char* title, bool useAND = kTRUE); AnalysisCompositeCut(const AnalysisCompositeCut& c); AnalysisCompositeCut& operator=(const AnalysisCompositeCut& c); diff --git a/PWGDQ/Core/CMakeLists.txt b/PWGDQ/Core/CMakeLists.txt index 835c14c17ae..265d1c76be2 100644 --- a/PWGDQ/Core/CMakeLists.txt +++ b/PWGDQ/Core/CMakeLists.txt @@ -22,3 +22,41 @@ o2physics_add_library(PWGDQCore MCProng.cxx MCSignal.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::DCAFitter O2::GlobalTracking O2Physics::AnalysisCore KFParticle::KFParticle O2Physics::MLCore) + +# Precompiled header shared across PWGDQ. Percentages are the share of the +# group's workflow translation units that include the header, measured over the tree. +# VarManager.h alone is ~8000 lines and is parsed by two thirds of the group. +# +# runDataProcessing.h is deliberately NOT precompiled: it requires each workflow's +# customize() to be declared before it is included, so precompiling it silently +# unregisters every option (the binary aborts at --dump-workflow, but links fine). +# +# Skipped under recc, which caches compilations remotely instead. +add_library(DQAnalysisPCH OBJECT dqAnalysisPCH.cxx) +# The PCH compile is one of the heaviest translation units in the tree -- it +# instantiates the whole data model up front -- but add_library() does not put +# it in the 'analysis' job pool that o2physics_add_dpl_workflow() uses for the +# workflows. Left out, it runs unthrottled alongside the pooled compilations +# and defeats the per-job memory budget the pool exists to enforce. +set_property(TARGET DQAnalysisPCH PROPERTY JOB_POOL_COMPILE analysis) +target_link_libraries(DQAnalysisPCH PUBLIC O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore) +if(NOT DEFINED ENV{USE_RECC}) + target_precompile_headers(DQAnalysisPCH PRIVATE + # Framework baseline -- same set AnalysisPCH precompiles. A target with + # REUSE_FROM uses ONLY this PCH, so anything left out here is parsed from + # scratch by every consumer: without this block these workflows precompile + # LESS than the default carrier they opted out of. + + + + + + + + + + + [["PWGDQ/DataModel/ReducedInfoTables.h"]] # 65% + [["PWGDQ/Core/VarManager.h"]] # 65%, ~8000 lines + ) +endif() diff --git a/PWGDQ/Core/CutsLibrary.cxx b/PWGDQ/Core/CutsLibrary.cxx index 0974eab27a4..8c617a96e59 100644 --- a/PWGDQ/Core/CutsLibrary.cxx +++ b/PWGDQ/Core/CutsLibrary.cxx @@ -3660,6 +3660,11 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } + if (!nameStr.compare("pairX3872Minitree")) { + cut->AddCut(GetAnalysisCut("pairX3872_minitree")); + return cut; + } + if (nameStr == "DipionPairCut1") { cut->AddCut(GetAnalysisCut("DipionMassCut1")); return cut; @@ -3697,6 +3702,11 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } + if (nameStr == "pairD0Topology") { + cut->AddCut(GetAnalysisCut("pairD0Topology")); + return cut; + } + if (nameStr == "pairD0HighPt1") { cut->AddCut(GetAnalysisCut("pairLxyzProjected3sigma")); cut->AddCut(GetAnalysisCut("pairPtLow5")); @@ -3918,6 +3928,36 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } + if (nameStr == "alice3StandardTrackD0") { + cut->AddCut(GetAnalysisCut("alice3StandardKineD0")); + cut->AddCut(GetAnalysisCut("alice3TrackQuality")); + return cut; + } + + if (nameStr == "alice3TOFEta") { + cut->AddCut(GetAnalysisCut("alice3TOFEta")); + cut->AddCut(GetAnalysisCut("alice3TrackQualityHF")); + return cut; + } + + if (nameStr == "alice3RICHEta") { + cut->AddCut(GetAnalysisCut("alice3RICHEta")); + cut->AddCut(GetAnalysisCut("alice3TrackQualityHF")); + return cut; + } + + if (nameStr == "alice3StandardTrackTOFAcceptanceD0") { + cut->AddCut(GetAnalysisCut("alice3KineTOFAcceptanceD0")); + cut->AddCut(GetAnalysisCut("alice3TrackQuality")); + return cut; + } + + if (nameStr == "alice3StandardTrackRICHAcceptanceD0") { + cut->AddCut(GetAnalysisCut("alice3KineRICHAcceptanceD0")); + cut->AddCut(GetAnalysisCut("alice3TrackQuality")); + return cut; + } + if (nameStr == "alice3TightDCATrack") { cut->AddCut(GetAnalysisCut("alice3StandardKine")); cut->AddCut(GetAnalysisCut("alice3TrackQualityTightDCA")); @@ -4000,6 +4040,32 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } + if (nameStr == "alice3EitherTOFPIDPi") { + auto* pidCut = new AnalysisCompositeCut( + "alice3EitherTOFPIDPi", + "iTOF OR oTOF pion PID", + kFALSE); + + pidCut->AddCut(GetAnalysisCut("alice3iTOFPIDPi")); + pidCut->AddCut(GetAnalysisCut("alice3oTOFPIDPi")); + + delete cut; + return pidCut; + } + + if (nameStr == "alice3EitherTOFPIDKa") { + auto* pidCut = new AnalysisCompositeCut( + "alice3EitherTOFPIDKa", + "iTOF OR oTOF kaon PID", + kFALSE); + + pidCut->AddCut(GetAnalysisCut("alice3iTOFPIDKa")); + pidCut->AddCut(GetAnalysisCut("alice3oTOFPIDKa")); + + delete cut; + return pidCut; + } + if (nameStr == "alice3RICHPIDEl") { cut->AddCut(GetAnalysisCut("alice3RICHPIDEl")); return cut; @@ -4048,6 +4114,30 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } + if (nameStr == "alice3KaonTOFRICH") { + cut->AddCut(GetAnalysisCut("alice3TrackQuality")); + cut->AddCut(GetAnalysisCut("alice3KaonTOFRICH")); + return cut; + } + + if (nameStr == "alice3PionTOFRICH") { + cut->AddCut(GetAnalysisCut("alice3TrackQuality")); + cut->AddCut(GetAnalysisCut("alice3PionTOFRICH")); + return cut; + } + + if (nameStr == "alice3ProtonTOFRICH") { + cut->AddCut(GetAnalysisCut("alice3TrackQuality")); + cut->AddCut(GetAnalysisCut("alice3ProtonTOFRICH")); + return cut; + } + + if (nameStr == "alice3ElectronTOFRICH") { + cut->AddCut(GetAnalysisCut("alice3TrackQuality")); + cut->AddCut(GetAnalysisCut("alice3ElectronTOFRICH")); + return cut; + } + if (nameStr == "alice3DielectronPID") { cut->AddCut(GetAnalysisCut("alice3JpsiKine")); cut->AddCut(GetAnalysisCut("alice3TrackQuality")); @@ -4103,6 +4193,32 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } + if (nameStr == "alice3TOFPIDPi") { + cut->AddCut(GetAnalysisCut("alice3iTOFPIDPi")); + cut->AddCut(GetAnalysisCut("alice3oTOFPIDPi")); + return cut; + } + + if (nameStr == "alice3TOFPIDKaRej") { + cut->AddCut(GetAnalysisCut("alice3D0KaonTOFPID")); + return cut; + } + + if (nameStr == "alice3TOFPIDPiRej") { + cut->AddCut(GetAnalysisCut("alice3D0PionTOFPID")); + return cut; + } + + if (nameStr == "alice3RICHTOFPIDPiRej") { + cut->AddCut(GetAnalysisCut("alice3D0PionRICHTOFPID")); + return cut; + } + + if (nameStr == "alice3RICHTOFPIDKaRej") { + cut->AddCut(GetAnalysisCut("alice3D0KaonRICHTOFPID")); + return cut; + } + delete cut; LOGF(fatal, Form("Did not find cut %s. Returning nullptr", cutName)); return nullptr; @@ -7078,6 +7194,31 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } + if (nameStr == "pairD0Topology") { + cut->AddCut(VarManager::kCosPointingAngle, 0.8, 1.0, false, VarManager::kPt, 0.0, 1.0); + cut->AddCut(VarManager::kCosPointingAngle, 0.9, 1.0, false, VarManager::kPt, 1.0, 4.0); + cut->AddCut(VarManager::kCosPointingAngle, 0.85, 1.0, false, VarManager::kPt, 4.0, 50.0); + cut->AddCut(VarManager::kD0DcaDaughters, 0.0, 0.01, false, VarManager::kPt, 0.0, 50.0); + cut->AddCut(VarManager::kVertexingLxy, 0.01, 10.0); + cut->AddCut(VarManager::kRap, -2.0, 2.0); + return cut; + } + + if (nameStr == "pairLambdaCTopology") { + cut->AddCut(VarManager::kPt, 0.0, 50.0); + cut->AddCut(VarManager::kRap, -2.0, 2.0); + cut->AddCut(VarManager::kVertexingProcCode, 0.5, 1.e6); + cut->AddCut(VarManager::kCosPointingAngle, 0.80, 1.0, false, VarManager::kPt, 0.0, 1.0); + cut->AddCut(VarManager::kCosPointingAngle, 0.90, 1.0, false, VarManager::kPt, 1.0, 4.0); + cut->AddCut(VarManager::kCosPointingAngle, 0.95, 1.0, false, VarManager::kPt, 4.0, 50.0); + cut->AddCut(VarManager::kVertexingLxy, 0.0, 10.0); + cut->AddCut(VarManager::kVertexingLxy, 0.002, 10.0, false, VarManager::kPt, 0.0, 1.0); + cut->AddCut(VarManager::kVertexingLxy, 0.003, 10.0, false, VarManager::kPt, 1.0, 4.0); + cut->AddCut(VarManager::kVertexingLxy, 0.005, 10.0, false, VarManager::kPt, 4.0, 50.0); + + return cut; + } + if (nameStr == "pairJpsi") { cut->AddCut(VarManager::kMass, 2.8, 3.3); return cut; @@ -7124,6 +7265,19 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } + std::shared_ptr fDeltaR2High = std::make_shared("fDeltaR2High", "[0]+[1]*x", 0.0, 5.0); + fDeltaR2High->SetParameters(3.0, -0.4); + if (!nameStr.compare("pairX3872_minitree")) { + cut->AddCut(VarManager::kRap, -0.8, 0.8); + cut->AddCut(VarManager::kQuadDefaultDileptonMass, 3.0, 5.0); + cut->AddCut(VarManager::kDeltaR2, 0.0, 1.0, false, VarManager::kPt, 5.0, 1000.0); + cut->AddCut(VarManager::kDeltaR2, 0.0, fDeltaR2High, false, VarManager::kPt, 0.0, 5.0); + cut->AddCut(VarManager::kQuadPt, 0.0, 1000.0); + cut->AddCut(VarManager::kVertexingProcCode, 0.5, 2.5); + cut->AddCut(VarManager::kVertexingQuadProcCode, 0.5, 2.5); + return cut; + } + if (nameStr == "pairPtLow1") { cut->AddCut(VarManager::kPt, 2.0, 1000.0); return cut; @@ -7321,6 +7475,36 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } + if (nameStr == "alice3StandardKineD0") { + cut->AddCut(VarManager::kPt, 0.7, 1000.0); + cut->AddCut(VarManager::kEta, -2.5, 2.5); // Total tracker acceptance in v3b geomety + return cut; + } + + if (nameStr == "alice3TOFEta") { + cut->AddCut(VarManager::kPt, 0.3, 1000.0); + cut->AddCut(VarManager::kEta, -2.0, 2.0); // Total tracker acceptance in v3b geomety + return cut; + } + + if (nameStr == "alice3RICHEta") { + cut->AddCut(VarManager::kPt, 0.3, 1000.0); + cut->AddCut(VarManager::kEta, -0.8, 0.8); // Total tracker acceptance in v3b geomety + return cut; + } + + if (nameStr == "alice3KineTOFAcceptanceD0") { + cut->AddCut(VarManager::kPt, 0.7, 1000.0); + cut->AddCut(VarManager::kEta, -2.0, 2.0); // TOF acceptance in v3b geomety + return cut; + } + + if (nameStr == "alice3KineRICHAcceptanceD0") { + cut->AddCut(VarManager::kPt, 0.7, 1000.0); + cut->AddCut(VarManager::kEta, -0.8, 0.8); // TOF acceptance in v3b geomety + return cut; + } + if (nameStr == "alice3LambdaCKine") { cut->AddCut(VarManager::kPt, 0.2, 1000.0); cut->AddCut(VarManager::kEta, -2.0, 2.0); @@ -7351,6 +7535,14 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } + if (nameStr == "alice3TrackQualityHF") { + cut->AddCut(VarManager::kIsReconstructed, 0.5, 1.5); + cut->AddCut(VarManager::kNSiliconHits, 5.0, 12.0); + cut->AddCut(VarManager::kTrackDCAxy, -0.05, 0.05); + cut->AddCut(VarManager::kTrackDCAz, -0.05, 0.05); + return cut; + } + if (nameStr == "alice3TrackQualityTightDCA") { cut->AddCut(VarManager::kIsReconstructed, 0.5, 1.5); cut->AddCut(VarManager::kNSiliconHits, 6.0, 12.0); @@ -7380,42 +7572,170 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) } if (nameStr == "alice3iTOFPIDEl") { - cut->AddCut(VarManager::kInnerTOFnSigmaEl, -3.0, 3.0); + cut->AddCut(VarManager::kInnerTOFnSigmaEl, -3.0, 3.0, false, VarManager::kP, 0.0, 1.5); return cut; } if (nameStr == "alice3iTOFPIDPi") { - cut->AddCut(VarManager::kInnerTOFnSigmaPi, -3.0, 3.0); + cut->AddCut(VarManager::kInnerTOFnSigmaPi, -3.0, 3.0, false, VarManager::kP, 0.0, 1.5); return cut; } if (nameStr == "alice3iTOFPIDKa") { - cut->AddCut(VarManager::kInnerTOFnSigmaKa, -3.0, 3.0); + cut->AddCut(VarManager::kInnerTOFnSigmaKa, -3.0, 3.0, false, VarManager::kP, 0.0, 1.5); return cut; } if (nameStr == "alice3iTOFPIDPr") { - cut->AddCut(VarManager::kInnerTOFnSigmaPr, -3.0, 3.0); + cut->AddCut(VarManager::kInnerTOFnSigmaPr, -3.0, 3.0, false, VarManager::kP, 0.0, 1.5); return cut; } if (nameStr == "alice3oTOFPIDEl") { - cut->AddCut(VarManager::kOuterTOFnSigmaEl, -3.0, 3.0); + cut->AddCut(VarManager::kOuterTOFnSigmaEl, -3.0, 3.0, false, VarManager::kP, 0.0, 2.5); return cut; } if (nameStr == "alice3oTOFPIDPi") { - cut->AddCut(VarManager::kOuterTOFnSigmaPi, -3.0, 3.0); + cut->AddCut(VarManager::kOuterTOFnSigmaPi, -3.0, 3.0, false, VarManager::kP, 0.0, 2.5); return cut; } if (nameStr == "alice3oTOFPIDKa") { - cut->AddCut(VarManager::kOuterTOFnSigmaKa, -3.0, 3.0); + cut->AddCut(VarManager::kOuterTOFnSigmaKa, -3.0, 3.0, false, VarManager::kP, 0.0, 2.5); return cut; } if (nameStr == "alice3oTOFPIDPr") { - cut->AddCut(VarManager::kOuterTOFnSigmaPr, -3.0, 3.0); + cut->AddCut(VarManager::kOuterTOFnSigmaPr, -3.0, 3.0, false, VarManager::kP, 0.0, 2.5); + return cut; + } + + if (nameStr == "alice3D0KaonTOFPID") { + cut->AddCut(VarManager::kInnerTOFnSigmaKa, -3.0, 3.0, false, VarManager::kP, 0.0, 1.5); + cut->AddCut(VarManager::kOuterTOFnSigmaKa, -3.0, 3.0, false, VarManager::kP, 0.0, 2.5); + + cut->AddCut(VarManager::kInnerTOFnSigmaPi, -3.0, 3.0, true, VarManager::kP, 0.0, 1.5); + cut->AddCut(VarManager::kInnerTOFnSigmaPr, -3.0, 3.0, true, VarManager::kP, 0.0, 1.5); + + cut->AddCut(VarManager::kOuterTOFnSigmaPi, -3.0, 3.0, true, VarManager::kP, 0.0, 2.5); + cut->AddCut(VarManager::kOuterTOFnSigmaPr, -3.0, 3.0, true, VarManager::kP, 0.0, 2.5); + return cut; + } + + if (nameStr == "alice3D0PionTOFPID") { + cut->AddCut(VarManager::kInnerTOFnSigmaPi, -3.0, 3.0, false, VarManager::kP, 0.0, 1.5); + cut->AddCut(VarManager::kOuterTOFnSigmaPi, -3.0, 3.0, false, VarManager::kP, 0.0, 2.5); + + cut->AddCut(VarManager::kInnerTOFnSigmaKa, -3.0, 3.0, true, VarManager::kP, 0.0, 1.5); + cut->AddCut(VarManager::kInnerTOFnSigmaPr, -3.0, 3.0, true, VarManager::kP, 0.0, 1.5); + + cut->AddCut(VarManager::kOuterTOFnSigmaKa, -3.0, 3.0, true, VarManager::kP, 0.0, 2.5); + cut->AddCut(VarManager::kOuterTOFnSigmaPr, -3.0, 3.0, true, VarManager::kP, 0.0, 2.5); + return cut; + } + + if (nameStr == "alice3D0KaonRICHTOFPID") { + cut->AddCut(VarManager::kInnerTOFnSigmaKa, -3.0, 3.0, false, VarManager::kP, 0.0, 1.5); + cut->AddCut(VarManager::kOuterTOFnSigmaKa, -3.0, 3.0, false, VarManager::kP, 0.0, 2.5); + cut->AddCut(VarManager::kRICHnSigmaKa, -3.0, 3.0, false, VarManager::kP, 2.0, 1000.0, false, VarManager::kHasRICHSigKa, 0.5, 1.5); + + cut->AddCut(VarManager::kInnerTOFnSigmaPi, -3.0, 3.0, true, VarManager::kP, 0.0, 1.5); + cut->AddCut(VarManager::kInnerTOFnSigmaPr, -3.0, 3.0, true, VarManager::kP, 0.0, 1.5); + + cut->AddCut(VarManager::kOuterTOFnSigmaPi, -3.0, 3.0, true, VarManager::kP, 0.0, 2.5); + cut->AddCut(VarManager::kOuterTOFnSigmaPr, -3.0, 3.0, true, VarManager::kP, 0.0, 2.5); + + cut->AddCut(VarManager::kRICHnSigmaPi, -3.0, 3.0, true, VarManager::kP, 0.57, 1000.0, false, VarManager::kRICHnSigmaPi, 0.5, 1.5); + cut->AddCut(VarManager::kRICHnSigmaPr, -3.0, 3.0, true, VarManager::kP, 3.8, 1000.0, false, VarManager::kRICHnSigmaPr, 0.5, 1.5); + return cut; + } + + if (nameStr == "alice3D0PionRICHTOFPID") { + cut->AddCut(VarManager::kInnerTOFnSigmaPi, -3.0, 3.0, false, VarManager::kP, 0.0, 1.5); + cut->AddCut(VarManager::kOuterTOFnSigmaPi, -3.0, 3.0, false, VarManager::kP, 0.0, 2.5); + cut->AddCut(VarManager::kRICHnSigmaPi, -3.0, 3.0, false, VarManager::kP, 0.57, 1000.0, false, VarManager::kHasRICHSigKa, 0.5, 1.5); + + cut->AddCut(VarManager::kInnerTOFnSigmaKa, -3.0, 3.0, true, VarManager::kP, 0.0, 1.5); + cut->AddCut(VarManager::kInnerTOFnSigmaPr, -3.0, 3.0, true, VarManager::kP, 0.0, 1.5); + + cut->AddCut(VarManager::kOuterTOFnSigmaKa, -3.0, 3.0, true, VarManager::kP, 0.0, 2.5); + cut->AddCut(VarManager::kOuterTOFnSigmaPr, -3.0, 3.0, true, VarManager::kP, 0.0, 2.5); + + cut->AddCut(VarManager::kRICHnSigmaKa, -3.0, 3.0, true, VarManager::kP, 2.0, 1000.0, false, VarManager::kRICHnSigmaPi, 0.5, 1.5); + cut->AddCut(VarManager::kRICHnSigmaPr, -3.0, 3.0, true, VarManager::kP, 3.8, 1000.0, false, VarManager::kRICHnSigmaPr, 0.5, 1.5); + return cut; + } + + if (nameStr == "alice3KaonTOFRICH") { + cut->AddCut(VarManager::kEta, -2.0, 2.0); + cut->AddCut(VarManager::kP, 0.3, 1000.0); + + cut->AddCut(VarManager::kTOFEventTimeErr, 0.0, 30.0, false, VarManager::kP, 0.0, 2.0); + cut->AddCut(VarManager::kOuterTOFnSigmaKa, -3.0, 3.0, false, VarManager::kP, 0.0f, 2.0); + + cut->AddCut(VarManager::kOuterTOFnSigmaPi, -2.0, 2.0, true, VarManager::kP, 0.0, 1.9); + cut->AddCut(VarManager::kOuterTOFnSigmaPr, -2.0, 2.0, true, VarManager::kP, 0.0, 3.2); + cut->AddCut(VarManager::kOuterTOFnSigmaEl, -2.0, 2.0, true, VarManager::kP, 0.0, 2.0); + + cut->AddCut(VarManager::kEta, -0.8, 0.8, false, VarManager::kP, 2.0, 1000.0); + cut->AddCut(VarManager::kRICHnSigmaKa, 0.5, 1.5, false, VarManager::kP, 2.0, 1000.0); + cut->AddCut(VarManager::kRICHnSigmaKa, -3.0, 3.0, false, VarManager::kP, 2.0, 1000.0); + + return cut; + } + + if (nameStr == "alice3PionTOFRICH") { + cut->AddCut(VarManager::kEta, -2.0, 2.0); + cut->AddCut(VarManager::kP, 0.3, 1000.0); + + cut->AddCut(VarManager::kTOFEventTimeErr, 0.0, 30.0, false, VarManager::kP, 0.0, 2.0); + cut->AddCut(VarManager::kOuterTOFnSigmaPi, -3.0, 3.0, false, VarManager::kP, 0.0, 2.0); + + cut->AddCut(VarManager::kOuterTOFnSigmaKa, -2.0, 2.0, true, VarManager::kP, 0.0, 1.9); + cut->AddCut(VarManager::kOuterTOFnSigmaPr, -2.0, 2.0, true, VarManager::kP, 0.0, 3.8); + cut->AddCut(VarManager::kOuterTOFnSigmaEl, -2.0, 2.0, true, VarManager::kP, 0.0, 0.55); + + cut->AddCut(VarManager::kEta, -0.8, 0.8, false, VarManager::kP, 2.0, 1000.0); + cut->AddCut(VarManager::kRICHnSigmaPi, 0.5, 1.5, false, VarManager::kP, 2.0, 1000.0); + cut->AddCut(VarManager::kRICHnSigmaPi, -3.0, 3.0, false, VarManager::kP, 2.0, 1000.0); + + return cut; + } + + if (nameStr == "alice3ProtonTOFRICH") { + cut->AddCut(VarManager::kEta, -2.0, 2.0); + cut->AddCut(VarManager::kP, 0.3f, 1000.0); + + cut->AddCut(VarManager::kTOFEventTimeErr, 0.0f, 30.0f, false, VarManager::kP, 0.0f, 4.5); + cut->AddCut(VarManager::kOuterTOFnSigmaPr, -3.0f, 3.0f, false, VarManager::kP, 0.0f, 4.5); + + cut->AddCut(VarManager::kOuterTOFnSigmaKa, -2.0, 2.0, true, VarManager::kP, 0.0, 3.2); + cut->AddCut(VarManager::kOuterTOFnSigmaPi, -2.0, 2.0, true, VarManager::kP, 0.0, 3.8); + cut->AddCut(VarManager::kOuterTOFnSigmaEl, -2.0, 2.0, true, VarManager::kP, 0.0, 3.8); + + cut->AddCut(VarManager::kEta, -0.8, 0.8, false, VarManager::kP, 4.5, 1000.0); + cut->AddCut(VarManager::kHasRICHSigPr, 0.5f, 1.5f, false, VarManager::kP, 4.5, 1000.0); + cut->AddCut(VarManager::kRICHnSigmaPr, -3.0f, 3.0f, false, VarManager::kP, 4.5, 1000.0); + + return cut; + } + + if (nameStr == "alice3ElectronTOFRICH") { + cut->AddCut(VarManager::kEta, -2.0, 2.0); + cut->AddCut(VarManager::kP, 0.3f, 1000.0); + + cut->AddCut(VarManager::kTOFEventTimeErr, 0.0f, 30.0f, false, VarManager::kP, 0.0f, 0.6); + cut->AddCut(VarManager::kOuterTOFnSigmaEl, -3.0f, 3.0f, false, VarManager::kP, 0.0f, 0.6); + + cut->AddCut(VarManager::kOuterTOFnSigmaPi, -2.0f, 2.0f, true, VarManager::kP, 0.0f, 0.6); + cut->AddCut(VarManager::kOuterTOFnSigmaKa, -2.0f, 2.0f, true, VarManager::kP, 0.0f, 0.6); + cut->AddCut(VarManager::kOuterTOFnSigmaPr, -2.0f, 2.0f, true, VarManager::kP, 0.0f, 0.6); + + cut->AddCut(VarManager::kEta, -0.8, 0.8, false, VarManager::kP, 0.6, 1000.0); + cut->AddCut(VarManager::kHasRICHSigEl, 0.5f, 1.5f, false, VarManager::kP, 0.6, 1000.0); + cut->AddCut(VarManager::kRICHnSigmaEl, -3.0f, 3.0f, false, VarManager::kP, 0.6, 1000.0); + return cut; } @@ -7471,7 +7791,7 @@ std::vector o2::aod::dqcuts::GetCutsFromJSON(const char* json) // Check that the json is parsed correctly rapidjson::ParseResult ok = document.Parse(json); if (!ok) { - LOG(fatal) << "JSON parse error: " << rapidjson::GetParseErrorFunc(ok.Code()) << " (" << ok.Offset() << ")"; + LOG(fatal) << "JSON parse error: " << rapidjson::GetParseErrorFunc(ok.Code()) << " (" << ok.Offset() << ")"; // NOLINT(google-readability-casting) return cuts; } @@ -7855,7 +8175,7 @@ o2::aod::dqmlcuts::BdtScoreConfig o2::aod::dqmlcuts::GetBdtScoreCutsAndConfigFro // Check that the json is parsed correctly rapidjson::ParseResult ok = document.Parse(json); if (!ok) { - LOG(fatal) << "JSON parse error: " << rapidjson::GetParseErrorFunc(ok.Code()) << " (" << ok.Offset() << ")"; + LOG(fatal) << "JSON parse error: " << rapidjson::GetParseErrorFunc(ok.Code()) << " (" << ok.Offset() << ")"; // NOLINT(google-readability-casting) return {}; } diff --git a/PWGDQ/Core/HistogramsLibrary.cxx b/PWGDQ/Core/HistogramsLibrary.cxx index 662a89f514e..d4e5e3eb2a8 100644 --- a/PWGDQ/Core/HistogramsLibrary.cxx +++ b/PWGDQ/Core/HistogramsLibrary.cxx @@ -1306,14 +1306,28 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h std::array bins = {50, 30, 10, 20, 10}; std::array xmin = {2., 0., 0, -1., 0.0}; std::array xmax = {4., 3., 100, 1., 6.28}; - hm->AddHistogram(histClass, "MC_Dielectron_Mass_Pt_Cent_cosThetaHE", "", 5, varspTHE.data(), bins.data(), xmin.data(), xmax.data(), 0, -1, kFALSE); + hm->AddHistogram(histClass, "MC_Dielectron_Mass_Pt_Cent_cosThetaHE", "", 5, varspTHE.data(), bins.data(), xmin.data(), xmax.data(), nullptr, -1, kFALSE); + } + if (groupStr.CompareTo("polarization-dielectron-pbpb-midy-gjb1-gen") == 0) { + std::array varspTGJB1 = {VarManager::kMCMass, VarManager::kMCPt, VarManager::kMCEventCentrFT0C, VarManager::kMCCosThetaGJB1, VarManager::kMCPhiGJB1}; + std::array bins = {50, 30, 10, 20, 10}; + std::array xmin = {2., 0., 0, -1., 0.0}; + std::array xmax = {4., 3., 100, 1., 6.28}; + hm->AddHistogram(histClass, "MC_Dielectron_Mass_Pt_Cent_cosThetaGJB1", "", 5, varspTGJB1.data(), bins.data(), xmin.data(), xmax.data(), nullptr, -1, kFALSE); + } + if (groupStr.CompareTo("polarization-dielectron-pbpb-midy-gjb2-gen") == 0) { + std::array varspTGJB2 = {VarManager::kMCMass, VarManager::kMCPt, VarManager::kMCEventCentrFT0C, VarManager::kMCCosThetaGJB2, VarManager::kMCPhiGJB2}; + std::array bins = {50, 30, 10, 20, 10}; + std::array xmin = {2., 0., 0, -1., 0.0}; + std::array xmax = {4., 3., 100, 1., 6.28}; + hm->AddHistogram(histClass, "MC_Dielectron_Mass_Pt_Cent_cosThetaGJB2", "", 5, varspTGJB2.data(), bins.data(), xmin.data(), xmax.data(), nullptr, -1, kFALSE); } if (groupStr.CompareTo("polarization-dielectron-pbpb-midy-cs-gen") == 0) { std::array varspTCS = {VarManager::kMCMass, VarManager::kMCPt, VarManager::kMCEventCentrFT0C, VarManager::kMCCosThetaCS, VarManager::kMCPhiCS}; std::array bins = {50, 30, 10, 20, 10}; std::array xmin = {2., 0., 0, -1., 0.0}; std::array xmax = {4., 3., 100, 1., 6.28}; - hm->AddHistogram(histClass, "MC_Dielectron_Mass_Pt_Cent_cosThetaCS", "", 5, varspTCS.data(), bins.data(), xmin.data(), xmax.data(), 0, -1, kFALSE); + hm->AddHistogram(histClass, "MC_Dielectron_Mass_Pt_Cent_cosThetaCS", "", 5, varspTCS.data(), bins.data(), xmin.data(), xmax.data(), nullptr, -1, kFALSE); } if (groupStr.CompareTo("pair") == 0) { if (subGroupStr.Contains("cepf")) { @@ -1403,6 +1417,13 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h } hm->AddHistogram(histClass, "Mass_PtLong", "", false, 500, alice3MassBins.data(), VarManager::kMass, 20, pTBins.data(), VarManager::kPt); } + if (subGroupStr.Contains("alice3pair")) { + hm->AddHistogram(histClass, "CosinePointingAngle", "cos(#theta_{p})", false, 2000.0, -1.0, 1.0, VarManager::kCosPointingAngle); + hm->AddHistogram(histClass, "DCADaughters", "DCA_{dau}", false, 2000.0, -10.0, 10.0, VarManager::kD0DcaDaughters); + hm->AddHistogram(histClass, "ImpParProduct", "d_{k} #times d_{#pi}", false, 2000.0, -100.0, 100.0, VarManager::kD0ImpParProduct); + hm->AddHistogram(histClass, "DCAxy1", "DCA_{xy, 1}", false, 2000.0, -10.0, 10.0, VarManager::kDCAxy1); + hm->AddHistogram(histClass, "DCAxy2", "DCA_{xy, 2}", false, 2000.0, -10.0, 10.0, VarManager::kDCAxy2); + } if (subGroupStr.Contains("alice3pbpb")) { hm->AddHistogram(histClass, "Mass_Cent", "", false, 125, 0.0, 5.0, VarManager::kMass, 20, 0.0, 100.0, VarManager::kCent); hm->AddHistogram(histClass, "Pt_CentFT0C", "", false, 100, 0.0, 10.0, VarManager::kPt, 20, 0.0, 100.0, VarManager::kCent); @@ -1422,6 +1443,22 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h hm->AddHistogram(histClass, "Dielectron_Mass_Pt_Cent_cosThetaHE", "", 5, varsHEpbpb.data(), binspT.data(), xminpT.data(), xmaxpT.data(), nullptr, -1, kFALSE); } + if (subGroupStr.Contains("dielectron-polarization-gjb1-pbpb")) { + std::array varsGJB1pbpb = {VarManager::kMass, VarManager::kPt, VarManager::kCentFT0C, VarManager::kCosThetaGJB1, VarManager::kPhiGJB1}; + std::array binspT = {100, 30, 10, 10, 10}; + std::array xminpT = {2., 0., 0, -1., 0.0}; + std::array xmaxpT = {4.5, 3., 100, 1., 6.28}; + hm->AddHistogram(histClass, "Dielectron_Mass_Pt_Cent_cosThetaGJB1", "", 5, varsGJB1pbpb.data(), binspT.data(), xminpT.data(), xmaxpT.data(), nullptr, -1, kFALSE); + } + + if (subGroupStr.Contains("dielectron-polarization-gjb2-pbpb")) { + std::array varsGJB2pbpb = {VarManager::kMass, VarManager::kPt, VarManager::kCentFT0C, VarManager::kCosThetaGJB2, VarManager::kPhiGJB2}; + std::array binspT = {100, 30, 10, 10, 10}; + std::array xminpT = {2., 0., 0, -1., 0.0}; + std::array xmaxpT = {4.5, 3., 100, 1., 6.28}; + hm->AddHistogram(histClass, "Dielectron_Mass_Pt_Cent_cosThetaGJB2", "", 5, varsGJB2pbpb.data(), binspT.data(), xminpT.data(), xmaxpT.data(), nullptr, -1, kFALSE); + } + if (subGroupStr.Contains("dielectron-polarization-cs-pbpb")) { std::array varsCSpbpb = {VarManager::kMass, VarManager::kPt, VarManager::kCentFT0C, VarManager::kCosThetaCS, VarManager::kPhiCS}; std::array binspT = {100, 30, 10, 10, 10}; @@ -2758,7 +2795,7 @@ void o2::aod::dqhistograms::AddHistogramsFromJSON(HistogramManager* hm, const ch rapidjson::Document document; rapidjson::ParseResult ok = document.Parse(json); if (!ok) { - LOG(fatal) << "JSON parse error: " << rapidjson::GetParseErrorFunc(ok.Code()) << " (" << ok.Offset() << ")"; + LOG(fatal) << "JSON parse error: " << rapidjson::GetParseErrorFunc(ok.Code()) << " (" << ok.Offset() << ")"; // NOLINT(google-readability-casting) TString str = ""; for (int i = ok.Offset() - 30; i < static_cast(ok.Offset()) + 50; i++) { if ((i >= 0) && (i < static_cast(strlen(json)))) { diff --git a/PWGDQ/Core/MCSignal.h b/PWGDQ/Core/MCSignal.h index 476fb4b8751..89f98c27d6f 100644 --- a/PWGDQ/Core/MCSignal.h +++ b/PWGDQ/Core/MCSignal.h @@ -331,25 +331,26 @@ bool MCSignal::CheckProng(int i, bool checkSources, const T& track) break; } } - } /*else { // check generation in time - if (!currentMCParticle.has_daughters()) { - return false; - } - const auto& daughtersSlice = currentMCParticle.template daughters_as

(); - for (auto& d : daughtersSlice) { - if (!fProngs[i].fExcludePDGInHistory[k] && fProngs[i].ComparePDG(d.pdgCode(), fProngs[i].fPDGInHistory[k], true, fProngs[i].fExcludePDGInHistory[k])) { - pdgInHistory.emplace_back(d.pdgCode()); - break; - } - if (fProngs[i].fExcludePDGInHistory[k] && !fProngs[i].ComparePDG(d.pdgCode(), fProngs[i].fPDGInHistory[k], true, fProngs[i].fExcludePDGInHistory[k])) { - return false; - } - ith++; - if (ith > 10) { // need error message. Given pdg code was not found within 10 generations of the particles decay chain. - break; - } - } - }*/ + } + // else { // check generation in time + // if (!currentMCParticle.has_daughters()) { + // return false; + // } + // const auto& daughtersSlice = currentMCParticle.template daughters_as

(); + // for (auto& d : daughtersSlice) { + // if (!fProngs[i].fExcludePDGInHistory[k] && fProngs[i].ComparePDG(d.pdgCode(), fProngs[i].fPDGInHistory[k], true, fProngs[i].fExcludePDGInHistory[k])) { + // pdgInHistory.emplace_back(d.pdgCode()); + // break; + // } + // if (fProngs[i].fExcludePDGInHistory[k] && !fProngs[i].ComparePDG(d.pdgCode(), fProngs[i].fPDGInHistory[k], true, fProngs[i].fExcludePDGInHistory[k])) { + // return false; + // } + // ith++; + // if (ith > 10) { // need error message. Given pdg code was not found within 10 generations of the particles decay chain. + // break; + // } + // } + // } } if (pdgInHistory.size() != nIncludedPDG) { // vector has as many entries as mothers (daughters) defined for prong return false; diff --git a/PWGDQ/Core/MCSignalLibrary.cxx b/PWGDQ/Core/MCSignalLibrary.cxx index af5677d07d7..918addd1b29 100644 --- a/PWGDQ/Core/MCSignalLibrary.cxx +++ b/PWGDQ/Core/MCSignalLibrary.cxx @@ -1754,6 +1754,16 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) signal = new MCSignal(name, "Kaons from D+/- decays", {prong}, {-1}); return signal; } + if (nameStr == "KFromD0") { + MCProng prong(2, {321, Pdg::kD0}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); + signal = new MCSignal(name, "Kaons from D0 decays", {prong}, {-1}); + return signal; + } + if (nameStr == "PiFromD0") { + MCProng prong(2, {211, Pdg::kD0}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); + signal = new MCSignal(name, "Pions from D0 decays", {prong}, {-1}); + return signal; + } if (nameStr == "LambdaC") { MCProng prong(1, {Pdg::kLambdaCPlus}, {true}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "Lambda_c", {prong}, {-1}); @@ -2109,7 +2119,7 @@ std::vector o2::aod::dqmcsignals::GetMCSignalsFromJSON(const char* js str += json[i]; } } - LOG(fatal) << "JSON parse error: " << rapidjson::GetParseErrorFunc(ok.Code()) << " (" << ok.Offset() << ")" << " **** Parsing error is somewhere here: " << str.Data(); + LOG(fatal) << "JSON parse error: " << rapidjson::GetParseErrorFunc(ok.Code()) << " (" << ok.Offset() << ")" << " **** Parsing error is somewhere here: " << str.Data(); // NOLINT(google-readability-casting) return signals; } diff --git a/PWGDQ/Core/VarManager.cxx b/PWGDQ/Core/VarManager.cxx index d1b193ec12c..b85e284dd40 100644 --- a/PWGDQ/Core/VarManager.cxx +++ b/PWGDQ/Core/VarManager.cxx @@ -1131,6 +1131,14 @@ void VarManager::SetDefaultVarNames() fgVariableUnits[kDCAxy2] = "cm"; fgVariableNames[kDCAz2] = "DCA_{z}"; fgVariableUnits[kDCAz2] = "cm"; + fgVariableNames[kDCAxyOverSig1] = "DCA_{xy}/#sigma_{DCA_{xy}}"; + fgVariableUnits[kDCAxyOverSig1] = ""; + fgVariableNames[kDCAzOverSig1] = "DCA_{z}/#sigma_{DCA_{z}}"; + fgVariableUnits[kDCAzOverSig1] = ""; + fgVariableNames[kDCAxyOverSig2] = "DCA_{xy}/#sigma_{DCA_{xy}}"; + fgVariableUnits[kDCAxyOverSig2] = ""; + fgVariableNames[kDCAzOverSig2] = "DCA_{z}/#sigma_{DCA_{z}}"; + fgVariableUnits[kDCAzOverSig2] = ""; fgVariableNames[kTPCnSigmaEl] = "n #sigma_{e}^{TPC}"; fgVariableUnits[kTPCnSigmaEl] = ""; fgVariableNames[kTPCnSigmaEl_Corr] = "n #sigma_{e}^{TPC} Corr."; @@ -1273,6 +1281,18 @@ void VarManager::SetDefaultVarNames() fgVariableUnits[kMCPhiCS] = "rad"; fgVariableNames[kMCPhiTildeCS] = "MC #tilde{#varphi}_{CS}"; fgVariableUnits[kMCPhiTildeCS] = "rad"; + fgVariableNames[kMCCosThetaGJB1] = "MC cos#it{#theta}_{GJ1}"; + fgVariableUnits[kMCCosThetaGJB1] = ""; + fgVariableNames[kMCPhiGJB1] = "MC #varphi_{GJ1}"; + fgVariableUnits[kMCPhiGJB1] = "rad"; + fgVariableNames[kMCPhiTildeGJB1] = "MC #tilde{#varphi}_{GJ1}"; + fgVariableUnits[kMCPhiTildeGJB1] = "rad"; + fgVariableNames[kMCCosThetaGJB2] = "MC cos#it{#theta}_{GJ2}"; + fgVariableUnits[kMCCosThetaGJB2] = ""; + fgVariableNames[kMCPhiGJB2] = "MC #varphi_{GJ2}"; + fgVariableUnits[kMCPhiGJB2] = "rad"; + fgVariableNames[kMCPhiTildeGJB2] = "MC #tilde{#varphi}_{GJ2}"; + fgVariableUnits[kMCPhiTildeGJB2] = "rad"; fgVariableNames[kMCCosThetaPP] = "MC cos(#theta_{PP})"; fgVariableUnits[kMCCosThetaPP] = ""; fgVariableNames[kMCPhiPP] = "MC #varphi_{PP}"; @@ -1345,8 +1365,14 @@ void VarManager::SetDefaultVarNames() fgVariableUnits[kMCVertexingTauxyzProjected] = "ns"; fgVariableNames[kCosPointingAngle] = "cos(#theta_{pointing})"; fgVariableUnits[kCosPointingAngle] = ""; + fgVariableNames[kVertexingImpactParameter] = "d_{0}"; + fgVariableUnits[kVertexingImpactParameter] = "cm"; fgVariableNames[kMCCosPointingAngle] = "MC cos(#theta_{pointing})"; fgVariableUnits[kMCCosPointingAngle] = ""; + fgVariableNames[kD0DcaDaughters] = "DCA of D0 daughters"; + fgVariableUnits[kD0DcaDaughters] = ""; + fgVariableNames[kD0ImpParProduct] = "D0 impact parameter product"; + fgVariableUnits[kD0ImpParProduct] = ""; fgVariableNames[kVertexingPz] = "Pz Pair"; fgVariableUnits[kVertexingPz] = "GeV/c"; fgVariableNames[kVertexingSV] = "Secondary Vertexing z"; @@ -1715,6 +1741,18 @@ void VarManager::SetDefaultVarNames() fgVariableUnits[kPhiCS] = "rad."; fgVariableNames[kPhiTildeCS] = "#tilde{#varphi}_{CS}"; fgVariableUnits[kPhiTildeCS] = "rad."; + fgVariableNames[kCosThetaGJB1] = "cos#it{#theta}_{GJ1}"; + fgVariableUnits[kCosThetaGJB1] = ""; + fgVariableNames[kPhiGJB1] = "#varphi_{GJ1}"; + fgVariableUnits[kPhiGJB1] = "rad."; + fgVariableNames[kPhiTildeGJB1] = "#tilde{#varphi}_{GJ1}"; + fgVariableUnits[kPhiTildeGJB1] = "rad."; + fgVariableNames[kCosThetaGJB2] = "cos#it{#theta}_{GJ2}"; + fgVariableUnits[kCosThetaGJB2] = ""; + fgVariableNames[kPhiGJB2] = "#varphi_{GJ2}"; + fgVariableUnits[kPhiGJB2] = "rad."; + fgVariableNames[kPhiTildeGJB2] = "#tilde{#varphi}_{GJ2}"; + fgVariableUnits[kPhiTildeGJB2] = "rad."; fgVariableNames[kCosThetaPP] = "cos#it{#theta}_{PP}"; fgVariableUnits[kCosThetaPP] = ""; fgVariableNames[kPhiPP] = "#varphi_{PP}"; @@ -2178,6 +2216,9 @@ void VarManager::SetDefaultVarNames() fgVarNamesMap["kMCEventImpParam"] = kMCEventImpParam; fgVarNamesMap["kMCEventPlaneAngle"] = kMCEventPlaneAngle; fgVarNamesMap["kMCEventCentrFT0C"] = kMCEventCentrFT0C; + fgVarNamesMap["kMultMCNParticlesEta05"] = kMultMCNParticlesEta05; + fgVarNamesMap["kMultMCNParticlesEta08"] = kMultMCNParticlesEta08; + fgVarNamesMap["kMultMCNParticlesEta10"] = kMultMCNParticlesEta10; fgVarNamesMap["kQ1ZNAX"] = kQ1ZNAX; fgVarNamesMap["kQ1ZNAY"] = kQ1ZNAY; fgVarNamesMap["kQ1ZNCX"] = kQ1ZNCX; @@ -2358,6 +2399,8 @@ void VarManager::SetDefaultVarNames() fgVarNamesMap["kCharge1"] = kCharge1; fgVarNamesMap["kDCAxy1"] = kDCAxy1; fgVarNamesMap["kDCAz1"] = kDCAz1; + fgVarNamesMap["kDCAxyOverSig1"] = kDCAxyOverSig1; + fgVarNamesMap["kDCAzOverSig1"] = kDCAzOverSig1; fgVarNamesMap["kITSclusterMap1"] = kITSclusterMap1; fgVarNamesMap["kTPCnSigmaEl1"] = kTPCnSigmaEl1; fgVarNamesMap["kPin_leg1"] = kPin_leg1; @@ -2368,6 +2411,8 @@ void VarManager::SetDefaultVarNames() fgVarNamesMap["kCharge2"] = kCharge2; fgVarNamesMap["kDCAxy2"] = kDCAxy2; fgVarNamesMap["kDCAz2"] = kDCAz2; + fgVarNamesMap["kDCAxyOverSig2"] = kDCAxyOverSig2; + fgVarNamesMap["kDCAzOverSig2"] = kDCAzOverSig2; fgVarNamesMap["kITSclusterMap2"] = kITSclusterMap2; fgVarNamesMap["kTPCnSigmaEl2"] = kTPCnSigmaEl2; fgVarNamesMap["kPin"] = kPin; @@ -2559,6 +2604,12 @@ void VarManager::SetDefaultVarNames() fgVarNamesMap["kMCCosThetaCS"] = kMCCosThetaCS; fgVarNamesMap["kMCPhiCS"] = kMCPhiCS; fgVarNamesMap["kMCPhiTildeCS"] = kMCPhiTildeCS; + fgVarNamesMap["kMCCosThetaGJB1"] = kMCCosThetaGJB1; + fgVarNamesMap["kMCPhiGJB1"] = kMCPhiGJB1; + fgVarNamesMap["kMCPhiTildeGJB1"] = kMCPhiTildeGJB1; + fgVarNamesMap["kMCCosThetaGJB2"] = kMCCosThetaGJB2; + fgVarNamesMap["kMCPhiGJB2"] = kMCPhiGJB2; + fgVarNamesMap["kMCPhiTildeGJB2"] = kMCPhiTildeGJB2; fgVarNamesMap["kMCCosThetaPP"] = kMCCosThetaPP; fgVarNamesMap["kMCPhiPP"] = kMCPhiPP; fgVarNamesMap["kMCPhiTildePP"] = kMCPhiTildePP; @@ -2605,6 +2656,12 @@ void VarManager::SetDefaultVarNames() fgVarNamesMap["kCosThetaCS"] = kCosThetaCS; fgVarNamesMap["kPhiCS"] = kPhiCS; fgVarNamesMap["kPhiTildeCS"] = kPhiTildeCS; + fgVarNamesMap["kCosThetaGJB1"] = kCosThetaGJB1; + fgVarNamesMap["kPhiGJB1"] = kPhiGJB1; + fgVarNamesMap["kPhiTildeGJB1"] = kPhiTildeGJB1; + fgVarNamesMap["kCosThetaGJB2"] = kCosThetaGJB2; + fgVarNamesMap["kPhiGJB2"] = kPhiGJB2; + fgVarNamesMap["kPhiTildeGJB2"] = kPhiTildeGJB2; fgVarNamesMap["kCosThetaPP"] = kCosThetaPP; fgVarNamesMap["kPhiPP"] = kPhiPP; fgVarNamesMap["kPhiTildePP"] = kPhiTildePP; @@ -2632,7 +2689,10 @@ void VarManager::SetDefaultVarNames() fgVarNamesMap["kQuadDCAsigXYZ"] = kQuadDCAsigXYZ; fgVarNamesMap["kSignQuadDCAsigXY"] = kSignQuadDCAsigXY; fgVarNamesMap["kCosPointingAngle"] = kCosPointingAngle; + fgVarNamesMap["kVertexingImpactParameter"] = kVertexingImpactParameter; fgVarNamesMap["kMCCosPointingAngle"] = kMCCosPointingAngle; + fgVarNamesMap["kD0ImpParProduct"] = kD0ImpParProduct; + fgVarNamesMap["kD0DcaDaughters"] = kD0DcaDaughters; fgVarNamesMap["kImpParXYJpsi"] = kImpParXYJpsi; fgVarNamesMap["kImpParXYK"] = kImpParXYK; fgVarNamesMap["kDCATrackProd"] = kDCATrackProd; diff --git a/PWGDQ/Core/VarManager.h b/PWGDQ/Core/VarManager.h index fa9ae2468a8..99c049374e8 100644 --- a/PWGDQ/Core/VarManager.h +++ b/PWGDQ/Core/VarManager.h @@ -23,6 +23,8 @@ #include "PWGUD/Core/UDHelpers.h" #include "PWGUD/Core/UPCHelpers.h" +// +#include "PWGHF/Core/HfHelper.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/TriggerAliases.h" @@ -154,9 +156,11 @@ class VarManager : public TObject enum PairCandidateType { // TODO: need to agree on a scheme to incorporate all various hypotheses (e.g. e - mu, jpsi - K+, Jpsi - pipi,...) - kDecayToEE = 0, // e.g. J/psi -> e+ e- - kDecayToMuMu, // e.g. J/psi -> mu+ mu- - kDecayToPiPi, + kDecayToEE = 0, // e.g. J/psi -> e+ e- + kDecayToMuMu, // e.g. J/psi -> mu+ mu- + kDecayToPiPi, // decay channel to pi+ pi- + kDecayToKchKch, // decay channel to K+ K- + kDecayToPrAPr, // decay channel to p+ p- kElectronMuon, // e.g. Electron - muon correlations kBcToThreeMuons, // e.g. Bc -> mu+ mu- mu+ kBtoJpsiEEK, // e.g. B+ -> e+ e- K+ @@ -164,6 +168,7 @@ class VarManager : public TObject kXtoJpsiPiPi, // e.g. X(3872) -> J/psi pi+ pi- kPsi2StoJpsiPiPi, // e.g. Psi(2S) -> J/psi pi+ pi- kChictoJpsiEE, // e.g. Chi_c1 -> J/psi e+ e- + kNonResonantToKpKmPiPi, // e.g. J/psi -> K+ K- pi+ pi+ kDstarToD0KPiPi, // e.g. D*+ -> D0 pi+ -> K- pi+ pi+ kTripleCandidateToEEPhoton, // e.g. chi_c -> e+ e- photon or pi0 -> e+ e- photon kDecayToKPi, // e.g. D0 -> K+ pi- or cc. @@ -558,6 +563,8 @@ class VarManager : public TObject kCharge1, kDCAxy1, kDCAz1, + kDCAxyOverSig1, + kDCAzOverSig1, kITSclusterMap1, kTPCnSigmaEl1, kPin_leg1, @@ -568,6 +575,8 @@ class VarManager : public TObject kCharge2, kDCAxy2, kDCAz2, + kDCAxyOverSig2, + kDCAzOverSig2, kITSclusterMap2, kTPCnSigmaEl2, @@ -776,6 +785,12 @@ class VarManager : public TObject kMCCosThetaCS, kMCPhiCS, kMCPhiTildeCS, + kMCCosThetaGJB1, + kMCPhiGJB1, + kMCPhiTildeGJB1, + kMCCosThetaGJB2, + kMCPhiGJB2, + kMCPhiTildeGJB2, kMCCosThetaPP, kMCPhiPP, kMCPhiTildePP, @@ -831,6 +846,12 @@ class VarManager : public TObject kCosThetaCS, kPhiCS, kPhiTildeCS, + kCosThetaGJB1, + kPhiGJB1, + kPhiTildeGJB1, + kCosThetaGJB2, + kPhiGJB2, + kPhiTildeGJB2, kCosThetaPP, kPhiPP, kPhiTildePP, @@ -874,6 +895,9 @@ class VarManager : public TObject kQuadDCAsigXYZ, kSignQuadDCAsigXY, kCosPointingAngle, + kVertexingImpactParameter, + kD0ImpParProduct, + kD0DcaDaughters, kImpParXYJpsi, kImpParXYK, kDCATrackProd, @@ -1059,6 +1083,7 @@ class VarManager : public TObject kDeltaR1, kDeltaR2, kDeltaR, + kVertexingQuadProcCode, // DQ-HF correlation variables kMassCharmHadron, @@ -1549,10 +1574,10 @@ class VarManager : public TObject static void FillDileptonPhoton(T1 const& dilepton, T2 const& photon, float* values = nullptr); template static void FillHadron(T const& hadron, float* values = nullptr, float hadronMass = 0.0f); - template - static void FillSingleDileptonCharmHadron(Cand const& candidate, H hfHelper, T& bdtScoreCharmHad, float* values = nullptr); - template - static void FillDileptonCharmHadron(DQ const& dilepton, HF const& charmHadron, H hfHelper, T& bdtScoreCharmHad, float* values = nullptr); + template + static void FillSingleDileptonCharmHadron(Cand const& candidate, T& bdtScoreCharmHad, float* values = nullptr); + template + static void FillDileptonCharmHadron(DQ const& dilepton, HF const& charmHadron, T& bdtScoreCharmHad, float* values = nullptr); template static void FillQVectorFromGFW(C const& collision, A const& compA11, A const& compB11, A const& compC11, A const& compA21, A const& compB21, A const& compC21, A const& compA31, A const& compB31, A const& compC31, A const& compA41, A const& compB41, A const& compC41, A const& compA23, A const& compA42, float S10A = 1.0, float S10B = 1.0, float S10C = 1.0, float S11A = 1.0, float S11B = 1.0, float S11C = 1.0, float S12A = 1.0, float S13A = 1.0, float S14A = 1.0, float S21A = 1.0, float S22A = 1.0, float S31A = 1.0, float S41A = 1.0, float* values = nullptr); template @@ -1895,7 +1920,7 @@ void VarManager::FillPropagateMuon(const T& muon, const C& collision, float* val } if constexpr ((fillMap & ReducedMuonCov) > 0) { - if (muon.filteringFlags() & (uint8_t(1) << VarManager::kMuonIsPropagated)) { // the muon is already propagated, so nothing to do + if (muon.filteringFlags() & (static_cast(1) << VarManager::kMuonIsPropagated)) { // the muon is already propagated, so nothing to do return; } } @@ -3133,26 +3158,26 @@ void VarManager::FillTrack(T const& track, float* values) values[kOrphanTrack] = static_cast((track.flags() & o2::aod::track::OrphanTrack) > 0); // NOTE: This is NOT for Run-2 } if (fgUsedVars[kIsSPDfirst]) { - values[kIsSPDfirst] = static_cast((track.itsClusterMap() & uint8_t(1)) > 0); + values[kIsSPDfirst] = static_cast((track.itsClusterMap() & static_cast(1)) > 0); } if (fgUsedVars[kIsSPDboth]) { - values[kIsSPDboth] = static_cast((track.itsClusterMap() & uint8_t(3)) > 0); + values[kIsSPDboth] = static_cast((track.itsClusterMap() & static_cast(3)) > 0); } if (fgUsedVars[kIsSPDany]) { - values[kIsSPDany] = static_cast((track.itsClusterMap() & uint8_t(1)) || (track.itsClusterMap() & uint8_t(2))); + values[kIsSPDany] = static_cast((track.itsClusterMap() & static_cast(1)) || (track.itsClusterMap() & static_cast(2))); } if (fgUsedVars[kITSClusterMap]) { values[kITSClusterMap] = track.itsClusterMap(); } if (fgUsedVars[kIsITSibFirst]) { - values[kIsITSibFirst] = static_cast((track.itsClusterMap() & uint8_t(1)) > 0); + values[kIsITSibFirst] = static_cast((track.itsClusterMap() & static_cast(1)) > 0); } if (fgUsedVars[kIsITSibAny]) { - values[kIsITSibAny] = static_cast((track.itsClusterMap() & (1 << uint8_t(0))) > 0 || (track.itsClusterMap() & (1 << uint8_t(1))) > 0 || (track.itsClusterMap() & (1 << uint8_t(2))) > 0); + values[kIsITSibAny] = static_cast((track.itsClusterMap() & (1 << static_cast(0))) > 0 || (track.itsClusterMap() & (1 << static_cast(1))) > 0 || (track.itsClusterMap() & (1 << static_cast(2))) > 0); } if (fgUsedVars[kIsITSibAll]) { - values[kIsITSibAll] = static_cast((track.itsClusterMap() & (1 << uint8_t(0))) > 0 && (track.itsClusterMap() & (1 << uint8_t(1))) > 0 && (track.itsClusterMap() & (1 << uint8_t(2))) > 0); + values[kIsITSibAll] = static_cast((track.itsClusterMap() & (1 << static_cast(0))) > 0 && (track.itsClusterMap() & (1 << static_cast(1))) > 0 && (track.itsClusterMap() & (1 << static_cast(2))) > 0); } values[kTrackTime] = track.trackTime(); @@ -3261,17 +3286,17 @@ void VarManager::FillTrack(T const& track, float* values) // Quantities based on the dalitz selections if constexpr ((fillMap & DalitzBits) > 0) { for (int i = 0; i < 8; i++) { - values[kIsDalitzLeg + i] = static_cast(track.dalitzBits() & (uint8_t(1) << i)); + values[kIsDalitzLeg + i] = static_cast(track.dalitzBits() & (static_cast(1) << i)); } } // Quantities based on the V0 selections if constexpr ((fillMap & TrackV0Bits) > 0) { - values[kIsLegFromGamma] = static_cast(track.pidbit() & (uint8_t(1) << VarManager::kIsConversionLeg)); - values[kIsLegFromK0S] = static_cast(track.pidbit() & (uint8_t(1) << VarManager::kIsK0sLeg)); - values[kIsLegFromLambda] = static_cast(track.pidbit() & (uint8_t(1) << VarManager::kIsLambdaLeg)); - values[kIsLegFromAntiLambda] = static_cast(track.pidbit() & (uint8_t(1) << VarManager::kIsALambdaLeg)); - values[kIsLegFromOmega] = static_cast(track.pidbit() & (uint8_t(1) << VarManager::kIsOmegaLeg)); + values[kIsLegFromGamma] = static_cast(track.pidbit() & (static_cast(1) << VarManager::kIsConversionLeg)); + values[kIsLegFromK0S] = static_cast(track.pidbit() & (static_cast(1) << VarManager::kIsK0sLeg)); + values[kIsLegFromLambda] = static_cast(track.pidbit() & (static_cast(1) << VarManager::kIsLambdaLeg)); + values[kIsLegFromAntiLambda] = static_cast(track.pidbit() & (static_cast(1) << VarManager::kIsALambdaLeg)); + values[kIsLegFromOmega] = static_cast(track.pidbit() & (static_cast(1) << VarManager::kIsOmegaLeg)); values[kIsProtonFromLambdaAndAntiLambda] = static_cast((values[kIsLegFromLambda] * track.sign() > 0) || (values[kIsLegFromAntiLambda] * (-track.sign()) > 0)); } @@ -3819,6 +3844,16 @@ void VarManager::FillPair(T1 const& t1, T2 const& t2, float* values) m2 = o2::constants::physics::MassPionCharged; } + if constexpr (pairType == kDecayToKchKch) { + m1 = o2::constants::physics::MassKaonCharged; + m2 = o2::constants::physics::MassKaonCharged; + } + + if constexpr (pairType == kDecayToPrAPr) { + m1 = o2::constants::physics::MassProton; + m2 = o2::constants::physics::MassProton; + } + if constexpr (pairType == kDecayToKPi) { m1 = o2::constants::physics::MassKaonCharged; m2 = o2::constants::physics::MassPionCharged; @@ -3886,12 +3921,14 @@ void VarManager::FillPair(T1 const& t1, T2 const& t2, float* values) } // polarization parameters - bool useHE = fgUsedVars[kCosThetaHE] || fgUsedVars[kPhiHE]; // helicity frame - bool useCS = fgUsedVars[kCosThetaCS] || fgUsedVars[kPhiCS]; // Collins-Soper frame - bool usePP = fgUsedVars[kCosThetaPP]; // production plane frame - bool useRM = fgUsedVars[kCosThetaRM]; // Random frame - - if (useHE || useCS || usePP || useRM) { + bool useHE = fgUsedVars[kCosThetaHE] || fgUsedVars[kPhiHE]; // helicity frame + bool useCS = fgUsedVars[kCosThetaCS] || fgUsedVars[kPhiCS]; // Collins-Soper frame + bool useGJB1 = fgUsedVars[kCosThetaGJB1] || fgUsedVars[kPhiGJB1]; // Gottfried-Jackson frame, beam 1 (+z) + bool useGJB2 = fgUsedVars[kCosThetaGJB2] || fgUsedVars[kPhiGJB2]; // Gottfried-Jackson frame, beam 2 (-z) + bool usePP = fgUsedVars[kCosThetaPP]; // production plane frame + bool useRM = fgUsedVars[kCosThetaRM]; // Random frame + + if (useHE || useCS || useGJB1 || useGJB2 || usePP || useRM) { ROOT::Math::Boost boostv12{v12.BoostToCM()}; ROOT::Math::XYZVectorF v1_CM{(boostv12(v1).Vect()).Unit()}; ROOT::Math::XYZVectorF v2_CM{(boostv12(v2).Vect()).Unit()}; @@ -3947,6 +3984,70 @@ void VarManager::FillPair(T1 const& t1, T2 const& t2, float* values) } } + if (useGJB1) { + // z-axis: beam direction in the mother rest frame + ROOT::Math::XYZVectorF zaxis_GJB1{Beam1_CM.Unit()}; + // mother momentum in LAB (defines the production plane) + ROOT::Math::XYZVectorF motherLab_GJB1{(ROOT::Math::XYZVectorF(v12.Vect())).Unit()}; + // y-axis: normal to the production plane (fallback to (0,1,0) if degenerate) + ROOT::Math::XYZVectorF yaxis_GJB1{zaxis_GJB1.Cross(motherLab_GJB1)}; + if (yaxis_GJB1.Mag2() > 1.e-6f) { + yaxis_GJB1 = yaxis_GJB1.Unit(); + } else { + yaxis_GJB1 = ROOT::Math::XYZVectorF(0.f, 1.f, 0.f); + } + ROOT::Math::XYZVectorF xaxis_GJB1{(yaxis_GJB1.Cross(zaxis_GJB1)).Unit()}; + if (fgUsedVars[kCosThetaGJB1]) { + values[kCosThetaGJB1] = zaxis_GJB1.Dot(v_CM); + } + if (fgUsedVars[kPhiGJB1]) { + values[kPhiGJB1] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_GJB1.Dot(v_CM), xaxis_GJB1.Dot(v_CM))); + } + if (fgUsedVars[kPhiTildeGJB1]) { + if (fgUsedVars[kCosThetaGJB1] && fgUsedVars[kPhiGJB1]) { + if (values[kCosThetaGJB1] > 0) { + values[kPhiTildeGJB1] = RecoDecay::constrainAngle(values[kPhiGJB1] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 + } else { + values[kPhiTildeGJB1] = RecoDecay::constrainAngle(values[kPhiGJB1] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 + } + } else { + values[kPhiTildeGJB1] = -999; // not computable + } + } + } + + if (useGJB2) { + // z-axis: beam direction in the mother rest frame + ROOT::Math::XYZVectorF zaxis_GJB2{Beam2_CM.Unit()}; + // mother momentum in LAB (defines the production plane) + ROOT::Math::XYZVectorF motherLab_GJB2{(ROOT::Math::XYZVectorF(v12.Vect())).Unit()}; + // y-axis: normal to the production plane (fallback to (0,1,0) if degenerate) + ROOT::Math::XYZVectorF yaxis_GJB2{zaxis_GJB2.Cross(motherLab_GJB2)}; + if (yaxis_GJB2.Mag2() > 1.e-6f) { + yaxis_GJB2 = yaxis_GJB2.Unit(); + } else { + yaxis_GJB2 = ROOT::Math::XYZVectorF(0.f, 1.f, 0.f); + } + ROOT::Math::XYZVectorF xaxis_GJB2{(yaxis_GJB2.Cross(zaxis_GJB2)).Unit()}; + if (fgUsedVars[kCosThetaGJB2]) { + values[kCosThetaGJB2] = zaxis_GJB2.Dot(v_CM); + } + if (fgUsedVars[kPhiGJB2]) { + values[kPhiGJB2] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_GJB2.Dot(v_CM), xaxis_GJB2.Dot(v_CM))); + } + if (fgUsedVars[kPhiTildeGJB2]) { + if (fgUsedVars[kCosThetaGJB2] && fgUsedVars[kPhiGJB2]) { + if (values[kCosThetaGJB2] > 0) { + values[kPhiTildeGJB2] = RecoDecay::constrainAngle(values[kPhiGJB2] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 + } else { + values[kPhiTildeGJB2] = RecoDecay::constrainAngle(values[kPhiGJB2] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 + } + } else { + values[kPhiTildeGJB2] = -999; // not computable + } + } + } + if (usePP) { ROOT::Math::XYZVector zaxis_PP = ROOT::Math::XYZVector(v12.Py(), -v12.Px(), 0.f); ROOT::Math::XYZVector yaxis_PP{(v12.Vect()).Unit()}; @@ -4023,14 +4124,18 @@ void VarManager::FillPair(T1 const& t1, T2 const& t2, float* values) values[kDCAxy2] = dca2XY; values[kDCAz2] = dca2Z; - if (fgUsedVars[kQuadDCAabsXY] || fgUsedVars[kQuadDCAsigXY] || fgUsedVars[kQuadDCAabsZ] || fgUsedVars[kQuadDCAsigZ] || fgUsedVars[kQuadDCAsigXYZ] || fgUsedVars[kSignQuadDCAsigXY]) { - // Quantities based on the barrel tables + double dca1sigXY = dca1XY / std::sqrt(t1.cYY()); + double dca2sigXY = dca2XY / std::sqrt(t2.cYY()); + double dca1sigZ = dca1Z / std::sqrt(t1.cZZ()); + double dca2sigZ = dca2Z / std::sqrt(t2.cZZ()); - double dca1sigXY = dca1XY / std::sqrt(t1.cYY()); - double dca2sigXY = dca2XY / std::sqrt(t2.cYY()); - double dca1sigZ = dca1Z / std::sqrt(t1.cZZ()); - double dca2sigZ = dca2Z / std::sqrt(t2.cZZ()); + values[kDCAxyOverSig1] = dca1sigXY; + values[kDCAzOverSig1] = dca1sigZ; + values[kDCAxyOverSig2] = dca2sigXY; + values[kDCAzOverSig2] = dca2sigZ; + if (fgUsedVars[kQuadDCAabsXY] || fgUsedVars[kQuadDCAsigXY] || fgUsedVars[kQuadDCAabsZ] || fgUsedVars[kQuadDCAsigZ] || fgUsedVars[kQuadDCAsigXYZ] || fgUsedVars[kSignQuadDCAsigXY]) { + // Quantities based on the barrel tables values[kQuadDCAabsXY] = std::sqrt((dca1XY * dca1XY + dca2XY * dca2XY) / 2); values[kQuadDCAsigXY] = std::sqrt((dca1sigXY * dca1sigXY + dca2sigXY * dca2sigXY) / 2); values[kQuadDCAabsZ] = std::sqrt((dca1Z * dca1Z + dca2Z * dca2Z) / 2); @@ -4381,12 +4486,14 @@ void VarManager::FillPairME(T1 const& t1, T2 const& t2, float* values) } // polarization parameters - bool useHE = fgUsedVars[kCosThetaHE] || fgUsedVars[kPhiHE]; // helicity frame - bool useCS = fgUsedVars[kCosThetaCS] || fgUsedVars[kPhiCS]; // Collins-Soper frame - bool usePP = fgUsedVars[kCosThetaPP]; // production plane frame - bool useRM = fgUsedVars[kCosThetaRM]; // Random frame - - if (useHE || useCS || usePP || useRM) { + bool useHE = fgUsedVars[kCosThetaHE] || fgUsedVars[kPhiHE]; // helicity frame + bool useCS = fgUsedVars[kCosThetaCS] || fgUsedVars[kPhiCS]; // Collins-Soper frame + bool useGJB1 = fgUsedVars[kCosThetaGJB1] || fgUsedVars[kPhiGJB1]; // Gottfried-Jackson frame, beam 1 (+z) + bool useGJB2 = fgUsedVars[kCosThetaGJB2] || fgUsedVars[kPhiGJB2]; // Gottfried-Jackson frame, beam 2 (-z) + bool usePP = fgUsedVars[kCosThetaPP]; // production plane frame + bool useRM = fgUsedVars[kCosThetaRM]; // Random frame + + if (useHE || useCS || useGJB1 || useGJB2 || usePP || useRM) { ROOT::Math::Boost boostv12{v12.BoostToCM()}; ROOT::Math::XYZVectorF v1_CM{(boostv12(v1).Vect()).Unit()}; ROOT::Math::XYZVectorF v2_CM{(boostv12(v2).Vect()).Unit()}; @@ -4442,6 +4549,70 @@ void VarManager::FillPairME(T1 const& t1, T2 const& t2, float* values) } } + if (useGJB1) { + // z-axis: beam direction in the mother rest frame + ROOT::Math::XYZVectorF zaxis_GJB1{Beam1_CM.Unit()}; + // mother momentum in LAB (defines the production plane) + ROOT::Math::XYZVectorF motherLab_GJB1{(ROOT::Math::XYZVectorF(v12.Vect())).Unit()}; + // y-axis: normal to the production plane + ROOT::Math::XYZVectorF yaxis_GJB1{zaxis_GJB1.Cross(motherLab_GJB1)}; + if (yaxis_GJB1.Mag2() > 1.e-6f) { + yaxis_GJB1 = yaxis_GJB1.Unit(); + } else { + yaxis_GJB1 = ROOT::Math::XYZVectorF(0.f, 1.f, 0.f); + } + ROOT::Math::XYZVectorF xaxis_GJB1{(yaxis_GJB1.Cross(zaxis_GJB1)).Unit()}; + if (fgUsedVars[kCosThetaGJB1]) { + values[kCosThetaGJB1] = zaxis_GJB1.Dot(v_CM); + } + if (fgUsedVars[kPhiGJB1]) { + values[kPhiGJB1] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_GJB1.Dot(v_CM), xaxis_GJB1.Dot(v_CM))); + } + if (fgUsedVars[kPhiTildeGJB1]) { + if (fgUsedVars[kCosThetaGJB1] && fgUsedVars[kPhiGJB1]) { + if (values[kCosThetaGJB1] > 0) { + values[kPhiTildeGJB1] = RecoDecay::constrainAngle(values[kPhiGJB1] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 + } else { + values[kPhiTildeGJB1] = RecoDecay::constrainAngle(values[kPhiGJB1] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 + } + } else { + values[kPhiTildeGJB1] = -999; // not computable + } + } + } + + if (useGJB2) { + // z-axis: beam direction in the mother rest frame + ROOT::Math::XYZVectorF zaxis_GJB2{Beam2_CM.Unit()}; + // mother momentum in LAB (defines the production plane) + ROOT::Math::XYZVectorF motherLab_GJB2{(ROOT::Math::XYZVectorF(v12.Vect())).Unit()}; + // y-axis: normal to the production plane (fallback to (0,1,0) if degenerate) + ROOT::Math::XYZVectorF yaxis_GJB2{zaxis_GJB2.Cross(motherLab_GJB2)}; + if (yaxis_GJB2.Mag2() > 1.e-6f) { + yaxis_GJB2 = yaxis_GJB2.Unit(); + } else { + yaxis_GJB2 = ROOT::Math::XYZVectorF(0.f, 1.f, 0.f); + } + ROOT::Math::XYZVectorF xaxis_GJB2{(yaxis_GJB2.Cross(zaxis_GJB2)).Unit()}; + if (fgUsedVars[kCosThetaGJB2]) { + values[kCosThetaGJB2] = zaxis_GJB2.Dot(v_CM); + } + if (fgUsedVars[kPhiGJB2]) { + values[kPhiGJB2] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_GJB2.Dot(v_CM), xaxis_GJB2.Dot(v_CM))); + } + if (fgUsedVars[kPhiTildeGJB2]) { + if (fgUsedVars[kCosThetaGJB2] && fgUsedVars[kPhiGJB2]) { + if (values[kCosThetaGJB2] > 0) { + values[kPhiTildeGJB2] = RecoDecay::constrainAngle(values[kPhiGJB2] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 + } else { + values[kPhiTildeGJB2] = RecoDecay::constrainAngle(values[kPhiGJB2] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 + } + } else { + values[kPhiTildeGJB2] = -999; // not computable + } + } + } + if (usePP) { ROOT::Math::XYZVector zaxis_PP = ROOT::Math::XYZVector(v12.Py(), -v12.Px(), 0.f); ROOT::Math::XYZVector yaxis_PP{(v12.Vect()).Unit()}; @@ -4743,12 +4914,14 @@ void VarManager::FillPairMC(T1 const& t1, T2 const& t2, float* values) values[kMCP2] = t2.p(); // polarization parameters - bool useHE = fgUsedVars[kMCCosThetaHE] || fgUsedVars[kMCPhiHE]; // helicity frame - bool useCS = fgUsedVars[kMCCosThetaCS] || fgUsedVars[kMCPhiCS]; // Collins-Soper frame - bool usePP = fgUsedVars[kMCCosThetaPP]; // production plane frame - bool useRM = fgUsedVars[kMCCosThetaRM]; // Random frame - - if (useHE || useCS || usePP || useRM) { + bool useHE = fgUsedVars[kMCCosThetaHE] || fgUsedVars[kMCPhiHE]; // helicity frame + bool useCS = fgUsedVars[kMCCosThetaCS] || fgUsedVars[kMCPhiCS]; // Collins-Soper frame + bool useGJB1 = fgUsedVars[kMCCosThetaGJB1] || fgUsedVars[kMCPhiGJB1]; // Gottfried-Jackson frame, beam 1 (+z) + bool useGJB2 = fgUsedVars[kMCCosThetaGJB2] || fgUsedVars[kMCPhiGJB2]; // Gottfried-Jackson frame, beam 2 (-z) + bool usePP = fgUsedVars[kMCCosThetaPP]; // production plane frame + bool useRM = fgUsedVars[kMCCosThetaRM]; // Random frame + + if (useHE || useCS || useGJB1 || useGJB2 || usePP || useRM) { ROOT::Math::Boost boostv12{v12.BoostToCM()}; ROOT::Math::XYZVectorF v1_CM{(boostv12(v1).Vect()).Unit()}; ROOT::Math::XYZVectorF v2_CM{(boostv12(v2).Vect()).Unit()}; @@ -4804,6 +4977,70 @@ void VarManager::FillPairMC(T1 const& t1, T2 const& t2, float* values) } } + if (useGJB1) { + // z-axis: beam direction in the mother rest frame + ROOT::Math::XYZVectorF zaxis_GJB1{Beam1_CM.Unit()}; + // mother momentum in LAB (defines the production plane) + ROOT::Math::XYZVectorF motherLab_GJB1{(ROOT::Math::XYZVectorF(v12.Vect())).Unit()}; + // y-axis: normal to the production plane (fallback to (0,1,0) if degenerate) + ROOT::Math::XYZVectorF yaxis_GJB1{zaxis_GJB1.Cross(motherLab_GJB1)}; + if (yaxis_GJB1.Mag2() > 1.e-6f) { + yaxis_GJB1 = yaxis_GJB1.Unit(); + } else { + yaxis_GJB1 = ROOT::Math::XYZVectorF(0.f, 1.f, 0.f); + } + ROOT::Math::XYZVectorF xaxis_GJB1{(yaxis_GJB1.Cross(zaxis_GJB1)).Unit()}; + if (fgUsedVars[kMCCosThetaGJB1]) { + values[kMCCosThetaGJB1] = zaxis_GJB1.Dot(v_CM); + } + if (fgUsedVars[kMCPhiGJB1]) { + values[kMCPhiGJB1] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_GJB1.Dot(v_CM), xaxis_GJB1.Dot(v_CM))); + } + if (fgUsedVars[kMCPhiTildeGJB1]) { + if (fgUsedVars[kMCCosThetaGJB1] && fgUsedVars[kMCPhiGJB1]) { + if (values[kMCCosThetaGJB1] > 0) { + values[kMCPhiTildeGJB1] = RecoDecay::constrainAngle(values[kMCPhiGJB1] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 + } else { + values[kMCPhiTildeGJB1] = RecoDecay::constrainAngle(values[kMCPhiGJB1] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 + } + } else { + values[kMCPhiTildeGJB1] = -999; // not computable + } + } + } + + if (useGJB2) { + // z-axis: beam direction in the mother rest frame + ROOT::Math::XYZVectorF zaxis_GJB2{Beam2_CM.Unit()}; + // mother momentum in LAB (defines the production plane) + ROOT::Math::XYZVectorF motherLab_GJB2{(ROOT::Math::XYZVectorF(v12.Vect())).Unit()}; + // y-axis: normal to the production plane (fallback to (0,1,0) if degenerate) + ROOT::Math::XYZVectorF yaxis_GJB2{zaxis_GJB2.Cross(motherLab_GJB2)}; + if (yaxis_GJB2.Mag2() > 1.e-6f) { + yaxis_GJB2 = yaxis_GJB2.Unit(); + } else { + yaxis_GJB2 = ROOT::Math::XYZVectorF(0.f, 1.f, 0.f); + } + ROOT::Math::XYZVectorF xaxis_GJB2{(yaxis_GJB2.Cross(zaxis_GJB2)).Unit()}; + if (fgUsedVars[kMCCosThetaGJB2]) { + values[kMCCosThetaGJB2] = zaxis_GJB2.Dot(v_CM); + } + if (fgUsedVars[kMCPhiGJB2]) { + values[kMCPhiGJB2] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_GJB2.Dot(v_CM), xaxis_GJB2.Dot(v_CM))); + } + if (fgUsedVars[kMCPhiTildeGJB2]) { + if (fgUsedVars[kMCCosThetaGJB2] && fgUsedVars[kMCPhiGJB2]) { + if (values[kMCCosThetaGJB2] > 0) { + values[kMCPhiTildeGJB2] = RecoDecay::constrainAngle(values[kMCPhiGJB2] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 + } else { + values[kMCPhiTildeGJB2] = RecoDecay::constrainAngle(values[kMCPhiGJB2] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 + } + } else { + values[kMCPhiTildeGJB2] = -999; // not computable + } + } + } + if (usePP) { ROOT::Math::XYZVector zaxis_PP = ROOT::Math::XYZVector(v12.Py(), -v12.Px(), 0.f); ROOT::Math::XYZVector yaxis_PP{v12.Vect().Unit()}; @@ -5022,6 +5259,9 @@ void VarManager::FillPairVertexing(C const& collision, T const& t1, T const& t2, values[kVertexingLxyErr] = -999.; values[kVertexingLxyzErr] = -999.; values[kVertexingLzErr] = -999.; + values[kVertexingLxyOverErr] = -999.; + values[kVertexingLzOverErr] = -999.; + values[kVertexingLxyzOverErr] = -999.; values[kVertexingTauxy] = -999.; values[kVertexingTauz] = -999.; @@ -5092,6 +5332,7 @@ void VarManager::FillPairVertexing(C const& collision, T const& t1, T const& t2, values[kCosPointingAngle] = (dx * v12.Px() + dy * v12.Py() + dz * v12.Pz()) / (v12.P() * values[VarManager::kVertexingLxyz]); + values[kVertexingImpactParameter] = values[VarManager::kVertexingLxyz] * std::sqrt(1 - values[VarManager::kCosPointingAngle] * values[VarManager::kCosPointingAngle]); // Decay length defined as in Run 2 values[kVertexingLzProjected] = (dz * v12.Pz()) / TMath::Sqrt(v12.Pz() * v12.Pz()); values[kVertexingLxyProjected] = (dx * v12.Px()) + (dy * v12.Py()); @@ -5172,6 +5413,7 @@ void VarManager::FillPairVertexing(C const& collision, T const& t1, T const& t2, std::sqrt(dyPair2PV * dyPair2PV) * v12.Py() + std::sqrt(dzPair2PV * dzPair2PV) * v12.Pz()) / (v12.P() * values[VarManager::kVertexingLxyz]); + values[kVertexingImpactParameter] = values[VarManager::kVertexingLxyz] * std::sqrt(1 - values[VarManager::kCosPointingAngle] * values[VarManager::kCosPointingAngle]); } // As defined in Run 2 (projected onto momentum) if (fgUsedVars[kVertexingLxyProjected] || fgUsedVars[kVertexingLxyzProjected] || fgUsedVars[kVertexingLzProjected]) { @@ -6618,8 +6860,8 @@ void VarManager::FillHadron(T const& hadron, float* values, float hadronMass) values[kRap] = vhadron.Rapidity(); } -template -void VarManager::FillSingleDileptonCharmHadron(Cand const& candidate, H hfHelper, T& bdtScoreCharmHad, float* values) +template +void VarManager::FillSingleDileptonCharmHadron(Cand const& candidate, T& bdtScoreCharmHad, float* values) { if (!values) { values = static_cast(fgValues); @@ -6632,33 +6874,33 @@ void VarManager::FillSingleDileptonCharmHadron(Cand const& candidate, H hfHelper values[kRap] = candidate.rap(); } if constexpr (partType == kD0ToPiK) { - values[kMassCharmHadron] = hfHelper.invMassD0ToPiK(candidate); + values[kMassCharmHadron] = HfHelper::invMassD0ToPiK(candidate); values[kPtCharmHadron] = candidate.pt(); values[kPhiCharmHadron] = candidate.phi(); - values[kRapCharmHadron] = hfHelper.yD0(candidate); + values[kRapCharmHadron] = HfHelper::yD0(candidate); values[kBdtCharmHadron] = static_cast(bdtScoreCharmHad); } if constexpr (partType == kD0barToKPi) { - values[kMassCharmHadron] = hfHelper.invMassD0barToKPi(candidate); + values[kMassCharmHadron] = HfHelper::invMassD0barToKPi(candidate); values[kPtCharmHadron] = candidate.pt(); values[kPhiCharmHadron] = candidate.phi(); - values[kRapCharmHadron] = hfHelper.yD0(candidate); + values[kRapCharmHadron] = HfHelper::yD0(candidate); values[kBdtCharmHadron] = static_cast(bdtScoreCharmHad); } if constexpr (partType == kDplusToPiKPi) { - values[kMassCharmHadron] = hfHelper.invMassDplusToPiKPi(candidate); + values[kMassCharmHadron] = HfHelper::invMassDplusToPiKPi(candidate); values[kPtCharmHadron] = candidate.pt(); values[kPhiCharmHadron] = candidate.phi(); - values[kRapCharmHadron] = hfHelper.yDplus(candidate); + values[kRapCharmHadron] = HfHelper::yDplus(candidate); values[kBdtCharmHadron] = static_cast(bdtScoreCharmHad); } } -template -void VarManager::FillDileptonCharmHadron(DQ const& dilepton, HF const& charmHadron, H hfHelper, T& bdtScoreCharmHad, float* values) +template +void VarManager::FillDileptonCharmHadron(DQ const& dilepton, HF const& charmHadron, T& bdtScoreCharmHad, float* values) { - FillSingleDileptonCharmHadron(dilepton, hfHelper, bdtScoreCharmHad, values); - FillSingleDileptonCharmHadron(charmHadron, hfHelper, bdtScoreCharmHad, values); + FillSingleDileptonCharmHadron(dilepton, bdtScoreCharmHad, values); + FillSingleDileptonCharmHadron(charmHadron, bdtScoreCharmHad, values); } template @@ -6681,6 +6923,11 @@ void VarManager::FillDileptonTrackTrack(T1 const& dilepton, T2 const& hadron1, T hadronMass1 = o2::constants::physics::MassElectron; hadronMass2 = o2::constants::physics::MassElectron; } + if (candidateType == kNonResonantToKpKmPiPi) { + defaultDileptonMass = dilepton.mass(); + hadronMass1 = o2::constants::physics::MassPionCharged; + hadronMass2 = o2::constants::physics::MassPionCharged; + } ROOT::Math::PtEtaPhiMVector v1(dilepton.pt(), dilepton.eta(), dilepton.phi(), dilepton.mass()); ROOT::Math::PtEtaPhiMVector v2(hadron1.pt(), hadron1.eta(), hadron1.phi(), hadronMass1); @@ -6734,6 +6981,11 @@ void VarManager::FillDileptonTrackTrackVertexing(C const& collision, T1 const& l float mlepton2 = o2::constants::physics::MassElectron; float mtrack1 = o2::constants::physics::MassPionCharged; float mtrack2 = o2::constants::physics::MassPionCharged; + float mMother = 3.686; + + if constexpr (candidateType == kXtoJpsiPiPi) { + mMother = o2::constants::physics::MassX3872; + } ROOT::Math::PtEtaPhiMVector v1(lepton1.pt(), lepton1.eta(), lepton1.phi(), mlepton1); ROOT::Math::PtEtaPhiMVector v2(lepton2.pt(), lepton2.eta(), lepton2.phi(), mlepton2); @@ -6747,35 +6999,41 @@ void VarManager::FillDileptonTrackTrackVertexing(C const& collision, T1 const& l values[kUsedKF] = static_cast(fgUsedKF); if (!fgUsedKF) { // create covariance matrix - std::array lepton1pars = {lepton1.y(), lepton1.z(), lepton1.snp(), lepton1.tgl(), lepton1.signed1Pt()}; - std::array lepton1covs = {lepton1.cYY(), lepton1.cZY(), lepton1.cZZ(), lepton1.cSnpY(), lepton1.cSnpZ(), - lepton1.cSnpSnp(), lepton1.cTglY(), lepton1.cTglZ(), lepton1.cTglSnp(), lepton1.cTglTgl(), - lepton1.c1PtY(), lepton1.c1PtZ(), lepton1.c1PtSnp(), lepton1.c1PtTgl(), lepton1.c1Pt21Pt2()}; - o2::track::TrackParCov pars1{lepton1.x(), lepton1.alpha(), lepton1pars, lepton1covs}; - std::array lepton2pars = {lepton2.y(), lepton2.z(), lepton2.snp(), lepton2.tgl(), lepton2.signed1Pt()}; - std::array lepton2covs = {lepton2.cYY(), lepton2.cZY(), lepton2.cZZ(), lepton2.cSnpY(), lepton2.cSnpZ(), - lepton2.cSnpSnp(), lepton2.cTglY(), lepton2.cTglZ(), lepton2.cTglSnp(), lepton2.cTglTgl(), - lepton2.c1PtY(), lepton2.c1PtZ(), lepton2.c1PtSnp(), lepton2.c1PtTgl(), lepton2.c1Pt21Pt2()}; - o2::track::TrackParCov pars2{lepton2.x(), lepton2.alpha(), lepton2pars, lepton2covs}; - std::array track1pars = {track1.y(), track1.z(), track1.snp(), track1.tgl(), track1.signed1Pt()}; - std::array track1covs = {track1.cYY(), track1.cZY(), track1.cZZ(), track1.cSnpY(), track1.cSnpZ(), - track1.cSnpSnp(), track1.cTglY(), track1.cTglZ(), track1.cTglSnp(), track1.cTglTgl(), - track1.c1PtY(), track1.c1PtZ(), track1.c1PtSnp(), track1.c1PtTgl(), track1.c1Pt21Pt2()}; - o2::track::TrackParCov pars3{track1.x(), track1.alpha(), track1pars, track1covs}; - std::array track2pars = {track2.y(), track2.z(), track2.snp(), track2.tgl(), track2.signed1Pt()}; - std::array track2covs = {track2.cYY(), track2.cZY(), track2.cZZ(), track2.cSnpY(), track2.cSnpZ(), - track2.cSnpSnp(), track2.cTglY(), track2.cTglZ(), track2.cTglSnp(), track2.cTglTgl(), - track2.c1PtY(), track2.c1PtZ(), track2.c1PtSnp(), track2.c1PtTgl(), track2.c1Pt21Pt2()}; - o2::track::TrackParCov pars4{track2.x(), track2.alpha(), track2pars, track2covs}; + o2::track::TrackParCov pars1 = getTrackParCov(lepton1); + o2::track::TrackParCov pars2 = getTrackParCov(lepton2); + o2::track::TrackParCov pars3 = getTrackParCov(track1); + o2::track::TrackParCov pars4 = getTrackParCov(track2); + // std::array lepton1pars = {lepton1.y(), lepton1.z(), lepton1.snp(), lepton1.tgl(), lepton1.signed1Pt()}; + // std::array lepton1covs = {lepton1.cYY(), lepton1.cZY(), lepton1.cZZ(), lepton1.cSnpY(), lepton1.cSnpZ(), + // lepton1.cSnpSnp(), lepton1.cTglY(), lepton1.cTglZ(), lepton1.cTglSnp(), lepton1.cTglTgl(), + // lepton1.c1PtY(), lepton1.c1PtZ(), lepton1.c1PtSnp(), lepton1.c1PtTgl(), lepton1.c1Pt21Pt2()}; + // o2::track::TrackParCov pars1{lepton1.x(), lepton1.alpha(), lepton1pars, lepton1covs}; + // std::array lepton2pars = {lepton2.y(), lepton2.z(), lepton2.snp(), lepton2.tgl(), lepton2.signed1Pt()}; + // std::array lepton2covs = {lepton2.cYY(), lepton2.cZY(), lepton2.cZZ(), lepton2.cSnpY(), lepton2.cSnpZ(), + // lepton2.cSnpSnp(), lepton2.cTglY(), lepton2.cTglZ(), lepton2.cTglSnp(), lepton2.cTglTgl(), + // lepton2.c1PtY(), lepton2.c1PtZ(), lepton2.c1PtSnp(), lepton2.c1PtTgl(), lepton2.c1Pt21Pt2()}; + // o2::track::TrackParCov pars2{lepton2.x(), lepton2.alpha(), lepton2pars, lepton2covs}; + // std::array track1pars = {track1.y(), track1.z(), track1.snp(), track1.tgl(), track1.signed1Pt()}; + // std::array track1covs = {track1.cYY(), track1.cZY(), track1.cZZ(), track1.cSnpY(), track1.cSnpZ(), + // track1.cSnpSnp(), track1.cTglY(), track1.cTglZ(), track1.cTglSnp(), track1.cTglTgl(), + // track1.c1PtY(), track1.c1PtZ(), track1.c1PtSnp(), track1.c1PtTgl(), track1.c1Pt21Pt2()}; + // o2::track::TrackParCov pars3{track1.x(), track1.alpha(), track1pars, track1covs}; + // std::array track2pars = {track2.y(), track2.z(), track2.snp(), track2.tgl(), track2.signed1Pt()}; + // std::array track2covs = {track2.cYY(), track2.cZY(), track2.cZZ(), track2.cSnpY(), track2.cSnpZ(), + // track2.cSnpSnp(), track2.cTglY(), track2.cTglZ(), track2.cTglSnp(), track2.cTglTgl(), + // track2.c1PtY(), track2.c1PtZ(), track2.c1PtSnp(), track2.c1PtTgl(), track2.c1Pt21Pt2()}; + // o2::track::TrackParCov pars4{track2.x(), track2.alpha(), track2pars, track2covs}; procCodeDilepton = VarManager::fgFitterTwoProngBarrel.process(pars1, pars2); // create dilepton track // o2::track::TrackParCov parsDilepton = VarManager::fgFitterTwoProngBarrel.createParentTrackParCov(0); // procCodeDileptonTrackTrack = VarManager::fgFitterThreeProngBarrel.process(parsDilepton, pars3, pars4); procCodeDileptonTrackTrack = VarManager::fgFitterFourProngBarrel.process(pars1, pars2, pars3, pars4); + values[kVertexingProcCode] = procCodeDilepton; + values[kVertexingQuadProcCode] = procCodeDileptonTrackTrack; // fill values - if (procCodeDilepton == 0 && procCodeDileptonTrackTrack == 0) { + if (procCodeDilepton == 0 || procCodeDileptonTrackTrack == 0) { // TODO: set the other variables to appropriate values and return values[kVertexingLxy] = -999.; values[kVertexingLxyz] = -999.; @@ -6811,6 +7069,7 @@ void VarManager::FillDileptonTrackTrackVertexing(C const& collision, T1 const& l std::sqrt((secondaryVertex[0] - collision.posX()) * (secondaryVertex[0] - collision.posX()) + (secondaryVertex[1] - collision.posY()) * (secondaryVertex[1] - collision.posY()))); + values[kVertexingChi2PCA] = fgFitterFourProngBarrel.getChi2AtPCACandidate(); values[kVertexingLxy] = (collision.posX() - secondaryVertex[0]) * (collision.posX() - secondaryVertex[0]) + (collision.posY() - secondaryVertex[1]) * (collision.posY() - secondaryVertex[1]); values[kVertexingLz] = (collision.posZ() - secondaryVertex[2]) * (collision.posZ() - secondaryVertex[2]); @@ -6823,11 +7082,11 @@ void VarManager::FillDileptonTrackTrackVertexing(C const& collision, T1 const& l values[kVertexingLxyErr] = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, phi, 0.) + getRotatedCovMatrixXX(covMatrixPCA, phi, 0.)); values[kVertexingLzErr] = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, 0, theta) + getRotatedCovMatrixXX(covMatrixPCA, 0, theta)); - values[kVertexingTauz] = (collision.posZ() - secondaryVertex[2]) * v1234.M() / (TMath::Abs(v1234.Pz()) * o2::constants::physics::LightSpeedCm2NS); - values[kVertexingTauxy] = values[kVertexingLxy] * v1234.M() / (v1234.Pt() * o2::constants::physics::LightSpeedCm2NS); + values[kVertexingTauz] = (collision.posZ() - secondaryVertex[2]) * mMother / (TMath::Abs(v1234.Pz()) * o2::constants::physics::LightSpeedCm2NS); + values[kVertexingTauxy] = values[kVertexingLxy] * mMother / (v1234.Pt() * o2::constants::physics::LightSpeedCm2NS); - values[kVertexingTauzErr] = values[kVertexingLzErr] * v1234.M() / (TMath::Abs(v1234.Pz()) * o2::constants::physics::LightSpeedCm2NS); - values[kVertexingTauxyErr] = values[kVertexingLxyErr] * v1234.M() / (v1234.Pt() * o2::constants::physics::LightSpeedCm2NS); + values[kVertexingTauzErr] = values[kVertexingLzErr] * mMother / (TMath::Abs(v1234.Pz()) * o2::constants::physics::LightSpeedCm2NS); + values[kVertexingTauxyErr] = values[kVertexingLxyErr] * mMother / (v1234.Pt() * o2::constants::physics::LightSpeedCm2NS); values[kCosPointingAngle] = ((secondaryVertex[0] - collision.posX()) * v1234.Px() + (secondaryVertex[1] - collision.posY()) * v1234.Py() + @@ -6841,9 +7100,9 @@ void VarManager::FillDileptonTrackTrackVertexing(C const& collision, T1 const& l values[kVertexingLxyzProjected] = ((secondaryVertex[0] - collision.posX()) * v1234.Px()) + ((secondaryVertex[1] - collision.posY()) * v1234.Py()) + ((secondaryVertex[2] - collision.posZ()) * v1234.Pz()); values[kVertexingLxyzProjected] = values[kVertexingLxyzProjected] / TMath::Sqrt((v1234.Px() * v1234.Px()) + (v1234.Py() * v1234.Py()) + (v1234.Pz() * v1234.Pz())); - values[kVertexingTauzProjected] = values[kVertexingLzProjected] * v1234.M() / TMath::Abs(v1234.Pz()); - values[kVertexingTauxyProjected] = values[kVertexingLxyProjected] * v1234.M() / (v1234.Pt()); - values[kVertexingTauxyzProjected] = values[kVertexingLxyzProjected] * v1234.M() / (v1234.P()); + values[kVertexingTauzProjected] = values[kVertexingLzProjected] * mMother / TMath::Abs(v1234.Pz()); + values[kVertexingTauxyProjected] = values[kVertexingLxyProjected] * mMother / (v1234.Pt()); + values[kVertexingTauxyzProjected] = values[kVertexingLxyzProjected] * mMother / (v1234.P()); } else { KFParticle lepton1KF; // lepton1 KFParticle lepton2KF; // lepton2 @@ -6983,7 +7242,7 @@ void VarManager::FillQuadMC(T1 const& dilepton, T2 const& track1, T2 const& trac double defaultDileptonMass = o2::constants::physics::MassJPsi; double hadronMass1 = o2::constants::physics::MassPionCharged; double hadronMass2 = o2::constants::physics::MassPionCharged; - if (candidateType == kXtoJpsiPiPi) { + if (candidateType == kXtoJpsiPiPi || candidateType == kPsi2StoJpsiPiPi) { defaultDileptonMass = o2::constants::physics::MassJPsi; hadronMass1 = o2::constants::physics::MassPionCharged; hadronMass2 = o2::constants::physics::MassPionCharged; @@ -7544,12 +7803,14 @@ void VarManager::FillPairAlice3(T1 const& t1, T2 const& t2, float* values) } // polarization parameters - bool useHE = fgUsedVars[kCosThetaHE] || fgUsedVars[kPhiHE]; // helicity frame - bool useCS = fgUsedVars[kCosThetaCS] || fgUsedVars[kPhiCS]; // Collins-Soper frame - bool usePP = fgUsedVars[kCosThetaPP]; // production plane frame - bool useRM = fgUsedVars[kCosThetaRM]; // Random frame - - if (useHE || useCS || usePP || useRM) { + bool useHE = fgUsedVars[kCosThetaHE] || fgUsedVars[kPhiHE]; // helicity frame + bool useCS = fgUsedVars[kCosThetaCS] || fgUsedVars[kPhiCS]; // Collins-Soper frame + bool useGJB1 = fgUsedVars[kCosThetaGJB1] || fgUsedVars[kPhiGJB1]; // Gottfried-Jackson frame, beam 1 (+z) + bool useGJB2 = fgUsedVars[kCosThetaGJB2] || fgUsedVars[kPhiGJB2]; // Gottfried-Jackson frame, beam 2 (-z) + bool usePP = fgUsedVars[kCosThetaPP]; // production plane frame + bool useRM = fgUsedVars[kCosThetaRM]; // Random frame + + if (useHE || useCS || useGJB1 || useGJB2 || usePP || useRM) { ROOT::Math::Boost boostv12{v12.BoostToCM()}; ROOT::Math::XYZVectorF v1_CM{(boostv12(v1).Vect()).Unit()}; ROOT::Math::XYZVectorF v2_CM{(boostv12(v2).Vect()).Unit()}; @@ -7605,6 +7866,70 @@ void VarManager::FillPairAlice3(T1 const& t1, T2 const& t2, float* values) } } + if (useGJB1) { + // z-axis: beam direction in the mother rest frame + ROOT::Math::XYZVectorF zaxis_GJB1{Beam1_CM.Unit()}; + // mother momentum in LAB (defines the production plane) + ROOT::Math::XYZVectorF motherLab_GJB1{(ROOT::Math::XYZVectorF(v12.Vect())).Unit()}; + // y-axis: normal to the production plane (fallback to (0,1,0) if degenerate) + ROOT::Math::XYZVectorF yaxis_GJB1{zaxis_GJB1.Cross(motherLab_GJB1)}; + if (yaxis_GJB1.Mag2() > 1.e-6f) { + yaxis_GJB1 = yaxis_GJB1.Unit(); + } else { + yaxis_GJB1 = ROOT::Math::XYZVectorF(0.f, 1.f, 0.f); + } + ROOT::Math::XYZVectorF xaxis_GJB1{(yaxis_GJB1.Cross(zaxis_GJB1)).Unit()}; + if (fgUsedVars[kCosThetaGJB1]) { + values[kCosThetaGJB1] = zaxis_GJB1.Dot(v_CM); + } + if (fgUsedVars[kPhiGJB1]) { + values[kPhiGJB1] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_GJB1.Dot(v_CM), xaxis_GJB1.Dot(v_CM))); + } + if (fgUsedVars[kPhiTildeGJB1]) { + if (fgUsedVars[kCosThetaGJB1] && fgUsedVars[kPhiGJB1]) { + if (values[kCosThetaGJB1] > 0) { + values[kPhiTildeGJB1] = RecoDecay::constrainAngle(values[kPhiGJB1] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 + } else { + values[kPhiTildeGJB1] = RecoDecay::constrainAngle(values[kPhiGJB1] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 + } + } else { + values[kPhiTildeGJB1] = -999; // not computable + } + } + } + + if (useGJB2) { + // z-axis: beam direction in the mother rest frame + ROOT::Math::XYZVectorF zaxis_GJB2{Beam2_CM.Unit()}; + // mother momentum in LAB (defines the production plane) + ROOT::Math::XYZVectorF motherLab_GJB2{(ROOT::Math::XYZVectorF(v12.Vect())).Unit()}; + // y-axis: normal to the production plane (fallback to (0,1,0) if degenerate) + ROOT::Math::XYZVectorF yaxis_GJB2{zaxis_GJB2.Cross(motherLab_GJB2)}; + if (yaxis_GJB2.Mag2() > 1.e-6f) { + yaxis_GJB2 = yaxis_GJB2.Unit(); + } else { + yaxis_GJB2 = ROOT::Math::XYZVectorF(0.f, 1.f, 0.f); + } + ROOT::Math::XYZVectorF xaxis_GJB2{(yaxis_GJB2.Cross(zaxis_GJB2)).Unit()}; + if (fgUsedVars[kCosThetaGJB2]) { + values[kCosThetaGJB2] = zaxis_GJB2.Dot(v_CM); + } + if (fgUsedVars[kPhiGJB2]) { + values[kPhiGJB2] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_GJB2.Dot(v_CM), xaxis_GJB2.Dot(v_CM))); + } + if (fgUsedVars[kPhiTildeGJB2]) { + if (fgUsedVars[kCosThetaGJB2] && fgUsedVars[kPhiGJB2]) { + if (values[kCosThetaGJB2] > 0) { + values[kPhiTildeGJB2] = RecoDecay::constrainAngle(values[kPhiGJB2] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 + } else { + values[kPhiTildeGJB2] = RecoDecay::constrainAngle(values[kPhiGJB2] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 + } + } else { + values[kPhiTildeGJB2] = -999; // not computable + } + } + } + if (usePP) { ROOT::Math::XYZVector zaxis_PP = ROOT::Math::XYZVector(v12.Py(), -v12.Px(), 0.f); ROOT::Math::XYZVector yaxis_PP{(v12.Vect()).Unit()}; @@ -7675,6 +8000,14 @@ void VarManager::FillPairAlice3(T1 const& t1, T2 const& t2, float* values) if (fgUsedVars[kPairPhiv]) { values[kPairPhiv] = calculatePhiV(t1, t2); } + + if constexpr (pairType == kDecayToKPi) { + if constexpr ((fillMap & TrackDCA) > 0 || (fillMap & ReducedTrackBarrel) > 0) { + values[kDCAxy1] = t1.dcaXY(); + values[kDCAxy2] = t2.dcaXY(); + values[kD0ImpParProduct] = t1.dcaXY() * t2.dcaXY(); + } + } } template @@ -7779,7 +8112,17 @@ void VarManager::FillPairVertexingAlice3(C const& collision, T const& t1, T cons v1 = {trackParVar0.getPt(), trackParVar0.getEta(), trackParVar0.getPhi(), m1}; v2 = {trackParVar1.getPt(), trackParVar1.getEta(), trackParVar1.getPhi(), m2}; v12 = v1 + v2; + + const auto pos1 = trackParVar0.getXYZGlo(); + const auto pos2 = trackParVar1.getXYZGlo(); + + const double dx = pos1.X() - pos2.X(); + const double dy = pos1.Y() - pos2.Y(); + const double dz = pos1.Z() - pos2.Z(); + + values[kD0DcaDaughters] = std::sqrt(dx * dx + dy * dy + dz * dz); } + double phi = std::atan2(secondaryVertex[1] - collision.posY(), secondaryVertex[0] - collision.posX()); double theta = std::atan2(secondaryVertex[2] - collision.posZ(), std::sqrt((secondaryVertex[0] - collision.posX()) * (secondaryVertex[0] - collision.posX()) + diff --git a/PWGDQ/Core/dqAnalysisPCH.cxx b/PWGDQ/Core/dqAnalysisPCH.cxx new file mode 100644 index 00000000000..aab5454efc3 --- /dev/null +++ b/PWGDQ/Core/dqAnalysisPCH.cxx @@ -0,0 +1,13 @@ +// Copyright 2019-2026 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. + +// Carrier for the DQAnalysisPCH precompiled header; see this directory's +// CMakeLists.txt. It exists only to own the PCH, so it has no code of its own. diff --git a/PWGDQ/DataModel/ReducedInfoTables.h b/PWGDQ/DataModel/ReducedInfoTables.h index ffd96c9b267..eeed55d4886 100644 --- a/PWGDQ/DataModel/ReducedInfoTables.h +++ b/PWGDQ/DataModel/ReducedInfoTables.h @@ -1198,6 +1198,8 @@ DECLARE_SOA_COLUMN(VertexingLxyProjected, vertexingLxyProjected, float); //! DECLARE_SOA_COLUMN(VertexingLxyzProjected, vertexingLxyzProjected, float); //! DECLARE_SOA_COLUMN(VertexingTauzProjected, vertexingTauzProjected, float); //! DECLARE_SOA_COLUMN(VertexingTauxyProjected, vertexingTauxyProjected, float); //! +DECLARE_SOA_BITMAP_COLUMN(DileptonFilterMap, dileptonFilterMap, 32); //! +DECLARE_SOA_BITMAP_COLUMN(QuadFilterMap, quadFilterMap, 32); //! } // namespace dileptonTrackTrackCandidate DECLARE_SOA_TABLE(DileptonTrackTrackCandidates, "AOD", "RTDQUADPLET", //! diff --git a/PWGDQ/Macros/evalMchTrackingEfficiency.cxx b/PWGDQ/Macros/evalMchTrackingEfficiency.cxx index c59afbb40b8..c66c63a1172 100644 --- a/PWGDQ/Macros/evalMchTrackingEfficiency.cxx +++ b/PWGDQ/Macros/evalMchTrackingEfficiency.cxx @@ -27,12 +27,13 @@ #include +#include #include double computeEfficiencyPerChamber(THnF* hnf, int iAxis, int iCh, double binLimits[2]); double computeEfficiencyPerChamber(THnF* hnf, const int iAxis[3], int iCh, double binLimits[3][2]); -double computeStationEfficiency(double effPerChamber[4], int iStation); -double computeTotalEfficiency(double effPerStation[5]); +double computeStationEfficiency(double const effPerChamber[4], int iStation); +double computeTotalEfficiency(double const effPerStation[5]); void getBinLimits(THnF* hnf, int nBins, int iAxis, std::vector& limits); void setHistoMinPt(THnF* hnf, int iAxis, float minPt); @@ -46,8 +47,26 @@ void evalMchTrackingEfficiency(const char* inFile = "AnalysisResults.root", cons { // read ouput from the taskComputeMchEfficiency TFile* file = TFile::Open(inFile, "read"); - TDirectoryFile* dir = reinterpret_cast(file->Get("task-muon-mch-trk-efficiency")); - THnF* hHitsEtaPtPhi = reinterpret_cast(dir->Get("hHitsEtaPtPhi")); + if (!file || file->IsZombie()) { + std::cerr << "Cannot open input file: " << inFile << std::endl; + return; + } + + TDirectoryFile* dir = nullptr; + file->GetObject("task-muon-mch-trk-efficiency", dir); + + THnF* hHitsEtaPtPhi = nullptr; + if (dir) { + dir->GetObject("hHitsEtaPtPhi", hHitsEtaPtPhi); + } else { + std::cerr << "Directory task-muon-mch-trk-efficiency not found in file " << inFile << std::endl; + return; + } + + if (!hHitsEtaPtPhi) { + std::cerr << "Histogram hHitsEtaPtPhi not found in directory." << std::endl; + return; + } // retrieve the eta/pt/phi axis binning configuration const int etaAx = 1, ptAx = 2, phiAx = 3; @@ -63,8 +82,6 @@ void evalMchTrackingEfficiency(const char* inFile = "AnalysisResults.root", cons getBinLimits(hHitsEtaPtPhi, nBinsPt, ptAx, limitsPt); // these are values! getBinLimits(hHitsEtaPtPhi, nBinsPhi, phiAx, limitsPhi); // these are values! - int nBinMinPt = hHitsEtaPtPhi->GetAxis(ptAx)->FindBin(minPt); - // Define the output file TFile* outFile = new TFile(outFileName, "recreate"); @@ -83,7 +100,6 @@ void evalMchTrackingEfficiency(const char* inFile = "AnalysisResults.root", cons // as well as the one per station // the total integrated value is stored in both histos as last quantity for reference TH1F* hEffIntegratedChamber = new TH1F("hEffIntegratedChamber", "integrated efficiency per chamber; ;Efficiency", kChamber + 1, -0.5, kChamber + 0.5); - const char* hChNames[kChamber + 1]; for (int i = 0; i < kChamber; i++) { hEffIntegratedChamber->GetXaxis()->SetBinLabel(i + 1, Form("Chamber %d", i)); } @@ -91,7 +107,6 @@ void evalMchTrackingEfficiency(const char* inFile = "AnalysisResults.root", cons // Define histogram for integrated efficiency per station TH1F* hEffIntegratedStation = new TH1F("hEffIntegratedStation", "integrated efficiency per station; ;Efficiency", kStation, -0.5, kStation + 0.5); - const char* hStNames[kStation]; for (int i = 0; i < 3; i++) { hEffIntegratedStation->GetXaxis()->SetBinLabel(i + 1, Form("Station %d", i)); } @@ -274,7 +289,7 @@ double computeEfficiencyPerChamber(THnF* hnf, int iAxis, int iCh, double binLimi hnf->GetAxis(iAxis)->SetRangeUser(binLimits[0], binLimits[1]); // Project onto the Nhits axis - TH1F* htmp = reinterpret_cast(hnf->Projection(0)); + TH1D* htmp = hnf->Projection(0); double NhitPairing[16]; for (int i = 0; i < 16; i++) { NhitPairing[i] = 0; @@ -322,7 +337,7 @@ double computeEfficiencyPerChamber(THnF* hnf, const int iAxis[3], int iCh, doubl } // Project onto the Nhits axis - TH1F* htmp = reinterpret_cast(hnf->Projection(0)); + TH1D* htmp = hnf->Projection(0); double NhitPairing[16]; for (int i = 0; i < 16; i++) { NhitPairing[i] = 0; @@ -365,7 +380,7 @@ double computeEfficiencyPerChamber(THnF* hnf, const int iAxis[3], int iCh, doubl // Compute the efficiency per station // Stations 1, 2, 3: Eff = 1 - ( 1 - E(i) ) * ( 1 - (E(j) ) // Stations 4 & 5 together: Eff = product(i=6...9) E(i) + sum(i=6...9) [ ( 1 - E(i) )* product(j=6...9, i!=i) E(j) ] -double computeStationEfficiency(double effPerChamber[4], int iStation) +double computeStationEfficiency(double const effPerChamber[4], int iStation) { double effSt = 0.; if (iStation < 3) { // Calculation for Station 1, 2, 3 @@ -390,7 +405,7 @@ double computeStationEfficiency(double effPerChamber[4], int iStation) } // Total efficiency corresponds to the product of the efficiency per station -double computeTotalEfficiency(double effPerStation[5]) +double computeTotalEfficiency(double const effPerStation[5]) { double effTot = 1.; for (int i = 0; i < 4; i++) { diff --git a/PWGDQ/Macros/fit_library/CB2Pdf.h b/PWGDQ/Macros/fit_library/CB2Pdf.h index 23cb291dcdd..d5b5326813e 100644 --- a/PWGDQ/Macros/fit_library/CB2Pdf.h +++ b/PWGDQ/Macros/fit_library/CB2Pdf.h @@ -25,7 +25,7 @@ class CB2Pdf : public RooAbsPdf { public: - CB2Pdf(){}; + CB2Pdf() {} CB2Pdf(const char* name, const char* title, RooAbsReal& _x, RooAbsReal& _A, diff --git a/PWGDQ/Macros/fit_library/ExpPdf.h b/PWGDQ/Macros/fit_library/ExpPdf.h index 306b0aea210..7495176d942 100644 --- a/PWGDQ/Macros/fit_library/ExpPdf.h +++ b/PWGDQ/Macros/fit_library/ExpPdf.h @@ -25,7 +25,7 @@ class ExpPdf : public RooAbsPdf { public: - ExpPdf(){}; + ExpPdf() {} ExpPdf(const char* name, const char* title, RooAbsReal& _x, RooAbsReal& _A, diff --git a/PWGDQ/Macros/fit_library/GausPdf.h b/PWGDQ/Macros/fit_library/GausPdf.h index 80a1e956817..22e07a2d522 100644 --- a/PWGDQ/Macros/fit_library/GausPdf.h +++ b/PWGDQ/Macros/fit_library/GausPdf.h @@ -25,7 +25,7 @@ class GausPdf : public RooAbsPdf { public: - GausPdf(){}; + GausPdf() {} GausPdf(const char* name, const char* title, RooAbsReal& _x, RooAbsReal& _A, diff --git a/PWGDQ/Macros/fit_library/Pol4ExpPdf.h b/PWGDQ/Macros/fit_library/Pol4ExpPdf.h index 67202dbe5ac..baca326c1f2 100644 --- a/PWGDQ/Macros/fit_library/Pol4ExpPdf.h +++ b/PWGDQ/Macros/fit_library/Pol4ExpPdf.h @@ -28,7 +28,7 @@ TString nameParameters[] = {"p0", "p1", "p2", "p3", "p4", "p5", "N_bkg", "N_sig" class Pol4ExpPdf : public RooAbsPdf { public: - Pol4ExpPdf(){}; + Pol4ExpPdf() {} Pol4ExpPdf(const char* name, const char* title, RooAbsReal& _x, RooAbsReal& _P0, diff --git a/PWGDQ/Macros/fit_library/VWGPdf.h b/PWGDQ/Macros/fit_library/VWGPdf.h index 3ed4f2f81e6..af8c6cba9eb 100644 --- a/PWGDQ/Macros/fit_library/VWGPdf.h +++ b/PWGDQ/Macros/fit_library/VWGPdf.h @@ -25,7 +25,7 @@ class VWGPdf : public RooAbsPdf { public: - VWGPdf(){}; + VWGPdf() {} VWGPdf(const char* name, const char* title, RooAbsReal& _x, RooAbsReal& _A, diff --git a/PWGDQ/TableProducer/CMakeLists.txt b/PWGDQ/TableProducer/CMakeLists.txt index b8fd20d356d..5ba06a37cb5 100644 --- a/PWGDQ/TableProducer/CMakeLists.txt +++ b/PWGDQ/TableProducer/CMakeLists.txt @@ -11,35 +11,42 @@ o2physics_add_dpl_workflow(table-maker SOURCES tableMaker.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase O2Physics::AnalysisCCDB O2Physics::PWGDQCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(table-maker-with-assoc SOURCES tableMaker_withAssoc.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase O2Physics::AnalysisCCDB O2Physics::PWGDQCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(table-maker-mc SOURCES tableMakerMC.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase O2Physics::AnalysisCCDB O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(table-maker-mc-with-assoc SOURCES tableMakerMC_withAssoc.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase O2Physics::AnalysisCCDB O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(table-maker-jpsi-hf SOURCES tableMakerJpsiHf.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(table-maker-muon-mch-trk-eff SOURCES tableMakerMuonMchTrkEfficiency.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(generated-quarkonia-mc SOURCES generatedQuarkoniaMC.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DetectorsBase O2Physics::PWGDQCore COMPONENT_NAME Analysis) diff --git a/PWGDQ/TableProducer/tableMaker.cxx b/PWGDQ/TableProducer/tableMaker.cxx index 15edcac01ec..eaa2e9a2bc2 100644 --- a/PWGDQ/TableProducer/tableMaker.cxx +++ b/PWGDQ/TableProducer/tableMaker.cxx @@ -202,7 +202,7 @@ struct TableMaker { struct : ConfigurableGroup { Configurable fConfigCcdbUrl{"useCCDBConfigurations.ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable fConfigCcdbPathTPC{"useCCDBConfigurations.ccdb-path-tpc", "Users/z/zhxiong/TPCPID/PostCalib", "base path to the ccdb object"}; - Configurable fConfigCcdbPathZorro{"useCCDBConfigurations.ccdb-path-zorro", "/Users/m/mpuccio/EventFiltering/OTS/", "base path to the ccdb object for zorro"}; + Configurable fConfigCcdbPathZorro{"useCCDBConfigurations.ccdb-path-zorro", "EventFiltering/Zorro/", "base path to the ccdb object for zorro"}; } useCCDBConfigurations; Configurable fConfigNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; @@ -236,7 +236,7 @@ struct TableMaker { int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. // TODO: filter on TPC dedx used temporarily until electron PID will be improved - Filter barrelSelectedTracks = ifnode(fIsRun2.node() == true, aod::track::trackType == uint8_t(aod::track::Run2Track), aod::track::trackType == uint8_t(aod::track::Track)) && o2::aod::track::pt >= fConfigBarrelTrackPtLow && nabs(o2::aod::track::eta) <= fConfigBarrelTrackMaxAbsEta && o2::aod::track::tpcSignal >= configTpcSignal.fConfigMinTpcSignal && o2::aod::track::tpcSignal <= configTpcSignal.fConfigMaxTpcSignal && o2::aod::track::tpcChi2NCl < 4.0f && o2::aod::track::itsChi2NCl < 36.0f; + Filter barrelSelectedTracks = ifnode(fIsRun2.node() == true, aod::track::trackType == static_cast(aod::track::Run2Track), aod::track::trackType == static_cast(aod::track::Track)) && o2::aod::track::pt >= fConfigBarrelTrackPtLow && nabs(o2::aod::track::eta) <= fConfigBarrelTrackMaxAbsEta && o2::aod::track::tpcSignal >= configTpcSignal.fConfigMinTpcSignal && o2::aod::track::tpcSignal <= configTpcSignal.fConfigMaxTpcSignal && o2::aod::track::tpcChi2NCl < 4.0f && o2::aod::track::itsChi2NCl < 36.0f; Filter muonFilter = o2::aod::fwdtrack::pt >= fConfigMuonPtLow; @@ -445,17 +445,17 @@ struct TableMaker { // fill stats information, before selections for (int i = 0; i < kNaliases; i++) { if (triggerAliases & (static_cast(1) << i)) { - (reinterpret_cast(fStatsList->At(0)))->Fill(2.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(2.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(2.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(2.0, static_cast(kNaliases)); if (useZorro.fConfigRunZorro) { zorro.setBaseCCDBPath(useCCDBConfigurations.fConfigCcdbPathZorro.value); zorro.setBCtolerance(useZorro.fBcTolerance); zorro.initCCDB(fCCDB.service, fCurrentRun, bc.timestamp(), useZorro.fConfigZorroTrigMask.value); - zorro.populateExternalHists(fCurrentRun, reinterpret_cast(fStatsList->At(3)), reinterpret_cast(fStatsList->At(4))); - bool zorroSel = zorro.isSelected(bc.globalBC(), useZorro.fBcTolerance, reinterpret_cast(fStatsList->At(4))); + zorro.populateExternalHists(fCurrentRun, dynamic_cast(fStatsList->At(3)), dynamic_cast(fStatsList->At(4))); + bool zorroSel = zorro.isSelected(bc.globalBC(), useZorro.fBcTolerance, dynamic_cast(fStatsList->At(4))); if (zorroSel) { tag |= (static_cast(true) << 56); // the same bit is used for this zorro selections from ccdb } @@ -471,10 +471,10 @@ struct TableMaker { // fill stats information, after selections for (int i = 0; i < kNaliases; i++) { if (triggerAliases & (static_cast(1) << i)) { - (reinterpret_cast(fStatsList->At(0)))->Fill(3.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(3.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(3.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(3.0, static_cast(kNaliases)); fHistMan->FillHistClass("Event_AfterCuts", VarManager::fgValues); @@ -534,7 +534,7 @@ struct TableMaker { } trackFilteringTag = static_cast(0); - trackTempFilterMap = uint8_t(0); + trackTempFilterMap = static_cast(0); VarManager::FillTrack(track); if (fDoDetailedQA) { fHistMan->FillHistClass("TrackBarrel_BeforeCuts", VarManager::fgValues); @@ -547,14 +547,14 @@ struct TableMaker { int i = 0; for (auto cut = fTrackCuts.begin(); cut != fTrackCuts.end(); cut++, i++) { if ((*cut).IsSelected(VarManager::fgValues)) { - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); if (fConfigQA) { fHistMan->FillHistClass(Form("TrackBarrel_%s", (*cut).GetName()), VarManager::fgValues); if (fIsAmbiguous && isAmbiguous == 1) { fHistMan->FillHistClass(Form("Ambiguous_TrackBarrel_%s", (*cut).GetName()), VarManager::fgValues); } } - (reinterpret_cast(fStatsList->At(1)))->Fill(static_cast(i)); + (dynamic_cast(fStatsList->At(1)))->Fill(static_cast(i)); } } if (!trackTempFilterMap) { @@ -571,8 +571,8 @@ struct TableMaker { if constexpr (static_cast(TTrackFillMap & VarManager::ObjTypes::TrackV0Bits)) { // BIT0-4: V0Bits trackFilteringTag = static_cast(track.pidbit()); for (int iv0 = 0; iv0 < 5; iv0++) { - if (track.pidbit() & (uint8_t(1) << iv0)) { - (reinterpret_cast(fStatsList->At(1)))->Fill(fTrackCuts.size() + static_cast(iv0)); + if (track.pidbit() & (static_cast(1) << iv0)) { + (dynamic_cast(fStatsList->At(1)))->Fill(fTrackCuts.size() + static_cast(iv0)); } } if (fConfigIsOnlyforMaps) { @@ -715,7 +715,7 @@ struct TableMaker { int i = 0; for (auto cut = fMuonCuts.begin(); cut != fMuonCuts.end(); cut++, i++) { if ((*cut).IsSelected(VarManager::fgValues)) - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); } if (!trackTempFilterMap) { // does not pass the cuts @@ -738,8 +738,8 @@ struct TableMaker { } } } - fwdFilteringTag = uint8_t(0); - trackTempFilterMap = uint8_t(0); + fwdFilteringTag = static_cast(0); + trackTempFilterMap = static_cast(0); VarManager::FillTrack(muon); @@ -759,14 +759,14 @@ struct TableMaker { int i = 0; for (auto cut = fMuonCuts.begin(); cut != fMuonCuts.end(); cut++, i++) { if ((*cut).IsSelected(VarManager::fgValues)) { - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); if (fConfigQA) { fHistMan->FillHistClass(Form("Muons_%s", (*cut).GetName()), VarManager::fgValues); if (fIsAmbiguous && isAmbiguous == 1) { fHistMan->FillHistClass(Form("Ambiguous_Muons_%s", (*cut).GetName()), VarManager::fgValues); } } - (reinterpret_cast(fStatsList->At(2)))->Fill(static_cast(i)); + (dynamic_cast(fStatsList->At(2)))->Fill(static_cast(i)); } } if (!trackTempFilterMap) { @@ -775,7 +775,7 @@ struct TableMaker { // store the cut decisions trackFilteringTag = trackTempFilterMap; // BIT0-7: user selection cuts if (fPropMuon) { - trackFilteringTag |= (uint8_t(1) << VarManager::kMuonIsPropagated); // store the info on whether the muon is propagated or not + trackFilteringTag |= (static_cast(1) << VarManager::kMuonIsPropagated); // store the info on whether the muon is propagated or not } // update the matching MCH/MFT index @@ -917,17 +917,17 @@ struct TableMaker { // fill stats information, before selections for (int i = 0; i < kNaliases; i++) { if (triggerAliases & (static_cast(1) << i)) { - (reinterpret_cast(fStatsList->At(0)))->Fill(2.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(2.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(2.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(2.0, static_cast(kNaliases)); if (useZorro.fConfigRunZorro) { zorro.setBaseCCDBPath(useCCDBConfigurations.fConfigCcdbPathZorro.value); zorro.setBCtolerance(useZorro.fBcTolerance); zorro.initCCDB(fCCDB.service, fCurrentRun, bc.timestamp(), useZorro.fConfigZorroTrigMask.value); - zorro.populateExternalHists(fCurrentRun, reinterpret_cast(fStatsList->At(3)), reinterpret_cast(fStatsList->At(4))); - bool zorroSel = zorro.isSelected(bc.globalBC(), useZorro.fBcTolerance, reinterpret_cast(fStatsList->At(4))); + zorro.populateExternalHists(fCurrentRun, dynamic_cast(fStatsList->At(3)), dynamic_cast(fStatsList->At(4))); + bool zorroSel = zorro.isSelected(bc.globalBC(), useZorro.fBcTolerance, dynamic_cast(fStatsList->At(4))); if (zorroSel) { tag |= (static_cast(true) << 56); // the same bit is used for this zorro selections from ccdb } @@ -943,10 +943,10 @@ struct TableMaker { // fill stats information, after selections for (int i = 0; i < kNaliases; i++) { if (triggerAliases & (static_cast(1) << i)) { - (reinterpret_cast(fStatsList->At(0)))->Fill(3.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(3.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(3.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(3.0, static_cast(kNaliases)); fHistMan->FillHistClass("Event_AfterCuts", VarManager::fgValues); @@ -999,7 +999,7 @@ struct TableMaker { } } trackFilteringTag = static_cast(0); - trackTempFilterMap = uint8_t(0); + trackTempFilterMap = static_cast(0); VarManager::FillTrack(track); if (fDoDetailedQA) { fHistMan->FillHistClass("TrackBarrel_BeforeCuts", VarManager::fgValues); @@ -1012,14 +1012,14 @@ struct TableMaker { int i = 0; for (auto cut = fTrackCuts.begin(); cut != fTrackCuts.end(); cut++, i++) { if ((*cut).IsSelected(VarManager::fgValues)) { - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); if (fConfigQA) { fHistMan->FillHistClass(Form("TrackBarrel_%s", (*cut).GetName()), VarManager::fgValues); if (fIsAmbiguous && isAmbiguous == 1) { fHistMan->FillHistClass(Form("Ambiguous_TrackBarrel_%s", (*cut).GetName()), VarManager::fgValues); } } - (reinterpret_cast(fStatsList->At(1)))->Fill(static_cast(i)); + (dynamic_cast(fStatsList->At(1)))->Fill(static_cast(i)); } } if (!trackTempFilterMap) { @@ -1036,8 +1036,8 @@ struct TableMaker { if constexpr (static_cast(TTrackFillMap & VarManager::ObjTypes::TrackV0Bits)) { // BIT2-6: V0Bits trackFilteringTag |= (static_cast(track.pidbit()) << 2); for (int iv0 = 0; iv0 < 5; iv0++) { - if (track.pidbit() & (uint8_t(1) << iv0)) { - (reinterpret_cast(fStatsList->At(1)))->Fill(fTrackCuts.size() + static_cast(iv0)); + if (track.pidbit() & (static_cast(1) << iv0)) { + (dynamic_cast(fStatsList->At(1)))->Fill(fTrackCuts.size() + static_cast(iv0)); } } if (fConfigIsOnlyforMaps) { @@ -1133,7 +1133,7 @@ struct TableMaker { int i = 0; for (auto cut = fMuonCuts.begin(); cut != fMuonCuts.end(); cut++, i++) { if ((*cut).IsSelected(VarManager::fgValues)) - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); } if (!trackTempFilterMap) { // does not pass the cuts @@ -1152,7 +1152,7 @@ struct TableMaker { } } trackFilteringTag = static_cast(0); - trackTempFilterMap = uint8_t(0); + trackTempFilterMap = static_cast(0); if constexpr (static_cast(TMuonFillMap & VarManager::ObjTypes::MuonRealign)) { if (static_cast(muon.isRemovable())) { @@ -1179,14 +1179,14 @@ struct TableMaker { int i = 0; for (auto cut = fMuonCuts.begin(); cut != fMuonCuts.end(); cut++, i++) { if ((*cut).IsSelected(VarManager::fgValues)) { - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); if (fConfigQA) { fHistMan->FillHistClass(Form("Muons_%s", (*cut).GetName()), VarManager::fgValues); if (fIsAmbiguous && isAmbiguous == 1) { fHistMan->FillHistClass(Form("Ambiguous_Muons_%s", (*cut).GetName()), VarManager::fgValues); } } - (reinterpret_cast(fStatsList->At(2)))->Fill(static_cast(i)); + (dynamic_cast(fStatsList->At(2)))->Fill(static_cast(i)); } } if (!trackTempFilterMap) { @@ -1196,7 +1196,7 @@ struct TableMaker { // store the cut decisions trackFilteringTag = trackTempFilterMap; // BIT0-7: user selection cuts if (fPropMuon) { - trackFilteringTag |= (uint8_t(1) << VarManager::kMuonIsPropagated); // store the info on whether the muon is propagated or not + trackFilteringTag |= (static_cast(1) << VarManager::kMuonIsPropagated); // store the info on whether the muon is propagated or not } // update the matching MCH/MFT index @@ -1383,10 +1383,10 @@ struct TableMaker { { for (int i = 0; i < kNaliases; i++) { if (collision.alias_bit(i) > 0) { - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); if (collision.eventFilter()) { fullSkimming(collision, bcs, tracksBarrel, tracksMuon, nullptr, nullptr); } @@ -1398,10 +1398,10 @@ struct TableMaker { { for (int i = 0; i < kNaliases; i++) { if (collision.alias_bit(i) > 0) { - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); if (collision.eventFilter()) { fullSkimming(collision, bcs, tracksBarrel, tracksMuon, nullptr, nullptr); } @@ -1447,7 +1447,7 @@ struct TableMaker { soa::Filtered const& tracksBarrel) { for (auto const& track : tracksBarrel) { - if (track.dalitzBits() != uint8_t(0)) { + if (track.dalitzBits() != static_cast(0)) { fullSkimming(collision, bcs, tracksBarrel, nullptr, nullptr, nullptr); break; } @@ -1460,12 +1460,12 @@ struct TableMaker { { for (auto const& track : tracksBarrel) { // check if this event has a Dalitz candidate - if (track.dalitzBits() != uint8_t(0)) { + if (track.dalitzBits() != static_cast(0)) { fullSkimming(collision, bcs, tracksBarrel, nullptr, nullptr, nullptr); break; } // check if this event has a electron candidate from V0 - if (track.pidbit() == uint8_t(1)) { + if (track.pidbit() == static_cast(1)) { fullSkimming(collision, bcs, tracksBarrel, nullptr, nullptr, nullptr); break; } @@ -1478,10 +1478,10 @@ struct TableMaker { { for (int i = 0; i < kNaliases; i++) { if (collision.alias_bit(i) > 0) { - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); if (collision.eventFilter()) { fullSkimming(collision, bcs, tracksBarrel, nullptr, nullptr, nullptr); } @@ -1500,10 +1500,10 @@ struct TableMaker { { for (int i = 0; i < kNaliases; i++) { if (collision.alias_bit(i) > 0) { - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); if (collision.eventFilter()) { fullSkimming(collision, bcs, tracksBarrel, nullptr, nullptr, nullptr); } @@ -1515,10 +1515,10 @@ struct TableMaker { { for (int i = 0; i < kNaliases; i++) { if (collision.alias_bit(i) > 0) { - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); if (collision.eventFilter()) { fullSkimming(collision, bcs, tracksBarrel, nullptr, nullptr, nullptr); } @@ -1593,10 +1593,10 @@ struct TableMaker { { for (int i = 0; i < kNaliases; i++) { if (collision.alias_bit(i) > 0) { - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); if (collision.eventFilter()) { fullSkimming(collision, bcs, nullptr, tracksMuon, nullptr, nullptr); } @@ -1710,10 +1710,10 @@ struct TableMaker { { for (int i = 0; i < kNaliases; i++) { if (bc.alias_bit(i) > 0) { - (reinterpret_cast(fStatsList->At(0)))->Fill(0.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(0.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(0.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(0.0, static_cast(kNaliases)); } // List of process functions removed because they are not using cov matrix @@ -1752,10 +1752,10 @@ struct TableMaker { { for (int i = 0; i < kNaliases; i++) { if (collision.alias_bit(i) > 0) { - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); if (collision.eventFilter()) { fullSkimming(collision, bcs, nullptr, tracksMuon, nullptr, nullptr); } @@ -1767,10 +1767,10 @@ struct TableMaker { { for (int i = 0; i < kNaliases; i++) { if (collision.alias_bit(i) > 0) { - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); if (collision.eventFilter()) { fullSkimming(collision, bcs, nullptr, tracksMuon, nullptr, nullptr); } diff --git a/PWGDQ/TableProducer/tableMakerJpsiHf.cxx b/PWGDQ/TableProducer/tableMakerJpsiHf.cxx index 59b06e3ef64..65f69e394d9 100644 --- a/PWGDQ/TableProducer/tableMakerJpsiHf.cxx +++ b/PWGDQ/TableProducer/tableMakerJpsiHf.cxx @@ -102,8 +102,6 @@ using TracksWithExtra = soa::Join; using MyBarrelTracksSelectedWithColl = soa::Join; using MyMuonTracksSelectedWithColl = soa::Join; -HfHelper hfHelper; - struct tableMakerJpsiHf { // // This task combines dilepton candidates with a open charm hadron @@ -321,9 +319,9 @@ struct tableMakerJpsiHf { double rapDmeson = -999.; if constexpr (TDmeson == DmesonType::D0) { - rapDmeson = hfHelper.yD0(dmeson); + rapDmeson = HfHelper::yD0(dmeson); } else if constexpr (TDmeson == DmesonType::Dplus) { - rapDmeson = hfHelper.yDplus(dmeson); + rapDmeson = HfHelper::yDplus(dmeson); } if (yCandDmesonMax >= 0. && std::abs(rapDmeson) > yCandDmesonMax) { @@ -332,18 +330,18 @@ struct tableMakerJpsiHf { if constexpr (TDmeson == DmesonType::D0) { if (dmeson.isSelD0() >= 1) { - VarManager::FillSingleDileptonCharmHadron(dmeson, hfHelper, scores[0], fValuesDileptonCharmHadron); + VarManager::FillSingleDileptonCharmHadron(dmeson, scores[0], fValuesDileptonCharmHadron); fHistMan->FillHistClass("Dmeson", fValuesDileptonCharmHadron); VarManager::ResetValues(0, VarManager::kNVars, fValuesDileptonCharmHadron); } if (dmeson.isSelD0bar() >= 1) { - VarManager::FillSingleDileptonCharmHadron(dmeson, hfHelper, scores[3], fValuesDileptonCharmHadron); + VarManager::FillSingleDileptonCharmHadron(dmeson, scores[3], fValuesDileptonCharmHadron); fHistMan->FillHistClass("Dmeson", fValuesDileptonCharmHadron); VarManager::ResetValues(0, VarManager::kNVars, fValuesDileptonCharmHadron); } } else if constexpr (TDmeson == DmesonType::Dplus) { if (dmeson.isSelDplusToPiKPi() >= 1) { - VarManager::FillSingleDileptonCharmHadron(dmeson, hfHelper, scores_dplus[0], fValuesDileptonCharmHadron); + VarManager::FillSingleDileptonCharmHadron(dmeson, scores_dplus[0], fValuesDileptonCharmHadron); fHistMan->FillHistClass("Dmeson", fValuesDileptonCharmHadron); VarManager::ResetValues(0, VarManager::kNVars, fValuesDileptonCharmHadron); } @@ -366,7 +364,7 @@ struct tableMakerJpsiHf { if (configDebug) { float dummyScore{-999.f}; - VarManager::FillSingleDileptonCharmHadron(dilepton, hfHelper, dummyScore, fValuesDileptonCharmHadron); + VarManager::FillSingleDileptonCharmHadron(dilepton, dummyScore, fValuesDileptonCharmHadron); fHistMan->FillHistClass("JPsi", fValuesDileptonCharmHadron); VarManager::ResetValues(0, VarManager::kNVars, fValuesDileptonCharmHadron); } @@ -391,9 +389,9 @@ struct tableMakerJpsiHf { double rapDmeson = -999.; if constexpr (TDmeson == DmesonType::D0) { - rapDmeson = hfHelper.yD0(dmeson); + rapDmeson = HfHelper::yD0(dmeson); } else if constexpr (TDmeson == DmesonType::Dplus) { - rapDmeson = hfHelper.yDplus(dmeson); + rapDmeson = HfHelper::yDplus(dmeson); } if (yCandDmesonMax >= 0. && std::abs(rapDmeson) > yCandDmesonMax) { @@ -471,14 +469,14 @@ struct tableMakerJpsiHf { if constexpr (TDmeson == DmesonType::D0) { if (dmeson.isSelD0() >= 1) { - massD0 = hfHelper.invMassD0ToPiK(dmeson); - VarManager::FillDileptonCharmHadron(dilepton, dmeson, hfHelper, scores[0], fValuesDileptonCharmHadron); + massD0 = HfHelper::invMassD0ToPiK(dmeson); + VarManager::FillDileptonCharmHadron(dilepton, dmeson, scores[0], fValuesDileptonCharmHadron); fHistMan->FillHistClass("JPsiDmeson", fValuesDileptonCharmHadron); VarManager::ResetValues(0, VarManager::kNVars, fValuesDileptonCharmHadron); } if (dmeson.isSelD0bar() >= 1) { - massD0bar = hfHelper.invMassD0barToKPi(dmeson); - VarManager::FillDileptonCharmHadron(dilepton, dmeson, hfHelper, scores[3], fValuesDileptonCharmHadron); + massD0bar = HfHelper::invMassD0barToKPi(dmeson); + VarManager::FillDileptonCharmHadron(dilepton, dmeson, scores[3], fValuesDileptonCharmHadron); fHistMan->FillHistClass("JPsiDmeson", fValuesDileptonCharmHadron); VarManager::ResetValues(0, VarManager::kNVars, fValuesDileptonCharmHadron); } @@ -487,8 +485,8 @@ struct tableMakerJpsiHf { } } else if constexpr (TDmeson == DmesonType::Dplus) { if (dmeson.isSelDplusToPiKPi() >= 1) { - massDplus = hfHelper.invMassDplusToPiKPi(dmeson); - VarManager::FillDileptonCharmHadron(dilepton, dmeson, hfHelper, scores_dplus[0], fValuesDileptonCharmHadron); + massDplus = HfHelper::invMassDplusToPiKPi(dmeson); + VarManager::FillDileptonCharmHadron(dilepton, dmeson, scores_dplus[0], fValuesDileptonCharmHadron); fHistMan->FillHistClass("JPsiDmeson", fValuesDileptonCharmHadron); VarManager::ResetValues(0, VarManager::kNVars, fValuesDileptonCharmHadron); } diff --git a/PWGDQ/TableProducer/tableMakerMC.cxx b/PWGDQ/TableProducer/tableMakerMC.cxx index 41e26e1a374..05baa8e279d 100644 --- a/PWGDQ/TableProducer/tableMakerMC.cxx +++ b/PWGDQ/TableProducer/tableMakerMC.cxx @@ -187,7 +187,7 @@ struct TableMakerMC { int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. // TODO: filter on TPC dedx used temporarily until electron PID will be improved - Filter barrelSelectedTracks = ifnode(fIsRun2.node() == true, aod::track::trackType == uint8_t(aod::track::Run2Track), aod::track::trackType == uint8_t(aod::track::Track)) && o2::aod::track::pt >= fConfigBarrelTrackPtLow && nabs(o2::aod::track::eta) <= 0.9f; + Filter barrelSelectedTracks = ifnode(fIsRun2.node() == true, aod::track::trackType == static_cast(aod::track::Run2Track), aod::track::trackType == static_cast(aod::track::Track)) && o2::aod::track::pt >= fConfigBarrelTrackPtLow && nabs(o2::aod::track::eta) <= 0.9f; Filter muonFilter = o2::aod::fwdtrack::pt >= fConfigMuonPtLow; @@ -432,10 +432,10 @@ struct TableMakerMC { // fill stats information, before selections for (int i = 0; i < kNaliases; i++) { if (triggerAliases & (static_cast(1) << i)) { - (reinterpret_cast(fStatsList->At(0)))->Fill(2.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(2.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(2.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(2.0, static_cast(kNaliases)); if (!fEventCut->IsSelected(VarManager::fgValues)) { continue; @@ -448,10 +448,10 @@ struct TableMakerMC { // fill stats information, after selections for (int i = 0; i < kNaliases; i++) { if (triggerAliases & (static_cast(1) << i)) { - (reinterpret_cast(fStatsList->At(0)))->Fill(3.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(3.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(3.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(3.0, static_cast(kNaliases)); event(tag, bc.runNumber(), collision.posX(), collision.posY(), collision.posZ(), collision.numContrib(), collision.collisionTime(), collision.collisionTimeRes()); if constexpr ((TEventFillMap & VarManager::ObjTypes::CollisionMult) > 0 && (TEventFillMap & VarManager::ObjTypes::CollisionCent) > 0) { @@ -578,14 +578,14 @@ struct TableMakerMC { int i = 0; for (auto& cut : fTrackCuts) { if (cut.IsSelected(VarManager::fgValues)) { - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); if (fConfigQA) { fHistMan->FillHistClass(Form("TrackBarrel_%s", cut.GetName()), VarManager::fgValues); // fill the reconstructed truth if (fIsAmbiguous && isAmbiguous == 1) { fHistMan->FillHistClass(Form("Ambiguous_TrackBarrel_%s", cut.GetName()), VarManager::fgValues); } } - (reinterpret_cast(fStatsList->At(1)))->Fill(static_cast(i)); + (dynamic_cast(fStatsList->At(1)))->Fill(static_cast(i)); } i++; } @@ -604,7 +604,7 @@ struct TableMakerMC { trackFilteringTag |= (static_cast(track.pidbit()) << 2); for (int iv0 = 0; iv0 < 5; iv0++) { if (track.pidbit() & (static_cast(1) << iv0)) { - (reinterpret_cast(fStatsList->At(1)))->Fill(fTrackCuts.size() + static_cast(iv0)); + (dynamic_cast(fStatsList->At(1)))->Fill(fTrackCuts.size() + static_cast(iv0)); } } } @@ -623,7 +623,7 @@ struct TableMakerMC { if (fDoDetailedQA) { j = 0; for (auto& cut : fTrackCuts) { - if (trackTempFilterMap & (uint8_t(1) << j)) { + if (trackTempFilterMap & (static_cast(1) << j)) { fHistMan->FillHistClass(Form("TrackBarrel_%s_%s", cut.GetName(), sig.GetName()), VarManager::fgValues); // fill the reconstructed truth } j++; @@ -775,7 +775,7 @@ struct TableMakerMC { int i = 0; for (auto& cut : fMuonCuts) { if (cut.IsSelected(VarManager::fgValues)) { - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); } i++; } @@ -823,11 +823,11 @@ struct TableMakerMC { int i = 0; for (auto& cut : fMuonCuts) { if (cut.IsSelected(VarManager::fgValues)) { - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); if (fIsAmbiguous && isAmbiguous == 1) { fHistMan->FillHistClass(Form("Ambiguous_Muons_%s", cut.GetName()), VarManager::fgValues); } - (reinterpret_cast(fStatsList->At(2)))->Fill(static_cast(i)); + (dynamic_cast(fStatsList->At(2)))->Fill(static_cast(i)); } i++; } @@ -847,7 +847,7 @@ struct TableMakerMC { if (fDoDetailedQA) { j = 0; for (auto& cut : fMuonCuts) { - if (trackTempFilterMap & (uint8_t(1) << j)) { + if (trackTempFilterMap & (static_cast(1) << j)) { fHistMan->FillHistClass(Form("Muons_%s_%s", cut.GetName(), sig.GetName()), VarManager::fgValues); // fill the reconstructed truth } j++; @@ -978,11 +978,11 @@ struct TableMakerMC { mctrack.vx(), mctrack.vy(), mctrack.vz(), mctrack.vt(), mcflags); for (unsigned int isig = 0; isig < fMCSignals.size(); isig++) { if (mcflags & (static_cast(1) << isig)) { - (reinterpret_cast(fStatsList->At(3)))->Fill(static_cast(isig)); + (dynamic_cast(fStatsList->At(3)))->Fill(static_cast(isig)); } } if (mcflags == 0) { - (reinterpret_cast(fStatsList->At(3)))->Fill(static_cast(fMCSignals.size())); + (dynamic_cast(fStatsList->At(3)))->Fill(static_cast(fMCSignals.size())); } } // end loop over labels @@ -1067,10 +1067,10 @@ struct TableMakerMC { // fill stats information, before selections for (int i = 0; i < kNaliases; i++) { if (triggerAliases & (static_cast(1) << i)) { - (reinterpret_cast(fStatsList->At(0)))->Fill(2.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(2.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(2.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(2.0, static_cast(kNaliases)); if (!fEventCut->IsSelected(VarManager::fgValues)) { continue; @@ -1083,10 +1083,10 @@ struct TableMakerMC { // fill stats information, after selections for (int i = 0; i < kNaliases; i++) { if (triggerAliases & (static_cast(1) << i)) { - (reinterpret_cast(fStatsList->At(0)))->Fill(3.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(3.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(3.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(3.0, static_cast(kNaliases)); event(tag, bc.runNumber(), collision.posX(), collision.posY(), collision.posZ(), collision.numContrib(), collision.collisionTime(), collision.collisionTimeRes()); if constexpr ((TEventFillMap & VarManager::ObjTypes::CollisionMult) > 0 && (TEventFillMap & VarManager::ObjTypes::CollisionCent) > 0) { @@ -1201,14 +1201,14 @@ struct TableMakerMC { int i = 0; for (auto& cut : fTrackCuts) { if (cut.IsSelected(VarManager::fgValues)) { - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); if (fConfigQA) { fHistMan->FillHistClass(Form("TrackBarrel_%s", cut.GetName()), VarManager::fgValues); // fill the reconstructed truth if (fIsAmbiguous && isAmbiguous == 1) { fHistMan->FillHistClass(Form("Ambiguous_TrackBarrel_%s", cut.GetName()), VarManager::fgValues); } } - (reinterpret_cast(fStatsList->At(1)))->Fill(static_cast(i)); + (dynamic_cast(fStatsList->At(1)))->Fill(static_cast(i)); } i++; } @@ -1227,7 +1227,7 @@ struct TableMakerMC { trackFilteringTag |= (static_cast(track.pidbit()) << 2); for (int iv0 = 0; iv0 < 5; iv0++) { if (track.pidbit() & (static_cast(1) << iv0)) { - (reinterpret_cast(fStatsList->At(1)))->Fill(fTrackCuts.size() + static_cast(iv0)); + (dynamic_cast(fStatsList->At(1)))->Fill(fTrackCuts.size() + static_cast(iv0)); } } } @@ -1246,7 +1246,7 @@ struct TableMakerMC { if (fDoDetailedQA) { j = 0; for (auto& cut : fTrackCuts) { - if (trackTempFilterMap & (uint8_t(1) << j)) { + if (trackTempFilterMap & (static_cast(1) << j)) { fHistMan->FillHistClass(Form("TrackBarrel_%s_%s", cut.GetName(), sig.GetName()), VarManager::fgValues); // fill the reconstructed truth } j++; @@ -1334,7 +1334,7 @@ struct TableMakerMC { int i = 0; for (auto& cut : fMuonCuts) { if (cut.IsSelected(VarManager::fgValues)) { - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); } i++; } @@ -1379,12 +1379,12 @@ struct TableMakerMC { int i = 0; for (auto& cut : fMuonCuts) { if (cut.IsSelected(VarManager::fgValues)) { - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); fHistMan->FillHistClass(Form("Muons_%s", cut.GetName()), VarManager::fgValues); if (fIsAmbiguous && isAmbiguous == 1) { fHistMan->FillHistClass(Form("Ambiguous_Muons_%s", cut.GetName()), VarManager::fgValues); } - (reinterpret_cast(fStatsList->At(2)))->Fill(static_cast(i)); + (dynamic_cast(fStatsList->At(2)))->Fill(static_cast(i)); } i++; } @@ -1404,7 +1404,7 @@ struct TableMakerMC { if (fDoDetailedQA) { j = 0; for (auto& cut : fMuonCuts) { - if (trackTempFilterMap & (uint8_t(1) << j)) { + if (trackTempFilterMap & (static_cast(1) << j)) { fHistMan->FillHistClass(Form("Muons_%s_%s", cut.GetName(), sig.GetName()), VarManager::fgValues); // fill the reconstructed truth } j++; @@ -1517,11 +1517,11 @@ struct TableMakerMC { mctrack.vx(), mctrack.vy(), mctrack.vz(), mctrack.vt(), mcflags); for (unsigned int isig = 0; isig < fMCSignals.size(); isig++) { if (mcflags & (static_cast(1) << isig)) { - (reinterpret_cast(fStatsList->At(3)))->Fill(static_cast(isig)); + (dynamic_cast(fStatsList->At(3)))->Fill(static_cast(isig)); } } if (mcflags == 0) { - (reinterpret_cast(fStatsList->At(3)))->Fill(static_cast(fMCSignals.size())); + (dynamic_cast(fStatsList->At(3)))->Fill(static_cast(fMCSignals.size())); } } // end loop over labels @@ -1576,7 +1576,7 @@ struct TableMakerMC { fStatsList.setObject(new TList()); fStatsList->SetOwner(kTRUE); std::vector eventLabels{"BCs", "Collisions before filtering", "Before cuts", "After cuts"}; - TH2I* histEvents = new TH2I("EventStats", "Event statistics", eventLabels.size(), -0.5, eventLabels.size() - 0.5, (float)kNaliases + 1, -0.5, (float)kNaliases + 0.5); + TH2I* histEvents = new TH2I("EventStats", "Event statistics", eventLabels.size(), -0.5, eventLabels.size() - 0.5, static_cast(kNaliases) + 1, -0.5, static_cast(kNaliases) + 0.5); int ib = 1; for (auto label = eventLabels.begin(); label != eventLabels.end(); label++, ib++) { histEvents->GetXaxis()->SetBinLabel(ib, (*label).Data()); @@ -1767,10 +1767,10 @@ struct TableMakerMC { { for (int i = 0; i < kNaliases; i++) { if (bc.alias_bit(i) > 0) { - (reinterpret_cast(fStatsList->At(0)))->Fill(0.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(0.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(0.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(0.0, static_cast(kNaliases)); } PROCESS_SWITCH(TableMakerMC, processFull, "Produce both barrel and muon skims", false); diff --git a/PWGDQ/TableProducer/tableMakerMC_withAssoc.cxx b/PWGDQ/TableProducer/tableMakerMC_withAssoc.cxx index d31a296d0ac..838d7e79af3 100644 --- a/PWGDQ/TableProducer/tableMakerMC_withAssoc.cxx +++ b/PWGDQ/TableProducer/tableMakerMC_withAssoc.cxx @@ -66,6 +66,7 @@ #include #include +#include #include #include #include @@ -935,7 +936,7 @@ struct TableMakerMC { if constexpr (static_cast(TTrackFillMap & VarManager::ObjTypes::TrackV0Bits)) { // BIT0-4: V0Bits trackFilteringTag |= static_cast(track.pidbit()); for (int iv0 = 0; iv0 < 5; iv0++) { - if (track.pidbit() & (uint8_t(1) << iv0)) { + if (track.pidbit() & (static_cast(1) << iv0)) { (dynamic_cast(fStatsList->At(1)))->Fill(fTrackCuts.size() + static_cast(iv0)); } } @@ -985,7 +986,7 @@ struct TableMakerMC { mcflags = 0; int isig = 0; // runs over the MC signals - int j = 0; // runs over the track cuts + int j = 0; // runs over the track cuts // check all the specified signals and fill histograms for MC truth matched tracks for (const auto& sig : fMCSignals) { if (sig->CheckSignal(true, mctrack)) { @@ -994,7 +995,7 @@ struct TableMakerMC { if (fDoDetailedQA) { j = 0; for (const auto& cut : fTrackCuts) { - if (trackTempFilterMap & (uint8_t(1) << j)) { + if (trackTempFilterMap & (static_cast(1) << j)) { fHistMan->FillHistClass(Form("TrackBarrel_%s_%s", cut->GetName(), sig->GetName()), dqtablemakermc_helpers::varValues()); // fill the reconstructed truth } j++; @@ -1158,8 +1159,8 @@ struct TableMakerMC { } } - trackFilteringTag = uint8_t(0); - trackTempFilterMap = uint8_t(0); + trackFilteringTag = static_cast(0); + trackTempFilterMap = static_cast(0); if constexpr (static_cast(TMuonFillMap & VarManager::ObjTypes::MuonRealign)) { // Check refit flag in case of realigned muons @@ -1206,7 +1207,7 @@ struct TableMakerMC { int i = 0; for (auto cut = fMuonCuts.begin(); cut != fMuonCuts.end(); cut++, i++) { if ((*cut)->IsSelected(dqtablemakermc_helpers::varValues())) { - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); if (fConfigHistOutput.fConfigQA) { fHistMan->FillHistClass(Form("Muons_%s", (*cut)->GetName()), dqtablemakermc_helpers::varValues()); } @@ -1239,7 +1240,7 @@ struct TableMakerMC { mcflags = 0; int isig = 0; // runs over the MC signals - int j = 0; // runs over the track cuts + int j = 0; // runs over the track cuts // check all the specified signals and fill histograms for MC truth matched tracks for (const auto& sig : fMCSignals) { if (sig->CheckSignal(true, mctrack)) { @@ -1247,7 +1248,7 @@ struct TableMakerMC { if (fDoDetailedQA) { j = 0; for (const auto& cut : fMuonCuts) { - if (trackTempFilterMap & (uint8_t(1) << j)) { + if (trackTempFilterMap & (static_cast(1) << j)) { fHistMan->FillHistClass(Form("Muons_%s_%s", cut->GetName(), sig->GetName()), dqtablemakermc_helpers::varValues()); // fill the reconstructed truth } j++; @@ -1290,7 +1291,7 @@ struct TableMakerMC { // So all the information which pertains to collision association or MFT associations should not be taken from the skimmed data, but recomputed at analysis time. uint32_t mchIdx = -1; uint32_t mftIdx = -1; - if (muon.trackType() == uint8_t(0) || muon.trackType() == uint8_t(2)) { // MCH-MID (2) or global (0) + if (muon.trackType() == static_cast(0) || muon.trackType() == static_cast(2)) { // MCH-MID (2) or global (0) if (fFwdTrackIndexMap.find(muon.matchMCHTrackId()) != fFwdTrackIndexMap.end()) { mchIdx = fFwdTrackIndexMap[muon.matchMCHTrackId()]; } @@ -1592,7 +1593,7 @@ struct TableMakerMC { fStatsList.setObject(new TList()); fStatsList->SetOwner(kTRUE); std::vector eventLabels{"BCs", "Collisions before filtering", "Before cuts", "After cuts"}; - TH2I* histEvents = new TH2I("EventStats", "Event statistics", eventLabels.size(), -0.5, eventLabels.size() - 0.5, o2::aod::evsel::kNsel + 1, -0.5, (float)o2::aod::evsel::kNsel + 0.5); + TH2I* histEvents = new TH2I("EventStats", "Event statistics", eventLabels.size(), -0.5, eventLabels.size() - 0.5, o2::aod::evsel::kNsel + 1, -0.5, static_cast(o2::aod::evsel::kNsel) + 0.5); int ib = 1; for (auto label = eventLabels.begin(); label != eventLabels.end(); label++, ib++) { histEvents->GetXaxis()->SetBinLabel(ib, (*label).Data()); diff --git a/PWGDQ/TableProducer/tableMakerMuonMchTrkEfficiency.cxx b/PWGDQ/TableProducer/tableMakerMuonMchTrkEfficiency.cxx index 6d2d711491f..92452eb0c29 100644 --- a/PWGDQ/TableProducer/tableMakerMuonMchTrkEfficiency.cxx +++ b/PWGDQ/TableProducer/tableMakerMuonMchTrkEfficiency.cxx @@ -187,7 +187,7 @@ struct tableMakerMuonMchTrkEfficiency { // define axes to be used // and the correspondent histograms - auto vbins = (std::vector)binsMuonPt; + auto vbins = std::vector(binsMuonPt); const AxisSpec axisEta{190, 2.3, 4.2, "|#eta|"}; const AxisSpec axisEtaRed{nEtaBins, etaMuonMin, etaMuonMax, "|#eta|"}; const AxisSpec axisEtaGenRed{nEtaBins, etaMuonMin, etaMuonMax, "|#eta| Gen"}; diff --git a/PWGDQ/TableProducer/tableMaker_withAssoc.cxx b/PWGDQ/TableProducer/tableMaker_withAssoc.cxx index 8ba2ee20340..a49d29cb830 100644 --- a/PWGDQ/TableProducer/tableMaker_withAssoc.cxx +++ b/PWGDQ/TableProducer/tableMaker_withAssoc.cxx @@ -286,7 +286,7 @@ struct TableMaker { struct : ConfigurableGroup { Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable fConfigCcdbPathTPC{"ccdb-path-tpc", "Users/z/zhxiong/TPCPID/PostCalib", "base path to the ccdb object"}; - Configurable fConfigCcdbPathZorro{"ccdb-path-zorro", "/Users/m/mpuccio/EventFiltering/OTS/Chunked/", "base path to the ccdb object for zorro"}; + Configurable fConfigCcdbPathZorro{"ccdb-path-zorro", "EventFiltering/Zorro/", "base path to the ccdb object for zorro"}; Configurable fConfigNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; Configurable fConfigGeoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; Configurable fConfigGrpMagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; @@ -1397,7 +1397,7 @@ struct TableMaker { if constexpr (static_cast(TTrackFillMap & VarManager::ObjTypes::TrackV0Bits)) { // BIT0-4: V0Bits trackFilteringTag |= static_cast(track.pidbit()); for (int iv0 = 0; iv0 < 5; iv0++) { - if (track.pidbit() & (uint8_t(1) << iv0)) { + if (track.pidbit() & (static_cast(1) << iv0)) { (dynamic_cast(fStatsList->At(kStatsTracks)))->Fill(fTrackCuts.size() + static_cast(iv0)); } } diff --git a/PWGDQ/Tasks/CMakeLists.txt b/PWGDQ/Tasks/CMakeLists.txt index 6e5ce44fb90..59594f03b54 100644 --- a/PWGDQ/Tasks/CMakeLists.txt +++ b/PWGDQ/Tasks/CMakeLists.txt @@ -18,6 +18,12 @@ # # Skipped under recc, which caches compilations remotely instead. add_library(TableReaderWithAssocPCH OBJECT tableReaderWithAssocPCH.cxx) +# The PCH compile is one of the heaviest translation units in the tree -- it +# instantiates the whole data model up front -- but add_library() does not put +# it in the 'analysis' job pool that o2physics_add_dpl_workflow() uses for the +# workflows. Left out, it runs unthrottled alongside the pooled compilations +# and defeats the per-job memory budget the pool exists to enforce. +set_property(TARGET TableReaderWithAssocPCH PROPERTY JOB_POOL_COMPILE analysis) target_link_libraries(TableReaderWithAssocPCH PUBLIC O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::PWGDQCore O2Physics::MLCore) if(NOT DEFINED ENV{USE_RECC}) target_precompile_headers(TableReaderWithAssocPCH PRIVATE @@ -26,6 +32,7 @@ endif() o2physics_add_dpl_workflow(table-reader SOURCES tableReader.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore O2Physics::MLCore COMPONENT_NAME Analysis) @@ -73,56 +80,67 @@ o2physics_add_dpl_workflow(table-reader-with-assoc-dilepton-track-track-pairing o2physics_add_dpl_workflow(table-reader-with-assoc-direct SOURCES tableReader_withAssoc_direct.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::PWGDQCore O2Physics::MLCore O2::ReconstructionDataFormats O2::DetectorsCommonDataFormats O2::DetectorsVertexing O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(efficiency SOURCES dqEfficiency.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(efficiency-with-assoc SOURCES dqEfficiency_withAssoc.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(efficiency-with-assoc-direct SOURCES dqEfficiency_withAssoc_direct.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore O2::ReconstructionDataFormats O2::DetectorsCommonDataFormats O2::DetectorsVertexing COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(energy-correlator-direct SOURCES dqEnergyCorrelator_direct.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(filter-pp SOURCES filterPP.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(filter-pp-with-association SOURCES filterPPwithAssociation.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(filter-pb-pb SOURCES filterPbPb.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore O2Physics::SGCutParHolder COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(v0-selector SOURCES v0selector.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DCAFitter O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(dalitz-selection SOURCES DalitzSelection.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(flow SOURCES dqFlow.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore O2Physics::GFWCore COMPONENT_NAME Analysis) @@ -133,21 +151,25 @@ o2physics_add_dpl_workflow(task-muon-mch-trk-eff o2physics_add_dpl_workflow(task-j-psi-hf SOURCES taskJpsiHf.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-muon-dca SOURCES muonDCA.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(correlation SOURCES dqCorrelation.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore O2Physics::GFWCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-mch-align-record SOURCES mchAlignRecord.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore @@ -168,11 +190,13 @@ o2physics_add_dpl_workflow(task-mch-align-record o2physics_add_dpl_workflow(task-muon-mid-eff SOURCES MIDefficiency.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::MIDBase + REUSE_FROM DQAnalysisPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore O2::MIDBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-fwd-track-pid SOURCES taskFwdTrackPid.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) @@ -183,45 +207,54 @@ o2physics_add_dpl_workflow(quarkonia-to-hyperons o2physics_add_dpl_workflow(model-converter-mult-pv SOURCES ModelConverterMultPv.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(model-converter-event-extended SOURCES ModelConverterEventExtended.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(model-converter-mc-reduced-event SOURCES ModelConverterReducedMCEvents.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tag-and-probe SOURCES TagAndProbe.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(qa-matching SOURCES qaMatching.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(mft-mch-matcher SOURCES mftMchMatcher.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(muon-global-alignment SOURCES muonGlobalAlignment.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore O2::MCHGeometryTransformer COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(j-psi-muon-correlations SOURCES jPsiMuonCorrelations.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore O2::MCHGeometryTransformer COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(global-muon-matcher SOURCES global-muon-matcher.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore O2::MCHGeometryTransformer COMPONENT_NAME Analysis) diff --git a/PWGDQ/Tasks/DalitzSelection.cxx b/PWGDQ/Tasks/DalitzSelection.cxx index e0452279ab2..ee455aae569 100644 --- a/PWGDQ/Tasks/DalitzSelection.cxx +++ b/PWGDQ/Tasks/DalitzSelection.cxx @@ -377,8 +377,8 @@ struct DalitzSelection { { fSkipEvent = true; for (const auto& track1 : tracksBarrel) { - auto filterMap = uint8_t(0); - auto filterMapProbe = uint8_t(0); + auto filterMap = static_cast(0); + auto filterMapProbe = static_cast(0); int fullTrackIdx = 0; if constexpr (isReassoc) { auto const& fullTrack = track1.template track_as(); @@ -417,7 +417,7 @@ struct DalitzSelection { int i = 0; for (auto cut = fTrackCuts.begin(); cut != fTrackCuts.end(); ++cut, ++i) { if ((*cut).IsSelected(static_cast(VarManager::fgValues))) { - filterMap |= (uint8_t(1) << i); + filterMap |= (static_cast(1) << i); fSkipEvent = false; } } @@ -425,7 +425,7 @@ struct DalitzSelection { i = 0; for (auto cut = fTrackCutsProbe.begin(); cut != fTrackCutsProbe.end(); ++cut, ++i) { if ((*cut).IsSelected(static_cast(VarManager::fgValues))) { - filterMapProbe |= (uint8_t(1) << i); + filterMapProbe |= (static_cast(1) << i); } } } @@ -532,7 +532,7 @@ struct DalitzSelection { int icut = 0; auto trackCut = fTrackCuts.begin(); for (auto pairCut = fPairCuts.begin(); pairCut != fPairCuts.end(); pairCut++, trackCut++, icut++) { - if (!(twoTracksFilterMap & (uint8_t(1) << icut))) { + if (!(twoTracksFilterMap & (static_cast(1) << icut))) { continue; } if ((*pairCut).IsSelected(static_cast(VarManager::fgValues))) { @@ -554,24 +554,24 @@ struct DalitzSelection { if (!isLikeSign) { if (isReassoc & fConfigOptions.fRemoveDoubleCounting) { // if we want to remove double-counting, we fill separately the map for this event and for previous ones, in order to check if it was previously selected - fDalitzmapAmbiguity[trackIdx1] |= (uint8_t(1) << icut); + fDalitzmapAmbiguity[trackIdx1] |= (static_cast(1) << icut); } else { - fDalitzmap[trackIdx1] |= (uint8_t(1) << icut); + fDalitzmap[trackIdx1] |= (static_cast(1) << icut); } if (fIsTagAndProbe) { if (isReassoc & fConfigOptions.fRemoveDoubleCounting) { - fDalitzmapProbeAmbiguity[trackIdx2] |= (uint8_t(1) << icut); + fDalitzmapProbeAmbiguity[trackIdx2] |= (static_cast(1) << icut); } else { - fDalitzmapProbe[trackIdx2] |= (uint8_t(1) << icut); + fDalitzmapProbe[trackIdx2] |= (static_cast(1) << icut); } if (fConfigOptions.fQA && !isPairAlreadySelected) { fHistMan->FillHistClass(Form("Pair_%s_%s_%s", (*trackCut).GetName(), fTrackCutsProbe.at(icut).GetName(), (*pairCut).GetName()), static_cast(VarManager::fgValues)); } } else { if (isReassoc & fConfigOptions.fRemoveDoubleCounting) { - fDalitzmapAmbiguity[trackIdx2] |= (uint8_t(1) << icut); + fDalitzmapAmbiguity[trackIdx2] |= (static_cast(1) << icut); } else { - fDalitzmap[trackIdx2] |= (uint8_t(1) << icut); + fDalitzmap[trackIdx2] |= (static_cast(1) << icut); } if (fConfigOptions.fQA && !isPairAlreadySelected) { fHistMan->FillHistClass(Form("Pair_%s_%s", (*trackCut).GetName(), (*pairCut).GetName()), static_cast(VarManager::fgValues)); @@ -581,8 +581,8 @@ struct DalitzSelection { if (fConfigOptions.fQA && !isPairAlreadySelected) { fHistMan->FillHistClass(fIsTagAndProbe ? Form("PairLS_%s_%s_%s", (*trackCut).GetName(), fTrackCutsProbe.at(icut).GetName(), (*pairCut).GetName()) : Form("PairLS_%s_%s", (*trackCut).GetName(), (*pairCut).GetName()), static_cast(VarManager::fgValues)); if (fConfigOptions.fConfigBitsToPropagate == 2) { - fDalitzmapLS[trackIdx1] |= (uint8_t(1) << icut); - fDalitzmapLS[trackIdx2] |= (uint8_t(1) << icut); + fDalitzmapLS[trackIdx1] |= (static_cast(1) << icut); + fDalitzmapLS[trackIdx2] |= (static_cast(1) << icut); } } } // end if like-sign @@ -594,8 +594,8 @@ struct DalitzSelection { // Fill Hists if (fConfigOptions.fQA && !fSkipEvent) { for (const auto& track : tracks1) { - auto filterMap = uint8_t(0); - auto filterMapProbe = uint8_t(0); + auto filterMap = static_cast(0); + auto filterMapProbe = static_cast(0); if constexpr (isReassoc) { auto const& fullTrack = track.template track_as(); filterMap = fDalitzmap[fullTrack.globalIndex()]; // cppcheck-suppress redundantInitialization @@ -657,12 +657,12 @@ struct DalitzSelection { int icut = 0; auto trackCut = fTrackCuts.begin(); for (auto pairCut = fPairCuts.begin(); pairCut != fPairCuts.end(); pairCut++, trackCut++, icut++) { - if (filterMap & (uint8_t(1) << icut)) { - static_cast(fStatsList->At(0))->Fill(fIsTagAndProbe ? 2 * icut + 1 : icut + 1); + if (filterMap & (static_cast(1) << icut)) { + dynamic_cast(fStatsList->At(0))->Fill(fIsTagAndProbe ? 2 * icut + 1 : icut + 1); fHistMan->FillHistClass(fIsTagAndProbe ? Form("TrackBarrelTag_%s_%s_%s", (*trackCut).GetName(), fTrackCutsProbe.at(icut).GetName(), (*pairCut).GetName()) : Form("TrackBarrel_%s_%s", (*trackCut).GetName(), (*pairCut).GetName()), static_cast(VarManager::fgValues)); } - if (filterMapProbe & (uint8_t(1) << icut)) { - static_cast(fStatsList->At(0))->Fill(2 * icut + 2); + if (filterMapProbe & (static_cast(1) << icut)) { + dynamic_cast(fStatsList->At(0))->Fill(2 * icut + 2); fHistMan->FillHistClass(Form("TrackBarrelProbe_%s_%s_%s", (*trackCut).GetName(), fTrackCutsProbe.at(icut).GetName(), (*pairCut).GetName()), static_cast(VarManager::fgValues)); } } @@ -821,7 +821,7 @@ struct DalitzSelection { bool isEventSelected = fEventCut->IsSelected(static_cast(VarManager::fgValues)); if (isEventSelected) { - static_cast(fStatsList->At(0))->Fill(0); + dynamic_cast(fStatsList->At(0))->Fill(0); auto bc = collision.template bc_as(); @@ -896,7 +896,7 @@ struct DalitzSelection { if (isEventSelected) { - static_cast(fStatsList->At(0))->Fill(0); + dynamic_cast(fStatsList->At(0))->Fill(0); auto bc = collision.template bc_as(); @@ -971,7 +971,7 @@ struct DalitzSelection { if (isEventSelected) { - static_cast(fStatsList->At(0))->Fill(0); + dynamic_cast(fStatsList->At(0))->Fill(0); auto bc = collision.template bc_as(); diff --git a/PWGDQ/Tasks/MIDefficiency.cxx b/PWGDQ/Tasks/MIDefficiency.cxx index 26bca045c2b..3e8d103e4f5 100644 --- a/PWGDQ/Tasks/MIDefficiency.cxx +++ b/PWGDQ/Tasks/MIDefficiency.cxx @@ -78,7 +78,7 @@ struct midEfficiency { o2::mid::Mapping mapping; // Filter only for MCH-MID matched tracks - Filter muonTrackType = aod::fwdtrack::trackType == uint8_t(3); + Filter muonTrackType = aod::fwdtrack::trackType == static_cast(3); void init(o2::framework::InitContext const& /*ic*/) { diff --git a/PWGDQ/Tasks/TagAndProbe.cxx b/PWGDQ/Tasks/TagAndProbe.cxx index 8ee82257af1..9fec1a9907a 100644 --- a/PWGDQ/Tasks/TagAndProbe.cxx +++ b/PWGDQ/Tasks/TagAndProbe.cxx @@ -213,7 +213,7 @@ struct AnalysisTagAndProbe { fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(true); fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); - VarManager::SetCollisionSystem((TString)fConfigOptions.collisionSystem, fConfigOptions.centerMassEnergy); // set collision system and center of mass energy + VarManager::SetCollisionSystem(TString(fConfigOptions.collisionSystem), fConfigOptions.centerMassEnergy); // set collision system and center of mass energy DefineHistograms(fHistMan, histNames.Data(), fConfigAddSEPHistogram.value.data()); // define all histograms // dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill diff --git a/PWGDQ/Tasks/dqEfficiency.cxx b/PWGDQ/Tasks/dqEfficiency.cxx index afcb0388c08..0495204234c 100644 --- a/PWGDQ/Tasks/dqEfficiency.cxx +++ b/PWGDQ/Tasks/dqEfficiency.cxx @@ -310,7 +310,7 @@ struct AnalysisTrackSelection { continue; } for (unsigned int j = 0; j < fTrackCuts.size(); j++) { - if (filterMap & (uint8_t(1) << j)) { + if (filterMap & (static_cast(1) << j)) { fHistMan->FillHistClass(fHistNamesMCMatched[j][iMC].Data(), VarManager::fgValues); } } // end loop over cuts @@ -483,7 +483,7 @@ struct AnalysisMuonSelection { continue; } for (unsigned int j = 0; j < fTrackCuts.size(); j++) { - if (filterMap & (uint8_t(1) << j)) { + if (filterMap & (static_cast(1) << j)) { fHistMan->FillHistClass(fHistNamesMCMatched[j][iMC].Data(), VarManager::fgValues); } } // end loop over cuts @@ -876,7 +876,7 @@ struct AnalysisSameEventPairing { // Loop over all fulfilled cuts and fill pair histograms for (unsigned int icut = 0; icut < ncuts; icut++) { - if (twoTrackFilter & (uint8_t(1) << icut)) { + if (twoTrackFilter & (static_cast(1) << icut)) { if (t1.sign() * t2.sign() < 0) { fHistMan->FillHistClass(histNames[icut][0].Data(), VarManager::fgValues); if (fConfigAmbiguousHist && !(t1.isAmbiguous() || t2.isAmbiguous())) { @@ -1369,7 +1369,7 @@ struct AnalysisDileptonTrackTrack { float* fValuesQuadruplet = nullptr; std::vector fQuadrupletCutNames; - AnalysisCompositeCut fDileptonCut; + AnalysisCompositeCut fDileptonCut{}; std::vector fQuadrupletCuts; TString fTrackCutName1; TString fTrackCutName2; diff --git a/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx b/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx index 176d7ce64b2..9b759f453e9 100644 --- a/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx +++ b/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx @@ -24,6 +24,7 @@ #include "PWGDQ/Core/VarManager.h" #include "PWGDQ/DataModel/ReducedInfoTables.h" +#include "Common/CCDB/RCTSelectionFlags.h" #include "Common/Core/TableHelper.h" #include @@ -164,8 +165,8 @@ DECLARE_SOA_COLUMN(Etaee, etaee, float); DECLARE_SOA_COLUMN(Rapee, rapee, float); DECLARE_SOA_COLUMN(Phiee, phiee, float); DECLARE_SOA_COLUMN(Ptee, ptee, float); -DECLARE_SOA_COLUMN(Lxyee, lxyee, float); -DECLARE_SOA_COLUMN(LxyeePoleMass, lxyeepolemass, float); +DECLARE_SOA_COLUMN(Lxyee, lxyJPsi2ee, float); +DECLARE_SOA_COLUMN(LxyeePoleMass, lxyJPsi2eePoleMass, float); DECLARE_SOA_COLUMN(Lzee, lzee, float); DECLARE_SOA_COLUMN(LxyeePoleMassPVrecomputed, lxyeePoleMassPVrecomputed, float); DECLARE_SOA_COLUMN(Vx, vx, float); @@ -178,10 +179,26 @@ DECLARE_SOA_COLUMN(DcaZ2, dcaZ2, float); DECLARE_SOA_COLUMN(MultiplicityFT0A, multiplicityFT0AJPsi2ee, float); DECLARE_SOA_COLUMN(MultiplicityFT0C, multiplicityFT0CJPsi2ee, float); DECLARE_SOA_COLUMN(PercentileFT0M, percentileFT0MJPsi2ee, float); +DECLARE_SOA_COLUMN(PercentileFT0C, percentileFT0CJPsi2ee, float); DECLARE_SOA_COLUMN(MultiplicityNContrib, multiplicityNContribJPsi2ee, float); DECLARE_SOA_COLUMN(AmbiguousInBunchPairs, AmbiguousJpsiPairsInBunch, bool); DECLARE_SOA_COLUMN(AmbiguousOutOfBunchPairs, AmbiguousJpsiPairsOutOfBunch, bool); DECLARE_SOA_COLUMN(Corrassoc, corrassoc, bool); +// Additional columns for prompt-non-prompt JPsi separation (BDT training), identical to tableReader_withAssoc +DECLARE_SOA_COLUMN(PairFilter, pairFilter, uint32_t); //! twoTrackFilter (cut bits + ambiguity bits 28-31) +DECLARE_SOA_COLUMN(McDecision, mcDecision, uint32_t); //! MC signal bits, 0 in data +DECLARE_SOA_COLUMN(TauxyeeErr, tauxyeeErr, float); //! kVertexingTauxyErr: in ns (not cm) and with the reconstructed mass +DECLARE_SOA_COLUMN(TauzeeErr, tauzeeErr, float); //! kVertexingTauzErr: in ns (not cm) and with the reconstructed mass +DECLARE_SOA_COLUMN(LxyeeProjected, lxyeeProjected, float); //! kVertexingLxyProjected +DECLARE_SOA_COLUMN(LxyeeOverErr, lxyeeOverErr, float); //! kVertexingLxyOverErr +DECLARE_SOA_COLUMN(Lxyzee, lxyzee, float); //! kVertexingLxyz +DECLARE_SOA_COLUMN(LxyzeeOverErr, lxyzeeOverErr, float); //! kVertexingLxyzOverErr +DECLARE_SOA_COLUMN(Chi2PCAee, chi2PCAee, float); //! kVertexingChi2PCA +DECLARE_SOA_COLUMN(CosPAee, cosPAee, float); //! kCosPointingAngle +DECLARE_SOA_COLUMN(DcaXYOverSig1, dcaXYOverSig1, float); +DECLARE_SOA_COLUMN(DcaZOverSig1, dcaZOverSig1, float); +DECLARE_SOA_COLUMN(DcaXYOverSig2, dcaXYOverSig2, float); +DECLARE_SOA_COLUMN(DcaZOverSig2, dcaZOverSig2, float); DECLARE_SOA_COLUMN(DeltaEta, deltaEta, float); DECLARE_SOA_COLUMN(DeltaPhi, deltaPhi, float); // Candidate columns efficiency calculation for prompt-non-prompt JPsi separation @@ -224,10 +241,19 @@ DECLARE_SOA_TABLE(JPsiMuonCandidates, "AOD", "DQJPSIMUONA", dqanalysisflags::Ptleg1, dqanalysisflags::Etaleg1, dqanalysisflags::Phileg1, dqanalysisflags::Ptleg2, dqanalysisflags::Etaleg2, dqanalysisflags::Phileg2, dqanalysisflags::McFlag); DECLARE_SOA_TABLE(JPsieeCandidates, "AOD", "DQPSEUDOPROPER", + dqanalysisflags::PairFilter, dqanalysisflags::McDecision, dqanalysisflags::Massee, dqanalysisflags::Ptee, dqanalysisflags::Etaee, dqanalysisflags::Rapee, dqanalysisflags::Phiee, dqanalysisflags::Lxyee, dqanalysisflags::LxyeePoleMass, dqanalysisflags::Lzee, dqanalysisflags::LxyeePoleMassPVrecomputed, - dqanalysisflags::Vx, dqanalysisflags::Vy, dqanalysisflags::Vz, dqanalysisflags::DcaXY1, dqanalysisflags::DcaZ1, dqanalysisflags::ITSClusterMapleg1, dqanalysisflags::TPCnsigmaElleg1, dqanalysisflags::DcaXY2, dqanalysisflags::DcaZ2, dqanalysisflags::ITSClusterMapleg2, dqanalysisflags::TPCnsigmaElleg2, - dqanalysisflags::AmbiguousInBunchPairs, dqanalysisflags::AmbiguousOutOfBunchPairs, dqanalysisflags::Corrassoc, dqanalysisflags::MultiplicityFT0A, dqanalysisflags::MultiplicityFT0C, dqanalysisflags::PercentileFT0M, dqanalysisflags::MultiplicityNContrib); + dqanalysisflags::TauxyeeErr, dqanalysisflags::TauzeeErr, + dqanalysisflags::LxyeeProjected, dqanalysisflags::LxyeeOverErr, dqanalysisflags::Lxyzee, dqanalysisflags::LxyzeeOverErr, + dqanalysisflags::Chi2PCAee, dqanalysisflags::CosPAee, + dqanalysisflags::Vx, dqanalysisflags::Vy, dqanalysisflags::Vz, + dqanalysisflags::Ptleg1, dqanalysisflags::Etaleg1, dqanalysisflags::DcaXY1, dqanalysisflags::DcaZ1, dqanalysisflags::DcaXYOverSig1, dqanalysisflags::DcaZOverSig1, + dqanalysisflags::ITSClusterMapleg1, dqanalysisflags::ITSChi2leg1, + dqanalysisflags::Ptleg2, dqanalysisflags::Etaleg2, dqanalysisflags::DcaXY2, dqanalysisflags::DcaZ2, dqanalysisflags::DcaXYOverSig2, dqanalysisflags::DcaZOverSig2, + dqanalysisflags::ITSClusterMapleg2, dqanalysisflags::ITSChi2leg2, + dqanalysisflags::AmbiguousInBunchPairs, dqanalysisflags::AmbiguousOutOfBunchPairs, dqanalysisflags::Corrassoc, + dqanalysisflags::MultiplicityFT0C, dqanalysisflags::PercentileFT0C, dqanalysisflags::MultiplicityNContrib); DECLARE_SOA_TABLE(OniaMCTruth, "AOD", "MCTRUTHONIA", dqanalysisflags::OniaPt, dqanalysisflags::OniaEta, dqanalysisflags::OniaY, dqanalysisflags::OniaPhi, dqanalysisflags::OniaVz, dqanalysisflags::OniaVtxZ, dqanalysisflags::MultiplicityFT0A, dqanalysisflags::MultiplicityFT0C, dqanalysisflags::PercentileFT0M, dqanalysisflags::MultiplicityNContrib); } // namespace o2::aod @@ -309,6 +335,12 @@ struct AnalysisEventSelection { Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable fConfigNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + o2::framework::Configurable cfgUseRCT{"cfgUseRCT", false, "Apply RCT quality selection"}; + o2::framework::Configurable cfgRCTPath{"cfgRCTPath", "RCT/Flags/RunFlags", "CCDB path to the RCT run flags"}; + o2::framework::Configurable cfgRCTLabel{"cfgRCTLabel", "CBT", "RCT selection label"}; + o2::framework::Configurable cfgCheckZDC{"cfgCheckZDC", false, "Also check ZDC quality"}; + o2::framework::Configurable cfgRCTPassName{"cfgRCTPassName", "", "Reconstruction pass; leave empty for run-only lookup"}; + HistogramManager* fHistMan = nullptr; MixingHandler* fMixHandler = nullptr; @@ -322,6 +354,10 @@ struct AnalysisEventSelection { std::map fMetadataRCT, fHeader; int fCurrentRun = -1; + o2::aod::rctsel::RCTFlagsChecker rctChecker{"CBT"}; + std::map rctFlags; + int rctRun = -1; + void init(o2::framework::InitContext& context) { if (context.mOptions.get("processDummy")) { @@ -378,6 +414,61 @@ struct AnalysisEventSelection { fCCDB->setLocalObjectValidityChecking(); fCCDB->setCreatedNotAfter(fConfigNoLaterThan.value); fCCDBApi.init(fConfigCcdbUrl.value); + + if (cfgUseRCT.value) { + if (cfgRCTLabel.value != "CBT" && cfgRCTLabel.value != "CBT_hadronPID" && cfgRCTLabel.value != "CBT_electronPID" && cfgRCTLabel.value != "CBT_calo" && cfgRCTLabel.value != "CBT_muon" && cfgRCTLabel.value != "CBT_muon_glo") { + LOGF(fatal, "RCT Label \"%s\" is unknown.", cfgRCTLabel.value.c_str()); + } + + rctChecker.init(cfgRCTLabel.value, cfgCheckZDC.value); + + if (!rctChecker.any()) { + LOGF(fatal, "Empty RCT mask: check cfgRCTLabel."); + } + } + } + + void loadRCT(int run, uint64_t timestamp) + { + std::map metadata{{"run", std::to_string(run)}}; + + LOG(info) << "Requesting " << cfgRCTPath.value << " run=" << run << " timestamp=" << timestamp; + + if (!cfgRCTPassName.value.empty()) { + metadata["passName"] = cfgRCTPassName.value; + } + + auto* flags = fCCDB->getSpecific>(cfgRCTPath.value, timestamp, metadata); + if (flags == nullptr && !cfgRCTPassName.value.empty()) { + metadata.erase("passName"); + flags = fCCDB->getSpecific>(cfgRCTPath.value, timestamp, metadata); + } + + if (flags == nullptr || flags->empty()) { + LOGF(fatal, "Missing or empty RCT flags for run %d.", run); + } + + rctFlags = *flags; + rctRun = run; + } + + bool passesRCT(int run, uint64_t timestamp) + { + if (!cfgUseRCT.value) { + return true; + } + + if (run != rctRun) { + loadRCT(run, timestamp); + } + + auto iterator = rctFlags.upper_bound(timestamp); + if (iterator == rctFlags.begin()) { + return false; + } + + const uint32_t flags = std::prev(iterator)->second; + return (static_cast(flags) & rctChecker.value()) == 0; } template @@ -406,7 +497,7 @@ struct AnalysisEventSelection { if (fConfigQA) { fHistMan->FillHistClass("Event_BeforeCuts", dqefficiency_helpers::varValues()); // automatically fill all the histograms in the class Event } - if (fEventCut->IsSelected(dqefficiency_helpers::varValues())) { + if (fEventCut->IsSelected(dqefficiency_helpers::varValues()) && passesRCT(event.runNumber(), event.timestamp())) { if (fConfigQA) { fHistMan->FillHistClass("Event_AfterCuts", dqefficiency_helpers::varValues()); } @@ -2166,7 +2257,7 @@ struct AnalysisSameEventPairing { fHistMan->SetUseDefaultVariableNames(kTRUE); fHistMan->SetDefaultVarNames(dqefficiency_helpers::varNames(), dqefficiency_helpers::varUnits()); - VarManager::SetCollisionSystem((TString)fConfigOptions.collisionSystem, fConfigOptions.centerMassEnergy); // set collision system and center of mass energy + VarManager::SetCollisionSystem(TString(fConfigOptions.collisionSystem), fConfigOptions.centerMassEnergy); // set collision system and center of mass energy DefineHistograms(fHistMan, histNames.Data(), fConfigAddSEPHistogram.value.data()); // define all histograms dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON @@ -2371,6 +2462,28 @@ struct AnalysisSameEventPairing { } } } + constexpr bool eventHasVtxCovForTable = ((TEventFillMap & VarManager::ObjTypes::ReducedEventVtxCov) > 0); + if constexpr ((TTrackFillMap & VarManager::ObjTypes::ReducedTrackBarrelPID) > 0 && trackHasCov && TTwoProngFitter && eventHasVtxCovForTable) { + // kVertexingProcCode == 0: the fit failed; FillPairVertexing then returns early without updating the + // projected / pseudo-proper / pointing-angle variables, so they would be stale -> skip the pair + if (t1.sign() * t2.sign() < 0 && mcDecision > 0) { + const bool ambiInBunch = (twoTrackFilter & (static_cast(1) << 28)) || (twoTrackFilter & (static_cast(1) << 29)); + const bool ambiOutOfBunch = (twoTrackFilter & (static_cast(1) << 30)) || (twoTrackFilter & (static_cast(1) << 31)); + PromptNonPromptSepTable(twoTrackFilter, mcDecision, + VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kRap], VarManager::fgValues[VarManager::kPhi], + VarManager::fgValues[VarManager::kVertexingTauxyProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMass], VarManager::fgValues[VarManager::kVertexingTauzProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMassRecalculatePV], + VarManager::fgValues[VarManager::kVertexingTauxyErr], VarManager::fgValues[VarManager::kVertexingTauzErr], + VarManager::fgValues[VarManager::kVertexingLxyProjected], VarManager::fgValues[VarManager::kVertexingLxyOverErr], VarManager::fgValues[VarManager::kVertexingLxyz], VarManager::fgValues[VarManager::kVertexingLxyzOverErr], + VarManager::fgValues[VarManager::kVertexingChi2PCA], VarManager::fgValues[VarManager::kCosPointingAngle], + VarManager::fgValues[VarManager::kVtxX], VarManager::fgValues[VarManager::kVtxY], VarManager::fgValues[VarManager::kVtxZ], + t1.pt(), t1.eta(), VarManager::fgValues[VarManager::kDCAxy1], VarManager::fgValues[VarManager::kDCAz1], VarManager::fgValues[VarManager::kDCAxyOverSig1], VarManager::fgValues[VarManager::kDCAzOverSig1], + VarManager::fgValues[VarManager::kITSclusterMap1], t1.itsChi2NCl(), + t2.pt(), t2.eta(), VarManager::fgValues[VarManager::kDCAxy2], VarManager::fgValues[VarManager::kDCAz2], VarManager::fgValues[VarManager::kDCAxyOverSig2], VarManager::fgValues[VarManager::kDCAzOverSig2], + VarManager::fgValues[VarManager::kITSclusterMap2], t2.itsChi2NCl(), + ambiInBunch, ambiOutOfBunch, isCorrectAssoc_leg1 && isCorrectAssoc_leg2, + VarManager::fgValues[VarManager::kMultFT0C], VarManager::fgValues[VarManager::kCentFT0C], VarManager::fgValues[VarManager::kVtxNcontribReal]); + } + } } if constexpr (TPairType == VarManager::kDecayToMuMu) { @@ -2469,11 +2582,6 @@ struct AnalysisSameEventPairing { // Fill histograms bool isAmbiInBunch = false; bool isAmbiOutOfBunch = false; - bool isCorrect_pair = false; - if (isCorrectAssoc_leg1 && isCorrectAssoc_leg2) { - isCorrect_pair = true; - } - for (int icut = 0; icut < ncuts; icut++) { if (twoTrackFilter & (static_cast(1) << icut)) { isAmbiInBunch = (twoTrackFilter & (static_cast(1) << 28)) || (twoTrackFilter & (static_cast(1) << 29)); @@ -2482,10 +2590,6 @@ struct AnalysisSameEventPairing { fHistMan->FillHistClass(histNames[icut][0].Data(), dqefficiency_helpers::varValues()); // reconstructed, unmatched for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals if (mcDecision & (static_cast(1) << isig)) { - PromptNonPromptSepTable(VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kRap], VarManager::fgValues[VarManager::kPhi], - VarManager::fgValues[VarManager::kVertexingTauxyProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMass], VarManager::fgValues[VarManager::kVertexingTauzProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMassRecalculatePV], - VarManager::fgValues[VarManager::kVtxX], VarManager::fgValues[VarManager::kVtxY], VarManager::fgValues[VarManager::kVtxZ], VarManager::fgValues[VarManager::kDCAxy1], VarManager::fgValues[VarManager::kDCAz1], VarManager::fgValues[VarManager::kITSclusterMap1], VarManager::fgValues[VarManager::kTPCnSigmaEl1], VarManager::fgValues[VarManager::kDCAxy2], VarManager::fgValues[VarManager::kDCAz2], VarManager::fgValues[VarManager::kITSclusterMap2], VarManager::fgValues[VarManager::kTPCnSigmaEl2], - isAmbiInBunch, isAmbiOutOfBunch, isCorrect_pair, VarManager::fgValues[VarManager::kMultFT0A], VarManager::fgValues[VarManager::kMultFT0C], VarManager::fgValues[VarManager::kCentFT0M], VarManager::fgValues[VarManager::kVtxNcontribReal]); fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][0].Data(), dqefficiency_helpers::varValues()); // matched signal if (useMiniTree.fConfigMiniTree) { if constexpr (TPairType == VarManager::kDecayToMuMu) { @@ -2638,6 +2742,11 @@ struct AnalysisSameEventPairing { if (!event.has_reducedMCevent()) { continue; } + // Fill the event-wise variables (e.g. centrality) of this reconstructed event, otherwise the generated particles and pairs + // get the values left over from the last event processed in runSameEventPairing + VarManager::ResetValues(0, VarManager::kNVars); + VarManager::FillEvent(event, dqefficiency_helpers::varValues()); + VarManager::FillEvent(event.reducedMCevent(), dqefficiency_helpers::varValues()); FinalStateMcParticleIndices.clear(); @@ -3818,8 +3927,28 @@ struct AnalysisAsymmetricPairing { int sign1 = 0; int sign2 = 0; uint32_t mcDecision = 0; - ditrackList.reserve(1); - ditrackExtraList.reserve(1); + + // estimate reserved size + int64_t reserveSize = 0; + for (auto const& event : events) { + auto groupedLegAAssocs = legACandidateAssocs.sliceBy(preslice, event.globalIndex()); + auto groupedLegBAssocs = legBCandidateAssocs.sliceBy(preslice, event.globalIndex()); + size_t nGoodA = 0; + size_t nGoodB = 0; + for (auto const& t : groupedLegAAssocs) { + if (t.isBarrelSelected_raw()) { + nGoodA++; + } + } + for (auto const& t : groupedLegBAssocs) { + if (t.isBarrelSelected_raw()) { + nGoodB++; + } + } + reserveSize += nGoodA * nGoodB; + } + ditrackList.reserve(reserveSize); + ditrackExtraList.reserve(reserveSize); constexpr bool trackHasCov = ((TTrackFillMap & VarManager::ObjTypes::TrackCov) > 0 || (TTrackFillMap & VarManager::ObjTypes::ReducedTrackBarrelCov) > 0); diff --git a/PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx b/PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx index ba62d644f59..82d99d1ed75 100644 --- a/PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx +++ b/PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx @@ -776,7 +776,7 @@ struct AnalysisTrackSelection { // compute quantities which depend on the associated collision, such as DCA if (track.collisionId() != event.globalIndex()) { VarManager::FillTrackCollision(track, event); - // cout << "Filled track observables for association" << endl; + // cout << "Filled track observables for association" << endl; } bool isCorrectAssoc = false; @@ -1160,6 +1160,8 @@ struct AnalysisSameEventPairing { Configurable fConfigPseudoRMQA{"cfgPseudoRMQA", false, "If true, fill QA histograms with pseudo-proper decay length analysis with polarization RM"}; Configurable fConfigTruthPbPbMIDYHE{"cfgTruthPbPbmidyHE", false, "If true, fill QA histograms with dielectron pairs in helicity frame"}; Configurable fConfigTruthPbPbMIDYCS{"cfgTruthPbPbmidyCS", false, "If true, fill QA histograms with dielectron pairs in collion-soper frame"}; + Configurable fConfigTruthPbPbMIDYGJB1{"cfgTruthPbPbmidyGJB1", false, "If true, fill QA histograms with dielectron pairs in Gottfried-Jackson frame, beam 1 (+z)"}; + Configurable fConfigTruthPbPbMIDYGJB2{"cfgTruthPbPbmidyGJB2", false, "If true, fill QA histograms with dielectron pairs in Gottfried-Jackson frame, beam 2 (-z)"}; Configurable fConfigAddSEPHistogram{"cfgAddSEPHistogram", "", "Comma separated list of histograms"}; Configurable fConfigAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format"}; Configurable useRemoteField{"cfgUseRemoteField", false, "Chose whether to fetch the magnetic field from ccdb or set it manually"}; @@ -1537,6 +1539,12 @@ struct AnalysisSameEventPairing { if (fConfigOptions.fConfigTruthPbPbMIDYCS.value) { histNames += Form("MCTruthGenPoldielectronPbPbPairCSSel_%s;", sig->GetName()); } + if (fConfigOptions.fConfigTruthPbPbMIDYGJB1.value) { + histNames += Form("MCTruthGenPoldielectronPbPbPairGJB1Sel_%s;", sig->GetName()); + } + if (fConfigOptions.fConfigTruthPbPbMIDYGJB2.value) { + histNames += Form("MCTruthGenPoldielectronPbPbPairGJB2Sel_%s;", sig->GetName()); + } fHasTwoProngGenMCsignals = true; } // for these pair level signals, also add histograms for each MCgenAcc cut if specified @@ -1560,6 +1568,12 @@ struct AnalysisSameEventPairing { if (fConfigOptions.fConfigTruthPbPbMIDYCS.value) { histNames += Form("MCTruthGenPoldielectronPbPbPairCSSel_%s_%s;", sig->GetName(), cut->GetName()); } + if (fConfigOptions.fConfigTruthPbPbMIDYGJB1.value) { + histNames += Form("MCTruthGenPoldielectronPbPbPairGJB1Sel_%s_%s;", sig->GetName(), cut->GetName()); + } + if (fConfigOptions.fConfigTruthPbPbMIDYGJB2.value) { + histNames += Form("MCTruthGenPoldielectronPbPbPairGJB2Sel_%s_%s;", sig->GetName(), cut->GetName()); + } } } } @@ -1585,7 +1599,7 @@ struct AnalysisSameEventPairing { fHistMan->SetUseDefaultVariableNames(kTRUE); fHistMan->SetDefaultVarNames(static_cast(VarManager::fgVariableNames), static_cast(VarManager::fgVariableUnits)); - VarManager::SetCollisionSystem((TString)fConfigOptions.collisionSystem, fConfigOptions.centerMassEnergy); // set collision system and center of mass energy + VarManager::SetCollisionSystem(TString(fConfigOptions.collisionSystem), fConfigOptions.centerMassEnergy); // set collision system and center of mass energy DefineHistograms(fHistMan, histNames.Data(), fConfigOptions.fConfigAddSEPHistogram.value.data()); // define all histograms dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigOptions.fConfigAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON @@ -1976,9 +1990,9 @@ struct AnalysisSameEventPairing { if (twoTrackFilter & (static_cast(1) << icut)) { isAmbiInBunch = (twoTrackFilter & (static_cast(1) << 28)) || (twoTrackFilter & (static_cast(1) << 29)); isAmbiOutOfBunch = (twoTrackFilter & (static_cast(1) << 30)) || (twoTrackFilter & (static_cast(1) << 31)); - if (sign1 * sign2 < 0) { // +- pairs + if (sign1 * sign2 < 0) { // +- pairs fHistMan->FillHistClass(histNames[icut][0].Data(), static_cast(VarManager::fgValues)); // reconstructed, unmatched - for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals + for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals if (mcDecision & (static_cast(1) << isig)) { PromptNonPromptSepTable(VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kRap], VarManager::fgValues[VarManager::kPhi], VarManager::fgValues[VarManager::kVertexingTauxyProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMass], VarManager::fgValues[VarManager::kVertexingTauzProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMassRecalculatePV], @@ -2239,6 +2253,12 @@ struct AnalysisSameEventPairing { if (fConfigOptions.fConfigTruthPbPbMIDYCS.value) { fHistMan->FillHistClass(Form("MCTruthGenPoldielectronPbPbPairCSSel_%s", sig->GetName()), static_cast(VarManager::fgValues)); } + if (fConfigOptions.fConfigTruthPbPbMIDYGJB1.value) { + fHistMan->FillHistClass(Form("MCTruthGenPoldielectronPbPbPairGJB1Sel_%s", sig->GetName()), static_cast(VarManager::fgValues)); + } + if (fConfigOptions.fConfigTruthPbPbMIDYGJB2.value) { + fHistMan->FillHistClass(Form("MCTruthGenPoldielectronPbPbPairGJB2Sel_%s", sig->GetName()), static_cast(VarManager::fgValues)); + } if (fUseMCGenAccCut) { for (const auto& cut : fMCGenAccCuts) { @@ -2262,6 +2282,12 @@ struct AnalysisSameEventPairing { if (fConfigOptions.fConfigTruthPbPbMIDYCS.value) { fHistMan->FillHistClass(Form("MCTruthGenPoldielectronPbPbPairCSSel_%s_%s", sig->GetName(), cut->GetName()), static_cast(VarManager::fgValues)); } + if (fConfigOptions.fConfigTruthPbPbMIDYGJB1.value) { + fHistMan->FillHistClass(Form("MCTruthGenPoldielectronPbPbPairGJB1Sel_%s_%s", sig->GetName(), cut->GetName()), static_cast(VarManager::fgValues)); + } + if (fConfigOptions.fConfigTruthPbPbMIDYGJB2.value) { + fHistMan->FillHistClass(Form("MCTruthGenPoldielectronPbPbPairGJB2Sel_%s_%s", sig->GetName(), cut->GetName()), static_cast(VarManager::fgValues)); + } } } } @@ -3189,6 +3215,551 @@ struct AnalysisDileptonTrack { PROCESS_SWITCH(AnalysisDileptonTrack, processDummy, "Dummy function", true); }; +struct AnalysisDileptonTrackTrack { + OutputObj fOutputList{"output"}; + + Configurable fConfigTrackCut1{"cfgTrackCut1", "pionPIDCut1", "track1 cut"}; // used for select the tracks from SelectedTracks + Configurable fConfigTrackCut2{"cfgTrackCut2", "pionPIDCut2", "track2 cut"}; // used for select the tracks from SelectedTracks + Configurable fConfigDileptonCut{"cfgDileptonCut", "pairJpsi2", "Dilepton cut"}; + Configurable fConfigQuadrupletCuts{"cfgQuadrupletCuts", "pairX3872Cut1", "Comma separated list of Dilepton-Track-Track cut"}; + Configurable fConfigMCRecSignals{"cfgBarrelMCRecSignals", "", "Comma separated list of MC signals (reconstructed)"}; + Configurable fConfigMCGenSignals{"cfgBarrelMCGenSignals", "", "Comma separated list of MC signals (generated)"}; + Configurable fConfigDileptonMCRecSignal{"cfgDileptonMCRecSignal", "", "Comma separated list of MC signals (reconstructed)"}; + Configurable fConfigTrackPtMin{"cfgTrackPtMin", 0.15f, "Minimum pt of tracks to be used in the quadruplet"}; + Configurable fConfigTrackEtaAbs{"cfgTrackEtaAbs", 0.9f, "Maximum absolute eta of tracks to be used in the quadruplet"}; + Configurable fConfigDileptonPtMin{"cfgDileptonPtMin", 1.0f, "Minimum pt of dileptons to be used in the quadruplet"}; + Configurable fConfigDileptonEtaAbs{"cfgDileptonEtaAbs", 0.9f, "Maximum absolute eta of dileptons to be used in the quadruplet"}; + Configurable fConfigAddDileptonHistogram{"cfgAddDileptonHistogram", "barrel", "Comma separated list of histograms"}; + Configurable fConfigAddQuadrupletHistogram{"cfgAddQuadrupletHistogram", "xtojpsipipi", "Comma separated list of histograms"}; + + Configurable fConfigUseKFVertexing{"cfgUseKFVertexing", false, "Use KF Particle for secondary vertex reconstruction (DCAFitter is used by default)"}; + Configurable fConfigSetupFourProngFitter{"cfgSetupFourProngFitter", false, "Use DCA for secondary vertex reconstruction (DCAFitter is used by default)"}; + // Configurable fConfigGRPmagPath{"cfgGrpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + // Configurable fConfigUseRemoteField{"cfgUseRemoteField", false, "Chose whether to fetch the magnetic field from ccdb or set it manually"}; + // Configurable fConfigMagField{"cfgMagField", 5.0f, "Manually set magnetic field"}; + Configurable fConfigDileptonLxyCut{"cfgDileptonLxyCut", -999.f, "Dilepton Lxy cut for secondary vertex"}; + + struct : ConfigurableGroup { + Configurable fConfigUseRemoteField{"cfgUseRemoteField", false, "Chose whether to fetch the magnetic field from ccdb or set it manually"}; + Configurable fConfigGRPmagPath{"cfgGrpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + Configurable fConfigMagField{"cfgMagField", 5.0f, "Manually set magnetic field"}; + Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable fConfigNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + Configurable fConfigGeoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; + } fConfigCCDBOptions; + + Produces DileptonTrackTrackTable; + + int fCurrentRun = -1; // needed to detect if the run changed and trigger update of calibrations etc. + // uint32_t fTrackCutBitMap; // track cut bit mask to be used in the selection of tracks associated with dileptons + // cut name setting + TString fTrackCutName1; + TString fTrackCutName2; + std::vector fTrackCutNames; + uint32_t fTrackCutBitMap1 = 0; + uint32_t fTrackCutBitMap2 = 0; + bool fIsSameTrackCut = false; + AnalysisCompositeCut fDileptonCut{}; + std::vector fQuadrupletCutNames; + std::vector fQuadrupletCuts; + + Service fCCDB; + o2::ccdb::CcdbApi fCCDBApi; + + Filter eventFilter = aod::dqanalysisflags::isEventSelected > static_cast(0); + Filter dileptonFilter = aod::reducedpair::sign == 0 && aod::reducedpair::lxy > fConfigDileptonLxyCut; + Filter filterBarrelTrackSelected = aod::dqanalysisflags::isBarrelSelected > static_cast(0); + + constexpr static uint32_t fgDileptonFillMap = VarManager::ObjTypes::ReducedTrack | VarManager::ObjTypes::Pair; // fill map + + float* fValuesQuadruplet = nullptr; + HistogramManager* fHistMan = nullptr; + + // o2::analysis::QuadMlResponse fQuadMlResponse; + // std::vector fOutputMlQuad; + + std::vector fRecMCSignals; + std::vector fGenMCSignals; + + void init(o2::framework::InitContext& context) + { + bool isX3872 = context.mOptions.get("processX3872"); + bool isPsi2S = context.mOptions.get("processPsi2S"); + bool isMCGen = context.mOptions.get("processMCGen") || context.mOptions.get("processMCGenWithEventSelection"); + bool isDummy = context.mOptions.get("processDummy"); + + if (isDummy) { + if (isPsi2S || isX3872 || isMCGen) { + LOG(fatal) << "Dummy function is enabled even if there are normal process functions running! Fix your config!"; + return; + } else { + LOG(info) << "Dummy function is enabled. Skipping the rest of the init function"; + return; + } + } + + fCurrentRun = 0; + + fCCDB->setURL(fConfigCCDBOptions.fConfigCcdbUrl.value); + fCCDB->setCaching(true); + fCCDB->setLocalObjectValidityChecking(); + fCCDB->setCreatedNotAfter(fConfigCCDBOptions.fConfigNoLaterThan.value); + if (!o2::base::GeometryManager::isGeometryLoaded()) { + fCCDB->get(fConfigCCDBOptions.fConfigGeoPath); + } + + fValuesQuadruplet = new float[VarManager::kNVars]; + VarManager::SetDefaultVarNames(); + fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); + fHistMan->SetUseDefaultVariableNames(true); + fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + + TString sigNamesStr = fConfigMCRecSignals.value; + std::unique_ptr objRecSigArray(sigNamesStr.Tokenize(",")); + if (!sigNamesStr.IsNull()) { + for (int isig = 0; isig < objRecSigArray->GetEntries(); ++isig) { + MCSignal* sig = o2::aod::dqmcsignals::GetMCSignal(objRecSigArray->At(isig)->GetName()); + if (sig) { + if (sig->GetNProngs() != 4) { + LOG(fatal) << "Signal at reconstructed level requested (" << sig->GetName() << ") " << "does not have 4 prongs! Fix it"; + } + fRecMCSignals.push_back(sig); + } else { + LOG(fatal) << "Signal at reconstructed level requested (" << objRecSigArray->At(isig)->GetName() << ") " << "could not be retrieved from the library! -> skipped"; + } + } + } + + TString sigGenNamesStr = fConfigMCGenSignals.value; + std::unique_ptr objGenSigArray(sigGenNamesStr.Tokenize(",")); + for (int isig = 0; isig < objGenSigArray->GetEntries(); ++isig) { + MCSignal* sig = o2::aod::dqmcsignals::GetMCSignal(objGenSigArray->At(isig)->GetName()); + if (sig) { + if (sig->GetNProngs() == 1) { // NOTE: 1-prong signals required + fGenMCSignals.push_back(sig); + } + } + } + + string cfgTrackSelection_TrackCuts; + getTaskOptionValue(context, "analysis-track-selection", "cfgTrackCuts", cfgTrackSelection_TrackCuts, false); + TObjArray* cfgTrackSelection_objArrayTrackCuts = nullptr; + if (!cfgTrackSelection_TrackCuts.empty()) { + cfgTrackSelection_objArrayTrackCuts = TString(cfgTrackSelection_TrackCuts).Tokenize(","); + } + getTaskOptionValue(context, "analysis-track-selection", "cfgBarrelTrackCutsJSON", cfgTrackSelection_TrackCuts, false); + if (!cfgTrackSelection_TrackCuts.empty()) { + if (cfgTrackSelection_objArrayTrackCuts == nullptr) { + cfgTrackSelection_objArrayTrackCuts = new TObjArray(); + } + std::vector addTrackCuts = dqcuts::GetCutsFromJSON(cfgTrackSelection_TrackCuts.data()); + for (const auto& t : addTrackCuts) { + auto tempObjStr = new TObjString(t->GetName()); + cfgTrackSelection_objArrayTrackCuts->Add(tempObjStr); + } + } + for (Int_t icut = 0; icut < cfgTrackSelection_objArrayTrackCuts->GetEntries(); ++icut) { + TString cutName = cfgTrackSelection_objArrayTrackCuts->At(icut)->GetName(); + fTrackCutNames.push_back(cutName); + } + + fTrackCutName1 = fConfigTrackCut1.value; + fTrackCutName2 = fConfigTrackCut2.value; + if (fTrackCutName1 == fTrackCutName2) { + fIsSameTrackCut = true; + } + if (!cfgTrackSelection_objArrayTrackCuts->FindObject(fTrackCutName1.Data())) { + LOGF(fatal, "Track cut name %s in cfgTrackCut1 is not found in analysis-track-selection:cfgTrackCuts", fTrackCutName1.Data()); + } + if (!cfgTrackSelection_objArrayTrackCuts->FindObject(fTrackCutName2.Data())) { + LOGF(fatal, "Track cut name %s in cfgTrackCut2 is not found in analysis-track-selection:cfgTrackCuts", fTrackCutName2.Data()); + } + LOGP(info, "track cut name 1: {}", fTrackCutName1.Data()); + for (Int_t icut = 0; icut < cfgTrackSelection_objArrayTrackCuts->GetEntries(); ++icut) { + LOGP(info, "Available track cut: {}", cfgTrackSelection_objArrayTrackCuts->At(icut)->GetName()); + TString fTrackSelectionCutName = cfgTrackSelection_objArrayTrackCuts->At(icut)->GetName(); + if (fTrackSelectionCutName.CompareTo(fTrackCutName1) == 0) { + LOGP(info, "Found track cut: {}", fTrackCutName1.Data()); + fTrackCutBitMap1 |= (static_cast(1) << icut); + } + if (fTrackSelectionCutName.CompareTo(fTrackCutName2) == 0) { + LOGP(info, "Found track cut: {}", fTrackCutName2.Data()); + fTrackCutBitMap2 |= (static_cast(1) << icut); + } + } + LOGP(info, "Track cut bitmap 1: {}", fTrackCutBitMap1); + + TString configDileptonCutNamesStr = fConfigDileptonCut.value; + fDileptonCut = *dqcuts::GetCompositeCut(configDileptonCutNamesStr.Data()); + TString configQuadruletCutNamesStr = fConfigQuadrupletCuts.value; + std::unique_ptr objArray(configQuadruletCutNamesStr.Tokenize(",")); + for (Int_t icut = 0; icut < objArray->GetEntries(); ++icut) { + TString cutName = objArray->At(icut)->GetName(); + fQuadrupletCutNames.push_back(cutName); + fQuadrupletCuts.push_back(*dqcuts::GetCompositeCut(cutName.Data())); + } + + if (isPsi2S || isX3872) { + DefineHistograms(fHistMan, Form("Pairs_%s", configDileptonCutNamesStr.Data()), fConfigAddDileptonHistogram.value.data()); + if (!configQuadruletCutNamesStr.IsNull()) { + for (std::size_t icut = 0; icut < fQuadrupletCutNames.size(); ++icut) { + if (fIsSameTrackCut) { + DefineHistograms(fHistMan, Form("QuadrupletSEPM_%s", fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); + } else { + DefineHistograms(fHistMan, Form("QuadrupletSEPM_%s", fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); + DefineHistograms(fHistMan, Form("QuadrupletSEMP_%s", fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); + } + DefineHistograms(fHistMan, Form("QuadrupletSEPP_%s", fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); + DefineHistograms(fHistMan, Form("QuadrupletSEMM_%s", fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); + for (auto& sig : fRecMCSignals) { + DefineHistograms(fHistMan, Form("QuadrupletMCMatched_%s_%s", fQuadrupletCutNames[icut].Data(), sig->GetName()), fConfigAddQuadrupletHistogram.value.data()); + // DefineHistograms(fHistMan, Form("QuadrupletMCMatchedAmbiguous_%s_%s", fQuadrupletCutNames[icut].Data(), sig->GetName()), fConfigAddQuadrupletHistogram.value.data()); + } + } + } + } + + if (isMCGen) { + for (auto& sig : fGenMCSignals) { + DefineHistograms(fHistMan, Form("MCTruthGenQuad_%s", sig->GetName()), ""); + DefineHistograms(fHistMan, Form("MCTruthGenQuadAccepted_%s", sig->GetName()), ""); + } + // DefineHistograms(fHistMan, "MCTruthGenQuadAccepted", ""); + } + + VarManager::SetUseVars(fHistMan->GetUsedVars()); + fOutputList.setObject(fHistMan->GetMainHistogramList()); + } + + // init parameters from CCDB + void initParamsFromCCDB(uint64_t timestamp) + { + if (fConfigCCDBOptions.fConfigUseRemoteField.value) { + o2::parameters::GRPMagField* grpmag = fCCDB->getForTimeStamp(fConfigCCDBOptions.fConfigGRPmagPath.value, timestamp); + float magField = 0.0; + if (grpmag != nullptr) { + magField = grpmag->getNominalL3Field(); + } else { + LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", timestamp); + } + if (fConfigUseKFVertexing.value) { + VarManager::SetupTwoProngKFParticle(magField); + VarManager::SetupFourProngKFParticle(magField); + } else if (fConfigSetupFourProngFitter.value) { + VarManager::SetupTwoProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables + VarManager::SetupFourProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables + } + } else { + if (fConfigUseKFVertexing.value) { + VarManager::SetupTwoProngKFParticle(fConfigCCDBOptions.fConfigMagField.value); + VarManager::SetupFourProngKFParticle(fConfigCCDBOptions.fConfigMagField.value); + } else if (fConfigSetupFourProngFitter.value) { + LOGP(info, "Setting up DCA fitter for two and four prong candidates"); + VarManager::SetupTwoProngDCAFitter(fConfigCCDBOptions.fConfigMagField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables + VarManager::SetupFourProngDCAFitter(fConfigCCDBOptions.fConfigMagField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables + } + } + } + + // Template function to run pair - hadron combinations + template + void runDileptonTrackTrack(TEvent const& event, TBCs const& /*bcs*/, soa::Join const& assocs, TTracks const& tracks, TDileptons const& dileptons, McCollisions const& /*mcEvents*/, McParticles const& /*mcTracks*/) + { + VarManager::ResetValues(0, VarManager::kNVars, fValuesQuadruplet); + VarManager::FillEvent(event, fValuesQuadruplet); + // VarManager::FillEvent(event.reducedMCevent(), fValuesQuadruplet); + if (event.has_mcCollision()) { + VarManager::FillEvent(event.mcCollision(), fValuesQuadruplet); + } + + uint32_t mcDecision = static_cast(0); + // size_t isig = 0; + + for (auto dilepton : dileptons) { + // get full track info of tracks based on the index + + int indexLepton1 = dilepton.index0Id(); + int indexLepton2 = dilepton.index1Id(); + auto lepton1 = tracks.rawIteratorAt(dilepton.index0Id()); + auto lepton2 = tracks.rawIteratorAt(dilepton.index1Id()); + auto lepton1MC = lepton1.mcParticle(); + auto lepton2MC = lepton2.mcParticle(); + // Check that the dilepton has zero charge + if (dilepton.sign() != 0) { + continue; + } + VarManager::FillTrack(dilepton, fValuesQuadruplet); + + // bool isAmbiguousLepton = (dilepton.filterMap_raw() & (static_cast(1) << 28)) || + // (dilepton.filterMap_raw() & (static_cast(1) << 29)) || + // (dilepton.filterMap_raw() & (static_cast(1) << 30)) || + // (dilepton.filterMap_raw() & (static_cast(1) << 31)); + // // if (isAmbi && isAmbiguousLepton) + // // continue; // skip ambiguous dileptons + // if constexpr ((TTrackFillMap & VarManager::ObjTypes::AmbiTrack) > 0) { + // if (isAmbiguousLepton) { + // // LOGP(info, "Dilepton {} is ambiguous, skipping", dilepton.index0Id()); + // continue; // skip ambiguous dileptons + // } + // } + + // apply the dilepton cut + if (!fDileptonCut.IsSelected(fValuesQuadruplet)) + continue; + + fHistMan->FillHistClass(Form("Pairs_%s", fDileptonCut.GetName()), fValuesQuadruplet); + + // loop over hadrons pairs + for (auto& [a1, a2] : o2::soa::combinations(assocs, assocs)) { + uint32_t trackSelection = 0; + trackSelection = ((a1.isBarrelSelected_raw() & fTrackCutBitMap1) && (a2.isBarrelSelected_raw() & fTrackCutBitMap2)); + // LOGP(info, "trackSelection: {}, a1: {}, a2: {}", trackSelection, a1.isBarrelSelected_raw(), a2.isBarrelSelected_raw()); + if (!trackSelection) { + continue; + } + + // get the track from this association + auto track1 = tracks.rawIteratorAt(a1.trackId()); + auto track2 = tracks.rawIteratorAt(a2.trackId()); + if (track1.sign() < 0 && track2.sign() > 0) { + std::swap(track1, track2); + } + // avoid self combinations + if (track1.globalIndex() == indexLepton1 || track1.globalIndex() == indexLepton2 || track2.globalIndex() == indexLepton1 || track2.globalIndex() == indexLepton2) { + continue; + } + + // fill variables + VarManager::FillDileptonTrackTrack(dilepton, track1, track2, fValuesQuadruplet); + // fValuesQuadruplet[VarManager::kWeight] = 1.0f; + if (fConfigSetupFourProngFitter || fConfigUseKFVertexing) { + // LOGP(info, "Using KF or DCA fitter for secondary vertexing"); + VarManager::FillDileptonTrackTrackVertexing(event, lepton1, lepton2, track1, track2, fValuesQuadruplet); + } + + // if (fConfigML.applyBDT) { + // bool isSelectedBDT = false; + // auto pT = fValuesQuadruplet[VarManager::kQuadPt]; + // if (pT < fConfigML.ptBins.value.front() || pT > fConfigML.ptBins.value.back()) { + // continue; // skip quadruplets outside of the pt range of the BDT models + // } + // // TODO: move some variables into varManager and reduce the number of input parameters + // std::vector inputFeatures = fQuadMlResponse.getInputFeatures(lepton1, lepton2, fValuesQuadruplet); + // isSelectedBDT = fQuadMlResponse.isSelectedMl(inputFeatures, fValuesQuadruplet[VarManager::kQuadPt], fOutputMlQuad); + // if (!isSelectedBDT) + // continue; + // VarManager::FillBdtScore(fOutputMlQuad, fValuesQuadruplet); + // } + + auto track1MC = track1.mcParticle(); + auto track2MC = track2.mcParticle(); + mcDecision = 0; + // size_t isig = 0; + for (size_t iSignal = 0; iSignal < fRecMCSignals.size(); ++iSignal) { + if (fRecMCSignals[iSignal]->CheckSignal(true, lepton1MC, lepton2MC, track1MC, track2MC)) { + mcDecision |= (static_cast(1) << iSignal); + } + } + int iCut = 0; + uint32_t CutDecision = 0; + for (auto cutname = fQuadrupletCutNames.begin(); cutname != fQuadrupletCutNames.end(); cutname++, iCut++) { + // apply dilepton-track-track cut + if (fQuadrupletCuts[iCut].IsSelected(fValuesQuadruplet)) { + CutDecision |= (1 << iCut); + if (fIsSameTrackCut) { + if (track1.sign() * track2.sign() < 0) { + fHistMan->FillHistClass(Form("QuadrupletSEPM_%s", fQuadrupletCutNames[iCut].Data()), fValuesQuadruplet); + for (uint32_t isig = 0; isig < fRecMCSignals.size(); isig++) { + if (mcDecision & (static_cast(1) << isig)) { + fHistMan->FillHistClass(Form("QuadrupletMCMatched_%s_%s", fQuadrupletCutNames[iCut].Data(), fRecMCSignals[isig]->GetName()), fValuesQuadruplet); + // if (isAmbiguousLepton || (track1.barrelAmbiguityInBunch() > 1) || (track2.barrelAmbiguityInBunch() > 1) || + // (track1.barrelAmbiguityOutOfBunch() > 1) || (track2.barrelAmbiguityOutOfBunch() > 1)) { + // fHistMan->FillHistClass(Form("QuadrupletMCMatchedAmbiguous_%s_%s", fQuadrupletCutNames[iCut].Data(), fRecMCSignals[isig]->GetName()), fValuesQuadruplet); + // } + } + } + } + } else { + if ((track1.sign() < 0) && (track2.sign() > 0)) { + fHistMan->FillHistClass(Form("QuadrupletSEMP_%s", fQuadrupletCutNames[iCut].Data()), fValuesQuadruplet); + } else if ((track1.sign() > 0) && (track2.sign() < 0)) { + fHistMan->FillHistClass(Form("QuadrupletSEPM_%s", fQuadrupletCutNames[iCut].Data()), fValuesQuadruplet); + } + } + if ((track1.sign() > 0) && (track2.sign() > 0)) { + fHistMan->FillHistClass(Form("QuadrupletSEPP_%s", fQuadrupletCutNames[iCut].Data()), fValuesQuadruplet); + } else if ((track1.sign() < 0) && (track2.sign() < 0)) { + fHistMan->FillHistClass(Form("QuadrupletSEMM_%s", fQuadrupletCutNames[iCut].Data()), fValuesQuadruplet); + } + } + } // end loop over quadruplet cuts + + // fill table + if (!CutDecision) + continue; + if (!mcDecision) + continue; + // if (fConfigML.applyBDT) + // continue; // skip filling table when BDT is applied for now + DileptonTrackTrackTable(fValuesQuadruplet[VarManager::kQuadDefaultDileptonMass], fValuesQuadruplet[VarManager::kQuadPt], fValuesQuadruplet[VarManager::kQuadEta], fValuesQuadruplet[VarManager::kQuadPhi], fValuesQuadruplet[VarManager::kRap], + fValuesQuadruplet[VarManager::kQ], fValuesQuadruplet[VarManager::kDeltaR1], fValuesQuadruplet[VarManager::kDeltaR2], fValuesQuadruplet[VarManager::kDeltaR], + dilepton.mass(), dilepton.pt(), dilepton.eta(), dilepton.phi(), dilepton.sign(), + lepton1.tpcNSigmaEl(), lepton1.tpcNSigmaPi(), lepton1.tpcNSigmaPr(), lepton1.tpcNClsFound(), + lepton2.tpcNSigmaEl(), lepton2.tpcNSigmaPi(), lepton2.tpcNSigmaPr(), lepton2.tpcNClsFound(), + fValuesQuadruplet[VarManager::kDitrackMass], fValuesQuadruplet[VarManager::kDitrackPt], track1.pt(), track2.pt(), track1.eta(), track2.eta(), track1.phi(), track2.phi(), track1.sign(), track2.sign(), track1.tpcNSigmaPi(), track2.tpcNSigmaPi(), track1.tpcNSigmaKa(), track2.tpcNSigmaKa(), track1.tpcNSigmaPr(), track1.tpcNSigmaPr(), track1.tpcNClsFound(), track2.tpcNClsFound(), + fValuesQuadruplet[VarManager::kKFMass], fValuesQuadruplet[VarManager::kVertexingProcCode], fValuesQuadruplet[VarManager::kVertexingChi2PCA], fValuesQuadruplet[VarManager::kCosPointingAngle], fValuesQuadruplet[VarManager::kKFDCAxyzBetweenProngs], fValuesQuadruplet[VarManager::kKFChi2OverNDFGeo], + fValuesQuadruplet[VarManager::kVertexingLz], fValuesQuadruplet[VarManager::kVertexingLxy], fValuesQuadruplet[VarManager::kVertexingLxyz], fValuesQuadruplet[VarManager::kVertexingTauz], fValuesQuadruplet[VarManager::kVertexingTauxy], fValuesQuadruplet[VarManager::kVertexingLzErr], fValuesQuadruplet[VarManager::kVertexingLxyzErr], + fValuesQuadruplet[VarManager::kVertexingTauzErr], fValuesQuadruplet[VarManager::kVertexingLzProjected], fValuesQuadruplet[VarManager::kVertexingLxyProjected], fValuesQuadruplet[VarManager::kVertexingLxyzProjected], fValuesQuadruplet[VarManager::kVertexingTauzProjected], fValuesQuadruplet[VarManager::kVertexingTauxyProjected]); + } // end loop over associations + } // end loop over dileptons + } + + Preslice> trackAssocsPerCollision = aod::track_association::collisionId; + Preslice dielectronsPerCollision = aod::reducedpair::reducedeventId; + // Preslice ditracksPerCollision = aod::reducedpair::reducedeventId; + + void processX3872(soa::Filtered const& events, BCsWithTimestamps const& bcs, + soa::Join const& assocs, + MyBarrelTracksWithCov const& tracks, soa::Filtered const& dileptons, + McCollisions const& mcEvents, McParticles const& mcTracks) + { + // set up KF or DCAfitter + if (events.size() == 0) { + return; + } + if (fCurrentRun != bcs.begin().runNumber()) { // start: runNumber + initParamsFromCCDB(bcs.begin().timestamp()); + fCurrentRun = bcs.begin().runNumber(); + } // end: runNumber + for (const auto& event : events) { + if (!event.isEventSelected_bit(0)) { + continue; + } + auto groupedBarrelAssocs = assocs.sliceBy(trackAssocsPerCollision, event.globalIndex()); + auto groupedDielectrons = dileptons.sliceBy(dielectronsPerCollision, event.globalIndex()); + runDileptonTrackTrack(event, bcs, groupedBarrelAssocs, tracks, groupedDielectrons, mcEvents, mcTracks); + } + } + + void processPsi2S(soa::Filtered const& events, BCsWithTimestamps const& bcs, + soa::Join const& assocs, + MyBarrelTracksWithCov const& tracks, soa::Filtered const& dileptons, + McCollisions const& mcEvents, McParticles const& mcTracks) + { + // set up KF or DCAfitter + if (events.size() == 0) { + return; + } + if (fCurrentRun != bcs.begin().runNumber()) { // start: runNumber + initParamsFromCCDB(bcs.begin().timestamp()); + fCurrentRun = bcs.begin().runNumber(); + } // end: runNumber + for (auto& event : events) { + if (!event.isEventSelected_bit(0)) { + continue; + } + auto groupedBarrelAssocs = assocs.sliceBy(trackAssocsPerCollision, event.globalIndex()); + auto groupedDielectrons = dileptons.sliceBy(dielectronsPerCollision, event.globalIndex()); + runDileptonTrackTrack(event, bcs, groupedBarrelAssocs, tracks, groupedDielectrons, mcEvents, mcTracks); + } + } + + PresliceUnsorted perReducedMcEvent = aod::mcparticle::mcCollisionId; + + void processMCGen(McParticles const& mcTracks) + { + // loop over mc stack and fill histograms for pure MC truth signals + // group all the MC tracks which belong to the MC event corresponding to the current reconstructed event + // auto groupedMCTracks = tracksMC.sliceBy(aod::reducedtrackMC::reducedMCeventId, event.reducedMCevent().globalIndex()); + for (auto& mctrack : mcTracks) { + VarManager::FillTrackMC(mcTracks, mctrack); + // NOTE: Signals are checked here mostly based on the skimmed MC stack, so depending on the requested signal, the stack could be incomplete. + // NOTE: However, the working model is that the decisions on MC signals are precomputed during skimming and are stored in the mcReducedFlags member. + // TODO: Use the mcReducedFlags to select signals + for (auto& sig : fGenMCSignals) { + if (sig->CheckSignal(true, mctrack)) { + int daughterIdFirst = mctrack.daughtersIds()[0]; + int daughterIdEnd = mctrack.daughtersIds()[1]; + int Ndaughters = daughterIdEnd - daughterIdFirst + 1; + if (Ndaughters == 3) { + auto dilepton = mcTracks.rawIteratorAt(daughterIdFirst); + auto track1 = mcTracks.rawIteratorAt(daughterIdFirst + 1); + auto track2 = mcTracks.rawIteratorAt(daughterIdFirst + 2); + // LOGP(info, "PDG of dilepton: {}, track1: {}, track2: {}", dilepton.pdgCode(), track1.pdgCode(), track2.pdgCode()); + VarManager::FillQuadMC(dilepton, track1, track2); + int daughterIdFirst2 = dilepton.daughtersIds()[0]; + int daughterIdEnd2 = dilepton.daughtersIds()[1]; + int Ndaughters2 = daughterIdEnd2 - daughterIdFirst2 + 1; + if (Ndaughters2 == 2) { + auto lepton1 = mcTracks.rawIteratorAt(daughterIdFirst2); + auto lepton2 = mcTracks.rawIteratorAt(daughterIdFirst2 + 1); + VarManager::FillPairMC(lepton1, lepton2); + // LOGP(info, "PDG of lepton1: {}, lepton2: {}", lepton1.pdgCode(), lepton2.pdgCode()); + if (lepton1.pt() > fConfigDileptonPtMin && lepton2.pt() > fConfigDileptonPtMin) { + if (std::abs(lepton1.eta()) < fConfigDileptonEtaAbs && std::abs(lepton2.eta()) < fConfigDileptonEtaAbs) { + if (track1.pt() > fConfigTrackPtMin && track2.pt() > fConfigTrackPtMin) { + if (std::abs(track1.eta()) < fConfigTrackEtaAbs && std::abs(track2.eta()) < fConfigTrackEtaAbs) { + fHistMan->FillHistClass(Form("MCTruthGenQuadAccepted_%s", sig->GetName()), VarManager::fgValues); + } + } + } + } + } + } + fHistMan->FillHistClass(Form("MCTruthGen_%s", sig->GetName()), VarManager::fgValues); + } + } + } + } + + void processMCGenWithEventSelection(soa::Filtered const& events, + McCollisions const& /*mcEvents*/, McParticles const& mcTracks) + { + for (auto& event : events) { + if (!event.isEventSelected_bit(0)) { + continue; + } + if (!event.has_mcCollision()) { + continue; + } + + auto groupedMCTracks = mcTracks.sliceBy(perReducedMcEvent, event.mcCollisionId()); + groupedMCTracks.bindInternalIndicesTo(&mcTracks); + for (auto& track : groupedMCTracks) { + + VarManager::FillTrackMC(mcTracks, track); + + auto track_raw = mcTracks.rawIteratorAt(track.globalIndex()); + for (auto& sig : fGenMCSignals) { + if (sig->CheckSignal(true, track_raw)) { + int daughterIdFirst = track_raw.daughtersIds()[0]; + int daughterIdEnd = track_raw.daughtersIds()[1]; + int Ndaughters = daughterIdEnd - daughterIdFirst + 1; + if (Ndaughters == 3) { + auto dilepton = mcTracks.rawIteratorAt(daughterIdFirst); + auto track1 = mcTracks.rawIteratorAt(daughterIdFirst + 1); + auto track2 = mcTracks.rawIteratorAt(daughterIdFirst + 2); + VarManager::FillQuadMC(dilepton, track1, track2); + } + fHistMan->FillHistClass(Form("MCTruthGenQuad_%s", sig->GetName()), VarManager::fgValues); + } + } + } + } // end loop over events + } + + void processDummy(MyEvents&) + { + // do nothing + } + + PROCESS_SWITCH(AnalysisDileptonTrackTrack, processPsi2S, "Run psi(2S) -> e+ e- + pi+ pi- pairing, using skimmed data", false); + PROCESS_SWITCH(AnalysisDileptonTrackTrack, processX3872, "Run X(3872) -> J/psi + pi+ pi- pairing, using skimmed data", false); + PROCESS_SWITCH(AnalysisDileptonTrackTrack, processMCGen, "Loop over MC particle stack and fill generator level histograms", false); + PROCESS_SWITCH(AnalysisDileptonTrackTrack, processMCGenWithEventSelection, "Loop over MC particle stack and fill generator level histograms with event selection", false); + PROCESS_SWITCH(AnalysisDileptonTrackTrack, processDummy, "Dummy function", true); +}; + WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { // Initialize metadata for TOF response @@ -3198,7 +3769,8 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) adaptAnalysisTask(cfgc), adaptAnalysisTask(cfgc), adaptAnalysisTask(cfgc), - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc)}; } void DefineHistograms(HistogramManager* histMan, const TString& histClasses, const char* histGroups) @@ -3278,6 +3850,12 @@ void DefineHistograms(HistogramManager* histMan, const TString& histClasses, con if (classStr.Contains("MCTruthGenPoldielectronPbPbPairCS")) { dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "polarization-dielectron-pbpb-midy-cs-gen", histName); } + if (classStr.Contains("MCTruthGenPoldielectronPbPbPairGJB1")) { + dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "polarization-dielectron-pbpb-midy-gjb1-gen", histName); + } + if (classStr.Contains("MCTruthGenPoldielectronPbPbPairGJB2")) { + dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "polarization-dielectron-pbpb-midy-gjb2-gen", histName); + } if (classStr.Contains("MCTruthGenSelBR")) { dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "mctruth_triple"); } else if (classStr.Contains("MCTruthGen")) { @@ -3288,6 +3866,10 @@ void DefineHistograms(HistogramManager* histMan, const TString& histClasses, con // dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "mctruth_track"); // } + if (classStr.Contains("MCTruthGenQuad")) { + dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "mctruth_quad", histName); + } + if (classStr.Contains("DileptonsSelected")) { dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "pair", histName); } @@ -3311,5 +3893,9 @@ void DefineHistograms(HistogramManager* histMan, const TString& histClasses, con if (classStr.Contains("DileptonHadronCorrelation")) { dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "dilepton-hadron-correlation"); } + + if (classStr.Contains("Quadruplet")) { + dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "dilepton-dihadron", histName); + } } // end loop over histogram classes } diff --git a/PWGDQ/Tasks/dqEnergyCorrelator_direct.cxx b/PWGDQ/Tasks/dqEnergyCorrelator_direct.cxx index 3fc4da628f5..7bcc2b70076 100644 --- a/PWGDQ/Tasks/dqEnergyCorrelator_direct.cxx +++ b/PWGDQ/Tasks/dqEnergyCorrelator_direct.cxx @@ -116,6 +116,8 @@ struct AnalysisEnergyCorrelator { Configurable fConfigJpsiPtMin{"cfgJpsiPtMin", 0.0, "J/psi pt minimum"}; Configurable fConfigJpsiPtMax{"cfgJpsiPtMax", 100.0, "J/psi pt maximum"}; Configurable fConfigJpsiRapMax{"cfgJpsiRapMax", 0.9, "J/psi rapidity maximum"}; + Configurable fConfigJpsiTauxyMin{"cfgJpsiTauxyMin", -10000, "J/psi tauxy minimum"}; + Configurable fConfigJpsiTauxyMax{"cfgJpsiTauxyMax", 10000.0, "J/psi tauxy maximum"}; Configurable fConfigAddSEPHistogram{"cfgAddSEPHistogram", "", "Comma separated list of histograms"}; Configurable recSignals{"cfgMCRecSignals", "eeFromJpsi", "Comma separated list of MC signals (reconstructed)"}; Configurable recSignalsJSON{"cfgMCRecSignalsJSON", "", "Comma separated list of MC signals (reconstructed) via JSON"}; @@ -195,7 +197,7 @@ struct AnalysisEnergyCorrelator { std::vector multBins = fConfigEventOptions.fConfigMultBins.value; multBins.insert(multBins.begin(), VARIABLE_WIDTH); - fMixingBinning = std::make_unique(std::array, 2>{zBins, multBins}, true); + fMixingBinning = std::make_unique(std::array, 2>{zBins, multBins}); bool isBarrelME = context.mOptions.get("processBarrelMixedEvent"); bool isMCGen_energycorrelators = context.mOptions.get("processMCGenEnergyCorrelators") || context.mOptions.get("processMCGenEnergyCorrelatorsPion"); @@ -238,7 +240,7 @@ struct AnalysisEnergyCorrelator { if (trackCutsJSON != "") { std::vector addTrackCuts = dqcuts::GetCutsFromJSON(trackCutsJSON.Data()); for (auto& t : addTrackCuts) { - fTrackCuts.push_back(reinterpret_cast(t)); + fTrackCuts.push_back(dynamic_cast(t)); fTrackCutNames.push_back(t->GetName()); trackCutStr += Form(",%s", t->GetName()); } @@ -311,7 +313,7 @@ struct AnalysisEnergyCorrelator { if (hadronCutsJSON != "") { std::vector addHadronCuts = dqcuts::GetCutsFromJSON(hadronCutsJSON.Data()); for (auto& t : addHadronCuts) { - fHadronCuts.push_back(reinterpret_cast(t)); + fHadronCuts.push_back(dynamic_cast(t)); fHadronCutNames.push_back(t->GetName()); hadronCutStr += Form(",%s", t->GetName()); } @@ -501,12 +503,12 @@ struct AnalysisEnergyCorrelator { if (!listAccs) { LOG(fatal) << "Problem getting TList object with efficiencies!"; } - hEfficiency_dilepton = static_cast(listAccs->FindObject("hEfficiency_dilepton")); - hEfficiency_hadron = static_cast(listAccs->FindObject("hEfficiency_hadron")); - hAcceptance_rec = static_cast(listAccs->FindObject("hAcceptance_rec")); - hAcceptance_gen = static_cast(listAccs->FindObject("hAcceptance_gen")); - hMasswindow = static_cast(listAccs->FindObject("hMasswindow")); - hReweighthadron = static_cast(listAccs->FindObject("hReweighthadron")); + hEfficiency_dilepton = dynamic_cast(listAccs->FindObject("hEfficiency_dilepton")); + hEfficiency_hadron = dynamic_cast(listAccs->FindObject("hEfficiency_hadron")); + hAcceptance_rec = dynamic_cast(listAccs->FindObject("hAcceptance_rec")); + hAcceptance_gen = dynamic_cast(listAccs->FindObject("hAcceptance_gen")); + hMasswindow = dynamic_cast(listAccs->FindObject("hMasswindow")); + hReweighthadron = dynamic_cast(listAccs->FindObject("hReweighthadron")); if (!hAcceptance_rec || !hAcceptance_gen || !hEfficiency_dilepton || !hEfficiency_hadron || !hMasswindow || !hReweighthadron) { LOG(fatal) << "Problem getting histograms from the TList object with efficiencies!"; } @@ -627,8 +629,8 @@ struct AnalysisEnergyCorrelator { } // Template function to run same event pairing (barrel-barrel, muon-muon, barrel-muon) - template - void runSameEventPairing(TTrack1 const& t1, TTrack2 const& t2, int iEleCut, aod::McParticles const& /*mcParticles*/) + template + void runSameEventPairing(TTrack1 const& t1, TTrack2 const& t2, int iEleCut, TEvent const& event, aod::McParticles const& /*mcParticles*/) { std::map> histNames = fTrackHistNames; std::map> histNamesMC = fBarrelHistNamesMCmatched; @@ -647,6 +649,7 @@ struct AnalysisEnergyCorrelator { } // end loop over MC signals VarManager::FillPair(t1, t2); + VarManager::FillPairVertexing(event, t1, t2, VarManager::fgValues); if (sign1 * sign2 < 0) { // +- pairs fHistMan->FillHistClass(histNames[iEleCut][0].Data(), VarManager::fgValues); // reconstructed, unmatched @@ -802,18 +805,20 @@ struct AnalysisEnergyCorrelator { // Fill pair histograms for all cuts that both tracks pass for (size_t iCut = 0; iCut < fTrackCuts.size(); iCut++) { if (twoTrackFilter & (static_cast(1) << iCut)) { - runSameEventPairing(t1, t2, iCut, mcParticles); + runSameEventPairing(t1, t2, iCut, event, mcParticles); } } float mass = VarManager::fgValues[VarManager::kMass]; float pt = VarManager::fgValues[VarManager::kPt]; float rap = VarManager::fgValues[VarManager::kRap]; + float tauxy = VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMass]; // Apply J/psi cuts if (mass < fConfigPairOptions.fConfigJpsiMassMin.value || mass > fConfigPairOptions.fConfigJpsiMassMax.value || pt < fConfigPairOptions.fConfigJpsiPtMin.value || pt > fConfigPairOptions.fConfigJpsiPtMax.value || - std::abs(rap) > fConfigPairOptions.fConfigJpsiRapMax.value) { + std::abs(rap) > fConfigPairOptions.fConfigJpsiRapMax.value || + tauxy < fConfigPairOptions.fConfigJpsiTauxyMin.value || tauxy > fConfigPairOptions.fConfigJpsiTauxyMax.value) { continue; } @@ -928,13 +933,17 @@ struct AnalysisEnergyCorrelator { } // Fill pair variables for cut VarManager::FillPair(t1, t2, VarManager::fgValues); + VarManager::FillPairVertexing(event1, t1, t2, VarManager::fgValues); float mass = VarManager::fgValues[VarManager::kMass]; float pt = VarManager::fgValues[VarManager::kPt]; float rap = VarManager::fgValues[VarManager::kRap]; + float tauxy = VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMass]; + // Apply J/psi cuts if (mass < fConfigPairOptions.fConfigJpsiMassMin.value || mass > fConfigPairOptions.fConfigJpsiMassMax.value || pt < fConfigPairOptions.fConfigJpsiPtMin.value || pt > fConfigPairOptions.fConfigJpsiPtMax.value || - std::abs(rap) > fConfigPairOptions.fConfigJpsiRapMax.value) { + std::abs(rap) > fConfigPairOptions.fConfigJpsiRapMax.value || + tauxy < fConfigPairOptions.fConfigJpsiTauxyMin.value || tauxy > fConfigPairOptions.fConfigJpsiTauxyMax.value) { continue; } if (t1.sign() * t2.sign() >= 0) { diff --git a/PWGDQ/Tasks/dqFlow.cxx b/PWGDQ/Tasks/dqFlow.cxx index 323ec4ade4a..31865fcd0d4 100644 --- a/PWGDQ/Tasks/dqFlow.cxx +++ b/PWGDQ/Tasks/dqFlow.cxx @@ -142,7 +142,7 @@ struct DQEventQvector { ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100.1}, "multiplicity / centrality axis for histograms"}; // Define the filter for barrel tracks and forward tracks - Filter trackFilter = (requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true); + Filter trackFilter = (requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == static_cast(true)); Filter fwdFilter = (aod::fwdtrack::eta < -2.45f) && (aod::fwdtrack::eta > -3.6f); // Histograms used for optionnal efficiency and non-uniform acceptance corrections diff --git a/PWGDQ/Tasks/global-muon-matcher.cxx b/PWGDQ/Tasks/global-muon-matcher.cxx index 98863864b8e..4125dabbd9c 100644 --- a/PWGDQ/Tasks/global-muon-matcher.cxx +++ b/PWGDQ/Tasks/global-muon-matcher.cxx @@ -227,7 +227,7 @@ struct GlobalMuonMatching { struct : ConfigurableGroup { Configurable cfgMixingDepth{"cfgMixingDepth", -1, "Maximum number of mixed candidate groups per MCH track (-1: no limit)"}; Configurable cfgMinDeltaBc{"cfgMinDeltaBc", 3564, "Minimum DeltaBc between mixed collisions"}; - Configurable cfgMaxDeltaPhi{"cfgMaxDeltaPhi", static_cast(o2::constants::math::PI / 10), "Maximum DelptaPhi between mixed MCH tracks (rad)"}; + Configurable cfgMaxDeltaPhi{"cfgMaxDeltaPhi", o2::constants::math::PI / 10.f, "Maximum DelptaPhi between mixed MCH tracks (rad)"}; Configurable cfgMaxDeltaR{"cfgMaxDeltaR", 10.f, "Maximum DeltaR between mixed MCH tracks"}; Configurable cfgMaxDeltaAttempts{"cfgMaxDeltaAttempts", 0.1f, "Maximum relative difference in match attempts"}; Configurable cfgMaxDeltaZ{"cfgMaxDeltaZ", 1.f, "Maximum deltaZ between mixed collisions"}; diff --git a/PWGDQ/Tasks/mftMchMatcher.cxx b/PWGDQ/Tasks/mftMchMatcher.cxx index 6149433f586..69b0c669945 100644 --- a/PWGDQ/Tasks/mftMchMatcher.cxx +++ b/PWGDQ/Tasks/mftMchMatcher.cxx @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,7 @@ #include #include #include +#include #include #include @@ -279,6 +281,8 @@ struct mftMchMatcher { // o2-linter: disable=name/struct (keep temporarily) o2::parameters::GRPMagField* fGrpMag = nullptr; + double mBzAtMftCenter{0}; + std::uniform_real_distribution mDistribution{0.0, 1.0}; std::mt19937 mGenerator; @@ -302,6 +306,7 @@ struct mftMchMatcher { // o2-linter: disable=name/struct (keep temporarily) o2::track::TrackParCovFwd trackparCov{track.z(), tpars, tcovs, chi2}; o2::dataformats::GlobalFwdTrack fwdtrack; fwdtrack.setParameters(trackparCov.getParameters()); + fwdtrack.setZ(trackparCov.getZ()); fwdtrack.setCovariances(trackparCov.getCovariances()); return fwdtrack; } @@ -422,6 +427,12 @@ struct mftMchMatcher { // o2-linter: disable=name/struct (keep temporarily) o2::base::Propagator::initFieldFromGRP(fGrpMag); VarManager::SetMagneticField(fGrpMag->getNominalL3Field()); VarManager::SetupMuonMagField(); + + auto* fieldB = dynamic_cast(TGeoGlobalMagField::Instance()->GetField()); + if (fieldB) { + const std::array centerMft{0, 0, -61.4}; // Field at center of MFT + mBzAtMftCenter = fieldB->getBz(centerMft.data()); + } } mRunNumber = bc.runNumber(); } @@ -647,6 +658,42 @@ struct mftMchMatcher { // o2-linter: disable=name/struct (keep temporarily) return attempts; } + void propagateToZ(o2::track::TrackParCovFwd& mft, o2::dataformats::GlobalFwdTrack& muon, const double z) + { + auto mchTrack = mExtrap.FwdtoMCH(muon); + + // MCH track extrapolation + // The multiple scattering effect is fully associated to the MCH track + // independently of the position of the matching plane + constexpr float absFront = -90.f; + constexpr float absBack = -505.f; + if (muon.getZ() < absBack) { + // the track origin is downstream of the absorber + if (z > absFront) { + // the destination is upstream of the absorber + // full extrapolation through the absorber in the upstream direction + o2::mch::TrackExtrap::extrapToVertexWithoutBranson(mchTrack, z); + } else { + // the destination is inside or downstream of the absorber + // 1. do a full extrapolation through the absorber to estimate the MS effect + o2::mch::TrackExtrap::extrapToVertexWithoutBranson(mchTrack, absFront + 0.1f); + // 2. extrapolate from there back to the destination z + o2::mch::TrackExtrap::extrapToZCov(mchTrack, z); + } + } else { + // all other cases + o2::mch::TrackExtrap::extrapToZCov(mchTrack, z); + } + + auto proptrack = mExtrap.MCHtoFwd(mchTrack); + muon.setParameters(proptrack.getParameters()); + muon.setZ(proptrack.getZ()); + muon.setCovariances(proptrack.getCovariances()); + + // MFT extrapolation without MS effects, as they are fully included in the MCH track extrapolation + mft.propagateToZhelix(z, mBzAtMftCenter); + } + template void fillTable(TCOLLS const& collisions, TBCS const& bcs, @@ -707,8 +754,7 @@ struct mftMchMatcher { // o2-linter: disable=name/struct (keep temporarily) o2::track::TrackParCovFwd mftprop = VarManager::FwdToTrackPar(mfttrack, mfttrackcov); o2::dataformats::GlobalFwdTrack muonprop = trackToGlobalFwd(muontrack, muontrack); if (cfgzMatching.value < 0.) { - mftprop = VarManager::PropagateFwd(mfttrack, mfttrackcov, cfgzMatching.value); - muonprop = VarManager::PropagateMuon(muontrack, collision, VarManager::kToMatching); + propagateToZ(mftprop, muonprop, cfgzMatching.value); } auto muonpropCov = muonprop.getCovariances(); auto mftpropCov = mftprop.getCovariances(); diff --git a/PWGDQ/Tasks/muonGlobalAlignment.cxx b/PWGDQ/Tasks/muonGlobalAlignment.cxx index c045c1c05ff..e8a85c3555c 100644 --- a/PWGDQ/Tasks/muonGlobalAlignment.cxx +++ b/PWGDQ/Tasks/muonGlobalAlignment.cxx @@ -19,6 +19,7 @@ #include "Common/CCDB/RCTSelectionFlags.h" #include "Common/Core/RecoDecay.h" #include "Common/Core/fwdtrackUtilities.h" +#include "Common/DataModel/CollisionAssociationTables.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/TrackSelectionTables.h" @@ -103,7 +104,7 @@ using namespace o2::aod::rctsel; using MyCollisions = aod::Collisions; using MyBCs = soa::Join; using MyEvents = soa::Join; -using MyMuonsWithCov = soa::Join; +using MyMuonsWithCov = soa::Join; using MyMFTs = aod::MFTTracks; using MyMFTCovariances = aod::MFTTracksCov; @@ -122,16 +123,21 @@ using o2::track::TrackParCovFwd; namespace muonaligncoll { DECLARE_SOA_COLUMN(RunNumber, runNumber, int); -DECLARE_SOA_COLUMN(Timestamp, timestamp, uint64_t); //! Timestamp of a BC in ms (epoch style) +DECLARE_SOA_COLUMN(GlobalBC, globalBC, uint64_t); //! Bunch crossing number (globally unique in this run) +DECLARE_SOA_COLUMN(Timestamp, timestamp, uint64_t); //! Timestamp of a BC in ms (epoch style) +DECLARE_SOA_COLUMN(CollisionTime, collisionTime, float); //! Collision time in ns relative to BC stored in bc() +DECLARE_SOA_COLUMN(CollisionTimeRes, collisionTimeRes, float); //! Resolution of collision time } // namespace muonaligncoll namespace o2::aod { // Reduced collisions table DECLARE_SOA_TABLE(MACollisions, "AOD", "MACOLL", //! Time and vertex information of collision - o2::soa::Index<>, muonaligncoll::RunNumber, muonaligncoll::Timestamp, + o2::soa::Index<>, muonaligncoll::RunNumber, + muonaligncoll::GlobalBC, muonaligncoll::Timestamp, collision::PosX, collision::PosY, collision::PosZ, - collision::CovXX, collision::CovYY, collision::CovZZ); + collision::CovXX, collision::CovYY, collision::CovZZ, + collision::CollisionTime, collision::CollisionTimeRes); using MACollision = MACollisions::iterator; } // namespace o2::aod @@ -166,6 +172,10 @@ DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Collision, collision, int32_t, MACollisions DECLARE_SOA_SELF_INDEX_COLUMN_FULL(MAMCHTrack, matchMCHTrack, int, "MAFWDTRACKs_MatchMCHTrack"); // o2-linter: disable=name/o2-column (to match naming in official tables) // ID of matching MFT track for GlobalMuonTracks and GlobalForwardTracks DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(MAMFTTrack, matchMFTTrack, int32_t, MAMFTTracks, "MAMFTTRKs", ""); // o2-linter: disable=name/o2-column (to match naming in official tables) +// Track time information +DECLARE_SOA_COLUMN(GlobalBC, globalBC, uint64_t); //! Bunch crossing number (globally unique in this run) +DECLARE_SOA_COLUMN(TrackTime, trackTime, float); //! Estimated time of the track in ns wrt collision().bc() or ambiguoustrack.bcSlice()[0] +DECLARE_SOA_COLUMN(TrackTimeRes, trackTimeRes, float); //! Resolution of the track time in ns } // namespace muonalignfwdtrk namespace o2::aod @@ -179,6 +189,8 @@ DECLARE_SOA_TABLE(StoredMAFwdTracks, "AOD", "MAFWDTRACK", fwdtrack::Py, fwdtrack::Pz, fwdtrack::Sign, + muonalignfwdtrk::GlobalBC, + muonalignfwdtrk::TrackTime, muonalignfwdtrk::TrackTimeRes, fwdtrack::Chi2, fwdtrack::Chi2MatchMCHMFT, muonalignfwdtrk::MAMFTTrackId, muonalignfwdtrk::MAMCHTrackId); @@ -237,6 +249,24 @@ DECLARE_SOA_TABLE(MAFwdTrkCls, "AOD", "MAFWDTRKCL", //! Forward Track Cluster in using MAFwdTrkCl = MAFwdTrkCls::iterator; } // namespace o2::aod +namespace o2::aod +{ + +namespace ma_track_association +{ +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Collision, collision, int32_t, MACollisions, "MACOLLs", ""); //! Collision index +DECLARE_SOA_ARRAY_INDEX_COLUMN_FULL_CUSTOM(Collision, compatibleColl, int32_t, MACollisions, "MACOLLs", ""); //! Array of collision indices +} // namespace ma_track_association + +DECLARE_SOA_TABLE(MAFwdTrackAssoc, "AOD", "MAFWDTRACKASSOC", //! Table for fwdtrack-to-collision association + ma_track_association::CollisionId, + muonaligncl::FwdTrackId); + +DECLARE_SOA_TABLE(MAFwdTrkCompColls, "AOD", "MAFWDTRKCOMPCOL", //! Table with vectors of collision indices stored per fwdtrack + ma_track_association::CollisionIds, o2::soa::Marker<1>); + +} // namespace o2::aod + namespace o2::aod { // Compact collision + MFT tracks table for DCA analysis @@ -252,7 +282,7 @@ using CompactMFTTrack = CompactMFTTracks::iterator; // Switch to enable/disable the processing of the derived tables // 0: process standard AO2Ds and produce the derived tables // 1: process derived AO2Ds and disable the derived tables creation -#define PROCESS_DERIVED_TABLES 0 +#define PROCESS_DERIVED_TABLES 0 // NOLINT(cppcoreguidelines-macro-usage) struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struct (exception) @@ -268,6 +298,8 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc Produces collTable; Produces fwdTable; Produces fwdCovTable; + Produces fwdTrackAssoc; + Produces fwdTrkCompColls; Produces mftTable; Produces clusTable; Produces compactMftTable; @@ -276,9 +308,18 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc Configurable cfgProduceMFTTable{"cfgProduceMFTTable", false, "flag to produce MFTs table"}; #endif + //// Variables for selecting events + struct : ConfigurableGroup { + Configurable fEvSelRequireGoodRCT{"cfgEvSelRequireGoodRCT", false, "Require good detector flags in Run Condition Table"}; + Configurable fEvSelRequireFrameBordersExclusion{"cfgEvSelRequireFrameBordersExclusion", false, "Exclude events in the TF and ITS ROF borders"}; + Configurable fEvSelRequireNoCollInTimeRange{"cfgEvSelRequireNoCollInTimeRange", false, "Exclude events with multiple collisions in time range (standard)"}; + Configurable fEvSelRequireNoSameBunch{"cfgEvSelRequireNoSameBunch", false, "Exclude events with multiple collisions in the same bunch crossing"}; + Configurable fEvSelVtxZMin{"cfgEvSelVtxZMin", -10.0f, "Minimum accepted value for vertex z position"}; + Configurable fEvSelVtxZMax{"cfgEvSelVtxZMax", 10.0f, "Maximum accepted value for vertex z position"}; + } configEvSel; + //// Variables for selecting MCH and MFT tracks Configurable cfgTrackChi2MchUp{"cfgTrackChi2MchUp", 5.f, ""}; - Configurable cfgPtMchLow{"cfgPtMchLow", 0.7f, ""}; Configurable cfgEtaMchLow{"cfgEtaMchLow", -4.0f, ""}; Configurable cfgEtaMchUp{"cfgEtaMchUp", -2.5f, ""}; Configurable cfgEtaMftLow{"cfgEtaMftLow", -3.6f, ""}; @@ -297,6 +338,10 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc Configurable cfgMftMchResidualsPLow{"cfgMftMchResidualsPLow", 30.f, ""}; Configurable cfgMftMchResidualsPtLow{"cfgMftMchResidualsPtLow", 4.f, ""}; + Configurable cfgDimuonPLow{"cfgDimuonPLow", 0.f, ""}; + Configurable cfgDimuonPtLow{"cfgDimuonPtLow", 0.7f, ""}; + Configurable cfgDimuonMassLow{"cfgDimuonMassLow", 1.8f, ""}; + Configurable cfgMftTracksMultiplicityMax{"cfgMftTracksMultiplicityMax", 0, "Maximum number of MFT tracks to be processed per event (zero means no limit)"}; // Magnetic field position bias @@ -344,8 +389,7 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; - // Configurable geoPathRealign{"geoPathRealign", "Users/j/jcastill/GeometryAlignedFix10Fix15ShiftCh1BNew2", "Path of the geometry file"}; - Configurable geoPathRealign{"geoPathRealign", "Users/j/jcastill/GeometryAlignedLoczzm4pLHC24anap1sR5a", "Path of the geometry file"}; + Configurable geoPathRealign{"geoPathRealign", "GLO/Config/GeometryAligned", "Path of the geometry file"}; Configurable cfgCcdbNoLaterThanRef{"cfgCcdbNoLaterThanRef", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object of reference basis"}; Configurable cfgCcdbNoLaterThanNew{"cfgCcdbNoLaterThanNew", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object of new basis"}; } configCCDB; @@ -363,8 +407,6 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc Configurable cfgHistDimuonTauzAxis{"cfgHistDimuonTauzAxis", "", "Di-muon tauz axis configuration"}; } configHistograms; - Configurable cfgRequireGoodRCT{"cfgRequireGoodRCT", true, "Require good detector flags in Run Condition Table"}; - Configurable cfgEnableVertexShiftAnalysis{"cfgEnableVertexShiftAnalysis", false, "Enable the analysis of vertex shift"}; Configurable cfgEnableMftDcaAnalysis{"cfgEnableMftDcaAnalysis", false, "Enable the analysis of DCA-based MFT alignment"}; Configurable cfgEnableMftDcaExtraPlots{"cfgEnableMftDcaExtraPlots", false, "Enable additional plots for the analysis of DCA-based MFT alignment"}; @@ -446,8 +488,8 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc using GlobalMuonPair = std::pair, std::vector>; geo::TransformationCreator transformation; - std::map transformRef; // reference geometry w.r.t track data - std::map transformNew; // new geometry + std::unordered_map transformRef; // reference geometry w.r.t track data + std::unordered_map transformNew; // new geometry TGeoManager* geoNew = nullptr; TGeoManager* geoRef = nullptr; TrackFitter trackFitter; // Track fitter from MCH tracking library @@ -460,7 +502,7 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc double y{0}; double z{0}; }; - std::map mMchAlignmentCorrections; + std::unordered_map mMchAlignmentCorrections; #if (PROCESS_DERIVED_TABLES == 0) Preslice perMuon = aod::fwdtrkcl::fwdtrackId; @@ -479,10 +521,11 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc // vector of all MFT-MCH(-MID) matching candidates associated to the same MCH(-MID) track, // to be sorted in descending order with respect to the matching quality // the map key is the MCH(-MID) track global index - using MatchingCandidates = std::map>; + using MatchingCandidates = std::unordered_map>; struct CollisionInfo { int runNumber{0}; + uint64_t globalBC{0}; uint64_t timestamp{0}; // z position of the collision double zVertex{0}; @@ -493,9 +536,16 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc // vector of MCH(-MID) track indexes std::vector mchTracks; // matching candidates - std::map> globalMuonTracks; + std::unordered_map> globalMuonTracks; + // number of ambiguous collisions associated to each muon track + std::unordered_map muonTracksCollMult; }; + // global vector of MCH(-MID) track indexes + std::vector mMchTracks; + // global vector of matching candidates + std::unordered_map> mGlobalMuonTracks; + std::tuple getAxisConfiguration(std::string cfgStr, int nBinsDef, double xMinDef, double xMaxDef) { // replace commas and semi-colons by spaces @@ -615,7 +665,7 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc .z = it->value["z"].GetDouble()}; } } else { - LOG(error) << "JSON parse error: " << rapidjson::GetParseErrorFunc(jsonOk.Code()) << " (" << jsonOk.Offset() << ")"; + LOG(error) << "JSON parse error: " << rapidjson::GetParseErrorFunc(jsonOk.Code()) << " (" << jsonOk.Offset() << ")"; // NOLINT(google-readability-casting) } // two-prong track fitter setup @@ -1940,76 +1990,115 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc { mMchTrackPars.clear(); mMchTrackParsNew.clear(); + mMchTracks.clear(); + mGlobalMuonTracks.clear(); // fill collision information for global muon tracks (MFT-MCH-MID matches) for (const auto& muonTrack : muonTracks) { - if (!muonTrack.has_collision()) { - continue; - } + uint32_t nCompatColl = 0; + for (const auto& collisionId : muonTrack.compatibleCollIds()) { - auto collision = collisions.rawIteratorAt(muonTrack.collisionId()); - const auto& bc = bcs.rawIteratorAt(collision.bcId()); + auto collision = collisions.rawIteratorAt(collisionId); + const auto& bc = bcs.rawIteratorAt(collision.bcId()); - // remove TF/ROF borders and ambiguous collisions - if (!bc.selection_bit(o2::aod::evsel::kNoTimeFrameBorder) || - !bc.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { - continue; - } + if (configEvSel.fEvSelRequireFrameBordersExclusion) { + // remove TF/ROF borders and ambiguous collisions + if (!bc.selection_bit(o2::aod::evsel::kNoTimeFrameBorder) || + !bc.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + continue; + } + } - if (cfgRequireGoodRCT && !rctChecker(collision)) { - continue; - } + // remove events with multiple collisions in one bunch crossing + if (configEvSel.fEvSelRequireNoCollInTimeRange && + !bc.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + continue; + } - uint64_t collisionIndex = collision.globalIndex(); + // remove events with multiple collisions in one bunch crossing + if (configEvSel.fEvSelRequireNoSameBunch && + !bc.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + continue; + } - auto& collisionInfo = collisionInfos[collisionIndex]; - collisionInfo.runNumber = bc.runNumber(); - collisionInfo.timestamp = bc.timestamp(); - collisionInfo.zVertex = collision.posZ(); + // remove events with bad RCT flags + if (configEvSel.fEvSelRequireGoodRCT && !rctChecker(collision)) { + continue; + } + + if (collision.posZ() < configEvSel.fEvSelVtxZMin || + collision.posZ() > configEvSel.fEvSelVtxZMax) { + continue; + } + + nCompatColl += 1; + + uint64_t collisionIndex = collision.globalIndex(); - if (static_cast(muonTrack.trackType()) > GlobalTrackTypeMax) { - // standalone MCH or MCH-MID tracks - uint64_t mchTrackIndex = muonTrack.globalIndex(); - collisionInfo.mchTracks.push_back(mchTrackIndex); + auto& collisionInfo = collisionInfos[collisionIndex]; + collisionInfo.runNumber = bc.runNumber(); + collisionInfo.globalBC = bc.globalBC(); + collisionInfo.timestamp = bc.timestamp(); + collisionInfo.zVertex = collision.posZ(); - // initialize the original MCH track parameters - mMchTrackPars.try_emplace(mchTrackIndex, TrackParExt(fwdtrackutils::getTrackParCovFwd(muonTrack, muonTrack), muonTrack.nClusters())); + if (static_cast(muonTrack.trackType()) > GlobalTrackTypeMax) { + // standalone MCH or MCH-MID tracks + uint64_t mchTrackIndex = muonTrack.globalIndex(); + collisionInfo.mchTracks.push_back(mchTrackIndex); - // refit MCH track if requested - if (configRealign.cfgEnableMCHRefit || configRealign.cfgEnableMCHRealign) { - TrackRealigned convertedTrack; - bool convertedTrackOk = MchRefitTrack(muonTrack, clusters, convertedTrack, !mMchAlignmentCorrections.empty()); + mMchTracks.push_back(mchTrackIndex); - // Get the re-aligned track parameters: track param at the first cluster - mch::TrackParam trackParam = mch::TrackParam(convertedTrack.first()); + // initialize the original MCH track parameters + mMchTrackPars.try_emplace(mchTrackIndex, TrackParExt(fwdtrackutils::getTrackParCovFwd(muonTrack, muonTrack), muonTrack.nClusters())); - auto mchTrackParIt = mMchTrackParsNew.try_emplace(mchTrackIndex, TrackParExt(MCHtoFwd(trackParam), convertedTrack.getNClusters())); - if (mchTrackParIt.second) { - // the insertion succeeded - mchTrackParIt.first->second.setTrackChi2(trackParam.getTrackChi2() / convertedTrack.getNDF()); - if (!convertedTrackOk) { - mchTrackParIt.first->second.setRemovable(); + // refit MCH track if requested + if (configRealign.cfgEnableMCHRefit || configRealign.cfgEnableMCHRealign) { + TrackRealigned convertedTrack; + bool convertedTrackOk = MchRefitTrack(muonTrack, clusters, convertedTrack, !mMchAlignmentCorrections.empty()); + + // Get the re-aligned track parameters: track param at the first cluster + mch::TrackParam trackParam = mch::TrackParam(convertedTrack.first()); + + auto mchTrackParIt = mMchTrackParsNew.try_emplace(mchTrackIndex, TrackParExt(MCHtoFwd(trackParam), convertedTrack.getNClusters())); + if (mchTrackParIt.second) { + // the insertion succeeded + mchTrackParIt.first->second.setTrackChi2(trackParam.getTrackChi2() / convertedTrack.getNDF()); + if (!convertedTrackOk) { + mchTrackParIt.first->second.setRemovable(); + } } + } else { + // initialize the new MCH track parameters with the original ones, without refitting + mMchTrackParsNew.try_emplace(mchTrackIndex, TrackParExt(fwdtrackutils::getTrackParCovFwd(muonTrack, muonTrack), muonTrack.nClusters())); } } else { - // initialize the new MCH track parameters with the original ones, without refitting - mMchTrackParsNew.try_emplace(mchTrackIndex, TrackParExt(fwdtrackutils::getTrackParCovFwd(muonTrack, muonTrack), muonTrack.nClusters())); + // global muon tracks (MFT-MCH or MFT-MCH-MID) + uint64_t muonTrackIndex = muonTrack.globalIndex(); + auto const& mchTrack = muonTrack.template matchMCHTrack_as(); + uint64_t mchTrackIndex = mchTrack.globalIndex(); + + mGlobalMuonTracks[mchTrackIndex].push_back(muonTrackIndex); + + // check if a vector of global muon candidates is already available for the current MCH index + // if not, initialize a new one and add the current global muon track + // bool globalMuonTrackFound = false; + auto matchingCandidateIterator = collisionInfo.globalMuonTracks.find(mchTrackIndex); + if (matchingCandidateIterator != collisionInfo.globalMuonTracks.end()) { + matchingCandidateIterator->second.push_back(muonTrackIndex); + // globalMuonTrackFound = true; + } else { + collisionInfo.globalMuonTracks[mchTrackIndex].push_back(muonTrackIndex); + } } - } else { - // global muon tracks (MFT-MCH or MFT-MCH-MID) - uint64_t muonTrackIndex = muonTrack.globalIndex(); - auto const& mchTrack = muonTrack.template matchMCHTrack_as(); - uint64_t mchTrackIndex = mchTrack.globalIndex(); - - // check if a vector of global muon candidates is already available for the current MCH index - // if not, initialize a new one and add the current global muon track - // bool globalMuonTrackFound = false; - auto matchingCandidateIterator = collisionInfo.globalMuonTracks.find(mchTrackIndex); - if (matchingCandidateIterator != collisionInfo.globalMuonTracks.end()) { - matchingCandidateIterator->second.push_back(muonTrackIndex); - // globalMuonTrackFound = true; - } else { - collisionInfo.globalMuonTracks[mchTrackIndex].push_back(muonTrackIndex); + } + + // store the number of compatible collisions for this track in all the corresponding CollisionInfo objects + for (const auto& collisionId : muonTrack.compatibleCollIds()) { + try { + auto& collisionInfo = collisionInfos.at(collisionId); + collisionInfo.muonTracksCollMult[muonTrack.globalIndex()] = nCompatColl; + } catch (const std::exception& e) { + continue; } } } @@ -2048,7 +2137,7 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc continue; } - if (cfgRequireGoodRCT && !rctChecker(collision)) { + if (configEvSel.fEvSelRequireGoodRCT && !rctChecker(collision)) { continue; } @@ -2083,68 +2172,85 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc TMFT const& mftTracks, std::unordered_map& collisionInfos) { - // InitCollisions(collisions, bcs, muonTracks, clusters, collisionInfos); - mMchTrackPars.clear(); mMchTrackParsNew.clear(); + mMchTracks.clear(); + mGlobalMuonTracks.clear(); // fill collision information for global muon tracks (MFT-MCH-MID matches) for (const auto& muonTrack : muonTracks) { - if (!muonTrack.has_collision()) { - continue; - } + uint32_t nCompatColl = 0; + for (const auto& collisionId : muonTrack.compatibleCollIds()) { - auto collision = collisions.rawIteratorAt(muonTrack.collisionId()); - uint64_t collisionIndex = collision.globalIndex(); + auto collision = collisions.rawIteratorAt(collisionId); + uint64_t collisionIndex = collision.globalIndex(); - auto& collisionInfo = collisionInfos[collisionIndex]; - collisionInfo.runNumber = collision.runNumber(); - collisionInfo.timestamp = collision.timestamp(); - collisionInfo.zVertex = collision.posZ(); + auto& collisionInfo = collisionInfos[collisionIndex]; + collisionInfo.runNumber = collision.runNumber(); + collisionInfo.globalBC = collision.globalBC(); + collisionInfo.timestamp = collision.timestamp(); + collisionInfo.zVertex = collision.posZ(); + + nCompatColl += 1; - if (static_cast(muonTrack.trackType()) > GlobalTrackTypeMax) { - // standalone MCH or MCH-MID tracks - uint64_t mchTrackIndex = muonTrack.globalIndex(); - collisionInfo.mchTracks.push_back(mchTrackIndex); + if (static_cast(muonTrack.trackType()) > GlobalTrackTypeMax) { + // standalone MCH or MCH-MID tracks + uint64_t mchTrackIndex = muonTrack.globalIndex(); + collisionInfo.mchTracks.push_back(mchTrackIndex); - // initialize the original MCH track parameters - mMchTrackPars.try_emplace(mchTrackIndex, TrackParExt(fwdtrackutils::getTrackParCovFwd(muonTrack, muonTrack), muonTrack.nClusters())); + mMchTracks.push_back(mchTrackIndex); - // refit MCH track if requested - if (configRealign.cfgEnableMCHRefit || configRealign.cfgEnableMCHRealign) { - TrackRealigned convertedTrack; - bool convertedTrackOk = MchRefitTrack(muonTrack, clusters, convertedTrack, !mMchAlignmentCorrections.empty()); + // initialize the original MCH track parameters + mMchTrackPars.try_emplace(mchTrackIndex, TrackParExt(fwdtrackutils::getTrackParCovFwd(muonTrack, muonTrack), muonTrack.nClusters())); - // Get the re-aligned track parameters: track param at the first cluster - mch::TrackParam trackParam = mch::TrackParam(convertedTrack.first()); + // refit MCH track if requested + if (configRealign.cfgEnableMCHRefit || configRealign.cfgEnableMCHRealign) { + TrackRealigned convertedTrack; + bool convertedTrackOk = MchRefitTrack(muonTrack, clusters, convertedTrack, !mMchAlignmentCorrections.empty()); - auto mchTrackParIt = mMchTrackParsNew.try_emplace(mchTrackIndex, TrackParExt(MCHtoFwd(trackParam), convertedTrack.getNClusters())); - if (mchTrackParIt.second) { - // the insertion succeeded - mchTrackParIt.first->second.setTrackChi2(trackParam.getTrackChi2() / convertedTrack.getNDF()); - if (!convertedTrackOk) { - mchTrackParIt.first->second.setRemovable(); + // Get the re-aligned track parameters: track param at the first cluster + mch::TrackParam trackParam = mch::TrackParam(convertedTrack.first()); + + auto mchTrackParIt = mMchTrackParsNew.try_emplace(mchTrackIndex, TrackParExt(MCHtoFwd(trackParam), convertedTrack.getNClusters())); + if (mchTrackParIt.second) { + // the insertion succeeded + mchTrackParIt.first->second.setTrackChi2(trackParam.getTrackChi2() / convertedTrack.getNDF()); + if (!convertedTrackOk) { + mchTrackParIt.first->second.setRemovable(); + } } + } else { + // initialize the new MCH track parameters with the original ones, without refitting + mMchTrackParsNew.try_emplace(mchTrackIndex, TrackParExt(fwdtrackutils::getTrackParCovFwd(muonTrack, muonTrack), muonTrack.nClusters())); } } else { - // initialize the new MCH track parameters with the original ones, without refitting - mMchTrackParsNew.try_emplace(mchTrackIndex, TrackParExt(fwdtrackutils::getTrackParCovFwd(muonTrack, muonTrack), muonTrack.nClusters())); + // global muon tracks (MFT-MCH or MFT-MCH-MID) + uint64_t muonTrackIndex = muonTrack.globalIndex(); + auto const& mchTrack = muonTrack.template matchMCHTrack_as(); + uint64_t mchTrackIndex = mchTrack.globalIndex(); + + mGlobalMuonTracks[mchTrackIndex].push_back(muonTrackIndex); + + // check if a vector of global muon candidates is already available for the current MCH index + // if not, initialize a new one and add the current global muon track + // bool globalMuonTrackFound = false; + auto matchingCandidateIterator = collisionInfo.globalMuonTracks.find(mchTrackIndex); + if (matchingCandidateIterator != collisionInfo.globalMuonTracks.end()) { + matchingCandidateIterator->second.push_back(muonTrackIndex); + // globalMuonTrackFound = true; + } else { + collisionInfo.globalMuonTracks[mchTrackIndex].push_back(muonTrackIndex); + } } - } else { - // global muon tracks (MFT-MCH or MFT-MCH-MID) - uint64_t muonTrackIndex = muonTrack.globalIndex(); - auto const& mchTrack = muonTrack.template matchMCHTrack_as(); - uint64_t mchTrackIndex = mchTrack.globalIndex(); - - // check if a vector of global muon candidates is already available for the current MCH index - // if not, initialize a new one and add the current global muon track - // bool globalMuonTrackFound = false; - auto matchingCandidateIterator = collisionInfo.globalMuonTracks.find(mchTrackIndex); - if (matchingCandidateIterator != collisionInfo.globalMuonTracks.end()) { - matchingCandidateIterator->second.push_back(muonTrackIndex); - // globalMuonTrackFound = true; - } else { - collisionInfo.globalMuonTracks[mchTrackIndex].push_back(muonTrackIndex); + } + + // store the number of compatible collisions for this track in all the corresponding CollisionInfo objects + for (const auto& collisionId : muonTrack.compatibleCollIds()) { + try { + auto& collisionInfo = collisionInfos.at(collisionId); + collisionInfo.muonTracksCollMult[muonTrack.globalIndex()] = nCompatColl; + } catch (const std::exception& e) { + continue; } } } @@ -2200,18 +2306,27 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc } #if (PROCESS_DERIVED_TABLES == 0) - template + template void StoreCollisions(const std::unordered_map& collisionInfos, COLL const& collisions, + BC const& bcs, TMUON const& muonTracks, TCLS const& clusters, - TMFT const& /*mftTracks*/) + TMFT const& /*mftTracks*/, + aod::FwdTrackAssoc const& fwdTrackAssocs) { - int32_t collId = 0; - int32_t fwdTrkId = 0; - int32_t mftTrkId = 0; + int32_t remappedCollisionIndex = 0; + int32_t remappedFwdTrkIndex = 0; + int32_t remappedMftTrkIndex = 0; + + // remapping tables for collisions and tracks IDs + std::unordered_map collisionIdRemapping; + std::unordered_map muonTrackIdRemapping; + std::vector muonTrackIdInverseMapping; + + // define vector of vectors to store indices of compatible collisions per track + std::vector>> collsPerTrack; - std::unordered_map mchTrackIdRemapping; for (const auto& [collisionIndex, collisionInfo] : collisionInfos) { auto const& c = collisions.rawIteratorAt(collisionIndex); @@ -2219,42 +2334,93 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc continue; } - collTable(collisionInfo.runNumber, collisionInfo.timestamp, + collTable(collisionInfo.runNumber, + collisionInfo.globalBC, + collisionInfo.timestamp, c.posX(), c.posY(), c.posZ(), - c.covXX(), c.covYY(), c.covZZ()); + c.covXX(), c.covYY(), c.covZZ(), + c.collisionTime(), c.collisionTimeRes()); + collisionIdRemapping[collisionIndex] = remappedCollisionIndex; - bool hasOppositeSignMuonPair = false; + bool hasGoodOppositeSignMuonPair = false; std::vector muonPairs; getMuonPairs(collisionInfo, muonPairs); for (auto [mchIndex1, mchIndex2] : muonPairs) { - int sign1 = muonTracks.rawIteratorAt(mchIndex1).sign(); - int sign2 = muonTracks.rawIteratorAt(mchIndex2).sign(); + const auto& muonTrack1 = muonTracks.rawIteratorAt(mchIndex1); + const auto& muonTrack2 = muonTracks.rawIteratorAt(mchIndex2); + int sign1 = muonTrack1.sign(); + int sign2 = muonTrack2.sign(); - // only consider opposite-sign pairs - if ((sign1 * sign2) < 0) { - hasOppositeSignMuonPair = true; - break; + // skip same-sign muon pairs + if ((sign1 * sign2) >= 0) { + continue; } + + bool isGoodMuon1 = IsGoodMuon(muonTrack1, c, cfgTrackChi2MchUp, cfgDimuonPLow, cfgDimuonPtLow, {cfgEtaMchLow, cfgEtaMchUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); + bool isGoodMuon2 = IsGoodMuon(muonTrack2, c, cfgTrackChi2MchUp, cfgDimuonPLow, cfgDimuonPtLow, {cfgEtaMchLow, cfgEtaMchUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); + bool goodMuonTracks = (isGoodMuon1 && isGoodMuon2); + if (!goodMuonTracks) { + continue; + } + + // get the pre-stored MCH track parameters + const auto mchTrackParIt1 = mMchTrackPars.find(mchIndex1); + if (mchTrackParIt1 == mMchTrackPars.end()) { + continue; + } + const auto mchTrackParIt2 = mMchTrackPars.find(mchIndex2); + if (mchTrackParIt2 == mMchTrackPars.end()) { + continue; + } + + // compute the track parameters at the vertex + const auto& mchTrackPar1 = mchTrackParIt1->second; + auto mchTrackPar1AtVertex = PropagateMCHToVertex(mchTrackPar1, c); + const auto& mchTrackPar2 = mchTrackParIt2->second; + auto mchTrackPar2AtVertex = PropagateMCHToVertex(mchTrackPar2, c); + + // skip opposite-sign muon pairs with invariant mass below the threshold + double mass = getMuMuInvariantMass(mchTrackPar1AtVertex, mchTrackPar2AtVertex); + if (mass < cfgDimuonMassLow) { + continue; + } + + hasGoodOppositeSignMuonPair = true; + break; } // loop over MCH tracks for (const auto& mchIndex : collisionInfo.mchTracks) { auto const& mchTrack = muonTracks.rawIteratorAt(mchIndex); + // skip tracks that have already been stored from another compatible collision + if (muonTrackIdRemapping.contains(mchIndex)) { + continue; + } + + // skip tracks without associated collision (this should never be true here) + if (!mchTrack.has_collision()) { + continue; + } + const auto& trackColl = collisions.rawIteratorAt(mchTrack.collisionId()); + const auto& trackBc = bcs.rawIteratorAt(trackColl.bcId()); + // store all MCH tracks when the event contains a muon pair of opposite sign - if (!hasOppositeSignMuonPair) { + if (!hasGoodOppositeSignMuonPair) { // only store good MCH tracks - if (!IsGoodMuon(mchTrack, c, cfgTrackChi2MchUp, 0.f, cfgPtMchLow, {cfgEtaMftLow, cfgEtaMftUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp)) { + if (!IsGoodMuon(mchTrack, c, cfgTrackChi2MchUp, cfgMftMchResidualsPLow, cfgMftMchResidualsPtLow, {cfgEtaMftLow, cfgEtaMftUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp)) { continue; } } - fwdTable(collId, + fwdTable(remappedCollisionIndex, mchTrack.trackType(), mchTrack.x(), mchTrack.y(), mchTrack.z(), mchTrack.phi(), mchTrack.tgl(), mchTrack.signed1Pt(), mchTrack.nClusters(), mchTrack.pDca(), mchTrack.rAtAbsorberEnd(), + trackBc.globalBC(), + mchTrack.trackTime(), mchTrack.trackTimeRes(), mchTrack.chi2(), mchTrack.chi2MatchMCHMFT(), mchTrack.matchMFTTrackId(), mchTrack.matchMCHTrackId()); @@ -2267,11 +2433,13 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc // loop over attached clusters auto clustersSliced = clusters.sliceBy(perMuon, mchTrack.globalIndex()); // Slice clusters by muon id for (auto const& cluster : clustersSliced) { - clusTable(fwdTrkId, cluster.x(), cluster.y(), cluster.z(), cluster.clInfo()); + clusTable(remappedFwdTrkIndex, cluster.x(), cluster.y(), cluster.z(), cluster.clInfo()); } - mchTrackIdRemapping[mchTrack.globalIndex()] = fwdTrkId; - fwdTrkId += 1; + muonTrackIdRemapping[mchTrack.globalIndex()] = remappedFwdTrkIndex; + muonTrackIdInverseMapping.push_back(mchTrack.globalIndex()); + collsPerTrack.emplace_back(std::make_unique>()); + remappedFwdTrkIndex += 1; } // loop over global muon tracks @@ -2280,21 +2448,35 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc const auto& mchTrack = muonTrack.template matchMCHTrack_as(); const auto& mftTrack = muonTrack.template matchMFTTrack_as(); + // skip tracks that have already been stored from another compatible collision + if (muonTrackIdRemapping.contains(muonTrack.globalIndex())) { + continue; + } + + // skip tracks without associated collision (this should never be true here) + if (!mchTrack.has_collision()) { + continue; + } try { - auto remappedMchId = mchTrackIdRemapping.at(mchTrack.globalIndex()); + auto remappedMchId = muonTrackIdRemapping.at(mchTrack.globalIndex()); + + const auto& trackColl = collisions.rawIteratorAt(mchTrack.collisionId()); + const auto& trackBc = bcs.rawIteratorAt(trackColl.bcId()); - mftTable(collId, + mftTable(remappedCollisionIndex, mftTrack.x(), mftTrack.y(), mftTrack.z(), mftTrack.phi(), mftTrack.tgl(), mftTrack.signed1Pt(), mftTrack.mftClusterSizesAndTrackFlags(), mftTrack.chi2()); - fwdTable(collId, + fwdTable(remappedCollisionIndex, muonTrack.trackType(), muonTrack.x(), muonTrack.y(), muonTrack.z(), muonTrack.phi(), muonTrack.tgl(), muonTrack.signed1Pt(), muonTrack.nClusters(), muonTrack.pDca(), muonTrack.rAtAbsorberEnd(), + trackBc.globalBC(), + mchTrack.trackTime(), mchTrack.trackTimeRes(), muonTrack.chi2(), muonTrack.chi2MatchMCHMFT(), - mftTrkId, remappedMchId); + remappedMftTrkIndex, remappedMchId); if (cfgProduceMuonAlignmentTablesWithCovariances.value) { fwdCovTable(muonTrack.sigmaX(), muonTrack.sigmaY(), muonTrack.sigmaPhi(), muonTrack.sigmaTgl(), muonTrack.sigma1Pt(), @@ -2302,14 +2484,50 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc muonTrack.rhoTglPhi(), muonTrack.rho1PtX(), muonTrack.rho1PtY(), muonTrack.rho1PtPhi(), muonTrack.rho1PtTgl()); } - fwdTrkId += 1; - mftTrkId += 1; + muonTrackIdRemapping[muonTrack.globalIndex()] = remappedFwdTrkIndex; + muonTrackIdInverseMapping.push_back(muonTrack.globalIndex()); + collsPerTrack.emplace_back(std::make_unique>()); + remappedFwdTrkIndex += 1; + remappedMftTrkIndex += 1; } catch (const std::exception& e) { continue; } } - collId += 1; + remappedCollisionIndex += 1; + } + + // store track-to-collision associations + for (const auto& assoc : fwdTrackAssocs) { + const auto collId = assoc.collisionId(); + const auto trackId = assoc.fwdtrackId(); + + try { + auto remappedCollId = collisionIdRemapping.at(collId); + auto remappedMchId = muonTrackIdRemapping.at(trackId); + fwdTrackAssoc(remappedCollId, remappedMchId); + } catch (const std::exception& e) { + continue; + } + } + + // store reverse track-to-collision associations + for (uint32_t remappedTrackId = 0; remappedTrackId < collsPerTrack.size(); remappedTrackId++) { + // step 1: fill vectors of remapped collision IDs for each of the tracks that is store in the derived tables + auto& remappedCollsVec = collsPerTrack[remappedTrackId]; + const auto trackId = muonTrackIdInverseMapping[remappedTrackId]; + const auto& muonTrack = muonTracks.rawIteratorAt(trackId); + for (const auto& collId : muonTrack.compatibleCollIds()) { + try { + auto remappedCollId = collisionIdRemapping.at(collId); + remappedCollsVec->push_back(remappedCollId); + } catch (const std::exception& e) { + continue; + } + } + + // step 2: output derived table + fwdTrkCompColls(*(remappedCollsVec.get())); } } #endif @@ -2486,7 +2704,7 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc continue; } - bool isGoodMuon = IsGoodMuon(mchTrack, collision, cfgTrackChi2MchUp, 0.f, cfgPtMchLow, {cfgEtaMftLow, cfgEtaMftUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); + bool isGoodMuon = IsGoodMuon(mchTrack, collision, cfgTrackChi2MchUp, cfgMftMchResidualsPLow, cfgMftMchResidualsPtLow, {cfgEtaMftLow, cfgEtaMftUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); if (!isGoodMuon) { continue; } @@ -2845,8 +3063,8 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc auto const& muonTrack1 = muonTracks.rawIteratorAt(mchIndex1); auto const& muonTrack2 = muonTracks.rawIteratorAt(mchIndex2); - bool isGoodMuon1 = IsGoodMuon(muonTrack1, collision, cfgTrackChi2MchUp, 0.f, cfgPtMchLow, {cfgEtaMchLow, cfgEtaMchUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); - bool isGoodMuon2 = IsGoodMuon(muonTrack2, collision, cfgTrackChi2MchUp, 0.f, cfgPtMchLow, {cfgEtaMchLow, cfgEtaMchUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); + bool isGoodMuon1 = IsGoodMuon(muonTrack1, collision, cfgTrackChi2MchUp, cfgDimuonPLow, cfgDimuonPtLow, {cfgEtaMchLow, cfgEtaMchUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); + bool isGoodMuon2 = IsGoodMuon(muonTrack2, collision, cfgTrackChi2MchUp, cfgDimuonPLow, cfgDimuonPtLow, {cfgEtaMchLow, cfgEtaMchUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); bool goodMuonTracks = (isGoodMuon1 && isGoodMuon2); if (!goodMuonTracks) { @@ -2910,8 +3128,8 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc auto const& fwdTrack1 = muonTracks.rawIteratorAt(fwdIndex1); auto const& fwdTrack2 = muonTracks.rawIteratorAt(fwdIndex2); - bool isGoodGlobalMuon1 = IsGoodMuon(muonTrack1, collision, cfgTrackChi2MchUp, 0.f, cfgPtMchLow, {cfgEtaMftLow, cfgEtaMftUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); - bool isGoodGlobalMuon2 = IsGoodMuon(muonTrack2, collision, cfgTrackChi2MchUp, 0.f, cfgPtMchLow, {cfgEtaMftLow, cfgEtaMftUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); + bool isGoodGlobalMuon1 = IsGoodMuon(muonTrack1, collision, cfgTrackChi2MchUp, cfgDimuonPLow, cfgDimuonPtLow, {cfgEtaMftLow, cfgEtaMftUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); + bool isGoodGlobalMuon2 = IsGoodMuon(muonTrack2, collision, cfgTrackChi2MchUp, cfgDimuonPLow, cfgDimuonPtLow, {cfgEtaMftLow, cfgEtaMftUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); bool goodGlobalMuonTracks = (isGoodGlobalMuon1 && isGoodGlobalMuon2); bool isGoodMatch1 = isGoodGlobalMatching(fwdTrack1, cfgDimuonMatchChi2Up.value); @@ -2998,7 +3216,9 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc MyBCs const& bcs, MyMuonsWithCov const& muonTracks, MyMFTs const& mftTracks, - aod::FwdTrkCls const& clusters) + aod::FwdTrkCls const& clusters, + aod::FwdTrackAssoc const& fwdTrackAssocs, + aod::FwdTrkCompColls const& /*fwdTrackCompColls*/) { auto bc = bcs.begin(); if (mRunNumber != bc.runNumber()) { @@ -3012,7 +3232,7 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc InitCollisions(collisions, bcs, muonTracks, clusters, mftTracks, collisionInfos); if (cfgProduceMuonAlignmentTables) { - StoreCollisions(collisionInfos, collisions, muonTracks, clusters, mftTracks); + StoreCollisions(collisionInfos, collisions, bcs, muonTracks, clusters, mftTracks, fwdTrackAssocs); } FillMftPlots(collisions, muonTracks, mftTracks, collisionInfos); @@ -3027,7 +3247,7 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc #else void processDerivedTables(o2::aod::MACollisions const& collisions, - soa::Join const& muonTracks, + soa::Join const& muonTracks, o2::aod::MAFwdTrkCls const& clusters, o2::aod::MAMFTTracks const& mftTracks) { @@ -3063,29 +3283,6 @@ struct MuonGlobalAlignmentSpawner { Spawns realignFwdTrksCov; Spawns realignMftTrks; void init(InitContext const&) {} - - /*auto process(o2::aod::StoredMAFwdTracks const& storedFwd, - o2::aod::StoredMAFwdTracksCov const& storedFwdCov, - o2::aod::StoredMAMFTTracks const& storedMft) - { - // Evaluate the dynamic kinematic expressions on the fly - auto fwdExtended = o2::soa::Extend(storedFwd); - - auto fwdExtended = o2::soa::Extend(storedFwd); - - auto mftExtended = o2::soa::Extend(storedMft); - - return std::make_tuple(fwdExtended, mftExtended); - }*/ }; #endif diff --git a/PWGDQ/Tasks/qaMatching.cxx b/PWGDQ/Tasks/qaMatching.cxx index 768cacc25da..4c875ffbeae 100644 --- a/PWGDQ/Tasks/qaMatching.cxx +++ b/PWGDQ/Tasks/qaMatching.cxx @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -52,6 +53,7 @@ #include #include #include +#include #include #include @@ -102,6 +104,7 @@ DECLARE_SOA_COLUMN(MatchType, matchType, int8_t); DECLARE_SOA_COLUMN(MatchChi2, matchChi2, float); DECLARE_SOA_COLUMN(MatchScore, matchScore, float); DECLARE_SOA_COLUMN(MatchRanking, matchRanking, int32_t); +DECLARE_SOA_COLUMN(UPCMode, upcMode, bool); DECLARE_SOA_COLUMN(MftMultiplicity, mftMultiplicity, int32_t); DECLARE_SOA_COLUMN(TrackType, trackType, int8_t); DECLARE_SOA_COLUMN(MftMatchAttempts, mftMatchAttempts, int32_t); @@ -125,6 +128,7 @@ namespace o2::aod DECLARE_SOA_TABLE(QaMatchingEvents, "AOD", "QAMEVT", o2::soa::Index<>, qamatching::MftMultiplicity, + qamatching::UPCMode, qamatching::ColX, qamatching::ColY, qamatching::ColZ); @@ -198,7 +202,7 @@ namespace qamatching DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Candidate, candidate, int32_t, o2::aod::QaMatchingCandidates, "QAMCANDs", ""); } // namespace qamatching -using MyEvents = soa::Join; +using MyEvents = soa::Join; using MyMuons = soa::Join; using MyMuonsReAlign = soa::Join; using MyMuonsMC = soa::Join; @@ -1282,7 +1286,7 @@ struct QaMatching { } int nTrackTypes = static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::MCHStandaloneTrack) + 1; - AxisSpec trackTypeAxis = {static_cast(nTrackTypes), 0.0, static_cast(nTrackTypes), "track type"}; + AxisSpec trackTypeAxis = {nTrackTypes, 0.0, static_cast(nTrackTypes), "track type"}; registry.add("nTracksPerType", "Number of tracks per type", {HistType::kTH1F, {trackTypeAxis}}); AxisSpec tracksMultiplicityAxis = {cfgMftTrackMultiplicityMax, 0, static_cast(cfgMftTrackMultiplicityMax), "tracks multiplicity"}; @@ -3249,9 +3253,11 @@ struct QaMatching { int32_t reducedEventId, int& debugCounter) { + const bool upcMode = (collision.flags() & o2::dataformats::Vertex>::Flags::UPCMode) != 0; int32_t mftMultiplicity = static_cast(collisionInfo.mftTracks.size()); qaMatchingEvents( mftMultiplicity, + upcMode, static_cast(collision.posX()), static_cast(collision.posY()), static_cast(collision.posZ())); diff --git a/PWGDQ/Tasks/quarkoniaToHyperons.cxx b/PWGDQ/Tasks/quarkoniaToHyperons.cxx index 9c1fcb459f8..84606f59fac 100644 --- a/PWGDQ/Tasks/quarkoniaToHyperons.cxx +++ b/PWGDQ/Tasks/quarkoniaToHyperons.cxx @@ -1773,7 +1773,7 @@ struct QuarkoniaToHyperons { float k0shortScore = -1; if (mlConfigurations.calculateK0ShortScores) { // evaluate machine-learning scores - float* k0shortProbability = mlCustomModelK0Short.evalModel(inputFeatures); + const std::vector k0shortProbability = mlCustomModelK0Short.evalModel(inputFeatures); k0shortScore = k0shortProbability[1]; } else { k0shortScore = v0.k0ShortBDTScore(); @@ -1788,7 +1788,7 @@ struct QuarkoniaToHyperons { float lambdaScore = -1; if (mlConfigurations.calculateLambdaScores) { // evaluate machine-learning scores - float* lambdaProbability = mlCustomModelLambda.evalModel(inputFeatures); + const std::vector lambdaProbability = mlCustomModelLambda.evalModel(inputFeatures); lambdaScore = lambdaProbability[1]; } else { lambdaScore = v0.lambdaBDTScore(); @@ -1803,7 +1803,7 @@ struct QuarkoniaToHyperons { float antiLambdaScore = -1; if (mlConfigurations.calculateAntiLambdaScores) { // evaluate machine-learning scores - float* antilambdaProbability = mlCustomModelAntiLambda.evalModel(inputFeatures); + const std::vector antilambdaProbability = mlCustomModelAntiLambda.evalModel(inputFeatures); antiLambdaScore = antilambdaProbability[1]; } else { antiLambdaScore = v0.antiLambdaBDTScore(); diff --git a/PWGDQ/Tasks/tableReader.cxx b/PWGDQ/Tasks/tableReader.cxx index e9deca68d91..dc8095e31a7 100644 --- a/PWGDQ/Tasks/tableReader.cxx +++ b/PWGDQ/Tasks/tableReader.cxx @@ -1308,7 +1308,7 @@ struct AnalysisSameEventPairing { // ccdb->setLocalObjectValidityChecking(); // ccdb->setCreatedNotAfter(nolaterthan.value); - VarManager::SetCollisionSystem((TString)fCollisionSystem, fCenterMassEnergy); // set collision system and center of mass energy + VarManager::SetCollisionSystem(TString(fCollisionSystem), fCenterMassEnergy); // set collision system and center of mass energy DefineHistograms(fHistMan, histNames.Data(), fConfigAddSEPHistogram); // define all histograms dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON @@ -2117,7 +2117,7 @@ struct AnalysisDileptonTrackTrack { TString fTrackCutName1; TString fTrackCutName2; bool fIsSameTrackCut = false; - AnalysisCompositeCut fDileptonCut; + AnalysisCompositeCut fDileptonCut{}; std::vector fQuadrupletCutNames; std::vector fQuadrupletCuts; diff --git a/PWGDQ/Tasks/tableReader_withAssoc.h b/PWGDQ/Tasks/tableReader_withAssoc.h index e7952a1fdad..1aff98249a4 100644 --- a/PWGDQ/Tasks/tableReader_withAssoc.h +++ b/PWGDQ/Tasks/tableReader_withAssoc.h @@ -30,6 +30,7 @@ #include "PWGDQ/DataModel/ReducedInfoTables.h" #include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" #include "Common/CCDB/ctpRateFetcher.h" #include "Common/Core/RecoDecay.h" #include "Common/Core/TableHelper.h" @@ -176,7 +177,27 @@ DECLARE_SOA_COLUMN(AmbiguousOutOfBunchPairs, AmbiguousJpsiPairsOutOfBunch, bool) DECLARE_SOA_COLUMN(MultiplicityFT0A, multiplicityFT0AJPsi2ee, float); DECLARE_SOA_COLUMN(MultiplicityFT0C, multiplicityFT0CJPsi2ee, float); DECLARE_SOA_COLUMN(PercentileFT0M, percentileFT0MJPsi2ee, float); +DECLARE_SOA_COLUMN(PercentileFT0C, percentileFT0CJPsi2ee, float); DECLARE_SOA_COLUMN(MultiplicityNContrib, multiplicityNContribJPsi2ee, float); + +// Additional columns for prompt-non-prompt JPsi separation (BDT training) +DECLARE_SOA_COLUMN(PairSign, pairSign, int8_t); //! t1.sign() + t2.sign() +DECLARE_SOA_COLUMN(PairFilter, pairFilter, uint32_t); //! twoTrackFilter (cut bits + ambiguity bits 28-31) +DECLARE_SOA_COLUMN(McDecision, mcDecision, uint32_t); //! MC signal bits, 0 in data +DECLARE_SOA_COLUMN(Corrassoc, corrassoc, bool); //! correct collision association (MC only, true in data) +DECLARE_SOA_COLUMN(TauxyeeErr, tauxyeeErr, float); //! kVertexingTauxyErr: in ns (not cm) and with the reconstructed mass +DECLARE_SOA_COLUMN(TauzeeErr, tauzeeErr, float); //! kVertexingTauzErr: in ns (not cm) and with the reconstructed mass +DECLARE_SOA_COLUMN(LxyeeProjected, lxyeeProjected, float); //! kVertexingLxyProjected +DECLARE_SOA_COLUMN(LxyeeOverErr, lxyeeOverErr, float); //! kVertexingLxyOverErr +DECLARE_SOA_COLUMN(Lxyzee, lxyzee, float); //! kVertexingLxyz +DECLARE_SOA_COLUMN(LxyzeeOverErr, lxyzeeOverErr, float); //! kVertexingLxyzOverErr +DECLARE_SOA_COLUMN(Chi2PCAee, chi2PCAee, float); //! kVertexingChi2PCA +DECLARE_SOA_COLUMN(CosPAee, cosPAee, float); //! kCosPointingAngle +DECLARE_SOA_COLUMN(DcaXYOverSig1, dcaXYOverSig1, float); +DECLARE_SOA_COLUMN(DcaZOverSig1, dcaZOverSig1, float); +DECLARE_SOA_COLUMN(DcaXYOverSig2, dcaXYOverSig2, float); +DECLARE_SOA_COLUMN(DcaZOverSig2, dcaZOverSig2, float); + // Candidate columns for JPsi/muon correlations DECLARE_SOA_COLUMN(DeltaEta, deltaEta, float); DECLARE_SOA_COLUMN(DeltaPhi, deltaPhi, float); @@ -210,12 +231,20 @@ DECLARE_SOA_TABLE(JPsiMuonCandidates, "AOD", "DQJPSIMUONA", dqanalysisflags::MassDileptonCandidate, dqanalysisflags::Ptpair, dqanalysisflags::Etapair, dqanalysisflags::Ptassoc, dqanalysisflags::Etaassoc, dqanalysisflags::Phiassoc, dqanalysisflags::Ptleg1, dqanalysisflags::Etaleg1, dqanalysisflags::Phileg1, dqanalysisflags::Ptleg2, dqanalysisflags::Etaleg2, dqanalysisflags::Phileg2); DECLARE_SOA_TABLE(JPsieeCandidates, "AOD", "DQPSEUDOPROPER", + dqanalysisflags::PairFilter, dqanalysisflags::McDecision, dqanalysisflags::Massee, dqanalysisflags::Ptee, dqanalysisflags::Etaee, dqanalysisflags::Rapee, dqanalysisflags::Phiee, dqanalysisflags::Lxyee, dqanalysisflags::LxyeePoleMass, dqanalysisflags::Lzee, dqanalysisflags::LxyeePoleMassPVrecomputed, - dqanalysisflags::Vx, dqanalysisflags::Vy, dqanalysisflags::Vz, dqanalysisflags::DcaXY1, dqanalysisflags::DcaZ1, dqanalysisflags::ITSClusterMapleg1, dqanalysisflags::TPCnsigmaElleg1, dqanalysisflags::DcaXY2, dqanalysisflags::DcaZ2, dqanalysisflags::ITSClusterMapleg2, dqanalysisflags::TPCnsigmaElleg2, - dqanalysisflags::AmbiguousInBunchPairs, dqanalysisflags::AmbiguousOutOfBunchPairs, dqanalysisflags::MultiplicityFT0A, dqanalysisflags::MultiplicityFT0C, dqanalysisflags::PercentileFT0M, dqanalysisflags::MultiplicityNContrib); + dqanalysisflags::TauxyeeErr, dqanalysisflags::TauzeeErr, + dqanalysisflags::LxyeeProjected, dqanalysisflags::LxyeeOverErr, dqanalysisflags::Lxyzee, dqanalysisflags::LxyzeeOverErr, + dqanalysisflags::Chi2PCAee, dqanalysisflags::CosPAee, + dqanalysisflags::Vx, dqanalysisflags::Vy, dqanalysisflags::Vz, + dqanalysisflags::Ptleg1, dqanalysisflags::Etaleg1, dqanalysisflags::DcaXY1, dqanalysisflags::DcaZ1, dqanalysisflags::DcaXYOverSig1, dqanalysisflags::DcaZOverSig1, + dqanalysisflags::ITSClusterMapleg1, dqanalysisflags::ITSChi2leg1, + dqanalysisflags::Ptleg2, dqanalysisflags::Etaleg2, dqanalysisflags::DcaXY2, dqanalysisflags::DcaZ2, dqanalysisflags::DcaXYOverSig2, dqanalysisflags::DcaZOverSig2, + dqanalysisflags::ITSClusterMapleg2, dqanalysisflags::ITSChi2leg2, + dqanalysisflags::AmbiguousInBunchPairs, dqanalysisflags::AmbiguousOutOfBunchPairs, dqanalysisflags::Corrassoc, + dqanalysisflags::MultiplicityFT0C, dqanalysisflags::PercentileFT0C, dqanalysisflags::MultiplicityNContrib); } // namespace o2::aod - // Declarations of various short names using MyEvents = o2::soa::Join; using MyEventsBasic = o2::soa::Join; @@ -339,6 +368,12 @@ struct AnalysisEventSelection { o2::framework::Configurable fConfigFetchInteractionRate{"cfgFetchInteractionRate", false, "Fetch event-wise interaction rate from the CCDB"}; o2::framework::Configurable fConfigIRSource{"cfgIRSource", "ZNC hadronic", "Estimator of the interaction rate (Recommended: pp --> T0VTX, Pb-Pb --> ZNC hadronic)"}; + o2::framework::Configurable cfgUseRCT{"cfgUseRCT", false, "Apply RCT quality selection"}; + o2::framework::Configurable cfgRCTPath{"cfgRCTPath", "RCT/Flags/RunFlags", "CCDB path to the RCT run flags"}; + o2::framework::Configurable cfgRCTLabel{"cfgRCTLabel", "CBT", "RCT selection label"}; + o2::framework::Configurable cfgCheckZDC{"cfgCheckZDC", false, "Also check ZDC quality"}; + o2::framework::Configurable cfgRCTPassName{"cfgRCTPassName", "", "Reconstruction pass; leave empty for run-only lookup"}; + HistogramManager* fHistMan = nullptr; MixingHandler* fMixHandler = nullptr; AnalysisCompositeCut* fEventCut = nullptr; @@ -351,9 +386,12 @@ struct AnalysisEventSelection { std::map> fBCCollMap; // key: global BC, value: vector of reduced event global indices int fCurrentRun = -1; + o2::aod::rctsel::RCTFlagsChecker rctChecker{"CBT"}; + std::map rctFlags; + int rctRun = -1; + void init(o2::framework::InitContext& context) { - bool isAnyProcessEnabled = context.mOptions.get("processSkimmed") || context.mOptions.get("processSkimmedBasic") || context.mOptions.get("processSkimmedWithZdc") || context.mOptions.get("processSkimmedWithMultExtra") || context.mOptions.get("processSkimmedWithMultExtraNoQvector") || context.mOptions.get("processSkimmedWithMultExtraZdc") || context.mOptions.get("processSkimmedWithMultExtraZdcFit") || context.mOptions.get("processSkimmedWithQvectorCentr") || context.mOptions.get("processSkimmedWithQvectorCentrMerge"); bool isDummyEnabled = context.mOptions.get("processDummy"); @@ -424,6 +462,61 @@ struct AnalysisEventSelection { fCCDB->setLocalObjectValidityChecking(); fCCDB->setCreatedNotAfter(fConfigNoLaterThan.value); fCCDBApi.init(fConfigCcdbUrl.value); + + if (cfgUseRCT.value) { + if (cfgRCTLabel.value != "CBT" && cfgRCTLabel.value != "CBT_hadronPID" && cfgRCTLabel.value != "CBT_electronPID" && cfgRCTLabel.value != "CBT_calo" && cfgRCTLabel.value != "CBT_muon" && cfgRCTLabel.value != "CBT_muon_glo") { + LOGF(fatal, "RCT Label \"%s\" is unknown.", cfgRCTLabel.value.c_str()); + } + + rctChecker.init(cfgRCTLabel.value, cfgCheckZDC.value); + + if (!rctChecker.any()) { + LOGF(fatal, "Empty RCT mask: check cfgRCTLabel."); + } + } + } + + void loadRCT(int run, uint64_t timestamp) + { + std::map metadata{{"run", std::to_string(run)}}; + + LOG(info) << "Requesting " << cfgRCTPath.value << " run=" << run << " timestamp=" << timestamp; + + if (!cfgRCTPassName.value.empty()) { + metadata["passName"] = cfgRCTPassName.value; + } + + auto* flags = fCCDB->getSpecific>(cfgRCTPath.value, timestamp, metadata); + if (flags == nullptr && !cfgRCTPassName.value.empty()) { + metadata.erase("passName"); + flags = fCCDB->getSpecific>(cfgRCTPath.value, timestamp, metadata); + } + + if (flags == nullptr || flags->empty()) { + LOGF(fatal, "Missing or empty RCT flags for run %d.", run); + } + + rctFlags = *flags; + rctRun = run; + } + + bool passesRCT(int run, uint64_t timestamp) + { + if (!cfgUseRCT.value) { + return true; + } + + if (run != rctRun) { + loadRCT(run, timestamp); + } + + auto iterator = rctFlags.upper_bound(timestamp); + if (iterator == rctFlags.begin()) { + return false; + } + + const uint32_t flags = std::prev(iterator)->second; + return (static_cast(flags) & rctChecker.value()) == 0; } template @@ -463,7 +556,7 @@ struct AnalysisEventSelection { } // Apply event cuts and fill histograms after selection - if (fEventCut->IsSelected(dqtablereader_helpers::varValues())) { + if (fEventCut->IsSelected(dqtablereader_helpers::varValues()) && passesRCT(event.runNumber(), event.timestamp())) { if (fConfigRunZorro) { if (event.tag_bit(56)) { // This is the bit used for the software trigger event selections [TO BE DONE: find a more clear way to use it] if (fConfigQA) { @@ -722,12 +815,13 @@ struct AnalysisTrackSelection { fTrackCuts.push_back(o2::aod::dqcuts::GetCompositeCut(objArray->At(icut)->GetName())); } } + // Extra cuts via JSON TString addTrackCutsStr = fConfigCutsJSON.value; if (addTrackCutsStr != "") { std::vector addTrackCuts = o2::aod::dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); for (auto const& t : addTrackCuts) { - fTrackCuts.push_back(dynamic_cast(t)); + fTrackCuts.push_back(reinterpret_cast(t)); } } @@ -1379,6 +1473,10 @@ struct AnalysisSameEventPairing { // TODO: Add pair cuts via JSON } fConfigCuts; + // add event level histograms after remove collision splitting candidates + o2::framework::Configurable fConfigAddEventHistogram{"cfgAddEventHistogram", "", "Comma separated list of histograms,only used after turn on the cfgRemoveCollSplittingCandidates option"}; + o2::framework::Configurable fConfigAddJSONEventHistograms{"cfgAddJSONEventHistograms", "", "Event histograms in JSON format,only used after turn on the cfgRemoveCollSplittingCandidates option"}; + o2::framework::Configurable fConfigMixingDepth{"cfgMixingDepth", 100, "Number of Events stored for event mixing"}; o2::framework::Configurable fConfigMixingVariables{"cfgMixingVars", "", "Mixing configs separated by a comma, default no mixing"}; o2::framework::Configurable fConfigMixingVariablesJson{"cfgMixingVarsJSON", "", "Mixing configs in JSON format"}; @@ -1467,6 +1565,8 @@ struct AnalysisSameEventPairing { bool fEnableBarrelMuonHistos = false; bool fEnableBarrelMuonMixingHistos = false; + bool fSplitCheckEnabled = false; + o2::framework::NoBinningPolicy hashBin; o2::framework::Preslice> trackAssocsPerCollision = o2::aod::reducedtrack_association::reducedeventId; @@ -1474,7 +1574,7 @@ struct AnalysisSameEventPairing { void init(o2::framework::InitContext& context) { - fEnableBarrelHistos = context.mOptions.get("processAllSkimmed") || context.mOptions.get("processBarrelOnlySkimmed") || context.mOptions.get("processBarrelOnlyWithCollSkimmed") || context.mOptions.get("processBarrelOnlySkimmedNoCov") || context.mOptions.get("processBarrelOnlySkimmedNoCovWithMultExtra") || context.mOptions.get("processBarrelOnlyWithQvectorCentrSkimmedNoCov") || context.mOptions.get("processBarrelOnlyWithQvectorCentrSkimmed"); + fEnableBarrelHistos = context.mOptions.get("processAllSkimmed") || context.mOptions.get("processBarrelOnlySkimmed") || context.mOptions.get("processBarrelOnlyWithCollSkimmed") || context.mOptions.get("processBarrelOnlySkimmedNoCov") || context.mOptions.get("processBarrelOnlySkimmedNoCovWithMultExtra") || context.mOptions.get("processBarrelOnlyWithQvectorCentrSkimmedNoCov") || context.mOptions.get("processBarrelOnlyWithQvectorCentrSkimmed") || context.mOptions.get("processBarrelOnlyDiKaons") || context.mOptions.get("processBarrelOnlyDiProtons"); fEnableBarrelMixingHistos = context.mOptions.get("processMixingAllSkimmed") || context.mOptions.get("processMixingBarrelSkimmed") || context.mOptions.get("processMixingBarrelSkimmedFlow") || context.mOptions.get("processMixingBarrelWithQvectorCentrSkimmedNoCov"); fEnableBarrelMixingHistos |= fConfigRunMixingAcrossTFs; fEnableMuonHistos = context.mOptions.get("processAllSkimmed") || context.mOptions.get("processMuonOnlySkimmed") || context.mOptions.get("processMuonOnlySkimmedMultExtra") || context.mOptions.get("processMuonOnlySkimmedFlow") || context.mOptions.get("processMuonOnlyVertexingSkimmed"); @@ -1813,12 +1913,18 @@ struct AnalysisSameEventPairing { } } + o2::common::core::getTaskOptionValue(context, "analysis-event-selection", "cfgCheckSplitCollisions", fSplitCheckEnabled, false); + if (fConfigQA) { fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(true); fHistMan->SetDefaultVarNames(dqtablereader_helpers::varNames(), dqtablereader_helpers::varUnits()); - VarManager::SetCollisionSystem((TString)fConfigOptions.collisionSystem, fConfigOptions.centerMassEnergy); // set collision system and center of mass energy - DefineHistograms(fHistMan, histNames.Data(), fConfigAddSEPHistogram.value.data()); // define all histograms + VarManager::SetCollisionSystem(TString(fConfigOptions.collisionSystem), fConfigOptions.centerMassEnergy); // set collision system and center of mass energy + if (fConfigCuts.event.value && fSplitCheckEnabled) { + DefineHistograms(fHistMan, "Event_AfterSplitVeto", fConfigAddEventHistogram.value.data()); // define event histograms after removing collision splitting candidates + o2::aod::dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigAddJSONEventHistograms.value.c_str()); + } + DefineHistograms(fHistMan, histNames.Data(), fConfigAddSEPHistogram.value.data()); // define all histograms if (fEnableBarrelHistos) { DefineHistograms(fHistMan, "PairingSEQA", "sameevent-pairing"); // histograms for QA of the pairing } @@ -1943,7 +2049,7 @@ struct AnalysisSameEventPairing { auto groupedAssocs = assocs.sliceBy(preslice, event.globalIndex()); size_t nGood = 0; for (auto const& t : groupedAssocs) { - if constexpr (TPairType == VarManager::kDecayToEE) { + if constexpr (TPairType == VarManager::kDecayToEE || TPairType == VarManager::kDecayToPiPi || TPairType == VarManager::kDecayToKchKch || TPairType == VarManager::kDecayToPrAPr) { if (t.isBarrelSelected_raw()) { nGood++; } @@ -1998,6 +2104,11 @@ struct AnalysisSameEventPairing { // VarManager::FillEvent(event, dqtablereader_helpers::varValues()); VarManager::FillEvent(event, dqtablereader_helpers::varValues()); + // fill the event histograms + if (fConfigQA && fConfigCuts.event && fSplitCheckEnabled) { + fHistMan->FillHistClass("Event_AfterSplitVeto", dqtablereader_helpers::varValues()); + } + auto groupedAssocs = assocs.sliceBy(preslice, event.globalIndex()); if (groupedAssocs.size() == 0) { continue; @@ -2016,7 +2127,7 @@ struct AnalysisSameEventPairing { bool isFirst = true; for (auto const& [a1, a2] : o2::soa::combinations(groupedAssocs, groupedAssocs)) { - if constexpr (TPairType == VarManager::kDecayToEE) { + if constexpr (TPairType == VarManager::kDecayToEE || TPairType == VarManager::kDecayToPiPi || TPairType == VarManager::kDecayToKchKch || TPairType == VarManager::kDecayToPrAPr) { twoTrackFilter = a1.isBarrelSelected_raw() & a2.isBarrelSelected_raw() & a1.isBarrelSelectedPrefilter_raw() & a2.isBarrelSelectedPrefilter_raw() & fTrackFilterMask; if (!twoTrackFilter) { // the tracks must have at least one filter bit in common to continue @@ -2152,6 +2263,28 @@ struct AnalysisSameEventPairing { (fConfigML.applyBDT && fOutputMlPsi2ee.size() > 1) ? VarManager::fgValues[VarManager::kBdtPrompt] : -999.f, (fConfigML.applyBDT && fOutputMlPsi2ee.size() > 2) ? VarManager::fgValues[VarManager::kBdtNonprompt] : -999.f); } + constexpr bool eventHasVtxCovForTable = ((TEventFillMap & VarManager::ObjTypes::ReducedEventVtxCov) > 0); + if constexpr (trackHasCov && TTwoProngFitter && eventHasVtxCovForTable) { + const bool ambiInBunch = (twoTrackFilter & (static_cast(1) << 28)) || (twoTrackFilter & (static_cast(1) << 29)); + const bool ambiOutOfBunch = (twoTrackFilter & (static_cast(1) << 30)) || (twoTrackFilter & (static_cast(1) << 31)); + // kVertexingProcCode == 0: the fit failed; FillPairVertexing then returns early without updating the + // projected / pseudo-proper / pointing-angle variables, so they would be stale -> skip the pair + if (t1.sign() * t2.sign() < 0) { + PromptNonPromptSepTable(twoTrackFilter, dileptonMcDecision, + VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kRap], VarManager::fgValues[VarManager::kPhi], + VarManager::fgValues[VarManager::kVertexingTauxyProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMass], VarManager::fgValues[VarManager::kVertexingTauzProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMassRecalculatePV], + VarManager::fgValues[VarManager::kVertexingTauxyErr], VarManager::fgValues[VarManager::kVertexingTauzErr], + VarManager::fgValues[VarManager::kVertexingLxyProjected], VarManager::fgValues[VarManager::kVertexingLxyOverErr], VarManager::fgValues[VarManager::kVertexingLxyz], VarManager::fgValues[VarManager::kVertexingLxyzOverErr], + VarManager::fgValues[VarManager::kVertexingChi2PCA], VarManager::fgValues[VarManager::kCosPointingAngle], + VarManager::fgValues[VarManager::kVtxX], VarManager::fgValues[VarManager::kVtxY], VarManager::fgValues[VarManager::kVtxZ], + t1.pt(), t1.eta(), VarManager::fgValues[VarManager::kDCAxy1], VarManager::fgValues[VarManager::kDCAz1], VarManager::fgValues[VarManager::kDCAxyOverSig1], VarManager::fgValues[VarManager::kDCAzOverSig1], + VarManager::fgValues[VarManager::kITSclusterMap1], t1.itsChi2NCl(), + t2.pt(), t2.eta(), VarManager::fgValues[VarManager::kDCAxy2], VarManager::fgValues[VarManager::kDCAz2], VarManager::fgValues[VarManager::kDCAxyOverSig2], VarManager::fgValues[VarManager::kDCAzOverSig2], + VarManager::fgValues[VarManager::kITSclusterMap2], t2.itsChi2NCl(), + ambiInBunch, ambiOutOfBunch, true, + VarManager::fgValues[VarManager::kMultFT0C], VarManager::fgValues[VarManager::kCentFT0C], VarManager::fgValues[VarManager::kVtxNcontribReal]); + } + } } } @@ -2237,6 +2370,7 @@ struct AnalysisSameEventPairing { VarManager::fgValues[VarManager::kCORR2POI], VarManager::fgValues[VarManager::kCORR4POI], VarManager::fgValues[VarManager::kM01POI], VarManager::fgValues[VarManager::kM0111POI], VarManager::fgValues[VarManager::kMultDimuons], VarManager::fgValues[VarManager::kVertexingPz], VarManager::fgValues[VarManager::kVertexingSV]); } + if constexpr ((TTrackFillMap & VarManager::ObjTypes::ReducedMuonCollInfo) > 0) { if constexpr (eventHasQvector || eventHasQvectorCentr) { dileptonFlowList(event.collisionId(), VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kCentFT0C], @@ -2278,7 +2412,7 @@ struct AnalysisSameEventPairing { isUnambiguous = !(isAmbiInBunch || isAmbiOutOfBunch); isLeg1Ambi = (twoTrackFilter & (static_cast(1) << 28) || (twoTrackFilter & (static_cast(1) << 30))); isLeg2Ambi = (twoTrackFilter & (static_cast(1) << 29) || (twoTrackFilter & (static_cast(1) << 31))); - if constexpr (TPairType == VarManager::kDecayToEE) { + if constexpr (TPairType == VarManager::kDecayToEE || TPairType == VarManager::kDecayToPiPi || TPairType == VarManager::kDecayToKchKch || TPairType == VarManager::kDecayToPrAPr) { if (isLeg1Ambi && isLeg2Ambi) { std::pair iPair(a1.reducedtrackId(), a2.reducedtrackId()); if (fAmbiguousPairs.contains(iPair)) { @@ -2293,10 +2427,6 @@ struct AnalysisSameEventPairing { } } if (sign1 * sign2 < 0) { - PromptNonPromptSepTable(VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kRap], VarManager::fgValues[VarManager::kPhi], - VarManager::fgValues[VarManager::kVertexingTauxyProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMass], VarManager::fgValues[VarManager::kVertexingTauzProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMassRecalculatePV], - VarManager::fgValues[VarManager::kVtxX], VarManager::fgValues[VarManager::kVtxY], VarManager::fgValues[VarManager::kVtxZ], VarManager::fgValues[VarManager::kDCAxy1], VarManager::fgValues[VarManager::kDCAz1], VarManager::fgValues[VarManager::kITSclusterMap1], VarManager::fgValues[VarManager::kTPCnSigmaEl1], VarManager::fgValues[VarManager::kDCAxy2], VarManager::fgValues[VarManager::kDCAz2], VarManager::fgValues[VarManager::kITSclusterMap2], VarManager::fgValues[VarManager::kTPCnSigmaEl2], - isAmbiInBunch, isAmbiOutOfBunch, VarManager::fgValues[VarManager::kMultFT0A], VarManager::fgValues[VarManager::kMultFT0C], VarManager::fgValues[VarManager::kCentFT0M], VarManager::fgValues[VarManager::kVtxNcontribReal]); if constexpr (TPairType == VarManager::kDecayToMuMu) { if (fConfigQA) { fHistMan->FillHistClass(histNames[icut][0].Data(), dqtablereader_helpers::varValues()); @@ -2332,7 +2462,7 @@ struct AnalysisSameEventPairing { } } } - if constexpr (TPairType == VarManager::kDecayToEE) { + if constexpr (TPairType == VarManager::kDecayToEE || TPairType == VarManager::kDecayToPiPi || TPairType == VarManager::kDecayToKchKch || TPairType == VarManager::kDecayToPrAPr) { if (fConfigQA) { fHistMan->FillHistClass(histNames[icut][0].Data(), dqtablereader_helpers::varValues()); if (isAmbiExtra) { @@ -2356,7 +2486,7 @@ struct AnalysisSameEventPairing { } } } - if constexpr (TPairType == VarManager::kDecayToEE) { + if constexpr (TPairType == VarManager::kDecayToEE || TPairType == VarManager::kDecayToPiPi || TPairType == VarManager::kDecayToKchKch || TPairType == VarManager::kDecayToPrAPr) { fHistMan->FillHistClass(histNames[icut][1].Data(), dqtablereader_helpers::varValues()); if (isAmbiExtra) { fHistMan->FillHistClass(histNames[icut][4].Data(), dqtablereader_helpers::varValues()); @@ -2377,7 +2507,7 @@ struct AnalysisSameEventPairing { } } } - if constexpr (TPairType == VarManager::kDecayToEE) { + if constexpr (TPairType == VarManager::kDecayToEE || TPairType == VarManager::kDecayToPiPi || TPairType == VarManager::kDecayToKchKch || TPairType == VarManager::kDecayToPrAPr) { fHistMan->FillHistClass(histNames[icut][2].Data(), dqtablereader_helpers::varValues()); if (isAmbiExtra) { fHistMan->FillHistClass(histNames[icut][5].Data(), dqtablereader_helpers::varValues()); @@ -2408,7 +2538,7 @@ struct AnalysisSameEventPairing { // edit // rotation 20 times if (fConfigTRPairs) { - if constexpr (TPairType == VarManager::kDecayToEE) { + if constexpr (TPairType == VarManager::kDecayToEE || TPairType == VarManager::kDecayToPiPi || TPairType == VarManager::kDecayToKchKch || TPairType == VarManager::kDecayToPrAPr) { twoTrackFilter = a1.isBarrelSelected_raw() & a2.isBarrelSelected_raw() & a1.isBarrelSelectedPrefilter_raw() & a2.isBarrelSelectedPrefilter_raw() & fTrackFilterMask; if (!twoTrackFilter) { // the tracks must have at least one filter bit in common to continue @@ -2439,40 +2569,35 @@ struct AnalysisSameEventPairing { isUnambiguous = !(isAmbiInBunch || isAmbiOutOfBunch); isLeg1Ambi = (twoTrackFilter & (static_cast(1) << 28) || (twoTrackFilter & (static_cast(1) << 30))); isLeg2Ambi = (twoTrackFilter & (static_cast(1) << 29) || (twoTrackFilter & (static_cast(1) << 31))); - if constexpr (TPairType == VarManager::kDecayToEE) { - if (isLeg1Ambi && isLeg2Ambi) { - std::pair iPair(a1.reducedtrackId(), a2.reducedtrackId()); - if (fAmbiguousPairs.contains(iPair)) { - if (fAmbiguousPairs[iPair] & (static_cast(1) << icut)) { // if this pair is already stored with this cut - isAmbiExtra = true; - } else { - fAmbiguousPairs[iPair] |= static_cast(1) << icut; - } + if (isLeg1Ambi && isLeg2Ambi) { + std::pair iPair(a1.reducedtrackId(), a2.reducedtrackId()); + if (fAmbiguousPairs.contains(iPair)) { + if (fAmbiguousPairs[iPair] & (static_cast(1) << icut)) { // if this pair is already stored with this cut + isAmbiExtra = true; } else { - fAmbiguousPairs[iPair] = static_cast(1) << icut; + fAmbiguousPairs[iPair] |= static_cast(1) << icut; } + } else { + fAmbiguousPairs[iPair] = static_cast(1) << icut; } } + if (sign1 * sign2 < 0) { if (fConfigNRotations.value == 1) { VarManager::FillPairRotation(t1, t2, fConfigNRotations.value); - if constexpr (TPairType == VarManager::kDecayToEE) { - if (fConfigQA) { - fHistMan->FillHistClass(Form("PairsBarrelTRPM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); - if (isAmbiExtra) { - fHistMan->FillHistClass(Form("PairsBarrelTRPM_ambiguousextra_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); - } + if (fConfigQA) { + fHistMan->FillHistClass(Form("PairsBarrelTRPM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + if (isAmbiExtra) { + fHistMan->FillHistClass(Form("PairsBarrelTRPM_ambiguousextra_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); } } } else if (fConfigNRotations.value == 3) { for (int irot = 1; irot <= fConfigNRotations.value; irot++) { VarManager::FillPairRotation(t1, t2, irot); - if constexpr (TPairType == VarManager::kDecayToEE) { - if (fConfigQA) { - fHistMan->FillHistClass(Form("PairsBarrelTRPM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); - if (isAmbiExtra) { - fHistMan->FillHistClass(Form("PairsBarrelTRPM_ambiguousextra_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); - } + if (fConfigQA) { + fHistMan->FillHistClass(Form("PairsBarrelTRPM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + if (isAmbiExtra) { + fHistMan->FillHistClass(Form("PairsBarrelTRPM_ambiguousextra_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); } } } @@ -2500,7 +2625,7 @@ struct AnalysisSameEventPairing { MixingHandler::MixingEvent mixingEvent; uint32_t trackFilterForMixing = 0; for (auto const& assoc : groupedAssocs) { - if constexpr (TPairType == VarManager::kDecayToEE) { + if constexpr (TPairType == VarManager::kDecayToEE || TPairType == VarManager::kDecayToPiPi || TPairType == VarManager::kDecayToKchKch || TPairType == VarManager::kDecayToPrAPr) { trackFilterForMixing = assoc.isBarrelSelected_raw() & assoc.isBarrelSelectedPrefilter_raw() & fTrackFilterMask; if (!trackFilterForMixing) { continue; @@ -2632,7 +2757,7 @@ struct AnalysisSameEventPairing { auto twoTrackFilter = static_cast(0); for (auto const& a1 : assocs1) { for (auto const& a2 : assocs2) { - if constexpr (TPairType == VarManager::kDecayToEE) { + if constexpr (TPairType == VarManager::kDecayToEE || TPairType == VarManager::kDecayToPiPi || TPairType == VarManager::kDecayToKchKch || TPairType == VarManager::kDecayToPrAPr) { twoTrackFilter = a1.isBarrelSelected_raw() & a2.isBarrelSelected_raw() & a1.isBarrelSelectedPrefilter_raw() & a2.isBarrelSelectedPrefilter_raw() & fTrackFilterMask; if (!twoTrackFilter) { // the tracks must have at least one filter bit in common to continue continue; @@ -2747,7 +2872,7 @@ struct AnalysisSameEventPairing { } } } - if constexpr (TPairType == VarManager::kDecayToEE) { + if constexpr (TPairType == VarManager::kDecayToEE || TPairType == VarManager::kDecayToPiPi || TPairType == VarManager::kDecayToKchKch || TPairType == VarManager::kDecayToPrAPr) { fHistMan->FillHistClass(Form("PairsBarrelMEPM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); } } else { @@ -2766,7 +2891,7 @@ struct AnalysisSameEventPairing { } } } - if constexpr (TPairType == VarManager::kDecayToEE) { + if constexpr (TPairType == VarManager::kDecayToEE || TPairType == VarManager::kDecayToPiPi || TPairType == VarManager::kDecayToKchKch || TPairType == VarManager::kDecayToPrAPr) { fHistMan->FillHistClass(Form("PairsBarrelMEPP_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); } } else { @@ -2784,7 +2909,7 @@ struct AnalysisSameEventPairing { } } } - if constexpr (TPairType == VarManager::kDecayToEE) { + if constexpr (TPairType == VarManager::kDecayToEE || TPairType == VarManager::kDecayToPiPi || TPairType == VarManager::kDecayToKchKch || TPairType == VarManager::kDecayToPrAPr) { fHistMan->FillHistClass(Form("PairsBarrelMEMM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); } } @@ -2864,6 +2989,11 @@ struct AnalysisSameEventPairing { VarManager::ResetValues(0, VarManager::kNVars); VarManager::FillEvent(event, dqtablereader_helpers::varValues()); + // fill the event histograms + if (fConfigQA && fConfigCuts.event && fSplitCheckEnabled) { + fHistMan->FillHistClass("Event_AfterSplitVeto", dqtablereader_helpers::varValues()); + } + auto groupedAssocs1 = assocs1.sliceBy(preslice1, event.globalIndex()); if (groupedAssocs1.size() == 0) { continue; @@ -3118,6 +3248,20 @@ struct AnalysisSameEventPairing { runSameEventPairing(events, trackAssocsPerCollision, barrelAssocs, barrelTracks); } + void processBarrelOnlyDiKaons(MyEventsVtxCovSelected const& events, + o2::soa::Join const& barrelAssocs, + MyBarrelTracksWithCovWithAmbiguities const& barrelTracks) + { + runSameEventPairing(events, trackAssocsPerCollision, barrelAssocs, barrelTracks); + } + + void processBarrelOnlyDiProtons(MyEventsVtxCovSelected const& events, + o2::soa::Join const& barrelAssocs, + MyBarrelTracksWithCovWithAmbiguities const& barrelTracks) + { + runSameEventPairing(events, trackAssocsPerCollision, barrelAssocs, barrelTracks); + } + void processMuonOnlySkimmed(MyEventsVtxCovSelected const& events, o2::soa::Join const& muonAssocs, MyMuonTracksWithCovWithAmbiguities const& muons) { @@ -3207,6 +3351,8 @@ struct AnalysisSameEventPairing { PROCESS_SWITCH(AnalysisSameEventPairing, processBarrelOnlyWithQvectorCentrSkimmedNoCov, "Run barrel only pairing (no covariances), with skimmed tracks, with Qvector from central framework", false); PROCESS_SWITCH(AnalysisSameEventPairing, processBarrelOnlyWithQvectorCentrSkimmed, "Run barrel only pairing (no covariances), with skimmed tracks, with collision information, with Qvector from central framework", false); PROCESS_SWITCH(AnalysisSameEventPairing, processBarrelOnlySkimmedFlow, "Run barrel only pairing, with skimmed tracks and with flow", false); + PROCESS_SWITCH(AnalysisSameEventPairing, processBarrelOnlyDiKaons, "Run barrel only pairing for di-kaons", false); + PROCESS_SWITCH(AnalysisSameEventPairing, processBarrelOnlyDiProtons, "Run barrel only pairing for di-protons", false); PROCESS_SWITCH(AnalysisSameEventPairing, processMuonOnlySkimmed, "Run muon only pairing, with skimmed tracks", false); PROCESS_SWITCH(AnalysisSameEventPairing, processMuonOnlySkimmedMultExtra, "Run muon only pairing, with skimmed tracks", false); PROCESS_SWITCH(AnalysisSameEventPairing, processMuonOnlySkimmedFlow, "Run muon only pairing, with skimmed tracks and flow", false); @@ -3574,16 +3720,21 @@ struct AnalysisAsymmetricPairing { // Reserve capacity for the output tables int64_t reserveSize = 0; for (auto const& event : events) { - if (!event.isEventSelected_bit(0)) { - continue; - } - if (fConfigRemoveCollSplittingCandidates.value && event.isEventSelected_bit(2)) { - continue; - } auto groupedLegAAssocs = legACandidateAssocs.sliceBy(preslice, event.globalIndex()); auto groupedLegBAssocs = legBCandidateAssocs.sliceBy(preslice, event.globalIndex()); - reserveSize += static_cast(groupedLegAAssocs.size()) * - static_cast(groupedLegBAssocs.size()); + size_t nGoodA = 0; + size_t nGoodB = 0; + for (auto const& t : groupedLegAAssocs) { + if (t.isBarrelSelected_raw()) { + nGoodA++; + } + } + for (auto const& t : groupedLegBAssocs) { + if (t.isBarrelSelected_raw()) { + nGoodB++; + } + } + reserveSize += nGoodA * nGoodB; } ditrackList.reserve(reserveSize); ditrackExtraList.reserve(reserveSize); @@ -4506,6 +4657,7 @@ struct AnalysisDileptonTrack { o2::framework::Preslice dielectronsPerCollision = o2::aod::reducedpair::reducedeventId; o2::framework::Preslice ditracksPerCollision = o2::aod::reducedpair::reducedeventId; + // Charged B mesons to J/psi+Kch void processBarrelSkimmed(o2::soa::Filtered const& events, o2::soa::Filtered> const& assocs, MyBarrelTracksWithCov const& tracks, o2::soa::Filtered const& dileptons) @@ -4732,8 +4884,7 @@ struct AnalysisDileptonTrack { struct AnalysisDileptonTrackTrack { o2::framework::OutputObj fOutputList{"output"}; - o2::framework::Configurable fConfigTrackCut1{"cfgTrackCut1", "pionPIDCut1", "track1 cut"}; // used for select the tracks from SelectedTracks - o2::framework::Configurable fConfigTrackCut2{"cfgTrackCut2", "pionPIDCut2", "track2 cut"}; // used for select the tracks from SelectedTracks + o2::framework::Configurable fConfigTrackCuts{"cfgTrackCuts", "", "doublets of track cuts in the format cutName1:cutName2,cutName3:cutName4"}; // used for select the tracks from SelectedTracks o2::framework::Configurable fConfigDileptonCut{"cfgDiLeptonCut", "pairJpsi2", "Dilepton cut"}; o2::framework::Configurable fConfigQuadrupletCuts{"cfgQuadrupletCuts", "pairX3872Cut1", "Comma separated list of Dilepton-Track-Track cut"}; o2::framework::Configurable fConfigAddDileptonHistogram{"cfgAddDileptonHistogram", "barrel", "Comma separated list of histograms"}; @@ -4746,14 +4897,17 @@ struct AnalysisDileptonTrackTrack { o2::framework::Produces DileptonTrackTrackTable; int fCurrentRun = -1; // needed to detect if the run changed and trigger update of calibrations etc. - // uint32_t fTrackCutBitMap; // track cut bit mask to be used in the selection of tracks associated with dileptons - // cut name setting - TString fTrackCutName1; - TString fTrackCutName2; - bool fIsSameTrackCut = false; - AnalysisCompositeCut fDileptonCut; + uint32_t fTrackCutMask1 = 0; // track cut bit mask to be used in the selection of tracks + uint32_t fTrackCutMask2 = 0; // track cut bit mask to be used in the selection of tracks + uint32_t fNDoubletCuts = 0; // number of doublet track cuts + std::vector fDoubletCutNames; + std::vector> fDoubletCutMasks; + + AnalysisCompositeCut fDileptonCut{}; + bool fApplyDileptonCut = false; std::vector fQuadrupletCutNames; std::vector fQuadrupletCuts; + uint32_t fNQuadrupletCuts = 0; // number of quadruplet track cuts o2::framework::Service fCCDB{}; @@ -4769,48 +4923,127 @@ struct AnalysisDileptonTrackTrack { void init(o2::framework::InitContext& context) { - // bool isBarrel = context.mOptions.get("processJpsiPiPi"); - if (context.mOptions.get("processDummy")) { return; } + // if the processKKPiPi function is enabled, then the analysis-same-event-pairing must have the processBarrelOnlyDiKaons enabled as well + bool processKKPiPi = context.mOptions.get("processKKPiPi"); + bool processBarrelOnlyDiKaons = false; + o2::common::core::getTaskOptionValue(context, "analysis-same-event-pairing", "processBarrelOnlyDiKaons", processBarrelOnlyDiKaons, false); + bool processDummy = false; + o2::common::core::getTaskOptionValue(context, "analysis-same-event-pairing", "processDummy", processDummy, false); + if (processKKPiPi) { + if (processDummy) { + LOGF(fatal, "processKKPiPi is enabled, but processDummy should not be enabled in analysis-same-event-pairing!"); + } + if (!processBarrelOnlyDiKaons) { + LOGF(fatal, "processKKPiPi is enabled, but processBarrelOnlyDiKaons is not enabled in analysis-same-event-pairing"); + } + } + // the cfgFlatTables option must be enabled in the analysis-same-event-pairing task because the dileptonsAll table has to be filled + bool flatTables = false; + o2::common::core::getTaskOptionValue(context, "analysis-same-event-pairing", "cfgFlatTables", flatTables, false); + if (!flatTables) { + LOGF(fatal, "Flat tables are not enabled in analysis-same-event-pairing! Enable the cfgFlatTables option!"); + } + + // check that there are track cuts defined otherwise throw a FATAL error + TString tempCutsStr = fConfigTrackCuts.value; + if (tempCutsStr.IsNull() || tempCutsStr.IsWhitespace()) { + LOGF(fatal, "No track cuts defined in analysis-track-selection task"); + } + VarManager::SetDefaultVarNames(); + + // get the list of track cuts computed in the AnalysisTrackSelection task + std::string tempCuts; + o2::common::core::getTaskOptionValue(context, "analysis-track-selection", "cfgTrackCuts", tempCuts, false); + tempCutsStr = tempCuts; + // check also the cuts added via JSON and add them to the string of cuts + o2::common::core::getTaskOptionValue(context, "analysis-track-selection", "cfgBarrelTrackCutsJSON", tempCuts, false); + TString addTrackCutsStr = tempCuts; + if (addTrackCutsStr != "") { + std::vector addTrackCuts = o2::aod::dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); + for (auto const& t : addTrackCuts) { + tempCutsStr += Form(",%s", t->GetName()); + } + } + std::unique_ptr trackSelectionsUpstream(tempCutsStr.Tokenize(",")); + + // interpret the list of track cuts and build the filter mask for each of the two prongs + TString trackCutsStr = fConfigTrackCuts.value; + std::unique_ptr doubletTrackSelections(trackCutsStr.Tokenize(",")); + for (Int_t icut = 0; icut < doubletTrackSelections->GetEntries(); ++icut) { + TString cutName = doubletTrackSelections->At(icut)->GetName(); + std::unique_ptr doubletCuts(cutName.Tokenize(":")); + if (doubletCuts->GetEntries() != 2) { + LOGF(fatal, "Doublet cut %s does not have exactly 2 prongs", cutName.Data()); + } + TObject* obj = trackSelectionsUpstream->FindObject(doubletCuts->At(0)->GetName()); + if (!obj) { + LOGF(fatal, "Track cut %s not found in upstream track selections", doubletCuts->At(0)->GetName()); + } + uint32_t mask1 = (static_cast(1) << trackSelectionsUpstream->IndexOf(obj)); + obj = trackSelectionsUpstream->FindObject(doubletCuts->At(1)->GetName()); + if (!obj) { + LOGF(fatal, "Track cut %s not found in upstream track selections", doubletCuts->At(1)->GetName()); + } + uint32_t mask2 = (static_cast(1) << trackSelectionsUpstream->IndexOf(obj)); + + fTrackCutMask1 |= mask1; + fTrackCutMask2 |= mask2; + fDoubletCutMasks.push_back(std::make_pair(mask1, mask2)); + fDoubletCutNames.push_back(cutName); + fNDoubletCuts++; + } + fCurrentRun = 0; fValuesQuadruplet = new float[VarManager::kNVars]; - VarManager::SetDefaultVarNames(); fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(kTRUE); fHistMan->SetDefaultVarNames(dqtablereader_helpers::varNames(), dqtablereader_helpers::varUnits()); - // define cuts - fTrackCutName1 = fConfigTrackCut1.value; - fTrackCutName2 = fConfigTrackCut2.value; - if (fTrackCutName1 == fTrackCutName2) { - fIsSameTrackCut = true; - } + // get the dilepton cut TString configDileptonCutNamesStr = fConfigDileptonCut.value; - fDileptonCut = *o2::aod::dqcuts::GetCompositeCut(configDileptonCutNamesStr.Data()); + if (!configDileptonCutNamesStr.IsNull()) { + fApplyDileptonCut = true; + fDileptonCut = *o2::aod::dqcuts::GetCompositeCut(configDileptonCutNamesStr.Data()); + } + TString configQuadruletCutNamesStr = fConfigQuadrupletCuts.value; std::unique_ptr objArray(configQuadruletCutNamesStr.Tokenize(",")); for (Int_t icut = 0; icut < objArray->GetEntries(); ++icut) { TString cutName = objArray->At(icut)->GetName(); fQuadrupletCutNames.push_back(cutName); fQuadrupletCuts.push_back(*o2::aod::dqcuts::GetCompositeCut(cutName.Data())); + fNQuadrupletCuts++; } + // define histogram directories + // One directory for the selected pairs (dileptons) if a cut was requested + // One directory for each combination of track doublet and quadruplet cuts if (!context.mOptions.get("processDummy")) { - DefineHistograms(fHistMan, Form("Pairs_%s", configDileptonCutNamesStr.Data()), fConfigAddDileptonHistogram.value.data()); - if (!configQuadruletCutNamesStr.IsNull()) { - for (std::size_t icut = 0; icut < fQuadrupletCutNames.size(); ++icut) { - if (fIsSameTrackCut) { - DefineHistograms(fHistMan, Form("QuadrupletSEPM_%s", fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); - } else { - DefineHistograms(fHistMan, Form("QuadrupletSEPM_%s", fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); - DefineHistograms(fHistMan, Form("QuadrupletSEMP_%s", fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); + if (fApplyDileptonCut) { + DefineHistograms(fHistMan, Form("Pairs_%s", configDileptonCutNamesStr.Data()), fConfigAddDileptonHistogram.value.data()); + } + for (uint32_t idcut = 0; idcut < fNDoubletCuts; ++idcut) { + for (uint32_t icut = 0; icut < fNQuadrupletCuts; ++icut) { + DefineHistograms(fHistMan, Form("QuadrupletSEPM_%s_%s", fDoubletCutNames[idcut].Data(), fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); + // if the doublet cut is asymmetric, add also the opposite ordering of the doublet cut + if (fDoubletCutMasks[idcut].first != fDoubletCutMasks[idcut].second) { + DefineHistograms(fHistMan, Form("QuadrupletSEMP_%s_%s", fDoubletCutNames[idcut].Data(), fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); } - DefineHistograms(fHistMan, Form("QuadrupletSEPP_%s", fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); - DefineHistograms(fHistMan, Form("QuadrupletSEMM_%s", fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); + DefineHistograms(fHistMan, Form("QuadrupletSEPP_%s_%s", fDoubletCutNames[idcut].Data(), fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); + DefineHistograms(fHistMan, Form("QuadrupletSEMM_%s_%s", fDoubletCutNames[idcut].Data(), fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); + } + if (fNQuadrupletCuts == 0) { + DefineHistograms(fHistMan, Form("QuadrupletSEPM_%s", fDoubletCutNames[idcut].Data()), fConfigAddQuadrupletHistogram.value.data()); + if (fDoubletCutMasks[idcut].first != fDoubletCutMasks[idcut].second) { + DefineHistograms(fHistMan, Form("QuadrupletSEMP_%s", fDoubletCutNames[idcut].Data()), fConfigAddQuadrupletHistogram.value.data()); + } + DefineHistograms(fHistMan, Form("QuadrupletSEPP_%s", fDoubletCutNames[idcut].Data()), fConfigAddQuadrupletHistogram.value.data()); + DefineHistograms(fHistMan, Form("QuadrupletSEMM_%s", fDoubletCutNames[idcut].Data()), fConfigAddQuadrupletHistogram.value.data()); } } } @@ -4844,12 +5077,9 @@ struct AnalysisDileptonTrackTrack { { VarManager::ResetValues(0, VarManager::kNVars, fValuesQuadruplet); VarManager::FillEvent(event, fValuesQuadruplet); - // int indexOffset = -999; - // std::vector trackGlobalIndexes; for (auto const& dilepton : dileptons) { // get full track info of tracks based on the index - int indexLepton1 = dilepton.index0Id(); int indexLepton2 = dilepton.index1Id(); auto lepton1 = tracks.rawIteratorAt(dilepton.index0Id()); @@ -4859,25 +5089,24 @@ struct AnalysisDileptonTrackTrack { continue; } VarManager::FillTrack(dilepton, fValuesQuadruplet); - // LOGP(info, "is dilepton selected: {}", fDileptonCut.IsSelected(fValuesQuadruplet)); // apply the dilepton cut - if (!fDileptonCut.IsSelected(fValuesQuadruplet)) { + if (fApplyDileptonCut && !fDileptonCut.IsSelected(fValuesQuadruplet)) { continue; } - fHistMan->FillHistClass(Form("Pairs_%s", fDileptonCut.GetName()), fValuesQuadruplet); + if (fApplyDileptonCut) { + fHistMan->FillHistClass(Form("Pairs_%s", fDileptonCut.GetName()), fValuesQuadruplet); + } - // loop over hadrons pairs + // loop over track pairs for (auto const& [a1, a2] : o2::soa::combinations(assocs, assocs)) { - uint32_t trackSelection = 0; - if (fIsSameTrackCut) { - trackSelection = ((a1.isBarrelSelected_raw() & (static_cast(1) << 1)) || (a2.isBarrelSelected_raw() & (static_cast(1) << 1))); - } else { - trackSelection = ((a1.isBarrelSelected_raw() & (static_cast(1) << 1)) && (a2.isBarrelSelected_raw() & (static_cast(1) << 2))); - } - // LOGP(info, "trackSelection: {}, a1: {}, a2: {}", trackSelection, a1.isBarrelSelected_raw(), a2.isBarrelSelected_raw()); - if (trackSelection == 0) { + uint32_t trackSelection1 = 0; + uint32_t trackSelection2 = 0; + trackSelection1 = (a1.isBarrelSelected_raw() & fTrackCutMask1) | (a2.isBarrelSelected_raw() & fTrackCutMask1); + trackSelection2 = (a2.isBarrelSelected_raw() & fTrackCutMask2) | (a1.isBarrelSelected_raw() & fTrackCutMask2); + // skip if no tracks were selected + if (trackSelection1 == 0 || trackSelection2 == 0) { continue; } @@ -4892,39 +5121,81 @@ struct AnalysisDileptonTrackTrack { // fill variables VarManager::FillDileptonTrackTrack(dilepton, track1, track2, fValuesQuadruplet); if (fConfigSetupFourProngFitter || fConfigUseKFVertexing) { - // LOGP(info, "Using KF or DCA fitter for secondary vertexing"); VarManager::FillDileptonTrackTrackVertexing(event, lepton1, lepton2, track1, track2, fValuesQuadruplet); } - int iCut = 0; + // first loop over the doublet track cuts and make a decision map + int idCut = 0; + uint32_t idDoubletCutDecision = 0; + for (auto cutmask = fDoubletCutMasks.begin(); cutmask != fDoubletCutMasks.end(); cutmask++, idCut++) { + // check the doublet cuts + if ((trackSelection1 & cutmask->first) && (trackSelection2 & cutmask->second)) { + idDoubletCutDecision |= (static_cast(1) << idCut); + } + // check the reverse double cuts + if ((trackSelection2 & cutmask->first) && (trackSelection1 & cutmask->second)) { + idDoubletCutDecision |= (static_cast(1) << idCut); + } + } + uint32_t CutDecision = 0; - for (auto cutname = fQuadrupletCutNames.begin(); cutname != fQuadrupletCutNames.end(); cutname++, iCut++) { + for (uint32_t iCut = 0; iCut < fNQuadrupletCuts; iCut++) { // apply dilepton-track-track cut if (fQuadrupletCuts[iCut].IsSelected(fValuesQuadruplet)) { - CutDecision |= (1 << iCut); - if (fIsSameTrackCut) { - if (track1.sign() * track2.sign() < 0) { - fHistMan->FillHistClass(Form("QuadrupletSEPM_%s", fQuadrupletCutNames[iCut].Data()), fValuesQuadruplet); + CutDecision |= (static_cast(1) << iCut); + } + } + if (fNQuadrupletCuts > 0 && !CutDecision) { + continue; + } + + // now make a double loop over doublet and quadruplet cuts and fill histograms + for (uint32_t iDoubletCut = 0; iDoubletCut < fNDoubletCuts; iDoubletCut++) { + if (!(idDoubletCutDecision & (static_cast(1) << iDoubletCut))) { + continue; + } + // check if the prong1 and prong2 cuts are the same + bool isSameTrackCut = fDoubletCutMasks[iDoubletCut].first == fDoubletCutMasks[iDoubletCut].second; + for (uint32_t iQuadrupletCut = 0; iQuadrupletCut < fNQuadrupletCuts; iQuadrupletCut++) { + if (CutDecision & (static_cast(1) << iQuadrupletCut)) { + if (isSameTrackCut) { + if (track1.sign() * track2.sign() < 0) { + fHistMan->FillHistClass(Form("QuadrupletSEPM_%s_%s", fDoubletCutNames[iDoubletCut].Data(), fQuadrupletCutNames[iQuadrupletCut].Data()), fValuesQuadruplet); + } + } else { + if ((track1.sign() < 0) && (track2.sign() > 0)) { + fHistMan->FillHistClass(Form("QuadrupletSEMP_%s_%s", fDoubletCutNames[iDoubletCut].Data(), fQuadrupletCutNames[iQuadrupletCut].Data()), fValuesQuadruplet); + } else if ((track1.sign() > 0) && (track2.sign() < 0)) { + fHistMan->FillHistClass(Form("QuadrupletSEPM_%s_%s", fDoubletCutNames[iDoubletCut].Data(), fQuadrupletCutNames[iQuadrupletCut].Data()), fValuesQuadruplet); + } } - } else { - if ((track1.sign() < 0) && (track2.sign() > 0)) { - fHistMan->FillHistClass(Form("QuadrupletSEMP_%s", fQuadrupletCutNames[iCut].Data()), fValuesQuadruplet); - } else if ((track1.sign() > 0) && (track2.sign() < 0)) { - fHistMan->FillHistClass(Form("QuadrupletSEPM_%s", fQuadrupletCutNames[iCut].Data()), fValuesQuadruplet); + if ((track1.sign() > 0) && (track2.sign() > 0)) { + fHistMan->FillHistClass(Form("QuadrupletSEPP_%s_%s", fDoubletCutNames[iDoubletCut].Data(), fQuadrupletCutNames[iQuadrupletCut].Data()), fValuesQuadruplet); + } else if ((track1.sign() < 0) && (track2.sign() < 0)) { + fHistMan->FillHistClass(Form("QuadrupletSEMM_%s_%s", fDoubletCutNames[iDoubletCut].Data(), fQuadrupletCutNames[iQuadrupletCut].Data()), fValuesQuadruplet); } } - if ((track1.sign() > 0) && (track2.sign() > 0)) { - fHistMan->FillHistClass(Form("QuadrupletSEPP_%s", fQuadrupletCutNames[iCut].Data()), fValuesQuadruplet); - } else if ((track1.sign() < 0) && (track2.sign() < 0)) { - fHistMan->FillHistClass(Form("QuadrupletSEMM_%s", fQuadrupletCutNames[iCut].Data()), fValuesQuadruplet); + } // end of loop over quadruplet cuts + // in case there are no quadruplet cuts specified by the user + if (isSameTrackCut) { + if (track1.sign() * track2.sign() < 0) { + fHistMan->FillHistClass(Form("QuadrupletSEPM_%s", fDoubletCutNames[iDoubletCut].Data()), fValuesQuadruplet); } + } else { + if ((track1.sign() < 0) && (track2.sign() > 0)) { + fHistMan->FillHistClass(Form("QuadrupletSEMP_%s", fDoubletCutNames[iDoubletCut].Data()), fValuesQuadruplet); + } else if ((track1.sign() > 0) && (track2.sign() < 0)) { + fHistMan->FillHistClass(Form("QuadrupletSEPM_%s", fDoubletCutNames[iDoubletCut].Data()), fValuesQuadruplet); + } + } + if ((track1.sign() > 0) && (track2.sign() > 0)) { + fHistMan->FillHistClass(Form("QuadrupletSEPP_%s", fDoubletCutNames[iDoubletCut].Data()), fValuesQuadruplet); + } else if ((track1.sign() < 0) && (track2.sign() < 0)) { + fHistMan->FillHistClass(Form("QuadrupletSEMM_%s", fDoubletCutNames[iDoubletCut].Data()), fValuesQuadruplet); } - } // loop over dilepton-track-track cuts + } // end of loop over doublet cuts // fill table - if (!CutDecision) { - continue; - } DileptonTrackTrackTable(fValuesQuadruplet[VarManager::kQuadMass], fValuesQuadruplet[VarManager::kQuadPt], fValuesQuadruplet[VarManager::kQuadEta], fValuesQuadruplet[VarManager::kQuadPhi], fValuesQuadruplet[VarManager::kRap], fValuesQuadruplet[VarManager::kQ], fValuesQuadruplet[VarManager::kDeltaR1], fValuesQuadruplet[VarManager::kDeltaR2], fValuesQuadruplet[VarManager::kDeltaR], dilepton.mass(), dilepton.pt(), dilepton.eta(), dilepton.phi(), dilepton.sign(), @@ -4960,12 +5231,31 @@ struct AnalysisDileptonTrackTrack { } } + void processKKPiPi(o2::soa::Filtered const& events, + o2::soa::Filtered> const& assocs, + MyBarrelTracksWithCov const& tracks, o2::soa::Filtered const& dileptons) + { + if (events.size() == 0) { + return; + } + if (fCurrentRun != events.begin().runNumber()) { // start: runNumber + initParamsFromCCDB(events.begin().timestamp()); + fCurrentRun = events.begin().runNumber(); + } // end: runNumber + for (auto const& event : events) { + auto groupedBarrelAssocs = assocs.sliceBy(trackAssocsPerCollision, event.globalIndex()); + auto groupedDielectrons = dileptons.sliceBy(dielectronsPerCollision, event.globalIndex()); + runDileptonTrackTrack(event, groupedBarrelAssocs, tracks, groupedDielectrons); + } + } + void processDummy(MyEventsBasic const&) { // do nothing } PROCESS_SWITCH(AnalysisDileptonTrackTrack, processJpsiPiPi, "Run barrel pairing of J/psi with pion candidate", false); + PROCESS_SWITCH(AnalysisDileptonTrackTrack, processKKPiPi, "Run barrel pairing of non-resonant K+ K- pi+ pi+ candidate", false); PROCESS_SWITCH(AnalysisDileptonTrackTrack, processDummy, "Dummy function", true); }; diff --git a/PWGDQ/Tasks/tableReader_withAssoc_direct.cxx b/PWGDQ/Tasks/tableReader_withAssoc_direct.cxx index 7bbff3763c9..47a21cedcc9 100644 --- a/PWGDQ/Tasks/tableReader_withAssoc_direct.cxx +++ b/PWGDQ/Tasks/tableReader_withAssoc_direct.cxx @@ -287,7 +287,7 @@ struct AnalysisEventSelection { Configurable fConfigZorroTrigMask{"cfgZorroTriggerMask", "fDiMuon", "DQ Trigger masks: fSingleE,fLMeeIMR,fLMeeHMR,fDiElectron,fSingleMuLow,fSingleMuHigh,fDiMuon"}; Configurable fConfigRunZorroSel{"cfgRunZorroSel", false, "Select events with trigger mask"}; Configurable fBcTolerance{"cfgBcTolerance", 100, "Number of BCs of margin for software triggers"}; - Configurable fConfigCcdbPathZorro{"ccdb-path-zorro", "/Users/m/mpuccio/EventFiltering/OTS/Chunked/", "base path to the ccdb object for zorro"}; + Configurable fConfigCcdbPathZorro{"ccdb-path-zorro", "EventFiltering/Zorro/", "base path to the ccdb object for zorro"}; } fConfigZorro; // RCT selection @@ -1362,7 +1362,7 @@ struct AnalysisSameEventPairing { std::map> fTrackMuonHistNames; // for electron-muon pairs: key = iTrack * fNCutsMuon + iMuon std::vector fPairCuts; - AnalysisCompositeCut fMCGenAccCut; + AnalysisCompositeCut fMCGenAccCut{}; // bool fUseMCGenAccCut = false; uint32_t fTrackFilterMask = 0; // mask for the track cuts required in this task to be applied on the barrel cuts produced upstream @@ -1551,7 +1551,7 @@ struct AnalysisSameEventPairing { fHistMan->SetUseDefaultVariableNames(kTRUE); fHistMan->SetDefaultVarNames(static_cast(VarManager::fgVariableNames), static_cast(VarManager::fgVariableUnits)); - VarManager::SetCollisionSystem((TString)fConfigOptions.collisionSystem, fConfigOptions.centerMassEnergy); // set collision system and center of mass energy + VarManager::SetCollisionSystem(TString(fConfigOptions.collisionSystem), fConfigOptions.centerMassEnergy); // set collision system and center of mass energy DefineHistograms(fHistMan, histNames.Data(), fConfigOptions.fConfigAddSEPHistogram.value.data()); // define all histograms dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigOptions.fConfigAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON @@ -1861,7 +1861,7 @@ struct AnalysisSameEventPairing { for (unsigned int iPairCut = 0; iPairCut < fPairCuts.size(); iPairCut++) { AnalysisCompositeCut cut = fPairCuts.at(iPairCut); if (!cut.IsSelected(static_cast(VarManager::fgValues))) { - continue; // apply pair cuts + continue; // apply pair cuts } if (sign1 * sign2 < 0) { // opposite sign pairs fHistMan->FillHistClass(histNames[ncuts + icut * fPairCuts.size() + iPairCut][0].Data(), static_cast(VarManager::fgValues)); diff --git a/PWGDQ/Tasks/taskJpsiHf.cxx b/PWGDQ/Tasks/taskJpsiHf.cxx index b41950b4056..2c5135e93ba 100644 --- a/PWGDQ/Tasks/taskJpsiHf.cxx +++ b/PWGDQ/Tasks/taskJpsiHf.cxx @@ -33,6 +33,7 @@ #include +#include #include #include #include @@ -55,10 +56,16 @@ static const std::vector labelsPt{}; static const std::vector labelsCutsBdt = {"BDT background", "BDT prompt", "BDT nonprompt"}; } // namespace bdtcuts +enum class DmesonType { + D0, + Dplus +}; + // Declarations of various short names using MyRedEvents = aod::RedJpDmColls; using MyRedPairCandidatesSelected = aod::RedJpDmDileptons; using MyRedD0CandidatesSelected = soa::Join; +using MyRedDplusCandidatesSelected = soa::Join; struct taskJPsiHf { // @@ -80,7 +87,8 @@ struct taskJPsiHf { // Preslices for unsorted indexes PresliceUnsorted perCollisionDilepton = aod::jpsidmescorr::redJpDmCollId; - PresliceUnsorted perCollisionDmeson = aod::jpsidmescorr::redJpDmCollId; + PresliceUnsorted perCollisionD0 = aod::jpsidmescorr::redJpDmCollId; + PresliceUnsorted perCollisionDplus = aod::jpsidmescorr::redJpDmCollId; // histogram for normalisation std::shared_ptr hCollisions; @@ -95,7 +103,7 @@ struct taskJPsiHf { // Template function to run pair - hadron combinations // TODO: generalise to all charm-hadron species - template + template void runDileptonDmeson(TEvent const& /*event*/, TDqTrack const& dileptons, THfTrack const& dmesons) { float ptDilepton = -999; @@ -123,29 +131,55 @@ struct taskJPsiHf { continue; } - auto minItsClsDmesDau = (dmeson.numItsClsDmesProng0() < dmeson.numItsClsDmesProng1()) ? dmeson.numItsClsDmesProng0() : dmeson.numItsClsDmesProng1(); - auto minTpcCrossRowsDmesDau = (dmeson.numTpcCrossedRowsDmesProng0() < dmeson.numTpcCrossedRowsDmesProng1()) ? dmeson.numTpcCrossedRowsDmesProng0() : dmeson.numTpcCrossedRowsDmesProng1(); - auto minPtDmesDau = (dmeson.ptDmesProng0() < dmeson.ptDmesProng1()) ? dmeson.ptDmesProng0() : dmeson.ptDmesProng1(); - auto minAbsEtaDmesDau = (std::abs(dmeson.etaDmesProng0()) < std::abs(dmeson.etaDmesProng1())) ? std::abs(dmeson.etaDmesProng0()) : std::abs(dmeson.etaDmesProng1()); - - if (dmeson.massD0() > 0) { - rapDmeson = RecoDecay::y(std::array{dmeson.px(), dmeson.py(), dmeson.pz()}, constants::physics::MassD0); - deltaRap = rapDilepton - rapDmeson; - auto bdtBkg = dmeson.bdtBkgMassHypo0(); - auto bdtPrompt = dmeson.bdtPromptMassHypo0(); - auto bdtNonPrompt = dmeson.bdtNonpromptMassHypo0(); - if ((dilepton.mass() > massDileptonCandMin && dilepton.mass() < massDileptonCandMax) && (dmeson.massD0() > massHfCandMin && dmeson.massD0() < massHfCandMax && bdtBkg < cutsDmesBdt->get(ptBinDmesForBdt, "BDT background") && bdtPrompt > cutsDmesBdt->get(ptBinDmesForBdt, "BDT prompt") && bdtNonPrompt > cutsDmesBdt->get(ptBinDmesForBdt, "BDT nonprompt"))) { - redDileptDimesAll(dilepton.mass(), dmeson.massD0(), ptDilepton, ptDmeson, rapDilepton, rapDmeson, phiDilepton, phiDmeson, deltaRap, deltaPhi, bdtBkg, bdtPrompt, bdtNonPrompt, minItsClsDmesDau, minTpcCrossRowsDmesDau, minPtDmesDau, minAbsEtaDmesDau); + int minItsClsDmesDau = 0; + int minTpcCrossRowsDmesDau = 0; + float minPtDmesDau = 0.f; + float minAbsEtaDmesDau = 0.f; + if constexpr (TDmeson == DmesonType::D0) { + minItsClsDmesDau = (dmeson.numItsClsDmesProng0() < dmeson.numItsClsDmesProng1()) ? dmeson.numItsClsDmesProng0() : dmeson.numItsClsDmesProng1(); + minTpcCrossRowsDmesDau = (dmeson.numTpcCrossedRowsDmesProng0() < dmeson.numTpcCrossedRowsDmesProng1()) ? dmeson.numTpcCrossedRowsDmesProng0() : dmeson.numTpcCrossedRowsDmesProng1(); + minPtDmesDau = (dmeson.ptDmesProng0() < dmeson.ptDmesProng1()) ? dmeson.ptDmesProng0() : dmeson.ptDmesProng1(); + minAbsEtaDmesDau = (std::abs(dmeson.etaDmesProng0()) < std::abs(dmeson.etaDmesProng1())) ? std::abs(dmeson.etaDmesProng0()) : std::abs(dmeson.etaDmesProng1()); + } + if constexpr (TDmeson == DmesonType::Dplus) { + minItsClsDmesDau = std::min({dmeson.numItsClsDmesProng0(), dmeson.numItsClsDmesProng1(), dmeson.numItsClsDmesProng2()}); + minTpcCrossRowsDmesDau = std::min({dmeson.numTpcCrossedRowsDmesProng0(), dmeson.numTpcCrossedRowsDmesProng1(), dmeson.numTpcCrossedRowsDmesProng2()}); + minPtDmesDau = std::min({dmeson.ptDmesProng0(), dmeson.ptDmesProng1(), dmeson.ptDmesProng2()}); + minAbsEtaDmesDau = std::min({std::abs(dmeson.etaDmesProng0()), std::abs(dmeson.etaDmesProng1()), std::abs(dmeson.etaDmesProng2())}); + } + + if constexpr (TDmeson == DmesonType::D0) { + if (dmeson.massD0() > 0) { + rapDmeson = RecoDecay::y(std::array{dmeson.px(), dmeson.py(), dmeson.pz()}, constants::physics::MassD0); + deltaRap = rapDilepton - rapDmeson; + auto bdtBkg = dmeson.bdtBkgMassHypo0(); + auto bdtPrompt = dmeson.bdtPromptMassHypo0(); + auto bdtNonPrompt = dmeson.bdtNonpromptMassHypo0(); + if ((dilepton.mass() > massDileptonCandMin && dilepton.mass() < massDileptonCandMax) && (dmeson.massD0() > massHfCandMin && dmeson.massD0() < massHfCandMax && bdtBkg < cutsDmesBdt->get(ptBinDmesForBdt, "BDT background") && bdtPrompt > cutsDmesBdt->get(ptBinDmesForBdt, "BDT prompt") && bdtNonPrompt > cutsDmesBdt->get(ptBinDmesForBdt, "BDT nonprompt"))) { + redDileptDimesAll(dilepton.mass(), dmeson.massD0(), ptDilepton, ptDmeson, rapDilepton, rapDmeson, phiDilepton, phiDmeson, deltaRap, deltaPhi, bdtBkg, bdtPrompt, bdtNonPrompt, minItsClsDmesDau, minTpcCrossRowsDmesDau, minPtDmesDau, minAbsEtaDmesDau); + } + } + if (dmeson.massD0bar() > 0) { + rapDmeson = RecoDecay::y(std::array{dmeson.px(), dmeson.py(), dmeson.pz()}, constants::physics::MassD0); + deltaRap = rapDilepton - rapDmeson; + auto bdtBkg = dmeson.bdtBkgMassHypo1(); + auto bdtPrompt = dmeson.bdtPromptMassHypo1(); + auto bdtNonPrompt = dmeson.bdtNonpromptMassHypo1(); + if ((dilepton.mass() > massDileptonCandMin && dilepton.mass() < massDileptonCandMax) && (dmeson.massD0bar() > massHfCandMin && dmeson.massD0bar() < massHfCandMax && bdtBkg < cutsDmesBdt->get(ptBinDmesForBdt, "BDT background") && bdtPrompt > cutsDmesBdt->get(ptBinDmesForBdt, "BDT prompt") && bdtNonPrompt > cutsDmesBdt->get(ptBinDmesForBdt, "BDT nonprompt"))) { + redDileptDimesAll(dilepton.mass(), dmeson.massD0bar(), ptDilepton, ptDmeson, rapDilepton, rapDmeson, phiDilepton, phiDmeson, deltaRap, deltaPhi, bdtBkg, bdtPrompt, bdtNonPrompt, minItsClsDmesDau, minTpcCrossRowsDmesDau, minPtDmesDau, minAbsEtaDmesDau); + } } } - if (dmeson.massD0bar() > 0) { - rapDmeson = RecoDecay::y(std::array{dmeson.px(), dmeson.py(), dmeson.pz()}, constants::physics::MassD0); - deltaRap = rapDilepton - rapDmeson; - auto bdtBkg = dmeson.bdtBkgMassHypo1(); - auto bdtPrompt = dmeson.bdtPromptMassHypo1(); - auto bdtNonPrompt = dmeson.bdtNonpromptMassHypo1(); - if ((dilepton.mass() > massDileptonCandMin && dilepton.mass() < massDileptonCandMax) && (dmeson.massD0bar() > massHfCandMin && dmeson.massD0bar() < massHfCandMax && bdtBkg < cutsDmesBdt->get(ptBinDmesForBdt, "BDT background") && bdtPrompt > cutsDmesBdt->get(ptBinDmesForBdt, "BDT prompt") && bdtNonPrompt > cutsDmesBdt->get(ptBinDmesForBdt, "BDT nonprompt"))) { - redDileptDimesAll(dilepton.mass(), dmeson.massD0bar(), ptDilepton, ptDmeson, rapDilepton, rapDmeson, phiDilepton, phiDmeson, deltaRap, deltaPhi, bdtBkg, bdtPrompt, bdtNonPrompt, minItsClsDmesDau, minTpcCrossRowsDmesDau, minPtDmesDau, minAbsEtaDmesDau); + if constexpr (TDmeson == DmesonType::Dplus) { + if (dmeson.massDmes() > 0) { + rapDmeson = RecoDecay::y(std::array{dmeson.px(), dmeson.py(), dmeson.pz()}, constants::physics::MassDPlus); + deltaRap = rapDilepton - rapDmeson; + auto bdtBkg = dmeson.bdtBkgMassHypo0(); + auto bdtPrompt = dmeson.bdtPromptMassHypo0(); + auto bdtNonPrompt = dmeson.bdtNonpromptMassHypo0(); + if ((dilepton.mass() > massDileptonCandMin && dilepton.mass() < massDileptonCandMax) && (dmeson.massDmes() > massHfCandMin && dmeson.massDmes() < massHfCandMax && bdtBkg < cutsDmesBdt->get(ptBinDmesForBdt, "BDT background") && bdtPrompt > cutsDmesBdt->get(ptBinDmesForBdt, "BDT prompt") && bdtNonPrompt > cutsDmesBdt->get(ptBinDmesForBdt, "BDT nonprompt"))) { + redDileptDimesAll(dilepton.mass(), dmeson.massDmes(), ptDilepton, ptDmeson, rapDilepton, rapDmeson, phiDilepton, phiDmeson, deltaRap, deltaPhi, bdtBkg, bdtPrompt, bdtNonPrompt, minItsClsDmesDau, minTpcCrossRowsDmesDau, minPtDmesDau, minAbsEtaDmesDau); + } } } } @@ -158,8 +192,19 @@ struct taskJPsiHf { for (auto& event : events) { hCollisions->Fill(1.f); auto groupedDileptonCandidates = dileptons.sliceBy(perCollisionDilepton, event.index()); - auto groupedDmesonCandidates = dmesons.sliceBy(perCollisionDmeson, event.index()); - runDileptonDmeson(event, groupedDileptonCandidates, groupedDmesonCandidates); + auto groupedDmesonCandidates = dmesons.sliceBy(perCollisionD0, event.index()); + runDileptonDmeson(event, groupedDileptonCandidates, groupedDmesonCandidates); + } + } + + void processRedJspiDplus(MyRedEvents const& events, MyRedPairCandidatesSelected const& dileptons, MyRedDplusCandidatesSelected const& dmesons) + { + // Fill the column of collisions with pairs + for (auto& event : events) { + hCollisions->Fill(1.f); + auto groupedDileptonCandidates = dileptons.sliceBy(perCollisionDilepton, event.index()); + auto groupedDmesonCandidates = dmesons.sliceBy(perCollisionDplus, event.index()); + runDileptonDmeson(event, groupedDileptonCandidates, groupedDmesonCandidates); } } @@ -172,6 +217,7 @@ struct taskJPsiHf { } PROCESS_SWITCH(taskJPsiHf, processRedJspiD0, "Process J/psi - D0", true); + PROCESS_SWITCH(taskJPsiHf, processRedJspiDplus, "Process J/psi - Dplus", true); PROCESS_SWITCH(taskJPsiHf, processNormCounter, "Process normalization counter", true); }; diff --git a/PWGDQ/Tasks/taskMuonMchTrkEfficiency.cxx b/PWGDQ/Tasks/taskMuonMchTrkEfficiency.cxx index 999b46bd148..806adcf79e8 100644 --- a/PWGDQ/Tasks/taskMuonMchTrkEfficiency.cxx +++ b/PWGDQ/Tasks/taskMuonMchTrkEfficiency.cxx @@ -110,7 +110,7 @@ struct taskMuonMchTrkEfficiency { // define axes to be used // and the correspondent histograms - auto vbins = (std::vector)binsMuonPt; + auto vbins = std::vector(binsMuonPt); const AxisSpec axisEta{nEtaBins, etaMuonMin, etaMuonMax, "|#eta|"}; const AxisSpec axisPt{vbins, "#it{p}_{T} (GeV/#it{c})"}; const AxisSpec axisPhi{nPhiBins, -3.14, 3.14, "#varphi"}; diff --git a/PWGDQ/Tasks/v0selector.cxx b/PWGDQ/Tasks/v0selector.cxx index bc27b0eb06e..807d3295f55 100644 --- a/PWGDQ/Tasks/v0selector.cxx +++ b/PWGDQ/Tasks/v0selector.cxx @@ -387,8 +387,8 @@ struct v0selector { registry.fill(HIST("hV0Psi"), psipair, mGamma); } if (mGamma < v0max_mee && std::abs(V0.posTrack_as().tpcNSigmaEl()) < cutNsigmaElTPC && std::abs(V0.negTrack_as().tpcNSigmaEl()) < cutNsigmaElTPC && std::abs(psipair) < maxpsipair) { - pidmap[V0.posTrackId()] |= (uint8_t(1) << kGamma); - pidmap[V0.negTrackId()] |= (uint8_t(1) << kGamma); + pidmap[V0.posTrackId()] |= (static_cast(1) << kGamma); + pidmap[V0.negTrackId()] |= (static_cast(1) << kGamma); storeV0AddID(V0.globalIndex(), kGamma); if (fillhisto) { registry.fill(HIST("hGammaRxy"), V0.x(), V0.y()); @@ -402,8 +402,8 @@ struct v0selector { registry.fill(HIST("hMassK0SPhi"), V0.phi(), mK0S); } if ((0.48 < mK0S && mK0S < 0.51) && std::abs(V0.posTrack_as().tpcNSigmaPi()) < cutNsigmaPiTPC && std::abs(V0.negTrack_as().tpcNSigmaPi()) < cutNsigmaPiTPC) { - pidmap[V0.posTrackId()] |= (uint8_t(1) << kK0S); - pidmap[V0.negTrackId()] |= (uint8_t(1) << kK0S); + pidmap[V0.posTrackId()] |= (static_cast(1) << kK0S); + pidmap[V0.negTrackId()] |= (static_cast(1) << kK0S); storeV0AddID(V0.globalIndex(), kK0S); } } else if (v0id == kLambda) { // L->p + pi- @@ -411,8 +411,8 @@ struct v0selector { registry.fill(HIST("hMassLambda"), V0radius, mLambda); } if (v0id == kLambda && (1.110 < mLambda && mLambda < 1.120) && std::abs(V0.posTrack_as().tpcNSigmaPr()) < cutNsigmaPrTPC && std::abs(V0.negTrack_as().tpcNSigmaPi()) < cutNsigmaPiTPC) { - pidmap[V0.posTrackId()] |= (uint8_t(1) << kLambda); - pidmap[V0.negTrackId()] |= (uint8_t(1) << kLambda); + pidmap[V0.posTrackId()] |= (static_cast(1) << kLambda); + pidmap[V0.negTrackId()] |= (static_cast(1) << kLambda); storeV0AddID(V0.globalIndex(), kLambda); } } else if (v0id == kAntiLambda) { // Lbar -> pbar + pi+ @@ -420,8 +420,8 @@ struct v0selector { registry.fill(HIST("hMassAntiLambda"), V0radius, mAntiLambda); } if ((1.110 < mAntiLambda && mAntiLambda < 1.120) && std::abs(V0.posTrack_as().tpcNSigmaPi()) < cutNsigmaPiTPC && std::abs(V0.negTrack_as().tpcNSigmaPr()) < cutNsigmaPrTPC) { - pidmap[V0.posTrackId()] |= (uint8_t(1) << kAntiLambda); - pidmap[V0.negTrackId()] |= (uint8_t(1) << kAntiLambda); + pidmap[V0.posTrackId()] |= (static_cast(1) << kAntiLambda); + pidmap[V0.negTrackId()] |= (static_cast(1) << kAntiLambda); storeV0AddID(V0.globalIndex(), kAntiLambda); } } @@ -543,7 +543,7 @@ struct v0selector { registry.fill(HIST("hMassOmega"), cascRadius, mOmega); } if (cutMassOmegaLow < mOmega && mOmega < cutMassOmegaHigh && cutMassCascV0Low < mV0Lambda && mV0Lambda < cutMassCascV0High && std::abs(casc.posTrack_as().tpcNSigmaPr()) < cutNsigmaPrTPC && std::abs(casc.negTrack_as().tpcNSigmaPi()) < cutNsigmaPiTPC && std::abs(casc.bachelor_as().tpcNSigmaKa()) < cutNsigmaKaTPC) { - pidmap[casc.bachelorId()] |= (uint8_t(1) << kOmega); + pidmap[casc.bachelorId()] |= (static_cast(1) << kOmega); storeCascAddID(casc.globalIndex(), kOmega); } } else if (cascid == kAntiOmega && v0id == kAntiLambda) { @@ -551,7 +551,7 @@ struct v0selector { registry.fill(HIST("hMassAntiOmega"), cascRadius, mOmega); } if (cutMassOmegaLow < mOmega && mOmega < cutMassOmegaHigh && cutMassCascV0Low < mV0Lambda && mV0Lambda < cutMassCascV0High && std::abs(casc.posTrack_as().tpcNSigmaPi()) < cutNsigmaPiTPC && std::abs(casc.negTrack_as().tpcNSigmaPr()) < cutNsigmaPrTPC && std::abs(casc.bachelor_as().tpcNSigmaKa()) < cutNsigmaKaTPC) { - pidmap[casc.bachelorId()] |= (uint8_t(1) << kAntiOmega); + pidmap[casc.bachelorId()] |= (static_cast(1) << kAntiOmega); storeCascAddID(casc.globalIndex(), kAntiOmega); } } diff --git a/PWGEM/Dilepton/Core/DielectronCut.h b/PWGEM/Dilepton/Core/DielectronCut.h index 8ac890a6783..8a41649e992 100644 --- a/PWGEM/Dilepton/Core/DielectronCut.h +++ b/PWGEM/Dilepton/Core/DielectronCut.h @@ -177,9 +177,9 @@ class DielectronCut : public TNamed template bool IsSelectedTrack(TTrack const& track) const { - if (!track.hasITS() || !track.hasTPC()) { - return false; - } + // if (!track.hasITS() || !track.hasTPC()) { + // return false; + // } if (!dont_require_pteta) { if (!IsSelectedTrack(track, DielectronCuts::kTrackPtRange)) { diff --git a/PWGEM/Dilepton/Core/Dilepton.h b/PWGEM/Dilepton/Core/Dilepton.h index 5cd306812cb..738634eeb3f 100644 --- a/PWGEM/Dilepton/Core/Dilepton.h +++ b/PWGEM/Dilepton/Core/Dilepton.h @@ -326,6 +326,7 @@ struct Dilepton { struct : o2::framework::ConfigurableGroup { std::string prefix = "flowcorrection_group"; + o2::framework::Configurable cfgFillNUAHist{"cfgFillNUAHist", true, "flag to fill the NUA correction hist"}; o2::framework::Configurable cfgApplyWeightNUA{"cfgApplyWeightNUA", false, "flag to apply q-vector Non-uniform acceptance weighting"}; o2::framework::Configurable nuaPath{"nuaPath", "Users/o/omassen/Dielectron/NUAWeights/LHC23_PbPb_pass5", "Path to NUA-weights file"}; o2::framework::ConfigurableAxis ConfNUAPhiBins{"ConfNUAPhiBins", {60, -M_PI, M_PI}, "NUA histogram bins - phi-angle"}; @@ -669,7 +670,9 @@ struct Dilepton { const o2::framework::AxisSpec axis_sp{ConfSPBins, Form("#vec{u}_{%d,ll} #upoint #vec{Q}_{%d}^{%s}", nmod, nmod, qvec_det_names[cfgQvecEstimator].data())}; fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_y, axis_sp}, true); - fRegistry.add("Pair/same/uls/hNUA", "NUA Histogram;#phi (rad.);#eta;VtxZ;", o2::framework::HistType::kTH3D, {flowcorrectionGroup.ConfNUAPhiBins, flowcorrectionGroup.ConfNUAEtaBins, flowcorrectionGroup.ConfNUAZVtxBins}, true); + if (flowcorrectionGroup.cfgFillNUAHist) { + fRegistry.add("Pair/same/uls/hNUA", "NUA Histogram;#phi (rad.);#eta;VtxZ;", o2::framework::HistType::kTH3D, {flowcorrectionGroup.ConfNUAPhiBins, flowcorrectionGroup.ConfNUAEtaBins, flowcorrectionGroup.ConfNUAZVtxBins}, true); + } fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); @@ -1099,13 +1102,19 @@ struct Dilepton { float sp = RecoDecay::dotProd(std::array{static_cast(std::cos(nmod * v12.Phi())), static_cast(std::sin(nmod * v12.Phi()))}, qvectors[nmod][cfgQvecEstimator]) / getSPresolution(collision.centFT0C(), collision.trackOccupancyInTimeRange()); if (t1.sign() * t2.sign() < 0) { // ULS fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), sp, weight); - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hNUA"), v12.Phi(), v12.Eta(), collision.posZ(), weight); + if (flowcorrectionGroup.cfgFillNUAHist) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hNUA"), v12.Phi(), v12.Eta(), collision.posZ(), weight); + } } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), sp, weight); - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hNUA"), v12.Phi(), v12.Eta(), collision.posZ(), weight); + if (flowcorrectionGroup.cfgFillNUAHist) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hNUA"), v12.Phi(), v12.Eta(), collision.posZ(), weight); + } } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), sp, weight); - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hNUA"), v12.Phi(), v12.Eta(), collision.posZ(), weight); + if (flowcorrectionGroup.cfgFillNUAHist) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hNUA"), v12.Phi(), v12.Eta(), collision.posZ(), weight); + } } } else if constexpr (ev_id == 1) { if (t1.sign() * t2.sign() < 0) { // ULS @@ -1525,11 +1534,11 @@ struct Dilepton { std::pair key_df_collision = std::make_pair(ndf, collision.globalIndex()); // this gives the current event. // make a vector of selected photons in this collision. - auto selected_posTracks_in_this_event = emh_pos->GetTracksPerCollision(key_df_collision); - auto selected_negTracks_in_this_event = emh_neg->GetTracksPerCollision(key_df_collision); + const auto& selected_posTracks_in_this_event = emh_pos->GetTracksPerCollision(key_df_collision); + const auto& selected_negTracks_in_this_event = emh_neg->GetTracksPerCollision(key_df_collision); // LOGF(info, "N selected tracks in current event (%d, %d), zvtx = %f, centrality = %f , npos = %d , nneg = %d, nuls = %d , nlspp = %d, nlsmm = %d", ndf, collision.globalIndex(), collision.posZ(), centralities[cfgCentEstimator], selected_posTracks_in_this_event.size(), selected_negTracks_in_this_event.size(), nuls, nlspp, nlsmm); - auto collisionIds_in_mixing_pool = emh_pos->GetCollisionIdsFromEventPool(key_bin); // pos/neg does not matter. + const auto& collisionIds_in_mixing_pool = emh_pos->GetCollisionIdsFromEventPool(key_bin); // pos/neg does not matter. // LOGF(info, "collisionIds_in_mixing_pool.size() = %d", collisionIds_in_mixing_pool.size()); for (const auto& mix_dfId_collisionId : collisionIds_in_mixing_pool) { @@ -1546,8 +1555,8 @@ struct Dilepton { continue; } - auto posTracks_from_event_pool = emh_pos->GetTracksPerCollision(mix_dfId_collisionId); - auto negTracks_from_event_pool = emh_neg->GetTracksPerCollision(mix_dfId_collisionId); + const auto& posTracks_from_event_pool = emh_pos->GetTracksPerCollision(mix_dfId_collisionId); + const auto& negTracks_from_event_pool = emh_neg->GetTracksPerCollision(mix_dfId_collisionId); // LOGF(info, "Do event mixing: current event (%d, %d) | event pool (%d, %d), npos = %d , nneg = %d", ndf, collision.globalIndex(), mix_dfId, mix_collisionId, posTracks_from_event_pool.size(), negTracks_from_event_pool.size()); for (const auto& pos : selected_posTracks_in_this_event) { // ULS mix @@ -1577,8 +1586,15 @@ struct Dilepton { if (nuls > 0 || nlspp > 0 || nlsmm > 0) { map_mixed_eventId_to_globalBC[key_df_collision] = collision.globalBC(); - emh_pos->AddCollisionIdAtLast(key_bin, key_df_collision); - emh_neg->AddCollisionIdAtLast(key_bin, key_df_collision); + const auto evicted_pos = emh_pos->AddCollisionIdAtLast(key_bin, key_df_collision); + const auto evicted_neg = emh_neg->AddCollisionIdAtLast(key_bin, key_df_collision); + if (evicted_pos != evicted_neg) { + LOGF(fatal, "Positive and negative mixing pools are out of sync"); + } + if (evicted_pos) { + map_mixed_eventId_to_globalBC.erase(*evicted_pos); + } + } // end of if pair exist } // end of collision loop diff --git a/PWGEM/Dilepton/Core/DileptonHadronMPC.h b/PWGEM/Dilepton/Core/DileptonHadronMPC.h index 38b45983fb5..0825c1d2f40 100644 --- a/PWGEM/Dilepton/Core/DileptonHadronMPC.h +++ b/PWGEM/Dilepton/Core/DileptonHadronMPC.h @@ -1178,10 +1178,10 @@ struct DileptonHadronMPC { std::tuple key_bin = std::make_tuple(zbin, centbin, epbin, occbin); // make a vector of selected electrons in this collision. - auto selected_posTracks_in_this_event = emh_pos->GetTracksPerCollision(key_df_collision); - auto selected_negTracks_in_this_event = emh_neg->GetTracksPerCollision(key_df_collision); + const auto& selected_posTracks_in_this_event = emh_pos->GetTracksPerCollision(key_df_collision); + const auto& selected_negTracks_in_this_event = emh_neg->GetTracksPerCollision(key_df_collision); - auto collisionIds_in_mixing_pool = emh_pos->GetCollisionIdsFromEventPool(key_bin); // pos/neg does not matter. + const auto& collisionIds_in_mixing_pool = emh_pos->GetCollisionIdsFromEventPool(key_bin); // pos/neg does not matter. // LOGF(info, "selected_posTracks_in_this_event.size() = %d, selected_negTracks_in_this_event.size() = %d, collisionIds_in_mixing_pool.size() = %d", selected_posTracks_in_this_event.size(), selected_negTracks_in_this_event.size(), collisionIds_in_mixing_pool.size()); @@ -1201,8 +1201,8 @@ struct DileptonHadronMPC { continue; } - auto posTracks_from_event_pool = emh_pos->GetTracksPerCollision(mix_dfId_collisionId); - auto negTracks_from_event_pool = emh_neg->GetTracksPerCollision(mix_dfId_collisionId); + const auto& posTracks_from_event_pool = emh_pos->GetTracksPerCollision(mix_dfId_collisionId); + const auto& negTracks_from_event_pool = emh_neg->GetTracksPerCollision(mix_dfId_collisionId); // LOGF(info, "posTracks_from_event_pool.size() = %d, negTracks_from_event_pool.size() = %d", posTracks_from_event_pool.size(), negTracks_from_event_pool.size()); for (const auto& pos : selected_posTracks_in_this_event) { // ULS mix @@ -1233,8 +1233,8 @@ struct DileptonHadronMPC { } // end of loop over mixed event pool for lepton-lepton if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonHadronAnalysisType::kAzimuthalCorrelation)) { - auto selected_refTracks_in_this_event = emh_ref->GetTracksPerCollision(key_df_collision); - auto collisionIds_in_mixing_pool_hadron = emh_ref->GetCollisionIdsFromEventPool(key_bin); + const auto& selected_refTracks_in_this_event = emh_ref->GetTracksPerCollision(key_df_collision); + const auto& collisionIds_in_mixing_pool_hadron = emh_ref->GetCollisionIdsFromEventPool(key_bin); // for ULS and hadron mix for (const auto& pos : selected_posTracks_in_this_event) { @@ -1253,7 +1253,7 @@ struct DileptonHadronMPC { continue; } - auto refTracks_from_event_pool = emh_ref->GetTracksPerCollision(mix_dfId_collisionId); + const auto& refTracks_from_event_pool = emh_ref->GetTracksPerCollision(mix_dfId_collisionId); for (const auto& ref : refTracks_from_event_pool) { fillDileptonHadron<1>(pos, neg, cut, ref); } @@ -1283,7 +1283,7 @@ struct DileptonHadronMPC { continue; } - auto refTracks_from_event_pool = emh_ref->GetTracksPerCollision(mix_dfId_collisionId); + const auto& refTracks_from_event_pool = emh_ref->GetTracksPerCollision(mix_dfId_collisionId); for (const auto& ref : refTracks_from_event_pool) { fillDileptonHadron<1>(pos1, pos2, cut, ref); } @@ -1311,7 +1311,7 @@ struct DileptonHadronMPC { continue; } - auto refTracks_from_event_pool = emh_ref->GetTracksPerCollision(mix_dfId_collisionId); + const auto& refTracks_from_event_pool = emh_ref->GetTracksPerCollision(mix_dfId_collisionId); for (const auto& ref : refTracks_from_event_pool) { fillDileptonHadron<1>(neg1, neg2, cut, ref); } @@ -1335,7 +1335,7 @@ struct DileptonHadronMPC { continue; } - auto refTracks_from_event_pool = emh_ref->GetTracksPerCollision(mix_dfId_collisionId); + const auto& refTracks_from_event_pool = emh_ref->GetTracksPerCollision(mix_dfId_collisionId); // LOGF(info, "selected_refTracks_in_this_event.size() = %d, collisionIds_in_mixing_pool_hadron.size() = %d, refTracks_from_event_pool.size() = %d", selected_refTracks_in_this_event.size(), collisionIds_in_mixing_pool_hadron.size(), refTracks_from_event_pool.size()); for (const auto& ref1 : selected_refTracks_in_this_event) { // ref-ref mix for (const auto& ref2 : refTracks_from_event_pool) { diff --git a/PWGEM/Dilepton/Core/DileptonSV.h b/PWGEM/Dilepton/Core/DileptonMixingTest.h similarity index 77% rename from PWGEM/Dilepton/Core/DileptonSV.h rename to PWGEM/Dilepton/Core/DileptonMixingTest.h index d59725ef917..692ccdfe82c 100644 --- a/PWGEM/Dilepton/Core/DileptonSV.h +++ b/PWGEM/Dilepton/Core/DileptonMixingTest.h @@ -14,8 +14,8 @@ // This code runs loop over leptons. // Please write to: daiki.sekihata@cern.ch -#ifndef PWGEM_DILEPTON_CORE_DILEPTONSV_H_ -#define PWGEM_DILEPTON_CORE_DILEPTONSV_H_ +#ifndef PWGEM_DILEPTON_CORE_DILEPTONMIXINGTEST_H_ +#define PWGEM_DILEPTON_CORE_DILEPTONMIXINGTEST_H_ #include "PWGEM/Dilepton/Core/DielectronCut.h" #include "PWGEM/Dilepton/Core/DimuonCut.h" @@ -32,8 +32,6 @@ #include "Common/CCDB/RCTSelectionFlags.h" #include "Common/Core/RecoDecay.h" #include "Common/Core/Zorro.h" -#include "Common/Core/fwdtrackUtilities.h" -#include "Common/Core/trackUtilities.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/PIDResponseTPC.h" @@ -43,13 +41,9 @@ #include #include #include -#include -#include #include #include #include -#include -#include #include #include #include @@ -70,7 +64,9 @@ #include #include #include +#include #include +#include #include #include @@ -84,7 +80,6 @@ #include #include #include -#include #include #include #include @@ -94,23 +89,18 @@ #include -using MyCollisions = o2::soa::Join; +using MyCollisions = o2::soa::Join; using MyCollision = MyCollisions::iterator; -using MyCollisionsSWT = o2::soa::Join; +using MyCollisionsSWT = o2::soa::Join; using MyCollisionSWT = MyCollisionsSWT::iterator; -using MyElectrons = o2::soa::Join; +using MyElectrons = o2::soa::Join; using MyElectron = MyElectrons::iterator; using FilteredMyElectrons = o2::soa::Filtered; using FilteredMyElectron = FilteredMyElectrons::iterator; -using MyElectronsSCT = o2::soa::Join; -using MyElectronSCT = MyElectronsSCT::iterator; -using FilteredMyElectronsSCT = o2::soa::Filtered; -using FilteredMyElectronSCT = FilteredMyElectronsSCT::iterator; - -using MyMuons = o2::soa::Join; +using MyMuons = o2::soa::Join; using MyMuon = MyMuons::iterator; using FilteredMyMuons = o2::soa::Filtered; using FilteredMyMuon = FilteredMyMuons::iterator; @@ -118,34 +108,42 @@ using FilteredMyMuon = FilteredMyMuons::iterator; using MyEMH_electron = o2::aod::pwgem::dilepton::utils::EventMixingHandler, std::pair, o2::aod::pwgem::dilepton::utils::EMTrack>; using MyEMH_muon = o2::aod::pwgem::dilepton::utils::EventMixingHandler, std::pair, o2::aod::pwgem::dilepton::utils::EMFwdTrack>; -template -struct DileptonSV { +template +struct Dilepton { + // Configurables o2::framework::Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; o2::framework::Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; o2::framework::Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - o2::framework::Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; - o2::framework::Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; - o2::framework::Configurable matCorrType{"matCorrType", 1, "0: none, 1: TGeo, 2: LUT, else: none"}; - + o2::framework::Configurable skipGRPOquery{"skipGRPOquery", true, "skip grpo query"}; + o2::framework::Configurable d_bz_input{"d_bz_input", -999, "bz field in kG, -999 is automatic"}; o2::framework::Configurable cfgApplySPresolution{"cfgApplySPresolution", false, "flag to apply resolution correction for flow analysis"}; o2::framework::Configurable spresoPath{"spresoPath", "Users/d/dsekihat/PWGEM/dilepton/Qvector/resolution/LHC23zzh/pass3/test", "Path to SP resolution file"}; o2::framework::Configurable spresoHistName{"spresoHistName", "h1_R2_FT0M_BPos_BNeg", "histogram name of SP resolution file"}; - o2::framework::Configurable cfgAnalysisType{"cfgAnalysisType", static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kQC), "kQC:0, kUPC:1, kFlowV2SP:2, kFlowV3SP:3, kPolarization:4, kHFll:5, kBootstrapv2:6, kFlowV2EP:7"}; + o2::framework::Configurable cfgAnalysisType{"cfgAnalysisType", static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kQC), "kQC:0, kUPC:1, kFlowV2SP:2, kFlowV3SP:3, kPolarization:4, kHFll:5, kBootstrapv2:6, kFlowV2EP:7, kFlowV3EP:8"}; + o2::framework::Configurable cfgEP2Estimator_for_Mix{"cfgEP2Estimator_for_Mix", 3, "FT0M:0, FT0A:1, FT0C:2, BTot:3, BPos:4, BNeg:5, FV0A:6"}; o2::framework::Configurable cfgQvecEstimator{"cfgQvecEstimator", 2, "FT0M:0, FT0A:1, FT0C:2, BTot:3, BPos:4, BNeg:5, FV0A:6"}; o2::framework::Configurable cfgCentEstimator{"cfgCentEstimator", 2, "FT0M:0, FT0A:1, FT0C:2"}; - + o2::framework::Configurable cfgOccupancyEstimator{"cfgOccupancyEstimator", 0, "FT0C:0, Track:1"}; + o2::framework::Configurable cfgDoMix{"cfgDoMix", true, "flag for event mixing"}; + o2::framework::Configurable ndepth{"ndepth", 1000, "depth for event mixing"}; + o2::framework::Configurable ndiff_bc_mix{"ndiff_bc_mix", 594, "difference in global BC required in mixed events"}; + o2::framework::ConfigurableAxis ConfVtxBins{"ConfVtxBins", {o2::framework::VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; + o2::framework::ConfigurableAxis ConfCentBins{"ConfCentBins", {o2::framework::VARIABLE_WIDTH, 0.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.f, 999.f}, "Mixing bins - centrality"}; + o2::framework::ConfigurableAxis ConfEPBins{"ConfEPBins", {16, -M_PI / 2, +M_PI / 2}, "Mixing bins - event plane angle"}; + o2::framework::ConfigurableAxis ConfOccupancyBins{"ConfOccupancyBins", {o2::framework::VARIABLE_WIDTH, -1, 1e+10}, "Mixing bins - occupancy"}; o2::framework::Configurable cfgApplyWeightTTCA{"cfgApplyWeightTTCA", false, "flag to apply weighting by 1/N"}; + o2::framework::Configurable cfgDCAType{"cfgDCAType", 0, "type of DCA for output. 0:3D, 1:XY, 2:Z, else:3D"}; + o2::framework::Configurable cfgUseSignedDCA{"cfgUseSignedDCA", false, "flag to use signs in the DCA calculation"}; o2::framework::Configurable cfgPolarizationFrame{"cfgPolarizationFrame", 0, "frame of polarization. 0:CS, 1:HX, else:FATAL"}; o2::framework::ConfigurableAxis ConfMllBins{"ConfMllBins", {o2::framework::VARIABLE_WIDTH, 0.00, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.20, 0.21, 0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.30, 0.31, 0.32, 0.33, 0.34, 0.35, 0.36, 0.37, 0.38, 0.39, 0.40, 0.41, 0.42, 0.43, 0.44, 0.45, 0.46, 0.47, 0.48, 0.49, 0.50, 0.51, 0.52, 0.53, 0.54, 0.55, 0.56, 0.57, 0.58, 0.59, 0.60, 0.61, 0.62, 0.63, 0.64, 0.65, 0.66, 0.67, 0.68, 0.69, 0.70, 0.71, 0.72, 0.73, 0.74, 0.75, 0.76, 0.77, 0.78, 0.79, 0.80, 0.81, 0.82, 0.83, 0.84, 0.85, 0.86, 0.87, 0.88, 0.89, 0.90, 0.91, 0.92, 0.93, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99, 1.00, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.09, 1.10, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18, 1.19, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.10, 2.20, 2.30, 2.40, 2.50, 2.60, 2.70, 2.75, 2.80, 2.85, 2.90, 2.95, 3.00, 3.05, 3.10, 3.15, 3.20, 3.25, 3.30, 3.35, 3.40, 3.45, 3.50, 3.55, 3.60, 3.65, 3.70, 3.75, 3.80, 3.85, 3.90, 3.95, 4.00}, "mll bins for output histograms"}; o2::framework::ConfigurableAxis ConfPtllBins{"ConfPtllBins", {o2::framework::VARIABLE_WIDTH, 0.00, 0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90, 1.00, 1.10, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.50, 3.00, 3.50, 4.00, 4.50, 5.00, 6.00, 7.00, 8.00, 9.00, 10.00}, "pTll bins for output histograms"}; o2::framework::ConfigurableAxis ConfDCAllBins{"ConfDCAllBins", {o2::framework::VARIABLE_WIDTH, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0}, "DCAll bins for output histograms"}; o2::framework::ConfigurableAxis ConfYllBins{"ConfYllBins", {1, -1.f, 1.f}, "yll bins for output histograms"}; // pair rapidity - o2::framework::ConfigurableAxis ConfLog10Chi2PCABins{"ConfLog10Chi2PCABins", {1, -12.f, 0.f}, "log10 of chi2PCA bins for output histograms"}; - o2::framework::ConfigurableAxis ConfDLBins{"ConfDLBins", {1, 0.f, 10.f}, "decay length bins for output histograms"}; - o2::framework::ConfigurableAxis ConfCPABins{"ConfCPABins", {o2::framework::VARIABLE_WIDTH, -1, -0.95, -0.9, -0.85, -0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.85, 0.9, 0.95, 1}, "cpa bins for output histograms"}; + + // o2::framework::ConfigurableAxis ConfMmumuBins{"ConfMmumuBins", {o2::framework::VARIABLE_WIDTH, 0.20, 0.21, 0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.30, 0.31, 0.32, 0.33, 0.34, 0.35, 0.36, 0.37, 0.38, 0.39, 0.40, 0.41, 0.42, 0.43, 0.44, 0.45, 0.46, 0.47, 0.48, 0.49, 0.50, 0.51, 0.52, 0.53, 0.54, 0.55, 0.56, 0.57, 0.58, 0.59, 0.60, 0.61, 0.62, 0.63, 0.64, 0.65, 0.66, 0.67, 0.68, 0.69, 0.70, 0.71, 0.72, 0.73, 0.74, 0.75, 0.76, 0.77, 0.78, 0.79, 0.80, 0.81, 0.82, 0.83, 0.84, 0.85, 0.86, 0.87, 0.88, 0.89, 0.90, 0.91, 0.92, 0.93, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99, 1.00, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.09, 1.10, 1.11,1.12,1.13,1.14,1.15,1.16,1.17,1.18,1.19, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.10, 2.20, 2.30, 2.40, 2.50, 2.60, 2.70, 2.75, 2.80, 2.85, 2.90, 2.95, 3.00, 3.05, 3.10, 3.15, 3.20, 3.25, 3.30, 3.35, 3.40, 3.45, 3.50, 3.55, 3.60, 3.65, 3.70, 3.75, 3.80, 3.85, 3.90, 3.95, 4.00, 4.10, 4.20, 4.30, 4.40, 4.50, 4.60, 4.70, 4.80, 4.90, 5.00, 5.10, 5.20, 5.30, 5.40, 5.50, 5.60, 5.70, 5.80, 5.90, 6.00, 6.10, 6.20, 6.30, 6.40, 6.50, 6.60, 6.70, 6.80, 6.90, 7.00, 7.10, 7.20, 7.30, 7.40, 7.50, 7.60, 7.70, 7.80, 7.90, 8.00, 8.10, 8.20, 8.30, 8.40, 8.50, 8.60, 8.70, 8.80, 8.90, 9.00, 9.10, 9.20, 9.30, 9.40, 9.50, 9.60, 9.70, 9.80, 9.90, 10.00, 10.10, 10.20, 10.30, 10.40, 10.50, 10.60, 10.70, 10.80, 10.90, 11.00, 11.50, 12.00}, "mmumu bins for output histograms"}; // for dimuon. one can copy bins here to hyperloop page. o2::framework::ConfigurableAxis ConfSPBins{"ConfSPBins", {200, -5, 5}, "SP bins for flow analysis"}; o2::framework::ConfigurableAxis ConfPolarizationCosThetaBins{"ConfPolarizationCosThetaBins", {20, -1.f, 1.f}, "cos(theta) bins for polarization analysis"}; @@ -154,27 +152,14 @@ struct DileptonSV { o2::framework::Configurable cfgNumBootstrapSamples{"cfgNumBootstrapSamples", 1, "Number of Bootstrap Samples"}; - struct : o2::framework::ConfigurableGroup { - std::string prefix = "mixingGroup"; - o2::framework::Configurable cfgOccupancyEstimator{"cfgOccupancyEstimator", 0, "FT0C:0, Track:1"}; - o2::framework::Configurable cfgEP2Estimator{"cfgEP2Estimator", 3, "FT0M:0, FT0A:1, FT0C:2, BTot:3, BPos:4, BNeg:5, FV0A:6"}; - o2::framework::Configurable cfgDoMix{"cfgDoMix", true, "flag for event mixing"}; - o2::framework::Configurable ndepth{"ndepth", 1000, "depth for event mixing"}; - o2::framework::Configurable ndiff_bc_mix{"ndiff_bc_mix", 594, "difference in global BC required in mixed events"}; - o2::framework::ConfigurableAxis ConfVtxBins{"ConfVtxBins", {20, -10.0f, 10.f}, "Mixing bins - z-vertex"}; - o2::framework::ConfigurableAxis ConfCentBins{"ConfCentBins", {o2::framework::VARIABLE_WIDTH, 0.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.f, 110.f}, "Mixing bins - centrality"}; - o2::framework::ConfigurableAxis ConfEPBins{"ConfEPBins", {1, -M_PI / 2, +M_PI / 2}, "Mixing bins - event plane angle"}; - o2::framework::ConfigurableAxis ConfOccupancyBins{"ConfOccupancyBins", {o2::framework::VARIABLE_WIDTH, -2, 1e+10}, "Mixing bins - occupancy"}; - } mixingGroup; - EMEventCut fEMEventCut; struct : o2::framework::ConfigurableGroup { std::string prefix = "eventcut_group"; o2::framework::Configurable cfgZvtxMin{"cfgZvtxMin", -10.f, "min. Zvtx"}; o2::framework::Configurable cfgZvtxMax{"cfgZvtxMax", +10.f, "max. Zvtx"}; - o2::framework::Configurable cfgRequireSel8{"cfgRequireSel8", false, "require sel8 in event cut"}; + o2::framework::Configurable cfgRequireSel8{"cfgRequireSel8", true, "require sel8 in event cut"}; o2::framework::Configurable cfgRequireFT0AND{"cfgRequireFT0AND", true, "require FT0AND in event cut"}; - o2::framework::Configurable cfgRequireNoTFB{"cfgRequireNoTFB", true, "require No time frame border in event cut"}; + o2::framework::Configurable cfgRequireNoTFB{"cfgRequireNoTFB", false, "require No time frame border in event cut"}; o2::framework::Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", false, "require no ITS readout frame border in event cut"}; o2::framework::Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", false, "require no same bunch pileup in event cut"}; o2::framework::Configurable cfgRequireVertexITSTPC{"cfgRequireVertexITSTPC", false, "require Vertex ITSTPC in event cut"}; // ITS-TPC matched track contributes PV. @@ -215,7 +200,7 @@ struct DileptonSV { o2::framework::Configurable cfg_max_pair_y{"cfg_max_pair_y", +0.8, "max pair rapidity"}; o2::framework::Configurable cfg_min_pair_dca3d{"cfg_min_pair_dca3d", 0.0, "min pair dca3d in sigma"}; o2::framework::Configurable cfg_max_pair_dca3d{"cfg_max_pair_dca3d", 1e+10, "max pair dca3d in sigma"}; - o2::framework::Configurable cfg_apply_phiv{"cfg_apply_phiv", false, "flag to apply phiv cut"}; + o2::framework::Configurable cfg_apply_phiv{"cfg_apply_phiv", true, "flag to apply phiv cut"}; o2::framework::Configurable cfg_phiv_slope{"cfg_phiv_slope", 0.0185, "slope for m vs. phiv"}; o2::framework::Configurable cfg_phiv_intercept{"cfg_phiv_intercept", -0.0280, "intercept for m vs. phiv"}; o2::framework::Configurable cfg_min_phiv{"cfg_min_phiv", 0.0, "min phiv (constant)"}; @@ -233,7 +218,7 @@ struct DileptonSV { o2::framework::Configurable cfg_apply_cuts_from_prefilter_derived{"cfg_apply_cuts_from_prefilter_derived", false, "flag to apply prefilter set in derived data"}; o2::framework::Configurable cfg_prefilter_bits_derived{"cfg_prefilter_bits_derived", 0, "prefilter bits [kNone : 0, kMee : 1, kPhiV : 2, kSplitOrMergedTrackLS : 4, kSplitOrMergedTrackULS : 8] Please consider logical-OR among them."}; // see PairUtilities.h - o2::framework::Configurable cfg_min_pt_track{"cfg_min_pt_track", 0.8, "min pT for single track"}; + o2::framework::Configurable cfg_min_pt_track{"cfg_min_pt_track", 0.2, "min pT for single track"}; o2::framework::Configurable cfg_max_pt_track{"cfg_max_pt_track", 1e+10, "max pT for single track"}; o2::framework::Configurable cfg_min_eta_track{"cfg_min_eta_track", -0.8, "min eta for single track"}; o2::framework::Configurable cfg_max_eta_track{"cfg_max_eta_track", +0.8, "max eta for single track"}; @@ -260,7 +245,6 @@ struct DileptonSV { o2::framework::Configurable cfgRefR{"cfgRefR", 0.50, "ref. radius (m) for calculating phi position"}; // 0.50 +/- 0.06 can be syst. unc. o2::framework::Configurable cfg_min_phiposition_track{"cfg_min_phiposition_track", 0.f, "min phi position for single track at certain radius"}; o2::framework::Configurable cfg_max_phiposition_track{"cfg_max_phiposition_track", 6.3, "max phi position for single track at certain radius"}; - o2::framework::Configurable cfgDCAType{"cfgDCAType", 0, "type of DCA for output. 0:3D, 1:XY, 2:Z, else:3D"}; o2::framework::Configurable cfg_pid_scheme{"cfg_pid_scheme", static_cast(DielectronCut::PIDSchemes::kTPChadrejORTOFreq), "pid scheme [kTOFreq : 0, kTPChadrej : 1, kTPChadrejORTOFreq : 2, kTPConly : 3, kTOFif : 4, kPIDML : 5, kTPChadrejORTOFreq_woTOFif : 6]"}; o2::framework::Configurable cfg_min_TPCNsigmaEl{"cfg_min_TPCNsigmaEl", -2.0, "min. TPC n sigma for electron inclusion"}; @@ -282,18 +266,13 @@ struct DileptonSV { // configuration for PID ML // o2::framework::Configurable> onnxFileNames{"onnxFileNames", std::vector{"filename"}, "ONNX file names for each bin (if not from CCDB full path)"}; // o2::framework::Configurable> onnxPathsCCDB{"onnxPathsCCDB", std::vector{"path"}, "Paths of models on CCDB"}; - o2::framework::Configurable> binsMLPID{"binsMLPID", std::vector{0.1, 0.15, 0.2, 0.25, 0.4, 0.8, 1.6, 2.0, 4.0, 20.f}, "Bin limits for ML application"}; - o2::framework::Configurable> cutsMLPID{"cutsMLPID", std::vector{0.97, 0.97, 0.97, 0.8, 0.95, 0.95, 0.8, 0.8, 0.8}, "ML cuts per bin"}; + o2::framework::Configurable> binsMLPID{"binsMLPID", std::vector{0.1, 0.15, 0.2, 0.25, 0.4, 0.8, 1.6, 2.0, 20.f}, "Bin limits for ML application"}; + o2::framework::Configurable> cutsMLPID{"cutsMLPID", std::vector{0.97, 0.97, 0.97, 0.8, 0.95, 0.95, 0.8, 0.8}, "ML cuts per bin"}; // o2::framework::Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature"}, "Names of ML model input features"}; // o2::framework::Configurable nameBinningFeature{"nameBinningFeature", "pt", "Names of ML model binning feature"}; // o2::framework::Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB. Exceptions: > 0 for the specific timestamp, 0 gets the run dependent timestamp"}; // o2::framework::Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; // o2::framework::Configurable enableOptimizations{"enableOptimizations", false, "Enables the ONNX extended model-optimization: sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED)"}; - - o2::framework::Configurable> binsMLSCT{"binsMLSCT", std::vector{0.1, 0.4, 0.6, 0.8, 1, 2, 4, 20.f}, "Bin limits for ML application"}; - o2::framework::Configurable> cutsMLSCTeT_prompt_hc{"cutsMLSCTeT_prompt_hc", std::vector{0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8}, "ML cuts per bin for prompt hc"}; - o2::framework::Configurable> cutsMLSCTeT_nonprompt_hc{"cutsMLSCTeT_nonprompt_hc", std::vector{0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8}, "ML cuts per bin for nonprompt hc"}; - o2::framework::Configurable> cutsMLSCTeT_hb{"cutsMLSCTeT_hb", std::vector{0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8}, "ML cuts per bin for hb"}; } dielectroncuts; DimuonCut fDimuonCut; @@ -312,7 +291,7 @@ struct DileptonSV { o2::framework::Configurable cfg_min_dphi{"cfg_min_dphi", 0.02, "min dphi between 2 muons (elliptic cut)"}; o2::framework::Configurable cfg_track_type{"cfg_track_type", 3, "muon track type [0: MFT-MCH-MID, 3: MCH-MID]"}; - o2::framework::Configurable cfg_min_pt_track{"cfg_min_pt_track", 0.8, "min pT for single track"}; + o2::framework::Configurable cfg_min_pt_track{"cfg_min_pt_track", 0.2, "min pT for single track"}; o2::framework::Configurable cfg_max_pt_track{"cfg_max_pt_track", 1e+10, "max pT for single track"}; o2::framework::Configurable cfg_min_eta_track{"cfg_min_eta_track", -4.0, "min eta for single track"}; o2::framework::Configurable cfg_max_eta_track{"cfg_max_eta_track", -2.5, "max eta for single track"}; @@ -346,39 +325,23 @@ struct DileptonSV { o2::framework::Configurable bcMarginForSoftwareTrigger{"bcMarginForSoftwareTrigger", 100, "Number of BCs of margin for software triggers"}; } zorroGroup; + struct : o2::framework::ConfigurableGroup { + std::string prefix = "flowcorrection_group"; + o2::framework::Configurable cfgFillNUAHist{"cfgFillNUAHist", true, "flag to fill the NUA correction hist"}; + o2::framework::Configurable cfgApplyWeightNUA{"cfgApplyWeightNUA", false, "flag to apply q-vector Non-uniform acceptance weighting"}; + o2::framework::Configurable nuaPath{"nuaPath", "Users/o/omassen/Dielectron/NUAWeights/LHC23_PbPb_pass5", "Path to NUA-weights file"}; + o2::framework::ConfigurableAxis ConfNUAPhiBins{"ConfNUAPhiBins", {60, -M_PI, M_PI}, "NUA histogram bins - phi-angle"}; + o2::framework::ConfigurableAxis ConfNUAEtaBins{"ConfNUAEtaBins", {40, -1., +1.}, "NUA histogram bins - eta"}; + o2::framework::ConfigurableAxis ConfNUAZVtxBins{"ConfNUAZVtxBins", {40, -10., 10.}, "NUA histogram bins - z-vtx"}; + } flowcorrectionGroup; + Zorro zorro; int mToIidx = 0; int mTOICounter = 0; int mATCounter = 0; - struct : o2::framework::ConfigurableGroup { - std::string prefix = "dfGroup"; - o2::framework::Configurable useAbsDCA{"useAbsDCA", true, "Minimise abs. distance rather than chi2"}; - o2::framework::Configurable useWeightedFinalPCA{"useWeightedFinalPCA", false, "Recalculate vertex position using track covariances, effective only if useAbsDCA is true"}; - o2::framework::Configurable maxR{"maxR", 20.f, "reject PCA's above this radius"}; - o2::framework::Configurable maxDZIni{"maxDZIni", 4.f, "reject (if > 0) PCA candidate if tracks DZ exceeds threshold"}; - o2::framework::Configurable minParamChange{"minParamChange", 1e-3, "stop iterations if largest change of any X is smaller than this"}; - o2::framework::Configurable minRelChi2Change{"minRelChi2Change", 0.9, "stop iterations is chi2/chi2old > this"}; - o2::framework::Configurable propagateToPCA{"propagateToPCA", true, "create tracks version propagated to PCA"}; - o2::framework::Configurable maxLog10Chi2PCA{"maxLog10Chi2PCA", 10, "max. log10(chi2PCA) for dilepton pair"}; - } dfGroup; // for DCAFitterN - - struct : o2::framework::ConfigurableGroup { - std::string prefix = "fdfGroup"; - o2::framework::Configurable useAbsDCA{"useAbsDCA", true, "Minimise abs. distance rather than chi2"}; - o2::framework::Configurable useWeightedFinalPCA{"useWeightedFinalPCA", false, "Recalculate vertex position using track covariances, effective only if useAbsDCA is true"}; - o2::framework::Configurable maxR{"maxR", 20.f, "reject PCA's above this radius"}; - o2::framework::Configurable maxDXIni{"maxDXIni", 4.f, "reject (if > 0) PCA candidate if tracks DZ exceeds threshold"}; - o2::framework::Configurable minParamChange{"minParamChange", 1e-3, "stop iterations if largest change of any X is smaller than this"}; - o2::framework::Configurable minRelChi2Change{"minRelChi2Change", 0.9, "stop iterations is chi2/chi2old > this"}; - o2::framework::Configurable propagateToPCA{"propagateToPCA", true, "create tracks version propagated to PCA"}; - o2::framework::Configurable maxLog10Chi2PCA{"maxLog10Chi2PCA", 10, "max. log10(chi2PCA) for dilepton pair"}; - } fdfGroup; // for FwdDCAFitterN - - o2::vertexing::DCAFitterN<2> mDCAFitter; - o2::vertexing::FwdDCAFitterN<2> mFwdDCAFitter; - o2::aod::rctsel::RCTFlagsChecker rctChecker; + // o2::ccdb::CcdbApi ccdbApi; o2::framework::Service ccdb; int mRunNumber{0}; float d_bz{0}; @@ -404,6 +367,7 @@ struct DileptonSV { float beamP1 = 0.f; // beam momentum float beamP2 = 0.f; // beam momentum TH2D* h2sp_resolution = nullptr; + std::vector h3nua_weights; void init(o2::framework::InitContext& /*context*/) { @@ -419,77 +383,78 @@ struct DileptonSV { ccdb->setFatalWhenNull(false); rctChecker.init(eventcuts.cfgRCTLabel.value, eventcuts.cfgCheckZDC.value, eventcuts.cfgTreatLimitedAcceptanceAsBad.value); - if (mixingGroup.ConfVtxBins.value[0] == o2::framework::VARIABLE_WIDTH) { - zvtx_bin_edges = std::vector(mixingGroup.ConfVtxBins.value.begin(), mixingGroup.ConfVtxBins.value.end()); + if (ConfVtxBins.value[0] == o2::framework::VARIABLE_WIDTH) { + zvtx_bin_edges = std::vector(ConfVtxBins.value.begin(), ConfVtxBins.value.end()); zvtx_bin_edges.erase(zvtx_bin_edges.begin()); - // for (const auto& edge : zvtx_bin_edges) { - // LOGF(info, "o2::framework::VARIABLE_WIDTH: zvtx_bin_edges = %f", edge); - // } + for (const auto& edge : zvtx_bin_edges) { + LOGF(info, "o2::framework::VARIABLE_WIDTH: zvtx_bin_edges = %f", edge); + } } else { - int nbins = static_cast(mixingGroup.ConfVtxBins.value[0]); - float xmin = static_cast(mixingGroup.ConfVtxBins.value[1]); - float xmax = static_cast(mixingGroup.ConfVtxBins.value[2]); + int nbins = static_cast(ConfVtxBins.value[0]); + float xmin = static_cast(ConfVtxBins.value[1]); + float xmax = static_cast(ConfVtxBins.value[2]); zvtx_bin_edges.resize(nbins + 1); for (int i = 0; i < nbins + 1; i++) { zvtx_bin_edges[i] = (xmax - xmin) / (nbins)*i + xmin; - // LOGF(info, "FIXED_WIDTH: zvtx_bin_edges[%d] = %f", i, zvtx_bin_edges[i]); + LOGF(info, "FIXED_WIDTH: zvtx_bin_edges[%d] = %f", i, zvtx_bin_edges[i]); } } - if (mixingGroup.ConfCentBins.value[0] == o2::framework::VARIABLE_WIDTH) { - cent_bin_edges = std::vector(mixingGroup.ConfCentBins.value.begin(), mixingGroup.ConfCentBins.value.end()); + if (ConfCentBins.value[0] == o2::framework::VARIABLE_WIDTH) { + cent_bin_edges = std::vector(ConfCentBins.value.begin(), ConfCentBins.value.end()); cent_bin_edges.erase(cent_bin_edges.begin()); - // for (const auto& edge : cent_bin_edges) { - // LOGF(info, "o2::framework::VARIABLE_WIDTH: cent_bin_edges = %f", edge); - // } + for (const auto& edge : cent_bin_edges) { + LOGF(info, "o2::framework::VARIABLE_WIDTH: cent_bin_edges = %f", edge); + } } else { - int nbins = static_cast(mixingGroup.ConfCentBins.value[0]); - float xmin = static_cast(mixingGroup.ConfCentBins.value[1]); - float xmax = static_cast(mixingGroup.ConfCentBins.value[2]); + int nbins = static_cast(ConfCentBins.value[0]); + float xmin = static_cast(ConfCentBins.value[1]); + float xmax = static_cast(ConfCentBins.value[2]); cent_bin_edges.resize(nbins + 1); for (int i = 0; i < nbins + 1; i++) { cent_bin_edges[i] = (xmax - xmin) / (nbins)*i + xmin; - // LOGF(info, "FIXED_WIDTH: cent_bin_edges[%d] = %f", i, cent_bin_edges[i]); + LOGF(info, "FIXED_WIDTH: cent_bin_edges[%d] = %f", i, cent_bin_edges[i]); } } - if (mixingGroup.ConfEPBins.value[0] == o2::framework::VARIABLE_WIDTH) { - ep_bin_edges = std::vector(mixingGroup.ConfEPBins.value.begin(), mixingGroup.ConfEPBins.value.end()); + if (ConfEPBins.value[0] == o2::framework::VARIABLE_WIDTH) { + ep_bin_edges = std::vector(ConfEPBins.value.begin(), ConfEPBins.value.end()); ep_bin_edges.erase(ep_bin_edges.begin()); - // for (const auto& edge : ep_bin_edges) { - // LOGF(info, "o2::framework::VARIABLE_WIDTH: ep_bin_edges = %f", edge); - // } + for (const auto& edge : ep_bin_edges) { + LOGF(info, "o2::framework::VARIABLE_WIDTH: ep_bin_edges = %f", edge); + } } else { - int nbins = static_cast(mixingGroup.ConfEPBins.value[0]); - float xmin = static_cast(mixingGroup.ConfEPBins.value[1]); - float xmax = static_cast(mixingGroup.ConfEPBins.value[2]); + int nbins = static_cast(ConfEPBins.value[0]); + float xmin = static_cast(ConfEPBins.value[1]); + float xmax = static_cast(ConfEPBins.value[2]); ep_bin_edges.resize(nbins + 1); for (int i = 0; i < nbins + 1; i++) { ep_bin_edges[i] = (xmax - xmin) / (nbins)*i + xmin; - // LOGF(info, "FIXED_WIDTH: ep_bin_edges[%d] = %f", i, ep_bin_edges[i]); + LOGF(info, "FIXED_WIDTH: ep_bin_edges[%d] = %f", i, ep_bin_edges[i]); } } - // LOGF(info, "mixingGroup.cfgOccupancyEstimator = %d", mixingGroup.cfgOccupancyEstimator.value); - if (mixingGroup.ConfOccupancyBins.value[0] == o2::framework::VARIABLE_WIDTH) { - occ_bin_edges = std::vector(mixingGroup.ConfOccupancyBins.value.begin(), mixingGroup.ConfOccupancyBins.value.end()); + LOGF(info, "cfgOccupancyEstimator = %d", cfgOccupancyEstimator.value); + if (ConfOccupancyBins.value[0] == o2::framework::VARIABLE_WIDTH) { + occ_bin_edges = std::vector(ConfOccupancyBins.value.begin(), ConfOccupancyBins.value.end()); occ_bin_edges.erase(occ_bin_edges.begin()); - // for (const auto& edge : occ_bin_edges) { - // LOGF(info, "o2::framework::VARIABLE_WIDTH: occ_bin_edges = %f", edge); - // } + for (const auto& edge : occ_bin_edges) { + LOGF(info, "o2::framework::VARIABLE_WIDTH: occ_bin_edges = %f", edge); + } } else { - int nbins = static_cast(mixingGroup.ConfOccupancyBins.value[0]); - float xmin = static_cast(mixingGroup.ConfOccupancyBins.value[1]); - float xmax = static_cast(mixingGroup.ConfOccupancyBins.value[2]); + int nbins = static_cast(ConfOccupancyBins.value[0]); + float xmin = static_cast(ConfOccupancyBins.value[1]); + float xmax = static_cast(ConfOccupancyBins.value[2]); occ_bin_edges.resize(nbins + 1); for (int i = 0; i < nbins + 1; i++) { occ_bin_edges[i] = (xmax - xmin) / (nbins)*i + xmin; - // LOGF(info, "FIXED_WIDTH: occ_bin_edges[%d] = %f", i, occ_bin_edges[i]); + LOGF(info, "FIXED_WIDTH: occ_bin_edges[%d] = %f", i, occ_bin_edges[i]); } } - emh_pos = new TEMH(mixingGroup.ndepth); - emh_neg = new TEMH(mixingGroup.ndepth); + emh_pos = new TEMH(ndepth); + emh_neg = new TEMH(ndepth); + maxBufferedEvents = static_cast(ndepth.value) * (zvtx_bin_edges.size() - 1) * (cent_bin_edges.size() - 1) * (ep_bin_edges.size() - 1) * (occ_bin_edges.size() - 1); DefineEMEventCut(); addhistograms(); @@ -503,12 +468,10 @@ struct DileptonSV { leptonM2 = o2::constants::physics::MassMuon; } - fRegistry.add("Event/hDiffBC", "diff. global BC in mixed event;|BC_{current} - BC_{mixed}|", o2::framework::HistType::kTH1D, {{10001, -0.5, 10000.5}}, false); - const o2::framework::AxisSpec axis_mix_zvtx{mixingGroup.ConfVtxBins, "Z_{vtx} (cm)"}; - const o2::framework::AxisSpec axis_mix_cent{mixingGroup.ConfCentBins, "centrality (%)"}; - const o2::framework::AxisSpec axis_mix_ep2{mixingGroup.ConfEPBins, "event plane of 2nd harmonics (rad.)"}; - const o2::framework::AxisSpec axis_mix_occ{mixingGroup.ConfOccupancyBins, "occupancy"}; - fRegistry.add("Event/hsMixCounter", "mixed event counter", o2::framework::HistType::kTHnSparseD, {axis_mix_zvtx, axis_mix_cent, axis_mix_ep2, axis_mix_occ}, false); + fRegistry.add("Pair/mix/hDiffBC", "diff. global BC in mixed event;|BC_{current} - BC_{mixed}|", o2::framework::HistType::kTH1D, {{10001, -0.5, 10000.5}}, true); + + fRegistry.add("Pair/mix/hEventBufferFillLevel", "Event buffer fill level;Number of processed events;Buffered events / maximum buffered events", o2::framework::HistType::kTProfile, {{1500, 0.5, 15000000.5}}, false); + fRegistry.get(HIST("Pair/mix/hEventBufferFillLevel"))->SetCanExtend(TH1::kXaxis); if (doprocessTriggerAnalysis) { LOGF(info, "Trigger analysis is enabled. Desired trigger name = %s", zorroGroup.cfg_swt_name.value.data()); @@ -532,53 +495,6 @@ struct DileptonSV { hTVXCounter->GetXaxis()->SetBinLabel(5, "TVX && NoTFB && NoITSROFB"); hTVXCounter->GetXaxis()->SetBinLabel(6, "TVX && NoTFB && NoITSROFB && GoodRCT"); } - - mDCAFitter.setPropagateToPCA(dfGroup.propagateToPCA); - mDCAFitter.setMaxR(dfGroup.maxR); - mDCAFitter.setMaxDZIni(dfGroup.maxDZIni); - mDCAFitter.setMinParamChange(dfGroup.minParamChange); - mDCAFitter.setMinRelChi2Change(dfGroup.minRelChi2Change); - mDCAFitter.setUseAbsDCA(dfGroup.useAbsDCA); - mDCAFitter.setWeightedFinalPCA(dfGroup.useWeightedFinalPCA); - mDCAFitter.setMaxChi2(std::pow(10, dfGroup.maxLog10Chi2PCA)); - - mFwdDCAFitter.setPropagateToPCA(fdfGroup.propagateToPCA); - mFwdDCAFitter.setMaxR(fdfGroup.maxR); - mFwdDCAFitter.setMaxDXIni(fdfGroup.maxDXIni); - mFwdDCAFitter.setMinParamChange(fdfGroup.minParamChange); - mFwdDCAFitter.setMinRelChi2Change(fdfGroup.minRelChi2Change); - mFwdDCAFitter.setUseAbsDCA(fdfGroup.useAbsDCA); - mFwdDCAFitter.setMaxChi2(std::pow(10, fdfGroup.maxLog10Chi2PCA)); - - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - switch (matCorrType.value) { - case static_cast(o2::aod::pwgem::dilepton::utils::pairutil::MatCorrType::kNone): - mFwdDCAFitter.setTGeoMat(false); - break; - case static_cast(o2::aod::pwgem::dilepton::utils::pairutil::MatCorrType::kTGeo): - LOGF(info, "TGeo correction requested, loading geometry"); - if (!o2::base::GeometryManager::isGeometryLoaded()) { - ccdb->get(geoPath); - } - // matCorr = o2::base::Propagator::MatCorrType::USEMatCorrTGeo; - mFwdDCAFitter.setTGeoMat(true); - break; - case static_cast(o2::aod::pwgem::dilepton::utils::pairutil::MatCorrType::kLUT): { - LOGF(info, "LUT correction requested, loading LUT"); - o2::base::MatLayerCylSet* lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->get(lutPath)); - // matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; - mFwdDCAFitter.setTGeoMat(false); - mFwdDCAFitter.setMatLUT(lut); - break; - } - default: - LOGF(info, "no correction requested, loading LUT by default!"); - // lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->get(lutPath)); - // matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; - mFwdDCAFitter.setTGeoMat(false); - break; - } - } } template @@ -588,9 +504,23 @@ struct DileptonSV { return; } + // In case override, don't proceed, please - no CCDB access required + if (d_bz_input > -990) { + d_bz = d_bz_input; + o2::parameters::GRPMagField grpmag; + if (std::fabs(d_bz) > 1e-5) { + grpmag.setL3Current(30000.f / (d_bz / 5.0f)); + } + o2::base::Propagator::initFieldFromGRP(&grpmag); + mRunNumber = collision.runNumber(); + return; + } + auto run3grp_timestamp = collision.timestamp(); o2::parameters::GRPObject* grpo = 0x0; o2::parameters::GRPMagField* grpmag = 0x0; + if (!skipGRPOquery) + grpo = ccdb->getForTimeStamp(grpPath, run3grp_timestamp); if (grpo) { o2::base::Propagator::initFieldFromGRP(grpo); // Fetch magnetic field from ccdb for current collision @@ -607,9 +537,6 @@ struct DileptonSV { LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kG"; } - mDCAFitter.setBz(d_bz); - mFwdDCAFitter.setBz(d_bz); - // zorro if constexpr (isTriggerAnalysis) { zorro.setCCDBpath(zorroGroup.ccdbPathSoftwareTrigger); @@ -648,9 +575,19 @@ struct DileptonSV { h2sp_resolution = reinterpret_cast(list->FindObject(spresoHistName.value.data())); LOGF(info, "h2sp_resolution.GetBinContent(40, 1) = %f", h2sp_resolution->GetBinContent(40, 1)); } + + if (flowcorrectionGroup.cfgApplyWeightNUA) { + auto list_nua = ccdb->getForTimeStamp(flowcorrectionGroup.nuaPath, collision.timestamp()); + h3nua_weights.push_back(reinterpret_cast(list_nua->FindObject("weights_uls_NUA"))); + LOGF(info, "h3nua_weights.at(0)->GetBinContent(5, 5, 5) = %f", h3nua_weights.at(0)->GetBinContent(5, 5, 5)); + h3nua_weights.push_back(reinterpret_cast(list_nua->FindObject("weights_lspp_NUA"))); + LOGF(info, "h3nua_weights.at(1)->GetBinContent(5, 5, 5) = %f", h3nua_weights.at(1)->GetBinContent(5, 5, 5)); + h3nua_weights.push_back(reinterpret_cast(list_nua->FindObject("weights_lsmm_NUA"))); + LOGF(info, "h3nua_weights.at(2)->GetBinContent(5, 5, 5) = %f", h3nua_weights.at(2)->GetBinContent(5, 5, 5)); + } } - ~DileptonSV() + ~Dilepton() { delete emh_pos; emh_pos = 0x0; @@ -677,11 +614,14 @@ struct DileptonSV { pair_pt_axis_title = "p_{T,ee} (GeV/c)"; pair_y_axis_title = "y_{ee}"; pair_dca_axis_title = "DCA_{ee}^{3D} (#sigma)"; - if (dielectroncuts.cfgDCAType == 1) { + if (cfgDCAType == 1) { pair_dca_axis_title = "DCA_{ee}^{XY} (#sigma)"; - } else if (dielectroncuts.cfgDCAType == 2) { + } else if (cfgDCAType == 2) { pair_dca_axis_title = "DCA_{ee}^{Z} (#sigma)"; } + if (cfgUseSignedDCA) { + pair_dca_axis_title = "Signed " + pair_dca_axis_title; + } } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { mass_axis_title = "m_{#mu#mu} (GeV/c^{2})"; pair_pt_axis_title = "p_{T,#mu#mu} (GeV/c)"; @@ -694,21 +634,19 @@ struct DileptonSV { const o2::framework::AxisSpec axis_pt{ConfPtllBins, pair_pt_axis_title}; const o2::framework::AxisSpec axis_dca{ConfDCAllBins, pair_dca_axis_title}; const o2::framework::AxisSpec axis_y{ConfYllBins, pair_y_axis_title}; - const o2::framework::AxisSpec axis_chi2PCA{ConfLog10Chi2PCABins, "log_{10}(#chi^{2}_{PCA})"}; - const o2::framework::AxisSpec axis_dl{ConfDLBins, "decay length (#sigma)"}; - const o2::framework::AxisSpec axis_cpa{ConfCPABins, "cos(#theta_{p})"}; if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kQC)) { - fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_y, axis_chi2PCA, axis_dl, axis_cpa}, true); - fRegistry.add("Pair/same/uls/hDeltaEtaDeltaPhi", "#Delta#eta-#Delta#varphi between 2 tracks;#Delta#varphi (rad.);#Delta#eta;", o2::framework::HistType::kTH2D, {{360, -M_PI, M_PI}, {400, -2, +2}}, true); + fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y}, false); + fRegistry.add("Pair/same/uls/hDeltaEtaDeltaPhi", "#Delta#eta-#Delta#varphi between 2 tracks;#Delta#varphi (rad.);#Delta#eta;", o2::framework::HistType::kTH2D, {{180, -M_PI, M_PI}, {400, -2, +2}}, false); if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - fRegistry.add("Pair/same/uls/hMvsPhiV", "m_{ee} vs. #varphi_{V};#varphi_{V} (rad.);m_{ee} (GeV/c^{2})", o2::framework::HistType::kTH2D, {{90, 0, M_PI}, {100, 0.0f, 1.0f}}, true); // phiv is only for dielectron + fRegistry.add("Pair/same/uls/hMvsPhiV", "m_{ee} vs. #varphi_{V};#varphi_{V} (rad.);m_{ee} (GeV/c^{2})", o2::framework::HistType::kTH2D, {{90, 0, M_PI}, {100, 0.0f, 1.0f}}, false); // phiv is only for dielectron + fRegistry.add("Pair/same/uls/hMvsOpAng", "m_{ee} vs. angle between 2 tracks;#omega (rad.);m_{ee} (GeV/c^{2})", o2::framework::HistType::kTH2D, {{90, 0, M_PI}, {100, 0.0f, 1.0f}}, false); + fRegistry.add("Pair/same/uls/hDCA1vsDCA2", "DCA of leg1 vs. DCA of leg2;DCA1(#sigma);DCA2 (#sigma)", o2::framework::HistType::kTH2D, {{200, -10, 10.0}, {200, -10, 10}}, false); } fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); fRegistry.addClone("Pair/same/", "Pair/mix/"); - } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kUPC)) { const o2::framework::AxisSpec axis_aco{10, 0, 1.f, "#alpha = 1 - #frac{|#varphi_{l^{+}} - #varphi_{l^{-}}|}{#pi}"}; const o2::framework::AxisSpec axis_asym_pt{10, 0, 1.f, "A = #frac{|p_{T,l^{+}} - p_{T,l^{-}}|}{|p_{T,l^{+}} + p_{T,l^{-}}|}"}; @@ -723,22 +661,27 @@ struct DileptonSV { LOG(fatal) << "set 0 or 1 to cfgPolarizationFrame!"; } const o2::framework::AxisSpec axis_cos_theta{ConfPolarizationCosThetaBins, Form("cos(#theta^{%s})", frameName.data())}; - fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_y, axis_aco, axis_asym_pt, axis_dphi_l_ll, axis_cos_theta}, true); + fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y, axis_aco, axis_asym_pt, axis_dphi_l_ll, axis_cos_theta}, false); fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); fRegistry.addClone("Pair/same/", "Pair/mix/"); - } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV2SP)) { + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV2SP) || cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV3SP)) { if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV2SP)) { nmod = 2; + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV3SP)) { + nmod = 3; } const o2::framework::AxisSpec axis_sp{ConfSPBins, Form("#vec{u}_{%d,ll} #upoint #vec{Q}_{%d}^{%s}", nmod, nmod, qvec_det_names[cfgQvecEstimator].data())}; - fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_y, axis_sp}, true); + fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y, axis_sp}, true); + if (flowcorrectionGroup.cfgFillNUAHist) { + fRegistry.add("Pair/same/uls/hNUA", "NUA Histogram;#phi (rad.);#eta;VtxZ;", o2::framework::HistType::kTH3D, {flowcorrectionGroup.ConfNUAPhiBins, flowcorrectionGroup.ConfNUAEtaBins, flowcorrectionGroup.ConfNUAZVtxBins}, true); // for NUA correction needs to be filled with weights so sumw2 is true + } fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); - fRegistry.add("Pair/mix/uls/hs", "dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_y}, true); + fRegistry.add("Pair/mix/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y}, true); fRegistry.addClone("Pair/mix/uls/", "Pair/mix/lspp/"); fRegistry.addClone("Pair/mix/uls/", "Pair/mix/lsmm/"); @@ -755,7 +698,7 @@ struct DileptonSV { const o2::framework::AxisSpec axis_cos_theta{ConfPolarizationCosThetaBins, Form("cos(#theta^{%s})", frameName.data())}; const o2::framework::AxisSpec axis_phi{ConfPolarizationPhiBins, Form("#varphi^{%s} (rad.)", frameName.data())}; const o2::framework::AxisSpec axis_quadmom{ConfPolarizationQuadMomBins, Form("#frac{3 cos^{2}(#theta^{%s}) -1}{2}", frameName.data())}; - fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_y, axis_cos_theta, axis_phi, axis_quadmom}, true); + fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y, axis_cos_theta, axis_phi, axis_quadmom}, false); fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); @@ -763,7 +706,7 @@ struct DileptonSV { } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kHFll)) { const o2::framework::AxisSpec axis_dphi_ll{36, -M_PI / 2., 3. / 2. * M_PI, "#Delta#varphi = #varphi_{l1} - #varphi_{l2} (rad.)"}; // for kHFll const o2::framework::AxisSpec axis_deta_ll{40, -2., 2., "#Delta#eta = #eta_{l1} - #eta_{l2}"}; - fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_y, axis_dphi_ll, axis_deta_ll}, true); + fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y, axis_dphi_ll, axis_deta_ll}, false); fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); fRegistry.addClone("Pair/same/", "Pair/mix/"); @@ -771,30 +714,32 @@ struct DileptonSV { nmod = 2; const o2::framework::AxisSpec axis_sp{ConfSPBins, Form("#vec{u}_{%d,ll} #upoint #vec{Q}_{%d}^{%s}", nmod, nmod, qvec_det_names[cfgQvecEstimator].data())}; const o2::framework::AxisSpec axis_bootstrap{cfgNumBootstrapSamples, 0.5, static_cast(cfgNumBootstrapSamples) + 0.5, "sample"}; // for bootstrap samples - fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_y, axis_sp, axis_bootstrap}, true); + fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y, axis_sp, axis_bootstrap}, false); fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); - fRegistry.add("Pair/mix/uls/hs", "dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_y}, true); + fRegistry.add("Pair/mix/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y}, false); fRegistry.addClone("Pair/mix/uls/", "Pair/mix/lspp/"); fRegistry.addClone("Pair/mix/uls/", "Pair/mix/lsmm/"); o2::aod::pwgem::dilepton::utils::eventhistogram::addEventHistogramsBootstrap(&fRegistry, cfgNumBootstrapSamples); - } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV2EP)) { + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV2EP) || cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV3EP)) { if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV2EP)) { nmod = 2; + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV3EP)) { + nmod = 3; } // similar to Eq.1 in PRL 126, 162001 (2021) const o2::framework::AxisSpec axis_ep{4, 0, 2 * M_PI / nmod, Form("#varphi_{ll} #minus #Psi_{%d}^{%s}", nmod, qvec_det_names[cfgQvecEstimator].data())}; - fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_y, axis_ep}, true); + fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y, axis_ep}, true); fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); - fRegistry.add("Pair/mix/uls/hs", "dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_y}, true); + fRegistry.add("Pair/mix/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y}, true); fRegistry.addClone("Pair/mix/uls/", "Pair/mix/lspp/"); fRegistry.addClone("Pair/mix/uls/", "Pair/mix/lsmm/"); } else { // same as kQC to avoid seg. fault - fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_y}, true); + fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y}, true); fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); fRegistry.addClone("Pair/same/", "Pair/mix/"); @@ -803,11 +748,13 @@ struct DileptonSV { // event info if (nmod == 2) { o2::aod::pwgem::dilepton::utils::eventhistogram::addEventHistograms<2>(&fRegistry); + } else if (nmod == 3) { + o2::aod::pwgem::dilepton::utils::eventhistogram::addEventHistograms<3>(&fRegistry); } else { o2::aod::pwgem::dilepton::utils::eventhistogram::addEventHistograms<-1>(&fRegistry); } - fRegistry.add("Event/before/hEP2_CentFT0C_forMix", Form("2nd harmonics event plane for mix;centrality FT0C (%%);#Psi_{2}^{%s} (rad.)", qvec_det_names[mixingGroup.cfgEP2Estimator].data()), o2::framework::HistType::kTH2F, {{110, 0, 110}, {180, -M_PI_2, +M_PI_2}}, false); - fRegistry.add("Event/after/hEP2_CentFT0C_forMix", Form("2nd harmonics event plane for mix;centrality FT0C (%%);#Psi_{2}^{%s} (rad.)", qvec_det_names[mixingGroup.cfgEP2Estimator].data()), o2::framework::HistType::kTH2F, {{110, 0, 110}, {180, -M_PI_2, +M_PI_2}}, false); + fRegistry.add("Event/before/hEP2_CentFT0C_forMix", Form("2nd harmonics event plane for mix;centrality FT0C (%%);#Psi_{2}^{%s} (rad.)", qvec_det_names[cfgEP2Estimator_for_Mix].data()), o2::framework::HistType::kTH2F, {{110, 0, 110}, {180, -M_PI_2, +M_PI_2}}, false); + fRegistry.add("Event/after/hEP2_CentFT0C_forMix", Form("2nd harmonics event plane for mix;centrality FT0C (%%);#Psi_{2}^{%s} (rad.)", qvec_det_names[cfgEP2Estimator_for_Mix].data()), o2::framework::HistType::kTH2F, {{110, 0, 110}, {180, -M_PI_2, +M_PI_2}}, false); } void DefineEMEventCut() @@ -859,7 +806,7 @@ struct DileptonSV { fDielectronCut.SetChi2PerClusterITS(0.0, dielectroncuts.cfg_max_chi2its); fDielectronCut.SetNClustersITS(dielectroncuts.cfg_min_ncluster_its, 7); fDielectronCut.SetMeanClusterSizeITS(dielectroncuts.cfg_min_its_cluster_size, dielectroncuts.cfg_max_its_cluster_size); - fDielectronCut.SetTrackMaxDcaSigma(dielectroncuts.cfg_max_dca_sigma, dielectroncuts.cfgDCAType); + fDielectronCut.SetTrackMaxDcaSigma(dielectroncuts.cfg_max_dca_sigma, cfgDCAType); fDielectronCut.SetTrackMaxDcaXY(dielectroncuts.cfg_max_dcaxy); fDielectronCut.SetTrackMaxDcaZ(dielectroncuts.cfg_max_dcaz); fDielectronCut.RequireITSibAny(dielectroncuts.cfg_require_itsib_any); @@ -925,26 +872,6 @@ struct DileptonSV { fDimuonCut.EnableTTCA(dimuoncuts.enableTTCA); } - template - bool foundHFSV(TTrack const& track) - { - int ptbin = lower_bound(dielectroncuts.binsMLSCT.value.begin(), dielectroncuts.binsMLSCT.value.end(), track.pt()) - dielectroncuts.binsMLSCT.value.begin() - 1; - if (ptbin < 0) { - ptbin = 0; - } else if (static_cast(dielectroncuts.binsMLSCT.value.size()) - 2 < ptbin) { - ptbin = static_cast(dielectroncuts.binsMLSCT.value.size()) - 2; - } - - for (int i = 0; i < static_cast(track.nSV()); i++) { - auto probaSCT = track.probaSCT(i); - // LOGF(info, "track.globalIndex() = %d, pt = %f, i = %d, probaSCT[0] = %f, probaSCT[1] = %f, probaSCT[2] = %f, probaSCT[3] = %f", track.globalIndex(), track.pt(), i, probaSCT[0], probaSCT[1], probaSCT[2], probaSCT[3]); - if (probaSCT[1] > dielectroncuts.cutsMLSCTeT_prompt_hc.value[ptbin] || probaSCT[2] > dielectroncuts.cutsMLSCTeT_nonprompt_hc.value[ptbin] || probaSCT[3] > dielectroncuts.cutsMLSCTeT_hb.value[ptbin]) { - return true; - } - } - return false; - } - template bool isGoodQvector(TQvectors const& qvectors) { @@ -983,133 +910,25 @@ struct DileptonSV { } } - struct dileptonSV { - float pt1{999}; - float eta1{999}; - float phi1{999}; - - float pt2{999}; - float eta2{999}; - float phi2{999}; - - // float mass{999}; - // float pt{999}; - // float rapidity{999}; - - float chi2PCA{999}; - float cpa{999}; - float lxy{999}; - float lz{999}; - float lxyz{999}; - float lxyErr{999}; - float lzErr{999}; - float lxyzErr{999}; - }; - - template - bool findSV(TCollision const& collision, TTrackParCov const& t1, TTrackParCov const& t2, dileptonSV& candidate) - { - try { - if (mDCAFitter.process(t1, t2) == 0) { - return false; - } - } catch (const std::runtime_error& error) { - LOG(info) << "Run time error found: " << error.what() << ". DCAFitterN cannot work, skipping the candidate."; - return false; - } - candidate.chi2PCA = mDCAFitter.getChi2AtPCACandidate(); - if (dfGroup.maxLog10Chi2PCA < std::log10(candidate.chi2PCA)) { - return false; - } - - mDCAFitter.propagateTracksToVertex(); - const auto& secondaryVertex = mDCAFitter.getPCACandidate(); - auto trackParCov0 = mDCAFitter.getTrack(0); - auto trackParCov1 = mDCAFitter.getTrack(1); - - // get track momenta - std::array pvertex = {collision.posX(), collision.posY(), collision.posZ()}; - std::array pvec0{}; - std::array pvec1{}; - trackParCov0.getPxPyPzGlo(pvec0); - trackParCov1.getPxPyPzGlo(pvec1); - std::array momDilepton = {pvec0[0] + pvec1[0], pvec0[1] + pvec1[1], pvec0[2] + pvec1[2]}; - candidate.cpa = std::clamp(RecoDecay::cpa(pvertex, secondaryVertex, momDilepton), -1.0, std::nextafter(1.0, 0.0)); - - candidate.lxy = std::sqrt(std::pow(secondaryVertex[0] - collision.posX(), 2) + std::pow(secondaryVertex[1] - collision.posY(), 2)); - candidate.lz = secondaryVertex[2] - collision.posZ(); - candidate.lxyz = std::sqrt(std::pow(candidate.lxy, 2) + std::pow(candidate.lz, 2)); - - auto primaryVertex = getPrimaryVertex(collision); - std::array covVtx = mDCAFitter.calcPCACovMatrixFlat(); - double phi{0}, theta{0}; - getPointDirection(std::array{primaryVertex.getX(), primaryVertex.getY(), primaryVertex.getZ()}, secondaryVertex, phi, theta); - candidate.lxyErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), phi, 0.) + getRotatedCovMatrixXX(covVtx, phi, 0.)); - candidate.lzErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), 0, theta) + getRotatedCovMatrixXX(covVtx, 0, theta)); - candidate.lxyzErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), phi, theta) + getRotatedCovMatrixXX(covVtx, phi, theta)); - - candidate.pt1 = trackParCov0.getPt(); - candidate.eta1 = trackParCov0.getEta(); - candidate.phi1 = RecoDecay::constrainAngle(trackParCov0.getPhi(), 0, 1U); // 0-2pi - - candidate.pt2 = trackParCov1.getPt(); - candidate.eta2 = trackParCov1.getEta(); - candidate.phi2 = RecoDecay::constrainAngle(trackParCov1.getPhi(), 0, 1U); // 0-2pi - - return true; - } - - template - bool findSVFwd(TCollision const& collision, TTrackParCov const& t1, TTrackParCov const& t2, dileptonSV& candidate) + float getNUAweight(const int type_int, const float phi, const float eta, const float zVtx) { - try { - if (mFwdDCAFitter.process(t1, t2) == 0) { - return false; - } - } catch (const std::runtime_error& error) { - LOG(info) << "Run time error found: " << error.what() << ". FwdDCAFitterN cannot work, skipping the candidate."; - return false; + if (h3nua_weights.at(type_int) == nullptr) { + return 1.f; } - mFwdDCAFitter.FwdpropagateTracksToVertex(); + int binId_phi = h3nua_weights.at(type_int)->GetXaxis()->FindBin(phi); + int binId_eta = h3nua_weights.at(type_int)->GetYaxis()->FindBin(eta); + int binId_zVtx = h3nua_weights.at(type_int)->GetZaxis()->FindBin(zVtx); + float nuaWeight = h3nua_weights.at(type_int)->GetBinContent(binId_phi, binId_eta, binId_zVtx); - std::array pvertex = {collision.posX(), collision.posY(), collision.posZ()}; - const auto& secondaryVertex = mFwdDCAFitter.getPCACandidate(); - candidate.chi2PCA = mFwdDCAFitter.getChi2AtPCACandidate(); - if (fdfGroup.maxLog10Chi2PCA < std::log10(candidate.chi2PCA)) { - return false; + if (nuaWeight == 0 || std::isnan(nuaWeight) || std::isinf(nuaWeight)) { + nuaWeight = 1.f; } - auto trackParCov0 = mFwdDCAFitter.getTrack(0); - auto trackParCov1 = mFwdDCAFitter.getTrack(1); - std::array momDilepton = {static_cast(trackParCov0.getPx() + trackParCov1.getPx()), static_cast(trackParCov0.getPy() + trackParCov1.getPy()), static_cast(trackParCov0.getPz() + trackParCov1.getPz())}; - candidate.cpa = std::clamp(RecoDecay::cpa(pvertex, secondaryVertex, momDilepton), -1.0, std::nextafter(1.0, 0.0)); - - candidate.lxy = std::sqrt(std::pow(secondaryVertex[0] - collision.posX(), 2) + std::pow(secondaryVertex[1] - collision.posY(), 2)); - candidate.lz = secondaryVertex[2] - collision.posZ(); - candidate.lxyz = std::sqrt(std::pow(candidate.lxy, 2) + std::pow(candidate.lz, 2)); - - auto primaryVertex = getPrimaryVertex(collision); - std::array covVtx = mFwdDCAFitter.calcPCACovMatrixFlat(); - double phi{0}, theta{0}; - getPointDirection(std::array{primaryVertex.getX(), primaryVertex.getY(), primaryVertex.getZ()}, secondaryVertex, phi, theta); - candidate.lxyErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), phi, 0.) + getRotatedCovMatrixXX(covVtx, phi, 0.)); - candidate.lzErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), 0, theta) + getRotatedCovMatrixXX(covVtx, 0, theta)); - candidate.lxyzErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), phi, theta) + getRotatedCovMatrixXX(covVtx, phi, theta)); - - candidate.pt1 = trackParCov0.getPt(); - candidate.eta1 = trackParCov0.getEta(); - candidate.phi1 = RecoDecay::constrainAngle(trackParCov0.getPhi(), 0, 1U); // 0-2pi - - candidate.pt2 = trackParCov1.getPt(); - candidate.eta2 = trackParCov1.getEta(); - candidate.phi2 = RecoDecay::constrainAngle(trackParCov1.getPhi(), 0, 1U); // 0-2pi - - return true; + return nuaWeight; } template bool fillPairInfo(TCollision const& collision, TTrack1 const& t1, TTrack2 const& t2, TCut const& cut, TAllTracks const&, const std::vector& weightvector) { - dileptonSV candidate; if constexpr (ev_id == 0) { if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { if (dielectroncuts.cfg_pid_scheme == static_cast(DielectronCut::PIDSchemes::kPIDML)) { @@ -1121,16 +940,6 @@ struct DileptonSV { return false; } } - auto trackParCov1 = getTrackParCov(t1); - auto trackParCov2 = getTrackParCov(t2); - if (!findSV(collision, trackParCov1, trackParCov2, candidate)) { - return false; - } - if constexpr (withSCT) { - if (foundHFSV(t1) || foundHFSV(t2)) { - return false; - } - } } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { return false; @@ -1138,19 +947,14 @@ struct DileptonSV { if (!map_best_match_globalmuon[t1.globalIndex()] || !map_best_match_globalmuon[t2.globalIndex()]) { return false; } - auto trackParCov1 = o2::aod::fwdtrackutils::getTrackParCovFwd(t1, t1); - auto trackParCov2 = o2::aod::fwdtrackutils::getTrackParCovFwd(t2, t2); - if (!findSVFwd(collision, trackParCov1, trackParCov2, candidate)) { - return false; - } + + // if (!o2::aod::pwgem::dilepton::utils::emtrackutil::isBestMatch(t1, cut, tracks)) { + // return false; + // } + // if (!o2::aod::pwgem::dilepton::utils::emtrackutil::isBestMatch(t2, cut, tracks)) { + // return false; + // } } - } else if constexpr (ev_id == 1) { // for mixed event - candidate.pt1 = t1.pt(); - candidate.eta1 = t1.eta(); - candidate.phi1 = t1.phi(); - candidate.pt2 = t2.pt(); - candidate.eta2 = t2.eta(); - candidate.phi2 = t2.phi(); } if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { @@ -1171,19 +975,29 @@ struct DileptonSV { // LOGF(info, "ev_id = %d, t1.sign() = %d, t2.sign() = %d, map_weight[std::make_pair(%d, %d)] = %f", ev_id, t1.sign(), t2.sign(), t1.globalIndex(), t2.globalIndex(), weight); } - // ROOT::Math::PtEtaPhiMVector v1(t1.pt(), t1.eta(), t1.phi(), leptonM1); - // ROOT::Math::PtEtaPhiMVector v2(t2.pt(), t2.eta(), t2.phi(), leptonM2); - ROOT::Math::PtEtaPhiMVector v1(candidate.pt1, candidate.eta1, candidate.phi1, leptonM1); - ROOT::Math::PtEtaPhiMVector v2(candidate.pt2, candidate.eta2, candidate.phi2, leptonM2); + ROOT::Math::PtEtaPhiMVector v1(t1.pt(), t1.eta(), t1.phi(), leptonM1); + ROOT::Math::PtEtaPhiMVector v2(t2.pt(), t2.eta(), t2.phi(), leptonM2); ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; float pair_dca = 999.f; if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCAQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::dca3DinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dca3DinSigma(t2)); - if (dielectroncuts.cfgDCAType == 1) { - pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCAQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t2)); - } else if (dielectroncuts.cfgDCAType == 2) { - pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCAQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t2)); + if (cfgUseSignedDCA) { + pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCASignQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::dca3DinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dca3DinSigma(t2), t1.sign(), t2.sign()); + } else { + pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCAQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::dca3DinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dca3DinSigma(t2)); + } + if (cfgDCAType == 1) { + if (cfgUseSignedDCA) { + pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCASignQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t2), t1.sign(), t2.sign()); + } else { + pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCAQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t2)); + } + } else if (cfgDCAType == 2) { + if (cfgUseSignedDCA) { + pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCASignQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t2), t1.sign(), t2.sign()); + } else { + pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCAQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t2)); + } } } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCAQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::fwdDcaXYinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::fwdDcaXYinSigma(t2)); @@ -1195,24 +1009,43 @@ struct DileptonSV { dphi = RecoDecay::constrainAngle(dphi, -M_PI, 1U); // -pi - +pi float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(t1.px(), t1.py(), t1.pz(), t2.px(), t2.py(), t2.pz(), t1.sign(), t2.sign(), d_bz); + float opAng = o2::aod::pwgem::dilepton::utils::pairutil::getOpeningAngle(t1.px(), t1.py(), t1.pz(), t2.px(), t2.py(), t2.pz()); if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hDeltaEtaDeltaPhi"), dphi, deta, weight); if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hMvsPhiV"), phiv, v12.M(), weight); + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hMvsOpAng"), opAng, v12.M(), weight); + if (cfgDCAType == 1) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hDCA1vsDCA2"), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t2), weight); + } else if (cfgDCAType == 2) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hDCA1vsDCA2"), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t2), weight); + } } } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hDeltaEtaDeltaPhi"), dphi, deta, weight); if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hMvsPhiV"), phiv, v12.M(), weight); + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hMvsOpAng"), opAng, v12.M(), weight); + if (cfgDCAType == 1) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hDCA1vsDCA2"), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t2), weight); + } else if (cfgDCAType == 2) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hDCA1vsDCA2"), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t2), weight); + } } } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hDeltaEtaDeltaPhi"), dphi, deta, weight); if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hMvsPhiV"), phiv, v12.M(), weight); + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hMvsOpAng"), opAng, v12.M(), weight); + if (cfgDCAType == 1) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hDCA1vsDCA2"), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t2), weight); + } else if (cfgDCAType == 2) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hDCA1vsDCA2"), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t2), weight); + } } } } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kUPC)) { @@ -1242,7 +1075,7 @@ struct DileptonSV { } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), aco, asym, std::fabs(dphi_l_ll), cos_thetaPol, weight); } - } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV2SP)) { + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV2SP) || cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV3SP)) { std::array q2ft0m = {collision.q2xft0m(), collision.q2yft0m()}; std::array q2ft0a = {collision.q2xft0a(), collision.q2yft0a()}; std::array q2ft0c = {collision.q2xft0c(), collision.q2yft0c()}; @@ -1250,21 +1083,43 @@ struct DileptonSV { std::array q2bpos = {collision.q2xbpos(), collision.q2ybpos()}; std::array q2bneg = {collision.q2xbneg(), collision.q2ybneg()}; std::array q2fv0a = {collision.q2xfv0a(), collision.q2yfv0a()}; + std::array q3ft0m = {collision.q3xft0m(), collision.q3yft0m()}; + std::array q3ft0a = {collision.q3xft0a(), collision.q3yft0a()}; + std::array q3ft0c = {collision.q3xft0c(), collision.q3yft0c()}; + std::array q3btot = {collision.q3xbtot(), collision.q3ybtot()}; + std::array q3bpos = {collision.q3xbpos(), collision.q3ybpos()}; + std::array q3bneg = {collision.q3xbneg(), collision.q3ybneg()}; + std::array q3fv0a = {collision.q3xfv0a(), collision.q3yfv0a()}; std::vector>> qvectors = { {{999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}}, // 0th harmonics {{999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}}, // 1st harmonics {q2ft0m, q2ft0a, q2ft0c, q2btot, q2bpos, q2bneg, q2fv0a}, // 2nd harmonics + {q3ft0m, q3ft0a, q3ft0c, q3btot, q3bpos, q3bneg, q3fv0a}, // 3rd harmonics }; if constexpr (ev_id == 0) { + // LOGF(info, "collision.centFT0C() = %f, collision.trackOccupancyInTimeRange() = %d, getSPresolution = %f", collision.centFT0C(), collision.trackOccupancyInTimeRange(), getSPresolution(collision.centFT0C(), collision.trackOccupancyInTimeRange())); + if (flowcorrectionGroup.cfgApplyWeightNUA) { + weight *= 1. / getNUAweight(t1.sign() * t2.sign() < 0 ? 0 : (t1.sign() > 0 && t2.sign() > 0 ? 1 : 2), v12.Phi(), v12.Eta(), collision.posZ()); + } + float sp = RecoDecay::dotProd(std::array{static_cast(std::cos(nmod * v12.Phi())), static_cast(std::sin(nmod * v12.Phi()))}, qvectors[nmod][cfgQvecEstimator]) / getSPresolution(collision.centFT0C(), collision.trackOccupancyInTimeRange()); if (t1.sign() * t2.sign() < 0) { // ULS fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), sp, weight); + if (flowcorrectionGroup.cfgFillNUAHist) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hNUA"), v12.Phi(), v12.Eta(), collision.posZ(), weight); + } } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), sp, weight); + if (flowcorrectionGroup.cfgFillNUAHist) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hNUA"), v12.Phi(), v12.Eta(), collision.posZ(), weight); + } } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), sp, weight); + if (flowcorrectionGroup.cfgFillNUAHist) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hNUA"), v12.Phi(), v12.Eta(), collision.posZ(), weight); + } } } else if constexpr (ev_id == 1) { if (t1.sign() * t2.sign() < 0) { // ULS @@ -1318,11 +1173,19 @@ struct DileptonSV { std::array q2bpos = {collision.q2xbpos(), collision.q2ybpos()}; std::array q2bneg = {collision.q2xbneg(), collision.q2ybneg()}; std::array q2fv0a = {collision.q2xfv0a(), collision.q2yfv0a()}; + std::array q3ft0m = {collision.q3xft0m(), collision.q3yft0m()}; + std::array q3ft0a = {collision.q3xft0a(), collision.q3yft0a()}; + std::array q3ft0c = {collision.q3xft0c(), collision.q3yft0c()}; + std::array q3btot = {collision.q3xbtot(), collision.q3ybtot()}; + std::array q3bpos = {collision.q3xbpos(), collision.q3ybpos()}; + std::array q3bneg = {collision.q3xbneg(), collision.q3ybneg()}; + std::array q3fv0a = {collision.q3xfv0a(), collision.q3yfv0a()}; std::vector>> qvectors = { {{999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}}, // 0th harmonics {{999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}}, // 1st harmonics {q2ft0m, q2ft0a, q2ft0c, q2btot, q2bpos, q2bneg, q2fv0a}, // 2nd harmonics + {q3ft0m, q3ft0a, q3ft0c, q3btot, q3bpos, q3bneg, q3fv0a}, // 3rd harmonics }; if constexpr (ev_id == 0) { @@ -1347,12 +1210,13 @@ struct DileptonSV { fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); } } - } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV2EP)) { + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV2EP) || cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV3EP)) { if constexpr (ev_id == 0) { std::vector> eventplanes = { {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, // 0th harmonics {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, // 1st harmonics {collision.ep2ft0m(), collision.ep2ft0a(), collision.ep2ft0c(), collision.ep2btot(), collision.ep2bpos(), collision.ep2bneg(), collision.ep2fv0a()}, // 2nd harmonics + {collision.ep3ft0m(), collision.ep3ft0a(), collision.ep3ft0c(), collision.ep3btot(), collision.ep3bpos(), collision.ep3bneg(), collision.ep3fv0a()}, // 3rd harmonics }; float ep = RecoDecay::constrainAngle(eventplanes[nmod][cfgQvecEstimator], -o2::constants::math::PI / nmod, static_cast(nmod)); @@ -1392,83 +1256,42 @@ struct DileptonSV { if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { if (std::find(used_trackIds_per_col.begin(), used_trackIds_per_col.end(), t1.globalIndex()) == used_trackIds_per_col.end()) { used_trackIds_per_col.emplace_back(t1.globalIndex()); - if (mixingGroup.cfgDoMix) { + if (cfgDoMix) { if (t1.sign() > 0) { - emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMTrack(candidate.pt1, candidate.eta1, candidate.phi1, leptonM1, t1.sign(), t1.dcaXY(), t1.dcaZ(), t1.cYY(), t1.cZY(), t1.cZZ())); - // emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMTrackWithCov(t1.pt(), t1.eta(), t1.phi(), leptonM1, t1.sign(), t1.dcaXY(), t1.dcaZ(), t1.cYY(), t1.cZY(), t1.cZZ(), - // t1.x(), t1.y(), t1.z(), t1.alpha(), t1.snp(), t1.tgl(), - // t1.cSnpY(), t1.cSnpZ(), t1.cSnpSnp(), - // t1.cTglY(), t1.cTglZ(), t1.cTglSnp(), t1.cTglTgl(), - // t1.c1PtY(), t1.c1PtZ(), t1.c1PtSnp(), t1.c1PtTgl(), t1.c1Pt21Pt2())); + emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMTrack(t1.pt(), t1.eta(), t1.phi(), leptonM1, t1.sign(), t1.dcaXY(), t1.dcaZ(), t1.cYY(), t1.cZY(), t1.cZZ())); } else { - emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMTrack(candidate.pt1, candidate.eta1, candidate.phi1, leptonM1, t1.sign(), t1.dcaXY(), t1.dcaZ(), t1.cYY(), t1.cZY(), t1.cZZ())); - // emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMTrackWithCov(t1.pt(), t1.eta(), t1.phi(), leptonM1, t1.sign(), t1.dcaXY(), t1.dcaZ(), t1.cYY(), t1.cZY(), t1.cZZ(), - // t1.x(), t1.y(), t1.z(), t1.alpha(), t1.snp(), t1.tgl(), - // t1.cSnpY(), t1.cSnpZ(), t1.cSnpSnp(), - // t1.cTglY(), t1.cTglZ(), t1.cTglSnp(), t1.cTglTgl(), - // t1.c1PtY(), t1.c1PtZ(), t1.c1PtSnp(), t1.c1PtTgl(), t1.c1Pt21Pt2())); + emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMTrack(t1.pt(), t1.eta(), t1.phi(), leptonM1, t1.sign(), t1.dcaXY(), t1.dcaZ(), t1.cYY(), t1.cZY(), t1.cZZ())); } } } if (std::find(used_trackIds_per_col.begin(), used_trackIds_per_col.end(), t2.globalIndex()) == used_trackIds_per_col.end()) { used_trackIds_per_col.emplace_back(t2.globalIndex()); - if (mixingGroup.cfgDoMix) { + if (cfgDoMix) { if (t2.sign() > 0) { - emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMTrack(candidate.pt2, candidate.eta2, candidate.phi2, leptonM2, t2.sign(), t2.dcaXY(), t2.dcaZ(), t2.cYY(), t2.cZY(), t2.cZZ())); - // emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMTrackWithCov(t2.pt(), t2.eta(), t2.phi(), leptonM2, t2.sign(), t2.dcaXY(), t2.dcaZ(), t2.cYY(), t2.cZY(), t2.cZZ(), - // t2.x(), t2.y(), t2.z(), t2.alpha(), t2.snp(), t2.tgl(), - // t2.cSnpY(), t2.cSnpZ(), t2.cSnpSnp(), - // t2.cTglY(), t2.cTglZ(), t2.cTglSnp(), t2.cTglTgl(), - // t2.c1PtY(), t2.c1PtZ(), t2.c1PtSnp(), t2.c1PtTgl(), t2.c1Pt21Pt2())); + emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMTrack(t2.pt(), t2.eta(), t2.phi(), leptonM2, t2.sign(), t2.dcaXY(), t2.dcaZ(), t2.cYY(), t2.cZY(), t2.cZZ())); } else { - emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMTrack(candidate.pt2, candidate.eta2, candidate.phi2, leptonM2, t2.sign(), t2.dcaXY(), t2.dcaZ(), t2.cYY(), t2.cZY(), t2.cZZ())); - // emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMTrackWithCov(t2.pt(), t2.eta(), t2.phi(), leptonM2, t2.sign(), t2.dcaXY(), t2.dcaZ(), t2.cYY(), t2.cZY(), t2.cZZ(), - // t2.x(), t2.y(), t2.z(), t2.alpha(), t2.snp(), t2.tgl(), - // t2.cSnpY(), t2.cSnpZ(), t2.cSnpSnp(), - // t2.cTglY(), t2.cTglZ(), t2.cTglSnp(), t2.cTglTgl(), - // t2.c1PtY(), t2.c1PtZ(), t2.c1PtSnp(), t2.c1PtTgl(), t2.c1Pt21Pt2())); + emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMTrack(t2.pt(), t2.eta(), t2.phi(), leptonM2, t2.sign(), t2.dcaXY(), t2.dcaZ(), t2.cYY(), t2.cZY(), t2.cZZ())); } } } } else if (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { if (std::find(used_trackIds_per_col.begin(), used_trackIds_per_col.end(), t1.globalIndex()) == used_trackIds_per_col.end()) { used_trackIds_per_col.emplace_back(t1.globalIndex()); - if (mixingGroup.cfgDoMix) { + if (cfgDoMix) { if (t1.sign() > 0) { - emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(candidate.pt1, candidate.eta1, candidate.phi1, leptonM1, t1.sign(), t1.fwdDcaX(), t1.fwdDcaY(), t1.cXXatDCA(), t1.cXYatDCA(), t1.cYYatDCA())); - // emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrackWithCov(t1.pt(), t1.eta(), t1.phi(), leptonM1, t1.sign(), t1.fwdDcaX(), t1.fwdDcaY(), t1.cXXatDCA(), t1.cXYatDCA(), t1.cYYatDCA(), - // t1.x(), t1.y(), t1.z(), t1.tgl(), - // t1.cPhiX(), t1.cPhiY(), t1.cPhiPhi(), - // t1.cTglX(), t1.cTglY(), t1.cTglPhi(), t1.cTglTgl(), - // t1.c1PtX(), t1.c1PtY(), t1.c1PtPhi(), t1.c1PtTgl(), t1.c1Pt21Pt2(), t1.chi2())); - + emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(t1.pt(), t1.eta(), t1.phi(), leptonM1, t1.sign(), t1.fwdDcaX(), t1.fwdDcaY(), t1.cXXatDCA(), t1.cXYatDCA(), t1.cYYatDCA())); } else { - emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(candidate.pt1, candidate.eta1, candidate.phi1, leptonM1, t1.sign(), t1.fwdDcaX(), t1.fwdDcaY(), t1.cXXatDCA(), t1.cXYatDCA(), t1.cYYatDCA())); - // emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrackWithCov(t1.pt(), t1.eta(), t1.phi(), leptonM1, t1.sign(), t1.fwdDcaX(), t1.fwdDcaY(), t1.cXXatDCA(), t1.cXYatDCA(), t1.cYYatDCA(), - // t1.x(), t1.y(), t1.z(), t1.tgl(), - // t1.cPhiX(), t1.cPhiY(), t1.cPhiPhi(), - // t1.cTglX(), t1.cTglY(), t1.cTglPhi(), t1.cTglTgl(), - // t1.c1PtX(), t1.c1PtY(), t1.c1PtPhi(), t1.c1PtTgl(), t1.c1Pt21Pt2(), t1.chi2())); + emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(t1.pt(), t1.eta(), t1.phi(), leptonM1, t1.sign(), t1.fwdDcaX(), t1.fwdDcaY(), t1.cXXatDCA(), t1.cXYatDCA(), t1.cYYatDCA())); } } } if (std::find(used_trackIds_per_col.begin(), used_trackIds_per_col.end(), t2.globalIndex()) == used_trackIds_per_col.end()) { used_trackIds_per_col.emplace_back(t2.globalIndex()); - if (mixingGroup.cfgDoMix) { + if (cfgDoMix) { if (t2.sign() > 0) { - emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(candidate.pt2, candidate.eta2, candidate.phi2, leptonM2, t2.sign(), t2.fwdDcaX(), t2.fwdDcaY(), t2.cXXatDCA(), t2.cXYatDCA(), t2.cYYatDCA())); - // emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrackWithCov(t2.pt(), t2.eta(), t2.phi(), leptonM2, t2.sign(), t2.fwdDcaX(), t2.fwdDcaY(), t2.cXXatDCA(), t2.cXYatDCA(), t2.cYYatDCA(), - // t2.x(), t2.y(), t2.z(), t2.tgl(), - // t2.cPhiX(), t2.cPhiY(), t2.cPhiPhi(), - // t2.cTglX(), t2.cTglY(), t2.cTglPhi(), t2.cTglTgl(), - // t2.c1PtX(), t2.c1PtY(), t2.c1PtPhi(), t2.c1PtTgl(), t2.c1Pt21Pt2(), t2.chi2())); + emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(t2.pt(), t2.eta(), t2.phi(), leptonM2, t2.sign(), t2.fwdDcaX(), t2.fwdDcaY(), t2.cXXatDCA(), t2.cXYatDCA(), t2.cYYatDCA())); } else { - emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(candidate.pt2, candidate.eta2, candidate.phi2, leptonM2, t2.sign(), t2.fwdDcaX(), t2.fwdDcaY(), t2.cXXatDCA(), t2.cXYatDCA(), t2.cYYatDCA())); - // emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrackWithCov(t2.pt(), t2.eta(), t2.phi(), leptonM2, t2.sign(), t2.fwdDcaX(), t2.fwdDcaY(), t2.cXXatDCA(), t2.cXYatDCA(), t2.cYYatDCA(), - // t2.x(), t2.y(), t2.z(), t2.tgl(), - // t2.cPhiX(), t2.cPhiY(), t2.cPhiPhi(), - // t2.cTglX(), t2.cTglY(), t2.cTglPhi(), t2.cTglTgl(), - // t2.c1PtX(), t2.c1PtY(), t2.c1PtPhi(), t2.c1PtTgl(), t2.c1Pt21Pt2(), t2.chi2())); + emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(t2.pt(), t2.eta(), t2.phi(), leptonM2, t2.sign(), t2.fwdDcaX(), t2.fwdDcaY(), t2.cXXatDCA(), t2.cXYatDCA(), t2.cYYatDCA())); } } } @@ -1485,6 +1308,7 @@ struct DileptonSV { using FilteredMyCollisionsSWT = o2::soa::Filtered; o2::framework::SliceCache cache; + // o2::framework::Preslice perCollision_electron = o2::aod::emprimaryelectron::emeventId; o2::framework::Preslice perCollision_electron = o2::aod::emprimaryelectron::emeventId; o2::framework::expressions::Filter trackFilter_electron = dielectroncuts.cfg_min_pt_track < o2::aod::track::pt && dielectroncuts.cfg_min_eta_track < o2::aod::track::eta && o2::aod::track::eta < dielectroncuts.cfg_max_eta_track && nabs(o2::aod::track::dcaXY) < dielectroncuts.cfg_max_dcaxy && nabs(o2::aod::track::dcaZ) < dielectroncuts.cfg_max_dcaz && o2::aod::track::itsChi2NCl < dielectroncuts.cfg_max_chi2its && o2::aod::track::tpcChi2NCl < dielectroncuts.cfg_max_chi2tpc; o2::framework::expressions::Filter pidFilter_electron = dielectroncuts.cfg_min_TPCNsigmaEl < o2::aod::pidtpc::tpcNSigmaEl && o2::aod::pidtpc::tpcNSigmaEl < dielectroncuts.cfg_max_TPCNsigmaEl; @@ -1510,6 +1334,7 @@ struct DileptonSV { o2::framework::Partition positive_electrons = o2::aod::emprimaryelectron::sign > int8_t(0); o2::framework::Partition negative_electrons = o2::aod::emprimaryelectron::sign < int8_t(0); + // o2::framework::Preslice perCollision_muon = o2::aod::emprimarymuon::emeventId; o2::framework::Preslice perCollision_muon = o2::aod::emprimarymuon::emeventId; o2::framework::expressions::Filter trackFilter_muon = o2::aod::fwdtrack::trackType == dimuoncuts.cfg_track_type && dimuoncuts.cfg_min_pt_track < o2::aod::fwdtrack::pt && o2::aod::fwdtrack::pt < dimuoncuts.cfg_max_pt_track && dimuoncuts.cfg_min_eta_track < o2::aod::fwdtrack::eta && o2::aod::fwdtrack::eta < dimuoncuts.cfg_max_eta_track; o2::framework::expressions::Filter ttcaFilter_muon = ifnode(dimuoncuts.enableTTCA.node(), true, o2::aod::emprimarymuon::isAssociatedToMPC == true); @@ -1524,6 +1349,18 @@ struct DileptonSV { std::unordered_map map_best_match_globalmuon; std::vector used_trackIds_per_col; + uint64_t nProcessedEvents = 0; + double maxBufferedEvents = 0.; + + void fillEventBufferLevel() + { + ++nProcessedEvents; + if (cfgDoMix && nProcessedEvents % 10000 == 0) { + const double fraction = static_cast(map_mixed_eventId_to_globalBC.size()) / maxBufferedEvents; + fRegistry.fill(HIST("Pair/mix/hEventBufferFillLevel"), nProcessedEvents, fraction); + } + } + int ndf = 0; template @@ -1535,6 +1372,7 @@ struct DileptonSV { const float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; float centrality = centralities[cfgCentEstimator]; if (centralities[cfgCentEstimator] < eventcuts.cfgCentMin || eventcuts.cfgCentMax < centralities[cfgCentEstimator]) { + fillEventBufferLevel(); continue; } @@ -1545,17 +1383,27 @@ struct DileptonSV { std::array q2bpos = {collision.q2xbpos(), collision.q2ybpos()}; std::array q2bneg = {collision.q2xbneg(), collision.q2ybneg()}; std::array q2fv0a = {collision.q2xfv0a(), collision.q2yfv0a()}; + std::array q3ft0m = {collision.q3xft0m(), collision.q3yft0m()}; + std::array q3ft0a = {collision.q3xft0a(), collision.q3yft0a()}; + std::array q3ft0c = {collision.q3xft0c(), collision.q3yft0c()}; + std::array q3btot = {collision.q3xbtot(), collision.q3ybtot()}; + std::array q3bpos = {collision.q3xbpos(), collision.q3ybpos()}; + std::array q3bneg = {collision.q3xbneg(), collision.q3ybneg()}; + std::array q3fv0a = {collision.q3xfv0a(), collision.q3yfv0a()}; const float eventplanes_2_for_mix[7] = {collision.ep2ft0m(), collision.ep2ft0a(), collision.ep2ft0c(), collision.ep2btot(), collision.ep2bpos(), collision.ep2bneg(), collision.ep2fv0a()}; - float ep2 = eventplanes_2_for_mix[mixingGroup.cfgEP2Estimator]; + float ep2 = eventplanes_2_for_mix[cfgEP2Estimator_for_Mix]; std::vector>> qvectors = { {{999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}}, // 0th harmonics {{999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}}, // 1st harmonics {q2ft0m, q2ft0a, q2ft0c, q2btot, q2bpos, q2bneg, q2fv0a}, // 2nd harmonics + {q3ft0m, q3ft0a, q3ft0c, q3btot, q3bpos, q3bneg, q3fv0a}, // 3rd harmonics }; if (nmod == 2) { o2::aod::pwgem::dilepton::utils::eventhistogram::fillEventInfo<0, 2>(&fRegistry, collision); + } else if (nmod == 3) { + o2::aod::pwgem::dilepton::utils::eventhistogram::fillEventInfo<0, 3>(&fRegistry, collision); } else { o2::aod::pwgem::dilepton::utils::eventhistogram::fillEventInfo<0, -1>(&fRegistry, collision); } @@ -1565,19 +1413,23 @@ struct DileptonSV { fRegistry.fill(HIST("Event/before/hEP2_CentFT0C_forMix"), collision.centFT0C(), ep2); if (!fEMEventCut.IsSelected(collision)) { + fillEventBufferLevel(); continue; } if (eventcuts.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { + fillEventBufferLevel(); continue; } if (nmod > 0 && !isGoodQvector(qvectors)) { + fillEventBufferLevel(); continue; } if constexpr (isTriggerAnalysis) { int emswtId = o2::aod::pwgem::dilepton::swt::aliasLabels.at(zorroGroup.cfg_swt_name.value); if (!collision.triggerMask_bit(emswtId)) { + fillEventBufferLevel(); continue; } @@ -1619,6 +1471,8 @@ struct DileptonSV { if (nmod == 2) { o2::aod::pwgem::dilepton::utils::eventhistogram::fillEventInfo<1, 2>(&fRegistry, collision); + } else if (nmod == 3) { + o2::aod::pwgem::dilepton::utils::eventhistogram::fillEventInfo<1, 3>(&fRegistry, collision); } else { o2::aod::pwgem::dilepton::utils::eventhistogram::fillEventInfo<1, -1>(&fRegistry, collision); } @@ -1655,7 +1509,8 @@ struct DileptonSV { used_trackIds_per_col.clear(); used_trackIds_per_col.shrink_to_fit(); - if (!mixingGroup.cfgDoMix || !(nuls > 0 || nlspp > 0 || nlsmm > 0)) { + if (!cfgDoMix || !(nuls > 0 || nlspp > 0 || nlsmm > 0)) { + fillEventBufferLevel(); continue; } @@ -1681,8 +1536,15 @@ struct DileptonSV { epbin = static_cast(ep_bin_edges.size()) - 2; } - float occupancy = std::array{collision.ft0cOccupancyInTimeRange(), static_cast(collision.trackOccupancyInTimeRange())}[mixingGroup.cfgOccupancyEstimator]; - int occbin = lower_bound(occ_bin_edges.begin(), occ_bin_edges.end(), occupancy) - occ_bin_edges.begin() - 1; + int occbin = -1; + if (cfgOccupancyEstimator == 0) { + occbin = lower_bound(occ_bin_edges.begin(), occ_bin_edges.end(), collision.ft0cOccupancyInTimeRange()) - occ_bin_edges.begin() - 1; + } else if (cfgOccupancyEstimator == 1) { + occbin = lower_bound(occ_bin_edges.begin(), occ_bin_edges.end(), collision.trackOccupancyInTimeRange()) - occ_bin_edges.begin() - 1; + } else { + occbin = lower_bound(occ_bin_edges.begin(), occ_bin_edges.end(), collision.ft0cOccupancyInTimeRange()) - occ_bin_edges.begin() - 1; + } + if (occbin < 0) { occbin = 0; } else if (static_cast(occ_bin_edges.size()) - 2 < occbin) { @@ -1695,11 +1557,11 @@ struct DileptonSV { std::pair key_df_collision = std::make_pair(ndf, collision.globalIndex()); // this gives the current event. // make a vector of selected photons in this collision. - auto selected_posTracks_in_this_event = emh_pos->GetTracksPerCollision(key_df_collision); - auto selected_negTracks_in_this_event = emh_neg->GetTracksPerCollision(key_df_collision); + const auto& selected_posTracks_in_this_event = emh_pos->GetTracksPerCollision(key_df_collision); + const auto& selected_negTracks_in_this_event = emh_neg->GetTracksPerCollision(key_df_collision); // LOGF(info, "N selected tracks in current event (%d, %d), zvtx = %f, centrality = %f , npos = %d , nneg = %d, nuls = %d , nlspp = %d, nlsmm = %d", ndf, collision.globalIndex(), collision.posZ(), centralities[cfgCentEstimator], selected_posTracks_in_this_event.size(), selected_negTracks_in_this_event.size(), nuls, nlspp, nlsmm); - auto collisionIds_in_mixing_pool = emh_pos->GetCollisionIdsFromEventPool(key_bin); // pos/neg does not matter. + const auto& collisionIds_in_mixing_pool = emh_pos->GetCollisionIdsFromEventPool(key_bin); // pos/neg does not matter. // LOGF(info, "collisionIds_in_mixing_pool.size() = %d", collisionIds_in_mixing_pool.size()); for (const auto& mix_dfId_collisionId : collisionIds_in_mixing_pool) { @@ -1711,14 +1573,13 @@ struct DileptonSV { auto globalBC_mix = map_mixed_eventId_to_globalBC[mix_dfId_collisionId]; uint64_t diffBC = std::max(collision.globalBC(), globalBC_mix) - std::min(collision.globalBC(), globalBC_mix); - fRegistry.fill(HIST("Event/hDiffBC"), diffBC); - if (diffBC < mixingGroup.ndiff_bc_mix) { + fRegistry.fill(HIST("Pair/mix/hDiffBC"), diffBC); + if (diffBC < ndiff_bc_mix) { continue; } - fRegistry.fill(HIST("Event/hsMixCounter"), collision.posZ(), centrality, ep2, occupancy); - auto posTracks_from_event_pool = emh_pos->GetTracksPerCollision(mix_dfId_collisionId); - auto negTracks_from_event_pool = emh_neg->GetTracksPerCollision(mix_dfId_collisionId); + const auto& posTracks_from_event_pool = emh_pos->GetTracksPerCollision(mix_dfId_collisionId); + const auto& negTracks_from_event_pool = emh_neg->GetTracksPerCollision(mix_dfId_collisionId); // LOGF(info, "Do event mixing: current event (%d, %d) | event pool (%d, %d), npos = %d , nneg = %d", ndf, collision.globalIndex(), mix_dfId, mix_collisionId, posTracks_from_event_pool.size(), negTracks_from_event_pool.size()); for (const auto& pos : selected_posTracks_in_this_event) { // ULS mix @@ -1748,10 +1609,18 @@ struct DileptonSV { if (nuls > 0 || nlspp > 0 || nlsmm > 0) { map_mixed_eventId_to_globalBC[key_df_collision] = collision.globalBC(); - emh_pos->AddCollisionIdAtLast(key_bin, key_df_collision); - emh_neg->AddCollisionIdAtLast(key_bin, key_df_collision); + const auto evicted_pos = emh_pos->AddCollisionIdAtLast(key_bin, key_df_collision); + const auto evicted_neg = emh_neg->AddCollisionIdAtLast(key_bin, key_df_collision); + if (evicted_pos != evicted_neg) { + LOGF(fatal, "Positive and negative mixing pools are out of sync"); + } + if (evicted_pos) { + map_mixed_eventId_to_globalBC.erase(*evicted_pos); + } + } // end of if pair exist + fillEventBufferLevel(); } // end of collision loop } // end of DF @@ -1769,13 +1638,6 @@ struct DileptonSV { return false; } } - - if constexpr (withSCT) { - if (foundHFSV(t1) || foundHFSV(t2)) { - return false; - } - } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { if (!cut.IsSelectedTrack(t1) || !cut.IsSelectedTrack(t2)) { return false; @@ -1783,13 +1645,6 @@ struct DileptonSV { if (!map_best_match_globalmuon[t1.globalIndex()] || !map_best_match_globalmuon[t2.globalIndex()]) { return false; } - - // if (!o2::aod::pwgem::dilepton::utils::emtrackutil::isBestMatch(t1, cut, tracks)) { - // return false; - // } - // if (!o2::aod::pwgem::dilepton::utils::emtrackutil::isBestMatch(t2, cut, tracks)) { - // return false; - // } } if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { @@ -1831,11 +1686,19 @@ struct DileptonSV { std::array q2bpos = {collision.q2xbpos(), collision.q2ybpos()}; std::array q2bneg = {collision.q2xbneg(), collision.q2ybneg()}; std::array q2fv0a = {collision.q2xfv0a(), collision.q2yfv0a()}; + std::array q3ft0m = {collision.q3xft0m(), collision.q3yft0m()}; + std::array q3ft0a = {collision.q3xft0a(), collision.q3yft0a()}; + std::array q3ft0c = {collision.q3xft0c(), collision.q3yft0c()}; + std::array q3btot = {collision.q3xbtot(), collision.q3ybtot()}; + std::array q3bpos = {collision.q3xbpos(), collision.q3ybpos()}; + std::array q3bneg = {collision.q3xbneg(), collision.q3ybneg()}; + std::array q3fv0a = {collision.q3xfv0a(), collision.q3yfv0a()}; std::vector>> qvectors = { {{999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}}, // 0th harmonics {{999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}}, // 1st harmonics {q2ft0m, q2ft0a, q2ft0c, q2btot, q2bpos, q2bneg, q2fv0a}, // 2nd harmonics + {q3ft0m, q3ft0a, q3ft0c, q3btot, q3bpos, q3bneg, q3fv0a}, // 3rd harmonics }; if (!fEMEventCut.IsSelected(collision)) { @@ -1924,7 +1787,7 @@ struct DileptonSV { map_best_match_globalmuon.clear(); ndf++; } - PROCESS_SWITCH(DileptonSV, processAnalysis, "run dilepton analysis", true); + PROCESS_SWITCH(Dilepton, processAnalysis, "run dilepton analysis", true); void processTriggerAnalysis(FilteredMyCollisionsSWT const& collisions, Types const&... args, o2::aod::EMSWTriggerATCounters const& countersAT, o2::aod::EMSWTriggerATOICounters const& countersATOI) { @@ -1959,7 +1822,7 @@ struct DileptonSV { } } } - PROCESS_SWITCH(DileptonSV, processTriggerAnalysis, "run dilepton analysis on triggered data", false); + PROCESS_SWITCH(Dilepton, processTriggerAnalysis, "run dilepton analysis on triggered data", false); void processNorm(o2::aod::EMEventNormInfos const& collisions) { @@ -2033,7 +1896,7 @@ struct DileptonSV { fRegistry.fill(HIST("Event/norm/hCollisionCounter"), o2::aod::pwgem::dilepton::utils::eventhistogram::nbin_ev); // accepted } // end of collision loop } - PROCESS_SWITCH(DileptonSV, processNorm, "process normalization info", false); + PROCESS_SWITCH(Dilepton, processNorm, "process normalization info", false); void processBC(o2::aod::EMBCs const& bcs) { @@ -2059,10 +1922,10 @@ struct DileptonSV { } } } - PROCESS_SWITCH(DileptonSV, processBC, "process BC counter", false); + PROCESS_SWITCH(Dilepton, processBC, "process BC counter", false); void processDummy(MyCollisions const&) {} - PROCESS_SWITCH(DileptonSV, processDummy, "Dummy function", false); + PROCESS_SWITCH(Dilepton, processDummy, "Dummy function", false); }; -#endif // PWGEM_DILEPTON_CORE_DILEPTONSV_H_ +#endif // PWGEM_DILEPTON_CORE_DILEPTONMIXINGTEST_H_ diff --git a/PWGEM/Dilepton/Core/DileptonSVMC.h b/PWGEM/Dilepton/Core/DileptonSVMC.h deleted file mode 100644 index 37c58fe8d53..00000000000 --- a/PWGEM/Dilepton/Core/DileptonSVMC.h +++ /dev/null @@ -1,2941 +0,0 @@ -// Copyright 2019-2020 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. -// -// ======================== -// -// This code runs loop over leptons in MC -// Please write to: daiki.sekihata@cern.ch - -#ifndef PWGEM_DILEPTON_CORE_DILEPTONSVMC_H_ -#define PWGEM_DILEPTON_CORE_DILEPTONSVMC_H_ - -#include "PWGEM/Dilepton/Core/DielectronCut.h" -#include "PWGEM/Dilepton/Core/DimuonCut.h" -#include "PWGEM/Dilepton/Core/EMEventCut.h" -#include "PWGEM/Dilepton/DataModel/EvSelFlags.h" -#include "PWGEM/Dilepton/DataModel/dileptonTables.h" -#include "PWGEM/Dilepton/Utils/EMTrackUtilities.h" -#include "PWGEM/Dilepton/Utils/EventHistograms.h" -#include "PWGEM/Dilepton/Utils/MCUtilities.h" -#include "PWGEM/Dilepton/Utils/PairUtilities.h" - -#include "Common/CCDB/RCTSelectionFlags.h" -#include "Common/Core/RecoDecay.h" -#include "Common/Core/fwdtrackUtilities.h" -#include "Common/Core/trackUtilities.h" -#include "Common/DataModel/Centrality.h" -#include "Common/DataModel/EventSelection.h" -#include "Common/DataModel/PIDResponseTPC.h" -#include "Common/DataModel/TrackSelectionTables.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -using MyCollisions = o2::soa::Join; -using MyCollision = MyCollisions::iterator; - -using MyMCCollisions = o2::soa::Join; -using MyMCCollision = MyMCCollisions::iterator; - -using MyMCElectrons = o2::soa::Join; -using MyMCElectron = MyMCElectrons::iterator; -using FilteredMyMCElectrons = o2::soa::Filtered; -using FilteredMyMCElectron = FilteredMyMCElectrons::iterator; - -using MyMCMuons = o2::soa::Join; -using MyMCMuon = MyMCMuons::iterator; -using FilteredMyMCMuons = o2::soa::Filtered; -using FilteredMyMCMuon = FilteredMyMCMuons::iterator; - -using MySmearedElectrons = o2::soa::Join; -using MySmearedElectron = MySmearedElectrons::iterator; - -using MySmearedMuons = o2::soa::Join; -using MySmearedMuon = MySmearedMuons::iterator; - -// template -template -struct DileptonSVMC { - - // Configurables - o2::framework::Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - o2::framework::Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; - o2::framework::Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - o2::framework::Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; - o2::framework::Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; - o2::framework::Configurable matCorrType{"matCorrType", 1, "0: none, 1: TGeo, 2: LUT, else: none"}; - - o2::framework::Configurable cfgEventGeneratorType{"cfgEventGeneratorType", -1, "if positive, select event generator type. i.e. gap or signal"}; - o2::framework::Configurable cfgCentEstimator{"cfgCentEstimator", 2, "FT0M:0, FT0A:1, FT0C:2"}; - o2::framework::Configurable cfgApplyWeightTTCA{"cfgApplyWeightTTCA", false, "flag to apply weighting by 1/N"}; - o2::framework::Configurable cfgFillUnfolding{"cfgFillUnfolding", false, "flag to fill histograms for unfolding"}; - o2::framework::Configurable cfgRequireTrueAssociation{"cfgRequireTrueAssociation", false, "flag to require true mc collision association"}; - o2::framework::Configurable cfgFillSeparateCharmHadronPairs{"cfgFillSeparateCharmHadronPairs", false, "flag to fill different ccbar pairs separately"}; - - o2::framework::ConfigurableAxis ConfMllBins{"ConfMllBins", {o2::framework::VARIABLE_WIDTH, 0.00, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.20, 0.21, 0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.30, 0.31, 0.32, 0.33, 0.34, 0.35, 0.36, 0.37, 0.38, 0.39, 0.40, 0.41, 0.42, 0.43, 0.44, 0.45, 0.46, 0.47, 0.48, 0.49, 0.50, 0.51, 0.52, 0.53, 0.54, 0.55, 0.56, 0.57, 0.58, 0.59, 0.60, 0.61, 0.62, 0.63, 0.64, 0.65, 0.66, 0.67, 0.68, 0.69, 0.70, 0.71, 0.72, 0.73, 0.74, 0.75, 0.76, 0.77, 0.78, 0.79, 0.80, 0.81, 0.82, 0.83, 0.84, 0.85, 0.86, 0.87, 0.88, 0.89, 0.90, 0.91, 0.92, 0.93, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99, 1.00, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.09, 1.10, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18, 1.19, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.10, 2.20, 2.30, 2.40, 2.50, 2.60, 2.70, 2.75, 2.80, 2.85, 2.90, 2.95, 3.00, 3.05, 3.10, 3.15, 3.20, 3.25, 3.30, 3.35, 3.40, 3.45, 3.50, 3.55, 3.60, 3.65, 3.70, 3.75, 3.80, 3.85, 3.90, 3.95, 4.00}, "mll bins for output histograms"}; - o2::framework::ConfigurableAxis ConfPtllBins{"ConfPtllBins", {o2::framework::VARIABLE_WIDTH, 0.00, 0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90, 1.00, 1.10, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.50, 3.00, 3.50, 4.00, 4.50, 5.00, 6.00, 7.00, 8.00, 9.00, 10.00}, "pTll bins for output histograms"}; - o2::framework::ConfigurableAxis ConfDCAllBins{"ConfDCAllBins", {o2::framework::VARIABLE_WIDTH, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0}, "DCAll bins for output histograms"}; - - o2::framework::ConfigurableAxis ConfDPtBins{"ConfDPtBins", {220, -1.0, +10.0}, "dpt bins for output histograms"}; - o2::framework::ConfigurableAxis ConfDCAllNarrowBins{"ConfDCAllNarrowBins", {200, 0.0, 10.0}, "narrow DCAll bins for output histograms"}; - o2::framework::ConfigurableAxis ConfTrackDCA{"ConfTrackDCA", {o2::framework::VARIABLE_WIDTH, -10, -9, -8, -7, -6, -5, -4.5, -4, -3.5, -3, -2.5, -2, -1.9, -1.8, -1.7, -1.6, -1.5, -1.4, -1.3, -1.2, -1.1, -1, -0.9, -0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2, 2.5, 3, 3.5, 4, 4.5, 5, 6, 7, 8, 9, 10}, "DCA binning for single tacks"}; - - o2::framework::ConfigurableAxis ConfYllBins{"ConfYllBins", {1, -1.f, +1.f}, "yll bins for output histograms"}; - o2::framework::ConfigurableAxis ConfLog10Chi2PCABins{"ConfLog10Chi2PCABins", {1, -12.f, 0.f}, "log10 of chi2PCA bins for output histograms"}; - o2::framework::ConfigurableAxis ConfDLBins{"ConfDLBins", {1, 0.f, 10.f}, "decay length bins for output histograms"}; - o2::framework::ConfigurableAxis ConfCPABins{"ConfCPABins", {o2::framework::VARIABLE_WIDTH, -1, -0.95, -0.9, -0.85, -0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.85, 0.9, 0.95, 1}, "cpa bins for output histograms"}; - - // o2::framework::ConfigurableAxis ConfMmumuBins{"ConfMmumuBins", {o2::framework::VARIABLE_WIDTH, 0.20, 0.21, 0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.30, 0.31, 0.32, 0.33, 0.34, 0.35, 0.36, 0.37, 0.38, 0.39, 0.40, 0.41, 0.42, 0.43, 0.44, 0.45, 0.46, 0.47, 0.48, 0.49, 0.50, 0.51, 0.52, 0.53, 0.54, 0.55, 0.56, 0.57, 0.58, 0.59, 0.60, 0.61, 0.62, 0.63, 0.64, 0.65, 0.66, 0.67, 0.68, 0.69, 0.70, 0.71, 0.72, 0.73, 0.74, 0.75, 0.76, 0.77, 0.78, 0.79, 0.80, 0.81, 0.82, 0.83, 0.84, 0.85, 0.86, 0.87, 0.88, 0.89, 0.90, 0.91, 0.92, 0.93, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99, 1.00, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.09, 1.10, 1.11,1.12,1.13,1.14,1.15,1.16,1.17,1.18,1.19, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.10, 2.20, 2.30, 2.40, 2.50, 2.60, 2.70, 2.75, 2.80, 2.85, 2.90, 2.95, 3.00, 3.05, 3.10, 3.15, 3.20, 3.25, 3.30, 3.35, 3.40, 3.45, 3.50, 3.55, 3.60, 3.65, 3.70, 3.75, 3.80, 3.85, 3.90, 3.95, 4.00, 4.10, 4.20, 4.30, 4.40, 4.50, 4.60, 4.70, 4.80, 4.90, 5.00, 5.10, 5.20, 5.30, 5.40, 5.50, 5.60, 5.70, 5.80, 5.90, 6.00, 6.10, 6.20, 6.30, 6.40, 6.50, 6.60, 6.70, 6.80, 6.90, 7.00, 7.10, 7.20, 7.30, 7.40, 7.50, 7.60, 7.70, 7.80, 7.90, 8.00, 8.10, 8.20, 8.30, 8.40, 8.50, 8.60, 8.70, 8.80, 8.90, 9.00, 9.10, 9.20, 9.30, 9.40, 9.50, 9.60, 9.70, 9.80, 9.90, 10.00, 10.10, 10.20, 10.30, 10.40, 10.50, 10.60, 10.70, 10.80, 10.90, 11.00, 11.50, 12.00}, "mmumu bins for output histograms"}; // for dimuon. one can copy bins here to hyperloop page. - - o2::framework::Configurable cfg_nbin_dphi_ee{"cfg_nbin_dphi_ee", 1, "number of bins for dphi_ee"}; // 36 - o2::framework::Configurable cfg_nbin_deta_ee{"cfg_nbin_deta_ee", 1, "number of bins for deta_ee"}; // 40 - // o2::framework::Configurable cfg_nbin_cos_theta_cs{"cfg_nbin_cos_theta_cs", 1, "number of bins for cos theta cs"}; // 10 - // o2::framework::Configurable cfg_nbin_phi_cs{"cfg_nbin_phi_cs", 1, "number of bins for phi cs"}; // 10 - o2::framework::Configurable cfg_nbin_aco{"cfg_nbin_aco", 1, "number of bins for acoplanarity"}; // 10 - o2::framework::Configurable cfg_nbin_asym_pt{"cfg_nbin_asym_pt", 1, "number of bins for pt asymmetry"}; // 10 - o2::framework::Configurable cfg_nbin_dphi_e_ee{"cfg_nbin_dphi_e_ee", 1, "number of bins for dphi_ee_e"}; // 18 - o2::framework::ConfigurableAxis ConfPolarizationCosThetaBins{"ConfPolarizationCosThetaBins", {1, -1.f, 1.f}, "cos(theta) bins for polarization analysis"}; - o2::framework::ConfigurableAxis ConfPolarizationPhiBins{"ConfPolarizationPhiBins", {1, -M_PI, M_PI}, "phi bins for polarization analysis"}; - o2::framework::Configurable cfgPolarizationFrame{"cfgPolarizationFrame", 0, "frame of polarization. 0:CS, 1:HX, else:FATAL"}; - o2::framework::ConfigurableAxis ConfPolarizationQuadMomBins{"ConfPolarizationQuadMomBins", {1, -0.5, 1}, "quadrupole moment bins for polarization analysis"}; // quardrupole moment <(3 x cos^2(theta) -1)/2> - - EMEventCut fEMEventCut; - struct : o2::framework::ConfigurableGroup { - std::string prefix = "eventcut_group"; - o2::framework::Configurable cfgZvtxMin{"cfgZvtxMin", -10.f, "min. Zvtx"}; - o2::framework::Configurable cfgZvtxMax{"cfgZvtxMax", +10.f, "max. Zvtx"}; - o2::framework::Configurable cfgRequireSel8{"cfgRequireSel8", false, "require sel8 in event cut"}; - o2::framework::Configurable cfgRequireFT0AND{"cfgRequireFT0AND", true, "require FT0AND in event cut"}; - o2::framework::Configurable cfgRequireNoTFB{"cfgRequireNoTFB", false, "require No time frame border in event cut"}; - o2::framework::Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", false, "require no ITS readout frame border in event cut"}; - o2::framework::Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", false, "require no same bunch pileup in event cut"}; - o2::framework::Configurable cfgRequireVertexITSTPC{"cfgRequireVertexITSTPC", false, "require Vertex ITSTPC in event cut"}; // ITS-TPC matched track contributes PV. - o2::framework::Configurable cfgRequireVertexTOFmatched{"cfgRequireVertexTOFmatched", false, "require Vertex TOFmatched in event cut"}; // ITS-TPC-TOF matched track contributes PV. - o2::framework::Configurable cfgRequireGoodZvtxFT0vsPV{"cfgRequireGoodZvtxFT0vsPV", false, "require good Zvtx between FT0 vs. PV in event cut"}; - o2::framework::Configurable cfgTrackOccupancyMin{"cfgTrackOccupancyMin", -2, "min. occupancy"}; - o2::framework::Configurable cfgTrackOccupancyMax{"cfgTrackOccupancyMax", 1000000000, "max. occupancy"}; - o2::framework::Configurable cfgFT0COccupancyMin{"cfgFT0COccupancyMin", -2, "min. FT0C occupancy"}; - o2::framework::Configurable cfgFT0COccupancyMax{"cfgFT0COccupancyMax", 1000000000, "max. FT0C occupancy"}; - o2::framework::Configurable cfgRequireNoCollInTimeRangeStandard{"cfgRequireNoCollInTimeRangeStandard", false, "require no collision in time range standard"}; - o2::framework::Configurable cfgRequireNoCollInTimeRangeStrict{"cfgRequireNoCollInTimeRangeStrict", false, "require no collision in time range strict"}; - o2::framework::Configurable cfgRequireNoCollInITSROFStandard{"cfgRequireNoCollInITSROFStandard", false, "require no collision in time range standard"}; - o2::framework::Configurable cfgRequireNoCollInITSROFStrict{"cfgRequireNoCollInITSROFStrict", false, "require no collision in time range strict"}; - o2::framework::Configurable cfgRequireNoHighMultCollInPrevRof{"cfgRequireNoHighMultCollInPrevRof", false, "require no HM collision in previous ITS ROF"}; - o2::framework::Configurable cfgRequireGoodITSLayer3{"cfgRequireGoodITSLayer3", false, "number of inactive chips on ITS layer 3 are below threshold "}; - o2::framework::Configurable cfgRequireGoodITSLayer0123{"cfgRequireGoodITSLayer0123", false, "number of inactive chips on ITS layers 0-3 are below threshold "}; - o2::framework::Configurable cfgRequireGoodITSLayersAll{"cfgRequireGoodITSLayersAll", false, "number of inactive chips on all ITS layers are below threshold "}; - - // for RCT - o2::framework::Configurable cfgRequireGoodRCT{"cfgRequireGoodRCT", false, "require good detector flag in run condtion table"}; - o2::framework::Configurable cfgRCTLabel{"cfgRCTLabel", "CBT_hadronPID", "select 1 [CBT, CBT_hadronPID, CBT_muon_glo] see O2Physics/Common/CCDB/RCTSelectionFlags.h"}; - o2::framework::Configurable cfgCheckZDC{"cfgCheckZDC", false, "set ZDC flag for PbPb"}; - o2::framework::Configurable cfgTreatLimitedAcceptanceAsBad{"cfgTreatLimitedAcceptanceAsBad", false, "reject all events where the detectors relevant for the specified Runlist are flagged as LimitedAcceptance"}; - - o2::framework::Configurable cfgCentMin{"cfgCentMin", -1, "min. centrality"}; - o2::framework::Configurable cfgCentMax{"cfgCentMax", 999.f, "max. centrality"}; - o2::framework::Configurable cfgNumContribMin{"cfgNumContribMin", 0, "min. numContrib"}; - o2::framework::Configurable cfgNumContribMax{"cfgNumContribMax", 65000, "max. numContrib"}; - } eventcuts; - - DielectronCut fDielectronCut; - struct : o2::framework::ConfigurableGroup { - std::string prefix = "dielectroncut_group"; - o2::framework::Configurable cfg_min_mass{"cfg_min_mass", 0.0, "min mass"}; - o2::framework::Configurable cfg_max_mass{"cfg_max_mass", 1e+10, "max mass"}; - o2::framework::Configurable cfg_min_pair_pt{"cfg_min_pair_pt", 0.0, "min pair pT"}; - o2::framework::Configurable cfg_max_pair_pt{"cfg_max_pair_pt", 1e+10, "max pair pT"}; - o2::framework::Configurable cfg_min_pair_y{"cfg_min_pair_y", -0.8, "min pair rapidity"}; - o2::framework::Configurable cfg_max_pair_y{"cfg_max_pair_y", +0.8, "max pair rapidity"}; - o2::framework::Configurable cfg_min_pair_dca3d{"cfg_min_pair_dca3d", 0.0, "min pair dca3d in sigma"}; - o2::framework::Configurable cfg_max_pair_dca3d{"cfg_max_pair_dca3d", 1e+10, "max pair dca3d in sigma"}; - o2::framework::Configurable cfg_apply_phiv{"cfg_apply_phiv", false, "flag to apply phiv cut"}; - o2::framework::Configurable cfg_phiv_slope{"cfg_phiv_slope", 0.0185, "slope for m vs. phiv"}; - o2::framework::Configurable cfg_phiv_intercept{"cfg_phiv_intercept", -0.0280, "intercept for m vs. phiv"}; - o2::framework::Configurable cfg_min_phiv{"cfg_min_phiv", 0.0, "min phiv (constant)"}; - o2::framework::Configurable cfg_max_phiv{"cfg_max_phiv", 3.2, "max phiv (constant)"}; - o2::framework::Configurable cfg_apply_detadphi{"cfg_apply_detadphi", false, "flag to apply deta-dphi elliptic cut at PV"}; - o2::framework::Configurable cfg_min_deta{"cfg_min_deta", 0.04, "min deta between 2 electrons (elliptic cut)"}; - o2::framework::Configurable cfg_min_dphi{"cfg_min_dphi", 0.12, "min dphi between 2 electrons (elliptic cut)"}; - o2::framework::Configurable cfg_min_opang{"cfg_min_opang", 0.0, "min opening angle"}; - o2::framework::Configurable cfg_max_opang{"cfg_max_opang", 6.4, "max opening angle"}; - // o2::framework::Configurable cfg_require_diff_sides{"cfg_require_diff_sides", false, "flag to require 2 tracks are from different sides."}; - - o2::framework::Configurable cfg_apply_cuts_from_prefilter{"cfg_apply_cuts_from_prefilter", false, "flag to apply prefilter set when producing derived data"}; - o2::framework::Configurable cfg_prefilter_bits{"cfg_prefilter_bits", 0, "prefilter bits [kNone : 0, kElFromPC : 1, kElFromPi0_20MeV : 2, kElFromPi0_40MeV : 4, kElFromPi0_60MeV : 8, kElFromPi0_80MeV : 16, kElFromPi0_100MeV : 32, kElFromPi0_120MeV : 64, kElFromPi0_140MeV : 128] Please consider logical-OR among them."}; // see PairUtilities.h - - o2::framework::Configurable cfg_apply_cuts_from_prefilter_derived{"cfg_apply_cuts_from_prefilter_derived", false, "flag to apply phiv cut inherited from prefilter"}; - o2::framework::Configurable cfg_prefilter_bits_derived{"cfg_prefilter_bits_derived", 0, "prefilter bits [kNone : 0, kMee : 1, kPhiV : 2, kSplitOrMergedTrackLS : 4, kSplitOrMergedTrackULS : 8] Please consider logical-OR among them."}; // see PairUtilities.h - - o2::framework::Configurable cfg_min_pt_track{"cfg_min_pt_track", 0.8, "min pT for single track"}; - o2::framework::Configurable cfg_max_pt_track{"cfg_max_pt_track", 1e+10, "max pT for single track"}; - o2::framework::Configurable cfg_min_eta_track{"cfg_min_eta_track", -0.8, "min eta for single track"}; - o2::framework::Configurable cfg_max_eta_track{"cfg_max_eta_track", +0.8, "max eta for single track"}; - o2::framework::Configurable cfg_min_phi_track{"cfg_min_phi_track", 0.f, "min phi for single track"}; - o2::framework::Configurable cfg_max_phi_track{"cfg_max_phi_track", 6.3, "max phi for single track"}; - o2::framework::Configurable cfg_mirror_phi_track{"cfg_mirror_phi_track", false, "mirror the phi cut around Pi, min and max Phi should be in 0-Pi"}; - o2::framework::Configurable cfg_reject_phi_track{"cfg_reject_phi_track", false, "reject the phi interval"}; - o2::framework::Configurable cfg_min_ncluster_tpc{"cfg_min_ncluster_tpc", 0, "min ncluster tpc"}; - o2::framework::Configurable cfg_min_ncluster_its{"cfg_min_ncluster_its", 5, "min ncluster its"}; - o2::framework::Configurable cfg_min_ncrossedrows{"cfg_min_ncrossedrows", 100, "min ncrossed rows"}; - o2::framework::Configurable cfg_max_frac_shared_clusters_tpc{"cfg_max_frac_shared_clusters_tpc", 999.f, "max fraction of shared clusters in TPC"}; - o2::framework::Configurable cfg_max_chi2tpc{"cfg_max_chi2tpc", 4.0, "max chi2/NclsTPC"}; - o2::framework::Configurable cfg_max_chi2its{"cfg_max_chi2its", 5.0, "max chi2/NclsITS"}; - o2::framework::Configurable cfg_max_chi2tof{"cfg_max_chi2tof", 1e+10, "max chi2 TOF"}; - o2::framework::Configurable cfg_max_dcaxy{"cfg_max_dcaxy", 1.0, "max dca XY for single track in cm"}; - o2::framework::Configurable cfg_max_dcaz{"cfg_max_dcaz", 1.0, "max dca Z for single track in cm"}; - o2::framework::Configurable cfg_max_dca_sigma{"cfg_max_dca_sigma", 1e+10, "max dca for single track in sigma"}; - o2::framework::Configurable cfg_require_itsib_any{"cfg_require_itsib_any", false, "flag to require ITS ib any hits"}; - o2::framework::Configurable cfg_require_itsib_1st{"cfg_require_itsib_1st", true, "flag to require ITS ib 1st hit"}; - o2::framework::Configurable cfg_min_its_cluster_size{"cfg_min_its_cluster_size", 0.f, "min ITS cluster size"}; - o2::framework::Configurable cfg_max_its_cluster_size{"cfg_max_its_cluster_size", 16.f, "max ITS cluster size"}; - // o2::framework::Configurable cfg_min_rel_diff_pin{"cfg_min_rel_diff_pin", -1e+10, "min rel. diff. between pin and ppv"}; - // o2::framework::Configurable cfg_max_rel_diff_pin{"cfg_max_rel_diff_pin", +1e+10, "max rel. diff. between pin and ppv"}; - o2::framework::Configurable cfgRefR{"cfgRefR", 0.50, "ref. radius (m) for calculating phi position"}; // 0.50 +/- 0.06 can be syst. unc. - o2::framework::Configurable cfg_min_phiposition_track{"cfg_min_phiposition_track", 0.f, "min phi position for single track at certain radius"}; - o2::framework::Configurable cfg_max_phiposition_track{"cfg_max_phiposition_track", 6.3, "max phi position for single track at certain radius"}; - o2::framework::Configurable cfgDCAType{"cfgDCAType", 0, "type of DCA for output. 0:3D, 1:XY, 2:Z, else:3D"}; - - o2::framework::Configurable cfg_pid_scheme{"cfg_pid_scheme", static_cast(DielectronCut::PIDSchemes::kTPChadrejORTOFreq), "pid scheme [kTOFreq : 0, kTPChadrej : 1, kTPChadrejORTOFreq : 2, kTPConly : 3, kTOFif = 4, kPIDML = 5]"}; - o2::framework::Configurable cfg_min_TPCNsigmaEl{"cfg_min_TPCNsigmaEl", -2.0, "min. TPC n sigma for electron inclusion"}; - o2::framework::Configurable cfg_max_TPCNsigmaEl{"cfg_max_TPCNsigmaEl", +3.0, "max. TPC n sigma for electron inclusion"}; - // o2::framework::Configurable cfg_min_TPCNsigmaMu{"cfg_min_TPCNsigmaMu", -0.0, "min. TPC n sigma for muon exclusion"}; - // o2::framework::Configurable cfg_max_TPCNsigmaMu{"cfg_max_TPCNsigmaMu", +0.0, "max. TPC n sigma for muon exclusion"}; - o2::framework::Configurable cfg_min_TPCNsigmaPi{"cfg_min_TPCNsigmaPi", -1e+10, "min. TPC n sigma for pion exclusion"}; - o2::framework::Configurable cfg_max_TPCNsigmaPi{"cfg_max_TPCNsigmaPi", +3.0, "max. TPC n sigma for pion exclusion"}; - o2::framework::Configurable cfg_min_TPCNsigmaKa{"cfg_min_TPCNsigmaKa", -3.0, "min. TPC n sigma for kaon exclusion"}; - o2::framework::Configurable cfg_max_TPCNsigmaKa{"cfg_max_TPCNsigmaKa", +3.0, "max. TPC n sigma for kaon exclusion"}; - o2::framework::Configurable cfg_min_TPCNsigmaPr{"cfg_min_TPCNsigmaPr", -3.0, "min. TPC n sigma for proton exclusion"}; - o2::framework::Configurable cfg_max_TPCNsigmaPr{"cfg_max_TPCNsigmaPr", +3.0, "max. TPC n sigma for proton exclusion"}; - o2::framework::Configurable cfg_min_TOFNsigmaEl{"cfg_min_TOFNsigmaEl", -3.0, "min. TOF n sigma for electron inclusion"}; - o2::framework::Configurable cfg_max_TOFNsigmaEl{"cfg_max_TOFNsigmaEl", +3.0, "max. TOF n sigma for electron inclusion"}; - o2::framework::Configurable cfg_min_pin_pirejTPC{"cfg_min_pin_pirejTPC", 0.f, "min. pin for pion rejection in TPC"}; - o2::framework::Configurable cfg_max_pin_pirejTPC{"cfg_max_pin_pirejTPC", 1e+10, "max. pin for pion rejection in TPC"}; - o2::framework::Configurable enableTTCA{"enableTTCA", true, "Flag to enable or disable TTCA"}; - - // configuration for PID ML - o2::framework::Configurable> onnxFileNames{"onnxFileNames", std::vector{"filename"}, "ONNX file names for each bin (if not from CCDB full path)"}; - o2::framework::Configurable> onnxPathsCCDB{"onnxPathsCCDB", std::vector{"path"}, "Paths of models on CCDB"}; - o2::framework::Configurable> binsMLPID{"binsMLPID", std::vector{0.1, 0.15, 0.2, 0.25, 0.4, 0.8, 1.6, 2.0, 4.0, 20.f}, "Bin limits for ML application"}; - o2::framework::Configurable> cutsMLPID{"cutsMLPID", std::vector{0.97, 0.97, 0.97, 0.8, 0.95, 0.95, 0.8, 0.8, 0.8}, "ML cuts per bin"}; - o2::framework::Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature"}, "Names of ML model input features"}; - o2::framework::Configurable nameBinningFeature{"nameBinningFeature", "pt", "Names of ML model binning feature"}; - o2::framework::Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB. Exceptions: > 0 for the specific timestamp, 0 gets the run dependent timestamp"}; - o2::framework::Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; - o2::framework::Configurable enableOptimizations{"enableOptimizations", false, "Enables the ONNX extended model-optimization: sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED)"}; - } dielectroncuts; - - DimuonCut fDimuonCut; - struct : o2::framework::ConfigurableGroup { - std::string prefix = "dimuoncut_group"; - o2::framework::Configurable cfg_min_mass{"cfg_min_mass", 0.0, "min mass"}; - o2::framework::Configurable cfg_max_mass{"cfg_max_mass", 1e+10, "max mass"}; - o2::framework::Configurable cfg_min_pair_pt{"cfg_min_pair_pt", 0.0, "min pair pt"}; - o2::framework::Configurable cfg_max_pair_pt{"cfg_max_pair_pt", 1e+10, "max pair pt"}; - o2::framework::Configurable cfg_min_pair_y{"cfg_min_pair_y", -4.0, "min pair rapidity"}; - o2::framework::Configurable cfg_max_pair_y{"cfg_max_pair_y", -2.5, "max pair rapidity"}; - o2::framework::Configurable cfg_min_pair_dcaxy{"cfg_min_pair_dcaxy", 0.0, "min pair dca3d in sigma"}; - o2::framework::Configurable cfg_max_pair_dcaxy{"cfg_max_pair_dcaxy", 1e+10, "max pair dca3d in sigma"}; - o2::framework::Configurable cfg_apply_detadphi{"cfg_apply_detadphi", false, "flag to apply deta-dphi elliptic cut"}; - o2::framework::Configurable cfg_min_deta{"cfg_min_deta", 0.02, "min deta between 2 muons (elliptic cut)"}; - o2::framework::Configurable cfg_min_dphi{"cfg_min_dphi", 0.02, "min dphi between 2 muons (elliptic cut)"}; - - o2::framework::Configurable cfg_track_type{"cfg_track_type", 3, "muon track type [0: MFT-MCH-MID, 3: MCH-MID]"}; - o2::framework::Configurable cfg_min_pt_track{"cfg_min_pt_track", 0.8, "min pT for single track"}; - o2::framework::Configurable cfg_max_pt_track{"cfg_max_pt_track", 1e+10, "max pT for single track"}; - o2::framework::Configurable cfg_min_eta_track{"cfg_min_eta_track", -4.0, "min eta for single track"}; - o2::framework::Configurable cfg_max_eta_track{"cfg_max_eta_track", -2.5, "max eta for single track"}; - o2::framework::Configurable cfg_min_phi_track{"cfg_min_phi_track", 0.f, "max phi for single track"}; - o2::framework::Configurable cfg_max_phi_track{"cfg_max_phi_track", 6.3, "max phi for single track"}; - o2::framework::Configurable cfg_min_ncluster_mft{"cfg_min_ncluster_mft", 5, "min ncluster MFT"}; - o2::framework::Configurable cfg_min_ncluster_mch{"cfg_min_ncluster_mch", 5, "min ncluster MCH"}; - o2::framework::Configurable cfg_max_chi2{"cfg_max_chi2", 1e+6, "max chi2/ndf"}; - o2::framework::Configurable cfg_max_chi2mft{"cfg_max_chi2mft", 1e+6, "max chi2/ndf"}; - // o2::framework::Configurable cfg_max_matching_chi2_mftmch{"cfg_max_matching_chi2_mftmch", 40, "max chi2 for MFT-MCH matching"}; - o2::framework::Configurable cfg_border_pt_for_chi2mchmft{"cfg_border_pt_for_chi2mchmft", 0, "border pt for different max chi2 for MFT-MCH matching"}; - o2::framework::Configurable cfg_max_matching_chi2_mftmch_lowPt{"cfg_max_matching_chi2_mftmch_lowPt", 8, "max chi2 for MFT-MCH matching for low pT"}; - o2::framework::Configurable cfg_max_matching_chi2_mftmch_highPt{"cfg_max_matching_chi2_mftmch_highPt", 40, "max chi2 for MFT-MCH matching for high pT"}; - o2::framework::Configurable cfg_max_matching_chi2_mchmid{"cfg_max_matching_chi2_mchmid", 1e+10, "max chi2 for MCH-MID matching"}; - o2::framework::Configurable cfg_max_dcaxy{"cfg_max_dcaxy", 1e+10, "max dca XY for single track in cm"}; - o2::framework::Configurable cfg_min_rabs{"cfg_min_rabs", 17.6, "min Radius at the absorber end"}; - o2::framework::Configurable cfg_max_rabs{"cfg_max_rabs", 89.5, "max Radius at the absorber end"}; - o2::framework::Configurable cfg_max_diff_chi2_mftmch{"cfg_max_diff_chi2_mftmch", -1.f, "max. diff chi2MatchingMCHMFT between the best and the 2nd best matched candidates"}; - o2::framework::Configurable enableTTCA{"enableTTCA", true, "Flag to enable or disable TTCA"}; - o2::framework::Configurable cfg_max_relDPt_wrt_matchedMCHMID{"cfg_max_relDPt_wrt_matchedMCHMID", 1e+10f, "max. relative dpt between MFT-MCH-MID and MCH-MID"}; - o2::framework::Configurable cfg_max_DEta_wrt_matchedMCHMID{"cfg_max_DEta_wrt_matchedMCHMID", 1e+10f, "max. deta between MFT-MCH-MID and MCH-MID"}; - o2::framework::Configurable cfg_max_DPhi_wrt_matchedMCHMID{"cfg_max_DPhi_wrt_matchedMCHMID", 1e+10f, "max. dphi between MFT-MCH-MID and MCH-MID"}; - o2::framework::Configurable requireMFTHitMap{"requireMFTHitMap", false, "flag to apply MFT hit map"}; - o2::framework::Configurable> requiredMFTDisks{"requiredMFTDisks", std::vector{0}, "hit map on MFT disks [0,1,2,3,4]. logical-OR of each double-sided disk"}; - o2::framework::Configurable acceptOnlyCorrectMatch{"acceptOnlyCorrectMatch", false, "flag to accept only correct match between MFT and MCH-MID"}; // this is only for MC study, as we don't know correct match in data. - o2::framework::Configurable acceptOnlyWrongMatch{"acceptOnlyWrongMatch", false, "flag to accept only wrong match between MFT and MCH-MID"}; // this is only for MC study, as we don't know correct match in data. - o2::framework::Configurable acceptOnlyXORMatching{"acceptOnlyXORMatching", false, "flag to accept only correct-wrong XOR pairs between MFT and MCH-MID"}; // this is only for MC study, as we don't know correct match in data. - } dimuoncuts; - - struct : o2::framework::ConfigurableGroup { - std::string prefix = "dfGroup"; - o2::framework::Configurable useAbsDCA{"useAbsDCA", true, "Minimise abs. distance rather than chi2"}; - o2::framework::Configurable useWeightedFinalPCA{"useWeightedFinalPCA", false, "Recalculate vertex position using track covariances, effective only if useAbsDCA is true"}; - o2::framework::Configurable maxR{"maxR", 20.f, "reject PCA's above this radius"}; - o2::framework::Configurable maxDZIni{"maxDZIni", 4.f, "reject (if > 0) PCA candidate if tracks DZ exceeds threshold"}; - o2::framework::Configurable minParamChange{"minParamChange", 1e-3, "stop iterations if largest change of any X is smaller than this"}; - o2::framework::Configurable minRelChi2Change{"minRelChi2Change", 0.9, "stop iterations is chi2/chi2old > this"}; - o2::framework::Configurable propagateToPCA{"propagateToPCA", true, "create tracks version propagated to PCA"}; - o2::framework::Configurable maxLog10Chi2PCA{"maxLog10Chi2PCA", 10, "max. log10(chi2PCA) for dilepton pair"}; - } dfGroup; // for DCAFitterN - - struct : o2::framework::ConfigurableGroup { - std::string prefix = "fdfGroup"; - o2::framework::Configurable useAbsDCA{"useAbsDCA", true, "Minimise abs. distance rather than chi2"}; - o2::framework::Configurable useWeightedFinalPCA{"useWeightedFinalPCA", false, "Recalculate vertex position using track covariances, effective only if useAbsDCA is true"}; - o2::framework::Configurable maxR{"maxR", 20.f, "reject PCA's above this radius"}; - o2::framework::Configurable maxDXIni{"maxDXIni", 4.f, "reject (if > 0) PCA candidate if tracks DZ exceeds threshold"}; - o2::framework::Configurable minParamChange{"minParamChange", 1e-3, "stop iterations if largest change of any X is smaller than this"}; - o2::framework::Configurable minRelChi2Change{"minRelChi2Change", 0.9, "stop iterations is chi2/chi2old > this"}; - o2::framework::Configurable propagateToPCA{"propagateToPCA", true, "create tracks version propagated to PCA"}; - o2::framework::Configurable maxLog10Chi2PCA{"maxLog10Chi2PCA", 10, "max. log10(chi2PCA) for dilepton pair"}; - } fdfGroup; // for FwdDCAFitterN - - o2::vertexing::DCAFitterN<2> mDCAFitter; - o2::vertexing::FwdDCAFitterN<2> mFwdDCAFitter; - - o2::aod::rctsel::RCTFlagsChecker rctChecker; - o2::framework::Service ccdb; - // o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; - int mRunNumber{0}; - float d_bz{0}; - - struct : o2::framework::ConfigurableGroup { - std::string prefix = "mctrackcut_group"; - o2::framework::Configurable min_mcPt{"min_mcPt", 0.1, "min. MC pT for generated single lepton"}; - o2::framework::Configurable max_mcPt{"max_mcPt", 1e+10, "max. MC pT for generated single lepton"}; - o2::framework::Configurable min_mcEta{"min_mcEta", -0.8, "max. MC eta for generated single lepton"}; - o2::framework::Configurable max_mcEta{"max_mcEta", +0.8, "max. MC eta for generated single lepton"}; - } mctrackcuts; - - o2::framework::HistogramRegistry fRegistry{"output", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject, false, false}; - static constexpr std::string_view event_cut_types[2] = {"before/", "after/"}; - static constexpr std::string_view pair_sign_types[3] = {"uls/", "lspp/", "lsmm/"}; - static constexpr std::string_view dilepton_source_types[20] = { - "sm/Photon/", // 0 - "sm/PromptPi0/", // 1 - "sm/NonPromptPi0/", // 2 - "sm/Eta/", // 3 - "sm/EtaPrime/", // 4 - "sm/Rho/", // 5 - "sm/Omega/", // 6 - "sm/Phi/", // 7 - "sm/PromptJPsi/", // 8 - "sm/NonPromptJPsi/", // 9 - "sm/PromptPsi2S/", // 10 - "sm/NonPromptPsi2S/", // 11 - "sm/Upsilon1S/", // 12 - "sm/Upsilon2S/", // 13 - "sm/Upsilon3S/", // 14 - "ccbar/c2l_c2l/", // 15 - "bbbar/b2l_b2l/", // 16 - "bbbar/b2c2l_b2c2l/", // 17 - "bbbar/b2c2l_b2l_sameb/", // 18 - "bbbar/b2c2l_b2l_diffb/" // 19 - }; // unordered_map is better, but cannot be constexpr. - static constexpr std::string_view unfolding_dilepton_source_types[3] = {"sm/", "ccbar/", "bbbar/"}; - - ~DileptonSVMC() {} - - void addhistograms() - { - // event info - o2::aod::pwgem::dilepton::utils::eventhistogram::addEventHistograms(&fRegistry); - fRegistry.add("MCEvent/before/hZvtx", "mc vertex z; Z_{vtx} (cm)", o2::framework::HistType::kTH1F, {{100, -50, +50}}, false); - fRegistry.add("MCEvent/before/hZvtx_rec", "rec. mc vertex z; Z_{vtx} (cm)", o2::framework::HistType::kTH1F, {{100, -50, +50}}, false); - fRegistry.addClone("MCEvent/before/", "MCEvent/after/"); - - std::string mass_axis_title = "m_{ll} (GeV/c^{2})"; - std::string pair_pt_axis_title = "p_{T,ll} (GeV/c)"; - std::string pair_y_axis_title = "y_{ll}"; - std::string pair_dca_axis_title = "DCA_{ll} (#sigma)"; - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - mass_axis_title = "m_{ee} (GeV/c^{2})"; - pair_pt_axis_title = "p_{T,ee} (GeV/c)"; - pair_y_axis_title = "y_{ee}"; - pair_dca_axis_title = "DCA_{ee}^{3D} (#sigma)"; - if (dielectroncuts.cfgDCAType == 1) { - pair_dca_axis_title = "DCA_{ee}^{XY} (#sigma)"; - } else if (dielectroncuts.cfgDCAType == 2) { - pair_dca_axis_title = "DCA_{ee}^{Z} (#sigma)"; - } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - mass_axis_title = "m_{#mu#mu} (GeV/c^{2})"; - pair_pt_axis_title = "p_{T,#mu#mu} (GeV/c)"; - pair_y_axis_title = "y_{#mu#mu}"; - pair_dca_axis_title = "DCA_{#mu#mu}^{XY} (#sigma)"; - } - - // pair info - const o2::framework::AxisSpec axis_mass{ConfMllBins, mass_axis_title}; - const o2::framework::AxisSpec axis_pt{ConfPtllBins, pair_pt_axis_title}; - const o2::framework::AxisSpec axis_y{ConfYllBins, pair_y_axis_title}; - const o2::framework::AxisSpec axis_dca{ConfDCAllBins, pair_dca_axis_title}; - const o2::framework::AxisSpec axis_pt_meson{ConfPtllBins, "p_{T}^{VM} (GeV/c)"}; // for omega, phi meson pT spectra - const o2::framework::AxisSpec axis_y_meson{ConfYllBins, "y^{VM}"}; // for omega, phi meson pT spectra - const o2::framework::AxisSpec axis_chi2PCA{ConfLog10Chi2PCABins, "log_{10}(#chi^{2}_{PCA})"}; - const o2::framework::AxisSpec axis_dl{ConfDLBins, "decay length (#sigma)"}; - const o2::framework::AxisSpec axis_cpa{ConfCPABins, "cos(#theta_{p})"}; - - const o2::framework::AxisSpec axis_dca_narrow{ConfDCAllNarrowBins, pair_dca_axis_title}; - const o2::framework::AxisSpec axis_dpt{ConfDPtBins, "#Delta p_{T,1}^{gen-rec} + #Delta p_{T,2}^{gen-rec} (GeV/c)"}; - const o2::framework::AxisSpec axis_dca_track1{ConfTrackDCA, "DCA_{e,1}^{Z} (#sigma)"}; - const o2::framework::AxisSpec axis_dca_track2{ConfTrackDCA, "DCA_{e,2}^{Z} (#sigma)"}; - - std::string frameName = "CS"; - if (cfgPolarizationFrame == 0) { - frameName = "CS"; - } else if (cfgPolarizationFrame == 1) { - frameName = "HX"; - } else { - LOG(fatal) << "set 0 or 1 to cfgPolarizationFrame!"; - } - - const o2::framework::AxisSpec axis_dphi_ee{cfg_nbin_dphi_ee, -M_PI / 2., 3. / 2. * M_PI, "#Delta#varphi = #varphi_{l1} - #varphi_{l2} (rad.)"}; // for kHFll - const o2::framework::AxisSpec axis_deta_ee{cfg_nbin_deta_ee, -2., 2., "#Delta#eta = #eta_{l1} - #eta_{l2}"}; // for kHFll - const o2::framework::AxisSpec axis_cos_theta_pol{ConfPolarizationCosThetaBins, Form("cos(#theta^{%s})", frameName.data())}; // for kPolarization, kUPC - const o2::framework::AxisSpec axis_phi_pol{ConfPolarizationPhiBins, Form("#varphi^{%s} (rad.)", frameName.data())}; // for kPolarization - const o2::framework::AxisSpec axis_quadmom{ConfPolarizationQuadMomBins, Form("#frac{3 cos^{2}(#theta^{%s}) -1}{2}", frameName.data())}; // for kPolarization - const o2::framework::AxisSpec axis_aco{cfg_nbin_aco, 0, 1.f, "#alpha = 1 - #frac{|#varphi_{l^{+}} - #varphi_{l^{-}}|}{#pi}"}; // for kUPC - const o2::framework::AxisSpec axis_asym_pt{cfg_nbin_asym_pt, 0, 1.f, "A = #frac{|p_{T,l^{+}} - p_{T,l^{-}}|}{|p_{T,l^{+}} + p_{T,l^{-}}|}"}; // for kUPC - const o2::framework::AxisSpec axis_dphi_e_ee{cfg_nbin_dphi_e_ee, 0, M_PI, "#Delta#varphi = #varphi_{l} - #varphi_{ll} (rad.)"}; // for kUPC - - // generated info - fRegistry.add("Generated/sm/PromptPi0/uls/hs", "gen. dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_y, axis_dphi_ee, axis_deta_ee, axis_cos_theta_pol, axis_phi_pol, axis_quadmom, axis_aco, axis_asym_pt, axis_dphi_e_ee}, true); - fRegistry.addClone("Generated/sm/PromptPi0/uls/", "Generated/sm/PromptPi0/lspp/"); - fRegistry.addClone("Generated/sm/PromptPi0/uls/", "Generated/sm/PromptPi0/lsmm/"); - - fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/NonPromptPi0/"); - fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/Eta/"); - fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/EtaPrime/"); - fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/Rho/"); - fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/Omega/"); - fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/Omega2ll/"); - fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/Phi/"); - fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/Phi2ll/"); - fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/PromptJPsi/"); - fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/NonPromptJPsi/"); - fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/PromptPsi2S/"); - fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/NonPromptPsi2S/"); - // fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/Upsilon1S/"); - // fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/Upsilon2S/"); - // fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/Upsilon3S/"); - - fRegistry.add("Generated/ccbar/c2l_c2l/uls/hs", "generated dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_y, axis_dphi_ee, axis_deta_ee, axis_cos_theta_pol, axis_phi_pol, axis_quadmom, axis_aco, axis_asym_pt, axis_dphi_e_ee}, true); - fRegistry.addClone("Generated/ccbar/c2l_c2l/uls/", "Generated/ccbar/c2l_c2l/lspp/"); - fRegistry.addClone("Generated/ccbar/c2l_c2l/uls/", "Generated/ccbar/c2l_c2l/lsmm/"); - - fRegistry.addClone("Generated/ccbar/c2l_c2l/", "Generated/bbbar/b2l_b2l/"); - fRegistry.addClone("Generated/ccbar/c2l_c2l/", "Generated/bbbar/b2c2l_b2c2l/"); - fRegistry.addClone("Generated/ccbar/c2l_c2l/", "Generated/bbbar/b2c2l_b2l_sameb/"); - fRegistry.addClone("Generated/ccbar/c2l_c2l/", "Generated/bbbar/b2c2l_b2l_diffb/"); // LS - - // for charmed hadrons // create 28 combinations - static constexpr std::string_view charmed_mesons[] = {"Dplus", "D0", "Dsplus"}; // 411, 421, 431 - static constexpr std::string_view anti_charmed_mesons[] = {"Dminus", "D0bar", "Dsminus"}; - const int nm = sizeof(charmed_mesons) / sizeof(charmed_mesons[0]); - static constexpr std::string_view charmed_baryons[] = {"Lcplus", "Xicplus", "Xic0", "Omegac0"}; // 4122, 4232, 4132, 4332 - static constexpr std::string_view anti_charmed_baryons[] = {"Lcminus", "Xicminus", "Xic0bar", "Omegac0bar"}; - const int nb = sizeof(charmed_baryons) / sizeof(charmed_baryons[0]); - static constexpr std::string_view sum_charmed_mesons[] = {"Dpm", "D0", "Dspm"}; - static constexpr std::string_view sum_charmed_baryons[] = {"Lcpm", "Xicpm", "Xic0", "Omegac0"}; - - if (cfgFillSeparateCharmHadronPairs) { - for (int im = 0; im < nm; im++) { - fRegistry.addClone("Generated/ccbar/c2l_c2l/", Form("Generated/ccbar/%s_%s/", charmed_mesons[im].data(), anti_charmed_mesons[im].data())); - } - for (int ib = 0; ib < nb; ib++) { - fRegistry.addClone("Generated/ccbar/c2l_c2l/", Form("Generated/ccbar/%s_%s/", charmed_baryons[ib].data(), anti_charmed_baryons[ib].data())); - } - for (int im1 = 0; im1 < nm - 1; im1++) { - for (int im2 = im1 + 1; im2 < nm; im2++) { - fRegistry.addClone("Generated/ccbar/c2l_c2l/", Form("Generated/ccbar/%s_%s/", sum_charmed_mesons[im1].data(), sum_charmed_mesons[im2].data())); - } - } - for (int ib1 = 0; ib1 < nb - 1; ib1++) { - for (int ib2 = ib1 + 1; ib2 < nb; ib2++) { - fRegistry.addClone("Generated/ccbar/c2l_c2l/", Form("Generated/ccbar/%s_%s/", sum_charmed_baryons[ib1].data(), sum_charmed_baryons[ib2].data())); - } - } - for (int im = 0; im < nm; im++) { - for (int ib = 0; ib < nb; ib++) { - fRegistry.addClone("Generated/ccbar/c2l_c2l/", Form("Generated/ccbar/%s_%s/", sum_charmed_mesons[im].data(), sum_charmed_baryons[ib].data())); - } - } - } - - // evaluate acceptance for polarization - fRegistry.add("Generated/VM/All/Phi/hs", "gen. VM #rightarrow ll", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_y, axis_cos_theta_pol, axis_phi_pol, axis_quadmom}, true); - fRegistry.addClone("Generated/VM/All/Phi/", "Generated/VM/All/Rho/"); - fRegistry.addClone("Generated/VM/All/Phi/", "Generated/VM/All/Omega/"); - fRegistry.addClone("Generated/VM/All/Phi/", "Generated/VM/All/PromptJPsi/"); - fRegistry.addClone("Generated/VM/All/Phi/", "Generated/VM/All/NonPromptJPsi/"); - fRegistry.addClone("Generated/VM/All/", "Generated/VM/Acc/"); - - // reconstructed pair info - fRegistry.add("Pair/sm/Photon/uls/hs", "rec. dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_y, axis_dphi_ee, axis_deta_ee, axis_cos_theta_pol, axis_phi_pol, axis_quadmom, axis_aco, axis_asym_pt, axis_dphi_e_ee, axis_dca, axis_chi2PCA, axis_dl, axis_cpa}, true); - - fRegistry.addClone("Pair/sm/Photon/uls/", "Pair/sm/Photon/lspp/"); - fRegistry.addClone("Pair/sm/Photon/uls/", "Pair/sm/Photon/lsmm/"); - fRegistry.addClone("Pair/sm/Photon/", "Pair/sm/PromptPi0/"); - fRegistry.addClone("Pair/sm/Photon/", "Pair/sm/NonPromptPi0/"); - fRegistry.addClone("Pair/sm/Photon/", "Pair/sm/Eta/"); - fRegistry.addClone("Pair/sm/Photon/", "Pair/sm/EtaPrime/"); - fRegistry.addClone("Pair/sm/Photon/", "Pair/sm/Rho/"); - fRegistry.addClone("Pair/sm/Photon/", "Pair/sm/Omega/"); - fRegistry.addClone("Pair/sm/Photon/", "Pair/sm/Omega2ll/"); - fRegistry.addClone("Pair/sm/Photon/", "Pair/sm/Phi/"); - fRegistry.addClone("Pair/sm/Photon/", "Pair/sm/Phi2ll/"); - fRegistry.addClone("Pair/sm/Photon/", "Pair/sm/PromptJPsi/"); - fRegistry.addClone("Pair/sm/Photon/", "Pair/sm/NonPromptJPsi/"); - fRegistry.addClone("Pair/sm/Photon/", "Pair/sm/PromptPsi2S/"); - fRegistry.addClone("Pair/sm/Photon/", "Pair/sm/NonPromptPsi2S/"); - - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - fRegistry.add("Pair/sm/Photon/uls/hMvsPhiV", "m_{ee} vs. #varphi_{V};#varphi (rad.);m_{ee} (GeV/c^{2})", o2::framework::HistType::kTH2F, {{90, 0, M_PI}, {100, 0.0f, 1.0f}}, true); - fRegistry.add("Pair/sm/Photon/uls/hMvsRxy", "m_{ee} vs. r_{xy};r_{xy}^{true} (cm);m_{ee} (GeV/c^{2})", o2::framework::HistType::kTH2F, {{100, 0, 100}, {100, 0.0f, 1.0f}}, true); - for (const auto& strSign : pair_sign_types) { - fRegistry.add(std::format("Pair/sm/PromptPi0/{0}hMvsPhiV", strSign), "m_{ee} vs. #varphi_{V};#varphi (rad.);m_{ee} (GeV/c^{2})", o2::framework::HistType::kTH2F, {{90, 0, M_PI}, {100, 0.0f, 1.0f}}, true); - fRegistry.add(std::format("Pair/sm/NonPromptPi0/{0}hMvsPhiV", strSign), "m_{ee} vs. #varphi_{V};#varphi (rad.);m_{ee} (GeV/c^{2})", o2::framework::HistType::kTH2F, {{90, 0, M_PI}, {100, 0.0f, 1.0f}}, true); - } - } - - fRegistry.add("Pair/ccbar/c2l_c2l/uls/hs", "rec. dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_y, axis_dphi_ee, axis_deta_ee, axis_cos_theta_pol, axis_phi_pol, axis_quadmom, axis_aco, axis_asym_pt, axis_dphi_e_ee, axis_dca, axis_chi2PCA, axis_dl, axis_cpa}, true); - fRegistry.addClone("Pair/ccbar/c2l_c2l/uls/", "Pair/ccbar/c2l_c2l/lspp/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/uls/", "Pair/ccbar/c2l_c2l/lsmm/"); - - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2l_b2l/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2c2l/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2l_sameb/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2l_diffb/"); // LS - - if (cfgFillSeparateCharmHadronPairs) { - for (int im = 0; im < nm; im++) { - fRegistry.addClone("Pair/ccbar/c2l_c2l/", Form("Pair/ccbar/%s_%s/", charmed_mesons[im].data(), anti_charmed_mesons[im].data())); - } - for (int ib = 0; ib < nb; ib++) { - fRegistry.addClone("Pair/ccbar/c2l_c2l/", Form("Pair/ccbar/%s_%s/", charmed_baryons[ib].data(), anti_charmed_baryons[ib].data())); - } - for (int im1 = 0; im1 < nm - 1; im1++) { - for (int im2 = im1 + 1; im2 < nm; im2++) { - fRegistry.addClone("Pair/ccbar/c2l_c2l/", Form("Pair/ccbar/%s_%s/", sum_charmed_mesons[im1].data(), sum_charmed_mesons[im2].data())); - } - } - for (int ib1 = 0; ib1 < nb - 1; ib1++) { - for (int ib2 = ib1 + 1; ib2 < nb; ib2++) { - fRegistry.addClone("Pair/ccbar/c2l_c2l/", Form("Pair/ccbar/%s_%s/", sum_charmed_baryons[ib1].data(), sum_charmed_baryons[ib2].data())); - } - } - for (int im = 0; im < nm; im++) { - for (int ib = 0; ib < nb; ib++) { - fRegistry.addClone("Pair/ccbar/c2l_c2l/", Form("Pair/ccbar/%s_%s/", sum_charmed_mesons[im].data(), sum_charmed_baryons[ib].data())); - } - } - } - - // for correlated bkg due to mis-identified hadrons, and true combinatorial bkg - fRegistry.add("Pair/corr_bkg_lh/uls/hs", "rec. bkg", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_y, axis_dphi_ee, axis_deta_ee, axis_cos_theta_pol, axis_phi_pol, axis_quadmom, axis_aco, axis_asym_pt, axis_dphi_e_ee, axis_dca, axis_chi2PCA, axis_dl, axis_cpa}, true); - fRegistry.addClone("Pair/corr_bkg_lh/uls/", "Pair/corr_bkg_lh/lspp/"); - fRegistry.addClone("Pair/corr_bkg_lh/uls/", "Pair/corr_bkg_lh/lsmm/"); - fRegistry.addClone("Pair/corr_bkg_lh/", "Pair/corr_bkg_hh/"); - fRegistry.addClone("Pair/corr_bkg_lh/", "Pair/comb_bkg/"); - - if (doprocessGen_VM) { - fRegistry.add("Generated/VM/Omega/hPtY", "pT vs. y of #omega", o2::framework::HistType::kTH2D, {axis_y_meson, axis_pt_meson}, true); // for pT spectrum - fRegistry.add("Generated/VM/Phi/hPtY", "pT vs. y of #phi", o2::framework::HistType::kTH2D, {axis_y_meson, axis_pt_meson}, true); // for pT spectrum - } - - if (cfgFillUnfolding) { - // for 2D unfolding - const o2::framework::AxisSpec axis_mass_gen{ConfMllBins, "m_{ll}^{gen} (GeV/c^{2})"}; - const o2::framework::AxisSpec axis_pt_gen{ConfPtllBins, "p_{T,ll}^{gen} (GeV/c)"}; - const o2::framework::AxisSpec axis_mass_rec{ConfMllBins, "m_{ll}^{rec} (GeV/c^{2})"}; - const o2::framework::AxisSpec axis_pt_rec{ConfPtllBins, "p_{T,ll}^{rec} (GeV/c)"}; - fRegistry.add("Unfold/sm/uls/hsRM", "response matrix", o2::framework::HistType::kTHnSparseD, {axis_mass_gen, axis_pt_gen, axis_mass_rec, axis_pt_rec}, true); - fRegistry.add("Unfold/sm/uls/hMiss", "missing dilepton", o2::framework::HistType::kTH2D, {axis_mass_gen, axis_pt_gen}, true); // e.g. true eta is in acceptance, but reconstructed eta is out of acceptance. - fRegistry.add("Unfold/sm/uls/hFake", "fake dilepton", o2::framework::HistType::kTH2D, {axis_mass_rec, axis_pt_rec}, true); // e.g. true eta is out of acceptance, but reconstructed eta is in acceptance. - fRegistry.addClone("Unfold/sm/uls/", "Unfold/sm/lspp/"); - fRegistry.addClone("Unfold/sm/uls/", "Unfold/sm/lsmm/"); - fRegistry.addClone("Unfold/sm/", "Unfold/ccbar/"); - fRegistry.addClone("Unfold/sm/", "Unfold/bbbar/"); - } - } - - float beamM1 = o2::constants::physics::MassProton; // mass of beam - float beamM2 = o2::constants::physics::MassProton; // mass of beam - float beamE1 = 0.f; // beam energy - float beamE2 = 0.f; // beam energy - float beamP1 = 0.f; // beam momentum - float beamP2 = 0.f; // beam momentum - - float leptonM1 = 0.f; - float leptonM2 = 0.f; - int pdg_lepton = 0; - void init(o2::framework::InitContext&) - { - if (doprocessAnalysis && doprocessAnalysis_Smeared) { - LOGF(fatal, "Cannot enable doprocessAnalysis and doprocessAnalysis_Smeared at the same time. Please choose one."); - } - - mRunNumber = 0; - d_bz = 0; - - ccdb->setURL(ccdburl); - ccdb->setCaching(true); - ccdb->setLocalObjectValidityChecking(); - ccdb->setFatalWhenNull(false); - rctChecker.init(eventcuts.cfgRCTLabel.value, eventcuts.cfgCheckZDC.value, eventcuts.cfgTreatLimitedAcceptanceAsBad.value); - - DefineEMEventCut(); - addhistograms(); - - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - DefineDielectronCut(); - leptonM1 = o2::constants::physics::MassElectron; - leptonM2 = o2::constants::physics::MassElectron; - pdg_lepton = 11; - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - DefineDimuonCut(); - leptonM1 = o2::constants::physics::MassMuon; - leptonM2 = o2::constants::physics::MassMuon; - pdg_lepton = 13; - } - if (doprocessNorm) { - fRegistry.addClone("Event/before/hCollisionCounter", "Event/norm/hCollisionCounter"); - } - if (doprocessBC) { - auto hTVXCounter = fRegistry.add("BC/hTVXCounter", "TVX counter", o2::framework::HistType::kTH1D, {{6, -0.5f, 5.5f}}); - hTVXCounter->GetXaxis()->SetBinLabel(1, "TVX"); - hTVXCounter->GetXaxis()->SetBinLabel(2, "TVX && NoTFB"); - hTVXCounter->GetXaxis()->SetBinLabel(3, "TVX && NoITSROFB"); - hTVXCounter->GetXaxis()->SetBinLabel(4, "TVX && GoodRCT"); - hTVXCounter->GetXaxis()->SetBinLabel(5, "TVX && NoTFB && NoITSROFB"); - hTVXCounter->GetXaxis()->SetBinLabel(6, "TVX && NoTFB && NoITSROFB && GoodRCT"); - } - - mDCAFitter.setPropagateToPCA(dfGroup.propagateToPCA); - mDCAFitter.setMaxR(dfGroup.maxR); - mDCAFitter.setMaxDZIni(dfGroup.maxDZIni); - mDCAFitter.setMinParamChange(dfGroup.minParamChange); - mDCAFitter.setMinRelChi2Change(dfGroup.minRelChi2Change); - mDCAFitter.setUseAbsDCA(dfGroup.useAbsDCA); - mDCAFitter.setWeightedFinalPCA(dfGroup.useWeightedFinalPCA); - mDCAFitter.setMaxChi2(std::pow(10, dfGroup.maxLog10Chi2PCA)); - - mFwdDCAFitter.setPropagateToPCA(fdfGroup.propagateToPCA); - mFwdDCAFitter.setMaxR(fdfGroup.maxR); - mFwdDCAFitter.setMaxDXIni(fdfGroup.maxDXIni); - mFwdDCAFitter.setMinParamChange(fdfGroup.minParamChange); - mFwdDCAFitter.setMinRelChi2Change(fdfGroup.minRelChi2Change); - mFwdDCAFitter.setUseAbsDCA(fdfGroup.useAbsDCA); - mFwdDCAFitter.setMaxChi2(std::pow(10, fdfGroup.maxLog10Chi2PCA)); - - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - switch (matCorrType.value) { - case static_cast(o2::aod::pwgem::dilepton::utils::pairutil::MatCorrType::kNone): - mFwdDCAFitter.setTGeoMat(false); - break; - case static_cast(o2::aod::pwgem::dilepton::utils::pairutil::MatCorrType::kTGeo): - LOGF(info, "TGeo correction requested, loading geometry"); - if (!o2::base::GeometryManager::isGeometryLoaded()) { - ccdb->get(geoPath); - } - // matCorr = o2::base::Propagator::MatCorrType::USEMatCorrTGeo; - mFwdDCAFitter.setTGeoMat(true); - break; - case static_cast(o2::aod::pwgem::dilepton::utils::pairutil::MatCorrType::kLUT): { - LOGF(info, "LUT correction requested, loading LUT"); - o2::base::MatLayerCylSet* lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->get(lutPath)); - // matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; - mFwdDCAFitter.setTGeoMat(false); - mFwdDCAFitter.setMatLUT(lut); - break; - } - default: - LOGF(info, "no correction requested, loading LUT by default!"); - // lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->get(lutPath)); - // matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; - mFwdDCAFitter.setTGeoMat(false); - break; - } - } - } - - template - void initCCDB(TCollision const& collision) - { - if (mRunNumber == collision.runNumber()) { - return; - } - - auto run3grp_timestamp = collision.timestamp(); - o2::parameters::GRPObject* grpo = 0x0; - o2::parameters::GRPMagField* grpmag = 0x0; - if (grpo) { - o2::base::Propagator::initFieldFromGRP(grpo); - // Fetch magnetic field from ccdb for current collision - d_bz = grpo->getNominalL3Field(); - LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kG"; - } else { - grpmag = ccdb->getForTimeStamp(grpmagPath, run3grp_timestamp); - if (!grpmag) { - LOG(fatal) << "Got nullptr from CCDB for path " << grpmagPath << " of object GRPMagField and " << grpPath << " of object GRPObject for timestamp " << run3grp_timestamp; - } - o2::base::Propagator::initFieldFromGRP(grpmag); - // Fetch magnetic field from ccdb for current collision - d_bz = std::lround(5.f * grpmag->getL3Current() / 30000.f); - LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kG"; - } - mRunNumber = collision.runNumber(); - fDielectronCut.SetTrackPhiPositionRange(dielectroncuts.cfg_min_phiposition_track, dielectroncuts.cfg_max_phiposition_track, dielectroncuts.cfgRefR, d_bz, dielectroncuts.cfg_mirror_phi_track); - - mDCAFitter.setBz(d_bz); - mFwdDCAFitter.setBz(d_bz); - - auto grplhcif = ccdb->getForTimeStamp("GLO/Config/GRPLHCIF", collision.timestamp()); - int beamZ1 = grplhcif->getBeamZ(o2::constants::lhc::BeamC); - int beamZ2 = grplhcif->getBeamZ(o2::constants::lhc::BeamA); - int beamA1 = grplhcif->getBeamA(o2::constants::lhc::BeamC); - int beamA2 = grplhcif->getBeamA(o2::constants::lhc::BeamA); - beamE1 = grplhcif->getBeamEnergyPerNucleonInGeV(o2::constants::lhc::BeamC); - beamE2 = grplhcif->getBeamEnergyPerNucleonInGeV(o2::constants::lhc::BeamA); - beamM1 = o2::constants::physics::MassProton * beamA1; - beamM2 = o2::constants::physics::MassProton * beamA2; - beamP1 = std::sqrt(std::pow(beamE1, 2) - std::pow(beamM1, 2)); - beamP2 = std::sqrt(std::pow(beamE2, 2) - std::pow(beamM2, 2)); - LOGF(info, "beamZ1 = %d, beamZ2 = %d, beamA1 = %d, beamA2 = %d, beamE1 = %f (GeV), beamE2 = %f (GeV), beamM1 = %f (GeV), beamM2 = %f (GeV), beamP1 = %f (GeV), beamP2 = %f (GeV)", beamZ1, beamZ2, beamA1, beamA2, beamE1, beamE2, beamM1, beamM2, beamP1, beamP2); - } - - void DefineEMEventCut() - { - fEMEventCut = EMEventCut("fEMEventCut", "fEMEventCut"); - fEMEventCut.SetRequireSel8(eventcuts.cfgRequireSel8); - fEMEventCut.SetRequireFT0AND(eventcuts.cfgRequireFT0AND); - fEMEventCut.SetZvtxRange(eventcuts.cfgZvtxMin, eventcuts.cfgZvtxMax); - fEMEventCut.SetRequireNoTFB(eventcuts.cfgRequireNoTFB); - fEMEventCut.SetRequireNoITSROFB(eventcuts.cfgRequireNoITSROFB); - fEMEventCut.SetRequireNoSameBunchPileup(eventcuts.cfgRequireNoSameBunchPileup); - fEMEventCut.SetRequireVertexITSTPC(eventcuts.cfgRequireVertexITSTPC); - fEMEventCut.SetRequireVertexTOFmatched(eventcuts.cfgRequireVertexTOFmatched); - fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); - fEMEventCut.SetRequireNoCollInTimeRangeStandard(eventcuts.cfgRequireNoCollInTimeRangeStandard); - fEMEventCut.SetRequireNoCollInTimeRangeStrict(eventcuts.cfgRequireNoCollInTimeRangeStrict); - fEMEventCut.SetRequireNoCollInITSROFStandard(eventcuts.cfgRequireNoCollInITSROFStandard); - fEMEventCut.SetRequireNoCollInITSROFStrict(eventcuts.cfgRequireNoCollInITSROFStrict); - fEMEventCut.SetRequireNoHighMultCollInPrevRof(eventcuts.cfgRequireNoHighMultCollInPrevRof); - fEMEventCut.SetRequireGoodITSLayer3(eventcuts.cfgRequireGoodITSLayer3); - fEMEventCut.SetRequireGoodITSLayer0123(eventcuts.cfgRequireGoodITSLayer0123); - fEMEventCut.SetRequireGoodITSLayersAll(eventcuts.cfgRequireGoodITSLayersAll); - } - - void DefineDielectronCut() - { - fDielectronCut = DielectronCut("fDielectronCut", "fDielectronCut"); - - // for pair - fDielectronCut.SetMeeRange(dielectroncuts.cfg_min_mass, dielectroncuts.cfg_max_mass); - fDielectronCut.SetPairPtRange(dielectroncuts.cfg_min_pair_pt, dielectroncuts.cfg_max_pair_pt); - fDielectronCut.SetPairYRange(dielectroncuts.cfg_min_pair_y, dielectroncuts.cfg_max_pair_y); - fDielectronCut.SetPairDCARange(dielectroncuts.cfg_min_pair_dca3d, dielectroncuts.cfg_max_pair_dca3d); // in sigma - fDielectronCut.SetMaxMeePhiVDep([&](float phiv) { return dielectroncuts.cfg_phiv_intercept + phiv * dielectroncuts.cfg_phiv_slope; }, dielectroncuts.cfg_min_phiv, dielectroncuts.cfg_max_phiv); - fDielectronCut.ApplyPhiV(dielectroncuts.cfg_apply_phiv); - fDielectronCut.SetMindEtadPhi(dielectroncuts.cfg_apply_detadphi, false, dielectroncuts.cfg_min_deta, dielectroncuts.cfg_min_dphi); - fDielectronCut.SetPairOpAng(dielectroncuts.cfg_min_opang, dielectroncuts.cfg_max_opang); - // fDielectronCut.SetRequireDifferentSides(dielectroncuts.cfg_require_diff_sides); - - // for track - fDielectronCut.SetTrackPtRange(dielectroncuts.cfg_min_pt_track, dielectroncuts.cfg_max_pt_track); - fDielectronCut.SetTrackEtaRange(dielectroncuts.cfg_min_eta_track, +dielectroncuts.cfg_max_eta_track); - fDielectronCut.SetTrackPhiRange(dielectroncuts.cfg_min_phi_track, dielectroncuts.cfg_max_phi_track, dielectroncuts.cfg_mirror_phi_track, dielectroncuts.cfg_reject_phi_track); - fDielectronCut.SetMinNClustersTPC(dielectroncuts.cfg_min_ncluster_tpc); - fDielectronCut.SetMinNCrossedRowsTPC(dielectroncuts.cfg_min_ncrossedrows); - fDielectronCut.SetMinNCrossedRowsOverFindableClustersTPC(0.8); - fDielectronCut.SetMaxFracSharedClustersTPC(dielectroncuts.cfg_max_frac_shared_clusters_tpc); - fDielectronCut.SetChi2PerClusterTPC(0.0, dielectroncuts.cfg_max_chi2tpc); - fDielectronCut.SetChi2PerClusterITS(0.0, dielectroncuts.cfg_max_chi2its); - fDielectronCut.SetNClustersITS(dielectroncuts.cfg_min_ncluster_its, 7); - fDielectronCut.SetMeanClusterSizeITS(dielectroncuts.cfg_min_its_cluster_size, dielectroncuts.cfg_max_its_cluster_size); - fDielectronCut.SetTrackMaxDcaSigma(dielectroncuts.cfg_max_dca_sigma, dielectroncuts.cfgDCAType); - fDielectronCut.SetTrackMaxDcaXY(dielectroncuts.cfg_max_dcaxy); - fDielectronCut.SetTrackMaxDcaZ(dielectroncuts.cfg_max_dcaz); - fDielectronCut.RequireITSibAny(dielectroncuts.cfg_require_itsib_any); - fDielectronCut.RequireITSib1st(dielectroncuts.cfg_require_itsib_1st); - fDielectronCut.SetChi2TOF(0.0, dielectroncuts.cfg_max_chi2tof); - // fDielectronCut.SetRelDiffPin(dielectroncuts.cfg_min_rel_diff_pin, dielectroncuts.cfg_max_rel_diff_pin); - fDielectronCut.EnableTTCA(dielectroncuts.enableTTCA); - - // for eID - fDielectronCut.SetPIDScheme(dielectroncuts.cfg_pid_scheme); - fDielectronCut.SetTPCNsigmaElRange(dielectroncuts.cfg_min_TPCNsigmaEl, dielectroncuts.cfg_max_TPCNsigmaEl); - // fDielectronCut.SetTPCNsigmaMuRange(dielectroncuts.cfg_min_TPCNsigmaMu, dielectroncuts.cfg_max_TPCNsigmaMu); - fDielectronCut.SetTPCNsigmaPiRange(dielectroncuts.cfg_min_TPCNsigmaPi, dielectroncuts.cfg_max_TPCNsigmaPi); - fDielectronCut.SetTPCNsigmaKaRange(dielectroncuts.cfg_min_TPCNsigmaKa, dielectroncuts.cfg_max_TPCNsigmaKa); - fDielectronCut.SetTPCNsigmaPrRange(dielectroncuts.cfg_min_TPCNsigmaPr, dielectroncuts.cfg_max_TPCNsigmaPr); - fDielectronCut.SetTOFNsigmaElRange(dielectroncuts.cfg_min_TOFNsigmaEl, dielectroncuts.cfg_max_TOFNsigmaEl); - fDielectronCut.SetPinRangeForPionRejectionTPC(dielectroncuts.cfg_min_pin_pirejTPC, dielectroncuts.cfg_max_pin_pirejTPC); - - if (dielectroncuts.cfg_pid_scheme == static_cast(DielectronCut::PIDSchemes::kPIDML)) { // please call this at the end of DefineDileptonCut - std::vector binsML{}; - binsML.reserve(dielectroncuts.binsMLPID.value.size()); - for (size_t i = 0; i < dielectroncuts.binsMLPID.value.size(); i++) { - binsML.emplace_back(dielectroncuts.binsMLPID.value[i]); - } - std::vector thresholdsML{}; - thresholdsML.reserve(dielectroncuts.cutsMLPID.value.size()); - for (size_t i = 0; i < dielectroncuts.cutsMLPID.value.size(); i++) { - thresholdsML.emplace_back(dielectroncuts.cutsMLPID.value[i]); - } - fDielectronCut.SetMLThresholds(binsML, thresholdsML); - } // end of PID ML - } - - void DefineDimuonCut() - { - fDimuonCut = DimuonCut("fDimuonCut", "fDimuonCut"); - - // for pair - fDimuonCut.SetMassRange(dimuoncuts.cfg_min_mass, dimuoncuts.cfg_max_mass); - fDimuonCut.SetPairPtRange(dimuoncuts.cfg_min_pair_pt, dimuoncuts.cfg_max_pair_pt); - fDimuonCut.SetPairYRange(dimuoncuts.cfg_min_pair_y, dimuoncuts.cfg_max_pair_y); - fDimuonCut.SetPairDCAxyRange(dimuoncuts.cfg_min_pair_dcaxy, dimuoncuts.cfg_max_pair_dcaxy); // DCAxy in cm - fDimuonCut.SetMindEtadPhi(dimuoncuts.cfg_apply_detadphi, dimuoncuts.cfg_min_deta, dimuoncuts.cfg_min_dphi); - - // for track - fDimuonCut.SetTrackType(dimuoncuts.cfg_track_type); - fDimuonCut.SetTrackPtRange(dimuoncuts.cfg_min_pt_track, dimuoncuts.cfg_max_pt_track); - fDimuonCut.SetTrackEtaRange(dimuoncuts.cfg_min_eta_track, dimuoncuts.cfg_max_eta_track); - fDimuonCut.SetTrackPhiRange(dimuoncuts.cfg_min_phi_track, dimuoncuts.cfg_max_phi_track); - fDimuonCut.SetNClustersMFT(dimuoncuts.cfg_min_ncluster_mft, 10); - fDimuonCut.SetNClustersMCHMID(dimuoncuts.cfg_min_ncluster_mch, 20); - fDimuonCut.SetChi2(0.f, dimuoncuts.cfg_max_chi2); - fDimuonCut.SetChi2MFT(0.f, dimuoncuts.cfg_max_chi2mft); - // fDimuonCut.SetMatchingChi2MCHMFT(0.f, dimuoncuts.cfg_max_matching_chi2_mftmch); - fDimuonCut.SetMaxDiffMatchingChi2MCHMFT(dimuoncuts.cfg_max_diff_chi2_mftmch); - fDimuonCut.SetMaxMatchingChi2MCHMFTPtDep([&](float pt) { return (pt < dimuoncuts.cfg_border_pt_for_chi2mchmft ? dimuoncuts.cfg_max_matching_chi2_mftmch_lowPt : dimuoncuts.cfg_max_matching_chi2_mftmch_highPt); }); - fDimuonCut.SetMatchingChi2MCHMID(0.f, dimuoncuts.cfg_max_matching_chi2_mchmid); - fDimuonCut.SetDCAxy(0.f, dimuoncuts.cfg_max_dcaxy); - fDimuonCut.SetRabs(dimuoncuts.cfg_min_rabs, dimuoncuts.cfg_max_rabs); - fDimuonCut.SetMaxPDCARabsDep([&](float rabs) { return (rabs < 26.5 ? 594.f : 324.f); }); - fDimuonCut.SetMaxdPtdEtadPhiwrtMCHMID(dimuoncuts.cfg_max_relDPt_wrt_matchedMCHMID, dimuoncuts.cfg_max_DEta_wrt_matchedMCHMID, dimuoncuts.cfg_max_DPhi_wrt_matchedMCHMID); // this is relevant for global muons - fDimuonCut.SetMFTHitMap(dimuoncuts.requireMFTHitMap, dimuoncuts.requiredMFTDisks); - fDimuonCut.EnableTTCA(dimuoncuts.enableTTCA); - } - - template - int FindSMULS(TTrack const& t1mc, TTrack const& t2mc, TMCParticles const& mcparticles) - { - int arr[] = { - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, pdg_lepton, 22, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, pdg_lepton, 111, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, pdg_lepton, 221, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, pdg_lepton, 331, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, pdg_lepton, 113, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, pdg_lepton, 223, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, pdg_lepton, 333, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, pdg_lepton, 443, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, pdg_lepton, 100443, mcparticles) - // o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, pdg_lepton, 553, mcparticles), - // o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, pdg_lepton, 100553, mcparticles), - // o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, pdg_lepton, 200553, mcparticles) - }; - int size = sizeof(arr) / sizeof(*arr); - int max = *std::max_element(arr, arr + size); - return max; - } - - template - int FindSMLSPP(TTrack const& t1mc, TTrack const& t2mc, TMCParticles const& mcparticles) - { - int arr[] = { - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, -pdg_lepton, 111, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, -pdg_lepton, 221, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, -pdg_lepton, 331, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, -pdg_lepton, 113, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, -pdg_lepton, 223, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, -pdg_lepton, 333, mcparticles)}; - int size = sizeof(arr) / sizeof(*arr); - int max = *std::max_element(arr, arr + size); - return max; - } - - template - int FindSMLSMM(TTrack const& t1mc, TTrack const& t2mc, TMCParticles const& mcparticles) - { - int arr[] = { - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, pdg_lepton, pdg_lepton, 111, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, pdg_lepton, pdg_lepton, 221, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, pdg_lepton, pdg_lepton, 331, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, pdg_lepton, pdg_lepton, 113, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, pdg_lepton, pdg_lepton, 223, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, pdg_lepton, pdg_lepton, 333, mcparticles)}; - int size = sizeof(arr) / sizeof(*arr); - int max = *std::max_element(arr, arr + size); - return max; - } - - template - bool isInAcceptance(T const& lepton) - { - float pt = 0.f, eta = 0.f; - if constexpr (isSmeared) { - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - pt = lepton.ptSmeared(); - eta = lepton.etaSmeared(); - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - if (dimuoncuts.cfg_track_type == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack)) { - pt = lepton.ptSmeared_sa_muon(); - eta = lepton.etaSmeared_sa_muon(); - } else if (dimuoncuts.cfg_track_type == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack)) { - pt = lepton.ptSmeared_gl_muon(); - eta = lepton.etaSmeared_gl_muon(); - } else { - pt = lepton.pt(); - eta = lepton.eta(); - } - } - } else { - pt = lepton.pt(); - eta = lepton.eta(); - } - - return isInAcceptance(pt, eta); - - // if ((mctrackcuts.min_mcPt < pt && pt < mctrackcuts.max_mcPt) && (mctrackcuts.min_mcEta < eta && eta < mctrackcuts.max_mcEta)) { - // return true; - // } else { - // return false; - // } - } - - bool isInAcceptance(const float pt, const float eta) - { - if ((mctrackcuts.min_mcPt < pt && pt < mctrackcuts.max_mcPt) && (mctrackcuts.min_mcEta < eta && eta < mctrackcuts.max_mcEta)) { - return true; - } else { - return false; - } - } - - struct dileptonSV { - float pt1{999}; - float eta1{999}; - float phi1{999}; - - float pt2{999}; - float eta2{999}; - float phi2{999}; - - // float mass{999}; - // float pt{999}; - // float rapidity{999}; - - float chi2PCA{999}; - float cpa{999}; - float lxy{999}; - float lz{999}; - float lxyz{999}; - float lxyErr{999}; - float lzErr{999}; - float lxyzErr{999}; - }; - - template - bool findSV(TCollision const& collision, TTrackParCov const& t1, TTrackParCov const& t2, dileptonSV& candidate) - { - try { - if (mDCAFitter.process(t1, t2) == 0) { - return false; - } - } catch (const std::runtime_error& error) { - LOG(info) << "Run time error found: " << error.what() << ". DCAFitterN cannot work, skipping the candidate."; - return false; - } - candidate.chi2PCA = mDCAFitter.getChi2AtPCACandidate(); - if (dfGroup.maxLog10Chi2PCA < std::log10(candidate.chi2PCA)) { - return false; - } - - mDCAFitter.propagateTracksToVertex(); - const auto& secondaryVertex = mDCAFitter.getPCACandidate(); - auto trackParCov0 = mDCAFitter.getTrack(0); - auto trackParCov1 = mDCAFitter.getTrack(1); - - // get track momenta - std::array pvertex = {collision.posX(), collision.posY(), collision.posZ()}; - std::array pvec0{}; - std::array pvec1{}; - trackParCov0.getPxPyPzGlo(pvec0); - trackParCov1.getPxPyPzGlo(pvec1); - std::array momDilepton = {pvec0[0] + pvec1[0], pvec0[1] + pvec1[1], pvec0[2] + pvec1[2]}; - candidate.cpa = std::clamp(RecoDecay::cpa(pvertex, secondaryVertex, momDilepton), -1.0, std::nextafter(1.0, 0.0)); - - candidate.lxy = std::sqrt(std::pow(secondaryVertex[0] - collision.posX(), 2) + std::pow(secondaryVertex[1] - collision.posY(), 2)); - candidate.lz = secondaryVertex[2] - collision.posZ(); - candidate.lxyz = std::sqrt(std::pow(candidate.lxy, 2) + std::pow(candidate.lz, 2)); - - auto primaryVertex = getPrimaryVertex(collision); - std::array covVtx = mDCAFitter.calcPCACovMatrixFlat(); - double phi{0}, theta{0}; - getPointDirection(std::array{primaryVertex.getX(), primaryVertex.getY(), primaryVertex.getZ()}, secondaryVertex, phi, theta); - candidate.lxyErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), phi, 0.) + getRotatedCovMatrixXX(covVtx, phi, 0.)); - candidate.lzErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), 0, theta) + getRotatedCovMatrixXX(covVtx, 0, theta)); - candidate.lxyzErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), phi, theta) + getRotatedCovMatrixXX(covVtx, phi, theta)); - - candidate.pt1 = trackParCov0.getPt(); - candidate.eta1 = trackParCov0.getEta(); - candidate.phi1 = RecoDecay::constrainAngle(trackParCov0.getPhi(), 0, 1U); // 0-2pi - - candidate.pt2 = trackParCov1.getPt(); - candidate.eta2 = trackParCov1.getEta(); - candidate.phi2 = RecoDecay::constrainAngle(trackParCov1.getPhi(), 0, 1U); // 0-2pi - - return true; - } - - template - bool findSVFwd(TCollision const& collision, TTrackParCov const& t1, TTrackParCov const& t2, dileptonSV& candidate) - { - try { - if (mFwdDCAFitter.process(t1, t2) == 0) { - return false; - } - } catch (const std::runtime_error& error) { - LOG(info) << "Run time error found: " << error.what() << ". FwdDCAFitterN cannot work, skipping the candidate."; - return false; - } - mFwdDCAFitter.FwdpropagateTracksToVertex(); - - std::array pvertex = {collision.posX(), collision.posY(), collision.posZ()}; - const auto& secondaryVertex = mFwdDCAFitter.getPCACandidate(); - candidate.chi2PCA = mFwdDCAFitter.getChi2AtPCACandidate(); - if (fdfGroup.maxLog10Chi2PCA < std::log10(candidate.chi2PCA)) { - return false; - } - auto trackParCov0 = mFwdDCAFitter.getTrack(0); - auto trackParCov1 = mFwdDCAFitter.getTrack(1); - std::array momDilepton = {static_cast(trackParCov0.getPx() + trackParCov1.getPx()), static_cast(trackParCov0.getPy() + trackParCov1.getPy()), static_cast(trackParCov0.getPz() + trackParCov1.getPz())}; - candidate.cpa = std::clamp(RecoDecay::cpa(pvertex, secondaryVertex, momDilepton), -1.0, std::nextafter(1.0, 0.0)); - - candidate.lxy = std::sqrt(std::pow(secondaryVertex[0] - collision.posX(), 2) + std::pow(secondaryVertex[1] - collision.posY(), 2)); - candidate.lz = secondaryVertex[2] - collision.posZ(); - candidate.lxyz = std::sqrt(std::pow(candidate.lxy, 2) + std::pow(candidate.lz, 2)); - - auto primaryVertex = getPrimaryVertex(collision); - std::array covVtx = mFwdDCAFitter.calcPCACovMatrixFlat(); - double phi{0}, theta{0}; - getPointDirection(std::array{primaryVertex.getX(), primaryVertex.getY(), primaryVertex.getZ()}, secondaryVertex, phi, theta); - candidate.lxyErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), phi, 0.) + getRotatedCovMatrixXX(covVtx, phi, 0.)); - candidate.lzErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), 0, theta) + getRotatedCovMatrixXX(covVtx, 0, theta)); - candidate.lxyzErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), phi, theta) + getRotatedCovMatrixXX(covVtx, phi, theta)); - - candidate.pt1 = trackParCov0.getPt(); - candidate.eta1 = trackParCov0.getEta(); - candidate.phi1 = RecoDecay::constrainAngle(trackParCov0.getPhi(), 0, 1U); // 0-2pi - - candidate.pt2 = trackParCov1.getPt(); - candidate.eta2 = trackParCov1.getEta(); - candidate.phi2 = RecoDecay::constrainAngle(trackParCov1.getPhi(), 0, 1U); // 0-2pi - - return true; - } - - template - void fillGenHistograms(const int sign1, const int sign2, const int pdgMotherC1, const int pdgMotherC2, const float mass, const float pt, const float rapidity, const float dphi, const float deta, const float cos_thetaPol, const float phiPol, const float quadmom, const float aco, const float asym, const float dphi_e_ee, const float weight) - { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/") + HIST(dilepton_source_types[sourceId]) + HIST("uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/") + HIST(dilepton_source_types[sourceId]) + HIST("lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/") + HIST(dilepton_source_types[sourceId]) + HIST("lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - - if (dilepton_source_types[sourceId].find("ccbar") != std::string_view::npos && cfgFillSeparateCharmHadronPairs) { - if (std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 411) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Dplus_Dminus/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Dplus_Dminus/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Dplus_Dminus/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if (std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 421) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/D0_D0bar/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/D0_D0bar/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/D0_D0bar/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if (std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 431) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Dsplus_Dsminus/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Dsplus_Dsminus/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Dsplus_Dsminus/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 421) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 421)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Dpm_D0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Dpm_D0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Dpm_D0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 431) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 431)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Dpm_Dspm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Dpm_Dspm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Dpm_Dspm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 431) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 431)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/D0_Dspm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/D0_Dspm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/D0_Dspm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if (std::abs(pdgMotherC1) == 4122 && std::abs(pdgMotherC2) == 4122) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Lcplus_Lcminus/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Lcplus_Lcminus/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Lcplus_Lcminus/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if (std::abs(pdgMotherC1) == 4232 && std::abs(pdgMotherC2) == 4232) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Xicplus_Xicminus/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Xicplus_Xicminus/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Xicplus_Xicminus/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if (std::abs(pdgMotherC1) == 4132 && std::abs(pdgMotherC2) == 4132) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Xic0_Xic0bar/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Xic0_Xic0bar/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Xic0_Xic0bar/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if (std::abs(pdgMotherC1) == 4332 && std::abs(pdgMotherC2) == 4332) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Omegac0_Omegac0bar/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Omegac0_Omegac0bar/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Omegac0_Omegac0bar/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 4122 && std::abs(pdgMotherC2) == 4232) || (std::abs(pdgMotherC2) == 4122 && std::abs(pdgMotherC1) == 4232)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Lcpm_Xicpm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Lcpm_Xicpm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Lcpm_Xicpm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 4122 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 4122 && std::abs(pdgMotherC1) == 4132)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Lcpm_Xic0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Lcpm_Xic0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Lcpm_Xic0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 4122 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 4122 && std::abs(pdgMotherC1) == 4332)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Lcpm_Omegac0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Lcpm_Omegac0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Lcpm_Omegac0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 4232 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 4232 && std::abs(pdgMotherC1) == 4132)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Xicpm_Xic0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Xicpm_Xic0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Xicpm_Xic0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 4232 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 4232 && std::abs(pdgMotherC1) == 4332)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Xicpm_Omegac0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Xicpm_Omegac0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Xicpm_Omegac0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 4132 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 4132 && std::abs(pdgMotherC1) == 4332)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Xic0_Omegac0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Xic0_Omegac0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Xic0_Omegac0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 4122) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 4122)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Dpm_Lcpm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Dpm_Lcpm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Dpm_Lcpm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 4232) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 4232)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Dpm_Xicpm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Dpm_Xicpm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Dpm_Xicpm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 4132)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Dpm_Xic0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Dpm_Xic0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Dpm_Xic0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 4332)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Dpm_Omegac0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Dpm_Omegac0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Dpm_Omegac0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 4122) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 4122)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/D0_Lcpm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/D0_Lcpm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/D0_Lcpm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 4232) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 4232)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/D0_Xicpm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/D0_Xicpm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/D0_Xicpm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 4132)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/D0_Xic0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/D0_Xic0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/D0_Xic0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 4332)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/D0_Omegac0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/D0_Omegac0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/D0_Omegac0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 4122) || (std::abs(pdgMotherC2) == 431 && std::abs(pdgMotherC1) == 4122)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Dspm_Lcpm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Dspm_Lcpm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Dspm_Lcpm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 4232) || (std::abs(pdgMotherC2) == 431 && std::abs(pdgMotherC1) == 4232)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Dspm_Xicpm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Dspm_Xicpm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Dspm_Xicpm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 431 && std::abs(pdgMotherC1) == 4132)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Dspm_Xic0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Dspm_Xic0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Dspm_Xic0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 431 && std::abs(pdgMotherC1) == 4332)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Dspm_Omegac0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Dspm_Omegac0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Dspm_Omegac0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } - } - } - - template - void fillRecHistograms(const int sign1, const int sign2, const int pdgMotherC1, const int pdgMotherC2, const float mass, const float pt, const float rapidity, const float dphi, const float deta, const float cos_thetaPol, const float phiPol, const float quadmom, const float aco, const float asym, const float dphi_e_ee, const float pair_dca, const float log10Chi2PCA, const float decayLength, const float cpa, const float weight) - { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/") + HIST(dilepton_source_types[sourceId]) + HIST("uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/") + HIST(dilepton_source_types[sourceId]) + HIST("lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/") + HIST(dilepton_source_types[sourceId]) + HIST("lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - - if (dilepton_source_types[sourceId].find("ccbar") != std::string_view::npos && cfgFillSeparateCharmHadronPairs) { - if (std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 411) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dplus_Dminus/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dplus_Dminus/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dplus_Dminus/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if (std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 421) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/D0_D0bar/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/D0_D0bar/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/D0_D0bar/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if (std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 431) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dsplus_Dsminus/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dsplus_Dsminus/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dsplus_Dsminus/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 421) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 421)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dpm_D0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dpm_D0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dpm_D0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 431) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 431)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dpm_Dspm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dpm_Dspm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dpm_Dspm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 431) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 431)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/D0_Dspm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/D0_Dspm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/D0_Dspm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if (std::abs(pdgMotherC1) == 4122 && std::abs(pdgMotherC2) == 4122) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Lcplus_Lcminus/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Lcplus_Lcminus/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Lcplus_Lcminus/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if (std::abs(pdgMotherC1) == 4232 && std::abs(pdgMotherC2) == 4232) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Xicplus_Xicminus/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Xicplus_Xicminus/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Xicplus_Xicminus/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if (std::abs(pdgMotherC1) == 4132 && std::abs(pdgMotherC2) == 4132) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Xic0_Xic0bar/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Xic0_Xic0bar/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Xic0_Xic0bar/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if (std::abs(pdgMotherC1) == 4332 && std::abs(pdgMotherC2) == 4332) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Omegac0_Omegac0bar/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Omegac0_Omegac0bar/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Omegac0_Omegac0bar/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 4122 && std::abs(pdgMotherC2) == 4232) || (std::abs(pdgMotherC2) == 4122 && std::abs(pdgMotherC1) == 4232)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xicpm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xicpm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xicpm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 4122 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 4122 && std::abs(pdgMotherC1) == 4132)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xic0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xic0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xic0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 4122 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 4122 && std::abs(pdgMotherC1) == 4332)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Omegac0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Omegac0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Omegac0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 4232 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 4232 && std::abs(pdgMotherC1) == 4132)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Xicpm_Xic0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Xicpm_Xic0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Xicpm_Xic0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 4232 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 4232 && std::abs(pdgMotherC1) == 4332)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Xicpm_Omegac0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Xicpm_Omegac0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Xicpm_Omegac0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 4132 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 4132 && std::abs(pdgMotherC1) == 4332)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Xic0_Omegac0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Xic0_Omegac0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Xic0_Omegac0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 4122) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 4122)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dpm_Lcpm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dpm_Lcpm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dpm_Lcpm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 4232) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 4232)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dpm_Xicpm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dpm_Xicpm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dpm_Xicpm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 4132)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dpm_Xic0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dpm_Xic0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dpm_Xic0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 4332)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dpm_Omegac0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dpm_Omegac0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dpm_Omegac0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 4122) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 4122)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/D0_Lcpm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/D0_Lcpm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/D0_Lcpm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 4232) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 4232)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/D0_Xicpm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/D0_Xicpm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/D0_Xicpm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 4132)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/D0_Xic0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/D0_Xic0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/D0_Xic0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 4332)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/D0_Omegac0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/D0_Omegac0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/D0_Omegac0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 4122) || (std::abs(pdgMotherC2) == 431 && std::abs(pdgMotherC1) == 4122)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dspm_Lcpm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dspm_Lcpm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dspm_Lcpm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 4232) || (std::abs(pdgMotherC2) == 431 && std::abs(pdgMotherC1) == 4232)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dspm_Xicpm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dspm_Xicpm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dspm_Xicpm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 431 && std::abs(pdgMotherC1) == 4132)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dspm_Xic0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dspm_Xic0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dspm_Xic0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 431 && std::abs(pdgMotherC1) == 4332)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dspm_Omegac0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dspm_Omegac0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dspm_Omegac0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } - } - } - - template - bool fillTruePairInfo(TCollision const& collision, TMCCollisions const&, TTrack1 const& t1, TTrack2 const& t2, TCut const& cut, TAllTracks const&, TMCParticles const& mcparticles) - { - auto t1mc = mcparticles.iteratorAt(t1.emmcparticleId()); - auto t2mc = mcparticles.iteratorAt(t2.emmcparticleId()); - bool is_pair_from_same_mcevent = (t1mc.emmceventId() == t2mc.emmceventId()); - - auto mccollision1 = t1mc.template emmcevent_as(); - auto mccollision2 = t2mc.template emmcevent_as(); - if (cfgEventGeneratorType >= 0 && mccollision1.getSubGeneratorId() != cfgEventGeneratorType) { - return false; - } - if (cfgEventGeneratorType >= 0 && mccollision2.getSubGeneratorId() != cfgEventGeneratorType) { - return false; - } - if (!isInAcceptance(t1mc) || !isInAcceptance(t2mc)) { - return false; - } - - dileptonSV candidate; - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (dielectroncuts.cfg_pid_scheme == static_cast(DielectronCut::PIDSchemes::kPIDML)) { - if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { - return false; - } - } else { // cut-based - if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { - return false; - } - } - - auto trackParCov1 = getTrackParCov(t1); - auto trackParCov2 = getTrackParCov(t2); - if (!findSV(collision, trackParCov1, trackParCov2, candidate)) { - return false; - } - - if (!cut.IsSelectedPair(t1, t2)) { - return false; - } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { - return false; - } - if (!map_best_match_globalmuon[t1.globalIndex()] || !map_best_match_globalmuon[t2.globalIndex()]) { - return false; - } - - bool isCorrectMatch1 = t1.emmcparticleId() == t1.emmftmcparticleId(); - bool isCorrectMatch2 = t2.emmcparticleId() == t2.emmftmcparticleId(); - - if (dimuoncuts.acceptOnlyCorrectMatch) { - if (t1.trackType() == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) && !isCorrectMatch1) { - return false; - } - if (t2.trackType() == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) && !isCorrectMatch2) { - return false; - } - } - if (dimuoncuts.acceptOnlyWrongMatch) { - if (t1.trackType() == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) && isCorrectMatch1) { - return false; - } - if (t2.trackType() == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) && isCorrectMatch2) { - return false; - } - } - if (dimuoncuts.acceptOnlyXORMatching) { // this is dummy comment. - if (t1.trackType() == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) && t2.trackType() == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack)) { - if (!(isCorrectMatch1 ^ isCorrectMatch2)) { - return false; - } - } - } - - auto trackParCov1 = o2::aod::fwdtrackutils::getTrackParCovFwd(t1, t1); - auto trackParCov2 = o2::aod::fwdtrackutils::getTrackParCovFwd(t2, t2); - if (!findSVFwd(collision, trackParCov1, trackParCov2, candidate)) { - return false; - } - - if (!cut.IsSelectedPair(t1, t2)) { - return false; - } - } - - float pt1 = 0.f, eta1 = 0.f, phi1 = 0.f, pt2 = 0.f, eta2 = 0.f, phi2 = 0.f; - if constexpr (isSmeared) { - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - pt1 = t1mc.ptSmeared(); - eta1 = t1mc.etaSmeared(); - phi1 = t1mc.phiSmeared(); - pt2 = t2mc.ptSmeared(); - eta2 = t2mc.etaSmeared(); - phi2 = t2mc.phiSmeared(); - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - if (dimuoncuts.cfg_track_type == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack)) { - pt1 = t1mc.ptSmeared_sa_muon(); - eta1 = t1mc.etaSmeared_sa_muon(); - phi1 = t1mc.phiSmeared_sa_muon(); - pt2 = t2mc.ptSmeared_sa_muon(); - eta2 = t2mc.etaSmeared_sa_muon(); - phi2 = t2mc.phiSmeared_sa_muon(); - } else if (dimuoncuts.cfg_track_type == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack)) { - pt1 = t1mc.ptSmeared_gl_muon(); - eta1 = t1mc.etaSmeared_gl_muon(); - phi1 = t1mc.phiSmeared_gl_muon(); - pt2 = t2mc.ptSmeared_gl_muon(); - eta2 = t2mc.etaSmeared_gl_muon(); - phi2 = t2mc.phiSmeared_gl_muon(); - } else { - pt1 = t1mc.pt(); - eta1 = t1mc.eta(); - phi1 = t1mc.phi(); - pt2 = t2mc.pt(); - eta2 = t2mc.eta(); - phi2 = t2mc.phi(); - } - } - } else { - pt1 = t1mc.pt(); - eta1 = t1mc.eta(); - phi1 = t1mc.phi(); - pt2 = t2mc.pt(); - eta2 = t2mc.eta(); - phi2 = t2mc.phi(); - } - - ROOT::Math::PtEtaPhiMVector v1mc(pt1, eta1, phi1, leptonM1); - ROOT::Math::PtEtaPhiMVector v2mc(pt2, eta2, phi2, leptonM2); - ROOT::Math::PtEtaPhiMVector v12mc = v1mc + v2mc; - - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (v12mc.Rapidity() < dielectroncuts.cfg_min_pair_y || dielectroncuts.cfg_max_pair_y < v12mc.Rapidity()) { - return false; - } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - if (v12mc.Rapidity() < dimuoncuts.cfg_min_pair_y || dimuoncuts.cfg_max_pair_y < v12mc.Rapidity()) { - return false; - } - } - - float weight = 1.f; - if (cfgApplyWeightTTCA) { - weight = map_weight[std::make_pair(t1.globalIndex(), t2.globalIndex())]; - } - // LOGF(info, "t1.sign() = %d, t2.sign() = %d, map_weight[std::make_pair(%d, %d)] = %f", t1.sign(), t2.sign(), t1.globalIndex(), t2.globalIndex(), weight); - - // ROOT::Math::PtEtaPhiMVector v1(t1.pt(), t1.eta(), t1.phi(), leptonM1); - // ROOT::Math::PtEtaPhiMVector v2(t2.pt(), t2.eta(), t2.phi(), leptonM2); - ROOT::Math::PtEtaPhiMVector v1(candidate.pt1, candidate.eta1, candidate.phi1, leptonM1); - ROOT::Math::PtEtaPhiMVector v2(candidate.pt2, candidate.eta2, candidate.phi2, leptonM2); - ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; - - float pair_dca = 999.f; - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCAQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::dca3DinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dca3DinSigma(t2)); - if (dielectroncuts.cfgDCAType == 1) { - pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCAQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t2)); - } else if (dielectroncuts.cfgDCAType == 2) { - pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCAQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t2)); - } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCAQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::fwdDcaXYinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::fwdDcaXYinSigma(t2)); - } - float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(t1.px(), t1.py(), t1.pz(), t2.px(), t2.py(), t2.pz(), t1.sign(), t2.sign(), d_bz); - - float deta = v1.Eta() - v2.Eta(); - float dphi = v1.Phi() - v2.Phi(); - o2::math_utils::bringToPMPi(dphi); - - float aco = 1.f - std::fabs(dphi) / M_PI; - float asym = std::fabs(v1.Pt() - v2.Pt()) / (v1.Pt() + v2.Pt()); - float dphi_e_ee = v1.Phi() - v12.Phi(); - o2::math_utils::bringToPMPi(dphi_e_ee); - dphi = RecoDecay::constrainAngle(dphi, -o2::constants::math::PIHalf, 1); // shift dphi in [-pi/2, +3pi/2] rad. - - std::array arrP1 = {t1.px(), t1.py(), t1.pz(), leptonM1}; - std::array arrP2 = {t2.px(), t2.py(), t2.pz(), leptonM2}; - float cos_thetaPol = 999, phiPol = 999.f; - if (cfgPolarizationFrame == 0) { - o2::aod::pwgem::dilepton::utils::pairutil::getAngleCS(arrP1, arrP2, beamE1, beamE2, beamP1, beamP2, t1.sign(), cos_thetaPol, phiPol); - } else if (cfgPolarizationFrame == 1) { - o2::aod::pwgem::dilepton::utils::pairutil::getAngleHX(arrP1, arrP2, beamE1, beamE2, beamP1, beamP2, t1.sign(), cos_thetaPol, phiPol); - } - o2::math_utils::bringToPMPi(phiPol); - float quadmom = (3.f * std::pow(cos_thetaPol, 2) - 1.f) / 2.f; - - if ((o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2ProngsWithoutPDG(t1mc, t2mc) > 0 || o2::aod::pwgem::dilepton::utils::mcutil::IsHF(t1mc, t2mc, mcparticles) > 0) && is_pair_from_same_mcevent) { // for bkg study - if (std::abs(t1mc.pdgCode()) != pdg_lepton || std::abs(t2mc.pdgCode()) != pdg_lepton) { // hh or lh correlated bkg - if (std::abs(t1mc.pdgCode()) != pdg_lepton && std::abs(t2mc.pdgCode()) != pdg_lepton) { // hh correlated bkg - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Pair/corr_bkg_hh/uls/hs"), v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); - } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ - fRegistry.fill(HIST("Pair/corr_bkg_hh/lspp/hs"), v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); - } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- - fRegistry.fill(HIST("Pair/corr_bkg_hh/lsmm/hs"), v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); - } - } else { // lh correlated bkg - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Pair/corr_bkg_lh/uls/hs"), v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); - } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ - fRegistry.fill(HIST("Pair/corr_bkg_lh/lspp/hs"), v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); - } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- - fRegistry.fill(HIST("Pair/corr_bkg_lh/lsmm/hs"), v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); - } - } - } - } else { // true combinatorial bkg - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Pair/comb_bkg/uls/hs"), v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); - } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ - fRegistry.fill(HIST("Pair/comb_bkg/lspp/hs"), v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); - } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- - fRegistry.fill(HIST("Pair/comb_bkg/lsmm/hs"), v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); - } - } - - if (std::abs(t1mc.pdgCode()) != pdg_lepton || std::abs(t2mc.pdgCode()) != pdg_lepton) { - return false; - } - - if (!is_pair_from_same_mcevent) { - return false; - } - if (cfgRequireTrueAssociation && (t1mc.emmceventId() != collision.emmceventId() || t2mc.emmceventId() != collision.emmceventId())) { - return false; - } - int mother_id = std::max({FindSMULS(t1mc, t2mc, mcparticles), FindSMULS(t2mc, t1mc, mcparticles), FindSMLSPP(t1mc, t2mc, mcparticles), FindSMLSMM(t1mc, t2mc, mcparticles)}); - int hfee_type = o2::aod::pwgem::dilepton::utils::mcutil::IsHF(t1mc, t2mc, mcparticles); - if (mother_id < 0 && hfee_type < 0) { - return false; - } - - if (mother_id > -1) { - auto mcmother = mcparticles.iteratorAt(mother_id); - if (mcmother.isPhysicalPrimary() || mcmother.producedByGenerator()) { - if ((t1mc.isPhysicalPrimary() || t1mc.producedByGenerator()) && (t2mc.isPhysicalPrimary() || t2mc.producedByGenerator())) { - switch (std::abs(mcmother.pdgCode())) { - case 111: - if (o2::aod::pwgem::dilepton::utils::mcutil::IsFromCharm(mcmother, mcparticles) < 0 && o2::aod::pwgem::dilepton::utils::mcutil::IsFromBeauty(mcmother, mcparticles) < 0) { // prompt pi0 - fillRecHistograms<1>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // prompt pi0 - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Pair/sm/PromptPi0/uls/hMvsPhiV"), phiv, v12.M()); - } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ - fRegistry.fill(HIST("Pair/sm/PromptPi0/lspp/hMvsPhiV"), phiv, v12.M()); - } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- - fRegistry.fill(HIST("Pair/sm/PromptPi0/lsmm/hMvsPhiV"), phiv, v12.M()); - } - } - } else { // non-prompt pi0 - fillRecHistograms<2>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // non-prompt pi0 - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Pair/sm/NonPromptPi0/uls/hMvsPhiV"), phiv, v12.M()); - } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ - fRegistry.fill(HIST("Pair/sm/NonPromptPi0/lspp/hMvsPhiV"), phiv, v12.M()); - } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- - fRegistry.fill(HIST("Pair/sm/NonPromptPi0/lsmm/hMvsPhiV"), phiv, v12.M()); - } - } - } - break; - case 221: - fillRecHistograms<3>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // eta - break; - case 331: - fillRecHistograms<4>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // eta' - break; - case 113: - fillRecHistograms<5>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // rho - break; - case 223: - fillRecHistograms<6>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // omega - if (mcmother.daughtersIds().size() == 2) { - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Pair/sm/Omega2ll/uls/hs"), v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // omeag->ee - } - } - break; - case 333: - fillRecHistograms<7>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // phi - if (mcmother.daughtersIds().size() == 2) { - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Pair/sm/Phi2ll/uls/hs"), v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // phi->ee - } - } - break; - case 443: - if (o2::aod::pwgem::dilepton::utils::mcutil::IsFromBeauty(mcmother, mcparticles) > 0) { - fillRecHistograms<9>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // non-prompt J/psi - } else { - fillRecHistograms<8>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // prompt J/psi - } - break; - case 100443: - if (o2::aod::pwgem::dilepton::utils::mcutil::IsFromBeauty(mcmother, mcparticles) > 0) { - fillRecHistograms<11>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // non-prompt psi2S - } else { - fillRecHistograms<10>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // prompt psi2S - } - break; - default: - break; - } - } else if (!(t1mc.isPhysicalPrimary() || t1mc.producedByGenerator()) && !(t2mc.isPhysicalPrimary() || t2mc.producedByGenerator())) { - switch (std::abs(mcmother.pdgCode())) { - case 22: - fillRecHistograms<0>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // photon conversion - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - fRegistry.fill(HIST("Pair/sm/Photon/uls/hMvsPhiV"), phiv, v12.M()); - float rxy_gen = std::sqrt(std::pow(t1mc.vx(), 2) + std::pow(t1mc.vy(), 2)); - fRegistry.fill(HIST("Pair/sm/Photon/uls/hMvsRxy"), rxy_gen, v12.M()); - } - break; - default: - break; - } - } // end of primary/secondary selection - } // end of primary selection for same mother - } else if (hfee_type > -1) { - if ((t1mc.isPhysicalPrimary() || t1mc.producedByGenerator()) && (t2mc.isPhysicalPrimary() || t2mc.producedByGenerator())) { - auto mp1 = mcparticles.iteratorAt(t1mc.mothersIds()[0]); - auto mp2 = mcparticles.iteratorAt(t2mc.mothersIds()[0]); - switch (hfee_type) { - case static_cast(o2::aod::pwgem::dilepton::utils::mcutil::EM_HFeeType::kCe_Ce): - fillRecHistograms<15>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // c2l_c2l - break; - case static_cast(o2::aod::pwgem::dilepton::utils::mcutil::EM_HFeeType::kBe_Be): - fillRecHistograms<16>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // b2l_b2l - break; - case static_cast(o2::aod::pwgem::dilepton::utils::mcutil::EM_HFeeType::kBCe_BCe): - fillRecHistograms<17>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // b2c2l_b2c2l - break; - case static_cast(o2::aod::pwgem::dilepton::utils::mcutil::EM_HFeeType::kBCe_Be_SameB): - fillRecHistograms<18>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // b2c2l_b2l_sameb - break; - case static_cast(o2::aod::pwgem::dilepton::utils::mcutil::EM_HFeeType::kBCe_Be_DiffB): - fillRecHistograms<19>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // b2c2l_b2l_diffb - break; - default: - break; - } - } - } // end of HF evaluation - return true; - } - - template - bool fillGenPairInfo(TMCParticle const& t1, TMCParticle const& t2, TMCParticles const& mcparticles) - { - if (!t1.isPhysicalPrimary() && !t1.producedByGenerator()) { - return false; - } - if (!t2.isPhysicalPrimary() && !t2.producedByGenerator()) { - return false; - } - - int mother_id = std::max({FindSMULS(t1, t2, mcparticles), FindSMULS(t2, t1, mcparticles), FindSMLSPP(t1, t2, mcparticles), FindSMLSMM(t1, t2, mcparticles)}); - int hfee_type = o2::aod::pwgem::dilepton::utils::mcutil::IsHF(t1, t2, mcparticles); - if (mother_id < 0 && hfee_type < 0) { - return false; - } - - float pt1 = 0.f, eta1 = 0.f, phi1 = 0.f, pt2 = 0.f, eta2 = 0.f, phi2 = 0.f; - if constexpr (isSmeared) { - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - pt1 = t1.ptSmeared(); - eta1 = t1.etaSmeared(); - phi1 = t1.phiSmeared(); - pt2 = t2.ptSmeared(); - eta2 = t2.etaSmeared(); - phi2 = t2.phiSmeared(); - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - if (dimuoncuts.cfg_track_type == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack)) { - pt1 = t1.ptSmeared_sa_muon(); - eta1 = t1.etaSmeared_sa_muon(); - phi1 = t1.phiSmeared_sa_muon(); - pt2 = t2.ptSmeared_sa_muon(); - eta2 = t2.etaSmeared_sa_muon(); - phi2 = t2.phiSmeared_sa_muon(); - } else if (dimuoncuts.cfg_track_type == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack)) { - pt1 = t1.ptSmeared_gl_muon(); - eta1 = t1.etaSmeared_gl_muon(); - phi1 = t1.phiSmeared_gl_muon(); - pt2 = t2.ptSmeared_gl_muon(); - eta2 = t2.etaSmeared_gl_muon(); - phi2 = t2.phiSmeared_gl_muon(); - } else { - pt1 = t1.pt(); - eta1 = t1.eta(); - phi1 = t1.phi(); - pt2 = t2.pt(); - eta2 = t2.eta(); - phi2 = t2.phi(); - } - } - } else { - pt1 = t1.pt(); - eta1 = t1.eta(); - phi1 = t1.phi(); - pt2 = t2.pt(); - eta2 = t2.eta(); - phi2 = t2.phi(); - } - - ROOT::Math::PtEtaPhiMVector v1(pt1, eta1, phi1, leptonM1); - ROOT::Math::PtEtaPhiMVector v2(pt2, eta2, phi2, leptonM2); - ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; - - float deta = v1.Eta() - v2.Eta(); - float dphi = v1.Phi() - v2.Phi(); - o2::math_utils::bringToPMPi(dphi); - - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (v12.Rapidity() < dielectroncuts.cfg_min_pair_y || dielectroncuts.cfg_max_pair_y < v12.Rapidity()) { - return false; - } - // if (dielectroncuts.cfg_apply_detadphi && std::pow(deta / dielectroncuts.cfg_min_deta, 2) + std::pow(dphi / dielectroncuts.cfg_min_dphi, 2) < 1.f) { - // continue; - // } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - if (v12.Rapidity() < dimuoncuts.cfg_min_pair_y || dimuoncuts.cfg_max_pair_y < v12.Rapidity()) { - return false; - } - // if (dimuoncuts.cfg_apply_detadphi && std::pow(deta / dimuoncuts.cfg_min_deta, 2) + std::pow(dphi / dimuoncuts.cfg_min_dphi, 2) < 1.f) { - // continue; - // } - } - - int sign1 = -t1.pdgCode() / pdg_lepton; - int sign2 = -t2.pdgCode() / pdg_lepton; - - float aco = 1.f - std::fabs(dphi) / M_PI; - float asym = std::fabs(v1.Pt() - v2.Pt()) / (v1.Pt() + v2.Pt()); - float dphi_e_ee = v1.Phi() - v12.Phi(); - o2::math_utils::bringToPMPi(dphi_e_ee); - dphi = RecoDecay::constrainAngle(dphi, -o2::constants::math::PIHalf, 1); // shift dphi in [-pi/2, +3pi/2] rad. after deta-dphi cut. - - std::array arrP1 = {static_cast(v1.Px()), static_cast(v1.Py()), static_cast(v1.Pz()), leptonM1}; - std::array arrP2 = {static_cast(v2.Px()), static_cast(v2.Py()), static_cast(v2.Pz()), leptonM2}; - float cos_thetaPol = 999, phiPol = 999.f; - if (cfgPolarizationFrame == 0) { - o2::aod::pwgem::dilepton::utils::pairutil::getAngleCS(arrP1, arrP2, beamE1, beamE2, beamP1, beamP2, -t1.pdgCode() / pdg_lepton, cos_thetaPol, phiPol); - } else if (cfgPolarizationFrame == 1) { - o2::aod::pwgem::dilepton::utils::pairutil::getAngleHX(arrP1, arrP2, beamE1, beamE2, beamP1, beamP2, -t1.pdgCode() / pdg_lepton, cos_thetaPol, phiPol); - } - o2::math_utils::bringToPMPi(phiPol); - float quadmom = (3.f * std::pow(cos_thetaPol, 2) - 1.f) / 2.f; - - if (!isInAcceptance(t1) || !isInAcceptance(t2)) { - return false; - } - - float weight = 1.f; - if (mother_id > -1) { - auto mcmother = mcparticles.iteratorAt(mother_id); - if (mcmother.isPhysicalPrimary() || mcmother.producedByGenerator()) { - switch (std::abs(mcmother.pdgCode())) { - case 111: - if (o2::aod::pwgem::dilepton::utils::mcutil::IsFromCharm(mcmother, mcparticles) < 0 && o2::aod::pwgem::dilepton::utils::mcutil::IsFromBeauty(mcmother, mcparticles) < 0) { // prompt pi0 - fillGenHistograms<1>(sign1, sign2, 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // prompt pi0 - } else { // non-prompt pi0 - fillGenHistograms<2>(sign1, sign2, 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // non-prompt pi0 - } - break; - case 221: - fillGenHistograms<3>(sign1, sign2, 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // eta - break; - case 331: - fillGenHistograms<4>(sign1, sign2, 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // eta' - break; - case 113: - fillGenHistograms<5>(sign1, sign2, 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // rho - break; - case 223: - fillGenHistograms<6>(sign1, sign2, 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // omega - if (mcmother.daughtersIds().size() == 2) { - fRegistry.fill(HIST("Generated/sm/Omega2ll/uls/hs"), v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // omega->ee - } - break; - case 333: - fillGenHistograms<7>(sign1, sign2, 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // phi - if (mcmother.daughtersIds().size() == 2) { - fRegistry.fill(HIST("Generated/sm/Phi2ll/uls/hs"), v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee)); // phi->ee - } - break; - case 443: - if (o2::aod::pwgem::dilepton::utils::mcutil::IsFromBeauty(mcmother, mcparticles) > 0) { - fillGenHistograms<9>(sign1, sign2, 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // non-prompt J/psi - } else { - fillGenHistograms<8>(sign1, sign2, 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // prompt J/psi - } - break; - case 100443: - if (o2::aod::pwgem::dilepton::utils::mcutil::IsFromBeauty(mcmother, mcparticles) > 0) { - fillGenHistograms<11>(sign1, sign2, 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // non-prompt psi2S - } else { - fillGenHistograms<10>(sign1, sign2, 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // prompt psi2S - } - break; - default: - break; - } - } - } else if (hfee_type > -1) { - auto mp1 = mcparticles.iteratorAt(t1.mothersIds()[0]); - auto mp2 = mcparticles.iteratorAt(t2.mothersIds()[0]); - switch (hfee_type) { - case static_cast(o2::aod::pwgem::dilepton::utils::mcutil::EM_HFeeType::kCe_Ce): - fillGenHistograms<15>(sign1, sign2, mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // c2l_c2l - break; - case static_cast(o2::aod::pwgem::dilepton::utils::mcutil::EM_HFeeType::kBe_Be): - fillGenHistograms<16>(sign1, sign2, 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // b2l_b2l - break; - case static_cast(o2::aod::pwgem::dilepton::utils::mcutil::EM_HFeeType::kBCe_BCe): - fillGenHistograms<17>(sign1, sign2, 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // b2c2l_b2c2l - break; - case static_cast(o2::aod::pwgem::dilepton::utils::mcutil::EM_HFeeType::kBCe_Be_SameB): - fillGenHistograms<18>(sign1, sign2, 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // b2c2l_b2l_sameb - break; - case static_cast(o2::aod::pwgem::dilepton::utils::mcutil::EM_HFeeType::kBCe_Be_DiffB): - fillGenHistograms<19>(sign1, sign2, 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // b2c2l_b2l_diffb - break; - default: - break; - } - } // end of HF evaluation - return false; - } - - template - bool fillGenParticleAcc(TMCParticle const& mcParticle, TMCParticles const& mcParticles) - { - if (!mcParticle.isPhysicalPrimary() && !mcParticle.producedByGenerator()) { - return false; - } - if (mcParticle.daughtersIds().size() < 2) { - return false; - } - - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (mcParticle.y() < dielectroncuts.cfg_min_pair_y || dielectroncuts.cfg_max_pair_y < mcParticle.y()) { - return false; - } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - if (mcParticle.y() < dimuoncuts.cfg_min_pair_y || dimuoncuts.cfg_max_pair_y < mcParticle.y()) { - return false; - } - } - - int pdg = mcParticle.pdgCode(); - if (std::abs(pdg) == 113 && mcParticle.daughtersIds().size() != 2) { // reject dalitz decay - return false; - } - if (std::abs(pdg) == 223 && mcParticle.daughtersIds().size() != 2) { // reject dalitz decay - return false; - } - if (std::abs(pdg) == 333 && mcParticle.daughtersIds().size() != 2) { // reject dalitz decay - return false; - } - // accept radiative decay of charmonia (ee + multiple gamma). - - // float pt1 = 0.f, eta1 = 0.f, phi1 = 0.f, sign1 = 0.f; - // float pt2 = 0.f, eta2 = 0.f, phi2 = 0.f, sign2 = 0.f; - std::vector> vDau; - vDau.reserve(mcParticle.daughtersIds().size()); - for (const auto& daughterId : mcParticle.daughtersIds()) { - auto dau = mcParticles.rawIteratorAt(daughterId); - if (std::abs(dau.pdgCode()) == pdg_lepton) { - vDau.emplace_back(std::array{dau.pt(), dau.eta(), dau.phi(), dau.pdgCode() > 0 ? -1.f : +1.f}); - } - } - if (vDau.size() != 2 || vDau[0][3] * vDau[1][3] > 0.f) { // decay objects must be ULS 2 leptons. - vDau.clear(); - vDau.shrink_to_fit(); - return false; - } - - // LOGF(info, "mcParticle.globalIndex() = %d, mcParticle.pdgCode() = %d, mcParticle.daughtersIds().size() = %d", mcParticle.globalIndex(), mcParticle.pdgCode(), mcParticle.daughtersIds().size()); - - ROOT::Math::PtEtaPhiMVector v1(vDau[0][0], vDau[0][1], vDau[0][2], leptonM1); - ROOT::Math::PtEtaPhiMVector v2(vDau[1][0], vDau[1][1], vDau[1][2], leptonM2); - ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; - - std::array arrP1 = {static_cast(v1.Px()), static_cast(v1.Py()), static_cast(v1.Pz()), leptonM1}; - std::array arrP2 = {static_cast(v2.Px()), static_cast(v2.Py()), static_cast(v2.Pz()), leptonM2}; - float cos_thetaPol = 999, phiPol = 999.f; - if (cfgPolarizationFrame == 0) { - o2::aod::pwgem::dilepton::utils::pairutil::getAngleCS(arrP1, arrP2, beamE1, beamE2, beamP1, beamP2, vDau[0][3] > 0 ? 1 : -1, cos_thetaPol, phiPol); - } else if (cfgPolarizationFrame == 1) { - o2::aod::pwgem::dilepton::utils::pairutil::getAngleHX(arrP1, arrP2, beamE1, beamE2, beamP1, beamP2, vDau[0][3] > 0 ? 1 : -1, cos_thetaPol, phiPol); - } - o2::math_utils::bringToPMPi(phiPol); - float quadmom = (3.f * std::pow(cos_thetaPol, 2) - 1.f) / 2.f; - - float weight = 1.f; - switch (std::abs(mcParticle.pdgCode())) { - case 113: - fRegistry.fill(HIST("Generated/VM/All/Rho/hs"), v12.M(), mcParticle.pt(), mcParticle.y(), cos_thetaPol, phiPol, quadmom, weight); - if (isInAcceptance(v1.Pt(), v1.Eta()) && isInAcceptance(v2.Pt(), v2.Eta())) { - fRegistry.fill(HIST("Generated/VM/Acc/Rho/hs"), v12.M(), mcParticle.pt(), mcParticle.y(), cos_thetaPol, phiPol, quadmom, weight); - } - break; - case 223: - fRegistry.fill(HIST("Generated/VM/All/Omega/hs"), v12.M(), mcParticle.pt(), mcParticle.y(), cos_thetaPol, phiPol, quadmom, weight); - if (isInAcceptance(v1.Pt(), v1.Eta()) && isInAcceptance(v2.Pt(), v2.Eta())) { - fRegistry.fill(HIST("Generated/VM/Acc/Omega/hs"), v12.M(), mcParticle.pt(), mcParticle.y(), cos_thetaPol, phiPol, quadmom, weight); - } - break; - case 333: - fRegistry.fill(HIST("Generated/VM/All/Phi/hs"), v12.M(), mcParticle.pt(), mcParticle.y(), cos_thetaPol, phiPol, quadmom, weight); - if (isInAcceptance(v1.Pt(), v1.Eta()) && isInAcceptance(v2.Pt(), v2.Eta())) { - fRegistry.fill(HIST("Generated/VM/Acc/Phi/hs"), v12.M(), mcParticle.pt(), mcParticle.y(), cos_thetaPol, phiPol, quadmom, weight); - } - break; - case 443: - if (o2::aod::pwgem::dilepton::utils::mcutil::IsFromBeauty(mcParticle, mcParticles) > 0) { - fRegistry.fill(HIST("Generated/VM/All/NonPromptJPsi/hs"), v12.M(), mcParticle.pt(), mcParticle.y(), cos_thetaPol, phiPol, quadmom, weight); - if (isInAcceptance(v1.Pt(), v1.Eta()) && isInAcceptance(v2.Pt(), v2.Eta())) { - fRegistry.fill(HIST("Generated/VM/Acc/NonPromptJPsi/hs"), v12.M(), mcParticle.pt(), mcParticle.y(), cos_thetaPol, phiPol, quadmom, weight); - } - } else { - fRegistry.fill(HIST("Generated/VM/All/PromptJPsi/hs"), v12.M(), mcParticle.pt(), mcParticle.y(), cos_thetaPol, phiPol, quadmom, weight); - if (isInAcceptance(v1.Pt(), v1.Eta()) && isInAcceptance(v2.Pt(), v2.Eta())) { - fRegistry.fill(HIST("Generated/VM/Acc/PromptJPsi/hs"), v12.M(), mcParticle.pt(), mcParticle.y(), cos_thetaPol, phiPol, quadmom, weight); - } - } - break; - default: - break; - } - - vDau.clear(); - vDau.shrink_to_fit(); - return false; - } - - template - void runTruePairing(TCollisions const& collisions, TMCLeptons const& posTracks, TMCLeptons const& negTracks, TPreslice const& perCollision, TCut const& cut, TAllTracks const& tracks, TMCCollisions const& mccollisions, TMCParticles const& mcparticles) - { - for (const auto& collision : collisions) { - initCCDB(collision); - float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; - if (centralities[cfgCentEstimator] < eventcuts.cfgCentMin || eventcuts.cfgCentMax < centralities[cfgCentEstimator]) { - continue; - } - - o2::aod::pwgem::dilepton::utils::eventhistogram::fillEventInfo<0, -1>(&fRegistry, collision); - if (!fEMEventCut.IsSelected(collision)) { - continue; - } - if (eventcuts.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { - continue; - } - o2::aod::pwgem::dilepton::utils::eventhistogram::fillEventInfo<1, -1>(&fRegistry, collision); - fRegistry.fill(HIST("Event/before/hCollisionCounter"), o2::aod::pwgem::dilepton::utils::eventhistogram::nbin_ev); // accepted - fRegistry.fill(HIST("Event/after/hCollisionCounter"), o2::aod::pwgem::dilepton::utils::eventhistogram::nbin_ev); // accepted - - auto posTracks_per_coll = posTracks.sliceByCached(perCollision, collision.globalIndex(), cache); - auto negTracks_per_coll = negTracks.sliceByCached(perCollision, collision.globalIndex(), cache); - // LOGF(info, "centrality = %f , posTracks_per_coll.size() = %d, negTracks_per_coll.size() = %d", centralities[cfgCentEstimator], posTracks_per_coll.size(), negTracks_per_coll.size()); - - for (const auto& [pos, neg] : combinations(o2::soa::CombinationsFullIndexPolicy(posTracks_per_coll, negTracks_per_coll))) { // ULS - fillTruePairInfo(collision, mccollisions, pos, neg, cut, tracks, mcparticles); - } // end of ULS pair loop - - for (const auto& [pos1, pos2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(posTracks_per_coll, posTracks_per_coll))) { // LS++ - fillTruePairInfo(collision, mccollisions, pos1, pos2, cut, tracks, mcparticles); - } // end of LS++ pair loop - - for (const auto& [neg1, neg2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(negTracks_per_coll, negTracks_per_coll))) { // LS-- - fillTruePairInfo(collision, mccollisions, neg1, neg2, cut, tracks, mcparticles); - } // end of LS-- pair loop - - } // end of collision loop - } - - template - void runGenInfo(TCollisions const& collisions, TMCCollisions const& mccollisions, TMCLeptons const& posTracksMC, TMCLeptons const& negTracksMC, TMCParticles const& mcparticles) - { - for (const auto& mccollision : mccollisions) { - if (cfgEventGeneratorType >= 0 && mccollision.getSubGeneratorId() != cfgEventGeneratorType) { - continue; - } - fRegistry.fill(HIST("MCEvent/before/hZvtx"), mccollision.posZ()); - if (mccollision.mpemeventId() < 0) { - continue; - } - auto collision = collisions.rawIteratorAt(mccollision.mpemeventId()); - - float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; - if (centralities[cfgCentEstimator] < eventcuts.cfgCentMin || eventcuts.cfgCentMax < centralities[cfgCentEstimator]) { - continue; - } - fRegistry.fill(HIST("MCEvent/before/hZvtx_rec"), mccollision.posZ()); - if (!fEMEventCut.IsSelected(collision)) { - continue; - } - if (eventcuts.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { - continue; - } - if (!(eventcuts.cfgTrackOccupancyMin <= collision.trackOccupancyInTimeRange() && collision.trackOccupancyInTimeRange() < eventcuts.cfgTrackOccupancyMax)) { - continue; - } - if (!(eventcuts.cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange() && collision.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { - continue; - } - fRegistry.fill(HIST("MCEvent/after/hZvtx"), mccollision.posZ()); - - auto posTracks_per_coll = posTracksMC.sliceByCachedUnsorted(o2::aod::emmcparticle::emmceventId, mccollision.globalIndex(), cache); - auto negTracks_per_coll = negTracksMC.sliceByCachedUnsorted(o2::aod::emmcparticle::emmceventId, mccollision.globalIndex(), cache); - - for (const auto& [t1, t2] : combinations(o2::soa::CombinationsFullIndexPolicy(posTracks_per_coll, negTracks_per_coll))) { // ULS - fillGenPairInfo(t1, t2, mcparticles); - } // end of true ULS pair loop - - for (const auto& [t1, t2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(posTracks_per_coll, posTracks_per_coll))) { // LS++ - fillGenPairInfo(t1, t2, mcparticles); - } // end of true LS++ pair loop - - for (const auto& [t1, t2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(negTracks_per_coll, negTracks_per_coll))) { // LS-- - fillGenPairInfo(t1, t2, mcparticles); - } // end of true LS-- pair loop - - // acceptance for polarization of vector mesons - auto mcParticles_per_coll = mcparticles.sliceBy(perMcCollision, mccollision.globalIndex()); - for (const auto& mcParticle : mcParticles_per_coll) { - if (!mcParticle.isPhysicalPrimary() && !mcParticle.producedByGenerator()) { - continue; - } - int pdg = std::abs(mcParticle.pdgCode()); - if (pdg == 113 || pdg == 223 || pdg == 333 || pdg == 443) { // select only VMs - fillGenParticleAcc(mcParticle, mcparticles); // VMs that decay into dilepton are stored in derived data. This is sufficient for polarization. - } - } // end of mc particle loop - } // end of collision loop - } - - template - bool isPairOK(TTrack1 const& t1, TTrack2 const& t2, TCut const& cut, TAllTracks const&) - { - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (dielectroncuts.cfg_pid_scheme == static_cast(DielectronCut::PIDSchemes::kPIDML)) { - if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { - return false; - } - } else { // cut-based - if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { - return false; - } - } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { - return false; - } - if (!map_best_match_globalmuon[t1.globalIndex()] || !map_best_match_globalmuon[t2.globalIndex()]) { - return false; - } - // if (!o2::aod::pwgem::dilepton::utils::emtrackutil::isBestMatch(t1, cut, tracks)) { - // return false; - // } - // if (!o2::aod::pwgem::dilepton::utils::emtrackutil::isBestMatch(t2, cut, tracks)) { - // return false; - // } - } - - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (!cut.template IsSelectedPair(t1, t2)) { - return false; - } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - if (!cut.template IsSelectedPair(t1, t2)) { - return false; - } - } - return true; - } - - std::map, float> map_weight; // -> float - template - void fillPairWeightMap(TCollisions const& collisions, TTracks1 const& posTracks, TTracks2 const& negTracks, TPresilce const& perCollision, TCut const& cut, TAllTracks const& tracks, TMCCollisions const&, TMCParticles const& mcparticles) - { - std::vector> passed_pairIds; - passed_pairIds.reserve(posTracks.size() * negTracks.size()); - - for (const auto& collision : collisions) { - initCCDB(collision); - const float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; - if (centralities[cfgCentEstimator] < eventcuts.cfgCentMin || eventcuts.cfgCentMax < centralities[cfgCentEstimator]) { - continue; - } - - if (!fEMEventCut.IsSelected(collision)) { - continue; - } - if (eventcuts.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { - continue; - } - - // auto mccollision = collision.template emmcevent_as(); - // if (cfgEventGeneratorType >= 0 && mccollision.getSubGeneratorId() != cfgEventGeneratorType) { - // continue; - // } - - auto posTracks_per_coll = posTracks.sliceByCached(perCollision, collision.globalIndex(), cache); - auto negTracks_per_coll = negTracks.sliceByCached(perCollision, collision.globalIndex(), cache); - - for (const auto& [pos, neg] : combinations(o2::soa::CombinationsFullIndexPolicy(posTracks_per_coll, negTracks_per_coll))) { // ULS - auto mcpos = mcparticles.iteratorAt(pos.emmcparticleId()); - auto mccollision_from_pos = mcpos.template emmcevent_as(); - if (cfgEventGeneratorType >= 0 && mccollision_from_pos.getSubGeneratorId() != cfgEventGeneratorType) { - continue; - } - auto mcneg = mcparticles.iteratorAt(neg.emmcparticleId()); - auto mccollision_from_neg = mcneg.template emmcevent_as(); - if (cfgEventGeneratorType >= 0 && mccollision_from_neg.getSubGeneratorId() != cfgEventGeneratorType) { - continue; - } - if (cfgRequireTrueAssociation && (mcpos.emmceventId() != collision.emmceventId() || mcneg.emmceventId() != collision.emmceventId())) { - continue; - } - - if (isPairOK(pos, neg, cut, tracks)) { - passed_pairIds.emplace_back(std::make_pair(pos.globalIndex(), neg.globalIndex())); - } - } - for (const auto& [pos1, pos2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(posTracks_per_coll, posTracks_per_coll))) { // LS++ - auto mcpos1 = mcparticles.iteratorAt(pos1.emmcparticleId()); - auto mccollision_from_pos1 = mcpos1.template emmcevent_as(); - if (cfgEventGeneratorType >= 0 && mccollision_from_pos1.getSubGeneratorId() != cfgEventGeneratorType) { - continue; - } - auto mcpos2 = mcparticles.iteratorAt(pos2.emmcparticleId()); - auto mccollision_from_pos2 = mcpos2.template emmcevent_as(); - if (cfgEventGeneratorType >= 0 && mccollision_from_pos2.getSubGeneratorId() != cfgEventGeneratorType) { - continue; - } - if (cfgRequireTrueAssociation && (mcpos1.emmceventId() != collision.emmceventId() || mcpos2.emmceventId() != collision.emmceventId())) { - continue; - } - - if (isPairOK(pos1, pos2, cut, tracks)) { - passed_pairIds.emplace_back(std::make_pair(pos1.globalIndex(), pos2.globalIndex())); - } - } - for (const auto& [neg1, neg2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(negTracks_per_coll, negTracks_per_coll))) { // LS-- - auto mcneg1 = mcparticles.iteratorAt(neg1.emmcparticleId()); - auto mccollision_from_neg1 = mcneg1.template emmcevent_as(); - if (cfgEventGeneratorType >= 0 && mccollision_from_neg1.getSubGeneratorId() != cfgEventGeneratorType) { - continue; - } - auto mcneg2 = mcparticles.iteratorAt(neg2.emmcparticleId()); - auto mccollision_from_neg2 = mcneg2.template emmcevent_as(); - if (cfgEventGeneratorType >= 0 && mccollision_from_neg2.getSubGeneratorId() != cfgEventGeneratorType) { - continue; - } - if (cfgRequireTrueAssociation && (mcneg1.emmceventId() != collision.emmceventId() || mcneg2.emmceventId() != collision.emmceventId())) { - continue; - } - if (isPairOK(neg1, neg2, cut, tracks)) { - passed_pairIds.emplace_back(std::make_pair(neg1.globalIndex(), neg2.globalIndex())); - } - } - } // end of collision loop - - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - for (const auto& pairId : passed_pairIds) { - auto t1 = tracks.rawIteratorAt(std::get<0>(pairId)); - auto t2 = tracks.rawIteratorAt(std::get<1>(pairId)); - // LOGF(info, "std::get<0>(pairId) = %d, std::get<1>(pairId) = %d, t1.globalIndex() = %d, t2.globalIndex() = %d", std::get<0>(pairId), std::get<1>(pairId), t1.globalIndex(), t2.globalIndex()); - - float n = 1.f; // include myself. - for (const auto& ambId1 : t1.ambiguousElectronsIds()) { - for (const auto& ambId2 : t2.ambiguousElectronsIds()) { - if (std::find(passed_pairIds.begin(), passed_pairIds.end(), std::make_pair(ambId1, ambId2)) != passed_pairIds.end()) { - n += 1.f; - } - } - } - map_weight[pairId] = 1.f / n; - } // end of passed_pairIds loop - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - for (const auto& pairId : passed_pairIds) { - auto t1 = tracks.rawIteratorAt(std::get<0>(pairId)); - auto t2 = tracks.rawIteratorAt(std::get<1>(pairId)); - - float n = 1.f; // include myself. - for (const auto& ambId1 : t1.ambiguousMuonsIds()) { - for (const auto& ambId2 : t2.ambiguousMuonsIds()) { - if (std::find(passed_pairIds.begin(), passed_pairIds.end(), std::make_pair(ambId1, ambId2)) != passed_pairIds.end()) { - n += 1.f; - } - } - } - map_weight[pairId] = 1.f / n; - } // end of passed_pairIds loop - } - passed_pairIds.clear(); - passed_pairIds.shrink_to_fit(); - } - - template - bool isPairInAcc(TTrack const& t1, TTrack const& t2) - { - ROOT::Math::PtEtaPhiMVector v1(t1.pt(), t1.eta(), t1.phi(), leptonM1); - ROOT::Math::PtEtaPhiMVector v2(t2.pt(), t2.eta(), t2.phi(), leptonM2); - ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if ((t1.pt() < dielectroncuts.cfg_min_pt_track || dielectroncuts.cfg_max_pt_track < t1.pt()) || (t2.pt() < dielectroncuts.cfg_min_pt_track || dielectroncuts.cfg_max_pt_track < t2.pt())) { - return false; - } - if ((t1.eta() < dielectroncuts.cfg_min_eta_track || dielectroncuts.cfg_max_eta_track < t1.eta()) || (t2.eta() < dielectroncuts.cfg_min_eta_track || dielectroncuts.cfg_max_eta_track < t2.eta())) { - return false; - } - if (v12.Rapidity() < dielectroncuts.cfg_min_pair_y || dielectroncuts.cfg_max_pair_y < v12.Rapidity()) { - return false; - } - return true; - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - if ((t1.pt() < dimuoncuts.cfg_min_pt_track || dimuoncuts.cfg_max_pt_track < t1.pt()) || (t2.pt() < dimuoncuts.cfg_min_pt_track || dimuoncuts.cfg_max_pt_track < t2.pt())) { - return false; - } - if ((t1.eta() < dimuoncuts.cfg_min_eta_track || dimuoncuts.cfg_max_eta_track < t1.eta()) || (t2.eta() < dimuoncuts.cfg_min_eta_track || dimuoncuts.cfg_max_eta_track < t2.eta())) { - return false; - } - if (v12.Rapidity() < dimuoncuts.cfg_min_pair_y || dimuoncuts.cfg_max_pair_y < v12.Rapidity()) { - return false; - } - return true; - } else { - return false; - } - return true; - } - - template - void fillHistogramsUnfolding(TTrack const& t1, TTrack const& t2, TMCParticles const& mcparticles) - { - auto t1mc = mcparticles.iteratorAt(t1.emmcparticleId()); - auto t2mc = mcparticles.iteratorAt(t2.emmcparticleId()); - - ROOT::Math::PtEtaPhiMVector v1rec(t1.pt(), t1.eta(), t1.phi(), leptonM1); - ROOT::Math::PtEtaPhiMVector v2rec(t2.pt(), t2.eta(), t2.phi(), leptonM2); - ROOT::Math::PtEtaPhiMVector v12rec = v1rec + v2rec; - - ROOT::Math::PtEtaPhiMVector v1mc(t1mc.pt(), t1mc.eta(), t1mc.phi(), leptonM1); - ROOT::Math::PtEtaPhiMVector v2mc(t2mc.pt(), t2mc.eta(), t2mc.phi(), leptonM2); - ROOT::Math::PtEtaPhiMVector v12mc = v1mc + v2mc; - - float weight = 1.f; - if (cfgApplyWeightTTCA) { - weight = map_weight[std::make_pair(t1.globalIndex(), t2.globalIndex())]; - } - - if (isPairInAcc(t1, t2) && isPairInAcc(t1mc, t2mc)) { // both rec and mc info are in acceptance. - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Unfold/") + HIST(unfolding_dilepton_source_types[sourceId]) + HIST("uls/hsRM"), v12mc.M(), v12mc.Pt(), v12rec.M(), v12rec.Pt(), weight); - } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ - fRegistry.fill(HIST("Unfold/") + HIST(unfolding_dilepton_source_types[sourceId]) + HIST("lspp/hsRM"), v12mc.M(), v12mc.Pt(), v12rec.M(), v12rec.Pt(), weight); - } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- - fRegistry.fill(HIST("Unfold/") + HIST(unfolding_dilepton_source_types[sourceId]) + HIST("lsmm/hsRM"), v12mc.M(), v12mc.Pt(), v12rec.M(), v12rec.Pt(), weight); - } - } else if (!isPairInAcc(t1, t2) && isPairInAcc(t1mc, t2mc)) { - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Unfold/") + HIST(unfolding_dilepton_source_types[sourceId]) + HIST("uls/hMiss"), v12mc.M(), v12mc.Pt(), weight); - } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ - fRegistry.fill(HIST("Unfold/") + HIST(unfolding_dilepton_source_types[sourceId]) + HIST("lspp/hMiss"), v12mc.M(), v12mc.Pt(), weight); - } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- - fRegistry.fill(HIST("Unfold/") + HIST(unfolding_dilepton_source_types[sourceId]) + HIST("lsmm/hMiss"), v12mc.M(), v12mc.Pt(), weight); - } - } else if (isPairInAcc(t1, t2) && !isPairInAcc(t1mc, t2mc)) { - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Unfold/") + HIST(unfolding_dilepton_source_types[sourceId]) + HIST("uls/hFake"), v12rec.M(), v12rec.Pt(), weight); - } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ - fRegistry.fill(HIST("Unfold/") + HIST(unfolding_dilepton_source_types[sourceId]) + HIST("lspp/hFake"), v12rec.M(), v12rec.Pt(), weight); - } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- - fRegistry.fill(HIST("Unfold/") + HIST(unfolding_dilepton_source_types[sourceId]) + HIST("lsmm/hFake"), v12rec.M(), v12rec.Pt(), weight); - } - } - } - - template - bool fillPairUnfolding(TTrack const& t1, TTrack const& t2, TTracks const& tracks, TCut const& cut, TMCCollisions const&, TMCParticles const& mcparticles) - { - auto t1mc = mcparticles.iteratorAt(t1.emmcparticleId()); - auto mccollision_from_t1 = t1mc.template emmcevent_as(); - if (cfgEventGeneratorType >= 0 && mccollision_from_t1.getSubGeneratorId() != cfgEventGeneratorType) { - return false; - } - - auto t2mc = mcparticles.iteratorAt(t2.emmcparticleId()); - auto mccollision_from_t2 = t2mc.template emmcevent_as(); - if (cfgEventGeneratorType >= 0 && mccollision_from_t2.getSubGeneratorId() != cfgEventGeneratorType) { - return false; - } - - if ((std::abs(t1mc.pdgCode()) != pdg_lepton || std::abs(t2mc.pdgCode()) != pdg_lepton) || (t1mc.emmceventId() != t2mc.emmceventId())) { - return false; - } - if (t1mc.pdgCode() * t2mc.pdgCode() > 0) { // ULS - return false; - } - if (!((t1mc.isPhysicalPrimary() || t1mc.producedByGenerator()) && (t2mc.isPhysicalPrimary() || t2mc.producedByGenerator()))) { - return false; - } - int mother_id = std::max({FindSMULS(t1mc, t2mc, mcparticles), FindSMULS(t2mc, t1mc, mcparticles), FindSMLSPP(t1mc, t2mc, mcparticles), FindSMLSMM(t1mc, t2mc, mcparticles)}); - int hfee_type = o2::aod::pwgem::dilepton::utils::mcutil::IsHF(t1mc, t2mc, mcparticles); - if (mother_id < 0 && hfee_type < 0) { - return false; - } - - if (!isPairOK(t1, t2, cut, tracks)) { // without acceptance - return false; - } - - // ROOT::Math::PtEtaPhiMVector v1rec(t1.pt(), t1.eta(), t1.phi(), leptonM1); - // ROOT::Math::PtEtaPhiMVector v2rec(t2.pt(), t2.eta(), t2.phi(), leptonM2); - // ROOT::Math::PtEtaPhiMVector v12rec = v1rec + v2rec; - - // ROOT::Math::PtEtaPhiMVector v1mc(t1mc.pt(), t1mc.eta(), t1mc.phi(), leptonM1); - // ROOT::Math::PtEtaPhiMVector v2mc(t2mc.pt(), t2mc.eta(), t2mc.phi(), leptonM2); - // ROOT::Math::PtEtaPhiMVector v12mc = v1mc + v2mc; - - if (mother_id > -1) { - auto mcmother = mcparticles.iteratorAt(mother_id); - if (!(mcmother.isPhysicalPrimary() || mcmother.producedByGenerator())) { - return false; - } - switch (std::abs(mcmother.pdgCode())) { - case 111: - case 221: - case 331: - case 113: - case 223: - case 333: - case 443: - case 100443: - fillHistogramsUnfolding<0>(t1, t2, mcparticles); - break; - default: - break; - } - } else if (hfee_type > -1) { - switch (hfee_type) { - case static_cast(o2::aod::pwgem::dilepton::utils::mcutil::EM_HFeeType::kCe_Ce): - fillHistogramsUnfolding<1>(t1, t2, mcparticles); - break; - case static_cast(o2::aod::pwgem::dilepton::utils::mcutil::EM_HFeeType::kBe_Be): - fillHistogramsUnfolding<2>(t1, t2, mcparticles); - break; - case static_cast(o2::aod::pwgem::dilepton::utils::mcutil::EM_HFeeType::kBCe_BCe): - fillHistogramsUnfolding<2>(t1, t2, mcparticles); - break; - case static_cast(o2::aod::pwgem::dilepton::utils::mcutil::EM_HFeeType::kBCe_Be_SameB): - fillHistogramsUnfolding<2>(t1, t2, mcparticles); - break; - case static_cast(o2::aod::pwgem::dilepton::utils::mcutil::EM_HFeeType::kBCe_Be_DiffB): - fillHistogramsUnfolding<2>(t1, t2, mcparticles); - break; - default: - break; - } - } - return true; - } - - template - void fillUnfolding(TCollisions const& collisions, TTracks1 const& posTracks, TTracks2 const& negTracks, TPresilce const& perCollision, TCut const& cut, TAllTracks const& tracks, TMCCollisions const& mcCollisions, TMCParticles const& mcparticles) - { - for (const auto& collision : collisions) { - initCCDB(collision); - float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; - if (centralities[cfgCentEstimator] < eventcuts.cfgCentMin || eventcuts.cfgCentMax < centralities[cfgCentEstimator]) { - continue; - } - - if (!fEMEventCut.IsSelected(collision)) { - continue; - } - if (eventcuts.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { - continue; - } - - auto posTracks_per_coll = posTracks.sliceByCached(perCollision, collision.globalIndex(), cache); // reconstructed pos tracks - auto negTracks_per_coll = negTracks.sliceByCached(perCollision, collision.globalIndex(), cache); // reconstructed neg tracks - - for (const auto& [pos, neg] : combinations(o2::soa::CombinationsFullIndexPolicy(posTracks_per_coll, negTracks_per_coll))) { // ULS - fillPairUnfolding(pos, neg, tracks, cut, mcCollisions, mcparticles); - } // end of ULS pairing - for (const auto& [pos1, pos2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(posTracks_per_coll, posTracks_per_coll))) { // LS++ - fillPairUnfolding(pos1, pos2, tracks, cut, mcCollisions, mcparticles); - } // end of LS++ pairing - for (const auto& [neg1, neg2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(negTracks_per_coll, negTracks_per_coll))) { // LS-- - fillPairUnfolding(neg1, neg2, tracks, cut, mcCollisions, mcparticles); - } // end of LS-- pairing - } // end of collision loop - } - - std::unordered_map map_best_match_globalmuon; - - o2::framework::SliceCache cache; - o2::framework::Preslice perCollision_electron = o2::aod::emprimaryelectron::emeventId; - o2::framework::expressions::Filter trackFilter_electron = nabs(o2::aod::track::dcaXY) < dielectroncuts.cfg_max_dcaxy && nabs(o2::aod::track::dcaZ) < dielectroncuts.cfg_max_dcaz && o2::aod::track::itsChi2NCl < dielectroncuts.cfg_max_chi2its && o2::aod::track::tpcChi2NCl < dielectroncuts.cfg_max_chi2tpc; - o2::framework::expressions::Filter pidFilter_electron = dielectroncuts.cfg_min_TPCNsigmaEl < o2::aod::pidtpc::tpcNSigmaEl && o2::aod::pidtpc::tpcNSigmaEl < dielectroncuts.cfg_max_TPCNsigmaEl; - o2::framework::expressions::Filter ttcaFilter_electron = ifnode(dielectroncuts.enableTTCA.node(), true, o2::aod::emprimaryelectron::isAssociatedToMPC == true); - o2::framework::expressions::Filter prefilter_derived_electron = ifnode(dielectroncuts.cfg_apply_cuts_from_prefilter_derived.node() && dielectroncuts.cfg_prefilter_bits_derived.node() >= static_cast(1), - ifnode((dielectroncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kMee))) > static_cast(0), (o2::aod::emprimaryelectron::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kMee))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kPhiV))) > static_cast(0), (o2::aod::emprimaryelectron::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kPhiV))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackLS))) > static_cast(0), (o2::aod::emprimaryelectron::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackLS))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackULS))) > static_cast(0), (o2::aod::emprimaryelectron::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackULS))) <= static_cast(0), true), - o2::aod::emprimaryelectron::pfbderived >= static_cast(0)); - - o2::framework::expressions::Filter prefilter_electron = ifnode(dielectroncuts.cfg_apply_cuts_from_prefilter.node() && dielectroncuts.cfg_prefilter_bits.node() >= static_cast(1), - ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPC))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPC))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_20MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_20MeV))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_40MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_40MeV))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_60MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_60MeV))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_80MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_80MeV))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_100MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_100MeV))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_120MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_120MeV))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_140MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_140MeV))) <= static_cast(0), true), - o2::aod::emprimaryelectron::pfb >= static_cast(0)); - - o2::framework::Preslice perCollision_muon = o2::aod::emprimarymuon::emeventId; - o2::framework::expressions::Filter trackFilter_muon = o2::aod::fwdtrack::trackType == dimuoncuts.cfg_track_type; - o2::framework::expressions::Filter ttcaFilter_muon = ifnode(dimuoncuts.enableTTCA.node(), true, o2::aod::emprimarymuon::isAssociatedToMPC == true); - - o2::framework::expressions::Filter collisionFilter_centrality = (eventcuts.cfgCentMin < o2::aod::cent::centFT0M && o2::aod::cent::centFT0M < eventcuts.cfgCentMax) || (eventcuts.cfgCentMin < o2::aod::cent::centFT0A && o2::aod::cent::centFT0A < eventcuts.cfgCentMax) || (eventcuts.cfgCentMin < o2::aod::cent::centFT0C && o2::aod::cent::centFT0C < eventcuts.cfgCentMax); - o2::framework::expressions::Filter collisionFilter_numContrib = eventcuts.cfgNumContribMin <= o2::aod::collision::numContrib && o2::aod::collision::numContrib < eventcuts.cfgNumContribMax; - o2::framework::expressions::Filter collisionFilter_occupancy_track = eventcuts.cfgTrackOccupancyMin <= o2::aod::evsel::trackOccupancyInTimeRange && o2::aod::evsel::trackOccupancyInTimeRange < eventcuts.cfgTrackOccupancyMax; - o2::framework::expressions::Filter collisionFilter_occupancy_ft0c = eventcuts.cfgFT0COccupancyMin <= o2::aod::evsel::ft0cOccupancyInTimeRange && o2::aod::evsel::ft0cOccupancyInTimeRange < eventcuts.cfgFT0COccupancyMax; - using FilteredMyCollisions = o2::soa::Filtered; - - o2::framework::Partition positive_electrons = o2::aod::emprimaryelectron::sign > int8_t(0); // reconstructed tracks - o2::framework::Partition negative_electrons = o2::aod::emprimaryelectron::sign < int8_t(0); // reconstructed tracks - o2::framework::Partition positive_muons = o2::aod::emprimarymuon::sign > int8_t(0); // reconstructed tracks - o2::framework::Partition negative_muons = o2::aod::emprimarymuon::sign < int8_t(0); // reconstructed tracks - - o2::framework::Partition positive_electronsMC = o2::aod::mcparticle::pdgCode == -11; // e+ - o2::framework::Partition negative_electronsMC = o2::aod::mcparticle::pdgCode == 11; // e- - o2::framework::Partition positive_muonsMC = o2::aod::mcparticle::pdgCode == -13; // mu+ - o2::framework::Partition negative_muonsMC = o2::aod::mcparticle::pdgCode == 13; // mu- - o2::framework::PresliceUnsorted perMcCollision = o2::aod::emmcparticle::emmceventId; - o2::framework::PresliceUnsorted perMcCollision_vm = o2::aod::emmcgenvectormeson::emmceventId; - // o2::framework::PresliceUnsorted recColperMcCollision = o2::aod::emmceventlabel::emmceventId; - - void processAnalysis(FilteredMyCollisions const& collisions, MyMCCollisions const& mccollisions, o2::aod::EMMCParticles const& mcparticles, TLeptons const& leptons) - { - // LOGF(info, "collisions.size() = %d, mccollisions.size() = %d, mcparticles.size() = %d", collisions.size(), mccollisions.size(), mcparticles.size()); - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (cfgApplyWeightTTCA) { - fillPairWeightMap(collisions, positive_electrons, negative_electrons, o2::aod::emprimaryelectron::emeventId, fDielectronCut, leptons, mccollisions, mcparticles); - } - runTruePairing(collisions, positive_electrons, negative_electrons, o2::aod::emprimaryelectron::emeventId, fDielectronCut, leptons, mccollisions, mcparticles); - runGenInfo(collisions, mccollisions, positive_electronsMC, negative_electronsMC, mcparticles); - if (cfgFillUnfolding) { - fillUnfolding(collisions, positive_electrons, negative_electrons, o2::aod::emprimaryelectron::emeventId, fDielectronCut, leptons, mccollisions, mcparticles); - } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - map_best_match_globalmuon = o2::aod::pwgem::dilepton::utils::emtrackutil::findBestMatchMap(leptons, fDimuonCut); - if (cfgApplyWeightTTCA) { - fillPairWeightMap(collisions, positive_muons, negative_muons, o2::aod::emprimarymuon::emeventId, fDimuonCut, leptons, mccollisions, mcparticles); - } - runTruePairing(collisions, positive_muons, negative_muons, o2::aod::emprimarymuon::emeventId, fDimuonCut, leptons, mccollisions, mcparticles); - runGenInfo(collisions, mccollisions, positive_muonsMC, negative_muonsMC, mcparticles); - if (cfgFillUnfolding) { - fillUnfolding(collisions, positive_muons, negative_muons, o2::aod::emprimarymuon::emeventId, fDimuonCut, leptons, mccollisions, mcparticles); - } - } - map_weight.clear(); - map_best_match_globalmuon.clear(); - } - PROCESS_SWITCH(DileptonSVMC, processAnalysis, "run dilepton mc analysis", true); - - o2::framework::Partition positive_electronsMC_smeared = o2::aod::mcparticle::pdgCode == -11; // e+ - o2::framework::Partition negative_electronsMC_smeared = o2::aod::mcparticle::pdgCode == 11; // e- - o2::framework::Partition positive_muonsMC_smeared = o2::aod::mcparticle::pdgCode == -13; // mu+ - o2::framework::Partition negative_muonsMC_smeared = o2::aod::mcparticle::pdgCode == 13; // mu- - - void processAnalysis_Smeared(FilteredMyCollisions const& collisions, MyMCCollisions const& mccollisions, TLeptons const& leptons, TSmeardMCParitlces const& mcparticles_smeared) - { - // LOGF(info, "collisions.size() = %d, mccollisions.size() = %d, mcparticles.size() = %d", collisions.size(), mccollisions.size(), mcparticles.size()); - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (cfgApplyWeightTTCA) { - fillPairWeightMap(collisions, positive_electrons, negative_electrons, o2::aod::emprimaryelectron::emeventId, fDielectronCut, leptons, mccollisions, mcparticles_smeared); - } - runTruePairing(collisions, positive_electrons, negative_electrons, o2::aod::emprimaryelectron::emeventId, fDielectronCut, leptons, mccollisions, mcparticles_smeared); - runGenInfo(collisions, mccollisions, positive_electronsMC_smeared, negative_electronsMC_smeared, mcparticles_smeared); - if (cfgFillUnfolding) { - fillUnfolding(collisions, positive_electrons, negative_electrons, o2::aod::emprimaryelectron::emeventId, fDielectronCut, leptons, mccollisions, mcparticles_smeared); - } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - map_best_match_globalmuon = o2::aod::pwgem::dilepton::utils::emtrackutil::findBestMatchMap(leptons, fDimuonCut); - if (cfgApplyWeightTTCA) { - fillPairWeightMap(collisions, positive_muons, negative_muons, o2::aod::emprimarymuon::emeventId, fDimuonCut, leptons, mccollisions, mcparticles_smeared); - } - runTruePairing(collisions, positive_muons, negative_muons, o2::aod::emprimarymuon::emeventId, fDimuonCut, leptons, mccollisions, mcparticles_smeared); - runGenInfo(collisions, mccollisions, positive_muonsMC_smeared, negative_muonsMC_smeared, mcparticles_smeared); - if (cfgFillUnfolding) { - fillUnfolding(collisions, positive_muons, negative_muons, o2::aod::emprimarymuon::emeventId, fDimuonCut, leptons, mccollisions, mcparticles_smeared); - } - } - map_weight.clear(); - map_best_match_globalmuon.clear(); - } - PROCESS_SWITCH(DileptonSVMC, processAnalysis_Smeared, "run dilepton mc analysis with smearing", false); - - void processGen_VM(FilteredMyCollisions const& collisions, MyMCCollisions const&, o2::aod::EMMCGenVectorMesons const& mcparticles) - { - // for oemga, phi efficiency - for (const auto& collision : collisions) { - float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; - if (centralities[cfgCentEstimator] < eventcuts.cfgCentMin || eventcuts.cfgCentMax < centralities[cfgCentEstimator]) { - continue; - } - - if (!fEMEventCut.IsSelected(collision)) { - continue; - } - if (eventcuts.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { - continue; - } - auto mccollision = collision.template emmcevent_as(); - if (cfgEventGeneratorType >= 0 && mccollision.getSubGeneratorId() != cfgEventGeneratorType) { - continue; - } - auto mctracks_per_coll = mcparticles.sliceBy(perMcCollision_vm, mccollision.globalIndex()); - - for (const auto& mctrack : mctracks_per_coll) { - if (!(mctrack.isPhysicalPrimary() || mctrack.producedByGenerator())) { - continue; - } - - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (mctrack.y() < dielectroncuts.cfg_min_pair_y || dielectroncuts.cfg_max_pair_y < mctrack.y()) { - continue; - } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - if (mctrack.y() < dimuoncuts.cfg_min_pair_y || dimuoncuts.cfg_max_pair_y < mctrack.y()) { - continue; - } - } - - switch (std::abs(mctrack.pdgCode())) { - case 223: - fRegistry.fill(HIST("Generated/VM/Omega/hPtY"), mctrack.y(), mctrack.pt(), 1.f / mctrack.dsf()); - break; - case 333: - fRegistry.fill(HIST("Generated/VM/Phi/hPtY"), mctrack.y(), mctrack.pt(), 1.f / mctrack.dsf()); - break; - default: - break; - } - - } // end of mctracks per mccollision - } // end of collision loop - } - PROCESS_SWITCH(DileptonSVMC, processGen_VM, "process generated info for vector mesons", false); - - void processNorm(o2::aod::EMEventNormInfos const& collisions) - { - for (const auto& collision : collisions) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 1.0); - if (collision.selection_bit(o2::aod::emevsel::kIsTriggerTVX)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 2.0); - } - if (collision.selection_bit(o2::aod::emevsel::kNoTimeFrameBorder)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 3.0); - } - if (collision.selection_bit(o2::aod::emevsel::kNoITSROFrameBorder)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 4.0); - } - if (collision.selection_bit(o2::aod::emevsel::kNoSameBunchPileup)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 5.0); - } - if (collision.selection_bit(o2::aod::emevsel::kIsGoodZvtxFT0vsPV)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 6.0); - } - if (collision.selection_bit(o2::aod::emevsel::kIsVertexITSTPC)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 7.0); - } - if (collision.selection_bit(o2::aod::emevsel::kIsVertexTRDmatched)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 8.0); - } - if (collision.selection_bit(o2::aod::emevsel::kIsVertexTOFmatched)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 9.0); - } - if (collision.sel8()) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 10.0); - } - if (std::fabs(collision.posZ()) < 10.0) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 11.0); - } - if (collision.selection_bit(o2::aod::emevsel::kNoCollInTimeRangeStandard)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 12.0); - } - if (collision.selection_bit(o2::aod::emevsel::kNoCollInTimeRangeStrict)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 13.0); - } - if (collision.selection_bit(o2::aod::emevsel::kNoCollInRofStandard)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 14.0); - } - if (collision.selection_bit(o2::aod::emevsel::kNoCollInRofStrict)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 15.0); - } - if (collision.selection_bit(o2::aod::emevsel::kNoHighMultCollInPrevRof)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 16.0); - } - if (collision.selection_bit(o2::aod::emevsel::kIsGoodITSLayer3)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 17.0); - } - if (collision.selection_bit(o2::aod::emevsel::kIsGoodITSLayer0123)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 18.0); - } - if (collision.selection_bit(o2::aod::emevsel::kIsGoodITSLayersAll)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 19.0); - } - if (!fEMEventCut.IsSelected(collision)) { - continue; - } - if (eventcuts.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { - continue; - } - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), o2::aod::pwgem::dilepton::utils::eventhistogram::nbin_ev); // accepted - } // end of collision loop - } - PROCESS_SWITCH(DileptonSVMC, processNorm, "process normalization info", false); - - void processBC(o2::aod::EMBCs const& bcs) - { - for (const auto& bc : bcs) { - if (bc.selection_bit(o2::aod::emevsel::kIsTriggerTVX)) { - fRegistry.fill(HIST("BC/hTVXCounter"), 0.f); - - if (bc.selection_bit(o2::aod::emevsel::kNoTimeFrameBorder)) { - fRegistry.fill(HIST("BC/hTVXCounter"), 1.f); - } - if (bc.selection_bit(o2::aod::emevsel::kNoITSROFrameBorder)) { - fRegistry.fill(HIST("BC/hTVXCounter"), 2.f); - } - if (rctChecker(bc)) { - fRegistry.fill(HIST("BC/hTVXCounter"), 3.f); - } - if (bc.selection_bit(o2::aod::emevsel::kNoTimeFrameBorder) && bc.selection_bit(o2::aod::emevsel::kNoITSROFrameBorder)) { - fRegistry.fill(HIST("BC/hTVXCounter"), 4.f); - } - if (bc.selection_bit(o2::aod::emevsel::kNoTimeFrameBorder) && bc.selection_bit(o2::aod::emevsel::kNoITSROFrameBorder) && rctChecker(bc)) { - fRegistry.fill(HIST("BC/hTVXCounter"), 5.f); - } - } - } - } - PROCESS_SWITCH(DileptonSVMC, processBC, "process BC counter", false); - - void processDummy(FilteredMyCollisions const&) {} - PROCESS_SWITCH(DileptonSVMC, processDummy, "Dummy function", false); -}; - -#endif // PWGEM_DILEPTON_CORE_DILEPTONSVMC_H_ diff --git a/PWGEM/Dilepton/Core/PWGEMDileptonCoreLinkDef.h b/PWGEM/Dilepton/Core/PWGEMDileptonCoreLinkDef.h index fe78534478e..a0ba6d2b630 100644 --- a/PWGEM/Dilepton/Core/PWGEMDileptonCoreLinkDef.h +++ b/PWGEM/Dilepton/Core/PWGEMDileptonCoreLinkDef.h @@ -9,8 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef PWGEM_DILEPTON_CORE_PWGEMDILEPTONCORELINKDEF_H_ -#define PWGEM_DILEPTON_CORE_PWGEMDILEPTONCORELINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -20,5 +19,3 @@ #pragma link C++ class DielectronCut + ; #pragma link C++ class DimuonCut + ; #pragma link C++ class EMTrackCut + ; - -#endif // PWGEM_DILEPTON_CORE_PWGEMDILEPTONCORELINKDEF_H_ diff --git a/PWGEM/Dilepton/DataModel/EvSelFlags.h b/PWGEM/Dilepton/DataModel/EvSelFlags.h index 0b05ff288eb..ae4d65a548b 100644 --- a/PWGEM/Dilepton/DataModel/EvSelFlags.h +++ b/PWGEM/Dilepton/DataModel/EvSelFlags.h @@ -9,8 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef PWGEM_DILEPTON_CORE_EVSELFLAGS_H -#define PWGEM_DILEPTON_CORE_EVSELFLAGS_H +#ifndef PWGEM_DILEPTON_DATAMODEL_EVSELFLAGS_H_ +#define PWGEM_DILEPTON_DATAMODEL_EVSELFLAGS_H_ namespace o2::aod::emevsel { @@ -37,4 +37,4 @@ enum EventSelectionFlags { }; } // namespace o2::aod::emevsel -#endif // PWGEM_DILEPTON_CORE_EVSELFLAGS_H +#endif // PWGEM_DILEPTON_DATAMODEL_EVSELFLAGS_H_ diff --git a/PWGEM/Dilepton/DataModel/dileptonTables.h b/PWGEM/Dilepton/DataModel/dileptonTables.h index 4178d0c7b89..991818d04e1 100644 --- a/PWGEM/Dilepton/DataModel/dileptonTables.h +++ b/PWGEM/Dilepton/DataModel/dileptonTables.h @@ -1234,7 +1234,7 @@ DECLARE_SOA_COLUMN(Signed1Pt, signed1Pt, float); //! (sign of charge)/Pt in c/ DECLARE_SOA_COLUMN(Eta, eta, float); //! DECLARE_SOA_COLUMN(Phi, phi, float); //! DECLARE_SOA_DYNAMIC_COLUMN(Pt, pt, [](float signed1Pt) -> float { return 1.f / std::fabs(signed1Pt); }); -DECLARE_SOA_DYNAMIC_COLUMN(Sign, sign, [](float signed1Pt) -> short { return (signed1Pt > 0) ? 1 : -1; }); //! Charge: positive: 1, negative: -1 +DECLARE_SOA_DYNAMIC_COLUMN(Sign, sign, [](float signed1Pt) -> int16_t { return (signed1Pt > 0) ? 1 : -1; }); //! Charge: positive: 1, negative: -1 } // namespace emprimarytrack DECLARE_SOA_TABLE_VERSIONED(EMPrimaryTracks_000, "AOD", "EMPRIMARYTRACK", 0, //! primary charged track table for 2PC @@ -1281,13 +1281,13 @@ DECLARE_SOA_INDEX_COLUMN(EMThinEvent, emthinevent); //! DECLARE_SOA_COLUMN(Pt1, pt1, float); //! DECLARE_SOA_COLUMN(Eta1, eta1, float); //! DECLARE_SOA_COLUMN(Phi1, phi1, float); //! -DECLARE_SOA_COLUMN(Sign1, sign1, short); //! +DECLARE_SOA_COLUMN(Sign1, sign1, int16_t); //! DECLARE_SOA_COLUMN(DCA1, dca1, float); //! DCA in sigma. Users should decide 3D or XY or Z DECLARE_SOA_COLUMN(Pt2, pt2, float); //! DECLARE_SOA_COLUMN(Eta2, eta2, float); //! DECLARE_SOA_COLUMN(Phi2, phi2, float); //! DECLARE_SOA_COLUMN(DCA2, dca2, float); //! DCA in sigma. Users should decide 3D or XY or Z -DECLARE_SOA_COLUMN(Sign2, sign2, short); //! +DECLARE_SOA_COLUMN(Sign2, sign2, int16_t); //! DECLARE_SOA_COLUMN(Weight, weight, float); //! possible pair weight } // namespace emdilepton diff --git a/PWGEM/Dilepton/DataModel/lmeeMLTables.h b/PWGEM/Dilepton/DataModel/lmeeMLTables.h index 3fc0d0aa90b..f7965db9a57 100644 --- a/PWGEM/Dilepton/DataModel/lmeeMLTables.h +++ b/PWGEM/Dilepton/DataModel/lmeeMLTables.h @@ -62,7 +62,7 @@ DECLARE_SOA_COLUMN(TPCNClsFound, tpcNClsFound, uint8_t); //! DECLARE_SOA_COLUMN(TPCNClsCrossedRows, tpcNClsCrossedRows, uint8_t); //! DECLARE_SOA_COLUMN(TPCNClsPID, tpcNClsPID, uint8_t); //! DECLARE_SOA_COLUMN(IsForValidation, isForValidation, bool); //! -DECLARE_SOA_COLUMN(Sign, sign, short); //! +DECLARE_SOA_COLUMN(Sign, sign, int16_t); //! DECLARE_SOA_COLUMN(P, p, float); //! DECLARE_SOA_COLUMN(PtGen, ptGen, float); //! DECLARE_SOA_COLUMN(EtaGen, etaGen, float); //! diff --git a/PWGEM/Dilepton/Tasks/CMakeLists.txt b/PWGEM/Dilepton/Tasks/CMakeLists.txt index 0bcd1085217..845a8fe4d98 100644 --- a/PWGEM/Dilepton/Tasks/CMakeLists.txt +++ b/PWGEM/Dilepton/Tasks/CMakeLists.txt @@ -95,16 +95,6 @@ o2physics_add_dpl_workflow(dielectron-mc PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::PWGEMDileptonCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(dielectron-sv - SOURCES dielectronSV.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::DCAFitter O2Physics::AnalysisCore O2Physics::PWGEMDileptonCore O2Physics::EventFilteringUtils - COMPONENT_NAME Analysis) - -o2physics_add_dpl_workflow(dielectron-sv-mc - SOURCES dielectronSVMC.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::DCAFitter O2Physics::AnalysisCore O2Physics::PWGEMDileptonCore - COMPONENT_NAME Analysis) - o2physics_add_dpl_workflow(dimuon SOURCES dimuon.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::PWGEMDileptonCore O2Physics::EventFilteringUtils @@ -115,16 +105,6 @@ o2physics_add_dpl_workflow(dimuon-mc PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::PWGEMDileptonCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(dimuon-sv - SOURCES dimuonSV.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::DCAFitter O2Physics::AnalysisCore O2Physics::PWGEMDileptonCore O2Physics::EventFilteringUtils - COMPONENT_NAME Analysis) - -o2physics_add_dpl_workflow(dimuon-sv-mc - SOURCES dimuonSVMC.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::DCAFitter O2Physics::AnalysisCore O2Physics::PWGEMDileptonCore - COMPONENT_NAME Analysis) - o2physics_add_dpl_workflow(dimuon-v1 SOURCES dimuonV1.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGEMDileptonCore @@ -189,3 +169,8 @@ o2physics_add_dpl_workflow(check-mc-pair-template SOURCES checkMCPairTemplate.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::PWGEMDileptonCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(dielectron-mixing-test + SOURCES dielectronMixingTest.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::PWGEMDileptonCore O2Physics::EventFilteringUtils + COMPONENT_NAME Analysis) diff --git a/PWGEM/Dilepton/Tasks/checkMCPairTemplate.cxx b/PWGEM/Dilepton/Tasks/checkMCPairTemplate.cxx index 0e2d7b2968d..62ef21abaab 100644 --- a/PWGEM/Dilepton/Tasks/checkMCPairTemplate.cxx +++ b/PWGEM/Dilepton/Tasks/checkMCPairTemplate.cxx @@ -376,6 +376,8 @@ struct checkMCPairTemplate { std::string pair_pt_axis_title = "p_{T,ll} (GeV/c)"; std::string pair_y_axis_title = "y_{ll}"; std::string pair_dca_axis_title = "DCA_{ll} (#sigma)"; + std::string mother1_pt_axis_title = "p_{T,Mother1} (GeV/c)"; + std::string mother2_pt_axis_title = "p_{T,Mother2} (GeV/c)"; if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { mass_axis_title = "m_{ee} (GeV/c^{2})"; pair_pt_axis_title = "p_{T,ee} (GeV/c)"; @@ -396,6 +398,8 @@ struct checkMCPairTemplate { // pair info const AxisSpec axis_mass{ConfMllBins, mass_axis_title}; const AxisSpec axis_pt{ConfPtllBins, pair_pt_axis_title}; + const AxisSpec axis_M1_pt{ConfPtllBins, mother1_pt_axis_title}; + const AxisSpec axis_M2_pt{ConfPtllBins, mother2_pt_axis_title}; const AxisSpec axis_y{ConfYllBins, pair_y_axis_title}; const AxisSpec axis_dca{ConfDCAllBins, pair_dca_axis_title}; const AxisSpec axis_pt_meson{ConfPtllBins, "p_{T}^{VM} (GeV/c)"}; // for omega, phi meson pT spectra @@ -461,11 +465,11 @@ struct checkMCPairTemplate { static constexpr std::string_view charmed_mesons[] = {"Dplus", "D0", "Dsplus"}; // 411, 421, 431 static constexpr std::string_view anti_charmed_mesons[] = {"Dminus", "D0bar", "Dsminus"}; const int nm_c = sizeof(charmed_mesons) / sizeof(charmed_mesons[0]); - static constexpr std::string_view charmed_baryons[] = {"Lcplus", "Xicplus", "Xic0", "Omegac0"}; // 4122, 4232, 4132, 4332 - static constexpr std::string_view anti_charmed_baryons[] = {"Lcminus", "Xicminus", "Xic0bar", "Omegac0bar"}; + static constexpr std::string_view charmed_baryons[] = {"Lcplus", "Xic0", "Xicplus", "Omegac0"}; // 4122, 4132, 4232, 4332 + static constexpr std::string_view anti_charmed_baryons[] = {"Lcminus", "Xic0bar", "Xicminus", "Omegac0bar"}; const int nb_c = sizeof(charmed_baryons) / sizeof(charmed_baryons[0]); static constexpr std::string_view sum_charmed_mesons[] = {"Dpm", "D0", "Dspm"}; - static constexpr std::string_view sum_charmed_baryons[] = {"Lcpm", "Xicpm", "Xic0", "Omegac0"}; + static constexpr std::string_view sum_charmed_baryons[] = {"Lcpm", "Xic0", "Xicpm", "Omegac0"}; if (cfgFillSeparateCharmHadronPairs) { for (int im = 0; im < nm_c; im++) { @@ -546,7 +550,7 @@ struct checkMCPairTemplate { fRegistry.addClone("Generated/VM/All/", "Generated/VM/Acc/"); // reconstructed pair info - fRegistry.add("Pair/sm/Photon/uls/hs", "rec. dilepton", kTHnSparseD, {axis_mass, axis_pt, axis_dca}, true); + fRegistry.add("Pair/sm/Photon/uls/hs", "rec. dilepton", kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_M1_pt, axis_M2_pt}, true); fRegistry.addClone("Pair/sm/Photon/uls/", "Pair/sm/Photon/lspp/"); fRegistry.addClone("Pair/sm/Photon/uls/", "Pair/sm/Photon/lsmm/"); @@ -586,17 +590,20 @@ struct checkMCPairTemplate { } } - fRegistry.add("Pair/ccbar/c2l_c2l/uls/hs", "rec. dilepton", kTHnSparseD, {axis_mass, axis_pt, axis_dca}, true); + fRegistry.add("Pair/ccbar/c2l_c2l/uls/hs", "rec. dilepton", kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_M1_pt, axis_M2_pt}, true); fRegistry.addClone("Pair/ccbar/c2l_c2l/uls/", "Pair/ccbar/c2l_c2l/lspp/"); fRegistry.addClone("Pair/ccbar/c2l_c2l/uls/", "Pair/ccbar/c2l_c2l/lsmm/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2l_b2l/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2c2l/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2l_sameb/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2l_diffb/"); // LS - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2c2l/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2cc2l/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2ll/"); + fRegistry.add("Pair/bbbar/b2l_b2l/uls/hs", "rec. dilepton", kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_M1_pt, axis_M2_pt}, true); + fRegistry.addClone("Pair/bbbar/b2l_b2l/uls/", "Pair/bbbar/b2l_b2l/lspp/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/uls/", "Pair/bbbar/b2l_b2l/lsmm/"); + // fRegistry.addClone("Pair/bbbar/c2l_c2l/", "Pair/bbbar/b2l_b2l/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2c2l_b2c2l/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2c2l_b2l_sameb/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2c2l_b2l_diffb/"); // LS + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2c2l/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2cc2l/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2ll/"); if (cfgFillSeparateCharmHadronPairs) { for (int im = 0; im < nm_c; im++) { @@ -625,57 +632,57 @@ struct checkMCPairTemplate { if (cfgFillSeparateBeautyHadronPairs) { // // (Hb->l) (Hb->l) combinations // for (int im = 0; im < nm_b; im++) { - // fRegistry.addClone("Pair/ccbar/c2l_c2l/", Form("Pair/bbbar/%s_%s/", beauty_mesons[im].data(), anti_beauty_mesons[im].data())); + // fRegistry.addClone("Pair/bbbar/b2l_b2l/", Form("Pair/bbbar/%s_%s/", beauty_mesons[im].data(), anti_beauty_mesons[im].data())); // } // for (int ib = 0; ib < nb_b; ib++) { - // fRegistry.addClone("Pair/ccbar/c2l_c2l/", Form("Pair/bbbar/%s_%s/", beauty_baryons[ib].data(), anti_beauty_baryons[ib].data())); + // fRegistry.addClone("Pair/bbbar/b2l_b2l/", Form("Pair/bbbar/%s_%s/", beauty_baryons[ib].data(), anti_beauty_baryons[ib].data())); // } // for (int im1 = 0; im1 < nm_b - 1; im1++) { // for (int im2 = im1 + 1; im2 < nm_b; im2++) { - // fRegistry.addClone("Pair/ccbar/c2l_c2l/", Form("Pair/bbbar/%s_%s/", sum_beauty_mesons[im1].data(), sum_beauty_mesons[im2].data())); + // fRegistry.addClone("Pair/bbbar/b2l_b2l/", Form("Pair/bbbar/%s_%s/", sum_beauty_mesons[im1].data(), sum_beauty_mesons[im2].data())); // } // } // for (int ib1 = 0; ib1 < nb_b - 1; ib1++) { // for (int ib2 = ib1 + 1; ib2 < nb_b; ib2++) { - // fRegistry.addClone("Pair/ccbar/c2l_c2l/", Form("Pair/bbbar/%s_%s/", sum_beauty_baryons[ib1].data(), sum_beauty_baryons[ib2].data())); + // fRegistry.addClone("Pair/bbbar/b2l_b2l/", Form("Pair/bbbar/%s_%s/", sum_beauty_baryons[ib1].data(), sum_beauty_baryons[ib2].data())); // } // } // for (int im = 0; im < nm_b; im++) { // for (int ib = 0; ib < nb_b; ib++) { - // fRegistry.addClone("Pair/ccbar/c2l_c2l/", Form("Pair/bbbar/%s_%s/", sum_beauty_mesons[im].data(), sum_beauty_baryons[ib].data())); + // fRegistry.addClone("Pair/bbbar/b2l_b2l/", Form("Pair/bbbar/%s_%s/", sum_beauty_mesons[im].data(), sum_beauty_baryons[ib].data())); // } // } // // (Hb->Hc->l) (Hb->Hc->l) combinations // for (int im = 0; im < nm_b; im++) { - // fRegistry.addClone("Pair/ccbar/c2l_c2l/", Form("Pair/bbbar/b2%s2l_b2%s2l/", sum_charmed_mesons[im].data(), sum_charmed_mesons[im].data())); + // fRegistry.addClone("Pair/bbbar/b2l_b2l/", Form("Pair/bbbar/b2%s2l_b2%s2l/", sum_charmed_mesons[im].data(), sum_charmed_mesons[im].data())); // } // for (int ib = 0; ib < nb_b; ib++) { - // fRegistry.addClone("Pair/ccbar/c2l_c2l/", Form("Pair/bbbar/b2%s2l_b2%s2l/", sum_charmed_baryons[ib].data(), sum_charmed_baryons[ib].data())); + // fRegistry.addClone("Pair/bbbar/b2l_b2l/", Form("Pair/bbbar/b2%s2l_b2%s2l/", sum_charmed_baryons[ib].data(), sum_charmed_baryons[ib].data())); // } - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau50/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau130/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau130/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau130_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau130/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau130/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2ll/Hc_ctau50/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2ll/Hc_ctau130/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2ll/Hc_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2ll/Hc_ctau50_ctau130/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2ll/Hc_ctau50_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2ll/Hc_ctau130_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau50/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau130/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau130/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau130_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau130/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau130/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2ll/Hc_ctau50/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2ll/Hc_ctau130/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2ll/Hc_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2ll/Hc_ctau50_ctau130/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2ll/Hc_ctau50_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2ll/Hc_ctau130_ctau300/"); } // for correlated bkg due to mis-identified hadrons, and true combinatorial bkg @@ -1139,11 +1146,11 @@ struct checkMCPairTemplate { } } else if ((std::abs(pdgMotherC1) == 4232 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 4232 && std::abs(pdgMotherC1) == 4132)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Xicpm_Xic0/uls/hs"), mass, pt, weight); + fRegistry.fill(HIST("Generated/ccbar/Xic0_Xicpm/uls/hs"), mass, pt, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Xicpm_Xic0/lspp/hs"), mass, pt, weight); + fRegistry.fill(HIST("Generated/ccbar/Xic0_Xicpm/lspp/hs"), mass, pt, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Xicpm_Xic0/lsmm/hs"), mass, pt, weight); + fRegistry.fill(HIST("Generated/ccbar/Xic0_Xicpm/lsmm/hs"), mass, pt, weight); } } else if ((std::abs(pdgMotherC1) == 4232 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 4232 && std::abs(pdgMotherC1) == 4332)) { if (sign1 * sign2 < 0) { // ULS @@ -1421,240 +1428,265 @@ struct checkMCPairTemplate { } template - void fillRecHistograms(const int sign1, const int sign2, const int pdgMotherC1, const int pdgMotherC2, const float mass, const float pt, const float pair_dca, const float weight) + void fillRecHistograms(const int sign1, const int sign2, const int pdgMotherC1, const int pdgMotherC2, const float mass, const float pt, const float pair_dca, const float weight, const float ptMotherC1, const float ptMotherC2) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/") + HIST(dilepton_source_types[sourceId]) + HIST("uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/") + HIST(dilepton_source_types[sourceId]) + HIST("uls/hs"), mass, pt, pair_dca, ptMotherC1, ptMotherC2, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/") + HIST(dilepton_source_types[sourceId]) + HIST("lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/") + HIST(dilepton_source_types[sourceId]) + HIST("lspp/hs"), mass, pt, pair_dca, ptMotherC1, ptMotherC2, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/") + HIST(dilepton_source_types[sourceId]) + HIST("lsmm/hs"), mass, pt, pair_dca, weight); - } + fRegistry.fill(HIST("Pair/") + HIST(dilepton_source_types[sourceId]) + HIST("lsmm/hs"), mass, pt, pair_dca, ptMotherC1, ptMotherC2, weight); + } + + // Keep the mother-pt axes in the same order as the hadron names in the histogram path. + const auto motherOrder = [](int pdg) { + switch (std::abs(pdg)) { + case 411: + return 0; // Dpm + case 421: + return 1; // D0 + case 431: + return 2; // Dspm + case 4122: + return 3; // Lcpm + case 4132: + return 4; // Xic0 + case 4232: + return 5; // Xicpm + case 4332: + return 6; // Omegac0 + default: + return 7; + } + }; + const bool motherOrderMatches = motherOrder(pdgMotherC1) <= motherOrder(pdgMotherC2); + const float ptMotherFirst = motherOrderMatches ? ptMotherC1 : ptMotherC2; + const float ptMotherSecond = motherOrderMatches ? ptMotherC2 : ptMotherC1; if (dilepton_source_types[sourceId].find("ccbar") != std::string_view::npos && cfgFillSeparateCharmHadronPairs) { if (std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 411) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dplus_Dminus/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dplus_Dminus/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dplus_Dminus/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dplus_Dminus/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dplus_Dminus/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dplus_Dminus/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 421) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/D0_D0bar/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_D0bar/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/D0_D0bar/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_D0bar/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/D0_D0bar/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_D0bar/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 431) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dsplus_Dsminus/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dsplus_Dsminus/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dsplus_Dsminus/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dsplus_Dsminus/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dsplus_Dsminus/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dsplus_Dsminus/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 421) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 421)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dpm_D0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_D0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dpm_D0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_D0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dpm_D0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_D0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 431) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 431)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dpm_Dspm/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Dspm/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dpm_Dspm/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Dspm/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dpm_Dspm/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Dspm/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 431) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 431)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/D0_Dspm/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Dspm/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/D0_Dspm/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Dspm/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/D0_Dspm/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Dspm/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (std::abs(pdgMotherC1) == 4122 && std::abs(pdgMotherC2) == 4122) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Lcplus_Lcminus/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcplus_Lcminus/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Lcplus_Lcminus/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcplus_Lcminus/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Lcplus_Lcminus/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcplus_Lcminus/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (std::abs(pdgMotherC1) == 4232 && std::abs(pdgMotherC2) == 4232) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Xicplus_Xicminus/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xicplus_Xicminus/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Xicplus_Xicminus/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xicplus_Xicminus/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Xicplus_Xicminus/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xicplus_Xicminus/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (std::abs(pdgMotherC1) == 4132 && std::abs(pdgMotherC2) == 4132) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Xic0_Xic0bar/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xic0_Xic0bar/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Xic0_Xic0bar/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xic0_Xic0bar/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Xic0_Xic0bar/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xic0_Xic0bar/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (std::abs(pdgMotherC1) == 4332 && std::abs(pdgMotherC2) == 4332) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Omegac0_Omegac0bar/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Omegac0_Omegac0bar/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Omegac0_Omegac0bar/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Omegac0_Omegac0bar/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Omegac0_Omegac0bar/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Omegac0_Omegac0bar/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 4122 && std::abs(pdgMotherC2) == 4232) || (std::abs(pdgMotherC2) == 4122 && std::abs(pdgMotherC1) == 4232)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xicpm/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xicpm/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xicpm/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xicpm/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xicpm/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xicpm/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 4122 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 4122 && std::abs(pdgMotherC1) == 4132)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xic0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xic0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xic0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xic0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xic0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xic0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 4122 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 4122 && std::abs(pdgMotherC1) == 4332)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Omegac0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcpm_Omegac0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Omegac0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcpm_Omegac0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Omegac0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcpm_Omegac0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 4232 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 4232 && std::abs(pdgMotherC1) == 4132)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Xicpm_Xic0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xic0_Xicpm/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Xicpm_Xic0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xic0_Xicpm/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Xicpm_Xic0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xic0_Xicpm/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 4232 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 4232 && std::abs(pdgMotherC1) == 4332)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Xicpm_Omegac0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xicpm_Omegac0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Xicpm_Omegac0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xicpm_Omegac0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Xicpm_Omegac0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xicpm_Omegac0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 4132 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 4132 && std::abs(pdgMotherC1) == 4332)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Xic0_Omegac0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xic0_Omegac0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Xic0_Omegac0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xic0_Omegac0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Xic0_Omegac0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xic0_Omegac0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 4122) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 4122)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dpm_Lcpm/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Lcpm/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dpm_Lcpm/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Lcpm/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dpm_Lcpm/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Lcpm/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 4232) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 4232)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dpm_Xicpm/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Xicpm/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dpm_Xicpm/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Xicpm/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dpm_Xicpm/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Xicpm/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 4132)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dpm_Xic0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Xic0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dpm_Xic0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Xic0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dpm_Xic0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Xic0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 4332)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dpm_Omegac0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Omegac0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dpm_Omegac0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Omegac0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dpm_Omegac0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Omegac0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 4122) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 4122)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/D0_Lcpm/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Lcpm/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/D0_Lcpm/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Lcpm/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/D0_Lcpm/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Lcpm/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 4232) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 4232)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/D0_Xicpm/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Xicpm/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/D0_Xicpm/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Xicpm/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/D0_Xicpm/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Xicpm/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 4132)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/D0_Xic0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Xic0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/D0_Xic0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Xic0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/D0_Xic0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Xic0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 4332)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/D0_Omegac0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Omegac0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/D0_Omegac0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Omegac0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/D0_Omegac0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Omegac0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 4122) || (std::abs(pdgMotherC2) == 431 && std::abs(pdgMotherC1) == 4122)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dspm_Lcpm/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Lcpm/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dspm_Lcpm/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Lcpm/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dspm_Lcpm/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Lcpm/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 4232) || (std::abs(pdgMotherC2) == 431 && std::abs(pdgMotherC1) == 4232)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dspm_Xicpm/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Xicpm/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dspm_Xicpm/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Xicpm/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dspm_Xicpm/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Xicpm/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 431 && std::abs(pdgMotherC1) == 4132)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dspm_Xic0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Xic0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dspm_Xic0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Xic0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dspm_Xic0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Xic0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 431 && std::abs(pdgMotherC1) == 4332)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dspm_Omegac0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Omegac0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dspm_Omegac0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Omegac0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dspm_Omegac0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Omegac0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } } @@ -1663,308 +1695,308 @@ struct checkMCPairTemplate { // (Hb->l) (Hb->l)combinations // if (std::abs(pdgMotherC1) == kBPlus && std::abs(pdgMotherC2) == kBPlus) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/Bplus_Bminus/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bplus_Bminus/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/Bplus_Bminus/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bplus_Bminus/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/Bplus_Bminus/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bplus_Bminus/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if (std::abs(pdgMotherC1) == kB0 && std::abs(pdgMotherC2) == kB0) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/B0_B0bar/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/B0_B0bar/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/B0_B0bar/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/B0_B0bar/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/B0_B0bar/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/B0_B0bar/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if (std::abs(pdgMotherC1) == kBS && std::abs(pdgMotherC2) == kBS) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/Bs0_Bs0bar/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bs0_Bs0bar/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/Bs0_Bs0bar/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bs0_Bs0bar/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/Bs0_Bs0bar/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bs0_Bs0bar/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if (std::abs(pdgMotherC1) == kLambdaB0 && std::abs(pdgMotherC2) == kLambdaB0) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/Lb0_Lb0bar/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Lb0_Lb0bar/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/Lb0_Lb0bar/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Lb0_Lb0bar/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/Lb0_Lb0bar/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Lb0_Lb0bar/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if ((std::abs(pdgMotherC1) == kBPlus && std::abs(pdgMotherC2) == kB0) || (std::abs(pdgMotherC2) == kBPlus && std::abs(pdgMotherC1) == kB0)) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/Bpm_B0/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bpm_B0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/Bpm_B0/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bpm_B0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/Bpm_B0/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bpm_B0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if ((std::abs(pdgMotherC1) == kBPlus && std::abs(pdgMotherC2) == kLambdaB0) || (std::abs(pdgMotherC2) == kBPlus && std::abs(pdgMotherC1) == kLambdaB0)) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/Bpm_Lb0/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bpm_Lb0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/Bpm_Lb0/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bpm_Lb0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/Bpm_Lb0/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bpm_Lb0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if ((std::abs(pdgMotherC1) == kB0 && std::abs(pdgMotherC2) == kLambdaB0) || (std::abs(pdgMotherC2) == kB0 && std::abs(pdgMotherC1) == kLambdaB0)) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/B0_Lb0/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/B0_Lb0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/B0_Lb0/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/B0_Lb0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/B0_Lb0/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/B0_Lb0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if ((std::abs(pdgMotherC1) == kBPlus && std::abs(pdgMotherC2) == kBS) || (std::abs(pdgMotherC2) == kBPlus && std::abs(pdgMotherC1) == kBS)) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/Bpm_Bs0/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bpm_Bs0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/Bpm_Bs0/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bpm_Bs0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/Bpm_Bs0/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bpm_Bs0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if ((std::abs(pdgMotherC1) == kB0 && std::abs(pdgMotherC2) == kBS) || (std::abs(pdgMotherC2) == kB0 && std::abs(pdgMotherC1) == kBS)) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/B0_Bs0/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/B0_Bs0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/B0_Bs0/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/B0_Bs0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/B0_Bs0/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/B0_Bs0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if ((std::abs(pdgMotherC1) == kBS && std::abs(pdgMotherC2) == kLambdaB0) || (std::abs(pdgMotherC2) == kBS && std::abs(pdgMotherC1) == kLambdaB0)) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/Bs0_Lb0/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bs0_Lb0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/Bs0_Lb0/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bs0_Lb0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/Bs0_Lb0/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bs0_Lb0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if (std::abs(pdgMotherC1) == kD0 && std::abs(pdgMotherC2) == kD0) { // (Hb->Hc->l) (Hb->Hc->l) combinations // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/b2D02l_b2D02l/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/b2D02l_b2D02l/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/b2D02l_b2D02l/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/b2D02l_b2D02l/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/b2D02l_b2D02l/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/b2D02l_b2D02l/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if (std::abs(pdgMotherC1) == kDPlus && std::abs(pdgMotherC2) == kDPlus) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/b2Dpm2l_b2Dpm2l/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/b2Dpm2l_b2Dpm2l/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/b2Dpm2l_b2Dpm2l/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/b2Dpm2l_b2Dpm2l/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/b2Dpm2l_b2Dpm2l/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/b2Dpm2l_b2Dpm2l/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if (std::abs(pdgMotherC1) == kLambdaCPlus && std::abs(pdgMotherC2) == kLambdaCPlus) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/b2Lcpm2l_b2Lcpm2l/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/b2Lcpm2l_b2Lcpm2l/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/b2Lcpm2l_b2Lcpm2l/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/b2Lcpm2l_b2Lcpm2l/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/b2Lcpm2l_b2Lcpm2l/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/b2Lcpm2l_b2Lcpm2l/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } if (sourceId == 17) { // (Hb->Hc->l) (Hb->Hc->l) combinations categorized by Hc lifetime if ((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus) && (std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (std::abs(pdgMotherC1) == kDPlus && std::abs(pdgMotherC2) == kDPlus) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus)) || ((std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0) && (std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau130/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau130/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau130/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau130/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau130/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau130/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kDPlus)) || ((std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0) && (std::abs(pdgMotherC1) == kDPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus) && (std::abs(pdgMotherC2) == kDPlus)) || ((std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus) && (std::abs(pdgMotherC1) == kDPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } } else if (sourceId == 20) { // (Hb->Hc+Hc->l) (Hb->Hc->l) combinations categorized by Hc lifetime if ((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus) && (std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (std::abs(pdgMotherC1) == kDPlus && std::abs(pdgMotherC2) == kDPlus) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus)) || ((std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0) && (std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau130/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau130/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau130/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau130/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau130/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau130/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kDPlus)) || ((std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0) && (std::abs(pdgMotherC1) == kDPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus) && (std::abs(pdgMotherC2) == kDPlus)) || ((std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus) && (std::abs(pdgMotherC1) == kDPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } } else if (sourceId == 21) { // (Hb->Hc+Hc->l) (Hb->Hc+Hc->l) combinations categorized by Hc lifetime if ((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus) && (std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (std::abs(pdgMotherC1) == kDPlus && std::abs(pdgMotherC2) == kDPlus) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus)) || ((std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0) && (std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau130/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau130/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau130/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau130/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau130/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau130/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kDPlus)) || ((std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0) && (std::abs(pdgMotherC1) == kDPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus) && (std::abs(pdgMotherC2) == kDPlus)) || ((std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus) && (std::abs(pdgMotherC1) == kDPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } } else if (sourceId == 22) { // (Hb->Hc+Hc->l) (Hb->Hc+Hc->l) combinations categorized by Hc lifetime if ((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus) && (std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (std::abs(pdgMotherC1) == kDPlus && std::abs(pdgMotherC2) == kDPlus) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus)) || ((std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0) && (std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau130/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau130/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau130/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau130/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau130/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau130/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kDPlus)) || ((std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0) && (std::abs(pdgMotherC1) == kDPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus) && (std::abs(pdgMotherC2) == kDPlus)) || ((std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus) && (std::abs(pdgMotherC1) == kDPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } } @@ -2182,8 +2214,8 @@ struct checkMCPairTemplate { float deltaPt2 = t2mc.pt() - t2.pt(); switch (std::abs(mcmother.pdgCode())) { case 111: - if (IsFromCharm(mcmother, mcparticles) < 0 && IsFromBeauty(mcmother, mcparticles) < 0) { // prompt pi0 - fillRecHistograms<1>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // prompt pi0 + if (IsFromCharm(mcmother, mcparticles) < 0 && IsFromBeauty(mcmother, mcparticles) < 0) { // prompt pi0 + fillRecHistograms<1>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // prompt pi0 if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { if (t1.sign() * t2.sign() < 0) { // ULS fRegistry.fill(HIST("Pair/sm/PromptPi0/uls/hMvsPhiV"), phiv, v12.M()); @@ -2199,8 +2231,8 @@ struct checkMCPairTemplate { fRegistry.fill(HIST("Pair/sm/PromptPi0/lsmm/hDCAz1vsDCAz2"), dcaZinSigma(t1), dcaZinSigma(t2)); } } - } else { // non-prompt pi0 - fillRecHistograms<2>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // non-prompt pi0 + } else { // non-prompt pi0 + fillRecHistograms<2>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // non-prompt pi0 if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { if (t1.sign() * t2.sign() < 0) { // ULS fRegistry.fill(HIST("Pair/sm/NonPromptPi0/uls/hMvsPhiV"), phiv, v12.M()); @@ -2219,33 +2251,33 @@ struct checkMCPairTemplate { } break; case 221: - fillRecHistograms<3>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // eta + fillRecHistograms<3>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // eta break; case 331: - fillRecHistograms<4>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // eta' + fillRecHistograms<4>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // eta' break; case 113: - fillRecHistograms<5>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // rho + fillRecHistograms<5>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // rho break; case 223: - fillRecHistograms<6>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // omega + fillRecHistograms<6>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // omega if (mcmother.daughtersIds().size() == 2) { - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Pair/sm/Omega2ll/uls/hs"), v12.M(), v12.Pt(), pair_dca, weight); // omeag->ee + if (t1.sign() * t2.sign() < 0) { // ULS + fRegistry.fill(HIST("Pair/sm/Omega2ll/uls/hs"), v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // omeag->ee } } break; case 333: - fillRecHistograms<7>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // phi + fillRecHistograms<7>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // phi if (mcmother.daughtersIds().size() == 2) { - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Pair/sm/Phi2ll/uls/hs"), v12.M(), v12.Pt(), pair_dca, weight); // phi->ee + if (t1.sign() * t2.sign() < 0) { // ULS + fRegistry.fill(HIST("Pair/sm/Phi2ll/uls/hs"), v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // phi->ee } } break; case 443: if (IsFromBeauty(mcmother, mcparticles) > 0) { - fillRecHistograms<9>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // non-prompt J/psi + fillRecHistograms<9>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // non-prompt J/psi if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { if (t1.sign() * t2.sign() < 0) { // ULS fRegistry.fill(HIST("Pair/sm/NonPromptJPsi/uls/hDeltaPtvsDCA"), pair_dca, deltaPt1 + deltaPt2); @@ -2259,7 +2291,7 @@ struct checkMCPairTemplate { } } } else { - fillRecHistograms<8>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // prompt J/psi + fillRecHistograms<8>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // prompt J/psi if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { if (t1.sign() * t2.sign() < 0) { // ULS fRegistry.fill(HIST("Pair/sm/PromptJPsi/uls/hDeltaPtvsDCA"), pair_dca, deltaPt1 + deltaPt2); @@ -2276,9 +2308,9 @@ struct checkMCPairTemplate { break; case 100443: if (IsFromBeauty(mcmother, mcparticles) > 0) { - fillRecHistograms<11>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // non-prompt psi2S + fillRecHistograms<11>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // non-prompt psi2S } else { - fillRecHistograms<10>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // prompt psi2S + fillRecHistograms<10>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // prompt psi2S } break; default: @@ -2287,7 +2319,7 @@ struct checkMCPairTemplate { } else if (!(t1mc.isPhysicalPrimary() || t1mc.producedByGenerator()) && !(t2mc.isPhysicalPrimary() || t2mc.producedByGenerator())) { switch (std::abs(mcmother.pdgCode())) { case 22: - fillRecHistograms<0>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // photon conversion + fillRecHistograms<0>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // photon conversion if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { fRegistry.fill(HIST("Pair/sm/Photon/uls/hMvsPhiV"), phiv, v12.M()); float rxy_gen = std::sqrt(std::pow(t1mc.vx(), 2) + std::pow(t1mc.vy(), 2)); @@ -2305,28 +2337,28 @@ struct checkMCPairTemplate { auto mp2 = mcparticles.iteratorAt(t2mc.mothersIds()[0]); switch (hfee_type) { case static_cast(EM_HFeeType::kCe_Ce): - fillRecHistograms<15>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight); // c2l_c2l + fillRecHistograms<15>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight, mp1.pt(), mp2.pt()); // c2l_c2l break; case static_cast(EM_HFeeType::kBe_Be): - fillRecHistograms<16>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight); // b2l_b2l + fillRecHistograms<16>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight, mp1.pt(), mp2.pt()); // b2l_b2l break; case static_cast(EM_HFeeType::kBCe_BCe): - fillRecHistograms<17>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight); // b2c2l_b2c2l + fillRecHistograms<17>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight, mp1.pt(), mp2.pt()); // b2c2l_b2c2l break; case static_cast(EM_HFeeType::kBCe_Be_SameB): - fillRecHistograms<18>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight); // b2c2l_b2l_sameb + fillRecHistograms<18>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight, mp1.pt(), mp2.pt()); // b2c2l_b2l_sameb break; case static_cast(EM_HFeeType::kBCe_Be_DiffB): - fillRecHistograms<19>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight); // b2c2l_b2l_diffb + fillRecHistograms<19>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight, mp1.pt(), mp2.pt()); // b2c2l_b2l_diffb break; case static_cast(EM_HFeeType::kBCCe_BCe): - fillRecHistograms<20>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight); // b2cc2l_b2c2l + fillRecHistograms<20>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight, mp1.pt(), mp2.pt()); // b2cc2l_b2c2l break; case static_cast(EM_HFeeType::kBCCe_BCCe): - fillRecHistograms<21>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight); // b2cc2l_b2cc2l + fillRecHistograms<21>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight, mp1.pt(), mp2.pt()); // b2cc2l_b2cc2l break; case static_cast(EM_HFeeType::kBCCee): - fillRecHistograms<22>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight); // b2cc2ll + fillRecHistograms<22>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight, mp1.pt(), mp2.pt()); // b2cc2ll break; default: break; diff --git a/PWGEM/Dilepton/Tasks/createResolutionMapDerived.cxx b/PWGEM/Dilepton/Tasks/createResolutionMapDerived.cxx index a608e07d79a..a322e7b0d97 100644 --- a/PWGEM/Dilepton/Tasks/createResolutionMapDerived.cxx +++ b/PWGEM/Dilepton/Tasks/createResolutionMapDerived.cxx @@ -142,6 +142,7 @@ struct createResolutionMapDerived { Configurable cfg_max_phiposition_track{"cfg_max_phiposition_track", 6.3, "max phi position for single track at certain radius"}; Configurable acceptOnlyCorrectMatch{"acceptOnlyCorrectMatch", false, "flag to accept only correct match between ITS and TPC"}; // this is only for MC study, as we don't know correct match in data. Configurable acceptOnlyWrongMatch{"acceptOnlyWrongMatch", false, "flag to accept only wrong match between ITS and TPC"}; // this is only for MC study, as we don't know correct match in data. + Configurable acceptOnlyPhysicalPrimary{"acceptOnlyPhysicalPrimary", false, "flag to accept only Physical Primary electrons"}; Configurable cfg_pid_scheme{"cfg_pid_scheme", static_cast(DielectronCut::PIDSchemes::kTPChadrejORTOFreq), "pid scheme [kTOFreq : 0, kTPChadrej : 1, kTPChadrejORTOFreq : 2, kTPConly : 3, kTOFif = 4, kPIDML = 5]"}; Configurable cfg_min_TPCNsigmaEl{"cfg_min_TPCNsigmaEl", -2.0, "min. TPC n sigma for electron inclusion"}; @@ -456,6 +457,9 @@ struct createResolutionMapDerived { if (electroncuts.acceptOnlyWrongMatch && !o2::aod::pwgem::dilepton::utils::mcutil::hasFakeMatchITSTPC(track)) { continue; } + if (electroncuts.acceptOnlyPhysicalPrimary && !mcParticle.isPhysicalPrimary()) { + continue; + } registry.fill(HIST("Electron/hPIDForTracking"), track.pidForTracking()); if (electroncuts.checkPIDForTracking && track.pidForTracking() != static_cast(electroncuts.PartIdentifier)) { diff --git a/PWGEM/Dilepton/Tasks/dielectronSV.cxx b/PWGEM/Dilepton/Tasks/dielectronMixingTest.cxx similarity index 79% rename from PWGEM/Dilepton/Tasks/dielectronSV.cxx rename to PWGEM/Dilepton/Tasks/dielectronMixingTest.cxx index d61e9beea71..70ed37ceb05 100644 --- a/PWGEM/Dilepton/Tasks/dielectronSV.cxx +++ b/PWGEM/Dilepton/Tasks/dielectronMixingTest.cxx @@ -14,7 +14,7 @@ // This code is for dielectron analyses. // Please write to: daiki.sekihata@cern.ch -#include "PWGEM/Dilepton/Core/DileptonSV.h" +#include "PWGEM/Dilepton/Core/DileptonMixingTest.h" #include "PWGEM/Dilepton/Utils/PairUtilities.h" #include @@ -25,5 +25,5 @@ using namespace o2::framework; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ - adaptAnalysisTask>(cfgc, TaskName{"dielectron-sv"})}; + adaptAnalysisTask>(cfgc, TaskName{"dielectronMixingTests"})}; } diff --git a/PWGEM/Dilepton/Tasks/dielectronSVMC.cxx b/PWGEM/Dilepton/Tasks/dielectronSVMC.cxx deleted file mode 100644 index 7bcd0ab497d..00000000000 --- a/PWGEM/Dilepton/Tasks/dielectronSVMC.cxx +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2019-2020 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. -// -// ======================== -// -// This code runs loop over electron table and make pairs. -// Please write to: daiki.sekihata@cern.ch - -#include "PWGEM/Dilepton/Core/DileptonSVMC.h" -#include "PWGEM/Dilepton/Utils/PairUtilities.h" - -#include -#include - -using namespace o2::framework; - -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - return WorkflowSpec{ - adaptAnalysisTask>(cfgc, TaskName{"dielectron-sv-mc"})}; -} diff --git a/PWGEM/Dilepton/Tasks/dimuonSV.cxx b/PWGEM/Dilepton/Tasks/dimuonSV.cxx deleted file mode 100644 index ec829a60065..00000000000 --- a/PWGEM/Dilepton/Tasks/dimuonSV.cxx +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2019-2020 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. -// -// ======================== -// -// This code is for dimuon analyses. -// Please write to: daiki.sekihata@cern.ch - -#include "PWGEM/Dilepton/Core/DileptonSV.h" -#include "PWGEM/Dilepton/Utils/PairUtilities.h" - -#include -#include - -using namespace o2::framework; - -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - return WorkflowSpec{ - adaptAnalysisTask>(cfgc, TaskName{"dimuon-sv"})}; -} diff --git a/PWGEM/Dilepton/Tasks/dimuonSVMC.cxx b/PWGEM/Dilepton/Tasks/dimuonSVMC.cxx deleted file mode 100644 index 2643f9a2344..00000000000 --- a/PWGEM/Dilepton/Tasks/dimuonSVMC.cxx +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2019-2020 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. -// -// ======================== -// -// This code runs loop over dalitz ee table for dalitz QC. -// Please write to: daiki.sekihata@cern.ch - -#include "PWGEM/Dilepton/Core/DileptonSVMC.h" -#include "PWGEM/Dilepton/Utils/PairUtilities.h" - -#include -#include - -using namespace o2::framework; - -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - return WorkflowSpec{ - adaptAnalysisTask>(cfgc, TaskName{"dimuon-sv-mc"})}; -} diff --git a/PWGEM/Dilepton/Tasks/emEfficiencyEE.cxx b/PWGEM/Dilepton/Tasks/emEfficiencyEE.cxx index 9cf9385ac71..a739ff9ca50 100644 --- a/PWGEM/Dilepton/Tasks/emEfficiencyEE.cxx +++ b/PWGEM/Dilepton/Tasks/emEfficiencyEE.cxx @@ -1043,8 +1043,7 @@ struct AnalysisTrackSelection { template void runRecTrackMore(TEvents const& events, TEventsMC const& /*eventsMC*/, TTracks const& groupedTracks, TTracksMC const& tracksMC, TAmbigTracks const& ambiTracksMid) { - - std::map fRecTrackLabels[fTrackCuts.size() + 1]; + std::vector> fRecTrackLabels(fTrackCuts.size() + 1); uint32_t filterMap = 0; trackSel.reserve(groupedTracks.size()); @@ -1052,10 +1051,7 @@ struct AnalysisTrackSelection { for (auto& track : groupedTracks) { // How many time the associated MC track was seen for this cut - Int_t fRecCounters[fTrackCuts.size() + 1]; - for (unsigned int k = 0; k < fTrackCuts.size() + 1; k++) { - fRecCounters[k] = 0; - } + std::vector fRecCounters(fTrackCuts.size() + 1, 0); filterMap = 0; Int_t ambiguousinfo = 0; @@ -1157,10 +1153,7 @@ struct AnalysisTrackSelection { uint32_t mcDecision = 0; int isig = 0; Int_t mctrackindex = -999; - Int_t doublereconstructedtrack[fTrackCuts.size() + 1]; - for (unsigned int k = 0; k < fTrackCuts.size() + 1; k++) { - doublereconstructedtrack[k] = 0; - } + std::vector doublereconstructedtrack(fTrackCuts.size() + 1, 0); for (auto sig = fMCSignals.begin(); sig != fMCSignals.end(); sig++, isig++) { if constexpr ((TTrackFillMap & VarManager::ObjTypes::Track) > 0) { if (track.has_mcParticle()) { diff --git a/PWGEM/Dilepton/Utils/EventMixingHandler.h b/PWGEM/Dilepton/Utils/EventMixingHandler.h index 4698654be49..1c69210490f 100644 --- a/PWGEM/Dilepton/Utils/EventMixingHandler.h +++ b/PWGEM/Dilepton/Utils/EventMixingHandler.h @@ -16,6 +16,8 @@ #define PWGEM_DILEPTON_UTILS_EVENTMIXINGHANDLER_H_ #include +#include +#include #include namespace o2::aod::pwgem::dilepton::utils @@ -36,6 +38,9 @@ class EventMixingHandler fNdepth = ndepth; fMapMixBins.clear(); fMap_Tracks_per_collision.clear(); + if (fNdepth <= 0) { + throw std::invalid_argument("mixing depth must be positive"); + } } ~EventMixingHandler() @@ -44,7 +49,13 @@ class EventMixingHandler fMap_Tracks_per_collision.clear(); } - void SetNdepth(int ndepth) { fNdepth = ndepth; } + void SetNdepth(int ndepth) + { + if (ndepth <= 0) { + throw std::invalid_argument("mixing depth must be positive"); + } + fNdepth = ndepth; + } void ReserveNTracksPerCollision(U key_df_collision, int ntrack) { @@ -56,20 +67,33 @@ class EventMixingHandler fMap_Tracks_per_collision[key_df_collision].emplace_back(obj); } - std::vector GetCollisionIdsFromEventPool(T key_bin) { return fMapMixBins[key_bin]; } - std::vector GetTracksPerCollision(T key_bin, int index) { return fMap_Tracks_per_collision[fMapMixBins[key_bin][index]]; } - std::vector GetTracksPerCollision(U key_df_collision) { return fMap_Tracks_per_collision[key_df_collision]; } + const std::vector& GetCollisionIdsFromEventPool(const T& key_bin) const + { + const auto it = fMapMixBins.find(key_bin); + static const std::vector empty; + return it == fMapMixBins.end() ? empty : it->second; + } + + const std::vector& GetTracksPerCollision(const U& key) const + { + const auto it = fMap_Tracks_per_collision.find(key); + static const std::vector empty; + return it == fMap_Tracks_per_collision.end() ? empty : it->second; + } // call this function at the end of collision loop - void AddCollisionIdAtLast(T key_bin, U key_df_collision) + std::optional AddCollisionIdAtLast(T key_bin, U key_df_collision) { // LOGF(info, "fMapMixBins[key_bin].size() = %d", fMapMixBins[key_bin].size()); - if (static_cast(fMapMixBins[key_bin].size()) >= fNdepth) { - fMap_Tracks_per_collision[fMapMixBins[key_bin][0]].clear(); - fMap_Tracks_per_collision[fMapMixBins[key_bin][0]].shrink_to_fit(); - fMapMixBins[key_bin].erase(fMapMixBins[key_bin].begin()); + auto& ids = fMapMixBins[key_bin]; + std::optional evicted; + if (static_cast(ids.size()) >= fNdepth) { + evicted = ids.front(); + fMap_Tracks_per_collision.erase(*evicted); + ids.erase(ids.begin()); } - fMapMixBins[key_bin].emplace_back(key_df_collision); + ids.emplace_back(key_df_collision); + return evicted; } private: diff --git a/PWGEM/PhotonMeson/Core/EMPhotonEventCut.cxx b/PWGEM/PhotonMeson/Core/EMPhotonEventCut.cxx index f7eaea2fd44..97791f3d4d9 100644 --- a/PWGEM/PhotonMeson/Core/EMPhotonEventCut.cxx +++ b/PWGEM/PhotonMeson/Core/EMPhotonEventCut.cxx @@ -19,6 +19,9 @@ #include +#include +#include + ClassImp(EMPhotonEventCut); void EMPhotonEventCut::SetRequireSel8(bool flag) @@ -134,3 +137,45 @@ void EMPhotonEventCut::SetRequireEMCHardwareTriggered(bool flag) mRequireEMCHardwareTriggered = flag; LOG(info) << "EM Photon Event Cut, require the EMC to be triggered by requiring kEMC7 or kDMC7: " << mRequireEMCHardwareTriggered; } + +void EMPhotonEventCut::SetTrackOccupancyRange(float min, float max) +{ + mMinTrackOcc = min; + mMaxTrackOcc = max; + LOG(info) << "EM Photon Event Cut, set track occupancy range: " << mMinTrackOcc << " - " << mMaxTrackOcc; +} + +void EMPhotonEventCut::SetFT0COccupancyRange(float min, float max) +{ + mMinFT0COcc = min; + mMaxFT0COcc = max; + LOG(info) << "EM Photon Event Cut, set FT0C occupancy range: " << mMinFT0COcc << " - " << mMaxFT0COcc; +} + +void EMPhotonEventCut::SetRequireGoodQvec(bool flag) +{ + mRequireGoodQvec = flag; + LOG(info) << "EM Photon Event Cut, require good Q vectors (only applied if the collision table has Q vectors): " << mRequireGoodQvec; +} + +void EMPhotonEventCut::SetQvecHarmonic(int harmonic) +{ + mQvecHarmonic = harmonic; + LOG(info) << "EM Photon Event Cut, set Q vector harmonic: " << mQvecHarmonic; +} + +void EMPhotonEventCut::SetMaxQvec(float max) +{ + mMaxQvec = max; + LOG(info) << "EM Photon Event Cut, set max. absolute Q vector component: " << mMaxQvec; +} + +void EMPhotonEventCut::SetQvecDetectors(std::vector const& detectors) +{ + mQvecDetectors = detectors; + std::string list; + for (const auto& detector : mQvecDetectors) { + list += std::to_string(detector) + " "; + } + LOG(info) << "EM Photon Event Cut, set Q vector detectors for Q vector cut: " << list; +} diff --git a/PWGEM/PhotonMeson/Core/EMPhotonEventCut.h b/PWGEM/PhotonMeson/Core/EMPhotonEventCut.h index 26b1f2c9490..9c03e498789 100644 --- a/PWGEM/PhotonMeson/Core/EMPhotonEventCut.h +++ b/PWGEM/PhotonMeson/Core/EMPhotonEventCut.h @@ -16,10 +16,26 @@ #ifndef PWGEM_PHOTONMESON_CORE_EMPHOTONEVENTCUT_H_ #define PWGEM_PHOTONMESON_CORE_EMPHOTONEVENTCUT_H_ +#include "PWGEM/PhotonMeson/Utils/EventUtilities.h" + #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/TriggerAliases.h" +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include #include +#include +#include class EMPhotonEventCut { @@ -47,9 +63,31 @@ class EMPhotonEventCut kIsGoodITSLayersAll, kEMCReadoutInMB, kEMCHardwareTriggered, + kTrackOccupancy, + kFT0COccupancy, + kGoodQvec, kNCuts }; + /// bins of the Event/{before,after}/hCollisionCounter histogram + enum class EventCounterBin : uint8_t { + kAll = 1, + kNoTFBorder = 2, + kNoITSROFBorder = 3, + kNoSameBunchPileup = 4, + kVertexITSTPC = 5, + kGoodZVtxFT0vsPV = 6, + kFT0AND = 7, + kSel8 = 8, + kZVtx = 9, + kTrackOccupancy = 10, + kFT0COccupancy = 11, + kEMCMBReadout = 12, + kEMCL0Trigger = 13, + kGoodQvec = 14, + kAccepted = 15 + }; + [[nodiscard]] const std::string& getName() const { return name; } [[nodiscard]] const std::string& getTitle() const { return title; } @@ -113,6 +151,15 @@ class EMPhotonEventCut if (mRequireEMCHardwareTriggered && !IsSelected(collision, EMPhotonEventCuts::kEMCHardwareTriggered)) { return false; } + if (!IsSelected(collision, EMPhotonEventCuts::kTrackOccupancy)) { + return false; + } + if (!IsSelected(collision, EMPhotonEventCuts::kFT0COccupancy)) { + return false; + } + if (mRequireGoodQvec && !IsSelected(collision, EMPhotonEventCuts::kGoodQvec)) { + return false; + } return true; } @@ -177,11 +224,153 @@ class EMPhotonEventCut case EMPhotonEventCuts::kEMCHardwareTriggered: return (collision.alias_bit(kEMC7) || collision.alias_bit(kDMC7)); + case EMPhotonEventCuts::kTrackOccupancy: + if constexpr (o2::aod::pwgem::pm::utils::event::HasTrackOccupancy) { + return mMinTrackOcc <= collision.trackOccupancyInTimeRange() && collision.trackOccupancyInTimeRange() < mMaxTrackOcc; + } else { + // the event table has no track occupancy, the cut can not be applied + warnOccupancyNotAvailable(isTrackOccupancyCutSet(), "track"); + return true; + } + + case EMPhotonEventCuts::kFT0COccupancy: + if constexpr (o2::aod::pwgem::pm::utils::event::HasFT0COccupancy) { + return mMinFT0COcc <= collision.ft0cOccupancyInTimeRange() && collision.ft0cOccupancyInTimeRange() < mMaxFT0COcc; + } else { + // the event table has no FT0C occupancy, the cut can not be applied + warnOccupancyNotAvailable(isFT0COccupancyCutSet(), "FT0C"); + return true; + } + + case EMPhotonEventCuts::kGoodQvec: + if constexpr (o2::aod::pwgem::pm::utils::event::HasQvec) { + return o2::aod::pwgem::pm::utils::event::isQvecGood(collision, mQvecDetectors, mQvecHarmonic, mMaxQvec); + } else { + // the event table has no Q vectors, the cut can not be applied + if (mRequireGoodQvec && !mWarnedNoQvec) { + LOG(warning) << "EM Photon Event Cut: Q vector cut requested, but the collision table has no Q vectors. Q vector cut is not applied!"; + mWarnedNoQvec = true; + } + return true; + } + default: return true; } } + /// \brief add the event QA histograms to the given registry. Call this after all cut values are set! + /// \param fRegistry pointer to the histogram registry of the task + /// \param useWeight whether the histograms should store weights (Sumw2) + void addEventHistograms(o2::framework::HistogramRegistry* fRegistry, bool useWeight = false) const + { + auto hCollisionCounter = fRegistry->add("Event/before/hCollisionCounter", "collision counter;;Number of events", o2::framework::HistType::kTH1D, {{static_cast(EventCounterBin::kAccepted), 0.5, static_cast(EventCounterBin::kAccepted) + 0.5f}}, useWeight); + hCollisionCounter->GetXaxis()->SetBinLabel(static_cast(EventCounterBin::kAll), "all"); + hCollisionCounter->GetXaxis()->SetBinLabel(static_cast(EventCounterBin::kNoTFBorder), "No TF border"); + hCollisionCounter->GetXaxis()->SetBinLabel(static_cast(EventCounterBin::kNoITSROFBorder), "No ITS ROF border"); + hCollisionCounter->GetXaxis()->SetBinLabel(static_cast(EventCounterBin::kNoSameBunchPileup), "No Same Bunch Pileup"); + hCollisionCounter->GetXaxis()->SetBinLabel(static_cast(EventCounterBin::kVertexITSTPC), "Is Vertex ITSTPC"); + hCollisionCounter->GetXaxis()->SetBinLabel(static_cast(EventCounterBin::kGoodZVtxFT0vsPV), "Is Good Zvtx FT0vsPV"); + hCollisionCounter->GetXaxis()->SetBinLabel(static_cast(EventCounterBin::kFT0AND), "FT0AND"); + hCollisionCounter->GetXaxis()->SetBinLabel(static_cast(EventCounterBin::kSel8), "sel8"); + hCollisionCounter->GetXaxis()->SetBinLabel(static_cast(EventCounterBin::kZVtx), std::format("{:g} < Z_{{vtx}} < {:g} cm", mMinZvtx, mMaxZvtx).c_str()); + hCollisionCounter->GetXaxis()->SetBinLabel(static_cast(EventCounterBin::kTrackOccupancy), "track occupancy"); + hCollisionCounter->GetXaxis()->SetBinLabel(static_cast(EventCounterBin::kFT0COccupancy), "FT0C occupancy"); + hCollisionCounter->GetXaxis()->SetBinLabel(static_cast(EventCounterBin::kEMCMBReadout), "EMC MB Readout"); + hCollisionCounter->GetXaxis()->SetBinLabel(static_cast(EventCounterBin::kEMCL0Trigger), "EMC L0 Triggered"); + hCollisionCounter->GetXaxis()->SetBinLabel(static_cast(EventCounterBin::kGoodQvec), "good Q vector"); + hCollisionCounter->GetXaxis()->SetBinLabel(static_cast(EventCounterBin::kAccepted), "accepted"); + + fRegistry->add("Event/before/hZvtx", "vertex z; Z_{vtx} (cm)", o2::framework::HistType::kTH1D, {{220, -11, +11}}, useWeight); + fRegistry->add("Event/before/hMultNTracksPV", "hMultNTracksPV; N_{track} to PV", o2::framework::HistType::kTH1F, {{6001, -0.5, 6000.5}}, useWeight); + fRegistry->add("Event/before/hMultNTracksPVeta1", "hMultNTracksPVeta1; N_{track} to PV", o2::framework::HistType::kTH1F, {{6001, -0.5, 6000.5}}, useWeight); + fRegistry->add("Event/before/hMultFT0", "hMultFT0;mult. FT0A;mult. FT0C", o2::framework::HistType::kTH2F, {{200, 0, 200000}, {60, 0, 60000}}, useWeight); + fRegistry->add("Event/before/hCentFT0A", "hCentFT0A;centrality FT0A (%)", o2::framework::HistType::kTH1F, {{100, 0., 100.}}, useWeight); + fRegistry->add("Event/before/hCentFT0C", "hCentFT0C;centrality FT0C (%)", o2::framework::HistType::kTH1F, {{100, 0., 100.}}, useWeight); + fRegistry->add("Event/before/hCentFT0M", "hCentFT0M;centrality FT0M (%)", o2::framework::HistType::kTH1F, {{100, 0., 100.}}, useWeight); + fRegistry->add("Event/before/hCentFT0CvsMultNTracksPV", "hCentFT0CvsMultNTracksPV;centrality FT0C (%);N_{track} to PV", o2::framework::HistType::kTH2F, {{110, 0, 110}, {500, 0, 5000}}, useWeight); + fRegistry->add("Event/before/hMultFT0CvsMultNTracksPV", "hMultFT0CvsMultNTracksPV;mult. FT0C;N_{track} to PV", o2::framework::HistType::kTH2F, {{60, 0, 60000}, {500, 0, 5000}}, useWeight); + fRegistry->add("Event/before/hMultFT0CvsOccupancy", "hMultFT0CvsOccupancy;mult. FT0C;N_{track} in time range", o2::framework::HistType::kTH2F, {{60, 0, 60000}, {500, 0, 10000}}, useWeight); + fRegistry->addClone("Event/before/", "Event/after/"); + } + + /// \brief fill the event QA histograms + /// \tparam ev_id 0 for before the event selection, 1 for after the event selection + /// \param fRegistry pointer to the histogram registry of the task + /// \param collision collision to fill + /// \param weight weight of the collision + template + void fillEventInfo(o2::framework::HistogramRegistry* fRegistry, TCollision const& collision, float weight = 1.f) const + { + static constexpr std::array EventTypes = {"before/", "after/"}; + auto fillCounter = [&](EventCounterBin bin) { + fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCollisionCounter"), static_cast(bin), weight); + }; + + fillCounter(EventCounterBin::kAll); + if (IsSelected(collision, EMPhotonEventCuts::kNoTFB)) { + fillCounter(EventCounterBin::kNoTFBorder); + } + if (IsSelected(collision, EMPhotonEventCuts::kNoITSROFB)) { + fillCounter(EventCounterBin::kNoITSROFBorder); + } + if (IsSelected(collision, EMPhotonEventCuts::kNoSameBunchPileup)) { + fillCounter(EventCounterBin::kNoSameBunchPileup); + } + if (IsSelected(collision, EMPhotonEventCuts::kIsVertexITSTPC)) { + fillCounter(EventCounterBin::kVertexITSTPC); + } + if (IsSelected(collision, EMPhotonEventCuts::kIsGoodZvtxFT0vsPV)) { + fillCounter(EventCounterBin::kGoodZVtxFT0vsPV); + } + if (IsSelected(collision, EMPhotonEventCuts::kFT0AND)) { + fillCounter(EventCounterBin::kFT0AND); + } + if (IsSelected(collision, EMPhotonEventCuts::kSel8)) { + fillCounter(EventCounterBin::kSel8); + } + if (IsSelected(collision, EMPhotonEventCuts::kZvtx)) { + fillCounter(EventCounterBin::kZVtx); + } + if (IsSelected(collision, EMPhotonEventCuts::kTrackOccupancy)) { + fillCounter(EventCounterBin::kTrackOccupancy); + } + if (IsSelected(collision, EMPhotonEventCuts::kFT0COccupancy)) { + fillCounter(EventCounterBin::kFT0COccupancy); + } + if (IsSelected(collision, EMPhotonEventCuts::kEMCReadoutInMB)) { + fillCounter(EventCounterBin::kEMCMBReadout); + } + if (IsSelected(collision, EMPhotonEventCuts::kEMCHardwareTriggered)) { + fillCounter(EventCounterBin::kEMCL0Trigger); + } + if constexpr (o2::aod::pwgem::pm::utils::event::HasQvec) { + if (IsSelected(collision, EMPhotonEventCuts::kGoodQvec)) { + fillCounter(EventCounterBin::kGoodQvec); + } + } + + fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hZvtx"), collision.posZ(), weight); + fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hMultNTracksPV"), collision.multNTracksPV(), weight); + fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hMultNTracksPVeta1"), collision.multNTracksPVeta1(), weight); + fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hMultFT0"), collision.multFT0A(), collision.multFT0C(), weight); + fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCentFT0A"), collision.centFT0A(), weight); + fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCentFT0C"), collision.centFT0C(), weight); + fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCentFT0M"), collision.centFT0M(), weight); + fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCentFT0CvsMultNTracksPV"), collision.centFT0C(), collision.multNTracksPV(), weight); + fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hMultFT0CvsMultNTracksPV"), collision.multFT0C(), collision.multNTracksPV(), weight); + fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hMultFT0CvsOccupancy"), collision.multFT0C(), collision.trackOccupancyInTimeRange(), weight); + } + + /// \brief fill the "accepted" bin of the before and after collision counter + /// \param fRegistry pointer to the histogram registry of the task + /// \param weight weight of the collision + void fillAccepted(o2::framework::HistogramRegistry* fRegistry, float weight = 1.f) const + { + fRegistry->fill(HIST("Event/before/hCollisionCounter"), static_cast(EventCounterBin::kAccepted), weight); + fRegistry->fill(HIST("Event/after/hCollisionCounter"), static_cast(EventCounterBin::kAccepted), weight); + } + // Setters void SetRequireSel8(bool flag); void SetRequireFT0AND(bool flag); @@ -202,8 +391,32 @@ class EMPhotonEventCut void SetRequireGoodITSLayersAll(bool flag); void SetRequireEMCReadoutInMB(bool flag); void SetRequireEMCHardwareTriggered(bool flag); + void SetTrackOccupancyRange(float min, float max); + void SetFT0COccupancyRange(float min, float max); + void SetRequireGoodQvec(bool flag); + void SetQvecHarmonic(int harmonic); + void SetMaxQvec(float max); + void SetQvecDetectors(std::vector const& detectors); private: + [[nodiscard]] bool isTrackOccupancyCutSet() const + { + return mMinTrackOcc != std::numeric_limits::lowest() || mMaxTrackOcc != std::numeric_limits::max(); + } + + [[nodiscard]] bool isFT0COccupancyCutSet() const + { + return mMinFT0COcc != std::numeric_limits::lowest() || mMaxFT0COcc != std::numeric_limits::max(); + } + + void warnOccupancyNotAvailable(bool cutSet, const char* estimator) const + { + if (cutSet && !mWarnedNoOccupancy) { + LOG(warning) << "EM Photon Event Cut: " << estimator << " occupancy cut set, but the collision table has no " << estimator << " occupancy. Occupancy cut is not applied!"; + mWarnedNoOccupancy = true; + } + } + std::string name; std::string title; bool mRequireSel8{false}; @@ -225,6 +438,18 @@ class EMPhotonEventCut bool mRequireGoodITSLayersAll{false}; bool mRequireEMCReadoutInMB{false}; bool mRequireEMCHardwareTriggered{false}; + + // occupancy cuts, by default no cut is applied + float mMinTrackOcc{std::numeric_limits::lowest()}, mMaxTrackOcc{std::numeric_limits::max()}; + float mMinFT0COcc{std::numeric_limits::lowest()}, mMaxFT0COcc{std::numeric_limits::max()}; + + // Q vector cut, off by default and only applied if the collision table has Q vectors + bool mRequireGoodQvec{false}; + int mQvecHarmonic{o2::aod::pwgem::pm::utils::event::kElliptic}; + float mMaxQvec{20.f}; + std::vector mQvecDetectors; + mutable bool mWarnedNoQvec{false}; + mutable bool mWarnedNoOccupancy{false}; }; #endif // PWGEM_PHOTONMESON_CORE_EMPHOTONEVENTCUT_H_ diff --git a/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h b/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h index 45e4f7c26d7..20fdfc1bb59 100644 --- a/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h +++ b/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h @@ -26,7 +26,6 @@ #include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" #include "PWGEM/PhotonMeson/Utils/EMPhoton.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" #include "PWGEM/PhotonMeson/Utils/NMHistograms.h" #include "PWGEM/PhotonMeson/Utils/PairUtilities.h" // Dilepton headers @@ -98,6 +97,9 @@ struct Pi0EtaToGammaGamma { o2::framework::ConfigurableAxis ConfCentBins{"ConfCentBins", {o2::framework::VARIABLE_WIDTH, 0.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.f, 999.f}, "Mixing bins - centrality"}; o2::framework::ConfigurableAxis ConfEPBins{"ConfEPBins", {o2::framework::VARIABLE_WIDTH, -o2::constants::math::PIHalf, -o2::constants::math::PIQuarter, 0.0f, +o2::constants::math::PIQuarter, +o2::constants::math::PIHalf}, "Mixing bins - event plane angle"}; o2::framework::ConfigurableAxis ConfOccupancyBins{"ConfOccupancyBins", {o2::framework::VARIABLE_WIDTH, -1, 1e+10}, "Mixing bins - occupancy"}; + o2::framework::Configurable cfgSparseFullAxes{"cfgSparseFullAxes", false, "add event plane, occupancy and z-vertex axes to Pair/same/hs and Pair/mix/hs"}; + o2::framework::Configurable cfgNbinsMass{"cfgNbinsMass", 400, "number of bins for the mass axis in same and mixed events"}; + o2::framework::Configurable> cfgMassBinRange{"cfgMassBinRange", {0., 0.8}, "range for the mass-axis in GeV/c2"}; o2::framework::Configurable cfgAlphaMesonCut{"cfgAlphaMesonCut", 0, "flag for photon energy asymmetry distribution cut: 0: no cut, 1: cut specific value, 2: cut depending on pT"}; o2::framework::Configurable cfgAlphaMeson{"cfgAlphaMeson", 0.65, "photon energy asymmetry distribution parameter for specific value cut"}; @@ -279,8 +281,8 @@ struct Pi0EtaToGammaGamma { o2::framework::PresliceOptional perEMCClusterMT = o2::aod::mintm::minClusterId; o2::framework::PresliceOptional perEMCClusterMS = o2::aod::mintm::minClusterId; - o2::framework::Partition>> positrons = o2::aod::emprimaryelectron::sign > int8_t(0) && dileptoncuts.cfg_min_pt_track < o2::aod::track::pt&& nabs(o2::aod::track::eta) < dileptoncuts.cfg_max_eta_track; - o2::framework::Partition>> electrons = o2::aod::emprimaryelectron::sign < int8_t(0) && dileptoncuts.cfg_min_pt_track < o2::aod::track::pt && nabs(o2::aod::track::eta) < dileptoncuts.cfg_max_eta_track; + o2::framework::Partition>> positrons = o2::aod::emprimaryelectron::sign > static_cast(0) && dileptoncuts.cfg_min_pt_track < o2::aod::track::pt&& nabs(o2::aod::track::eta) < dileptoncuts.cfg_max_eta_track; + o2::framework::Partition>> electrons = o2::aod::emprimaryelectron::sign < static_cast(0) && dileptoncuts.cfg_min_pt_track < o2::aod::track::pt && nabs(o2::aod::track::eta) < dileptoncuts.cfg_max_eta_track; o2::aod::pwgem::dilepton::utils::EventMixingHandler, std::pair, o2::aod::pwgem::photonmeson::utils::EMPhoton>* emh1 = nullptr; o2::aod::pwgem::dilepton::utils::EventMixingHandler, std::pair, o2::aod::pwgem::photonmeson::utils::EMPhoton>* emh2 = nullptr; @@ -422,13 +424,16 @@ struct Pi0EtaToGammaGamma { emh1 = new o2::aod::pwgem::dilepton::utils::EventMixingHandler, std::pair, o2::aod::pwgem::photonmeson::utils::EMPhoton>(ndepth); emh2 = new o2::aod::pwgem::dilepton::utils::EventMixingHandler, std::pair, o2::aod::pwgem::photonmeson::utils::EMPhoton>(ndepth); - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(&fRegistry); + DefineEMEventCut(); + fEMEventCut.addEventHistograms(&fRegistry); + std::vector occBinsForHs(occ_bin_edges.begin(), occ_bin_edges.end()); + std::vector epBinsForHs(ep_bin_edges.begin(), ep_bin_edges.end()); + std::vector vtxBinsForHs(zvtx_bin_edges.begin(), zvtx_bin_edges.end()); if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMDalitzEE) { - o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, false, "ee#gamma"); + o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, false, "ee#gamma", cfgSparseFullAxes.value, occBinsForHs, epBinsForHs, vtxBinsForHs, cfgNbinsMass, {cfgMassBinRange->at(0), cfgMassBinRange->at(1)}); } else { - o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, false, "#gamma#gamma"); + o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, false, "#gamma#gamma", cfgSparseFullAxes.value, occBinsForHs, epBinsForHs, vtxBinsForHs, cfgNbinsMass, {cfgMassBinRange->at(0), cfgMassBinRange->at(1)}); } - DefineEMEventCut(); DefinePCMCut(); mPhotonClassSelA = o2::aod::pwgem::photonmeson::utils::pairutil::buildV0PhotonClassSelection(photonclassA); @@ -471,10 +476,17 @@ struct Pi0EtaToGammaGamma { return; } - o2::base::Propagator::initFieldFromGRP(&collision.grpMagField()); + // o2::base::Propagator::initFieldFromGRP(&collision.grpMagField()); // Fetch magnetic field from ccdb for current collision - d_bz = collision.grpMagField().getNominalL3Field(); + // d_bz = collision.grpMagField().getNominalL3Field(); + + auto* grpmag = ccdb->getForRun("GLO/Config/GRPMagField", collision.runNumber()); + if (grpmag == nullptr) { + LOGF(fatal, "Could not retrieve GRPMagField for run %d", collision.runNumber()); + } + d_bz = grpmag->getNominalL3Field(); LOG(info) << "Retrieved GRP for timestamp " << collision.timestamp() << " with magnetic field of " << d_bz << " kZG"; + fV0PhotonCut.SetD_Bz(d_bz); } @@ -505,6 +517,8 @@ struct Pi0EtaToGammaGamma { fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + fEMEventCut.SetTrackOccupancyRange(eventcuts.cfgTrackOccupancyMin, eventcuts.cfgTrackOccupancyMax); + fEMEventCut.SetFT0COccupancyRange(eventcuts.cfgFT0COccupancyMin, eventcuts.cfgFT0COccupancyMax); } void DefinePCMCut() @@ -756,13 +770,12 @@ struct Pi0EtaToGammaGamma { continue; } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(&fRegistry, collision, weight); + fEMEventCut.fillEventInfo<0>(&fRegistry, collision, weight); if (!fEMEventCut.IsSelected(collision)) { continue; } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(&fRegistry, collision, weight); - fRegistry.fill(HIST("Event/before/hCollisionCounter"), 12.0, weight); // accepted - fRegistry.fill(HIST("Event/after/hCollisionCounter"), 12.0, weight); // accepted + fEMEventCut.fillEventInfo<1>(&fRegistry, collision, weight); + fEMEventCut.fillAccepted(&fRegistry, weight); int zbin = lower_bound(zvtx_bin_edges.begin(), zvtx_bin_edges.end(), collision.posZ()) - zvtx_bin_edges.begin() - 1; if (zbin < 0) { @@ -800,6 +813,8 @@ struct Pi0EtaToGammaGamma { occbin = static_cast(occ_bin_edges.size()) - 2; } + float occupancy = (cfgOccupancyEstimator == 1) ? static_cast(collision.trackOccupancyInTimeRange()) : collision.ft0cOccupancyInTimeRange(); + // LOGF(info, "collision.globalIndex() = %d, collision.posZ() = %f, centrality = %f, ep2 = %f, collision.trackOccupancyInTimeRange() = %d, zbin = %d, centbin = %d, epbin = %d, occbin = %d", collision.globalIndex(), collision.posZ(), centrality, ep2, collision.trackOccupancyInTimeRange(), zbin, centbin, epbin, occbin); std::tuple key_bin = std::make_tuple(zbin, centbin, epbin, occbin); @@ -859,7 +874,11 @@ struct Pi0EtaToGammaGamma { continue; } - fRegistry.fill(HIST("Pair/same/hs"), veeg.M(), veeg.Pt(), weight); + if (cfgSparseFullAxes.value) { + fRegistry.fill(HIST("Pair/same/hs"), veeg.M(), veeg.Pt(), ep2, occupancy, collision.posZ(), weight); + } else { + fRegistry.fill(HIST("Pair/same/hs"), veeg.M(), veeg.Pt(), weight); + } std::pair tuple_tmp_id2 = std::make_pair(pos2.trackId(), ele2.trackId()); if (std::find(used_photonIds_per_col.begin(), used_photonIds_per_col.end(), g1.globalIndex()) == used_photonIds_per_col.end()) { @@ -953,7 +972,11 @@ struct Pi0EtaToGammaGamma { wpair *= g2.omegaMBWeight(); } - fRegistry.fill(HIST("Pair/same/hs"), v12.M(), v12.Pt(), wpair); + if (cfgSparseFullAxes.value) { + fRegistry.fill(HIST("Pair/same/hs"), v12.M(), v12.Pt(), ep2, occupancy, collision.posZ(), wpair); + } else { + fRegistry.fill(HIST("Pair/same/hs"), v12.M(), v12.Pt(), wpair); + } if (std::find(used_photonIds_per_col.begin(), used_photonIds_per_col.end(), g1.globalIndex()) == used_photonIds_per_col.end()) { auto emphoton1 = o2::aod::pwgem::photonmeson::utils::EMPhoton(g1.pt(), g1.eta(), g1.phi(), 0); @@ -986,11 +1009,11 @@ struct Pi0EtaToGammaGamma { } // make a vector of selected photons in this collision. - auto selected_photons1_in_this_event = emh1->GetTracksPerCollision(key_df_collision); - auto selected_photons2_in_this_event = emh2->GetTracksPerCollision(key_df_collision); + const auto& selected_photons1_in_this_event = emh1->GetTracksPerCollision(key_df_collision); + const auto& selected_photons2_in_this_event = emh2->GetTracksPerCollision(key_df_collision); - auto collisionIds1_in_mixing_pool = emh1->GetCollisionIdsFromEventPool(key_bin); - auto collisionIds2_in_mixing_pool = emh2->GetCollisionIdsFromEventPool(key_bin); + const auto& collisionIds1_in_mixing_pool = emh1->GetCollisionIdsFromEventPool(key_bin); + const auto& collisionIds2_in_mixing_pool = emh2->GetCollisionIdsFromEventPool(key_bin); if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPCM || pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPHOSPHOS || pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kEMCEMC) { // same kinds pairing for (const auto& mix_dfId_collisionId : collisionIds1_in_mixing_pool) { @@ -1008,7 +1031,7 @@ struct Pi0EtaToGammaGamma { continue; } - auto photons1_from_event_pool = emh1->GetTracksPerCollision(mix_dfId_collisionId); + const auto& photons1_from_event_pool = emh1->GetTracksPerCollision(mix_dfId_collisionId); // LOGF(info, "Do event mixing: current event (%d, %d), ngamma = %d | event pool (%d, %d), ngamma = %d", ndf, collision.globalIndex(), selected_photons1_in_this_event.size(), mix_dfId, mix_collisionId, photons1_from_event_pool.size()); for (const auto& g1 : selected_photons1_in_this_event) { @@ -1047,7 +1070,11 @@ struct Pi0EtaToGammaGamma { continue; } - fRegistry.fill(HIST("Pair/mix/hs"), v12.M(), v12.Pt(), weight); + if (cfgSparseFullAxes.value) { + fRegistry.fill(HIST("Pair/mix/hs"), v12.M(), v12.Pt(), ep2, occupancy, collision.posZ(), weight); + } else { + fRegistry.fill(HIST("Pair/mix/hs"), v12.M(), v12.Pt(), weight); + } } } } // end of loop over mixed event pool @@ -1068,7 +1095,7 @@ struct Pi0EtaToGammaGamma { continue; } - auto photons2_from_event_pool = emh2->GetTracksPerCollision(mix_dfId_collisionId); + const auto& photons2_from_event_pool = emh2->GetTracksPerCollision(mix_dfId_collisionId); // LOGF(info, "Do event mixing: current event (%d, %d), ngamma = %d | event pool (%d, %d), nll = %d", ndf, collision.globalIndex(), selected_photons1_in_this_event.size(), mix_dfId, mix_collisionId, photons2_from_event_pool.size()); for (const auto& g1 : selected_photons1_in_this_event) { @@ -1082,7 +1109,11 @@ struct Pi0EtaToGammaGamma { if (std::fabs(v12.Rapidity()) > maxY) { continue; } - fRegistry.fill(HIST("Pair/mix/hs"), v12.M(), v12.Pt(), weight); + if (cfgSparseFullAxes.value) { + fRegistry.fill(HIST("Pair/mix/hs"), v12.M(), v12.Pt(), ep2, occupancy, collision.posZ(), weight); + } else { + fRegistry.fill(HIST("Pair/mix/hs"), v12.M(), v12.Pt(), weight); + } } } } // end of loop over mixed event pool @@ -1101,7 +1132,7 @@ struct Pi0EtaToGammaGamma { continue; } - auto photons1_from_event_pool = emh1->GetTracksPerCollision(mix_dfId_collisionId); + const auto& photons1_from_event_pool = emh1->GetTracksPerCollision(mix_dfId_collisionId); // LOGF(info, "Do event mixing: current event (%d, %d), nll = %d | event pool (%d, %d), ngamma = %d", ndf, collision.globalIndex(), selected_photons2_in_this_event.size(), mix_dfId, mix_collisionId, photons1_from_event_pool.size()); for (const auto& g1 : selected_photons2_in_this_event) { @@ -1115,7 +1146,11 @@ struct Pi0EtaToGammaGamma { if (std::fabs(v12.Rapidity()) > maxY) { continue; } - fRegistry.fill(HIST("Pair/mix/hs"), v12.M(), v12.Pt(), weight); + if (cfgSparseFullAxes.value) { + fRegistry.fill(HIST("Pair/mix/hs"), v12.M(), v12.Pt(), ep2, occupancy, collision.posZ(), weight); + } else { + fRegistry.fill(HIST("Pair/mix/hs"), v12.M(), v12.Pt(), weight); + } } } } // end of loop over mixed event pool @@ -1139,7 +1174,7 @@ struct Pi0EtaToGammaGamma { o2::framework::expressions::Filter prefilter_primaryelectron = ifnode(dileptoncuts.cfg_apply_cuts_from_prefilter_derived.node(), o2::aod::emprimaryelectron::pfbderived == static_cast(0), true); int ndf = 0; - void processAnalysis(o2::soa::Filtered> const& collisions, Types const&... args) + void processAnalysis(o2::soa::Filtered> const& collisions, Types const&... args) { // LOGF(info, "ndf = %d", ndf); if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPCM) { @@ -1176,7 +1211,7 @@ struct Pi0EtaToGammaGamma { PROCESS_SWITCH(Pi0EtaToGammaGamma, processAnalysis, "process pair analysis", true); // using FilteredMyCollisionsWithJJMC = o2::soa::Filtered, o2::aod::EMEventsWeight>>; - void processAnalysisJJMC(o2::soa::Filtered> const& collisions, Types const&... args) + void processAnalysisJJMC(o2::soa::Filtered> const& collisions, Types const&... args) { // LOGF(info, "ndf = %d", ndf); if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPCM) { diff --git a/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGammaMC.h b/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGammaMC.h index 79c7b4308ae..989196fd278 100644 --- a/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGammaMC.h +++ b/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGammaMC.h @@ -25,7 +25,6 @@ #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" #include "PWGEM/PhotonMeson/Utils/MCUtilities.h" #include "PWGEM/PhotonMeson/Utils/NMHistograms.h" #include "PWGEM/PhotonMeson/Utils/PairUtilities.h" @@ -59,7 +58,6 @@ #include #include #include -#include #include #include @@ -89,6 +87,9 @@ struct Pi0EtaToGammaGammaMC { o2::framework::Configurable fd_k0s_to_pi0{"fd_k0s_pi0", "1.0", "feed down correction to pi0"}; o2::framework::Configurable cfgRequireTrueAssociation{"cfgRequireTrueAssociation", false, "flag to require true mc collision association"}; + o2::framework::Configurable cfgNbinsMass{"cfgNbinsMass", 400, "number of bins for the mass axis in same and mixed events"}; + o2::framework::Configurable> cfgMassBinRange{"cfgMassBinRange", {0., 0.8}, "range for the mass-axis in GeV/c2"}; + o2::framework::Configurable cfgAlphaMesonCut{"cfgAlphaMesonCut", 0, "flag for photon energy asymmetry distribution cut: 0: no cut, 1: cut specific value, 2: cut depending on pT"}; o2::framework::Configurable cfgAlphaMeson{"cfgAlphaMeson", 0.65, "photon energy asymmetry distribution parameter for specific value cut"}; o2::framework::Configurable cfgAlphaMesonA{"cfgAlphaMesonA", 0.65, "photon energy asymmetry distribution parameter A for pT dependent cut (A * tanh(B*pT))"}; @@ -274,13 +275,13 @@ struct Pi0EtaToGammaGammaMC { void init(o2::framework::InitContext&) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(&fRegistry); + DefineEMEventCut(); + fEMEventCut.addEventHistograms(&fRegistry); if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMDalitzEE) { - o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, true, "ee#gamma"); + o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, true, "ee#gamma", false, {}, {}, {}, cfgNbinsMass, {cfgMassBinRange->at(0), cfgMassBinRange->at(1)}); } else { - o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, true, "#gamma#gamma"); + o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, true, "#gamma#gamma", false, {}, {}, {}, cfgNbinsMass, {cfgMassBinRange->at(0), cfgMassBinRange->at(1)}); } - DefineEMEventCut(); DefinePCMCut(); mPhotonClassSelA = o2::aod::pwgem::photonmeson::utils::pairutil::buildV0PhotonClassSelection(photonclassA); mPhotonClassSelB = o2::aod::pwgem::photonmeson::utils::pairutil::buildV0PhotonClassSelection(photonclassB); @@ -288,7 +289,7 @@ struct Pi0EtaToGammaGammaMC { DefineEMCCut(); DefinePHOSCut(); - f1fd_k0s_to_pi0 = new TF1("f1fd_k0s_to_pi0", TString(fd_k0s_to_pi0), 0.f, 100.f); + f1fd_k0s_to_pi0 = new TF1("f1fd_k0s_to_pi0", fd_k0s_to_pi0.value.c_str(), 0.f, 100.f); fRegistry.add("Event/hNrecPerMCCollision", "Nrec per mc collision;N_{rec} collisions per MC collision", o2::framework::HistType::kTH1F, {{21, -0.5f, 20.5f}}, false); if (cfgGGContaCheck) { @@ -326,9 +327,14 @@ struct Pi0EtaToGammaGammaMC { return; } - o2::base::Propagator::initFieldFromGRP(&collision.grpMagField()); - // Fetch magnetic field from ccdb for current collision - d_bz = collision.grpMagField().getNominalL3Field(); + // o2::base::Propagator::initFieldFromGRP(&collision.grpMagField()); + // // Fetch magnetic field from ccdb for current collision + // d_bz = collision.grpMagField().getNominalL3Field(); + auto* grpmag = ccdb->getForRun("GLO/Config/GRPMagField", collision.runNumber()); + if (grpmag == nullptr) { + LOGF(fatal, "Could not retrieve GRPMagField for run %d", collision.runNumber()); + } + d_bz = grpmag->getNominalL3Field(); LOG(info) << "Retrieved GRP for timestamp " << collision.timestamp() << " with magnetic field of " << d_bz << " kZG"; fV0PhotonCut.SetD_Bz(d_bz); } @@ -352,6 +358,8 @@ struct Pi0EtaToGammaGammaMC { fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + fEMEventCut.SetTrackOccupancyRange(eventcuts.cfgTrackOccupancyMin, eventcuts.cfgTrackOccupancyMax); + fEMEventCut.SetFT0COccupancyRange(eventcuts.cfgFT0COccupancyMin, eventcuts.cfgFT0COccupancyMax); } void DefinePCMCut() @@ -474,8 +482,8 @@ struct Pi0EtaToGammaGammaMC { o2::framework::PresliceOptional perEMCClusterMT = o2::aod::mintm::minClusterId; o2::framework::PresliceOptional perEMCClusterMS = o2::aod::mintm::minClusterId; - o2::framework::Partition>> positrons = o2::aod::emprimaryelectron::sign > int8_t(0) && dileptoncuts.cfg_min_pt_track < o2::aod::track::pt&& nabs(o2::aod::track::eta) < dileptoncuts.cfg_max_eta_track; - o2::framework::Partition>> electrons = o2::aod::emprimaryelectron::sign < int8_t(0) && dileptoncuts.cfg_min_pt_track < o2::aod::track::pt && nabs(o2::aod::track::eta) < dileptoncuts.cfg_max_eta_track; + o2::framework::Partition>> positrons = o2::aod::emprimaryelectron::sign > static_cast(0) && dileptoncuts.cfg_min_pt_track < o2::aod::track::pt&& nabs(o2::aod::track::eta) < dileptoncuts.cfg_max_eta_track; + o2::framework::Partition>> electrons = o2::aod::emprimaryelectron::sign < static_cast(0) && dileptoncuts.cfg_min_pt_track < o2::aod::track::pt && nabs(o2::aod::track::eta) < dileptoncuts.cfg_max_eta_track; //--------------------------------------------------------------------------- // In the following are tags defined which help to select the correct preslice and cuts @@ -623,16 +631,15 @@ struct Pi0EtaToGammaGammaMC { continue; } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(&fRegistry, collision, weight); + fEMEventCut.fillEventInfo<0>(&fRegistry, collision, weight); if (!fEMEventCut.IsSelected(collision)) { continue; } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(&fRegistry, collision, weight); - fRegistry.fill(HIST("Event/before/hCollisionCounter"), 12.0, weight); // accepted - fRegistry.fill(HIST("Event/after/hCollisionCounter"), 12.0, weight); // accepted + fEMEventCut.fillEventInfo<1>(&fRegistry, collision, weight); + fEMEventCut.fillAccepted(&fRegistry, weight); int photonid1 = -1, photonid2 = -1, pi0id = -1, etaid = -1; - if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPCM || pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPHOSPHOS || pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kEMCEMC) { // same kinds pairing + if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPCM || pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPHOSPHOS || pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kEMCEMC || pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMEMC) { // same kinds pairing and PCM-EMC auto photons1_per_collision = photons1.sliceByCached(TDetectorTag1::perCollision(), collision.globalIndex(), cache); auto photons2_per_collision = photons2.sliceByCached(TDetectorTag2::perCollision(), collision.globalIndex(), cache); @@ -647,7 +654,7 @@ struct Pi0EtaToGammaGammaMC { continue; } } else { - if (!TDetectorTag1::applyCut(*this, g1) || !TDetectorTag2::applyCut(*this, g2)) { + if (!TDetectorTag1::applyCut(*this, g1)) { // g2 is checked below with its own tag (and matched tracks for EMC) continue; } } @@ -691,6 +698,20 @@ struct Pi0EtaToGammaGammaMC { photonid1 = o2::aod::pwgem::photonmeson::utils::mcutil::FindMotherInChain(cluster1mcparticle, mcparticles, std::vector{111, 221}); photonid2 = o2::aod::pwgem::photonmeson::utils::mcutil::FindMotherInChain(cluster2mcparticle, mcparticles, std::vector{111, 221}); + } else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMEMC) { + // PCM: true conversion as for PCM-PCM + auto pos1 = g1.template posTrack_as(); + auto ele1 = g1.template negTrack_as(); + auto pos1mc = pos1.template emmcparticle_as(); + auto ele1mc = ele1.template emmcparticle_as(); + photonid1 = o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(pos1mc, ele1mc, -11, 11, 22, mcparticles); + + // EMC: photon below the pi0/eta of the leading contributor as for EMC-EMC + if (g2.emmcparticleIds().empty()) { + continue; // noise cluster without MC label + } + auto cluster2mcparticle = mcparticles.iteratorAt(g2.emmcparticleIds()[0]); + photonid2 = o2::aod::pwgem::photonmeson::utils::mcutil::FindMotherInChain(cluster2mcparticle, mcparticles, std::vector{111, 221}); } else { photonid1 = -1; photonid2 = -1; @@ -706,6 +727,10 @@ struct Pi0EtaToGammaGammaMC { if (!o2::aod::pwgem::photonmeson::utils::mcutil::IsConversionPointInAcceptance(g1mc, maxRgen, maxY_gen, margin_z_mc, mcparticles) || !o2::aod::pwgem::photonmeson::utils::mcutil::IsConversionPointInAcceptance(g2mc, maxRgen, maxY_gen, margin_z_mc, mcparticles)) { continue; } + } else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMEMC) { + if (!o2::aod::pwgem::photonmeson::utils::mcutil::IsConversionPointInAcceptance(g1mc, maxRgen, maxY_gen, margin_z_mc, mcparticles)) { + continue; + } } pi0id = o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(g1mc, g2mc, 22, 22, 111, mcparticles); @@ -1005,7 +1030,7 @@ struct Pi0EtaToGammaGammaMC { o2::framework::expressions::Filter prefilter_pcm = ifnode(pcmcuts.cfg_apply_cuts_from_prefilter_derived.node(), o2::aod::v0photonkf::pfbderived == static_cast(0), true); o2::framework::expressions::Filter prefilter_primaryelectron = ifnode(dileptoncuts.cfg_apply_cuts_from_prefilter_derived.node(), o2::aod::emprimaryelectron::pfbderived == static_cast(0), true); - void processAnalysis(o2::soa::Filtered> const& collisions, o2::soa::Join const& mccollisions, o2::aod::EMMCParticles const& mcparticles, Types const&... args) + void processAnalysis(o2::soa::Filtered> const& collisions, o2::soa::Join const& mccollisions, o2::aod::EMMCParticles const& mcparticles, Types const&... args) { if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPCM) { auto&& [v0photons, v0legs] = std::forward_as_tuple(args...); @@ -1019,19 +1044,17 @@ struct Pi0EtaToGammaGammaMC { auto&& [emcclusters, emcMatchedTracks, emcMatchedSecondaries] = std::forward_as_tuple(args...); runTruePairing(collisions, emcclusters, emcclusters, mccollisions, mcparticles, nullptr, emcMatchedTracks, emcMatchedSecondaries); runGenInfo(collisions, mccollisions, mcparticles); + } else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMEMC) { + auto&& [v0photons, emcclusters, v0legs, emcMatchedTracks, emcMatchedSecondaries] = std::forward_as_tuple(args...); + runTruePairing(collisions, v0photons, emcclusters, mccollisions, mcparticles, v0legs, emcMatchedTracks, emcMatchedSecondaries); + runGenInfo(collisions, mccollisions, mcparticles); } // else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPHOSPHOS) { // auto phosclusters = std::get<0>(std::tie(args...)); // runPairing(collisions, phosclusters, phosclusters, nullptr, nullptr, perCollision_phos, perCollision_phos, fPHOSCut, fPHOSCut, nullptr, nullptr); // } - // else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMEMC) { - // auto v0photons = std::get<0>(std::tie(args...)); - // auto v0legs = std::get<1>(std::tie(args...)); - // auto emcclusters = std::get<2>(std::tie(args...)); - // auto emcmatchedtracks = std::get<3>(std::tie(args...)); - // runPairing(collisions, v0photons, emcclusters, v0legs, nullptr, perCollision_pcm, perCollision_emc, fV0PhotonCut, fEMCCut, emcmatchedtracks, nullptr); - // } else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPHOS) { + // else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPHOS) { // auto v0photons = std::get<0>(std::tie(args...)); // auto v0legs = std::get<1>(std::tie(args...)); // auto phosclusters = std::get<2>(std::tie(args...)); @@ -1041,7 +1064,7 @@ struct Pi0EtaToGammaGammaMC { PROCESS_SWITCH(Pi0EtaToGammaGammaMC, processAnalysis, "process pair analysis", true); // using FilteredMyCollisionsWithJJMC = o2::soa::Filtered, o2::aod::EMEventsWeight>>; - void processAnalysisJJMC(o2::soa::Filtered> const& collisions, o2::soa::Join const& mccollisions, o2::aod::EMMCParticles const& mcparticles, Types const&... args) + void processAnalysisJJMC(o2::soa::Filtered> const& collisions, o2::soa::Join const& mccollisions, o2::aod::EMMCParticles const& mcparticles, Types const&... args) { if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPCM) { auto&& [v0photons, v0legs] = std::forward_as_tuple(args...); @@ -1065,19 +1088,17 @@ struct Pi0EtaToGammaGammaMC { // runTruePairing(collisions, emcclusters, emcclusters, nullptr, nullptr, perCollision_emc, perCollision_emc, fEMCCut, fEMCCut, mccollisions, mcparticles); runTruePairing(collisions, emcclusters, emcclusters, mccollisions, mcparticles, nullptr, emcMatchedTracks, emcMatchedSecondaries); runGenInfo(collisions, mccollisions, mcparticles); + } else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMEMC) { + auto&& [v0photons, emcclusters, v0legs, emcMatchedTracks, emcMatchedSecondaries] = std::forward_as_tuple(args...); + runTruePairing(collisions, v0photons, emcclusters, mccollisions, mcparticles, v0legs, emcMatchedTracks, emcMatchedSecondaries); + runGenInfo(collisions, mccollisions, mcparticles); } // else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPHOSPHOS) { // auto phosclusters = std::get<0>(std::tie(args...)); // runPairing(collisions, phosclusters, phosclusters, nullptr, nullptr, perCollision_phos, perCollision_phos, fPHOSCut, fPHOSCut, nullptr, nullptr); // } - // else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMEMC) { - // auto v0photons = std::get<0>(std::tie(args...)); - // auto v0legs = std::get<1>(std::tie(args...)); - // auto emcclusters = std::get<2>(std::tie(args...)); - // auto emcmatchedtracks = std::get<3>(std::tie(args...)); - // runPairing(collisions, v0photons, emcclusters, v0legs, nullptr, perCollision_pcm, perCollision_emc, fV0PhotonCut, fEMCCut, emcmatchedtracks, nullptr); - // } else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPHOS) { + // else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPHOS) { // auto v0photons = std::get<0>(std::tie(args...)); // auto v0legs = std::get<1>(std::tie(args...)); // auto phosclusters = std::get<2>(std::tie(args...)); diff --git a/PWGEM/PhotonMeson/Core/TaggingPi0.h b/PWGEM/PhotonMeson/Core/TaggingPi0.h index e81d5e3ba9c..c058fb110ff 100644 --- a/PWGEM/PhotonMeson/Core/TaggingPi0.h +++ b/PWGEM/PhotonMeson/Core/TaggingPi0.h @@ -23,7 +23,6 @@ #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" #include "PWGEM/PhotonMeson/Utils/EMPhoton.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" #include "PWGEM/PhotonMeson/Utils/PairUtilities.h" // #include "PWGEM/Dilepton/Utils/EventMixingHandler.h" @@ -244,10 +243,10 @@ struct TaggingPi0 { emh1 = new MyEMH(ndepth); emh2 = new MyEMH(ndepth); - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(&fRegistry); + DefineEMEventCut(); + fEMEventCut.addEventHistograms(&fRegistry); addHistogrms(); - DefineEMEventCut(); DefinePCMCut(); DefineDileptonCut(); DefineEMCCut(); @@ -336,6 +335,8 @@ struct TaggingPi0 { fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + fEMEventCut.SetTrackOccupancyRange(eventcuts.cfgTrackOccupancyMin, eventcuts.cfgTrackOccupancyMax); + fEMEventCut.SetFT0COccupancyRange(eventcuts.cfgFT0COccupancyMin, eventcuts.cfgFT0COccupancyMax); } void DefinePCMCut() @@ -429,8 +430,8 @@ struct TaggingPi0 { // o2::framework::Preslice perCollision_phos = o2::aod::phoscluster::pmeventId; o2::framework::Preslice perCollision_electron = o2::aod::emprimaryelectronda::pmeventId; - o2::framework::Partition positrons = o2::aod::emprimaryelectron::sign > int8_t(0) && static_cast(dileptoncuts.cfg_min_pt_track) < o2::aod::track::pt&& nabs(o2::aod::track::eta) < static_cast(dileptoncuts.cfg_max_eta_track) && static_cast(dileptoncuts.cfg_min_TPCNsigmaEl) < o2::aod::pidtpc::tpcNSigmaEl&& o2::aod::pidtpc::tpcNSigmaEl < static_cast(dileptoncuts.cfg_max_TPCNsigmaEl); - o2::framework::Partition electrons = o2::aod::emprimaryelectron::sign < int8_t(0) && static_cast(dileptoncuts.cfg_min_pt_track) < o2::aod::track::pt && nabs(o2::aod::track::eta) < static_cast(dileptoncuts.cfg_max_eta_track) && static_cast(dileptoncuts.cfg_min_TPCNsigmaEl) < o2::aod::pidtpc::tpcNSigmaEl && o2::aod::pidtpc::tpcNSigmaEl < static_cast(dileptoncuts.cfg_max_TPCNsigmaEl); + o2::framework::Partition positrons = o2::aod::emprimaryelectron::sign > static_cast(0) && static_cast(dileptoncuts.cfg_min_pt_track) < o2::aod::track::pt&& nabs(o2::aod::track::eta) < static_cast(dileptoncuts.cfg_max_eta_track) && static_cast(dileptoncuts.cfg_min_TPCNsigmaEl) < o2::aod::pidtpc::tpcNSigmaEl&& o2::aod::pidtpc::tpcNSigmaEl < static_cast(dileptoncuts.cfg_max_TPCNsigmaEl); + o2::framework::Partition electrons = o2::aod::emprimaryelectron::sign < static_cast(0) && static_cast(dileptoncuts.cfg_min_pt_track) < o2::aod::track::pt && nabs(o2::aod::track::eta) < static_cast(dileptoncuts.cfg_max_eta_track) && static_cast(dileptoncuts.cfg_min_TPCNsigmaEl) < o2::aod::pidtpc::tpcNSigmaEl && o2::aod::pidtpc::tpcNSigmaEl < static_cast(dileptoncuts.cfg_max_TPCNsigmaEl); MyEMH* emh1 = nullptr; MyEMH* emh2 = nullptr; @@ -463,13 +464,12 @@ struct TaggingPi0 { continue; } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(&fRegistry, collision, weight); + fEMEventCut.fillEventInfo<0>(&fRegistry, collision, weight); if (!fEMEventCut.IsSelected(collision)) { continue; } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(&fRegistry, collision, weight); - fRegistry.fill(HIST("Event/before/hCollisionCounter"), 12.0, weight); // accepted - fRegistry.fill(HIST("Event/after/hCollisionCounter"), 12.0, weight); // accepted + fEMEventCut.fillEventInfo<1>(&fRegistry, collision, weight); + fEMEventCut.fillAccepted(&fRegistry, weight); int zbin = lower_bound(zvtx_bin_edges.begin(), zvtx_bin_edges.end(), collision.posZ()) - zvtx_bin_edges.begin() - 1; if (zbin < 0) { @@ -599,11 +599,11 @@ struct TaggingPi0 { } // make a vector of selected photons in this collision. - auto selected_photons1_in_this_event = emh1->GetTracksPerCollision(key_df_collision); - // auto selected_photons2_in_this_event = emh2->GetTracksPerCollision(key_df_collision); + const auto& selected_photons1_in_this_event = emh1->GetTracksPerCollision(key_df_collision); + // const auto& selected_photons2_in_this_event = emh2->GetTracksPerCollision(key_df_collision); - // auto collisionIds1_in_mixing_pool = emh1->GetCollisionIdsFromEventPool(key_bin); - auto collisionIds2_in_mixing_pool = emh2->GetCollisionIdsFromEventPool(key_bin); + // const auto& collisionIds1_in_mixing_pool = emh1->GetCollisionIdsFromEventPool(key_bin); + const auto& collisionIds2_in_mixing_pool = emh2->GetCollisionIdsFromEventPool(key_bin); for (const auto& mix_dfId_collisionId : collisionIds2_in_mixing_pool) { int mix_dfId = mix_dfId_collisionId.first; @@ -620,7 +620,7 @@ struct TaggingPi0 { continue; } - auto photons2_from_event_pool = emh2->GetTracksPerCollision(mix_dfId_collisionId); + const auto& photons2_from_event_pool = emh2->GetTracksPerCollision(mix_dfId_collisionId); // LOGF(info, "Do event mixing: current event (%d, %d), ngamma = %d | event pool (%d, %d), nll = %d", ndf, collision.globalIndex(), selected_photons1_in_this_event.size(), mix_dfId, mix_collisionId, photons2_from_event_pool.size()); for (const auto& g1 : selected_photons1_in_this_event) { // [photon from event1, dilepton from event2] diff --git a/PWGEM/PhotonMeson/Core/TaggingPi0MC.h b/PWGEM/PhotonMeson/Core/TaggingPi0MC.h index 4588f70182d..9173ac293cf 100644 --- a/PWGEM/PhotonMeson/Core/TaggingPi0MC.h +++ b/PWGEM/PhotonMeson/Core/TaggingPi0MC.h @@ -23,7 +23,6 @@ #include "PWGEM/PhotonMeson/Core/V0PhotonCut.h" #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" #include "PWGEM/PhotonMeson/Utils/MCUtilities.h" #include "PWGEM/PhotonMeson/Utils/PairUtilities.h" // @@ -55,11 +54,11 @@ #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include #include -#include #include #include #include +#include #include #include #include @@ -221,9 +220,9 @@ struct TaggingPi0MC { void init(o2::framework::InitContext&) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(&fRegistry); - addHistogrms(); DefineEMEventCut(); + fEMEventCut.addEventHistograms(&fRegistry); + addHistogrms(); DefinePCMCut(); DefineDileptonCut(); DefineEMCCut(); @@ -231,7 +230,7 @@ struct TaggingPi0MC { mRunNumber = 0; d_bz = 0; - f1fd_k0s_to_pi0 = new TF1("f1fd_k0s_to_pi0", TString(fd_k0s_to_pi0), 0.f, 100.f); + f1fd_k0s_to_pi0 = new TF1("f1fd_k0s_to_pi0", fd_k0s_to_pi0.value.c_str(), 0.f, 100.f); ccdb->setURL(ccdburl); ccdb->setCaching(true); @@ -275,14 +274,14 @@ struct TaggingPi0MC { void addHistogrms() { - TString mggTitle = "ee#gamma"; + std::string mggTitle = "ee#gamma"; if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMDalitzEE) { mggTitle = "ee#gamma"; } else { mggTitle = "#gamma#gamma"; } - const o2::framework::AxisSpec axis_m{200, 0, 0.4, Form("m_{%s} (GeV/c^{2})", mggTitle.Data())}; + const o2::framework::AxisSpec axis_m{200, 0, 0.4, std::format("m_{} (GeV/c^{{2}})", mggTitle)}; const o2::framework::AxisSpec axis_pt{ConfPtBins, "p_{T,#gamma} (GeV/c)"}; fRegistry.add("Photon/candidate/hPt", "photon candidates;p_{T,#gamma} (GeV/c)", o2::framework::HistType::kTH1D, {axis_pt}, true); // for purity @@ -311,6 +310,8 @@ struct TaggingPi0MC { fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + fEMEventCut.SetTrackOccupancyRange(eventcuts.cfgTrackOccupancyMin, eventcuts.cfgTrackOccupancyMax); + fEMEventCut.SetFT0COccupancyRange(eventcuts.cfgFT0COccupancyMin, eventcuts.cfgFT0COccupancyMax); } void DefinePCMCut() @@ -404,8 +405,8 @@ struct TaggingPi0MC { // o2::framework::Preslice perCollision_phos = o2::aod::phoscluster::pmeventId; o2::framework::Preslice perCollision_electron = o2::aod::emprimaryelectronda::pmeventId; - o2::framework::Partition positrons = o2::aod::emprimaryelectron::sign > int8_t(0) && static_cast(dileptoncuts.cfg_min_pt_track) < o2::aod::track::pt&& nabs(o2::aod::track::eta) < static_cast(dileptoncuts.cfg_max_eta_track) && static_cast(dileptoncuts.cfg_min_TPCNsigmaEl) < o2::aod::pidtpc::tpcNSigmaEl&& o2::aod::pidtpc::tpcNSigmaEl < static_cast(dileptoncuts.cfg_max_TPCNsigmaEl); - o2::framework::Partition electrons = o2::aod::emprimaryelectron::sign < int8_t(0) && static_cast(dileptoncuts.cfg_min_pt_track) < o2::aod::track::pt && nabs(o2::aod::track::eta) < static_cast(dileptoncuts.cfg_max_eta_track) && static_cast(dileptoncuts.cfg_min_TPCNsigmaEl) < o2::aod::pidtpc::tpcNSigmaEl && o2::aod::pidtpc::tpcNSigmaEl < static_cast(dileptoncuts.cfg_max_TPCNsigmaEl); + o2::framework::Partition positrons = o2::aod::emprimaryelectron::sign > static_cast(0) && static_cast(dileptoncuts.cfg_min_pt_track) < o2::aod::track::pt&& nabs(o2::aod::track::eta) < static_cast(dileptoncuts.cfg_max_eta_track) && static_cast(dileptoncuts.cfg_min_TPCNsigmaEl) < o2::aod::pidtpc::tpcNSigmaEl&& o2::aod::pidtpc::tpcNSigmaEl < static_cast(dileptoncuts.cfg_max_TPCNsigmaEl); + o2::framework::Partition electrons = o2::aod::emprimaryelectron::sign < static_cast(0) && static_cast(dileptoncuts.cfg_min_pt_track) < o2::aod::track::pt && nabs(o2::aod::track::eta) < static_cast(dileptoncuts.cfg_max_eta_track) && static_cast(dileptoncuts.cfg_min_TPCNsigmaEl) < o2::aod::pidtpc::tpcNSigmaEl && o2::aod::pidtpc::tpcNSigmaEl < static_cast(dileptoncuts.cfg_max_TPCNsigmaEl); template void runTruePairing(TCollisions const& collisions, @@ -432,13 +433,12 @@ struct TaggingPi0MC { continue; } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(&fRegistry, collision, weight); + fEMEventCut.fillEventInfo<0>(&fRegistry, collision, weight); if (!fEMEventCut.IsSelected(collision)) { continue; } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(&fRegistry, collision, weight); - fRegistry.fill(HIST("Event/before/hCollisionCounter"), 12.0, weight); // accepted - fRegistry.fill(HIST("Event/after/hCollisionCounter"), 12.0, weight); // accepted + fEMEventCut.fillEventInfo<1>(&fRegistry, collision, weight); + fEMEventCut.fillAccepted(&fRegistry, weight); if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMDalitzEE) { auto photons1_per_collision = photons1.sliceBy(perCollision1, collision.globalIndex()); diff --git a/PWGEM/PhotonMeson/Core/V0PhotonCut.h b/PWGEM/PhotonMeson/Core/V0PhotonCut.h index 1921a71c79b..d9c442f499b 100644 --- a/PWGEM/PhotonMeson/Core/V0PhotonCut.h +++ b/PWGEM/PhotonMeson/Core/V0PhotonCut.h @@ -246,8 +246,10 @@ class V0PhotonCut : public TNamed fRegistry->add("QA/V0Photon/before/hConvZR", "Conversion point ZR", o2::framework::HistType::kTH2F, {thAxisConvZ, thAxisConvR}, true); fRegistry->add("QA/V0Photon/before/hChi2", "Chi2/ndf from KFParticle;#chi^{2}_{KF}/ndf;counts", o2::framework::HistType::kTH1D, {thAxisChi2}, true); - // TODO: add psi_pair once available - // fRegistry->add("QA/V0Photon/before/hPsiPair", "Psi pair;#Psi_{pair};counts", o2::framework::HistType::kTH1D, {thAxisPsiPair}, true); + // psi_pair (V0PhotonsPhiVPsi) and the KF chi2 split (V0PhotonsKFChi2), filled only if the photon table joins them + fRegistry->add("QA/V0Photon/before/hPsiPair", "Psi pair;#Psi_{pair};counts", o2::framework::HistType::kTH1D, {thAxisPsiPair}, true); + fRegistry->add("QA/V0Photon/before/hChi2Vtx", "KF chi2/ndf of the conversion-vertex fit;#chi^{2}_{vtx}/ndf;counts", o2::framework::HistType::kTH1D, {thAxisChi2}, true); + fRegistry->add("QA/V0Photon/before/hChi2PV", "KF chi2 to the primary vertex;#chi^{2}_{PV};counts", o2::framework::HistType::kTH1D, {{100, 0., 200.}}, true); fRegistry->add("QA/V0Photon/before/Pos/NSigmaE", "NSigmaE of pos leg vs momentum", o2::framework::HistType::kTH2F, {thAxisMomentum, thAxisNSigmaE}, true); fRegistry->add("QA/V0Photon/before/Pos/NSigmaPi", "NSigmaE of pos leg vs momentum", o2::framework::HistType::kTH2F, {thAxisMomentum, thAxisNSigmaPi}, true); @@ -316,8 +318,13 @@ class V0PhotonCut : public TNamed fRegistry->fill(HIST("QA/V0Photon/before/hConvZR"), v0.vz(), v0.v0radius()); fRegistry->fill(HIST("QA/V0Photon/before/hChi2"), v0.chiSquareNDF()); - // TODO: add psi_pair once available - // fRegistry->fill(HIST("QA/V0Photon/before/hPsiPair"), v0.psiPair()); + if constexpr (requires { v0.psipair(); }) { + fRegistry->fill(HIST("QA/V0Photon/before/hPsiPair"), v0.psipair()); + } + if constexpr (requires { v0.chi2PV(); }) { + fRegistry->fill(HIST("QA/V0Photon/before/hChi2Vtx"), v0.chi2NDFVtx()); + fRegistry->fill(HIST("QA/V0Photon/before/hChi2PV"), v0.chi2PV()); + } fRegistry->fill(HIST("QA/V0Photon/before/Pos/NSigmaE"), pos.p(), pos.tpcNSigmaEl()); fRegistry->fill(HIST("QA/V0Photon/before/Pos/NSigmaPi"), pos.p(), pos.tpcNSigmaPi()); @@ -346,8 +353,13 @@ class V0PhotonCut : public TNamed fRegistry->fill(HIST("QA/V0Photon/after/hConvZR"), v0.vz(), v0.v0radius()); fRegistry->fill(HIST("QA/V0Photon/after/hChi2"), v0.chiSquareNDF()); - // TODO: add psi_pair once available - // fRegistry->fill(HIST("QA/V0Photon/after/hPsiPair"), v0.psiPair()); + if constexpr (requires { v0.psipair(); }) { + fRegistry->fill(HIST("QA/V0Photon/after/hPsiPair"), v0.psipair()); + } + if constexpr (requires { v0.chi2PV(); }) { + fRegistry->fill(HIST("QA/V0Photon/after/hChi2Vtx"), v0.chi2NDFVtx()); + fRegistry->fill(HIST("QA/V0Photon/after/hChi2PV"), v0.chi2PV()); + } fRegistry->fill(HIST("QA/V0Photon/after/Pos/NSigmaE"), pos.p(), pos.tpcNSigmaEl()); fRegistry->fill(HIST("QA/V0Photon/after/Pos/NSigmaPi"), pos.p(), pos.tpcNSigmaPi()); diff --git a/PWGEM/PhotonMeson/DataModel/EventTables.h b/PWGEM/PhotonMeson/DataModel/EventTables.h index 93630002fc5..dd24a33457b 100644 --- a/PWGEM/PhotonMeson/DataModel/EventTables.h +++ b/PWGEM/PhotonMeson/DataModel/EventTables.h @@ -23,12 +23,13 @@ #include #include -#include // IWYU pragma: keep -#include // IWYU pragma: keep +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) #include // for BIT #include +#include #include namespace o2::aod @@ -61,6 +62,23 @@ enum EventAcceptanceBits { kSize }; +const std::vector vecEventAccBitNames = { + "all", + "has MC coll", + "good zVtx", + "is FT0AND", + "No TFB", + "ITS ROFB", + "Same bunch Pileup", + "ZVtxFT0PV", + "No coll in time range", + "good track occi", + "Good FT0 occupancy", + "kTVXinEMC", + "Good centrality", + "Good RCT", + "Good Sel8"}; + } // namespace pmevsel namespace pmevent diff --git a/PWGEM/PhotonMeson/DataModel/bcWiseTables.h b/PWGEM/PhotonMeson/DataModel/bcWiseTables.h index e1effd5871f..d2eed4b2f58 100644 --- a/PWGEM/PhotonMeson/DataModel/bcWiseTables.h +++ b/PWGEM/PhotonMeson/DataModel/bcWiseTables.h @@ -19,8 +19,8 @@ #include #include -#include // IWYU pragma: keep -#include // IWYU pragma: keep +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) #include #include diff --git a/PWGEM/PhotonMeson/DataModel/gammaTables.h b/PWGEM/PhotonMeson/DataModel/gammaTables.h index f0e0bdf8d85..454bcea9740 100644 --- a/PWGEM/PhotonMeson/DataModel/gammaTables.h +++ b/PWGEM/PhotonMeson/DataModel/gammaTables.h @@ -292,6 +292,31 @@ DECLARE_SOA_TABLE_VERSIONED(V0LegsDeDxMCTmp, "AOD", "V0LEGDEDXMCTMP", 0, mcpidtp // iterators using V0LegDeDxMCTmp = V0LegsDeDxMCTmp::iterator; +namespace v0legcov +{ +DECLARE_SOA_COLUMN(X, x, float); //! x of the leg parameters given to the V0 fit (innermost update, cm) +DECLARE_SOA_COLUMN(Alpha, alpha, float); //! frame angle +DECLARE_SOA_COLUMN(Y, y, float); //! local y (cm) +DECLARE_SOA_COLUMN(Z, z, float); //! z (cm), TPC-only legs after the drift correction to the V0's collision +DECLARE_SOA_COLUMN(Snp, snp, float); //! sin(phi) in the local frame +DECLARE_SOA_COLUMN(Tgl, tgl, float); //! tan(lambda) +DECLARE_SOA_COLUMN(Signed1Pt, signed1Pt, float); //! q/pT (c/GeV) +DECLARE_SOA_COLUMN(Cov, cov, float[15]); // NOLINT(modernize-avoid-c-arrays) //! covariance matrix, o2::track::TrackParCov order +} // namespace v0legcov + +// leg track parameters as used in the V0 fit, one row per V0Legs row (optional builder output, joinable with V0Legs) +DECLARE_SOA_TABLE_VERSIONED(V0LegsCov_000, "AOD", "V0LEGCOV", 0, //! + v0legcov::X, v0legcov::Alpha, v0legcov::Y, v0legcov::Z, v0legcov::Snp, v0legcov::Tgl, v0legcov::Signed1Pt, v0legcov::Cov); +using V0LegsCov = V0LegsCov_000; +// iterators +using V0LegCov = V0LegsCov::iterator; + +// copy of V0LegsCov table +DECLARE_SOA_TABLE_VERSIONED(V0LegsCovTmp, "AOD", "V0LEGCOVTMP", 0, //! + v0legcov::X, v0legcov::Alpha, v0legcov::Y, v0legcov::Z, v0legcov::Snp, v0legcov::Tgl, v0legcov::Signed1Pt, v0legcov::Cov); +// iterators +using V0LegCovTmp = V0LegsCovTmp::iterator; + namespace emevent { DECLARE_SOA_COLUMN(NgPCM, ngpcm, int); @@ -540,6 +565,17 @@ DECLARE_SOA_INDEX_COLUMN(PMEvent, pmevent); //! DECLARE_SOA_COLUMN(PhiV, phiv, float); //! DECLARE_SOA_COLUMN(PsiPair, psipair, float); } // namespace v0photonsphivpsi +// KF quality of the photon: the stored v0photonkf::ChiSquareNDF is the chi2/ndf AFTER the topological constraint to the PV; +// these two separate the conversion-vertex fit from the pointing to the PV (row-by-row extension of the photon table, join by index) +namespace v0photonkfchi2 +{ +DECLARE_SOA_COLUMN(Chi2NDFVtx, chi2NDFVtx, float); //! chi2/ndf of the two-leg conversion-vertex fit (incl. the mass constraint if applied), before the PV constraint +DECLARE_SOA_COLUMN(Chi2PV, chi2PV, float); //! chi2 deviation of the photon from the primary vertex (KFParticle::GetDeviationFromVertex) +} // namespace v0photonkfchi2 +DECLARE_SOA_TABLE(V0PhotonsKFChi2, "AOD", "V0PHKFCHI2", o2::soa::Index<>, v0photonkfchi2::Chi2NDFVtx, v0photonkfchi2::Chi2PV); //! +DECLARE_SOA_TABLE(V0PhotonsKFChi2Tmp, "AOD", "V0PHKFCHI2TMP", o2::soa::Index<>, v0photonkfchi2::Chi2NDFVtx, v0photonkfchi2::Chi2PV); //! +DECLARE_SOA_TABLE(V0PhotonsKFChi2Mix, "AOD", "V0PHKFCHI2MIX", o2::soa::Index<>, v0photonkfchi2::Chi2NDFVtx, v0photonkfchi2::Chi2PV); //! builder run 2 + DECLARE_SOA_TABLE(V0PhotonsPhiVPsi, "AOD", "V0PHOTONPHIVPSI", //! o2::soa::Index<>, v0photonsphivpsi::PhiV, v0photonsphivpsi::PsiPair); // iterators @@ -551,6 +587,217 @@ DECLARE_SOA_TABLE(V0PhotonsPhiVPsiTmp, "AOD", "V0PHOTONPHIVTMP", //! // iterators using V0PhotonsPhiVPsiTmp = V0PhotonsPhiVPsiTmp; +// co-reconstructed mixed event: artificial events made by photon-mixed-event-producer from the photons of builder run 1, +// rebuilt from their legs by builder run 2 (photon-conversion-builder-mixed) +namespace pcmmixcol +{ +DECLARE_SOA_COLUMN(IsMixed, isMixed, bool); //! false: the photons of one event, true: N photons from N different events +DECLARE_SOA_COLUMN(SourceCollisionId, sourceCollisionId, int); //! collision (this DF) whose vertex the artificial event has +DECLARE_SOA_COLUMN(SourceEventUid, sourceEventUid, int64_t); //! mixer event counter of that collision +DECLARE_SOA_COLUMN(ZBin, zBin, int); //! pool z bin +DECLARE_SOA_COLUMN(OccBin, occBin, int); //! pool occupancy bin +DECLARE_SOA_COLUMN(Occupancy, occupancy, float); //! occupancy of the source collision +DECLARE_SOA_COLUMN(CentFT0M, centFT0M, float); //! centralities of the source collision (V0 fit input) +DECLARE_SOA_COLUMN(CentFT0A, centFT0A, float); //! +DECLARE_SOA_COLUMN(CentFT0C, centFT0C, float); //! +DECLARE_SOA_COLUMN(FirstLeg, firstLeg, int); //! first PCMMixLegs row of this artificial event +DECLARE_SOA_COLUMN(NLegs, nLegs, int); //! number of PCMMixLegs rows (2 per photon, contiguous) +DECLARE_SOA_COLUMN(RunNumber, runNumber, int); //! run of the source collision (CCDB: B field, TPC drift velocity) +DECLARE_SOA_COLUMN(Timestamp, timestamp, uint64_t); //! CCDB timestamp of the source collision (builder run 2 on derived data: no BCs) +DECLARE_SOA_COLUMN(MultNTracksPVeta1, multNTracksPVeta1, int); //! PV contributors with |eta| < 1 of the source collision +DECLARE_SOA_COLUMN(MultFT0M, multFT0M, float); //! FT0A + FT0C amplitude of the source collision +} // namespace pcmmixcol +DECLARE_SOA_TABLE(PCMMixCollisions, "AOD", "PCMMIXCOL", //! + o2::soa::Index<>, collision::PosX, collision::PosY, collision::PosZ, + collision::CovXX, collision::CovXY, collision::CovXZ, collision::CovYY, collision::CovYZ, collision::CovZZ, + collision::Chi2, collision::NumContrib, + pcmmixcol::CentFT0M, pcmmixcol::CentFT0A, pcmmixcol::CentFT0C, + pcmmixcol::IsMixed, pcmmixcol::SourceCollisionId, pcmmixcol::SourceEventUid, + pcmmixcol::ZBin, pcmmixcol::OccBin, pcmmixcol::Occupancy, pcmmixcol::FirstLeg, pcmmixcol::NLegs, + pcmmixcol::RunNumber, pcmmixcol::MultNTracksPVeta1, pcmmixcol::MultFT0M, pcmmixcol::Timestamp); +using PCMMixCollision = PCMMixCollisions::iterator; + +namespace pcmmixleg +{ +DECLARE_SOA_COLUMN(CollisionId, collisionId, int); //! PCMMixCollisions row +DECLARE_SOA_COLUMN(Pt, pt, float); //! pT of the AO2D track (track-like interface for the builder) +DECLARE_SOA_COLUMN(Phi, phi, float); //! phi of the AO2D track +DECLARE_SOA_COLUMN(CYY, cYY, float); //! covariance, o2::track::TrackParCov order (from V0LegsCov) +DECLARE_SOA_COLUMN(CZY, cZY, float); //! +DECLARE_SOA_COLUMN(CZZ, cZZ, float); //! +DECLARE_SOA_COLUMN(CSnpY, cSnpY, float); //! +DECLARE_SOA_COLUMN(CSnpZ, cSnpZ, float); //! +DECLARE_SOA_COLUMN(CSnpSnp, cSnpSnp, float); //! +DECLARE_SOA_COLUMN(CTglY, cTglY, float); //! +DECLARE_SOA_COLUMN(CTglZ, cTglZ, float); //! +DECLARE_SOA_COLUMN(CTglSnp, cTglSnp, float); //! +DECLARE_SOA_COLUMN(CTglTgl, cTglTgl, float); //! +DECLARE_SOA_COLUMN(C1PtY, c1PtY, float); //! +DECLARE_SOA_COLUMN(C1PtZ, c1PtZ, float); //! +DECLARE_SOA_COLUMN(C1PtSnp, c1PtSnp, float); //! +DECLARE_SOA_COLUMN(C1PtTgl, c1PtTgl, float); //! +DECLARE_SOA_COLUMN(C1Pt21Pt2, c1Pt21Pt2, float); //! +DECLARE_SOA_COLUMN(SourceEventUid, sourceEventUid, int64_t); //! mixer event counter of the event the leg comes from +DECLARE_SOA_COLUMN(SourcePhotonUid, sourcePhotonUid, int64_t); //! mixer photon counter of the run-1 photon the leg belonged to +DECLARE_SOA_COLUMN(SourceV0Id, sourceV0Id, int); //! v0Id of that photon in run 1 (< 0: not an SVertexer V0) +DECLARE_SOA_COLUMN(SourcePx, sourcePx, float); //! run-1 photon momentum at the PV +DECLARE_SOA_COLUMN(SourcePy, sourcePy, float); //! +DECLARE_SOA_COLUMN(SourcePz, sourcePz, float); //! +DECLARE_SOA_COLUMN(SourceVx, sourceVx, float); //! run-1 conversion point +DECLARE_SOA_COLUMN(SourceVy, sourceVy, float); //! +DECLARE_SOA_COLUMN(SourceVz, sourceVz, float); //! +DECLARE_SOA_COLUMN(SourcePvX, sourcePvX, float); //! primary vertex of the source event (run 2: refit of the original at its own vertex) +DECLARE_SOA_COLUMN(SourcePvY, sourcePvY, float); //! +DECLARE_SOA_COLUMN(SourcePvZ, sourcePvZ, float); //! +DECLARE_SOA_COLUMN(SourcePvCovXX, sourcePvCovXX, float); //! +DECLARE_SOA_COLUMN(SourcePvCovXY, sourcePvCovXY, float); //! +DECLARE_SOA_COLUMN(SourcePvCovXZ, sourcePvCovXZ, float); //! +DECLARE_SOA_COLUMN(SourcePvCovYY, sourcePvCovYY, float); //! +DECLARE_SOA_COLUMN(SourcePvCovYZ, sourcePvCovYZ, float); //! +DECLARE_SOA_COLUMN(SourcePvCovZZ, sourcePvCovZZ, float); //! +DECLARE_SOA_COLUMN(SourcePvChi2, sourcePvChi2, float); //! +DECLARE_SOA_COLUMN(SourcePvNContrib, sourcePvNContrib, uint16_t); //! +DECLARE_SOA_COLUMN(OrigTrackId, origTrackId, int); //! AO2D track (DF of the source event) +DECLARE_SOA_COLUMN(McParticleId, mcParticleId, int); //! MC: particle of the leg (DF of the source event), -1 without +DECLARE_SOA_COLUMN(McMotherId, mcMotherId, int); //! MC: first mother (DF of the source event), -1 without +DECLARE_SOA_COLUMN(McPdg, mcPdg, int); //! MC: PDG of the leg +DECLARE_SOA_COLUMN(McMotherPdg, mcMotherPdg, int); //! MC: PDG of the mother +DECLARE_SOA_COLUMN(McMotherPx, mcMotherPx, float); //! MC: true momentum of the mother +DECLARE_SOA_COLUMN(McMotherPy, mcMotherPy, float); //! +DECLARE_SOA_COLUMN(McMotherPz, mcMotherPz, float); //! +DECLARE_SOA_COLUMN(McGrandMotherId, mcGrandMotherId, int); //! MC: first mother of the mother (DF of the source event), -1 without +DECLARE_SOA_COLUMN(McGrandMotherPdg, mcGrandMotherPdg, int); //! MC: PDG of the grandmother +DECLARE_SOA_COLUMN(McAncestorId, mcAncestorId, int); //! MC: first physical primary along the mothers of the leg's mother (itself if primary), -1 without +DECLARE_SOA_COLUMN(McGGMotherId, mcGGMotherId, int); //! MC: mother of the grandmother (e.g. the eta of eta -> 3 pi0), -1 without +DECLARE_SOA_COLUMN(McGGMotherPdg, mcGGMotherPdg, int); //! MC: PDG of that particle +DECLARE_SOA_COLUMN(McRootId, mcRootId, int); //! MC: last particle along the first mothers of the leg's mother (no mother), -1 without +DECLARE_SOA_COLUMN(McRootPdg, mcRootPdg, int); //! MC: PDG of the root +DECLARE_SOA_COLUMN(McRootDepth, mcRootDepth, int); //! MC: generations from the leg's mother to the root +DECLARE_SOA_COLUMN(McVx, mcVx, float); //! MC: production vertex of the leg = true conversion point (lab frame of the source event; translated with the photon), 0 without +DECLARE_SOA_COLUMN(McVy, mcVy, float); //! +DECLARE_SOA_COLUMN(McVz, mcVz, float); //! +} // namespace pcmmixleg +// one row per leg of an artificial event; the columns the builder reads from a track carry the same names as in the AO2D +DECLARE_SOA_TABLE(PCMMixLegs, "AOD", "PCMMIXLEG", //! + o2::soa::Index<>, pcmmixleg::CollisionId, v0leg::Sign, + track::X, track::Alpha, track::Y, track::Z, track::Snp, track::Tgl, track::Signed1Pt, pcmmixleg::Pt, pcmmixleg::Phi, + pcmmixleg::CYY, pcmmixleg::CZY, pcmmixleg::CZZ, pcmmixleg::CSnpY, pcmmixleg::CSnpZ, pcmmixleg::CSnpSnp, + pcmmixleg::CTglY, pcmmixleg::CTglZ, pcmmixleg::CTglSnp, pcmmixleg::CTglTgl, + pcmmixleg::C1PtY, pcmmixleg::C1PtZ, pcmmixleg::C1PtSnp, pcmmixleg::C1PtTgl, pcmmixleg::C1Pt21Pt2, + track::TPCNClsFindable, track::TPCNClsFindableMinusFound, track::TPCNClsFindableMinusCrossedRows, track::TPCNClsShared, + track::TPCChi2NCl, track::TPCInnerParam, track::TPCSignal, pidtpc::TPCNSigmaEl, pidtpc::TPCNSigmaPi, + track::ITSClusterSizes, track::ITSChi2NCl, track::DetectorMap, + pcmmixleg::SourceEventUid, pcmmixleg::SourcePhotonUid, pcmmixleg::SourceV0Id, + pcmmixleg::SourcePx, pcmmixleg::SourcePy, pcmmixleg::SourcePz, pcmmixleg::SourceVx, pcmmixleg::SourceVy, pcmmixleg::SourceVz, + pcmmixleg::SourcePvX, pcmmixleg::SourcePvY, pcmmixleg::SourcePvZ, pcmmixleg::SourcePvCovXX, pcmmixleg::SourcePvCovXY, pcmmixleg::SourcePvCovXZ, + pcmmixleg::SourcePvCovYY, pcmmixleg::SourcePvCovYZ, pcmmixleg::SourcePvCovZZ, pcmmixleg::SourcePvChi2, pcmmixleg::SourcePvNContrib, + pcmmixleg::OrigTrackId, pcmmixleg::McParticleId, pcmmixleg::McMotherId, pcmmixleg::McPdg, pcmmixleg::McMotherPdg, + pcmmixleg::McMotherPx, pcmmixleg::McMotherPy, pcmmixleg::McMotherPz, pcmmixleg::McGrandMotherId, pcmmixleg::McGrandMotherPdg, pcmmixleg::McAncestorId, + pcmmixleg::McGGMotherId, pcmmixleg::McGGMotherPdg, pcmmixleg::McRootId, pcmmixleg::McRootPdg, pcmmixleg::McRootDepth, pcmmixleg::McVx, pcmmixleg::McVy, pcmmixleg::McVz, + + // dynamic column + track::TPCNClsFound, + track::TPCNClsCrossedRows, + track::TPCCrossedRowsOverFindableCls, + track::TPCFoundOverFindableCls, + track::TPCFractionSharedCls, + track::v001::ITSClusterMap, track::v001::ITSNCls, track::v001::ITSNClsInnerBarrel, + track::HasITS, track::HasTPC, track::HasTRD, track::HasTOF); +using PCMMixLeg = PCMMixLegs::iterator; + +// free electron legs of builder run 1 (fillFreeLegs): tracks that pass the leg cuts and are the leg of no stored photon; the +// parameters as the V0 fit would use them (TPC-only legs moved to their own collision). The mixer pools them with the photons +// (cfgPoolFreeLegs), so that run 2 can pair them in the same and the mixed event alike. +namespace pcmfreeleg +{ +DECLARE_SOA_COLUMN(CollisionId, collisionId, int); //! collision of the track +DECLARE_SOA_COLUMN(TrackId, trackId, int); //! AO2D track +} // namespace pcmfreeleg +DECLARE_SOA_TABLE(PCMFreeLegs, "AOD", "PCMFREELEG", //! + o2::soa::Index<>, pcmfreeleg::CollisionId, pcmfreeleg::TrackId, v0leg::Sign, + v0legcov::X, v0legcov::Alpha, v0legcov::Y, v0legcov::Z, v0legcov::Snp, v0legcov::Tgl, v0legcov::Signed1Pt, v0legcov::Cov, + track::TPCNClsFindable, track::TPCNClsFindableMinusFound, track::TPCNClsFindableMinusCrossedRows, track::TPCNClsShared, + track::TPCChi2NCl, track::TPCInnerParam, track::TPCSignal, pidtpc::TPCNSigmaEl, pidtpc::TPCNSigmaPi, + track::ITSClusterSizes, track::ITSChi2NCl, track::DetectorMap, + + // dynamic column + track::HasITS, track::HasTPC); +using PCMFreeLeg = PCMFreeLegs::iterator; + +// free legs as AO2D track indices only (linked derived data): run 1 writes (collision, track) of every free leg of a selected +// collision; associate-photon-to-emevent adds the derived event row. The mixer on linked derived data reads the tracks from the parent AO2D. +namespace pcmfreelegid +{ +DECLARE_SOA_INDEX_COLUMN(PMEvent, pmevent); //! derived event of the free leg (-1: collision not in PMEvents) +DECLARE_SOA_COLUMN(TrackIdDelta, trackIdDelta, int); //! AO2D track index minus the one of the previous row of this DF (first row: the index): small numbers, ~5x smaller on disk +} // namespace pcmfreelegid +DECLARE_SOA_TABLE(PCMFreeLegIds, "AOD", "PCMFREELEGID", //! rows in increasing track order; track index = running sum of TrackIdDelta over the DF + o2::soa::Index<>, pcmfreeleg::CollisionId, pcmfreelegid::TrackIdDelta); +using PCMFreeLegId = PCMFreeLegIds::iterator; +DECLARE_SOA_TABLE(PCMFreeLegEMEventIds, "AOD", "PCMFREELEGEVID", pcmfreelegid::PMEventId); //! to be joined with PCMFreeLegIds + +// builder run 2 output (same columns as the run-1 tables; collisionId = PCMMixCollisions row, trackId = PCMMixLegs row, +// v0Id >= 0: original pairing of run 1, -1: crossed pairing) +DECLARE_SOA_TABLE(V0LegsMix, "AOD", "V0LEGMIX", //! + o2::soa::Index<>, v0leg::CollisionId, v0leg::TrackId, v0leg::Sign, + v0leg::Px, v0leg::Py, v0leg::Pz, + track::DcaXY, track::DcaZ, + track::TPCNClsFindable, track::TPCNClsFindableMinusFound, track::TPCNClsFindableMinusCrossedRows, track::TPCNClsShared, + track::TPCChi2NCl, track::TPCInnerParam, + track::TPCSignal, pidtpc::TPCNSigmaEl, pidtpc::TPCNSigmaPi, + track::ITSClusterSizes, track::ITSChi2NCl, track::DetectorMap, + + // dynamic column + v0leg::P, + v0leg::Pt, + v0leg::Eta, + v0leg::Phi, + v0leg::Tgl, + + track::TPCNClsFound, + track::TPCNClsCrossedRows, + track::TPCCrossedRowsOverFindableCls, + track::TPCFoundOverFindableCls, + track::TPCFractionSharedCls, + track::v001::ITSClusterMap, track::v001::ITSNCls, track::v001::ITSNClsInnerBarrel, + track::HasITS, track::HasTPC, track::HasTRD, track::HasTOF, + v0leg::MeanClusterSizeITS, + v0leg::MeanClusterSizeITSib, + v0leg::MeanClusterSizeITSob); +using V0LegMix = V0LegsMix::iterator; + +namespace v0photonkfmix +{ +DECLARE_SOA_INDEX_COLUMN_FULL(PosTrack, posTrack, int, V0LegsMix, "_Pos"); //! +DECLARE_SOA_INDEX_COLUMN_FULL(NegTrack, negTrack, int, V0LegsMix, "_Neg"); //! +DECLARE_SOA_DYNAMIC_COLUMN(PMEventId, pmeventId, [](int collisionId) -> int { return collisionId; }); //! event index as in V0KFEMEventIds (= PCMMixCollisions row), for V0PhotonCut +} // namespace v0photonkfmix +DECLARE_SOA_TABLE(V0PhotonsKFMix, "AOD", "V0PHOTONKFMIX", //! + o2::soa::Index<>, v0photonkf::CollisionId, v0photonkf::V0Id, v0photonkfmix::PosTrackId, v0photonkfmix::NegTrackId, + v0photonkf::Vx, v0photonkf::Vy, v0photonkf::Vz, + v0photonkf::Px, v0photonkf::Py, v0photonkf::Pz, + v0photonkf::MGamma, + v0photonkf::DCAxyToPV, v0photonkf::DCAzToPV, + v0photonkf::CosPA, v0photonkf::CosPAXY, v0photonkf::CosPARZ, v0photonkf::PCA, + v0photonkf::Alpha, v0photonkf::QtArm, + v0photonkf::ChiSquareNDF, + + // dynamic column + v0photonkf::E, + v0photonkf::Pt, + v0photonkf::Eta, + v0photonkf::Phi, + v0photonkf::P, + v0photonkf::V0Radius, + v0photonkfmix::PMEventId); +using V0PhotonKFMix = V0PhotonsKFMix::iterator; + +DECLARE_SOA_TABLE(V0LegsXYZMix, "AOD", "V0LEGXYZMIX", track::X, track::Y, track::Z); //! +DECLARE_SOA_TABLE(V0LegsDeDxMCMix, "AOD", "V0LEGDEDXMCMIX", mcpidtpc::DeDxTunedMc, o2::soa::Marker<3>); //! stays empty (run 2 runs without MC) +DECLARE_SOA_TABLE(V0PhotonsPhiVPsiMix, "AOD", "V0PHOTONPHIVMIX", o2::soa::Index<>, v0photonsphivpsi::PhiV, v0photonsphivpsi::PsiPair); //! +DECLARE_SOA_TABLE(V0LegsCovMix, "AOD", "V0LEGCOVMIX", //! + v0legcov::X, v0legcov::Alpha, v0legcov::Y, v0legcov::Z, v0legcov::Snp, v0legcov::Tgl, v0legcov::Signed1Pt, v0legcov::Cov); + namespace dalitzee { DECLARE_SOA_INDEX_COLUMN(EMEvent, emevent); //! diff --git a/PWGEM/PhotonMeson/TableProducer/CMakeLists.txt b/PWGEM/PhotonMeson/TableProducer/CMakeLists.txt index b7eaafafcaf..9f787c4bb06 100644 --- a/PWGEM/PhotonMeson/TableProducer/CMakeLists.txt +++ b/PWGEM/PhotonMeson/TableProducer/CMakeLists.txt @@ -21,6 +21,16 @@ o2physics_add_dpl_workflow(photon-conversion-builder-tmptable PUBLIC_LINK_LIBRARIES O2::Framework O2::DCAFitter O2Physics::AnalysisCore O2Physics::MLCore KFParticle::KFParticle O2Physics::TPCDriftManager COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(photon-conversion-builder-mixed + SOURCES photonconversionbuilderMixed.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DCAFitter O2Physics::AnalysisCore O2Physics::MLCore KFParticle::KFParticle O2Physics::TPCDriftManager + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(photon-mixed-event-producer + SOURCES photonMixedEventProducer.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(create-emevent-photon SOURCES createEMEventPhoton.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGJECore diff --git a/PWGEM/PhotonMeson/TableProducer/associateMCinfoPhoton.cxx b/PWGEM/PhotonMeson/TableProducer/associateMCinfoPhoton.cxx index e89f5c765e6..7248b009e04 100644 --- a/PWGEM/PhotonMeson/TableProducer/associateMCinfoPhoton.cxx +++ b/PWGEM/PhotonMeson/TableProducer/associateMCinfoPhoton.cxx @@ -57,7 +57,7 @@ using namespace o2::soa; using namespace o2::aod::pwgem::photonmeson::utils::mcutil; using namespace o2::constants::physics; -using MyCollisionsMC = soa::Join; +using MyCollisionsMC = soa::Join; using TracksMC = soa::Join; using FwdTracksMC = soa::Join; using MyEMCClusters = soa::Join; @@ -95,6 +95,7 @@ struct AssociateMCInfoPhoton { Configurable maxY{"maxY", 0.9f, "max |rapidity| for BinnedGenPts table"}; Configurable doStoreAllDaughters{"doStoreAllDaughters", false, "flag to enable storing of all photon, pi0, eta, eta' and omega daughters. This will increase the dervied data size!"}; Configurable useIsSelected{"useIsSelected", true, "flag to enable the usage of isSelected from our EM event selection. Disabling this will increase the derived data size!"}; + Configurable useIsEOI{"useIsEOI", true, "flag to enable the usage of the EOI table"}; HistogramRegistry registry{"EMMCEvent"}; @@ -172,12 +173,16 @@ struct AssociateMCInfoPhoton { template bool passesCollisionGate(TCollision const& collisionIter) { + // event selection same as in createEMEventPhoton if (!collisionIter.has_mcCollision()) { return false; } if (useIsSelected.value && !collisionIter.isSelected()) { return false; } + if (useIsEOI.value && !collisionIter.isEoI()) { + return false; + } return true; } diff --git a/PWGEM/PhotonMeson/TableProducer/createEMEventPhoton.cxx b/PWGEM/PhotonMeson/TableProducer/createEMEventPhoton.cxx index 03aba7421de..85bf97981e0 100644 --- a/PWGEM/PhotonMeson/TableProducer/createEMEventPhoton.cxx +++ b/PWGEM/PhotonMeson/TableProducer/createEMEventPhoton.cxx @@ -41,6 +41,7 @@ #include #include #include +#include using namespace o2; using namespace o2::framework; @@ -67,6 +68,8 @@ struct CreateEMEventPhoton { Produces emswtbit; Produces event_norm_info; Produces eventWeights; + Produces eventXY; // filled only with fillVertex + Produces eventCov; // filled only with fillVertex enum class EMEventType : int { kEvent = 0, @@ -75,6 +78,7 @@ struct CreateEMEventPhoton { kEvent_JJ = 3, }; + Configurable fillVertex{"fillVertex", false, "fill EMEventsXY and EMEventsCov (primary vertex x, y, covariance, chi2; the tables are written in any case, empty if false), joinable to PMEvents: needed by the co-reconstructed mixed event on derived data (photon-mixed-event-producer processDerived)"}; Configurable needEMCTrigger{"needEMCTrigger", false, "flag to only save events which have kTVXinEMC trigger bit. To reduce PbPb derived data size"}; Configurable needPHSTrigger{"needPHSTrigger", false, "flag to only save events which have kTVXinPHOS trigger bit. To reduce PbPb derived data size"}; Configurable enableJJHistograms{"enableJJHistograms", false, "flag to fill JJ QA histograms for outlier rejection"}; @@ -149,7 +153,10 @@ struct CreateEMEventPhoton { collision.numContrib(), collision.trackOccupancyInTimeRange(), collision.ft0cOccupancyInTimeRange()); eventalias(collision.alias_raw()); - // eventCov(collision.covXX(), collision.covXY(), collision.covXZ(), collision.covYY(), collision.covYZ(), collision.covZZ(), collision.chi2()); + if (fillVertex) { + eventXY(collision.posX(), collision.posY()); + eventCov(collision.covXX(), collision.covXY(), collision.covXZ(), collision.covYY(), collision.covYZ(), collision.covZZ(), collision.chi2()); + } eventMult(collision.multFT0A(), collision.multFT0C(), collision.multNTracksPV(), collision.multNTracksPVeta1(), collision.multNTracksPVetaHalf()); @@ -279,6 +286,7 @@ struct CreateEMEventPhoton { }; struct AssociatePhotonToEMEvent { Produces v0kfeventid; + Produces freelegeventid; Produces prmeleventid; Produces phoseventid; Produces emceventid; @@ -312,6 +320,19 @@ struct AssociatePhotonToEMEvent { fillEventId(collisions, photons, v0kfeventid, perCollisionPCM); } + // free legs of run 1 (PCMFreeLegIds, linked derived data): one row per free leg, in its order; -1 when the collision has no PMEvent + void processPCMFreeLegs(aod::PMEvents const& collisions, aod::PCMFreeLegIds const& freeLegs) + { + std::unordered_map eventOf; + for (const auto& collision : collisions) { + eventOf[collision.collisionId()] = collision.globalIndex(); + } + for (const auto& leg : freeLegs) { + const auto it = eventOf.find(leg.collisionId()); + freelegeventid(it == eventOf.end() ? -1 : it->second); + } + } + void processElectronFromDalitz(aod::PMEvents const& collisions, aod::EMPrimaryElectronsFromDalitz const& tracks) { fillEventId(collisions, tracks, prmeleventid, perCollisionEl); @@ -335,6 +356,7 @@ struct AssociatePhotonToEMEvent { void processDummy(aod::PMEvents const&) {} PROCESS_SWITCH(AssociatePhotonToEMEvent, processPCM, "process pcm-event indexing", false); + PROCESS_SWITCH(AssociatePhotonToEMEvent, processPCMFreeLegs, "free legs of run 1 (PCMFreeLegIds) -> derived event index, for the mixer on linked derived data", false); PROCESS_SWITCH(AssociatePhotonToEMEvent, processElectronFromDalitz, "process dalitzee-event indexing", false); PROCESS_SWITCH(AssociatePhotonToEMEvent, processPHOS, "process phos-event indexing", false); PROCESS_SWITCH(AssociatePhotonToEMEvent, processEMC, "process emc-event indexing", false); diff --git a/PWGEM/PhotonMeson/TableProducer/photonMixedEventProducer.cxx b/PWGEM/PhotonMeson/TableProducer/photonMixedEventProducer.cxx new file mode 100644 index 00000000000..5e873e46102 --- /dev/null +++ b/PWGEM/PhotonMeson/TableProducer/photonMixedEventProducer.cxx @@ -0,0 +1,787 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file photonMixedEventProducer.cxx +/// \brief This task implements a new mixed event method and is the necessary step between builder run 1 and run 2. +// At first it pools of the run-1 events (photons and free legs,copies with their legs) in z x occupancy bins; +// per event one same-event artificial event (the complete event) and cfgOverlay mixed artificial events +// (the complete event + one complete partner event of its pool, translated to the +/// current vertex), written as PCMMixCollisions / PCMMixLegs for photon-conversion-builder-mixed. Needs builder run 1 with +/// fillLegCov = true and fillFreeLegs = true. +/// \author Stefanie Mrozinski + +#include "PWGEM/PhotonMeson/DataModel/EventTables.h" +#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +namespace +{ +constexpr int kMaxAncestorDepth = 100; // mother-chain steps searched for the physical primary + +// copy of one leg +struct LegCopy { + int8_t sign = 0; + float x = 0.f, alpha = 0.f, y = 0.f, z = 0.f, snp = 0.f, tgl = 0.f, signed1Pt = 0.f, pt = 0.f, phi = 0.f; + std::array cov{}; + uint8_t tpcNClsFindable = 0; + int8_t tpcNClsFindableMinusFound = 0, tpcNClsFindableMinusCrossedRows = 0; + uint8_t tpcNClsShared = 0; + float tpcChi2NCl = 0.f, tpcInnerParam = 0.f, tpcSignal = 0.f, tpcNSigmaEl = 0.f, tpcNSigmaPi = 0.f; + uint32_t itsClusterSizes = 0; + float itsChi2NCl = 0.f; + uint8_t detectorMap = 0; + int origTrackId = -1, mcParticleId = -1, mcMotherId = -1, mcPdg = 0, mcMotherPdg = 0, mcGrandMotherId = -1, mcGrandMotherPdg = 0, mcAncestorId = -1; + int mcGGMotherId = -1, mcGGMotherPdg = 0, mcRootId = -1, mcRootPdg = 0, mcRootDepth = -1; + float mcMotherPx = 0.f, mcMotherPy = 0.f, mcMotherPz = 0.f; + float mcVx = 0.f, mcVy = 0.f, mcVz = 0.f; // MC production vertex of the leg = true conversion point +}; + +struct PhotonCopy { + int64_t eventUid = -1, photonUid = -1; + int v0Id = -1; + float px = 0.f, py = 0.f, pz = 0.f, vx = 0.f, vy = 0.f, vz = 0.f; + float pvX = 0.f, pvY = 0.f, pvZ = 0.f, pvCovXX = 0.f, pvCovXY = 0.f, pvCovXZ = 0.f, pvCovYY = 0.f, pvCovYZ = 0.f, pvCovZZ = 0.f, pvChi2 = 0.f; // primary vertex of its event + uint16_t pvNContrib = 0; + std::vector legs; + int timesUsed = 0; // in mixed artificial events +}; + +struct EventCopy { + int64_t eventUid = -1; + float x = 0.f, y = 0.f, z = 0.f, occupancy = 0.f, cent = 0.f; + std::vector photons; + std::vector freeLegs; +}; + +// vertex and centralities of the event whose vertex an artificial event gets +struct VertexCopy { + float x = 0.f, y = 0.f, z = 0.f, covXX = 0.f, covXY = 0.f, covXZ = 0.f, covYY = 0.f, covYZ = 0.f, covZZ = 0.f, chi2 = 0.f; + uint16_t numContrib = 0; + float centFT0M = 0.f, centFT0A = 0.f, centFT0C = 0.f; + int runNumber = 0, multNTracksPVeta1 = 0; // run and multiplicities of the event whose vertex it is (photonhbt: CCDB, 2D CF axes) + float multFT0M = 0.f; + uint64_t timestamp = 0; // CCDB timestamp +}; +} // namespace + +struct PhotonMixedEventProducer { + using MyCollisions = soa::Join; + using MyTracksMC = soa::Join; + using MyEvents = soa::Join; + using MyPhotonsDerived = soa::Join; + using MyLegsMCDerived = soa::Join; + + Produces mixCollisions; + Produces mixLegs; + + Configurable cfgCentEstimator{"cfgCentEstimator", 2, "centrality estimator: 0 FT0M, 1 FT0A, 2 FT0C"}; + Configurable cfgCentMin{"cfgCentMin", 10.f, "min centrality (one class)"}; + Configurable cfgCentMax{"cfgCentMax", 20.f, "max centrality"}; + Configurable cfgZMax{"cfgZMax", 10.f, "max |z_vtx| (cm)"}; + Configurable cfgZBinWidth{"cfgZBinWidth", 2.f, "pool z bin width (cm)"}; + Configurable cfgOccupancyEstimator{"cfgOccupancyEstimator", 0, "occupancy: 0 trackOccupancyInTimeRange, 1 ft0cOccupancyInTimeRange"}; + Configurable> cfgOccupancyBins{"cfgOccupancyBins", {-1.f, 1.e9f}, "pool occupancy bin edges (events outside are not used)"}; + Configurable cfgPoolDepth{"cfgPoolDepth", 50, "events per pool (has to exceed the photon multiplicity N)"}; + Configurable cfgNMixPerEvent{"cfgNMixPerEvent", 1, "mixed artificial events per event"}; + Configurable cfgRequireFullPool{"cfgRequireFullPool", true, "skip an event completely (same and mixed) if its pool has fewer than N events; false: write the same-event one and no mixed one"}; + Configurable cfgSeed{"cfgSeed", 12345, "random seed (choice of partner events and photons)"}; + Configurable cfgAlignToVertex{"cfgAlignToVertex", true, "translate the legs and the conversion point of each partner photon by (current vertex - partner vertex): every photon of the mixed artificial event originates from its vertex, as in the same event"}; + Configurable cfgPoolGroups{"cfgPoolGroups", false, "pool conflict groups instead of photons (run 1 with deduplicationMode 3 = keep all): candidates sharing a track form one group with all its tracks once; the decision is taken only in run 2, after mixing, identically for the same and the mixed event"}; + Configurable cfgPhotonWeighted{"cfgPhotonWeighted", true, "partner events drawn with probability proportional to their photon number (each pooled photon equally likely, still N distinct events); false: events equally likely"}; + Configurable cfgInject{"cfgInject", 0, "injection mixed event: instead of N photons from N partner events, K mixed artificial events per event, each = the COMPLETE current event (its photons and, with cfgPoolFreeLegs, its free legs) + ONE photon of a distinct partner event (translated with cfgAlignToVertex). K = cfgInject, -1: K = N (mixed pairs = 2 x same-event pairs), -2: K = N/2 rounded up (mixed pairs = same-event pairs, half the CPU); the pool needs K partner events. Run 2 with mixed_group.skipSameSourceCrossed: the current event's pairings stay as run 1 decided them, only the injected legs form new candidates (also with the free legs, which the N-photon mixed event cannot offer)"}; + Configurable cfgOverlay{"cfgOverlay", 1, "two-event leg overlay: K mixed artificial events per event, each = the COMPLETE current event + the COMPLETE partner event k (all its photons and, with cfgPoolFreeLegs, all its free legs, translated with cfgAlignToVertex). Every leg configuration of the same event exists across the two events, incl. true pairings that failed a run-1 cut. Run 2 with mixed_group.skipSameSourceCrossed: pairings inside one source event stay as run 1 decided them, only cross-event pairings are new. 0 = off (not with cfgInject)"}; + Configurable cfgPoolFreeLegs{"cfgPoolFreeLegs", true, "pool the free legs of run 1 (PCMFreeLegs, builder fillFreeLegs) with the photons: the same artificial event gets all M free legs of the event, the mixed one M free legs drawn from the N partner events, so that run 2 pairs free legs identically in both"}; + + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; + + std::map, std::deque> mPools; + std::mt19937 mRandom; + int mRunNumber = -1; + int64_t mEventUid = 0; + int64_t mPhotonUid = 0; + int64_t mNPooledPhotons = 0; + + enum CutFlow { kAll = 0, + kSelected, + kCent, + kZ, + kOccupancy, + kHasPhotons, + kPoolTooSmall, + kWritten }; + + void init(InitContext&) + { + mRandom.seed(cfgSeed.value); + if (cfgOccupancyBins->size() < 2) { + LOG(fatal) << "cfgOccupancyBins needs at least two edges"; + } + const AxisSpec axType{2, -0.5f, 1.5f, "artificial event (0 same, 1 mixed)"}; + const AxisSpec axN{101, -0.5f, 100.5f, "photons per event N"}; + const int nZ = static_cast(std::lround(2.f * cfgZMax / cfgZBinWidth)); + registry.add("Event/hCutFlow", "events;0 all, 1 selected, 2 centrality, 3 z, 4 occupancy, 5 with photons, 6 pool too small (skipped), 7 written", kTH1D, {{8, -0.5f, 7.5f}}); + registry.add("Event/hNPhotons", "accepted events;photons per event N", kTH1D, {axN}); + registry.add("Event/hNFreeLegs", "accepted events (cfgPoolFreeLegs);free legs per event M", kTH1D, {{300, -0.5f, 299.5f}}); + registry.add("Mix/hFreeLegsMixedVsCurrent", "cfgPoolFreeLegs;M of the current event;free legs in the mixed artificial event", kTH2D, {{300, -0.5f, 299.5f}, {300, -0.5f, 299.5f}}); + registry.add("Event/hNPhotonsMixedVsCurrent", "mixed artificial events (must be the diagonal);N of the current event;photons in the mixed event", kTH2D, {axN, axN}); + registry.add("Event/hZ", "accepted events;z_{vtx} (cm)", kTH1D, {{nZ, -cfgZMax, cfgZMax}}); + registry.add("Event/hOccupancy", "accepted events;occupancy", kTH1D, {{200, 0.f, 20000.f}}); + registry.add("Pool/hDepthVsZ", "pool size when used;z_{vtx} (cm);events in the pool", kTH2D, {{nZ, -cfgZMax, cfgZMax}, {cfgPoolDepth + 1, -0.5f, cfgPoolDepth + 0.5f}}); + registry.add("Pool/hDepthVsOccBin", "pool size when used;occupancy bin;events in the pool", kTH2D, {{static_cast(cfgOccupancyBins->size()) - 1, -0.5f, cfgOccupancyBins->size() - 1.5f}, {cfgPoolDepth + 1, -0.5f, cfgPoolDepth + 0.5f}}); + registry.add("Pool/hPartnersVsN", "partner events available vs needed;N of the current event;events in the pool", kTH2D, {axN, {cfgPoolDepth + 1, -0.5f, cfgPoolDepth + 0.5f}}); + registry.add("Pool/hNPooledPhotons", "photons in all pools (memory), per event;photons", kTH1D, {{500, 0.f, 5.e5f}}); + registry.add("Pool/hNPools", "filled pools, per event;pools", kTH1D, {{1000, 0.f, 10000.f}}); + registry.add("Pool/hResets", "pool resets (run change);resets", kTH1D, {{1, -0.5f, 0.5f}}); + registry.add("Pool/hTimesUsed", "pooled photons leaving the pool;times used in a mixed event", kTH1D, {{51, -0.5f, 50.5f}}); + registry.add("Mix/hDz", "mixed photons;z_{vtx}^{partner} - z_{vtx}^{current} (cm)", kTH1D, {{200, -0.5f, 0.5f}}); + registry.add("Mix/hDOccupancy", "mixed photons;occupancy^{partner} - occupancy^{current}", kTH1D, {{200, -5000.f, 5000.f}}); + registry.add("Mix/hDCent", "mixed photons;centrality^{partner} - centrality^{current} (%)", kTH1D, {{200, -10.f, 10.f}}); + registry.add("Mix/hPartnerAge", "mixed photons;position of the partner event in the pool (0 = newest)", kTH1D, {{cfgPoolDepth, -0.5f, cfgPoolDepth - 0.5f}}); + registry.add("Mix/hDistinctSourcesMinusN", "mixed artificial events (must be 0);distinct source events - N", kTH1D, {{21, -10.5f, 10.5f}}); + registry.add("Mix/hSourceIsCurrent", "mixed photons from the current event (must be 0);0 other event, 1 current event", kTH1D, {{2, -0.5f, 1.5f}}); + registry.add("Mix/hGroupLegs", "cfgPoolGroups: tracks per conflict group;tracks;groups", kTH1D, {{30, 0.5f, 30.5f}}); + registry.add("Event/hNCandidates", "cfgPoolGroups: run-1 candidates per event (before grouping);candidates;events", kTH1D, {{200, -0.5f, 199.5f}}); + registry.add("Mix/hLegsMinus2N", "artificial events (must be 0);artificial event;legs written - 2 N", kTH2D, {axType, {21, -10.5f, 10.5f}}); + registry.add("Mix/hPhotonPtEta", "photons of the artificial events;artificial event;p_{T} (GeV/c);#eta", kTH3D, {axType, {100, 0.f, 5.f}, {40, -1.f, 1.f}}); + registry.add("Mix/hLegTypes", "legs of the artificial events;artificial event;0 ITS-TPC, 1 TPC-only, 2 ITS-only, 3 other", kTH2D, {axType, {4, -0.5f, 3.5f}}); + // AO2D input: leg pT and phi of the track vs the values computed from V0LegsCov (what the derived-data mode uses), must be 0 + registry.add("Leg/hPtPhiFromCov", "legs (AO2D input);(p_{T}^{track} - p_{T}^{cov}) / p_{T}^{track};#varphi^{track} - #varphi^{cov} (rad)", kTH2D, {{100, -1.e-4f, 1.e-4f}, {100, -1.e-4f, 1.e-4f}}); + if (static_cast(doprocessRec.value) + static_cast(doprocessMC.value) + static_cast(doprocessDerived.value) + static_cast(doprocessDerivedMC.value) != 1) { + LOG(fatal) << "enable exactly one of processRec, processMC (AO2D input) and processDerived, processDerivedMC (derived data)"; + } + if (cfgOverlay != 0 && cfgInject != 0) { + LOG(fatal) << "cfgOverlay and cfgInject are two different mixed events, enable one"; + } + if (cfgOverlay != 0 && !cfgPoolFreeLegs) { + LOG(warning) << "cfgOverlay without cfgPoolFreeLegs: the partner event brings its stored photons only, not its free legs"; + } + if (cfgPoolFreeLegs && (doprocessDerived || doprocessDerivedMC)) { + LOG(fatal) << "cfgPoolFreeLegs needs PCMFreeLegs, which are not in the derived data"; + } + } + + template + float centrality(TCollision const& collision) const + { + switch (cfgCentEstimator.value) { + case 0: + return collision.centFT0M(); + case 1: + return collision.centFT0A(); + default: + return collision.centFT0C(); + } + } + + template + float occupancy(TCollision const& collision) const + { + return cfgOccupancyEstimator.value == 1 ? collision.ft0cOccupancyInTimeRange() : static_cast(collision.trackOccupancyInTimeRange()); + } + + int occupancyBin(const float occ) const + { + const auto& edges = cfgOccupancyBins.value; + for (size_t i = 0; i + 1 < edges.size(); ++i) { + if (edges[i] <= occ && occ < edges[i + 1]) { + return static_cast(i); + } + } + return -1; + } + + // leg pT and phi from the fit-input parameters + template + static std::pair ptPhiOf(TLegCov const& legCov) + { + const float pt = legCov.signed1Pt() != 0.f ? 1.f / std::fabs(legCov.signed1Pt()) : 0.f; + float phi = std::asin(std::clamp(legCov.snp(), -1.f, 1.f)) + legCov.alpha(); + phi = std::fmod(phi, o2::constants::math::TwoPI); + if (phi < 0.f) { + phi += o2::constants::math::TwoPI; + } + return {pt, phi}; + } + + // MC history of a leg + template + static void fillLegMC(LegCopy& c, TMCParticle const& mcLeg, TMCParticles const& mcParticles) + { + c.mcParticleId = mcLeg.globalIndex(); + c.mcPdg = mcLeg.pdgCode(); + c.mcVx = mcLeg.vx(); + c.mcVy = mcLeg.vy(); + c.mcVz = mcLeg.vz(); + if (!mcLeg.has_mothers()) { + return; + } + const auto mother = mcParticles.rawIteratorAt(mcLeg.mothersIds()[0]); + c.mcMotherId = mother.globalIndex(); + c.mcMotherPdg = mother.pdgCode(); + c.mcMotherPx = mother.px(); + c.mcMotherPy = mother.py(); + c.mcMotherPz = mother.pz(); + if (mother.has_mothers()) { // e.g. the e+- that radiated a bremsstrahlung photon + const auto grandMother = mcParticles.rawIteratorAt(mother.mothersIds()[0]); + c.mcGrandMotherId = grandMother.globalIndex(); + c.mcGrandMotherPdg = grandMother.pdgCode(); + if (grandMother.has_mothers()) { // e.g. the eta of eta -> 3 pi0 + const auto ggMother = mcParticles.rawIteratorAt(grandMother.mothersIds()[0]); + c.mcGGMotherId = ggMother.globalIndex(); + c.mcGGMotherPdg = ggMother.pdgCode(); + } + } + auto root = mother; + int rootDepth = 0; + for (; root.has_mothers() && rootDepth < kMaxAncestorDepth; ++rootDepth) { + root = mcParticles.rawIteratorAt(root.mothersIds()[0]); + } + c.mcRootId = static_cast(root.globalIndex()); + c.mcRootPdg = root.pdgCode(); + c.mcRootDepth = rootDepth; + auto anc = mother; + for (int depth = 0; !anc.isPhysicalPrimary() && anc.has_mothers() && depth < kMaxAncestorDepth; ++depth) { + anc = mcParticles.rawIteratorAt(anc.mothersIds()[0]); + } + c.mcAncestorId = anc.isPhysicalPrimary() ? static_cast(anc.globalIndex()) : -1; + } + + template + LegCopy copyLeg(TLeg const& leg, TLegCov const& legCov, [[maybe_unused]] TTracks const* tracks, [[maybe_unused]] TMCParticles const* mcParticles) + { + LegCopy c; + c.sign = leg.sign(); + c.x = legCov.x(); + c.alpha = legCov.alpha(); + c.y = legCov.y(); + c.z = legCov.z(); + c.snp = legCov.snp(); + c.tgl = legCov.tgl(); + c.signed1Pt = legCov.signed1Pt(); + const auto& cov = legCov.cov(); + for (size_t i = 0; i < c.cov.size(); ++i) { + c.cov[i] = cov[i]; + } + c.tpcNClsFindable = leg.tpcNClsFindable(); + c.tpcNClsFindableMinusFound = leg.tpcNClsFindableMinusFound(); + c.tpcNClsFindableMinusCrossedRows = leg.tpcNClsFindableMinusCrossedRows(); + c.tpcNClsShared = leg.tpcNClsShared(); + c.tpcChi2NCl = leg.tpcChi2NCl(); + c.tpcInnerParam = leg.tpcInnerParam(); + c.tpcSignal = leg.tpcSignal(); + c.tpcNSigmaEl = leg.tpcNSigmaEl(); + c.tpcNSigmaPi = leg.tpcNSigmaPi(); + c.itsClusterSizes = leg.itsClusterSizes(); + c.itsChi2NCl = leg.itsChi2NCl(); + c.detectorMap = leg.detectorMap(); + c.origTrackId = leg.trackId(); + const auto [ptCov, phiCov] = ptPhiOf(legCov); + if constexpr (isDerived) { + c.pt = ptCov; + c.phi = phiCov; + if constexpr (isMC) { + if (leg.has_emmcparticle()) { + fillLegMC(c, leg.template emmcparticle_as(), *mcParticles); + } + } + } else { + const auto track = tracks->rawIteratorAt(leg.trackId()); + c.pt = track.pt(); // as the builder reads them from the AO2D track + c.phi = track.phi(); + registry.fill(HIST("Leg/hPtPhiFromCov"), c.pt > 0.f ? (c.pt - ptCov) / c.pt : 0.f, RecoDecay::constrainAngle(c.phi - phiCov, -o2::constants::math::PI)); + if constexpr (isMC) { + if (track.has_mcParticle()) { + fillLegMC(c, track.template mcParticle_as(), *mcParticles); + } + } + } + return c; + } + + void writeLeg(LegCopy const& l, PhotonCopy const& p) + { + mixLegs(mixCollisions.lastIndex(), l.sign, + l.x, l.alpha, l.y, l.z, l.snp, l.tgl, l.signed1Pt, l.pt, l.phi, + l.cov[0], l.cov[1], l.cov[2], l.cov[3], l.cov[4], l.cov[5], l.cov[6], l.cov[7], l.cov[8], l.cov[9], l.cov[10], l.cov[11], l.cov[12], l.cov[13], l.cov[14], + l.tpcNClsFindable, l.tpcNClsFindableMinusFound, l.tpcNClsFindableMinusCrossedRows, l.tpcNClsShared, + l.tpcChi2NCl, l.tpcInnerParam, l.tpcSignal, l.tpcNSigmaEl, l.tpcNSigmaPi, + l.itsClusterSizes, l.itsChi2NCl, l.detectorMap, + p.eventUid, p.photonUid, p.v0Id, p.px, p.py, p.pz, p.vx, p.vy, p.vz, + p.pvX, p.pvY, p.pvZ, p.pvCovXX, p.pvCovXY, p.pvCovXZ, p.pvCovYY, p.pvCovYZ, p.pvCovZZ, p.pvChi2, p.pvNContrib, + l.origTrackId, l.mcParticleId, l.mcMotherId, l.mcPdg, l.mcMotherPdg, l.mcMotherPx, l.mcMotherPy, l.mcMotherPz, l.mcGrandMotherId, l.mcGrandMotherPdg, l.mcAncestorId, + l.mcGGMotherId, l.mcGGMotherPdg, l.mcRootId, l.mcRootPdg, l.mcRootDepth, l.mcVx, l.mcVy, l.mcVz); + } + + static PhotonCopy translated(PhotonCopy const& p, const float dx, const float dy, const float dz) + { + PhotonCopy t = p; + t.vx += dx; + t.vy += dy; + t.vz += dz; + t.pvX += dx; // its own vertex moves with it + t.pvY += dy; + t.pvZ += dz; + for (auto& l : t.legs) { + const float ca = std::cos(l.alpha), sa = std::sin(l.alpha); + l.x += dx * ca + dy * sa; + l.y += -dx * sa + dy * ca; + l.z += dz; + l.mcVx += dx; // the true conversion point moves with the photon + l.mcVy += dy; + l.mcVz += dz; + } + return t; + } + + void writeArtificialEvent(VertexCopy const& v, EventCopy const& current, const bool isMixed, const int zBin, const int occBin, + std::vector const& photons, const int currentCollisionId) + { + const float type = isMixed ? 1.f : 0.f; + const int firstLeg = mixLegs.lastIndex() + 1; + mixCollisions(v.x, v.y, v.z, v.covXX, v.covXY, v.covXZ, v.covYY, v.covYZ, v.covZZ, v.chi2, v.numContrib, + v.centFT0M, v.centFT0A, v.centFT0C, isMixed, currentCollisionId, current.eventUid, + zBin, occBin, current.occupancy, firstLeg, nLegsOf(photons), v.runNumber, v.multNTracksPVeta1, v.multFT0M, v.timestamp); + for (const auto* p : photons) { + if (p->legs.size() > 1) { // not a free-leg stub + registry.fill(HIST("Mix/hPhotonPtEta"), type, std::hypot(p->px, p->py), std::asinh(p->pz / std::hypot(p->px, p->py))); + } + for (const auto& l : p->legs) { + const bool hasITS = (l.detectorMap & o2::aod::track::ITS) != 0; + const bool hasTPC = (l.detectorMap & o2::aod::track::TPC) != 0; + registry.fill(HIST("Mix/hLegTypes"), type, hasITS && hasTPC ? 0.f : (hasTPC ? 1.f : (hasITS ? 2.f : 3.f))); + writeLeg(l, *p); + } + } + registry.fill(HIST("Mix/hLegsMinus2N"), type, (mixLegs.lastIndex() + 1 - firstLeg) - nLegsOf(photons)); + } + + static int nLegsOf(std::vector const& photons) + { + int n = 0; + for (const auto* p : photons) { + n += static_cast(p->legs.size()); + } + return n; + } + + std::vector toGroups(std::vector const& cands) + { + const int n = cands.size(); + std::vector parent(n); + std::iota(parent.begin(), parent.end(), 0); + std::function find = [&](int i) { return parent[i] == i ? i : parent[i] = find(parent[i]); }; + std::unordered_map firstOfTrack; + for (int i = 0; i < n; ++i) { + for (const auto& l : cands[i].legs) { + const auto [it, inserted] = firstOfTrack.emplace(l.origTrackId, i); + if (!inserted) { + parent[find(i)] = find(it->second); + } + } + } + std::map groups; // root -> group + std::map> tracksOfGroup; + for (int i = 0; i < n; ++i) { + const int r = find(i); + auto [it, inserted] = groups.try_emplace(r, cands[i]); + if (inserted) { + it->second.legs.clear(); + it->second.photonUid = mPhotonUid++; + } + for (const auto& l : cands[i].legs) { + if (tracksOfGroup[r].insert(l.origTrackId).second) { + it->second.legs.push_back(l); + } + } + } + std::vector out; + for (auto& [r, g] : groups) { + registry.fill(HIST("Mix/hGroupLegs"), static_cast(g.legs.size())); + out.push_back(std::move(g)); + } + return out; + } + + template + void runMixer(TCollisions const& collisions, TPhotons const& photons, TLegs const& legs, aod::V0LegsCov const& legsCov, + aod::PCMFreeLegs const* freeLegs, TTracks const* tracks, TMCParticles const* mcParticles) + { + if (legs.size() != 2 * photons.size() || legsCov.size() != legs.size()) { + LOG(fatal) << "V0LegsCov (" << legsCov.size() << ") / V0Legs (" << legs.size() << ") / V0PhotonsKF (" << photons.size() << ") do not match: run builder run 1 with fillLegCov = true"; + } + if (cfgPoolFreeLegs && (freeLegs == nullptr || (freeLegs->size() == 0 && photons.size() > 0))) { + LOG(fatal) << "cfgPoolFreeLegs: PCMFreeLegs is empty, run builder run 1 with fillFreeLegs = true"; + } + std::unordered_map> photonsOfCollision, freeLegsOfCollision; + for (const auto& photon : photons) { + if constexpr (isDerived) { + photonsOfCollision[photon.pmeventId()].push_back(photon.globalIndex()); + } else { + photonsOfCollision[photon.collisionId()].push_back(photon.globalIndex()); + } + } + if (cfgPoolFreeLegs) { + for (const auto& leg : *freeLegs) { + freeLegsOfCollision[leg.collisionId()].push_back(leg.globalIndex()); + } + } + + for (const auto& collision : collisions) { + registry.fill(HIST("Event/hCutFlow"), kAll); + int runNumber = 0; + uint64_t timestamp = 0; + if constexpr (isDerived) { + runNumber = collision.runNumber(); + timestamp = collision.timestamp(); + } else { + const auto bc = collision.template foundBC_as(); + runNumber = bc.runNumber(); + timestamp = bc.timestamp(); + } + if (runNumber != mRunNumber) { + if (mRunNumber >= 0) { + registry.fill(HIST("Pool/hResets"), 0.f); + } + mPools.clear(); + mNPooledPhotons = 0; + mRunNumber = runNumber; + } + if constexpr (!isDerived) { + if (!collision.isSelected()) { + continue; + } + } + registry.fill(HIST("Event/hCutFlow"), kSelected); + const float cent = centrality(collision); + if (cent < cfgCentMin || cfgCentMax <= cent) { + continue; + } + registry.fill(HIST("Event/hCutFlow"), kCent); + if (std::fabs(collision.posZ()) >= cfgZMax) { + continue; + } + registry.fill(HIST("Event/hCutFlow"), kZ); + const float occ = occupancy(collision); + const int occBin = occupancyBin(occ); + if (occBin < 0) { + continue; + } + registry.fill(HIST("Event/hCutFlow"), kOccupancy); + const auto it = photonsOfCollision.find(collision.globalIndex()); + if (it == photonsOfCollision.end()) { + continue; + } + registry.fill(HIST("Event/hCutFlow"), kHasPhotons); + const int zBin = static_cast(std::floor((collision.posZ() + cfgZMax) / cfgZBinWidth)); + registry.fill(HIST("Event/hZ"), collision.posZ()); + registry.fill(HIST("Event/hOccupancy"), occ); + + // copies of this event's photons + EventCopy current; + current.eventUid = mEventUid++; + current.x = collision.posX(); + current.y = collision.posY(); + current.z = collision.posZ(); + current.occupancy = occ; + current.cent = cent; + for (const auto& photonId : it->second) { + const auto photon = photons.rawIteratorAt(photonId); + PhotonCopy p; + p.eventUid = current.eventUid; + p.photonUid = mPhotonUid++; + p.v0Id = photon.v0Id(); + p.px = photon.px(); + p.py = photon.py(); + p.pz = photon.pz(); + p.vx = photon.vx(); + p.vy = photon.vy(); + p.vz = photon.vz(); + p.pvX = collision.posX(); + p.pvY = collision.posY(); + p.pvZ = collision.posZ(); + p.pvCovXX = collision.covXX(); + p.pvCovXY = collision.covXY(); + p.pvCovXZ = collision.covXZ(); + p.pvCovYY = collision.covYY(); + p.pvCovYZ = collision.covYZ(); + p.pvCovZZ = collision.covZZ(); + p.pvChi2 = collision.chi2(); + p.pvNContrib = collision.numContrib(); + p.legs.push_back(copyLeg(legs.rawIteratorAt(photon.posTrackId()), legsCov.rawIteratorAt(photon.posTrackId()), tracks, mcParticles)); + p.legs.push_back(copyLeg(legs.rawIteratorAt(photon.negTrackId()), legsCov.rawIteratorAt(photon.negTrackId()), tracks, mcParticles)); + current.photons.push_back(p); + } + if (cfgPoolGroups) { + registry.fill(HIST("Event/hNCandidates"), static_cast(current.photons.size())); + current.photons = toGroups(current.photons); + } + if constexpr (!isDerived) { + if (cfgPoolFreeLegs) { // one-leg stubs: event and vertex of this event, no photon momentum, v0Id -2 + if (const auto itF = freeLegsOfCollision.find(collision.globalIndex()); itF != freeLegsOfCollision.end()) { + for (const auto& legId : itF->second) { + const auto leg = freeLegs->rawIteratorAt(legId); + PhotonCopy f; + f.eventUid = current.eventUid; + f.photonUid = mPhotonUid++; + f.v0Id = -2; + f.pvX = collision.posX(); + f.pvY = collision.posY(); + f.pvZ = collision.posZ(); + f.pvCovXX = collision.covXX(); + f.pvCovXY = collision.covXY(); + f.pvCovXZ = collision.covXZ(); + f.pvCovYY = collision.covYY(); + f.pvCovYZ = collision.covYZ(); + f.pvCovZZ = collision.covZZ(); + f.pvChi2 = collision.chi2(); + f.pvNContrib = collision.numContrib(); + f.legs.push_back(copyLeg(leg, leg, tracks, mcParticles)); + current.freeLegs.push_back(f); + } + } + registry.fill(HIST("Event/hNFreeLegs"), static_cast(current.freeLegs.size())); + } + } + const int nPhotons = static_cast(current.photons.size()); + registry.fill(HIST("Event/hNPhotons"), nPhotons); + + auto& pool = mPools[{zBin, occBin}]; + const int depth = static_cast(pool.size()); + registry.fill(HIST("Pool/hPartnersVsN"), nPhotons, depth); + registry.fill(HIST("Pool/hDepthVsZ"), collision.posZ(), depth); + registry.fill(HIST("Pool/hDepthVsOccBin"), occBin, depth); + + VertexCopy v{collision.posX(), collision.posY(), collision.posZ(), collision.covXX(), collision.covXY(), collision.covXZ(), + collision.covYY(), collision.covYZ(), collision.covZZ(), collision.chi2(), collision.numContrib(), + collision.centFT0M(), collision.centFT0A(), collision.centFT0C(), runNumber, collision.multNTracksPVeta1(), collision.multFT0M(), timestamp}; + const int nPartnersNeeded = cfgOverlay > 0 ? cfgOverlay.value : (cfgInject > 0 ? cfgInject.value : (cfgInject == -2 ? std::max(1, (nPhotons + 1) / 2) : nPhotons)); // overlay / injection: K partner events + const bool poolFull = depth >= nPartnersNeeded; + if (!poolFull) { + registry.fill(HIST("Event/hCutFlow"), kPoolTooSmall); + } + if (poolFull || !cfgRequireFullPool) { + registry.fill(HIST("Event/hCutFlow"), kWritten); + } + std::vector host; // the complete current event: its photons and (cfgPoolFreeLegs) its free legs + host.reserve(current.photons.size() + current.freeLegs.size()); + for (const auto& p : current.photons) { + host.push_back(&p); + } + for (const auto& f : current.freeLegs) { + host.push_back(&f); + } + if (poolFull || !cfgRequireFullPool) { + writeArtificialEvent(v, current, false, zBin, occBin, host, collision.globalIndex()); + } + if (poolFull) { + std::vector order(depth); + auto drawOrder = [&]() { // partner events in random order (cfgPhotonWeighted: weighted sampling without replacement, largest log(u) / w first) + std::iota(order.begin(), order.end(), 0); + if (cfgPhotonWeighted) { + std::uniform_real_distribution uni(0., 1.); + std::vector key(depth); + for (int k = 0; k < depth; ++k) { + key[k] = std::log(std::max(uni(mRandom), 1e-300)) / static_cast(pool[k].photons.size()); // o2-linter: disable=magic-number (log floor) + } + std::sort(order.begin(), order.end(), [&](int x, int y) { return key[x] > key[y]; }); + } else { + std::shuffle(order.begin(), order.end(), mRandom); + } + }; + auto fillPartnerQA = [&](EventCopy const& partner, int age) { + registry.fill(HIST("Mix/hDz"), partner.z - current.z); + registry.fill(HIST("Mix/hDOccupancy"), partner.occupancy - current.occupancy); + registry.fill(HIST("Mix/hDCent"), partner.cent - current.cent); + registry.fill(HIST("Mix/hPartnerAge"), age); + registry.fill(HIST("Mix/hSourceIsCurrent"), partner.eventUid == current.eventUid ? 1.f : 0.f); + }; + if (cfgOverlay > 0) { // overlay: K artificial events = the complete current event + the complete partner event k + for (int iMix = 0; iMix < cfgNMixPerEvent; ++iMix) { + drawOrder(); + for (int k = 0; k < std::min(nPartnersNeeded, depth); ++k) { + auto& partner = pool[order[k]]; + fillPartnerQA(partner, order[k]); + std::vector overlay(host); + std::vector> aligned; // translated copies; reserved, so the pointers stay valid + aligned.reserve(partner.photons.size() + partner.freeLegs.size()); + for (auto* src : {&partner.photons, &partner.freeLegs}) { + for (auto& p : *src) { + if (src == &partner.photons) { + p.timesUsed++; + } + if (cfgAlignToVertex) { + aligned.push_back(std::make_unique(translated(p, current.x - partner.x, current.y - partner.y, current.z - partner.z))); + overlay.push_back(aligned.back().get()); + } else { + overlay.push_back(&p); + } + } + } + registry.fill(HIST("Mix/hDistinctSourcesMinusN"), 2 - nPhotons); // overlay: the current event + one partner + registry.fill(HIST("Event/hNPhotonsMixedVsCurrent"), nPhotons, nPhotons + static_cast(partner.photons.size())); + writeArtificialEvent(v, current, true, zBin, occBin, overlay, collision.globalIndex()); + } + } + } + if (cfgInject != 0) { + for (int iMix = 0; iMix < cfgNMixPerEvent; ++iMix) { + drawOrder(); + for (int k = 0; k < std::min(nPartnersNeeded, depth); ++k) { + auto& partner = pool[order[k]]; + std::uniform_int_distribution pick(0, partner.photons.size() - 1); + auto& p = partner.photons[pick(mRandom)]; + p.timesUsed++; + fillPartnerQA(partner, order[k]); + std::vector injected(host); + PhotonCopy alignedCopy; + if (cfgAlignToVertex) { + alignedCopy = translated(p, current.x - partner.x, current.y - partner.y, current.z - partner.z); + injected.push_back(&alignedCopy); + } else { + injected.push_back(&p); + } + registry.fill(HIST("Mix/hDistinctSourcesMinusN"), 2 - nPhotons); + registry.fill(HIST("Event/hNPhotonsMixedVsCurrent"), nPhotons, nPhotons + 1); + writeArtificialEvent(v, current, true, zBin, occBin, injected, collision.globalIndex()); + } + } + } + for (int iMix = 0; iMix < ((cfgInject != 0 || cfgOverlay > 0) ? 0 : cfgNMixPerEvent.value); ++iMix) { + drawOrder(); + std::vector mixedPhotons; + std::vector> aligned; // translated copies (cfgAlignToVertex); reserved, so the pointers stay valid + aligned.reserve(nPhotons + (cfgPoolFreeLegs ? static_cast(current.freeLegs.size()) : 0)); + std::set sources; + for (int k = 0; k < nPhotons; ++k) { // N partner events, one random photon each + auto& partner = pool[order[k]]; + std::uniform_int_distribution pick(0, partner.photons.size() - 1); + auto& p = partner.photons[pick(mRandom)]; + p.timesUsed++; + if (cfgAlignToVertex) { + aligned.push_back(std::make_unique(translated(p, current.x - partner.x, current.y - partner.y, current.z - partner.z))); + // cppcheck-suppress invalidContainer + mixedPhotons.push_back(aligned.back().get()); + } else { + mixedPhotons.push_back(&p); + } + sources.insert(partner.eventUid); + fillPartnerQA(partner, order[k]); + } + registry.fill(HIST("Mix/hDistinctSourcesMinusN"), static_cast(sources.size()) - nPhotons); + registry.fill(HIST("Event/hNPhotonsMixedVsCurrent"), nPhotons, mixedPhotons.size()); + if (cfgPoolFreeLegs) { // M free legs of the current event -> M drawn without replacement from the free legs of the N partner events + std::vector pooledFree; + for (int k = 0; k < nPhotons; ++k) { + for (const auto& f : pool[order[k]].freeLegs) { + pooledFree.push_back(&f); + } + } + std::shuffle(pooledFree.begin(), pooledFree.end(), mRandom); + const size_t nTake = std::min(pooledFree.size(), current.freeLegs.size()); + for (size_t k = 0; k < nTake; ++k) { + if (cfgAlignToVertex) { + const auto& partner = *pooledFree[k]; + aligned.push_back(std::make_unique(translated(partner, current.x - partner.pvX, current.y - partner.pvY, current.z - partner.pvZ))); + // cppcheck-suppress invalidContainer + mixedPhotons.push_back(aligned.back().get()); + } else { + mixedPhotons.push_back(pooledFree[k]); + } + } + registry.fill(HIST("Mix/hFreeLegsMixedVsCurrent"), static_cast(current.freeLegs.size()), static_cast(nTake)); + } + writeArtificialEvent(v, current, true, zBin, occBin, mixedPhotons, collision.globalIndex()); + } + } + + // this event joins its pool after it was mixed (never mixed with itself) + mNPooledPhotons += nPhotons; + pool.push_front(std::move(current)); + if (static_cast(pool.size()) > cfgPoolDepth) { + for (const auto& p : pool.back().photons) { + registry.fill(HIST("Pool/hTimesUsed"), p.timesUsed); + } + mNPooledPhotons -= static_cast(pool.back().photons.size()); + pool.pop_back(); + } + registry.fill(HIST("Pool/hNPooledPhotons"), mNPooledPhotons); + registry.fill(HIST("Pool/hNPools"), mPools.size()); + } + } + + // AO2D input (builder run 1 in the same workflow) + void processRec(MyCollisions const& collisions, aod::BCsWithTimestamps const&, aod::V0PhotonsKF const& photons, aod::V0Legs const& legs, aod::V0LegsCov const& legsCov, aod::PCMFreeLegs const& freeLegs, aod::TracksIU const& tracks) + { + runMixer(collisions, photons, legs, legsCov, &freeLegs, &tracks, static_cast(nullptr)); + } + PROCESS_SWITCH(PhotonMixedEventProducer, processRec, "data, AO2D input", true); + + void processMC(MyCollisions const& collisions, aod::BCsWithTimestamps const&, aod::V0PhotonsKF const& photons, aod::V0Legs const& legs, aod::V0LegsCov const& legsCov, aod::PCMFreeLegs const& freeLegs, MyTracksMC const& tracks, aod::McParticles const& mcParticles) + { + runMixer(collisions, photons, legs, legsCov, &freeLegs, &tracks, &mcParticles); + } + PROCESS_SWITCH(PhotonMixedEventProducer, processMC, "MC, AO2D input (leg truth into PCMMixLegs)", false); + + // derived data (EM skim with V0LegsCov and, from create-emevent-photon fillVertex, EMEventsXY + EMEventsCov) + void processDerived(MyEvents const& collisions, MyPhotonsDerived const& photons, aod::V0Legs const& legs, aod::V0LegsCov const& legsCov) + { + runMixer(collisions, photons, legs, legsCov, nullptr, static_cast(nullptr), static_cast(nullptr)); + } + PROCESS_SWITCH(PhotonMixedEventProducer, processDerived, "data, derived-data input", false); + + void processDerivedMC(MyEvents const& collisions, MyPhotonsDerived const& photons, MyLegsMCDerived const& legs, aod::V0LegsCov const& legsCov, aod::EMMCParticles const& mcParticles) + { + runMixer(collisions, photons, legs, legsCov, nullptr, static_cast(nullptr), &mcParticles); + } + PROCESS_SWITCH(PhotonMixedEventProducer, processDerivedMC, "MC, derived-data input (leg truth from V0LegMCLabels / EMMCParticles)", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc, TaskName{"photon-mixed-event-producer"})}; +} diff --git a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.cxx b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.cxx index 714a3ca3f72..0f210615c7c 100644 --- a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.cxx +++ b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.cxx @@ -16,8 +16,12 @@ #include "PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h" +#include +#include +#include + o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) { return o2::framework::WorkflowSpec{ - adaptAnalysisTask>(context, o2::framework::TaskName{"photon-conversion-builder"})}; + adaptAnalysisTask>(context, o2::framework::TaskName{"photon-conversion-builder"})}; } diff --git a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h index 95f181e49fd..3c77efa0efd 100644 --- a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h +++ b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h @@ -26,8 +26,10 @@ #include "PWGEM/PhotonMeson/Core/V0PhotonCut.h" #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" +#include "PWGEM/PhotonMeson/Utils/ConversionPointProfile.h" #include "PWGEM/PhotonMeson/Utils/PCMUtilities.h" #include "PWGEM/PhotonMeson/Utils/TrackSelection.h" +#include "PWGEM/PhotonMeson/Utils/V0DedupUtilities.h" #include "Common/Core/RecoDecay.h" #include "Common/Core/TPCVDriftManager.h" @@ -39,9 +41,11 @@ #include #include +#include #include #include #include +#include #include #include #include @@ -52,12 +56,15 @@ #include #include #include +#include #include #include #include #include +#include #include #include +#include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include @@ -71,17 +78,25 @@ #include #include +#include #include #include #include +#include +#include #include +#include +#include +#include #include #include #include +#include #include #include #include +// NOLINTBEGIN(google-global-names-in-headers) using o2::framework::AxisSpec; using o2::framework::Configurable; using o2::framework::ConfigurableGroup; @@ -89,9 +104,21 @@ using o2::framework::HistType; using o2::framework::kTH1D; using o2::framework::kTH1F; using o2::framework::kTH2F; +using o2::framework::kTH3F; using o2::framework::kTHnSparseF; using o2::framework::Preslice; using o2::framework::Produces; +using o2::pwgem::photonmeson::cpRadiusProfile; +using o2::pwgem::photonmeson::cpScanLeg; +using o2::pwgem::photonmeson::FinderCollision; +using o2::pwgem::photonmeson::FinderCuts; +using o2::pwgem::photonmeson::FinderLeg; +using o2::pwgem::photonmeson::FinderStep; +using o2::pwgem::photonmeson::LegScan; +using o2::pwgem::photonmeson::qInv; +using o2::pwgem::photonmeson::RadiusProfile; +using o2::pwgem::photonmeson::solveConflictGroup; +// NOLINTEND(google-global-names-in-headers) using MyCollisions = o2::soa::Join; // using MyCollisionsWithSWT = soa::Join; @@ -133,6 +160,15 @@ struct V0CandidateHelper { float pxAtPV = 0.f; float pyAtPV = 0.f; float pzAtPV = 0.f; + float svX = 0.f; + float svY = 0.f; + float svZ = 0.f; + float pxAtSV = 0.f; // momentum at the conversion point without the PV constraint (pointing residuals) + float pyAtSV = 0.f; + float pzAtSV = 0.f; + float purity = -999.f; + float cosPAxy = -2.f; // transverse pointing angle + float cosPA3D = -2.f; // 3D pointing angle V0CandidateHelper() = default; @@ -147,17 +183,110 @@ struct V0CandidateHelper { }; // (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) -using CandKey = std::tuple; +// Identifies a V0 candidate by collision, positive-track and negative track IDs, and the SVertexer V0 index (or -1 for finder and resolver photons) +struct CandKey { + int64_t v0Id = -1; // SVertexer V0 index, -1 for finder and resolver photons + int64_t collisionId = -1; + int64_t posId = -1; + int64_t eleId = -1; + auto operator<=>(const CandKey&) const = default; +}; + +// Stores whether a trial fit succeded and the score of the candidate (for the ambiguity resolver, the score is used to decide which of two crossed candidates to keep) +struct PairFit { + bool ok = false; + float score = 999.f; +}; + +// options for fitting a leg pair that was not built by the S-Vertexer (finder, ambiguity resolver) +struct SourcePV { + float x = 0.f, y = 0.f, z = 0.f, cXX = 0.f, cXY = 0.f, cXZ = 0.f, cYY = 0.f, cYZ = 0.f, cZZ = 0.f, c2 = 0.f; + uint16_t nContrib = 0; + [[nodiscard]] float posX() const { return x; } + [[nodiscard]] float posY() const { return y; } + [[nodiscard]] float posZ() const { return z; } + [[nodiscard]] float covXX() const { return cXX; } + [[nodiscard]] float covXY() const { return cXY; } + [[nodiscard]] float covXZ() const { return cXZ; } + [[nodiscard]] float covYY() const { return cYY; } + [[nodiscard]] float covYZ() const { return cYZ; } + [[nodiscard]] float covZZ() const { return cZZ; } + [[nodiscard]] float chi2() const { return c2; } + [[nodiscard]] uint16_t numContrib() const { return nContrib; } +}; + +struct LegDcaKey { + int64_t trackId = -1, collisionId = -1; + float x = 0.f, y = 0.f, z = 0.f; + bool sideBased = false; + bool operator==(LegDcaKey const& o) const { return trackId == o.trackId && collisionId == o.collisionId && x == o.x && y == o.y && z == o.z && sideBased == o.sideBased; } +}; +struct LegDcaKeyHash { + size_t operator()(LegDcaKey const& k) const + { + size_t h = std::hash{}(k.trackId) * 1000003u ^ std::hash{}(k.collisionId); + h = h * 1000003u ^ std::hash{}(k.x); + h = h * 1000003u ^ std::hash{}(k.y); + return (h * 1000003u ^ std::hash{}(k.z)) * 2u + (k.sideBased ? 1u : 0u); + } +}; +// Caches a propagated leg (electron PID) at its DCA to the vertex, for reuse in the ambiguity resolver and the mixed-event builder +struct LegAtDca { + std::array dca{}; + o2::track::TrackParCov trk; // leg (electron PID) propagated to its DCA to the vertex +}; + +struct FitOptions { + bool nonSVertexer = false; // finder PCA cut for mixed legs, side-based TPC drift correction + bool relaxAP = false; // no Armenteros-Podolanski cut + bool purityGate = false; // finder: require purity score > finderMinScore + float minPurity = -999.f; // purity gate threshold for this fit; below -998 the configured finderMinScore + PairFit* probe = nullptr; // only return the dedup score, do not add a candidate + const SourcePV* pv = nullptr; // run 2: fit against this vertex instead of the collision's (original pairings at their own source vertex) +}; + +// twin of PCMFreeLegs for the mixed instance of the builder +namespace o2::aod +{ +DECLARE_SOA_TABLE(PCMFreeLegsMix, "AOD", "PCMFREELEGMIX", //! + o2::soa::Index<>, pcmfreeleg::CollisionId, pcmfreeleg::TrackId, v0leg::Sign, + v0legcov::X, v0legcov::Alpha, v0legcov::Y, v0legcov::Z, v0legcov::Snp, v0legcov::Tgl, v0legcov::Signed1Pt, v0legcov::Cov, + track::TPCNClsFindable, track::TPCNClsFindableMinusFound, track::TPCNClsFindableMinusCrossedRows, track::TPCNClsShared, + track::TPCChi2NCl, track::TPCInnerParam, track::TPCSignal, pidtpc::TPCNSigmaEl, pidtpc::TPCNSigmaPi, + track::ITSClusterSizes, track::ITSChi2NCl, track::DetectorMap); +DECLARE_SOA_TABLE(PCMFreeLegIdsMix, "AOD", "PCMFREELEGIDMIX", //! twin of PCMFreeLegIds for the mixed instance (never written) + o2::soa::Index<>, pcmfreeleg::CollisionId, pcmfreelegid::TrackIdDelta); +} // namespace o2::aod + +// one tested pair of stored photons in the ambiguity resolver (also read by the MC diagnostics) +struct SwapRecord { + CandKey keyI{}, keyJ{}; + float q = -1.f; + bool complete = false; + bool swapped = false; + float gain = -999.f; + int fail = 0; // bits: 1 stored I refit, 2 stored J refit, 4 crossed I prefilter, 8 crossed J prefilter, 16 crossed I fit, 32 crossed J fit + int line = 0; // rejection line in fitV0Candidate of the first crossed fit that failed +}; + +// MC truth of the legs of a genuine photon, and the SVertexer and finder results for this leg pair +struct TrueLegTrace { + int svLine = -1; // SVertexer V0 of these legs: -1 none, 0 accepted as candidate, > 0 source line of the rejection in fitV0Candidate + int finderStep = -9; // finder: -9 pair not tested (tgl window, leg not offered, finder off), -8 both legs with ITS, -7 pair already built, else evalFinderPair + int finderLine = -1; // finder fit: -1 not fitted, 0 accepted, > 0 source line of the rejection in fitV0Candidate +}; -// Everything the truth diagnosis need, struct DedupDiag { - std::map> blockersByKey; // rejected candidate -> ALL candidates that took one of its legs - std::vector snapshot; // ALL candidates, before deduplication - std::vector stored; // the survivors + std::map> blockersByKey; // rejected candidate -> candidates that took one of its legs + std::set finderKeys; // candidates built by the run-1 finder (the other v0Id = -1 ones come from the swap resolver) + std::map, TrueLegTrace> trueLegTrace; // MC: leg pairs of genuine photons, SVertexer fit and finder result + std::vector snapshot; // all candidates, before deduplication + std::vector stored; // surviving candidates std::map> lossMargin; + std::vector swaps; }; -template +template struct PhotonConversionBuilder { // using namespace o2; @@ -172,6 +301,10 @@ struct PhotonConversionBuilder { Produces v0legsXYZ; Produces v0legsDeDxMC; Produces v0photonsphivpsi; + Produces v0legsCov; + Produces v0photonskfchi2; // KF chi2 of the conversion-vertex fit and to the PV + Produces> pcmfreelegs; + Produces> pcmfreelegids; // linked derived data: free legs as track indices // Produces v0photonskfcov; // Produces events_ngpcm; @@ -187,6 +320,9 @@ struct PhotonConversionBuilder { Configurable modeTrackPropagation{"modeTrackPropagation", 0, "0: use real track propagation, including material, 1: use fast approximation using only geometry, 2: Use real track propagation and make comparison to fast propagation (only for debugging and testing)"}; Configurable propV0LegsRadius{"propV0LegsRadius", 60.f, "Radius to which the V0 legs are propagated to calculate psipair and phiV"}; o2::framework::Configurable useSideBasedCorrection{"useSideBasedCorrection", false, "when moving TPC-only tracks, use TPC side flags (with legacy fallback) instead of tgl sign"}; + Configurable fillLegCov{"fillLegCov", false, "fill V0LegsCov (the table is written in any case, empty if false): the leg track parameters and covariance given to the V0 fit (TPC-only legs moved to the collision), one row per V0Legs row"}; + Configurable fillFreeLegIds{"fillFreeLegIds", false, "fill PCMFreeLegIds (the table is written in any case, empty if false): the free legs (as for fillFreeLegs) as AO2D (collision, track) indices only, for the mixer on linked derived data (the leg parameters are read from the parent AO2D); only selected collisions"}; + Configurable fillFreeLegs{"fillFreeLegs", false, "fill PCMFreeLegs (the table is written in any case, empty if false): tracks that pass the leg cuts (as the finder takes them) and are the leg of no stored photon, with the parameters the V0 fit would use (TPC-only legs moved to their own collision); pooled by the mixer with cfgPoolFreeLegs"}; // deduplication settings struct : ConfigurableGroup { @@ -195,13 +331,64 @@ struct PhotonConversionBuilder { Configurable deduplicationScoreWeight{"deduplicationScoreWeight", 0.5f, "0.:only pca goes into the score, 1: only cosPA goes itno score, any number in between is a mix of pca and cosPA"}; Configurable cfgDedupTruthMaps{"cfgDedupTruthMaps", true, "fill the fine (dEta, dPhi, q) truth maps in MC"}; Configurable dedupMaxGroupSize{"dedupMaxGroupSize", 12, "group matching (mode 2): conflict groups up to this size are solved exactly, larger ones greedily (capped at 16)"}; + Configurable groupMatchingScoreThreshold{"groupMatchingScoreThreshold", -1.f, "dedup mode 2, choice of the best leg-disjoint subset of a conflict group. <= 0 (master): most photons first, then lowest summed score. > 0: largest sum of max(threshold - score, 1e-3) over the photons, so a photon with a score near or above the threshold adds almost nothing and two poor candidates cannot outweigh one good one (e.g. 0.4)"}; + Configurable gapWeight{"gapWeight", 0.f, "dedup score: weight of the helix-circle gap of the legs (0 = off)"}; + Configurable gapScale{"gapScale", 1.f, "dedup score: scale of the circle gap (cm)"}; + Configurable useCosPA3D{"useCosPA3D", false, "use the 3D cosPA instead of the xy cosPA in V0PhotonCandidate"}; + Configurable resolveSplitLegs{"resolveSplitLegs", false, "after the dedup, remove photons that share a split track with a better photon - avoids duplicates for photon candidates"}; + Configurable splitLegMaxDTgl{"splitLegMaxDTgl", 0.03f, "split legs: max |tgl1 - tgl2|"}; + Configurable splitLegMaxChi2{"splitLegMaxChi2", 15.f, "split legs: max chi2 of two track pieces"}; + Configurable dedupHalfSwapRule{"dedupHalfSwapRule", false, "dedup mode 2, after the choice of a conflict group: a non-selected candidate whose two legs went to two different winners replaces both if it scores better than either and the two remaining legs of the winners form no candidate (two fakes on the legs of one photon cannot outweigh it through the score floor); same rule in run 2"}; + Configurable cfgExtendedDiag{"cfgExtendedDiag", false, "MC: truth of the stored photons, pairs and swaps"}; } dedup; + // recovery finder for not thinned data (step 2) and ambiguity resolver settings (step 5) + struct : ConfigurableGroup { + std::string prefix = "recovery_group"; + Configurable runFinder{"runFinder", false, "photon finder for free legs without ITS, candidates enter the dedup"}; + Configurable finderMinScore{"finderMinScore", 0.f, "finder: min. purity score (log-likelihood ratio true / fake)"}; + Configurable finderMaxDeltaTgl{"finderMaxDeltaTgl", 0.15f, "finder: max |tgl+ - tgl-|"}; + Configurable finderMaxCircleGap{"finderMaxCircleGap", 5.f, "finder: max |circle gap| (cm)"}; + Configurable finderMinCosXY{"finderMinCosXY", 0.9f, "finder: min. xy pointing at the touching point"}; + Configurable finderMaxDeltaZ{"finderMaxDeltaZ", 3.f, "finder: max |z+ - z-| at the touching point (cm)"}; + Configurable finderMaxDz{"finderMaxDz", 4.f, "finder: max |z of the photon line at the beam axis - z_PV| (cm)"}; + Configurable finderRMargin{"finderRMargin", 5.f, "finder: margin below the min. conversion radius (cm)"}; + Configurable finderTimeMargin{"finderTimeMargin", 100.f, "finder: margin of the leg time windows (ns)"}; + Configurable finderTimeMarginFwd{"finderTimeMarginFwd", 3000.f, "finder: extra late margin for TPC-only legs (ns)"}; + Configurable finderMaxPCAMixed{"finderMaxPCAMixed", -1.f, "finder: max PCA for an ITS and a non-ITS leg (cm), < 0 builder cuts"}; + Configurable finderFreeCollinearLegs{"finderFreeCollinearLegs", 0.f, "finder: two candidates of one collision with q_inv below this (GeV/c) count as collinear; their legs stay available to the finder (leg pairs already built are not refitted). Recovers true photons next to a close partner photon. 0 = off"}; + Configurable finderUniformFit{"finderUniformFit", false, "finder candidates (run 1) and finder-origin originals (run 2) are fitted exactly like SVertexer candidates: no side-based drift correction, AP cut applied, no purity gate (symmetric contest between finder-built and SVertexer-built photons)"}; + Configurable svertexerLegRule{"svertexerLegRule", false, "run 1: an SVertexer V0 candidate with a leg without ITS is fitted like a finder candidate (no AP cut, purity gate; the TPC drift correction of moved legs is useSideBasedCorrection), so that run 1 and run 2 (uniformPairFit) apply one leg-based rule to every pairing"}; + Configurable finderFreeAllLegs{"finderFreeAllLegs", false, "finder: no leg blocking at all, legs of accepted candidates stay available (leg pairs already built are not refitted), so the candidate set does not depend on the partner photons"}; + Configurable runSwapResolver{"runSwapResolver", false, "ambiguity resolver: for close stored photon pairs, fit the crossed leg pairing (no AP cut) and keep the pairing with the lower dedup score"}; + Configurable swapMaxQ{"swapMaxQ", 0.1f, "swap resolver: max q_inv of the two photons (GeV/c)"}; + Configurable swapMaxConvDist{"swapMaxConvDist", 20.f, "swap resolver: max distance of the conversion points (cm)"}; + } recovery; + + // builder run 2 of the co-reconstructed mixed event (processMixed) + struct : ConfigurableGroup { + std::string prefix = "mixed_group"; + Configurable prefilterCrossed{"prefilterCrossed", true, "crossed leg pairs (legs of two different run-1 photons): fit only if |dtgl| < recovery finderMaxDeltaTgl and |circle gap| < finderMaxCircleGap (CPU; validation: off)"}; + Configurable skipSameSourcePairs{"skipSameSourcePairs", true, "mixed event: do not count pairs whose 4 legs all come from the same source event (with pooled conflict groups a group can give two photons of one partner event: a same-event pair inside the mixed event). Data-safe (mixer event counter), pair counting only"}; + Configurable freeLegRuleMask{"freeLegRuleMask", 5, "with freeLegRun1Rules: which run-1 rules a crossed pairing with a pooled free leg gets: bit 0 AP cut, bit 1 purity gate (pairings with a leg without ITS), bit 2 finder fit options (side-based drift correction, finder PCA; pairings with a leg without ITS); 7 = all run-1 rules, 0 = the lenient crossed fit"}; + Configurable freeLegMinPurity{"freeLegMinPurity", -999.f, "with freeLegRun1Rules and bit 1 of freeLegRuleMask: purity threshold for crossed pairings with a pooled free leg; below -998 recovery.finderMinScore (as run 1)"}; + Configurable mixedDiagnostics{"mixedDiagnostics", false, "MC diagnostics of run 2 (hCrossedFit, four-leg decisions, matched configurations, mixed pair classes, fake classes, hQSym, swap/brems pointing, alternative pairings): CPU on data, no effect on the stored photons"}; + Configurable diagFreeLegFits{"diagFreeLegFits", false, "diagnostic: every crossed pairing with a pooled free leg is fitted twice as a trial, with the lenient crossed options and with the run-1 rules, and Mixed/Decision/hFreeLegFit records both results (accepted or the rejection line), split by MC truth; the trial candidates are removed again, the real fit follows the configuration (CPU and QA side effect: V0 QA histograms count the trial fits)"}; + Configurable freeLegRun1Rules{"freeLegRun1Rules", true, "crossed pairings of a pooled free leg (sourceV0Id -2) with a stored leg are fitted as run 1 would have fitted them: with a leg without ITS as a finder candidate (side-based drift correction, AP cut, purity gate), with both legs with ITS as an SVertexer V0 (AP cut, no purity gate); stored x stored pairings keep crossedRelaxAP (overlay mixed event)"}; + Configurable freeFreeFinderOnly{"freeFreeFinderOnly", false, "with skipFreeFreeCrossed off: a crossed pairing of two pooled free legs is formed only if at least one leg has no ITS (the run-1 finder domain); free x free with both legs with ITS could only have been an SVertexer V0 in run 1 and is skipped (overlay mixed event)"}; + Configurable skipFreeFreeCrossed{"skipFreeFreeCrossed", true, "crossed pairings of two pooled free legs (sourceV0Id -2 on both) are not formed: run 1 rejected these legs (finder purity gate, AP cut), the run-2 crossed fit would accept them as fakes (overlay mixed event)"}; + Configurable skipSameSourceCrossed{"skipSameSourceCrossed", true, "crossed pairings of two legs from one source event are not formed (injection mixed event, mixer cfgInject: the current event's pairings were decided by run 1, only the injected legs form new candidates; the same-event artificial event is then exactly the run-1 output). Candidate formation and the hMatched configurations of the mixed event"}; + Configurable crossedRelaxAP{"crossedRelaxAP", true, "crossed leg pairs are fitted without the AP cut, like the originals without an SVertexer V0 (symmetric fit options, control)"}; + Configurable refitOriginalsAtSourceVertex{"refitOriginalsAtSourceVertex", true, "the original pairings are refitted against the primary vertex of their own source event (position and covariance carried in PCMMixLegs), so a mixed photon enters the decision and the output exactly as it left run 1; the crossed pairings use the vertex of the artificial event"}; + Configurable uniformPairFit{"uniformPairFit", false, "decide in run 2 only: every leg pairing of an artificial event (original or crossed, inside or across pooled conflict groups) passes the same prefilter and is fitted with the same options, chosen by the legs as the run-1 finder does (a pair with a leg without ITS gets the finder options: side-based drift correction, no AP cut, purity gate); for run 1 keep-all + mixer cfgPoolGroups, so that the same and the mixed event are decided by identical code on identical kinds of candidates"}; + } mixed; + // event-level skim for derived data struct : ConfigurableGroup { std::string prefix = "skim_group"; Configurable minNv0PerCollision{"minNv0PerCollision", -1, "store V0 photons only for collisions with at least this many surviving candidates (event-level skim for derived data). negative = store all"}; - Configurable maxMinvPairPerCollision{"maxMinvPairPerCollision", -1.f, "store a collision's photons only if at least one photon pair has qinv below this (GeV/c^2). negative = disabled"}; + Configurable minMinvPairPerCollision{"minMinvPairPerCollision", -1.f, "store a collision's photons only if at least one photon pair has qinv (minv) above this (GeV/c^2). negative = disabled"}; + Configurable maxMinvPairPerCollision{"maxMinvPairPerCollision", -1.f, "store a collision's photons only if at least one photon pair has qinv (minv) below this (GeV/c^2). negative = disabled"}; } skim; // single track cuts @@ -229,13 +416,14 @@ struct PhotonConversionBuilder { struct : ConfigurableGroup { std::string prefix = "v0cut_group"; Configurable min_v0cospa_tpconly{"min_v0cospa_tpconly", 0.99, "min V0 CosPA to V0s with TPConly tracks"}; // double -> N.B. dcos(x)/dx = 0 at x=0) - Configurable min_v0cospa_its{"min_v0cospa_its", 0.99, "min V0 CosPA to V0s with ITs hits"}; // double -> N.B. dcos(x)/dx = 0 at x=0) + Configurable min_v0cospa_its{"min_v0cospa_its", 0.99, "min V0 CosPA to V0s with ITs hits"}; Configurable max_dcav0dau_tpconly{"max_dcav0dau_tpconly", 3.0, "max distance btween 2 legs to V0s with TPConly tracks"}; Configurable max_dcav0dau_its{"max_dcav0dau_its", 0.5, "max distance btween 2 legs to V0s with ITS hits"}; Configurable max_dcav0dau_itsibss{"max_dcav0dau_itsibss", 1.0, "max distance btween 2 legs to V0s with ITS hits on ITSib SS"}; Configurable max_dcav0dau_tpc_inner_fc{"max_dcav0dau_tpc_inner_fc", 1.5, "max distance btween 2 legs to V0s with ITS hits on TPC inner FC"}; Configurable min_v0radius{"min_v0radius", 1.0, "min v0 radius"}; Configurable max_v0radius{"max_v0radius", 90.0, "max v0 radius"}; + Configurable conversionPointMethod{"conversionPointMethod", 0, "0: helix touching point of the legs (Run 2), 1: radius-profiled pair fit through the material, falls back to 0 without a valid minimum"}; Configurable margin_r_its{"margin_r_its", 3.0, "margin for r cut in cm"}; Configurable margin_r_tpc{"margin_r_tpc", 7.0, "margin for r cut in cm"}; Configurable margin_r_itstpc_tpc{"margin_r_itstpc_tpc", 7.0, "margin for r cut in cm"}; @@ -287,11 +475,25 @@ struct PhotonConversionBuilder { o2::base::MatLayerCylSet* lut = nullptr; o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; o2::aod::common::TPCVDriftManager mVDriftMgr; + uint64_t mLastCCDBTimestamp{0}; + std::set mRelaxedKeys; + std::unordered_map mLegDcaCache; // leg DCA to the vertex per DF (fitV0Candidate) + std::map> mGroupOfKey; + std::vector mGroupSizeOfCand; + struct PermuteResult { + int colID = -1; + int nMembers = 0; + bool changed = false; + bool greedy = false; + }; + std::vector mPermuteResults; // run 2, mode 2: one entry per conflict group with >= 2 candidates + int mNAnomalyDump = 0; o2::framework::HistogramRegistry registry{ "registry", { {"hCollisionCounter", "hCollisionCounter", {HistType::kTH1F, {{1, 0.5f, 1.5f}}}}, + {"hNFreeLegs", "free legs filled per DF (fillFreeLegs);legs;DFs", {HistType::kTH1F, {{200, 0.f, 20000.f}}}}, {"V0/hAP", "Armenteros Podolanski;#alpha;q_{T} (GeV/c)", {HistType::kTH2F, {{200, -1.0f, 1.0f}, {250, 0, 0.25}}}}, {"V0/hConversionPointXY", "conversion point in XY;X (cm);Y (cm)", {HistType::kTH2F, {{400, -100.0f, 100.0f}, {400, -100.f, 100.f}}}}, {"V0/hConversionPointRZ", "conversion point in RZ;Z (cm);R_{xy} (cm)", {HistType::kTH2F, {{200, -100.0f, 100.0f}, {200, 0.f, 100.f}}}}, @@ -335,8 +537,8 @@ struct PhotonConversionBuilder { } LOGF(info, "photon-conversion-builder: deduplicationMode = %d (%s), score weight = %.2f", dedup.deduplicationMode.value, DedupNames[dedup.deduplicationMode.value], dedup.deduplicationScoreWeight.value); - LOGF(info, "photon-conversion-builder: minNv0PerCollision = %d, maxMinvPairPerCollision = %f", - skim.minNv0PerCollision.value, skim.maxMinvPairPerCollision.value); + LOGF(info, "photon-conversion-builder: minNv0PerCollision = %d, minMinvPairPerCollision = %f, maxMinvPairPerCollision = %f", + skim.minNv0PerCollision.value, skim.minMinvPairPerCollision.value, skim.maxMinvPairPerCollision.value); ccdb->setURL(ccdburl); ccdb->setCaching(true); @@ -446,12 +648,36 @@ struct PhotonConversionBuilder { LOG(warning) << "deduplicationScoreWeight is smaller than zero which is not allowed"; } + registry.add("V0/hSVFitReject", "SVertexer V0 fits (run 1, dedup pass): source line of the rejecting cut, 0 = accepted (CPU order of the cuts);line;V0s", kTH1F, {{2400, -0.5f, 2399.5f}}); + if (v0cuts.conversionPointMethod == 1) { + registry.add("V0/hConvPointProfile", "conversionPointMethod 1;0 fallback to the touching point, 1 profile minimum used;candidates", kTH1F, {{2, -0.5f, 1.5f}}); + registry.add("V0/hConvPointProfileShift", "conversionPointMethod 1;R_{profile} - R_{touching point} (cm);candidates", kTH1F, {{200, -10.f, 10.f}}); + } + if (recovery.runSwapResolver && dedup.deduplicationMode == V0DeduplicationMode::KeepAll) { + LOG(fatal) << "runSwapResolver needs deduplicationMode 0, 1 or 2"; + } + if (dedup.resolveSplitLegs) { + registry.add("Dedup/hSplitLegRemoved", "split legs: photons removed per DF;photons;DFs", kTH1F, {{100, -0.5f, 99.5f}}); + } + if (dedup.dedupHalfSwapRule) { + registry.add("Dedup/hHalfSwapRule", "half-swap rule: two winners replaced by the candidate on their legs;replacements per DF;DFs", kTH1F, {{100, -0.5f, 99.5f}}); + } + if (recovery.runFinder) { + registry.add("Finder/hSteps", "finder;step passed;pairs", kTH1D, {{FinderStep::kNFindStep, -0.5f, static_cast(FinderStep::kNFindStep) - 0.5f}}); + registry.add("Finder/hScore", "finder;purity score;pairs", kTH1F, {{100, -20.f, 10.f}}); + registry.add("Finder/hNAdded", "finder;photons added per DF;DFs", kTH1F, {{200, 0.f, 2000.f}}); + } + if (recovery.runSwapResolver) { + registry.add("Swap/hTested", "swap resolver;0 tested, 1 swapped;pairs", kTH1D, {{2, -0.5f, 1.5f}}); + registry.add("Swap/hQ", "swap resolver;q_{inv} (GeV/c);0 tested, 1 swapped", kTH2F, {{100, 0.f, 0.1f}, {2, -0.5f, 1.5f}}); + } + if (doprocessMC) { const AxisSpec axQ{60, 0.f, 0.3f, "q_{inv}^{true} (GeV/c)"}; const AxisSpec axDEta{80, -1.6f, 1.6f, "#Delta#eta_{#gamma#gamma}^{true}"}; const AxisSpec axClass{3, -0.5f, 2.5f, "0 = true, 1 = cross-leg fake, 2 = other fake"}; // lostByPartnerFake is a subset of lostByCrossFake, and a pair can appear in several of them. - for (const auto& nm : {"before", "bothSurvive", "bothSurviveSameColl", "oneLost", "bothLost", + for (const auto& nm : {"before", "bothSurvive", "bothSurviveSameColl", "surviveAsCrossedPairing", "halfCrossedStored", "oneLost", "bothLost", "lostByCrossFake", "lostByPartnerFake", "lostByOtherFake", "lostByTrue"}) { const std::string dir = std::string("MCDedup/Pairs/") + nm + "/"; registry.add((dir + "hQ").c_str(), "truth photon pairs with >=1 true V0 candidate before deduplication;q_{inv}^{true} (GeV/c);pairs", kTH1F, {axQ}, true); @@ -462,6 +688,17 @@ struct PhotonConversionBuilder { const AxisSpec axBlocker{4, -0.5f, 3.5f, "0 = true, 1 = cross-leg fake, 2 = other fake, 3 = no blocker (cut, not dedup)"}; registry.add("MCDedup/Photons/hFate", "fate of the true photons;0 = alive, 1 = lost (blocked), 2 = lost (no blocker);photons", kTH1F, {{3, -0.5f, 2.5f}}, true); + registry.add("MCDedup/Photons/hFateProps", "true photons with a candidate;0 alive, 1 lost to a cross-leg fake, 2 lost to another candidate, 3 lost without blocker;p_{T} (GeV/c);R_{xy} (cm);legs with ITS;pointing angle (rad);PCA (cm);other candidates sharing a leg", kTHnSparseF, {{4, -0.5f, 3.5f}, {20, 0.f, 2.f}, {18, 0.f, 90.f}, {3, -0.5f, 2.5f}, {16, 0.f, 0.08f}, {15, 0.f, 3.f}, {11, -0.5f, 10.5f}}); + registry.add("MCDedup/FourLeg/hRun1", "run 1: two true photon candidates whose crossed pairings are candidates too (MC);q_{inv} of the true photons (GeV/c);outcome: 0 both true stored, 1 both crossed stored (swap), 2 one true stored, 3 one crossed stored, 4 none, 5 other;isolated 4-candidate decision: 0 true, 1 crossed, 2 other;other candidates using one of the 4 legs;photons related (same mother, or mother e^{#pm});one true candidate is a resolver/finder photon (no SVertexer V0)", kTHnSparseF, {{10, 0.f, 0.05f}, {6, -0.5f, 5.5f}, {3, -0.5f, 2.5f}, {6, -0.5f, 5.5f}, {2, -0.5f, 1.5f}, {2, -0.5f, 1.5f}}); + registry.add("MCDedup/HalfSwap/hSource", "run 1, true photons with a candidate (MC);0 stored, 1 both legs in two other stored candidates (half-swap source), 2 one leg used, 3 no leg used, 4 both legs in one other stored candidate;score S of the best true candidate;objective: c(S_{w1}) + c(S_{w2}) - c(S_{true}), c = max(s_{0} - S, 0.001);(S_{w1} + S_{w2})/2 - S_{true};p_{T} of the true candidate (GeV/c)", kTHnSparseF, {{5, -0.5f, 4.5f}, {40, 0.f, 2.f}, {40, -0.5f, 0.5f}, {40, -1.f, 1.f}, {10, 0.f, 2.f}}); + registry.add("MCDedup/FourLeg/hRun1Origin", "run 1: as hRun1, with the origin of the candidates (MC);q_{inv} of the true photons (GeV/c);outcome: 0 both true stored, 1 swap, 2 one true stored, 3 one crossed stored, 4 none, 5 other;crossed candidates: 0 both SVertexer V0s, 1 one built by the swap resolver, 2 both built by the resolver;true candidates: 0 both SVertexer V0s, 1 one non-SVertexer, 2 both non-SVertexer;other candidates on the 4 legs (0, 1+);photons related", kTHnSparseF, {{10, 0.f, 0.05f}, {6, -0.5f, 5.5f}, {3, -0.5f, 2.5f}, {3, -0.5f, 2.5f}, {2, -0.5f, 1.5f}, {2, -0.5f, 1.5f}}); + registry.add("MCDedup/Inventory/hPhoton", "eligible true photons (MC);#it{p} (GeV/c);R_{conv} (cm);legs with ITS;fate: 0 no true candidate, 1 candidate lost, 2 stored, 3 lost but a leg in a stored crossed candidate;q_{true} to the closest other eligible photon (GeV/c)", kTHnSparseF, {{20, 0.f, 2.f}, {18, 0.f, 90.f}, {3, -0.5f, 2.5f}, {4, -0.5f, 3.5f}, {20, 0.f, 0.1f}}); + registry.add("MCDedup/Inventory/hPair", "eligible pairs of true photons (MC);q_{true} (GeV/c);|E_{1} - E_{2}| / (E_{1} + E_{2});k_{T} (GeV/c);fate: 0 both stored, 1 one stored, 2 none stored and no crossed stored, 3 both crossed stored (swap), 4 one crossed stored, 5 other;true candidates of the pair (0, 1, 2);crossed candidates of the pair (0, 1, 2)", kTHnSparseF, {{20, 0.f, 0.1f}, {10, 0.f, 1.f}, {6, 0.f, 1.5f}, {6, -0.5f, 5.5f}, {3, -0.5f, 2.5f}, {3, -0.5f, 2.5f}}); + registry.add("MCDedup/Inventory/hSwapOrigin", "eligible pairs of true photons with a stored crossed candidate (MC);q_{true} (GeV/c);fate: 0 both crossed stored (swap), 1 one crossed stored;true candidates of the pair (0, 1, 2);stored crossed: 0 SV+SV, 1 SV+finder, 2 finder+finder, 3 SV+resolver, 4 finder+resolver, 5 resolver+resolver;history of photon 1 (the higher code);history of photon 2", kTHnSparseF, {{20, 0.f, 0.1f}, {2, -0.5f, 1.5f}, {3, -0.5f, 2.5f}, {6, -0.5f, 5.5f}, {7, -0.5f, 6.5f}, {7, -0.5f, 6.5f}}); + registry.add("MCDedup/Inventory/hNeverDetail", "true photons without a true candidate in eligible pairs with q_{true} < 20 MeV (MC, entries = pairs);pair fate (as hPair);history (as hSwapOrigin);detail: SVertexer or finder fit rejection line (history 1, 5), finder step (history 2-4: -8 both ITS, -7 built, -9 not tested, else evalFinderPair)", kTH3F, {{6, -0.5f, 5.5f}, {7, -0.5f, 6.5f}, {2010, -10.5f, 1999.5f}}); + registry.add("MCDedup/Inventory/hSwapLoss", "same-event full swaps of two true photons (MC);q_{true} (GeV/c);true candidates of the pair (0, 1, 2);resolver: 0 crossed in different collisions, 1 not tested q_{rec} > swapMaxQ, 2 not tested conv. distance > swapMaxConvDist, 3 true pairing fails the prefilter, 4 true pairing rejected by a fit cut, 5 stored refit failed, 6 complete, crossed score better, 7 complete, gain > 0 but leg taken by another swap, 8 other;gain = S_{crossed} - S_{true} (resolver sign: S_{stored} - S_{alternative})", kTHnSparseF, {{20, 0.f, 0.1f}, {3, -0.5f, 2.5f}, {9, -0.5f, 8.5f}, {40, -2.f, 2.f}}); + registry.add("MCDedup/Inventory/hSwapLossCut", "same-event full swaps whose true pairing the resolver fit rejects (MC, q_{true} < 20 MeV);true candidates of the pair (0, 1, 2);rejection line in fitV0Candidate", kTH2F, {{3, -0.5f, 2.5f}, {2000, -0.5f, 1999.5f}}); + registry.add("MCDedup/Swap/hResolverTruth", "swap resolver, every tested pair of close stored photons (MC);true photons among the 2 stored;true photons among the 2 alternatives;outcome: 0 incomplete (prefilter), 1 incomplete (fit cut), 2 incomplete (stored refit), 3 complete kept, 4 swapped;gain S_{stored} - S_{alternative};q_{inv} of the stored pair (GeV/c)", kTHnSparseF, {{3, -0.5f, 2.5f}, {3, -0.5f, 2.5f}, {5, -0.5f, 4.5f}, {40, -2.f, 2.f}, {20, 0.f, 0.1f}}); registry.add("MCDedup/Photons/hBlockerClass", "blockers of a killed true photon (ENTRIES = blockers, not photons);class of the blocker;blocker entries", kTH1F, {axBlocker}, true); registry.add("MCDedup/Photons/hKillMargin", "mode 0/1 only;S_{victim} - S_{winner};kills", kTH1F, {{200, 0.f, 1.f}}, true); registry.add("MCDedup/Photons/hVictimVsWinnerPCA", "PCA of the two;PCA_{victim} (cm);PCA_{winner} (cm)", kTH2F, {{60, 0.f, 3.f}, {60, 0.f, 3.f}}, true); @@ -476,17 +713,107 @@ struct PhotonConversionBuilder { registry.add((std::string("MCDedup/Pairs/") + nm + "/hMap").c_str(), "truth photon pairs", kTHnSparseF, {axDEtaFine, axDPhi, axQ}, true); } } + if (dedup.cfgExtendedDiag) { + const AxisSpec axPt{50, 0.f, 5.f, "p_{T,#gamma} (GeV/c)"}; + const AxisSpec axQrec{50, 0.f, 0.1f, "q_{inv} (GeV/c)"}; + registry.add("MCDedup/Stored/hClassSource", "stored photons;class;0 SVertexer, 1 finder or swap;p_{T,#gamma} (GeV/c)", kTH3F, {axClass, {2, -0.5f, 1.5f}, axPt}); + registry.add("MCDedup/Stored/hPairClass", "stored photon pairs, same collision;0 true-true, 1 crossed doublet, 2 other;q_{inv} (GeV/c)", kTH2F, {{3, -0.5f, 2.5f}, axQrec}); + registry.add("MCDedup/Stored/hSwapTrueCands", "stored crossed doublets (full swaps);true pairings among the dedup candidates (0, 1, 2);q_{inv} (GeV/c)", kTH2F, {{3, -0.5f, 2.5f}, axQrec}); + registry.add("MCDedup/Copies/hChoice", "true photons with a candidate in the dedup sample;collisions with a true candidate;0 = right coll., 1 = wrong (right available), 2 = wrong (no right copy), 3 = lost", kTH2F, {{5, 0.5f, 5.5f}, {4, -0.5f, 3.5f}}); + registry.add("MCDedup/Copies/hRule", "true photons, >= 2 copies, right copy available;0 = current dedup choice, 1 = smallest |#Deltaz| of the photon line, 2 = largest xy cos(PA) (master), 3 = largest 3D cos(PA);0 = wrong, 1 = right", kTH2F, {{4, -0.5f, 3.5f}, {2, -0.5f, 1.5f}}); + registry.add("MCDedup/Copies/hPAxy", "copies of true photons with >= 2 copies;0 = wrong coll., 1 = right coll.;transverse pointing angle (rad)", kTH2F, {{2, -0.5f, 1.5f}, {200, 0.f, 0.2f}}); + registry.add("MCDedup/Copies/hPA3D", "copies of true photons with >= 2 copies;0 = wrong coll., 1 = right coll.;3D pointing angle (rad)", kTH2F, {{2, -0.5f, 1.5f}, {200, 0.f, 0.2f}}); + registry.add("MCDedup/Swap/hCategory", "swap resolver;3 #times true stored + true crossed;0 kept, 1 swapped;q_{inv} (GeV/c)", kTH3F, {{9, -0.5f, 8.5f}, {2, -0.5f, 1.5f}, axQrec}); + } + } + + if (doprocessMixed && doprocessMixedDerived) { + LOG(fatal) << "enable processMixed (AO2D input) or processMixedDerived (derived data), not both"; + } + if (doprocessMixed || doprocessMixedDerived) { + if (doprocessRec || doprocessMC || doprocessRec_OnlyIfDielectron) { + LOG(fatal) << "processMixed (builder run 2) has to run in its own instance (photon-conversion-builder-mixed) without processRec / processMC"; + } + addMixedHistograms(); } mVDriftMgr.setUseSideBasedCorrection(useSideBasedCorrection.value); } + void addMixedHistograms() + { + const AxisSpec axType{2, -0.5f, 1.5f, "artificial event (0 same, 1 mixed)"}; + registry.add("Mixed/hEvents", "artificial events", kTH1F, {axType}); + registry.add("Mixed/hNPhotonsIn", "run-1 photons given to run 2;artificial event;photons per event", kTH2F, {axType, {201, -0.5f, 200.5f}}); + registry.add("Mixed/hCandidates", "candidate cut flow;artificial event;0 original pairs, 1 original refit ok, 2 crossed pairs, 3 crossed after pre-cut, 4 crossed fit ok", kTH2F, {axType, {5, -0.5f, 4.5f}}); + registry.add("Mixed/hStored", "stored photons;artificial event;0 original pairing, 1 crossed pairing", kTH2F, {axType, {2, -0.5f, 1.5f}}); + registry.add("Mixed/hNStoredVsIn", "photons per artificial event;artificial event;photons in (run 1);photons out (run 2)", kTH3F, {axType, {101, -0.5f, 100.5f}, {101, -0.5f, 100.5f}}); + registry.add("Mixed/hNOriginalLost", "run-1 photons not stored again;artificial event;lost originals per event", kTH2F, {axType, {21, -0.5f, 20.5f}}); + registry.add("Mixed/hNCrossedStored", "crossed photons stored;artificial event;crossed photons per event", kTH2F, {axType, {21, -0.5f, 20.5f}}); + registry.add("Mixed/hOrigRelDPt", "stored original pairing vs run 1;artificial event;p_{T}^{run 2} / p_{T}^{run 1} - 1", kTH2F, {axType, {400, -0.02f, 0.02f}}); + registry.add("Mixed/hOrigDPhi", "stored original pairing vs run 1;artificial event;#varphi^{run 2} - #varphi^{run 1} (rad)", kTH2F, {axType, {400, -0.02f, 0.02f}}); + registry.add("Mixed/hOrigDEta", "stored original pairing vs run 1;artificial event;#eta^{run 2} - #eta^{run 1}", kTH2F, {axType, {400, -0.02f, 0.02f}}); + registry.add("Mixed/hOrigExact", "stored original pairing vs run 1;artificial event;0 differs, 1 identical p (float)", kTH2F, {axType, {2, -0.5f, 1.5f}}); + registry.add("Mixed/hCrossedSourceQ", "stored crossed photons: the two run-1 photons of its legs;artificial event;q_{inv} (GeV/c)", kTH2F, {axType, {200, 0.f, 0.2f}}); + registry.add("Mixed/hCrossedSourceDist", "stored crossed photons: the two run-1 photons of its legs;artificial event;distance of the conversion points (cm)", kTH2F, {axType, {200, 0.f, 40.f}}); + registry.add("Mixed/hCrossedSourceEvents", "stored crossed photons;artificial event;legs from 0 the same event, 1 two events", kTH2F, {axType, {2, -0.5f, 1.5f}}); + registry.add("Mixed/hStoredTruth", "MC, stored photons;artificial event;0 original true, 1 original fake, 2 crossed true, 3 crossed fake, 4 no label", kTH2F, {axType, {5, -0.5f, 4.5f}}); + registry.add("Mixed/hTimeCandidates", "CPU of the candidate fits;artificial event;time per event (ms)", kTH2F, {axType, {1000, 0.f, 1000.f}}); + registry.add("Mixed/hTimeDF", "CPU of run 2 per DF;time (ms)", kTH1F, {{1000, 0.f, 100000.f}}); + const AxisSpec axPairTruth{5, -0.5f, 4.5f, "pair (0 all, 1 two true photons, 2 full swap of two true photons, 3 other with a fake, 4 no label)"}; + const AxisSpec axQ{500, 0.f, 0.5f, "q_{inv} (GeV/c)"}; + registry.add("Mixed/Pairs/hQBefore", "photon pairs before run 2 (run-1 photons);artificial event", kTH3F, {axType, axPairTruth, axQ}); + registry.add("Mixed/Pairs/hQAfter", "photon pairs after run 2;artificial event", kTH3F, {axType, axPairTruth, axQ}); + const AxisSpec axAlt{9, -0.5f, 8.5f, "3 #times alternatives passing the fit + (0 kept, 1 changed, 2 other)"}; + const AxisSpec axQLow{100, 0.f, 0.1f, "q_{inv} of the input pair (GeV/c)"}; + registry.add("Mixed/Pairs/hAltTrueTrue", "input pairs of two true photons;artificial event", kTH3F, {axType, axAlt, axQLow}); + registry.add("Mixed/Pairs/hFakeClass", "photon pairs by MC class (MC);artificial event;0 before / 1 after run 2;class (see mixFakeClass);q_{inv} (GeV/c);legs with ITS;crossed pairing passed the run-2 fit", kTHnSparseF, {axType, {2, -0.5f, 1.5f}, {18, -0.5f, 17.5f}, {200, 0.f, 0.2f}, {5, -0.5f, 4.5f}, {2, -0.5f, 1.5f}}); + const AxisSpec axDR{std::vector{0., 0.5, 1., 2., 3., 5., 10., 20., 50., 200.}, "|R_{A} - R_{B}| (cm)"}; + registry.add("Mixed/Pairs/hFakeClassOrder", "photon pairs by MC class and conversion order (MC);artificial event;0 before / 1 after run 2;class (see mixFakeClass);q_{inv} (GeV/c);0 softer photon at the smaller radius (kept), 1 softer photon at the larger radius;|R_{A} - R_{B}| (cm)", kTHnSparseF, {axType, {2, -0.5f, 1.5f}, {18, -0.5f, 17.5f}, {40, 0.f, 0.2f}, {2, -0.5f, 1.5f}, axDR}); + registry.add("Mixed/Pairs/hAltSwap", "input pairs = full swap of two true photons;artificial event", kTH3F, {axType, axAlt, axQLow}); + registry.add("Mixed/Pairs/hFakeClassGeo", "photon pairs by MC class and geometry (MC);artificial event;0 before / 1 after run 2;class (see mixFakeClass);q_{inv} (GeV/c);crossed pairing passed the run-2 fit;same physical-primary ancestor;conversion-point distance (cm);log_{10} closest same-sign legs #sqrt{#Delta#varphi^{2}+#Deltatan#lambda^{2}};max TPC shared-cluster fraction", kTHnSparseF, {axType, {2, -0.5f, 1.5f}, {18, -0.5f, 17.5f}, {50, 0.f, 0.1f}, {2, -0.5f, 1.5f}, {2, -0.5f, 1.5f}, {40, 0.f, 40.f}, {30, -5.f, 1.f}, {10, 0.f, 1.f}}); + registry.add("Mixed/Pairs/hGroupCtx", "photon pairs after run 2, conflict group of the run-2 dedup (MC);artificial event;class (see mixFakeClass);q_{inv} (GeV/c);crossed pairing passed the run-2 fit;same conflict group;candidates in the group;legs in the group;photons stored from the group", kTHnSparseF, {axType, {18, -0.5f, 17.5f}, {20, 0.f, 0.1f}, {2, -0.5f, 1.5f}, {2, -0.5f, 1.5f}, {20, 0.5f, 20.5f}, {15, 3.5f, 33.5f}, {10, 0.5f, 10.5f}}); + registry.add("Mixed/Decision/hFourLeg", "pairs of run-1 photons whose two crossed pairings also passed the fit (MC), selected before the decision;artificial event;MC: 0 original assignment = two conversions, 1 crossed assignment = two conversions, 2 neither;isolated 4-candidate decision: 0 original, 1 crossed, 2 other;actual decision: 0 original stored, 1 crossed stored, 2 other;q_{inv} original assignment (GeV/c);q_{inv} crossed assignment (GeV/c);candidates in the conflict group;S_{crossed} - S_{original};an original was fitted without the AP cut", kTHnSparseF, {axType, {3, -0.5f, 2.5f}, {3, -0.5f, 2.5f}, {3, -0.5f, 2.5f}, {50, 0.f, 0.1f}, {50, 0.f, 0.1f}, {12, 3.5f, 15.5f}, {80, -1.f, 1.f}, {2, -0.5f, 1.5f}}); + registry.add("Mixed/Decision/hFourLegKin", "4-leg configurations with min(q_{orig}, q_{crossed}) < 50 MeV (MC);artificial event;MC: 0 original = two conversions, 1 crossed = two conversions, 2 neither;isolated decision: 0 original, 1 crossed, 2 other;#Sigma PCA_{crossed} - #Sigma PCA_{original} (cm);#Sigma #theta_{PA, crossed} - #Sigma #theta_{PA, original} (rad);|#DeltaR_{xy}| of the conversions (cm);|#Deltaz| of the conversions (cm);momentum asymmetry of the more similar same-sign legs;photons related by MC history (brems, siblings)", kTHnSparseF, {axType, {3, -0.5f, 2.5f}, {3, -0.5f, 2.5f}, {40, -2.f, 2.f}, {40, -0.1f, 0.1f}, {20, 0.f, 20.f}, {20, 0.f, 20.f}, {10, 0.f, 1.f}, {2, -0.5f, 1.5f}}); + { + const AxisSpec axTruth2{2, -0.5f, 1.5f, "MC: 0 original = two conversions, 1 crossed = two conversions"}; + const AxisSpec axChoice{3, -0.5f, 2.5f, "isolated decision: 0 genuine assignment, 1 wrong assignment, 2 other"}; + const AxisSpec axRel2{2, -0.5f, 1.5f, "photons related by MC history"}; + registry.add("Mixed/Decision/hFourLegTruth", "4-leg configurations with a genuine assignment, min(q) < 50 MeV (MC);artificial event", kTHnSparseF, {axType, axTruth2, axChoice, {4, -0.5f, 3.5f, "MC relation: 0 none, 1 radiated by a leg, 2 siblings, 3 leg daughter of a leg"}, {10, 0.f, 0.05f, "q_{inv} of the genuine assignment (GeV/c)"}, {9, -0.5f, 8.5f, "3 #times (genuine photons with S #geq s_{0}) + wrong candidates with S #geq s_{0}"}, {24, -60.f, 60.f, "#Sigma#theta_{PA} wrong - genuine (mrad)"}, {20, -1.f, 1.f, "#Sigma PCA wrong - genuine (cm)"}, {std::vector{0., 0.005, 0.05, 0.1, 0.2, 0.4, 1.}, "max TPC shared-cluster fraction of the 4 legs"}}); + registry.add("Mixed/Decision/hFourLegTrack", "4-leg configurations with a genuine assignment, min(q) < 50 MeV (MC);artificial event", kTHnSparseF, {axType, axTruth2, axChoice, axRel2, {15, -2.f, 1.5f, "log_{10} min distance in the TPC of the legs the wrong assignment pairs (cm)"}, {15, -2.f, 1.5f, "log_{10} min distance in the TPC of the same-sign legs (cm)"}, {5, -0.5f, 4.5f, "legs with ITS"}, {std::vector{-10., 722., 2228., 1.e6}, "occupancy of the artificial event (terciles)"}}); + registry.add("Mixed/Decision/hFourLegResid", "4-leg configurations with a genuine assignment, min(q) < 50 MeV (MC), residuals signed toward the partner;artificial event", kTHnSparseF, {axType, axTruth2, axChoice, axRel2, {20, -0.01f, 0.01f, "#varphi residual photon 1 (rad)"}, {20, -0.01f, 0.01f, "#varphi residual photon 2 (rad)"}, {20, -0.01f, 0.01f, "#eta residual photon 1"}, {20, -0.01f, 0.01f, "#eta residual photon 2"}}); + registry.add("Mixed/Decision/hFourLegAlt", "4-leg configurations with a genuine assignment, min(q) < 50 MeV (MC), isolated decision with other scores;artificial event", kTHnSparseF, {axType, axTruth2, axRel2, {3, -0.5f, 2.5f, "baseline: 0 genuine, 1 wrong, 2 other"}, {3, -0.5f, 2.5f, "pointing only (w = 1)"}, {3, -0.5f, 2.5f, "PCA only (w = 0)"}, {3, -0.5f, 2.5f, "baseline score, raw-sum objective"}, {3, -0.5f, 2.5f, "baseline weight without gap term"}}); + registry.add("Mixed/Decision/hFourLegOrigin", "4-leg configurations with a genuine assignment, min(q) < 50 MeV (MC), common origin of the two genuine photons;artificial event", kTHnSparseF, {axType, axTruth2, axChoice, {4, -0.5f, 3.5f, "MC relation: 0 none, 1 radiated by a leg, 2 siblings, 3 leg daughter of a leg"}, {10, 0.f, 0.05f, "q_{inv} of the genuine assignment (GeV/c)"}, {5, -1.5f, 3.5f, "common origin: -1 n/a, 0 none, 1 same chain root, 2 same great-grandmother, 3 same grandmother"}, {8, -0.5f, 7.5f, "PDG of the chain root: 0 other, 1 pi0, 2 eta, 3 omega, 4 eta', 5 rho/K*, 6 K0, 7 baryon"}}); + registry.add("Mixed/Decision/hFourLegPoint", "4-leg configurations with a genuine assignment, min(q) < 50 MeV (MC), pointing to the PV split into transverse (#varphi) and longitudinal (#lambda) parts;artificial event", kTHnSparseF, {axType, axTruth2, axChoice, axRel2, {5, 0.f, 0.05f, "q_{inv} of the genuine assignment (GeV/c)"}, {24, -30.f, 30.f, "#Sigma|#Delta#varphi| wrong - genuine (mrad)"}, {24, -30.f, 30.f, "#Sigma|#Delta#lambda| wrong - genuine (mrad)"}, {20, -20.f, 20.f, "#Delta#lambda genuine photon 1 (mrad)"}, {20, -20.f, 20.f, "#Delta#lambda genuine photon 2 (mrad)"}, {20, -20.f, 20.f, "#Delta#varphi genuine photon 1 (mrad)"}, {20, -20.f, 20.f, "#Delta#varphi genuine photon 2 (mrad)"}}); + } + registry.add("Mixed/Decision/hFreeLegFit", "crossed pairings with a pooled free leg (run 2, mixed_group.diagFreeLegFits): trial fits with both option sets (MC);artificial event;truth: 0 legs of two genuine photons with q_{true} < 50 MeV (swap ingredient), 1 other;lenient crossed fit: 0 accepted, else rejection line;run-1 rules: 0 accepted, else rejection line;legs with ITS;free legs in the pairing (1, 2)", kTHnSparseF, {axType, {2, -0.5f, 1.5f}, {2000, -0.5f, 1999.5f}, {2000, -0.5f, 1999.5f}, {3, -0.5f, 2.5f}, {2, 0.5f, 2.5f}}); + registry.add("Mixed/Decision/hCrossedFit", "crossed leg pairings in run 2: result of the fit (MC);artificial event;two genuine photons, q_{true} < 50 MeV;0 accepted, 1 prefilter (only pairs inside the tgl window are visited), else source line of the rejection in fitV0Candidate;legs with ITS;q_{true} of the two photons (GeV/c)", kTHnSparseF, {axType, {2, -0.5f, 1.5f}, {1600, -0.5f, 1599.5f}, {3, -0.5f, 2.5f}, {5, 0.f, 0.05f}}); + registry.add("Mixed/Decision/hMatched", "two genuine MC photons whose 4 legs are all in the artificial event (same definition for SE and ME, q from the MC photons);artificial event;q_{true} (GeV/c);run-1 input pairing: 0 both genuine photons, 1 swapped (both crossed), 2 other;genuine pairings fitted in run 2 (0-2);crossed pairings fitted in run 2 (0-2);run-2 outcome: 0 both genuine stored, 1 both crossed stored, 2 other;photons related (same mother, or one radiated by a leg of the other);PV contributors of the event (charged multiplicity);photons in the artificial event;occupancy of the event;occupancy of the source event - current event (signed, the photon with the larger difference; 0 for SE);TPC-only legs among the 4", kTHnSparseF, {axType, {10, 0.f, 0.05f}, {3, -0.5f, 2.5f}, {3, -0.5f, 2.5f}, {3, -0.5f, 2.5f}, {3, -0.5f, 2.5f}, {2, -0.5f, 1.5f}, {std::vector{0., 50., 100., 200., 400., 800., 1200., 1600., 2000., 2500., 3000., 5000.}, "PV contributors"}, {12, 0.f, 60.f, "photons in the artificial event"}, {std::vector{-10., 722., 2228., 1.e6}, "occupancy"}, {std::vector{-20000., -3000., -2000., -1000., -500., -250., 250., 500., 1000., 2000., 3000., 20000.}, "occupancy partner - current"}, {5, -0.5f, 4.5f, "TPC-only legs"}}); + registry.add("Mixed/Decision/hMatchedCE", "two genuine MC photons whose 4 legs are all in the artificial event, central-electrode variables;artificial event;q_{true} (GeV/c);run-1 input pairing: 0 both genuine photons, 1 swapped, 2 other;run-2 outcome: 0 both genuine stored, 1 both crossed stored, 2 other;photons related;min |#eta| of the two MC photons;min |z| of the two true conversion points (cm);legs crossing the central electrode in the TPC (of 4);TPC-only legs", kTHnSparseF, {axType, {5, 0.f, 0.05f}, {3, -0.5f, 2.5f}, {3, -0.5f, 2.5f}, {2, -0.5f, 1.5f}, {std::vector{0., 0.05, 0.1, 0.15, 0.2, 0.3, 0.4, 0.6, 0.9, 2.}, "min |#eta|"}, {std::vector{0., 1., 2., 5., 10., 20., 50., 300.}, "min |z_{conv}| (cm)"}, {5, -0.5f, 4.5f}, {5, -0.5f, 4.5f}}); + registry.add("Mixed/Decision/hMatchedLegs", "two genuine MC photons whose 4 legs are all in the artificial event, partner of each leg in the stored photons;artificial event;q_{true} (GeV/c);run-1 input pairing: 0 both genuine photons, 1 swapped, 2 other;run-2 outcome: 0 both genuine stored, 1 both crossed stored, 2 other;photons related;legs not stored;legs stored with their own genuine partner;legs stored with the crossed partner (other photon of the pair);legs stored with a leg of a third genuine photon (both legs in the event);legs stored with a leg of an incomplete MC photon (arrived inside a stored fake);legs stored with a leg without photon mother", kTHnSparseF, {axType, {5, 0.f, 0.05f}, {3, -0.5f, 2.5f}, {3, -0.5f, 2.5f}, {2, -0.5f, 1.5f}, {5, -0.5f, 4.5f}, {5, -0.5f, 4.5f}, {5, -0.5f, 4.5f}, {5, -0.5f, 4.5f}, {5, -0.5f, 4.5f}, {5, -0.5f, 4.5f}}); + registry.add("Mixed/Decision/hFourLegAll", "pairs of run-1 photons whose two crossed pairings are candidates INCLUDING those appended by the run-2 resolvers (MC);artificial event;MC: 0 original = two conversions, 1 crossed = two conversions, 2 neither;actual decision: 0 original stored, 1 crossed stored, 2 other;q_{inv} of the genuine (truth 0: original, 1: crossed) assignment (GeV/c);crossed candidates: 0 both from the run-2 fit, 1 one appended by a resolver, 2 both appended;original photons: 0 both SVertexer V0s in run 1, 1 one not, 2 both not", kTHnSparseF, {axType, {3, -0.5f, 2.5f}, {3, -0.5f, 2.5f}, {10, 0.f, 0.05f}, {3, -0.5f, 2.5f}, {3, -0.5f, 2.5f}}); + registry.add("Mixed/Decision/hPermute", "conflict groups of the run-2 dedup with >= 2 candidates;artificial event;candidates;reversed order selects other candidates;greedy (no enumeration)", kTHnSparseF, {axType, {30, 0.5f, 30.5f}, {2, -0.5f, 1.5f}, {2, -0.5f, 1.5f}}); + registry.add("Mixed/Pairs/hPairOrigin", "photon pairs after run 2 by MC class and common origin of the two MC photons (MC);artificial event;MC class (see mixFakeClass);q_{inv} (GeV/c);common origin: -1 same photon / no label, 0 none, 1 same chain root, 2 same great-grandmother, 3 same grandmother", kTHnSparseF, {axType, {18, -0.5f, 17.5f}, {50, 0.f, 0.1f}, {5, -1.5f, 3.5f}}); + registry.add("Mixed/Pairs/hSameSource", "mixed event after run 2: pairs whose 4 legs all come from one source event (same-event pairs inside the mixed event, from pooled groups);q_{inv} (GeV/c);MC class (see mixFakeClass)", kTH2F, {{200, 0.f, 0.2f}, {18, -0.5f, 17.5f}}); + registry.add("Mixed/Pairs/hQSym", "photon pairs after run 2, swap-symmetric counting: a stored pair whose two crossed pairings (same 4 legs) also passed the run-2 fit counts 1/2 at its q and 1/2 at q of the crossed pairing;artificial event;MC class of the stored pair (see mixFakeClass);q_{inv} (GeV/c)", kTH3F, {axType, {18, -0.5f, 17.5f}, {500, 0.f, 0.5f}}); + registry.add("Mixed/Pairs/hQClean", "photon pairs, all legs TPC shared fraction < 0.4;artificial event;0 before / 1 after run 2;q_{inv} (GeV/c)", kTH3F, {axType, {2, -0.5f, 1.5f}, {500, 0.f, 0.5f}}); + registry.add("Mixed/Pairs/hBrems", "photon pairs, bremsstrahlung pointing;artificial event;0 before / 1 after run 2;class (see mixFakeClass);MC relation (0 none, 1 radiated by a leg, 2 siblings, 3 leg daughter of a leg);q_{inv} (GeV/c);log_{10} d_{min} (cm);log_{10} angle to the leg (rad);radius of the closest point (cm);B converts further out;p_{T} of B (GeV/c);all legs TPC shared fraction < 0.4", kTHnSparseF, {axType, {2, -0.5f, 1.5f}, {18, -0.5f, 17.5f}, {4, -0.5f, 3.5f}, {50, 0.f, 0.1f}, {40, -3.f, 2.f}, {40, -4.f, 0.5f}, {45, 0.f, 90.f}, {2, -0.5f, 1.5f}, {20, 0.f, 1.f}, {2, -0.5f, 1.5f}}); + registry.add("Mixed/Pairs/hSwapDecision", "full swaps after run 2 (classes 14 and 17);artificial event;0 created in run 2, 1 carried from run 1, 2 one of each;0 true pairings scored better, 1 swap scored better, 2 a true pairing failed the fit;q_{inv} (GeV/c);all legs TPC shared fraction < 0.4", kTHnSparseF, {axType, {3, -0.5f, 2.5f}, {3, -0.5f, 2.5f}, {50, 0.f, 0.1f}, {2, -0.5f, 1.5f}}); + } + void initCCDB(MyBCs::iterator const& bc) { - if (mRunNumber == bc.runNumber()) { + initCCDBFor(bc.runNumber(), bc.timestamp(), bc.grpMagField()); + } + + // run number, timestamp and GRPMagField of a BC (AO2D) or of a PMEvents row with EmMagFields (derived data, builder run 2) + template + void initCCDBFor(const int runNumber, const uint64_t timestamp, TGrpMagField const& grpMagField) + { + if (mRunNumber == runNumber) { return; } - mRunNumber = bc.runNumber(); + mRunNumber = runNumber; // In case override, don't proceed, please - no CCDB access required if (d_bz_input > -990) { // o2-linter: disable=magic-number (override value) d_bz = d_bz_input; @@ -499,10 +826,11 @@ struct PhotonConversionBuilder { return; } - auto run3grp_timestamp = bc.timestamp(); - o2::base::Propagator::initFieldFromGRP(&bc.grpMagField()); + auto run3grp_timestamp = timestamp; + o2::parameters::GRPMagField grp = grpMagField; // copy: getNominalL3Field is not const + o2::base::Propagator::initFieldFromGRP(&grp); // Fetch magnetic field from ccdb for current collision - d_bz = bc.grpMagField().getNominalL3Field(); + d_bz = grp.getNominalL3Field(); LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; if (useMatCorrType == 2) { // o2-linter: disable=magic-number (material budget correction) @@ -518,9 +846,13 @@ struct PhotonConversionBuilder { void updateCCDB(MyBCs::iterator const& bc) { - auto timestamp = bc.timestamp(); + updateCCDBAt(bc.timestamp()); + } + void updateCCDBAt(const uint64_t timestamp) + { mVDriftMgr.update(timestamp); + mLastCCDBTimestamp = timestamp; } std::pair> its_ib_Requirement = {0, {0, 1, 2}}; // no hit on 3 ITS ib layers. @@ -644,37 +976,135 @@ struct PhotonConversionBuilder { } } + void fillLegCovTable(o2::track::TrackParCov const& trackParCov) + { + const auto cov = trackParCov.getCov(); + v0legsCov(trackParCov.getX(), trackParCov.getAlpha(), trackParCov.getY(), trackParCov.getZ(), + trackParCov.getSnp(), trackParCov.getTgl(), trackParCov.getQ2Pt(), cov.data()); + } + + // free legs (fillFreeLegs): after the tables are written; a track that passes the leg cuts, has a collision and the TPC (as the + // finder takes them) and is the leg of no stored photon; TPC-only legs moved to their own collision as in the fit + template + void writeFreeLegs(TCollisions const& collisions, TTracks const& tracks, std::set const& skimRejected) + { + std::set usedLegs; + for (const auto& key : stored_fullv0Ids) { + usedLegs.insert(key.posId); + usedLegs.insert(key.eleId); + } + int nFree = 0; + int64_t lastFreeLegTrack = 0; // PCMFreeLegIds: track index stored as the difference to the previous row of this DF + for (const auto& track : tracks) { + if (track.collisionId() < 0 || !track.hasTPC() || usedLegs.contains(track.globalIndex()) || skimRejected.contains(track.collisionId()) || + track.pt() < trackcuts.min_pt_trackiu || !checkV0leg(track)) { + continue; + } + const auto collision = collisions.rawIteratorAt(track.collisionId()); + if (!collision.isSelected()) { // the mixer only takes selected events (PMEvents): free legs of other collisions are never used + continue; + } + auto par = getTrackParCov(track); + if constexpr (!std::is_void_v) { + if (trackcuts.moveTPCTracks && o2::pwgem::photonmeson::isTPConlyTrack(track) && !mVDriftMgr.moveTPCTrack(collision, track, par)) { + continue; + } + } + if (fillFreeLegIds) { // tracks are visited in increasing index order: store the difference to the previous free leg of this DF + pcmfreelegids(track.collisionId(), static_cast(track.globalIndex() - lastFreeLegTrack)); + lastFreeLegTrack = track.globalIndex(); + } + if (!fillFreeLegs) { + ++nFree; + continue; + } + const auto cov = par.getCov(); + pcmfreelegs(track.collisionId(), track.globalIndex(), track.sign(), + par.getX(), par.getAlpha(), par.getY(), par.getZ(), par.getSnp(), par.getTgl(), par.getQ2Pt(), cov.data(), + track.tpcNClsFindable(), track.tpcNClsFindableMinusFound(), track.tpcNClsFindableMinusCrossedRows(), track.tpcNClsShared(), + track.tpcChi2NCl(), track.tpcInnerParam(), track.tpcSignal(), track.tpcNSigmaEl(), track.tpcNSigmaPi(), + track.itsClusterSizes(), track.itsChi2NCl(), track.detectorMap()); + ++nFree; + } + registry.fill(HIST("hNFreeLegs"), static_cast(nFree)); + } + template void fillV0Table(TV0 const& v0, const bool filltable) { - using namespace o2::pwgem::photonmeson; - // Get tracks const auto& pos = v0.template posTrack_as(); const auto& ele = v0.template negTrack_as(); - const auto& collision = v0.template collision_as(); // collision where this v0 belongs to. - // LOGF(info, "v0.collisionId() = %d, pos.collisionId() = %d, ele.collisionId() = %d", v0.collisionId(), pos.collisionId(), ele.collisionId()); + const auto& collision = v0.template collision_as(); // collision where this v0 belongs to. + const bool finderLike = recovery.svertexerLegRule.value && !(pos.hasITS() && ele.hasITS()); // one leg-based rule for every pairing + fitV0Candidate(collision, pos, ele, v0.globalIndex(), filltable, + FitOptions{.nonSVertexer = finderLike, .relaxAP = finderLike, .purityGate = finderLike && recovery.runFinder.value}); + if (!filltable) { + registry.fill(HIST("V0/hSVFitReject"), static_cast(mLastFitReject)); + } + if constexpr (isMC) { + if (auto* t = filltable ? nullptr : trueLegTrace(pos.globalIndex(), ele.globalIndex()); t != nullptr) { + t->svLine = t->svLine == 0 ? 0 : mLastFitReject; // accepted in any collision wins + } + } + } + + // the purity score (finder LLR tables) is only needed for the finder gate and the split-leg order + bool usePurity() const { return recovery.runFinder.value || dedup.resolveSplitLegs.value; } + + template + static float purityScore(TTrack const& pos, TTrack const& ele, const float cosPA3D, const float pca, const float mee, const float gap, const float psipair) + { + return o2::pwgem::photonmeson::finderPurityScore({cosPA3D, pca, mee, gap, std::fabs(pos.tgl() - ele.tgl()), psipair, + std::max(std::fabs(pos.tpcNSigmaEl()), std::fabs(ele.tpcNSigmaEl())), + std::min(std::fabs(pos.tpcNSigmaPi()), std::fabs(ele.tpcNSigmaPi()))}); + } + + // deduplication score, lower = better: master score (cosPA, PCA) + optional circle-gap term + float dedupScore(const float cosPA, const float pca, const float gap) + { + float score = getScoreV0(cosPA, pca, dedup.deduplicationScoreWeight); + if (dedup.gapWeight > 0.f) { + score += dedup.gapWeight * std::min(std::fabs(gap) / dedup.gapScale.value, 3.f); // o2-linter: disable=magic-number (cap) + } + return score; + } + + // fit of one leg pair with the V0 cuts + template + void fitV0Candidate(TCollision const& collision, TTrack const& pos, TTrack const& ele, const int64_t v0Id, const bool filltable, FitOptions const& opt = {}) + { + using namespace o2::pwgem::photonmeson; + mVDriftMgr.setUseSideBasedCorrection(opt.nonSVertexer || useSideBasedCorrection.value); + mLastFitReject = 0; + const float pvX = opt.pv ? opt.pv->posX() : collision.posX(), pvY = opt.pv ? opt.pv->posY() : collision.posY(), pvZ = opt.pv ? opt.pv->posZ() : collision.posZ(); if (pos.sign() * ele.sign() > 0) { // reject same sign pair + mLastFitReject = __LINE__; // rejection point return; } if (pos.pt() < trackcuts.min_pt_trackiu || ele.pt() < trackcuts.min_pt_trackiu) { + mLastFitReject = __LINE__; // rejection point return; } if (pos.globalIndex() == ele.globalIndex()) { + mLastFitReject = __LINE__; // rejection point return; } if (isITSonlyTrack(pos) && !ele.hasITS()) { + mLastFitReject = __LINE__; // rejection point return; } if (isITSonlyTrack(ele) && !pos.hasITS()) { + mLastFitReject = __LINE__; // rejection point return; } if (!checkV0leg(pos) || !checkV0leg(ele)) { + mLastFitReject = __LINE__; // rejection point return; } @@ -689,23 +1119,44 @@ struct PhotonConversionBuilder { std::array dcaInfo{}; auto pTrack = getTrackParCov(pos); - if (trackcuts.moveTPCTracks && isTPConlyTrack(pos) && !mVDriftMgr.moveTPCTrack(collision, pos, pTrack)) { - LOGP(error, "failed correction for positive tpc track"); + bool failedMovePos = false; + if constexpr (!std::is_void_v) { // TBCs = void: legs of builder run 2, already moved in run 1 + failedMovePos = trackcuts.moveTPCTracks && isTPConlyTrack(pos) && !mVDriftMgr.moveTPCTrack(collision, pos, pTrack); + } + if (failedMovePos) { + if (opt.probe == nullptr) { + LOGP(error, "failed correction for positive tpc track"); + } + mLastFitReject = __LINE__; // rejection point (diagnostic) return; } + const bool sideBased = opt.nonSVertexer || useSideBasedCorrection.value; + const bool cacheDca = modeTrackPropagation == TrackPropMode::kProper; + std::optional posAtDca, eleAtDca; // legs at their DCA to the vertex (reused for phi_V / psi_pair) + const LegDcaKey posKey{pos.globalIndex(), collision.globalIndex(), pvX, pvY, pvZ, sideBased}; + const LegAtDca* posCached = nullptr; + if (cacheDca) { + if (const auto it = mLegDcaCache.find(posKey); it != mLegDcaCache.end()) { + posCached = &it->second; + } + } auto pTrackC = pTrack; - o2::math_utils::Point3D vtxPrim{ - collision.posX(), - collision.posY(), - collision.posZ()}; + o2::math_utils::Point3D vtxPrim{pvX, pvY, pvZ}; if (modeTrackPropagation != TrackPropMode::kProper) { dcaInfo = CalculateDCAFast(pTrackC, vtxPrim, d_bz); } pTrackC.setPID(o2::track::PID::Electron); std::array dcaInfoFast = dcaInfo; - if (modeTrackPropagation != TrackPropMode::kFast) { - o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, pTrackC, 2.f, matCorr, &dcaInfo); + if (posCached != nullptr) { + dcaInfo = posCached->dca; + posAtDca = posCached->trk; + } else if (modeTrackPropagation != TrackPropMode::kFast) { + const bool propOk = o2::base::Propagator::Instance()->propagateToDCABxByBz({pvX, pvY, pvZ}, pTrackC, 2.f, matCorr, &dcaInfo); + if (cacheDca && propOk) { // a failed propagation leaves dcaInfo as it was: not cached + mLegDcaCache.emplace(posKey, LegAtDca{dcaInfo, pTrackC}); + posAtDca = pTrackC; + } if (filltable && modeTrackPropagation == TrackPropMode::kBoth) { registry.fill(HIST("V0Leg/hDCAxyPropagationCompare"), dcaInfo[0], dcaInfoFast[0]); registry.fill(HIST("V0Leg/hDCAzPropagationCompare"), dcaInfo[1], dcaInfoFast[1]); @@ -715,10 +1166,24 @@ struct PhotonConversionBuilder { auto posdcaZ = dcaInfo[1]; auto nTrack = getTrackParCov(ele); - if (trackcuts.moveTPCTracks && isTPConlyTrack(ele) && !mVDriftMgr.moveTPCTrack(collision, ele, nTrack)) { - LOGP(error, "failed correction for negative tpc track"); + bool failedMoveEle = false; + if constexpr (!std::is_void_v) { // TBCs = void: legs of builder run 2, already moved in run 1 + failedMoveEle = trackcuts.moveTPCTracks && isTPConlyTrack(ele) && !mVDriftMgr.moveTPCTrack(collision, ele, nTrack); + } + if (failedMoveEle) { + if (opt.probe == nullptr) { + LOGP(error, "failed correction for negative tpc track"); + } + mLastFitReject = __LINE__; // rejection point (diagnostic) return; } + const LegDcaKey eleKey{ele.globalIndex(), collision.globalIndex(), pvX, pvY, pvZ, sideBased}; + const LegAtDca* eleCached = nullptr; + if (cacheDca) { + if (const auto it = mLegDcaCache.find(eleKey); it != mLegDcaCache.end()) { + eleCached = &it->second; + } + } auto nTrackC = nTrack; if (modeTrackPropagation != TrackPropMode::kProper) { dcaInfo = CalculateDCAFast(nTrackC, vtxPrim, d_bz); @@ -726,8 +1191,15 @@ struct PhotonConversionBuilder { nTrackC.setPID(o2::track::PID::Electron); dcaInfoFast = dcaInfo; - if (modeTrackPropagation != TrackPropMode::kFast) { - o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, nTrackC, 2.f, matCorr, &dcaInfo); + if (eleCached != nullptr) { + dcaInfo = eleCached->dca; + eleAtDca = eleCached->trk; + } else if (modeTrackPropagation != TrackPropMode::kFast) { + const bool propOk = o2::base::Propagator::Instance()->propagateToDCABxByBz({pvX, pvY, pvZ}, nTrackC, 2.f, matCorr, &dcaInfo); + if (cacheDca && propOk) { // a failed propagation leaves dcaInfo as it was: not cached + mLegDcaCache.emplace(eleKey, LegAtDca{dcaInfo, nTrackC}); + eleAtDca = nTrackC; + } if (filltable && modeTrackPropagation == TrackPropMode::kBoth) { registry.fill(HIST("V0Leg/hDCAxyPropagationCompare"), dcaInfo[0], dcaInfoFast[0]); registry.fill(HIST("V0Leg/hDCAzPropagationCompare"), dcaInfo[1], dcaInfoFast[1]); @@ -737,85 +1209,44 @@ struct PhotonConversionBuilder { auto eledcaZ = dcaInfo[1]; if (std::fabs(posdcaXY) < trackcuts.dcapostopv || std::fabs(eledcaXY) < trackcuts.dcanegtopv) { + mLastFitReject = __LINE__; // rejection point (diagnostic) return; } std::array xyz = {0.f, 0.f, 0.f}; + if (v0cuts.conversionPointMethod != 1) { + // Calculate the conversion point as the weighted average of the two legs' closest approach points -- avoids expensice KFParticle construction for every candidate + const float bzNominal = o2::base::Propagator::Instance()->getNominalBz(); + const o2::track::TrackAuxPar cP(pTrack, bzNominal), cN(nTrack, bzNominal); + const float xC = (cP.xC * cN.rC + cN.xC * cP.rC) / (cP.rC + cN.rC); + const float yC = (cP.yC * cN.rC + cN.yC * cP.rC) / (cP.rC + cN.rC); + if (RecoDecay::sqrtSumOfSquares(xC, yC) > trackcuts.maxX + v0cuts.margin_r_tpc) { + mLastFitReject = __LINE__; // rejection point + return; + } + } Vtx_recalculationParCov(o2::base::Propagator::Instance(), pTrack, nTrack, xyz, matCorr); + if (v0cuts.conversionPointMethod == 1) { + LegScan scanP, scanN; + cpScanLeg(pTrack, d_bz, matCorr, scanP); + cpScanLeg(nTrack, d_bz, matCorr, scanN); + RadiusProfile prof; + cpRadiusProfile(scanP, scanN, pos.hasITS() ? 0.f : 0.25f, ele.hasITS() ? 0.f : 0.25f, prof); // o2-linter: disable=magic-number (0.5 cm extra z for legs without ITS) + const bool useProfile = prof.iMin >= 0 && prof.status != 4; // o2-linter: disable=magic-number (profile class: failed) + if (useProfile) { + registry.fill(HIST("V0/hConvPointProfileShift"), prof.rMin - std::hypot(xyz[0], xyz[1])); + xyz = prof.xyz; + } + registry.fill(HIST("V0/hConvPointProfile"), useProfile ? 1.f : 0.f); + } float rxy_tmp = RecoDecay::sqrtSumOfSquares(xyz[0], xyz[1]); if (rxy_tmp > trackcuts.maxX + v0cuts.margin_r_tpc) { + mLastFitReject = __LINE__; // rejection point (diagnostic) return; } if (rxy_tmp < std::fabs(xyz[2]) * std::tan(2 * std::atan(std::exp(-v0cuts.max_eta_v0))) - v0cuts.margin_z) { - return; // RZ line cut - } - - float phiv = 999.f; - float psipair = 999.f; - float phivFast = 999.f; - float psipairFast = 999.f; - float baseR = std::hypot(xyz[0], xyz[1]); - // This method uses the track Helix instead of the full propagation. - // Hence, it is only an approximation but much faster - if (modeTrackPropagation != TrackPropMode::kProper) { - - o2::track::TrackAuxPar helixPosEle(nTrack, d_bz); - o2::track::TrackAuxPar helixPosPos(pTrack, d_bz); - - float diffX = helixPosEle.xC - helixPosPos.xC; - float diffY = helixPosEle.yC - helixPosPos.yC; - auto phiHelix = RecoDecay::constrainAngle(std::atan2(diffY, diffX) - o2::constants::math::PI / 2.); - - // Electron - float arcLenghtEle = helixPosEle.rC * 0.9 > propV0LegsRadius ? std::asin(propV0LegsRadius / helixPosEle.rC) * helixPosEle.rC : o2::constants::math::PI / 2.2 * helixPosEle.rC; // This assumes that the photon momentum vector is a tangent of the circle // o2-linter: disable=magic-number (geometrical assumption for propagation) - auto propTrackEle = getPropMomentumFromTrackHelix(arcLenghtEle, ele, helixPosEle, d_bz / 10., phiHelix - ele.phi()); - // Positron - float arcLenghtPos = helixPosPos.rC * 0.9 > propV0LegsRadius ? std::asin(propV0LegsRadius / helixPosPos.rC) * helixPosPos.rC : o2::constants::math::PI / 2.2 * helixPosPos.rC; // This assumes that the photon momentum vector is a tangent of the circle // o2-linter: disable=magic-number (geometrical assumption for propagation) - auto propTrackPos = getPropMomentumFromTrackHelix(arcLenghtPos, pos, helixPosPos, d_bz / 10., phiHelix - pos.phi()); - - phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(propTrackPos[0], propTrackPos[1], propTrackPos[2], propTrackEle[0], propTrackEle[1], propTrackEle[2], pos.sign(), ele.sign(), d_bz); - psipair = o2::aod::pwgem::dilepton::utils::pairutil::getPsiPair(propTrackPos[0], propTrackPos[1], propTrackPos[2], propTrackEle[0], propTrackEle[1], propTrackEle[2]); - - // Store values for later comparison - phivFast = phiv; - psipairFast = psipair; - } - // This uses the full propagation including material effects - if (modeTrackPropagation != TrackPropMode::kFast) { - std::array offsetsR = {propV0LegsRadius, 30.f, 10.f}; - bool pPropagatedSuccess = false; - bool nPropagatedSuccess = false; - auto pTrackProp = pTrack; - auto nTrackProp = nTrack; - for (const auto& offsetR : offsetsR) { - pTrackProp = pTrack; - pTrackProp.setPID(o2::track::PID::Electron); - nTrackProp = nTrack; - nTrackProp.setPID(o2::track::PID::Electron); - - o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, pTrackProp, 2.f, matCorr, &dcaInfo); - o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, nTrackProp, 2.f, matCorr, &dcaInfo); - - pPropagatedSuccess = o2::base::Propagator::Instance()->propagateToR(pTrackProp, baseR + offsetR); - nPropagatedSuccess = o2::base::Propagator::Instance()->propagateToR(nTrackProp, baseR + offsetR); - - if (pPropagatedSuccess && nPropagatedSuccess) { - KFPTrack kfp_track_posProp = createKFPTrackFromTrackParCov(pTrackProp, pos.sign(), pos.tpcNClsFound(), pos.tpcChi2NCl()); - KFPTrack kfp_track_eleProp = createKFPTrackFromTrackParCov(nTrackProp, ele.sign(), ele.tpcNClsFound(), ele.tpcChi2NCl()); - phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(kfp_track_posProp.GetPx(), kfp_track_posProp.GetPy(), kfp_track_posProp.GetPz(), kfp_track_eleProp.GetPx(), kfp_track_eleProp.GetPy(), kfp_track_eleProp.GetPz(), pos.sign(), ele.sign(), d_bz); - psipair = o2::aod::pwgem::dilepton::utils::pairutil::getPsiPair(kfp_track_posProp.GetPx(), kfp_track_posProp.GetPy(), kfp_track_posProp.GetPz(), kfp_track_eleProp.GetPx(), kfp_track_eleProp.GetPy(), kfp_track_eleProp.GetPz()); - break; - } - LOG(debug) << "Propagation to offset" << offsetR << " cm failed for " << (pPropagatedSuccess ? "negative" : "positive") << " track. Trying smaller offset."; - } - if (filltable && modeTrackPropagation == TrackPropMode::kBoth) { - registry.fill(HIST("V0/hPhivPropagationCompare"), phiv, phivFast); - registry.fill(HIST("V0/hPsiPairPropagationCompare"), psipair, psipairFast); - } - } - - if (phiv == 999.f || psipair == 999.f) { // o2-linter: disable=magic-number (nonsensical default value) - LOG(debug) << "Propagation failed for all radii (" << propV0LegsRadius << ", 30, 10 cm). Using default values for phiv and psipair (999.f)."; + mLastFitReject = __LINE__; // rejection point (diagnostic) // RZ line cut + return; } KFPTrack kfp_track_pos = createKFPTrackFromTrackParCov(pTrack, pos.sign(), pos.tpcNClsFound(), pos.tpcChi2NCl()); @@ -830,8 +1261,10 @@ struct PhotonConversionBuilder { if (v0cuts.kfMassConstrain > -0.1) { // o2-linter: disable=magic-number (nonsensical default value) gammaKF.SetNonlinearMassConstraint(v0cuts.kfMassConstrain); } - KFPVertex kfpVertex = createKFPVertexFromCollision(collision); + KFPVertex kfpVertex = opt.pv ? createKFPVertexFromCollision(*opt.pv) : createKFPVertexFromCollision(collision); KFParticle KFPV(kfpVertex); + const float chi2NDFVtx = gammaKF.GetNDF() > 0 ? gammaKF.GetChi2() / gammaKF.GetNDF() : -1.f; // before the PV constraint + const float chi2PV = gammaKF.GetDeviationFromVertex(KFPV); // Transport the gamma to the recalculated decay vertex KFParticle gammaKF_DecayVtx = gammaKF; // with respect to (0,0,0) @@ -840,26 +1273,31 @@ struct PhotonConversionBuilder { float cospa_kf = cpaFromKF(gammaKF_DecayVtx, KFPV); if (!ele.hasITS() && !pos.hasITS()) { if (cospa_kf < v0cuts.min_v0cospa_tpconly) { + mLastFitReject = __LINE__; // rejection point return; } } else { if (cospa_kf < v0cuts.min_v0cospa_its) { + mLastFitReject = __LINE__; // rejection point return; } } float rxy = RecoDecay::sqrtSumOfSquares(gammaKF_DecayVtx.GetX(), gammaKF_DecayVtx.GetY()); if (rxy > trackcuts.maxX + v0cuts.margin_r_tpc) { + mLastFitReject = __LINE__; // rejection point return; } if (rxy < std::fabs(gammaKF_DecayVtx.GetZ()) * std::tan(2 * std::atan(std::exp(-v0cuts.max_eta_v0))) - v0cuts.margin_z) { - return; // RZ line cut + mLastFitReject = __LINE__; // rejection point // RZ line cut + return; } if (rxy < v0cuts.min_v0radius || v0cuts.max_v0radius < rxy) { + mLastFitReject = __LINE__; // rejection point return; } - if (!filltable) { + if (!filltable && opt.probe == nullptr) { if (isITSTPCTrack(pos) && isITSTPCTrack(ele)) { registry.fill(HIST("V0/hRxy_minX_ITSTPC_ITSTPC"), std::min(pTrack.getX(), nTrack.getX()), std::min(pTrack.getX(), nTrack.getX()) - rxy); // trackiu.x() - rxy should be positive } else if (isITSonlyTrack(pos) && isITSonlyTrack(ele)) { @@ -877,27 +1315,33 @@ struct PhotonConversionBuilder { if (pos.hasITS() && ele.hasITS()) { // ITSonly-ITSonly, ITSTPC-ITSTPC, ITSTPC-ITSonly if (rxy > std::min(pTrack.getX(), nTrack.getX()) + v0cuts.margin_r_its) { + mLastFitReject = __LINE__; // rejection point return; } } else if (!pos.hasITS() && ele.hasITS()) { // ITSTPC-TPC if (rxy > std::min(83.f, nTrack.getX()) + v0cuts.margin_r_itstpc_tpc) { + mLastFitReject = __LINE__; // rejection point return; } } else if (pos.hasITS() && !ele.hasITS()) { // ITSTPC-TPC if (rxy > std::min(pTrack.getX(), 83.f) + v0cuts.margin_r_itstpc_tpc) { + mLastFitReject = __LINE__; // rejection point return; } } else if (!pos.hasITS() && !ele.hasITS()) { // TPC-TPC if (rxy > std::min(83.f, 83.f) + v0cuts.margin_r_tpc) { + mLastFitReject = __LINE__; // rejection point return; } } if ((!pos.hasITS() || !ele.hasITS()) && rxy < v0cuts.max_r_req_its) { // conversion points smaller than v0cuts.max_r_req_its have to be detected with ITS hits. + mLastFitReject = __LINE__; // rejection point return; } if ((!pos.hasITS() && !ele.hasITS()) && rxy < v0cuts.min_r_tpconly) { // TPConly tracks can detect conversion points larger than v0cuts.min_r_tpconly. + mLastFitReject = __LINE__; // rejection point return; } @@ -916,13 +1360,91 @@ struct PhotonConversionBuilder { // LOGF(info, "gammaKF_PV.GetPz() = %f, gammaKF_DecayVtx.GetPz() = %f, gammaKF_DecayVtx2.GetPz() = %f", gammaKF_PV.GetPz(), gammaKF_DecayVtx.GetPz(), gammaKF_DecayVtx2.GetPz()); if (std::fabs(v0eta) > v0cuts.max_eta_v0 || v0pt < v0cuts.min_pt_v0) { + mLastFitReject = __LINE__; // rejection point return; } if (isITSonlyTrack(ele) && isITSonlyTrack(pos) && v0pt > v0cuts.max_pt_v0_itsonly) { + mLastFitReject = __LINE__; // rejection point return; } + // phi_V and psi_pair (4 material propagations) are only stored with the candidate: computed after the cuts (CPU; identical values) + float phiv = 999.f; + float psipair = 999.f; + float phivFast = 999.f; + float psipairFast = 999.f; + float baseR = std::hypot(xyz[0], xyz[1]); + // This method uses the track Helix instead of the full propagation. + // Hence, it is only an approximation but much faster + if (modeTrackPropagation != TrackPropMode::kProper) { + + o2::track::TrackAuxPar helixPosEle(nTrack, d_bz); + o2::track::TrackAuxPar helixPosPos(pTrack, d_bz); + + float diffX = helixPosEle.xC - helixPosPos.xC; + float diffY = helixPosEle.yC - helixPosPos.yC; + auto phiHelix = RecoDecay::constrainAngle(std::atan2(diffY, diffX) - o2::constants::math::PI / 2.); + + // Electron + float arcLenghtEle = helixPosEle.rC * 0.9 > propV0LegsRadius ? std::asin(propV0LegsRadius / helixPosEle.rC) * helixPosEle.rC : o2::constants::math::PI / 2.2 * helixPosEle.rC; // This assumes that the photon momentum vector is a tangent of the circle // o2-linter: disable=magic-number (geometrical assumption for propagation) + auto propTrackEle = getPropMomentumFromTrackHelix(arcLenghtEle, ele, helixPosEle, d_bz / 10., phiHelix - ele.phi()); + // Positron + float arcLenghtPos = helixPosPos.rC * 0.9 > propV0LegsRadius ? std::asin(propV0LegsRadius / helixPosPos.rC) * helixPosPos.rC : o2::constants::math::PI / 2.2 * helixPosPos.rC; // This assumes that the photon momentum vector is a tangent of the circle // o2-linter: disable=magic-number (geometrical assumption for propagation) + auto propTrackPos = getPropMomentumFromTrackHelix(arcLenghtPos, pos, helixPosPos, d_bz / 10., phiHelix - pos.phi()); + + phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(propTrackPos[0], propTrackPos[1], propTrackPos[2], propTrackEle[0], propTrackEle[1], propTrackEle[2], pos.sign(), ele.sign(), d_bz); + psipair = o2::aod::pwgem::dilepton::utils::pairutil::getPsiPair(propTrackPos[0], propTrackPos[1], propTrackPos[2], propTrackEle[0], propTrackEle[1], propTrackEle[2]); + + // Store values for later comparison + phivFast = phiv; + psipairFast = psipair; + } + // This uses the full propagation including material effects + if (modeTrackPropagation != TrackPropMode::kFast) { + std::array offsetsR = {propV0LegsRadius, 30.f, 10.f}; + bool pPropagatedSuccess = false; + bool nPropagatedSuccess = false; + auto pTrackProp = pTrack; + auto nTrackProp = nTrack; + for (const auto& offsetR : offsetsR) { + // the legs at their DCA to the vertex: the same propagation as for the DCA cut above (reused when it succeeded there) + if (posAtDca) { + pTrackProp = *posAtDca; + } else { + pTrackProp = pTrack; + pTrackProp.setPID(o2::track::PID::Electron); + o2::base::Propagator::Instance()->propagateToDCABxByBz({pvX, pvY, pvZ}, pTrackProp, 2.f, matCorr, &dcaInfo); + } + if (eleAtDca) { + nTrackProp = *eleAtDca; + } else { + nTrackProp = nTrack; + nTrackProp.setPID(o2::track::PID::Electron); + o2::base::Propagator::Instance()->propagateToDCABxByBz({pvX, pvY, pvZ}, nTrackProp, 2.f, matCorr, &dcaInfo); + } + + pPropagatedSuccess = o2::base::Propagator::Instance()->propagateToR(pTrackProp, baseR + offsetR); + nPropagatedSuccess = o2::base::Propagator::Instance()->propagateToR(nTrackProp, baseR + offsetR); + + if (pPropagatedSuccess && nPropagatedSuccess) { + KFPTrack kfp_track_posProp = createKFPTrackFromTrackParCov(pTrackProp, pos.sign(), pos.tpcNClsFound(), pos.tpcChi2NCl()); + KFPTrack kfp_track_eleProp = createKFPTrackFromTrackParCov(nTrackProp, ele.sign(), ele.tpcNClsFound(), ele.tpcChi2NCl()); + phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(kfp_track_posProp.GetPx(), kfp_track_posProp.GetPy(), kfp_track_posProp.GetPz(), kfp_track_eleProp.GetPx(), kfp_track_eleProp.GetPy(), kfp_track_eleProp.GetPz(), pos.sign(), ele.sign(), d_bz); + psipair = o2::aod::pwgem::dilepton::utils::pairutil::getPsiPair(kfp_track_posProp.GetPx(), kfp_track_posProp.GetPy(), kfp_track_posProp.GetPz(), kfp_track_eleProp.GetPx(), kfp_track_eleProp.GetPy(), kfp_track_eleProp.GetPz()); + break; + } + LOG(debug) << "Propagation to offset" << offsetR << " cm failed for " << (pPropagatedSuccess ? "negative" : "positive") << " track. Trying smaller offset."; + } + if (filltable && modeTrackPropagation == TrackPropMode::kBoth) { + registry.fill(HIST("V0/hPhivPropagationCompare"), phiv, phivFast); + registry.fill(HIST("V0/hPsiPairPropagationCompare"), psipair, psipairFast); + } + } + + if (phiv == 999.f || psipair == 999.f) { // o2-linter: disable=magic-number (nonsensical default value) + LOG(debug) << "Propagation failed for all radii (" << propV0LegsRadius << ", 30, 10 cm). Using default values for phiv and psipair (999.f)."; + } KFParticle kfp_pos_DecayVtx = kfp_pos; // Don't set Primary Vertex KFParticle kfp_ele_DecayVtx = kfp_ele; // Don't set Primary Vertex kfp_pos_DecayVtx.TransportToPoint(xyz.data()); // Don't set Primary Vertex @@ -931,62 +1453,103 @@ struct PhotonConversionBuilder { float cospaXYKF = cospaXY_KF(gammaKF_DecayVtx, KFPV); float cospaRZKF = cospaRZ_KF(gammaKF_DecayVtx, KFPV); auto centType = static_cast(mlcuts.centTypePCMMl.value); - v0photoncandidate.setPhotonCandidate(gammaKF_DecayVtx, gammaKF_PV, pos, kfp_pos_DecayVtx, ele, kfp_ele_DecayVtx, collision, cospaXYKF, cospaRZKF, cospaXYKF, psipair, phiv, centType, posdcaXY, eledcaXY, posdcaZ, eledcaZ); + // cosPA slot of the candidate (dedup score, cosPA cut): xy as in master, or 3D with dedup_group.useCosPA3D + v0photoncandidate.setPhotonCandidate(gammaKF_DecayVtx, gammaKF_PV, pos, kfp_pos_DecayVtx, ele, kfp_ele_DecayVtx, collision, dedup.useCosPA3D ? cospa_kf : cospaXYKF, cospaRZKF, cospaXYKF, psipair, phiv, centType, posdcaXY, eledcaXY, posdcaZ, eledcaZ); if (!ele.hasITS() && !pos.hasITS()) { // V0s with TPConly-TPConly if (v0cuts.max_r_itsmft_ss < rxy && rxy < trackcuts.maxX + v0cuts.margin_r_tpc) { if (v0photoncandidate.getPCA() > v0cuts.max_dcav0dau_tpc_inner_fc) { + mLastFitReject = __LINE__; // rejection point return; } } else { if (v0photoncandidate.getPCA() > v0cuts.max_dcav0dau_tpconly) { + mLastFitReject = __LINE__; // rejection point return; } } + } else if (opt.nonSVertexer && recovery.finderMaxPCAMixed > 0.f && pos.hasITS() != ele.hasITS()) { + if (v0photoncandidate.getPCA() > recovery.finderMaxPCAMixed) { + mLastFitReject = __LINE__; // rejection point + return; + } } else { // V0s with ITS hits if (rxy < v0cuts.max_r_req_its) { if (v0photoncandidate.getPCA() > v0cuts.max_dcav0dau_itsibss) { + mLastFitReject = __LINE__; // rejection point return; } } else { if (v0photoncandidate.getPCA() > v0cuts.max_dcav0dau_its) { + mLastFitReject = __LINE__; // rejection point return; } } } if (isITSonlyTrack(pos) && v0photoncandidate.getPosPt() > trackcuts.maxpt_itsonly) { + mLastFitReject = __LINE__; // rejection point return; } if (isITSonlyTrack(ele) && v0photoncandidate.getElePt() > trackcuts.maxpt_itsonly) { + mLastFitReject = __LINE__; // rejection point return; } if (v0photoncandidate.getChi2NDF() > 6e+3) { // protection for uint16. // o2-linter: disable=magic-number (protection for uint16) + mLastFitReject = __LINE__; // rejection point return; } if (std::fabs(v0photoncandidate.getDcaXYToPV()) > v0cuts.max_dcatopv_xy_v0 || std::fabs(v0photoncandidate.getDcaZToPV()) > v0cuts.max_dcatopv_z_v0) { + mLastFitReject = __LINE__; // rejection point return; } - if (!checkAP(v0photoncandidate.getAlpha(), v0photoncandidate.getQt(), v0cuts.max_alpha_ap, v0cuts.max_qt_ap)) { // store only photon conversions + if (!opt.relaxAP && !checkAP(v0photoncandidate.getAlpha(), v0photoncandidate.getQt(), v0cuts.max_alpha_ap, v0cuts.max_qt_ap)) { // store only photon conversions + mLastFitReject = __LINE__; // rejection point return; } - pca_map[std::make_tuple(v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex())] = v0photoncandidate.getPCA(); - cospa_map[std::make_tuple(v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex())] = v0photoncandidate.getCosPA(); ROOT::Math::PxPyPzMVector vposDedup(kfp_pos_DecayVtx.GetPx(), kfp_pos_DecayVtx.GetPy(), kfp_pos_DecayVtx.GetPz(), o2::constants::physics::MassElectron); ROOT::Math::PxPyPzMVector veleDedup(kfp_ele_DecayVtx.GetPx(), kfp_ele_DecayVtx.GetPy(), kfp_ele_DecayVtx.GetPz(), o2::constants::physics::MassElectron); const auto meeSV = static_cast((vposDedup + veleDedup).M()); - const auto score = getScoreV0(v0photoncandidate.getCosPA(), v0photoncandidate.getPCA(), dedup.deduplicationScoreWeight); - V0CandidateHelper v0Helper(v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex(), + // passed all cuts: score and dedup candidate + const float gap = circleGap(o2::track::TrackAuxPar(pTrack, d_bz), o2::track::TrackAuxPar(nTrack, d_bz)); + const float score = dedupScore(v0photoncandidate.getCosPA(), v0photoncandidate.getPCA(), gap); + if (opt.probe != nullptr) { + *opt.probe = {.ok = true, .score = score}; + mLastFitReject = __LINE__; // rejection point (diagnostic) + return; + } + const float purity = usePurity() ? purityScore(pos, ele, cospa_kf, v0photoncandidate.getPCA(), meeSV, gap, v0photoncandidate.getPsiPair()) : -999.f; + if (opt.purityGate) { + registry.fill(HIST("Finder/hScore"), purity); + if (purity <= (opt.minPurity > -998.f ? opt.minPurity : recovery.finderMinScore.value)) { // o2-linter: disable=magic-number (unset marker) + mLastFitReject = __LINE__; // rejection point (diagnostic) + return; + } + } + + pca_map[CandKey{v0Id, collision.globalIndex(), pos.globalIndex(), ele.globalIndex()}] = v0photoncandidate.getPCA(); + cospa_map[CandKey{v0Id, collision.globalIndex(), pos.globalIndex(), ele.globalIndex()}] = v0photoncandidate.getCosPA(); + + V0CandidateHelper v0Helper(v0Id, collision.globalIndex(), pos.globalIndex(), ele.globalIndex(), v0photoncandidate.getCosPA(), v0photoncandidate.getPCA(), score, meeSV); v0Helper.pxAtPV = gammaKF_PV.GetPx(); v0Helper.pyAtPV = gammaKF_PV.GetPy(); v0Helper.pzAtPV = gammaKF_PV.GetPz(); + v0Helper.svX = gammaKF_DecayVtx.GetX(); + v0Helper.svY = gammaKF_DecayVtx.GetY(); + v0Helper.svZ = gammaKF_DecayVtx.GetZ(); + v0Helper.pxAtSV = gammaKF_DecayVtx.GetPx(); + v0Helper.pyAtSV = gammaKF_DecayVtx.GetPy(); + v0Helper.pzAtSV = gammaKF_DecayVtx.GetPz(); + v0Helper.purity = purity; + v0Helper.cosPAxy = cospaXYKF; + v0Helper.cosPA3D = cospa_kf; vecV0Dedup.emplace_back(v0Helper); if (mlcuts.applyPCMMl) { @@ -1010,6 +1573,7 @@ struct PhotonConversionBuilder { } } if (!isSelectedML) { + mLastFitReject = __LINE__; // rejection point return; } if (filltable) { @@ -1068,436 +1632,2289 @@ struct PhotonConversionBuilder { ROOT::Math::PxPyPzMVector v0_sv = vpos_sv + vele_sv; registry.fill(HIST("V0/hMeeSV_Rxy"), rxy, v0_sv.M()); - v0photonskf(collision.globalIndex(), v0.globalIndex(), v0legs.lastIndex() + 1, v0legs.lastIndex() + 2, + v0photonskf(collision.globalIndex(), v0Id, v0legs.lastIndex() + 1, v0legs.lastIndex() + 2, gammaKF_DecayVtx.GetX(), gammaKF_DecayVtx.GetY(), gammaKF_DecayVtx.GetZ(), gammaKF_PV.GetPx(), gammaKF_PV.GetPy(), gammaKF_PV.GetPz(), v0_sv.M(), v0photoncandidate.getDcaXYToPV(), v0photoncandidate.getDcaZToPV(), cospa_kf, cospaXYKF, cospaRZKF, v0photoncandidate.getPCA(), v0photoncandidate.getAlpha(), v0photoncandidate.getQt(), v0photoncandidate.getChi2NDF()); v0photonsphivpsi(v0photoncandidate.getPhiV(), v0photoncandidate.getPsiPair()); + v0photonskfchi2(chi2NDFVtx, chi2PV); // v0photonskfcov(gammaKF_PV.GetCovariance(9), gammaKF_PV.GetCovariance(14), gammaKF_PV.GetCovariance(20), gammaKF_PV.GetCovariance(13), gammaKF_PV.GetCovariance(19), gammaKF_PV.GetCovariance(18)); fillTrackTable(pos, kfp_pos_DecayVtx, posdcaXY, posdcaZ); // positive leg first fillTrackTable(ele, kfp_ele_DecayVtx, eledcaXY, eledcaZ); // negative leg second + if (fillLegCov) { + fillLegCovTable(pTrack); + fillLegCovTable(nTrack); + } } // end of fill table } - Preslice perCollision = o2::aod::v0::collisionId; - std::map, float> pca_map; // (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) -> pca - std::map, float> cospa_map; // (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) -> cospa - std::vector vecV0Dedup; // vector with all V0Candidates that is used to sort them by score (see struct V0CandidateHelper for more details of content) - std::vector> stored_v0Ids; // (pos.globalIndex(), ele.globalIndex()) - std::vector> stored_fullv0Ids; // (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) - std::unordered_map nv0_map; // map collisionId -> nv0 + // bookkeeping of candidates and stored photons + static CandKey keyOf(V0CandidateHelper const& c) + { + return CandKey{.v0Id = c.v0ID, .collisionId = c.colID, .posId = c.posID, .eleId = c.eleID}; + } - template - void build(TCollisions const& collisions, TV0s const& v0s, TTracks const& tracks, TBCs const&, DedupDiag* diag = nullptr) + // MC diagnostics: the trace of a leg pair of one genuine photon, nullptr for any other pair or without diagnostics + TrueLegTrace* trueLegTrace(const int64_t posId, const int64_t eleId) { - for (const auto& collision : collisions) { - if constexpr (isMC) { - if (!collision.has_mcCollision()) { - continue; - } - } + if (mDiag == nullptr) { + return nullptr; + } + const auto ip = mLegMother.find(posId); + const auto ie = mLegMother.find(eleId); + if (ip == mLegMother.end() || ie == mLegMother.end() || ip->second != ie->second) { + return nullptr; + } + return &mDiag->trueLegTrace[{posId, eleId}]; + } - if (!collision.isSelected()) { - continue; - } + std::map candidateIndexByKey() const + { + std::map index; + for (int i = 0; i < static_cast(vecV0Dedup.size()); ++i) { + index[keyOf(vecV0Dedup[i])] = i; + } + return index; + } - // if constexpr (isTriggerAnalysis) { - // if (collision.triggerMask_raw() == 0) { - // continue; - // } - // } + // replaces the stored photons after the dedup and keeps stored_v0Ids and nv0_map consistent + void setStored(std::vector keys) + { + std::stable_sort(keys.begin(), keys.end(), [](const auto& a, const auto& b) { return a.collisionId < b.collisionId; }); + stored_fullv0Ids.swap(keys); + stored_v0Ids.clear(); + for (auto& [col, n] : nv0_map) { // o2-linter: disable=const-ref-in-for-loop (counters are reset in place) + n = 0; + } + for (const auto& key : stored_fullv0Ids) { + stored_v0Ids.emplace_back(key.posId, key.eleId); + nv0_map[key.collisionId]++; + } + } - nv0_map[collision.globalIndex()] = 0; + // finder leg: helix circle and the time window in which the track can belong to a collision (TPC-only legs: from the drift-time asymmetry) + template + bool makeFinderLeg(TTrack const& track, TCollisions const& collisions, const int64_t bcRef, FinderLeg& leg) + { + using namespace o2::pwgem::photonmeson; + if (track.collisionId() < 0 || !track.hasTPC()) { + return false; + } + leg.id = track.globalIndex(); + leg.tgl = track.tgl(); + leg.noITS = !track.hasITS(); + leg.movable = isTPConlyTrack(track) && (track.flags() & o2::aod::track::TrackTimeAsym) != 0; + const auto bc = collisions.rawIteratorAt(track.collisionId()).template bc_as(); + leg.t = static_cast(static_cast(bc.globalBC()) - bcRef) * o2::constants::lhc::LHCBunchSpacingNS + track.trackTime(); + if (leg.movable) { + const int side = tpcDriftSide(track); + if (side == TPCSideUndetermined) { + return false; + } + leg.side = side; + leg.tMin = leg.t - track.tpcDeltaTBwd() - recovery.finderTimeMargin; + leg.tMax = leg.t + track.tpcDeltaTFwd() + recovery.finderTimeMarginFwd; + } else { + constexpr float NSigmaTime = 4.f; + const float sigma = std::max(track.trackTimeRes(), 1.f); + const float halfWidth = (track.flags() & o2::aod::track::TrackTimeResIsRange) != 0 ? sigma : NSigmaTime * sigma; + leg.side = 0; + leg.tMin = leg.t - halfWidth - recovery.finderTimeMargin; + leg.tMax = leg.t + halfWidth + recovery.finderTimeMargin; + } + leg.par = getTrackPar(track); + leg.circle = o2::track::TrackAuxPar(leg.par, d_bz); + return true; + } - const auto& bc = collision.template foundBC_as(); - initCCDB(bc); - registry.fill(HIST("hCollisionCounter"), 1); + // pairs the free legs of the whole DF (not used by any candidate unless finderFreeCollinearLegs, at least one leg without ITS); a pair that passes the geometry + // (evalFinderPair) and matches a collision in z is fitted and becomes a dedup candidate with v0Id = -1 + template + void runPhotonFinder(TCollisions const& collisions, TTracks const& tracks) + { + std::set usedLegs; + std::set> builtPairs; + // legs of accepted candidates are not free; with finderFreeCollinearLegs, legs of collinear candidate pairs stay free; with + // finderFreeAllLegs no leg is blocked (only pairs already built are skipped) + std::vector collinear(vecV0Dedup.size(), recovery.finderFreeAllLegs.value); + if (recovery.finderFreeCollinearLegs > 0.f && !recovery.finderFreeAllLegs) { + std::map> candsOfColl; + for (size_t i = 0; i < vecV0Dedup.size(); ++i) { + candsOfColl[vecV0Dedup[i].colID].push_back(i); + } + for (const auto& [colId, members] : candsOfColl) { + for (size_t a = 0; a < members.size(); ++a) { + for (size_t b = a + 1; b < members.size(); ++b) { + const auto& ci = vecV0Dedup[members[a]]; + const auto& cj = vecV0Dedup[members[b]]; + if (ci.posID == cj.posID || ci.eleID == cj.eleID) { + continue; + } + if (qInv(ci.pxAtPV, ci.pyAtPV, ci.pzAtPV, cj.pxAtPV, cj.pyAtPV, cj.pzAtPV) < recovery.finderFreeCollinearLegs) { + collinear[members[a]] = true; + collinear[members[b]] = true; + } + } + } + } + } + std::vector inCandidate(tracks.size(), 0); // CPU: builtPairs is looked up only when both legs are in a candidate + for (size_t i = 0; i < vecV0Dedup.size(); ++i) { + const auto& c = vecV0Dedup[i]; + builtPairs.emplace(c.posID, c.eleID); + if (c.posID >= 0 && c.posID < static_cast(inCandidate.size()) && c.eleID >= 0 && c.eleID < static_cast(inCandidate.size())) { + inCandidate[c.posID] = inCandidate[c.eleID] = 1; + } + if (!collinear[i]) { + usedLegs.insert(c.posID); + usedLegs.insert(c.eleID); + } + } + std::vector colls; + int64_t bcRef = -1; + for (const auto& col : collisions) { + if constexpr (isMC) { + if (!col.has_mcCollision()) { + continue; + } + } + if (!col.isSelected()) { + continue; + } + const auto bc = col.template bc_as(); + if (bcRef < 0) { + bcRef = static_cast(bc.globalBC()); + } + colls.push_back({static_cast(static_cast(bc.globalBC()) - bcRef) * o2::constants::lhc::LHCBunchSpacingNS + col.collisionTime(), col.globalIndex(), col.posX(), col.posY(), col.posZ()}); + } + if (colls.empty()) { + return; + } + std::sort(colls.begin(), colls.end(), [](const FinderCollision& a, const FinderCollision& b) { return a.t < b.t; }); + const auto* vd = ccdb->getForTimeStamp("TPC/Calib/VDriftTgl", mLastCCDBTimestamp); + if (vd == nullptr) { + LOG(warning) << "photon finder: no TPC drift velocity for timestamp " << mLastCCDBTimestamp; + return; + } + const double vDrift = vd->refVDrift * vd->corrFact * 1e-3; + float xB = 0.f, yB = 0.f; + for (const auto& c : colls) { + xB += c.x / static_cast(colls.size()); + yB += c.y / static_cast(colls.size()); + } - updateCCDB(bc); // delay update until is needed + std::vector posLegs, eleLegs; + for (const auto& track : tracks) { + if (usedLegs.contains(track.globalIndex()) || track.pt() < trackcuts.min_pt_trackiu || !checkV0leg(track)) { + continue; + } + FinderLeg leg; + if (makeFinderLeg(track, collisions, bcRef, leg)) { + (track.sign() > 0 ? posLegs : eleLegs).push_back(leg); + } + } + std::sort(eleLegs.begin(), eleLegs.end(), [](const FinderLeg& a, const FinderLeg& b) { return a.tgl < b.tgl; }); + + const FinderCuts cuts{recovery.finderMaxDeltaTgl, recovery.finderMaxCircleGap, v0cuts.min_r_tpconly, v0cuts.max_r_req_its, recovery.finderRMargin, + trackcuts.maxX + v0cuts.margin_r_tpc, recovery.finderMinCosXY, recovery.finderMaxDeltaZ, recovery.finderMaxDz, d_bz}; + const FitOptions finderFit{.nonSVertexer = !recovery.finderUniformFit.value, .purityGate = !recovery.finderUniformFit.value}; + const size_t nBefore = vecV0Dedup.size(); + std::array stepCount{}; // filled into Finder/hSteps once per DF (CPU: one fill per pair dominated on data) + auto isBuilt = [&](int64_t a, int64_t b) { return inCandidate[a] && inCandidate[b] && builtPairs.contains({a, b}); }; + // one leg pair, in the order of the original loop (positive legs in track order, negative legs in tgl order) + auto tryPair = [&](FinderLeg const& p, FinderLeg const& e) { + TrueLegTrace* trace = nullptr; + if constexpr (isMC) { + trace = trueLegTrace(p.id, e.id); + } + if ((!p.noITS && !e.noITS) || isBuilt(p.id, e.id)) { + if (trace != nullptr) { + trace->finderStep = (!p.noITS && !e.noITS) ? -8 : -7; // o2-linter: disable=magic-number (trace codes) + } + return; + } + int64_t colId = -1; + const int step = evalFinderPair(p, e, colls, cuts, xB, yB, vDrift, colId); + if (step >= 0 && step < FinderStep::kNFindStep) { + stepCount[step] += 1.; + } + if (trace != nullptr) { + trace->finderStep = step; + } + if (step == FinderStep::kFindCollision) { + fitV0Candidate(collisions.rawIteratorAt(colId), tracks.rawIteratorAt(p.id), tracks.rawIteratorAt(e.id), -1, false, finderFit); + if (trace != nullptr) { + trace->finderLine = mLastFitReject; + } + } + }; + const auto tglLess = [](const FinderLeg& c, float v) { return c.tgl < v; }; + // time-binning of the negative legs: the time window of a positive leg is intersected with the time windows of the negative legs in the same bin. Reason for implementation: faster than checking all negative legs for each positive leg + const bool timeBinned = (!isMC || mDiag == nullptr) && !eleLegs.empty(); + std::vector> eleInBin; + double tBin0 = 0.; + const double binWidth = 5000.; // ns + if (timeBinned) { + double tLoAll = eleLegs.front().tMin, tHiAll = eleLegs.front().tMax; + for (const auto& e : eleLegs) { + tLoAll = std::min(tLoAll, e.tMin); + tHiAll = std::max(tHiAll, e.tMax); + } + tBin0 = tLoAll; + eleInBin.resize(static_cast((tHiAll - tLoAll) / binWidth) + 1); + for (uint32_t k = 0; k < eleLegs.size(); ++k) { // k increasing: every bin stays sorted by index = tgl order + const auto b0 = static_cast((eleLegs[k].tMin - tBin0) / binWidth); + const auto b1 = std::min(static_cast((eleLegs[k].tMax - tBin0) / binWidth), eleInBin.size() - 1); + for (size_t b = b0; b <= b1; ++b) { + eleInBin[b].push_back(k); + } + } + } + std::vector tried; + for (const auto& p : posLegs) { + const auto lo = static_cast(std::lower_bound(eleLegs.begin(), eleLegs.end(), p.tgl - recovery.finderMaxDeltaTgl, tglLess) - eleLegs.begin()); + if (!timeBinned) { + for (uint32_t k = lo; k < eleLegs.size() && eleLegs[k].tgl <= p.tgl + recovery.finderMaxDeltaTgl; ++k) { + tryPair(p, eleLegs[k]); + } + continue; + } + const auto hi = static_cast(std::upper_bound(eleLegs.begin(), eleLegs.end(), p.tgl + recovery.finderMaxDeltaTgl, [](float v, const FinderLeg& c) { return v < c.tgl; }) - eleLegs.begin()); + if (lo >= hi || p.tMax < tBin0) { + continue; + } + tried.clear(); + const auto b0 = static_cast(std::max(0., (p.tMin - tBin0) / binWidth)); + const auto b1 = std::min(static_cast((p.tMax - tBin0) / binWidth), eleInBin.size() - 1); + for (size_t b = b0; b <= b1 && b < eleInBin.size(); ++b) { + const auto& v = eleInBin[b]; + for (auto it = std::lower_bound(v.begin(), v.end(), lo); it != v.end() && *it < hi; ++it) { + tried.push_back(*it); + } + } + if (b1 > b0) { + std::sort(tried.begin(), tried.end()); + tried.erase(std::unique(tried.begin(), tried.end()), tried.end()); + } + for (const auto k : tried) { + tryPair(p, eleLegs[k]); + } + } + for (int st = 0; st < FinderStep::kNFindStep; ++st) { + if (stepCount[st] > 0.) { + registry.fill(HIST("Finder/hSteps"), static_cast(st), stepCount[st]); + } + } + registry.fill(HIST("Finder/hNAdded"), static_cast(vecV0Dedup.size() - nBefore)); + if (mDiag != nullptr) { + for (size_t i = nBefore; i < vecV0Dedup.size(); ++i) { + mDiag->finderKeys.insert(keyOf(vecV0Dedup[i])); + } + } + } - vecV0Dedup.reserve(30000); // rough estimate for number of V0s per DF - const auto& v0s_per_coll = v0s.sliceBy(perCollision, collision.globalIndex()); - // LOGF(info, "n v0 = %d", v0s_per_coll.size()); - for (const auto& v0 : v0s_per_coll) { - // LOGF(info, "collision.globalIndex() = %d, v0.globalIndex() = %d, v0.posTrackId() = %d, v0.negTrackId() = %d", collision.globalIndex(), v0.globalIndex(), v0.posTrackId() , v0.negTrackId()); - fillV0Table(v0, false); - } // end of v0 loop - } // end of collision loop + // two stored photons that use two pieces of one track (same tgl, compatible parameters): keep the better one + template + void resolveSplitLegConflicts(TTracks const& tracks, DedupDiag* diag) + { + const auto indexOfKey = candidateIndexByKey(); + std::vector> order; + order.reserve(stored_fullv0Ids.size()); + for (size_t k = 0; k < stored_fullv0Ids.size(); ++k) { + const auto it = indexOfKey.find(stored_fullv0Ids[k]); + order.emplace_back(it == indexOfKey.end() ? std::numeric_limits::max() : (usePurity() ? -vecV0Dedup[it->second].purity : vecV0Dedup[it->second].score), k); + } + std::stable_sort(order.begin(), order.end(), [](const auto& a, const auto& b) { return a.first < b.first; }); + auto samePiece = [&](int64_t idA, int64_t idB) { + if (idA == idB) { + return false; + } + const auto trA = tracks.rawIteratorAt(idA); + const auto trB = tracks.rawIteratorAt(idB); + if (std::fabs(trA.tgl() - trB.tgl()) > dedup.splitLegMaxDTgl) { + return false; + } + const float chi2 = splitPieceChi2(trA, trB, d_bz); + return chi2 >= 0.f && chi2 < dedup.splitLegMaxChi2; + }; + // the keptPosOf and keptEleOf maps are keyed by collisionId (or 0 for mixed instances) and contain a map of tgl to the kept leg information + struct KeptLeg { + int64_t id; + size_t owner; + size_t order; + }; + std::unordered_map> keptPosOf, keptEleOf; + std::unordered_map tglOf; + auto tgl = [&](int64_t id) { + const auto it = tglOf.find(id); + return it != tglOf.end() ? it->second : (tglOf[id] = tracks.rawIteratorAt(id).tgl()); + }; + auto firstOwner = [&](std::multimap const& kept, int64_t id) { + const float t = tgl(id); + size_t bestOrder = std::numeric_limits::max(); + int64_t owner = -1; + for (auto it = kept.lower_bound(t - dedup.splitLegMaxDTgl); it != kept.end() && it->first <= t + dedup.splitLegMaxDTgl; ++it) { + if (it->second.order < bestOrder && samePiece(id, it->second.id)) { + bestOrder = it->second.order; + owner = static_cast(it->second.owner); + } + } + return owner; + }; + std::vector removed(stored_fullv0Ids.size(), 0); + int nRemoved = 0; + for (const auto& [rank, k] : order) { + const auto& key = stored_fullv0Ids[k]; + auto& keptPos = keptPosOf[kMixedInstance ? static_cast(key.collisionId) : 0]; + auto& keptEle = keptEleOf[kMixedInstance ? static_cast(key.collisionId) : 0]; + int64_t owner = firstOwner(keptPos, key.posId); + if (owner < 0) { + owner = firstOwner(keptEle, key.eleId); + } + if (owner >= 0) { + removed[k] = 1; + ++nRemoved; + if (diag != nullptr) { + diag->blockersByKey[key].push_back(stored_fullv0Ids[owner]); + } + continue; + } + keptPos.emplace(tgl(key.posId), KeptLeg{key.posId, k, keptPos.size()}); + keptEle.emplace(tgl(key.eleId), KeptLeg{key.eleId, k, keptEle.size()}); + } + if (nRemoved > 0) { + std::vector kept; + kept.reserve(stored_fullv0Ids.size() - nRemoved); + for (size_t k = 0; k < stored_fullv0Ids.size(); ++k) { + if (removed[k] == 0) { + kept.push_back(stored_fullv0Ids[k]); + } + } + setStored(std::move(kept)); + } + registry.fill(HIST("Dedup/hSplitLegRemoved"), static_cast(nRemoved)); + } - stored_v0Ids.reserve(pca_map.size()); // number of photon candidates per DF - stored_fullv0Ids.reserve(pca_map.size()); // number of photon candidates per DF + // ambiguity resolver + // two stored photons of one collision, close in q and in space: fit the crossed leg pairing; if both crossed photons pass and + // their summed score is lower, swap + template + void runSwapResolver(TCollisions const& collisions, TTracks const& tracks, DedupDiag* diag) + { + const auto indexOfKey = candidateIndexByKey(); + std::map> storedOfColl; + for (const auto& key : stored_fullv0Ids) { + if (const auto it = indexOfKey.find(key); it != indexOfKey.end()) { + storedOfColl[key.collisionId].push_back(it->second); + } + } + auto fitPair = [&](int64_t colId, int64_t posId, int64_t eleId, PairFit* probe, bool original = false) { + const auto pos = tracks.rawIteratorAt(posId); + SourcePV srcPv; + const SourcePV* pv = nullptr; + if constexpr (requires { pos.sourcePvX(); }) { + if (original && mixed.refitOriginalsAtSourceVertex) { + srcPv = SourcePV{pos.sourcePvX(), pos.sourcePvY(), pos.sourcePvZ(), pos.sourcePvCovXX(), pos.sourcePvCovXY(), pos.sourcePvCovXZ(), pos.sourcePvCovYY(), pos.sourcePvCovYZ(), pos.sourcePvCovZZ(), pos.sourcePvChi2(), pos.sourcePvNContrib()}; + pv = &srcPv; + } + } + const auto ele = tracks.rawIteratorAt(eleId); + const bool finderLike = mixed.uniformPairFit ? !(pos.hasITS() && ele.hasITS()) : !recovery.finderUniformFit.value; // uniformPairFit (run 2): options by the legs + fitV0Candidate(collisions.rawIteratorAt(colId), pos, ele, -1, false, + FitOptions{.nonSVertexer = finderLike, .relaxAP = finderLike, .purityGate = mixed.uniformPairFit && finderLike && recovery.runFinder.value, .probe = probe, .pv = pv}); + }; + auto probeFit = [&](int64_t colId, int64_t posId, int64_t eleId, bool original = false) { + PairFit out; + fitPair(colId, posId, eleId, &out, original); + return out; + }; + auto crossedPrefilter = [&](int64_t posId, int64_t eleId) { + const auto pos = tracks.rawIteratorAt(posId); + const auto ele = tracks.rawIteratorAt(eleId); + if (std::fabs(pos.tgl() - ele.tgl()) > recovery.finderMaxDeltaTgl) { + return false; + } + const o2::track::TrackAuxPar cp(getTrackPar(pos), d_bz); + const o2::track::TrackAuxPar ce(getTrackPar(ele), d_bz); + return std::fabs(circleGap(cp, ce)) < recovery.finderMaxCircleGap; + }; + std::map storedFit; + auto storedFitOf = [&](int idx) -> const PairFit& { + auto it = storedFit.find(idx); + if (it == storedFit.end()) { + const auto& c = vecV0Dedup[idx]; + it = storedFit.emplace(idx, probeFit(c.colID, c.posID, c.eleID, true)).first; + } + return it->second; + }; - // find minimal pca - if (dedup.deduplicationMode == V0DeduplicationMode::Pairwise) { - for (const auto& [key, value] : pca_map) { - auto v0Id = std::get<0>(key); - auto collisionId = std::get<1>(key); - auto posId = std::get<2>(key); - auto eleId = std::get<3>(key); - float v0pca = value; - float cospa = cospa_map[key]; - bool is_closest_v0 = true; - bool is_most_aligned_v0 = true; - - for (const auto& [key_tmp, value_tmp] : pca_map) { - auto v0Id_tmp = std::get<0>(key_tmp); - auto collisionId_tmp = std::get<1>(key_tmp); - auto posId_tmp = std::get<2>(key_tmp); - auto eleId_tmp = std::get<3>(key_tmp); - float v0pca_tmp = value_tmp; - float cospa_tmp = cospa_map[key_tmp]; - - if (v0Id == v0Id_tmp) { // skip exactly the same v0 + // test all close pairs of stored photons + struct Test { + SwapRecord rec; + float gain = -999.f; + }; + std::vector swaps; + for (const auto& [colId, members] : storedOfColl) { + std::vector tests; + for (size_t a = 0; a < members.size(); ++a) { + for (size_t b = a + 1; b < members.size(); ++b) { + const auto& ci = vecV0Dedup[members[a]]; + const auto& cj = vecV0Dedup[members[b]]; + if (ci.posID == cj.posID || ci.eleID == cj.eleID) { continue; } - - if (collisionId != collisionId_tmp && eleId == eleId_tmp && posId == posId_tmp && cospa < cospa_tmp) { - if (diag != nullptr) { - diag->blockersByKey[key].push_back(key_tmp); - } - is_most_aligned_v0 = false; - break; + const float q = qInv(ci.pxAtPV, ci.pyAtPV, ci.pzAtPV, cj.pxAtPV, cj.pyAtPV, cj.pzAtPV); + if (q > recovery.swapMaxQ || std::hypot(ci.svX - cj.svX, ci.svY - cj.svY, ci.svZ - cj.svZ) > recovery.swapMaxConvDist) { + continue; } - if ((eleId == eleId_tmp || posId == posId_tmp) && v0pca > v0pca_tmp) { - if (diag != nullptr) { - diag->blockersByKey[key].push_back(key_tmp); - } - is_closest_v0 = false; - break; - } - } // end of pca_map tmp loop - - bool is_stored = std::find(stored_v0Ids.begin(), stored_v0Ids.end(), std::make_pair(posId, eleId)) != stored_v0Ids.end(); - if (is_closest_v0 && is_most_aligned_v0 && !is_stored) { - // auto v0 = v0s.rawIteratorAt(v0Id); - // auto collision = collisions.rawIteratorAt(collisionId); - // auto pos = tracks.rawIteratorAt(posId); - // auto ele = tracks.rawIteratorAt(eleId); - // LOGF(info, "!accept! | collision id = %d | v0id1 = %d , posid1 = %d , eleid1 = %d , pca1 = %f , cospa = %f", collisionId, v0Id, posId, eleId, v0pca, cospa); - - // fillV0Table(v0, true); - stored_v0Ids.emplace_back(std::make_pair(posId, eleId)); - stored_fullv0Ids.emplace_back(std::make_tuple(v0Id, collisionId, posId, eleId)); - nv0_map[collisionId]++; - } - } // end of pca_map loop - // LOGF(info, "pca_map.size() = %d", pca_map.size()); - - } else if (dedup.deduplicationMode == V0DeduplicationMode::KeepAll) { - // Keep-all: Every candidate that survived the quality cuts is stored, including all duplicates - stored_v0Ids.clear(); - stored_fullv0Ids.clear(); - nv0_map.clear(); - for (const auto& v0Cand : vecV0Dedup) { - stored_v0Ids.emplace_back(v0Cand.posID, v0Cand.eleID); - stored_fullv0Ids.emplace_back(v0Cand.v0ID, v0Cand.colID, v0Cand.posID, v0Cand.eleID); - nv0_map[v0Cand.colID]++; - } - std::sort(stored_fullv0Ids.begin(), stored_fullv0Ids.end(), - [](const auto& a, const auto& b) { - return std::get<1>(a) < std::get<1>(b); - }); - } else if (dedup.deduplicationMode == V0DeduplicationMode::GroupMatching) { - const int maxEnum = std::min(dedup.dedupMaxGroupSize.value, 16); // o2-linter: disable=magic-number (subset check of photons) - const int nCand = static_cast(vecV0Dedup.size()); - stored_v0Ids.clear(); - stored_fullv0Ids.clear(); - nv0_map.clear(); - int nGreedyFallback = 0; - - // ---- conflict groups: candidates connected through a shared leg ----- - std::vector parent(nCand); - for (int i = 0; i < nCand; ++i) { - parent[i] = i; + const PairFit storedI = storedFitOf(members[a]); + const PairFit storedJ = storedFitOf(members[b]); + const bool preI = crossedPrefilter(ci.posID, cj.eleID); + const PairFit crossedI = preI ? probeFit(colId, ci.posID, cj.eleID) : PairFit{}; + const int lineI = preI && !crossedI.ok ? mLastFitReject : 0; + const bool preJ = crossedPrefilter(cj.posID, ci.eleID); + const PairFit crossedJ = preJ ? probeFit(colId, cj.posID, ci.eleID) : PairFit{}; + const int lineJ = preJ && !crossedJ.ok ? mLastFitReject : 0; + Test t; + t.rec = {keyOf(ci), keyOf(cj), q, storedI.ok && storedJ.ok && crossedI.ok && crossedJ.ok, false}; + t.rec.fail = (storedI.ok ? 0 : 1) | (storedJ.ok ? 0 : 2) | (preI ? 0 : 4) | (preJ ? 0 : 8) | (preI && !crossedI.ok ? 16 : 0) | (preJ && !crossedJ.ok ? 32 : 0); // o2-linter: disable=magic-number (bit mask) + t.rec.line = lineI > 0 ? lineI : lineJ; + if (t.rec.complete) { // gain > 0: the crossed pairing has the lower (better) summed dedup score + t.gain = (storedI.score + storedJ.score) - (crossedI.score + crossedJ.score); + t.rec.gain = t.gain; + } + tests.push_back(t); + } } - auto findRoot = [&parent](int x) { - while (parent[x] != x) { - parent[x] = parent[parent[x]]; // path halving - x = parent[x]; + std::vector order(tests.size()); + std::iota(order.begin(), order.end(), 0); + std::sort(order.begin(), order.end(), [&](int x, int y) { return tests[x].gain > tests[y].gain; }); + std::set used; + for (const int& k : order) { + auto& t = tests[k]; + if (t.rec.complete && t.gain > 0.f && !used.contains(t.rec.keyI) && !used.contains(t.rec.keyJ)) { + t.rec.swapped = true; + used.insert(t.rec.keyI); + used.insert(t.rec.keyJ); + swaps.push_back(t.rec); } - return x; - }; - std::unordered_map firstCandOfTrack; - for (int i = 0; i < nCand; ++i) { - for (const int& trackId : {vecV0Dedup[i].posID, vecV0Dedup[i].eleID}) { - auto [it, inserted] = firstCandOfTrack.try_emplace(trackId, i); - if (!inserted) { - parent[findRoot(i)] = findRoot(it->second); - } + registry.fill(HIST("Swap/hTested"), 0.f); + registry.fill(HIST("Swap/hQ"), t.rec.q, t.rec.swapped ? 1.f : 0.f); + if (diag != nullptr) { + diag->swaps.push_back(t.rec); } } - std::map> groups; - for (int i = 0; i < nCand; ++i) { - groups[findRoot(i)].push_back(i); + } + if (swaps.empty()) { + return; + } + + // apply swaps: the crossed photons become candidates and replace the stored pair + const std::set storedBefore(stored_fullv0Ids.begin(), stored_fullv0Ids.end()); + std::set removedKeys; + std::vector addedKeys; + for (const auto& s : swaps) { + const int64_t colId = s.keyI.collisionId; + const int64_t posI = s.keyI.posId, eleI = s.keyI.eleId, posJ = s.keyJ.posId, eleJ = s.keyJ.eleId; + const CandKey a1 = CandKey{.v0Id = -1, .collisionId = colId, .posId = posI, .eleId = eleJ}; + const CandKey a2 = CandKey{.v0Id = -1, .collisionId = colId, .posId = posJ, .eleId = eleI}; + if (storedBefore.contains(a1) || storedBefore.contains(a2)) { + continue; } + if (!pca_map.contains(a1)) { + fitPair(colId, posI, eleJ, nullptr); + } + if (!pca_map.contains(a2)) { + fitPair(colId, posJ, eleI, nullptr); + } + if (!pca_map.contains(a1) || !pca_map.contains(a2)) { + continue; + } + removedKeys.insert(s.keyI); + removedKeys.insert(s.keyJ); + addedKeys.push_back(a1); + addedKeys.push_back(a2); + mRelaxedKeys.insert(a1); // refitted for the table without the AP cut, as in the resolver fit + mRelaxedKeys.insert(a2); + registry.fill(HIST("Swap/hTested"), 1.f); + if (diag != nullptr) { + diag->blockersByKey[s.keyI] = {a1, a2}; + diag->blockersByKey[s.keyJ] = {a1, a2}; + } + } + std::vector kept; + kept.reserve(stored_fullv0Ids.size()); + for (const auto& key : stored_fullv0Ids) { + if (!removedKeys.contains(key)) { + kept.push_back(key); + } + } + kept.insert(kept.end(), addedKeys.begin(), addedKeys.end()); + setStored(std::move(kept)); + } - // ---- decide each group on its own ---------------------------------- - for (auto& [root, members] : groups) { // o2-linter: disable=const-ref-in-for-loop (members is sorted in place below) - const int nMembers = static_cast(members.size()); - std::vector selected(nMembers, 0); + // deduplication + Preslice perCollision = o2::aod::v0::collisionId; + std::map pca_map; // (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) -> pca + std::map cospa_map; // (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) -> cospa + int mLastFitReject = 0; // source line of the last rejection in fitV0Candidate, 0 = accepted (diagnostic) + DedupDiag* mDiag = nullptr; // run 1 with MC diagnostics: where the finder and fillV0Table record the genuine leg pairs + std::unordered_map mLegMother; // MC: track -> first mother of its MC electron (genuine leg pairs share it) + std::unordered_map mOccOfSourceEvent; // run 2: mixer event uid -> occupancy (from its same-event artificial event) + std::vector vecV0Dedup; // vector with all V0Candidates that is used to sort them by score (see struct V0CandidateHelper for more details of content) + std::vector> stored_v0Ids; // (pos.globalIndex(), ele.globalIndex()) + std::vector stored_fullv0Ids; // (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) + std::unordered_map nv0_map; // map collisionId -> nv0 + + // mode 0: a candidate is kept if no other candidate sharing a leg has a smaller PCA and no copy in another collision has a larger cosPA + void selectPairwise(DedupDiag* diag) + { + for (const auto& [key, value] : pca_map) { + auto v0Id = key.v0Id; + auto collisionId = key.collisionId; + auto posId = key.posId; + auto eleId = key.eleId; + float v0pca = value; + float cospa = cospa_map[key]; + bool is_closest_v0 = true; + bool is_most_aligned_v0 = true; + + for (const auto& [key_tmp, value_tmp] : pca_map) { + auto collisionId_tmp = key_tmp.collisionId; + auto posId_tmp = key_tmp.posId; + auto eleId_tmp = key_tmp.eleId; + float v0pca_tmp = value_tmp; + float cospa_tmp = cospa_map[key_tmp]; + + if (key == key_tmp) { // skip exactly the same candidate + continue; + } - // Fix the order inside the group: best score first - std::sort(members.begin(), members.end(), [this](int a, int b) { - if (vecV0Dedup[a].score != vecV0Dedup[b].score) { - return vecV0Dedup[a].score < vecV0Dedup[b].score; + if (collisionId != collisionId_tmp && eleId == eleId_tmp && posId == posId_tmp && cospa < cospa_tmp) { + if (diag != nullptr) { + diag->blockersByKey[key].push_back(key_tmp); + } + is_most_aligned_v0 = false; + break; + } + if ((eleId == eleId_tmp || posId == posId_tmp) && v0pca > v0pca_tmp) { + if (diag != nullptr) { + diag->blockersByKey[key].push_back(key_tmp); } + is_closest_v0 = false; + break; + } + } // end of pca_map tmp loop + + bool is_stored = std::find(stored_v0Ids.begin(), stored_v0Ids.end(), std::make_pair(posId, eleId)) != stored_v0Ids.end(); + if (is_closest_v0 && is_most_aligned_v0 && !is_stored) { + // auto v0 = v0s.rawIteratorAt(v0Id); + // auto collision = collisions.rawIteratorAt(collisionId); + // auto pos = tracks.rawIteratorAt(posId); + // auto ele = tracks.rawIteratorAt(eleId); + // LOGF(info, "!accept! | collision id = %d | v0id1 = %d , posid1 = %d , eleid1 = %d , pca1 = %f , cospa = %f", collisionId, v0Id, posId, eleId, v0pca, cospa); + + // fillV0Table(v0, true); + stored_v0Ids.emplace_back(std::make_pair(posId, eleId)); + stored_fullv0Ids.push_back(CandKey{.v0Id = v0Id, .collisionId = collisionId, .posId = posId, .eleId = eleId}); + nv0_map[collisionId]++; + } + } // end of pca_map loop + if (recovery.runFinder || recovery.runSwapResolver) { + std::stable_sort(stored_fullv0Ids.begin(), stored_fullv0Ids.end(), [](const auto& a, const auto& b) { return a.collisionId < b.collisionId; }); + } + // LOGF(info, "pca_map.size() = %d", pca_map.size()); + } + + // mode 3: every candidate is kept, including all duplicates + void keepAll() + { + // Keep-all: Every candidate that survived the quality cuts is stored, including all duplicates + stored_v0Ids.clear(); + stored_fullv0Ids.clear(); + nv0_map.clear(); + for (const auto& v0Cand : vecV0Dedup) { + stored_v0Ids.emplace_back(v0Cand.posID, v0Cand.eleID); + stored_fullv0Ids.push_back(keyOf(v0Cand)); + nv0_map[v0Cand.colID]++; + } + std::sort(stored_fullv0Ids.begin(), stored_fullv0Ids.end(), + [](const auto& a, const auto& b) { + return a.collisionId < b.collisionId; + }); + } + + // mode 2: candidates sharing a leg form a conflict group; per group the best leg-disjoint subset is kept + void selectByConflictGroup(DedupDiag* diag) + { + const int maxEnum = std::min(dedup.dedupMaxGroupSize.value, 16); // o2-linter: disable=magic-number (subset check of photons) + const int nCand = static_cast(vecV0Dedup.size()); + stored_v0Ids.clear(); + stored_fullv0Ids.clear(); + nv0_map.clear(); + mGroupOfKey.clear(); + mPermuteResults.clear(); + if constexpr (kMixedInstance) { + mGroupSizeOfCand.assign(nCand, 0); + } + int nGreedyFallback = 0; + + // conflict groups: candidates connected through a shared leg + std::vector parent(nCand); + for (int i = 0; i < nCand; ++i) { + parent[i] = i; + } + auto findRoot = [&parent](int x) { + while (parent[x] != x) { + parent[x] = parent[parent[x]]; // path halving + x = parent[x]; + } + return x; + }; + std::unordered_map firstCandOfTrack; + for (int i = 0; i < nCand; ++i) { + for (const int& trackId : {vecV0Dedup[i].posID, vecV0Dedup[i].eleID}) { + auto [it, inserted] = firstCandOfTrack.try_emplace(trackId, i); + if (!inserted) { + parent[findRoot(i)] = findRoot(it->second); + } + } + } + std::map> groups; + for (int i = 0; i < nCand; ++i) { + groups[findRoot(i)].push_back(i); + } + + // decide each group + for (auto& [root, members] : groups) { // o2-linter: disable=const-ref-in-for-loop (members is sorted in place below) + const int nMembers = static_cast(members.size()); + std::vector selected(nMembers, 0); + + // Fix the order inside the group: best score first + std::sort(members.begin(), members.end(), [this](int a, int b) { + if (vecV0Dedup[a].score != vecV0Dedup[b].score) { + return vecV0Dedup[a].score < vecV0Dedup[b].score; + } + if (vecV0Dedup[a].v0ID != vecV0Dedup[b].v0ID) { return vecV0Dedup[a].v0ID < vecV0Dedup[b].v0ID; - }); + } + return std::tie(vecV0Dedup[a].posID, vecV0Dedup[a].eleID, vecV0Dedup[a].colID) < std::tie(vecV0Dedup[b].posID, vecV0Dedup[b].eleID, vecV0Dedup[b].colID); + }); - if (nMembers == 1) { - selected[0] = 1; - } else if (nMembers <= maxEnum) { + if (nMembers == 1) { + selected[0] = 1; + } else if (nMembers <= maxEnum) { - std::unordered_map legBit; - std::vector legMask(nMembers, 0); + std::unordered_map legBit; + std::vector legMask(nMembers, 0); + for (int k = 0; k < nMembers; ++k) { + for (const int& trackId : {vecV0Dedup[members[k]].posID, vecV0Dedup[members[k]].eleID}) { + const int bit = legBit.try_emplace(trackId, static_cast(legBit.size())).first->second; + legMask[k] |= (1ull << bit); // at most 2 * 16 = 32 legs, fits into 64 bit + } + } + std::vector scores(nMembers); + for (int k = 0; k < nMembers; ++k) { + scores[k] = vecV0Dedup[members[k]].score; + } + const auto sol = solveConflictGroup(legMask, scores, dedup.groupMatchingScoreThreshold.value, diag != nullptr); + for (int k = 0; k < nMembers; ++k) { + selected[k] = ((sol.bestMask & (1u << k)) != 0) ? 1 : 0; + if (diag != nullptr && selected[k] == 0 && sol.bestWithCount[k] >= 0) { + const auto& cand = vecV0Dedup[members[k]]; + diag->lossMargin[keyOf(cand)] = + std::make_pair(sol.bestCount - sol.bestWithCount[k], sol.bestWithScore[k] - sol.bestScore); + } + } + if constexpr (kMixedInstance) { // permutation test: the same group in reversed order must select the same candidates + const std::vector legMaskRev(legMask.rbegin(), legMask.rend()); + const std::vector scoresRev(scores.rbegin(), scores.rend()); + const auto solRev = solveConflictGroup(legMaskRev, scoresRev, dedup.groupMatchingScoreThreshold.value, false); + bool changed = false; for (int k = 0; k < nMembers; ++k) { - for (const int& trackId : {vecV0Dedup[members[k]].posID, vecV0Dedup[members[k]].eleID}) { - const int bit = legBit.try_emplace(trackId, static_cast(legBit.size())).first->second; - legMask[k] |= (1ull << bit); // at most 2 * 16 = 32 legs, fits into 64 bit - } + changed = changed || (((solRev.bestMask & (1u << (nMembers - 1 - k))) != 0) != (selected[k] != 0)); } - uint32_t bestMask = 0; - int bestCount = 0; - float bestScore = 0.f; - - std::vector bestWithCount(nMembers, -1); - std::vector bestWithScore(nMembers, 0.f); - for (uint32_t mask = 1; mask < (1u << nMembers); ++mask) { - uint64_t usedLegsLocal = 0; - float scoreSum = 0.f; - int count = 0; - bool disjoint = true; - for (int k = 0; k < nMembers; ++k) { - if ((mask & (1u << k)) == 0) { - continue; - } - if ((usedLegsLocal & legMask[k]) != 0) { - disjoint = false; - break; - } - usedLegsLocal |= legMask[k]; - scoreSum += vecV0Dedup[members[k]].score; - ++count; - } - if (!disjoint) { + mPermuteResults.push_back({vecV0Dedup[members[0]].colID, nMembers, changed, false}); + } + } else { + // Too large to enumerate: greedy by score + ++nGreedyFallback; + if constexpr (kMixedInstance) { + mPermuteResults.push_back({vecV0Dedup[members[0]].colID, nMembers, false, true}); + } + std::vector usedTracks; + usedTracks.reserve(2 * nMembers); + for (int k = 0; k < nMembers; ++k) { + const auto& cand = vecV0Dedup[members[k]]; + if (std::find(usedTracks.begin(), usedTracks.end(), cand.posID) != usedTracks.end() || + std::find(usedTracks.begin(), usedTracks.end(), cand.eleID) != usedTracks.end()) { + continue; + } + usedTracks.push_back(cand.posID); + usedTracks.push_back(cand.eleID); + selected[k] = 1; + } + } + + // run 1 (MC diagnostic of the swap decision): half-swap rule: if a candidate is worse than two others that share its legs, and the crossed candidate is not present, replace it + if (dedup.dedupHalfSwapRule && nMembers >= 3) { + int nReplaced = 0; + for (int t = 0; t < nMembers; ++t) { + if (selected[t] != 0) { + continue; + } + const auto& cT = vecV0Dedup[members[t]]; + int w1 = -1, w2 = -1; + for (int k = 0; k < nMembers; ++k) { + if (selected[k] == 0 || k == t) { continue; } - - if (count > bestCount || (count == bestCount && scoreSum < bestScore)) { - bestCount = count; - bestScore = scoreSum; - bestMask = mask; + const auto& cW = vecV0Dedup[members[k]]; + if (cW.posID == cT.posID) { + w1 = k; } - if (diag != nullptr) { - for (int k = 0; k < nMembers; ++k) { - if ((mask & (1u << k)) == 0) { - continue; - } - if (count > bestWithCount[k] || (count == bestWithCount[k] && scoreSum < bestWithScore[k])) { - bestWithCount[k] = count; - bestWithScore[k] = scoreSum; - } - } + if (cW.eleID == cT.eleID) { + w2 = k; } } + if (w1 < 0 || w2 < 0 || w1 == w2) { + continue; + } + const auto& cW1 = vecV0Dedup[members[w1]]; + const auto& cW2 = vecV0Dedup[members[w2]]; + if (!(cT.score < std::min(cW1.score, cW2.score))) { + continue; + } + bool complementIsCandidate = false; for (int k = 0; k < nMembers; ++k) { - selected[k] = ((bestMask & (1u << k)) != 0) ? 1 : 0; - if (diag != nullptr && selected[k] == 0 && bestWithCount[k] >= 0) { - const auto& cand = vecV0Dedup[members[k]]; - diag->lossMargin[std::make_tuple(static_cast(cand.v0ID), static_cast(cand.colID), - static_cast(cand.posID), static_cast(cand.eleID))] = - std::make_pair(bestCount - bestWithCount[k], bestWithScore[k] - bestScore); + const auto& cC = vecV0Dedup[members[k]]; + if (cC.posID == cW2.posID && cC.eleID == cW1.eleID) { + complementIsCandidate = true; + break; } } - } else { - // Too large to enumerate: greedy by score (members is already sorted that way) - ++nGreedyFallback; - std::vector usedTracks; - usedTracks.reserve(2 * nMembers); - for (int k = 0; k < nMembers; ++k) { - const auto& cand = vecV0Dedup[members[k]]; - if (std::find(usedTracks.begin(), usedTracks.end(), cand.posID) != usedTracks.end() || - std::find(usedTracks.begin(), usedTracks.end(), cand.eleID) != usedTracks.end()) { + if (complementIsCandidate) { + continue; + } + selected[w1] = 0; + selected[w2] = 0; + selected[t] = 1; + ++nReplaced; + } + registry.fill(HIST("Dedup/hHalfSwapRule"), static_cast(nReplaced)); + } + + std::array groupInfo{root, nMembers, 0, static_cast(std::count(selected.begin(), selected.end(), 1))}; + if constexpr (kMixedInstance) { + std::set legsOfGroup; + for (const int& m : members) { + legsOfGroup.insert(vecV0Dedup[m].posID); + legsOfGroup.insert(vecV0Dedup[m].eleID); + mGroupSizeOfCand[m] = nMembers; + } + groupInfo[2] = static_cast(legsOfGroup.size()); + } + // store the winners, book the blockers of the losers + for (int k = 0; k < nMembers; ++k) { + const auto& cand = vecV0Dedup[members[k]]; + if (selected[k] != 0) { + stored_v0Ids.emplace_back(cand.posID, cand.eleID); + stored_fullv0Ids.push_back(keyOf(cand)); + nv0_map[cand.colID]++; + if constexpr (kMixedInstance) { + mGroupOfKey[keyOf(cand)] = groupInfo; + } + continue; + } + if (diag == nullptr) { + continue; + } + // all winners that took one leg + const CandKey victimKey = keyOf(cand); + for (int j = 0; j < nMembers; ++j) { + if (selected[j] == 0) { + continue; + } + const auto& winner = vecV0Dedup[members[j]]; + if (winner.posID == cand.posID || winner.eleID == cand.eleID) { + diag->blockersByKey[victimKey].push_back(keyOf(winner)); + } + } + } + } + + if (nGreedyFallback > 0) { + LOGF(warning, "group matching: %d conflict group(s) larger than dedupMaxGroupSize = %d were solved greedily", nGreedyFallback, maxEnum); + } + + // the table expects candidates ordered by collision + std::sort(stored_fullv0Ids.begin(), stored_fullv0Ids.end(), + [](const auto& a, const auto& b) { + return a.collisionId < b.collisionId; + }); + } + + // mode 1: candidates sorted by score (lower = better), a candidate is kept if both legs are still free + void selectGreedy(const size_t nTracks, DedupDiag* diag) + { + // Sort best candidates first, depending on score + std::sort(vecV0Dedup.begin(), vecV0Dedup.end()); + // container to keep track of which electron and positron tracks have already been used + std::vector usedLegs(nTracks, false); + std::unordered_map ownerOfLeg; // diagnostics only + stored_v0Ids.clear(); + stored_fullv0Ids.clear(); + nv0_map.clear(); + for (const auto& v0Cand : vecV0Dedup) { + const CandKey candKey = keyOf(v0Cand); + if (usedLegs[v0Cand.posID] || usedLegs[v0Cand.eleID]) { + if (diag != nullptr) { + // both legs, not just the first one found: a cross-leg fake is usually blocked by + // two different winners, and that is exactly the case being measured here + auto& blockers = diag->blockersByKey[candKey]; + for (const int& legId : {v0Cand.posID, v0Cand.eleID}) { + const auto itOwner = ownerOfLeg.find(legId); + if (itOwner == ownerOfLeg.end()) { + continue; + } + if (std::find(blockers.begin(), blockers.end(), itOwner->second) == blockers.end()) { + blockers.push_back(itOwner->second); // the same winner may hold both legs + } + } + } + continue; + } + usedLegs[v0Cand.posID] = true; + usedLegs[v0Cand.eleID] = true; + if (diag != nullptr) { + ownerOfLeg[v0Cand.posID] = candKey; + ownerOfLeg[v0Cand.eleID] = candKey; + } + stored_v0Ids.emplace_back(v0Cand.posID, v0Cand.eleID); + stored_fullv0Ids.push_back(keyOf(v0Cand)); + nv0_map[v0Cand.colID]++; + } + + // sort accepted candidates by collision ID to be compatible with table + std::sort(stored_fullv0Ids.begin(), stored_fullv0Ids.end(), + [](const auto& a, const auto& b) { + return a.collisionId < b.collisionId; + }); + } + + template + void build(TCollisions const& collisions, TV0s const& v0s, TTracks const& tracks, TBCs const&, DedupDiag* diag = nullptr) + { + mRelaxedKeys.clear(); + mLegDcaCache.clear(); + mDiag = diag; + mLegMother.clear(); + if constexpr (isMC) { + if (diag != nullptr) { // genuine leg pairs = opposite-sign MC electrons with the same first mother (MCDedup/Inventory/hSwapOrigin) + for (const auto& track : tracks) { + if (!track.has_mcParticle()) { + continue; + } + const auto mc = track.template mcParticle_as(); + if (std::abs(mc.pdgCode()) == kElectron && mc.has_mothers()) { + mLegMother[track.globalIndex()] = mc.mothersIds()[0]; + } + } + } + } + for (const auto& collision : collisions) { + if constexpr (isMC) { + if (!collision.has_mcCollision()) { + continue; + } + } + + if (!collision.isSelected()) { + continue; + } + + // if constexpr (isTriggerAnalysis) { + // if (collision.triggerMask_raw() == 0) { + // continue; + // } + // } + + nv0_map[collision.globalIndex()] = 0; + + const auto& bc = collision.template foundBC_as(); + initCCDB(bc); + registry.fill(HIST("hCollisionCounter"), 1); + + updateCCDB(bc); // delay update until is needed + + vecV0Dedup.reserve(30000); // rough estimate for number of V0s per DF + const auto& v0s_per_coll = v0s.sliceBy(perCollision, collision.globalIndex()); + // LOGF(info, "n v0 = %d", v0s_per_coll.size()); + for (const auto& v0 : v0s_per_coll) { + // LOGF(info, "collision.globalIndex() = %d, v0.globalIndex() = %d, v0.posTrackId() = %d, v0.negTrackId() = %d", collision.globalIndex(), v0.globalIndex(), v0.posTrackId() , v0.negTrackId()); + fillV0Table(v0, false); + } // end of v0 loop + } // end of collision loop + + if (recovery.runFinder) { + runPhotonFinder(collisions, tracks); + } + + stored_v0Ids.reserve(pca_map.size()); // number of photon candidates per DF + stored_fullv0Ids.reserve(pca_map.size()); // number of photon candidates per DF + + switch (dedup.deduplicationMode.value) { + case V0DeduplicationMode::Pairwise: + selectPairwise(diag); + break; + case V0DeduplicationMode::KeepAll: + keepAll(); + break; + case V0DeduplicationMode::GroupMatching: + selectByConflictGroup(diag); + break; + default: + selectGreedy(tracks.size(), diag); + break; + } + + if (dedup.resolveSplitLegs) { + resolveSplitLegConflicts(tracks, diag); + } + if (recovery.runSwapResolver) { + runSwapResolver(collisions, tracks, diag); + } + + if (diag != nullptr) { + diag->snapshot = vecV0Dedup; + diag->stored = stored_fullv0Ids; + } + + std::set skimRejected; + if (skim.minNv0PerCollision > 0 || skim.maxMinvPairPerCollision > 0.f || skim.minMinvPairPerCollision > 0.f) { // o2-linter: disable=magic-number (check if configurable is enabled) + const std::set storedSetSkim(stored_fullv0Ids.begin(), stored_fullv0Ids.end()); + std::unordered_map> candsPerColl; + for (const auto& c : vecV0Dedup) { + if (storedSetSkim.contains(keyOf(c))) { + candsPerColl[c.colID].push_back(&c); + } + } + for (const auto& [colId, cands] : candsPerColl) { + if (skim.minNv0PerCollision > 0 && static_cast(cands.size()) < skim.minNv0PerCollision) { + skimRejected.insert(colId); + continue; + } + if (skim.maxMinvPairPerCollision > 0.f || skim.minMinvPairPerCollision > 0.f) { // o2-linter: disable=magic-number (check if configurable is enabled) + bool hasPairOfInterest = false; + const float minMinv = skim.minMinvPairPerCollision > 0.f ? skim.minMinvPairPerCollision.value : 0.f; + const float maxMinv = skim.maxMinvPairPerCollision > 0.f ? skim.maxMinvPairPerCollision.value : std::numeric_limits::max(); + for (size_t i = 0; i < cands.size() && !hasPairOfInterest; ++i) { + for (size_t j = i + 1; j < cands.size(); ++j) { + ROOT::Math::PxPyPzMVector v1(cands[i]->pxAtPV, cands[i]->pyAtPV, cands[i]->pzAtPV, 0.f); + ROOT::Math::PxPyPzMVector v2(cands[j]->pxAtPV, cands[j]->pyAtPV, cands[j]->pzAtPV, 0.f); + const double massPair = (v1 + v2).M(); + if (massPair > minMinv && massPair < maxMinv) { + hasPairOfInterest = true; + break; + } + } + } + if (!hasPairOfInterest) { + skimRejected.insert(colId); + } + } + } + } + + for (const auto& fullv0Id : stored_fullv0Ids) { + auto v0Id = fullv0Id.v0Id; + const auto collisionId = fullv0Id.collisionId; + if (skimRejected.contains(collisionId)) { + continue; // collision failed the skim - do not store its photons/legs + } + // auto posId = fullv0Id.posId; + // auto eleId = fullv0Id.eleId; + // LOGF(info, "!accept! | collision id = %d | v0id = %d , posid = %d , eleid = %d", collisionId, v0Id, posId, eleId); + + if (v0Id >= 0) { + auto v0 = v0s.rawIteratorAt(v0Id); + if constexpr (enableFilter) { + auto collision_tmp = v0.template collision_as(); // collision where this v0 belongs. + if (!(collision_tmp.neeuls() >= 1 || collision_tmp.neeuls() + nv0_map[collision_tmp.globalIndex()] >= 2)) { // o2-linter: disable=magic-number (nonsensical default value) + continue; + } + // LOGF(info, "collision_tmp.globalIndex() = %d, collision_tmp.neeuls() = %d, nv0_map = %d", collision_tmp.globalIndex(), collision_tmp.neeuls(), nv0_map[collision_tmp.globalIndex()]); + } + + fillV0Table(v0, true); + continue; + } + const auto collision = collisions.rawIteratorAt(collisionId); + if constexpr (enableFilter) { + if (!(collision.neeuls() >= 1 || collision.neeuls() + nv0_map[collisionId] >= 2)) { // o2-linter: disable=magic-number (nonsensical default value) + continue; + } + } + fitV0Candidate(collision, tracks.rawIteratorAt(fullv0Id.posId), tracks.rawIteratorAt(fullv0Id.eleId), v0Id, true, + FitOptions{.nonSVertexer = !recovery.finderUniformFit.value, .relaxAP = mRelaxedKeys.contains(fullv0Id) && !recovery.finderUniformFit.value}); + } // end of fullv0Id loop + if (fillFreeLegs || fillFreeLegIds) { + writeFreeLegs(collisions, tracks, skimRejected); + } + + // for (const auto& collision : collisions) { + // if constexpr (isMC) { + // if (!collision.has_mcCollision()) { + // continue; + // } + // } + // // events_ngpcm(nv0_map[collision.globalIndex()]); + // } // end of collision loop + + pca_map.clear(); + cospa_map.clear(); + nv0_map.clear(); + stored_v0Ids.clear(); + stored_v0Ids.shrink_to_fit(); + stored_fullv0Ids.clear(); + stored_fullv0Ids.shrink_to_fit(); + vecV0Dedup.clear(); + } // end of build + + // MC photon pair information for the deduplication diagnostics (run 1) and the mixer (run 2) + struct MixPairPhoton { + float px = 0.f, py = 0.f, pz = 0.f; + std::pair posKey{-1, -1}, eleKey{-1, -1}; + bool posTrue = false, eleTrue = false; + int64_t posRow = -1, eleRow = -1; // PCMMixLegs rows + bool original = true; // leg pairing of a run-1 photon (false: crossed pairing built in run 2) + std::array group{-1, 0, 0, 0}; // run-2 conflict group (id, candidates, legs, stored photons), id -1: unknown + float vx = 0.f, vy = 0.f, vz = 0.f; // conversion point (run 1 before, run 2 after) + struct LegMC { // MC of one leg for the fake-pair classes (ids within the DF of the source event) + int64_t evt = -1; + int pid = -1, mid = -1, gmid = -1, pdg = 0, mpdg = 0, trk = -1, anc = -1; + int ggid = -1, root = -1, rootPdg = 0; // mother of the grandmother, root of the decay chain of the photon + bool its = false; + float phi = 0.f, tgl = 0.f, sharedFrac = 0.f; // track direction and TPC shared-cluster fraction (data-computable) + float vx = 0.f, vy = 0.f, vz = 0.f; // run-1 conversion point of the photon the leg belonged to + }; + LegMC posMC, eleMC; + }; + template + static void setMixPairLegs(MixPairPhoton& p, TLeg const& pos, TLeg const& ele) + { + p.posKey = {pos.sourceEventUid(), pos.mcMotherId()}; + p.eleKey = {ele.sourceEventUid(), ele.mcMotherId()}; + p.posTrue = pos.mcMotherId() >= 0 && pos.mcMotherPdg() == kGamma; + p.eleTrue = ele.mcMotherId() >= 0 && ele.mcMotherPdg() == kGamma; + using LegMC = typename MixPairPhoton::LegMC; + auto legMC = [](TLeg const& l) { + return LegMC{l.sourceEventUid(), l.mcParticleId(), l.mcMotherId(), l.mcGrandMotherId(), l.mcPdg(), l.mcMotherPdg(), l.origTrackId(), l.mcAncestorId(), + l.mcGGMotherId(), l.mcRootId(), l.mcRootPdg(), static_cast(l.hasITS()), l.phi(), l.tgl(), l.tpcFractionSharedCls(), l.sourceVx(), l.sourceVy(), l.sourceVz()}; + }; + p.posMC = legMC(pos); + p.eleMC = legMC(ele); + } + + static int mixCommonOrigin(MixPairPhoton::LegMC const& x, MixPairPhoton::LegMC const& y) + { + if (x.mid < 0 || y.mid < 0 || (x.evt == y.evt && x.mid == y.mid)) { + return -1; + } + if (x.evt != y.evt) { + return 0; + } + if (x.gmid >= 0 && x.gmid == y.gmid) { + return 3; + } + if (x.ggid >= 0 && x.ggid == y.ggid) { + return 2; + } + return (x.root >= 0 && x.root == y.root) ? 1 : 0; + } + // common origin of the two MC photons whose legs form a photon pair (true pair, full or half swap): first leg vs the first leg of + // another MC photon among the remaining three + static int mixPairOrigin(MixPairPhoton const& a, MixPairPhoton const& b) + { + const auto& x = a.posMC; + for (const auto* y : {&a.eleMC, &b.posMC, &b.eleMC}) { + if (y->mid >= 0 && x.mid >= 0 && !(x.evt == y->evt && x.mid == y->mid)) { + return mixCommonOrigin(x, *y); + } + } + return -1; + } + + static bool mixRelated(MixPairPhoton const& a, MixPairPhoton const& b) + { + using L = MixPairPhoton::LegMC; + auto radiatedBy = [](L const& x, L const& y) { return x.evt == y.evt && x.gmid >= 0 && x.gmid == y.pid; }; + auto siblings = [](L const& x, L const& y) { return x.evt == y.evt && x.gmid >= 0 && x.gmid == y.gmid && x.mid != y.mid; }; + for (const auto* x : {&a.posMC, &a.eleMC}) { + for (const auto* y : {&b.posMC, &b.eleMC}) { + if (radiatedBy(*x, *y) || radiatedBy(*y, *x) || siblings(*x, *y)) { + return true; + } + } + } + return false; + } + + static int mixFakeClass(MixPairPhoton const& a, MixPairPhoton const& b) + { + using L = MixPairPhoton::LegMC; + const std::array la{&a.posMC, &a.eleMC}, lb{&b.posMC, &b.eleMC}; + auto samePart = [](L const& x, L const& y) { return x.evt == y.evt && x.pid >= 0 && x.pid == y.pid; }; + auto sameMother = [](L const& x, L const& y) { return x.evt == y.evt && x.mid >= 0 && x.mid == y.mid; }; + auto daughterOf = [](L const& x, L const& y) { return x.evt == y.evt && x.mid >= 0 && x.mid == y.pid; }; + auto truePhoton = [&](MixPairPhoton const& p) { return p.posMC.mpdg == kGamma && p.eleMC.mpdg == kGamma && sameMother(p.posMC, p.eleMC); }; + for (const auto* x : la) { + for (const auto* y : lb) { + if (x->evt == y->evt && x->trk >= 0 && x->trk == y->trk) { + return 12; + } + } + } + if (truePhoton(a) && truePhoton(b) && sameMother(a.posMC, b.posMC)) { + return 1; + } + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < 2; ++j) { + if (samePart(*la[i], *lb[j])) { + return (la[1 - i]->mpdg == kGamma && lb[1 - j]->mpdg == kGamma) ? 2 : 3; + } + } + } + for (const auto* x : la) { + for (const auto* y : lb) { + if (x->mpdg == kGamma && sameMother(*x, *y)) { + return 4; + } + } + } + for (const auto* x : la) { + for (const auto* y : lb) { + if (daughterOf(*x, *y) || daughterOf(*y, *x)) { + return 5; + } + } + } + if (mixRelated(a, b)) { + return 5; + } + for (const auto* x : la) { + for (const auto* y : lb) { + if (sameMother(*x, *y)) { + return 6; + } + } + } + const bool aFake = !truePhoton(a), bFake = !truePhoton(b); + if (aFake && bFake) { + return 7; + } + const auto& f = aFake ? a : b; + if (sameMother(f.posMC, f.eleMC)) { + return 8; + } + if (std::abs(f.posMC.pdg) != kElectron || std::abs(f.eleMC.pdg) != kElectron) { + return 9; + } + if (f.posMC.mpdg == kGamma && f.eleMC.mpdg == kGamma) { + return 10; + } + return 11; + } + // two photons come from one shower: a leg of each has the same physical-primary ancestor (photons of one primary photon/e+-) + static bool mixSameAncestor(MixPairPhoton const& a, MixPairPhoton const& b) + { + for (const auto* x : {&a.posMC, &a.eleMC}) { + for (const auto* y : {&b.posMC, &b.eleMC}) { + if (x->evt == y->evt && x->anc >= 0 && x->anc == y->anc) { + return true; + } + } + } + return false; + } + + static std::array mixPairGeometry(MixPairPhoton const& a, MixPairPhoton const& b) + { + using L = MixPairPhoton::LegMC; + auto mid = [](L const& p, L const& e) { return std::array{0.5f * (p.vx + e.vx), 0.5f * (p.vy + e.vy), 0.5f * (p.vz + e.vz)}; }; + const auto ca = mid(a.posMC, a.eleMC), cb = mid(b.posMC, b.eleMC); + auto legDist = [](L const& x, L const& y) { return std::hypot(RecoDecay::constrainAngle(x.phi - y.phi, -o2::constants::math::PI), x.tgl - y.tgl); }; + return {std::hypot(ca[0] - cb[0], ca[1] - cb[1], ca[2] - cb[2]), std::min(legDist(a.posMC, b.posMC), legDist(a.eleMC, b.eleMC)), + std::max({a.posMC.sharedFrac, a.eleMC.sharedFrac, b.posMC.sharedFrac, b.eleMC.sharedFrac})}; + } + // MC photon pair relationship: 0 unrelated, 1 radiated by the other, 2 siblings (same grandmother), 3 daughter of the other + static int mixRelKind(MixPairPhoton const& a, MixPairPhoton const& b) + { + using L = MixPairPhoton::LegMC; + auto radiatedBy = [](L const& x, L const& y) { return x.evt == y.evt && x.gmid >= 0 && x.gmid == y.pid; }; + auto siblings = [](L const& x, L const& y) { return x.evt == y.evt && x.gmid >= 0 && x.gmid == y.gmid && x.mid != y.mid; }; + auto daughterOf = [](L const& x, L const& y) { return x.evt == y.evt && x.mid >= 0 && x.mid == y.pid; }; + int kind = 0; + for (const auto* x : {&a.posMC, &a.eleMC}) { + for (const auto* y : {&b.posMC, &b.eleMC}) { + if (radiatedBy(*x, *y) || radiatedBy(*y, *x)) { + return 1; + } + if (daughterOf(*x, *y) || daughterOf(*y, *x)) { + kind = 3; + } else if (kind == 0 && siblings(*x, *y)) { + kind = 2; + } + } + } + return kind; + } + struct BremsPointing { + float dMin = 999.f, angle = 3.15f, rEmit = -1.f, outward = 0.f, ptB = 0.f; + }; + template + BremsPointing mixBremsPointing(MixPairPhoton const& a, MixPairPhoton const& b, TLegs const& legs) const + { + constexpr float StepCm = 0.25f, MaxArcCm = 100.f, MarginCm = 2.f; + BremsPointing best; + for (int order = 0; order < 2; ++order) { + const auto& emitter = order == 0 ? a : b; + const auto& brems = order == 0 ? b : a; + const float pB = std::hypot(brems.px, brems.py, brems.pz); + if (pB <= 0.f) { + continue; + } + const std::array u{brems.px / pB, brems.py / pB, brems.pz / pB}; + const float rA = std::hypot(emitter.vx, emitter.vy), rB = std::hypot(brems.vx, brems.vy); + for (const int64_t row : {emitter.posRow, emitter.eleRow}) { + const auto leg = legs.rawIteratorAt(row); + const o2::track::TrackPar par(leg.x(), leg.alpha(), std::array{leg.y(), leg.z(), leg.snp(), leg.tgl(), leg.signed1Pt()}); + o2::math_utils::CircleXYf_t circle; + float sna = 0.f, csa = 0.f; + par.getCircleParams(d_bz, circle, sna, csa); + std::array x0{}, p0{}; + par.getXYZGlo(x0); + par.getPxPyPzGlo(p0); + if (circle.rC <= 0.f) { + continue; + } + const float psi0 = std::atan2(x0[1] - circle.yC, x0[0] - circle.xC); + const float sense = ((x0[0] - circle.xC) * p0[1] - (x0[1] - circle.yC) * p0[0]) > 0.f ? 1.f : -1.f; // direction of motion on the circle + const float tgl = par.getTgl(), norm = std::sqrt(1.f + tgl * tgl); + constexpr int NSteps = static_cast((MaxArcCm + MaxArcCm) / StepCm); + for (int iStep = 0; iStep <= NSteps; ++iStep) { + const float s = -MaxArcCm + static_cast(iStep) * StepCm; + const float psi = psi0 + sense * s / circle.rC; + const std::array h{circle.xC + circle.rC * std::cos(psi), circle.yC + circle.rC * std::sin(psi), x0[2] + tgl * s}; + const float rH = std::hypot(h[0], h[1]); + if (rH < rA - MarginCm || rH > rB + MarginCm) { + continue; // emission between the two conversions + } + const std::array d{h[0] - brems.vx, h[1] - brems.vy, h[2] - brems.vz}; + const float t = d[0] * u[0] + d[1] * u[1] + d[2] * u[2]; + if (t > 0.f) { + continue; // in front of B's conversion point + } + const float dist = std::hypot(d[0] - t * u[0], d[1] - t * u[1], d[2] - t * u[2]); + if (dist < best.dMin) { + const std::array tan{-sense * std::sin(psi) / norm, sense * std::cos(psi) / norm, tgl / norm}; + best.dMin = dist; + best.angle = std::acos(std::clamp(tan[0] * u[0] + tan[1] * u[1] + tan[2] * u[2], -1.f, 1.f)); + best.rEmit = rH; + best.outward = rB > rA ? 1.f : 0.f; + best.ptB = std::hypot(brems.px, brems.py); + } + } + } + } + return best; + } + // skip pairs of photons that come from the same source event (MC truth) when mixing, if requested + bool skipSameSourcePair(const float type, MixPairPhoton const& a, MixPairPhoton const& b) + { + if (type < 0.5f) { + return false; + } + const int64_t e = a.posMC.evt; + const bool same = e >= 0 && a.eleMC.evt == e && b.posMC.evt == e && b.eleMC.evt == e; + return same && mixed.skipSameSourcePairs.value; + } + void fillSameSourcePairs(const float type, std::vector const& photons) + { + if (type < 0.5f) { + return; + } + for (size_t i = 0; i < photons.size(); ++i) { + for (size_t j = i + 1; j < photons.size(); ++j) { + const auto& a = photons[i]; + const auto& b = photons[j]; + const int64_t e = a.posMC.evt; + if (e >= 0 && a.eleMC.evt == e && b.posMC.evt == e && b.eleMC.evt == e) { + registry.fill(HIST("Mixed/Pairs/hSameSource"), qInv(a.px, a.py, a.pz, b.px, b.py, b.pz), mixPairClass(a, b)); + } + } + } + } + void fillMixFakeClasses(const float type, const float stage, const int64_t colId, std::vector const& photons, + std::set> const& candLegs) + { + for (size_t i = 0; i < photons.size(); ++i) { + for (size_t j = i + 1; j < photons.size(); ++j) { + const auto& a = photons[i]; + const auto& b = photons[j]; + if (stage > 0.5f && skipSameSourcePair(type, a, b)) { + continue; + } + const float q = qInv(a.px, a.py, a.pz, b.px, b.py, b.pz); + if (q < 0.5f && mixPairGeometry(a, b)[2] < 0.4f) { // o2-linter: disable=magic-number (normalisation range, analysis leg cut) + registry.fill(HIST("Mixed/Pairs/hQClean"), type, stage, q); + } + if (q >= 0.2f) { // o2-linter: disable=magic-number (diagnostic range) + continue; + } + const auto nIts = static_cast(a.posMC.its + a.eleMC.its + b.posMC.its + b.eleMC.its); + const float amb = (candLegs.contains({colId, a.posRow, b.eleRow}) || candLegs.contains({colId, b.posRow, a.eleRow})) ? 1.f : 0.f; + const float cls = mixPairClass(a, b); + registry.fill(HIST("Mixed/Pairs/hFakeClass"), type, stage, 0.f, q, nIts, amb); + registry.fill(HIST("Mixed/Pairs/hFakeClass"), type, stage, cls, q, nIts, amb); + { + const float rA = std::hypot(a.vx, a.vy), rB = std::hypot(b.vx, b.vy); + const bool aSofter = std::hypot(a.px, a.py, a.pz) < std::hypot(b.px, b.py, b.pz); + const float rSoft = aSofter ? rA : rB, rHard = aSofter ? rB : rA; + registry.fill(HIST("Mixed/Pairs/hFakeClassOrder"), type, stage, cls, q, rSoft < rHard ? 0.f : 1.f, std::fabs(rA - rB)); + } + if (q < 0.1f) { // o2-linter: disable=magic-number (diagnostic range) + const auto g = mixPairGeometry(a, b); + registry.fill(HIST("Mixed/Pairs/hFakeClassGeo"), type, stage, cls, q, amb, mixSameAncestor(a, b) ? 1.f : 0.f, g[0], + std::log10(std::max(g[1], 1e-6f)), g[2]); // o2-linter: disable=magic-number (log floor) + if (stage > 0.5f) { + registry.fill(HIST("Mixed/Pairs/hPairOrigin"), type, cls, q, static_cast(mixPairOrigin(a, b))); + } + if (stage > 0.5f && a.group[0] >= 0 && b.group[0] >= 0) { + // same group: its context; different groups: the larger one + const bool sameGroup = a.group[0] == b.group[0]; + const auto& gr = (sameGroup || a.group[1] >= b.group[1]) ? a.group : b.group; + registry.fill(HIST("Mixed/Pairs/hGroupCtx"), type, cls, q, amb, sameGroup ? 1.f : 0.f, static_cast(std::min(gr[1], 20)), + static_cast(std::min(gr[2], 33)), static_cast(std::min(gr[3], 10))); // o2-linter: disable=magic-number (last bins) + } + } + } + } + } + // MC class of a photon pair (see fillMixFakeClasses) + static float mixPairClass(MixPairPhoton const& a, MixPairPhoton const& b) + { + const int truth = mixPairTruth(a, b); + if (truth == 3) { + return static_cast(mixFakeClass(a, b)); + } + if (truth == 1 || truth == 2) { + return (truth == 1 ? 13.f : 14.f) + (mixRelated(a, b) ? 3.f : 0.f); + } + return 15.f; + } + + template + void fillMixSwapBrems(const float type, const float stage, const int64_t colId, std::vector const& photons, + std::map, float> const& candScore, TLegs const& legs) + { + constexpr float MaxQ = 0.1f, MaxShared = 0.4f; + auto score = [&](int64_t pos, int64_t ele) { + const auto it = candScore.find({colId, pos, ele}); + return it == candScore.end() ? -1.f : it->second; + }; + for (size_t i = 0; i < photons.size(); ++i) { + for (size_t j = i + 1; j < photons.size(); ++j) { + const auto& a = photons[i]; + const auto& b = photons[j]; + if (stage > 0.5f && skipSameSourcePair(type, a, b)) { + continue; + } + const float q = qInv(a.px, a.py, a.pz, b.px, b.py, b.pz); + if (q >= MaxQ) { + continue; + } + const float cls = mixPairClass(a, b); + const float clean = mixPairGeometry(a, b)[2] < MaxShared ? 1.f : 0.f; + const auto bp = mixBremsPointing(a, b, legs); + registry.fill(HIST("Mixed/Pairs/hBrems"), type, stage, cls, static_cast(mixRelKind(a, b)), q, + std::log10(std::max(bp.dMin, 1e-3f)), std::log10(std::max(bp.angle, 1e-4f)), bp.rEmit, bp.outward, bp.ptB, clean); // o2-linter: disable=magic-number (log floors) + if (stage < 0.5f || (cls != 14.f && cls != 17.f)) { // o2-linter: disable=magic-number (swap classes) + continue; + } + const float sa = score(a.posRow, a.eleRow), sb = score(b.posRow, b.eleRow); + const float t1 = score(a.posRow, b.eleRow), t2 = score(b.posRow, a.eleRow); + float decision = 2.f; + if (t1 >= 0.f && t2 >= 0.f) { + decision = (sa + sb < t1 + t2) ? 1.f : 0.f; + } + const float origin = (a.original && b.original) ? 1.f : ((!a.original && !b.original) ? 0.f : 2.f); + registry.fill(HIST("Mixed/Pairs/hSwapDecision"), type, origin, decision, q, clean); + } + } + } + // truth of a photon pair: 1 two true photons, 2 the legs of two true photons crossed (full swap), 3 other with a fake, 4 no label + static int mixPairTruth(MixPairPhoton const& a, MixPairPhoton const& b) + { + if (a.posKey.second < 0 || a.eleKey.second < 0 || b.posKey.second < 0 || b.eleKey.second < 0) { + return 4; + } + const bool allTrue = a.posTrue && a.eleTrue && b.posTrue && b.eleTrue; + if (allTrue && a.posKey == a.eleKey && b.posKey == b.eleKey && a.posKey != b.posKey) { + return 1; + } + if (allTrue && a.posKey == b.eleKey && a.eleKey == b.posKey && a.posKey != a.eleKey) { + return 2; + } + return 3; + } + template + void fillMixPairs(const float type, std::vector const& photons) + { + for (size_t i = 0; i < photons.size(); ++i) { + for (size_t j = i + 1; j < photons.size(); ++j) { + const auto& a = photons[i]; + const auto& b = photons[j]; + if (after && skipSameSourcePair(type, a, b)) { + continue; + } + const float q = qInv(a.px, a.py, a.pz, b.px, b.py, b.pz); + const auto truth = static_cast(mixPairTruth(a, b)); + if constexpr (after) { + registry.fill(HIST("Mixed/Pairs/hQAfter"), type, 0.f, q); + registry.fill(HIST("Mixed/Pairs/hQAfter"), type, truth, q); + } else { + registry.fill(HIST("Mixed/Pairs/hQBefore"), type, 0.f, q); + registry.fill(HIST("Mixed/Pairs/hQBefore"), type, truth, q); + } + } + } + } + + // fill the occupancy of the source events and the deduplication diagnostics for the mixed-event photon pairs + template + void fillMatchedConfigs(TMixCollisions const& collisions, TMixLegs const& legs) + { + for (const auto& collision : collisions) { // same-event artificial events carry the occupancy of their source event + if (!collision.isMixed()) { + mOccOfSourceEvent[collision.sourceEventUid()] = collision.occupancy(); + } + } + using Key3 = std::tuple; + std::set cands, stored; + for (const auto& c : vecV0Dedup) { + cands.insert(Key3{c.colID, c.posID, c.eleID}); + } + std::unordered_map storedPartner; + for (const auto& k : stored_fullv0Ids) { + stored.insert(Key3{k.collisionId, k.posId, k.eleId}); + storedPartner.try_emplace(k.posId, k.eleId); + storedPartner.try_emplace(k.eleId, k.posId); + } + for (const auto& collision : collisions) { + const int64_t colId = collision.globalIndex(); + const float type = collision.isMixed() ? 1.f : 0.f; + // MC photons with both legs present: (source event, mother) -> (pos row, ele row) + std::map, std::array> photonLegs; + for (int i = collision.firstLeg(); i < collision.firstLeg() + collision.nLegs(); ++i) { + const auto& l = mLegIt[i]; + if (l.mcMotherId() < 0 || l.mcMotherPdg() != kGamma) { + continue; + } + auto [it, inserted] = photonLegs.try_emplace(std::make_pair(static_cast(l.sourceEventUid()), l.mcMotherId()), std::array{-1, -1}); + it->second[l.sign() > 0 ? 0 : 1] = i; + } + std::vector> photons; + for (const auto& [k, rows] : photonLegs) { + if (rows[0] >= 0 && rows[1] >= 0) { + photons.push_back(rows); + } + } + for (size_t i = 0; i < photons.size(); ++i) { + for (size_t j = i + 1; j < photons.size(); ++j) { + const auto& ap = mLegIt[photons[i][0]]; + const auto& am = mLegIt[photons[i][1]]; + const auto& bp = mLegIt[photons[j][0]]; + const auto& bm = mLegIt[photons[j][1]]; + // related: same mother (siblings) or one photon radiated by a leg particle of the other (bremsstrahlung), as mixRelated + const bool sameEvt = ap.sourceEventUid() == bp.sourceEventUid(); + if (type > 0.5f && sameEvt && mixed.skipSameSourceCrossed) { + continue; // injection: a configuration of two current-event photons is a same-event one + } + const float qTrue = qInv(ap.mcMotherPx(), ap.mcMotherPy(), ap.mcMotherPz(), bp.mcMotherPx(), bp.mcMotherPy(), bp.mcMotherPz()); + if (qTrue >= 0.05f) { // o2-linter: disable=magic-number (diagnostic range) + continue; + } + const int gA = ap.mcGrandMotherId(), gB = bp.mcGrandMotherId(); + const bool related = sameEvt && ((gA >= 0 && gA == gB) || (gA >= 0 && (gA == bp.mcParticleId() || gA == bm.mcParticleId())) || + (gB >= 0 && (gB == ap.mcParticleId() || gB == am.mcParticleId()))); + const int64_t A = photons[i][0], a = photons[i][1], B = photons[j][0], b = photons[j][1]; // pos / ele rows + float input = 2.f; + if (ap.sourcePhotonUid() == am.sourcePhotonUid() && bp.sourcePhotonUid() == bm.sourcePhotonUid() && ap.sourcePhotonUid() != bp.sourcePhotonUid()) { + input = 0.f; + } else if (ap.sourcePhotonUid() == bm.sourcePhotonUid() && bp.sourcePhotonUid() == am.sourcePhotonUid() && ap.sourcePhotonUid() != bp.sourcePhotonUid()) { + input = 1.f; + } + const int nGen = static_cast(cands.contains(Key3{colId, A, a})) + static_cast(cands.contains(Key3{colId, B, b})); + const int nCross = static_cast(cands.contains(Key3{colId, A, b})) + static_cast(cands.contains(Key3{colId, B, a})); + const bool sGen = stored.contains(Key3{colId, A, a}) && stored.contains(Key3{colId, B, b}); + const bool sCross = stored.contains(Key3{colId, A, b}) && stored.contains(Key3{colId, B, a}); + float dOcc = 0.f; // ME: occupancy of the photon's source event - current event, the photon with the larger |difference| + if (type > 0.5f) { + float best = 0.f; + for (const int64_t uid : {static_cast(ap.sourceEventUid()), static_cast(bp.sourceEventUid())}) { + const auto it = mOccOfSourceEvent.find(uid); + const float d = it == mOccOfSourceEvent.end() ? -19999.f : it->second - collision.occupancy(); + if (std::fabs(d) > std::fabs(best)) { + best = d; + } + } + dOcc = best; + } + const int nTpcOnly = static_cast(!ap.hasITS()) + static_cast(!am.hasITS()) + static_cast(!bp.hasITS()) + static_cast(!bm.hasITS()); + registry.fill(HIST("Mixed/Decision/hMatched"), type, qTrue, input, static_cast(nGen), static_cast(nCross), sGen ? 0.f : (sCross ? 1.f : 2.f), related ? 1.f : 0.f, static_cast(collision.numContrib()), static_cast(collision.nLegs()) / 2.f, collision.occupancy(), std::clamp(dOcc, -19999.f, 19999.f), static_cast(nTpcOnly)); + // central electrode: true conversion point (ME partners shifted by the alignment, < 2 cm) and the leg's tan lambda (unchanged by it) + constexpr float kTpcRin = 85.f, kTpcRout = 247.f; // o2-linter: disable=magic-number (TPC drift volume, diagnostic) + auto crossesCE = [&](auto const& l) { + const float rConv = std::hypot(l.mcVx(), l.mcVy()); + if (rConv > kTpcRout) { + return 0; + } + const float z1 = l.mcVz() + l.tgl() * (std::max(rConv, kTpcRin) - rConv), z2 = l.mcVz() + l.tgl() * (kTpcRout - rConv); + return z1 * z2 <= 0.f ? 1 : 0; + }; + auto absEta = [](float px, float py, float pz) { return std::fabs(std::asinh(pz / std::max(std::hypot(px, py), 1.e-6f))); }; + const float minEta = std::min(absEta(ap.mcMotherPx(), ap.mcMotherPy(), ap.mcMotherPz()), absEta(bp.mcMotherPx(), bp.mcMotherPy(), bp.mcMotherPz())); + const float minZ = std::min(std::fabs(ap.mcVz()), std::fabs(bp.mcVz())); + const int nCE = crossesCE(ap) + crossesCE(am) + crossesCE(bp) + crossesCE(bm); + registry.fill(HIST("Mixed/Decision/hMatchedCE"), type, qTrue, input, sGen ? 0.f : (sCross ? 1.f : 2.f), related ? 1.f : 0.f, std::min(minEta, 1.99f), std::min(minZ, 299.f), static_cast(nCE), static_cast(nTpcOnly)); + // leg fates: 0 not stored, 1 own genuine partner, 2 crossed partner, 3 leg of a third complete photon, 4 leg of an incomplete MC photon, 5 no photon mother + std::array nFate{}; + const std::array rows{A, a, B, b}; + const std::array own{a, A, b, B}, crossed{b, B, a, A}; + for (int k = 0; k < 4; ++k) { + const auto it = storedPartner.find(rows[k]); + if (it == storedPartner.end()) { + ++nFate[0]; + } else if (it->second == own[k]) { + ++nFate[1]; + } else if (it->second == crossed[k]) { + ++nFate[2]; + } else { + const auto p = legs.rawIteratorAt(it->second); + if (p.mcMotherId() < 0 || p.mcMotherPdg() != kGamma) { + ++nFate[5]; + } else { + const auto pIt = photonLegs.find(std::make_pair(static_cast(p.sourceEventUid()), p.mcMotherId())); + const bool complete = pIt != photonLegs.end() && pIt->second[0] >= 0 && pIt->second[1] >= 0; + ++nFate[complete ? 3 : 4]; + } + } + } + registry.fill(HIST("Mixed/Decision/hMatchedLegs"), type, qTrue, input, sGen ? 0.f : (sCross ? 1.f : 2.f), related ? 1.f : 0.f, + static_cast(nFate[0]), static_cast(nFate[1]), static_cast(nFate[2]), static_cast(nFate[3]), + static_cast(nFate[4]), static_cast(nFate[5])); + } + } + } + } + + // truth of a photon pair: 0 both genuine, 1 crossed, 2 other (at least one leg not from a true photon), for all candidates of a collision + template + void fillFourLegAll(TMixCollisions const& collisions, TMixLegs const& legs, const size_t nFit) + { + using Key3 = std::tuple; + std::map firstCand; + std::map> originalsOfColl; + for (size_t i = 0; i < vecV0Dedup.size(); ++i) { + const auto& c = vecV0Dedup[i]; + if (firstCand.emplace(Key3{c.colID, c.posID, c.eleID}, static_cast(i)).second && c.v0ID >= 0 && i < nFit) { + originalsOfColl[c.colID].push_back(static_cast(i)); + } + } + std::set storedSet; + for (const auto& k : stored_fullv0Ids) { + storedSet.insert(Key3{k.collisionId, k.posId, k.eleId}); + } + auto truePhoton = [&](int64_t posRow, int64_t eleRow) { + const auto p = legs.rawIteratorAt(posRow); + const auto e = legs.rawIteratorAt(eleRow); + return p.mcMotherId() >= 0 && p.sourceEventUid() == e.sourceEventUid() && p.mcMotherId() == e.mcMotherId() && p.mcMotherPdg() == kGamma; + }; + for (const auto& [colId, orig] : originalsOfColl) { + const float type = collisions.rawIteratorAt(colId).isMixed() ? 1.f : 0.f; + for (size_t i = 0; i < orig.size(); ++i) { + for (size_t j = i + 1; j < orig.size(); ++j) { + const auto& a = vecV0Dedup[orig[i]]; + const auto& b = vecV0Dedup[orig[j]]; + const auto itX1 = firstCand.find(Key3{colId, a.posID, b.eleID}); + const auto itX2 = firstCand.find(Key3{colId, b.posID, a.eleID}); + if (itX1 == firstCand.end() || itX2 == firstCand.end()) { + continue; + } + const auto& x1 = vecV0Dedup[itX1->second]; + const auto& x2 = vecV0Dedup[itX2->second]; + const auto ap = legs.rawIteratorAt(a.posID); + const auto bp = legs.rawIteratorAt(b.posID); + const bool distinctMothers = ap.sourceEventUid() != bp.sourceEventUid() || ap.mcMotherId() != bp.mcMotherId(); + const bool physO = truePhoton(a.posID, a.eleID) && truePhoton(b.posID, b.eleID) && distinctMothers; + const bool physX = truePhoton(a.posID, b.eleID) && truePhoton(b.posID, a.eleID) && distinctMothers; + const float truth = physO ? 0.f : (physX ? 1.f : 2.f); + const float qGen = physX ? qInv(x1.pxAtPV, x1.pyAtPV, x1.pzAtPV, x2.pxAtPV, x2.pyAtPV, x2.pzAtPV) : qInv(a.pxAtPV, a.pyAtPV, a.pzAtPV, b.pxAtPV, b.pyAtPV, b.pzAtPV); + if (qGen >= 0.05f) { // o2-linter: disable=magic-number (diagnostic range) + continue; + } + const bool storedO = storedSet.contains(Key3{colId, a.posID, a.eleID}) && storedSet.contains(Key3{colId, b.posID, b.eleID}); + const bool storedX = storedSet.contains(Key3{colId, a.posID, b.eleID}) && storedSet.contains(Key3{colId, b.posID, a.eleID}); + const float actual = storedO ? 0.f : (storedX ? 1.f : 2.f); + const int crossedOrigin = (static_cast(itX1->second) >= nFit ? 1 : 0) + (static_cast(itX2->second) >= nFit ? 1 : 0); + const int origOrigin = (ap.sourceV0Id() < 0 ? 1 : 0) + (bp.sourceV0Id() < 0 ? 1 : 0); + registry.fill(HIST("Mixed/Decision/hFourLegAll"), type, truth, actual, qGen, static_cast(crossedOrigin), static_cast(origOrigin)); + } + } + } + } + + template + void fillFourLegTruthAligned(TMixCollisions const& collisions, TMixLegs const& legs, const int64_t colId, const float type, const float truth, + const float isolated, const float actual, V0CandidateHelper const& a, V0CandidateHelper const& b, + V0CandidateHelper const& x1, V0CandidateHelper const& x2, const int groupSize) + { + const bool origTrue = truth < 0.5f; + const auto& t1 = origTrue ? a : x1; + const auto& t2 = origTrue ? b : x2; + const auto& w1 = origTrue ? x1 : a; + const auto& w2 = origTrue ? x2 : b; + // decision in truth terms: 0 genuine assignment, 1 wrong assignment, 2 other + auto inTruth = [&](float decision) { return decision > 1.5f ? 2.f : ((decision < 0.5f) == origTrue ? 0.f : 1.f); }; + const float choice = inTruth(isolated); + MixPairPhoton p1, p2; + setMixPairLegs(p1, legs.rawIteratorAt(t1.posID), legs.rawIteratorAt(t1.eleID)); + setMixPairLegs(p2, legs.rawIteratorAt(t2.posID), legs.rawIteratorAt(t2.eleID)); + const int relKind = mixRelKind(p1, p2); + const auto rel = static_cast(relKind); + const float rel2 = relKind > 0 ? 1.f : 0.f; + const float s0 = dedup.groupMatchingScoreThreshold.value; + const float qTrue = qInv(t1.pxAtPV, t1.pyAtPV, t1.pzAtPV, t2.pxAtPV, t2.pyAtPV, t2.pzAtPV); + const int nClipT = (t1.score >= s0 ? 1 : 0) + (t2.score >= s0 ? 1 : 0); + const int nClipW = (w1.score >= s0 ? 1 : 0) + (w2.score >= s0 ? 1 : 0); + auto angle = [](float cpa) { return std::acos(std::clamp(cpa, -1.f, 1.f)); }; + const float dTheta = 1e3f * ((angle(w1.cosPA) + angle(w2.cosPA)) - (angle(t1.cosPA) + angle(t2.cosPA))); // mrad + const float dPCA = (w1.pca + w2.pca) - (t1.pca + t2.pca); + const std::array rows{t1.posID, t1.eleID, t2.posID, t2.eleID}; + float fshMax = 0.f; + int nITS = 0; + for (const auto& r : rows) { + const auto l = legs.rawIteratorAt(r); + fshMax = std::max(fshMax, static_cast(l.tpcFractionSharedCls())); + nITS += l.hasITS() ? 1 : 0; + } + registry.fill(HIST("Mixed/Decision/hFourLegTruth"), type, truth, choice, rel, qTrue, static_cast(3 * nClipT + nClipW), dTheta, dPCA, fshMax); + { + const int origin = mixCommonOrigin(p1.posMC, p2.posMC); + auto rootClass = [](int pdg) { + switch (std::abs(pdg)) { + case 111: + return 1.f; // o2-linter: disable=pdg/explicit-code (diagnostic classes) + case 221: + return 2.f; // o2-linter: disable=pdg/explicit-code + case 223: + return 3.f; // o2-linter: disable=pdg/explicit-code + case 331: + return 4.f; // o2-linter: disable=pdg/explicit-code + case 113: + case 213: + case 313: + case 323: + return 5.f; // o2-linter: disable=pdg/explicit-code + case 310: + case 130: + case 311: + return 6.f; // o2-linter: disable=pdg/explicit-code + default: + return std::abs(pdg) > 1000 ? 7.f : 0.f; // o2-linter: disable=magic-number (baryons) + } + }; + registry.fill(HIST("Mixed/Decision/hFourLegOrigin"), type, truth, choice, rel, qTrue, static_cast(origin), origin > 0 ? rootClass(p1.posMC.rootPdg) : 0.f); + } + + // TPC leg proximity: the minimum distance of the two legs of one photon to the two legs of the other photon, at 17 radii in the TPC + constexpr int NRadii = 17; + auto positions = [&](int64_t row) { + std::array, NRadii> xyz{}; + std::array ok{}; + const auto tp0 = getTrackPar(legs.rawIteratorAt(row)); + for (int k = 0; k < NRadii; ++k) { + auto tp = tp0; + float x = 0.f; + ok[k] = tp.getXatLabR(85.f + 10.f * k, x, d_bz) && tp.propagateParamTo(x, d_bz); // o2-linter: disable=magic-number (TPC radii) + if (ok[k]) { + tp.getXYZGlo(xyz[k]); + } + } + return std::make_pair(xyz, ok); + }; + const auto pT1p = positions(t1.posID), pT1e = positions(t1.eleID), pT2p = positions(t2.posID), pT2e = positions(t2.eleID); + auto minDist = [&](auto const& u, auto const& v) { + float d = 999.f; + for (int k = 0; k < NRadii; ++k) { + if (u.second[k] && v.second[k]) { + d = std::min(d, std::hypot(u.first[k][0] - v.first[k][0], u.first[k][1] - v.first[k][1], u.first[k][2] - v.first[k][2])); + } + } + return d; + }; + const float dOS = std::min(minDist(pT1p, pT2e), minDist(pT2p, pT1e)); // legs that the wrong assignment pairs + const float dSS = std::min(minDist(pT1p, pT2p), minDist(pT1e, pT2e)); // control: same-sign legs + const float occ = collisions.rawIteratorAt(colId).occupancy(); + registry.fill(HIST("Mixed/Decision/hFourLegTrack"), type, truth, choice, rel2, std::log10(std::max(dOS, 1e-3f)), std::log10(std::max(dSS, 1e-3f)), + static_cast(nITS), occ); // o2-linter: disable=magic-number (log floor) + + auto mother = [&](int64_t row) { + const auto l = legs.rawIteratorAt(row); + return std::array{l.mcMotherPx(), l.mcMotherPy(), l.mcMotherPz()}; + }; + const auto m1 = mother(t1.posID), m2 = mother(t2.posID); + auto phi = [](float px, float py) { return std::atan2(py, px); }; + auto eta = [](float px, float py, float pz) { return std::asinh(pz / std::max(std::hypot(px, py), 1e-6f)); }; + const float uPhi = RecoDecay::constrainAngle(phi(m2[0], m2[1]) - phi(m1[0], m1[1]), -o2::constants::math::PI) > 0.f ? 1.f : -1.f; + const float uEta = eta(m2[0], m2[1], m2[2]) - eta(m1[0], m1[1], m1[2]) > 0.f ? 1.f : -1.f; + const float rPhi1 = uPhi * RecoDecay::constrainAngle(phi(t1.pxAtPV, t1.pyAtPV) - phi(m1[0], m1[1]), -o2::constants::math::PI); + const float rPhi2 = -uPhi * RecoDecay::constrainAngle(phi(t2.pxAtPV, t2.pyAtPV) - phi(m2[0], m2[1]), -o2::constants::math::PI); + const float rEta1 = uEta * (eta(t1.pxAtPV, t1.pyAtPV, t1.pzAtPV) - eta(m1[0], m1[1], m1[2])); + const float rEta2 = -uEta * (eta(t2.pxAtPV, t2.pyAtPV, t2.pzAtPV) - eta(m2[0], m2[1], m2[2])); + registry.fill(HIST("Mixed/Decision/hFourLegResid"), type, truth, choice, rel2, rPhi1, rPhi2, rEta1, rEta2); + + const std::vector legMask{0b0011, 0b1100, 0b1001, 0b0110}; + auto decide = [&](std::vector const& sc, float thr) { + const auto sol = solveConflictGroup(legMask, sc, thr, false); + return inTruth(sol.bestMask == 0b0011 ? 0.f : (sol.bestMask == 0b1100 ? 1.f : 2.f)); // o2-linter: disable=magic-number (subset masks) + }; + auto scores = [&](float w) { + return std::vector{getScoreV0(a.cosPA, a.pca, w), getScoreV0(b.cosPA, b.pca, w), getScoreV0(x1.cosPA, x1.pca, w), getScoreV0(x2.cosPA, x2.pca, w)}; + }; + const std::vector baseScores{a.score, b.score, x1.score, x2.score}; + registry.fill(HIST("Mixed/Decision/hFourLegAlt"), type, truth, rel2, choice, decide(scores(1.f), s0), decide(scores(0.f), s0), decide(baseScores, -1.f), + decide(scores(dedup.deduplicationScoreWeight.value), s0)); + + const auto col = collisions.rawIteratorAt(colId); + auto pointing = [&](V0CandidateHelper const& c) { + const float dx = c.svX - col.posX(), dy = c.svY - col.posY(), dz = c.svZ - col.posZ(); + const float dPhi = RecoDecay::constrainAngle(std::atan2(c.pyAtSV, c.pxAtSV) - std::atan2(dy, dx), -o2::constants::math::PI); + const float dLam = std::atan2(c.pzAtSV, std::hypot(c.pxAtSV, c.pyAtSV)) - std::atan2(dz, std::hypot(dx, dy)); + return std::make_pair(1e3f * dPhi, 1e3f * dLam); // mrad + }; + const auto pt1 = pointing(t1), pt2 = pointing(t2), pw1 = pointing(w1), pw2 = pointing(w2); + const float dPhiSum = (std::fabs(pw1.first) + std::fabs(pw2.first)) - (std::fabs(pt1.first) + std::fabs(pt2.first)); + const float dLamSum = (std::fabs(pw1.second) + std::fabs(pw2.second)) - (std::fabs(pt1.second) + std::fabs(pt2.second)); + registry.fill(HIST("Mixed/Decision/hFourLegPoint"), type, truth, choice, rel2, qTrue, dPhiSum, dLamSum, pt1.second, pt2.second, pt1.first, pt2.first); + + // log dump of the few cases where the isolated decision is wrong, but the actual decision is correct and genuine + if (type < 0.5f && origTrue && isolated > 0.5f && isolated < 1.5f && actual < 0.5f && mNAnomalyDump < 200) { // o2-linter: disable=magic-number (cap) + ++mNAnomalyDump; + LOGF(info, "[4leg-anom] col %ld group %d scores A %.4f B %.4f X1 %.4f X2 %.4f s0 %.2f q %.4f rel %d", colId, groupSize, a.score, b.score, x1.score, x2.score, s0, qTrue, relKind); + } + } + + template + void fillFourLegDecisions(TMixCollisions const& collisions, TMixLegs const& legs, const size_t nFit) + { + fillFourLegAll(collisions, legs, nFit); + using Key3 = std::tuple; + std::map firstCand; // first fit of a leg pair = its dedup candidate + std::map> originalsOfColl; + for (size_t i = 0; i < nFit; ++i) { + const auto& c = vecV0Dedup[i]; + if (firstCand.emplace(Key3{c.colID, c.posID, c.eleID}, static_cast(i)).second && c.v0ID >= 0) { + originalsOfColl[c.colID].push_back(static_cast(i)); + } + } + std::set storedSet; + for (const auto& k : stored_fullv0Ids) { + storedSet.insert(Key3{k.collisionId, k.posId, k.eleId}); + } + auto truePhoton = [&](int64_t posRow, int64_t eleRow) { + const auto p = legs.rawIteratorAt(posRow); + const auto e = legs.rawIteratorAt(eleRow); + return p.mcMotherId() >= 0 && p.sourceEventUid() == e.sourceEventUid() && p.mcMotherId() == e.mcMotherId() && p.mcMotherPdg() == kGamma; + }; + const float s0 = dedup.groupMatchingScoreThreshold.value; + for (const auto& [colId, orig] : originalsOfColl) { + const float type = collisions.rawIteratorAt(colId).isMixed() ? 1.f : 0.f; + for (size_t i = 0; i < orig.size(); ++i) { + for (size_t j = i + 1; j < orig.size(); ++j) { + const auto& a = vecV0Dedup[orig[i]]; + const auto& b = vecV0Dedup[orig[j]]; + const auto itX1 = firstCand.find(Key3{colId, a.posID, b.eleID}); + const auto itX2 = firstCand.find(Key3{colId, b.posID, a.eleID}); + if (itX1 == firstCand.end() || itX2 == firstCand.end()) { + continue; // not both assignments available + } + const auto& x1 = vecV0Dedup[itX1->second]; + const auto& x2 = vecV0Dedup[itX2->second]; + const float qO = qInv(a.pxAtPV, a.pyAtPV, a.pzAtPV, b.pxAtPV, b.pyAtPV, b.pzAtPV); + const float qX = qInv(x1.pxAtPV, x1.pyAtPV, x1.pzAtPV, x2.pxAtPV, x2.pyAtPV, x2.pzAtPV); + if (std::min(qO, qX) >= 0.1f) { // o2-linter: disable=magic-number (diagnostic range) + continue; + } + // the isolated decision is the dedup objective on only these four candidates, the actual decision is the stored set after the dedup and the resolvers + const std::vector legMask{0b0011, 0b1100, 0b1001, 0b0110}; + const std::vector scores{a.score, b.score, x1.score, x2.score}; + const auto sol = solveConflictGroup(legMask, scores, s0, false); + const float isolated = sol.bestMask == 0b0011 ? 0.f : (sol.bestMask == 0b1100 ? 1.f : 2.f); // o2-linter: disable=magic-number (subset masks) + const bool storedO = storedSet.contains(Key3{colId, a.posID, a.eleID}) && storedSet.contains(Key3{colId, b.posID, b.eleID}); + const bool storedX = storedSet.contains(Key3{colId, a.posID, b.eleID}) && storedSet.contains(Key3{colId, b.posID, a.eleID}); + const float actual = storedO ? 0.f : (storedX ? 1.f : 2.f); + const auto ap = legs.rawIteratorAt(a.posID); + const auto bp = legs.rawIteratorAt(b.posID); + const bool distinctMothers = ap.sourceEventUid() != bp.sourceEventUid() || ap.mcMotherId() != bp.mcMotherId(); + const bool physO = truePhoton(a.posID, a.eleID) && truePhoton(b.posID, b.eleID) && distinctMothers; + const bool physX = truePhoton(a.posID, b.eleID) && truePhoton(b.posID, a.eleID) && distinctMothers; + const float truth = physO ? 0.f : (physX ? 1.f : 2.f); + const int groupSize = std::max({mGroupSizeOfCand[orig[i]], mGroupSizeOfCand[orig[j]], mGroupSizeOfCand[itX1->second], mGroupSizeOfCand[itX2->second]}); + // originals without an SVertexer V0 are fitted without the AP cut, the crossed pairings with it (asymmetric options) + const bool relaxedOriginal = ap.sourceV0Id() < 0 || bp.sourceV0Id() < 0; + registry.fill(HIST("Mixed/Decision/hFourLeg"), type, truth, isolated, actual, qO, qX, static_cast(std::min(groupSize, 15)), // o2-linter: disable=magic-number (last bin) + (x1.score + x2.score) - (a.score + b.score), relaxedOriginal ? 1.f : 0.f); + if (std::min(qO, qX) < 0.05f) { // o2-linter: disable=magic-number (diagnostic range) + // what tips the 4-leg decision: score components, geometry and kinematics of the two conversions, MC relation + auto angle = [](float cpa) { return std::acos(std::clamp(cpa, -1.f, 1.f)); }; + const float dPCA = (x1.pca + x2.pca) - (a.pca + b.pca); + const float dAngle = (angle(x1.cosPA) + angle(x2.cosPA)) - (angle(a.cosPA) + angle(b.cosPA)); + const float dR = std::fabs(std::hypot(a.svX, a.svY) - std::hypot(b.svX, b.svY)); + const float dZ = std::fabs(a.svZ - b.svZ); + auto pLeg = [&](int64_t row) { + const auto l = legs.rawIteratorAt(row); + return std::fabs(1.f / l.signed1Pt()) * std::sqrt(1.f + l.tgl() * l.tgl()); + }; + auto asym = [](float p1, float p2) { return std::fabs(p1 - p2) / std::max(p1 + p2, 1e-6f); }; + const float sameSignSim = std::min(asym(pLeg(a.posID), pLeg(b.posID)), asym(pLeg(a.eleID), pLeg(b.eleID))); + MixPairPhoton pa, pb; + setMixPairLegs(pa, legs.rawIteratorAt(a.posID), legs.rawIteratorAt(a.eleID)); + setMixPairLegs(pb, legs.rawIteratorAt(b.posID), legs.rawIteratorAt(b.eleID)); + registry.fill(HIST("Mixed/Decision/hFourLegKin"), type, truth, isolated, dPCA, dAngle, dR, dZ, sameSignSim, mixRelated(pa, pb) ? 1.f : 0.f); + } + if (truth < 1.5f && std::min(qO, qX) < 0.05f) { // o2-linter: disable=magic-number (genuine assignment exists; diagnostic range) + fillFourLegTruthAligned(collisions, legs, colId, type, truth, isolated, actual, a, b, x1, x2, groupSize); + } + } + } + } + for (const auto& r : mPermuteResults) { + registry.fill(HIST("Mixed/Decision/hPermute"), collisions.rawIteratorAt(r.colID).isMixed() ? 1.f : 0.f, static_cast(std::min(r.nMembers, 30)), // o2-linter: disable=magic-number (last bin) + r.changed ? 1.f : 0.f, r.greedy ? 1.f : 0.f); + } + } + + // run 2: build the mixed event, fill the crossed pairings, and fill the diagnostics of the run-1 photons (pooled free legs) before the dedup and resolvers + template + void buildMixed(TMixCollisions const& collisions, TMixLegs const& legs) + { + const auto tDF = std::chrono::steady_clock::now(); + mRelaxedKeys.clear(); + mLegDcaCache.clear(); + // build the leg iterator cache for the current event + mLegIt.clear(); + mLegIt.reserve(legs.size()); + for (int64_t i = 0; i < static_cast(legs.size()); ++i) { + mLegIt.push_back(legs.rawIteratorAt(i)); + } + std::unordered_map> inputOfColl; // artificial event -> input photons (pair diagnostics) + auto crossedPrefilter = [&](auto const& pos, auto const& ele) { + if (std::fabs(pos.tgl() - ele.tgl()) > recovery.finderMaxDeltaTgl) { + return false; + } + const o2::track::TrackAuxPar cp(getTrackPar(pos), d_bz); + const o2::track::TrackAuxPar ce(getTrackPar(ele), d_bz); + return std::fabs(circleGap(cp, ce)) < recovery.finderMaxCircleGap; + }; + for (const auto& collision : collisions) { + const float type = collision.isMixed() ? 1.f : 0.f; + const auto t0 = std::chrono::steady_clock::now(); + registry.fill(HIST("Mixed/hEvents"), type); + std::vector posLegs, eleLegs; + for (int i = collision.firstLeg(); i < collision.firstLeg() + collision.nLegs(); ++i) { + (mLegIt[i].sign() > 0 ? posLegs : eleLegs).push_back(i); + } + std::unordered_map circleOf; + std::unordered_map eleOfPhoton; + std::unordered_map nEleOfSource; + std::vector> eleByTgl; + eleByTgl.reserve(eleLegs.size()); + for (const auto& ie : eleLegs) { + eleOfPhoton.emplace(mLegIt[ie].sourcePhotonUid(), ie); + nEleOfSource[mLegIt[ie].sourceEventUid()]++; + eleByTgl.emplace_back(mLegIt[ie].tgl(), ie); + } + std::sort(eleByTgl.begin(), eleByTgl.end()); + const bool tglWindow = mixed.prefilterCrossed && !mixed.uniformPairFit; + if (mixed.prefilterCrossed) { + for (const auto& i : posLegs) { + circleOf.emplace(i, o2::track::TrackAuxPar(getTrackPar(mLegIt[i]), d_bz)); + } + for (const auto& i : eleLegs) { + circleOf.emplace(i, o2::track::TrackAuxPar(getTrackPar(mLegIt[i]), d_bz)); + } + } + auto crossedPrefilterCached = [&](int64_t ip, int64_t ie) { + return std::fabs(mLegIt[ip].tgl() - mLegIt[ie].tgl()) <= recovery.finderMaxDeltaTgl && std::fabs(circleGap(circleOf.at(ip), circleOf.at(ie))) < recovery.finderMaxCircleGap; + }; + std::vector before; // crossed pairings of this artificial event, before the dedup and resolvers + for (const auto& ip : posLegs) { + const auto& pos = mLegIt[ip]; + if (const auto itE = eleOfPhoton.find(pos.sourcePhotonUid()); itE != eleOfPhoton.end()) { + const auto ie = itE->second; + const auto& ele = mLegIt[ie]; + { + MixPairPhoton p; + p.px = pos.sourcePx(); + p.py = pos.sourcePy(); + p.pz = pos.sourcePz(); + setMixPairLegs(p, pos, ele); + p.posRow = ip; + p.eleRow = ie; + p.vx = pos.sourceVx(); + p.vy = pos.sourceVy(); + p.vz = pos.sourceVz(); + before.push_back(p); + } + } + } + registry.fill(HIST("Mixed/hNPhotonsIn"), type, static_cast(before.size())); // run-1 photons (pooled free legs are single legs) + fillMixPairs(type, before); + inputOfColl[collision.globalIndex()] = before; + std::vector visit; // negative legs paired with this positive leg, in the original (row) order + for (const auto& ip : posLegs) { + const auto& pos = mLegIt[ip]; + visit.clear(); + if (tglWindow) { // crossed pairings are prefiltered by tgl, then by circle gap + const float tglP = pos.tgl(); + for (auto it = std::lower_bound(eleByTgl.begin(), eleByTgl.end(), std::make_pair(tglP - recovery.finderMaxDeltaTgl - 1e-4f, int64_t{-1})); it != eleByTgl.end() && it->first <= tglP + recovery.finderMaxDeltaTgl + 1e-4f; ++it) { + visit.push_back(it->second); + } + if (const auto itE = eleOfPhoton.find(pos.sourcePhotonUid()); itE != eleOfPhoton.end()) { + visit.push_back(itE->second); + } + std::sort(visit.begin(), visit.end()); + visit.erase(std::unique(visit.begin(), visit.end()), visit.end()); + // crossed pairings outside the window: counted in the cut flow as prefilter rejections (not split by MC in hCrossedFit) + const bool hasPartner = eleOfPhoton.contains(pos.sourcePhotonUid()); + int nOutside = mixed.skipSameSourceCrossed ? static_cast(eleLegs.size()) - nEleOfSource[pos.sourceEventUid()] : static_cast(eleLegs.size()) - (hasPartner ? 1 : 0); + for (const auto& ie : visit) { + const auto& ele = mLegIt[ie]; + if (ele.sourcePhotonUid() != pos.sourcePhotonUid() && !(mixed.skipSameSourceCrossed && pos.sourceEventUid() == ele.sourceEventUid())) { + nOutside--; + } + } + if (nOutside > 0) { + registry.fill(HIST("Mixed/hCandidates"), type, 2.f, static_cast(nOutside)); + } + } else { + visit.assign(eleLegs.begin(), eleLegs.end()); + } + for (const auto& ie : visit) { + const auto& ele = mLegIt[ie]; + const size_t nBefore = vecV0Dedup.size(); + const bool originalPairing = pos.sourcePhotonUid() == ele.sourcePhotonUid(); + if (!originalPairing && mixed.skipSameSourceCrossed && pos.sourceEventUid() == ele.sourceEventUid()) { + continue; // injection: same-source crossed pairings were decided by run 1 + } + if (!originalPairing && mixed.skipFreeFreeCrossed && pos.sourceV0Id() == -2 && ele.sourceV0Id() == -2) { // o2-linter: disable=magic-number (pooled free leg) + continue; // overlay: two free legs, rejected by run 1 + } + if (!originalPairing && mixed.freeFreeFinderOnly && pos.sourceV0Id() == -2 && ele.sourceV0Id() == -2 && pos.hasITS() && ele.hasITS()) { // o2-linter: disable=magic-number (pooled free leg) + continue; // overlay: two free legs with ITS, only an SVertexer V0 in run 1 + } + if (mixed.uniformPairFit) { + // the crossed pairings are fitted with the same options as the originals, but without the AP cut if they were not found by an SVertexer V0 + const bool finderLike = !(pos.hasITS() && ele.hasITS()); + registry.fill(HIST("Mixed/hCandidates"), type, originalPairing ? 0.f : 2.f); + if (mixed.prefilterCrossed && !crossedPrefilter(pos, ele)) { continue; } - usedTracks.push_back(cand.posID); - usedTracks.push_back(cand.eleID); - selected[k] = 1; - } - } - - // ---- store the winners, book the blockers of the losers ---------- - for (int k = 0; k < nMembers; ++k) { - const auto& cand = vecV0Dedup[members[k]]; - if (selected[k] != 0) { - stored_v0Ids.emplace_back(cand.posID, cand.eleID); - stored_fullv0Ids.emplace_back(cand.v0ID, cand.colID, cand.posID, cand.eleID); - nv0_map[cand.colID]++; + if (!originalPairing) { + registry.fill(HIST("Mixed/hCandidates"), type, 3.f); + } + const SourcePV srcPv{pos.sourcePvX(), pos.sourcePvY(), pos.sourcePvZ(), pos.sourcePvCovXX(), pos.sourcePvCovXY(), pos.sourcePvCovXZ(), + pos.sourcePvCovYY(), pos.sourcePvCovYZ(), pos.sourcePvCovZZ(), pos.sourcePvChi2(), pos.sourcePvNContrib()}; + fitV0Candidate(collision, pos, ele, originalPairing ? ip : -1, false, + FitOptions{.nonSVertexer = finderLike, .relaxAP = finderLike, .purityGate = finderLike && recovery.runFinder.value, .pv = (originalPairing && mixed.refitOriginalsAtSourceVertex) ? &srcPv : nullptr}); + if (vecV0Dedup.size() > nBefore) { + registry.fill(HIST("Mixed/hCandidates"), type, originalPairing ? 1.f : 4.f); + } continue; } - if (diag == nullptr) { + if (originalPairing) { // the original pairings are fitted with the same options as in run 1, but without the AP cut if they were not found by an SVertexer V0 + registry.fill(HIST("Mixed/hCandidates"), type, 0.f); + const SourcePV srcPv{pos.sourcePvX(), pos.sourcePvY(), pos.sourcePvZ(), pos.sourcePvCovXX(), pos.sourcePvCovXY(), pos.sourcePvCovXZ(), + pos.sourcePvCovYY(), pos.sourcePvCovYZ(), pos.sourcePvCovZZ(), pos.sourcePvChi2(), pos.sourcePvNContrib()}; + fitV0Candidate(collision, pos, ele, ip, false, + FitOptions{.nonSVertexer = pos.sourceV0Id() < 0 && !recovery.finderUniformFit.value, .relaxAP = pos.sourceV0Id() < 0 && !recovery.finderUniformFit.value, .pv = mixed.refitOriginalsAtSourceVertex ? &srcPv : nullptr}); + if (vecV0Dedup.size() > nBefore) { + registry.fill(HIST("Mixed/hCandidates"), type, 1.f); + } continue; } - // all winners that took one leg - const CandKey victimKey = std::make_tuple(static_cast(cand.v0ID), static_cast(cand.colID), - static_cast(cand.posID), static_cast(cand.eleID)); - for (int j = 0; j < nMembers; ++j) { - if (selected[j] == 0) { - continue; - } - const auto& winner = vecV0Dedup[members[j]]; - if (winner.posID == cand.posID || winner.eleID == cand.eleID) { - diag->blockersByKey[victimKey].emplace_back(static_cast(winner.v0ID), static_cast(winner.colID), - static_cast(winner.posID), static_cast(winner.eleID)); + registry.fill(HIST("Mixed/hCandidates"), type, 2.f); + // crossed pairings: the same prefilter and the same leg-based fit options for every pairing (original pairings keep v0Id = row of the positive leg) + const bool twoPhotons = pos.mcMotherPdg() == kGamma && ele.mcMotherPdg() == kGamma && pos.mcMotherId() >= 0 && ele.mcMotherId() >= 0 && + (pos.sourceEventUid() != ele.sourceEventUid() || pos.mcMotherId() != ele.mcMotherId()); + const float qTwo = twoPhotons ? qInv(pos.mcMotherPx(), pos.mcMotherPy(), pos.mcMotherPz(), ele.mcMotherPx(), ele.mcMotherPy(), ele.mcMotherPz()) : 1.f; + const float genuineClose = (twoPhotons && qTwo < 0.05f) ? 1.f : 0.f; // o2-linter: disable=magic-number (diagnostic range) + const auto nITSx = static_cast((pos.hasITS() ? 1 : 0) + (ele.hasITS() ? 1 : 0)); + if (mixed.prefilterCrossed && !crossedPrefilterCached(ip, ie)) { + if (mixed.mixedDiagnostics) { + registry.fill(HIST("Mixed/Decision/hCrossedFit"), type, genuineClose, 1.f, nITSx, std::min(qTwo, 0.0499f)); } + continue; } - } - } - - if (nGreedyFallback > 0) { - LOGF(warning, "group matching: %d conflict group(s) larger than dedupMaxGroupSize = %d were solved greedily", nGreedyFallback, maxEnum); - } - - // the table expects candidates ordered by collision - std::sort(stored_fullv0Ids.begin(), stored_fullv0Ids.end(), - [](const auto& a, const auto& b) { - return std::get<1>(a) < std::get<1>(b); - }); - - } else { - // Sort best candidates first, depending on score - std::sort(vecV0Dedup.begin(), vecV0Dedup.end()); - // container to keep track of which electron and positron tracks have already been used - std::vector usedLegs(tracks.size(), false); - std::unordered_map ownerOfLeg; // diagnostics only - stored_v0Ids.clear(); - stored_fullv0Ids.clear(); - nv0_map.clear(); - for (const auto& v0Cand : vecV0Dedup) { - const CandKey candKey = std::make_tuple(static_cast(v0Cand.v0ID), static_cast(v0Cand.colID), - static_cast(v0Cand.posID), static_cast(v0Cand.eleID)); - if (usedLegs[v0Cand.posID] || usedLegs[v0Cand.eleID]) { - if (diag != nullptr) { - // both legs, not just the first one found: a cross-leg fake is usually blocked by - // two different winners, and that is exactly the case being measured here - auto& blockers = diag->blockersByKey[candKey]; - for (const int& legId : {v0Cand.posID, v0Cand.eleID}) { - const auto itOwner = ownerOfLeg.find(legId); - if (itOwner == ownerOfLeg.end()) { - continue; + registry.fill(HIST("Mixed/hCandidates"), type, 3.f); + const bool anyFree = pos.sourceV0Id() == -2 || ele.sourceV0Id() == -2; // o2-linter: disable=magic-number (pooled free leg) + const bool freeLegPair = mixed.freeLegRun1Rules && anyFree; + const bool finderLikeFree = freeLegPair && !(pos.hasITS() && ele.hasITS()); + if (mixed.diagFreeLegFits && anyFree) { // trial fits with both option sets, candidates removed again + const bool finderLike = !(pos.hasITS() && ele.hasITS()); + auto trial = [&](FitOptions const& o) { + const size_t n0 = vecV0Dedup.size(); + fitV0Candidate(collision, pos, ele, -1, false, o); + // cppcheck-suppress knownConditionTrueFalse + if (vecV0Dedup.size() > n0) { + const auto k = keyOf(vecV0Dedup.back()); + pca_map.erase(k); + cospa_map.erase(k); + vecV0Dedup.pop_back(); + return 0; } - if (std::find(blockers.begin(), blockers.end(), itOwner->second) == blockers.end()) { - blockers.push_back(itOwner->second); // the same winner may hold both legs - } - } + return std::max(mLastFitReject, 2); + }; + const int lenient = trial(FitOptions{.relaxAP = mixed.crossedRelaxAP.value}); + const int run1 = trial(FitOptions{.nonSVertexer = finderLike, .purityGate = finderLike && recovery.runFinder.value}); + const auto nFree = static_cast((pos.sourceV0Id() == -2 ? 1 : 0) + (ele.sourceV0Id() == -2 ? 1 : 0)); // o2-linter: disable=magic-number (pooled free leg) + registry.fill(HIST("Mixed/Decision/hFreeLegFit"), type, genuineClose > 0.5f ? 0.f : 1.f, static_cast(std::min(lenient, 1999)), static_cast(std::min(run1, 1999)), nITSx, nFree); + } + const int mask = mixed.freeLegRuleMask.value; + const FitOptions freeOpt{.nonSVertexer = finderLikeFree && (mask & 4) != 0, .relaxAP = (mask & 1) == 0 && mixed.crossedRelaxAP.value, // o2-linter: disable=magic-number (bit mask) + .purityGate = finderLikeFree && (mask & 2) != 0 && recovery.runFinder.value, + .minPurity = mixed.freeLegMinPurity.value}; // o2-linter: disable=magic-number (bit mask) + fitV0Candidate(collision, pos, ele, -1, false, freeLegPair ? freeOpt : FitOptions{.relaxAP = mixed.crossedRelaxAP.value}); + if (vecV0Dedup.size() > nBefore) { + registry.fill(HIST("Mixed/hCandidates"), type, 4.f); + } + if (mixed.mixedDiagnostics) { + registry.fill(HIST("Mixed/Decision/hCrossedFit"), type, genuineClose, vecV0Dedup.size() > nBefore ? 0.f : static_cast(mLastFitReject), nITSx, std::min(qTwo, 0.0499f)); } - continue; - } - usedLegs[v0Cand.posID] = true; - usedLegs[v0Cand.eleID] = true; - if (diag != nullptr) { - ownerOfLeg[v0Cand.posID] = candKey; - ownerOfLeg[v0Cand.eleID] = candKey; } - stored_v0Ids.emplace_back(v0Cand.posID, v0Cand.eleID); - stored_fullv0Ids.emplace_back(v0Cand.v0ID, v0Cand.colID, v0Cand.posID, v0Cand.eleID); - nv0_map[v0Cand.colID]++; } + registry.fill(HIST("Mixed/hTimeCandidates"), type, std::chrono::duration(std::chrono::steady_clock::now() - t0).count()); + } - // sort accepted candidates by collision ID to be compatible with table - std::sort(stored_fullv0Ids.begin(), stored_fullv0Ids.end(), - [](const auto& a, const auto& b) { - return std::get<1>(a) < std::get<1>(b); - }); - - // End of deduplication + const size_t nFit = vecV0Dedup.size(); // candidates of the run-2 fit (the resolvers may append more) + switch (dedup.deduplicationMode.value) { + case V0DeduplicationMode::Pairwise: + selectPairwise(nullptr); + break; + case V0DeduplicationMode::KeepAll: + keepAll(); + break; + case V0DeduplicationMode::GroupMatching: + selectByConflictGroup(nullptr); + break; + default: + selectGreedy(legs.size(), nullptr); + break; + } + if (dedup.resolveSplitLegs) { + resolveSplitLegConflicts(legs, nullptr); + } + if (recovery.runSwapResolver) { + runSwapResolver(collisions, legs, nullptr); + } + if (mixed.mixedDiagnostics && dedup.deduplicationMode.value == static_cast(V0DeduplicationMode::GroupMatching)) { + fillFourLegDecisions(collisions, legs, nFit); + } + if (mixed.mixedDiagnostics) { + fillMatchedConfigs(collisions, legs); } - if (diag != nullptr) { - diag->snapshot = vecV0Dedup; - diag->stored = stored_fullv0Ids; + std::unordered_map> nStored; // artificial event -> (originals, crossed) + std::unordered_map> after; // artificial event -> stored photons + for (const auto& key : stored_fullv0Ids) { + const auto collision = collisions.rawIteratorAt(key.collisionId); + const auto pos = legs.rawIteratorAt(key.posId); + const auto ele = legs.rawIteratorAt(key.eleId); + const bool original = key.v0Id >= 0; + const float type = collision.isMixed() ? 1.f : 0.f; + const auto nWritten = v0photonskf.lastIndex(); + const SourcePV srcPv{pos.sourcePvX(), pos.sourcePvY(), pos.sourcePvZ(), pos.sourcePvCovXX(), pos.sourcePvCovXY(), pos.sourcePvCovXZ(), + pos.sourcePvCovYY(), pos.sourcePvCovYZ(), pos.sourcePvCovZZ(), pos.sourcePvChi2(), pos.sourcePvNContrib()}; + const bool finderLike = mixed.uniformPairFit && !(pos.hasITS() && ele.hasITS()); // uniformPairFit: options by the legs, as in the candidate fit + fitV0Candidate(collision, pos, ele, key.v0Id, true, + FitOptions{.nonSVertexer = mixed.uniformPairFit ? finderLike : (original && pos.sourceV0Id() < 0 && !recovery.finderUniformFit.value), + .relaxAP = mRelaxedKeys.contains(key) || (mixed.uniformPairFit ? finderLike : (original ? (pos.sourceV0Id() < 0 && !recovery.finderUniformFit.value) : mixed.crossedRelaxAP.value)), + .purityGate = finderLike && recovery.runFinder.value, + .pv = (original && mixed.refitOriginalsAtSourceVertex) ? &srcPv : nullptr}); + if (v0photonskf.lastIndex() == nWritten) { + continue; // rejected by the ML cut (not a dedup candidate then either) + } + const auto& c = vecV0Dedup.back(); + nStored[key.collisionId][original ? 0 : 1]++; + registry.fill(HIST("Mixed/hStored"), type, original ? 0.f : 1.f); + if (original) { + const float pt1 = std::hypot(pos.sourcePx(), pos.sourcePy()); + const float pt2 = std::hypot(c.pxAtPV, c.pyAtPV); + registry.fill(HIST("Mixed/hOrigRelDPt"), type, pt2 / pt1 - 1.f); + registry.fill(HIST("Mixed/hOrigDPhi"), type, RecoDecay::constrainAngle(RecoDecay::phi(c.pxAtPV, c.pyAtPV) - RecoDecay::phi(pos.sourcePx(), pos.sourcePy()), -o2::constants::math::PI)); + registry.fill(HIST("Mixed/hOrigDEta"), type, RecoDecay::eta(std::array{c.pxAtPV, c.pyAtPV, c.pzAtPV}) - RecoDecay::eta(std::array{pos.sourcePx(), pos.sourcePy(), pos.sourcePz()})); + registry.fill(HIST("Mixed/hOrigExact"), type, (c.pxAtPV == pos.sourcePx() && c.pyAtPV == pos.sourcePy() && c.pzAtPV == pos.sourcePz()) ? 1.f : 0.f); + } else if (pos.sourceV0Id() != -2 && ele.sourceV0Id() != -2) { // -2: pooled free leg, no run-1 photon + registry.fill(HIST("Mixed/hCrossedSourceQ"), type, qInv(pos.sourcePx(), pos.sourcePy(), pos.sourcePz(), ele.sourcePx(), ele.sourcePy(), ele.sourcePz())); + registry.fill(HIST("Mixed/hCrossedSourceDist"), type, std::hypot(pos.sourceVx() - ele.sourceVx(), pos.sourceVy() - ele.sourceVy(), pos.sourceVz() - ele.sourceVz())); + registry.fill(HIST("Mixed/hCrossedSourceEvents"), type, pos.sourceEventUid() == ele.sourceEventUid() ? 0.f : 1.f); + } + float truth = 4.f; // no label + if (pos.mcMotherId() >= 0 && ele.mcMotherId() >= 0) { + const bool truePhoton = pos.sourceEventUid() == ele.sourceEventUid() && pos.mcMotherId() == ele.mcMotherId() && pos.mcMotherPdg() == kGamma; + truth = (original ? 0.f : 2.f) + (truePhoton ? 0.f : 1.f); + } + registry.fill(HIST("Mixed/hStoredTruth"), type, truth); + MixPairPhoton stored; + stored.px = c.pxAtPV; + stored.py = c.pyAtPV; + stored.pz = c.pzAtPV; + setMixPairLegs(stored, pos, ele); + stored.posRow = key.posId; + stored.eleRow = key.eleId; + stored.original = original; + if (const auto itGroup = mGroupOfKey.find(key); itGroup != mGroupOfKey.end()) { + stored.group = itGroup->second; + } + stored.vx = c.svX; + stored.vy = c.svY; + stored.vz = c.svZ; + after[key.collisionId].push_back(stored); + } + for (const auto& collision : collisions) { + const float type = collision.isMixed() ? 1.f : 0.f; + const auto it = nStored.find(collision.globalIndex()); + const std::array n = it == nStored.end() ? std::array{0, 0} : it->second; + const auto itIn = inputOfColl.find(collision.globalIndex()); + const int nIn = itIn == inputOfColl.end() ? 0 : static_cast(itIn->second.size()); // run-1 photons (not the pooled free legs) + registry.fill(HIST("Mixed/hNStoredVsIn"), type, nIn, n[0] + n[1]); + registry.fill(HIST("Mixed/hNOriginalLost"), type, nIn - n[0]); + registry.fill(HIST("Mixed/hNCrossedStored"), type, n[1]); + if (const auto itA = after.find(collision.globalIndex()); mixed.mixedDiagnostics && itA != after.end()) { + fillMixPairs(type, itA->second); + fillSameSourcePairs(type, itA->second); + } } - std::set skimRejected; - if (skim.minNv0PerCollision > 0 || skim.maxMinvPairPerCollision > 0.f) { - const std::set storedSetSkim(stored_fullv0Ids.begin(), stored_fullv0Ids.end()); - std::unordered_map> candsPerColl; + if (mixed.mixedDiagnostics) { // MC diagnostics of the pairings (cost on data) + // alternative pairings of the input pairs: passed the run-2 fit (candidate) / stored + std::set> candLegs, storedLegs; for (const auto& c : vecV0Dedup) { - if (storedSetSkim.contains(std::make_tuple(static_cast(c.v0ID), static_cast(c.colID), - static_cast(c.posID), static_cast(c.eleID)))) { - candsPerColl[c.colID].push_back(&c); - } + candLegs.emplace(c.colID, c.posID, c.eleID); } - for (const auto& [colId, cands] : candsPerColl) { - if (skim.minNv0PerCollision > 0 && static_cast(cands.size()) < skim.minNv0PerCollision) { - skimRejected.insert(colId); - continue; + for (const auto& k : stored_fullv0Ids) { + storedLegs.emplace(k.collisionId, k.posId, k.eleId); + } + for (const auto& [colId, input] : inputOfColl) { + fillMixFakeClasses(collisions.rawIteratorAt(colId).isMixed() ? 1.f : 0.f, 0.f, colId, input, candLegs); + } + for (const auto& [colId, out] : after) { + fillMixFakeClasses(collisions.rawIteratorAt(colId).isMixed() ? 1.f : 0.f, 1.f, colId, out, candLegs); + } + // swap-symmetric counting (data-computable): the decision between the two pairings of an ambiguous 4-leg configuration no longer + // enters the pair count, in the same and the mixed event alike + { + std::map, std::array> candMom; // first fit of a leg pair = its dedup candidate + for (const auto& c : vecV0Dedup) { + candMom.emplace(std::make_tuple(static_cast(c.colID), static_cast(c.posID), static_cast(c.eleID)), std::array{c.pxAtPV, c.pyAtPV, c.pzAtPV}); } - if (skim.maxMinvPairPerCollision > 0.f) { - bool hasLowMPair = false; - for (size_t i = 0; i < cands.size() && !hasLowMPair; ++i) { - for (size_t j = i + 1; j < cands.size(); ++j) { - ROOT::Math::PxPyPzMVector v1(cands[i]->pxAtPV, cands[i]->pyAtPV, cands[i]->pzAtPV, 0.f); - ROOT::Math::PxPyPzMVector v2(cands[j]->pxAtPV, cands[j]->pyAtPV, cands[j]->pzAtPV, 0.f); - if ((v1 + v2).M() < skim.maxMinvPairPerCollision) { - hasLowMPair = true; - break; + for (const auto& [colId, out] : after) { + const float type = collisions.rawIteratorAt(colId).isMixed() ? 1.f : 0.f; + for (size_t i = 0; i < out.size(); ++i) { + for (size_t j = i + 1; j < out.size(); ++j) { + const auto& a = out[i]; + const auto& b = out[j]; + if (skipSameSourcePair(type, a, b)) { + continue; + } + const float q = qInv(a.px, a.py, a.pz, b.px, b.py, b.pz); + const auto itX1 = candMom.find(std::make_tuple(static_cast(colId), a.posRow, b.eleRow)); + const auto itX2 = candMom.find(std::make_tuple(static_cast(colId), b.posRow, a.eleRow)); + const bool ambiguous = itX1 != candMom.end() && itX2 != candMom.end(); + const float qX = ambiguous ? qInv(itX1->second[0], itX1->second[1], itX1->second[2], itX2->second[0], itX2->second[1], itX2->second[2]) : 999.f; + if (q >= 0.5f && (!ambiguous || qX >= 0.5f)) { // o2-linter: disable=magic-number (histogram range) + continue; + } + const float cls = mixPairClass(a, b); + if (ambiguous) { + registry.fill(HIST("Mixed/Pairs/hQSym"), type, cls, q, 0.5f); + registry.fill(HIST("Mixed/Pairs/hQSym"), type, cls, qX, 0.5f); + } else { + registry.fill(HIST("Mixed/Pairs/hQSym"), type, cls, q); } } } - if (!hasLowMPair) { - skimRejected.insert(colId); - } } } - } - - for (const auto& fullv0Id : stored_fullv0Ids) { - auto v0Id = std::get<0>(fullv0Id); - const auto collisionId = std::get<1>(fullv0Id); - if (skimRejected.contains(collisionId)) { - continue; // collision failed the skim - do not store its photons/legs + std::map, float> candScore; // first fit of a leg pair = its dedup candidate + for (const auto& c : vecV0Dedup) { + candScore.emplace(std::make_tuple(static_cast(c.colID), static_cast(c.posID), static_cast(c.eleID)), c.score); } - // auto posId = std::get<2>(fullv0Id); - // auto eleId = std::get<3>(fullv0Id); - // LOGF(info, "!accept! | collision id = %d | v0id = %d , posid = %d , eleid = %d", collisionId, v0Id, posId, eleId); - - auto v0 = v0s.rawIteratorAt(v0Id); - if constexpr (enableFilter) { - auto collision_tmp = v0.template collision_as(); // collision where this v0 belongs. - if (!(collision_tmp.neeuls() >= 1 || collision_tmp.neeuls() + nv0_map[collision_tmp.globalIndex()] >= 2)) { // o2-linter: disable=magic-number (nonsensical default value) - continue; + for (const auto& [colId, input] : inputOfColl) { + fillMixSwapBrems(collisions.rawIteratorAt(colId).isMixed() ? 1.f : 0.f, 0.f, colId, input, candScore, legs); + } + for (const auto& [colId, out] : after) { + fillMixSwapBrems(collisions.rawIteratorAt(colId).isMixed() ? 1.f : 0.f, 1.f, colId, out, candScore, legs); + } + for (const auto& [colId, input] : inputOfColl) { + const float type = collisions.rawIteratorAt(colId).isMixed() ? 1.f : 0.f; + for (size_t i = 0; i < input.size(); ++i) { + for (size_t j = i + 1; j < input.size(); ++j) { + const auto& a = input[i]; + const auto& b = input[j]; + const int truth = mixPairTruth(a, b); + if (truth != 1 && truth != 2) { + continue; + } + const float q = qInv(a.px, a.py, a.pz, b.px, b.py, b.pz); + if (q >= 0.1f) { // o2-linter: disable=magic-number (diagnostic range) + continue; + } + const auto alt1 = std::make_tuple(colId, a.posRow, b.eleRow); + const auto alt2 = std::make_tuple(colId, b.posRow, a.eleRow); + const int nAlt = static_cast(candLegs.contains(alt1)) + static_cast(candLegs.contains(alt2)); + int outcome = 2; + if (storedLegs.contains({colId, a.posRow, a.eleRow}) && storedLegs.contains({colId, b.posRow, b.eleRow})) { + outcome = 0; + } else if (storedLegs.contains(alt1) && storedLegs.contains(alt2)) { + outcome = 1; + } + if (truth == 1) { + registry.fill(HIST("Mixed/Pairs/hAltTrueTrue"), type, static_cast(3 * nAlt + outcome), q); + } else { + registry.fill(HIST("Mixed/Pairs/hAltSwap"), type, static_cast(3 * nAlt + outcome), q); + } + } } - // LOGF(info, "collision_tmp.globalIndex() = %d, collision_tmp.neeuls() = %d, nv0_map = %d", collision_tmp.globalIndex(), collision_tmp.neeuls(), nv0_map[collision_tmp.globalIndex()]); } - - fillV0Table(v0, true); - } // end of fullv0Id loop - - // for (const auto& collision : collisions) { - // if constexpr (isMC) { - // if (!collision.has_mcCollision()) { - // continue; - // } - // } - // // events_ngpcm(nv0_map[collision.globalIndex()]); - // } // end of collision loop + } pca_map.clear(); cospa_map.clear(); nv0_map.clear(); stored_v0Ids.clear(); - stored_v0Ids.shrink_to_fit(); stored_fullv0Ids.clear(); - stored_fullv0Ids.shrink_to_fit(); vecV0Dedup.clear(); - } // end of build + registry.fill(HIST("Mixed/hTimeDF"), std::chrono::duration(std::chrono::steady_clock::now() - tDF).count()); + } - o2::framework::expressions::Filter v0Filter = o2::aod::v0::v0Type > (uint8_t)0; + o2::framework::expressions::Filter v0Filter = o2::aod::v0::v0Type > static_cast(0); using FilteredV0s = o2::soa::Filtered; enum PairFate { @@ -1574,8 +3991,146 @@ struct PhotonConversionBuilder { } } - template - void fillDedupTruthDiagnostics(TTracks const& tracks, TMCParticles const& mcparticles, DedupDiag const& diag) + struct PhotonFate { + bool alive = false; + int best = -1; // best-score candidate + std::vector cands; // all true candidates of this photon + std::set aliveColls; // collisions with a surviving candidate + }; + + // which collision the copies of a true photon end up in + template + void fillCopyDiagnostics(TCollisions const& collisions, TMCParticles const& mcparticles, DedupDiag const& diag, std::map const& fate) + { + auto isRightColl = [&](int64_t colId, int64_t mid) { + const auto col = collisions.rawIteratorAt(colId); + return col.has_mcCollision() && col.mcCollisionId() == mcparticles.iteratorAt(mid).mcCollisionId(); + }; + auto dzPointing = [&](V0CandidateHelper const& c) { + const auto col = collisions.rawIteratorAt(c.colID); + const float pt = std::hypot(c.pxAtPV, c.pyAtPV); + return pt > 0.f ? std::fabs(c.svZ - std::hypot(c.svX - col.posX(), c.svY - col.posY()) * c.pzAtPV / pt - col.posZ()) : 999.f; // o2-linter: disable=magic-number (no direction) + }; + for (const auto& [mid, f] : fate) { + std::set copyColls; + bool rightAvailable = false; + for (const int& ci : f.cands) { + copyColls.insert(diag.snapshot[ci].colID); + rightAvailable = rightAvailable || isRightColl(diag.snapshot[ci].colID, mid); + } + int choice = 3; // o2-linter: disable=magic-number (bin 3 = lost) + if (f.alive) { + const auto& midRef = mid; // ordinary variable, not a structured binding + const bool storedRight = std::any_of(f.aliveColls.begin(), f.aliveColls.end(), [&](int64_t c) { return isRightColl(c, midRef); }); + choice = storedRight ? 0 : (rightAvailable ? 1 : 2); + } + const int nCopies = static_cast(copyColls.size()); + registry.fill(HIST("MCDedup/Copies/hChoice"), static_cast(std::min(nCopies, 5)), static_cast(choice)); // o2-linter: disable=magic-number (last bin) + constexpr int MinCopies = 2; + if (nCopies < MinCopies || !rightAvailable) { + continue; + } + int bestDz = -1, bestPA = -1, bestPA3D = -1; + for (const int& ci : f.cands) { + const auto& c = diag.snapshot[ci]; + const float right = isRightColl(c.colID, mid) ? 1.f : 0.f; + registry.fill(HIST("MCDedup/Copies/hPAxy"), right, std::acos(std::clamp(c.cosPAxy, -1.f, 1.f))); + registry.fill(HIST("MCDedup/Copies/hPA3D"), right, std::acos(std::clamp(c.cosPA3D, -1.f, 1.f))); + if (bestDz < 0 || dzPointing(c) < dzPointing(diag.snapshot[bestDz])) { + bestDz = ci; + } + if (bestPA < 0 || c.cosPAxy > diag.snapshot[bestPA].cosPAxy) { + bestPA = ci; + } + if (bestPA3D < 0 || c.cosPA3D > diag.snapshot[bestPA3D].cosPA3D) { + bestPA3D = ci; + } + } + if (f.alive) { // same photons for all rules + registry.fill(HIST("MCDedup/Copies/hRule"), 0.f, choice == 0 ? 1.f : 0.f); + registry.fill(HIST("MCDedup/Copies/hRule"), 1.f, isRightColl(diag.snapshot[bestDz].colID, mid) ? 1.f : 0.f); + registry.fill(HIST("MCDedup/Copies/hRule"), 2.f, isRightColl(diag.snapshot[bestPA].colID, mid) ? 1.f : 0.f); + registry.fill(HIST("MCDedup/Copies/hRule"), 3.f, isRightColl(diag.snapshot[bestPA3D].colID, mid) ? 1.f : 0.f); + } + } + } + + template + void fillRun1FourLeg(TMCParticles const& mcparticles, DedupDiag const& diag, std::vector const& cls, std::vector const& motherPos, + std::set const& storedSet, std::map& nCandsOfTrack) + { + using Key3 = std::tuple; + const int nCand = static_cast(diag.snapshot.size()); + std::map firstCand; // first fit of a leg pair = its candidate + std::map, int> trueOfPhoton; // (collision, MC photon) -> first true candidate + for (int i = 0; i < nCand; ++i) { + const auto& c = diag.snapshot[i]; + firstCand.emplace(Key3{c.colID, c.posID, c.eleID}, i); + if (cls[i] == kV0True) { + trueOfPhoton.emplace(std::make_pair(static_cast(c.colID), motherPos[i]), i); + } + } + std::map>> photonsOfColl; + for (const auto& [k, i] : trueOfPhoton) { + photonsOfColl[k.first].emplace_back(k.second, i); + } + auto stored = [&](int i) { return storedSet.contains(keyOf(diag.snapshot[i])); }; + const std::vector legMask{0b0011, 0b1100, 0b1001, 0b0110}; + for (const auto& [colId, phs] : photonsOfColl) { + for (size_t i = 0; i < phs.size(); ++i) { + for (size_t j = i + 1; j < phs.size(); ++j) { + const auto& a = diag.snapshot[phs[i].second]; + const auto& b = diag.snapshot[phs[j].second]; + if (a.posID == b.posID || a.eleID == b.eleID) { + continue; // two MC photons sharing a track (should not happen for true candidates) + } + const auto itX1 = firstCand.find(Key3{colId, a.posID, b.eleID}); + const auto itX2 = firstCand.find(Key3{colId, b.posID, a.eleID}); + if (itX1 == firstCand.end() || itX2 == firstCand.end()) { + continue; + } + const auto mA = mcparticles.rawIteratorAt(phs[i].first); + const auto mB = mcparticles.rawIteratorAt(phs[j].first); + const float qTrue = qInv(mA.px(), mA.py(), mA.pz(), mB.px(), mB.py(), mB.pz()); + if (qTrue >= 0.05f) { // o2-linter: disable=magic-number (diagnostic range) + continue; + } + const int iA = phs[i].second, iB = phs[j].second, iX1 = itX1->second, iX2 = itX2->second; + const int nT = (stored(iA) ? 1 : 0) + (stored(iB) ? 1 : 0), nX = (stored(iX1) ? 1 : 0) + (stored(iX2) ? 1 : 0); + float outcome = 5.f; + if (nT == 2 && nX == 0) { + outcome = 0.f; + } else if (nT == 0 && nX == 2) { + outcome = 1.f; + } else if (nT == 1 && nX == 0) { + outcome = 2.f; + } else if (nT == 0 && nX == 1) { + outcome = 3.f; + } else if (nT == 0 && nX == 0) { + outcome = 4.f; + } + const std::vector scores{a.score, b.score, diag.snapshot[iX1].score, diag.snapshot[iX2].score}; + const auto sol = solveConflictGroup(legMask, scores, dedup.groupMatchingScoreThreshold.value, false); + const float isolated = sol.bestMask == 0b0011 ? 0.f : (sol.bestMask == 0b1100 ? 1.f : 2.f); // o2-linter: disable=magic-number (subset masks) + const int nThird = nCandsOfTrack[a.posID] + nCandsOfTrack[a.eleID] + nCandsOfTrack[b.posID] + nCandsOfTrack[b.eleID] - 8; // o2-linter: disable=magic-number (each leg is in 2 of the 4) + bool related = false; + if (mA.has_mothers() && mB.has_mothers()) { + const auto gA = mcparticles.rawIteratorAt(mA.mothersIds()[0]); + const auto gB = mcparticles.rawIteratorAt(mB.mothersIds()[0]); + related = gA.globalIndex() == gB.globalIndex() || std::abs(gA.pdgCode()) == kElectron || std::abs(gB.pdgCode()) == kElectron; + } + const bool finder = a.v0ID < 0 || b.v0ID < 0; + registry.fill(HIST("MCDedup/FourLeg/hRun1"), qTrue, outcome, isolated, static_cast(std::clamp(nThird, 0, 5)), related ? 1.f : 0.f, finder ? 1.f : 0.f); // o2-linter: disable=magic-number (last bin) + const int crossedOrigin = (diag.snapshot[iX1].v0ID < 0 ? 1 : 0) + (diag.snapshot[iX2].v0ID < 0 ? 1 : 0); // finder off: non-SVertexer = swap resolver + const int trueOrigin = (a.v0ID < 0 ? 1 : 0) + (b.v0ID < 0 ? 1 : 0); + registry.fill(HIST("MCDedup/FourLeg/hRun1Origin"), qTrue, outcome, static_cast(crossedOrigin), static_cast(trueOrigin), nThird > 0 ? 1.f : 0.f, related ? 1.f : 0.f); + } + } + } + } + + template + void fillDedupTruthDiagnostics(TCollisions const& collisions, TTracks const& tracks, TMCParticles const& mcparticles, DedupDiag const& diag) { const int nCand = static_cast(diag.snapshot.size()); if (nCand == 0) { @@ -1589,8 +4144,7 @@ struct PhotonConversionBuilder { std::map indexOfKey; for (int i = 0; i < nCand; ++i) { const auto& c = diag.snapshot[i]; - key[i] = std::make_tuple(static_cast(c.v0ID), static_cast(c.colID), - static_cast(c.posID), static_cast(c.eleID)); + key[i] = keyOf(c); indexOfKey[key[i]] = i; cls[i] = static_cast(classifyV0Truth(tracks.rawIteratorAt(c.posID), tracks.rawIteratorAt(c.eleID), mcparticles, motherPos[i], motherEle[i])); @@ -1600,12 +4154,6 @@ struct PhotonConversionBuilder { } } - struct PhotonFate { - bool alive = false; - int best = -1; // best-score candidate, only used for the margin plots - std::vector cands; // all true candidates of this photon - std::set aliveColls; // collisions with a surviving candidate - }; std::map fate; for (int i = 0; i < nCand; ++i) { if (cls[i] != kV0True) { @@ -1615,17 +4163,32 @@ struct PhotonConversionBuilder { f.cands.push_back(i); if (storedSet.contains(key[i]) > 0) { f.alive = true; - f.aliveColls.insert(std::get<1>(key[i])); + f.aliveColls.insert(key[i].collisionId); } if (f.best < 0 || diag.snapshot[i].score < diag.snapshot[f.best].score) { f.best = i; } } + std::map nCandsOfTrack; + for (int i = 0; i < nCand; ++i) { + nCandsOfTrack[diag.snapshot[i].posID]++; + nCandsOfTrack[diag.snapshot[i].eleID]++; + } + auto fillFateProps = [&](int ci, float fateBin) { + const auto& c = diag.snapshot[ci]; + const int nITS = (tracks.rawIteratorAt(c.posID).hasITS() ? 1 : 0) + (tracks.rawIteratorAt(c.eleID).hasITS() ? 1 : 0); + const int nOther = nCandsOfTrack[c.posID] + nCandsOfTrack[c.eleID] - 2; + registry.fill(HIST("MCDedup/Photons/hFateProps"), fateBin, std::hypot(c.pxAtPV, c.pyAtPV), std::hypot(c.svX, c.svY), static_cast(nITS), + std::acos(std::clamp(c.cosPA, -1.f, 1.f)), c.pca, static_cast(std::min(nOther, 10))); // o2-linter: disable=magic-number (last bin) + }; + std::map> blockersOfPhoton; for (const auto& [mid, f] : fate) { if (f.alive) { registry.fill(HIST("MCDedup/Photons/hFate"), 0.f); + const auto itStored = std::find_if(f.cands.begin(), f.cands.end(), [&](int ci) { return storedSet.contains(key[ci]); }); + fillFateProps(itStored != f.cands.end() ? *itStored : f.best, 0.f); continue; } auto& blockers = blockersOfPhoton[mid]; @@ -1664,6 +4227,10 @@ struct PhotonConversionBuilder { } // hVictimBlockerClass below counts one entry per blocker and a victim can have two. registry.fill(HIST("MCDedup/Photons/hFate"), blockers.empty() ? 2.f : 1.f); + const bool lostToCross = std::any_of(blockers.begin(), blockers.end(), [&](int w) { return cls[w] == kV0CrossLegFake; }); + if (f.best >= 0) { + fillFateProps(f.best, blockers.empty() ? 3.f : (lostToCross ? 1.f : 2.f)); + } if (blockers.empty()) { registry.fill(HIST("MCDedup/Photons/hBlockerClass"), 3.f); // o2-linter: disable=magic-number (bin 3 = no blocker) @@ -1681,7 +4248,62 @@ struct PhotonConversionBuilder { } } - // ---- truth pairs, grouped by MC collision ---------------------------- + fillRun1FourLeg(mcparticles, diag, cls, motherPos, storedSet, nCandsOfTrack); + + { + std::map, int> storedOfTrack; // (collision, track) -> stored candidate + for (int i = 0; i < nCand; ++i) { + if (storedSet.contains(key[i])) { + const auto& c = diag.snapshot[i]; + storedOfTrack[{c.colID, c.posID}] = i; + storedOfTrack[{c.colID, c.eleID}] = i; + } + } + const float s0 = dedup.groupMatchingScoreThreshold.value; + auto contrib = [&](float sc) { return std::max(s0 - sc, 1e-3f); }; // o2-linter: disable=magic-number (objective floor) + for (const auto& [mid, f] : fate) { + if (f.best < 0) { + continue; + } + const auto& t = diag.snapshot[f.best]; + const float pt = std::hypot(t.pxAtPV, t.pyAtPV); + if (f.alive) { + registry.fill(HIST("MCDedup/HalfSwap/hSource"), 0.f, t.score, 0.f, 0.f, pt); + continue; + } + const auto ip = storedOfTrack.find({t.colID, t.posID}); + const auto ie = storedOfTrack.find({t.colID, t.eleID}); + const bool hp = ip != storedOfTrack.end(), he = ie != storedOfTrack.end(); + float cat = 3.f; + if (hp && he) { + cat = ip->second != ie->second ? 1.f : 4.f; + } else if (hp || he) { + cat = 2.f; + } + float dObj = 0.f, dRaw = 0.f; + if (cat == 1.f) { + const auto& w1 = diag.snapshot[ip->second]; + const auto& w2 = diag.snapshot[ie->second]; + dObj = contrib(w1.score) + contrib(w2.score) - contrib(t.score); + dRaw = 0.5f * (w1.score + w2.score) - t.score; + } + registry.fill(HIST("MCDedup/HalfSwap/hSource"), cat, t.score, std::clamp(dObj, -0.499f, 0.499f), std::clamp(dRaw, -0.999f, 0.999f), pt); + } + } + + if (dedup.cfgExtendedDiag) { + fillCopyDiagnostics(collisions, mcparticles, diag, fate); + } + + // stored cross-leg fakes by collisions + std::map, std::set> storedCrossColl; + for (int i = 0; i < nCand; ++i) { + if (cls[i] == kV0CrossLegFake && storedSet.contains(key[i])) { + storedCrossColl[{motherPos[i], motherEle[i]}].insert(key[i].collisionId); + } + } + + // truth pairs, grouped by MC collision std::map> mothersByMcColl; for (const auto& [mid, f] : fate) { mothersByMcColl[mcparticles.iteratorAt(mid).mcCollisionId()].push_back(mid); @@ -1718,6 +4340,21 @@ struct PhotonConversionBuilder { } continue; } + { // the pair stored as the crossed pairing (A+B-, B+A-) in one collision or one crossed pair only + const auto itAB = storedCrossColl.find({mids[a], mids[b]}); + const auto itBA = storedCrossColl.find({mids[b], mids[a]}); + bool bothCrossed = false; + if (itAB != storedCrossColl.end() && itBA != storedCrossColl.end()) { + for (const auto& c : itAB->second) { + bothCrossed = bothCrossed || itBA->second.contains(c); + } + } + if (bothCrossed) { + registry.fill(HIST("MCDedup/Pairs/surviveAsCrossedPairing/hQ"), qTrue); + } else if (itAB != storedCrossColl.end() || itBA != storedCrossColl.end()) { + registry.fill(HIST("MCDedup/Pairs/halfCrossedStored/hQ"), qTrue); + } + } if (nLost == 1) { fillTruthPair(kOneLost, qTrue, dEta); } else { @@ -1767,8 +4404,268 @@ struct PhotonConversionBuilder { } } } + + { + struct Elig { + int64_t pos = -1, ele = -1; + int nIts = 0; + float vx = 0.f, vy = 0.f; + }; + std::map elig; + for (const auto& track : tracks) { + if (track.pt() < trackcuts.min_pt_trackiu || !checkV0leg(track)) { + continue; + } + const int64_t mid = getPhotonMotherId(track, mcparticles); + if (mid < 0) { + continue; + } + auto& e = elig[mid]; + if (track.sign() > 0) { + e.pos = track.globalIndex(); + } else { + e.ele = track.globalIndex(); + } + if (track.hasITS()) { + e.nIts++; + } + const auto mc = track.template mcParticle_as(); + e.vx = mc.vx(); + e.vy = mc.vy(); + } + std::map crossedStoredOfPhoton; + std::map, std::pair> crossedOfPair; // (offered, stored) + std::map, std::vector> crossedOriginOfPair; // stored crossed: 0 SVertexer, 1 finder, 2 swap resolver + std::map, std::vector> crossedIndexOfPair; // stored crossed: candidate index + std::map, const SwapRecord*> swapRecordOf; + for (const auto& r : diag.swaps) { + swapRecordOf[std::minmax(r.keyI, r.keyJ)] = &r; + } + for (int i = 0; i < nCand; ++i) { + if (cls[i] != kV0CrossLegFake) { + continue; + } + auto& cp = crossedOfPair[std::minmax(motherPos[i], motherEle[i])]; + cp.first++; + if (storedSet.contains(key[i])) { + cp.second++; + crossedOriginOfPair[std::minmax(motherPos[i], motherEle[i])].push_back(key[i].v0Id >= 0 ? 0 : (diag.finderKeys.contains(key[i]) ? 1 : 2)); + crossedIndexOfPair[std::minmax(motherPos[i], motherEle[i])].push_back(i); + crossedStoredOfPhoton[motherPos[i]]++; + crossedStoredOfPhoton[motherEle[i]]++; + } + } + std::map> eligByMcColl; + for (const auto& [mid, e] : elig) { + if (e.pos >= 0 && e.ele >= 0) { + eligByMcColl[mcparticles.iteratorAt(mid).mcCollisionId()].push_back(mid); + } + } + auto history = [&](int64_t mid, int& detail) { // run-1 history of one true photon, see MCDedup/Inventory/hSwapOrigin + detail = -10; + if (fate.contains(mid)) { + return 0; + } + const auto& e = elig.at(mid); + const auto it = diag.trueLegTrace.find({e.pos, e.ele}); + const TrueLegTrace t = it == diag.trueLegTrace.end() ? TrueLegTrace{} : it->second; + if (t.svLine > 0) { + detail = t.svLine; + return 1; + } + if (t.svLine == 0) { + return 6; // o2-linter: disable=magic-number (history codes) + } + detail = t.finderStep; + if (e.nIts == 2 || t.finderStep == -8) { // o2-linter: disable=magic-number (both legs with ITS) + detail = -8; // o2-linter: disable=magic-number (trace code) + return 2; + } + if (t.finderStep == FinderStep::kFindCollision) { + detail = t.finderLine; + return t.finderLine > 0 ? 5 : 6; // o2-linter: disable=magic-number (history codes) + } + return t.finderStep >= -1 ? 4 : 3; // o2-linter: disable=magic-number (history codes) + }; + auto photonFate = [&](int64_t mid) { // 0 no true candidate, 1 candidate lost, 2 stored, 3 lost but a leg in a stored crossed candidate + const auto it = fate.find(mid); + const bool inCrossed = crossedStoredOfPhoton.contains(mid); + if (it == fate.end()) { + return inCrossed ? 3 : 0; + } + if (it->second.alive) { + return 2; + } + return inCrossed ? 3 : 1; + }; + for (const auto& [mcCol, mids] : eligByMcColl) { + std::vector closest(mids.size(), 0.0999f); + for (size_t a = 0; a < mids.size(); ++a) { + const auto ga = mcparticles.iteratorAt(mids[a]); + for (size_t b = a + 1; b < mids.size(); ++b) { + const auto gb = mcparticles.iteratorAt(mids[b]); + const float qTrue = qInv(ga.px(), ga.py(), ga.pz(), gb.px(), gb.py(), gb.pz()); + closest[a] = std::min(closest[a], qTrue); + closest[b] = std::min(closest[b], qTrue); + if (qTrue >= 0.1f) { + continue; + } + const float e1 = ga.p(), e2 = gb.p(); + const float asym = (e1 + e2) > 0.f ? std::fabs(e1 - e2) / (e1 + e2) : 0.f; + const float kt = 0.5f * std::hypot(ga.px() + gb.px(), ga.py() + gb.py()); + const int fa = photonFate(mids[a]), fb = photonFate(mids[b]); + const auto itx = crossedOfPair.find(std::minmax(mids[a], mids[b])); + const int nCrossOff = itx == crossedOfPair.end() ? 0 : std::min(itx->second.first, 2); + const int nCrossSt = itx == crossedOfPair.end() ? 0 : itx->second.second; + const int nStoredTrue = (fa == 2 ? 1 : 0) + (fb == 2 ? 1 : 0); + int pairFate = 5; + if (nStoredTrue == 2) { + pairFate = 0; + } else if (nStoredTrue == 1 && nCrossSt == 0) { + pairFate = 1; + } else if (nStoredTrue == 0 && nCrossSt == 0) { + pairFate = 2; + } else if (nStoredTrue == 0 && nCrossSt == 2) { + pairFate = 3; + } else if (nCrossSt == 1) { + pairFate = 4; + } + const int nTrueCand = (fate.contains(mids[a]) ? 1 : 0) + (fate.contains(mids[b]) ? 1 : 0); + registry.fill(HIST("MCDedup/Inventory/hPair"), qTrue, std::min(asym, 0.999f), std::min(kt, 1.499f), static_cast(pairFate), static_cast(nTrueCand), static_cast(nCrossOff)); + int detA = -10, detB = -10; + int hA = history(mids[a], detA), hB = history(mids[b], detB); + if (qTrue < 0.02f) { // o2-linter: disable=magic-number (close pairs) + if (hA > 0) { + registry.fill(HIST("MCDedup/Inventory/hNeverDetail"), static_cast(pairFate), static_cast(hA), static_cast(detA)); + } + if (hB > 0) { + registry.fill(HIST("MCDedup/Inventory/hNeverDetail"), static_cast(pairFate), static_cast(hB), static_cast(detB)); + } + } + if (pairFate == 3 || pairFate == 4) { // o2-linter: disable=magic-number (swap, one crossed stored) + auto o = crossedOriginOfPair.at(std::minmax(mids[a], mids[b])); + const int o1 = std::min(o[0], o.size() > 1 ? o[1] : o[0]), o2 = std::max(o[0], o.size() > 1 ? o[1] : o[0]); + static constexpr std::array, 3> OriginCode{{{0, 1, 3}, {1, 2, 4}, {3, 4, 5}}}; + if (hA < hB) { + std::swap(hA, hB); + } + registry.fill(HIST("MCDedup/Inventory/hSwapOrigin"), qTrue, pairFate == 3 ? 0.f : 1.f, static_cast(nTrueCand), static_cast(OriginCode[o1][o2]), static_cast(hA), static_cast(hB)); + } + if (pairFate == 3) { // o2-linter: disable=magic-number (full swap): what the swap resolver did with the two stored crossed photons + const auto& ix = crossedIndexOfPair.at(std::minmax(mids[a], mids[b])); + const auto& x1 = diag.snapshot[ix[0]]; + const auto& x2 = diag.snapshot[ix.size() > 1 ? ix[1] : ix[0]]; + int outcome = 8; // o2-linter: disable=magic-number (outcome codes) + float gain = -1.999f; + int line = 0; + const float qRec = qInv(x1.pxAtPV, x1.pyAtPV, x1.pzAtPV, x2.pxAtPV, x2.pyAtPV, x2.pzAtPV); + if (x1.colID != x2.colID) { + outcome = 0; + } else if (!recovery.runSwapResolver.value) { + outcome = 8; // o2-linter: disable=magic-number (outcome codes) + } else if (qRec > recovery.swapMaxQ) { + outcome = 1; + } else if (std::hypot(x1.svX - x2.svX, x1.svY - x2.svY, x1.svZ - x2.svZ) > recovery.swapMaxConvDist) { + outcome = 2; + } else if (const auto it = swapRecordOf.find(std::minmax(keyOf(x1), keyOf(x2))); it != swapRecordOf.end()) { + const auto& r = *it->second; + if (r.complete) { + gain = std::clamp(-r.gain, -1.999f, 1.999f); // crossed (stored) minus true (alternative) + outcome = r.gain > 0.f ? 7 : 6; // o2-linter: disable=magic-number (outcome codes) + } else if ((r.fail & 3) != 0) { // o2-linter: disable=magic-number (bit mask) + outcome = 5; // o2-linter: disable=magic-number (outcome codes) + } else if ((r.fail & 48) != 0) { // o2-linter: disable=magic-number (bit mask) + outcome = 4; // o2-linter: disable=magic-number (outcome codes) + line = r.line; + } else { + outcome = 3; + } + } + registry.fill(HIST("MCDedup/Inventory/hSwapLoss"), qTrue, static_cast(nTrueCand), static_cast(outcome), gain); + if (outcome == 4 && qTrue < 0.02f) { // o2-linter: disable=magic-number (close pairs) + registry.fill(HIST("MCDedup/Inventory/hSwapLossCut"), static_cast(nTrueCand), static_cast(line)); + } + } + } + } + for (size_t a = 0; a < mids.size(); ++a) { + const auto ga = mcparticles.iteratorAt(mids[a]); + const auto& e = elig.at(mids[a]); + registry.fill(HIST("MCDedup/Inventory/hPhoton"), std::min(ga.p(), 1.999f), std::min(std::hypot(e.vx, e.vy), 89.9f), static_cast(e.nIts), static_cast(photonFate(mids[a])), closest[a]); + } + } + } + } + + template + void fillExtendedDiagnostics(TTracks const& tracks, TMCParticles const& mcparticles, DedupDiag const& diag) + { + struct Stored { + CandKey key; + int cls = 0; + int64_t mPos = -1; + int64_t mEle = -1; + std::array p{}; + }; + std::map candOfKey; + std::set> candLegs; // (collision, pos, ele) of all dedup candidates + for (const auto& c : diag.snapshot) { + candOfKey[keyOf(c)] = &c; + candLegs.emplace(c.colID, c.posID, c.eleID); + } + std::map> storedOfColl; + for (const auto& key : diag.stored) { + const auto it = candOfKey.find(key); + if (it == candOfKey.end()) { + continue; + } + Stored s{key, 0, -1, -1, {it->second->pxAtPV, it->second->pyAtPV, it->second->pzAtPV}}; + s.cls = classifyV0Truth(tracks.rawIteratorAt(key.posId), tracks.rawIteratorAt(key.eleId), mcparticles, s.mPos, s.mEle); + const float pt = std::hypot(s.p[0], s.p[1]); + registry.fill(HIST("MCDedup/Stored/hClassSource"), static_cast(s.cls), key.v0Id < 0 ? 1.f : 0.f, pt); + storedOfColl[key.collisionId].push_back(s); + } + constexpr float MaxQ = 0.1f; + for (const auto& [col, photons] : storedOfColl) { + for (size_t i = 0; i < photons.size(); ++i) { + for (size_t j = i + 1; j < photons.size(); ++j) { + const auto& a = photons[i]; + const auto& b = photons[j]; + const float q = qInv(a.p[0], a.p[1], a.p[2], b.p[0], b.p[1], b.p[2]); + if (q > MaxQ) { + continue; + } + int cat = 2; + if (a.cls == kV0True && b.cls == kV0True && a.mPos != b.mPos) { + cat = 0; + } else if (a.cls == kV0CrossLegFake && b.cls == kV0CrossLegFake && a.mPos == b.mEle && a.mEle == b.mPos) { + cat = 1; + const int nTrue = static_cast(candLegs.contains({col, a.key.posId, b.key.eleId})) + static_cast(candLegs.contains({col, b.key.posId, a.key.eleId})); + registry.fill(HIST("MCDedup/Stored/hSwapTrueCands"), static_cast(nTrue), q); + } + registry.fill(HIST("MCDedup/Stored/hPairClass"), static_cast(cat), q); + } + } + } + for (const auto& s : diag.swaps) { + const std::array, 4> legs = {{{s.keyI.posId, s.keyI.eleId}, {s.keyJ.posId, s.keyJ.eleId}, {s.keyI.posId, s.keyJ.eleId}, {s.keyJ.posId, s.keyI.eleId}}}; + std::array isTrue{}; + for (int k = 0; k < 4; ++k) { // o2-linter: disable=magic-number (four leg pairs) + int64_t mPos = -1, mEle = -1; + isTrue[k] = classifyV0Truth(tracks.rawIteratorAt(legs[k].first), tracks.rawIteratorAt(legs[k].second), mcparticles, mPos, mEle) == kV0True ? 1 : 0; + } + registry.fill(HIST("MCDedup/Swap/hCategory"), static_cast(3 * (isTrue[0] + isTrue[1]) + isTrue[2] + isTrue[3]), s.swapped ? 1.f : 0.f, s.q); + int outcome = 3; // o2-linter: disable=magic-number (outcome codes) + if (s.swapped) { + outcome = 4; // o2-linter: disable=magic-number (outcome codes) + } else if (!s.complete) { + outcome = (s.fail & 3) != 0 ? 2 : ((s.fail & 48) != 0 ? 1 : 0); // o2-linter: disable=magic-number (bit mask) + } + registry.fill(HIST("MCDedup/Swap/hResolverTruth"), static_cast(isTrue[0] + isTrue[1]), static_cast(isTrue[2] + isTrue[3]), static_cast(outcome), s.complete ? std::clamp(s.gain, -1.999f, 1.999f) : -1.999f, std::min(s.q, 0.0999f)); + } } + // ===== process functions ===== void processRec(MyCollisions const& collisions, FilteredV0s const& v0s, MyTracksIU const& tracks, MyBCs const& bcs) { build(collisions, v0s, tracks, bcs); @@ -1786,7 +4683,10 @@ struct PhotonConversionBuilder { { DedupDiag diag; build(collisions, v0s, tracks, bcs, &diag); - fillDedupTruthDiagnostics(tracks, mcparticles, diag); + fillDedupTruthDiagnostics(collisions, tracks, mcparticles, diag); + if (dedup.cfgExtendedDiag) { + fillExtendedDiagnostics(tracks, mcparticles, diag); + } } PROCESS_SWITCH(PhotonConversionBuilder, processMC, "process reconstructed info for MC", false); @@ -1796,6 +4696,45 @@ struct PhotonConversionBuilder { } PROCESS_SWITCH(PhotonConversionBuilder, processRec_OnlyIfDielectron, "process reconstructed info for data", false); + using MixCollisionsIn = std::conditional_t; + using MixLegsIn = std::conditional_t; + std::vector().rawIteratorAt(0))> mLegIt; // run 2: one iterator per PCMMixLegs row of the DF (buildMixed) + void processMixed(MixCollisionsIn const& collisions, MixLegsIn const& legs, MyBCs const& bcs) + { + if constexpr (kMixedInstance) { + if (collisions.size() == 0 || bcs.size() == 0) { + return; + } + const auto bc = bcs.begin(); // B field / material of this DF's run + initCCDB(bc); + updateCCDB(bc); + buildMixed(collisions, legs); + } else { + LOG(fatal) << "processMixed only in photon-conversion-builder-mixed (o2-analysis-em-photon-conversion-builder-mixed)"; + } + } + PROCESS_SWITCH(PhotonConversionBuilder, processMixed, "builder run 2 of the co-reconstructed mixed event (input from photon-mixed-event-producer), own instance only", false); + + void processMixedDerived(MixCollisionsIn const& collisions, MixLegsIn const& legs) + { + if constexpr (kMixedInstance) { + if (collisions.size() == 0) { + return; + } + const auto event = collisions.begin(); // B field / material of this DF's run + const auto* grp = ccdb->template getForTimeStamp("GLO/Config/GRPMagField", event.timestamp()); + if (grp == nullptr) { + LOG(fatal) << "processMixedDerived: no GRPMagField for timestamp " << event.timestamp(); + } + initCCDBFor(event.runNumber(), event.timestamp(), *grp); + updateCCDBAt(event.timestamp()); + buildMixed(collisions, legs); + } else { + LOG(fatal) << "processMixedDerived only in photon-conversion-builder-mixed (o2-analysis-em-photon-conversion-builder-mixed)"; + } + } + PROCESS_SWITCH(PhotonConversionBuilder, processMixedDerived, "builder run 2 on derived data (input from photon-mixed-event-producer processDerived), own instance only", false); + // void processRec_SWT_OnlyIfDielectron(soa::Join const& collisions, FilteredV0s const& v0s, MyTracksIU const& tracks, MyBCs const& bcs) // { // build(collisions, v0s, tracks, bcs); diff --git a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilderMixed.cxx b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilderMixed.cxx new file mode 100644 index 00000000000..5514fb86c31 --- /dev/null +++ b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilderMixed.cxx @@ -0,0 +1,28 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// + +/// \file photonconversionbuilderMixed.cxx +/// \brief builder run 2 of the mixed event: rebuilds the photons of the artificial events of photon-mixed-event-producer +/// from their legs (set processRec = false, processMixed = true) +/// \author Stefanie Mrozinski + +#include "PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h" + +#include +#include +#include + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + return o2::framework::WorkflowSpec{ + adaptAnalysisTask>(context, o2::framework::TaskName{"photon-conversion-builder-mixed"})}; +} diff --git a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilderTmpTable.cxx b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilderTmpTable.cxx index 6b7927b6340..1c873c197ed 100644 --- a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilderTmpTable.cxx +++ b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilderTmpTable.cxx @@ -16,8 +16,12 @@ #include "PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h" +#include +#include +#include + o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) { return o2::framework::WorkflowSpec{ - adaptAnalysisTask>(context, o2::framework::TaskName{"photon-conversion-builder-tmptable"})}; + adaptAnalysisTask>(context, o2::framework::TaskName{"photon-conversion-builder-tmptable"})}; } diff --git a/PWGEM/PhotonMeson/TableProducer/skimmerDalitzEvents.cxx b/PWGEM/PhotonMeson/TableProducer/skimmerDalitzEvents.cxx index e038b916848..4d5bcf13a00 100644 --- a/PWGEM/PhotonMeson/TableProducer/skimmerDalitzEvents.cxx +++ b/PWGEM/PhotonMeson/TableProducer/skimmerDalitzEvents.cxx @@ -59,6 +59,7 @@ struct skimmerDalitzEvents { Configurable minNElecCand{"minNElecCand", 2, "minimum number of electrons/positron candidates in one event"}; Configurable minNGammaCand{"minNGammaCand", 1, "minimum number of V0 photon candidates in one event"}; + Configurable accPCMGGAndDalitz{"accPCMGGAndDalitz", false, "set to true if you want to create DD for both PCM-PCM and PCM-Dalitz"}; // ---------- for data ---------- template @@ -71,7 +72,7 @@ struct skimmerDalitzEvents { auto tracks = emPrimaryElecTmp.sliceBy(perCollisionEl, collision.globalIndex()); // check that there are at least minNElecCand in the collision - if (tracks.size() < minNElecCand) { + if (!accPCMGGAndDalitz && tracks.size() < minNElecCand) { continue; } @@ -79,10 +80,16 @@ struct skimmerDalitzEvents { auto photonskf = v0photonskfTmp.sliceBy(perCol_pcm, collision.globalIndex()); // check that there are at least minNGammaCand in the collision - if (photonskf.size() < minNGammaCand) { + if (!accPCMGGAndDalitz && photonskf.size() < minNGammaCand) { continue; } + if (accPCMGGAndDalitz) { + if (!(photonskf.size() >= 2 || (tracks.size() >= 2 && photonskf.size() >= 1))) { // o2-linter: disable=magic-number (settings to obtain either two photons in the event or one photon with one dielectron) + continue; + } + } + // Write electron tracks for (const auto& track : tracks) { emprimaryelectrons(track.collisionId(), track.trackId(), track.sign(), diff --git a/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.cxx b/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.cxx index 97fe86ece34..1efd61d1ab7 100644 --- a/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.cxx +++ b/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.cxx @@ -15,6 +15,12 @@ #include "PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h" +#include "PID/PIDTOFParamService.h" + +#include +#include +#include + using namespace o2::framework; using namespace o2::aod; using namespace o2::framework::expressions; diff --git a/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h b/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h index ed671564446..148f1667dd5 100644 --- a/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h +++ b/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include @@ -49,6 +48,8 @@ #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include +#include +#include #include #include @@ -61,12 +62,7 @@ #include #include -// using namespace o2; -// using namespace o2::soa; -// using namespace o2::framework; -// using namespace o2::framework::expressions; -// using namespace o2::constants::physics; - +// NOLINTBEGIN(google-global-names-in-headers) using o2::framework::AxisSpec; using o2::framework::Configurable; using o2::framework::ConfigurableGroup; @@ -77,6 +73,7 @@ using o2::framework::kTH2F; using o2::framework::kTHnSparseF; using o2::framework::Preslice; using o2::framework::Produces; +// NOLINTEND(google-global-names-in-headers) using MyCollisions = o2::soa::Join; using MyCollisionsWithSWT = o2::soa::Join; @@ -126,6 +123,33 @@ struct skimmerPrimaryElectronFromDalitzEE { EpEmPairsAndPhoton = 3 }; + enum RejectReason { + kAllTracks = 0, + kPassed, + kNoMcParticle, + kNotTrueElectron, + kTOFRequired, + kNoITS, + kITSChi2, + kITSNCls, + kITSNClsInnerBarrel, + kNoTPC, + kTPCChi2, + kTPCNClsFound, + kTPCCrossedRows, + kTPCCrossedRowsOverFindable, + kTPCSharedFraction, + kPropagation, + kDCAxyz, + kDCA3D, + kPtEta, + kITSClusterSize, + kITSsaPt, + kNotElectron, + kNRejectReasons + }; + static constexpr std::array RejectReasonLabels = {"all tracks", "accepted tracks", "no MC particle", "not true electron", "TOF required", "no ITS", "ITS chi2", "ITS Ncls", "ITS Ncls IB", "no TPC", "TPC chi2", "TPC Ncls found", "TPC crossed rows", "TPC Ncr/Nf", "TPC shared frac", "propagation", "DCAxy/z", "DCA 3D", "pt/eta", "ITS cluster size", "ITSsa pt", "not electron"}; + // Configurables Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; @@ -177,9 +201,11 @@ struct skimmerPrimaryElectronFromDalitzEE { Configurable intercept{"intercept", -0.0380, "intercept for m vs. phiv"}; Configurable useTOFNSigmaDeltaBC{"useTOFNSigmaDeltaBC", false, "Flag to shift delta BC for TOF n sigma (only with TTCA)"}; Configurable storeOnlyTrueElectronMC{"storeOnlyTrueElectronMC", false, "Flag to store only true electron in MC"}; + Configurable fillHistogramsBeforeSelection{"fillHistogramsBeforeSelection", false, "Flag to fill track histograms before track selections (only for fillingMode 1)"}; o2::framework::HistogramRegistry fRegistry{"output", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject, false, false}; static constexpr std::array DileptonSigns = {"uls/", "lspp/", "lsmm/"}; + static constexpr std::array TrackDirs = {"Track/", "TrackTrueElec/", "TrackNoSel/", "TrackNoSelTrueElec/"}; int mRunNumber = 0; float dBz = 0.; @@ -242,6 +268,24 @@ struct skimmerPrimaryElectronFromDalitzEE { fRegistry.add("Track/hTOFNsigmaEl", "TOF n sigma el;p_{pv} (GeV/c);n #sigma_{e}^{TOF}", kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); fRegistry.add("Track/hTOFNsigmaPi", "TOF n sigma pi;p_{pv} (GeV/c);n #sigma_{#pi}^{TOF}", kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); + fRegistry.add("Track/hTrackRejectReason", "track rejection reason vs. p_{T};;p_{T} (GeV/c)", kTH2F, {{static_cast(kNRejectReasons), -0.5, static_cast(kNRejectReasons) - 0.5}, {200, 0, 20}}, false); + auto hRejectReason = fRegistry.get(HIST("Track/hTrackRejectReason")); + for (int i = 0; i < kNRejectReasons; i++) { + hRejectReason->GetXaxis()->SetBinLabel(i + 1, RejectReasonLabels[i]); + } + + if (doprocessMC) { + LOG(info) << "adding TrackTrueElec folder"; + fRegistry.addClone("Track/", "TrackTrueElec/"); + } + + if (fillHistogramsBeforeSelection) { + fRegistry.addClone("Track/", "TrackNoSel/"); + if (doprocessMC) { + fRegistry.addClone("Track/", "TrackNoSelTrueElec/"); + } + } + // pair fRegistry.add("Pair/uls/hTrackMvsPt", "m_{ee} vs. p_{T,ee};m_{ee} (GeV/c^{2});p_{T,ee} (GeV/c)", kTH2F, {{100, 0, 0.1}, {200, 0, 2}}, false); fRegistry.add("Pair/uls/hCheckEMvsPt", "m_{ee} vs. p_{T,ee};m_{ee} (GeV/c^{2});p_{T,ee} (GeV/c)", kTH2F, {{100, 0, 0.1}, {200, 0, 2}}, false); @@ -359,63 +403,72 @@ struct skimmerPrimaryElectronFromDalitzEE { } template - bool checkTrack(TCollision const& collision, TTrack const& track) + bool checkTrack(TCollision const& collision, TTrack const& track, int* rejectReason = nullptr) { + // Lambda function: Sets the reject reason to the correct value and returns false. + // Used for every return statement in the checkTrack function where false is returned + auto reject = [rejectReason](RejectReason reason) { + if (rejectReason != nullptr) { + *rejectReason = reason; + } + return false; + }; + if constexpr (isMC) { if (!track.has_mcParticle()) { - return false; + return reject(kNoMcParticle); } if (storeOnlyTrueElectronMC) { const auto& mcParticle = track.template mcParticle_as(); - if (std::abs(mcParticle.pdgCode()) != 11) { - return false; + if (std::abs(mcParticle.pdgCode()) != PDG_t::kElectron) { + return reject(kNotTrueElectron); } } } float tofNSigmaEl = mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]; if (requireTOF && !(track.hasTOF() && std::fabs(tofNSigmaEl) < maxTOFNsigmaEl)) { - return false; + return reject(kTOFRequired); } if (!track.hasITS()) { - return false; + return reject(kNoITS); } if (track.itsChi2NCl() < minchi2its || maxchi2its < track.itsChi2NCl()) { - return false; + return reject(kITSChi2); } if (track.itsNCls() < min_ncluster_its) { - return false; + return reject(kITSNCls); } if (track.itsNClsInnerBarrel() < min_ncluster_itsib) { - return false; + return reject(kITSNClsInnerBarrel); } if (!includeITSsa && !track.hasTPC()) { - return false; + return reject(kNoTPC); } if (track.hasTPC()) { if (track.tpcChi2NCl() < minchi2tpc || maxchi2tpc < track.tpcChi2NCl()) { - return false; + return reject(kTPCChi2); } if (track.tpcNClsFound() < min_ncluster_tpc) { - return false; + return reject(kTPCNClsFound); } if (track.tpcNClsCrossedRows() < mincrossedrows) { - return false; + return reject(kTPCCrossedRows); } if (track.tpcCrossedRowsOverFindableCls() < min_tpc_cr_findable_ratio) { - return false; + return reject(kTPCCrossedRowsOverFindable); } if (track.tpcFractionSharedCls() > max_frac_shared_clusters_tpc) { - return false; + return reject(kTPCSharedFraction); } } @@ -427,13 +480,13 @@ struct skimmerPrimaryElectronFromDalitzEE { mVtx.setCov(collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()); bool isPropOK = o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, trackParCov, 2.f, matCorr, &mDcaInfoCov); if (!isPropOK) { - return false; + return reject(kPropagation); } float dcaXY = mDcaInfoCov.getY(); float dcaZ = mDcaInfoCov.getZ(); if (std::fabs(dcaXY) > dca_xy_max || std::fabs(dcaZ) > dca_z_max) { - return false; + return reject(kDCAxyz); } float dca3D = 999.f; @@ -445,11 +498,11 @@ struct skimmerPrimaryElectronFromDalitzEE { dca3D = std::sqrt(std::fabs(chi2) / 2.); } if (dca3D > dca_3d_sigma_max) { - return false; + return reject(kDCA3D); } if (trackParCov.getPt() < minpt || std::fabs(trackParCov.getEta()) > maxeta) { - return false; + return reject(kPtEta); } int total_cluster_size = 0, nl = 0; @@ -462,11 +515,11 @@ struct skimmerPrimaryElectronFromDalitzEE { } if (maxMeanITSClusterSize < static_cast(total_cluster_size) / static_cast(nl) * std::cos(std::atan(trackParCov.getTgl()))) { - return false; + return reject(kITSClusterSize); } if ((track.hasITS() && !track.hasTPC() && !track.hasTRD() && !track.hasTOF()) && maxpt_itssa < track.pt()) { - return false; + return reject(kITSsaPt); } // if ((track.hasITS() && !track.hasTPC() && !track.hasTRD() && !track.hasTOF()) && maxpt_itssa < track.pt()) { @@ -533,11 +586,24 @@ struct skimmerPrimaryElectronFromDalitzEE { { for (const auto& track : tracks) { - if (!checkTrack(collision, track)) { - continue; + + if (fillHistogramsBeforeSelection) { + fillTrackHistograms(track); } - if (!isElectron(collision, track)) { + fRegistry.fill(HIST("Track/hTrackRejectReason"), kAllTracks, track.pt()); + int rejectReason = kPassed; + if (checkTrack(collision, track, &rejectReason) && !isElectron(collision, track)) { + rejectReason = kNotElectron; + } + fRegistry.fill(HIST("Track/hTrackRejectReason"), rejectReason, track.pt()); + if constexpr (isMC) { + if (isTruePrimaryElectron(track)) { + fRegistry.fill(HIST("TrackTrueElec/hTrackRejectReason"), kAllTracks, track.pt()); + fRegistry.fill(HIST("TrackTrueElec/hTrackRejectReason"), rejectReason, track.pt()); + } + } + if (rejectReason != kPassed) { continue; } @@ -549,7 +615,16 @@ struct skimmerPrimaryElectronFromDalitzEE { template void fillPairInfo(TCollision const& collision, TTracks1 const& tracks1, TTracks2 const& tracks2) { - if constexpr (pairtype == 0) { // ULS + if constexpr (pairtype == 0) { // ULS + if (fillHistogramsBeforeSelection) { // we cannot fill the track histograms in the pair loop as they would get filled multiple times. Hence we loop over all tracks here + for (const auto& track : tracks1) { + fillTrackHistograms(track); + } + for (const auto& track : tracks2) { + fillTrackHistograms(track); + } + } + for (const auto& [t1, t2] : combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { if (!checkTrack(collision, t1) || !checkTrack(collision, t2)) { @@ -626,44 +701,66 @@ struct skimmerPrimaryElectronFromDalitzEE { } } - template + template + bool isTruePrimaryElectron(TTrack const& track) + { + if (!track.has_mcParticle()) { + return false; + } + const auto mcParticle = track.template mcParticle_as(); + return std::abs(mcParticle.pdgCode()) == PDG_t::kElectron && mcParticle.isPhysicalPrimary(); // o2-linter: disable=pdg/explicit-code (electron) + } + + template void fillTrackHistograms(TTrack const& track) + { + constexpr int kDirAllTracks = beforeSelection ? 2 : 0; + fillTrackHistogramsImpl(track); + if constexpr (isMC) { + if (isTruePrimaryElectron(track)) { + fillTrackHistogramsImpl(track); + } + } + } + + template + void fillTrackHistogramsImpl(TTrack const& track) { float mcTunedTPCSignal = 0.f; if constexpr (isMC) { mcTunedTPCSignal = track.mcTunedTPCSignal(); } - fRegistry.fill(HIST("Track/hPt"), track.pt()); - fRegistry.fill(HIST("Track/hEtaPhi"), track.phi(), track.eta()); - fRegistry.fill(HIST("Track/hQoverPt"), track.sign() / track.pt()); - fRegistry.fill(HIST("Track/hRelDeltaPt"), track.pt(), track.sigma1Pt() * track.pt()); - fRegistry.fill(HIST("Track/hDCAxyz"), track.dcaXY(), track.dcaZ()); - fRegistry.fill(HIST("Track/hDCAxy_Pt"), track.pt(), track.dcaXY()); - fRegistry.fill(HIST("Track/hDCAz_Pt"), track.pt(), track.dcaZ()); - fRegistry.fill(HIST("Track/hDCAxyzSigma"), track.dcaXY() / std::sqrt(track.cYY()), track.dcaZ() / std::sqrt(track.cZZ())); - fRegistry.fill(HIST("Track/hDCAxyRes_Pt"), track.pt(), std::sqrt(track.cYY()) * 1e+4); // convert cm to um - fRegistry.fill(HIST("Track/hDCAzRes_Pt"), track.pt(), std::sqrt(track.cZZ()) * 1e+4); // convert cm to um - - fRegistry.fill(HIST("Track/hNclsTPC"), track.tpcNClsFound()); - fRegistry.fill(HIST("Track/hNcrTPC"), track.tpcNClsCrossedRows()); - fRegistry.fill(HIST("Track/hChi2TPC"), track.tpcChi2NCl()); - fRegistry.fill(HIST("Track/hTPCNcr2Nf"), track.tpcCrossedRowsOverFindableCls()); - fRegistry.fill(HIST("Track/hTPCNcls2Nf"), track.tpcFoundOverFindableCls()); - fRegistry.fill(HIST("Track/hTPCNclsShared"), track.pt(), track.tpcFractionSharedCls()); - fRegistry.fill(HIST("Track/hTPCdEdx"), track.tpcInnerParam(), track.tpcSignal()); - fRegistry.fill(HIST("Track/hTPCdEdxMC"), track.tpcInnerParam(), mcTunedTPCSignal); - fRegistry.fill(HIST("Track/hTPCNsigmaEl"), track.tpcInnerParam(), track.tpcNSigmaEl()); - fRegistry.fill(HIST("Track/hTPCNsigmaPi"), track.tpcInnerParam(), track.tpcNSigmaPi()); - - fRegistry.fill(HIST("Track/hChi2TOF"), track.tofChi2()); - fRegistry.fill(HIST("Track/hTOFbeta"), track.p(), track.beta()); - fRegistry.fill(HIST("Track/hTOFNsigmaEl"), track.p(), track.tofNSigmaEl()); - fRegistry.fill(HIST("Track/hTOFNsigmaPi"), track.p(), track.tofNSigmaPi()); - - fRegistry.fill(HIST("Track/hNclsITS"), track.itsNCls()); - fRegistry.fill(HIST("Track/hChi2ITS"), track.itsChi2NCl()); - fRegistry.fill(HIST("Track/hITSClusterMap"), track.itsClusterMap()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hPt"), track.pt()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hEtaPhi"), track.phi(), track.eta()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hQoverPt"), track.sign() / track.pt()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hRelDeltaPt"), track.pt(), track.sigma1Pt() * track.pt()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hDCAxyz"), track.dcaXY(), track.dcaZ()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hDCAxy_Pt"), track.pt(), track.dcaXY()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hDCAz_Pt"), track.pt(), track.dcaZ()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hDCAxyzSigma"), track.dcaXY() / std::sqrt(track.cYY()), track.dcaZ() / std::sqrt(track.cZZ())); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hDCAxyRes_Pt"), track.pt(), std::sqrt(track.cYY()) * 1e+4); // convert cm to um + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hDCAzRes_Pt"), track.pt(), std::sqrt(track.cZZ()) * 1e+4); // convert cm to um + + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hNclsTPC"), track.tpcNClsFound()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hNcrTPC"), track.tpcNClsCrossedRows()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hChi2TPC"), track.tpcChi2NCl()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hTPCNcr2Nf"), track.tpcCrossedRowsOverFindableCls()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hTPCNcls2Nf"), track.tpcFoundOverFindableCls()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hTPCNclsShared"), track.pt(), track.tpcFractionSharedCls()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hTPCdEdx"), track.tpcInnerParam(), track.tpcSignal()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hTPCdEdxMC"), track.tpcInnerParam(), mcTunedTPCSignal); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hTPCNsigmaEl"), track.tpcInnerParam(), track.tpcNSigmaEl()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hTPCNsigmaPi"), track.tpcInnerParam(), track.tpcNSigmaPi()); + + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hChi2TOF"), track.tofChi2()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hTOFbeta"), track.p(), track.beta()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hTOFNsigmaEl"), track.p(), track.tofNSigmaEl()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hTOFNsigmaPi"), track.p(), track.tofNSigmaPi()); + + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hNclsITS"), track.itsNCls()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hChi2ITS"), track.itsChi2NCl()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hITSClusterMap"), track.itsClusterMap()); int totalClusterSize = 0, nl = 0; for (unsigned int layer = 0; layer < 7; layer++) { // o2-linter: disable=magic-number (number of ITS layers) @@ -691,9 +788,9 @@ struct skimmerPrimaryElectronFromDalitzEE { } totalClusterSizeOb += clusterSizePerLayer; } - fRegistry.fill(HIST("Track/hMeanClusterSizeITS"), track.p(), static_cast(totalClusterSize) / static_cast(nl) * std::cos(std::atan(track.tgl()))); - fRegistry.fill(HIST("Track/hMeanClusterSizeITSib"), track.p(), static_cast(totalClusterSizeIb) / static_cast(nl_ib) * std::cos(std::atan(track.tgl()))); - fRegistry.fill(HIST("Track/hMeanClusterSizeITSob"), track.p(), static_cast(totalClusterSizeOb) / static_cast(nlOb) * std::cos(std::atan(track.tgl()))); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hMeanClusterSizeITS"), track.p(), static_cast(totalClusterSize) / static_cast(nl) * std::cos(std::atan(track.tgl()))); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hMeanClusterSizeITSib"), track.p(), static_cast(totalClusterSizeIb) / static_cast(nl_ib) * std::cos(std::atan(track.tgl()))); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hMeanClusterSizeITSob"), track.p(), static_cast(totalClusterSizeOb) / static_cast(nlOb) * std::cos(std::atan(track.tgl()))); } template diff --git a/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEETmpTable.cxx b/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEETmpTable.cxx index 4587e8d6975..32ba2e5f287 100644 --- a/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEETmpTable.cxx +++ b/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEETmpTable.cxx @@ -15,6 +15,12 @@ #include "PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h" +#include "PID/PIDTOFParamService.h" + +#include +#include +#include + using namespace o2::framework; using namespace o2::aod; using namespace o2::framework::expressions; diff --git a/PWGEM/PhotonMeson/Tasks/CMakeLists.txt b/PWGEM/PhotonMeson/Tasks/CMakeLists.txt index 729346bab9e..850a578b3e4 100644 --- a/PWGEM/PhotonMeson/Tasks/CMakeLists.txt +++ b/PWGEM/PhotonMeson/Tasks/CMakeLists.txt @@ -116,6 +116,11 @@ o2physics_add_dpl_workflow(pi0eta-to-gammagamma-mc-emcemc PUBLIC_LINK_LIBRARIES O2::Framework O2::EMCALBase O2::EMCALCalib O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(pi0eta-to-gammagamma-mc-pcmemc + SOURCES Pi0EtaToGammaGammaMCPCMEMC.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::EMCALBase O2::EMCALCalib O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(tag-and-probe SOURCES TagAndProbe.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::EMCALBase O2::EMCALCalib O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore O2Physics::MLCore @@ -176,6 +181,11 @@ o2physics_add_dpl_workflow(photon-reso-task PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::EMCALBase O2::EMCALCalib O2Physics::PWGEMPhotonMesonCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(photon-mc-task + SOURCES photonMcTask.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::EMCALBase O2::EMCALCalib O2Physics::PWGEMPhotonMesonCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(photonhbt SOURCES photonhbt.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore @@ -205,3 +215,18 @@ o2physics_add_dpl_workflow(pair-qc-task SOURCES pairQcTask.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(two-photon-mc-task + SOURCES twoPhotonMcTask.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(dilepton-effi-for-dalitz-skimmer + SOURCES dileptonEffiForDalitzSkimmer.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(meson-signal-loss + SOURCES mesonSignalLoss.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaMCPCMEMC.cxx b/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaMCPCMEMC.cxx new file mode 100644 index 00000000000..6bd33481ce2 --- /dev/null +++ b/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaMCPCMEMC.cxx @@ -0,0 +1,39 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file Pi0EtaToGammaGammaMCPCMEMC.cxx +/// \brief This code loops over photons and makes pairs for neutral mesons analyses in MC for PCM-EMC. +/// \author M. Hemmer, marvin.hemmer@cern.ch + +#include "PWGEM/PhotonMeson/Core/Pi0EtaToGammaGammaMC.h" +#include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" +#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" +#include "PWGEM/PhotonMeson/Utils/PairUtilities.h" + +#include +#include +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::framework; +using namespace o2::aod::pwgem::photonmeson::photonpair; + +using MyV0Photons = o2::soa::Filtered>; +using MyMCV0Legs = soa::Join; +using MyEMCClusters = soa::Join; + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{ + adaptAnalysisTask>(context, TaskName{"pi0eta-to-gammagamma-mc-pcmemc"}), + }; +} diff --git a/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaPCMEMC.cxx b/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaPCMEMC.cxx index 8b135111b22..b076a94d3db 100644 --- a/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaPCMEMC.cxx +++ b/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaPCMEMC.cxx @@ -14,6 +14,7 @@ /// \author M. Hemmer, marvin.hemmer@cern.ch #include "PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h" +#include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" #include "PWGEM/PhotonMeson/Utils/PairUtilities.h" @@ -27,8 +28,8 @@ using namespace o2::aod; using namespace o2::framework; using namespace o2::aod::pwgem::photonmeson::photonpair; -using MyV0Photons = Filtered>; -using MyEMCClusters = soa::Join; +using MyV0Photons = Filtered>; +using MyEMCClusters = soa::Join; WorkflowSpec defineDataProcessing(ConfigContext const& context) { diff --git a/PWGEM/PhotonMeson/Tasks/TagAndProbe.cxx b/PWGEM/PhotonMeson/Tasks/TagAndProbe.cxx index 6bb206de9dc..f43011ad92c 100644 --- a/PWGEM/PhotonMeson/Tasks/TagAndProbe.cxx +++ b/PWGEM/PhotonMeson/Tasks/TagAndProbe.cxx @@ -349,9 +349,9 @@ struct TagAndProbe { using BinningType_M = ColumnBinningPolicy; using BinningType_A = ColumnBinningPolicy; using BinningType_C = ColumnBinningPolicy; - BinningType_M colBinning_M{{ConfVtxBins, ConfCentBins}, true}; - BinningType_A colBinning_A{{ConfVtxBins, ConfCentBins}, true}; - BinningType_C colBinning_C{{ConfVtxBins, ConfCentBins}, true}; + BinningType_M colBinning_M{{ConfVtxBins, ConfCentBins}}; + BinningType_A colBinning_A{{ConfVtxBins, ConfCentBins}}; + BinningType_C colBinning_C{{ConfVtxBins, ConfCentBins}}; template void MixedEventPairing(TEvents const& collisions, TPhotons1 const& photons1, TPhotons2 const& photons2, TPreslice1 const& perCollision1, TPreslice2 const& perCollision2, TTagCut const& tagcut, TProbeCuts const& probecuts, TPairCuts const& paircuts, TLegs const& /*legs*/, TMixedBinning const& colBinning) diff --git a/PWGEM/PhotonMeson/Tasks/calibTaskEmc.cxx b/PWGEM/PhotonMeson/Tasks/calibTaskEmc.cxx index f6a6bad2c2e..6ae54d84dc1 100644 --- a/PWGEM/PhotonMeson/Tasks/calibTaskEmc.cxx +++ b/PWGEM/PhotonMeson/Tasks/calibTaskEmc.cxx @@ -20,7 +20,6 @@ #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" #include "PWGEM/PhotonMeson/Utils/emcalHistoDefinitions.h" #include "Common/Core/RecoDecay.h" @@ -72,16 +71,6 @@ using namespace o2::framework::expressions; using namespace o2::soa; using namespace o2::aod::pwgem::photon; -enum QvecEstimator { - FT0M = 0, - FT0A, - FT0C, - TPCPos, - TPCNeg, - TPCTot, - FV0A -}; - enum CentralityEstimator { None = 0, CFT0A, @@ -90,18 +79,6 @@ enum CentralityEstimator { NCentralityEstimators }; -enum Harmonics { - kNone = 0, - kDirect = 1, - kElliptic = 2, - kTriangluar = 3, - kQuadrangular = 4, - kPentagonal = 5, - kHexagonal = 6, - kHeptagonal = 7, - kOctagonal = 8 -}; - enum class MapLevel { kGood = 1, kNoBad = 2, @@ -294,6 +271,7 @@ struct CalibTaskEmc { fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + fEMEventCut.SetFT0COccupancyRange(eventcuts.cfgFT0COccupancyMin, eventcuts.cfgFT0COccupancyMax); } void defineEMCCut() @@ -363,7 +341,7 @@ struct CalibTaskEmc { fEMCCut.addQAHistograms(®istry); definePCMCut(); fV0PhotonCut.addQAHistograms(®istry); - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(®istry); + fEMEventCut.addEventHistograms(®istry); const AxisSpec thnAxisInvMass{thnConfigAxisInvMass, "#it{M}_{#gamma#gamma} (GeV/#it{c}^{2})"}; const AxisSpec thnAxisCent{thnConfigAxisCent, "Centrality (%)"}; @@ -587,25 +565,20 @@ struct CalibTaskEmc { bool isFullEventSelected(TCollision const& collision, bool fillHisto = false) { if (fillHisto) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(®istry, collision); + fEMEventCut.fillEventInfo<0>(®istry, collision); } if (!(fEMEventCut.IsSelected(collision))) { // general event selection return false; } - if (!(eventcuts.cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange() && collision.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { - // occupancy selection - return false; - } float cent = getCentrality(collision); if (cent < eventcuts.cfgMinCent || cent > eventcuts.cfgMaxCent) { // event selection return false; } if (fillHisto) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(®istry, collision); - registry.fill(HIST("Event/before/hCollisionCounter"), 12.0); // accepted - registry.fill(HIST("Event/after/hCollisionCounter"), 12.0); // accepted + fEMEventCut.fillEventInfo<1>(®istry, collision); + fEMEventCut.fillAccepted(®istry); } return true; } @@ -704,7 +677,7 @@ struct CalibTaskEmc { fEMCCut.AreSelectedRunning(flags, clusters, matchedPrims, matchedSeconds); using BinningType = ColumnBinningPolicy; - BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins}, true}; + BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins}}; auto clustersTuple = std::make_tuple(clusters); SameKindPair pair{binningMixedEvent, mixingConfig.cfgMixingDepth, -1, collisions, clustersTuple, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored @@ -714,10 +687,6 @@ struct CalibTaskEmc { // general event selection continue; } - if (!(eventcuts.cfgFT0COccupancyMin <= c1.ft0cOccupancyInTimeRange()) || !(c1.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax) || !(eventcuts.cfgFT0COccupancyMin <= c2.ft0cOccupancyInTimeRange()) || !(c2.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { - // occupancy selection - continue; - } if (getCentrality(c1) < eventcuts.cfgMinCent || getCentrality(c1) > eventcuts.cfgMaxCent || getCentrality(c2) < eventcuts.cfgMinCent || getCentrality(c2) > eventcuts.cfgMaxCent) { // event selection continue; @@ -866,7 +835,7 @@ struct CalibTaskEmc { return; } using BinningTypeMixed = ColumnBinningPolicy; - BinningTypeMixed binningOnPositions{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins}, true}; + BinningTypeMixed binningOnPositions{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins}}; auto associatedTables = std::make_tuple(clusters, pcmPhotons); @@ -887,10 +856,6 @@ struct CalibTaskEmc { // general event selection continue; } - if (!(eventcuts.cfgFT0COccupancyMin <= c1.ft0cOccupancyInTimeRange()) || !(c1.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax) || !(eventcuts.cfgFT0COccupancyMin <= c2.ft0cOccupancyInTimeRange()) || !(c2.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { - // occupancy selection - continue; - } if (getCentrality(c1) < eventcuts.cfgMinCent || getCentrality(c1) > eventcuts.cfgMaxCent || getCentrality(c2) < eventcuts.cfgMinCent || getCentrality(c2) > eventcuts.cfgMaxCent) { // event selection continue; @@ -1020,7 +985,7 @@ struct CalibTaskEmc { return; } using BinningType = ColumnBinningPolicy; - BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins}, true}; + BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins}}; auto pcmPhotonTuple = std::make_tuple(pcmPhotons); SameKindPair pair{binningMixedEvent, mixingConfig.cfgMixingDepth, -1, collisions, pcmPhotonTuple, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored @@ -1033,10 +998,6 @@ struct CalibTaskEmc { // general event selection continue; } - if (!(eventcuts.cfgFT0COccupancyMin <= c1.ft0cOccupancyInTimeRange()) || !(c1.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax) || !(eventcuts.cfgFT0COccupancyMin <= c2.ft0cOccupancyInTimeRange()) || !(c2.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { - // occupancy selection - continue; - } if (getCentrality(c1) < eventcuts.cfgMinCent || getCentrality(c1) > eventcuts.cfgMaxCent || getCentrality(c2) < eventcuts.cfgMinCent || getCentrality(c2) > eventcuts.cfgMaxCent) { // event selection continue; diff --git a/PWGEM/PhotonMeson/Tasks/dalitzEEQC.cxx b/PWGEM/PhotonMeson/Tasks/dalitzEEQC.cxx index 43f82e92041..c18b6e319a6 100644 --- a/PWGEM/PhotonMeson/Tasks/dalitzEEQC.cxx +++ b/PWGEM/PhotonMeson/Tasks/dalitzEEQC.cxx @@ -18,7 +18,6 @@ #include "PWGEM/PhotonMeson/Core/EMPhotonEventCut.h" #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/PIDResponseTPC.h" @@ -52,7 +51,6 @@ #include #include #include -#include #include using namespace o2; @@ -136,7 +134,7 @@ struct DalitzEEQC { float d_bz = 0; HistogramRegistry fRegistry{"output", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; - static constexpr std::array event_cut_types = {"before/", "after/"}; + // static constexpr std::array event_cut_types = {"before/", "after/"}; // unused void init(InitContext& /*context*/) { @@ -198,7 +196,7 @@ struct DalitzEEQC { void addhistograms() { // event info - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(&fRegistry); + fEMEventCut.addEventHistograms(&fRegistry); // pair info const AxisSpec axis_mass{200, 0, 0.2, "m_{ee} (GeV/c^{2})"}; @@ -246,6 +244,7 @@ struct DalitzEEQC { fEMEventCut.SetRequireNoSameBunchPileup(eventcuts.cfgRequireNoSameBunchPileup); fEMEventCut.SetRequireVertexITSTPC(eventcuts.cfgRequireVertexITSTPC); fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); + fEMEventCut.SetTrackOccupancyRange(eventcuts.cfgOccupancyMin, eventcuts.cfgOccupancyMax); } void DefineDileptonCut() @@ -356,8 +355,8 @@ struct DalitzEEQC { Filter trackFilter = dileptoncuts.cfg_min_pt_track < o2::aod::track::pt && nabs(o2::aod::track::eta) < dileptoncuts.cfg_max_eta_track && o2::aod::track::tpcChi2NCl < dileptoncuts.cfg_max_chi2tpc && o2::aod::track::itsChi2NCl < dileptoncuts.cfg_max_chi2its && nabs(o2::aod::track::dcaXY) < dileptoncuts.cfg_max_dcaxy && nabs(o2::aod::track::dcaZ) < dileptoncuts.cfg_max_dcaz; Filter pidFilter = dileptoncuts.cfg_min_TPCNsigmaEl < o2::aod::pidtpc::tpcNSigmaEl && o2::aod::pidtpc::tpcNSigmaEl < dileptoncuts.cfg_max_TPCNsigmaEl && (o2::aod::pidtpc::tpcNSigmaPi < dileptoncuts.cfg_min_TPCNsigmaPi || dileptoncuts.cfg_max_TPCNsigmaPi < o2::aod::pidtpc::tpcNSigmaPi); using FilteredMyTracks = soa::Filtered; - Partition posTracks = o2::aod::emprimaryelectron::sign > int8_t(0); - Partition negTracks = o2::aod::emprimaryelectron::sign < int8_t(0); + Partition posTracks = o2::aod::emprimaryelectron::sign > static_cast(0); + Partition negTracks = o2::aod::emprimaryelectron::sign < static_cast(0); std::vector used_trackIds; void processQC(FilteredMyCollisions const& collisions, FilteredMyTracks const& /*tracks*/) @@ -369,16 +368,12 @@ struct DalitzEEQC { continue; } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(&fRegistry, collision); + fEMEventCut.fillEventInfo<0>(&fRegistry, collision); if (!fEMEventCut.IsSelected(collision)) { continue; } - if (!(eventcuts.cfgOccupancyMin <= collision.trackOccupancyInTimeRange()) || !(collision.trackOccupancyInTimeRange() < eventcuts.cfgOccupancyMax)) { - continue; - } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(&fRegistry, collision); - fRegistry.fill(HIST("Event/before/hCollisionCounter"), 12.0); // accepted - fRegistry.fill(HIST("Event/after/hCollisionCounter"), 12.0); // accepted + fEMEventCut.fillEventInfo<1>(&fRegistry, collision); + fEMEventCut.fillAccepted(&fRegistry); auto posTracks_per_coll = posTracks->sliceByCached(o2::aod::emprimaryelectronda::pmeventId, collision.globalIndex(), cache); auto negTracks_per_coll = negTracks->sliceByCached(o2::aod::emprimaryelectronda::pmeventId, collision.globalIndex(), cache); diff --git a/PWGEM/PhotonMeson/Tasks/dalitzEEQCMC.cxx b/PWGEM/PhotonMeson/Tasks/dalitzEEQCMC.cxx index c0dd75f3545..064dc2d0633 100644 --- a/PWGEM/PhotonMeson/Tasks/dalitzEEQCMC.cxx +++ b/PWGEM/PhotonMeson/Tasks/dalitzEEQCMC.cxx @@ -20,7 +20,6 @@ #include "PWGEM/PhotonMeson/Core/EMPhotonEventCut.h" #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/PIDResponseTPC.h" @@ -149,7 +148,7 @@ struct DalitzEEQCMC { } mctrackcuts; HistogramRegistry fRegistry{"output", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; - static constexpr std::array event_cut_types = {"before/", "after/"}; + // static constexpr std::array event_cut_types = {"before/", "after/"}; // unused static constexpr std::array track_types = {"primary/", "secondary/"}; ~DalitzEEQCMC() = default; @@ -157,7 +156,7 @@ struct DalitzEEQCMC { void addhistograms() { // event info - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(&fRegistry); + fEMEventCut.addEventHistograms(&fRegistry); const AxisSpec axis_mass{200, 0, 0.2, "m_{ee} (GeV/c^{2})"}; const AxisSpec axis_pt{200, 0, 2, "p_{T,ee} (GeV/c)"}; @@ -279,6 +278,7 @@ struct DalitzEEQCMC { fEMEventCut.SetRequireNoSameBunchPileup(eventcuts.cfgRequireNoSameBunchPileup); fEMEventCut.SetRequireVertexITSTPC(eventcuts.cfgRequireVertexITSTPC); fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); + fEMEventCut.SetTrackOccupancyRange(eventcuts.cfgOccupancyMin, eventcuts.cfgOccupancyMax); } void DefineDileptonCut() @@ -362,7 +362,8 @@ struct DalitzEEQCMC { } float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(t1.px(), t1.py(), t1.pz(), t2.px(), t2.py(), t2.pz(), t1.sign(), t2.sign(), d_bz); - if (mother_id > -1 && t1mc.pdgCode() * t2mc.pdgCode() < 0) { + // if (mother_id > -1 && t1mc.pdgCode() * t2mc.pdgCode() < 0) { + if (t1mc.pdgCode() * t2mc.pdgCode() < 0) { auto mcmother = mcparticles.iteratorAt(mother_id); if (cfgRequireTrueAssociation && (mcmother.emmceventId() != collision.emmceventId())) { return false; @@ -506,8 +507,8 @@ struct DalitzEEQCMC { Filter trackFilter = dileptoncuts.cfg_min_pt_track < o2::aod::track::pt && nabs(o2::aod::track::eta) < dileptoncuts.cfg_max_eta_track && o2::aod::track::tpcChi2NCl < dileptoncuts.cfg_max_chi2tpc && o2::aod::track::itsChi2NCl < dileptoncuts.cfg_max_chi2its && nabs(o2::aod::track::dcaXY) < dileptoncuts.cfg_max_dcaxy && nabs(o2::aod::track::dcaZ) < dileptoncuts.cfg_max_dcaz; Filter pidFilter = dileptoncuts.cfg_min_TPCNsigmaEl < o2::aod::pidtpc::tpcNSigmaEl && o2::aod::pidtpc::tpcNSigmaEl < dileptoncuts.cfg_max_TPCNsigmaEl && (o2::aod::pidtpc::tpcNSigmaPi < dileptoncuts.cfg_min_TPCNsigmaPi || dileptoncuts.cfg_max_TPCNsigmaPi < o2::aod::pidtpc::tpcNSigmaPi); using FilteredMyMCTracks = soa::Filtered; - Partition posTracks = o2::aod::emprimaryelectron::sign > int8_t(0); - Partition negTracks = o2::aod::emprimaryelectron::sign < int8_t(0); + Partition posTracks = o2::aod::emprimaryelectron::sign > static_cast(0); + Partition negTracks = o2::aod::emprimaryelectron::sign < static_cast(0); Filter collisionFilter_centrality = (cfgCentMin < o2::aod::cent::centFT0M && o2::aod::cent::centFT0M < cfgCentMax) || (cfgCentMin < o2::aod::cent::centFT0A && o2::aod::cent::centFT0A < cfgCentMax) || (cfgCentMin < o2::aod::cent::centFT0C && o2::aod::cent::centFT0C < cfgCentMax); using FilteredMyCollisions = soa::Filtered; @@ -524,16 +525,12 @@ struct DalitzEEQCMC { continue; } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(&fRegistry, collision); + fEMEventCut.fillEventInfo<0>(&fRegistry, collision); if (!fEMEventCut.IsSelected(collision)) { continue; } - if (!(eventcuts.cfgOccupancyMin <= collision.trackOccupancyInTimeRange()) || !(collision.trackOccupancyInTimeRange() < eventcuts.cfgOccupancyMax)) { - continue; - } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(&fRegistry, collision); - fRegistry.fill(HIST("Event/before/hCollisionCounter"), 12.0); // accepted - fRegistry.fill(HIST("Event/after/hCollisionCounter"), 12.0); // accepted + fEMEventCut.fillEventInfo<1>(&fRegistry, collision); + fEMEventCut.fillAccepted(&fRegistry); auto posTracks_per_coll = posTracks->sliceByCached(o2::aod::emprimaryelectronda::pmeventId, collision.globalIndex(), cache); auto negTracks_per_coll = negTracks->sliceByCached(o2::aod::emprimaryelectronda::pmeventId, collision.globalIndex(), cache); @@ -567,9 +564,6 @@ struct DalitzEEQCMC { if (!fEMEventCut.IsSelected(collision)) { continue; } - if (!(eventcuts.cfgOccupancyMin <= collision.trackOccupancyInTimeRange()) || !(collision.trackOccupancyInTimeRange() < eventcuts.cfgOccupancyMax)) { - continue; - } auto mccollision = collision.emmcevent_as(); auto posTracks_per_coll = posTracksMC->sliceByCachedUnsorted(o2::aod::emmcparticle::emmceventId, mccollision.globalIndex(), cache); @@ -601,34 +595,34 @@ struct DalitzEEQCMC { continue; } - if (mother_id > -1) { - auto mcmother = mcparticles.iteratorAt(mother_id); - if (mcmother.isPhysicalPrimary() || mcmother.producedByGenerator()) { - - switch (std::abs(mcmother.pdgCode())) { - case 111: - fRegistry.fill(HIST("Generated/sm/Pi0/hMvsPt"), v12.M(), v12.Pt()); - break; - case 221: - fRegistry.fill(HIST("Generated/sm/Eta/hMvsPt"), v12.M(), v12.Pt()); - break; - case 331: - fRegistry.fill(HIST("Generated/sm/EtaPrime/hMvsPt"), v12.M(), v12.Pt()); - break; - case 113: - fRegistry.fill(HIST("Generated/sm/Rho/hMvsPt"), v12.M(), v12.Pt()); - break; - case 223: - fRegistry.fill(HIST("Generated/sm/Omega/hMvsPt"), v12.M(), v12.Pt()); - break; - case 333: - fRegistry.fill(HIST("Generated/sm/Phi/hMvsPt"), v12.M(), v12.Pt()); - break; - default: - break; - } + // if (mother_id > -1) { // not needed because of + auto mcmother = mcparticles.iteratorAt(mother_id); + if (mcmother.isPhysicalPrimary() || mcmother.producedByGenerator()) { + + switch (std::abs(mcmother.pdgCode())) { + case 111: + fRegistry.fill(HIST("Generated/sm/Pi0/hMvsPt"), v12.M(), v12.Pt()); + break; + case 221: + fRegistry.fill(HIST("Generated/sm/Eta/hMvsPt"), v12.M(), v12.Pt()); + break; + case 331: + fRegistry.fill(HIST("Generated/sm/EtaPrime/hMvsPt"), v12.M(), v12.Pt()); + break; + case 113: + fRegistry.fill(HIST("Generated/sm/Rho/hMvsPt"), v12.M(), v12.Pt()); + break; + case 223: + fRegistry.fill(HIST("Generated/sm/Omega/hMvsPt"), v12.M(), v12.Pt()); + break; + case 333: + fRegistry.fill(HIST("Generated/sm/Phi/hMvsPt"), v12.M(), v12.Pt()); + break; + default: + break; } } + // } } // end of true ULS pair loop } // end of collision loop } diff --git a/PWGEM/PhotonMeson/Tasks/dileptonEffiForDalitzSkimmer.cxx b/PWGEM/PhotonMeson/Tasks/dileptonEffiForDalitzSkimmer.cxx new file mode 100644 index 00000000000..58a62d358f1 --- /dev/null +++ b/PWGEM/PhotonMeson/Tasks/dileptonEffiForDalitzSkimmer.cxx @@ -0,0 +1,446 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file dielptonEffi.cxx +/// \brief calculating dilepton efficiency for dalitz analyses +/// \author Laura Gans-Bartl, laura.gans-bartl@cern.ch + +#include "PWGEM/Dilepton/DataModel/dileptonTables.h" +#include "PWGEM/PhotonMeson/DataModel/EventTables.h" +#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" + +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) + +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::soa; +using namespace o2::framework; + +using MyEMPrimaryElectrons = soa::Join; +using MyEMPrimaryElectron = MyEMPrimaryElectrons::iterator; + +using MyTracks = soa::Join; +using MyTrack = MyTracks::iterator; +using MyTracksMC = soa::Join; +using MyTrackMC = MyTracksMC::iterator; + +using MyMCV0Legs = soa::Join; +using MyMCV0Leg = MyMCV0Legs::iterator; + +using MyV0Photons = soa::Join; +using MyV0Photon = MyV0Photons::iterator; + +using MyCollisionsMC = soa::Join; +using MyCollisionMC = MyCollisionsMC::iterator; + +struct DileptonEffiForDalitzSkimmer { + SliceCache cache; + PresliceOptional perCollision = aod::emprimaryelectronda::pmeventId; + PresliceOptional perTracksCollision = aod::track::collisionId; + PresliceOptional perDileptonMcCollision = aod::mcparticle::mcCollisionId; + Preslice perCollisionV0 = aod::v0photonkf::pmeventId; + + HistogramRegistry fRegistry{"output", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; + + bool isAmbiguousTrack(const auto& track, const aod::AmbiguousTracks& ambiguousTracks) + { + for (const auto& ambiguousTrack : ambiguousTracks) { + if (ambiguousTrack.trackId() == track.globalIndex()) { + return true; + } + } + return false; + } + + void init(o2::framework::InitContext&) + { + fRegistry.add("hPtSglEpEMPrimTrue", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtSglEmEMPrimTrue", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtSglEpEMPrimTruePtRec", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtSglEmEMPrimTruePtRec", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + + fRegistry.add("hPtSglPipEMPrimTrue", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtSglPimEMPrimTrue", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + + fRegistry.add("hPtSglEpMCPart", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtSglEmMCPart", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + + fRegistry.add("hPtSgltpcNSigmaEl", "pT;p_{T} (GeV/c); tpcNSigmaEl", kTH2F, {{1000, 0.0f, 10}, {200, -5.f, 5}}, false); + fRegistry.add("hPtSgltpcNSigmaPi", "pT;p_{T} (GeV/c); tpcNSigmaPi", kTH2F, {{1000, 0.0f, 10}, {200, -5.f, 5}}, false); + fRegistry.add("hPtSgltofNSigmaEl", "pT;p_{T} (GeV/c); tofNSigmaEl", kTH2F, {{1000, 0.0f, 10}, {200, -5.f, 5}}, false); + + fRegistry.add("hPtSglTracktpcNSigmaEl", "pT;p_{T} (GeV/c); tpcNSigmaEl", kTH2F, {{1000, 0.0f, 10}, {200, -5.f, 5}}, false); + fRegistry.add("hPtSglTracktpcNSigmaPi", "pT;p_{T} (GeV/c); tpcNSigmaPi", kTH2F, {{1000, 0.0f, 10}, {200, -5.f, 5}}, false); + fRegistry.add("hPtSglTracktofNSigmaEl", "pT;p_{T} (GeV/c); tofNSigmaEl", kTH2F, {{1000, 0.0f, 10}, {200, -5.f, 5}}, false); + + fRegistry.add("hPtMcEtaGeneral", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtMcEtaDalitz", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + + fRegistry.add("hPtEPrimMulti", ";p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hEPrimNumOccurence", ";p_{T} (GeV/c)", kTH1F, {{11, -0.5f, 10.5}}, false); + + fRegistry.add("hPtTrackMulti", ";p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hTrackNumOccurence", ";p_{T} (GeV/c)", kTH1F, {{11, -0.5f, 10.5}}, false); + + fRegistry.add("hPtSglEpTrackEMPrimTrue", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtSglEmTrackEMPrimTrue", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + + fRegistry.add("hPtSglEmFromTrueEtaGen", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtGammaFromTrueEtaGen", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + + fRegistry.add("hPtSglEmFromTrueEtaRecoRecoPt", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtSglEmFromTrueEtaRecoGenPt", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtGammaFromTrueEtaReco", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + + fRegistry.add("hPtSglPipTrackEMPrimTrue", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtSglPimTrackEMPrimTrue", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + + fRegistry.add("hMcPartsPerColl", " ;N_{tracks}/collision; N", kTH1F, {{400, -0.5, 400.5}}, false); + + fRegistry.add("hCheckAsso", "pT;p_{T} (GeV/c)", kTH1F, {{2, -0.5f, 1.5}}, false); + } + void processReco(aod::PMEvents const& collisions, MyEMPrimaryElectrons const& emprimaryelectrons, aod::EMMCParticles_001 const& emmcparticle) // aod::EMEvents_005, aod::EMMCParticles_001 + { + LOG(info) << "started process"; + if (emprimaryelectrons.size() <= 0 || emmcparticle.size() <= 0) { + return; + } + LOG(info) << "size emprimaryelectrons: " << emprimaryelectrons.size(); + + const int pdgEp = -11; + const int pdgEm = 11; + const int pdgPip = -211; + const int pdgPim = 211; + auto iterMC = emmcparticle.begin(); + std::unordered_map mapMultCount; + + //**Single electrons and positrons**/ + for (auto const& collision : collisions) { + auto slicedParticles = emprimaryelectrons.sliceBy(perCollision, collision.globalIndex()); + + for (const auto& ePrimary : slicedParticles) { + if (!std::isfinite(ePrimary.pt())) { // || std::abs(ePrimary.eta()) > 0.8 + continue; + } + + fRegistry.fill(HIST("hPtSgltpcNSigmaEl"), ePrimary.pt(), ePrimary.tpcNSigmaEl()); + fRegistry.fill(HIST("hPtSgltpcNSigmaPi"), ePrimary.pt(), ePrimary.tpcNSigmaPi()); + fRegistry.fill(HIST("hPtSgltofNSigmaEl"), ePrimary.pt(), ePrimary.tofNSigmaEl()); + + const auto mcId = ePrimary.emmcparticleId(); + + if (mcId < 0 || mcId >= emmcparticle.size()) { + continue; + } + iterMC.setCursor(mcId); + mapMultCount[static_cast(iterMC.globalIndex())]++; + + if (!iterMC.isPhysicalPrimary() || fabs(iterMC.y()) > 0.8) { + continue; + } + + if (iterMC.pdgCode() == pdgEp) { + fRegistry.fill(HIST("hPtSglEpEMPrimTrue"), iterMC.pt()); + fRegistry.fill(HIST("hPtSglEpEMPrimTruePtRec"), ePrimary.pt()); + } + if (iterMC.pdgCode() == pdgEm) { + fRegistry.fill(HIST("hPtSglEmEMPrimTrue"), iterMC.pt()); + fRegistry.fill(HIST("hPtSglEmEMPrimTruePtRec"), ePrimary.pt()); + } + if (iterMC.pdgCode() == pdgPip) { + fRegistry.fill(HIST("hPtSglPipEMPrimTrue"), iterMC.pt()); + } + if (iterMC.pdgCode() == pdgPim) { + fRegistry.fill(HIST("hPtSglPimEMPrimTrue"), iterMC.pt()); + } + } + } + // uint32_t maxNumOccurrences = 0; + // uint32_t mcIndexWithMaxTracks = 0; + // uint32_t numOkMc = 0; + // uint32_t numNotOkMc = 0; + + // for (const auto& [mcGlobalIndex, numOccurrences] : mapMultCount) { + // if (numOccurrences > 1) { + // iterMC.setCursor(mcGlobalIndex); + // fRegistry.fill(HIST("hPtEPrimMulti"), iterMC.pt()); + // fRegistry.fill(HIST("hEPrimNumOccurence"), numOccurrences); + // numNotOkMc++; + // } else { + // numOkMc++; + // } + // if (numOccurrences > maxNumOccurrences) { + // maxNumOccurrences = numOccurrences; + // mcIndexWithMaxTracks = mcGlobalIndex; + // } + // } + // LOG(info) << "numOkMc: " << numOkMc; + // LOG(info) << "numNotOkMc: " << numNotOkMc; + } + PROCESS_SWITCH(DileptonEffiForDalitzSkimmer, processReco, "process for reco table emprimary", false); + + void processTracks(aod::Collisions const& collisions, aod::McParticles const& mcParticles, MyTracksMC const& tracks) + { + if (tracks.size() <= 0 || mcParticles.size() <= 0) { + return; + } + LOG(info) << "size tracks: " << tracks.size(); + + ////**Analysing tracks **/ + const int pdgEp = -11; + const int pdgEm = 11; + const int pdgPip = -211; + const int pdgPim = 211; + auto iterMCTrack = mcParticles.begin(); + + std::unordered_map mapMultCount; + ////******Tracks before process function******//// + for (auto const& collision : collisions) { + auto slicedTracks = tracks.sliceBy(perTracksCollision, collision.globalIndex()); + for (const auto& track : slicedTracks) { + if (!track.has_mcParticle() || !std::isfinite(track.pt())) { //|| std::abs(track.eta()) > 0.8 + continue; + } + + fRegistry.fill(HIST("hPtSglTracktpcNSigmaEl"), track.pt(), track.tpcNSigmaEl()); + fRegistry.fill(HIST("hPtSglTracktpcNSigmaPi"), track.pt(), track.tpcNSigmaPi()); + fRegistry.fill(HIST("hPtSglTracktofNSigmaEl"), track.pt(), track.tofNSigmaEl()); + + const auto mcId = track.mcParticleId(); + + if (mcId < 0 || mcId >= mcParticles.size()) { + continue; + } + + iterMCTrack.setCursor(mcId); + mapMultCount[static_cast(iterMCTrack.globalIndex())]++; + + if (!iterMCTrack.isPhysicalPrimary() || fabs(iterMCTrack.y()) > 0.8) { + continue; + } + + if (iterMCTrack.pdgCode() == pdgEp) { + fRegistry.fill(HIST("hPtSglEpTrackEMPrimTrue"), iterMCTrack.pt()); + } + + if (iterMCTrack.pdgCode() == pdgEm) { + fRegistry.fill(HIST("hPtSglEmTrackEMPrimTrue"), iterMCTrack.pt()); + } + if (iterMCTrack.pdgCode() == pdgPip) { + fRegistry.fill(HIST("hPtSglPipTrackEMPrimTrue"), iterMCTrack.pt()); + } + if (iterMCTrack.pdgCode() == pdgPim) { + fRegistry.fill(HIST("hPtSglPimTrackEMPrimTrue"), iterMCTrack.pt()); + } + } + } + // uint32_t maxNumOccurrences = 0; + // uint64_t mcIndexWithMaxTracks = 0; + // uint32_t numOkMc = 0; + // uint64_t numNotOkMc = 0; + + // for (const auto& [mcGlobalIndex, numOccurrences] : mapMultCount) { + // if (numOccurrences > 1) { + // iterMCTrack.setCursor(mcGlobalIndex); + // // LOG(info) << "Track | Global Index: " << iterMCTrack.globalIndex(); + // fRegistry.fill(HIST("hPtTrackMulti"), iterMCTrack.pt()); + // fRegistry.fill(HIST("hTrackNumOccurence"), numOccurrences); + // numNotOkMc++; + // } else { + // numOkMc++; + // } + // if (numOccurrences > maxNumOccurrences) { + // maxNumOccurrences = numOccurrences; + // mcIndexWithMaxTracks = mcGlobalIndex; + // } + // } + } + PROCESS_SWITCH(DileptonEffiForDalitzSkimmer, processTracks, "process for reco MyTracks", true); + + using FilteredMyCollisionsMC = soa::Filtered; + void processGens(aod::McCollisions const& mcCollisions, aod::McParticles const& mcParticles, aod::PMEvents const& pmcollisions, MyEMPrimaryElectrons const& emprimaryelectrons, aod::EMMCParticles_001 const& emmcparticle) + { + LOG(info) << "started process MC"; + if (mcParticles.size() <= 0 || emprimaryelectrons.size() <= 0 || emmcparticle.size() <= 0) { + return; + } + LOG(info) << " size mcParticles: " << mcParticles.size(); + auto iterChild = mcParticles.begin(); + auto iterMom = mcParticles.begin(); + + const int pdgEp = -11; + const int pdgEm = 11; + const int pdgEta = 221; + const int pdgGamma = 22; + + ////******generated Dileptons******//// + std::vector vecEpMCParticles; + vecEpMCParticles.reserve(1e5); + std::vector vecEmMCParticles; + vecEpMCParticles.reserve(1e5); + + std::vector vecEmMCPartPt; + vecEmMCPartPt.reserve(1e5); + + //**Single electrons and positrons**// + for (auto const& mcCollision : mcCollisions) { + auto slicedMcParticles = mcParticles.sliceBy(perDileptonMcCollision, mcCollision.globalIndex()); + fRegistry.fill(HIST("hMcPartsPerColl"), slicedMcParticles.size()); + for (const auto& mcParticle : slicedMcParticles) { + if (!mcParticle.isPhysicalPrimary() || fabs(mcParticle.y()) > 0.8) { + continue; + } + vecEmMCPartPt.push_back(mcParticle.pt()); + + // const float dx = mcParticle.vx(); //- mcCollision.posX() + // const float dy = mcParticle.vy(); + // const float dz = mcParticle.vz(); + + // const float prodDist = sqrt(dx * dx + dy * dy); //+ dz * dz + + if (mcParticle.pdgCode() == pdgEp) { + if (!std::isfinite(mcParticle.pt())) { + continue; + } + fRegistry.fill(HIST("hPtSglEpMCPart"), mcParticle.pt()); + vecEpMCParticles.push_back(mcParticle.globalIndex()); + } + if (mcParticle.pdgCode() == pdgEm) { + if (!std::isfinite(mcParticle.pt())) { + continue; + } + fRegistry.fill(HIST("hPtSglEmMCPart"), mcParticle.pt()); + vecEmMCParticles.push_back(mcParticle.globalIndex()); + } + + ////******Investigating Eta Daltz Decay******//// + if (!mcParticle.has_mothers()) { + continue; + } + if (mcParticle.mothersIds()[0] < 0 || mcParticle.mothersIds()[0] >= mcParticles.size()) { + continue; + } + iterMom.setCursor(mcParticle.mothersIds()[0]); + if (iterMom.pdgCode() != pdgEta) { + continue; + } + fRegistry.fill(HIST("hPtMcEtaGeneral"), iterMom.pt()); + + if (!iterMom.has_daughters()) { + continue; + } + bool childEp = false, childEm = false, childG = false; + double ptEmFromDalitzEta = -1; + double ptGammaFromDalitzEta = -1; + for (int childId = iterMom.daughtersIds()[0]; childId <= iterMom.daughtersIds()[1]; ++childId) { + iterChild.setCursor(childId); + if (iterChild.pdgCode() == pdgEp) { + childEp = true; + } + if (iterChild.pdgCode() == pdgEm) { + childEm = true; + ptEmFromDalitzEta = iterChild.pt(); + } + if (iterChild.pdgCode() == pdgGamma) { + childG = true; + ptGammaFromDalitzEta = iterChild.pt(); + } + } + if (childEp && childEm && childG) { + fRegistry.fill(HIST("hPtMcEtaDalitz"), iterMom.pt()); + fRegistry.fill(HIST("hPtSglEmFromTrueEtaGen"), ptEmFromDalitzEta); + fRegistry.fill(HIST("hPtGammaFromTrueEtaGen"), ptGammaFromDalitzEta); + } + } + if (vecEpMCParticles.empty() || vecEmMCParticles.empty()) { + continue; + } + } + + // Reco loop Elektronen + auto iterMC = emmcparticle.begin(); + auto iterMomE = emmcparticle.begin(); + for (auto const& pmcollision : pmcollisions) { + auto slicedParticles = emprimaryelectrons.sliceBy(perCollision, pmcollision.globalIndex()); + + for (const auto& ePrimary : slicedParticles) { + if (!std::isfinite(ePrimary.pt()) || fabs(iterMC.y()) > 0.8) { // || std::abs(ePrimary.eta()) > 0.8 + continue; + } + const auto mcId = ePrimary.emmcparticleId(); + iterMC.setCursor(mcId); + + if (!iterMC.isPhysicalPrimary() || fabs(iterMC.y()) > 0.8) { + continue; + } + + if (iterMC.pdgCode() != pdgEm) { + continue; + } + + if (!iterMC.has_mothers()) { + continue; + } + iterMomE.setCursor(iterMC.mothersIds()[0]); + if (iterMomE.pdgCode() != pdgEta) { + continue; + } + fRegistry.fill(HIST("hPtSglEmFromTrueEtaRecoRecoPt"), ePrimary.pt()); + fRegistry.fill(HIST("hPtSglEmFromTrueEtaRecoGenPt"), iterMC.pt()); // to do!!!!!!!!!!!!!!! + } + } + } + PROCESS_SWITCH(DileptonEffiForDalitzSkimmer, processGens, "process for generated (MCParticles)", false); + + void processCheckAssociation(MyEMPrimaryElectrons const& emprimaryelectrons, MyTracksMC const& tracks) + { + auto iterTrack = tracks.begin(); + LOG(info) << "start process Association"; + for (const auto& ePrimary : emprimaryelectrons) { + int idEm = ePrimary.emmcparticleId(); + iterTrack.setCursor(ePrimary.trackId()); + int idTrack = iterTrack.mcParticleId(); + + if (idEm == idTrack) { + fRegistry.fill(HIST("hCheckAsso"), 1); + } else { + fRegistry.fill(HIST("hCheckAsso"), 0); + } + } + } + PROCESS_SWITCH(DileptonEffiForDalitzSkimmer, processCheckAssociation, "checks if McParticle association is valid", false); +}; + +WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + return WorkflowSpec{adaptAnalysisTask(context, TaskName{"dilepton-effi-for-dalitz-skimmer"})}; +} diff --git a/PWGEM/PhotonMeson/Tasks/emcalMcTask.cxx b/PWGEM/PhotonMeson/Tasks/emcalMcTask.cxx index 073479c2c9e..2ccf37fd92b 100644 --- a/PWGEM/PhotonMeson/Tasks/emcalMcTask.cxx +++ b/PWGEM/PhotonMeson/Tasks/emcalMcTask.cxx @@ -18,7 +18,6 @@ #include "PWGEM/PhotonMeson/Core/EMPhotonEventCut.h" #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" #include #include @@ -166,6 +165,7 @@ struct EmcalMcTask { fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + fEMEventCut.SetFT0COccupancyRange(eventcuts.cfgFT0COccupancyMin, eventcuts.cfgFT0COccupancyMax); } void defineEMCCut() @@ -198,7 +198,7 @@ struct EmcalMcTask { defineEMEventCut(); defineEMCCut(); fEMCCut.addQAHistograms(®istry); - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(®istry); + fEMEventCut.addEventHistograms(®istry); const AxisSpec thnAxisERec{thnConfigAxisE, "#it{E}_{Rec} (GeV)"}; @@ -272,25 +272,20 @@ struct EmcalMcTask { bool isFullEventSelected(TCollision const& collision, bool fillHisto = false) { if (fillHisto) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(®istry, collision); + fEMEventCut.fillEventInfo<0>(®istry, collision); } if (!(fEMEventCut.IsSelected(collision))) { // general event selection return false; } - if (!(eventcuts.cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange() && collision.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { - // occupancy selection - return false; - } float centOrMult = getCentralityOrMultiplicity(collision); if (useCent && (centOrMult < eventcuts.cfgMinCent || centOrMult > eventcuts.cfgMaxCent)) { // event selection return false; } if (fillHisto) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(®istry, collision); - registry.fill(HIST("Event/before/hCollisionCounter"), 12.0); // accepted - registry.fill(HIST("Event/after/hCollisionCounter"), 12.0); // accepted + fEMEventCut.fillEventInfo<1>(®istry, collision); + fEMEventCut.fillAccepted(®istry); } return true; } diff --git a/PWGEM/PhotonMeson/Tasks/emcalPhotonMcTask.cxx b/PWGEM/PhotonMeson/Tasks/emcalPhotonMcTask.cxx index 9d233f75ca0..f3f90bd7e44 100644 --- a/PWGEM/PhotonMeson/Tasks/emcalPhotonMcTask.cxx +++ b/PWGEM/PhotonMeson/Tasks/emcalPhotonMcTask.cxx @@ -22,7 +22,6 @@ #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" #include "PWGEM/PhotonMeson/Utils/MCUtilities.h" #include "PWGEM/PhotonMeson/Utils/ParticleOrigin.h" @@ -361,6 +360,7 @@ struct EmcalPhotonMcTask { fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + fEMEventCut.SetFT0COccupancyRange(eventcuts.cfgFT0COccupancyMin, eventcuts.cfgFT0COccupancyMax); } void defineEMCCut() @@ -393,7 +393,7 @@ struct EmcalPhotonMcTask { defineEMEventCut(); defineEMCCut(); fEMCCut.addQAHistograms(®istry); - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(®istry); + fEMEventCut.addEventHistograms(®istry); const AxisSpec thnAxisERec{thnConfigAxisPt, "#it{E}_{Rec} (GeV)"}; const AxisSpec thnAxisPtRec{thnConfigAxisPt, "#it{p}_{T} (GeV/#it{c})"}; @@ -568,25 +568,20 @@ struct EmcalPhotonMcTask { bool isFullEventSelected(TCollision const& collision, bool fillHisto = false) { if (fillHisto) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(®istry, collision); + fEMEventCut.fillEventInfo<0>(®istry, collision); } if (!(fEMEventCut.IsSelected(collision))) { // general event selection return false; } - if (!(eventcuts.cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange() && collision.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { - // occupancy selection - return false; - } float centOrMult = getCentralityOrMultiplicity(collision); if (useCent && (centOrMult < eventcuts.cfgMinCent || centOrMult > eventcuts.cfgMaxCent)) { // event selection return false; } if (fillHisto) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(®istry, collision); - registry.fill(HIST("Event/before/hCollisionCounter"), 12.0); // accepted - registry.fill(HIST("Event/after/hCollisionCounter"), 12.0); // accepted + fEMEventCut.fillEventInfo<1>(®istry, collision); + fEMEventCut.fillAccepted(®istry); } return true; } diff --git a/PWGEM/PhotonMeson/Tasks/emcalQC.cxx b/PWGEM/PhotonMeson/Tasks/emcalQC.cxx index def4442d5d5..b8007742c20 100644 --- a/PWGEM/PhotonMeson/Tasks/emcalQC.cxx +++ b/PWGEM/PhotonMeson/Tasks/emcalQC.cxx @@ -20,7 +20,6 @@ #include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" #include "PWGEM/PhotonMeson/Utils/ClusterHistograms.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/TriggerAliases.h" @@ -118,6 +117,7 @@ struct EmcalQC { fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + fEMEventCut.SetTrackOccupancyRange(eventcuts.cfgOccupancyMin, eventcuts.cfgOccupancyMax); } void defineEMCCut() @@ -159,7 +159,7 @@ struct EmcalQC { const AxisSpec thAxisdEta{200, -0.06, 0.06, "#Delta#eta"}; const AxisSpec thAxisdPhi{200, -0.06, 0.06, "#Delta#varphi (rad)"}; - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(&fRegistry); + fEMEventCut.addEventHistograms(&fRegistry); auto hEMCCollisionCounter = fRegistry.add("Event/hEMCCollisionCounter", "Number of collisions after event cuts", HistType::kTH1D, {{7, 0.5, 7.5}}, false); hEMCCollisionCounter->GetXaxis()->SetBinLabel(1, "all"); hEMCCollisionCounter->GetXaxis()->SetBinLabel(2, "+TVX"); // TVX @@ -210,17 +210,13 @@ struct EmcalQC { } } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(&fRegistry, collision, collision.weight()); + fEMEventCut.fillEventInfo<0>(&fRegistry, collision, collision.weight()); if (!fEMEventCut.IsSelected(collision)) { return false; } - if (!(eventcuts.cfgOccupancyMin <= collision.trackOccupancyInTimeRange()) || !(collision.trackOccupancyInTimeRange() < eventcuts.cfgOccupancyMax)) { - return false; - } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(&fRegistry, collision, collision.weight()); - fRegistry.fill(HIST("Event/before/hCollisionCounter"), 12.0, collision.weight()); // accepted - fRegistry.fill(HIST("Event/after/hCollisionCounter"), 12.0, collision.weight()); // accepted + fEMEventCut.fillEventInfo<1>(&fRegistry, collision, collision.weight()); + fEMEventCut.fillAccepted(&fRegistry, collision.weight()); return true; } diff --git a/PWGEM/PhotonMeson/Tasks/eventNormalizationTask.cxx b/PWGEM/PhotonMeson/Tasks/eventNormalizationTask.cxx index 1c5b4bc8eaa..5ac8343abf8 100644 --- a/PWGEM/PhotonMeson/Tasks/eventNormalizationTask.cxx +++ b/PWGEM/PhotonMeson/Tasks/eventNormalizationTask.cxx @@ -43,25 +43,10 @@ struct EventNormalizationTask { void init(InitContext& /*context*/) { // Names derived from EventAcceptance bits in EventTables.h - std::vector vecLabelNames = { - "all", - "has MC coll", - "good zVtx", - "is FT0AND", - "No TFB", - "ITS ROFB", - "Same bunch Pileup", - "ZVtxFT0PV", - "No coll in time range", - "good track occi", - "Good FT0 occupancy", - "kTVXinEMC", - "Good centrality", - "Good RCT", - "Good Sel8"}; - fRegistry.add("EventNormalization/hEventCounter", "Event Counter;category;#it{N}_{evt}", kTH1D, {{static_cast(vecLabelNames.size()), -0.5, -0.5 + static_cast(vecLabelNames.size())}}, false); - for (size_t i = 0; i < vecLabelNames.size(); ++i) { - fRegistry.get(HIST("EventNormalization/hEventCounter"))->GetXaxis()->SetBinLabel(i + 1, vecLabelNames[i].c_str()); + + fRegistry.add("EventNormalization/hEventCounter", "Event Counter;category;#it{N}_{evt}", kTH1D, {{static_cast(o2::aod::pmevsel::vecEventAccBitNames.size()), -0.5, -0.5 + static_cast(o2::aod::pmevsel::vecEventAccBitNames.size())}}, false); + for (size_t i = 0; i < o2::aod::pmevsel::vecEventAccBitNames.size(); ++i) { + fRegistry.get(HIST("EventNormalization/hEventCounter"))->GetXaxis()->SetBinLabel(i + 1, o2::aod::pmevsel::vecEventAccBitNames[i].c_str()); } } diff --git a/PWGEM/PhotonMeson/Tasks/mcGeneratorStudies.cxx b/PWGEM/PhotonMeson/Tasks/mcGeneratorStudies.cxx index 6aebaf77f63..6a0ca911f01 100644 --- a/PWGEM/PhotonMeson/Tasks/mcGeneratorStudies.cxx +++ b/PWGEM/PhotonMeson/Tasks/mcGeneratorStudies.cxx @@ -13,6 +13,8 @@ /// \brief Task that produces the generated pT spectrum of a given particle for MC studies based on on the fly MC simulations /// \author Nicolas Strangmann (nicolas.strangmann@cern.ch) - Goethe University Frankfurt +#include "PWGEM/PhotonMeson/Utils/MCUtilities.h" + #include #include #include @@ -22,6 +24,9 @@ #include #include +#include +#include + #include using namespace o2; @@ -31,23 +36,64 @@ struct MCGeneratorStudies { HistogramRegistry mHistManager{"MCGeneratorStudyHistograms"}; Configurable cfgSelectedParticleCode{"cfgSelectedParticleCode", 111, "PDG code of the particle to be investigated"}; Configurable cfgMaxZVertex{"cfgMaxZVertex", 10, "Maximum absolute z-vertex distance (cm)"}; - Configurable cfgRapidityCut{"cfgRapidityCut", 0.8, "Maximum absolute rapditity of selected generated particles"}; - ConfigurableAxis cfgMultiplicityBinning{"cfgMultiplicityBinning", {1000, 0, 10000}, "Binning used for the binning of the number of particles in the event"}; + Configurable cfgRapidityCut{"cfgRapidityCut", 0.8, "Maximum absolute rapidity of selected generated particles"}; expressions::Filter zVertexFilter = aod::mccollision::posZ < cfgMaxZVertex && aod::mccollision::posZ > -cfgMaxZVertex; void init(InitContext const&) { - mHistManager.add("Multiplicity", "Number of generated particles per MC collision;#bf{#it{N} (Multiplicity)};#bf{#it{N}_{MC collisions}}", HistType::kTH1F, {cfgMultiplicityBinning}); - mHistManager.add("YieldVsMultiplicity", "pT of selected particles in all MC collisions vs number of all generated particles in event;#bf{#it{p}_{T} (GeV/#it{c})};#bf{#it{N} (Multiplicity)};#bf{#it{N}}", HistType::kTH2F, {{200, 0, 20}, cfgMultiplicityBinning}); + mHistManager.add("Yield", Form("Yield of primary selected particles in all MC collisions (PDG code %d);#bf{#it{p}_{T} (GeV/#it{c})};#bf{#it{N}}", cfgSelectedParticleCode.value), HistType::kTH1D, {{250, 0, 25}}); + mHistManager.add("FullYield", Form("Yield of selected particles in all MC collisions (PDG code %d);#bf{#it{p}_{T} (GeV/#it{c})};#bf{#it{N}}", cfgSelectedParticleCode.value), HistType::kTH1D, {{250, 0, 25}}); + mHistManager.add("MCGenXSec", "Generated cross section = GetBinContent(1)/GetBinContent(2)", HistType::kTH1D, {{2, 0, 2}}); + auto hX = mHistManager.get(HIST("MCGenXSec")); + hX->GetXaxis()->SetBinLabel(1, "#sigma_{gen} (b)"); + hX->GetXaxis()->SetBinLabel(2, "nStreams"); + hX->SetBinContent(2, 1); // Unity to enable normalization by number of streams + hX->SetBinError(2, 0); + mHistManager.add("RapidityCut", "#Delta y = GetBinContent(1)/GetBinContent(2)", HistType::kTH1D, {{2, 0, 2}}); + auto hY = mHistManager.get(HIST("RapidityCut")); + hY->GetXaxis()->SetBinLabel(1, "2#Delta y"); + hY->GetXaxis()->SetBinLabel(2, "nStreams"); + hY->SetBinContent(1, 2 * cfgRapidityCut); + hY->SetBinContent(2, 1); // Unity to enable normalization by number of streams + hY->SetBinError(2, 0); + mHistManager.add("EventWeight", "MC collision weight;Event weight;#bf{#it{N}_{MC Collisions}}", HistType::kTH1D, {{1000, 0, 10}}); + mHistManager.add("MCGenXSecDistribution", "Generated cross section distribution;#sigma_{gen} (b);#bf{#it{N}_{MC Collisions}}", HistType::kTH1D, {{20000, 0, 2}}); + mHistManager.add("MCGenXSecErrDistribution", "Generated cross section error distribution;#sigma_{gen}^{err} (b);#bf{#it{N}_{MC Collisions}}", HistType::kTH1D, {{20000, 0, 2}}); + mHistManager.add("EventWeightSum", "Sum of event weights", HistType::kTH1D, {{2, 0., 2.}}, true); + auto hE = mHistManager.get(HIST("EventWeightSum")); + hE->GetXaxis()->SetBinLabel(1, "#Sigma w"); + hE->GetXaxis()->SetBinLabel(2, "N_{MC Collisions}"); + mHistManager.add("zVertex", "zVertex position;#bf{z (cm)};#bf{#it{N}_{MC Collisions}}", HistType::kTH1D, {{500, -25., 25.}}, true); } - void process(soa::Filtered::iterator const&, aod::McParticles const& mcParticles) + void process(soa::Filtered::iterator const& mcCollision, aod::McParticles const& mcParticles, aod::HepMCXSections const& crossSections) { - int nParticles = mcParticles.size(); - mHistManager.fill(HIST("Multiplicity"), nParticles); + const double w = mcCollision.weight(); + mHistManager.fill(HIST("EventWeight"), w); + mHistManager.fill(HIST("EventWeightSum"), 0.5, w); // Fill with event weight (used for normalization) + mHistManager.fill(HIST("EventWeightSum"), 1.5, 1); // Fill with number of MC collisions + mHistManager.fill(HIST("zVertex"), mcCollision.posZ(), w); + if (crossSections.size() == 1) { + auto const xs = crossSections.begin(); + const double sigmaGen = xs.xsectGen() * 1E-12; // From pb to b + const double sigmaGenErr = xs.xsectErr() * 1E-12; // From pb to b + mHistManager.fill(HIST("MCGenXSecDistribution"), sigmaGen); + mHistManager.fill(HIST("MCGenXSecErrDistribution"), sigmaGenErr); + + auto hX = mHistManager.get(HIST("MCGenXSec")); + hX->SetBinContent(1, sigmaGen); // Set bin content to latest generated cross section and uncertainty + hX->SetBinError(1, sigmaGenErr); + } else { + LOG(warn) << "Multiple cross sections found: " << crossSections.size(); + } for (auto& mcParticle : mcParticles) { if (mcParticle.pdgCode() == cfgSelectedParticleCode && std::abs(mcParticle.y()) < cfgRapidityCut) { - mHistManager.fill(HIST("YieldVsMultiplicity"), mcParticle.pt(), nParticles); + mHistManager.fill(HIST("FullYield"), mcParticle.pt(), w); + bool isPrimary = mcParticle.isPhysicalPrimary() || mcParticle.producedByGenerator(); + bool isFromWD = (aod::pwgem::photonmeson::utils::mcutil::IsFromWD(mcCollision, mcParticle, mcParticles)) > 0; + if (isPrimary && !isFromWD) { + mHistManager.fill(HIST("Yield"), mcParticle.pt(), w); + } } } } diff --git a/PWGEM/PhotonMeson/Tasks/mesonSignalLoss.cxx b/PWGEM/PhotonMeson/Tasks/mesonSignalLoss.cxx new file mode 100644 index 00000000000..172e093488a --- /dev/null +++ b/PWGEM/PhotonMeson/Tasks/mesonSignalLoss.cxx @@ -0,0 +1,436 @@ +// Copyright 2019-2020 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. +// +// ======================== +// +// Calculate signal and event loss for neutral mesons and photons +// Joshua König, joshua.konig@cern.ch + +#include "PWGEM/PhotonMeson/DataModel/EventTables.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/CCDB/TriggerAliases.h" +#include "Common/DataModel/EventSelection.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; + +struct MesonSignalLoss { + + using MyBCs = soa::Join; + + using RecoCollisions = soa::Join; + using EventAccBits = o2::aod::pmevsel::EventAcceptanceBits; + + enum MultiplicityEstimator { kCentralBarrel, + kFT0A, + kFT0C }; + + // stage kStageHasReco + k means "passed event cut k of EventAccBits" + enum Stage { kStageAllMC = 0, + kStageTVX, + kStageHasReco }; + + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; + Preslice mcCollisionsPerBC = aod::mccollision::bcId; + Preslice particlesPerMCCollision = aod::mcparticle::mcCollisionId; + PresliceUnsorted recoCollisionsPerMCCollision = aod::mccollisionlabel::mcCollisionId; + + struct : ConfigurableGroup { + std::string prefix = "axisConfig"; + ConfigurableAxis cfgPtAxis{"cfgPtAxis", {400, 0.f, 20.f}, "pT axis for generated particles"}; + ConfigurableAxis cfgMultiplicityAxis{"cfgMultiplicityAxis", {200, -0.5f, 199.5f}, "axis for true charged-particle multiplicity"}; + } axisConfig; + + struct : ConfigurableGroup { + std::string prefix = "particleConfig"; + Configurable cfgRapidity{"cfgRapidity", 0.8, "rapidity selection"}; + Configurable cfgSecondaryDecayLength{"cfgSecondaryDecayLength", 1.f, "Minimum ancestor decay length (cm) to classify a particle as secondary"}; + Configurable cfgEnableMultiplicityHistograms{"cfgEnableMultiplicityHistograms", false, "enable generated-particle and event-stage histograms versus true charged-particle multiplicity"}; + Configurable cfgMultiplicityEstimator{"cfgMultiplicityEstimator", "centralBarrel", "eta range for true multiplicity: centralBarrel, FT0A, or FT0C"}; + } particleConfig; + + struct : ConfigurableGroup { + std::string prefix = "rctConfig"; + Configurable cfgRequireGoodRCT{"cfgRequireGoodRCT", false, "require good detector flag in run condtion table"}; + Configurable cfgRCTLabel{"cfgRCTLabel", "CBT_hadronPID", "select 1 [CBT, CBT_hadronPID, CBT_muon_glo] see O2Physics/Common/CCDB/RCTSelectionFlags.h"}; + Configurable cfgCheckZDC{"cfgCheckZDC", false, "set ZDC flag for PbPb"}; + Configurable cfgTreatLimitedAcceptanceAsBad{"cfgTreatLimitedAcceptanceAsBad", false, "reject all events where the detectors relevant for the specified Runlist are flagged as LimitedAcceptance"}; + } rctConfig; + + struct : ConfigurableGroup { + std::string prefix = "eventCuts"; + Configurable cfgZvtxMin{"cfgZvtxMin", -1e+10, "min. Zvtx"}; + Configurable cfgZvtxMax{"cfgZvtxMax", 1e+10, "max. Zvtx"}; + Configurable cfgRequireFT0AND{"cfgRequireFT0AND", false, "require FT0AND in event cut"}; + Configurable cfgRequireNoTFB{"cfgRequireNoTFB", false, "require No time frame border in event cut"}; + Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", false, "require no ITS readout frame border in event cut"}; + Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", false, "require no same bunch pileup in event cut"}; + Configurable cfgRequireGoodZvtxFT0vsPV{"cfgRequireGoodZvtxFT0vsPV", false, "require good Zvtx between FT0 vs. PV in event cut"}; + Configurable cfgTrackOccupancyMin{"cfgTrackOccupancyMin", -2, "min. track occupancy"}; + Configurable cfgTrackOccupancyMax{"cfgTrackOccupancyMax", 1000000000, "max. track occupancy"}; + Configurable cfgFT0COccupancyMin{"cfgFT0COccupancyMin", -2, "min. occupancy"}; + Configurable cfgFT0COccupancyMax{"cfgFT0COccupancyMax", 1000000000, "max. occupancy"}; + Configurable cfgRequireNoCollInTimeRangeStandard{"cfgRequireNoCollInTimeRangeStandard", false, "require no collision in time range standard"}; + Configurable cfgRequireTVXinEMC{"cfgRequireTVXinEMC", false, "require kTVXinEMC (only for EMC analyses)"}; + Configurable cfgRequireSel8{"cfgRequireSel8", false, "require sel8 condition"}; + } eventCuts; + + Service pdgDB{}; + o2::aod::rctsel::RCTFlagsChecker rctChecker; + MultiplicityEstimator multiplicityEstimator{kCentralBarrel}; + + /// \brief Creates the histograms used for signal-loss and event-selection analysis. + void Inithistograms() + { + registry.add("hBCCounter", "hBCCounter;;#it{N}_{BC}", kTH1F, {{3, -0.5, 3 - 0.5}}); + auto h = registry.get(HIST("hBCCounter")); + h->GetXaxis()->SetBinLabel(1, "All BCs"); + h->GetXaxis()->SetBinLabel(2, "kTVX fired"); + h->GetXaxis()->SetBinLabel(3, "kTVX fired && has MC collision"); + + registry.add("hMCCollStage", "hMCCollStage;;#it{N}_{MC coll}", kTH1F, {{20, -0.5, 20 - 0.5}}); + auto hStage = registry.get(HIST("hMCCollStage")); + hStage->GetXaxis()->SetBinLabel(kStageAllMC + 1, "all MC coll"); + hStage->GetXaxis()->SetBinLabel(kStageTVX + 1, "kTVX fired"); + hStage->GetXaxis()->SetBinLabel(kStageHasReco + 1, "has reco coll"); + for (int cut = 1; cut < EventAccBits::kSize; ++cut) { + hStage->GetXaxis()->SetBinLabel(kStageHasReco + cut + 1, o2::aod::pmevsel::vecEventAccBitNames[cut].c_str()); + } + + registry.add("QC/hNRecoPerMCColl", "reco collisions per MC collision;N_{reco};N_{MC coll}", kTH1F, {{10, -0.5, 9.5}}); + registry.add("QC/hNSelectedRecoPerMCColl", "selected reco collisions per MC collision;N_{sel};N_{MC coll}", kTH1F, {{10, -0.5, 9.5}}); + registry.add("QC/hNContribVsOrder", "N contributors;order in matched list;N_{contrib}", kTH2F, {{10, -0.5, 9.5}, {200, 0, 200}}); + + const AxisSpec ptAxis{axisConfig.cfgPtAxis, "p_{T} (GeV/c)"}; + const AxisSpec multAxis{axisConfig.cfgMultiplicityAxis, "N_{ch}^{true}"}; + registry.add("Generated/Pi0/hGeneratedParticlePt", "Generated Pi0;p_{T} (GeV/c);selection stage", kTH2F, {ptAxis, {20, -0.5, 20 - 0.5}}); + registry.add("Generated/Eta/hGeneratedParticlePt", "Generated Eta;p_{T} (GeV/c);selection stage", kTH2F, {ptAxis, {20, -0.5, 20 - 0.5}}); + registry.add("Generated/Gamma/hGeneratedParticlePt", "Generated Gamma;p_{T} (GeV/c);selection stage", kTH2F, {ptAxis, {20, -0.5, 20 - 0.5}}); + if (particleConfig.cfgEnableMultiplicityHistograms) { + registry.add("Generated/Multiplicity/hMCCollStage", "MC collision stages vs. true multiplicity;N_{ch}^{true};selection stage", kTH2F, {multAxis, {20, -0.5, 20 - 0.5}}); + registry.add("Generated/Multiplicity/Pi0/hGeneratedParticlePt", "Generated Pi0;p_{T} (GeV/c);N_{ch}^{true};selection stage", kTH3F, {ptAxis, multAxis, {20, -0.5, 20 - 0.5}}); + registry.add("Generated/Multiplicity/Eta/hGeneratedParticlePt", "Generated Eta;p_{T} (GeV/c);N_{ch}^{true};selection stage", kTH3F, {ptAxis, multAxis, {20, -0.5, 20 - 0.5}}); + registry.add("Generated/Multiplicity/Gamma/hGeneratedParticlePt", "Generated Gamma;p_{T} (GeV/c);N_{ch}^{true};selection stage", kTH3F, {ptAxis, multAxis, {20, -0.5, 20 - 0.5}}); + } + } + + /// \brief Configures the multiplicity estimator and initializes histograms. + /// \param[in] context Workflow initialization context. + void init([[maybe_unused]] InitContext& context) + { + // Init multiplicity estimator (only relevant for multiplicity dependent analyses) + if (particleConfig.cfgMultiplicityEstimator.value == "centralBarrel") { + multiplicityEstimator = kCentralBarrel; + } else if (particleConfig.cfgMultiplicityEstimator.value == "FT0A") { + multiplicityEstimator = kFT0A; + } else if (particleConfig.cfgMultiplicityEstimator.value == "FT0C") { + multiplicityEstimator = kFT0C; + } else if (particleConfig.cfgEnableMultiplicityHistograms) { + LOGF(fatal, "Unknown cfgMultiplicityEstimator '%s'; use centralBarrel, FT0A, or FT0C", particleConfig.cfgMultiplicityEstimator.value.c_str()); + } + if (rctConfig.cfgRequireGoodRCT) { + rctChecker.init(rctConfig.cfgRCTLabel.value, rctConfig.cfgCheckZDC.value, rctConfig.cfgTreatLimitedAcceptanceAsBad.value); + } + Inithistograms(); + } + + /// \brief Checks whether eta lies in the configured multiplicity-estimator range. + /// \param[in] eta Pseudorapidity to check. + /// \return True if eta is inside the configured range, otherwise false. + bool isInMultiplicityEtaRange(float eta) const + { + bool decision = false; + if (multiplicityEstimator == kCentralBarrel) { + decision = std::abs(eta) < 0.8f; // o2-linter: disable=magic-number (rapidity range of central barrel) + } else if (multiplicityEstimator == kFT0A) { + decision = 3.5f < eta && eta < 4.9f; // o2-linter: disable=magic-number (rapidity range of FT0) + } else if (multiplicityEstimator == kFT0C) { + decision = -3.3f < eta && eta < -2.1f; // o2-linter: disable=magic-number (rapidity range of FT0) + } + return decision; + } + + /// \brief Counts charged physical-primary particles in the configured eta range. + /// \param[in] particles Particles to include in the multiplicity count. + /// \return Number of charged physical-primary particles in range. + template + int countTrueMultiplicity(TParticles const& particles) const + { + int multiplicity = 0; + for (const auto& particle : particles) { + if (!particle.isPhysicalPrimary() || !isInMultiplicityEtaRange(particle.eta())) { + continue; + } + const auto* pdgParticle = pdgDB->GetParticle(particle.pdgCode()); + if (pdgParticle && std::abs(pdgParticle->Charge()) >= 1.) { // o2-linter: disable=magic-number (check that particle has electric charge) + ++multiplicity; + } + } + return multiplicity; + } + + /// \brief Determines whether a particle has a secondary-production origin. + /// \param[in] particle Particle whose origin is checked. + /// \param[in] mcParticles MC-particle table used to trace the particle ancestry. + /// \return True if the particle is classified as secondary, otherwise false. + bool isSecondary(aod::McParticle const& particle, aod::McParticles const& mcParticles) const + { + std::vector particlesToCheck{particle.globalIndex()}; + std::unordered_set checkedParticles; + auto currentParticle = mcParticles.begin(); + auto mother = mcParticles.begin(); + + while (!particlesToCheck.empty()) { + const auto particleIndex = particlesToCheck.back(); + particlesToCheck.pop_back(); + if (particleIndex < 0 || particleIndex >= mcParticles.size() || !checkedParticles.insert(particleIndex).second) { + continue; + } + + currentParticle.setCursor(particleIndex); + const auto process = currentParticle.getProcess(); + const bool fromDecay = currentParticle.producedByGenerator() || process == TMCProcess::kPDecay || process == TMCProcess::kPRadDecay; + if (!currentParticle.producedByGenerator() && !fromDecay) { + return true; + } + + for (const auto motherIndex : currentParticle.mothersIds()) { + if (motherIndex < 0 || motherIndex >= mcParticles.size()) { + continue; + } + mother.setCursor(motherIndex); + const float decayLength = std::sqrt(std::pow(currentParticle.vx() - mother.vx(), 2) + std::pow(currentParticle.vy() - mother.vy(), 2) + std::pow(currentParticle.vz() - mother.vz(), 2)); + if (fromDecay && decayLength > particleConfig.cfgSecondaryDecayLength.value) { + return true; + } + particlesToCheck.push_back(motherIndex); + } + } + return false; + } + + /// \brief Fills generated-particle histograms for the requested selection stages. + /// \param[in] particlesInMCCollision Particles associated with the MC collision. + /// \param[in] mcParticles MC-particle table used to inspect particle ancestry. + /// \param[in] selectionStage First selection stage to fill. + /// \param[in] selectionStageMax Last selection stage to fill. + /// \param[in] trueMultiplicity True charged-particle multiplicity of the collision. + template + void fillGeneratedParticles(TParticles const& particlesInMCCollision, aod::McParticles const& mcParticles, int selectionStage, int selectionStageMax, int trueMultiplicity) + { + if (selectionStageMax < selectionStage) { + selectionStageMax = selectionStage; + } + for (const auto& particle : particlesInMCCollision) { + if (std::abs(particle.y()) > particleConfig.cfgRapidity) { + continue; + } + if (isSecondary(particle, mcParticles)) { + continue; + } + for (int iSel = selectionStage; iSel <= selectionStageMax; ++iSel) { + switch (particle.pdgCode()) { + case PDG_t::kPi0: + registry.fill(HIST("Generated/Pi0/hGeneratedParticlePt"), particle.pt(), iSel); // also add rapdidity?? + if (particleConfig.cfgEnableMultiplicityHistograms) { + registry.fill(HIST("Generated/Multiplicity/Pi0/hGeneratedParticlePt"), particle.pt(), trueMultiplicity, iSel); + } + break; + case o2::constants::physics::Pdg::kEta: + registry.fill(HIST("Generated/Eta/hGeneratedParticlePt"), particle.pt(), iSel); // also add rapdidity?? + if (particleConfig.cfgEnableMultiplicityHistograms) { + registry.fill(HIST("Generated/Multiplicity/Eta/hGeneratedParticlePt"), particle.pt(), trueMultiplicity, iSel); + } + break; + case PDG_t::kGamma: + registry.fill(HIST("Generated/Gamma/hGeneratedParticlePt"), particle.pt(), iSel); // also add rapdidity?? + if (particleConfig.cfgEnableMultiplicityHistograms) { + registry.fill(HIST("Generated/Multiplicity/Gamma/hGeneratedParticlePt"), particle.pt(), trueMultiplicity, iSel); + } + break; + default: + break; + } + } + } + } + + /// \brief Returns the event-acceptance bit for the first failed collision selection. + /// \param[in] collision Reconstructed collision to evaluate. + /// \return Event-acceptance bit identifying the first failed cut, or kSize if accepted. + template + int isSelectedEvent(TCollision const& collision) + { + if (!collision.has_mcCollision()) { + return EventAccBits::kHasMCColl; + } + + if (collision.posZ() < eventCuts.cfgZvtxMin || eventCuts.cfgZvtxMax < collision.posZ()) { + return EventAccBits::kGoodZVtx; + } + + if (eventCuts.cfgRequireFT0AND && !collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { + return EventAccBits::kIsFT0AND; + } + + if (eventCuts.cfgRequireNoTFB && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + return EventAccBits::kNoTFB; + } + + if (eventCuts.cfgRequireNoITSROFB && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + return EventAccBits::kITSROFB; + } + + if (eventCuts.cfgRequireNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return EventAccBits::kNoSameBunchPileUp; + } + + if (eventCuts.cfgRequireGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return EventAccBits::kGoodZVtxFTOPV; + } + + if (eventCuts.cfgRequireNoCollInTimeRangeStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + return EventAccBits::kNoCollInTimeRange; + } + + if (!(eventCuts.cfgTrackOccupancyMin <= collision.trackOccupancyInTimeRange() && collision.trackOccupancyInTimeRange() < eventCuts.cfgTrackOccupancyMax)) { + return EventAccBits::kGoodTrackOccupancy; + } + + if (!(eventCuts.cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange() && collision.ft0cOccupancyInTimeRange() < eventCuts.cfgFT0COccupancyMax)) { + return EventAccBits::kGoodFT0Occupancy; + } + + if (eventCuts.cfgRequireTVXinEMC && !collision.alias_bit(triggerAliases::kTVXinEMC)) { + return EventAccBits::kTVXInEMC; + } + + // if constexpr (std::is_same_v, MyCollisions_Cent::iterator>) { + // std::array centralities = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + // if (centralities[cfgCentEstimator] < cfgCentMin || cfgCentMax < centralities[cfgCentEstimator]) { + // return false; + // } + // } + + if (rctConfig.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { + return EventAccBits::kGoodRCT; + } + + if (eventCuts.cfgRequireSel8 && !collision.sel8()) { + return EventAccBits::kGoodSel8; + } + + return EventAccBits::kSize; + } + + /// \brief Processes MC bunch crossings and fills generated signal-loss histograms. + /// \param[in] bcs Bunch crossings to process. + /// \param[in] collisions Reconstructed collisions and their MC labels. + /// \param[in] mcCollisions MC collisions to process. + /// \param[in] mcParticles MC particles used for multiplicity and ancestry checks. + void processMBMC(MyBCs const& bcs, RecoCollisions const& collisions, aod::McCollisions const& mcCollisions, aod::McParticles const& mcParticles) + { + // loop over all bcs + for (const auto& bc : bcs) { + registry.fill(HIST("hBCCounter"), 0); + + // Check if TVX trigger fired in that bc + if (bc.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { + registry.fill(HIST("hBCCounter"), 1); + } + + auto mcCollisionsInBC = mcCollisions.sliceBy(mcCollisionsPerBC, bc.globalIndex()); + // check if has at least one mc collision + if (bc.selection_bit(o2::aod::evsel::kIsTriggerTVX) && mcCollisionsInBC.size() > 0) { + registry.fill(HIST("hBCCounter"), 2); + } + + // loop over all mc collisions in the current bc (should be only 1 mc collision in most cases) + for (const auto& mcCollision : mcCollisionsInBC) { + int lastStage = kStageAllMC; + auto particlesInMCCollision = mcParticles.sliceBy(particlesPerMCCollision, mcCollision.globalIndex()); + const int trueMultiplicity = particleConfig.cfgEnableMultiplicityHistograms ? countTrueMultiplicity(particlesInMCCollision) : 0; + + if (bc.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { + lastStage = kStageTVX; + + auto matchedRecoCollisions = collisions.sliceBy(recoCollisionsPerMCCollision, mcCollision.globalIndex()); + registry.fill(HIST("QC/hNRecoPerMCColl"), matchedRecoCollisions.size()); + + // if at least one reco collision is found for the current mc collision, loop over all reco collisions and check if the collision is selected + if (matchedRecoCollisions.size() > 0) { + + int bestReject = EventAccBits::kHasMCColl; + int nSelected = 0; + int order = 0; + for (const auto& recColl : matchedRecoCollisions) { + // this function returns if the collision is accepted or not. If not, the reason is encoded in the return value + const int reject = isSelectedEvent(recColl); + // furthest-passing matched collision decides, so split vertices are not double counted + bestReject = std::max(bestReject, reject); + if (reject == EventAccBits::kSize) { + ++nSelected; + } + // Fill how many contributers to the primary vertex are in the current collision (QA info) + registry.fill(HIST("QC/hNContribVsOrder"), order++, recColl.numContrib()); + } + // Fill number of collisions that are assigned to the current mc collision + registry.fill(HIST("QC/hNSelectedRecoPerMCColl"), nSelected); + // this variable is used to keep track at which stage the collision was rejected or if it was accepted. It is used later for filling the generated meson distributions + lastStage = kStageHasReco + (bestReject - 1); + } + } + // Fill the particles of interested as a function of the reject reason (lastStage) + fillGeneratedParticles(particlesInMCCollision, mcParticles, kStageAllMC, lastStage, trueMultiplicity); + for (int stage = kStageAllMC; stage <= lastStage; ++stage) { + registry.fill(HIST("hMCCollStage"), stage); + if (particleConfig.cfgEnableMultiplicityHistograms) { + registry.fill(HIST("Generated/Multiplicity/hMCCollStage"), trueMultiplicity, stage); + } + } + } + } + } + PROCESS_SWITCH(MesonSignalLoss, processMBMC, "Process MC bunch crossings for signal and event loss", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{adaptAnalysisTask(context)}; +} diff --git a/PWGEM/PhotonMeson/Tasks/pairQcTask.cxx b/PWGEM/PhotonMeson/Tasks/pairQcTask.cxx index d2d05d32ae5..5844fab6531 100644 --- a/PWGEM/PhotonMeson/Tasks/pairQcTask.cxx +++ b/PWGEM/PhotonMeson/Tasks/pairQcTask.cxx @@ -18,7 +18,6 @@ #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" #include "PWGEM/PhotonMeson/Utils/AnalyticV0.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" #include "PWGEM/PhotonMeson/Utils/MCUtilities.h" #include "PWGEM/PhotonMeson/Utils/PairUtilities.h" @@ -389,7 +388,7 @@ struct PairQCTask { ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(&fRegistry); + fEMEventCut.addEventHistograms(&fRegistry); fRegistry.add("Photon/hPtEtaPhi", "selected V0 photons;p_{T} (GeV/c);#eta;#varphi (rad)", kTH3F, {{100, 0.f, 2.f}, {40, -0.8f, 0.8f}, {72, 0.f, 6.2832f}}, true); // o2-linter: disable=magic-number (axis definition) fRegistry.add("Photon/hNPhotonsPerEvent", "selected V0 photons per event;N_{#gamma};events", kTH1F, {{21, -0.5f, 20.5f}}, true); fRegistry.add("Photon/hV0LegClass", "leg classes of the selected V0s;;V0s", kTH1F, {makeAxisEnum(kNV0Classes, kV0ClassTitle)}, true); @@ -442,6 +441,8 @@ struct PairQCTask { fEMEventCut.SetRequireGoodITSLayer3(eventcuts.cfgRequireGoodITSLayer3); fEMEventCut.SetRequireGoodITSLayer0123(eventcuts.cfgRequireGoodITSLayer0123); fEMEventCut.SetRequireGoodITSLayersAll(eventcuts.cfgRequireGoodITSLayersAll); + fEMEventCut.SetTrackOccupancyRange(eventcuts.cfgTrackOccupancyMin, eventcuts.cfgTrackOccupancyMax); + fEMEventCut.SetFT0COccupancyRange(eventcuts.cfgFT0COccupancyMin, eventcuts.cfgFT0COccupancyMax); } void definePCMCut() @@ -1004,7 +1005,7 @@ struct PairQCTask { } if (mcutil::classifyDupPair(a.mc, b.mc) == mcutil::DupClass::SameMcPhoton) { // 0 no split leg, 1 split e+, 2 split e-, 3 both - const float splitType = static_cast((split[0] ? 1 : 0) + (split[1] ? 2 : 0)); // o2-linter: disable=magic-number (bit code, see booking) + const auto splitType = static_cast((split[0] ? 1 : 0) + (split[1] ? 2 : 0)); // o2-linter: disable=magic-number (bit code, see booking) fRegistry.fill(HIST("Dedup/MC/hSamePhotonDuplicateType"), splitType); fRegistry.fill(HIST("Dedup/MC/hSamePhotonDuplicateType_vs_Flagged"), splitType, flagged ? 1.f : 0.f); fRegistry.fill(HIST("Dedup/MC/hDupTrackClassMatrix"), static_cast(a.dedupCand.nITSTPC), static_cast(b.dedupCand.nITSTPC)); @@ -1413,13 +1414,12 @@ struct PairQCTask { centralitySelection.cfgCentMax < cent[centralitySelection.cfgCentEstimator]) { continue; } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(&fRegistry, collision, 1.f); + fEMEventCut.fillEventInfo<0>(&fRegistry, collision, 1.f); if (!fEMEventCut.IsSelected(collision)) { continue; } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(&fRegistry, collision, 1.f); - fRegistry.fill(HIST("Event/before/hCollisionCounter"), 12.0); // o2-linter: disable=magic-number (counter bin, as in photonhbt) - fRegistry.fill(HIST("Event/after/hCollisionCounter"), 12.0); // o2-linter: disable=magic-number (counter bin, as in photonhbt) + fEMEventCut.fillEventInfo<1>(&fRegistry, collision, 1.f); + fEMEventCut.fillAccepted(&fRegistry); auto photonsColl = photons.sliceBy(perCollisionPCM, collision.globalIndex()); collectPhotons(photonsColl, mcParticles, collision.posZ(), cands); @@ -1454,7 +1454,7 @@ struct PairQCTask { PROCESS_SWITCH(PairQCTask, processMC, "pair QC on MC", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(context)}; } diff --git a/PWGEM/PhotonMeson/Tasks/pcmQC.cxx b/PWGEM/PhotonMeson/Tasks/pcmQC.cxx index 496cf7a8067..112a686abc9 100644 --- a/PWGEM/PhotonMeson/Tasks/pcmQC.cxx +++ b/PWGEM/PhotonMeson/Tasks/pcmQC.cxx @@ -85,7 +85,7 @@ using namespace o2::aod::pwgem::photon; using namespace o2::aod::pwgem::photonmeson::utils::mcutil; using namespace o2::aod::pwgem::dilepton::utils::mcutil; -using MyCollisions = soa::Join; +using MyCollisions = soa::Join; using MyCollision = MyCollisions::iterator; using MyV0Photons = soa::Join; @@ -95,7 +95,7 @@ using MyV0PhotonsML = soa::Join; using MyV0PhotonML = MyV0PhotonsML::iterator; // MC Joins -using MyCollisionsMC = soa::Join; +using MyCollisionsMC = soa::Join; using MyCollisionMC = MyCollisionsMC::iterator; using MyMCCollisions = soa::Join; @@ -276,8 +276,15 @@ struct PCMQC { } // Fetch magnetic field from ccdb for current collision - d_bz = collision.grpMagField().getNominalL3Field(); - LOG(info) << "Retrieved GRP for timestamp " << collision.timestamp() << " with magnetic field of " << d_bz << " kZG"; + // d_bz = collision.grpMagField().getNominalL3Field(); + // LOG(info) << "Retrieved GRP for timestamp " << collision.timestamp() << " with magnetic field of " << d_bz << " kZG"; + auto* grpmag = ccdb->getForRun("GLO/Config/GRPMagField", collision.runNumber()); + if (grpmag == nullptr) { + LOGF(fatal, "Could not retrieve GRPMagField for run %d", collision.runNumber()); + return; + } + d_bz = grpmag->getNominalL3Field(); + LOG(info) << "Retrieved GRP for run " << collision.runNumber() << " with magnetic field of " << d_bz << " kZG"; fV0PhotonCut.SetD_Bz(d_bz); } diff --git a/PWGEM/PhotonMeson/Tasks/photonMcTask.cxx b/PWGEM/PhotonMeson/Tasks/photonMcTask.cxx new file mode 100644 index 00000000000..6378574b705 --- /dev/null +++ b/PWGEM/PhotonMeson/Tasks/photonMcTask.cxx @@ -0,0 +1,649 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file photonMcTask.cxx +/// \brief Analysis task for to obtain photon acceptance, efficiency and purity for photons from PCM and/or EMCal +/// \author M. Hemmer, marvin.hemmer@cern.ch + +#include "PWGEM/Dilepton/Utils/MCUtilities.h" +#include "PWGEM/PhotonMeson/Core/EMBitFlags.h" +#include "PWGEM/PhotonMeson/Core/EMCPhotonCut.h" +#include "PWGEM/PhotonMeson/Core/EMPhotonEventCut.h" +#include "PWGEM/PhotonMeson/Core/V0PhotonCut.h" +#include "PWGEM/PhotonMeson/DataModel/EventTables.h" +#include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" +#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; +using namespace o2::aod::pwgem::photon; + +enum CentralityEstimator { + None = 0, + CFT0A = 1, + CFT0C = 2, + CFT0M = 3, + NCentralityEstimators = 4 +}; + +enum class MapLevel { + kGood = 1, + kNoBad = 2, + kInEMC = 3, + kAll = 4 +}; + +// all values here have a little bit of headroom +constexpr float EmcalMaxEta = 0.75f; // it is actuall 0.7 +constexpr float DcalMinEta = 0.2f; // it is actuall 0.23 + +constexpr float EmcalLowerPhi = 1.4f - 0.01f; +constexpr float EmcalUpperPhi = 3.26f + 0.01f; +constexpr float DcalLowerPhi = 4.54f + 0.01f; +constexpr float DcalUpperPhosGap = 5.58f + 0.01f; +constexpr float DcalUpperPhi = 5.70f + 0.01f; + +struct PhotonMcTask { + Configurable cfgMaxEta{"cfgMaxEta", 0.9f, "max. eta for generated particles"}; + + // configurable axis + ConfigurableAxis thnConfigAxisPt{"thnConfigAxisPt", {400, 0., 20.}, "pT axis for the neutral meson"}; + ConfigurableAxis thnConfigAxisERelative{"thnConfigAxisERelative", {600, -1., 5.}, "(E rec - E true) / E true axis"}; + ConfigurableAxis thnConfigAxisPRelative{"thnConfigAxisPRelative", {300, -1., 2.}, "(P rec - P true) / P true axis"}; + ConfigurableAxis thnConfigAxisEtaRelative{"thnConfigAxisEtaRelative", {300, -1., 2.}, "(eta rec - eta true) / eta true axis"}; + ConfigurableAxis thnConfigAxisPhiRelative{"thnConfigAxisPhiRelative", {300, -1., 2.}, "(phi rec - phi true) / phi true axis"}; + ConfigurableAxis thnConfigAxisCent{"thnConfigAxisCent", {20, 0., 100.}, "centrality axis for the current event"}; + ConfigurableAxis thnConfigAxisMult{"thnConfigAxisMult", {60, 0., 60000.}, "multiplicity axis for the current event"}; + Configurable useCent{"useCent", false, "flag to enable usage of centrality instead of multiplicity as axis."}; + + EMPhotonEventCut fEMEventCut; + struct : ConfigurableGroup { + std::string prefix = "eventcuts"; + Configurable cfgZvtxMax{"cfgZvtxMax", 10.f, "max. Zvtx"}; + Configurable cfgRequireSel8{"cfgRequireSel8", true, "require sel8 in event cut"}; + Configurable cfgRequireFT0AND{"cfgRequireFT0AND", true, "require FT0AND in event cut"}; + Configurable cfgRequireNoTFB{"cfgRequireNoTFB", false, "require No time frame border in event cut"}; + Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", false, "require no ITS readout frame border in event cut"}; + Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", false, "require no same bunch pileup in event cut"}; + Configurable cfgRequireVertexITSTPC{"cfgRequireVertexITSTPC", false, "require Vertex ITSTPC in event cut"}; // ITS-TPC matched track contributes PV. + Configurable cfgRequireGoodZvtxFT0vsPV{"cfgRequireGoodZvtxFT0vsPV", false, "require good Zvtx between FT0 vs. PV in event cut"}; + Configurable cfgRequireEMCReadoutInMB{"cfgRequireEMCReadoutInMB", true, "require the EMC to be read out in an MB collision (kTVXinEMC)"}; + Configurable cfgRequireEMCHardwareTriggered{"cfgRequireEMCHardwareTriggered", false, "require the EMC to be hardware triggered (kEMC7 or kDMC7)"}; + Configurable cfgFT0COccupancyMin{"cfgFT0COccupancyMin", -1, "min. FT0C occupancy"}; + Configurable cfgFT0COccupancyMax{"cfgFT0COccupancyMax", 1000000000, "max. FT0C occupancy"}; + Configurable cfgMinCent{"cfgMinCent", 0, "min. centrality (%)"}; + Configurable cfgMaxCent{"cfgMaxCent", 90, "max. centrality (%)"}; + Configurable centEstimator{"centEstimator", 2, "Centrality estimation (FT0A: 1, FT0C: 2, FT0M: 3)"}; + Configurable onlyKeepWeightedEvents{"onlyKeepWeightedEvents", false, "flag to keep only weighted events (for JJ MCs) and remove all MB events (with weight = 1)"}; + } eventcuts; + + EMCPhotonCut fEMCCut; + struct : ConfigurableGroup { + std::string prefix = "emccuts"; + Configurable clusterDefinition{"clusterDefinition", "kV3MostSplitSmallestTimeDiff", "Clusterizer to be selected, e.g. V3Default"}; + Configurable cfgEMCminTime{"cfgEMCminTime", -25., "Minimum cluster time for EMCal time cut"}; + Configurable cfgEMCmaxTime{"cfgEMCmaxTime", +30., "Maximum cluster time for EMCal time cut"}; + Configurable cfgEMCminM02{"cfgEMCminM02", 0.1, "Minimum M02 for EMCal M02 cut"}; + Configurable cfgEMCmaxM02{"cfgEMCmaxM02", 0.7, "Maximum M02 for EMCal M02 cut"}; + Configurable cfgEMCminE{"cfgEMCminE", 0.7, "Minimum cluster energy for EMCal energy cut"}; + Configurable cfgEMCminNCell{"cfgEMCminNCell", 1, "Minimum number of cells per cluster for EMCal NCell cut"}; + Configurable> cfgEMCTMEta{"cfgEMCTMEta", {0.01f, 4.07f, -2.5f}, "|eta| <= [0]+(pT+[1])^[2] for EMCal primary track matching"}; + Configurable> cfgEMCTMPhi{"cfgEMCTMPhi", {0.015f, 3.65f, -2.f}, "|phi| <= [0]+(pT+[1])^[2] for EMCal primary track matching"}; + Configurable> emcSecTMEta{"emcSecTMEta", {0.01f, 4.07f, -2.5f}, "|eta| <= [0]+(pT+[1])^[2] for EMCal secondary track matching"}; + Configurable> emcSecTMPhi{"emcSecTMPhi", {0.015f, 3.65f, -2.f}, "|phi| <= [0]+(pT+[1])^[2] for EMCal secondary track matching"}; + Configurable cfgEMCEoverp{"cfgEMCEoverp", 1.75, "Minimum cluster energy over track momentum for EMCal primary track matching"}; + Configurable cfgEMCUseExoticCut{"cfgEMCUseExoticCut", true, "Flag to use the EMCal exotic cluster cut"}; + Configurable cfgEMCUseTM{"cfgEMCUseTM", false, "flag to use EMCal track matching cut or not"}; + Configurable emcUseSecondaryTM{"emcUseSecondaryTM", false, "flag to use EMCal secondary track matching cut or not"}; + Configurable cfgEnableQA{"cfgEnableQA", false, "flag to turn QA plots on/off"}; + } emccuts; + + V0PhotonCut fV0PhotonCut; + struct : o2::framework::ConfigurableGroup { + std::string prefix = "PCMcuts"; + o2::framework::Configurable requireV0WithITSTPC{"requireV0WithITSTPC", false, "flag to enforce V0s have ITS and TPC"}; + o2::framework::Configurable requireV0WithITSOnly{"requireV0WithITSOnly", false, "flag to select V0s with ITSonly tracks"}; + o2::framework::Configurable requireV0WithTPCOnly{"requireV0WithTPCOnly", false, "flag to select V0s with TPConly tracks"}; + o2::framework::Configurable minPtV0{"minPtV0", 0.1, "min pT for v0 photons at PV"}; + o2::framework::Configurable maxPtV0{"maxPtV0", 1e+10, "max pT for v0 photons at PV"}; + o2::framework::Configurable minEtaV0{"minEtaV0", -0.8, "min eta for v0 photons at PV"}; + o2::framework::Configurable maxEtaV0{"maxEtaV0", 0.8, "max eta for v0 photons at PV"}; + o2::framework::Configurable minRV0{"minRV0", 4.0, "min v0 radius"}; + o2::framework::Configurable maxRV0{"maxRV0", 90.0, "max v0 radius"}; + o2::framework::Configurable maxAlphaAP{"maxAlphaAP", 0.95, "max alpha for AP cut"}; + o2::framework::Configurable maxQtAP{"maxQtAP", 0.01, "max qT for AP cut"}; + o2::framework::Configurable minCosPA{"minCosPA", 0.999, "min V0 CosPA"}; + o2::framework::Configurable maxPCA{"maxPCA", 1.5, "max distance btween 2 legs"}; + o2::framework::Configurable maxChi2KF{"maxChi2KF", 1.e+10f, "max chi2/ndf with KF"}; + o2::framework::Configurable rejectV0onITSib{"rejectV0onITSib", true, "flag to reject V0s on ITSib"}; + o2::framework::Configurable applyPrefilter{"applyPrefilter", false, "flag to apply prefilter to V0"}; + + o2::framework::Configurable minNClusterTPC{"minNClusterTPC", 0, "min NCluster TPC"}; + o2::framework::Configurable minNCrossedRowsTPC{"minNCrossedRowsTPC", 40, "min ncrossed rows in TPC"}; + o2::framework::Configurable minNCrossedRowsOverFindableClustersTPC{"minNCrossedRowsOverFindableClustersTPC", 0.8f, "min fraction of crossed rows over findable clusters in TPC"}; + o2::framework::Configurable maxFracSharedClustersTPC{"maxFracSharedClustersTPC", 999.f, "max fraction of shared clusters in TPC"}; + o2::framework::Configurable minNClusterITS{"minNClusterITS", 0, "min NCluster ITS"}; + o2::framework::Configurable minMeanClusterSizeITSob{"minMeanClusterSizeITSob", 0.f, "min ITSob"}; + o2::framework::Configurable maxMeanClusterSizeITSob{"maxMeanClusterSizeITSob", 16.f, "max ITSob"}; + o2::framework::Configurable macChi2TPC{"macChi2TPC", 4.f, "max chi2/NclsTPC"}; + o2::framework::Configurable macChi2ITS{"macChi2ITS", 36.f, "max chi2/NclsITS"}; + o2::framework::Configurable minTPCNSigmaEl{"minTPCNSigmaEl", -3.0f, "min. TPC n sigma for electron"}; + o2::framework::Configurable maxTPCNSigmaEl{"maxTPCNSigmaEl", +3.0f, "max. TPC n sigma for electron"}; + o2::framework::Configurable disableITSOnly{"disableITSOnly", false, "flag to disable ITSonly tracks"}; + o2::framework::Configurable disableTPCOnly{"disableTPCOnly", false, "flag to disable TPConly tracks"}; + o2::framework::Configurable doQA{"doQA", false, "flag to set QA flag."}; + } pcmcuts; + + SliceCache cache; + + using EMCalPhotons = soa::Join; + using PcmPhotons = soa::Join; + + using PcmMcLegs = soa::Join; + + using Colls = soa::Join; + + using McColls = o2::soa::Join; + using McParticles = EMMCParticles; + + Partition mcPhotons = aod::mcparticle::pdgCode == static_cast(PDG_t::kGamma); + PresliceUnsorted perMcEvent = aod::emmcparticle::emmceventId; + + PresliceOptional perCollisionEMC = o2::aod::emccluster::pmeventId; + PresliceOptional perCollisionPCM = aod::v0photonkf::pmeventId; + PresliceOptional perEMCClusterMT = o2::aod::mintm::minClusterId; + PresliceOptional perEMCClusterMS = o2::aod::mintm::minClusterId; + + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; + + o2::framework::Service ccdb{}; + int mRunNumber{-1}; + float dBz{0.f}; + + void defineEMEventCut() + { + fEMEventCut = EMPhotonEventCut("fEMEventCut", "fEMEventCut"); + fEMEventCut.SetRequireSel8(eventcuts.cfgRequireSel8); + fEMEventCut.SetRequireFT0AND(eventcuts.cfgRequireFT0AND); + fEMEventCut.SetZvtxRange(-eventcuts.cfgZvtxMax, +eventcuts.cfgZvtxMax); + fEMEventCut.SetRequireNoTFB(eventcuts.cfgRequireNoTFB); + fEMEventCut.SetRequireNoITSROFB(eventcuts.cfgRequireNoITSROFB); + fEMEventCut.SetRequireNoSameBunchPileup(eventcuts.cfgRequireNoSameBunchPileup); + fEMEventCut.SetRequireVertexITSTPC(eventcuts.cfgRequireVertexITSTPC); + fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); + fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); + fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + fEMEventCut.SetFT0COccupancyRange(eventcuts.cfgFT0COccupancyMin, eventcuts.cfgFT0COccupancyMax); + } + + void defineEMCCut() + { + fEMCCut = EMCPhotonCut("fEMCCut", "fEMCCut"); + + fEMCCut.SetTrackMatchingEtaParams(emccuts.cfgEMCTMEta->at(0), emccuts.cfgEMCTMEta->at(1), emccuts.cfgEMCTMEta->at(2)); + fEMCCut.SetTrackMatchingPhiParams(emccuts.cfgEMCTMPhi->at(0), emccuts.cfgEMCTMPhi->at(1), emccuts.cfgEMCTMPhi->at(2)); + + fEMCCut.SetSecTrackMatchingEtaParams(emccuts.emcSecTMEta->at(0), emccuts.emcSecTMEta->at(1), emccuts.emcSecTMEta->at(2)); + fEMCCut.SetSecTrackMatchingPhiParams(emccuts.emcSecTMPhi->at(0), emccuts.emcSecTMPhi->at(1), emccuts.emcSecTMPhi->at(2)); + fEMCCut.SetMinEoverP(emccuts.cfgEMCEoverp); + + fEMCCut.SetMinE(emccuts.cfgEMCminE); + fEMCCut.SetMinNCell(emccuts.cfgEMCminNCell); + fEMCCut.SetM02Range(emccuts.cfgEMCminM02, emccuts.cfgEMCmaxM02); + fEMCCut.SetTimeRange(emccuts.cfgEMCminTime, emccuts.cfgEMCmaxTime); + fEMCCut.SetUseExoticCut(emccuts.cfgEMCUseExoticCut); + fEMCCut.SetClusterizer(emccuts.clusterDefinition); + fEMCCut.SetUseTM(emccuts.cfgEMCUseTM.value); // disables or enables TM + fEMCCut.SetUseSecondaryTM(emccuts.emcUseSecondaryTM.value); // disables or enables secondary TM + fEMCCut.SetDoQA(emccuts.cfgEnableQA.value); + } + + void definePCMCut() + { + fV0PhotonCut = V0PhotonCut("fV0PhotonCut", "fV0PhotonCut"); + + // for v0 + fV0PhotonCut.SetV0PtRange(pcmcuts.minPtV0, pcmcuts.maxPtV0); + fV0PhotonCut.SetV0EtaRange(pcmcuts.minEtaV0, pcmcuts.maxEtaV0); + fV0PhotonCut.SetMinCosPA(pcmcuts.minCosPA); + fV0PhotonCut.SetMaxPCA(pcmcuts.maxPCA); + fV0PhotonCut.SetMaxChi2KF(pcmcuts.maxChi2KF); + fV0PhotonCut.SetRxyRange(pcmcuts.minRV0, pcmcuts.maxRV0); + fV0PhotonCut.SetAPRange(pcmcuts.maxAlphaAP, pcmcuts.maxQtAP); + fV0PhotonCut.RejectITSib(pcmcuts.rejectV0onITSib); + + // for track + fV0PhotonCut.SetMinNClustersTPC(pcmcuts.minNClusterTPC); + fV0PhotonCut.SetMinNCrossedRowsTPC(pcmcuts.minNCrossedRowsTPC); + fV0PhotonCut.SetMinNCrossedRowsOverFindableClustersTPC(pcmcuts.minNCrossedRowsOverFindableClustersTPC); + fV0PhotonCut.SetMaxFracSharedClustersTPC(pcmcuts.maxFracSharedClustersTPC); + fV0PhotonCut.SetChi2PerClusterTPC(0.f, pcmcuts.macChi2TPC); + fV0PhotonCut.SetTPCNsigmaElRange(pcmcuts.minTPCNSigmaEl, pcmcuts.maxTPCNSigmaEl); + fV0PhotonCut.SetChi2PerClusterITS(0.f, pcmcuts.macChi2ITS); + fV0PhotonCut.SetNClustersITS(pcmcuts.minNClusterITS, 7); + fV0PhotonCut.SetMeanClusterSizeITSob(pcmcuts.minMeanClusterSizeITSob, pcmcuts.maxMeanClusterSizeITSob); + fV0PhotonCut.SetDisableITSonly(pcmcuts.disableITSOnly); + fV0PhotonCut.SetDisableTPConly(pcmcuts.disableTPCOnly); + fV0PhotonCut.SetRequireITSTPC(pcmcuts.requireV0WithITSTPC); + fV0PhotonCut.SetRequireITSonly(pcmcuts.requireV0WithITSOnly); + fV0PhotonCut.SetRequireTPConly(pcmcuts.requireV0WithTPCOnly); + + fV0PhotonCut.setDoQA(pcmcuts.doQA.value); + } + + void init(InitContext&) + { + mRunNumber = 0; + dBz = 0; + + defineEMEventCut(); + defineEMCCut(); + fEMCCut.addQAHistograms(®istry); + definePCMCut(); + fV0PhotonCut.addQAHistograms(®istry); + fEMEventCut.addEventHistograms(®istry); + + const AxisSpec thnAxisPt{thnConfigAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec thnAxisPtGen{thnConfigAxisPt, "#it{p}_{T,Gen} (GeV/#it{c})"}; + const AxisSpec thnAxisPtRec{thnConfigAxisPt, "#it{p}_{T,Rec} (GeV/#it{c})"}; + const AxisSpec thnAxisPGen{thnConfigAxisPt, "#it{p}_{Gen} (GeV/#it{c})"}; + const AxisSpec thnAxisPRec{thnConfigAxisPt, "#it{p}_{Rec} (GeV/#it{c})"}; + const AxisSpec thnAxisPRelative{thnConfigAxisPRelative, "#it{p}_{Rec} - #it{p}_{Gen} / #it{p}_{Gen}"}; + const AxisSpec thnAxisEGen{thnConfigAxisPt, "#it{E}_{Gen} (GeV)"}; + const AxisSpec thnAxisERec{thnConfigAxisPt, "#it{E}_{Rec} (GeV)"}; + const AxisSpec thnAxisERelative{thnConfigAxisERelative, "#it{E}_{Rec} - #it{E}_{Gen} / #it{E}_{Gen}"}; + + const AxisSpec thnAxisEtaRelative{thnConfigAxisEtaRelative, "#it{#eta}_{Rec} - #it{#eta}_{Gen} / #it{#eta}_{Gen}"}; + const AxisSpec thnAxisPhiRelative{thnConfigAxisPhiRelative, "#it{#varphi}_{Rec} - #it{#varphi}_{Gen} / #it{#varphi}_{Gen}"}; + + const AxisSpec thnAxisEtaGen{280, -0.7, 0.7, "#it{#eta}_{Gen}"}; + const AxisSpec thnAxisPhiGen{360, 0., o2::constants::math::TwoPI, "#it{#varphi}_{Gen} (rad)"}; + + AxisSpec thnAxisCentOrMult{1, 0., 1., "Centrality/Multiplicity"}; // placeholder, overwritten in init + if (useCent.value) { + // PbPb: use centrality + thnAxisCentOrMult = {thnConfigAxisCent, "Centrality (%)"}; + } else { + // pp: use multiplicity + thnAxisCentOrMult = {thnConfigAxisMult, "FT0C Multiplicity"}; + } + + registry.add("hGenerated", "generated photons pT vs cent/mult", HistType::kTH2D, {thnAxisPt, thnAxisCentOrMult}); + + if (doprocessEmcal || doprocessPcmEmcal) { + registry.add("EMCal/hAccepted", "EMCal accepted photon pt vs cent/mult", HistType::kTH2D, {thnAxisPt, thnAxisCentOrMult}); + registry.add("EMCal/hMeasured", "EMCal measured photon candidate pt vs cent/mult ", HistType::kTH2D, {thnAxisPt, thnAxisCentOrMult}); + registry.add("EMCal/hMeasuredTruePhoton", "EMCal measured and validated photon pt vs cent/mult ", HistType::kTH2D, {thnAxisPt, thnAxisCentOrMult}); + } + + if (doprocessPcm || doprocessPcmEmcal) { + registry.add("PCM/hAccepted", "PCM accepted photon pt vs cent/mult", HistType::kTH2D, {thnAxisPt, thnAxisCentOrMult}); + registry.add("PCM/hMeasured", "PCM measured photon candidate pt vs cent/mult ", HistType::kTH2D, {thnAxisPt, thnAxisCentOrMult}); + registry.add("PCM/hMeasuredTruePhoton", "PCM measured and validated photon pt vs cent/mult ", HistType::kTH2D, {thnAxisPt, thnAxisCentOrMult}); + } + + }; // end init + + template + void initCCDB(TCollision const& collision) + { + if (mRunNumber == collision.runNumber()) { + return; + } + mRunNumber = collision.runNumber(); + + dBz = collision.grpMagField().getNominalL3Field(); + LOG(info) << "Retrieved GRP for timestamp " << collision.timestamp() << " with magnetic field of " << dBz << " kZG"; + fV0PhotonCut.SetD_Bz(dBz); + } + + template + float getCentralityOrMultiplicity(TCollision const& collision) + { + if (useCent.value) { + return getCentrality(collision); + } + // pp: use raw FT0C multiplicity + return collision.multFT0C(); + } + + /// Get the centrality + /// \param collision is the collision with the centrality information + template + float getCentrality(TCollision const& collision) + { + float cent = -999.; + switch (eventcuts.centEstimator) { + case CentralityEstimator::CFT0M: + cent = collision.centFT0M(); + break; + case CentralityEstimator::CFT0A: + cent = collision.centFT0A(); + break; + case CentralityEstimator::CFT0C: + cent = collision.centFT0C(); + break; + default: + LOG(warning) << "Centrality estimator not valid. Possible values are T0M, T0A, T0C. Fallback to T0C"; + cent = collision.centFT0C(); + break; + } + return cent; + } + + /// \brief check if standard event cuts + FT0 occupancy + centrality + QVec good is + /// \param collision collision that will be checked + /// \return true if collision survives all checks, otherwise false + template + bool isFullEventSelected(TCollision const& collision, bool fillHisto = false) + { + if (fillHisto) { + fEMEventCut.fillEventInfo<0>(®istry, collision); + } + if (!(fEMEventCut.IsSelected(collision))) { + // general event selection + return false; + } + float centOrMult = getCentralityOrMultiplicity(collision); + if (useCent && (centOrMult < eventcuts.cfgMinCent || centOrMult > eventcuts.cfgMaxCent)) { + // event selection + return false; + } + + if (collision.emmceventId() < 0) { + return false; + } + + if (fillHisto) { + fEMEventCut.fillEventInfo<1>(®istry, collision); + fEMEventCut.fillAccepted(®istry); + } + + return true; + } + + /// \brief check if particle is within the EMCal acceptance + /// \param particle particle will be checked + /// \return true if particle is within the EMCal, false otherwise + template + bool inEMCalAcceptance(TParticle const& particle) + { + // general EMCal eta acceptance + if (std::abs(particle.eta()) > EmcalMaxEta) { + return false; + } + + // before EMCal or after DCal + if (particle.phi() < EmcalLowerPhi || particle.phi() > DcalUpperPhi) { + return false; + } + + // between EMCal and DCal + if (EmcalUpperPhi < particle.phi() && particle.phi() < DcalLowerPhi) { + return false; + } + + // PHOS GAP in DCal + if (particle.phi() >= DcalLowerPhi && particle.phi() <= DcalUpperPhosGap) { + if (std::abs(particle.eta()) < DcalMinEta) { + return false; + } + } + + return true; + } + + // TODO: + // - efficiency: number of found photon candidates / number of photons within accpetance + // - acceptance: number of photons in acceptance / number of photons generated + // - photon Purity: number of actual photons / number of photon candidates + // - How to handle EMCal cluster in this regard? Sometimes both conversion leptons can end up in the same cluster. + // What is a good check when to start treating them as a photon? + // When they both deposite at least X% of their momentum into the cluster? + // - electron "purity": number of actual electrons/ number of photon candidates + + // PCM + void processPcm(Colls const& collisions, PcmPhotons const& photons, PcmMcLegs const& legs, EMMCParticles const& mcParticles) + { + EMBitFlags v0flags(photons.size()); + if (photons.size() > 0) { + fV0PhotonCut.AreSelectedRunning(v0flags, photons, ®istry); + } + + // leg iterators for PCM + auto pos1 = legs.begin(); + auto ele1 = legs.begin(); + + // MC leg iterators for PCM + auto pos1mc = mcParticles.begin(); + auto ele1mc = mcParticles.begin(); + + for (const auto& collision : collisions) { + + initCCDB(collision); + if (!isFullEventSelected(collision, true)) { + continue; + } + + auto centOrMult = getCentralityOrMultiplicity(collision); + auto photonsThisEvent = mcPhotons->sliceByCachedUnsorted(aod::emmcparticle::emmceventId, collision.emmceventId(), cache); + + for (const auto& truePhoton : photonsThisEvent) { + if (std::abs(truePhoton.eta()) <= cfgMaxEta) { + registry.fill(HIST("hGenerated"), truePhoton.pt(), centOrMult); + // trivial case I guess for PCM + registry.fill(HIST("PCM/hAccepted"), truePhoton.pt(), centOrMult); + } + } + + auto photonsPCMPerCollision = photons.sliceBy(perCollisionPCM, collision.globalIndex()); + + for (const auto& photon : photonsPCMPerCollision) { + if (!(v0flags.test(photon.globalIndex()))) { + continue; + } + registry.fill(HIST("PCM/hMeasured"), photon.pt(), centOrMult); + + pos1.setCursor(photon.posTrackId()); + ele1.setCursor(photon.negTrackId()); + + pos1mc.setCursor(pos1.emmcparticleId()); + ele1mc.setCursor(ele1.emmcparticleId()); + + const auto photonid1 = o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(pos1mc, ele1mc, PDG_t::kPositron, PDG_t::kElectron, PDG_t::kGamma, mcParticles); + + if (photonid1 < 0) { + continue; + } + registry.fill(HIST("PCM/hMeasuredTruePhoton"), photon.pt(), centOrMult); + } + } + } + PROCESS_SWITCH(PhotonMcTask, processPcm, "Process for pcm photons", false) + + // EMCal + void processEmcal(Colls const& collisions, EMCalPhotons const& clusters, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds, EMMCParticles const& mcParticles) + { + EMBitFlags emcFlags(clusters.size()); + if (clusters.size() > 0) { + fEMCCut.AreSelectedRunning(emcFlags, clusters, matchedPrims, matchedSeconds, ®istry); + } + + auto mcPhoton = mcParticles.begin(); + + for (const auto& collision : collisions) { + + initCCDB(collision); + if (!isFullEventSelected(collision, true)) { + continue; + } + + auto centOrMult = getCentralityOrMultiplicity(collision); + auto photonsThisEvent = mcPhotons->sliceByCachedUnsorted(aod::emmcparticle::emmceventId, collision.emmceventId(), cache); + + for (const auto& truePhoton : photonsThisEvent) { + if (std::abs(truePhoton.eta()) <= cfgMaxEta) { + registry.fill(HIST("hGenerated"), truePhoton.pt(), centOrMult); + + if (inEMCalAcceptance(truePhoton)) { + registry.fill(HIST("EMCal/hAccepted"), truePhoton.pt(), centOrMult); + } + } + } + + auto photonsEMCPerCollision = clusters.sliceBy(perCollisionEMC, collision.globalIndex()); + + for (const auto& photonEMC : photonsEMCPerCollision) { + if (!(emcFlags.test(photonEMC.globalIndex()))) { + continue; + } + + registry.fill(HIST("EMCal/hMeasured"), photonEMC.pt(), centOrMult); + + if (photonEMC.emmcparticleIds().empty()) { + // this is a cluster with just noise, skip + continue; + } + // we only want to look at the largest contribution + mcPhoton.setCursor(photonEMC.emmcparticleIds()[0]); + + // if the largest contribution is photon, just fill the photon histograms + if (std::abs(mcPhoton.pdgCode()) != PDG_t::kGamma) { + continue; + } + + registry.fill(HIST("EMCal/hMeasuredTruePhoton"), photonEMC.pt(), centOrMult); + } + } + } + PROCESS_SWITCH(PhotonMcTask, processEmcal, "Process for emcal photons", false) + + // PCM-EMCal same event + void processPcmEmcal(Colls const& collisions, EMCalPhotons const& clusters, PcmPhotons const& photons, PcmMcLegs const& legs, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds, EMMCParticles const& mcParticles) + { + EMBitFlags emcFlags(clusters.size()); + if (clusters.size() > 0) { + fEMCCut.AreSelectedRunning(emcFlags, clusters, matchedPrims, matchedSeconds, ®istry); + } + EMBitFlags v0flags(photons.size()); + if (photons.size() > 0) { + fV0PhotonCut.AreSelectedRunning(v0flags, photons, ®istry); + } + + // create iterators for photon mc particles + auto mcPhoton = mcParticles.begin(); + + // leg iterators for PCM + auto pos1 = legs.begin(); + auto ele1 = legs.begin(); + + // MC leg iterators for PCM + auto pos1mc = mcParticles.begin(); + auto ele1mc = mcParticles.begin(); + + for (const auto& collision : collisions) { + initCCDB(collision); + if (!isFullEventSelected(collision, true)) { + continue; + } + + float centOrMult = getCentralityOrMultiplicity(collision); + + auto photonsThisEvent = mcPhotons->sliceByCachedUnsorted(aod::emmcparticle::emmceventId, collision.emmceventId(), cache); + + for (const auto& truePhoton : photonsThisEvent) { + if (std::abs(truePhoton.eta()) <= cfgMaxEta) { + registry.fill(HIST("hGenerated"), truePhoton.pt(), centOrMult); + registry.fill(HIST("PCM/hAccepted"), truePhoton.pt(), centOrMult); + } + if (inEMCalAcceptance(truePhoton)) { + registry.fill(HIST("EMCal/hAccepted"), truePhoton.pt(), centOrMult); + } + } + + auto photonsEMCPerCollision = clusters.sliceBy(perCollisionEMC, collision.globalIndex()); + auto photonsPCMPerCollision = photons.sliceBy(perCollisionPCM, collision.globalIndex()); + + for (const auto& photonEMC : photonsEMCPerCollision) { + if (!(emcFlags.test(photonEMC.globalIndex()))) { + continue; + } + + registry.fill(HIST("EMCal/hMeasured"), photonEMC.pt(), centOrMult); + + if (photonEMC.emmcparticleIds().empty()) { + // this is a cluster with just noise, skip + continue; + } + // we only want to look at the largest contribution + mcPhoton.setCursor(photonEMC.emmcparticleIds()[0]); + + // if the largest contribution is photon, just fill the photon histograms + if (std::abs(mcPhoton.pdgCode()) != PDG_t::kGamma) { + continue; + } + + registry.fill(HIST("EMCal/hMeasuredTruePhoton"), photonEMC.pt(), centOrMult); + } + + for (const auto& photon : photonsPCMPerCollision) { + if (!(v0flags.test(photon.globalIndex()))) { + continue; + } + registry.fill(HIST("PCM/hMeasured"), photon.pt(), centOrMult); + + pos1.setCursor(photon.posTrackId()); + ele1.setCursor(photon.negTrackId()); + + pos1mc.setCursor(pos1.emmcparticleId()); + ele1mc.setCursor(ele1.emmcparticleId()); + + const auto photonid1 = o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(pos1mc, ele1mc, PDG_t::kPositron, PDG_t::kElectron, PDG_t::kGamma, mcParticles); + + if (photonid1 < 0) { + continue; + } + registry.fill(HIST("PCM/hMeasuredTruePhoton"), photon.pt(), centOrMult); + } + } + } + PROCESS_SWITCH(PhotonMcTask, processPcmEmcal, "Process for pcm and emcal photons", true); + +}; // End struct PhotonMcTask + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{adaptAnalysisTask(context)}; +} diff --git a/PWGEM/PhotonMeson/Tasks/photonResoTask.cxx b/PWGEM/PhotonMeson/Tasks/photonResoTask.cxx index a51c8533f95..1e8e04cee93 100644 --- a/PWGEM/PhotonMeson/Tasks/photonResoTask.cxx +++ b/PWGEM/PhotonMeson/Tasks/photonResoTask.cxx @@ -21,7 +21,6 @@ #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" #include "PWGEM/PhotonMeson/Utils/MCUtilities.h" #include "Common/Core/RecoDecay.h" @@ -211,6 +210,7 @@ struct PhotonResoTask { fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + fEMEventCut.SetFT0COccupancyRange(eventcuts.cfgFT0COccupancyMin, eventcuts.cfgFT0COccupancyMax); } void defineEMCCut() @@ -283,7 +283,7 @@ struct PhotonResoTask { fEMCCut.addQAHistograms(®istry); definePCMCut(); fV0PhotonCut.addQAHistograms(®istry); - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(®istry); + fEMEventCut.addEventHistograms(®istry); const AxisSpec thnAxisPtGen{thnConfigAxisPt, "#it{p}_{T,Gen} (GeV/#it{c})"}; const AxisSpec thnAxisPtRec{thnConfigAxisPt, "#it{p}_{T,Rec} (GeV/#it{c})"}; @@ -405,25 +405,20 @@ struct PhotonResoTask { bool isFullEventSelected(TCollision const& collision, bool fillHisto = false) { if (fillHisto) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(®istry, collision); + fEMEventCut.fillEventInfo<0>(®istry, collision); } if (!(fEMEventCut.IsSelected(collision))) { // general event selection return false; } - if (!(eventcuts.cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange() && collision.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { - // occupancy selection - return false; - } float centOrMult = getCentralityOrMultiplicity(collision); if (useCent && (centOrMult < eventcuts.cfgMinCent || centOrMult > eventcuts.cfgMaxCent)) { // event selection return false; } if (fillHisto) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(®istry, collision); - registry.fill(HIST("Event/before/hCollisionCounter"), 12.0); // accepted - registry.fill(HIST("Event/after/hCollisionCounter"), 12.0); // accepted + fEMEventCut.fillEventInfo<1>(®istry, collision); + fEMEventCut.fillAccepted(®istry); } return true; } diff --git a/PWGEM/PhotonMeson/Tasks/photonhbt.cxx b/PWGEM/PhotonMeson/Tasks/photonhbt.cxx index 1bbaab29506..9c89b99bd94 100644 --- a/PWGEM/PhotonMeson/Tasks/photonhbt.cxx +++ b/PWGEM/PhotonMeson/Tasks/photonhbt.cxx @@ -19,9 +19,9 @@ #include "PWGEM/PhotonMeson/Core/V0PhotonCut.h" #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" #include "PWGEM/PhotonMeson/Utils/PCMUtilities.h" #include "PWGEM/PhotonMeson/Utils/PairUtilities.h" +#include "PWGEM/PhotonMeson/Utils/V0DedupUtilities.h" #include "Common/Core/RecoDecay.h" #include "Common/Core/trackUtilities.h" @@ -69,6 +69,7 @@ #include #include #include +#include #include #include #include @@ -107,6 +108,9 @@ using MyV0Photon = MyV0Photons::iterator; using MyMCV0Legs = soa::Join; using MyMCV0Leg = MyMCV0Legs::iterator; +// output of builder run 2 (co-reconstructed mixed event, processCoReco): photons of the artificial events of PCMMixCollisions +using MyV0PhotonsMix = soa::Join; + static constexpr float kMinMagnitude = 1e-12f; static constexpr float kMinCosine = 1e-12f; static constexpr float kMinSigma = 1e-9f; @@ -273,6 +277,16 @@ struct Photonhbt { // m(e+e-) of the two leg-swapped combinations std::array mee{999.f, 999.f}; float meeOverQ{999.f}; + // geometry of the two leg-swapped combinations (a's e+ with b's e-, b's e+ with a's e-): would the swapped legs make a photon? + // helix circles from the conversion point and the leg momentum; gap = |c+ - c-| - (r+ + r-) (0 for a conversion), |tgl+ - tgl-|, + // z+ - z- of the two legs at the touching point of the circles (vertex-subtracted z, so mixed pairs are treated like same pairs) + std::array gap{999.f, 999.f}; + std::array dTgl{999.f, 999.f}; + std::array dz{999.f, 999.f}; + int bestCombo{-1}; // the more photon-like combination (smaller |gap| / gapMax + |dz| / dzMax) + float gapBest{999.f}, dTglBest{999.f}, dzBest{999.f}; + bool geomPhotonLike{false}; // a combination inside the windows crosspair_group.cfgCrossMax{Gap,DTgl,Dz} + float qSwap{-1.f}; // q_inv of the two leg-swapped "photons" (a's e+ with b's e-, b's e+ with a's e-): the q this pair would have if the legs had been swapped }; struct TruthGamma { @@ -361,6 +375,12 @@ struct Photonhbt { Configurable cfgFillLegSimilaritySparse{"cfgFillLegSimilaritySparse", true, "book/fill the like-sign leg similarity sparse (duplicate diagnostic: DeltaEta, DeltaPhi, pT ratio, dNsigma_e vs qinv)"}; Configurable cfgFillLegSimilarityMixSparse{"cfgFillLegSimilarityMixSparse", false, "book/fill the MIXED-EVENT leg similarity sparse (kinematic baseline for the SE-ME duplicate excess; needs cfgFillLegSimilaritySparse)"}; Configurable cfgFillR1R2Sparse{"cfgFillR1R2Sparse", true, "book/fill the FullRange (R1, R2, q_inv) sparse: fix one conversion radius, see where the partner sits, per q_inv"}; + // pair-class QA on the co-reconstructed mixed event (processPairClassMix) + Configurable cfgPairClassMaxQ{"cfgPairClassMaxQ", 0.5f, "pair-class QA: max q_inv of the pairs (GeV/c)"}; + Configurable cfgPairClassMixDepth{"cfgPairClassMixDepth", 10, "pair-class QA, standard mixed event: same-event artificial events kept per pool"}; + Configurable cfgPairClassMixZBinWidth{"cfgPairClassMixZBinWidth", 2.f, "pair-class QA, standard mixed event: pool z-vertex bin width (cm)"}; + Configurable cfgPairClassMixCentBinWidth{"cfgPairClassMixCentBinWidth", 10.f, "pair-class QA, standard mixed event: pool centrality (FT0C) bin width (%)"}; + Configurable cfgPairClassMaxSharedFrac{"cfgPairClassMaxSharedFrac", 0.4f, "pair-class QA, clean flag: all four legs with TPC shared-cluster fraction below this"}; } qaflags; // ─── HBT analysis mode ─────────────────────────────────────────────────────────── @@ -420,6 +440,10 @@ struct Photonhbt { ConfigurableAxis confCentBins{"confCentBins", {VARIABLE_WIDTH, 0.f, 5.f, 10.f, 20.f, 30.f, 40.f, 50.f, 60.f, 70.f, 80.f, 90.f, 100.f, 999.f}, "Mixing bins - centrality"}; ConfigurableAxis confEPBinsBins{"confEPBinsBins", {16, -o2::constants::math::PIHalf, +o2::constants::math::PIHalf}, "Mixing bins - EP angle"}; ConfigurableAxis confOccupancyBins{"confOccupancyBins", {VARIABLE_WIDTH, -1, 1e+10}, "Mixing bins - occupancy"}; + // processCoReco (output of builder run 2): which mixed event fills Pair/mix + Configurable cfgCoRecoMixMode{"cfgCoRecoMixMode", 0, "processCoReco: 0 co-reconstructed mixed event (pairs inside the mixed artificial events), 1 standard mixed event built here from the same-event artificial events (pools as for processAnalysis, no BC gap), for old vs new on the same photons"}; + Configurable cfgCoRecoSkipOneSource{"cfgCoRecoSkipOneSource", true, "processCoReco: skip pairs of a mixed artificial event whose four legs all come from one source event (same-event pairs by construction)"}; + Configurable cfgCoRecoMC{"cfgCoRecoMC", false, "processCoReco: MC diagnostics from the leg labels carried in PCMMixLegs (truth-type CFs as processMC, pair-class decomposition)"}; } mixing; // ─── Centrality slection ───────────────────────────────────────────────── @@ -501,6 +525,10 @@ struct Photonhbt { Configurable cfgDoCrossPairCut{"cfgDoCrossPairCut", false, "reject pairs with a photon-like crossed combination; applied to SE AND ME"}; Configurable cfgCrossMaxMeeRatio{"cfgCrossMaxMeeRatio", 0.2f, "veto: min(m_ee^cross)/q_inv below this; genuine pairs sit near 0.5, leg swaps near 0"}; Configurable cfgCrossMaxQinvQA{"cfgCrossMaxQinvQA", 0.3f, "fill crossed QA sparses only below this qinv"}; + Configurable cfgDoCrossGeomCut{"cfgDoCrossGeomCut", false, "reject pairs whose leg-swapped combination is geometrically a photon: helix circles of the swapped legs touching (|gap| < cfgCrossMaxGap), |tgl+ - tgl-| < cfgCrossMaxDTgl and the legs' z at the touching point within cfgCrossMaxDz (vertex-subtracted); applied to SE AND ME. A stored leg-swapped pair (two crossed fakes of two collinear photons) always has such a combination (the true photons), a genuine pair only when the two photons are collinear and convert on one line"}; + Configurable cfgCrossMaxGap{"cfgCrossMaxGap", 1.5f, "geometric crossed veto: max |circle gap| of the swapped legs (cm)"}; + Configurable cfgCrossMaxDTgl{"cfgCrossMaxDTgl", 0.1f, "geometric crossed veto: max |tgl+ - tgl-| of the swapped legs"}; + Configurable cfgCrossMaxDz{"cfgCrossMaxDz", 2.f, "geometric crossed veto: max |z+ - z-| of the swapped legs at the touching point (cm)"}; } crosspair; struct : ConfigurableGroup { @@ -543,6 +571,7 @@ struct Photonhbt { Configurable cfgMaxPCA{"cfgMaxPCA", 3.0, "max distance between 2 legs"}; Configurable cfgMaxChi2KF{"cfgMaxChi2KF", 1e+10, "max chi2/ndf with KF"}; Configurable cfgRejectV0OnITSIB{"cfgRejectV0OnITSIB", true, "reject V0s on ITSib"}; + Configurable cfgMinNCrossedRowsOverFindable{"cfgMinNCrossedRowsOverFindable", 0.8f, "min. TPC crossed rows / findable clusters of the legs (was hard-coded 0.8)"}; Configurable cfgDisableITSOnlyTrack{"cfgDisableITSOnlyTrack", false, "disable ITS-only tracks"}; Configurable cfgDisableTPCOnlyTrack{"cfgDisableTPCOnlyTrack", false, "disable TPC-only tracks"}; Configurable cfgMinNClusterTPC{"cfgMinNClusterTPC", 70, "min ncluster TPC"}; @@ -675,9 +704,19 @@ struct Photonhbt { void init(InitContext& context) { isMC = context.mOptions.get("processMC"); + const bool doCoReco = context.mOptions.get("processCoReco"); + if (doCoReco && (isMC || context.mOptions.get("processAnalysis"))) { + LOGF(fatal, "processCoReco fills the same Pair/same and Pair/mix histograms as processAnalysis / processMC: run it in its own photonhbt instance"); + } + if (doCoReco) { + isMC = mixing.cfgCoRecoMC.value; + } if (context.mOptions.get("processMCAOD")) { addMCAODHistograms(); } + if (context.mOptions.get("processPairClassMix")) { + addPairClassMixHistograms(); + } if (pairsep.cfgCloseThrCm.value.size() != 4) { // o2-linter: disable=magic-number (four thresholds) LOGF(fatal, "cfgCloseThrCm must contain exactly four thresholds, got %zu", pairsep.cfgCloseThrCm.value.size()); @@ -699,7 +738,7 @@ struct Photonhbt { (ggpaircuts.cfgDoEllipseCut.value || ggpaircuts.cfgDoRCut.value || ggpaircuts.cfgDoZCut.value || ggpaircuts.cfgMinDRCosOA.value > 0.f || ggpaircuts.cfgMaxAsymmetry.value >= 0.f || pairsep.cfgDoPairMergeCut.value || - crosspair.cfgDoCrossPairCut.value || cfgDoPhotonClassPairCut.value); + crosspair.cfgDoCrossPairCut.value || crosspair.cfgDoCrossGeomCut.value || cfgDoPhotonClassPairCut.value); LOGF(info, "photonhbt QA level %d -> pairQA %d, singlePhotonQA %d, legPairQA %d, pairSep %d, dRdZ sparse %d, R1R2 sparse %d, pairSep afterCut %d", qaLevel, mQa.pairQa, mQa.singlePhotonQa, mQa.legPairQa, mQa.pairSepQa, mQa.fillDRDZSparse, mQa.fillR1R2Sparse, mQa.pairSepAfterCut); mRunNumber = 0; @@ -714,6 +753,9 @@ struct Photonhbt { mPhotonClassSelA = pairutil::buildV0PhotonClassSelection(photonclassA); mPhotonClassSelB = pairutil::buildV0PhotonClassSelection(photonclassB); addhistograms(); + if (doCoReco) { + addCoRecoHistograms(); + } std::random_device seedGen; engine = std::mt19937(seedGen()); dist01 = std::uniform_int_distribution(0, 1); @@ -751,9 +793,10 @@ struct Photonhbt { LOGF(info, "photonhbt pair merging cut: %s, D_pair > %.2f, N_close < %d (0 = off); control window %.2f < q_inv < %.2f", pairsep.cfgDoPairMergeCut.value ? "ON" : "off", pairsep.cfgDPairCut.value, pairsep.cfgNCloseCut.value, pairsep.cfgCondQinvMin.value, pairsep.cfgCondQinvMax.value); - LOGF(info, "photonhbt: crossed-pair QA %s, cut %s (min(m_ee)/q_inv > %.2f)", + LOGF(info, "photonhbt: crossed-pair QA %s, cut %s (min(m_ee)/q_inv > %.2f), geometric cut %s (|gap| < %.2f cm, |dtgl| < %.3f, |dz| < %.2f cm)", crosspair.cfgDoCrossPairQA.value ? "on" : "off", - crosspair.cfgDoCrossPairCut.value ? "ON" : "off", crosspair.cfgCrossMaxMeeRatio.value); + crosspair.cfgDoCrossPairCut.value ? "ON" : "off", crosspair.cfgCrossMaxMeeRatio.value, + crosspair.cfgDoCrossGeomCut.value ? "ON" : "off", crosspair.cfgCrossMaxGap.value, crosspair.cfgCrossMaxDTgl.value, crosspair.cfgCrossMaxDz.value); LOGF(info, "photonhbt: q_inv gates -- QA < %.3f, FullRange < %.3f, PairSep < %.3f", qaflags.cfgMaxQinvForQA.value, qaflags.cfgMaxQinvForFullRange.value, pairsep.cfgPairSepMaxQinv.value); @@ -809,6 +852,8 @@ struct Photonhbt { fEMEventCut.SetRequireGoodITSLayer3(eventcuts.cfgRequireGoodITSLayer3); fEMEventCut.SetRequireGoodITSLayer0123(eventcuts.cfgRequireGoodITSLayer0123); fEMEventCut.SetRequireGoodITSLayersAll(eventcuts.cfgRequireGoodITSLayersAll); + fEMEventCut.SetTrackOccupancyRange(eventcuts.cfgTrackOccupancyMin, eventcuts.cfgTrackOccupancyMax); + fEMEventCut.SetFT0COccupancyRange(eventcuts.cfgFT0COccupancyMin, eventcuts.cfgFT0COccupancyMax); } void DefinePCMCut() @@ -824,7 +869,7 @@ struct Photonhbt { fV0PhotonCut.RejectITSib(pcmcuts.cfgRejectV0OnITSIB); fV0PhotonCut.SetMinNClustersTPC(pcmcuts.cfgMinNClusterTPC); fV0PhotonCut.SetMinNCrossedRowsTPC(pcmcuts.cfgMinNCrossedRows); - fV0PhotonCut.SetMinNCrossedRowsOverFindableClustersTPC(0.8); + fV0PhotonCut.SetMinNCrossedRowsOverFindableClustersTPC(pcmcuts.cfgMinNCrossedRowsOverFindable); fV0PhotonCut.SetMaxFracSharedClustersTPC(pcmcuts.cfgMaxFracSharedClustersTPC); fV0PhotonCut.SetChi2PerClusterTPC(0.0, pcmcuts.cfgMaxChi2TPC); fV0PhotonCut.SetTPCNsigmaElRange(pcmcuts.cfgMinTPCNsigmaEl, pcmcuts.cfgMaxTPCNsigmaEl); @@ -973,7 +1018,7 @@ struct Photonhbt { { static constexpr std::array det = {"FT0M", "FT0A", "FT0C", "BTot", "BPos", "BNeg"}; - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(&fRegistry); + fEMEventCut.addEventHistograms(&fRegistry); fRegistry.add("Event/before/hEP2_CentFT0C_forMix", Form("2nd harmonics EP for mix;centrality FT0C (%%);#Psi_{2}^{%s} (rad.)", det[mixing.cfgEP2EstimatorForMix].data()), kTH2D, {{110, 0, 110}, {180, -o2::constants::math::PIHalf, +o2::constants::math::PIHalf}}, false); @@ -1112,13 +1157,17 @@ struct Photonhbt { void addCrossPairHistograms() { - if (!(crosspair.cfgDoCrossPairQA.value || crosspair.cfgDoCrossPairCut.value)) { + if (!(crosspair.cfgDoCrossPairQA.value || crosspair.cfgDoCrossPairCut.value || crosspair.cfgDoCrossGeomCut.value)) { return; } for (const auto& sm : {std::string("Pair/same/CrossPair/"), std::string("Pair/mix/CrossPair/")}) { fRegistryCF.add((sm + "hSparse_MeeRatio_dR_Qinv").c_str(), "crossed-hypothesis QA;min(m_{ee}^{cross})/q_{inv};|R_{1}-R_{2}| (cm);q_{inv} (GeV/c)", kTHnSparseF, {{100, 0.f, 2.f}, {80, 0.f, 80.f}, {60, 0.f, 0.3f}}, true); + fRegistryCF.add((sm + "hSparse_CrossGeom_Qinv").c_str(), + "geometry of the more photon-like leg-swapped combination;circle gap (cm);|z_{+} - z_{-}| at the touching point (cm);|tgl_{+} - tgl_{-}|;q_{inv} (GeV/c)", + kTHnSparseF, {{100, -5.f, 5.f}, {60, 0.f, 6.f}, {50, 0.f, 0.5f}, {30, 0.f, 0.15f}}, true); + fRegistryCF.add((sm + "hSwapTemplate").c_str(), "swap template: q_inv the pair would have with swapped legs;q_{inv} of the pair (GeV/c);q_{inv} of the leg-swapped pairing (GeV/c);0 swapped combination not photon-like, 1 photon-like (in the geometric window)", kTH3F, {{60, 0.f, 0.3f}, {60, 0.f, 0.3f}, {2, -0.5f, 1.5f}}, true); auto h = fRegistryCF.add((sm + "hVetoCounter").c_str(), "crossed-pair veto;;pairs", kTH1D, {{2, -0.5f, 1.5f}}, true); h->GetXaxis()->SetBinLabel(1, "evaluated"); h->GetXaxis()->SetBinLabel(2, "vetoed"); @@ -1243,6 +1292,8 @@ struct Photonhbt { kTHnSparseF, {axisPsiPair, axisPhiV, axisQinv, axisTruthType, axisIsTruePhoton}, true); const AxisSpec axisMeeRatio{100, 0.f, 2.f, "min(m_{ee}^{cross})/q_{inv}"}; + fRegistryPairMC.add("Pair/same/MC/hSparse_SwapTemplate_Type", "swap template per truth type;q_{inv} of the pair (GeV/c);q_{inv} of the leg-swapped pairing (GeV/c);swapped combination photon-like;truth type", kTHnSparseF, {{60, 0.f, 0.3f}, {60, 0.f, 0.3f}, {2, -0.5f, 1.5f}, {7, -0.5f, 6.5f}}, true); + fRegistryPairMC.add("Pair/same/MC/hSparse_CrossGeom_Qinv_Type", "geometry of the more photon-like leg-swapped combination per truth type;circle gap (cm);|z_{+} - z_{-}| at the touching point (cm);q_{inv} (GeV/c);truth type", kTHnSparseF, {{100, -5.f, 5.f}, {60, 0.f, 6.f}, {30, 0.f, 0.15f}, {7, -0.5f, 6.5f}}, true); fRegistryPairMC.add("Pair/same/MC/hSparse_MeeRatio_Qinv_Type", "crossed-pair hypothesis per truth type;" "min(m_{ee}^{cross})/q_{inv};q_{inv} (GeV/c);truth type", @@ -1280,6 +1331,8 @@ struct Photonhbt { const AxisSpec axisFakeSubtype{3, -0.5f, 2.5f, "0=ordinary, 1=swap doublet, 2=partial swap"}; fRegistryPairMC.add("Pair/same/MC/hFakeSubtypeVsQinv", "fake pair subtype vs q_{inv};q_{inv} (GeV/c);subtype", kTH2D, {axisQinv, axisFakeSubtype}, true); + fRegistryPairMC.add("Pair/same/MC/hQrecVsQtrueVsKt", "distinct true photons;q_{inv}^{true} (GeV/c);q_{inv}^{rec} (GeV/c);k_{T}^{rec} (GeV/c)", kTH3F, + {{300, 0.f, 0.3f}, {300, 0.f, 0.3f}, {std::vector{0.1, 0.2, 0.3, 0.4, 0.6, 1.0, 2.0}}}, false); fRegistryPairMC.add("Pair/same/MC/hDEtaDPhi_truePairs", "true reco pairs (TrueTrueDistinct+SamePhoton+Pi0);" "#Delta#eta_{#gamma#gamma};#Delta#phi_{#gamma#gamma} (rad)", @@ -1375,6 +1428,13 @@ struct Photonhbt { fRegistryTruthMC.add("MC/TruthAO2D/hMinRconv_vs_kT_truthConverted", "denominator: min(R_{conv}) vs k_{T};k_{T} (GeV/c);min(R_{conv}^{true}) (cm)", kTH2D, {axisKt, axRconv}, true); fRegistryTruthMC.add("MC/TruthAO2D/hMinRconv_vs_kT_bothPhotonsSelected", "numerator: min(R_{conv}) vs k_{T};k_{T} (GeV/c);min(R_{conv}^{true}) (cm)", kTH2D, {axisKt, axRconv}, true); + const AxisSpec axSpPt{std::vector{0.1, 0.125, 0.15, 0.175, 0.2, 0.25, 0.3, 0.35, 0.4, 0.5, 0.6, 0.7, 0.8, 1.0, 1.25, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0}, "p_{T}^{true} (GeV/c)"}; + const AxisSpec axSpR{90, 0.f, 180.f, "R_{conv}^{true} (cm)"}; + const AxisSpec axSpEta{16, -0.8f, 0.8f, "#eta^{true}"}; + for (const auto& stage : {"converted", "built", "selected"}) { + fRegistryTruthMC.add((std::string("MC/SinglePhotonEff/hPtREta_") + stage).c_str(), (std::string("true primary converted photons, ") + stage + ";p_{T}^{true} (GeV/c);R_{conv}^{true} (cm);#eta^{true}").c_str(), kTH3F, {axSpPt, axSpR, axSpEta}, true); + } + // ─── Stage summary fRegistryTruthMC.add("MC/TruthAO2D/hStage_vs_kT", "efficiency waterfall;k_{T} (GeV/c);stage (0=converted,1=all4legs,2=bothBuilt,3=bothSel)", kTH2D, {axisKt, AxisSpec{4, -0.5f, 3.5f, "stage"}}, true); fRegistryTruthMC.add("MC/TruthAO2D/hStageConsistency", "stage consistency (expect all at 0);N(V0 built but legs not found);counts", kTH1D, {AxisSpec{20, -0.5f, 19.5f, "N_{bad}"}}, true); @@ -1898,15 +1958,70 @@ struct Photonhbt { if (qinv > 0.f) { c.meeOverQ = std::min(c.mee[0], c.mee[1]) / qinv; } + { // swap template: q_inv of the two leg-swapped combinations (the leg-level combinatorics of a crossed pair) + const auto f1 = legVec(a, 0) + legVec(b, 1); + const auto f2 = legVec(b, 0) + legVec(a, 1); + const auto dq = f1 - f2; + c.qSwap = static_cast(std::sqrt(std::max(0., -dq.M2()))); + } + // geometry: helix circle of leg i (cm) from the conversion point and the leg momentum, as legHelixAt + auto circle = [&](PhotonWithLegs const& p, int i, float& xc, float& yc, float& r) { + r = p.fLegPt[i] / (0.3f * std::fabs(mBzT)) * kCmPerM; // o2-linter: disable=magic-number (0.3 GeV/c per T m) + const float sgn = (mBzT >= 0.f ? 1.f : -1.f) * (i == 0 ? 1.f : -1.f); + xc = p.fVx + sgn * r * std::sin(p.fLegPhi[i]); + yc = p.fVy - sgn * r * std::cos(p.fLegPhi[i]); + }; + // z of leg i at the point of its circle closest to (xT, yT): signed arc along the motion from the conversion point, local z + auto zAt = [&](PhotonWithLegs const& p, int i, float xc, float yc, float r, float xT, float yT) { + const float phiStart = std::atan2(p.fVy - yc, p.fVx - xc); + const float phiT = std::atan2(yT - yc, xT - xc); + const bool ccw = std::cos(p.fLegPhi[i]) * (-(p.fVy - yc)) + std::sin(p.fLegPhi[i]) * (p.fVx - xc) > 0.f; // momentum along the counterclockwise tangent + const float dphi = RecoDecay::constrainAngle(phiT - phiStart, -o2::constants::math::PI); + return (p.fVz - p.fVtxZ) + r * (ccw ? dphi : -dphi) * std::sinh(p.fLegEta[i]); + }; + const float gapMax = std::max(crosspair.cfgCrossMaxGap.value, kMinSigma), dzMax = std::max(crosspair.cfgCrossMaxDz.value, kMinSigma); + float bestScore = 999.f; + if (std::fabs(mBzT) > kMinSigma) { + for (int combo = 0; combo < 2; ++combo) { // o2-linter: disable=magic-number (the two leg-swap combinations) + PhotonWithLegs const& pPos = (combo == 0) ? a : b; + PhotonWithLegs const& pEle = (combo == 0) ? b : a; + float xP = 0.f, yP = 0.f, rP = 0.f, xE = 0.f, yE = 0.f, rE = 0.f; + circle(pPos, 0, xP, yP, rP); + circle(pEle, 1, xE, yE, rE); + const float dC = std::hypot(xE - xP, yE - yP); + if (!(dC > kMinSigma) || !(rP + rE > kMinSigma)) { + continue; + } + c.gap[combo] = dC - (rP + rE); + c.dTgl[combo] = std::fabs(std::sinh(pPos.fLegEta[0]) - std::sinh(pEle.fLegEta[1])); + const float w = rP / (rP + rE); + const float xT = xP + w * (xE - xP), yT = yP + w * (yE - yP); // touching point of the circles + c.dz[combo] = zAt(pPos, 0, xP, yP, rP, xT, yT) - zAt(pEle, 1, xE, yE, rE, xT, yT); + const float score = std::fabs(c.gap[combo]) / gapMax + std::fabs(c.dz[combo]) / dzMax; + if (score < bestScore) { + bestScore = score; + c.bestCombo = combo; + c.gapBest = c.gap[combo]; + c.dTglBest = c.dTgl[combo]; + c.dzBest = c.dz[combo]; + } + if (std::fabs(c.gap[combo]) < crosspair.cfgCrossMaxGap.value && c.dTgl[combo] < crosspair.cfgCrossMaxDTgl.value && std::fabs(c.dz[combo]) < crosspair.cfgCrossMaxDz.value) { + c.geomPhotonLike = true; + } + } + } return c; } [[nodiscard]] inline bool passCrossPairVeto(CrossObs const& c) const { - if (!crosspair.cfgDoCrossPairCut.value) { - return true; + if (crosspair.cfgDoCrossPairCut.value && c.meeOverQ <= crosspair.cfgCrossMaxMeeRatio.value) { + return false; + } + if (crosspair.cfgDoCrossGeomCut.value && c.geomPhotonLike) { + return false; } - return c.meeOverQ > crosspair.cfgCrossMaxMeeRatio.value; + return true; } template @@ -1914,10 +2029,16 @@ struct Photonhbt { { constexpr auto dir = (ev_id == 0) ? "Pair/same/CrossPair/" : "Pair/mix/CrossPair/"; fRegistryCF.fill(HIST(dir) + HIST("hVetoCounter"), vetoed ? 1.0 : 0.0); + if (c.qSwap >= 0.f) { + fRegistryCF.fill(HIST(dir) + HIST("hSwapTemplate"), qinv, c.qSwap, c.geomPhotonLike ? 1.f : 0.f); + } if (qinv > crosspair.cfgCrossMaxQinvQA.value) { return; } fRegistryCF.fill(HIST(dir) + HIST("hSparse_MeeRatio_dR_Qinv"), c.meeOverQ, deltaR, qinv); + if (c.bestCombo >= 0) { + fRegistryCF.fill(HIST(dir) + HIST("hSparse_CrossGeom_Qinv"), c.gapBest, std::fabs(c.dzBest), c.dTglBest, qinv); + } } /*************************************************/ @@ -2657,13 +2778,12 @@ struct Photonhbt { collision.ep2fv0a(), collision.ep2btot(), collision.ep2bpos(), collision.ep2bneg()}; const float ep2 = epArr[mixing.cfgEP2EstimatorForMix]; fRegistry.fill(HIST("Event/before/hEP2_CentFT0C_forMix"), collision.centFT0C(), ep2); - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(&fRegistry, collision, 1.f); + fEMEventCut.fillEventInfo<0>(&fRegistry, collision, 1.f); if (!fEMEventCut.IsSelected(collision)) { continue; } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(&fRegistry, collision, 1.f); - fRegistry.fill(HIST("Event/before/hCollisionCounter"), 12.0); - fRegistry.fill(HIST("Event/after/hCollisionCounter"), 12.0); + fEMEventCut.fillEventInfo<1>(&fRegistry, collision, 1.f); + fEMEventCut.fillAccepted(&fRegistry); fRegistry.fill(HIST("Event/after/hEP2_CentFT0C_forMix"), collision.centFT0C(), ep2); const float occupancy = (mixing.cfgOccupancyEstimator == 1) ? static_cast(collision.trackOccupancyInTimeRange()) @@ -2737,7 +2857,7 @@ struct Photonhbt { if (isInsideEllipse(obs.deta, obs.dphi)) { continue; } - if (crosspair.cfgDoCrossPairQA.value || crosspair.cfgDoCrossPairCut.value) { + if (crosspair.cfgDoCrossPairQA.value || crosspair.cfgDoCrossPairCut.value || crosspair.cfgDoCrossGeomCut.value) { const auto cross = computeCrossObs(pwl1, pwl2, obs.qinv); const bool vetoed = !passCrossPairVeto(cross); fillCrossPair<0>(cross, obs.qinv, obs.deltaR, vetoed); @@ -2782,8 +2902,8 @@ struct Photonhbt { if (!mixing.cfgDoMix || ndiphoton == 0) { continue; } - auto selectedPhotons = emh1->GetTracksPerCollision(keyDFCollision); - auto poolIDs = emh1->GetCollisionIdsFromEventPool(keyBin); + const auto& selectedPhotons = emh1->GetTracksPerCollision(keyDFCollision); + const auto& poolIDs = emh1->GetCollisionIdsFromEventPool(keyBin); for (const auto& mixID : poolIDs) { if (mixID.second == collision.globalIndex() && mixID.first == ndf) { continue; @@ -2794,7 +2914,7 @@ struct Photonhbt { if (diffBC < mixing.ndiffBCMix) { continue; } - auto poolPhotons = emh1->GetTracksPerCollision(mixID); + const auto& poolPhotons = emh1->GetTracksPerCollision(mixID); for (const auto& g1 : selectedPhotons) { for (const auto& g2 : poolPhotons) { if (!passAsymmetryCut(g1.pt(), g2.pt())) { @@ -2837,7 +2957,7 @@ struct Photonhbt { if (isInsideEllipse(obs.deta, obs.dphi)) { continue; } - if (crosspair.cfgDoCrossPairQA.value || crosspair.cfgDoCrossPairCut.value) { + if (crosspair.cfgDoCrossPairQA.value || crosspair.cfgDoCrossPairCut.value || crosspair.cfgDoCrossGeomCut.value) { const auto cross = computeCrossObs(g1, g2, obs.qinv); const bool vetoed = !passCrossPairVeto(cross); fillCrossPair<1>(cross, obs.qinv, obs.deltaR, vetoed); @@ -2888,13 +3008,12 @@ struct Photonhbt { collision.ep2fv0a(), collision.ep2btot(), collision.ep2bpos(), collision.ep2bneg()}; const float ep2 = epArr[mixing.cfgEP2EstimatorForMix]; fRegistry.fill(HIST("Event/before/hEP2_CentFT0C_forMix"), collision.centFT0C(), ep2); - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(&fRegistry, collision, 1.f); + fEMEventCut.fillEventInfo<0>(&fRegistry, collision, 1.f); if (!fEMEventCut.IsSelected(collision)) { continue; } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(&fRegistry, collision, 1.f); - fRegistry.fill(HIST("Event/before/hCollisionCounter"), 12.0); - fRegistry.fill(HIST("Event/after/hCollisionCounter"), 12.0); + fEMEventCut.fillEventInfo<1>(&fRegistry, collision, 1.f); + fEMEventCut.fillAccepted(&fRegistry); fRegistry.fill(HIST("Event/after/hEP2_CentFT0C_forMix"), collision.centFT0C(), ep2); const float occupancy = (mixing.cfgOccupancyEstimator == 1) ? static_cast(collision.trackOccupancyInTimeRange()) @@ -2989,7 +3108,7 @@ struct Photonhbt { if (isInsideEllipse(obs.deta, obs.dphi)) { continue; } - if (crosspair.cfgDoCrossPairQA.value || crosspair.cfgDoCrossPairCut.value) { + if (crosspair.cfgDoCrossPairQA.value || crosspair.cfgDoCrossPairCut.value || crosspair.cfgDoCrossGeomCut.value) { const auto cross = computeCrossObs(pwl1, pwl2, obs.qinv); const bool vetoed = !passCrossPairVeto(cross); fillCrossPair<0>(cross, obs.qinv, obs.deltaR, vetoed); @@ -3045,6 +3164,13 @@ struct Photonhbt { } } + if (mc1.sameMother && mc1.isTruePhoton && mc2.sameMother && mc2.isTruePhoton && mc1.motherId != mc2.motherId) { + const auto& t1 = mcParticles.iteratorAt(mc1.motherId); + const auto& t2 = mcParticles.iteratorAt(mc2.motherId); + const ROOT::Math::PtEtaPhiMVector vt1(t1.pt(), t1.eta(), t1.phi(), 0.), vt2(t2.pt(), t2.eta(), t2.phi(), 0.); + const float qTrue = static_cast(std::sqrt(std::max(0., -(vt1 - vt2).M2()))); + fRegistryPairMC.fill(HIST("Pair/same/MC/hQrecVsQtrueVsKt"), qTrue, obs.qinv, obs.kt); + } fRegistryPairMC.fill(HIST("Pair/same/MC/hSparse_PsiPair_PhiV_Qinv_Type"), std::fabs(g1.psipair()), g1.phiv(), obs.qinv, static_cast(static_cast(truthType)), (mc1.sameMother && mc1.isTruePhoton) ? 1.f : 0.f); fRegistryPairMC.fill(HIST("Pair/same/MC/hSparse_PsiPair_PhiV_Qinv_Type"), std::fabs(g2.psipair()), g2.phiv(), obs.qinv, static_cast(static_cast(truthType)), (mc2.sameMother && mc2.isTruePhoton) ? 1.f : 0.f); if (truthType == PairTruthType::FakeFake || truthType == PairTruthType::TrueFake) { @@ -3056,6 +3182,12 @@ struct Photonhbt { if (crossMC.meeOverQ < 900.f) { // o2-linter: disable=magic-number (qinv was zero, skip) fRegistryPairMC.fill(HIST("Pair/same/MC/hSparse_MeeRatio_Qinv_Type"), crossMC.meeOverQ, obs.qinv, static_cast(static_cast(truthType))); } + if (crossMC.bestCombo >= 0) { + fRegistryPairMC.fill(HIST("Pair/same/MC/hSparse_CrossGeom_Qinv_Type"), crossMC.gapBest, std::fabs(crossMC.dzBest), obs.qinv, static_cast(static_cast(truthType))); + } + if (crossMC.qSwap >= 0.f) { + fRegistryPairMC.fill(HIST("Pair/same/MC/hSparse_SwapTemplate_Type"), obs.qinv, crossMC.qSwap, crossMC.geomPhotonLike ? 1.f : 0.f, static_cast(static_cast(truthType))); + } switch (truthType) { case PairTruthType::TrueTrueDistinct: @@ -3102,8 +3234,8 @@ struct Photonhbt { if (!mixing.cfgDoMix || ndiphoton == 0) { continue; } - auto selectedPhotons = emh1->GetTracksPerCollision(keyDFCollision); - auto poolIDs = emh1->GetCollisionIdsFromEventPool(keyBin); + const auto& selectedPhotons = emh1->GetTracksPerCollision(keyDFCollision); + const auto& poolIDs = emh1->GetCollisionIdsFromEventPool(keyBin); for (const auto& mixID : poolIDs) { if (mixID.second == collision.globalIndex() && mixID.first == ndf) { continue; @@ -3114,7 +3246,7 @@ struct Photonhbt { if (diffBC < mixing.ndiffBCMix) { continue; } - auto poolPhotons = emh1->GetTracksPerCollision(mixID); + const auto& poolPhotons = emh1->GetTracksPerCollision(mixID); for (const auto& g1 : selectedPhotons) { for (const auto& g2 : poolPhotons) { if (!passAsymmetryCut(g1.pt(), g2.pt())) { @@ -3154,7 +3286,7 @@ struct Photonhbt { if (isInsideEllipse(obs.deta, obs.dphi)) { continue; } - if (crosspair.cfgDoCrossPairQA.value || crosspair.cfgDoCrossPairCut.value) { + if (crosspair.cfgDoCrossPairQA.value || crosspair.cfgDoCrossPairCut.value || crosspair.cfgDoCrossGeomCut.value) { const auto cross = computeCrossObs(g1, g2, obs.qinv); const bool vetoed = !passCrossPairVeto(cross); fillCrossPair<1>(cross, obs.qinv, obs.deltaR, vetoed); @@ -3401,6 +3533,18 @@ struct Photonhbt { fRegistryTruthMC.fill(HIST("MC/TruthAO2D/hStageConsistency"), static_cast(nBad)); } + // single-photon efficiency: every true converted primary photon of this collision, built (V0 of its own legs) / selected (HBT cut) + for (const auto& tg : trueGammas) { + fRegistryTruthMC.fill(HIST("MC/SinglePhotonEff/hPtREta_converted"), tg.pt, tg.rTrue, tg.eta); + const auto it = gammaRecoMap.find(tg.id); + if (it != gammaRecoMap.end() && it->second.hasV0) { + fRegistryTruthMC.fill(HIST("MC/SinglePhotonEff/hPtREta_built"), tg.pt, tg.rTrue, tg.eta); + if (it->second.passesCut) { + fRegistryTruthMC.fill(HIST("MC/SinglePhotonEff/hPtREta_selected"), tg.pt, tg.rTrue, tg.eta); + } + } + } + for (const auto& tg : trueGammas) { const bool posFound = legIdsThisCollision.contains(tg.posId) > 0; const bool negFound = legIdsThisCollision.contains(tg.negId) > 0; @@ -4205,6 +4349,1110 @@ struct Photonhbt { } PROCESS_SWITCH(Photonhbt, processMCAOD, "truth/SVertexer pair QA directly on AO2Ds", false); + + static constexpr int kPcGamma = 22, kPcElectron = 11; + struct PcLeg { // MC of one leg (ids within the DF of the source event), as the builder's MixPairPhoton::LegMC + int64_t evt = -1; + int pid = -1, mid = -1, gmid = -1, pdg = 0, mpdg = 0, gpdg = 0, trk = -1, anc = -1, ggid = -1, root = -1, ggpdg = 0, rootPdg = 0; + float sharedFrac = 0.f; + float vx = 0.f, vy = 0.f, vz = 0.f; // MC production vertex = true conversion point (valid for pid >= 0) + float px = 0.f, py = 0.f, pz = 0.f; // reconstructed leg momentum at the fitted vertex, before the mass constraint (V0LegsMix) + float mpx = 0.f, mpy = 0.f, mpz = 0.f; // MC momentum of the mother (the true photon for a conversion leg) + bool its = false; // leg has ITS hits + bool free = false; // pooled free leg (PCMMixLegs sourceV0Id -2, overlay mixed event) + int64_t srcPhoton = -1; // run-1 photon the leg came from (PCMMixLegs sourcePhotonUid; own uid for a free leg) + }; + struct PcPhoton { + float px = 0.f, py = 0.f, pz = 0.f; + float vx = 0.f, vy = 0.f, vz = 0.f; // reconstructed conversion point + bool original = true; // run-1 pairing (false: crossed pairing built in run 2) + bool svertexer = true; // run-1 photon was an SVertexer V0 (false: built by the finder) + float chi2 = 0.f, dcaxy = 0.f, dcaz = 0.f, cospa = 1.f; // KF chi2/ndf, DCA to the PV (cm), cos of the pointing angle to the PV + float chi2Vtx = -1.f, chi2PV = -1.f, psipair = 0.f; // KF chi2/ndf of the vertex fit alone, chi2 to the PV, psi_pair (processPairClassMix) + int nFree = 0; // legs that were pooled free legs (PCMMixLegs sourceV0Id -2, overlay): 0, 1, 2 + PcLeg pos, ele; + }; + static inline std::map>> mPcPools; // standard mixed event: (z bin, centrality bin) -> last cfgMixDepth same-event artificial events + + void addPairClassMixHistograms() + { + const int nq = std::max(1, static_cast(std::lround(qaflags.cfgPairClassMaxQ.value * 1000.f))); + const AxisSpec axSample{4, -0.5f, 3.5f, "0 same event, 1 standard mixed event, 2 co-reconstructed mixed event, 3 co-reco ME, one source event"}; + const AxisSpec axClass{21, -0.5f, 20.5f, "pair class: 13 true distinct, 14 full swap, 4 half swap, 1-3 duplicates, 16 bremsstrahlung (both true), 5 bremsstrahlung with a fake, 18 photons of the same mother (pi0/eta, both true), 19 swap of two such photons, 20 same mother with a fake, 17 related swap, 15 no label, 6-12 other fakes"}; + const AxisSpec axQ{nq, 0.f, qaflags.cfgPairClassMaxQ.value, "#it{q}_{inv} (GeV/#it{c})"}; + const AxisSpec axOrigin{4, -0.5f, 3.5f, "stored photons: 0 both SVertexer originals, 1 one finder original, 2 both finder, 3 a crossed pairing"}; + const AxisSpec axClean{2, -0.5f, 1.5f, "all four legs below the TPC shared-cluster cut"}; + fRegistryPairMC.add("PairClass/Pairs/hPairClass", "photon pairs by MC class", kTHnSparseF, {axSample, axClass, axQ, axOrigin, axClean}); + fRegistryPairMC.add("PairClass/Pairs/hDEQinv", "photon pairs: |#Delta E| vs q_{inv} by MC class", kTHnSparseF, {axSample, axClass, {100, 0.f, 0.5f, "#it{q}_{inv} (GeV/#it{c})"}, {100, 0.f, 0.5f, "|#it{E}_{1} #minus #it{E}_{2}| (GeV)"}, {std::vector{0., 0.2, 0.4, 0.6, 0.9, 1.5, 3.}, "#it{k}_{T} (GeV/#it{c})"}}); + fRegistryPairMC.add("PairClass/Pairs/hFreeLegPair", "photon pairs by MC class: photons built with a pooled free leg", kTHnSparseF, {axSample, axClass, axQ, {3, -0.5f, 2.5f, "photons of the pair with a free leg: 0, 1, 2"}, {3, -0.5f, 2.5f, "crossed photons (run-2 pairings) in the pair: 0, 1, 2"}}); + fRegistryPairMC.add("PairClass/Pairs/hHalfSwapLegs", "half swaps: run-1 origin of the two legs of the shared true photon", kTHnSparseF, {axSample, axQ, {4, -0.5f, 3.5f, "legs of the shared photon: 0 both free legs, 1 one stored photon (taken apart), 2 two different stored photons, 3 one free one stored"}, {3, -0.5f, 2.5f, "photons of the pair with a free leg: 0, 1, 2"}}); + fRegistryPairMC.add("PairClass/Pairs/hKtQ", "photon pairs: k_{T} vs q_{inv} by MC class", kTHnSparseF, {axSample, axClass, axQ, {100, 0.f, 2.f, "#it{k}_{T} (GeV/#it{c})"}}); + fRegistryPairMC.add("PairClass/Pairs/hQinvQout", "photon pairs: q_{inv} vs |q_{out}| (LCMS) by MC class", kTHnSparseF, {axSample, axClass, axQ, {60, 0.f, 0.3f, "|#it{q}_{out}| (LCMS) (GeV/#it{c})"}, {std::vector{0., 0.2, 0.4, 0.6, 0.9, 1.5, 3.}, "#it{k}_{T} (GeV/#it{c})"}}); + fRegistryPairMC.add("PairClass/Pairs/hPairOpening", "photon pairs with q_{inv} < 50 MeV: orientation of the opening by MC class", kTHnSparseF, {axSample, axClass, {10, 0.f, 0.05f, "#it{q}_{inv} (GeV/#it{c})"}, {30, 0.f, 0.15f, "|#it{q}_{side}| (LCMS) (GeV/#it{c})"}, {30, 0.f, 0.15f, "|#it{q}_{long}| (LCMS) (GeV/#it{c})"}, {18, 0.f, 1.5708f, "#psi = atan(|#Delta#eta| / |#Delta#varphi|) (rad)"}}); + fRegistryPairMC.add("PairClass/Pairs/hSoftHard", "photon pairs with q_{inv} < 100 MeV: soft vs hard photon (reconstructed) by MC class", kTHnSparseF, {axSample, axClass, {20, 0.f, 0.1f, "#it{q}_{inv} (GeV/#it{c})"}, {20, 0.f, 1.f, "#it{E}_{soft} / #it{E}_{hard}"}, {60, -60.f, 60.f, "#it{R}_{xy}(soft) #minus #it{R}_{xy}(hard) (cm)"}, {18, 0.f, 90.f, "#it{R}_{xy}(hard) (cm)"}, {18, 0.f, 1.5708f, "#psi (rad)"}}); + fRegistryPairMC.add("PairClass/Pairs/hPairRes", "pairs of two true photons, q_{inv} < 100 MeV: resolution by MC class", kTHnSparseF, {axSample, axClass, {20, 0.f, 0.1f, "#it{q}_{inv} (GeV/#it{c})"}, {40, -0.02f, 0.02f, "#it{q}_{inv}^{rec} #minus #it{q}_{inv}^{true} (GeV/#it{c})"}, {40, -0.1f, 0.1f, "#Delta#it{E}^{rec} #minus #Delta#it{E}^{true} (GeV)"}}); + fRegistryPairMC.add("PairClass/Pairs/hPairDist", "photon pairs: conversion-point distances by MC class", kTHnSparseF, {axSample, axClass, {50, 0.f, 0.5f, "#it{q}_{inv} (GeV/#it{c})"}, {100, 0.f, 50.f, "reconstructed: distance of the two conversion points (cm)"}, {104, -2.f, 50.f, "true: largest distance of the legs' production vertices (cm)"}}); + fRegistryPairMC.add("PairClass/Pairs/hPairQ", "photon pairs: fitted q, four-leg invariant mass, true q by MC class", kTHnSparseF, {axSample, axClass, {100, 0.f, 0.2f, "#it{q}_{inv} of the fitted photons (GeV/#it{c})"}, {100, 0.f, 0.2f, "#it{M} of the four legs (GeV/#it{c}^{2})"}, {100, 0.f, 0.2f, "#it{q}_{inv} of the two true photons (GeV/#it{c})"}, {6, 0.f, 1.5f, "#it{k}_{T} (GeV/#it{c})"}}); + fRegistryPairMC.add("PairClass/Pairs/hPairAlpha", "photon pairs: signed energy sharing of the two photons by MC class", kTHnSparseF, {axSample, axClass, {50, 0.f, 0.1f, "#it{q}_{inv} (GeV/#it{c})"}, {20, -1.f, 1.f, "#alpha_{1}"}, {20, -1.f, 1.f, "#alpha_{2}"}, {10, 0.f, 1.f, "|#it{E}_{1} #minus #it{E}_{2}| / (#it{E}_{1} + #it{E}_{2}) of the mother photons"}}); + fRegistryPairMC.add("PairClass/Pairs/hPairLegs", "photon pairs: leg diagnostics by MC class", kTHnSparseF, {axSample, axClass, {20, 0.f, 0.1f, "#it{q}_{inv} (GeV/#it{c})"}, {15, 0.f, 0.3f, "max over the two photons of |tan#lambda_{+} #minus tan#lambda_{#minus}|"}, {10, 0.f, 1.f, "softest leg #it{p} (GeV/#it{c})"}, {5, -0.5f, 4.5f, "legs with ITS (of 4)"}, axOrigin}); + fRegistryPairMC.add("PairClass/Pairs/hPairCE", "photon pairs: central-electrode variables by MC class", kTHnSparseF, {axSample, axClass, {20, 0.f, 0.1f, "#it{q}_{inv} (GeV/#it{c})"}, {std::vector{0., 0.05, 0.1, 0.15, 0.2, 0.3, 0.4, 0.6, 0.9, 2.}, "min |#eta| of the two photons"}, {std::vector{0., 1., 2., 5., 10., 20., 50., 300.}, "min |z_{conv}^{true}| (cm)"}, {5, -0.5f, 4.5f, "legs crossing the central electrode in the TPC (of 4)"}, {5, -0.5f, 4.5f, "legs with ITS (of 4)"}}); + fRegistryPairMC.add("PairClass/Pairs/hPairR", "photon pairs: conversion radii by MC class", kTHnSparseF, {axSample, axClass, {20, 0.f, 0.1f, "#it{q}_{inv} (GeV/#it{c})"}, {90, 0.f, 90.f, "#it{R}_{xy} of the inner photon (cm)"}, {90, 0.f, 90.f, "#it{R}_{xy} of the outer photon (cm)"}, {91, -1.f, 90.f, "true #it{R}_{xy} of the inner photon (cm), -1 without MC"}}); + fRegistryPairMC.add("PairClass/Pairs/hBrems", "bremsstrahlung pairs (classes 16 and 5, MC)", kTHnSparseF, {axSample, {2, -0.5f, 1.5f, "0 class 16 (both true), 1 class 5 (with a fake)"}, {20, 0.f, 0.1f, "#it{q}_{inv} (GeV/#it{c})"}, {5, -0.5f, 4.5f, "radiating electron from: 0 photon conversion, 1 #pi^{0} Dalitz, 2 #eta Dalitz, 3 other, 4 not found"}, {6, -0.5f, 5.5f, "mother of the parent photon: 0 #pi^{0}, 1 #eta, 2 #omega, 3 e^{#pm} (bremsstrahlung), 4 other, 5 none / not a photon"}, {50, 0.f, 0.25f, "#theta(#gamma_{brems}, radiating e^{#pm} rec.) (rad)"}, {50, 0.f, 0.25f, "#theta(#gamma_{brems}, parent #gamma) (rad), MC"}, {60, -40.f, 80.f, "true #it{R}_{xy}(#gamma_{brems}) #minus #it{R}_{xy}(parent) (cm)"}, {20, 0.f, 1.f, "#it{E}(#gamma_{brems}) / #it{E}(parent), MC"}}); + fRegistryPairMC.add("PairClass/Pairs/hPhotonQuality", "photons of the pairs by MC class (each photon once per pair)", kTHnSparseF, {axSample, axClass, {20, 0.f, 0.1f, "#it{q}_{inv} (GeV/#it{c})"}, {2, -0.5f, 1.5f, "0 photon radiated by an e^{#pm} (MC mother e^{#pm}), 1 other"}, {50, 0.f, 50.f, "KF #chi^{2}/ndf"}, {60, 0.f, 0.3f, "pointing angle to the PV (rad)"}, {50, 0.f, 5.f, "|DCA_{xy}| to the PV (cm)"}, {50, 0.f, 5.f, "|DCA_{z}| to the PV (cm)"}}); + { + const char* anc = "0 #pi^{0}, 1 #eta, 2 #omega, 3 #eta', 4 #rho/K*, 5 K^{0}, 6 baryon, 7 none (primary), 8 other"; + fRegistryPairMC.add("PairClass/Pairs/hBremsAncestry", "bremsstrahlung pairs (classes 16 and 5, MC): ancestry of the parent photon (the photon whose electron radiated)", kTHnSparseF, {axSample, {2, -0.5f, 1.5f, "0 class 16 (both true), 1 class 5 (with a fake)"}, {20, 0.f, 0.1f, "#it{q}_{inv} (GeV/#it{c})"}, {9, -0.5f, 8.5f, Form("mother of the parent photon: %s", anc)}, {9, -0.5f, 8.5f, Form("mother of that particle: %s", anc)}, {9, -0.5f, 8.5f, Form("root of the decay chain: %s", anc)}}); + } + fRegistryPairMC.add("PairClass/Pairs/hPhotonKF", "photons of the pairs by MC class (each photon once per pair)", kTHnSparseF, {axSample, axClass, {20, 0.f, 0.1f, "#it{q}_{inv} (GeV/#it{c})"}, {2, -0.5f, 1.5f, "0 photon radiated by an e^{#pm} (MC mother e^{#pm}), 1 other"}, {50, 0.f, 25.f, "KF #chi^{2}/ndf of the conversion-vertex fit"}, {60, 0.f, 300.f, "KF #chi^{2} to the primary vertex"}, {40, -0.2f, 0.2f, "#psi_{pair} (rad)"}}); + fRegistryPairMC.add("PairClass/Pairs/hHiddenBrems", "true distinct pairs (class 13) with a photon radiated by an e^{#pm} (MC)", kTHnSparseF, {axSample, {20, 0.f, 0.1f, "#it{q}_{inv} (GeV/#it{c})"}, {3, -0.5f, 2.5f, "photons of the pair radiated by an e^{#pm}"}, {4, -0.5f, 3.5f, "partner vs the radiating e^{#pm}: 0 unrelated, 1 same mother (e.g. Dalitz #gamma), 2 same root ancestor, 3 partner is itself radiated"}, {6, -0.5f, 5.5f, "radiating e^{#pm} from: 0 photon conversion, 1 #pi^{0} Dalitz, 2 #eta Dalitz, 3 heavy flavour (D, B), 4 other hadron, 5 none / unknown"}, {3, -0.5f, 2.5f, "partner vs the radiating e^{#pm}: 0 different first physical primary, 1 same first physical primary, 2 n/a"}}); + fRegistryPairMC.add("PairClass/Photons/hLegTrueDist", "stored photons: distance of the true production vertices of the two legs;0 same event, 1 mixed event;0 SVertexer original, 1 finder original, 2 crossed pairing;distance (cm)", kTH3F, {{2, -0.5f, 1.5f}, {3, -0.5f, 2.5f}, {104, -2.f, 50.f}}); + auto hEv = fRegistryPairMC.add("PairClass/Events/hArtificialEvents", "artificial events;;events", kTH1F, {{4, -0.5f, 3.5f}}); + hEv->GetXaxis()->SetBinLabel(1, "same event"); + hEv->GetXaxis()->SetBinLabel(2, "mixed event"); + hEv->GetXaxis()->SetBinLabel(3, "standard mixing: partner events used"); + hEv->GetXaxis()->SetBinLabel(4, "standard mixing: events without a partner"); + fRegistryPairMC.add("PairClass/Events/hNPhotons", "photons per artificial event;0 same event, 1 mixed event;photons", kTH2F, {{2, -0.5f, 1.5f}, {100, -0.5f, 99.5f}}); + fRegistryPairMC.add("PairClass/Photons/hOrigin", "stored photons;0 same event, 1 mixed event;0 SVertexer original, 1 finder original, 2 crossed pairing", kTH2F, {{2, -0.5f, 1.5f}, {3, -0.5f, 2.5f}}); + fRegistryPairMC.add("PairClass/Legs/hNSigma", "legs of stored photons (same-event artificial events)", kTHnSparseF, {{3, -0.5f, 2.5f, "MC: 0 electron from a photon, 1 electron other, 2 not an electron"}, {60, 0.f, 3.f, "#it{p}_{TPC in} (GeV/#it{c})"}, {100, -5.f, 5.f, "n#sigma_{e}^{TPC}"}, {120, -6.f, 6.f, "n#sigma_{#pi}^{TPC}"}, {2, -0.5f, 1.5f, "has ITS"}}); + } + + static bool pcSamePart(PcLeg const& x, PcLeg const& y) { return x.evt == y.evt && x.pid >= 0 && x.pid == y.pid; } + static bool pcSameMother(PcLeg const& x, PcLeg const& y) { return x.evt == y.evt && x.mid >= 0 && x.mid == y.mid; } + static bool pcDaughterOf(PcLeg const& x, PcLeg const& y) { return x.evt == y.evt && x.mid >= 0 && x.mid == y.pid; } + static bool pcRadiatedBy(PcLeg const& x, PcLeg const& y) { return x.evt == y.evt && x.gmid >= 0 && x.gmid == y.pid; } + static bool pcSiblings(PcLeg const& x, PcLeg const& y) { return x.evt == y.evt && x.gmid >= 0 && x.gmid == y.gmid && x.mid != y.mid; } + static bool pcTruePhoton(PcPhoton const& p) { return p.pos.mpdg == kPcGamma && p.ele.mpdg == kPcGamma && pcSameMother(p.pos, p.ele); } + // bremsstrahlung: a leg of one photon radiated the other photon (the photon's grandmother is that electron) + static bool pcRelated(PcPhoton const& a, PcPhoton const& b) + { + for (const auto* x : {&a.pos, &a.ele}) { + for (const auto* y : {&b.pos, &b.ele}) { + if (pcRadiatedBy(*x, *y) || pcRadiatedBy(*y, *x)) { + return true; + } + } + } + return false; + } + static bool pcSiblingPair(PcPhoton const& a, PcPhoton const& b) + { + for (const auto* x : {&a.pos, &a.ele}) { + for (const auto* y : {&b.pos, &b.ele}) { + if (pcSiblings(*x, *y)) { + return true; + } + } + } + return false; + } + // 1 two true photons, 2 the legs of two true photons crossed (full swap), 3 other with a fake, 4 no label + static int pcPairTruth(PcPhoton const& a, PcPhoton const& b) + { + if (a.pos.mid < 0 || a.ele.mid < 0 || b.pos.mid < 0 || b.ele.mid < 0) { + return 4; + } + auto key = [](PcLeg const& l) { return std::make_pair(l.evt, l.mid); }; + auto isTrue = [](PcLeg const& l) { return l.mid >= 0 && l.mpdg == kPcGamma; }; + const bool allTrue = isTrue(a.pos) && isTrue(a.ele) && isTrue(b.pos) && isTrue(b.ele); + if (allTrue && key(a.pos) == key(a.ele) && key(b.pos) == key(b.ele) && key(a.pos) != key(b.pos)) { + return 1; + } + if (allTrue && key(a.pos) == key(b.ele) && key(a.ele) == key(b.pos) && key(a.pos) != key(a.ele)) { + return 2; + } + return 3; + } + + static int pcFakeClass(PcPhoton const& a, PcPhoton const& b) + { + const std::array la{&a.pos, &a.ele}, lb{&b.pos, &b.ele}; + for (const auto* x : la) { + for (const auto* y : lb) { + if (x->evt == y->evt && x->trk >= 0 && x->trk == y->trk) { + return 12; + } + } + } + if (pcTruePhoton(a) && pcTruePhoton(b) && pcSameMother(a.pos, b.pos)) { + return 1; + } + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < 2; ++j) { + if (pcSamePart(*la[i], *lb[j])) { + return (la[1 - i]->mpdg == kPcGamma && lb[1 - j]->mpdg == kPcGamma) ? 2 : 3; + } + } + } + for (const auto* x : la) { + for (const auto* y : lb) { + if (x->mpdg == kPcGamma && pcSameMother(*x, *y)) { + return 4; + } + } + } + for (const auto* x : la) { + for (const auto* y : lb) { + if (pcDaughterOf(*x, *y) || pcDaughterOf(*y, *x)) { + return 5; + } + } + } + if (pcRelated(a, b)) { + return 5; + } + if (pcSiblingPair(a, b)) { + return 20; + } + for (const auto* x : la) { + for (const auto* y : lb) { + if (pcSameMother(*x, *y)) { + return 6; + } + } + } + const bool aFake = !pcTruePhoton(a), bFake = !pcTruePhoton(b); + if (aFake && bFake) { + return 7; + } + const auto& f = aFake ? a : b; + if (pcSameMother(f.pos, f.ele)) { + return 8; + } + if (std::abs(f.pos.pdg) != kPcElectron || std::abs(f.ele.pdg) != kPcElectron) { + return 9; + } + if (f.pos.mpdg == kPcGamma && f.ele.mpdg == kPcGamma) { + return 10; + } + return 11; + } + static int pcPairClass(PcPhoton const& a, PcPhoton const& b) + { + const int truth = pcPairTruth(a, b); + if (truth == 3) { + return pcFakeClass(a, b); + } + if (truth == 1 || truth == 2) { + if (pcRelated(a, b)) { + return truth == 1 ? 16 : 17; // bremsstrahlung + } + if (pcSiblingPair(a, b)) { + return truth == 1 ? 18 : 19; // photons of the same mother + } + return truth == 1 ? 13 : 14; + } + return 15; + } + // --- end of the copied classification --- + static int pcPairOrigin(PcPhoton const& a, PcPhoton const& b) + { + if (!a.original || !b.original) { + return 3; + } + return (a.svertexer ? 0 : 1) + (b.svertexer ? 0 : 1); + } + void fillPcPair(const float sample, PcPhoton const& a, PcPhoton const& b) + { + const float q = o2::pwgem::photonmeson::qInv(a.px, a.py, a.pz, b.px, b.py, b.pz); + if (q >= qaflags.cfgPairClassMaxQ.value) { + return; + } + const float clean = std::max({a.pos.sharedFrac, a.ele.sharedFrac, b.pos.sharedFrac, b.ele.sharedFrac}) < qaflags.cfgPairClassMaxSharedFrac.value ? 1.f : 0.f; + const auto cls = static_cast(pcPairClass(a, b)); + fRegistryPairMC.fill(HIST("PairClass/Pairs/hPairClass"), sample, cls, q, static_cast(pcPairOrigin(a, b)), clean); + fRegistryPairMC.fill(HIST("PairClass/Pairs/hKtQ"), sample, cls, q, 0.5f * std::hypot(a.px + b.px, a.py + b.py)); + if (cls == 4.f) { // o2-linter: disable=magic-number (half swap) + for (const auto* x : {&a.pos, &a.ele}) { + for (const auto* y : {&b.pos, &b.ele}) { + if (x->mpdg == kPcGamma && pcSameMother(*x, *y)) { + const float src = (x->free && y->free) ? 0.f : (x->free || y->free) ? 3.f + : (x->srcPhoton == y->srcPhoton ? 1.f : 2.f); + fRegistryPairMC.fill(HIST("PairClass/Pairs/hHalfSwapLegs"), sample, q, src, static_cast((a.nFree > 0 ? 1 : 0) + (b.nFree > 0 ? 1 : 0))); + } + } + } + } + { + const float eA = std::sqrt(a.px * a.px + a.py * a.py + a.pz * a.pz), eB = std::sqrt(b.px * b.px + b.py * b.py + b.pz * b.pz); + fRegistryPairMC.fill(HIST("PairClass/Pairs/hDEQinv"), sample, cls, q, std::fabs(eA - eB), 0.5f * std::hypot(a.px + b.px, a.py + b.py)); + } + fRegistryPairMC.fill(HIST("PairClass/Pairs/hFreeLegPair"), sample, cls, q, static_cast((a.nFree > 0 ? 1 : 0) + (b.nFree > 0 ? 1 : 0)), static_cast((a.original ? 0 : 1) + (b.original ? 0 : 1))); + { + const ROOT::Math::PxPyPzMVector v1(a.px, a.py, a.pz, 0.f), v2(b.px, b.py, b.pz, 0.f); + const auto sum = v1 + v2; + const ROOT::Math::Boost bstZ(0., 0., -sum.Pz() / sum.E()); + const auto q3 = bstZ(ROOT::Math::PxPyPzEVector(v1 - v2)).Vect(); + const float qout = std::fabs(q3.Dot(ROOT::Math::XYZVector(sum.Px(), sum.Py(), 0.).Unit())); + const float kt = 0.5f * std::hypot(a.px + b.px, a.py + b.py); + fRegistryPairMC.fill(HIST("PairClass/Pairs/hQinvQout"), sample, cls, q, qout, std::min(kt, 2.99f)); + const auto ktUnit = ROOT::Math::XYZVector(sum.Px(), sum.Py(), 0.).Unit(); + const float qside = std::fabs(q3.Dot(ROOT::Math::XYZVector(-ktUnit.Y(), ktUnit.X(), 0.))); + const float qlong = std::fabs(q3.Z()); + auto etaOf = [](PcPhoton const& p) { return std::asinh(p.pz / std::max(std::hypot(p.px, p.py), 1.e-6f)); }; + const float dEta = std::fabs(etaOf(a) - etaOf(b)); + const float dPhi = std::fabs(RecoDecay::constrainAngle(std::atan2(a.py, a.px) - std::atan2(b.py, b.px), -o2::constants::math::PI)); + const float psi = std::atan2(dEta, dPhi); + if (q < 0.05f) { // o2-linter: disable=magic-number (diagnostic range) + fRegistryPairMC.fill(HIST("PairClass/Pairs/hPairOpening"), sample, cls, q, std::min(qside, 0.149f), std::min(qlong, 0.149f), std::min(psi, 1.57f)); + } + if (q < 0.1f) { // o2-linter: disable=magic-number (diagnostic range) + const float eA = std::hypot(a.px, a.py, a.pz), eB = std::hypot(b.px, b.py, b.pz); + PcPhoton const& soft = eA <= eB ? a : b; + PcPhoton const& hard = eA <= eB ? b : a; + const float rSoft = std::hypot(soft.vx, soft.vy), rHard = std::hypot(hard.vx, hard.vy); + fRegistryPairMC.fill(HIST("PairClass/Pairs/hSoftHard"), sample, cls, q, std::min(std::min(eA, eB) / std::max(std::max(eA, eB), 1.e-6f), 0.999f), std::clamp(rSoft - rHard, -59.9f, 59.9f), std::min(rHard, 89.9f), std::min(psi, 1.57f)); + if (pcTruePhoton(a) && pcTruePhoton(b)) { + const float tA = std::hypot(a.pos.mpx, a.pos.mpy, a.pos.mpz), tB = std::hypot(b.pos.mpx, b.pos.mpy, b.pos.mpz); + const float qT = o2::pwgem::photonmeson::qInv(a.pos.mpx, a.pos.mpy, a.pos.mpz, b.pos.mpx, b.pos.mpy, b.pos.mpz); + const float dERec = eA >= eB ? eA - eB : eB - eA; + const float dETrue = eA >= eB ? tA - tB : tB - tA; // same photon order as the reconstructed one + fRegistryPairMC.fill(HIST("PairClass/Pairs/hPairRes"), sample, cls, q, std::clamp(q - qT, -0.0199f, 0.0199f), std::clamp(dERec - dETrue, -0.0999f, 0.0999f)); + } + } + } + fRegistryPairMC.fill(HIST("PairClass/Pairs/hPairDist"), sample, cls, q, std::hypot(a.vx - b.vx, a.vy - b.vy, a.vz - b.vz), pcTrueDist({&a.pos, &a.ele, &b.pos, &b.ele})); + ROOT::Math::PxPyPzMVector sum4; + const std::array legs4{&a.pos, &a.ele, &b.pos, &b.ele}; + for (const auto* l : legs4) { + sum4 += ROOT::Math::PxPyPzMVector(l->px, l->py, l->pz, o2::constants::physics::MassElectron); + } + float qTrue = -1.f; + { + std::vector mothers; + for (const auto* l : legs4) { + if (l->mid < 0 || l->mpdg != kPcGamma) { + continue; + } + bool seen = false; + for (const auto* m : mothers) { + seen = seen || pcSameMother(*l, *m); + } + if (!seen) { + mothers.push_back(l); + } + } + if (mothers.size() == 2) { + qTrue = o2::pwgem::photonmeson::qInv(mothers[0]->mpx, mothers[0]->mpy, mothers[0]->mpz, mothers[1]->mpx, mothers[1]->mpy, mothers[1]->mpz); + } + } + fRegistryPairMC.fill(HIST("PairClass/Pairs/hPairQ"), sample, cls, q, static_cast(sum4.M()), qTrue, 0.5f * std::hypot(a.px + b.px, a.py + b.py)); + if (q < 0.1f) { + auto dtgl = [](PcPhoton const& p) { const float ptp = std::hypot(p.pos.px, p.pos.py), pte = std::hypot(p.ele.px, p.ele.py); return (ptp > 0.f && pte > 0.f) ? std::fabs(p.pos.pz / ptp - p.ele.pz / pte) : 0.3f; }; + const float pmin = std::min({std::hypot(a.pos.px, a.pos.py, a.pos.pz), std::hypot(a.ele.px, a.ele.py, a.ele.pz), std::hypot(b.pos.px, b.pos.py, b.pos.pz), std::hypot(b.ele.px, b.ele.py, b.ele.pz)}); + const auto nIts = static_cast((a.pos.its ? 1 : 0) + (a.ele.its ? 1 : 0) + (b.pos.its ? 1 : 0) + (b.ele.its ? 1 : 0)); + fRegistryPairMC.fill(HIST("PairClass/Pairs/hPairLegs"), sample, cls, q, std::min(std::max(dtgl(a), dtgl(b)), 0.299f), std::min(pmin, 0.999f), nIts, static_cast(pcPairOrigin(a, b))); + constexpr float kTpcRin = 85.f, kTpcRout = 247.f; // o2-linter: disable=magic-number (TPC drift volume, diagnostic) + auto crossesCE = [&](PcLeg const& l) { + const float pt = std::hypot(l.px, l.py), rConv = std::hypot(l.vx, l.vy); + if (l.pid < 0 || pt <= 0.f || rConv > kTpcRout) { + return 0; + } + const float tgl = l.pz / pt, z1 = l.vz + tgl * (std::max(rConv, kTpcRin) - rConv), z2 = l.vz + tgl * (kTpcRout - rConv); + return z1 * z2 <= 0.f ? 1 : 0; + }; + auto absEta = [](PcPhoton const& p) { return std::fabs(std::asinh(p.pz / std::max(std::hypot(p.px, p.py), 1.e-6f))); }; + auto absZ = [](PcPhoton const& p) { return p.pos.pid >= 0 ? std::fabs(p.pos.vz) : (p.ele.pid >= 0 ? std::fabs(p.ele.vz) : 299.f); }; + const int nCE = crossesCE(a.pos) + crossesCE(a.ele) + crossesCE(b.pos) + crossesCE(b.ele); + fRegistryPairMC.fill(HIST("PairClass/Pairs/hPairCE"), sample, cls, q, std::min(std::min(absEta(a), absEta(b)), 1.99f), std::min(std::min(absZ(a), absZ(b)), 299.f), static_cast(nCE), nIts); + { + const float rA = std::hypot(a.vx, a.vy), rB = std::hypot(b.vx, b.vy); + PcPhoton const& inner = rA <= rB ? a : b; + const float rTrue = inner.pos.pid >= 0 ? std::hypot(inner.pos.vx, inner.pos.vy) : (inner.ele.pid >= 0 ? std::hypot(inner.ele.vx, inner.ele.vy) : -0.5f); + fRegistryPairMC.fill(HIST("PairClass/Pairs/hPairR"), sample, cls, q, std::min(std::min(rA, rB), 89.9f), std::min(std::max(rA, rB), 89.9f), std::min(rTrue, 89.9f)); + } + { + auto radiated = [](PcPhoton const& p) { return p.pos.mpdg == kPcGamma && p.pos.mid >= 0 && std::abs(p.pos.gpdg) == kPcElectron; }; + for (const auto* p : {&a, &b}) { + const float ang = std::acos(std::clamp(p->cospa, -1.f, 1.f)); + fRegistryPairMC.fill(HIST("PairClass/Pairs/hPhotonQuality"), sample, cls, q, radiated(*p) ? 0.f : 1.f, std::min(p->chi2, 49.9f), std::min(ang, 0.299f), std::min(std::fabs(p->dcaxy), 4.99f), std::min(std::fabs(p->dcaz), 4.99f)); + fRegistryPairMC.fill(HIST("PairClass/Pairs/hPhotonKF"), sample, cls, q, radiated(*p) ? 0.f : 1.f, std::clamp(p->chi2Vtx, 0.f, 24.99f), std::clamp(p->chi2PV, 0.f, 299.f), std::clamp(p->psipair, -0.199f, 0.199f)); + } + if (cls == 13.f && (radiated(a) || radiated(b))) { // o2-linter: disable=magic-number (true distinct) + const PcPhoton& r = radiated(a) ? a : b; + const PcPhoton& o = radiated(a) ? b : a; + float rel = 0.f; + if (radiated(o)) { + rel = 3.f; + } else if (o.pos.evt == r.pos.evt && o.pos.gmid >= 0 && o.pos.gmid == r.pos.ggid) { + rel = 1.f; // partner photon and the radiating electron have the same mother + } else if (o.pos.evt == r.pos.evt && o.pos.root >= 0 && o.pos.root == r.pos.root) { + rel = 2.f; + } + const int em = std::abs(r.pos.ggpdg); + float radFrom = 5.f; // o2-linter: disable=magic-number (category codes) + if (em == kPcGamma) { + radFrom = 0.f; + } else if (em == 111) { // o2-linter: disable=pdg/explicit-code (pi0) + radFrom = 1.f; + } else if (em == 221) { // o2-linter: disable=pdg/explicit-code (eta) + radFrom = 2.f; + } else if ((em >= 400 && em < 600) || (em >= 4000 && em < 6000)) { // o2-linter: disable=magic-number (charm and beauty hadrons) + radFrom = 3.f; + } else if (em > 100) { // o2-linter: disable=magic-number (hadrons) + radFrom = 4.f; + } + // first physical primary of the brems photon + const float sameAnc = (r.pos.anc < 0 || o.pos.anc < 0 || r.pos.evt != o.pos.evt) ? 2.f : (r.pos.anc == o.pos.anc ? 1.f : 0.f); + fRegistryPairMC.fill(HIST("PairClass/Pairs/hHiddenBrems"), sample, q, static_cast((radiated(a) ? 1 : 0) + (radiated(b) ? 1 : 0)), rel, radFrom, sameAnc); + } + } + if (cls == 16.f || cls == 5.f) { // o2-linter: disable=magic-number (bremsstrahlung classes) + // x = leg of the bremsstrahlung photon, y = the radiating electron (a leg of the partner photon): x's grandmother is y + const PcLeg *x = nullptr, *y = nullptr; + const std::array, 2> orders{{{&a, &b}, {&b, &a}}}; + for (const auto& [p1, p2] : orders) { + for (const auto* l1 : {&p1->pos, &p1->ele}) { + for (const auto* l2 : {&p2->pos, &p2->ele}) { + if (!x && pcRadiatedBy(*l1, *l2)) { + x = l1; + y = l2; + } + } + } + } + auto pdgClass = [](int pdg) { pdg = std::abs(pdg); return pdg == 111 ? 0 : (pdg == 221 ? 1 : (pdg == 223 ? 2 : (pdg == 11 ? 3 : (pdg == 0 ? 5 : 4)))); }; // o2-linter: disable=magic-number (PDG codes) + auto angle = [](float ax, float ay, float az, float bx, float by, float bz) { + const float n = std::sqrt((ax * ax + ay * ay + az * az) * (bx * bx + by * by + bz * bz)); + return n > 0.f ? std::acos(std::clamp((ax * bx + ay * by + az * bz) / n, -1.f, 1.f)) : 0.249f; + }; + float origin = 4.f, parentMother = 5.f, thE = 0.249f, thG = 0.249f, dR = -39.9f, eFrac = 0.999f; + if (x && y) { + const int mp = std::abs(y->mpdg); + origin = mp == kPcGamma ? 0.f : (mp == 111 ? 1.f : (mp == 221 ? 2.f : 3.f)); // o2-linter: disable=magic-number (PDG codes) + parentMother = mp == kPcGamma ? static_cast(pdgClass(y->gpdg)) : 5.f; + thE = std::min(angle(x->mpx, x->mpy, x->mpz, y->px, y->py, y->pz), 0.249f); + if (mp == kPcGamma) { + thG = std::min(angle(x->mpx, x->mpy, x->mpz, y->mpx, y->mpy, y->mpz), 0.249f); + const float eB = std::hypot(x->mpx, x->mpy, x->mpz), eA = std::hypot(y->mpx, y->mpy, y->mpz); + eFrac = eA > 0.f ? std::min(eB / eA, 0.999f) : 0.999f; + } + dR = std::clamp(std::hypot(x->vx, x->vy) - std::hypot(y->vx, y->vy), -39.9f, 79.9f); + } + fRegistryPairMC.fill(HIST("PairClass/Pairs/hBrems"), sample, cls == 16.f ? 0.f : 1.f, q, origin, parentMother, thE, thG, dR, eFrac); // o2-linter: disable=magic-number (class) + if (x && y && std::abs(y->mpdg) == kPcGamma) { + auto ancClass = [](int pdg) { // o2-linter: disable=pdg/explicit-code (diagnostic classes) + pdg = std::abs(pdg); + switch (pdg) { + case 111: + return 0.f; + case 221: + return 1.f; + case 223: + return 2.f; + case 331: + return 3.f; + case 113: + case 213: + case 313: + case 323: + return 4.f; + case 310: + case 130: + case 311: + return 5.f; + default: + break; + } + if (pdg == 11 || pdg == 13 || pdg == 22) { + return 8.f; // o2-linter: disable=magic-number (leptons, photon: other) + } + if (pdg > 1000) { + return 6.f; // o2-linter: disable=magic-number (baryons) + } + if (pdg < 100) { + return 7.f; // o2-linter: disable=magic-number (no hadron mother: quark, gluon, string, cluster, none) + } + return 8.f; + }; + fRegistryPairMC.fill(HIST("PairClass/Pairs/hBremsAncestry"), sample, cls == 16.f ? 0.f : 1.f, q, ancClass(y->gpdg), ancClass(y->ggpdg), ancClass(y->rootPdg)); // o2-linter: disable=magic-number (class) + } + } + auto alphaOf = [](PcPhoton const& p) { + const float lp = p.pos.px * p.px + p.pos.py * p.py + p.pos.pz * p.pz, le = p.ele.px * p.px + p.ele.py * p.py + p.ele.pz * p.pz; + return (lp + le) != 0.f ? std::clamp((lp - le) / (lp + le), -0.999f, 0.999f) : 0.f; + }; + float asym = -1.f; + { + std::vector mothers; + for (const auto* l : legs4) { + if (l->mid < 0 || l->mpdg != kPcGamma) { + continue; + } + bool seen = false; + for (const auto* m : mothers) { + seen = seen || pcSameMother(*l, *m); + } + if (!seen) { + mothers.push_back(l); + } + } + if (mothers.size() == 2) { + const float e1 = std::hypot(mothers[0]->mpx, mothers[0]->mpy, mothers[0]->mpz), e2 = std::hypot(mothers[1]->mpx, mothers[1]->mpy, mothers[1]->mpz); + asym = (e1 + e2) > 0.f ? std::min(std::fabs(e1 - e2) / (e1 + e2), 0.999f) : -1.f; + } + } + fRegistryPairMC.fill(HIST("PairClass/Pairs/hPairAlpha"), sample, cls, q, alphaOf(a), alphaOf(b), asym); + } + } + static float pcTrueDist(std::initializer_list legs) + { + float d = 0.f; + for (auto x = legs.begin(); x != legs.end(); ++x) { + if ((*x)->pid < 0) { + return -1.f; + } + for (auto y = std::next(x); y != legs.end(); ++y) { + d = std::max(d, std::hypot((*x)->vx - (*y)->vx, (*x)->vy - (*y)->vy, (*x)->vz - (*y)->vz)); + } + } + return d; + } + + void processPairClassMix(aod::PCMMixCollisions const& collisions, aod::V0PhotonsKFMix const& photons, aod::V0LegsMix const& legs, aod::PCMMixLegs const& mixLegs, + aod::V0PhotonsKFChi2Mix const& kfChi2, aod::V0PhotonsPhiVPsiMix const& phiVPsi) + { + std::map> perCollision; + std::map collisionIsMixed; + for (const auto& collision : collisions) { + collisionIsMixed[collision.globalIndex()] = collision.isMixed(); + } + auto legOf = [&](int legRow, bool sameEvent) { + const auto l = legs.rawIteratorAt(legRow); + const auto m = mixLegs.rawIteratorAt(l.trackId()); + const PcLeg out = pcLegFrom(l, m); + if (sameEvent) { + const float ident = std::abs(out.pdg) == kPcElectron ? (out.mpdg == kPcGamma ? 0.f : 1.f) : 2.f; + fRegistryPairMC.fill(HIST("PairClass/Legs/hNSigma"), ident, m.tpcInnerParam(), m.tpcNSigmaEl(), m.tpcNSigmaPi(), m.hasITS() ? 1.f : 0.f); + } + return std::make_pair(out, m.sourceV0Id()); + }; + for (const auto& photon : photons) { + PcPhoton p; + p.px = photon.px(); + p.py = photon.py(); + p.pz = photon.pz(); + p.vx = photon.vx(); + p.vy = photon.vy(); + p.vz = photon.vz(); + p.original = photon.v0Id() >= 0; + const bool sameEvent = !collisionIsMixed[photon.collisionId()]; + const auto [pos, posV0] = legOf(photon.posTrackId(), sameEvent); + const auto [ele, eleV0] = legOf(photon.negTrackId(), sameEvent); + p.pos = pos; + p.ele = ele; + p.svertexer = p.original && posV0 >= 0; + p.nFree = (posV0 == -2 ? 1 : 0) + (eleV0 == -2 ? 1 : 0); // o2-linter: disable=magic-number (free-leg stub v0Id of the mixer) + p.chi2 = photon.chiSquareNDF(); + p.dcaxy = photon.dcaXYtopv(); + p.dcaz = photon.dcaZtopv(); + p.cospa = photon.cospa(); + if (photon.globalIndex() < kfChi2.size()) { + const auto kc = kfChi2.rawIteratorAt(photon.globalIndex()); + p.chi2Vtx = kc.chi2NDFVtx(); + p.chi2PV = kc.chi2PV(); + } + if (photon.globalIndex() < phiVPsi.size()) { + p.psipair = phiVPsi.rawIteratorAt(photon.globalIndex()).psipair(); + } + perCollision[photon.collisionId()].push_back(p); + } + for (const auto& collision : collisions) { + const auto it = perCollision.find(collision.globalIndex()); + const std::vector empty; + const auto& list = it == perCollision.end() ? empty : it->second; + const float type = collision.isMixed() ? 1.f : 0.f; + fRegistryPairMC.fill(HIST("PairClass/Events/hArtificialEvents"), type); + fRegistryPairMC.fill(HIST("PairClass/Events/hNPhotons"), type, static_cast(list.size())); + for (const auto& p : list) { + const float origin = p.original ? (p.svertexer ? 0.f : 1.f) : 2.f; + fRegistryPairMC.fill(HIST("PairClass/Photons/hOrigin"), type, origin); + fRegistryPairMC.fill(HIST("PairClass/Photons/hLegTrueDist"), type, origin, pcTrueDist({&p.pos, &p.ele})); + } + for (size_t i = 0; i < list.size(); ++i) { + for (size_t j = i + 1; j < list.size(); ++j) { + const auto& a = list[i]; + const auto& b = list[j]; + float sample = 0.f; + if (collision.isMixed()) { + const bool oneSource = a.pos.evt == a.ele.evt && a.pos.evt == b.pos.evt && a.pos.evt == b.ele.evt; + sample = oneSource ? 3.f : 2.f; + } + fillPcPair(sample, a, b); + } + } + if (collision.isMixed()) { + continue; + } + // standard mixed event: this same-event artificial event against the pooled ones of the same z and centrality bin + const int zBin = static_cast(std::floor((collision.posZ() + 10.f) / qaflags.cfgPairClassMixZBinWidth.value)); + const int cBin = static_cast(std::floor(collision.centFT0C() / qaflags.cfgPairClassMixCentBinWidth.value)); + auto& pool = mPcPools[zBin * 1000 + cBin]; + if (pool.empty()) { + fRegistryPairMC.fill(HIST("PairClass/Events/hArtificialEvents"), 3.f); + } + for (const auto& partner : pool) { + fRegistryPairMC.fill(HIST("PairClass/Events/hArtificialEvents"), 2.f); + for (const auto& a : list) { + for (const auto& b : partner) { + fillPcPair(1.f, a, b); + } + } + } + pool.push_back(list); + if (static_cast(pool.size()) > qaflags.cfgPairClassMixDepth.value) { + pool.pop_front(); + } + } + } + PROCESS_SWITCH(Photonhbt, processPairClassMix, "pair classes vs q_inv on the output of builder run 2 (artificial events): same event, standard mixed event, co-reconstructed mixed event", false); + + template + static PcLeg pcLegFrom(TLeg const& l, TMixLeg const& m) + { + PcLeg out; + out.evt = m.sourceEventUid(); + out.pid = m.mcParticleId(); + out.mid = m.mcMotherId(); + out.gmid = m.mcGrandMotherId(); + out.pdg = m.mcPdg(); + out.mpdg = m.mcMotherPdg(); + out.gpdg = m.mcGrandMotherPdg(); + out.trk = m.origTrackId(); + out.anc = m.mcAncestorId(); + out.ggid = m.mcGGMotherId(); + out.root = m.mcRootId(); + out.ggpdg = m.mcGGMotherPdg(); + out.rootPdg = m.mcRootPdg(); + out.sharedFrac = l.tpcFractionSharedCls(); + out.vx = m.mcVx(); + out.vy = m.mcVy(); + out.vz = m.mcVz(); + out.px = l.px(); + out.py = l.py(); + out.pz = l.pz(); + out.mpx = m.mcMotherPx(); + out.mpy = m.mcMotherPy(); + out.mpz = m.mcMotherPz(); + out.its = m.hasITS(); + out.free = m.sourceV0Id() == -2; // o2-linter: disable=magic-number (free-leg stub v0Id of the mixer) + out.srcPhoton = m.sourcePhotonUid(); + return out; + } + + template + static PcPhoton pcPhotonFrom(TPhoton const& photon, TLegs const& legs, TMixLegs const& mixLegs) + { + PcPhoton p; + p.px = photon.px(); + p.py = photon.py(); + p.pz = photon.pz(); + p.vx = photon.vx(); + p.vy = photon.vy(); + p.vz = photon.vz(); + p.original = photon.v0Id() >= 0; + const auto lp = legs.rawIteratorAt(photon.posTrackId()); + const auto ln = legs.rawIteratorAt(photon.negTrackId()); + const auto mp = mixLegs.rawIteratorAt(lp.trackId()); + p.pos = pcLegFrom(lp, mp); + p.ele = pcLegFrom(ln, mixLegs.rawIteratorAt(ln.trackId())); + p.svertexer = p.original && mp.sourceV0Id() >= 0; + p.nFree = (mp.sourceV0Id() == -2) + (mixLegs.rawIteratorAt(ln.trackId()).sourceV0Id() == -2); // o2-linter: disable=magic-number (free-leg stub v0Id of the mixer) + p.chi2 = photon.chiSquareNDF(); + p.dcaxy = photon.dcaXYtopv(); + p.dcaz = photon.dcaZtopv(); + p.cospa = photon.cospa(); + return p; + } + + // the two photons share an AO2D track of the same source event (the trackId check of processAnalysis) + static bool pcSharedTrack(PcPhoton const& a, PcPhoton const& b) + { + for (const auto* x : {&a.pos, &a.ele}) { + for (const auto* y : {&b.pos, &b.ele}) { + if (x->evt == y->evt && x->trk >= 0 && x->trk == y->trk) { + return true; + } + } + } + return false; + } + static bool pcHasLabels(PcPhoton const& p) { return p.pos.pid >= 0 && p.ele.pid >= 0; } + // PhotonMCInfo of processMC from the labels in PCMMixLegs (same-event artificial events: all MC ids are of one event) + static PhotonMCInfo pcMCInfo(PcPhoton const& p) + { + PhotonMCInfo info{}; + if (!pcHasLabels(p)) { + return info; + } + info.hasMC = true; + info.mcPosId = p.pos.pid; + info.mcNegId = p.ele.pid; + if (p.pos.mid < 0 || p.ele.mid < 0) { + return info; + } + info.posMotherId = p.pos.mid; + info.negMotherId = p.ele.mid; + info.posMotherIsPhoton = p.pos.mpdg == kPcGamma; + info.negMotherIsPhoton = p.ele.mpdg == kPcGamma; + if (!pcSameMother(p.pos, p.ele)) { + return info; + } + info.sameMother = true; + info.motherId = p.pos.mid; + info.motherPdg = p.pos.mpdg; + info.isTruePhoton = info.motherPdg == kPcGamma; + return info; + } + // isPi0DaughterPair of processMC: two true photons of the same pi0 + static bool pcPi0Daughters(PcPhoton const& a, PcPhoton const& b) + { + return pcTruePhoton(a) && pcTruePhoton(b) && a.pos.evt == b.pos.evt && a.pos.mid != b.pos.mid && a.pos.gmid >= 0 && + a.pos.gmid == b.pos.gmid && std::abs(a.pos.gpdg) == kPi0; + } + // q_inv of the two MC photons behind the four legs, -1 unless the four legs come from exactly two photons + static float pcTrueQ(PcPhoton const& a, PcPhoton const& b) + { + std::vector mothers; + for (const auto* l : {&a.pos, &a.ele, &b.pos, &b.ele}) { + if (l->mid < 0 || l->mpdg != kPcGamma) { + continue; + } + bool seen = false; + for (const auto* m : mothers) { + seen = seen || pcSameMother(*l, *m); + } + if (!seen) { + mothers.push_back(l); + } + } + if (mothers.size() != 2) { + return -1.f; + } + return o2::pwgem::photonmeson::qInv(mothers[0]->mpx, mothers[0]->mpy, mothers[0]->mpz, mothers[1]->mpx, mothers[1]->mpy, mothers[1]->mpz); + } + // signed energy sharing: which charge carries more of the photon momentum (data-computable swap tag, status 13ac) + static float pcAlpha(PcPhoton const& p) + { + const float lp = p.pos.px * p.px + p.pos.py * p.py + p.pos.pz * p.pz, le = p.ele.px * p.px + p.ele.py * p.py + p.ele.pz * p.pz; + return (lp + le) != 0.f ? (lp - le) / (lp + le) : 0.f; + } + // |q_out|, |q_side|, |q_long| in the LCMS, as in fillPairHistogram + static std::array lcmsQ(ROOT::Math::PtEtaPhiMVector const& v1, ROOT::Math::PtEtaPhiMVector const& v2) + { + const auto k12 = 0.5 * (v1 + v2); + const ROOT::Math::XYZVector uvOut(k12.Px() / k12.Pt(), k12.Py() / k12.Pt(), 0), uvLong(0, 0, 1); + const ROOT::Math::XYZVector uvSide = uvOut.Cross(uvLong); + const ROOT::Math::PxPyPzEVector v1c(v1), v2c(v2); + const float betaZ = (v1 + v2).Beta() * std::cos((v1 + v2).Theta()); + const ROOT::Math::Boost bstZ(0, 0, -betaZ); + const auto q3 = bstZ(v1c - v2c).Vect(); + return {static_cast(std::fabs(q3.Dot(uvOut))), static_cast(std::fabs(q3.Dot(uvSide))), static_cast(std::fabs(q3.Dot(uvLong)))}; + } + + void addCoRecoHistograms() + { + const AxisSpec axSample{2, -0.5f, 1.5f, "0 same event, 1 mixed event"}; + const AxisSpec axOrigin{4, -0.5f, 3.5f, "stored photons: 0 both SVertexer originals, 1 one finder original, 2 both finder, 3 a crossed pairing"}; + const AxisSpec axSign{2, -0.5f, 1.5f, "#alpha_{1}#alpha_{2} > 0 (same charge carries more momentum)"}; + auto hEv = fRegistry.add("CoReco/hArtificialEvents", "artificial events (PCMMixCollisions);;events", kTH1F, {{4, -0.5f, 3.5f}}); + hEv->GetXaxis()->SetBinLabel(1, "same event"); + hEv->GetXaxis()->SetBinLabel(2, "same event, centrality"); + hEv->GetXaxis()->SetBinLabel(3, "mixed event"); + hEv->GetXaxis()->SetBinLabel(4, "mixed event, centrality"); + fRegistry.add("CoReco/hNPhotons", "photons per artificial event (before the photon cut);0 same event, 1 mixed event;photons", kTH2F, {axSample, {100, -0.5f, 99.5f}}); + fRegistry.add("CoReco/hOneSourcePairs", "mixed artificial events: pairs with all four legs from one source event;q_{inv} (GeV/c);pairs", kTH1F, {axisQinv}); + fRegistry.add("CoReco/hPairOrigin", "accepted pairs by origin of the stored photons", kTHnSparseF, {axSample, axOrigin, axisQinv, axisKt, axSign}); + if (!isMC) { + return; + } + const AxisSpec axClass{21, -0.5f, 20.5f, "pair class: 13 true distinct, 14 full swap, 4 half swap, 1-3 duplicates, 16 bremsstrahlung (both true), 5 bremsstrahlung with a fake, 18 photons of the same mother (pi0/eta, both true), 19 swap of two such photons, 20 same mother with a fake, 17 related swap, 15 no label, 6-12 other fakes"}; + const AxisSpec axQFine{200, 0.f, 0.2f, "#it{q}_{inv} (GeV/#it{c})"}; + const AxisSpec axQTrue{201, -0.001f, 0.2f, "#it{q}_{inv} of the two MC photons (GeV/#it{c}), -1 if not two photons"}; + const AxisSpec axKtCoarse{std::vector{0., 0.2, 0.4, 0.6, 1.0, 2.0}, "#it{k}_{T} (GeV/#it{c})"}; + fRegistryPairMC.add("CoReco/MC/hClass3D", "accepted pairs by MC class: LCMS q components (CF binning)", kTHnSparseF, {axSample, axClass, axisQout, axisQside, axisQlong, axisKt}); + fRegistryPairMC.add("CoReco/MC/hClassQ", "accepted pairs by MC class: reconstructed vs true q", kTHnSparseF, {axSample, axClass, axQFine, axQTrue, axKtCoarse}); + fRegistryPairMC.add("CoReco/MC/hClassTag", "accepted pairs by MC class: photon origin and energy-sharing sign", kTHnSparseF, {axSample, axClass, axQFine, axOrigin, axSign}); + } + + // processMC's mixed-event truth fill (both photons labelled): both true / one fake / both fake + template + void fillMixTruth(TCollision const& collision, const int true1, const int true2, PairQAObservables const& obs) + { + if (true1 < 0 || true2 < 0) { + return; + } + const int nTrue = true1 + true2; + if (nTrue == 2) { // o2-linter: disable=magic-number (both photons true) + fRegistryPairMC.fill(HIST("Pair/mix/MC/hQinv_TrueTrue"), obs.qinv); + fillPairHistogramMC<1, PairTruthType::TrueTrueDistinct>(collision, obs.v1, obs.v2); + } else if (nTrue == 1) { + fRegistryPairMC.fill(HIST("Pair/mix/MC/hQinv_TrueFake"), obs.qinv); + fillPairHistogramMC<1, PairTruthType::TrueFake>(collision, obs.v1, obs.v2); + } else { + fRegistryPairMC.fill(HIST("Pair/mix/MC/hQinv_FakeFake"), obs.qinv); + fillPairHistogramMC<1, PairTruthType::FakeFake>(collision, obs.v1, obs.v2); + } + } + + // processMC's same-event truth block for an accepted pair, with the MC information from PCMMixLegs + template + void fillCoRecoSameTruth(TCollision const& collision, TPhoton const& g1, TPhoton const& g2, PcPhoton const& a, PcPhoton const& b, + PairQAObservables const& obs, LegPairObservables const& legObs, PhotonWithLegs const& pwl1, PhotonWithLegs const& pwl2, + const bool doQA, const bool doFR) + { + if (!pcHasLabels(a) || !pcHasLabels(b)) { + fillPairHistogramNoLabel(collision, obs.v1, obs.v2); + fRegistryPairMC.fill(HIST("Pair/same/MC/NoLabel/hDEtaDPhi"), obs.deta, obs.dphi); + fRegistryPairMC.fill(HIST("Pair/same/MC/NoLabel/hKt"), obs.kt); + fRegistryPairMC.fill(HIST("Pair/same/MC/NoLabel/hQinv"), obs.qinv); + return; + } + const auto mc1 = pcMCInfo(a), mc2 = pcMCInfo(b); + auto truthType = classifyPairTruth(mc1, mc2); + if (truthType == PairTruthType::TrueTrueDistinct && pcPi0Daughters(a, b)) { + truthType = PairTruthType::Pi0Daughters; + } + const bool doMCQA = passQinvMCQAGate(obs.qinv); + fillMCPairQA(truthType, obs, doQA, doMCQA); + if (mQa.legPairQa) { + fillLegPairMC(truthType, legObs, obs.kt); + } + if (doFR) { + fillMCPairQAFullRange(truthType, obs); + } + const bool isTruePair = (truthType == PairTruthType::TrueTrueDistinct || truthType == PairTruthType::TrueTrueSamePhoton || + truthType == PairTruthType::Pi0Daughters); + if (isTruePair) { + fRegistryPairMC.fill(HIST("Pair/same/MC/hDEtaDPhi_truePairs"), obs.deta, obs.dphi); + if (doMCQA) { + fRegistryPairMC.fill(HIST("Pair/same/MC/hSparse_DEtaDPhi_kT_truePairs"), obs.deta, obs.dphi, obs.kt); + fRegistryPairMC.fill(HIST("Pair/same/MC/hSparse_DEtaDPhi_qinv_truePairs"), obs.deta, obs.dphi, obs.qinv); + } + } else { + fRegistryPairMC.fill(HIST("Pair/same/MC/hDEtaDPhi_fakePairs"), obs.deta, obs.dphi); + if (doMCQA) { + fRegistryPairMC.fill(HIST("Pair/same/MC/hSparse_DEtaDPhi_kT_fakePairs"), obs.deta, obs.dphi, obs.kt); + fRegistryPairMC.fill(HIST("Pair/same/MC/hSparse_DEtaDPhi_qinv_fakePairs"), obs.deta, obs.dphi, obs.qinv); + } + } + if (mc1.sameMother && mc1.isTruePhoton && mc2.sameMother && mc2.isTruePhoton && mc1.motherId != mc2.motherId) { + const ROOT::Math::PxPyPzMVector t1(a.pos.mpx, a.pos.mpy, a.pos.mpz, 0.), t2(b.pos.mpx, b.pos.mpy, b.pos.mpz, 0.); + const float qTrue = static_cast(std::sqrt(std::max(0., -(t1 - t2).M2()))); + fRegistryPairMC.fill(HIST("Pair/same/MC/hQrecVsQtrueVsKt"), qTrue, obs.qinv, obs.kt); + } + fRegistryPairMC.fill(HIST("Pair/same/MC/hSparse_PsiPair_PhiV_Qinv_Type"), std::fabs(g1.psipair()), g1.phiv(), obs.qinv, static_cast(static_cast(truthType)), (mc1.sameMother && mc1.isTruePhoton) ? 1.f : 0.f); + fRegistryPairMC.fill(HIST("Pair/same/MC/hSparse_PsiPair_PhiV_Qinv_Type"), std::fabs(g2.psipair()), g2.phiv(), obs.qinv, static_cast(static_cast(truthType)), (mc2.sameMother && mc2.isTruePhoton) ? 1.f : 0.f); + if (truthType == PairTruthType::FakeFake || truthType == PairTruthType::TrueFake) { + fRegistryPairMC.fill(HIST("Pair/same/MC/hFakeSubtypeVsQinv"), obs.qinv, static_cast(classifyFakeSubtype(mc1, mc2))); + } + const auto crossMC = computeCrossObs(pwl1, pwl2, obs.qinv); + if (crossMC.meeOverQ < 900.f) { // o2-linter: disable=magic-number (qinv was zero, skip) + fRegistryPairMC.fill(HIST("Pair/same/MC/hSparse_MeeRatio_Qinv_Type"), crossMC.meeOverQ, obs.qinv, static_cast(static_cast(truthType))); + } + if (crossMC.bestCombo >= 0) { + fRegistryPairMC.fill(HIST("Pair/same/MC/hSparse_CrossGeom_Qinv_Type"), crossMC.gapBest, std::fabs(crossMC.dzBest), obs.qinv, static_cast(static_cast(truthType))); + } + if (crossMC.qSwap >= 0.f) { + fRegistryPairMC.fill(HIST("Pair/same/MC/hSparse_SwapTemplate_Type"), obs.qinv, crossMC.qSwap, crossMC.geomPhotonLike ? 1.f : 0.f, static_cast(static_cast(truthType))); + } + switch (truthType) { + case PairTruthType::TrueTrueDistinct: + fillPairHistogramMC<0, PairTruthType::TrueTrueDistinct>(collision, obs.v1, obs.v2); + break; + case PairTruthType::TrueTrueSamePhoton: + fillPairHistogramMC<0, PairTruthType::TrueTrueSamePhoton>(collision, obs.v1, obs.v2); + break; + case PairTruthType::SharedMcLeg: + fillPairHistogramMC<0, PairTruthType::SharedMcLeg>(collision, obs.v1, obs.v2); + break; + case PairTruthType::TrueFake: + fillPairHistogramMC<0, PairTruthType::TrueFake>(collision, obs.v1, obs.v2); + break; + case PairTruthType::FakeFake: + fillPairHistogramMC<0, PairTruthType::FakeFake>(collision, obs.v1, obs.v2); + break; + case PairTruthType::Pi0Daughters: + fillPairHistogramMC<0, PairTruthType::Pi0Daughters>(collision, obs.v1, obs.v2); + break; + default: + break; + } + } + + template + int pairCoRecoEvent(TCollision const& collision, std::vector const& ids, MyV0PhotonsMix const& photons, + std::vector const& pc, const float centForQA, const float occupancy, const bool pool) + { + constexpr bool kSame = ev_id == 0; + constexpr float kSample = kSame ? 0.f : 1.f; + std::vector selected(ids.size(), false); + for (size_t i = 0; i < ids.size(); ++i) { + const auto g = photons.rawIteratorAt(ids[i]); + selected[i] = fV0PhotonCut.IsSelected(g); + if (kSame && selected[i] && mQa.singlePhotonQa) { + fillSinglePhotonQAStep<0>(g); + } + } + const auto keyDFCollision = std::make_pair(ndf, static_cast(collision.globalIndex())); + std::unordered_set idsAfterPairCuts; + int ndiphoton = 0; + for (size_t i = 0; i < ids.size(); ++i) { + if (!selected[i]) { + continue; + } + for (size_t j = i + 1; j < ids.size(); ++j) { + if (!selected[j]) { + continue; + } + const auto g1 = photons.rawIteratorAt(ids[i]); + const auto g2 = photons.rawIteratorAt(ids[j]); + const auto& a = pc[ids[i]]; + const auto& b = pc[ids[j]]; + if (pcSharedTrack(a, b)) { + continue; + } + if constexpr (!kSame) { + if (a.pos.evt == a.ele.evt && a.pos.evt == b.pos.evt && a.pos.evt == b.ele.evt) { + fRegistry.fill(HIST("CoReco/hOneSourcePairs"), o2::pwgem::photonmeson::qInv(a.px, a.py, a.pz, b.px, b.py, b.pz)); + if (mixing.cfgCoRecoSkipOneSource.value) { + continue; + } + } + } + if (!passAsymmetryCut(g1.pt(), g2.pt())) { + continue; + } + const auto pos1 = g1.template posTrack_as(), ele1 = g1.template negTrack_as(); + const auto pos2 = g2.template posTrack_as(), ele2 = g2.template negTrack_as(); + auto obs = buildPairQAObservables(g1, g2); + if (!obs.valid) { + continue; + } + const bool doQA = passQinvQAGate(obs.qinv), doFR = passQinvFullRangeGate(obs.qinv); + const auto legObs = buildLegPairObservables(g1, g2, pos1, ele1, pos2, ele2); + auto pwl1 = makePhotonWithLegs(g1, pos1, ele1, collision.posZ()); + auto pwl2 = makePhotonWithLegs(g2, pos2, ele2, collision.posZ()); + if (isMC) { + pwl1.fIsTruePhoton = pcHasLabels(a) ? (pcTruePhoton(a) ? 1 : 0) : -1; + pwl2.fIsTruePhoton = pcHasLabels(b) ? (pcTruePhoton(b) ? 1 : 0) : -1; + } + const auto sep = computePairSep(pwl1, pwl2); + + // ─── before pair cuts ───────────────────────────────────── + fillPairSep(sep, obs); + if (doQA) { + fillPairQAStep(obs, centForQA, occupancy); + if constexpr (kSame) { + fillLegPairQAStep<0>(legObs, obs.kt); + } + } + if (doFR) { + fillFullRangeDeltaRCosOA(obs.qinv, obs.drOverCosOA); + } + if constexpr (kSame) { + fillLegSimilarity(pos1, pos2, obs.qinv); + fillLegSimilarity(ele1, ele2, obs.qinv); + } else { + fillLegSimilarityMix(pwl1, pwl2, obs.qinv); + } + + // ─── pair cuts ──────────────────────────────────────────── + if (!passPhotonClassPairCut(pwl1.legCounts(), pwl2.legCounts())) { + continue; + } + if (obs.drOverCosOA < ggpaircuts.cfgMinDRCosOA) { + continue; + } + if (!passPairMergeCut(sep)) { + continue; + } + if (!passRZCut(obs.deltaR, obs.deltaZ)) { + continue; + } + if (isInsideEllipse(obs.deta, obs.dphi)) { + continue; + } + if (crosspair.cfgDoCrossPairQA.value || crosspair.cfgDoCrossPairCut.value || crosspair.cfgDoCrossGeomCut.value) { + const auto cross = computeCrossObs(pwl1, pwl2, obs.qinv); + const bool vetoed = !passCrossPairVeto(cross); + fillCrossPair(cross, obs.qinv, obs.deltaR, vetoed); + if (vetoed) { + continue; + } + } + + // ─── after pair cuts ────────────────────────────────────── + fillPairSep(sep, obs); + idsAfterPairCuts.insert(ids[i]); + idsAfterPairCuts.insert(ids[j]); + if (doQA) { + fillPairQAStep(obs, centForQA, occupancy); + if constexpr (kSame) { + fillLegPairQAStep<1>(legObs, obs.kt); + } + } + if (doFR) { + fillFullRangeQA(obs, centForQA, occupancy); + } + fillPairHistogram(collision, obs.v1, obs.v2, 1.f); + if constexpr (kSame) { + fRegistryCF.fill(HIST("Pair/same/hPhi_lowerPtV0"), (g1.pt() < g2.pt()) ? g1.phi() : g2.phi()); + } else { + fRegistryCF.fill(HIST("Pair/mix/hPhi_lowerPtV0"), (g1.pt() < g2.pt()) ? g1.phi() : g2.phi()); + } + ndiphoton++; + const auto origin = static_cast(pcPairOrigin(a, b)); + const float sameSign = pcAlpha(a) * pcAlpha(b) > 0.f ? 1.f : 0.f; + fRegistry.fill(HIST("CoReco/hPairOrigin"), kSample, origin, obs.qinv, obs.kt, sameSign); + + if (isMC) { + if constexpr (kSame) { + fillCoRecoSameTruth(collision, g1, g2, a, b, obs, legObs, pwl1, pwl2, doQA, doFR); + } else { + fillMixTruth(collision, pwl1.fIsTruePhoton, pwl2.fIsTruePhoton, obs); + } + const auto cls = static_cast(pcPairClass(a, b)); + const auto q3 = lcmsQ(obs.v1, obs.v2); + fRegistryPairMC.fill(HIST("CoReco/MC/hClass3D"), kSample, cls, q3[0], q3[1], q3[2], obs.kt); + fRegistryPairMC.fill(HIST("CoReco/MC/hClassQ"), kSample, cls, obs.qinv, pcTrueQ(a, b), obs.kt); + fRegistryPairMC.fill(HIST("CoReco/MC/hClassTag"), kSample, cls, obs.qinv, origin, sameSign); + } + if (pool) { + if (usedPhotonIdsPerCol.insert(static_cast(ids[i])).second) { + emh1->AddTrackToEventPool(keyDFCollision, pwl1); + } + if (usedPhotonIdsPerCol.insert(static_cast(ids[j])).second) { + emh1->AddTrackToEventPool(keyDFCollision, pwl2); + } + } + } + } + if (kSame && mQa.singlePhotonQa) { + for (size_t i = 0; i < ids.size(); ++i) { + if (selected[i] && idsAfterPairCuts.contains(ids[i])) { + fillSinglePhotonQAStep<1>(photons.rawIteratorAt(ids[i])); + } + } + } + usedPhotonIdsPerCol.clear(); + return ndiphoton; + } + + template + void mixStandardCoReco(TCollision const& collision, std::tuple const& keyBin, const float centForQA, const float occupancy) + { + const auto keyDFCollision = std::make_pair(ndf, static_cast(collision.globalIndex())); + const auto& selectedPhotons = emh1->GetTracksPerCollision(keyDFCollision); + const auto& poolIDs = emh1->GetCollisionIdsFromEventPool(keyBin); + for (const auto& mixID : poolIDs) { + if (mixID == keyDFCollision) { + continue; + } + const auto& poolPhotons = emh1->GetTracksPerCollision(mixID); + for (const auto& g1 : selectedPhotons) { + for (const auto& g2 : poolPhotons) { + if (!passAsymmetryCut(g1.pt(), g2.pt())) { + continue; + } + if (!passFastQinvGate(g1, g2)) { + continue; + } + auto obs = buildPairQAObservables(g1, g2); + if (!obs.valid) { + continue; + } + const bool doQA = passQinvQAGate(obs.qinv), doFR = passQinvFullRangeGate(obs.qinv); + const auto sep = computePairSep(g1, g2); + fillPairSep<1>(sep, obs); + if (doQA) { + fillPairQAStep<1, 0>(obs, centForQA, occupancy); + } + if (doFR) { + fillFullRangeDeltaRCosOA<1>(obs.qinv, obs.drOverCosOA); + } + fillLegSimilarityMix(g1, g2, obs.qinv); + if (!passPhotonClassPairCut(g1.legCounts(), g2.legCounts())) { + continue; + } + if (obs.drOverCosOA < ggpaircuts.cfgMinDRCosOA) { + continue; + } + if (!passPairMergeCut(sep)) { + continue; + } + if (!passRZCut(obs.deltaR, obs.deltaZ)) { + continue; + } + if (isInsideEllipse(obs.deta, obs.dphi)) { + continue; + } + if (crosspair.cfgDoCrossPairQA.value || crosspair.cfgDoCrossPairCut.value || crosspair.cfgDoCrossGeomCut.value) { + const auto cross = computeCrossObs(g1, g2, obs.qinv); + const bool vetoed = !passCrossPairVeto(cross); + fillCrossPair<1>(cross, obs.qinv, obs.deltaR, vetoed); + if (vetoed) { + continue; + } + } + fillPairSep<1, true>(sep, obs); + if (doQA) { + fillPairQAStep<1, 1>(obs, centForQA, occupancy); + } + if (doFR) { + fillFullRangeQA<1>(obs, centForQA, occupancy); + } + fillPairHistogram<1>(collision, obs.v1, obs.v2, 1.f); + if (isMC) { + fillMixTruth(collision, g1.fIsTruePhoton, g2.fIsTruePhoton, obs); + } + fRegistryCF.fill(HIST("Pair/mix/hPhi_lowerPtV0"), (g1.pt() < g2.pt()) ? g1.phi() : g2.phi()); + } + } + } + emh1->AddCollisionIdAtLast(keyBin, keyDFCollision); + } + + void processCoReco(aod::PCMMixCollisions const& collisions, MyV0PhotonsMix const& photons, aod::V0LegsMix const& legs, aod::PCMMixLegs const& mixLegs) + { + // run-2 photons per artificial event, and the leg view of every photon (source event, AO2D track, MC labels, run-1 origin) + std::vector> photonsOf(collisions.size()); + std::vector pc(photons.size()); + for (const auto& photon : photons) { + if (photon.collisionId() >= 0 && photon.collisionId() < static_cast(collisions.size())) { + photonsOf[photon.collisionId()].push_back(photon.globalIndex()); + } + pc[photon.globalIndex()] = pcPhotonFrom(photon, legs, mixLegs); + } + const bool standardMix = mixing.cfgCoRecoMixMode.value == 1; + for (const auto& collision : collisions) { + const bool mixedEvent = collision.isMixed(); + const auto& ids = photonsOf[collision.globalIndex()]; + fRegistry.fill(HIST("CoReco/hArtificialEvents"), mixedEvent ? 2.f : 0.f); + fRegistry.fill(HIST("CoReco/hNPhotons"), mixedEvent ? 1.f : 0.f, static_cast(ids.size())); + const std::array cent = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + const float centForQA = cent[mixing.cfgCentEstimator]; + if (centForQA < centralitySelection.cfgCentMin || centralitySelection.cfgCentMax < centForQA) { + continue; + } + fRegistry.fill(HIST("CoReco/hArtificialEvents"), mixedEvent ? 3.f : 1.f); + if (mixedEvent && standardMix) { + continue; + } + initCCDB(collision); + const float occupancy = collision.occupancy(); + if (mixedEvent) { + pairCoRecoEvent<1>(collision, ids, photons, pc, centForQA, occupancy, false); + continue; + } + const int ndiphoton = pairCoRecoEvent<0>(collision, ids, photons, pc, centForQA, occupancy, standardMix && mixing.cfgDoMix.value); + if (standardMix && mixing.cfgDoMix.value && ndiphoton > 0) { + const auto keyBin = std::make_tuple(binOf(ztxBinEdges, collision.posZ()), binOf(centBinEdges, centForQA), 0, binOf(occBinEdges, occupancy)); + mixStandardCoReco(collision, keyBin, centForQA, occupancy); + } + } + ndf++; + } + PROCESS_SWITCH(Photonhbt, processCoReco, "analysis (CF, QA, MC truth types) on the output of builder run 2: same event and co-reconstructed mixed event from the artificial events", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& context) diff --git a/PWGEM/PhotonMeson/Tasks/taskFlowReso.cxx b/PWGEM/PhotonMeson/Tasks/taskFlowReso.cxx index 6c4b0ecaed8..dcd322c2ef7 100644 --- a/PWGEM/PhotonMeson/Tasks/taskFlowReso.cxx +++ b/PWGEM/PhotonMeson/Tasks/taskFlowReso.cxx @@ -16,7 +16,7 @@ #include "PWGEM/PhotonMeson/Core/EMPhotonEventCut.h" #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" +#include "PWGEM/PhotonMeson/Utils/EventUtilities.h" #include "Common/Core/EventPlaneHelper.h" #include "Common/Core/RecoDecay.h" @@ -35,9 +35,7 @@ #include -#include #include -#include #include #include #include @@ -48,16 +46,7 @@ using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::soa; using namespace o2::aod::pwgem::photon; - -enum QvecEstimator { - FT0M = 0, - FT0A, - FT0C, - TPCPos, - TPCNeg, - TPCTot, - FV0A -}; +using namespace o2::aod::pwgem::pm::utils::event; enum CentralityEstimator { None = 0, @@ -67,18 +56,6 @@ enum CentralityEstimator { NCentralityEstimators }; -enum Harmonics { - kNone = 0, - kDirect = 1, - kElliptic = 2, - kTriangluar = 3, - kQuadrangular = 4, - kPentagonal = 5, - kHexagonal = 6, - kHeptagonal = 7, - kOctagonal = 8 -}; - struct TaskFlowReso { // configurable for flow @@ -123,8 +100,6 @@ struct TaskFlowReso { using CollsWithQvecs = soa::Join; using CollWithQvec = CollsWithQvecs::iterator; - static constexpr std::size_t NQVecEntries = 6; - HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; void defineEMEventCut() @@ -140,6 +115,12 @@ struct TaskFlowReso { fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + fEMEventCut.SetTrackOccupancyRange(eventcuts.cfgTrackOccupancyMin, eventcuts.cfgTrackOccupancyMax); + fEMEventCut.SetFT0COccupancyRange(eventcuts.cfgFT0COccupancyMin, eventcuts.cfgFT0COccupancyMax); + fEMEventCut.SetRequireGoodQvec(true); + fEMEventCut.SetQvecHarmonic(harmonic); + fEMEventCut.SetMaxQvec(cfgMaxQVector); + fEMEventCut.SetQvecDetectors({qvecDetector.value, qvecSubADetector.value, qvecSubBDetector.value}); } void init(InitContext&) @@ -149,7 +130,7 @@ struct TaskFlowReso { } defineEMEventCut(); - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(®istry); + fEMEventCut.addEventHistograms(®istry); const AxisSpec thnAxisCent{thnConfigAxisCent, "Centrality (%)"}; const AxisSpec thnAxisCosNPhi{thnConfigAxisCosNPhi, Form("cos(%d#varphi)", harmonic.value)}; @@ -262,142 +243,29 @@ struct TaskFlowReso { return cent; } - /// Get all used Q vector - /// \param collision is the collision with the Q vector information - template - std::array getAllQvec(TCollision const& collision) - { - // Retrieve the Q vectors using the helper function for each detector - auto [xQVecMain, yQVecMain] = getQvec(collision, qvecDetector); - auto [xQVecSubA, yQVecSubA] = getQvec(collision, qvecSubADetector); - auto [xQVecSubB, yQVecSubB] = getQvec(collision, qvecSubBDetector); - - return {xQVecMain, yQVecMain, xQVecSubA, yQVecSubA, xQVecSubB, yQVecSubB}; - } - /// Get the Q vector /// \param collision is the collision with the Q vector information template std::pair getQvec(TCollision const& collision, int detector) { - float xQVec = -999.f; - float yQVec = -999.f; - - switch (detector) { - case QvecEstimator::FT0M: - if (harmonic == kElliptic) { - xQVec = collision.q2xft0m(); - yQVec = collision.q2yft0m(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xft0m(); - yQVec = collision.q3yft0m(); - } - break; - case QvecEstimator::FT0A: - if (harmonic == kElliptic) { - xQVec = collision.q2xft0a(); - yQVec = collision.q2yft0a(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xft0a(); - yQVec = collision.q3yft0a(); - } - break; - case QvecEstimator::FT0C: - if (harmonic == kElliptic) { - xQVec = collision.q2xft0c(); - yQVec = collision.q2yft0c(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xft0c(); - yQVec = collision.q3yft0c(); - } - break; - case QvecEstimator::TPCPos: - if (harmonic == kElliptic) { - xQVec = collision.q2xbpos(); - yQVec = collision.q2ybpos(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xbpos(); - yQVec = collision.q3ybpos(); - } - break; - case QvecEstimator::TPCNeg: - if (harmonic == kElliptic) { - xQVec = collision.q2xbneg(); - yQVec = collision.q2ybneg(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xbneg(); - yQVec = collision.q3ybneg(); - } - break; - case QvecEstimator::TPCTot: - if (harmonic == kElliptic) { - xQVec = collision.q2xbtot(); - yQVec = collision.q2ybtot(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xbtot(); - yQVec = collision.q3ybtot(); - } - break; - case QvecEstimator::FV0A: - if (harmonic == kElliptic) { - xQVec = collision.q2xfv0a(); - yQVec = collision.q2yfv0a(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xfv0a(); - yQVec = collision.q3yfv0a(); - } - break; - default: - LOG(warning) << "Q vector estimator not valid. Falling back to FT0M"; - if (harmonic == kElliptic) { - xQVec = collision.q2xft0m(); - yQVec = collision.q2yft0m(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xft0m(); - yQVec = collision.q3yft0m(); - } - break; - } - return {xQVec, yQVec}; - } - - /// Check if the QVector values are within reasonable range - /// \param collision is the collision with the Q vector information - bool isQvecGood(std::array const& QVecs) - { - bool isgood = true; - for (const auto& QVec : QVecs) { - if (std::fabs(QVec) > cfgMaxQVector) { - isgood = false; - break; - } - } - return isgood; + return o2::aod::pwgem::pm::utils::event::getQvec(collision, detector, harmonic); } // Resolution void processResolution(CollWithQvec const& collision) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(®istry, collision); + fEMEventCut.fillEventInfo<0>(®istry, collision); if (!(fEMEventCut.IsSelected(collision))) { // no selection on the centrality is applied on purpose to allow for the resolution study in post-processing return; } - if (!(eventcuts.cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange()) || !(collision.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { - return; - } float cent = getCentrality(collision); if (cent < eventcuts.cfgMinCent || cent > eventcuts.cfgMaxCent) { // event selection return; } - if (!isQvecGood(getAllQvec(collision))) { - // selection based on QVector - return; - } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(®istry, collision); - registry.fill(HIST("Event/before/hCollisionCounter"), 12.0); // accepted - registry.fill(HIST("Event/after/hCollisionCounter"), 12.0); // accepted + fEMEventCut.fillEventInfo<1>(®istry, collision); + fEMEventCut.fillAccepted(®istry); float centrality = getCentrality(collision); // centrality not updated in the rejection mask function float xQVecFT0a = -999.f; diff --git a/PWGEM/PhotonMeson/Tasks/taskPhotonFlow.cxx b/PWGEM/PhotonMeson/Tasks/taskPhotonFlow.cxx index 41f47171448..9ef5ba0e761 100644 --- a/PWGEM/PhotonMeson/Tasks/taskPhotonFlow.cxx +++ b/PWGEM/PhotonMeson/Tasks/taskPhotonFlow.cxx @@ -20,7 +20,7 @@ #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" +#include "PWGEM/PhotonMeson/Utils/EventUtilities.h" #include "Common/Core/EventPlaneHelper.h" #include "Common/Core/RecoDecay.h" @@ -48,7 +48,6 @@ #include #include -#include #include #include #include @@ -61,16 +60,7 @@ using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::soa; using namespace o2::aod::pwgem::photon; - -enum QvecEstimator { - FT0M = 0, - FT0A = 1, - FT0C = 2, - TPCPos = 3, - TPCNeg = 4, - TPCTot = 5, - FV0A = 6 -}; +using namespace o2::aod::pwgem::pm::utils::event; enum CentralityEstimator { None = 0, @@ -80,18 +70,6 @@ enum CentralityEstimator { NCentralityEstimators = 4 }; -enum Harmonics { - kNone = 0, - kDirect = 1, - kElliptic = 2, - kTriangluar = 3, - kQuadrangular = 4, - kPentagonal = 5, - kHexagonal = 6, - kHeptagonal = 7, - kOctagonal = 8 -}; - enum class MapLevel { kGood = 1, kNoBad = 2, @@ -220,8 +198,6 @@ struct TaskPhotonFlow { Partition emcalPhotons = aod::mincluster::storedPhi < MaxPhiEMCalUint; Partition dcalPhotons = aod::mincluster::storedPhi >= MaxPhiEMCalUint; - static constexpr std::size_t NQVecEntries = 6; - PresliceOptional perCollisionEMC = o2::aod::emccluster::pmeventId; PresliceOptional perCollisionPCM = aod::v0photonkf::pmeventId; PresliceOptional perEMCClusterMT = o2::aod::mintm::minClusterId; @@ -274,6 +250,11 @@ struct TaskPhotonFlow { fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + fEMEventCut.SetFT0COccupancyRange(eventcuts.cfgFT0COccupancyMin, eventcuts.cfgFT0COccupancyMax); + fEMEventCut.SetRequireGoodQvec(true); + fEMEventCut.SetQvecHarmonic(harmonic); + fEMEventCut.SetMaxQvec(cfgMaxQVector); + fEMEventCut.SetQvecDetectors({qvecDetector.value, qvecSubADetector.value, qvecSubBDetector.value}); } void defineEMCCut() @@ -342,7 +323,7 @@ struct TaskPhotonFlow { fEMCCut.addQAHistograms(®istry); definePCMCut(); fV0PhotonCut.addQAHistograms(®istry); - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(®istry); + fEMEventCut.addEventHistograms(®istry); const AxisSpec thnAxisPt{thnConfigAxisPt, "#it{p}_{T} (GeV/#it{c})"}; const AxisSpec thnAxisCent{thnConfigAxisCent, "Centrality (%)"}; @@ -428,117 +409,12 @@ struct TaskPhotonFlow { return cent; } - /// Get all used Q vector - /// \param collision is the collision with the Q vector information - template - std::array getAllQvec(TCollision const& collision) - { - // Retrieve the Q vectors using the helper function for each detector - auto [xQVecMain, yQVecMain] = getQvec(collision, qvecDetector); - auto [xQVecSubA, yQVecSubA] = getQvec(collision, qvecSubADetector); - auto [xQVecSubB, yQVecSubB] = getQvec(collision, qvecSubBDetector); - - return {xQVecMain, yQVecMain, xQVecSubA, yQVecSubA, xQVecSubB, yQVecSubB}; - } - /// Get the Q vector /// \param collision is the collision with the Q vector information template std::pair getQvec(TCollision const& collision, int detector) { - float xQVec = -999.f; - float yQVec = -999.f; - - switch (detector) { - case QvecEstimator::FT0M: - if (harmonic == kElliptic) { - xQVec = collision.q2xft0m(); - yQVec = collision.q2yft0m(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xft0m(); - yQVec = collision.q3yft0m(); - } - break; - case QvecEstimator::FT0A: - if (harmonic == kElliptic) { - xQVec = collision.q2xft0a(); - yQVec = collision.q2yft0a(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xft0a(); - yQVec = collision.q3yft0a(); - } - break; - case QvecEstimator::FT0C: - if (harmonic == kElliptic) { - xQVec = collision.q2xft0c(); - yQVec = collision.q2yft0c(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xft0c(); - yQVec = collision.q3yft0c(); - } - break; - case QvecEstimator::TPCPos: - if (harmonic == kElliptic) { - xQVec = collision.q2xbpos(); - yQVec = collision.q2ybpos(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xbpos(); - yQVec = collision.q3ybpos(); - } - break; - case QvecEstimator::TPCNeg: - if (harmonic == kElliptic) { - xQVec = collision.q2xbneg(); - yQVec = collision.q2ybneg(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xbneg(); - yQVec = collision.q3ybneg(); - } - break; - case QvecEstimator::TPCTot: - if (harmonic == kElliptic) { - xQVec = collision.q2xbtot(); - yQVec = collision.q2ybtot(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xbtot(); - yQVec = collision.q3ybtot(); - } - break; - case QvecEstimator::FV0A: - if (harmonic == kElliptic) { - xQVec = collision.q2xfv0a(); - yQVec = collision.q2yfv0a(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xfv0a(); - yQVec = collision.q3yfv0a(); - } - break; - default: - LOG(warning) << "Q vector estimator not valid. Falling back to FT0M"; - if (harmonic == kElliptic) { - xQVec = collision.q2xft0m(); - yQVec = collision.q2yft0m(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xft0m(); - yQVec = collision.q3yft0m(); - } - break; - } - return {xQVec, yQVec}; - } - - /// Check if the QVector values are within reasonable range - /// \param collision is the collision with the Q vector information - bool isQvecGood(std::array const& QVecs) - { - bool isgood = true; - for (const auto& QVec : QVecs) { - if (std::fabs(QVec) > cfgMaxQVector) { - isgood = false; - break; - } - } - return isgood; + return o2::aod::pwgem::pm::utils::event::getQvec(collision, detector, harmonic); } bool isTooCloseToEdge(const int cellID, const int DistanceToBorder = 1) @@ -638,29 +514,20 @@ struct TaskPhotonFlow { bool isFullEventSelected(TCollision const& collision, bool fillHisto = false) { if (fillHisto) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(®istry, collision); + fEMEventCut.fillEventInfo<0>(®istry, collision); } if (!(fEMEventCut.IsSelected(collision))) { // general event selection return false; } - if (!(eventcuts.cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange() && collision.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { - // occupancy selection - return false; - } float cent = getCentrality(collision); if (cent < eventcuts.cfgMinCent || cent > eventcuts.cfgMaxCent) { // event selection return false; } - if (!isQvecGood(getAllQvec(collision))) { - // selection based on QVector - return false; - } if (fillHisto) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(®istry, collision); - registry.fill(HIST("Event/before/hCollisionCounter"), 12.0); // accepted - registry.fill(HIST("Event/after/hCollisionCounter"), 12.0); // accepted + fEMEventCut.fillEventInfo<1>(®istry, collision); + fEMEventCut.fillAccepted(®istry); } return true; } diff --git a/PWGEM/PhotonMeson/Tasks/taskPi0FlowEMC.cxx b/PWGEM/PhotonMeson/Tasks/taskPi0FlowEMC.cxx index df0c4ac6456..5e518f8f507 100644 --- a/PWGEM/PhotonMeson/Tasks/taskPi0FlowEMC.cxx +++ b/PWGEM/PhotonMeson/Tasks/taskPi0FlowEMC.cxx @@ -20,7 +20,7 @@ #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" +#include "PWGEM/PhotonMeson/Utils/EventUtilities.h" #include "Common/Core/EventPlaneHelper.h" #include "Common/Core/RecoDecay.h" @@ -59,7 +59,6 @@ #include #include -#include #include #include #include @@ -73,16 +72,7 @@ using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::soa; using namespace o2::aod::pwgem::photon; - -enum QvecEstimator { - FT0M = 0, - FT0A = 1, - FT0C = 2, - TPCPos = 3, - TPCNeg = 4, - TPCTot = 5, - FV0A = 6 -}; +using namespace o2::aod::pwgem::pm::utils::event; enum CentralityEstimator { None = 0, @@ -92,18 +82,6 @@ enum CentralityEstimator { NCentralityEstimators = 4 }; -enum Harmonics { - kNone = 0, - kDirect = 1, - kElliptic = 2, - kTriangluar = 3, - kQuadrangular = 4, - kPentagonal = 5, - kHexagonal = 6, - kHeptagonal = 7, - kOctagonal = 8 -}; - enum class MapLevel { kGood = 1, kNoBad = 2, @@ -133,7 +111,6 @@ struct TaskPi0FlowEMC { ConfigurableAxis thnConfigAxisCosDeltaPhi{"thnConfigAxisCosDeltaPhi", {8, -1., 1.}, "cos(delta phi) axis for the current event"}; ConfigurableAxis thnConfigAxisM02{"thnConfigAxisM02", {200, 0., 5.}, "M02 axis for the EMCal cluster"}; ConfigurableAxis thnConfigAxisEnergyCalib{"thnConfigAxisEnergyCalib", {200, 0., 20.}, "energy axis for the emcal clusters for the calibration process"}; - ConfigurableAxis thConfigAxisEtaPhiAngle{"thConfigAxisEtaPhiAngle", {180, -o2::constants::math::PI, o2::constants::math::PI}, "atan2(#Delta#eta,#Delta#varphi) axis (deg)"}; EMPhotonEventCut fEMEventCut; struct : ConfigurableGroup { @@ -220,9 +197,13 @@ struct TaskPi0FlowEMC { Configurable minOpenAngle{"minOpenAngle", 0.0202, "apply min opening angle. Default value one EMCal cell"}; Configurable enableTanThetadPhi{"enableTanThetadPhi", false, "flag to turn cut opening angle in delta theta delta phi on/off"}; Configurable minTanThetadPhi{"minTanThetadPhi", 4., "apply min opening angle in delta theta delta phi to cut on late conversion"}; - Configurable maxEnergyAsymmetry{"maxEnergyAsymmetry", 1., "apply max energy asymmetry for meson candidate"}; Configurable cfgEnableQA{"cfgEnableQA", false, "flag to turn QA plots on/off"}; ConfigurableAxis thConfigAxisTanThetaPhi{"thConfigAxisTanThetaPhi", {180, -90.f, 90.f}, ""}; + Configurable cfgPairBkgMeson{"cfgPairBkgMeson", 0, "meson for the mass binning of the pair background QA THn (0: pi0, 1: eta)"}; + ConfigurableAxis thnConfigAxisPairBkgMassPi0{"thnConfigAxisPairBkgMassPi0", {VARIABLE_WIDTH, 0.0f, 0.07f, 0.10f, 0.17f, 0.30f, 0.50f}, "invariant mass axis (sidebands and peak) of the pair background QA THn for the pi0"}; + ConfigurableAxis thnConfigAxisPairBkgMassEta{"thnConfigAxisPairBkgMassEta", {VARIABLE_WIDTH, 0.30f, 0.45f, 0.50f, 0.60f, 0.70f, 0.80f}, "invariant mass axis (sidebands and peak) of the pair background QA THn for the eta"}; + ConfigurableAxis thnConfigAxisPairBkgPt{"thnConfigAxisPairBkgPt", {VARIABLE_WIDTH, 0.f, 1.f, 2.f, 3.f, 5.f, 8.f, 20.f}, "pT axis of the pair background QA THn"}; + ConfigurableAxis thnConfigAxisPairBkgCent{"thnConfigAxisPairBkgCent", {VARIABLE_WIDTH, 0.f, 10.f, 30.f, 50.f, 90.f}, "centrality axis of the pair background QA THn"}; } mesonConfig; struct : ConfigurableGroup { @@ -272,8 +253,6 @@ struct TaskPi0FlowEMC { Partition emcalPhotons = aod::mincluster::storedPhi < MaxPhiEMCalUint; Partition dcalPhotons = aod::mincluster::storedPhi >= MaxPhiEMCalUint; - static constexpr std::size_t NQVecEntries = 6; - PresliceOptional perCollisionEMC = o2::aod::emccluster::pmeventId; PresliceOptional perCollisionPCM = aod::v0photonkf::pmeventId; PresliceOptional perEMCClusterMT = o2::aod::mintm::minClusterId; @@ -332,6 +311,11 @@ struct TaskPi0FlowEMC { fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + fEMEventCut.SetFT0COccupancyRange(eventcuts.cfgFT0COccupancyMin, eventcuts.cfgFT0COccupancyMax); + fEMEventCut.SetRequireGoodQvec(true); + fEMEventCut.SetQvecHarmonic(harmonic); + fEMEventCut.SetMaxQvec(cfgMaxQVector); + fEMEventCut.SetQvecDetectors({qvecDetector.value, qvecSubADetector.value, qvecSubBDetector.value}); } void defineEMCCut() @@ -400,7 +384,7 @@ struct TaskPi0FlowEMC { fEMCCut.addQAHistograms(®istry); definePCMCut(); fV0PhotonCut.addQAHistograms(®istry); - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(®istry); + fEMEventCut.addEventHistograms(®istry); const AxisSpec thnAxisInvMass{thnConfigAxisInvMass, "#it{M}_{#gamma#gamma} (GeV/#it{c}^{2})"}; const AxisSpec thnAxisPt{thnConfigAxisPt, "#it{p}_{T} (GeV/#it{c})"}; @@ -418,7 +402,12 @@ struct TaskPi0FlowEMC { const AxisSpec thnAxisMixingVtx{mixingConfig.cfgVtxBins, "#it{z} (cm)"}; const AxisSpec thnAxisMixingCent{mixingConfig.cfgCentBins, "Centrality (%)"}; const AxisSpec thnAxisMixingEP{mixingConfig.cfgEPBins, Form("cos(%d#varphi)", harmonic.value)}; - const AxisSpec thAxisEtaPhiAngle{thConfigAxisEtaPhiAngle, "atan2(#Delta#eta,#Delta#varphi) (rad)"}; + + const AxisSpec thnQaAxisMinv{16, 0, 0.8, "#it{M}_{#gamma#gamma} (GeV/#it{c}^{2})"}; // bin witdh 0.05 GeV/c^2 + const AxisSpec thnQaAxisPt{10, 0, 20., "#it{p}_{T} (GeV/#it{c})"}; // bin width 2 GeV/c + const AxisSpec thnQaAxisDeltaEta{20, -1.6, 1.6, "|#Delta#eta|"}; // bin witdh 0.16 + const AxisSpec thnQaAxisDeltaPhi{24, 0, o2::constants::math::PI, "#Delta#varphi"}; // bin width 7.5 degrees in rad + const AxisSpec thnQaAxisCent{10, 0, 100, "Centrality (%)"}; // bin width 10% if (!doprocessM02) { registry.add("hSparsePi0Flow", " vs m_{inv} vs p_T vs cent for same event", HistType::kTProfile3D, {thnAxisInvMass, thnAxisPt, thnAxisCent}); @@ -472,8 +461,16 @@ struct TaskPi0FlowEMC { registry.add("mesonQA/hInvMassPtMixed", "Histo for inv pair mass vs pt for mixed event", HistType::kTH2D, {thnAxisInvMass, thnAxisPt}); registry.add("mesonQA/hTanThetaPhiMixed", "Histo for identification of conversion cluster for mixed event", HistType::kTH2D, {thnAxisInvMass, thAxisTanThetaPhi}); registry.add("mesonQA/hAlphaPtMixed", "Histo of meson asymmetry vs pT for mixed event", HistType::kTH2D, {thAxisAlpha, thnAxisPt}); - registry.add("mesonQA/hEtaPhiAngleMassCent", "atan2(#Delta#eta,#Delta#varphi) vs m_{inv} vs cent, same event", HistType::kTH3D, {thAxisEtaPhiAngle, thnAxisInvMass, thnAxisCent}); - registry.add("mesonQA/hEtaPhiAngleMassCentMixed", "atan2(#Delta#eta,#Delta#varphi) vs m_{inv} vs cent, mixed event", HistType::kTH3D, {thAxisEtaPhiAngle, thnAxisInvMass, thnAxisCent}); + const bool isPCMEMC = doprocessEMCalPCMC.value || doprocessEMCalPCMMixed.value; + const AxisSpec thnAxisPairBkgMass{mesonConfig.cfgPairBkgMeson.value == 0 ? mesonConfig.thnConfigAxisPairBkgMassPi0 : mesonConfig.thnConfigAxisPairBkgMassEta, "#it{M}_{#gamma#gamma} (GeV/#it{c}^{2})"}; + const AxisSpec thnAxisPairBkgPt{mesonConfig.thnConfigAxisPairBkgPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec thnAxisPairBkgCent{mesonConfig.thnConfigAxisPairBkgCent, "Centrality (%)"}; + const AxisSpec thnAxisPairBkgAlpha = isPCMEMC ? AxisSpec{40, -1.f, 1.f, "#alpha = (#it{E}_{PCM} - #it{E}_{EMC}) / (#it{E}_{PCM} + #it{E}_{EMC})"} : AxisSpec{20, 0.f, 1.f, "#alpha = |#it{E}_{1} - #it{E}_{2}| / (#it{E}_{1} + #it{E}_{2})"}; + const AxisSpec thnAxisPairBkgDirection{36, -o2::constants::math::PIHalf, o2::constants::math::PIHalf, "atan2(#Delta#eta, #Delta#varphi) (rad)"}; + registry.add("mesonQA/hPairBkgSE", "#it{m}_{inv} vs #it{p}_{T} vs cent vs #alpha vs pair direction for same event", HistType::kTHnF, {thnAxisPairBkgMass, thnAxisPairBkgPt, thnAxisPairBkgCent, thnAxisPairBkgAlpha, thnAxisPairBkgDirection}); + registry.add("mesonQA/hPairBkgME", "#it{m}_{inv} vs #it{p}_{T} vs cent vs #alpha vs pair direction for mixed event", HistType::kTHnF, {thnAxisPairBkgMass, thnAxisPairBkgPt, thnAxisPairBkgCent, thnAxisPairBkgAlpha, thnAxisPairBkgDirection}); + registry.add("mesonQA/hDeltaEtaDeltaPhiMinvPtCentSE", "#it{m}_{inv} vs #it{p}_{T} vs #Delta#eta vs #Delta#varphi vs cent for same event", HistType::kTHnF, {thnQaAxisMinv, thnQaAxisPt, thnQaAxisDeltaEta, thnQaAxisDeltaPhi, thnQaAxisCent}); + registry.add("mesonQA/hDeltaEtaDeltaPhiMinvPtCentME", "#it{m}_{inv} vs #it{p}_{T} vs #Delta#eta vs #Delta#varphi vs cent for mixed event", HistType::kTHnF, {thnQaAxisMinv, thnQaAxisPt, thnQaAxisDeltaEta, thnQaAxisDeltaPhi, thnQaAxisCent}); } if (correctionConfig.doEMCalCalib.value) { @@ -534,6 +531,38 @@ struct TaskPi0FlowEMC { registry.fill(HIST(HistTypes[histType]), mass, pt, cent); } + /// \brief Fill the pair background QA THn (m_inv, pT, cent, alpha, direction) + /// The direction is folded such that dPhi is in [0, pi] and dEta keeps its sign, identical for same and mixed event. + /// \tparam isMixed fill the mixed instead of the same event histogram + /// \tparam isSignedAlpha use the signed asymmetry (E1 - E2) / (E1 + E2), for PCM-EMC photon1 has to be the PCM photon + /// \param photon1 first photon + /// \param photon2 second photon + /// \param meson pair of photon1 and photon2 + /// \param cent centrality of the (first) collision + template + void fillPairBkg(ROOT::Math::PtEtaPhiMVector const& photon1, ROOT::Math::PtEtaPhiMVector const& photon2, ROOT::Math::PtEtaPhiMVector const& meson, const float cent) + { + if (!mesonConfig.cfgEnableQA.value) { + return; + } + float alpha = (photon1.E() - photon2.E()) / (photon1.E() + photon2.E()); + if constexpr (!isSignedAlpha) { + alpha = std::fabs(alpha); + } + float dPhi = RecoDecay::constrainAngle(photon1.Phi() - photon2.Phi(), -o2::constants::math::PI); // wrap to [-pi, pi) + float dEta = photon1.Eta() - photon2.Eta(); + if (dPhi < 0.f) { // swap the labels: flip BOTH signs + dPhi = -dPhi; + dEta = -dEta; + } + const float direction = std::atan2(dEta, dPhi); // in [-pi/2, pi/2] since dPhi >= 0 + if constexpr (isMixed) { + registry.fill(HIST("mesonQA/hPairBkgME"), meson.M(), meson.Pt(), cent, alpha, direction); + } else { + registry.fill(HIST("mesonQA/hPairBkgSE"), meson.M(), meson.Pt(), cent, alpha, direction); + } + } + /// \brief eta a cluster would have if its own vertex vzOld is swapped for vzNew, /// assuming a nominal cylindrical EMCal surface at transverse radius emcalR (cm). /// phi is untouched: transverse vertex spread is negligible next to emcalR. @@ -570,117 +599,12 @@ struct TaskPi0FlowEMC { return cent; } - /// Get all used Q vector - /// \param collision is the collision with the Q vector information - template - std::array getAllQvec(TCollision const& collision) - { - // Retrieve the Q vectors using the helper function for each detector - auto [xQVecMain, yQVecMain] = getQvec(collision, qvecDetector); - auto [xQVecSubA, yQVecSubA] = getQvec(collision, qvecSubADetector); - auto [xQVecSubB, yQVecSubB] = getQvec(collision, qvecSubBDetector); - - return {xQVecMain, yQVecMain, xQVecSubA, yQVecSubA, xQVecSubB, yQVecSubB}; - } - /// Get the Q vector /// \param collision is the collision with the Q vector information template std::pair getQvec(TCollision const& collision, int detector) { - float xQVec = -999.f; - float yQVec = -999.f; - - switch (detector) { - case QvecEstimator::FT0M: - if (harmonic == kElliptic) { - xQVec = collision.q2xft0m(); - yQVec = collision.q2yft0m(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xft0m(); - yQVec = collision.q3yft0m(); - } - break; - case QvecEstimator::FT0A: - if (harmonic == kElliptic) { - xQVec = collision.q2xft0a(); - yQVec = collision.q2yft0a(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xft0a(); - yQVec = collision.q3yft0a(); - } - break; - case QvecEstimator::FT0C: - if (harmonic == kElliptic) { - xQVec = collision.q2xft0c(); - yQVec = collision.q2yft0c(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xft0c(); - yQVec = collision.q3yft0c(); - } - break; - case QvecEstimator::TPCPos: - if (harmonic == kElliptic) { - xQVec = collision.q2xbpos(); - yQVec = collision.q2ybpos(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xbpos(); - yQVec = collision.q3ybpos(); - } - break; - case QvecEstimator::TPCNeg: - if (harmonic == kElliptic) { - xQVec = collision.q2xbneg(); - yQVec = collision.q2ybneg(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xbneg(); - yQVec = collision.q3ybneg(); - } - break; - case QvecEstimator::TPCTot: - if (harmonic == kElliptic) { - xQVec = collision.q2xbtot(); - yQVec = collision.q2ybtot(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xbtot(); - yQVec = collision.q3ybtot(); - } - break; - case QvecEstimator::FV0A: - if (harmonic == kElliptic) { - xQVec = collision.q2xfv0a(); - yQVec = collision.q2yfv0a(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xfv0a(); - yQVec = collision.q3yfv0a(); - } - break; - default: - LOG(warning) << "Q vector estimator not valid. Falling back to FT0M"; - if (harmonic == kElliptic) { - xQVec = collision.q2xft0m(); - yQVec = collision.q2yft0m(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xft0m(); - yQVec = collision.q3yft0m(); - } - break; - } - return {xQVec, yQVec}; - } - - /// Check if the QVector values are within reasonable range - /// \param collision is the collision with the Q vector information - bool isQvecGood(std::array const& QVecs) - { - bool isgood = true; - for (const auto& QVec : QVecs) { - if (std::fabs(QVec) > cfgMaxQVector) { - isgood = false; - break; - } - } - return isgood; + return o2::aod::pwgem::pm::utils::event::getQvec(collision, detector, harmonic); } bool isTooCloseToEdge(const int cellID, const int DistanceToBorder = 1) @@ -1020,29 +944,20 @@ struct TaskPi0FlowEMC { bool isFullEventSelected(TCollision const& collision, bool fillHisto = false) { if (fillHisto) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(®istry, collision); + fEMEventCut.fillEventInfo<0>(®istry, collision); } if (!(fEMEventCut.IsSelected(collision))) { // general event selection return false; } - if (!(eventcuts.cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange() && collision.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { - // occupancy selection - return false; - } float cent = getCentrality(collision); if (cent < eventcuts.cfgMinCent || cent > eventcuts.cfgMaxCent) { // event selection return false; } - if (!isQvecGood(getAllQvec(collision))) { - // selection based on QVector - return false; - } if (fillHisto) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(®istry, collision); - registry.fill(HIST("Event/before/hCollisionCounter"), 12.0); // accepted - registry.fill(HIST("Event/after/hCollisionCounter"), 12.0); // accepted + fEMEventCut.fillEventInfo<1>(®istry, collision); + fEMEventCut.fillAccepted(®istry); } return true; } @@ -1070,7 +985,12 @@ struct TaskPi0FlowEMC { float dTheta = v1.Theta() - v2.Theta(); float dPhi = v1.Phi() - v2.Phi(); + float dPhiWrapped = RecoDecay::constrainAngle(v1.Phi() - v2.Phi(), -o2::constants::math::PI); // wrap to [-π, π) float dEta = v1.Eta() - v2.Eta(); + if (dPhiWrapped < 0.) { // swap the labels: flip BOTH signs + dPhiWrapped = -dPhiWrapped; + dEta = -dEta; + } float openingAngle = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); registry.fill(HIST("hMesonCuts"), 1); @@ -1093,13 +1013,14 @@ struct TaskPi0FlowEMC { registry.fill(HIST("mesonQA/hInvMassPt"), vMeson.M(), vMeson.Pt()); registry.fill(HIST("mesonQA/hTanThetaPhi"), vMeson.M(), getAngleDegree(std::atan(dTheta / dPhi))); registry.fill(HIST("mesonQA/hAlphaPt"), (v1.E() - v2.E()) / (v1.E() + v2.E()), vMeson.Pt()); - registry.fill(HIST("mesonQA/hEtaPhiAngleMassCent"), RecoDecay::constrainAngle(std::atan2(dEta, dPhi), -o2::constants::math::PI), vMeson.M(), getCentrality(collision)); + registry.fill(HIST("mesonQA/hDeltaEtaDeltaPhiMinvPtCentSE"), vMeson.M(), vMeson.Pt(), dEta, dPhiWrapped, getCentrality(collision)); } if (mesonConfig.enableTanThetadPhi.value && mesonConfig.minTanThetadPhi > std::fabs(getAngleDegree(std::atan(dTheta / dPhi)))) { registry.fill(HIST("hMesonCuts"), 5); continue; } registry.fill(HIST("hMesonCuts"), 6); + fillPairBkg(v1, v2, vMeson, getCentrality(collision)); runFlowAnalysis<0>(collision, vMeson); } } @@ -1173,7 +1094,7 @@ struct TaskPi0FlowEMC { fEMCCut.AreSelectedRunning(flags, clusters, matchedPrims, matchedSeconds); using BinningType = ColumnBinningPolicy>; - BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins, mixingConfig.cfgEPBins}, true}; + BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins, mixingConfig.cfgEPBins}}; auto clustersTuple = std::make_tuple(clusters); SameKindPair pair{binningMixedEvent, mixingConfig.cfgMixingDepth, -1, collisions, clustersTuple, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored @@ -1183,18 +1104,10 @@ struct TaskPi0FlowEMC { // general event selection continue; } - if (!(eventcuts.cfgFT0COccupancyMin <= c1.ft0cOccupancyInTimeRange()) || !(c1.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax) || !(eventcuts.cfgFT0COccupancyMin <= c2.ft0cOccupancyInTimeRange()) || !(c2.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { - // occupancy selection - continue; - } if (getCentrality(c1) < eventcuts.cfgMinCent || getCentrality(c1) > eventcuts.cfgMaxCent || getCentrality(c2) < eventcuts.cfgMinCent || getCentrality(c2) > eventcuts.cfgMaxCent) { // event selection continue; } - if (!isQvecGood(getAllQvec(c1)) || !isQvecGood(getAllQvec(c2))) { - // selection based on QVector - continue; - } runNow = c1.runNumber(); if (runNow != runBefore) { initCCDB(c1); @@ -1233,7 +1146,12 @@ struct TaskPi0FlowEMC { float dTheta = v1.Theta() - v2.Theta(); float dPhi = v1.Phi() - v2.Phi(); - float dEta = v1.Eta() - eta2; + float dPhiWrapped = RecoDecay::constrainAngle(v1.Phi() - v2.Phi(), -o2::constants::math::PI); // wrap to [-π, π) + float dEta = v1.Eta() - v2.Eta(); + if (dPhiWrapped < 0.) { // swap the labels: flip BOTH signs + dPhiWrapped = -dPhiWrapped; + dEta = -dEta; + } float openingAngle = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); registry.fill(HIST("hMesonCutsMixed"), 1); @@ -1253,13 +1171,14 @@ struct TaskPi0FlowEMC { registry.fill(HIST("mesonQA/hInvMassPtMixed"), vMeson.M(), vMeson.Pt()); registry.fill(HIST("mesonQA/hTanThetaPhiMixed"), vMeson.M(), getAngleDegree(std::atan(dTheta / dPhi))); registry.fill(HIST("mesonQA/hAlphaPtMixed"), (v1.E() - v2.E()) / (v1.E() + v2.E()), vMeson.Pt()); - registry.fill(HIST("mesonQA/hEtaPhiAngleMassCentMixed"), RecoDecay::constrainAngle(std::atan2(dEta, dPhi), -o2::constants::math::PI), vMeson.M(), getCentrality(c1)); + registry.fill(HIST("mesonQA/hDeltaEtaDeltaPhiMinvPtCentME"), vMeson.M(), vMeson.Pt(), dEta, dPhiWrapped, getCentrality(c1)); } if (mesonConfig.enableTanThetadPhi.value && mesonConfig.minTanThetadPhi > std::fabs(getAngleDegree(std::atan(dTheta / dPhi)))) { registry.fill(HIST("hMesonCutsMixed"), 5); continue; } registry.fill(HIST("hMesonCutsMixed"), 6); + fillPairBkg(v1, v2, vMeson, getCentrality(c1)); runFlowAnalysis<2>(c1, vMeson); } } @@ -1326,6 +1245,12 @@ struct TaskPi0FlowEMC { float dTheta = v1.Theta() - v2.Theta(); float dPhi = v1.Phi() - v2.Phi(); + float dPhiWrapped = RecoDecay::constrainAngle(v1.Phi() - v2.Phi(), -o2::constants::math::PI); // wrap to [-π, π) + float dEta = v1.Eta() - v2.Eta(); + if (dPhiWrapped < 0.) { // swap the labels: flip BOTH signs + dPhiWrapped = -dPhiWrapped; + dEta = -dEta; + } float openingAngle = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); registry.fill(HIST("hMesonCuts"), 1); if (openingAngle <= mesonConfig.minOpenAngle) { @@ -1347,11 +1272,13 @@ struct TaskPi0FlowEMC { registry.fill(HIST("mesonQA/hInvMassPt"), vMeson.M(), vMeson.Pt()); registry.fill(HIST("mesonQA/hTanThetaPhi"), vMeson.M(), getAngleDegree(std::atan(dTheta / dPhi))); registry.fill(HIST("mesonQA/hAlphaPt"), (v1.E() - v2.E()) / (v1.E() + v2.E()), vMeson.Pt()); + registry.fill(HIST("mesonQA/hDeltaEtaDeltaPhiMinvPtCentSE"), vMeson.M(), vMeson.Pt(), dEta, dPhiWrapped, getCentrality(collision)); } if (mesonConfig.enableTanThetadPhi.value && mesonConfig.minTanThetadPhi > std::fabs(getAngleDegree(std::atan(dTheta / dPhi)))) { registry.fill(HIST("hMesonCuts"), 5); continue; } + fillPairBkg(v2, v1, vMeson, getCentrality(collision)); // v2 is the PCM photon runFlowAnalysis<0>(collision, vMeson); } } @@ -1367,7 +1294,7 @@ struct TaskPi0FlowEMC { } using BinningTypeMixed = ColumnBinningPolicy>; - BinningTypeMixed binningOnPositions{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins, mixingConfig.cfgEPBins}, true}; + BinningTypeMixed binningOnPositions{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins, mixingConfig.cfgEPBins}}; auto associatedTables = std::make_tuple(clusters, pcmPhotons); @@ -1388,18 +1315,10 @@ struct TaskPi0FlowEMC { // general event selection continue; } - if (!(eventcuts.cfgFT0COccupancyMin <= c1.ft0cOccupancyInTimeRange()) || !(c1.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax) || !(eventcuts.cfgFT0COccupancyMin <= c2.ft0cOccupancyInTimeRange()) || !(c2.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { - // occupancy selection - continue; - } if (getCentrality(c1) < eventcuts.cfgMinCent || getCentrality(c1) > eventcuts.cfgMaxCent || getCentrality(c2) < eventcuts.cfgMinCent || getCentrality(c2) > eventcuts.cfgMaxCent) { // event selection continue; } - if (!isQvecGood(getAllQvec(c1)) || !isQvecGood(getAllQvec(c2))) { - // selection based on QVector - continue; - } runNow = c1.runNumber(); if (runNow != runBefore) { initCCDB(c1); @@ -1422,6 +1341,12 @@ struct TaskPi0FlowEMC { float dTheta = v1.Theta() - v2.Theta(); float dPhi = v1.Phi() - v2.Phi(); + float dPhiWrapped = RecoDecay::constrainAngle(v1.Phi() - v2.Phi(), -o2::constants::math::PI); // wrap to [-π, π) + float dEta = v1.Eta() - v2.Eta(); + if (dPhiWrapped < 0.) { // swap the labels: flip BOTH signs + dPhiWrapped = -dPhiWrapped; + dEta = -dEta; + } float openingAngle = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); registry.fill(HIST("hMesonCutsMixed"), 1); @@ -1441,12 +1366,14 @@ struct TaskPi0FlowEMC { registry.fill(HIST("mesonQA/hInvMassPtMixed"), vMeson.M(), vMeson.Pt()); registry.fill(HIST("mesonQA/hTanThetaPhiMixed"), vMeson.M(), getAngleDegree(std::atan(dTheta / dPhi))); registry.fill(HIST("mesonQA/hAlphaPtMixed"), (v1.E() - v2.E()) / (v1.E() + v2.E()), vMeson.Pt()); + registry.fill(HIST("mesonQA/hDeltaEtaDeltaPhiMinvPtCentME"), vMeson.M(), vMeson.Pt(), dEta, dPhiWrapped, getCentrality(c1)); } if (mesonConfig.enableTanThetadPhi.value && mesonConfig.minTanThetadPhi > std::fabs(getAngleDegree(std::atan(dTheta / dPhi)))) { registry.fill(HIST("hMesonCutsMixed"), 5); continue; } registry.fill(HIST("hMesonCutsMixed"), 6); + fillPairBkg(v2, v1, vMeson, getCentrality(c1)); // v2 is the PCM photon runFlowAnalysis<2>(c1, vMeson); } } @@ -1540,6 +1467,12 @@ struct TaskPi0FlowEMC { ROOT::Math::PtEtaPhiMVector v2(g2.corrPt(), g2.eta(), g2.phi(), 0.); ROOT::Math::PtEtaPhiMVector vMeson = v1 + v2; + float dPhiWrapped = RecoDecay::constrainAngle(v1.Phi() - v2.Phi(), -o2::constants::math::PI); // wrap to [-π, π) + float dEta = v1.Eta() - v2.Eta(); + if (dPhiWrapped < 0.) { // swap the labels: flip BOTH signs + dPhiWrapped = -dPhiWrapped; + dEta = -dEta; + } float openingAngle = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); registry.fill(HIST("hMesonCuts"), 1); @@ -1558,8 +1491,10 @@ struct TaskPi0FlowEMC { if (mesonConfig.cfgEnableQA.value) { registry.fill(HIST("mesonQA/hInvMassPt"), vMeson.M(), vMeson.Pt()); registry.fill(HIST("mesonQA/hAlphaPt"), (v1.E() - v2.E()) / (v1.E() + v2.E()), vMeson.Pt()); + registry.fill(HIST("mesonQA/hDeltaEtaDeltaPhiMinvPtCentSE"), vMeson.M(), vMeson.Pt(), dEta, dPhiWrapped, getCentrality(collision)); } registry.fill(HIST("hMesonCuts"), 6); + fillPairBkg(v1, v2, vMeson, getCentrality(collision)); runFlowAnalysis<0>(collision, vMeson); } } // end of loop over collisions @@ -1576,7 +1511,7 @@ struct TaskPi0FlowEMC { } using BinningType = ColumnBinningPolicy>; - BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins, mixingConfig.cfgEPBins}, true}; + BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins, mixingConfig.cfgEPBins}}; auto pcmPhotonTuple = std::make_tuple(pcmPhotons); SameKindPair pair{binningMixedEvent, mixingConfig.cfgMixingDepth, -1, collisions, pcmPhotonTuple, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored @@ -1589,10 +1524,6 @@ struct TaskPi0FlowEMC { // general event selection continue; } - if (!(eventcuts.cfgFT0COccupancyMin <= c1.ft0cOccupancyInTimeRange()) || !(c1.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax) || !(eventcuts.cfgFT0COccupancyMin <= c2.ft0cOccupancyInTimeRange()) || !(c2.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { - // occupancy selection - continue; - } if (getCentrality(c1) < eventcuts.cfgMinCent || getCentrality(c1) > eventcuts.cfgMaxCent || getCentrality(c2) < eventcuts.cfgMinCent || getCentrality(c2) > eventcuts.cfgMaxCent) { // event selection continue; @@ -1611,6 +1542,12 @@ struct TaskPi0FlowEMC { ROOT::Math::PtEtaPhiMVector v2(g2.corrPt(), g2.eta(), g2.phi(), 0.); ROOT::Math::PtEtaPhiMVector vMeson = v1 + v2; + float dPhiWrapped = RecoDecay::constrainAngle(v1.Phi() - v2.Phi(), -o2::constants::math::PI); // wrap to [-π, π) + float dEta = v1.Eta() - v2.Eta(); + if (dPhiWrapped < 0.) { // swap the labels: flip BOTH signs + dPhiWrapped = -dPhiWrapped; + dEta = -dEta; + } float openingAngle = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); registry.fill(HIST("hMesonCutsMixed"), 1); @@ -1629,8 +1566,10 @@ struct TaskPi0FlowEMC { if (mesonConfig.cfgEnableQA.value) { registry.fill(HIST("mesonQA/hInvMassPtMixed"), vMeson.M(), vMeson.Pt()); registry.fill(HIST("mesonQA/hAlphaPtMixed"), (v1.E() - v2.E()) / (v1.E() + v2.E()), vMeson.Pt()); + registry.fill(HIST("mesonQA/hDeltaEtaDeltaPhiMinvPtCentME"), vMeson.M(), vMeson.Pt(), dEta, dPhiWrapped, getCentrality(c1)); } registry.fill(HIST("hMesonCutsMixed"), 6); + fillPairBkg(v1, v2, vMeson, getCentrality(c1)); runFlowAnalysis<2>(c1, vMeson); } } diff --git a/PWGEM/PhotonMeson/Tasks/twoPhotonMcTask.cxx b/PWGEM/PhotonMeson/Tasks/twoPhotonMcTask.cxx new file mode 100644 index 00000000000..9f080d9a02d --- /dev/null +++ b/PWGEM/PhotonMeson/Tasks/twoPhotonMcTask.cxx @@ -0,0 +1,1569 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file twoPhotonMcTask.cxx +/// \brief Analysis task to obtain the invariant mass distributions for different MC truth classes +/// \author M. Hemmer, marvin.hemmer@cern.ch + +#include "PWGEM/Dilepton/Utils/MCUtilities.h" +#include "PWGEM/PhotonMeson/Core/EMBitFlags.h" +#include "PWGEM/PhotonMeson/Core/EMCPhotonCut.h" +#include "PWGEM/PhotonMeson/Core/EMPhotonEventCut.h" +#include "PWGEM/PhotonMeson/Core/V0PhotonCut.h" +#include "PWGEM/PhotonMeson/DataModel/EventTables.h" +#include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" +#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" +#include "PWGEM/PhotonMeson/Utils/MCUtilities.h" + +#include "Common/DataModel/Centrality.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; +using namespace o2::aod::pwgem::photon; +using namespace o2::aod::pwgem::photonmeson::utils::mcutil; + +static constexpr int kMaxChainLength = 16; +using Chain = std::array; // filled with -1 after the last ancestor + +/// \brief MC truth of a single photon candidate, ordered from best to worst such that std::max gives the worse flag of a pair +enum class PhotonTruth : uint8_t { + kTruePhoton = 0, // PCM: both legs from the same photon, EMC: leading contributor is a photon (or e+-, converted in front of the EMCal) + kNonPhoton, // PCM: both legs have the same direct mother which is not a photon (K0S, Lambda, pi0 Dalitz), EMC: leading contributor is not a photon + kFake, // PCM: leg without MC label or legs without common direct mother, EMC: cluster without MC label + kNTruth +}; + +/// \brief Class of the closest common ancestor of the two photon candidates of a pair +enum class PairBucket : uint8_t { + kNone = 0, // no common ancestor + kSameParticle, // one particle seen twice: the common ancestor is one of the two candidates themselves (EMC conversion split, Dalitz), + // or a photon/electron whose electromagnetic shower made both candidates (e.g. bremsstrahlung of both conversion legs) + kSignalPi0, // pi0 is the direct mother of both + kSignalEta, // eta is the direct mother of both + kCrossPi0, // pi0 further up in the chain: the pi0 has no hadronic decays, so these are pi0 decays with an EM interaction in between (conversion, bremsstrahlung, shower in front of the EMCal) + kCrossEta, // eta further up in the chain: hadronic decays (eta -> 3 pi0, photons of different pi0) and eta decays with an EM interaction in between + kCrossOmega, // omega + kCrossK0S, // K0S + kCrossEtaPrime, // eta' + kCrossOtherHadron, // any other hadron (including the rare Sigma0) + kPartonString, // quark, gluon, diquark or string/cluster: jet-like + kNBuckets +}; + +/// \brief Origin of a photon candidate for the pi0 prefilter study: first particle above the EM steps (conversion, bremsstrahlung, shower) +enum class PhotonOrigin : uint8_t { + kFromPi0 = 0, // incl. photons of pi0 from eta -> 3 pi0 + kFromEta, + kElse +}; + +/// \brief Class of a photon pair for the pi0 prefilter study +enum class PrefilterPairClass : uint8_t { + kSameParticle = 0, // one particle seen twice (same particle bucket), never counted as pi0/eta pair + kSamePi0, // both from the same pi0: pi0 signal + kTwoPi0, // both from pi0, but from two different pi0 + kSameEta, // both from the same eta: eta signal + kTwoEta, // both from eta, but from two different eta + kPi0Eta, + kPi0Else, + kEtaElse, + kBothElse, + kNClasses +}; + +/// \brief What the leading contributor of the EMC cluster is in a PCM-EMC same particle pair +enum class SameParticleOrigin : uint8_t { + kLegElectron = 0, // the e+- that was reconstructed as one of the V0 legs, should have been removed by track matching + kBremsFromLeg, // a photon radiated by one of the V0 leg e+-, cannot be removed by track matching + kOtherElectron, // any other e+- + kOther, // anything else + kNOrigins +}; + +/// \brief Why the V0 leg did not veto the EMC cluster, from the tracks matched to the cluster +enum class LegMatchStatus : uint8_t { + kNoMatchStored = 0, // no matched track stored for this cluster at all + kNoCompatibleMatch, // matched tracks stored, but none with the momentum of the leg + kOutsideWindow, // leg found, but outside the track matching window of the analysis + kInsideWindowEoverP, // leg found inside the window, but E/p above the cut (primaries only) + kInsideWindow, // leg found inside the window: the cluster should have been vetoed + kNotGlobalTrack, // primaries only: the leg is not a global track, so it is no candidate of the primary matching + kNStatus +}; + +/// \brief Species of the closest common ancestor of an "other hadron" pair +enum class OtherHadronSpecies : uint8_t { + kChargedPion = 0, + kChargedKaon, + kK0L, + kRho, + kKstar, + kPhi, + kA1, // a1(1260) + kExcitedK, // K1(1270), K1(1400), K0*(1430), K2*(1430) + kOpenCharm, // D, D*, Ds, ... (one c quark) + kCharmonium, // J/psi, psi(2S), chi_c, ... + kOpenBeauty, // B, B*, Bs, Bc, ... (one b quark) + kBottomonium, // Upsilon, chi_b, ... + kOtherMeson, // incl. f0, f2, b, h resonances + kProton, + kNeutron, + kDelta, + kLambda, + kSigma, + kXi, + kOtherBaryon, + kOther, + kNSpecies +}; + +/// \brief How the particles directly below the common ancestor were produced, i.e. how the ancestor split into the two branches. +/// Ordered such that std::max of the two branches gives the material interaction if one branch comes from it. +enum class SplitProduction : uint8_t { + kGeneratorDecay = 0, // produced by the event generator: decay in the generator (resonances, Sigma0, ...) + kTransportDecay, // produced by the transport code in a decay (weak decays of K+-, K0L, Lambda, Xi, ...) + kHadronicInteraction, // produced by the transport code in a hadronic interaction in the detector material + kOtherProcess, // produced by the transport code in any other process + kNProduction +}; + +enum CentralityEstimator { + None = 0, + CFT0A = 1, + CFT0C = 2, + CFT0M = 3, + NCentralityEstimators = 4 +}; + +struct TwoPhotonMcTask { + // configurable axis + ConfigurableAxis thnConfigAxisInvMass{"thnConfigAxisInvMass", {400, 0.0, 0.8}, "invariant mass axis for the neutral meson"}; + ConfigurableAxis thnConfigAxisPt{"thnConfigAxisPt", {100, 0., 20.}, "pT axis for the neutral meson"}; + ConfigurableAxis thnConfigAxisERelative{"thnConfigAxisERelative", {600, -1., 5.}, "(E rec - E true) / E true axis"}; + ConfigurableAxis thnConfigAxisPRelative{"thnConfigAxisPRelative", {300, -1., 2.}, "(P rec - P true) / P true axis"}; + ConfigurableAxis thnConfigAxisEtaRelative{"thnConfigAxisEtaRelative", {300, -1., 2.}, "(eta rec - eta true) / eta true axis"}; + ConfigurableAxis thnConfigAxisPhiRelative{"thnConfigAxisPhiRelative", {300, -1., 2.}, "(phi rec - phi true) / phi true axis"}; + ConfigurableAxis thnConfigAxisCent{"thnConfigAxisCent", {10, 0., 100.}, "centrality axis for the current event"}; + ConfigurableAxis thnConfigAxisMult{"thnConfigAxisMult", {60, 0., 60000.}, "multiplicity axis for the current event"}; + Configurable useCent{"useCent", false, "flag to enable usage of centrality instead of multiplicity as axis."}; + + EMPhotonEventCut fEMEventCut; + struct : ConfigurableGroup { + std::string prefix = "eventcuts"; + Configurable cfgZvtxMax{"cfgZvtxMax", 10.f, "max. Zvtx"}; + Configurable cfgRequireSel8{"cfgRequireSel8", true, "require sel8 in event cut"}; + Configurable cfgRequireFT0AND{"cfgRequireFT0AND", true, "require FT0AND in event cut"}; + Configurable cfgRequireNoTFB{"cfgRequireNoTFB", false, "require No time frame border in event cut"}; + Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", false, "require no ITS readout frame border in event cut"}; + Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", false, "require no same bunch pileup in event cut"}; + Configurable cfgRequireVertexITSTPC{"cfgRequireVertexITSTPC", false, "require Vertex ITSTPC in event cut"}; // ITS-TPC matched track contributes PV. + Configurable cfgRequireGoodZvtxFT0vsPV{"cfgRequireGoodZvtxFT0vsPV", false, "require good Zvtx between FT0 vs. PV in event cut"}; + Configurable cfgRequireEMCReadoutInMB{"cfgRequireEMCReadoutInMB", true, "require the EMC to be read out in an MB collision (kTVXinEMC)"}; + Configurable cfgRequireEMCHardwareTriggered{"cfgRequireEMCHardwareTriggered", false, "require the EMC to be hardware triggered (kEMC7 or kDMC7)"}; + Configurable cfgFT0COccupancyMin{"cfgFT0COccupancyMin", -1, "min. FT0C occupancy"}; + Configurable cfgFT0COccupancyMax{"cfgFT0COccupancyMax", 1000000000, "max. FT0C occupancy"}; + Configurable cfgMinCent{"cfgMinCent", 0, "min. centrality (%)"}; + Configurable cfgMaxCent{"cfgMaxCent", 90, "max. centrality (%)"}; + Configurable centEstimator{"centEstimator", 2, "Centrality estimation (FT0A: 1, FT0C: 2, FT0M: 3)"}; + Configurable onlyKeepWeightedEvents{"onlyKeepWeightedEvents", false, "flag to keep only weighted events (for JJ MCs) and remove all MB events (with weight = 1)"}; + } eventcuts; + + EMCPhotonCut fEMCCut; + struct : ConfigurableGroup { + std::string prefix = "emccuts"; + Configurable clusterDefinition{"clusterDefinition", "kV3MostSplitSmallestTimeDiff", "Clusterizer to be selected, e.g. V3Default"}; + Configurable cfgEMCminTime{"cfgEMCminTime", -25., "Minimum cluster time for EMCal time cut"}; + Configurable cfgEMCmaxTime{"cfgEMCmaxTime", +30., "Maximum cluster time for EMCal time cut"}; + Configurable cfgEMCminM02{"cfgEMCminM02", 0.1, "Minimum M02 for EMCal M02 cut"}; + Configurable cfgEMCmaxM02{"cfgEMCmaxM02", 0.7, "Maximum M02 for EMCal M02 cut"}; + Configurable cfgEMCminE{"cfgEMCminE", 0.7, "Minimum cluster energy for EMCal energy cut"}; + Configurable cfgEMCminNCell{"cfgEMCminNCell", 1, "Minimum number of cells per cluster for EMCal NCell cut"}; + Configurable> cfgEMCTMEta{"cfgEMCTMEta", {0.01f, 4.07f, -2.5f}, "|eta| <= [0]+(pT+[1])^[2] for EMCal track matching"}; + Configurable> cfgEMCTMPhi{"cfgEMCTMPhi", {0.015f, 3.65f, -2.f}, "|phi| <= [0]+(pT+[1])^[2] for EMCal track matching"}; + Configurable> emcSecTMEta{"emcSecTMEta", {0.01f, 4.07f, -2.5f}, "|eta| <= [0]+(pT+[1])^[2] for EMCal track matching"}; + Configurable> emcSecTMPhi{"emcSecTMPhi", {0.015f, 3.65f, -2.f}, "|phi| <= [0]+(pT+[1])^[2] for EMCal track matching"}; + Configurable cfgEMCEoverp{"cfgEMCEoverp", 1.75, "Minimum cluster energy over track momentum for EMCal track matching"}; + Configurable cfgEMCUseExoticCut{"cfgEMCUseExoticCut", true, "FLag to use the EMCal exotic cluster cut"}; + Configurable cfgEMCUseTM{"cfgEMCUseTM", false, "flag to use EMCal track matching cut or not"}; + Configurable emcUseSecondaryTM{"emcUseSecondaryTM", false, "flag to use EMCal secondary track matching cut or not"}; + Configurable cfgEnableQA{"cfgEnableQA", false, "flag to turn QA plots on/off"}; + } emccuts; + + V0PhotonCut fV0PhotonCut; + struct : o2::framework::ConfigurableGroup { + std::string prefix = "PCMcuts"; + o2::framework::Configurable requireV0WithITSTPC{"requireV0WithITSTPC", false, "flag to enforce V0s have ITS and TPC"}; + o2::framework::Configurable requireV0WithITSOnly{"requireV0WithITSOnly", false, "flag to select V0s with ITSonly tracks"}; + o2::framework::Configurable requireV0WithTPCOnly{"requireV0WithTPCOnly", false, "flag to select V0s with TPConly tracks"}; + o2::framework::Configurable minPtV0{"minPtV0", 0.1, "min pT for v0 photons at PV"}; + o2::framework::Configurable maxPtV0{"maxPtV0", 1e+10, "max pT for v0 photons at PV"}; + o2::framework::Configurable minEtaV0{"minEtaV0", -0.8, "min eta for v0 photons at PV"}; + o2::framework::Configurable maxEtaV0{"maxEtaV0", 0.8, "max eta for v0 photons at PV"}; + o2::framework::Configurable minRV0{"minRV0", 4.0, "min v0 radius"}; + o2::framework::Configurable maxRV0{"maxRV0", 90.0, "max v0 radius"}; + o2::framework::Configurable maxAlphaAP{"maxAlphaAP", 0.95, "max alpha for AP cut"}; + o2::framework::Configurable maxQtAP{"maxQtAP", 0.01, "max qT for AP cut"}; + o2::framework::Configurable minCosPA{"minCosPA", 0.999, "min V0 CosPA"}; + o2::framework::Configurable maxPCA{"maxPCA", 1.5, "max distance btween 2 legs"}; + o2::framework::Configurable maxChi2KF{"maxChi2KF", 1.e+10f, "max chi2/ndf with KF"}; + o2::framework::Configurable rejectV0onITSib{"rejectV0onITSib", true, "flag to reject V0s on ITSib"}; + o2::framework::Configurable applyPrefilter{"applyPrefilter", false, "flag to apply prefilter to V0"}; + + o2::framework::Configurable minNClusterTPC{"minNClusterTPC", 0, "min NCluster TPC"}; + o2::framework::Configurable minNCrossedRowsTPC{"minNCrossedRowsTPC", 40, "min ncrossed rows in TPC"}; + o2::framework::Configurable minNCrossedRowsOverFindableClustersTPC{"minNCrossedRowsOverFindableClustersTPC", 0.8f, "min fraction of crossed rows over findable clusters in TPC"}; + o2::framework::Configurable maxFracSharedClustersTPC{"maxFracSharedClustersTPC", 999.f, "max fraction of shared clusters in TPC"}; + o2::framework::Configurable minNClusterITS{"minNClusterITS", 0, "min NCluster ITS"}; + o2::framework::Configurable minMeanClusterSizeITSob{"minMeanClusterSizeITSob", 0.f, "min ITSob"}; + o2::framework::Configurable maxMeanClusterSizeITSob{"maxMeanClusterSizeITSob", 16.f, "max ITSob"}; + o2::framework::Configurable macChi2TPC{"macChi2TPC", 4.f, "max chi2/NclsTPC"}; + o2::framework::Configurable macChi2ITS{"macChi2ITS", 36.f, "max chi2/NclsITS"}; + o2::framework::Configurable minTPCNSigmaEl{"minTPCNSigmaEl", -3.0f, "min. TPC n sigma for electron"}; + o2::framework::Configurable maxTPCNSigmaEl{"maxTPCNSigmaEl", +3.0f, "max. TPC n sigma for electron"}; + o2::framework::Configurable disableITSOnly{"disableITSOnly", false, "flag to disable ITSonly tracks"}; + o2::framework::Configurable disableTPCOnly{"disableTPCOnly", false, "flag to disable TPConly tracks"}; + o2::framework::Configurable doQA{"doQA", false, "flag to set QA flag."}; + } pcmcuts; + + struct : ConfigurableGroup { + std::string prefix = "mesonConfig"; + Configurable minOpenAngle{"minOpenAngle", 0.0202, "apply min opening angle. Default value one EMCal cell"}; + Configurable cfgEnableQA{"cfgEnableQA", false, "flag to turn QA plots on/off"}; + Configurable prefilterMassMin{"prefilterMassMin", 0.105f, "pi0 prefilter study: lower edge of the pi0 mass window (GeV/c2)"}; + Configurable prefilterMassMax{"prefilterMassMax", 0.165f, "pi0 prefilter study: upper edge of the pi0 mass window (GeV/c2)"}; + Configurable prefilterAllPairTypes{"prefilterAllPairTypes", false, "pi0 prefilter study, processPcmEmcal: also tag with PCM-PCM and EMC-EMC pairs and fill hPrefilterAll (for PCM-PCM / EMC-EMC use processPcmPrefilterAll / processEmcalPrefilterAll)"}; + ConfigurableAxis thnConfigAxisPrefilterMass{"thnConfigAxisPrefilterMass", {200, 0., 0.8}, "pi0 prefilter study: invariant mass axis"}; + ConfigurableAxis thnConfigAxisPrefilterPt{"thnConfigAxisPrefilterPt", {VARIABLE_WIDTH, 0., 1., 1.5, 2., 2.5, 3., 4., 5., 6., 8., 10., 12., 15., 20.}, "pi0 prefilter study: pT axis"}; + } mesonConfig; + + // same mixing as taskPi0FlowEMC, but without event plane bins: the derived MC has no Q vectors + struct : ConfigurableGroup { + std::string prefix = "mixingConfig"; + ConfigurableAxis cfgVtxBins{"cfgVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; + ConfigurableAxis cfgCentBins{"cfgCentBins", {VARIABLE_WIDTH, 0.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.f}, "Mixing bins - centrality"}; + Configurable cfgMixingDepth{"cfgMixingDepth", 2, "Mixing depth"}; + Configurable cfgCorrectMixedVtxEta{"cfgCorrectMixedVtxEta", true, "EMC-EMC: re-project the eta of cluster 2 onto the vertex of collision 1 in the mixed event"}; + Configurable cfgEmcalEffRadius{"cfgEmcalEffRadius", 430.f, "effective EMCal radius (cm) used for the mixed event vertex correction"}; + } mixingConfig; + + using BinningType = ColumnBinningPolicy; + + SliceCache cache; + + using EMCalPhotons = soa::Join; + using PcmPhotons = soa::Join; + + using PcmMcLegs = soa::Join; + + using Colls = soa::Join; + + using McColls = o2::soa::Join; + using McParticles = EMMCParticles; + + PresliceOptional perCollisionEMC = o2::aod::emccluster::pmeventId; + PresliceOptional perCollisionPCM = aod::v0photonkf::pmeventId; + PresliceOptional perEMCClusterMT = o2::aod::mintm::minClusterId; + PresliceOptional perEMCClusterMS = o2::aod::mintm::minClusterId; + + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; + + int mRunNumber{-1}; + float dBz{0.f}; + + static constexpr int PhotonPCM = 0; + static constexpr int PhotonEMC = 1; + static constexpr int PairPCMPCM = 0; + static constexpr int PairEMCEMC = 1; + static constexpr int PairPCMEMC = 2; + + // ancestor chains and truth flags, filled once per DF, index = globalIndex of the V0 photon / cluster + std::vector mChainsPCM; + std::vector mChainsEMC; + std::vector mTruthPCM; + std::vector mTruthEMC; + // pi0 prefilter study: photon forms a pair inside the pi0 mass window with another photon of its event, index = globalIndex + std::vector mPrefilterPCM; + std::vector mPrefilterEMC; + // the same, tagged with all three pair types (PCM-PCM, EMC-EMC, PCM-EMC) + std::vector mPrefilterAllPCM; + std::vector mPrefilterAllEMC; + + void defineEMEventCut() + { + fEMEventCut = EMPhotonEventCut("fEMEventCut", "fEMEventCut"); + fEMEventCut.SetRequireSel8(eventcuts.cfgRequireSel8); + fEMEventCut.SetRequireFT0AND(eventcuts.cfgRequireFT0AND); + fEMEventCut.SetZvtxRange(-eventcuts.cfgZvtxMax, +eventcuts.cfgZvtxMax); + fEMEventCut.SetRequireNoTFB(eventcuts.cfgRequireNoTFB); + fEMEventCut.SetRequireNoITSROFB(eventcuts.cfgRequireNoITSROFB); + fEMEventCut.SetRequireNoSameBunchPileup(eventcuts.cfgRequireNoSameBunchPileup); + fEMEventCut.SetRequireVertexITSTPC(eventcuts.cfgRequireVertexITSTPC); + fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); + fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); + fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + fEMEventCut.SetFT0COccupancyRange(eventcuts.cfgFT0COccupancyMin, eventcuts.cfgFT0COccupancyMax); + } + + void defineEMCCut() + { + fEMCCut = EMCPhotonCut("fEMCCut", "fEMCCut"); + + fEMCCut.SetTrackMatchingEtaParams(emccuts.cfgEMCTMEta->at(0), emccuts.cfgEMCTMEta->at(1), emccuts.cfgEMCTMEta->at(2)); + fEMCCut.SetTrackMatchingPhiParams(emccuts.cfgEMCTMPhi->at(0), emccuts.cfgEMCTMPhi->at(1), emccuts.cfgEMCTMPhi->at(2)); + + fEMCCut.SetSecTrackMatchingEtaParams(emccuts.emcSecTMEta->at(0), emccuts.emcSecTMEta->at(1), emccuts.emcSecTMEta->at(2)); + fEMCCut.SetSecTrackMatchingPhiParams(emccuts.emcSecTMPhi->at(0), emccuts.emcSecTMPhi->at(1), emccuts.emcSecTMPhi->at(2)); + fEMCCut.SetMinEoverP(emccuts.cfgEMCEoverp); + + fEMCCut.SetMinE(emccuts.cfgEMCminE); + fEMCCut.SetMinNCell(emccuts.cfgEMCminNCell); + fEMCCut.SetM02Range(emccuts.cfgEMCminM02, emccuts.cfgEMCmaxM02); + fEMCCut.SetTimeRange(emccuts.cfgEMCminTime, emccuts.cfgEMCmaxTime); + fEMCCut.SetUseExoticCut(emccuts.cfgEMCUseExoticCut); + fEMCCut.SetClusterizer(emccuts.clusterDefinition); + fEMCCut.SetUseTM(emccuts.cfgEMCUseTM.value); // disables or enables TM + fEMCCut.SetUseSecondaryTM(emccuts.emcUseSecondaryTM.value); // disables or enables secondary TM + fEMCCut.SetDoQA(emccuts.cfgEnableQA.value); + } + + void definePCMCut() + { + fV0PhotonCut = V0PhotonCut("fV0PhotonCut", "fV0PhotonCut"); + + // for v0 + fV0PhotonCut.SetV0PtRange(pcmcuts.minPtV0, pcmcuts.maxPtV0); + fV0PhotonCut.SetV0EtaRange(pcmcuts.minEtaV0, pcmcuts.maxEtaV0); + fV0PhotonCut.SetMinCosPA(pcmcuts.minCosPA); + fV0PhotonCut.SetMaxPCA(pcmcuts.maxPCA); + fV0PhotonCut.SetMaxChi2KF(pcmcuts.maxChi2KF); + fV0PhotonCut.SetRxyRange(pcmcuts.minRV0, pcmcuts.maxRV0); + fV0PhotonCut.SetAPRange(pcmcuts.maxAlphaAP, pcmcuts.maxQtAP); + fV0PhotonCut.RejectITSib(pcmcuts.rejectV0onITSib); + + // for track + fV0PhotonCut.SetMinNClustersTPC(pcmcuts.minNClusterTPC); + fV0PhotonCut.SetMinNCrossedRowsTPC(pcmcuts.minNCrossedRowsTPC); + fV0PhotonCut.SetMinNCrossedRowsOverFindableClustersTPC(pcmcuts.minNCrossedRowsOverFindableClustersTPC); + fV0PhotonCut.SetMaxFracSharedClustersTPC(pcmcuts.maxFracSharedClustersTPC); + fV0PhotonCut.SetChi2PerClusterTPC(0.f, pcmcuts.macChi2TPC); + fV0PhotonCut.SetTPCNsigmaElRange(pcmcuts.minTPCNSigmaEl, pcmcuts.maxTPCNSigmaEl); + fV0PhotonCut.SetChi2PerClusterITS(0.f, pcmcuts.macChi2ITS); + fV0PhotonCut.SetNClustersITS(pcmcuts.minNClusterITS, 7); + fV0PhotonCut.SetMeanClusterSizeITSob(pcmcuts.minMeanClusterSizeITSob, pcmcuts.maxMeanClusterSizeITSob); + fV0PhotonCut.SetDisableITSonly(pcmcuts.disableITSOnly); + fV0PhotonCut.SetDisableTPConly(pcmcuts.disableTPCOnly); + fV0PhotonCut.SetRequireITSTPC(pcmcuts.requireV0WithITSTPC); + fV0PhotonCut.SetRequireITSonly(pcmcuts.requireV0WithITSOnly); + fV0PhotonCut.SetRequireTPConly(pcmcuts.requireV0WithTPCOnly); + + fV0PhotonCut.setDoQA(pcmcuts.doQA.value); + } + + void init(InitContext&) + { + if ((doprocessPcm.value && doprocessPcmPrefilterAll.value) || (doprocessEmcal.value && doprocessEmcalPrefilterAll.value)) { + LOG(fatal) << "processPcm / processEmcal and their PrefilterAll variants fill the same histograms, enable only one of each"; + } + mRunNumber = 0; + dBz = 0; + + defineEMEventCut(); + defineEMCCut(); + fEMCCut.addQAHistograms(®istry); + definePCMCut(); + fV0PhotonCut.addQAHistograms(®istry); + fEMEventCut.addEventHistograms(®istry); + + const AxisSpec thnAxisPtGen{thnConfigAxisPt, "#it{p}_{T,Gen} (GeV/#it{c})"}; + const AxisSpec thnAxisPtRec{thnConfigAxisPt, "#it{p}_{T,Rec} (GeV/#it{c})"}; + const AxisSpec thnAxisPGen{thnConfigAxisPt, "#it{p}_{Gen} (GeV/#it{c})"}; + const AxisSpec thnAxisPRec{thnConfigAxisPt, "#it{p}_{Rec} (GeV/#it{c})"}; + const AxisSpec thnAxisPRelative{thnConfigAxisPRelative, "#it{p}_{Rec} - #it{p}_{Gen} / #it{p}_{Gen}"}; + const AxisSpec thnAxisEGen{thnConfigAxisPt, "#it{E}_{Gen} (GeV)"}; + const AxisSpec thnAxisERec{thnConfigAxisPt, "#it{E}_{Rec} (GeV)"}; + const AxisSpec thnAxisERelative{thnConfigAxisERelative, "#it{E}_{Rec} - #it{E}_{Gen} / #it{E}_{Gen}"}; + const AxisSpec thnAxisInvMass{thnConfigAxisInvMass, "#it{M}_{#gamma#gamma} (GeV/#it{c}^{2})"}; + + const AxisSpec thnAxisEtaRelative{thnConfigAxisEtaRelative, "#it{#eta}_{Rec} - #it{#eta}_{Gen} / #it{#eta}_{Gen}"}; + const AxisSpec thnAxisPhiRelative{thnConfigAxisPhiRelative, "#it{#varphi}_{Rec} - #it{#varphi}_{Gen} / #it{#varphi}_{Gen}"}; + + const AxisSpec thnAxisEtaGen{280, -0.7, 0.7, "#it{#eta}_{Gen}"}; + const AxisSpec thnAxisPhiGen{360, 0., o2::constants::math::TwoPI, "#it{#varphi}_{Gen} (rad)"}; + + const AxisSpec thnAxisPairTruth{static_cast(PhotonTruth::kNTruth), -0.5, static_cast(PhotonTruth::kNTruth) - 0.5, "pair truth (worse photon)"}; + const AxisSpec thnAxisPairBucket{static_cast(PairBucket::kNBuckets), -0.5, static_cast(PairBucket::kNBuckets) - 0.5, "closest common ancestor"}; + + AxisSpec thnAxisCentOrMult{1, 0., 1., "Centrality/Multiplicity"}; // placeholder, overwritten in init + if (useCent.value) { + // PbPb: use centrality + thnAxisCentOrMult = {thnConfigAxisCent, "Centrality (%)"}; + } else { + // pp: use multiplicity + thnAxisCentOrMult = {thnConfigAxisMult, "FT0C Multiplicity"}; + } + + auto hPairAncestor = registry.add("PCM/hPairAncestor", "minv vs pT vs cent vs pair truth vs closest common ancestor", HistType::kTHnF, {thnAxisInvMass, thnAxisPtRec, thnAxisCentOrMult, thnAxisPairTruth, thnAxisPairBucket}); + static constexpr std::array(PhotonTruth::kNTruth)> TruthLabels = {"true #gamma", "non #gamma", "fake"}; + for (std::size_t i = 0; i < TruthLabels.size(); ++i) { + hPairAncestor->GetAxis(3)->SetBinLabel(static_cast(i) + 1, TruthLabels[i].data()); + } + static constexpr std::array(PairBucket::kNBuckets)> BucketLabels = {"none", "same particle", "#pi^{0} direct", "#eta direct", "#pi^{0} cross", "#eta cross", "#omega", "K^{0}_{S}", "#eta'", "other hadron", "parton/string"}; + for (std::size_t i = 0; i < BucketLabels.size(); ++i) { + hPairAncestor->GetAxis(4)->SetBinLabel(static_cast(i) + 1, BucketLabels[i].data()); + } + // "other hadron" pairs: which hadron is the common ancestor, and did it split in a decay or a material interaction + const AxisSpec axisOHMass{160, 0., 0.8, "#it{M}_{#gamma#gamma} (GeV/#it{c}^{2})"}; + const AxisSpec axisOHPt{20, 0., 20., "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisOHSpecies{static_cast(OtherHadronSpecies::kNSpecies), -0.5, static_cast(OtherHadronSpecies::kNSpecies) - 0.5, "common ancestor"}; + const AxisSpec axisOHProduction{static_cast(SplitProduction::kNProduction), -0.5, static_cast(SplitProduction::kNProduction) - 0.5, "split of the common ancestor"}; + auto hOtherHadron = registry.add("PCM/OtherHadron/hAncestor", "other hadron pairs: minv vs pT vs ancestor species vs split production vs pair truth", HistType::kTHnF, {axisOHMass, axisOHPt, axisOHSpecies, axisOHProduction, thnAxisPairTruth}); + static constexpr std::array(OtherHadronSpecies::kNSpecies)> SpeciesLabels = {"#pi^{#pm}", "K^{#pm}", "K^{0}_{L}", "#rho", "K^{*}", "#phi", "a_{1}", "K_{1}, K^{*}_{0,2}", "open charm", "c#bar{c}", "open beauty", "b#bar{b}", "other meson", "p", "n", "#Delta", "#Lambda", "#Sigma", "#Xi", "other baryon", "other"}; + for (std::size_t i = 0; i < SpeciesLabels.size(); ++i) { + hOtherHadron->GetAxis(2)->SetBinLabel(static_cast(i) + 1, SpeciesLabels[i].data()); + } + static constexpr std::array(SplitProduction::kNProduction)> ProductionLabels = {"generator decay", "transport decay", "hadronic interaction", "other process"}; + for (std::size_t i = 0; i < ProductionLabels.size(); ++i) { + hOtherHadron->GetAxis(3)->SetBinLabel(static_cast(i) + 1, ProductionLabels[i].data()); + } + for (std::size_t i = 0; i < TruthLabels.size(); ++i) { + hOtherHadron->GetAxis(4)->SetBinLabel(static_cast(i) + 1, TruthLabels[i].data()); + } + registry.add("PCM/OtherHadron/hAncestorPdg", "other hadron pairs: |PDG| of the common ancestor;|PDG| (last bin: #geq 9999);pairs", HistType::kTH1D, {{10000, -0.5, 9999.5}}); + + // pi0 prefilter study for the eta: photon origins of the pair vs number of photons tagged by a pi0 mass window prefilter + const AxisSpec axisPrefilterMass{mesonConfig.thnConfigAxisPrefilterMass, "#it{M}_{#gamma#gamma} (GeV/#it{c}^{2})"}; + const AxisSpec axisPrefilterPt{mesonConfig.thnConfigAxisPrefilterPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisPrefilterClass{static_cast(PrefilterPairClass::kNClasses), -0.5, static_cast(PrefilterPairClass::kNClasses) - 0.5, "photon origins"}; + const AxisSpec axisPrefilterTagged{3, -0.5, 2.5, "photons tagged by the #pi^{0} prefilter"}; + auto hPrefilter = registry.add("PCM/hPrefilter", "minv vs pT vs cent vs photon origins vs photons tagged by the pi0 prefilter", HistType::kTHnF, {axisPrefilterMass, axisPrefilterPt, thnAxisCentOrMult, axisPrefilterClass, axisPrefilterTagged}); + static constexpr std::array(PrefilterPairClass::kNClasses)> PrefilterClassLabels = {"same particle", "same #pi^{0}", "two #pi^{0}", "same #eta", "two #eta", "#pi^{0} + #eta", "#pi^{0} + else", "#eta + else", "both else"}; + for (std::size_t i = 0; i < PrefilterClassLabels.size(); ++i) { + hPrefilter->GetAxis(3)->SetBinLabel(static_cast(i) + 1, PrefilterClassLabels[i].data()); + } + static constexpr std::array TaggedLabels = {"0", "1", "2"}; + for (std::size_t i = 0; i < TaggedLabels.size(); ++i) { + hPrefilter->GetAxis(4)->SetBinLabel(static_cast(i) + 1, TaggedLabels[i].data()); + } + // same, but the photons are tagged with all three pair types, only booked if a process function fills it + if (doprocessPcmPrefilterAll.value || doprocessEmcalPrefilterAll.value || (doprocessPcmEmcal.value && mesonConfig.prefilterAllPairTypes.value)) { + registry.addClone("PCM/hPrefilter", "PCM/hPrefilterAll"); + } + + // mixed event, same axes as hPairAncestor without the bucket (pairs from different events share no ancestor) + auto hMixed = registry.add("PCM/hMixed", "minv vs pT vs cent vs pair truth, mixed event", HistType::kTHnF, {thnAxisInvMass, thnAxisPtRec, thnAxisCentOrMult, thnAxisPairTruth}); + for (std::size_t i = 0; i < TruthLabels.size(); ++i) { + hMixed->GetAxis(3)->SetBinLabel(static_cast(i) + 1, TruthLabels[i].data()); + } + registry.addClone("PCM/", "EMC/"); + registry.addClone("PCM/", "PCMEMC/"); + + // ancestor chain QA + registry.add("QA/PCM/hChainLength", "length of the ancestor chain;chain length;counts", HistType::kTH1D, {{kMaxChainLength, 0.5, kMaxChainLength + 0.5}}); + auto hChainEnd = registry.add("QA/PCM/hChainEnd", "reason the ancestor chain ended;;counts", HistType::kTH1D, {{4, -0.5, 3.5}}); + hChainEnd->GetXaxis()->SetBinLabel(1, "parton-like"); + hChainEnd->GetXaxis()->SetBinLabel(2, "beam nucleus"); + hChainEnd->GetXaxis()->SetBinLabel(3, "no mothers"); + hChainEnd->GetXaxis()->SetBinLabel(4, "truncated"); + auto hChainEndKind = registry.add("QA/PCM/hChainEndKind", "kind of the last chain element;;counts", HistType::kTH1D, {{static_cast(AncestorKind::NClasses), -0.5, static_cast(AncestorKind::NClasses) - 0.5}}); + static constexpr std::array(AncestorKind::NClasses)> KindLabels = {"none", "string/parton", "beam/nucleus", "#pi^{0}", "#eta", "other meson", "baryon", "#gamma", "e^{#pm}", "other"}; + for (std::size_t i = 0; i < KindLabels.size(); ++i) { + hChainEndKind->GetXaxis()->SetBinLabel(static_cast(i) + 1, KindLabels[i].data()); + } + registry.add("QA/PCM/hChainEndPdg", "|PDG| of the last chain element;|PDG| (last bin: #geq 9999, incl. nuclei);counts", HistType::kTH1D, {{10000, -0.5, 9999.5}}); + registry.addClone("QA/PCM/", "QA/EMC/"); + + // origin of PCM-EMC same particle pairs and why track matching did not remove them + if (doprocessPcmEmcal.value) { + const AxisSpec axisOrigin{static_cast(SameParticleOrigin::kNOrigins), -0.5, static_cast(SameParticleOrigin::kNOrigins) - 0.5, "leading contributor of the cluster"}; + const AxisSpec axisStatus{static_cast(LegMatchStatus::kNStatus), -0.5, static_cast(LegMatchStatus::kNStatus) - 0.5, "V0 leg among the matched tracks"}; + const AxisSpec axisPtLeg{40, 0., 20., "#it{p}_{T,leg} (GeV/#it{c})"}; + const AxisSpec axisDeltaEta{100, -0.1, 0.1, "#Delta#eta (cluster - track)"}; + const AxisSpec axisDeltaPhi{100, -0.1, 0.1, "#Delta#varphi (cluster - track)"}; + const AxisSpec axisDp{200, -1., 1., "(#it{p}_{match} - #it{p}_{leg}) / #it{p}_{leg}"}; + static constexpr std::array(SameParticleOrigin::kNOrigins)> OriginLabels = {"leg e^{#pm}", "brems #gamma of leg", "other e^{#pm}", "other"}; + static constexpr std::array(LegMatchStatus::kNStatus)> StatusLabels = {"no match stored", "no match with leg #it{p}", "outside window", "in window, #it{E}/#it{p} #geq cut", "in window", "not a global track"}; + + auto hOrigin = registry.add("PCMEMC/SameParticle/hOrigin", "same particle pairs: leading contributor of the cluster", HistType::kTH3F, {thnAxisInvMass, thnAxisPtRec, axisOrigin}); + for (std::size_t i = 0; i < OriginLabels.size(); ++i) { + hOrigin->GetZaxis()->SetBinLabel(static_cast(i) + 1, OriginLabels[i].data()); + } + // labels make the axis extendable, and TH1Merger::LabelMerge in ROOT 6.36 reads the z labels from the y axis: + // merged TH3 content would end up in a new, unlabelled z bin. With a fixed axis the merge goes bin by bin. + hOrigin->GetZaxis()->SetCanExtend(false); + registry.add("PCMEMC/SameParticle/hLegEoverP", "cluster from a V0 leg: #it{E}_{cls}/#it{p}_{leg}", HistType::kTH2F, {{200, 0., 4., "#it{E}_{cls}/#it{p}_{leg}"}, axisPtLeg}); + for (const auto& [type, title] : {std::pair{"Prim", "primary"}, {"Sec", "secondary"}}) { + auto hStatus = registry.add(std::format("PCMEMC/SameParticle/hLeg{}Status", type.data()).data(), std::format("cluster from a V0 leg: leg among the matched {} tracks", title.data()).data(), HistType::kTH2F, {axisStatus, axisPtLeg}); + for (std::size_t i = 0; i < StatusLabels.size(); ++i) { + hStatus->GetXaxis()->SetBinLabel(static_cast(i) + 1, StatusLabels[i].data()); + } + registry.add(std::format("PCMEMC/SameParticle/hLeg{}Dp", type.data()).data(), std::format("cluster from a V0 leg: closest {} match in #it{{p}}", title.data()).data(), HistType::kTH1F, {axisDp}); + registry.add(std::format("PCMEMC/SameParticle/hLeg{}Residual", type.data()).data(), std::format("cluster from a V0 leg: residual of the {} match", title.data()).data(), HistType::kTH3F, {axisDeltaEta, axisDeltaPhi, axisPtLeg}); + } + } + + auto hMesonCuts = registry.add("hMesonCuts", "hMesonCuts;;Counts", kTH1D, {{6, 0.5, 6.5}}, false); + hMesonCuts->GetXaxis()->SetBinLabel(1, "in"); + hMesonCuts->GetXaxis()->SetBinLabel(2, "opening angle"); + hMesonCuts->GetXaxis()->SetBinLabel(3, "#it{M}_{#gamma#gamma}"); + hMesonCuts->GetXaxis()->SetBinLabel(4, "#it{p}_{T}"); + hMesonCuts->GetXaxis()->SetBinLabel(5, "conversion cut"); + hMesonCuts->GetXaxis()->SetBinLabel(6, "out"); + if (mesonConfig.cfgEnableQA.value) { + registry.add("mesonQA/hInvMassPt", "Histo for inv pair mass vs pt", HistType::kTH2D, {thnAxisInvMass, thnAxisPtRec}); + } + }; // end init + + template + void initCCDB(TCollision const& collision) + { + if (mRunNumber == collision.runNumber()) { + return; + } + mRunNumber = collision.runNumber(); + + dBz = collision.grpMagField().getNominalL3Field(); + LOG(info) << "Retrieved GRP for timestamp " << collision.timestamp() << " with magnetic field of " << dBz << " kZG"; + fV0PhotonCut.SetD_Bz(dBz); + } + + template + float getCentralityOrMultiplicity(TCollision const& collision) + { + if (useCent.value) { + return getCentrality(collision); + } + // pp: use raw FT0C multiplicity + return collision.multFT0C(); + } + + /// Get the centrality + /// \param collision is the collision with the centrality information + template + float getCentrality(TCollision const& collision) + { + float cent = -999.; + switch (eventcuts.centEstimator) { + case CentralityEstimator::CFT0M: + cent = collision.centFT0M(); + break; + case CentralityEstimator::CFT0A: + cent = collision.centFT0A(); + break; + case CentralityEstimator::CFT0C: + cent = collision.centFT0C(); + break; + default: + LOG(warning) << "Centrality estimator not valid. Possible values are T0M, T0A, T0C. Fallback to T0C"; + cent = collision.centFT0C(); + break; + } + return cent; + } + + /// \brief check if standard event cuts + FT0 occupancy + centrality + QVec good is + /// \param collision collision that will be checked + /// \return true if collision survives all checks, otherwise false + template + bool isFullEventSelected(TCollision const& collision, bool fillHisto = false) + { + if (fillHisto) { + fEMEventCut.fillEventInfo<0>(®istry, collision); + } + if (!(fEMEventCut.IsSelected(collision))) { + // general event selection + return false; + } + float centOrMult = getCentralityOrMultiplicity(collision); + if (useCent && (centOrMult < eventcuts.cfgMinCent || centOrMult > eventcuts.cfgMaxCent)) { + // event selection + return false; + } + if (fillHisto) { + fEMEventCut.fillEventInfo<1>(®istry, collision); + fEMEventCut.fillAccepted(®istry); + } + return true; + } + + /// \brief Reset the per DF cache of one photon type + /// \param chains ancestor chains, set to -1 (not {}, which gives 0, a valid index) + /// \param truth truth flags, set to kFake + /// \param size size of the full photon table + static void resetCache(std::vector& chains, std::vector& truth, const std::size_t size) + { + Chain empty; + empty.fill(-1); + chains.assign(size, empty); + truth.assign(size, PhotonTruth::kFake); + } + + /// \brief Fill the ancestor chain QA + /// \param mcIter MC particle iterator, has to sit where getAncestorChain left it (last chain element or the nucleus that ended the chain) + /// \param length length of the chain as returned by getAncestorChain + template + void fillChainQA(TMCIter const& mcIter, const int length) + { + static constexpr std::array HistLength = {"QA/PCM/hChainLength", "QA/EMC/hChainLength"}; + static constexpr std::array HistEnd = {"QA/PCM/hChainEnd", "QA/EMC/hChainEnd"}; + static constexpr std::array HistEndKind = {"QA/PCM/hChainEndKind", "QA/EMC/hChainEndKind"}; + static constexpr std::array HistEndPdg = {"QA/PCM/hChainEndPdg", "QA/EMC/hChainEndPdg"}; + if (length <= 0) { + return; + } + static constexpr int MaxPdgBin = 9999; // nuclei and other large codes go to the last bin + const int lastPdg = mcIter.pdgCode(); + int endReason = 3; // chain is full without reaching a parton, a nucleus or a particle without mothers + if (isPartonLike(lastPdg)) { + endReason = 0; + } else if (isNucleus(lastPdg)) { + endReason = 1; + } else if (!mcIter.has_mothers()) { + endReason = 2; + } + registry.fill(HIST(HistLength[photonType]), length); + registry.fill(HIST(HistEnd[photonType]), endReason); + registry.fill(HIST(HistEndKind[photonType]), static_cast(ancestorKindOfPdg(lastPdg))); + registry.fill(HIST(HistEndPdg[photonType]), std::min(std::abs(lastPdg), MaxPdgBin)); + } + + /// \brief Fill ancestor chains and truth flags of all EMC clusters of the DF + /// \param clusters full (unfiltered) cluster table + /// \param mcIter iterator of the full MC particle table, WILL BE CHANGED + /// \param doChainQA fill the ancestor chain QA (off for the mixed event, so chains are not counted twice) + template + void fillEmcCache(TClusters const& clusters, TMCIter& mcIter, const bool doChainQA = true) + { + for (const auto& cluster : clusters) { + const auto mcIds = cluster.emmcparticleIds(); + if (mcIds.empty() || mcIds[0] < 0) { + continue; // noise cluster without MC label: kFake, chain stays -1 + } + auto& chain = mChainsEMC[cluster.globalIndex()]; + const int length = getAncestorChain(mcIter, mcIds[0], chain); // leading contributor + if (doChainQA) { + fillChainQA(mcIter, length); + } + if (length <= 0) { + continue; // leading contributor is a nucleus: kFake, chain stays -1 + } + + // e+- whose mother is a photon (conversion in front of the EMCal): start the chain at the photon, + // otherwise the direct mother check of the pair classification fails for converted photons + mcIter.setCursor(chain[0]); + if (length > 1 && std::abs(mcIter.pdgCode()) == PDG_t::kElectron) { + mcIter.setCursor(chain[1]); + if (mcIter.pdgCode() == PDG_t::kGamma) { + std::shift_left(chain.begin(), chain.end(), 1); + chain.back() = -1; + } + } + mcIter.setCursor(chain[0]); + mTruthEMC[cluster.globalIndex()] = mcIter.pdgCode() == PDG_t::kGamma ? PhotonTruth::kTruePhoton : PhotonTruth::kNonPhoton; + } + } + + /// \brief Fill ancestor chains and truth flags of all V0 photons of the DF + /// \param photons full (unfiltered) V0 photon table + /// \param legs full V0 leg table with MC labels + /// \param mcParticles full MC particle table the leg labels point into + /// \param doChainQA fill the ancestor chain QA (off for the mixed event, so chains are not counted twice) + template + void fillPcmCache(TPhotons const& photons, TLegs const& legs, TMCParticles const& mcParticles, const bool doChainQA = true) + { + auto mcIter = mcParticles.begin(); + auto posLeg = legs.begin(); + auto negLeg = legs.begin(); + auto posMc = mcParticles.begin(); + auto negMc = mcParticles.begin(); + + Chain chainPos{}; + Chain chainNeg{}; + + for (const auto& photon : photons) { + posLeg.setCursor(photon.posTrackId()); + negLeg.setCursor(photon.negTrackId()); + if (posLeg.emmcparticleId() < 0 || negLeg.emmcparticleId() < 0) { + continue; // leg without MC label: kFake, chain stays -1 + } + posMc.setCursor(posLeg.emmcparticleId()); + negMc.setCursor(negLeg.emmcparticleId()); + + auto& chain = mChainsPCM[photon.globalIndex()]; + auto& truth = mTruthPCM[photon.globalIndex()]; + const int photonId = o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(posMc, negMc, PDG_t::kPositron, PDG_t::kElectron, PDG_t::kGamma, mcParticles); + if (photonId >= 0) { + truth = PhotonTruth::kTruePhoton; + const int length = getAncestorChain(mcIter, photonId, chain); + if (doChainQA) { + fillChainQA(mcIter, length); + } + continue; + } + + // not a true conversion: K0S/Lambda V0, Dalitz pair or combinatorial fake + const int lengthPos = getAncestorChain(mcIter, posLeg.emmcparticleId(), chainPos); + if (doChainQA) { + fillChainQA(mcIter, lengthPos); // the kept chain is a tail of this one, so it ends the same way + } + getAncestorChain(mcIter, negLeg.emmcparticleId(), chainNeg); + const int ancestor = getFirstCommonAncestor(chainPos, chainNeg); + if (ancestor < 0) { + continue; // no common ancestor: kFake, chain stays -1 + } + const bool isDirectMother = chainPos[1] == ancestor && chainNeg[1] == ancestor; + truth = isDirectMother ? PhotonTruth::kNonPhoton : PhotonTruth::kFake; + const auto start = std::ranges::find(chainPos, ancestor); + std::ranges::fill(std::copy(start, chainPos.end(), chain.begin()), chain.end(), -1); + } + } + + /// \brief Classify a pair by the closest common ancestor of the two candidates + /// \param chain1 ancestor chain of the first candidate + /// \param chain2 ancestor chain of the second candidate + /// \param mcIter iterator of the full MC particle table, WILL BE CHANGED + template + PairBucket classifyPair(Chain const& chain1, Chain const& chain2, TMCIter& mcIter) + { + const int ancestor = getFirstCommonAncestor(chain1, chain2); + if (ancestor < 0) { + return PairBucket::kNone; + } + if (ancestor == chain1[0] || ancestor == chain2[0]) { + return PairBucket::kSameParticle; + } + mcIter.setCursor(ancestor); + const int absPdg = std::abs(mcIter.pdgCode()); + if (absPdg == PDG_t::kGamma || absPdg == PDG_t::kElectron) { + return PairBucket::kSameParticle; // both candidates come from the electromagnetic shower of one photon or electron + } + const bool isDirectMother = chain1[1] == ancestor && chain2[1] == ancestor; + if (isDirectMother && absPdg == PDG_t::kPi0) { + return PairBucket::kSignalPi0; + } + if (isDirectMother && absPdg == o2::constants::physics::Pdg::kEta) { + return PairBucket::kSignalEta; + } + switch (absPdg) { + case PDG_t::kPi0: + return PairBucket::kCrossPi0; + case o2::constants::physics::Pdg::kEta: + return PairBucket::kCrossEta; + case o2::constants::physics::Pdg::kOmega: + return PairBucket::kCrossOmega; + case PDG_t::kK0Short: + return PairBucket::kCrossK0S; + case o2::constants::physics::Pdg::kEtaPrime: + return PairBucket::kCrossEtaPrime; + default: + break; + } + if (isPartonLike(absPdg)) { + return PairBucket::kPartonString; + } + return PairBucket::kCrossOtherHadron; + } + + /// \brief Whether the track of a V0 leg passes the global track selection used by the primary EMCal track matching. + /// Rebuilt from the leg columns of the derived data, following getGlobalTrackSelectionRun3ITSMatch(Run3ITSibAny): + /// the golden chi2 requirement cannot be checked here, and pT/eta are the ones of the leg at the V0 vertex. + /// \param leg V0 leg + template + static bool isLikelyGlobalTrack(TLeg const& leg) + { + constexpr float MinPt = 0.1f, MaxEta = 0.8f; + constexpr int MinCrossedRows = 70; + constexpr float MinCrossedRowsOverFindable = 0.8f, MaxChi2TPC = 4.f, MaxChi2ITS = 36.f, MaxDcaZ = 2.f; + const float pt = leg.pt(); + if (!leg.hasITS() || !leg.hasTPC() || pt < MinPt || std::fabs(leg.eta()) > MaxEta) { + return false; + } + if (leg.itsNClsInnerBarrel() < 1 || leg.itsChi2NCl() > MaxChi2ITS) { // Run3ITSibAny: at least one hit in ITS layers 0-2 + return false; + } + if (leg.tpcNClsCrossedRows() < MinCrossedRows || leg.tpcCrossedRowsOverFindableCls() < MinCrossedRowsOverFindable || leg.tpcChi2NCl() > MaxChi2TPC) { + return false; + } + const float maxDcaXY = 0.0105f + 0.035f / std::pow(pt, 1.1f); // o2-linter: disable=magic-number (global track selection) + return std::fabs(leg.dcaXY()) <= maxDcaXY && std::fabs(leg.dcaZ()) <= MaxDcaZ; + } + + /// \brief Look for the V0 leg among the tracks matched to an EMC cluster and classify why it did not veto the cluster. + /// The matched track tables store no track index, so the leg is identified by its momentum. + /// \tparam isSecondary matches are secondaries (V0 legs, no E/p condition) instead of primaries + /// \param matches tracks matched to this cluster + /// \param eCluster cluster energy + /// \param pLeg momentum of the V0 leg + /// \param ptLeg transverse momentum of the V0 leg + template + void fillLegMatch(TMatches const& matches, const float eCluster, const float pLeg, const float ptLeg) + { + static constexpr std::array HistStatus = {"PCMEMC/SameParticle/hLegPrimStatus", "PCMEMC/SameParticle/hLegSecStatus"}; + static constexpr std::array HistDp = {"PCMEMC/SameParticle/hLegPrimDp", "PCMEMC/SameParticle/hLegSecDp"}; + static constexpr std::array HistResidual = {"PCMEMC/SameParticle/hLegPrimResidual", "PCMEMC/SameParticle/hLegSecResidual"}; + static constexpr float MaxRelDp = 0.1f; // max |p_match - p_leg| / p_leg to identify the match as the leg + + LegMatchStatus status = LegMatchStatus::kNoMatchStored; + if (matches.size() > 0) { + float bestDp = 1e9f; + float dEta = 0.f, dPhi = 0.f, trackPt = 0.f, trackP = 0.f; + for (const auto& match : matches) { + const float dp = (match.trackP() - pLeg) / pLeg; + if (std::fabs(dp) < std::fabs(bestDp)) { + bestDp = dp; + dEta = match.deltaEta(); + dPhi = match.deltaPhi(); + trackPt = match.trackPt(); + trackP = match.trackP(); + } + } + registry.fill(HIST(HistDp[isSecondary]), bestDp); + if (std::fabs(bestDp) > MaxRelDp) { + status = LegMatchStatus::kNoCompatibleMatch; + } else { + registry.fill(HIST(HistResidual[isSecondary]), dEta, dPhi, ptLeg); + // same windows as EMCPhotonCut::checkTrackMatching + bool isInWindow = false; + if constexpr (isSecondary) { + isInWindow = std::fabs(dEta) <= fEMCCut.GetSecTrackMatchingEta(trackPt) && std::fabs(dPhi) <= fEMCCut.GetSecTrackMatchingPhi(trackPt); + } else { + isInWindow = std::fabs(dEta) <= fEMCCut.GetTrackMatchingEta(trackPt) && std::fabs(dPhi) <= fEMCCut.GetTrackMatchingPhi(trackPt); + } + if (!isInWindow) { + status = LegMatchStatus::kOutsideWindow; + } else if (!isSecondary && eCluster / trackP >= emccuts.cfgEMCEoverp) { + status = LegMatchStatus::kInsideWindowEoverP; + } else { + status = LegMatchStatus::kInsideWindow; + } + } + } + registry.fill(HIST(HistStatus[isSecondary]), static_cast(status), ptLeg); + } + + /// \brief Find out where a PCM-EMC same particle pair comes from: what made the EMC cluster and why track matching did not remove it + /// \param pcm V0 photon + /// \param emc EMC cluster + /// \param legs full V0 leg table with MC labels + /// \param matchedPrims full table of primary tracks matched to clusters + /// \param matchedSeconds full table of secondary tracks (V0 legs) matched to clusters + /// \param mcIter iterator of the full MC particle table, WILL BE CHANGED + template + void diagnoseSameParticle(TPCM const& pcm, TEMC const& emc, TLegs const& legs, TMatchedPrims const& matchedPrims, TMatchedSecs const& matchedSeconds, TMCIter& mcIter) + { + const auto mcIds = emc.emmcparticleIds(); + if (mcIds.empty() || mcIds[0] < 0) { + return; + } + const int lead = mcIds[0]; // leading contributor, before the conversion shift of the chain + const auto posLeg = legs.iteratorAt(pcm.posTrackId()); + const auto negLeg = legs.iteratorAt(pcm.negTrackId()); + const std::array legMcIds = {posLeg.emmcparticleId(), negLeg.emmcparticleId()}; + + SameParticleOrigin origin = SameParticleOrigin::kOther; + int legIndex = -1; + if (lead == legMcIds[0] || lead == legMcIds[1]) { + origin = SameParticleOrigin::kLegElectron; + legIndex = lead == legMcIds[0] ? 0 : 1; + } else { + mcIter.setCursor(lead); + const int absPdg = std::abs(mcIter.pdgCode()); + const int motherId = mcIter.has_mothers() ? mcIter.mothersIds()[0] : -1; + if (absPdg == PDG_t::kGamma && motherId >= 0 && (motherId == legMcIds[0] || motherId == legMcIds[1])) { + origin = SameParticleOrigin::kBremsFromLeg; + } else if (absPdg == PDG_t::kElectron) { + origin = SameParticleOrigin::kOtherElectron; + } + } + + ROOT::Math::PtEtaPhiMVector v1(pcm.pt(), pcm.eta(), pcm.phi(), 0.); + ROOT::Math::PtEtaPhiMVector v2(emc.pt(), emc.eta(), emc.phi(), 0.); + ROOT::Math::PtEtaPhiMVector vMeson = v1 + v2; + registry.fill(HIST("PCMEMC/SameParticle/hOrigin"), vMeson.M(), vMeson.Pt(), static_cast(origin)); + if (origin != SameParticleOrigin::kLegElectron) { + return; + } + + const auto& leg = legIndex == 0 ? posLeg : negLeg; + const float pLeg = std::hypot(leg.px(), leg.py(), leg.pz()); + const float ptLeg = std::hypot(leg.px(), leg.py()); + if (pLeg <= 0.f) { + return; + } + registry.fill(HIST("PCMEMC/SameParticle/hLegEoverP"), emc.e() / pLeg, ptLeg); + // the primary matching only uses global tracks: other legs are no candidate there and get their own status + if (isLikelyGlobalTrack(leg)) { + fillLegMatch(matchedPrims.sliceBy(perEMCClusterMT, emc.globalIndex()), emc.e(), pLeg, ptLeg); + } else { + registry.fill(HIST("PCMEMC/SameParticle/hLegPrimStatus"), static_cast(LegMatchStatus::kNotGlobalTrack), ptLeg); + } + fillLegMatch(matchedSeconds.sliceBy(perEMCClusterMS, emc.globalIndex()), emc.e(), pLeg, ptLeg); + } + + /// \brief Species class of the common ancestor of an "other hadron" pair + /// \param pdg PDG code of the ancestor + static OtherHadronSpecies otherHadronSpecies(const int pdg) + { + const int absPdg = std::abs(pdg); + switch (absPdg) { + case PDG_t::kPiPlus: + return OtherHadronSpecies::kChargedPion; + case PDG_t::kKPlus: + return OtherHadronSpecies::kChargedKaon; + case PDG_t::kK0Long: + return OtherHadronSpecies::kK0L; + case PDG_t::kRho770_0: + case PDG_t::kRho770Plus: + return OtherHadronSpecies::kRho; + case o2::constants::physics::Pdg::kK0Star892: + case o2::constants::physics::Pdg::kKPlusStar892: + return OtherHadronSpecies::kKstar; + case o2::constants::physics::Pdg::kPhi: + return OtherHadronSpecies::kPhi; + case PDG_t::kProton: + return OtherHadronSpecies::kProton; + case PDG_t::kNeutron: + return OtherHadronSpecies::kNeutron; + case 1114: // o2-linter: disable=pdg/explicit-code (Delta-, no named constant) + case 2114: // o2-linter: disable=pdg/explicit-code (Delta0, no named constant) + case 2214: // o2-linter: disable=pdg/explicit-code (Delta+, no named constant) + case 2224: // o2-linter: disable=pdg/explicit-code (Delta++, no named constant) + return OtherHadronSpecies::kDelta; + case PDG_t::kLambda0: + return OtherHadronSpecies::kLambda; + case PDG_t::kSigmaMinus: + case PDG_t::kSigma0: + case PDG_t::kSigmaPlus: + return OtherHadronSpecies::kSigma; + case PDG_t::kXiMinus: + case 3322: // o2-linter: disable=pdg/explicit-code (Xi0, no named constant) + return OtherHadronSpecies::kXi; + default: + break; + } + constexpr int MinHadronPdg = 100; + constexpr int MaxHadronPdg = 10000000; + if (absPdg < MinHadronPdg || absPdg >= MaxHadronPdg) { + return OtherHadronSpecies::kOther; + } + // PDG numbering: mesons have no third quark, i.e. the thousands digit is 0 (also for 9000000 type resonances) + if ((absPdg / 1000) % 10 != 0) { // o2-linter: disable=magic-number (PDG digit) + return OtherHadronSpecies::kOtherBaryon; + } + // mesons: hundreds digit = heavier quark, tens digit = lighter quark, last digit = 2J+1 + // (also covers excited states with a radial/orbital prefix, e.g. 10441, 20443, 100553) + const int heavyQuark = (absPdg / 100) % 10; // o2-linter: disable=magic-number (PDG digit) + const int lightQuark = (absPdg / 10) % 10; // o2-linter: disable=magic-number (PDG digit) + constexpr int Strange = 3, Charm = 4, Beauty = 5; + constexpr int ExcitedPrefix = 10000; + constexpr int SpinTwo = 5; // 2J+1 for J = 2 + if (heavyQuark == Charm) { + return lightQuark == Charm ? OtherHadronSpecies::kCharmonium : OtherHadronSpecies::kOpenCharm; + } + if (heavyQuark == Beauty) { + return lightQuark == Beauty ? OtherHadronSpecies::kBottomonium : OtherHadronSpecies::kOpenBeauty; + } + if (absPdg == 20213 || absPdg == 20113) { // o2-linter: disable=pdg/explicit-code (a1(1260), no named constant) + return OtherHadronSpecies::kA1; + } + if (heavyQuark == Strange && lightQuark < Strange && (absPdg >= ExcitedPrefix || absPdg % 10 == SpinTwo)) { // o2-linter: disable=magic-number (PDG digit) + return OtherHadronSpecies::kExcitedK; + } + return OtherHadronSpecies::kOtherMeson; + } + + /// \brief How an MC particle was produced + /// \param mcIter iterator of the full MC particle table, WILL BE CHANGED + /// \param id index of the particle + template + static SplitProduction productionOf(TMCIter& mcIter, const int id) + { + mcIter.setCursor(id); + if (mcIter.producedByGenerator()) { + return SplitProduction::kGeneratorDecay; + } + switch (mcIter.getProcess()) { + case TMCProcess::kPDecay: + return SplitProduction::kTransportDecay; + case TMCProcess::kPHadronic: + case TMCProcess::kPHInhelastic: + case TMCProcess::kPNuclearAbsorption: + case TMCProcess::kPNCapture: + case TMCProcess::kPPbarAnnihilation: + case TMCProcess::kPNbarAnnihilation: + case TMCProcess::kPEvaporation: + case TMCProcess::kPNuclearFission: + case TMCProcess::kPPhotoNuclear: + case TMCProcess::kPPhotonInhelastic: + return SplitProduction::kHadronicInteraction; + default: + return SplitProduction::kOtherProcess; + } + } + + /// \brief Fill which hadron is the common ancestor of an "other hadron" pair and how it split into the two branches + /// \param chain1 ancestor chain of the first candidate + /// \param chain2 ancestor chain of the second candidate + /// \param pairTruth truth flag of the pair + /// \param vMeson pair four vector + /// \param mcIter iterator of the full MC particle table, WILL BE CHANGED + template + void diagnoseOtherHadron(Chain const& chain1, Chain const& chain2, const PhotonTruth pairTruth, ROOT::Math::PtEtaPhiMVector const& vMeson, TMCIter& mcIter) + { + static constexpr std::array HistAncestor = {"PCM/OtherHadron/hAncestor", "EMC/OtherHadron/hAncestor", "PCMEMC/OtherHadron/hAncestor"}; + static constexpr std::array HistAncestorPdg = {"PCM/OtherHadron/hAncestorPdg", "EMC/OtherHadron/hAncestorPdg", "PCMEMC/OtherHadron/hAncestorPdg"}; + static constexpr int MaxPdgBin = 9999; + + const int ancestor = getFirstCommonAncestor(chain1, chain2); + const auto pos1 = std::distance(chain1.begin(), std::ranges::find(chain1, ancestor)); + const auto pos2 = std::distance(chain2.begin(), std::ranges::find(chain2, ancestor)); + if (ancestor < 0 || pos1 < 1 || pos2 < 1 || pos1 >= std::ssize(chain1) || pos2 >= std::ssize(chain2)) { + return; // not possible for "other hadron" (the ancestor is neither of the candidates), protection only + } + // the particle directly below the ancestor in each branch tells how the ancestor split + const SplitProduction production = std::max(productionOf(mcIter, chain1[pos1 - 1]), productionOf(mcIter, chain2[pos2 - 1])); + mcIter.setCursor(ancestor); + const int pdg = mcIter.pdgCode(); + registry.fill(HIST(HistAncestor[pairType]), vMeson.M(), vMeson.Pt(), static_cast(otherHadronSpecies(pdg)), static_cast(production), static_cast(pairTruth)); + registry.fill(HIST(HistAncestorPdg[pairType]), std::min(std::abs(pdg), MaxPdgBin)); + } + + /// \brief Origin of a photon candidate: walk up its chain through the EM steps (gamma, e+-), the first other particle decides + /// \param chain ancestor chain of the candidate + /// \param mcIter iterator of the full MC particle table, WILL BE CHANGED + /// \return origin and the index of the pi0/eta (-1 for else) + template + static std::pair photonOrigin(Chain const& chain, TMCIter& mcIter) + { + for (const auto& id : chain) { + if (id < 0) { + break; + } + mcIter.setCursor(id); + const int absPdg = std::abs(mcIter.pdgCode()); + if (absPdg == PDG_t::kGamma || absPdg == PDG_t::kElectron) { + continue; // EM step: conversion, bremsstrahlung, shower + } + if (absPdg == PDG_t::kPi0) { + return {PhotonOrigin::kFromPi0, id}; + } + if (absPdg == o2::constants::physics::Pdg::kEta) { + return {PhotonOrigin::kFromEta, id}; + } + break; + } + return {PhotonOrigin::kElse, -1}; + } + + /// \brief Class of a photon pair for the pi0 prefilter study + /// \param bucket closest common ancestor bucket of the pair: same particle pairs get their own class + template + static PrefilterPairClass prefilterClassOf(const PairBucket bucket, Chain const& chain1, Chain const& chain2, TMCIter& mcIter) + { + if (bucket == PairBucket::kSameParticle) { + return PrefilterPairClass::kSameParticle; + } + const auto [origin1, id1] = photonOrigin(chain1, mcIter); + const auto [origin2, id2] = photonOrigin(chain2, mcIter); + const int nPi0 = (origin1 == PhotonOrigin::kFromPi0 ? 1 : 0) + (origin2 == PhotonOrigin::kFromPi0 ? 1 : 0); + const int nEta = (origin1 == PhotonOrigin::kFromEta ? 1 : 0) + (origin2 == PhotonOrigin::kFromEta ? 1 : 0); + constexpr int Both = 2; + if (nPi0 == Both) { + return id1 == id2 ? PrefilterPairClass::kSamePi0 : PrefilterPairClass::kTwoPi0; + } + if (nEta == Both) { + return id1 == id2 ? PrefilterPairClass::kSameEta : PrefilterPairClass::kTwoEta; + } + if (nPi0 == 1 && nEta == 1) { + return PrefilterPairClass::kPi0Eta; + } + if (nPi0 == 1) { + return PrefilterPairClass::kPi0Else; + } + return nEta == 1 ? PrefilterPairClass::kEtaElse : PrefilterPairClass::kBothElse; + } + + /// \brief Whether a photon pair passes the pair cuts of fillPair and lies inside the pi0 window of the prefilter + template + bool isInPrefilterWindow(TPhoton1 const& g1, TPhoton2 const& g2) + { + ROOT::Math::PtEtaPhiMVector v1(g1.pt(), g1.eta(), g1.phi(), 0.); + ROOT::Math::PtEtaPhiMVector v2(g2.pt(), g2.eta(), g2.phi(), 0.); + const float openingAngle = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); + if (openingAngle <= mesonConfig.minOpenAngle) { + return false; + } + const float mass = (v1 + v2).M(); + return mesonConfig.prefilterMassMin <= mass && mass <= mesonConfig.prefilterMassMax; + } + + /// \brief pi0 prefilter: tag both photons of every same-kind pair inside the pi0 window (PCM-PCM or EMC-EMC) + /// \param tags tag vector of the photon type, index = globalIndex + template + void tagSameKind(TPhotons const& photonsPerCollision, EMBitFlags const& flags, std::vector& tags) + { + for (const auto& [g1, g2] : combinations(CombinationsStrictlyUpperIndexPolicy(photonsPerCollision, photonsPerCollision))) { + if (flags.test(g1.globalIndex()) && flags.test(g2.globalIndex()) && isInPrefilterWindow(g1, g2)) { + tags[g1.globalIndex()] = 1; + tags[g2.globalIndex()] = 1; + } + } + } + + /// \brief pi0 prefilter: tag both photons of every PCM-EMC pair inside the pi0 window + template + void tagPcmEmc(TPcmPhotons const& pcmPerCollision, TEmcPhotons const& emcPerCollision, EMBitFlags const& v0flags, EMBitFlags const& emcFlags, std::vector& tagsPCM, std::vector& tagsEMC) + { + for (const auto& [g1, g2] : combinations(CombinationsFullIndexPolicy(pcmPerCollision, emcPerCollision))) { + if (v0flags.test(g1.globalIndex()) && emcFlags.test(g2.globalIndex()) && isInPrefilterWindow(g1, g2)) { + tagsPCM[g1.globalIndex()] = 1; + tagsEMC[g2.globalIndex()] = 1; + } + } + } + + /// \brief pi0 prefilter with all three pair types: fill mPrefilterAllPCM / mPrefilterAllEMC for one collision + template + void tagAllPairTypes(TPcmPhotons const& pcmPerCollision, TEmcPhotons const& emcPerCollision, EMBitFlags const& v0flags, EMBitFlags const& emcFlags) + { + tagSameKind(pcmPerCollision, v0flags, mPrefilterAllPCM); + tagSameKind(emcPerCollision, emcFlags, mPrefilterAllEMC); + tagPcmEmc(pcmPerCollision, emcPerCollision, v0flags, emcFlags, mPrefilterAllPCM, mPrefilterAllEMC); + } + + /// \brief Apply the pair cuts and fill the pair ancestor histogram + /// \param nTagged number of photons of this pair tagged by the pi0 prefilter with the pair type of the process function + /// \param nTaggedAll number of photons of this pair tagged by the pi0 prefilter with all three pair types, -1: not computed + /// \return bucket of the pair, std::nullopt if the pair was rejected + template + std::optional fillPair(TPhoton1 const& g1, TPhoton2 const& g2, Chain const& chain1, Chain const& chain2, const PhotonTruth truth1, const PhotonTruth truth2, const float centOrMult, const int nTagged, const int nTaggedAll, TMCIter& mcIter) + { + static constexpr std::array HistPairAncestor = {"PCM/hPairAncestor", "EMC/hPairAncestor", "PCMEMC/hPairAncestor"}; + static constexpr std::array HistPrefilter = {"PCM/hPrefilter", "EMC/hPrefilter", "PCMEMC/hPrefilter"}; + static constexpr std::array HistPrefilterAll = {"PCM/hPrefilterAll", "EMC/hPrefilterAll", "PCMEMC/hPrefilterAll"}; + + ROOT::Math::PtEtaPhiMVector v1(g1.pt(), g1.eta(), g1.phi(), 0.); + ROOT::Math::PtEtaPhiMVector v2(g2.pt(), g2.eta(), g2.phi(), 0.); + ROOT::Math::PtEtaPhiMVector vMeson = v1 + v2; + float openingAngle = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); + + registry.fill(HIST("hMesonCuts"), 1); + if (openingAngle <= mesonConfig.minOpenAngle) { + registry.fill(HIST("hMesonCuts"), 2); + return std::nullopt; + } + registry.fill(HIST("hMesonCuts"), 6); + + const PhotonTruth pairTruth = std::max(truth1, truth2); + const PairBucket bucket = classifyPair(chain1, chain2, mcIter); + registry.fill(HIST(HistPairAncestor[pairType]), vMeson.M(), vMeson.Pt(), centOrMult, static_cast(pairTruth), static_cast(bucket)); + const int prefilterClass = static_cast(prefilterClassOf(bucket, chain1, chain2, mcIter)); + registry.fill(HIST(HistPrefilter[pairType]), vMeson.M(), vMeson.Pt(), centOrMult, prefilterClass, nTagged); + if (nTaggedAll >= 0) { + registry.fill(HIST(HistPrefilterAll[pairType]), vMeson.M(), vMeson.Pt(), centOrMult, prefilterClass, nTaggedAll); + } + if (bucket == PairBucket::kCrossOtherHadron) { + diagnoseOtherHadron(chain1, chain2, pairTruth, vMeson, mcIter); + } + if (mesonConfig.cfgEnableQA.value) { + registry.fill(HIST("mesonQA/hInvMassPt"), vMeson.M(), vMeson.Pt()); + } + return bucket; + } + + /// \brief eta a cluster would have if its own vertex vzOld is swapped for vzNew, same as in taskPi0FlowEMC + /// \param etaOld old eta value + /// \param vzOld old primary vertex z position + /// \param vzNew new primary vertex z position + /// \param emcalR radius of the EMCal + static float correctEtaForVertexShift(float etaOld, float vzOld, float vzNew, float emcalR) + { + return std::asinh(std::sinh(etaOld) + (vzOld - vzNew) / emcalR); + } + + /// \brief Apply the pair cuts to a mixed event pair and fill the mixed event histogram + /// \param eta2 eta of the second photon, possibly re-projected onto the vertex of the first collision + template + void fillMixedPair(TPhoton1 const& g1, TPhoton2 const& g2, const float eta2, const PhotonTruth truth1, const PhotonTruth truth2, const float centOrMult) + { + static constexpr std::array HistMixed = {"PCM/hMixed", "EMC/hMixed", "PCMEMC/hMixed"}; + + ROOT::Math::PtEtaPhiMVector v1(g1.pt(), g1.eta(), g1.phi(), 0.); + ROOT::Math::PtEtaPhiMVector v2(g2.pt(), eta2, g2.phi(), 0.); + ROOT::Math::PtEtaPhiMVector vMeson = v1 + v2; + float openingAngle = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); + if (openingAngle <= mesonConfig.minOpenAngle) { + return; + } + registry.fill(HIST(HistMixed[pairType]), vMeson.M(), vMeson.Pt(), centOrMult, static_cast(std::max(truth1, truth2))); + } + + /// \brief Size of a table that is only subscribed in some process functions, nullptr otherwise + template + static std::size_t optionalTableSize(TTable const& table) + { + if constexpr (std::is_same_v) { + return 0; + } else { + return table.size(); + } + } + + /// \brief PCM-PCM same event + /// \tparam AllPairTypes also tag the photons for the pi0 prefilter with EMC-EMC and PCM-EMC pairs (needs the EMC tables, nullptr otherwise) + template + void runPcm(Colls const& collisions, PcmPhotons const& photons, PcmMcLegs const& legs, McParticles const& mcParticles, TClusters const& clusters, TMatchedPrims const& matchedPrims, TMatchedSeconds const& matchedSeconds) + { + if (photons.size() <= 0) { + LOG(info) << "Skipping DF because there are not photons!"; + return; + } + // none of the photon tables is filtered, so its size is the size of the full table the globalIndex points into + resetCache(mChainsPCM, mTruthPCM, photons.size()); + fillPcmCache(photons, legs, mcParticles); + mPrefilterPCM.assign(photons.size(), 0); + + EMBitFlags v0flags(photons.size()); + fV0PhotonCut.AreSelectedRunning(v0flags, photons, ®istry); + + // EMC clusters only for the prefilter tagging: no chains, no cut QA + EMBitFlags emcFlags(optionalTableSize(clusters)); + if constexpr (AllPairTypes) { + mPrefilterAllPCM.assign(photons.size(), 0); + mPrefilterAllEMC.assign(clusters.size(), 0); + fEMCCut.AreSelectedRunning(emcFlags, clusters, matchedPrims, matchedSeconds); + } + + auto mcIter = mcParticles.begin(); + for (const auto& collision : collisions) { + initCCDB(collision); + if (!isFullEventSelected(collision, true)) { + continue; + } + const float centOrMult = getCentralityOrMultiplicity(collision); + auto photonsPerCollision = photons.sliceBy(perCollisionPCM, collision.globalIndex()); + + // pi0 prefilter: tag both photons of every pair inside the pi0 window, with the pair type of this process function + tagSameKind(photonsPerCollision, v0flags, mPrefilterPCM); + if constexpr (AllPairTypes) { + tagAllPairTypes(photonsPerCollision, clusters.sliceBy(perCollisionEMC, collision.globalIndex()), v0flags, emcFlags); + } + + for (const auto& [g1, g2] : combinations(CombinationsStrictlyUpperIndexPolicy(photonsPerCollision, photonsPerCollision))) { + if (!(v0flags.test(g1.globalIndex())) || !(v0flags.test(g2.globalIndex()))) { + continue; + } + const int nTagged = mPrefilterPCM[g1.globalIndex()] + mPrefilterPCM[g2.globalIndex()]; + int nTaggedAll = -1; + if constexpr (AllPairTypes) { + nTaggedAll = mPrefilterAllPCM[g1.globalIndex()] + mPrefilterAllPCM[g2.globalIndex()]; + } + fillPair(g1, g2, mChainsPCM[g1.globalIndex()], mChainsPCM[g2.globalIndex()], mTruthPCM[g1.globalIndex()], mTruthPCM[g2.globalIndex()], centOrMult, nTagged, nTaggedAll, mcIter); + } + } + } + + /// \brief EMC-EMC same event + /// \tparam AllPairTypes also tag the photons for the pi0 prefilter with PCM-PCM and PCM-EMC pairs (needs the PCM table, nullptr otherwise) + template + void runEmcal(Colls const& collisions, EMCalPhotons const& clusters, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds, McParticles const& mcParticles, TPcmPhotons const& photons) + { + if (clusters.size() <= 0) { + LOG(info) << "Skipping DF because there are not photons!"; + return; + } + auto mcIter = mcParticles.begin(); + // none of the photon tables is filtered, so its size is the size of the full table the globalIndex points into + resetCache(mChainsEMC, mTruthEMC, clusters.size()); + fillEmcCache(clusters, mcIter); + mPrefilterEMC.assign(clusters.size(), 0); + + EMBitFlags emcFlags(clusters.size()); + fEMCCut.AreSelectedRunning(emcFlags, clusters, matchedPrims, matchedSeconds, ®istry); + + // PCM photons only for the prefilter tagging: no chains, no cut QA + EMBitFlags v0flags(optionalTableSize(photons)); + if constexpr (AllPairTypes) { + mPrefilterAllPCM.assign(photons.size(), 0); + mPrefilterAllEMC.assign(clusters.size(), 0); + fV0PhotonCut.AreSelectedRunning(v0flags, photons); + } + + for (const auto& collision : collisions) { + initCCDB(collision); + if (!isFullEventSelected(collision, true)) { + continue; + } + const float centOrMult = getCentralityOrMultiplicity(collision); + auto photonsPerCollision = clusters.sliceBy(perCollisionEMC, collision.globalIndex()); + + // pi0 prefilter: tag both photons of every pair inside the pi0 window, with the pair type of this process function + tagSameKind(photonsPerCollision, emcFlags, mPrefilterEMC); + if constexpr (AllPairTypes) { + tagAllPairTypes(photons.sliceBy(perCollisionPCM, collision.globalIndex()), photonsPerCollision, v0flags, emcFlags); + } + + for (const auto& [g1, g2] : combinations(CombinationsStrictlyUpperIndexPolicy(photonsPerCollision, photonsPerCollision))) { + if (!(emcFlags.test(g1.globalIndex())) || !(emcFlags.test(g2.globalIndex()))) { + continue; + } + const int nTagged = mPrefilterEMC[g1.globalIndex()] + mPrefilterEMC[g2.globalIndex()]; + int nTaggedAll = -1; + if constexpr (AllPairTypes) { + nTaggedAll = mPrefilterAllEMC[g1.globalIndex()] + mPrefilterAllEMC[g2.globalIndex()]; + } + fillPair(g1, g2, mChainsEMC[g1.globalIndex()], mChainsEMC[g2.globalIndex()], mTruthEMC[g1.globalIndex()], mTruthEMC[g2.globalIndex()], centOrMult, nTagged, nTaggedAll, mcIter); + } + } + } + + // PCM-PCM same event + void processPcm(Colls const& collisions, PcmPhotons const& photons, PcmMcLegs const& legs, McParticles const& mcParticles) + { + runPcm(collisions, photons, legs, mcParticles, nullptr, nullptr, nullptr); + } + PROCESS_SWITCH(TwoPhotonMcTask, processPcm, "Process for pcm photons", false); + + // PCM-PCM same event, pi0 prefilter study also with the photons tagged by all three pair types (needs the EMC tables) + void processPcmPrefilterAll(Colls const& collisions, PcmPhotons const& photons, PcmMcLegs const& legs, EMCalPhotons const& clusters, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds, McParticles const& mcParticles) + { + runPcm(collisions, photons, legs, mcParticles, clusters, matchedPrims, matchedSeconds); + } + PROCESS_SWITCH(TwoPhotonMcTask, processPcmPrefilterAll, "Process for pcm photons, pi0 prefilter also with all pair types (instead of processPcm)", false); + + // EMCal-EMCal same event + void processEmcal(Colls const& collisions, EMCalPhotons const& clusters, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds, McParticles const& mcParticles) + { + runEmcal(collisions, clusters, matchedPrims, matchedSeconds, mcParticles, nullptr); + } + PROCESS_SWITCH(TwoPhotonMcTask, processEmcal, "Process for emcal photons", false); + + // EMCal-EMCal same event, pi0 prefilter study also with the photons tagged by all three pair types (needs the PCM tables) + void processEmcalPrefilterAll(Colls const& collisions, EMCalPhotons const& clusters, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds, PcmPhotons const& photons, PcmMcLegs const&, McParticles const& mcParticles) + { + runEmcal(collisions, clusters, matchedPrims, matchedSeconds, mcParticles, photons); + } + PROCESS_SWITCH(TwoPhotonMcTask, processEmcalPrefilterAll, "Process for emcal photons, pi0 prefilter also with all pair types (instead of processEmcal)", false); + + // PCM-EMCal same event + void processPcmEmcal(Colls const& collisions, EMCalPhotons const& clusters, PcmPhotons const& photons, PcmMcLegs const& legs, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds, McParticles const& mcParticles) + { + if (clusters.size() <= 0 || photons.size() <= 0) { + LOG(info) << "Skipping DF because there are not photons!"; + return; + } + auto mcIter = mcParticles.begin(); + // none of the photon tables is filtered, so its size is the size of the full table the globalIndex points into + resetCache(mChainsPCM, mTruthPCM, photons.size()); + resetCache(mChainsEMC, mTruthEMC, clusters.size()); + fillPcmCache(photons, legs, mcParticles); + fillEmcCache(clusters, mcIter); + mPrefilterPCM.assign(photons.size(), 0); + mPrefilterEMC.assign(clusters.size(), 0); + const bool prefilterAll = mesonConfig.prefilterAllPairTypes.value; + if (prefilterAll) { + mPrefilterAllPCM.assign(photons.size(), 0); + mPrefilterAllEMC.assign(clusters.size(), 0); + } + + EMBitFlags emcFlags(clusters.size()); + fEMCCut.AreSelectedRunning(emcFlags, clusters, matchedPrims, matchedSeconds, ®istry); + EMBitFlags v0flags(photons.size()); + fV0PhotonCut.AreSelectedRunning(v0flags, photons, ®istry); + + for (const auto& collision : collisions) { + initCCDB(collision); + if (!isFullEventSelected(collision, true)) { + continue; + } + const float centOrMult = getCentralityOrMultiplicity(collision); + auto photonsEMCPerCollision = clusters.sliceBy(perCollisionEMC, collision.globalIndex()); + auto photonsPCMPerCollision = photons.sliceBy(perCollisionPCM, collision.globalIndex()); + + // pi0 prefilter: tag both photons of every pair inside the pi0 window, with the pair type of this process function + tagPcmEmc(photonsPCMPerCollision, photonsEMCPerCollision, v0flags, emcFlags, mPrefilterPCM, mPrefilterEMC); + if (prefilterAll) { + tagAllPairTypes(photonsPCMPerCollision, photonsEMCPerCollision, v0flags, emcFlags); + } + + // PCM - EMC loop, two different tables: all combinations + for (const auto& [g1, g2] : combinations(CombinationsFullIndexPolicy(photonsPCMPerCollision, photonsEMCPerCollision))) { + if (!(v0flags.test(g1.globalIndex())) || !(emcFlags.test(g2.globalIndex()))) { + continue; + } + const int nTagged = mPrefilterPCM[g1.globalIndex()] + mPrefilterEMC[g2.globalIndex()]; + const int nTaggedAll = prefilterAll ? mPrefilterAllPCM[g1.globalIndex()] + mPrefilterAllEMC[g2.globalIndex()] : -1; + const auto bucket = fillPair(g1, g2, mChainsPCM[g1.globalIndex()], mChainsEMC[g2.globalIndex()], mTruthPCM[g1.globalIndex()], mTruthEMC[g2.globalIndex()], centOrMult, nTagged, nTaggedAll, mcIter); + if (bucket == PairBucket::kSameParticle) { + diagnoseSameParticle(g1, g2, legs, matchedPrims, matchedSeconds, mcIter); + } + } + } + } + PROCESS_SWITCH(TwoPhotonMcTask, processPcmEmcal, "Process for pcm and emcal photons", true); + + // PCM-PCM mixed event + void processPcmMixed(Colls const& collisions, PcmPhotons const& photons, PcmMcLegs const& legs, McParticles const& mcParticles) + { + if (photons.size() <= 0) { + LOG(info) << "Skipping DF because there are not photons!"; + return; + } + // the mixed pairs are built inside the DF, so the per DF cache is valid for them + resetCache(mChainsPCM, mTruthPCM, photons.size()); + fillPcmCache(photons, legs, mcParticles, false); + + EMBitFlags v0flags(photons.size()); + fV0PhotonCut.AreSelectedRunning(v0flags, photons); + + BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins}}; + auto photonTuple = std::make_tuple(photons); + SameKindPair pair{binningMixedEvent, mixingConfig.cfgMixingDepth, -1, collisions, photonTuple, &cache}; + + for (const auto& [c1, photons1, c2, photons2] : pair) { + if (!isFullEventSelected(c1) || !isFullEventSelected(c2)) { + continue; + } + const float centOrMult = getCentralityOrMultiplicity(c1); + for (const auto& [g1, g2] : combinations(CombinationsFullIndexPolicy(photons1, photons2))) { + if (!(v0flags.test(g1.globalIndex())) || !(v0flags.test(g2.globalIndex()))) { + continue; + } + fillMixedPair(g1, g2, g2.eta(), mTruthPCM[g1.globalIndex()], mTruthPCM[g2.globalIndex()], centOrMult); + } + } + } + PROCESS_SWITCH(TwoPhotonMcTask, processPcmMixed, "Process for pcm photons, mixed event", false); + + // EMCal-EMCal mixed event + void processEmcalMixed(Colls const& collisions, EMCalPhotons const& clusters, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds, McParticles const& mcParticles) + { + if (clusters.size() <= 0) { + LOG(info) << "Skipping DF because there are not photons!"; + return; + } + auto mcIter = mcParticles.begin(); + // the mixed pairs are built inside the DF, so the per DF cache is valid for them + resetCache(mChainsEMC, mTruthEMC, clusters.size()); + fillEmcCache(clusters, mcIter, false); + + EMBitFlags emcFlags(clusters.size()); + fEMCCut.AreSelectedRunning(emcFlags, clusters, matchedPrims, matchedSeconds); + + BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins}}; + auto clusterTuple = std::make_tuple(clusters); + SameKindPair pair{binningMixedEvent, mixingConfig.cfgMixingDepth, -1, collisions, clusterTuple, &cache}; + + for (const auto& [c1, clusters1, c2, clusters2] : pair) { + if (!isFullEventSelected(c1) || !isFullEventSelected(c2)) { + continue; + } + const float centOrMult = getCentralityOrMultiplicity(c1); + for (const auto& [g1, g2] : combinations(CombinationsFullIndexPolicy(clusters1, clusters2))) { + if (!(emcFlags.test(g1.globalIndex())) || !(emcFlags.test(g2.globalIndex()))) { + continue; + } + // cluster 2 seen from the vertex of collision 1, as in taskPi0FlowEMC + const float eta2 = mixingConfig.cfgCorrectMixedVtxEta.value ? correctEtaForVertexShift(g2.eta(), c2.posZ(), c1.posZ(), mixingConfig.cfgEmcalEffRadius.value) : g2.eta(); + fillMixedPair(g1, g2, eta2, mTruthEMC[g1.globalIndex()], mTruthEMC[g2.globalIndex()], centOrMult); + } + } + } + PROCESS_SWITCH(TwoPhotonMcTask, processEmcalMixed, "Process for emcal photons, mixed event", false); + + // PCM-EMCal mixed event + void processPcmEmcalMixed(Colls const& collisions, EMCalPhotons const& clusters, PcmPhotons const& photons, PcmMcLegs const& legs, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds, McParticles const& mcParticles) + { + if (clusters.size() <= 0 || photons.size() <= 0) { + LOG(info) << "Skipping DF because there are not photons!"; + return; + } + auto mcIter = mcParticles.begin(); + // the mixed pairs are built inside the DF, so the per DF cache is valid for them + resetCache(mChainsPCM, mTruthPCM, photons.size()); + resetCache(mChainsEMC, mTruthEMC, clusters.size()); + fillPcmCache(photons, legs, mcParticles, false); + fillEmcCache(clusters, mcIter, false); + + EMBitFlags emcFlags(clusters.size()); + fEMCCut.AreSelectedRunning(emcFlags, clusters, matchedPrims, matchedSeconds); + EMBitFlags v0flags(photons.size()); + fV0PhotonCut.AreSelectedRunning(v0flags, photons); + + BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins}}; + auto associatedTables = std::make_tuple(photons, clusters); + Pair pair{binningMixedEvent, mixingConfig.cfgMixingDepth, -1, collisions, associatedTables, &cache}; + + for (const auto& [c1, photonsPCM, c2, photonsEMC] : pair) { + if (!isFullEventSelected(c1) || !isFullEventSelected(c2)) { + continue; + } + const float centOrMult = getCentralityOrMultiplicity(c1); + for (const auto& [g1, g2] : combinations(CombinationsFullIndexPolicy(photonsPCM, photonsEMC))) { + if (!(v0flags.test(g1.globalIndex())) || !(emcFlags.test(g2.globalIndex()))) { + continue; + } + fillMixedPair(g1, g2, g2.eta(), mTruthPCM[g1.globalIndex()], mTruthEMC[g2.globalIndex()], centOrMult); + } + } + } + PROCESS_SWITCH(TwoPhotonMcTask, processPcmEmcalMixed, "Process for pcm and emcal photons, mixed event", false); + +}; // End struct TwoPhotonMcTask + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{adaptAnalysisTask(context)}; +} diff --git a/PWGEM/PhotonMeson/Utils/AnalyticV0.h b/PWGEM/PhotonMeson/Utils/AnalyticV0.h index 90bfb05b272..ede542509cf 100644 --- a/PWGEM/PhotonMeson/Utils/AnalyticV0.h +++ b/PWGEM/PhotonMeson/Utils/AnalyticV0.h @@ -22,10 +22,11 @@ #include #include -#include #include #include +#include + #include #include #include diff --git a/PWGEM/PhotonMeson/Utils/ConversionPointProfile.h b/PWGEM/PhotonMeson/Utils/ConversionPointProfile.h new file mode 100644 index 00000000000..2749319def2 --- /dev/null +++ b/PWGEM/PhotonMeson/Utils/ConversionPointProfile.h @@ -0,0 +1,177 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file ConversionPointProfile.h +/// \brief radius-profiled conversion point of a photon conversion (alternative to the helix touching point) +/// \author Stefanie Mrozinski, stefanie.mrozinski@cern.ch + +#ifndef PWGEM_PHOTONMESON_UTILS_CONVERSIONPOINTPROFILE_H_ +#define PWGEM_PHOTONMESON_UTILS_CONVERSIONPOINTPROFILE_H_ + +#include "Common/Core/RecoDecay.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +namespace o2::pwgem::photonmeson +{ + +constexpr int NProfPar = 4; // (rphi, z, phi, tgl) +constexpr int NProfR = 75; +constexpr float ProfRMin = 10.f; // trial radii 10 .. 84 cm + +/// \brief chi2 of a leg pair vs the trial radius, its minimum and class +struct RadiusProfile { + std::array chi2{}; + std::array ok{}; + int status = 4; + int iMin = -1; + float rMin = -1.f; + float chi2Min = -1.f; + float halfWidth = -1.f; + std::array xyz{-999.f, -999.f, -999.f}; +}; + +/// \brief parameters and covariance of one leg at every trial radius +struct LegScan { + std::array ok{}; + std::array, NProfR> m{}; // phi of the point, z, phi of the direction, tgl + std::array, NProfR> c{}; // lower triangle of the (rphi, z, phi, tgl) covariance + std::array, NProfR> pos{}; // global point +}; + +//_______________________________________________________________________ +/// \brief propagate one leg inward from its IU to a radius and keep its parameters there +/// \param trackIU leg at its innermost update +/// \param bz magnetic field +/// \param matCorr material correction of the propagation +/// \param out output: parameters and covariance per radius, ok = false outside the reach of the leg +inline void cpScanLeg(o2::track::TrackParCov const& trackIU, const float bz, const o2::base::Propagator::MatCorrType matCorr, LegScan& out) +{ + auto trk = trackIU; + const auto iu = trk.getXYZGlo(); + const float rIU = std::hypot(iu.X(), iu.Y()); + for (int k = NProfR - 1; k >= 0; --k) { + const float r = ProfRMin + k; + out.ok[k] = false; + if (r >= rIU - 0.1f) { // o2-linter: disable=magic-number (margin) + continue; + } + float xl = 0.f; + if (!trk.getXatLabR(r, xl, bz, o2::track::DirInward)) { + break; + } + if (!o2::base::Propagator::Instance()->propagateToX(trk, xl, bz, o2::base::PropagatorImpl::MAX_SIN_PHI, o2::base::PropagatorImpl::MAX_STEP, matCorr)) { + break; + } + const auto g = trk.getXYZGlo(); + const float snp = trk.getSnp(); + const float f = 1.f / std::sqrt(std::max(1.f - snp * snp, 1e-4f)); // o2-linter: disable=magic-number (floor) + out.m[k] = {std::atan2(g.Y(), g.X()), trk.getZ(), trk.getAlpha() + std::asin(std::clamp(snp, -1.f, 1.f)), trk.getTgl()}; + out.c[k] = {trk.getSigmaY2(), trk.getSigmaZY(), trk.getSigmaZ2(), trk.getSigmaSnpY() * f, trk.getSigmaSnpZ() * f, trk.getSigmaSnp2() * f * f, trk.getSigmaTglY(), trk.getSigmaTglZ(), trk.getSigmaTglSnp() * f, trk.getSigmaTgl2()}; + out.pos[k] = {g.X(), g.Y(), g.Z()}; + out.ok[k] = true; + } +} + +inline void cpProfileFinish(LegScan const& p, LegScan const& n, RadiusProfile& out); + +//_______________________________________________________________________ +/// \brief chi2 of a common point and a common direction of two legs at every trial radius, then minimum and class +/// \param p scan of the positive leg +/// \param n scan of the negative leg +/// \param zSig2Pos extra z variance of the positive leg (cm^2, legs without ITS) +/// \param zSig2Ele extra z variance of the negative leg (cm^2, legs without ITS) +/// \param out output: profile, minimum, half width and class +inline void cpRadiusProfile(LegScan const& p, LegScan const& n, const float zSig2Pos, const float zSig2Ele, RadiusProfile& out) +{ + out = RadiusProfile{}; + for (int k = 0; k < NProfR; ++k) { + out.ok[k] = p.ok[k] && n.ok[k]; + if (!out.ok[k]) { + continue; + } + const float r = ProfRMin + k; + ROOT::Math::SVector d; + d[0] = r * RecoDecay::constrainAngle(p.m[k][0] - n.m[k][0], -o2::constants::math::PI); + d[1] = p.m[k][1] - n.m[k][1]; + d[2] = RecoDecay::constrainAngle(p.m[k][2] - n.m[k][2], -o2::constants::math::PI); + d[3] = p.m[k][3] - n.m[k][3]; + ROOT::Math::SMatrix> cov; + int idx = 0; + for (int i = 0; i < NProfPar; ++i) { + for (int j = 0; j <= i; ++j) { + cov(i, j) = p.c[k][idx] + n.c[k][idx]; + ++idx; + } + } + cov(1, 1) += zSig2Pos + zSig2Ele; + if (!cov.Invert()) { + out.ok[k] = false; + continue; + } + out.chi2[k] = static_cast(ROOT::Math::Similarity(d, cov)); + if (out.iMin < 0 || out.chi2[k] < out.chi2Min) { + out.iMin = k; + out.chi2Min = out.chi2[k]; + } + } + cpProfileFinish(p, n, out); +} +//_______________________________________________________________________ +/// \brief minimum, half width of the dchi2 < 1 region, other minima and class of a filled profile +/// \param p scan of the positive leg (point at the minimum) +/// \param n scan of the negative leg (point at the minimum) +/// \param out profile, completed in place +inline void cpProfileFinish(LegScan const& p, LegScan const& n, RadiusProfile& out) +{ + if (out.iMin < 0) { + return; + } + out.rMin = ProfRMin + out.iMin; + out.xyz = {0.5f * (p.pos[out.iMin][0] + n.pos[out.iMin][0]), 0.5f * (p.pos[out.iMin][1] + n.pos[out.iMin][1]), 0.5f * (p.pos[out.iMin][2] + n.pos[out.iMin][2])}; + // valid range, dchi2 < 1 region around the minimum, other minima + int kLo = out.iMin; + int kHi = kLo; + while (kLo - 1 >= 0 && out.ok[kLo - 1] && out.chi2[kLo - 1] - out.chi2Min < 1.f) { + --kLo; + } + while (kHi + 1 < NProfR && out.ok[kHi + 1] && out.chi2[kHi + 1] - out.chi2Min < 1.f) { + ++kHi; + } + out.halfWidth = 0.5f * (kHi - kLo) + 0.5f; + const bool edge = (out.iMin == 0 || !out.ok[out.iMin - 1]) || (out.iMin == NProfR - 1 || !out.ok[out.iMin + 1]); + bool other = false; + for (int k = 1; k < NProfR - 1 && !other; ++k) { + if (k >= kLo && k <= kHi) { + continue; + } + if (out.ok[k] && out.ok[k - 1] && out.ok[k + 1] && out.chi2[k] <= out.chi2[k - 1] && out.chi2[k] <= out.chi2[k + 1] && out.chi2[k] - out.chi2Min < 1.f) { + other = true; + } + } + out.status = edge ? 3 : (other ? 2 : (out.halfWidth > 5.f ? 1 : 0)); // o2-linter: disable=magic-number (broad: half width above 5 cm) +} + +} // namespace o2::pwgem::photonmeson + +#endif // PWGEM_PHOTONMESON_UTILS_CONVERSIONPOINTPROFILE_H_ diff --git a/PWGEM/PhotonMeson/Utils/EventHistograms.h b/PWGEM/PhotonMeson/Utils/EventHistograms.h deleted file mode 100644 index f9f9fcdec0b..00000000000 --- a/PWGEM/PhotonMeson/Utils/EventHistograms.h +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file EventHistograms.h -/// \brief header file for event histograms -/// \author daiki.sekihata@cern.ch - -#ifndef PWGEM_PHOTONMESON_UTILS_EVENTHISTOGRAMS_H_ -#define PWGEM_PHOTONMESON_UTILS_EVENTHISTOGRAMS_H_ - -#include "Common/CCDB/EventSelectionParams.h" -#include "Common/CCDB/TriggerAliases.h" - -#include -#include -#include - -#include - -#include -#include - -namespace o2::aod::pwgem::photonmeson::utils::eventhistogram -{ -inline void addEventHistograms(o2::framework::HistogramRegistry* fRegistry, bool useWeight = false) -{ - // event info - auto hCollisionCounter = fRegistry->add("Event/before/hCollisionCounter", "collision counter;;Number of events", o2::framework::HistType::kTH1D, {{12, 0.5, 12.5}}, useWeight); - hCollisionCounter->GetXaxis()->SetBinLabel(1, "all"); - hCollisionCounter->GetXaxis()->SetBinLabel(2, "No TF border"); - hCollisionCounter->GetXaxis()->SetBinLabel(3, "No ITS ROF border"); - hCollisionCounter->GetXaxis()->SetBinLabel(4, "No Same Bunch Pileup"); - hCollisionCounter->GetXaxis()->SetBinLabel(5, "Is Vertex ITSTPC"); - hCollisionCounter->GetXaxis()->SetBinLabel(6, "Is Good Zvtx FT0vsPV"); - hCollisionCounter->GetXaxis()->SetBinLabel(7, "FT0AND"); - hCollisionCounter->GetXaxis()->SetBinLabel(8, "sel8"); - hCollisionCounter->GetXaxis()->SetBinLabel(9, "|Z_{vtx}| < 10 cm"); - hCollisionCounter->GetXaxis()->SetBinLabel(10, "EMC MB Readout"); - hCollisionCounter->GetXaxis()->SetBinLabel(11, "EMC L0 Triggered"); - hCollisionCounter->GetXaxis()->SetBinLabel(12, "accepted"); - - fRegistry->add("Event/before/hZvtx", "vertex z; Z_{vtx} (cm)", o2::framework::HistType::kTH1D, {{220, -11, +11}}, useWeight); - fRegistry->add("Event/before/hMultNTracksPV", "hMultNTracksPV; N_{track} to PV", o2::framework::HistType::kTH1F, {{6001, -0.5, 6000.5}}, useWeight); - fRegistry->add("Event/before/hMultNTracksPVeta1", "hMultNTracksPVeta1; N_{track} to PV", o2::framework::HistType::kTH1F, {{6001, -0.5, 6000.5}}, useWeight); - fRegistry->add("Event/before/hMultFT0", "hMultFT0;mult. FT0A;mult. FT0C", o2::framework::HistType::kTH2F, {{200, 0, 200000}, {60, 0, 60000}}, useWeight); - fRegistry->add("Event/before/hCentFT0A", "hCentFT0A;centrality FT0A (%)", o2::framework::HistType::kTH1F, {{100, 0., 100.}}, useWeight); - fRegistry->add("Event/before/hCentFT0C", "hCentFT0C;centrality FT0C (%)", o2::framework::HistType::kTH1F, {{100, 0., 100.}}, useWeight); - fRegistry->add("Event/before/hCentFT0M", "hCentFT0M;centrality FT0M (%)", o2::framework::HistType::kTH1F, {{100, 0., 100.}}, useWeight); - fRegistry->add("Event/before/hCentFT0CvsMultNTracksPV", "hCentFT0CvsMultNTracksPV;centrality FT0C (%);N_{track} to PV", o2::framework::HistType::kTH2F, {{110, 0, 110}, {500, 0, 5000}}, useWeight); - fRegistry->add("Event/before/hMultFT0CvsMultNTracksPV", "hMultFT0CvsMultNTracksPV;mult. FT0C;N_{track} to PV", o2::framework::HistType::kTH2F, {{60, 0, 60000}, {500, 0, 5000}}, useWeight); - fRegistry->add("Event/before/hMultFT0CvsOccupancy", "hMultFT0CvsOccupancy;mult. FT0C;N_{track} in time range", o2::framework::HistType::kTH2F, {{60, 0, 60000}, {500, 0, 10000}}, useWeight); - fRegistry->addClone("Event/before/", "Event/after/"); -} - -template -void fillEventInfo(o2::framework::HistogramRegistry* fRegistry, TCollision const& collision, float weight = 1.) -{ - const float maxZ = 10.f; - static constexpr std::array EventTypes = {"before/", "after/"}; - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCollisionCounter"), 1.0, weight); - if (collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCollisionCounter"), 2.0, weight); - } - if (collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCollisionCounter"), 3.0, weight); - } - if (collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCollisionCounter"), 4.0, weight); - } - if (collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCollisionCounter"), 5.0, weight); - } - if (collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCollisionCounter"), 6.0, weight); - } - if (collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCollisionCounter"), 7.0, weight); - } - if (collision.sel8()) { - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCollisionCounter"), 8.0, weight); - } - if (std::abs(collision.posZ()) < maxZ) { - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCollisionCounter"), 9.0, weight); - } - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hZvtx"), collision.posZ(), weight); - if (collision.alias_bit(kTVXinEMC)) { - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCollisionCounter"), 10.0, weight); - } - if (collision.alias_bit(kEMC7) || collision.alias_bit(kDMC7)) { - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCollisionCounter"), 11.0, weight); - } - - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hMultNTracksPV"), collision.multNTracksPV(), weight); - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hMultNTracksPVeta1"), collision.multNTracksPVeta1(), weight); - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hMultFT0"), collision.multFT0A(), collision.multFT0C(), weight); - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCentFT0A"), collision.centFT0A(), weight); - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCentFT0C"), collision.centFT0C(), weight); - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCentFT0M"), collision.centFT0M(), weight); - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCentFT0CvsMultNTracksPV"), collision.centFT0C(), collision.multNTracksPV(), weight); - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hMultFT0CvsMultNTracksPV"), collision.multFT0C(), collision.multNTracksPV(), weight); - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hMultFT0CvsOccupancy"), collision.multFT0C(), collision.trackOccupancyInTimeRange(), weight); -} - -} // namespace o2::aod::pwgem::photonmeson::utils::eventhistogram - -#endif // PWGEM_PHOTONMESON_UTILS_EVENTHISTOGRAMS_H_ diff --git a/PWGEM/PhotonMeson/Utils/EventUtilities.h b/PWGEM/PhotonMeson/Utils/EventUtilities.h new file mode 100644 index 00000000000..bffd220a945 --- /dev/null +++ b/PWGEM/PhotonMeson/Utils/EventUtilities.h @@ -0,0 +1,178 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file EventUtilities.h +/// \brief Utilities for event level variables (e.g. Q vectors) used in PWGEM/PhotonMeson +/// \author M. Hemmer, marvin.hemmer@cern.ch + +#ifndef PWGEM_PHOTONMESON_UTILS_EVENTUTILITIES_H_ +#define PWGEM_PHOTONMESON_UTILS_EVENTUTILITIES_H_ + +#include +#include + +#include +#include +#include +#include + +namespace o2::aod::pwgem::pm::utils::event +{ + +/// Detectors that provide a Q vector in the EMEventsQvec tables +enum QvecEstimator { + FT0M = 0, + FT0A = 1, + FT0C = 2, + TPCPos = 3, + TPCNeg = 4, + TPCTot = 5, + FV0A = 6 +}; + +/// Supported flow harmonics +enum Harmonics { + kNone = 0, + kDirect = 1, + kElliptic = 2, + kTriangluar = 3, + kQuadrangular = 4, + kPentagonal = 5, + kHexagonal = 6, + kHeptagonal = 7, + kOctagonal = 8 +}; + +/// Value returned for Q vectors that are not available +constexpr float InvalidQvec = -999.f; + +// Compile-time checks for the availability of Q vector columns. +// EMEventsQvec_000 has q2 and q3 without FV0A, EMEventsQvec_001 has q2 and q3 with FV0A, +// EMEventsQvec2 has only q2 (with FV0A) and EMEventsQvec3 has only q3 (with FV0A). +template +concept HasQvec2 = requires(T const& c) { c.q2xft0m(); }; + +template +concept HasQvec3 = requires(T const& c) { c.q3xft0m(); }; + +template +concept HasQvec2FV0A = requires(T const& c) { c.q2xfv0a(); }; + +template +concept HasQvec3FV0A = requires(T const& c) { c.q3xfv0a(); }; + +template +concept HasQvec = HasQvec2 || HasQvec3; + +// Compile-time checks for the availability of the occupancy columns (evsel::NumTracksInTimeRange and evsel::SumAmpFT0CInTimeRange) +template +concept HasTrackOccupancy = requires(T const& c) { c.trackOccupancyInTimeRange(); }; + +template +concept HasFT0COccupancy = requires(T const& c) { c.ft0cOccupancyInTimeRange(); }; + +/// \brief Get the Q vector of a given detector and harmonic +/// \param collision collision with the Q vector information +/// \param detector detector used for the Q vector (see QvecEstimator) +/// \param harmonic harmonic of the Q vector (see Harmonics) +/// \return pair of (Qx, Qy). {InvalidQvec, InvalidQvec} if not available in the table or for an unsupported harmonic +template +std::pair getQvec(TCollision const& collision, int detector, int harmonic) +{ + if (harmonic == kElliptic) { + if constexpr (HasQvec2) { + switch (detector) { + case QvecEstimator::FT0M: + return {collision.q2xft0m(), collision.q2yft0m()}; + case QvecEstimator::FT0A: + return {collision.q2xft0a(), collision.q2yft0a()}; + case QvecEstimator::FT0C: + return {collision.q2xft0c(), collision.q2yft0c()}; + case QvecEstimator::TPCPos: + return {collision.q2xbpos(), collision.q2ybpos()}; + case QvecEstimator::TPCNeg: + return {collision.q2xbneg(), collision.q2ybneg()}; + case QvecEstimator::TPCTot: + return {collision.q2xbtot(), collision.q2ybtot()}; + case QvecEstimator::FV0A: + if constexpr (HasQvec2FV0A) { + return {collision.q2xfv0a(), collision.q2yfv0a()}; + } else { + return {InvalidQvec, InvalidQvec}; + } + default: + LOG(warning) << "Q vector estimator not valid. Falling back to FT0M"; + return {collision.q2xft0m(), collision.q2yft0m()}; + } + } + } else if (harmonic == kTriangluar) { + if constexpr (HasQvec3) { + switch (detector) { + case QvecEstimator::FT0M: + return {collision.q3xft0m(), collision.q3yft0m()}; + case QvecEstimator::FT0A: + return {collision.q3xft0a(), collision.q3yft0a()}; + case QvecEstimator::FT0C: + return {collision.q3xft0c(), collision.q3yft0c()}; + case QvecEstimator::TPCPos: + return {collision.q3xbpos(), collision.q3ybpos()}; + case QvecEstimator::TPCNeg: + return {collision.q3xbneg(), collision.q3ybneg()}; + case QvecEstimator::TPCTot: + return {collision.q3xbtot(), collision.q3ybtot()}; + case QvecEstimator::FV0A: + if constexpr (HasQvec3FV0A) { + return {collision.q3xfv0a(), collision.q3yfv0a()}; + } else { + return {InvalidQvec, InvalidQvec}; + } + default: + LOG(warning) << "Q vector estimator not valid. Falling back to FT0M"; + return {collision.q3xft0m(), collision.q3yft0m()}; + } + } + } + return {InvalidQvec, InvalidQvec}; +} + +/// \brief Check if all given Q vector components are within a reasonable range +/// \param qvecs Q vector components (e.g. {Qx_1, Qy_1, Qx_2, Qy_2, ...}) +/// \param maxQvec maximum allowed absolute value of each component +inline bool isQvecGood(std::span qvecs, float maxQvec) +{ + for (const auto& qvec : qvecs) { + if (std::fabs(qvec) > maxQvec) { + return false; + } + } + return true; +} + +/// \brief Check if the Q vectors of all given detectors are within a reasonable range +/// \param collision collision with the Q vector information +/// \param detectors detectors to check (see QvecEstimator) +/// \param harmonic harmonic of the Q vectors (see Harmonics) +/// \param maxQvec maximum allowed absolute value of Qx and Qy +template +bool isQvecGood(TCollision const& collision, std::vector const& detectors, int harmonic, float maxQvec) +{ + for (const auto& detector : detectors) { + auto [xQvec, yQvec] = getQvec(collision, detector, harmonic); + if (std::fabs(xQvec) > maxQvec || std::fabs(yQvec) > maxQvec) { + return false; + } + } + return true; +} + +} // namespace o2::aod::pwgem::pm::utils::event + +#endif // PWGEM_PHOTONMESON_UTILS_EVENTUTILITIES_H_ diff --git a/PWGEM/PhotonMeson/Utils/MCUtilities.h b/PWGEM/PhotonMeson/Utils/MCUtilities.h index 270d6d2ead9..1326f513aef 100644 --- a/PWGEM/PhotonMeson/Utils/MCUtilities.h +++ b/PWGEM/PhotonMeson/Utils/MCUtilities.h @@ -32,12 +32,16 @@ #include #include #include +#include #include //_______________________________________________________________________ namespace o2::aod::pwgem::photonmeson::utils::mcutil { +template +concept is_unfiltered_iterator = o2::soa::is_iterator && !o2::soa::has_filtered_policy; + constexpr float kVertexEps = 1e-4f; // cm template @@ -1012,6 +1016,66 @@ float trueOpeningAngle(TMCParticles const& mcParticles, const int id1, const int return std::acos(std::clamp(c, -1.f, 1.f)); } +//_______________________________________________________________________ +/// \brief Whether the PDG code is a quark, gluon, diquark or string/cluster, i.e. where the decay chain ends +[[nodiscard]] inline bool isPartonLike(int pdg) +{ + const int absPdg = std::abs(pdg); + const bool isDiquark = absPdg > 1000 && absPdg < 6000 && (absPdg / 10) % 10 == 0; // o2-linter: disable=magic-number + return (absPdg >= PDG_t::kDown && absPdg <= PDG_t::kTop) || absPdg == PDG_t::kGluon || absPdg == 91 || absPdg == 92 || isDiquark; // o2-linter: disable=magic-number +} + +//_______________________________________________________________________ +/// \brief Whether the PDG code is a nucleus (10LZZZAAAI), e.g. one of the colliding beam nuclei +[[nodiscard]] inline bool isNucleus(int pdg) +{ + constexpr int NucleusOffset = 1000000000; + return std::abs(pdg) >= NucleusOffset; +} + +//_______________________________________________________________________ +/// \brief Fill the ancestor chain of a particle, starting with the particle itself. +/// Ends with the first parton-like ancestor (included), a particle without mothers, or when chain is full. +/// A nucleus (the colliding beam nuclei) is never part of the chain, otherwise all particles of an event would share it. +/// Unused entries of chain are set to -1. +/// \param mcParticle iterator of the full (unsliced) MC particle table. !WILL BE CHANGED! +/// Afterwards it points to the last chain element, or to the nucleus that ended the chain. +/// \param startId global index of the particle of interest +/// \param chain output, global indices, closest first +/// \return number of filled entries +template +int getAncestorChain(TMCParticle& mcParticle, int startId, std::span chain) +{ + int length = 0; + int id = startId; + while (id >= 0 && length < std::ssize(chain)) { + mcParticle.setCursor(id); + if (isNucleus(mcParticle.pdgCode())) { + break; // colliding nuclei are not an ancestor + } + chain[length++] = id; + if (isPartonLike(mcParticle.pdgCode()) || !mcParticle.has_mothers()) { + break; + } + id = mcParticle.mothersIds()[0]; + } + std::ranges::fill(chain.subspan(length), -1); + return length; +} + +//_______________________________________________________________________ +/// \brief Find the closest common ancestor of two ancestor chains +/// \param chain1 ancestor chain of first particle (particle itself first, may be padded with -1) +/// \param chain2 ancestor chain of second particle (particle itself first, may be padded with -1) +/// \return global index of the closest common ancestor, -1 if none found +[[nodiscard]] inline int getFirstCommonAncestor(std::span chain1, std::span chain2) +{ + chain1 = chain1.first(std::ranges::distance(chain1.begin(), std::ranges::find(chain1, -1))); + chain2 = chain2.first(std::ranges::distance(chain2.begin(), std::ranges::find(chain2, -1))); + const auto it = std::ranges::find_first_of(chain1, chain2); + return it != chain1.end() ? *it : -1; +} + } // namespace o2::aod::pwgem::photonmeson::utils::mcutil #endif // PWGEM_PHOTONMESON_UTILS_MCUTILITIES_H_ diff --git a/PWGEM/PhotonMeson/Utils/NMHistograms.h b/PWGEM/PhotonMeson/Utils/NMHistograms.h index ac361d9783e..d7c87380417 100644 --- a/PWGEM/PhotonMeson/Utils/NMHistograms.h +++ b/PWGEM/PhotonMeson/Utils/NMHistograms.h @@ -18,6 +18,7 @@ #include "PWGEM/PhotonMeson/Utils/MCUtilities.h" +#include #include #include #include @@ -28,11 +29,27 @@ #include #include +#include #include namespace o2::aod::pwgem::photonmeson::utils::nmhistogram { -inline void addNMHistograms(o2::framework::HistogramRegistry* fRegistry, bool isMC, const char* pairname = "#gamma#gamma") +/// \brief Add the pair histograms +/// \param isMC book the MC truth histograms (only mass and pT axes) instead of the same/mixed event histograms +/// \param pairname name of the pair used in the axis titles +/// \param sparseFullAxes data only: add EP, occupancy and z-vertex axes to the same/mixed event histograms +/// \param occBins data only: occupancy bin edges +/// \param epBins data only: event plane bin edges +/// \param vtxBins data only: z-vertex bin edges +inline void addNMHistograms(o2::framework::HistogramRegistry* fRegistry, + bool isMC, + const char* pairname = "#gamma#gamma", + bool sparseFullAxes = false, + std::vector const& occBins = {0, 500, 1000, 2000, 3000, 5000, 10000}, + std::vector const& epBins = {-o2::constants::math::PIHalf, -o2::constants::math::PIQuarter, 0, o2::constants::math::PIQuarter, o2::constants::math::PIHalf}, + std::vector const& vtxBins = {-10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10}, + const int nBinsMass = 400, + const std::array massBinRange = {0., 0.8}) { // !!Don't change pt,eta,y binning. These binnings have to be consistent with binned data at skimming.!! std::vector ptbins; @@ -50,7 +67,10 @@ inline void addNMHistograms(o2::framework::HistogramRegistry* fRegistry, bool is ptbins.emplace_back(1.0 * (i - 61) + 10.0); // from 10 to 20 GeV/c, every 1 GeV/c } const o2::framework::AxisSpec axis_pt{ptbins, Form("p_{T,%s} (GeV/c)", pairname)}; - const o2::framework::AxisSpec axis_mass{400, 0, 0.8, Form("m_{%s} (GeV/c^{2})", pairname)}; + const o2::framework::AxisSpec axis_mass{nBinsMass, massBinRange[0], massBinRange[1], Form("m_{%s} (GeV/c^{2})", pairname)}; + const o2::framework::AxisSpec axis_occ{occBins, "occupancy"}; + const o2::framework::AxisSpec axis_ep{epBins, "#Psi_{2} (rad)"}; + const o2::framework::AxisSpec axis_vtx{vtxBins, "Z_{vtx} (cm)"}; if (isMC) { fRegistry->add("Pair/Pi0/hs_Primary", "rec. true pi0", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt}, true); @@ -74,7 +94,15 @@ inline void addNMHistograms(o2::framework::HistogramRegistry* fRegistry, bool is fRegistry->get(HIST("Generated/Eta/hPtY"))->SetXTitle("p_{T} (GeV/c)"); fRegistry->get(HIST("Generated/Eta/hPtY"))->SetYTitle("rapidity |y|"); } else { - fRegistry->add("Pair/same/hs", "diphoton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt}, true); + if (sparseFullAxes) { + fRegistry->add("Pair/same/hs", "diphoton", + o2::framework::HistType::kTHnSparseD, + {axis_mass, axis_pt, axis_ep, axis_occ, axis_vtx}, true); + } else { + fRegistry->add("Pair/same/hs", "diphoton", + o2::framework::HistType::kTHnSparseD, + {axis_mass, axis_pt}, true); + } fRegistry->addClone("Pair/same/", "Pair/mix/"); } } diff --git a/PWGEM/PhotonMeson/Utils/PCMUtilities.h b/PWGEM/PhotonMeson/Utils/PCMUtilities.h index e69703a3ec5..f8f6daed325 100644 --- a/PWGEM/PhotonMeson/Utils/PCMUtilities.h +++ b/PWGEM/PhotonMeson/Utils/PCMUtilities.h @@ -21,12 +21,18 @@ #include #include +#include #include +#include #include +#include #include #include // IWYU pragma: keep (for rotate) -#include // IWYU pragma: keep (do not replace with Math/Vector2Dfwd.h) +#include +#include +#include +#include // IWYU pragma: keep (do not replace with Math/Vector2Dfwd.h) #include #include @@ -37,12 +43,22 @@ #include //_______________________________________________________________________ +/// \brief Armenteros-Podolanski cut of photon conversions: (alpha, qT) inside the ellipse with half axes alpha_max and qt_max +/// \param alpha Armenteros alpha +/// \param qt Armenteros qT (GeV/c) +/// \param alpha_max half axis in alpha +/// \param qt_max half axis in qT (GeV/c) +/// \return true if inside the ellipse inline bool checkAP(const float alpha, const float qt, const float alpha_max = 0.95, const float qt_max = 0.05) { float ellipse = std::pow(alpha / alpha_max, 2) + std::pow(qt / qt_max, 2); return (ellipse < 1.0); } //_______________________________________________________________________ +/// \brief Armenteros alpha of a V0: (pL+ - pL-) / (pL+ + pL-), pL = momentum of a leg along the V0 momentum +/// \param pxpos px of the positive leg (GeV/c), same for pypos, pzpos +/// \param pxneg px of the negative leg (GeV/c), same for pyneg, pzneg +/// \return alpha inline float v0Alpha(float pxpos, float pypos, float pzpos, float pxneg, float pyneg, float pzneg) { float momTot = RecoDecay::p(pxpos + pxneg, pypos + pyneg, pzpos + pzneg); @@ -51,6 +67,10 @@ inline float v0Alpha(float pxpos, float pypos, float pzpos, float pxneg, float p return (lQlPos - lQlNeg) / (lQlPos + lQlNeg); // longitudinal momentum asymmetry of v0 } //_______________________________________________________________________ +/// \brief Armenteros qT of a V0: momentum of the negative leg transverse to the V0 momentum +/// \param pxpos px of the positive leg (GeV/c), same for pypos, pzpos +/// \param pxneg px of the negative leg (GeV/c), same for pyneg, pzneg +/// \return qT (GeV/c) inline float v0Qt(float pxpos, float pypos, float pzpos, float pxneg, float pyneg, float pzneg) { float momTot = RecoDecay::p2(pxpos + pxneg, pypos + pyneg, pzpos + pzneg); @@ -58,6 +78,13 @@ inline float v0Qt(float pxpos, float pypos, float pzpos, float pxneg, float pyne return std::sqrt(RecoDecay::p2(pxneg, pyneg, pzneg) - dp * dp / momTot); // qt of v0 } //_______________________________________________________________________ +/// \brief conversion point of two legs (Run 2 recipe): xy = touching point of the two helix circles (centres weighted with the +/// radius of the other circle), z = radius-weighted mean of the leg z after propagating each leg to its point closest to it +/// \param prop propagator (also gives the nominal field) +/// \param trackPosInformation positive leg +/// \param trackNegInformation negative leg +/// \param xyz output: conversion point (cm) +/// \param matCorr material correction of the propagation template inline void Vtx_recalculationParCov(o2::base::Propagator* prop, const o2::track::TrackParametrizationWithError& trackPosInformation, const o2::track::TrackParametrizationWithError& trackNegInformation, std::array& xyz, o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE) { @@ -187,6 +214,12 @@ inline std::array getPropMomentumFromTrackHelix(const float s, const T } //_______________________________________________________________________ +/// \brief conversion point of two legs from their track rows (Vtx_recalculationParCov) +/// \param prop propagator (also gives the nominal field) +/// \param lTrackPos positive leg +/// \param lTrackNeg negative leg +/// \param xyz output: conversion point (cm) +/// \param matCorr material correction of the propagation template inline void Vtx_recalculation(o2::base::Propagator* prop, T1 lTrackPos, T2 lTrackNeg, std::array& xyz, o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE) { @@ -262,6 +295,11 @@ inline V0TruthClass classifyV0Truth(TTrack const& pos, TTrack const& ele, TMCPar return kV0OtherFake; } +//_______________________________________________________________________ +/// \brief electron-PID compatibility of the two legs: mean |n sigma_e| + 2 * (n sigma_e+ + n sigma_e-) in the TPC +/// \param pos positive leg +/// \param ele negative leg +/// \return kappa template float getV0Kappa(TLeg const& pos, TLeg const& ele) { @@ -269,4 +307,139 @@ float getV0Kappa(TLeg const& pos, TLeg const& ele) return kappa; } +//_______________________________________________________________________ +/// \brief gap between the helix circles of two legs in xy +/// \param cPos circle of the positive leg +/// \param cEle circle of the negative leg +/// \return distance of the centres minus the sum of the radii (cm); < 0: the circles overlap +inline float circleGap(o2::track::TrackAuxPar const& cPos, o2::track::TrackAuxPar const& cEle) +{ + return std::hypot(cEle.xC - cPos.xC, cEle.yC - cPos.yC) - (cPos.rC + cEle.rC); +} + +/// \brief return value of tpcDriftSide if the side cannot be determined +constexpr int TPCSideUndetermined = 2; + +//_______________________________________________________________________ +/// \brief TPC side in which a track drifted, from the TPC side flags, else from z, tgl and the time asymmetry +/// \param track track row (flags, z, tgl, tpcDeltaTFwd, tpcDeltaTBwd) +/// \return 1 A side, -1 C side, 0 both sides (crosses the central electrode), TPCSideUndetermined +template +inline int tpcDriftSide(TTrack const& track) +{ + constexpr float MaxCECrossingDeltaTNS = 1000.f; + const auto sides = track.flags() & (o2::aod::track::TPCSideA | o2::aod::track::TPCSideC); + if (sides == o2::aod::track::TPCSideA) { + return 1; + } + if (sides == o2::aod::track::TPCSideC) { + return -1; + } + if (sides != 0) { + return 0; + } + const float dFwd = track.tpcDeltaTFwd(); + const float dBwd = track.tpcDeltaTBwd(); + if (dFwd == dBwd && dFwd < MaxCECrossingDeltaTNS) { + return 0; + } + const bool zPositive = track.z() > 0.f; + const bool tglPositive = track.tgl() > 0.f; + if (zPositive == tglPositive) { + return tglPositive ? 1 : -1; + } + if (dBwd == 0.f && dFwd > 0.f) { + return zPositive ? 1 : -1; + } + if (dBwd > 0.f) { + return tglPositive ? 1 : -1; + } + return TPCSideUndetermined; +} + +/// \brief z and momentum of a track helix at a point of its circle (helixPointAt) +struct HelixPoint { + bool ok = false; + bool inward = false; + float z = 0.f; + std::array p{}; +}; + +//_______________________________________________________________________ +/// \brief z and momentum of a track helix at an xy point of its circle +/// \param par track parameters +/// \param xP x of the point on the circle (cm) +/// \param yP y of the point on the circle (cm) +/// \param xB x of the beam position (cm) +/// \param yB y of the beam position (cm) +/// \param bz magnetic field (kG) +/// \return ok = false for a degenerate circle; inward = the track moves towards the beam at the point (more than 10 cm from +/// the beam), then the momentum is reversed; z and momentum at the point +inline HelixPoint helixPointAt(o2::track::TrackPar const& par, const float xP, const float yP, const float xB, const float yB, const float bz) +{ + HelixPoint h; + const o2::track::TrackAuxPar c(par, bz); + std::array r{}, p{}; + par.getXYZGlo(r); + par.getPxPyPzGlo(p); + const float pt = std::hypot(p[0], p[1]); + if (!(c.rC > 0.f) || !(pt > 0.f)) { + return h; + } + const float sense = (r[0] - c.xC) * p[1] - (r[1] - c.yC) * p[0] > 0.f ? 1.f : -1.f; + float back = RecoDecay::constrainAngle(sense * (std::atan2(r[1] - c.yC, r[0] - c.xC) - std::atan2(yP - c.yC, xP - c.xC)), 0.f); + const float tx = -sense * (yP - c.yC) / c.rC; + const float ty = sense * (xP - c.xC) / c.rC; + constexpr float MinRadiusOrientation = 10.f; + h.inward = std::hypot(xP - xB, yP - yB) > MinRadiusOrientation && tx * (xP - xB) + ty * (yP - yB) < 0.f; + if (!h.inward) { + if (back > o2::constants::math::PI && (r[0] - xB) * p[0] + (r[1] - yB) * p[1] > 0.f) { + back -= o2::constants::math::TwoPI; // o2-linter: disable=two-pi-add-subtract (signed arc length to a point behind the track, not an angle range) + } + h.z = r[2] - par.getTgl() * c.rC * back; + h.p = {pt * tx, pt * ty, pt * par.getTgl()}; + } else { + h.z = r[2] + par.getTgl() * c.rC * (back > 0.f ? o2::constants::math::TwoPI - back : 0.f); + h.p = {-pt * tx, -pt * ty, -pt * par.getTgl()}; + } + h.ok = true; + return h; +} + +//_______________________________________________________________________ +/// \brief compatibility of two track pieces with one track +/// \param trA first track +/// \param trB second track +/// \param bz magnetic field (kG) +/// \return chi2 of (y, snp, tgl, q/pt) at a common x with the summed covariances (4 dof); -1 if the propagation or the inversion fails +template +inline float splitPieceChi2(TTrack const& trA, TTrack const& trB, const float bz) +{ + auto a = getTrackParCov(trA); + auto b = getTrackParCov(trB); + if (!a.rotate(b.getAlpha()) || !a.propagateTo(b.getX(), bz)) { + a = getTrackParCov(trA); + b = getTrackParCov(trB); + if (!b.rotate(a.getAlpha()) || !b.propagateTo(a.getX(), bz)) { + return -1.f; + } + } + ROOT::Math::SMatrix> c; + c(0, 0) = a.getSigmaY2() + b.getSigmaY2(); + c(1, 0) = a.getSigmaSnpY() + b.getSigmaSnpY(); + c(1, 1) = a.getSigmaSnp2() + b.getSigmaSnp2(); + c(2, 0) = a.getSigmaTglY() + b.getSigmaTglY(); + c(2, 1) = a.getSigmaTglSnp() + b.getSigmaTglSnp(); + c(2, 2) = a.getSigmaTgl2() + b.getSigmaTgl2(); + c(3, 0) = a.getSigma1PtY() + b.getSigma1PtY(); + c(3, 1) = a.getSigma1PtSnp() + b.getSigma1PtSnp(); + c(3, 2) = a.getSigma1PtTgl() + b.getSigma1PtTgl(); + c(3, 3) = a.getSigma1Pt2() + b.getSigma1Pt2(); + if (!c.Invert()) { + return -1.f; + } + const ROOT::Math::SVector d(a.getY() - b.getY(), a.getSnp() - b.getSnp(), a.getTgl() - b.getTgl(), a.getQ2Pt() - b.getQ2Pt()); + return static_cast(ROOT::Math::Similarity(c, d)); +} + #endif // PWGEM_PHOTONMESON_UTILS_PCMUTILITIES_H_ diff --git a/PWGEM/PhotonMeson/Utils/PairUtilities.h b/PWGEM/PhotonMeson/Utils/PairUtilities.h index e4deb542747..87c45f4ad99 100644 --- a/PWGEM/PhotonMeson/Utils/PairUtilities.h +++ b/PWGEM/PhotonMeson/Utils/PairUtilities.h @@ -21,9 +21,9 @@ #include #include -#include +#include // IWYU pragma: keep #include -#include +#include // IWYU pragma: keep #include #include diff --git a/PWGEM/PhotonMeson/Utils/V0DedupUtilities.h b/PWGEM/PhotonMeson/Utils/V0DedupUtilities.h new file mode 100644 index 00000000000..757d86b5a86 --- /dev/null +++ b/PWGEM/PhotonMeson/Utils/V0DedupUtilities.h @@ -0,0 +1,309 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file V0DedupUtilities.h +/// \brief helpers of the photon conversion builder: conflict-group solver of the deduplication and the geometry of the photon finder +/// \author Stefanie Mrozinski, stefanie.mrozinski@cern.ch + +#ifndef PWGEM_PHOTONMESON_UTILS_V0DEDUPUTILITIES_H_ +#define PWGEM_PHOTONMESON_UTILS_V0DEDUPUTILITIES_H_ + +#include "PWGEM/PhotonMeson/Utils/PCMUtilities.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace o2::pwgem::photonmeson +{ + +//_______________________________________________________________________ +/// \brief q_inv of two massless particles (photons) +/// \param px1 px of the first photon (GeV/c), same for py1, pz1 +/// \param px2 px of the second photon (GeV/c), same for py2, pz2 +/// \return sqrt(2 (|p1| |p2| - p1 . p2)) (GeV/c) +inline float qInv(const float px1, const float py1, const float pz1, const float px2, const float py2, const float pz2) +{ + return std::sqrt(std::max(0.f, 2.f * (std::hypot(px1, py1, pz1) * std::hypot(px2, py2, pz2) - px1 * px2 - py1 * py2 - pz1 * pz2))); +} + +/// \brief result of solveConflictGroup +struct ConflictGroupSolution { + uint32_t bestMask = 0; + int bestCount = 0; + float bestScore = 0.f; + std::vector bestWithCount; // per candidate: size of the best subset that contains it + std::vector bestWithScore; // per candidate: score sum of that subset +}; + +//_______________________________________________________________________ +/// \brief best leg-disjoint subset of a conflict group of the deduplication (mode 2), exact enumeration of all subsets +/// \param legMask per candidate: bit mask of its legs (group-local bits); at most 16 candidates +/// \param score per candidate: dedup score, lower = better +/// \param s0 objective: <= 0 maximum number of candidates, then minimum score sum; > 0 maximum sum of max(s0 - score, 1e-3), +/// ties by the score sum +/// \param memberBest also find, per candidate, the best subset that contains it (MC diagnostics) +/// \return mask of the best subset, its size and score sum, and the per-candidate results +inline ConflictGroupSolution solveConflictGroup(std::vector const& legMask, std::vector const& score, const float s0, const bool memberBest) +{ + const int nMembers = static_cast(legMask.size()); + const bool weighted = s0 > 0.f; + constexpr float MinWeight = 1e-3f; + constexpr float WeightTolerance = 1e-6f; + ConflictGroupSolution sol; + sol.bestWithCount.assign(nMembers, -1); + sol.bestWithScore.assign(nMembers, 0.f); + float bestWeight = -1.f; + for (uint32_t mask = 1; mask < (1u << nMembers); ++mask) { + uint64_t legs = 0; + float scoreSum = 0.f; + float weightSum = 0.f; + int count = 0; + bool disjoint = true; + for (int k = 0; k < nMembers; ++k) { + if ((mask & (1u << k)) == 0) { + continue; + } + if ((legs & legMask[k]) != 0) { + disjoint = false; + break; + } + legs |= legMask[k]; + scoreSum += score[k]; + weightSum += std::max(s0 - score[k], MinWeight); + ++count; + } + if (!disjoint) { + continue; + } + const bool better = weighted ? (weightSum > bestWeight + WeightTolerance || (std::fabs(weightSum - bestWeight) <= WeightTolerance && scoreSum < sol.bestScore)) + : (count > sol.bestCount || (count == sol.bestCount && scoreSum < sol.bestScore)); + if (better) { + sol.bestCount = count; + sol.bestScore = scoreSum; + bestWeight = weightSum; + sol.bestMask = mask; + } + if (memberBest) { + for (int k = 0; k < nMembers; ++k) { + if ((mask & (1u << k)) == 0) { + continue; + } + if (count > sol.bestWithCount[k] || (count == sol.bestWithCount[k] && scoreSum < sol.bestWithScore[k])) { + sol.bestWithCount[k] = count; + sol.bestWithScore[k] = scoreSum; + } + } + } + } + return sol; +} + +/// \brief steps of the photon finder (pairing of free legs at the touching point of their helix circles); evalFinderPair returns the last step passed +enum FinderStep { + kFindTglWindow = 0, + kFindTime, + kFindGap, + kFindRTouch, + kFindPointing, + kFindAlongMotion, + kFindZMatch, + kFindCollision, + kNFindStep +}; + +/// \brief collision for the photon finder: time (ns, relative to the first BC of the DF), index, vertex (cm) +struct FinderCollision { + double t = 0.; + int64_t id = -1; + float x = 0.f, y = 0.f, z = 0.f; +}; + +/// \brief leg for the photon finder: track parameters, helix circle, TPC drift side and the time window (ns) of the track +struct FinderLeg { + int64_t id = -1; + float tgl = 0.f; + bool noITS = false; + bool movable = false; + int side = 0; + double t = 0., tMin = 0., tMax = 0.; + o2::track::TrackPar par; + o2::track::TrackAuxPar circle; +}; + +/// \brief cuts of the photon finder (cm) and the magnetic field (kG) +struct FinderCuts { + float maxDeltaTgl = 0.f; + float maxCircleGap = 0.f; + float minRTPConly = 0.f; + float maxRReqITS = 0.f; + float rMargin = 0.f; + float maxR = 0.f; + float minCosXY = 0.f; + float maxDeltaZ = 0.f; + float maxDz = 0.f; + float bz = 0.f; +}; + +//_______________________________________________________________________ +/// \brief geometric test of one leg pair of the photon finder: tgl, time windows, circle gap, touching radius, xy pointing to the +/// beam, direction of motion, z match at the touching point (incl. the drift of TPC-only legs), collision by z +/// \param p positive leg +/// \param e negative leg +/// \param colls collisions of the DF, sorted by time +/// \param cuts cuts of the finder +/// \param xB x of the beam position (cm) +/// \param yB y of the beam position (cm) +/// \param vDrift TPC drift velocity (cm/ns) +/// \param colId output: index of the matched collision, -1 if none +/// \return last FinderStep passed, -1 if the tgl window fails; kFindCollision if a collision matches +inline int evalFinderPair(FinderLeg const& p, FinderLeg const& e, std::vector const& colls, FinderCuts const& cuts, const float xB, const float yB, const double vDrift, int64_t& colId) +{ + colId = -1; + if (std::fabs(p.tgl - e.tgl) > cuts.maxDeltaTgl) { + return -1; + } + const double tLo = std::max(p.tMin, e.tMin); + const double tHi = std::min(p.tMax, e.tMax); + if (tLo > tHi) { + return kFindTglWindow; + } + const auto& cp = p.circle; + const auto& ce = e.circle; + const float dxC = ce.xC - cp.xC; + const float dyC = ce.yC - cp.yC; + const float dC = std::hypot(dxC, dyC); + if (std::fabs(circleGap(cp, ce)) > cuts.maxCircleGap) { + return kFindTime; + } + const float xT = (cp.xC * ce.rC + ce.xC * cp.rC) / (cp.rC + ce.rC); + const float yT = (cp.yC * ce.rC + ce.yC * cp.rC) / (cp.rC + ce.rC); + const float rTouch = std::hypot(xT, yT); + const float rMin = (p.noITS && e.noITS ? cuts.minRTPConly : cuts.maxRReqITS) - cuts.rMargin; + if (rTouch > cuts.maxR || rTouch < rMin) { + return kFindGap; + } + const float dB = std::hypot(xT - xB, yT - yB); + if (!(dC > 0.f) || !(dB > 0.f) || std::fabs((xT - xB) * (-dyC / dC) + (yT - yB) * (dxC / dC)) / dB < cuts.minCosXY) { + return kFindRTouch; + } + auto nearest = [&](o2::track::TrackAuxPar const& c, float& x, float& y) { + const float d = std::max(std::hypot(xT - c.xC, yT - c.yC), 1e-6f); + x = c.xC + c.rC * (xT - c.xC) / d; + y = c.yC + c.rC * (yT - c.yC) / d; + }; + float xP = 0.f, yP = 0.f, xE = 0.f, yE = 0.f; + nearest(cp, xP, yP); + nearest(ce, xE, yE); + const auto hp = helixPointAt(p.par, xP, yP, xB, yB, cuts.bz); + const auto he = helixPointAt(e.par, xE, yE, xB, yB, cuts.bz); + if (!hp.ok || !he.ok || hp.inward || he.inward) { + return kFindPointing; + } + const double b = vDrift * (p.side - e.side); + const double a = (hp.z - p.side * vDrift * p.t) - (he.z - e.side * vDrift * e.t); + const double tBest = b == 0. ? tLo : std::clamp(-a / b, tLo, tHi); + if (std::fabs(a + b * tBest) > cuts.maxDeltaZ) { + return kFindAlongMotion; + } + const float ptG = std::hypot(hp.p[0] + he.p[0], hp.p[1] + he.p[1]); + const float tglG = ptG > 0.f ? (hp.p[2] + he.p[2]) / ptG : 0.f; + float best = std::numeric_limits::max(); + for (auto it = std::lower_bound(colls.begin(), colls.end(), tLo, [](const FinderCollision& c, double t) { return c.t < t; }); it != colls.end() && it->t <= tHi; ++it) { + const auto zPos = static_cast(hp.z + p.side * vDrift * (it->t - p.t)); + const auto zEle = static_cast(he.z + e.side * vDrift * (it->t - e.t)); + const float deltaZ = zPos - zEle; + const float dz = (zPos * ce.rC + zEle * cp.rC) / (cp.rC + ce.rC) - std::hypot(xT - it->x, yT - it->y) * tglG - it->z; + const float dist = std::hypot(dz, deltaZ); + if (dist < best && std::fabs(dz) < cuts.maxDz && std::fabs(deltaZ) < cuts.maxDeltaZ) { + best = dist; + colId = it->id; + } + } + return colId >= 0 ? kFindCollision : kFindZMatch; +} + +/// \brief input features of the finder purity score (naive-Bayes log-likelihood ratio, finderScore_v1, PbPb MC LHC26a5a) +enum FinderScoreFeature { + kScoreCosPA = 0, // 3D cosine of the pointing angle + kScorePCA, // distance of the legs at the conversion point (cm) + kScoreMee, // e+e- mass at the conversion point (GeV/c^2) + kScoreGap, // helix-circle gap of the legs (cm) + kScoreDTgl, // |tgl+ - tgl-| + kScorePsiPair, + kScoreMaxNSigmaEl, // max(|n sigma_e|) of the legs in the TPC + kScoreMinNSigmaPi, // min(|n sigma_pi|) of the legs in the TPC + kNScoreFeatures +}; + +/// \brief log-likelihood-ratio table of one feature: nBins uniform bins in [lo, hi), llr[0] underflow, llr[nBins + 1] overflow +/// (same bin lookup as TH1::FindBin) +struct FinderScoreTable { + int nBins; + double lo; + double hi; + const double* llr; +}; + +/// \brief log(p_true / p_fake) per bin of each feature (finderScore_v1) +inline constexpr std::array FinderLlrCosPA = {-0.42326231102291223, -3.0746401250262112, -2.902679468526141, -2.7737498628462491, -2.8653323764415854, -2.9977154358984874, -2.879287982624958, -2.9449559801189884, -2.8305718792936898, -2.7610493575753212, -2.6743115383729115, -2.739759837578176, -2.7445126982363175, -2.6010956990444813, -2.5377329273302194, -2.4413963521466235, -2.5007549142569676, -2.5484647707016048, -2.3339023465737707, -2.3706555897531327, -2.3350661347670365, -2.3987685637953868, -2.2562477335729185, -2.2430310349096305, -2.1429211398936303, -2.1233771903398493, -2.0022839108013391, -1.9558000169723206, -1.8729479034124974, -1.9065302845458256, -1.8542708039089619, -1.7230031706190478, -1.6490950919627059, -1.633800343346663, -1.5180730364736743, -1.389137677593709, -1.3181114339137541, -1.2615432549457208, -1.1021895055813662, -0.97913284177589832, -0.97669632321874755, -0.81070582095170196, -0.70030094035730539, -0.55130110346066707, -0.34392181466201621, -0.16444806113591265, 0.025741391801742678, 0.22392951134446418, 0.48449501950084528, 0.74029958080748082, 1.3544181524605163, 2.1416870464386246}; +inline constexpr std::array FinderLlrPca = {-0.42328563731423596, 0.74173300442294698, 0.60064005460866021, 0.48774214670698213, 0.43172212684872524, 0.34949695425445604, 0.2876013953788461, 0.13114334603545452, -0.013681195276045386, -0.10436433242742214, -0.27099684361934573, -0.38200720615175005, -0.47403001970996045, -0.57728096929145511, -0.65985610396473016, -0.75403841222733481, -0.76619973024014831, -0.76182156236311549, -0.84274340404963699, -0.93501651159467125, -0.99602554213200045, -1.029448537116822, -1.1240438482723172, -1.124554873172243, -1.2439733445571921, -1.2077440689826793, -1.3081144381420802, -1.3626265850153199, -1.3892300573742717, -1.3978783977548588, -1.4463232066948644, -0.42328563731423596}; +inline constexpr std::array FinderLlrMee = {-0.42329146929478079, 0.82940375143002754, 0.6225570701387575, 0.30541176737301567, -0.042962035242895512, -0.43745174446394713, -0.78901712806679281, -1.1834130413160895, -1.5486037315484942, -1.8446313857300292, -2.2387380284795446, -2.1811493868471543, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079}; +inline constexpr std::array FinderLlrGap = {-0.42329730143842487, -3.0127420263468552, -2.6803080781356927, -2.2310834132674926, -1.852433867664681, -1.4426351719772665, -1.1194240909923954, -0.72769983474283484, -0.24592785900768224, 0.40475570320106047, 1.4082550958014817, 1.1535415604659081, 0.43945228763843225, 0.14326171246634631, -0.19523294768817565, -0.39258756076294099, -0.41539680606824775, -0.42329730143842487, -0.42329730143842487, -0.42329730143842487, -0.42329730143842487, -0.42329730143842487}; +inline constexpr std::array FinderLlrDTgl = {-0.42330313374517559, 0.84513809550928687, 0.46735826574627454, 0.13082343003051436, -0.12182979016333238, -0.33846868362980387, -0.52696466906551476, -0.72450796222957359, -0.89155673129371438, -1.0310133083500654, -1.2327015991375558, -1.3726345840013925, -1.4519781151564646, -1.7004785855213946, -1.7559986132128569, -1.818203125662764, -0.42330313374517559}; +inline constexpr std::array FinderLlrPsiPair = {-0.42330196727077668, 1.5226081817845367, 0.42399589311642699, 1.6969615689293145, 1.6135799599902634, 0.44573587975283285, 0.50095693425255516, 0.43535965176674207, 0.0028547913604746104, -0.0059523726814127804, 0.61779960880440121, 0.82263503615419142, 0.81841116503800626, 0.99808371366038406, 0.57522686284035063, 0.80047346435133904, 2.521137011895664, 2.2392858597546761}; +inline constexpr std::array FinderLlrMaxNSigmaEl = {-0.42331129924865812, 0.58307082727859216, 0.52708190318359982, 0.41704446150850366, 0.16536503067868297, -0.38368187287820599, -1.4189814933894278, -3.0185512358113016, -5.190101150118414, -0.42331129924865812}; +inline constexpr std::array FinderLlrMinNSigmaPi = {-0.42330896621503916, -0.9609371208084968, -0.46431089172823747, -0.06050992822674324, 0.16077238768804142, 0.3920347340880036, 0.64255032840474002, 0.82818496003043784, 0.96447986209716363, 1.0700938177484094, 1.1082284119787249, 1.017209914244056}; + +/// \brief binning and LLR table of each feature, in the order of FinderScoreFeature +inline constexpr std::array FinderScoreTables = {{ + {50, 0.99000000953674316, 1, FinderLlrCosPA.data()}, + {30, 0, 3, FinderLlrPca.data()}, + {25, 0, 0.05000000074505806, FinderLlrMee.data()}, + {20, -5, 5, FinderLlrGap.data()}, + {15, 0, 0.15000000596046448, FinderLlrDTgl.data()}, + {16, -1.6000000238418579, 1.6000000238418579, FinderLlrPsiPair.data()}, + {8, 0, 4, FinderLlrMaxNSigmaEl.data()}, + {10, 0, 10, FinderLlrMinNSigmaPi.data()}, +}}; + +//_______________________________________________________________________ +/// \brief purity score of a finder candidate: log(p_true / p_fake) summed over the 8 features (naive Bayes) +/// \param x feature values in the order of FinderScoreFeature +/// \return score; > 0: more likely a true photon +inline float finderPurityScore(std::array const& x) +{ + float score = 0.f; + for (int i = 0; i < kNScoreFeatures; ++i) { + const auto& t = FinderScoreTables[i]; + const double v = x[i]; + int bin = 0; + if (v < t.lo) { + bin = 0; + } else if (!(v < t.hi)) { + bin = t.nBins + 1; + } else { + bin = 1 + static_cast(t.nBins * (v - t.lo) / (t.hi - t.lo)); + } + score += t.llr[bin]; + } + return score; +} + +} // namespace o2::pwgem::photonmeson + +#endif // PWGEM_PHOTONMESON_UTILS_V0DEDUPUTILITIES_H_ diff --git a/PWGEM/Tasks/phosCalibration.cxx b/PWGEM/Tasks/phosCalibration.cxx index 5e62addf7dc..0f244127a0e 100644 --- a/PWGEM/Tasks/phosCalibration.cxx +++ b/PWGEM/Tasks/phosCalibration.cxx @@ -44,6 +44,7 @@ #include #include #include +#include #include using namespace o2; diff --git a/PWGHF/ALICE3/TableProducer/CMakeLists.txt b/PWGHF/ALICE3/TableProducer/CMakeLists.txt index 5bbc40353ee..4aba14d7e30 100644 --- a/PWGHF/ALICE3/TableProducer/CMakeLists.txt +++ b/PWGHF/ALICE3/TableProducer/CMakeLists.txt @@ -13,16 +13,19 @@ o2physics_add_dpl_workflow(candidate-creator-chic SOURCES candidateCreatorChic.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-x SOURCES candidateCreatorX.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-xicc SOURCES candidateCreatorXicc.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter COMPONENT_NAME Analysis) @@ -30,46 +33,55 @@ o2physics_add_dpl_workflow(candidate-creator-xicc o2physics_add_dpl_workflow(candidate-selector-chic-to-jpsi-gamma SOURCES candidateSelectorChicToJpsiGamma.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-d0-alice3-barrel SOURCES candidateSelectorD0Alice3Barrel.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-d0-alice3-forward SOURCES candidateSelectorD0Alice3Forward.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-d0-parametrized-pid SOURCES candidateSelectorD0ParametrizedPid.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-jpsi SOURCES candidateSelectorJpsi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-lc-alice3 SOURCES candidateSelectorLcAlice3.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-lc-parametrized-pid SOURCES candidateSelectorLcParametrizedPid.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-x-to-jpsi-pi-pi SOURCES candidateSelectorXToJpsiPiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-xicc-to-p-k-pi-pi SOURCES candidateSelectorXiccToPKPiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -77,15 +89,18 @@ o2physics_add_dpl_workflow(candidate-selector-xicc-to-p-k-pi-pi o2physics_add_dpl_workflow(tree-creator-chic-to-jpsi-gamma SOURCES treeCreatorChicToJpsiGamma.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-x-to-jpsi-pi-pi SOURCES treeCreatorXToJpsiPiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-xicc-to-p-k-pi-pi SOURCES treeCreatorXiccToPKPiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) diff --git a/PWGHF/ALICE3/Tasks/CMakeLists.txt b/PWGHF/ALICE3/Tasks/CMakeLists.txt index c94c68a9587..3394d304c58 100644 --- a/PWGHF/ALICE3/Tasks/CMakeLists.txt +++ b/PWGHF/ALICE3/Tasks/CMakeLists.txt @@ -11,36 +11,43 @@ o2physics_add_dpl_workflow(task-chic SOURCES taskChic.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-d0-alice3-barrel SOURCES taskD0Alice3Barrel.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-d0-alice3-forward SOURCES taskD0Alice3Forward.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-d0-parametrized-pid SOURCES taskD0ParametrizedPid.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-jpsi SOURCES taskJpsi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-lc-alice3 SOURCES taskLcAlice3.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-lc-parametrized-pid SOURCES taskLcParametrizedPid.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -51,10 +58,12 @@ o2physics_add_dpl_workflow(task-qa-pid-rejection o2physics_add_dpl_workflow(task-x SOURCES taskX.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-xicc SOURCES taskXicc.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) diff --git a/PWGHF/Core/CMakeLists.txt b/PWGHF/Core/CMakeLists.txt index cb84e8bb00c..bd5c35a3f9b 100644 --- a/PWGHF/Core/CMakeLists.txt +++ b/PWGHF/Core/CMakeLists.txt @@ -12,3 +12,42 @@ o2physics_add_library(HfMlCore SOURCES HfMlResponse.cxx PUBLIC_LINK_LIBRARIES O2Physics::MLCore) + +# Precompiled header shared across PWGHF. Percentages are the share of the +# group's workflow translation units that include the header, measured over the tree. +# The D2H reduced-data-model workflows share this carrier too. +# +# runDataProcessing.h is deliberately NOT precompiled: it requires each workflow's +# customize() to be declared before it is included, so precompiling it silently +# unregisters every option (the binary aborts at --dump-workflow, but links fine). +# +# Skipped under recc, which caches compilations remotely instead. +add_library(HfCandidatesPCH OBJECT hfCandidatesPCH.cxx) +# The PCH compile is one of the heaviest translation units in the tree -- it +# instantiates the whole data model up front -- but add_library() does not put +# it in the 'analysis' job pool that o2physics_add_dpl_workflow() uses for the +# workflows. Left out, it runs unthrottled alongside the pooled compilations +# and defeats the per-job memory budget the pool exists to enforce. +set_property(TARGET HfCandidatesPCH PROPERTY JOB_POOL_COMPILE analysis) +target_link_libraries(HfCandidatesPCH PUBLIC O2::Framework O2Physics::AnalysisCore) +if(NOT DEFINED ENV{USE_RECC}) + target_precompile_headers(HfCandidatesPCH PRIVATE + # Framework baseline -- same set AnalysisPCH precompiles. A target with + # REUSE_FROM uses ONLY this PCH, so anything left out here is parsed from + # scratch by every consumer: without this block these workflows precompile + # LESS than the default carrier they opted out of. + + + + + + + + + + + [["PWGHF/DataModel/CandidateReconstructionTables.h"]] # 78%, 2479 lines + [["PWGHF/DataModel/CandidateSelectionTables.h"]] # 72% + [["Common/Core/RecoDecay.h"]] # 65%, 1523 lines + ) +endif() diff --git a/PWGHF/Core/HfHelper.h b/PWGHF/Core/HfHelper.h index e6299b483f8..155dfda35f2 100644 --- a/PWGHF/Core/HfHelper.h +++ b/PWGHF/Core/HfHelper.h @@ -39,6 +39,8 @@ concept IsB0ToDstarPiChannel = requires(T candidate) { }; struct HfHelper { + HfHelper() = delete; // Prevent instances. + // 2-prong // D0(bar) → π± K∓ diff --git a/PWGHF/Core/HfMlResponseBplusToD0Pi.h b/PWGHF/Core/HfMlResponseBplusToD0Pi.h index 9593101719e..3ca6e41e53e 100644 --- a/PWGHF/Core/HfMlResponseBplusToD0Pi.h +++ b/PWGHF/Core/HfMlResponseBplusToD0Pi.h @@ -29,10 +29,9 @@ // Fill the map of available input features // the key is the feature's name (std::string) // the value is the corresponding value in EnumInputFeatures -#define FILL_MAP_BPLUS(FEATURE) \ - { \ - #FEATURE, static_cast(InputFeaturesBplusToD0Pi::FEATURE) \ - } +#define FILL_MAP_BPLUS(FEATURE) \ + { \ + #FEATURE, static_cast(InputFeaturesBplusToD0Pi::FEATURE)} // Check if the index of mCachedIndices (index associated to a FEATURE) // matches the entry in EnumInputFeatures associated to this FEATURE @@ -89,6 +88,14 @@ enum class InputFeaturesBplusToD0Pi : uint8_t { decayLengthXYNormalised, cpa, cpaXY, + decayLengthDToPv, + decayLengthDToPvXY, + decayLengthDToB, + decayLengthDToBXY, + cpaDToPv, + cpaDToPvXY, + cpaDToB, + cpaDToBXY, maxNormalisedDeltaIP, prong0MlProbBkg, prong0MlProbPrompt, @@ -134,6 +141,14 @@ class HfMlResponseBplusToD0Pi : public HfMlResponse CHECK_AND_FILL_VEC_BPLUS(decayLengthXYNormalised); CHECK_AND_FILL_VEC_BPLUS(cpa); CHECK_AND_FILL_VEC_BPLUS(cpaXY); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToPv); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToPvXY); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToB); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToBXY); + CHECK_AND_FILL_VEC_BPLUS(cpaDToPv); + CHECK_AND_FILL_VEC_BPLUS(cpaDToPvXY); + CHECK_AND_FILL_VEC_BPLUS(cpaDToB); + CHECK_AND_FILL_VEC_BPLUS(cpaDToBXY); CHECK_AND_FILL_VEC_BPLUS(maxNormalisedDeltaIP); CHECK_AND_FILL_VEC_D0_INDEX(prong0MlProbBkg, mlProbD0, mlProbD0bar, 0); CHECK_AND_FILL_VEC_D0_INDEX(prong0MlProbPrompt, mlProbD0, mlProbD0bar, 1); @@ -159,6 +174,14 @@ class HfMlResponseBplusToD0Pi : public HfMlResponse CHECK_AND_FILL_VEC_BPLUS(decayLengthXYNormalised); CHECK_AND_FILL_VEC_BPLUS(cpa); CHECK_AND_FILL_VEC_BPLUS(cpaXY); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToPv); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToPvXY); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToB); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToBXY); + CHECK_AND_FILL_VEC_BPLUS(cpaDToPv); + CHECK_AND_FILL_VEC_BPLUS(cpaDToPvXY); + CHECK_AND_FILL_VEC_BPLUS(cpaDToB); + CHECK_AND_FILL_VEC_BPLUS(cpaDToBXY); CHECK_AND_FILL_VEC_BPLUS(maxNormalisedDeltaIP); // TPC PID variable CHECK_AND_FILL_VEC_BPLUS_FULL(prong1, tpcNSigmaPi1, tpcNSigmaPi); @@ -190,6 +213,14 @@ class HfMlResponseBplusToD0Pi : public HfMlResponse FILL_MAP_BPLUS(decayLengthXYNormalised), FILL_MAP_BPLUS(cpa), FILL_MAP_BPLUS(cpaXY), + FILL_MAP_BPLUS(decayLengthDToPv), + FILL_MAP_BPLUS(decayLengthDToPvXY), + FILL_MAP_BPLUS(decayLengthDToB), + FILL_MAP_BPLUS(decayLengthDToBXY), + FILL_MAP_BPLUS(cpaDToPv), + FILL_MAP_BPLUS(cpaDToPvXY), + FILL_MAP_BPLUS(cpaDToB), + FILL_MAP_BPLUS(cpaDToBXY), FILL_MAP_BPLUS(maxNormalisedDeltaIP), FILL_MAP_BPLUS(prong0MlProbBkg), FILL_MAP_BPLUS(prong0MlProbPrompt), diff --git a/PWGHF/Core/HfMlResponseBplusToD0PiReduced.h b/PWGHF/Core/HfMlResponseBplusToD0PiReduced.h index 93e2ea67eb4..43052a86ccf 100644 --- a/PWGHF/Core/HfMlResponseBplusToD0PiReduced.h +++ b/PWGHF/Core/HfMlResponseBplusToD0PiReduced.h @@ -27,10 +27,9 @@ // Fill the map of available input features // the key is the feature's name (std::string) // the value is the corresponding value in EnumInputFeatures -#define FILL_MAP_BPLUS(FEATURE) \ - { \ - #FEATURE, static_cast(InputFeaturesBplusToD0PiReduced::FEATURE) \ - } +#define FILL_MAP_BPLUS(FEATURE) \ + { \ + #FEATURE, static_cast(InputFeaturesBplusToD0PiReduced::FEATURE)} // Check if the index of mCachedIndices (index associated to a FEATURE) // matches the entry in EnumInputFeatures associated to this FEATURE @@ -76,6 +75,14 @@ enum class InputFeaturesBplusToD0PiReduced : uint8_t { decayLengthXYNormalised, cpa, cpaXY, + decayLengthDToPv, + decayLengthDToPvXY, + decayLengthDToB, + decayLengthDToBXY, + cpaDToPv, + cpaDToPvXY, + cpaDToB, + cpaDToBXY, maxNormalisedDeltaIP, prong0MlScoreBkg, prong0MlScorePrompt, @@ -119,6 +126,14 @@ class HfMlResponseBplusToD0PiReduced : public HfMlResponse CHECK_AND_FILL_VEC_BPLUS(decayLengthXYNormalised); CHECK_AND_FILL_VEC_BPLUS(cpa); CHECK_AND_FILL_VEC_BPLUS(cpaXY); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToPv); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToPvXY); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToB); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToBXY); + CHECK_AND_FILL_VEC_BPLUS(cpaDToPv); + CHECK_AND_FILL_VEC_BPLUS(cpaDToPvXY); + CHECK_AND_FILL_VEC_BPLUS(cpaDToB); + CHECK_AND_FILL_VEC_BPLUS(cpaDToBXY); CHECK_AND_FILL_VEC_BPLUS(maxNormalisedDeltaIP); CHECK_AND_FILL_VEC_BPLUS(prong0MlScoreBkg); CHECK_AND_FILL_VEC_BPLUS(prong0MlScorePrompt); @@ -144,6 +159,14 @@ class HfMlResponseBplusToD0PiReduced : public HfMlResponse CHECK_AND_FILL_VEC_BPLUS(decayLengthXYNormalised); CHECK_AND_FILL_VEC_BPLUS(cpa); CHECK_AND_FILL_VEC_BPLUS(cpaXY); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToPv); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToPvXY); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToB); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToBXY); + CHECK_AND_FILL_VEC_BPLUS(cpaDToPv); + CHECK_AND_FILL_VEC_BPLUS(cpaDToPvXY); + CHECK_AND_FILL_VEC_BPLUS(cpaDToB); + CHECK_AND_FILL_VEC_BPLUS(cpaDToBXY); CHECK_AND_FILL_VEC_BPLUS(maxNormalisedDeltaIP); // TPC PID variable CHECK_AND_FILL_VEC_BPLUS_FULL(prong1, tpcNSigmaPi1, tpcNSigmaPi); @@ -175,6 +198,14 @@ class HfMlResponseBplusToD0PiReduced : public HfMlResponse FILL_MAP_BPLUS(decayLengthXYNormalised), FILL_MAP_BPLUS(cpa), FILL_MAP_BPLUS(cpaXY), + FILL_MAP_BPLUS(decayLengthDToPv), + FILL_MAP_BPLUS(decayLengthDToPvXY), + FILL_MAP_BPLUS(decayLengthDToB), + FILL_MAP_BPLUS(decayLengthDToBXY), + FILL_MAP_BPLUS(cpaDToPv), + FILL_MAP_BPLUS(cpaDToPvXY), + FILL_MAP_BPLUS(cpaDToB), + FILL_MAP_BPLUS(cpaDToBXY), FILL_MAP_BPLUS(maxNormalisedDeltaIP), FILL_MAP_BPLUS(prong0MlScoreBkg), FILL_MAP_BPLUS(prong0MlScorePrompt), diff --git a/PWGHF/Core/HfMlResponseOmegacToOmegaPiQa.h b/PWGHF/Core/HfMlResponseOmegacToOmegaPiQa.h new file mode 100644 index 00000000000..2c3fd60bcae --- /dev/null +++ b/PWGHF/Core/HfMlResponseOmegacToOmegaPiQa.h @@ -0,0 +1,237 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file HfMlResponseOmegacToOmegaPiQa.h +/// \brief Class to compute the ML response for Ωc0 → Ω- π+ (and charge-conjugate) analysis selections +/// \author Yunfan Liu , China University of Geosciences +/// \author Maria Fernanda Torres Cabrera , University of Houston + +#ifndef PWGHF_CORE_HFMLRESPONSEOMEGACTOOMEGAPIQA_H_ +#define PWGHF_CORE_HFMLRESPONSEOMEGACTOOMEGAPIQA_H_ + +#include "PWGHF/Core/HfMlResponse.h" +#include "PWGHF/DataModel/CandidateReconstructionTables.h" + +#include "Tools/ML/MlResponse.h" + +#include +#include +#include +#include + +// Fill the map of available input features +// the key is the feature's name (std::string) +// the value is the corresponding value in EnumInputFeatures +#define FILL_MAP_OMEGAC0(FEATURE) \ + { \ + #FEATURE, static_cast(InputFeaturesOmegacToOmegaPi::FEATURE)} + +// Check if the index of mCachedIndices (index associated to a FEATURE) +// matches the entry in EnumInputFeatures associated to this FEATURE +// if so, the inputFeatures vector is filled with the FEATURE's value +// by calling the corresponding GETTER from OBJECT +#define CHECK_AND_FILL_VEC_OMEGAC0_FULL(OBJECT, FEATURE, GETTER) \ + case static_cast(InputFeaturesOmegacToOmegaPi::FEATURE): { \ + inputFeatures.emplace_back(OBJECT.GETTER()); \ + break; \ + } + +// Specific case of CHECK_AND_FILL_VEC_OMEGAC0_FULL(OBJECT, FEATURE, GETTER) +// where OBJECT is named candidate and FEATURE = GETTER +#define CHECK_AND_FILL_VEC_OMEGAC0(GETTER) \ + case static_cast(InputFeaturesOmegacToOmegaPi::GETTER): { \ + inputFeatures.emplace_back(candidate.GETTER()); \ + break; \ + } + +namespace o2::analysis +{ +enum class InputFeaturesOmegacToOmegaPi : uint8_t { + + // Common variables + dcaCharmBaryonDau = 0, + dcaCascDau, + nSigmaTPCPiFromV0, + nSigmaTPCPiFromOmegac, + nSigmaTPCKaFromCasc, + + // DCAFitter variables + impactParBachFromCharmBaryonXY, + impactParBachFromCharmBaryonZ, + impactParCascXY, + impactParCascZ, + cosPACharmBaryon, + cosPACasc, + normDecayLenCharmBar, + + // KFParticle variables + cosPaOmegacToPv, + kfDcaXYPiFromOmegac, + chi2TopoPiFromOmegacToPv, + invMassCascade, + massCascChi2OverNdf, + kfDcaXYCascToPv, + cosPaCascToPv, + cosThetaStarPiFromOmegac, + chi2NdfTopoOmegacToPv, + ldlCasc, + cosPaCascToOmegac, + decayLenXYCasc, + decayLenXYOmegac, + ldlOmegac, + chi2NdfTopoCascToOmegac, + chi2NdfTopoCascToPv, + chi2GeoOmegac, + chi2GeoCasc, +}; + +template +class HfMlResponseOmegacToOmegaPi : public HfMlResponse +{ + public: + /// Default constructor + HfMlResponseOmegacToOmegaPi() = default; + /// Default destructor + virtual ~HfMlResponseOmegacToOmegaPi() = default; + + /// Method to get the input features vector needed for ML inference + /// \param candidate is the OMEGAC0 candidate + /// \param lamProngPi is the candidate's lamProngPi + /// \param cascProng is the bachelor track from the Omega decay + /// \param charmBaryonProng is the bachelor pion from the OmegaC decay + /// \return inputFeatures vector + template + std::vector getInputFeatures(T1 const& candidate, T2 const& lamProngPi, T2 const& cascProng, T3 const& charmBaryonProng) + { + std::vector inputFeatures; + + for (const auto& idx : MlResponse::mCachedIndices) { + + // Variables common to DCAFitter and KFParticle + switch (idx) { + + CHECK_AND_FILL_VEC_OMEGAC0(dcaCharmBaryonDau); + CHECK_AND_FILL_VEC_OMEGAC0(dcaCascDau); + // TPC PID variables + CHECK_AND_FILL_VEC_OMEGAC0_FULL(lamProngPi, nSigmaTPCPiFromV0, tpcNSigmaPi); + CHECK_AND_FILL_VEC_OMEGAC0_FULL(cascProng, nSigmaTPCKaFromCasc, tpcNSigmaKa); + CHECK_AND_FILL_VEC_OMEGAC0_FULL(charmBaryonProng, nSigmaTPCPiFromOmegac, tpcNSigmaPi); + } + + // DCAFitter variables + if constexpr (reconstructionType == aod::hf_cand_casc_lf::ConstructMethod::DcaFitter) { + + switch (idx) { + + CHECK_AND_FILL_VEC_OMEGAC0(impactParBachFromCharmBaryonXY); + CHECK_AND_FILL_VEC_OMEGAC0(impactParBachFromCharmBaryonZ); + CHECK_AND_FILL_VEC_OMEGAC0(impactParCascXY); + CHECK_AND_FILL_VEC_OMEGAC0(impactParCascZ); + CHECK_AND_FILL_VEC_OMEGAC0(cosPACharmBaryon); + CHECK_AND_FILL_VEC_OMEGAC0(cosPACasc); + CHECK_AND_FILL_VEC_OMEGAC0(normDecayLenCharmBar); + } + } + + // KFParticle variables + if constexpr (reconstructionType == aod::hf_cand_casc_lf::ConstructMethod::KfParticle) { + + switch (idx) { + + CHECK_AND_FILL_VEC_OMEGAC0_FULL(candidate, cosPaOmegacToPv, cosPACharmBaryon); + CHECK_AND_FILL_VEC_OMEGAC0(kfDcaXYPiFromOmegac); + CHECK_AND_FILL_VEC_OMEGAC0(chi2TopoPiFromOmegacToPv); + CHECK_AND_FILL_VEC_OMEGAC0(invMassCascade); + CHECK_AND_FILL_VEC_OMEGAC0(massCascChi2OverNdf); + CHECK_AND_FILL_VEC_OMEGAC0(kfDcaXYCascToPv); + CHECK_AND_FILL_VEC_OMEGAC0_FULL(candidate, cosPaCascToPv, cosPACasc); + CHECK_AND_FILL_VEC_OMEGAC0(cosThetaStarPiFromOmegac); + CHECK_AND_FILL_VEC_OMEGAC0_FULL(candidate, chi2NdfTopoOmegacToPv, chi2TopoOmegacToPv); + CHECK_AND_FILL_VEC_OMEGAC0_FULL(candidate, ldlCasc, cascldl); + CHECK_AND_FILL_VEC_OMEGAC0(cosPaCascToOmegac); + CHECK_AND_FILL_VEC_OMEGAC0(decayLenXYCasc); + CHECK_AND_FILL_VEC_OMEGAC0(decayLenXYOmegac); + CHECK_AND_FILL_VEC_OMEGAC0_FULL(candidate, ldlOmegac, omegacldl); + CHECK_AND_FILL_VEC_OMEGAC0_FULL(candidate, chi2NdfTopoCascToOmegac, chi2TopoCascToOmegac); + CHECK_AND_FILL_VEC_OMEGAC0_FULL(candidate, chi2NdfTopoCascToPv, chi2TopoCascToPv); + CHECK_AND_FILL_VEC_OMEGAC0(chi2GeoOmegac); + CHECK_AND_FILL_VEC_OMEGAC0(chi2GeoCasc); + } + } + } + + return inputFeatures; + } + + protected: + /// Method to fill the map of available input features + void setAvailableInputFeatures() + { + // Common variables + MlResponse::mAvailableInputFeatures = { + + FILL_MAP_OMEGAC0(dcaCharmBaryonDau), + FILL_MAP_OMEGAC0(dcaCascDau), + FILL_MAP_OMEGAC0(nSigmaTPCPiFromV0), + FILL_MAP_OMEGAC0(nSigmaTPCPiFromOmegac), + FILL_MAP_OMEGAC0(nSigmaTPCKaFromCasc)}; + + // DCAFitter variables + if constexpr (reconstructionType == aod::hf_cand_casc_lf::ConstructMethod::DcaFitter) { + + std::map mapDcaFeatures{ + + FILL_MAP_OMEGAC0(impactParBachFromCharmBaryonXY), + FILL_MAP_OMEGAC0(impactParBachFromCharmBaryonZ), + FILL_MAP_OMEGAC0(impactParCascXY), + FILL_MAP_OMEGAC0(impactParCascZ), + FILL_MAP_OMEGAC0(cosPACharmBaryon), + FILL_MAP_OMEGAC0(cosPACasc), + FILL_MAP_OMEGAC0(normDecayLenCharmBar)}; + + MlResponse::mAvailableInputFeatures.insert(mapDcaFeatures.begin(), mapDcaFeatures.end()); + } + + // KFParticle variables + if constexpr (reconstructionType == aod::hf_cand_casc_lf::ConstructMethod::KfParticle) { + + std::map mapKfFeatures{ + + FILL_MAP_OMEGAC0(cosPaOmegacToPv), + FILL_MAP_OMEGAC0(kfDcaXYPiFromOmegac), + FILL_MAP_OMEGAC0(chi2TopoPiFromOmegacToPv), + FILL_MAP_OMEGAC0(invMassCascade), + FILL_MAP_OMEGAC0(massCascChi2OverNdf), + FILL_MAP_OMEGAC0(kfDcaXYCascToPv), + FILL_MAP_OMEGAC0(cosPaCascToPv), + FILL_MAP_OMEGAC0(cosThetaStarPiFromOmegac), + FILL_MAP_OMEGAC0(chi2NdfTopoOmegacToPv), + FILL_MAP_OMEGAC0(ldlCasc), + FILL_MAP_OMEGAC0(cosPaCascToOmegac), + FILL_MAP_OMEGAC0(decayLenXYCasc), + FILL_MAP_OMEGAC0(decayLenXYOmegac), + FILL_MAP_OMEGAC0(ldlOmegac), + FILL_MAP_OMEGAC0(chi2NdfTopoCascToOmegac), + FILL_MAP_OMEGAC0(chi2NdfTopoCascToPv), + FILL_MAP_OMEGAC0(chi2GeoOmegac), + FILL_MAP_OMEGAC0(chi2GeoCasc)}; + + MlResponse::mAvailableInputFeatures.insert(mapKfFeatures.begin(), mapKfFeatures.end()); + } + } +}; + +} // namespace o2::analysis + +#undef FILL_MAP_OMEGAC0 +#undef CHECK_AND_FILL_VEC_OMEGAC0_FULL +#undef CHECK_AND_FILL_VEC_OMEGAC0 +#endif // PWGHF_CORE_HFMLRESPONSEOMEGACTOOMEGAPIQA_H_ diff --git a/PWGHF/Core/HfMlResponseXic0ToXiPiKf.h b/PWGHF/Core/HfMlResponseXic0ToXiPiKf.h index 3fbb6cf4052..2a16648320b 100644 --- a/PWGHF/Core/HfMlResponseXic0ToXiPiKf.h +++ b/PWGHF/Core/HfMlResponseXic0ToXiPiKf.h @@ -18,26 +18,20 @@ #include "PWGHF/Core/HfMlResponse.h" +#include "Common/Core/RecoDecay.h" #include "Tools/ML/MlResponse.h" #include #include -// Fill the map of available input features -// the key is the feature's name (std::string) -// the value is the corresponding value in EnumInputFeatures -#define FILL_MAP_XIC0TOXIPIKF(FEATURE) \ - { \ - #FEATURE, static_cast(InputFeaturesXic0ToXiPiKf::FEATURE) \ - } - +// Feature names are registered explicitly with the training-tree branch names. // Check if the index of mCachedIndices (index associated to a FEATURE) // matches the entry in EnumInputFeatures associated to this FEATURE // if so, the inputFeatures vector is filled with the FEATURE's value // by calling the corresponding GETTER from OBJECT #define CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(OBJECT, FEATURE, GETTER) \ case static_cast(InputFeaturesXic0ToXiPiKf::FEATURE): { \ - inputFeatures.emplace_back(OBJECT.GETTER()); \ + inputFeatures.emplace_back((OBJECT).GETTER()); \ break; \ } @@ -48,10 +42,20 @@ break; \ } +#define CHECK_AND_FILL_VEC_XIC0TOXIPIKF_VALUE(FEATURE, VALUE) \ + case static_cast(InputFeaturesXic0ToXiPiKf::FEATURE): { \ + inputFeatures.emplace_back(VALUE); \ + break; \ + } + +#define FILL_MAP_XIC0TOXIPIKF_AS(NAME, FEATURE) \ + {NAME, static_cast(InputFeaturesXic0ToXiPiKf::FEATURE)} + namespace o2::analysis { enum class InputFeaturesXic0ToXiPiKf : uint8_t { + // Internal feature identifiers; configuration names are registered below. tpcNSigmaPiFromLambda, tpcNSigmaPiFromCasc, tpcNSigmaPiFromCharmBaryon, @@ -65,7 +69,27 @@ enum class InputFeaturesXic0ToXiPiKf : uint8_t { chi2TopoCascToPv, chi2TopoCascToXic, cosPaCascToXic, - decayLenXYCasc + decayLenXYCasc, + // Remaining features used by the local PbPb Transformer. + decayLenXYXic, + chi2TopoXicToPv, + chi2TopoPiFromXicToPv, + kfptPiFromXic, + cosPaCascToPv, + dcaXYToPvCascDau, + decayLenXYLambda, + v0Chi2OverNdf, + chi2TopoV0ToPv, + chi2TopoV0ToCasc, + cosPaV0ToPv, + cosPaV0ToCasc, + dcaV0Dau, + dcaXYToPvV0Dau0, + dcaXYToPvV0Dau1, + tpcNSigmaPrFromLambda, + chi2GeoXic, + cTauXic, + chi2GeoCasc }; template @@ -81,34 +105,53 @@ class HfMlResponseXic0ToXiPiKf : public HfMlResponse /// \param candidate is the Xic0 candidate /// \return inputFeatures vector template - // std::vector getInputFeatures(T1 const& candidate) - std::vector getInputFeatures(T1 const& candidate, T2 const& lamProngPi, T2 const& cascProngPi, T3 const& charmBaryonProngPi) + std::vector getInputFeatures(T1 const& candidate, T2 const& lamProngPi, T2 const& lamProngPr, + T2 const& cascProngPi, T3 const& charmBaryonProngPi) { std::vector inputFeatures; + inputFeatures.reserve(MlResponse::mCachedIndices.size()); for (const auto& idx : MlResponse::mCachedIndices) { switch (idx) { - // PID variables - CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(lamProngPi, tpcNSigmaPiFromLambda, tpcNSigmaPi); - CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(cascProngPi, tpcNSigmaPiFromCasc, tpcNSigmaPi); - CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(charmBaryonProngPi, tpcNSigmaPiFromCharmBaryon, tpcNSigmaPi); - // DCA - CHECK_AND_FILL_VEC_XIC0TOXIPIKF(dcaCascDau); + // Xic candidate topology. + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(decayLenXYXic); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(xicChi2OverNdf); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(chi2TopoXicToPv); CHECK_AND_FILL_VEC_XIC0TOXIPIKF(dcaCharmBaryonDau); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(chi2GeoXic); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(cTauXic); + // Bachelor pion from Xic. CHECK_AND_FILL_VEC_XIC0TOXIPIKF(kfDcaXYPiFromXic); - CHECK_AND_FILL_VEC_XIC0TOXIPIKF(kfDcaXYCascToPv); - // Chi2Geo + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(chi2TopoPiFromXicToPv); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF_VALUE(kfptPiFromXic, RecoDecay::sqrtSumOfSquares(candidate.pxBachFromCharmBaryon(), candidate.pyBachFromCharmBaryon())); + // Cascade. + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(decayLenXYCasc); CHECK_AND_FILL_VEC_XIC0TOXIPIKF(cascChi2OverNdf); - CHECK_AND_FILL_VEC_XIC0TOXIPIKF(xicChi2OverNdf); - // ldl - CHECK_AND_FILL_VEC_XIC0TOXIPIKF(cascldl); - // Chi2Topo + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(chi2GeoCasc); CHECK_AND_FILL_VEC_XIC0TOXIPIKF(chi2TopoCascToPv); CHECK_AND_FILL_VEC_XIC0TOXIPIKF(chi2TopoCascToXic); - // CosPa + CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(candidate, cosPaCascToPv, cosPACasc); CHECK_AND_FILL_VEC_XIC0TOXIPIKF(cosPaCascToXic); - // Decay length - CHECK_AND_FILL_VEC_XIC0TOXIPIKF(decayLenXYCasc); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(dcaCascDau); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(kfDcaXYCascToPv); + // Pion from cascade. + CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(cascProngPi, tpcNSigmaPiFromCasc, tpcNSigmaPi); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(dcaXYToPvCascDau); + // Lambda and its charged daughters. The selector passes sign-corrected prongs. + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(decayLenXYLambda); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(v0Chi2OverNdf); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(chi2TopoV0ToPv); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(chi2TopoV0ToCasc); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(candidate, cosPaV0ToPv, cosPAV0); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(cosPaV0ToCasc); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(dcaV0Dau); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(dcaXYToPvV0Dau0); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(dcaXYToPvV0Dau1); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(lamProngPr, tpcNSigmaPrFromLambda, tpcNSigmaPr); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(lamProngPi, tpcNSigmaPiFromLambda, tpcNSigmaPi); + // Additional supported features. + CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(charmBaryonProngPi, tpcNSigmaPiFromCharmBaryon, tpcNSigmaPi); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(cascldl); } } @@ -120,28 +163,48 @@ class HfMlResponseXic0ToXiPiKf : public HfMlResponse void setAvailableInputFeatures() { MlResponse::mAvailableInputFeatures = { - FILL_MAP_XIC0TOXIPIKF(tpcNSigmaPiFromLambda), - FILL_MAP_XIC0TOXIPIKF(tpcNSigmaPiFromCasc), - FILL_MAP_XIC0TOXIPIKF(tpcNSigmaPiFromCharmBaryon), - FILL_MAP_XIC0TOXIPIKF(dcaCascDau), - FILL_MAP_XIC0TOXIPIKF(dcaCharmBaryonDau), - FILL_MAP_XIC0TOXIPIKF(kfDcaXYPiFromXic), - FILL_MAP_XIC0TOXIPIKF(kfDcaXYCascToPv), - FILL_MAP_XIC0TOXIPIKF(cascChi2OverNdf), - FILL_MAP_XIC0TOXIPIKF(xicChi2OverNdf), - FILL_MAP_XIC0TOXIPIKF(cascldl), - FILL_MAP_XIC0TOXIPIKF(chi2TopoCascToPv), - FILL_MAP_XIC0TOXIPIKF(chi2TopoCascToXic), - FILL_MAP_XIC0TOXIPIKF(cosPaCascToXic), - FILL_MAP_XIC0TOXIPIKF(decayLenXYCasc), + FILL_MAP_XIC0TOXIPIKF_AS("fDecayLenXYXic", decayLenXYXic), + FILL_MAP_XIC0TOXIPIKF_AS("fXicChi2OverNdf", xicChi2OverNdf), + FILL_MAP_XIC0TOXIPIKF_AS("fChi2TopoXicToPv", chi2TopoXicToPv), + FILL_MAP_XIC0TOXIPIKF_AS("fDcaCharmBaryonDau", dcaCharmBaryonDau), + FILL_MAP_XIC0TOXIPIKF_AS("fChi2GeoXic", chi2GeoXic), + FILL_MAP_XIC0TOXIPIKF_AS("fCtXic", cTauXic), + FILL_MAP_XIC0TOXIPIKF_AS("fKfDcaXYPiFromXic", kfDcaXYPiFromXic), + FILL_MAP_XIC0TOXIPIKF_AS("fChi2TopoPiFromXicToPv", chi2TopoPiFromXicToPv), + FILL_MAP_XIC0TOXIPIKF_AS("fKfptPiFromXic", kfptPiFromXic), + FILL_MAP_XIC0TOXIPIKF_AS("fDecayLenXYCasc", decayLenXYCasc), + FILL_MAP_XIC0TOXIPIKF_AS("fCascChi2OverNdf", cascChi2OverNdf), + FILL_MAP_XIC0TOXIPIKF_AS("fChi2GeoCasc", chi2GeoCasc), + FILL_MAP_XIC0TOXIPIKF_AS("fChi2TopoCascToPv", chi2TopoCascToPv), + FILL_MAP_XIC0TOXIPIKF_AS("fChi2TopoCascToXic", chi2TopoCascToXic), + FILL_MAP_XIC0TOXIPIKF_AS("fCosPaCascToPv", cosPaCascToPv), + FILL_MAP_XIC0TOXIPIKF_AS("fCosPaCascToXic", cosPaCascToXic), + FILL_MAP_XIC0TOXIPIKF_AS("fDcaCascDau", dcaCascDau), + FILL_MAP_XIC0TOXIPIKF_AS("fKfDcaXYCascToPv", kfDcaXYCascToPv), + FILL_MAP_XIC0TOXIPIKF_AS("fTpcNSigmaPiFromCasc", tpcNSigmaPiFromCasc), + FILL_MAP_XIC0TOXIPIKF_AS("fDcaXYToPvCascDau", dcaXYToPvCascDau), + FILL_MAP_XIC0TOXIPIKF_AS("fDecayLenXYLambda", decayLenXYLambda), + FILL_MAP_XIC0TOXIPIKF_AS("fV0Chi2OverNdf", v0Chi2OverNdf), + FILL_MAP_XIC0TOXIPIKF_AS("fChi2TopoV0ToPv", chi2TopoV0ToPv), + FILL_MAP_XIC0TOXIPIKF_AS("fChi2TopoV0ToCasc", chi2TopoV0ToCasc), + FILL_MAP_XIC0TOXIPIKF_AS("fCosPaV0ToPv", cosPaV0ToPv), + FILL_MAP_XIC0TOXIPIKF_AS("fCosPaV0ToCasc", cosPaV0ToCasc), + FILL_MAP_XIC0TOXIPIKF_AS("fDcaV0Dau", dcaV0Dau), + FILL_MAP_XIC0TOXIPIKF_AS("fDcaXYToPvV0Dau0", dcaXYToPvV0Dau0), + FILL_MAP_XIC0TOXIPIKF_AS("fDcaXYToPvV0Dau1", dcaXYToPvV0Dau1), + FILL_MAP_XIC0TOXIPIKF_AS("fTpcNSigmaPrFromLambda", tpcNSigmaPrFromLambda), + FILL_MAP_XIC0TOXIPIKF_AS("fTpcNSigmaPiFromLambda", tpcNSigmaPiFromLambda), + FILL_MAP_XIC0TOXIPIKF_AS("fTpcNSigmaPiFromCharmBaryon", tpcNSigmaPiFromCharmBaryon), + FILL_MAP_XIC0TOXIPIKF_AS("fCascldl", cascldl), }; } }; } // namespace o2::analysis -#undef FILL_MAP_XIC0TOXIPIKF +#undef FILL_MAP_XIC0TOXIPIKF_AS #undef CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL #undef CHECK_AND_FILL_VEC_XIC0TOXIPIKF +#undef CHECK_AND_FILL_VEC_XIC0TOXIPIKF_VALUE #endif // PWGHF_CORE_HFMLRESPONSEXIC0TOXIPIKF_H_ diff --git a/PWGHF/Core/HfMlResponseXicToXiPiPi.h b/PWGHF/Core/HfMlResponseXicToXiPiPi.h index 4bb4a4d023f..6e8b598a166 100644 --- a/PWGHF/Core/HfMlResponseXicToXiPiPi.h +++ b/PWGHF/Core/HfMlResponseXicToXiPiPi.h @@ -17,19 +17,21 @@ #define PWGHF_CORE_HFMLRESPONSEXICTOXIPIPI_H_ #include "PWGHF/Core/HfMlResponse.h" +#include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "Tools/ML/MlResponse.h" #include +#include +#include #include // Fill the map of available input features // the key is the feature's name (std::string) // the value is the corresponding value in EnumInputFeatures -#define FILL_MAP_XICTOXIPIPI(FEATURE) \ - { \ - #FEATURE, static_cast(InputFeaturesXicToXiPiPi::FEATURE) \ - } +#define FILL_MAP_XICTOXIPIPI(FEATURE) \ + { \ + #FEATURE, static_cast(InputFeaturesXicToXiPiPi::FEATURE)} // Check if the index of mCachedIndices (index associated to a FEATURE) // matches the entry in EnumInputFeatures associated to this FEATURE @@ -88,10 +90,31 @@ enum class InputFeaturesXicToXiPiPi : uint8_t { nSigTofPiFromXicPlus1, nSigTofBachelorPi, nSigTofPiFromLambda, - nSigTofPrFromLambda + nSigTofPrFromLambda, + // kf-specific variables + kfDecayLength, + kfDecayLengthNormalised, + kfDecayLengthXY, + kfDecayLengthXYNormalised, + kfCascadeChi2, + kfV0Chi2, + chi2XicPlusToPV, + chi2TopoXicPlusToPV, + chi2XiToPV, + chi2Pi0ToPV, + chi2Pi1ToPV, + chi2DevPi0Pi1, + chi2DevPi0Xi, + chi2DevPi1Xi, + dcaPi0Pi1, + dcaPi0Xi, + dcaPi1Xi, + dcaXYPi0Pi1, + dcaXYPi0Xi, + dcaXYPi1Xi }; -template +template class HfMlResponseXicToXiPiPi : public HfMlResponse { public: @@ -147,6 +170,30 @@ class HfMlResponseXicToXiPiPi : public HfMlResponse CHECK_AND_FILL_VEC_XICTOXIPIPI(nSigTofPiFromLambda); CHECK_AND_FILL_VEC_XICTOXIPIPI(nSigTofPrFromLambda); } + if constexpr (ReconstructionType == aod::hf_cand::VertexerType::KfParticle) { + switch (idx) { + CHECK_AND_FILL_VEC_XICTOXIPIPI(kfDecayLength); + CHECK_AND_FILL_VEC_XICTOXIPIPI(kfDecayLengthNormalised); + CHECK_AND_FILL_VEC_XICTOXIPIPI(kfDecayLengthXY); + CHECK_AND_FILL_VEC_XICTOXIPIPI(kfDecayLengthXYNormalised); + CHECK_AND_FILL_VEC_XICTOXIPIPI(kfCascadeChi2); + CHECK_AND_FILL_VEC_XICTOXIPIPI(kfV0Chi2); + CHECK_AND_FILL_VEC_XICTOXIPIPI(chi2XicPlusToPV); + CHECK_AND_FILL_VEC_XICTOXIPIPI(chi2TopoXicPlusToPV); + CHECK_AND_FILL_VEC_XICTOXIPIPI(chi2XiToPV); + CHECK_AND_FILL_VEC_XICTOXIPIPI(chi2Pi0ToPV); + CHECK_AND_FILL_VEC_XICTOXIPIPI(chi2Pi1ToPV); + CHECK_AND_FILL_VEC_XICTOXIPIPI(chi2DevPi0Pi1); + CHECK_AND_FILL_VEC_XICTOXIPIPI(chi2DevPi0Xi); + CHECK_AND_FILL_VEC_XICTOXIPIPI(chi2DevPi1Xi); + CHECK_AND_FILL_VEC_XICTOXIPIPI(dcaPi0Pi1); + CHECK_AND_FILL_VEC_XICTOXIPIPI(dcaPi0Xi); + CHECK_AND_FILL_VEC_XICTOXIPIPI(dcaPi1Xi); + CHECK_AND_FILL_VEC_XICTOXIPIPI(dcaXYPi0Pi1); + CHECK_AND_FILL_VEC_XICTOXIPIPI(dcaXYPi0Xi); + CHECK_AND_FILL_VEC_XICTOXIPIPI(dcaXYPi1Xi); + } + } } return inputFeatures; @@ -193,6 +240,30 @@ class HfMlResponseXicToXiPiPi : public HfMlResponse FILL_MAP_XICTOXIPIPI(nSigTofBachelorPi), FILL_MAP_XICTOXIPIPI(nSigTofPiFromLambda), FILL_MAP_XICTOXIPIPI(nSigTofPrFromLambda)}; + if constexpr (ReconstructionType == aod::hf_cand::VertexerType::KfParticle) { + std::map mapKfFeatures{ + FILL_MAP_XICTOXIPIPI(kfDecayLength), + FILL_MAP_XICTOXIPIPI(kfDecayLengthNormalised), + FILL_MAP_XICTOXIPIPI(kfDecayLengthXY), + FILL_MAP_XICTOXIPIPI(kfDecayLengthXYNormalised), + FILL_MAP_XICTOXIPIPI(kfCascadeChi2), + FILL_MAP_XICTOXIPIPI(kfV0Chi2), + FILL_MAP_XICTOXIPIPI(chi2XicPlusToPV), + FILL_MAP_XICTOXIPIPI(chi2TopoXicPlusToPV), + FILL_MAP_XICTOXIPIPI(chi2XiToPV), + FILL_MAP_XICTOXIPIPI(chi2Pi0ToPV), + FILL_MAP_XICTOXIPIPI(chi2Pi1ToPV), + FILL_MAP_XICTOXIPIPI(chi2DevPi0Pi1), + FILL_MAP_XICTOXIPIPI(chi2DevPi0Xi), + FILL_MAP_XICTOXIPIPI(chi2DevPi1Xi), + FILL_MAP_XICTOXIPIPI(dcaPi0Pi1), + FILL_MAP_XICTOXIPIPI(dcaPi0Xi), + FILL_MAP_XICTOXIPIPI(dcaPi1Xi), + FILL_MAP_XICTOXIPIPI(dcaXYPi0Pi1), + FILL_MAP_XICTOXIPIPI(dcaXYPi0Xi), + FILL_MAP_XICTOXIPIPI(dcaXYPi1Xi)}; + MlResponse::mAvailableInputFeatures.insert(mapKfFeatures.begin(), mapKfFeatures.end()); + } } }; diff --git a/PWGHF/Core/hfCandidatesPCH.cxx b/PWGHF/Core/hfCandidatesPCH.cxx new file mode 100644 index 00000000000..95f76a2131e --- /dev/null +++ b/PWGHF/Core/hfCandidatesPCH.cxx @@ -0,0 +1,13 @@ +// Copyright 2019-2026 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. + +// Carrier for the HfCandidatesPCH precompiled header; see this directory's +// CMakeLists.txt. It exists only to own the PCH, so it has no code of its own. diff --git a/PWGHF/D2H/Macros/HFInvMassFitterLinkDef.h b/PWGHF/D2H/Macros/HFInvMassFitterLinkDef.h index cc755de478f..f5ddd506b55 100644 --- a/PWGHF/D2H/Macros/HFInvMassFitterLinkDef.h +++ b/PWGHF/D2H/Macros/HFInvMassFitterLinkDef.h @@ -18,8 +18,7 @@ /// \author Biao Zhang /// \author Oleksii Lubynets -#ifndef PWGHF_D2H_MACROS_HFINVMASSFITTERLINKDEF_H_ -#define PWGHF_D2H_MACROS_HFINVMASSFITTERLINKDEF_H_ +// NOLINT(build/header_guard) #ifdef __CINT__ #pragma link off all globals; @@ -27,5 +26,3 @@ #pragma link off all functions; #pragma link C++ class HFInvMassFitter + ; #endif - -#endif // PWGHF_D2H_MACROS_HFINVMASSFITTERLINKDEF_H_ diff --git a/PWGHF/D2H/Macros/compute_fraction_cutvar.py b/PWGHF/D2H/Macros/compute_fraction_cutvar.py index 74432688725..d3b3b87111d 100644 --- a/PWGHF/D2H/Macros/compute_fraction_cutvar.py +++ b/PWGHF/D2H/Macros/compute_fraction_cutvar.py @@ -16,12 +16,13 @@ import numpy as np # pylint: disable=import-error import ROOT # pylint: disable=import-error -sys.path.insert(0, '..') +sys.path.insert(0, "..") from cut_variation import CutVarMinimiser, MinimisationStatus from style_formatter import set_object_style # pylint: disable=no-member,too-many-locals,too-many-statements + class PlotType(IntEnum): Rawy = 0 Eff = auto() @@ -31,6 +32,7 @@ class PlotType(IntEnum): RelUnc = auto() N = auto() + class ObjectToSave(IntEnum): Canvas = 0 RawYield = auto() @@ -44,6 +46,7 @@ class ObjectToSave(IntEnum): MinimisationStatus = auto() N = auto() + def main(config): """ Main function @@ -65,7 +68,7 @@ def main(config): hist_rawy_name = cfg["rawyields"]["histoname"] hist_rawy.append(infile_rawy.Get(hist_rawy_name)) if not isinstance(hist_rawy[-1], ROOT.TH1): - sys.exit(f"\33[31mFatal error: Histogram with raw yield \"{hist_rawy_name}\" is absent. Exit.\33[0m") + sys.exit(f'\33[31mFatal error: Histogram with raw yield "{hist_rawy_name}" is absent. Exit.\33[0m') hist_rawy[-1].SetDirectory(0) infile_rawy.Close() @@ -75,14 +78,22 @@ def main(config): hist_effp.append(infile_eff.Get(hist_effp_name)) hist_effnp.append(infile_eff.Get(hist_effnp_name)) if not isinstance(hist_effp[-1], ROOT.TH1): - sys.exit(f"\33[31mFatal error: Histogram with efficiency for prompt \"{hist_effp_name}\" is absent. Exit.\33[0m") + sys.exit( + f'\33[31mFatal error: Histogram with efficiency for prompt "{hist_effp_name}" is absent. Exit.\33[0m' + ) if not isinstance(hist_effnp[-1], ROOT.TH1): - sys.exit(f"\33[31mFatal error: Histogram with efficiency for nonprompt \"{hist_effnp}\" is absent. Exit.\33[0m") + sys.exit( + f'\33[31mFatal error: Histogram with efficiency for nonprompt "{hist_effnp}" is absent. Exit.\33[0m' + ) hist_effp[-1].SetDirectory(0) hist_effnp[-1].SetDirectory(0) for i_bin in range(1, hist_effp[-1].GetNbinsX() + 1): - hist_effp[-1].SetBinContent(i_bin, hist_effp[-1].GetBinContent(i_bin) + effp_shift_nsigma*hist_effp[-1].GetBinError(i_bin)) - hist_effnp[-1].SetBinContent(i_bin, hist_effnp[-1].GetBinContent(i_bin) + effnp_shift_nsigma*hist_effnp[-1].GetBinError(i_bin)) + hist_effp[-1].SetBinContent( + i_bin, hist_effp[-1].GetBinContent(i_bin) + effp_shift_nsigma * hist_effp[-1].GetBinError(i_bin) + ) + hist_effnp[-1].SetBinContent( + i_bin, hist_effnp[-1].GetBinContent(i_bin) + effnp_shift_nsigma * hist_effnp[-1].GetBinError(i_bin) + ) if zero_eff_unc: hist_effp[-1].SetBinError(i_bin, 0.0) hist_effnp[-1].SetBinError(i_bin, 0.0) @@ -92,7 +103,9 @@ def main(config): if not isinstance(pt_bin_to_process, int): sys.exit("\33[31mFatal error: pt_bin_to_process must be an integer value. Exit.") if (pt_bin_to_process != -1 and pt_bin_to_process < 1) or pt_bin_to_process > hist_rawy[0].GetNbinsX(): - sys.exit("\33[31mFatal error: pt_bin_to_process must be a positive value up to number of bins in raw yield histogram. Exit.") + sys.exit( + "\33[31mFatal error: pt_bin_to_process must be a positive value up to number of bins in raw yield histogram. Exit." + ) is_draw_title = [False] * PlotType.N is_draw_title[PlotType.Rawy] = cfg.get("is_draw_title", {}).get("rawy", True) @@ -117,10 +130,16 @@ def main(config): is_save_to_root_file[ObjectToSave.Efficiency] = cfg.get("is_save_to_root_file", {}).get("efficiency", True) is_save_to_root_file[ObjectToSave.Fraction] = cfg.get("is_save_to_root_file", {}).get("fraction", True) is_save_to_root_file[ObjectToSave.CorrectedYield] = cfg.get("is_save_to_root_file", {}).get("corrected_yield", True) - is_save_to_root_file[ObjectToSave.CorrelationMatrix] = cfg.get("is_save_to_root_file", {}).get("correlation_matrix", True) + is_save_to_root_file[ObjectToSave.CorrelationMatrix] = cfg.get("is_save_to_root_file", {}).get( + "correlation_matrix", True + ) is_save_to_root_file[ObjectToSave.Covariance] = cfg.get("is_save_to_root_file", {}).get("covariance", True) - is_save_to_root_file[ObjectToSave.CorrectedFraction] = cfg.get("is_save_to_root_file", {}).get("corrected_fraction", True) - is_save_to_root_file[ObjectToSave.MinimisationStatus] = cfg.get("is_save_to_root_file", {}).get("minimisation_status", True) + is_save_to_root_file[ObjectToSave.CorrectedFraction] = cfg.get("is_save_to_root_file", {}).get( + "corrected_fraction", True + ) + is_save_to_root_file[ObjectToSave.MinimisationStatus] = cfg.get("is_save_to_root_file", {}).get( + "minimisation_status", True + ) if cfg["central_efficiency"]["computerawfrac"]: infile_name = os.path.join(cfg["central_efficiency"]["inputdir"], cfg["central_efficiency"]["inputfile"]) @@ -128,11 +147,15 @@ def main(config): hist_central_effp_name = cfg["central_efficiency"]["histonames"]["prompt"] hist_central_effp = infile_central_eff.Get(hist_central_effp_name) if not isinstance(hist_central_effp[-1], ROOT.TH1): - sys.exit(f"\33[31mFatal error: Histogram with central efficiency for prompt \"{hist_central_effp_name}\" is absent. Exit.\33[0m") + sys.exit( + f'\33[31mFatal error: Histogram with central efficiency for prompt "{hist_central_effp_name}" is absent. Exit.\33[0m' + ) hist_central_effnp_name = cfg["central_efficiency"]["histonames"]["nonprompt"] hist_central_effnp = infile_central_eff.Get(hist_central_effnp_name) if not isinstance(hist_central_effnp[-1], ROOT.TH1): - sys.exit(f"\33[31mFatal error: Histogram with central efficiency for nonprompt \"{hist_central_effnp_name}\" is absent. Exit.\33[0m") + sys.exit( + f'\33[31mFatal error: Histogram with central efficiency for nonprompt "{hist_central_effnp_name}" is absent. Exit.\33[0m' + ) hist_central_effp.SetDirectory(0) hist_central_effnp.SetDirectory(0) infile_central_eff.Close() @@ -150,8 +173,18 @@ def main(config): hist_corrfrac_prompt = hist_rawy[0].Clone("hCorrFracPrompt") hist_corrfrac_nonprompt = hist_rawy[0].Clone("hCorrFracNonPrompt") hist_minimisation_status = hist_rawy[0].Clone("hMinimizationStatus") - hist_red_chi2 = hist_rawy[0].Clone("hChi2OverNdf") - for histo in hist_corry_prompt, hist_corry_nonprompt, hist_covariance_pnp, hist_covariance_pp, hist_covariance_npnp, hist_corrfrac_prompt, hist_corrfrac_nonprompt, hist_minimisation_status, hist_red_chi2: + hist_red_chi2 = hist_rawy[0].Clone("hChi2OverNdf") + for histo in ( + hist_corry_prompt, + hist_corry_nonprompt, + hist_covariance_pnp, + hist_covariance_pp, + hist_covariance_npnp, + hist_corrfrac_prompt, + hist_corrfrac_nonprompt, + hist_minimisation_status, + hist_red_chi2, + ): histo.Reset() hist_corry_prompt.GetYaxis().SetTitle("corrected yields prompt") hist_corry_nonprompt.GetYaxis().SetTitle("corrected yields non-prompt") @@ -164,7 +197,7 @@ def main(config): hist_red_chi2.GetYaxis().SetTitle("#chi^{2}/ndf") hist_minimisation_status_title = "" for min_status in MinimisationStatus: - hist_minimisation_status_title += (str(min_status.value) + " = " + min_status.name + ", ") + hist_minimisation_status_title += str(min_status.value) + " = " + min_status.name + ", " hist_minimisation_status_title = hist_minimisation_status_title[:-2] hist_minimisation_status.SetTitle(hist_minimisation_status_title) set_object_style( @@ -221,17 +254,17 @@ def main(config): if pt_bin_to_process != -1: pt_bin_to_process_name_suffix = "_bin_" + str(pt_bin_to_process) - output_name_template = cfg['output']['file'].replace(".root", "") + pt_bin_to_process_name_suffix + ".root" + output_name_template = cfg["output"]["file"].replace(".root", "") + pt_bin_to_process_name_suffix + ".root" output = ROOT.TFile(os.path.join(cfg["output"]["directory"], output_name_template), "recreate") n_sets = len(hist_rawy) pt_axis_title = hist_rawy[0].GetXaxis().GetTitle() for ipt in range(hist_rawy[0].GetNbinsX()): - if pt_bin_to_process !=-1 and ipt+1 != pt_bin_to_process: + if pt_bin_to_process != -1 and ipt + 1 != pt_bin_to_process: continue all_vectors_monotonous = MinimisationStatus.Success pt_min = hist_rawy[0].GetXaxis().GetBinLowEdge(ipt + 1) pt_max = hist_rawy[0].GetXaxis().GetBinUpEdge(ipt + 1) - print(f"\n\nINFO: processing pt range {ipt+1} from {pt_min} to {pt_max} {pt_axis_title}") + print(f"\n\nINFO: processing pt range {ipt + 1} from {pt_min} to {pt_max} {pt_axis_title}") rawy, effp, effnp, unc_rawy, unc_effp, unc_effnp = (np.zeros(n_sets) for _ in range(6)) for iset, (hrawy, heffp, heffnp) in enumerate(zip(hist_rawy, hist_effp, hist_effnp)): @@ -245,16 +278,24 @@ def main(config): if cfg["minimisation"]["correlated"]: if not (np.all(rawy[1:] > rawy[:-1]) or np.all(rawy[1:] < rawy[:-1])): all_vectors_monotonous = MinimisationStatus.MonotonyViolation - print("\0\33[33mWARNING! main(): the raw yield vector is not monotonous. Check the input for stability.\0\33[0m") + print( + "\0\33[33m\nWARNING! main(): the raw yield vector is not monotonous. Check the input for stability.\n\0\33[0m" + ) print(f"raw yield vector elements = {rawy}\n") if not (np.all(unc_rawy[1:] > unc_rawy[:-1]) or np.all(unc_rawy[1:] < unc_rawy[:-1])): all_vectors_monotonous = MinimisationStatus.MonotonyViolation - print("\0\33[33mWARNING! main(): the raw yield uncertainties vector is not monotonous. Check the input for stability.\0\33[0m") + print( + "\0\33[33m\nWARNING! main(): the raw yield uncertainties vector is not monotonous. Check the input for stability.\n\0\33[0m" + ) print(f"raw yield uncertainties vector elements = {unc_rawy}\n") if not (np.all(effp[1:] > effp[:-1]) or np.all(effp[1:] < effp[:-1])): - sys.exit("\33[31mFatal error: the prompt efficiency vector is not monotonous. Check the input. Exit.\33[0m") + all_vectors_monotonous = MinimisationStatus.MonotonyViolation + print("\0\33[33m\nWARNING! the prompt efficiency vector is not monotonous. Check the input.\0\33[0m") + print(f"prompt efficiency vector elements = {effp}\n") if not (np.all(effnp[1:] > effnp[:-1]) or np.all(effnp[1:] < effnp[:-1])): - sys.exit("\33[31mFatal error: the nonprompt efficiency vector is not monotonous. Check the input. Exit.\33[0m") + all_vectors_monotonous = MinimisationStatus.MonotonyViolation + print("\0\33[33m\nWARNING! the nonprompt efficiency vector is not monotonous. Check the input.\0\33[0m") + print(f"nonprompt efficiency vector elements = {effnp}\n") minimiser = CutVarMinimiser(rawy, effp, effnp, unc_rawy, unc_effp, unc_effnp) status = minimiser.minimise_system(cfg["minimisation"]["correlated"]) @@ -290,7 +331,7 @@ def main(config): hist_frac_raw_nonprompt.SetBinContent(ipt + 1, raw_frac_nonprompt[0]) hist_frac_raw_nonprompt.SetBinError(ipt + 1, raw_frac_nonprompt[1]) - hist_bin_title = f"bin # {ipt+1}; {pt_axis_title}#in ({pt_min}; {pt_max})" + hist_bin_title = f"bin # {ipt + 1}; {pt_axis_title}#in ({pt_min}; {pt_max})" hist_bin_title_rawy = hist_bin_title if is_draw_title[PlotType.Rawy] else "" canv_rawy, histos_rawy, _leg_r = minimiser.plot_result(f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_rawy) @@ -298,32 +339,36 @@ def main(config): if is_save_to_root_file[ObjectToSave.Canvas]: canv_rawy.Write() if is_save_to_root_file[ObjectToSave.RawYield]: - for _, hist in histos_rawy.values(): + for hist in histos_rawy.values(): hist.Write() if is_save_canvas_as_macro[PlotType.Rawy]: - canv_rawy.SaveAs(f"canv_rawy_{ipt+1}.C") + canv_rawy.SaveAs(f"canv_rawy_{ipt + 1}.C") hist_bin_title_unc = hist_bin_title if is_draw_title[PlotType.Unc] else "" - canv_unc, histos_unc, _leg_unc = minimiser.plot_uncertainties(f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_unc) + canv_unc, histos_unc, _leg_unc = minimiser.plot_uncertainties( + f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_unc + ) output.cd() if is_save_to_root_file[ObjectToSave.Canvas]: canv_unc.Write() if is_save_to_root_file[ObjectToSave.Uncertainty]: - for _, hist in histos_unc.values(): + for hist in histos_unc.values(): hist.Write() if is_save_canvas_as_macro[PlotType.Unc]: - canv_unc.SaveAs(f"canv_unc_{ipt+1}.C") + canv_unc.SaveAs(f"canv_unc_{ipt + 1}.C") hist_bin_title_rel_unc = hist_bin_title if is_draw_title[PlotType.RelUnc] else "" - canv_rel_unc, histos_rel_unc, _leg_rel_unc = minimiser.plot_relative_uncertainties(f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_rel_unc) + canv_rel_unc, histos_rel_unc, _leg_rel_unc = minimiser.plot_relative_uncertainties( + f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_rel_unc + ) output.cd() if is_save_to_root_file[ObjectToSave.Canvas]: canv_rel_unc.Write() if is_save_to_root_file[ObjectToSave.Uncertainty]: - for _, hist in histos_rel_unc.values(): + for hist in histos_rel_unc.values(): hist.Write() if is_save_canvas_as_macro[PlotType.RelUnc]: - canv_rel_unc.SaveAs(f"canv_rel_unc_{ipt+1}.C") + canv_rel_unc.SaveAs(f"canv_rel_unc_{ipt + 1}.C") hist_bin_title_eff = hist_bin_title if is_draw_title[PlotType.Eff] else "" canv_eff, histos_eff, _leg_e = minimiser.plot_efficiencies(f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_eff) @@ -331,10 +376,10 @@ def main(config): if is_save_to_root_file[ObjectToSave.Canvas]: canv_eff.Write() if is_save_to_root_file[ObjectToSave.Efficiency]: - for _, hist in histos_eff.values(): + for hist in histos_eff.values(): hist.Write() if is_save_canvas_as_macro[PlotType.Eff]: - canv_eff.SaveAs(f"canv_eff_{ipt+1}.C") + canv_eff.SaveAs(f"canv_eff_{ipt + 1}.C") hist_bin_title_frac = hist_bin_title if is_draw_title[PlotType.Frac] else "" canv_frac, histos_frac, _leg_f = minimiser.plot_fractions(f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_frac) @@ -342,10 +387,10 @@ def main(config): if is_save_to_root_file[ObjectToSave.Canvas]: canv_frac.Write() if is_save_to_root_file[ObjectToSave.Fraction]: - for _, hist in histos_frac.values(): + for hist in histos_frac.values(): hist.Write() if is_save_canvas_as_macro[PlotType.Frac]: - canv_frac.SaveAs(f"canv_frac_{ipt+1}.C") + canv_frac.SaveAs(f"canv_frac_{ipt + 1}.C") hist_bin_title_cov = hist_bin_title if is_draw_title[PlotType.Cov] else "" canv_cov, histo_cov = minimiser.plot_cov_matrix(True, f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_cov) @@ -355,7 +400,7 @@ def main(config): if is_save_to_root_file[ObjectToSave.CorrelationMatrix]: histo_cov.Write() if is_save_canvas_as_macro[PlotType.Cov]: - canv_cov.SaveAs(f"canv_cov_{ipt+1}.C") + canv_cov.SaveAs(f"canv_cov_{ipt + 1}.C") else: print(f"Minimization for pT {pt_min}, {pt_max} not successful") hist_minimisation_status.SetBinContent(ipt + 1, MinimisationStatus.Fail) @@ -377,7 +422,7 @@ def main(config): canv_combined.cd(4) canv_cov.DrawClonePad() - output_name_template = output_name_template.replace('.root', '.pdf') + output_name_template = output_name_template.replace(".root", ".pdf") output_name_rawy_pdf = f"Distr_{output_name_template}" output_name_eff_pdf = f"Eff_{output_name_template}" diff --git a/PWGHF/D2H/Macros/cut_variation.py b/PWGHF/D2H/Macros/cut_variation.py index 8a6c31a96b4..a361da090d6 100644 --- a/PWGHF/D2H/Macros/cut_variation.py +++ b/PWGHF/D2H/Macros/cut_variation.py @@ -13,7 +13,7 @@ import numpy as np # pylint: disable=import-error import ROOT # pylint: disable=import-error -sys.path.insert(0, '..') +sys.path.insert(0, "..") from style_formatter import set_global_style, set_object_style @@ -23,6 +23,7 @@ class MinimisationStatus(IntEnum): MonotonyViolation = auto() Fail = auto() + # pylint: disable=too-many-instance-attributes class CutVarMinimiser: """ @@ -176,10 +177,9 @@ def minimise_system(self, correlated=True, precision=1.0e-8, max_iterations=100) ) if correlated and unc_row > 0 and unc_col > 0: - self.m_cov_sets[i_row, i_col] = min(unc_row **2, unc_col ** 2) + self.m_cov_sets[i_row, i_col] = min(unc_row**2, unc_col**2) else: - self.m_cov_sets[i_row, i_col] = unc_row ** 2 if i_row == i_col else 0.0 - + self.m_cov_sets[i_row, i_col] = unc_row**2 if i_row == i_col else 0.0 self.m_cov_sets = np.matrix(self.m_cov_sets) try: @@ -209,13 +209,17 @@ def minimise_system(self, correlated=True, precision=1.0e-8, max_iterations=100) m_corr_yields_old = np.copy(self.m_corr_yields) - print(f"INFO: number of processed iterations = {iteration+1}\n") + print(f"INFO: number of processed iterations = {iteration + 1}\n") minimisation_status = MinimisationStatus.Success if correlated: m_cov_sets_diag = np.diag(self.m_cov_sets) - if not (np.all(m_cov_sets_diag[1:] > m_cov_sets_diag[:-1]) or np.all(m_cov_sets_diag[1:] < m_cov_sets_diag[:-1])): + if not ( + np.all(m_cov_sets_diag[1:] > m_cov_sets_diag[:-1]) or np.all(m_cov_sets_diag[1:] < m_cov_sets_diag[:-1]) + ): minimisation_status = MinimisationStatus.MonotonyViolation - print("\033[33mWARNING! minimise_system(): the residual vector uncertainties elements are not monotonous. Check the input for stability.\033[0m") + print( + "\033[33mWARNING! minimise_system(): the residual vector uncertainties elements are not monotonous. Check the input for stability.\033[0m" + ) print(f"residual vector uncertainties elements = {np.sqrt(m_cov_sets_diag)}\n") # chi2 @@ -351,8 +355,7 @@ def get_raw_prompt_fraction(self, effacc_p, effacc_np): return f_p, f_p_unc - def get_raw_prompt_fraction_ext(self, corry_p, corry_np, unc_corry_p, - unc_corry_np, cov_p_np, effacc_p, effacc_np): + def get_raw_prompt_fraction_ext(self, corry_p, corry_np, unc_corry_p, unc_corry_np, cov_p_np, effacc_p, effacc_np): """ Helper function to get the raw prompt fraction given the efficiencies @@ -386,11 +389,7 @@ def get_raw_prompt_fraction_ext(self, corry_p, corry_np, unc_corry_p, # derivatives of prompt fraction wrt corr yields d_p = (effacc_p * (rawy_p + rawy_np) - effacc_p**2 * corry_p) / (rawy_p + rawy_np) ** 2 d_np = -effacc_np * rawy_p / (rawy_p + rawy_np) ** 2 - f_p_unc = np.sqrt( - d_p**2 * unc_corry_p**2 - + d_np**2 * unc_corry_np**2 - + 2 * d_p * d_np * cov_p_np - ) + f_p_unc = np.sqrt(d_p**2 * unc_corry_p**2 + d_np**2 * unc_corry_np**2 + 2 * d_p * d_np * cov_p_np) return f_p, f_p_unc @@ -417,8 +416,9 @@ def get_raw_nonprompt_fraction(self, effacc_p, effacc_np): return f_np, f_np_unc - def get_raw_nonprompt_fraction_ext(self, corry_p, corry_np, unc_corry_p, - unc_corry_np, cov_p_np, effacc_p, effacc_np): + def get_raw_nonprompt_fraction_ext( + self, corry_p, corry_np, unc_corry_p, unc_corry_np, cov_p_np, effacc_p, effacc_np + ): """ Helper function to get the raw non-prompt fraction given the efficiencies @@ -446,8 +446,9 @@ def get_raw_nonprompt_fraction_ext(self, corry_p, corry_np, unc_corry_p, """ - f_p, f_np_unc = self.get_raw_prompt_fraction_ext(corry_p, corry_np, unc_corry_p, - unc_corry_np, cov_p_np, effacc_p, effacc_np) + f_p, f_np_unc = self.get_raw_prompt_fraction_ext( + corry_p, corry_np, unc_corry_p, unc_corry_np, cov_p_np, effacc_p, effacc_np + ) f_np = 1 - f_p return f_np, f_np_unc @@ -628,7 +629,7 @@ def plot_cov_matrix(self, correlated=True, suffix="", title=""): padleftmargin=0.14, padbottommargin=0.12, padrightmargin=0.12, - padtopmargin = 0.075, + padtopmargin=0.075, palette=ROOT.kRainBow, ) @@ -683,7 +684,7 @@ def plot_efficiencies(self, suffix="", title=""): """ suffix = suffix.replace(".", "_") - set_global_style(padleftmargin=0.14, padbottommargin=0.12, titleoffset=1.2, padtopmargin = 0.075) + set_global_style(padleftmargin=0.14, padbottommargin=0.12, titleoffset=1.2, padtopmargin=0.075) hist_eff_prompt = ROOT.TH1F( f"hEffPromptVsCut{suffix}", @@ -776,7 +777,7 @@ def plot_fractions(self, suffix="", title=""): """ suffix = suffix.replace(".", "_") - set_global_style(padleftmargin=0.14, padbottommargin=0.12, titleoffset=1.2, padtopmargin = 0.075) + set_global_style(padleftmargin=0.14, padbottommargin=0.12, titleoffset=1.2, padtopmargin=0.075) hist_f_prompt = ROOT.TH1F( f"hFracPromptVsCut{suffix}", @@ -887,7 +888,7 @@ def plot_uncertainties(self, suffix="", title=""): for i_bin, (unc_rawy, unc_res) in enumerate(zip(self.unc_raw_yields, m_cov_sets_diag)): hist_raw_yield_unc.SetBinContent(i_bin + 1, unc_rawy) - hist_residual_unc.SetBinContent(i_bin+1, unc_res) + hist_residual_unc.SetBinContent(i_bin + 1, unc_res) set_object_style(hist_raw_yield_unc, color=ROOT.kRed + 1, fillstyle=0) set_object_style(hist_residual_unc, color=ROOT.kAzure + 4, fillstyle=0) @@ -923,7 +924,6 @@ def plot_uncertainties(self, suffix="", title=""): return canvas, histos, leg - # pylint: disable=no-member def plot_relative_uncertainties(self, suffix="", title=""): """ @@ -974,10 +974,19 @@ def plot_relative_uncertainties(self, suffix="", title=""): self.n_sets - 0.5, ) - for i_bin, (unc_rawy, rawy, unc_eff_prompt, eff_prompt, unc_eff_nonprompt, eff_nonprompt) in enumerate(zip(self.unc_raw_yields, self.raw_yields, self.unc_eff_prompt, self.eff_prompt, self.unc_eff_nonprompt, self.eff_nonprompt)): + for i_bin, (unc_rawy, rawy, unc_eff_prompt, eff_prompt, unc_eff_nonprompt, eff_nonprompt) in enumerate( + zip( + self.unc_raw_yields, + self.raw_yields, + self.unc_eff_prompt, + self.eff_prompt, + self.unc_eff_nonprompt, + self.eff_nonprompt, + ) + ): hist_raw_yield_rel_unc.SetBinContent(i_bin + 1, unc_rawy / rawy) - hist_eff_prompt_rel_unc.SetBinContent(i_bin+1, unc_eff_prompt / eff_prompt) - hist_eff_nonprompt_rel_unc.SetBinContent(i_bin+1, unc_eff_nonprompt / eff_nonprompt) + hist_eff_prompt_rel_unc.SetBinContent(i_bin + 1, unc_eff_prompt / eff_prompt) + hist_eff_nonprompt_rel_unc.SetBinContent(i_bin + 1, unc_eff_nonprompt / eff_nonprompt) set_object_style(hist_raw_yield_rel_unc, color=ROOT.kBlack, fillstyle=0) set_object_style(hist_eff_prompt_rel_unc, color=ROOT.kRed + 1, fillstyle=0) @@ -988,7 +997,12 @@ def plot_relative_uncertainties(self, suffix="", title=""): -0.5, 0.0, self.n_sets - 0.5, - max(hist_raw_yield_rel_unc.GetMaximum(), hist_eff_prompt_rel_unc.GetMaximum(), hist_eff_nonprompt_rel_unc.GetMaximum()) * 1.2, + max( + hist_raw_yield_rel_unc.GetMaximum(), + hist_eff_prompt_rel_unc.GetMaximum(), + hist_eff_nonprompt_rel_unc.GetMaximum(), + ) + * 1.2, ";cut set;relative unc.", ) leg = ROOT.TLegend(0.2, 0.75, 0.4, 0.85) @@ -1012,7 +1026,7 @@ def plot_relative_uncertainties(self, suffix="", title=""): histos = { "rawy": hist_raw_yield_rel_unc, "prompt": hist_eff_prompt_rel_unc, - "nonprompt": hist_eff_nonprompt_rel_unc + "nonprompt": hist_eff_nonprompt_rel_unc, } return canvas, histos, leg diff --git a/PWGHF/D2H/TableProducer/CMakeLists.txt b/PWGHF/D2H/TableProducer/CMakeLists.txt index 3492a98acff..ebc99c83782 100644 --- a/PWGHF/D2H/TableProducer/CMakeLists.txt +++ b/PWGHF/D2H/TableProducer/CMakeLists.txt @@ -118,5 +118,6 @@ o2physics_add_dpl_workflow(converter-reduced-3-prongs-ml o2physics_add_dpl_workflow(tree-creator-dstar-spin-align-mixing SOURCES treeCreatorDstarSpinAlignMixing.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) diff --git a/PWGHF/D2H/TableProducer/candidateCreatorB0Reduced.cxx b/PWGHF/D2H/TableProducer/candidateCreatorB0Reduced.cxx index df07d60011b..81a8e3d79b0 100644 --- a/PWGHF/D2H/TableProducer/candidateCreatorB0Reduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateCreatorB0Reduced.cxx @@ -65,6 +65,7 @@ struct HfCandidateCreatorB0Reduced { Configurable maxDZIni{"maxDZIni", 4., "reject (if>0) PCA candidate if tracks DZ exceeds threshold"}; Configurable minParamChange{"minParamChange", 1.e-3, "stop iterations if largest change of any B0 is smaller than this"}; Configurable minRelChi2Change{"minRelChi2Change", 0.9, "stop iterations is chi2/chi2old > this"}; + Configurable maxChi2{"maxChi2", 1.e9, "maximum chi2 for vertex reconstruction"}; // selection Configurable invMassWindowDPiTolerance{"invMassWindowDPiTolerance", 0.01, "invariant-mass window tolerance for DPi pair preselections (GeV/c2)"}; @@ -94,21 +95,27 @@ struct HfCandidateCreatorB0Reduced { } // Initialize fitter - df2.setPropagateToPCA(propagateToPCA); - df2.setMaxR(maxR); - df2.setMaxDZIni(maxDZIni); - df2.setMinParamChange(minParamChange); - df2.setMinRelChi2Change(minRelChi2Change); - df2.setUseAbsDCA(useAbsDCA); - df2.setWeightedFinalPCA(useWeightedFinalPCA); - - df3.setPropagateToPCA(propagateToPCA); - df3.setMaxR(maxR); - df3.setMaxDZIni(maxDZIni); - df3.setMinParamChange(minParamChange); - df3.setMinRelChi2Change(minRelChi2Change); - df3.setUseAbsDCA(useAbsDCA); - df3.setWeightedFinalPCA(useWeightedFinalPCA); + if (doprocessDataDplusPi || doprocessDataDplusPiWithDmesMl) { + df2.setPropagateToPCA(propagateToPCA); + df2.setMaxR(maxR); + df2.setMaxDZIni(maxDZIni); + df2.setMinParamChange(minParamChange); + df2.setMinRelChi2Change(minRelChi2Change); + df2.setUseAbsDCA(useAbsDCA); + df2.setWeightedFinalPCA(useWeightedFinalPCA); + df2.setMaxChi2(maxChi2); + } + + if (doprocessDataDstarPi || doprocessDataDstarPiWithDmesMl) { + df3.setPropagateToPCA(propagateToPCA); + df3.setMaxR(maxR); + df3.setMaxDZIni(maxDZIni); + df3.setMinParamChange(minParamChange); + df3.setMinRelChi2Change(minRelChi2Change); + df3.setUseAbsDCA(useAbsDCA); + df3.setWeightedFinalPCA(useWeightedFinalPCA); + df3.setMaxChi2(maxChi2); + } // histograms if (doprocessDataDplusPi || doprocessDataDplusPiWithDmesMl) { diff --git a/PWGHF/D2H/TableProducer/candidateCreatorBToJpsiReduced.cxx b/PWGHF/D2H/TableProducer/candidateCreatorBToJpsiReduced.cxx index 25430a1ce18..b2b550aefb1 100644 --- a/PWGHF/D2H/TableProducer/candidateCreatorBToJpsiReduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateCreatorBToJpsiReduced.cxx @@ -129,7 +129,7 @@ struct HfCandidateCreatorBToJpsiReduced { df4.setMaxDZIni(maxDZIni); df4.setMinParamChange(minParamChange); df4.setMinRelChi2Change(minRelChi2Change); - df3.setMaxChi2(maxChi2BhadVtx); + df4.setMaxChi2(maxChi2BhadVtx); df4.setUseAbsDCA(useAbsDCA); df4.setWeightedFinalPCA(useWeightedFinalPCA); df4.setMatCorrType(noMatCorr); diff --git a/PWGHF/D2H/TableProducer/candidateCreatorBplusReduced.cxx b/PWGHF/D2H/TableProducer/candidateCreatorBplusReduced.cxx index 2ef89ce34c5..0278f36fd50 100644 --- a/PWGHF/D2H/TableProducer/candidateCreatorBplusReduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateCreatorBplusReduced.cxx @@ -62,6 +62,7 @@ struct HfCandidateCreatorBplusReduced { Configurable maxDZIni{"maxDZIni", 4., "reject (if>0) PCA candidate if tracks DZ exceeds threshold"}; Configurable minParamChange{"minParamChange", 1.e-3, "stop iterations if largest change of any B+ is smaller than this"}; Configurable minRelChi2Change{"minRelChi2Change", 0.9, "stop iterations is chi2/chi2old > this"}; + Configurable maxChi2{"maxChi2", 1.e9, "maximum chi2 for vertex reconstruction"}; // selection Configurable invMassWindowD0PiTolerance{"invMassWindowD0PiTolerance", 0.01, "invariant-mass window tolerance for D0Pi pair preselections (GeV/c2)"}; @@ -101,6 +102,7 @@ struct HfCandidateCreatorBplusReduced { df2.setMinRelChi2Change(minRelChi2Change); df2.setUseAbsDCA(useAbsDCA); df2.setWeightedFinalPCA(useWeightedFinalPCA); + df2.setMaxChi2(maxChi2); // histograms registry.add("hMassBplusToD0Pi", "2-prong candidates;inv. mass (B^{+} #rightarrow #overline{D^{0}}#pi^{#plus} #rightarrow #pi^{#minus}K^{#plus}#pi^{#plus}) (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{500, 3., 8.}}}); @@ -198,7 +200,8 @@ struct HfCandidateCreatorBplusReduced { pVecD0[0], pVecD0[1], pVecD0[2], pVecPion[0], pVecPion[1], pVecPion[2], dcaD0.getY(), dcaPion.getY(), - std::sqrt(dcaD0.getSigmaY2()), std::sqrt(dcaPion.getSigmaY2())); + std::sqrt(dcaD0.getSigmaY2()), std::sqrt(dcaPion.getSigmaY2()), + candD0.xSecondaryVertex(), candD0.ySecondaryVertex(), candD0.zSecondaryVertex()); rowCandidateProngs(candD0.globalIndex(), trackPion.globalIndex()); diff --git a/PWGHF/D2H/TableProducer/candidateCreatorBsReduced.cxx b/PWGHF/D2H/TableProducer/candidateCreatorBsReduced.cxx index 8bed227e12d..a46d274b8d5 100644 --- a/PWGHF/D2H/TableProducer/candidateCreatorBsReduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateCreatorBsReduced.cxx @@ -62,6 +62,7 @@ struct HfCandidateCreatorBsReduced { Configurable maxDZIni{"maxDZIni", 4., "reject (if>0) PCA candidate if tracks DZ exceeds threshold"}; Configurable minParamChange{"minParamChange", 1.e-3, "stop iterations if largest change of any B0 is smaller than this"}; Configurable minRelChi2Change{"minRelChi2Change", 0.9, "stop iterations is chi2/chi2old > this"}; + Configurable maxChi2{"maxChi2", 1.e9, "maximum chi2 for vertex reconstruction"}; // selection Configurable invMassWindowDPiTolerance{"invMassWindowDPiTolerance", 0.01, "invariant-mass window tolerance for DsPi pair preselections (GeV/c2)"}; @@ -97,6 +98,7 @@ struct HfCandidateCreatorBsReduced { df2.setMinRelChi2Change(minRelChi2Change); df2.setUseAbsDCA(useAbsDCA); df2.setWeightedFinalPCA(useWeightedFinalPCA); + df2.setMaxChi2(maxChi2); // histograms registry.add("hMassBsToDsPi", "2-prong candidates;inv. mass (B^{0}_{s} #rightarrow D_{s}^{#minus}#pi^{#plus} #rightarrow #K^{#minus}K^{#plus}#pi^{#minus}#pi^{#plus}) (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{500, 3., 8.}}}); diff --git a/PWGHF/D2H/TableProducer/candidateCreatorCharmResoReduced.cxx b/PWGHF/D2H/TableProducer/candidateCreatorCharmResoReduced.cxx index b1affb1cbac..bd7e4f64cc4 100644 --- a/PWGHF/D2H/TableProducer/candidateCreatorCharmResoReduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateCreatorCharmResoReduced.cxx @@ -765,7 +765,7 @@ struct HfCandidateCreatorCharmResoReduced { V0TrRedTable const& candsV0Tr) { using BinningType = ColumnBinningPolicy; - BinningType const corrBinning{{cfgMixedEvent.zPoolBins, cfgMixedEvent.multPoolBins}, true}; + BinningType const corrBinning{{cfgMixedEvent.zPoolBins, cfgMixedEvent.multPoolBins}}; auto bachTuple = std::make_tuple(candsD, candsV0Tr); Pair const pairs{corrBinning, cfgMixedEvent.numberEventsMixed, cfgMixedEvent.numberEventsToSkip, collisions, bachTuple, &cache}; for (const auto& [collision1, bachDs, collision2, bachV0Trs] : pairs) { diff --git a/PWGHF/D2H/TableProducer/candidateCreatorLbReduced.cxx b/PWGHF/D2H/TableProducer/candidateCreatorLbReduced.cxx index 1b43d988285..64692dc72eb 100644 --- a/PWGHF/D2H/TableProducer/candidateCreatorLbReduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateCreatorLbReduced.cxx @@ -64,6 +64,7 @@ struct HfCandidateCreatorLbReduced { Configurable maxDZIni{"maxDZIni", 4., "reject (if>0) PCA candidate if tracks DZ exceeds threshold"}; Configurable minParamChange{"minParamChange", 1.e-3, "stop iterations if largest change of any Lb is smaller than this"}; Configurable minRelChi2Change{"minRelChi2Change", 0.9, "stop iterations is chi2/chi2old > this"}; + Configurable maxChi2{"maxChi2", 1.e9, "maximum chi2 for vertex reconstruction"}; // selection Configurable invMassWindowLcPiTolerance{"invMassWindowLcPiTolerance", 0.01, "invariant-mass window tolerance for LcPi pair preselections (GeV/c2)"}; @@ -96,6 +97,7 @@ struct HfCandidateCreatorLbReduced { df2.setMinRelChi2Change(minRelChi2Change); df2.setUseAbsDCA(useAbsDCA); df2.setWeightedFinalPCA(useWeightedFinalPCA); + df2.setMaxChi2(maxChi2); // histograms registry.add("hMassLambdaB0ToLcPi", "2-prong candidates;inv. mass (#Lambda_{b}^{0} #rightarrow #Lambda_{c}^{#plus}#pi^{#minus} #rightarrow pK^{#minus}#pi^{#plus}#pi^{#minus}) (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{500, 3., 8.}}}); diff --git a/PWGHF/D2H/TableProducer/candidateSelectorB0ToDPiReduced.cxx b/PWGHF/D2H/TableProducer/candidateSelectorB0ToDPiReduced.cxx index 89299a0136c..93ec3498cd0 100644 --- a/PWGHF/D2H/TableProducer/candidateSelectorB0ToDPiReduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateSelectorB0ToDPiReduced.cxx @@ -144,7 +144,7 @@ struct HfCandidateSelectorB0ToDPiReduced { labels[1 + SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/D2H/TableProducer/candidateSelectorBplusToD0PiReduced.cxx b/PWGHF/D2H/TableProducer/candidateSelectorBplusToD0PiReduced.cxx index e9088c68e93..5673db5971b 100644 --- a/PWGHF/D2H/TableProducer/candidateSelectorBplusToD0PiReduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateSelectorBplusToD0PiReduced.cxx @@ -19,11 +19,13 @@ #include "PWGHF/Core/SelectorCuts.h" #include "PWGHF/D2H/DataModel/ReducedDataModel.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" +#include "PWGHF/Utils/utilsAnalysis.h" #include "PWGHF/Utils/utilsPid.h" #include "Common/Core/TrackSelectorPID.h" #include +#include #include #include #include @@ -91,6 +93,7 @@ struct HfCandidateSelectorBplusToD0PiReduced { Configurable> onnxFileNames{"onnxFileNames", std::vector{"ModelHandler_onnx_BPLUSToD0Pi.onnx"}, "ONNX file names for each pT bin (if not from CCDB full path)"}; Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB"}; Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; + Configurable useD0TriggerMassCut{"useD0TriggerMassCut", false, "Flag to enable parametrize pT differential mass cut for triggered data"}; // variable that will store the value of selectionFlagD (defined in dataCreatorD0PiReduced.cxx) int mySelectionFlagD0 = -1; int mySelectionFlagD0bar = -1; @@ -101,6 +104,7 @@ struct HfCandidateSelectorBplusToD0PiReduced { o2::ccdb::CcdbApi ccdbApi; TrackSelectorPi selectorPion; + HfTrigger2ProngCuts hfTriggerCuts; using TracksPion = soa::Join; @@ -135,7 +139,7 @@ struct HfCandidateSelectorBplusToD0PiReduced { labels[1 + SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } @@ -154,6 +158,19 @@ struct HfCandidateSelectorBplusToD0PiReduced { } } + /// Utility function to apply the pT-differential mass cut for triggered data + /// on the D0 daughter of the B+ candidate + /// \param candidate is the B+ candidate + /// \param trackPi is the bachelor pion of the B+ candidate + /// \return true if the D0 daughter passes the mass selection + template + bool isDmesInMassRange(const T1& candidate, TracksPion::iterator const& trackPi) + { + const auto candD0 = candidate.template prong0_as(); + const auto invMassD0 = (trackPi.signed1Pt() < 0) ? candD0.invMassHypo0() : candD0.invMassHypo1(); + return isCandidateInMassRange(invMassD0, o2::constants::physics::MassD0, candidate.ptProng0(), hfTriggerCuts); + } + /// Main function to perform B+ candidate creation /// \param withDmesMl is the flag to use the table with ML scores for the D- daughter (only possible if present in the derived data) /// \param hfCandsBp B+ candidates @@ -189,6 +206,18 @@ struct HfCandidateSelectorBplusToD0PiReduced { continue; } + auto trackPi = hfCandBp.template prong1_as(); + + // pT-differential mass cut on the D0 daughter for triggered data + if (useD0TriggerMassCut && !isDmesInMassRange(hfCandBp, trackPi)) { + hfSelBplusToD0PiCandidate(statusBplus); + if (applyBplusMl) { + hfMlBplusToD0PiCandidate(outputMlNotPreselected); + } + // LOGF(info, "B+ candidate selection failed at D0-meson mass selection"); + continue; + } + if constexpr (WithDmesMl) { // we include it in the topological selections if (!HfHelper::selectionDmesMlScoresForBReduced(hfCandBp, cutsDmesMl, binsPtDmesMl)) { hfSelBplusToD0PiCandidate(statusBplus); @@ -206,7 +235,6 @@ struct HfCandidateSelectorBplusToD0PiReduced { } // track-level PID selection - auto trackPi = hfCandBp.template prong1_as(); if (pionPidMethod == PidMethod::TpcOrTof || pionPidMethod == PidMethod::TpcAndTof) { int pidTrackPi{TrackSelectorPID::Status::NotApplicable}; if (pionPidMethod == PidMethod::TpcOrTof) { @@ -250,7 +278,8 @@ struct HfCandidateSelectorBplusToD0PiReduced { void processSelection(HfRedCandBplus const& hfCandsBp, TracksPion const& pionTracks, - HfCandBpConfigs const& configs) + HfCandBpConfigs const& configs, + HfRed2Prongs const& /*candsD*/) { runSelection(hfCandsBp, pionTracks, configs); } // processSelection @@ -259,7 +288,8 @@ struct HfCandidateSelectorBplusToD0PiReduced { void processSelectionWithDmesMl(soa::Join const& hfCandsBp, TracksPion const& pionTracks, - HfCandBpConfigs const& configs) + HfCandBpConfigs const& configs, + HfRed2Prongs const& /*candsD*/) { runSelection(hfCandsBp, pionTracks, configs); } // processSelectionWithDmesMl diff --git a/PWGHF/D2H/TableProducer/candidateSelectorBsToDsPiReduced.cxx b/PWGHF/D2H/TableProducer/candidateSelectorBsToDsPiReduced.cxx index 988df046c06..7a9e71e26bc 100644 --- a/PWGHF/D2H/TableProducer/candidateSelectorBsToDsPiReduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateSelectorBsToDsPiReduced.cxx @@ -131,7 +131,7 @@ struct HfCandidateSelectorBsToDsPiReduced { labels[1 + SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/D2H/TableProducer/candidateSelectorLbToLcPiReduced.cxx b/PWGHF/D2H/TableProducer/candidateSelectorLbToLcPiReduced.cxx index 705f23ae97e..80b38931c13 100644 --- a/PWGHF/D2H/TableProducer/candidateSelectorLbToLcPiReduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateSelectorLbToLcPiReduced.cxx @@ -132,7 +132,7 @@ struct HfCandidateSelectorLbToLcPiReduced { labels[1 + SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/D2H/TableProducer/dataCreatorCharmHadPiReduced.cxx b/PWGHF/D2H/TableProducer/dataCreatorCharmHadPiReduced.cxx index 6d09d96b998..0080ceb0715 100644 --- a/PWGHF/D2H/TableProducer/dataCreatorCharmHadPiReduced.cxx +++ b/PWGHF/D2H/TableProducer/dataCreatorCharmHadPiReduced.cxx @@ -187,7 +187,8 @@ struct HfDataCreatorCharmHadPiReduced { Configurable maxR{"maxR", 200., "reject PCA's above this radius"}; Configurable maxDZIni{"maxDZIni", 4., "reject (if>0) PCA candidate if tracks DZ exceeds threshold"}; Configurable minParamChange{"minParamChange", 1.e-3, "stop iterations if largest change of any B0 is smaller than this"}; - Configurable minRelChi2Change{"minRelChi2Change", 0.9, "stop iterations is chi2/chi2old > this"}; + Configurable minRelChi2Change{"minRelChi2Change", 0.9, "stop iterations is chi2/chi2old < this"}; + Configurable maxChi2{"maxChi2", 1.e9, "maximum chi2 for vertex reconstruction"}; } vertexConfigurations; // selection struct : o2::framework::ConfigurableGroup { @@ -201,6 +202,7 @@ struct HfDataCreatorCharmHadPiReduced { struct : o2::framework::ConfigurableGroup { Configurable cpaMin{"cpaMin", 0., "Minimum cosine of pointing angle for B candidates"}; Configurable decLenMin{"decLenMin", 0., "Minimum decay length for B candidates"}; + Configurable impParProductMax{"impParProductMax", 10., "Maximum product of the impact parameters of the B daughters (cm^2)"}; } bhadronConfigurations; // HF flags struct : o2::framework::ConfigurableGroup { @@ -325,6 +327,7 @@ struct HfDataCreatorCharmHadPiReduced { df2.setMinRelChi2Change(vertexConfigurations.minRelChi2Change); df2.setUseAbsDCA(vertexConfigurations.useAbsDCA); df2.setWeightedFinalPCA(vertexConfigurations.useWeightedFinalPCA); + df2.setMaxChi2(vertexConfigurations.maxChi2); df2.setMatCorrType(noMatCorr); if (doprocessDplusPiData || doprocessDplusPiDataWithMl || doprocessDplusPiDataWithQvec || doprocessDplusPiDataWithMlAndQvec || doprocessDplusPiMc || doprocessDplusPiMcWithMl || doprocessDsPiData || doprocessDsPiDataWithMl || doprocessDsPiDataWithQvec || doprocessDsPiDataWithMlAndQvec || doprocessDsPiMc || doprocessDsPiMcWithMl || @@ -336,6 +339,7 @@ struct HfDataCreatorCharmHadPiReduced { df3.setMinRelChi2Change(vertexConfigurations.minRelChi2Change); df3.setUseAbsDCA(vertexConfigurations.useAbsDCA); df3.setWeightedFinalPCA(vertexConfigurations.useWeightedFinalPCA); + df3.setMaxChi2(vertexConfigurations.maxChi2); df3.setMatCorrType(noMatCorr); } @@ -1031,8 +1035,9 @@ struct HfDataCreatorCharmHadPiReduced { /// \param momentum is the B meson momentum /// \param secondaryVertex is the reconstructed secondary vertex /// \param collision is the reconstructed collision + /// \param impParProduct is the product of the impact parameters of the B daughters template - bool isBHadronSelected(const T1& momentum, const T2& secondaryVertex, const T3& collision) + bool isBHadronSelected(const T1& momentum, const T2& secondaryVertex, const T3& collision, const float impParProduct) { // B candidate CPA if (RecoDecay::cpa(std::array{collision.posX(), collision.posY(), collision.posZ()}, secondaryVertex, momentum) < bhadronConfigurations.cpaMin) { @@ -1044,6 +1049,11 @@ struct HfDataCreatorCharmHadPiReduced { return false; } + // B candidate product of daughter impact parameters + if (impParProduct > bhadronConfigurations.impParProductMax) { + return false; + } + return true; } @@ -1361,7 +1371,10 @@ struct HfDataCreatorCharmHadPiReduced { df2.getTrack(0).getPxPyPzGlo(pVecDHad); df2.getTrack(1).getPxPyPzGlo(pVecDau); pVecBHad = RecoDecay::pVec(pVecDHad, pVecDau); - if (!isBHadronSelected(pVecBHad, secondaryVertexBHad, collision)) { + o2::track::TrackParCov trackParCovCharmHadAtPv = trackParCovCharmHad; + std::array dcaCharmHad{}; + o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, trackParCovCharmHadAtPv, 2.f, noMatCorr, &dcaCharmHad); + if (!isBHadronSelected(pVecBHad, secondaryVertexBHad, collision, dcaCharmHad[0] * dcaPion[0])) { continue; } // fill Pion tracks table diff --git a/PWGHF/D2H/TableProducer/dataCreatorJpsiHadReduced.cxx b/PWGHF/D2H/TableProducer/dataCreatorJpsiHadReduced.cxx index 0bdc38965c7..55aa2f61dd5 100644 --- a/PWGHF/D2H/TableProducer/dataCreatorJpsiHadReduced.cxx +++ b/PWGHF/D2H/TableProducer/dataCreatorJpsiHadReduced.cxx @@ -249,7 +249,7 @@ struct HfDataCreatorJpsiHadReduced { labels[1 + aod::SelectionStep::RecoTopol] = "Skims & Topological selections"; labels[1 + aod::SelectionStep::RecoPID] = "Skims & Topological & PID selections"; static const AxisSpec axisSelections = {NumBinsSelections, 0.5, NumBinsSelections + 0.5, ""}; - registry.add("hSelectionsJpsi", "J/Psi selection;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelectionsJpsi", "J/Psi selection;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < NumBinsSelections; ++iBin) { registry.get(HIST("hSelectionsJpsi"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } @@ -266,7 +266,7 @@ struct HfDataCreatorJpsiHadReduced { } registry.add("hMassJpsi", "J/Psi mass;#it{M}_{#mu#mu} (GeV/#it{c}^{2});Counts", {HistType::kTH1D, {{600, 2.8, 3.4, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hPtJpsi", "J/Psi #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1D, {{(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hPtJpsi", "J/Psi #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1D, {{std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hCpaJpsi", "J/Psi cos#theta_{p};J/Psi cos#theta_{p};Counts", {HistType::kTH1D, {{200, -1., 1, "J/Psi cos#theta_{p}"}}}); std::shared_ptr hFitCandidatesJpsi = registry.add("hFitCandidatesJpsi", "Jpsi candidate counter", {HistType::kTH1D, {axisCands}}); std::shared_ptr hFitCandidatesBPlus = registry.add("hFitCandidatesBPlus", "hFitCandidatesBPlus candidate counter", {HistType::kTH1D, {axisCands}}); @@ -535,11 +535,11 @@ struct HfDataCreatorJpsiHadReduced { // Now we check that subtracting gammas from bremsstrahlung, the number of daughters of the J/Psi is 2 (µ+µ-) // In EVTGEN, bremmstralhung photons are considered as daughters of the J/Psi - std::vector jpsiDauIndices{}; + std::vector jpsiDauPdgs{}; for (const auto& dau : jpsiPart.template daughters_as()) { - jpsiDauIndices.push_back(dau.pdgCode()); + jpsiDauPdgs.push_back(dau.pdgCode()); } - if (jpsiDauIndices.size() - std::count(jpsiDauIndices.begin(), jpsiDauIndices.end(), kGamma) == nProngsJpsi) { + if (jpsiDauPdgs.size() - std::count(jpsiDauPdgs.begin(), jpsiDauPdgs.end(), kGamma) == nProngsJpsi) { flag = signB * o2::hf_decay::hf_cand_beauty::BplusToJpsiKToMuMuK; } } @@ -568,11 +568,11 @@ struct HfDataCreatorJpsiHadReduced { // Now we check that subtracting gammas from bremsstrahlung, the number of daughters of the J/Psi is 2 (e+e-) // In EVTGEN, bremmstralhung photons are considered as daughters of the J/Psi - std::vector jpsiDauIndices{}; + std::vector jpsiDauPdgs{}; for (const auto& dau : jpsiPart.template daughters_as()) { - jpsiDauIndices.push_back(dau.pdgCode()); + jpsiDauPdgs.push_back(dau.pdgCode()); } - if (jpsiDauIndices.size() - std::count(jpsiDauIndices.begin(), jpsiDauIndices.end(), kGamma) == nProngsJpsi) { + if (jpsiDauPdgs.size() - std::count(jpsiDauPdgs.begin(), jpsiDauPdgs.end(), kGamma) == nProngsJpsi) { flag = signB * o2::hf_decay::hf_cand_beauty::BplusToJpsiKToEEK; } } @@ -615,16 +615,16 @@ struct HfDataCreatorJpsiHadReduced { std::vector dauIndices{}; const int8_t signB = sign; // Since J/Psi is self-conjugate, we need to set acceptAntiParticles to false - if (RecoDecay::isMatchedMCGen(particlesMc, bParticle, signB * Pdg::kB0, std::array{+kJPsi, signB * Pdg::kK0Star892}, false, nullptr, 1, &dauIndices)) { + if (RecoDecay::isMatchedMCGen(particlesMc, bParticle, signB * Pdg::kB0, std::array{+kJPsi, signB * Pdg::kK0Star892}, false, nullptr, 1, &dauIndices)) { const auto& jpsiPart = particlesMc.rawIteratorAt(dauIndices[0]); // Now we check that subtracting gammas from bremsstrahlung, the number of daughters of the J/Psi is 2 (e+e-) // In EVTGEN, bremmstralhung photons are considered as daughters of the J/Psi - std::vector jpsiDauIndices{}; + std::vector jpsiDauPdgs{}; for (const auto& dau : jpsiPart.template daughters_as()) { - jpsiDauIndices.push_back(dau.pdgCode()); + jpsiDauPdgs.push_back(dau.pdgCode()); } - if (jpsiDauIndices.size() - std::count(jpsiDauIndices.begin(), jpsiDauIndices.end(), kGamma) == nProngsJpsi) { + if (jpsiDauPdgs.size() - std::count(jpsiDauPdgs.begin(), jpsiDauPdgs.end(), kGamma) == nProngsJpsi) { flag = signB * o2::hf_decay::hf_cand_beauty::B0ToJpsiPiKToMuMuPiK; indexRec = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[2], vecDaughtersB[3]}, Pdg::kK0Star892, std::array{+kKPlus, -kPiPlus}, true, &sign, 1); if (indexRec > -1) { @@ -652,16 +652,16 @@ struct HfDataCreatorJpsiHadReduced { const int8_t signB = sign; // Since J/Psi is self-conjugate, we need to set acceptAntiParticles to false - if (RecoDecay::isMatchedMCGen(particlesMc, bParticle, signB * Pdg::kB0, std::array{+kJPsi, signB * Pdg::kK0Star892}, false, nullptr, 1, &dauIndices)) { + if (RecoDecay::isMatchedMCGen(particlesMc, bParticle, signB * Pdg::kB0, std::array{+kJPsi, signB * Pdg::kK0Star892}, false, nullptr, 1, &dauIndices)) { const auto& jpsiPart = particlesMc.rawIteratorAt(dauIndices[0]); // Now we check that subtracting gammas from bremsstrahlung, the number of daughters of the J/Psi is 2 (e+e-) // In EVTGEN, bremmstralhung photons are considered as daughters of the J/Psi - std::vector jpsiDauIndices{}; + std::vector jpsiDauPdgs{}; for (const auto& dau : jpsiPart.template daughters_as()) { - jpsiDauIndices.push_back(dau.pdgCode()); + jpsiDauPdgs.push_back(dau.pdgCode()); } - if (jpsiDauIndices.size() - std::count(jpsiDauIndices.begin(), jpsiDauIndices.end(), kGamma) == nProngsJpsi) { + if (jpsiDauPdgs.size() - std::count(jpsiDauPdgs.begin(), jpsiDauPdgs.end(), kGamma) == nProngsJpsi) { flag = signB * o2::hf_decay::hf_cand_beauty::B0ToJpsiPiKToEEPiK; indexRec = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[2], vecDaughtersB[3]}, Pdg::kK0Star892, std::array{+kKPlus, -kPiPlus}, true, &sign, 1); if (indexRec > -1) { @@ -715,16 +715,16 @@ struct HfDataCreatorJpsiHadReduced { std::vector dauIndices{}; const int8_t signB = sign; // Since J/Psi and phi are self-conjugate, we need to set acceptAntiParticles to false - if (RecoDecay::isMatchedMCGen(particlesMc, bParticle, signB * Pdg::kBS, std::array{+kJPsi, +Pdg::kPhi}, false, nullptr, 1, &dauIndices)) { + if (RecoDecay::isMatchedMCGen(particlesMc, bParticle, signB * Pdg::kBS, std::array{+kJPsi, +Pdg::kPhi}, false, nullptr, 1, &dauIndices)) { const auto& jpsiPart = particlesMc.rawIteratorAt(dauIndices[0]); // Now we check that subtracting gammas from bremsstrahlung, the number of daughters of the J/Psi is 2 (e+e-) // In EVTGEN, bremmstralhung photons are considered as daughters of the J/Psi - std::vector jpsiDauIndices{}; + std::vector jpsiDauPdgs{}; for (const auto& dau : jpsiPart.template daughters_as()) { - jpsiDauIndices.push_back(dau.pdgCode()); + jpsiDauPdgs.push_back(dau.pdgCode()); } - if (jpsiDauIndices.size() - std::count(jpsiDauIndices.begin(), jpsiDauIndices.end(), kGamma) == nProngsJpsi) { + if (jpsiDauPdgs.size() - std::count(jpsiDauPdgs.begin(), jpsiDauPdgs.end(), kGamma) == nProngsJpsi) { flag = signB * o2::hf_decay::hf_cand_beauty::BsToJpsiKKToMuMuKK; indexRec = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[2], vecDaughtersB[3]}, Pdg::kPhi, std::array{+kKPlus, -kKPlus}, true, &sign, 1); if (indexRec > -1) { @@ -752,16 +752,16 @@ struct HfDataCreatorJpsiHadReduced { const int8_t signB = sign; // Since J/Psi and phi are self-conjugate, we need to set acceptAntiParticles to false - if (RecoDecay::isMatchedMCGen(particlesMc, bParticle, signB * Pdg::kBS, std::array{+kJPsi, +Pdg::kPhi}, false, nullptr, 1, &dauIndices)) { + if (RecoDecay::isMatchedMCGen(particlesMc, bParticle, signB * Pdg::kBS, std::array{+kJPsi, +Pdg::kPhi}, false, nullptr, 1, &dauIndices)) { const auto& jpsiPart = particlesMc.rawIteratorAt(dauIndices[0]); // Now we check that subtracting gammas from bremsstrahlung, the number of daughters of the J/Psi is 2 (e+e-) // In EVTGEN, bremmstralhung photons are considered as daughters of the J/Psi - std::vector jpsiDauIndices{}; + std::vector jpsiDauPdgs{}; for (const auto& dau : jpsiPart.template daughters_as()) { - jpsiDauIndices.push_back(dau.pdgCode()); + jpsiDauPdgs.push_back(dau.pdgCode()); } - if (jpsiDauIndices.size() - std::count(jpsiDauIndices.begin(), jpsiDauIndices.end(), kGamma) == nProngsJpsi) { + if (jpsiDauPdgs.size() - std::count(jpsiDauPdgs.begin(), jpsiDauPdgs.end(), kGamma) == nProngsJpsi) { flag = signB * o2::hf_decay::hf_cand_beauty::BsToJpsiKKToEEKK; indexRec = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[2], vecDaughtersB[3]}, Pdg::kPhi, std::array{+kKPlus, -kKPlus}, true, &sign, 1); if (indexRec > -1) { @@ -844,14 +844,21 @@ struct HfDataCreatorJpsiHadReduced { // Match generated particles. for (const auto& particle : mcParticlesPerMcColl) { - int8_t sign{0}, flag{0}, channel{0}; + int8_t sign = particle.pdgCode() > 0 ? 1 : -1; + int8_t flag{0}, channel{0}; if constexpr (DecChannel == DecayChannel::BplusToJpsiK) { // B+ → J/Psi K+ → (µ+µ-) K+ - if (RecoDecay::isMatchedMCGen(particlesMc, particle, Pdg::kBPlus, std::array{static_cast(Pdg::kJPsi), +kKPlus}, true, &sign)) { + // Since J/Psi is self-conjugate, we need to set acceptAntiParticles to false + if (RecoDecay::isMatchedMCGen(particlesMc, particle, sign * Pdg::kBPlus, std::array{static_cast(Pdg::kJPsi), sign * kKPlus}, false)) { // Match J/Psi -> µ+µ- - auto candJpsiMC = particlesMc.rawIteratorAt(particle.daughtersIds().front()); - // Printf("Checking J/Psi -> µ+µ-"); - if (RecoDecay::isMatchedMCGen(particlesMc, candJpsiMC, static_cast(Pdg::kJPsi), std::array{-kMuonMinus, +kMuonMinus}, true)) { + const auto& jpsiPart = particlesMc.rawIteratorAt(particle.daughtersIds().front()); + // Now we check that subtracting gammas from bremsstrahlung, the number of daughters of the J/Psi is 2 (µ+µ-) + // In EVTGEN, bremmstralhung photons are considered as daughters of the J/Psi + std::vector jpsiDauPdgs{}; + for (const auto& dau : jpsiPart.template daughters_as()) { + jpsiDauPdgs.push_back(dau.pdgCode()); + } + if (std::count(jpsiDauPdgs.begin(), jpsiDauPdgs.end(), kMuonMinus) == 1 && std::count(jpsiDauPdgs.begin(), jpsiDauPdgs.end(), -kMuonMinus) && jpsiDauPdgs.size() - std::count(jpsiDauPdgs.begin(), jpsiDauPdgs.end(), kGamma) == nProngsJpsi) { flag = sign * o2::hf_decay::hf_cand_beauty::BplusToJpsiKToMuMuK; } } @@ -880,16 +887,22 @@ struct HfDataCreatorJpsiHadReduced { ptProngs[1], yProngs[1], etaProngs[1], hfRejMap, centFT0C, centFT0M); } else if constexpr (DecChannel == DecayChannel::B0ToJpsiK0Star) { // B0 → J/Psi K*0 → (µ+µ-) (K+pi-) - if (RecoDecay::isMatchedMCGen(particlesMc, particle, Pdg::kB0, std::array{static_cast(Pdg::kJPsi), +kKPlus, -kPiPlus}, true, &sign, 2)) { + // Since J/Psi is self-conjugate, we need to set acceptAntiParticles to false + if (RecoDecay::isMatchedMCGen(particlesMc, particle, sign * Pdg::kB0, std::array{static_cast(Pdg::kJPsi), sign * kKPlus, -1 * sign * kPiPlus}, false, nullptr, 2)) { // Match J/Psi -> µ+µ- and K*0 -> K+pi- - auto candJpsiMC = particlesMc.rawIteratorAt(particle.daughtersIds().front()); - auto candK0StarMC = particlesMc.rawIteratorAt(particle.daughtersIds().back()); - // Printf("Checking J/Psi -> µ+µ- and K*0 -> K+pi-"); - if (RecoDecay::isMatchedMCGen(particlesMc, candJpsiMC, static_cast(Pdg::kJPsi), std::array{-kMuonMinus, +kMuonMinus}, true)) { + const auto& jpsiPart = particlesMc.rawIteratorAt(particle.daughtersIds().front()); + // Now we check that subtracting gammas from bremsstrahlung, the number of daughters of the J/Psi is 2 (µ+µ-) + // In EVTGEN bremmstralhung photons are considered as daughters of the J/Psi + std::vector jpsiDauPdgs{}; + for (const auto& dau : jpsiPart.template daughters_as()) { + jpsiDauPdgs.push_back(dau.pdgCode()); + } + if (std::count(jpsiDauPdgs.begin(), jpsiDauPdgs.end(), kMuonMinus) == 1 && std::count(jpsiDauPdgs.begin(), jpsiDauPdgs.end(), -kMuonMinus) && jpsiDauPdgs.size() - std::count(jpsiDauPdgs.begin(), jpsiDauPdgs.end(), kGamma) == nProngsJpsi) { flag = sign * o2::hf_decay::hf_cand_beauty::B0ToJpsiPiKToMuMuPiK; } + const auto& kStarPart = particlesMc.rawIteratorAt(particle.daughtersIds().back()); // Check K*0 -> K+pi- - if (RecoDecay::isMatchedMCGen(particlesMc, candK0StarMC, static_cast(Pdg::kK0Star892), std::array{+kKPlus, -kPiPlus}, true)) { + if (RecoDecay::isMatchedMCGen(particlesMc, kStarPart, static_cast(Pdg::kK0Star892), std::array{+kKPlus, -kPiPlus}, true)) { channel = o2::hf_decay::hf_cand_beauty::B0ToJpsiKstar0; } } @@ -918,16 +931,22 @@ struct HfDataCreatorJpsiHadReduced { ptProngs[1], yProngs[1], etaProngs[1], hfRejMap, centFT0C, centFT0M); } else if constexpr (DecChannel == DecayChannel::BsToJpsiPhi) { // Bs → J/Psi phi → (µ+µ-) (K+K-) - if (RecoDecay::isMatchedMCGen(particlesMc, particle, Pdg::kBS, std::array{static_cast(Pdg::kJPsi), +kKPlus, -kKPlus}, true, &sign, 2)) { + // Since J/Psi is self-conjugate, we need to set acceptAntiParticles to false + if (RecoDecay::isMatchedMCGen(particlesMc, particle, sign * Pdg::kBS, std::array{static_cast(Pdg::kJPsi), sign * kKPlus, -1 * sign * kKPlus}, false, nullptr, 2)) { // Match J/Psi -> µ+µ- and phi -> K+K- - auto candJpsiMC = particlesMc.rawIteratorAt(particle.daughtersIds().front()); - auto candPhiMC = particlesMc.rawIteratorAt(particle.daughtersIds().back()); - // Printf("Checking J/Psi -> µ+µ- and phi -> K+K-"); - if (RecoDecay::isMatchedMCGen(particlesMc, candJpsiMC, static_cast(Pdg::kJPsi), std::array{-kMuonMinus, +kMuonMinus}, true)) { + const auto& jpsiPart = particlesMc.rawIteratorAt(particle.daughtersIds().front()); + // Now we check that subtracting gammas from bremsstrahlung, the number of daughters of the J/Psi is 2 (µ+µ-) + // In EVTGEN, bremmstralhung photons are considered as daughters of the J/Psi + std::vector jpsiDauPdgs{}; + for (const auto& dau : jpsiPart.template daughters_as()) { + jpsiDauPdgs.push_back(dau.pdgCode()); + } + if (std::count(jpsiDauPdgs.begin(), jpsiDauPdgs.end(), kMuonMinus) == 1 && std::count(jpsiDauPdgs.begin(), jpsiDauPdgs.end(), -kMuonMinus) && jpsiDauPdgs.size() - std::count(jpsiDauPdgs.begin(), jpsiDauPdgs.end(), kGamma) == nProngsJpsi) { flag = sign * o2::hf_decay::hf_cand_beauty::BsToJpsiKKToMuMuKK; } + const auto& phiPart = particlesMc.rawIteratorAt(particle.daughtersIds().back()); // Check phi -> K+K- - if (RecoDecay::isMatchedMCGen(particlesMc, candPhiMC, static_cast(Pdg::kPhi), std::array{-kKPlus, +kKPlus}, true)) { + if (RecoDecay::isMatchedMCGen(particlesMc, phiPart, static_cast(Pdg::kPhi), std::array{-kKPlus, +kKPlus}, true)) { channel = o2::hf_decay::hf_cand_beauty::BsToJpsiPhi; } } diff --git a/PWGHF/D2H/TableProducer/mlBasedTrackSelector.cxx b/PWGHF/D2H/TableProducer/mlBasedTrackSelector.cxx index fd69e2bc816..5961630d3d2 100644 --- a/PWGHF/D2H/TableProducer/mlBasedTrackSelector.cxx +++ b/PWGHF/D2H/TableProducer/mlBasedTrackSelector.cxx @@ -190,9 +190,9 @@ struct HfTrackSelectorTagSelCollisions { } // set numerical value of the Run 2 trigger class - auto* const triggerAlias = std::find(aliasLabels, aliasLabels + kNaliases, triggerClassName.value.data()); - if (triggerAlias != aliasLabels + kNaliases) { - hfEvSel.triggerClass.value = std::distance(aliasLabels, triggerAlias); + auto* const triggerAlias = std::find(aliasLabels.begin(), aliasLabels.end(), triggerClassName.value.data()); + if (triggerAlias != aliasLabels.end()) { + hfEvSel.triggerClass.value = std::distance(aliasLabels.begin(), triggerAlias); } hfEvSel.init(registry, &zorroSummary); // collision monitoring @@ -312,6 +312,7 @@ struct HfTrackSelectorTagSelTracks { Configurable testAcknowledgement{"testAcknowledgement", false, "test acknowledgement"}; Configurable fillHistograms{"fillHistograms", true, "fill histograms"}; Configurable ptMinTrack{"ptMinTrack", 0.3f, "min. track pT entering the charm combinatorics"}; + Configurable etaMaxTrack{"etaMaxTrack", 0.8f, "max. track eta entering the charm combinatorics"}; Configurable enableTiming{"enableTiming", false, "fill hTiming with the CPU of the feature building and of each model evaluation (adds two clock reads per call)"}; // D+ model Configurable applyMlDplus{"applyMlDplus", true, "evaluate the D+ track model"}; @@ -589,7 +590,7 @@ struct HfTrackSelectorTagSelTracks { registry.fill(HIST("hPtNoCuts"), track.pt()); } - const bool passesQuality = track.pt() >= config.ptMinTrack && track.isGlobalTrackWoDCA(); + const bool passesQuality = track.pt() >= config.ptMinTrack && std::abs(track.eta()) <= config.etaMaxTrack && track.isGlobalTrackWoDCA(); if (passesQuality && !scoreCollision && config.fillHistograms) { registry.fill(HIST("hPtQualityRejColl"), track.pt()); } diff --git a/PWGHF/D2H/TableProducer/treeCreatorDstarSpinAlignMixing.cxx b/PWGHF/D2H/TableProducer/treeCreatorDstarSpinAlignMixing.cxx index e89d601aa3b..325b21a502e 100644 --- a/PWGHF/D2H/TableProducer/treeCreatorDstarSpinAlignMixing.cxx +++ b/PWGHF/D2H/TableProducer/treeCreatorDstarSpinAlignMixing.cxx @@ -17,6 +17,7 @@ /// \author Mingze li , CCNU/UniTo #include "PWGHF/Core/CentralityEstimation.h" +#include "PWGHF/Core/DecayChannels.h" #include "PWGHF/D2H/Utils/utilsFlow.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" @@ -24,8 +25,10 @@ #include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/Qvectors.h" +#include #include #include #include @@ -35,6 +38,16 @@ #include #include +#include +#include +#include // IWYU pragma: keep (do not replace with Math/Vector3Dfwd.h) +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include + +#include #include #include @@ -44,6 +57,7 @@ using namespace o2::framework::expressions; using namespace o2::hf_centrality; using namespace o2::hf_occupancy; using namespace o2::analysis::hf_flow_utils; +using namespace o2::constants::physics; namespace o2::aod { @@ -70,6 +84,7 @@ DECLARE_SOA_COLUMN(Pt, pt, float); DECLARE_SOA_COLUMN(Eta, eta, float); DECLARE_SOA_COLUMN(Phi, phi, float); DECLARE_SOA_COLUMN(Y, y, float); +DECLARE_SOA_COLUMN(CosThetaStarEP, cosThetaStarEP, float); DECLARE_SOA_COLUMN(MlProbDstarToD0PiBkg, mlProbDstarToD0PiBkg, float); DECLARE_SOA_COLUMN(MlProbDstarToD0PiPrompt, mlProbDstarToD0PiPrompt, float); DECLARE_SOA_COLUMN(MlProbDstarToD0PiNonPrompt, mlProbDstarToD0PiNonPrompt, float); @@ -112,24 +127,45 @@ DECLARE_SOA_TABLE(HfCandDstMix, "AOD", "HFCANDDSTMIX", mixing_dstar::MinNumTpcCls, mixing_dstar::GIndexCol, mixing_dstar::TimeStamp); +DECLARE_SOA_TABLE(HfMcParticles, "AOD", "HFMCPARTICLES", + mixing_dstar::Pt, + mixing_dstar::Eta, + mixing_dstar::Phi, + mixing_dstar::Y, + mixing_dstar::CosThetaStarEP, + mixing_dstar::Centrality); } // namespace o2::aod /// Writes the full information in an output TTree struct HfTreeCreatorDstarSpinAlignMixing { Produces rowCandidateMix; + Produces rowGenMcParticles; Configurable qVecDetector{"qVecDetector", 2, "Detector for Q vector estimation (FV0A: 0, FT0M: 1, FT0C: 2)"}; Configurable centEstimator{"centEstimator", 2, "Centrality estimator ((None: 0, FT0C: 2, FT0M: 3))"}; Configurable occEstimator{"occEstimator", 2, "If enabled, replace number of PV contributors with occupancy estimation (0: don't use, 1: ITS, 2: FT0C)"}; + Configurable activateAxisRotation{"activateAxisRotation", false, "Activate the rotation of the quantisation axis for cos(Theta*) calculation for phi dependence studies"}; + Configurable axisRotationAngle{"axisRotationAngle", 0.f, "Rotation angle of the quantisation axis for cos(Theta*) calculation for phi dependence studies (in rad)"}; + Configurable activatePartRecoDstar{"activatePartRecoDstar", false, "Activate the study of partly reconstructed D*+ -> D0 (-> KPiPi0) Pi decays"}; using CollsWithQVecs = soa::Join; using TracksWithExtra = soa::Join; using CandDstarWSelFlag = soa::Join; + + using CollisionsWithMcLabels = soa::SmallGroups>; + using CollisionsWithMcLabelsAndCent = soa::SmallGroups>; + using McParticlesDstarMatched = soa::Join; + using BCsInfo = soa::Join; + using FilteredCandDstarWSelFlagAndMl = soa::Filtered>; + using FilteredCandDstarWSelFlagAndMcAndMl = soa::Filtered>; Filter filterSelectDstarCandidates = aod::hf_sel_candidate_dstar::isSelDstarToD0Pi == true; Preslice dstarWithMlPerCollision = aod::hf_cand::collisionId; + Preslice dstarWithMcAndMlPerCollision = aod::hf_cand::collisionId; + + PresliceUnsorted colPerMcCollision = aod::mcparticle::mcCollisionId; void init(InitContext const&) { @@ -159,9 +195,72 @@ struct HfTreeCreatorDstarSpinAlignMixing { } } - template + /// \param ptCharmHad is the pt of the candidate + double sampleDeltaPhi(float ptCharmHad) + { + + double y0 = 6.49e-2; + double cons = 8.42e-1; + double coef = -3.78e-1; + double x0 = -6.37e-1; + double norm = -3.64e-1; + double mu = 4.68e-2; + double sigma = 1.513; + + double sigmoid = y0 + (cons - y0) / (1 + std::exp(-coef * (ptCharmHad - x0))); + double peak = norm * std::exp(-(ptCharmHad - mu) * (ptCharmHad - mu) / (2 * sigma * sigma)); + + double v2 = sigmoid + peak; + const double funcMax = 1.0 + 2.0 * std::abs(v2); + + while (true) { + double deltaPhi = gRandom->Uniform(0, constants::math::TwoPI); + double funcVal = gRandom->Uniform(0, funcMax); + + double funcDeltaPhi = 1.0 + 2.0 * v2 * std::cos(2.0 * deltaPhi); + + if (funcVal < funcDeltaPhi) + return deltaPhi; + } + return -1.; + } + + /// \param daughter is the daughter particle + /// \param ptMin is the minimum pt + /// \param etaMax is the maximum eta + /// \return true if daughter is in acceptance + template + bool isDaughterInAcceptance(Part const& daughter, float ptMin, float etaMax) + { + if (daughter.pt() < ptMin) { + return false; + } + if (std::abs(daughter.eta()) > etaMax) { + return false; + } + + return true; + } + + template void fillCandidateTable(CollType const& collision, const T& candidate, Trk const& /*tracks*/, BcType const& /*bcs*/) { + int8_t origin{RecoDecay::OriginType::None}; + bool partRecoDstar{false}; + if constexpr (DoMc) { + partRecoDstar = std::abs(candidate.flagMcMatchRec()) == hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPiPi0 && std::abs(candidate.flagMcMatchRecD0()) == hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiKPi0; + bool const signalDstar = std::abs(candidate.flagMcMatchRec()) == hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPi && std::abs(candidate.flagMcMatchRecD0()) == hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK; + if (!signalDstar && (!partRecoDstar || !activatePartRecoDstar)) { // this candidate is not signal and not partially reconstructed signal, skip + return; + } + origin = candidate.originMcRec(); + int const pdgBhadMother = candidate.pdgBhadMotherPart(); + // For unknown reasons there are charm hadrons coming directly from beauty diquarks without an intermediate B-hadron which have an unreasonable correlation between the pT of the charm hadron and the beauty mother. We also remove charm hadrons from quarkonia. + if (origin == RecoDecay::OriginType::NonPrompt && (pdgBhadMother == 5101 || pdgBhadMother == 5103 || pdgBhadMother == 5201 || pdgBhadMother == 5203 || pdgBhadMother == 5301 || pdgBhadMother == 5303 || pdgBhadMother == 5401 || pdgBhadMother == 5403 || pdgBhadMother == 5503 || pdgBhadMother == 553 || pdgBhadMother == 555 || pdgBhadMother == 557)) { // o2-linter: disable=pdg/explicit-code, magic-number (constants not in the PDG header) + return; + } + } + const auto bc = collision.template bc_as(); const int64_t timeStamp = bc.timestamp(); @@ -186,7 +285,12 @@ struct HfTreeCreatorDstarSpinAlignMixing { auto trackProng1 = candidate.template prong1_as(); auto trackProng2 = candidate.template prongPi_as(); getTrackingInfos(std::vector{trackProng0, trackProng1, trackProng2}, absEtaTrackMin, numItsClsMin, numTpcClsMin); - std::array const Qvector = getQvec(collision, qVecDetector.value); + std::array Qvector = getQvec(collision, qVecDetector.value); + if constexpr (DoMc) { + double const deltaPhi = sampleDeltaPhi(candidate.pt()); + double psi = candidate.phi() - deltaPhi; + Qvector = {static_cast(-std::sin(psi)), static_cast(std::cos(psi)), 0.f}; + } rowCandidateMix( massD0, @@ -214,6 +318,90 @@ struct HfTreeCreatorDstarSpinAlignMixing { timeStamp); } + /// \param mcParticle is the Mc particle + /// \param mcParticles is the table of Mc particles + /// \param centrality is the centrality of the collision + template + void fillGenParticleTable(Part const& mcParticle, Particles const& mcParticles, float const centrality) + { + int8_t origin{RecoDecay::OriginType::None}; + bool partRecoDstar{false}; + partRecoDstar = (std::abs(mcParticle.flagMcMatchGen()) == hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPiPi0) && (std::abs(mcParticle.flagMcMatchGenD0()) == hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiKPi0); + bool const signalDstar = (std::abs(mcParticle.flagMcMatchGen()) == hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPi) && (std::abs(mcParticle.flagMcMatchGenD0()) == hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK); + + if (!signalDstar && (!activatePartRecoDstar || !partRecoDstar)) { // this particle is not signal and not partially reconstructed signal, skip + return; + } + origin = mcParticle.originMcGen(); + float massDau{0.f}, massCharmHad{0.f}; + if (origin == RecoDecay::OriginType::NonPrompt) { + auto bHadMother = mcParticles.rawIteratorAt(mcParticle.idxBhadMotherPart() - mcParticles.offset()); + int const pdgBhadMother = std::abs(bHadMother.pdgCode()); + // For unknown reasons there are charm hadrons coming directly from beauty diquarks without an intermediate B-hadron which have an unreasonable correlation between the pT of the charm hadron and the beauty mother. We also remove charm hadrons from quarkonia. + if (pdgBhadMother == 5101 || pdgBhadMother == 5103 || pdgBhadMother == 5201 || pdgBhadMother == 5203 || pdgBhadMother == 5301 || pdgBhadMother == 5303 || pdgBhadMother == 5401 || pdgBhadMother == 5403 || pdgBhadMother == 5503 || pdgBhadMother == 553 || pdgBhadMother == 555 || pdgBhadMother == 557) { // o2-linter: disable=pdg/explicit-code, magic-number (constants not in the PDG header) + return; + } + } + std::vector listDaughters{}; + std::array const dauPdgs = {kPiPlus, o2::constants::physics::Pdg::kD0}; + RecoDecay::getDaughters(mcParticle, &listDaughters, dauPdgs, 1); + massDau = MassPiPlus; + massCharmHad = MassDStar; + + float const rapidity = mcParticle.y(); + if (std::abs(rapidity) > 1.f) { // we do not keep particles with |y| > 1 + return; + } + + bool areDauInAcc{true}; + float pxDau{-1000.f}, pyDau{-1000.f}, pzDau{-1000.f}; + for (const auto& dauIdx : listDaughters) { + auto dauPart = mcParticles.rawIteratorAt(dauIdx - mcParticles.offset()); + if (std::abs(dauPart.pdgCode()) == kPiPlus) { + pxDau = dauPart.px(); + pyDau = dauPart.py(); + pzDau = dauPart.pz(); + if (areDauInAcc) { + areDauInAcc = isDaughterInAcceptance(dauPart, 0.1, 0.8); + } + } else if (areDauInAcc) { // check also D0 daughters + std::vector listDaughtersD0{}; + std::array const dauPdgsD0 = {kPiPlus, -kKPlus}; + RecoDecay::getDaughters(mcParticle, &listDaughtersD0, dauPdgsD0, 1); + for (const auto& dauIdxD0 : listDaughtersD0) { + auto dauPartD0 = mcParticles.rawIteratorAt(dauIdxD0 - mcParticles.offset()); + areDauInAcc = isDaughterInAcceptance(dauPartD0, 0.3, 0.8); + } + } + } + + float const pxCharmHad = mcParticle.px(); + float const pyCharmHad = mcParticle.py(); + float const pzCharmHad = mcParticle.pz(); + ROOT::Math::PxPyPzMVector const fourVecDau = ROOT::Math::PxPyPzMVector(pxDau, pyDau, pzDau, massDau); + ROOT::Math::PxPyPzMVector const fourVecMother = ROOT::Math::PxPyPzMVector(pxCharmHad, pyCharmHad, pzCharmHad, massCharmHad); + ROOT::Math::Boost const boost{fourVecMother.BoostToCM()}; + ROOT::Math::PxPyPzMVector const fourVecDauCM = boost(fourVecDau); + ROOT::Math::XYZVector const threeVecDauCM = fourVecDauCM.Vect(); + + // EP analysis for MC + double const deltaPhi = sampleDeltaPhi(mcParticle.pt()); + double psi = mcParticle.phi() - deltaPhi; + ROOT::Math::XYZVector qVecNorm = ROOT::Math::XYZVector(-std::sin(psi), std::cos(psi), 0.f); + if (activateAxisRotation) { + qVecNorm = ROOT::Math::RotationZ(axisRotationAngle)(qVecNorm); + } + float const cosThetaStarEP = qVecNorm.Dot(threeVecDauCM) / std::sqrt(threeVecDauCM.Mag2()); + + rowGenMcParticles( + mcParticle.pt(), + mcParticle.eta(), + mcParticle.phi(), + mcParticle.y(), + cosThetaStarEP, + centrality); + } + void processData(CollsWithQVecs const& collisions, FilteredCandDstarWSelFlagAndMl const& dstarCandidates, TracksWithExtra const& tracks, @@ -223,11 +411,35 @@ struct HfTreeCreatorDstarSpinAlignMixing { auto thisCollId = collision.globalIndex(); auto groupedDstarCandidates = dstarCandidates.sliceBy(dstarWithMlPerCollision, thisCollId); for (const auto& dstarCandidate : groupedDstarCandidates) { - fillCandidateTable(collision, dstarCandidate, tracks, bcWithTimeStamps); + fillCandidateTable(collision, dstarCandidate, tracks, bcWithTimeStamps); } } } PROCESS_SWITCH(HfTreeCreatorDstarSpinAlignMixing, processData, "Process data", true); + + void processDstarMcWithMlInPbPb(aod::McCollisions::iterator const&, + McParticlesDstarMatched const& mcParticles, + CollisionsWithMcLabelsAndCent const& collisions, // this is grouped with SmallGroupsCollisionsWithMcLabels const& collisions, + FilteredCandDstarWSelFlagAndMcAndMl const& dstarCandidates, + TracksWithExtra const& tracks, + BCsInfo const& bcWithTimeStamps) + { + for (const auto& collision : collisions) { // loop over reco collisions associated to this gen collision + auto thisCollId = collision.globalIndex(); + auto groupedDstarCandidates = dstarCandidates.sliceBy(dstarWithMcAndMlPerCollision, thisCollId); + for (const auto& dstarCandidate : groupedDstarCandidates) { + fillCandidateTable(collision, dstarCandidate, tracks, bcWithTimeStamps); + } + } + rowGenMcParticles.reserve(mcParticles.size()); + + for (const auto& mcParticle : mcParticles) { + const auto& recoCollsPerMcColl = collisions.sliceBy(colPerMcCollision, mcParticle.mcCollision().globalIndex()); + const auto centrality = getCentralityGenColl(recoCollsPerMcColl, centEstimator); + fillGenParticleTable(mcParticle, mcParticles, centrality); + } + } + PROCESS_SWITCH(HfTreeCreatorDstarSpinAlignMixing, processDstarMcWithMlInPbPb, "Process Dstar candidates in PbPb MC with ML", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGHF/D2H/Tasks/CMakeLists.txt b/PWGHF/D2H/Tasks/CMakeLists.txt index 4d6a60998fc..a3726135da8 100644 --- a/PWGHF/D2H/Tasks/CMakeLists.txt +++ b/PWGHF/D2H/Tasks/CMakeLists.txt @@ -11,6 +11,7 @@ o2physics_add_dpl_workflow(task-b0 SOURCES taskB0.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -26,6 +27,7 @@ o2physics_add_dpl_workflow(task-b0-to-jpsi-k0-star-reduced o2physics_add_dpl_workflow(task-bplus SOURCES taskBplus.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -51,16 +53,19 @@ o2physics_add_dpl_workflow(task-bs-to-jpsi-phi-reduced o2physics_add_dpl_workflow(task-bs SOURCES taskBs.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-cd SOURCES taskCd.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-charm-polarisation SOURCES taskCharmPolarisation.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) @@ -76,41 +81,49 @@ o2physics_add_dpl_workflow(task-charm-reso-to-d-trk-reduced o2physics_add_dpl_workflow(task-d0 SOURCES taskD0.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::SGCutParHolder O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-directed-flow-charm-hadrons SOURCES taskDirectedFlowCharmHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-dplus SOURCES taskDplus.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::SGCutParHolder O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-ds SOURCES taskDs.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-dstar-to-d0-pi SOURCES taskDstarToD0Pi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-flattenicity-d0-lc SOURCES taskFlattenicityD0Lc.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::SGCutParHolder O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-flow-charm-hadrons SOURCES taskFlowCharmHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-lb SOURCES taskLb.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -121,56 +134,73 @@ o2physics_add_dpl_workflow(task-lb-reduced o2physics_add_dpl_workflow(task-lc SOURCES taskLc.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::SGCutParHolder O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-upc-lc SOURCES taskUpcLc.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::SGCutParHolder O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-lc-to-k0s-p SOURCES taskLcToK0sP.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-net-charm-fluctuations SOURCES taskNetCharmFluctuations.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-omegac0-to-omega-pi SOURCES taskOmegac0ToOmegaPi.cxx + REUSE_FROM HfCandidatesPCH + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(task-omegac0-to-omega-pi-qa + SOURCES taskOmegac0ToOmegaPiQa.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-pt-fluc-charm-hadrons SOURCES taskPtFlucCharmHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-sigmac SOURCES taskSigmac.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-sigmac-to-cascade SOURCES taskSigmacToCascade.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-xic SOURCES taskXic.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-xic-to-xi-pi-pi SOURCES taskXicToXiPiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-xic0-to-xi-pi SOURCES taskXic0ToXiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) diff --git a/PWGHF/D2H/Tasks/taskB0.cxx b/PWGHF/D2H/Tasks/taskB0.cxx index ca89758c2b9..b55eaedad4d 100644 --- a/PWGHF/D2H/Tasks/taskB0.cxx +++ b/PWGHF/D2H/Tasks/taskB0.cxx @@ -79,7 +79,7 @@ struct HfTaskB0 { void init(InitContext const&) { static const AxisSpec axisMassB0 = {300, 4.5, 6.0, "inv. mass (GeV/#it{c}^{2})"}; - static const AxisSpec axisPt = {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}; + static const AxisSpec axisPt = {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}; registry.add("hMass", "B^{0} candidates;inv. mass D^{#minus}#pi^{#plus} (GeV/#it{c}^{2});#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisMassB0, axisPt}}); registry.add("hDecLength", "B^{0} candidates;decay length (cm);entries", {HistType::kTH2F, {{200, 0., 0.4}, axisPt}}); diff --git a/PWGHF/D2H/Tasks/taskB0ToJpsiK0StarReduced.cxx b/PWGHF/D2H/Tasks/taskB0ToJpsiK0StarReduced.cxx index fdcf6be8b80..15d69d2f1bb 100644 --- a/PWGHF/D2H/Tasks/taskB0ToJpsiK0StarReduced.cxx +++ b/PWGHF/D2H/Tasks/taskB0ToJpsiK0StarReduced.cxx @@ -342,7 +342,7 @@ struct HfTaskB0ToJpsiK0StarReduced { const AxisSpec axisImpactPar{200, -0.05, 0.05}; const AxisSpec axisPtJpsi{100, 0., 50.}; const AxisSpec axisRapidity{100, -2., 2.}; - const AxisSpec axisPtB{(std::vector)binsPt, "#it{p}_{T}^{B^{0}} (GeV/#it{c})"}; + const AxisSpec axisPtB{500, 0., 50., "#it{p}_{T}^{B^{0}} (GeV/#it{c})"}; const AxisSpec axisPtK0Star{100, 0.f, 10.f}; registry.add("hMass", b0CandTitle + "inv. mass J/#Psi K*^{0} (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMassB0, axisPtB}}); @@ -353,7 +353,8 @@ struct HfTaskB0ToJpsiK0StarReduced { if (doprocessMc || doprocessMcWithB0Ml) { registry.add("hPtJpsiGen", mcParticleMatched + "J/#Psi #it{p}_{T}^{gen} (GeV/#it{c}); B^{0} " + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); registry.add("hPtK0StarGen", mcParticleMatched + "K*^{0} #it{p}_{T}^{gen} (GeV/#it{c}); B^{0} " + stringPt, {HistType::kTH2F, {axisPtK0Star, axisPtB}}); - registry.add("hYGenWithProngsInAcceptance", mcParticleMatched + "B^{0} #it{p}_{T}^{gen} (GeV/#it{c}); B^{0} #it{y}", {HistType::kTH2F, {axisPtProng, axisRapidity}}); + registry.add("hPtYGenSig", mcParticleMatched + "B^{0} #it{p}_{T}^{gen} (GeV/#it{c}); B^{0} #it{y}", {HistType::kTH2F, {axisPtB, axisRapidity}}); + registry.add("hPtYWithProngsInAccepanceGenSig", mcParticleMatched + "B^{0} #it{p}_{T}^{gen} (GeV/#it{c}); B^{0} #it{y}", {HistType::kTH2F, {axisPtB, axisRapidity}}); registry.add("hMassRecSig", b0CandMatch + "inv. mass J/#Psi K*^{0} (GeV/#it{c}^{2}); B^{0} " + stringPt, {HistType::kTH2F, {axisMassB0, axisPtB}}); registry.add("hMassJpsiRecSig", b0CandMatch + "inv. mass #mu^{+}#mu^{#minus} (GeV/#it{c}^{2}); J/#Psi " + stringPt, {HistType::kTH2F, {axisMassJpsi, axisPtJpsi}}); registry.add("hMassK0StarRecSig", b0CandMatch + "inv. mass K^{+}#pi^{#minus} (GeV/#it{c}^{2}); K*^{0} " + stringPt, {HistType::kTH2F, {axisMassK0Star, axisPtK0Star}}); @@ -679,9 +680,10 @@ struct HfTaskB0ToJpsiK0StarReduced { registry.fill(HIST("hPtJpsiGen"), ptProngs[0], ptParticle); registry.fill(HIST("hPtK0StarGen"), ptProngs[1], ptParticle); + registry.fill(HIST("hPtYGenSig"), ptParticle, yParticle); // generated B0 with daughters in geometrical acceptance if (prongsInAcc) { - registry.fill(HIST("hYGenWithProngsInAcceptance"), ptParticle, yParticle); + registry.fill(HIST("hPtYWithProngsInAccepanceGenSig"), ptParticle, yParticle); } } diff --git a/PWGHF/D2H/Tasks/taskBplus.cxx b/PWGHF/D2H/Tasks/taskBplus.cxx index f3223f55eba..98d12dc90f7 100644 --- a/PWGHF/D2H/Tasks/taskBplus.cxx +++ b/PWGHF/D2H/Tasks/taskBplus.cxx @@ -98,7 +98,7 @@ struct HfTaskBplus { const AxisSpec axisNormDecLength{40, 0., 20}; const AxisSpec axisEta{100, -2., 2.}; const AxisSpec axisRapidity{100, -2., 2.}; - const AxisSpec axisPtB{(std::vector)binsPt, "#it{p}_{T}^{B^{+}} (GeV/#it{c})"}; + const AxisSpec axisPtB{std::vector(binsPt), "#it{p}_{T}^{B^{+}} (GeV/#it{c})"}; registry.add("hMass", bPlusCandTitle + "inv. mass #bar{D^{0}}#pi^{+} (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMass, axisPtB}}); registry.add("hDecLength", bPlusCandTitle + "decay length (cm);" + stringPt, {HistType::kTH2F, {axisDecLength, axisPtB}}); @@ -127,8 +127,8 @@ struct HfTaskBplus { registry.add("hEtaProng1Gen", mcParticleMatched + "prong 1 #it{#eta}^{gen};" + stringPt, {HistType::kTH2F, {axisEta, axisPtB}}); registry.add("hPtGenWithRapidityBelowHalf", "MC particles (generated - |#it{y}^{gen}|<0.5);candidate #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{300, 0., 30.}}}); registry.add("hPtGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);candidate #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{300, 0., 30.}}}); - registry.add("hEtaGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);B^{0} candidate #it{#eta}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hYGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);B^{0} candidate #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hEtaGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);B^{0} candidate #it{#eta}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hYGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);B^{0} candidate #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); // histograms MC: Reco Level registry.add("hCPARecSig", bPlusCandMatch + "candidate cosine of pointing angle;" + stringPt, {HistType::kTH2F, {axisCPA, axisPtB}}); registry.add("hCPARecBg", bPlusCandUnmatch + "candidate cosine of pointing angle;" + stringPt, {HistType::kTH2F, {axisCPA, axisPtB}}); diff --git a/PWGHF/D2H/Tasks/taskBplusReduced.cxx b/PWGHF/D2H/Tasks/taskBplusReduced.cxx index 5913760dcea..190918846e4 100644 --- a/PWGHF/D2H/Tasks/taskBplusReduced.cxx +++ b/PWGHF/D2H/Tasks/taskBplusReduced.cxx @@ -15,8 +15,8 @@ /// \author Antonio Palasciano , Università degli Studi di Bari & INFN, Sezione di Bari #include "PWGHF/Core/HfHelper.h" -#include "PWGHF/Core/SelectorCuts.h" #include "PWGHF/D2H/DataModel/ReducedDataModel.h" +#include "PWGHF/D2H/Utils/utilsFlow.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" @@ -38,15 +38,16 @@ #include #include +#include #include #include -#include using namespace o2; using namespace o2::aod; using namespace o2::analysis; using namespace o2::framework; using namespace o2::framework::expressions; +using namespace o2::analysis::hf_flow_utils; namespace o2::aod { @@ -87,19 +88,26 @@ DECLARE_SOA_COLUMN(DecayLengthNormalised, decayLengthNormalised, float); DECLARE_SOA_COLUMN(DecayLengthXYNormalised, decayLengthXYNormalised, float); //! Normalised transverse decay length of candidate DECLARE_SOA_COLUMN(DecayLengthD, decayLengthD, float); //! Decay length of D-meson daughter candidate (cm) DECLARE_SOA_COLUMN(DecayLengthXYD, decayLengthXYD, float); //! Transverse decay length of D-meson daughter candidate (cm) +DECLARE_SOA_COLUMN(DecayLengthDToB, decayLengthDToB, float); //! Decay length of D-meson daughter candidate from B decay vertex (cm) +DECLARE_SOA_COLUMN(DecayLengthXYDToB, decayLengthXYDToB, float); //! Transverse decay length of D-meson daughter candidate from B decay vertex (cm) DECLARE_SOA_COLUMN(ImpactParameterD, impactParameterD, float); //! Impact parameter product of D-meson daughter candidate DECLARE_SOA_COLUMN(ImpactParameterBach, impactParameterBach, float); //! Impact parameter product of bachelor pion DECLARE_SOA_COLUMN(ImpactParameterProduct, impactParameterProduct, float); //! Impact parameter product of daughters DECLARE_SOA_COLUMN(Cpa, cpa, float); //! Cosine pointing angle of candidate DECLARE_SOA_COLUMN(CpaXY, cpaXY, float); //! Cosine pointing angle of candidate in transverse plane -DECLARE_SOA_COLUMN(CpaD, cpaD, float); //! Cosine pointing angle of D-meson daughter candidate -DECLARE_SOA_COLUMN(CpaXYD, cpaXYD, float); //! Cosine pointing angle in transverse plane of D-meson daughter candidate +DECLARE_SOA_COLUMN(CpaD, cpaD, float); //! Cosine pointing angle of D candidate to PV +DECLARE_SOA_COLUMN(CpaDXY, cpaDXY, float); //! Cosine pointing angle of D candidate to PV in transverse plane +DECLARE_SOA_COLUMN(CpaDToB, cpaDToB, float); //! Cosine pointing angle of D candidate to B decay vertex +DECLARE_SOA_COLUMN(CpaDToBXY, cpaDToBXY, float); //! Cosine pointing angle of D candidate to B decay vertex in transverse plane +DECLARE_SOA_COLUMN(ScalarProd, scalarProd, float); //! Scalar product of B candidate +DECLARE_SOA_COLUMN(Centrality, centrality, float); //! Centrality of the collision DECLARE_SOA_COLUMN(MaxNormalisedDeltaIP, maxNormalisedDeltaIP, float); //! Maximum normalized difference between measured and expected impact parameter of candidate prongs DECLARE_SOA_COLUMN(MlScoreSig, mlScoreSig, float); //! ML score for signal class DECLARE_SOA_COLUMN(FlagWrongCollision, flagWrongCollision, int8_t); //! Flag for association with wrong collision } // namespace hf_cand_bplus_lite DECLARE_SOA_TABLE(HfRedCandBpLites, "AOD", "HFREDCANDBPLITE", //! Table with some B+ properties + // B meson features hf_cand_bplus_lite::M, hf_cand_bplus_lite::Pt, hf_cand_bplus_lite::Eta, @@ -116,14 +124,20 @@ DECLARE_SOA_TABLE(HfRedCandBpLites, "AOD", "HFREDCANDBPLITE", //! Table with som hf_cand_bplus_lite::MaxNormalisedDeltaIP, hf_cand_bplus_lite::MlScoreSig, hf_sel_candidate_bplus::IsSelBplusToD0Pi, + hf_cand_bplus_lite::ScalarProd, + hf_cand_bplus_lite::Centrality, // D meson features hf_cand_bplus_lite::MD, hf_cand_bplus_lite::PtD, hf_cand_bplus_lite::DecayLengthD, hf_cand_bplus_lite::DecayLengthXYD, - hf_cand_bplus_lite::ImpactParameterD, + hf_cand_bplus_lite::DecayLengthDToB, + hf_cand_bplus_lite::DecayLengthXYDToB, hf_cand_bplus_lite::CpaD, - hf_cand_bplus_lite::CpaXYD, + hf_cand_bplus_lite::CpaDXY, + hf_cand_bplus_lite::CpaDToB, + hf_cand_bplus_lite::CpaDToBXY, + hf_cand_bplus_lite::ImpactParameterD, hf_cand_bplus_lite::PtDmesProngMin, hf_cand_bplus_lite::AbsEtaDmesProngMin, hf_cand_bplus_lite::ItsNClsDmesProngMin, @@ -170,13 +184,14 @@ DECLARE_SOA_TABLE(HfRedBpMcCheck, "AOD", "HFREDBPMCCHECK", //! Table with MC dec } // namespace o2::aod // string definitions, used for histogram axis labels -const TString stringPt = "#it{p}_{T} (GeV/#it{c})"; -const TString stringPtD = "#it{p}_{T}(D0) (GeV/#it{c});"; -const TString bPlusCandTitle = "B+ candidates;"; +const TString stringPt = "#it{p}_{T}(B^{+}) (GeV/#it{c});"; +const TString stringPtGen = "#it{p}_{T}^{gen}(B^{+}) (GeV/#it{c});"; +const TString stringPtD = "#it{p}_{T}(D^{0}) (GeV/#it{c});"; +const TString bPlusCandTitle = "B^{+} candidates;"; const TString entries = "entries"; -const TString bPlusCandMatch = "B+ candidates (matched);"; -const TString bPlusCandUnmatch = "B+ candidates (unmatched);"; -const TString mcParticleMatched = "MC particles (matched);"; +const TString bPlusCandMatch = "B^{+} candidates (matched);"; +const TString bPlusCandUnmatch = "B^{+} candidates (unmatched);"; +const TString bPlusPartGen = "B^{+} particles (generated);"; /// B+ analysis task struct HfTaskBplusReduced { @@ -184,18 +199,22 @@ struct HfTaskBplusReduced { Produces hfRedBpMcCheck; Configurable selectionFlagBplus{"selectionFlagBplus", 1, "Selection Flag for Bplus"}; - Configurable yCandGenMax{"yCandGenMax", 0.5, "max. gen particle rapidity"}; - Configurable yCandRecoMax{"yCandRecoMax", 0.8, "max. cand. rapidity"}; - Configurable etaTrackMax{"etaTrackMax", 0.8, "max. track pseudo-rapidity"}; - Configurable ptTrackMin{"ptTrackMin", 0.1, "min. track transverse momentum"}; + Configurable yCandGenMax{"yCandGenMax", 0.5, "max. gen particle rapidity"}; + Configurable yCandRecoMax{"yCandRecoMax", 0.8, "max. cand. rapidity"}; + Configurable etaTrackMax{"etaTrackMax", 0.8, "max. track pseudo-rapidity for acceptance calculation"}; + Configurable ptTrackMin{"ptTrackMin", 0.1, "min. track transverse momentum for acceptance calculation"}; Configurable fillHistograms{"fillHistograms", true, "Flag to enable histogram filling"}; Configurable fillSparses{"fillSparses", false, "Flag to enable sparse filling"}; Configurable fillTree{"fillTree", false, "Flag to enable tree filling"}; Configurable fillBackground{"fillBackground", false, "Flag to enable filling of background histograms/sparses/tree (only MC)"}; Configurable downSampleBkgFactor{"downSampleBkgFactor", 1., "Fraction of background candidates to keep for ML trainings"}; Configurable ptMaxForDownSample{"ptMaxForDownSample", 10., "Maximum pt for the application of the downsampling factor"}; - Configurable> binsPt{"binsPt", std::vector{hf_cuts_bplus_to_d0_pi::vecBinsPt}, "pT bin limits"}; + Configurable centralityEstimator{"centralityEstimator", 1, "Centrality estimator (FT0M: 0, FT0C: 1)"}; + Configurable harmonic{"harmonic", 2, "harmonic number"}; + Configurable qVecDetector{"qVecDetector", 3, "Detector for Q vector estimation (FT0M: 1, FT0A: 2, FT0C: 3, TPC Pos: 4, TPC Neg: 5, TPC Tot: 6)"}; + using CollisionsWithCents = soa::Join; + using CollisionsWithCentsQvecs = soa::Join; using TracksPion = soa::Join; using CandsD0 = soa::Join; @@ -205,7 +224,7 @@ struct HfTaskBplusReduced { void init(InitContext&) { - std::array processFuncData{doprocessData, doprocessDataWithDmesMl, doprocessDataWithBplusMl}; + std::array processFuncData{doprocessData, doprocessDataWithDmesMl, doprocessDataWithDmesMlScalarProd, doprocessDataWithBplusMl, doprocessDataWithBplusMlScalarProd}; if ((std::accumulate(processFuncData.begin(), processFuncData.end(), 0)) > 1) { LOGP(fatal, "Only one process function for data can be enabled at a time."); } @@ -215,131 +234,125 @@ struct HfTaskBplusReduced { } const AxisSpec axisMlScore{100, 0.f, 1.f}; - const AxisSpec axisMassBplus{150, 4.5, 6.0}; + const AxisSpec axisMassBplus{300, 4.5f, 6.0f}; const AxisSpec axisMassD0{300, 1.75f, 2.05f}; - const AxisSpec axisCpa{120, -1.1, 1.1}; - const AxisSpec axisCpaD{101, 0.9, 1.01}; - const AxisSpec axisPtProng{100, 0., 10.}; - const AxisSpec axisD0Prong{200, -0.05, 0.05}; - const AxisSpec axisImpParProd{200, -0.001, 0.001}; - const AxisSpec axisDecLength{100, 0., 0.5}; - const AxisSpec axisNormDecLength{40, 0., 20}; - const AxisSpec axisEta{100, -2., 2.}; - const AxisSpec axisRapidity{100, -2., 2.}; - const AxisSpec axisPtD0{100, 0., 50.}; - const AxisSpec axisPtB{(std::vector)binsPt, "#it{p}_{T}^{B^{+}} (GeV/#it{c})"}; + const AxisSpec axisDecayLength{200, 0.f, 0.4f}; + const AxisSpec axisNormDecayLength{100, 0.f, 50.f}; + const AxisSpec axisDca{100, -0.05f, 0.05f}; + const AxisSpec axisCosp{110, 0.f, 1.1f}; + const AxisSpec axisEta{30, -1.5f, 1.5f}; + const AxisSpec axisImpParProd{100, -1.e-3, 1.e-3}; + const AxisSpec axisPtBplus{100, 0.f, 50.f}; + const AxisSpec axisPtD0{100, 0.f, 50.f}; const AxisSpec axisPtPi{100, 0.f, 10.f}; - if (doprocessData || doprocessDataWithDmesMl || doprocessDataWithBplusMl) { + if (doprocessData || doprocessDataWithDmesMl || doprocessDataWithDmesMlScalarProd || doprocessDataWithBplusMl || doprocessDataWithBplusMlScalarProd) { if (fillHistograms) { - registry.add("hMass", bPlusCandTitle + "inv. mass #bar{D^{0}}#pi^{+} (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMassBplus, axisPtB}}); - registry.add("hPtCand", bPlusCandTitle + "candidate #it{p}_{T} (GeV/#it{c});" + entries, {HistType::kTH1F, {{1000, 0., 50.}}}); - registry.add("hPtProng0", bPlusCandTitle + "prong 0 #it{p}_{T}^{gen} (GeV/#it{c});" + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); - registry.add("hPtProng1", bPlusCandTitle + "prong 1 #it{p}_{T}^{gen} (GeV/#it{c});" + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); - registry.add("hDecLength", bPlusCandTitle + "decay length (cm);" + stringPt, {HistType::kTH2F, {axisDecLength, axisPtB}}); - registry.add("hDecLengthXy", bPlusCandTitle + "decay length xy (cm);" + stringPt, {HistType::kTH2F, {axisDecLength, axisPtB}}); - registry.add("hNormDecLengthXy", bPlusCandTitle + "Norm. decay length xy (cm);" + stringPt, {HistType::kTH2F, {axisNormDecLength, axisPtB}}); - registry.add("hd0Prong0", bPlusCandTitle + "prong 0 DCAxy to prim. vertex (cm);" + stringPt, {HistType::kTH2F, {axisD0Prong, axisPtB}}); - registry.add("hd0Prong1", bPlusCandTitle + "prong 1 DCAxy to prim. vertex (cm);" + stringPt, {HistType::kTH2F, {axisD0Prong, axisPtB}}); - registry.add("hCpa", bPlusCandTitle + "candidate cosine of pointing angle;" + stringPt, {HistType::kTH2F, {axisCpa, axisPtB}}); - registry.add("hCpaXy", bPlusCandTitle + "candidate cosine of pointing angle xy;" + stringPt, {HistType::kTH2F, {axisCpa, axisPtB}}); - registry.add("hEta", bPlusCandTitle + "candidate #it{#eta};" + stringPt, {HistType::kTH2F, {axisEta, axisPtB}}); - registry.add("hRapidity", bPlusCandTitle + "candidate #it{y};" + stringPt, {HistType::kTH2F, {axisRapidity, axisPtB}}); - registry.add("hd0d0", bPlusCandTitle + "candidate product of DCAxy to prim. vertex (cm^{2});" + stringPt, {HistType::kTH2F, {axisImpParProd, axisPtB}}); - registry.add("hInvMassD0", bPlusCandTitle + "prong0, D0 inv. mass (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMassD0, axisPtD0}}); - registry.add("hDecLengthD0", bPlusCandTitle + "D^{0} candidate decay length (cm);#it{p}_{T}(D^{0}) (GeV/#it{c});entries", {HistType::kTH2F, {axisDecLength, axisPtD0}}); - registry.add("hDecLengthXyD0", bPlusCandTitle + "decay length XY (cm);#it{p}_{T}(D^{0}) (GeV/#it{c});entries", {HistType::kTH2F, {axisDecLength, axisPtD0}}); - registry.add("hCpaD0", bPlusCandTitle + "D^{0} candidate cos(#vartheta_{P});#it{p}_{T}(D^{0}) (GeV/#it{c});entries", {HistType::kTH2F, {axisCpaD, axisPtD0}}); - registry.add("hCpaXyD0", bPlusCandTitle + "D^{0} candidate cos(#vartheta_{P}^{XY});#it{p}_{T}(D^{0}) (GeV/#it{c});entries", {HistType::kTH2F, {axisCpaD, axisPtD0}}); + registry.add("hMass", bPlusCandTitle + stringPt + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});" + entries, {HistType::kTH2F, {axisPtBplus, axisMassBplus}}); + registry.add("hDecLength", bPlusCandTitle + stringPt + "B^{+} candidate decay length (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDecayLength}}); + registry.add("hDecLengthXy", bPlusCandTitle + stringPt + "decay length XY (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDecayLength}}); + registry.add("hNormDecLengthXy", bPlusCandTitle + stringPt + "B^{+} candidate norm. decay length XY (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisNormDecayLength}}); + registry.add("hDcaProng0", bPlusCandTitle + stringPt + "prong 0 (D^{0}) DCAxy to prim. vertex (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDca}}); + registry.add("hDcaProng1", bPlusCandTitle + stringPt + "prong 1 (#pi^{#plus}) DCAxy to prim. vertex (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDca}}); + registry.add("hCosp", bPlusCandTitle + stringPt + "B^{+} candidate cos(#vartheta_{P});" + entries, {HistType::kTH2F, {axisPtBplus, axisCosp}}); + registry.add("hCospXy", bPlusCandTitle + stringPt + "B^{+} candidate cos(#vartheta_{P}^{XY});" + entries, {HistType::kTH2F, {axisPtBplus, axisCosp}}); + registry.add("hEta", bPlusCandTitle + stringPt + "B^{+} candidate #it{#eta};" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hRapidity", bPlusCandTitle + stringPt + "B^{+} candidate #it{y};" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hImpParProd", bPlusCandTitle + stringPt + "B^{+} candidate impact parameter product;" + entries, {HistType::kTH2F, {axisPtBplus, axisImpParProd}}); + registry.add("hPtProng0", bPlusCandTitle + stringPt + stringPtD + entries, {HistType::kTH2F, {axisPtBplus, axisPtD0}}); + registry.add("hPtProng1", bPlusCandTitle + stringPt + "#it{p}_{T}(#pi^{#plus}) (GeV/#it{c});" + entries, {HistType::kTH2F, {axisPtBplus, axisPtPi}}); + registry.add("hInvMassD", bPlusCandTitle + stringPtD + "prong0, #it{M}(K#pi) (GeV/#it{c}^{2});" + entries, {HistType::kTH2F, {axisPtD0, axisMassD0}}); + registry.add("hDecLengthD", bPlusCandTitle + stringPtD + "D^{0} candidate decay length (cm);" + entries, {HistType::kTH2F, {axisPtD0, axisDecayLength}}); + registry.add("hDecLengthXyD", bPlusCandTitle + stringPtD + "decay length XY (cm);" + entries, {HistType::kTH2F, {axisPtD0, axisDecayLength}}); + registry.add("hCospD", bPlusCandTitle + stringPtD + "D^{0} candidate cos(#vartheta_{P});" + entries, {HistType::kTH2F, {axisPtD0, axisCosp}}); + registry.add("hCospXyD", bPlusCandTitle + stringPtD + "D^{0} candidate cos(#vartheta_{P}^{XY});" + entries, {HistType::kTH2F, {axisPtD0, axisCosp}}); // ML scores of D0 daughter - if (doprocessDataWithDmesMl) { - registry.add("hMlScoreBkgD", bPlusCandTitle + "#it{p}_{T}(D0) (GeV/#it{c});prong0, D0 ML background score;entries", {HistType::kTH2F, {axisPtD0, axisMlScore}}); - registry.add("hMlScorePromptD", bPlusCandTitle + "#it{p}_{T}(D0) (GeV/#it{c});prong0, D0 ML prompt score;entries", {HistType::kTH2F, {axisPtD0, axisMlScore}}); - registry.add("hMlScoreNonPromptD", bPlusCandTitle + "#it{p}_{T}(D0) (GeV/#it{c});prong0, D0 ML nonprompt score;entries", {HistType::kTH2F, {axisPtD0, axisMlScore}}); + if (doprocessDataWithDmesMl || doprocessDataWithDmesMlScalarProd) { + registry.add("hMlScoreBkgD", bPlusCandTitle + stringPtD + "prong0, D^{0} ML background score;" + entries, {HistType::kTH2F, {axisPtD0, axisMlScore}}); + registry.add("hMlScorePromptD", bPlusCandTitle + stringPtD + "prong0, D^{0} ML prompt score;" + entries, {HistType::kTH2F, {axisPtD0, axisMlScore}}); + registry.add("hMlScoreNonPromptD", bPlusCandTitle + stringPtD + "prong0, D^{0} ML nonprompt score;" + entries, {HistType::kTH2F, {axisPtD0, axisMlScore}}); } // ML scores of B+ candidate - if (doprocessDataWithBplusMl) { - registry.add("hMlScoreSigBplus", bPlusCandTitle + "#it{p}_{T} (GeV/#it{c});prong0, B^{+} ML signal score;entries", {HistType::kTH2F, {axisPtB, axisMlScore}}); + if (doprocessDataWithBplusMl || doprocessDataWithBplusMlScalarProd) { + registry.add("hMlScoreSigBplus", bPlusCandTitle + stringPt + "prong0, B^{+} ML signal score;" + entries, {HistType::kTH2F, {axisPtBplus, axisMlScore}}); } } if (fillSparses) { - if (!(doprocessDataWithDmesMl || doprocessDataWithBplusMl)) { - registry.add("hMassPtCutVars", bPlusCandTitle + "#it{M} (D0#pi) (GeV/#it{c}^{2});#it{p}_{T}(B^{+}) (GeV/#it{c});B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});#it{p}_{T}(D0) (GeV/#it{c});D0 candidate decay length (cm);D0 candidate cos(#vartheta_{P})", {HistType::kTHnSparseF, {axisMassBplus, axisPtB, axisDecLength, axisNormDecLength, axisImpParProd, axisCpa, axisMassD0, axisPtD0, axisDecLength, axisCpa}}); + if (!(doprocessDataWithDmesMl || doprocessDataWithDmesMlScalarProd)) { + registry.add("hMassPtCutVars", bPlusCandTitle + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});" + stringPt + "B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});" + stringPtD + "D^{0} candidate decay length (cm);D^{0} candidate cos(#vartheta_{P})", {HistType::kTHnSparseF, {axisMassBplus, axisPtBplus, axisDecayLength, axisNormDecayLength, axisImpParProd, axisCosp, axisMassD0, axisPtD0, axisDecayLength, axisCosp}}); } else { - registry.add("hMassPtCutVars", bPlusCandTitle + "#it{M} (D0#pi) (GeV/#it{c}^{2});#it{p}_{T}(B^{+}) (GeV/#it{c});B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});#it{p}_{T}(D0) (GeV/#it{c});D0 candidate ML score bkg;D0 candidate ML score nonprompt", {HistType::kTHnSparseF, {axisMassBplus, axisPtB, axisDecLength, axisNormDecLength, axisImpParProd, axisCpa, axisMassD0, axisPtD0, axisMlScore, axisMlScore}}); + registry.add("hMassPtCutVars", bPlusCandTitle + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});" + stringPt + "B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});" + stringPtD + "D^{0} candidate ML score bkg;D^{0} candidate ML score nonprompt", {HistType::kTHnSparseF, {axisMassBplus, axisPtBplus, axisDecayLength, axisNormDecayLength, axisImpParProd, axisCosp, axisMassD0, axisPtD0, axisMlScore, axisMlScore}}); } } } - // histograms processMC if (doprocessMc || doprocessMcWithDecayTypeCheck || doprocessMcWithDmesMl || doprocessMcWithDmesMlAndDecayTypeCheck || doprocessMcWithBplusMl || doprocessMcWithBplusMlAndDecayTypeCheck) { if (fillHistograms) { - // Gen Level - registry.add("hEtaGen", mcParticleMatched + "candidate #it{#eta}^{gen};" + stringPt, {HistType::kTH2F, {axisEta, axisPtB}}); - registry.add("hYGen", mcParticleMatched + "candidate #it{y}^{gen};" + stringPt, {HistType::kTH2F, {axisRapidity, axisPtB}}); - registry.add("hPtProng0Gen", mcParticleMatched + "prong 0 #it{p}_{T}^{gen} (GeV/#it{c});" + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); - registry.add("hPtProng1Gen", mcParticleMatched + "prong 1 #it{p}_{T}^{gen} (GeV/#it{c});" + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); - registry.add("hYProng0Gen", mcParticleMatched + "prong 0 #it{y}^{gen};" + stringPt, {HistType::kTH2F, {axisRapidity, axisPtB}}); - registry.add("hYProng1Gen", mcParticleMatched + "prong 1 #it{y}^{gen};" + stringPt, {HistType::kTH2F, {axisRapidity, axisPtB}}); - registry.add("hEtaProng0Gen", mcParticleMatched + "prong 0 #it{#eta}^{gen};" + stringPt, {HistType::kTH2F, {axisEta, axisPtB}}); - registry.add("hEtaProng1Gen", mcParticleMatched + "prong 1 #it{#eta}^{gen};" + stringPt, {HistType::kTH2F, {axisEta, axisPtB}}); - registry.add("hPtProngsVsPtBGen", mcParticleMatched + "prong 0 #it{p}_{T}^{gen} (GeV/#it{c});prong 1 #it{p}_{T}^{gen} (GeV/#it{c});" + stringPt, {HistType::kTH3F, {axisPtProng, axisPtProng, axisPtB}}); - registry.add("hYProngsVsBplusGen", mcParticleMatched + "prong 0 #it{y}^{gen};prong 1 #it{y}^{gen};" + stringPt, {HistType::kTH3F, {axisRapidity, axisRapidity, axisPtB}}); - registry.add("hEtaProngsVsBplusGen", mcParticleMatched + "prong 0 #it{#eta}^{gen};prong 1 #it{#eta}^{gen};" + stringPt, {HistType::kTH3F, {axisEta, axisEta, axisPtB}}); - registry.add("hYGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);B^{+} candidate #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - // Reco Level - Signal - registry.add("hPtRecSig", bPlusCandMatch + "candidate #it{p}_{T} (GeV/#it{c});" + entries, {HistType::kTH1F, {{300, 0., 30.}}}); - registry.add("hCpaRecSig", bPlusCandMatch + "candidate cosine of pointing angle;" + stringPt, {HistType::kTH2F, {axisCpa, axisPtB}}); - registry.add("hCpaXyRecSig", bPlusCandMatch + "candidate CpaXy;" + stringPt, {HistType::kTH2F, {axisCpa, axisPtB}}); - registry.add("hEtaRecSig", bPlusCandMatch + "candidate #it{#eta};" + stringPt, {HistType::kTH2F, {axisEta, axisPtB}}); - registry.add("hRapidityRecSig", bPlusCandMatch + "candidate #it{y};" + stringPt, {HistType::kTH2F, {axisRapidity, axisPtB}}); - registry.add("hPtProng0RecSig", bPlusCandMatch + "prong 0 #it{p}_{T} (GeV/#it{c});" + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); - registry.add("hPtProng1RecSig", bPlusCandMatch + "prong 1 #it{p}_{T} (GeV/#it{c});" + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); - registry.add("hMassRecSig", bPlusCandMatch + "inv. mass #bar{D^{0}}#pi^{+} (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMassBplus, axisPtB}}); - registry.add("hd0Prong0RecSig", bPlusCandMatch + "prong 0 DCAxy to prim. vertex (cm);" + stringPt, {HistType::kTH2F, {axisD0Prong, axisPtB}}); - registry.add("hd0Prong1RecSig", bPlusCandMatch + "prong 1 DCAxy to prim. vertex (cm);" + stringPt, {HistType::kTH2F, {axisD0Prong, axisPtB}}); - registry.add("hDecLengthRecSig", bPlusCandMatch + "candidate decay length (cm);" + stringPt, {HistType::kTH2F, {axisDecLength, axisPtB}}); - registry.add("hDecLengthXyRecSig", bPlusCandMatch + "candidate decay length xy (cm);" + stringPt, {HistType::kTH2F, {axisDecLength, axisPtB}}); - registry.add("hNormDecLengthXyRecSig", bPlusCandMatch + "candidate normalized decay length (cm);" + stringPt, {HistType::kTH2F, {axisNormDecLength, axisPtB}}); - registry.add("hd0d0RecSig", bPlusCandMatch + "product of DCAxy to prim. vertex (cm^{2});" + stringPt, {HistType::kTH2F, {axisImpParProd, axisPtB}}); - registry.add("hCpaD0RecSig", bPlusCandMatch + "prong0 (D^{0}) cosine of pointing angle;" + stringPtD + entries, {HistType::kTH2F, {{220, 0., 1.1}, {120, 0., 60.}}}); - registry.add("hCpaXyD0RecSig", bPlusCandMatch + "prong0 (D^{0}) cosine of pointing angle;" + stringPtD + entries, {HistType::kTH2F, {{220, 0., 1.1}, {120, 0., 60.}}}); - registry.add("hDecLengthD0RecSig", bPlusCandMatch + "prong0 D^{0} decay length (cm);" + stringPtD + entries, {HistType::kTH2F, {{100, 0., 0.5}, {120, 0., 60.}}}); - registry.add("hDecLengthXyD0RecSig", bPlusCandMatch + "prong0 D^{0} decay length XY (cm);" + stringPtD + entries, {HistType::kTH2F, {{100, 0., 0.5}, {120, 0., 60.}}}); + // gen histos + registry.add("hEtaGen", bPlusPartGen + stringPtGen + "#it{#eta}^{gen}(B^{+});" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hYGen", bPlusPartGen + stringPtGen + "#it{y}^{gen}(B^{+});" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hYGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);" + stringPtGen + "#it{y}^{gen}(B^{+});" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hPtProng0Gen", bPlusPartGen + stringPtGen + "#it{p}_{T}^{gen}(D^{0}) (GeV/#it{c});" + entries, {HistType::kTH2F, {axisPtBplus, axisPtD0}}); + registry.add("hPtProng1Gen", bPlusPartGen + stringPtGen + "#it{p}_{T}^{gen}(#pi^{#plus}) (GeV/#it{c});" + entries, {HistType::kTH2F, {axisPtBplus, axisPtPi}}); + registry.add("hYProng0Gen", bPlusPartGen + stringPtGen + "#it{y}^{gen}(D^{0});" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hYProng1Gen", bPlusPartGen + stringPtGen + "#it{y}^{gen}(#pi^{#plus});" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hEtaProng0Gen", bPlusPartGen + stringPtGen + "#it{#eta}^{gen}(D^{0});" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hEtaProng1Gen", bPlusPartGen + stringPtGen + "#it{#eta}^{gen}(#pi^{#plus});" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + + // reco histos + // signal + registry.add("hMassRecSig", bPlusCandMatch + stringPt + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});" + entries, {HistType::kTH2F, {axisPtBplus, axisMassBplus}}); + registry.add("hDecLengthRecSig", bPlusCandMatch + stringPt + "B^{+} candidate decay length (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDecayLength}}); + registry.add("hDecLengthXyRecSig", bPlusCandMatch + stringPt + "decay length XY (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDecayLength}}); + registry.add("hNormDecLengthXyRecSig", bPlusCandMatch + stringPt + "B^{+} candidate norm. decay length XY (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisNormDecayLength}}); + registry.add("hDcaProng0RecSig", bPlusCandMatch + stringPt + "prong 0 (D^{0}) DCAxy to prim. vertex (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDca}}); + registry.add("hDcaProng1RecSig", bPlusCandMatch + stringPt + "prong 1 (#pi^{#plus}) DCAxy to prim. vertex (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDca}}); + registry.add("hCospRecSig", bPlusCandMatch + stringPt + "B^{+} candidate cos(#vartheta_{P});" + entries, {HistType::kTH2F, {axisPtBplus, axisCosp}}); + registry.add("hCospXyRecSig", bPlusCandMatch + stringPt + "B^{+} candidate cos(#vartheta_{P}^{XY});" + entries, {HistType::kTH2F, {axisPtBplus, axisCosp}}); + registry.add("hEtaRecSig", bPlusCandMatch + stringPt + "B^{+} candidate #it{#eta};" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hRapidityRecSig", bPlusCandMatch + stringPt + "B^{+} candidate #it{y};" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hImpParProdRecSig", bPlusCandMatch + stringPt + "B^{+} candidate impact parameter product;" + entries, {HistType::kTH2F, {axisPtBplus, axisImpParProd}}); + registry.add("hPtProng0RecSig", bPlusCandMatch + stringPt + stringPtD + entries, {HistType::kTH2F, {axisPtBplus, axisPtD0}}); + registry.add("hPtProng1RecSig", bPlusCandMatch + stringPt + "#it{p}_{T}(#pi^{#plus}) (GeV/#it{c});" + entries, {HistType::kTH2F, {axisPtBplus, axisPtPi}}); + registry.add("hInvMassDRecSig", bPlusCandMatch + stringPtD + "prong0, #it{M}(K#pi) (GeV/#it{c}^{2});" + entries, {HistType::kTH2F, {axisPtD0, axisMassD0}}); + registry.add("hDecLengthDRecSig", bPlusCandMatch + stringPtD + "D^{0} candidate decay length (cm);" + entries, {HistType::kTH2F, {axisPtD0, axisDecayLength}}); + registry.add("hDecLengthXyDRecSig", bPlusCandMatch + stringPtD + "decay length XY (cm);" + entries, {HistType::kTH2F, {axisPtD0, axisDecayLength}}); + registry.add("hCospDRecSig", bPlusCandMatch + stringPtD + "D^{0} candidate cos(#vartheta_{P});" + entries, {HistType::kTH2F, {axisPtD0, axisCosp}}); + registry.add("hCospXyDRecSig", bPlusCandMatch + stringPtD + "D^{0} candidate cos(#vartheta_{P}^{XY});" + entries, {HistType::kTH2F, {axisPtD0, axisCosp}}); + // background if (fillBackground) { - registry.add("hPtRecBg", bPlusCandUnmatch + "candidate #it{p}_{T} (GeV/#it{c});" + entries, {HistType::kTH1F, {{300, 0., 30.}}}); - registry.add("hCpaRecBg", bPlusCandUnmatch + "candidate cosine of pointing angle;" + stringPt, {HistType::kTH2F, {axisCpa, axisPtB}}); - registry.add("hCpaXyRecBg", bPlusCandUnmatch + "candidate CpaXy;" + stringPt, {HistType::kTH2F, {axisCpa, axisPtB}}); - registry.add("hEtaRecBg", bPlusCandUnmatch + "candidate #it{#eta};" + stringPt, {HistType::kTH2F, {axisEta, axisPtB}}); - registry.add("hRapidityRecBg", bPlusCandUnmatch + "candidate #it{#y};" + stringPt, {HistType::kTH2F, {axisRapidity, axisPtB}}); - registry.add("hPtProngsVsBplusRecBg", bPlusCandUnmatch + "prong 0 #it{p}_{T} (GeV/#it{c});prong 1 #it{p}_{T} (GeV/#it{c});" + stringPt, {HistType::kTH3F, {axisPtProng, axisPtProng, axisPtB}}); - registry.add("hPtProng0RecBg", bPlusCandUnmatch + "prong 0 #it{p}_{T} (GeV/#it{c});" + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); - registry.add("hPtProng1RecBg", bPlusCandUnmatch + "prong 1 #it{p}_{T} (GeV/#it{c});" + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); - registry.add("hMassRecBg", bPlusCandUnmatch + "inv. mass #bar{D^{0}}#pi^{+} (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMassBplus, axisPtB}}); - registry.add("hd0Prong0RecBg", bPlusCandUnmatch + "prong 0 DCAxy to prim. vertex (cm);" + stringPt, {HistType::kTH2F, {axisD0Prong, axisPtB}}); - registry.add("hd0Prong1RecBg", bPlusCandUnmatch + "prong 1 DCAxy to prim. vertex (cm);" + stringPt, {HistType::kTH2F, {axisD0Prong, axisPtB}}); - registry.add("hDecLengthRecBg", bPlusCandUnmatch + "candidate decay length (cm);" + stringPt, {HistType::kTH2F, {axisDecLength, axisPtB}}); - registry.add("hDecLengthXyRecBg", bPlusCandUnmatch + "candidate decay length xy(cm);" + stringPt, {HistType::kTH2F, {axisDecLength, axisPtB}}); - registry.add("hNormDecLengthXyRecBg", bPlusCandUnmatch + "candidate normalized decay length (cm);" + stringPt, {HistType::kTH2F, {axisNormDecLength, axisPtB}}); - registry.add("hd0d0RecBg", bPlusCandUnmatch + "product of DCAxy to prim. vertex (cm^{2});" + stringPt, {HistType::kTH2F, {axisImpParProd, axisPtB}}); - registry.add("hCpaD0RecBg", bPlusCandUnmatch + "prong0 (D^{0}) cosine of pointing angle;" + stringPtD + entries, {HistType::kTH2F, {{220, 0., 1.1}, {120, 0., 60.}}}); - registry.add("hCpaXyD0RecBg", bPlusCandUnmatch + "prong0 (D^{0}) cosine of pointing angle;" + stringPtD + entries, {HistType::kTH2F, {{220, 0., 1.1}, {120, 0., 60.}}}); - registry.add("hDecLengthD0RecBg", bPlusCandUnmatch + "prong0 D^{0} decay length (cm);" + stringPtD + entries, {HistType::kTH2F, {{100, 0., 0.5}, {120, 0., 60.}}}); - registry.add("hDecLengthXyD0RecBg", bPlusCandUnmatch + "prong0 D^{0} decay length XY (cm);" + stringPtD + entries, {HistType::kTH2F, {{100, 0., 0.5}, {120, 0., 60.}}}); + registry.add("hMassRecBg", bPlusCandUnmatch + stringPt + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});" + entries, {HistType::kTH2F, {axisPtBplus, axisMassBplus}}); + registry.add("hDecLengthRecBg", bPlusCandUnmatch + stringPt + "B^{+} candidate decay length (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDecayLength}}); + registry.add("hDecLengthXyRecBg", bPlusCandUnmatch + stringPt + "decay length XY (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDecayLength}}); + registry.add("hNormDecLengthXyRecBg", bPlusCandUnmatch + stringPt + "B^{+} candidate norm. decay length XY (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisNormDecayLength}}); + registry.add("hDcaProng0RecBg", bPlusCandUnmatch + stringPt + "prong 0 (D^{0}) DCAxy to prim. vertex (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDca}}); + registry.add("hDcaProng1RecBg", bPlusCandUnmatch + stringPt + "prong 1 (#pi^{#plus}) DCAxy to prim. vertex (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDca}}); + registry.add("hCospRecBg", bPlusCandUnmatch + stringPt + "B^{+} candidate cos(#vartheta_{P});" + entries, {HistType::kTH2F, {axisPtBplus, axisCosp}}); + registry.add("hCospXyRecBg", bPlusCandUnmatch + stringPt + "B^{+} candidate cos(#vartheta_{P}^{XY});" + entries, {HistType::kTH2F, {axisPtBplus, axisCosp}}); + registry.add("hEtaRecBg", bPlusCandUnmatch + stringPt + "B^{+} candidate #it{#eta};" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hRapidityRecBg", bPlusCandUnmatch + stringPt + "B^{+} candidate #it{y};" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hImpParProdRecBg", bPlusCandUnmatch + stringPt + "B^{+} candidate impact parameter product;" + entries, {HistType::kTH2F, {axisPtBplus, axisImpParProd}}); + registry.add("hPtProng0RecBg", bPlusCandUnmatch + stringPt + stringPtD + entries, {HistType::kTH2F, {axisPtBplus, axisPtD0}}); + registry.add("hPtProng1RecBg", bPlusCandUnmatch + stringPt + "#it{p}_{T}(#pi^{#plus}) (GeV/#it{c});" + entries, {HistType::kTH2F, {axisPtBplus, axisPtPi}}); + registry.add("hInvMassDRecBg", bPlusCandUnmatch + stringPtD + "prong0, #it{M}(K#pi) (GeV/#it{c}^{2});" + entries, {HistType::kTH2F, {axisPtD0, axisMassD0}}); + registry.add("hDecLengthDRecBg", bPlusCandUnmatch + stringPtD + "D^{0} candidate decay length (cm);" + entries, {HistType::kTH2F, {axisPtD0, axisDecayLength}}); + registry.add("hDecLengthXyDRecBg", bPlusCandUnmatch + stringPtD + "decay length XY (cm);" + entries, {HistType::kTH2F, {axisPtD0, axisDecayLength}}); + registry.add("hCospDRecBg", bPlusCandUnmatch + stringPtD + "D^{0} candidate cos(#vartheta_{P});" + entries, {HistType::kTH2F, {axisPtD0, axisCosp}}); + registry.add("hCospXyDRecBg", bPlusCandUnmatch + stringPtD + "D^{0} candidate cos(#vartheta_{P}^{XY});" + entries, {HistType::kTH2F, {axisPtD0, axisCosp}}); } // MC checks if (doprocessMcWithDecayTypeCheck || doprocessMcWithDmesMlAndDecayTypeCheck || doprocessMcWithBplusMlAndDecayTypeCheck) { constexpr uint8_t kNBinsDecayTypeMc = hf_cand_bplus::DecayTypeMc::NDecayTypeMc; TString labels[kNBinsDecayTypeMc]; labels[hf_cand_bplus::DecayTypeMc::BplusToD0PiToKPiPi] = "B^{+} #rightarrow (#overline{D^{0}} #rightarrow K^{#plus} #pi^{#minus}) #pi^{#plus}"; - labels[hf_cand_bplus::DecayTypeMc::BplusToD0KToKPiK] = "B^{+} #rightarrow (#overline{D^{0}} #rightarrow K^{#plus} #pi^{#minus}) #K^{#plus}"; + labels[hf_cand_bplus::DecayTypeMc::BplusToD0KToKPiK] = "B^{+} #rightarrow (#overline{D^{0}} #rightarrow K^{#plus} #pi^{#minus}) K^{#plus}"; labels[hf_cand_bplus::DecayTypeMc::PartlyRecoDecay] = "Partly reconstructed decay channel"; labels[hf_cand_bplus::DecayTypeMc::OtherDecay] = "Other decays"; static const AxisSpec axisDecayType = {kNBinsDecayTypeMc, 0.5, kNBinsDecayTypeMc + 0.5, ""}; - registry.add("hDecayTypeMc", "DecayType", {HistType::kTH3F, {axisDecayType, axisMassBplus, axisPtB}}); + registry.add("hDecayTypeMc", "DecayType", {HistType::kTH3F, {axisDecayType, axisMassBplus, axisPtBplus}}); for (uint8_t iBin = 0; iBin < kNBinsDecayTypeMc; ++iBin) { registry.get(HIST("hDecayTypeMc"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin]); } @@ -347,36 +360,37 @@ struct HfTaskBplusReduced { // ML scores of D0 daughter if (doprocessMcWithDmesMl || doprocessMcWithDmesMlAndDecayTypeCheck) { // signal - registry.add("hMlScoreBkgDRecSig", bPlusCandMatch + stringPtD + "prong0, D0 ML background score;entries", {HistType::kTH2F, {axisPtD0, axisMlScore}}); - registry.add("hMlScorePromptDRecSig", bPlusCandMatch + stringPtD + "prong0, D0 ML prompt score;entries", {HistType::kTH2F, {axisPtD0, axisMlScore}}); - registry.add("hMlScoreNonPromptDRecSig", bPlusCandMatch + stringPtD + "prong0, D0 ML nonprompt score;entries", {HistType::kTH2F, {axisPtD0, axisMlScore}}); + registry.add("hMlScoreBkgDRecSig", bPlusCandMatch + stringPtD + "prong0, D^{0} ML background score;" + entries, {HistType::kTH2F, {axisPtD0, axisMlScore}}); + registry.add("hMlScorePromptDRecSig", bPlusCandMatch + stringPtD + "prong0, D^{0} ML prompt score;" + entries, {HistType::kTH2F, {axisPtD0, axisMlScore}}); + registry.add("hMlScoreNonPromptDRecSig", bPlusCandMatch + stringPtD + "prong0, D^{0} ML nonprompt score;" + entries, {HistType::kTH2F, {axisPtD0, axisMlScore}}); // background - registry.add("hMlScoreBkgDRecBg", bPlusCandUnmatch + stringPtD + "prong0, D0 ML background score;entries", {HistType::kTH2F, {axisPtD0, axisMlScore}}); - registry.add("hMlScorePromptDRecBg", bPlusCandUnmatch + stringPtD + "prong0, D0 ML prompt score;entries", {HistType::kTH2F, {axisPtD0, axisMlScore}}); - registry.add("hMlScoreNonPromptDRecBg", bPlusCandUnmatch + stringPtD + "prong0, D0 ML nonprompt score;entries", {HistType::kTH2F, {axisPtD0, axisMlScore}}); + registry.add("hMlScoreBkgDRecBg", bPlusCandUnmatch + stringPtD + "prong0, D^{0} ML background score;" + entries, {HistType::kTH2F, {axisPtD0, axisMlScore}}); + registry.add("hMlScorePromptDRecBg", bPlusCandUnmatch + stringPtD + "prong0, D^{0} ML prompt score;" + entries, {HistType::kTH2F, {axisPtD0, axisMlScore}}); + registry.add("hMlScoreNonPromptDRecBg", bPlusCandUnmatch + stringPtD + "prong0, D^{0} ML nonprompt score;" + entries, {HistType::kTH2F, {axisPtD0, axisMlScore}}); } // ML scores of B+ candidate if (doprocessMcWithBplusMl || doprocessMcWithBplusMlAndDecayTypeCheck) { // signal - registry.add("hMlScoreSigBplusRecSig", bPlusCandMatch + "#it{p}_{T}(B^{+}) (GeV/#it{c});prong0, B^{+} ML signal score;entries", {HistType::kTH2F, {axisPtB, axisMlScore}}); + registry.add("hMlScoreSigBplusRecSig", bPlusCandMatch + stringPt + "prong0, B^{+} ML signal score;" + entries, {HistType::kTH2F, {axisPtBplus, axisMlScore}}); // background - registry.add("hMlScoreSigBplusRecBg", bPlusCandUnmatch + "#it{p}_{T}(B^{+}) (GeV/#it{c});prong0, B^{+} ML signal score;entries", {HistType::kTH2F, {axisPtB, axisMlScore}}); + registry.add("hMlScoreSigBplusRecBg", bPlusCandUnmatch + stringPt + "prong0, B^{+} ML signal score;" + entries, {HistType::kTH2F, {axisPtBplus, axisMlScore}}); } } if (fillSparses) { // gen sparses - registry.add("hPtYGenSig", "B^{+} particles (generated);#it{p}_{T}(B^{+}) (GeV/#it{c});#it{y}(B^{+})", {HistType::kTHnSparseF, {axisPtB, axisEta}}); - registry.add("hPtYWithProngsInAccepanceGenSig", "B^{+} particles (generated-daughters in acceptance);#it{p}_{T}(B^{+}) (GeV/#it{c});#it{y}(B^{+})", {HistType::kTHnSparseF, {axisPtB, axisEta}}); + registry.add("hPtYGenSig", bPlusPartGen + stringPt + "#it{y}(B^{+})", {HistType::kTHnSparseF, {axisPtBplus, axisEta}}); + registry.add("hPtYWithProngsInAccepanceGenSig", "B^{+} particles (generated-daughters in acceptance);" + stringPt + "#it{y}(B^{+})", {HistType::kTHnSparseF, {axisPtBplus, axisEta}}); + // reco sparses - if (!doprocessDataWithDmesMl) { - registry.add("hMassPtCutVarsRecSig", bPlusCandMatch + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});#it{p}_{T}(B^{+}) (GeV/#it{c});B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});#it{p}_{T}(D0) (GeV/#it{c});D0 candidate decay length (cm);D0 candidate cos(#vartheta_{P})", {HistType::kTHnSparseF, {axisMassBplus, axisPtB, axisDecLength, axisNormDecLength, axisImpParProd, axisCpa, axisMassD0, axisPtD0, axisDecLength, axisCpa}}); + if (!(doprocessMcWithDmesMl || doprocessMcWithDmesMlAndDecayTypeCheck)) { + registry.add("hMassPtCutVarsRecSig", bPlusCandMatch + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});" + stringPt + "B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});" + stringPtD + "D^{0} candidate decay length (cm);D^{0} candidate cos(#vartheta_{P})", {HistType::kTHnSparseF, {axisMassBplus, axisPtBplus, axisDecayLength, axisNormDecayLength, axisImpParProd, axisCosp, axisMassD0, axisPtD0, axisDecayLength, axisCosp}}); if (fillBackground) { - registry.add("hMassPtCutVarsRecBg", bPlusCandUnmatch + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});#it{p}_{T}(B^{+}) (GeV/#it{c});B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});#it{p}_{T}(D0) (GeV/#it{c});D0 candidate decay length (cm);D0 candidate cos(#vartheta_{P})", {HistType::kTHnSparseF, {axisMassBplus, axisPtB, axisDecLength, axisNormDecLength, axisImpParProd, axisCpa, axisMassD0, axisPtD0, axisDecLength, axisCpa}}); + registry.add("hMassPtCutVarsRecBg", bPlusCandUnmatch + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});" + stringPt + "B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});" + stringPtD + "D^{0} candidate decay length (cm);D^{0} candidate cos(#vartheta_{P})", {HistType::kTHnSparseF, {axisMassBplus, axisPtBplus, axisDecayLength, axisNormDecayLength, axisImpParProd, axisCosp, axisMassD0, axisPtD0, axisDecayLength, axisCosp}}); } } else { - registry.add("hMassPtCutVarsRecSig", bPlusCandMatch + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});#it{p}_{T}(B^{+}) (GeV/#it{c});B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});#it{p}_{T}(D0) (GeV/#it{c});D0 candidate ML score bkg;D0 candidate ML score nonprompt", {HistType::kTHnSparseF, {axisMassBplus, axisPtB, axisDecLength, axisNormDecLength, axisImpParProd, axisCpa, axisMassD0, axisPtD0, axisMlScore, axisMlScore}}); + registry.add("hMassPtCutVarsRecSig", bPlusCandMatch + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});" + stringPt + "B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});" + stringPtD + "D^{0} candidate ML score bkg;D^{0} candidate ML score nonprompt", {HistType::kTHnSparseF, {axisMassBplus, axisPtBplus, axisDecayLength, axisNormDecayLength, axisImpParProd, axisCosp, axisMassD0, axisPtD0, axisMlScore, axisMlScore}}); if (fillBackground) { - registry.add("hMassPtCutVarsRecBg", bPlusCandUnmatch + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});#it{p}_{T}(B^{+}) (GeV/#it{c});B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});#it{p}_{T}(D0) (GeV/#it{c});D0 candidate ML score bkg;D0 candidate ML score nonprompt", {HistType::kTHnSparseF, {axisMassBplus, axisPtB, axisDecLength, axisNormDecLength, axisImpParProd, axisCpa, axisMassD0, axisPtD0, axisMlScore, axisMlScore}}); + registry.add("hMassPtCutVarsRecBg", bPlusCandUnmatch + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});" + stringPt + "B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});" + stringPtD + "D^{0} candidate ML score bkg;D^{0} candidate ML score nonprompt", {HistType::kTHnSparseF, {axisMassBplus, axisPtBplus, axisDecayLength, axisNormDecayLength, axisImpParProd, axisCosp, axisMassD0, axisPtD0, axisMlScore, axisMlScore}}); } } } @@ -398,19 +412,21 @@ struct HfTaskBplusReduced { /// \param withDecayTypeCheck is the flag to enable MC with decay type check /// \param withDmesMl is the flag to enable the filling with ML scores for the D0 daughter /// \param withBplusMl is the flag to enable the filling with ML scores for the B+ candidate + /// \param withScalarProd is the flag to enable the filling with scalar product + /// \param collision is the collision /// \param candidate is the B+ candidate /// \param candidatesD is the table with D0 candidates - template - void fillCand(Cand const& candidate, - CandsDmes const& /*candidatesD*/, - TracksPion const&) + template + void fillCand(Collision const& collision, + Cand const& candidate, + CandsDmes const&) { auto ptCandBplus = candidate.pt(); auto invMassBplus = HfHelper::invMassBplusToD0Pi(candidate); auto candD0 = candidate.template prong0_as(); - auto candPi = candidate.template prong1_as(); + auto prong1 = candidate.template prong1_as(); auto ptD0 = candidate.ptProng0(); - auto invMassD0 = (candPi.signed1Pt() < 0) ? candD0.invMassHypo0() : candD0.invMassHypo1(); + auto invMassD0 = (prong1.signed1Pt() < 0) ? candD0.invMassHypo0() : candD0.invMassHypo1(); std::array const posPv{candidate.posX(), candidate.posY(), candidate.posZ()}; std::array const posSvD{candD0.xSecondaryVertex(), candD0.ySecondaryVertex(), candD0.zSecondaryVertex()}; std::array const momD{candD0.pVector()}; @@ -431,26 +447,24 @@ struct HfTaskBplusReduced { if (fillHistograms) { if constexpr (DoMc) { if (isSignal) { - registry.fill(HIST("hMassRecSig"), invMassBplus, ptCandBplus); - registry.fill(HIST("hPtRecSig"), ptCandBplus); - registry.fill(HIST("hPtProng0RecSig"), ptD0, ptCandBplus); - registry.fill(HIST("hPtProng1RecSig"), candidate.ptProng1(), ptCandBplus); - registry.fill(HIST("hCpaRecSig"), candidate.cpa(), ptCandBplus); - registry.fill(HIST("hCpaXyRecSig"), candidate.cpaXY(), ptCandBplus); - registry.fill(HIST("hEtaRecSig"), candidate.eta(), ptCandBplus); - registry.fill(HIST("hRapidityRecSig"), HfHelper::yBplus(candidate), ptCandBplus); - registry.fill(HIST("hDecLengthRecSig"), candidate.decayLength(), ptCandBplus); - registry.fill(HIST("hDecLengthXyRecSig"), candidate.decayLengthXY(), ptCandBplus); - registry.fill(HIST("hNormDecLengthXyRecSig"), candidate.decayLengthXYNormalised(), ptCandBplus); - registry.fill(HIST("hd0Prong0RecSig"), candidate.impactParameter0(), ptCandBplus); - registry.fill(HIST("hd0Prong1RecSig"), candidate.impactParameter1(), ptCandBplus); - registry.fill(HIST("hd0d0RecSig"), candidate.impactParameterProduct(), ptCandBplus); - registry.fill(HIST("hPtProng0RecSig"), candidate.ptProng0(), ptCandBplus); - registry.fill(HIST("hPtProng1RecSig"), candidate.ptProng1(), ptCandBplus); - registry.fill(HIST("hDecLengthD0RecSig"), decLenD0, candidate.ptProng0()); - registry.fill(HIST("hDecLengthXyD0RecSig"), decLenXyD0, ptD0); - registry.fill(HIST("hCpaD0RecSig"), cpaD0, ptD0); - registry.fill(HIST("hCpaXyD0RecSig"), cpaXyD0, ptD0); + registry.fill(HIST("hMassRecSig"), ptCandBplus, invMassBplus); + registry.fill(HIST("hPtProng0RecSig"), ptCandBplus, candidate.ptProng0()); + registry.fill(HIST("hPtProng1RecSig"), ptCandBplus, candidate.ptProng1()); + registry.fill(HIST("hImpParProdRecSig"), ptCandBplus, candidate.impactParameterProduct()); + registry.fill(HIST("hDecLengthRecSig"), ptCandBplus, candidate.decayLength()); + registry.fill(HIST("hDecLengthXyRecSig"), ptCandBplus, candidate.decayLengthXY()); + registry.fill(HIST("hNormDecLengthXyRecSig"), ptCandBplus, candidate.decayLengthXY() / candidate.errorDecayLengthXY()); + registry.fill(HIST("hDcaProng0RecSig"), ptCandBplus, candidate.impactParameter0()); + registry.fill(HIST("hDcaProng1RecSig"), ptCandBplus, candidate.impactParameter1()); + registry.fill(HIST("hCospRecSig"), ptCandBplus, candidate.cpa()); + registry.fill(HIST("hCospXyRecSig"), ptCandBplus, candidate.cpaXY()); + registry.fill(HIST("hEtaRecSig"), ptCandBplus, candidate.eta()); + registry.fill(HIST("hRapidityRecSig"), ptCandBplus, HfHelper::yBplus(candidate)); + registry.fill(HIST("hInvMassDRecSig"), ptD0, invMassD0); + registry.fill(HIST("hDecLengthDRecSig"), ptD0, decLenD0); + registry.fill(HIST("hDecLengthXyDRecSig"), ptD0, decLenXyD0); + registry.fill(HIST("hCospDRecSig"), ptD0, cpaD0); + registry.fill(HIST("hCospXyDRecSig"), ptD0, cpaXyD0); if constexpr (WithDecayTypeCheck) { registry.fill(HIST("hDecayTypeMc"), 1 + hf_cand_bplus::DecayTypeMc::BplusToD0PiToKPiPi, invMassBplus, ptCandBplus); } @@ -463,26 +477,24 @@ struct HfTaskBplusReduced { registry.fill(HIST("hMlScoreSigBplusRecSig"), ptCandBplus, candidate.mlProbBplusToD0Pi()); } } else if (fillBackground) { - registry.fill(HIST("hMassRecBg"), invMassBplus, ptCandBplus); - registry.fill(HIST("hPtRecBg"), ptCandBplus); - registry.fill(HIST("hPtProng0RecBg"), candidate.ptProng0(), ptCandBplus); - registry.fill(HIST("hPtProng1RecBg"), candidate.ptProng1(), ptCandBplus); - registry.fill(HIST("hCpaRecBg"), candidate.cpa(), ptCandBplus); - registry.fill(HIST("hCpaXyRecBg"), candidate.cpaXY(), ptCandBplus); - registry.fill(HIST("hEtaRecBg"), candidate.eta(), ptCandBplus); - registry.fill(HIST("hRapidityRecBg"), HfHelper::yBplus(candidate), ptCandBplus); - registry.fill(HIST("hDecLengthRecBg"), candidate.decayLength(), ptCandBplus); - registry.fill(HIST("hDecLengthXyRecBg"), candidate.decayLengthXY(), ptCandBplus); - registry.fill(HIST("hNormDecLengthXyRecBg"), candidate.decayLengthXYNormalised(), ptCandBplus); - registry.fill(HIST("hd0Prong0RecBg"), candidate.impactParameter0(), ptCandBplus); - registry.fill(HIST("hd0Prong1RecBg"), candidate.impactParameter1(), ptCandBplus); - registry.fill(HIST("hd0d0RecBg"), candidate.impactParameterProduct(), ptCandBplus); - registry.fill(HIST("hDecLengthD0RecBg"), decLenD0, candidate.ptProng0()); - registry.fill(HIST("hInvMassDRecBg"), invMassD0, ptD0); - registry.fill(HIST("hDecLengthDRecBg"), decLenD0, ptD0); - registry.fill(HIST("hDecLengthXyDRecBg"), decLenXyD0, ptD0); - registry.fill(HIST("hCpaDRecBg"), cpaD0, ptD0); - registry.fill(HIST("hCpaXyDRecBg"), cpaXyD0, ptD0); + registry.fill(HIST("hMassRecBg"), ptCandBplus, invMassBplus); + registry.fill(HIST("hPtProng0RecBg"), ptCandBplus, candidate.ptProng0()); + registry.fill(HIST("hPtProng1RecBg"), ptCandBplus, candidate.ptProng1()); + registry.fill(HIST("hImpParProdRecBg"), ptCandBplus, candidate.impactParameterProduct()); + registry.fill(HIST("hDecLengthRecBg"), ptCandBplus, candidate.decayLength()); + registry.fill(HIST("hDecLengthXyRecBg"), ptCandBplus, candidate.decayLengthXY()); + registry.fill(HIST("hNormDecLengthXyRecBg"), ptCandBplus, candidate.decayLengthXY() / candidate.errorDecayLengthXY()); + registry.fill(HIST("hDcaProng0RecBg"), ptCandBplus, candidate.impactParameter0()); + registry.fill(HIST("hDcaProng1RecBg"), ptCandBplus, candidate.impactParameter1()); + registry.fill(HIST("hCospRecBg"), ptCandBplus, candidate.cpa()); + registry.fill(HIST("hCospXyRecBg"), ptCandBplus, candidate.cpaXY()); + registry.fill(HIST("hEtaRecBg"), ptCandBplus, candidate.eta()); + registry.fill(HIST("hRapidityRecBg"), ptCandBplus, HfHelper::yBplus(candidate)); + registry.fill(HIST("hInvMassDRecBg"), ptD0, invMassD0); + registry.fill(HIST("hDecLengthDRecBg"), ptD0, decLenD0); + registry.fill(HIST("hDecLengthXyDRecBg"), ptD0, decLenXyD0); + registry.fill(HIST("hCospDRecBg"), ptD0, cpaD0); + registry.fill(HIST("hCospXyDRecBg"), ptD0, cpaXyD0); if constexpr (WithDmesMl) { registry.fill(HIST("hMlScoreBkgDRecBg"), ptD0, candidate.prong0MlScoreBkg()); registry.fill(HIST("hMlScorePromptDRecBg"), ptD0, candidate.prong0MlScorePrompt()); @@ -492,7 +504,7 @@ struct HfTaskBplusReduced { registry.fill(HIST("hMlScoreSigBplusRecBg"), ptCandBplus, candidate.mlProbBplusToD0Pi()); } } else if constexpr (WithDecayTypeCheck) { - if (TESTBIT(flagMcMatchRec, hf_cand_bplus::DecayTypeMc::BplusToD0KToKPiK)) { // Partly reconstructed decay channel + if (TESTBIT(flagMcMatchRec, hf_cand_bplus::DecayTypeMc::BplusToD0KToKPiK)) { // B+ → D0bar K+ → (K+ π-) K+ registry.fill(HIST("hDecayTypeMc"), 1 + hf_cand_bplus::DecayTypeMc::BplusToD0KToKPiK, invMassBplus, ptCandBplus); } else if (TESTBIT(flagMcMatchRec, hf_cand_bplus::DecayTypeMc::PartlyRecoDecay)) { // Partly reconstructed decay channel registry.fill(HIST("hDecayTypeMc"), 1 + hf_cand_bplus::DecayTypeMc::PartlyRecoDecay, invMassBplus, ptCandBplus); @@ -501,25 +513,25 @@ struct HfTaskBplusReduced { } } } else { - registry.fill(HIST("hMass"), invMassBplus, ptCandBplus); - registry.fill(HIST("hPtCand"), ptCandBplus); - registry.fill(HIST("hPtProng0"), ptD0, ptCandBplus); - registry.fill(HIST("hPtProng1"), candidate.ptProng1(), ptCandBplus); - registry.fill(HIST("hd0d0"), candidate.impactParameterProduct(), ptCandBplus); - registry.fill(HIST("hDecLength"), candidate.decayLength(), ptCandBplus); - registry.fill(HIST("hDecLengthXy"), candidate.decayLengthXY(), ptCandBplus); - registry.fill(HIST("hNormDecLengthXy"), candidate.decayLengthXY() / candidate.errorDecayLengthXY(), ptCandBplus); - registry.fill(HIST("hd0Prong0"), candidate.impactParameter0(), ptCandBplus); - registry.fill(HIST("hd0Prong1"), candidate.impactParameter1(), ptCandBplus); - registry.fill(HIST("hCpa"), candidate.cpa(), ptCandBplus); - registry.fill(HIST("hCpaXy"), candidate.cpaXY(), ptCandBplus); - registry.fill(HIST("hEta"), candidate.eta(), ptCandBplus); - registry.fill(HIST("hRapidity"), HfHelper::yBplus(candidate), ptCandBplus); - registry.fill(HIST("hInvMassD0"), invMassD0, ptCandBplus); - registry.fill(HIST("hDecLengthD0"), decLenD0, ptD0); - registry.fill(HIST("hDecLengthXyD0"), decLenXyD0, ptD0); - registry.fill(HIST("hCpaD0"), cpaD0, ptD0); - registry.fill(HIST("hCpaXyD0"), cpaXyD0, ptD0); + registry.fill(HIST("hMass"), ptCandBplus, invMassBplus); + registry.fill(HIST("hPtProng0"), ptCandBplus, candidate.ptProng0()); + registry.fill(HIST("hPtProng1"), ptCandBplus, candidate.ptProng1()); + registry.fill(HIST("hImpParProd"), ptCandBplus, candidate.impactParameterProduct()); + registry.fill(HIST("hDecLength"), ptCandBplus, candidate.decayLength()); + registry.fill(HIST("hDecLengthXy"), ptCandBplus, candidate.decayLengthXY()); + registry.fill(HIST("hNormDecLengthXy"), ptCandBplus, candidate.decayLengthXY() / candidate.errorDecayLengthXY()); + registry.fill(HIST("hDcaProng0"), ptCandBplus, candidate.impactParameter0()); + registry.fill(HIST("hDcaProng1"), ptCandBplus, candidate.impactParameter1()); + registry.fill(HIST("hCosp"), ptCandBplus, candidate.cpa()); + registry.fill(HIST("hCospXy"), ptCandBplus, candidate.cpaXY()); + registry.fill(HIST("hEta"), ptCandBplus, candidate.eta()); + registry.fill(HIST("hRapidity"), ptCandBplus, HfHelper::yBplus(candidate)); + registry.fill(HIST("hInvMassD"), ptD0, invMassD0); + registry.fill(HIST("hDecLengthD"), ptD0, decLenD0); + registry.fill(HIST("hDecLengthXyD"), ptD0, decLenXyD0); + registry.fill(HIST("hCospD"), ptD0, cpaD0); + registry.fill(HIST("hCospXyD"), ptD0, cpaXyD0); + if constexpr (WithDmesMl) { registry.fill(HIST("hMlScoreBkgD"), ptD0, candidate.prong0MlScoreBkg()); registry.fill(HIST("hMlScorePromptD"), ptD0, candidate.prong0MlScorePrompt()); @@ -555,11 +567,12 @@ struct HfTaskBplusReduced { } if (fillTree) { float const pseudoRndm = ptD0 * 1000. - static_cast(ptD0 * 1000); - if (ptCandBplus >= ptMaxForDownSample || pseudoRndm < downSampleBkgFactor) { + if (flagMcMatchRec != 0 || (((DoMc && fillBackground) || !DoMc) && (ptCandBplus >= ptMaxForDownSample || pseudoRndm < downSampleBkgFactor))) { float prong0MlScoreBkg = -1.; float prong0MlScorePrompt = -1.; float prong0MlScoreNonprompt = -1.; float candidateMlScoreSig = -1; + float scalarProd = -1; if constexpr (WithDmesMl) { prong0MlScoreBkg = candidate.prong0MlScoreBkg(); prong0MlScorePrompt = candidate.prong0MlScorePrompt(); @@ -568,7 +581,12 @@ struct HfTaskBplusReduced { if constexpr (WithBplusMl) { candidateMlScoreSig = candidate.mlProbBplusToD0Pi(); } - auto prong1 = candidate.template prong1_as(); + if constexpr (WithScalarProd) { + auto qVecs = getQvec(collision, qVecDetector.value); + float xQVec = qVecs[0]; + float yQVec = qVecs[1]; + scalarProd = std::cos(harmonic * candidate.phi()) * xQVec + std::sin(harmonic * candidate.phi()) * yQVec; + } float tpcNSigmaPi, tofNSigmaPi, tpcTofNSigmaPi, tpcNSigmaKa, tofNSigmaKa, tpcTofNSigmaKa; if (prong1.signed1Pt() < 0) { tpcNSigmaPi = candD0.tpcNSigmaPiProng1(); @@ -592,7 +610,7 @@ struct HfTaskBplusReduced { } hfRedCandBpLite( - // B+ - meson features + // B-meson features invMassBplus, ptCandBplus, candidate.eta(), @@ -609,14 +627,20 @@ struct HfTaskBplusReduced { candidate.maxNormalisedDeltaIP(), candidateMlScoreSig, candidate.isSelBplusToD0Pi(), + scalarProd, + centralityEstimator == 0 ? collision.centFT0M() : collision.centFT0C(), // D-meson features invMassD0, ptD0, - decLenD0, - decLenXyD0, + candidate.decayLengthDToPv(), + candidate.decayLengthDToPvXY(), + candidate.decayLengthDToB(), + candidate.decayLengthDToBXY(), + candidate.cpaDToPv(), + candidate.cpaDToPvXY(), + candidate.cpaDToB(), + candidate.cpaDToBXY(), candidate.impactParameter0(), - cpaD0, - cpaXyD0, candD0.ptProngMin(), candD0.absEtaProngMin(), candD0.itsNClsProngMin(), @@ -705,56 +729,88 @@ struct HfTaskBplusReduced { } // Process functions - void processData(soa::Filtered> const& candidates, + void processData(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, CandsD0 const& candidatesD, - TracksPion const& pionTracks) + TracksPion const&) { for (const auto& candidate : candidates) { if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD, pionTracks); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // candidate loop } // processData - PROCESS_SWITCH(HfTaskBplusReduced, processData, "Process data without ML scores for D0 daughter", true); + PROCESS_SWITCH(HfTaskBplusReduced, processData, "Process data without ML scores for B+ and D0 daughter", true); - void processDataWithDmesMl(soa::Filtered> const& candidates, + void processDataWithDmesMl(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, CandsD0 const& candidatesD, - TracksPion const& pionTracks) + TracksPion const&) { for (const auto& candidate : candidates) { if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD, pionTracks); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // candidate loop } // processDataWithDmesMl - PROCESS_SWITCH(HfTaskBplusReduced, processDataWithDmesMl, "Process data with ML scores for D0 daughter", false); + PROCESS_SWITCH(HfTaskBplusReduced, processDataWithDmesMl, "Process data with(out) ML scores for D0 daughter (B+)", false); - void processDataWithBplusMl(soa::Filtered> const& candidates, + void processDataWithDmesMlScalarProd(CollisionsWithCentsQvecs const& collisions, + soa::Filtered> const& candidates, + CandsD0 const& candidatesD, + TracksPion const&) + { + for (const auto& candidate : candidates) { + if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { + continue; + } + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); + } // candidate loop + } // processDataWithDmesMlScalarProd + PROCESS_SWITCH(HfTaskBplusReduced, processDataWithDmesMlScalarProd, "Process data with(out) ML scores for D0 daughter (B+) and scalar products", false); + + void processDataWithBplusMl(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, CandsD0 const& candidatesD, - TracksPion const& pionTracks) + TracksPion const&) { for (const auto& candidate : candidates) { if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD, pionTracks); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // candidate loop } // processDataWithBplusMl PROCESS_SWITCH(HfTaskBplusReduced, processDataWithBplusMl, "Process data with(out) ML scores for B+ (D0 daughter)", false); - void processMc(soa::Filtered> const& candidates, + void processDataWithBplusMlScalarProd(CollisionsWithCentsQvecs const& collisions, + soa::Filtered> const& candidates, + CandsD0 const& candidatesD, + TracksPion const&) + { + for (const auto& candidate : candidates) { + if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { + continue; + } + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); + } // candidate loop + } // processDataWithBplusMlScalarProd + PROCESS_SWITCH(HfTaskBplusReduced, processDataWithBplusMlScalarProd, "Process data with(out) ML scores for B+ (D0 daughter) and scalar products", false); + + void processMc(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, aod::HfMcGenRedBps const& mcParticles, CandsD0 const& candidatesD, - TracksPion const& pionTracks) + TracksPion const&) { // MC rec for (const auto& candidate : candidates) { if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD, pionTracks); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // rec // MC gen. level @@ -764,37 +820,39 @@ struct HfTaskBplusReduced { } // processMc PROCESS_SWITCH(HfTaskBplusReduced, processMc, "Process MC without ML scores for B+ and D0 daughter", false); - void processMcWithDecayTypeCheck(soa::Filtered> const& candidates, + void processMcWithDecayTypeCheck(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, aod::HfMcGenRedBps const& mcParticles, CandsD0 const& candidatesD, - TracksPion const& pionTracks) + TracksPion const&) { // MC rec for (const auto& candidate : candidates) { if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD, pionTracks); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // rec // MC gen. level for (const auto& particle : mcParticles) { fillCandMcGen(particle); } // gen - } // processMc - PROCESS_SWITCH(HfTaskBplusReduced, processMcWithDecayTypeCheck, "Process MC with decay type check and without ML scores for B+ and D daughter", false); + } // processMcWithDecayTypeCheck + PROCESS_SWITCH(HfTaskBplusReduced, processMcWithDecayTypeCheck, "Process MC with decay type check and without ML scores for B+ and D0 daughter", false); - void processMcWithDmesMl(soa::Filtered> const& candidates, + void processMcWithDmesMl(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, aod::HfMcGenRedBps const& mcParticles, CandsD0 const& candidatesD, - TracksPion const& pionTracks) + TracksPion const&) { // MC rec for (const auto& candidate : candidates) { if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD, pionTracks); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // rec // MC gen. level @@ -804,37 +862,39 @@ struct HfTaskBplusReduced { } // processMcWithDmesMl PROCESS_SWITCH(HfTaskBplusReduced, processMcWithDmesMl, "Process MC with(out) ML scores for D0 daughter (B+)", false); - void processMcWithDmesMlAndDecayTypeCheck(soa::Filtered> const& candidates, + void processMcWithDmesMlAndDecayTypeCheck(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, aod::HfMcGenRedBps const& mcParticles, CandsD0 const& candidatesD, - TracksPion const& pionTracks) + TracksPion const&) { // MC rec for (const auto& candidate : candidates) { if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD, pionTracks); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // rec // MC gen. level for (const auto& particle : mcParticles) { fillCandMcGen(particle); } // gen - } // processMc + } // processMcWithDmesMlAndDecayTypeCheck PROCESS_SWITCH(HfTaskBplusReduced, processMcWithDmesMlAndDecayTypeCheck, "Process MC with decay type check and with(out) ML scores for B+ (D0 daughter)", false); - void processMcWithBplusMl(soa::Filtered> const& candidates, + void processMcWithBplusMl(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, aod::HfMcGenRedBps const& mcParticles, CandsD0 const& candidatesD, - TracksPion const& pionTracks) + TracksPion const&) { // MC rec for (const auto& candidate : candidates) { if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD, pionTracks); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // rec // MC gen. level @@ -844,24 +904,25 @@ struct HfTaskBplusReduced { } // processMcWithBplusMl PROCESS_SWITCH(HfTaskBplusReduced, processMcWithBplusMl, "Process MC with(out) ML scores for B+ (D0 daughter)", false); - void processMcWithBplusMlAndDecayTypeCheck(soa::Filtered> const& candidates, + void processMcWithBplusMlAndDecayTypeCheck(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, aod::HfMcGenRedBps const& mcParticles, CandsD0 const& candidatesD, - TracksPion const& pionTracks) + TracksPion const&) { // MC rec for (const auto& candidate : candidates) { if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD, pionTracks); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // rec // MC gen. level for (const auto& particle : mcParticles) { fillCandMcGen(particle); } // gen - } // processMc + } // processMcWithBplusMlAndDecayTypeCheck PROCESS_SWITCH(HfTaskBplusReduced, processMcWithBplusMlAndDecayTypeCheck, "Process MC with decay type check and with(out) ML scores for B+ (D0 daughter)", false); }; // struct diff --git a/PWGHF/D2H/Tasks/taskBplusToJpsiKReduced.cxx b/PWGHF/D2H/Tasks/taskBplusToJpsiKReduced.cxx index bdb9a91b64f..316bdad3a22 100644 --- a/PWGHF/D2H/Tasks/taskBplusToJpsiKReduced.cxx +++ b/PWGHF/D2H/Tasks/taskBplusToJpsiKReduced.cxx @@ -303,7 +303,7 @@ struct HfTaskBplusToJpsiKReduced { const AxisSpec axisImpactPar{200, -0.05, 0.05}; const AxisSpec axisPtJpsi{100, 0., 50.}; const AxisSpec axisRapidity{100, -2., 2.}; - const AxisSpec axisPtB{(std::vector)binsPt, "#it{p}_{T}^{B^{+}} (GeV/#it{c})"}; + const AxisSpec axisPtB{500, 0., 50., "#it{p}_{T}^{B^{0}} (GeV/#it{c})"}; const AxisSpec axisPtKa{100, 0.f, 10.f}; registry.add("hMass", bPlusCandTitle + "inv. mass J/#Psi K^{+} (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMassBplus, axisPtB}}); @@ -314,7 +314,8 @@ struct HfTaskBplusToJpsiKReduced { if (doprocessMc || doprocessMcWithBplusMl) { registry.add("hPtJpsiGen", mcParticleMatched + "J/#Psi #it{p}_{T}^{gen} (GeV/#it{c}); B^{+} " + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); registry.add("hPtKGen", mcParticleMatched + "Kaon #it{p}_{T}^{gen} (GeV/#it{c}); B^{+} " + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); - registry.add("hYGenWithProngsInAcceptance", mcParticleMatched + "B^{+} #it{p}_{T}^{gen} (GeV/#it{c}); B^{+} #it{y}", {HistType::kTH2F, {axisPtProng, axisRapidity}}); + registry.add("hPtYGenSig", mcParticleMatched + "B^{+} #it{p}_{T}^{gen} (GeV/#it{c}); B^{+} #it{y}", {HistType::kTH2F, {axisPtB, axisRapidity}}); + registry.add("hPtYWithProngsInAccepanceGenSig", mcParticleMatched + "B^{+} #it{p}_{T}^{gen} (GeV/#it{c}); B^{+} #it{y}", {HistType::kTH2F, {axisPtB, axisRapidity}}); registry.add("hMassRecSig", bPlusCandMatch + "inv. mass J/#Psi K^{+} (GeV/#it{c}^{2}); B^{+} " + stringPt, {HistType::kTH2F, {axisMassBplus, axisPtB}}); registry.add("hMassJpsiRecSig", bPlusCandMatch + "inv. mass #mu^{+}#mu^{#minus} (GeV/#it{c}^{2}); J/#Psi " + stringPt, {HistType::kTH2F, {axisMassJpsi, axisPtJpsi}}); registry.add("hd0KRecSig", bPlusCandMatch + "Kaon DCAxy to prim. vertex (cm); K^{+} " + stringPt, {HistType::kTH2F, {axisImpactPar, axisPtKa}}); @@ -526,9 +527,10 @@ struct HfTaskBplusToJpsiKReduced { registry.fill(HIST("hPtJpsiGen"), ptProngs[0], ptParticle); registry.fill(HIST("hPtKGen"), ptProngs[1], ptParticle); + registry.fill(HIST("hPtYGenSig"), ptParticle, yParticle); // generated B+ with daughters in geometrical acceptance if (prongsInAcc) { - registry.fill(HIST("hYGenWithProngsInAcceptance"), ptParticle, yParticle); + registry.fill(HIST("hPtYWithProngsInAccepanceGenSig"), ptParticle, yParticle); } } diff --git a/PWGHF/D2H/Tasks/taskBs.cxx b/PWGHF/D2H/Tasks/taskBs.cxx index 46b4dacec14..b6635bf670a 100644 --- a/PWGHF/D2H/Tasks/taskBs.cxx +++ b/PWGHF/D2H/Tasks/taskBs.cxx @@ -79,7 +79,7 @@ struct HfTaskBs { void init(InitContext const&) { static const AxisSpec axisMassBs = {300, 4.5, 6.0, "inv. mass (GeV/#it{c}^{2})"}; - static const AxisSpec axisPt = {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}; + static const AxisSpec axisPt = {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}; registry.add("hEta", "B^{0}_{s} candidates;B^{0}_{s} candidate #it{#eta};entries", {HistType::kTH2F, {{100, -2., 2.}, axisPt}}); registry.add("hRapidity", "B^{0}_{s} candidates;B^{0}_{s} candidate #it{y};entries", {HistType::kTH2F, {{100, -2., 2.}, axisPt}}); diff --git a/PWGHF/D2H/Tasks/taskBsReduced.cxx b/PWGHF/D2H/Tasks/taskBsReduced.cxx index 703a527ffac..d304fe782c4 100644 --- a/PWGHF/D2H/Tasks/taskBsReduced.cxx +++ b/PWGHF/D2H/Tasks/taskBsReduced.cxx @@ -79,12 +79,18 @@ DECLARE_SOA_COLUMN(TpcChi2NClDmesProngMax, tpcChi2NClDmesProngMax, float); DECLARE_SOA_COLUMN(NSigTpcPiDmesProng0, nSigTpcPiDmesProng0, float); //! TPC Nsigma separation for D-meson prong0 with pion mass hypothesis DECLARE_SOA_COLUMN(NSigTofPiDmesProng0, nSigTofPiDmesProng0, float); //! TOF Nsigma separation for D-meson prong0 with pion mass hypothesis DECLARE_SOA_COLUMN(NSigTpcTofPiDmesProng0, nSigTpcTofPiDmesProng0, float); //! Combined TPC and TOF Nsigma separation for D-meson prong0 with pion mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcKaDmesProng0, nSigTpcKaDmesProng0, float); //! TPC Nsigma separation for D-meson prong0 with kaon mass hypothesis +DECLARE_SOA_COLUMN(NSigTofKaDmesProng0, nSigTofKaDmesProng0, float); //! TOF Nsigma separation for D-meson prong0 with kaon mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcTofKaDmesProng0, nSigTpcTofKaDmesProng0, float); //! Combined TPC and TOF Nsigma separation for D-meson prong0 with kaon mass hypothesis DECLARE_SOA_COLUMN(NSigTpcKaDmesProng1, nSigTpcKaDmesProng1, float); //! TPC Nsigma separation for D-meson prong1 with kaon mass hypothesis DECLARE_SOA_COLUMN(NSigTofKaDmesProng1, nSigTofKaDmesProng1, float); //! TOF Nsigma separation for D-meson prong1 with kaon mass hypothesis DECLARE_SOA_COLUMN(NSigTpcTofKaDmesProng1, nSigTpcTofKaDmesProng1, float); //! Combined TPC and TOF Nsigma separation for D-meson prong1 with kaon mass hypothesis DECLARE_SOA_COLUMN(NSigTpcPiDmesProng2, nSigTpcPiDmesProng2, float); //! TPC Nsigma separation for D-meson prong2 with pion mass hypothesis DECLARE_SOA_COLUMN(NSigTofPiDmesProng2, nSigTofPiDmesProng2, float); //! TOF Nsigma separation for D-meson prong2 with pion mass hypothesis -DECLARE_SOA_COLUMN(NSigTpcTofPiDmesProng2, nSigTpcTofPiDmesProng2, float); //! Combined TPC and TOF Nsigma separation for D-meson prong0 with pion mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcTofPiDmesProng2, nSigTpcTofPiDmesProng2, float); //! Combined TPC and TOF Nsigma separation for D-meson prong2 with pion mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcKaDmesProng2, nSigTpcKaDmesProng2, float); //! TPC Nsigma separation for D-meson prong2 with kaon mass hypothesis +DECLARE_SOA_COLUMN(NSigTofKaDmesProng2, nSigTofKaDmesProng2, float); //! TOF Nsigma separation for D-meson prong2 with kaon mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcTofKaDmesProng2, nSigTpcTofKaDmesProng2, float); //! Combined TPC and TOF Nsigma separation for D-meson prong2 with kaon mass hypothesis // pion features DECLARE_SOA_COLUMN(PtProng1, ptProng1, float); //! Transverse momentum of prong1 (GeV/c) DECLARE_SOA_COLUMN(AbsEtaProng1, absEtaProng1, float); //! Absolute pseudorapidity of Prong1 @@ -135,12 +141,18 @@ DECLARE_SOA_TABLE(HfRedCandBsLites, "AOD", "HFREDCANDBSLITE", //! Table with som hf_cand_bs_lite::NSigTpcPiDmesProng0, hf_cand_bs_lite::NSigTofPiDmesProng0, hf_cand_bs_lite::NSigTpcTofPiDmesProng0, + hf_cand_bs_lite::NSigTpcKaDmesProng0, + hf_cand_bs_lite::NSigTofKaDmesProng0, + hf_cand_bs_lite::NSigTpcTofKaDmesProng0, hf_cand_bs_lite::NSigTpcKaDmesProng1, hf_cand_bs_lite::NSigTofKaDmesProng1, hf_cand_bs_lite::NSigTpcTofKaDmesProng1, hf_cand_bs_lite::NSigTpcPiDmesProng2, hf_cand_bs_lite::NSigTofPiDmesProng2, hf_cand_bs_lite::NSigTpcTofPiDmesProng2, + hf_cand_bs_lite::NSigTpcKaDmesProng2, + hf_cand_bs_lite::NSigTofKaDmesProng2, + hf_cand_bs_lite::NSigTpcTofKaDmesProng2, hf_cand_bs_reduced::Prong0MlScoreBkg, hf_cand_bs_reduced::Prong0MlScorePrompt, hf_cand_bs_reduced::Prong0MlScoreNonprompt, @@ -596,9 +608,15 @@ struct HfTaskBsReduced { candDs.tpcNSigmaPiProng0(), candDs.tofNSigmaPiProng0(), candDs.tpcTofNSigmaPiProng0(), + candDs.tpcNSigmaKaProng0(), + candDs.tofNSigmaKaProng0(), + candDs.tpcTofNSigmaKaProng0(), candDs.tpcNSigmaKaProng1(), candDs.tofNSigmaKaProng1(), candDs.tpcTofNSigmaKaProng1(), + candDs.tpcNSigmaPiProng2(), + candDs.tofNSigmaPiProng2(), + candDs.tpcTofNSigmaPiProng2(), candDs.tpcNSigmaKaProng2(), candDs.tofNSigmaKaProng2(), candDs.tpcTofNSigmaKaProng2(), diff --git a/PWGHF/D2H/Tasks/taskBsToJpsiPhiReduced.cxx b/PWGHF/D2H/Tasks/taskBsToJpsiPhiReduced.cxx index 4853f6c6035..4cc6089befb 100644 --- a/PWGHF/D2H/Tasks/taskBsToJpsiPhiReduced.cxx +++ b/PWGHF/D2H/Tasks/taskBsToJpsiPhiReduced.cxx @@ -329,7 +329,7 @@ struct HfTaskBsToJpsiPhiReduced { const AxisSpec axisImpactPar{200, -0.05, 0.05}; const AxisSpec axisPtJpsi{100, 0., 50.}; const AxisSpec axisRapidity{100, -2., 2.}; - const AxisSpec axisPtB{(std::vector)binsPt, "#it{p}_{T}^{B_{s}^{0}} (GeV/#it{c})"}; + const AxisSpec axisPtB{500, 0., 50., "#it{p}_{T}^{B^{0}} (GeV/#it{c})"}; const AxisSpec axisPtPhi{100, 0.f, 10.f}; registry.add("hMass", bsCandTitle + "inv. mass J/#Psi K^{+} (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMassBs, axisPtB}}); @@ -341,7 +341,8 @@ struct HfTaskBsToJpsiPhiReduced { registry.add("hPtJpsiGen", mcParticleMatched + "J/#Psi #it{p}_{T}^{gen} (GeV/#it{c}); B_{s}^{0} " + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); registry.add("hPtPhiGen", mcParticleMatched + "#phi #it{p}_{T}^{gen} (GeV/#it{c}); B_{s}^{0} " + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); registry.add("hPtKGen", mcParticleMatched + "Kaon #it{p}_{T}^{gen} (GeV/#it{c}); B_{s}^{0} " + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); - registry.add("hYGenWithProngsInAcceptance", mcParticleMatched + "B_{s}^{0} #it{p}_{T}^{gen} (GeV/#it{c}); B_{s}^{0} #it{y}", {HistType::kTH2F, {axisPtProng, axisRapidity}}); + registry.add("hPtYGenSig", mcParticleMatched + "B_{s}^{0} #it{p}_{T}^{gen} (GeV/#it{c}); B_{s}^{0} #it{y}", {HistType::kTH2F, {axisPtB, axisRapidity}}); + registry.add("hPtYWithProngsInAccepanceGenSig", mcParticleMatched + "B_{s}^{0} #it{p}_{T}^{gen} (GeV/#it{c}); B_{s}^{0} #it{y}", {HistType::kTH2F, {axisPtB, axisRapidity}}); registry.add("hMassRecSig", bsCandMatch + "inv. mass J/#Psi K^{+} (GeV/#it{c}^{2}); B_{s}^{0} " + stringPt, {HistType::kTH2F, {axisMassBs, axisPtB}}); registry.add("hMassJpsiRecSig", bsCandMatch + "inv. mass #mu^{+}#mu^{#minus} (GeV/#it{c}^{2}); J/#Psi " + stringPt, {HistType::kTH2F, {axisMassJpsi, axisPtJpsi}}); registry.add("hMassPhiRecSig", bsCandMatch + "inv. mass K^{+}K^{#minus} (GeV/#it{c}^{2}); #phi " + stringPt, {HistType::kTH2F, {axisMassPhi, axisPtPhi}}); @@ -575,9 +576,10 @@ struct HfTaskBsToJpsiPhiReduced { registry.fill(HIST("hPtJpsiGen"), ptProngs[0], ptParticle); registry.fill(HIST("hPtKGen"), ptProngs[1], ptParticle); + registry.fill(HIST("hPtYGenSig"), ptParticle, yParticle); // generated Bs with daughters in geometrical acceptance if (prongsInAcc) { - registry.fill(HIST("hYGenWithProngsInAcceptance"), ptParticle, yParticle); + registry.fill(HIST("hPtYWithProngsInAccepanceGenSig"), ptParticle, yParticle); } } diff --git a/PWGHF/D2H/Tasks/taskCd.cxx b/PWGHF/D2H/Tasks/taskCd.cxx index 8282a7eecf5..6e35eb255c3 100644 --- a/PWGHF/D2H/Tasks/taskCd.cxx +++ b/PWGHF/D2H/Tasks/taskCd.cxx @@ -109,6 +109,7 @@ DECLARE_SOA_COLUMN(NSigmaTofPi, nSigmaTofPi, float); //! TOF nσ DECLARE_SOA_COLUMN(NItsClusters, nItsClusters, float); //! Number of ITS clusters used in the track fit DECLARE_SOA_COLUMN(NItsNClusterSize, nItsNClusterSize, float); //! Number of ITS clusters size used in the track fit DECLARE_SOA_COLUMN(NTpcClusters, nTpcClusters, float); //! Number of TPC clusters used in the track fit +DECLARE_SOA_COLUMN(TpcNClsFoundDe, tpcNClsFoundDe, uint16_t); //! Number of found TPC clusters for the selected deuteron prong DECLARE_SOA_COLUMN(NTpcSignalsDe, nTpcSignalsDe, float); //! Number of TPC signas for deuteron DECLARE_SOA_COLUMN(NTpcSignalsPi, nTpcSignalsPi, float); //! Number of TPC signas for pion DECLARE_SOA_COLUMN(NTpcSignalsKa, nTpcSignalsKa, float); //! Number of TPC signas for kaon @@ -210,7 +211,8 @@ DECLARE_SOA_TABLE(HfCandCdFull, "AOD", "HFCANDCDFULL", full::Cent, full::VtxZ, full::GIndexCol, - full::TimeStamp); + full::TimeStamp, + full::TpcNClsFoundDe); DECLARE_SOA_TABLE(HfCandCdGen, "AOD", "HFCANDCDGEN", full::Pt, @@ -624,7 +626,7 @@ struct HfTaskCd { candidate.ct(o2::constants::physics::MassCDeuteron) * CmToMum, candFlag, candSign, candidate.flagMcMatchRec(), isCandidateSwapped, hypothesisMask, candidateGlobalIndex, candidate.originMcRec(), candidate.flagMcDecayChanRec(), ctGen, collision.numContrib(), o2::hf_centrality::getCentralityColl(collision), - collision.posZ(), collision.globalIndex(), timeStamp); + collision.posZ(), collision.globalIndex(), timeStamp, deuteronProng.tpcNClsFound()); } }; @@ -905,7 +907,7 @@ struct HfTaskCd { nSigmaTpcPi, nSigmaTofPi, nSigmaTpcKa, nSigmaTofKa, candidate.ct(o2::constants::physics::MassCDeuteron), candFlag, candSign, 0, -1, hypothesisMask, candidateGlobalIndex, 0, 0, -1.f, collision.numContrib(), cent, - collision.posZ(), collision.globalIndex(), timeStamp); + collision.posZ(), collision.globalIndex(), timeStamp, deuteronProng.tpcNClsFound()); } }; diff --git a/PWGHF/D2H/Tasks/taskCharmPolarisation.cxx b/PWGHF/D2H/Tasks/taskCharmPolarisation.cxx index 0691e76b104..db34b9de314 100644 --- a/PWGHF/D2H/Tasks/taskCharmPolarisation.cxx +++ b/PWGHF/D2H/Tasks/taskCharmPolarisation.cxx @@ -47,6 +47,7 @@ #include #include +#include #include // IWYU pragma: keep (do not replace with Math/Vector3Dfwd.h) #include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) @@ -60,6 +61,7 @@ #include #include #include +#include #include using namespace o2; @@ -161,6 +163,8 @@ struct HfTaskCharmPolarisation { Configurable centralityMin{"centralityMin", 30, "Minimum centrality (0-100) to be considered in the analysis"}; Configurable centralityMax{"centralityMax", 50, "Maximum centrality (0-100) to be considered in the analysis"}; Configurable occEstimator{"occEstimator", 0, "If enabled, replace number of PV contributors with occupancy estimation (0: don't use, 1: ITS, 2: FT0C)"}; + Configurable activateAxisRotation{"activateAxisRotation", false, "Activate the rotation of the quantisation axis for cos(Theta*) calculation for phi dependence studies"}; + Configurable axisRotationAngle{"axisRotationAngle", 0.f, "Rotation angle of the quantisation axis for cos(Theta*) calculation for phi dependence studies (in rad)"}; /// activate rotational background Configurable nBkgRotations{"nBkgRotations", 0, "Number of rotated copies (background) per each original candidate"}; @@ -357,7 +361,9 @@ struct HfTaskCharmPolarisation { const AxisSpec thnAxisCosThetaStarProduction{configThnAxisCosThetaStarProduction, "cos(#vartheta_{production})"}; const AxisSpec thnAxisCosThetaStarRandom{configThnAxisCosThetaStarRandom, "cos(#vartheta_{random})"}; const AxisSpec thnAxisCosThetaStarBeam{configThnAxisCosThetaStarBeam, "cos(#vartheta_{beam})"}; - const AxisSpec thnAxisCosThetaStarEP{configThnAxisCosThetaStarEP, "cos(#vartheta_{EP})"}; // reaction plane + std::string hAxisName = "Rotated cos(#vartheta_{EP}) with" + std::to_string(axisRotationAngle) + " rad"; + const AxisSpec thnAxisCosThetaStarEP{configThnAxisCosThetaStarEP, activateAxisRotation ? hAxisName.c_str() : "cos(#vartheta_{EP})"}; // reaction plane + // const AxisSpec thnAxisCosThetaStarEP{configThnAxisCosThetaStarEP, "cos(#vartheta_{EP})"}; // reaction plane const AxisSpec thnAxisCosPhiStarEP{configThnAxisCosPhiStarEP, "cos(2#varphi_{EP})"}; // reaction plane const AxisSpec thnAxisCosSqThetaStarEP{configThnAxisCosSqThetaStarEP, "cos^{2}(#vartheta_{EP})"}; // reaction plane const AxisSpec thnAxisMlBkg{configThnAxisMlBkg, "ML bkg"}; @@ -2344,7 +2350,10 @@ struct HfTaskCharmPolarisation { // EP analysis for data float const xQvec = (*qVecs).at(0); float const yQvec = (*qVecs).at(1); - ROOT::Math::XYZVector const qVecNorm = ROOT::Math::XYZVector(yQvec, -xQvec, 0.f); + ROOT::Math::XYZVector qVecNorm = ROOT::Math::XYZVector(yQvec, -xQvec, 0.f); + if (activateAxisRotation) { + qVecNorm = ROOT::Math::RotationZ(axisRotationAngle)(qVecNorm); + } float const phiStarBeam = std::atan2(threeVecDauCM.Y(), threeVecDauCM.X()); float const psiAngle = epHelper.GetEventPlane(xQvec, yQvec, 2); float const deltaPhiStarEP = RecoDecay::constrainAngle(phiStarBeam - psiAngle, 0., 2); @@ -2356,6 +2365,9 @@ struct HfTaskCharmPolarisation { double const deltaPhi = sampleDeltaPhi(ptCharmHad); double psi = candidate.phi() - deltaPhi; ROOT::Math::XYZVector qVecNorm = ROOT::Math::XYZVector(-std::sin(psi), std::cos(psi), 0.f); + if (activateAxisRotation) { + qVecNorm = ROOT::Math::RotationZ(axisRotationAngle)(qVecNorm); + } float const cosThetaStarEP = qVecNorm.Dot(threeVecDauCM) / std::sqrt(threeVecDauCM.Mag2()); fillRecoHistos(invMassCharmHadForSparse, ptCharmHad, numPvContributors, rapidity, invMassD0, invMassKPiLc, cosThetaStarEP, -99.f, outputMl, isRotatedCandidate, origin, ptBhadMother, resoChannelLc, absEtaTrackMin, numItsClsMin, numTpcClsMin, charge, nMuons, partRecoDstar, centrality); } @@ -2721,6 +2733,9 @@ struct HfTaskCharmPolarisation { double const deltaPhi = sampleDeltaPhi(ptCharmHad); double psi = mcParticle.phi() - deltaPhi; ROOT::Math::XYZVector qVecNorm = ROOT::Math::XYZVector(-std::sin(psi), std::cos(psi), 0.f); + if (activateAxisRotation) { + qVecNorm = ROOT::Math::RotationZ(axisRotationAngle)(qVecNorm); + } float const cosThetaStarEP = qVecNorm.Dot(threeVecDauCM) / std::sqrt(threeVecDauCM.Mag2()); if constexpr (WithCent) { fillGenHistos(ptCharmHad, numPvContributors, rapidity, cosThetaStarEP, origin, ptBhadMother, areDauInAcc, resoChannelLc, charge, partRecoDstar, *centrality); diff --git a/PWGHF/D2H/Tasks/taskD0.cxx b/PWGHF/D2H/Tasks/taskD0.cxx index 66d9c90d620..59778af1fa0 100644 --- a/PWGHF/D2H/Tasks/taskD0.cxx +++ b/PWGHF/D2H/Tasks/taskD0.cxx @@ -272,7 +272,7 @@ struct HfTaskD0 { if ((storeZdcEnergy || storeZdcTime) && !(doprocessDataWithDCAFitterNWithUpc || doprocessDataWithDCAFitterNMlWithUpc)) { LOGP(fatal, "Can't enable the storeZdcEnergy and storeZdcTime without UPC process"); } - auto vbins = (std::vector)binsPt; + auto vbins = std::vector(binsPt); registry.add("hMass", "2-prong candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{500, 0., 5.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassVsPhi", "2-prong candidates vs phi;inv. mass (#pi K) (GeV/#it{c}^{2});phi (rad);entries", {HistType::kTH3F, {{120, 1.5848, 2.1848}, {vbins, "#it{p}_{T} (GeV/#it{c})"}, {32, 0, o2::constants::math::TwoPI}}}); registry.add("hDecLength", "2-prong candidates;decay length (cm);entries", {HistType::kTH2F, {{800, 0., 4.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); @@ -389,8 +389,8 @@ struct HfTaskD0 { axes.insert(axes.begin() + 2, thnAxisPromptScore); axes.insert(axes.begin() + 2, thnAxisBkgScore); } else { - axes.insert(axes.begin(), thnAxisPromptScore); axes.insert(axes.begin(), thnAxisNonPromptScore); + axes.insert(axes.begin(), thnAxisPromptScore); axes.insert(axes.begin(), thnAxisBkgScore); } } diff --git a/PWGHF/D2H/Tasks/taskDeuteronFromLb.cxx b/PWGHF/D2H/Tasks/taskDeuteronFromLb.cxx index d50ee5c5c1b..28106a38b6e 100644 --- a/PWGHF/D2H/Tasks/taskDeuteronFromLb.cxx +++ b/PWGHF/D2H/Tasks/taskDeuteronFromLb.cxx @@ -59,7 +59,6 @@ struct HfTaskDeuteronFromLb { Configurable applySkimming{"applySkimming", false, "Skimmed dataset processing"}; Configurable cfgSkimming{"cfgSkimming", "fH2fromLb", "Configurable for skimming"}; Configurable sel8{"sel8", true, "sel8 event selection"}; - Configurable separateAntideuterons{"separateAntideuterons", true, "Fill FromLb histos for primary deuterons whose mother is Lb, Primary histos for all others"}; Configurable cfgEta{"cfgEta", 0.8f, "Track eta selection"}; Configurable cfgTPCNclsFound{"cfgTPCNclsFound", 100, "Minimum TPC clusters found"}; Configurable cfgTPCChi2Ncl{"cfgTPCChi2Ncl", 4.0f, "Maximum TPC chi2 per N clusters"}; @@ -73,29 +72,43 @@ struct HfTaskDeuteronFromLb { Configurable cfgDCAmin{"cfgDCAmin", 0.05f, "Minimum DCA for deuteron PID"}; Configurable cfgDCAmax{"cfgDCAmax", 1000.0f, "Maximum DCA for deuteron PID"}; Configurable rapidityCut{"rapidityCut", 0.5f, "Rapidity cut"}; - Configurable ptThresholdforPID{"ptThresholdforPID", 1.0f, "pT threshold for PID, above this value TPC+TOF PID is used, below only TPC PID is used"}; + Configurable applyRapidityCut{"applyRapidityCut", true, "Apply the rapidity cut |y| < rapidityCut (reco and gen)"}; + Configurable ptThresholdforPID{"ptThresholdforPID", 1.0f, "pT threshold for PID, below TPC+ITS PID is used, above TPC+TOF PID is used and VETO on TPC for e,pi,K,p is applied"}; + Configurable ptThresholdforPID2{"ptThresholdforPID2", 2.2f, "pT threshold above which TPC+TOF PID with TOF veto on pi,K,p is applied (TPC veto below)"}; // PDG codes Configurable pdgCodeBeautyMeson{"pdgCodeBeautyMeson", -521, "PDG code of the beauty meson mother particle (default: B-)"}; Configurable pdgCodeBeautyBaryon{"pdgCodeBeautyBaryon", -5122, "PDG code of the beauty baryon mother particle (default: anti-Lambda_b)"}; Configurable pdgCodeDaughter{"pdgCodeDaughter", -1000010020, "PDG code of the daughter particle (default: anti-deuteron)"}; + Configurable pdgCodeHypertriton{"pdgCodeHypertriton", -1010010030, "PDG code of the hypertriton mother particle (default: anti-hypertriton)"}; + // Veto switches: if true, tracks compatible with that species are rejected + Configurable applyVetoElTPC{"applyVetoElTPC", true, "Apply TPC veto on electrons"}; + Configurable applyVetoPiTPC{"applyVetoPiTPC", true, "Apply TPC veto on pions"}; + Configurable applyVetoKaTPC{"applyVetoKaTPC", true, "Apply TPC veto on kaons"}; + Configurable applyVetoPrTPC{"applyVetoPrTPC", true, "Apply TPC veto on protons"}; + Configurable applyVetoPiTOF{"applyVetoPiTOF", true, "Apply TOF veto on pions"}; + Configurable applyVetoKaTOF{"applyVetoKaTOF", true, "Apply TOF veto on kaons"}; + Configurable applyVetoPrTOF{"applyVetoPrTOF", true, "Apply TOF veto on protons"}; int mRunNumber = 0; - float dBz = 0.f; int mCurrentRun = -1; framework::Service ccdb{}; using CollisionCandidates = o2::soa::Join; - using MCTrackCandidates = o2::soa::Join; + using TrackCandidates = o2::soa::Join; + using MCTrackCandidates = o2::soa::Join; using MCCollisionCandidates = o2::soa::Join; - using TrackCandidates = o2::soa::Join; Preslice trackIndicesPerCollision = o2::aod::track_association::collisionId; + Preslice mcParticlesPerCollision = o2::aod::mcparticle::mcCollisionId; ConfigurableAxis ptAxis{"ptAxis", {100, 0.f, 10.f}, "p_{T} GeV/c"}; ConfigurableAxis nSigmaAxis{"nSigmaAxis", {200, -10.f, 10.f}, "nSigma"}; - ConfigurableAxis dcaXyAxis{"dcaXyAxis", {1000, -0.2f, 0.2f}, "DCA xy (cm)"}; - ConfigurableAxis dcaZAxis{"dcaZAxis", {1000, -0.2f, 0.2f}, "DCA z (cm)"}; + ConfigurableAxis dcaXyAxis{"dcaXyAxis", {4000, -2.0f, 2.0f}, "DCA xy (cm)"}; + ConfigurableAxis dcaZAxis{"dcaZAxis", {4000, -2.0f, 2.0f}, "DCA z (cm)"}; + ConfigurableAxis ctauHypertritonAxis{"ctauHypertritonAxis", {100, 0.f, 30.f}, "c#tau (cm)"}; + ConfigurableAxis etaAxis{"etaAxis", {20, -1.f, 1.f}, "#eta"}; + ConfigurableAxis rapidityAxis{"rapidityAxis", {20, -1.f, 1.f}, "y"}; HistogramRegistry qaHistos{"QAHistos", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -103,12 +116,12 @@ struct HfTaskDeuteronFromLb { OutputObj zorroSummary{"zorroSummary"}; OutputObj hProcessedEvents{TH1D("hProcessedEvents", "Event filtered;; Number of events", 4, 0., 4.)}; - bool isSelectedBeautyHadron(int pdgCode) + bool isSelectedMother(int pdgCode) { - return pdgCode == pdgCodeBeautyMeson || pdgCode == pdgCodeBeautyBaryon; + return pdgCode == pdgCodeBeautyMeson || pdgCode == pdgCodeBeautyBaryon || pdgCode == pdgCodeHypertriton; } - double getBeautyHadronMass(int pdgCode) + double getMotherMass(int pdgCode) { if (pdgCode == pdgCodeBeautyMeson) { return o2::constants::physics::MassBPlus; @@ -117,6 +130,9 @@ struct HfTaskDeuteronFromLb { if (pdgCode == pdgCodeBeautyBaryon) { return o2::constants::physics::MassLambdaB0; } + if (pdgCode == pdgCodeHypertriton) { + return o2::constants::physics::MassHyperTriton; + } return -1.; } @@ -133,60 +149,123 @@ struct HfTaskDeuteronFromLb { qaHistos.add("Data/hDCAxyVsPt", "DCAxy #bar{d} vs p_{T}", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); qaHistos.add("Data/hDCAzVsPt", "DCAz #bar{d} vs p_{T}", {HistType::kTH2D, {ptAxis, dcaZAxis}}); - qaHistos.add("Data/hnSigmaTPCVsPt", "n#sigma TPC vs p_{T} for #bar{d} hypothesis; p_{T} (GeV/c); n#sigma TPC", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); - qaHistos.add("Data/hnSigmaTOFVsPt", "n#sigma TOF vs p_{T} for #bar{d} hypothesis; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); + qaHistos.add("Data/hnSigmaTPCVsPtFinalSel", "n#sigma TPC vs p_{T} for #bar{d} hypothesis; p_{T} (GeV/c); n#sigma TPC", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); + qaHistos.add("Data/hnSigmaTOFVsPtFinalSel", "n#sigma TOF vs p_{T} for #bar{d} hypothesis; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); qaHistos.add("Data/ptAntiDeuteron", "ptAntiDeuteron", {HistType::kTH1F, {ptAxis}}); qaHistos.add("Data/etaAntideuteron", "etaAntideuteron", {HistType::kTH1F, {{100, -1.0f, 1.0f, "eta #bar{d}"}}}); + qaHistos.add("Data/rapidityAntiDeuteron", "rapidityAntiDeuteron;y #bar{d};Counts", {HistType::kTH1F, {rapidityAxis}}); qaHistos.add("Data/hVtxZ", "Z-Vertex distribution after selection;Z (cm)", HistType::kTH1F, {{100, -50, 50}}); - qaHistos.add("Data/hnSigmaTOFVsPtPurity", "n#sigma TOF vs p_{T} for #bar{d} hypothesis for Data-driven purity check; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); - qaHistos.add("Data/hnSigmaTOFVsPtPurityVeto", "n#sigma TOF vs p_{T} for #bar{d} hypothesis for Data-driven purity check with TPC veto for pi,K,p; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); + qaHistos.add("Data/hnSigmaTOFVsPtPurityNoVeto", "n#sigma TOF vs p_{T} for #bar{d} hypothesis for Data-driven purity check; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); + qaHistos.add("Data/hnSigmaTOFVsPtPurityVeto", "n#sigma TOF vs p_{T} for #bar{d} hypothesis for Data-driven purity check with TPC veto for e,pi,K,p below the threshold and TOF veto for pi,K,p above the threshold; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); qaHistos.add("Data/hnSigmaTPCVsPtPurity", "n#sigma TPC vs p_{T} for #bar{d} hypothesis for Data-driven purity check; p_{T} (GeV/c); n#sigma TPC", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); qaHistos.add("Data/hnSigmaITSVsPt", "n#sigma ITS vs p_{T} for #bar{d} hypothesis, used to clean up the TPC/TOF purity sample; p_{T} (GeV/c); n#sigma ITS", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); // MC generated-level histograms qaHistos.add("MCGen/ptGeneratedBminus", "p_{T} generated B^{-};p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); qaHistos.add("MCGen/ptGeneratedAntiLambdaB", "p_{T} generated #bar{#Lambda}_{b};p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); - qaHistos.add("MCGen/ptAntiDeuteronPrimary", "p_{T} #bar{d} primary gen;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); - qaHistos.add("MCGen/ptAntiDeuteronFromBeautyHadron", "p_{T} #bar{d} from beauty gen hadrons;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); - qaHistos.add("MCGen/hMotherPdgCode", "PDG code of mother, gen level;PDG code;Counts", HistType::kTH1I, {{12000, -6000, 6000}}); qaHistos.add("MCGen/ctauBminus", "ctau of B^{-}, gen level;ctau (#mu m);Counts", HistType::kTH1F, {{25, 0., 2000.f}}); qaHistos.add("MCGen/ctauAntiLambdaB", "ctau of #bar{#Lambda}_{b}, gen level;ctau (#mu m);Counts", HistType::kTH1F, {{25, 0., 2000.f}}); - - // MC reco/MC-anchored histograms - qaHistos.add("MCReco/ptAntiDeuteronFromBminus", - "p_{T} #bar{d} from B^{-} reco/MC anchored;p_{T} (GeV/c);Counts", - HistType::kTH1F, {ptAxis}); - - qaHistos.add("MCReco/ptAntiDeuteronFromAntiLambdaB", - "p_{T} #bar{d} from #bar{#Lambda}_{b} reco/MC anchored;p_{T} (GeV/c);Counts", - HistType::kTH1F, {ptAxis}); - qaHistos.add( - "MCReco/ptAntiDeuteronPrimary", - "p_{T} #bar{d} primary reco/MC anchored;p_{T} (GeV/c);Counts", - HistType::kTH1F, - {ptAxis}); qaHistos.add("MCGen/ptAntiDeuteronFromBminus", "p_{T} #bar{d} from B^{-} gen;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); qaHistos.add("MCGen/ptAntiDeuteronFromAntiLambdaB", "p_{T} #bar{d} from #bar{#Lambda}_{b} gen;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); qaHistos.add("MCGen/ptAntiDeuteronAll", "p_{T} #bar{d} all gen;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); - qaHistos.add("MCReco/hDCAxy-Primary", "DCAxy primary reco/MC anchored;DCA xy (cm);Counts", {HistType::kTH1D, {{400, -0.2f, 0.2f, "DCA xy (cm)"}}}); - qaHistos.add("MCReco/hDCAxy-FromBeautyHadron", "DCAxy from beauty reco/MC anchored;DCA xy (cm);Counts", {HistType::kTH1D, {{400, -0.2f, 0.2f, "DCA xy (cm)"}}}); + qaHistos.add("MCGen/hEvents", "Events MC gen;;Counts", HistType::kTH1F, {{2, 0., 2.}}); + qaHistos.add("MCGen/ptGeneratedAntiHypertriton", "p_{T} generated ^{3}_{#bar{#Lambda}}#bar{H};p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCGen/ctauAntiHypertriton", "ctau of ^{3}_{#bar{#Lambda}}#bar{H}, gen level;c#tau (cm);Counts", HistType::kTH1F, {ctauHypertritonAxis}); + qaHistos.add("MCGen/ptAntiDeuteronFromAntiHypertriton", "p_{T} #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} gen;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCGen/hPtEtaAntiDeuteronFromBminus", "#bar{d} from B^{-} gen;p_{T} (GeV/c);#eta", {HistType::kTH2D, {ptAxis, etaAxis}}); + qaHistos.add("MCGen/hPtEtaAntiDeuteronFromAntiLambdaB", "#bar{d} from #bar{#Lambda}_{b} gen;p_{T} (GeV/c);#eta", {HistType::kTH2D, {ptAxis, etaAxis}}); + qaHistos.add("MCGen/hPtEtaAntiDeuteronFromAntiHypertriton", "#bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} gen;p_{T} (GeV/c);#eta", {HistType::kTH2D, {ptAxis, etaAxis}}); + qaHistos.add("MCGen/hPtEtaAntiDeuteronPrimary", "#bar{d} primary gen;p_{T} (GeV/c);#eta", {HistType::kTH2D, {ptAxis, etaAxis}}); + qaHistos.add("MCGen/rapidityAntiDeuteronFromBminus", "y #bar{d} from B^{-} gen;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCGen/rapidityAntiDeuteronFromAntiLambdaB", "y #bar{d} from #bar{#Lambda}_{b} gen;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCGen/rapidityAntiDeuteronFromAntiHypertriton", "y #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} gen;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCGen/rapidityAntiDeuteronPrimary", "y #bar{d} primary gen;y;Counts", HistType::kTH1F, {rapidityAxis}); + // MC reco/MC-anchored histograms + qaHistos.add("MCReco/hDCAxyVsPtDeFromBminus", "DCAxy #bar{d} from B^{-} vs p_{T};p_{T} (GeV/c);DCA_{xy} (cm)", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); + qaHistos.add("MCReco/hDCAxyVsPtDeFromAntiLambdaB", "DCAxy #bar{d} from #bar{#Lambda}_{b} vs p_{T};p_{T} (GeV/c);DCA_{xy} (cm)", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); + qaHistos.add("MCReco/hDCAxyVsPtDeFromAntiHypertriton", "DCAxy #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} vs p_{T};p_{T} (GeV/c);DCA_{xy} (cm)", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); + qaHistos.add("MCReco/hDCAxyVsPtDePrimary", "DCAxy #bar{d} primary vs p_{T};p_{T} (GeV/c);DCA_{xy} (cm)", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); + qaHistos.add("MCReco/hDCAzVsPtDeFromBminus", "DCAz #bar{d} from B^{-} vs p_{T};p_{T} (GeV/c);DCA_{z} (cm)", {HistType::kTH2D, {ptAxis, dcaZAxis}}); + qaHistos.add("MCReco/hDCAzVsPtDeFromAntiLambdaB", "DCAz #bar{d} from #bar{#Lambda}_{b} vs p_{T};p_{T} (GeV/c);DCA_{z} (cm)", {HistType::kTH2D, {ptAxis, dcaZAxis}}); + qaHistos.add("MCReco/hDCAzVsPtDeFromAntiHypertriton", "DCAz #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} vs p_{T};p_{T} (GeV/c);DCA_{z} (cm)", {HistType::kTH2D, {ptAxis, dcaZAxis}}); + qaHistos.add("MCReco/hDCAzVsPtDePrimary", "DCAz #bar{d} primary vs p_{T};p_{T} (GeV/c);DCA_{z} (cm)", {HistType::kTH2D, {ptAxis, dcaZAxis}}); + qaHistos.add("MCReco/hDCAxyVsPtDeFromBminusSameMC", "DCAxy #bar{d} from B^{-} vs p_{T}, same MC collision;p_{T} (GeV/c);DCA_{xy} (cm)", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); + qaHistos.add("MCReco/hDCAxyVsPtDeFromAntiLambdaBSameMC", "DCAxy #bar{d} from #bar{#Lambda}_{b} vs p_{T}, same MC collision;p_{T} (GeV/c);DCA_{xy} (cm)", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); + qaHistos.add("MCReco/hDCAxyVsPtDeFromAntiHypertritonSameMC", "DCAxy #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} vs p_{T}, same MC collision;p_{T} (GeV/c);DCA_{xy} (cm)", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); + qaHistos.add("MCReco/hDCAxyVsPtDePrimarySameMC", "DCAxy #bar{d} primary vs p_{T}, same MC collision;p_{T} (GeV/c);DCA_{xy} (cm)", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); + qaHistos.add("MCReco/hDCAzVsPtDeFromBminusSameMC", "DCAz #bar{d} from B^{-} vs p_{T}, same MC collision;p_{T} (GeV/c);DCA_{z} (cm)", {HistType::kTH2D, {ptAxis, dcaZAxis}}); + qaHistos.add("MCReco/hDCAzVsPtDeFromAntiLambdaBSameMC", "DCAz #bar{d} from #bar{#Lambda}_{b} vs p_{T}, same MC collision;p_{T} (GeV/c);DCA_{z} (cm)", {HistType::kTH2D, {ptAxis, dcaZAxis}}); + qaHistos.add("MCReco/hDCAzVsPtDeFromAntiHypertritonSameMC", "DCAz #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} vs p_{T}, same MC collision;p_{T} (GeV/c);DCA_{z} (cm)", {HistType::kTH2D, {ptAxis, dcaZAxis}}); + qaHistos.add("MCReco/hDCAzVsPtDePrimarySameMC", "DCAz #bar{d} primary vs p_{T}, same MC collision;p_{T} (GeV/c);DCA_{z} (cm)", {HistType::kTH2D, {ptAxis, dcaZAxis}}); + qaHistos.add("MCReco/ptAntiDeuteronFromBminusSameMC", "p_{T} #bar{d} from B^{-} reco/MC anchored, same MC collision;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronFromAntiLambdaBSameMC", "p_{T} #bar{d} from #bar{#Lambda}_{b} reco/MC anchored, same MC collision;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronFromAntiHypertritonSameMC", "p_{T} #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} reco/MC anchored, same MC collision;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronPrimarySameMC", "p_{T} #bar{d} primary reco/MC anchored, same MC collision;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronFromBminus", "p_{T} #bar{d} from B^{-} reco/MC anchored;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronFromAntiLambdaB", "p_{T} #bar{d} from #bar{#Lambda}_{b} reco/MC anchored;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronFromAntiHypertriton", "p_{T} #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} reco/MC anchored;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronPrimary", "p_{T} #bar{d} primary reco/MC anchored;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/hEvents", "Events MC reco;;Counts", HistType::kTH1F, {{3, 0., 3.}}); qaHistos.add("MCReco/hMotherPdgCode", "PDG code of mother, reco/MC anchored;PDG code;Counts", HistType::kTH1I, {{12000, -6000, 6000}}); qaHistos.add("MCReco/ctauBminus", "ctau of B^{-}, reco/MC anchored;ctau (#mu m);Counts", HistType::kTH1F, {{25, 0., 2000.f}}); qaHistos.add("MCReco/ctauAntiLambdaB", "ctau of #bar{#Lambda}_{b}, reco/MC anchored;ctau (#mu m);Counts", HistType::kTH1F, {{25, 0., 2000.f}}); + qaHistos.add("MCReco/ctauAntiHypertriton", "ctau of ^{3}_{#bar{#Lambda}}#bar{H}, reco/MC anchored;c#tau (cm);Counts", HistType::kTH1F, {ctauHypertritonAxis}); qaHistos.add("MCReco/ptAntiDeuteronPIDSelected", "p_{T} #bar{d} reco/MC anchored PID selection;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); qaHistos.add("MCReco/ptAntiDeuteronPIDSelectedTrue", "p_{T} #bar{d} reco/MC anchored PID and PDG;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); - qaHistos.add("MCReco/hnSigmaTPCVsPt", "n#sigma TPC vs p_{T} for #bar{d} hypothesis for MC; p_{T} (GeV/c); n#sigma TPC", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); - qaHistos.add("MCReco/hnSigmaTOFVsPt", "n#sigma TOF vs p_{T} for #bar{d} hypothesis for MC; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); qaHistos.add("MCReco/ptAntiDeuteronTPCOnlyPIDSelected", "p_{T} #bar{d} reco/MC anchored TPC only PID selection;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); qaHistos.add("MCReco/ptAntiDeuteronTPCOnlyPIDSelectedTrue", "p_{T} #bar{d} reco/MC anchored TPC only PID and PDG;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); qaHistos.add("MCReco/ptAntiDeuteronTPCorTPCTOFPIDSelected", "p_{T} #bar{d} reco/MC anchored TPC or TPC+TOF PID selection;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); qaHistos.add("MCReco/ptAntiDeuteronTPCorTPCTOFPIDSelectedTrue", "p_{T} #bar{d} reco/MC anchored TPC or TPC+TOF PID and PDG;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronGenFromBminus", "p_{T} #bar{d} from B^{-} gen in reco events;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronGenFromAntiLambdaB", "p_{T} #bar{d} from #bar{#Lambda}_{b} gen in reco events;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronGenFromAntiHypertriton", "p_{T} #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} gen in reco events;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronGenPrimary", "p_{T} #bar{d} primary gen in reco events;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + // MC reco: eta and rapidity of reconstructed tracks passing the final selection + qaHistos.add("MCReco/etaAntiDeuteronFromBminus", "#eta #bar{d} from B^{-} reco/MC anchored;#eta;Counts", HistType::kTH1F, {etaAxis}); + qaHistos.add("MCReco/etaAntiDeuteronFromAntiLambdaB", "#eta #bar{d} from #bar{#Lambda}_{b} reco/MC anchored;#eta;Counts", HistType::kTH1F, {etaAxis}); + qaHistos.add("MCReco/etaAntiDeuteronFromAntiHypertriton", "#eta #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} reco/MC anchored;#eta;Counts", HistType::kTH1F, {etaAxis}); + qaHistos.add("MCReco/etaAntiDeuteronPrimary", "#eta #bar{d} primary reco/MC anchored;#eta;Counts", HistType::kTH1F, {etaAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronFromBminus", "y #bar{d} from B^{-} reco/MC anchored;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronFromAntiLambdaB", "y #bar{d} from #bar{#Lambda}_{b} reco/MC anchored;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronFromAntiHypertriton", "y #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} reco/MC anchored;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronPrimary", "y #bar{d} primary reco/MC anchored;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/etaAntiDeuteronFromBminusSameMC", "#eta #bar{d} from B^{-} reco/MC anchored, same MC collision;#eta;Counts", HistType::kTH1F, {etaAxis}); + qaHistos.add("MCReco/etaAntiDeuteronFromAntiLambdaBSameMC", "#eta #bar{d} from #bar{#Lambda}_{b} reco/MC anchored, same MC collision;#eta;Counts", HistType::kTH1F, {etaAxis}); + qaHistos.add("MCReco/etaAntiDeuteronFromAntiHypertritonSameMC", "#eta #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} reco/MC anchored, same MC collision;#eta;Counts", HistType::kTH1F, {etaAxis}); + qaHistos.add("MCReco/etaAntiDeuteronPrimarySameMC", "#eta #bar{d} primary reco/MC anchored, same MC collision;#eta;Counts", HistType::kTH1F, {etaAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronFromBminusSameMC", "y #bar{d} from B^{-} reco/MC anchored, same MC collision;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronFromAntiLambdaBSameMC", "y #bar{d} from #bar{#Lambda}_{b} reco/MC anchored, same MC collision;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronFromAntiHypertritonSameMC", "y #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} reco/MC anchored, same MC collision;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronPrimarySameMC", "y #bar{d} primary reco/MC anchored, same MC collision;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/hPtEtaAntiDeuteronGenFromBminus", "#bar{d} from B^{-} gen in reco events;p_{T} (GeV/c);#eta", {HistType::kTH2D, {ptAxis, etaAxis}}); + qaHistos.add("MCReco/hPtEtaAntiDeuteronGenFromAntiLambdaB", "#bar{d} from #bar{#Lambda}_{b} gen in reco events;p_{T} (GeV/c);#eta", {HistType::kTH2D, {ptAxis, etaAxis}}); + qaHistos.add("MCReco/hPtEtaAntiDeuteronGenFromAntiHypertriton", "#bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} gen in reco events;p_{T} (GeV/c);#eta", {HistType::kTH2D, {ptAxis, etaAxis}}); + qaHistos.add("MCReco/hPtEtaAntiDeuteronGenPrimary", "#bar{d} primary gen in reco events;p_{T} (GeV/c);#eta", {HistType::kTH2D, {ptAxis, etaAxis}}); + qaHistos.add("MCReco/rapidityAntiDeuteronGenFromBminus", "y #bar{d} from B^{-} gen in reco events;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronGenFromAntiLambdaB", "y #bar{d} from #bar{#Lambda}_{b} gen in reco events;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronGenFromAntiHypertriton", "y #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} gen in reco events;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronGenPrimary", "y #bar{d} primary gen in reco events;y;Counts", HistType::kTH1F, {rapidityAxis}); hProcessedEvents->GetXaxis()->SetBinLabel(1, "Events processed"); hProcessedEvents->GetXaxis()->SetBinLabel(2, "ZORRO"); hProcessedEvents->GetXaxis()->SetBinLabel(3, "sel8"); hProcessedEvents->GetXaxis()->SetBinLabel(4, "z-vertex"); + auto hEvents = qaHistos.get(HIST("MCReco/hEvents")); + hEvents->GetXaxis()->SetBinLabel(1, "All"); + hEvents->GetXaxis()->SetBinLabel(2, "sel8"); + hEvents->GetXaxis()->SetBinLabel(3, "z-vertex"); + auto hEventsGen = qaHistos.get(HIST("MCGen/hEvents")); + hEventsGen->GetXaxis()->SetBinLabel(1, "All"); + hEventsGen->GetXaxis()->SetBinLabel(2, "z-vertex"); + + qaHistos.add("MCReco/hMotherCategory", "Mother of #bar{d};;Counts", HistType::kTH1F, {{5, 0., 5.}}); + auto hMotherCategory = qaHistos.get(HIST("MCReco/hMotherCategory")); + hMotherCategory->GetXaxis()->SetBinLabel(1, "B^{-}"); + hMotherCategory->GetXaxis()->SetBinLabel(2, "#bar{#Lambda}_{b}"); + hMotherCategory->GetXaxis()->SetBinLabel(3, "^{3}_{#bar{#Lambda}}#bar{H}"); + hMotherCategory->GetXaxis()->SetBinLabel(4, "primary"); + hMotherCategory->GetXaxis()->SetBinLabel(5, "other"); } void initCCDB(o2::aod::BCsWithTimestamps::iterator const& bc) @@ -194,12 +273,12 @@ struct HfTaskDeuteronFromLb { if (mRunNumber == bc.runNumber()) { return; } - dBz = 0.f; if (applySkimming) { zorro.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), cfgSkimming.value); zorro.populateHistRegistry(registry, bc.runNumber()); } + mRunNumber = bc.runNumber(); } template @@ -208,6 +287,9 @@ struct HfTaskDeuteronFromLb { if (std::abs(track.eta()) > cfgEta) { return false; } + if (applyRapidityCut && std::abs(track.rapidity(o2::constants::physics::MassDeuteron)) > rapidityCut) { + return false; + } if (!track.hasITS()) { return false; } @@ -244,6 +326,7 @@ struct HfTaskDeuteronFromLb { TrackCandidates const& tracks, o2::aod::BCsWithTimestamps const&) { + auto tracksWithItsPid = o2::soa::Attach(tracks); for (const auto& collision : collisions) { if (mCurrentRun != collision.bc_as().runNumber()) { auto* grpo = ccdb->getForTimeStamp("GLO/Config/GRPMagField", collision.bc_as().timestamp()); @@ -271,7 +354,6 @@ struct HfTaskDeuteronFromLb { qaHistos.fill(HIST("Data/hVtxZ"), collision.posZ()); const auto& trackIdsThisCollision = trackIndices.sliceBy(trackIndicesPerCollision, collision.globalIndex()); - auto tracksWithItsPid = o2::soa::Attach(tracks); for (const auto& trackId : trackIdsThisCollision) { const auto& track = tracksWithItsPid.rawIteratorAt(trackId.trackId()); @@ -296,134 +378,317 @@ struct HfTaskDeuteronFromLb { qaHistos.fill(HIST("Data/hnSigmaITSVsPt"), track.pt(), track.itsNSigmaDe()); - const bool isTPCDeClean = isTPCDe && - std::abs(track.tpcNSigmaPi()) > cfgTPCNsigma && - std::abs(track.tpcNSigmaKa()) > cfgTPCNsigma && - std::abs(track.tpcNSigmaPr()) > cfgTPCNsigma; + const bool isElectronForTPC = std::abs(track.tpcNSigmaEl()) < cfgTPCNsigma; + const bool isPionForTPC = std::abs(track.tpcNSigmaPi()) < cfgTPCNsigma; + const bool isKaonForTPC = std::abs(track.tpcNSigmaKa()) < cfgTPCNsigma; + const bool isProtonForTPC = std::abs(track.tpcNSigmaPr()) < cfgTPCNsigma; + const bool isPionForTOF = std::abs(track.tofNSigmaPi()) < cfgTofNsigma; + const bool isKaonForTOF = std::abs(track.tofNSigmaKa()) < cfgTofNsigma; + const bool isProtonForTOF = std::abs(track.tofNSigmaPr()) < cfgTofNsigma; + + const bool isNotElectronForTPC = !applyVetoElTPC || !isElectronForTPC; + const bool isNotPionForTPC = !applyVetoPiTPC || !isPionForTPC; + const bool isNotKaonForTPC = !applyVetoKaTPC || !isKaonForTPC; + const bool isNotProtonForTPC = !applyVetoPrTPC || !isProtonForTPC; + const bool isNotPionForTOF = !applyVetoPiTOF || !isPionForTOF; + const bool isNotKaonForTOF = !applyVetoKaTOF || !isKaonForTOF; + const bool isNotProtonForTOF = !applyVetoPrTOF || !isProtonForTOF; + + const bool isTPCandVetoTPC = isTPCDe && isNotElectronForTPC && isNotPionForTPC && isNotKaonForTPC && isNotProtonForTPC; + const bool isTPCandVetoTOF = isTPCDe && isNotPionForTOF && isNotKaonForTOF && isNotProtonForTOF; + + const bool isLowPt = track.pt() < ptThresholdforPID; + const bool isMidPt = track.pt() >= ptThresholdforPID && track.pt() < ptThresholdforPID2; + const bool isHighPt = track.pt() >= ptThresholdforPID2; + + bool passVeto = false; + if (isMidPt) { + passVeto = isTPCandVetoTPC; // veto TPC for e,pi,K,p + } else if (isHighPt) { + passVeto = isTPCandVetoTOF; // veto TOF for pi,K,p + } - if (track.pt() < ptThresholdforPID) { - if (isITSDe) { - qaHistos.fill(HIST("Data/hnSigmaTPCVsPtPurity"), track.pt(), track.tpcNSigmaDe()); - } - } else { - if (isTPCDe && track.hasTOF()) { - qaHistos.fill(HIST("Data/hnSigmaTOFVsPtPurity"), track.pt(), track.tofNSigmaDe()); - } - if (isTPCDeClean && track.hasTOF()) { + // pT < 1.0 GeV/c: purity TPC using ITS + if (isLowPt && isITSDe) { + qaHistos.fill(HIST("Data/hnSigmaTPCVsPtPurity"), track.pt(), track.tpcNSigmaDe()); + } + // pT >= 1.0 GeV/c: purity TOF with and without vetoes + if (!isLowPt && isTPCDe && track.hasTOF()) { + qaHistos.fill(HIST("Data/hnSigmaTOFVsPtPurityNoVeto"), track.pt(), track.tofNSigmaDe()); + if (passVeto) { qaHistos.fill(HIST("Data/hnSigmaTOFVsPtPurityVeto"), track.pt(), track.tofNSigmaDe()); } } - if (track.pt() < ptThresholdforPID && isTPCDe && isITSDe) { - qaHistos.fill(HIST("Data/ptAntiDeuteron"), track.pt()); - qaHistos.fill(HIST("Data/etaAntideuteron"), track.eta()); - qaHistos.fill(HIST("Data/hDCAxyVsPt"), track.pt(), dca[0]); - qaHistos.fill(HIST("Data/hDCAzVsPt"), track.pt(), dca[1]); - qaHistos.fill(HIST("Data/hnSigmaTPCVsPt"), track.pt(), track.tpcNSigmaDe()); - qaHistos.fill(HIST("Data/hnSigmaTOFVsPt"), track.pt(), track.tofNSigmaDe()); - } - if (track.pt() >= ptThresholdforPID && isTPCDe && isTOFDe && track.hasTOF()) { + const bool passFinalSel = (isLowPt && isTPCDe && isITSDe) || (!isLowPt && passVeto && isTOFDe && track.hasTOF()); + + if (passFinalSel) { qaHistos.fill(HIST("Data/ptAntiDeuteron"), track.pt()); qaHistos.fill(HIST("Data/etaAntideuteron"), track.eta()); + qaHistos.fill(HIST("Data/rapidityAntiDeuteron"), track.rapidity(o2::constants::physics::MassDeuteron)); qaHistos.fill(HIST("Data/hDCAxyVsPt"), track.pt(), dca[0]); qaHistos.fill(HIST("Data/hDCAzVsPt"), track.pt(), dca[1]); - qaHistos.fill(HIST("Data/hnSigmaTPCVsPt"), track.pt(), track.tpcNSigmaDe()); - qaHistos.fill(HIST("Data/hnSigmaTOFVsPt"), track.pt(), track.tofNSigmaDe()); + qaHistos.fill(HIST("Data/hnSigmaTPCVsPtFinalSel"), track.pt(), track.tpcNSigmaDe()); + if (!isLowPt) { + qaHistos.fill(HIST("Data/hnSigmaTOFVsPtFinalSel"), track.pt(), track.tofNSigmaDe()); + } } } } } PROCESS_SWITCH(HfTaskDeuteronFromLb, processData, "processData", true); - void processMC(MCCollisionCandidates::iterator const& collision, MCTrackCandidates const& tracks, o2::aod::McParticles const&) + void processMC(MCCollisionCandidates const& collisions, + o2::aod::TrackAssoc const& trackIndices, + MCTrackCandidates const& tracks, + o2::aod::McParticles const& mcParticles, + o2::aod::BCsWithTimestamps const&) { - for (const auto& track : tracks) { - if (std::abs(track.dcaXY()) < cfgDCAmin || std::abs(track.dcaXY()) > cfgDCAmax) { - continue; + auto tracksWithItsPid = o2::soa::Attach(tracks); + tracksWithItsPid.bindExternalIndices(&mcParticles); + + for (const auto& collision : collisions) { + const auto& bc = collision.bc_as(); + if (mCurrentRun != bc.runNumber()) { + auto* grpo = ccdb->getForTimeStamp("GLO/Config/GRPMagField", bc.timestamp()); + o2::base::Propagator::initFieldFromGRP(grpo); + mCurrentRun = bc.runNumber(); } - if (!passedSingleTrackSelection(track)) { + + qaHistos.fill(HIST("MCReco/hEvents"), 0.5); + if (sel8 && !collision.sel8()) { continue; } - - if (!track.has_mcParticle()) { + qaHistos.fill(HIST("MCReco/hEvents"), 1.5); + if (std::abs(collision.posZ()) > cutzvertex) { continue; } + qaHistos.fill(HIST("MCReco/hEvents"), 2.5); - auto mcParticle = track.mcParticle(); - const bool isTrueDeuteron = (mcParticle.pdgCode() == pdgCodeDaughter); - const bool isTPCDe = std::abs(track.tpcNSigmaDe()) < cfgTPCNsigma; - const bool isTOFDe = std::abs(track.tofNSigmaDe()) < cfgTofNsigma; - const bool selTpcOrTpcTof = isTPCDe && (!track.hasTOF() || isTOFDe); + if (collision.has_mcCollision()) { + const auto mcParticlesThisEvent = mcParticles.sliceBy(mcParticlesPerCollision, collision.mcCollisionId()); - if (isTPCDe) { - qaHistos.fill(HIST("MCReco/ptAntiDeuteronTPCOnlyPIDSelected"), track.pt()); - if (isTrueDeuteron) { - qaHistos.fill(HIST("MCReco/ptAntiDeuteronTPCOnlyPIDSelectedTrue"), track.pt()); + for (const auto& mcParticle : mcParticlesThisEvent) { + + if (mcParticle.pdgCode() != pdgCodeDaughter) { + continue; + } + if (std::abs(mcParticle.eta()) > cfgEta || (applyRapidityCut && std::abs(mcParticle.y()) > rapidityCut)) { + continue; + } + if (mcParticle.pt() < cfgMinPt || mcParticle.pt() > cfgMaxPt) { + continue; + } + bool fromMother = false; + for (const auto& mom : mcParticle.mothers_as()) { + if (mom.pdgCode() == pdgCodeBeautyMeson) { + qaHistos.fill(HIST("MCReco/ptAntiDeuteronGenFromBminus"), mcParticle.pt()); + qaHistos.fill(HIST("MCReco/hPtEtaAntiDeuteronGenFromBminus"), mcParticle.pt(), mcParticle.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronGenFromBminus"), mcParticle.y()); + fromMother = true; + break; + } + if (mom.pdgCode() == pdgCodeBeautyBaryon) { + qaHistos.fill(HIST("MCReco/ptAntiDeuteronGenFromAntiLambdaB"), mcParticle.pt()); + qaHistos.fill(HIST("MCReco/hPtEtaAntiDeuteronGenFromAntiLambdaB"), mcParticle.pt(), mcParticle.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronGenFromAntiLambdaB"), mcParticle.y()); + fromMother = true; + break; + } + if (mom.pdgCode() == pdgCodeHypertriton) { + qaHistos.fill(HIST("MCReco/ptAntiDeuteronGenFromAntiHypertriton"), mcParticle.pt()); + qaHistos.fill(HIST("MCReco/hPtEtaAntiDeuteronGenFromAntiHypertriton"), mcParticle.pt(), mcParticle.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronGenFromAntiHypertriton"), mcParticle.y()); + fromMother = true; + break; + } + } + if (!fromMother && mcParticle.isPhysicalPrimary()) { + qaHistos.fill(HIST("MCReco/ptAntiDeuteronGenPrimary"), mcParticle.pt()); + qaHistos.fill(HIST("MCReco/hPtEtaAntiDeuteronGenPrimary"), mcParticle.pt(), mcParticle.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronGenPrimary"), mcParticle.y()); + } } } - if (track.hasTOF()) { - qaHistos.fill(HIST("MCReco/hnSigmaTPCVsPt"), track.pt(), track.tpcNSigmaDe()); - qaHistos.fill(HIST("MCReco/hnSigmaTOFVsPt"), track.pt(), track.tofNSigmaDe()); - } + const auto& trackIdsThisCollision = trackIndices.sliceBy(trackIndicesPerCollision, collision.globalIndex()); - if (selTpcOrTpcTof) { - qaHistos.fill(HIST("MCReco/ptAntiDeuteronTPCorTPCTOFPIDSelected"), track.pt()); - if (isTrueDeuteron) { - qaHistos.fill(HIST("MCReco/ptAntiDeuteronTPCorTPCTOFPIDSelectedTrue"), track.pt()); + for (const auto& trackId : trackIdsThisCollision) { + const auto& track = tracksWithItsPid.rawIteratorAt(trackId.trackId()); + std::array dca{track.dcaXY(), track.dcaZ()}; + + if (track.collisionId() != collision.globalIndex()) { + auto trackPar = getTrackParCov(track); + o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, trackPar, 2.f, noMatCorr, &dca); } - } - if (isTPCDe && isTOFDe && track.hasTOF()) { - qaHistos.fill(HIST("MCReco/ptAntiDeuteronPIDSelected"), track.pt()); - if (isTrueDeuteron) { - qaHistos.fill(HIST("MCReco/ptAntiDeuteronPIDSelectedTrue"), track.pt()); + if (std::abs(dca[0]) < cfgDCAmin || std::abs(dca[0]) > cfgDCAmax) { + continue; + } + if (!passedSingleTrackSelection(track)) { + continue; } - } else { - continue; - } - if (isTrueDeuteron) { - if (std::abs(mcParticle.y()) > rapidityCut) { + if (!track.has_mcParticle()) { continue; } - if (mcParticle.isPhysicalPrimary()) { + + auto mcParticle = track.mcParticle(); + const bool isSameMcCollision = collision.has_mcCollision() && (mcParticle.mcCollisionId() == collision.mcCollisionId()); + const bool isTrueDeuteron = (mcParticle.pdgCode() == pdgCodeDaughter); + const bool isTPCDe = std::abs(track.tpcNSigmaDe()) < cfgTPCNsigma; + const bool isTOFDe = std::abs(track.tofNSigmaDe()) < cfgTofNsigma; + const bool isITSDe = track.itsNSigmaDe() > cfgITSNsigma; + + const bool selTpcOrTpcTof = isTPCDe && (!track.hasTOF() || isTOFDe); + + if (isTPCDe) { + qaHistos.fill(HIST("MCReco/ptAntiDeuteronTPCOnlyPIDSelected"), track.pt()); + if (isTrueDeuteron) { + qaHistos.fill(HIST("MCReco/ptAntiDeuteronTPCOnlyPIDSelectedTrue"), track.pt()); + } + } + + if (selTpcOrTpcTof) { + qaHistos.fill(HIST("MCReco/ptAntiDeuteronTPCorTPCTOFPIDSelected"), track.pt()); + if (isTrueDeuteron) { + qaHistos.fill(HIST("MCReco/ptAntiDeuteronTPCorTPCTOFPIDSelectedTrue"), track.pt()); + } + } + // Purity study: TPC && TOF + if (isTPCDe && isTOFDe && track.hasTOF()) { + qaHistos.fill(HIST("MCReco/ptAntiDeuteronPIDSelected"), track.pt()); + if (isTrueDeuteron) { + qaHistos.fill(HIST("MCReco/ptAntiDeuteronPIDSelectedTrue"), track.pt()); + } + } + + const bool isElectronForTPC = std::abs(track.tpcNSigmaEl()) < cfgTPCNsigma; + const bool isPionForTPC = std::abs(track.tpcNSigmaPi()) < cfgTPCNsigma; + const bool isKaonForTPC = std::abs(track.tpcNSigmaKa()) < cfgTPCNsigma; + const bool isProtonForTPC = std::abs(track.tpcNSigmaPr()) < cfgTPCNsigma; + const bool isPionForTOF = std::abs(track.tofNSigmaPi()) < cfgTofNsigma; + const bool isKaonForTOF = std::abs(track.tofNSigmaKa()) < cfgTofNsigma; + const bool isProtonForTOF = std::abs(track.tofNSigmaPr()) < cfgTofNsigma; + + const bool isNotElectronForTPC = !applyVetoElTPC || !isElectronForTPC; + const bool isNotPionForTPC = !applyVetoPiTPC || !isPionForTPC; + const bool isNotKaonForTPC = !applyVetoKaTPC || !isKaonForTPC; + const bool isNotProtonForTPC = !applyVetoPrTPC || !isProtonForTPC; + const bool isNotPionForTOF = !applyVetoPiTOF || !isPionForTOF; + const bool isNotKaonForTOF = !applyVetoKaTOF || !isKaonForTOF; + const bool isNotProtonForTOF = !applyVetoPrTOF || !isProtonForTOF; + + const bool isTPCandVetoTPC = isTPCDe && isNotElectronForTPC && isNotPionForTPC && isNotKaonForTPC && isNotProtonForTPC; + const bool isTPCandVetoTOF = isTPCDe && isNotPionForTOF && isNotKaonForTOF && isNotProtonForTOF; + + const bool isLowPt = track.pt() < ptThresholdforPID; + const bool isMidPt = track.pt() >= ptThresholdforPID && track.pt() < ptThresholdforPID2; + const bool isHighPt = track.pt() >= ptThresholdforPID2; + + bool passVeto = false; + if (isMidPt) { + passVeto = isTPCandVetoTPC; // veto TPC for e,pi,K,p + } else if (isHighPt) { + passVeto = isTPCandVetoTOF; // veto TOF for pi,K,p + } + + if (isTrueDeuteron) { int motherPdg = 0; - if (separateAntideuterons) { - for (const auto& mom : mcParticle.mothers_as()) { - qaHistos.fill(HIST("MCReco/hMotherPdgCode"), mom.pdgCode()); - if (mom.pdgCode() == pdgCodeBeautyMeson || mom.pdgCode() == pdgCodeBeautyBaryon) { - motherPdg = mom.pdgCode(); - double dx = mcParticle.vx() - collision.posX(); - double dy = mcParticle.vy() - collision.posY(); - double dz = mcParticle.vz() - collision.posZ(); - - double flightDistance = std::sqrt(dx * dx + dy * dy + dz * dz); - double massBeauty = getBeautyHadronMass(mom.pdgCode()); - - if (massBeauty > 0.) { - double ctauBeauty = flightDistance / mom.p() * massBeauty; - ctauBeauty *= 1.e4; - - if (motherPdg == pdgCodeBeautyMeson) { - qaHistos.fill(HIST("MCReco/ctauBminus"), ctauBeauty); - } else if (motherPdg == pdgCodeBeautyBaryon) { - qaHistos.fill(HIST("MCReco/ctauAntiLambdaB"), ctauBeauty); - } + for (const auto& mom : mcParticle.mothers_as()) { + qaHistos.fill(HIST("MCReco/hMotherPdgCode"), mom.pdgCode()); + if (mom.pdgCode() == pdgCodeBeautyMeson || mom.pdgCode() == pdgCodeBeautyBaryon || mom.pdgCode() == pdgCodeHypertriton) { + motherPdg = mom.pdgCode(); + double dx = mcParticle.vx() - mom.vx(); + double dy = mcParticle.vy() - mom.vy(); + double dz = mcParticle.vz() - mom.vz(); + + double flightDistance = std::sqrt(dx * dx + dy * dy + dz * dz); + double massBeauty = getMotherMass(mom.pdgCode()); + + if (massBeauty > 0.) { + double ctauBeauty = flightDistance / mom.p() * massBeauty; + + if (motherPdg == pdgCodeBeautyMeson) { + qaHistos.fill(HIST("MCReco/ctauBminus"), ctauBeauty * 1.e4); + } else if (motherPdg == pdgCodeBeautyBaryon) { + qaHistos.fill(HIST("MCReco/ctauAntiLambdaB"), ctauBeauty * 1.e4); + } else if (motherPdg == pdgCodeHypertriton) { + qaHistos.fill(HIST("MCReco/ctauAntiHypertriton"), ctauBeauty); // cm } - break; } + break; } } + if (motherPdg == pdgCodeBeautyMeson) { - qaHistos.fill(HIST("MCReco/hDCAxy-FromBeautyHadron"), track.dcaXY()); - qaHistos.fill(HIST("MCReco/ptAntiDeuteronFromBminus"), track.pt()); + qaHistos.fill(HIST("MCReco/hMotherCategory"), 0.5); } else if (motherPdg == pdgCodeBeautyBaryon) { - qaHistos.fill(HIST("MCReco/hDCAxy-FromBeautyHadron"), track.dcaXY()); - qaHistos.fill(HIST("MCReco/ptAntiDeuteronFromAntiLambdaB"), track.pt()); + qaHistos.fill(HIST("MCReco/hMotherCategory"), 1.5); + } else if (motherPdg == pdgCodeHypertriton) { + qaHistos.fill(HIST("MCReco/hMotherCategory"), 2.5); + } else if (mcParticle.isPhysicalPrimary()) { + qaHistos.fill(HIST("MCReco/hMotherCategory"), 3.5); } else { - qaHistos.fill(HIST("MCReco/hDCAxy-Primary"), track.dcaXY()); - qaHistos.fill(HIST("MCReco/ptAntiDeuteronPrimary"), track.pt()); + qaHistos.fill(HIST("MCReco/hMotherCategory"), 4.5); + } + // same final selection as in data + const bool passFinalSel = (isLowPt && isTPCDe && isITSDe) || (!isLowPt && passVeto && isTOFDe && track.hasTOF()); + if (passFinalSel) { + const float rapidity = track.rapidity(o2::constants::physics::MassDeuteron); + if (motherPdg == pdgCodeBeautyMeson) { + qaHistos.fill(HIST("MCReco/hDCAxyVsPtDeFromBminus"), track.pt(), dca[0]); + qaHistos.fill(HIST("MCReco/hDCAzVsPtDeFromBminus"), track.pt(), dca[1]); + qaHistos.fill(HIST("MCReco/ptAntiDeuteronFromBminus"), track.pt()); + qaHistos.fill(HIST("MCReco/etaAntiDeuteronFromBminus"), track.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronFromBminus"), rapidity); + if (isSameMcCollision) { + qaHistos.fill(HIST("MCReco/hDCAxyVsPtDeFromBminusSameMC"), track.pt(), dca[0]); + qaHistos.fill(HIST("MCReco/hDCAzVsPtDeFromBminusSameMC"), track.pt(), dca[1]); + qaHistos.fill(HIST("MCReco/ptAntiDeuteronFromBminusSameMC"), track.pt()); + qaHistos.fill(HIST("MCReco/etaAntiDeuteronFromBminusSameMC"), track.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronFromBminusSameMC"), rapidity); + } + } else if (motherPdg == pdgCodeBeautyBaryon) { + qaHistos.fill(HIST("MCReco/hDCAxyVsPtDeFromAntiLambdaB"), track.pt(), dca[0]); + qaHistos.fill(HIST("MCReco/hDCAzVsPtDeFromAntiLambdaB"), track.pt(), dca[1]); + qaHistos.fill(HIST("MCReco/ptAntiDeuteronFromAntiLambdaB"), track.pt()); + qaHistos.fill(HIST("MCReco/etaAntiDeuteronFromAntiLambdaB"), track.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronFromAntiLambdaB"), rapidity); + if (isSameMcCollision) { + qaHistos.fill(HIST("MCReco/hDCAxyVsPtDeFromAntiLambdaBSameMC"), track.pt(), dca[0]); + qaHistos.fill(HIST("MCReco/hDCAzVsPtDeFromAntiLambdaBSameMC"), track.pt(), dca[1]); + qaHistos.fill(HIST("MCReco/ptAntiDeuteronFromAntiLambdaBSameMC"), track.pt()); + qaHistos.fill(HIST("MCReco/etaAntiDeuteronFromAntiLambdaBSameMC"), track.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronFromAntiLambdaBSameMC"), rapidity); + } + } else if (motherPdg == pdgCodeHypertriton) { + qaHistos.fill(HIST("MCReco/hDCAxyVsPtDeFromAntiHypertriton"), track.pt(), dca[0]); + qaHistos.fill(HIST("MCReco/hDCAzVsPtDeFromAntiHypertriton"), track.pt(), dca[1]); + qaHistos.fill(HIST("MCReco/ptAntiDeuteronFromAntiHypertriton"), track.pt()); + qaHistos.fill(HIST("MCReco/etaAntiDeuteronFromAntiHypertriton"), track.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronFromAntiHypertriton"), rapidity); + if (isSameMcCollision) { + qaHistos.fill(HIST("MCReco/hDCAxyVsPtDeFromAntiHypertritonSameMC"), track.pt(), dca[0]); + qaHistos.fill(HIST("MCReco/hDCAzVsPtDeFromAntiHypertritonSameMC"), track.pt(), dca[1]); + qaHistos.fill(HIST("MCReco/ptAntiDeuteronFromAntiHypertritonSameMC"), track.pt()); + qaHistos.fill(HIST("MCReco/etaAntiDeuteronFromAntiHypertritonSameMC"), track.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronFromAntiHypertritonSameMC"), rapidity); + } + } else if (mcParticle.isPhysicalPrimary()) { + qaHistos.fill(HIST("MCReco/hDCAxyVsPtDePrimary"), track.pt(), dca[0]); + qaHistos.fill(HIST("MCReco/hDCAzVsPtDePrimary"), track.pt(), dca[1]); + qaHistos.fill(HIST("MCReco/ptAntiDeuteronPrimary"), track.pt()); + qaHistos.fill(HIST("MCReco/etaAntiDeuteronPrimary"), track.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronPrimary"), rapidity); + if (isSameMcCollision) { + qaHistos.fill(HIST("MCReco/hDCAxyVsPtDePrimarySameMC"), track.pt(), dca[0]); + qaHistos.fill(HIST("MCReco/hDCAzVsPtDePrimarySameMC"), track.pt(), dca[1]); + qaHistos.fill(HIST("MCReco/ptAntiDeuteronPrimarySameMC"), track.pt()); + qaHistos.fill(HIST("MCReco/etaAntiDeuteronPrimarySameMC"), track.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronPrimarySameMC"), rapidity); + } + } } } } @@ -433,38 +698,39 @@ struct HfTaskDeuteronFromLb { void processGen(o2::aod::McCollision const& collision, o2::aod::McParticles const& mcParticles) { - hProcessedEvents->Fill(0.5); + qaHistos.fill(HIST("MCGen/hEvents"), 0.5); + if (std::abs(collision.posZ()) > cutzvertex) { + return; + } + qaHistos.fill(HIST("MCGen/hEvents"), 1.5); for (const auto& mcParticle : mcParticles) { - - // Beauty hadron mother - if (isSelectedBeautyHadron(mcParticle.pdgCode())) { - if (std::abs(mcParticle.y()) <= rapidityCut) { - if (mcParticle.pdgCode() == pdgCodeBeautyMeson) { - qaHistos.fill(HIST("MCGen/ptGeneratedBminus"), mcParticle.pt()); - } else if (mcParticle.pdgCode() == pdgCodeBeautyBaryon) { - qaHistos.fill(HIST("MCGen/ptGeneratedAntiLambdaB"), mcParticle.pt()); - } + if (isSelectedMother(mcParticle.pdgCode())) { + if (mcParticle.pdgCode() == pdgCodeBeautyMeson) { + qaHistos.fill(HIST("MCGen/ptGeneratedBminus"), mcParticle.pt()); + } else if (mcParticle.pdgCode() == pdgCodeBeautyBaryon) { + qaHistos.fill(HIST("MCGen/ptGeneratedAntiLambdaB"), mcParticle.pt()); + } else if (mcParticle.pdgCode() == pdgCodeHypertriton) { + qaHistos.fill(HIST("MCGen/ptGeneratedAntiHypertriton"), mcParticle.pt()); } - if (mcParticle.has_daughters()) { for (const auto& daughter : mcParticle.daughters_as()) { - double dx = daughter.vx() - collision.posX(); - double dy = daughter.vy() - collision.posY(); - double dz = daughter.vz() - collision.posZ(); - + double dx = daughter.vx() - mcParticle.vx(); + double dy = daughter.vy() - mcParticle.vy(); + double dz = daughter.vz() - mcParticle.vz(); double flightDistance = std::sqrt(dx * dx + dy * dy + dz * dz); - double massBeauty = getBeautyHadronMass(mcParticle.pdgCode()); + double massBeauty = getMotherMass(mcParticle.pdgCode()); if (massBeauty > 0.) { double ctauBeauty = flightDistance / mcParticle.p() * massBeauty; - ctauBeauty *= 1.e4; // cm -> micrometers if (mcParticle.pdgCode() == pdgCodeBeautyMeson) { - qaHistos.fill(HIST("MCGen/ctauBminus"), ctauBeauty); + qaHistos.fill(HIST("MCGen/ctauBminus"), ctauBeauty * 1.e4); } else if (mcParticle.pdgCode() == pdgCodeBeautyBaryon) { - qaHistos.fill(HIST("MCGen/ctauAntiLambdaB"), ctauBeauty); + qaHistos.fill(HIST("MCGen/ctauAntiLambdaB"), ctauBeauty * 1.e4); + } else if (mcParticle.pdgCode() == pdgCodeHypertriton) { + qaHistos.fill(HIST("MCGen/ctauAntiHypertriton"), ctauBeauty); // cm } } break; @@ -473,14 +739,21 @@ struct HfTaskDeuteronFromLb { } if (mcParticle.pdgCode() == pdgCodeDaughter) { - if (std::abs(mcParticle.eta()) < cfgEta) { - qaHistos.fill(HIST("MCGen/ptAntiDeuteronAll"), mcParticle.pt()); + + if (std::abs(mcParticle.eta()) > cfgEta) { + continue; } - if (std::abs(mcParticle.y()) > rapidityCut) { + if (applyRapidityCut && std::abs(mcParticle.y()) > rapidityCut) { continue; } + if (mcParticle.pt() < cfgMinPt || mcParticle.pt() > cfgMaxPt) { + continue; + } + + qaHistos.fill(HIST("MCGen/ptAntiDeuteronAll"), mcParticle.pt()); + int motherPdg = 0; if (mcParticle.has_mothers()) { for (const auto& mom : mcParticle.mothers_as()) { @@ -496,15 +769,30 @@ struct HfTaskDeuteronFromLb { motherPdg = mom.pdgCode(); break; } + + if (mom.pdgCode() == pdgCodeHypertriton) { + motherPdg = mom.pdgCode(); + break; + } } } if (motherPdg == pdgCodeBeautyMeson) { qaHistos.fill(HIST("MCGen/ptAntiDeuteronFromBminus"), mcParticle.pt()); + qaHistos.fill(HIST("MCGen/hPtEtaAntiDeuteronFromBminus"), mcParticle.pt(), mcParticle.eta()); + qaHistos.fill(HIST("MCGen/rapidityAntiDeuteronFromBminus"), mcParticle.y()); } else if (motherPdg == pdgCodeBeautyBaryon) { qaHistos.fill(HIST("MCGen/ptAntiDeuteronFromAntiLambdaB"), mcParticle.pt()); + qaHistos.fill(HIST("MCGen/hPtEtaAntiDeuteronFromAntiLambdaB"), mcParticle.pt(), mcParticle.eta()); + qaHistos.fill(HIST("MCGen/rapidityAntiDeuteronFromAntiLambdaB"), mcParticle.y()); + } else if (motherPdg == pdgCodeHypertriton) { + qaHistos.fill(HIST("MCGen/ptAntiDeuteronFromAntiHypertriton"), mcParticle.pt()); + qaHistos.fill(HIST("MCGen/hPtEtaAntiDeuteronFromAntiHypertriton"), mcParticle.pt(), mcParticle.eta()); + qaHistos.fill(HIST("MCGen/rapidityAntiDeuteronFromAntiHypertriton"), mcParticle.y()); } else if (mcParticle.isPhysicalPrimary()) { qaHistos.fill(HIST("MCGen/ptAntiDeuteronPrimary"), mcParticle.pt()); + qaHistos.fill(HIST("MCGen/hPtEtaAntiDeuteronPrimary"), mcParticle.pt(), mcParticle.eta()); + qaHistos.fill(HIST("MCGen/rapidityAntiDeuteronPrimary"), mcParticle.y()); } } } diff --git a/PWGHF/D2H/Tasks/taskDstarToD0Pi.cxx b/PWGHF/D2H/Tasks/taskDstarToD0Pi.cxx index cc0bcff73fb..03c39466c18 100644 --- a/PWGHF/D2H/Tasks/taskDstarToD0Pi.cxx +++ b/PWGHF/D2H/Tasks/taskDstarToD0Pi.cxx @@ -137,7 +137,7 @@ struct HfTaskDstarToD0Pi { if ((doprocessDataWoML && doprocessDataWML) || (doprocessMcWoMl && doprocessMcWML) || (doprocessDataWoML && doprocessMcWML) || (doprocessDataWML && doprocessMcWoMl)) { LOGP(fatal, "Only Without-ML or With-ML functions should be enabled at a time! Please check your configuration!"); } - auto vecPtBins = (std::vector)ptBins; + auto vecPtBins = std::vector(ptBins); AxisSpec const axisImpactParam = {binningImpactParam, "impact parameter (cm)"}; AxisSpec const axisDecayLength = {binningDecayLength, " decay length (cm)"}; diff --git a/PWGHF/D2H/Tasks/taskFlowCharmHadrons.cxx b/PWGHF/D2H/Tasks/taskFlowCharmHadrons.cxx index 707fdf48373..c7b099f64ea 100644 --- a/PWGHF/D2H/Tasks/taskFlowCharmHadrons.cxx +++ b/PWGHF/D2H/Tasks/taskFlowCharmHadrons.cxx @@ -120,7 +120,8 @@ enum DecayChannel { DplusToPiKPi = 0, LcToPiKP, XicToPKPi, XicToPiKP, - Xic0ToXiPi + Xic0ToXiPi, + DstarToD0Pi }; enum RunMode { @@ -182,14 +183,16 @@ struct HfTaskFlowCharmHadrons { using CandXic0Data = soa::Filtered>; using CandXic0DataWMl = soa::Filtered>; using CandD0DataWMl = soa::Filtered>; - using CollsWithSPQvecs = soa::Join; - using CollsWithEsEQvecs = soa::Join; - using CollsWithSPEsEQvecs = soa::Join; + using CandDstarDataWMl = soa::Filtered>; + using CollsWithSPQvecs = soa::Join; + using CollsWithEsEQvecs = soa::Join; + using CollsWithSPEsEQvecs = soa::Join; using TracksWithExtra = soa::Join; Filter filterSelectDsCandidates = aod::hf_sel_candidate_ds::isSelDsToKKPi >= selectionFlag || aod::hf_sel_candidate_ds::isSelDsToPiKK >= selectionFlag; Filter filterSelectDplusCandidates = aod::hf_sel_candidate_dplus::isSelDplusToPiKPi >= selectionFlag; Filter filterSelectD0Candidates = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlag || aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlag; + Filter filterSelectDstarCandidates = aod::hf_sel_candidate_dstar::isSelDstarToD0Pi == true; Filter filterSelectLcCandidates = aod::hf_sel_candidate_lc::isSelLcToPKPi >= selectionFlag || aod::hf_sel_candidate_lc::isSelLcToPiKP >= selectionFlag; Filter filterSelectXicCandidates = aod::hf_sel_candidate_xic::isSelXicToPKPi >= selectionFlag || aod::hf_sel_candidate_xic::isSelXicToPiKP >= selectionFlag; Filter filterSelectXic0Candidates = aod::hf_sel_toxipi::resultSelections == true; @@ -198,6 +201,7 @@ struct HfTaskFlowCharmHadrons { Partition selectedDsToPiKKWMl = aod::hf_sel_candidate_ds::isSelDsToPiKK >= selectionFlag; Partition selectedD0ToPiKWMl = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlag; Partition selectedD0ToKPiWMl = aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlag; + Partition selectedDstarToD0PiWMl = aod::hf_sel_candidate_dstar::isSelDstarToD0Pi == true; Partition selectedLcToPKPiWMl = aod::hf_sel_candidate_lc::isSelLcToPKPi >= selectionFlag; Partition selectedLcToPiKPWMl = aod::hf_sel_candidate_lc::isSelLcToPiKP >= selectionFlag; Partition selectedXicToPKPiWMl = aod::hf_sel_candidate_xic::isSelXicToPKPi >= selectionFlag; @@ -418,8 +422,10 @@ struct HfTaskFlowCharmHadrons { addProngIfInSubevent(cand.pxProng0(), cand.pyProng0(), cand.pzProng0()); addProngIfInSubevent(cand.pxProng1(), cand.pyProng1(), cand.pzProng1()); - // 3-prong channels - if constexpr (Channel != DecayChannel::D0ToPiK && Channel != DecayChannel::D0ToKPi) { + if constexpr (Channel == DecayChannel::DstarToD0Pi) { + addProngIfInSubevent(cand.pxSoftPi(), cand.pySoftPi(), cand.pzSoftPi()); + } else if constexpr (Channel != DecayChannel::D0ToPiK && Channel != DecayChannel::D0ToKPi) { + // Other 3-prong channels addProngIfInSubevent(cand.pxProng2(), cand.pyProng2(), cand.pzProng2()); } } @@ -621,11 +627,13 @@ struct HfTaskFlowCharmHadrons { float const amplRedQVec = qVecRedComps[2]; if (useOnlineResoCorrection) { + // Access BC information through the collision's BC index. + const auto bc = collision.template bc_as(); // Per-run update - if (collision.runNumber() != lastRunNumber) { - LOG(info) << "Run number changed from " << lastRunNumber << " to " << collision.runNumber() << ". Updating resolution values."; - qVecResoHelper.update(ccdb, ccdbResoHistPath, resoTripletPath.value, collision.timestamp(), collision.runNumber()); - lastRunNumber = collision.runNumber(); + if (bc.runNumber() != lastRunNumber) { + LOG(info) << "Run number changed from " << lastRunNumber << " to " << bc.runNumber() << ". Updating resolution values."; + qVecResoHelper.update(ccdb, ccdbResoHistPath, resoTripletPath.value, bc.timestamp(), bc.runNumber()); + lastRunNumber = bc.runNumber(); } scalarProdReso = qVecResoHelper[static_cast(cent - 0.5f)]; // Cent is given in X.5f format } @@ -688,6 +696,13 @@ struct HfTaskFlowCharmHadrons { default: break; } + } else if constexpr (std::is_same_v) { + // Use the soft-pion charge to select the D0 or D0bar mass hypothesis. + signCand = candidate.signSoftPi(); + massCand = signCand > 0 ? candidate.invMassDstar() - candidate.invMassD0() : candidate.invMassAntiDstar() - candidate.invMassD0Bar(); + for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { + outputMl[iclass] = candidate.mlProbDstarToD0Pi()[classMl->at(iclass)]; + } } else if constexpr (std::is_same_v) { switch (Channel) { case DecayChannel::LcToPKPi: @@ -840,7 +855,8 @@ struct HfTaskFlowCharmHadrons { // Ds with ML void processDsMl(CollsWithSPQvecs::iterator const& collision, CandDsDataWMl const&, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { auto candsDsToKKPiWMl = selectedDsToKKPiWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); auto candsDsToPiKKWMl = selectedDsToPiKKWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); @@ -852,7 +868,8 @@ struct HfTaskFlowCharmHadrons { // Ds with ML EsE void processDsEsEMl(CollsWithEsEQvecs::iterator const& collision, CandDsDataWMl const& /*candidatesDs*/, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { auto candsDsToKKPiWMl = selectedDsToKKPiWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); auto candsDsToPiKKWMl = selectedDsToPiKKWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); @@ -864,7 +881,8 @@ struct HfTaskFlowCharmHadrons { // Ds with ML EsE and SP void processDsSPEsEMl(CollsWithSPEsEQvecs::iterator const& collision, CandDsDataWMl const& /*candidatesDs*/, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { auto candsDsToKKPiWMl = selectedDsToKKPiWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); auto candsDsToPiKKWMl = selectedDsToPiKKWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); @@ -876,7 +894,8 @@ struct HfTaskFlowCharmHadrons { // Dplus with ML void processDplusMl(CollsWithSPQvecs::iterator const& collision, CandDplusDataWMl const& candidatesDplus, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { runFlowAnalysis(collision, candidatesDplus, tracks); } @@ -885,7 +904,8 @@ struct HfTaskFlowCharmHadrons { // Dplus with ML EsE void processDplusEsEMl(CollsWithEsEQvecs::iterator const& collision, CandDplusDataWMl const& candidatesDplus, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { runFlowAnalysis(collision, candidatesDplus, tracks); } @@ -894,7 +914,8 @@ struct HfTaskFlowCharmHadrons { // Dplus with ML SP and EsE void processDplusSPEsEMl(CollsWithSPEsEQvecs::iterator const& collision, CandDplusDataWMl const& candidatesDplus, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { runFlowAnalysis(collision, candidatesDplus, tracks); } @@ -903,7 +924,8 @@ struct HfTaskFlowCharmHadrons { // D0 with ML void processD0Ml(CollsWithSPQvecs::iterator const& collision, CandD0DataWMl const& /*candidatesD0*/, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { auto candsD0ToPiKWMl = selectedD0ToPiKWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); auto candsD0ToKPiWMl = selectedD0ToKPiWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); @@ -915,7 +937,8 @@ struct HfTaskFlowCharmHadrons { // D0 with ML void processD0EsEMl(CollsWithEsEQvecs::iterator const& collision, CandD0DataWMl const& /*candidatesD0*/, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { auto candsD0ToPiKWMl = selectedD0ToPiKWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); auto candsD0ToKPiWMl = selectedD0ToKPiWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); @@ -927,7 +950,8 @@ struct HfTaskFlowCharmHadrons { // D0 with ML void processD0SPEsEMl(CollsWithSPEsEQvecs::iterator const& collision, CandD0DataWMl const& /*candidatesD0*/, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { auto candsD0ToPiKWMl = selectedD0ToPiKWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); auto candsD0ToKPiWMl = selectedD0ToKPiWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); @@ -936,10 +960,21 @@ struct HfTaskFlowCharmHadrons { } PROCESS_SWITCH(HfTaskFlowCharmHadrons, processD0SPEsEMl, "Process D0 candidates with ML for SP and EsE information", false); + // Dstar with ML + void processDstarMl(CollsWithSPQvecs::iterator const& collision, + CandDstarDataWMl const& candidatesDstar, + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) + { + runFlowAnalysis(collision, candidatesDstar, tracks); + } + PROCESS_SWITCH(HfTaskFlowCharmHadrons, processDstarMl, "Process Dstar candidates with ML", false); + // Lc with ML void processLcMl(CollsWithSPQvecs::iterator const& collision, CandLcDataWMl const& /*candidatesLc*/, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { auto candsLcToPKPiWMl = selectedLcToPKPiWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); auto candsLcToPiKPWMl = selectedLcToPiKPWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); @@ -951,7 +986,8 @@ struct HfTaskFlowCharmHadrons { // Xic with ML void processXicMl(CollsWithSPQvecs::iterator const& collision, CandXicDataWMl const& /*candidatesXic*/, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { auto candsXicToPKPiWMl = selectedXicToPKPiWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); auto candsXicToPiKPWMl = selectedXicToPiKPWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); @@ -963,7 +999,8 @@ struct HfTaskFlowCharmHadrons { // Xic0 with ML void processXic0Ml(CollsWithSPQvecs::iterator const& collision, CandXic0DataWMl const& /*candidatesXic0*/, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { auto candsXic0WMl = selectedXic0WMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); runFlowAnalysis(collision, candsXic0WMl, tracks); @@ -973,7 +1010,8 @@ struct HfTaskFlowCharmHadrons { // Xic0 void processXic0(CollsWithSPQvecs::iterator const& collision, CandXic0Data const& /*candidatesXic0*/, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { auto candsXic0 = selectedXic0->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); runFlowAnalysis(collision, candsXic0, tracks); diff --git a/PWGHF/D2H/Tasks/taskLb.cxx b/PWGHF/D2H/Tasks/taskLb.cxx index 9a605393658..6ad885313c6 100644 --- a/PWGHF/D2H/Tasks/taskLb.cxx +++ b/PWGHF/D2H/Tasks/taskLb.cxx @@ -121,65 +121,65 @@ struct HfTaskLb { void init(InitContext&) { - registry.add("hMass", "#Lambda_{b}^{0} candidates;inv. mass #Lambda_{c}^{#plus}#pi^{#minus} (GeV/#it{c}^{2});#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{500, 0., 10.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hDecLength", "#Lambda_{b}^{0} candidates;decay length (cm);entries", {HistType::kTH2F, {{200, 0., 0.4}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hDecLengthXY", "#Lambda_{b}^{0} candidates;decay length xy (cm);entries", {HistType::kTH2F, {{200, 0., 0.4}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hd0Prong0", "#Lambda_{b}^{0} candidates;prong 0 (#Lambda_{c}^{#plus}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{100, -0.05, 0.05}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hd0Prong1", "#Lambda_{b}^{0} candidates;prong 1 (#pi^{#minus}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{100, -0.05, 0.05}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hCPA", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate cosine of pointing angle;entries", {HistType::kTH2F, {{110, -1.1, 1.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hEta", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate #it{#eta};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hRapidity", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate #it{y};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hImpParErr", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate impact parameter error (cm);entries", {HistType::kTH2F, {{100, -1., 1.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hDecLenErr", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate decay length error (cm);entries", {HistType::kTH2F, {{100, 0., 1.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hDecLenXYErr", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate decay length xy error (cm);entries", {HistType::kTH2F, {{100, 0., 1.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hIPProd", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate impact parameter product;entries", {HistType::kTH2F, {{100, -0.5, 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hInvMassLc", "#Lambda_{b}^{0} candidates;prong0, #Lambda_{c}^{+} inv. mass (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{500, 0, 5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hMass", "#Lambda_{b}^{0} candidates;inv. mass #Lambda_{c}^{#plus}#pi^{#minus} (GeV/#it{c}^{2});#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{500, 0., 10.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hDecLength", "#Lambda_{b}^{0} candidates;decay length (cm);entries", {HistType::kTH2F, {{200, 0., 0.4}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hDecLengthXY", "#Lambda_{b}^{0} candidates;decay length xy (cm);entries", {HistType::kTH2F, {{200, 0., 0.4}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hd0Prong0", "#Lambda_{b}^{0} candidates;prong 0 (#Lambda_{c}^{#plus}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{100, -0.05, 0.05}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hd0Prong1", "#Lambda_{b}^{0} candidates;prong 1 (#pi^{#minus}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{100, -0.05, 0.05}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hCPA", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate cosine of pointing angle;entries", {HistType::kTH2F, {{110, -1.1, 1.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hEta", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate #it{#eta};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hRapidity", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate #it{y};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hImpParErr", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate impact parameter error (cm);entries", {HistType::kTH2F, {{100, -1., 1.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hDecLenErr", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate decay length error (cm);entries", {HistType::kTH2F, {{100, 0., 1.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hDecLenXYErr", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate decay length xy error (cm);entries", {HistType::kTH2F, {{100, 0., 1.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hIPProd", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate impact parameter product;entries", {HistType::kTH2F, {{100, -0.5, 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hInvMassLc", "#Lambda_{b}^{0} candidates;prong0, #Lambda_{c}^{+} inv. mass (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{500, 0, 5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); // Now add MC histograms - registry.add("MC/hEtaGen", "MC particles (matched);#Lambda_{b}^{0} candidate #it{#eta}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hYGen", "MC particles (matched);#Lambda_{b}^{0} candidate #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hPtProng0Gen", "MC particles (matched);prong 0 (#Lambda_{c}^{+}) #it{p}_{T}^{gen} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hPtProng1Gen", "MC particles (matched);prong 1 (#pi^{-}) #it{p}_{T}^{gen} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hYProng0Gen", "MC particles (matched);prong 0 (#Lambda_{c}^{+}) #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2, 2}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hYProng1Gen", "MC particles (matched);prong 1 (#pi^{-}) #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2, 2}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hEtaProng0Gen", "MC particles (matched);prong 0 (#Lambda_{b}^{0}) #it{#eta}^{gen};entries", {HistType::kTH2F, {{100, -2, 2}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hEtaProng1Gen", "MC particles (matched);prong 1 (#pi^{-}) #it{#eta}^{gen};entries", {HistType::kTH2F, {{100, -2, 2}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hCPARecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate cosine of pointing angle;entries", {HistType::kTH2F, {{220, 0., 1.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hCPARecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate cosine of pointing angle;entries", {HistType::kTH2F, {{220, 0., 1.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hCPAxyRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate CPAxy;entries", {HistType::kTH2F, {{220, 0., 1.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hCPAxyRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate CPAxy;entries", {HistType::kTH2F, {{220, 0., 1.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hCPALcRecSig", "#Lambda_{b}^{0} candidates (matched);prong 0 (#Lambda_{c}^{+}) cosine of pointing angle;entries", {HistType::kTH2F, {{220, 0., 1.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hCPALcRecBg", "#Lambda_{b}^{0} candidates (unmatched);prong 0 (#Lambda_{c}^{+}) cosine of pointing angle;entries", {HistType::kTH2F, {{220, 0., 1.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hEtaRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate #it{#eta};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hEtaRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate #it{#eta};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hRapidityRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate #it{y};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hRapidityRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate #it{#y};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - - registry.add("MC/hPtProng0RecSig", "#Lambda_{b}^{0} candidates (matched);prong 0 (#Lambda_{c}^{+}) #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hPtProng1RecSig", "#Lambda_{b}^{0} candidates (matched);prong 1 (#pi^{#minus}) #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hPtProng0RecBg", "#Lambda_{b}^{0} candidates (unmatched);prong 0 (#Lambda_{c}^{+}) #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hPtProng1RecBg", "#Lambda_{b}^{0} candidates (unmatched);prong 1 (#pi^{#minus}) #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hMassRecSig", "#Lambda_{b}^{0} candidates (matched);inv. mass #Lambda_{c}^{+}#pi^{+} (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{300, 4.0, 7.00}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hMassRecBg", "#Lambda_{b}^{0} candidates (unmatched);inv. mass #Lambda_{c}^{+}#pi^{+} (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{300, 4.0, 7.0}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hd0Prong0RecSig", "#Lambda_{b}^{0} candidates (matched);prong 0 (#Lambda_{c}^{+}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{200, -0.05, 0.05}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hd0Prong1RecSig", "#Lambda_{b}^{0} candidates (matched);prong 1 (#pi^{#minus}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{200, -0.05, 0.05}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hd0Prong0RecBg", "#Lambda_{b}^{0} candidates (unmatched);prong 0 (#Lambda_{c}^{+}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{200, -0.05, 0.05}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hd0Prong1RecBg", "#Lambda_{b}^{0} candidates (unmatched);prong 1 (#pi^{#minus}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{200, -0.05, 0.05}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hDecLengthRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hDecLengthXYRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate decay length xy (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hDecLengthRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hDecLengthXYRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate decay length xy(cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hDecLengthLcRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hDecLengthLcRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hDecLengthNormRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hDecLengthNormRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hImpParProdLbRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate impact parameter product ;entries", {HistType::kTH2F, {{100, -0.5, 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hImpParProdLbRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate impact parameter product ;entries", {HistType::kTH2F, {{100, -0.5, 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - - registry.add("MC/hChi2PCARecSig", "#Lambda_{b}^{0} candidates (matched);sum of distances of the secondary vertex to its prongs;entries", {HistType::kTH2F, {{240, -0.01, 0.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hChi2PCARecBg", "#Lambda_{b}^{0} candidates (unmatched);sum of distances of the secondary vertex to its prongs;entries", {HistType::kTH2F, {{240, -0.01, 0.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hThetaStarRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} #cos(#theta^{*});entries", {HistType::kTH2F, {{110, -1.1, 1.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hThetaStarRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} #cos(#theta^{*});entries", {HistType::kTH2F, {{110, -1.1, 1.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hEtaGen", "MC particles (matched);#Lambda_{b}^{0} candidate #it{#eta}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hYGen", "MC particles (matched);#Lambda_{b}^{0} candidate #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hPtProng0Gen", "MC particles (matched);prong 0 (#Lambda_{c}^{+}) #it{p}_{T}^{gen} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hPtProng1Gen", "MC particles (matched);prong 1 (#pi^{-}) #it{p}_{T}^{gen} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hYProng0Gen", "MC particles (matched);prong 0 (#Lambda_{c}^{+}) #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2, 2}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hYProng1Gen", "MC particles (matched);prong 1 (#pi^{-}) #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2, 2}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hEtaProng0Gen", "MC particles (matched);prong 0 (#Lambda_{b}^{0}) #it{#eta}^{gen};entries", {HistType::kTH2F, {{100, -2, 2}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hEtaProng1Gen", "MC particles (matched);prong 1 (#pi^{-}) #it{#eta}^{gen};entries", {HistType::kTH2F, {{100, -2, 2}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hCPARecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate cosine of pointing angle;entries", {HistType::kTH2F, {{220, 0., 1.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hCPARecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate cosine of pointing angle;entries", {HistType::kTH2F, {{220, 0., 1.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hCPAxyRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate CPAxy;entries", {HistType::kTH2F, {{220, 0., 1.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hCPAxyRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate CPAxy;entries", {HistType::kTH2F, {{220, 0., 1.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hCPALcRecSig", "#Lambda_{b}^{0} candidates (matched);prong 0 (#Lambda_{c}^{+}) cosine of pointing angle;entries", {HistType::kTH2F, {{220, 0., 1.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hCPALcRecBg", "#Lambda_{b}^{0} candidates (unmatched);prong 0 (#Lambda_{c}^{+}) cosine of pointing angle;entries", {HistType::kTH2F, {{220, 0., 1.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hEtaRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate #it{#eta};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hEtaRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate #it{#eta};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hRapidityRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate #it{y};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hRapidityRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate #it{#y};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + + registry.add("MC/hPtProng0RecSig", "#Lambda_{b}^{0} candidates (matched);prong 0 (#Lambda_{c}^{+}) #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hPtProng1RecSig", "#Lambda_{b}^{0} candidates (matched);prong 1 (#pi^{#minus}) #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hPtProng0RecBg", "#Lambda_{b}^{0} candidates (unmatched);prong 0 (#Lambda_{c}^{+}) #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hPtProng1RecBg", "#Lambda_{b}^{0} candidates (unmatched);prong 1 (#pi^{#minus}) #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hMassRecSig", "#Lambda_{b}^{0} candidates (matched);inv. mass #Lambda_{c}^{+}#pi^{+} (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{300, 4.0, 7.00}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hMassRecBg", "#Lambda_{b}^{0} candidates (unmatched);inv. mass #Lambda_{c}^{+}#pi^{+} (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{300, 4.0, 7.0}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hd0Prong0RecSig", "#Lambda_{b}^{0} candidates (matched);prong 0 (#Lambda_{c}^{+}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{200, -0.05, 0.05}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hd0Prong1RecSig", "#Lambda_{b}^{0} candidates (matched);prong 1 (#pi^{#minus}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{200, -0.05, 0.05}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hd0Prong0RecBg", "#Lambda_{b}^{0} candidates (unmatched);prong 0 (#Lambda_{c}^{+}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{200, -0.05, 0.05}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hd0Prong1RecBg", "#Lambda_{b}^{0} candidates (unmatched);prong 1 (#pi^{#minus}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{200, -0.05, 0.05}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hDecLengthRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hDecLengthXYRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate decay length xy (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hDecLengthRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hDecLengthXYRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate decay length xy(cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hDecLengthLcRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hDecLengthLcRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hDecLengthNormRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hDecLengthNormRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hImpParProdLbRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate impact parameter product ;entries", {HistType::kTH2F, {{100, -0.5, 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hImpParProdLbRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate impact parameter product ;entries", {HistType::kTH2F, {{100, -0.5, 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + + registry.add("MC/hChi2PCARecSig", "#Lambda_{b}^{0} candidates (matched);sum of distances of the secondary vertex to its prongs;entries", {HistType::kTH2F, {{240, -0.01, 0.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hChi2PCARecBg", "#Lambda_{b}^{0} candidates (unmatched);sum of distances of the secondary vertex to its prongs;entries", {HistType::kTH2F, {{240, -0.01, 0.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hThetaStarRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} #cos(#theta^{*});entries", {HistType::kTH2F, {{110, -1.1, 1.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hThetaStarRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} #cos(#theta^{*});entries", {HistType::kTH2F, {{110, -1.1, 1.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); } void processData(aod::Collisions::iterator const& collision, diff --git a/PWGHF/D2H/Tasks/taskLbReduced.cxx b/PWGHF/D2H/Tasks/taskLbReduced.cxx index fe43e8d2c6e..da430368482 100644 --- a/PWGHF/D2H/Tasks/taskLbReduced.cxx +++ b/PWGHF/D2H/Tasks/taskLbReduced.cxx @@ -73,6 +73,9 @@ DECLARE_SOA_COLUMN(E, e, float); DECLARE_SOA_COLUMN(NSigTpcPiBachelor, nSigTpcPiBachelor, float); //! TPC Nsigma separation for bachelor with pion mass hypothesis DECLARE_SOA_COLUMN(NSigTofPiBachelor, nSigTofPiBachelor, float); //! TOF Nsigma separation for bachelor with pion mass hypothesis DECLARE_SOA_COLUMN(NSigTpcTofPiBachelor, nSigTpcTofPiBachelor, float); //! Combined TPC and TOF Nsigma separation for bachelor with pion mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcPiLcProng0, nSigTpcPiLcProng0, float); //! TPC Nsigma separation for Lc-baryon prong0 with pion mass hypothesis +DECLARE_SOA_COLUMN(NSigTofPiLcProng0, nSigTofPiLcProng0, float); //! TOF Nsigma separation for Lc-baryon prong0 with pion mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcTofPiLcProng0, nSigTpcTofPiLcProng0, float); //! Combined TPC and TOF Nsigma separation for Lc-baryon prong0 with pion mass hypothesis DECLARE_SOA_COLUMN(NSigTpcPrLcProng0, nSigTpcPrLcProng0, float); //! TPC Nsigma separation for Lc-baryon prong0 with proton mass hypothesis DECLARE_SOA_COLUMN(NSigTofPrLcProng0, nSigTofPrLcProng0, float); //! TOF Nsigma separation for Lc-baryon prong0 with proton mass hypothesis DECLARE_SOA_COLUMN(NSigTpcTofPrLcProng0, nSigTpcTofPrLcProng0, float); //! Combined TPC and TOF Nsigma separation for Lc-baryon prong0 with proton mass hypothesis @@ -82,6 +85,9 @@ DECLARE_SOA_COLUMN(NSigTpcTofKaLcProng1, nSigTpcTofKaLcProng1, float); DECLARE_SOA_COLUMN(NSigTpcPiLcProng2, nSigTpcPiLcProng2, float); //! TPC Nsigma separation for Lc-baryon prong2 with pion mass hypothesis DECLARE_SOA_COLUMN(NSigTofPiLcProng2, nSigTofPiLcProng2, float); //! TOF Nsigma separation for Lc-baryon prong2 with pion mass hypothesis DECLARE_SOA_COLUMN(NSigTpcTofPiLcProng2, nSigTpcTofPiLcProng2, float); //! Combined TPC and TOF Nsigma separation for Lc-baryon prong0 with pion mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcPrLcProng2, nSigTpcPrLcProng2, float); //! TPC Nsigma separation for Lc-baryon prong2 with proton mass hypothesis +DECLARE_SOA_COLUMN(NSigTofPrLcProng2, nSigTofPrLcProng2, float); //! TOF Nsigma separation for Lc-baryon prong2 with proton mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcTofPrLcProng2, nSigTpcTofPrLcProng2, float); //! Combined TPC and TOF Nsigma separation for Lc-baryon prong2 with proton mass hypothesis DECLARE_SOA_COLUMN(DecayLength, decayLength, float); //! Decay length of candidate (cm) DECLARE_SOA_COLUMN(DecayLengthXY, decayLengthXY, float); //! Transverse decay length of candidate (cm) DECLARE_SOA_COLUMN(DecayLengthNormalised, decayLengthNormalised, float); //! Normalised decay length of candidate @@ -127,6 +133,9 @@ DECLARE_SOA_TABLE(HfRedCandLbLites, "AOD", "HFREDCANDLBLITE", //! Table with som hf_cand_lb_lite::ItsNClsLcProngMin, hf_cand_lb_lite::TpcNClsCrossedRowsLcProngMin, hf_cand_lb_lite::TpcChi2NClLcProngMax, + hf_cand_lb_lite::NSigTpcPiLcProng0, + hf_cand_lb_lite::NSigTofPiLcProng0, + hf_cand_lb_lite::NSigTpcTofPiLcProng0, hf_cand_lb_lite::NSigTpcPrLcProng0, hf_cand_lb_lite::NSigTofPrLcProng0, hf_cand_lb_lite::NSigTpcTofPrLcProng0, @@ -136,6 +145,9 @@ DECLARE_SOA_TABLE(HfRedCandLbLites, "AOD", "HFREDCANDLBLITE", //! Table with som hf_cand_lb_lite::NSigTpcPiLcProng2, hf_cand_lb_lite::NSigTofPiLcProng2, hf_cand_lb_lite::NSigTpcTofPiLcProng2, + hf_cand_lb_lite::NSigTpcPrLcProng2, + hf_cand_lb_lite::NSigTofPrLcProng2, + hf_cand_lb_lite::NSigTpcTofPrLcProng2, hf_cand_lb_reduced::Prong0MlScoreBkg, hf_cand_lb_reduced::Prong0MlScorePrompt, hf_cand_lb_reduced::Prong0MlScoreNonprompt, @@ -587,6 +599,9 @@ struct HfTaskLbReduced { candLc.itsNClsProngMin(), candLc.tpcNClsCrossedRowsProngMin(), candLc.tpcChi2NClProngMax(), + candLc.tpcNSigmaPiProng0(), + candLc.tofNSigmaPiProng0(), + candLc.tpcTofNSigmaPiProng0(), candLc.tpcNSigmaPrProng0(), candLc.tofNSigmaPrProng0(), candLc.tpcTofNSigmaPrProng0(), @@ -596,6 +611,9 @@ struct HfTaskLbReduced { candLc.tpcNSigmaPiProng2(), candLc.tofNSigmaPiProng2(), candLc.tpcTofNSigmaPiProng2(), + candLc.tpcNSigmaPrProng2(), + candLc.tofNSigmaPrProng2(), + candLc.tpcTofNSigmaPrProng2(), prong0MlScoreBkg, prong0MlScorePrompt, prong0MlScoreNonprompt, diff --git a/PWGHF/D2H/Tasks/taskLc.cxx b/PWGHF/D2H/Tasks/taskLc.cxx index c74ef2485ba..68bc5266735 100644 --- a/PWGHF/D2H/Tasks/taskLc.cxx +++ b/PWGHF/D2H/Tasks/taskLc.cxx @@ -204,7 +204,7 @@ struct HfTaskLc { addHistogramsRec("hPhi", "#it{#Phi}", "entries", {HistType::kTH1F, {{100, 0., 6.3}}}); addHistogramsGen("hPhi", "#it{#Phi}", "entries", {HistType::kTH1F, {{100, 0., 6.3}}}); - auto vbins = (std::vector)binsPt; + auto vbins = std::vector(binsPt); /// mass candidate if (isData) { registry.add("Data/hMassVsPtVsNPvContributors", "3-prong candidates;inv. mass (p K #pi) (GeV/#it{c}^{2}); p_{T}; Number of PV contributors", {HistType::kTH3F, {{600, 1.98, 2.58}, {vbins, "#it{p}_{T} (GeV/#it{c})"}, {5000, 0., 10000.}}}); diff --git a/PWGHF/D2H/Tasks/taskOmegac0ToOmegaPiQa.cxx b/PWGHF/D2H/Tasks/taskOmegac0ToOmegaPiQa.cxx new file mode 100644 index 00000000000..90ebbb0d312 --- /dev/null +++ b/PWGHF/D2H/Tasks/taskOmegac0ToOmegaPiQa.cxx @@ -0,0 +1,557 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file taskOmegac0ToOmegaPiQa.cxx +/// \brief OmegaC0 analysis task +/// \author Yunfan Liu , China University of Geosciences +/// \author Fabio Catalano , University of Houston +/// \author Maria Fernanda Torres Cabrera , University of Houston + +#include "PWGHF/Core/CentralityEstimation.h" +#include "PWGHF/Core/DecayChannelsLegacy.h" +#include "PWGHF/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/DataModel/CandidateSelectionTables.h" +#include "PWGHF/DataModel/TrackIndexSkimmingTables.h" +#include "PWGLF/DataModel/mcCentrality.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +/// Omegac0 analysis task +struct HfTaskOmegac0ToOmegaPiQa { + + Configurable selectionFlagOmegac0{"selectionFlagOmegac0", true, "Select Omegac0 candidates"}; + Configurable yCandGenMax{"yCandGenMax", 0.5, "Max. gen particle rapidity"}; + Configurable yCandRecoMax{"yCandRecoMax", 0.8, "Max. cand. rapidity"}; + + SliceCache cache; + + using Omegac0Cands = soa::Filtered>; + using Omegac0CandsKF = soa::Filtered>; + using Omegac0CandsMc = soa::Filtered>; + using Omegac0CandsMcKF = soa::Filtered>; + using Omegac0CandsMl = soa::Filtered>; + using Omegac0CandsMlKF = soa::Filtered>; + using Omegac0CandsMlMc = soa::Filtered>; + using Omegac0CandsMlMcKF = soa::Filtered>; + + using Omegac0Gen = soa::Filtered>; + + using Collisions = soa::Join; + using CollisionsWithFT0C = soa::Join; + using CollisionsWithFT0M = soa::Join; + using CollisionsWithMcLabels = soa::Join; + + using McCollisionsWithFT0M = soa::Join; + + Filter filterOmegaCToOmegaPiFlag = (aod::hf_track_index::hfflag & static_cast(BIT(aod::hf_cand_casc_lf::DecayType2Prong::OmegaczeroToOmegaPi))) != static_cast(0); + Filter filterOmegaCMatchedRec = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(BIT(aod::hf_cand_xic0_omegac0::DecayType::OmegaczeroToOmegaPi)); + Filter filterOmegaCMatchedGen = nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(BIT(aod::hf_cand_xic0_omegac0::DecayType::OmegaczeroToOmegaPi)); + + Preslice candOmegacPerCollision = aod::hf_cand_xic0_omegac0::collisionId; + Preslice candOmegacKFPerCollision = aod::hf_cand_xic0_omegac0::collisionId; + Preslice candOmegacMlPerCollision = aod::hf_cand_xic0_omegac0::collisionId; + Preslice candOmegacKFMlPerCollision = aod::hf_cand_xic0_omegac0::collisionId; + + PresliceUnsorted colPerMcCollision = aod::mccollisionlabel::mcCollisionId; + + ConfigurableAxis thnConfigAxisMass{"thnConfigAxisMass", {700, 2.4, 3.1}, "Cand. inv. mass"}; + ConfigurableAxis thnConfigAxisPt{"thnConfigAxisPt", {500, 0, 50}, "Cand. pT"}; + ConfigurableAxis thnConfigAxisPtB{"thnConfigAxisPtB", {500, 0, 50}, "Cand. beauty mother pT"}; + ConfigurableAxis thnConfigAxisY{"thnConfigAxisY", {20, -1, 1}, "Cand. rapidity"}; + ConfigurableAxis thnConfigAxisOrigin{"thnConfigAxisOrigin", {3, -0.5, 2.5}, "Cand. origin"}; + ConfigurableAxis thnConfigAxisMatchFlag{"thnConfigAxisMatchFlag", {15, -7.5, 7.5}, "Cand. MC match flag"}; + ConfigurableAxis thnConfigAxisNumPvContr{"thnConfigAxisNumPvContr", {200, -0.5, 199.5}, "Coll. num. PV contributors"}; + ConfigurableAxis thnConfigAxisCent{"thnConfigAxisCent", {100, 0, 100}, "Coll. centrality precentile"}; + ConfigurableAxis thnConfigAxisPromptScore{"thnConfigAxisPromptScore", {100, 0, 1}, "Prompt score"}; + HistogramRegistry registry{"registry", {}}; + + void init(InitContext&) + { + std::array doprocess{doprocessDataDCAFitter, doprocessDataDCAFitterMl, doprocessDataDCAFitterFT0C, doprocessDataDCAFitterMlFT0C, + doprocessDataDCAFitterFT0M, doprocessDataDCAFitterMlFT0M, + doprocessDataKFParticle, doprocessDataKFParticleMl, doprocessDataKFParticleFT0C, doprocessDataKFParticleMlFT0C, + doprocessDataKFParticleFT0M, doprocessDataKFParticleMlFT0M, + doprocessMcDCAFitter, doprocessMcDCAFitterMl, + doprocessMcDCAFitterFT0M, doprocessMcDCAFitterMlFT0M, + doprocessMcKFParticle, doprocessMcKFParticleMl, + doprocessMcKFParticleFT0M, doprocessMcKFParticleMlFT0M}; + if ((std::accumulate(doprocess.begin(), doprocess.end(), 0)) != 1) { + LOGP(fatal, "One and only one process function should be enabled at a time."); + } + + const AxisSpec thnAxisMass{thnConfigAxisMass, "Inv. mass (#Omega#pi) (GeV/#it{c}^{2})"}; + const AxisSpec thnAxisPt{thnConfigAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec thnAxisPtB{thnConfigAxisPtB, "#it{p}_{T}^{B} (GeV/#it{c})"}; + const AxisSpec thnAxisY{thnConfigAxisY, "y"}; + const AxisSpec thnAxisOrigin{thnConfigAxisOrigin, "Origin"}; + const AxisSpec thnAxisMatchFlag{thnConfigAxisMatchFlag, "MC match flag"}; + const AxisSpec thnAxisNumPvContr{thnConfigAxisNumPvContr, "Number of primary vtx. contributors"}; + const AxisSpec thnAxisCent{thnConfigAxisCent, "Centrality percentile"}; + const AxisSpec thnAxisCentMc{thnConfigAxisCent, "Centrality percentile (from gen. MC info)"}; + const AxisSpec thnAxisPromptScore{thnConfigAxisPromptScore, "BDT score prompt"}; + + std::vector axes = {thnAxisMass, thnAxisPt, thnAxisY}; + std::vector axesMcGen = {thnAxisPt, thnAxisPtB, thnAxisY, thnAxisOrigin}; + + if (doprocessDataDCAFitterFT0C || doprocessDataDCAFitterMlFT0C || doprocessDataDCAFitterFT0M || doprocessDataDCAFitterMlFT0M || doprocessDataKFParticleFT0C || doprocessDataKFParticleMlFT0C || doprocessDataKFParticleFT0M || doprocessDataKFParticleMlFT0M) { + axes.push_back(thnAxisCent); + axes.emplace_back(thnConfigAxisNumPvContr); + } + + if (doprocessMcDCAFitterFT0M || doprocessMcDCAFitterMlFT0M || doprocessMcKFParticleFT0M || doprocessMcKFParticleMlFT0M) { + axes.push_back(thnAxisCentMc); + axes.emplace_back(thnConfigAxisNumPvContr); + axesMcGen.push_back(thnAxisCentMc); + axesMcGen.emplace_back(thnConfigAxisNumPvContr); + } + + if (doprocessMcDCAFitter || doprocessMcDCAFitterMl || doprocessMcDCAFitterFT0M || doprocessMcDCAFitterMlFT0M || doprocessMcKFParticle || doprocessMcKFParticleMl || doprocessMcKFParticleFT0M || doprocessMcKFParticleMlFT0M) { + registry.add("hMcGen", "Gen. #Omega_{c}^{0} from charm and beauty", HistType::kTHnSparseD, axesMcGen); + registry.get(HIST("hMcGen"))->Sumw2(); + + if (doprocessMcDCAFitterFT0M || doprocessMcDCAFitterMlFT0M || doprocessMcKFParticleFT0M || doprocessMcKFParticleMlFT0M) { + registry.add("hMcGenWithRecoColl", "Gen. #Omega_{c}^{0} from charm and beauty (associated to a reco collision)", HistType::kTHnSparseD, axesMcGen); + registry.add("hNumRecoCollPerMcColl", "Number of reco collisions associated to a mc collision;Num. reco. coll. per Mc coll.;", {HistType::kTH1D, {{10, -0.5, 9.5}}}); + registry.get(HIST("hMcGenWithRecoColl"))->Sumw2(); + } + + axes.push_back(thnAxisPtB); + axes.push_back(thnAxisOrigin); + axes.push_back(thnAxisMatchFlag); + } + + if (doprocessDataKFParticleMl || doprocessDataKFParticleMlFT0C || doprocessDataKFParticleMlFT0M || doprocessDataDCAFitterMl || doprocessDataDCAFitterMlFT0C || doprocessDataDCAFitterMlFT0M || doprocessMcKFParticleMl || doprocessMcKFParticleMlFT0M || doprocessMcDCAFitterMl || doprocessMcDCAFitterMlFT0M) { + axes.push_back(thnAxisPromptScore); + } + + registry.add("hReco", "Reco. #Omega_{c}^{0} candidates", HistType::kTHnSparseD, axes); + registry.get(HIST("hReco"))->Sumw2(); + } + + template + void processData(const CandType& candidates) + { + for (const auto& candidate : candidates) { + if (!(candidate.resultSelections() == true || (candidate.resultSelections() == false && !selectionFlagOmegac0))) { + continue; + } + + double yOmegac{0.}; + if constexpr (UseKfParticle) { + yOmegac = candidate.kfRapOmegac(); + } else { + yOmegac = RecoDecay::y(std::array{candidate.pxCharmBaryon(), candidate.pyCharmBaryon(), candidate.pzCharmBaryon()}, o2::constants::physics::MassOmegaC0); + } + if (yCandRecoMax >= 0. && std::abs(yOmegac) > yCandRecoMax) { + continue; + } + + if constexpr (ApplyMl) { + registry.fill(HIST("hReco"), candidate.invMassCharmBaryon(), candidate.ptCharmBaryon(), yOmegac, candidate.mlProbOmegac()[0]); + } else { + registry.fill(HIST("hReco"), candidate.invMassCharmBaryon(), candidate.ptCharmBaryon(), yOmegac); + } + } + } + + template + void processDataCent(const CandType& candidate, CollType const& collision) + { + if (!(candidate.resultSelections() == true || (candidate.resultSelections() == false && !selectionFlagOmegac0))) { + return; + } + + double yOmegac{0.}; + if constexpr (UseKfParticle) { + yOmegac = candidate.kfRapOmegac(); + } else { + yOmegac = RecoDecay::y(std::array{candidate.pxCharmBaryon(), candidate.pyCharmBaryon(), candidate.pzCharmBaryon()}, o2::constants::physics::MassOmegaC0); + } + + if (yCandRecoMax >= 0. && std::abs(yOmegac) > yCandRecoMax) { + return; + } + + auto numPvContributors = collision.numContrib(); + if constexpr (UseCentrality) { + float const cent = o2::hf_centrality::getCentralityColl(collision); + if constexpr (ApplyMl) { + registry.fill(HIST("hReco"), candidate.invMassCharmBaryon(), candidate.ptCharmBaryon(), yOmegac, + cent, numPvContributors, candidate.mlProbOmegac()[0]); + } else { + registry.fill(HIST("hReco"), candidate.invMassCharmBaryon(), candidate.ptCharmBaryon(), yOmegac, + cent, numPvContributors); + } + } else { + if constexpr (ApplyMl) { + registry.fill(HIST("hReco"), candidate.invMassCharmBaryon(), candidate.ptCharmBaryon(), yOmegac, candidate.mlProbOmegac()[0]); + } else { + registry.fill(HIST("hReco"), candidate.invMassCharmBaryon(), candidate.ptCharmBaryon(), yOmegac); + } + } + } + + template + void processMc(const CandType& candidates, Omegac0Gen const& mcParticles) + { + // MC rec. + for (const auto& candidate : candidates) { + if (!(candidate.resultSelections() == true || (candidate.resultSelections() == false && !selectionFlagOmegac0))) { + continue; + } + + double yOmegac{0.}; + if constexpr (UseKfParticle) { + yOmegac = candidate.kfRapOmegac(); + } else { + yOmegac = RecoDecay::y(std::array{candidate.pxCharmBaryon(), candidate.pyCharmBaryon(), candidate.pzCharmBaryon()}, o2::constants::physics::MassOmegaC0); + } + + if (yCandRecoMax >= 0. && std::abs(yOmegac) > yCandRecoMax) { + continue; + } + + if constexpr (ApplyMl) { + registry.fill(HIST("hReco"), candidate.invMassCharmBaryon(), candidate.ptCharmBaryon(), yOmegac, candidate.ptBhadMotherPart(), candidate.originMcRec(), candidate.flagMcMatchRec(), candidate.mlProbOmegac()[0]); + } else { + registry.fill(HIST("hReco"), candidate.invMassCharmBaryon(), candidate.ptCharmBaryon(), yOmegac, candidate.ptBhadMotherPart(), candidate.originMcRec(), candidate.flagMcMatchRec()); + } + } + + // MC gen. + for (const auto& particle : mcParticles) { + if (yCandGenMax >= 0. && std::abs(particle.rapidityCharmBaryonGen()) > yCandGenMax) { + continue; + } + + auto ptGen = particle.pt(); + auto yGen = particle.rapidityCharmBaryonGen(); + + if (particle.originMcGen() == RecoDecay::OriginType::Prompt) { + registry.fill(HIST("hMcGen"), ptGen, -1., yGen, RecoDecay::OriginType::Prompt); + } else { + float const ptGenB = mcParticles.rawIteratorAt(particle.idxBhadMotherPart()).pt(); + registry.fill(HIST("hMcGen"), ptGen, ptGenB, yGen, RecoDecay::OriginType::NonPrompt); + } + } + } + + template + void processMcCent(const CandType& candidates, Omegac0Gen const& mcParticles, + CollisionsWithMcLabels const& collisions, McCollisionWithCents const&) + { + // MC rec. + for (const auto& candidate : candidates) { + if (!(candidate.resultSelections() == true || (candidate.resultSelections() == false && !selectionFlagOmegac0))) { + continue; + } + + double yOmegac{0.}; + if constexpr (UseKfParticle) { + yOmegac = candidate.kfRapOmegac(); + } else { + yOmegac = RecoDecay::y(std::array{candidate.pxCharmBaryon(), candidate.pyCharmBaryon(), candidate.pzCharmBaryon()}, o2::constants::physics::MassOmegaC0); + } + + if (yCandRecoMax >= 0. && std::abs(yOmegac) > yCandRecoMax) { + continue; + } + + auto collision = candidate.template collision_as(); + uint16_t const numPvContributors = collision.numContrib(); + float const mcCent = o2::hf_centrality::getCentralityColl(collision.template mcCollision_as()); + + if constexpr (ApplyMl) { + registry.fill(HIST("hReco"), candidate.invMassCharmBaryon(), candidate.ptCharmBaryon(), yOmegac, mcCent, numPvContributors, candidate.ptBhadMotherPart(), candidate.originMcRec(), candidate.flagMcMatchRec(), candidate.mlProbOmegac()[0]); + } else { + registry.fill(HIST("hReco"), candidate.invMassCharmBaryon(), candidate.ptCharmBaryon(), yOmegac, mcCent, numPvContributors, candidate.ptBhadMotherPart(), candidate.originMcRec(), candidate.flagMcMatchRec()); + } + } + + // MC gen. + for (const auto& particle : mcParticles) { + if (yCandGenMax >= 0. && std::abs(particle.rapidityCharmBaryonGen()) > yCandGenMax) { + continue; + } + + auto ptGen = particle.pt(); + auto yGen = particle.rapidityCharmBaryonGen(); + auto mcCollision = particle.template mcCollision_as(); + + int maxNumContrib = 0; + const auto& recoCollsPerMcColl = collisions.sliceBy(colPerMcCollision, mcCollision.globalIndex()); + for (const auto& recCol : recoCollsPerMcColl) { + maxNumContrib = recCol.numContrib() > maxNumContrib ? recCol.numContrib() : maxNumContrib; + } + + float const mcCent = o2::hf_centrality::getCentralityColl(mcCollision); + + if (particle.originMcGen() == RecoDecay::OriginType::Prompt) { + registry.fill(HIST("hMcGen"), ptGen, -1., yGen, RecoDecay::OriginType::Prompt, mcCent, maxNumContrib); + } else { + float const ptGenB = mcParticles.rawIteratorAt(particle.idxBhadMotherPart()).pt(); + registry.fill(HIST("hMcGen"), ptGen, ptGenB, yGen, RecoDecay::OriginType::NonPrompt, mcCent, maxNumContrib); + } + + registry.fill(HIST("hNumRecoCollPerMcColl"), recoCollsPerMcColl.size()); + + // fill sparse only for gen particles associated to a reconstructed collision + if (recoCollsPerMcColl.size() >= 1) { + if (particle.originMcGen() == RecoDecay::OriginType::Prompt) { + registry.fill(HIST("hMcGenWithRecoColl"), ptGen, -1., yGen, RecoDecay::OriginType::Prompt, mcCent, maxNumContrib); + } else { + float const ptGenB = mcParticles.rawIteratorAt(particle.idxBhadMotherPart()).pt(); + registry.fill(HIST("hMcGenWithRecoColl"), ptGen, ptGenB, yGen, RecoDecay::OriginType::NonPrompt, mcCent, maxNumContrib); + } + } + } + } + + /////////////////////////////////// + /// Data with DCAFitter // + /////////////////////////////////// + + void processDataDCAFitter(Omegac0Cands const& candidates) + { + processData(candidates); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataDCAFitter, "process data with DCAFitter", false); + + void processDataDCAFitterMl(Omegac0CandsMl const& candidates) + { + processData(candidates); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataDCAFitterMl, "process data with DCAFitter, ML selections", false); + + void processDataDCAFitterFT0C(Omegac0Cands const& candidates, + CollisionsWithFT0C const& collisions) + { + for (const auto& collision : collisions) { + auto groupedOmegacCandidates = candidates.sliceBy(candOmegacPerCollision, collision.globalIndex()); + + for (const auto& candidate : groupedOmegacCandidates) { + processDataCent(candidate, collision); + } + } + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataDCAFitterFT0C, "process data with DCAFitter, FT0C centrality", false); + + void processDataDCAFitterMlFT0C(Omegac0CandsMl const& candidates, + CollisionsWithFT0C const& collisions) + { + for (const auto& collision : collisions) { + auto groupedOmegacCandidates = candidates.sliceBy(candOmegacMlPerCollision, collision.globalIndex()); + + for (const auto& candidate : groupedOmegacCandidates) { + processDataCent(candidate, collision); + } + } + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataDCAFitterMlFT0C, "process data with DCAFitter, ML selections, FT0C centrality", false); + + void processDataDCAFitterFT0M(Omegac0Cands const& candidates, + CollisionsWithFT0M const& collisions) + { + for (const auto& collision : collisions) { + auto groupedOmegacCandidates = candidates.sliceBy(candOmegacPerCollision, collision.globalIndex()); + + for (const auto& candidate : groupedOmegacCandidates) { + processDataCent(candidate, collision); + } + } + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataDCAFitterFT0M, "process data with DCAFitter, FT0M centrality", false); + + void processDataDCAFitterMlFT0M(Omegac0CandsMl const& candidates, + CollisionsWithFT0M const& collisions) + { + for (const auto& collision : collisions) { + auto groupedOmegacCandidates = candidates.sliceBy(candOmegacMlPerCollision, collision.globalIndex()); + + for (const auto& candidate : groupedOmegacCandidates) { + processDataCent(candidate, collision); + } + } + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataDCAFitterMlFT0M, "process data with DCAFitter, ML selections, FT0M centrality", false); + + //////////////////////////////////// + /// Data with KFParticle // + //////////////////////////////////// + + void processDataKFParticle(Omegac0CandsKF const& candidates) + { + processData(candidates); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataKFParticle, "process data with KFParticle", false); + + void processDataKFParticleMl(Omegac0CandsMlKF const& candidates) + { + processData(candidates); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataKFParticleMl, "process data with KFParticle, ML selections", false); + + void processDataKFParticleFT0C(Omegac0CandsKF const& candidates, + CollisionsWithFT0C const& collisions) + { + for (const auto& collision : collisions) { + auto groupedOmegacCandidates = candidates.sliceBy(candOmegacKFPerCollision, collision.globalIndex()); + + for (const auto& candidate : groupedOmegacCandidates) { + processDataCent(candidate, collision); + } + } + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataKFParticleFT0C, "process data with KFParticle, FT0C centrality", false); + + void processDataKFParticleMlFT0C(Omegac0CandsMlKF const& candidates, + CollisionsWithFT0C const& collisions) + { + for (const auto& collision : collisions) { + auto groupedOmegacCandidates = candidates.sliceBy(candOmegacKFMlPerCollision, collision.globalIndex()); + + for (const auto& candidate : groupedOmegacCandidates) { + processDataCent(candidate, collision); + } + } + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataKFParticleMlFT0C, "process data with KFParticle, ML selections, FT0C centrality", false); + + void processDataKFParticleFT0M(Omegac0CandsKF const& candidates, + CollisionsWithFT0M const& collisions) + { + for (const auto& collision : collisions) { + auto groupedOmegacCandidates = candidates.sliceBy(candOmegacKFPerCollision, collision.globalIndex()); + + for (const auto& candidate : groupedOmegacCandidates) { + processDataCent(candidate, collision); + } + } + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataKFParticleFT0M, "process data with KFParticle, FT0M centrality", false); + + void processDataKFParticleMlFT0M(Omegac0CandsMlKF const& candidates, + CollisionsWithFT0M const& collisions) + { + for (const auto& collision : collisions) { + auto groupedOmegacCandidates = candidates.sliceBy(candOmegacKFMlPerCollision, collision.globalIndex()); + + for (const auto& candidate : groupedOmegacCandidates) { + processDataCent(candidate, collision); + } + } + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataKFParticleMlFT0M, "process data with KFParticle, ML selections, FT0M centrality", false); + + /////////////////////////////////// + /// MC with DCAFitter // + /////////////////////////////////// + + void processMcDCAFitter(Omegac0CandsMc const& omegaC0CandidatesMc, + Omegac0Gen const& mcParticles) + { + processMc(omegaC0CandidatesMc, mcParticles); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processMcDCAFitter, "Process MC with DCAFitter", false); + + void processMcDCAFitterMl(Omegac0CandsMlMc const& omegac0CandidatesMlMc, + Omegac0Gen const& mcParticles) + { + processMc(omegac0CandidatesMlMc, mcParticles); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processMcDCAFitterMl, "Process MC with DCAFitter, ML selections", false); + + void processMcDCAFitterFT0M(Omegac0CandsMc const& omegaC0CandidatesMc, + Omegac0Gen const& mcParticles, + CollisionsWithMcLabels const& collisions, + McCollisionsWithFT0M const& mcCollisions) + { + processMcCent(omegaC0CandidatesMc, mcParticles, collisions, mcCollisions); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processMcDCAFitterFT0M, "Process MC with DCAFitter, FT0M centrality (from MC)", false); + + void processMcDCAFitterMlFT0M(Omegac0CandsMlMc const& omegac0CandidatesMlMc, + Omegac0Gen const& mcParticles, + CollisionsWithMcLabels const& collisions, + McCollisionsWithFT0M const& mcCollisions) + { + processMcCent(omegac0CandidatesMlMc, mcParticles, collisions, mcCollisions); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processMcDCAFitterMlFT0M, "Process MC with DCAFitter, ML selections, FT0M centrality (from MC)", false); + + //////////////////////////////////// + /// MC with KFParticle // + //////////////////////////////////// + + void processMcKFParticle(Omegac0CandsMcKF const& omegaC0CandidatesMcKF, + Omegac0Gen const& mcParticles) + { + processMc(omegaC0CandidatesMcKF, mcParticles); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processMcKFParticle, "Process MC with KFParticle", false); + + void processMcKFParticleMl(Omegac0CandsMlMcKF const& omegac0CandidatesMlMcKF, + Omegac0Gen const& mcParticles) + { + processMc(omegac0CandidatesMlMcKF, mcParticles); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processMcKFParticleMl, "Process MC with KFParticle, ML selections", false); + + void processMcKFParticleFT0M(Omegac0CandsMcKF const& omegaC0CandidatesMcKF, + Omegac0Gen const& mcParticles, + CollisionsWithMcLabels const& collisions, + McCollisionsWithFT0M const& mcCollisions) + { + processMcCent(omegaC0CandidatesMcKF, mcParticles, collisions, mcCollisions); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processMcKFParticleFT0M, "Process MC with KFParticle, FT0M centrality (from MC)", false); + + void processMcKFParticleMlFT0M(Omegac0CandsMlMcKF const& omegac0CandidatesMlMcKF, + Omegac0Gen const& mcParticles, + CollisionsWithMcLabels const& collisions, + McCollisionsWithFT0M const& mcCollisions) + { + processMcCent(omegac0CandidatesMlMcKF, mcParticles, collisions, mcCollisions); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processMcKFParticleMlFT0M, "Process MC with KFParticle, ML selections, FT0M centrality (from MC)", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGHF/D2H/Tasks/taskUpcLc.cxx b/PWGHF/D2H/Tasks/taskUpcLc.cxx index d9be942e4e0..9d8d28b9d8a 100644 --- a/PWGHF/D2H/Tasks/taskUpcLc.cxx +++ b/PWGHF/D2H/Tasks/taskUpcLc.cxx @@ -190,7 +190,7 @@ struct HfTaskUpcLc { LOGP(fatal, "no or more than one process function enabled! Please check your configuration!"); } - auto vbins = (std::vector)binsPt; + auto vbins = std::vector(binsPt); registry.add("Data/fitInfo/ampFT0A_vs_ampFT0C", "FT0-A vs FT0-C amplitude;FT0-A amplitude (a.u.);FT0-C amplitude (a.u.)", {HistType::kTH2F, {{200, 0., 200}, {200, 0., 200}}}); registry.add("Data/zdc/energyZNA_vs_energyZNC", "ZNA vs ZNC common energy;E_{ZNA}^{common} (a.u.);E_{ZNC}^{common} (a.u.)", {HistType::kTH2F, {{100, 0., 10}, {100, 0., 10}}}); registry.add("Data/zdc/timeZNA_vs_timeZNC", "ZNA vs ZNC time;ZNA Time;ZNC time", {HistType::kTH2F, {{200, -10., 10}, {200, -10., 10}}}); diff --git a/PWGHF/D2H/Tasks/taskXic.cxx b/PWGHF/D2H/Tasks/taskXic.cxx index 46dfdd64521..e00f8af2153 100644 --- a/PWGHF/D2H/Tasks/taskXic.cxx +++ b/PWGHF/D2H/Tasks/taskXic.cxx @@ -125,7 +125,7 @@ struct HfTaskXic { AxisSpec const axisNSigmaPi = {100, -6.f, 6.f, "n#it{#sigma}_{#pi}"}; AxisSpec const axisNSigmaKa = {100, -6.f, 6.f, "n#it{#sigma}_{K}"}; - auto vbins = (std::vector)binsPt; // histo in pt bins + auto vbins = std::vector(binsPt); // histo in pt bins registry.add("Data/hMassVsPt", "3-prong candidates;inv. mass (p K #pi) (GeV/#it{c}^{2});;entries", {HistType::kTH2F, {{500, 2., 3.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("Data/hDecLength", "3-prong candidates;decay length (cm);;entries", {HistType::kTH2F, {{200, 0., 2.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("Data/hDecLengthXY", "3-prong candidates;decay length XY (cm);;entries", {HistType::kTH2F, {{200, 0., 2.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); @@ -175,8 +175,8 @@ struct HfTaskXic { registry.add("Data/hPVsTOFNSigmaKa_Prong2", "3-prong candidates;#it{p} bachelor (GeV/#it{c}) ;n#it{#sigma}_{K} TOF", {HistType::kTH2F, {axisPPid, axisNSigmaKa}}); // MC generated - registry.add("MC/generated/hYGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);candidate #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/generated/hEtaGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);candidate #it{#eta}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/generated/hYGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);candidate #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/generated/hEtaGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);candidate #it{#eta}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); // MC reconstructed:signal registry.add("MC/reconstructed/signal/hMassSig", "Invariant mass (matched);m (p K #pi) (GeV/#it{c}^{2});;entries", {HistType::kTH2F, {{500, 1.6, 3.1}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); diff --git a/PWGHF/D2H/Tasks/taskXicToXiPiPi.cxx b/PWGHF/D2H/Tasks/taskXicToXiPiPi.cxx index b6b709c7c55..880952751cf 100644 --- a/PWGHF/D2H/Tasks/taskXicToXiPiPi.cxx +++ b/PWGHF/D2H/Tasks/taskXicToXiPiPi.cxx @@ -110,7 +110,7 @@ struct HfTaskXicToXiPiPi { static const AxisSpec axisMassXic = {300, 1.8, 3.0, "inv. mass (GeV/#it{c}^{2})"}; static const AxisSpec axisMassXiRes = {300, 1.4, 2.7, "inv. mass (GeV/#it{c}^{2})"}; - static const AxisSpec axisPt = {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}; + static const AxisSpec axisPt = {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}; static const AxisSpec axisDecLength = {binsDecLength}; static const AxisSpec axisErrDecLength = {binsErrDecLength}; static const AxisSpec axisDCA = {binsDCA}; diff --git a/PWGHF/DataModel/CandidateReconstructionTables.h b/PWGHF/DataModel/CandidateReconstructionTables.h index c5c3eea19f1..6036b9238bc 100644 --- a/PWGHF/DataModel/CandidateReconstructionTables.h +++ b/PWGHF/DataModel/CandidateReconstructionTables.h @@ -584,6 +584,9 @@ DECLARE_SOA_TABLE(HfCandCascadeMcGen, "AOD", "HFCANDCASCMCGEN", //! // specific BPlus candidate properties namespace hf_cand_bplus { +DECLARE_SOA_COLUMN(XSecondaryVertexD, xSecondaryVertexD, float); +DECLARE_SOA_COLUMN(YSecondaryVertexD, ySecondaryVertexD, float); +DECLARE_SOA_COLUMN(ZSecondaryVertexD, zSecondaryVertexD, float); DECLARE_SOA_INDEX_COLUMN_FULL(Prong0, prong0, int, HfCand2Prong, "_0"); // D0 index DECLARE_SOA_DYNAMIC_COLUMN(ImpactParameterProduct, impactParameterProduct, // Impact parameter product for B+ -> J/Psi K [](float px0, float py0, float pz0, float px1, float py1, float pz1, float xVtxP, float yVtxP, float zVtxP, float xVtxS, float yVtxS, float zVtxS, float impParK) -> float { return impParK * RecoDecay::impParXY(std::array{xVtxP, yVtxP, zVtxP}, std::array{xVtxS, yVtxS, zVtxS}, RecoDecay::pVec(std::array{px0, py0, pz0}, std::array{px1, py1, pz1})); }); @@ -593,6 +596,22 @@ DECLARE_SOA_DYNAMIC_COLUMN(CpaJpsi, cpaJpsi, //! [](float xVtxP, float yVtxP, float zVtxP, float xVtxS, float yVtxS, float zVtxS, float px0, float py0, float pz0, float px1, float py1, float pz1) -> float { return RecoDecay::cpa(std::array{xVtxP, yVtxP, zVtxP}, std::array{xVtxS, yVtxS, zVtxS}, std::array{px0 + px1, py0 + py1, pz0 + pz1}); }); DECLARE_SOA_DYNAMIC_COLUMN(CpaJpsiXY, cpaJpsiXY, //! [](float xVtxP, float yVtxP, float xVtxS, float yVtxS, float px0, float py0, float px1, float py1) -> float { return RecoDecay::cpaXY(std::array{xVtxP, yVtxP}, std::array{xVtxS, yVtxS}, std::array{px0 + px1, py0 + py1}); }); +DECLARE_SOA_DYNAMIC_COLUMN(CpaDToPv, cpaDToPv, //! + [](float xVtxP, float yVtxP, float zVtxP, float xVtxS, float yVtxS, float zVtxS, float px, float py, float pz) -> float { return RecoDecay::cpa(std::array{xVtxP, yVtxP, zVtxP}, std::array{xVtxS, yVtxS, zVtxS}, std::array{px, py, pz}); }); +DECLARE_SOA_DYNAMIC_COLUMN(CpaDToPvXY, cpaDToPvXY, //! + [](float xVtxP, float yVtxP, float xVtxS, float yVtxS, float px, float py) -> float { return RecoDecay::cpaXY(std::array{xVtxP, yVtxP}, std::array{xVtxS, yVtxS}, std::array{px, py}); }); +DECLARE_SOA_DYNAMIC_COLUMN(CpaDToB, cpaDToB, //! + [](float xVtxB, float yVtxB, float zVtxB, float xVtxS, float yVtxS, float zVtxS, float px, float py, float pz) -> float { return RecoDecay::cpa(std::array{xVtxB, yVtxB, zVtxB}, std::array{xVtxS, yVtxS, zVtxS}, std::array{px, py, pz}); }); +DECLARE_SOA_DYNAMIC_COLUMN(CpaDToBXY, cpaDToBXY, //! + [](float xVtxB, float yVtxB, float xVtxS, float yVtxS, float px, float py) -> float { return RecoDecay::cpaXY(std::array{xVtxB, yVtxB}, std::array{xVtxS, yVtxS}, std::array{px, py}); }); +DECLARE_SOA_DYNAMIC_COLUMN(DecayLengthDToPv, decayLengthDToPv, //! + [](float xVtxP, float yVtxP, float zVtxP, float xVtxS, float yVtxS, float zVtxS) -> float { return RecoDecay::distance(std::array{xVtxP, yVtxP, zVtxP}, std::array{xVtxS, yVtxS, zVtxS}); }); +DECLARE_SOA_DYNAMIC_COLUMN(DecayLengthDToPvXY, decayLengthDToPvXY, //! + [](float xVtxP, float yVtxP, float xVtxS, float yVtxS) -> float { return RecoDecay::distanceXY(std::array{xVtxP, yVtxP}, std::array{xVtxS, yVtxS}); }); +DECLARE_SOA_DYNAMIC_COLUMN(DecayLengthDToB, decayLengthDToB, //! + [](float xVtxB, float yVtxB, float zVtxB, float xVtxS, float yVtxS, float zVtxS) -> float { return RecoDecay::distance(std::array{xVtxB, yVtxB, zVtxB}, std::array{xVtxS, yVtxS, zVtxS}); }); +DECLARE_SOA_DYNAMIC_COLUMN(DecayLengthDToBXY, decayLengthDToBXY, //! + [](float xVtxB, float yVtxB, float xVtxS, float yVtxS) -> float { return RecoDecay::distanceXY(std::array{xVtxB, yVtxB}, std::array{xVtxS, yVtxS}); }); enum DecayTypeMc : uint8_t { BplusToD0PiToKPiPi = 0, BplusToD0KToKPiK, @@ -616,7 +635,17 @@ DECLARE_SOA_TABLE(HfCandBplusBase, "AOD", "HFCANDBPLUSBASE", hf_cand::PxProng1, hf_cand::PyProng1, hf_cand::PzProng1, hf_cand::ImpactParameter0, hf_cand::ImpactParameter1, hf_cand::ErrorImpactParameter0, hf_cand::ErrorImpactParameter1, + // D meson decay vertex position + hf_cand_bplus::XSecondaryVertexD, hf_cand_bplus::YSecondaryVertexD, hf_cand_bplus::ZSecondaryVertexD, /* dynamic columns */ + hf_cand_bplus::CpaDToPv, + hf_cand_bplus::CpaDToPvXY, + hf_cand_bplus::CpaDToB, + hf_cand_bplus::CpaDToBXY, + hf_cand_bplus::DecayLengthDToPv, + hf_cand_bplus::DecayLengthDToPvXY, + hf_cand_bplus::DecayLengthDToB, + hf_cand_bplus::DecayLengthDToBXY, hf_cand_2prong::M, hf_cand_2prong::M2, hf_cand_2prong::ImpactParameterProduct, @@ -1193,6 +1222,9 @@ DECLARE_SOA_DYNAMIC_COLUMN(PtPiFromCharmBaryon, ptPiFromCharmBaryon, DECLARE_SOA_DYNAMIC_COLUMN(PtKaFromCasc, ptKaFromCasc, [](float px, float py) -> float { return RecoDecay::pt(px, py); }); +DECLARE_SOA_DYNAMIC_COLUMN(NormDecayLenCharmBar, normDecayLenCharmBar, + [](float decLen, float errDecLen) -> float { return decLen / errDecLen; }); + } // end of namespace hf_cand_xic0_omegac0 // declare dedicated Omegac and Xic to Xi Pi candidate table @@ -1259,7 +1291,8 @@ DECLARE_SOA_TABLE(HfCandToOmegaPi, "AOD", "HFCANDTOOMEGAPI", hf_cand_xic0_omegac0::DcaXYToPvV0Dau0, hf_cand_xic0_omegac0::DcaXYToPvV0Dau1, hf_cand_xic0_omegac0::DcaXYToPvCascDau, hf_cand_xic0_omegac0::DcaZToPvV0Dau0, hf_cand_xic0_omegac0::DcaZToPvV0Dau1, hf_cand_xic0_omegac0::DcaZToPvCascDau, hf_cand_xic0_omegac0::DcaCascDau, hf_cand_xic0_omegac0::DcaV0Dau, hf_cand_xic0_omegac0::DcaCharmBaryonDau, - hf_cand_xic0_omegac0::DecLenCharmBaryon, hf_cand_xic0_omegac0::DecLenCascade, hf_cand_xic0_omegac0::DecLenV0, hf_cand_xic0_omegac0::ErrorDecayLengthCharmBaryon, hf_cand_xic0_omegac0::ErrorDecayLengthXYCharmBaryon, hf_track_index::HFflag); + hf_cand_xic0_omegac0::DecLenCharmBaryon, hf_cand_xic0_omegac0::DecLenCascade, hf_cand_xic0_omegac0::DecLenV0, hf_cand_xic0_omegac0::ErrorDecayLengthCharmBaryon, hf_cand_xic0_omegac0::ErrorDecayLengthXYCharmBaryon, hf_track_index::HFflag, + hf_cand_xic0_omegac0::NormDecayLenCharmBar); DECLARE_SOA_TABLE(HfCandToOmegaK, "AOD", "HFCANDTOOMEGAK", o2::soa::Index<>, @@ -1500,6 +1533,7 @@ DECLARE_SOA_COLUMN(NSigTofPiFromLambda, nSigTofPiFromLambda, float); DECLARE_SOA_COLUMN(NSigTofPrFromLambda, nSigTofPrFromLambda, float); // MC DECLARE_SOA_COLUMN(DecayLengthMcGen, decayLengthMcGen, float); +DECLARE_SOA_COLUMN(MassMcGen, massMcGen, float); // Residuals and pulls DECLARE_SOA_COLUMN(PtResidual, ptResidual, float); DECLARE_SOA_COLUMN(PResidual, pResidual, float); @@ -1605,7 +1639,8 @@ DECLARE_SOA_TABLE(HfCandXicMcGen, "AOD", "HFCANDXICMCGEN", hf_cand_mc_flag::FlagMcMatchGen, hf_cand_mc_flag::OriginMcGen, hf_cand_mc_flag::PdgBhadMotherPart, - hf_cand_xic_to_xi_pi_pi::DecayLengthMcGen); + hf_cand_xic_to_xi_pi_pi::DecayLengthMcGen, + hf_cand_xic_to_xi_pi_pi::MassMcGen); // table with residuals and pulls of PV DECLARE_SOA_TABLE(HfCandXicResid, "AOD", "HFCANDXICRESID", diff --git a/PWGHF/HFC/DataModel/CorrelationTables.h b/PWGHF/HFC/DataModel/CorrelationTables.h index de52b75420f..fcea57be77c 100644 --- a/PWGHF/HFC/DataModel/CorrelationTables.h +++ b/PWGHF/HFC/DataModel/CorrelationTables.h @@ -497,6 +497,21 @@ DECLARE_SOA_TABLE(TrkRecInfoDplus, "AOD", "TRKRECINFODPLUS", //! Tracks Reconstr aod::hf_correlation_dplus_hadron::TrackDcaZ, aod::hf_correlation_dplus_hadron::TrackTPCNClsCrossedRows); +// definition of columns and tables for the beauty-mother information of the charm-hadron trigger (MC only), usable by any D-hadron correlation analysis +namespace hf_correlation_info_b_mother +{ +DECLARE_SOA_COLUMN(PtB, ptB, float); //! Generated pT of the beauty mother of the charm hadron (-1 if not from beauty) +DECLARE_SOA_COLUMN(DeltaPhiBtoD, deltaPhiBtoD, float); //! phi(charm hadron) - phi(B mother, generated) in [-pihalf, 3.*pihalf) (-999 if not from beauty) +} // namespace hf_correlation_info_b_mother + +DECLARE_SOA_TABLE(BMotherPairInfo, "AOD", "BMOTHERPAIRINFO", //! Charm-hadron-hadron pairs beauty-mother information, joinable with the pair table + aod::hf_correlation_info_b_mother::PtB, + aod::hf_correlation_info_b_mother::DeltaPhiBtoD); + +DECLARE_SOA_TABLE(BMotherCandInfo, "AOD", "BMOTHERCANDINFO", //! Charm-hadron candidates beauty-mother information, joinable with the candidate table + aod::hf_correlation_info_b_mother::PtB, + aod::hf_correlation_info_b_mother::DeltaPhiBtoD); + // definition of columns and tables for Dstar-Hadron correlation pair namespace hf_correlation_dstar_hadron { diff --git a/PWGHF/HFC/DataModel/DMesonPairsTables.h b/PWGHF/HFC/DataModel/DMesonPairsTables.h index 65f76dc586e..a5dccbaf0aa 100644 --- a/PWGHF/HFC/DataModel/DMesonPairsTables.h +++ b/PWGHF/HFC/DataModel/DMesonPairsTables.h @@ -113,15 +113,6 @@ DECLARE_SOA_COLUMN(PoolBinD, poolBinD, int); //! Pool bin of the D meson DECLARE_SOA_COLUMN(GIndexColD, gIndexColD, int); //! G-index column of the D meson DECLARE_SOA_COLUMN(TimestampD, timestampD, int64_t); //! Timestamp of the D meson -// Associated hadron -DECLARE_SOA_COLUMN(PtHad, ptHad, float); //! Transverse momentum of the associated hadron -DECLARE_SOA_COLUMN(YHad, yHad, float); //! Rapidity of the associated hadron -DECLARE_SOA_COLUMN(EtaHad, etaHad, float); //! Pseudorapidity of the associated hadron -DECLARE_SOA_COLUMN(PhiHad, phiHad, float); //! Azimuthal angle of the associated hadron -DECLARE_SOA_COLUMN(PoolBinHad, poolBinHad, int); //! Pool bin of the associated hadron -DECLARE_SOA_COLUMN(GIndexColHad, gIndexColHad, int); //! G-index column of the associated hadron -DECLARE_SOA_COLUMN(TimestampHad, timestampHad, int64_t); //! Timestamp of the associated hadron - } // namespace hf_correlation_d_meson_had // Definition of the D meson table for D-had correlations. Contains the info needed at Data level. @@ -139,14 +130,6 @@ DECLARE_SOA_COLUMN(TimestampHad, timestampHad, int64_t); //! Timestamp of the as DECLARE_DMESON_ME_TABLE(DMesonInfoCand1, 1, "DMESCAND1"); //! D0 cand 1 info for event mixing DECLARE_DMESON_ME_TABLE(DMesonInfoCand2, 2, "DMESCAND2"); //! D0 cand 2 info for event mixing -DECLARE_SOA_TABLE(AssocHadInfo, "AOD", "ASSOCHADINFO", - hf_correlation_d_meson_had::PtHad, - hf_correlation_d_meson_had::EtaHad, - hf_correlation_d_meson_had::PhiHad, - hf_correlation_d_meson_had::PoolBinHad, - hf_correlation_d_meson_had::GIndexColHad, - hf_correlation_d_meson_had::TimestampHad); - } // namespace o2::aod #endif // PWGHF_HFC_DATAMODEL_DMESONPAIRSTABLES_H_ diff --git a/PWGHF/HFC/DataModel/DerivedDataCorrelationTables.h b/PWGHF/HFC/DataModel/DerivedDataCorrelationTables.h index e7c69695f32..6a807a0db0c 100644 --- a/PWGHF/HFC/DataModel/DerivedDataCorrelationTables.h +++ b/PWGHF/HFC/DataModel/DerivedDataCorrelationTables.h @@ -47,16 +47,17 @@ using HfcRedCollision = HfcRedCollisions::iterator; namespace hf_candidate_reduced { -DECLARE_SOA_INDEX_COLUMN(HfcRedCollision, hfcRedCollision); //! ReducedCollision index -DECLARE_SOA_COLUMN(Prong0Id, prong0Id, int); //! Prong 0 index -DECLARE_SOA_COLUMN(Prong1Id, prong1Id, int); //! Prong 1 index -DECLARE_SOA_COLUMN(Prong2Id, prong2Id, int); //! Prong2 index -DECLARE_SOA_COLUMN(PhiCand, phiCand, float); //! Phi of the candidate -DECLARE_SOA_COLUMN(EtaCand, etaCand, float); //! Eta of the candidate -DECLARE_SOA_COLUMN(PtCand, ptCand, float); //! Pt of the candidate -DECLARE_SOA_COLUMN(InvMass, invMass, float); //! Invariant mass of charm candidate -DECLARE_SOA_COLUMN(BdtScorePrompt, bdtScorePrompt, float); //! BDT output score for prompt hypothesis -DECLARE_SOA_COLUMN(BdtScoreBkg, bdtScoreBkg, float); //! BDT output score for background hypothesis +DECLARE_SOA_INDEX_COLUMN(HfcRedCollision, hfcRedCollision); //! ReducedCollision index +DECLARE_SOA_COLUMN(Prong0Id, prong0Id, int); //! Prong 0 index +DECLARE_SOA_COLUMN(Prong1Id, prong1Id, int); //! Prong 1 index +DECLARE_SOA_COLUMN(Prong2Id, prong2Id, int); //! Prong2 index +DECLARE_SOA_COLUMN(PhiCand, phiCand, float); //! Phi of the candidate +DECLARE_SOA_COLUMN(EtaCand, etaCand, float); //! Eta of the candidate +DECLARE_SOA_COLUMN(PtCand, ptCand, float); //! Pt of the candidate +DECLARE_SOA_COLUMN(InvMass, invMass, float); //! Invariant mass of charm candidate +DECLARE_SOA_COLUMN(BdtScorePrompt, bdtScorePrompt, float); //! BDT output score for prompt hypothesis +DECLARE_SOA_COLUMN(BdtScoreNonPrompt, bdtScoreNonPrompt, float); //! BDT output score for non-prompt hypothesis +DECLARE_SOA_COLUMN(BdtScoreBkg, bdtScoreBkg, float); //! BDT output score for background hypothesis } // namespace hf_candidate_reduced DECLARE_SOA_TABLE(HcCandReduceds, "AOD", "HCCANDREDUCED", //! Table with charm hadron candidate info soa::Index<>, @@ -72,8 +73,9 @@ DECLARE_SOA_TABLE(HcCandReduceds, "AOD", "HCCANDREDUCED", //! Table with charm h DECLARE_SOA_TABLE(HcCandSelInfos, "AOD", "HCCANDSELINFO", //! Table with charm hadron candidate selection info soa::Index<>, aod::hf_candidate_reduced::HfcRedCollisionId, + aod::hf_candidate_reduced::BdtScoreBkg, aod::hf_candidate_reduced::BdtScorePrompt, - aod::hf_candidate_reduced::BdtScoreBkg); + aod::hf_candidate_reduced::BdtScoreNonPrompt); namespace hf_assoc_track_reduced { diff --git a/PWGHF/HFC/DataModel/ReducedDMesonPairsTables.h b/PWGHF/HFC/DataModel/ReducedDMesonPairsTables.h index 9c4484de852..42724605adf 100644 --- a/PWGHF/HFC/DataModel/ReducedDMesonPairsTables.h +++ b/PWGHF/HFC/DataModel/ReducedDMesonPairsTables.h @@ -38,6 +38,8 @@ DECLARE_SOA_TABLE(HfCandDpMcEvs, "AOD", "HFCANDDPMCEV", namespace full { +DECLARE_SOA_INDEX_COLUMN(HfCandDpFullEv, hfCandDpFullEv); +DECLARE_SOA_INDEX_COLUMN(HfCandDpMcEv, hfCandDpMcEv); DECLARE_SOA_COLUMN(RSecondaryVertex, rSecondaryVertex, float); //! Radius of secondary vertex (cm) DECLARE_SOA_COLUMN(PtProng0, ptProng0, float); //! Transverse momentum of prong0 (GeV/c) DECLARE_SOA_COLUMN(PProng0, pProng0, float); //! Momentum of prong0 (GeV/c) @@ -57,8 +59,6 @@ DECLARE_SOA_COLUMN(Eta, eta, float); DECLARE_SOA_COLUMN(Phi, phi, float); //! Azimuth angle of candidate DECLARE_SOA_COLUMN(E, e, float); //! Energy of candidate (GeV) DECLARE_SOA_COLUMN(Centrality, centrality, float); //! Collision centrality -DECLARE_SOA_INDEX_COLUMN(HfCandDpMcEv, hfCandDpMcEv); //! The Mc collision index this MC particles belongs to -DECLARE_SOA_INDEX_COLUMN(HfCandDpFullEv, hfCandDpFullEv); //! The collision index this candidate belongs to DECLARE_SOA_COLUMN(NSigTpcPi0, nSigTpcPi0, float); //! TPC Nsigma separation for prong0 with pion mass hypothesis DECLARE_SOA_COLUMN(NSigTpcKa0, nSigTpcKa0, float); //! TPC Nsigma separation for prong0 with kaon mass hypothesis DECLARE_SOA_COLUMN(NSigTofPi0, nSigTofPi0, float); //! TOF Nsigma separation for prong0 with pion mass hypothesis @@ -71,12 +71,12 @@ DECLARE_SOA_COLUMN(NSigTpcPi2, nSigTpcPi2, float); DECLARE_SOA_COLUMN(NSigTpcKa2, nSigTpcKa2, float); //! TPC Nsigma separation for prong2 with kaon mass hypothesis DECLARE_SOA_COLUMN(NSigTofPi2, nSigTofPi2, float); //! TOF Nsigma separation for prong2 with pion mass hypothesis DECLARE_SOA_COLUMN(NSigTofKa2, nSigTofKa2, float); //! TOF Nsigma separation for prong2 with kaon mass hypothesis -DECLARE_SOA_COLUMN(NSigTpcTofPi0, nSigTpcTofPi0, float); //! TPC and TOF combined Nsigma separation for prong0 with pion mass hypothesis -DECLARE_SOA_COLUMN(NSigTpcTofKa0, nSigTpcTofKa0, float); //! TPC and TOF combined Nsigma separation for prong0 with kaon mass hypothesis -DECLARE_SOA_COLUMN(NSigTpcTofPi1, nSigTpcTofPi1, float); //! TPC and TOF combined Nsigma separation for prong1 with pion mass hypothesis -DECLARE_SOA_COLUMN(NSigTpcTofKa1, nSigTpcTofKa1, float); //! TPC and TOF combined Nsigma separation for prong1 with kaon mass hypothesis -DECLARE_SOA_COLUMN(NSigTpcTofPi2, nSigTpcTofPi2, float); //! TPC and TOF combined Nsigma separation for prong2 with pion mass hypothesis -DECLARE_SOA_COLUMN(NSigTpcTofKa2, nSigTpcTofKa2, float); //! TPC and TOF combined Nsigma separation for prong2 with kaon mass hypothesis +DECLARE_SOA_COLUMN(TpcTofNSigmaPi0, tpcTofNSigmaPi0, float); //! TPC and TOF combined Nsigma separation for prong0 with pion mass hypothesis +DECLARE_SOA_COLUMN(TpcTofNSigmaKa0, tpcTofNSigmaKa0, float); //! TPC and TOF combined Nsigma separation for prong0 with kaon mass hypothesis +DECLARE_SOA_COLUMN(TpcTofNSigmaPi1, tpcTofNSigmaPi1, float); //! TPC and TOF combined Nsigma separation for prong1 with pion mass hypothesis +DECLARE_SOA_COLUMN(TpcTofNSigmaKa1, tpcTofNSigmaKa1, float); //! TPC and TOF combined Nsigma separation for prong1 with kaon mass hypothesis +DECLARE_SOA_COLUMN(TpcTofNSigmaPi2, tpcTofNSigmaPi2, float); //! TPC and TOF combined Nsigma separation for prong2 with pion mass hypothesis +DECLARE_SOA_COLUMN(TpcTofNSigmaKa2, tpcTofNSigmaKa2, float); //! TPC and TOF combined Nsigma separation for prong2 with kaon mass hypothesis DECLARE_SOA_COLUMN(DecayLength, decayLength, float); //! Decay length of candidate (cm) DECLARE_SOA_COLUMN(DecayLengthXY, decayLengthXY, float); //! Transverse decay length of candidate (cm) DECLARE_SOA_COLUMN(DecayLengthNormalised, decayLengthNormalised, float); //! Normalised decay length of candidate @@ -96,7 +96,7 @@ DECLARE_SOA_TABLE(HfCandDpMls, "AOD", "HFCANDDPML", full::MlScore0, full::MlScore1) -DECLARE_SOA_TABLE(HfCandDpTinys, "AOD", "HFCANDDPTINY", +DECLARE_SOA_TABLE(HfCandDpTinys, "AOD", "HFCANDDPTINY", o2::soa::Index<>, full::CandidateSelFlag, full::M, full::Pt, @@ -107,7 +107,7 @@ DECLARE_SOA_TABLE(HfCandDpTinys, "AOD", "HFCANDDPTINY", hf_cand_mc_flag::OriginMcRec, hf_cand_mc_flag::FlagMcDecayChanRec) -DECLARE_SOA_TABLE(HfCandDpLites, "AOD", "HFCANDDPLITE", +DECLARE_SOA_TABLE(HfCandDpSimples, "AOD", "HFCANDDPSIMPLE", o2::soa::Index<>, hf_cand::Chi2PCA, full::DecayLength, full::DecayLengthXY, @@ -126,20 +126,20 @@ DECLARE_SOA_TABLE(HfCandDpLites, "AOD", "HFCANDDPLITE", full::NSigTpcKa0, full::NSigTofPi0, full::NSigTofKa0, - full::NSigTpcTofPi0, - full::NSigTpcTofKa0, + full::TpcTofNSigmaPi0, + full::TpcTofNSigmaKa0, full::NSigTpcPi1, full::NSigTpcKa1, full::NSigTofPi1, full::NSigTofKa1, - full::NSigTpcTofPi1, - full::NSigTpcTofKa1, + full::TpcTofNSigmaPi1, + full::TpcTofNSigmaKa1, full::NSigTpcPi2, full::NSigTpcKa2, full::NSigTofPi2, full::NSigTofKa2, - full::NSigTpcTofPi2, - full::NSigTpcTofKa2, + full::TpcTofNSigmaPi2, + full::TpcTofNSigmaKa2, full::CandidateSelFlag, full::M, full::Pt, @@ -155,7 +155,7 @@ DECLARE_SOA_TABLE(HfCandDpLites, "AOD", "HFCANDDPLITE", hf_cand_mc_flag::OriginMcRec, hf_cand_mc_flag::FlagMcDecayChanRec) -DECLARE_SOA_TABLE(HfCandDpFulls, "AOD", "HFCANDDPFULL", +DECLARE_SOA_TABLE(HfCandDpFulls, "AOD", "HFCANDDPFULL", o2::soa::Index<>, hf_cand::XSecondaryVertex, hf_cand::YSecondaryVertex, hf_cand::ZSecondaryVertex, @@ -201,20 +201,20 @@ DECLARE_SOA_TABLE(HfCandDpFulls, "AOD", "HFCANDDPFULL", full::NSigTpcKa0, full::NSigTofPi0, full::NSigTofKa0, - full::NSigTpcTofPi0, - full::NSigTpcTofKa0, + full::TpcTofNSigmaPi0, + full::TpcTofNSigmaKa0, full::NSigTpcPi1, full::NSigTpcKa1, full::NSigTofPi1, full::NSigTofKa1, - full::NSigTpcTofPi1, - full::NSigTpcTofKa1, + full::TpcTofNSigmaPi1, + full::TpcTofNSigmaKa1, full::NSigTpcPi2, full::NSigTpcKa2, full::NSigTofPi2, full::NSigTofKa2, - full::NSigTpcTofPi2, - full::NSigTpcTofKa2, + full::TpcTofNSigmaPi2, + full::TpcTofNSigmaKa2, full::CandidateSelFlag, full::M, full::Pt, @@ -233,7 +233,7 @@ DECLARE_SOA_TABLE(HfCandDpFulls, "AOD", "HFCANDDPFULL", hf_cand_mc_flag::OriginMcRec, hf_cand_mc_flag::FlagMcDecayChanRec); -DECLARE_SOA_TABLE(HfCandDpMcPs, "AOD", "HFCANDDPMCP", +DECLARE_SOA_TABLE(HfCandDpMcPs, "AOD", "HFCANDDPMCP", o2::soa::Index<>, full::Pt, full::Eta, full::Phi, diff --git a/PWGHF/HFC/Macros/DhCorrelationExtraction.cxx b/PWGHF/HFC/Macros/DhCorrelationExtraction.cxx index 328c05f9bbd..fd5b2ca77bc 100644 --- a/PWGHF/HFC/Macros/DhCorrelationExtraction.cxx +++ b/PWGHF/HFC/Macros/DhCorrelationExtraction.cxx @@ -325,11 +325,11 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t normalizeMePlot(hMeSideb[iPool], hMeSidebSoftPi[iPool]); // Apply Event Mixing Correction - hCorrSign[iPool] = reinterpret_cast(hSeSign[iPool]->Clone(Form("hCorr_Sign_Pool%d", iPool))); + hCorrSign[iPool] = dynamic_cast(hSeSign[iPool]->Clone(Form("hCorr_Sign_Pool%d", iPool))); hCorrSign[iPool]->Sumw2(); hCorrSign[iPool]->Divide(hMeSign[iPool]); - hCorrSideb[iPool] = reinterpret_cast(hSeSideb[iPool]->Clone(Form("hCorr_Sideb_Pool%d", iPool))); + hCorrSideb[iPool] = dynamic_cast(hSeSideb[iPool]->Clone(Form("hCorr_Sideb_Pool%d", iPool))); hCorrSideb[iPool]->Sumw2(); hCorrSideb[iPool]->Divide(hMeSideb[iPool]); @@ -374,8 +374,8 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t // Pools integration if (iPool == 0) { - h2DSign = reinterpret_cast(hCorrSign[0]->Clone("h2D_Sign")); - h2DSideb = reinterpret_cast(hCorrSideb[0]->Clone("h2D_Sideb")); + h2DSign = dynamic_cast(hCorrSign[0]->Clone("h2D_Sign")); + h2DSideb = dynamic_cast(hCorrSideb[0]->Clone("h2D_Sideb")); h2DSign->Sumw2(); h2DSideb->Sumw2(); } else { @@ -425,7 +425,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t // Bkg subtraction (2D plot) auto* c2DSub = new TCanvas(Form("c2D_Subtr_IntPools_PtHAd%.0fto%.0f", ptHadMin, ptHadMax), Form("c2D_%s_Subtr_IntPools_PtAssoc%.0fto%.0f", fDmesonLabel.Data(), ptHadMin, ptHadMax), 100, 100, 1500, 800); - h2DSubtr = reinterpret_cast(h2DSign->Clone("h2D_Subtr")); + h2DSubtr = dynamic_cast(h2DSign->Clone("h2D_Subtr")); h2DSubtr->Sumw2(); h2DSubtr->Add(h2DSideb, -1); h2DSubtr->SetEntries(h2DSign->GetEntries() - h2DSideb->GetEntries()); @@ -443,7 +443,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t h1DSideb->Scale(1. / h1DSideb->GetXaxis()->GetBinWidth(1)); // Bkg subtraction (1D plot) - h1DSubtr = reinterpret_cast(h1DSign->Clone("h1D_Subtr")); + h1DSubtr = dynamic_cast(h1DSign->Clone("h1D_Subtr")); h1DSubtr->Sumw2(); h1DSubtr->Add(h1DSideb, -1); h1DSubtr->SetEntries(h1DSign->GetEntries() - h1DSideb->GetEntries()); @@ -462,8 +462,8 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t c1D->SaveAs(Form("Output_CorrelationExtraction_%s_Root/h1D_%s_Canvas_PtCand%.0fto%.0f_PoolInt_PtAssoc%.0fto%.0f.root", codeName.Data(), fDmesonLabel.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax)); if (fDebug >= 1) { - h1DSignNorm = reinterpret_cast(h1DSign->Clone("h1D_Sign_Norm")); - h1DSidebNorm = reinterpret_cast(h1DSideb->Clone("h1D_Sideb_Norm")); + h1DSignNorm = dynamic_cast(h1DSign->Clone("h1D_Sign_Norm")); + h1DSidebNorm = dynamic_cast(h1DSideb->Clone("h1D_Sideb_Norm")); h1DSignNorm->Scale(1. / (fSgnYieldNorm + fBkgYield)); // h1D_SidebNorm -> Scale(1./fBkgYield); h1DSidebNorm->Scale(1. / fBkgScaleFactor); @@ -488,7 +488,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t c->SaveAs(Form("Output_CorrelationExtraction_%s_Root/ComparisonSignalSidebCorr_%s_Canvas_PtCand%.0fto%.0f_PoolInt_PtAssoc%.0fto%.0f.root", codeName.Data(), fDmesonLabel.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax)); } // Apply normalization to number of triggers - h1DSubtrNorm = reinterpret_cast(h1DSubtr->Clone("h1D_SubtrNorm")); + h1DSubtrNorm = dynamic_cast(h1DSubtr->Clone("h1D_SubtrNorm")); h1DSubtrNorm->Sumw2(); h1DSubtrNorm->Scale(1. / fSgnYieldNorm); h1DSubtrNorm->SetTitle("Signal region after sideb. subt. corr. - Normalized to # of triggers"); @@ -498,7 +498,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t hModul = evaluateMcClosModulations(ptCandMin, ptCandMax, ptHadMin, ptHadMax); auto* c1DCorrBbias = new TCanvas(Form("c1D_corrBbias_IntPools_PtCand%.0fto%.0f_PtHad%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax), Form("c1D_corrBbias_%s_IntPools_PtAssoc%.0fto%.0f", fDmesonLabel.Data(), ptHadMin, ptHadMax), 100, 100, 1600, 500); c1DCorrBbias->cd(); - hBeforeModulCorr = reinterpret_cast(h1DSubtrNorm->Clone("hBeforeModulCorr")); + hBeforeModulCorr = dynamic_cast(h1DSubtrNorm->Clone("hBeforeModulCorr")); hBeforeModulCorr->SetLineColor(kViolet - 3); hBeforeModulCorr->GetYaxis()->SetRangeUser(0., 5.); hBeforeModulCorr->Draw(); @@ -508,10 +508,10 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t c1DCorrBbias->SaveAs(Form("Output_CorrelationExtraction_%s_Root/ComparisonCorrBiasBtoD_%s_Canvas_PtCand%.0fto%.0f_PoolInt_PtAssoc%.0fto%.0f.root", codeName.Data(), fDmesonLabel.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax)); auto* file = new TFile(Form("Output_CorrelationExtraction_%s_Root/SystematicCorrBiasBtoD_%s_PtCand%.0fto%.0f_PoolInt_PtAssoc%.0fto%.0f.root", codeName.Data(), fDmesonLabel.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax), "RECREATE"); // Open file in write mode - TH1D* h1DSubtrNormClone = reinterpret_cast(h1DSubtrNorm->Clone("h1D_SubtrNorm_Clone")); + TH1D* h1DSubtrNormClone = dynamic_cast(h1DSubtrNorm->Clone("h1D_SubtrNorm_Clone")); h1DSubtrNormClone = reflectCorrHistogram(h1DSubtrNormClone); hBeforeModulCorr = reflectCorrHistogram(hBeforeModulCorr); - TH1D* hSystematicCorrBiasBtoD = reinterpret_cast(h1DSubtrNormClone->Clone("hSystematicCorrBiasBtoD")); + TH1D* hSystematicCorrBiasBtoD = dynamic_cast(h1DSubtrNormClone->Clone("hSystematicCorrBiasBtoD")); hSystematicCorrBiasBtoD->Add(h1DSubtrNormClone, hBeforeModulCorr, 1, -1); // Set bin contents to absolute values for (int i = 1; i <= hSystematicCorrBiasBtoD->GetNbinsX(); ++i) { @@ -533,7 +533,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t h1DAllPartCorr->RebinX(fRebinAxisDeltaPhi); std::cout << "Secondary particle histogram rebinned" << std::endl; } - h1DSecPartFrac = reinterpret_cast(h1DPrimaryPartCorr->Clone(Form("hCorrRatio_PtD%.0fto%.0f_PtHad%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); + h1DSecPartFrac = dynamic_cast(h1DPrimaryPartCorr->Clone(Form("hCorrRatio_PtD%.0fto%.0f_PtHad%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); h1DSecPartFrac->Sumw2(); h1DSecPartFrac->Divide(h1DPrimaryPartCorr, h1DAllPartCorr, 1., 1., "B"); @@ -544,7 +544,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t c1D->SaveAs(Form("Output_CorrelationExtraction_%s_png/CorrPrimaryPartRatio_%s_Canvas_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f.png", codeName.Data(), fDmesonLabel.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax)); c1D->SaveAs(Form("Output_CorrelationExtraction_%s_Root/CorrPrimaryPartRatio_%s_Canvas_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f.root", codeName.Data(), fDmesonLabel.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax)); - h1DSubtrNormSecPart = reinterpret_cast(h1DSubtrNorm->Clone("h1D_SubtrNorm_SecPart")); + h1DSubtrNormSecPart = dynamic_cast(h1DSubtrNorm->Clone("h1D_SubtrNorm_SecPart")); h1DSubtrNormSecPart->Sumw2(); Int_t const nBinsPhi = h1DSubtrNormSecPart->GetNbinsX(); if (nBinsPhi != h1DSecPartFrac->GetNbinsX()) { @@ -562,7 +562,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t h1DFdTemplatePrompt->Scale(1. / h1DFdTemplatePrompt->GetXaxis()->GetBinWidth(1)); h1DFdTemplateNonPrompt->Scale(1. / h1DFdTemplateNonPrompt->GetXaxis()->GetBinWidth(1)); - h1DTemplateTotal = reinterpret_cast(h1DFdTemplatePrompt->Clone("h1D_TemplateTotal")); + h1DTemplateTotal = dynamic_cast(h1DFdTemplatePrompt->Clone("h1D_TemplateTotal")); h1DTemplateTotal->Sumw2(); h1DTemplateTotal->Scale(fdPromptFrac); h1DTemplateTotal->Add(h1DFdTemplateNonPrompt, 1 - fdPromptFrac); @@ -611,16 +611,16 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t std::cout << " " << std::endl; Double_t const baselinediff = baselineData - baselineFd; - TH1D* hBaselineDiff = reinterpret_cast(h1DFdTemplateNonPrompt->Clone("hBaselineDiff")); + TH1D* hBaselineDiff = dynamic_cast(h1DFdTemplateNonPrompt->Clone("hBaselineDiff")); for (int iBin = 0; iBin < hBaselineDiff->GetNbinsX(); iBin++) { hBaselineDiff->SetBinContent(iBin + 1, baselinediff); } h1DFdTemplateNonPrompt->Add(hBaselineDiff); h1DTemplateTotal->Add(hBaselineDiff); if (fSecPartContamination) { - h1DSubtrFdNorm = reinterpret_cast(h1DSubtrNormSecPart->Clone("h1D_SubtrFDNorm")); + h1DSubtrFdNorm = dynamic_cast(h1DSubtrNormSecPart->Clone("h1D_SubtrFDNorm")); } else { - h1DSubtrFdNorm = reinterpret_cast(h1DSubtrNorm->Clone("h1D_SubtrFDNorm")); + h1DSubtrFdNorm = dynamic_cast(h1DSubtrNorm->Clone("h1D_SubtrFDNorm")); } h1DFdTemplateNonPrompt->Scale(1 - fdPromptFrac); h1DSubtrFdNorm->Add(h1DFdTemplateNonPrompt, -1); @@ -667,11 +667,11 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t } if (fFDsubtraction) { - fCorrectedCorrHisto = reinterpret_cast(h1DSubtrFdNorm->Clone(Form("hCorrectedCorr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); + fCorrectedCorrHisto = dynamic_cast(h1DSubtrFdNorm->Clone(Form("hCorrectedCorr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); } else if (fSecPartContamination) { - fCorrectedCorrHisto = reinterpret_cast(h1DSubtrNormSecPart->Clone(Form("hCorrectedCorr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); + fCorrectedCorrHisto = dynamic_cast(h1DSubtrNormSecPart->Clone(Form("hCorrectedCorr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); } else { - fCorrectedCorrHisto = reinterpret_cast(h1DSubtrNorm->Clone(Form("hCorrectedCorr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); + fCorrectedCorrHisto = dynamic_cast(h1DSubtrNorm->Clone(Form("hCorrectedCorr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); } std::cout << "Analysis steps completed - baseline subtraction missing" << std::endl; @@ -715,7 +715,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t // Baseline subtraction Double_t baselineData, baselineDataErr; - TH1D* hBaseline = reinterpret_cast(h1DSubtrNorm->Clone("hBaseline")); + TH1D* hBaseline = dynamic_cast(h1DSubtrNorm->Clone("hBaseline")); hBaseline->Sumw2(); if (fFDsubtraction) { baselineData = calculateBaseline(h1DSubtrFdNorm, kTRUE, kFALSE); // introduced kFALSE @@ -724,7 +724,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t hBaseline->SetBinContent(iBin + 1, baselineData); hBaseline->SetBinError(iBin + 1, baselineDataErr); } - h1DBaselineSubtr = reinterpret_cast(h1DSubtrFdNorm->Clone("h1D_BaselineSubtr")); + h1DBaselineSubtr = dynamic_cast(h1DSubtrFdNorm->Clone("h1D_BaselineSubtr")); h1DBaselineSubtr->Add(hBaseline, -1.); } else if (fSecPartContamination) { baselineData = calculateBaseline(h1DSubtrNormSecPart, kTRUE, kFALSE); @@ -733,7 +733,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t hBaseline->SetBinContent(iBin + 1, baselineData); hBaseline->SetBinError(iBin + 1, baselineDataErr); } - h1DBaselineSubtr = reinterpret_cast(h1DSubtrNormSecPart->Clone("h1D_BaselineSubtr")); + h1DBaselineSubtr = dynamic_cast(h1DSubtrNormSecPart->Clone("h1D_BaselineSubtr")); h1DBaselineSubtr->Add(hBaseline, -1.); } else { baselineData = calculateBaseline(h1DSubtrNorm, kTRUE, kFALSE); @@ -742,11 +742,11 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t hBaseline->SetBinContent(iBin + 1, baselineData); hBaseline->SetBinError(iBin + 1, baselineDataErr); } - h1DBaselineSubtr = reinterpret_cast(h1DSubtrNorm->Clone("h1D_BaselineSubtr")); + h1DBaselineSubtr = dynamic_cast(h1DSubtrNorm->Clone("h1D_BaselineSubtr")); h1DBaselineSubtr->Add(hBaseline, -1.); } - fCorrectedCorrHistoBaselineSubtr = reinterpret_cast(h1DBaselineSubtr->Clone(Form("hCorrectedCorrBaselineSubtr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); + fCorrectedCorrHistoBaselineSubtr = dynamic_cast(h1DBaselineSubtr->Clone(Form("hCorrectedCorrBaselineSubtr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); auto* cFinalBaselineSubtr = new TCanvas(Form("cFinal_BaselineSubtr_%.0fto%.0f", ptHadMin, ptHadMax), Form("cFinal_BaselineSubtr_%s_IntPools_PtAssoc%.0fto%.0f", fDmesonLabel.Data(), ptHadMin, ptHadMax), 100, 100, 1200, 700); h1DBaselineSubtr->SetMarkerColor(kOrange + 8); @@ -795,7 +795,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t cFinalReflected->SaveAs(Form("Output_CorrelationExtraction_%s_Root/AzimCorrDistr_Reflected_%s_Canvas_PtCand%.0fto%.0f_PoolInt_PtAssoc%.0fto%.0f.root", codeName.Data(), fDmesonLabel.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax)); // Reflected histograms baseline subtracted - TH1D* hBaselineRefl = reinterpret_cast(h1DReflCorr->Clone("hBaseline_Refl")); + TH1D* hBaselineRefl = dynamic_cast(h1DReflCorr->Clone("hBaseline_Refl")); hBaselineRefl->Sumw2(); baselineData = calculateBaseline(h1DReflCorr, kFALSE, kTRUE); baselineDataErr = calculateBaselineError(h1DReflCorr, kFALSE, kTRUE); @@ -804,7 +804,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t hBaselineRefl->SetBinContent(iBin + 1, baselineData); hBaselineRefl->SetBinError(iBin + 1, baselineDataErr); } - h1DReflCorrBaselineSubtr = reinterpret_cast(h1DReflCorr->Clone("h1D_ReflCorr_BaselineSubtr")); + h1DReflCorrBaselineSubtr = dynamic_cast(h1DReflCorr->Clone("h1D_ReflCorr_BaselineSubtr")); h1DReflCorrBaselineSubtr->Sumw2(); h1DReflCorrBaselineSubtr->Add(hBaselineRefl, -1.); @@ -832,8 +832,8 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t cFinalReflectedBaselineSubtr->SaveAs(Form("Output_CorrelationExtraction_%s_png/AzimCorrDistr_Reflected_BaselineSubtr_%s_Canvas_PtCand%.0fto%.0f_PoolInt_PtAssoc%.0fto%.0f.png", codeName.Data(), fDmesonLabel.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax)); cFinalReflectedBaselineSubtr->SaveAs(Form("Output_CorrelationExtraction_%s_Root/AzimCorrDistr_Reflected_BaselineSubtr_%s_Canvas_PtCand%.0fto%.0f_PoolInt_PtAssoc%.0fto%.0f.root", codeName.Data(), fDmesonLabel.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax)); - fCorrectedCorrHistoReflected = reinterpret_cast(h1DReflCorr->Clone(Form("hCorrectedCorrReflected_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); - fCorrectedCorrHistoReflectedBaselineSubtr = reinterpret_cast(h1DReflCorrBaselineSubtr->Clone(Form("hCorrectedCorrReflected_BaselineSubtr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); + fCorrectedCorrHistoReflected = dynamic_cast(h1DReflCorr->Clone(Form("hCorrectedCorrReflected_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); + fCorrectedCorrHistoReflectedBaselineSubtr = dynamic_cast(h1DReflCorrBaselineSubtr->Clone(Form("hCorrectedCorrReflected_BaselineSubtr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); return kTRUE; } @@ -853,8 +853,8 @@ Bool_t DhCorrelationExtraction::readInputSeAndMe() return kFALSE; } - fDirSE = reinterpret_cast(fFileSE->Get(fDirNameSE.Data())); - fDirME = reinterpret_cast(fFileME->Get(fDirNameME.Data())); + fDirSE = dynamic_cast(fFileSE->Get(fDirNameSE.Data())); + fDirME = dynamic_cast(fFileME->Get(fDirNameME.Data())); std::cout << "===================== " << std::endl; std::cout << "Read inputs SE and ME" << std::endl; @@ -925,7 +925,7 @@ Bool_t DhCorrelationExtraction::readInputSecondaryPartContamination() return kFALSE; } - fDirSecPart = reinterpret_cast(fFileSecPart->Get(fDirSecPartName.Data())); + fDirSecPart = dynamic_cast(fFileSecPart->Get(fDirSecPartName.Data())); if (fDirSecPart == nullptr) { std::cout << "[ERROR] Directory " << fDirSecPart << " cannot be opened! check your file path!" << std::endl; @@ -957,10 +957,10 @@ TH1D* DhCorrelationExtraction::evaluateMcClosModulations(Double_t ptCandMin, Dou } // TODO: generalise this part - TH1D* hRecPrompt = reinterpret_cast(fFilePromptMc->Get(Form("h1D_Rec_iPtD%d_iPtAssoc%d", fBinPtCand, fBinPtHad))); - TH1D* hRecNonPrompt = reinterpret_cast(fFileNonPromptMc->Get(Form("h1D_Rec_iPtD%d_iPtAssoc%d", fBinPtCand, fBinPtHad))); - TH1D* hGenPrompt = reinterpret_cast(fFilePromptMc->Get(Form("h1D_Gen_iPtD%d_iPtAssoc%d", fBinPtCand, fBinPtHad))); - TH1D* hGenNonPrompt = reinterpret_cast(fFileNonPromptMc->Get(Form("h1D_Gen_iPtD%d_iPtAssoc%d", fBinPtCand, fBinPtHad))); + TH1D* hRecPrompt = dynamic_cast(fFilePromptMc->Get(Form("h1D_Rec_iPtD%d_iPtAssoc%d", fBinPtCand, fBinPtHad))); + TH1D* hRecNonPrompt = dynamic_cast(fFileNonPromptMc->Get(Form("h1D_Rec_iPtD%d_iPtAssoc%d", fBinPtCand, fBinPtHad))); + TH1D* hGenPrompt = dynamic_cast(fFilePromptMc->Get(Form("h1D_Gen_iPtD%d_iPtAssoc%d", fBinPtCand, fBinPtHad))); + TH1D* hGenNonPrompt = dynamic_cast(fFileNonPromptMc->Get(Form("h1D_Gen_iPtD%d_iPtAssoc%d", fBinPtCand, fBinPtHad))); printf("[INFO] Bin cand %d - Bin had %d \n", fBinPtCand, fBinPtHad); @@ -974,10 +974,10 @@ TH1D* DhCorrelationExtraction::evaluateMcClosModulations(Double_t ptCandMin, Dou hGenPrompt->Sumw2(); hGenNonPrompt->Sumw2(); - TH1D* hRatioNonPrompt = reinterpret_cast(hRecNonPrompt->Clone("hRatioNonPrompt")); + TH1D* hRatioNonPrompt = dynamic_cast(hRecNonPrompt->Clone("hRatioNonPrompt")); hRatioNonPrompt->Sumw2(); hRatioNonPrompt->Divide(hRecNonPrompt, hGenNonPrompt, 1., 1., "B"); - hModul = reinterpret_cast(hRatioNonPrompt->Clone("hModul")); + hModul = dynamic_cast(hRatioNonPrompt->Clone("hModul")); TF1* funFit = new TF1("funFit", "[0]", TMath::Pi() * 3. / 8., TMath::Pi() * 3 / 2); hRatioNonPrompt->Fit(funFit, "R"); @@ -1026,51 +1026,51 @@ TH2D* DhCorrelationExtraction::getCorrelHisto(Int_t sEorMe, Int_t sorSb, Int_t p THnSparseD* hSparse = nullptr; if (sEorMe == kSE) { // Same Event if (sorSb == kSign) { - hSparse = reinterpret_cast(fDirSE->Get(fSECorrelSignalRegionName.Data())); + hSparse = dynamic_cast(fDirSE->Get(fSECorrelSignalRegionName.Data())); } else if (!fSidebandDivided) { - hSparse = reinterpret_cast(fDirSE->Get(fSECorrelSidebandsName.Data())); + hSparse = dynamic_cast(fDirSE->Get(fSECorrelSidebandsName.Data())); } else { if (fUseSidebLeft && !fUseSidebRight) { - hSparse = reinterpret_cast(fDirSE->Get(fSECorrelSidebandLeftName.Data())); + hSparse = dynamic_cast(fDirSE->Get(fSECorrelSidebandLeftName.Data())); } else if (!fUseSidebLeft && fUseSidebRight) { - hSparse = reinterpret_cast(fDirSE->Get(fSECorrelSidebandRightName.Data())); + hSparse = dynamic_cast(fDirSE->Get(fSECorrelSidebandRightName.Data())); } else if (fUseSidebLeft && fUseSidebRight) { - hSparse = reinterpret_cast(fDirSE->Get(fSECorrelSidebandLeftName.Data())); + hSparse = dynamic_cast(fDirSE->Get(fSECorrelSidebandLeftName.Data())); hSparse->SetName("hSparse"); - auto* hSparseRightSideb = reinterpret_cast(fDirSE->Get(fSECorrelSidebandRightName.Data())); + auto* hSparseRightSideb = dynamic_cast(fDirSE->Get(fSECorrelSidebandRightName.Data())); hSparse->Add(hSparseRightSideb, 1.); } } } else { // Mixed Event if (sorSb == kSign) { - hSparse = reinterpret_cast(fDirME->Get(fMECorrelSignalRegionName.Data())); + hSparse = dynamic_cast(fDirME->Get(fMECorrelSignalRegionName.Data())); } else if (!fSidebandDivided) { - hSparse = reinterpret_cast(fDirME->Get(fMECorrelSidebandsName.Data())); + hSparse = dynamic_cast(fDirME->Get(fMECorrelSidebandsName.Data())); } else { if (fUseSidebLeft && !fUseSidebRight) { - hSparse = reinterpret_cast(fDirME->Get(fMECorrelSidebandLeftName.Data())); + hSparse = dynamic_cast(fDirME->Get(fMECorrelSidebandLeftName.Data())); } else if (!fUseSidebLeft && fUseSidebRight) { - hSparse = reinterpret_cast(fDirME->Get(fMECorrelSidebandRightName.Data())); + hSparse = dynamic_cast(fDirME->Get(fMECorrelSidebandRightName.Data())); } else if (fUseSidebLeft && fUseSidebRight) { - hSparse = reinterpret_cast(fDirME->Get(fMECorrelSidebandLeftName.Data())); + hSparse = dynamic_cast(fDirME->Get(fMECorrelSidebandLeftName.Data())); hSparse->SetName("hSparse"); - auto* hSparseRightSideb = reinterpret_cast(fDirME->Get(fMECorrelSidebandRightName.Data())); + auto* hSparseRightSideb = dynamic_cast(fDirME->Get(fMECorrelSidebandRightName.Data())); hSparse->Add(hSparseRightSideb, 1.); } } } /*else if (fSidebandDivided) { // Mixed Event - if (SorSB == kSign) { hSparse = reinterpret_cast fDirME -> Get(fMECorrelSignalRegionName.Data()); - } else if (!fSidebandDivided) { hSparse = reinterpret_cast fDirME -> Get(fMECorrelSidebandsName.Data()); + if (SorSB == kSign) { hSparse = dynamic_cast fDirME -> Get(fMECorrelSignalRegionName.Data()); + } else if (!fSidebandDivided) { hSparse = dynamic_cast fDirME -> Get(fMECorrelSidebandsName.Data()); } else { if (fUseSidebLeft && !fUseSidebRight) { - hSparse = reinterpret_cast fDirME -> Get(fMECorrelSidebandLeftName.Data()); + hSparse = dynamic_cast fDirME -> Get(fMECorrelSidebandLeftName.Data()); } else if (!fUseSidebLeft && fUseSidebRight) { - hSparse = reinterpret_cast fDirME -> Get(fMECorrelSidebandRightName.Data()); + hSparse = dynamic_cast fDirME -> Get(fMECorrelSidebandRightName.Data()); } else if (fUseSidebLeft && fUseSidebRight) { - hSparse = reinterpret_cast fDirME -> Get(fMECorrelSidebandLeftName.Data()); + hSparse = dynamic_cast fDirME -> Get(fMECorrelSidebandLeftName.Data()); hSparse -> SetName("hSparse"); - THnSparseD *hSparseRightSideb = reinterpret_cast fDirME -> Get(fMECorrelSidebandRightName.Data()); + THnSparseD *hSparseRightSideb = dynamic_cast fDirME -> Get(fMECorrelSidebandRightName.Data()); hSparse -> Add(hSparseRightSideb, 1.); } } @@ -1140,11 +1140,11 @@ void DhCorrelationExtraction::getSignalAndBackgroundForNorm(Double_t ptCandMin, { // using results obtained from HFInvariantMassFitter.cxx class - TH1F* hMassFitSgnYield = reinterpret_cast(fFileMass->Get(fMassHistoNameSgn.Data())); - TH1F* hMassFitBkgYield = reinterpret_cast(fFileMass->Get(fMassHistoNameBkg.Data())); - TH1F* hMassFitSBsYield = reinterpret_cast(fFileMass->Get(fMassHistoNameSBs.Data())); - TH1F* hMassFitSBLYield = reinterpret_cast(fFileMass->Get("hBackgroundSidebandLeft")); - TH1F* hMassFitSBRYield = reinterpret_cast(fFileMass->Get("hBackgroundSidebandRight")); + TH1F* hMassFitSgnYield = dynamic_cast(fFileMass->Get(fMassHistoNameSgn.Data())); + TH1F* hMassFitBkgYield = dynamic_cast(fFileMass->Get(fMassHistoNameBkg.Data())); + TH1F* hMassFitSBsYield = dynamic_cast(fFileMass->Get(fMassHistoNameSBs.Data())); + TH1F* hMassFitSBLYield = dynamic_cast(fFileMass->Get("hBackgroundSidebandLeft")); + TH1F* hMassFitSBRYield = dynamic_cast(fFileMass->Get("hBackgroundSidebandRight")); Int_t const ptCandBin = hMassFitSgnYield->FindBin(ptCandMin + 0.01); if (ptCandBin != hMassFitSgnYield->FindBin(ptCandMax - 0.01)) { @@ -1188,9 +1188,9 @@ TH2D* DhCorrelationExtraction::getFdTemplateHisto(Int_t promptOrFd, Double_t ptC TH2D* h2D = nullptr; // pointer to be returned if (promptOrFd == kPrompt) { - h2D = reinterpret_cast(fFileFDTemplate->Get(Form("%s%.0f_%.0f_ptassoc%.0f_%.0f", fHistoFDTemplatePromptName.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax))); + h2D = dynamic_cast(fFileFDTemplate->Get(Form("%s%.0f_%.0f_ptassoc%.0f_%.0f", fHistoFDTemplatePromptName.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax))); } else { - h2D = reinterpret_cast(fFileFDTemplate->Get(Form("%s%.0f_%.0f_ptassoc%.0f_%.0f", fHistoFDTemplateNonPromptName.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax))); + h2D = dynamic_cast(fFileFDTemplate->Get(Form("%s%.0f_%.0f_ptassoc%.0f_%.0f", fHistoFDTemplateNonPromptName.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax))); } Int_t binExtEtaMin = h2D->GetXaxis()->FindBin(fDeltaEtaMin + 0.000001); @@ -1222,9 +1222,9 @@ TH1D* DhCorrelationExtraction::getCorrelHistoSecondaryPart(Int_t partType, Doubl THnSparseD* hSparse = nullptr; if (partType == kPrimaryPart) { // primary particles - hSparse = reinterpret_cast(fDirSecPart->Get(fHistoPrimaryPartName.Data())); + hSparse = dynamic_cast(fDirSecPart->Get(fHistoPrimaryPartName.Data())); } else { // all selected particles - hSparse = reinterpret_cast(fDirSecPart->Get(fHistoAllPartName.Data())); + hSparse = dynamic_cast(fDirSecPart->Get(fHistoAllPartName.Data())); } Int_t const binExtPtCandMin = hSparse->GetAxis(2)->FindBin(ptCandMin + 0.01); // axis2: ptCand, the 0.01 to avoid bin edges! Int_t const binExtPtCandMax = hSparse->GetAxis(2)->FindBin(ptCandMax - 0.01); @@ -1274,7 +1274,7 @@ TH1D* DhCorrelationExtraction::reflectCorrHistogram(TH1D*& histo) TH1D* h1D = new TH1D("h1D_Reflected", "", nBinsPhiRefl, 0., TMath::Pi()); // pointer to be returned h1D->Sumw2(); - // TH1D* h1D = reinterpret_cast histo -> Clone("h1D_Reflected"); + // TH1D* h1D = dynamic_cast histo -> Clone("h1D_Reflected"); // h1D -> GetXaxis() -> SetRange(bin0Phi, binPiPhi); // reflection @@ -1331,7 +1331,7 @@ TH1D* DhCorrelationExtraction::reflectHistoRun2(TH1D* h, Double_t scale) Double_t DhCorrelationExtraction::getFdPromptFrac(Double_t ptCandMin, Double_t ptCandMax, Double_t /*ptHadMin*/, Double_t /*ptHadMax*/) { - TH1D* h1D = reinterpret_cast(fFileFDPromptFrac->Get(fHistoFDPromptFracName.Data())); + TH1D* h1D = dynamic_cast(fFileFDPromptFrac->Get(fHistoFDPromptFracName.Data())); Int_t const binPtCandMin = h1D->GetXaxis()->FindBin(ptCandMin + 0.01); Int_t const binPtCandMax = h1D->GetXaxis()->FindBin(ptCandMax - 0.01); diff --git a/PWGHF/HFC/Macros/FitCorrel.C b/PWGHF/HFC/Macros/FitCorrel.C index a90d502e58f..328227900ee 100644 --- a/PWGHF/HFC/Macros/FitCorrel.C +++ b/PWGHF/HFC/Macros/FitCorrel.C @@ -110,10 +110,7 @@ void fitCorrelDs(const TString& cfgFileName = "config_CorrAnalysis.json") const int nBinsPtCand = binsPtCandIntervalsVec.size() - 1; const int nBinsPtHad = binsPtHadIntervals.size() - 1; - double binsPtCandIntervals[nBinsPtCand + 1]; - for (int i = 0; i < nBinsPtCand + 1; i++) { - binsPtCandIntervals[i] = binsPtCandIntervalsVec[i]; - } + std::vector binsPtCandIntervals{binsPtCandIntervalsVec}; const Value& fitFuncValue = config["FitFunction"]; readArray(fitFuncValue, fitFunc); @@ -122,17 +119,14 @@ void fitCorrelDs(const TString& cfgFileName = "config_CorrAnalysis.json") int const fixMean = config["FixMean"].GetInt(); int const nBaselinePoints = config["nBaselinePoints"].GetInt(); - vector pointsForBaselineVec; + std::vector pointsForBaselineVec; const Value& pointsForBaselineValue = config["binsForBaseline"]; readArray(pointsForBaselineValue, pointsForBaselineVec); if (pointsForBaselineVec.size() != nBaselinePoints) { cout << "ERROR: size of the vector pointsForBaseline is different from the number of nBaselinePoints" << endl; return; } - int pointsForBaseline[nBaselinePoints]; - for (int i = 0; i < nBaselinePoints; i++) { - pointsForBaseline[i] = pointsForBaselineVec[i]; - } + std::vector pointsForBaseline{pointsForBaselineVec}; std::cout << "=========================== " << std::endl; std::cout << "Input variables from config" << std::endl; @@ -193,16 +187,16 @@ void fitCorrelDs(const TString& cfgFileName = "config_CorrAnalysis.json") for (int iBinPtCand = 0; iBinPtCand < nBinsPtCand; iBinPtCand++) { for (int iBinPtHad = 0; iBinPtHad < nBinsPtHad; iBinPtHad++) { if (isReflected) { - hCorrPhi[iBinPtCand][iBinPtHad] = reinterpret_cast(inFile->Get(Form("hCorrectedCorrReflected_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", binsPtCandIntervals[iBinPtCand], binsPtCandIntervals[iBinPtCand + 1], binsPtHadIntervals[iBinPtHad], binsPtHadIntervals[iBinPtHad + 1]))); + hCorrPhi[iBinPtCand][iBinPtHad] = dynamic_cast(inFile->Get(Form("hCorrectedCorrReflected_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", binsPtCandIntervals[iBinPtCand], binsPtCandIntervals[iBinPtCand + 1], binsPtHadIntervals[iBinPtHad], binsPtHadIntervals[iBinPtHad + 1]))); } else { - hCorrPhi[iBinPtCand][iBinPtHad] = reinterpret_cast(inFile->Get(Form("hCorrectedCorr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", binsPtCandIntervals[iBinPtCand], binsPtCandIntervals[iBinPtCand + 1], binsPtHadIntervals[iBinPtHad], binsPtHadIntervals[iBinPtHad + 1]))); + hCorrPhi[iBinPtCand][iBinPtHad] = dynamic_cast(inFile->Get(Form("hCorrectedCorr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", binsPtCandIntervals[iBinPtCand], binsPtCandIntervals[iBinPtCand + 1], binsPtHadIntervals[iBinPtHad], binsPtHadIntervals[iBinPtHad + 1]))); } - corrFitter[iBinPtHad][iBinPtCand] = new DhCorrelationFitter(reinterpret_cast(hCorrPhi[iBinPtCand][iBinPtHad]), fMin, fMax); + corrFitter[iBinPtHad][iBinPtCand] = new DhCorrelationFitter(dynamic_cast(hCorrPhi[iBinPtCand][iBinPtHad]), fMin, fMax); // FIXME: invalid cast corrFitter[iBinPtHad][iBinPtCand]->setHistoIsReflected(refl); corrFitter[iBinPtHad][iBinPtCand]->setFixBaseline(fixBase); corrFitter[iBinPtHad][iBinPtCand]->setBaselineUpOrDown(shiftBaseUp, shiftBaseDown); - corrFitter[iBinPtHad][iBinPtCand]->setPointsForBaseline(nBaselinePoints, pointsForBaseline); + corrFitter[iBinPtHad][iBinPtCand]->setPointsForBaseline(nBaselinePoints, pointsForBaseline.data()); corrFitter[iBinPtHad][iBinPtCand]->setv2(v2AssocPart[iBinPtCand], v2Dmeson[iBinPtCand]); corrFitter[iBinPtHad][iBinPtCand]->setReflectedCorrHisto(isReflected); @@ -222,14 +216,14 @@ void fitCorrelDs(const TString& cfgFileName = "config_CorrAnalysis.json") canvasCorrPhi[iBinPtHad]->Divide(3, 2); } // histograms with fir parameters - hBaselin[iBinPtHad] = new TH1D(Form("hBaselin_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals); - hNSYield[iBinPtHad] = new TH1D(Form("hNSYield_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals); - hNSSigma[iBinPtHad] = new TH1D(Form("hNSSigma_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals); - hASYield[iBinPtHad] = new TH1D(Form("hASYield_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals); - hASSigma[iBinPtHad] = new TH1D(Form("hASSigma_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals); - hBeta[iBinPtHad] = new TH1D(Form("hBeta_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals); - hNSYieldBinCount[iBinPtHad] = new TH1D(Form("hNSYieldBinCount_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals); - hASYieldBinCount[iBinPtHad] = new TH1D(Form("hASYieldBinCount_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals); + hBaselin[iBinPtHad] = new TH1D(Form("hBaselin_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals.data()); + hNSYield[iBinPtHad] = new TH1D(Form("hNSYield_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals.data()); + hNSSigma[iBinPtHad] = new TH1D(Form("hNSSigma_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals.data()); + hASYield[iBinPtHad] = new TH1D(Form("hASYield_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals.data()); + hASSigma[iBinPtHad] = new TH1D(Form("hASSigma_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals.data()); + hBeta[iBinPtHad] = new TH1D(Form("hBeta_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals.data()); + hNSYieldBinCount[iBinPtHad] = new TH1D(Form("hNSYieldBinCount_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals.data()); + hASYieldBinCount[iBinPtHad] = new TH1D(Form("hASYieldBinCount_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals.data()); for (int iBinPtCand = 0; iBinPtCand < nBinsPtCand; iBinPtCand++) { setTH1HistoStyle(hCorrPhi[iBinPtCand][iBinPtHad], "", "#Delta#phi [rad]", "#frac{1}{N_{D_{s}}}#frac{dN^{assoc}}{d#Delta#phi} [rad^{-1}]", kFullCircle, kRed + 1, 1.4, kRed + 1, 3); @@ -244,8 +238,8 @@ void fitCorrelDs(const TString& cfgFileName = "config_CorrAnalysis.json") // draw systematic errors int const nBinsPhi = hCorrPhi[iBinPtCand][iBinPtHad]->GetNbinsX(); if (drawSystematicErrors) { - hSystematicErrors[iBinPtCand][iBinPtHad] = reinterpret_cast(inFileSystematicErrors->Get(Form("hSystematicErrorsMerged_PtBin%d_PtBinAssoc%d", iBinPtCand + 1, iBinPtHad + 1))); - hSystematicErrorsPlot[iBinPtCand][iBinPtHad] = reinterpret_cast(hCorrPhi[iBinPtCand][iBinPtHad]->Clone(Form("hSystematicErrorsPlot_PtBin%d_PtBinAssoc%d", iBinPtCand + 1, iBinPtHad + 1))); + hSystematicErrors[iBinPtCand][iBinPtHad] = dynamic_cast(inFileSystematicErrors->Get(Form("hSystematicErrorsMerged_PtBin%d_PtBinAssoc%d", iBinPtCand + 1, iBinPtHad + 1))); + hSystematicErrorsPlot[iBinPtCand][iBinPtHad] = dynamic_cast(hCorrPhi[iBinPtCand][iBinPtHad]->Clone(Form("hSystematicErrorsPlot_PtBin%d_PtBinAssoc%d", iBinPtCand + 1, iBinPtHad + 1))); for (int iPhi = 0; iPhi < nBinsPhi; iPhi++) { if (sameSystematics) { hSystematicErrorsPlot[iBinPtCand][iBinPtHad]->SetBinError(iPhi + 1, hSystematicErrors[iBinPtCand][iBinPtHad]->GetBinContent(1) * hCorrPhi[iBinPtCand][iBinPtHad]->GetBinContent(iPhi + 1)); @@ -265,13 +259,13 @@ void fitCorrelDs(const TString& cfgFileName = "config_CorrAnalysis.json") corrFitter[iBinPtHad][iBinPtCand]->setFuncType(static_cast(fitFunc[iBinPtCand])); corrFitter[iBinPtHad][iBinPtCand]->fitting(kTRUE, kTRUE); // the first term is for drawing the fit functions, the second argument is useExternalParams - TF1* fFit = corrFitter[iBinPtHad][iBinPtCand]->getFitFunction(); + // TF1* fFit = corrFitter[iBinPtHad][iBinPtCand]->getFitFunction(); // FIXME: unused // Title of the histogram auto* pttext = new TPaveText(0.15, 0.9, 0.85, 0.95, "NDC"); pttext->SetFillStyle(0); pttext->SetBorderSize(0); - TText* tpT = pttext->AddText(0., 0.8, Form("%.0f < p_{T}^{D_{s}} < %.0f GeV/c, p_{T}^{assoc} > %.1f GeV/c", binsPtCandIntervals[iBinPtCand], binsPtCandIntervals[iBinPtCand + 1], binsPtHadIntervals[iBinPtHad])); + pttext->AddText(0., 0.8, Form("%.0f < p_{T}^{D_{s}} < %.0f GeV/c, p_{T}^{assoc} > %.1f GeV/c", binsPtCandIntervals[iBinPtCand], binsPtCandIntervals[iBinPtCand + 1], binsPtHadIntervals[iBinPtHad])); // pttext -> Draw("same"); // Fill the histograms with the fit parameters @@ -397,11 +391,11 @@ void fitCorrelDs(const TString& cfgFileName = "config_CorrAnalysis.json") hBaselin[iBinPtHad]->Draw(); if (drawSystematicErrors) { - TH1F* hBaselinSyst = reinterpret_cast(inFileFitSystematicErrors->Get(Form("hSystematicErrorsBaselinMerged_PtBinAssoc%d", iBinPtHad + 1))); - TH1F* hNSYieldSyst = reinterpret_cast(inFileFitSystematicErrors->Get(Form("hSystematicErrorsNSYieldMerged_PtBinAssoc%d", iBinPtHad + 1))); - TH1F* hNSSigmaSyst = reinterpret_cast(inFileFitSystematicErrors->Get(Form("hSystematicErrorsNSSigmaMerged_PtBinAssoc%d", iBinPtHad + 1))); - TH1F* hASYieldSyst = reinterpret_cast(inFileFitSystematicErrors->Get(Form("hSystematicErrorsASYieldMerged_PtBinAssoc%d", iBinPtHad + 1))); - TH1F* hASSigmaSyst = reinterpret_cast(inFileFitSystematicErrors->Get(Form("hSystematicErrorsASSigmaMerged_PtBinAssoc%d", iBinPtHad + 1))); + TH1F* hBaselinSyst = dynamic_cast(inFileFitSystematicErrors->Get(Form("hSystematicErrorsBaselinMerged_PtBinAssoc%d", iBinPtHad + 1))); + TH1F* hNSYieldSyst = dynamic_cast(inFileFitSystematicErrors->Get(Form("hSystematicErrorsNSYieldMerged_PtBinAssoc%d", iBinPtHad + 1))); + TH1F* hNSSigmaSyst = dynamic_cast(inFileFitSystematicErrors->Get(Form("hSystematicErrorsNSSigmaMerged_PtBinAssoc%d", iBinPtHad + 1))); + TH1F* hASYieldSyst = dynamic_cast(inFileFitSystematicErrors->Get(Form("hSystematicErrorsASYieldMerged_PtBinAssoc%d", iBinPtHad + 1))); + TH1F* hASSigmaSyst = dynamic_cast(inFileFitSystematicErrors->Get(Form("hSystematicErrorsASSigmaMerged_PtBinAssoc%d", iBinPtHad + 1))); for (int iBinPtCand = 0; iBinPtCand < nBinsPtCand; iBinPtCand++) { hBaselinSyst->SetBinError(iBinPtCand + 1, hBaselinSyst->GetBinContent(iBinPtCand + 1) * hBaselin[iBinPtHad]->GetBinContent(iBinPtCand + 1)); diff --git a/PWGHF/HFC/TableProducer/CMakeLists.txt b/PWGHF/HFC/TableProducer/CMakeLists.txt index 299981d8c1f..87d3e12ea40 100644 --- a/PWGHF/HFC/TableProducer/CMakeLists.txt +++ b/PWGHF/HFC/TableProducer/CMakeLists.txt @@ -11,46 +11,55 @@ o2physics_add_dpl_workflow(correlator-d0-d0bar SOURCES correlatorD0D0bar.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(correlator-d0-d0bar-barrel-full-pid SOURCES correlatorD0D0barBarrelFullPid.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(correlator-d0-hadrons SOURCES correlatorD0Hadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(correlator-d-meson-pairs SOURCES correlatorDMesonPairs.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(correlator-dplus-dminus SOURCES correlatorDplusDminus.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(correlator-dplus-dplus-reduced SOURCES correlatorDplusDplusReduced.cxx - PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils + REUSE_FROM HfCandidatesPCH + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(correlator-dplus-hadrons SOURCES correlatorDplusHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(correlator-ds-hadrons SOURCES correlatorDsHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derived-data-creator-correlations-reduced SOURCES derivedDataCreatorCorrelationsReduced.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) @@ -66,6 +75,7 @@ o2physics_add_dpl_workflow(correlator-ds-hadrons-reduced o2physics_add_dpl_workflow(correlator-dstar-hadrons SOURCES correlatorDstarHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -76,30 +86,36 @@ o2physics_add_dpl_workflow(correlator-hfe-hadrons o2physics_add_dpl_workflow(correlator-lc-hadrons SOURCES correlatorLcHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(correlator-lc-sc-hadrons SOURCES correlatorLcScHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(correlator-xic-hadrons SOURCES correlatorXicHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(producer-charm-hadrons-track-femto-dream SOURCES producerCharmHadronsTrackFemtoDream.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(producer-charm-hadrons-v0-femto-dream SOURCES producerCharmHadronsV0FemtoDream.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(producer-charm-hadrons-charm-femto-dream SOURCES producerCharmHadronsCharmFemtoDream.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) diff --git a/PWGHF/HFC/TableProducer/correlatorD0D0bar.cxx b/PWGHF/HFC/TableProducer/correlatorD0D0bar.cxx index dbef6c1ee60..ba37e499c1e 100644 --- a/PWGHF/HFC/TableProducer/correlatorD0D0bar.cxx +++ b/PWGHF/HFC/TableProducer/correlatorD0D0bar.cxx @@ -135,7 +135,7 @@ struct HfCorrelatorD0D0bar { void init(InitContext&) { - auto vbins = (std::vector)binsPt; + auto vbins = std::vector(binsPt); registry.add("hMass", "D0,D0bar candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassD0", "D0,D0bar candidates;inv. mass D0 only (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassD0bar", "D0,D0bar candidates;inv. mass D0bar only (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); diff --git a/PWGHF/HFC/TableProducer/correlatorD0D0barBarrelFullPid.cxx b/PWGHF/HFC/TableProducer/correlatorD0D0barBarrelFullPid.cxx index ff4bdbba50e..3f1289e4413 100644 --- a/PWGHF/HFC/TableProducer/correlatorD0D0barBarrelFullPid.cxx +++ b/PWGHF/HFC/TableProducer/correlatorD0D0barBarrelFullPid.cxx @@ -136,7 +136,7 @@ struct HfCorrelatorD0D0barBarrelFullPid { void init(InitContext&) { - auto vbins = (std::vector)binsPt; + auto vbins = std::vector(binsPt); registry.add("hMass", "D0,D0bar candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassD0", "D0,D0bar candidates;inv. mass D0 only (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassD0bar", "D0,D0bar candidates;inv. mass D0bar only (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); diff --git a/PWGHF/HFC/TableProducer/correlatorD0Hadrons.cxx b/PWGHF/HFC/TableProducer/correlatorD0Hadrons.cxx index 496df5d5b51..cd7edd8130a 100644 --- a/PWGHF/HFC/TableProducer/correlatorD0Hadrons.cxx +++ b/PWGHF/HFC/TableProducer/correlatorD0Hadrons.cxx @@ -239,6 +239,7 @@ struct HfCorrelatorD0Hadrons { Configurable storeAutoCorrelationFlag{"storeAutoCorrelationFlag", false, "Store flag that indicates if the track is paired to its D-meson mother instead of skipping it"}; Configurable numberEventsMixed{"numberEventsMixed", 5, "Number of events mixed in ME process"}; Configurable useCentrality{"useCentrality", false, "Flag for centrality dependent analyses"}; + Configurable enableCentralityQA{"enableCentralityQA", false, "Enable FT0M centrality vs multiplicity QA in processData"}; int leadingIndex = 0; double softPiMass = 0.14543; // pion mass + Q-value of the D*->D0pi decay @@ -249,7 +250,7 @@ struct HfCorrelatorD0Hadrons { Filter trackFilter = requireGlobalTrackWoDCAInFilter() && (nabs(aod::track::eta) < etaTrackMax) && (aod::track::pt > ptTrackMin) && (aod::track::pt < ptTrackMax) && (nabs(aod::track::dcaXY) < dcaXYTrackMax) && (nabs(aod::track::dcaZ) < dcaZTrackMax); Filter d0Filter = (aod::hf_sel_candidate_d0::isSelD0 >= 1) || (aod::hf_sel_candidate_d0::isSelD0bar >= 1); Filter collisionFilterGen = aod::hf_selection_dmeson_collision::dmesonSel == true; - Filter particlesFilter = nabs(aod::mcparticle::pdgCode) == static_cast(Pdg::kD0) || ((aod::mcparticle::flags & (uint8_t)o2::aod::mcparticle::enums::PhysicalPrimary) == (uint8_t)o2::aod::mcparticle::enums::PhysicalPrimary); + Filter particlesFilter = nabs(aod::mcparticle::pdgCode) == static_cast(Pdg::kD0) || ((aod::mcparticle::flags & static_cast(o2::aod::mcparticle::enums::PhysicalPrimary)) == static_cast(o2::aod::mcparticle::enums::PhysicalPrimary)); Preslice perCol = aod::hf_cand::collisionId; Preslice perCollisionID = aod::track::collisionId; @@ -278,8 +279,8 @@ struct HfCorrelatorD0Hadrons { AxisSpec const axisEta = {binsEta, "#it{#eta}"}; AxisSpec const axisPhi = {binsPhi, "#it{#varphi}"}; AxisSpec const axisRapidity = {100, -5., 5., "Rapidity"}; - AxisSpec axisPtD = {(std::vector)binsPtD, "#it{p}_{T} (GeV/#it{c})"}; - AxisSpec axisPtHadron = {(std::vector)binsPtHadron, "#it{p}_{T} Hadron (GeV/#it{c})"}; + AxisSpec axisPtD = {std::vector(binsPtD), "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec axisPtHadron = {std::vector(binsPtHadron), "#it{p}_{T} Hadron (GeV/#it{c})"}; AxisSpec const axisMultiplicity = {binsMultiplicity, "Multiplicity"}; AxisSpec axisMultFT0M = {binsMultFT0M, "MultiplicityFT0M"}; AxisSpec const axisPosZ = {binsPosZ, "PosZ"}; @@ -366,6 +367,9 @@ struct HfCorrelatorD0Hadrons { registry.get(HIST("hTrackCounter"))->GetXaxis()->SetBinLabel(5, "fake tracks"); registry.add("hZvtx", "z vertex", {HistType::kTH1F, {axisPosZ}}); registry.add("hMultFT0M", "Multiplicity FT0M", {HistType::kTH1F, {axisMultFT0M}}); + if (enableCentralityQA) { + registry.add("hCentFT0MVsMultFT0M", "Centrality FT0M vs multiplicity FT0M", {HistType::kTH2D, {axisMultFT0M, axisCent}}); + } registry.add("hCollisionPoolBin", "collision pool bin", {HistType::kTH1F, {axisPoolBin}}); registry.add("hD0PoolBin", "D0 selected in pool Bin", {HistType::kTH1F, {axisPoolBin}}); registry.add("hTracksPoolBin", "Particles associated pool bin", {HistType::kTH1F, {axisPoolBin}}); @@ -379,7 +383,7 @@ struct HfCorrelatorD0Hadrons { SelectedCandidatesDataMl const& candidates, aod::BCsWithTimestamps const&) { - BinningType const corrBinning{{zPoolBins, multPoolBins}, true}; + BinningType const corrBinning{{zPoolBins, multPoolBins}}; auto bc = collision.bc_as(); int gCollisionId = collision.globalIndex(); @@ -398,6 +402,9 @@ struct HfCorrelatorD0Hadrons { registry.fill(HIST("hCollisionPoolBin"), poolBin); registry.fill(HIST("hZvtx"), collision.posZ()); registry.fill(HIST("hMultFT0M"), collision.multFT0M()); + if (enableCentralityQA) { + registry.fill(HIST("hCentFT0MVsMultFT0M"), collision.multFT0M(), collision.centFT0M()); + } int nTracks = 0; if (collision.numContrib() > 1) { @@ -598,7 +605,7 @@ struct HfCorrelatorD0Hadrons { aod::McParticles const& mcParticles, aod::BCsWithTimestamps const&) { - BinningType const corrBinning{{zPoolBins, multPoolBins}, true}; + BinningType const corrBinning{{zPoolBins, multPoolBins}}; auto bc = collision.bc_as(); int gCollisionId = collision.globalIndex(); @@ -928,7 +935,7 @@ struct HfCorrelatorD0Hadrons { void processMcGen(SelectedCollisionsMcGen::iterator const& mcCollision, SelectedParticlesMcGen const& mcParticles) { - BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBinsMcGen}, true}; + BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBinsMcGen}}; int poolBin = corrBinningMcGen.getBin(std::make_tuple(mcCollision.posZ(), mcCollision.multMCFT0A())); int gCollisionId = mcCollision.globalIndex(); int64_t timeStamp = 0; @@ -1095,7 +1102,7 @@ struct HfCorrelatorD0Hadrons { SelectedCandidatesDataMl const& candidates, SelectedTracks const& tracks) { - BinningType const corrBinning{{zPoolBins, multPoolBins}, true}; + BinningType const corrBinning{{zPoolBins, multPoolBins}}; for (const auto& collision : collisions) { registry.fill(HIST("hMultFT0M"), collision.multFT0M()); registry.fill(HIST("hZvtx"), collision.posZ()); @@ -1184,7 +1191,7 @@ struct HfCorrelatorD0Hadrons { SelectedTracksMcRec const& tracks, aod::McParticles const& mcParticles) { - BinningType const corrBinning{{zPoolBins, multPoolBins}, true}; + BinningType const corrBinning{{zPoolBins, multPoolBins}}; auto tracksTuple = std::make_tuple(candidates, tracks); Pair const pairMcRec{corrBinning, numberEventsMixed, -1, collisions, tracksTuple, &cache}; bool isD0Prompt = false; @@ -1319,7 +1326,7 @@ struct HfCorrelatorD0Hadrons { void processMcGenMixedEvent(SelectedCollisionsMcGen const& collisions, SelectedParticlesMcGen const& mcParticles) { - BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBinsMcGen}, true}; + BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBinsMcGen}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair const pairMcGen{corrBinningMcGen, numberEventsMixed, -1, collisions, tracksTuple, &cache}; diff --git a/PWGHF/HFC/TableProducer/correlatorDMesonPairs.cxx b/PWGHF/HFC/TableProducer/correlatorDMesonPairs.cxx index 19130525213..b6d40c9e764 100644 --- a/PWGHF/HFC/TableProducer/correlatorDMesonPairs.cxx +++ b/PWGHF/HFC/TableProducer/correlatorDMesonPairs.cxx @@ -21,7 +21,6 @@ #include "PWGHF/DataModel/AliasTables.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" -#include "PWGHF/DataModel/TrackIndexSkimmingTables.h" #include "PWGHF/HFC/DataModel/CorrelationTables.h" #include "PWGHF/HFC/DataModel/DMesonPairsTables.h" @@ -41,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -56,6 +56,8 @@ #include #include #include +#include +#include #include using namespace o2; @@ -66,6 +68,8 @@ using namespace o2::framework::expressions; namespace { +using DecayChannel = o2::hf_decay::hf_cand_2prong::DecayChannelMain; + enum CandidateType { SelectedD = 0, // This particle is selected as a D SelectedDbar, // This particle is selected as a Dbar @@ -102,25 +106,21 @@ struct HfCorrelatorD0HadronsSelection { using CandidatesD0Data = soa::Join; Partition selectedD0Candidates = - aod::hf_sel_candidate_d0::isSelD0 >= 1 || - aod::hf_sel_candidate_d0::isSelD0bar >= 1; + aod::hf_sel_candidate_d0::isSelD0 >= selectionFlagD0 || + aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlagD0bar; // Process function to select collisions with at least one D0 candidate passing the selection criteria void processD0SelectionData(SelCollisions::iterator const& collision, CandidatesD0Data const&) { - bool isSel8 = !useSel8 || collision.sel8(); - bool isNoSameBunchPileUp = !selNoSameBunchPileUpColl || - collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup); + bool const isSel8 = !useSel8 || collision.sel8(); + bool const isNoSameBunchPileUp = !selNoSameBunchPileUpColl || + collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup); bool isD0Found = !doSelD0Collision; if (doSelD0Collision) { auto grouped = selectedD0Candidates.sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); for (const auto& candidate : grouped) { - - if (candidate.isSelD0() < selectionFlagD0 && candidate.isSelD0bar() < selectionFlagD0bar) { - continue; - } if (std::abs(HfHelper::yD0(candidate)) > yCandMax || candidate.pt() < ptCandMin) { continue; } @@ -145,11 +145,9 @@ struct HfCorrelatorDMesonPairs { // Tables for event mixing Produces entryDMesonInfoCand1; Produces entryDMesonInfoCand2; - Produces entryAssocHad; Configurable selectionFlagD0{"selectionFlagD0", 1, "Selection Flag for D0"}; Configurable selectionFlagD0bar{"selectionFlagD0bar", 1, "Selection Flag for D0bar"}; - Configurable selectionFlagHf{"selectionFlagHf", 1, "Selection Flag for HF flagged candidates"}; Configurable yCandMax{"yCandMax", 0.8, "maximum |y| of D0 candidates"}; Configurable ptCandMin{"ptCandMin", -1., "minimum pT of D0 candidates"}; Configurable> binsPt{"binsPt", std::vector{o2::analysis::hf_cuts_d0_to_pi_k::vecBinsPt}, "pT bin limits for candidate mass plots"}; @@ -157,18 +155,7 @@ struct HfCorrelatorDMesonPairs { Configurable massCut{"massCut", 0.05, "Maximum deviation from PDG peak allowed for signal region"}; Configurable removeAmbiguous{"removeAmbiguous", false, "Flag to remove ambiguous candidates"}; Configurable ptMaxRemoveAmbiguous{"ptMaxRemoveAmbiguous", 5.0, "Max. pT to remove the ambiguous candidates"}; - - // Event mixing configurables Configurable applyMixedEvent{"applyMixedEvent", false, "Flag to make the event mixing"}; - Configurable daughterTracksCutFlag{"daughterTracksCutFlag", false, "Flag to add cut on daughter tracks"}; - Configurable> binsPtHadron{"binsPtHadron", std::vector{0.3, 2., 4., 8., 12., 50.}, "pT bin limits for assoc particle"}; - Configurable etaTrackMax{"etaTrackMax", 0.8, "max. eta of tracks"}; - Configurable dcaXYTrackMax{"dcaXYTrackMax", 1., "max. DCA_xy of tracks"}; - Configurable dcaZTrackMax{"dcaZTrackMax", 1., "max. DCA_z of tracks"}; - Configurable ptTrackMin{"ptTrackMin", 0.3, "min. track pT"}; - Configurable ptTrackMax{"ptTrackMax", 100., "max. track pT"}; - Configurable multMin{"multMin", 0., "minimum multiplicity accepted"}; - Configurable multMax{"multMax", 10000., "maximum multiplicity accepted"}; // ML inference Configurable applyMl{"applyMl", false, "Flag to apply ML selections"}; @@ -187,19 +174,16 @@ struct HfCorrelatorDMesonPairs { SliceCache cache; + // Table aliases. The partitions below must have exactly the same type as the + // candidate table taken by the process functions, otherwise they are never bound. using SelCollisionsWithD0 = soa::Filtered>; - using CandidatesD0Data = soa::Join; - - using FilteredD0Candidates = soa::Filtered; - using TracksData = soa::Filtered>; // trackFilter applied + using CandidatesD0Data = soa::Join; + using CandidatesD0McRec = soa::Join; Filter collisionFilter = aod::hf_selection_dmeson_collision::dmesonSel == true; - Filter d0Filter = (o2::aod::hf_track_index::hfflag & static_cast(BIT(aod::hf_cand_2prong::DecayType::D0ToPiK))) != static_cast(0); - Filter trackFilter = (nabs(aod::track::eta) < etaTrackMax) && (nabs(aod::track::pt) > ptTrackMin) && (nabs(aod::track::dcaXY) < dcaXYTrackMax) && (nabs(aod::track::dcaZ) < dcaZTrackMax); - Preslice perCol2Prong = aod::hf_cand::collisionId; - Preslice candsD0PerCollision = aod::hf_cand::collisionId; - Preslice trackIndicesPerCollision = aod::track::collisionId; + Partition selectedD0Candidates = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlagD0 || aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlagD0bar; + Partition selectedD0CandidatesMc = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlagD0 || aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlagD0bar; o2::analysis::HfMlResponseD0ToKPi hfMlResponse; o2::ccdb::CcdbApi ccdbApi; @@ -210,11 +194,6 @@ struct HfCorrelatorDMesonPairs { std::vector outputMlD0Cand2; std::vector outputMlD0barCand2; - // using TracksWPid = soa::Join; - - Partition> selectedD0Candidates = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlagD0 || aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlagD0bar; - Partition> selectedD0CandidatesMc = aod::hf_sel_candidate_d0::isRecoHfFlag >= selectionFlagHf; - // ThnSparse for ML outputScores and Vars ConfigurableAxis thnConfigAxisBkgScore{"thnConfigAxisBkgScore", {100, 0, 1}, "Bkg score bins"}; ConfigurableAxis thnConfigAxisSignalScore{"thnConfigAxisSignalScore", {100, 0, 1}, "Signal score bins"}; @@ -235,8 +214,6 @@ struct HfCorrelatorDMesonPairs { ConfigurableAxis binsMultiplicity{"binsMultiplicity", {VARIABLE_WIDTH, 0.0f, 2000.0f, 6000.0f, 100000.0f}, "Mixing bins - multiplicity"}; ConfigurableAxis binsZVtx{"binsZVtx", {VARIABLE_WIDTH, -10.0f, -2.5f, 2.5f, 10.0f}, "Mixing bins - z-vertex"}; ConfigurableAxis binsPoolBin{"binsPoolBin", {9, 0., 9.}, "PoolBin"}; - ConfigurableAxis binsMultFT0M{"binsMultFT0M", {600, 0., 6000.}, "Multiplicity as FT0M signal amplitude"}; - ConfigurableAxis binsDcaXY{"binsDcaXY", {128, -0.2, 0.2}, "DCA xy"}; std::unique_ptr corrBinning; HistogramRegistry registry{ @@ -256,7 +233,7 @@ struct HfCorrelatorDMesonPairs { {"hPhiMcGen", "D meson candidates MC Gen;candidate #it{#varphi};entries", hTH1Phi}, {"hPtVsYVsNContribMcGen", "D meson candidates MC Gen;candidate #it{p}_{T} (GeV/#it{c});#it{y};Number of contributors", hTH3PtVsYVsNContrib}, {"hPtVsYVsNContribMcGenPrompt", "D meson candidates MC Gen Prompt;candidate #it{p}_{T} (GeV/#it{c});#it{y};Number of contributors", hTH3PtVsYVsNContrib}, - {"hPtVsYVsNContribMcGenNonPrompt", "D meson candidates MC Gen Prompt;candidate #it{p}_{T} (GeV/#it{c});#it{y};Number of contributors", hTH3PtVsYVsNContrib}, + {"hPtVsYVsNContribMcGenNonPrompt", "D meson candidates MC Gen Non-prompt;candidate #it{p}_{T} (GeV/#it{c});#it{y};Number of contributors", hTH3PtVsYVsNContrib}, {"hNContribMcGen", "D meson candidates MC Gen;Number of PV contributors", hTH1NContrib}, // MC Rec plots {"hPtVsYVsNContribMcRec", "D meson candidates MC Rec;candidate #it{p}_{T} (GeV/#it{c});#it{y};Number of contributors", hTH3PtVsYVsNContrib}, @@ -273,7 +250,6 @@ struct HfCorrelatorDMesonPairs { void init(InitContext&) { - if (applyMl) { hfMlResponse.configure(binsPtMl, cutsMl, cutDirMl, nClassesMl); if (loadModelsFromCCDB) { @@ -382,6 +358,7 @@ struct HfCorrelatorDMesonPairs { registry.add("hInputCheckD0OrD0barMcGen", "Check on input D0 | D0bar meson candidates/event MC Gen", {HistType::kTH1F, {axisInputD0}}); registry.add("hMass", "D Meson pair candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hMassSig", "MC-matched D Meson pair candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassMcRecPrompt", "D Meson pair candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassMcRecNonPrompt", "D Meson pair candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassMcRecReflections", "D Meson pair candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); @@ -409,26 +386,93 @@ struct HfCorrelatorDMesonPairs { } // Event mixing - AxisSpec axisPtHadron = {(std::vector)binsPtHadron, "#it{p}_{T} Hadron (GeV/#it{c})"}; - AxisSpec const axisMultiplicity = {binsMultiplicity, "Multiplicity"}; - AxisSpec axisMultFT0M = {binsMultFT0M, "MultiplicityFT0M"}; - AxisSpec const axisPosZ = {binsZVtx, "PosZ"}; AxisSpec const axisPoolBin = {binsPoolBin, "PoolBin"}; - AxisSpec const axisDcaXY = {binsDcaXY, "DCA xy"}; - corrBinning = std::make_unique(BinningType{{binsZVtx, binsMultiplicity}, true}); + corrBinning = std::make_unique(BinningType{{binsZVtx, binsMultiplicity}}); if (applyMixedEvent) { - registry.add("hMultiplicityPreSelection", "multiplicity prior to selection;multiplicity;entries", {HistType::kTH1F, {{10000, 0., 10000.}}}); - registry.add("hMultiplicity", "multiplicity;multiplicity;entries", {HistType::kTH1F, {{10000, 0., 10000.}}}); registry.add("hMultFT0M", "multiplicity;multiplicity;entries", {HistType::kTH1F, {{10000, 0., 10000.}}}); registry.add("hZvtx", "z vertex;z vertex;entries", {HistType::kTH1F, {{200, -20., 20.}}}); registry.add("hD0Bin", "D0 selected in pool Bin;pool Bin;entries", {HistType::kTH1F, {{axisPoolBin}}}); registry.add("hD0Cand2Bin", "D0 selected in pool Bin;pool Bin;entries", {HistType::kTH1F, {{axisPoolBin}}}); - registry.add("hTracksBin", "Tracks selected in pool Bin;pool Bin;entries", {HistType::kTH1F, {{axisPoolBin}}}); - registry.add("hDcaXYVsPt", "DCA xy vs pt", {HistType::kTH2F, {{axisDcaXY}, {axisPtHadron}}}); } } + /// ------- FUNCTION DEFINITIONS ------- + + /// Kinematic, signal-region and ambiguity selections of a candidate + /// \return true if the candidate passes the selections + template + bool passKinematicSelections(const T& candidate, const bool isDCand, const bool isDbarCand) + { + if (std::abs(HfHelper::yD0(candidate)) > yCandMax) { + return false; + } + if (ptCandMin >= 0. && candidate.pt() < ptCandMin) { + return false; + } + bool const isSignalD0Cand = std::abs(HfHelper::invMassD0ToPiK(candidate) - MassD0) < massCut; + bool const isSignalD0barCand = std::abs(HfHelper::invMassD0barToKPi(candidate) - MassD0Bar) < massCut; + if (selectSignalRegionOnly && !(isSignalD0Cand || isSignalD0barCand)) { + return false; + } + // Remove ambiguous D0 candidates if flag is true + if (removeAmbiguous && (isDCand && isDbarCand) && candidate.pt() < ptMaxRemoveAmbiguous) { + return false; + } + return true; + } + + /// ML selections of a candidate + /// \return true if the candidate is selected by the ML model (as D0 and/or D0bar) + template + bool passMlSelections(const T& candidate, const bool isDCand, const bool isDbarCand, std::vector& outputMlD0Cand, std::vector& outputMlD0barCand) + { + bool isSelectedMlD0Cand = false; + bool isSelectedMlD0barCand = false; + + if (isDCand) { + std::vector inputFeaturesD0 = hfMlResponse.getInputFeatures(candidate, o2::constants::physics::kD0); + isSelectedMlD0Cand = hfMlResponse.isSelectedMl(inputFeaturesD0, candidate.pt(), outputMlD0Cand); + } + if (isDbarCand) { + std::vector inputFeaturesD0bar = hfMlResponse.getInputFeatures(candidate, o2::constants::physics::kD0Bar); + isSelectedMlD0barCand = hfMlResponse.isSelectedMl(inputFeaturesD0bar, candidate.pt(), outputMlD0barCand); + } + + return isSelectedMlD0Cand || isSelectedMlD0barCand; + } + + /// Rapidity and pT selections of gen-level particles + template + bool passGenSelections(const T& particle) + { + return std::abs(particle.y()) <= yCandMax && (ptCandMin < 0. || particle.pt() >= ptCandMin); + } + + /// Sets bits to select pair types. + /// \param candidate is candidate + /// \return bitmap with type of pair (DD, DbarDbar, DDbar, DbarD) + uint8_t setPairType(const uint8_t candidateType1, const uint8_t candidateType2) + { + uint8_t pairType(0); + bool const isDCand1 = isD(candidateType1), isDCand2 = isD(candidateType2); + bool const isDbarCand1 = isDbar(candidateType1), isDbarCand2 = isDbar(candidateType2); + + if (isDCand1 && isDCand2) { + SETBIT(pairType, DD); + } + if (isDbarCand1 && isDbarCand2) { + SETBIT(pairType, DbarDbar); + } + if (isDCand1 && isDbarCand2) { + SETBIT(pairType, DDbar); + } + if (isDbarCand1 && isDCand2) { + SETBIT(pairType, DbarD); + } + return pairType; + } + /// Sets bits to select candidate type for D0 /// SelectedD and SelectedDbar bits look at whether the candidate passed the selection flags. /// \param candidate is candidate @@ -444,10 +488,10 @@ struct HfCorrelatorDMesonPairs { SETBIT(candidateType, SelectedDbar); } if constexpr (IsMcRec) { - if (candidate.flagMcMatchRec() == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { // matched as D0 + if (candidate.flagMcMatchRec() == DecayChannel::D0ToPiK) { // matched as D0 SETBIT(candidateType, TrueD); } - if (candidate.flagMcMatchRec() == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { // matched as D0bar + if (candidate.flagMcMatchRec() == -DecayChannel::D0ToPiK) { // matched as D0bar SETBIT(candidateType, TrueDbar); } } @@ -471,37 +515,40 @@ struct HfCorrelatorDMesonPairs { } /// Check if the candidate is a D - /// \param candidate is candidate - /// \return true if is a D bool isD(const uint8_t candidateType) { return (TESTBIT(candidateType, SelectedD)); } /// Check if the candidate is a Dbar - /// \param candidate is candidate - /// \return true if is a Dbar bool isDbar(const uint8_t candidateType) { return (TESTBIT(candidateType, SelectedDbar)); } /// Check if the candidate is a true D - /// \param candidate is candidate - /// \return true if is a true D bool isTrueD(const uint8_t candidateType) { return (TESTBIT(candidateType, TrueD)); } /// Check if the candidate is a true Dbar - /// \param candidate is candidate - /// \return true if is a true Dbar bool isTrueDbar(const uint8_t candidateType) { return (TESTBIT(candidateType, TrueDbar)); } + /// Canonical pair ordering: D0 before D0bar; for equal flavours, higher pT first. + /// \return true if candidate 1 and candidate 2 should be exchanged + template + static bool shouldSwapOrder(const bool isD1, const bool isDbar1, const bool isD2, const bool isDbar2, const T& candidate1, const T& candidate2) + { + if (isD1 != isD2 || isDbar1 != isDbar2) { // different flavour content + return !isD1 && isD2; // Dbar(-only) first, D second -> swap + } + return candidate1.pt() < candidate2.pt(); + } + /// Fill PID related plots for D0 and D0bar /// \param candidate is candidate template @@ -526,51 +573,51 @@ struct HfCorrelatorDMesonPairs { } /// Fill counters for D0 and D0bar - /// \param d0Candidates contains all D0 candidates - template + /// \param d0Candidates contains the D0 candidates of the collision + template void getCountersPerEvent(const T& d0Candidates) { int nDevent = 0, nDbarevent = 0, nDDbarevent = 0, nDorDbarevent = 0; for (const auto& candidate : d0Candidates) { // Get counters per event - bool const isSignalD0 = std::abs(HfHelper::invMassD0ToPiK(candidate) - MassD0) < massCut; - bool const isSignalD0bar = std::abs(HfHelper::invMassD0barToKPi(candidate) - MassD0Bar) < massCut; - if (selectSignalRegionOnly && !(isSignalD0 || isSignalD0bar)) { - continue; - } - auto candidateType1 = assignCandidateTypeD0(candidate); // Candidate type attribution - registry.fill(HIST("hPtCand"), candidate.pt()); - if (std::abs(HfHelper::yD0(candidate)) > yCandMax) { - continue; - } - if (ptCandMin >= 0. && candidate.pt() < ptCandMin) { + auto const candidateType = assignCandidateTypeD0(candidate); // Candidate type attribution + bool const isDCand = isD(candidateType); + bool const isDbarCand = isDbar(candidateType); + bool const isTrueDCand = isTrueD(candidateType); + bool const isTrueDbarCand = isTrueDbar(candidateType); + + if (!passKinematicSelections(candidate, isDCand, isDbarCand)) { continue; } - bool const isDCand1 = isD(candidateType1); - bool const isDbarCand1 = isDbar(candidateType1); - if (isDCand1) { + if (isDCand) { nDevent++; } - if (isDbarCand1) { + if (isDbarCand) { nDbarevent++; } - if (isDCand1 && isDbarCand1) { + if (isDCand && isDbarCand) { nDDbarevent++; } - if (isDCand1 || isDbarCand1) { + if (isDCand || isDbarCand) { nDorDbarevent++; } // Fill selection status single particle registry.fill(HIST("hStatusSinglePart"), 1); - if (isDCand1 && !isDbarCand1) { + if (isDCand && !isDbarCand) { registry.fill(HIST("hStatusSinglePart"), 2); - } else if (isDbarCand1 && !isDCand1) { + } else if (isDbarCand && !isDCand) { registry.fill(HIST("hStatusSinglePart"), 3); - } else if (isDCand1 && isDbarCand1) { + } else if (isDCand && isDbarCand) { registry.fill(HIST("hStatusSinglePart"), 4); } + // fill true info (available only in MC) + if (isTrueDCand) { + registry.fill(HIST("hStatusSinglePart"), 5); + } else if (isTrueDbarCand) { + registry.fill(HIST("hStatusSinglePart"), 6); + } } if (nDevent > 0) { registry.fill(HIST("hInputCheckD0"), nDevent); @@ -586,11 +633,12 @@ struct HfCorrelatorDMesonPairs { } } - /// Fill selection status histogram - void fillEntry(const bool& isDCand1, const bool& isDbarCand1, const bool& isDCand2, const bool& isDbarCand2, - const uint8_t candidateType1, const uint8_t candidateType2, float yCand1, float yCand2, float etaCand1, float etaCand2, float phiCand1, float phiCand2, - double ptCand1, double ptCand2, float massDCand1, float massDbarCand1, float massDCand2, float massDbarCand2) + void fillSelectionStatusHisto(const uint8_t candidateType1, const uint8_t candidateType2) { + bool const isDCand1 = isD(candidateType1), isDCand2 = isD(candidateType2); + bool const isDbarCand1 = isDbar(candidateType1), isDbarCand2 = isDbar(candidateType2); + bool const isTrueDCand1 = isTrueD(candidateType1), isTrueDCand2 = isTrueD(candidateType2); + bool const isTrueDbarCand1 = isTrueDbar(candidateType1), isTrueDbarCand2 = isTrueDbar(candidateType2); /// Fill information on the D candidates registry.fill(HIST("hSelectionStatus"), 2); @@ -612,168 +660,119 @@ struct HfCorrelatorDMesonPairs { } /// Collect information on the D pairs - uint8_t pairType(0); registry.fill(HIST("hSelectionStatus"), 1); if (isDCand1 && isDCand2) { - SETBIT(pairType, DD); registry.fill(HIST("hSelectionStatus"), 14); if ((!isDbarCand1) && (!isDbarCand2)) { registry.fill(HIST("hSelectionStatus"), 18); } } if (isDbarCand1 && isDbarCand2) { - SETBIT(pairType, DbarDbar); registry.fill(HIST("hSelectionStatus"), 15); if ((!isDCand1) && (!isDCand2)) { registry.fill(HIST("hSelectionStatus"), 19); } } if (isDCand1 && isDbarCand2) { - SETBIT(pairType, DDbar); registry.fill(HIST("hSelectionStatus"), 16); if ((!isDbarCand1) && (!isDCand2)) { registry.fill(HIST("hSelectionStatus"), 20); } } if (isDbarCand1 && isDCand2) { - SETBIT(pairType, DbarD); registry.fill(HIST("hSelectionStatus"), 17); if ((!isDCand1) && (!isDbarCand2)) { registry.fill(HIST("hSelectionStatus"), 21); } } - - entryD0Pair(ptCand1, ptCand2, yCand1, yCand2, etaCand1, etaCand2, phiCand1, phiCand2, massDCand1, massDbarCand1, massDCand2, massDbarCand2, pairType, candidateType1, candidateType2); - } - - void fillMcHistos(int8_t matchedRec1, int8_t matchedRec2, int8_t isTrueDCand1, int8_t isTrueDbarCand1, int8_t isTrueDCand2, int8_t isTrueDbarCand2) - { - // Fill hMatchingMcRec - Cand 1 - registry.fill(HIST("hMatchingMcRec"), 1); - if (matchedRec1 == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { - registry.fill(HIST("hMatchingMcRec"), 2); - } else if (matchedRec1 == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { - registry.fill(HIST("hMatchingMcRec"), 3); - } else if (matchedRec1 == 0) { - registry.fill(HIST("hMatchingMcRec"), 4); - } - // Fill hMatchingMcRec - Cand 2 - registry.fill(HIST("hMatchingMcRec"), 5); - if (matchedRec2 == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { - registry.fill(HIST("hMatchingMcRec"), 6); - } else if (matchedRec2 == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { - registry.fill(HIST("hMatchingMcRec"), 7); - } else if (matchedRec2 == 0) { - registry.fill(HIST("hMatchingMcRec"), 8); - } // Fill True info - if (isTrueDCand1 != 0) { + if (isTrueDCand1) { registry.fill(HIST("hSelectionStatus"), 6); - } else if (isTrueDbarCand1 != 0) { + } else if (isTrueDbarCand1) { registry.fill(HIST("hSelectionStatus"), 7); } - if (isTrueDCand2 != 0) { + if (isTrueDCand2) { registry.fill(HIST("hSelectionStatus"), 12); - } else if (isTrueDbarCand2 != 0) { + } else if (isTrueDbarCand2) { registry.fill(HIST("hSelectionStatus"), 13); } - if ((isTrueDCand1 != 0) && (isTrueDCand2 != 0)) { + if (isTrueDCand1 && isTrueDCand2) { registry.fill(HIST("hSelectionStatus"), 22); - } else if ((isTrueDbarCand1 != 0) && (isTrueDbarCand2 != 0)) { + } else if (isTrueDbarCand1 && isTrueDbarCand2) { registry.fill(HIST("hSelectionStatus"), 23); - } else if ((isTrueDCand1 != 0) && (isTrueDbarCand2 != 0)) { + } else if (isTrueDCand1 && isTrueDbarCand2) { registry.fill(HIST("hSelectionStatus"), 24); - } else if ((isTrueDbarCand1 != 0) && (isTrueDCand2 != 0)) { + } else if (isTrueDbarCand1 && isTrueDCand2) { registry.fill(HIST("hSelectionStatus"), 25); } } - /// D0(bar)-D0(bar) correlation pair builder - for real data and data-like analysis (i.e. reco-level w/o matching request via MC truth) - void processData(SelCollisionsWithD0::iterator const& collision, - CandidatesD0Data const& candidates, TracksData const& tracks, aod::BCsWithTimestamps const&) + template + void fillMatchingHisto(const int8_t matched1, const int8_t matched2) { - - auto bc = collision.bc_as(); - int gCollisionId = collision.globalIndex(); - int64_t timeStamp = bc.timestamp(); - - int poolBin = corrBinning->getBin(std::make_tuple(collision.posZ(), collision.multFT0M())); - - int nTracks = 0; - if (collision.numContrib() > 1) { - for (const auto& track : tracks) { - if (std::abs(track.eta()) >= etaTrackMax || std::abs(track.dcaXY()) >= dcaXYTrackMax || std::abs(track.dcaZ()) >= dcaZTrackMax) { - continue; - } - nTracks++; - } - } - if (applyMixedEvent) { - registry.fill(HIST("hMultiplicityPreSelection"), nTracks); + auto hist = IsGen ? registry.get(HIST("hMatchingMcGen")) : registry.get(HIST("hMatchingMcRec")); + hist->Fill(1); + if (matched1 == DecayChannel::D0ToPiK) { + hist->Fill(2); + } else if (matched1 == -DecayChannel::D0ToPiK) { + hist->Fill(3); + } else if (matched1 == 0) { + hist->Fill(4); } - if (nTracks < multMin || nTracks > multMax) { - return; - } - if (applyMixedEvent) { - registry.fill(HIST("hMultiplicity"), nTracks); + hist->Fill(5); + if (matched2 == DecayChannel::D0ToPiK) { + hist->Fill(6); + } else if (matched2 == -DecayChannel::D0ToPiK) { + hist->Fill(7); + } else if (matched2 == 0) { + hist->Fill(8); } + } - int cntD0 = 0; + /// D0(bar)-D0(bar) correlation pair builder, shared by the data and MC-reco modes + /// \param collision is the collision + /// \param selectedD0CandidatesGrouped is the per-collision slice of the selected D0 candidates + /// \param tracks are the (already filtered) tracks of the collision + /// \param timeStamp is the BC timestamp of the collision + template + void processImpl(TCollision const& collision, + TCandidates const& selectedD0CandidatesGrouped, + const int64_t timeStamp) + { + int const gCollisionId = collision.globalIndex(); + int const poolBin = corrBinning->getBin(std::make_tuple(collision.posZ(), collision.multFT0M())); + + getCountersPerEvent(selectedD0CandidatesGrouped); - for (const auto& candidate : candidates) { - analysePid(candidate); - } - auto selectedD0CandidatesGrouped = selectedD0Candidates->sliceByCached(aod::hf_cand::collisionId, gCollisionId, cache); - getCountersPerEvent(selectedD0CandidatesGrouped); - // protection against empty tables to be sliced - if (selectedD0Candidates.size() <= 1) { - return; - } for (const auto& candidate1 : selectedD0CandidatesGrouped) { + analysePid(candidate1); outputMlD0Cand1.clear(); outputMlD0barCand1.clear(); - if (std::abs(HfHelper::yD0(candidate1)) > yCandMax) { - continue; - } - if (ptCandMin >= 0. && candidate1.pt() < ptCandMin) { - continue; - } - auto prong0Cand1 = candidate1.template prong0_as(); - auto prong1Cand1 = candidate1.template prong1_as(); + auto const prong0Cand1 = candidate1.prong0Id(); + auto const prong1Cand1 = candidate1.prong1Id(); - bool const isSignalD0Cand1 = std::abs(HfHelper::invMassD0ToPiK(candidate1) - MassD0) < massCut; - bool const isSignalD0barCand1 = std::abs(HfHelper::invMassD0barToKPi(candidate1) - MassD0Bar) < massCut; - if (selectSignalRegionOnly && !(isSignalD0Cand1 || isSignalD0barCand1)) { - continue; + [[maybe_unused]] int8_t matchedRec1 = 0; + [[maybe_unused]] int8_t originRec1 = 0; + if constexpr (IsMcRec) { + matchedRec1 = candidate1.flagMcMatchRec(); + originRec1 = candidate1.originMcRec(); } - auto candidateType1 = assignCandidateTypeD0(candidate1); // Candidate type attribution + auto const candidateType1 = assignCandidateTypeD0(candidate1); // Candidate type attribution bool const isDCand1 = isD(candidateType1); bool const isDbarCand1 = isDbar(candidateType1); + bool const isTrueDCand1 = isTrueD(candidateType1); + bool const isTrueDbarCand1 = isTrueDbar(candidateType1); - bool isSelectedMlD0Cand1 = false; - bool isSelectedMlD0barCand1 = false; + registry.fill(HIST("hPtCand"), candidate1.pt()); - if (applyMl) { - if (isDCand1) { - std::vector inputFeaturesD0 = hfMlResponse.getInputFeatures(candidate1, o2::constants::physics::kD0); - isSelectedMlD0Cand1 = hfMlResponse.isSelectedMl(inputFeaturesD0, candidate1.pt(), outputMlD0Cand1); - } - if (isDbarCand1) { - std::vector inputFeaturesD0bar = hfMlResponse.getInputFeatures(candidate1, o2::constants::physics::kD0Bar); - isSelectedMlD0barCand1 = hfMlResponse.isSelectedMl(inputFeaturesD0bar, candidate1.pt(), outputMlD0barCand1); - } - - // Remove non-ML selected D0 candidates - if (!isSelectedMlD0Cand1 && !isSelectedMlD0barCand1) { - continue; - } + if (!passKinematicSelections(candidate1, isDCand1, isDbarCand1)) { + continue; } - // Remove ambiguous D0 candidates if flag is true - if (removeAmbiguous && (isDCand1 && isDbarCand1) && candidate1.pt() < ptMaxRemoveAmbiguous) { + if (applyMl && !passMlSelections(candidate1, isDCand1, isDbarCand1, outputMlD0Cand1, outputMlD0barCand1)) { continue; } @@ -790,240 +789,15 @@ struct HfCorrelatorDMesonPairs { if (isDCand1) { registry.fill(HIST("hMass"), HfHelper::invMassD0ToPiK(candidate1), candidate1.pt()); - if (applyMl) { - registry.fill(HIST("hnDMesonMl"), outputMlD0Cand1[0], outputMlD0Cand1[1], HfHelper::invMassD0ToPiK(candidate1), candidate1.pt(), candidate1.y(MassD0), collision.numContrib(), 0, candidateType1); - } else { - registry.fill(HIST("hnDMeson"), HfHelper::invMassD0ToPiK(candidate1), candidate1.pt(), candidate1.y(MassD0), collision.numContrib(), 0, candidateType1); - } - // Fill D0 table for offline event mixing - if (applyMixedEvent) { - entryDMesonInfoCand1(candidate1.pt(), candidate1.eta(), candidate1.phi(), HfHelper::invMassD0ToPiK(candidate1), candidateType1, poolBin, gCollisionId, timeStamp); - } - } - if (isDbarCand1) { - registry.fill(HIST("hMass"), HfHelper::invMassD0barToKPi(candidate1), candidate1.pt()); - if (applyMl) { - registry.fill(HIST("hnDMesonMl"), outputMlD0barCand1[0], outputMlD0barCand1[1], HfHelper::invMassD0barToKPi(candidate1), candidate1.pt(), candidate1.y(MassD0), collision.numContrib(), 0, candidateType1); - } else { - registry.fill(HIST("hnDMeson"), HfHelper::invMassD0barToKPi(candidate1), candidate1.pt(), candidate1.y(MassD0), collision.numContrib(), 0, candidateType1); - } - // Fill D0 table for offline event mixing - if (applyMixedEvent) { - entryDMesonInfoCand1(candidate1.pt(), candidate1.eta(), candidate1.phi(), HfHelper::invMassD0ToPiK(candidate1), candidateType1, poolBin, gCollisionId, timeStamp); - } - } - - if (applyMixedEvent) { - // Loop on the associated tracks for offline event mixing - for (const auto& track : tracks) { - // apply track selection - if (track.collisionId() != gCollisionId) { - continue; - } - - // Manual trackFilter check - if (std::abs(track.eta()) >= etaTrackMax) { - continue; - } - if (track.pt() <= ptTrackMin) { - continue; - } - if (std::abs(track.dcaXY()) >= dcaXYTrackMax) { - continue; - } - if (std::abs(track.dcaZ()) >= dcaZTrackMax) { - continue; - } - // Removing D0 daughters by checking track indices - if (daughterTracksCutFlag) { - if ((candidate1.prong0Id() == track.globalIndex()) || (candidate1.prong1Id() == track.globalIndex())) { - continue; - } - } - if (cntD0 == 0) { - entryAssocHad(track.pt(), track.eta(), track.phi(), poolBin, gCollisionId, timeStamp); - registry.fill(HIST("hTracksBin"), poolBin); - registry.fill(HIST("hDcaXYVsPt"), track.dcaXY(), track.pt()); - } - } // Hadron Tracks loop - cntD0++; - } - - // Loop on the second D0 candidate - for (auto candidate2 = candidate1 + 1; candidate2 != selectedD0CandidatesGrouped.end(); ++candidate2) { - - outputMlD0Cand2.clear(); - outputMlD0barCand2.clear(); - - if (std::abs(HfHelper::yD0(candidate2)) > yCandMax) { - continue; - } - if (ptCandMin >= 0. && candidate2.pt() < ptCandMin) { - continue; - } - auto prong0Cand2 = candidate2.template prong0_as(); - auto prong1Cand2 = candidate2.template prong1_as(); - if ((prong0Cand1 == prong0Cand2) || (prong1Cand1 == prong1Cand2) || (prong0Cand1 == prong1Cand2) || (prong1Cand1 == prong0Cand2)) { - continue; - } - - bool const isSignalD0Cand2 = std::abs(HfHelper::invMassD0ToPiK(candidate2) - MassD0) < massCut; - bool const isSignalD0barCand2 = std::abs(HfHelper::invMassD0barToKPi(candidate2) - MassD0Bar) < massCut; - if (selectSignalRegionOnly && !(isSignalD0Cand2 || isSignalD0barCand2)) { - continue; - } - auto candidateType2 = assignCandidateTypeD0(candidate2); // Candidate type attribution - - bool const isDCand2 = isD(candidateType2); - bool const isDbarCand2 = isDbar(candidateType2); - - bool isSelectedMlD0Cand2 = false; - bool isSelectedMlD0barCand2 = false; - - if (applyMl) { - if (isDCand2) { - std::vector inputFeaturesD0 = hfMlResponse.getInputFeatures(candidate2, o2::constants::physics::kD0); - isSelectedMlD0Cand2 = hfMlResponse.isSelectedMl(inputFeaturesD0, candidate2.pt(), outputMlD0Cand2); - } - if (isDbarCand2) { - std::vector inputFeaturesD0bar = hfMlResponse.getInputFeatures(candidate2, o2::constants::physics::kD0Bar); - isSelectedMlD0barCand2 = hfMlResponse.isSelectedMl(inputFeaturesD0bar, candidate2.pt(), outputMlD0barCand2); - } - - // Remove non-ML selected D0 candidates - if (!isSelectedMlD0Cand2 && !isSelectedMlD0barCand2) { - continue; - } - - // Remove ambiguous D0 candidates if flag is true - if (removeAmbiguous && (isDCand2 && isDbarCand2) && candidate2.pt() < ptMaxRemoveAmbiguous) { - continue; - } - - if (applyMixedEvent) { - if (isDCand2) { - entryDMesonInfoCand2(candidate2.pt(), candidate2.eta(), candidate2.phi(), HfHelper::invMassD0barToKPi(candidate2), candidateType2, poolBin, gCollisionId, timeStamp); - } - if (isDbarCand2) { - entryDMesonInfoCand2(candidate2.pt(), candidate2.eta(), candidate2.phi(), HfHelper::invMassD0barToKPi(candidate2), candidateType2, poolBin, gCollisionId, timeStamp); - } - registry.fill(HIST("hD0Cand2Bin"), poolBin); - } - - fillEntry(isDCand1, isDbarCand1, isDCand2, isDbarCand2, candidateType1, candidateType2, HfHelper::yD0(candidate1), HfHelper::yD0(candidate2), - candidate1.eta(), candidate2.eta(), candidate1.phi(), candidate2.phi(), - candidate1.pt(), candidate2.pt(), HfHelper::invMassD0ToPiK(candidate1), HfHelper::invMassD0barToKPi(candidate1), - HfHelper::invMassD0ToPiK(candidate2), HfHelper::invMassD0barToKPi(candidate2)); - - entryD0PairMl(outputMlD0Cand1, outputMlD0barCand1, outputMlD0Cand2, outputMlD0barCand2); - } else { - // Fill entries - fillEntry(isDCand1, isDbarCand1, isDCand2, isDbarCand2, candidateType1, candidateType2, HfHelper::yD0(candidate1), HfHelper::yD0(candidate2), - candidate1.eta(), candidate2.eta(), candidate1.phi(), candidate2.phi(), - candidate1.pt(), candidate2.pt(), HfHelper::invMassD0ToPiK(candidate1), HfHelper::invMassD0barToKPi(candidate1), - HfHelper::invMassD0ToPiK(candidate2), HfHelper::invMassD0barToKPi(candidate2)); - } - } // end inner loop (Cand2) - } // end outer loop (Cand1) - - if (applyMixedEvent) { - registry.fill(HIST("hZvtx"), collision.posZ()); - registry.fill(HIST("hMultFT0M"), collision.multFT0M()); - } - } - - PROCESS_SWITCH(HfCorrelatorDMesonPairs, processData, "Process data mode", true); - - void processMcRec(aod::Collision const& collision, soa::Join const& candidates, aod::Tracks const&) - { - for (const auto& candidate : candidates) { - analysePid(candidate); - } - auto selectedD0CandidatesGroupedMc = selectedD0CandidatesMc->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); - getCountersPerEvent(selectedD0CandidatesGroupedMc); - for (const auto& candidate1 : selectedD0CandidatesGroupedMc) { - - outputMlD0Cand1.clear(); - outputMlD0barCand1.clear(); - - auto ptCandidate1 = candidate1.pt(); - auto yCandidate1 = HfHelper::yD0(candidate1); - auto phiCandidate1 = candidate1.phi(); - auto etaCandidate1 = candidate1.eta(); - float const massD0Cand1 = HfHelper::invMassD0ToPiK(candidate1); - float const massD0barCand1 = HfHelper::invMassD0barToKPi(candidate1); - auto prong0Cand1 = candidate1.template prong0_as(); - auto prong1Cand1 = candidate1.template prong1_as(); - - if (std::abs(HfHelper::yD0(candidate1)) > yCandMax) { - continue; - } - if (ptCandMin >= 0. && candidate1.pt() < ptCandMin) { - continue; - } - bool const isSignalD0Cand1 = std::abs(massD0Cand1 - MassD0) < massCut; - bool const isSignalD0barCand1 = std::abs(massD0barCand1 - MassD0Bar) < massCut; - if (selectSignalRegionOnly && !(isSignalD0Cand1 || isSignalD0barCand1)) { - continue; - } - if (candidate1.isSelD0() < selectionFlagD0 && candidate1.isSelD0bar() < selectionFlagD0bar) { - continue; - } - - auto candidateType1 = assignCandidateTypeD0(candidate1); // Candidate type attribution - - bool const isDCand1 = isD(candidateType1); - bool const isDbarCand1 = isDbar(candidateType1); - bool const isTrueDCand1 = isTrueD(candidateType1); - bool const isTrueDbarCand1 = isTrueDbar(candidateType1); - - int8_t matchedRec1 = candidate1.flagMcMatchRec(); - int8_t originRec1 = candidate1.originMcRec(); - - bool isSelectedMlD0Cand1 = false; - bool isSelectedMlD0barCand1 = false; - - if (applyMl) { - if (isDCand1) { - std::vector inputFeaturesD0 = hfMlResponse.getInputFeatures(candidate1, o2::constants::physics::kD0); - isSelectedMlD0Cand1 = hfMlResponse.isSelectedMl(inputFeaturesD0, candidate1.pt(), outputMlD0Cand1); - } - if (isDbarCand1) { - std::vector inputFeaturesD0bar = hfMlResponse.getInputFeatures(candidate1, o2::constants::physics::kD0Bar); - isSelectedMlD0barCand1 = hfMlResponse.isSelectedMl(inputFeaturesD0bar, candidate1.pt(), outputMlD0barCand1); - } - // Remove non-ML selected D0 candidates - if (!isSelectedMlD0Cand1 && !isSelectedMlD0barCand1) { - continue; - } - } - - // Remove ambiguous D0 candidates if flag is true - if (removeAmbiguous && (isDCand1 && isDbarCand1) && candidate1.pt() < ptMaxRemoveAmbiguous) { - continue; - } - - if (isTrueDCand1) { - registry.fill(HIST("hStatusSinglePart"), 5); - } else if (isTrueDbarCand1) { - registry.fill(HIST("hStatusSinglePart"), 6); - } - - registry.fill(HIST("hPtProng0"), candidate1.ptProng0()); - registry.fill(HIST("hPtProng1"), candidate1.ptProng1()); - registry.fill(HIST("hEta"), candidate1.eta()); - registry.fill(HIST("hPhi"), candidate1.phi()); - registry.fill(HIST("hY"), candidate1.y(MassD0)); - registry.fill(HIST("hPtCandAfterCut"), candidate1.pt()); - - if (isDCand1) { + // Fill THnSparse for D0 candidates if (applyMl) { registry.fill(HIST("hnDMesonMl"), outputMlD0Cand1[0], outputMlD0Cand1[1], HfHelper::invMassD0ToPiK(candidate1), candidate1.pt(), candidate1.y(MassD0), collision.numContrib(), originRec1, candidateType1); } else { registry.fill(HIST("hnDMeson"), HfHelper::invMassD0ToPiK(candidate1), candidate1.pt(), candidate1.y(MassD0), collision.numContrib(), originRec1, candidateType1); } + // Fill MC-matched histograms for D0 candidates if (isTrueDCand1) { - registry.fill(HIST("hMass"), HfHelper::invMassD0ToPiK(candidate1), candidate1.pt()); + registry.fill(HIST("hMassSig"), HfHelper::invMassD0ToPiK(candidate1), candidate1.pt()); registry.fill(HIST("hPtVsYVsNContribMcRec"), candidate1.pt(), HfHelper::yD0(candidate1), collision.numContrib()); registry.fill(HIST("hNContribMcRec"), collision.numContrib()); if (originRec1 == RecoDecay::Prompt) { @@ -1036,149 +810,190 @@ struct HfCorrelatorDMesonPairs { } else if (isTrueDbarCand1) { registry.fill(HIST("hMassMcRecReflections"), HfHelper::invMassD0ToPiK(candidate1), candidate1.pt()); } + // Fill D0 table for offline event mixing + if (applyMixedEvent) { + entryDMesonInfoCand1(candidate1.pt(), candidate1.eta(), candidate1.phi(), HfHelper::invMassD0ToPiK(candidate1), candidateType1, poolBin, gCollisionId, timeStamp); + } } if (isDbarCand1) { + registry.fill(HIST("hMass"), HfHelper::invMassD0barToKPi(candidate1), candidate1.pt()); + // Fill THnSparse for D0bar candidates if (applyMl) { registry.fill(HIST("hnDMesonMl"), outputMlD0barCand1[0], outputMlD0barCand1[1], HfHelper::invMassD0barToKPi(candidate1), candidate1.pt(), candidate1.y(MassD0), collision.numContrib(), originRec1, candidateType1); } else { registry.fill(HIST("hnDMeson"), HfHelper::invMassD0barToKPi(candidate1), candidate1.pt(), candidate1.y(MassD0), collision.numContrib(), originRec1, candidateType1); } + // Fill MC-matched histograms for D0bar candidates if (isTrueDbarCand1) { - registry.fill(HIST("hMass"), HfHelper::invMassD0barToKPi(candidate1), candidate1.pt()); + registry.fill(HIST("hMassSig"), HfHelper::invMassD0barToKPi(candidate1), candidate1.pt()); registry.fill(HIST("hPtVsYVsNContribMcRec"), candidate1.pt(), HfHelper::yD0(candidate1), collision.numContrib()); registry.fill(HIST("hNContribMcRec"), collision.numContrib()); if (originRec1 == RecoDecay::Prompt) { registry.fill(HIST("hMassMcRecPrompt"), HfHelper::invMassD0barToKPi(candidate1), candidate1.pt()); + registry.fill(HIST("hPtVsYVsNContribMcRecPrompt"), candidate1.pt(), HfHelper::yD0(candidate1), collision.numContrib()); } else if (originRec1 == RecoDecay::NonPrompt) { registry.fill(HIST("hMassMcRecNonPrompt"), HfHelper::invMassD0barToKPi(candidate1), candidate1.pt()); + registry.fill(HIST("hPtVsYVsNContribMcRecNonPrompt"), candidate1.pt(), HfHelper::yD0(candidate1), collision.numContrib()); } } else if (isTrueDCand1) { registry.fill(HIST("hMassMcRecReflections"), HfHelper::invMassD0barToKPi(candidate1), candidate1.pt()); } + // Fill D0bar table for offline event mixing + if (applyMixedEvent) { + entryDMesonInfoCand1(candidate1.pt(), candidate1.eta(), candidate1.phi(), HfHelper::invMassD0barToKPi(candidate1), candidateType1, poolBin, gCollisionId, timeStamp); + } } - for (auto candidate2 = candidate1 + 1; candidate2 != selectedD0CandidatesGroupedMc.end(); ++candidate2) { + // Loop on the second D0 candidate + for (auto candidate2 = candidate1 + 1; candidate2 != selectedD0CandidatesGrouped.end(); ++candidate2) { outputMlD0Cand2.clear(); outputMlD0barCand2.clear(); - auto ptCandidate2 = candidate2.pt(); - auto yCandidate2 = HfHelper::yD0(candidate2); - auto phiCandidate2 = candidate2.phi(); - auto etaCandidate2 = candidate2.eta(); - float const massD0Cand2 = HfHelper::invMassD0ToPiK(candidate2); - float const massD0barCand2 = HfHelper::invMassD0barToKPi(candidate2); - auto prong0Cand2 = candidate2.template prong0_as(); - auto prong1Cand2 = candidate2.template prong1_as(); - - if (std::abs(HfHelper::yD0(candidate2)) > yCandMax) { - continue; - } - if (ptCandMin >= 0. && candidate2.pt() < ptCandMin) { - continue; - } - bool const isSignalD0Cand2 = std::abs(massD0Cand2 - MassD0) < massCut; - bool const isSignalD0barCand2 = std::abs(massD0barCand2 - MassD0Bar) < massCut; - if (selectSignalRegionOnly && !(isSignalD0Cand2 || isSignalD0barCand2)) { - continue; - } - if (candidate2.isSelD0() < selectionFlagD0 && candidate2.isSelD0bar() < selectionFlagD0bar) { - continue; - } + auto const prong0Cand2 = candidate2.prong0Id(); + auto const prong1Cand2 = candidate2.prong1Id(); if ((prong0Cand1 == prong0Cand2) || (prong1Cand1 == prong1Cand2) || (prong0Cand1 == prong1Cand2) || (prong1Cand1 == prong0Cand2)) { continue; } - auto candidateType2 = assignCandidateTypeD0(candidate2); // Candidate type attribution + auto const candidateType2 = assignCandidateTypeD0(candidate2); // Candidate type attribution bool const isDCand2 = isD(candidateType2); bool const isDbarCand2 = isDbar(candidateType2); - bool const isTrueDCand2 = isTrueD(candidateType2); - bool const isTrueDbarCand2 = isTrueDbar(candidateType2); - int8_t matchedRec2 = candidate2.flagMcMatchRec(); - int8_t originRec2 = candidate2.originMcRec(); + int8_t matchedRec2 = 0; + int8_t originRec2 = 0; + if constexpr (IsMcRec) { + matchedRec2 = candidate2.flagMcMatchRec(); + originRec2 = candidate2.originMcRec(); + } - bool isSelectedMlD0Cand2 = false; - bool isSelectedMlD0barCand2 = false; + if (!passKinematicSelections(candidate2, isDCand2, isDbarCand2)) { + continue; + } - if (applyMl) { + if (applyMl && !passMlSelections(candidate2, isDCand2, isDbarCand2, outputMlD0Cand2, outputMlD0barCand2)) { + continue; + } + + if (applyMixedEvent) { if (isDCand2) { - std::vector inputFeaturesD0 = hfMlResponse.getInputFeatures(candidate2, o2::constants::physics::kD0); - isSelectedMlD0Cand2 = hfMlResponse.isSelectedMl(inputFeaturesD0, candidate2.pt(), outputMlD0Cand2); + entryDMesonInfoCand2(candidate2.pt(), candidate2.eta(), candidate2.phi(), HfHelper::invMassD0ToPiK(candidate2), candidateType2, poolBin, gCollisionId, timeStamp); } if (isDbarCand2) { - std::vector inputFeaturesD0bar = hfMlResponse.getInputFeatures(candidate2, o2::constants::physics::kD0Bar); - isSelectedMlD0barCand2 = hfMlResponse.isSelectedMl(inputFeaturesD0bar, candidate2.pt(), outputMlD0barCand2); + entryDMesonInfoCand2(candidate2.pt(), candidate2.eta(), candidate2.phi(), HfHelper::invMassD0barToKPi(candidate2), candidateType2, poolBin, gCollisionId, timeStamp); } + registry.fill(HIST("hD0Cand2Bin"), poolBin); + } - // Remove non-ML selected D0 candidates - if (!isSelectedMlD0Cand2 && !isSelectedMlD0barCand2) { - continue; - } - - // Remove ambiguous D0 candidates if flag is true - if (removeAmbiguous && (isDCand2 && isDbarCand2) && candidate2.pt() < ptMaxRemoveAmbiguous) { - continue; - } - - // Fill tables - fillEntry(isDCand1, isDbarCand1, isDCand2, isDbarCand2, candidateType1, candidateType2, yCandidate1, yCandidate2, etaCandidate1, etaCandidate2, phiCandidate1, phiCandidate2, - ptCandidate1, ptCandidate2, massD0Cand1, massD0barCand1, massD0Cand2, massD0barCand2); - fillMcHistos(matchedRec1, matchedRec2, static_cast(isTrueDCand1), static_cast(isTrueDbarCand1), static_cast(isTrueDCand2), static_cast(isTrueDbarCand2)); - entryD0PairMcInfo(originRec1, originRec2, matchedRec1, matchedRec2); - entryD0PairMl(outputMlD0Cand1, outputMlD0barCand1, outputMlD0Cand2, outputMlD0barCand2); - - } else { - // Fill tables - fillEntry(isDCand1, isDbarCand1, isDCand2, isDbarCand2, candidateType1, candidateType2, yCandidate1, yCandidate2, etaCandidate1, etaCandidate2, phiCandidate1, phiCandidate2, - ptCandidate1, ptCandidate2, massD0Cand1, massD0barCand1, massD0Cand2, massD0barCand2); - fillMcHistos(matchedRec1, matchedRec2, static_cast(isTrueDCand1), static_cast(isTrueDbarCand1), static_cast(isTrueDCand2), static_cast(isTrueDbarCand2)); - entryD0PairMcInfo(originRec1, originRec2, matchedRec1, matchedRec2); + // Canonical ordering of the pair: D0 first (higher pT first for equal flavours) + bool const swapOrder = shouldSwapOrder(isDCand1, isDbarCand1, isDCand2, isDbarCand2, candidate1, candidate2); + uint8_t const type1 = swapOrder ? candidateType2 : candidateType1; + uint8_t const type2 = swapOrder ? candidateType1 : candidateType2; + auto const& first = swapOrder ? candidate2 : candidate1; + auto const& second = swapOrder ? candidate1 : candidate2; + uint8_t const pairType = setPairType(type1, type2); + + // Fill entries for D0-D0(bar) pairs and plot histos + fillSelectionStatusHisto(type1, type2); + entryD0Pair(first.pt(), second.pt(), HfHelper::yD0(first), HfHelper::yD0(second), first.eta(), second.eta(), + first.phi(), second.phi(), HfHelper::invMassD0ToPiK(first), HfHelper::invMassD0barToKPi(first), + HfHelper::invMassD0ToPiK(second), HfHelper::invMassD0barToKPi(second), pairType, type1, type2); + if (applyMl) { + entryD0PairMl(swapOrder ? outputMlD0Cand2 : outputMlD0Cand1, swapOrder ? outputMlD0barCand2 : outputMlD0barCand1, + swapOrder ? outputMlD0Cand1 : outputMlD0Cand2, swapOrder ? outputMlD0barCand1 : outputMlD0barCand2); + } + if constexpr (IsMcRec) { + fillMatchingHisto(swapOrder ? matchedRec2 : matchedRec1, swapOrder ? matchedRec1 : matchedRec2); + entryD0PairMcInfo(swapOrder ? originRec2 : originRec1, swapOrder ? originRec1 : originRec2, + swapOrder ? matchedRec2 : matchedRec1, swapOrder ? matchedRec1 : matchedRec2); } } // end inner loop (Cand2) } // end outer loop (Cand1) + + if (applyMixedEvent) { + registry.fill(HIST("hZvtx"), collision.posZ()); + registry.fill(HIST("hMultFT0M"), collision.multFT0M()); + } } - PROCESS_SWITCH(HfCorrelatorDMesonPairs, processMcRec, "Process Mc reco mode", false); + /// D0(bar)-D0(bar) correlation pair builder - for real data and data-like analysis (i.e. reco-level w/o matching request via MC truth) + void processData(SelCollisionsWithD0::iterator const& collision, + CandidatesD0Data const&, aod::Tracks const&, aod::BCsWithTimestamps const&) + { + auto const timeStamp = collision.bc_as().timestamp(); + auto grouped = selectedD0Candidates->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); + processImpl(collision, grouped, timeStamp); + } + PROCESS_SWITCH(HfCorrelatorDMesonPairs, processData, "Process data mode", true); + + /// D0(bar)-D0(bar) correlation pair builder - MC reco level (candidates matched to MC truth) + void processMcRec(SelCollisionsWithD0::iterator const& collision, + CandidatesD0McRec const&, aod::Tracks const&, aod::BCsWithTimestamps const&) + { + auto const timeStamp = collision.bc_as().timestamp(); + auto grouped = selectedD0CandidatesMc->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); + processImpl(collision, grouped, timeStamp); + } + PROCESS_SWITCH(HfCorrelatorDMesonPairs, processMcRec, "Process MC reco mode", false); + /// D0(bar)-D0(bar) correlation pair builder - MC generator level (particles flagged as D0 -> pi K) void processMcGen(aod::McCollision const&, soa::SmallGroups> const& collisions, McParticlesPlus2Prong const& mcParticles) { + // Take the associated reconstructed collision with the highest number of PV contributors int numPvContributorsGen{0}; - for (const auto& collision : collisions) { // loop over reco collisions associated to this gen collision + for (const auto& collision : collisions) { int const numPvContributors = collision.numContrib(); - - if (numPvContributors > numPvContributorsGen) { // we take the associated reconstructed collision with higher number of PV contributors + if (numPvContributors > numPvContributorsGen) { numPvContributorsGen = numPvContributors; } } - // Get counters per event - int nDevent = 0, nDbarevent = 0, nDDbarevent = 0, nDorDbarevent = 0; + + // Select the D0(bar) -> K pi particles + std::vector selectedParticles; for (const auto& particle : mcParticles) { - // check if the particle is D0 or D0bar - if (std::abs(particle.pdgCode()) != Pdg::kD0) { + if (std::abs(particle.flagMcMatchGen()) != DecayChannel::D0ToPiK) { continue; } - if (std::abs(particle.y()) > yCandMax) { + registry.fill(HIST("hPtCandMcGen"), particle.pt()); + if (!passGenSelections(particle)) { continue; } - if (ptCandMin >= 0. && particle.pt() < ptCandMin) { - continue; - } - auto particleType = assignParticleTypeD0Gen(particle); // Candidate type attribution + selectedParticles.push_back(particle); + } + + // Single-particle histograms and per-event counters + int nDevent = 0, nDbarevent = 0, nDDbarevent = 0, nDorDbarevent = 0; + for (const auto& particle : selectedParticles) { + auto const particleType = assignParticleTypeD0Gen(particle); bool const isDParticle = isTrueD(particleType); bool const isDbarParticle = isTrueDbar(particleType); - if (isDParticle) { - nDevent++; - } - if (isDbarParticle) { - nDbarevent++; - } - if (isDParticle && isDbarParticle) { - nDDbarevent++; + nDevent += isDParticle; + nDbarevent += isDbarParticle; + nDDbarevent += (isDParticle && isDbarParticle); + nDorDbarevent += (isDParticle || isDbarParticle); + + registry.fill(HIST("hEtaMcGen"), particle.eta()); + registry.fill(HIST("hPhiMcGen"), particle.phi()); + registry.fill(HIST("hPtCandAfterCutMcGen"), particle.pt()); + + registry.fill(HIST("hStatusSinglePartMcGen"), 1); + if (isDParticle && !isDbarParticle) { + registry.fill(HIST("hStatusSinglePartMcGen"), 2); + registry.fill(HIST("hStatusSinglePartMcGen"), 5); + } else if (isDbarParticle && !isDParticle) { + registry.fill(HIST("hStatusSinglePartMcGen"), 3); + registry.fill(HIST("hStatusSinglePartMcGen"), 6); + } else if (isDParticle && isDbarParticle) { + registry.fill(HIST("hStatusSinglePartMcGen"), 4); } - if (isDParticle || isDbarParticle) { - nDorDbarevent++; + + registry.fill(HIST("hPtVsYVsNContribMcGen"), particle.pt(), particle.y(), numPvContributorsGen); + if (particle.originMcGen() == RecoDecay::Prompt) { + registry.fill(HIST("hPtVsYVsNContribMcGenPrompt"), particle.pt(), particle.y(), numPvContributorsGen); + } else if (particle.originMcGen() == RecoDecay::NonPrompt) { + registry.fill(HIST("hPtVsYVsNContribMcGenNonPrompt"), particle.pt(), particle.y(), numPvContributorsGen); } + registry.fill(HIST("hNContribMcGen"), numPvContributorsGen); } if (nDevent > 0) { registry.fill(HIST("hInputCheckD0McGen"), nDevent); @@ -1193,138 +1008,61 @@ struct HfCorrelatorDMesonPairs { registry.fill(HIST("hInputCheckD0OrD0barMcGen"), nDorDbarevent); } - for (const auto& particle1 : mcParticles) { - // check if the particle is D0 or D0bar - if (std::abs(particle1.pdgCode()) != Pdg::kD0) { - continue; - } - registry.fill(HIST("hPtCandMcGen"), particle1.pt()); + // Pairs of selected particles + for (size_t i = 0; i < selectedParticles.size(); ++i) { + for (size_t j = i + 1; j < selectedParticles.size(); ++j) { + auto const& particle1 = selectedParticles[i]; + auto const& particle2 = selectedParticles[j]; - if (std::abs(particle1.y()) > yCandMax) { - continue; - } - if (ptCandMin >= 0. && particle1.pt() < ptCandMin) { - continue; - } - registry.fill(HIST("hEtaMcGen"), particle1.eta()); - registry.fill(HIST("hPhiMcGen"), particle1.phi()); - registry.fill(HIST("hPtCandAfterCutMcGen"), particle1.pt()); - - auto particleType1 = assignParticleTypeD0Gen(particle1); // Candidate sign attribution - bool const isDParticle1 = isTrueD(particleType1); - bool const isDbarParticle1 = isTrueDbar(particleType1); - - // check if it's MC matched - int8_t matchedGen1 = particle1.flagMcMatchGen(); - // check origin - int8_t originGen1 = particle1.originMcGen(); - - // Fill selection status single particle - registry.fill(HIST("hStatusSinglePartMcGen"), 1); - if (isDParticle1 && !isDbarParticle1) { - registry.fill(HIST("hStatusSinglePartMcGen"), 2); - registry.fill(HIST("hStatusSinglePartMcGen"), 5); - } else if (isDbarParticle1 && !isDParticle1) { - registry.fill(HIST("hStatusSinglePartMcGen"), 3); - registry.fill(HIST("hStatusSinglePartMcGen"), 6); - } else if (isDParticle1 && isDbarParticle1) { - registry.fill(HIST("hStatusSinglePartMcGen"), 4); - } + auto const type1 = assignParticleTypeD0Gen(particle1); + auto const type2 = assignParticleTypeD0Gen(particle2); - registry.fill(HIST("hPtVsYVsNContribMcGen"), particle1.pt(), particle1.y(), numPvContributorsGen); - if (originGen1 == RecoDecay::Prompt) { - registry.fill(HIST("hPtVsYVsNContribMcGenPrompt"), particle1.pt(), particle1.y(), numPvContributorsGen); - } - if (originGen1 == RecoDecay::NonPrompt) { - registry.fill(HIST("hPtVsYVsNContribMcGenNonPrompt"), particle1.pt(), particle1.y(), numPvContributorsGen); - } - registry.fill(HIST("hNContribMcGen"), numPvContributorsGen); + // Canonical ordering (same as reco): D0 first, higher pT first for equal flavours + bool const swapOrder = shouldSwapOrder(isTrueD(type1), isTrueDbar(type1), isTrueD(type2), isTrueDbar(type2), particle1, particle2); + auto const& first = swapOrder ? particle2 : particle1; + auto const& second = swapOrder ? particle1 : particle2; + auto const typeFirst = swapOrder ? type2 : type1; + auto const typeSecond = swapOrder ? type1 : type2; + bool const isDFirst = isTrueD(typeFirst), isDbarFirst = isTrueDbar(typeFirst); + bool const isDSecond = isTrueD(typeSecond), isDbarSecond = isTrueDbar(typeSecond); - for (auto particle2 = particle1 + 1; particle2 != mcParticles.end(); ++particle2) { - // check if the particle is D0 or D0bar - if (std::abs(particle2.pdgCode()) != Pdg::kD0) { - continue; - } - if (std::abs(particle2.y()) > yCandMax) { - continue; - } - if (ptCandMin >= 0. && particle2.pt() < ptCandMin) { - continue; - } - // Candidate sign attribution. - auto particleType2 = assignParticleTypeD0Gen(particle2); - bool const isDParticle2 = isTrueD(particleType2); - bool const isDbarParticle2 = isTrueDbar(particleType2); - - // check if it's MC matched - int8_t matchedGen2 = particle2.flagMcMatchGen(); - // check origin - int8_t originGen2 = particle2.originMcGen(); - - // Fill hMatchingMcGen - Cand 1 - registry.fill(HIST("hMatchingMcGen"), 1); - if (matchedGen1 == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { - registry.fill(HIST("hMatchingMcGen"), 2); - } else if (matchedGen1 == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { - registry.fill(HIST("hMatchingMcGen"), 3); - } else if (matchedGen1 == 0) { - registry.fill(HIST("hMatchingMcGen"), 4); - } - // Fill hMatchingMcRec - Cand 2 - registry.fill(HIST("hMatchingMcGen"), 5); - if (matchedGen2 == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { - registry.fill(HIST("hMatchingMcGen"), 6); - } else if (matchedGen2 == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { - registry.fill(HIST("hMatchingMcGen"), 7); - } else if (matchedGen2 == 0) { - registry.fill(HIST("hMatchingMcGen"), 8); - } + fillMatchingHisto(first.flagMcMatchGen(), second.flagMcMatchGen()); - // Fill particle1's Selection Status + // Selection status registry.fill(HIST("hSelectionStatusMcGen"), 2); - if (isDParticle1 && !isDbarParticle1) { + if (isDFirst && !isDbarFirst) { registry.fill(HIST("hSelectionStatusMcGen"), 6); - } else if (isDbarParticle1 && !isDParticle1) { + } else if (isDbarFirst && !isDFirst) { registry.fill(HIST("hSelectionStatusMcGen"), 7); } - - // Fill particle2's Selection Status registry.fill(HIST("hSelectionStatusMcGen"), 8); - if (isDParticle2 && !isDbarParticle2) { + if (isDSecond && !isDbarSecond) { registry.fill(HIST("hSelectionStatusMcGen"), 12); - } else if (isDbarParticle2 && !isDParticle2) { + } else if (isDbarSecond && !isDSecond) { registry.fill(HIST("hSelectionStatusMcGen"), 13); } - // Fill pair Selection Status - uint8_t pairType(0); registry.fill(HIST("hSelectionStatusMcGen"), 1); - - if (isDParticle1 && isDParticle2) { - SETBIT(pairType, DD); + if (isDFirst && isDSecond) { registry.fill(HIST("hSelectionStatusMcGen"), 22); } - if (isDbarParticle1 && isDbarParticle2) { - SETBIT(pairType, DbarDbar); + if (isDbarFirst && isDbarSecond) { registry.fill(HIST("hSelectionStatusMcGen"), 23); } - if (isDParticle1 && isDbarParticle2) { - SETBIT(pairType, DDbar); + if (isDFirst && isDbarSecond) { registry.fill(HIST("hSelectionStatusMcGen"), 24); } - if (isDbarParticle1 && isDParticle2) { - SETBIT(pairType, DbarD); + if (isDbarFirst && isDSecond) { registry.fill(HIST("hSelectionStatusMcGen"), 25); } - // Fill pair Selection Status - entryD0PairMcGen(particle1.pt(), particle2.pt(), particle1.y(), particle2.y(), particle1.eta(), particle2.eta(), particle1.phi(), particle2.phi(), MassD0, MassD0Bar, MassD0, MassD0Bar, pairType, particleType1, particleType2); - entryD0PairMcGenInfo(originGen1, originGen2, matchedGen1, matchedGen2); - - } // end inner loop - } // end outer loop + uint8_t const pairType = setPairType(typeFirst, typeSecond); + entryD0PairMcGen(first.pt(), second.pt(), first.y(), second.y(), first.eta(), second.eta(), first.phi(), second.phi(), + MassD0, MassD0Bar, MassD0, MassD0Bar, pairType, typeFirst, typeSecond); + entryD0PairMcGenInfo(first.originMcGen(), second.originMcGen(), first.flagMcMatchGen(), second.flagMcMatchGen()); + } + } } - PROCESS_SWITCH(HfCorrelatorDMesonPairs, processMcGen, "Process D0 Mc Gen mode", false); }; diff --git a/PWGHF/HFC/TableProducer/correlatorDplusDminus.cxx b/PWGHF/HFC/TableProducer/correlatorDplusDminus.cxx index 14eed44af7f..897d192eaa4 100644 --- a/PWGHF/HFC/TableProducer/correlatorDplusDminus.cxx +++ b/PWGHF/HFC/TableProducer/correlatorDplusDminus.cxx @@ -139,7 +139,7 @@ struct HfCorrelatorDplusDminus { void init(InitContext&) { - auto vbins = (std::vector)binsPt; + auto vbins = std::vector(binsPt); registry.add("hMass", "Dplus,Dminus candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassDplus", "Dplus,Dminus candidates;inv. mass Dplus only (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassDminus", "Dplus,Dminus candidates;inv. mass Dminus only (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); diff --git a/PWGHF/HFC/TableProducer/correlatorDplusDplusReduced.cxx b/PWGHF/HFC/TableProducer/correlatorDplusDplusReduced.cxx index d58da7fccd6..d3530cfff96 100644 --- a/PWGHF/HFC/TableProducer/correlatorDplusDplusReduced.cxx +++ b/PWGHF/HFC/TableProducer/correlatorDplusDplusReduced.cxx @@ -20,6 +20,8 @@ #include "PWGHF/Core/CentralityEstimation.h" #include "PWGHF/Core/DecayChannels.h" #include "PWGHF/Core/HfHelper.h" +#include "PWGHF/Core/HfMlResponseDplusToPiKPi.h" +#include "PWGHF/Core/SelectorCuts.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" #include "PWGHF/HFC/DataModel/ReducedDMesonPairsTables.h" @@ -30,10 +32,12 @@ #include "Common/DataModel/Centrality.h" #include +#include #include #include #include #include +#include #include #include #include @@ -41,9 +45,12 @@ #include #include +#include +#include #include using namespace o2; +using namespace o2::analysis; using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::hf_centrality; @@ -51,7 +58,7 @@ using namespace o2::hf_centrality; /// Writes the full information in an output TTree struct HfCorrelatorDplusDplusReduced { Produces rowCandidateFull; - Produces rowCandidateLite; + Produces rowCandidateLite; Produces rowCandidateTiny; Produces rowCandidateFullEvents; Produces rowCandidateMl; @@ -69,7 +76,33 @@ struct HfCorrelatorDplusDplusReduced { Configurable cfgSkimmedProcessing{"cfgSkimmedProcessing", true, "Enables processing of skimmed datasets"}; Configurable skipSingleD{"skipSingleD", true, "Skip collisions with one or less D candidates"}; - HfHelper hfHelper; + Configurable applyMl{"applyMl", false, "Flag to apply ML selections"}; + Configurable applySkimming{"applySkimming", false, "Flag to apply Skimming selections"}; + Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; + Configurable> binsPtMl{"binsPtMl", std::vector{hf_cuts_ml::vecBinsPt}, "pT bin limits for ML application"}; + Configurable> cutDirMl{"cutDirMl", std::vector{hf_cuts_ml::vecCutDir}, "Whether to reject score values greater or smaller than the threshold"}; + Configurable> cutsMl{"cutsMl", {hf_cuts_ml::Cuts[0], hf_cuts_ml::NBinsPt, hf_cuts_ml::NCutScores, hf_cuts_ml::labelsPt, hf_cuts_ml::labelsCutScore}, "ML selections per pT bin"}; + Configurable nClassesMl{"nClassesMl", static_cast(hf_cuts_ml::NCutScores), "Number of classes in ML model"}; + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable> modelPathsCCDB{"modelPathsCCDB", std::vector{"EventFiltering/PWGHF/BDTDPlus"}, "Paths of models on CCDB"}; + Configurable> onnxFileNames{"onnxFileNames", std::vector{"ModelHandler_onnx_DPlusToKPiPi.onnx"}, "ONNX file names for each pT bin (if not from CCDB full path)"}; + Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB"}; + Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature1", "feature2"}, "Names of ML model input features"}; + + Configurable> cutPtSkimming{"cutPtSkimming", {1, 5, 1000}, "pT bin limits for Skimming application"}; + Configurable> massMin{"massMin", {1.7, 1.7}, "Mass minimal for the cut for each pt bin"}; + Configurable> massMax{"massMax", {2.0, 2.1}, "Mass maximal for the cut for each pt bin"}; + Configurable> cosThetaMin{"cosThetaMin", {0.96, 0.98}, "CosTheta minimal for the cut for each pt bin"}; + Configurable> decayLengthMin{"decayLengthMin", {0.02, 0.03}, "DecayLength minimal for the cut for each pt bin"}; + Configurable> nSigmaTpcMax{"nSigmaTpcMax", {3, 3}, "NsigmaTPC maximal for the cut for each pt bin"}; + Configurable> nSigmaTofMax{"nSigmaTofMax", {3, 3}, "NsigmaTOF maximal for the cut for each pt bin"}; + + Configurable> binsPtSkimming{"binsPtSkimming", {0}, "pT bin limits for Skimming application"}; + + HfMlResponseDplusToPiKPi hfMlResponse; + + std::vector outputMl; + o2::ccdb::CcdbApi ccdbApi; Service ccdb; @@ -102,6 +135,39 @@ struct HfCorrelatorDplusDplusReduced { if (cfgSkimmedProcessing) { zorroSummary.setObject(zorro.getZorroSummary()); } + + if (applyMl) { + hfMlResponse.configure(binsPtMl, cutsMl, cutDirMl, nClassesMl); + if (loadModelsFromCCDB) { + ccdbApi.init(ccdbUrl); + hfMlResponse.setModelPathsCCDB(onnxFileNames, ccdbApi, modelPathsCCDB, timestampCCDB); + } else { + hfMlResponse.setModelPathsLocal(onnxFileNames); + } + hfMlResponse.cacheInputFeaturesIndices(namesInputFeatures); + hfMlResponse.init(); + } + } + + bool skimming(auto const& candidate) + { + if (candidate.pt() < cutPtSkimming.value.at(0) || candidate.pt() > cutPtSkimming.value.at(cutPtSkimming.value.size() - 1)) { + return false; + } + for (size_t i = 0; i < cutPtSkimming.value.size() - 1; i++) { + if (candidate.pt() <= cutPtSkimming.value.at(i + 1)) { + if (HfHelper::invMassDplusToPiKPi(candidate) < massMin.value.at(i) || + HfHelper::invMassDplusToPiKPi(candidate) > massMax.value.at(i) || + candidate.cpa() < cosThetaMin.value.at(i) || + candidate.decayLength() < decayLengthMin.value.at(i) || + std::abs(candidate.nSigTofKa1()) > nSigmaTofMax.value.at(i) || + std::abs(candidate.nSigTpcKa1()) > nSigmaTpcMax.value.at(i)) { + return false; + } + return true; + } + } + return false; } template @@ -127,14 +193,14 @@ struct HfCorrelatorDplusDplusReduced { channelMc = candidate.flagMcDecayChanRec(); } - std::vector outputMl = {-999., -999.}; + std::vector mlProb = {-999., -999.}; if constexpr (DoMl) { for (unsigned int iclass = 0; iclass < classMlIndexes->size(); iclass++) { - outputMl[iclass] = candidate.mlProbDplusToPiKPi()[classMlIndexes->at(iclass)]; + mlProb[iclass] = candidate.mlProbDplusToPiKPi()[classMlIndexes->at(iclass)]; } rowCandidateMl( - outputMl[0], - outputMl[1]); + mlProb[0], + mlProb[1]); } float cent{-1.}; @@ -146,7 +212,7 @@ struct HfCorrelatorDplusDplusReduced { if (fillCandidateTinyTable) { rowCandidateTiny( candidate.isSelDplusToPiKPi(), - hfHelper.invMassDplusToPiKPi(candidate), + HfHelper::invMassDplusToPiKPi(candidate), sign * candidate.pt(), candidate.eta(), candidate.phi(), @@ -189,14 +255,14 @@ struct HfCorrelatorDplusDplusReduced { candidate.tpcTofNSigmaPi2(), candidate.tpcTofNSigmaKa2(), candidate.isSelDplusToPiKPi(), - hfHelper.invMassDplusToPiKPi(candidate), + HfHelper::invMassDplusToPiKPi(candidate), sign * candidate.pt(), candidate.cpa(), candidate.cpaXY(), candidate.maxNormalisedDeltaIP(), candidate.eta(), candidate.phi(), - hfHelper.yDplus(candidate), + HfHelper::yDplus(candidate), cent, localEvIdx, flagMc, @@ -264,17 +330,17 @@ struct HfCorrelatorDplusDplusReduced { candidate.tpcTofNSigmaPi2(), candidate.tpcTofNSigmaKa2(), candidate.isSelDplusToPiKPi(), - hfHelper.invMassDplusToPiKPi(candidate), + HfHelper::invMassDplusToPiKPi(candidate), sign * candidate.pt(), candidate.p(), candidate.cpa(), candidate.cpaXY(), candidate.maxNormalisedDeltaIP(), - hfHelper.ctDplus(candidate), + HfHelper::ctDplus(candidate), candidate.eta(), candidate.phi(), - hfHelper.yDplus(candidate), - hfHelper.eDplus(candidate), + HfHelper::yDplus(candidate), + HfHelper::eDplus(candidate), cent, localEvIdx, flagMc, @@ -291,7 +357,9 @@ struct HfCorrelatorDplusDplusReduced { static int lastRunNumber = -1; // reserve memory rowCandidateFullEvents.reserve(collisions.size()); - if (fillCandidateLiteTable) { + if (fillCandidateTinyTable) { + rowCandidateTiny.reserve(candidates.size()); + } else if (fillCandidateLiteTable) { rowCandidateLite.reserve(candidates.size()); } else { rowCandidateFull.reserve(candidates.size()); @@ -313,13 +381,31 @@ struct HfCorrelatorDplusDplusReduced { const auto colId = collision.globalIndex(); auto candidatesInThisCollision = candidates.sliceBy(tracksPerCollision, colId); - if (skipSingleD) + if (skipSingleD) { if (candidatesInThisCollision.size() < 2) // o2-linter: disable=magic-number (number of candidate must be larger than 1) + { continue; + } + } fillEvent(collision); for (const auto& candidate : candidatesInThisCollision) { auto prongCandidate = candidate.prong1_as(); auto candidateSign = -prongCandidate.sign(); + + if (applySkimming && + !skimming(candidate)) { + continue; + } + + if (applyMl) { + std::vector inputFeatures = hfMlResponse.getInputFeatures(candidate); + bool const isSelectedMl = hfMlResponse.isSelectedMl(inputFeatures, + std::abs(candidate.pt()), + outputMl); + if (!isSelectedMl) { + continue; + } + } fillCandidateTable(candidate, rowCandidateFullEvents.lastIndex(), candidateSign); } } @@ -332,7 +418,9 @@ struct HfCorrelatorDplusDplusReduced { { // reserve memory rowCandidateFullEvents.reserve(collisions.size()); - if (fillCandidateLiteTable) { + if (fillCandidateTinyTable) { + rowCandidateTiny.reserve(candidates.size()); + } else if (fillCandidateLiteTable) { rowCandidateLite.reserve(candidates.size()); } else { rowCandidateFull.reserve(candidates.size()); @@ -341,13 +429,30 @@ struct HfCorrelatorDplusDplusReduced { for (const auto& collision : collisions) { // No skimming for MC data. No Zorro ! const auto colId = collision.globalIndex(); auto candidatesInThisCollision = candidates.sliceBy(tracksPerCollision, colId); - if (skipSingleD) + if (skipSingleD) { if (candidatesInThisCollision.size() < 2) // o2-linter: disable=magic-number (number of candidate must be larger than 1) + { continue; + } + } fillEvent(collision); for (const auto& candidate : candidatesInThisCollision) { auto prongCandidate = candidate.prong1_as(); auto candidateSign = -prongCandidate.sign(); + + if (applySkimming && + !skimming(candidate)) { + continue; + } + if (applyMl) { + std::vector inputFeatures = hfMlResponse.getInputFeatures(candidate); + bool const isSelectedMl = hfMlResponse.isSelectedMl(inputFeatures, + std::abs(candidate.pt()), + outputMl); + if (!isSelectedMl) { + continue; + } + } fillCandidateTable(candidate, rowCandidateFullEvents.lastIndex(), candidateSign); } } @@ -364,9 +469,12 @@ struct HfCorrelatorDplusDplusReduced { for (const auto& mccollision : mccollisions) { // No skimming for MC data. No Zorro ! const auto colId = mccollision.globalIndex(); const auto particlesInThisCollision = mcparticles.sliceBy(mcParticlesPerMcCollision, colId); - if (skipSingleD) + if (skipSingleD) { if (particlesInThisCollision.size() < 2) // o2-linter: disable=magic-number (number of candidate must be larger than 1) + { continue; + } + } rowCandidateMcCollisions( mccollision.posX(), mccollision.posY(), diff --git a/PWGHF/HFC/TableProducer/correlatorDplusHadrons.cxx b/PWGHF/HFC/TableProducer/correlatorDplusHadrons.cxx index 334e473114c..6f7c3f3c6a1 100644 --- a/PWGHF/HFC/TableProducer/correlatorDplusHadrons.cxx +++ b/PWGHF/HFC/TableProducer/correlatorDplusHadrons.cxx @@ -22,6 +22,7 @@ #include "PWGHF/DataModel/TrackIndexSkimmingTables.h" #include "PWGHF/HFC/DataModel/CorrelationTables.h" #include "PWGHF/HFC/DataModel/DerivedDataCorrelationTables.h" +#include "PWGHF/HFC/Utils/utilsCorrelations.h" #include "PWGHF/Utils/utilsAnalysis.h" #include "Common/CCDB/EventSelectionParams.h" @@ -99,13 +100,12 @@ struct HfCorrelatorDplusHadronsDplusSelection { CandidatesDplusData const& candidates) { bool isSelColl = true; - bool isDplusFound = true; + bool isDplusFound = !doSelDplusCollision; bool isSel8 = true; bool isNosameBunchPileUp = true; if (doSelDplusCollision) { for (const auto& candidate : candidates) { if (std::abs(HfHelper::yDplus(candidate)) > yCandMax || candidate.pt() < ptCandMin) { - isDplusFound = false; continue; } isDplusFound = true; @@ -127,7 +127,7 @@ struct HfCorrelatorDplusHadronsDplusSelection { CandidatesDplusMcRec const& candidates) { bool isSelColl = true; - bool isDplusFound = false; + bool isDplusFound = !doSelDplusCollision; bool isSel8 = true; bool isNosameBunchPileUp = true; if (doSelDplusCollision) { @@ -187,9 +187,12 @@ struct HfCorrelatorDplusHadrons { Produces candSelInfo; Produces assocTrackReduced; Produces assocTrackSelInfo; + Produces entryDplusHadronBMotherInfo; + Produces entryDplusCandBMotherInfo; static constexpr std::size_t NDaughters{3u}; - static constexpr float EtaDaughtersMax = 0.8f; // Eta cut on daughters of D+ meson as Run2 + static constexpr float EtaDaughtersMax = 0.8f; // Eta cut on daughters of D+ meson as Run2 + static constexpr int8_t DepthMaxDplusDaughters{2}; // max. depth of the D+ daughters in the decay chain (resonant channels) Configurable selectionFlagDplus{"selectionFlagDplus", 7, "Selection Flag for Dplus"}; // 7 corresponds to topo+PID cuts Configurable numberEventsMixed{"numberEventsMixed", 5, "Number of events mixed in ME process"}; @@ -255,7 +258,7 @@ struct HfCorrelatorDplusHadrons { ConfigurableAxis binsMultFT0M{"binsMultFT0M", {600, 0., 6000.}, "Multiplicity as FT0M signal amplitude"}; ConfigurableAxis binsMassD{"binsMassD", {200, 1.7, 2.10}, "inv. mass (#pi^{+}K^{-}#pi^{+}) (GeV/#it{c}^{2})"}; ConfigurableAxis binsDcaXY{"binsDcaXY", {128, -0.2, 0.2}, "DCA xy"}; - BinningType corrBinning{{binsZVtx, binsMultiplicity}, true}; + BinningType corrBinning{{binsZVtx, binsMultiplicity}}; HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; void init(InitContext&) @@ -263,8 +266,8 @@ struct HfCorrelatorDplusHadrons { AxisSpec axisMassD = {binsMassD, "inv. mass (pi^{+}K^{-}#pi^{+}) (GeV/#it{c}^{2})"}; AxisSpec const axisEta = {binsEta, "#it{#eta}"}; AxisSpec const axisPhi = {binsPhi, "#it{#varphi}"}; - AxisSpec axisPtD = {(std::vector)binsPtD, "#it{p}_{T} (GeV/#it{c})"}; - AxisSpec axisPtHadron = {(std::vector)binsPtHadron, "#it{p}_{T} Hadron (GeV/#it{c})"}; + AxisSpec axisPtD = {std::vector(binsPtD), "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec axisPtHadron = {std::vector(binsPtHadron), "#it{p}_{T} Hadron (GeV/#it{c})"}; AxisSpec const axisMultiplicity = {binsMultiplicity, "Multiplicity"}; AxisSpec axisMultFT0M = {binsMultFT0M, "MultiplicityFT0M"}; AxisSpec const axisPosZ = {binsZVtx, "PosZ"}; @@ -330,7 +333,7 @@ struct HfCorrelatorDplusHadrons { registry.add("hPhiMcGen", "D+,Hadron particles - MC Gen", {HistType::kTH1F, {axisPhi}}); registry.add("hMultFT0AMcGen", "D+,Hadron multiplicity FT0A - MC Gen", {HistType::kTH1F, {axisMultiplicity}}); registry.add("hFakeCollision", "Fake collision counter", {HistType::kTH1F, {{1, -0.5, 0.5, "n fake coll"}}}); - corrBinning = {{binsZVtx, binsMultiplicity}, true}; + corrBinning = {{binsZVtx, binsMultiplicity}}; } template @@ -402,6 +405,9 @@ struct HfCorrelatorDplusHadrons { // Count triggers registry.fill(HIST("hcountDplustriggersMCGen"), 0, particle.pt()); + // beauty-mother information + const auto [ptB, deltaPhiBtoD] = hf_correlations::getBMotherInfoMcGen(particle, particlesToLoop); + for (const auto& particleAssoc : groupedMcParticles) { if (std::abs(particleAssoc.eta()) > etaTrackMax || @@ -448,6 +454,7 @@ struct HfCorrelatorDplusHadrons { entryDplusHadronGenInfo(isDplusPrompt, particleAssoc.isPhysicalPrimary(), trackOrigin); + entryDplusHadronBMotherInfo(ptB, deltaPhiBtoD); } } } @@ -553,7 +560,7 @@ struct HfCorrelatorDplusHadrons { int indexHfcReducedCollision = collReduced.lastIndex() + 1; - // Ds fill histograms and Dplus candidates information stored + // Dplus candidates information stored for (const auto& candidate : candsDplusThisColl) { std::vector outputMl = {-1., -1., -1.}; // candidate selected @@ -562,7 +569,8 @@ struct HfCorrelatorDplusHadrons { outputMl[iclass] = candidate.mlProbDplusToPiKPi()[classMl->at(iclass)]; } candReduced(indexHfcReducedCollision, candidate.phi(), candidate.eta(), candidate.pt(), HfHelper::invMassDplusToPiKPi(candidate), candidate.prong0Id(), candidate.prong1Id(), candidate.prong2Id()); - candSelInfo(indexHfcReducedCollision, outputMl[0], outputMl[2]); + // Check compatibility with the candidate selector options: BdtScoreBkg, BdtScorePrompt, BdtScoreNonPrompt + candSelInfo(indexHfcReducedCollision, outputMl[0], outputMl[1], outputMl[2]); } } @@ -632,6 +640,8 @@ struct HfCorrelatorDplusHadrons { bool isDplusNonPrompt = candidate.originMcRec() == RecoDecay::OriginType::NonPrompt; std::vector outputMl = {-1., -1., -1.}; + // beauty-mother information (generated B, reconstructed D+) + const auto [ptB, deltaPhiBtoD] = hf_correlations::getBMotherInfoMcRec(candidate, mcParticles, Pdg::kDPlus, DepthMaxDplusDaughters); // fill invariant mass plots from Dplus signal and background candidates registry.fill(HIST("hMassDplusMcRec"), HfHelper::invMassDplusToPiKPi(candidate), efficiencyWeightD); @@ -664,6 +674,7 @@ struct HfCorrelatorDplusHadrons { registry.fill(HIST("hMassDplusMcRecSig"), HfHelper::invMassDplusToPiKPi(candidate), candidate.pt(), efficiencyWeightD); entryDplusCandRecoInfo(HfHelper::invMassDplusToPiKPi(candidate), candidate.pt(), outputMl[0], outputMl[1], outputMl[2]); entryDplusCandGenInfo(isDplusPrompt); + entryDplusCandBMotherInfo(ptB, deltaPhiBtoD); } else { registry.fill(HIST("hPtCandMcRecBkg"), candidate.pt()); registry.fill(HIST("hEtaMcRecBkg"), candidate.eta()); @@ -692,6 +703,7 @@ struct HfCorrelatorDplusHadrons { track.pt(), poolBin); entryDplusHadronRecoInfo(HfHelper::invMassDplusToPiKPi(candidate), isDplusSignal); entryDplusHadronMlInfo(outputMl[0], outputMl[1], outputMl[2]); + entryDplusHadronBMotherInfo(ptB, deltaPhiBtoD); if (track.has_mcParticle()) { auto mcParticle = track.template mcParticle_as(); isPhysicalPrimary = mcParticle.isPhysicalPrimary(); @@ -721,7 +733,7 @@ struct HfCorrelatorDplusHadrons { soa::Join const& collisions, CandDplusMcGen const& mcParticles) { - BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}, true}; + BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}}; for (const auto& mcCollision : mcCollisions) { @@ -806,7 +818,7 @@ struct HfCorrelatorDplusHadrons { TracksWithMc const& tracks, aod::McParticles const& mcParticles) { - BinningType const corrBinning{{binsZVtx, binsMultiplicityMc}, true}; + BinningType const corrBinningMcRec{{binsZVtx, binsMultiplicityMc}}; for (const auto& candidate : candidates) { if (std::abs(HfHelper::yDplus(candidate)) > yCandMax || candidate.pt() < ptCandMin || candidate.pt() > ptCandMax) { continue; @@ -831,11 +843,11 @@ struct HfCorrelatorDplusHadrons { } } auto tracksTuple = std::make_tuple(candidates, tracks); - Pair const pairMcRec{corrBinning, numberEventsMixed, -1, collisions, tracksTuple, &cache}; + Pair const pairMcRec{corrBinningMcRec, numberEventsMixed, -1, collisions, tracksTuple, &cache}; for (const auto& [c1, tracks1, c2, tracks2] : pairMcRec) { - int poolBin = corrBinning.getBin(std::make_tuple(c2.posZ(), c2.multFT0M())); - int const poolBinDplus = corrBinning.getBin(std::make_tuple(c1.posZ(), c1.multFT0M())); + int poolBin = corrBinningMcRec.getBin(std::make_tuple(c2.posZ(), c2.multFT0M())); + int const poolBinDplus = corrBinningMcRec.getBin(std::make_tuple(c1.posZ(), c1.multFT0M())); registry.fill(HIST("hMultFT0M"), c1.multFT0M()); registry.fill(HIST("hZVtx"), c1.posZ()); registry.fill(HIST("hTracksPoolBin"), poolBin); // note that the selections here are not yet applied @@ -867,6 +879,8 @@ struct HfCorrelatorDplusHadrons { poolBin); entryDplusHadronRecoInfo(HfHelper::invMassDplusToPiKPi(candidate), isDplusSignal); entryDplusHadronGenInfo(isDplusPrompt, isPhysicalPrimary, trackOrigin); + const auto [ptB, deltaPhiBtoD] = hf_correlations::getBMotherInfoMcRec(candidate, mcParticles, Pdg::kDPlus, DepthMaxDplusDaughters); + entryDplusHadronBMotherInfo(ptB, deltaPhiBtoD); for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { outputMl[iclass] = candidate.mlProbDplusToPiKPi()[classMl->at(iclass)]; } @@ -880,7 +894,7 @@ struct HfCorrelatorDplusHadrons { void processMcGenMixedEvent(SelCollisionsWithDplusMc const& collisions, CandDplusMcGen const& mcParticles) { - BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}, true}; + BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair const pairMcGen{corrBinningMcGen, numberEventsMixed, -1, collisions, tracksTuple, &cache}; for (const auto& [c1, tracks1, c2, tracks2] : pairMcGen) { @@ -911,6 +925,8 @@ struct HfCorrelatorDplusHadrons { poolBin); entryDplusHadronRecoInfo(MassDPlus, true); entryDplusHadronGenInfo(isDplusPrompt, particleAssoc.isPhysicalPrimary(), trackOrigin); + const auto [ptB, deltaPhiBtoD] = hf_correlations::getBMotherInfoMcGen(candidate, mcParticles); + entryDplusHadronBMotherInfo(ptB, deltaPhiBtoD); } } } diff --git a/PWGHF/HFC/TableProducer/correlatorDsHadrons.cxx b/PWGHF/HFC/TableProducer/correlatorDsHadrons.cxx index eabedd61556..7404d0f6a61 100644 --- a/PWGHF/HFC/TableProducer/correlatorDsHadrons.cxx +++ b/PWGHF/HFC/TableProducer/correlatorDsHadrons.cxx @@ -255,8 +255,8 @@ struct HfCorrelatorDsHadrons { AxisSpec axisMassD = {binsMassD, "inv. mass (K^{#pm}K^{-}#pi^{+}) (GeV/#it{c}^{2})"}; AxisSpec axisEta = {binsEta, "#it{#eta}"}; AxisSpec axisPhi = {binsPhi, "#it{#varphi}"}; - AxisSpec axisPtD = {(std::vector)binsPtD, "#it{p}_{T} (GeV/#it{c})"}; - AxisSpec axisPtHadron = {(std::vector)binsPtHadron, "#it{p}_{T} Hadron (GeV/#it{c})"}; + AxisSpec axisPtD = {std::vector(binsPtD), "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec axisPtHadron = {std::vector(binsPtHadron), "#it{p}_{T} Hadron (GeV/#it{c})"}; AxisSpec const axisMultiplicity = {binsMultiplicity, "Multiplicity"}; AxisSpec axisMultFT0M = {binsMultFT0M, "MultiplicityFT0M"}; AxisSpec const axisPosZ = {binsPosZ, "PosZ"}; @@ -443,7 +443,7 @@ struct HfCorrelatorDsHadrons { CandDsData const& candidates, MyTracksData const& tracks) { - BinningType const corrBinning{{zPoolBins, multPoolBins}, true}; + BinningType const corrBinning{{zPoolBins, multPoolBins}}; registry.fill(HIST("hZVtx"), collision.posZ()); registry.fill(HIST("hMultFT0M"), collision.multFT0M()); int poolBin = corrBinning.getBin(std::make_tuple(collision.posZ(), collision.multFT0M())); @@ -529,7 +529,7 @@ struct HfCorrelatorDsHadrons { TracksWithMc const& tracks, aod::McParticles const& mcParticles) { - BinningType const corrBinning{{zPoolBins, multPoolBins}, true}; + BinningType const corrBinning{{zPoolBins, multPoolBins}}; registry.fill(HIST("hZVtx"), collision.posZ()); registry.fill(HIST("hMultFT0M"), collision.multFT0M()); int poolBin = corrBinning.getBin(std::make_tuple(collision.posZ(), collision.multFT0M())); @@ -673,7 +673,6 @@ struct HfCorrelatorDsHadrons { entryTrackRecoInfo(track.dcaXY(), track.dcaZ(), track.tpcNClsCrossedRows()); } else if (isCorrectInvMassHypo && candidate.isSelDsToPiKK() >= selectionFlagDs) { // if (track.has_mcParticle()) { - auto mcParticle = track.template mcParticle_as(); entryDsHadronPair(getDeltaPhi(track.phi(), candidate.phi()), track.eta() - candidate.eta(), candidate.pt() * chargeDs, @@ -706,7 +705,7 @@ struct HfCorrelatorDsHadrons { soa::Join const& collisions, CandDsMcGen const& mcParticles) { - BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBins}, true}; + BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBins}}; for (const auto& mcCollision : mcCollisions) { @@ -859,13 +858,14 @@ struct HfCorrelatorDsHadrons { outputMl[iclass] = candidate.mlProbDsToKKPi()[classMl->at(iclass)]; } candReduced(indexHfcReducedCollision, candidate.phi(), candidate.eta(), candidate.pt() * chargeDs, HfHelper::invMassDsToKKPi(candidate), candidate.prong0Id(), candidate.prong1Id(), candidate.prong2Id()); - candSelInfo(indexHfcReducedCollision, outputMl[0], outputMl[2]); + // Check compatibility with the candidate selector options; candSelInfo considers BdtScoreBkg, BdtScorePrompt, BdtScoreNonPrompt + candSelInfo(indexHfcReducedCollision, outputMl[2], outputMl[0], outputMl[1]); // bkg, prompt, non-prompt (Ds convention: outputMl[0] = prompt, outputMl[2] = bkg) } else if (candidate.isSelDsToPiKK() >= selectionFlagDs) { for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { outputMl[iclass] = candidate.mlProbDsToPiKK()[classMl->at(iclass)]; } candReduced(indexHfcReducedCollision, candidate.phi(), candidate.eta(), candidate.pt() * chargeDs, HfHelper::invMassDsToPiKK(candidate), candidate.prong0Id(), candidate.prong1Id(), candidate.prong2Id()); - candSelInfo(indexHfcReducedCollision, outputMl[0], outputMl[2]); + candSelInfo(indexHfcReducedCollision, outputMl[2], outputMl[0], outputMl[1]); // bkg, prompt, non-prompt (Ds convention: outputMl[0] = prompt, outputMl[2] = bkg) } } @@ -918,7 +918,7 @@ struct HfCorrelatorDsHadrons { CandDsData const& candidates, MyTracksData const& tracks) { - BinningType const corrBinning{{zPoolBins, multPoolBins}, true}; + BinningType const corrBinning{{zPoolBins, multPoolBins}}; for (const auto& collision : collisions) { registry.fill(HIST("hMultFT0M"), collision.multFT0M()); registry.fill(HIST("hZVtx"), collision.posZ()); @@ -994,7 +994,7 @@ struct HfCorrelatorDsHadrons { TracksWithMc const& tracks, aod::McParticles const& mcParticles) { - BinningType const corrBinning{{zPoolBins, multPoolBins}, true}; + BinningType const corrBinning{{zPoolBins, multPoolBins}}; for (const auto& candidate : candidates) { if (std::abs(HfHelper::yDs(candidate)) > yCandMax || candidate.pt() < ptCandMin || candidate.pt() > ptCandMax) { continue; @@ -1085,7 +1085,7 @@ struct HfCorrelatorDsHadrons { void processMcGenME(SelCollisionsMc const& collisions, CandDsMcGen const& mcParticles) { - BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBins}, true}; + BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBins}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair const pairMcGen{corrBinningMcGen, numberEventsMixed, -1, collisions, tracksTuple, &cache}; for (const auto& [c1, tracks1, c2, tracks2] : pairMcGen) { diff --git a/PWGHF/HFC/TableProducer/correlatorDsHadronsReduced.cxx b/PWGHF/HFC/TableProducer/correlatorDsHadronsReduced.cxx index 4365220fa88..d72373f1b9b 100644 --- a/PWGHF/HFC/TableProducer/correlatorDsHadronsReduced.cxx +++ b/PWGHF/HFC/TableProducer/correlatorDsHadronsReduced.cxx @@ -88,8 +88,8 @@ struct HfCorrelatorDsHadronsReduced { AxisSpec const axisPoolBin = {binsPoolBin, "PoolBin"}; AxisSpec axisEta = {binsEta, "#it{#eta}"}; AxisSpec axisPhi = {binsPhi, "#it{#varphi}"}; - AxisSpec axisPtD = {(std::vector)binsPtD, "#it{p}_{T} (GeV/#it{c})"}; - AxisSpec axisPtHadron = {(std::vector)binsPtHadron, "#it{p}_{T} Hadron (GeV/#it{c})"}; + AxisSpec axisPtD = {std::vector(binsPtD), "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec axisPtHadron = {std::vector(binsPtHadron), "#it{p}_{T} Hadron (GeV/#it{c})"}; // Histograms for data analysis if (fillHistoData) { @@ -110,7 +110,7 @@ struct HfCorrelatorDsHadronsReduced { soa::Join const& tracks) { - BinningTypeDerived const corrBinning{{zPoolBins, multPoolBins}, true}; + BinningTypeDerived const corrBinning{{zPoolBins, multPoolBins}}; for (const auto& collision : collisions) { int poolBin = corrBinning.getBin(std::make_tuple(collision.posZ(), collision.multiplicity())); @@ -157,7 +157,7 @@ struct HfCorrelatorDsHadronsReduced { aod::AssocTrackReds const& tracks) { - BinningTypeDerived const corrBinning{{zPoolBins, multPoolBins}, true}; + BinningTypeDerived const corrBinning{{zPoolBins, multPoolBins}}; for (const auto& collision : collisions) { int const poolBin = corrBinning.getBin(std::make_tuple(collision.posZ(), collision.multiplicity())); diff --git a/PWGHF/HFC/TableProducer/correlatorDstarHadrons.cxx b/PWGHF/HFC/TableProducer/correlatorDstarHadrons.cxx index ff27fa3f3e9..aedb3fadca9 100644 --- a/PWGHF/HFC/TableProducer/correlatorDstarHadrons.cxx +++ b/PWGHF/HFC/TableProducer/correlatorDstarHadrons.cxx @@ -179,7 +179,7 @@ struct HfCorrelatorDstarHadrons { ConfigurableAxis binsMultiplicity{"binsMultiplicity", {VARIABLE_WIDTH, 0.0f, 2000.0f, 6000.0f, 100000.0f}, "Mixing bins - multiplicity"}; ConfigurableAxis binsZVtx{"binsZVtx", {VARIABLE_WIDTH, -10.0f, -2.5f, 2.5f, 10.0f}, "Mixing bins - z-vertex"}; - BinningType binningScheme{{binsZVtx, binsMultiplicity}, true}; + BinningType binningScheme{{binsZVtx, binsMultiplicity}}; // Eta Phi Axes ConfigurableAxis axisEta{"axisEta", {16, -1.0, 1.0}, "Eta Axis"}; ConfigurableAxis axisPhi{"axisPhi", {64, 0.0, 3.14}, "Phi Axis"}; @@ -203,7 +203,7 @@ struct HfCorrelatorDstarHadrons { invMassD0Particle = -999.0; binNumber = -2; - binningScheme = {{binsZVtx, binsMultiplicity}, true}; + binningScheme = {{binsZVtx, binsMultiplicity}}; registry.add("QA/hMultFT0M", "Multiplicity distribution in FT0M", {HistType::kTH1D, {axisSpecMultFT0M}}); registry.add("QA/hCandsPerCol", "Candidates per Collision", {HistType::kTH1D, {{100, 0.0, 100.0}}}); diff --git a/PWGHF/HFC/TableProducer/correlatorFlowCharmHadronsReduced.cxx b/PWGHF/HFC/TableProducer/correlatorFlowCharmHadronsReduced.cxx index eb62101c165..9740b182ae1 100644 --- a/PWGHF/HFC/TableProducer/correlatorFlowCharmHadronsReduced.cxx +++ b/PWGHF/HFC/TableProducer/correlatorFlowCharmHadronsReduced.cxx @@ -17,6 +17,7 @@ #include "Common/Core/RecoDecay.h" +#include #include #include #include @@ -33,9 +34,17 @@ #include #include +#include +#include +#include +#include +#include + +#include #include #include #include +#include #include #include @@ -82,22 +91,24 @@ double getPhi(const TTrack& track) } } +enum HistoEffType { + kTH1 = 0, + kTH2, + kTH3, + kTHnSparse +}; + struct HfCorrelatorFlowCharmHadronsReduced { - // Produces rowPairSECharmHads; //! Correlation pairs information Same Event - // Produces rowPairMECharmHads; //! Correlation pairs information Mixed Event - // Produces rowPairSEHadHads; //! Correlation pairs information Same Event - // Produces rowPairMEHadHads; //! Correlation pairs information Mixed Event - // Produces rowCollInfos; //! Collision info - - Configurable fillSparses{"fillSparses", true, "Fill sparse histograms"}; - Configurable fillTables{"fillTables", false, "Fill tables"}; + Configurable numberEventsMixed{"numberEventsMixed", 5, "Number of events mixed in ME process"}; - Configurable> binsPtTrig{"binsPtTrig", std::vector{0., 3., 5., 8., 16., 36.}, "pT bin limits for trigger candidates"}; Configurable> bdtScore0PtMaxs{"bdtScore0PtMaxs", std::vector{0.1, 0.1, 0.1, 0.1, 0.1}, "pT-differential maximum score 0 for charm candidates"}; Configurable> bdtScore1PtMins{"bdtScore1PtMins", std::vector{0.1, 0.1, 0.1, 0.1, 0.1}, "pT-differential minimum score 1 for charm candidates"}; - Configurable> binsPtAssoc{"binsPtAssoc", std::vector{0.3, 1., 2., 50.}, "pT bin limits for associated particles"}; - Configurable centralityMin{"centralityMin", 0, "min. centrality"}; - Configurable centralityMax{"centralityMax", 10., "max. centrality"}; + Configurable selCentrality{"selCentrality", false, "Use centrality to select events"}; + Configurable selMultiplicity{"selMultiplicity", false, "Use multiplicity to select events"}; + Configurable centMin{"centMin", 0, "min. centrality"}; + Configurable centMax{"centMax", 10., "max. centrality"}; + Configurable multMin{"multMin", 0, "min. multiplicity"}; + Configurable multMax{"multMax", 10., "max. multiplicity"}; Configurable deltaEtaAbsMin{"deltaEtaAbsMin", 0.5, "min. pair delta eta"}; Configurable deltaEtaAbsMax{"deltaEtaAbsMax", 2., "max. pair delta eta"}; Configurable dcaXYTrackMax{"dcaXYTrackMax", 1., "max. track DCA XY"}; @@ -106,12 +117,28 @@ struct HfCorrelatorFlowCharmHadronsReduced { Configurable itsNClsMin{"itsNClsMin", 1, "min. ITS clusters"}; Configurable downSamplePairs{"downSamplePairs", 1., "Fraction of pairs to keep"}; Configurable ptMaxForDownSample{"ptMaxForDownSample", 10., "Maximum pt for the application of the downsampling factor"}; - Configurable centMaxForDownSample{"centMaxForDownSample", 10., "Maximum centrality for the application of the downsampling factor"}; + Configurable centMultMaxForDownSample{"centMultMaxForDownSample", 10., "Maximum centrality for the application of the downsampling factor"}; + + // Efficiency correction + Configurable doEfficiencyCorrection{"doEfficiencyCorrection", false, "Flag for applying efficiency corrections"}; + Configurable effHistTypeCharm{"effHistTypeCharm", 0, "Histogram type for charm efficiency (0=TH1, 1=TH2, 2=TH3, 3=THnSparse)"}; + Configurable effHistTypeAssoc{"effHistTypeAssoc", 0, "Histogram type for associated efficiency (0=TH1, 1=TH2, 2=TH3, 3=THnSparse)"}; + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable assocEffCcdbPath{"assocEffCcdbPath", "", "CCDB path for associated efficiency"}; + Configurable promptEffCcdbPath{"promptEffCcdbPath", "", "CCDB path for trigger efficiency"}; + Configurable timestampCcdb{"timestampCcdb", -1, "timestamp of the efficiency files used to query in CCDB"}; + Configurable ccdbNoLaterThan{"ccdbNoLaterThan", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; SliceCache cache; + Service ccdb{}; + std::shared_ptr hEffPrompt = nullptr; + std::shared_ptr hEffAssoc = nullptr; + int poolBins{0}; + std::vector mPtEdgesTrig; + using SameEvtPairsChHadWEffInfo = soa::Filtered>; using SameEvtPairsChHad = soa::Filtered>; using SameEvtPairsHadHad = soa::Filtered>; using AssocTracks = soa::Filtered>; @@ -124,41 +151,79 @@ struct HfCorrelatorFlowCharmHadronsReduced { Preslice assocTracksPerCol = aod::hf_correl_charm_had_reduced::hfcRedCorrCollId; Preslice trigCharmCandsPerCol = aod::hf_correl_charm_had_reduced::hfcRedCorrCollId; - ConfigurableAxis zPoolBins{"zPoolBins", {VARIABLE_WIDTH, -10.0, -2.5, 2.5, 10.0}, "Z vertex position pools"}; - ConfigurableAxis multPoolBins{"multPoolBins", {VARIABLE_WIDTH, 0., 900., 1800., 6000.}, "Event multiplicity pools (FT0M)"}; - ConfigurableAxis centPoolBins{"centPoolBins", {VARIABLE_WIDTH, 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100}, "Event centrality pools"}; + ConfigurableAxis binsPtTrig{"binsPtTrig", {VARIABLE_WIDTH, 0., 1., 2., 3., 4., 5., 6., 8., 10., 12., 16., 24.}, "Trigger candidate pT bins"}; + ConfigurableAxis binsPtAssoc{"binsPtAssoc", {VARIABLE_WIDTH, 0.3, 1., 2., 50.}, "Associated particle pT bins"}; + ConfigurableAxis binsZPool{"binsZPool", {VARIABLE_WIDTH, -10.0, -2.5, 2.5, 10.0}, "Z vertex position pools"}; + ConfigurableAxis binsMultPool{"binsMultPool", {VARIABLE_WIDTH, 0., 900., 1800., 6000.}, "Event multiplicity pools (FT0M)"}; + ConfigurableAxis binsCentPool{"binsCentPool", {VARIABLE_WIDTH, 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100}, "Event centrality pools"}; ConfigurableAxis binsInvMass{"binsInvMass", {300, 1.6, 2.2}, "Invariant mass bins"}; ConfigurableAxis binsMultFT0M{"binsMultFT0M", {100, 0., 10000.}, "Multiplicity as FT0M signal amplitude"}; ConfigurableAxis binsCent{"binsCent", {100, 0., 100.}, "Centrality bins"}; ConfigurableAxis binsPosZ{"binsPosZ", {100, -10., 10.}, "Primary vertex z coordinate"}; - ConfigurableAxis binsEta{"binsEta", {50, -2., 2.}, "Eta bins"}; - ConfigurableAxis binsPhi{"binsPhi", {64, -o2::constants::math::PIHalf, 3. * o2::constants::math::PIHalf}, "Phi bins"}; ConfigurableAxis binsDeltaEta{"binsDeltaEta", {100, -2., 2.}, "Delta Eta bins"}; - ConfigurableAxis binsDeltaPhi{"binsDeltaPhi", {64, -3., 3.}, "Delta Phi bins"}; + ConfigurableAxis binsDeltaPhi{"binsDeltaPhi", {64, -o2::constants::math::PIHalf, 3. * o2::constants::math::PIHalf}, "Delta Phi bins"}; ConfigurableAxis binsMlOne{"binsMlOne", {100, 0., 1.}, "ML score index 1 bins"}; ConfigurableAxis binsMlTwo{"binsMlTwo", {100, 0., 1.}, "ML score index 2 bins"}; HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; + std::shared_ptr loadEfficiencyHistogram(const std::string& path, int histoType) + { + TObject* object = nullptr; + + switch (histoType) { + case kTH1: + object = ccdb->getForTimeStamp(Form("%s/TH1/", path.c_str()), timestampCcdb); + break; + case kTH2: + object = ccdb->getForTimeStamp(Form("%s/TH2/", path.c_str()), timestampCcdb); + break; + case kTH3: + object = ccdb->getForTimeStamp(Form("%s/TH3/", path.c_str()), timestampCcdb); + break; + case kTHnSparse: + object = ccdb->getForTimeStamp(Form("%s/THnSparse/", path.c_str()), timestampCcdb); + break; + default: + LOGF(fatal, "Invalid efficiency histogram type: %d", histoType); + } + + if (!object) { + LOGF(fatal, "Could not load efficiency histogram from %s", path.c_str()); + } + + return std::shared_ptr(object); + } + void init(InitContext&) { - if ((doprocessSameEventCharmHadWCentMix && doprocessMixedEventCharmHadWMultMix) || - (doprocessSameEventCharmHadWMultMix && doprocessMixedEventCharmHadWCentMix) || - (doprocessSameEventHadHadWCentMix && doprocessMixedEventHadHadWMultMix) || - (doprocessSameEventHadHadWMultMix && doprocessMixedEventHadHadWCentMix)) { - LOGP(fatal, "You cannot mix centrality and multiplicity mixing in the same processing! Please check your configuration!"); + if (selCentrality && selMultiplicity) { + LOGP(fatal, "You cannot select events based on both centrality and multiplicity! Please check your configuration!"); } - if (!fillSparses && !fillTables) { - LOGP(fatal, "At least one of fillSparses or fillTables must be true!"); + if (!selCentrality && !selMultiplicity) { + LOGP(fatal, "You must select events based on either centrality or multiplicity! Please check your configuration!"); } - if ((binsPtTrig.value.size() != (bdtScore0PtMaxs.value.size() + 1) || binsPtTrig.value.size() != (bdtScore1PtMins.value.size() + 1))) { + + if ((doprocessSECharmHadWCentMix && doprocessMECharmHadWMultMix) || + (doprocessSECharmHadWMultMix && doprocessMECharmHadWCentMix) || + (doprocessSEHadHadWCentMix && doprocessMEHadHadWMultMix) || + (doprocessSEHadHadWMultMix && doprocessMEHadHadWCentMix)) { + LOGP(fatal, "You cannot mix centrality and multiplicity mixing in the same processing! Please check your configuration!"); + } + + mPtEdgesTrig.assign(binsPtTrig.value.begin() + 1, binsPtTrig.value.end()); + if ((mPtEdgesTrig.size() != (bdtScore0PtMaxs.value.size() + 1) || + mPtEdgesTrig.size() != (bdtScore1PtMins.value.size() + 1))) { + LOG(info) << "mPtEdgesTrig.size(): " << mPtEdgesTrig.size(); + LOG(info) << "bdtScore0PtMaxs.value.size(): " << bdtScore0PtMaxs.value.size(); + LOG(info) << "bdtScore1PtMins.value.size(): " << bdtScore1PtMins.value.size(); LOGP(fatal, "The size of bdtScore0PtMaxs and bdtScore1PtMins must be the one of binsPtTrig minus one!"); } - if (doprocessSameEventCharmHadWCentMix || doprocessSameEventHadHadWCentMix || doprocessMixedEventCharmHadWCentMix || doprocessMixedEventHadHadWCentMix || doprocessSameEventCharmHadWCentMixBase || doprocessMixedEventCharmHadWCentMixBase) { - poolBins = (centPoolBins->size() - 2) * (zPoolBins->size() - 2); + if (doprocessSECharmHadWCentMix || doprocessSEHadHadWCentMix || doprocessMECharmHadWCentMix || doprocessMEHadHadWCentMix) { + poolBins = (binsCentPool->size() - 2) * (binsZPool->size() - 2); } else { - poolBins = (multPoolBins->size() - 2) * (zPoolBins->size() - 2); + poolBins = (binsMultPool->size() - 2) * (binsZPool->size() - 2); } const AxisSpec axisInvMass{binsInvMass, "Inv. mass (GeV/#it{c}^{2})"}; @@ -168,47 +233,149 @@ struct HfCorrelatorFlowCharmHadronsReduced { const AxisSpec axisPoolBin = {poolBins, 0., static_cast(poolBins), "PoolBin"}; const AxisSpec axisDeltaEta = {binsDeltaEta, "#Delta#it{#eta}"}; const AxisSpec axisDeltaPhi = {binsDeltaPhi, "#Delta#it{#varphi}"}; - const AxisSpec axisPtTrig = {(std::vector)binsPtTrig, "#it{p}_{T} Trig (GeV/#it{c})"}; - const AxisSpec axisPtAssoc = {(std::vector)binsPtAssoc, "#it{p}_{T} Assoc (GeV/#it{c})"}; + const AxisSpec axisPtTrig = {binsPtTrig, "#it{p}_{T} Trig (GeV/#it{c})"}; + const AxisSpec axisPtAssoc = {binsPtAssoc, "#it{p}_{T} Assoc (GeV/#it{c})"}; const AxisSpec axisMlOne{binsMlOne, "bdtScore0"}; const AxisSpec axisMlTwo{binsMlTwo, "bdtScore1"}; // Histograms for data analysis - if (doprocessSameEventCharmHadWCentMix || doprocessSameEventHadHadWCentMix) { + if (doprocessSECharmHadWCentMix || doprocessSECharmHadWCentMixWEffInfo || doprocessSEHadHadWCentMix) { registry.add("hCentPoolBinSE", "Centrality SE", {HistType::kTH2F, {{axisCent}, {axisPoolBin}}}); - } else if (doprocessSameEventCharmHadWMultMix || doprocessSameEventHadHadWMultMix) { + } else if (doprocessSECharmHadWMultMix || doprocessSECharmHadWMultMixWEffInfo || doprocessSEHadHadWMultMix) { registry.add("hMultFT0MPoolBinSE", "Multiplicity FT0M SE", {HistType::kTH2F, {{axisMultFT0M}, {axisPoolBin}}}); - } else if (doprocessMixedEventCharmHadWCentMix || doprocessMixedEventHadHadWCentMix) { + } else if (doprocessMECharmHadWCentMix || doprocessMECharmHadWCentMixWEffInfo || doprocessMEHadHadWCentMix) { registry.add("hCentPoolBinME", "Centrality ME", {HistType::kTH2F, {{axisCent}, {axisPoolBin}}}); - } else if (doprocessMixedEventCharmHadWMultMix || doprocessMixedEventHadHadWMultMix) { + } else if (doprocessMECharmHadWMultMix || doprocessMECharmHadWMultMixWEffInfo || doprocessMEHadHadWMultMix) { registry.add("hMultFT0MPoolBinME", "Multiplicity FT0M ME", {HistType::kTH2F, {{axisMultFT0M}, {axisPoolBin}}}); } registry.add("hZVtxPoolBinSE", "z vertex SE", {HistType::kTH2F, {{axisPosZ}, {axisPoolBin}}}); registry.add("hZVtxPoolBinME", "z vertex ME", {HistType::kTH2F, {{axisPosZ}, {axisPoolBin}}}); + registry.add("hCollisionsCentMultAll", "Collision centrality vs multiplicity", {HistType::kTH2F, {{axisCent}, {axisMultFT0M}}}); + registry.add("hCollisionsCentMultSel", "Collision centrality vs multiplicity", {HistType::kTH2F, {{axisCent}, {axisMultFT0M}}}); registry.add("hPoolBinTrigSE", "Trigger candidates pool bin SE", {HistType::kTH1F, {axisPoolBin}}); registry.add("hPoolBinTrigME", "Trigger candidates pool bin ME", {HistType::kTH1F, {axisPoolBin}}); registry.add("hPoolBinAssocSE", "Associated particles pool bin SE", {HistType::kTH1F, {axisPoolBin}}); registry.add("hPoolBinAssocME", "Associated particles pool bin ME", {HistType::kTH1F, {axisPoolBin}}); - if (fillSparses) { - std::vector axesTrigger = {axisInvMass, axisPtTrig, axisMlOne, axisMlTwo}; - std::vector axes = {axisPoolBin, axisPtTrig, axisPtAssoc, axisDeltaEta, axisDeltaPhi}; - if (doprocessSameEventHadHadWCentMix || doprocessSameEventHadHadWMultMix) { - registry.add("hSparseCorrelationsSEHadHad", "THn for SE Had-Had correlations", HistType::kTHnSparseF, axes); - } else if (doprocessMixedEventHadHadWCentMix || doprocessMixedEventHadHadWMultMix) { - registry.add("hSparseCorrelationsMEHadHad", "THn for ME Had-Had correlations", HistType::kTHnSparseF, axes); - } else { - axes.insert(axes.end(), {axisInvMass}); - // axes.insert(axes.end(), {axisInvMass, axisMlOne, axisMlTwo}); - if (doprocessSameEventCharmHadWCentMix || doprocessSameEventCharmHadWMultMix || doprocessSameEventCharmHadWCentMixBase) { - registry.add("hSparseCorrelationsSECharmHad", "THn for SE Charm-Had correlations", HistType::kTHnSparseF, axes); - } else if (doprocessMixedEventCharmHadWCentMix || doprocessMixedEventCharmHadWMultMix || doprocessMixedEventCharmHadWCentMixBase) { - registry.add("hSparseCorrelationsMECharmHad", "THn for ME Charm-Had correlations", HistType::kTHnSparseF, axes); + + std::vector axesTrigger = {axisInvMass, axisPtTrig, axisMlOne, axisMlTwo}; + std::vector axes = {axisPoolBin, axisPtTrig, axisPtAssoc, axisDeltaEta, axisDeltaPhi}; + if (doprocessSEHadHadWCentMix || doprocessSEHadHadWMultMix) { + registry.add("hSparseCorrelationsSEHadHad", "THn for SE Had-Had correlations", HistType::kTHnSparseF, axes); + if (doEfficiencyCorrection) { + registry.get(HIST("hSparseCorrelationsSEHadHad"))->Sumw2(); + } + } else if (doprocessMEHadHadWCentMix || doprocessMEHadHadWMultMix) { + registry.add("hSparseCorrelationsMEHadHad", "THn for ME Had-Had correlations", HistType::kTHnSparseF, axes); + if (doEfficiencyCorrection) { + registry.get(HIST("hSparseCorrelationsMEHadHad"))->Sumw2(); + } + } else { + axes.insert(axes.end(), {axisInvMass, axisMlOne, axisMlTwo}); + if (doprocessSECharmHadWCentMix || doprocessSECharmHadWCentMixWEffInfo || doprocessSECharmHadWMultMix || doprocessSECharmHadWMultMixWEffInfo) { + registry.add("hSparseCorrelationsSECharmHad", "THn for SE Charm-Had correlations", HistType::kTHnSparseF, axes); + if (doEfficiencyCorrection) { + registry.get(HIST("hSparseCorrelationsSECharmHad"))->Sumw2(); } - if (doprocessCharmTriggers) { - registry.add("hSparseTrigCandsCharm", "THn for Charm trigger candidates", HistType::kTHnSparseF, axesTrigger); + } else if (doprocessMECharmHadWCentMix || doprocessMECharmHadWMultMixWEffInfo || doprocessMECharmHadWMultMix || doprocessMECharmHadWCentMixWEffInfo) { + registry.add("hSparseCorrelationsMECharmHad", "THn for ME Charm-Had correlations", HistType::kTHnSparseF, axes); + if (doEfficiencyCorrection) { + registry.get(HIST("hSparseCorrelationsMECharmHad"))->Sumw2(); + } + } + if (doprocessCharmTriggers || doprocessCharmTriggersWEffInfo) { + registry.add("hSparseTrigCandsCharm", "THn for Charm trigger candidates", HistType::kTHnSparseF, axesTrigger); + if (doEfficiencyCorrection) { + registry.get(HIST("hSparseTrigCandsCharm"))->Sumw2(); } } } + + if (doEfficiencyCorrection) { + hEffPrompt = loadEfficiencyHistogram(promptEffCcdbPath, effHistTypeCharm); + hEffAssoc = loadEfficiencyHistogram(assocEffCcdbPath, effHistTypeAssoc); + } + } + + template + double getEfficiency(TObject* hist, int histoType, const TCand& cand) //, const std::vector& values) + { + if (!hist) { + LOG(debug) << "Candidate does not have required efficiency histogram."; + return 0.; + } + + std::vector kineFeatures{}; + + // Charm triggers + if constexpr (IsCharm) { + kineFeatures = {cand.ptTrig(), cand.bdtScore0Trig(), cand.bdtScore1Trig(), cand.etaTrig(), cand.phiTrig()}; + } else { + auto collision = cand.template hfcRedCorrColl_as(); + kineFeatures = {cand.ptAssoc(), cand.etaAssoc(), cand.phiAssoc(), collision.posZ()}; + } + + switch (histoType) { + + case kTH1: { + auto* h = dynamic_cast(hist); + if (!h || kineFeatures.size() < 1) { + return 0.; + } + return h->GetBinContent(h->FindBin(kineFeatures[0])); + } + + case kTH2: { + auto* h = dynamic_cast(hist); + if (!h || kineFeatures.size() < 2) { + return 0.; + } + return h->GetBinContent(h->FindBin(kineFeatures[0], kineFeatures[1])); + } + + case kTH3: { + auto* h = dynamic_cast(hist); + if (!h || kineFeatures.size() < 3) { + return 0.; + } + return h->GetBinContent( + h->FindBin(kineFeatures[0], kineFeatures[1], kineFeatures[2])); + } + + case kTHnSparse: { + auto* h = dynamic_cast(hist); + if (!h || kineFeatures.size() < static_cast(h->GetNdimensions())) { + return 0.; + } + + std::vector bins(h->GetNdimensions()); + + for (int i = 0; i < h->GetNdimensions(); ++i) { + bins[i] = h->GetAxis(i)->FindBin(kineFeatures[i]); + } + + return h->GetBinContent(bins.data()); + } + + default: + return 0.; + } + } + + bool isValidEfficiency(double efficiency) + { + return std::isfinite(efficiency) && efficiency > 0.; + } + + template + bool isSelCollision(const TColl& collision) + { + bool isSel = false; + if (selCentrality) { + isSel = (collision.centrality() >= centMin && collision.centrality() <= centMax); + } else if (selMultiplicity) { + isSel = (collision.multiplicity() >= multMin && collision.multiplicity() <= multMax); + } + return isSel; } /// Get the binning pool associated to the collision @@ -246,8 +413,8 @@ struct HfCorrelatorFlowCharmHadronsReduced { bool isSelBdtScoreCut(TCand const& cand, double ptTrig) { - for (size_t iPt = 0; iPt < binsPtTrig.value.size() - 1; iPt++) { - if (ptTrig >= binsPtTrig.value[iPt] && ptTrig < binsPtTrig.value[iPt + 1]) { + for (size_t iPt = 0; iPt < mPtEdgesTrig.size() - 1; iPt++) { + if (ptTrig >= mPtEdgesTrig[iPt] && ptTrig < mPtEdgesTrig[iPt + 1]) { return (cand.bdtScore0Trig() < bdtScore0PtMaxs.value[iPt]) && (cand.bdtScore1Trig() > bdtScore1PtMins.value[iPt]); } } @@ -259,15 +426,18 @@ struct HfCorrelatorFlowCharmHadronsReduced { /// \param trigCands are the selected trigger candidates /// \param assocTracks are the selected associated tracks /// \param binPolicy is the binning policy for the correlation - template + template void fillSameEvent(TPair const& pair, TTrigCand const& trigCand, const TBinningType& binPolicy) { auto collision = pair.template hfcRedCorrColl_as(); - if (collision.centrality() < centralityMin || collision.centrality() > centralityMax) { + registry.fill(HIST("hCollisionsCentMultAll"), collision.centrality(), collision.multiplicity()); + if (!isSelCollision(collision)) { return; } + registry.fill(HIST("hCollisionsCentMultSel"), collision.centrality(), collision.multiplicity()); + double const ptTrig = trigCand.ptTrig(); if constexpr (requires { trigCand.bdtScore0Trig(); }) { // ML selection on bkg score for Charm-Had case if (!isSelBdtScoreCut(trigCand, ptTrig)) { @@ -276,32 +446,61 @@ struct HfCorrelatorFlowCharmHadronsReduced { } if (downSamplePairs < 1.) { float const pseudoRndm = ptTrig * 1000. - static_cast(ptTrig * 1000); - if (ptTrig < ptMaxForDownSample && collision.centrality() < centMaxForDownSample && pseudoRndm >= downSamplePairs) { + float collCentMult = selCentrality ? collision.centrality() : collision.multiplicity(); + if (ptTrig < ptMaxForDownSample && collCentMult < centMultMaxForDownSample && pseudoRndm >= downSamplePairs) { return; } } int const poolBin = getPoolBin(collision, binPolicy); registry.fill(HIST("hPoolBinTrigSE"), poolBin); registry.fill(HIST("hPoolBinAssocSE"), poolBin); - // if constexpr (FillTables) { - // if constexpr (requires { trigCand.bdtScore0Trig(); }) { // Separate Charm-Had and Had-Had cases - // rowPairSECharmHads(poolBin, ptTrig, pair.ptAssoc(), pair.deltaEta(), pair.deltaPhi(), - // trigCand.invMassTrig(), trigCand.bdtScore0Trig(), trigCand.bdtScore1Trig(), - // pair.nTpcCrossedRowsAssoc(), pair.itsClsMapAssoc(), pair.itsNClsAssoc(), pair.dcaXYAssoc(), pair.dcaZAssoc()); - // } else { - // rowPairSEHadHads(poolBin, ptTrig, pair.ptAssoc(), pair.deltaEta(), pair.deltaPhi(), - // trigCand.nTpcCrossedRowsTrig(), trigCand.itsClsMapTrig(), trigCand.itsNClsTrig(), trigCand.dcaXYTrig(), trigCand.dcaZTrig(), - // pair.nTpcCrossedRowsAssoc(), pair.itsClsMapAssoc(), pair.itsNClsAssoc(), pair.dcaXYAssoc(), pair.dcaZAssoc()); - // } - // rowCollInfos(collision.multiplicity(), collision.numPvContrib(), collision.centrality()); - // } - if constexpr (FillSparses) { - if constexpr (requires { trigCand.bdtScore0Trig(); }) { // Separate Charm-Had and Had-Had cases - registry.fill(HIST("hSparseCorrelationsSECharmHad"), poolBin, ptTrig, pair.ptAssoc(), pair.deltaEta(), - pair.deltaPhi(), trigCand.invMassTrig()); // , trigCand.bdtScore0Trig(), trigCand.bdtScore1Trig()); - } else { - registry.fill(HIST("hSparseCorrelationsSEHadHad"), poolBin, ptTrig, pair.ptAssoc(), pair.deltaEta(), pair.deltaPhi()); + + double effAssoc{1.f}; + if constexpr (CorrectEff) { + if (doEfficiencyCorrection) { + effAssoc = getEfficiency(hEffAssoc.get(), effHistTypeAssoc.value, pair); + if (!isValidEfficiency(effAssoc)) { + LOGF(warn, "Invalid efficiency value for associated track: effAssoc = %f", effAssoc); + return; + } + } + } + + if constexpr (requires { trigCand.bdtScore0Trig(); }) { // Separate Charm-Had and Had-Had cases + + double effTrig{1.f}; + if constexpr (CorrectEff) { + if (doEfficiencyCorrection) { + effTrig = getEfficiency(hEffPrompt.get(), effHistTypeCharm.value, trigCand); + if (!isValidEfficiency(effTrig)) { + LOGF(warn, "Invalid efficiency value for trigger candidate: effTrig = %f", effTrig); + return; + } + } + } + + double weight = doEfficiencyCorrection ? 1 / (effTrig * effAssoc) : 1.f; + + std::vector values = {static_cast(poolBin), ptTrig, pair.ptAssoc(), pair.deltaEta(), pair.deltaPhi(), trigCand.invMassTrig(), trigCand.bdtScore0Trig(), trigCand.bdtScore1Trig()}; + + registry.get(HIST("hSparseCorrelationsSECharmHad"))->Fill(values.data(), weight); + } else { + + double effTrig{1.f}; + if constexpr (CorrectEff) { + if (doEfficiencyCorrection) { + effTrig = getEfficiency(hEffAssoc.get(), effHistTypeAssoc.value, trigCand); + if (!isValidEfficiency(effTrig)) { + LOGF(warn, "Invalid efficiency value for trigger candidate: effTrig = %f", effTrig); + return; + } + } } + + double weight = doEfficiencyCorrection ? 1 / (effTrig * effAssoc) : 1.f; + + std::vector valuesHadHad = {static_cast(poolBin), ptTrig, pair.ptAssoc(), pair.deltaEta(), pair.deltaPhi()}; + registry.get(HIST("hSparseCorrelationsSEHadHad"))->Fill(valuesHadHad.data(), weight); } } @@ -309,7 +508,7 @@ struct HfCorrelatorFlowCharmHadronsReduced { /// \param collisions are the selected collisions /// \param pairs are the mixed event pairs of trigger candidates and associated tracks /// \param binPolicy is the binning policy for the correlation - template + template void fillMixedEvent(TPairs const& pairs, const TBinningType& binPolicy) { @@ -317,10 +516,14 @@ struct HfCorrelatorFlowCharmHadronsReduced { if (trigCands.size() == 0 || assocTracks.size() == 0) { continue; } - if (trigColl.centrality() < centralityMin || trigColl.centrality() > centralityMax || - assocColl.centrality() < centralityMin || assocColl.centrality() > centralityMax) { + registry.fill(HIST("hCollisionsCentMultAll"), trigColl.centrality(), trigColl.multiplicity()); + registry.fill(HIST("hCollisionsCentMultAll"), assocColl.centrality(), assocColl.multiplicity()); + if (!isSelCollision(trigColl) || !isSelCollision(assocColl)) { continue; } + registry.fill(HIST("hCollisionsCentMultSel"), trigColl.centrality(), trigColl.multiplicity()); + registry.fill(HIST("hCollisionsCentMultSel"), assocColl.centrality(), assocColl.multiplicity()); + int const poolBinTrig = getPoolBin(trigColl, binPolicy); int const poolBinAssoc = getPoolBin(assocColl, binPolicy); if (poolBinAssoc != poolBinTrig) { @@ -345,299 +548,226 @@ struct HfCorrelatorFlowCharmHadronsReduced { double const ptAssoc = getPt(assocTrack); if (downSamplePairs < 1.) { float const pseudoRndm = ptAssoc * 1000. - static_cast(ptAssoc * 1000); - if (ptTrig < ptMaxForDownSample && trigColl.centrality() < centMaxForDownSample && - assocColl.centrality() < centMaxForDownSample && pseudoRndm >= downSamplePairs) { + float trigCollCentMult = selCentrality ? trigColl.centrality() : trigColl.multiplicity(); + float assocCollCentMult = selCentrality ? assocColl.centrality() : assocColl.multiplicity(); + if (ptTrig < ptMaxForDownSample && trigCollCentMult < centMultMaxForDownSample && + assocCollCentMult < centMultMaxForDownSample && pseudoRndm >= downSamplePairs) { continue; } } double const deltaPhi = RecoDecay::constrainAngle(getPhi(assocTrack) - getPhi(trigCand), -o2::constants::math::PIHalf); - // if constexpr (FillTables) { - // if constexpr (requires { trigCand.bdtScore0Trig(); }) { // Separate Charm-Had and Had-Had cases - // rowPairMECharmHads(poolBinTrig, ptTrig, ptAssoc, deltaEta, deltaPhi, - // trigCand.invMassTrig(), trigCand.bdtScore0Trig(), trigCand.bdtScore1Trig(), - // assocTrack.nTpcCrossedRowsAssoc(), assocTrack.itsClsMapAssoc(), assocTrack.itsNClsAssoc(), assocTrack.dcaXYAssoc(), assocTrack.dcaZAssoc()); - // } else { - // rowPairMEHadHads(poolBinTrig, ptTrig, ptAssoc, deltaEta, deltaPhi, - // trigCand.nTpcCrossedRowsAssoc(), trigCand.itsClsMapAssoc(), trigCand.itsNClsAssoc(), trigCand.dcaXYAssoc(), trigCand.dcaZAssoc(), - // assocTrack.nTpcCrossedRowsAssoc(), assocTrack.itsClsMapAssoc(), assocTrack.itsNClsAssoc(), assocTrack.dcaXYAssoc(), assocTrack.dcaZAssoc()); - // } - // rowCollInfos(trigColl.multiplicity(), trigColl.numPvContrib(), trigColl.centrality()); - // } - if constexpr (FillSparses) { - if constexpr (requires { trigCand.bdtScore0Trig(); }) { // Separate Charm-Had and Had-Had cases - registry.fill(HIST("hSparseCorrelationsMECharmHad"), poolBinTrig, ptTrig, ptAssoc, deltaEta, - deltaPhi, trigCand.invMassTrig()); //, trigCand.bdtScore0Trig(), trigCand.bdtScore1Trig()); - } else { - registry.fill(HIST("hSparseCorrelationsMEHadHad"), poolBinTrig, ptTrig, ptAssoc, deltaEta, deltaPhi); + + double effAssoc{1.f}; + if (doEfficiencyCorrection) { + effAssoc = getEfficiency(hEffAssoc.get(), effHistTypeAssoc.value, assocTrack); + if (!isValidEfficiency(effAssoc)) { + LOGF(warn, "Invalid efficiency value for associated track: effAssoc = %f", effAssoc); + continue; } } - } - } - } - /// Correlations for Same Event pairs - /// \param poolBin collision pool bin based on multiplicity and z-vertex position - /// \param trigCandsThisColl are the selected trigger candidates in the collision - /// \param assocTracksThisColl are the selected associated tracks in the collision - template - void doCorrelationsSameEvent(int poolBin, - const TTrigCand& trigCandsThisColl, - const TTrackAssoc& assocTracksThisColl) - { - for (const auto& trigCand : trigCandsThisColl) { - double const ptTrig = trigCand.ptTrig(); - if constexpr (requires { trigCand.bdtScore0Trig(); }) { // ML selection on bkg score for Charm-Had case - if (!isSelBdtScoreCut(trigCand, ptTrig)) { - continue; - } - } + if constexpr (requires { trigCand.bdtScore0Trig(); }) { // Separate Charm-Had and Had-Had cases - for (const auto& assTrk : assocTracksThisColl) { - // TODO: Remove Ds daughters - /*if (assTrk.originTrackId() == candidate.prong0Id() || - assTrk.originTrackId() == candidate.prong1Id() || - assTrk.originTrackId() == candidate.prong2Id()) { - continue; - }*/ - // TODO: DCA cut - double deltaPhi = RecoDecay::constrainAngle(assTrk.phiAssoc() - trigCand.phiTrig(), -o2::constants::math::PIHalf); - double deltaEta = assTrk.etaAssoc() - trigCand.etaTrig(); - if constexpr (FillSparses) { - if constexpr (requires { trigCand.bdtScore0Trig(); }) { // Separate Charm-Had and Had-Had cases - registry.fill(HIST("hSparseCorrelationsSECharmHad"), poolBin, ptTrig, assTrk.ptAssoc(), deltaEta, - deltaPhi, trigCand.invMassTrig()); - } else { - registry.fill(HIST("hSparseCorrelationsSEHadHad"), poolBin, ptTrig, assTrk.ptAssoc(), deltaEta, deltaPhi); + double effTrig{1.f}; + if (doEfficiencyCorrection) { + effTrig = getEfficiency(hEffPrompt.get(), effHistTypeCharm.value, trigCand); + if (!isValidEfficiency(effTrig)) { + LOGF(warn, "Invalid efficiency value for trigger candidate: effTrig = %f", effTrig); + continue; + } } - } - } - } - } - /// Correlations for Mixed Event pairs - /// \param collisions are the selected collisions - /// \param trigCands are the trigger candidates - /// \param assocTracks are the associated tracks - /// \param binPolicy is the binning policy for the correlation - template - void doCorrelationsMixedEvent(const TCollision& collisions, - const TTrigCand& trigCands, - const TTrackAssoc& assocTracks, - const TBinningType& binPolicy) - { - auto tracksTuple = std::make_tuple(trigCands, assocTracks); - - Pair pairData{binPolicy, numberEventsMixed, -1, collisions, tracksTuple, &cache}; + double weight = doEfficiencyCorrection ? 1 / (effTrig * effAssoc) : 1.f; - for (const auto& [c1, tracks1, c2, tracks2] : pairData) { - if (tracks1.size() == 0) { - continue; - } + std::vector values = {static_cast(poolBinTrig), ptTrig, ptAssoc, deltaEta, deltaPhi, trigCand.invMassTrig(), trigCand.bdtScore0Trig(), trigCand.bdtScore1Trig()}; + registry.get(HIST("hSparseCorrelationsMECharmHad"))->Fill(values.data(), weight); + } else { - int poolBin = binPolicy.getBin({c2.posZ(), c2.centrality()}); - int poolBinTrigCand = binPolicy.getBin({c1.posZ(), c1.centrality()}); + double effTrig{1.f}; + if (doEfficiencyCorrection) { + effTrig = getEfficiency(hEffAssoc.get(), effHistTypeAssoc.value, trigCand); + if (!isValidEfficiency(effTrig)) { + LOGF(warn, "Invalid efficiency value for trigger candidate: effTrig = %f", effTrig); + continue; + } + } - if (poolBin != poolBinTrigCand) { - LOGF(info, "Error, poolBins are different"); - continue; - } + double weight = doEfficiencyCorrection ? 1 / (effTrig * effAssoc) : 1.f; - for (const auto& [trigCand, assTrk] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { - if (!isSelBdtScoreCut(trigCand, trigCand.ptTrig())) { - continue; - } - LOGF(info, "Mixed event tracks pair: (%d, %d) from events (%d, %d), track event: (%d, %d)", trigCand.index(), assTrk.index(), c1.index(), c2.index(), trigCand.hfcRedCorrCollId(), assTrk.hfcRedCorrCollId()); - - double deltaPhi = RecoDecay::constrainAngle(assTrk.phiAssoc() - trigCand.phiTrig(), -o2::constants::math::PIHalf); - double deltaEta = assTrk.etaAssoc() - trigCand.etaTrig(); - if constexpr (FillSparses) { - if constexpr (requires { trigCand.bdtScore0Trig(); }) { // Separate Charm-Had and Had-Had cases - registry.fill(HIST("hSparseCorrelationsMECharmHad"), poolBin, trigCand.ptTrig(), assTrk.ptAssoc(), deltaEta, - deltaPhi, trigCand.invMassTrig()); - } else { - registry.fill(HIST("hSparseCorrelationsMEHadHad"), poolBin, trigCand.ptTrig(), assTrk.ptAssoc(), deltaEta, deltaPhi); - } + std::vector valuesHadHad = {static_cast(poolBinTrig), ptTrig, ptAssoc, deltaEta, deltaPhi}; + registry.get(HIST("hSparseCorrelationsMEHadHad"))->Fill(valuesHadHad.data(), weight); } } } } - void processSameEventCharmHadWMultMix(SameEvtPairsChHad::iterator const& pair, - aod::HfcRedTrigCharms const&, - aod::HfcRedCorrColls const&) + void processSECharmHadWMultMix(SameEvtPairsChHad::iterator const& pair, + aod::HfcRedTrigCharms const&, + aod::HfcRedCorrColls const&) { - BinningMultPosZ binPolicyPosZMult{{zPoolBins, multPoolBins}, true}; + BinningMultPosZ binPolicyPosZMult{{binsZPool, binsMultPool}}; auto trigCand = pair.template hfcRedTrigCharm_as(); - if (fillSparses && fillTables) { - fillSameEvent(pair, trigCand, binPolicyPosZMult); - } else if (fillSparses) { - fillSameEvent(pair, trigCand, binPolicyPosZMult); - } else if (fillTables) { - fillSameEvent(pair, trigCand, binPolicyPosZMult); - } + fillSameEvent(pair, trigCand, binPolicyPosZMult); } - PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processSameEventCharmHadWMultMix, "Process Same Event for Charm-Had with multiplicity pools", true); + PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processSECharmHadWMultMix, "Process Same Event for Charm-Had with multiplicity pools", false); - void processSameEventHadHadWMultMix(SameEvtPairsHadHad::iterator const& pair, - aod::HfcRedTrigTracks const&, - aod::HfcRedCorrColls const&) + void processSECharmHadWMultMixWEffInfo(SameEvtPairsChHadWEffInfo::iterator const& pair, + TrigCharmCands const&, + aod::HfcRedCorrColls const&) { - BinningMultPosZ binPolicyPosZMult{{zPoolBins, multPoolBins}, true}; + BinningMultPosZ binPolicyPosZMult{{binsZPool, binsMultPool}}; + auto trigCand = pair.template hfcRedTrigCharm_as(); + fillSameEvent(pair, trigCand, binPolicyPosZMult); + } + PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processSECharmHadWMultMixWEffInfo, "Process Same Event for Charm-Had with multiplicity pools", false); + + void processSEHadHadWMultMix(SameEvtPairsHadHad::iterator const& pair, + aod::HfcRedTrigTracks const&, + aod::HfcRedCorrColls const&) + { + BinningMultPosZ binPolicyPosZMult{{binsZPool, binsMultPool}}; auto trigCand = pair.template hfcRedTrigTrack_as(); - if (fillSparses && fillTables) { - fillSameEvent(pair, trigCand, binPolicyPosZMult); - } else if (fillSparses) { - fillSameEvent(pair, trigCand, binPolicyPosZMult); - } else if (fillTables) { - fillSameEvent(pair, trigCand, binPolicyPosZMult); - } + fillSameEvent(pair, trigCand, binPolicyPosZMult); } - PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processSameEventHadHadWMultMix, "Process Same Event for Had-Had with multiplicity pools", false); + PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processSEHadHadWMultMix, "Process Same Event for Had-Had with multiplicity pools", false); - void processSameEventCharmHadWCentMix(SameEvtPairsChHad::iterator const& pair, - aod::HfcRedTrigCharms const&, - aod::HfcRedCorrColls const&) + void processSECharmHadWCentMix(SameEvtPairsChHad::iterator const& pair, + aod::HfcRedTrigCharms const&, + aod::HfcRedCorrColls const&) { - BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}, true}; + BinningCentPosZ binPolicyPosZCent{{binsZPool, binsCentPool}}; auto trigCand = pair.template hfcRedTrigCharm_as(); - if (fillSparses && fillTables) { - fillSameEvent(pair, trigCand, binPolicyPosZCent); - } else if (fillSparses) { - fillSameEvent(pair, trigCand, binPolicyPosZCent); - } else if (fillTables) { - fillSameEvent(pair, trigCand, binPolicyPosZCent); - } + fillSameEvent(pair, trigCand, binPolicyPosZCent); } - PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processSameEventCharmHadWCentMix, "Process Same Event for Charm-Had with centrality pools", true); + PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processSECharmHadWCentMix, "Process Same Event for Charm-Had with centrality pools", true); - void processSameEventHadHadWCentMix(SameEvtPairsHadHad::iterator const& pair, - aod::HfcRedTrigTracks const&, - aod::HfcRedCorrColls const&) + void processSECharmHadWCentMixWEffInfo(SameEvtPairsChHadWEffInfo::iterator const& pair, + TrigCharmCands const&, + aod::HfcRedCorrColls const&) { - BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}, true}; + BinningCentPosZ binPolicyPosZCent{{binsZPool, binsCentPool}}; + auto trigCand = pair.template hfcRedTrigCharm_as(); + fillSameEvent(pair, trigCand, binPolicyPosZCent); + } + PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processSECharmHadWCentMixWEffInfo, "Process Same Event for Charm-Had with centrality pools", false); + + void processSEHadHadWCentMix(SameEvtPairsHadHad::iterator const& pair, + aod::HfcRedTrigTracks const&, + aod::HfcRedCorrColls const&) + { + BinningCentPosZ binPolicyPosZCent{{binsZPool, binsCentPool}}; auto trigCand = pair.template hfcRedTrigTrack_as(); - if (fillSparses && fillTables) { - fillSameEvent(pair, trigCand, binPolicyPosZCent); - } else if (fillSparses) { - fillSameEvent(pair, trigCand, binPolicyPosZCent); - } else if (fillTables) { - fillSameEvent(pair, trigCand, binPolicyPosZCent); - } + fillSameEvent(pair, trigCand, binPolicyPosZCent); + } + PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processSEHadHadWCentMix, "Process Same Event for Had-Had with centrality pools", false); + + void processMECharmHadWCentMix(aod::HfcRedCorrColls const& collisions, + TrigCharmCands const& candidates, + AssocTracks const& tracks) + { + BinningCentPosZ binPolicyPosZCent{{binsZPool, binsCentPool}}; + auto pairsTuple = std::make_tuple(candidates, tracks); + Pair const pairs{binPolicyPosZCent, numberEventsMixed, -1, collisions, pairsTuple, &cache}; + fillMixedEvent(pairs, binPolicyPosZCent); } - PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processSameEventHadHadWCentMix, "Process Same Event for Had-Had with centrality pools", false); + PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processMECharmHadWCentMix, "Process Mixed Event for Charm-Had with centrality pools", false); - void processMixedEventCharmHadWCentMix(aod::HfcRedCorrColls const& collisions, + void processMECharmHadWCentMixWEffInfo(aod::HfcRedCorrColls const& collisions, TrigCharmCands const& candidates, AssocTracks const& tracks) { - BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}, true}; + BinningCentPosZ binPolicyPosZCent{{binsZPool, binsCentPool}}; auto pairsTuple = std::make_tuple(candidates, tracks); Pair const pairs{binPolicyPosZCent, numberEventsMixed, -1, collisions, pairsTuple, &cache}; - if (fillSparses && fillTables) { - fillMixedEvent(pairs, binPolicyPosZCent); - } else if (fillSparses) { - fillMixedEvent(pairs, binPolicyPosZCent); - } else if (fillTables) { - fillMixedEvent(pairs, binPolicyPosZCent); - } + fillMixedEvent(pairs, binPolicyPosZCent); + } + PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processMECharmHadWCentMixWEffInfo, "Process Mixed Event for Charm-Had with centrality pools and efficiency information", false); + + void processMECharmHadWMultMix(aod::HfcRedCorrColls const& collisions, + TrigCharmCands const& candidates, + AssocTracks const& tracks) + { + BinningMultPosZ binPolicyPosZMult{{binsZPool, binsMultPool}}; + auto pairsTuple = std::make_tuple(candidates, tracks); + Pair const pairs{binPolicyPosZMult, numberEventsMixed, -1, collisions, pairsTuple, &cache}; + fillMixedEvent(pairs, binPolicyPosZMult); } - PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processMixedEventCharmHadWCentMix, "Process Mixed Event for Charm-Had with centrality pools", false); + PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processMECharmHadWMultMix, "Process Mixed Event for Charm-Had with multiplicity pools", false); - void processMixedEventCharmHadWMultMix(aod::HfcRedCorrColls const& collisions, + void processMECharmHadWMultMixWEffInfo(aod::HfcRedCorrColls const& collisions, TrigCharmCands const& candidates, AssocTracks const& tracks) { - BinningMultPosZ binPolicyPosZMult{{zPoolBins, multPoolBins}, true}; + BinningMultPosZ binPolicyPosZMult{{binsZPool, binsMultPool}}; auto pairsTuple = std::make_tuple(candidates, tracks); Pair const pairs{binPolicyPosZMult, numberEventsMixed, -1, collisions, pairsTuple, &cache}; - if (fillSparses && fillTables) { - fillMixedEvent(pairs, binPolicyPosZMult); - } else if (fillSparses) { - fillMixedEvent(pairs, binPolicyPosZMult); - } else if (fillTables) { - fillMixedEvent(pairs, binPolicyPosZMult); - } + fillMixedEvent(pairs, binPolicyPosZMult); } - PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processMixedEventCharmHadWMultMix, "Process Mixed Event for Charm-Had with multiplicity pools", false); + PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processMECharmHadWMultMixWEffInfo, "Process Mixed Event for Charm-Had with multiplicity pools and efficiency information", false); - void processMixedEventHadHadWCentMix(aod::HfcRedCorrColls const& collisions, - AssocTracks const& tracks) + void processMEHadHadWCentMix(aod::HfcRedCorrColls const& collisions, + AssocTracks const& tracks) { - BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}, true}; + BinningCentPosZ binPolicyPosZCent{{binsZPool, binsCentPool}}; auto tracksTuple = std::make_tuple(tracks); SameKindPair const pairs{binPolicyPosZCent, numberEventsMixed, -1, collisions, tracksTuple, &cache}; - if (fillSparses && fillTables) { - fillMixedEvent(pairs, binPolicyPosZCent); - } else if (fillSparses) { - fillMixedEvent(pairs, binPolicyPosZCent); - } else if (fillTables) { - fillMixedEvent(pairs, binPolicyPosZCent); - } + fillMixedEvent(pairs, binPolicyPosZCent); } - PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processMixedEventHadHadWCentMix, "Process Mixed Event for Had-Had with centrality pools", false); + PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processMEHadHadWCentMix, "Process Mixed Event for Had-Had with centrality pools", false); - void processMixedEventHadHadWMultMix(aod::HfcRedCorrColls const& collisions, - AssocTracks const& tracks) + void processMEHadHadWMultMix(aod::HfcRedCorrColls const& collisions, + AssocTracks const& tracks) { - BinningMultPosZ binPolicyPosZMult{{zPoolBins, multPoolBins}, true}; + BinningMultPosZ binPolicyPosZMult{{binsZPool, binsMultPool}}; auto tracksTuple = std::make_tuple(tracks); SameKindPair const pairs{binPolicyPosZMult, numberEventsMixed, -1, collisions, tracksTuple, &cache}; - if (fillSparses && fillTables) { - fillMixedEvent(pairs, binPolicyPosZMult); - } else if (fillSparses) { - fillMixedEvent(pairs, binPolicyPosZMult); - } else if (fillTables) { - fillMixedEvent(pairs, binPolicyPosZMult); - } + fillMixedEvent(pairs, binPolicyPosZMult); } - PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processMixedEventHadHadWMultMix, "Process Mixed Event for Had-Had with multiplicity pools", false); + PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processMEHadHadWMultMix, "Process Mixed Event for Had-Had with multiplicity pools", false); void processCharmTriggers(aod::HfcRedTrigCharms const& trigCands, aod::HfcRedCorrColls const&) { + for (const auto& trigCand : trigCands) { auto collision = trigCand.template hfcRedCorrColl_as(); - if (collision.centrality() < centralityMin || collision.centrality() > centralityMax) { + if (!isSelCollision(collision)) { continue; } if (!isSelBdtScoreCut(trigCand, trigCand.ptTrig())) { continue; } - registry.fill(HIST("hSparseTrigCandsCharm"), trigCand.invMassTrig(), trigCand.ptTrig(), trigCand.bdtScore0Trig(), trigCand.bdtScore1Trig()); + + std::vector values = {trigCand.invMassTrig(), trigCand.ptTrig(), trigCand.bdtScore0Trig(), trigCand.bdtScore1Trig()}; + registry.get(HIST("hSparseTrigCandsCharm"))->Fill(values.data()); } } PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processCharmTriggers, "Process charm trigger info", false); - void processSameEventCharmHadWCentMixBase(aod::HfcRedCorrColls const& collisions, - TrigCharmCands const& candidates, - aod::HfcRedAssBases const& tracks) + void processCharmTriggersWEffInfo(TrigCharmCands const& trigCands, + aod::HfcRedCorrColls const&) { - BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}, true}; - for (const auto& collision : collisions) { - if (collision.centrality() < centralityMin || collision.centrality() > centralityMax) { + for (const auto& trigCand : trigCands) { + auto collision = trigCand.template hfcRedCorrColl_as(); + if (!isSelCollision(collision)) { + continue; + } + if (!isSelBdtScoreCut(trigCand, trigCand.ptTrig())) { continue; } - int poolBin = binPolicyPosZCent.getBin({collision.posZ(), collision.centrality()}); - - auto thisCollId = collision.globalIndex(); - auto candsThisColl = candidates.sliceBy(trigCharmCandsPerCol, thisCollId); - auto tracksThisColl = tracks.sliceBy(assocTracksPerCol, thisCollId); - doCorrelationsSameEvent(poolBin, candsThisColl, tracksThisColl); + double effTrig = doEfficiencyCorrection ? getEfficiency(hEffPrompt.get(), effHistTypeCharm.value, trigCand) : 1.; + if (doEfficiencyCorrection && !isValidEfficiency(effTrig)) { + LOGF(warn, "Invalid efficiency value for trigger candidate: effTrig = %f", effTrig); + continue; + } + std::vector values = {trigCand.invMassTrig(), trigCand.ptTrig(), trigCand.bdtScore0Trig(), trigCand.bdtScore1Trig()}; + double weight = doEfficiencyCorrection ? 1. / effTrig : 1.; + registry.get(HIST("hSparseTrigCandsCharm"))->Fill(values.data(), weight); } } - PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processSameEventCharmHadWCentMixBase, "Process Same Event base", false); - - void processMixedEventCharmHadWCentMixBase(aod::HfcRedCorrColls const& collisions, - TrigCharmCands const& candidates, - aod::HfcRedAssBases const& tracks) - { - BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}, true}; - - doCorrelationsMixedEvent(collisions, candidates, tracks, binPolicyPosZCent); - } - PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processMixedEventCharmHadWCentMixBase, "Process Mixed Event base", false); + PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processCharmTriggersWEffInfo, "Process charm trigger info with efficiency", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGHF/HFC/TableProducer/correlatorHfeHadrons.cxx b/PWGHF/HFC/TableProducer/correlatorHfeHadrons.cxx index b832a897cbf..5b5722562d3 100644 --- a/PWGHF/HFC/TableProducer/correlatorHfeHadrons.cxx +++ b/PWGHF/HFC/TableProducer/correlatorHfeHadrons.cxx @@ -122,7 +122,7 @@ struct HfCorrelatorHfeHadrons { ConfigurableAxis binsNSigma{"binsNSigma", {30, -15., 15.}, "#it{#sigma_{TPC}}"}; ConfigurableAxis binsMass{"binsMass", {100, 0.0, 2.0}, "Mass (GeV/#it{c}^{2}); entries"}; - BinningType corrBinning{{zPoolBins, multPoolBins}, true}; + BinningType corrBinning{{zPoolBins, multPoolBins}}; HistogramRegistry registry{ "registry", {}}; @@ -479,7 +479,7 @@ struct HfCorrelatorHfeHadrons { void processMcGen(McGenTableCollision const& mcCollision, aod::McParticles const& mcParticles, aod::HfMcGenSelEl const& electrons) { - BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBinsMcGen}, true}; + BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBinsMcGen}}; int poolBin = corrBinningMcGen.getBin(std::make_tuple(mcCollision.posZ(), mcCollision.multMCFT0A())); for (const auto& particleMc : mcParticles) { @@ -598,7 +598,7 @@ struct HfCorrelatorHfeHadrons { void processMcGenMixedEvent(McGenTableCollisions const& mcCollision, aod::HfMcGenSelEl const& electrons, aod::McParticles const& mcParticles) { - BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBinsMcGen}, true}; + BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBinsMcGen}}; auto tracksTuple = std::make_tuple(electrons, mcParticles); Pair const pairMcGen{corrBinningMcGen, 5, -1, mcCollision, tracksTuple, &cache}; diff --git a/PWGHF/HFC/TableProducer/correlatorLcHadrons.cxx b/PWGHF/HFC/TableProducer/correlatorLcHadrons.cxx index 879981fec67..25a7091e6b8 100644 --- a/PWGHF/HFC/TableProducer/correlatorLcHadrons.cxx +++ b/PWGHF/HFC/TableProducer/correlatorLcHadrons.cxx @@ -282,7 +282,7 @@ struct HfCorrelatorLcHadrons { ConfigurableAxis binsMassLc{"binsMassLc", {200, 1.98, 2.58}, "inv. mass (p K #pi) (GeV/#it{c}^{2})"}; ConfigurableAxis binsCentFt0m{"binsCentFt0m", {100, 0., 100.}, "Centrality percentile (FT0M)"}; - BinningType corrBinning{{binsZVtx, binsMultiplicity}, true}; + BinningType corrBinning{{binsZVtx, binsMultiplicity}}; HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; void init(InitContext&) @@ -290,8 +290,8 @@ struct HfCorrelatorLcHadrons { AxisSpec axisMassLc = {binsMassLc, "inv. mass (p K #pi) (GeV/#it{c}^{2})"}; AxisSpec const axisEta = {binsEta, "#it{eta}"}; AxisSpec const axisPhi = {binsPhi, "#it{#varphi}"}; - AxisSpec axisPtLc = {(std::vector)binsPtLc, "#it{p}_{T} (GeV/#it{c})"}; - AxisSpec axisPtHadron = {(std::vector)binsPtHadron, "#it{p}_{T} Hadron (GeV/#it{c})"}; + AxisSpec axisPtLc = {std::vector(binsPtLc), "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec axisPtHadron = {std::vector(binsPtHadron), "#it{p}_{T} Hadron (GeV/#it{c})"}; AxisSpec axisPtTrack = {500, 0, 50, "#it{p}_{T} Hadron (GeV/#it{c})"}; AxisSpec const axisMultiplicity = {binsMultiplicity, "Multiplicity"}; AxisSpec axisMultFT0M = {binsMultFT0M, "MultiplicityFT0M"}; @@ -362,7 +362,7 @@ struct HfCorrelatorLcHadrons { registry.add("hEtaMcGen", "Lc,Hadron particles - MC Gen", {HistType::kTH1D, {axisEta}}); registry.add("hPhiMcGen", "Lc,Hadron particles - MC Gen", {HistType::kTH1D, {axisPhi}}); registry.add("hMultFT0AMcGen", "Lc,Hadron multiplicity FT0A - MC Gen", {HistType::kTH1D, {axisMultiplicity}}); - corrBinning = {{binsZVtx, binsMultiplicity}, true}; + corrBinning = {{binsZVtx, binsMultiplicity}}; } /// Lc-hadron correlation pair builder - for real data and data-like analysis (i.e. reco-level w/o matching request via MC truth) @@ -825,7 +825,7 @@ struct HfCorrelatorLcHadrons { int counterLcHadron = 0; registry.fill(HIST("hMcEvtCount"), 0); - BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}, true}; + BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}}; int poolBin = corrBinningMcGen.getBin(std::make_tuple(mcCollision.posZ(), mcCollision.multMCFT0A())); registry.fill(HIST("hMultFT0AMcGen"), mcCollision.multMCFT0A()); @@ -1011,7 +1011,7 @@ struct HfCorrelatorLcHadrons { TracksWithMc const& tracks, aod::McParticles const& mcParticles) { - BinningType const corrBinningMcRec{{binsZVtx, binsMultiplicityMc}, true}; + BinningType const corrBinningMcRec{{binsZVtx, binsMultiplicityMc}}; for (const auto& candidate : candidates) { if (std::abs(HfHelper::yLc(candidate)) > yCandMax || candidate.pt() < ptCandMin || candidate.pt() > ptCandMax) { continue; @@ -1132,7 +1132,7 @@ struct HfCorrelatorLcHadrons { void processMcGenMixedEvent(SelCollisionsWithLcMc const& collisions, CandidatesLcMcGen const& mcParticles) { - BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}, true}; + BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair const pairMcGen{corrBinningMcGen, numberEventsMixed, -1, collisions, tracksTuple, &cache}; for (const auto& [c1, tracks1, c2, tracks2] : pairMcGen) { diff --git a/PWGHF/HFC/TableProducer/correlatorLcScHadrons.cxx b/PWGHF/HFC/TableProducer/correlatorLcScHadrons.cxx index da60d9982c2..3b2b29b1f8b 100644 --- a/PWGHF/HFC/TableProducer/correlatorLcScHadrons.cxx +++ b/PWGHF/HFC/TableProducer/correlatorLcScHadrons.cxx @@ -417,7 +417,7 @@ struct HfCorrelatorLcScHadrons { ConfigurableAxis binsCandMass{"binsCandMass", {200, 1.98, 2.58}, "inv. mass (p K #pi) (GeV/#it{c}^{2})"}; ConfigurableAxis binsNSigmas{"binsNSigmas", {4000, -500., 500.}, "n#sigma"}; - BinningType corrBinning{{binsZVtx, binsMultiplicity}, true}; + BinningType corrBinning{{binsZVtx, binsMultiplicity}}; HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; int8_t chargeCand = 3; @@ -536,7 +536,7 @@ struct HfCorrelatorLcScHadrons { registry.add("hV0LambdaReflPiKRejMcRec", "McRec V0 Lambda reflected candidates with #pi K rejection;inv. mass (p #pi) (GeV/#it{c}^{2});GeV/#it{c};GeV/#it{c}", {HistType::kTH3F, {{axisMassV0}, {axisPtV0}, {axisPtHadron}}}); registry.add("hV0PtPrimLambdaMcGen", "Mcgen V0 Lambda candidates;GeV/#it{c}", {HistType::kTH1F, {{axisPtV0}}}); - corrBinning = {{binsZVtx, binsMultiplicity}, true}; + corrBinning = {{binsZVtx, binsMultiplicity}}; } template @@ -1603,7 +1603,7 @@ struct HfCorrelatorLcScHadrons { static constexpr std::size_t PDGChargeScale{3u}; registry.fill(HIST("hMcEvtCount"), 0); - BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}, true}; + BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}}; poolBin = corrBinningMcGen.getBin(std::make_tuple(mcCollision.posZ(), mcCollision.multMCFT0A())); registry.fill(HIST("hMultFT0AMcGen"), mcCollision.multMCFT0A()); @@ -1834,7 +1834,7 @@ struct HfCorrelatorLcScHadrons { void processMcGenMixedEvent(SelCollisionsMc const& collisions, CandidatesLcMcGen const& mcParticles) { - BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}, true}; + BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair const pairMcGen{corrBinningMcGen, cfgCharmCand.numberEventsMixed, -1, collisions, tracksTuple, &cache}; for (const auto& [c1, tracks1, c2, tracks2] : pairMcGen) { diff --git a/PWGHF/HFC/TableProducer/correlatorXicHadrons.cxx b/PWGHF/HFC/TableProducer/correlatorXicHadrons.cxx index 249b4eea990..5c0d11af9d5 100644 --- a/PWGHF/HFC/TableProducer/correlatorXicHadrons.cxx +++ b/PWGHF/HFC/TableProducer/correlatorXicHadrons.cxx @@ -459,7 +459,7 @@ struct HfCorrelatorXicHadrons { ConfigurableAxis binsCandMassXic0{"binsCandMassXic0", {200, 2.4, 2.8}, "inv. mass (Xi pi) (GeV/#it{c}^{2})"}; ConfigurableAxis binsNSigmas{"binsNSigmas", {4000, -500., 500.}, "n#sigma"}; - BinningType corrBinning{{binsZVtx, binsMultiplicity}, true}; + BinningType corrBinning{{binsZVtx, binsMultiplicity}}; HistogramRegistry registry{"registry"}; int8_t chargeCand = 3; @@ -584,7 +584,7 @@ struct HfCorrelatorXicHadrons { registry.add("hV0LambdaReflPiKRejMcRec", "McRec V0 Lambda reflected candidates with #pi K rejection;inv. mass (p #pi) (GeV/#it{c}^{2});GeV/#it{c};GeV/#it{c}", {HistType::kTH3F, {{axisMassV0}, {axisPtV0}, {axisPtHadron}}}); registry.add("hV0PtPrimLambdaMcGen", "Mcgen V0 Lambda candidates;GeV/#it{c}", {HistType::kTH1F, {{axisPtV0}}}); - corrBinning = {{binsZVtx, binsMultiplicity}, true}; + corrBinning = {{binsZVtx, binsMultiplicity}}; } template @@ -1525,7 +1525,7 @@ struct HfCorrelatorXicHadrons { int8_t candSign = 0; registry.fill(HIST("hMcEvtCount"), 0); - BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}, true}; + BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}}; poolBin = corrBinningMcGen.getBin(std::make_tuple(mcCollision.posZ(), mcCollision.multMCFT0A())); registry.fill(HIST("hMultFT0AMcGen"), mcCollision.multMCFT0A()); @@ -1865,7 +1865,7 @@ struct HfCorrelatorXicHadrons { void processMcGenMixedEvent(SelCollisionsMc const& collisions, CandidatesXicPlusMcGen const& mcParticles) { - BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}, true}; + BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair const pairMcGen{corrBinningMcGen, cfgXicCand.numberEventsMixed, -1, collisions, tracksTuple, &cache}; diff --git a/PWGHF/HFC/TableProducer/derivedDataCreatorCorrelationsReduced.cxx b/PWGHF/HFC/TableProducer/derivedDataCreatorCorrelationsReduced.cxx index 55bc064a960..73157c06d08 100644 --- a/PWGHF/HFC/TableProducer/derivedDataCreatorCorrelationsReduced.cxx +++ b/PWGHF/HFC/TableProducer/derivedDataCreatorCorrelationsReduced.cxx @@ -16,11 +16,12 @@ /// \author Wu Chuntai , CCNU, INFN Padova, and Padova University #include "PWGHF/Core/CentralityEstimation.h" -#include "PWGHF/Core/HfHelper.h" +#include "PWGHF/Core/DecayChannels.h" #include "PWGHF/DataModel/AliasTables.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" #include "PWGHF/HFC/DataModel/DerivedDataCorrelationTables.h" +#include "PWGHF/HFC/Utils/utilsFlow.h" #include "PWGHF/Utils/utilsEvSelHf.h" #include "Common/Core/RecoDecay.h" @@ -31,7 +32,6 @@ #include #include -#include #include #include #include @@ -49,6 +49,7 @@ #include #include #include +#include #include #include @@ -57,18 +58,10 @@ using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::hf_centrality; using namespace o2::hf_evsel; +using namespace o2::hf_decay; +using namespace o2::hfc_utils_flow; -enum CandidateType { - DplusToPiKPi = 0, - DsToKKPi, - DsToPiKK, - D0ToPiK, - D0ToKPi, - LcToPKPi, - Hadron -}; - -/// Code to select collisions with at least one Ds meson +/// Code to produce self-contained derived datasets for v2 analyses of charm hadrons via 2PC struct HfDerivedDataCreatorCorrelationsReduced { Produces rowCollisions; // Table with reduced collision info Produces rowSECharmHadPairs; // Table with same-event pairs info @@ -82,6 +75,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { Configurable centEstimator{"centEstimator", 2, "Centrality estimation (FT0A: 1, FT0C: 2, FT0M: 3, FV0A: 4)"}; Configurable selectionFlag{"selectionFlag", 15, "Selection Flag for hadron (ML score tables are required to run the task)"}; Configurable forceCharmInCollision{"forceCharmInCollision", true, "Flag to force charm in collision"}; + Configurable acceptOnlyPrompt{"acceptOnlyPrompt", true, "Flag to accept only prompt charm in the MC process functions"}; Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable> classMl{"classMl", {0, 2}, "Indexes of BDT scores to be stored. Two indexes max."}; Configurable yCandMax{"yCandMax", 0.8, "max. cand. rapidity"}; @@ -101,83 +95,96 @@ struct HfDerivedDataCreatorCorrelationsReduced { Configurable downSampleTrksFactor{"downSampleTrksFactor", 1., "Fraction of associated tracks to keep"}; Configurable ptMaxForDownSample{"ptMaxForDownSample", 10., "Maximum pt for the application of the downsampling factor"}; Configurable centMaxForDownSample{"centMaxForDownSample", 101., "Maximum centrality for the application of the downsampling factor"}; - Configurable> binsPtTrig{"binsPtTrig", std::vector{0., 1., 2., 3., 5., 8., 12., 24., 36.}, "pT bin limits for trigger candidates"}; - Configurable> binsPtAssoc{"binsPtAssoc", std::vector{0.2, 1., 2., 50.}, "pT bin limits for associated particles"}; HfEventSelection hfEvSel; // event selection and monitoring + HfcFlowHelper hfcFlowHelper; // flow utilities o2::framework::Service ccdb{}; SliceCache cache; - double massCharm{0.}; - TF1* funcDcaXYPtCutPrimTrk = nullptr; + std::unique_ptr funcDcaXYPtCutPrimTrk{nullptr}; - using CollsWithCentMult = soa::Join; - using CandDsData = soa::Filtered>; - using CandDplusData = soa::Filtered>; + using DataCollsWithCentMult = soa::Join; + using McCollsWithCentMult = soa::Join; + + // Data using CandD0Data = soa::Filtered>; + using CandDplusData = soa::Filtered>; + using CandDsData = soa::Filtered>; using CandLcData = soa::Filtered>; - using TracksData = soa::Filtered>; + using TracksData = soa::Filtered>; //, aod::pidTPCFullMu, aod::pidTPCFullEl, aod::pidTPCFullPi, aod::pidTPCFullKa, aod::pidTPCFullPr>>; + // MC + using CandD0Mc = soa::Filtered>; + using CandDplusMc = soa::Filtered>; + using CandDsMc = soa::Filtered>; + using CandLcMc = soa::Filtered>; + using TracksMc = soa::Filtered>; //, aod::pidTPCFullMu, aod::pidTPCFullEl, aod::pidTPCFullPi, aod::pidTPCFullKa, aod::pidTPCFullPr>>; - Filter filterSelectDsCandidates = aod::hf_sel_candidate_ds::isSelDsToKKPi >= selectionFlag || aod::hf_sel_candidate_ds::isSelDsToPiKK >= selectionFlag; - Filter filterSelectDplusCandidates = aod::hf_sel_candidate_dplus::isSelDplusToPiKPi >= selectionFlag; Filter filterSelectD0Candidates = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlag || aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlag; - Filter filterSelectLcCandidates = aod::hf_sel_candidate_lc::isSelLcToPKPi >= selectionFlag; + Filter filterSelectDplusCandidates = aod::hf_sel_candidate_dplus::isSelDplusToPiKPi >= selectionFlag; + Filter filterSelectDsCandidates = aod::hf_sel_candidate_ds::isSelDsToKKPi >= selectionFlag || aod::hf_sel_candidate_ds::isSelDsToPiKK >= selectionFlag; + Filter filterSelectLcCandidates = aod::hf_sel_candidate_lc::isSelLcToPKPi >= selectionFlag || aod::hf_sel_candidate_lc::isSelLcToPiKP >= selectionFlag; Filter filterSelectTrkData = (nabs(aod::track::eta) < etaTrkMax) && (aod::track::pt > ptTrkMin) && (aod::track::pt < ptTrkMax) && (nabs(aod::track::dcaXY) < dcaXYTrkMax) && (nabs(aod::track::dcaZ) < dcaZTrkMax); - Preslice candsDsPerColl = aod::hf_cand::collisionId; - Preslice candsDplusPerColl = aod::hf_cand::collisionId; - Preslice candsD0PerColl = aod::hf_cand::collisionId; - Preslice candsLcPerColl = aod::hf_cand::collisionId; - Preslice trackIndicesPerColl = aod::track::collisionId; - - Partition selectedDsToKKPi = aod::hf_sel_candidate_ds::isSelDsToKKPi >= selectionFlag; - Partition selectedDsToPiKK = aod::hf_sel_candidate_ds::isSelDsToPiKK >= selectionFlag; - Partition selectedD0ToPiK = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlag; - Partition selectedD0ToKPi = aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlag; - Partition selectedLcToPKPi = aod::hf_sel_candidate_lc::isSelLcToPKPi >= selectionFlag; - - ConfigurableAxis binsInvMass{"binsInvMass", {300, 1.6, 2.2}, ""}; - ConfigurableAxis binsMultFT0M{"binsMultFT0M", {100, 0., 10000.}, "Multiplicity as FT0M signal amplitude"}; + Partition selD0ToPiK = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlag; + Partition selD0ToKPi = aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlag; + Partition selDsToKKPi = aod::hf_sel_candidate_ds::isSelDsToKKPi >= selectionFlag; + Partition selDsToPiKK = aod::hf_sel_candidate_ds::isSelDsToPiKK >= selectionFlag; + Partition selLcToPKPi = aod::hf_sel_candidate_lc::isSelLcToPKPi >= selectionFlag; + Partition selLcToPiKP = aod::hf_sel_candidate_lc::isSelLcToPiKP >= selectionFlag; + + Partition recoSelD0ToPiK = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_cand_2prong::DecayChannelMain::D0ToPiK)) && aod::hf_sel_candidate_d0::isSelD0 >= selectionFlag && ((!acceptOnlyPrompt) || (acceptOnlyPrompt && aod::hf_cand_mc_flag::originMcRec == static_cast(RecoDecay::OriginType::Prompt))); + Partition recoSelD0ToKPi = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_cand_2prong::DecayChannelMain::D0ToPiK)) && aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlag && ((!acceptOnlyPrompt) || (acceptOnlyPrompt && aod::hf_cand_mc_flag::originMcRec == static_cast(RecoDecay::OriginType::Prompt))); + Partition recoSelDPlusToPiKPi = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_cand_3prong::DecayChannelMain::DplusToPiKPi)) && aod::hf_sel_candidate_dplus::isSelDplusToPiKPi >= selectionFlag && ((!acceptOnlyPrompt) || (acceptOnlyPrompt && aod::hf_cand_mc_flag::originMcRec == static_cast(RecoDecay::OriginType::Prompt))); + Partition recoSelDsToKKPi = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_cand_3prong::DecayChannelMain::DsToPiKK) && nabs(aod::hf_cand_mc_flag::flagMcDecayChanRec) == static_cast(hf_cand_3prong::DecayChannelResonant::DsToPhiPi)) && aod::hf_sel_candidate_ds::isSelDsToKKPi >= selectionFlag && ((!acceptOnlyPrompt) || (acceptOnlyPrompt && aod::hf_cand_mc_flag::originMcRec == static_cast(RecoDecay::OriginType::Prompt))); + Partition recoSelDsToPiKK = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_cand_3prong::DecayChannelMain::DsToPiKK) && nabs(aod::hf_cand_mc_flag::flagMcDecayChanRec) == static_cast(hf_cand_3prong::DecayChannelResonant::DsToPhiPi)) && aod::hf_sel_candidate_ds::isSelDsToPiKK >= selectionFlag && ((!acceptOnlyPrompt) || (acceptOnlyPrompt && aod::hf_cand_mc_flag::originMcRec == static_cast(RecoDecay::OriginType::Prompt))); + Partition recoSelLcToPKPi = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_cand_3prong::DecayChannelMain::LcToPKPi)) && aod::hf_sel_candidate_lc::isSelLcToPKPi >= selectionFlag && ((!acceptOnlyPrompt) || (acceptOnlyPrompt && aod::hf_cand_mc_flag::originMcRec == static_cast(RecoDecay::OriginType::Prompt))); + Partition recoSelLcToPiKP = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_cand_3prong::DecayChannelMain::LcToPKPi)) && aod::hf_sel_candidate_lc::isSelLcToPiKP >= selectionFlag && ((!acceptOnlyPrompt) || (acceptOnlyPrompt && aod::hf_cand_mc_flag::originMcRec == static_cast(RecoDecay::OriginType::Prompt))); + + ConfigurableAxis binsTpcSignal{"binsTpcSignal", {500, 0., 500.}, "TPC signal bins"}; + ConfigurableAxis binsPtTrig{"binsPtTrig", {24, 0, 24}, ""}; + ConfigurableAxis binsPtAssoc{"binsPtAssoc", {20, 0, 10}, ""}; + ConfigurableAxis binsMult{"binsMult", {100, 0., 10000.}, "Multiplicity bins"}; ConfigurableAxis binsCent{"binsCent", {100, 0., 100.}, "Centrality bins"}; ConfigurableAxis binsPosZ{"binsPosZ", {100, -10., 10.}, "Primary vertex z coordinate"}; ConfigurableAxis binsEta{"binsEta", {50, -2., 2.}, "Eta bins"}; ConfigurableAxis binsPhi{"binsPhi", {64, -o2::constants::math::PIHalf, 3. * o2::constants::math::PIHalf}, "Phi bins"}; - ConfigurableAxis binsDeltaEta{"binsDeltaEta", {100, -2., 2.}, "Delta Eta bins"}; - ConfigurableAxis binsDeltaPhi{"binsDeltaPhi", {64, -3., 3.}, "Delta Phi bins"}; - ConfigurableAxis binsMlOne{"binsMlOne", {100, 0., 1.}, ""}; - ConfigurableAxis binsMlTwo{"binsMlTwo", {100, 0., 1.}, ""}; ConfigurableAxis binsDca{"binsDca", {200, -1., 1.}, ""}; HistogramRegistry registry{"registry", {}}; void init(InitContext&) { - if (doprocessDplusSameEvent || doprocessDplusMixedEvent) { - massCharm = o2::constants::physics::MassDPlus; - } else if (doprocessDsSameEvent || doprocessDsMixedEvent) { - massCharm = o2::constants::physics::MassDS; - } else if (doprocessD0SameEvent || doprocessD0MixedEvent) { - massCharm = o2::constants::physics::MassD0; - } else if (doprocessLcSameEvent || doprocessLcMixedEvent) { - massCharm = o2::constants::physics::MassLambdaCPlus; - } else if (doprocessHadronHadronSameEvent || doprocessHadronHadronMixedEvent) { - LOG(info) << "Charm mass not set, processing Hadron-Hadron case"; - } else { - LOG(fatal) << "No decay channel selected to process"; - } - - hfEvSel.addHistograms(registry); // collision monitoring + + hfEvSel.init(registry); + ccdb->setURL(ccdbUrl); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); + std::vector classesMl; + for (const auto scoreClass : classMl.value) { + if (scoreClass < 0) { + LOG(fatal) << "ML class indexes must be non-negative"; + return; + } + classesMl.push_back(static_cast(scoreClass)); + } + + if (classesMl.size() > 2) { + LOG(fatal) << "At most two ML classes can be selected"; + return; + } + + hfcFlowHelper.init(classesMl, yCandMax.value); + + const AxisSpec axisTpcSignal = {binsTpcSignal, "TPC Signal"}; const AxisSpec axisCent = {binsCent, "Centrality"}; - const AxisSpec axisMultFT0M = {binsMultFT0M, "MultiplicityFT0M"}; + const AxisSpec axisMult = {binsMult, "Multiplicity"}; const AxisSpec axisPosZ = {binsPosZ, "PosZ"}; const AxisSpec axisEta = {binsEta, "#it{#eta}"}; const AxisSpec axisPhi = {binsPhi, "#it{#varphi}"}; - const AxisSpec axisPtTrig = {(std::vector)binsPtTrig, "#it{p}_{T} Trig (GeV/#it{c})"}; - const AxisSpec axisPtAssoc = {(std::vector)binsPtAssoc, "#it{p}_{T} Assoc (GeV/#it{c})"}; + const AxisSpec axisPtTrig = {binsPtTrig, "#it{p}_{T} Trig (GeV/#it{c})"}; + const AxisSpec axisPtAssoc = {binsPtAssoc, "#it{p}_{T} Assoc (GeV/#it{c})"}; const AxisSpec axisDcaXY = {binsDca, "DCA XY (cm)"}; const AxisSpec axisDcaZ = {binsDca, "DCA Z (cm)"}; @@ -188,132 +195,23 @@ struct HfDerivedDataCreatorCorrelationsReduced { registry.add("hEtaVsPtTrigAssoc", "Associated particles etaVsPt", {HistType::kTH3F, {{axisEta}, {axisPtTrig}, {axisPtAssoc}}}); registry.add("hPhiVsPtAssoc", "Associated particles phiVsPt", {HistType::kTH2F, {{axisPhi}, {axisPtAssoc}}}); registry.add("hEtaVsPtAssoc", "Associated particles etaVsPt", {HistType::kTH2F, {{axisEta}, {axisPtAssoc}}}); - registry.add("hDcaXYVsPtAssoc", "Associated particles DCAxyVsPt", {HistType::kTH2F, {{axisDcaXY}, {axisPtAssoc}}}); - registry.add("hDcaZVsPtAssoc", "Associated particles DCAzVsPt", {HistType::kTH2F, {{axisDcaZ}, {axisPtAssoc}}}); + registry.add("hDcaXYVsPtVsCentAssoc", "Associated particles DCAxyVsPtVsCent", {HistType::kTH3F, {{axisPtAssoc}, {axisDcaXY}, {axisCent}}}); + registry.add("hDcaZVsPtVsCentAssoc", "Associated particles DCAzVsPtVsCent", {HistType::kTH3F, {{axisPtAssoc}, {axisDcaZ}, {axisCent}}}); + registry.add("hDcaXYVsPtVsMultAssoc", "Associated particles DCAxyVsPtVsMult", {HistType::kTH3F, {{axisPtAssoc}, {axisDcaXY}, {axisMult}}}); + registry.add("hDcaZVsPtVsMultAssoc", "Associated particles DCAzVsPtVsMult", {HistType::kTH3F, {{axisPtAssoc}, {axisDcaZ}, {axisMult}}}); // Setup pt-dependent DCAxy cut function if (usePtDiffDcaXYCut) { - funcDcaXYPtCutPrimTrk = new TF1("funcDcaXYPtCutPrimTrk", Form("[0]*%s", dcaXYPtPrimTrkFunc.value.data()), 0.001, 100); + funcDcaXYPtCutPrimTrk = std::make_unique("funcDcaXYPtCutPrimTrk", Form("[0]*%s", dcaXYPtPrimTrkFunc.value.data()), 0.001, 100.); funcDcaXYPtCutPrimTrk->SetParameter(0, dcaXYTrkNSigmaMax); LOGF(info, "DCAxy pt-dependence function: %s", Form("[0]*%s", dcaXYPtPrimTrkFunc.value.data())); } }; // end init - /// Get charm hadron candidate mass - /// \param candidate is the charm hadron candidate - template - double getCandMass(const TCand& candidate) - { - if constexpr (CandType == CandidateType::DsToKKPi) { - return HfHelper::invMassDsToKKPi(candidate); - } - if constexpr (CandType == CandidateType::DsToPiKK) { - return HfHelper::invMassDsToPiKK(candidate); - } - if constexpr (CandType == CandidateType::DplusToPiKPi) { - return HfHelper::invMassDplusToPiKPi(candidate); - } - if constexpr (CandType == CandidateType::D0ToPiK) { - return HfHelper::invMassD0ToPiK(candidate); - } - if constexpr (CandType == CandidateType::D0ToKPi) { - return HfHelper::invMassD0barToKPi(candidate); - } - if constexpr (CandType == CandidateType::LcToPKPi) { - return HfHelper::invMassLcToPKPi(candidate); - } - return -1.; - } - - /// Get charm hadron bdt scores - /// \param candidate is the charm hadron candidate - template - std::array getCandMlScores(const TCand& candidate) - { - std::array outputMl{-999.f, -999.f}; - if constexpr (CandType == CandidateType::DsToKKPi) { - for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { - outputMl[iclass] = candidate.mlProbDsToKKPi()[classMl->at(iclass)]; - } - } - if constexpr (CandType == CandidateType::DsToPiKK) { - for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { - outputMl[iclass] = candidate.mlProbDsToPiKK()[classMl->at(iclass)]; - } - } - if constexpr (CandType == CandidateType::DplusToPiKPi) { - for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { - outputMl[iclass] = candidate.mlProbDplusToPiKPi()[classMl->at(iclass)]; - } - } - if constexpr (CandType == CandidateType::D0ToPiK) { - for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { - outputMl[iclass] = candidate.mlProbD0()[classMl->at(iclass)]; - } - } - if constexpr (CandType == CandidateType::D0ToKPi) { - for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { - outputMl[iclass] = candidate.mlProbD0bar()[classMl->at(iclass)]; - } - } - if constexpr (CandType == CandidateType::LcToPKPi) { - for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { - outputMl[iclass] = candidate.mlProbLcToPKPi()[classMl->at(iclass)]; - } - } - return outputMl; - } - - /// Cut on rapidity of the candidate - /// \param candidate is the charm hadron candidate - template - bool cutCandRapidity(const TCand& candidate) - { - double y = 0.0; - if constexpr (CandType == CandidateType::DsToKKPi || CandType == CandidateType::DsToPiKK) { - y = HfHelper::yDs(candidate); - } else if constexpr (CandType == CandidateType::DplusToPiKPi) { - y = HfHelper::yDplus(candidate); - } else if constexpr (CandType == CandidateType::D0ToPiK || CandType == CandidateType::D0ToKPi) { - y = HfHelper::yD0(candidate); - } else if constexpr (CandType == CandidateType::LcToPKPi) { - y = HfHelper::yLc(candidate); - } else { - return true; - } - return std::fabs(y) < yCandMax; - } - - /// Check event selections for collision and fill the collision table - /// \param collision is the collision - template - bool checkCollision(Coll const& collision, float& cent, float& mult) - { - o2::hf_evsel::HfCollisionRejectionMask collRejMask{}; - if (centEstimator == CentralityEstimator::FT0A) { - collRejMask = hfEvSel.getHfCollisionRejectionMask(collision, cent, ccdb, registry); - mult = collision.multFT0A(); - } else if (centEstimator == CentralityEstimator::FT0C) { - collRejMask = hfEvSel.getHfCollisionRejectionMask(collision, cent, ccdb, registry); - mult = collision.multFT0C(); - } else if (centEstimator == CentralityEstimator::FT0M) { - collRejMask = hfEvSel.getHfCollisionRejectionMask(collision, cent, ccdb, registry); - mult = collision.multFT0M(); - } else if (centEstimator == CentralityEstimator::FV0A) { - collRejMask = hfEvSel.getHfCollisionRejectionMask(collision, cent, ccdb, registry); - mult = collision.multFV0A(); - } else { - LOG(fatal) << "Centrality estimator not recognized for collision selection"; - std::abort(); - } - hfEvSel.fillHistograms(collision, collRejMask, cent); - return collRejMask == 0; - } - /// Checks if the trigger cand-associated track pair can be accepted for SE correlation /// \param assTrk is the associated track /// \param cand is the trigger candidate - template + template bool acceptSameEvtPair(TAssocTrk const& assTrk, TCand const& cand, double deltaEta) { if (std::abs(deltaEta) <= deltaEtaAbsMin || std::abs(deltaEta) > deltaEtaAbsMax) { @@ -325,7 +223,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { } int const trackGlobalIndex = assTrk.globalIndex(); - if constexpr (CandType == CandidateType::Hadron) { + if constexpr (CandType == TriggerChannels::Hadron) { if (!cand.isGlobalTrackWoDCA() || cand.tpcNClsCrossedRows() < tpcNClsCrossedRowsMin) { return false; } @@ -349,24 +247,27 @@ struct HfDerivedDataCreatorCorrelationsReduced { /// Fill histograms and tables for same-event correlations /// \param trigCands are the trigger candidates /// \param assTrks are the associated tracks - /// \param collCentrality is the collision centrality - template + /// \param cent is the collision centrality + /// \param mult is the collision multiplicity + template void fillSameEvent(TTrigCands const& trigCands, TAssocTrks const& assTrks, - const float collCentrality) + const float cent, + const float mult) { for (const auto& trigCand : trigCands) { - if (!cutCandRapidity(trigCand)) { + if (!hfcFlowHelper.cutCandRapidity(trigCand)) { continue; } double trigCandPt = trigCand.pt(); registry.fill(HIST("hPhiVsPtTrig"), RecoDecay::constrainAngle(trigCand.phi(), -o2::constants::math::PIHalf), trigCandPt); registry.fill(HIST("hEtaVsPtTrig"), trigCand.eta(), trigCandPt); - if constexpr (CandType == CandidateType::Hadron) { + if constexpr (CandType == TriggerChannels::Hadron) { rowTrigHads(rowCollisions.lastIndex(), trigCandPt, trigCand.tpcNClsCrossedRows(), trigCand.itsClusterMap(), trigCand.itsNCls(), trigCand.dcaXY(), trigCand.dcaZ()); } else { - std::array outputMl = getCandMlScores(trigCand); - rowTrigCharms(rowCollisions.lastIndex(), trigCandPt, getCandMass(trigCand), outputMl[0], outputMl[1]); + std::array outputMl = hfcFlowHelper.getCandMlScores(trigCand); + rowTrigCharms(rowCollisions.lastIndex(), trigCandPt, hfcFlowHelper.getCandMass(trigCand), outputMl[0], outputMl[1]); + rowTrigBases(trigCand.phi(), trigCand.eta()); } for (const auto& assTrk : assTrks) { @@ -384,7 +285,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { } if (downSampleTrksFactor < 1.) { float const pseudoRndm = assTrkPt * 1000. - static_cast(assTrkPt * 1000); - if (assTrkPt < ptMaxForDownSample && collCentrality < centMaxForDownSample && pseudoRndm >= downSampleTrksFactor) { + if (assTrkPt < ptMaxForDownSample && cent < centMaxForDownSample && pseudoRndm >= downSampleTrksFactor) { continue; } } @@ -392,12 +293,15 @@ struct HfDerivedDataCreatorCorrelationsReduced { registry.fill(HIST("hEtaVsPtTrigAssoc"), assTrk.eta(), trigCandPt, assTrkPt); registry.fill(HIST("hPhiVsPtAssoc"), RecoDecay::constrainAngle(assTrk.phi(), -o2::constants::math::PIHalf), assTrkPt); registry.fill(HIST("hEtaVsPtAssoc"), assTrk.eta(), assTrkPt); - registry.fill(HIST("hDcaXYVsPtAssoc"), assTrk.dcaXY(), assTrkPt); - registry.fill(HIST("hDcaZVsPtAssoc"), assTrk.dcaZ(), assTrkPt); + registry.fill(HIST("hDcaXYVsPtVsCentAssoc"), assTrkPt, assTrk.dcaXY(), cent); + registry.fill(HIST("hDcaXYVsPtVsMultAssoc"), assTrkPt, assTrk.dcaXY(), mult); + registry.fill(HIST("hDcaZVsPtVsCentAssoc"), assTrkPt, assTrk.dcaZ(), cent); + registry.fill(HIST("hDcaZVsPtVsMultAssoc"), assTrkPt, assTrk.dcaZ(), mult); double deltaPhi = RecoDecay::constrainAngle(assTrk.phi() - trigCand.phi(), -o2::constants::math::PIHalf); + rowAssocBases(rowCollisions.lastIndex(), assTrk.phi(), assTrk.eta(), assTrkPt); rowAssocTrkSels(assTrk.tpcNClsCrossedRows(), assTrk.itsClusterMap(), assTrk.itsNCls(), assTrk.dcaXY(), assTrk.dcaZ()); - if constexpr (CandType == CandidateType::Hadron) { + if constexpr (CandType == TriggerChannels::Hadron) { rowSEHadHadPairs(rowCollisions.lastIndex(), rowTrigHads.lastIndex(), assTrkPt, deltaEta, deltaPhi); } else { rowSECharmHadPairs(rowCollisions.lastIndex(), rowTrigCharms.lastIndex(), assTrkPt, deltaEta, deltaPhi); @@ -408,28 +312,30 @@ struct HfDerivedDataCreatorCorrelationsReduced { /// Fill charm hadron tables for mixed-event /// \param trigCands are the charm trigger candidates - template + template void fillCharmMixedEvent(TTrigCands const& trigCands) { for (const auto& trigCand : trigCands) { - if (!cutCandRapidity(trigCand)) { + if (!hfcFlowHelper.cutCandRapidity(trigCand)) { continue; } registry.fill(HIST("hPhiVsPtTrig"), RecoDecay::constrainAngle(trigCand.phi(), -o2::constants::math::PIHalf), trigCand.pt()); registry.fill(HIST("hEtaVsPtTrig"), trigCand.eta(), trigCand.pt()); - std::array outputMl = getCandMlScores(trigCand); + std::array outputMl = hfcFlowHelper.getCandMlScores(trigCand); rowTrigBases(trigCand.phi(), trigCand.eta()); - rowTrigCharms(rowCollisions.lastIndex(), trigCand.pt(), getCandMass(trigCand), outputMl[0], outputMl[1]); + rowTrigCharms(rowCollisions.lastIndex(), trigCand.pt(), hfcFlowHelper.getCandMass(trigCand), outputMl[0], outputMl[1]); } } /// Fill track tables for mixed-event /// \param assTrks are the associated tracks - /// \param collCentrality is the collision centrality - template + /// \param cent is the collision centrality + /// \param mult is the collision multiplicity + template void fillTrkMixedEvent(TAssocTrks const& assTrks, - const float collCentrality) + const float cent, + const float mult) { bool first = true; for (const auto& assTrk : assTrks) { @@ -445,202 +351,418 @@ struct HfDerivedDataCreatorCorrelationsReduced { } if (!first && downSampleTrksFactor < 1.) { // skip downsampling for the first track to avoid empty tables float const pseudoRndm = assTrkPt * 1000. - static_cast(assTrkPt * 1000); - if (assTrkPt < ptMaxForDownSample && collCentrality < centMaxForDownSample && pseudoRndm >= downSampleTrksFactor) { + if (assTrkPt < ptMaxForDownSample && cent < centMaxForDownSample && pseudoRndm >= downSampleTrksFactor) { continue; } } first = false; registry.fill(HIST("hPhiVsPtAssoc"), RecoDecay::constrainAngle(assTrk.phi(), -o2::constants::math::PIHalf), assTrkPt); registry.fill(HIST("hEtaVsPtAssoc"), assTrk.eta(), assTrkPt); - registry.fill(HIST("hDcaXYVsPtAssoc"), assTrk.dcaXY(), assTrkPt); - registry.fill(HIST("hDcaZVsPtAssoc"), assTrk.dcaZ(), assTrkPt); + registry.fill(HIST("hDcaXYVsPtVsCentAssoc"), assTrkPt, assTrk.dcaXY(), cent); + registry.fill(HIST("hDcaZVsPtVsCentAssoc"), assTrkPt, assTrk.dcaZ(), cent); + registry.fill(HIST("hDcaXYVsPtVsMultAssoc"), assTrkPt, assTrk.dcaXY(), mult); + registry.fill(HIST("hDcaZVsPtVsMultAssoc"), assTrkPt, assTrk.dcaZ(), mult); rowAssocBases(rowCollisions.lastIndex(), assTrk.phi(), assTrk.eta(), assTrkPt); rowAssocTrkSels(assTrk.tpcNClsCrossedRows(), assTrk.itsClusterMap(), assTrk.itsNCls(), assTrk.dcaXY(), assTrk.dcaZ()); } } - // Dplus with ML selections - void processDplusSameEvent(CollsWithCentMult::iterator const& coll, - CandDplusData const& candsDplus, - TracksData const& tracks) + // CHARM-HADRON CORRELATIONS PROCESS FUNCTIONS --- DATA + // Dplus data + void processDataDplusSE(DataCollsWithCentMult::iterator const& coll, + CandDplusData const& candsDplus, + TracksData const& tracks) { if (forceCharmInCollision && candsDplus.size() < 1) { return; } float cent{-1.}, mult{-1.}; - if (!checkCollision(coll, cent, mult)) { + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); - fillSameEvent(candsDplus, tracks, cent); + fillSameEvent(candsDplus, tracks, cent, mult); } - PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDplusSameEvent, "Process Same Event for Dplus candidates", true); + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDataDplusSE, "Process Same Event for Dplus candidates", true); - // Dplus with ML selections - void processDplusMixedEvent(CollsWithCentMult::iterator const& coll, - CandDplusData const& candsDplus, - TracksData const& tracks) + // Dplus data + void processDataDplusME(DataCollsWithCentMult::iterator const& coll, + CandDplusData const& candsDplus, + TracksData const& tracks) { if (forceCharmInCollision && candsDplus.size() < 1) { return; } float cent{-1.}, mult{-1.}; - if (!checkCollision(coll, cent, mult)) { + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); - fillCharmMixedEvent(candsDplus); - fillTrkMixedEvent(tracks, cent); + fillCharmMixedEvent(candsDplus); + fillTrkMixedEvent(tracks, cent, mult); } - PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDplusMixedEvent, "Process Mixed Event for Dplus candidates", false); + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDataDplusME, "Process Mixed Event for Dplus candidates", false); - // Ds with ML selections - void processDsSameEvent(CollsWithCentMult::iterator const& coll, - TracksData const& tracks, - CandDsData const&) + // Ds data + void processDataDsSE(DataCollsWithCentMult::iterator const& coll, + TracksData const& tracks, + CandDsData const&) { - auto candsDsToPiKK = selectedDsToPiKK->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); - auto candsDsToKKPi = selectedDsToKKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsDsToPiKK = selDsToPiKK->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsDsToKKPi = selDsToKKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); if (forceCharmInCollision && candsDsToPiKK.size() < 1 && candsDsToKKPi.size() < 1) { return; } float cent{-1.}, mult{-1.}; - if (!checkCollision(coll, cent, mult)) { + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); - fillSameEvent(candsDsToPiKK, tracks, cent); - fillSameEvent(candsDsToKKPi, tracks, cent); + fillSameEvent(candsDsToPiKK, tracks, cent, mult); + fillSameEvent(candsDsToKKPi, tracks, cent, mult); } - PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDsSameEvent, "Process Same Event for Ds candidates", false); + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDataDsSE, "Process Same Event for Ds candidates", false); - // Ds with ML selections - void processDsMixedEvent(CollsWithCentMult::iterator const& coll, - TracksData const& tracks, - CandDsData const&) + // Ds data + void processDataDsME(DataCollsWithCentMult::iterator const& coll, + TracksData const& tracks, + CandDsData const&) { - auto candsDsToPiKK = selectedDsToPiKK->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); - auto candsDsToKKPi = selectedDsToKKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsDsToPiKK = selDsToPiKK->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsDsToKKPi = selDsToKKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); if (forceCharmInCollision && candsDsToPiKK.size() < 1 && candsDsToKKPi.size() < 1) { return; } float cent{-1.}, mult{-1.}; - if (!checkCollision(coll, cent, mult)) { + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); - fillCharmMixedEvent(candsDsToPiKK); - fillCharmMixedEvent(candsDsToKKPi); - fillTrkMixedEvent(tracks, cent); + fillCharmMixedEvent(candsDsToPiKK); + fillCharmMixedEvent(candsDsToKKPi); + fillTrkMixedEvent(tracks, cent, mult); } - PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDsMixedEvent, "Process Mixed Event for Ds candidates", false); + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDataDsME, "Process Mixed Event for Ds candidates", false); - // D0 with ML selections - void processD0SameEvent(CollsWithCentMult::iterator const& coll, - TracksData const& tracks, - CandD0Data const&) + // D0 data + void processDataD0SE(DataCollsWithCentMult::iterator const& coll, + TracksData const& tracks, + CandD0Data const&) { - auto candsD0ToPiK = selectedD0ToPiK->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); - auto candsD0ToKPi = selectedD0ToKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsD0ToPiK = selD0ToPiK->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsD0ToKPi = selD0ToKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); if (forceCharmInCollision && candsD0ToPiK.size() < 1 && candsD0ToKPi.size() < 1) { return; } float cent{-1.}, mult{-1.}; - if (!checkCollision(coll, cent, mult)) { + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); - fillSameEvent(candsD0ToPiK, tracks, cent); - fillSameEvent(candsD0ToKPi, tracks, cent); + fillSameEvent(candsD0ToPiK, tracks, cent, mult); + fillSameEvent(candsD0ToKPi, tracks, cent, mult); } - PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processD0SameEvent, "Process Same Event for D0 candidates", false); + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDataD0SE, "Process Same Event for D0 candidates", false); - // D0 with ML selections - void processD0MixedEvent(CollsWithCentMult::iterator const& coll, - TracksData const& tracks, - CandD0Data const&) + // D0 data + void processDataD0ME(DataCollsWithCentMult::iterator const& coll, + TracksData const& tracks, + CandD0Data const&) { - auto candsD0ToPiK = selectedD0ToPiK->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); - auto candsD0ToKPi = selectedD0ToKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsD0ToPiK = selD0ToPiK->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsD0ToKPi = selD0ToKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); if (forceCharmInCollision && candsD0ToPiK.size() < 1 && candsD0ToKPi.size() < 1) { return; } float cent{-1.}, mult{-1.}; - if (!checkCollision(coll, cent, mult)) { + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { + return; + } + rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); + fillCharmMixedEvent(candsD0ToPiK); + fillCharmMixedEvent(candsD0ToKPi); + fillTrkMixedEvent(tracks, cent, mult); + } + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDataD0ME, "Process Mixed Event for D0 candidates", false); + + // Lc data + void processDataLcSE(DataCollsWithCentMult::iterator const& coll, + TracksData const& tracks, + CandLcData const&) + { + auto candsLcToPKPi = selLcToPKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsLcToPiKP = selLcToPiKP->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + if (forceCharmInCollision && candsLcToPKPi.size() < 1 && candsLcToPiKP.size() < 1) { + return; + } + float cent{-1.}, mult{-1.}; + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { + return; + } + rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); + fillSameEvent(candsLcToPKPi, tracks, cent, mult); + fillSameEvent(candsLcToPiKP, tracks, cent, mult); + } + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDataLcSE, "Process Same Event for Lc candidates", false); + + // Lc data + void processDataLcME(DataCollsWithCentMult::iterator const& coll, + TracksData const& tracks, + CandLcData const&) + { + auto candsLcToPKPi = selLcToPKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsLcToPiKP = selLcToPiKP->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + if (forceCharmInCollision && candsLcToPKPi.size() < 1 && candsLcToPiKP.size() < 1) { + return; + } + float cent{-1.}, mult{-1.}; + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); - fillCharmMixedEvent(candsD0ToPiK); - fillCharmMixedEvent(candsD0ToKPi); - fillTrkMixedEvent(tracks, cent); + fillCharmMixedEvent(candsLcToPKPi); + fillCharmMixedEvent(candsLcToPiKP); + fillTrkMixedEvent(tracks, cent, mult); } - PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processD0MixedEvent, "Process Mixed Event for D0 candidates", false); + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDataLcME, "Process Mixed Event for Lc candidates", false); - // Lc with ML selections - void processLcSameEvent(CollsWithCentMult::iterator const& coll, - TracksData const& tracks, - CandLcData const&) + // CHARM-HADRON CORRELATIONS PROCESS FUNCTIONS --- MC + // Dplus MC + void processMcDplusSE(McCollsWithCentMult::iterator const& coll, + TracksMc const& tracks, + CandDplusMc const&) { - auto candsLcToPKPi = selectedLcToPKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); - if (forceCharmInCollision && candsLcToPKPi.size() < 1) { + auto candsRecoDplus = recoSelDPlusToPiKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + if (forceCharmInCollision && candsRecoDplus.size() < 1) { return; } float cent{-1.}, mult{-1.}; - if (!checkCollision(coll, cent, mult)) { + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); - fillSameEvent(candsLcToPKPi, tracks, cent); + fillSameEvent(candsRecoDplus, tracks, cent, mult); } - PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processLcSameEvent, "Process Same Event for Lc candidates", false); + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processMcDplusSE, "Process Same Event for Dplus candidates", false); - // Lc with ML selections - void processLcMixedEvent(CollsWithCentMult::iterator const& coll, - TracksData const& tracks, - CandLcData const&) + // Dplus MC + void processMcDplusME(McCollsWithCentMult::iterator const& coll, + TracksMc const& tracks, + CandDplusMc const&) { - auto candsLcToPKPi = selectedLcToPKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); - if (forceCharmInCollision && candsLcToPKPi.size() < 1) { + auto candsRecoDplus = recoSelDPlusToPiKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + if (forceCharmInCollision && candsRecoDplus.size() < 1) { return; } float cent{-1.}, mult{-1.}; - if (!checkCollision(coll, cent, mult)) { + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { + return; + } + rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); + fillCharmMixedEvent(candsRecoDplus); + fillTrkMixedEvent(tracks, cent, mult); + } + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processMcDplusME, "Process Mixed Event for Dplus candidates", false); + + // Ds MC + void processMcDsSE(McCollsWithCentMult::iterator const& coll, + TracksMc const& tracks, + CandDsMc const&) + { + auto candsRecoDsToPiKK = recoSelDsToPiKK->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsRecoDsToKKPi = recoSelDsToKKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + if (forceCharmInCollision && candsRecoDsToPiKK.size() < 1 && candsRecoDsToKKPi.size() < 1) { + return; + } + float cent{-1.}, mult{-1.}; + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { + return; + } + rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); + fillSameEvent(candsRecoDsToPiKK, tracks, cent, mult); + fillSameEvent(candsRecoDsToKKPi, tracks, cent, mult); + } + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processMcDsSE, "Process Same Event for Ds candidates", false); + + // Ds MC + void processMcDsME(McCollsWithCentMult::iterator const& coll, + TracksMc const& tracks, + CandDsMc const&) + { + auto candsRecoDsToPiKK = recoSelDsToPiKK->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsRecoDsToKKPi = recoSelDsToKKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + if (forceCharmInCollision && candsRecoDsToPiKK.size() < 1 && candsRecoDsToKKPi.size() < 1) { + return; + } + float cent{-1.}, mult{-1.}; + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { + return; + } + rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); + fillCharmMixedEvent(candsRecoDsToPiKK); + fillCharmMixedEvent(candsRecoDsToKKPi); + fillTrkMixedEvent(tracks, cent, mult); + } + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processMcDsME, "Process Mixed Event for Ds candidates", false); + + // D0 MC + void processMcD0SE(McCollsWithCentMult::iterator const& coll, + TracksMc const& tracks, + CandD0Mc const&) + { + auto candsRecoD0ToPiK = recoSelD0ToPiK->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsRecoD0ToKPi = recoSelD0ToKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + if (forceCharmInCollision && candsRecoD0ToPiK.size() < 1 && candsRecoD0ToKPi.size() < 1) { + return; + } + float cent{-1.}, mult{-1.}; + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { + return; + } + rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); + fillSameEvent(candsRecoD0ToPiK, tracks, cent, mult); + fillSameEvent(candsRecoD0ToKPi, tracks, cent, mult); + } + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processMcD0SE, "Process Same Event for D0 candidates", false); + + // D0 MC + void processMcD0ME(McCollsWithCentMult::iterator const& coll, + TracksMc const& tracks, + CandD0Mc const&) + { + auto candsRecoD0ToPiK = recoSelD0ToPiK->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsRecoD0ToKPi = recoSelD0ToKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + if (forceCharmInCollision && candsRecoD0ToPiK.size() < 1 && candsRecoD0ToKPi.size() < 1) { + return; + } + float cent{-1.}, mult{-1.}; + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { + return; + } + rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); + fillCharmMixedEvent(candsRecoD0ToPiK); + fillCharmMixedEvent(candsRecoD0ToKPi); + fillTrkMixedEvent(tracks, cent, mult); + } + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processMcD0ME, "Process Mixed Event for D0 candidates", false); + + // Lc MC + void processMcLcSE(McCollsWithCentMult::iterator const& coll, + TracksMc const& tracks, + CandLcMc const&) + { + auto candsRecoLcToPKPi = recoSelLcToPKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsRecoLcToPiKP = recoSelLcToPiKP->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + if (forceCharmInCollision && candsRecoLcToPKPi.size() < 1 && candsRecoLcToPiKP.size() < 1) { + return; + } + float cent{-1.}, mult{-1.}; + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { + return; + } + rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); + fillSameEvent(candsRecoLcToPKPi, tracks, cent, mult); + fillSameEvent(candsRecoLcToPiKP, tracks, cent, mult); + } + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processMcLcSE, "Process Same Event for Lc candidates", false); + + // Lc MC + void processMcLcME(McCollsWithCentMult::iterator const& coll, + TracksMc const& tracks, + CandLcMc const&) + { + auto candsRecoLcToPKPi = recoSelLcToPKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsRecoLcToPiKP = recoSelLcToPiKP->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + if (forceCharmInCollision && candsRecoLcToPKPi.size() < 1 && candsRecoLcToPiKP.size() < 1) { + return; + } + float cent{-1.}, mult{-1.}; + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { + return; + } + rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); + fillCharmMixedEvent(candsRecoLcToPKPi); + fillCharmMixedEvent(candsRecoLcToPiKP); + fillTrkMixedEvent(tracks, cent, mult); + } + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processMcLcME, "Process Mixed Event for Lc candidates", false); + + // HADRON-HADRON CORRELATIONS PROCESS FUNCTIONS --- DATA + // Hadron Hadron Same Event + void processDataHadronHadronSE(DataCollsWithCentMult::iterator const& coll, + TracksData const& tracks) + { + float cent{-1.}, mult{-1.}; + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { + return; + } + rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); + fillSameEvent(tracks, tracks, cent, mult); + } + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDataHadronHadronSE, "Process Same Event for hadron candidates", false); + + // Hadron Hadron Mixed Event + void processDataHadronHadronME(DataCollsWithCentMult::iterator const& coll, + TracksData const& tracks) + { + float cent{-1.}, mult{-1.}; + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); - fillCharmMixedEvent(candsLcToPKPi); - fillTrkMixedEvent(tracks, cent); + fillTrkMixedEvent(tracks, cent, mult); } - PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processLcMixedEvent, "Process Mixed Event for Lc candidates", false); + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDataHadronHadronME, "Process Mixed Event for hadron candidates", false); + // HADRON-HADRON CORRELATIONS PROCESS FUNCTIONS --- MC // Hadron Hadron Same Event - void processHadronHadronSameEvent(CollsWithCentMult::iterator const& coll, - TracksData const& tracks) + void processMcHadronHadronSE(McCollsWithCentMult::iterator const& coll, + TracksMc const& tracks) { float cent{-1.}, mult{-1.}; - if (!checkCollision(coll, cent, mult)) { + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); - fillSameEvent(tracks, tracks, cent); + fillSameEvent(tracks, tracks, cent, mult); } - PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processHadronHadronSameEvent, "Process Same Event for hadron candidates", false); + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processMcHadronHadronSE, "Process Same Event for hadron candidates", false); // Hadron Hadron Mixed Event - void processHadronHadronMixedEvent(CollsWithCentMult::iterator const& coll, - TracksData const& tracks) + void processMcHadronHadronME(McCollsWithCentMult::iterator const& coll, + TracksMc const& tracks) { float cent{-1.}, mult{-1.}; - if (!checkCollision(coll, cent, mult)) { + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); - fillTrkMixedEvent(tracks, cent); + fillTrkMixedEvent(tracks, cent, mult); } - PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processHadronHadronMixedEvent, "Process Mixed Event for hadron candidates", false); + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processMcHadronHadronME, "Process Mixed Event for hadron candidates", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(context)}; } diff --git a/PWGHF/HFC/TableProducer/producerCharmHadronsTrackFemtoDream.cxx b/PWGHF/HFC/TableProducer/producerCharmHadronsTrackFemtoDream.cxx index dbf1d9deb2d..0b1102163c7 100644 --- a/PWGHF/HFC/TableProducer/producerCharmHadronsTrackFemtoDream.cxx +++ b/PWGHF/HFC/TableProducer/producerCharmHadronsTrackFemtoDream.cxx @@ -435,9 +435,9 @@ struct HfProducerCharmHadronsTrackFemtoDream { void fillDebugParticle(ParticleType const& particle) { tables.outputDebugParts(particle.sign(), - (uint8_t)particle.tpcNClsFound(), + static_cast(particle.tpcNClsFound()), particle.tpcNClsFindable(), - (uint8_t)particle.tpcNClsCrossedRows(), + static_cast(particle.tpcNClsCrossedRows()), particle.tpcNClsShared(), particle.tpcInnerParam(), particle.itsNCls(), @@ -465,8 +465,30 @@ struct HfProducerCharmHadronsTrackFemtoDream { -999., -999., -999.); } + /// Classify true kaons from the correct collision for the DCA templates. + template + static int getKaonOrigin(T const& particles, typename T::iterator const& particle) + { + using namespace o2::aod::femtodreamMCparticle; + constexpr int GenFromTransport = -1; + if (particle.getProcess() == TMCProcess::kPHInhelastic && particle.getGenStatusCode() == GenFromTransport) { + return kMaterial; + } + // HF decay products can be physical primaries. Check their ancestry first. + if (RecoDecay::getParticleOrigin(particles, particle) != RecoDecay::OriginType::None) { + return kSecondaryFromHF; + } + if (particle.isPhysicalPrimary()) { + return kPrimary; + } + if (particle.getProcess() == TMCProcess::kPDecay && particle.getGenStatusCode() == GenFromTransport && particle.has_mothers()) { + return kSecondaryFromLF; + } + return kElse; + } + template - void fillMcParticle(CollisionType const& col, ParticleType const& particle, o2::aod::femtodreamparticle::ParticleType fdparttype) + void fillMcParticle(CollisionType const& col, ParticleType const& particle, o2::aod::femtodreamparticle::ParticleType fdparttype, aod::McParticles const& mcParticles) { if (particle.has_mcParticle()) { // get corresponding MC particle and its info @@ -484,6 +506,11 @@ struct HfProducerCharmHadronsTrackFemtoDream { // check if the collision associated with the particle is the same as the analyzed collision by checking their Ids if ((col.has_mcCollision() && (particleMc.mcCollisionId() != col.mcCollisionId())) || !col.has_mcCollision()) { particleOrigin = aod::femtodreamMCparticle::ParticleOriginMCTruth::kWrongCollision; + } else if (std::abs(pdgCode) == kKPlus) { + particleOrigin = getKaonOrigin(mcParticles, particleMc); + if (!motherparticlesMc.empty()) { + pdgCodeMother = motherparticlesMc.front().pdgCode(); + } // check if particle is primary } else if (particleMc.isPhysicalPrimary()) { particleOrigin = aod::femtodreamMCparticle::ParticleOriginMCTruth::kPrimary; @@ -537,7 +564,7 @@ struct HfProducerCharmHadronsTrackFemtoDream { } template - bool fillTracksForCharmHadron(CollisionType const& col, TrackType const& tracks) + bool fillTracksForCharmHadron(CollisionType const& col, TrackType const& tracks, aod::McParticles const* mcParticles = nullptr) { std::vector const childIDs = {0, 0}; // these IDs are necessary to keep track of the children @@ -590,14 +617,14 @@ struct HfProducerCharmHadronsTrackFemtoDream { } if constexpr (IsMc) { - fillMcParticle(col, track, o2::aod::femtodreamparticle::ParticleType::kTrack); + fillMcParticle(col, track, o2::aod::femtodreamparticle::ParticleType::kTrack, *mcParticles); } } return fIsTrackFilled; } template - void fillCharmHadronTable(CollisionType const& col, TrackType const& tracks, CandType const& candidates) + void fillCharmHadronTable(CollisionType const& col, TrackType const& tracks, CandType const& candidates, aod::McParticles const* mcParticles = nullptr) { const auto vtxZ = col.posZ(); const auto sizeCand = candidates.size(); @@ -884,7 +911,7 @@ struct HfProducerCharmHadronsTrackFemtoDream { fillTable(2, candidate.isSelDstarToD0Pi(), outputMlDstar.at(0), outputMlDstar.at(1), outputMlDstar.at(2)); } } - isTrackFilled = fillTracksForCharmHadron(col, tracks); + isTrackFilled = fillTracksForCharmHadron(col, tracks, mcParticles); aod::femtodreamcollision::BitMaskType bitTrack = 0; if (isTrackFilled) { @@ -908,7 +935,7 @@ struct HfProducerCharmHadronsTrackFemtoDream { } template - void fillXicHadronTable(CollisionType const& col, TrackType const& tracks, CandType const& candidates, DaughterTrackType const&) + void fillXicHadronTable(CollisionType const& col, TrackType const& tracks, CandType const& candidates, DaughterTrackType const&, aod::McParticles const* mcParticles = nullptr) { const auto vtxZ = col.posZ(); const auto sizeCand = candidates.size(); @@ -1030,7 +1057,7 @@ struct HfProducerCharmHadronsTrackFemtoDream { } } - isTrackFilled = fillTracksForCharmHadron(col, tracks); + isTrackFilled = fillTracksForCharmHadron(col, tracks, mcParticles); aod::femtodreamcollision::BitMaskType bitTrack = 0; if (isTrackFilled) { @@ -1153,25 +1180,25 @@ struct HfProducerCharmHadronsTrackFemtoDream { void processMcD0ToPiK(FemtoFullCollisionMc const& col, aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, CandidateD0Mc const& candidates) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); - fillCharmHadronTable(col, tracks, candidates); + fillCharmHadronTable(col, tracks, candidates, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcD0ToPiK, "Provide Mc for D0ToPiK", false); void processMcD0ToPiKWithML(FemtoFullCollisionMc const& col, aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, soa::Join const& candidates) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); - fillCharmHadronTable(col, tracks, candidates); + fillCharmHadronTable(col, tracks, candidates, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcD0ToPiKWithML, "Provide Mc for D0ToPiK with ml", false); @@ -1208,25 +1235,25 @@ struct HfProducerCharmHadronsTrackFemtoDream { void processMcDstarToD0Pi(FemtoFullCollisionMc const& col, aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, CandidateDstarMc const& candidates) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); - fillCharmHadronTable(col, tracks, candidates); + fillCharmHadronTable(col, tracks, candidates, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcDstarToD0Pi, "Provide Mc for DstarToD0Pi", false); void processMcDstarToD0PiWithML(FemtoFullCollisionMc const& col, aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, soa::Join const& candidates) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); - fillCharmHadronTable(col, tracks, candidates); + fillCharmHadronTable(col, tracks, candidates, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcDstarToD0PiWithML, "Provide Mc for DstarToD0Pi with ml", false); @@ -1265,25 +1292,25 @@ struct HfProducerCharmHadronsTrackFemtoDream { void processMcDplusToPiKPi(FemtoFullCollisionMc const& col, aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, CandidateDplusMc const& candidates) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); - fillCharmHadronTable(col, tracks, candidates); + fillCharmHadronTable(col, tracks, candidates, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcDplusToPiKPi, "Provide Mc for DplusToPiKPi", false); void processMcDplusToPiKPiWithML(FemtoFullCollisionMc const& col, aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, soa::Join const& candidates) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); - fillCharmHadronTable(col, tracks, candidates); + fillCharmHadronTable(col, tracks, candidates, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcDplusToPiKPiWithML, "Provide Mc for DplusToPiKPi with ml", false); @@ -1322,25 +1349,25 @@ struct HfProducerCharmHadronsTrackFemtoDream { void processMcLcToPKPi(FemtoFullCollisionMc const& col, aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, CandidateLcMc const& candidates) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); - fillCharmHadronTable(col, tracks, candidates); + fillCharmHadronTable(col, tracks, candidates, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcLcToPKPi, "Provide Mc for lctopkpi", false); void processMcLcToPKPiWithML(FemtoFullCollisionMc const& col, aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, soa::Join const& candidates) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); - fillCharmHadronTable(col, tracks, candidates); + fillCharmHadronTable(col, tracks, candidates, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcLcToPKPiWithML, "Provide Mc for lctopkpi with ml", false); @@ -1400,11 +1427,11 @@ struct HfProducerCharmHadronsTrackFemtoDream { aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, aod::FullTracks const& daughterTracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, soa::Filtered const& candidates) { getMagneticFieldTesla(col.bc_as()); - fillXicHadronTable(col, tracks, candidates, daughterTracks); + fillXicHadronTable(col, tracks, candidates, daughterTracks, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcXicToXiPiPi, "MC for XicToXiPiPi (DCAFitter)", false); @@ -1412,11 +1439,11 @@ struct HfProducerCharmHadronsTrackFemtoDream { aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, aod::FullTracks const& daughterTracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, soa::Filtered const& candidates) { getMagneticFieldTesla(col.bc_as()); - fillXicHadronTable(col, tracks, candidates, daughterTracks); + fillXicHadronTable(col, tracks, candidates, daughterTracks, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcXicToXiPiPiKf, "MC for XicToXiPiPi (KFParticle)", false); @@ -1424,11 +1451,11 @@ struct HfProducerCharmHadronsTrackFemtoDream { aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, aod::FullTracks const& daughterTracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, soa::Filtered> const& candidates) { getMagneticFieldTesla(col.bc_as()); - fillXicHadronTable(col, tracks, candidates, daughterTracks); + fillXicHadronTable(col, tracks, candidates, daughterTracks, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcXicToXiPiPiWithML, "MC for XicToXiPiPi with ML (DCAFitter)", false); @@ -1436,11 +1463,11 @@ struct HfProducerCharmHadronsTrackFemtoDream { aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, aod::FullTracks const& daughterTracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, soa::Filtered> const& candidates) { getMagneticFieldTesla(col.bc_as()); - fillXicHadronTable(col, tracks, candidates, daughterTracks); + fillXicHadronTable(col, tracks, candidates, daughterTracks, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcXicToXiPiPiWithMLKf, "MC for XicToXiPiPi with ML (KFParticle)", false); diff --git a/PWGHF/HFC/TableProducer/producerCharmHadronsV0FemtoDream.cxx b/PWGHF/HFC/TableProducer/producerCharmHadronsV0FemtoDream.cxx index fa5c70e33d0..4b8594f8833 100644 --- a/PWGHF/HFC/TableProducer/producerCharmHadronsV0FemtoDream.cxx +++ b/PWGHF/HFC/TableProducer/producerCharmHadronsV0FemtoDream.cxx @@ -449,9 +449,9 @@ struct HfProducerCharmHadronsV0FemtoDream { { if constexpr (isTrackOrV0) { outputDebugParts(particle.sign(), - (uint8_t)particle.tpcNClsFound(), + static_cast(particle.tpcNClsFound()), particle.tpcNClsFindable(), - (uint8_t)particle.tpcNClsCrossedRows(), + static_cast(particle.tpcNClsCrossedRows()), particle.tpcNClsShared(), particle.tpcInnerParam(), particle.itsNCls(), diff --git a/PWGHF/HFC/Tasks/CMakeLists.txt b/PWGHF/HFC/Tasks/CMakeLists.txt index 69e279424c9..8c60c81b29d 100644 --- a/PWGHF/HFC/Tasks/CMakeLists.txt +++ b/PWGHF/HFC/Tasks/CMakeLists.txt @@ -21,6 +21,7 @@ o2physics_add_dpl_workflow(task-charm-hadrons-v0-femto-dream o2physics_add_dpl_workflow(task-correlation-d0-hadrons SOURCES taskCorrelationD0Hadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -41,11 +42,13 @@ o2physics_add_dpl_workflow(task-correlation-dplus-dplus-reduced o2physics_add_dpl_workflow(task-correlation-dplus-hadrons SOURCES taskCorrelationDplusHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-correlation-ds-hadrons SOURCES taskCorrelationDsHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -61,14 +64,21 @@ o2physics_add_dpl_workflow(task-correlation-hfe-hadrons o2physics_add_dpl_workflow(task-correlation-lc-hadrons SOURCES taskCorrelationLcHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-flow SOURCES taskFlow.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGCFCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(task-correlations-efficiencies + SOURCES taskCorrelationsEfficiencies.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils + COMPONENT_NAME Analysis) + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/PWGCF/Core) o2physics_add_dpl_workflow(task-charm-hadrons-charm-femto-dream diff --git a/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx index 264b4ac2d25..74ab959c959 100644 --- a/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx +++ b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx @@ -113,9 +113,9 @@ struct HfTaskCharmHadronsCharmFemtoDream { ConfigurableAxis mixingBinMult{"mixingBinMult", {VARIABLE_WIDTH, 0.f, 20.f, 60.f, 200.f}, "Mixing bins - multiplicity"}; ConfigurableAxis mixingBinMultPercentile{"mixingBinMultPercentile", {VARIABLE_WIDTH, 0.f, 100.f}, "Mixing bins - multiplicity percentile"}; ConfigurableAxis mixingBinVztx{"mixingBinVztx", {VARIABLE_WIDTH, -10.f, -4.f, 0.f, 4.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinningMult{{mixingBinVztx, mixingBinMult}, true}; - ColumnBinningPolicy colBinningMultPercentile{{mixingBinVztx, mixingBinMultPercentile}, true}; - ColumnBinningPolicy colBinningMultMultPercentile{{mixingBinVztx, mixingBinMult, mixingBinMultPercentile}, true}; + ColumnBinningPolicy colBinningMult{{mixingBinVztx, mixingBinMult}}; + ColumnBinningPolicy colBinningMultPercentile{{mixingBinVztx, mixingBinMultPercentile}}; + ColumnBinningPolicy colBinningMultMultPercentile{{mixingBinVztx, mixingBinMult, mixingBinMultPercentile}}; aod::femtodreamcollision::BitMaskType bitMask = 1 << 0; HistogramRegistry registry{"registry"}; @@ -127,9 +127,9 @@ struct HfTaskCharmHadronsCharmFemtoDream { if (mixSetting.mixingDepth < 0 || mixSetting.mixingBinPolicy < 0 || mixSetting.mixingBinPolicy > MixingBinPolicyMax || ptD0Min < 0 || ptD0Min >= ptD0Max || ptDstarMin < 0 || ptDstarMin >= ptDstarMax || etaCandMax <= 0 || massD0Min >= massD0Max || deltaMassDstarMin >= deltaMassDstarMax || massD0DaughterMin >= massD0DaughterMax || charmHadCandSel < 1 || eventSel.multMin > eventSel.multMax || eventSel.multPercentileMin > eventSel.multPercentileMax) { LOGP(fatal, "Invalid charm-charm selection or mixing configuration"); } - colBinningMult = {{mixingBinVztx, mixingBinMult}, true}; - colBinningMultPercentile = {{mixingBinVztx, mixingBinMultPercentile}, true}; - colBinningMultMultPercentile = {{mixingBinVztx, mixingBinMult, mixingBinMultPercentile}, true}; + colBinningMult = {{mixingBinVztx, mixingBinMult}}; + colBinningMultPercentile = {{mixingBinVztx, mixingBinMultPercentile}}; + colBinningMultMultPercentile = {{mixingBinVztx, mixingBinMult, mixingBinMultPercentile}}; const AxisSpec kstar{400, 0., 2., "k* (GeV/c)"}; const AxisSpec massD0{300, massD0Min.value, massD0Max.value, "M(Kpi) (GeV/c2)"}; const AxisSpec deltaMass{310, deltaMassDstarMin.value, deltaMassDstarMax.value, "Delta M (GeV/c2)"}; diff --git a/PWGHF/HFC/Tasks/taskCharmHadronsTrackFemtoDream.cxx b/PWGHF/HFC/Tasks/taskCharmHadronsTrackFemtoDream.cxx index aeb4e57e88d..cfbd216ec22 100644 --- a/PWGHF/HFC/Tasks/taskCharmHadronsTrackFemtoDream.cxx +++ b/PWGHF/HFC/Tasks/taskCharmHadronsTrackFemtoDream.cxx @@ -202,10 +202,10 @@ struct HfTaskCharmHadronsTrackFemtoDream { Filter eventMultiplicityPercentile = aod::femtodreamcollision::multV0M >= eventSel.multPercentileMin && aod::femtodreamcollision::multV0M <= eventSel.multPercentileMax; Filter hfCandSelFilter = aod::fdhf::candidateSelFlag >= charmSel.charmHadCandSel; Filter hfMcSelFilter = (nabs(aod::fdhf::flagMc) == charmSel.charmHadMcSel); - Filter trackEtaFilterLow = ifnode(aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack), aod::femtodreamparticle::eta < trackSel.etaTrack1Max, true); - Filter trackEtaFilterUp = ifnode(aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack), aod::femtodreamparticle::eta > trackSel.etaTrack1Min, true); - Filter trackPtFilterLow = ifnode(aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack), aod::femtodreamparticle::pt < trackSel.ptTrack1Max, true); - Filter trackPtFilterUp = ifnode(aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack), aod::femtodreamparticle::pt > trackSel.ptTrack1Min, true); + Filter trackEtaFilterLow = ifnode(aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack), aod::femtodreamparticle::eta < trackSel.etaTrack1Max, true); + Filter trackEtaFilterUp = ifnode(aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack), aod::femtodreamparticle::eta > trackSel.etaTrack1Min, true); + Filter trackPtFilterLow = ifnode(aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack), aod::femtodreamparticle::pt < trackSel.ptTrack1Max, true); + Filter trackPtFilterUp = ifnode(aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack), aod::femtodreamparticle::pt > trackSel.ptTrack1Min, true); struct : PresliceGroup { Preslice perCol = aod::femtodreamparticle::fdCollisionId; @@ -216,14 +216,14 @@ struct HfTaskCharmHadronsTrackFemtoDream { } preslices; /// Partition for particle 1 - Partition partitionTrk1 = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack)) && (ncheckbit(aod::femtodreamparticle::cut, trackSel.cutBitTrack1)) && ifnode(aod::femtodreamparticle::pt * coshEta(aod::femtodreamparticle::eta) <= trackSel.pidThresTrack1, ncheckbit(aod::femtodreamparticle::pidcut, trackSel.tpcBitTrack1), ncheckbit(aod::femtodreamparticle::pidcut, trackSel.tpcTofBitTrack1)); - Partition partitionTrk1Ka = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack)) && (ncheckbit(aod::femtodreamparticle::cut, trackSel.cutBitTrack1)) && (aod::femtodreamparticle::pidcut == PidTrackPass); + Partition partitionTrk1 = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack)) && (ncheckbit(aod::femtodreamparticle::cut, trackSel.cutBitTrack1)) && ifnode(aod::femtodreamparticle::pt * coshEta(aod::femtodreamparticle::eta) <= trackSel.pidThresTrack1, ncheckbit(aod::femtodreamparticle::pidcut, trackSel.tpcBitTrack1), ncheckbit(aod::femtodreamparticle::pidcut, trackSel.tpcTofBitTrack1)); + Partition partitionTrk1Ka = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack)) && (ncheckbit(aod::femtodreamparticle::cut, trackSel.cutBitTrack1)) && (aod::femtodreamparticle::pidcut == PidTrackPass); - Partition partitionMcTrk1 = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack)) && + Partition partitionMcTrk1 = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack)) && (ncheckbit(aod::femtodreamparticle::cut, trackSel.cutBitTrack1)) && ifnode(aod::femtodreamparticle::pt * coshEta(aod::femtodreamparticle::eta) <= trackSel.pidThresTrack1, ncheckbit(aod::femtodreamparticle::pidcut, trackSel.tpcBitTrack1), ncheckbit(aod::femtodreamparticle::pidcut, trackSel.tpcTofBitTrack1)); - Partition partitionMcTrk1Ka = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack)) && (ncheckbit(aod::femtodreamparticle::cut, trackSel.cutBitTrack1)) && (aod::femtodreamparticle::pidcut == PidTrackPass); + Partition partitionMcTrk1Ka = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack)) && (ncheckbit(aod::femtodreamparticle::cut, trackSel.cutBitTrack1)) && (aod::femtodreamparticle::pidcut == PidTrackPass); /// Partition for particle 2 Partition partitionCharmHadron3Prong = aod::fdhf::bdtBkg < charmSel.charmHadBkgBDTmax && aod::fdhf::bdtFD < charmSel.charmHadFdBDTmax && aod::fdhf::bdtFD > charmSel.charmHadFdBDTmin&& aod::fdhf::bdtPrompt charmSel.charmHadPromptBDTmin; @@ -263,9 +263,9 @@ struct HfTaskCharmHadronsTrackFemtoDream { ConfigurableAxis mixingBinMultPercentile{"mixingBinMultPercentile", {VARIABLE_WIDTH, 0.0f, 100.f}, "Mixing bins - multiplicity percentile"}; ConfigurableAxis mixingBinVztx{"mixingBinVztx", {VARIABLE_WIDTH, -10.0f, -4.f, 0.f, 4.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinningMult{{mixingBinVztx, mixingBinMult}, true}; - ColumnBinningPolicy colBinningMultPercentile{{mixingBinVztx, mixingBinMultPercentile}, true}; - ColumnBinningPolicy colBinningMultMultPercentile{{mixingBinVztx, mixingBinMult, mixingBinMultPercentile}, true}; + ColumnBinningPolicy colBinningMult{{mixingBinVztx, mixingBinMult}}; + ColumnBinningPolicy colBinningMultPercentile{{mixingBinVztx, mixingBinMultPercentile}}; + ColumnBinningPolicy colBinningMultMultPercentile{{mixingBinVztx, mixingBinMult, mixingBinMultPercentile}}; FemtoDreamContainer sameEventCont; FemtoDreamContainer mixedEventCont; @@ -312,9 +312,9 @@ struct HfTaskCharmHadronsTrackFemtoDream { massTwo = o2::analysis::femtoDream::getMass(charmSel.charmHadPDGCode.value); // setup columnpolicy for binning - colBinningMult = {{mixingBinVztx, mixingBinMult}, true}; - colBinningMultPercentile = {{mixingBinVztx, mixingBinMultPercentile}, true}; - colBinningMultMultPercentile = {{mixingBinVztx, mixingBinMult, mixingBinMultPercentile}, true}; + colBinningMult = {{mixingBinVztx, mixingBinMult}}; + colBinningMultPercentile = {{mixingBinVztx, mixingBinMultPercentile}}; + colBinningMultMultPercentile = {{mixingBinVztx, mixingBinMult, mixingBinMultPercentile}}; eventHisto.init(®istry); allTrackHisto.init(®istry, binmultTempFit, binMulPercentile, binpTTrack, binEta, binPhi, binTempFitVarTrack, binNSigmaTPC, binNSigmaTOF, binNSigmaTPCTOF, binTPCClusters, dummy, dummy, isMc, trackSel.pdgCodeTrack1, true); selectedTrackHisto.init(®istry, binmultTempFit, binMulPercentile, binpTTrack, binEta, binPhi, binTempFitVarTrack, binNSigmaTPC, binNSigmaTOF, binNSigmaTPCTOF, binTPCClusters, dummy, dummy, isMc, trackSel.pdgCodeTrack1, true); diff --git a/PWGHF/HFC/Tasks/taskCharmHadronsV0FemtoDream.cxx b/PWGHF/HFC/Tasks/taskCharmHadronsV0FemtoDream.cxx index 4ffdd61583c..29538febfce 100644 --- a/PWGHF/HFC/Tasks/taskCharmHadronsV0FemtoDream.cxx +++ b/PWGHF/HFC/Tasks/taskCharmHadronsV0FemtoDream.cxx @@ -195,7 +195,7 @@ struct HfTaskCharmHadronsV0FemtoDream { Preslice perHfDstarByCol = aod::femtodreamparticle::fdCollisionId; /// Partitions for particle Lambda - Partition partitionLambda = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kV0)) && + Partition partitionLambda = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kV0)) && ((aod::femtodreamparticle::cut & v0Sel.cutBit) == v0Sel.cutBit) && (aod::femtodreamparticle::pt > v0Sel.ptV0Min) && (aod::femtodreamparticle::pt < v0Sel.ptV0Max) && @@ -206,7 +206,7 @@ struct HfTaskCharmHadronsV0FemtoDream { (aod::femtodreamparticle::mAntiLambda > v0Sel.invMassAntiV0Min) && (aod::femtodreamparticle::mAntiLambda < v0Sel.invMassAntiV0Max); - Partition partitionMcLambda = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kV0)) && + Partition partitionMcLambda = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kV0)) && ((aod::femtodreamparticle::cut & v0Sel.cutBit) == v0Sel.cutBit) && (aod::femtodreamparticle::pt > v0Sel.ptV0Min) && (aod::femtodreamparticle::pt < v0Sel.ptV0Max) && @@ -218,7 +218,7 @@ struct HfTaskCharmHadronsV0FemtoDream { (aod::femtodreamparticle::mAntiLambda < v0Sel.invMassAntiV0Max); /// Partitions for particle K0Short - Partition partitionK0Short = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kV0K0Short)) && + Partition partitionK0Short = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kV0K0Short)) && ((aod::femtodreamparticle::cut & v0Sel.cutBit) == v0Sel.cutBit) && (aod::femtodreamparticle::pt > v0Sel.ptV0Min) && (aod::femtodreamparticle::pt < v0Sel.ptV0Max) && @@ -229,7 +229,7 @@ struct HfTaskCharmHadronsV0FemtoDream { (aod::femtodreamparticle::mAntiLambda > v0Sel.invMassAntiV0Min) && (aod::femtodreamparticle::mAntiLambda < v0Sel.invMassAntiV0Max); - Partition partitionMcK0Short = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kV0K0Short)) && + Partition partitionMcK0Short = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kV0K0Short)) && ((aod::femtodreamparticle::cut & v0Sel.cutBit) == v0Sel.cutBit) && (aod::femtodreamparticle::pt > v0Sel.ptV0Min) && (aod::femtodreamparticle::pt < v0Sel.ptV0Max) && @@ -279,9 +279,9 @@ struct HfTaskCharmHadronsV0FemtoDream { ConfigurableAxis mixingBinVztx{"mixingBinVztx", {VARIABLE_WIDTH, -10.0f, -4.f, 0.f, 4.f, 10.f}, "Mixing bins - z-vertex"}; } AxisBinning; - ColumnBinningPolicy colBinningMult{{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMult}, true}; - ColumnBinningPolicy colBinningMultPercentile{{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMultPercentile}, true}; - ColumnBinningPolicy colBinningMultMultPercentile{{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMult, AxisBinning.mixingBinMultPercentile}, true}; + ColumnBinningPolicy colBinningMult{{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMult}}; + ColumnBinningPolicy colBinningMultPercentile{{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMultPercentile}}; + ColumnBinningPolicy colBinningMultMultPercentile{{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMult, AxisBinning.mixingBinMultPercentile}}; FemtoDreamContainer sameEventCont; FemtoDreamContainer mixedEventCont; @@ -330,9 +330,9 @@ struct HfTaskCharmHadronsV0FemtoDream { bool processDstar = doprocessDataDstarV0; // setup columnpolicy for binning - colBinningMult = {{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMult}, true}; - colBinningMultPercentile = {{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMultPercentile}, true}; - colBinningMultMultPercentile = {{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMult, AxisBinning.mixingBinMultPercentile}, true}; + colBinningMult = {{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMult}}; + colBinningMultPercentile = {{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMultPercentile}}; + colBinningMultMultPercentile = {{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMult, AxisBinning.mixingBinMultPercentile}}; eventHisto.init(®istry); v0HistoPartOne.init(®istry, AxisBinning.binmultTempFit, AxisBinning.dummy, AxisBinning.binpTV0, AxisBinning.binEta, AxisBinning.binPhi, AxisBinning.binTempFitVarV0, AxisBinning.dummy, AxisBinning.dummy, AxisBinning.dummy, AxisBinning.dummy, AxisBinning.binInvMassV0, AxisBinning.binInvMassV0, isMc, v0Sel.pdgCodeV0, true); v0HistoPartOneSelected.init(®istry, AxisBinning.binmultTempFit, AxisBinning.dummy, AxisBinning.binpTV0, AxisBinning.binEta, AxisBinning.binPhi, AxisBinning.binTempFitVarV0, AxisBinning.dummy, AxisBinning.dummy, AxisBinning.dummy, AxisBinning.dummy, AxisBinning.binInvMassV0, AxisBinning.binInvMassV0, isMc, v0Sel.pdgCodeV0, true); diff --git a/PWGHF/HFC/Tasks/taskCorrelationD0Hadrons.cxx b/PWGHF/HFC/Tasks/taskCorrelationD0Hadrons.cxx index 334b2878346..873c0bc052c 100644 --- a/PWGHF/HFC/Tasks/taskCorrelationD0Hadrons.cxx +++ b/PWGHF/HFC/Tasks/taskCorrelationD0Hadrons.cxx @@ -155,8 +155,8 @@ struct HfTaskCorrelationD0Hadrons { AxisSpec axisDeltaEta = {binsEta, "#it{#eta}^{Hadron}-#it{#eta}^{D}"}; AxisSpec const axisEta = {binsEta, "#it{#eta}"}; AxisSpec axisDeltaPhi = {binsPhi, "#it{#varphi}^{Hadron}-#it{#varphi}^{D} (rad)"}; - AxisSpec axisPtD = {(std::vector)binsPtD, "#it{p}_{T}^{D} (GeV/#it{c})"}; - AxisSpec axisPtHadron = {(std::vector)binsPtHadron, "#it{p}_{T}^{Hadron} (GeV/#it{c})"}; + AxisSpec axisPtD = {std::vector(binsPtD), "#it{p}_{T}^{D} (GeV/#it{c})"}; + AxisSpec axisPtHadron = {std::vector(binsPtHadron), "#it{p}_{T}^{Hadron} (GeV/#it{c})"}; AxisSpec axisPoolBin = {binsPoolBin, "poolBin"}; AxisSpec const axisBdtScore = {binsBdtScore, "Bdt score"}; AxisSpec const axisMultFT0M = {binsMultFT0M, "MultiplicityFT0M"}; @@ -207,8 +207,8 @@ struct HfTaskCorrelationD0Hadrons { registry.add("hDeltaEtaPtIntSignalRegionRecSig", "D0-h deltaEta MC reco signal region, signal", {HistType::kTH1F, {axisDeltaEta}}); registry.add("hDeltaPhiPtIntSignalRegionRecSig", "D0-h deltaPhi MC reco signal region, signal", {HistType::kTH1F, {axisDeltaPhi}}); registry.add("hCorrel2DPtIntSignalRegionRecSig", "D0-h deltaPhi vs deltaEta MC reco signal region, signal", {HistType::kTH2F, {{axisDeltaPhi}, {axisDeltaEta}}}); - registry.add("hCorrel2DVsPtSignalRegionRecSig", "D0-h correlations MC reco signal region, signal", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisD0Prompt}, {axisPoolBin}}}); - registry.add("hCorrel2DVsPtPhysicalPrimaryRecSig", "D0-h correlations signal region (only true primary particles) MC reco", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisD0Prompt}, {axisPoolBin}}}); + registry.add("hCorrel2DVsPtSignalRegionRecSig", "D0-h correlations MC reco signal region, signal", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisD0Prompt}, {axisPoolBin}, {axisCentFT0M}}}); + registry.add("hCorrel2DVsPtPhysicalPrimaryRecSig", "D0-h correlations signal region (only true primary particles) MC reco", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisD0Prompt}, {axisPoolBin}, {axisCentFT0M}}}); registry.add("hCorrel2DVsPtSignalRegionPromptD0PromptHadronRecSig", "D0-h correlations signal region Prompt-NonPrompt MC reco", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); registry.add("hCorrel2DVsPtSignalRegionNonPromptD0NonPromptHadronRecSig", "D0-h correlations signal region NonPrompt-NonPrompt MC reco", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); registry.add("hDeltaEtaPtIntSignalRegionSoftPiRecSig", "D0-h deltaEta MC reco signal region, signal", {HistType::kTH1F, {axisDeltaEta}}); @@ -601,6 +601,10 @@ struct HfTaskCorrelationD0Hadrons { } for (const auto& pairEntry : pairEntries) { + float cent = 0.; + if (useCentrality) { + cent = pairEntry.cent(); + } // define variables for widely used quantities double const deltaPhi = pairEntry.deltaPhi(); double const deltaEta = pairEntry.deltaEta(); @@ -685,12 +689,12 @@ struct HfTaskCorrelationD0Hadrons { // ---------------------- Fill plots for signal case, D0 ->1, D0bar ->8 --------------------------------------------- if ((massD > signalRegionLeft->at(ptBinD) && massD < signalRegionRight->at(ptBinD)) && TESTBIT(signalStatus, ParticleTypeMcRec::D0Sig) && passesD0Ml) { // in signal region, tests bit ParticleTypeMcRec::D0Sig, SE-> softpi removed, ME-> inclusive - registry.fill(HIST("hCorrel2DVsPtSignalRegionRecSig"), deltaPhi, deltaEta, ptD, ptHadron, static_cast(isD0Prompt), poolBin, efficiencyWeight); + registry.fill(HIST("hCorrel2DVsPtSignalRegionRecSig"), deltaPhi, deltaEta, ptD, ptHadron, static_cast(isD0Prompt), poolBin, cent, efficiencyWeight); registry.fill(HIST("hCorrel2DPtIntSignalRegionRecSig"), deltaPhi, deltaEta, efficiencyWeight); registry.fill(HIST("hDeltaEtaPtIntSignalRegionRecSig"), deltaEta, efficiencyWeight); registry.fill(HIST("hDeltaPhiPtIntSignalRegionRecSig"), deltaPhi, efficiencyWeight); if (isPhysicalPrimary) { - registry.fill(HIST("hCorrel2DVsPtPhysicalPrimaryRecSig"), deltaPhi, deltaEta, ptD, ptHadron, static_cast(isD0Prompt), poolBin, efficiencyWeight); + registry.fill(HIST("hCorrel2DVsPtPhysicalPrimaryRecSig"), deltaPhi, deltaEta, ptD, ptHadron, static_cast(isD0Prompt), poolBin, cent, efficiencyWeight); if (isD0Prompt && statusPromptHadron == RecoDecay::OriginType::Prompt) { registry.fill(HIST("hCorrel2DVsPtSignalRegionPromptD0PromptHadronRecSig"), deltaPhi, deltaEta, ptD, ptHadron, poolBin, efficiencyWeight); } else if (!isD0Prompt && statusPromptHadron == RecoDecay::OriginType::NonPrompt) { @@ -701,12 +705,12 @@ struct HfTaskCorrelationD0Hadrons { if ((massDbar > signalRegionLeft->at(ptBinD) && massDbar < signalRegionRight->at(ptBinD)) && TESTBIT(signalStatus, ParticleTypeMcRec::D0barSig) && passesD0barMl) { // in signal region, tests bit ParticleTypeMcRec::D0barSig, SE-> softpi removed, ME-> inclusive - registry.fill(HIST("hCorrel2DVsPtSignalRegionRecSig"), deltaPhi, deltaEta, ptD, ptHadron, static_cast(isD0Prompt), poolBin, efficiencyWeight); + registry.fill(HIST("hCorrel2DVsPtSignalRegionRecSig"), deltaPhi, deltaEta, ptD, ptHadron, static_cast(isD0Prompt), poolBin, cent, efficiencyWeight); registry.fill(HIST("hCorrel2DPtIntSignalRegionRecSig"), deltaPhi, deltaEta, efficiencyWeight); registry.fill(HIST("hDeltaEtaPtIntSignalRegionRecSig"), deltaEta, efficiencyWeight); registry.fill(HIST("hDeltaPhiPtIntSignalRegionRecSig"), deltaPhi, efficiencyWeight); if (isPhysicalPrimary) { - registry.fill(HIST("hCorrel2DVsPtPhysicalPrimaryRecSig"), deltaPhi, deltaEta, ptD, ptHadron, static_cast(isD0Prompt), poolBin, efficiencyWeight); + registry.fill(HIST("hCorrel2DVsPtPhysicalPrimaryRecSig"), deltaPhi, deltaEta, ptD, ptHadron, static_cast(isD0Prompt), poolBin, cent, efficiencyWeight); if (isD0Prompt && statusPromptHadron == RecoDecay::OriginType::Prompt) { registry.fill(HIST("hCorrel2DVsPtSignalRegionPromptD0PromptHadronRecSig"), deltaPhi, deltaEta, ptD, ptHadron, poolBin, efficiencyWeight); } else if (!isD0Prompt && statusPromptHadron == RecoDecay::OriginType::NonPrompt) { diff --git a/PWGHF/HFC/Tasks/taskCorrelationDplusDplusReduced.cxx b/PWGHF/HFC/Tasks/taskCorrelationDplusDplusReduced.cxx index c37c1f73feb..a1746b6663a 100644 --- a/PWGHF/HFC/Tasks/taskCorrelationDplusDplusReduced.cxx +++ b/PWGHF/HFC/Tasks/taskCorrelationDplusDplusReduced.cxx @@ -30,6 +30,7 @@ #include #include +#include using namespace o2; using namespace o2::framework; @@ -37,8 +38,13 @@ using namespace o2::framework::expressions; struct HfTaskCorrelationDplusDplusReduced { Configurable selectionFlagDplus{"selectionFlagDplus", 1, "Selection Flag for Dplus"}; + Configurable pseudorapidityMax{"pseudorapidityMax", 1, "Selection the cut of rapidity"}; + Configurable applyMl{"applyMl", false, "Flag to apply ML selections"}; + Configurable applySkimming{"applySkimming", false, "Flag to apply Skimming selections"}; - using SelectedCandidates = soa::Filtered; + using SelectedCandidatesTiny = soa::Filtered; + using SelectedCandidatesLite = soa::Filtered; + using SelectedCandidatesFull = soa::Filtered; using SelectedMcParticles = o2::aod::HfCandDpMcPs; Filter filterSelectCandidates = aod::full::candidateSelFlag >= selectionFlagDplus; @@ -54,24 +60,28 @@ struct HfTaskCorrelationDplusDplusReduced { void init(InitContext const&) { - registry.add("hMassDplus", "D+ candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2}))", {HistType::kTH1F, {{120, 1.5848, 2.1848}}}); - registry.add("hMassDminus", "D- candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2}))", {HistType::kTH1F, {{120, 1.5848, 2.1848}}}); - registry.add("hMassDplusMatched", "D+ matched candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2}))", {HistType::kTH1F, {{120, 1.5848, 2.1848}}}); - registry.add("hMassDminusMatched", "D- matched candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2}))", {HistType::kTH1F, {{120, 1.5848, 2.1848}}}); - registry.add("hMassDplusminusPair", "D plus-minus pair candidates;inv. mass (#pi K) (GeV/#it{c}^{2});inv. mass (#pi K) (GeV/#it{c}^{2})", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {120, 1.5848, 2.1848}}}); - registry.add("hMassDplusPair", "D plus pair candidates;inv. mass (#pi K) (GeV/#it{c}^{2});inv. mass (#pi K) (GeV/#it{c}^{2})", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {120, 1.5848, 2.1848}}}); - registry.add("hMassDminusPair", "D minus pair candidates;inv. mass (#pi K) (GeV/#it{c}^{2});inv. mass (#pi K) (GeV/#it{c}^{2})", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {120, 1.5848, 2.1848}}}); + registry.add("hMassDplus", "D^{+} candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2}))", {HistType::kTH1F, {{120, 1.5848, 2.1848}}}); + registry.add("hMassDminus", "D^{-} candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2}))", {HistType::kTH1F, {{120, 1.5848, 2.1848}}}); + registry.add("hMassDplusMatched", "D^{+} matched candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2}))", {HistType::kTH1F, {{120, 1.5848, 2.1848}}}); + registry.add("hMassDminusMatched", "D^{-} matched candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2}))", {HistType::kTH1F, {{120, 1.5848, 2.1848}}}); + registry.add("hMassDplusPairsMatched", "D^{+} matched pairs candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2});inv. mass (#pi#pi K) (GeV/#it{c}^{2})", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {120, 1.5848, 2.1848}}}); + registry.add("hMassDminusPairsMatched", "D^{-} matched pairs candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2});inv. mass (#pi#pi K) (GeV/#it{c}^{2})", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {120, 1.5848, 2.1848}}}); + registry.add("hMassDplusminusPairsMatched", "D^{+} and D^{-} matched pairs candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2});inv. mass (#pi#pi K) (GeV/#it{c}^{2})", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {120, 1.5848, 2.1848}}}); + registry.add("hMassDplusminusPair", "D^{+-} pair candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2});inv. mass (#pi#pi K) (GeV/#it{c}^{2})", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {120, 1.5848, 2.1848}}}); + registry.add("hMassDplusPair", "D^{+} pair candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2});inv. mass (#pi#pi K) (GeV/#it{c}^{2})", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {120, 1.5848, 2.1848}}}); + registry.add("hMassDminusPair", "D^{-} pair candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2});inv. mass (#pi#pi K) (GeV/#it{c}^{2})", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {120, 1.5848, 2.1848}}}); registry.add("hDltPhiMcGen", "Azimuthal correlation for D mesons; #Delta#phi", {HistType::kTH1F, {{100, -3.141593, 3.141593}}}); } - void processLocalData(o2::aod::HfCandDpFullEvs::iterator const&, - SelectedCandidates const& localCandidates) + template + void processData(const T& localCandidates) { registry.fill(HIST("hNCand"), localCandidates.size()); for (const auto& cand1 : localCandidates) { auto mass1 = cand1.m(); auto sign1 = 1; + auto flattening = 0; if (cand1.pt() < 0) { sign1 = -1; registry.fill(HIST("hMassDminus"), mass1); @@ -85,6 +95,12 @@ struct HfTaskCorrelationDplusDplusReduced { if (cand2.pt() < 0) { sign2 = -1; } + if (flattening == 0) { + std::swap(mass1, mass2); + flattening = 1; + } else if (flattening == 1) { + flattening = 0; + } if (sign1 == sign2) { if (sign1 == 1) { registry.fill(HIST("hMassDplusPair"), mass2, mass1); @@ -97,27 +113,104 @@ struct HfTaskCorrelationDplusDplusReduced { } } } - PROCESS_SWITCH(HfTaskCorrelationDplusDplusReduced, processLocalData, "Process local data", true); - void processLocalDataMcRec(o2::aod::HfCandDpFullEvs::iterator const&, - SelectedCandidates const& localCandidates) + template + void processLocalDataMcRec(const T& localCandidates) { registry.fill(HIST("hNMcRec"), localCandidates.size()); for (const auto& cand1 : localCandidates) { auto mass1 = cand1.m(); + auto sign1 = 1; + auto flattening = 0; if (cand1.pt() < 0) { + sign1 = -1; registry.fill(HIST("hMassDminus"), mass1); - if (std::abs(cand1.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi) + if (std::abs(cand1.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi) { registry.fill(HIST("hMassDminusMatched"), mass1); + } } else { registry.fill(HIST("hMassDplus"), mass1); - if (std::abs(cand1.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi) + if (std::abs(cand1.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi) { registry.fill(HIST("hMassDplusMatched"), mass1); + } + } + + for (auto cand2 = cand1 + 1; cand2 != localCandidates.end(); ++cand2) { + auto mass2 = cand2.m(); + auto sign2 = 1; + if (cand2.pt() < 0) { + sign2 = -1; + } + if (flattening == 0) { + std::swap(mass1, mass2); + flattening = 1; + } else if (flattening == 1) { + flattening = 0; + } + if (sign1 == sign2) { + if (sign1 == 1) { + registry.fill(HIST("hMassDplusPair"), mass1, mass2); + if (std::abs(cand2.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi && std::abs(cand1.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi) { + registry.fill(HIST("hMassDplusPairsMatched"), mass1, mass2); + } + } else { + registry.fill(HIST("hMassDminusPair"), mass1, mass2); + if (std::abs(cand2.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi && std::abs(cand1.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi) { + registry.fill(HIST("hMassDminusPairsMatched"), mass1, mass2); + } + } + } else { + registry.fill(HIST("hMassDplusminusPair"), mass1, mass2); + if (std::abs(cand2.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi && std::abs(cand1.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi) { + registry.fill(HIST("hMassDplusminusPairsMatched"), mass1, mass2); + } + } } } } - PROCESS_SWITCH(HfTaskCorrelationDplusDplusReduced, processLocalDataMcRec, "Process local MC data", false); + + void processLocalDataTiny(o2::aod::HfCandDpFullEvs::iterator const&, + SelectedCandidatesTiny const& localCandidates) + { + processData(localCandidates); + } + PROCESS_SWITCH(HfTaskCorrelationDplusDplusReduced, processLocalDataTiny, "Process local data for Tiny", false); + + void processLocalDataLite(o2::aod::HfCandDpFullEvs::iterator const&, + SelectedCandidatesLite const& localCandidates) + { + processData(localCandidates); + } + PROCESS_SWITCH(HfTaskCorrelationDplusDplusReduced, processLocalDataLite, "Process local data for Lite", false); + + void processLocalDataFull(o2::aod::HfCandDpFullEvs::iterator const&, + SelectedCandidatesFull const& localCandidates) + { + processData(localCandidates); + } + PROCESS_SWITCH(HfTaskCorrelationDplusDplusReduced, processLocalDataFull, "Process local data for Full", true); + + void processLocalDataMcRecTiny(o2::aod::HfCandDpFullEvs::iterator const&, + SelectedCandidatesTiny const& localCandidates) + { + processLocalDataMcRec(localCandidates); + } + PROCESS_SWITCH(HfTaskCorrelationDplusDplusReduced, processLocalDataMcRecTiny, "Process local MC data for Tiny", false); + + void processLocalDataMcRecLite(o2::aod::HfCandDpFullEvs::iterator const&, + SelectedCandidatesLite const& localCandidates) + { + processLocalDataMcRec(localCandidates); + } + PROCESS_SWITCH(HfTaskCorrelationDplusDplusReduced, processLocalDataMcRecLite, "Process local MC data for Lite", false); + + void processLocalDataMcRecFull(o2::aod::HfCandDpFullEvs::iterator const&, + SelectedCandidatesFull const& localCandidates) + { + processLocalDataMcRec(localCandidates); + } + PROCESS_SWITCH(HfTaskCorrelationDplusDplusReduced, processLocalDataMcRecFull, "Process local MC data for Full", false); void processLocalDataMcGen(o2::aod::HfCandDpMcEvs::iterator const&, SelectedMcParticles const& localMcParticles) @@ -126,7 +219,9 @@ struct HfTaskCorrelationDplusDplusReduced { for (const auto& part1 : localMcParticles) { for (auto part2 = part1 + 1; part2 != localMcParticles.end(); ++part2) { - registry.fill(HIST("hDltPhiMcGen"), part2.phi() - part1.phi()); + if (std::abs(part1.eta()) < pseudorapidityMax && std::abs(part2.eta()) < pseudorapidityMax) { + registry.fill(HIST("hDltPhiMcGen"), part2.phi() - part1.phi()); + } } } } diff --git a/PWGHF/HFC/Tasks/taskCorrelationDplusHadrons.cxx b/PWGHF/HFC/Tasks/taskCorrelationDplusHadrons.cxx index dc49dd86dfe..fcce20ad9ad 100644 --- a/PWGHF/HFC/Tasks/taskCorrelationDplusHadrons.cxx +++ b/PWGHF/HFC/Tasks/taskCorrelationDplusHadrons.cxx @@ -46,7 +46,6 @@ #include #include -#include #include #include @@ -103,23 +102,25 @@ struct HfTaskCorrelationDplusHadrons { Configurable fillHistoData{"fillHistoData", true, "Flag for filling histograms in data processes"}; Configurable fillHistoMcRec{"fillHistoMcRec", true, "Flag for filling histograms in MC Rec processes"}; Configurable fillHistoMcGen{"fillHistoMcGen", true, "Flag for filling histograms in MC Gen processes"}; - Configurable fillHistoMcEff{"fillHistoMcEff", true, "Flag for filling histograms in efficiency processes"}; Configurable applyEfficiency{"applyEfficiency", 1, "Flag for applying efficiency weights"}; Configurable loadAccXEffFromCCDB{"loadAccXEffFromCCDB", false, "Flag for loading efficiency distributions from CCDB"}; Configurable selectionFlagDplus{"selectionFlagDplus", 7, "Selection Flag for D+"}; // 7 corresponds to topo+PID cuts Configurable selNoSameBunchPileUpColl{"selNoSameBunchPileUpColl", true, "Flag for rejecting the collisions associated with the same bunch crossing"}; + // Machine Learning options and selections Configurable> classMl{"classMl", {0, 1, 2}, "Indexes of ML scores to be stored. Three indexes max."}; - Configurable> mlScorePromptOrNonPromptMin{"mlScorePromptOrNonPromptMin", {0.5, 0.5, 0.5, 0.5}, "Minimum Machine learning scores for prompt or Feed-down"}; - Configurable> mlScorePromptOrNonPromptMax{"mlScorePromptOrNonPromptMax", {1.0, 1.0, 1.0, 1.0}, "Maximum Machine learning scores for prompt or Feed-down"}; - Configurable> mlScoreBkg{"mlScoreBkg", {0.5, 0.5, 0.5, 0.5}, "Machine learning scores for bkg"}; - // pT ranges for correlation plots: the default values are those embedded in hf_cuts_dplus_to_pi_k_pi (i.e. the mass pT bins), but can be redefined via json files - Configurable> binsPtCorrelations{"binsPtCorrelations", std::vector{ptBinsCorrelationsVec}, "pT bin limits for correlation plots"}; + // default ML cuts defined in the default pT bins of the ML cuts (binsPtEfficiencyD, since binsPtMl is empty by default) + Configurable> mlScorePromptOrNonPromptMin{"mlScorePromptOrNonPromptMin", std::vector(npTBinsEfficiency, 0.), "Minimum Machine learning scores for prompt or Feed-down, one per pT bin of the ML cuts"}; + Configurable> mlScorePromptOrNonPromptMax{"mlScorePromptOrNonPromptMax", std::vector(npTBinsEfficiency, 1.0), "Maximum Machine learning scores for prompt or Feed-down, one per pT bin of the ML cuts"}; + Configurable> mlScoreBkg{"mlScoreBkg", std::vector(npTBinsEfficiency, 0.5), "Machine learning scores for bkg, one per pT bin of the ML cuts"}; + Configurable> binsPtMl{"binsPtMl", std::vector{}, "pT bin limits for the ML cuts, defining also the accepted D+ pT range (if empty, binsPtEfficiencyD is used)"}; + // pT ranges of the signal and sideband regions (the pT axis of all the histograms is binsPtEfficiencyD), can be redefined via json files + Configurable> binsPtCorrelations{"binsPtCorrelations", std::vector{ptBinsCorrelationsVec}, "pT bin limits for the signal and sideband regions"}; Configurable> binsPtHadron{"binsPtHadron", std::vector{0.3, 2., 4., 8., 12., 50.}, "pT bin limits for assoc particle efficiency"}; - Configurable> binsPtEfficiencyD{"binsPtEfficiencyD", std::vector{o2::analysis::hf_cuts_dplus_to_pi_k_pi::vecBinsPt}, "pT bin limits for efficiency"}; + Configurable> binsPtEfficiencyD{"binsPtEfficiencyD", std::vector{o2::analysis::hf_cuts_dplus_to_pi_k_pi::vecBinsPt}, "pT bin limits for efficiency and for the D+ pT axis of all the histograms"}; Configurable> binsPtEfficiencyHad{"binsPtEfficiencyHad", std::vector{0.3, 2., 4., 8., 12., 50.}, "pT bin limits for associated particle efficiency"}; - Configurable> efficiencyD{"efficiencyD", {1., 1., 1., 1., 1., 1.}, "efficiency values for prompt D+ meson"}; - Configurable> efficiencyFdD{"efficiencyFdD", {1., 1., 1., 1., 1., 1.}, "efficiency values for beauty feed-down D+ meson"}; - Configurable> efficiencyHad{"efficiencyHad", {1., 1., 1., 1., 1., 1.}, "efficiency values for associated particles"}; + Configurable> efficiencyD{"efficiencyD", std::vector(npTBinsEfficiency, 1.f), "efficiency values for prompt D+ meson, one per pT bin of binsPtEfficiencyD"}; + Configurable> efficiencyFdD{"efficiencyFdD", std::vector(npTBinsEfficiency, 1.f), "efficiency values for beauty feed-down D+ meson, one per pT bin of binsPtEfficiencyD"}; + Configurable> efficiencyHad{"efficiencyHad", {1., 1., 1., 1., 1.}, "efficiency values for associated particles, one per pT bin of binsPtEfficiencyHad"}; // signal and sideband region edges, to be defined via json file (initialised to empty) Configurable> signalRegionInner{"signalRegionInner", std::vector{signalRegionInnerVec}, "Inner values of signal region vs pT"}; Configurable> signalRegionOuter{"signalRegionOuter", std::vector{signalRegionOuterVec}, "Outer values of signal region vs pT"}; @@ -137,10 +138,11 @@ struct HfTaskCorrelationDplusHadrons { Configurable yCandMax{"yCandMax", 0.8, "max. cand. rapidity"}; Configurable yCandGenMax{"yCandGenMax", 0.5, "max. gen. cand. rapidity"}; Configurable ptDaughterMin{"ptDaughterMin", 0.1, "min. daughter pT"}; - Configurable activateQA{"activateQA", false, "Flag to enable debug histogram"}; - Configurable nTpcCrossedRaws{"nTpcCrossedRaws", 70, "Number of crossed TPC Rows"}; + Configurable nTpcCrossedRows{"nTpcCrossedRows", 70, "Number of crossed TPC Rows"}; Configurable cutCollPosZMc{"cutCollPosZMc", 10., "max z-vertex position for collision acceptance"}; Configurable storeMassAxis{"storeMassAxis", false, "Store Mass axis in THnSparse"}; + Configurable storeBMotherInfo{"storeBMotherInfo", false, "Store generated pT(B mother) and phi(D+) - phi(B mother) of non-prompt D+ in MC Rec histograms"}; + Configurable> binsPtBMother{"binsPtBMother", std::vector{0., 2., 4., 6., 8., 12., 16., 24., 36., 50., 99.}, "pT bin limits for the B mother of non-prompt D+"}; // CCDB configuration Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable associatedEffCcdbPath{"associatedEffCcdbPath", "", "CCDB path for associated efficiency"}; @@ -153,8 +155,7 @@ struct HfTaskCorrelationDplusHadrons { std::shared_ptr mEfficiencyPrompt = nullptr; std::shared_ptr mEfficiencyFD = nullptr; std::shared_ptr mEfficiencyAssociated = nullptr; - std::shared_ptr effD = nullptr; - int idxBdtScore = 1; // Index BDTScore 1 for Prompt and 2 for FD Analysis + std::vector binsPtMlD; // pT bin limits for the ML cuts (binsPtMl, or binsPtEfficiencyD if binsPtMl is empty) enum CandidateStep { kCandidateStepMcGenAll = 0, kCandidateStepMcGenDplusToPiKPi, @@ -164,8 +165,9 @@ struct HfTaskCorrelationDplusHadrons { kCandidateStepMcRecoInAcceptance, kCandidateNSteps }; - using DplusHadronPair = soa::Join; + using DplusHadronPairWithBMother = soa::Join; using DplusHadronPairFullWithMl = soa::Join; + using DplusHadronPairFullWithMlAndBMother = soa::Join; using CandDplusMcReco = soa::Filtered>; using CandDplusMcGen = soa::Join; using TracksWithMc = soa::Filtered>; // trackFilter applied @@ -179,6 +181,7 @@ struct HfTaskCorrelationDplusHadrons { ConfigurableAxis binsPhi{"binsPhi", {64, -PIHalf, 3. * PIHalf}, "#it{#varphi}"}; ConfigurableAxis binsMultFT0M{"binsMultFT0M", {600, 0., 8000.}, "Multiplicity as FT0M signal amplitude"}; ConfigurableAxis binsPoolBin{"binsPoolBin", {9, 0., 9.}, "PoolBin"}; + ConfigurableAxis binsDeltaPhiBMother{"binsDeltaPhiBMother", {64, -PIHalf, 3. * PIHalf}, "#it{#varphi}^{D}-#it{#varphi}^{B}"}; HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -186,15 +189,16 @@ struct HfTaskCorrelationDplusHadrons { { // Axis definition AxisSpec axisMassD = {binsMassD, "inv. mass (#pi^{+}K^{-}#pi^{+}) (GeV/#it{c}^{2})"}; - AxisSpec axisPtCorr = {(std::vector)binsPtCorrelations, "#it{p}_{T}^{D} (GeV/#it{c})"}; - AxisSpec axisPtD = {(std::vector)binsPtEfficiencyD, "#it{p}_{T}^{D} (GeV/#it{c})"}; + AxisSpec axisPtD = {std::vector(binsPtEfficiencyD), "#it{p}_{T}^{D} (GeV/#it{c})"}; AxisSpec const axisMultFT0M = {binsMultFT0M, "MultiplicityFT0M"}; AxisSpec axisDeltaEta = {binsEta, "#it{#eta}^{Hadron}-#it{#eta}^{D}"}; AxisSpec axisDeltaPhi = {binsPhi, "#it{#varphi}^{Hadron}-#it{#varphi}^{D} (rad)"}; - AxisSpec axisPtHadron = {(std::vector)binsPtHadron, "#it{p}_{T}^{Hadron} (GeV/#it{c})"}; + AxisSpec axisPtHadron = {std::vector(binsPtHadron), "#it{p}_{T}^{Hadron} (GeV/#it{c})"}; AxisSpec axisPoolBin = {binsPoolBin, "poolBin"}; AxisSpec axisDplusPrompt = {2, -0.5, 1.5, "Prompt D+"}; AxisSpec const axisBdtScore = {binsBdtScore, "Bdt score"}; + AxisSpec axisPtBMother = {std::vector(binsPtBMother), "#it{p}_{T}^{B} (GeV/#it{c})"}; + AxisSpec axisDeltaPhiBMother = {binsDeltaPhiBMother, "#it{#varphi}^{D}-#it{#varphi}^{B} (rad)"}; // Histograms for data analysis registry.add("hBdtScorePrompt", "D+ BDT prompt score", {HistType::kTH1F, {axisBdtScore}}); @@ -203,17 +207,17 @@ struct HfTaskCorrelationDplusHadrons { registry.add("hMassDplusVsPtWoEff", "D+ candidates massVsPt without efficiency", {HistType::kTH2F, {{axisMassD}, {axisPtD}}}); if (fillHistoData) { if (storeMassAxis) { - registry.add("hCorrel2DVsPtVsMass", stringDHadron + stringSignal + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtCorr}, {axisPtHadron}, {axisPoolBin}, {axisMassD}}}); + registry.add("hCorrel2DVsPtVsMass", stringDHadron + stringSignal + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}, {axisMassD}}}); registry.get(HIST("hCorrel2DVsPtVsMass"))->Sumw2(); } else { registry.add("hDeltaEtaPtIntSignalRegion", stringDHadron + stringSignal + stringDeltaEta + "entries", {HistType::kTH1F, {axisDeltaEta}}); registry.add("hDeltaPhiPtIntSignalRegion", stringDHadron + stringSignal + stringDeltaPhi + "entries", {HistType::kTH1F, {axisDeltaPhi}}); registry.add("hCorrel2DPtIntSignalRegion", stringDHadron + stringSignal + stringDeltaPhi + stringDeltaEta + "entries", {HistType::kTH2F, {{axisDeltaPhi}, {axisDeltaEta}}}); - registry.add("hCorrel2DVsPtSignalRegion", stringDHadron + stringSignal + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtCorr}, {axisPtHadron}, {axisPoolBin}}}); + registry.add("hCorrel2DVsPtSignalRegion", stringDHadron + stringSignal + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); registry.add("hDeltaEtaPtIntSidebands", stringDHadron + stringSideband + stringDeltaEta + "entries", {HistType::kTH1F, {axisDeltaEta}}); registry.add("hDeltaPhiPtIntSidebands", stringDHadron + stringSideband + stringDeltaPhi + "entries", {HistType::kTH1F, {axisDeltaPhi}}); registry.add("hCorrel2DPtIntSidebands", stringDHadron + stringSideband + stringDeltaPhi + stringDeltaEta + "entries", {HistType::kTH2F, {{axisDeltaPhi}, {axisDeltaEta}}}); - registry.add("hCorrel2DVsPtSidebands", stringDHadron + stringSideband + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtCorr}, {axisPtHadron}, {axisPoolBin}}}); + registry.add("hCorrel2DVsPtSidebands", stringDHadron + stringSideband + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); registry.add("hDeltaEtaPtIntSidebandLeft", stringDHadron + "Left" + stringSideband + stringDeltaEta, {HistType::kTH1F, {axisDeltaEta}}); registry.add("hDeltaPhiPtIntSidebandLeft", stringDHadron + "Left" + stringSideband + stringDeltaPhi, {HistType::kTH1F, {axisDeltaPhi}}); registry.add("hDeltaEtaPtIntSidebandRight", stringDHadron + "Right" + stringSideband + stringDeltaEta, {HistType::kTH1F, {axisDeltaEta}}); @@ -235,12 +239,12 @@ struct HfTaskCorrelationDplusHadrons { registry.add("hDeltaPhiPtIntSignalRegionMcRec", stringDHadron + stringSignal + stringDeltaPhi + "entries", {HistType::kTH1F, {axisDeltaPhi}}); registry.add("hDeltaEtaPtIntSidebandsMcRec", stringDHadron + stringSideband + stringDeltaEta + "entries", {HistType::kTH1F, {axisDeltaEta}}); registry.add("hCorrel2DPtIntSignalRegionMcRec", stringDHadron + stringSignal + stringDeltaPhi + stringDeltaEta + "entries", {HistType::kTH2F, {{axisDeltaPhi}, {axisDeltaEta}}}); - registry.add("hCorrel2DVsPtSignalRegionMcRec", stringDHadron + stringSignal + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtCorr}, {axisPtHadron}, {axisDplusPrompt}, {axisPoolBin}}}); - registry.add("hCorrel2DVsPtSignalMcRec", stringDHadron + stringSignal + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtCorr}, {axisPtHadron}, {axisPoolBin}}}); - registry.add("hCorrel2DVsPtBkgMcRec", stringDHadron + stringSignal + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtCorr}, {axisPtHadron}, {axisPoolBin}}}); + registry.add("hCorrel2DVsPtSignalRegionMcRec", stringDHadron + stringSignal + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisDplusPrompt}, {axisPoolBin}}}); + registry.add("hCorrel2DVsPtSignalMcRec", stringDHadron + stringSignal + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); + registry.add("hCorrel2DVsPtBkgMcRec", stringDHadron + stringSignal + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); registry.add("hDeltaPhiPtIntSidebandsMcRec", stringDHadron + stringSideband + stringDeltaPhi + "entries", {HistType::kTH1F, {axisDeltaPhi}}); registry.add("hCorrel2DPtIntSidebandsMcRec", stringDHadron + stringSideband + stringDeltaPhi + stringDeltaEta + "entries", {HistType::kTH2F, {{axisDeltaPhi}, {axisDeltaEta}}}); - registry.add("hCorrel2DVsPtSidebandsMcRec", stringDHadron + stringSideband + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtCorr}, {axisPtHadron}, {axisPoolBin}}}); + registry.add("hCorrel2DVsPtSidebandsMcRec", stringDHadron + stringSideband + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); registry.add("hCorrel2DVsPtPhysicalPrimaryMcRec", stringDHadron + "(only true primary particles)" + stringSignal, {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisDplusPrompt}, {axisPoolBin}}}); registry.add("hDeltaEtaPtIntSidebandLeftMcRec", stringDHadron + "Left" + stringSideband + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTH1F, {axisDeltaEta}}); registry.add("hDeltaPhiPtIntSidebandLeftMcRec", stringDHadron + "Left" + stringSideband + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTH1F, {axisDeltaPhi}}); @@ -249,7 +253,7 @@ struct HfTaskCorrelationDplusHadrons { registry.add("hDeltaPhiPtIntSidebandRightMcRec", stringDHadron + "Right" + stringSideband + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTH1F, {axisDeltaPhi}}); registry.add("hCorrel2DVsPtSidebandRightMcRec", stringDHadron + "Right" + stringSideband + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); registry.add("hCorrel2DVsPtSignalRegionPromptDplusPromptHadronMcRec", stringDHadron + "signal region PromptD - Prompt Track MC reco", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); - registry.add("hCorrel2DVsPtSignalRegionNonPromptDplusNonPromptHadronMcRec", stringDHadron + " signal region PromptD - NonPrompt Track MC reco", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); + registry.add("hCorrel2DVsPtSignalRegionNonPromptDplusNonPromptHadronMcRec", stringDHadron + " signal region NonPrompt - NonPrompt Track MC reco", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); registry.get(HIST("hCorrel2DVsPtSignalRegionMcRec"))->Sumw2(); registry.get(HIST("hCorrel2DVsPtSidebandsMcRec"))->Sumw2(); @@ -257,15 +261,24 @@ struct HfTaskCorrelationDplusHadrons { registry.get(HIST("hCorrel2DVsPtBkgMcRec"))->Sumw2(); registry.get(HIST("hCorrel2DVsPtSidebandLeftMcRec"))->Sumw2(); registry.get(HIST("hCorrel2DVsPtSidebandRightMcRec"))->Sumw2(); - registry.get(HIST("hCorrel2DVsPtSignalRegionMcRec"))->Sumw2(); registry.get(HIST("hCorrel2DVsPtPhysicalPrimaryMcRec"))->Sumw2(); + + // beauty-mother information of non-prompt D+ (generated B, reconstructed D+) + if (storeBMotherInfo) { + registry.add("hMassNonPromptDplusVsPtVsPtBMother", "D+ non prompt candidates mass Vs Pt Vs Pt B mother", {HistType::kTH3F, {{axisMassD}, {axisPtD}, {axisPtBMother}}}); + registry.add("hDeltaPhiBMotherVsPtVsPtBMother", "D+ non prompt candidates #varphi^{D}-#varphi^{B} Vs Pt Vs Pt B mother", {HistType::kTH3F, {{axisDeltaPhiBMother}, {axisPtD}, {axisPtBMother}}}); + registry.add("hCorrel2DVsPtNonPromptDplusPhysicalPrimaryMcRecBMother", stringDHadron + "non-prompt D (only true primary particles)" + stringSignal, {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPtBMother}, {axisDeltaPhiBMother}}}); + registry.add("hCorrel2DVsPtSignalRegionNonPromptDplusNonPromptHadronMcRecBMother", stringDHadron + "NonPrompt D - Non Prompt Track MC reco", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPtBMother}, {axisDeltaPhiBMother}}}); + + registry.get(HIST("hCorrel2DVsPtNonPromptDplusPhysicalPrimaryMcRecBMother"))->Sumw2(); + registry.get(HIST("hCorrel2DVsPtSignalRegionNonPromptDplusNonPromptHadronMcRecBMother"))->Sumw2(); + } } // Histograms for MC Gen analysis if (fillHistoMcGen) { registry.add("hDeltaEtaPtIntMcGen", stringMCParticles + stringDeltaEta + "entries", {HistType::kTH1F, {axisDeltaEta}}); registry.add("hDeltaPhiPtIntMcGen", stringMCParticles + stringDeltaPhi + "entries", {HistType::kTH1F, {axisDeltaPhi}}); - registry.add("hCorrel2DPtIntMcGen", stringMCParticles + stringDeltaPhi + stringDeltaEta + "entries", {HistType::kTH2F, {{axisDeltaPhi}, {axisDeltaEta}}}); - registry.add("hCorrel2DVsPtMcGen", stringMCParticles + stringDeltaPhi + stringDeltaEta + stringPtD + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtCorr}, {axisPtHadron}, {axisPoolBin}}}); + registry.add("hCorrel2DVsPtMcGen", stringMCParticles + stringDeltaPhi + stringDeltaEta + stringPtD + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); registry.add("hCorrel2DVsPtMcGenPrompt", stringDHadron + " Prompt MC Gen", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); registry.add("hCorrel2DVsPtMcGenPromptDPromptHadron", stringDHadron + "prompt D prompt h MC Gen", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); registry.add("hCorrel2DVsPtMcGenNonPromptDNonPromptHadron", stringDHadron + " non prompt D non prompt h MC Gen", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); @@ -274,6 +287,15 @@ struct HfTaskCorrelationDplusHadrons { registry.get(HIST("hCorrel2DVsPtMcGen"))->Sumw2(); registry.get(HIST("hCorrel2DVsPtMcGenPrompt"))->Sumw2(); registry.get(HIST("hCorrel2DVsPtMcGenNonPrompt"))->Sumw2(); + + // beauty-mother information of non-prompt D+, same axes as the MC Rec counterparts to allow Rec/Gen ratios + if (storeBMotherInfo) { + registry.add("hCorrel2DVsPtMcGenNonPromptBMother", stringDHadron + " NonPrompt MC Gen", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPtBMother}, {axisDeltaPhiBMother}}}); + registry.add("hCorrel2DVsPtMcGenNonPromptDNonPromptHadronBMother", stringDHadron + " non prompt D non prompt h MC Gen", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPtBMother}, {axisDeltaPhiBMother}}}); + + registry.get(HIST("hCorrel2DVsPtMcGenNonPromptBMother"))->Sumw2(); + registry.get(HIST("hCorrel2DVsPtMcGenNonPromptDNonPromptHadronBMother"))->Sumw2(); + } } // Histograms for efficiencies registry.add("Efficiency/hPtCandMcRecPrompt", stringMCRecoDPrompt + stringPtD, {HistType::kTH1F, {axisPtD}}); @@ -312,26 +334,105 @@ struct HfTaskCorrelationDplusHadrons { } LOGF(info, "Loaded associated efficiency histogram from %s", associatedEffCcdbPath.value.c_str()); } - auto effD = isPromptAnalysis ? mEfficiencyPrompt : mEfficiencyFD; - idxBdtScore = isPromptAnalysis ? 1 : 2; - - if (activateQA) { - const int regionLimits = 6; - std::string labels[regionLimits] = {"SigReg Left", "SigReg Right", "Left SB Low", "Left SB Up", "Right SB Low", "Right SB Up"}; - static const AxisSpec axisSidebandLimits = {regionLimits, 0.5, 6.5, ""}; - auto hSigSidebandLimits = registry.add("Inputs/hSigSidebandLimits", "Signal and Sideband Limits;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSidebandLimits, {(std::vector)binsPtCorrelations, "#it{p}_{T} (GeV/#it{c})"}}}); - for (int iLim = 0; iLim < regionLimits; iLim++) { - hSigSidebandLimits->GetXaxis()->SetBinLabel(iLim + 1, labels[iLim].data()); - } - for (size_t iPtD = 0; iPtD < binsPtCorrelations->size() - 1; iPtD++) { - hSigSidebandLimits->SetBinContent(1, iPtD + 1, signalRegionInner->at(iPtD)); - hSigSidebandLimits->SetBinContent(2, iPtD + 1, signalRegionOuter->at(iPtD)); - hSigSidebandLimits->SetBinContent(3, iPtD + 1, sidebandLeftOuter->at(iPtD)); - hSigSidebandLimits->SetBinContent(4, iPtD + 1, sidebandLeftInner->at(iPtD)); - hSigSidebandLimits->SetBinContent(5, iPtD + 1, sidebandRightInner->at(iPtD)); - hSigSidebandLimits->SetBinContent(6, iPtD + 1, sidebandRightOuter->at(iPtD)); + + constexpr std::size_t NBinLimitsMin{2u}; // at least one pT bin + + // efficiency values from configurables: one entry per pT bin + if ((applyEfficiency != 0) && !loadAccXEffFromCCDB) { + if (binsPtEfficiencyD->size() < NBinLimitsMin || binsPtEfficiencyHad->size() < NBinLimitsMin) { + LOGF(fatal, "binsPtEfficiencyD and binsPtEfficiencyHad must contain at least two pT bin limits"); + } + std::size_t const nBinsPtEffD = binsPtEfficiencyD->size() - 1; + if (efficiencyD->size() != nBinsPtEffD || efficiencyFdD->size() != nBinsPtEffD) { + LOGF(fatal, "efficiencyD (%zu entries) and efficiencyFdD (%zu entries) must have one entry per pT bin of binsPtEfficiencyD (%zu bins)", efficiencyD->size(), efficiencyFdD->size(), nBinsPtEffD); + } + std::size_t const nBinsPtEffHad = binsPtEfficiencyHad->size() - 1; + if (efficiencyHad->size() != nBinsPtEffHad) { + LOGF(fatal, "efficiencyHad (%zu entries) must have one entry per pT bin of binsPtEfficiencyHad (%zu bins)", efficiencyHad->size(), nBinsPtEffHad); + } + } + + // pT bins of the ML cuts, decoupled from the efficiency binning + binsPtMlD = binsPtMl->empty() ? binsPtEfficiencyD.value : binsPtMl.value; + if (binsPtMlD.size() < NBinLimitsMin) { + LOGF(fatal, "binsPtMl (or binsPtEfficiencyD if binsPtMl is empty) must contain at least two pT bin limits"); + } + std::size_t const nBinsPtMl = binsPtMlD.size() - 1; + if (mlScorePromptOrNonPromptMin->size() != nBinsPtMl || mlScorePromptOrNonPromptMax->size() != nBinsPtMl || mlScoreBkg->size() != nBinsPtMl) { + LOGF(fatal, "mlScorePromptOrNonPromptMin, mlScorePromptOrNonPromptMax and mlScoreBkg must have one entry per pT bin of the ML cuts (%zu bins)", nBinsPtMl); + } + } + + /// ML selection of the D+ candidate, in the pT bins of the ML cuts + /// \param ptD is the D+ transverse momentum + /// \param bdtScoreBkg is the BDT score for the background hypothesis (outputMl[0]) + /// \param bdtScorePrompt is the BDT score for the prompt hypothesis (outputMl[1]) + /// \param bdtScoreNonPrompt is the BDT score for the non-prompt hypothesis (outputMl[2]) + /// \return true if the candidate is inside the pT range of the ML cuts and passes them + bool isSelectedMl(float ptD, float bdtScoreBkg, float bdtScorePrompt, float bdtScoreNonPrompt) + { + int const mlBinD = o2::analysis::findBin(&binsPtMlD, ptD); + if (mlBinD < 0) { + return false; + } + float const bdtScorePromptOrNonPrompt = isPromptAnalysis ? bdtScorePrompt : bdtScoreNonPrompt; + return bdtScorePromptOrNonPrompt >= mlScorePromptOrNonPromptMin->at(mlBinD) && bdtScorePromptOrNonPrompt <= mlScorePromptOrNonPromptMax->at(mlBinD) && bdtScoreBkg <= mlScoreBkg->at(mlBinD); + } + + /// D+ efficiency, from the CCDB histograms or from the configurable vectors + /// \param ptD is the D+ transverse momentum + /// \param isPrompt selects the prompt (true) or non-prompt (false) efficiency + /// \return the efficiency, or a non-positive value if it is not available for this pT + double getEfficiencyDplus(float ptD, bool isPrompt) + { + if (loadAccXEffFromCCDB) { + auto const& hEfficiency = isPrompt ? mEfficiencyPrompt : mEfficiencyFD; + return hEfficiency->GetBinContent(hEfficiency->FindBin(ptD)); + } + int const effBinD = o2::analysis::findBin(binsPtEfficiencyD, ptD); + if (effBinD < 0) { + return -1.; + } + return isPrompt ? efficiencyD->at(effBinD) : efficiencyFdD->at(effBinD); + } + + /// Associated-hadron efficiency, from the CCDB histogram or from the configurable vector + /// \param ptHadron is the associated-hadron transverse momentum + /// \return the efficiency, or a non-positive value if it is not available for this pT + double getEfficiencyHadron(float ptHadron) + { + if (loadAccXEffFromCCDB) { + return mEfficiencyAssociated->GetBinContent(mEfficiencyAssociated->FindBin(ptHadron)); + } + int const effBinHadron = o2::analysis::findBin(binsPtEfficiencyHad, ptHadron); + if (effBinHadron < 0) { + return -1.; + } + return efficiencyHad->at(effBinHadron); + } + + /// Efficiency weight of the D+ candidate, or of the D+-hadron pair if ptHadron is given + /// \param ptD is the D+ transverse momentum + /// \param isPrompt selects the prompt (true) or non-prompt (false) D+ efficiency + /// \param ptHadron is the associated-hadron transverse momentum (negative for the D+ only) + /// \return 1/efficiency, 1 if the efficiencies are not applied, or -1 if the efficiency is not available + double getEfficiencyWeight(float ptD, bool isPrompt, float ptHadron = -1.f) + { + if (applyEfficiency == 0) { + return 1.; + } + double efficiency = getEfficiencyDplus(ptD, isPrompt); + if (efficiency <= 0.) { + return -1.; + } + if (ptHadron >= 0.f) { + double const efficiencyHadron = getEfficiencyHadron(ptHadron); + if (efficiencyHadron <= 0.) { + return -1.; } + efficiency *= efficiencyHadron; } + return 1. / efficiency; } void processData(DplusHadronPairFullWithMl const& pairEntries, aod::DplusRecoInfo const& candidates) @@ -342,23 +443,14 @@ struct HfTaskCorrelationDplusHadrons { float const bdtScorePrompt = candidate.mlScorePrompt(); float const bdtScoreNonPrompt = candidate.mlScoreNonPrompt(); float const bdtScoreBkg = candidate.mlScoreBkg(); - int const effBinD = o2::analysis::findBin(binsPtEfficiencyD, ptD); - float const bdtScorePromptOrNonPrompt = isPromptAnalysis ? bdtScorePrompt : bdtScoreNonPrompt; - // reject entries outside pT ranges of interest - if (ptD < binsPtEfficiencyD->front() || ptD > binsPtEfficiencyD->back()) { + // reject candidates outside the pT range of the ML cuts or not passing them + if (!isSelectedMl(ptD, bdtScoreBkg, bdtScorePrompt, bdtScoreNonPrompt)) { continue; } - - if (bdtScorePromptOrNonPrompt < mlScorePromptOrNonPromptMin->at(effBinD) || bdtScorePromptOrNonPrompt > mlScorePromptOrNonPromptMax->at(effBinD) || bdtScoreBkg > mlScoreBkg->at(effBinD)) { - continue; - } - double efficiencyWeightD = 1.; - if (applyEfficiency != 0) { - efficiencyWeightD = 1. / efficiencyD->at(o2::analysis::findBin(binsPtEfficiencyD, ptD)); - if (loadAccXEffFromCCDB) { - efficiencyWeightD = 1. / effD->GetBinContent(effD->FindBin(ptD)); - } + double const efficiencyWeightD = getEfficiencyWeight(ptD, isPromptAnalysis); + if (efficiencyWeightD < 0.) { + continue; // efficiency not available for this pT } registry.fill(HIST("hMassDplusVsPt"), massD, ptD, efficiencyWeightD); registry.fill(HIST("hMassDplusVsPtWoEff"), massD, ptD); @@ -380,32 +472,26 @@ struct HfTaskCorrelationDplusHadrons { int const trackTpcCrossedRows = pairEntry.trackTPCNClsCrossedRows(); int const poolBin = pairEntry.poolBin(); double const massD = pairEntry.mD(); - int const effBinD = o2::analysis::findBin(binsPtEfficiencyD, ptD); - int const pTBinD = o2::analysis::findBin(binsPtCorrelations, ptD); - float const bdtScorePromptOrNonPrompt = isPromptAnalysis ? bdtScorePrompt : bdtScoreNonPrompt; - // reject entries outside pT ranges of interest - if (ptD < binsPtEfficiencyD->front() || ptD > binsPtEfficiencyD->back()) { + // reject entries outside the pT range of the ML cuts or not passing them + if (!isSelectedMl(ptD, bdtScoreBkg, bdtScorePrompt, bdtScoreNonPrompt)) { continue; } - - if (bdtScorePromptOrNonPrompt < mlScorePromptOrNonPromptMin->at(effBinD) || bdtScorePromptOrNonPrompt > mlScorePromptOrNonPromptMax->at(effBinD) || bdtScoreBkg > mlScoreBkg->at(effBinD)) { - continue; - } - if (trackDcaXY > dcaXYTrackMax || trackDcaZ > dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRaws) { + if (std::abs(trackDcaXY) >= dcaXYTrackMax || std::abs(trackDcaZ) >= dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRows) { continue; } - double efficiencyWeight = 1.; - if (applyEfficiency != 0) { - efficiencyWeight = 1. / (efficiencyD->at(effBinD) * efficiencyHad->at(o2::analysis::findBin(binsPtEfficiencyHad, ptHadron))); - if (loadAccXEffFromCCDB) { - efficiencyWeight = 1. / (effD->GetBinContent(effD->FindBin(ptD)) * mEfficiencyAssociated->GetBinContent(mEfficiencyAssociated->FindBin(ptHadron))); - } + double const efficiencyWeight = getEfficiencyWeight(ptD, isPromptAnalysis, ptHadron); + if (efficiencyWeight < 0.) { + continue; // efficiency not available for this pT } if (storeMassAxis) { registry.fill(HIST("hCorrel2DVsPtVsMass"), deltaPhi, deltaEta, ptD, ptHadron, poolBin, massD, efficiencyWeight); continue; } + int const pTBinD = o2::analysis::findBin(binsPtCorrelations, ptD); + if (pTBinD < 0) { + continue; // outside the pT range of the signal and sideband regions + } // check if correlation entry belongs to signal region, sidebands or is outside both, and fill correlation plots if (massD > signalRegionInner->at(pTBinD) && massD < signalRegionOuter->at(pTBinD)) { // in signal region @@ -439,8 +525,8 @@ struct HfTaskCorrelationDplusHadrons { PROCESS_SWITCH(HfTaskCorrelationDplusHadrons, processData, "Process data", false); /// D-Hadron correlation pair filling task, from pair tables - for MC reco-level analysis (candidates matched to true signal only, but also bkg sources are studied) - void processMcRec(DplusHadronPairFullWithMl const& pairEntries, - soa::Join const& candidates) + void processMcRec(DplusHadronPairFullWithMlAndBMother const& pairEntries, + soa::Join const& candidates) { for (const auto& candidate : candidates) { float const massD = candidate.mD(); @@ -448,40 +534,27 @@ struct HfTaskCorrelationDplusHadrons { float const bdtScorePrompt = candidate.mlScorePrompt(); float const bdtScoreNonPrompt = candidate.mlScoreNonPrompt(); float const bdtScoreBkg = candidate.mlScoreBkg(); - int const effBinD = o2::analysis::findBin(binsPtEfficiencyD, ptD); bool const isDplusPrompt = candidate.isPrompt(); - float const bdtScorePromptOrNonPrompt = isPromptAnalysis ? bdtScorePrompt : bdtScoreNonPrompt; - // reject entries outside pT ranges of interest - if (ptD < binsPtEfficiencyD->front() || ptD > binsPtEfficiencyD->back()) { + // reject candidates outside the pT range of the ML cuts or not passing them + if (!isSelectedMl(ptD, bdtScoreBkg, bdtScorePrompt, bdtScoreNonPrompt)) { continue; } - - if (bdtScorePromptOrNonPrompt < mlScorePromptOrNonPromptMin->at(effBinD) || bdtScorePromptOrNonPrompt > mlScorePromptOrNonPromptMax->at(effBinD) || bdtScoreBkg > mlScoreBkg->at(effBinD)) { - continue; + double const efficiencyWeightD = getEfficiencyWeight(ptD, isDplusPrompt); + if (efficiencyWeightD < 0.) { + continue; // efficiency not available for this pT } - double efficiencyWeightD = 1.; - if (applyEfficiency != 0) { - if (isDplusPrompt) { - efficiencyWeightD = 1. / efficiencyD->at(effBinD); - if (loadAccXEffFromCCDB) { - efficiencyWeightD = 1. / mEfficiencyPrompt->GetBinContent(mEfficiencyPrompt->FindBin(ptD)); - } - registry.fill(HIST("hMassDplusVsPt"), massD, ptD, efficiencyWeightD); - registry.fill(HIST("hMassDplusVsPtWoEff"), massD, ptD); - registry.fill(HIST("hMassPromptDplusVsPt"), massD, ptD, efficiencyWeightD); - registry.fill(HIST("hBdtScorePrompt"), bdtScorePrompt); - registry.fill(HIST("hBdtScoreBkg"), bdtScoreBkg); - } else { - efficiencyWeightD = 1. / efficiencyFdD->at(effBinD); - if (loadAccXEffFromCCDB) { - efficiencyWeightD = 1. / mEfficiencyFD->GetBinContent(mEfficiencyFD->FindBin(ptD)); - } - registry.fill(HIST("hMassDplusVsPt"), massD, ptD, efficiencyWeightD); - registry.fill(HIST("hMassDplusVsPtWoEff"), massD, ptD); - registry.fill(HIST("hMassNonPromptDplusVsPt"), massD, ptD, efficiencyWeightD); - registry.fill(HIST("hBdtScorePrompt"), bdtScorePrompt); - registry.fill(HIST("hBdtScoreBkg"), bdtScoreBkg); + registry.fill(HIST("hMassDplusVsPt"), massD, ptD, efficiencyWeightD); + registry.fill(HIST("hMassDplusVsPtWoEff"), massD, ptD); + registry.fill(HIST("hBdtScorePrompt"), bdtScorePrompt); + registry.fill(HIST("hBdtScoreBkg"), bdtScoreBkg); + if (isDplusPrompt) { + registry.fill(HIST("hMassPromptDplusVsPt"), massD, ptD, efficiencyWeightD); + } else { + registry.fill(HIST("hMassNonPromptDplusVsPt"), massD, ptD, efficiencyWeightD); + if (storeBMotherInfo) { + registry.fill(HIST("hMassNonPromptDplusVsPtVsPtBMother"), massD, ptD, candidate.ptB(), efficiencyWeightD); + registry.fill(HIST("hDeltaPhiBMotherVsPtVsPtBMother"), candidate.deltaPhiBtoD(), ptD, candidate.ptB(), efficiencyWeightD); } } } @@ -503,35 +576,17 @@ struct HfTaskCorrelationDplusHadrons { bool const isDplusPrompt = pairEntry.isPrompt(); int const originHadron = pairEntry.trackOrigin(); int const poolBin = pairEntry.poolBin(); - int const effBinD = o2::analysis::findBin(binsPtEfficiencyD, ptD); - int const pTBinD = o2::analysis::findBin(binsPtCorrelations, ptD); - float const bdtScorePromptOrNonPrompt = isPromptAnalysis ? bdtScorePrompt : bdtScoreNonPrompt; - // reject entries outside pT ranges of interest - if (ptD < binsPtEfficiencyD->front() || ptD > binsPtEfficiencyD->back()) { + // reject entries outside the pT range of the ML cuts or not passing them + if (!isSelectedMl(ptD, bdtScoreBkg, bdtScorePrompt, bdtScoreNonPrompt)) { continue; } - - if (bdtScorePromptOrNonPrompt < mlScorePromptOrNonPromptMin->at(effBinD) || bdtScorePromptOrNonPrompt > mlScorePromptOrNonPromptMax->at(effBinD) || bdtScoreBkg > mlScoreBkg->at(effBinD)) { + if (std::abs(trackDcaXY) > dcaXYTrackMax || std::abs(trackDcaZ) > dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRows) { continue; } - if (trackDcaXY > dcaXYTrackMax || trackDcaZ > dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRaws) { - continue; - } - double efficiencyWeight = 1.; - - if (applyEfficiency != 0) { - if (isDplusPrompt) { - efficiencyWeight = 1. / (efficiencyD->at(effBinD) * efficiencyHad->at(o2::analysis::findBin(binsPtEfficiencyHad, ptHadron))); - if (loadAccXEffFromCCDB) { - efficiencyWeight = 1. / (mEfficiencyPrompt->GetBinContent(mEfficiencyPrompt->FindBin(ptD)) * mEfficiencyAssociated->GetBinContent(mEfficiencyAssociated->FindBin(ptHadron))); - } - } else { - efficiencyWeight = 1. / (efficiencyFdD->at(effBinD) * efficiencyHad->at(o2::analysis::findBin(binsPtEfficiencyHad, ptHadron))); - if (loadAccXEffFromCCDB) { - efficiencyWeight = 1. / (mEfficiencyFD->GetBinContent(mEfficiencyFD->FindBin(ptD)) * mEfficiencyAssociated->GetBinContent(mEfficiencyAssociated->FindBin(ptHadron))); - } - } + double const efficiencyWeight = getEfficiencyWeight(ptD, isDplusPrompt, ptHadron); + if (efficiencyWeight < 0.) { + continue; // efficiency not available for this pT } // fill correlation plots for signal/bagkground correlations @@ -540,8 +595,11 @@ struct HfTaskCorrelationDplusHadrons { } else { registry.fill(HIST("hCorrel2DVsPtBkgMcRec"), deltaPhi, deltaEta, ptD, ptHadron, poolBin, efficiencyWeight); } - // reject entries outside pT ranges of interest + int const pTBinD = o2::analysis::findBin(binsPtCorrelations, ptD); + if (pTBinD < 0) { + continue; // outside the pT range of the signal and sideband regions + } // check if correlation entry belongs to signal region, sidebands or is outside both, and fill correlation plots if (massD > signalRegionInner->at(pTBinD) && massD < signalRegionOuter->at(pTBinD)) { // in signal region @@ -556,6 +614,15 @@ struct HfTaskCorrelationDplusHadrons { } else if (!isDplusPrompt && originHadron == RecoDecay::OriginType::NonPrompt) { registry.fill(HIST("hCorrel2DVsPtSignalRegionNonPromptDplusNonPromptHadronMcRec"), deltaPhi, deltaEta, ptD, ptHadron, poolBin, efficiencyWeight); } + // beauty-mother information, only for true non-prompt D+, to study the structures in the MC Closure test + if (storeBMotherInfo && pairEntry.signalStatus() && !isDplusPrompt) { + float const ptB = pairEntry.ptB(); + float const deltaPhiBtoD = pairEntry.deltaPhiBtoD(); + registry.fill(HIST("hCorrel2DVsPtNonPromptDplusPhysicalPrimaryMcRecBMother"), deltaPhi, deltaEta, ptD, ptHadron, ptB, deltaPhiBtoD, efficiencyWeight); + if (originHadron == RecoDecay::OriginType::NonPrompt) { + registry.fill(HIST("hCorrel2DVsPtSignalRegionNonPromptDplusNonPromptHadronMcRecBMother"), deltaPhi, deltaEta, ptD, ptHadron, ptB, deltaPhiBtoD, efficiencyWeight); + } + } } } // in sideband left region @@ -583,7 +650,7 @@ struct HfTaskCorrelationDplusHadrons { PROCESS_SWITCH(HfTaskCorrelationDplusHadrons, processMcRec, "Process MC Reco mode", true); /// D-Hadron correlation pair filling task, from pair tables - for MC gen-level analysis (no filter/selection, only true signal) - void processMcGen(DplusHadronPair const& pairEntries) + void processMcGen(DplusHadronPairWithBMother const& pairEntries) { for (const auto& pairEntry : pairEntries) { // define variables for widely used quantities @@ -608,6 +675,15 @@ struct HfTaskCorrelationDplusHadrons { if (originHadron == RecoDecay::OriginType::NonPrompt) { registry.fill(HIST("hCorrel2DVsPtMcGenNonPromptDNonPromptHadron"), deltaPhi, deltaEta, ptD, ptHadron, poolBin); } + // beauty-mother information + if (storeBMotherInfo) { + float const ptB = pairEntry.ptB(); + float const deltaPhiBtoD = pairEntry.deltaPhiBtoD(); + registry.fill(HIST("hCorrel2DVsPtMcGenNonPromptBMother"), deltaPhi, deltaEta, ptD, ptHadron, ptB, deltaPhiBtoD); + if (originHadron == RecoDecay::OriginType::NonPrompt) { + registry.fill(HIST("hCorrel2DVsPtMcGenNonPromptDNonPromptHadronBMother"), deltaPhi, deltaEta, ptD, ptHadron, ptB, deltaPhiBtoD); + } + } } } } @@ -669,7 +745,8 @@ struct HfTaskCorrelationDplusHadrons { for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { outputMl[iclass] = candidate.mlProbDplusToPiKPi()[classMl->at(iclass)]; } - if (outputMl[0] > mlScoreBkg->at(o2::analysis::findBin(binsPtEfficiencyD, candidate.pt())) || outputMl[idxBdtScore] < mlScorePromptOrNonPromptMin->at(o2::analysis::findBin(binsPtEfficiencyD, candidate.pt())) || outputMl[idxBdtScore] > mlScorePromptOrNonPromptMax->at(o2::analysis::findBin(binsPtEfficiencyD, candidate.pt()))) { + // same ML selection as in the correlation analysis (outputMl: 0 = bkg, 1 = prompt, 2 = non-prompt) + if (!isSelectedMl(candidate.pt(), outputMl[0], outputMl[1], outputMl[2])) { continue; } auto collision = candidate.template collision_as>(); diff --git a/PWGHF/HFC/Tasks/taskCorrelationDsHadrons.cxx b/PWGHF/HFC/Tasks/taskCorrelationDsHadrons.cxx index 61c87645ba4..2d93fe6408d 100644 --- a/PWGHF/HFC/Tasks/taskCorrelationDsHadrons.cxx +++ b/PWGHF/HFC/Tasks/taskCorrelationDsHadrons.cxx @@ -105,7 +105,7 @@ struct HfTaskCorrelationDsHadrons { // Configurable doMcCollisionCheck{"doMcCollisionCheck", false, "Flag for applying the collision check and selection based on MC collision info"}; Configurable centEstimator{"centEstimator", 2, "Centrality estimation (FT0A: 1, FT0C: 2, FT0M: 3, FV0A: 4)"}; Configurable selectionFlagDs{"selectionFlagDs", 7, "Selection Flag for Ds (avoid the case of flag = 0, no outputMlScore)"}; - Configurable nTpcCrossedRaws{"nTpcCrossedRaws", 70, "Number of crossed TPC Rows"}; + Configurable nTpcCrossedRows{"nTpcCrossedRows", 70, "Number of crossed TPC Rows"}; // Configurable eventGeneratorType{"eventGeneratorType", -1, "If positive, enable event selection using subGeneratorId information. The value indicates which events to keep (0 = MB, 4 = charm triggered, 5 = beauty triggered)"}; Configurable decayChannel{"decayChannel", 1, "Resonant decay channels: 1 for Ds->PhiPi->KKpi, 2 for Ds->K0*K->KKPi"}; Configurable cutCollPosZMc{"cutCollPosZMc", 10., "max z-vertex position for collision acceptance"}; @@ -196,9 +196,9 @@ struct HfTaskCorrelationDsHadrons { AxisSpec axisDetlaEta = {binsEta, "#it{#eta}^{Hadron}-#it{#eta}^{D}"}; AxisSpec axisEta = {binsEta, "#it{#eta}"}; AxisSpec axisDetlaPhi = {binsPhi, "#it{#varphi}^{Hadron}-#it{#varphi}^{D} (rad)"}; - AxisSpec axisPtD = {(std::vector)binsPtD, "#it{p}_{T}^{D} (GeV/#it{c})"}; - AxisSpec axisCentrality = {(std::vector)binsCentrality, "centrality FT0M (%)"}; - AxisSpec axisPtHadron = {(std::vector)binsPtHadron, "#it{p}_{T}^{Hadron} (GeV/#it{c})"}; + AxisSpec axisPtD = {std::vector(binsPtD), "#it{p}_{T}^{D} (GeV/#it{c})"}; + AxisSpec axisCentrality = {std::vector(binsCentrality), "centrality FT0M (%)"}; + AxisSpec axisPtHadron = {std::vector(binsPtHadron), "#it{p}_{T}^{Hadron} (GeV/#it{c})"}; AxisSpec axisPoolBin = {binsPoolBin, "poolBin"}; AxisSpec const axisBdtScore = {binsBdtScore, "Bdt score"}; AxisSpec const axisMultFT0M = {binsMultFT0M, "MultiplicityFT0M"}; @@ -535,7 +535,7 @@ struct HfTaskCorrelationDsHadrons { continue; } - if (trackDcaXY > dcaXYTrackMax || trackDcaZ > dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRaws) { + if (std::abs(trackDcaXY) >= dcaXYTrackMax || std::abs(trackDcaZ) >= dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRows) { continue; } @@ -645,7 +645,7 @@ struct HfTaskCorrelationDsHadrons { continue; } - if (trackDcaXY > dcaXYTrackMax || trackDcaZ > dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRaws) { + if (std::abs(trackDcaXY) > dcaXYTrackMax || std::abs(trackDcaZ) > dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRows) { continue; } @@ -762,7 +762,7 @@ struct HfTaskCorrelationDsHadrons { continue; } - if (trackDcaXY > dcaXYTrackMax || trackDcaZ > dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRaws) { + if (std::abs(trackDcaXY) > dcaXYTrackMax || std::abs(trackDcaZ) > dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRows) { continue; } @@ -901,7 +901,7 @@ struct HfTaskCorrelationDsHadrons { continue; } - if (trackDcaXY > dcaXYTrackMax || trackDcaZ > dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRaws) { + if (std::abs(trackDcaXY) > dcaXYTrackMax || std::abs(trackDcaZ) > dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRows) { continue; } @@ -1246,7 +1246,7 @@ struct HfTaskCorrelationDsHadrons { // reconstructed track loop for (const auto& track : groupedTracks) { - if (!track.isGlobalTrackWoDCA() || track.tpcNClsCrossedRows() < nTpcCrossedRaws) { + if (!track.isGlobalTrackWoDCA() || track.tpcNClsCrossedRows() < nTpcCrossedRows) { continue; } if (track.has_mcParticle()) { diff --git a/PWGHF/HFC/Tasks/taskCorrelationDstarHadrons.cxx b/PWGHF/HFC/Tasks/taskCorrelationDstarHadrons.cxx index e14bc59a504..a8c1004df3e 100644 --- a/PWGHF/HFC/Tasks/taskCorrelationDstarHadrons.cxx +++ b/PWGHF/HFC/Tasks/taskCorrelationDstarHadrons.cxx @@ -124,7 +124,7 @@ struct HfTaskCorrelationDstarHadrons { void init(InitContext&) { - auto axisPtDstar = (std::vector)binsPtEfficiency; + auto axisPtDstar = std::vector(binsPtEfficiency); AxisSpec const axisSpecPtDstar = {axisPtDstar}; AxisSpec const axisSpecDeltaPhi = {nBinsDeltaPhi, -o2::constants::math::PIHalf, 3. * o2::constants::math::PIHalf}; AxisSpec const axisSpecDeltaEta = {deltaEtaBinEdges}; diff --git a/PWGHF/HFC/Tasks/taskCorrelationLcHadrons.cxx b/PWGHF/HFC/Tasks/taskCorrelationLcHadrons.cxx index 6293db1ea25..e977e1d3524 100644 --- a/PWGHF/HFC/Tasks/taskCorrelationLcHadrons.cxx +++ b/PWGHF/HFC/Tasks/taskCorrelationLcHadrons.cxx @@ -162,7 +162,7 @@ struct HfTaskCorrelationLcHadrons { Configurable yCandGenMax{"yCandGenMax", 0.5, "max. gen. cand. rapidity"}; Configurable ptDaughterMin{"ptDaughterMin", 0.1, "min. daughter pT"}; Configurable activateQA{"activateQA", false, "Flag to enable debug histogram"}; - Configurable nTpcCrossedRaws{"nTpcCrossedRaws", 70, "Number of crossed TPC Rows"}; + Configurable nTpcCrossedRows{"nTpcCrossedRows", 70, "Number of crossed TPC Rows"}; Configurable useCentrality{"useCentrality", false, "Flag for centrality dependent analyses"}; // sign and invMasss Configurable fillSignAndMass{"fillSignAndMass", false, "flag to select Lc-h corr with Lc invarient mass and sign of pairs"}; @@ -215,12 +215,12 @@ struct HfTaskCorrelationLcHadrons { { // Axis definition AxisSpec axisMassLc = {binsMassLc, "inv. mass (p K #pi) (GeV/#it{c}^{2})"}; - AxisSpec axisPtCorr = {(std::vector)binsPtCorrelations, "#it{p}_{T}^{#Lambda_c} (GeV/#it{c})"}; - AxisSpec axisPtLc = {(std::vector)binsPtEfficiencyLc, "#it{p}_{T}^{#Lambda_c} (GeV/#it{c})"}; + AxisSpec axisPtCorr = {std::vector(binsPtCorrelations), "#it{p}_{T}^{#Lambda_c} (GeV/#it{c})"}; + AxisSpec axisPtLc = {std::vector(binsPtEfficiencyLc), "#it{p}_{T}^{#Lambda_c} (GeV/#it{c})"}; AxisSpec const axisMultFT0M = {binsMultFT0M, "MultiplicityFT0M"}; AxisSpec axisDeltaEta = {binsEta, "#it{#eta}^{Hadron}-#it{#eta}^{#Lambda_c}"}; AxisSpec axisDeltaPhi = {binsPhi, "#it{#varphi}^{Hadron}-#it{#varphi}^{#Lambda_c} (rad)"}; - AxisSpec axisPtHadron = {(std::vector)binsPtHadron, "#it{p}_{T}^{Hadron} (GeV/#it{c})"}; + AxisSpec axisPtHadron = {std::vector(binsPtHadron), "#it{p}_{T}^{Hadron} (GeV/#it{c})"}; AxisSpec axisPoolBin = {binsPoolBin, "poolBin"}; AxisSpec axisLcPrompt = {2, -0.5, 1.5, "Prompt #Lambda_c"}; AxisSpec const axisBdtScore = {binsBdtScore, "Bdt score"}; @@ -431,7 +431,7 @@ struct HfTaskCorrelationLcHadrons { "Right SB Low", "Right SB Up"}; static const AxisSpec axisSidebandLimits = {regionLimits, 0.5, 6.5, ""}; - auto hSigSidebandLimits = registry.add("Inputs/hSigSidebandLimits", "Signal and Sideband Limits;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSidebandLimits, {(std::vector)binsPtCorrelations, "#it{p}_{T} (GeV/#it{c})"}}}); + auto hSigSidebandLimits = registry.add("Inputs/hSigSidebandLimits", "Signal and Sideband Limits;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSidebandLimits, {std::vector(binsPtCorrelations), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iLim = 0; iLim < regionLimits; iLim++) { hSigSidebandLimits->GetXaxis()->SetBinLabel(iLim + 1, labels[iLim].data()); } @@ -505,7 +505,7 @@ struct HfTaskCorrelationLcHadrons { if (bdtScorePrompt < mlOutputPrompt->at(effBinLc) || bdtScoreBkg > mlOutputBkg->at(effBinLc)) { continue; } - if (trackDcaXY > dcaXYTrackMax || trackDcaZ > dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRaws) { + if (std::abs(trackDcaXY) >= dcaXYTrackMax || std::abs(trackDcaZ) >= dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRows) { continue; } @@ -673,7 +673,7 @@ struct HfTaskCorrelationLcHadrons { if (bdtScorePrompt < mlOutputPrompt->at(effBinLc) || bdtScoreBkg > mlOutputBkg->at(effBinLc)) { continue; } - if (trackDcaXY > dcaXYTrackMax || trackDcaZ > dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRaws) { + if (std::abs(trackDcaXY) > dcaXYTrackMax || std::abs(trackDcaZ) > dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRows) { continue; } double efficiencyWeight = 1.; diff --git a/PWGHF/HFC/Tasks/taskCorrelationsEfficiencies.cxx b/PWGHF/HFC/Tasks/taskCorrelationsEfficiencies.cxx new file mode 100644 index 00000000000..365920f243b --- /dev/null +++ b/PWGHF/HFC/Tasks/taskCorrelationsEfficiencies.cxx @@ -0,0 +1,473 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file taskCorrelationsEfficiencies.cxx +/// \brief Charm hadrons and associated tracks efficiency evaluation for correlations studies +/// \author Marcello Di Costanzo , Politecnico and INFN Torino + +#include "PWGHF/Core/CentralityEstimation.h" +#include "PWGHF/Core/DecayChannels.h" +#include "PWGHF/DataModel/AliasTables.h" +#include "PWGHF/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/DataModel/CandidateSelectionTables.h" +#include "PWGHF/HFC/DataModel/DerivedDataCorrelationTables.h" +#include "PWGHF/HFC/Utils/utilsFlow.h" +#include "PWGHF/Utils/utilsEvSelHf.h" +#include "PWGLF/DataModel/mcCentrality.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::hf_centrality; +using namespace o2::hf_evsel; +using namespace o2::hfc_utils_flow; + +namespace o2::aod +{ +namespace hf_correlations_eff +{ +// Charm-hadron candidate columns +DECLARE_SOA_COLUMN(Pt, pt, float); //! Transverse momentum of candidate (GeV/c) +DECLARE_SOA_COLUMN(PvContrib, pvContrib, int); //! Number of primary vertex contributors +DECLARE_SOA_COLUMN(Eta, eta, float); //! Pseudorapidity of candidate +DECLARE_SOA_COLUMN(Phi, phi, float); //! Azimuthal angle of candidate +DECLARE_SOA_COLUMN(Origin, origin, int); //! Origin of the candidate (prompt or non-prompt) +DECLARE_SOA_COLUMN(MlScore0, mlScore0, float); //! ML score of the first configured index +DECLARE_SOA_COLUMN(MlScore1, mlScore1, float); //! ML score of the second configured index + +// Associated track columns +DECLARE_SOA_COLUMN(PtAssoc, ptAssoc, float); //! Transverse momentum of associated track (GeV/c) +DECLARE_SOA_COLUMN(EtaAssoc, etaAssoc, float); //! Pseudorapidity of associated track +DECLARE_SOA_COLUMN(PhiAssoc, phiAssoc, float); //! Azimuthal angle of associated track +DECLARE_SOA_COLUMN(ZVtx, zVtx, float); //! Primary vertex z position (cm) +DECLARE_SOA_COLUMN(AssTrkSpecie, assTrkSpecie, int); //! Associated track species (0=pi, 1=K, 2=p, 3=el, 4=mu) +DECLARE_SOA_COLUMN(AssTrkOrigin, assTrkOrigin, int); //! Associated track origin (0=primary, 1=secondary) +} // namespace hf_correlations_eff + +DECLARE_SOA_TABLE(HfcMcrigInfo, "AOD", "HFCMCTRIGINFO", + hf_correlations_eff::Pt, + hf_correlations_eff::PvContrib, + hf_correlations_eff::Eta, + hf_correlations_eff::Phi, + hf_correlations_eff::Origin, + hf_correlations_eff::MlScore0, + hf_correlations_eff::MlScore1); + +DECLARE_SOA_TABLE(HfcMcAssocInfo, "AOD", "HFCMCASSOCINFO", + hf_correlations_eff::PtAssoc, + hf_correlations_eff::EtaAssoc, + hf_correlations_eff::PhiAssoc, + hf_correlations_eff::ZVtx, + hf_correlations_eff::AssTrkSpecie, + hf_correlations_eff::AssTrkOrigin); +} // namespace o2::aod + +/// Code to obtain efficiency correction factors for (Delta Eta, Delta Phi) correlation maps +struct HfTaskCorrelationsEfficiencies { + + Produces rowTrigMc; + Produces rowAssocMc; + Produces rowAssocTrkSels; + + Configurable selectionFlag{"selectionFlag", 15, "Selection Flag for hadron (ML score tables are required to run the task)"}; + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable> classMl{"classMl", {0, 2}, "Indexes of BDT scores to be stored. Two indexes max."}; + Configurable yCandMax{"yCandMax", 0.8, "max. cand. rapidity"}; + Configurable tpcNClsCrossedRowsMin{"tpcNClsCrossedRowsMin", 70, "min. TPC crossed rows for associated tracks"}; + Configurable etaTrkMax{"etaTrkMax", 1., "max. track eta"}; + Configurable ptTrkMin{"ptTrkMin", 0.3, "min. track pT"}; + Configurable ptTrkMax{"ptTrkMax", 3., "max. track pT"}; + Configurable dcaXYTrkMax{"dcaXYTrkMax", 1., "max. track DCA XY"}; + Configurable dcaZTrkMax{"dcaZTrkMax", 1., "max. track DCA Z"}; + Configurable usePtDiffDcaXYCut{"usePtDiffDcaXYCut", false, "Use pt-differential DCAxy cut for associated tracks"}; + Configurable dcaXYTrkNSigmaMax{"dcaXYTrkNSigmaMax", 7, "Cut on number of sigma deviations from expected DCA in the transverse direction"}; + Configurable dcaXYPtPrimTrkFunc{"dcaXYPtPrimTrkFunc", "(0.0026+0.005/(x^1.01))", "Functional form of pt-dependent DCAxy cut"}; + + // Configurables for tree filling + Configurable fillRecoTrees{"fillRecoTrees", false, "Fill trees of reconstructed charmed hadrons and associated tracks"}; + Configurable downSampleTracks{"downSampleTracks", 1., "Fraction of tracks to keep"}; + Configurable ptMaxDownSampleTracks{"ptMaxDownSampleTracks", 10., "Maximum pt for the application of the downsampling factor"}; + Configurable downSampleCharm{"downSampleCharm", 1., "Fraction of pairs to keep"}; + Configurable ptMaxDownSampleCharm{"ptMaxDownSampleCharm", 10., "Maximum pt for the application of the downsampling factor"}; + + HfEventSelection hfEvSel; // event selection and monitoring + HfEventSelectionMc hfEvSelMc; + HfcFlowHelper hfcFlowHelper; // flow utilities + o2::framework::Service ccdb{}; + SliceCache cache; + + std::unique_ptr funcDcaXYPtCutPrimTrk{nullptr}; + + using BCsInfo = soa::Join; + using McCollisionsWMult = soa::Join; + using RecoCollisions = soa::Join; + using McCandsDs = soa::Filtered>; + using McCandsDplus = soa::Filtered>; + using McCandsD0 = soa::Filtered>; + using McCandsLc = soa::Filtered>; + using McTracks = soa::Filtered>; + + Filter filterSelectDsCands = aod::hf_sel_candidate_ds::isSelDsToKKPi >= selectionFlag || aod::hf_sel_candidate_ds::isSelDsToPiKK >= selectionFlag; + Filter filterSelectDplusCands = aod::hf_sel_candidate_dplus::isSelDplusToPiKPi >= selectionFlag; + Filter filterSelectD0Cands = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlag || aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlag; + Filter filterSelectLcCands = aod::hf_sel_candidate_lc::isSelLcToPKPi >= selectionFlag || aod::hf_sel_candidate_lc::isSelLcToPiKP >= selectionFlag; + Filter filterSelectTrk = (nabs(aod::track::eta) < etaTrkMax) && (aod::track::pt > ptTrkMin) && (aod::track::pt < ptTrkMax) && (nabs(aod::track::dcaXY) < dcaXYTrkMax) && (nabs(aod::track::dcaZ) < dcaZTrkMax); + + Preslice candsDsPerColl = aod::hf_cand::collisionId; + Preslice candsDplusPerColl = aod::hf_cand::collisionId; + Preslice candsD0PerColl = aod::hf_cand::collisionId; + Preslice candsLcPerColl = aod::hf_cand::collisionId; + Preslice trackIndicesPerColl = aod::track::collisionId; + + PresliceUnsorted mcPartsPerMcCollision = aod::mcparticle::mcCollisionId; + PresliceUnsorted colPerMcCollision = aod::mccollisionlabel::mcCollisionId; + + ConfigurableAxis binsPtCharm{"binsPtCharm", {VARIABLE_WIDTH, 0., 2., 4., 6., 8., 24.}, ""}; + ConfigurableAxis binsPtAssoc{"binsPtAssoc", {20, 0, 10}, ""}; + ConfigurableAxis binsZVtx{"binsZVtx", {120, -11, 11}, ""}; + ConfigurableAxis binsPvContrib{"binsPvContrib", {5001, -0.5, 5000.5}, ""}; + ConfigurableAxis binsEta{"binsEta", {50, -2., 2.}, ""}; + ConfigurableAxis binsPhi{"binsPhi", {64, 0, 6.28}, ""}; + ConfigurableAxis binsMlOne{"binsMlOne", {100, 0., 1.}, ""}; + ConfigurableAxis binsMlTwo{"binsMlTwo", {100, 0., 1.}, ""}; + + HistogramRegistry registry{"registry", {}}; + + void init(InitContext& initContext) + { + + hfEvSel.init(registry); + const auto& workflows = initContext.services().get(); + for (const DeviceSpec& device : workflows.devices) { + if (device.name == "hf-task-correlations-efficiencies") { + // init HF event selection helper + hfEvSelMc.init(device, registry); + break; + } + } + ccdb->setURL(ccdbUrl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + + std::vector classesMl; + for (const auto scoreClass : classMl.value) { + if (scoreClass < 0) { + LOG(fatal) << "ML class indexes must be non-negative"; + return; + } + classesMl.push_back(static_cast(scoreClass)); + } + + if (classesMl.size() > 2) { + LOG(fatal) << "At most two ML classes can be selected"; + return; + } + + hfcFlowHelper.init(classesMl, yCandMax.value); + + // Setup pt-dependent DCAxy cut function + if (usePtDiffDcaXYCut) { + funcDcaXYPtCutPrimTrk = std::make_unique("funcDcaXYPtCutPrimTrk", Form("[0]*%s", dcaXYPtPrimTrkFunc.value.data()), 0.001, 100.); + funcDcaXYPtCutPrimTrk->SetParameter(0, dcaXYTrkNSigmaMax); + LOGF(info, "DCAxy pt-dependence function: %s", Form("[0]*%s", dcaXYPtPrimTrkFunc.value.data())); + } + + // Add efficiency sparses + // Axes: mass, pt, pv contributors, pseudo-rapidity, ml_score_0, ml_score_1 + const AxisSpec axisPtTrig = {binsPtCharm, "#it{p}_{T} Trig (GeV/#it{c})"}; + const AxisSpec axisPvContrib = {binsPvContrib, "PV contributors"}; + const AxisSpec axisEtaTrig = {binsEta, "#it{#eta}"}; + const AxisSpec axisPhiTrig = {binsPhi, "#it{#varphi}"}; + const AxisSpec axisMlScore0 = {binsMlOne, "ML score 0"}; + const AxisSpec axisMlScore1 = {binsMlTwo, "ML score 1"}; + std::vector axesCharmReco = {axisPtTrig, axisPvContrib, axisEtaTrig, axisPhiTrig, axisMlScore0, axisMlScore1}; + + registry.add("hCharmPromptGen", "Generated prompt charm-hadron candidates;;", HistType::kTHnSparseF, {{axisPtTrig}, {axisEtaTrig}, {axisPhiTrig}}); + registry.add("hCharmFdGen", "Generated non-prompt charm-hadron candidates;;", HistType::kTHnSparseF, {{axisPtTrig}, {axisEtaTrig}, {axisPhiTrig}}); + registry.add("hCharmPromptReco", "Reconstructed prompt charm-hadron candidates;;", HistType::kTHnSparseF, axesCharmReco); + registry.add("hCharmFdReco", "Reconstructed non-prompt charm-hadron candidates;;", HistType::kTHnSparseF, axesCharmReco); + + // Axes: pt, eta, phi, z_vtx, specie + const AxisSpec axisPtAssoc = {binsPtAssoc, "#it{p}_{T} Assoc (GeV/#it{c})"}; + const AxisSpec axisEtaAssoc = {binsEta, "#it{#eta}"}; + const AxisSpec axisPhiAssoc = {binsPhi, "#it{#varphi}"}; + const AxisSpec axisZVtx = {binsZVtx, "#it{z}_{vtx}"}; + const AxisSpec axisAssTrkSpecie = {static_cast(NAssTracksTypes), -0.5, static_cast(NAssTracksTypes) - 0.5, "Particle species"}; + const AxisSpec axisTrackOrigin = {2, -0.5, 1.5, "Particle origin"}; + std::vector axesAssocTracks = {axisPtAssoc, axisEtaAssoc, axisPhiAssoc, axisZVtx, axisAssTrkSpecie, axisTrackOrigin}; + registry.add("hAssTrackReco", "Reconstructed associated tracks;;", HistType::kTHnSparseF, axesAssocTracks); + registry.add("hAssTrackGen", "Generated associated tracks;;", HistType::kTHnSparseF, axesAssocTracks); + } + + /// Match charm-hadron based on its reconstruction flag + /// \param assTrk is the associated track + /// \return the associated track specie index + template + bool isMcMatched(TCharmHad const& cand) + { + int flagMc{-1}; + if constexpr (IsReco) { + flagMc = std::abs(cand.flagMcMatchRec()); + } else { + flagMc = std::abs(cand.flagMcMatchGen()); + } + + if constexpr (CandType == TriggerChannels::D0ToPiK) { + return flagMc == static_cast(hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK); + } else if constexpr (CandType == TriggerChannels::DplusToPiKPi) { + return flagMc == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi); + } else if constexpr (CandType == TriggerChannels::LcToPKPi) { + return flagMc == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi); + } else { // Ds + int8_t reso{0}; + if constexpr (IsReco) { + reso = std::abs(cand.flagMcDecayChanRec()); + } else { + reso = std::abs(cand.flagMcDecayChanGen()); + } + return flagMc == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DsToPiKK) && + reso == static_cast(hf_decay::hf_cand_3prong::DecayChannelResonant::DsToPhiPi); + } + } + + /// Fill info for charm-hadron efficiencies + /// \param trigCands are the trigger candidates + /// \param assTrks are the associated tracks + /// \param cent is the collision centrality + /// \param mult is the collision multiplicity + template + void fillCharmEfficiencies(TCharmCands const& charmCands, int numContrib) + { + for (const auto& cand : charmCands) { + if (!hfcFlowHelper.cutCandRapidity(cand)) { + continue; + } + + if (!isMcMatched(cand)) { + continue; + } + + // Fill the sparse + std::array outputMl = hfcFlowHelper.getCandMlScores(cand); + if (cand.originMcRec() == RecoDecay::OriginType::Prompt) { + registry.fill(HIST("hCharmPromptReco"), cand.pt(), numContrib, cand.eta(), cand.phi(), outputMl[0], outputMl[1]); + } + if (cand.originMcRec() == RecoDecay::OriginType::NonPrompt) { + registry.fill(HIST("hCharmFdReco"), cand.pt(), numContrib, cand.eta(), cand.phi(), outputMl[0], outputMl[1]); + } + + if (fillRecoTrees) { + // Downsample charm candidates based on pt + if (downSampleCharm < 1.) { + float const pseudoRndm = cand.pt() * 1000. - static_cast(cand.pt() * 1000); + if (cand.pt() < ptMaxDownSampleCharm && pseudoRndm >= downSampleCharm) { + continue; + } + } + rowTrigMc(cand.pt(), numContrib, cand.eta(), cand.phi(), cand.originMcRec(), outputMl[0], outputMl[1]); + } + } + } + + // Dplus efficiency + void processEffDplus(RecoCollisions const& recoCollisions, + McCandsDplus const& candsDplus, + soa::Join const& genParticles, + aod::McCollisions const&) + { + for (const auto& coll : recoCollisions) { + auto candsInColl = candsDplus.sliceBy(candsDplusPerColl, coll.globalIndex()); + if (candsInColl.size() < 1) { + continue; + } + float cent{-1.}, mult{-1.}; + CentralityEstimator centDetector = CentralityEstimator::None; + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { + continue; + } + fillCharmEfficiencies(candsInColl, coll.numContrib()); + } + + for (const auto& genCand : genParticles) { + if (!genCand.has_mcCollision()) { + continue; + } + if (std::abs(genCand.y()) > yCandMax) { + continue; + } + if (!isMcMatched(genCand)) { + continue; + } + + if (genCand.originMcGen() == RecoDecay::OriginType::Prompt) { + registry.fill(HIST("hCharmPromptGen"), genCand.pt(), genCand.eta(), genCand.phi()); + } + if (genCand.originMcGen() == RecoDecay::OriginType::NonPrompt) { + registry.fill(HIST("hCharmFdGen"), genCand.pt(), genCand.eta(), genCand.phi()); + } + } + } + PROCESS_SWITCH(HfTaskCorrelationsEfficiencies, processEffDplus, "Process efficiencies for Dplus candidates", false); + + /// Match associated tracks based on their PDG code + /// \param assTrk is the associated track + /// \return the associated track specie index + template + int getAssocTrkSpecie(TAssocTrk const& assTrk) + { + switch (std::abs(assTrk.pdgCode())) { + case kPiPlus: + return static_cast(AssTracksTypes::Pi); + case kKPlus: + return static_cast(AssTracksTypes::Ka); + case kProton: + return static_cast(AssTracksTypes::Pr); + case kElectron: + return static_cast(AssTracksTypes::El); + case kMuonMinus: + return static_cast(AssTracksTypes::Mu); + default: + return -1; + } + } + + // Associated Tracks + void processEffAssTracks(RecoCollisions const& recoCollisions, + McTracks const& recoTracks, + aod::McParticles const& genParticles, + McCollisionsWMult const& genCollisions, + BCsInfo const&) + { + for (const auto& coll : recoCollisions) { + float cent{-1.}, mult{-1.}; + CentralityEstimator centDetector = CentralityEstimator::None; + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { + continue; + } + if (!coll.has_mcCollision()) { + continue; + } + auto mcCollision = coll.template mcCollision_as(); + + auto tracksInColl = recoTracks.sliceBy(trackIndicesPerColl, coll.globalIndex()); + for (const auto& assTrk : tracksInColl) { + + if (!assTrk.isGlobalTrackWoDCA() || assTrk.tpcNClsCrossedRows() < tpcNClsCrossedRowsMin) { + continue; + } + + if (!assTrk.has_mcParticle()) { + continue; + } + auto mcParticle = assTrk.template mcParticle_as(); + int matchedSpecie = getAssocTrkSpecie(mcParticle); + if (matchedSpecie < 0) { + continue; + } + + // DCA selection + double assTrkPt = assTrk.pt(); + if (usePtDiffDcaXYCut) { + float const dcaXYTrkCut = funcDcaXYPtCutPrimTrk->Eval(assTrkPt); + if (std::fabs(assTrk.dcaXY()) > dcaXYTrkCut) { + continue; + } + } + int assTrkOrigin = mcParticle.isPhysicalPrimary() ? 0 : 1; // 0 for primary, 1 for secondary + + std::vector values = {assTrk.pt(), assTrk.eta(), assTrk.phi(), mcCollision.posZ(), static_cast(matchedSpecie), static_cast(assTrkOrigin)}; + + // Fill histograms for associated tracks + registry.get(HIST("hAssTrackReco"))->Fill(values.data()); + + if (fillRecoTrees) { + // Downsample tracks based on pt + if (downSampleTracks < 1.) { + float const pseudoRndm = assTrk.pt() * 1000. - static_cast(assTrk.pt() * 1000); + if (assTrk.pt() < ptMaxDownSampleTracks && pseudoRndm >= downSampleTracks) { + continue; + } + } + rowAssocMc(assTrk.pt(), assTrk.eta(), assTrk.phi(), mcCollision.posZ(), matchedSpecie, assTrkOrigin); + rowAssocTrkSels(assTrk.tpcNClsCrossedRows(), assTrk.itsClusterMap(), assTrk.itsNCls(), assTrk.dcaXY(), assTrk.dcaZ()); + } + } + } + + // Fill histograms for generated associated tracks + for (const auto& mcColl : genCollisions) { + + float cent{-1.}, mult{-1.}; + const auto recoColls = recoCollisions.sliceBy(colPerMcCollision, mcColl.globalIndex()); + CentralityEstimator centDetector = CentralityEstimator::None; + if (!hfcFlowHelper.selectGenCollision(hfEvSelMc, mcColl, recoColls, centDetector, cent, mult)) { + continue; + } + + const auto groupedMcParticles = genParticles.sliceBy(mcPartsPerMcCollision, mcColl.globalIndex()); + for (const auto& genTrk : groupedMcParticles) { + int matchedSpecie = getAssocTrkSpecie(genTrk); + if (matchedSpecie < 0) { + continue; + } + if (!genTrk.has_mcCollision()) { + // Should never happen because we group on the generated collisions + continue; + } + + // Pt and eta selection + if (genTrk.pt() < ptTrkMin || genTrk.pt() > ptTrkMax || std::fabs(genTrk.eta()) > etaTrkMax) { + continue; + } + int genTrkOrigin = genTrk.isPhysicalPrimary() ? 0 : 1; // 0 for primary, 1 for secondary + registry.fill(HIST("hAssTrackGen"), genTrk.pt(), genTrk.eta(), genTrk.phi(), mcColl.posZ(), matchedSpecie, genTrkOrigin); + } + } + } + PROCESS_SWITCH(HfTaskCorrelationsEfficiencies, processEffAssTracks, "Process efficiencies for associated tracks", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{adaptAnalysisTask(context)}; +} diff --git a/PWGHF/HFC/Tasks/taskFlow.cxx b/PWGHF/HFC/Tasks/taskFlow.cxx index 1337864b67b..0113ba3b3af 100644 --- a/PWGHF/HFC/Tasks/taskFlow.cxx +++ b/PWGHF/HFC/Tasks/taskFlow.cxx @@ -22,12 +22,14 @@ #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" #include "PWGHF/DataModel/TrackIndexSkimmingTables.h" +#include "PWGLF/DataModel/mcCentrality.h" #include "PWGMM/Mult/DataModel/bestCollisionTable.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/RCTSelectionFlags.h" #include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" +#include "Common/DataModel/CollisionAssociationTables.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/McCollisionExtra.h" #include "Common/DataModel/Multiplicity.h" @@ -46,7 +48,6 @@ #include #include #include -#include #include #include #include @@ -63,6 +64,7 @@ #include #include +#include #include #include #include @@ -82,7 +84,7 @@ using namespace o2::framework; using namespace o2::framework::expressions; enum CorrelationCase { - TpcTpc, + TpcTpc = 0, TpcMft, TpcFv0a, MftFv0a, @@ -103,6 +105,24 @@ enum DataType { Mc }; +enum FT0DetectorChannels { + FT0AInnerRingMin = 0, + FT0AInnerRingMax = 31, + FT0AOuterRingMin = 32, + FT0AOuterRingMax = 95, + FT0CInnerRingMin = 96, + FT0CInnerRingMax = 143, + FT0COuterRingMin = 144, + FT0COuterRingMax = 207 +}; + +enum FT0InnerOrOuter { + FT0Error, + FT0InnerRing, + FT0OuterRing, + NFT0InnerOrOuterSteps +}; + enum ReassociationMftTracks { NotReassociatedMftTracks = 0, ReassociatedMftTracks, @@ -129,6 +149,11 @@ enum MftTrackAmbiguityStep { NMftAmbiguitySteps }; +enum MftTrackTemplate { + FilteredMftTracks = 0, + FilteredMftTracksWCollsMcLabels +}; + enum MftTrackSelectionStep { NoSelection = 0, Eta, @@ -168,6 +193,7 @@ enum SpecificEventSelectionStep { IsNoCollInTimeRangeStrict, IsNoHighMultCollInPrevRof, IsRctFlagChecked, + IsFoundFT0, NSpecificEventSelectionSteps }; @@ -176,27 +202,14 @@ enum TrackSelection { TrackSelectionGlobalTrack }; -// static constexpr std::string_view whatEventType[] = {"SameEvent/", "MixedEvent/"}; -static constexpr std::string_view WhatDataType[] = {"Data/", "MC/"}; -static constexpr std::string_view WhatCorrelationCase[] = {"TpcTpc/", "TpcMft/", "TpcFv0a/", "MftFv0a/", "TpcFt0a/", "MftFt0a/", "TpcFt0c/", "Ft0aFt0c/"}; -static constexpr std::string_view WhatParticles[] = {"ChPartChPart/", "D0ChPart/", "LcChPart/"}; -static constexpr std::string_view WhatCentralityEstimator[] = {"centFT0C", "CentFT0CVariant1", "centFT0M"}; -static constexpr std::string_view WhatMultiplicityEstimator[] = {"multNTracksPV", "multNumContrib", "multFT0C", "multFT0M"}; +static constexpr std::array WhatDataType = {"Data/", "MC/"}; +static constexpr std::array WhatCorrelationCase = {"TpcTpc/", "TpcMft/", "TpcFv0a/", "MftFv0a/", "TpcFt0a/", "MftFt0a/", "TpcFt0c/", "Ft0aFt0c/"}; +static constexpr std::array WhatParticles = {"ChPartChPart/", "D0ChPart/", "LcChPart/"}; +static constexpr std::array WhatCentralityEstimator = {"centFT0C", "CentFT0CVariant1", "centFT0M"}; +static constexpr std::array WhatMultiplicityEstimator = {"multNTracksPV", "multNumContrib", "multFT0C", "multFT0M"}; auto static constexpr MinFt0cCell = 96; -static constexpr TrackSelectionFlags::flagtype TrackSelectionIts = - TrackSelectionFlags::kITSNCls | TrackSelectionFlags::kITSChi2NDF | - TrackSelectionFlags::kITSHits; -static constexpr TrackSelectionFlags::flagtype TrackSelectionTpc = - TrackSelectionFlags::kTPCNCls | - TrackSelectionFlags::kTPCCrossedRowsOverNCls | - TrackSelectionFlags::kTPCChi2NDF; -static constexpr TrackSelectionFlags::flagtype TrackSelectionDca = - TrackSelectionFlags::kDCAz | TrackSelectionFlags::kDCAxy; -static constexpr TrackSelectionFlags::flagtype TrackSelectionDcaxyOnly = - TrackSelectionFlags::kDCAxy; - -static constexpr float PairCutDefaults[1][5] = {{-1, -1, -1, -1, -1}}; +static constexpr std::array, 1> PairCutDefaults{{{{-1, -1, -1, -1, -1}}}}; struct HfTaskFlow { @@ -234,6 +247,8 @@ struct HfTaskFlow { Configurable nMixedEvents{"nMixedEvents", 5, "Number of mixed events per event"}; Configurable nSamples{"nSamples", 10, "number of different samples for correlations"}; Configurable nameCorrelationContainer{"nameCorrelationContainer", "", "Add the possibility to the rename the correlation container as configurable"}; + Configurable removeQAForSystematics{"removeQAForSystematics", false, "Remove QA plots for systematics, deactivate all plots except trigger hist"}; + Configurable useCutNeutralParticles{"useCutNeutralParticles", false, "Choose to use or not use neutral particles in the correlation analysis"}; Configurable useEfficiencyCorrection{"useEfficiencyCorrection", false, "Choose to use or not use efficiency correction, if not used, weight is set to 1"}; Configurable useOnlyPrimaryInMc{"useOnlyPrimaryInMc", false, "Choose to use or not use only primaries for McGen"}; } configTask; @@ -258,6 +273,7 @@ struct HfTaskFlow { Configurable useCentrality{"useCentrality", false, "use centrality instead of multiplicity"}; Configurable useMultiplicityFromTracks{"useMultiplicityFromTracks", false, "Use multiplicity from counting tracks"}; Configurable useMultiplicityFromTracksCorrected{"useMultiplicityFromTracksCorrected", false, "Use multiplicity from counting tracks, corrected but takes a lot of computation time"}; + Configurable useNoFoundFT0Cut{"useNoFoundFT0Cut", false, "Use cut on no FT0 signal"}; Configurable requireRCTFlagChecker{"requireRCTFlagChecker", false, "Check event quality in run condition table"}; Configurable requireCorrelationAnalysisRCTFlagChecker{"requireCorrelationAnalysisRCTFlagChecker", false, "Check event quality in run condition table for correlation analysis"}; Configurable setRCTFlagCheckerLabel{"setRCTFlagCheckerLabel", "CBT_muon_global", "Evt sel: RCT flag checker label"}; @@ -286,7 +302,7 @@ struct HfTaskFlow { Configurable minMergingRadius{"minMergingRadius", 0.8, "max radius for merging cut"}; Configurable minTpcClusters{"minTpcClusters", 50.0f, "cut for minimum TPC clusters"}; Configurable minTpcCrossedRows{"minTpcCrossedRows", 70.0f, "cut for minimum TOC crossed rows"}; - Configurable> pairCut{"pairCut", {PairCutDefaults[0], 5, {"Photon", "K0", "Lambda", "Phi", "Rho"}}, "Pair cuts on various particles"}; + Configurable> pairCut{"pairCut", {PairCutDefaults[0].data(), 5, {"Photon", "K0", "Lambda", "Phi", "Rho"}}, "Pair cuts on various particles"}; Configurable ptCentralTrackMin{"ptCentralTrackMin", 0.2f, "min. pT of central tracks"}; Configurable ptCentralTrackMax{"ptCentralTrackMax", 10.0f, "max. pT of central tracks"}; Configurable trackSelectionType{"trackSelectionType", 1, "Track selection: 0 -> kGlobalTrack or isGlobalTrackSDD , 1 -> kGlobalTrack, 2 -> kGlobalTrackWoPtEta, 3 -> kGlobalTrackWoDCA, 4 -> No globalTrack selection"}; @@ -334,6 +350,7 @@ struct HfTaskFlow { std::string prefix = "ConfigFit_group"; Configurable cfgGainEqPath{"cfgGainEqPath", "Analysis/EventPlane/GainEq", "CCDB path for gain equalization constants"}; Configurable cfgCorrLevel{"cfgCorrLevel", 1, "calibration step: 0 = no corr, 1 = gain corr"}; + Configurable discriminateInnerOrOuter{"discriminateInnerOrOuter", false, "if true, fill correlationContainer with either Inner ou Outer for FIT, if false, fill with real eta information"}; } configFit; SliceCache cache; @@ -344,7 +361,7 @@ struct HfTaskFlow { o2::ccdb::CcdbApi ccdbApi; o2::ft0::Geometry ft0Det; // o2::fv0::Geometry* fv0Det{}; - std::vector cstFT0RelGain{}; + std::vector cstFT0RelGain; RCTFlagsChecker rctChecker; RCTFlagsChecker correlationAnalysisRctChecker{kFT0Bad, kITSBad, kTPCBadTracking, kTPCBadPID, kMFTBad, kITSLimAccMCRepr, kMFTLimAccMCRepr, kTPCLimAccMCRepr}; @@ -374,9 +391,15 @@ struct HfTaskFlow { // ========================= using SmallGroupMcCollisions = soa::SmallGroups>; - using FilteredMcCollisionsWMult = soa::Filtered>; - using FilteredMcCollisionsWMultWCollsExtra = soa::Filtered>; - using FilteredMcParticles = soa::Filtered; + // using FilteredMcCollisionsWMult = soa::Filtered>; + using FilteredMcCollisionsWMult = soa::Filtered>; + using FilteredMcCollisionsWMultWCollsExtra = soa::Filtered>; + // using FilteredMcParticles = soa::Filtered; + using FilteredMcParticles = aod::McParticles; + + // MC reconstructed + + // using FilteredMftTracksWCollsMcLabels = soa::Filtered>; // ========================= // Filters & partitions : DATA @@ -399,11 +422,7 @@ struct HfTaskFlow { (aod::track::pt < configCentral.ptCentralTrackMax) && requireGlobalTrackInFilter(); - Filter centralTrackItsTpcMatchingFilter = ifnode(ncheckbit(aod::track::v001::detectorMap, (uint8_t)o2::aod::track::TPC), ncheckbit(aod::track::trackCutFlag, TrackSelectionTpc), true) && - ncheckbit(aod::track::v001::detectorMap, (uint8_t)o2::aod::track::ITS) && - ncheckbit(aod::track::trackCutFlag, TrackSelectionIts); - - Filter centralTrackDcaFilter = (ifnode(configCentral.dcaZCentralTrackMax.node() > 0.f, nabs(aod::track::dcaZ) <= configCentral.dcaZCentralTrackMax && ncheckbit(aod::track::trackCutFlag, TrackSelectionDcaxyOnly), ncheckbit(aod::track::trackCutFlag, TrackSelectionDca))); + Filter centralTrackDcaFilter = (nabs(aod::track::dcaZ) <= configCentral.dcaZCentralTrackMax); Filter centralTrackChi2TpcClusterFilter = (aod::track::tpcChi2NCl < configCentral.maxChi2TpcClusters); @@ -419,7 +438,8 @@ struct HfTaskFlow { // Filters & partitions : MC // ========================= - Filter mcParticleFilter = (((aod::mcparticle::eta > configTask.etaMcParticlesTriggerMin) && (aod::mcparticle::eta < configTask.etaMcParticlesTriggerMax)) || ((aod::mcparticle::eta > configTask.etaMcParticlesAssocMin) && (aod::mcparticle::eta < configTask.etaMcParticlesAssocMax)) || (nabs(aod::mcparticle::eta) < configCentral.etaCentralTrackMax)) && (aod::mcparticle::pt > configTask.ptMcParticlesTriggerMin) && (aod::mcparticle::pt < configTask.ptMcParticlesTriggerMax); + // // Filter for McParticles + // Filter mcParticleFilter = (((aod::mcparticle::eta > configTask.etaMcParticlesTriggerMin) && (aod::mcparticle::eta < configTask.etaMcParticlesTriggerMax)) || ((aod::mcparticle::eta > configTask.etaMcParticlesAssocMin) && (aod::mcparticle::eta < configTask.etaMcParticlesAssocMax)) || (nabs(aod::mcparticle::eta) < configCentral.etaCentralTrackMax)) && (aod::mcparticle::pt < configTask.ptMcParticlesTriggerMax); // Filter for MCcollisions Filter mcCollisionFilter = nabs(aod::mccollision::posZ) < configCollision.zVertexMax; @@ -454,6 +474,7 @@ struct HfTaskFlow { ConfigurableAxis binsMixingMultiplicity{"binsMixingMultiplicity", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 100.1}, "multiplicity bins for event mixing"}; ConfigurableAxis binsMixingVertex{"binsMixingVertex", {20, -10, 10}, "vertex bins for event mixing"}; ConfigurableAxis axisCentrality{"axisCentrality", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, "centrality axis for histograms"}; + ConfigurableAxis axisChi2Ndf{"axisChi2Ndf", {100, 0, 100}, "chi2/ndf axis for MFT histograms"}; ConfigurableAxis axisNClusters{"axisNClusters", {9, 1, 10}, "axis for number of clusters of MFT tracks"}; ConfigurableAxis axisEtaEfficiency{"axisEtaEfficiency", {1, -1.0, 1.0}, "eta axis for efficiency histograms"}; ConfigurableAxis axisEtaAssociated{"axisEtaAssociated", {48, -4, -2}, "eta axis for MFT histograms"}; @@ -507,7 +528,7 @@ struct HfTaskFlow { void addMftHistograms() { registry.add("Data/Mft/hAmbiguityOfMftTracks", "hAmbiguityOfMftTracks", {HistType::kTH1D, {{MftTrackAmbiguityStep::NMftAmbiguitySteps, -0.5, +MftTrackAmbiguityStep::NMftAmbiguitySteps - 0.5}}}); - std::string labelsAmbiguityOfMftTracks[MftTrackAmbiguityStep::NMftAmbiguitySteps]; + std::array labelsAmbiguityOfMftTracks{}; labelsAmbiguityOfMftTracks[MftTrackAmbiguityStep::AllMftTracks] = "all MFT tracks"; labelsAmbiguityOfMftTracks[MftTrackAmbiguityStep::AfterTrackSelection] = "MFT tracks after selection"; labelsAmbiguityOfMftTracks[MftTrackAmbiguityStep::NumberOfAmbiguousTracks] = "how much tracks are ambigous"; @@ -519,7 +540,7 @@ struct HfTaskFlow { } registry.add("Data/Mft/hMftTracksSelection", "hMftTracksSelection", {HistType::kTH1D, {{MftTrackSelectionStep::NMftTrackSelectionSteps, -0.5, +MftTrackSelectionStep::NMftTrackSelectionSteps - 0.5}}}); - std::string labelsMftTracksSelection[MftTrackSelectionStep::NMftTrackSelectionSteps]; + std::array labelsMftTracksSelection{}; labelsMftTracksSelection[MftTrackSelectionStep::NoSelection] = "all MFT tracks"; labelsMftTracksSelection[MftTrackSelectionStep::Eta] = "MFT tracks after eta selection"; labelsMftTracksSelection[MftTrackSelectionStep::Cluster] = "MFT tracks after clusters selection"; @@ -537,7 +558,7 @@ struct HfTaskFlow { } registry.add("Data/Mft/hReassociationMftTracks", "hReassociationMftTracks", {HistType::kTH1D, {{ReassociationMftTracks::NReassociationMftTracksSteps, -0.5, +ReassociationMftTracks::NReassociationMftTracksSteps - 0.5}}}); - std::string labelsReassociationMftTracks[ReassociationMftTracks::NReassociationMftTracksSteps]; + std::array labelsReassociationMftTracks{}; labelsReassociationMftTracks[ReassociationMftTracks::NotReassociatedMftTracks] = "Ambiguous MFT tracks after track selection"; labelsReassociationMftTracks[ReassociationMftTracks::ReassociatedMftTracks] = "Reassociated MFT tracks by DCAxy method"; registry.get(HIST("Data/Mft/hReassociationMftTracks"))->SetMinimum(0); @@ -546,6 +567,7 @@ struct HfTaskFlow { registry.get(HIST("Data/Mft/hReassociationMftTracks"))->GetXaxis()->SetBinLabel(iBin + 1, labelsReassociationMftTracks[iBin].data()); } + registry.add("Data/Mft/hChi2OverNdfMft", "", {HistType::kTH1D, {configAxis.axisChi2Ndf}}); registry.add("Data/Mft/hPtMft", "", {HistType::kTH1D, {configAxis.axisPt}}); registry.add("Data/Mft/hPtVsClustersLTF", "", {HistType::kTH2D, {configAxis.axisPt, configAxis.axisNClusters}}); registry.add("Data/Mft/hPtVsClustersCA", "", {HistType::kTH2D, {configAxis.axisPt, configAxis.axisNClusters}}); @@ -555,6 +577,22 @@ struct HfTaskFlow { registry.add("Data/Mft/hNBestCollisionFwd", "", {HistType::kTH1F, {configAxis.axisMultiplicity}}); } + void addFt0Histograms() + { + registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + + registry.add("Data/FT0IsInnerOrOuter", "FT0IsInnerOrOuter", {HistType::kTH1D, {{FT0InnerOrOuter::NFT0InnerOrOuterSteps, -0.5, +FT0InnerOrOuter::NFT0InnerOrOuterSteps - 0.5}}}); + std::array labels{}; + labels[FT0InnerOrOuter::FT0InnerRing] = "FT0InnerRing"; + labels[FT0InnerOrOuter::FT0OuterRing] = "FT0OuterRing"; + registry.get(HIST("Data/FT0IsInnerOrOuter"))->SetMinimum(0); + + for (int iBin = 0; iBin < FT0InnerOrOuter::NFT0InnerOrOuterSteps; iBin++) { + registry.get(HIST("Data/FT0IsInnerOrOuter"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); + } + } + // ========================= // init() // ========================= @@ -581,47 +619,54 @@ struct HfTaskFlow { rctChecker.init(configCollision.setRCTFlagCheckerLabel, configCollision.requireZDCCheck, configCollision.requireRCTFlagCheckerLimitAcceptanceAsBad, true); correlationAnalysisRctChecker.init({kFT0Bad, kITSBad, kTPCBadTracking, kTPCBadPID, kMFTBad, kITSLimAccMCRepr, kMFTLimAccMCRepr, kTPCLimAccMCRepr}); - registry.add("Data/hVtxZ", "v_{z} (cm)", {HistType::kTH1D, {configAxis.axisVertex}}); - registry.add("Data/hNTracks", "", {HistType::kTH1F, {configAxis.axisMultiplicity}}); - registry.add(Form("Data/hMultiplicity_%s", WhatMultiplicityEstimator[configCollision.multiplicityEstimator].data()), "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); - registry.add(Form("Data/hCentrality_%s", WhatCentralityEstimator[configCollision.centralityEstimator].data()), "", {HistType::kTH1D, {configAxis.axisCentrality}}); - - registry.add("Data/hEventCounter", "hEventCounter", {HistType::kTH1D, {{EventSelectionStep::NEventSelectionSteps, -0.5, +EventSelectionStep::NEventSelectionSteps - 0.5}}}); - std::string labels[EventSelectionStep::NEventSelectionSteps]; - labels[EventSelectionStep::AllEvents] = "all"; - labels[EventSelectionStep::AfterEventSelection] = "after Physics selection"; - registry.get(HIST("Data/hEventCounter"))->SetMinimum(0); - - for (int iBin = 0; iBin < EventSelectionStep::NEventSelectionSteps; iBin++) { - registry.get(HIST("Data/hEventCounter"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); - } - - registry.add("Data/hPreciseEventCounter", "hPreciseEventCounter", {HistType::kTH1D, {{SpecificEventSelectionStep::NSpecificEventSelectionSteps, -0.5, +SpecificEventSelectionStep::NSpecificEventSelectionSteps - 0.5}}}); - std::string labelsPreciseEventSelection[SpecificEventSelectionStep::NSpecificEventSelectionSteps]; - labelsPreciseEventSelection[SpecificEventSelectionStep::AllEventsPrecise] = "all"; - labelsPreciseEventSelection[SpecificEventSelectionStep::IsSel8] = "sel8"; - labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoSameBunchPileup] = "IsNoSameBunchPileup"; - labelsPreciseEventSelection[SpecificEventSelectionStep::IsGoodItsLayersAll] = "IsGoodItsLayersAll"; - labelsPreciseEventSelection[SpecificEventSelectionStep::IsGoodZvtxFT0vsPV] = "IsGoodZvtxFT0vsPV"; - labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoCollInRofStandard] = "IsNoCollInRofStandard"; - labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoCollInRofStrict] = "IsNoCollInRofStrict"; - labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoCollInTimeRangeStandard] = "IsNoCollInTimeRangeStandard"; - labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoCollInTimeRangeStrict] = "IsNoCollInTimeRangeStrict"; - labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoHighMultCollInPrevRof] = "IsNoHighMultCollInPrevRof"; - labelsPreciseEventSelection[SpecificEventSelectionStep::IsRctFlagChecked] = "IsRctFlagChecked"; - registry.get(HIST("Data/hPreciseEventCounter"))->SetMinimum(0); - - for (int iBin = 0; iBin < SpecificEventSelectionStep::NSpecificEventSelectionSteps; iBin++) { - registry.get(HIST("Data/hPreciseEventCounter"))->GetXaxis()->SetBinLabel(iBin + 1, labelsPreciseEventSelection[iBin].data()); - } - - mPairCuts.SetHistogramRegistry(®istry); - if (configCentral.pairCut->get("Photon") > 0 || configCentral.pairCut->get("K0") > 0 || configCentral.pairCut->get("Lambda") > 0 || configCentral.pairCut->get("Phi") > 0 || configCentral.pairCut->get("Rho") > 0) { - mPairCuts.SetPairCut(PairCuts::Photon, configCentral.pairCut->get("Photon")); - mPairCuts.SetPairCut(PairCuts::K0, configCentral.pairCut->get("K0")); - mPairCuts.SetPairCut(PairCuts::Lambda, configCentral.pairCut->get("Lambda")); - mPairCuts.SetPairCut(PairCuts::Phi, configCentral.pairCut->get("Phi")); - mPairCuts.SetPairCut(PairCuts::Rho, configCentral.pairCut->get("Rho")); + if (!configTask.removeQAForSystematics) { + registry.add("Data/hVtxZ", "v_{z} (cm)", {HistType::kTH1D, {configAxis.axisVertex}}); + registry.add("Data/hNTracks", "", {HistType::kTH1F, {configAxis.axisMultiplicity}}); + registry.add(Form("Data/hMultiplicity_%s", WhatMultiplicityEstimator[configCollision.multiplicityEstimator].data()), "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add("Data/hMultiplicityTracksSize", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add("Data/hMultiplicityUsed", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add(Form("Data/hCentrality_%s", WhatCentralityEstimator[configCollision.centralityEstimator].data()), "", {HistType::kTH1D, {configAxis.axisCentrality}}); + registry.add("Data/hCentralityWeighted", "", {HistType::kTH1D, {configAxis.axisCentrality}}); + registry.add("Data/hCentralityUsed", "", {HistType::kTH1D, {configAxis.axisCentrality}}); + + registry.add("Data/hEventCounter", "hEventCounter", {HistType::kTH1D, {{EventSelectionStep::NEventSelectionSteps, -0.5, +EventSelectionStep::NEventSelectionSteps - 0.5}}}); + std::array labels{}; + labels[EventSelectionStep::AllEvents] = "all"; + labels[EventSelectionStep::AfterEventSelection] = "after Physics selection"; + registry.get(HIST("Data/hEventCounter"))->SetMinimum(0); + + for (int iBin = 0; iBin < EventSelectionStep::NEventSelectionSteps; iBin++) { + registry.get(HIST("Data/hEventCounter"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); + } + + registry.add("Data/hPreciseEventCounter", "hPreciseEventCounter", {HistType::kTH1D, {{SpecificEventSelectionStep::NSpecificEventSelectionSteps, -0.5, +SpecificEventSelectionStep::NSpecificEventSelectionSteps - 0.5}}}); + std::array labelsPreciseEventSelection{}; + labelsPreciseEventSelection[SpecificEventSelectionStep::AllEventsPrecise] = "all"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsSel8] = "sel8"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoSameBunchPileup] = "IsNoSameBunchPileup"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsGoodItsLayersAll] = "IsGoodItsLayersAll"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsGoodZvtxFT0vsPV] = "IsGoodZvtxFT0vsPV"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoCollInRofStandard] = "IsNoCollInRofStandard"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoCollInRofStrict] = "IsNoCollInRofStrict"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoCollInTimeRangeStandard] = "IsNoCollInTimeRangeStandard"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoCollInTimeRangeStrict] = "IsNoCollInTimeRangeStrict"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoHighMultCollInPrevRof] = "IsNoHighMultCollInPrevRof"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsRctFlagChecked] = "IsRctFlagChecked"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsFoundFT0] = "IsFoundFT0"; + registry.get(HIST("Data/hPreciseEventCounter"))->SetMinimum(0); + + for (int iBin = 0; iBin < SpecificEventSelectionStep::NSpecificEventSelectionSteps; iBin++) { + registry.get(HIST("Data/hPreciseEventCounter"))->GetXaxis()->SetBinLabel(iBin + 1, labelsPreciseEventSelection[iBin].data()); + } + + mPairCuts.SetHistogramRegistry(®istry); + if (configCentral.pairCut->get("Photon") > 0 || configCentral.pairCut->get("K0") > 0 || configCentral.pairCut->get("Lambda") > 0 || configCentral.pairCut->get("Phi") > 0 || configCentral.pairCut->get("Rho") > 0) { + mPairCuts.SetPairCut(PairCuts::Photon, configCentral.pairCut->get("Photon")); + mPairCuts.SetPairCut(PairCuts::K0, configCentral.pairCut->get("K0")); + mPairCuts.SetPairCut(PairCuts::Lambda, configCentral.pairCut->get("Lambda")); + mPairCuts.SetPairCut(PairCuts::Phi, configCentral.pairCut->get("Phi")); + mPairCuts.SetPairCut(PairCuts::Rho, configCentral.pairCut->get("Rho")); + } } // ========================= @@ -659,7 +704,9 @@ struct HfTaskFlow { // ========================= if (doprocessSameTpcTpcChCh) { - addHistograms(); + if (!configTask.removeQAForSystematics) { + addHistograms(); + } if (!configTask.doEtaDependentFlow) { sameEvent.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxis, effAxis, {})); @@ -671,14 +718,18 @@ struct HfTaskFlow { } if (doprocessSameTpcTpcD0Ch) { - addHistograms(); + if (!configTask.removeQAForSystematics) { + addHistograms(); + } sameEventHf.setObject(new CorrelationContainer("sameEventHf", "sameEventHf", corrAxis, effAxis, hfUserAxis)); mixedEventHf.setObject(new CorrelationContainer("mixedEventHf", "mixedEventHf", corrAxis, effAxis, hfUserAxis)); } if (doprocessSameTpcTpcLcCh) { - addHistograms(); + if (!configTask.removeQAForSystematics) { + addHistograms(); + } sameEventHf.setObject(new CorrelationContainer("sameEventHf", "sameEventHf", corrAxis, effAxis, hfUserAxis)); mixedEventHf.setObject(new CorrelationContainer("mixedEventHf", "mixedEventHf", corrAxis, effAxis, hfUserAxis)); @@ -688,13 +739,14 @@ struct HfTaskFlow { // Initialization of histograms and CorrelationContainers for TpcMft cases // ========================= - if (doprocessSameTpcMftChCh || doprocessSameTpcMftChChReassociated || doprocessSameTpcMftChChReassociated3d || doprocessSameTpcMftChChNonAmbiguous) { - addHistograms(); - addMftHistograms(); - registry.add("Data/hEfficiencyTrigger", "", {HistType::kTH3D, {{configAxis.axisPtTrigger}, {configAxis.axisEtaTrigger}, {configAxis.axisVertex}}}); - registry.add("Data/hEfficiencyAssociated", "", {HistType::kTH3D, {{configAxis.axisPtAssoc}, {configAxis.axisEtaAssociated}, {configAxis.axisVertex}}}); + // if (doprocessSameTpcMftChCh || doprocessSameTpcMftChChReassociated || doprocessSameTpcMftChChReassociated3d || doprocessSameTpcMftChChNonAmbiguous || doprocessSameTpcMftChChReassociated3dMcReconstructed) { + if (doprocessSameTpcMftChCh || doprocessSameTpcMftChChReassociated3d || doprocessSameTpcMftChChNonAmbiguous || doprocessSameTpcMftChChReassociated3dMcReconstructed) { - registry.add("Data/hMultiplicity_uncorrected_vs_corrected", "", {HistType::kTH2D, {{configAxis.axisMultiplicity}, {configAxis.axisMultiplicity}}}); + if (!configTask.removeQAForSystematics) { + addHistograms(); + addMftHistograms(); + registry.add("Data/hMultiplicity_uncorrected_vs_corrected", "", {HistType::kTH2D, {{configAxis.axisMultiplicity}, {configAxis.axisMultiplicity}}}); + } if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { registry.add("Trig_hist_TPC_MFT", "", {HistType::kTHnSparseF, {{configAxis.axisSamples, configAxis.axisVertex, configAxis.axisPtTrigger}}}); @@ -711,17 +763,24 @@ struct HfTaskFlow { } } - if (doprocessSameTpcMftD0Ch || doprocessSameTpcMftD0ChReassociated) { - addHistograms(); - addMftHistograms(); + // if (doprocessSameTpcMftD0Ch || doprocessSameTpcMftD0ChReassociated) { + if (doprocessSameTpcMftD0Ch) { + if (!configTask.removeQAForSystematics) { + addHistograms(); + addMftHistograms(); + } sameEventHf.setObject(new CorrelationContainer("sameEventHf", "sameEventHf", corrAxis, effAxis, hfUserAxis)); mixedEventHf.setObject(new CorrelationContainer("mixedEventHf", "mixedEventHf", corrAxis, effAxis, hfUserAxis)); } - if (doprocessSameTpcMftLcCh || doprocessSameTpcMftLcChReassociated) { - addHistograms(); - addMftHistograms(); + // if (doprocessSameTpcMftLcCh || doprocessSameTpcMftLcChReassociated) { + if (doprocessSameTpcMftLcCh) { + + if (!configTask.removeQAForSystematics) { + addHistograms(); + addMftHistograms(); + } sameEventHf.setObject(new CorrelationContainer("sameEventHf", "sameEventHf", corrAxis, effAxis, hfUserAxis)); mixedEventHf.setObject(new CorrelationContainer("mixedEventHf", "mixedEventHf", corrAxis, effAxis, hfUserAxis)); @@ -780,9 +839,10 @@ struct HfTaskFlow { // ========================= if (doprocessSameTpcFt0aChCh) { - addHistograms(); - registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); - registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + if (!configTask.removeQAForSystematics) { + addHistograms(); + addFt0Histograms(); + } if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { registry.add("Trig_hist_TPC_FT0A", "", {HistType::kTHnSparseF, {{configAxis.axisSamples, configAxis.axisVertex, configAxis.axisPtTrigger}}}); @@ -800,18 +860,24 @@ struct HfTaskFlow { } if (doprocessSameTpcFt0aD0Ch) { - addHistograms(); - registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); - registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + + if (!configTask.removeQAForSystematics) { + addHistograms(); + registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + } sameEventHf.setObject(new CorrelationContainer("sameEventHf", "sameEventHf", corrAxis, effAxis, hfUserAxis)); mixedEventHf.setObject(new CorrelationContainer("mixedEventHf", "mixedEventHf", corrAxis, effAxis, hfUserAxis)); } if (doprocessSameTpcFt0aLcCh) { - addHistograms(); - registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); - registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + + if (!configTask.removeQAForSystematics) { + addHistograms(); + registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + } sameEventHf.setObject(new CorrelationContainer("sameEventHf", "sameEventHf", corrAxis, effAxis, hfUserAxis)); mixedEventHf.setObject(new CorrelationContainer("mixedEventHf", "mixedEventHf", corrAxis, effAxis, hfUserAxis)); @@ -821,11 +887,14 @@ struct HfTaskFlow { // Initialization of histograms and CorrelationContainers for MftFt0a cases // ========================= - if (doprocessSameMftFt0aChCh || doprocessSameMftFt0aChChReassociated || doprocessSameMftFt0aChChReassociated3d || doprocessSameMftFt0aChChNonAmbiguous) { - addHistograms(); - addMftHistograms(); - registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); - registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + // if (doprocessSameMftFt0aChCh || doprocessSameMftFt0aChChReassociated || doprocessSameMftFt0aChChReassociated3d || doprocessSameMftFt0aChChNonAmbiguous) { + if (doprocessSameMftFt0aChCh || doprocessSameMftFt0aChChReassociated3d || doprocessSameMftFt0aChChNonAmbiguous || doprocessSameMftFt0aChChReassociated3dMcReconstructed) { + + if (!configTask.removeQAForSystematics) { + addHistograms(); + addMftHistograms(); + addFt0Histograms(); + } if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { registry.add("Trig_hist_MFT_FT0A", "", {HistType::kTHnSparseF, {{configAxis.axisSamples, configAxis.axisVertex, configAxis.axisPtTrigger}}}); @@ -847,9 +916,11 @@ struct HfTaskFlow { // ========================= if (doprocessSameTpcFt0cChCh) { - addHistograms(); - registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); - registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + + if (!configTask.removeQAForSystematics) { + addHistograms(); + addFt0Histograms(); + } if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { registry.add("Trig_hist_TPC_FT0C", "", {HistType::kTHnSparseF, {{configAxis.axisSamples, configAxis.axisVertex, configAxis.axisPtTrigger}}}); @@ -867,18 +938,24 @@ struct HfTaskFlow { } if (doprocessSameTpcFt0cD0Ch) { - addHistograms(); - registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); - registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + + if (!configTask.removeQAForSystematics) { + addHistograms(); + registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + } sameEventHf.setObject(new CorrelationContainer("sameEventHf", "sameEventHf", corrAxis, effAxis, hfUserAxis)); mixedEventHf.setObject(new CorrelationContainer("mixedEventHf", "mixedEventHf", corrAxis, effAxis, hfUserAxis)); } if (doprocessSameTpcFt0cLcCh) { - addHistograms(); - registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); - registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + + if (!configTask.removeQAForSystematics) { + addHistograms(); + registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + } sameEventHf.setObject(new CorrelationContainer("sameEventHf", "sameEventHf", corrAxis, effAxis, hfUserAxis)); mixedEventHf.setObject(new CorrelationContainer("mixedEventHf", "mixedEventHf", corrAxis, effAxis, hfUserAxis)); @@ -889,9 +966,13 @@ struct HfTaskFlow { // ========================= if (doprocessSameFt0aFt0cChCh) { - addHistograms(); - registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); - registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + + if (!configTask.removeQAForSystematics) { + addHistograms(); + addFt0Histograms(); + registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + } if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { registry.add("Trig_hist_FT0A_FT0C", "", {HistType::kTHnSparseF, {{configAxis.axisSamples, configAxis.axisVertex, configAxis.axisPtTrigger}}}); @@ -914,8 +995,22 @@ struct HfTaskFlow { if (doprocessSameMcGen) { - registry.add("MC/hEfficiencyTrigger", "", {HistType::kTH3D, {{configAxis.axisPtTrigger}, {configAxis.axisEtaTrigger}, {configAxis.axisVertex}}}); - registry.add("MC/hEfficiencyAssociated", "", {HistType::kTH3D, {{configAxis.axisPtAssoc}, {configAxis.axisEtaAssociated}, {configAxis.axisVertex}}}); + registry.add(Form("MC/hCentrality_%s", WhatCentralityEstimator[configCollision.centralityEstimator].data()), "", {HistType::kTH1D, {configAxis.axisCentrality}}); + registry.add("MC/hCentralityUsed", "", {HistType::kTH1D, {configAxis.axisCentrality}}); + registry.add("MC/hMultiplicity_multMCFT0C", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add("MC/hMultiplicityTracksSize", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add("MC/hMultiplicityUsed", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + + registry.add("MC/hMixedMultiplicity_multMCFT0C_coll1", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add("MC/hMixedMultiplicityTracksSize_coll1", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add("MC/hMixedMultiplicityUsed_coll1", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add("MC/hMixedCentrality_coll1", "", {HistType::kTH1D, {configAxis.axisCentrality}}); + registry.add("MC/hMixedCentralityUsed_coll1", "", {HistType::kTH1D, {configAxis.axisCentrality}}); + registry.add("MC/hMixedMultiplicity_multMCFT0C_coll2", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add("MC/hMixedMultiplicityTracksSize_coll2", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add("MC/hMixedMultiplicityUsed_coll2", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add("MC/hMixedCentrality_coll2", "", {HistType::kTH1D, {configAxis.axisCentrality}}); + registry.add("MC/hMixedCentralityUsed_coll2", "", {HistType::kTH1D, {configAxis.axisCentrality}}); if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcTpc)) { addHistograms(); @@ -951,12 +1046,15 @@ struct HfTaskFlow { } if (doprocessTrackEfficiencies) { - registry.add("MC/hEfficiencyTrigger", "", {HistType::kTH3D, {{configAxis.axisPtTrigger}, {configAxis.axisEtaTrigger}, {configAxis.axisVertex}}}); - registry.add("MC/hEfficiencyAssociated", "", {HistType::kTH3D, {{configAxis.axisPtTrigger}, {configAxis.axisEtaAssociated}, {configAxis.axisVertex}}}); - registry.add("Data/hEfficiencyTrigger", "", {HistType::kTH3D, {{configAxis.axisPtTrigger}, {configAxis.axisEtaTrigger}, {configAxis.axisVertex}}}); - registry.add("Data/hEfficiencyAssociated", "", {HistType::kTH3D, {{configAxis.axisPtAssoc}, {configAxis.axisEtaAssociated}, {configAxis.axisVertex}}}); + if (!configTask.removeQAForSystematics) { + registry.add("MC/hEfficiencyTrigger", "", {HistType::kTH3D, {{configAxis.axisPtTrigger}, {configAxis.axisEtaTrigger}, {configAxis.axisVertex}}}); + registry.add("MC/hEfficiencyAssociated", "", {HistType::kTH3D, {{configAxis.axisPtTrigger}, {configAxis.axisEtaAssociated}, {configAxis.axisVertex}}}); + registry.add("Data/hEfficiencyTrigger", "", {HistType::kTH3D, {{configAxis.axisPtTrigger}, {configAxis.axisEtaTrigger}, {configAxis.axisVertex}}}); + registry.add("Data/hEfficiencyAssociated", "", {HistType::kTH3D, {{configAxis.axisPtAssoc}, {configAxis.axisEtaAssociated}, {configAxis.axisVertex}}}); + } } + registry.print(); } // End of init() function // ========================= @@ -1004,22 +1102,22 @@ struct HfTaskFlow { { switch (configCollision.multiplicityEstimator) { case MultiplicityEstimators::MultNTracksPV: - if (isSameEvent) { + if (isSameEvent && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hMultiplicity_multNTracksPV"), collision.multNTracksPV()); } return collision.multNTracksPV(); case MultiplicityEstimators::MultNumContrib: - if (isSameEvent) { + if (isSameEvent && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hMultiplicity_multNumContrib"), collision.numContrib()); } return collision.numContrib(); case MultiplicityEstimators::MultFT0C: - if (isSameEvent) { + if (isSameEvent && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hMultiplicity_multFT0C"), collision.multFT0C()); } return collision.multFT0C(); case MultiplicityEstimators::MultFT0M: - if (isSameEvent) { + if (isSameEvent && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hMultiplicity_multFT0M"), collision.multFT0M()); } return collision.multFT0M(); @@ -1028,23 +1126,23 @@ struct HfTaskFlow { } } - template + template float getCentralityEstimator(TCollision const& collision, bool isSameEvent) { switch (configCollision.centralityEstimator) { case CentralityEstimators::CentFT0C: - if (isSameEvent) { - registry.fill(HIST("Data/hCentrality_centFT0C"), collision.centFT0C()); + if (isSameEvent && !configTask.removeQAForSystematics) { + registry.fill(HIST(WhatDataType[DataType]) + HIST("hCentrality_centFT0C"), collision.centFT0C()); } return collision.centFT0C(); case CentralityEstimators::CentFT0CVariant1: - if (isSameEvent) { - registry.fill(HIST("Data/hCentrality_centFT0CVariant1"), collision.centFT0CVariant1()); + if (isSameEvent && !configTask.removeQAForSystematics) { + registry.fill(HIST(WhatDataType[DataType]) + HIST("hCentrality_centFT0CVariant1"), collision.centFT0CVariant1()); } return collision.centFT0CVariant1(); case CentralityEstimators::CentFT0M: - if (isSameEvent) { - registry.fill(HIST("Data/hCentrality_centFT0M"), collision.centFT0M()); + if (isSameEvent && !configTask.removeQAForSystematics) { + registry.fill(HIST(WhatDataType[DataType]) + HIST("hCentrality_centFT0M"), collision.centFT0M()); } return collision.centFT0M(); default: @@ -1184,15 +1282,19 @@ struct HfTaskFlow { } else { LOGF(fatal, "Cor Index %d out of range", fitType); } - registry.fill(HIST("Data/FT0Amp"), rID, amplitude); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/FT0Amp"), rID, amplitude); + } amplitude = amplitude / cstFT0RelGain[id]; - registry.fill(HIST("Data/FT0AmpCorr"), rID, amplitude); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/FT0AmpCorr"), rID, amplitude); + } } void loadGain(aod::BCsWithTimestamps::iterator const& bc) { cstFT0RelGain.clear(); - cstFT0RelGain = {}; + // cstFT0RelGain = {}; std::string fullPath; auto timestamp = bc.timestamp(); @@ -1215,33 +1317,69 @@ struct HfTaskFlow { } } + int isInnerOrOuter(int channelId, int fitType) + { + if (fitType == isFT0C) { + if (channelId >= FT0DetectorChannels::FT0CInnerRingMin && channelId <= FT0DetectorChannels::FT0CInnerRingMax) { + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/FT0IsInnerOrOuter"), FT0InnerOrOuter::FT0InnerRing); + } + return FT0InnerOrOuter::FT0InnerRing; + } else if (channelId >= FT0DetectorChannels::FT0COuterRingMin && channelId <= FT0DetectorChannels::FT0COuterRingMax) { + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/FT0IsInnerOrOuter"), FT0InnerOrOuter::FT0OuterRing); + } + return FT0InnerOrOuter::FT0OuterRing; + } + LOGF(fatal, "Channel ID %d out of range for FT0C", channelId); + return FT0InnerOrOuter::FT0Error; + } else if (fitType == isFT0A) { + if (channelId >= FT0DetectorChannels::FT0AInnerRingMin && channelId <= FT0DetectorChannels::FT0AInnerRingMax) { + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/FT0IsInnerOrOuter"), FT0InnerOrOuter::FT0InnerRing); + } + return FT0InnerOrOuter::FT0InnerRing; + } else if (channelId >= FT0DetectorChannels::FT0AOuterRingMin && channelId <= FT0DetectorChannels::FT0AOuterRingMax) { + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/FT0IsInnerOrOuter"), FT0InnerOrOuter::FT0OuterRing); + } + return FT0InnerOrOuter::FT0OuterRing; + } + LOGF(fatal, "Channel ID %d out of range for FT0A", channelId); + return FT0InnerOrOuter::FT0Error; + } else { + LOGF(fatal, "Fit type %d is not valid for FT0", fitType); + return FT0InnerOrOuter::FT0Error; + } + } + void loadEfficiencyCorrection(uint64_t timestamp) { if (areCorrectionsLoaded) { return; } - if (configTask.loadEfficienciesForTpc.value.empty() == false) { + if (!configTask.loadEfficienciesForTpc.value.empty()) { mEfficiencyTpc = ccdb->getForTimeStamp(configTask.loadEfficienciesForTpc, timestamp); if (mEfficiencyTpc == nullptr) { LOGF(fatal, "Could not load efficiency histogram for TPC tracks from %s", configTask.loadEfficienciesForTpc.value.c_str()); } LOGF(info, "Loaded efficiency histogram from %s (%p)", configTask.loadEfficienciesForTpc.value.c_str(), static_cast(mEfficiencyTpc)); } - if (configTask.loadEfficienciesForMft.value.empty() == false) { + if (!configTask.loadEfficienciesForMft.value.empty()) { mEfficiencyMft = ccdb->getForTimeStamp(configTask.loadEfficienciesForMft, timestamp); if (mEfficiencyMft == nullptr) { LOGF(fatal, "Could not load efficiency histogram for MFT tracks from %s", configTask.loadEfficienciesForMft.value.c_str()); } LOGF(info, "Loaded efficiency histogram from %s (%p)", configTask.loadEfficienciesForMft.value.c_str(), static_cast(mEfficiencyMft)); } - if (configTask.loadEfficienciesForNch.value.empty() == false) { + if (!configTask.loadEfficienciesForNch.value.empty()) { mEfficiencyNch = ccdb->getForTimeStamp(configTask.loadEfficienciesForNch, timestamp); if (!mEfficiencyNch) { LOGF(fatal, "Could not load efficiency histogram for Nch estimator from %s", configTask.loadEfficienciesForNch.value.c_str()); } LOGF(info, "Loaded efficiency histogram from %s (%p)", configTask.loadEfficienciesForNch.value.c_str(), static_cast(mEfficiencyNch)); } - if (configTask.loadCentralityWeight.value.empty() == false) { + if (!configTask.loadCentralityWeight.value.empty()) { mCentralityWeight = ccdb->getForTimeStamp(configTask.loadCentralityWeight, timestamp); if (mCentralityWeight == nullptr) { LOGF(fatal, "Could not load centrality weight correction from %s", configTask.loadCentralityWeight.value.c_str()); @@ -1292,14 +1430,14 @@ struct HfTaskFlow { } template - double getCorrectedMultiplicity(const TTracks& tracks) // function to count the number of tracks in the event and fill the histogram + float getCorrectedMultiplicity(const TTracks& tracks) // function to count the number of tracks in the event and fill the histogram { - auto trackCounter = 0; + float trackCounter = 0.f; auto weightMultiplicity = 1.0f; for (auto const& track : tracks) { - if (std::abs(track.eta()) < configCentral.etaCentralTrackMax && track.pt() >= configCentral.ptCentralTrackMin && track.pt() <= configCentral.ptCentralTrackMax) { - trackCounter += 1; + if (std::abs(track.eta()) > configCentral.etaCentralTrackMax || track.pt() <= configCentral.ptCentralTrackMin || track.pt() >= configCentral.ptCentralTrackMax) { + continue; } auto efficiencyNch = 1.0f; @@ -1311,12 +1449,13 @@ struct HfTaskFlow { } if (efficiencyNch == 0) { - return false; + // return false; + efficiencyNch = 1.0f; } weightMultiplicity = 1. / efficiencyNch; } else { - weightMultiplicity = 0; + weightMultiplicity = 1.0f; } trackCounter += weightMultiplicity; @@ -1335,6 +1474,11 @@ struct HfTaskFlow { if (weight == 0) { return false; } + + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hCentralityWeighted"), centrality, weight); + } + weightCent = weight; return true; } @@ -1343,11 +1487,44 @@ struct HfTaskFlow { // Cuts with functions // ========================= + template + bool isAcceptedEventActivity(TCollision const& collision, TTracks const& tracks, auto& multiplicity, float& centrality, float& centralityWeight) + { + if (configCollision.useMultiplicityFromTracks) { + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hMultiplicityTracksSize"), tracks.size()); + } + multiplicity = tracks.size(); + } else { + multiplicity = getMultiplicityEstimator(collision, true); + } + + centrality = getCentralityEstimator(collision, true); + if (configCollision.useCentrality) { + getCentralityWeight(centralityWeight, centrality); + if (centrality < configCollision.minCentrality || centrality >= configCollision.maxCentrality) { + return false; + } + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hCentralityUsed"), centrality); + } + } else { + if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { + return false; + } + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hMultiplicityUsed"), multiplicity); + } + } + + return true; + } + // FIXME: Some collisions are rejected here, what causes (part of) differences with the D0 task template bool isAcceptedCollision(TCollision const& collision, bool fillHistograms = false) { - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hEventCounter"), EventSelectionStep::AllEvents); registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::AllEventsPrecise); } @@ -1355,55 +1532,55 @@ struct HfTaskFlow { if (!collision.sel8()) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsSel8); } if (configCollision.isApplySameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsNoSameBunchPileup); } if (configCollision.isApplyGoodItsLayersAll && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsGoodItsLayersAll); } if (configCollision.isApplyGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsGoodZvtxFT0vsPV); } if (configCollision.isApplyNoCollInRofStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsNoCollInRofStandard); } if (configCollision.isApplyNoCollInRofStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStrict)) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsNoCollInRofStrict); } if (configCollision.isApplyNoCollInTimeRangeStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsNoCollInTimeRangeStandard); } if (configCollision.isApplyNoCollInTimeRangeStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStrict)) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsNoCollInTimeRangeStrict); } if (configCollision.isApplyNoHighMultCollInPrevRof && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsNoHighMultCollInPrevRof); } if (configCollision.requireRCTFlagChecker && !rctChecker(collision)) { @@ -1412,12 +1589,20 @@ struct HfTaskFlow { if (configCollision.requireCorrelationAnalysisRCTFlagChecker && !correlationAnalysisRctChecker(collision)) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsRctFlagChecked); - registry.fill(HIST("Data/hEventCounter"), EventSelectionStep::AfterEventSelection); + } + if (configCollision.useNoFoundFT0Cut && !collision.has_foundFT0()) { + return false; + } + if (fillHistograms && !configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsFoundFT0); } - registry.fill(HIST("Data/hVtxZ"), collision.posZ()); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hEventCounter"), EventSelectionStep::AfterEventSelection); + registry.fill(HIST("Data/hVtxZ"), collision.posZ()); + } return true; } @@ -1479,7 +1664,7 @@ struct HfTaskFlow { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::Eta); } @@ -1488,7 +1673,7 @@ struct HfTaskFlow { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::Cluster); } @@ -1497,7 +1682,8 @@ struct HfTaskFlow { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { + registry.fill(HIST("Data/Mft/hPtMft"), mftTrack.pt()); registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::Pt); } @@ -1505,14 +1691,14 @@ struct HfTaskFlow { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::DCAxy); } if (configMft.cutOnDcaZ && std::abs(dcaZ) > configMft.mftMaxDCAz) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hDcaXYMft"), dcaXY); registry.fill(HIST("Data/Mft/hDcaZMft"), dcaZ); registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::DCAz); @@ -1525,7 +1711,8 @@ struct HfTaskFlow { return false; } } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { + registry.fill(HIST("Data/Mft/hChi2OverNdfMft"), mftTrack.chi2() / std::max(2.0f * mftTrack.nClusters() - 5.0f, 1.0f)); registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::Chi2OverNdf); } @@ -1537,13 +1724,13 @@ struct HfTaskFlow { return false; } } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::Phi); } // cut on the track algorithm of MFT tracks if (mftTrack.isCA()) { - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::IsCA); registry.fill(HIST("Data/Mft/hPtVsClustersCA"), mftTrack.pt(), mftTrack.nClusters()); } @@ -1553,7 +1740,7 @@ struct HfTaskFlow { } } else { - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::IsLTF); registry.fill(HIST("Data/Mft/hPtVsClustersLTF"), mftTrack.pt(), mftTrack.nClusters()); } @@ -1571,13 +1758,13 @@ struct HfTaskFlow { bool isAmbiguousMftTrack(TTrack const& mftTrack, bool fillHistograms) { if (mftTrack.ambDegree() > 1) { - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::NumberOfAmbiguousTracks); } return true; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::NumberOfNonAmbiguousTracks); } return false; @@ -1601,7 +1788,7 @@ struct HfTaskFlow { auto triggerWeight = 1.0f; auto associatedWeight = 1.0f; auto loopCounter = 0; // To avoid filling associated tracks QA many times, I fill it only for the first trigger track of the collision - int sampleIndex = gRandom->Uniform(0, configTask.nSamples); + int sampleIndex = static_cast(gRandom->Uniform(0, configTask.nSamples)); // TRIGGER PARTICLE for (const auto& track1 : tracksTrigger) { @@ -1655,7 +1842,7 @@ struct HfTaskFlow { } // FILL QA PLOTS for trigger particle - if (isSameEvent && (step == CorrelationContainer::kCFStepReconstructed)) { + if (isSameEvent && (step == CorrelationContainer::kCFStepReconstructed) && !configTask.removeQAForSystematics) { if constexpr (!std::is_same_v) { // IF TPC-TPC case if constexpr (std::is_same_v) { // IF D0 CASE -> TPC-TPC D0-h fillTriggerQa(multiplicity, eta1, phi1, pt1); @@ -1688,7 +1875,9 @@ struct HfTaskFlow { if constexpr (std::is_same_v) { if (isSameEvent && loopCounter == 1) { // To avoid double counting, we fill the plots only the first time - registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::NoSelection); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::NoSelection); + } if (!isAcceptedMftTrack(track2, 0.f, 0.f, true)) { continue; @@ -1731,19 +1920,19 @@ struct HfTaskFlow { const double kLimit = 3.0 * configCentral.mergingCut; - bool bIsBelow = false; + [[maybe_unused]] bool bIsBelow = false; if (std::abs(dPhiStarLow) < kLimit || std::abs(dPhiStarHigh) < kLimit || dPhiStarLow * dPhiStarHigh < 0) { - for (double rad(configCentral.minMergingRadius); rad < configCentral.maxMergingRadius; rad += 0.01) { // FIXME: Variable 'rad' with floating point type 'double' should not be used as a loop counter. + constexpr double StepRad = 0.01; + const int nSteps = static_cast((configCentral.maxMergingRadius - configCentral.minMergingRadius) / StepRad); + for (int iStep = 1; iStep < nSteps; ++iStep) { + double rad = configCentral.minMergingRadius + iStep * StepRad; double dPhiStar = getDPhiStar(track1, track2, rad, magneticField); if (std::abs(dPhiStar) < kLimit) { bIsBelow = true; break; } } - if (bIsBelow) { - continue; - } } } } @@ -1803,7 +1992,7 @@ struct HfTaskFlow { } // FILL QA PLOTS for associated particle - if (isSameEvent && (loopCounter == 1) && (step == CorrelationContainer::kCFStepReconstructed)) { + if (isSameEvent && (loopCounter == 1) && (step == CorrelationContainer::kCFStepReconstructed) && !configTask.removeQAForSystematics) { if constexpr (!std::is_same_v) { // IF TPC-TPC case if constexpr (std::is_same_v) { // IF D0 CASE -> TPC-TPC D0-h fillAssociatedQa(multiplicity, eta2, phi2); @@ -1815,13 +2004,10 @@ struct HfTaskFlow { } else { // IF TPC-MFT case if constexpr (std::is_same_v) { // IF D0 CASE -> TPC-MFT D0-h fillAssociatedQa(multiplicity, eta2, phi2); - registry.fill(HIST("Data/Mft/hPtMft"), pt2); } else if constexpr (std::is_same_v) { // IF LC CASE -> TPC-MFT Lc-h fillAssociatedQa(multiplicity, eta2, phi2); - registry.fill(HIST("Data/Mft/hPtMft"), pt2); } else { // IF NEITHER D0 NOR LC -> TPC-MFT h-h fillAssociatedQa(multiplicity, eta2, phi2); - registry.fill(HIST("Data/Mft/hPtMft"), pt2); } // end of if condition for TPC-TPC or TPC-MFT case } } @@ -1830,7 +2016,7 @@ struct HfTaskFlow { } // end of loop over tracks 1 } - template + template void fillCorrelationsReassociatedMftTracks(TTarget target, CorrelationContainer::CFStep step, TTracksTrig const& tracksTrigger, TTracksAssoc const& tracksAssoc, float multiplicity, float posZ, bool isSameEvent, bool cutAmbiguousTracks, float centralityWeight) @@ -1838,7 +2024,7 @@ struct HfTaskFlow { auto triggerWeight = 1.0f; auto associatedWeight = 1.0f; auto loopCounter = 0; // To avoid filling associated tracks QA many times, I fill it only for the first trigger track of the collision - int sampleIndex = gRandom->Uniform(0, configTask.nSamples); + int sampleIndex = static_cast(gRandom->Uniform(0, configTask.nSamples)); // TRIGGER PARTICLE for (const auto& track1 : tracksTrigger) { @@ -1888,14 +2074,13 @@ struct HfTaskFlow { } // FILL QA PLOTS for trigger particle - if (isSameEvent) { + if (isSameEvent && !configTask.removeQAForSystematics) { if constexpr (std::is_same_v) { fillTriggerQa(multiplicity, eta1, phi1, pt1); } else if constexpr (std::is_same_v) { fillTriggerQa(multiplicity, eta1, phi1, pt1); } else { fillTriggerQa(multiplicity, eta1, phi1, pt1); - registry.fill(HIST("Data/hEfficiencyTrigger"), pt1, eta1, posZ); } } @@ -1903,14 +2088,24 @@ struct HfTaskFlow { for (const auto& track2 : tracksAssoc) { // Fill QA plot for all MFT tracks () (only if cutAmbiguousTracks is false to avoid double counting) - if (!cutAmbiguousTracks && isSameEvent && (loopCounter == 1)) { + if (!cutAmbiguousTracks && isSameEvent && (loopCounter == 1) && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::AllMftTracks); } - auto reassociatedMftTrack = track2.template mfttrack_as(); + auto reassociatedMftTrack = track2.template mfttrack_as(); + auto reassociatedMftTrackDcaXY = 0.f; auto reassociatedMftTrackDcaZ = 0.f; + if constexpr (std::is_same_v>, TTracksAssoc>) { + if (!reassociatedMftTrack.has_mcParticle()) { + continue; + } + if (configTask.useOnlyPrimaryInMc && !reassociatedMftTrack.mcParticle().isPhysicalPrimary()) { + continue; + } + } + if constexpr (std::is_same_v, TTracksAssoc>) { reassociatedMftTrackDcaXY = track2.bestDCAXY(); } @@ -1920,7 +2115,10 @@ struct HfTaskFlow { } if (isSameEvent && loopCounter == 1) { // To avoid double counting, we fill the plots only the first time - registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::NoSelection); + + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::NoSelection); + } if (!isAcceptedMftTrack(reassociatedMftTrack, reassociatedMftTrackDcaXY, reassociatedMftTrackDcaZ, true)) { continue; @@ -1932,7 +2130,7 @@ struct HfTaskFlow { } // Fill QA plot for MFT tracks after physical selection (eta + clusters) - if (!cutAmbiguousTracks && isSameEvent && (loopCounter == 1)) { + if (!cutAmbiguousTracks && isSameEvent && (loopCounter == 1) && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::AfterTrackSelection); } @@ -1940,7 +2138,7 @@ struct HfTaskFlow { // Fill plots only if cutAmbiguousTracks is false (to avoid double counting) if (isAmbiguousMftTrack(track2, (!cutAmbiguousTracks && isSameEvent && (loopCounter == 1)))) { // If the MFT track is ambiguous we may cut or not on the ambiguous track - if (isSameEvent && (loopCounter == 1)) { + if (isSameEvent && (loopCounter == 1) && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hReassociationMftTracks"), ReassociationMftTracks::NotReassociatedMftTracks); } if (cutAmbiguousTracks) { @@ -1949,7 +2147,7 @@ struct HfTaskFlow { } if (reassociatedMftTrack.collisionId() != track2.bestCollisionId()) { - if (isSameEvent && (loopCounter == 1)) { + if (isSameEvent && (loopCounter == 1) && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hReassociationMftTracks"), ReassociationMftTracks::ReassociatedMftTracks); } } @@ -2012,17 +2210,13 @@ struct HfTaskFlow { } // FILL QA PLOTS for associated particle - if (isSameEvent && (loopCounter == 1)) { + if (isSameEvent && (loopCounter == 1) && !configTask.removeQAForSystematics) { if constexpr (std::is_same_v) { fillAssociatedQa(multiplicity, eta2, phi2); - registry.fill(HIST("Data/Mft/hPtMft"), pt2); } else if constexpr (std::is_same_v) { fillAssociatedQa(multiplicity, eta2, phi2); - registry.fill(HIST("Data/Mft/hPtMft"), pt2); } else { fillAssociatedQa(multiplicity, eta2, phi2); - registry.fill(HIST("Data/Mft/hPtMft"), pt2); - registry.fill(HIST("Data/hEfficiencyAssociated"), pt2, eta2, posZ); } } // end of fill QA } // end of loop over tracks2 @@ -2037,7 +2231,7 @@ struct HfTaskFlow { auto triggerWeight = 1.0f; auto associatedWeight = 1.0f; auto loopCounter = 0; // To avoid filling associated tracks QA many times, I fill it only for the first trigger track of the collision - int sampleIndex = gRandom->Uniform(0, configTask.nSamples); + int sampleIndex = static_cast(gRandom->Uniform(0, configTask.nSamples)); // TRIGGER PARTICLE for (auto const& track1 : tracksTrigger) { @@ -2049,7 +2243,10 @@ struct HfTaskFlow { } } else if constexpr (std::is_same_v) { if (isSameEvent && loopCounter == 1) { // To avoid double counting, we fill the plots only the first time - registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::NoSelection); + + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::NoSelection); + } if (!isAcceptedMftTrack(track1, 0.f, 0.f, true)) { continue; @@ -2130,7 +2327,7 @@ struct HfTaskFlow { } // FILL QA PLOTS for trigger particle - if (isSameEvent && (step == CorrelationContainer::kCFStepReconstructed)) { + if (isSameEvent && (step == CorrelationContainer::kCFStepReconstructed) && !configTask.removeQAForSystematics) { if constexpr (!std::is_same_v) { // If not FilteredMftTracks as trigger -> TPC-FV0a correlations if constexpr (std::is_same_v) { // IF D0 CASE -> TPC-FV0a D0-h if constexpr (std::is_same_v) { // IF NEITHER D0 NOR LC -> @@ -2243,8 +2440,13 @@ struct HfTaskFlow { target->getPairHist()->Fill(step, eta1 - eta2, 0.5, pt1, multiplicity, deltaPhi, posZ, amplitude * centralityWeight * triggerWeight * associatedWeight); } else if (configTask.doEtaDependentFlow) { - target->getPairHist()->Fill(step, sampleIndex, posZ, eta2, eta1, deltaPhi, eta1 - eta2, - amplitude * centralityWeight * triggerWeight * associatedWeight); + if (!configFit.discriminateInnerOrOuter) { + target->getPairHist()->Fill(step, sampleIndex, posZ, eta2, eta1, deltaPhi, eta1 - eta2, + amplitude * centralityWeight * triggerWeight * associatedWeight); + } else { + target->getPairHist()->Fill(step, sampleIndex, posZ, isInnerOrOuter(channelId, fitType), eta1, deltaPhi, eta1 - eta2, + amplitude * centralityWeight * triggerWeight * associatedWeight); + } } else { target->getPairHist()->Fill(step, sampleIndex, posZ, pt1, multiplicity, deltaPhi, eta1 - eta2, amplitude * centralityWeight * triggerWeight * associatedWeight); @@ -2255,7 +2457,7 @@ struct HfTaskFlow { } // FILL QA PLOTS for associated particle - if (isSameEvent && (loopCounter == 1) && (step == CorrelationContainer::kCFStepReconstructed)) { + if (isSameEvent && (loopCounter == 1) && (step == CorrelationContainer::kCFStepReconstructed) && !configTask.removeQAForSystematics) { if constexpr (!std::is_same_v) { // If not FilteredMftTracks as trigger -> TPC-Ft0a correlations if constexpr (std::is_same_v) { // IF D0 CASE -> TPC-FV0a D0-h if (fitType == isFT0A) { @@ -2290,7 +2492,7 @@ struct HfTaskFlow { } // end of loop over tracks 1 } - template + template void fillCorrelationsFITReassociatedMftTracks(TTarget target, CorrelationContainer::CFStep step, TTracksTrig const& tracksTrigger, TTracksAssoc const& tracksAssoc, TFits const&, float multiplicity, float posZ, bool isSameEvent, bool cutAmbiguousTracks, int fitType, float centralityWeight) @@ -2298,26 +2500,37 @@ struct HfTaskFlow { auto triggerWeight = 1.0f; auto associatedWeight = 1.0f; auto loopCounter = 0; // To avoid filling associated tracks QA many times, I fill it only for the first trigger track of the collision - int sampleIndex = gRandom->Uniform(0, configTask.nSamples); + int sampleIndex = static_cast(gRandom->Uniform(0, configTask.nSamples)); // TRIGGER PARTICLE for (auto const& track1 : tracksTrigger) { loopCounter++; - auto reassociatedMftTrack = track1.template mfttrack_as(); + auto reassociatedMftTrack = track1.template mfttrack_as(); auto reassociatedMftTrackDcaXY = 0.f; auto reassociatedMftTrackDcaZ = 0.f; - if constexpr (std::is_same_v, TTracksAssoc>) { + if constexpr (std::is_same_v>, TTracksTrig>) { + if (!reassociatedMftTrack.has_mcParticle()) { + continue; + } + if (configTask.useOnlyPrimaryInMc && !reassociatedMftTrack.mcParticle().isPhysicalPrimary()) { + continue; + } + } + + if constexpr (std::is_same_v, TTracksTrig>) { reassociatedMftTrackDcaXY = track1.bestDCAXY(); } - if constexpr (std::is_same_v, TTracksAssoc>) { + if constexpr (std::is_same_v, TTracksTrig>) { reassociatedMftTrackDcaXY = track1.bestDCAXY(); reassociatedMftTrackDcaZ = track1.bestDCAZ(); } if (isSameEvent && loopCounter == 1) { // To avoid double counting, we fill the plots only the first time - registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::NoSelection); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::NoSelection); + } if (!isAcceptedMftTrack(reassociatedMftTrack, reassociatedMftTrackDcaXY, reassociatedMftTrackDcaZ, true)) { continue; @@ -2353,7 +2566,7 @@ struct HfTaskFlow { } // FILL QA PLOTS for trigger particle - if (isSameEvent && (step == CorrelationContainer::kCFStepReconstructed)) { + if (isSameEvent && (step == CorrelationContainer::kCFStepReconstructed) && !configTask.removeQAForSystematics) { if constexpr (std::is_same_v) { fillTriggerQa(multiplicity, eta1, phi1, pt1); } else if constexpr (std::is_same_v) { @@ -2416,15 +2629,20 @@ struct HfTaskFlow { target->getPairHist()->Fill(step, eta1 - eta2, 0.5, pt1, multiplicity, deltaPhi, posZ, amplitude * centralityWeight * triggerWeight * associatedWeight); } else if (configTask.doEtaDependentFlow) { - target->getPairHist()->Fill(step, sampleIndex, posZ, eta2, eta1, deltaPhi, eta1 - eta2, - amplitude * centralityWeight * triggerWeight * associatedWeight); + if (!configFit.discriminateInnerOrOuter) { + target->getPairHist()->Fill(step, sampleIndex, posZ, eta2, eta1, deltaPhi, eta1 - eta2, + amplitude * centralityWeight * triggerWeight * associatedWeight); + } else { + target->getPairHist()->Fill(step, sampleIndex, posZ, isInnerOrOuter(channelId, fitType), eta1, deltaPhi, eta1 - eta2, + amplitude * centralityWeight * triggerWeight * associatedWeight); + } } else { target->getPairHist()->Fill(step, sampleIndex, posZ, pt1, multiplicity, deltaPhi, eta1 - eta2, amplitude * centralityWeight * triggerWeight * associatedWeight); } // FILL QA PLOTS for associated particle - if (isSameEvent && (loopCounter == 1) && (step == CorrelationContainer::kCFStepReconstructed)) { + if (isSameEvent && (loopCounter == 1) && (step == CorrelationContainer::kCFStepReconstructed) && !configTask.removeQAForSystematics) { if (fitType == isFT0A) { fillAssociatedQa(multiplicity, eta2, phi2); } @@ -2442,7 +2660,7 @@ struct HfTaskFlow { auto triggerWeight = 1.0f; auto associatedWeight = 1.0f; auto loopCounter = 0; // To avoid filling associated tracks QA many times, I fill it only for the first trigger track of the collision - int sampleIndex = gRandom->Uniform(0, configTask.nSamples); + int sampleIndex = static_cast(gRandom->Uniform(0, configTask.nSamples)); // TRIGGER PARTICLE FROM FT0A for (std::size_t indexChannelA = 0; indexChannelA < ft0as.channelA().size(); indexChannelA++) { @@ -2462,7 +2680,7 @@ struct HfTaskFlow { registry.fill(HIST("Trig_hist_FT0A_FT0C"), sampleIndex, posZ, 0.5, amplitudeA * centralityWeight * triggerWeight); } - if (isSameEvent && (step == CorrelationContainer::kCFStepReconstructed)) { + if (isSameEvent && (step == CorrelationContainer::kCFStepReconstructed) && !configTask.removeQAForSystematics) { fillTriggerQa(multiplicity, etaA, phiA, 0.5); } // end of fill trigger QA @@ -2480,14 +2698,22 @@ struct HfTaskFlow { target->getPairHist()->Fill(step, etaA - etaC, 0.5, 0.5, multiplicity, deltaPhi, posZ, amplitudeA * amplitudeC * centralityWeight * triggerWeight * associatedWeight); } else if (configTask.doEtaDependentFlow) { - target->getPairHist()->Fill(step, sampleIndex, posZ, etaC, etaA, deltaPhi, etaA - etaC, - amplitudeA * amplitudeC * centralityWeight * triggerWeight * associatedWeight); + // target->getPairHist()->Fill(step, sampleIndex, posZ, etaC, etaA, deltaPhi, etaA - etaC, + // amplitudeA * amplitudeC * centralityWeight * triggerWeight * associatedWeight); + + if (!configFit.discriminateInnerOrOuter) { + target->getPairHist()->Fill(step, sampleIndex, posZ, etaC, etaA, deltaPhi, etaA - etaC, + amplitudeA * amplitudeC * centralityWeight * triggerWeight * associatedWeight); + } else { + target->getPairHist()->Fill(step, sampleIndex, posZ, isInnerOrOuter(channelIdA, isFT0A), isInnerOrOuter(channelIdC, isFT0C), deltaPhi, etaA - etaC, + amplitudeA * amplitudeC * centralityWeight * triggerWeight * associatedWeight); + } } else { target->getPairHist()->Fill(step, sampleIndex, posZ, 0.5, multiplicity, deltaPhi, etaA - etaC, amplitudeA * amplitudeC * centralityWeight * triggerWeight * associatedWeight); } - if (isSameEvent && (loopCounter == 1) && (step == CorrelationContainer::kCFStepReconstructed)) { + if (isSameEvent && (loopCounter == 1) && (step == CorrelationContainer::kCFStepReconstructed) && !configTask.removeQAForSystematics) { fillAssociatedQa(multiplicity, etaC, phiC); } // end of fill associated QA } // end of associated loop @@ -2502,21 +2728,16 @@ struct HfTaskFlow { auto triggerWeight = 1.0f; auto associatedWeight = 1.0f; auto loopCounter = 0; // To avoid filling associated tracks QA many times, I fill it only for the first trigger track of the collision - int sampleIndex = gRandom->Uniform(0, configTask.nSamples); - bool fillQaPlots = false; - - if (configTask.fillOnlyStepAll) { - if (step == CorrelationContainer::kCFStepAll) { - fillQaPlots = true; - } - } else { - if (step == CorrelationContainer::kCFStepTrackedOnlyPrim) { - fillQaPlots = true; - } - } + int sampleIndex = static_cast(gRandom->Uniform(0, configTask.nSamples)); for (auto const& track1 : tracksTrigger) { - loopCounter++; + + if (configTask.useCutNeutralParticles) { + auto pdgTriggerParticle = pdg->GetParticle(track1.pdgCode()); + if (!pdgTriggerParticle || std::abs(pdgTriggerParticle->Charge()) < o2::constants::math::Almost0) { + continue; + } + } if (track1.eta() < configTask.etaMcParticlesTriggerMin || track1.eta() > configTask.etaMcParticlesTriggerMax) { continue; @@ -2538,8 +2759,7 @@ struct HfTaskFlow { } // FILL QA FOR TRIGGER PARTICLE - if (isSameEvent && fillQaPlots) { - registry.fill(HIST("MC/hEfficiencyTrigger"), track1.pt(), track1.eta(), posZ); + if (isSameEvent && !configTask.removeQAForSystematics) { if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcTpc)) { fillTriggerQa(multiplicity, track1.eta(), track1.phi(), track1.pt()); } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcMft)) { @@ -2559,8 +2779,17 @@ struct HfTaskFlow { } } + loopCounter++; + for (auto const& track2 : tracksAssoc) { + if (configTask.useCutNeutralParticles) { + auto pdgAssociatedParticle = pdg->GetParticle(track2.pdgCode()); + if (!pdgAssociatedParticle || std::abs(pdgAssociatedParticle->Charge()) < o2::constants::math::Almost0) { + continue; + } + } + if (track1.globalIndex() == track2.globalIndex()) { continue; } @@ -2574,8 +2803,8 @@ struct HfTaskFlow { continue; } - float deltaPhi = RecoDecay::constrainAngle(track2.phi() - track1.phi(), -PIHalf); - float deltaEta = track2.eta() - track1.eta(); + float deltaPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi(), -PIHalf); + float deltaEta = track1.eta() - track2.eta(); if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { target->getPairHist()->Fill(step, deltaEta, track2.pt(), track1.pt(), multiplicity, deltaPhi, posZ, @@ -2589,8 +2818,7 @@ struct HfTaskFlow { } // FILL QA PLOTS for associated particle - if (isSameEvent && fillQaPlots && (loopCounter == 1)) { - registry.fill(HIST("MC/hEfficiencyAssociated"), track2.pt(), track2.eta(), posZ); + if (isSameEvent && (loopCounter == 1) && !configTask.removeQAForSystematics) { if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcTpc)) { fillAssociatedQa(multiplicity, track2.eta(), track2.phi()); } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcMft)) { @@ -2632,15 +2860,14 @@ struct HfTaskFlow { // auto mult = 0.f; if (configCollision.useMultiplicityFromTracks) { return associatedTracks.size(); - } else { - return getMultiplicityEstimator(collision, false); } + return getMultiplicityEstimator(collision, false); }; // The first one that I call "Data" should work for data and mc rec using BinningTypeData = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - BinningTypeData binningWithTracksSize{{getMultiplicity}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}, true}; + BinningTypeData binningWithTracksSize{{getMultiplicity}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}}; auto tracksTuple = std::make_tuple(tracksTrigger, tracksAssoc); Pair pair{binningWithTracksSize, configTask.nMixedEvents, -1, collisions, tracksTuple, &cache}; @@ -2655,7 +2882,7 @@ struct HfTaskFlow { auto bc = collision1.template bc_as(); loadEfficiencyCorrection(bc.timestamp()); - auto multiplicity = 0; + float multiplicity = 0.f; if (configCollision.useMultiplicityFromTracks) { multiplicity = tracks1.size(); } else { @@ -2664,16 +2891,16 @@ struct HfTaskFlow { float centralityWeight = 1.0f; if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision1, true)); - if (getCentralityEstimator(collision1, false) < configCollision.minCentrality || getCentralityEstimator(collision1, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; + getCentralityWeight(centralityWeight, getCentralityEstimator(collision1, false)); + if (getCentralityEstimator(collision1, false) < configCollision.minCentrality || getCentralityEstimator(collision1, false) >= configCollision.maxCentrality) { + continue; } } + if (!configCollision.useCentrality && (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity)) { + continue; + } + corrContainer->fillEvent(multiplicity, step); fillCorrelations(corrContainer, step, tracks1, tracks2, multiplicity, collision1.posZ(), false, getMagneticField(bc.timestamp()), centralityWeight); } @@ -2694,13 +2921,12 @@ struct HfTaskFlow { auto mult = associatedTracks.size(); if (configCollision.useMultiplicityFromTracks) { return mult; - } else { - return getMultiplicityEstimator(collision, false); } + return getMultiplicityEstimator(collision, false); }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - MixedBinning const binningOnVtxAndMult{{getMultiplicity}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}, true}; + MixedBinning const binningOnVtxAndMult{{getMultiplicity}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}}; for (auto const& [collision1, collision2] : soa::selfCombinations(binningOnVtxAndMult, configTask.nMixedEvents, -1, collisions, collisions)) { @@ -2716,7 +2942,7 @@ struct HfTaskFlow { auto bc = collision1.template bc_as(); loadEfficiencyCorrection(bc.timestamp()); - auto multiplicity = 0; + float multiplicity = 0.f; if (configCollision.useMultiplicityFromTracks) { multiplicity = tracksTrigger.size(); } else { @@ -2725,16 +2951,16 @@ struct HfTaskFlow { float centralityWeight = 1.0f; if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision1, true)); - if (getCentralityEstimator(collision1, false) < configCollision.minCentrality || getCentralityEstimator(collision1, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; + getCentralityWeight(centralityWeight, getCentralityEstimator(collision1, false)); + if (getCentralityEstimator(collision1, false) < configCollision.minCentrality || getCentralityEstimator(collision1, false) >= configCollision.maxCentrality) { + continue; } } + if (!configCollision.useCentrality && (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity)) { + continue; + } + auto slicedTriggerTracks = tracksTrigger.sliceBy(presliceTrigger, collision1.globalIndex()); auto slicedAssociatedTracks = tracksAssoc.sliceBy(presliceAssociated, collision2.globalIndex()); @@ -2743,7 +2969,7 @@ struct HfTaskFlow { } } - template + template void mixCollisionsReassociatedMftTracks(TCollisions const& collisions, CorrelationContainer::CFStep step, TTracksTpc const& tracksTpc, TTracksTrig const& tracksTrigger, TTracksAssoc const& tracksAssoc, TPresliceTrigger const& presliceTrigger, TPresliceAssociated const& presliceAssociated, OutputObj& corrContainer, bool cutAmbiguousTracks, aod::BCsWithTimestamps const&) @@ -2758,13 +2984,12 @@ struct HfTaskFlow { auto mult = associatedTracks.size(); if (configCollision.useMultiplicityFromTracks) { return mult; - } else { - return getMultiplicityEstimator(collision, false); } + return getMultiplicityEstimator(collision, false); }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - MixedBinning const binningOnVtxAndMult{{getMultiplicity}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}, true}; + MixedBinning const binningOnVtxAndMult{{getMultiplicity}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}}; for (auto const& [collision1, collision2] : soa::selfCombinations(binningOnVtxAndMult, configTask.nMixedEvents, -1, collisions, collisions)) { @@ -2778,7 +3003,7 @@ struct HfTaskFlow { auto bc = collision1.template bc_as(); loadEfficiencyCorrection(bc.timestamp()); auto tracksForMultiplicity = tracksTpc.sliceByCached(o2::aod::track::collisionId, collision1.globalIndex(), cache); - auto multiplicity = 0; + float multiplicity = 0.f; if (configCollision.useMultiplicityFromTracks) { multiplicity = tracksForMultiplicity.size(); } else { @@ -2787,29 +3012,29 @@ struct HfTaskFlow { float centralityWeight = 1.0f; if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision1, true)); - if (getCentralityEstimator(collision1, false) < configCollision.minCentrality || getCentralityEstimator(collision1, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; + getCentralityWeight(centralityWeight, getCentralityEstimator(collision1, false)); + if (getCentralityEstimator(collision1, false) < configCollision.minCentrality || getCentralityEstimator(collision1, false) >= configCollision.maxCentrality) { + continue; } } + if (!configCollision.useCentrality && (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity)) { + continue; + } + // if TPC-MFT cases if constexpr (std::is_same_v) { auto slicedTriggerTracks = tracksTrigger.sliceBy(presliceTrigger, collision1.globalIndex()); auto slicedAssociatedTracks = tracksAssoc.sliceBy(presliceAssociated, collision2.globalIndex()); corrContainer->fillEvent(multiplicity, step); - fillCorrelationsReassociatedMftTracks(corrContainer, step, slicedTriggerTracks, slicedAssociatedTracks, multiplicity, collision1.posZ(), false, cutAmbiguousTracks, centralityWeight); + fillCorrelationsReassociatedMftTracks(corrContainer, step, slicedTriggerTracks, slicedAssociatedTracks, multiplicity, collision1.posZ(), false, cutAmbiguousTracks, centralityWeight); } else if ((collision1.has_foundFT0() && collision2.has_foundFT0())) { // if MFT-FT0A cases auto slicedTriggerTracks = tracksTrigger.sliceBy(presliceTrigger, collision1.globalIndex()); const auto& ft0 = collision2.foundFT0(); corrContainer->fillEvent(multiplicity, step); - fillCorrelationsFITReassociatedMftTracks(corrContainer, step, slicedTriggerTracks, ft0, tracksAssoc, multiplicity, collision1.posZ(), false, cutAmbiguousTracks, isFT0A, centralityWeight); + fillCorrelationsFITReassociatedMftTracks(corrContainer, step, slicedTriggerTracks, ft0, tracksAssoc, multiplicity, collision1.posZ(), false, cutAmbiguousTracks, isFT0A, centralityWeight); } } // end of for loop } @@ -2824,18 +3049,17 @@ struct HfTaskFlow { // return multiplicity; // }; - auto getMultiplicity = [&tracksTrigger, this](FilteredCollisionsWSelMult::iterator const& collision) { - auto associatedTracks = tracksTrigger.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); + auto getMultiplicity = [&tracksTpc, this](FilteredCollisionsWSelMult::iterator const& collision) { + auto associatedTracks = tracksTpc.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); auto mult = associatedTracks.size(); if (configCollision.useMultiplicityFromTracks) { return mult; - } else { - return getMultiplicityEstimator(collision, false); } + return getMultiplicityEstimator(collision, false); }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - MixedBinning const binningOnVtxAndMult{{getMultiplicity}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}, true}; + MixedBinning const binningOnVtxAndMult{{getMultiplicity}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}}; for (auto const& [collision1, collision2] : soa::selfCombinations(binningOnVtxAndMult, configTask.nMixedEvents, -1, collisions, collisions)) { @@ -2857,7 +3081,7 @@ struct HfTaskFlow { // auto tracksForMultiplicity = tracksTpc.sliceByCached(o2::aod::track::collisionId, collision1.globalIndex(), cache); // const auto& fv0 = collision2.foundFV0(); - // auto multiplicity = 0; + // float multiplicity = 0.f; // if (configCollision.useMultiplicityFromTracks) { // multiplicity = tracksForMultiplicity.size(); // } else { @@ -2865,7 +3089,7 @@ struct HfTaskFlow { // } // if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - // return; + // continue; // } // corrContainer->fillEvent(multiplicity, step); @@ -2880,7 +3104,7 @@ struct HfTaskFlow { auto tracksForMultiplicity = tracksTpc.sliceByCached(o2::aod::track::collisionId, collision1.globalIndex(), cache); const auto& ft0 = collision2.foundFT0(); - auto multiplicity = 0; + float multiplicity = 0.f; if (configCollision.useMultiplicityFromTracks) { multiplicity = tracksForMultiplicity.size(); } else { @@ -2889,16 +3113,16 @@ struct HfTaskFlow { float centralityWeight = 1.0f; if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision1, true)); - if (getCentralityEstimator(collision1, false) < configCollision.minCentrality || getCentralityEstimator(collision1, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; + getCentralityWeight(centralityWeight, getCentralityEstimator(collision1, false)); + if (getCentralityEstimator(collision1, false) < configCollision.minCentrality || getCentralityEstimator(collision1, false) >= configCollision.maxCentrality) { + continue; } } + if (!configCollision.useCentrality && (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity)) { + continue; + } + corrContainer->fillEvent(multiplicity, step); fillCorrelationsFIT(corrContainer, step, slicedTriggerTracks, ft0, tracksAssoc, multiplicity, collision1.posZ(), false, fitType, centralityWeight); } @@ -2921,13 +3145,12 @@ struct HfTaskFlow { auto mult = associatedTracks.size(); if (configCollision.useMultiplicityFromTracks) { return mult; - } else { - return getMultiplicityEstimator(collision, false); } + return getMultiplicityEstimator(collision, false); }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - MixedBinning const binningOnVtxAndMult{{getMultiplicity}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}, true}; + MixedBinning const binningOnVtxAndMult{{getMultiplicity}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}}; for (auto const& [collision1, collision2] : soa::selfCombinations(binningOnVtxAndMult, configTask.nMixedEvents, -1, collisions, collisions)) { @@ -2947,7 +3170,7 @@ struct HfTaskFlow { auto bc = collision1.template bc_as(); loadEfficiencyCorrection(bc.timestamp()); auto tracksForMultiplicity = tracksTpc.sliceByCached(o2::aod::track::collisionId, collision1.globalIndex(), cache); - auto multiplicity = 0; + float multiplicity = 0.f; if (configCollision.useMultiplicityFromTracks) { multiplicity = tracksForMultiplicity.size(); } else { @@ -2956,16 +3179,16 @@ struct HfTaskFlow { float centralityWeight = 1.0f; if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision1, true)); - if (getCentralityEstimator(collision1, false) < configCollision.minCentrality || getCentralityEstimator(collision1, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; + getCentralityWeight(centralityWeight, getCentralityEstimator(collision1, false)); + if (getCentralityEstimator(collision1, false) < configCollision.minCentrality || getCentralityEstimator(collision1, false) >= configCollision.maxCentrality) { + continue; } } + if (!configCollision.useCentrality && (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity)) { + continue; + } + corrContainer->fillEvent(multiplicity, step); fillCorrelationsFt0aFt0c(corrContainer, step, ft0as, ft0cs, multiplicity, collision1.posZ(), true, centralityWeight); } @@ -2994,26 +3217,18 @@ struct HfTaskFlow { return; } - registry.fill(HIST("Data/hNTracks"), tracks.size()); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hNTracks"), tracks.size()); + } + auto bc = collision.template bc_as(); loadEfficiencyCorrection(bc.timestamp()); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); @@ -3041,26 +3256,18 @@ struct HfTaskFlow { return; } - registry.fill(HIST("Data/hNTracks"), tracks.size()); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hNTracks"), tracks.size()); + } + auto bc = collision.template bc_as(); loadEfficiencyCorrection(bc.timestamp()); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); @@ -3088,26 +3295,18 @@ struct HfTaskFlow { return; } - registry.fill(HIST("Data/hNTracks"), tracks.size()); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hNTracks"), tracks.size()); + } + auto bc = collision.template bc_as(); loadEfficiencyCorrection(bc.timestamp()); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); @@ -3128,26 +3327,18 @@ struct HfTaskFlow { return; } - registry.fill(HIST("Data/hNTracks"), tracks.size()); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hNTracks"), tracks.size()); + } + auto bc = collision.template bc_as(); loadEfficiencyCorrection(bc.timestamp()); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { @@ -3160,49 +3351,41 @@ struct HfTaskFlow { } PROCESS_SWITCH(HfTaskFlow, processSameTpcMftChCh, "DATA : Process same-event correlations for TPC-MFT h-h case", false); - void processSameTpcMftChChReassociated(FilteredCollisionsWSelMult::iterator const& collision, - soa::SmallGroups const& reassociatedMftTracks, - FilteredTracksWDcaSel const& tracks, - FilteredMftTracks const& mftTracks, - aod::BCsWithTimestamps const&) - { - if (!(isAcceptedCollision(collision, true))) { - return; - } + // void processSameTpcMftChChReassociated(FilteredCollisionsWSelMult::iterator const& collision, + // soa::SmallGroups const& reassociatedMftTracks, + // FilteredTracksWDcaSel const& tracks, + // FilteredMftTracks const& mftTracks, + // aod::BCsWithTimestamps const&) + // { + // if (!(isAcceptedCollision(collision, true))) { + // return; + // } - registry.fill(HIST("Data/hNTracks"), tracks.size()); - registry.fill(HIST("Data/Mft/hNMftTracks"), mftTracks.size()); - registry.fill(HIST("Data/Mft/hNBestCollisionFwd"), reassociatedMftTracks.size()); - auto bc = collision.template bc_as(); - loadEfficiencyCorrection(bc.timestamp()); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + // if (!configTask.removeQAForSystematics) { + // registry.fill(HIST("Data/hNTracks"), tracks.size()); + // registry.fill(HIST("Data/Mft/hNMftTracks"), mftTracks.size()); + // registry.fill(HIST("Data/Mft/hNBestCollisionFwd"), reassociatedMftTracks.size()); + // } - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } - } + // auto bc = collision.template bc_as(); + // loadEfficiencyCorrection(bc.timestamp()); + // float multiplicity = 0.f; + // float centrality = -1.f; + // float centralityWeight = 1.f; - if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { - sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); - } else { - sameEventTpcMft->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsReassociatedMftTracks(sameEventTpcMft, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); - } - } - PROCESS_SWITCH(HfTaskFlow, processSameTpcMftChChReassociated, "DATA : Process same-event correlations for TPC-MFT h-h case reassociated", false); + // if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + // return; + // } + + // if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + // sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + // fillCorrelationsReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); + // } else { + // sameEventTpcMft->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + // fillCorrelationsReassociatedMftTracks(sameEventTpcMft, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); + // } + // } + // PROCESS_SWITCH(HfTaskFlow, processSameTpcMftChChReassociated, "DATA : Process same-event correlations for TPC-MFT h-h case reassociated", false); void processSameTpcMftChChReassociated3d(FilteredCollisionsWSelMult::iterator const& collision, soa::SmallGroups const& reassociatedMftTracks, @@ -3214,42 +3397,38 @@ struct HfTaskFlow { return; } - registry.fill(HIST("Data/hNTracks"), tracks.size()); - registry.fill(HIST("Data/Mft/hNMftTracks"), mftTracks.size()); - registry.fill(HIST("Data/Mft/hNBestCollisionFwd"), reassociatedMftTracks.size()); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hNTracks"), tracks.size()); + registry.fill(HIST("Data/Mft/hNMftTracks"), mftTracks.size()); + registry.fill(HIST("Data/Mft/hNBestCollisionFwd"), reassociatedMftTracks.size()); + } + auto bc = collision.template bc_as(); loadEfficiencyCorrection(bc.timestamp()); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; + + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } auto uncorrectedMultiplicity = multiplicity; - registry.fill(HIST("Data/hMultiplicity_uncorrected_vs_corrected"), uncorrectedMultiplicity, multiplicity); + if (configCollision.useMultiplicityFromTracksCorrected) { multiplicity = getCorrectedMultiplicity(tracks); } - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hMultiplicity_uncorrected_vs_corrected"), uncorrectedMultiplicity, multiplicity); } if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); + fillCorrelationsReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); } else { sameEventTpcMft->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsReassociatedMftTracks(sameEventTpcMft, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); + fillCorrelationsReassociatedMftTracks(sameEventTpcMft, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); } } PROCESS_SWITCH(HfTaskFlow, processSameTpcMftChChReassociated3d, "DATA : Process same-event correlations for TPC-MFT h-h case 3d reassociated", false); @@ -3264,36 +3443,28 @@ struct HfTaskFlow { return; // when process function has iterator } - registry.fill(HIST("Data/hNTracks"), tracks.size()); - registry.fill(HIST("Data/Mft/hNMftTracks"), mftTracks.size()); - registry.fill(HIST("Data/Mft/hNBestCollisionFwd"), reassociatedMftTracks.size()); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hNTracks"), tracks.size()); + registry.fill(HIST("Data/Mft/hNMftTracks"), mftTracks.size()); + registry.fill(HIST("Data/Mft/hNBestCollisionFwd"), reassociatedMftTracks.size()); + } + auto bc = collision.template bc_as(); loadEfficiencyCorrection(bc.timestamp()); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, true, centralityWeight); + fillCorrelationsReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, true, centralityWeight); } else { sameEventTpcMft->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsReassociatedMftTracks(sameEventTpcMft, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, true, centralityWeight); + fillCorrelationsReassociatedMftTracks(sameEventTpcMft, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, true, centralityWeight); } } PROCESS_SWITCH(HfTaskFlow, processSameTpcMftChChNonAmbiguous, "DATA : Process same-event correlations for TPC-MFT h-h case with non-ambiguous tracks", false); @@ -3321,23 +3492,12 @@ struct HfTaskFlow { auto bc = collision.template bc_as(); loadEfficiencyCorrection(bc.timestamp()); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); @@ -3345,42 +3505,31 @@ struct HfTaskFlow { } PROCESS_SWITCH(HfTaskFlow, processSameTpcMftD0Ch, "DATA : Process same-event correlations for TPC-MFT D0-h case", false); - void processSameTpcMftD0ChReassociated(FilteredCollisionsWSelMult::iterator const& collision, - HfCandidatesSelD0 const& candidates, - soa::SmallGroups const& reassociatedMftTracks, - FilteredTracksWDcaSel const& tracks, - FilteredMftTracks const&, - aod::BCsWithTimestamps const&) - { - if (!(isAcceptedCollision(collision, true))) { - return; // when process function has iterator - } + // void processSameTpcMftD0ChReassociated(FilteredCollisionsWSelMult::iterator const& collision, + // HfCandidatesSelD0 const& candidates, + // soa::SmallGroups const& reassociatedMftTracks, + // FilteredTracksWDcaSel const& tracks, + // FilteredMftTracks const&, + // aod::BCsWithTimestamps const&) + // { + // if (!(isAcceptedCollision(collision, true))) { + // return; // when process function has iterator + // } - auto bc = collision.template bc_as(); - loadEfficiencyCorrection(bc.timestamp()); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + // auto bc = collision.template bc_as(); + // loadEfficiencyCorrection(bc.timestamp()); + // float multiplicity = 0.f; + // float centrality = -1.f; + // float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } - } + // if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + // return; + // } - sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsReassociatedMftTracks(sameEventHf, CorrelationContainer::CFStep::kCFStepReconstructed, candidates, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); - } - PROCESS_SWITCH(HfTaskFlow, processSameTpcMftD0ChReassociated, "DATA : Process same-event correlations for TPC-MFT D0-h case reassociated", false); + // sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + // fillCorrelationsReassociatedMftTracks(sameEventHf, CorrelationContainer::CFStep::kCFStepReconstructed, candidates, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); + // } + // PROCESS_SWITCH(HfTaskFlow, processSameTpcMftD0ChReassociated, "DATA : Process same-event correlations for TPC-MFT D0-h case reassociated", false); // ===================================== // DATA : process same event correlations: TPC-MFT HF-h case for Lc @@ -3405,23 +3554,12 @@ struct HfTaskFlow { auto bc = collision.template bc_as(); loadEfficiencyCorrection(bc.timestamp()); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); @@ -3429,42 +3567,31 @@ struct HfTaskFlow { } PROCESS_SWITCH(HfTaskFlow, processSameTpcMftLcCh, "DATA : Process same-event correlations for TPC-MFT Lc-h case", false); - void processSameTpcMftLcChReassociated(FilteredCollisionsWSelMult::iterator const& collision, - HfCandidatesSelLc const& candidates, - soa::SmallGroups const& reassociatedMftTracks, - FilteredTracksWDcaSel const& tracks, - FilteredMftTracks const&, - aod::BCsWithTimestamps const&) - { - if (!(isAcceptedCollision(collision, true))) { - return; // when process function has iterator - } + // void processSameTpcMftLcChReassociated(FilteredCollisionsWSelMult::iterator const& collision, + // HfCandidatesSelLc const& candidates, + // soa::SmallGroups const& reassociatedMftTracks, + // FilteredTracksWDcaSel const& tracks, + // FilteredMftTracks const&, + // aod::BCsWithTimestamps const&) + // { + // if (!(isAcceptedCollision(collision, true))) { + // return; // when process function has iterator + // } - auto multiplicity = 0; - auto bc = collision.template bc_as(); - loadEfficiencyCorrection(bc.timestamp()); - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + // auto bc = collision.template bc_as(); + // loadEfficiencyCorrection(bc.timestamp()); + // float multiplicity = 0.f; + // float centrality = -1.f; + // float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } - } + // if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + // return; + // } - sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsReassociatedMftTracks(sameEventHf, CorrelationContainer::CFStep::kCFStepReconstructed, candidates, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); - } - PROCESS_SWITCH(HfTaskFlow, processSameTpcMftLcChReassociated, "DATA : Process same-event correlations for TPC-MFT Lc-h case reassociated", false); + // sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + // fillCorrelationsReassociatedMftTracks(sameEventHf, CorrelationContainer::CFStep::kCFStepReconstructed, candidates, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); + // } + // PROCESS_SWITCH(HfTaskFlow, processSameTpcMftLcChReassociated, "DATA : Process same-event correlations for TPC-MFT Lc-h case reassociated", false); // ===================================== // DATA : process same event correlations: TPC-FV0A Ch. Part. - Ch. Part @@ -3590,7 +3717,7 @@ struct HfTaskFlow { // } // sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - // fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, fv0, fv0as, multiplicity, collision.posZ(), true, false, isFV0A); + // fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, fv0, fv0as, multiplicity, collision.posZ(), true, false, isFV0A); // } // } // PROCESS_SWITCH(HfTaskFlow, processSameMftFv0aChChReassociated, "DATA : Process same-event correlations for MFT-FV0a h-h case reassociated", false); @@ -3614,7 +3741,7 @@ struct HfTaskFlow { } sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, fv0, fv0as, multiplicity, collision.posZ(), true, false, isFV0A); + fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, fv0, fv0as, multiplicity, collision.posZ(), true, false, isFV0A); } } PROCESS_SWITCH(HfTaskFlow, processSameMftFv0aChChReassociated3d, "DATA : Process same-event correlations for MFT-FV0a h-h case 3d reassociated", false); @@ -3638,7 +3765,7 @@ struct HfTaskFlow { // } // sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - // fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, fv0, fv0as, multiplicity, collision.posZ(), true, true, isFV0A); + // fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, fv0, fv0as, multiplicity, collision.posZ(), true, true, isFV0A); // } // } // PROCESS_SWITCH(HfTaskFlow, processSameMftFv0aChChNonAmbiguous, "DATA : Process same-event correlations for MFT-FV0a h-h non-ambiguous case", false); @@ -3662,24 +3789,15 @@ struct HfTaskFlow { if (collision.has_foundFT0()) { loadGain(bc); const auto& ft0 = collision.foundFT0(); - registry.fill(HIST("Data/hNTracks"), tracks.size()); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hNTracks"), tracks.size()); } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { @@ -3713,23 +3831,12 @@ struct HfTaskFlow { if (collision.has_foundFT0()) { loadGain(bc); const auto& ft0 = collision.foundFT0(); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); @@ -3758,23 +3865,12 @@ struct HfTaskFlow { if (collision.has_foundFT0()) { loadGain(bc); const auto& ft0 = collision.foundFT0(); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); @@ -3803,23 +3899,12 @@ struct HfTaskFlow { if (collision.has_foundFT0()) { loadGain(bc); const auto& ft0 = collision.foundFT0(); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { @@ -3833,52 +3918,42 @@ struct HfTaskFlow { } PROCESS_SWITCH(HfTaskFlow, processSameMftFt0aChCh, "DATA : Process same-event correlations for MFT-FT0-A h-h case", false); - void processSameMftFt0aChChReassociated(FilteredCollisionsWSelMult::iterator const& collision, - soa::SmallGroups const& reassociatedMftTracks, - FilteredMftTracks const&, - aod::FT0s const& ft0as, - FilteredTracksWDcaSel const& tracks, - aod::BCsWithTimestamps const&) - { - if (!(isAcceptedCollision(collision, true))) { - return; - } + // void processSameMftFt0aChChReassociated(FilteredCollisionsWSelMult::iterator const& collision, + // soa::SmallGroups const& reassociatedMftTracks, + // FilteredMftTracks const&, + // aod::FT0s const& ft0as, + // FilteredTracksWDcaSel const& tracks, + // aod::BCsWithTimestamps const&) + // { + // if (!(isAcceptedCollision(collision, true))) { + // return; + // } - auto bc = collision.template bc_as(); - loadEfficiencyCorrection(bc.timestamp()); + // auto bc = collision.template bc_as(); + // loadEfficiencyCorrection(bc.timestamp()); - if (collision.has_foundFT0()) { - loadGain(bc); - const auto& ft0 = collision.foundFT0(); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + // if (collision.has_foundFT0()) { - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } - } + // loadGain(bc); + // const auto& ft0 = collision.foundFT0(); + // float multiplicity = 0.f; + // float centrality = -1.f; + // float centralityWeight = 1.f; - if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { - sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, false, isFT0A, centralityWeight); - } else { - sameEventMftFt0a->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsFITReassociatedMftTracks(sameEventMftFt0a, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, false, isFT0A, centralityWeight); - } - } - } - PROCESS_SWITCH(HfTaskFlow, processSameMftFt0aChChReassociated, "DATA : Process same-event correlations for MFT-FT0-A h-h case reassociated", false); + // if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + // return; + // } + + // if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + // sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + // fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, false, isFT0A, centralityWeight); + // } else { + // sameEventMftFt0a->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + // fillCorrelationsFITReassociatedMftTracks(sameEventMftFt0a, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, false, isFT0A, centralityWeight); + // } + // } + // } + // PROCESS_SWITCH(HfTaskFlow, processSameMftFt0aChChReassociated, "DATA : Process same-event correlations for MFT-FT0-A h-h case reassociated", false); void processSameMftFt0aChChReassociated3d(FilteredCollisionsWSelMult::iterator const& collision, soa::SmallGroups const& reassociatedMftTracks, @@ -3895,33 +3970,23 @@ struct HfTaskFlow { loadEfficiencyCorrection(bc.timestamp()); if (collision.has_foundFT0()) { + loadGain(bc); const auto& ft0 = collision.foundFT0(); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, false, isFT0A, centralityWeight); + fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, false, isFT0A, centralityWeight); } else { sameEventMftFt0a->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsFITReassociatedMftTracks(sameEventMftFt0a, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, false, isFT0A, centralityWeight); + fillCorrelationsFITReassociatedMftTracks(sameEventMftFt0a, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, false, isFT0A, centralityWeight); } } } @@ -3944,31 +4009,20 @@ struct HfTaskFlow { if (collision.has_foundFT0()) { loadGain(bc); const auto& ft0 = collision.foundFT0(); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, true, isFT0A, centralityWeight); + fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, true, isFT0A, centralityWeight); } else { sameEventMftFt0a->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsFITReassociatedMftTracks(sameEventMftFt0a, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, true, isFT0A, centralityWeight); + fillCorrelationsFITReassociatedMftTracks(sameEventMftFt0a, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, true, isFT0A, centralityWeight); } } } @@ -3993,24 +4047,15 @@ struct HfTaskFlow { if (collision.has_foundFT0()) { loadGain(bc); const auto& ft0 = collision.foundFT0(); - registry.fill(HIST("Data/hNTracks"), tracks.size()); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hNTracks"), tracks.size()); } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); @@ -4039,23 +4084,12 @@ struct HfTaskFlow { if (collision.has_foundFT0()) { loadGain(bc); const auto& ft0 = collision.foundFT0(); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); @@ -4084,23 +4118,12 @@ struct HfTaskFlow { if (collision.has_foundFT0()) { loadGain(bc); const auto& ft0 = collision.foundFT0(); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); @@ -4128,23 +4151,12 @@ struct HfTaskFlow { if (collision.has_foundFT0()) { loadGain(bc); const auto& ft0 = collision.foundFT0(); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); @@ -4157,29 +4169,135 @@ struct HfTaskFlow { // MONTE-CARLO // =================================================================================================================================================================================================================================================================== - void processSameMcGen(FilteredMcCollisionsWMult::iterator const& mcCollision, + void processSameTpcMftChChReassociated3dMcReconstructed(FilteredCollisionsWSelMult::iterator const& collision, + soa::SmallGroups> const& reassociatedMftTracks, + FilteredTracksWDcaSel const& tracks, + FilteredMftTracksWCollsMcLabels const& mftTracks, + aod::McCollisions const& /*mcCollisions*/, + aod::McParticles const& /*particles*/, + aod::BCsWithTimestamps const&) + { + if (!(isAcceptedCollision(collision, true))) { + return; + } + + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hNTracks"), tracks.size()); + registry.fill(HIST("Data/Mft/hNMftTracks"), mftTracks.size()); + registry.fill(HIST("Data/Mft/hNBestCollisionFwd"), reassociatedMftTracks.size()); + } + + auto bc = collision.template bc_as(); + loadEfficiencyCorrection(bc.timestamp()); + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; + + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; + } + + auto uncorrectedMultiplicity = multiplicity; + + if (configCollision.useMultiplicityFromTracksCorrected) { + multiplicity = getCorrectedMultiplicity(tracks); + } + + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hMultiplicity_uncorrected_vs_corrected"), uncorrectedMultiplicity, multiplicity); + } + + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrelationsReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); + } else { + sameEventTpcMft->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrelationsReassociatedMftTracks(sameEventTpcMft, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); + } + } + PROCESS_SWITCH(HfTaskFlow, processSameTpcMftChChReassociated3dMcReconstructed, "Mc Rec : Process same-event correlations for TPC-MFT h-h case 3d reassociated", false); + + void processSameMftFt0aChChReassociated3dMcReconstructed(FilteredCollisionsWSelMult::iterator const& collision, + soa::SmallGroups> const& reassociatedMftTracks, + FilteredTracksWDcaSel const& tracks, + FilteredMftTracksWCollsMcLabels const& /*mftTracks*/, + aod::McCollisions const& /*mcCollisions*/, + aod::McParticles const& /*particles*/, + aod::FT0s const& ft0as, + aod::BCsWithTimestamps const&) + { + if (!(isAcceptedCollision(collision, true))) { + return; + } + + auto bc = collision.template bc_as(); + loadEfficiencyCorrection(bc.timestamp()); + + if (collision.has_foundFT0()) { + + loadGain(bc); + const auto& ft0 = collision.foundFT0(); + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; + + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; + } + + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, false, isFT0A, centralityWeight); + } else { + sameEventMftFt0a->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrelationsFITReassociatedMftTracks(sameEventMftFt0a, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, false, isFT0A, centralityWeight); + } + } + } + PROCESS_SWITCH(HfTaskFlow, processSameMftFt0aChChReassociated3dMcReconstructed, "MC Rec : Process same-event correlations for MFT-FT0-A h-h case reassociated 3d", false); + + void processSameMcGen(FilteredMcCollisionsWMultWCollsExtra::iterator const& mcCollision, FilteredMcParticles const& mcParticles, SmallGroupMcCollisions const& collisions) { - auto multiplicity = 0; + + if (mcParticles.size() == 0) { // guard against empty filtered batch + return; + } + if (collisions.size() == 0) { + return; + } + + float multiplicity = 0.f; if (configCollision.useMultiplicityFromTracks) { for (const auto& track : mcParticles) { if (std::abs(track.eta()) < configCentral.etaCentralTrackMax && track.pt() >= configCentral.ptCentralTrackMin && track.pt() <= configCentral.ptCentralTrackMax) { multiplicity += 1; } } + registry.fill(HIST("MC/hMultiplicityTracksSize"), multiplicity); } else { - multiplicity = mcCollision.multMCPVz(); + multiplicity = mcCollision.multMCFT0C(); + registry.fill(HIST("MC/hMultiplicity_multMCFT0C"), multiplicity); } - if ((multiplicity < configCollision.minMultiplicity) || (multiplicity >= configCollision.maxMultiplicity)) { - return; - } + float centrality = 999.f; + centrality = getCentralityEstimator(mcCollision, true); - if (collisions.size() == 0) { - return; + if (configCollision.useCentrality) { + if (centrality < configCollision.minCentrality || centrality >= configCollision.maxCentrality) { + return; + } + } + if (!configCollision.useCentrality) { + if ((multiplicity < configCollision.minMultiplicity) || (multiplicity >= configCollision.maxMultiplicity)) { + return; + } } + registry.fill(HIST("MC/hCentralityUsed"), centrality); + registry.fill(HIST("MC/hMultiplicityUsed"), multiplicity); + sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepAll); fillCorrelationsMonteCarlo(sameEvent, CorrelationContainer::CFStep::kCFStepAll, mcParticles, mcParticles, multiplicity, mcCollision.posZ(), true); } @@ -4263,19 +4381,19 @@ struct HfTaskFlow { } PROCESS_SWITCH(HfTaskFlow, processMixedTpcMftChChBis, "DATA : Process mixed-event correlations for TPC-MFT h-h case", false); - void processMixedTpcMftChChReassociated2d(FilteredCollisionsWSelMult const& collisions, - FilteredTracksWDcaSel const& tracks, - FilteredMftTracks const& /*mftTracks*/, - soa::SmallGroups const& reassociated2dMftTracks, - aod::BCsWithTimestamps const& bcs) - { - if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { - mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated2dMftTracks, perColTracks, perColReassociated2dTracks, mixedEvent, false, bcs); - } else { - mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated2dMftTracks, perColTracks, perColReassociated2dTracks, mixedEventTpcMft, false, bcs); - } - } - PROCESS_SWITCH(HfTaskFlow, processMixedTpcMftChChReassociated2d, "DATA : Process mixed-event correlations for TPC-MFT h-h case", false); + // void processMixedTpcMftChChReassociated2d(FilteredCollisionsWSelMult const& collisions, + // FilteredTracksWDcaSel const& tracks, + // FilteredMftTracks const& /*mftTracks*/, + // soa::SmallGroups const& reassociated2dMftTracks, + // aod::BCsWithTimestamps const& bcs) + // { + // if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + // mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated2dMftTracks, perColTracks, perColReassociated2dTracks, mixedEvent, false, bcs); + // } else { + // mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated2dMftTracks, perColTracks, perColReassociated2dTracks, mixedEventTpcMft, false, bcs); + // } + // } + // PROCESS_SWITCH(HfTaskFlow, processMixedTpcMftChChReassociated2d, "DATA : Process mixed-event correlations for TPC-MFT h-h case", false); void processMixedTpcMftChChReassociated3d(FilteredCollisionsWSelMult const& collisions, FilteredTracksWDcaSel const& tracks, @@ -4284,9 +4402,9 @@ struct HfTaskFlow { aod::BCsWithTimestamps const& bcs) { if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { - mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated3dMftTracks, perColTracks, perColReassociated3dTracks, mixedEvent, false, bcs); + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated3dMftTracks, perColTracks, perColReassociated3dTracks, mixedEvent, false, bcs); } else { - mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated3dMftTracks, perColTracks, perColReassociated3dTracks, mixedEventTpcMft, false, bcs); + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated3dMftTracks, perColTracks, perColReassociated3dTracks, mixedEventTpcMft, false, bcs); } } PROCESS_SWITCH(HfTaskFlow, processMixedTpcMftChChReassociated3d, "DATA : Process mixed-event correlations for TPC-MFT h-h case", false); @@ -4298,9 +4416,9 @@ struct HfTaskFlow { aod::BCsWithTimestamps const& bcs) { if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { - mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated2dMftTracks, perColTracks, perColReassociated2dTracks, mixedEvent, true, bcs); + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated2dMftTracks, perColTracks, perColReassociated2dTracks, mixedEvent, true, bcs); } else { - mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated2dMftTracks, perColTracks, perColReassociated2dTracks, mixedEventTpcMft, true, bcs); + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated2dMftTracks, perColTracks, perColReassociated2dTracks, mixedEventTpcMft, true, bcs); } } PROCESS_SWITCH(HfTaskFlow, processMixedTpcMftChChNonAmbiguous, "DATA : Process mixed-event correlations for TPC-MFT h-h case", false); @@ -4443,20 +4561,20 @@ struct HfTaskFlow { } PROCESS_SWITCH(HfTaskFlow, processMixedMftFt0aChCh, "DATA : Process mixed-event correlations for MFT-FT0-A h-h case", false); - void processMixedMftFt0aChChReassociated2d(FilteredCollisionsWSelMult const& collisions, - FilteredMftTracks const& /*mftTracks*/, - FilteredTracksWDcaSel const& tracksTpc, - soa::SmallGroups const& reassociated2dMftTracks, - aod::FT0s const& ft0s, - aod::BCsWithTimestamps const& bcs) - { - if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { - mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated2dMftTracks, ft0s, perColReassociated2dTracks, perColReassociated2dTracks, mixedEvent, false, bcs); - } else { - mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated2dMftTracks, ft0s, perColReassociated2dTracks, perColReassociated2dTracks, mixedEventMftFt0a, false, bcs); - } - } - PROCESS_SWITCH(HfTaskFlow, processMixedMftFt0aChChReassociated2d, "DATA : Process mixed-event correlations for MFT-FT0-A h-h case", false); + // void processMixedMftFt0aChChReassociated2d(FilteredCollisionsWSelMult const& collisions, + // FilteredMftTracks const& /*mftTracks*/, + // FilteredTracksWDcaSel const& tracksTpc, + // soa::SmallGroups const& reassociated2dMftTracks, + // aod::FT0s const& ft0s, + // aod::BCsWithTimestamps const& bcs) + // { + // if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + // mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated2dMftTracks, ft0s, perColReassociated2dTracks, perColReassociated2dTracks, mixedEvent, false, bcs); + // } else { + // mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated2dMftTracks, ft0s, perColReassociated2dTracks, perColReassociated2dTracks, mixedEventMftFt0a, false, bcs); + // } + // } + // PROCESS_SWITCH(HfTaskFlow, processMixedMftFt0aChChReassociated2d, "DATA : Process mixed-event correlations for MFT-FT0-A h-h case", false); void processMixedMftFt0aChChReassociated3d(FilteredCollisionsWSelMult const& collisions, FilteredMftTracks const& /*mftTracks*/, @@ -4466,9 +4584,9 @@ struct HfTaskFlow { aod::BCsWithTimestamps const& bcs) { if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { - mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated3dMftTracks, ft0s, perColReassociated3dTracks, perColReassociated3dTracks, mixedEvent, false, bcs); + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated3dMftTracks, ft0s, perColReassociated3dTracks, perColReassociated3dTracks, mixedEvent, false, bcs); } else { - mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated3dMftTracks, ft0s, perColReassociated3dTracks, perColReassociated3dTracks, mixedEventMftFt0a, false, bcs); + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated3dMftTracks, ft0s, perColReassociated3dTracks, perColReassociated3dTracks, mixedEventMftFt0a, false, bcs); } } PROCESS_SWITCH(HfTaskFlow, processMixedMftFt0aChChReassociated3d, "DATA : Process mixed-event correlations for MFT-FT0-A h-h case", false); @@ -4481,9 +4599,9 @@ struct HfTaskFlow { aod::BCsWithTimestamps const& bcs) { if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { - mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated2dMftTracks, ft0s, perColReassociated2dTracks, perColReassociated2dTracks, mixedEvent, true, bcs); + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated2dMftTracks, ft0s, perColReassociated2dTracks, perColReassociated2dTracks, mixedEvent, true, bcs); } else { - mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated2dMftTracks, ft0s, perColReassociated2dTracks, perColReassociated2dTracks, mixedEventMftFt0a, true, bcs); + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated2dMftTracks, ft0s, perColReassociated2dTracks, perColReassociated2dTracks, mixedEventMftFt0a, true, bcs); } } PROCESS_SWITCH(HfTaskFlow, processMixedMftFt0aChChNonAmbiguous, "DATA : Process mixed-event correlations for MFT-FT0-A h-h case", false); @@ -4546,12 +4664,41 @@ struct HfTaskFlow { // MONTE-CARLO // =================================================================================================================================================================================================================================================================== - void processMixedMcGen(FilteredMcCollisionsWMult const& mcCollisions, + void processMixedTpcMftChChReassociated3dMcReconstructed(FilteredCollisionsWSelMult const& collisions, + FilteredTracksWDcaSel const& tracks, + FilteredMftTracksWCollsMcLabels const& /*mftTracks*/, + soa::SmallGroups> const& reassociated3dMftTracks, + aod::BCsWithTimestamps const& bcs) + { + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated3dMftTracks, perColTracks, perColReassociated3dTracks, mixedEvent, false, bcs); + } else { + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated3dMftTracks, perColTracks, perColReassociated3dTracks, mixedEventTpcMft, false, bcs); + } + } + PROCESS_SWITCH(HfTaskFlow, processMixedTpcMftChChReassociated3dMcReconstructed, "MC Rec : Process mixed-event correlations for TPC-MFT h-h case", false); + + void processMixedMftFt0aChChReassociated3dMcReconstructed(FilteredCollisionsWSelMult const& collisions, + FilteredMftTracksWCollsMcLabels const& /*mftTracks*/, + FilteredTracksWDcaSel const& tracksTpc, + soa::SmallGroups> const& reassociated3dMftTracks, + aod::FT0s const& ft0s, + aod::BCsWithTimestamps const& bcs) + { + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated3dMftTracks, ft0s, perColReassociated3dTracks, perColReassociated3dTracks, mixedEvent, false, bcs); + } else { + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated3dMftTracks, ft0s, perColReassociated3dTracks, perColReassociated3dTracks, mixedEventMftFt0a, false, bcs); + } + } + PROCESS_SWITCH(HfTaskFlow, processMixedMftFt0aChChReassociated3dMcReconstructed, "MC Rec : Process mixed-event correlations for MFT-FT0-A h-h case", false); + + void processMixedMcGen(FilteredMcCollisionsWMultWCollsExtra const& mcCollisions, FilteredMcParticles const& mcParticles, SmallGroupMcCollisions const& collisions) { - auto getTracksSize = [&mcParticles, this](soa::Join::iterator const& mcCollision) { + auto getTracksSize = [&mcParticles, this](FilteredMcCollisionsWMultWCollsExtra::iterator const& mcCollision) { auto associatedTracks = mcParticles.sliceByCached(o2::aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), this->cache); auto multiplicity = 0; if (configCollision.useMultiplicityFromTracks) { @@ -4561,56 +4708,86 @@ struct HfTaskFlow { } } } else { - multiplicity = mcCollision.multMCPVz(); + multiplicity = mcCollision.multMCFT0C(); } return multiplicity; }; using MixedBinning = FlexibleBinningPolicy, aod::mccollision::PosZ, decltype(getTracksSize)>; - MixedBinning const binningOnVtxAndMult{{getTracksSize}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}, true}; + MixedBinning const binningOnVtxAndMult{{getTracksSize}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); - Pair pairs{binningOnVtxAndMult, configTask.nMixedEvents, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + Pair pairs{binningOnVtxAndMult, configTask.nMixedEvents, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto it = pairs.begin(); it != pairs.end(); it++) { auto& [collision1, tracks1, collision2, tracks2] = *it; - auto multiplicityCollision1 = 0; - auto multiplicityCollision2 = 0; + auto groupedCollisions1 = collisions.sliceBy(collisionPerMcCollision, collision1.globalIndex()); + auto groupedCollisions2 = collisions.sliceBy(collisionPerMcCollision, collision2.globalIndex()); + if (groupedCollisions1.size() == 0) { + continue; + } + if (groupedCollisions2.size() == 0) { + continue; + } + + float multiplicityCollision1 = 0.f; + float multiplicityCollision2 = 0.f; if (configCollision.useMultiplicityFromTracks) { - for (const auto& track : tracks1) { - if (std::abs(track.eta()) < configCentral.etaCentralTrackMax && track.pt() >= configCentral.ptCentralTrackMin && track.pt() <= configCentral.ptCentralTrackMax) { - multiplicityCollision1 += 1; - } - } - for (const auto& track : tracks2) { - if (std::abs(track.eta()) < configCentral.etaCentralTrackMax && track.pt() >= configCentral.ptCentralTrackMin && track.pt() <= configCentral.ptCentralTrackMax) { - multiplicityCollision2 += 1; - } - } + // for (const auto& track : tracks1) { + // if (std::abs(track.eta()) < configCentral.etaCentralTrackMax && track.pt() >= configCentral.ptCentralTrackMin && track.pt() <= configCentral.ptCentralTrackMax) { + // multiplicityCollision1 += 1; + // } + // } + // for (const auto& track : tracks2) { + // if (std::abs(track.eta()) < configCentral.etaCentralTrackMax && track.pt() >= configCentral.ptCentralTrackMin && track.pt() <= configCentral.ptCentralTrackMax) { + // multiplicityCollision2 += 1; + // } + // } + multiplicityCollision1 = getTracksSize(collision1); + multiplicityCollision2 = getTracksSize(collision2); + registry.fill(HIST("MC/hMixedMultiplicityTracksSize_coll1"), multiplicityCollision1); + registry.fill(HIST("MC/hMixedMultiplicityTracksSize_coll2"), multiplicityCollision2); } else { - multiplicityCollision1 = collision1.multMCPVz(); - multiplicityCollision2 = collision2.multMCPVz(); + multiplicityCollision1 = collision1.multMCFT0C(); + multiplicityCollision2 = collision2.multMCFT0C(); + registry.fill(HIST("MC/hMixedMultiplicity_multMCFT0C_coll1"), multiplicityCollision1); + registry.fill(HIST("MC/hMixedMultiplicity_multMCFT0C_coll2"), multiplicityCollision2); } - if ((multiplicityCollision1 < configCollision.minMultiplicity || multiplicityCollision1 >= configCollision.maxMultiplicity)) { - continue; - } - if ((multiplicityCollision2 < configCollision.minMultiplicity || multiplicityCollision2 >= configCollision.maxMultiplicity)) { - continue; - } + float centralityCollision1 = 999.f; + float centralityCollision2 = 999.f; + centralityCollision1 = getCentralityEstimator(collision1, false); + centralityCollision2 = getCentralityEstimator(collision2, false); + registry.fill(HIST("MC/hMixedCentrality_coll1"), centralityCollision1); + registry.fill(HIST("MC/hMixedCentrality_coll2"), centralityCollision2); - auto groupedCollisions1 = collisions.sliceBy(collisionPerMcCollision, collision1.globalIndex()); - auto groupedCollisions2 = collisions.sliceBy(collisionPerMcCollision, collision2.globalIndex()); - if (groupedCollisions1.size() == 0) { - return; + if (configCollision.useCentrality) { + if (centralityCollision1 < configCollision.minCentrality || centralityCollision1 >= configCollision.maxCentrality) { + continue; + } + if (centralityCollision2 < configCollision.minCentrality || centralityCollision2 >= configCollision.maxCentrality) { + continue; + } } - if (groupedCollisions2.size() == 0) { - return; + + registry.fill(HIST("MC/hMixedCentralityUsed_coll1"), centralityCollision1); + registry.fill(HIST("MC/hMixedCentralityUsed_coll2"), centralityCollision2); + + if (!configCollision.useCentrality) { + if ((multiplicityCollision1 < configCollision.minMultiplicity || multiplicityCollision1 >= configCollision.maxMultiplicity)) { + continue; + } + if ((multiplicityCollision2 < configCollision.minMultiplicity || multiplicityCollision2 >= configCollision.maxMultiplicity)) { + continue; + } } + registry.fill(HIST("MC/hMixedMultiplicityUsed_coll1"), multiplicityCollision1); + registry.fill(HIST("MC/hMixedMultiplicityUsed_coll2"), multiplicityCollision2); + mixedEvent->fillEvent(multiplicityCollision1, CorrelationContainer::kCFStepAll); fillCorrelationsMonteCarlo(mixedEvent, CorrelationContainer::CFStep::kCFStepAll, tracks1, tracks2, multiplicityCollision1, collision1.posZ(), false); } @@ -4666,7 +4843,7 @@ struct HfTaskFlow { if (configMft.useMftPhiCut) { if (particle.phi() < configMft.phiMftTrackMax && particle.phi() > configMft.phiMftTrackMin && - !(particle.phi() > configMft.phiMftTrackMiddleMin && particle.phi() < configMft.phiMftTrackMiddleMax)) { + (particle.phi() <= configMft.phiMftTrackMiddleMin || particle.phi() >= configMft.phiMftTrackMiddleMax)) { if (hasReconstructedCollision) { registry.fill(HIST("MC/hEfficiencyAssociated"), particle.pt(), particle.eta(), mcCollision.posZ()); } @@ -4706,7 +4883,7 @@ struct HfTaskFlow { } auto groupedreassociated3dMftTracks = reassociated3dMftTracks.sliceBy(perColReassociated3dTracks, reconstructedCollision.globalIndex()); - for (const auto& reassociated3dMftTrack : reassociated3dMftTracks) { + for (const auto& reassociated3dMftTrack : groupedreassociated3dMftTracks) { if (!reassociated3dMftTrack.has_mcParticle()) { continue; } diff --git a/PWGHF/HFC/Utils/utilsCorrelations.h b/PWGHF/HFC/Utils/utilsCorrelations.h index 0f5fea00a9c..884be2d1704 100644 --- a/PWGHF/HFC/Utils/utilsCorrelations.h +++ b/PWGHF/HFC/Utils/utilsCorrelations.h @@ -18,6 +18,7 @@ #include "PWGHF/Core/DecayChannels.h" +#include "Common/Core/RecoDecay.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" @@ -29,6 +30,9 @@ #include #include +#include +#include +#include namespace o2::analysis::hf_correlations { @@ -227,5 +231,62 @@ int findLeadingParticleMcGen(TMcParticles const& mcParticles, T1 const etaTrackM } return leadingParticle.globalIndex(); } + +// ======= Beauty-mother information of non-prompt charm hadrons ============ +constexpr float PtBMotherDefault{-1.f}; // pT(B mother) returned when the charm hadron is not from beauty +constexpr float DeltaPhiBMotherDefault{-999.f}; // phi(charm hadron) - phi(B mother) returned when the charm hadron is not from beauty + +/// Gets the generated beauty mother of a reconstructed charm-hadron candidate matched to a non-prompt charm hadron +/// \tparam TTracks is the track table with MC labels, used to access the candidate prongs +/// \param candidate is the reconstructed candidate (with originMcRec column) +/// \param mcParticles is the table of MC particles +/// \param pdgCharmHadron is the PDG code of the charm hadron (e.g. Pdg::kDPlus), antiparticles are accepted +/// \param depthMaxDaughters is the max. depth of the candidate prong in the charm-hadron decay chain (> 1 for resonant channels) +/// \return pair (pT of the B mother, phi(candidate, reco) - phi(B mother, gen) in [-pi, pi)), default values if the candidate is not from beauty +template +std::pair getBMotherInfoMcRec(TCandidate const& candidate, TMcParticles const& mcParticles, int pdgCharmHadron, int8_t depthMaxDaughters = 2) +{ + std::pair bMotherInfo{PtBMotherDefault, DeltaPhiBMotherDefault}; + if (candidate.originMcRec() != RecoDecay::OriginType::NonPrompt) { + return bMotherInfo; + } + // retrieve the generated charm hadron from the MC particle of one of its daughters + auto prong0 = candidate.template prong0_as(); + if (!prong0.has_mcParticle()) { + return bMotherInfo; + } + auto prong0McPart = prong0.template mcParticle_as(); + int const indexCharmHadronGen = RecoDecay::getMother(mcParticles, prong0McPart, pdgCharmHadron, true, nullptr, depthMaxDaughters); + if (indexCharmHadronGen < 0) { + return bMotherInfo; + } + // same procedure used in the candidate creators to fill PtBhadMotherPart + auto charmHadronGen = mcParticles.rawIteratorAt(indexCharmHadronGen - mcParticles.offset()); + std::vector idxBhadMothers{}; + if (RecoDecay::getCharmHadronOrigin(mcParticles, charmHadronGen, false, &idxBhadMothers) != RecoDecay::OriginType::NonPrompt || idxBhadMothers.empty()) { + return bMotherInfo; + } + auto bHadMother = mcParticles.rawIteratorAt(idxBhadMothers[0] - mcParticles.offset()); + bMotherInfo.first = bHadMother.pt(); + bMotherInfo.second = RecoDecay::constrainAngle(candidate.phi() - bHadMother.phi(), -o2::constants::math::PIHalf); + return bMotherInfo; +} + +/// Gets the beauty mother of a generated charm hadron +/// \param particle is the generated charm hadron (with originMcGen and idxBhadMotherPart columns) +/// \param mcParticles is the table of MC particles containing the B mother +/// \return pair (pT of the B mother, phi(charm hadron) - phi(B mother) in [-pi, pi)), default values if the charm hadron is not from beauty +template +std::pair getBMotherInfoMcGen(TParticle const& particle, TMcParticles const& mcParticles) +{ + std::pair bMotherInfo{PtBMotherDefault, DeltaPhiBMotherDefault}; + if (particle.originMcGen() != RecoDecay::OriginType::NonPrompt || particle.idxBhadMotherPart() < 0) { + return bMotherInfo; + } + auto bHadMother = mcParticles.rawIteratorAt(particle.idxBhadMotherPart() - mcParticles.offset()); + bMotherInfo.first = bHadMother.pt(); + bMotherInfo.second = RecoDecay::constrainAngle(particle.phi() - bHadMother.phi(), -o2::constants::math::PIHalf); + return bMotherInfo; +} } // namespace o2::analysis::hf_correlations #endif // PWGHF_HFC_UTILS_UTILSCORRELATIONS_H_ diff --git a/PWGHF/HFC/Utils/utilsFlow.h b/PWGHF/HFC/Utils/utilsFlow.h new file mode 100644 index 00000000000..8d345728769 --- /dev/null +++ b/PWGHF/HFC/Utils/utilsFlow.h @@ -0,0 +1,274 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file utilsFlow.h +/// \brief Utilities for HFC analyses +/// \author Marcello Di Costanzo + +#ifndef PWGHF_HFC_UTILS_UTILSFLOW_H_ +#define PWGHF_HFC_UTILS_UTILSFLOW_H_ + +#include "PWGHF/Core/CentralityEstimation.h" +#include "PWGHF/Core/HfHelper.h" +#include "PWGHF/Utils/utilsEvSelHf.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace o2::hfc_utils_flow +{ + +enum TriggerChannels { + DplusToPiKPi = 0, + DsToKKPi, + DsToPiKK, + D0ToPiK, + D0ToKPi, + LcToPKPi, + LcToPiKP, + Hadron +}; + +enum AssTracksTypes { + Pi = 0, + Ka, + Pr, + El, + Mu, + NAssTracksTypes +}; + +class HfcFlowHelper +{ + + public: + HfcFlowHelper() = default; + + /// Initialize the HFC flow helper + /// \param ccdbService is the CCDB service + /// \param histRegistry is the histogram registry + /// \param mlClasses are the machine learning classes + /// \param maxRapidity is the maximum rapidity + void init(std::vector mlClasses, + float maxRapidity) + { + this->classMl = std::move(mlClasses); + this->yCandMax = maxRapidity; + LOG(info) << "HFC flow helper initialized"; + } + + /// Get charm hadron candidate mass + /// \param candidate is the charm hadron candidate + template + double getCandMass(const TCand& candidate) + { + if constexpr (CandType == TriggerChannels::DsToKKPi) { + return HfHelper::invMassDsToKKPi(candidate); + } + if constexpr (CandType == TriggerChannels::DsToPiKK) { + return HfHelper::invMassDsToPiKK(candidate); + } + if constexpr (CandType == TriggerChannels::DplusToPiKPi) { + return HfHelper::invMassDplusToPiKPi(candidate); + } + if constexpr (CandType == TriggerChannels::D0ToPiK) { + return HfHelper::invMassD0ToPiK(candidate); + } + if constexpr (CandType == TriggerChannels::D0ToKPi) { + return HfHelper::invMassD0barToKPi(candidate); + } + if constexpr (CandType == TriggerChannels::LcToPKPi) { + return HfHelper::invMassLcToPKPi(candidate); + } + if constexpr (CandType == TriggerChannels::LcToPiKP) { + return HfHelper::invMassLcToPiKP(candidate); + } + return -1.; + } + + /// Get charm hadron bdt scores + /// \param candidate is the charm hadron candidate + template + std::array getCandMlScores(const TCand& candidate) + { + std::array outputMl{-999.f, -999.f}; + if constexpr (CandType == TriggerChannels::DsToKKPi) { + for (unsigned int iclass = 0; iclass < classMl.size(); iclass++) { + outputMl[iclass] = candidate.mlProbDsToKKPi()[classMl.at(iclass)]; + } + } + if constexpr (CandType == TriggerChannels::DsToPiKK) { + for (unsigned int iclass = 0; iclass < classMl.size(); iclass++) { + outputMl[iclass] = candidate.mlProbDsToPiKK()[classMl.at(iclass)]; + } + } + if constexpr (CandType == TriggerChannels::DplusToPiKPi) { + for (unsigned int iclass = 0; iclass < classMl.size(); iclass++) { + outputMl[iclass] = candidate.mlProbDplusToPiKPi()[classMl.at(iclass)]; + } + } + if constexpr (CandType == TriggerChannels::D0ToPiK) { + for (unsigned int iclass = 0; iclass < classMl.size(); iclass++) { + outputMl[iclass] = candidate.mlProbD0()[classMl.at(iclass)]; + } + } + if constexpr (CandType == TriggerChannels::D0ToKPi) { + for (unsigned int iclass = 0; iclass < classMl.size(); iclass++) { + outputMl[iclass] = candidate.mlProbD0bar()[classMl.at(iclass)]; + } + } + if constexpr (CandType == TriggerChannels::LcToPKPi) { + for (unsigned int iclass = 0; iclass < classMl.size(); iclass++) { + outputMl[iclass] = candidate.mlProbLcToPKPi()[classMl.at(iclass)]; + } + } + if constexpr (CandType == TriggerChannels::LcToPiKP) { + for (unsigned int iclass = 0; iclass < classMl.size(); iclass++) { + outputMl[iclass] = candidate.mlProbLcToPiKP()[classMl.at(iclass)]; + } + } + return outputMl; + } + + /// Cut on rapidity of the candidate + /// \param candidate is the charm hadron candidate + /// \return true if the candidate passes the rapidity cut, false otherwise + template + bool cutCandRapidity(const TCand& candidate) + { + double y = 0.0; + if constexpr (CandType == TriggerChannels::DsToKKPi || CandType == TriggerChannels::DsToPiKK) { + y = HfHelper::yDs(candidate); + } else if constexpr (CandType == TriggerChannels::DplusToPiKPi) { + y = HfHelper::yDplus(candidate); + } else if constexpr (CandType == TriggerChannels::D0ToPiK || CandType == TriggerChannels::D0ToKPi) { + y = HfHelper::yD0(candidate); + } else if constexpr (CandType == TriggerChannels::LcToPKPi || CandType == TriggerChannels::LcToPiKP) { + y = HfHelper::yLc(candidate); + } else { + return true; + } + return std::fabs(y) < yCandMax; + } + + /// Runtime dispatch to the compile-time checkCollision + /// \param collision is the collision + /// \param centEst is the centrality estimator + /// \param cent is the centrality value + /// \param mult is the multiplicity value + template + bool selectGenCollision(o2::hf_evsel::HfEventSelectionMc& hfEvSelMc, + GenColl const& genColl, + RecoCollSlice const& recoCollsSlice, + o2::hf_centrality::CentralityEstimator centEst, + float& cent, + float& mult) + { + int nSplitColl = recoCollsSlice.size(); + o2::hf_evsel::HfCollisionRejectionMask collRejMask{}; + switch (centEst) { + case o2::hf_centrality::CentralityEstimator::FT0A: + collRejMask = hfEvSelMc.getHfMcCollisionRejectionMask(genColl, recoCollsSlice, cent); + mult = o2::hf_centrality::getCentralityGenColl(recoCollsSlice, o2::hf_centrality::CentralityEstimator::FT0A); + hfEvSelMc.fillHistograms(genColl, collRejMask, nSplitColl); + break; + case o2::hf_centrality::CentralityEstimator::FT0C: + collRejMask = hfEvSelMc.getHfMcCollisionRejectionMask(genColl, recoCollsSlice, cent); + mult = o2::hf_centrality::getCentralityGenColl(recoCollsSlice, o2::hf_centrality::CentralityEstimator::FT0C); + hfEvSelMc.fillHistograms(genColl, collRejMask, nSplitColl); + break; + case o2::hf_centrality::CentralityEstimator::FT0M: + collRejMask = hfEvSelMc.getHfMcCollisionRejectionMask(genColl, recoCollsSlice, cent); + mult = o2::hf_centrality::getCentralityGenColl(recoCollsSlice, o2::hf_centrality::CentralityEstimator::FT0M); + hfEvSelMc.fillHistograms(genColl, collRejMask, nSplitColl); + break; + case o2::hf_centrality::CentralityEstimator::FV0A: + collRejMask = hfEvSelMc.getHfMcCollisionRejectionMask(genColl, recoCollsSlice, cent); + mult = o2::hf_centrality::getCentralityGenColl(recoCollsSlice, o2::hf_centrality::CentralityEstimator::FV0A); + hfEvSelMc.fillHistograms(genColl, collRejMask, nSplitColl); + break; + case o2::hf_centrality::CentralityEstimator::None: + collRejMask = hfEvSelMc.getHfMcCollisionRejectionMask(genColl, recoCollsSlice, cent); + mult = -1.f; + hfEvSelMc.fillHistograms(genColl, collRejMask, nSplitColl); + break; + default: + LOG(fatal) << "Centrality estimator not recognized for collision selection"; + std::abort(); + } + + return (collRejMask == 0); + } + + /// Apply event selection on reconstructed collisions + /// \param collision is the collision + /// \param centEst is the centrality estimator + /// \param cent is the centrality value + /// \param mult is the multiplicity value + template + bool selectRecoCollision(o2::hf_evsel::HfEventSelection& hfEvSel, + o2::framework::Service& ccdb, + o2::framework::HistogramRegistry& registry, + Coll const& collision, + o2::hf_centrality::CentralityEstimator centEst, + float& cent, + float& mult) + { + o2::hf_evsel::HfCollisionRejectionMask collRejMask{}; + switch (centEst) { + case o2::hf_centrality::CentralityEstimator::FT0A: + collRejMask = hfEvSel.getHfCollisionRejectionMask(collision, cent, ccdb, registry); + mult = collision.multFT0A(); + break; + case o2::hf_centrality::CentralityEstimator::FT0C: + collRejMask = hfEvSel.getHfCollisionRejectionMask(collision, cent, ccdb, registry); + mult = collision.multFT0C(); + break; + case o2::hf_centrality::CentralityEstimator::FT0M: + collRejMask = hfEvSel.getHfCollisionRejectionMask(collision, cent, ccdb, registry); + mult = collision.multFT0M(); + break; + case o2::hf_centrality::CentralityEstimator::FV0A: + collRejMask = hfEvSel.getHfCollisionRejectionMask(collision, cent, ccdb, registry); + mult = collision.multFV0A(); + break; + case o2::hf_centrality::CentralityEstimator::None: + collRejMask = hfEvSel.getHfCollisionRejectionMask(collision, cent, ccdb, registry); + mult = -1.f; + break; + default: + LOG(fatal) << "Centrality estimator not recognized for collision selection"; + std::abort(); + } + + hfEvSel.fillHistograms(collision, collRejMask, cent); + + return (collRejMask == 0); + } + + private: + std::vector classMl; + float yCandMax{-999.f}; +}; + +} // namespace o2::hfc_utils_flow +#endif // PWGHF_HFC_UTILS_UTILSFLOW_H_ diff --git a/PWGHF/HFL/Tasks/taskElectronWeakBoson.cxx b/PWGHF/HFL/Tasks/taskElectronWeakBoson.cxx index 78f9ac10578..6fa6239f900 100644 --- a/PWGHF/HFL/Tasks/taskElectronWeakBoson.cxx +++ b/PWGHF/HFL/Tasks/taskElectronWeakBoson.cxx @@ -125,7 +125,10 @@ struct HfTaskElectronWeakBoson { Configurable applySel8{"applySel8", true, "Apply sel8 filter or not"}; // CCDB service configurations - Configurable cfgCCDBPath{"cfgCCDBPath", "Users/m/mpuccio/EventFiltering/OTS/", "Path to CCDB for trigger data"}; + // Zorro's own default (Common/Core/Zorro.h: mBaseCCDBPath = "EventFiltering/Zorro/"). + // The old value "Users/m/mpuccio/EventFiltering/OTS/" is a personal area from early Run 3 + // and no longer holds FilterCounters, so Zorro::initCCDB aborts the job on it. + Configurable cfgCCDBPath{"cfgCCDBPath", "EventFiltering/Zorro/", "Path to CCDB for trigger data"}; Configurable ccdbPathGrpMag{"ccdbPathGrpMag", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object (Run 3)"}; // KFParticle @@ -157,6 +160,35 @@ struct HfTaskElectronWeakBoson { Configurable useUEsub{"useUEsub", true, "apply UE subtraction in isolation"}; } ueCfg; + // W->e - h correlation. Grouped so that the three options cost one task member instead of + // three: the struct is close to the 99 the framework allows. + struct : ConfigurableGroup { + Configurable enableWeHadron{"enableWeHadron", false, "fill the isolated-electron - hadron correlation"}; + Configurable ptWeMin{"ptWeMin", 20.0f, "minimum pT of the trigger electron"}; + Configurable vetoZeeElectron{"vetoZeeElectron", true, "reject trigger electrons that build a Z->ee candidate"}; + } weCfg; + + // Systematics by cut variation (method C): one extra THnSparse axis whose bin 0 is the + // nominal selection and bins 1..N the variations. + struct : ConfigurableGroup { + Configurable enableSystematics{"enableSystematics", false, "add the cut-variation axis"}; + // associated hadron, checked against dcaxyTrk / nclusterTPC (crossed rows) / nclusterITS + Configurable> varDcaxyMax{"varDcaxyMax", {}, "variations of dcaxyMax"}; + Configurable> varNclCrossTpcMin{"varNclCrossTpcMin", {}, "variations of nclcrossTpcMin"}; + Configurable> varNclItsMin{"varNclItsMin", {}, "variations of nclItsMin"}; + // W trigger electron identification + Configurable> varEopMin{"varEopMin", {}, "variations of eopMin"}; + Configurable> varEopMax{"varEopMax", {}, "variations of eopMax"}; + Configurable> varNsigTpcMin{"varNsigTpcMin", {}, "variations of nsigTpcMin"}; + Configurable> varNsigTpcMax{"varNsigTpcMax", {}, "variations of nsigTpcMax"}; + Configurable> varM02Min{"varM02Min", {}, "variations of m02Min"}; + Configurable> varM02Max{"varM02Max", {}, "variations of m02Max"}; + // Z candidate. Two-sided: reconstructedZ holds every KF chi2-passing pair at any mass, + // so a wider window really adds candidates instead of being a no-op. + Configurable> varMassZMin{"varMassZMin", {}, "variations of massZMin"}; + Configurable> varMassZMax{"varMassZMax", {}, "variations of massZMax"}; + } sysCfg; + // MC : efficiency of the associated hadrons in the Z-h correlation. struct : ConfigurableGroup { std::string prefix = "mcEfficiency"; // JSON group name @@ -232,9 +264,7 @@ struct HfTaskElectronWeakBoson { ConfigurableAxis confaxisIsoEnergy{"confaxisIsoEnergy", {255, 0, 2.0}, "E_{iso}"}; ConfigurableAxis confaxisIsoMomentum{"confaxisIsoMomentum", {255, 0, 2.0}, "p_{iso}"}; ConfigurableAxis confaxisIsoTrack{"confaxisIsoTrack", {25, -0.5, 24.5}, "Isolation Track"}; - // raw TPC dE/dx (arbitrary units, MIP around 50) -- used by hTHnElectrons - ConfigurableAxis confaxisDedxTrack{"confaxisDedxTrack", {200, 0, 200}, "dE/dx"}; - // TPC n-sigma of the associated hadrons -- used by hZHadronDphi + ConfigurableAxis confaxisEtCluster{"confaxisEtCluster", {100, 0, 100}, "E_{T} (GeV)"}; ConfigurableAxis confaxisNsigmaHadron{"confaxisNsigmaHadron", {200, -10, 10}, "n#sigma^{#pi}_{TPC}"}; ConfigurableAxis confaxisInvMassZgamma{"confaxisInvMassZgamma", {150, 0, 150}, "M_{ee} (GeV/c^{2})"}; ConfigurableAxis confaxisInvMassZ{"confaxisInvMassZ", {130, 20, 150}, "M_{ee} (GeV/c^{2})"}; @@ -294,7 +324,7 @@ struct HfTaskElectronWeakBoson { const AxisSpec axisNsigma{100, -5, 5, "N#sigma"}; const AxisSpec axisNsigmaZneg{100, -5, 5, "N#sigma_{pos}"}; const AxisSpec axisNsigmaZpos{100, -5, 5, "N#sigma_{neg}"}; - const AxisSpec axisDedx{confaxisDedxTrack, "dE/dx"}; + const AxisSpec axisEtCluster{confaxisEtCluster, "E_{T} (GeV)"}; const AxisSpec axisNsigmaHadron{confaxisNsigmaHadron, "n#sigma^{#pi}_{TPC}"}; const AxisSpec axisSignHadron{2, -2, 2, "charge sign (hadron)"}; const AxisSpec axisNMatch{10, -0.5, 9.5, "N_{cluster} within rMatchMax"}; @@ -359,6 +389,8 @@ struct HfTaskElectronWeakBoson { registry.add("hPt", "track pt", kTH1F, {axisPt}); registry.add("hTPCNsigma", "TPC electron Nsigma", kTH2F, {{axisPt}, {axisNsigma}}); registry.add("hEnergy", "EMC cluster energy", kTH1F, {axisE}); + registry.add("hEnergyT", "EMC cluster transverse energy E/cosh(#eta)", kTH1F, {axisE}); + registry.add("hEnergyEt", "EMC cluster energy vs. transverse energy", kTH2F, {{axisE}, {axisE}}); registry.add("hEnergyMult", "EMC cluster energy vs Multiplicity", kTH2F, {{axisCentrality}, {axisE}}); registry.add("hEnergyNcell", "EMC cluster energy and cell", kTH2F, {{axisE}, {axisNcell}}); registry.add("hTrMatchR", "Track EMC Match in radius", kTH2F, {{axisPt}, {axisdR}}); @@ -377,13 +409,41 @@ struct HfTaskElectronWeakBoson { registry.add("hInvMassZeeTrkQAbg", "Track QA for invariant mass for Z", HistType::kTHnSparseF, {axisInvMassZ, axisPtZneg, axisPtZpos, axisDCAxyneg, axisDCAxypos, axisDCAzpos, axisNclsTPCneg, axisNclsTPCpos, axisNclsITSneg, axisNclsITSpos, axisSectorTPCneg, axisSectorTPCneg, axisEopZneg, axisEopZpos, axisIsoEnergyZneg, axisIsoEnergyZpos, axisIsoMomentumZneg, axisIsoMomentumZpos, axisIsoTrackZneg, axisIsoTrackZpos}); registry.add("hInvMassZeeEmcQA", "EMCal QA for invariant mass for Z", HistType::kTHnSparseF, {axisInvMassZ, axisPtZneg, axisPtZpos, axisEopZneg, axisEopZpos, axisM02neg, axisM02pos, axisNsigmaZneg, axisNsigmaZpos, axisIsoEnergyZneg, axisIsoEnergyZpos, axisIsoMomentumZneg, axisIsoMomentumZpos, axisIsoTrackZneg, axisIsoTrackZpos}); registry.add("hInvMassZeeEmcQAbg", "EMCal QA for invariant mass for Z", HistType::kTHnSparseF, {axisInvMassZ, axisPtZneg, axisPtZpos, axisEopZneg, axisEopZpos, axisM02neg, axisM02pos, axisNsigmaZneg, axisNsigmaZpos, axisIsoEnergyZneg, axisIsoEnergyZpos, axisIsoMomentumZneg, axisIsoMomentumZpos, axisIsoTrackZneg, axisIsoTrackZpos}); - registry.add("hTHnElectrons", "electron info", HistType::kTHnSparseF, {axisPt, axisNsigma, axisM02, axisEop, axisIsoEnergy, axisIsoMomentum, axisIsoTrack, axisEta, axisDedx}); + registry.add("hTHnElectrons", "electron info", HistType::kTHnSparseF, {axisPt, axisNsigma, axisM02, axisEop, axisIsoEnergy, axisIsoMomentum, axisIsoTrack, axisEta, axisEtCluster}); registry.add("hTHnTrMatch", "Track EMC Match", HistType::kTHnSparseF, {axisPt, axisdPhi, axisdEta}); registry.add("hNMatchPerTrack", "number of matched clusters within rMatchMax per track", kTH2F, {{axisPt}, {axisNMatch}}); // Z-hadron correlation histograms - registry.add("hZHadronDphi", "Z-hadron #Delta#phi correlation", HistType::kTHnSparseF, {axisCentrality, axisSign, axisPtZ, axisDPhiZh, axisDEtaZh, axisZfrag, axisPtHadron, axisNsigmaHadron, axisSignHadron}); - registry.add("hZptSpectrum", "Z boson p_{T} spectrum", kTH2F, {{axisSign}, {axisPtZ}}); + const int nSysVar = nCutVariations(); + if (sysCfg.enableSystematics) { + const size_t nVar = static_cast(nSysVar - 1); + if (nVar == 0) { + LOGF(warn, "systematics: enableSystematics is on but every variation vector is empty, so the axis has one bin and nothing is varied."); + } + for (const auto& [values, name] : variationVectorsNamed()) { + if (!values->empty() && values->size() != nVar) { + LOGF(fatal, "systematics: %s has %zu entries but there are %zu variations. Give it either %zu entries or none at all.", name, values->size(), nVar, nVar); + } + } + for (size_t i = 0; i < nVar; ++i) { + const int iv = static_cast(i) + 1; + if (variedValue(sysCfg.varDcaxyMax.value, iv, dcaxyMax) > dcaxyMax || + variedValue(sysCfg.varNclCrossTpcMin.value, iv, nclcrossTpcMin) < nclcrossTpcMin || + variedValue(sysCfg.varNclItsMin.value, iv, nclItsMin) < nclItsMin) { + LOGF(warn, "systematics: variation %zu loosens an associated-hadron cut. The container only holds tracks passing the nominal selection, so that variation is identical to the nominal there. Loosen the nominal configurable instead.", i + 1); + } + } + LOGF(info, "systematics: %d bins on the variation axis (bin 0 = nominal)", nSysVar); + } + const AxisSpec axisSysVar{nSysVar, -0.5, nSysVar - 0.5, "cut variation"}; + + registry.add("hZHadronDphi", "Z-hadron #Delta#phi correlation", HistType::kTHnSparseF, {axisCentrality, axisSign, axisPtZ, axisDPhiZh, axisDEtaZh, axisZfrag, axisPtHadron, axisNsigmaHadron, axisSignHadron, axisSysVar}); + registry.add("hZptSpectrum", "Z boson p_{T} spectrum", kTH3F, {{axisSign}, {axisPtZ}, {axisSysVar}}); + + if (weCfg.enableWeHadron) { + registry.add("hWeHadronDphi", "isolated electron - hadron #Delta#phi correlation", HistType::kTHnSparseF, {axisCentrality, axisSign, axisPtZ, axisDPhiZh, axisDEtaZh, axisIsoEnergy, axisIsoMomentum, axisPtHadron, axisNsigmaHadron, axisSignHadron, axisSysVar}); + registry.add("hWeSpectrum", "trigger electrons for the e-h correlation", HistType::kTHnSparseF, {axisCentrality, axisSign, axisPtZ, axisIsoEnergy, axisIsoMomentum, axisSysVar}); + } // hisotgram for EMCal trigger registry.add("hEMCalTrigger", "EMCal trigger", kTH1D, {axisTrigger}); @@ -408,13 +468,13 @@ struct HfTaskElectronWeakBoson { if (doprocessMcRec) { registry.add("hMcRecCollCounter", "reconstructed collisions in MC", kTH1D, {axisCounter}); // numerator of the efficiency, in generated kinematics (use with hMcGenHadron) - registry.add("hMcRecHadronMatchedGenKine", "reconstructed primary charged hadrons, generated kinematics", HistType::kTHnSparseF, {axisCentrality, axisPtHadronMc, axisEtaHadronMc}); + registry.add("hMcRecHadronMatchedGenKine", "reconstructed primary charged hadrons, generated kinematics", HistType::kTHnSparseF, {axisCentrality, axisPtHadronMc, axisEtaHadronMc, axisSysVar}); // same tracks, but in reconstructed kinematics (use to correct in measured bins) - registry.add("hMcRecHadronMatchedRecKine", "reconstructed primary charged hadrons, reconstructed kinematics", HistType::kTHnSparseF, {axisCentrality, axisPtHadronMc, axisEtaHadronMc}); + registry.add("hMcRecHadronMatchedRecKine", "reconstructed primary charged hadrons, reconstructed kinematics", HistType::kTHnSparseF, {axisCentrality, axisPtHadronMc, axisEtaHadronMc, axisSysVar}); // purity : all selected tracks and the non-primary / unlabelled sub-samples - registry.add("hMcRecHadronAll", "all selected associated tracks", HistType::kTHnSparseF, {axisCentrality, axisPtHadronMc, axisEtaHadronMc}); - registry.add("hMcRecHadronSecondary", "selected tracks matched to a non-primary particle", HistType::kTHnSparseF, {axisCentrality, axisPtHadronMc, axisEtaHadronMc}); - registry.add("hMcRecHadronFake", "selected tracks without MC particle", HistType::kTHnSparseF, {axisCentrality, axisPtHadronMc, axisEtaHadronMc}); + registry.add("hMcRecHadronAll", "all selected associated tracks", HistType::kTHnSparseF, {axisCentrality, axisPtHadronMc, axisEtaHadronMc, axisSysVar}); + registry.add("hMcRecHadronSecondary", "selected tracks matched to a non-primary particle", HistType::kTHnSparseF, {axisCentrality, axisPtHadronMc, axisEtaHadronMc, axisSysVar}); + registry.add("hMcRecHadronFake", "selected tracks without MC particle", HistType::kTHnSparseF, {axisCentrality, axisPtHadronMc, axisEtaHadronMc, axisSysVar}); // momentum resolution registry.add("hMcPtGenRec", "generated vs. reconstructed p_{T}", kTH2F, {{axisPtHadronMc}, {axisPtHadronMc}}); } @@ -616,12 +676,12 @@ struct HfTaskElectronWeakBoson { // Outcome of the track - EMCal cluster matching for one track struct HfEmcMatchResult { - int64_t posBestMatch = -1; // cursor position of the closest match inside the slice, -1 if none - double rMin = 999.9; // smallest sqrt(dPhi^2 + dEta^2) over the matched clusters + int64_t posBestMatch = -1; + double rMin = 999.9; double dPhiMin = 999.9; double dEtaMin = 999.9; - int nMatchInRadius = 0; // number of matched clusters inside rMatchMax - bool isMatched = false; // the closest cluster is inside rMatchMax + int nMatchInRadius = 0; + bool isMatched = false; }; template @@ -647,6 +707,9 @@ struct HfTaskElectronWeakBoson { registry.fill(HIST("hTHnTrMatch"), match.template track_as().pt(), dPhi, dEta); registry.fill(HIST("hEMCtime"), cluster.time()); registry.fill(HIST("hEnergy"), cluster.energy()); + double const energyT = cluster.energy() / std::cosh(cluster.eta()); + registry.fill(HIST("hEnergyT"), energyT); + registry.fill(HIST("hEnergyEt"), cluster.energy(), energyT); registry.fill(HIST("hEnergyMult"), centrality, cluster.energy()); if (std::abs(dPhi) <= rMatchMax && std::abs(dEta) <= rMatchMax) { @@ -740,6 +803,267 @@ struct HfTaskElectronWeakBoson { } } + // ------------------------------------------------------------------------- + // Cut variations (systematics). Bin 0 is always the nominal configurables; bin iv>0 reads + // entry iv-1 of each variation vector. Member FUNCTIONS, so none of this costs a task member. + // ------------------------------------------------------------------------- + int nCutVariations() + { + if (!sysCfg.enableSystematics) { + return 1; + } + size_t n = 0; + for (const auto& [values, name] : variationVectorsNamed()) { + n = std::max(n, values->size()); + } + return 1 + static_cast(n); + } + // The eleven variation vectors, paired with their names for the messages in init(). + std::array*, const char*>, 11> variationVectorsNamed() + { + return {{{&sysCfg.varDcaxyMax.value, "varDcaxyMax"}, + {&sysCfg.varNclCrossTpcMin.value, "varNclCrossTpcMin"}, + {&sysCfg.varNclItsMin.value, "varNclItsMin"}, + {&sysCfg.varEopMin.value, "varEopMin"}, + {&sysCfg.varEopMax.value, "varEopMax"}, + {&sysCfg.varNsigTpcMin.value, "varNsigTpcMin"}, + {&sysCfg.varNsigTpcMax.value, "varNsigTpcMax"}, + {&sysCfg.varM02Min.value, "varM02Min"}, + {&sysCfg.varM02Max.value, "varM02Max"}, + {&sysCfg.varMassZMin.value, "varMassZMin"}, + {&sysCfg.varMassZMax.value, "varMassZMax"}}}; + } + // One variation's value of a cut, falling back to the nominal for bin 0 or a short vector. + static float variedValue(const std::vector& values, int iv, float nominal) + { + if (iv <= 0 || static_cast(iv - 1) >= values.size()) { + return nominal; + } + return values[iv - 1]; + } + + // Associated hadron. dcaxyTrk, nclusterTPC (= crossed rows, see where the container is + // filled) and nclusterITS are the three quantities the candidate carries. + bool passHadronVariation(const HfElectronCandidate& hadron, int iv) + { + if (iv == 0) { + return true; // the container already holds the nominal selection + } + if (std::abs(hadron.dcaxyTrk) > variedValue(sysCfg.varDcaxyMax.value, iv, dcaxyMax)) { + return false; + } + if (hadron.nclusterTPC < variedValue(sysCfg.varNclCrossTpcMin.value, iv, nclcrossTpcMin)) { + return false; + } + if (hadron.nclusterITS < variedValue(sysCfg.varNclItsMin.value, iv, nclItsMin)) { + return false; + } + return true; + } + + // Same three cuts on a track, for the MC efficiency: the numerator has to be recomputed with + // the very selection the correlation used, or the correction does not match the data. + template + bool passHadronVariationTrack(TTrack const& track, int iv) + { + if (iv == 0) { + return true; + } + if (std::abs(track.dcaXY()) > variedValue(sysCfg.varDcaxyMax.value, iv, dcaxyMax)) { + return false; + } + if (track.tpcNClsCrossedRows() < variedValue(sysCfg.varNclCrossTpcMin.value, iv, nclcrossTpcMin)) { + return false; + } + if (track.itsNCls() < variedValue(sysCfg.varNclItsMin.value, iv, nclItsMin)) { + return false; + } + return true; + } + + // The Z mass window for one variation. Unlike the associated-hadron cuts this one is + // two-sided: reconstructedZ holds every pair that passed the KF chi2 cut, at any mass, so a + // wider window genuinely adds candidates. iv == 0 uses the nominal configurables. + bool passZVariation(const HfZeeCandidate& zBoson, int iv) + { + if (zBoson.mass < variedValue(sysCfg.varMassZMin.value, iv, massZMin) || + zBoson.mass > variedValue(sysCfg.varMassZMax.value, iv, massZMax)) { + return false; + } + return true; + } + + // W trigger electron. selectedElectronsIso carries no identification cut, so these three can + // be varied in both directions. + bool passElectronVariation(const HfElectronCandidate& ele, int iv) + { + if (ele.eop < variedValue(sysCfg.varEopMin.value, iv, eopMin) || + ele.eop > variedValue(sysCfg.varEopMax.value, iv, eopMax)) { + return false; + } + if (ele.nSigmaTpc < variedValue(sysCfg.varNsigTpcMin.value, iv, nsigTpcMin) || + ele.nSigmaTpc > variedValue(sysCfg.varNsigTpcMax.value, iv, nsigTpcMax)) { + return false; + } + if (ele.m02Emc < variedValue(sysCfg.varM02Min.value, iv, m02Min) || + ele.m02Emc > variedValue(sysCfg.varM02Max.value, iv, m02Max)) { + return false; + } + return true; + } + + // ------------------------------------------------------------------------- + // Z->ee QA. + // ------------------------------------------------------------------------- + template + void fillZeeQaPair(const TCandidate& trackNeg, const TCandidate& trackPos) + { + auto child1 = RecoDecayPtEtaPhi::pVector(trackNeg.pt, trackNeg.eta, trackNeg.phi); + auto child2 = RecoDecayPtEtaPhi::pVector(trackPos.pt, trackPos.eta, trackPos.phi); + double const invMass = RecoDecay::m(std::array{child1, child2}, std::array{o2::constants::physics::MassElectron, o2::constants::physics::MassElectron}); + if (invMass <= zeeQaCfg.massZMinQA) { + return; + } + float const sectorneg = trackNeg.phi / o2::constants::math::SectorSpanRad; + float const sectorpos = trackPos.phi / o2::constants::math::SectorSpanRad; + + if (zeeQaCfg.enableZeeTrkRecoQA) { + if constexpr (IsSignal) { + registry.fill(HIST("hInvMassZeeTrkQA"), invMass, trackNeg.pt, trackPos.pt, trackNeg.dcaxyTrk, trackPos.dcaxyTrk, trackPos.dcazTrk, trackNeg.nclusterTPC, trackPos.nclusterTPC, trackNeg.nclusterITS, trackPos.nclusterITS, sectorneg, sectorpos, trackNeg.eop, trackPos.eop, trackNeg.energyIso, trackPos.energyIso, trackNeg.momIso, trackPos.momIso, trackNeg.ntrackIso, trackPos.ntrackIso); + } else { + registry.fill(HIST("hInvMassZeeTrkQAbg"), invMass, trackNeg.pt, trackPos.pt, trackNeg.dcaxyTrk, trackPos.dcaxyTrk, trackPos.dcazTrk, trackNeg.nclusterTPC, trackPos.nclusterTPC, trackNeg.nclusterITS, trackPos.nclusterITS, sectorneg, sectorpos, trackNeg.eop, trackPos.eop, trackNeg.energyIso, trackPos.energyIso, trackNeg.momIso, trackPos.momIso, trackNeg.ntrackIso, trackPos.ntrackIso); + } + } + if (zeeQaCfg.enableZeeEmcRecoQA) { + if constexpr (IsSignal) { + registry.fill(HIST("hInvMassZeeEmcQA"), invMass, trackNeg.pt, trackPos.pt, trackNeg.eop, trackPos.eop, trackNeg.m02Emc, trackPos.m02Emc, trackNeg.nSigmaTpc, trackPos.nSigmaTpc, trackNeg.energyIso, trackPos.energyIso, trackNeg.momIso, trackPos.momIso, trackNeg.ntrackIso, trackPos.ntrackIso); + } else { + registry.fill(HIST("hInvMassZeeEmcQAbg"), invMass, trackNeg.pt, trackPos.pt, trackNeg.eop, trackPos.eop, trackNeg.m02Emc, trackPos.m02Emc, trackNeg.nSigmaTpc, trackPos.nSigmaTpc, trackNeg.energyIso, trackPos.energyIso, trackNeg.momIso, trackPos.momIso, trackNeg.ntrackIso, trackPos.ntrackIso); + } + } + } + + void fillZeeQa() + { + if (!zeeQaCfg.enableZeeTrkRecoQA && !zeeQaCfg.enableZeeEmcRecoQA) { + return; + } + + for (const auto& trackEle : selectedElectronsIso) { + for (const auto& trackPos : selectedPositronsIso) { + fillZeeQaPair(trackEle, trackPos); + } + } + // each unordered like-sign pair once + for (size_t i = 0; i < selectedElectronsIso.size(); ++i) { + for (size_t j = i + 1; j < selectedElectronsIso.size(); ++j) { + fillZeeQaPair(selectedElectronsIso[i], selectedElectronsIso[j]); + } + } + for (size_t i = 0; i < selectedPositronsIso.size(); ++i) { + for (size_t j = i + 1; j < selectedPositronsIso.size(); ++j) { + fillZeeQaPair(selectedPositronsIso[i], selectedPositronsIso[j]); + } + } + } + + // ------------------------------------------------------------------------- + // Z - h correlation. + // ------------------------------------------------------------------------- + void fillZHadron(float centrality) + { + const int nVar = nCutVariations(); + for (const auto& zBoson : reconstructedZ) { + // The Z counter is filled per variation, so that a varied mass window moves the + // denominator of the per-trigger yield together with its numerator. + bool anyVariation = false; + for (int iv = 0; iv < nVar; ++iv) { + if (!passZVariation(zBoson, iv)) { + continue; + } + anyVariation = true; + registry.fill(HIST("hZptSpectrum"), zBoson.charge, zBoson.pt, iv); + } + if (!anyVariation) { // outside the widest window of any variation + continue; + } + + for (const auto& trackAss : selectedElectronsAss) { + // remove the two tracks used to build this Z, matched exactly by globalIndex + if (trackAss.indexTrk == zBoson.indexChild0 || trackAss.indexTrk == zBoson.indexChild1) { + continue; + } + double const deltaPhi = RecoDecay::constrainAngle(trackAss.phi - zBoson.phi, -o2::constants::math::PIHalf); + double const deltaEta = zBoson.eta - trackAss.eta; + double const ptRatio = trackAss.pt / zBoson.pt; + // one entry per cut variation the pair passes; bin 0 is the nominal selection + for (int iv = 0; iv < nVar; ++iv) { + if (!passZVariation(zBoson, iv) || !passHadronVariation(trackAss, iv)) { + continue; + } + registry.fill(HIST("hZHadronDphi"), centrality, zBoson.charge, zBoson.pt, deltaPhi, deltaEta, + ptRatio, trackAss.pt, trackAss.nSigmaTpc, trackAss.sign, iv); + } + } + } + } + + // ------------------------------------------------------------------------- + // W->e - h correlation. + // ------------------------------------------------------------------------- + bool isZeeDaughter(int64_t indexTrk) + { + for (const auto& zBoson : reconstructedZ) { + if (zBoson.charge >= 0) { // like-sign pairs are the combinatorial background, not a Z + continue; + } + if (zBoson.mass < massZMin || zBoson.mass > massZMax) { + continue; + } + if (indexTrk == zBoson.indexChild0 || indexTrk == zBoson.indexChild1) { + return true; + } + } + return false; + } + + void fillWeHadron(const std::vector& electrons, float centrality) + { + for (const auto& ele : electrons) { + if (ele.pt < weCfg.ptWeMin) { + continue; + } + if (ele.eop <= 0.f) { + continue; + } + if (weCfg.vetoZeeElectron && isZeeDaughter(ele.indexTrk)) { + continue; + } + + const int nVar = nCutVariations(); + for (int iv = 0; iv < nVar; ++iv) { + if (!passElectronVariation(ele, iv)) { + continue; + } + registry.fill(HIST("hWeSpectrum"), centrality, ele.sign, ele.pt, ele.energyIso, ele.momIso, iv); + + for (const auto& trackAss : selectedElectronsAss) { + // the trigger electron itself, matched exactly by globalIndex + if (trackAss.indexTrk == ele.indexTrk) { + continue; + } + if (!passHadronVariation(trackAss, iv)) { + continue; + } + double const deltaPhi = RecoDecay::constrainAngle(trackAss.phi - ele.phi, -o2::constants::math::PIHalf); + double const deltaEta = ele.eta - trackAss.eta; + registry.fill(HIST("hWeHadronDphi"), centrality, ele.sign, ele.pt, deltaPhi, deltaEta, + ele.energyIso, ele.momIso, trackAss.pt, trackAss.nSigmaTpc, trackAss.sign, iv); + } + } + } + } + void processData(soa::Filtered::iterator const& collision, aod::BCsWithTimestamps const&, SelectedClusters const& emcClusters, @@ -838,11 +1162,15 @@ struct HfTaskElectronWeakBoson { if (ueCfg.useUEsub) { rho = estimateRhoRC(emcClusters); - energyUE = rho * static_cast(o2::constants::math::PI * rIsolation * rIsolation); + energyUE = rho * (o2::constants::math::PI * rIsolation * rIsolation); registry.fill(HIST("hEnergyUE"), centrality, energyUE); // LOG(info) << "UE = " << energyUE; } + const float ptIsoStoreMin = weCfg.enableWeHadron + ? std::min(static_cast(ptZeeMin), static_cast(weCfg.ptWeMin)) + : static_cast(ptZeeMin); + // track loop for (const auto& track : tracks) { @@ -921,6 +1249,8 @@ struct HfTaskElectronWeakBoson { const auto& cluster = match.emcalcluster_as(); float const m02Emc = cluster.m02(); float const energyEmc = cluster.energy(); + // transverse energy of the cluster; cosh(eta) >= 1, so energyTEmc <= energyEmc + float const energyTEmc = energyEmc / std::cosh(cluster.eta()); registry.fill(HIST("hTrMatchR"), match.track_as().pt(), rMin); registry.fill(HIST("hEnergyNcell"), energyEmc, cluster.nCells()); @@ -931,7 +1261,7 @@ struct HfTaskElectronWeakBoson { isoEnergy = getIsolatedCluster(cluster, emcClusters, energyUE); if (match.track_as().pt() > ptTHnThresh && isTHnElectron) { - registry.fill(HIST("hTHnElectrons"), match.track_as().pt(), match.track_as().tpcNSigmaEl(), m02Emc, eop, isoEnergy, isoMomentum, trackCount, track.eta(), track.tpcSignal()); + registry.fill(HIST("hTHnElectrons"), match.track_as().pt(), match.track_as().tpcNSigmaEl(), m02Emc, eop, isoEnergy, isoMomentum, trackCount, track.eta(), energyTEmc); } // LOG(info) << "E/p" << eop; registry.fill(HIST("hEopNsigTPC"), match.track_as().tpcNSigmaEl(), eop); @@ -971,7 +1301,9 @@ struct HfTaskElectronWeakBoson { // LOG(info) << "R mim = " << rMin; registry.fill(HIST("hTrMatch_mim"), dPhiMin, dEtaMin); } - if ((zeeQaCfg.enableZeeTrkRecoQA || zeeQaCfg.enableZeeEmcRecoQA) && track.pt() > ptZeeMin) { + // The e-h correlation reads the same two containers, so they must also be filled when + // only that analysis is enabled, and down to the looser of the two pT thresholds. + if ((zeeQaCfg.enableZeeTrkRecoQA || zeeQaCfg.enableZeeEmcRecoQA || weCfg.enableWeHadron) && track.pt() > ptIsoStoreMin) { if (track.sign() < 0) { selectedElectronsIso.emplace_back( track.pt(), @@ -1010,90 +1342,17 @@ struct HfTaskElectronWeakBoson { } } // end of track loop - // Z-hadron - if (!reconstructedZ.empty()) { - for (const auto& zBoson : reconstructedZ) { - // Z boson selection - if (zBoson.mass < massZMin || zBoson.mass > massZMax) { - continue; - } - registry.fill(HIST("hZptSpectrum"), zBoson.charge, zBoson.pt); - for (const auto& trackAss : selectedElectronsAss) { - // remove the two tracks used to build this Z, matched exactly by globalIndex - if (trackAss.indexTrk == zBoson.indexChild0 || trackAss.indexTrk == zBoson.indexChild1) { - continue; - } - // calculate Z-h correlation - double const deltaPhi = RecoDecay::constrainAngle(trackAss.phi - zBoson.phi, -o2::constants::math::PIHalf); - double const ptRatio = trackAss.pt / zBoson.pt; - double const deltaEta = zBoson.eta - trackAss.eta; - registry.fill(HIST("hZHadronDphi"), centrality, zBoson.charge, zBoson.pt, deltaPhi, deltaEta, ptRatio, trackAss.pt, trackAss.nSigmaTpc, trackAss.sign); - } - } - } // end of Z-hadron correlation + // Z - hadron correlation + fillZHadron(centrality); + + // W->e - h correlation. Electrons and positrons are kept in two containers, so the same + // routine runs twice; the electron charge goes on the sign axis (W- / W+). + if (weCfg.enableWeHadron) { + fillWeHadron(selectedElectronsIso, centrality); + fillWeHadron(selectedPositronsIso, centrality); + } // end of W->e - hadron correlation // Z->ee QA - if (zeeQaCfg.enableZeeTrkRecoQA || zeeQaCfg.enableZeeEmcRecoQA) { - if (!selectedElectronsIso.empty() && !selectedPositronsIso.empty()) { - // signal - for (const auto& trackEle : selectedElectronsIso) { - auto child1 = RecoDecayPtEtaPhi::pVector(trackEle.pt, trackEle.eta, trackEle.phi); - float const sectorneg = trackEle.phi / o2::constants::math::SectorSpanRad; - for (const auto& trackPos : selectedPositronsIso) { - auto child2 = RecoDecayPtEtaPhi::pVector(trackPos.pt, trackPos.eta, trackPos.phi); - double const invMass = RecoDecay::m(std::array{child1, child2}, std::array{o2::constants::physics::MassElectron, o2::constants::physics::MassElectron}); - float const sectorpos = trackPos.phi / o2::constants::math::SectorSpanRad; - if (invMass > zeeQaCfg.massZMinQA) { - if (zeeQaCfg.enableZeeTrkRecoQA) { - registry.fill(HIST("hInvMassZeeTrkQA"), invMass, trackEle.pt, trackPos.pt, trackEle.dcaxyTrk, trackPos.dcaxyTrk, trackPos.dcazTrk, trackEle.nclusterTPC, trackPos.nclusterTPC, trackEle.nclusterITS, trackPos.nclusterITS, sectorneg, sectorpos, trackEle.eop, trackPos.eop, trackEle.energyIso, trackPos.energyIso, trackEle.momIso, trackPos.momIso, trackEle.ntrackIso, trackPos.ntrackIso); - } - if (zeeQaCfg.enableZeeEmcRecoQA) { - registry.fill(HIST("hInvMassZeeEmcQA"), invMass, trackEle.pt, trackPos.pt, trackEle.eop, trackPos.eop, trackEle.m02Emc, trackPos.m02Emc, trackEle.nSigmaTpc, trackPos.nSigmaTpc, trackEle.energyIso, trackPos.energyIso, trackEle.momIso, trackPos.momIso, trackEle.ntrackIso, trackPos.ntrackIso); - } - } - } - } - // bg e-e- - for (size_t i = 0; i < selectedElectronsIso.size(); ++i) { - const auto& trackEle = selectedElectronsIso[i]; - auto child1 = RecoDecayPtEtaPhi::pVector(trackEle.pt, trackEle.eta, trackEle.phi); - float const sectorneg = trackEle.phi / o2::constants::math::SectorSpanRad; - for (size_t j = i + 1; j < selectedElectronsIso.size(); ++j) { - const auto& trackEle2 = selectedElectronsIso[j]; - auto child2 = RecoDecayPtEtaPhi::pVector(trackEle2.pt, trackEle2.eta, trackEle2.phi); - float const sectorpos = trackEle2.phi / o2::constants::math::SectorSpanRad; - double const invMass = RecoDecay::m(std::array{child1, child2}, std::array{o2::constants::physics::MassElectron, o2::constants::physics::MassElectron}); - if (invMass > zeeQaCfg.massZMinQA) { - if (zeeQaCfg.enableZeeTrkRecoQA) { - registry.fill(HIST("hInvMassZeeTrkQAbg"), invMass, trackEle.pt, trackEle2.pt, trackEle.dcaxyTrk, trackEle2.dcaxyTrk, trackEle2.dcazTrk, trackEle.nclusterTPC, trackEle2.nclusterTPC, trackEle.nclusterITS, trackEle2.nclusterITS, sectorneg, sectorpos, trackEle.eop, trackEle2.eop, trackEle.energyIso, trackEle2.energyIso, trackEle.momIso, trackEle2.momIso, trackEle.ntrackIso, trackEle2.ntrackIso); - } - if (zeeQaCfg.enableZeeEmcRecoQA) { - registry.fill(HIST("hInvMassZeeEmcQAbg"), invMass, trackEle.pt, trackEle2.pt, trackEle.eop, trackEle2.eop, trackEle.m02Emc, trackEle2.m02Emc, trackEle.nSigmaTpc, trackEle2.nSigmaTpc, trackEle.energyIso, trackEle2.energyIso, trackEle.momIso, trackEle2.momIso, trackEle.ntrackIso, trackEle2.ntrackIso); - } - } - } - } - // bg e+e+ - for (size_t i = 0; i < selectedPositronsIso.size(); ++i) { - const auto& trackPos = selectedPositronsIso[i]; - auto child1 = RecoDecayPtEtaPhi::pVector(trackPos.pt, trackPos.eta, trackPos.phi); - float const sectorneg = trackPos.phi / o2::constants::math::SectorSpanRad; - for (size_t j = i + 1; j < selectedPositronsIso.size(); ++j) { - const auto& trackPos2 = selectedPositronsIso[j]; - auto child2 = RecoDecayPtEtaPhi::pVector(trackPos2.pt, trackPos2.eta, trackPos2.phi); - float const sectorpos = trackPos2.phi / o2::constants::math::SectorSpanRad; - double const invMass = RecoDecay::m(std::array{child1, child2}, std::array{o2::constants::physics::MassElectron, o2::constants::physics::MassElectron}); - if (invMass > zeeQaCfg.massZMinQA) { - if (zeeQaCfg.enableZeeTrkRecoQA) { - registry.fill(HIST("hInvMassZeeTrkQAbg"), invMass, trackPos.pt, trackPos2.pt, trackPos.dcaxyTrk, trackPos2.dcaxyTrk, trackPos2.dcazTrk, trackPos.nclusterTPC, trackPos2.nclusterTPC, trackPos.nclusterITS, trackPos2.nclusterITS, sectorneg, sectorpos, trackPos.eop, trackPos2.eop, trackPos.energyIso, trackPos2.energyIso, trackPos.momIso, trackPos2.momIso, trackPos.ntrackIso, trackPos2.ntrackIso); - } - if (zeeQaCfg.enableZeeEmcRecoQA) { - registry.fill(HIST("hInvMassZeeEmcQAbg"), invMass, trackPos.pt, trackPos2.pt, trackPos.eop, trackPos2.eop, trackPos.m02Emc, trackPos2.m02Emc, trackPos.nSigmaTpc, trackPos2.nSigmaTpc, trackPos.energyIso, trackPos2.energyIso, trackPos.momIso, trackPos2.momIso, trackPos.ntrackIso, trackPos2.ntrackIso); - } - } - } - } // end of pos-pos paring - } // check arrays aren't empty - } // end of Z->ee QA + fillZeeQa(); } // processData PROCESS_SWITCH(HfTaskElectronWeakBoson, processData, "process data (Z->ee and Z-h correlation)", true); @@ -1181,27 +1440,46 @@ struct HfTaskElectronWeakBoson { continue; } - registry.fill(HIST("hMcRecHadronAll"), centrality, track.pt(), track.eta()); + // The efficiency has to be recomputed with the same cut variation the correlation used, + // so the numerator histograms carry the variation axis too. hMcGenHadron does not: no + // track cut enters the denominator, so it is common to every variation. + const int nVar = nCutVariations(); + + for (int iv = 0; iv < nVar; ++iv) { + if (passHadronVariationTrack(track, iv)) { + registry.fill(HIST("hMcRecHadronAll"), centrality, track.pt(), track.eta(), iv); + } + } if (!track.has_mcParticle()) { - registry.fill(HIST("hMcRecHadronFake"), centrality, track.pt(), track.eta()); + for (int iv = 0; iv < nVar; ++iv) { + if (passHadronVariationTrack(track, iv)) { + registry.fill(HIST("hMcRecHadronFake"), centrality, track.pt(), track.eta(), iv); + } + } continue; } auto const mcParticle = track.mcParticle(); // reject pileup : the matched particle must come from this MC collision - if (mcCfg.requireSameMcCollision && mcParticle.mcCollisionId() != collision.mcCollisionId()) { - registry.fill(HIST("hMcRecHadronSecondary"), centrality, track.pt(), track.eta()); - continue; - } - if (!isChargedPrimary(mcParticle)) { - registry.fill(HIST("hMcRecHadronSecondary"), centrality, track.pt(), track.eta()); + const bool isSecondary = (mcCfg.requireSameMcCollision && mcParticle.mcCollisionId() != collision.mcCollisionId()) || !isChargedPrimary(mcParticle); + if (isSecondary) { + for (int iv = 0; iv < nVar; ++iv) { + if (passHadronVariationTrack(track, iv)) { + registry.fill(HIST("hMcRecHadronSecondary"), centrality, track.pt(), track.eta(), iv); + } + } continue; } - registry.fill(HIST("hMcRecHadronMatchedGenKine"), centrality, mcParticle.pt(), mcParticle.eta()); - registry.fill(HIST("hMcRecHadronMatchedRecKine"), centrality, track.pt(), track.eta()); + for (int iv = 0; iv < nVar; ++iv) { + if (!passHadronVariationTrack(track, iv)) { + continue; + } + registry.fill(HIST("hMcRecHadronMatchedGenKine"), centrality, mcParticle.pt(), mcParticle.eta(), iv); + registry.fill(HIST("hMcRecHadronMatchedRecKine"), centrality, track.pt(), track.eta(), iv); + } registry.fill(HIST("hMcPtGenRec"), mcParticle.pt(), track.pt()); } } // processMcRec diff --git a/PWGHF/Macros/computeFonllPlusPythiaPredictions.C b/PWGHF/Macros/computeFonllPlusPythiaPredictions.C index 9d3dab4d407..c0e92758d40 100644 --- a/PWGHF/Macros/computeFonllPlusPythiaPredictions.C +++ b/PWGHF/Macros/computeFonllPlusPythiaPredictions.C @@ -318,7 +318,7 @@ void computeFonllPlusPythiaPredictions(int nDecays, int seed, std::string inFile for (auto iFonll{0}; iFonll < 3; ++iFonll) { normCrossSec[iFonll] = hFonllBhad[iFonll]->Integral(1, hFonllBhad[iFonll]->GetNbinsX() + 1, "width"); for (auto iChad{0}; iChad < NCharmHadrons; ++iChad) { - hFonllPythiaNonPromptChad[charmHadPdgs[iChad]][NBeautyHadrons][iFonll] = reinterpret_cast(hFonllPythiaNonPromptChad[charmHadPdgs[iChad]][0][iFonll]->Clone(Form("hFonllNonPrompt%s%s", charmHadNames[iChad].data(), namesFonll[iFonll].data()))); + hFonllPythiaNonPromptChad[charmHadPdgs[iChad]][NBeautyHadrons][iFonll] = dynamic_cast(hFonllPythiaNonPromptChad[charmHadPdgs[iChad]][0][iFonll]->Clone(Form("hFonllNonPrompt%s%s", charmHadNames[iChad].data(), namesFonll[iFonll].data()))); hFonllPythiaNonPromptChad[charmHadPdgs[iChad]][NBeautyHadrons][iFonll]->Reset(); for (auto iBHad{0}; iBHad < NBeautyHadrons; ++iBHad) { hFonllPythiaNonPromptChad[charmHadPdgs[iChad]][iBHad][iFonll]->Scale(normCrossSec[iFonll] / nDecays, "width"); diff --git a/PWGHF/TableProducer/CMakeLists.txt b/PWGHF/TableProducer/CMakeLists.txt index 174cbfa9621..0029240333b 100644 --- a/PWGHF/TableProducer/CMakeLists.txt +++ b/PWGHF/TableProducer/CMakeLists.txt @@ -25,6 +25,7 @@ o2physics_add_dpl_workflow(refit-pv-dummy o2physics_add_dpl_workflow(pid-creator SOURCES pidCreator.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -37,71 +38,85 @@ o2physics_add_dpl_workflow(mc-pid-tof o2physics_add_dpl_workflow(candidate-creator-2prong SOURCES candidateCreator2Prong.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter KFParticle::KFParticle O2Physics::SGCutParHolder O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-3prong SOURCES candidateCreator3Prong.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter KFParticle::KFParticle O2Physics::SGCutParHolder O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-b0 SOURCES candidateCreatorB0.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-bplus SOURCES candidateCreatorBplus.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-bs SOURCES candidateCreatorBs.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-cascade SOURCES candidateCreatorCascade.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-dstar SOURCES candidateCreatorDstar.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-lb SOURCES candidateCreatorLb.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-sigmac0plusplus SOURCES candidateCreatorSigmac0plusplus.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-sigmac0plusplus-cascade SOURCES candidateCreatorSigmac0plusplusCascade.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-xic0-omegac0 SOURCES candidateCreatorXic0Omegac0.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter KFParticle::KFParticle O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-xic0-omegac0-qa SOURCES candidateCreatorXic0Omegac0Qa.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter KFParticle::KFParticle O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-xic-to-xi-pi-pi SOURCES candidateCreatorXicToXiPiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter KFParticle::KFParticle O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-mc-gen SOURCES candidateCreatorMcGen.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -109,101 +124,127 @@ o2physics_add_dpl_workflow(candidate-creator-mc-gen o2physics_add_dpl_workflow(candidate-selector-b0-to-d-pi SOURCES candidateSelectorB0ToDPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-bplus-to-d0-pi SOURCES candidateSelectorBplusToD0Pi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-bs-to-ds-pi SOURCES candidateSelectorBsToDsPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-d0 SOURCES candidateSelectorD0.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-dplus-to-pi-k-pi SOURCES candidateSelectorDplusToPiKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-ds-to-k-k-pi SOURCES candidateSelectorDsToKKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-dstar-to-d0-pi SOURCES candidateSelectorDstarToD0Pi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-lb-to-lc-pi SOURCES candidateSelectorLbToLcPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-lc SOURCES candidateSelectorLc.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-lc-pid-ml SOURCES candidateSelectorLcPidMl.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-lc-to-k0s-p SOURCES candidateSelectorLcToK0sP.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-omegac0-to-omega-ka SOURCES candidateSelectorOmegac0ToOmegaKa.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-omegac0-to-omega-pi SOURCES candidateSelectorOmegac0ToOmegaPi.cxx + REUSE_FROM HfCandidatesPCH + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(candidate-selector-omegac0-to-omega-pi-qa + SOURCES candidateSelectorOmegac0ToOmegaPiQa.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-omegac0-xic0-to-omega-ka SOURCES candidateSelectorOmegac0Xic0ToOmegaKa.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-xic0-to-xi-pi-kf SOURCES candidateSelectorXic0ToXiPiKf.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-to-xi-pi SOURCES candidateSelectorToXiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-to-xi-pi-qa SOURCES candidateSelectorToXiPiQa.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-xic-to-p-k-pi SOURCES candidateSelectorXicToPKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-xic-to-xi-pi-pi SOURCES candidateSelectorXicToXiPiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-cd SOURCES candidateSelectorCd.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -211,56 +252,67 @@ o2physics_add_dpl_workflow(candidate-selector-cd o2physics_add_dpl_workflow(tree-creator-b0-to-d-pi SOURCES treeCreatorB0ToDPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-bplus-to-d0-pi SOURCES treeCreatorBplusToD0Pi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-bs-to-ds-pi SOURCES treeCreatorBsToDsPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-d0-to-k-pi SOURCES treeCreatorD0ToKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-dplus-to-pi-k-pi SOURCES treeCreatorDplusToPiKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-ds-to-k-k-pi SOURCES treeCreatorDsToKKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-dstar-to-d0-pi SOURCES treeCreatorDstarToD0Pi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-lb-to-lc-pi SOURCES treeCreatorLbToLcPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-lc-to-k0s-p SOURCES treeCreatorLcToK0sP.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-lc-to-p-k-pi SOURCES treeCreatorLcToPKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-sigmac-corr-bkg SOURCES treeCreatorSigmacCorrBkg.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -271,87 +323,110 @@ o2physics_add_dpl_workflow(tree-creator-omegac-st o2physics_add_dpl_workflow(tree-creator-omegac0-to-omega-ka SOURCES treeCreatorOmegac0ToOmegaKa.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-omegac0-to-omega-pi SOURCES treeCreatorOmegac0ToOmegaPi.cxx + REUSE_FROM HfCandidatesPCH + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(tree-creator-omegac0-to-omega-pi-qa + SOURCES treeCreatorOmegac0ToOmegaPiQa.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-to-xi-pi SOURCES treeCreatorToXiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-to-xi-pi-qa SOURCES treeCreatorToXiPiQa.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-xic0-to-xi-pi-kf SOURCES treeCreatorXic0ToXiPiKf.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-xic-to-p-k-pi SOURCES treeCreatorXicToPKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-xic-to-xi-pi-pi SOURCES treeCreatorXicToXiPiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-tcc-to-d0-d0-pi SOURCES treeCreatorTccToD0D0Pi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) # Derived-data creators o2physics_add_dpl_workflow(derived-data-creator-b0-to-d-pi SOURCES derivedDataCreatorB0ToDPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derived-data-creator-bplus-to-d0-pi SOURCES derivedDataCreatorBplusToD0Pi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derived-data-creator-d0-to-k-pi SOURCES derivedDataCreatorD0ToKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derived-data-creator-dplus-to-pi-k-pi SOURCES derivedDataCreatorDplusToPiKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derived-data-creator-ds-to-k-k-pi SOURCES derivedDataCreatorDsToKKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derived-data-creator-dstar-to-d0-pi SOURCES derivedDataCreatorDstarToD0Pi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derived-data-creator-lc-to-p-k-pi SOURCES derivedDataCreatorLcToPKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derived-data-creator-lc-to-k0s-p SOURCES derivedDataCreatorLcToK0sP.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derived-data-creator-xic-to-xi-pi-pi SOURCES derivedDataCreatorXicToXiPiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) diff --git a/PWGHF/TableProducer/candidateCreatorBplus.cxx b/PWGHF/TableProducer/candidateCreatorBplus.cxx index 22ded3ecc7b..8894c7ec60f 100644 --- a/PWGHF/TableProducer/candidateCreatorBplus.cxx +++ b/PWGHF/TableProducer/candidateCreatorBplus.cxx @@ -366,7 +366,8 @@ struct HfCandidateCreatorBplus { pVecD0[0], pVecD0[1], pVecD0[2], pVecBach[0], pVecBach[1], pVecBach[2], impactParameter0.getY(), impactParameter1.getY(), - std::sqrt(impactParameter0.getSigmaY2()), std::sqrt(impactParameter1.getSigmaY2())); + std::sqrt(impactParameter0.getSigmaY2()), std::sqrt(impactParameter1.getSigmaY2()), + candD0.xSecondaryVertex(), candD0.ySecondaryVertex(), candD0.zSecondaryVertex()); rowCandidateProngs(candD0.globalIndex(), trackPion.globalIndex()); // index D0 and bachelor } // track loop diff --git a/PWGHF/TableProducer/candidateCreatorXic0Omegac0.cxx b/PWGHF/TableProducer/candidateCreatorXic0Omegac0.cxx index 4f1c007d20a..1030e695c4b 100644 --- a/PWGHF/TableProducer/candidateCreatorXic0Omegac0.cxx +++ b/PWGHF/TableProducer/candidateCreatorXic0Omegac0.cxx @@ -617,7 +617,7 @@ struct HfCandidateCreatorXic0Omegac0 { } else { mCasc = casc.mOmega(); } - auto arrMassCharmBaryon = std::array{0., 0.}; + std::array arrMassCharmBaryon{}; if constexpr (DecayChannel == hf_cand_casc_lf::DecayType2Prong::XiczeroOmegaczeroToXiPi) { arrMassCharmBaryon = {MassXiMinus, MassPiPlus}; } else if constexpr (DecayChannel == hf_cand_casc_lf::DecayType2Prong::OmegaczeroToOmegaPi) { diff --git a/PWGHF/TableProducer/candidateCreatorXic0Omegac0Qa.cxx b/PWGHF/TableProducer/candidateCreatorXic0Omegac0Qa.cxx index 6f37217b7f6..67f4bee267d 100644 --- a/PWGHF/TableProducer/candidateCreatorXic0Omegac0Qa.cxx +++ b/PWGHF/TableProducer/candidateCreatorXic0Omegac0Qa.cxx @@ -234,7 +234,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { Service ccdb{}; Service pdgdb{}; - o2::base::MatLayerCylSet* lut; + o2::base::MatLayerCylSet* lut{}; o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; // DCAFitter @@ -259,7 +259,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { HfEventSelection hfEvSel; // PDG Id of daughter tracks & V0s & cascades & charm baryons - Used in KFParticle - int pdgIdOfV0DauPos{}, pdgIdOfV0DauNeg{}, pdgIdOfBach{}, pdgIdOfCharmBach{}; + int pdgIdOfV0DauPos{}, pdgIdOfV0DauNeg{}, pdgIdOfBach{}, pdgIdOfBachRej{}, pdgIdOfCharmBach{}; int pdgIdOfV0{}, pdgIdOfCascade{}, pdgIdOfCharmBaryon{}; // Track PID: PID value of tracks defined under o2::track::PID namespace @@ -315,6 +315,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { pdgIdOfV0DauPos = kProton; pdgIdOfV0DauNeg = kPiMinus; pdgIdOfBach = kPiMinus; + pdgIdOfBachRej = 0; // -> Not used in XiPi channel pdgIdOfCharmBach = kPiPlus; pdgIdOfV0 = kLambda0; @@ -332,6 +333,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { pdgIdOfV0DauPos = kProton; pdgIdOfV0DauNeg = kPiMinus; pdgIdOfBach = kKMinus; + pdgIdOfBachRej = kPiMinus; pdgIdOfCharmBach = kPiPlus; pdgIdOfV0 = kLambda0; @@ -349,6 +351,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { pdgIdOfV0DauPos = kProton; pdgIdOfV0DauNeg = kPiMinus; pdgIdOfBach = kKMinus; + pdgIdOfBachRej = kPiMinus; pdgIdOfCharmBach = kKPlus; pdgIdOfV0 = kLambda0; @@ -370,6 +373,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { LOGF(info, " PDG ID of V0 negative daughter: %d", pdgIdOfV0DauNeg); LOGF(info, " PDG ID of V0: %d", pdgIdOfV0); LOGF(info, " PDG ID of Bachelor: %d", pdgIdOfBach); + LOGF(info, " PDG ID of Bachelor Rej: %d", pdgIdOfBachRej); LOGF(info, " PDG ID of Cascade: %d", pdgIdOfCascade); LOGF(info, " PDG ID of Charm Bachelor: %d", pdgIdOfCharmBach); LOGF(info, " PDG ID of Charm Baryon: %d", pdgIdOfCharmBaryon); @@ -965,7 +969,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { KFParticle kfPos(kfTrack0, (isAnti ? -pdgIdOfV0DauNeg : pdgIdOfV0DauPos)); KFParticle kfNeg(kfTrack1, (isAnti ? -pdgIdOfV0DauPos : pdgIdOfV0DauNeg)); KFParticle kfBach(kfTrackBach, (isAnti ? -pdgIdOfBach : pdgIdOfBach)); - KFParticle kfBachRej(kfTrackBach, (isAnti ? -pdgIdOfBach : pdgIdOfBach)); // Rej -> Used for Omegac0->OmegaPi only + KFParticle kfBachRej(kfTrackBach, (isAnti ? -pdgIdOfBachRej : pdgIdOfBachRej)); // Rej -> Used for Omegac0->OmegaPi only // ~~~~~~~Construct V0 with KF~~~~~~~ const KFParticle* v0Daughters[2] = {&kfPos, &kfNeg}; @@ -1286,7 +1290,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { 0.f, 0.f, 0.f, // -> vertexCharmBaryonFromFitter. For KF, this is 0 vertexCasc[0], vertexCasc[1], vertexCasc[2], vertexV0[0], vertexV0[1], vertexV0[2], - trackCharmBachelor.sign(), + bachTrack.sign(), covVtxCharmBaryon[0], covVtxCharmBaryon[1], covVtxCharmBaryon[2], covVtxCharmBaryon[3], covVtxCharmBaryon[4], covVtxCharmBaryon[5], kfCharmBaryon.GetPx(), kfCharmBaryon.GetPy(), kfCharmBaryon.GetPz(), kfCascToCharmBaryon.GetPx(), kfCascToCharmBaryon.GetPy(), kfCascToCharmBaryon.GetPz(), @@ -1647,7 +1651,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { // bitmask with event selection info float centrality{-1.f}; float occupancy = getOccupancyColl(collision, OccupancyEstimator::Its); - const auto rejectionMask = hfEvSel.getHfCollisionRejectionMask(collision, centrality, ccdb, registry); + const auto rejectionMask = hfEvSel.getHfCollisionRejectionMask(collision, centrality, ccdb, registry); // monitor the satisfied event selection hfEvSel.fillHistograms(collision, rejectionMask, centrality, occupancy); @@ -1663,7 +1667,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { // bitmask with event selection info float centrality{-1.f}; float occupancy = getOccupancyColl(collision, OccupancyEstimator::Its); - const auto rejectionMask = hfEvSel.getHfCollisionRejectionMask(collision, centrality, ccdb, registry); + const auto rejectionMask = hfEvSel.getHfCollisionRejectionMask(collision, centrality, ccdb, registry); // monitor the satisfied event selection hfEvSel.fillHistograms(collision, rejectionMask, centrality, occupancy); diff --git a/PWGHF/TableProducer/candidateCreatorXicToXiPiPi.cxx b/PWGHF/TableProducer/candidateCreatorXicToXiPiPi.cxx index 3780b21c1f9..3ad4779009a 100644 --- a/PWGHF/TableProducer/candidateCreatorXicToXiPiPi.cxx +++ b/PWGHF/TableProducer/candidateCreatorXicToXiPiPi.cxx @@ -151,6 +151,8 @@ struct HfCandidateCreatorXicToXiPiPi { HistogramRegistry registry{"registry"}; OutputObj zorroSummary{"zorroSummary"}; + static constexpr float cmToMicrometer{10000.}; + void init(InitContext const&) { if ((doprocessNoCentXicplusWithDcaFitter + doprocessCentFT0CXicplusWithDcaFitter + doprocessCentFT0MXicplusWithDcaFitter + doprocessNoCentXicplusWithKFParticle + doprocessCentFT0CXicplusWithKFParticle + doprocessCentFT0MXicplusWithKFParticle) != 1) { @@ -541,12 +543,12 @@ struct HfCandidateCreatorXicToXiPiPi { registry.fill(HIST("hCovSVXZ"), covMatrixSV[3]); registry.fill(HIST("hCovSVZZ"), covMatrixSV[5]); // DCAs of prongs - registry.fill(HIST("hDcaXYProngs"), trackCasc.getPt(), impactParameterCasc.getY()); - registry.fill(HIST("hDcaXYProngs"), trackCharmBachelor0.pt(), impactParameter0.getY()); - registry.fill(HIST("hDcaXYProngs"), trackCharmBachelor1.pt(), impactParameter1.getY()); - registry.fill(HIST("hDcaZProngs"), trackCasc.getPt(), impactParameterCasc.getZ()); - registry.fill(HIST("hDcaZProngs"), trackCharmBachelor0.pt(), impactParameter0.getZ()); - registry.fill(HIST("hDcaZProngs"), trackCharmBachelor1.pt(), impactParameter1.getZ()); + registry.fill(HIST("hDcaXYProngs"), trackCasc.getPt(), impactParameterCasc.getY() * cmToMicrometer); + registry.fill(HIST("hDcaXYProngs"), trackCharmBachelor0.pt(), impactParameter0.getY() * cmToMicrometer); + registry.fill(HIST("hDcaXYProngs"), trackCharmBachelor1.pt(), impactParameter1.getY() * cmToMicrometer); + registry.fill(HIST("hDcaZProngs"), trackCasc.getPt(), impactParameterCasc.getZ() * cmToMicrometer); + registry.fill(HIST("hDcaZProngs"), trackCharmBachelor0.pt(), impactParameter0.getZ() * cmToMicrometer); + registry.fill(HIST("hDcaZProngs"), trackCharmBachelor1.pt(), impactParameter1.getZ() * cmToMicrometer); } //---------------------------------fill candidate table rows------------------------------------------------------------------------------------------- @@ -873,9 +875,9 @@ struct HfCandidateCreatorXicToXiPiPi { registry.fill(HIST("hCovSVXZ"), covMatrixXicPlus[3]); registry.fill(HIST("hCovSVZZ"), covMatrixXicPlus[5]); // DCAs of prongs - registry.fill(HIST("hDcaXYProngs"), kfXi.GetPt(), impactParameterXiXY); - registry.fill(HIST("hDcaXYProngs"), kfCharmBachelor0.GetPt(), impactParameterPi0XY); - registry.fill(HIST("hDcaXYProngs"), kfCharmBachelor1.GetPt(), impactParameterPi1XY); + registry.fill(HIST("hDcaXYProngs"), kfXi.GetPt(), impactParameterXiXY * cmToMicrometer); + registry.fill(HIST("hDcaXYProngs"), kfCharmBachelor0.GetPt(), impactParameterPi0XY * cmToMicrometer); + registry.fill(HIST("hDcaXYProngs"), kfCharmBachelor1.GetPt(), impactParameterPi1XY * cmToMicrometer); } //------------------------------fill candidate table rows-------------------------------------- @@ -1093,6 +1095,7 @@ struct HfCandidateCreatorXicToXiPiPiExpressions { registry.add("hDecayedPions", "hDecayedPions", {HistType::kTH1F, {{5, -0.5, 4.5}}}); registry.add("hInteractionsWithMaterial", "hInteractionsWithMaterial", {HistType::kTH1F, {{6, -0.5, 5.5}}}); registry.add("hDebugRec", "hDebugRec", {HistType::kTH1F, {{4, -0.5, 3.5}}}); + registry.add("hYPtGen", "Generated MC", {HistType::kTH2F, {{20, -1.0, 1.0}, {200, 0, 20}}}); registry.get(HIST("hDebugRec"))->GetXaxis()->SetBinLabel(1 + TotalRec, "total"); registry.get(HIST("hDebugRec"))->GetXaxis()->SetBinLabel(1 + XicToFinalState, "#Xi^{+}_{c} #rightarrow #pi^{#plus} #pi^{#plus} #pi^{#minus} p #pi^{#minus}"); registry.get(HIST("hDebugRec"))->GetXaxis()->SetBinLabel(1 + XiToPiPPi, "#Xi^{#minus} #rightarrow #pi^{#minus} p #pi^{#minus}"); @@ -1300,12 +1303,13 @@ struct HfCandidateCreatorXicToXiPiPiExpressions { if (rejectionMask != 0) { // at least one event selection not satisfied --> reject all particles from this collision for (unsigned int i = 0; i < mcParticlesPerMcColl.size(); ++i) { - rowMcMatchGen(-99, -99, -99, decayLengthGen); + rowMcMatchGen(-99, -99, -99, decayLengthGen, -1.f); } continue; } for (const auto& particle : mcParticlesPerMcColl) { + const float massGen = RecoDecay::m(particle.pVector(), particle.e()); sign = 0; flag = 0; origin = RecoDecay::OriginType::None; @@ -1345,6 +1349,10 @@ struct HfCandidateCreatorXicToXiPiPiExpressions { // Check whether the charm baryon is non-prompt (from a b quark). if (flag != 0) { + // fill inclusive generated Xic+ + if (fillMcHistograms) + registry.fill(HIST("hYPtGen"), particle.y(), particle.pt()); + origin = RecoDecay::getCharmHadronOrigin(mcParticles, particle, false, &idxBhadMothers); // Calculate the decay length of the generated particle auto dau0 = particle.template daughters_as().begin(); @@ -1355,9 +1363,9 @@ struct HfCandidateCreatorXicToXiPiPiExpressions { // Fill table if (origin == RecoDecay::OriginType::NonPrompt) { auto bHadMother = mcParticles.rawIteratorAt(idxBhadMothers[0]); - rowMcMatchGen(flag, origin, bHadMother.pdgCode(), decayLengthGen); + rowMcMatchGen(flag, origin, bHadMother.pdgCode(), decayLengthGen, massGen); } else { - rowMcMatchGen(flag, origin, 0, decayLengthGen); + rowMcMatchGen(flag, origin, 0, decayLengthGen, massGen); } } // close loop over generated particles } // close loop over McCollisions diff --git a/PWGHF/TableProducer/candidateSelectorB0ToDPi.cxx b/PWGHF/TableProducer/candidateSelectorB0ToDPi.cxx index c083d530baf..358df7a0326 100644 --- a/PWGHF/TableProducer/candidateSelectorB0ToDPi.cxx +++ b/PWGHF/TableProducer/candidateSelectorB0ToDPi.cxx @@ -136,7 +136,7 @@ struct HfCandidateSelectorB0ToDPi { labels[1 + SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/TableProducer/candidateSelectorBplusToD0Pi.cxx b/PWGHF/TableProducer/candidateSelectorBplusToD0Pi.cxx index 7b51a915c4c..9c538f544fe 100644 --- a/PWGHF/TableProducer/candidateSelectorBplusToD0Pi.cxx +++ b/PWGHF/TableProducer/candidateSelectorBplusToD0Pi.cxx @@ -139,7 +139,7 @@ struct HfCandidateSelectorBplusToD0Pi { labels[1 + SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/TableProducer/candidateSelectorBsToDsPi.cxx b/PWGHF/TableProducer/candidateSelectorBsToDsPi.cxx index eee6a06b93f..376d485bffd 100644 --- a/PWGHF/TableProducer/candidateSelectorBsToDsPi.cxx +++ b/PWGHF/TableProducer/candidateSelectorBsToDsPi.cxx @@ -120,7 +120,7 @@ struct HfCandidateSelectorBsToDsPi { labels[1 + SelectionStep::RecoTopol] = "Skims & Topological selections"; labels[1 + SelectionStep::RecoPID] = "Skims & Topological & PID selections"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/TableProducer/candidateSelectorCd.cxx b/PWGHF/TableProducer/candidateSelectorCd.cxx index 7658c95cf11..1fcbdbcfedf 100644 --- a/PWGHF/TableProducer/candidateSelectorCd.cxx +++ b/PWGHF/TableProducer/candidateSelectorCd.cxx @@ -110,7 +110,7 @@ struct HfCandidateSelectorCd { labels[1 + aod::SelectionStep::RecoTopol] = "Skims & Topological selections"; labels[1 + aod::SelectionStep::RecoPID] = "Skims & Topological & PID selections"; static const AxisSpec axisSelections = {NBinsSelections, 0.5, NBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < NBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/TableProducer/candidateSelectorDplusToPiKPi.cxx b/PWGHF/TableProducer/candidateSelectorDplusToPiKPi.cxx index b66c7ac8540..53e26443b6c 100644 --- a/PWGHF/TableProducer/candidateSelectorDplusToPiKPi.cxx +++ b/PWGHF/TableProducer/candidateSelectorDplusToPiKPi.cxx @@ -126,7 +126,7 @@ struct HfCandidateSelectorDplusToPiKPi { labels[1 + aod::SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/TableProducer/candidateSelectorDsToKKPi.cxx b/PWGHF/TableProducer/candidateSelectorDsToKKPi.cxx index bbe41e275fe..153c19e3f6d 100644 --- a/PWGHF/TableProducer/candidateSelectorDsToKKPi.cxx +++ b/PWGHF/TableProducer/candidateSelectorDsToKKPi.cxx @@ -127,7 +127,7 @@ struct HfCandidateSelectorDsToKKPi { labels[1 + aod::SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/TableProducer/candidateSelectorDstarToD0Pi.cxx b/PWGHF/TableProducer/candidateSelectorDstarToD0Pi.cxx index 0d24a58d91b..ee2310e4d34 100644 --- a/PWGHF/TableProducer/candidateSelectorDstarToD0Pi.cxx +++ b/PWGHF/TableProducer/candidateSelectorDstarToD0Pi.cxx @@ -147,7 +147,7 @@ struct HfCandidateSelectorDstarToD0Pi { labels[1 + aod::SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("QA/hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPtDstar, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("QA/hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPtDstar), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("QA/hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/TableProducer/candidateSelectorLc.cxx b/PWGHF/TableProducer/candidateSelectorLc.cxx index a3155b30e3e..5088a876513 100644 --- a/PWGHF/TableProducer/candidateSelectorLc.cxx +++ b/PWGHF/TableProducer/candidateSelectorLc.cxx @@ -156,7 +156,7 @@ struct HfCandidateSelectorLc { labels[1 + aod::SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/TableProducer/candidateSelectorLcPidMl.cxx b/PWGHF/TableProducer/candidateSelectorLcPidMl.cxx index f27e5edc3cd..efccd81c0fd 100644 --- a/PWGHF/TableProducer/candidateSelectorLcPidMl.cxx +++ b/PWGHF/TableProducer/candidateSelectorLcPidMl.cxx @@ -307,12 +307,12 @@ struct HfCandidateSelectorLcPidMl { std::vector inputFeaturesD{trackParPos1.getPt(), trackPos1.dcaXY(), trackPos1.dcaZ(), trackParNeg.getPt(), trackNeg.dcaXY(), trackNeg.dcaZ(), trackParPos2.getPt(), trackPos2.dcaXY(), trackPos2.dcaZ()}; float scores[3] = {-1.f, -1.f, -1.f}; if (dataTypeML == 1) { - auto* scoresRaw = model.evalModel(inputFeaturesF); + const auto scoresRaw = model.evalModel(inputFeaturesF); for (int iScore = 0; iScore < 3; ++iScore) { scores[iScore] = scoresRaw[iScore]; } } else if (dataTypeML == 11) { - auto* scoresRaw = model.evalModel(inputFeaturesD); + const auto scoresRaw = model.evalModel(inputFeaturesD); for (int iScore = 0; iScore < 3; ++iScore) { scores[iScore] = scoresRaw[iScore]; } diff --git a/PWGHF/TableProducer/candidateSelectorOmegac0ToOmegaPiQa.cxx b/PWGHF/TableProducer/candidateSelectorOmegac0ToOmegaPiQa.cxx new file mode 100644 index 00000000000..2f3ff74b2fe --- /dev/null +++ b/PWGHF/TableProducer/candidateSelectorOmegac0ToOmegaPiQa.cxx @@ -0,0 +1,887 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file candidateSelectorOmegac0ToOmegaPiQa.cxx +/// \brief Omegac0 → Omega Pi selection task + +/// \author Fabio Catalano , University of Houston +/// \author Maria Fernanda Torres Cabrera , University of Houston + +#include "PWGHF/Core/HfMlResponseOmegacToOmegaPiQa.h" +#include "PWGHF/Core/SelectorCuts.h" +#include "PWGHF/DataModel/AliasTables.h" +#include "PWGHF/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/DataModel/CandidateSelectionTables.h" +#include "PWGHF/Utils/utilsAnalysis.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/Core/TrackSelectorPID.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::framework; +using namespace o2::analysis; + +enum PidInfoStored { + PiFromLam = 0, + PrFromLam, + KaFromCasc, + PiFromCharm +}; + +enum { + doDcaFitter = 0, + doKfParticle +}; + +/// Struct for applying Omegac0 -> Omega pi selection cuts +struct HfCandidateSelectorToOmegaPiQa { + // DCAFitter and KFParticle + Produces hfSelToOmegaPi; + // ML selection - filled for both DCAFitter and KFParticle + Produces hfMlSelToOmegaPi; + + // cuts from SelectorCuts.h - pT dependent cuts + Configurable> binsPt{"binsPt", std::vector{hf_cuts_omegac_to_omega_pi::vecBinsPt}, "pT bin limits"}; + Configurable> cuts{"cuts", {hf_cuts_omegac_to_omega_pi::Cuts[0], hf_cuts_omegac_to_omega_pi::NBinsPt, hf_cuts_omegac_to_omega_pi::NCutVars, hf_cuts_omegac_to_omega_pi::labelsPt, hf_cuts_omegac_to_omega_pi::labelsCutVar}, "OmegaC0 candidate selection per pT bin"}; + + // ML inference + Configurable applyMl{"applyMl", false, "Flag to apply ML selections"}; + Configurable> binsPtMl{"binsPtMl", std::vector{hf_cuts_ml::vecBinsPt}, "pT bin limits for ML application"}; + Configurable> cutDirMl{"cutDirMl", std::vector{hf_cuts_ml::vecCutDir}, "Whether to reject score values greater or smaller than the threshold"}; + Configurable> cutsMl{"cutsMl", {hf_cuts_ml::Cuts[0], hf_cuts_ml::NBinsPt, hf_cuts_ml::NCutScores, hf_cuts_ml::labelsPt, hf_cuts_ml::labelsCutScore}, "ML selections per pT bin"}; + Configurable nClassesMl{"nClassesMl", static_cast(hf_cuts_ml::NCutScores), "Number of classes in ML model"}; + Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature1", "feature2"}, "Names of ML model input features"}; + + // CCDB configuration + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable> modelPathsCCDB{"modelPathsCCDB", std::vector{"EventFiltering/PWGHF/BDTOmegac0"}, "Paths of models on CCDB"}; + Configurable> onnxFileNames{"onnxFileNames", std::vector{"ModelHandler_onnx_Omegac0ToOmegaPi.onnx"}, "ONNX file names for each pT bin (if not from CCDB full path)"}; + Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB"}; + Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; + + // LF analysis selections + Configurable radiusCascMin{"radiusCascMin", 0.5, "Min cascade radius"}; + Configurable radiusV0Min{"radiusV0Min", 1.1, "Min V0 radius"}; + Configurable cosPAV0Min{"cosPAV0Min", 0.97, "Min value CosPA V0"}; + Configurable cosPACascMin{"cosPACascMin", 0.97, "Min value CosPA cascade"}; + Configurable dcaV0DauMax{"dcaV0DauMax", 1.0, "Max DCA V0 daughters"}; + Configurable dcaCascDauMax{"dcaCascDauMax", 1.0, "Max DCA cascade daughters"}; + Configurable dcaNegToPvMin{"dcaNegToPvMin", 0.06, "DCA Neg To PV"}; + Configurable dcaPosToPvMin{"dcaPosToPvMin", 0.06, "DCA Pos To PV"}; + Configurable dcaBachToPvMin{"dcaBachToPvMin", 0.04, "DCA Bach To PV"}; + Configurable applyTrkSelLf{"applyTrkSelLf", true, "Apply track selection for LF daughters"}; + + // Mass window + Configurable v0MassWindow{"v0MassWindow", 0.01, "V0 mass window"}; + Configurable cascadeMassWindow{"cascadeMassWindow", 0.01, "Cascade mass window"}; + Configurable invMassCharmBaryonMin{"invMassCharmBaryonMin", 2.3, "Lower limit invariant mass spectrum charm baryon"}; + Configurable invMassCharmBaryonMax{"invMassCharmBaryonMax", 3.1, "Upper limit invariant mass spectrum charm baryon"}; + + // kinematic selections + Configurable etaTrackCharmBachMax{"etaTrackCharmBachMax", 0.8, "Max absolute value of eta for charm baryon bachelor"}; + Configurable etaTrackLFDauMax{"etaTrackLFDauMax", 1.0, "Max absolute value of eta for V0 and cascade daughters"}; + Configurable ptKaFromCascMin{"ptKaFromCascMin", 0.15, "Min pT kaon <- casc"}; + + Configurable impactParameterXYPiFromCharmBaryonMin{"impactParameterXYPiFromCharmBaryonMin", 0., "Min dcaxy pi from charm baryon track to PV"}; + Configurable impactParameterXYPiFromCharmBaryonMax{"impactParameterXYPiFromCharmBaryonMax", 10., "Max dcaxy pi from charm baryon track to PV"}; + Configurable impactParameterXYCascMin{"impactParameterXYCascMin", 0., "Min dcaxy cascade track to PV"}; + Configurable impactParameterXYCascMax{"impactParameterXYCascMax", 10., "Max dcaxy cascade track to PV"}; + Configurable impactParameterZPiFromCharmBaryonMin{"impactParameterZPiFromCharmBaryonMin", 0., "Min dcaz pi from charm baryon track to PV"}; + Configurable impactParameterZPiFromCharmBaryonMax{"impactParameterZPiFromCharmBaryonMax", 10., "Max dcaz pi from charm baryon track to PV"}; + Configurable impactParameterZCascMin{"impactParameterZCascMin", 0., "Min dcaz cascade track to PV"}; + Configurable impactParameterZCascMax{"impactParameterZCascMax", 10., "Max dcaz cascade track to PV"}; + + Configurable ptCandMin{"ptCandMin", 0., "Lower bound of candidate pT"}; + Configurable ptCandMax{"ptCandMax", 50., "Upper bound of candidate pT"}; + + Configurable dcaCharmBaryonDauMax{"dcaCharmBaryonDauMax", 2.0, "Max DCA charm baryon daughters"}; + + // PID options + Configurable usePidTpcOnly{"usePidTpcOnly", true, "Perform PID using only TPC"}; + Configurable usePidTpcTofCombined{"usePidTpcTofCombined", false, "Perform PID using TPC or TOF"}; + + // PID - TPC selections + Configurable ptPiPidTpcMin{"ptPiPidTpcMin", -1, "Lower bound of track pT for TPC PID for pion selection"}; + Configurable ptPiPidTpcMax{"ptPiPidTpcMax", 9999.9, "Upper bound of track pT for TPC PID for pion selection"}; + Configurable nSigmaTpcPiMax{"nSigmaTpcPiMax", 3., "Nsigma cut on TPC only for pion selection"}; + Configurable nSigmaTpcCombinedPiMax{"nSigmaTpcCombinedPiMax", 0., "Nsigma cut on TPC combined with TOF for pion selection"}; + + Configurable ptPrPidTpcMin{"ptPrPidTpcMin", -1, "Lower bound of track pT for TPC PID for proton selection"}; + Configurable ptPrPidTpcMax{"ptPrPidTpcMax", 9999.9, "Upper bound of track pT for TPC PID for proton selection"}; + Configurable nSigmaTpcPrMax{"nSigmaTpcPrMax", 3., "Nsigma cut on TPC only for proton selection"}; + Configurable nSigmaTpcCombinedPrMax{"nSigmaTpcCombinedPrMax", 0., "Nsigma cut on TPC combined with TOF for proton selection"}; + + Configurable ptKaPidTpcMin{"ptKaPidTpcMin", -1, "Lower bound of track pT for TPC PID for kaon selection"}; + Configurable ptKaPidTpcMax{"ptKaPidTpcMax", 9999.9, "Upper bound of track pT for TPC PID for kaon selection"}; + Configurable nSigmaTpcKaMax{"nSigmaTpcKaMax", 3., "Nsigma cut on TPC only for kaon selection"}; + Configurable nSigmaTpcCombinedKaMax{"nSigmaTpcCombinedKaMax", 0., "Nsigma cut on TPC combined with TOF for kaon selection"}; + + // PID - TOF selections + Configurable ptPiPidTofMin{"ptPiPidTofMin", -1, "Lower bound of track pT for TOF PID for pion selection"}; + Configurable ptPiPidTofMax{"ptPiPidTofMax", 9999.9, "Upper bound of track pT for TOF PID for pion selection"}; + Configurable nSigmaTofPiMax{"nSigmaTofPiMax", 3., "Nsigma cut on TOF only for pion selection"}; + Configurable nSigmaTofCombinedPiMax{"nSigmaTofCombinedPiMax", 0., "Nsigma cut on TOF combined with TPC for pion selection"}; + + Configurable ptPrPidTofMin{"ptPrPidTofMin", -1, "Lower bound of track pT for TOF PID for proton selection"}; + Configurable ptPrPidTofMax{"ptPrPidTofMax", 9999.9, "Upper bound of track pT for TOF PID for proton selection"}; + Configurable nSigmaTofPrMax{"nSigmaTofPrMax", 3., "Nsigma cut on TOF only for proton selection"}; + Configurable nSigmaTofCombinedPrMax{"nSigmaTofCombinedPrMax", 0., "Nsigma cut on TOF combined with TPC for proton selection"}; + + Configurable ptKaPidTofMin{"ptKaPidTofMin", -1, "Lower bound of track pT for TOF PID for kaon selection"}; + Configurable ptKaPidTofMax{"ptKaPidTofMax", 9999.9, "Upper bound of track pT for TOF PID for kaon selection"}; + Configurable nSigmaTofKaMax{"nSigmaTofKaMax", 3., "Nsigma cut on TOF only for kaon selection"}; + Configurable nSigmaTofCombinedKaMax{"nSigmaTofCombinedKaMax", 0., "Nsigma cut on TOF combined with TPC for kaon selection"}; + + // detector clusters selections + Configurable nClustersTpcMin{"nClustersTpcMin", 70, "Minimum number of TPC clusters requirement"}; + Configurable nTpcCrossedRowsMin{"nTpcCrossedRowsMin", 70, "Minimum number of TPC crossed rows requirement"}; + Configurable tpcCrossedRowsOverFindableClustersRatioMin{"tpcCrossedRowsOverFindableClustersRatioMin", 0.8, "Minimum ratio TPC crossed rows over findable clusters requirement"}; + Configurable tpcChi2PerClusterMax{"tpcChi2PerClusterMax", 4, "Maximum value of chi2 fit over TPC clusters"}; + Configurable nClustersItsMin{"nClustersItsMin", 3, "Minimum number of ITS clusters requirement for pi <- charm baryon"}; + Configurable nClustersItsInnBarrMin{"nClustersItsInnBarrMin", 1, "Minimum number of ITS clusters in inner barrel requirement for pi <- charm baryon"}; + Configurable itsChi2PerClusterMax{"itsChi2PerClusterMax", 36, "Maximum value of chi2 fit over ITS clusters for pi <- charm baryon"}; + + o2::analysis::HfMlResponseOmegacToOmegaPi hfMlResponseDca; + o2::analysis::HfMlResponseOmegacToOmegaPi hfMlResponseKf; + std::vector outputMlOmegac; + o2::ccdb::CcdbApi ccdbApi; + + TrackSelectorPi selectorPion; + TrackSelectorPr selectorProton; + TrackSelectorKa selectorKaon; + + using TracksSel = soa::Join; + using TracksSelLf = soa::Join; + + HistogramRegistry registry{"registry"}; // for QA of selections + + struct : ConfigurableGroup { + //// KF selection + std::string prefix = "kfSel"; + Configurable applyKFpreselections{"applyKFpreselections", false, "Apply KFParticle related rejection"}; + Configurable applyCompetingCascRejection{"applyCompetingCascRejection", false, "Apply competing Xi(for Omegac0) rejection"}; + Configurable cascadeRejMassWindow{"cascadeRejMassWindow", 0.01, "competing Xi(for Omegac0) rejection mass window"}; + Configurable v0LdlMin{"v0LdlMin", 3., "Minimum value of l/dl of V0"}; // l/dl and Chi2 are to be determined + Configurable cascLdlMin{"cascLdlMin", 1., "Minimum value of l/dl of casc"}; + Configurable omegacLdlMax{"omegacLdlMax", 5., "Maximum value of l/dl of Omegac"}; + Configurable cTauOmegacMax{"cTauOmegacMax", 0.4, "lifetime τ of Omegac"}; + Configurable v0Chi2OverNdfMax{"v0Chi2OverNdfMax", 100., "Maximum chi2Geo/NDF of V0"}; + Configurable cascChi2OverNdfMax{"cascChi2OverNdfMax", 100., "Maximum chi2Geo/NDF of casc"}; + Configurable omegacChi2OverNdfMax{"omegacChi2OverNdfMax", 100., "Maximum chi2Geo/NDF of Omegac"}; + Configurable chi2TopoV0ToCascMax{"chi2TopoV0ToCascMax", 100., "Maximum chi2Topo/NDF of V0ToCas"}; + Configurable chi2TopoOmegacToPvMax{"chi2TopoOmegacToPvMax", 100., "Maximum chi2Topo/NDF of OmegacToPv"}; + Configurable chi2TopoCascToOmegacMax{"chi2TopoCascToOmegacMax", 100., "Maximum chi2Topo/NDF of CascToOmegac"}; + Configurable chi2TopoCascToPvMax{"chi2TopoCascToPvMax", 100., "Maximum chi2Topo/NDF of CascToPv"}; + Configurable decayLenXYOmegacMax{"decayLenXYOmegacMax", 1.5, "Maximum decay lengthXY of Omegac"}; + Configurable decayLenXYCascMin{"decayLenXYCascMin", 1., "Minimum decay lengthXY of Cascade"}; + Configurable decayLenXYLambdaMin{"decayLenXYLambdaMin", 0., "Minimum decay lengthXY of V0"}; + Configurable cosPaCascToOmegacMin{"cosPaCascToOmegacMin", 0.995, "Minimum cosPA of cascade<-Omegac"}; + Configurable cosPaV0ToCascMin{"cosPaV0ToCascMin", 0.99, "Minimum cosPA of V0<-cascade"}; + } kfConfigurableGroup; + + void init(InitContext const&) + { + std::array processesSelector = {doprocessOmegac0SelectorWithDCAFitter, doprocessOmegac0SelectorWithKFParticle}; + const int nProcessesSelector = std::accumulate(processesSelector.begin(), processesSelector.end(), 0); + if (nProcessesSelector != 1) { + LOGP(fatal, "Exactly one process function for selector can be enabled at a time."); + } + + selectorPion.setRangePtTpc(ptPiPidTpcMin, ptPiPidTpcMax); + selectorPion.setRangeNSigmaTpc(-nSigmaTpcPiMax, nSigmaTpcPiMax); + selectorPion.setRangeNSigmaTpcCondTof(-nSigmaTpcCombinedPiMax, nSigmaTpcCombinedPiMax); + selectorPion.setRangePtTof(ptPiPidTofMin, ptPiPidTofMax); + selectorPion.setRangeNSigmaTof(-nSigmaTofPiMax, nSigmaTofPiMax); + selectorPion.setRangeNSigmaTofCondTpc(-nSigmaTofCombinedPiMax, nSigmaTofCombinedPiMax); + + selectorProton.setRangePtTpc(ptPrPidTpcMin, ptPrPidTpcMax); + selectorProton.setRangeNSigmaTpc(-nSigmaTpcPrMax, nSigmaTpcPrMax); + selectorProton.setRangeNSigmaTpcCondTof(-nSigmaTpcCombinedPrMax, nSigmaTpcCombinedPrMax); + selectorProton.setRangePtTof(ptPrPidTofMin, ptPrPidTofMax); + selectorProton.setRangeNSigmaTof(-nSigmaTofPrMax, nSigmaTofPrMax); + selectorProton.setRangeNSigmaTofCondTpc(-nSigmaTofCombinedPrMax, nSigmaTofCombinedPrMax); + + selectorKaon.setRangePtTpc(ptKaPidTpcMin, ptKaPidTpcMax); + selectorKaon.setRangeNSigmaTpc(-nSigmaTpcKaMax, nSigmaTpcKaMax); + selectorKaon.setRangeNSigmaTpcCondTof(-nSigmaTpcCombinedKaMax, nSigmaTpcCombinedKaMax); + selectorKaon.setRangePtTof(ptKaPidTofMin, ptKaPidTofMax); + selectorKaon.setRangeNSigmaTof(-nSigmaTofKaMax, nSigmaTofKaMax); + selectorKaon.setRangeNSigmaTofCondTpc(-nSigmaTofCombinedKaMax, nSigmaTofCombinedKaMax); + + const AxisSpec axisSel{2, -0.5, 1.5, "status"}; + const AxisSpec axisSelOnLfDca{16, -0.5, 15.5, "status"}; + const AxisSpec axisSelOnLfKf{23, -0.5, 22.5, "status"}; + const AxisSpec axisSelOnHfDca{6, -0.5, 5.5, "status"}; + const AxisSpec axisSelOnHfKf{12, -0.5, 11.5, "status"}; + + // for QA of the selections (bin 0 -> candidates that did not pass the selection, bin 1 -> candidates that passed the selection) + registry.add("hSelSignDec", "hSelSignDec;status;entries", {HistType::kTH1F, {axisSel}}); + registry.add("hSelStatusCluster", "hSelStatusCluster:# of events Passed;;", {HistType::kTH1F, {{6, -0.5, 5.5}}}); + registry.get(HIST("hSelStatusCluster"))->GetXaxis()->SetBinLabel(1, "All"); + registry.get(HIST("hSelStatusCluster"))->GetXaxis()->SetBinLabel(2, "TpcCluster PiFromV0"); + registry.get(HIST("hSelStatusCluster"))->GetXaxis()->SetBinLabel(3, "TpcCluster PrFromV0"); + registry.get(HIST("hSelStatusCluster"))->GetXaxis()->SetBinLabel(4, "TpcCluster KaFromCasc"); + registry.get(HIST("hSelStatusCluster"))->GetXaxis()->SetBinLabel(5, "TpcCluster PiFromCharm"); + registry.get(HIST("hSelStatusCluster"))->GetXaxis()->SetBinLabel(6, "ItsCluster PiFromCharm"); + + registry.add("hSelStatusPID", "hSelStatusPID;# of events Passed;;", {HistType::kTH1F, {{4, -0.5, 3.5}}}); + registry.get(HIST("hSelStatusPID"))->GetXaxis()->SetBinLabel(1, "All"); + registry.get(HIST("hSelStatusPID"))->GetXaxis()->SetBinLabel(2, "Lambda"); + registry.get(HIST("hSelStatusPID"))->GetXaxis()->SetBinLabel(3, "Cascade"); + registry.get(HIST("hSelStatusPID"))->GetXaxis()->SetBinLabel(4, "CharmBaryon"); + + // For QA of LF & HF selection + if (doprocessOmegac0SelectorWithDCAFitter) { + registry.add("hSelStatusLf", "hSelStatusLf;# of candidate passed;", {HistType::kTH1F, {axisSelOnLfDca}}); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(1, "All"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(2, "etaV0PosDau"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(3, "etaV0NegDau"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(4, "etaKaFromCasc"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(5, "radiusV0"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(6, "radiusCasc"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(7, "cosPAV0"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(8, "cosPACasc"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(9, "dcaV0Dau"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(10, "dcaCascDau"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(11, "dcaXYToPvV0Dau0"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(12, "dcaXYToPvV0Dau1"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(13, "dcaXYToPvCascDau"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(14, "ptKaFromCasc"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(15, "impactParCascXY"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(16, "impactParCascZ"); + + registry.add("hSelStatusHf", "hSelStatusHf;# of candidate passed;", {HistType::kTH1F, {axisSelOnHfDca}}); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(1, "All"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(2, "etaTrackCharmBach"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(3, "dcaCharmBaryonDau"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(4, "ptPiFromCharmBaryon"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(5, "impactParBachFromCharmXY"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(6, "impactParBachFromCharmZ"); + } + + if (doprocessOmegac0SelectorWithKFParticle) { + registry.add("hSelStatusLf", "hSelStatusLf;# of candidate passed;", {HistType::kTH1F, {axisSelOnLfKf}}); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(1, "All"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(2, "etaV0PosDau"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(3, "etaV0NegDau"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(4, "etaKaFromCasc"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(5, "radiusV0"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(6, "radiusCasc"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(7, "cosPAV0"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(8, "cosPACasc"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(9, "dcaV0Dau"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(10, "dcaCascDau"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(11, "dcaXYToPvV0Dau0"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(12, "dcaXYToPvV0Dau1"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(13, "dcaXYToPvCascDau"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(14, "ptKaFromCasc"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(15, "cosPaV0ToCasc"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(16, "v0Chi2OverNdf"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(17, "cascChi2OverNdf"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(18, "chi2TopoCascToPv"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(19, "chi2TopoV0ToCasc"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(20, "v0ldl"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(21, "cascldl"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(22, "decayLenXYLambda"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(23, "decayLenXYCasc"); + + registry.add("hSelStatusHf", "hSelStatusHf;# of candidate passed;", {HistType::kTH1F, {axisSelOnHfKf}}); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(1, "All"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(2, "etaTrackCharmBach"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(3, "dcaCharmBaryonDau"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(4, "ptPiFromCharmBaryon"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(5, "kfptOmegac"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(6, "cosPaCascToOmegac"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(7, "omegacChi2OverNdf"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(8, "chi2TopoOmegacToPv"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(9, "chi2TopoCascToOmegac"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(10, "decayLenXYOmegac"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(11, "cTauOmegac"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(12, "omegacldl"); + } + + registry.add("hInvMassCharmBaryon", "Charm baryon invariant mass; inv. mass; entries", {HistType::kTH1F, {{500, 2.3, 3.1}}}); + registry.add("hPtCharmBaryon", "Charm baryon transverse momentum; p_{T} (GeV/#it{c}); entries", {HistType::kTH1F, {{8000, 0., 80.}}}); + + if (doprocessOmegac0SelectorWithKFParticle) { + registry.add("hSelCompetingCasc", "hSelCompetingCasc;status;entries", {HistType::kTH1F, {axisSel}}); + registry.add("hInvMassXiMinus_rej_cut", "hInvMassXiMinus_rej_cut;m_{#Lambda#pi} under Xi hypothesis (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{1000, 1.25f, 1.65f}}}); + } + + // HfMlResponse initialization + if (applyMl) { + if (doprocessOmegac0SelectorWithKFParticle) { + registry.add("hBDTScoreKF", "hBDTScoreKF", {HistType::kTH1D, {{100, 0.0f, 1.0f, "score"}}}); + hfMlResponseKf.configure(binsPtMl, cutsMl, cutDirMl, nClassesMl); + if (loadModelsFromCCDB) { + ccdbApi.init(ccdbUrl); + hfMlResponseKf.setModelPathsCCDB(onnxFileNames, ccdbApi, modelPathsCCDB, timestampCCDB); + } else { + hfMlResponseKf.setModelPathsLocal(onnxFileNames); + } + hfMlResponseKf.cacheInputFeaturesIndices(namesInputFeatures); + hfMlResponseKf.init(); + } else if (doprocessOmegac0SelectorWithDCAFitter) { + registry.add("hBDTScoreDCA", "hBDTScoreDCA", {HistType::kTH1D, {{100, 0.0f, 1.0f, "score"}}}); + hfMlResponseDca.configure(binsPtMl, cutsMl, cutDirMl, nClassesMl); + if (loadModelsFromCCDB) { + ccdbApi.init(ccdbUrl); + hfMlResponseDca.setModelPathsCCDB(onnxFileNames, ccdbApi, modelPathsCCDB, timestampCCDB); + } else { + hfMlResponseDca.setModelPathsLocal(onnxFileNames); + } + hfMlResponseDca.cacheInputFeaturesIndices(namesInputFeatures); + hfMlResponseDca.init(); + } + } + } + + // LF cuts - Cuts on LF tracks reco + // Selection on LF related informations + // returns true if all cuts are passed + template + bool selectOnLf(const T& candidate) + { + + registry.fill(HIST("hSelStatusLf"), 0.0); + + // Eta selection of V0, Cascade daughters + double etaV0PosDau = candidate.etaV0PosDau(); + double etaV0NegDau = candidate.etaV0NegDau(); + double etaKaFromCasc = candidate.etaBachFromCasc(); + + if (std::abs(etaV0PosDau) > etaTrackLFDauMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 1.0); + + if (std::abs(etaV0NegDau) > etaTrackLFDauMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 2.0); + + if (std::abs(etaKaFromCasc) > etaTrackLFDauMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 3.0); + + // Minimum radius cut + double radiusV0 = RecoDecay::sqrtSumOfSquares(candidate.xDecayVtxV0(), candidate.yDecayVtxV0()); + double radiusCasc = RecoDecay::sqrtSumOfSquares(candidate.xDecayVtxCascade(), candidate.yDecayVtxCascade()); + + if (radiusV0 < radiusV0Min) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 4.0); + if (radiusCasc < radiusCascMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 5.0); + + // Cosine of pointing angle + if (candidate.cosPAV0() < cosPAV0Min) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 6.0); + if (candidate.cosPACasc() < cosPACascMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 7.0); + + // Distance of Closest Approach(DCA) + if (candidate.dcaV0Dau() > dcaV0DauMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 8.0); + + if (candidate.dcaCascDau() > dcaCascDauMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 9.0); + + if (std::abs(candidate.dcaXYToPvV0Dau0()) < dcaPosToPvMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 10.0); + + if (std::abs(candidate.dcaXYToPvV0Dau1()) < dcaNegToPvMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 11.0); + + if (std::abs(candidate.dcaXYToPvCascDau()) < dcaBachToPvMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 12.0); + + // pT: Bachelor + double ptKaFromCasc = RecoDecay::sqrtSumOfSquares(candidate.pxBachFromCasc(), candidate.pyBachFromCasc()); + if (std::abs(ptKaFromCasc) < ptKaFromCascMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 13.0); + + // Extra cuts for KFParticle + if constexpr (svReco == doKfParticle) { + if (kfConfigurableGroup.applyKFpreselections) { + // Cosine of Pointing angle + if (candidate.cosPaV0ToCasc() < kfConfigurableGroup.cosPaV0ToCascMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 14.0); + + // Chi2 + if (candidate.v0Chi2OverNdf() < 0 || candidate.v0Chi2OverNdf() > kfConfigurableGroup.v0Chi2OverNdfMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 15.0); + if (candidate.cascChi2OverNdf() < 0 || candidate.cascChi2OverNdf() > kfConfigurableGroup.cascChi2OverNdfMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 16.0); + if (candidate.chi2TopoCascToPv() < 0 || candidate.chi2TopoCascToPv() > kfConfigurableGroup.chi2TopoCascToPvMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 17.0); + if (candidate.chi2TopoV0ToCasc() < 0 || candidate.chi2TopoV0ToCasc() > kfConfigurableGroup.chi2TopoV0ToCascMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 18.0); + + // ldl + if (candidate.v0ldl() < kfConfigurableGroup.v0LdlMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 19.0); + if (candidate.cascldl() < kfConfigurableGroup.cascLdlMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 20.0); + + // Decay length + if (std::abs(candidate.decayLenXYLambda()) < kfConfigurableGroup.decayLenXYLambdaMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 21.0); + if (std::abs(candidate.decayLenXYCasc()) < kfConfigurableGroup.decayLenXYCascMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 22.0); + + // Competing Xi rejection + if (kfConfigurableGroup.applyCompetingCascRejection) { + const auto invMassXiHypothesis = candidate.cascRejectInvmass(); + + if (std::abs(invMassXiHypothesis - o2::constants::physics::MassXiMinus) < kfConfigurableGroup.cascadeRejMassWindow) { + registry.fill(HIST("hSelCompetingCasc"), 0.0); + return false; + } + + registry.fill(HIST("hSelCompetingCasc"), 1.0); + registry.fill(HIST("hInvMassXiMinus_rej_cut"), invMassXiHypothesis); + } + } + } else { + // Impact parameter + if (std::abs(candidate.impactParCascXY()) < impactParameterXYCascMin || std::abs(candidate.impactParCascXY()) > impactParameterXYCascMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 14.0); + if (std::abs(candidate.impactParCascZ()) < impactParameterZCascMin || std::abs(candidate.impactParCascZ()) > impactParameterZCascMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 15.0); + } + + // If passes all cuts, return true + return true; + } + + // HF cuts - Cuts on Charm baryon reco + // Apply cuts with charm baryon & charm bachelor related informations + // returns true if all cuts are passed + template + bool selectOnHf(const T& candidate, const int& inputPtBin) + { + registry.fill(HIST("hSelStatusHf"), 0.0); + + // eta selection on charm bayron bachelor + if (std::abs(candidate.etaBachFromCharmBaryon()) > etaTrackCharmBachMax) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 1.0); + // Distance of Closest Approach(DCA) + if (candidate.dcaCharmBaryonDau() > dcaCharmBaryonDauMax) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 2.0); + + // pT: Charm Bachelor + double ptPiFromCharmBaryon = RecoDecay::sqrtSumOfSquares(candidate.pxBachFromCharmBaryon(), candidate.pyBachFromCharmBaryon()); + if (inputPtBin < 0 || ptPiFromCharmBaryon < cuts->get(inputPtBin, "pT pi from Omegac")) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 3.0); + + // specific selections with KFParticle output + if constexpr (svReco == doKfParticle) { + if (kfConfigurableGroup.applyKFpreselections) { + // Omegac Pt selection + if (std::abs(candidate.kfptOmegac()) <= ptCandMin || std::abs(candidate.kfptOmegac()) >= ptCandMax) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 4.0); + + // Cosine of pointing angle + if (candidate.cosPaCascToOmegac() < kfConfigurableGroup.cosPaCascToOmegacMin) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 5.0); + + // Chi2 + if (candidate.omegacChi2OverNdf() < 0 || candidate.omegacChi2OverNdf() > kfConfigurableGroup.omegacChi2OverNdfMax) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 6.0); + if (candidate.chi2TopoOmegacToPv() < 0 || candidate.chi2TopoOmegacToPv() > kfConfigurableGroup.chi2TopoOmegacToPvMax) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 7.0); + if (candidate.chi2TopoCascToOmegac() < 0 || candidate.chi2TopoCascToOmegac() > kfConfigurableGroup.chi2TopoCascToOmegacMax) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 8.0); + + // Decay Length + if (std::abs(candidate.decayLenXYOmegac()) > kfConfigurableGroup.decayLenXYOmegacMax) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 9.0); + + // ctau + if (std::abs(candidate.cTauOmegac()) > kfConfigurableGroup.cTauOmegacMax) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 10.0); + + // Omegac l/dl + if (candidate.omegacldl() > kfConfigurableGroup.omegacLdlMax) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 11.0); + } + } else { + // Impact parameter + if ((std::abs(candidate.impactParBachFromCharmBaryonXY()) < impactParameterXYPiFromCharmBaryonMin) || (std::abs(candidate.impactParBachFromCharmBaryonXY()) > impactParameterXYPiFromCharmBaryonMax)) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 4.0); + if ((std::abs(candidate.impactParBachFromCharmBaryonZ()) < impactParameterZPiFromCharmBaryonMin) || (std::abs(candidate.impactParBachFromCharmBaryonZ()) > impactParameterZPiFromCharmBaryonMax)) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 5.0); + } + + // If passes all cuts, return true + return true; + } + + template + void runOmegac0Selector(TCandTable const& candidates, + TracksSel const& tracks, + TracksSelLf const& lfTracks) + { + // looping over charm baryon candidates + for (const auto& candidate : candidates) { + + bool resultSelections = true; // True if the candidate passes all the selections, False otherwise + outputMlOmegac.clear(); + + auto trackV0PosDau = lfTracks.rawIteratorAt(candidate.posTrackId()); + auto trackV0NegDau = lfTracks.rawIteratorAt(candidate.negTrackId()); + auto trackKaFromCasc = lfTracks.rawIteratorAt(candidate.bachelorId()); + auto trackPiFromCharm = tracks.rawIteratorAt(candidate.bachelorFromCharmBaryonId()); + + auto trackPiFromLam = trackV0NegDau; + auto trackPrFromLam = trackV0PosDau; + + int8_t const signDecay = candidate.signDecay(); // sign of pi <- cascade + + if (signDecay > 0) { + trackPiFromLam = trackV0PosDau; + trackPrFromLam = trackV0NegDau; + registry.fill(HIST("hSelSignDec"), 1); // anti-particle decay + } else { + registry.fill(HIST("hSelSignDec"), 0); // particle decay + } + + // pT selection + auto ptCandOmegac = RecoDecay::pt(candidate.pxCharmBaryon(), candidate.pyCharmBaryon()); + + if (ptCandOmegac < ptCandMin || ptCandOmegac > ptCandMax) { + resultSelections = false; + } + + int pTBin = findBin(binsPt, ptCandOmegac); + if (pTBin == -1) { + resultSelections = false; + } + + // Topological selection + const bool selectionResOnLF = selectOnLf(candidate); + const bool selectionResOnHF = selectOnHf(candidate, pTBin); + if (!selectionResOnLF || !selectionResOnHF) { + resultSelections = false; + } + + // TPC clusters selections + if (resultSelections) { + registry.fill(HIST("hSelStatusCluster"), 0.0); + } + if (applyTrkSelLf) { + if (!isSelectedTrackTpcQuality(trackPiFromLam, nClustersTpcMin, nTpcCrossedRowsMin, tpcCrossedRowsOverFindableClustersRatioMin, tpcChi2PerClusterMax)) { + resultSelections = false; + } else { + if (resultSelections) { + registry.fill(HIST("hSelStatusCluster"), 1.0); + } + } + + if (!isSelectedTrackTpcQuality(trackPrFromLam, nClustersTpcMin, nTpcCrossedRowsMin, tpcCrossedRowsOverFindableClustersRatioMin, tpcChi2PerClusterMax)) { + resultSelections = false; + } else { + if (resultSelections) { + registry.fill(HIST("hSelStatusCluster"), 2.0); + } + } + + if (!isSelectedTrackTpcQuality(trackKaFromCasc, nClustersTpcMin, nTpcCrossedRowsMin, tpcCrossedRowsOverFindableClustersRatioMin, tpcChi2PerClusterMax)) { + resultSelections = false; + } else { + if (resultSelections) { + registry.fill(HIST("hSelStatusCluster"), 3.0); + } + } + } + + if (!isSelectedTrackTpcQuality(trackPiFromCharm, nClustersTpcMin, nTpcCrossedRowsMin, tpcCrossedRowsOverFindableClustersRatioMin, tpcChi2PerClusterMax)) { + resultSelections = false; + } else { + if (resultSelections) { + registry.fill(HIST("hSelStatusCluster"), 4.0); + } + } + + // ITS clusters selection + if (!isSelectedTrackItsQuality(trackPiFromCharm, nClustersItsMin, itsChi2PerClusterMax) || trackPiFromCharm.itsNClsInnerBarrel() < nClustersItsInnBarrMin) { + resultSelections = false; + } else { + if (resultSelections) { + registry.fill(HIST("hSelStatusCluster"), 5.0); + } + } + + // Track level PID selection + if (resultSelections) { + registry.fill(HIST("hSelStatusPID"), 0.0); + } + int statusPidPrFromLam = -999; + int statusPidPiFromLam = -999; + int statusPidKaFromCasc = -999; + int statusPidPiFromCharmBaryon = -999; + + int infoTpcStored = 0; + int infoTofStored = 0; + + if (usePidTpcOnly == usePidTpcTofCombined) { + LOGF(fatal, "Check the PID configurables, usePidTpcOnly and usePidTpcTofCombined can't have the same value"); + } + + if (trackPiFromLam.hasTPC()) { + SETBIT(infoTpcStored, PiFromLam); + } + if (trackPrFromLam.hasTPC()) { + SETBIT(infoTpcStored, PrFromLam); + } + if (trackKaFromCasc.hasTPC()) { + SETBIT(infoTpcStored, KaFromCasc); + } + if (trackPiFromCharm.hasTPC()) { + SETBIT(infoTpcStored, PiFromCharm); + } + if (trackPiFromLam.hasTOF()) { + SETBIT(infoTofStored, PiFromLam); + } + if (trackPrFromLam.hasTOF()) { + SETBIT(infoTofStored, PrFromLam); + } + if (trackKaFromCasc.hasTOF()) { + SETBIT(infoTofStored, KaFromCasc); + } + if (trackPiFromCharm.hasTOF()) { + SETBIT(infoTofStored, PiFromCharm); + } + + if (usePidTpcOnly) { + statusPidPrFromLam = selectorProton.statusTpc(trackPrFromLam); + statusPidPiFromLam = selectorPion.statusTpc(trackPiFromLam); + statusPidKaFromCasc = selectorKaon.statusTpc(trackKaFromCasc); + statusPidPiFromCharmBaryon = selectorPion.statusTpc(trackPiFromCharm); + } else if (usePidTpcTofCombined) { + statusPidPrFromLam = selectorProton.statusTpcOrTof(trackPrFromLam); + statusPidPiFromLam = selectorPion.statusTpcOrTof(trackPiFromLam); + statusPidKaFromCasc = selectorKaon.statusTpcOrTof(trackKaFromCasc); + statusPidPiFromCharmBaryon = selectorPion.statusTpcOrTof(trackPiFromCharm); + } + + bool statusPidLambda = (statusPidPrFromLam == TrackSelectorPID::Accepted) && (statusPidPiFromLam == TrackSelectorPID::Accepted); + if (statusPidLambda && resultSelections) { + registry.fill(HIST("hSelStatusPID"), 1.0); + } + bool statusPidCascade = (statusPidLambda && statusPidKaFromCasc == TrackSelectorPID::Accepted); + if (statusPidCascade && resultSelections) { + registry.fill(HIST("hSelStatusPID"), 2.0); + } + bool statusPidCharmBaryon = (statusPidCascade && statusPidPiFromCharmBaryon == TrackSelectorPID::Accepted); + if (statusPidCharmBaryon && resultSelections) { + registry.fill(HIST("hSelStatusPID"), 3.0); + } + + // invariant mass cuts + bool statusInvMassLambda = false; + bool statusInvMassCascade = false; + bool statusInvMassCharmBaryon = false; + + double const invMassLambda = candidate.invMassLambda(); + double const invMassCascade = candidate.invMassCascade(); + double const invMassCharmBaryon = candidate.invMassCharmBaryon(); + + if (std::abs(invMassLambda - o2::constants::physics::MassLambda0) < v0MassWindow) { + statusInvMassLambda = true; + } + if (std::abs(invMassCascade - o2::constants::physics::MassOmegaMinus) < cascadeMassWindow) { + statusInvMassCascade = true; + } + if ((invMassCharmBaryon >= invMassCharmBaryonMin) && (invMassCharmBaryon <= invMassCharmBaryonMax)) { + statusInvMassCharmBaryon = true; + } + + // Fill in selection result + if (!statusPidLambda || !statusPidCascade || !statusPidCharmBaryon || + !statusInvMassLambda || !statusInvMassCascade || !statusInvMassCharmBaryon) { + resultSelections = false; + } + + // Check candidate pT range for ML inference + if (applyMl && findBin(binsPtMl, ptCandOmegac) == -1) { + resultSelections = false; + } + + // ML BDT selection + if (applyMl && resultSelections) { + bool isSelectedMlOmegac = false; + std::vector inputFeaturesOmegaC = {}; + + if constexpr (svReco == doKfParticle) { + inputFeaturesOmegaC = hfMlResponseKf.getInputFeatures(candidate, trackPiFromLam, trackKaFromCasc, trackPiFromCharm); + isSelectedMlOmegac = hfMlResponseKf.isSelectedMl(inputFeaturesOmegaC, ptCandOmegac, outputMlOmegac); + if (isSelectedMlOmegac) { + registry.fill(HIST("hBDTScoreKF"), outputMlOmegac[0]); + } else { + resultSelections = false; + } + } else if constexpr (svReco == doDcaFitter) { + inputFeaturesOmegaC = hfMlResponseDca.getInputFeatures(candidate, trackPiFromLam, trackKaFromCasc, trackPiFromCharm); + isSelectedMlOmegac = hfMlResponseDca.isSelectedMl(inputFeaturesOmegaC, ptCandOmegac, outputMlOmegac); + if (isSelectedMlOmegac) { + registry.fill(HIST("hBDTScoreDCA"), outputMlOmegac[0]); + } else { + resultSelections = false; + } + } + } + + if (applyMl) { + hfMlSelToOmegaPi(outputMlOmegac); + } + + hfSelToOmegaPi(statusPidLambda, statusPidCascade, statusPidCharmBaryon, statusInvMassLambda, statusInvMassCascade, statusInvMassCharmBaryon, resultSelections, infoTpcStored, infoTofStored, + trackPiFromCharm.tpcNSigmaPi(), trackKaFromCasc.tpcNSigmaKa(), trackPiFromLam.tpcNSigmaPi(), trackPrFromLam.tpcNSigmaPr(), + trackPiFromCharm.tofNSigmaPi(), trackKaFromCasc.tofNSigmaKa(), trackPiFromLam.tofNSigmaPi(), trackPrFromLam.tofNSigmaPr()); + + // Fill in invariant mass histogram + if (statusPidLambda && statusPidCascade && statusPidCharmBaryon && statusInvMassLambda && statusInvMassCascade && statusInvMassCharmBaryon && resultSelections) { + registry.fill(HIST("hInvMassCharmBaryon"), invMassCharmBaryon); + if constexpr (svReco == doKfParticle) { + registry.fill(HIST("hPtCharmBaryon"), candidate.kfptOmegac()); + } else { + registry.fill(HIST("hPtCharmBaryon"), ptCandOmegac); + } + } + + } // end of candidate loop + } // end run function + + /////////////////////////////////// + /// Process with DCAFitter // + /////////////////////////////////// + void processOmegac0SelectorWithDCAFitter(aod::HfCandToOmegaPi const& candidates, + TracksSel const& tracks, + TracksSelLf const& lfTracks) + { + runOmegac0Selector(candidates, tracks, lfTracks); + } + PROCESS_SWITCH(HfCandidateSelectorToOmegaPiQa, processOmegac0SelectorWithDCAFitter, "Omegac0 candidate selection with DCAFitter output", true); + + //////////////////////////////////// + /// Process with KFParticle // + //////////////////////////////////// + void processOmegac0SelectorWithKFParticle(soa::Join const& candidates, + TracksSel const& tracks, + TracksSelLf const& lfTracks) + { + runOmegac0Selector(candidates, tracks, lfTracks); + } + PROCESS_SWITCH(HfCandidateSelectorToOmegaPiQa, processOmegac0SelectorWithKFParticle, "Omegac0 candidate selection with KFParticle output", false); + +}; // end struct + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGHF/TableProducer/candidateSelectorToXiPiQa.cxx b/PWGHF/TableProducer/candidateSelectorToXiPiQa.cxx index 8845d78ee70..2c8dfc1cbb7 100644 --- a/PWGHF/TableProducer/candidateSelectorToXiPiQa.cxx +++ b/PWGHF/TableProducer/candidateSelectorToXiPiQa.cxx @@ -78,12 +78,12 @@ struct HfCandidateSelectorToXiPiQa { // cuts from SelectorCuts.h - pT dependent cuts Configurable> binsPt{"binsPt", std::vector{hf_cuts_xic_to_xi_pi::vecBinsPt}, "pT bin limits"}; - Configurable> cuts{"cuts", {hf_cuts_xic_to_xi_pi::Cuts[0], hf_cuts_xic_to_xi_pi::NBinsPt, hf_cuts_xic_to_xi_pi::NCutVars, hf_cuts_xic_to_xi_pi::labelsPt, hf_cuts_xic_to_xi_pi::labelsCutVar}, "Xic0 candidate selection per Pt Bin"}; + Configurable> cuts{"cuts", {&hf_cuts_xic_to_xi_pi::Cuts[0][0], hf_cuts_xic_to_xi_pi::NBinsPt, hf_cuts_xic_to_xi_pi::NCutVars, hf_cuts_xic_to_xi_pi::labelsPt, hf_cuts_xic_to_xi_pi::labelsCutVar}, "Xic0 candidate selection per Pt Bin"}; // ML inference Configurable applyMl{"applyMl", false, "Flag to apply ML selections"}; Configurable> binsPtMl{"binsPtMl", std::vector{hf_cuts_ml::vecBinsPt}, "pT bin limits for ML application"}; Configurable> cutDirMl{"cutDirMl", std::vector{hf_cuts_ml::vecCutDir}, "Whether to reject score values greater or smaller than the threshold"}; - Configurable> cutsMl{"cutsMl", {hf_cuts_ml::Cuts[0], hf_cuts_ml::NBinsPt, hf_cuts_ml::NCutScores, hf_cuts_ml::labelsPt, hf_cuts_ml::labelsCutScore}, "ML selections per pT bin"}; + Configurable> cutsMl{"cutsMl", {&hf_cuts_ml::Cuts[0][0], hf_cuts_ml::NBinsPt, hf_cuts_ml::NCutScores, hf_cuts_ml::labelsPt, hf_cuts_ml::labelsCutScore}, "ML selections per pT bin"}; Configurable nClassesMl{"nClassesMl", static_cast(hf_cuts_ml::NCutScores), "Number of classes in ML model"}; Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature1", "feature2"}, "Names of ML model input features"}; // CCDB configuration @@ -144,7 +144,7 @@ struct HfCandidateSelectorToXiPiQa { o2::analysis::HfMlResponseXic0ToXiPi hfMlResponseDca; o2::analysis::HfMlResponseXic0ToXiPiKf hfMlResponseKf; - std::vector outputMlXic0ToXiPi = {}; + std::vector outputMlXic0ToXiPi; o2::ccdb::CcdbApi ccdbApi; TrackSelectorPi selectorPion; @@ -718,12 +718,11 @@ struct HfCandidateSelectorToXiPiQa { // ML BDT selection if required if (applyMl) { bool isSelectedMlXic0 = false; - std::vector inputFeaturesXic0 = {}; if constexpr (svReco == doDcaFitter) { - inputFeaturesXic0 = hfMlResponseDca.getInputFeatures(candidate, trackPiFromLam, trackPiFromCasc, trackPiFromCharm); + auto inputFeaturesXic0 = hfMlResponseDca.getInputFeatures(candidate, trackPiFromLam, trackPiFromCasc, trackPiFromCharm); isSelectedMlXic0 = hfMlResponseDca.isSelectedMl(inputFeaturesXic0, ptCandXic0, outputMlXic0ToXiPi); } else { - inputFeaturesXic0 = hfMlResponseKf.getInputFeatures(candidate, trackPiFromLam, trackPiFromCasc, trackPiFromCharm); + auto inputFeaturesXic0 = hfMlResponseKf.getInputFeatures(candidate, trackPiFromLam, trackPrFromLam, trackPiFromCasc, trackPiFromCharm); isSelectedMlXic0 = hfMlResponseKf.isSelectedMl(inputFeaturesXic0, ptCandXic0, outputMlXic0ToXiPi); } @@ -767,8 +766,8 @@ struct HfCandidateSelectorToXiPiQa { }; // end struct -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(context)}; } diff --git a/PWGHF/TableProducer/candidateSelectorXic0ToXiPiKf.cxx b/PWGHF/TableProducer/candidateSelectorXic0ToXiPiKf.cxx index 69d2a73339b..5139c504e57 100644 --- a/PWGHF/TableProducer/candidateSelectorXic0ToXiPiKf.cxx +++ b/PWGHF/TableProducer/candidateSelectorXic0ToXiPiKf.cxx @@ -120,15 +120,23 @@ struct HfCandidateSelectorXic0ToXiPiKf { // topological cuts Configurable> binsPt{"binsPt", std::vector{hf_cuts_xic_to_xi_pi::vecBinsPt}, "pT bin limits"}; - Configurable> cuts{"cuts", {hf_cuts_xic_to_xi_pi::Cuts[0], hf_cuts_xic_to_xi_pi::NBinsPt, hf_cuts_xic_to_xi_pi::NCutVars, hf_cuts_xic_to_xi_pi::labelsPt, hf_cuts_xic_to_xi_pi::labelsCutVar}, "Xic0 candidate selection per pT bin"}; + Configurable> cuts{"cuts", {&hf_cuts_xic_to_xi_pi::Cuts[0][0], hf_cuts_xic_to_xi_pi::NBinsPt, hf_cuts_xic_to_xi_pi::NCutVars, hf_cuts_xic_to_xi_pi::labelsPt, hf_cuts_xic_to_xi_pi::labelsCutVar}, "Xic0 candidate selection per pT bin"}; // ML inference Configurable applyMl{"applyMl", true, "Flag to apply ML selections"}; Configurable> binsPtMl{"binsPtMl", std::vector{hf_cuts_ml::vecBinsPt}, "pT bin limits for ML application"}; Configurable> cutDirMl{"cutDirMl", std::vector{hf_cuts_ml::vecCutDir}, "Whether to reject score values greater or smaller than the threshold"}; - Configurable> cutsMl{"cutsMl", {hf_cuts_ml::Cuts[0], hf_cuts_ml::NBinsPt, hf_cuts_ml::NCutScores, hf_cuts_ml::labelsPt, hf_cuts_ml::labelsCutScore}, "ML selections per pT bin"}; + Configurable> cutsMl{"cutsMl", {&hf_cuts_ml::Cuts[0][0], hf_cuts_ml::NBinsPt, hf_cuts_ml::NCutScores, hf_cuts_ml::labelsPt, hf_cuts_ml::labelsCutScore}, "ML selections per pT bin"}; Configurable nClassesMl{"nClassesMl", static_cast(hf_cuts_ml::NCutScores), "Number of classes in ML model"}; - Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature1", "feature2"}, "Names of ML model input features"}; + Configurable> namesInputFeatures{"namesInputFeatures", + std::vector{"fDecayLenXYXic", "fXicChi2OverNdf", "fChi2TopoXicToPv", "fDcaCharmBaryonDau", + "fKfDcaXYPiFromXic", "fChi2TopoPiFromXicToPv", "fKfptPiFromXic", "fDecayLenXYCasc", + "fCascChi2OverNdf", "fChi2TopoCascToPv", "fChi2TopoCascToXic", "fCosPaCascToPv", + "fCosPaCascToXic", "fDcaCascDau", "fKfDcaXYCascToPv", "fTpcNSigmaPiFromCasc", + "fDcaXYToPvCascDau", "fDecayLenXYLambda", "fV0Chi2OverNdf", "fChi2TopoV0ToPv", + "fChi2TopoV0ToCasc", "fCosPaV0ToPv", "fCosPaV0ToCasc", "fDcaV0Dau", + "fDcaXYToPvV0Dau0", "fDcaXYToPvV0Dau1", "fTpcNSigmaPrFromLambda", "fTpcNSigmaPiFromLambda"}, + "Names of ML model input features"}; // CCDB configuration Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; @@ -224,14 +232,11 @@ struct HfCandidateSelectorXic0ToXiPiKf { auto trackPiFromCasc = lfTracks.rawIteratorAt(trackPiFromCascId); auto trackPiFromCharm = tracks.rawIteratorAt(trackPiFromCharmId); - auto trackPiFromLam = trackV0NegDau; - auto trackPrFromLam = trackV0PosDau; - int8_t const signDecay = candidate.signDecay(); // sign of pi <- cascade + auto const trackPiFromLam = signDecay > 0 ? trackV0PosDau : trackV0NegDau; + auto const trackPrFromLam = signDecay > 0 ? trackV0NegDau : trackV0PosDau; if (signDecay > 0) { - trackPiFromLam = trackV0PosDau; - trackPrFromLam = trackV0NegDau; registry.fill(HIST("hSelSignDec"), 1); // anti-particle decay } else if (signDecay < 0) { registry.fill(HIST("hSelSignDec"), 0); // particle decay @@ -418,7 +423,7 @@ struct HfCandidateSelectorXic0ToXiPiKf { // ML selections if (applyMl) { bool isSelectedMlXic0 = false; - std::vector inputFeaturesXic0 = hfMlResponse.getInputFeatures(candidate, trackPiFromLam, trackPiFromCasc, trackPiFromCharm); + std::vector inputFeaturesXic0 = hfMlResponse.getInputFeatures(candidate, trackPiFromLam, trackPrFromLam, trackPiFromCasc, trackPiFromCharm); if (!resultSelections) { hfMlToXiPi(outputMlXic0ToXiPiKf); } else { @@ -526,7 +531,7 @@ struct HfCandidateSelectorXic0ToXiPiKf { } // dcaXY v0 daughters to PV cut - if (std::abs(candidate.dcaXYToPvV0Dau0()) < cuts->get(pTBin, "dcaXYToPvV0Dau0") || std::abs(candidate.dcaXYToPvV0Dau1()) < cuts->get(pTBin, "dcaXYToPvV0Dau0")) { + if (std::abs(candidate.dcaXYToPvV0Dau0()) < cuts->get(pTBin, "dcaXYToPvV0Dau0") || std::abs(candidate.dcaXYToPvV0Dau1()) < cuts->get(pTBin, "dcaXYToPvV0Dau1")) { return false; } @@ -597,8 +602,8 @@ struct HfCandidateSelectorXic0ToXiPiKf { }; // end struct -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(context)}; } diff --git a/PWGHF/TableProducer/candidateSelectorXicToPKPi.cxx b/PWGHF/TableProducer/candidateSelectorXicToPKPi.cxx index cd7d7028925..525b569b0e1 100644 --- a/PWGHF/TableProducer/candidateSelectorXicToPKPi.cxx +++ b/PWGHF/TableProducer/candidateSelectorXicToPKPi.cxx @@ -123,7 +123,7 @@ struct HfCandidateSelectorXicToPKPi { labels[1 + aod::SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/TableProducer/candidateSelectorXicToXiPiPi.cxx b/PWGHF/TableProducer/candidateSelectorXicToXiPiPi.cxx index 070e92a43e2..a2cd6957b92 100644 --- a/PWGHF/TableProducer/candidateSelectorXicToXiPiPi.cxx +++ b/PWGHF/TableProducer/candidateSelectorXicToXiPiPi.cxx @@ -92,7 +92,8 @@ struct HfCandidateSelectorXicToXiPiPi { Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB"}; Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; - o2::analysis::HfMlResponseXicToXiPiPi hfMlResponse; + o2::analysis::HfMlResponseXicToXiPiPi hfMlResponseDca; + o2::analysis::HfMlResponseXicToXiPiPi hfMlResponseKf; std::vector outputMlXicToXiPiPi; o2::ccdb::CcdbApi ccdbApi; TrackSelectorPi selectorPion; @@ -121,9 +122,13 @@ struct HfCandidateSelectorXicToXiPiPi { void init(InitContext&) { - if ((doprocessData + doprocessMc) != 1) { + if ((doprocessDataWithDCAFitter + doprocessDataWithKFParticle + doprocessMcWithDCAFitter + doprocessMcWithKFParticle) != 1) { LOGP(fatal, "Enable exactly one process function at a time."); } + const bool doprocessData = doprocessDataWithDCAFitter || doprocessDataWithKFParticle; + const bool doprocessMc = doprocessMcWithDCAFitter || doprocessMcWithKFParticle; + const bool doprocessDCAFitter = doprocessDataWithDCAFitter || doprocessMcWithDCAFitter; + [[maybe_unused]] const bool doprocessKFParticle = doprocessDataWithKFParticle || doprocessMcWithKFParticle; if (usePid) { // pion @@ -245,19 +250,28 @@ struct HfCandidateSelectorXicToXiPiPi { registry.add("hNCrossedRowsTpcBachelorPionRecBkg", "Reconstructed background;#it{N}_{rows}^{TPC} (#pi #leftarrow #Xi);entries", {HistType::kTH1F, {{103, 49.5, 152.5}}}); registry.add("hNCrossedRowsTpcV0PosDauRecBkg", "Reconstructed background;#it{N}_{rows}^{TPC} (V0 pos. dau.);entries", {HistType::kTH1F, {{103, 49.5, 152.5}}}); registry.add("hNCrossedRowsTpcV0NegDauRecBkg", "Reconstructed background;#it{N}_{rows}^{TPC} (V0 neg. dau.);entries", {HistType::kTH1F, {{103, 49.5, 152.5}}}); + /// reconstructed MC + registry.add("hMassPtRecSig", "Reconstructed truth matching MC;inv.mass(#Xi#pi#pi) [GeV/c^{2}];p_{T} [GeV/c]", {HistType::kTH2F, {{40, 2.3, 2.7}, {200, 0, 20}}}); } } if (applyMl) { - hfMlResponse.configure(binsPtMl, cutsMl, cutDirMl, nClassesMl); - if (loadModelsFromCCDB) { - ccdbApi.init(ccdbUrl); - hfMlResponse.setModelPathsCCDB(onnxFileNames, ccdbApi, modelPathsCCDB, timestampCCDB); + auto initMlResponse = [&](auto& hfMlResponseGeneric) { + hfMlResponseGeneric.configure(binsPtMl, cutsMl, cutDirMl, nClassesMl); + if (loadModelsFromCCDB) { + ccdbApi.init(ccdbUrl); + hfMlResponseGeneric.setModelPathsCCDB(onnxFileNames, ccdbApi, modelPathsCCDB, timestampCCDB); + } else { + hfMlResponseGeneric.setModelPathsLocal(onnxFileNames); + } + hfMlResponseGeneric.cacheInputFeaturesIndices(namesInputFeatures); + hfMlResponseGeneric.init(); + }; + if (doprocessDCAFitter) { + initMlResponse(hfMlResponseDca); } else { - hfMlResponse.setModelPathsLocal(onnxFileNames); + initMlResponse(hfMlResponseKf); } - hfMlResponse.cacheInputFeaturesIndices(namesInputFeatures); - hfMlResponse.init(); } } @@ -641,6 +655,8 @@ struct HfCandidateSelectorXicToXiPiPi { if constexpr (IsMc) { if (isMatchedSignal) { registry.fill(HIST("hSelCandidatesRecSig"), PidSelected); + if (fillQAHistograms) + registry.fill(HIST("hMassPtRecSig"), hfCandXic.invMassXicPlus(), hfCandXic.pt()); } else { registry.fill(HIST("hSelCandidatesRecBkg"), PidSelected); } @@ -666,7 +682,7 @@ struct HfCandidateSelectorXicToXiPiPi { /// \param hfCandXic Xic candidate /// \param statusXicToXiPiPi Flag to store selection status as defined in hf_sel_candidate_xic::XicToXiPiPiSelectionStep /// \param isMatchedSignal Flag to indicate if the candidate is matched to a genereated XiCplus MC particle - template + template void isBdtSelected(XicCandidate const& hfCandXic, int& statusXicToXiPiPi, bool const isMatchedSignal = false) @@ -674,8 +690,13 @@ struct HfCandidateSelectorXicToXiPiPi { bool isSelectedMlXicToXiPiPi = false; float const ptCandXic = hfCandXic.pt(); - std::vector inputFeaturesXicToXiPiPi = hfMlResponse.getInputFeatures(hfCandXic); - isSelectedMlXicToXiPiPi = hfMlResponse.isSelectedMl(inputFeaturesXicToXiPiPi, ptCandXic, outputMlXicToXiPiPi); + if constexpr (ReconstructionType == aod::hf_cand::VertexerType::DCAFitter) { + std::vector inputFeaturesXicToXiPiPi = hfMlResponseDca.getInputFeatures(hfCandXic); + isSelectedMlXicToXiPiPi = hfMlResponseDca.isSelectedMl(inputFeaturesXicToXiPiPi, ptCandXic, outputMlXicToXiPiPi); + } else { + std::vector inputFeaturesXicToXiPiPi = hfMlResponseKf.getInputFeatures(hfCandXic); + isSelectedMlXicToXiPiPi = hfMlResponseKf.isSelectedMl(inputFeaturesXicToXiPiPi, ptCandXic, outputMlXicToXiPiPi); + } hfMlXicToXiPiPiCandidate(outputMlXicToXiPiPi); @@ -696,8 +717,8 @@ struct HfCandidateSelectorXicToXiPiPi { } } - void processData(aod::HfCandXic const& hfCandsXic, - TracksExtraWPid const& tracks) + template + void runData(CandType const& hfCandsXic, TracksExtraWPid const& tracks) { for (const auto& hfCandXic : hfCandsXic) { int statusXicToXiPiPi = 0; @@ -716,7 +737,7 @@ struct HfCandidateSelectorXicToXiPiPi { // ML selection if (applyMl) { - isBdtSelected(hfCandXic, statusXicToXiPiPi); + isBdtSelected(hfCandXic, statusXicToXiPiPi); } else { registry.fill(HIST("hSelCandidates"), BdtSelected); } @@ -724,10 +745,9 @@ struct HfCandidateSelectorXicToXiPiPi { hfSelXicToXiPiPiCandidate(statusXicToXiPiPi); } } - PROCESS_SWITCH(HfCandidateSelectorXicToXiPiPi, processData, "Select candidates without MC matching information", true); - void processMc(soa::Join const& hfCandsXic, - TracksExtraWPid const& tracks) + template + void runMc(CandType const& hfCandsXic, TracksExtraWPid const& tracks) { for (const auto& hfCandXic : hfCandsXic) { int statusXicToXiPiPi = 0; @@ -736,7 +756,7 @@ struct HfCandidateSelectorXicToXiPiPi { } bool isMatchedCandidate = false; - if (hfCandXic.flagMcMatchRec() != int8_t(0)) { + if (hfCandXic.flagMcMatchRec() != static_cast(0)) { isMatchedCandidate = true; } @@ -751,7 +771,7 @@ struct HfCandidateSelectorXicToXiPiPi { // ML selection if (applyMl) { - isBdtSelected(hfCandXic, statusXicToXiPiPi, isMatchedCandidate); + isBdtSelected(hfCandXic, statusXicToXiPiPi, isMatchedCandidate); } else if (isMatchedCandidate) { registry.fill(HIST("hSelCandidatesRecSig"), BdtSelected); } else { @@ -761,7 +781,34 @@ struct HfCandidateSelectorXicToXiPiPi { hfSelXicToXiPiPiCandidate(statusXicToXiPiPi); } } - PROCESS_SWITCH(HfCandidateSelectorXicToXiPiPi, processMc, "Select candidates with MC matching information", false); + + void processDataWithDCAFitter(aod::HfCandXic const& hfCandsXic, + TracksExtraWPid const& tracks) + { + runData(hfCandsXic, tracks); + } + PROCESS_SWITCH(HfCandidateSelectorXicToXiPiPi, processDataWithDCAFitter, "Select candidates without MC matching information using DCAFitter", true); + + void processDataWithKFParticle(soa::Join const& hfCandsXic, + TracksExtraWPid const& tracks) + { + runData(hfCandsXic, tracks); + } + PROCESS_SWITCH(HfCandidateSelectorXicToXiPiPi, processDataWithKFParticle, "Select candidates without MC matching information using KFParticle", false); + + void processMcWithDCAFitter(soa::Join const& hfCandsXic, + TracksExtraWPid const& tracks) + { + runMc(hfCandsXic, tracks); + } + PROCESS_SWITCH(HfCandidateSelectorXicToXiPiPi, processMcWithDCAFitter, "Select candidates with MC matching information using DCAFitter", false); + + void processMcWithKFParticle(soa::Join const& hfCandsXic, + TracksExtraWPid const& tracks) + { + runMc(hfCandsXic, tracks); + } + PROCESS_SWITCH(HfCandidateSelectorXicToXiPiPi, processMcWithKFParticle, "Select candidates with MC matching information using KFParticle", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGHF/TableProducer/derivedDataCreatorD0ToKPi.cxx b/PWGHF/TableProducer/derivedDataCreatorD0ToKPi.cxx index 6c8dd98f3c9..212756317ee 100644 --- a/PWGHF/TableProducer/derivedDataCreatorD0ToKPi.cxx +++ b/PWGHF/TableProducer/derivedDataCreatorD0ToKPi.cxx @@ -105,6 +105,7 @@ struct HfDerivedDataCreatorD0ToKPi { using SelectedCandidatesMcKfMl = soa::Filtered>; using MatchedGenCandidatesMc = soa::Filtered>; using TypeMcCollisions = soa::Join; + using TypeMcGenCollisions = aod::McCollisions; Filter filterSelectCandidates = aod::hf_sel_candidate_d0::isSelD0 >= 1 || aod::hf_sel_candidate_d0::isSelD0bar >= 1; Filter filterMcGenMatching = (nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchGen != 0); @@ -299,7 +300,7 @@ struct HfDerivedDataCreatorD0ToKPi { } } if constexpr (OnlySig) { - if (std::abs(flagMcRec) != o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { + if (!acceptCorrelatedBkgs && (std::abs(flagMcRec) != o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK)) { continue; } } @@ -533,7 +534,7 @@ struct HfDerivedDataCreatorD0ToKPi { } PROCESS_SWITCH(HfDerivedDataCreatorD0ToKPi, processMcWithKFParticleMlAll, "Process MC with KFParticle and ML", false); - void processMcGenOnly(TypeMcCollisions const& mcCollisions, + void processMcGenOnly(TypeMcGenCollisions const& mcCollisions, MatchedGenCandidatesMc const& mcParticles) { rowsCommon.processMcParticles(mcCollisions, mcParticlesPerMcCollision, mcParticles, Mass); diff --git a/PWGHF/TableProducer/derivedDataCreatorDplusToPiKPi.cxx b/PWGHF/TableProducer/derivedDataCreatorDplusToPiKPi.cxx index b1cad301ba6..bf66abd0420 100644 --- a/PWGHF/TableProducer/derivedDataCreatorDplusToPiKPi.cxx +++ b/PWGHF/TableProducer/derivedDataCreatorDplusToPiKPi.cxx @@ -304,7 +304,7 @@ struct HfDerivedDataCreatorDplusToPiKPi { } } if constexpr (OnlySig) { - if (std::abs(flagMcRec) != hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi) { + if (!acceptCorrelatedBkgs && (std::abs(flagMcRec) != hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi)) { continue; } } diff --git a/PWGHF/TableProducer/derivedDataCreatorDsToKKPi.cxx b/PWGHF/TableProducer/derivedDataCreatorDsToKKPi.cxx index 7ed944efb1c..8d7a2fd1b63 100644 --- a/PWGHF/TableProducer/derivedDataCreatorDsToKKPi.cxx +++ b/PWGHF/TableProducer/derivedDataCreatorDsToKKPi.cxx @@ -89,7 +89,7 @@ struct HfDerivedDataCreatorDsToKKPi { SliceCache cache; static constexpr double Mass{o2::constants::physics::MassDS}; - static constexpr int NHypothesesCand{1}; // Number of possible selection hypotheses per candidate. + static constexpr int NHypothesesCand{2}; // Number of possible selection hypotheses per candidate. using CollisionsWCentMult = soa::Join; using CollisionsWMcCentMult = soa::Join; @@ -100,8 +100,9 @@ struct HfDerivedDataCreatorDsToKKPi { using SelectedCandidatesMcMl = soa::Filtered>; using MatchedGenCandidatesMc = soa::Filtered>; using TypeMcCollisions = soa::Join; + using TypeMcGenCollisions = aod::McCollisions; - Filter filterSelectCandidates = (aod::hf_sel_candidate_ds::isSelDsToKKPi & static_cast(BIT(aod::SelectionStep::RecoMl - 1))) != 0; // select candidates which passed all cuts at least up to RecoMl - 1 + Filter filterSelectCandidates = (aod::hf_sel_candidate_ds::isSelDsToKKPi & static_cast(BIT(aod::SelectionStep::RecoMl - 1))) != 0 || (aod::hf_sel_candidate_ds::isSelDsToPiKK & static_cast(BIT(aod::SelectionStep::RecoMl - 1))) != 0; // select candidates which passed all cuts at least up to RecoMl - 1 Filter filterMcGenMatching = (nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DsToPiKK)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchGen != 0); Preslice candidatesPerCollision = aod::hf_cand::collisionId; @@ -266,7 +267,7 @@ struct HfDerivedDataCreatorDsToKKPi { int8_t flagMcRec = 0, origin = 0, swapping = 0, flagDecayChanRec = 0; for (const auto& candidate : candidatesThisColl) { if constexpr (IsMl) { - if (!TESTBIT(candidate.isSelDsToKKPi(), aod::SelectionStep::RecoMl)) { + if (!TESTBIT(candidate.isSelDsToKKPi(), aod::SelectionStep::RecoMl) && !TESTBIT(candidate.isSelDsToPiKK(), aod::SelectionStep::RecoMl)) { continue; } } @@ -287,7 +288,7 @@ struct HfDerivedDataCreatorDsToKKPi { } } if constexpr (OnlySig) { - if (std::abs(flagMcRec) != hf_decay::hf_cand_3prong::DecayChannelMain::DsToPiKK) { + if (!acceptCorrelatedBkgs && (std::abs(flagMcRec) != hf_decay::hf_cand_3prong::DecayChannelMain::DsToPiKK)) { continue; } } @@ -295,11 +296,20 @@ struct HfDerivedDataCreatorDsToKKPi { double const ct = HfHelper::ctDs(candidate); double const y = HfHelper::yDs(candidate); float const massDsToKKPi = HfHelper::invMassDsToKKPi(candidate); - std::vector mlScoresDsToKKPi; + float const massDsToPiKK = HfHelper::invMassDsToPiKK(candidate); + std::vector mlScoresDsToKKPi, mlScoresDsToPiKK; + auto selectionStep = aod::SelectionStep::RecoMl - 1; if constexpr (IsMl) { + selectionStep = aod::SelectionStep::RecoMl; std::copy(candidate.mlProbDsToKKPi().begin(), candidate.mlProbDsToKKPi().end(), std::back_inserter(mlScoresDsToKKPi)); + std::copy(candidate.mlProbDsToPiKK().begin(), candidate.mlProbDsToPiKK().end(), std::back_inserter(mlScoresDsToPiKK)); + } + if (TESTBIT(candidate.isSelDsToKKPi(), selectionStep)) { + fillTablesCandidate(candidate, 0, massDsToKKPi, ct, y, flagMcRec, origin, swapping, flagDecayChanRec, mlScoresDsToKKPi); + } + if (TESTBIT(candidate.isSelDsToPiKK(), selectionStep)) { + fillTablesCandidate(candidate, 1, massDsToPiKK, ct, y, flagMcRec, origin, swapping, flagDecayChanRec, mlScoresDsToPiKK); } - fillTablesCandidate(candidate, 0, massDsToKKPi, ct, y, flagMcRec, origin, swapping, flagDecayChanRec, mlScoresDsToKKPi); } } } @@ -402,7 +412,7 @@ struct HfDerivedDataCreatorDsToKKPi { } PROCESS_SWITCH(HfDerivedDataCreatorDsToKKPi, processMcMlAll, "Process MC with ML", false); - void processMcGenOnly(TypeMcCollisions const& mcCollisions, + void processMcGenOnly(TypeMcGenCollisions const& mcCollisions, MatchedGenCandidatesMc const& mcParticles) { rowsCommon.processMcParticles(mcCollisions, mcParticlesPerMcCollision, mcParticles, Mass); diff --git a/PWGHF/TableProducer/derivedDataCreatorDstarToD0Pi.cxx b/PWGHF/TableProducer/derivedDataCreatorDstarToD0Pi.cxx index 5f20c81e4a5..c73262f6572 100644 --- a/PWGHF/TableProducer/derivedDataCreatorDstarToD0Pi.cxx +++ b/PWGHF/TableProducer/derivedDataCreatorDstarToD0Pi.cxx @@ -279,7 +279,7 @@ struct HfDerivedDataCreatorDstarToD0Pi { } } if constexpr (OnlySig) { - if (std::abs(flagMcRec) != hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPi) { + if (!acceptCorrelatedBkgs && (std::abs(flagMcRec) != hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPi)) { continue; } } diff --git a/PWGHF/TableProducer/derivedDataCreatorLcToPKPi.cxx b/PWGHF/TableProducer/derivedDataCreatorLcToPKPi.cxx index d543b4605f0..c308143988c 100644 --- a/PWGHF/TableProducer/derivedDataCreatorLcToPKPi.cxx +++ b/PWGHF/TableProducer/derivedDataCreatorLcToPKPi.cxx @@ -280,7 +280,7 @@ struct HfDerivedDataCreatorLcToPKPi { } } if constexpr (OnlySig) { - if (std::abs(flagMcRec) != hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi) { + if (!acceptCorrelatedBkgs && (std::abs(flagMcRec) != hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi)) { continue; } } diff --git a/PWGHF/TableProducer/mcPidTof.cxx b/PWGHF/TableProducer/mcPidTof.cxx index 2ab5832ff9e..0462d3a35f6 100644 --- a/PWGHF/TableProducer/mcPidTof.cxx +++ b/PWGHF/TableProducer/mcPidTof.cxx @@ -573,7 +573,7 @@ struct TofEventTime { tableFlags(0); tableEvTime(0.f, 999.f); if (enableTableEvTimeTOFOnly) { - tableEvTimeTOFOnly((uint8_t)0, 0.f, 0.f, -1); + tableEvTimeTOFOnly(static_cast(0), 0.f, 0.f, -1); } continue; } @@ -604,7 +604,7 @@ struct TofEventTime { // Reset the event time eventTime = 0.f; sumOfWeights = 0.f; - weight = 0.f; + // weight = 0.f; // FIXME: unused // Remove the bias on TOF ev. time if constexpr (RemoveTofEvTimeBias) { evTimeMakerTOF.removeBias(trk, nGoodTracksForTOF, t0TOF[0], t0TOF[1], 2); @@ -639,7 +639,7 @@ struct TofEventTime { } tableEvTime(eventTime / sumOfWeights, std::sqrt(1. / sumOfWeights)); if (enableTableEvTimeTOFOnly) { - tableEvTimeTOFOnly((uint8_t)filterForTOFEventTime(trk), t0TOF[0], t0TOF[1], evTimeMakerTOF.mEventTimeMultiplicity); + tableEvTimeTOFOnly(static_cast(filterForTOFEventTime(trk)), t0TOF[0], t0TOF[1], evTimeMakerTOF.mEventTimeMultiplicity); } } } @@ -650,7 +650,7 @@ struct TofEventTime { tableFlags(0); tableEvTime(0.f, 999.f); if (enableTableEvTimeTOFOnly) { - tableEvTimeTOFOnly((uint8_t)0, 0.f, 0.f, -1); + tableEvTimeTOFOnly(static_cast(0), 0.f, 0.f, -1); } continue; } @@ -682,14 +682,14 @@ struct TofEventTime { tableFlags(flags); tableEvTime(et, erret); if (enableTableEvTimeTOFOnly) { - tableEvTimeTOFOnly((uint8_t)filterForTOFEventTime(trk), et, erret, evTimeMakerTOF.mEventTimeMultiplicity); + tableEvTimeTOFOnly(static_cast(filterForTOFEventTime(trk)), et, erret, evTimeMakerTOF.mEventTimeMultiplicity); } } } } else if (mComputeEvTimeWithTOF == 0 && mComputeEvTimeWithFT0 == 1) { for (auto const& t : tracks) { // Loop on collisions if (enableTableEvTimeTOFOnly) { - tableEvTimeTOFOnly((uint8_t)0, 0.f, 0.f, -1); + tableEvTimeTOFOnly(static_cast(0), 0.f, 0.f, -1); } if (!t.has_collision()) { // Track was not assigned, cannot compute event time tableFlags(0); @@ -973,16 +973,16 @@ struct McPidTof { auto* calibList = ccdb->getSpecific(mcRecalib.ccdbPath, timestamp, metadata); std::vector updatedSpecies{}; for (auto const& pidId : mEnabledParticles) { // Loop on enabled particle hypotheses (tiny) - gMcPostCalibMean[pidId] = reinterpret_cast(calibList->FindObject(Form("Mean%s", particleNames[pidId].data()))); - gMcPostCalibSigma[pidId] = reinterpret_cast(calibList->FindObject(Form("Sigma%s", particleNames[pidId].data()))); + gMcPostCalibMean[pidId] = dynamic_cast(calibList->FindObject(Form("Mean%s", particleNames[pidId].data()))); + gMcPostCalibSigma[pidId] = dynamic_cast(calibList->FindObject(Form("Sigma%s", particleNames[pidId].data()))); updatedSpecies.push_back(pidId); } for (auto const& pidId : mEnabledParticlesFull) { // Loop on enabled particle hypotheses (full) if (std::find(updatedSpecies.begin(), updatedSpecies.end(), pidId) != updatedSpecies.end()) { continue; } - gMcPostCalibMean[pidId] = reinterpret_cast(calibList->FindObject(Form("Mean%s", particleNames[pidId].data()))); - gMcPostCalibSigma[pidId] = reinterpret_cast(calibList->FindObject(Form("Sigma%s", particleNames[pidId].data()))); + gMcPostCalibMean[pidId] = dynamic_cast(calibList->FindObject(Form("Mean%s", particleNames[pidId].data()))); + gMcPostCalibSigma[pidId] = dynamic_cast(calibList->FindObject(Form("Sigma%s", particleNames[pidId].data()))); } } diff --git a/PWGHF/TableProducer/trackIndexSkimCreator.cxx b/PWGHF/TableProducer/trackIndexSkimCreator.cxx index c8456183f31..6f00d719a0b 100644 --- a/PWGHF/TableProducer/trackIndexSkimCreator.cxx +++ b/PWGHF/TableProducer/trackIndexSkimCreator.cxx @@ -174,9 +174,9 @@ struct HfTrackIndexSkimCreatorTagSelCollisions { } // set numerical value of the Run 2 trigger class - auto* const triggerAlias = std::find(aliasLabels, aliasLabels + kNaliases, triggerClassName.value.data()); - if (triggerAlias != aliasLabels + kNaliases) { - hfEvSel.triggerClass.value = std::distance(aliasLabels, triggerAlias); + auto* const triggerAlias = std::find(aliasLabels.begin(), aliasLabels.end(), triggerClassName.value.data()); + if (triggerAlias != aliasLabels.end()) { + hfEvSel.triggerClass.value = std::distance(aliasLabels.begin(), triggerAlias); } hfEvSel.init(registry, &zorroSummary); // collision monitoring @@ -1506,6 +1506,8 @@ struct HfTrackIndexSkimCreator { std::array, kN2ProngDecays> binsPt2Prong{}; std::array, kN3ProngDecays> cut3Prong{}; std::array, kN3ProngDecays> binsPt3Prong{}; + LabeledArray cutsDstar{}; + std::vector binsPtDstar{}; // ML response o2::analysis::MlResponse hfMlResponse2Prongs; // only D0 @@ -1584,6 +1586,9 @@ struct HfTrackIndexSkimCreator { // cuts for 3-prong decays retrieved by json. the order must be then one in hf_cand_3prong::DecayType cut3Prong = {config.cutsDplusToPiKPi, config.cutsLcToPKPi, config.cutsDsToKKPi, config.cutsXicToPKPi, config.cutsCdToDeKPi, config.cutsCtToTrKPi, config.cutsChToHeKPi, config.cutsCaToAlKPi}; binsPt3Prong = {config.binsPtDplusToPiKPi, config.binsPtLcToPKPi, config.binsPtDsToKKPi, config.binsPtXicToPKPi, config.binsPtCdToDeKPi, config.binsPtCtToTrKPi, config.binsPtChToHeKPi, config.binsPtCaToAlKPi}; + // D* cuts retrieved by json + cutsDstar = config.cutsDstarToD0Pi; + binsPtDstar = config.binsPtDstarToD0Pi; df2.setPropagateToPCA(config.propagateToPCA); df2.setMaxR(config.maxR); @@ -1724,6 +1729,15 @@ struct HfTrackIndexSkimCreator { } } + /// One track of the collision under study, propagated to that collision's primary vertex. + /// \tparam TTrackIndex is the iterator type of the track-collision associations + template + struct HfPropagatedSoftPion { + TTrackIndex trackIndex; ///< track-collision association, carries isSelProng and isIdentifiedPid + int64_t trackGlobalIndex{-1}; ///< track global index + std::array pVec{}; ///< momentum at the PCA to the primary vertex + }; + /// One track of the collision under study, propagated to that collision's primary vertex. /// \tparam TTrackIndex is the iterator type of the track-collision associations /// \tparam TTrack is the iterator type of the track table @@ -1741,21 +1755,44 @@ struct HfTrackIndexSkimCreator { /// \param collision is the collision under study /// \param trackIndices are the track associations of this collision /// \param prongs is the cache to be filled, in the order of trackIndices - template + template void fillPropagatedProngCache(TCollision const& collision, TTrackIndices const& trackIndices, std::vector& prongs) { prongs.clear(); const auto thisCollId = collision.globalIndex(); for (auto trackIndex = trackIndices.begin(); trackIndex != trackIndices.end(); ++trackIndex) { const auto track = trackIndex.template track_as(); - auto& prong = prongs.emplace_back(TProng{trackIndex, track, getTrackParCov(track), track.pVector(), {track.dcaXY(), track.dcaZ()}}); - if (thisCollId != track.collisionId()) { // this is not the "default" collision for this track, we have to re-propagate it - o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, prong.trackParVar, 2.f, noMatCorr, &prong.dcaInfo); - getPxPyPz(prong.trackParVar, prong.pVec); + if constexpr (IsSoftPion) { + std::array pVec = track.pVector(); + if (thisCollId != track.collisionId()) { // this is not the "default" collision for this track, we have to re-propagate it + auto trackParCov = getTrackParCov(track); + std::array dcaInfo{}; + o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, trackParCov, 2.f, noMatCorr, &dcaInfo); + getPxPyPz(trackParCov, pVec); + } + prongs.push_back(TProng{trackIndex, track.globalIndex(), pVec}); + } else { + auto& prong = prongs.emplace_back(TProng{trackIndex, track, getTrackParCov(track), track.pVector(), {track.dcaXY(), track.dcaZ()}}); + if (thisCollId != track.collisionId()) { // this is not the "default" collision for this track, we have to re-propagate it + o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, prong.trackParVar, 2.f, noMatCorr, &prong.dcaInfo); + getPxPyPz(prong.trackParVar, prong.pVec); + } } } } + /// Overloaded function of fillPropagatedProngCache to handle optional track indices. + /// \param collision is the collision under study + /// \param trackIndices are the track associations of this collision + /// \param prongs is the cache to be filled, in the order of trackIndices + template + void fillPropagatedProngCache(TCollision const& collision, std::optional const& trackIndices, std::vector& prongs) + { + if (trackIndices.has_value()) { + fillPropagatedProngCache(collision, *trackIndices, prongs); + } + } + /// Method to perform selections for 2-prong candidates before vertex reconstruction /// \param pVecTrack0 is the momentum array of the first daughter track /// \param pVecTrack1 is the momentum array of the second daughter track @@ -1827,10 +1864,10 @@ struct HfTrackIndexSkimCreator { // additional check for D0 to be used in D* finding if (iDecay2P == hf_cand_2prong::DecayType::D0ToPiK && config.doDstar && TESTBIT(isSelected, iDecay2P)) { - const auto binPtDstar = findBin(config.binsPtDstarToD0Pi, pt * 1.2); // assuming the D* pT about 20% higher than the one of the D0 to be safe + const auto binPtDstar = findBin(&binsPtDstar, pt * 1.2); // assuming the D* pT about 20% higher than the one of the D0 to be safe if (binPtDstar >= 0) { whichHypo[kN2ProngDecays] = whichHypo[hf_cand_2prong::DecayType::D0ToPiK]; - const double deltaMass = config.cutsDstarToD0Pi->get(binPtDstar, 1u); + const double deltaMass = cutsDstar.get(binPtDstar, 1u); if (TESTBIT(whichHypo[iDecay2P], 0) && (massHypos[0] > (MassD0 + deltaMass) * (MassD0 + deltaMass) || massHypos[0] < (MassD0 - deltaMass) * (MassD0 - deltaMass))) { CLRBIT(whichHypo[kN2ProngDecays], 0); @@ -2156,18 +2193,18 @@ struct HfTrackIndexSkimCreator { /// \param pVecTrack1 is the momentum array of the second daughter track (opposite charge) /// \param pVecTrack2 is the momentum array of the third daughter track (same charge) /// \param cutStatus is the cut status (filled only in case of debug) + /// \param invMassD0 is the invariant mass of the D0 candidate /// \param deltaMass is the M(Kpipi) - M(Kpi) value to be filled in the control histogram /// \return a bitmap with selection outcome template - uint8_t applySelectionDstar(T1 const& pVecTrack0, T1 const& pVecTrack1, T1 const& pVecTrack2, uint8_t& cutStatus, T2& deltaMass) + uint8_t applySelectionDstar(T1 const& pVecTrack0, T1 const& pVecTrack1, T1 const& pVecTrack2, uint8_t& cutStatus, T2 invMassD0, T2& deltaMass) { uint8_t isSelected{1}; const std::array arrMom{pVecTrack0, pVecTrack1, pVecTrack2}; - const std::array arrMomD0{pVecTrack0, pVecTrack1}; const auto pt = RecoDecay::pt(pVecTrack0, pVecTrack1, pVecTrack2) + config.ptTolerance; // add tolerance because of no reco decay vertex // pT - const auto binPt = findBin(config.binsPtDstarToD0Pi, pt); + const auto binPt = findBin(&binsPtDstar, pt); // return immediately if it is outside the defined pT bins if (binPt == -1) { isSelected = 0; @@ -2178,8 +2215,7 @@ struct HfTrackIndexSkimCreator { } // D0 mass - const double deltaMassD0 = config.cutsDstarToD0Pi->get(binPt, 1u); // 1u == deltaMassD0Index - const double invMassD0 = RecoDecay::m(arrMomD0, std::array{MassPiPlus, MassKPlus}); + const double deltaMassD0 = cutsDstar.get(binPt, 1u); // 1u == deltaMassD0Index if (std::abs(invMassD0 - MassD0) > deltaMassD0) { isSelected = 0; if (config.debug) { @@ -2189,7 +2225,7 @@ struct HfTrackIndexSkimCreator { } // D*+ mass - const double maxDeltaMass = config.cutsDstarToD0Pi->get(binPt, 0u); // 0u == deltaMassIndex + const double maxDeltaMass = cutsDstar.get(binPt, 0u); // 0u == deltaMassIndex const double invMassDstar = RecoDecay::m(arrMom, std::array{MassPiPlus, MassKPlus, MassPiPlus}); deltaMass = invMassDstar - invMassD0; if (deltaMass > maxDeltaMass) { @@ -2356,9 +2392,17 @@ struct HfTrackIndexSkimCreator { using PropagatedProng = HfPropagatedProng; std::vector prongsPos{}; std::vector prongsNeg{}; + using SoftPionIterator = decltype(positiveSoftPions->sliceByCached(aod::track::collisionId, 0, cache).begin()); + using PropagatedSoftPi = HfPropagatedSoftPion; + std::vector softPionsPos{}; + std::vector softPionsNeg{}; + bool softPiPosCacheFilled{false}, softPiNegCacheFilled{false}; for (const auto& collision : collisions) { + softPiPosCacheFilled = false; + softPiNegCacheFilled = false; + /// retrieve PV contributors for the current collision std::vector vecPvContributorGlobId{}; std::vector vecPvContributorTrackParCov{}; @@ -2438,8 +2482,8 @@ struct HfTrackIndexSkimCreator { int lastFilledD0 = -1; // index to be filled in table for D* mesons // propagate each track to this collision's PV once, instead of once per pair or triplet - fillPropagatedProngCache(collision, groupedTrackIndicesPos1, prongsPos); - fillPropagatedProngCache(collision, groupedTrackIndicesNeg1, prongsNeg); + fillPropagatedProngCache(collision, groupedTrackIndicesPos1, prongsPos); + fillPropagatedProngCache(collision, groupedTrackIndicesNeg1, prongsNeg); for (std::size_t iPos1 = 0; iPos1 < prongsPos.size(); ++iPos1) { const auto& trackIndexPos1 = prongsPos[iPos1].trackIndex; @@ -3216,32 +3260,35 @@ struct HfTrackIndexSkimCreator { } } - if (config.doDstar && TESTBIT(isSelected2ProngCand, hf_cand_2prong::DecayType::D0ToPiK) && (pt2Prong + config.ptTolerance) * 1.2 > config.binsPtDstarToD0Pi->at(0) && whichHypo2Prong[kN2ProngDecays] != 0) { // o2-linter: disable="magic-number" (see comment below) - // if D* enabled and pt of the D0 is larger than the minimum of the D* one within 20% (D* and D0 momenta are very similar, always within 20% according to PYTHIA8) + if (config.doDstar && TESTBIT(isSelected2ProngCand, hf_cand_2prong::DecayType::D0ToPiK) && (pt2Prong + config.ptTolerance) * 1.2 > binsPtDstar[0] && whichHypo2Prong[kN2ProngDecays] != 0) { // o2-linter: disable="magic-number" (see comment below) + // if D* enabled and pt of the D0 is larger than the minimum of the D* one within 20% (D* and D0 momenta are very similar, always within 20% according to PYTHIA8) // second loop over positive tracks if (TESTBIT(whichHypo2Prong[kN2ProngDecays], 0) && (!config.applyKaonPidIn3Prongs || TESTBIT(trackIndexNeg1.isIdentifiedPid(), ChannelKaonPid))) { // only for D0 candidates; moreover if kaon PID enabled, apply to the negative track - if (!groupedTrackIndicesSoftPionsPos) { + + // Compute D0 invariant mass for all D* candidates + const std::array arrMomD0{pVecTrackPos1, pVecTrackNeg1}; + const float invMassD0 = RecoDecay::m(arrMomD0, std::array{MassPiPlus, MassKPlus}); + + if (!softPiPosCacheFilled) { groupedTrackIndicesSoftPionsPos.emplace(positiveSoftPions->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache)); + fillPropagatedProngCache(collision, groupedTrackIndicesSoftPionsPos, softPionsPos); + softPiPosCacheFilled = true; } - for (auto trackIndexPos2 = groupedTrackIndicesSoftPionsPos->begin(); trackIndexPos2 != groupedTrackIndicesSoftPionsPos->end(); ++trackIndexPos2) { - if (trackIndexPos2 == trackIndexPos1) { + + for (std::size_t iPos2 = 0; iPos2 < softPionsPos.size(); ++iPos2) { + if (softPionsPos[iPos2].trackIndex == trackIndexPos1) { continue; } - auto trackPos2 = trackIndexPos2.template track_as(); - std::array pVecTrackPos2{trackPos2.pVector()}; - if (thisCollId != trackPos2.collisionId()) { // this is not the "default" collision for this track, we have to re-propagate it - auto trackParVarPos2 = getTrackParCov(trackPos2); - std::array dcaInfoPos2{trackPos2.dcaXY(), trackPos2.dcaZ()}; - o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, trackParVarPos2, 2.f, noMatCorr, &dcaInfoPos2); - getPxPyPz(trackParVarPos2, pVecTrackPos2); - } + + const auto& trackGlobalIndexPos2 = softPionsPos[iPos2].trackGlobalIndex; + const auto& pVecTrackPos2 = softPionsPos[iPos2].pVec; uint8_t isSelectedDstar{0}; uint8_t cutStatus{BIT(kNCutsDstar) - 1}; float deltaMass{-1.}; - isSelectedDstar = applySelectionDstar(pVecTrackPos1, pVecTrackNeg1, pVecTrackPos2, cutStatus, deltaMass); // we do not compute the D* decay vertex at this stage because we are not interested in applying topological selections + isSelectedDstar = applySelectionDstar(pVecTrackPos1, pVecTrackNeg1, pVecTrackPos2, cutStatus, invMassD0, deltaMass); // we do not compute the D* decay vertex at this stage because we are not interested in applying topological selections if (isSelectedDstar) { - rowTrackIndexDstar(thisCollId, trackPos2.globalIndex(), lastFilledD0); + rowTrackIndexDstar(thisCollId, trackGlobalIndexPos2, lastFilledD0); if (config.fillHistograms) { registry.fill(HIST("hMassDstarToD0Pi"), deltaMass); } @@ -3259,28 +3306,31 @@ struct HfTrackIndexSkimCreator { // second loop over negative tracks if (TESTBIT(whichHypo2Prong[kN2ProngDecays], 1) && (!config.applyKaonPidIn3Prongs || TESTBIT(trackIndexPos1.isIdentifiedPid(), ChannelKaonPid))) { // only for D0bar candidates; moreover if kaon PID enabled, apply to the positive track - if (!groupedTrackIndicesSoftPionsNeg) { + + // Compute D0 invariant mass for all D* candidates + const std::array arrMomD0{pVecTrackNeg1, pVecTrackPos1}; + const float invMassD0 = RecoDecay::m(arrMomD0, std::array{MassPiPlus, MassKPlus}); + + if (!softPiNegCacheFilled) { groupedTrackIndicesSoftPionsNeg.emplace(negativeSoftPions->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache)); + fillPropagatedProngCache(collision, groupedTrackIndicesSoftPionsNeg, softPionsNeg); + softPiNegCacheFilled = true; } - for (auto trackIndexNeg2 = groupedTrackIndicesSoftPionsNeg->begin(); trackIndexNeg2 != groupedTrackIndicesSoftPionsNeg->end(); ++trackIndexNeg2) { - if (trackIndexNeg1 == trackIndexNeg2) { + + for (std::size_t iNeg2 = 0; iNeg2 < softPionsNeg.size(); ++iNeg2) { + if (trackIndexNeg1 == softPionsNeg[iNeg2].trackIndex) { continue; } - auto trackNeg2 = trackIndexNeg2.template track_as(); - std::array pVecTrackNeg2{trackNeg2.pVector()}; - if (thisCollId != trackNeg2.collisionId()) { // this is not the "default" collision for this track, we have to re-propagate it - auto trackParVarNeg2 = getTrackParCov(trackNeg2); - std::array dcaInfoNeg2{trackNeg2.dcaXY(), trackNeg2.dcaZ()}; - o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, trackParVarNeg2, 2.f, noMatCorr, &dcaInfoNeg2); - getPxPyPz(trackParVarNeg2, pVecTrackNeg2); - } + + const auto& trackGlobalIndexNeg2 = softPionsNeg[iNeg2].trackGlobalIndex; + const auto& pVecTrackNeg2 = softPionsNeg[iNeg2].pVec; uint8_t isSelectedDstar{0}; uint8_t cutStatus{BIT(kNCutsDstar) - 1}; float deltaMass{-1.}; - isSelectedDstar = applySelectionDstar(pVecTrackNeg1, pVecTrackPos1, pVecTrackNeg2, cutStatus, deltaMass); // we do not compute the D* decay vertex at this stage because we are not interested in applying topological selections + isSelectedDstar = applySelectionDstar(pVecTrackNeg1, pVecTrackPos1, pVecTrackNeg2, cutStatus, invMassD0, deltaMass); // we do not compute the D* decay vertex at this stage because we are not interested in applying topological selections if (isSelectedDstar) { - rowTrackIndexDstar(thisCollId, trackNeg2.globalIndex(), lastFilledD0); + rowTrackIndexDstar(thisCollId, trackGlobalIndexNeg2, lastFilledD0); if (config.fillHistograms) { registry.fill(HIST("hMassDstarToD0Pi"), deltaMass); } diff --git a/PWGHF/TableProducer/treeCreatorD0ToKPi.cxx b/PWGHF/TableProducer/treeCreatorD0ToKPi.cxx index 4200d499db8..03f97287433 100644 --- a/PWGHF/TableProducer/treeCreatorD0ToKPi.cxx +++ b/PWGHF/TableProducer/treeCreatorD0ToKPi.cxx @@ -228,8 +228,8 @@ DECLARE_SOA_TABLE(HfCandD0FullPs, "AOD", "HFCANDD0FULLP", DECLARE_SOA_TABLE(HfCandD0Mls, "AOD", "HFCANDD0ML", ml::BdtOutputBkg, - ml::BdtOutputNonPrompt, - ml::BdtOutputPrompt); + ml::BdtOutputPrompt, + ml::BdtOutputNonPrompt); } // namespace o2::aod diff --git a/PWGHF/TableProducer/treeCreatorOmegac0ToOmegaPiQa.cxx b/PWGHF/TableProducer/treeCreatorOmegac0ToOmegaPiQa.cxx new file mode 100644 index 00000000000..55ba2da9efc --- /dev/null +++ b/PWGHF/TableProducer/treeCreatorOmegac0ToOmegaPiQa.cxx @@ -0,0 +1,663 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file treeCreatorOmegac0ToOmegaPiQa.cxx +/// \brief Writer of the omegac0 to Omega Pi candidates in the form of flat tables to be stored in TTrees. +/// In this file are defined and filled the output tables +/// +/// \author Fabio Catalano , University of Houston +/// \author Maria Fernanda Torres Cabrera , University of Houston + +#include "PWGHF/Core/CentralityEstimation.h" +#include "PWGHF/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/DataModel/CandidateSelectionTables.h" +#include "PWGHF/DataModel/TrackIndexSkimmingTables.h" +#include "PWGLF/DataModel/mcCentrality.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +// SV Reco method +enum { + DCAFITTER = 0, + KFPARTICLE +}; + +namespace o2::aod +{ +namespace full +{ +// collision info +DECLARE_SOA_COLUMN(IsEventSel8, isEventSel8, bool); +DECLARE_SOA_COLUMN(IsEventSelZ, isEventSelZ, bool); +DECLARE_SOA_COLUMN(Centrality, centrality, float); +// from creator +DECLARE_SOA_COLUMN(XPv, xPv, float); +DECLARE_SOA_COLUMN(YPv, yPv, float); +DECLARE_SOA_COLUMN(ZPv, zPv, float); +DECLARE_SOA_COLUMN(XDecayVtxCharmBaryon, xDecayVtxCharmBaryon, float); +DECLARE_SOA_COLUMN(YDecayVtxCharmBaryon, yDecayVtxCharmBaryon, float); +DECLARE_SOA_COLUMN(ZDecayVtxCharmBaryon, zDecayVtxCharmBaryon, float); +DECLARE_SOA_COLUMN(XDecayVtxCascade, xDecayVtxCascade, float); +DECLARE_SOA_COLUMN(YDecayVtxCascade, yDecayVtxCascade, float); +DECLARE_SOA_COLUMN(ZDecayVtxCascade, zDecayVtxCascade, float); +DECLARE_SOA_COLUMN(XDecayVtxV0, xDecayVtxV0, float); +DECLARE_SOA_COLUMN(YDecayVtxV0, yDecayVtxV0, float); +DECLARE_SOA_COLUMN(ZDecayVtxV0, zDecayVtxV0, float); +DECLARE_SOA_COLUMN(SignDecay, signDecay, int8_t); // sign of ka <- omega +DECLARE_SOA_COLUMN(PxCharmBaryon, pxCharmBaryon, float); +DECLARE_SOA_COLUMN(PyCharmBaryon, pyCharmBaryon, float); +DECLARE_SOA_COLUMN(PzCharmBaryon, pzCharmBaryon, float); +DECLARE_SOA_COLUMN(PxCasc, pxCasc, float); +DECLARE_SOA_COLUMN(PyCasc, pyCasc, float); +DECLARE_SOA_COLUMN(PzCasc, pzCasc, float); +DECLARE_SOA_COLUMN(PxPiFromCharmBaryon, pxPiFromCharmBaryon, float); +DECLARE_SOA_COLUMN(PyPiFromCharmBaryon, pyPiFromCharmBaryon, float); +DECLARE_SOA_COLUMN(PzPiFromCharmBaryon, pzPiFromCharmBaryon, float); +DECLARE_SOA_COLUMN(PxLambda, pxLambda, float); +DECLARE_SOA_COLUMN(PyLambda, pyLambda, float); +DECLARE_SOA_COLUMN(PzLambda, pzLambda, float); +DECLARE_SOA_COLUMN(PtCharmBaryon, ptCharmBaryon, float); +DECLARE_SOA_COLUMN(PtPiFromCharmBaryon, ptPiFromCharmBaryon, float); +DECLARE_SOA_COLUMN(PxKaFromCasc, pxKaFromCasc, float); +DECLARE_SOA_COLUMN(PyKaFromCasc, pyKaFromCasc, float); +DECLARE_SOA_COLUMN(PzKaFromCasc, pzKaFromCasc, float); +DECLARE_SOA_COLUMN(PxPosV0Dau, pxPosV0Dau, float); +DECLARE_SOA_COLUMN(PyPosV0Dau, pyPosV0Dau, float); +DECLARE_SOA_COLUMN(PzPosV0Dau, pzPosV0Dau, float); +DECLARE_SOA_COLUMN(PxNegV0Dau, pxNegV0Dau, float); +DECLARE_SOA_COLUMN(PyNegV0Dau, pyNegV0Dau, float); +DECLARE_SOA_COLUMN(PzNegV0Dau, pzNegV0Dau, float); +DECLARE_SOA_COLUMN(ImpactParCascXY, impactParCascXY, float); +DECLARE_SOA_COLUMN(ImpactParPiFromCharmBaryonXY, impactParPiFromCharmBaryonXY, float); +DECLARE_SOA_COLUMN(ImpactParCascZ, impactParCascZ, float); +DECLARE_SOA_COLUMN(ImpactParPiFromCharmBaryonZ, impactParPiFromCharmBaryonZ, float); +DECLARE_SOA_COLUMN(ErrImpactParCascXY, errImpactParCascXY, float); +DECLARE_SOA_COLUMN(ErrImpactParPiFromCharmBaryonXY, errImpactParPiFromCharmBaryonXY, float); +DECLARE_SOA_COLUMN(InvMassLambda, invMassLambda, float); +DECLARE_SOA_COLUMN(InvMassCascade, invMassCascade, float); +DECLARE_SOA_COLUMN(InvMassCharmBaryon, invMassCharmBaryon, float); +DECLARE_SOA_COLUMN(CosPAV0, cosPAV0, float); +DECLARE_SOA_COLUMN(CosPACharmBaryon, cosPACharmBaryon, float); +DECLARE_SOA_COLUMN(CosPACasc, cosPACasc, float); +DECLARE_SOA_COLUMN(CosPAXYV0, cosPAXYV0, float); +DECLARE_SOA_COLUMN(CosPAXYCharmBaryon, cosPAXYCharmBaryon, float); +DECLARE_SOA_COLUMN(CosPAXYCasc, cosPAXYCasc, float); +DECLARE_SOA_COLUMN(CTauCascade, cTauCascade, float); +DECLARE_SOA_COLUMN(CTauV0, cTauV0, float); +DECLARE_SOA_COLUMN(EtaV0PosDau, etaV0PosDau, float); +DECLARE_SOA_COLUMN(EtaV0NegDau, etaV0NegDau, float); +DECLARE_SOA_COLUMN(EtaKaFromCasc, etaKaFromCasc, float); +DECLARE_SOA_COLUMN(EtaPiFromCharmBaryon, etaPiFromCharmBaryon, float); +DECLARE_SOA_COLUMN(EtaCharmBaryon, etaCharmBaryon, float); +DECLARE_SOA_COLUMN(EtaCascade, etaCascade, float); +DECLARE_SOA_COLUMN(EtaV0, etaV0, float); +DECLARE_SOA_COLUMN(DcaXYToPvV0Dau0, dcaXYToPvV0Dau0, float); +DECLARE_SOA_COLUMN(DcaXYToPvV0Dau1, dcaXYToPvV0Dau1, float); +DECLARE_SOA_COLUMN(DcaXYToPvCascDau, dcaXYToPvCascDau, float); +DECLARE_SOA_COLUMN(DcaZToPvV0Dau0, dcaZToPvV0Dau0, float); +DECLARE_SOA_COLUMN(DcaZToPvV0Dau1, dcaZToPvV0Dau1, float); +DECLARE_SOA_COLUMN(DcaZToPvCascDau, dcaZToPvCascDau, float); +DECLARE_SOA_COLUMN(DcaCascDau, dcaCascDau, float); +DECLARE_SOA_COLUMN(DcaV0Dau, dcaV0Dau, float); +DECLARE_SOA_COLUMN(DcaCharmBaryonDau, dcaCharmBaryonDau, float); +DECLARE_SOA_COLUMN(DecLenCharmBaryon, decLenCharmBaryon, float); +DECLARE_SOA_COLUMN(DecLenCascade, decLenCascade, float); +DECLARE_SOA_COLUMN(DecLenV0, decLenV0, float); +DECLARE_SOA_COLUMN(ErrorDecayLengthCharmBaryon, errorDecayLengthCharmBaryon, float); +DECLARE_SOA_COLUMN(NormImpParCascade, normImpParCascade, double); +DECLARE_SOA_COLUMN(NormImpParPiFromCharmBar, normImpParPiFromCharmBar, double); +DECLARE_SOA_COLUMN(NormDecayLenCharmBar, normDecayLenCharmBar, double); +DECLARE_SOA_COLUMN(IsPionGlbTrkWoDca, isPionGlbTrkWoDca, bool); +DECLARE_SOA_COLUMN(PionItsNCls, pionItsNCls, uint8_t); +DECLARE_SOA_COLUMN(NTpcRowsPion, nTpcRowsPion, int16_t); +DECLARE_SOA_COLUMN(NTpcRowsKaFromCasc, nTpcRowsKaFromCasc, int16_t); +DECLARE_SOA_COLUMN(NTpcRowsPosV0Dau, nTpcRowsPosV0Dau, int16_t); +DECLARE_SOA_COLUMN(NTpcRowsNegV0Dau, nTpcRowsNegV0Dau, int16_t); +// from creator KF +DECLARE_SOA_COLUMN(KfDcaXYPiFromOmegac, kfDcaXYPiFromOmegac, float); +DECLARE_SOA_COLUMN(KfDcaXYCascToPv, kfDcaXYCascToPv, float); +DECLARE_SOA_COLUMN(Chi2GeoV0, chi2GeoV0, float); +DECLARE_SOA_COLUMN(Chi2GeoCasc, chi2GeoCasc, float); +DECLARE_SOA_COLUMN(Chi2GeoOmegac, chi2GeoOmegac, float); +DECLARE_SOA_COLUMN(Chi2MassV0, chi2MassV0, float); +DECLARE_SOA_COLUMN(Chi2MassCasc, chi2MassCasc, float); +DECLARE_SOA_COLUMN(V0ldl, v0ldl, float); +DECLARE_SOA_COLUMN(Cascldl, cascldl, float); +DECLARE_SOA_COLUMN(Omegacldl, omegacldl, float); +DECLARE_SOA_COLUMN(Chi2TopoV0ToPv, chi2TopoV0ToPv, float); +DECLARE_SOA_COLUMN(Chi2TopoCascToPv, chi2TopoCascToPv, float); +DECLARE_SOA_COLUMN(Chi2TopoPiFromOmegacToPv, chi2TopoPiFromOmegacToPv, float); +DECLARE_SOA_COLUMN(Chi2TopoOmegacToPv, chi2TopoOmegacToPv, float); +DECLARE_SOA_COLUMN(Chi2TopoV0ToCasc, chi2TopoV0ToCasc, float); +DECLARE_SOA_COLUMN(Chi2TopoCascToOmegac, chi2TopoCascToOmegac, float); +DECLARE_SOA_COLUMN(DecayLenXYLambda, decayLenXYLambda, float); +DECLARE_SOA_COLUMN(DecayLenXYCasc, decayLenXYCasc, float); +DECLARE_SOA_COLUMN(DecayLenXYOmegac, decayLenXYOmegac, float); +DECLARE_SOA_COLUMN(CosPaV0ToCasc, cosPaV0ToCasc, float); +DECLARE_SOA_COLUMN(CosPaV0ToPv, cosPaV0ToPv, float); +DECLARE_SOA_COLUMN(CosPaCascToOmegac, cosPaCascToOmegac, float); +DECLARE_SOA_COLUMN(CosPaCascToPv, cosPaCascToPv, float); +DECLARE_SOA_COLUMN(CosPaOmegacToPv, cosPaOmegacToPv, float); +DECLARE_SOA_COLUMN(KfRapOmegac, kfRapOmegac, float); +DECLARE_SOA_COLUMN(KfptPiFromOmegac, kfptPiFromOmegac, float); +DECLARE_SOA_COLUMN(KfptOmegac, kfptOmegac, float); +DECLARE_SOA_COLUMN(CosThetaStarPiFromOmegac, cosThetaStarPiFromOmegac, float); +DECLARE_SOA_COLUMN(CtOmegac, ctOmegac, float); +DECLARE_SOA_COLUMN(EtaOmegac, etaOmegac, float); +DECLARE_SOA_COLUMN(V0Ndf, v0Ndf, float); +DECLARE_SOA_COLUMN(CascNdf, cascNdf, float); +DECLARE_SOA_COLUMN(OmegacNdf, omegacNdf, float); +DECLARE_SOA_COLUMN(MassV0Ndf, massV0Ndf, float); +DECLARE_SOA_COLUMN(MassCascNdf, massCascNdf, float); +DECLARE_SOA_COLUMN(V0Chi2OverNdf, v0Chi2OverNdf, float); +DECLARE_SOA_COLUMN(CascChi2OverNdf, cascChi2OverNdf, float); +DECLARE_SOA_COLUMN(OmegacChi2OverNdf, omegacChi2OverNdf, float); +DECLARE_SOA_COLUMN(MassV0Chi2OverNdf, massV0Chi2OverNdf, float); +DECLARE_SOA_COLUMN(MassCascChi2OverNdf, massCascChi2OverNdf, float); +DECLARE_SOA_COLUMN(CascRejectInvmass, cascRejectInvmass, float); +// from creator - MC +DECLARE_SOA_COLUMN(FlagMcMatchRec, flagMcMatchRec, int8_t); // reconstruction level +DECLARE_SOA_COLUMN(OriginMcRec, originMcRec, int8_t); +DECLARE_SOA_COLUMN(CollisionMatched, collisionMatched, bool); +// from selector +DECLARE_SOA_COLUMN(StatusPidLambda, statusPidLambda, bool); +DECLARE_SOA_COLUMN(StatusPidCascade, statusPidCascade, bool); +DECLARE_SOA_COLUMN(StatusPidCharmBaryon, statusPidCharmBaryon, bool); +DECLARE_SOA_COLUMN(StatusInvMassLambda, statusInvMassLambda, bool); +DECLARE_SOA_COLUMN(StatusInvMassCascade, statusInvMassCascade, bool); +DECLARE_SOA_COLUMN(StatusInvMassCharmBaryon, statusInvMassCharmBaryon, bool); +DECLARE_SOA_COLUMN(ResultSelections, resultSelections, bool); +DECLARE_SOA_COLUMN(PidTpcInfoStored, pidTpcInfoStored, int); +DECLARE_SOA_COLUMN(PidTofInfoStored, pidTofInfoStored, int); +DECLARE_SOA_COLUMN(TpcNSigmaPiFromCharmBaryon, tpcNSigmaPiFromCharmBaryon, float); +DECLARE_SOA_COLUMN(TpcNSigmaKaFromCasc, tpcNSigmaKaFromCasc, float); +DECLARE_SOA_COLUMN(TpcNSigmaPiFromLambda, tpcNSigmaPiFromLambda, float); +DECLARE_SOA_COLUMN(TpcNSigmaPrFromLambda, tpcNSigmaPrFromLambda, float); +DECLARE_SOA_COLUMN(TofNSigmaPiFromCharmBaryon, tofNSigmaPiFromCharmBaryon, float); +DECLARE_SOA_COLUMN(TofNSigmaKaFromCasc, tofNSigmaKaFromCasc, float); +DECLARE_SOA_COLUMN(TofNSigmaPiFromLambda, tofNSigmaPiFromLambda, float); +DECLARE_SOA_COLUMN(TofNSigmaPrFromLambda, tofNSigmaPrFromLambda, float); +} // namespace full + +DECLARE_SOA_TABLE(HfToOmegaPiEvs, "AOD", "HFTOOMEPIEV", + full::IsEventSel8, full::IsEventSelZ); + +DECLARE_SOA_TABLE(HfOmegac0ToOmegaPi, "AOD", "HFTOOMEGAPI", + full::XPv, full::YPv, full::ZPv, full::Centrality, collision::NumContrib, collision::Chi2, + full::XDecayVtxCharmBaryon, full::YDecayVtxCharmBaryon, full::ZDecayVtxCharmBaryon, + full::XDecayVtxCascade, full::YDecayVtxCascade, full::ZDecayVtxCascade, + full::XDecayVtxV0, full::YDecayVtxV0, full::ZDecayVtxV0, + full::SignDecay, + full::PtCharmBaryon, full::PtPiFromCharmBaryon, + full::PxCharmBaryon, full::PyCharmBaryon, full::PzCharmBaryon, + full::PxPiFromCharmBaryon, full::PyPiFromCharmBaryon, full::PzPiFromCharmBaryon, + full::PxKaFromCasc, full::PyKaFromCasc, full::PzKaFromCasc, + full::PxPosV0Dau, full::PyPosV0Dau, full::PzPosV0Dau, + full::PxNegV0Dau, full::PyNegV0Dau, full::PzNegV0Dau, + full::ImpactParCascXY, full::ImpactParPiFromCharmBaryonXY, + full::ErrImpactParCascXY, full::ErrImpactParPiFromCharmBaryonXY, + full::InvMassLambda, full::InvMassCascade, full::InvMassCharmBaryon, + full::CosPAV0, full::CosPACharmBaryon, full::CosPACasc, + full::EtaV0PosDau, full::EtaV0NegDau, full::EtaKaFromCasc, full::EtaPiFromCharmBaryon, full::EtaCharmBaryon, + full::DcaXYToPvV0Dau0, full::DcaXYToPvV0Dau1, full::DcaXYToPvCascDau, + full::DcaCascDau, full::DcaV0Dau, full::DcaCharmBaryonDau, + full::DecLenCharmBaryon, + full::ErrorDecayLengthCharmBaryon, full::NormImpParCascade, full::NormImpParPiFromCharmBar, + full::NormDecayLenCharmBar, + full::IsPionGlbTrkWoDca, full::PionItsNCls, + full::NTpcRowsPion, full::NTpcRowsKaFromCasc, full::NTpcRowsPosV0Dau, full::NTpcRowsNegV0Dau, + full::StatusPidLambda, full::StatusPidCascade, full::StatusPidCharmBaryon, + full::StatusInvMassLambda, full::StatusInvMassCascade, full::StatusInvMassCharmBaryon, full::ResultSelections, + full::PidTpcInfoStored, full::PidTofInfoStored, + full::TpcNSigmaPiFromCharmBaryon, full::TpcNSigmaKaFromCasc, full::TpcNSigmaPiFromLambda, full::TpcNSigmaPrFromLambda, + full::TofNSigmaPiFromCharmBaryon, full::TofNSigmaKaFromCasc, full::TofNSigmaPiFromLambda, full::TofNSigmaPrFromLambda, + full::FlagMcMatchRec, full::OriginMcRec, full::CollisionMatched); + +DECLARE_SOA_TABLE(HfKfOmegac, "AOD", "HFKFOMEGAC", + full::Centrality, + collision::NumContrib, + full::TpcNSigmaPiFromCharmBaryon, full::TofNSigmaPiFromCharmBaryon, full::TpcNSigmaKaFromCasc, full::TofNSigmaKaFromCasc, + full::TpcNSigmaPiFromLambda, full::TofNSigmaPiFromLambda, full::TpcNSigmaPrFromLambda, full::TofNSigmaPrFromLambda, + full::KfDcaXYPiFromOmegac, full::DcaCascDau, full::DcaV0Dau, full::DcaCharmBaryonDau, full::KfDcaXYCascToPv, + full::DcaXYToPvV0Dau0, full::DcaXYToPvV0Dau1, full::DcaXYToPvCascDau, + full::Chi2GeoV0, full::Chi2GeoCasc, full::Chi2GeoOmegac, + full::Chi2MassV0, full::Chi2MassCasc, + full::V0ldl, full::Cascldl, full::Omegacldl, + full::Chi2TopoV0ToPv, full::Chi2TopoCascToPv, full::Chi2TopoPiFromOmegacToPv, full::Chi2TopoOmegacToPv, + full::Chi2TopoV0ToCasc, full::Chi2TopoCascToOmegac, + full::DecayLenXYLambda, full::DecayLenXYCasc, full::DecayLenXYOmegac, + full::CosPaV0ToCasc, full::CosPaV0ToPv, full::CosPaCascToOmegac, full::CosPaCascToPv, full::CosPaOmegacToPv, + full::InvMassLambda, full::InvMassCascade, full::InvMassCharmBaryon, + full::KfRapOmegac, full::KfptPiFromOmegac, full::KfptOmegac, + full::CosThetaStarPiFromOmegac, full::CtOmegac, full::EtaOmegac, + full::V0Ndf, full::CascNdf, full::OmegacNdf, + full::MassV0Ndf, full::MassCascNdf, + full::V0Chi2OverNdf, full::CascChi2OverNdf, full::OmegacChi2OverNdf, + full::MassV0Chi2OverNdf, full::MassCascChi2OverNdf, full::CascRejectInvmass, + full::ResultSelections, + full::FlagMcMatchRec, full::OriginMcRec, full::CollisionMatched); +} // namespace o2::aod + +/// Writes the full information in an output TTree +struct HfTreeCreatorToOmegaPiQa { + + Produces rowCandidate; + Produces rowKfCandidate; + Produces rowEv; + + Configurable zPvCut{"zPvCut", 10., "Cut on absolute value of primary vertex z coordinate"}; + Configurable keepOnlyMcSignal{"keepOnlyMcSignal", true, "Fill MC tree only with signal candidates"}; + + using MyTrackTable = soa::Join; + + using CandSel = soa::Filtered>; + using CandKfSel = soa::Filtered>; + using CandMcSel = soa::Filtered>; + using CandKfMcSel = soa::Filtered>; + + using CollsSel = soa::Join; + using CollsSelWithFT0M = soa::Join; + using CollsWithMcLabels = soa::Join; + using McCollsWithFT0M = soa::Join; + + Filter filterOmegaCToOmegaPiFlag = (o2::aod::hf_track_index::hfflag & static_cast(BIT(aod::hf_cand_casc_lf::DecayType2Prong::OmegaczeroToOmegaPi))) != static_cast(0); + Filter filterSelectCandidates = aod::hf_sel_toomegapi::resultSelections == true; + + void init(InitContext const&) + { + } + + ////////////////////////////////////////////////////// + // // + // Fill functions to fill in the tables // + // // + ////////////////////////////////////////////////////// + + template + void fillEvent(const T& collision) + { + rowEv(collision.sel8(), std::abs(collision.posZ()) < zPvCut); + } + + template + void fillCandidate(const T& candidate, int8_t flagMc, int8_t originMc, bool collisionMatched, float centrality = -999.f) + { + + // save all candidate information + if constexpr (useCentrality) { + if (centrality < -998.f) { + auto const& collision = candidate.template collision_as(); + centrality = o2::hf_centrality::getCentralityColl(collision); + } + } + + if constexpr (svReco == DCAFITTER) { + rowCandidate(candidate.xPv(), + candidate.yPv(), + candidate.zPv(), + centrality, + candidate.template collision_as().numContrib(), + candidate.template collision_as().chi2(), + candidate.xDecayVtxCharmBaryon(), + candidate.yDecayVtxCharmBaryon(), + candidate.zDecayVtxCharmBaryon(), + candidate.xDecayVtxCascade(), + candidate.yDecayVtxCascade(), + candidate.zDecayVtxCascade(), + candidate.xDecayVtxV0(), + candidate.yDecayVtxV0(), + candidate.zDecayVtxV0(), + candidate.signDecay(), + RecoDecay::pt(candidate.pxCharmBaryon(), candidate.pyCharmBaryon()), + RecoDecay::pt(candidate.pxBachFromCharmBaryon(), candidate.pyBachFromCharmBaryon()), + candidate.pxCharmBaryon(), + candidate.pyCharmBaryon(), + candidate.pzCharmBaryon(), + candidate.pxBachFromCharmBaryon(), + candidate.pyBachFromCharmBaryon(), + candidate.pzBachFromCharmBaryon(), + candidate.pxBachFromCasc(), + candidate.pyBachFromCasc(), + candidate.pzBachFromCasc(), + candidate.pxPosV0Dau(), + candidate.pyPosV0Dau(), + candidate.pzPosV0Dau(), + candidate.pxNegV0Dau(), + candidate.pyNegV0Dau(), + candidate.pzNegV0Dau(), + candidate.impactParCascXY(), + candidate.impactParBachFromCharmBaryonXY(), + candidate.errImpactParCascXY(), + candidate.errImpactParBachFromCharmBaryonXY(), + candidate.invMassLambda(), + candidate.invMassCascade(), + candidate.invMassCharmBaryon(), + candidate.cosPAV0(), + candidate.cosPACharmBaryon(), + candidate.cosPACasc(), + candidate.etaV0PosDau(), + candidate.etaV0NegDau(), + candidate.etaBachFromCasc(), + candidate.etaBachFromCharmBaryon(), + candidate.etaCharmBaryon(), + candidate.dcaXYToPvV0Dau0(), + candidate.dcaXYToPvV0Dau1(), + candidate.dcaXYToPvCascDau(), + candidate.dcaCascDau(), + candidate.dcaV0Dau(), + candidate.dcaCharmBaryonDau(), + candidate.decLenCharmBaryon(), + candidate.errorDecayLengthCharmBaryon(), + candidate.impactParCascXY() / candidate.errImpactParCascXY(), + candidate.impactParBachFromCharmBaryonXY() / candidate.errImpactParBachFromCharmBaryonXY(), + candidate.normDecayLenCharmBar(), + candidate.template bachelorFromCharmBaryon_as().isGlobalTrackWoDCA(), + candidate.template bachelorFromCharmBaryon_as().itsNCls(), + candidate.template bachelorFromCharmBaryon_as().tpcNClsCrossedRows(), + candidate.template bachelor_as().tpcNClsCrossedRows(), + candidate.template posTrack_as().tpcNClsCrossedRows(), + candidate.template negTrack_as().tpcNClsCrossedRows(), + candidate.statusPidLambda(), + candidate.statusPidCascade(), + candidate.statusPidCharmBaryon(), + candidate.statusInvMassLambda(), + candidate.statusInvMassCascade(), + candidate.statusInvMassCharmBaryon(), + candidate.resultSelections(), + candidate.pidTpcInfoStored(), + candidate.pidTofInfoStored(), + candidate.tpcNSigmaPiFromCharmBaryon(), + candidate.tpcNSigmaKaFromCasc(), + candidate.tpcNSigmaPiFromLambda(), + candidate.tpcNSigmaPrFromLambda(), + candidate.tofNSigmaPiFromCharmBaryon(), + candidate.tofNSigmaKaFromCasc(), + candidate.tofNSigmaPiFromLambda(), + candidate.tofNSigmaPrFromLambda(), + flagMc, + originMc, + collisionMatched); + } else { + rowKfCandidate(centrality, + candidate.template collision_as().numContrib(), + candidate.tpcNSigmaPiFromCharmBaryon(), + candidate.tofNSigmaPiFromCharmBaryon(), + candidate.tpcNSigmaKaFromCasc(), + candidate.tofNSigmaKaFromCasc(), + candidate.tpcNSigmaPiFromLambda(), + candidate.tofNSigmaPiFromLambda(), + candidate.tpcNSigmaPrFromLambda(), + candidate.tofNSigmaPrFromLambda(), + candidate.kfDcaXYPiFromOmegac(), + candidate.dcaCascDau(), + candidate.dcaV0Dau(), + candidate.dcaCharmBaryonDau(), + candidate.kfDcaXYCascToPv(), + candidate.dcaXYToPvV0Dau0(), + candidate.dcaXYToPvV0Dau1(), + candidate.dcaXYToPvCascDau(), + candidate.chi2GeoV0(), + candidate.chi2GeoCasc(), + candidate.chi2GeoOmegac(), + candidate.chi2MassV0(), + candidate.chi2MassCasc(), + candidate.v0ldl(), + candidate.cascldl(), + candidate.omegacldl(), + candidate.chi2TopoV0ToPv(), + candidate.chi2TopoCascToPv(), + candidate.chi2TopoPiFromOmegacToPv(), + candidate.chi2TopoOmegacToPv(), + candidate.chi2TopoV0ToCasc(), + candidate.chi2TopoCascToOmegac(), + candidate.decayLenXYLambda(), + candidate.decayLenXYCasc(), + candidate.decayLenXYOmegac(), + candidate.cosPaV0ToCasc(), + candidate.cosPAV0(), + candidate.cosPaCascToOmegac(), + candidate.cosPACasc(), + candidate.cosPACharmBaryon(), + candidate.invMassLambda(), + candidate.invMassCascade(), + candidate.invMassCharmBaryon(), + candidate.kfRapOmegac(), + candidate.kfptPiFromOmegac(), + candidate.kfptOmegac(), + candidate.cosThetaStarPiFromOmegac(), + candidate.cTauOmegac(), + candidate.etaCharmBaryon(), + candidate.v0Ndf(), + candidate.cascNdf(), + candidate.omegacNdf(), + candidate.massV0Ndf(), + candidate.massCascNdf(), + candidate.v0Chi2OverNdf(), + candidate.cascChi2OverNdf(), + candidate.omegacChi2OverNdf(), + candidate.massV0Chi2OverNdf(), + candidate.massCascChi2OverNdf(), + candidate.cascRejectInvmass(), + candidate.resultSelections(), + flagMc, + originMc, + collisionMatched); + } + } + + //////////////////////////////////// + // // + // Process functions // + // // + //////////////////////////////////// + + //*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*// + //*~~~~~~~Data with DCAFitter~~~~~~~~*// + //*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*// + + void processData(CollsSel const& collisions, + MyTrackTable const&, + CandSel const& candidates) + { + // Filling event properties + rowEv.reserve(collisions.size()); + for (const auto& collision : collisions) { + fillEvent(collision); + } + + // Filling candidate properties + rowCandidate.reserve(candidates.size()); + for (const auto& candidate : candidates) { + fillCandidate(candidate, -7, RecoDecay::OriginType::None, false); + } + } + + void processDataCent(CollsSelWithFT0M const& collisions, + MyTrackTable const&, + CandSel const& candidates) + { + // Filling event properties + rowEv.reserve(collisions.size()); + for (const auto& collision : collisions) { + fillEvent(collision); + } + + // Filling candidate properties + rowCandidate.reserve(candidates.size()); + for (const auto& candidate : candidates) { + fillCandidate(candidate, -7, RecoDecay::OriginType::None, false); + } + } + + PROCESS_SWITCH(HfTreeCreatorToOmegaPiQa, processData, "Process DCAFitter data, no cent", true); + PROCESS_SWITCH(HfTreeCreatorToOmegaPiQa, processDataCent, "Process DCAFitter data, with FT0M", false); + + //*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*// + //*~~~~~~~Data with KFParticle~~~~~~~~*// + //*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*// + + void processKfData(CollsSel const& collisions, + CandKfSel const& candidates) + { + // Filling event properties + rowEv.reserve(collisions.size()); + for (const auto& collision : collisions) { + fillEvent(collision); + } + + // Filling candidate properties + rowKfCandidate.reserve(candidates.size()); + for (const auto& candidate : candidates) { + fillCandidate(candidate, -7, RecoDecay::OriginType::None, false); + } + } + + void processKfDataCent(CollsSelWithFT0M const& collisions, + CandKfSel const& candidates) + { + // Filling event properties + rowEv.reserve(collisions.size()); + for (const auto& collision : collisions) { + fillEvent(collision); + } + + // Filling candidate properties + rowKfCandidate.reserve(candidates.size()); + for (const auto& candidate : candidates) { + fillCandidate(candidate, -7, RecoDecay::OriginType::None, false); + } + } + + PROCESS_SWITCH(HfTreeCreatorToOmegaPiQa, processKfData, "Process KF data, no cent", false); + PROCESS_SWITCH(HfTreeCreatorToOmegaPiQa, processKfDataCent, "Process KF data, with FT0M", false); + + //*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*// + //*~~~~~~~MC with DCAFitter~~~~~~~~*// + //*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*// + + void processMc(CollsSel const& collisions, + MyTrackTable const&, + CandMcSel const& candidates) + { + // Filling event properties + rowEv.reserve(collisions.size()); + for (const auto& collision : collisions) { + fillEvent(collision); + } + + // Filling candidate properties + rowCandidate.reserve(candidates.size()); + for (const auto& candidate : candidates) { + if (keepOnlyMcSignal && candidate.originMcRec() == RecoDecay::OriginType::None) { + continue; + } + fillCandidate(candidate, candidate.flagMcMatchRec(), candidate.originMcRec(), candidate.collisionMatched()); + } + } + + void processMcCent(CollsWithMcLabels const& collisions, + MyTrackTable const&, + CandMcSel const& candidates, + McCollsWithFT0M const&) + { + // Filling event properties + rowEv.reserve(collisions.size()); + for (const auto& collision : collisions) { + fillEvent(collision); + } + + // Filling candidate properties + rowCandidate.reserve(candidates.size()); + for (const auto& candidate : candidates) { + if (keepOnlyMcSignal && candidate.originMcRec() == RecoDecay::OriginType::None) { + continue; + } + auto mcCollision = candidate.collision_as().mcCollision_as(); + float centFt0m = o2::hf_centrality::getCentralityColl(mcCollision); + fillCandidate(candidate, candidate.flagMcMatchRec(), candidate.originMcRec(), candidate.collisionMatched(), centFt0m); + } + } + + PROCESS_SWITCH(HfTreeCreatorToOmegaPiQa, processMc, "Process DCAFitter MC for Omegac0", false); + PROCESS_SWITCH(HfTreeCreatorToOmegaPiQa, processMcCent, "Process DCAFitter MC for Omegac0 with FT0M", false); + + //*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*// + //*~~~~~~~MC with KFParticle~~~~~~~~*// + //*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*// + + void processKfMc(CollsSel const& collisions, + CandKfMcSel const& candidates) + { + // Filling event properties + rowEv.reserve(collisions.size()); + for (const auto& collision : collisions) { + fillEvent(collision); + } + + // Filling candidate properties + rowKfCandidate.reserve(candidates.size()); + for (const auto& candidate : candidates) { + if (keepOnlyMcSignal && candidate.originMcRec() == RecoDecay::OriginType::None) { + continue; + } + fillCandidate(candidate, candidate.flagMcMatchRec(), candidate.originMcRec(), candidate.collisionMatched()); + } + } + + void processKfMcCent(CollsWithMcLabels const& collisions, + CandKfMcSel const& candidates, + McCollsWithFT0M const&) + { + // Filling event properties + rowEv.reserve(collisions.size()); + for (const auto& collision : collisions) { + fillEvent(collision); + } + + // Filling candidate properties + rowKfCandidate.reserve(candidates.size()); + for (const auto& candidate : candidates) { + if (keepOnlyMcSignal && candidate.originMcRec() == RecoDecay::OriginType::None) { + continue; + } + auto mcCollision = candidate.collision_as().mcCollision_as(); + float centFt0m = o2::hf_centrality::getCentralityColl(mcCollision); + fillCandidate(candidate, candidate.flagMcMatchRec(), candidate.originMcRec(), candidate.collisionMatched(), centFt0m); + } + } + + PROCESS_SWITCH(HfTreeCreatorToOmegaPiQa, processKfMc, "Process KF MC for Omegac0", false); + PROCESS_SWITCH(HfTreeCreatorToOmegaPiQa, processKfMcCent, "Process KF MC for Omegac0 with FT0M", false); +}; // end of struct + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGHF/TableProducer/treeCreatorXicToXiPiPi.cxx b/PWGHF/TableProducer/treeCreatorXicToXiPiPi.cxx index b407f3b51be..338fe6ca1fd 100644 --- a/PWGHF/TableProducer/treeCreatorXicToXiPiPi.cxx +++ b/PWGHF/TableProducer/treeCreatorXicToXiPiPi.cxx @@ -31,10 +31,13 @@ #include #include +#include + #include #include #include +#include using namespace o2; using namespace o2::framework; @@ -70,13 +73,20 @@ DECLARE_SOA_COLUMN(CpaXY, cpaXY, float); //! DECLARE_SOA_COLUMN(PtXi, ptXi, float); //! Transverse momentum of Xi (prong0) (GeV/c) DECLARE_SOA_COLUMN(ImpactParameterXi, impactParameterXi, float); //! Impact parameter of Xi (prong0) DECLARE_SOA_COLUMN(ImpactParameterNormalisedXi, impactParameterNormalisedXi, float); //! Normalised impact parameter of Xi (prong0) +DECLARE_SOA_COLUMN(EtaBachelorPi, etaBachelorPi, float); //! Pseudorapidity of bachelor Pi DECLARE_SOA_COLUMN(PPi0, pPi0, float); //! Momentum of Pi0 (prong1) (GeV/c) DECLARE_SOA_COLUMN(PtPi0, ptPi0, float); //! Transverse momentum of Pi0 (prong1) (GeV/c) +DECLARE_SOA_COLUMN(EtaPi0, etaPi0, float); //! Pseudorapidity of Pi0 (prong1 DECLARE_SOA_COLUMN(ImpactParameterPi0, impactParameterPi0, float); //! Impact parameter of Pi0 (prong1) DECLARE_SOA_COLUMN(ImpactParameterNormalisedPi0, impactParameterNormalisedPi0, float); //! Normalised impact parameter of Pi0 (prong1) DECLARE_SOA_COLUMN(PPi1, pPi1, float); //! Momentum of Pi1 (prong2) (GeV/c) DECLARE_SOA_COLUMN(PtPi1, ptPi1, float); //! Transverse momentum of Pi1 (prong2) (GeV/c) -DECLARE_SOA_COLUMN(ImpactParameterPi1, impactParameterPi1, float); //! Normalised impact parameter of Pi1 (prong2) +DECLARE_SOA_COLUMN(EtaPi1, etaPi1, float); //! Pseudorapidity of Pi1 (prong2) +DECLARE_SOA_COLUMN(PtPiFromLambda, ptPiFromLambda, float); //! Transverse momentum of pion from Lambda (GeV/c) +DECLARE_SOA_COLUMN(EtaPiFromLambda, etaPiFromLambda, float); //! Pseudorapidity of pion from Lambda (GeV/c) +DECLARE_SOA_COLUMN(PtPrFromLambda, ptPrFromLambda, float); //! Transverse momentum of proton from Lambda (GeV/c) +DECLARE_SOA_COLUMN(EtaPrFromLambda, etaPrFromLambda, float); //! Pseudorapidity of proton from Lambda (GeV/c) +DECLARE_SOA_COLUMN(ImpactParameterPi1, impactParameterPi1, float); //! Impact parameter of Pi1 (prong2) DECLARE_SOA_COLUMN(ImpactParameterNormalisedPi1, impactParameterNormalisedPi1, float); //! Normalised impact parameter of Pi1 (prong2) DECLARE_SOA_COLUMN(MaxNormalisedDeltaIP, maxNormalisedDeltaIP, float); //! Maximum normalized difference between measured and expected impact parameter of candidate prongs DECLARE_SOA_COLUMN(MlScoreBkg, mlScoreBkg, float); //! ML score for background class @@ -364,10 +374,21 @@ DECLARE_SOA_TABLE(HfCandXicToXiPiPiFullPs, "AOD", "HFXICXI2PIFULLP", hf_cand_mc_flag::OriginMcGen, hf_cand_mc_flag::PdgBhadMotherPart, full::Pt, + full::M, full::Eta, full::Phi, full::Y, - hf_cand_xic_to_xi_pi_pi::DecayLengthMcGen); + hf_cand_xic_to_xi_pi_pi::DecayLengthMcGen, + full::PtPi0, + full::PtPi1, + hf_cand_xic_to_xi_pi_pi::PtBachelorPi, + full::PtPiFromLambda, + full::PtPrFromLambda, + full::EtaPi0, + full::EtaPi1, + full::EtaBachelorPi, + full::EtaPiFromLambda, + full::EtaPrFromLambda); } // namespace o2::aod /// Writes the full information in an output TTree @@ -382,6 +403,7 @@ struct HfTreeCreatorXicToXiPiPi { Configurable selectionFlagXic{"selectionFlagXic", 1, "Selection Flag for Xic"}; Configurable fillCandidateLiteTable{"fillCandidateLiteTable", false, "Switch to fill lite table with candidate properties"}; Configurable fillGenParticleTable{"fillGenParticleTable", false, "Switch to fill table with MC truth for generated particles"}; + Configurable fillGenInfoForAcceptance{"fillGenInfoForAcceptance", true, "Enable filling of information for acceptance computation"}; // parameters for production of training samples Configurable fillOnlySignal{"fillOnlySignal", false, "Flag to fill derived tables with signal for ML trainings"}; Configurable fillOnlyBackground{"fillOnlyBackground", false, "Flag to fill derived tables with background for ML trainings"}; @@ -399,12 +421,12 @@ struct HfTreeCreatorXicToXiPiPi { using MatchedGenXicToXiPiPi = soa::Filtered>; Filter filterSelectCandidates = aod::hf_sel_candidate_xic::isSelXicToXiPiPi >= selectionFlagXic; - Filter filterGenXicToXiPiPi = (nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(BIT(aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi)) || nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(BIT(aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiResPiToXiPiPi))); + Filter filterGenXicToXiPiPi = (nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(BIT(aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi)) || nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(BIT(aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiResPiToXiPiPi)) || fillGenInfoForAcceptance == true); - Partition recSig = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != int8_t(0); - Partition recBg = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == int8_t(0); - Partition recSigKf = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != int8_t(0); - Partition recBgKf = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == int8_t(0); + Partition recSig = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != static_cast(0); + Partition recBg = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(0); + Partition recSigKf = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != static_cast(0); + Partition recBgKf = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(0); void init(InitContext const&) { @@ -715,6 +737,61 @@ struct HfTreeCreatorXicToXiPiPi { } } + template + void getMcDaughterInformation(Parts const& particles, + Parts::iterator const& particle, + float& ptPi0, + float& ptPi1, + float& ptPiBachelor, + float& ptPiFromLambda, + float& ptPrFromLambda, + float& etaPi0, + float& etaPi1, + float& etaPiBachelor, + float& etaPiFromLambda, + float& etaPrFromLambda) + { + std::vector arrDaughIndex = {}; + RecoDecay::getDaughters(particle, &arrDaughIndex, std::array{+kXiMinus, +kPiPlus, +kPiPlus}, 2); + bool foundFirstPi{false}; + for (const int iProng : arrDaughIndex) { + auto daugh = particles.rawIteratorAt(iProng); + if (std::abs(daugh.pdgCode()) == +kPiPlus) { + if (foundFirstPi) { + ptPi1 = daugh.pt(); + etaPi1 = daugh.eta(); + } else { + ptPi0 = daugh.pt(); + etaPi0 = daugh.eta(); + foundFirstPi = true; + } + } else if (std::abs(daugh.pdgCode()) == +kXiMinus) { + std::vector arrDaughIndexXi = {}; + RecoDecay::getDaughters(daugh, &arrDaughIndexXi, std::array{+kLambda0, -kPiPlus}, 1); + for (const int iProngXi : arrDaughIndexXi) { + auto daughXi = particles.rawIteratorAt(iProngXi); + if (std::abs(daughXi.pdgCode()) == +kPiPlus) { + ptPiBachelor = daughXi.pt(); + etaPiBachelor = daughXi.eta(); + } else { + std::vector arrDaughIndexLambda = {}; + RecoDecay::getDaughters(daughXi, &arrDaughIndexLambda, std::array{+kProton, -kPiPlus}, 1); + for (const int iProngLambda : arrDaughIndexLambda) { + auto daughLambda = particles.rawIteratorAt(iProngLambda); + if (std::abs(daughLambda.pdgCode()) == +kPiPlus) { + ptPiFromLambda = daughLambda.pt(); + etaPiFromLambda = daughLambda.eta(); + } else { + ptPrFromLambda = daughLambda.pt(); + etaPrFromLambda = daughLambda.eta(); + } + } + } + } + } + } + } + void processData(SelectedCandidates const& candidates) { // Filling candidate properties @@ -806,15 +883,34 @@ struct HfTreeCreatorXicToXiPiPi { if (fillGenParticleTable) { rowCandidateFullParticles.reserve(particles.size()); for (const auto& particle : particles) { + float ptPi0{-1.f}, ptPi1{-1.f}, ptPiBachelor{-1.f}, ptPiFromLambda{-1.f}, ptPrFromLambda{-1.f}; + float etaPi0{-1.f}, etaPi1{-1.f}, etaPiBachelor{-1.f}, etaPiFromLambda{-1.f}, etaPrFromLambda{-1.f}; + if (fillGenInfoForAcceptance) { + if (std::abs(particle.flagMcMatchGen()) != BIT(aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi) && std::abs(particle.flagMcMatchGen()) != BIT(aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiResPiToXiPiPi)) { // we cannot filter, otherwise we have no daughter particles in the filtered table + continue; + } + getMcDaughterInformation(particles, particle, ptPi0, ptPi1, ptPiBachelor, ptPiFromLambda, ptPrFromLambda, etaPi0, etaPi1, etaPiBachelor, etaPiFromLambda, etaPrFromLambda); + } rowCandidateFullParticles( particle.flagMcMatchGen(), particle.originMcGen(), particle.pdgBhadMotherPart(), particle.pt(), + particle.massMcGen(), particle.eta(), particle.phi(), RecoDecay::y(particle.pVector(), o2::constants::physics::MassXiCPlus), - particle.decayLengthMcGen()); + particle.decayLengthMcGen(), + ptPi0, + ptPi1, + ptPiBachelor, + ptPiFromLambda, + ptPrFromLambda, + etaPi0, + etaPi1, + etaPiBachelor, + etaPiFromLambda, + etaPrFromLambda); } } } @@ -860,15 +956,34 @@ struct HfTreeCreatorXicToXiPiPi { if (fillGenParticleTable) { rowCandidateFullParticles.reserve(particles.size()); for (const auto& particle : particles) { + float ptPi0{-1.f}, ptPi1{-1.f}, ptPiBachelor{-1.f}, ptPiFromLambda{-1.f}, ptPrFromLambda{-1.f}; + float etaPi0{-1.f}, etaPi1{-1.f}, etaPiBachelor{-1.f}, etaPiFromLambda{-1.f}, etaPrFromLambda{-1.f}; + if (fillGenInfoForAcceptance) { + if (std::abs(particle.flagMcMatchGen()) != BIT(aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi) && std::abs(particle.flagMcMatchGen()) != BIT(aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiResPiToXiPiPi)) { // we cannot filter, otherwise we have no daughter particles in the filtered table + continue; + } + getMcDaughterInformation(particles, particle, ptPi0, ptPi1, ptPiBachelor, ptPiFromLambda, ptPrFromLambda, etaPi0, etaPi1, etaPiBachelor, etaPiFromLambda, etaPrFromLambda); + } rowCandidateFullParticles( particle.flagMcMatchGen(), particle.originMcGen(), particle.pdgBhadMotherPart(), particle.pt(), + particle.massMcGen(), particle.eta(), particle.phi(), RecoDecay::y(particle.pVector(), o2::constants::physics::MassXiCPlus), - particle.decayLengthMcGen()); + particle.decayLengthMcGen(), + ptPi0, + ptPi1, + ptPiBachelor, + ptPiFromLambda, + ptPrFromLambda, + etaPi0, + etaPi1, + etaPiBachelor, + etaPiFromLambda, + etaPrFromLambda); } } } @@ -881,14 +996,14 @@ struct HfTreeCreatorXicToXiPiPi { rowCandidateLiteMl.reserve(candidates.size()); if (fillOnlySignal) { for (const auto& candidate : candidates) { - if (candidate.flagMcMatchRec() == int8_t(0)) { + if (candidate.flagMcMatchRec() == static_cast(0)) { continue; } fillCandidateTable(candidate); } } else if (fillOnlyBackground) { for (const auto& candidate : candidates) { - if (candidate.flagMcMatchRec() != int8_t(0)) { + if (candidate.flagMcMatchRec() != static_cast(0)) { continue; } float const pseudoRndm = candidate.ptProng1() * 1000. - static_cast(candidate.ptProng1() * 1000); @@ -906,15 +1021,35 @@ struct HfTreeCreatorXicToXiPiPi { if (fillGenParticleTable) { rowCandidateFullParticles.reserve(particles.size()); for (const auto& particle : particles) { + float ptPi0{-1.f}, ptPi1{-1.f}, ptPiBachelor{-1.f}, ptPiFromLambda{-1.f}, ptPrFromLambda{-1.f}; + float etaPi0{-1.f}, etaPi1{-1.f}, etaPiBachelor{-1.f}, etaPiFromLambda{-1.f}, etaPrFromLambda{-1.f}; + if (fillGenInfoForAcceptance) { + if (std::abs(particle.flagMcMatchGen()) != BIT(aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi) && std::abs(particle.flagMcMatchGen()) != BIT(aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiResPiToXiPiPi)) { // we cannot filter, otherwise we have no daughter particles in the filtered table + continue; + } + getMcDaughterInformation(particles, particle, ptPi0, ptPi1, ptPiBachelor, ptPiFromLambda, ptPrFromLambda, etaPi0, etaPi1, etaPiBachelor, etaPiFromLambda, etaPrFromLambda); + } + rowCandidateFullParticles( particle.flagMcMatchGen(), particle.originMcGen(), particle.pdgBhadMotherPart(), particle.pt(), + particle.massMcGen(), particle.eta(), particle.phi(), RecoDecay::y(particle.pVector(), o2::constants::physics::MassXiCPlus), - particle.decayLengthMcGen()); + particle.decayLengthMcGen(), + ptPi0, + ptPi1, + ptPiBachelor, + ptPiFromLambda, + ptPrFromLambda, + etaPi0, + etaPi1, + etaPiBachelor, + etaPiFromLambda, + etaPrFromLambda); } } } diff --git a/PWGHF/Tasks/CMakeLists.txt b/PWGHF/Tasks/CMakeLists.txt index d75bbfee3c8..6c4a4a71b71 100644 --- a/PWGHF/Tasks/CMakeLists.txt +++ b/PWGHF/Tasks/CMakeLists.txt @@ -11,6 +11,7 @@ o2physics_add_dpl_workflow(task-charm-had-impact-par SOURCES taskCharmHadImpactPar.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -21,11 +22,13 @@ o2physics_add_dpl_workflow(task-charm-had-impact-par o2physics_add_dpl_workflow(task-mc-efficiency SOURCES taskMcEfficiency.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-mc-efficiency-to-xi-pi SOURCES taskMcEfficiencyToXiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -36,6 +39,7 @@ o2physics_add_dpl_workflow(task-mc-gen-pt-rap-shapes o2physics_add_dpl_workflow(task-mc-validation SOURCES taskMcValidation.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) diff --git a/PWGHF/Tasks/taskLcCentrality.cxx b/PWGHF/Tasks/taskLcCentrality.cxx index ce3752b40e1..0a34527690f 100644 --- a/PWGHF/Tasks/taskLcCentrality.cxx +++ b/PWGHF/Tasks/taskLcCentrality.cxx @@ -71,7 +71,7 @@ struct HfTaskLcCentrality { void init(InitContext&) { - auto vbins = (std::vector)binsPt; + auto vbins = std::vector(binsPt); registry.add("hMass", "3-prong candidates;inv. mass (p K #pi) (GeV/#it{c}^{2}); p_{T}; centrality", {HistType::kTH3F, {{500, 1.6, 3.1}, {vbins, "#it{p}_{T} (GeV/#it{c})"}, {100, 0., 100.}}}); registry.add("hDecLength", "3-prong candidates;decay length (cm);entries", {HistType::kTH2F, {{200, 0., 2.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hd0Prong0", "3-prong candidates;prong 0 DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{100, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); @@ -147,7 +147,7 @@ struct HfTaskLcCentralityMc { void init(InitContext&) { - auto vbins = (std::vector)binsPt; + auto vbins = std::vector(binsPt); registry.add("hPtRecSig", "3-prong candidates (matched);#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH1F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hPtRecSigPrompt", "3-prong candidates (matched, prompt);#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH1F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hPtRecSigNonPrompt", "3-prong candidates (matched, non-prompt);#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH1F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}}}); diff --git a/PWGHF/Tasks/taskMcValidation.cxx b/PWGHF/Tasks/taskMcValidation.cxx index 3167bcd8f69..8fe64b12f51 100644 --- a/PWGHF/Tasks/taskMcValidation.cxx +++ b/PWGHF/Tasks/taskMcValidation.cxx @@ -1037,7 +1037,7 @@ struct HfTaskMcValidationRec { nITSlayers++; } } - uint const index = uint(track.collisionId() >= 0); + uint const index = static_cast(track.collisionId() >= 0); if (track.has_mcParticle()) { const auto& particle = track.mcParticle(); // get corresponding MC particle to check origin const auto& mcCollision = particle.mcCollision_as(); diff --git a/PWGHF/Utils/utilsDerivedData.h b/PWGHF/Utils/utilsDerivedData.h index f2b63291665..12238d7eb65 100644 --- a/PWGHF/Utils/utilsDerivedData.h +++ b/PWGHF/Utils/utilsDerivedData.h @@ -168,11 +168,16 @@ struct HfProducesDerivedData : o2::framework::ProducesGroup { void fillTablesMcCollision(TMcCollision const& mcCollision) { if (conf->fillMcCollBase.value) { + float centFT0M{-1.f}; // Input without centrality: e.g. generator-only + if constexpr (requires { mcCollision.centFT0M(); }) { + // MC general: input includes centrality + centFT0M = mcCollision.centFT0M(); + } rowMcCollBase( mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), - mcCollision.centFT0M()); + centFT0M); } if (conf->fillMcCollId.value) { rowMcCollId( diff --git a/PWGHF/Utils/utilsEvSelHf.h b/PWGHF/Utils/utilsEvSelHf.h index 7ce2b179dad..7eb9d311187 100644 --- a/PWGHF/Utils/utilsEvSelHf.h +++ b/PWGHF/Utils/utilsEvSelHf.h @@ -197,6 +197,7 @@ struct HfEventSelection : o2::framework::ConfigurableGroup { o2::framework::Configurable occupancyMax{"occupancyMax", 1000000, "Maximum occupancy"}; o2::framework::Configurable requireINEL0{"requireINEL0", false, "Require INEL > 0"}; o2::framework::Configurable nPvContributorsMin{"nPvContributorsMin", 0, "Minimum number of PV contributors"}; + o2::framework::Configurable nPvContributorsMax{"nPvContributorsMax", 1000000, "Maximum number of PV contributors"}; o2::framework::Configurable chi2PvMax{"chi2PvMax", -1.f, "Maximum PV chi2"}; o2::framework::Configurable zPvPosMin{"zPvPosMin", -10.f, "Minimum PV posZ (cm)"}; o2::framework::Configurable zPvPosMax{"zPvPosMax", 10.f, "Maximum PV posZ (cm)"}; @@ -410,7 +411,7 @@ struct HfEventSelection : o2::framework::ConfigurableGroup { } /// number of PV contributors - if (collision.numContrib() < nPvContributorsMin) { + if (collision.numContrib() < nPvContributorsMin || collision.numContrib() > nPvContributorsMax) { SETBIT(rejectionMask, EventRejection::NContrib); } diff --git a/PWGJE/Core/CMakeLists.txt b/PWGJE/Core/CMakeLists.txt index a7713c9900d..f1adfc59b7a 100644 --- a/PWGJE/Core/CMakeLists.txt +++ b/PWGJE/Core/CMakeLists.txt @@ -27,3 +27,41 @@ o2physics_target_root_dictionary(PWGJECore utilsTrackMatchingEMC.h LINKDEF PWGJECoreLinkDef.h) endif() + +# Precompiled header shared across PWGJE. Percentages are the share of the +# group's workflow translation units that include the header, measured over the tree. +# Jet.h reaches 90% of the group; the existing substructure carriers already +# precompile it, so this one serves everything they do not cover. +# +# runDataProcessing.h is deliberately NOT precompiled: it requires each workflow's +# customize() to be declared before it is included, so precompiling it silently +# unregisters every option (the binary aborts at --dump-workflow, but links fine). +# +# Skipped under recc, which caches compilations remotely instead. +add_library(JetDataModelPCH OBJECT jetDataModelPCH.cxx) +# The PCH compile is one of the heaviest translation units in the tree -- it +# instantiates the whole data model up front -- but add_library() does not put +# it in the 'analysis' job pool that o2physics_add_dpl_workflow() uses for the +# workflows. Left out, it runs unthrottled alongside the pooled compilations +# and defeats the per-job memory budget the pool exists to enforce. +set_property(TARGET JetDataModelPCH PROPERTY JOB_POOL_COMPILE analysis) +target_link_libraries(JetDataModelPCH PUBLIC O2::Framework O2Physics::AnalysisCore O2Physics::PWGJECore) +if(NOT DEFINED ENV{USE_RECC}) + target_precompile_headers(JetDataModelPCH PRIVATE + # Framework baseline -- same set AnalysisPCH precompiles. A target with + # REUSE_FROM uses ONLY this PCH, so anything left out here is parsed from + # scratch by every consumer: without this block these workflows precompile + # LESS than the default carrier they opted out of. + + + + + + + + + + + [["PWGJE/DataModel/Jet.h"]] # 90% + ) +endif() diff --git a/PWGJE/Core/JetDerivedDataUtilities.h b/PWGJE/Core/JetDerivedDataUtilities.h index 035293a9b7d..45f0a55d214 100644 --- a/PWGJE/Core/JetDerivedDataUtilities.h +++ b/PWGJE/Core/JetDerivedDataUtilities.h @@ -653,12 +653,12 @@ bool applyTrackKinematics(T const& track, float pTMin = 0.15, float pTMax = 100. } template -bool selectTrack(T const& track, int trackSelection, bool isEmbedded = false) +bool selectTrack(T const& track, int trackSelection, bool rejectNonEmbedded = false) { if (!(track.trackSel() & (1ULL << JTrackSel::notBadMcTrack))) { return false; } - if (isEmbedded && !(track.trackSel() & (1ULL << JTrackSel::embeddedTrack))) { // will get rid of non embedded tracks + if (rejectNonEmbedded && !(track.trackSel() & (1ULL << JTrackSel::embeddedTrack))) { // will get rid of non embedded tracks return false; } if (trackSelection == -1) { diff --git a/PWGJE/Core/JetFinder.cxx b/PWGJE/Core/JetFinder.cxx index 0434e5cfa4a..075c92bbbfe 100644 --- a/PWGJE/Core/JetFinder.cxx +++ b/PWGJE/Core/JetFinder.cxx @@ -60,7 +60,7 @@ void JetFinder::setParams() /// \param inputParticles vector of input particles/tracks /// \param jets veector of jets to be filled /// \return ClusterSequenceArea object needed to access constituents -fastjet::ClusterSequenceArea JetFinder::findJets(std::vector& inputParticles, std::vector& jets) // ideally find a way of passing the cluster sequence as a reeference +fastjet::ClusterSequenceArea JetFinder::findJets(std::vector const& inputParticles, std::vector& jets) // ideally find a way of passing the cluster sequence as a reference { setParams(); jets.clear(); diff --git a/PWGJE/Core/JetFinder.h b/PWGJE/Core/JetFinder.h index 90b9a981f6a..7f281d3de03 100644 --- a/PWGJE/Core/JetFinder.h +++ b/PWGJE/Core/JetFinder.h @@ -46,7 +46,7 @@ class JetFinder /// \param inputParticles vector of input particles/tracks /// \param jets veector of jets to be filled /// \return ClusterSequenceArea object needed to access constituents - // fastjet::ClusterSequenceArea findJets(std::vector &inputParticles, std::vector &jets); + // fastjet::ClusterSequenceArea findJets(std::vector const& inputParticles, std::vector& jets); float phiMin = -1. * M_PI; float phiMax = 2. * M_PI; @@ -92,7 +92,7 @@ class JetFinder /// \param inputParticles vector of input particles/tracks /// \param jets veector of jets to be filled /// \return ClusterSequenceArea object needed to access constituents - fastjet::ClusterSequenceArea findJets(std::vector& inputParticles, std::vector& jets); // ideally find a way of passing the cluster sequence as a reeference + fastjet::ClusterSequenceArea findJets(std::vector const& inputParticles, std::vector& jets); // ideally find a way of passing the cluster sequence as a reference private: ClassDefNV(JetFinder, 1); diff --git a/PWGJE/Core/JetFindingUtilities.h b/PWGJE/Core/JetFindingUtilities.h index 9d03d9e9054..9f0d76c9c08 100644 --- a/PWGJE/Core/JetFindingUtilities.h +++ b/PWGJE/Core/JetFindingUtilities.h @@ -91,10 +91,10 @@ constexpr bool isEMCALClusterTable() */ template -bool isTrackSelected(T const& track, int trackSelection, const U* candidate = nullptr) +bool isTrackSelected(T const& track, int trackSelection, bool rejectNonEmbedded = false, const U* candidate = nullptr) { - if (!jetderiveddatautilities::selectTrack(track, trackSelection)) { + if (!jetderiveddatautilities::selectTrack(track, trackSelection, rejectNonEmbedded)) { return false; } if (candidate != nullptr) { @@ -115,10 +115,10 @@ bool isTrackSelected(T const& track, int trackSelection, const U* candidate = nu */ template -void analyseTracks(std::vector& inputParticles, T const& tracks, int trackSelection, const U* candidate = nullptr) +void analyseTracks(std::vector& inputParticles, T const& tracks, int trackSelection, bool rejectNonEmbedded = false, const U* candidate = nullptr) { for (auto& track : tracks) { - if (isTrackSelected(track, trackSelection, candidate)) { + if (isTrackSelected(track, trackSelection, rejectNonEmbedded, candidate)) { fastjetutilities::fillTracks(track, inputParticles, track.globalIndex()); } } @@ -134,11 +134,11 @@ void analyseTracks(std::vector& inputParticles, T const& tra */ template -void analyseTracksMultipleCandidates(std::vector& inputParticles, T const& tracks, int trackSelection, U const& candidates) +void analyseTracksMultipleCandidates(std::vector& inputParticles, T const& tracks, int trackSelection, bool rejectNonEmbedded, U const& candidates) { for (auto& track : tracks) { bool isSelected = true; - if (!jetderiveddatautilities::selectTrack(track, trackSelection)) { + if (!jetderiveddatautilities::selectTrack(track, trackSelection, rejectNonEmbedded)) { continue; } for (auto& candidate : candidates) { @@ -239,6 +239,9 @@ bool analyseV0s(std::vector& inputParticles, T const& v0s, f int nSelectedV0s = 0; for (auto const& v0 : v0s) { if constexpr (jetv0utilities::isV0McTable()) { + if (!v0.isPhysicalPrimary()) { + continue; + } v0Mass = v0.m(); v0Y = v0.y(); } else { @@ -284,12 +287,11 @@ bool analyseV0s(std::vector& inputParticles, T const& v0s, f * @param doHFJetFinding set whether only jets containing a HF candidate are saved */ template -void findJets(JetFinder& jetFinder, std::vector& inputParticles, float jetPtMin, float jetPtMax, std::vector jetRadius, float jetAreaFractionMin, T const& collision, U& jetsTable, V& constituentsTable, const std::shared_ptr& thnSparseJet, bool fillThnSparse, bool doCandidateJetFinding = false) +void findJets(JetFinder& jetFinder, std::vector const& inputParticles, float jetPtMin, float jetPtMax, std::vector const& jetRadius, float jetAreaFractionMin, T const& collision, U& jetsTable, V& constituentsTable, const std::shared_ptr& thnSparseJet, bool fillThnSparse, bool doCandidateJetFinding = false) { - auto jetRValues = static_cast>(std::move(jetRadius)); jetFinder.jetPtMin = jetPtMin; jetFinder.jetPtMax = jetPtMax; - for (auto R : jetRValues) { + for (auto R : jetRadius) { jetFinder.jetR = R; std::vector jets; fastjet::ClusterSequenceArea clusterSeq(jetFinder.findJets(inputParticles, jets)); diff --git a/PWGJE/Core/JetTaggingUtilities.h b/PWGJE/Core/JetTaggingUtilities.h index 754c19331dc..1d07fd40844 100644 --- a/PWGJE/Core/JetTaggingUtilities.h +++ b/PWGJE/Core/JetTaggingUtilities.h @@ -997,8 +997,8 @@ int vertexClustering(AnyCollision const& collision, AnalysisJet const& jet, AnyT trkLabels["trkOrigin"].push_back(0); // mismatched coll track } else { int orig = RecoDecay::getParticleOrigin(particles, particle, searchUpToQuark); - trkLabels["trkOrigin"].push_back((orig != RecoDecay::OriginType::None) ? static_cast(orig) : (trkLabels["trkVtxIndex"][trkIdx] == 0) ? 3 - : 4); // 1: charm, 2: beauty, 3: primary, 4: other secondary + trkLabels["trkOrigin"].push_back((orig != RecoDecay::OriginType::None) ? orig : (trkLabels["trkVtxIndex"][trkIdx] == 0) ? 3 + : 4); // 1: charm, 2: beauty, 3: primary, 4: other secondary } } @@ -1155,6 +1155,65 @@ void analyzeJetTrackInfo4GNN(AnalysisJet const& analysisJet, AnyTracks const& /* } } +// Looping over the track info and putting them in the input vectors (for ParT b-jet tagging). +template +void analyzeJetTrackInfo4ParT(AnalysisJet const& analysisJet, AnyTracks const& /*allTracks*/, std::vector>& featConstit, std::vector>& vecConstit, float trackPtMin = 0.5, float trackDcaXYMax = 10.0, float trackDcaZMax = 10.0) +{ + for (const auto& constituent : analysisJet.template tracks_as()) { + + if (constituent.pt() < trackPtMin || !trackAcceptanceWithDca(constituent, trackDcaXYMax, trackDcaZMax)) { + continue; + } + + int sign = getGeoSign(analysisJet, constituent); + + float trackPt = constituent.pt(); + float trackEta = constituent.eta(); + float trackPhi = constituent.phi(); + + float ip2d = std::abs(constituent.dcaXY()) * sign; + float ipz = std::abs(constituent.dcaZ()) * sign; + float ip2dSigma = constituent.sigmadcaXY(); + float ipzSigma = constituent.sigmadcaZ(); + + float dPhi = std::atan2(std::sin(trackPhi - analysisJet.phi()), std::cos(trackPhi - analysisJet.phi())); + + featConstit.emplace_back(std::vector{ + std::log(trackPt), + std::log(trackPt / analysisJet.pt()), + jetutilities::deltaR(analysisJet, constituent), + trackEta - analysisJet.eta(), + dPhi, + std::tanh(ip2d), + std::tanh(ipz), + (ip2dSigma != 0.f) ? ip2d / ip2dSigma : 0.f, + (ipzSigma != 0.f) ? ipz / ipzSigma : 0.f, + static_cast(constituent.sign())}); + + vecConstit.emplace_back(std::vector{ + trackPt * std::cos(trackPhi), + trackPt * std::sin(trackPhi), + trackPt * std::sinh(trackEta), + trackPt * std::cosh(trackEta)}); + } +} + +// Discriminant value. Probability order is assumed to be [b, c, lf]. +template +T getDbFromProb(const std::vector& probs, double fC = 0.018) +{ + // MlResponse::getModelOutput() returns exactly nClassesMl entries, so a mismatched + // nClassesMl silently truncates the model output and would read out of bounds here. + if (probs.size() < 3) { + LOG(fatal) << "getDbFromProb expects 3 class probabilities but received " << probs.size() << ". Set the nClassesMl configurable to 3 (and give cutDirMl 3 entries to match)."; + } + T denominator = fC * probs[1] + (1. - fC) * probs[2]; + if (probs[0] <= 0. || denominator <= 0.) { + return std::numeric_limits::quiet_NaN(); + } + return std::log(probs[0] / denominator); +} + // Discriminant value for GNN b-jet tagging template T getDb(const std::vector& logits, double fC = 0.018) diff --git a/PWGJE/Core/MlResponseHfTagging.h b/PWGJE/Core/MlResponseHfTagging.h index b144d6e145a..b6cc72ed8d9 100644 --- a/PWGJE/Core/MlResponseHfTagging.h +++ b/PWGJE/Core/MlResponseHfTagging.h @@ -119,6 +119,22 @@ class MlResponseHfTagging : public MlResponse /// @return number of output nodes int getOutputNodes(int imod = 0) const { return this->mModels[imod].getNumOutputNodes(); } + /// @brief Run a model that takes several pre-built input tensors (e.g. ParT: pf_features, pf_vectors, pf_mask inputs). + /// \param input the input tensors, in the order the graph declares them + /// \param nModel index of the model + /// @return the model output, mNClasses entries long + template + std::vector getModelOutputMultiInput(T& input, int nModel = 0) + { + // evalModel returns an owning copy of the (last) output tensor of the model + std::vector output = this->mModels[nModel].template evalModel(input); + if (output.size() < this->mNClasses) { + LOG(fatal) << "Model " << this->mPaths[nModel] << " returned " << output.size() << " scores, but " << static_cast(this->mNClasses) << " classes are expected. Please check your configurables."; + } + output.resize(this->mNClasses); + return output; + } + /// Method to fill the inputs of jet, tracks and secondary vertices /// \param jet is the b-jet candidate /// \param tracks is the vector of tracks associated to the jet @@ -483,6 +499,85 @@ class GNNBjetAllocator : public TensorAllocator } }; +// TensorAllocator for the ParT b-jet tagger. +// Inputs, in graph order: pf_features (1, nFeat, nConstit), pf_vectors (1, nVec, nConstit), +// pf_mask (1, 1, nConstit). +class ParTBjetAllocator : public TensorAllocator +{ + private: + int64_t nConstit; + int64_t nFeat; + int64_t nVec; + bool padWrap; + + // Replace non-finite values in a vector, which the model cannot consume + template + static void replaceNonFinite(std::vector& vec, T value) + { + for (auto& el : vec) { // o2-linter: disable=const-ref-in-for-loop + if (std::isnan(el) || std::isinf(el)) { + el = value; + } + } + } + + public: + ParTBjetAllocator() : TensorAllocator(), nConstit(32), nFeat(10), nVec(4), padWrap(true) {} + ParTBjetAllocator(int64_t nConstit, int64_t nFeat, int64_t nVec, bool padWrap = true) + : TensorAllocator(), nConstit(nConstit), nFeat(nFeat), nVec(nVec), padWrap(padWrap) {} + ~ParTBjetAllocator() = default; + + ParTBjetAllocator& operator=(const ParTBjetAllocator& other) + { + nConstit = other.nConstit; + nFeat = other.nFeat; + nVec = other.nVec; + padWrap = other.padWrap; + return *this; + } + + // Allocate and return the ParT input tensors (std::vector). + // featData/vecData/maskData back the tensors + void getParTInput(const std::vector>& featConstit, const std::vector>& vecConstit, std::vector& featData, std::vector& vecData, std::vector& maskData, std::vector& partInput) + { + featData.assign(nFeat * nConstit, 0.f); + vecData.assign(nVec * nConstit, 0.f); + maskData.assign(nConstit, 0.f); + + int64_t nSelected = static_cast(featConstit.size()); + const int64_t nReal = (nSelected < nConstit) ? nSelected : nConstit; // jets longer than nConstit are truncated + + for (int64_t iConstit = 0; iConstit < nConstit; ++iConstit) { + int64_t iSource = iConstit; + if (iConstit < nReal) { + maskData[iConstit] = 1.f; + } else { + if (!padWrap || nReal == 0) { + continue; // zero padding, mask stays 0 + } + iSource = iConstit % nReal; // weaver 'wrap' padding: cyclic repetition, mask stays 0 + } + for (int64_t iFeat = 0; iFeat < nFeat; ++iFeat) { + featData[iFeat * nConstit + iConstit] = featConstit[iSource][iFeat]; + } + for (int64_t iVec = 0; iVec < nVec; ++iVec) { + vecData[iVec * nConstit + iConstit] = vecConstit[iSource][iVec]; + } + } + + replaceNonFinite(featData, 0.f); + replaceNonFinite(vecData, 0.f); + + std::vector featShape{1, nFeat, nConstit}; + std::vector vecShape{1, nVec, nConstit}; + std::vector maskShape{1, 1, nConstit}; + + partInput.emplace_back(createTensor(featData, featShape)); + partInput.emplace_back(createTensor(vecData, vecShape)); + partInput.emplace_back(createTensor(maskData, maskShape)); + } +}; + } // namespace o2::analysis #undef FILL_MAP_BJET diff --git a/PWGJE/Core/PWGJECoreLinkDef.h b/PWGJE/Core/PWGJECoreLinkDef.h index 869be064e38..cd25d7b94be 100644 --- a/PWGJE/Core/PWGJECoreLinkDef.h +++ b/PWGJE/Core/PWGJECoreLinkDef.h @@ -9,8 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef PWGJE_CORE_PWGJECORELINKDEF_H_ -#define PWGJE_CORE_PWGJECORELINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -21,5 +20,3 @@ #pragma link C++ namespace jetutilities + ; #pragma link C++ namespace fastjetutilities + ; #pragma link C++ namespace jettaggingutilities + ; - -#endif // PWGJE_CORE_PWGJECORELINKDEF_H_ diff --git a/PWGJE/Core/jetDataModelPCH.cxx b/PWGJE/Core/jetDataModelPCH.cxx new file mode 100644 index 00000000000..ed5b36ec798 --- /dev/null +++ b/PWGJE/Core/jetDataModelPCH.cxx @@ -0,0 +1,13 @@ +// Copyright 2019-2026 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. + +// Carrier for the JetDataModelPCH precompiled header; see this directory's +// CMakeLists.txt. It exists only to own the PCH, so it has no code of its own. diff --git a/PWGJE/DataModel/Jet.h b/PWGJE/DataModel/Jet.h index f4afdc17d1f..18780a9c979 100644 --- a/PWGJE/DataModel/Jet.h +++ b/PWGJE/DataModel/Jet.h @@ -156,6 +156,8 @@ DECLARE_SOA_DYNAMIC_COLUMN(P, p, //! absolute p DECLARE_JET_TABLES(JCollision, _jet_type_##EventWiseSubtracted, _subtracted_track_type_, _hfcand_type_, _shortname_ "EWSJET") \ DECLARE_JETMATCHING_TABLE(_jet_type_, _jet_type_##EventWiseSubtracted, _shortname_ "JET2EWS") \ DECLARE_JETMATCHING_TABLE(_jet_type_##EventWiseSubtracted, _jet_type_, _shortname_ "EWSJET2") \ + DECLARE_JETMATCHING_TABLE(_jet_type_##MCDetectorLevel, _jet_type_##EventWiseSubtracted, _shortname_ "DJET2EWS") \ + DECLARE_JETMATCHING_TABLE(_jet_type_##EventWiseSubtracted, _jet_type_##MCDetectorLevel, _shortname_ "EWSJET2D") \ DECLARE_JET_TABLES(JCollision, _jet_type_##MCDetectorLevelEventWiseSubtracted, _subtracted_track_type_, _hfcand_type_, _shortname_ "DEWSJET") \ DECLARE_MCEVENTWEIGHT_TABLE(_jet_type_##MCDetectorLevelEventWiseSubtracted, _jet_type_##MCDetectorLevelEventWiseSubtracted, _shortname_ "DJETEWSW") \ DECLARE_JETMATCHING_TABLE(_jet_type_##MCDetectorLevel, _jet_type_##MCDetectorLevelEventWiseSubtracted, _shortname_ "DJET2DEWS") \ diff --git a/PWGJE/JetFinders/CMakeLists.txt b/PWGJE/JetFinders/CMakeLists.txt index 4426a43c4a4..218d03cad31 100644 --- a/PWGJE/JetFinders/CMakeLists.txt +++ b/PWGJE/JetFinders/CMakeLists.txt @@ -15,227 +15,272 @@ if(FastJet_FOUND) o2physics_add_dpl_workflow(jet-finder-data-charged SOURCES jetFinderDataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-mcd-charged SOURCES jetFinderMCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-mcp-charged SOURCES jetFinderMCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-data-full SOURCES jetFinderDataFull.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-mcd-full SOURCES jetFinderMCDFull.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-mcp-full SOURCES jetFinderMCPFull.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-data-neutral SOURCES jetFinderDataNeutral.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-mcd-neutral SOURCES jetFinderMCDNeutral.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-mcp-neutral SOURCES jetFinderMCPNeutral.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-d0-data-charged SOURCES jetFinderD0DataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-d0-mcd-charged SOURCES jetFinderD0MCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-d0-mcp-charged SOURCES jetFinderD0MCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dplus-data-charged SOURCES jetFinderDplusDataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dplus-mcd-charged SOURCES jetFinderDplusMCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dplus-mcp-charged SOURCES jetFinderDplusMCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-ds-data-charged SOURCES jetFinderDsDataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-ds-mcd-charged SOURCES jetFinderDsMCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-ds-mcp-charged SOURCES jetFinderDsMCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dstar-data-charged SOURCES jetFinderDstarDataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dstar-mcd-charged SOURCES jetFinderDstarMCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dstar-mcp-charged SOURCES jetFinderDstarMCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-lc-data-charged SOURCES jetFinderLcDataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-lc-mcd-charged SOURCES jetFinderLcMCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-lc-mcp-charged SOURCES jetFinderLcMCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-b0-data-charged SOURCES jetFinderB0DataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-b0-mcd-charged SOURCES jetFinderB0MCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-b0-mcp-charged SOURCES jetFinderB0MCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-bplus-data-charged SOURCES jetFinderBplusDataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-bplus-mcd-charged SOURCES jetFinderBplusMCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-bplus-mcp-charged SOURCES jetFinderBplusMCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-xictoxipipi-data-charged SOURCES jetFinderXicToXiPiPiDataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-xictoxipipi-mcd-charged SOURCES jetFinderXicToXiPiPiMCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-xictoxipipi-mcp-charged SOURCES jetFinderXicToXiPiPiMCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-v0-data-charged SOURCES jetFinderV0DataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-v0-mcd-charged SOURCES jetFinderV0MCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-v0-mcp-charged SOURCES jetFinderV0MCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dielectron-data-charged SOURCES jetFinderDielectronDataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dielectron-mcd-charged SOURCES jetFinderDielectronMCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dielectron-mcp-charged SOURCES jetFinderDielectronMCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-d0d0bar-data-charged SOURCES jetFinderD0D0BarDataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-d0d0bar-mcd-charged SOURCES jetFinderD0D0BarMCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-d0d0bar-mcp-charged SOURCES jetFinderD0D0BarMCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dplusdminus-data-charged SOURCES jetFinderDplusDminusDataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dplusdminus-mcd-charged SOURCES jetFinderDplusDminusMCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dplusdminus-mcp-charged SOURCES jetFinderDplusDminusMCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) diff --git a/PWGJE/JetFinders/Duplicates/CMakeLists.txt b/PWGJE/JetFinders/Duplicates/CMakeLists.txt index 4270de9f221..2e0217b488d 100644 --- a/PWGJE/JetFinders/Duplicates/CMakeLists.txt +++ b/PWGJE/JetFinders/Duplicates/CMakeLists.txt @@ -14,16 +14,19 @@ if(FastJet_FOUND) o2physics_add_dpl_workflow(jet-finder-data-charged-1 SOURCES jetFinderDataCharged1.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-mcd-charged-1 SOURCES jetFinderMCDCharged1.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-mcp-charged-1 SOURCES jetFinderMCPCharged1.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) diff --git a/PWGJE/JetFinders/jetFinder.h b/PWGJE/JetFinders/jetFinder.h index 493251442b6..d673c550fe1 100644 --- a/PWGJE/JetFinders/jetFinder.h +++ b/PWGJE/JetFinders/jetFinder.h @@ -64,6 +64,7 @@ struct JetFinderTask { o2::framework::Configurable triggerMasks{"triggerMasks", "", "possible JE Trigger masks: fJetChLowPt,fJetChHighPt,fTrackLowPt,fTrackHighPt,fJetD0ChLowPt,fJetD0ChHighPt,fJetLcChLowPt,fJetLcChHighPt,fEMCALReadout,fJetFullHighPt,fJetFullLowPt,fJetNeutralHighPt,fJetNeutralLowPt,fGammaVeryHighPtEMCAL,fGammaVeryHighPtDCAL,fGammaHighPtEMCAL,fGammaHighPtDCAL,fGammaLowPtEMCAL,fGammaLowPtDCAL,fGammaVeryLowPtEMCAL,fGammaVeryLowPtDCAL"}; o2::framework::Configurable skipMBGapEvents{"skipMBGapEvents", true, "decide to run over MB gap events or not"}; o2::framework::Configurable applyRCTSelections{"applyRCTSelections", true, "decide to apply RCT selections"}; + o2::framework::Configurable isEmbedding{"isEmbedding", false, "running on an embedded dataset"}; // track level configurables o2::framework::Configurable trackPtMin{"trackPtMin", 0.15, "minimum track pT"}; @@ -126,13 +127,13 @@ struct JetFinderTask { void init(o2::framework::InitContext const&) { - hadronicCorrectionTypesVec = (std::vector)hadronicCorrectionTypes; - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + hadronicCorrectionTypesVec = hadronicCorrectionTypes; + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); - particleSelection = static_cast(particleSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); + particleSelection = particleSelections; - clusterDefinitionsVec = jetderiveddatautilities::initialiseClusterDefinitions(clusterDefinitions.value); + clusterDefinitionsVec = jetderiveddatautilities::initialiseClusterDefinitions(clusterDefinitions); jetFinder.etaMin = trackEtaMin; jetFinder.etaMax = trackEtaMax; @@ -163,8 +164,8 @@ struct JetFinderTask { jetFinder.jetEtaDefault = true; } - jetFinder.algorithm = static_cast(static_cast(jetAlgorithm)); - jetFinder.recombScheme = static_cast(static_cast(jetRecombScheme)); + jetFinder.algorithm = static_cast(jetAlgorithm.value); + jetFinder.recombScheme = static_cast(jetRecombScheme.value); jetFinder.ghostArea = jetGhostArea; jetFinder.ghostRepeatN = ghostRepeat; if (DoTriggering) { @@ -172,7 +173,7 @@ struct JetFinderTask { } jetFinder.fastjetExtraParam = jetExtraParam; - auto jetRadiiBins = (std::vector)jetRadius; + auto jetRadiiBins = jetRadius.value; if (jetRadiiBins.size() > 1) { jetRadiiBins.push_back(jetRadiiBins[jetRadiiBins.size() - 1] + (TMath::Abs(jetRadiiBins[jetRadiiBins.size() - 1] - jetRadiiBins[jetRadiiBins.size() - 2]))); } else { @@ -207,7 +208,7 @@ struct JetFinderTask { return; } inputParticles.clear(); - jetfindingutilities::analyseTracks, o2::soa::Filtered::iterator>(inputParticles, tracks, trackSelection); + jetfindingutilities::analyseTracks, o2::soa::Filtered::iterator>(inputParticles, tracks, trackSelection, isEmbedding); jetfindingutilities::findJets(jetFinder, inputParticles, jetPtMin, jetPtMax, jetRadius, jetAreaFractionMin, collision, jetsTable, constituentsTable, fillTHnSparse ? registry.get(HIST("hJet")) : std::shared_ptr(nullptr), fillTHnSparse); } @@ -220,7 +221,7 @@ struct JetFinderTask { return; } inputParticles.clear(); - jetfindingutilities::analyseTracks, o2::soa::Filtered::iterator>(inputParticles, tracks, trackSelection); + jetfindingutilities::analyseTracks, o2::soa::Filtered::iterator>(inputParticles, tracks, trackSelection, isEmbedding); jetfindingutilities::findJets(jetFinder, inputParticles, jetEWSPtMin, jetEWSPtMax, jetRadius, jetAreaFractionMin, collision, jetsEvtWiseSubTable, constituentsEvtWiseSubTable, fillTHnSparse ? registry.get(HIST("hJetEWS")) : std::shared_ptr(nullptr), fillTHnSparse); } @@ -252,7 +253,7 @@ struct JetFinderTask { for (auto const& clusterDefinition : clusterDefinitionsVec) { for (auto const& hadronicCorrectionType : hadronicCorrectionTypesVec) { inputParticles.clear(); - jetfindingutilities::analyseTracks, o2::soa::Filtered::iterator>(inputParticles, tracks, trackSelection); + jetfindingutilities::analyseTracks, o2::soa::Filtered::iterator>(inputParticles, tracks, trackSelection, isEmbedding); jetfindingutilities::analyseClusters(inputParticles, clusters, clusterDefinition, hadronicCorrectionType); jetfindingutilities::findJets(jetFinder, inputParticles, jetPtMin, jetPtMax, jetRadius, jetAreaFractionMin, collision, jetsTable, constituentsTable, fillTHnSparse ? registry.get(HIST("hJet")) : std::shared_ptr(nullptr), fillTHnSparse); } diff --git a/PWGJE/JetFinders/jetFinderHF.h b/PWGJE/JetFinders/jetFinderHF.h index ebe5629f1b5..e0a44e52fee 100644 --- a/PWGJE/JetFinders/jetFinderHF.h +++ b/PWGJE/JetFinders/jetFinderHF.h @@ -65,6 +65,7 @@ struct JetFinderHFTask { o2::framework::Configurable triggerMasks{"triggerMasks", "", "possible JE Trigger masks: fJetChLowPt,fJetChHighPt,fTrackLowPt,fTrackHighPt,fJetD0ChLowPt,fJetD0ChHighPt,fJetLcChLowPt,fJetLcChHighPt,fEMCALReadout,fJetFullHighPt,fJetFullLowPt,fJetNeutralHighPt,fJetNeutralLowPt,fGammaVeryHighPtEMCAL,fGammaVeryHighPtDCAL,fGammaHighPtEMCAL,fGammaHighPtDCAL,fGammaLowPtEMCAL,fGammaLowPtDCAL,fGammaVeryLowPtEMCAL,fGammaVeryLowPtDCAL"}; o2::framework::Configurable skipMBGapEvents{"skipMBGapEvents", true, "decide to run over MB gap events or not"}; o2::framework::Configurable applyRCTSelections{"applyRCTSelections", true, "decide to apply RCT selections"}; + o2::framework::Configurable isEmbedding{"isEmbedding", false, "running on an embedded dataset"}; // track level configurables o2::framework::Configurable trackPtMin{"trackPtMin", 0.15, "minimum track pT"}; @@ -133,11 +134,11 @@ struct JetFinderHFTask { void init(o2::framework::InitContext const&) { - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - particleSelection = static_cast(particleSelections); - clusterDefinition = o2::aod::emcalcluster::getClusterDefinitionFromString(clusterDefinitionS.value); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + particleSelection = particleSelections; + clusterDefinition = o2::aod::emcalcluster::getClusterDefinitionFromString(clusterDefinitionS); jetFinder.etaMin = trackEtaMin; jetFinder.etaMax = trackEtaMax; @@ -167,8 +168,8 @@ struct JetFinderHFTask { if (jetEtaMin < -98.0) { jetFinder.jetEtaDefault = true; } - jetFinder.algorithm = static_cast(static_cast(jetAlgorithm)); - jetFinder.recombScheme = static_cast(static_cast(jetRecombScheme)); + jetFinder.algorithm = static_cast(jetAlgorithm.value); + jetFinder.recombScheme = static_cast(jetRecombScheme.value); jetFinder.ghostArea = jetGhostArea; jetFinder.ghostRepeatN = ghostRepeat; if (DoTriggering) { @@ -176,7 +177,7 @@ struct JetFinderHFTask { } jetFinder.fastjetExtraParam = jetExtraParam; - auto jetRadiiBins = (std::vector)jetRadius; + auto jetRadiiBins = jetRadius.value; if (jetRadiiBins.size() > 1) { jetRadiiBins.push_back(jetRadiiBins[jetRadiiBins.size() - 1] + (TMath::Abs(jetRadiiBins[jetRadiiBins.size() - 1] - jetRadiiBins[jetRadiiBins.size() - 2]))); } else { @@ -239,9 +240,9 @@ struct JetFinderHFTask { } } if constexpr (isEvtWiseSub) { - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, isEmbedding); } else { - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, isEmbedding, &candidate); } jetfindingutilities::findJets(jetFinder, inputParticles, minJetPt, maxJetPt, jetRadius, jetAreaFractionMin, collision, jetsTableInput, constituentsTableInput, registry.get(HIST("hJet")), fillTHnSparse, true); } diff --git a/PWGJE/JetFinders/jetFinderHFHFBar.h b/PWGJE/JetFinders/jetFinderHFHFBar.h index 8bc9506ec55..f37e8bd62b6 100644 --- a/PWGJE/JetFinders/jetFinderHFHFBar.h +++ b/PWGJE/JetFinders/jetFinderHFHFBar.h @@ -65,6 +65,7 @@ struct JetFinderHFHFBarTask { o2::framework::Configurable triggerMasks{"triggerMasks", "", "possible JE Trigger masks: fJetChLowPt,fJetChHighPt,fTrackLowPt,fTrackHighPt,fJetD0ChLowPt,fJetD0ChHighPt,fJetLcChLowPt,fJetLcChHighPt,fEMCALReadout,fJetFullHighPt,fJetFullLowPt,fJetNeutralHighPt,fJetNeutralLowPt,fGammaVeryHighPtEMCAL,fGammaVeryHighPtDCAL,fGammaHighPtEMCAL,fGammaHighPtDCAL,fGammaLowPtEMCAL,fGammaLowPtDCAL,fGammaVeryLowPtEMCAL,fGammaVeryLowPtDCAL"}; o2::framework::Configurable skipMBGapEvents{"skipMBGapEvents", true, "decide to run over MB gap events or not"}; o2::framework::Configurable applyRCTSelections{"applyRCTSelections", true, "decide to apply RCT selections"}; + o2::framework::Configurable isEmbedding{"isEmbedding", false, "running on an embedded dataset"}; // track level configurables o2::framework::Configurable trackPtMin{"trackPtMin", 0.15, "minimum track pT"}; @@ -131,10 +132,10 @@ struct JetFinderHFHFBarTask { void init(o2::framework::InitContext const&) { - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - particleSelection = static_cast(particleSelections); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + particleSelection = particleSelections; jetFinder.etaMin = trackEtaMin; jetFinder.etaMax = trackEtaMax; @@ -164,8 +165,8 @@ struct JetFinderHFHFBarTask { if (jetEtaMin < -98.0) { jetFinder.jetEtaDefault = true; } - jetFinder.algorithm = static_cast(static_cast(jetAlgorithm)); - jetFinder.recombScheme = static_cast(static_cast(jetRecombScheme)); + jetFinder.algorithm = static_cast(jetAlgorithm.value); + jetFinder.recombScheme = static_cast(jetRecombScheme.value); jetFinder.ghostArea = jetGhostArea; jetFinder.ghostRepeatN = ghostRepeat; if (DoTriggering) { @@ -173,7 +174,7 @@ struct JetFinderHFHFBarTask { } jetFinder.fastjetExtraParam = jetExtraParam; - auto jetRadiiBins = (std::vector)jetRadius; + auto jetRadiiBins = jetRadius.value; if (jetRadiiBins.size() > 1) { jetRadiiBins.push_back(jetRadiiBins[jetRadiiBins.size() - 1] + (TMath::Abs(jetRadiiBins[jetRadiiBins.size() - 1] - jetRadiiBins[jetRadiiBins.size() - 2]))); } else { @@ -191,7 +192,7 @@ struct JetFinderHFHFBarTask { registry.add("hJetMCP", "sparse for mcp jets", {o2::framework::HistType::kTHnD, {{jetRadiiBins, ""}, {jetPtBinNumber, jetPtMinDouble, jetPtMaxDouble}, {40, -1.0, 1.0}, {18, 0.0, 7.0}}}); } - o2::aod::EMCALClusterDefinition clusterDefinition = o2::aod::emcalcluster::getClusterDefinitionFromString(clusterDefinitionS.value); + o2::aod::EMCALClusterDefinition clusterDefinition = o2::aod::emcalcluster::getClusterDefinitionFromString(clusterDefinitionS); o2::framework::expressions::Filter collisionFilter = (nabs(o2::aod::jcollision::posZ) < vertexZCut && o2::aod::jcollision::centFT0M >= centralityMin && o2::aod::jcollision::centFT0M < centralityMax && o2::aod::jcollision::trackOccupancyInTimeRange <= trackOccupancyInTimeRangeMax); o2::framework::expressions::Filter mcCollisionFilter = (nabs(o2::aod::jmccollision::posZ) < vertexZCut); o2::framework::expressions::Filter trackCuts = (o2::aod::jtrack::pt >= trackPtMin && o2::aod::jtrack::pt < trackPtMax && o2::aod::jtrack::eta >= trackEtaMin && o2::aod::jtrack::eta <= trackEtaMax && o2::aod::jtrack::phi >= trackPhiMin && o2::aod::jtrack::phi <= trackPhiMax && (phiExclusionMin < -998.0 || (o2::aod::jtrack::phi <= phiExclusionMin || o2::aod::jtrack::phi >= phiExclusionMax))); @@ -244,7 +245,7 @@ struct JetFinderHFHFBarTask { return; } } - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, isEmbedding, &candidate); jetfindingutilities::findJets(jetFinder, inputParticles, minJetPt, maxJetPt, jetRadius, jetAreaFractionMin, collision, jetsTableInput, constituentsTableInput, registry.get(HIST("hJet")), fillTHnSparse, true); } diff --git a/PWGJE/JetFinders/jetFinderV0.h b/PWGJE/JetFinders/jetFinderV0.h index 8d3b9b7f660..29c95e5f5b9 100644 --- a/PWGJE/JetFinders/jetFinderV0.h +++ b/PWGJE/JetFinders/jetFinderV0.h @@ -60,6 +60,7 @@ struct JetFinderV0Task { o2::framework::Configurable triggerMasks{"triggerMasks", "", "possible JE Trigger masks: fJetChLowPt,fJetChHighPt,fTrackLowPt,fTrackHighPt,fJetD0ChLowPt,fJetD0ChHighPt,fJetLcChLowPt,fJetLcChHighPt,fEMCALReadout,fJetFullHighPt,fJetFullLowPt,fJetNeutralHighPt,fJetNeutralLowPt,fGammaVeryHighPtEMCAL,fGammaVeryHighPtDCAL,fGammaHighPtEMCAL,fGammaHighPtDCAL,fGammaLowPtEMCAL,fGammaLowPtDCAL,fGammaVeryLowPtEMCAL,fGammaVeryLowPtDCAL"}; o2::framework::Configurable skipMBGapEvents{"skipMBGapEvents", true, "decide to run over MB gap events or not"}; o2::framework::Configurable applyRCTSelections{"applyRCTSelections", true, "decide to apply RCT selections"}; + o2::framework::Configurable isEmbedding{"isEmbedding", false, "running on an embedded dataset"}; // track level configurables o2::framework::Configurable trackPtMin{"trackPtMin", 0.15, "minimum track pT"}; @@ -115,10 +116,10 @@ struct JetFinderV0Task { void init(o2::framework::InitContext const&) { - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - particleSelection = static_cast(particleSelections); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + particleSelection = particleSelections; jetFinder.etaMin = trackEtaMin; jetFinder.etaMax = trackEtaMax; @@ -148,8 +149,8 @@ struct JetFinderV0Task { if (jetEtaMin < -98.0) { jetFinder.jetEtaDefault = true; } - jetFinder.algorithm = static_cast(static_cast(jetAlgorithm)); - jetFinder.recombScheme = static_cast(static_cast(jetRecombScheme)); + jetFinder.algorithm = static_cast(jetAlgorithm.value); + jetFinder.recombScheme = static_cast(jetRecombScheme.value); jetFinder.ghostArea = jetGhostArea; jetFinder.ghostRepeatN = ghostRepeat; if (DoTriggering) { @@ -164,7 +165,7 @@ struct JetFinderV0Task { candIndex = 1; } - auto jetRadiiBins = (std::vector)jetRadius; + auto jetRadiiBins = jetRadius.value; if (jetRadiiBins.size() > 1) { jetRadiiBins.push_back(jetRadiiBins[jetRadiiBins.size() - 1] + (TMath::Abs(jetRadiiBins[jetRadiiBins.size() - 1] - jetRadiiBins[jetRadiiBins.size() - 2]))); } else { @@ -208,7 +209,7 @@ struct JetFinderV0Task { } } */ - jetfindingutilities::analyseTracksMultipleCandidates(inputParticles, tracks, trackSelection, candidates); + jetfindingutilities::analyseTracksMultipleCandidates(inputParticles, tracks, trackSelection, isEmbedding, candidates); jetfindingutilities::findJets(jetFinder, inputParticles, minJetPt, maxJetPt, jetRadius, jetAreaFractionMin, collision, jetsTableInput, constituentsTableInput, registry.get(HIST("hJet")), fillTHnSparse, saveJetsWithCandidatesOnly); } diff --git a/PWGJE/TableProducer/CMakeLists.txt b/PWGJE/TableProducer/CMakeLists.txt index 06c12902c91..c789aff9d49 100644 --- a/PWGJE/TableProducer/CMakeLists.txt +++ b/PWGJE/TableProducer/CMakeLists.txt @@ -18,6 +18,11 @@ o2physics_add_dpl_workflow(jet-deriveddata-producer PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils O2Physics::SGCutParHolder COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(jet-deriveddata-embedding-producer + SOURCES derivedDataProducerEmbedding.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils O2Physics::SGCutParHolder + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(jet-deriveddata-trigger-producer SOURCES derivedDataTriggerProducer.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore @@ -25,16 +30,19 @@ o2physics_add_dpl_workflow(jet-deriveddata-trigger-producer o2physics_add_dpl_workflow(jet-deriveddata-selector SOURCES derivedDataSelector.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-deriveddata-writer SOURCES derivedDataWriter.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-luminosity-producer SOURCES luminosityProducer.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -45,16 +53,19 @@ o2physics_add_dpl_workflow(jet-luminosity-calculator o2physics_add_dpl_workflow(jet-eventweight-mcd SOURCES jetEventWeightMCD.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-eventweight-mcp SOURCES jetEventWeightMCP.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(mc-outlier-rejector SOURCES mcOutlierRejector.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -65,31 +76,37 @@ o2physics_add_dpl_workflow(jet-track-derived o2physics_add_dpl_workflow(jet-hf-definition SOURCES heavyFlavourDefinition.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-taggerhf SOURCES jetTaggerHF.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(estimator-rho SOURCES rhoEstimator.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(subtractor-eventwiseconstituent SOURCES eventwiseConstituentSubtractor.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-sv-reconstruction SOURCES secondaryVertexReconstruction.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(quark-gluon-jets-producer SOURCES quarkGluonJetsProducer.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -108,16 +125,19 @@ o2physics_add_dpl_workflow(emcal-matchedtracks-writer o2physics_add_dpl_workflow(emcal-cluster-hadronic-correction-task SOURCES emcalClusterHadronicCorrectionTask.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase O2::EMCALBase O2::EMCALReconstruction COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(slim-tables-producer SOURCES slimTablesProducer.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(berkeley-tree-producer SOURCES berkeleyTreeProducer.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) diff --git a/PWGJE/TableProducer/Matching/CMakeLists.txt b/PWGJE/TableProducer/Matching/CMakeLists.txt index a5450dfb05e..3ea0bfb3d33 100644 --- a/PWGJE/TableProducer/Matching/CMakeLists.txt +++ b/PWGJE/TableProducer/Matching/CMakeLists.txt @@ -16,166 +16,205 @@ if(FastJet_FOUND) o2physics_add_dpl_workflow(jet-matching-mc-ch SOURCES jetMatchingMCCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-full SOURCES jetMatchingMCFull.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-neutral SOURCES jetMatchingMCNeutral.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-d0-ch SOURCES jetMatchingMCD0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-dplus-ch SOURCES jetMatchingMCDplusCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-ds-ch SOURCES jetMatchingMCDsCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-dstar-ch SOURCES jetMatchingMCDstarCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-lc-ch SOURCES jetMatchingMCLcCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-b0-ch SOURCES jetMatchingMCB0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-bplus-ch SOURCES jetMatchingMCBplusCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-xictoxipipi-ch SOURCES jetMatchingMCXicToXiPiPiCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-dielectron-ch SOURCES jetMatchingMCDielectronCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-v0-ch SOURCES jetMatchingMCV0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-sub-ch SOURCES jetMatchingMCSubCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-sub-d0-ch SOURCES jetMatchingMCSubD0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-sub-dplus-ch SOURCES jetMatchingMCSubDplusCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-sub-ds-ch SOURCES jetMatchingMCSubDsCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-sub-dstar-ch SOURCES jetMatchingMCSubDstarCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-sub-lc-ch SOURCES jetMatchingMCSubLcCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-sub-b0-ch SOURCES jetMatchingMCSubB0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-sub-bplus-ch SOURCES jetMatchingMCSubBplusCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-sub-xictoxipipi-ch SOURCES jetMatchingMCSubXicToXiPiPiCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-sub-dielectron-ch SOURCES jetMatchingMCSubDielectronCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-sub-ch SOURCES jetMatchingSubCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-sub-d0-ch SOURCES jetMatchingSubD0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-sub-dplus-ch SOURCES jetMatchingSubDplusCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-sub-ds-ch SOURCES jetMatchingSubDsCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-sub-dstar-ch SOURCES jetMatchingSubDstarCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-sub-lc-ch SOURCES jetMatchingSubLcCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-sub-bplus-ch SOURCES jetMatchingSubBplusCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-sub-b0-ch SOURCES jetMatchingSubB0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-sub-xictoxipipi-ch SOURCES jetMatchingSubXicToXiPiPiCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-sub-dielectron-ch SOURCES jetMatchingSubDielectronCharged.cxx + REUSE_FROM JetDataModelPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(jet-matching-mcd-sub-ch + SOURCES jetMatchingMCDSubCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) diff --git a/PWGJE/TableProducer/Matching/Duplicates/CMakeLists.txt b/PWGJE/TableProducer/Matching/Duplicates/CMakeLists.txt index 52a7a0377c9..1b754ba9fb5 100644 --- a/PWGJE/TableProducer/Matching/Duplicates/CMakeLists.txt +++ b/PWGJE/TableProducer/Matching/Duplicates/CMakeLists.txt @@ -13,16 +13,19 @@ if(FastJet_FOUND) o2physics_add_dpl_workflow(jet-matching-data-ch-1 SOURCES jetMatchingDuplicatesChargedData1.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mcd-ch-1 SOURCES jetMatchingDuplicatesChargedMCD1.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mcp-ch-1 SOURCES jetMatchingDuplicatesChargedMCP1.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) diff --git a/PWGJE/TableProducer/Matching/Substructure/CMakeLists.txt b/PWGJE/TableProducer/Matching/Substructure/CMakeLists.txt index 238e84ddc91..8d15ef727c8 100644 --- a/PWGJE/TableProducer/Matching/Substructure/CMakeLists.txt +++ b/PWGJE/TableProducer/Matching/Substructure/CMakeLists.txt @@ -14,101 +14,121 @@ if(FastJet_FOUND) o2physics_add_dpl_workflow(jet-substructure-matching-mc-ch SOURCES jetSubstructureMatchingMCCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-mc-d0-ch SOURCES jetSubstructureMatchingMCD0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-mc-dplus-ch SOURCES jetSubstructureMatchingMCDplusCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-mc-ds-ch SOURCES jetSubstructureMatchingMCDsCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-mc-dstar-ch SOURCES jetSubstructureMatchingMCDstarCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-mc-lc-ch SOURCES jetSubstructureMatchingMCLcCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-mc-b0-ch SOURCES jetSubstructureMatchingMCB0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-mc-bplus-ch SOURCES jetSubstructureMatchingMCBplusCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-mc-xictoxipipi-ch SOURCES jetSubstructureMatchingMCXicToXiPiPiCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-mc-dielectron-ch SOURCES jetSubstructureMatchingMCDielectronCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-sub-ch SOURCES jetSubstructureMatchingSubCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-sub-d0-ch SOURCES jetSubstructureMatchingSubD0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-sub-dplus-ch SOURCES jetSubstructureMatchingSubDplusCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-sub-ds-ch SOURCES jetSubstructureMatchingSubDsCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-sub-dstar-ch SOURCES jetSubstructureMatchingSubDstarCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-sub-lc-ch SOURCES jetSubstructureMatchingSubLcCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-sub-b0-ch SOURCES jetSubstructureMatchingSubB0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-sub-bplus-ch SOURCES jetSubstructureMatchingSubBplusCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-sub-xictoxipipi-ch SOURCES jetSubstructureMatchingSubXicToXiPiPiCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-sub-dielectron-ch SOURCES jetSubstructureMatchingSubDielectronCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) diff --git a/PWGJE/TableProducer/Matching/jetMatchingMCDSubCharged.cxx b/PWGJE/TableProducer/Matching/jetMatchingMCDSubCharged.cxx new file mode 100644 index 00000000000..3a38398b97d --- /dev/null +++ b/PWGJE/TableProducer/Matching/jetMatchingMCDSubCharged.cxx @@ -0,0 +1,45 @@ +// Copyright 2019-2020 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. + +// jet matching mcd subtracted charged task +// +/// \author Nima Zardoshti + +#include "PWGJE/DataModel/Jet.h" +#include "PWGJE/DataModel/JetReducedData.h" +#include "PWGJE/DataModel/JetSubtraction.h" +#include "PWGJE/TableProducer/Matching/jetMatchingSub.h" + +#include +#include +#include +#include +#include + +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +using ChargedJetMatchingSub = JetMatchingSub, + soa::Join, + aod::ChargedMCDetectorLevelJetsMatchedToChargedEventWiseSubtractedJets, + aod::ChargedEventWiseSubtractedJetsMatchedToChargedMCDetectorLevelJets, + aod::JTrackSubs, + aod::JDummys>; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + std::vector tasks; + tasks.emplace_back(adaptAnalysisTask(cfgc, SetDefaultProcesses{}, TaskName{"jet-matching-mcd-sub-ch"})); + return WorkflowSpec{tasks}; +} diff --git a/PWGJE/TableProducer/berkeleyTreeProducer.cxx b/PWGJE/TableProducer/berkeleyTreeProducer.cxx index fe49ffdef91..669ba295b7f 100644 --- a/PWGJE/TableProducer/berkeleyTreeProducer.cxx +++ b/PWGJE/TableProducer/berkeleyTreeProducer.cxx @@ -39,6 +39,7 @@ namespace o2::aod { namespace berkeleytree { +DECLARE_SOA_COLUMN(RunNumber, runNumber, int); DECLARE_SOA_COLUMN(VtxZ, vtxZ, float); DECLARE_SOA_COLUMN(Weight, weight, float); DECLARE_SOA_COLUMN(PtHat, ptHat, float); @@ -63,6 +64,7 @@ DECLARE_SOA_COLUMN(PdgId, pdgId, std::vector); } // namespace berkeleytree DECLARE_SOA_TABLE(BerkeleyTree, "AOD", "BERKELEYTREE", + berkeleytree::RunNumber, berkeleytree::VtxZ, berkeleytree::Weight, berkeleytree::PtHat, @@ -133,7 +135,7 @@ struct BerkeleyTreeProducer { } using JetParticlesWithOriginal = soa::Join; - void processMCJJ(aod::JetCollisionsMCD::iterator const& collision, aod::JetTracksMCD const& tracks, JetParticlesWithOriginal const& mcParticles, aod::JetMcCollisions const&) + void processMCJJ(aod::JetCollisionsMCD::iterator const& collision, aod::JetTracksMCD const& tracks, JetParticlesWithOriginal const& mcParticles, aod::JetMcCollisions const&, aod::JBCs const&) { // do not do any RCT selections, will be done on analysis level if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, false, "", false, false)) @@ -143,6 +145,7 @@ struct BerkeleyTreeProducer { float weight = collision.has_mcCollision() ? collision.mcCollision().weight() : 1.f; float pthat = collision.has_mcCollision() ? collision.mcCollision().ptHard() : 1.f; + int runNumber = collision.bc_as().runNumber(); std::vector detPt, detEta, detPhi; std::vector detTrackSel; @@ -196,7 +199,7 @@ struct BerkeleyTreeProducer { } } - tree(collision.posZ(), weight, pthat, collision.multFT0C(), collision.eventSel(), collision.trackOccupancyInTimeRange(), collision.rct_raw(), detPt, detEta, detPhi, detTrackSel, detMcId, genPt, genEta, genPhi, genE, genCharge, genMcId, pdgId); + tree(runNumber, collision.posZ(), weight, pthat, collision.multFT0C(), collision.eventSel(), collision.trackOccupancyInTimeRange(), collision.rct_raw(), detPt, detEta, detPhi, detTrackSel, detMcId, genPt, genEta, genPhi, genE, genCharge, genMcId, pdgId); } PROCESS_SWITCH(BerkeleyTreeProducer, processMCJJ, "MC processing for JJ simulations", false); diff --git a/PWGJE/TableProducer/derivedDataProducer.cxx b/PWGJE/TableProducer/derivedDataProducer.cxx index 80bb8fecb6a..f4cac2e7ac4 100644 --- a/PWGJE/TableProducer/derivedDataProducer.cxx +++ b/PWGJE/TableProducer/derivedDataProducer.cxx @@ -298,7 +298,7 @@ struct JetDerivedDataProducerTask { } PROCESS_SWITCH(JetDerivedDataProducerTask, processBunchCrossingsWithoutSels, "produces derived bunch crossing table with bunch crossing selections", false); - void processCollisions(soa::Join::iterator const& collision, soa::Join const&, aod::FT0s const&, aod::FV0As const&, aod::FDDs const&) + void processCollisions(soa::Join::iterator const& collision, soa::Join const&) { auto bc = collision.bc_as>(); if (config.includeHadronicRate) { diff --git a/PWGJE/TableProducer/derivedDataProducerEmbedding.cxx b/PWGJE/TableProducer/derivedDataProducerEmbedding.cxx new file mode 100644 index 00000000000..e093d61b686 --- /dev/null +++ b/PWGJE/TableProducer/derivedDataProducerEmbedding.cxx @@ -0,0 +1,1252 @@ +// Copyright 2019-2020 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. + +// task to produce a self contained data format for jet analyses from the full AO2D +// +/// \author Nima Zardoshti + +// #include "PWGJE/Core/JetDQUtilities.h" +#include "PWGJE/Core/JetDerivedDataUtilities.h" +// #include "PWGJE/Core/JetV0Utilities.h" +#include "PWGJE/DataModel/EMCALClusters.h" +#include "PWGJE/DataModel/EMCALMatchedCollisions.h" +#include "PWGJE/DataModel/JetReducedData.h" +#include "PWGJE/DataModel/JetReducedDataDQ.h" +#include "PWGJE/DataModel/JetReducedDataHF.h" +#include "PWGJE/DataModel/JetReducedDataV0.h" +// +// #include "PWGDQ/DataModel/ReducedInfoTables.h" +// #include "PWGHF/DataModel/DerivedTables.h" +#include "PWGHF/Utils/utilsBfieldCCDB.h" +// #include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGLF/DataModel/mcCentrality.h" +#include "PWGUD/Core/SGCutParHolder.h" +#include "PWGUD/Core/SGSelector.h" +#include "PWGUD/Core/UDHelpers.h" + +#include "Common/CCDB/ctpRateFetcher.h" +// #include "Common/Core/RecoDecay.h" +#include "Common/Core/Zorro.h" +#include "Common/Core/trackUtilities.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/CollisionAssociationTables.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +namespace o2::aod +{ +// O2ORIGIN("EMB"); +// template +// using McCollisionsFrom = McCollisions_001From; +// template +// using CollisionsFrom = Collisions_001From; +// template +// using TracksExtraFrom = TracksExtra_002From; +// template +// using McParticlesFrom = McParticles_001From; +// template +// using MultsExtraMCFrom = MultMCExtras_001From; +} // namespace o2::aod + +struct JetDerivedDataEmbeddingProducerTask { + struct : ProducesGroup { + Produces bcCountsTable; + Produces collisionCountsTable; + Produces jDummysTable; + Produces jBCsTable; + Produces jBCParentIndexTable; + Produces jCollisionsTable; + Produces jCollisionUPCsTable; + Produces jCollisionMcInfosTable; + Produces jCollisionsParentIndexTable; + Produces jCollisionsEMCalLabelTable; + Produces jMcCollisionsLabelTable; + Produces jMcCollisionsTable; + Produces jMcCollisionsParentIndexTable; + Produces jTracksTable; + Produces jTracksExtraTable; + Produces jTracksEMCalTable; + Produces jTracksParentIndexTable; + Produces jMcTracksLabelTable; + Produces jMcParticlesTable; + Produces jParticlesParentIndexTable; + Produces jClustersTable; + Produces jClustersParentIndexTable; + Produces jClustersMatchedTracksTable; + Produces jMcClustersLabelTable; + Produces jD0CollisionIdsTable; + Produces jD0McCollisionIdsTable; + Produces jD0IdsTable; + Produces jD0ParticleIdsTable; + Produces jDplusCollisionIdsTable; + Produces jDplusMcCollisionIdsTable; + Produces jDplusIdsTable; + Produces jDplusParticleIdsTable; + Produces jDsCollisionIdsTable; + Produces jDsMcCollisionIdsTable; + Produces jDsIdsTable; + Produces jDsParticleIdsTable; + Produces jDstarCollisionIdsTable; + Produces jDstarMcCollisionIdsTable; + Produces jDstarIdsTable; + Produces jDstarParticleIdsTable; + Produces jLcCollisionIdsTable; + Produces jLcMcCollisionIdsTable; + Produces jLcIdsTable; + Produces jLcParticleIdsTable; + Produces jB0CollisionIdsTable; + Produces jB0McCollisionIdsTable; + Produces jB0IdsTable; + Produces jB0ParticleIdsTable; + Produces jBplusCollisionIdsTable; + Produces jBplusMcCollisionIdsTable; + Produces jBplusIdsTable; + Produces jBplusParticleIdsTable; + Produces jXicToXiPiPiCollisionIdsTable; + Produces jXicToXiPiPiMcCollisionIdsTable; + Produces jXicToXiPiPiIdsTable; + Produces jXicToXiPiPiParticleIdsTable; + Produces jV0IdsTable; + Produces jV0McCollisionsTable; + Produces jV0McCollisionIdsTable; + Produces jV0McsTable; + Produces jV0McIdsTable; + Produces jDielectronCollisionIdsTable; + Produces jDielectronIdsTable; + Produces jDielectronMcCollisionsTable; + Produces jDielectronMcCollisionIdsTable; + Produces JDielectronMcRCollDummysTable; + Produces jDielectronMcsTable; + Produces jDielectronMcIdsTable; + + Produces>> jMcCollisionsLabelMCDTable; + Produces>> jCollisionMcInfosMCDTable; + } products; + + struct : ConfigurableGroup { + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable ccdbPathLut{"ccdbPathLut", "GLO/Param/MatLUT", "Path for LUT parametrization"}; + Configurable ccdbPathGrp{"ccdbPathGrp", "GLO/GRP/GRP", "Path of the grp file (Run 2)"}; + Configurable ccdbPathGrpMag{"ccdbPathGrpMag", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object (Run 3)"}; + Configurable dcaZMax{"dcaZMax", 0.2, "maximum DCAZ selection for tracks - only applied for reassociation"}; + Configurable upcBCRangeTimeWindow{"upcBCRangeTimeWindow", 1000, "time range in ns for bunch crossing range checking in upc gap determination"}; + Configurable upcMinNBCs{"upcMinNBCs", 7, "minuimum number of bunch crossing to check in upc gap determination"}; + Configurable upcMinNTracks{"upcMinNTracks", 0, "minuimum number of tracks in upc event"}; + Configurable upcMaxNTracks{"upcMaxNTracks", 100, "maximum number of tracks in upc event"}; + Configurable upcMaxFITTime{"upcMaxFITTime", 4.0, "maximum fit time in upc event"}; + Configurable upcMaxFV0AAmplitude{"upcMaxFV0AAmplitude", 100.0, "maximum amplitude for FV0A in upc event"}; + Configurable upcMaxFT0AAmplitude{"upcMaxFT0AAmplitude", 100.0, "maximum amplitude for FT0A in upc event"}; + Configurable upcMaxFT0CAmplitude{"upcMaxFT0CAmplitude", 50.0, "maximum amplitude for FT0C in upc event"}; + Configurable upcMaxFDDAAmplitude{"upcMaxFDDAAmplitude", -1.0, "maximum amplitude for FDDA in upc event"}; + Configurable upcMaxFDDCAmplitude{"upcMaxFDDCAmplitude", -1.0, "maximum amplitude for FDDC in upc event"}; + + Configurable includeTriggers{"includeTriggers", false, "fill the collision information with software trigger decisions"}; + Configurable includeHadronicRate{"includeHadronicRate", true, "fill the collision information with the hadronic rate"}; + Configurable v0ChargedDecaysOnly{"v0ChargedDecaysOnly", true, "store V0s (at particle-level) only if they decay to charged particles"}; + Configurable isMCGenOnly{"isMCGenOnly", false, "analysis is run over mcGen only"}; + + o2::framework::Configurable applyTrackingEfficiency{"applyTrackingEfficiency", {false}, "configurable to decide whether to apply artificial tracking efficiency (discarding tracks) in jet finding"}; + o2::framework::Configurable> trackingEfficiencyPtBinning{"trackingEfficiencyPtBinning", {0., 10, 999.}, "pt binning of tracking efficiency array if applyTrackingEfficiency is true"}; + o2::framework::Configurable> trackingEfficiency{"trackingEfficiency", {1.0, 1.0}, "tracking efficiency array applied to jet finding if applyTrackingEfficiency is true"}; + } config; + + struct : PresliceGroup { + Preslice perClusterCells = aod::emcalclustercell::emcalclusterId; + Preslice perClusterTracks = aod::emcalclustercell::emcalclusterId; + Preslice perCollisionTrackIndices = aod::track_association::collisionId; + Preslice perBCCollisions = aod::collision::bcId; + Preslice>> permcdBCCollisions = aod::collision::bcId; + Preslice> perCollisionTracks = aod::track::collisionId; + Preslice>, aod::TracksExtraFrom>, aod::TracksCovFrom>, aod::TracksDCAFrom>, aod::TracksDCACovFrom>, aod::TrackSelectionFrom>, aod::TrackSelectionExtensionFrom>, aod::McTrackLabelsFrom>>> perCollisionTracksMCD = aod::track::collisionId; + } preslices; + + std::map, int32_t> trackCollisionMapping; + std::map, int32_t> trackCollisionMCDMapping; + std::vector collisionMapping; + std::vector collisionTomcdCollisionMapping; + std::vector mcdcollisionToCollisionMapping; + Service ccdb; + o2::base::MatLayerCylSet* lut; + o2::base::Propagator::MatCorrType noMatCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; + Service pdgDatabase; + Zorro triggerDecider; + + SGCutParHolder upcCuts; + SGSelector upcSelector; + std::vector amplitudesFV0; + std::vector amplitudesFT0A; + std::vector amplitudesFT0C; + std::vector amplitudesFDDA; + std::vector amplitudesFDDC; + + std::vector trackMCSelection; + + std::vector bcSelMapping; + std::vector bcRctMapping; + + ctpRateFetcher rateFetcher; + int runNumber; + float hadronicRate; + bool withCollisionAssociator; + TRandom3 trackingEfficiencyRandomNumber; + + void init(InitContext const&) + { + hadronicRate = -1.0; + if (doprocessTracksWithCollisionAssociator || config.includeHadronicRate || config.includeTriggers) { + ccdb->setURL(config.ccdbUrl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + runNumber = 0; + if (doprocessTracksWithCollisionAssociator) { + withCollisionAssociator = true; + lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->get(config.ccdbPathLut)); + } else { + withCollisionAssociator = false; + } + } + + upcCuts.SetNDtcoll(config.upcBCRangeTimeWindow); + upcCuts.SetMinNBCs(config.upcMinNBCs); + upcCuts.SetNTracks(config.upcMinNTracks, config.upcMaxNTracks); + upcCuts.SetMaxFITtime(config.upcMaxFITTime); + upcCuts.SetFITAmpLimits({config.upcMaxFV0AAmplitude, config.upcMaxFT0AAmplitude, config.upcMaxFT0CAmplitude, config.upcMaxFDDAAmplitude, config.upcMaxFDDCAmplitude}); + + if (config.applyTrackingEfficiency) { + trackingEfficiencyRandomNumber.SetSeed(0); + if (config.trackingEfficiencyPtBinning->size() < 2) { + LOGP(fatal, "jetFinder workflow: trackingEfficiencyPtBinning configurable should have at least two bin edges"); + } + if (config.trackingEfficiency->size() + 1 != config.trackingEfficiencyPtBinning->size()) { + LOGP(fatal, "jetFinder workflow: trackingEfficiency configurable should have exactly one less entry than the number of bin edges set in trackingEfficiencyPtBinning configurable"); + } + } + } + + void processClearMaps(aod::Collisions const& collisions, aod::CollisionsFrom> const& mcdCollisions, aod::BCs const& bcs, aod::BCsFrom> const& mcdBCs, aod::TracksFrom> const& mcTracks) + { + trackCollisionMapping.clear(); + trackCollisionMCDMapping.clear(); + trackMCSelection.clear(); + trackMCSelection.resize(mcTracks.size(), true); + + collisionTomcdCollisionMapping.clear(); + collisionTomcdCollisionMapping.resize(collisions.size(), -1); + for (const auto& collision : collisions) { + auto const& colisionBC = collision.bc_as(); + auto const& globalBC = colisionBC.globalBC(); + for (auto const& mcdBC : mcdBCs) { + if (mcdBC.globalBC() == globalBC) { + const auto bcCollisions = mcdCollisions.sliceBy(preslices.permcdBCCollisions, mcdBC.globalIndex()); + for (auto const& bcCollision : bcCollisions) { + collisionTomcdCollisionMapping[collision.globalIndex()] = bcCollision.globalIndex(); // only takes the first collision. If multiple collisions are associated to the same BC then it skips the next ones + break; + } + break; + } + } + } + + mcdcollisionToCollisionMapping.clear(); + mcdcollisionToCollisionMapping.resize(mcdCollisions.size(), -1); + for (const auto& mcdCollision : mcdCollisions) { + auto const& mcdCollisionBC = mcdCollision.bc_as>>(); + auto const& mcdGlobalBC = mcdCollisionBC.globalBC(); + for (auto const& bc : bcs) { + if (bc.globalBC() == mcdGlobalBC) { + const auto bcCollisions = collisions.sliceBy(preslices.perBCCollisions, bc.globalIndex()); + for (auto const& bcCollision : bcCollisions) { + mcdcollisionToCollisionMapping[mcdCollision.globalIndex()] = bcCollision.globalIndex(); + break; + } + break; + } + } + } + + if (config.applyTrackingEfficiency) { + std::vector trackingEfficiencyPtBinningVec = config.trackingEfficiencyPtBinning; + std::vector trackingEfficiencyVec = config.trackingEfficiency; + for (const auto& mcTrack : mcTracks) { + auto iter = std::upper_bound(trackingEfficiencyPtBinningVec.begin(), trackingEfficiencyPtBinningVec.end(), mcTrack.pt()); + if (iter != trackingEfficiencyPtBinningVec.begin() && iter != trackingEfficiencyPtBinningVec.end()) { + std::size_t index = std::distance(trackingEfficiencyPtBinningVec.begin(), iter) - 1; + if (trackingEfficiencyRandomNumber.Rndm() > trackingEfficiencyVec[index]) { + trackMCSelection[mcTrack.globalIndex()] = false; + } + } + } + } + if (!doprocessEMCalCollisionLabels) { + for (int i = 0; i < collisions.size(); i++) { // check if this is the correct collision loop + products.jCollisionsEMCalLabelTable(false, false); + } + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processClearMaps, "clears all maps", true); + + void processBunchCrossings(soa::Join const& bcs, aod::BCsFrom> const& mcBCs) + { + bcSelMapping.clear(); + bcSelMapping.resize(mcBCs.size(), ~uint16_t{0}); + bcRctMapping.clear(); + bcRctMapping.resize(mcBCs.size(), ~uint32_t{0}); + for (const auto& bc : bcs) { + products.jBCsTable(bc.runNumber(), bc.globalBC(), bc.triggerMask(), bc.timestamp(), bc.alias_raw(), bc.selection_raw(), bc.rct_raw()); + products.jBCParentIndexTable(bc.globalIndex()); + for (const auto& mcBC : mcBCs) { + if (mcBC.globalBC() == bc.globalBC()) { + bcSelMapping[mcBC.globalIndex()] = jetderiveddatautilities::setMCEventSelectionBit(bc); // note here we are setting the selection on the bc for the MC to the one in data. Is this correct? + bcRctMapping[mcBC.globalIndex()] = bc.rct_raw(); + } + } + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processBunchCrossings, "produces derived bunch crossing table", true); + + void processBunchCrossingsWithoutSels(soa::Join const& bcs, aod::BCsFrom> const& mcBCs) + { + bcSelMapping.clear(); + bcSelMapping.resize(mcBCs.size(), ~uint16_t{0}); + bcRctMapping.clear(); + bcRctMapping.resize(mcBCs.size(), ~uint32_t{0}); + for (const auto& bc : bcs) { + products.jBCsTable(bc.runNumber(), bc.globalBC(), bc.triggerMask(), bc.timestamp(), ~uint32_t{0}, ~uint64_t{0}, ~uint32_t{0}); + products.jBCParentIndexTable(bc.globalIndex()); + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processBunchCrossingsWithoutSels, "produces derived bunch crossing table with bunch crossing selections", false); + + void processCollisions(soa::Join::iterator const& collision, soa::Join const&) + { + // shall we skip collisions without an associated detector level collision to embed? + auto bc = collision.bc_as>(); + if (config.includeHadronicRate) { + if (runNumber != bc.runNumber()) { + runNumber = bc.runNumber(); + hadronicRate = rateFetcher.fetch(ccdb.service, bc.timestamp(), runNumber, "ZNC hadronic") * 0.001; + } + } + uint64_t triggerBit = 0; + if (config.includeTriggers) { + triggerDecider.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), jetderiveddatautilities::JTriggerMasks); + triggerBit = jetderiveddatautilities::setTriggerSelectionBit(triggerDecider.getTriggerOfInterestResults(bc.globalBC())); + } + + int upcGapResult = o2::aod::sgselector::NoGap; + + products.jCollisionsTable(collision.bcId(), collision.posX(), collision.posY(), collision.posZ(), collision.collisionTime(), collision.multFV0A(), collision.multFV0C(), collision.multFT0A(), collision.multFT0C(), collision.centFV0A(), -1.0, collision.centFT0A(), collision.centFT0C(), collision.centFT0M(), collision.centFT0CVariant1(), hadronicRate, collision.trackOccupancyInTimeRange(), collision.alias_raw(), jetderiveddatautilities::setEventSelectionBit(collision, upcGapResult), collision.rct_raw(), triggerBit); // note change multFT0C to multFT0M when problems with multFT0A are fixed + products.jCollisionsParentIndexTable(collision.globalIndex()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processCollisions, "produces derived collision tables", true); + + void processCollisionsUPC(soa::Join::iterator const& collision, soa::Join const& bcs, aod::FT0s const&, aod::FV0As const&, aod::FDDs const&, aod::Zdcs const&) + { + // shall we skip collisions without an associated detector level collision to embed? + auto bc = collision.bc_as>(); + if (config.includeHadronicRate) { + if (runNumber != bc.runNumber()) { + runNumber = bc.runNumber(); + hadronicRate = rateFetcher.fetch(ccdb.service, bc.timestamp(), runNumber, "ZNC hadronic") * 0.001; + } + } + uint64_t triggerBit = 0; + if (config.includeTriggers) { + triggerDecider.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), jetderiveddatautilities::JTriggerMasks); + triggerBit = jetderiveddatautilities::setTriggerSelectionBit(triggerDecider.getTriggerOfInterestResults(bc.globalBC())); + } + + int upcGapResult = o2::aod::sgselector::NoGap; + amplitudesFV0.clear(); + amplitudesFT0A.clear(); + amplitudesFT0C.clear(); + amplitudesFDDA.clear(); + amplitudesFDDC.clear(); + if (collision.has_foundBC()) { + auto const upcBC = collision.foundBC_as>(); + auto const upcBCRange = udhelpers::compatibleBCs(collision, upcCuts.NDtcoll(), bcs, upcCuts.minNBCs()); + auto const upcResult = upcSelector.IsSelected(upcCuts, collision, upcBCRange, upcBC, &litudesFV0, &litudesFT0A, &litudesFT0C, &litudesFDDA, &litudesFDDC); + upcGapResult = upcResult.value; + if (upcGapResult != o2::aod::sgselector::SingleGapA && upcGapResult != o2::aod::sgselector::SingleGapC && upcGapResult != o2::aod::sgselector::DoubleGap) { + amplitudesFV0.clear(); + amplitudesFT0A.clear(); + amplitudesFT0C.clear(); + amplitudesFDDA.clear(); + amplitudesFDDC.clear(); + } + } + float energyCommonZNA = -1.0; + float energyCommonZNC = -1.0; + float timeZNA = -1.0; + float timeZNC = -1.0; + if (bc.has_zdc()) { + auto const& zdc = bc.zdc_as(); + energyCommonZNA = zdc.energyCommonZNA(); + energyCommonZNC = zdc.energyCommonZNC(); + timeZNA = zdc.timeZNA(); + timeZNC = zdc.timeZNC(); + } + products.jCollisionUPCsTable(amplitudesFV0, amplitudesFT0A, amplitudesFT0C, amplitudesFDDA, amplitudesFDDC, energyCommonZNA, energyCommonZNC, timeZNA, timeZNC); + + products.jCollisionsTable(collision.bcId(), collision.posX(), collision.posY(), collision.posZ(), collision.collisionTime(), collision.multFV0A(), collision.multFV0C(), collision.multFT0A(), collision.multFT0C(), collision.centFV0A(), -1.0, collision.centFT0A(), collision.centFT0C(), collision.centFT0M(), collision.centFT0CVariant1(), hadronicRate, collision.trackOccupancyInTimeRange(), collision.alias_raw(), jetderiveddatautilities::setEventSelectionBit(collision, upcGapResult), collision.rct_raw(), triggerBit); // note change multFT0C to multFT0M when problems with multFT0A are fixed + products.jCollisionsParentIndexTable(collision.globalIndex()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processCollisionsUPC, "produces derived collision tables with UPC information", false); + + void processCollisionsWithoutCentralityAndMultiplicity(soa::Join::iterator const& collision, soa::Join const&) + { + uint64_t triggerBit = 0; + if (config.includeTriggers) { + auto bc = collision.bc_as>(); + triggerDecider.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), jetderiveddatautilities::JTriggerMasks); + triggerBit = jetderiveddatautilities::setTriggerSelectionBit(triggerDecider.getTriggerOfInterestResults(bc.globalBC())); + } + products.jCollisionsTable(collision.bcId(), collision.posX(), collision.posY(), collision.posZ(), collision.collisionTime(), -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1, collision.alias_raw(), jetderiveddatautilities::setEventSelectionBit(collision), collision.rct_raw(), triggerBit); + products.jCollisionsParentIndexTable(collision.globalIndex()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processCollisionsWithoutCentralityAndMultiplicity, "produces derived collision tables without centrality or multiplicity", false); + + void processCollisionsRun2(soa::Join::iterator const& collision) + { + products.jCollisionsTable(collision.bcId(), collision.posX(), collision.posY(), collision.posZ(), -1.0, -1.0, -1.0, -1.0, -1.0, collision.centRun2V0A(), collision.centRun2V0M(), -1.0, -1.0, -1.0, -1.0, -1.0, -1, collision.alias_raw(), jetderiveddatautilities::setEventSelectionBit(collision), collision.rct_raw(), 0); // note change multFT0C to multFT0M when problems with multFT0A are fixed + products.jCollisionsParentIndexTable(collision.globalIndex()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processCollisionsRun2, "produces derived collision tables for Run 2 data", false); + + void processCollisionsALICE3(aod::Collision const& collision) + { + products.jCollisionsTable(collision.bcId(), collision.posX(), collision.posY(), collision.posZ(), -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1, -1.0, 0, 0, 0); + products.jCollisionsParentIndexTable(collision.globalIndex()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processCollisionsALICE3, "produces derived collision tables for ALICE 3 simulations", false); + + void processEMCalCollisionLabels(aod::EMCALMatchedCollision const& collision) + { + products.jCollisionsEMCalLabelTable(collision.ambiguous(), collision.isemcreadout()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processEMCalCollisionLabels, "produces derived collision labels for EMCal", false); + + void processMcCollisionLabels(soa::Join>, aod::McCollisionLabelsFrom>> const& mcdCollisions, aod::Collisions const& collisions, aod::McCollisionsFrom> const&) + { + for (auto const& mcdCollision : mcdCollisions) { + products.jMcCollisionsLabelMCDTable(mcdCollision.mcCollisionId()); // collision.mcCollisionId() returns -1 if collision has no associated mcCollision + if (mcdCollision.has_mcCollision()) { + auto const& mcCollision = mcdCollision.mcCollision_as>>(); + products.jCollisionMcInfosMCDTable(mcCollision.weight(), mcCollision.getSubGeneratorId()); + } else { + products.jCollisionMcInfosMCDTable(0.0, jetderiveddatautilities::JCollisionSubGeneratorId::none); + } + } + + for (auto const& collision : collisions) { + int32_t mcdCollisionId = collisionTomcdCollisionMapping[collision.globalIndex()]; + if (mcdCollisionId >= 0) { + auto const& mcdCollision = mcdCollisions.iteratorAt(mcdCollisionId); // is this correct????!!!!! + products.jMcCollisionsLabelTable(mcdCollision.mcCollisionId()); // collision.mcCollisionId() returns -1 if collision has no associated mcCollision + if (mcdCollision.has_mcCollision()) { + auto const& mcCollision = mcdCollision.mcCollision_as>>(); + products.jCollisionMcInfosTable(mcCollision.weight(), mcCollision.getSubGeneratorId()); + } else { + products.jCollisionMcInfosTable(0.0, jetderiveddatautilities::JCollisionSubGeneratorId::none); + } + } else { + products.jMcCollisionsLabelTable(-1); + products.jCollisionMcInfosTable(0.0, jetderiveddatautilities::JCollisionSubGeneratorId::none); + } + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processMcCollisionLabels, "produces derived MC collision labels table", true); + + void processMcCollisions(soa::Join>, aod::HepMCXSectionsFrom>, aod::MultsExtraMCFrom>, aod::McCentFT0MsFrom>>::iterator const& mcCollision) + { + uint32_t selDecision = ~uint16_t{0}; + uint32_t rctDecision = ~uint32_t{0}; + int32_t bcId = -1; + if (!config.isMCGenOnly) { + bcId = mcCollision.bcId(); + selDecision = bcSelMapping[bcId]; + rctDecision = bcRctMapping[bcId]; + } else { + products.jBCsTable(-1, -1, -1, -1, ~uint32_t{0}, ~uint32_t{0}, ~uint32_t{0}); + bcId = products.jBCsTable.lastIndex(); + } + products.jMcCollisionsTable(bcId, mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), mcCollision.multMCFV0A(), mcCollision.multMCFT0A(), mcCollision.multMCFT0C(), mcCollision.centFT0M(), mcCollision.weight(), mcCollision.accepted(), mcCollision.attempted(), mcCollision.xsectGen(), mcCollision.xsectErr(), mcCollision.ptHard(), selDecision, rctDecision, mcCollision.getGeneratorId(), mcCollision.getSubGeneratorId(), mcCollision.getSourceId(), mcCollision.impactParameter(), mcCollision.eventPlaneAngle()); + products.jMcCollisionsParentIndexTable(mcCollision.globalIndex()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processMcCollisions, "produces derived MC collision table", true); + + void processMcCollisionsWithoutCentralityAndMultiplicity(soa::Join>, aod::HepMCXSectionsFrom>>::iterator const& mcCollision) + { + + uint32_t selDecision = ~uint16_t{0}; + uint32_t rctDecision = ~uint32_t{0}; + int32_t bcId = -1; + if (!config.isMCGenOnly) { + bcId = mcCollision.bcId(); + selDecision = bcSelMapping[bcId]; + rctDecision = bcRctMapping[bcId]; + } else { + products.jBCsTable(-1, -1, -1, -1, ~uint32_t{0}, ~uint32_t{0}, ~uint32_t{0}); + bcId = products.jBCsTable.lastIndex(); + } + products.jMcCollisionsTable(bcId, mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), -1.0, -1.0, -1.0, -1.0, mcCollision.weight(), mcCollision.accepted(), mcCollision.attempted(), mcCollision.xsectGen(), mcCollision.xsectErr(), mcCollision.ptHard(), selDecision, rctDecision, mcCollision.getGeneratorId(), mcCollision.getSubGeneratorId(), mcCollision.getSourceId(), mcCollision.impactParameter(), mcCollision.eventPlaneAngle()); + products.jMcCollisionsParentIndexTable(mcCollision.globalIndex()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processMcCollisionsWithoutCentralityAndMultiplicity, "produces derived MC collision table without centraility and multiplicity", false); + + void processMcCollisionsWithoutXsection(soa::Join>, aod::MultsExtraMCFrom>, aod::McCentFT0MsFrom>>::iterator const& mcCollision) + { + uint32_t selDecision = ~uint16_t{0}; + uint32_t rctDecision = ~uint32_t{0}; + int32_t bcId = -1; + if (!config.isMCGenOnly) { + bcId = mcCollision.bcId(); + selDecision = bcSelMapping[bcId]; + rctDecision = bcRctMapping[bcId]; + } else { + products.jBCsTable(-1, -1, -1, -1, ~uint32_t{0}, ~uint32_t{0}, ~uint32_t{0}); + bcId = products.jBCsTable.lastIndex(); + } + products.jMcCollisionsTable(bcId, mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), mcCollision.multMCFV0A(), mcCollision.multMCFT0A(), mcCollision.multMCFT0C(), mcCollision.centFT0M(), mcCollision.weight(), 1, 1, 1.0, 1.0, 999.0, selDecision, rctDecision, mcCollision.getGeneratorId(), mcCollision.getSubGeneratorId(), mcCollision.getSourceId(), mcCollision.impactParameter(), mcCollision.eventPlaneAngle()); + products.jMcCollisionsParentIndexTable(mcCollision.globalIndex()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processMcCollisionsWithoutXsection, "produces derived MC collision table without cross section information", false); + + void processMcCollisionsWithoutCentralityAndMultiplicityAndXsection(aod::McCollisionsFrom>::iterator const& mcCollision) + { + uint32_t selDecision = ~uint16_t{0}; + uint32_t rctDecision = ~uint32_t{0}; + int32_t bcId = -1; + if (!config.isMCGenOnly) { + bcId = mcCollision.bcId(); + selDecision = bcSelMapping[bcId]; + rctDecision = bcRctMapping[bcId]; + } else { + products.jBCsTable(-1, -1, -1, -1, ~uint32_t{0}, ~uint32_t{0}, ~uint32_t{0}); + bcId = products.jBCsTable.lastIndex(); + } + products.jMcCollisionsTable(bcId, mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), -1.0, -1.0, -1.0, -1.0, mcCollision.weight(), 1, 1, 1.0, 1.0, 999.0, selDecision, rctDecision, mcCollision.getGeneratorId(), mcCollision.getSubGeneratorId(), mcCollision.getSourceId(), mcCollision.impactParameter(), mcCollision.eventPlaneAngle()); + products.jMcCollisionsParentIndexTable(mcCollision.globalIndex()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processMcCollisionsWithoutCentralityAndMultiplicityAndXsection, "produces derived MC collision table without centrality, multiplicity and cross section information", false); + + void processTrackSelectionForWeightedMC(soa::Join>, aod::McTrackLabelsFrom>> const& tracks, soa::Join>, aod::McCollisionLabelsFrom>> const&, aod::McCollisionsFrom> const& mcCollisions, aod::McParticlesFrom> const&) + { + bool hasMBGap = false; + for (auto const& mcCollision : mcCollisions) { + if (mcCollision.getSubGeneratorId() == jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { + hasMBGap = true; + break; + } + } + if (hasMBGap) { + for (auto const& track : tracks) { + if (track.has_collision()) { + auto const& trackCollision = track.collision_as>, aod::McCollisionLabelsFrom>>>(); + if (track.has_mcParticle() && trackCollision.has_mcCollision()) { + auto const& trackMcCollision = trackCollision.mcCollision_as>>(); + auto const& particleMcCollision = track.mcParticle().mcCollision_as>>(); + if (trackMcCollision.globalIndex() != particleMcCollision.globalIndex() && trackMcCollision.getSubGeneratorId() != jetderiveddatautilities::JCollisionSubGeneratorId::mbGap && particleMcCollision.getSubGeneratorId() != jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { + trackMCSelection[track.globalIndex()] = false; + } + } + } + } + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processTrackSelectionForWeightedMC, "select whether tracks should be dropped in weighted productions", false); + + void processTracks(aod::Collisions const& collisions, soa::Join const& tracks, soa::Join>, aod::TracksExtraFrom>, aod::TracksCovFrom>, aod::TracksDCAFrom>, aod::TracksDCACovFrom>, aod::TrackSelectionFrom>, aod::TrackSelectionExtensionFrom>, aod::McTrackLabelsFrom>> const& tracksMCD, aod::CollisionsFrom> const& collisionsMCD) // need to check where in the framework assumes that the JTracks are always indexed the same as tracks + { + + std::vector mcCollisionSelected(collisionsMCD.size(), false); + for (auto const& collision : collisions) { + auto collisionTrakcs = tracks.sliceBy(preslices.perCollisionTracks, collision.globalIndex()); + for (auto const& track : collisionTrakcs) { + + products.jTracksTable(track.collisionId(), track.pt(), track.eta(), track.phi(), jetderiveddatautilities::setTrackSelectionBit(track, track.dcaZ(), config.dcaZMax, true)); + auto trackParCov = getTrackParCov(track); + auto xyzTrack = trackParCov.getXYZGlo(); + float sigmaDCAXYZ2; + float dcaXYZ = getDcaXYZ(track, &sigmaDCAXYZ2); + float dcaX = -99.0; + float dcaY = -99.0; + if (track.collisionId() >= 0) { + auto const& collision = track.collision_as(); + dcaX = xyzTrack.X() - collision.posX(); + dcaY = xyzTrack.Y() - collision.posY(); + } + + products.jTracksExtraTable(dcaX, dcaY, track.dcaZ(), track.dcaXY(), dcaXYZ, std::sqrt(track.sigmaDcaZ2()), std::sqrt(track.sigmaDcaXY2()), std::sqrt(sigmaDCAXYZ2), track.sigma1Pt()); // why is this getSigmaZY + products.jTracksParentIndexTable(track.globalIndex()); + products.jMcTracksLabelTable(-1); + trackCollisionMapping[{track.globalIndex(), track.collisionId()}] = products.jTracksTable.lastIndex(); + } + + if (collisionTomcdCollisionMapping[collision.globalIndex()] >= 0) { + + auto collisionMCDTrakcs = tracksMCD.sliceBy(preslices.perCollisionTracksMCD, collisionTomcdCollisionMapping[collision.globalIndex()]); + mcCollisionSelected[collisionTomcdCollisionMapping[collision.globalIndex()]] = true; + for (auto const& track : collisionMCDTrakcs) { + + products.jTracksTable(collision.globalIndex(), track.pt(), track.eta(), track.phi(), jetderiveddatautilities::setTrackSelectionBit(track, track.dcaZ(), config.dcaZMax, trackMCSelection[track.globalIndex()], true)); + auto trackParCov = getTrackParCov(track); + auto xyzTrack = trackParCov.getXYZGlo(); + float sigmaDCAXYZ2; + float dcaXYZ = getDcaXYZ(track, &sigmaDCAXYZ2); + float dcaX = -99.0; + float dcaY = -99.0; + if (track.collisionId() >= 0) { + auto const& collision = track.collision_as>>(); + dcaX = xyzTrack.X() - collision.posX(); + dcaY = xyzTrack.Y() - collision.posY(); + } + + products.jTracksExtraTable(dcaX, dcaY, track.dcaZ(), track.dcaXY(), dcaXYZ, std::sqrt(track.sigmaDcaZ2()), std::sqrt(track.sigmaDcaXY2()), std::sqrt(sigmaDCAXYZ2), track.sigma1Pt()); // why is this getSigmaZY + products.jTracksParentIndexTable(track.globalIndex()); + if (track.has_mcParticle()) { + products.jMcTracksLabelTable(track.mcParticleId()); + } else { + products.jMcTracksLabelTable(-1); + } + trackCollisionMCDMapping[{track.globalIndex(), track.collisionId()}] = products.jTracksTable.lastIndex(); + } + } + } + /* + for (int i = 0; i < collisionsMCD.size(); i++) { + if (!mcCollisionSelected[i]) { + auto collisionMCDTrakcs = tracksMCD.sliceBy(preslices.perCollisionTracksMCD, i); + for (auto const& track : collisionMCDTrakcs) { + products.jTracksTable(mcdcollisionToCollisionMapping[i], track.pt(), track.eta(), track.phi(), jetderiveddatautilities::setTrackSelectionBit(track, track.dcaZ(), config.dcaZMax, trackMCSelection[track.globalIndex()], true)); + auto trackParCov = getTrackParCov(track); + auto xyzTrack = trackParCov.getXYZGlo(); + float sigmaDCAXYZ2; + float dcaXYZ = getDcaXYZ(track, &sigmaDCAXYZ2); + float dcaX = -99.0; + float dcaY = -99.0; + products.jTracksExtraTable(dcaX, dcaY, track.dcaZ(), track.dcaXY(), dcaXYZ, std::sqrt(track.sigmaDcaZ2()), std::sqrt(track.sigmaDcaXY2()), std::sqrt(sigmaDCAXYZ2), track.sigma1Pt()); // why is this getSigmaZY + products.jTracksParentIndexTable(track.globalIndex()); + if (track.has_mcParticle()) { + products.jMcTracksLabelTable(track.mcParticleId()); + } else { + products.jMcTracksLabelTable(-1); + } + trackCollisionMCDMapping[{track.globalIndex(), track.collisionId()}] = products.jTracksTable.lastIndex(); + } + } + } + + for (auto const& track : tracks) { + if (!track.has_collision()) { + products.jTracksTable(track.collisionId(), track.pt(), track.eta(), track.phi(), jetderiveddatautilities::setTrackSelectionBit(track, track.dcaZ(), config.dcaZMax, trackMCSelection[track.globalIndex()])); + float sigmaDCAXYZ2; + float dcaXYZ = getDcaXYZ(track, &sigmaDCAXYZ2); + float dcaX = -99.0; + float dcaY = -99.0; + products.jTracksExtraTable(dcaX, dcaY, track.dcaZ(), track.dcaXY(), dcaXYZ, std::sqrt(track.sigmaDcaZ2()), std::sqrt(track.sigmaDcaXY2()), std::sqrt(sigmaDCAXYZ2), track.sigma1Pt()); // why is this getSigmaZY + products.jTracksParentIndexTable(track.globalIndex()); + products.jMcTracksLabelTable(-1); + trackCollisionMapping[{track.globalIndex(), -1}] = products.jTracksTable.lastIndex(); + } + } + + for (auto const& track : tracksMCD) { + if (!track.has_collision()) { + products.jTracksTable(track.collisionId(), track.pt(), track.eta(), track.phi(), jetderiveddatautilities::setTrackSelectionBit(track, track.dcaZ(), config.dcaZMax, trackMCSelection[track.globalIndex()], true)); + float sigmaDCAXYZ2; + float dcaXYZ = getDcaXYZ(track, &sigmaDCAXYZ2); + float dcaX = -99.0; + float dcaY = -99.0; + products.jTracksExtraTable(dcaX, dcaY, track.dcaZ(), track.dcaXY(), dcaXYZ, std::sqrt(track.sigmaDcaZ2()), std::sqrt(track.sigmaDcaXY2()), std::sqrt(sigmaDCAXYZ2), track.sigma1Pt()); // why is this getSigmaZY + products.jTracksParentIndexTable(track.globalIndex()); + if (track.has_mcParticle()) { + products.jMcTracksLabelTable(track.mcParticleId()); + } else { + products.jMcTracksLabelTable(-1); + } + trackCollisionMapping[{track.globalIndex(), -1}] = products.jTracksTable.lastIndex(); + } + } + */ + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processTracks, "produces derived track table", true); + + void processTracksWithCollisionAssociator(aod::Collisions const& collisions, soa::Join const&, soa::Join const&, aod::TrackAssoc const& assocCollisions) + { + runNumber = 0; + for (auto const& collision : collisions) { + auto collisionTrackIndices = assocCollisions.sliceBy(preslices.perCollisionTrackIndices, collision.globalIndex()); + for (auto const& collisionTrackIndex : collisionTrackIndices) { + auto track = collisionTrackIndex.track_as>(); + auto trackParCov = getTrackParCov(track); + if (track.collisionId() == collision.globalIndex()) { + products.jTracksTable(collision.globalIndex(), track.pt(), track.eta(), track.phi(), jetderiveddatautilities::setTrackSelectionBit(track, track.dcaZ(), config.dcaZMax, trackMCSelection[track.globalIndex()])); + products.jTracksParentIndexTable(track.globalIndex()); + auto xyzTrack = trackParCov.getXYZGlo(); + float sigmaDCAXYZ2; + float dcaXYZ = getDcaXYZ(track, &sigmaDCAXYZ2); + products.jTracksExtraTable(xyzTrack.X() - collision.posX(), xyzTrack.Y() - collision.posY(), track.dcaZ(), track.dcaXY(), dcaXYZ, std::sqrt(track.sigmaDcaZ2()), std::sqrt(track.sigmaDcaXY2()), std::sqrt(sigmaDCAXYZ2), track.sigma1Pt()); // why is this getSigmaZY + } else { + auto bc = collision.bc_as>(); + initCCDB(bc, runNumber, ccdb, doprocessCollisionsRun2 ? config.ccdbPathGrp : config.ccdbPathGrpMag, lut, doprocessCollisionsRun2); + o2::dataformats::DCA dcaCovInfo; + dcaCovInfo.set(-999., -999., -999., -999., -999.); + o2::dataformats::VertexBase collisionInfo; + collisionInfo.setPos({collision.posX(), collision.posY(), collision.posZ()}); + collisionInfo.setCov(collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()); + o2::base::Propagator::Instance()->propagateToDCABxByBz(collisionInfo, trackParCov, 2.f, noMatCorr, &dcaCovInfo); + products.jTracksTable(collision.globalIndex(), trackParCov.getPt(), trackParCov.getEta(), trackParCov.getPhi(), jetderiveddatautilities::setTrackSelectionBit(track, dcaCovInfo.getZ(), config.dcaZMax, trackMCSelection[track.globalIndex()])); // only qualitytracksWDCA are a reliable selection + products.jTracksParentIndexTable(track.globalIndex()); + auto xyzTrack = trackParCov.getXYZGlo(); + float dcaXY = dcaCovInfo.getY(); + float dcaZ = dcaCovInfo.getZ(); + float dcaXYZ = std::sqrt(dcaXY * dcaXY + dcaZ * dcaZ); + float covYY = dcaCovInfo.getSigmaY2(); + float covZZ = dcaCovInfo.getSigmaZ2(); + float covYZ = dcaCovInfo.getSigmaYZ(); + float sigmaDCAXYZ; + if (dcaXYZ < o2::constants::math::Almost0) { + sigmaDCAXYZ = o2::constants::math::VeryBig; // Protection against division by zero + } else { + sigmaDCAXYZ = covYY * (2.f * dcaXY / dcaXYZ) * (2.f * dcaXY / dcaXYZ) + covZZ * (2.f * dcaZ / dcaXYZ) * (2.f * dcaZ / dcaXYZ) + 2.f * covYZ * (2.f * dcaXY / dcaXYZ) * (2.f * dcaZ / dcaXYZ); + } + products.jTracksExtraTable(xyzTrack.X() - collision.posX(), xyzTrack.Y() - collision.posY(), dcaZ, dcaXY, dcaXYZ, std::sqrt(covZZ), std::sqrt(covYY), std::sqrt(sigmaDCAXYZ), std::sqrt(trackParCov.getSigma1Pt2())); + } + trackCollisionMapping[{track.globalIndex(), collision.globalIndex()}] = products.jTracksTable.lastIndex(); + } + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processTracksWithCollisionAssociator, "produces derived track table taking into account track-to-collision associations", false); + + void processTracksRun2(soa::Join::iterator const& track) + { + // TracksDCACov table is not yet available for Run 2 converted data. Remove this process function and use only processTracks when that becomes available. + products.jTracksTable(track.collisionId(), track.pt(), track.eta(), track.phi(), jetderiveddatautilities::setTrackSelectionBit(track, track.dcaZ(), config.dcaZMax, trackMCSelection[track.globalIndex()])); + float sigmaDCAXYZ2 = 0.0; + float dcaXYZ = getDcaXYZ(track, &sigmaDCAXYZ2); + float dcaX = -99.0; + float dcaY = -99.0; + + products.jTracksExtraTable(dcaX, dcaY, track.dcaZ(), track.dcaXY(), dcaXYZ, std::sqrt(1.), std::sqrt(1.), std::sqrt(sigmaDCAXYZ2), track.sigma1Pt()); // dummy values - will be fixed when TracksDCACov table is available for Run 2 + products.jTracksParentIndexTable(track.globalIndex()); + trackCollisionMapping[{track.globalIndex(), track.collisionId()}] = products.jTracksTable.lastIndex(); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processTracksRun2, "produces derived track table for Run2 AO2Ds", false); + + void processMcTrackLabelsWithCollisionAssociator(aod::Collisions const& collisions, soa::Join const&, aod::TrackAssoc const& assocCollisions) + { + for (auto const& collision : collisions) { + auto collisionTrackIndices = assocCollisions.sliceBy(preslices.perCollisionTrackIndices, collision.globalIndex()); + for (auto const& collisionTrackIndex : collisionTrackIndices) { + auto track = collisionTrackIndex.track_as>(); + if (track.has_mcParticle()) { + products.jMcTracksLabelTable(track.mcParticleId()); + } else { + products.jMcTracksLabelTable(-1); + } + } + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processMcTrackLabelsWithCollisionAssociator, "produces derived track labels table taking into account track-to-collision associations", false); + + void processParticles(aod::McParticlesFrom>::iterator const& particle) + { + std::vector mothersId; + if (particle.has_mothers()) { + auto mothersIdTemps = particle.mothersIds(); + for (auto mothersIdTemp : mothersIdTemps) { + mothersId.push_back(mothersIdTemp); + } + } + int daughtersId[2] = {-1, -1}; + if (particle.has_daughters()) { + auto i = 0; + for (auto daughterId : particle.daughtersIds()) { + if (i > 1) { + break; + } + daughtersId[i] = daughterId; + i++; + } + } + products.jMcParticlesTable(particle.mcCollisionId(), particle.pt(), particle.eta(), particle.phi(), particle.y(), particle.e(), particle.pdgCode(), particle.statusCode(), particle.flags(), mothersId, daughtersId); + products.jParticlesParentIndexTable(particle.globalIndex()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processParticles, "produces derived parrticle table", true); + + void processClusters(aod::Collision const&, aod::EMCALClusters const& clusters, aod::EMCALClusterCells const& cells, aod::Calos const&, aod::EMCALMatchedTracks const& matchedTracks, soa::Join const&) + { + + for (auto const& cluster : clusters) { + + auto const clusterCells = cells.sliceBy(preslices.perClusterCells, cluster.globalIndex()); + + float leadingCellEnergy = -1.0; + float subleadingCellEnergy = -1.0; + int leadingCellNumber = -1; + int subleadingCellNumber = -1; + for (auto const& clutserCell : clusterCells) { + float cellAmplitude = clutserCell.calo().amplitude(); + int cellNumber = clutserCell.calo().cellNumber(); + if (cellAmplitude > subleadingCellEnergy) { + subleadingCellEnergy = cellAmplitude; + subleadingCellNumber = cellNumber; + } + if (subleadingCellEnergy > leadingCellEnergy) { + std::swap(leadingCellEnergy, subleadingCellEnergy); + std::swap(leadingCellNumber, subleadingCellNumber); + } + } + + products.jClustersTable(cluster.collisionId(), cluster.id(), cluster.energy(), cluster.coreEnergy(), cluster.rawEnergy(), cluster.eta(), cluster.phi(), cluster.m02(), cluster.m20(), cluster.nCells(), cluster.time(), cluster.isExotic(), cluster.distanceToBadChannel(), cluster.nlm(), cluster.definition(), leadingCellEnergy, subleadingCellEnergy, leadingCellNumber, subleadingCellNumber); + products.jClustersParentIndexTable(cluster.globalIndex()); + + auto const clusterTracks = matchedTracks.sliceBy(preslices.perClusterTracks, cluster.globalIndex()); + std::vector clusterTrackIDs; + for (const auto& clusterTrack : clusterTracks) { + auto JClusterID = trackCollisionMapping.find({clusterTrack.trackId(), cluster.collisionId()}); // does EMCal use its own associator? + clusterTrackIDs.push_back(JClusterID->second); + auto emcTrack = clusterTrack.track_as>(); + products.jTracksEMCalTable(JClusterID->second, emcTrack.trackEtaEmcal(), emcTrack.trackPhiEmcal(), clusterTrack.deltaEta(), clusterTrack.deltaPhi()); + } + products.jClustersMatchedTracksTable(clusterTrackIDs); + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processClusters, "produces derived cluster tables", false); + + void processMcClusterLabels(aod::EMCALMCCluster const& cluster) + { + std::vector particleIds; + for (auto particleId : cluster.mcParticleIds()) { + particleIds.push_back(particleId); + } + std::vector amplitudeA; + auto amplitudeASpan = cluster.amplitudeA(); + std::copy(amplitudeASpan.begin(), amplitudeASpan.end(), std::back_inserter(amplitudeA)); + products.jMcClustersLabelTable(particleIds, amplitudeA); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processMcClusterLabels, "produces derived cluster particle label table", false); + /* + void processD0Collisions(aod::HfD0CollIds::iterator const& D0Collision) + { + products.jD0CollisionIdsTable(D0Collision.collisionId()); + } + + void processD0McCollisions(aod::HfD0McCollIdsFrom>::iterator const& D0McCollision) + { + products.jD0McCollisionIdsTable(D0McCollision.mcCollisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processD0McCollisions, "produces derived index for D0 MC collisions", false); + + void processD0(aod::HfD0Idss const& D0Candidates, aod::Tracks const&, aod::HfD0IdssFrom> const& D0CandidatesMCD, aod::TracksFrom> const&) + { + for (auto const& D0Candidate : D0Candidates) { + auto JProng0ID = trackCollisionMapping.find({D0Candidate.prong0Id(), D0Candidate.prong0_as().collisionId()}); + auto JProng1ID = trackCollisionMapping.find({D0Candidate.prong1Id(), D0Candidate.prong1_as().collisionId()}); + if (withCollisionAssociator) { + JProng0ID = trackCollisionMapping.find({D0Candidate.prong0Id(), D0Candidate.collisionId()}); + JProng1ID = trackCollisionMapping.find({D0Candidate.prong1Id(), D0Candidate.collisionId()}); + } + products.jD0IdsTable(D0Candidate.collisionId(), JProng0ID->second, JProng1ID->second); + } + // need to order them by collision though. Actually no this should be done in the HF + for (auto const& D0Candidate : D0CandidatesMCD) { + auto JProng0ID = trackCollisionMCDMapping.find({D0Candidate.prong0Id(), D0Candidate.prong0_as().collisionId()}); + auto JProng1ID = trackCollisionMCDMapping.find({D0Candidate.prong1Id(), D0Candidate.prong1_as().collisionId()}); + if (withCollisionAssociator) { + JProng0ID = trackCollisionMCDMapping.find({D0Candidate.prong0Id(), D0Candidate.collisionId()}); + JProng1ID = trackCollisionMCDMapping.find({D0Candidate.prong1Id(), D0Candidate.collisionId()}); + } + products.jD0IdsTable(D0Candidate.collisionId(), JProng0ID->second, JProng1ID->second); + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processD0, "produces derived index for D0 candidates", false); + + void processD0MC(aod::HfD0PIds::iterator const& D0Particle) + { + products.jD0ParticleIdsTable(D0Particle.mcCollisionId(), D0Particle.mcParticleId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processD0MC, "produces derived index for D0 particles", false); + + void processDplusCollisions(aod::HfDplusCollIds::iterator const& DplusCollision) + { + products.jDplusCollisionIdsTable(DplusCollision.collisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDplusCollisions, "produces derived index for Dplus collisions", false); + + void processDplusMcCollisions(aod::HfDplusMcCollIds::iterator const& DplusMcCollision) + { + products.jDplusMcCollisionIdsTable(DplusMcCollision.mcCollisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDplusMcCollisions, "produces derived index for Dplus MC collisions", false); + + void processDplus(aod::HfDplusIds::iterator const& DplusCandidate, aod::Tracks const&) + { + auto JProng0ID = trackCollisionMapping.find({DplusCandidate.prong0Id(), DplusCandidate.prong0_as().collisionId()}); + auto JProng1ID = trackCollisionMapping.find({DplusCandidate.prong1Id(), DplusCandidate.prong1_as().collisionId()}); + auto JProng2ID = trackCollisionMapping.find({DplusCandidate.prong2Id(), DplusCandidate.prong2_as().collisionId()}); + if (withCollisionAssociator) { + JProng0ID = trackCollisionMapping.find({DplusCandidate.prong0Id(), DplusCandidate.collisionId()}); + JProng1ID = trackCollisionMapping.find({DplusCandidate.prong1Id(), DplusCandidate.collisionId()}); + JProng2ID = trackCollisionMapping.find({DplusCandidate.prong2Id(), DplusCandidate.collisionId()}); + } + products.jDplusIdsTable(DplusCandidate.collisionId(), JProng0ID->second, JProng1ID->second, JProng2ID->second); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDplus, "produces derived index for Dplus candidates", false); + + void processDplusMC(aod::HfDplusPIds::iterator const& DplusParticle) + { + products.jDplusParticleIdsTable(DplusParticle.mcCollisionId(), DplusParticle.mcParticleId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDplusMC, "produces derived index for Dplus particles", false); + + void processDsCollisions(aod::HfDsCollIds::iterator const& DsCollision) + { + products.jDsCollisionIdsTable(DsCollision.collisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDsCollisions, "produces derived index for Ds collisions", false); + + void processDsMcCollisions(aod::HfDsMcCollIds::iterator const& DsMcCollision) + { + products.jDsMcCollisionIdsTable(DsMcCollision.mcCollisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDsMcCollisions, "produces derived index for Ds MC collisions", false); + + void processDs(aod::HfDsIds::iterator const& DsCandidate, aod::Tracks const&) + { + auto JProng0ID = trackCollisionMapping.find({DsCandidate.prong0Id(), DsCandidate.prong0_as().collisionId()}); + auto JProng1ID = trackCollisionMapping.find({DsCandidate.prong1Id(), DsCandidate.prong1_as().collisionId()}); + auto JProng2ID = trackCollisionMapping.find({DsCandidate.prong2Id(), DsCandidate.prong2_as().collisionId()}); + if (withCollisionAssociator) { + JProng0ID = trackCollisionMapping.find({DsCandidate.prong0Id(), DsCandidate.collisionId()}); + JProng1ID = trackCollisionMapping.find({DsCandidate.prong1Id(), DsCandidate.collisionId()}); + JProng2ID = trackCollisionMapping.find({DsCandidate.prong2Id(), DsCandidate.collisionId()}); + } + products.jDsIdsTable(DsCandidate.collisionId(), JProng0ID->second, JProng1ID->second, JProng2ID->second); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDs, "produces derived index for Ds candidates", false); + + void processDsMC(aod::HfDsPIds::iterator const& DsParticle) + { + products.jDsParticleIdsTable(DsParticle.mcCollisionId(), DsParticle.mcParticleId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDsMC, "produces derived index for Ds particles", false); + + void processDstarCollisions(aod::HfDstarCollIds::iterator const& DstarCollision) + { + products.jDstarCollisionIdsTable(DstarCollision.collisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDstarCollisions, "produces derived index for Dstar collisions", false); + + void processDstarMcCollisions(aod::HfDstarMcCollIds::iterator const& DstarMcCollision) + { + products.jDstarMcCollisionIdsTable(DstarMcCollision.mcCollisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDstarMcCollisions, "produces derived index for Dstar MC collisions", false); + + void processDstar(aod::HfDstarIds::iterator const& DstarCandidate, aod::Tracks const&) + { + auto JProng0ID = trackCollisionMapping.find({DstarCandidate.prong0Id(), DstarCandidate.prong0_as().collisionId()}); + auto JProng1ID = trackCollisionMapping.find({DstarCandidate.prong1Id(), DstarCandidate.prong1_as().collisionId()}); + auto JProng2ID = trackCollisionMapping.find({DstarCandidate.prong2Id(), DstarCandidate.prong2_as().collisionId()}); + if (withCollisionAssociator) { + JProng0ID = trackCollisionMapping.find({DstarCandidate.prong0Id(), DstarCandidate.collisionId()}); + JProng1ID = trackCollisionMapping.find({DstarCandidate.prong1Id(), DstarCandidate.collisionId()}); + JProng2ID = trackCollisionMapping.find({DstarCandidate.prong2Id(), DstarCandidate.collisionId()}); + } + products.jDstarIdsTable(DstarCandidate.collisionId(), JProng0ID->second, JProng1ID->second, JProng2ID->second); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDstar, "produces derived index for Dstar candidates", false); + + void processDstarMC(aod::HfDstarPIds::iterator const& DstarParticle) + { + products.jDstarParticleIdsTable(DstarParticle.mcCollisionId(), DstarParticle.mcParticleId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDstarMC, "produces derived index for Dstar particles", false); + + void processLcCollisions(aod::HfLcCollIds::iterator const& LcCollision) + { + products.jLcCollisionIdsTable(LcCollision.collisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processLcCollisions, "produces derived index for Lc collisions", false); + + void processLcMcCollisions(aod::HfLcMcCollIds::iterator const& LcMcCollision) + { + products.jLcMcCollisionIdsTable(LcMcCollision.mcCollisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processLcMcCollisions, "produces derived index for Lc MC collisions", false); + + void processLc(aod::HfLcIds::iterator const& LcCandidate, aod::Tracks const&) + { + auto JProng0ID = trackCollisionMapping.find({LcCandidate.prong0Id(), LcCandidate.prong0_as().collisionId()}); + auto JProng1ID = trackCollisionMapping.find({LcCandidate.prong1Id(), LcCandidate.prong1_as().collisionId()}); + auto JProng2ID = trackCollisionMapping.find({LcCandidate.prong2Id(), LcCandidate.prong2_as().collisionId()}); + if (withCollisionAssociator) { + JProng0ID = trackCollisionMapping.find({LcCandidate.prong0Id(), LcCandidate.collisionId()}); + JProng1ID = trackCollisionMapping.find({LcCandidate.prong1Id(), LcCandidate.collisionId()}); + JProng2ID = trackCollisionMapping.find({LcCandidate.prong2Id(), LcCandidate.collisionId()}); + } + products.jLcIdsTable(LcCandidate.collisionId(), JProng0ID->second, JProng1ID->second, JProng2ID->second); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processLc, "produces derived index for Lc candidates", false); + + void processLcMC(aod::HfLcPIds::iterator const& LcParticle) + { + products.jLcParticleIdsTable(LcParticle.mcCollisionId(), LcParticle.mcParticleId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processLcMC, "produces derived index for Lc particles", false); + + void processB0Collisions(aod::HfB0CollIds::iterator const& B0Collision) + { + products.jB0CollisionIdsTable(B0Collision.collisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processB0Collisions, "produces derived index for B0 collisions", false); + + void processB0McCollisions(aod::HfB0McCollIds::iterator const& B0McCollision) + { + products.jB0McCollisionIdsTable(B0McCollision.mcCollisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processB0McCollisions, "produces derived index for B0 MC collisions", false); + + void processB0(aod::HfB0Ids::iterator const& B0Candidate, aod::Tracks const&) + { + auto JProng0ID = trackCollisionMapping.find({B0Candidate.prong0Id(), B0Candidate.prong0_as().collisionId()}); + auto JProng1ID = trackCollisionMapping.find({B0Candidate.prong1Id(), B0Candidate.prong1_as().collisionId()}); + auto JProng2ID = trackCollisionMapping.find({B0Candidate.prong2Id(), B0Candidate.prong2_as().collisionId()}); + auto JProng3ID = trackCollisionMapping.find({B0Candidate.prong3Id(), B0Candidate.prong3_as().collisionId()}); + if (withCollisionAssociator) { + JProng0ID = trackCollisionMapping.find({B0Candidate.prong0Id(), B0Candidate.collisionId()}); + JProng1ID = trackCollisionMapping.find({B0Candidate.prong1Id(), B0Candidate.collisionId()}); + JProng2ID = trackCollisionMapping.find({B0Candidate.prong2Id(), B0Candidate.collisionId()}); + JProng3ID = trackCollisionMapping.find({B0Candidate.prong3Id(), B0Candidate.collisionId()}); + } + products.jB0IdsTable(B0Candidate.collisionId(), JProng0ID->second, JProng1ID->second, JProng2ID->second, JProng3ID->second); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processB0, "produces derived index for B0 candidates", false); + + void processB0MC(aod::HfB0PIds::iterator const& B0Particle) + { + products.jB0ParticleIdsTable(B0Particle.mcCollisionId(), B0Particle.mcParticleId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processB0MC, "produces derived index for B0 particles", false); + + void processBplusCollisions(aod::HfBplusCollIds::iterator const& BplusCollision) + { + products.jBplusCollisionIdsTable(BplusCollision.collisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processBplusCollisions, "produces derived index for Bplus collisions", false); + + void processBplusMcCollisions(aod::HfBplusMcCollIds::iterator const& BplusMcCollision) + { + products.jBplusMcCollisionIdsTable(BplusMcCollision.mcCollisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processBplusMcCollisions, "produces derived index for Bplus MC collisions", false); + + void processBplus(aod::HfBplusIds::iterator const& BplusCandidate, aod::Tracks const&) + { + auto JProng0ID = trackCollisionMapping.find({BplusCandidate.prong0Id(), BplusCandidate.prong0_as().collisionId()}); + auto JProng1ID = trackCollisionMapping.find({BplusCandidate.prong1Id(), BplusCandidate.prong1_as().collisionId()}); + auto JProng2ID = trackCollisionMapping.find({BplusCandidate.prong2Id(), BplusCandidate.prong2_as().collisionId()}); + if (withCollisionAssociator) { + JProng0ID = trackCollisionMapping.find({BplusCandidate.prong0Id(), BplusCandidate.collisionId()}); + JProng1ID = trackCollisionMapping.find({BplusCandidate.prong1Id(), BplusCandidate.collisionId()}); + JProng2ID = trackCollisionMapping.find({BplusCandidate.prong2Id(), BplusCandidate.collisionId()}); + } + products.jBplusIdsTable(BplusCandidate.collisionId(), JProng0ID->second, JProng1ID->second, JProng2ID->second); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processBplus, "produces derived index for Bplus candidates", false); + + void processBplusMC(aod::HfBplusPIds::iterator const& BplusParticle) + { + products.jBplusParticleIdsTable(BplusParticle.mcCollisionId(), BplusParticle.mcParticleId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processBplusMC, "produces derived index for Bplus particles", false); + + void processXicToXiPiPiCollisions(aod::HfXicToXiPiPiCollIds::iterator const& XicToXiPiPiCollision) + { + products.jXicToXiPiPiCollisionIdsTable(XicToXiPiPiCollision.collisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processXicToXiPiPiCollisions, "produces derived index for XicToXiPiPi collisions", false); + + void processXicToXiPiPiMcCollisions(aod::HfXicToXiPiPiMcCollIds::iterator const& XicToXiPiPiMcCollision) + { + products.jXicToXiPiPiMcCollisionIdsTable(XicToXiPiPiMcCollision.mcCollisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processXicToXiPiPiMcCollisions, "produces derived index for XicToXiPiPi MC collisions", false); + + void processXicToXiPiPi(aod::HfXicToXiPiPiIds::iterator const& XicToXiPiPiCandidate, aod::Tracks const&) + { + auto JProng0ID = trackCollisionMapping.find({XicToXiPiPiCandidate.prong0Id(), XicToXiPiPiCandidate.prong0_as().collisionId()}); + auto JProng1ID = trackCollisionMapping.find({XicToXiPiPiCandidate.prong1Id(), XicToXiPiPiCandidate.prong1_as().collisionId()}); + auto JProng2ID = trackCollisionMapping.find({XicToXiPiPiCandidate.prong2Id(), XicToXiPiPiCandidate.prong2_as().collisionId()}); + auto JProng3ID = trackCollisionMapping.find({XicToXiPiPiCandidate.prong3Id(), XicToXiPiPiCandidate.prong3_as().collisionId()}); + auto JProng4ID = trackCollisionMapping.find({XicToXiPiPiCandidate.prong4Id(), XicToXiPiPiCandidate.prong4_as().collisionId()}); + if (withCollisionAssociator) { + JProng0ID = trackCollisionMapping.find({XicToXiPiPiCandidate.prong0Id(), XicToXiPiPiCandidate.collisionId()}); + JProng1ID = trackCollisionMapping.find({XicToXiPiPiCandidate.prong1Id(), XicToXiPiPiCandidate.collisionId()}); + JProng2ID = trackCollisionMapping.find({XicToXiPiPiCandidate.prong2Id(), XicToXiPiPiCandidate.collisionId()}); + JProng3ID = trackCollisionMapping.find({XicToXiPiPiCandidate.prong3Id(), XicToXiPiPiCandidate.collisionId()}); + JProng4ID = trackCollisionMapping.find({XicToXiPiPiCandidate.prong4Id(), XicToXiPiPiCandidate.collisionId()}); + } + products.jXicToXiPiPiIdsTable(XicToXiPiPiCandidate.collisionId(), JProng0ID->second, JProng1ID->second, JProng2ID->second, JProng3ID->second, JProng4ID->second); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processXicToXiPiPi, "produces derived index for XicToXiPiPi candidates", false); + + void processXicToXiPiPiMC(aod::HfXicToXiPiPiPIds::iterator const& XicToXiPiPiParticle) + { + products.jXicToXiPiPiParticleIdsTable(XicToXiPiPiParticle.mcCollisionId(), XicToXiPiPiParticle.mcParticleId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processXicToXiPiPiMC, "produces derived index for XicToXiPiPi particles", false); + + void processV0(aod::V0Indices::iterator const& V0Candidate, aod::Tracks const&) + { + auto JPosTrackID = trackCollisionMapping.find({V0Candidate.posTrackId(), V0Candidate.posTrack_as().collisionId()}); + auto JNegTrackID = trackCollisionMapping.find({V0Candidate.negTrackId(), V0Candidate.negTrack_as().collisionId()}); + if (withCollisionAssociator) { + JPosTrackID = trackCollisionMapping.find({V0Candidate.posTrackId(), V0Candidate.collisionId()}); + JNegTrackID = trackCollisionMapping.find({V0Candidate.negTrackId(), V0Candidate.collisionId()}); + } + products.jV0IdsTable(V0Candidate.collisionId(), JPosTrackID->second, JNegTrackID->second); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processV0, "produces derived index for V0 candidates", false); + + void processV0MC(aod::McCollision const& mcCollision, aod::McParticles const& particles) + { // can loop over McV0Labels tables if we want to only store matched V0Particles + bool filledV0McCollisionTable = false; + for (auto const& particle : particles) { + if (jetv0utilities::isV0Particle(particles, particle, config.v0ChargedDecaysOnly)) { + if (!filledV0McCollisionTable) { + products.jV0McCollisionsTable(mcCollision.posX(), mcCollision.posY(), mcCollision.posZ()); + products.jV0McCollisionIdsTable(mcCollision.globalIndex()); + filledV0McCollisionTable = true; + } + std::vector mothersId; + if (particle.has_mothers()) { + auto mothersIdTemps = particle.mothersIds(); + for (auto mothersIdTemp : mothersIdTemps) { + mothersId.push_back(mothersIdTemp); + } + } + int daughtersId[2] = {-1, -1}; + if (particle.has_daughters()) { + auto i = 0; + for (auto daughterId : particle.daughtersIds()) { + if (i > 1) { + break; + } + daughtersId[i] = daughterId; + i++; + } + } + auto pdgParticle = pdgDatabase->GetParticle(particle.pdgCode()); + products.jV0McsTable(products.jV0McCollisionsTable.lastIndex(), particle.pt(), particle.eta(), particle.phi(), particle.y(), particle.e(), pdgParticle->Mass(), particle.pdgCode(), particle.statusCode(), particle.flags(), jetv0utilities::setV0ParticleDecayBit(particles, particle)); + products.jV0McIdsTable(mcCollision.globalIndex(), particle.globalIndex(), mothersId, daughtersId); + } + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processV0MC, "produces V0 particles", false); + + void processDielectronCollisions(aod::ReducedEventsInfo::iterator const& DielectronCollision) + { + products.jDielectronCollisionIdsTable(DielectronCollision.collisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDielectronCollisions, "produces derived index for Dielectron collisions", false); + + void processDielectron(aod::DielectronInfo const& DielectronCandidate, aod::Tracks const&) + { + auto JProng0ID = trackCollisionMapping.find({DielectronCandidate.prong0Id(), DielectronCandidate.prong0_as().collisionId()}); + auto JProng1ID = trackCollisionMapping.find({DielectronCandidate.prong1Id(), DielectronCandidate.prong1_as().collisionId()}); + if (withCollisionAssociator) { + JProng0ID = trackCollisionMapping.find({DielectronCandidate.prong0Id(), DielectronCandidate.collisionId()}); + JProng1ID = trackCollisionMapping.find({DielectronCandidate.prong1Id(), DielectronCandidate.collisionId()}); + } + products.jDielectronIdsTable(DielectronCandidate.collisionId(), JProng0ID->second, JProng1ID->second); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDielectron, "produces derived index for Dielectron candidates", false); + + void processDielectronMc(aod::McCollision const& mcCollision, aod::McParticles const& particles) + { + bool filledDielectronMcCollisionTable = false; + for (auto const& particle : particles) { + if (jetdqutilities::isDielectronParticle(particles, particle)) { + if (!filledDielectronMcCollisionTable) { + products.jDielectronMcCollisionsTable(mcCollision.posX(), mcCollision.posY(), mcCollision.posZ()); + products.jDielectronMcCollisionIdsTable(mcCollision.globalIndex()); + filledDielectronMcCollisionTable = true; + } + std::vector mothersId; + if (particle.has_mothers()) { + auto mothersIdTemps = particle.mothersIds(); + for (auto mothersIdTemp : mothersIdTemps) { + mothersId.push_back(mothersIdTemp); + } + } + int daughtersId[2] = {-1, -1}; + if (particle.has_daughters()) { + auto i = 0; + for (auto daughterId : particle.daughtersIds()) { + if (i > 1) { + break; + } + daughtersId[i] = daughterId; + i++; + } + } + auto pdgParticle = pdgDatabase->GetParticle(particle.pdgCode()); + products.jDielectronMcsTable(products.jDielectronMcCollisionsTable.lastIndex(), particle.pt(), particle.eta(), particle.phi(), particle.y(), particle.e(), pdgParticle->Mass(), particle.vx(), particle.vy(), particle.vz(), particle.vt(), particle.pdgCode(), particle.statusCode(), particle.flags(), jetdqutilities::setDielectronParticleDecayBit(particles, particle), RecoDecay::getCharmHadronOrigin(particles, particle, false)); // Todo: should the last thing be false? + products.jDielectronMcIdsTable(mcCollision.globalIndex(), particle.globalIndex(), mothersId, daughtersId); + products.JDielectronMcRCollDummysTable(false); + } + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDielectronMc, "produces Dielectron mccollisions and particles", false); + */ +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc, TaskName{"jet-deriveddata-embedding-producer"})}; +} diff --git a/PWGJE/TableProducer/derivedDataSelector.cxx b/PWGJE/TableProducer/derivedDataSelector.cxx index 25d4e69d3ba..b82419f7079 100644 --- a/PWGJE/TableProducer/derivedDataSelector.cxx +++ b/PWGJE/TableProducer/derivedDataSelector.cxx @@ -21,6 +21,7 @@ #include "PWGJE/DataModel/Jet.h" #include "PWGJE/DataModel/JetReducedData.h" #include "PWGJE/DataModel/JetReducedDataSelector.h" +#include "PWGJE/DataModel/JetSubtraction.h" #include #include diff --git a/PWGJE/TableProducer/derivedDataWriter.cxx b/PWGJE/TableProducer/derivedDataWriter.cxx index 5cb9b9c8f10..d0146f7c294 100644 --- a/PWGJE/TableProducer/derivedDataWriter.cxx +++ b/PWGJE/TableProducer/derivedDataWriter.cxx @@ -28,6 +28,7 @@ #include "PWGJE/DataModel/JetReducedDataDQ.h" #include "PWGJE/DataModel/JetReducedDataHF.h" #include "PWGJE/DataModel/JetReducedDataSelector.h" +#include "PWGJE/DataModel/JetReducedDataV0.h" #include "PWGLF/DataModel/LFStrangenessTables.h" #include "PWGLF/DataModel/V0SelectorTables.h" diff --git a/PWGJE/TableProducer/emcalCorrectionTask.cxx b/PWGJE/TableProducer/emcalCorrectionTask.cxx index 985e622f7fa..7ccafbfef24 100644 --- a/PWGJE/TableProducer/emcalCorrectionTask.cxx +++ b/PWGJE/TableProducer/emcalCorrectionTask.cxx @@ -133,7 +133,6 @@ struct EmcalCorrectionTask { Configurable useWeightExotic{"useWeightExotic", false, "States if weights should be used for exotic cell cut"}; Configurable isMC{"isMC", false, "States if run over MC"}; Configurable applyCellTimeCorrection{"applyCellTimeCorrection", true, "apply a correction to the cell time for data and MC: Shift both average cell times to 0 and smear MC time distribution to fit data better. For MC requires isMC to be true"}; - Configurable trackMinPt{"trackMinPt", 0.3, "Minimum pT for tracks to perform track matching, to reduce computing time. Tracks below a certain pT will be loopers anyway."}; Configurable fillQA{"fillQA", false, "Switch to turn on QA histograms."}; Configurable useCCDBAlignment{"useCCDBAlignment", false, "EXPERTS ONLY! Switch to use the alignment object stored in CCDB instead of using the default alignment from the global geometry object."}; Configurable applyTempCalib{"applyTempCalib", false, "Switch to turn on Temperature calibration."}; @@ -147,6 +146,12 @@ struct EmcalCorrectionTask { Configurable storePerDFInfo{"storePerDFInfo", false, "store addition information per DF."}; ConfigurableAxis thConfigAxisClusters{"thConfigAxisClusters", {1000, 0.5f, 1000.5f}, ""}; ConfigurableAxis thConfigAxisCells{"thConfigAxisCells", {1000, 0.5f, 1000.5f}, ""}; + // Track matching config + struct : ConfigurableGroup { + std::string prefix = "trackmatching"; + Configurable trackMinPt{"trackMinPt", 0.3, "Minimum pT (GeV) for tracks to perform track matching, to reduce computing time. Tracks below a certain pT will be loopers anyway."}; + Configurable useAllTracks{"useAllTracks", false, "Flag to use all tracks instead of just global tracks."}; + } trackmatching; // cross talk emulation configs EmcCrossTalkConf emcCrossTalkConf; @@ -1494,7 +1499,7 @@ struct EmcalCorrectionTask { if (trackEtaEmcal < TrackNotOnEMCal || trackPhiEmcal < TrackNotOnEMCal) { continue; } - if (trackMinPt > 0 && track.pt() < trackMinPt) { + if (trackmatching.trackMinPt > 0 && track.pt() < trackmatching.trackMinPt) { continue; } trackPhi.emplace_back(RecoDecay::constrainAngle(trackPhiEmcal)); @@ -1508,7 +1513,7 @@ struct EmcalCorrectionTask { void fillTrackInfo(Tracks const& tracks, std::vector& trackPhi, std::vector& trackEta, std::vector& trackGlobalIndex) { for (const auto& track : tracks) { - if (!track.isGlobalTrack()) { // only global tracks + if (!trackmatching.useAllTracks && !track.isGlobalTrack()) { // only global tracks depending on trackmatching.useAllTracks flag continue; } // Tracks that do not point to the EMCal/DCal/PHOS get default values of -999 @@ -1516,7 +1521,7 @@ struct EmcalCorrectionTask { if (track.trackEtaEmcal() < TrackNotOnEMCal || track.trackPhiEmcal() < TrackNotOnEMCal) { continue; } - if (trackMinPt > 0 && track.pt() < trackMinPt) { + if (trackmatching.trackMinPt > 0 && track.pt() < trackmatching.trackMinPt) { continue; } trackPhi.emplace_back(RecoDecay::constrainAngle(track.trackPhiEmcal())); diff --git a/PWGJE/TableProducer/eventwiseConstituentSubtractor.cxx b/PWGJE/TableProducer/eventwiseConstituentSubtractor.cxx index d60275c6fac..18e7f854171 100644 --- a/PWGJE/TableProducer/eventwiseConstituentSubtractor.cxx +++ b/PWGJE/TableProducer/eventwiseConstituentSubtractor.cxx @@ -103,9 +103,9 @@ struct eventWiseConstituentSubtractorTask { void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); - particleSelection = static_cast(particleSelections); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); + particleSelection = particleSelections; eventWiseConstituentSubtractor.setDoRhoMassSub(doRhoMassSub); eventWiseConstituentSubtractor.setConstSubAlphaRMax(alpha, rMax); @@ -130,7 +130,7 @@ struct eventWiseConstituentSubtractorTask { } inputParticles.clear(); tracksSubtracted.clear(); - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, false, &candidate); tracksSubtracted = eventWiseConstituentSubtractor.JetBkgSubUtils::doEventConstSub(inputParticles, candidate.rho(), candidate.rhoM()); for (auto const& trackSubtracted : tracksSubtracted) { diff --git a/PWGJE/TableProducer/jetTaggerHF.cxx b/PWGJE/TableProducer/jetTaggerHF.cxx index bd8625b676c..4f70a9f9d31 100644 --- a/PWGJE/TableProducer/jetTaggerHF.cxx +++ b/PWGJE/TableProducer/jetTaggerHF.cxx @@ -145,6 +145,17 @@ struct JetTaggerHFTask { ConfigurableAxis binTrackProbability{"binTrackProbability", {100, 0.f, 1.f}, ""}; ConfigurableAxis binJetFlavour{"binJetFlavour", {6, -0.5, 5.5}, ""}; + // ParT (Particle Transformer) tagger. Grouped in a ConfigurableGroup because + // StructToTuple.h supports at most 99 task members and this task is near the limit. + struct ParTCfg : ConfigurableGroup { + std::string prefix = "part"; + Configurable nConstit{"nConstit", 32, "fixed number of constituents fed to the ParT model, must match training"}; + Configurable nFeat{"nFeat", 10, "number of pf_features channels"}; + Configurable nVec{"nVec", 4, "number of pf_vectors channels"}; + Configurable padWrap{"padWrap", true, "pad short jets by cyclic repetition (weaver 'wrap' mode); set false for zero padding"}; + Configurable minTracks{"minTracks", 2, "minimum number of selected constituents; training applied jet_nTracks >= 2"}; + } parTCfg; + o2::analysis::MlResponseHfTagging bMlResponse; o2::ccdb::CcdbApi ccdbApi; @@ -277,7 +288,7 @@ struct JetTaggerHFTask { } } } - if (doprocessAlgorithmGNN || doprocessAlgorithmGNNwExtra) { + if (doprocessAlgorithmGNN || doprocessAlgorithmGNNwExtra || doprocessAlgorithmParT) { if (jet.pt() >= jetpTMin) { float dbRange; if (scoreML[jet.globalIndex()] < dbMin) { @@ -376,46 +387,46 @@ struct JetTaggerHFTask { // Set up the resolution function switch (resoFuncMatch) { case 0: - vecParamsData = (std::vector)paramsResoFuncData; - vecParamsCharmJetMC = (std::vector)paramsResoFuncCharmJetMC; - vecParamsBeautyJetMC = (std::vector)paramsResoFuncBeautyJetMC; - vecParamsLfJetMC = (std::vector)paramsResoFuncLfJetMC; + vecParamsData = paramsResoFuncData; + vecParamsCharmJetMC = paramsResoFuncCharmJetMC; + vecParamsBeautyJetMC = paramsResoFuncBeautyJetMC; + vecParamsLfJetMC = paramsResoFuncLfJetMC; LOG(info) << "defined parameters of resolution function: custom"; break; case 1: - vecParamsData = (std::vector)paramsResoFuncData; - vecParamsIncJetMC = (std::vector)paramsResoFuncIncJetMC; + vecParamsData = paramsResoFuncData; + vecParamsIncJetMC = paramsResoFuncIncJetMC; useResoFuncFromIncJet = true; LOG(info) << "defined parameters of resolution function: custom & use inclusive distribution"; break; case 2: // TODO - vecParamsData = (std::vector)paramsResoFuncData; + vecParamsData = paramsResoFuncData; vecParamsCharmJetMC = {282119.753, -0.065, 0.893, 11.608, 0.945, 8.029, 0.131, 6.244, 0.027}; vecParamsBeautyJetMC = {74901.583, -0.082, 0.874, 10.332, 0.941, 7.352, 0.097, 6.220, 0.022}; vecParamsLfJetMC = {1539435.343, -0.061, 0.896, 13.272, 1.034, 5.884, 0.004, 7.843, 0.090}; LOG(info) << "defined parameters of resolution function: PYTHIA8, MB, LHC23d1k"; break; case 3: // TODO - vecParamsData = (std::vector)paramsResoFuncData; + vecParamsData = paramsResoFuncData; vecParamsIncJetMC = {1908803.027, -0.059, 0.895, 13.467, 1.005, 8.867, 0.098, 6.929, 0.011}; LOG(info) << "defined parameters of resolution function: PYTHIA8, MB, LHC23d1k & use inclusive distribution"; useResoFuncFromIncJet = true; break; case 4: // TODO - vecParamsData = (std::vector)paramsResoFuncData; + vecParamsData = paramsResoFuncData; vecParamsCharmJetMC = {743719.121, -0.960, -0.240, 13.765, 1.314, 10.761, 0.293, 8.538, 0.052}; vecParamsBeautyJetMC = {88888.418, 0.256, 1.003, 10.185, 0.740, 8.216, 0.147, 7.228, 0.040}; vecParamsLfJetMC = {414860.372, -1.000, 0.285, 14.561, 1.464, 11.693, 0.339, 9.183, 0.052}; LOG(info) << "defined parameters of resolution function: PYTHIA8, JJ, weighted, LHC24g4"; break; case 5: // TODO - vecParamsData = (std::vector)paramsResoFuncData; + vecParamsData = paramsResoFuncData; vecParamsIncJetMC = {2211391.862, 0.360, 1.028, 13.019, 0.650, 11.151, 0.215, 9.462, 0.044}; LOG(info) << "defined parameters of resolution function: PYTHIA8, JJ, weighted, LHC24g4 & use inclusive distribution"; useResoFuncFromIncJet = true; break; case 6: // TODO - vecParamsData = (std::vector)paramsResoFuncData; + vecParamsData = paramsResoFuncData; for (size_t j = 0; j < resoFuncIncCCDB.size(); j++) { std::vector params; if (resoFuncIncCCDB[j]) { @@ -429,7 +440,7 @@ struct JetTaggerHFTask { useResoFuncFromIncJet = true; break; case 7: // TODO - vecParamsData = (std::vector)paramsResoFuncData; + vecParamsData = paramsResoFuncData; for (size_t j = 0; j < resoFuncBeautyCCDB.size(); j++) { std::vector params; if (resoFuncBeautyCCDB[j]) { @@ -525,9 +536,24 @@ struct JetTaggerHFTask { bMlResponse.init(); } + if (doprocessAlgorithmParT) { + bMlResponse.configure(binsPtMl, cutsMl, cutDirMl, nClassesMl); + if (loadModelsFromCCDB) { + ccdbApi.init(ccdbUrl); + bMlResponse.setModelPathsCCDB(onnxFileNames, ccdbApi, modelPathsCCDB, timestampCCDB); + } else { + bMlResponse.setModelPathsLocal(onnxFileNames); + } + bMlResponse.init(); + + LOGF(info, "ParT: loaded %s with %d output node(s); %d constituents, %s padding", onnxFileNames.value[0].c_str(), bMlResponse.getOutputNodes(), static_cast(parTCfg.nConstit.value), parTCfg.padWrap.value ? "wrap" : "zero"); + } + if (doprocessAlgorithmGNN || doprocessAlgorithmGNNwExtra) { tensorAlloc = o2::analysis::GNNBjetAllocator(nJetFeat.value, nTrkFeat.value, nClassesMl.value, nTrkOrigin.value, transformFeatureJetMean.value, transformFeatureJetStdev.value, transformFeatureTrkMean.value, transformFeatureTrkStdev.value, nJetConst, tfFuncTypeGNN.value); + } + if (doprocessAlgorithmGNN || doprocessAlgorithmGNNwExtra || doprocessAlgorithmParT) { registry.add("h2_count_db", "#it{D}_{b} underflow/overflow;Jet flavour;#it{D}_{b} range", {HistType::kTH2F, {{4, 0., 4.}, {3, 0., 3.}}}); auto h2CountDb = registry.get(HIST("h2_count_db")); h2CountDb->GetXaxis()->SetBinLabel(1, "b-jet"); @@ -558,7 +584,7 @@ struct JetTaggerHFTask { int nSVs = analysisJet.template secondaryVertices_as().size(); - jettaggingutilities::BJetParams jetparam = {analysisJet.pt(), analysisJet.eta(), analysisJet.phi(), static_cast(tracksParams.size()), static_cast(nSVs), analysisJet.mass()}; + jettaggingutilities::BJetParams jetparam = {analysisJet.pt(), analysisJet.eta(), analysisJet.phi(), static_cast(tracksParams.size()), nSVs, analysisJet.mass()}; tracksParams.resize(nJetConst); // resize to the number of inputs of the ML svsParams.resize(nJetConst); // resize to the number of inputs of the ML @@ -669,6 +695,42 @@ struct JetTaggerHFTask { } } + template + void analyzeJetAlgorithmParT(AnyJets const& jets, AnyTracks const& tracks) + { + o2::analysis::ParTBjetAllocator parTTensorAlloc(parTCfg.nConstit.value, parTCfg.nFeat.value, parTCfg.nVec.value, parTCfg.padWrap.value); + + for (const auto& jet : jets) { + std::vector> featConstit; + std::vector> vecConstit; + jettaggingutilities::analyzeJetTrackInfo4ParT(jet, tracks, featConstit, vecConstit, trackPtMin, trackDcaXYMax, trackDcaZMax); + + // Training applied a jet_nTracks >= 2 selection, so jets below it are out of distribution. + if (static_cast(featConstit.size()) < parTCfg.minTracks) { + scoreML[jet.globalIndex()] = -999.; + LOGF(debug, "doprocessAlgorithmParT, too few selected constituents (%d)", jet.globalIndex()); + continue; + } + + std::vector featData; + std::vector vecData; + std::vector maskData; + std::vector parTInput; + parTTensorAlloc.getParTInput(featConstit, vecConstit, featData, vecData, maskData, parTInput); + + // The exported graph applies the softmax itself, so the output is already + // [p_b, p_c, p_udsg] and must not be passed through getDb(), which would softmax again. + auto modelOutput = bMlResponse.getModelOutputMultiInput(parTInput, 0); + float db = jettaggingutilities::getDbFromProb(modelOutput, fC); + if (!std::isnan(db)) { + scoreML[jet.globalIndex()] = db; + } else { + scoreML[jet.globalIndex()] = 999.; + LOGF(debug, "doprocessAlgorithmParT, Db is NaN (%d)", jet.globalIndex()); + } + } + } + void processDummy(aod::JetCollisions const&) { } @@ -725,6 +787,12 @@ struct JetTaggerHFTask { } PROCESS_SWITCH(JetTaggerHFTask, processAlgorithmGNN, "Fill GNN evaluation score (D_b) for charged jets", false); + void processAlgorithmParT(JetTable const& jets, JetTracksExt const& jtracks) + { + analyzeJetAlgorithmParT(jets, jtracks); + } + PROCESS_SWITCH(JetTaggerHFTask, processAlgorithmParT, "Fill ParT evaluation score (D_b) for charged jets", false); + void processFillTables(std::conditional_t, JetTable>::iterator const& jet, JetTracksExt const& tracks) { fillTables(jet, tracks); diff --git a/PWGJE/TableProducer/mcOutlierRejector.cxx b/PWGJE/TableProducer/mcOutlierRejector.cxx index f0b7672c40a..7a149994a4d 100644 --- a/PWGJE/TableProducer/mcOutlierRejector.cxx +++ b/PWGJE/TableProducer/mcOutlierRejector.cxx @@ -47,7 +47,7 @@ struct McOutlierRejectorTask { std::vector collisionFlag; std::vector mcCollisionFlag; - int trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + int trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); void processSetupCollisionSelection(aod::JCollisions const& collisions) { diff --git a/PWGJE/TableProducer/quarkGluonJetsProducer.cxx b/PWGJE/TableProducer/quarkGluonJetsProducer.cxx index 92c2a05d92b..96305941c12 100644 --- a/PWGJE/TableProducer/quarkGluonJetsProducer.cxx +++ b/PWGJE/TableProducer/quarkGluonJetsProducer.cxx @@ -39,6 +39,7 @@ #include #include +#include #include #include @@ -47,6 +48,8 @@ #include #include #include +#include +#include #include namespace o2::aod @@ -94,7 +97,7 @@ using QGMLJet = QGMLJets::iterator; namespace qgmlconst { -DECLARE_SOA_INDEX_COLUMN(QGMLJet, qGMLJet); +DECLARE_SOA_INDEX_COLUMN_CUSTOM(QGMLJet, qGMLJet, "QGMLJET"); DECLARE_SOA_COLUMN(Pt, pt, float); DECLARE_SOA_COLUMN(P, p, float); DECLARE_SOA_COLUMN(Eta, eta, float); @@ -117,6 +120,7 @@ DECLARE_SOA_COLUMN(TofNSigmaPi, tofNSigmaPi, float); DECLARE_SOA_COLUMN(TofNSigmaKa, tofNSigmaKa, float); DECLARE_SOA_COLUMN(TofNSigmaPr, tofNSigmaPr, float); DECLARE_SOA_COLUMN(TofBeta, tofBeta, float); +DECLARE_SOA_COLUMN(HasTruthMatch, hasTruthMatch, bool); DECLARE_SOA_COLUMN(TruthPdg, truthPdg, int32_t); DECLARE_SOA_COLUMN(TruthPt, truthPt, float); DECLARE_SOA_COLUMN(TruthEta, truthEta, float); @@ -148,11 +152,96 @@ DECLARE_SOA_TABLE(QGMLConstituents, "AOD", "QGMLCONSTS", qgmlconst::TofNSigmaKa, qgmlconst::TofNSigmaPr, qgmlconst::TofBeta, + qgmlconst::HasTruthMatch, qgmlconst::TruthPdg, qgmlconst::TruthPt, qgmlconst::TruthEta, qgmlconst::TruthPhi, qgmlconst::IsPhysicalPrimary); + +namespace qgmltruthjet +{ +DECLARE_SOA_COLUMN(SourceMcCollisionId, sourceMcCollisionId, int64_t); +DECLARE_SOA_COLUMN(SourceJetId, sourceJetId, int64_t); +DECLARE_SOA_COLUMN(FlavorLabel, flavorLabel, int32_t); +DECLARE_SOA_COLUMN(LeadingPartonPdg, leadingPartonPdg, int32_t); +DECLARE_SOA_COLUMN(LeadingPartonPt, leadingPartonPt, float); +DECLARE_SOA_COLUMN(LeadingPartonDeltaR, leadingPartonDeltaR, float); +DECLARE_SOA_COLUMN(SubleadingPartonPdg, subleadingPartonPdg, int32_t); +DECLARE_SOA_COLUMN(SubleadingPartonPt, subleadingPartonPt, float); +DECLARE_SOA_COLUMN(SubleadingPartonDeltaR, subleadingPartonDeltaR, float); +DECLARE_SOA_COLUMN(NPartonsInCone, nPartonsInCone, int32_t); +DECLARE_SOA_COLUMN(LabelAmbiguous, labelAmbiguous, bool); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Area, area, float); +DECLARE_SOA_COLUMN(Radius, radius, float); +DECLARE_SOA_COLUMN(NConstituents, nConstituents, int32_t); +DECLARE_SOA_COLUMN(NPions, nPions, int32_t); +DECLARE_SOA_COLUMN(NKaons, nKaons, int32_t); +DECLARE_SOA_COLUMN(NProtons, nProtons, int32_t); +} // namespace qgmltruthjet +DECLARE_SOA_TABLE(QGMLTruthJets, "AOD", "QGMLTJET", + o2::soa::Index<>, + qgmltruthjet::SourceMcCollisionId, + qgmltruthjet::SourceJetId, + qgmltruthjet::FlavorLabel, + qgmltruthjet::LeadingPartonPdg, + qgmltruthjet::LeadingPartonPt, + qgmltruthjet::LeadingPartonDeltaR, + qgmltruthjet::SubleadingPartonPdg, + qgmltruthjet::SubleadingPartonPt, + qgmltruthjet::SubleadingPartonDeltaR, + qgmltruthjet::NPartonsInCone, + qgmltruthjet::LabelAmbiguous, + qgmltruthjet::Pt, + qgmltruthjet::Eta, + qgmltruthjet::Phi, + qgmltruthjet::Area, + qgmltruthjet::Radius, + qgmltruthjet::NConstituents, + qgmltruthjet::NPions, + qgmltruthjet::NKaons, + qgmltruthjet::NProtons); + +namespace qgmltruthconst +{ +DECLARE_SOA_INDEX_COLUMN_CUSTOM(QGMLTruthJet, qGMLTruthJet, "QGMLTJET"); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(DeltaEta, deltaEta, float); +DECLARE_SOA_COLUMN(DeltaPhi, deltaPhi, float); +DECLARE_SOA_COLUMN(DeltaR, deltaR, float); +DECLARE_SOA_COLUMN(PtFraction, ptFraction, float); +DECLARE_SOA_COLUMN(Pdg, pdg, int32_t); +DECLARE_SOA_COLUMN(Charge, charge, float); +DECLARE_SOA_COLUMN(IsPhysicalPrimary, isPhysicalPrimary, bool); +} // namespace qgmltruthconst +DECLARE_SOA_TABLE(QGMLTruthConstituents, "AOD", "QGMLTCONST", + qgmltruthconst::QGMLTruthJetId, + qgmltruthconst::Pt, + qgmltruthconst::Eta, + qgmltruthconst::Phi, + qgmltruthconst::DeltaEta, + qgmltruthconst::DeltaPhi, + qgmltruthconst::DeltaR, + qgmltruthconst::PtFraction, + qgmltruthconst::Pdg, + qgmltruthconst::Charge, + qgmltruthconst::IsPhysicalPrimary); + +namespace qgmlmatch +{ +DECLARE_SOA_INDEX_COLUMN_CUSTOM(QGMLJet, qGMLJet, "QGMLJET"); +DECLARE_SOA_INDEX_COLUMN_CUSTOM(QGMLTruthJet, qGMLTruthJet, "QGMLTJET"); +DECLARE_SOA_COLUMN(DeltaR, deltaR, float); +} // namespace qgmlmatch +DECLARE_SOA_TABLE(QGMLJetMatches, "AOD", "QGMLMATCH", + qgmlmatch::QGMLJetId, + qgmlmatch::QGMLTruthJetId, + qgmlmatch::DeltaR); } // namespace o2::aod using namespace o2; @@ -162,6 +251,11 @@ using namespace o2::framework; using MCDJetEvents = aod::JetCollisionsMCD; using ChargedMCDJets = soa::Join; +using ChargedMCPJets = soa::Join; +using MatchedChargedMCDJets = soa::Join; using JetTrackRefs = soa::Join; using HadronTracksMC = soa::Join qgMLJets; Produces qgMLConstituents; + Produces qgMLTruthJets; + Produces qgMLTruthConstituents; + Produces qgMLJetMatches; static constexpr double JetRadiusTolerance = 1.e-3; @@ -220,6 +317,11 @@ struct QuarkGluonJetsProducer { Configurable dropAmbiguousLabels{"dropAmbiguousLabels", false, "drop ambiguous labels"}; Preslice mcParticlesPerCollision = aod::jmcparticle::mcCollisionId; + Preslice truthJetsPerCollision = aod::jet::mcCollisionId; + + Configurable minTruthJetPt{"minTruthJetPt", 10.0, "minimum truth jet pT"}; + Configurable maxTruthJetPt{"maxTruthJetPt", 1000.0, "maximum truth jet pT"}; + Configurable truthPhysicalPrimariesOnly{"truthPhysicalPrimariesOnly", true, "retain physical primaries only"}; enum JetFlavor { UnknownJet = 0, QuarkJet = 1, @@ -239,7 +341,7 @@ struct QuarkGluonJetsProducer { void init(InitContext const&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); } template @@ -340,7 +442,8 @@ struct QuarkGluonJetsProducer { } void process(MCDJetEvents::iterator const& collision, - ChargedMCDJets const& detectorLevelJets, + MatchedChargedMCDJets const& detectorLevelJets, + ChargedMCPJets const& truthJets, JetTrackRefs const&, HadronTracksMC const&, aod::JetParticles const& jetParticles, @@ -358,6 +461,9 @@ struct QuarkGluonJetsProducer { return; } + const int64_t mcCollisionId = collision.mcCollisionId(); + std::vector> pendingMatches; + for (auto const& jet : detectorLevelJets) { const double jetRadius = static_cast(jet.r()) / 100.0; if (std::abs(jetRadius - static_cast(rJet)) > JetRadiusTolerance) { @@ -427,6 +533,32 @@ struct QuarkGluonJetsProducer { const auto qgMLJetId = qgMLJets.lastIndex(); + auto geoMatches = jet.matchedJetGeo_as(); + auto ptMatches = jet.matchedJetPt_as(); + float bestDR = std::numeric_limits::infinity(); + int64_t bestTruthSourceId = -1; + for (auto const& candidate : geoMatches) { + bool sharedMatch = false; + for (auto const& ptCandidate : ptMatches) { + if (candidate.globalIndex() == ptCandidate.globalIndex()) { + sharedMatch = true; + break; + } + } + if (!sharedMatch || candidate.mcCollisionId() != mcCollisionId) { + continue; + } + const auto dPhi = RecoDecay::constrainAngle(candidate.phi() - jet.phi(), -PI); + const float dR = std::hypot(candidate.eta() - jet.eta(), dPhi); + if (dR < bestDR) { + bestDR = dR; + bestTruthSourceId = candidate.globalIndex(); + } + } + if (bestTruthSourceId >= 0) { + pendingMatches.emplace_back(qgMLJetId, bestTruthSourceId, bestDR); + } + for (auto const& jtrack : jet.tracks_as()) { auto track = jtrack.track_as(); @@ -477,6 +609,7 @@ struct QuarkGluonJetsProducer { track.hasTOF() ? static_cast(track.tofNSigmaKa()) : missingTOF, track.hasTOF() ? static_cast(track.tofNSigmaPr()) : missingTOF, track.hasTOF() ? static_cast(track.beta()) : missingTOF, + track.has_mcParticle(), truthPdg, truthPt, truthEta, @@ -484,6 +617,91 @@ struct QuarkGluonJetsProducer { isPhysicalPrimary); } } + + auto truthJetsInCollision = truthJets.sliceBy(truthJetsPerCollision, mcCollisionId); + std::unordered_map truthOutputIndex; + for (auto const& truthJet : truthJetsInCollision) { + const double radius = static_cast(truthJet.r()) / 100.0; + if (std::abs(radius - static_cast(rJet)) > JetRadiusTolerance) { + continue; + } + if (isppRefAnalysis && std::abs(truthJet.eta()) > cfgEtaJetMax) { + continue; + } + if (!isppRefAnalysis && std::abs(truthJet.eta()) + radius > maxEta - deltaEtaEdge) { + continue; + } + if (truthJet.pt() < minTruthJetPt || truthJet.pt() > maxTruthJetPt) { + continue; + } + if (applyAreaCut && truthJet.area() / (PI * radius * radius) < minNormalizedJetArea) { + continue; + } + + struct SelectedTruthParticle { + float pt, eta, phi, charge; + int32_t pdg; + bool primary; + }; + std::vector selected; + int32_t nPions = 0, nKaons = 0, nProtons = 0; + for (auto const& particle : truthJet.tracks_as()) { + if (truthPhysicalPrimariesOnly && !particle.isPhysicalPrimary()) { + continue; + } + if (particle.pt() < minPt || particle.pt() > maxPt || + particle.eta() < minEta || particle.eta() > maxEta) { + continue; + } + auto* pdgInfo = TDatabasePDG::Instance()->GetParticle(particle.pdgCode()); + if (!pdgInfo) { + continue; + } + const float charge = static_cast(pdgInfo->Charge() / 3.0); + if (std::abs(charge) < 0.5f) { + continue; + } + const int apdg = std::abs(particle.pdgCode()); + nPions += (apdg == 211); + nKaons += (apdg == 321); + nProtons += (apdg == 2212); + selected.push_back({particle.pt(), particle.eta(), particle.phi(), charge, + particle.pdgCode(), particle.isPhysicalPrimary()}); + } + if (selected.empty()) { + continue; + } + auto collisionParticles = jetParticles.sliceBy(mcParticlesPerCollision, mcCollisionId); + auto flavor = getJetFlavorTag(truthJet, collisionParticles); + if (dropUnknownLabels && flavor.flavor == UnknownJet) { + continue; + } + if (dropAmbiguousLabels && flavor.ambiguous) { + continue; + } + qgMLTruthJets(mcCollisionId, truthJet.globalIndex(), + flavor.flavor, flavor.leadingPartonPdg, flavor.leadingPartonPt, + flavor.leadingPartonDeltaR, flavor.subleadingPartonPdg, + flavor.subleadingPartonPt, flavor.subleadingPartonDeltaR, + flavor.nPartonsInCone, flavor.ambiguous, + truthJet.pt(), truthJet.eta(), truthJet.phi(), truthJet.area(), + radius, static_cast(selected.size()), nPions, nKaons, nProtons); + const auto truthOutputId = qgMLTruthJets.lastIndex(); + truthOutputIndex.emplace(truthJet.globalIndex(), truthOutputId); + for (auto const& part : selected) { + const float dEta = part.eta - truthJet.eta(); + const float dPhi = RecoDecay::constrainAngle(part.phi - truthJet.phi(), -PI); + qgMLTruthConstituents(truthOutputId, part.pt, part.eta, part.phi, + dEta, dPhi, std::hypot(dEta, dPhi), + truthJet.pt() > 0 ? part.pt / truthJet.pt() : 0.f, + part.pdg, part.charge, part.primary); + } + } + for (auto const& [recoOutputId, sourceTruthId, dR] : pendingMatches) { + if (auto it = truthOutputIndex.find(sourceTruthId); it != truthOutputIndex.end()) { + qgMLJetMatches(recoOutputId, it->second, dR); + } + } } }; diff --git a/PWGJE/TableProducer/rhoEstimator.cxx b/PWGJE/TableProducer/rhoEstimator.cxx index c7f11e99b0c..1e340b41587 100644 --- a/PWGJE/TableProducer/rhoEstimator.cxx +++ b/PWGJE/TableProducer/rhoEstimator.cxx @@ -238,7 +238,7 @@ struct RhoEstimatorTask { continue; } inputParticles.clear(); - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, false, &candidate); auto [rho, rhoM] = bkgSub.estimateRhoAreaMedian(inputParticles, config.doSparse); rhoD0Table(rho, rhoM); @@ -270,7 +270,7 @@ struct RhoEstimatorTask { continue; } inputParticles.clear(); - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, false, &candidate); auto [rho, rhoM] = bkgSub.estimateRhoAreaMedian(inputParticles, config.doSparse); rhoDplusTable(rho, rhoM); @@ -303,7 +303,7 @@ struct RhoEstimatorTask { continue; } inputParticles.clear(); - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, false, &candidate); auto [rho, rhoM] = bkgSub.estimateRhoAreaMedian(inputParticles, config.doSparse); rhoDsTable(rho, rhoM); @@ -335,7 +335,7 @@ struct RhoEstimatorTask { continue; } inputParticles.clear(); - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, false, &candidate); auto [rho, rhoM] = bkgSub.estimateRhoAreaMedian(inputParticles, config.doSparse); rhoDstarTable(rho, rhoM); @@ -367,7 +367,7 @@ struct RhoEstimatorTask { continue; } inputParticles.clear(); - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, false, &candidate); auto [rho, rhoM] = bkgSub.estimateRhoAreaMedian(inputParticles, config.doSparse); rhoLcTable(rho, rhoM); @@ -399,7 +399,7 @@ struct RhoEstimatorTask { continue; } inputParticles.clear(); - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, false, &candidate); auto [rho, rhoM] = bkgSub.estimateRhoAreaMedian(inputParticles, config.doSparse); rhoB0Table(rho, rhoM); @@ -431,7 +431,7 @@ struct RhoEstimatorTask { continue; } inputParticles.clear(); - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, false, &candidate); auto [rho, rhoM] = bkgSub.estimateRhoAreaMedian(inputParticles, config.doSparse); rhoBplusTable(rho, rhoM); @@ -463,7 +463,7 @@ struct RhoEstimatorTask { continue; } inputParticles.clear(); - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, false, &candidate); auto [rho, rhoM] = bkgSub.estimateRhoAreaMedian(inputParticles, config.doSparse); rhoXicToXiPiPiTable(rho, rhoM); @@ -495,7 +495,7 @@ struct RhoEstimatorTask { continue; } inputParticles.clear(); - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, false, &candidate); auto [rho, rhoM] = bkgSub.estimateRhoAreaMedian(inputParticles, config.doSparse); rhoDielectronTable(rho, rhoM); diff --git a/PWGJE/TableProducer/slimTablesProducer.cxx b/PWGJE/TableProducer/slimTablesProducer.cxx index cbaf139646e..5cff154999b 100644 --- a/PWGJE/TableProducer/slimTablesProducer.cxx +++ b/PWGJE/TableProducer/slimTablesProducer.cxx @@ -146,8 +146,8 @@ struct SlimTablesProducer { histos.add("Ntracks_pT", "track pT distribution;#it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{200, -0.5, 199.5}}}, doSumw2); histos.add("Nparticles_pT", "particle pT distribution;#it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{200, -0.5, 199.5}}}, doSumw2); - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); } Produces slimCollisions; diff --git a/PWGJE/Tasks/CMakeLists.txt b/PWGJE/Tasks/CMakeLists.txt index 9a2f85caa4e..d3726eb2ed2 100644 --- a/PWGJE/Tasks/CMakeLists.txt +++ b/PWGJE/Tasks/CMakeLists.txt @@ -9,6 +9,12 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. add_library(JetSubstructureHFPCH OBJECT jetSubstructureHFPCH.cxx) +# The PCH compile is one of the heaviest translation units in the tree -- it +# instantiates the whole data model up front -- but add_library() does not put +# it in the 'analysis' job pool that o2physics_add_dpl_workflow() uses for the +# workflows. Left out, it runs unthrottled alongside the pooled compilations +# and defeats the per-job memory budget the pool exists to enforce. +set_property(TARGET JetSubstructureHFPCH PROPERTY JOB_POOL_COMPILE analysis) target_link_libraries(JetSubstructureHFPCH PUBLIC O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore) if(NOT DEFINED ENV{USE_RECC}) @@ -29,6 +35,12 @@ target_precompile_headers(JetSubstructureHFPCH PRIVATE endif() add_library(JetSubstructureHFOutputPCH OBJECT jetSubstructureHFOutputPCH.cxx) +# The PCH compile is one of the heaviest translation units in the tree -- it +# instantiates the whole data model up front -- but add_library() does not put +# it in the 'analysis' job pool that o2physics_add_dpl_workflow() uses for the +# workflows. Left out, it runs unthrottled alongside the pooled compilations +# and defeats the per-job memory budget the pool exists to enforce. +set_property(TARGET JetSubstructureHFOutputPCH PROPERTY JOB_POOL_COMPILE analysis) target_link_libraries(JetSubstructureHFOutputPCH PUBLIC O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore) if(NOT DEFINED ENV{USE_RECC}) @@ -86,6 +98,7 @@ o2physics_add_dpl_workflow(photon-isolation-qa COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(photon-charged-trigger-correlation SOURCES photonChargedTriggerCorrelation.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-emc-extensive-mc-qa @@ -94,10 +107,12 @@ o2physics_add_dpl_workflow(task-emc-extensive-mc-qa COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(photon-charged-trigger-producer SOURCES photonChargedTriggerProducer.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(hadron-photon-correlation SOURCES hadronPhotonCorrelation.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -222,6 +237,11 @@ if(FastJet_FOUND) REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(lf-jet-spectra + SOURCES lfJetSpectra.cxx + REUSE_FROM JetSubstructureHFPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore + COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-charged-qa SOURCES jetFinderQA.cxx REUSE_FROM JetSubstructureHFPCH @@ -299,6 +319,7 @@ if(FastJet_FOUND) COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-spectra-charged-gen SOURCES jetSpectraChargedGen.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(charged-jet-hadron @@ -397,6 +418,11 @@ if(FastJet_FOUND) REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(dq-fragmentation-function + SOURCES DqFragmentationFunction.cxx + REUSE_FROM JetSubstructureHFPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore + COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-planarflow SOURCES jetPlanarFlow.cxx REUSE_FROM JetSubstructureHFPCH @@ -471,6 +497,7 @@ if(FastJet_FOUND) COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-correlation-dmeson SOURCES jetCorrelationDmeson.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(hf-debug @@ -505,14 +532,26 @@ if(FastJet_FOUND) COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-hadrons-pid SOURCES jetHadronsPid.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGJECore FastJet::FastJet FastJet::Contrib O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-hf-ang-substructure SOURCES jetHFAngularity.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-upc-qa SOURCES jetUpcQa.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils O2Physics::SGCutParHolder COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(embedding-qa + SOURCES embeddingQA.cxx + REUSE_FROM JetDataModelPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(baryon-pair-in-jets + SOURCES baryonPairInJets.cxx + REUSE_FROM JetDataModelPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore + COMPONENT_NAME Analysis) endif() diff --git a/PWGJE/Tasks/DqFragmentationFunction.cxx b/PWGJE/Tasks/DqFragmentationFunction.cxx new file mode 100644 index 00000000000..18f03f8116f --- /dev/null +++ b/PWGJE/Tasks/DqFragmentationFunction.cxx @@ -0,0 +1,828 @@ +// Copyright 2019-2020 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. + +// Fragmentation function task for Jpsi mesons in Jets. +// +/// \author Andreas Ulvseth Mikalsen +// +#include "PWGJE/Core/JetCandidateUtilities.h" +#include "PWGJE/Core/JetDerivedDataUtilities.h" +#include "PWGJE/Core/JetUtilities.h" +#include "PWGJE/DataModel/Jet.h" +#include "PWGJE/DataModel/JetReducedData.h" +#include "PWGJE/DataModel/JetSubtraction.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +struct DqFragmentationFunctionTask { + // Jet Pt bins for multidimension histograms, not affected by masterswitch unlike other configurables. + // The first variable is not a bin edge, but a flag for variable width bins, the rest are bin edges. LEAVE AT 0 IN CONFIG! + ConfigurableAxis jetPtBins{"jetPtBins", {VARIABLE_WIDTH, 0., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., 17., 18., 19., 20., 25., 30., 35., 40.}, "Jet pT bin edges (GeV/c), used for multidimensional histograms"}; + + // Master switch + Configurable useCuts{"useCuts", true, "Enable or disable: eventselection, jetselection and dielectron selection cuts"}; + Configurable useMCPCuts{"useMCPCuts", false, "Enable or disable (truth only): eventselection, jetselection and dielectron selection cuts"}; + + // Event selection + Configurable eventSelections{"eventSelections", "NoTimeFrameBorder+NoITSROFrameBorder", "List of available event cuts JetDerivedDataUtilities.h line 113"}; + Configurable skipMBgapEvents{"skipMBgapEvents", true, "Skip MB gap events"}; + Configurable applyRCTflag{"applyRCTflag", true, "Apply RCT flag"}; + Configurable rctLabel{"rctLabel", "CBT_hadronPID", "RCT label to use for event selection"}; + Configurable vertexZCut{"vertexZCut", 10.f, "Maximum absolute primary vertex z (cm)"}; + + // Electron kinematics + Configurable electronPMin{"electronPMin", 1.f, "Minimum electron momentum (GeV/c)"}; + Configurable electronPairPtMin{"electronPairPtMin", 0.f, "Minimum electron pair transverse momentum (GeV/c)"}; + Configurable electronEtaMax{"electronEtaMax", 0.8f, "Maximum absolute electron pseudorapidity"}; + Configurable electronPairEtaMax{"electronPairEtaMax", 0.8f, "Maximum absolute electron pair pseudorapidity"}; + + // Electron TPC PID + Configurable nSigmaElLowPMin{"nSigmaElLowPMin", -2.5f, "Minimum electron n-sigma for p below threshold"}; + Configurable nSigmaElHighPMin{"nSigmaElHighPMin", -1.5f, "Minimum electron n-sigma for p above threshold"}; + Configurable nSigmaElMax{"nSigmaElMax", 3.f, "Maximum electron n-sigma"}; + Configurable electronPIDMomentumThreshold{"electronPIDMomentumThreshold", 5.f, "Momentum threshold for electron PID (GeV/c)"}; + + // Pion and proton rejection (low momentum only) + Configurable pionNSigmaAbsMin{"pionNSigmaAbsMin", 2.5f, "Minimum absolute pion TPC n-sigma"}; + Configurable protonNSigmaAbsMin{"protonNSigmaAbsMin", 3.f, "Minimum absolute proton TPC n-sigma"}; + + // Track DCA + Configurable dcaXYMax{"dcaXYMax", 0.5f, "Maximum absolute DCAxy (cm)"}; + Configurable dcaZMax{"dcaZMax", 0.5f, "Maximum absolute DCAz (cm)"}; + + // Track quality + Configurable tpcChi2Max{"tpcChi2Max", 4.f, "Maximum TPC chi2 per cluster"}; + Configurable tpcClustersMin{"tpcClustersMin", 70, "Minimum number of TPC clusters"}; + Configurable tpcClustersMax{"tpcClustersMax", 161, "Maximum number of TPC clusters"}; + + // ITS inner-barrel selection + Configurable nITSHitsFirst3{"nITSHitsFirst3", 1, "Minimum number of ITS inner-barrel hits (layers 0-2)"}; + Configurable nITSHitsAll7{"nITSHitsAll7", 1, "Minimum number of ITS inner-barrel hits (layers 0-6)"}; + Configurable requireITSFirstLayer{"requireITSFirstLayer", false, "Require a hit in the first ITS layer (bit 0=1)"}; + + // Jet selection + Configurable jetPtMin{"jetPtMin", 0.f, "Minimum jet transverse momentum (GeV/c)"}; + Configurable jetPtMax{"jetPtMax", 1.e6f, "Maximum jet transverse momentum (GeV/c)"}; + Configurable jetEtaMax{"jetEtaMax", 1.e6f, "Maximum absolute jet pseudorapidity"}; + + HistogramRegistry registry{"registry", + {{"Data/Jet/h_dielectron_jet_pt", ";p_{T,jet} (GeV/c);Counts", {HistType::kTH1F, {{200, 0., 80.}}}}, + {"Data/Jet/h_constituent_multiplicity_vs_jet_pt", "Jet constituent multiplicity vs p_{T};p_{T,jet} (GeV/c);N_{constituents}", {HistType::kTH2F, {{200, 0., 80.}, {101, -0.5, 100.5}}}}, + {"Data/Jet/h_sum_constituent_pt_vs_jet_pt", "Scalar constituent p_{T} sum vs jet p_{T};p_{T,jet} (GeV/c);#Sigma p_{T,constituent} (GeV/c)", {HistType::kTH2F, {{100, 0., 40.}, {100, 0., 40.}}}}, + {"Data/Jet/h_dielectron_jet_eta", ";#eta_{jet};Counts", {HistType::kTH1F, {{200, -1., 1.}}}}, + {"Data/Jet/h_dielectron_jet_phi", ";#phi_{jet};Counts", {HistType::kTH1F, {{128, 0., o2::constants::math::TwoPI}}}}, + {"Data/Jet/h_dielectron_jet_Fragmentation_z", ";z^{e^{+}e^{-},jet};dN/dz^{e^{+}e^{-},jet}", {HistType::kTH1F, {{100, 0., 1.}}}}, + {"Data/Jet/h_dielectron_jet_distance", ";#DeltaR_{e^{+}e^{-},jet};Counts", {HistType::kTH1F, {{100, 0., 1.}}}}, + {"Data/Jet/h_dielectron_jet_distance_vs_projection", ";#DeltaR_{e^{+}e^{-},jet};z^{e^{+}e^{-},jet}", {HistType::kTH2F, {{100, 0., 1.}, {100, 0., 1.}}}}, + {"Data/Jet/h_electron_pairs_per_jet", "Number of accepted electron pairs per jet;Electron pairs per jet;Jets", {HistType::kTH1F, {{10, -0.5, 9.5}}}}, + {"Data/Dielectron/h_dielectron_pt", ";p_{T,e^{+}e^{-}};dN/dp_{T,e^{+}e^{-}}", {HistType::kTH1F, {{200, 0., 80.}}}}, + {"Data/Dielectron/h_dielectron_pt_vs_jet_pt", "Dielectron p_{T} vs jet p_{T};p_{T,jet} (GeV/c);p_{T,e^{+}e^{-}} (GeV/c)", {HistType::kTH2F, {{200, 0., 80.}, {200, 0., 80.}}}}, + {"Data/Dielectron/h_dielectron_eta", ";#eta_{e^{+}e^{-}};Counts", {HistType::kTH1F, {{160, -0.8, 0.8}}}}, + {"Data/Dielectron/h_dielectron_phi", ";#phi_{e^{+}e^{-}};Counts", {HistType::kTH1F, {{128, 0., o2::constants::math::TwoPI}}}}, + {"Data/Dielectron/h_dielectron_mass", ";m_{e^{+}e^{-}} (GeV/c^{2});dN/dm_{e^{+}e^{-}}", {HistType::kTH1F, {{1000, 0., 10.}}}}, + {"Data/Dielectron/h_dielectron_dcaxy", "Electron daughter DCA_{xy};DCA_{xy} (cm);Tracks", {HistType::kTH1F, {{200, -1.5, 1.5}}}}, + {"Data/Dielectron/h_dielectron_dcaz", "Electron daughter DCA_{z};DCA_{z} (cm);Tracks", {HistType::kTH1F, {{200, -1.5, 1.5}}}}, + {"Data/Dielectron/h_dielectron_Lxy", ";L_{xy};dN/dL_{xy}", {HistType::kTH1F, {{1000, -2., 2.}}}}, + {"Data/Dielectron/h_dielectron_Lz", ";L_{z};dN/dL_{z}", {HistType::kTH1F, {{1000, -2., 2.}}}}, + {"Data/Dielectron/h_dielectron_Tau_xy", ";#tau_{xy};dN/d#tau_{xy}", {HistType::kTH1F, {{1000, -2., 2.}}}}, + {"Data/Dielectron/h_dielectron_Tau_z", ";#tau_{z};dN/d#tau_{z}", {HistType::kTH1F, {{1000, -2., 2.}}}}, + {"Data/SingleTrack/h_electron_pt", "Electron daughter p_{T};p_{T,e} (GeV/c);Counts", {HistType::kTH1F, {{200, 0., 20.}}}}, + {"Data/SingleTrack/h_electron_eta", "Electron daughter #eta;#eta_{e};Counts", {HistType::kTH1F, {{160, -0.8, 0.8}}}}, + {"Data/SingleTrack/h_electron_phi", "Electron daughter #phi;#phi_{e};Counts", {HistType::kTH1F, {{128, 0., o2::constants::math::TwoPI}}}}, + {"Data/SingleTrack/h_electron_tpc_nsigma", "Electron daughter TPC PID;TPC n#sigma_{e};Counts", {HistType::kTH1F, {{160, -8., 8.}}}}, + {"Data/SingleTrack/h_electron_tpc_nsigma_vs_pt", "Electron daughter TPC PID;p_{T,e} (GeV/c);TPC n#sigma_{e}", {HistType::kTH2F, {{200, 0., 20.}, {160, -8., 8.}}}}, + {"Data/SingleTrack/h_electron_tpc_ncls_found", "Electron daughter TPC clusters found;N_{TPC clusters};Tracks", {HistType::kTH1F, {{162, -0.5, 161.5}}}}, + {"Data/SingleTrack/h_electron_tpc_chi2_per_cluster", "Electron daughter TPC #chi^{2}/cluster;TPC #chi^{2}/cluster;Tracks", {HistType::kTH1F, {{200, 0., 10.}}}}, + {"Data/SingleTrack/h_electron_its_chi2_per_cluster", "Electron daughter ITS #chi^{2}/cluster;ITS #chi^{2}/cluster;Tracks", {HistType::kTH1F, {{200, 0., 10.}}}}, + {"Data/SingleTrack/h_jet_track_pt", "Jet constituent p_{T};p_{T,track} (GeV/c);Counts", {HistType::kTH1F, {{200, 0., 80.}}}}, + {"Data/SingleTrack/h_jet_track_eta", "Jet constituent #eta;#eta_{track};Counts", {HistType::kTH1F, {{180, -0.9, 0.9}}}}, + {"Data/SingleTrack/h_n_tracks_per_jet", "Charged constituents per jet;N_{constituents};Counts", {HistType::kTH1F, {{101, -0.5, 100.5}}}}, + + {"Matching/h_collision_counter", "Collision counter;Stage;Counts", {HistType::kTH1F, {{4, 0., 4.}}}}, + {"Matching/h_jet_counter", "Jet counter;Stage;Counts", {HistType::kTH1F, {{4, 0., 4.}}}}, + {"Matching/h_candidate_counter", "Candidate matching stages;Stage;Counts", {HistType::kTH1F, {{4, 0., 4.}}}}, + {"Matching/h_2D_prompt_z", "Prompt z comparison;z^{MCD};z^{MCP}", {HistType::kTH2F, {{20, 0., 1.}, {20, 0., 1.}}}}, + {"Matching/h_2D_prompt_jetpt", "Prompt jetpt;p_{T,jet}^{MCD};p_{T,jet}^{MCP};", {HistType::kTH2F, {{40, 0., 40.}, {40, 0., 40.}}}}, + {"Matching/h_2D_prompt_jpsipt", "Prompt J/#psi pT comparison;p_{T,J/#psi}^{MCD} (GeV/c);p_{T,J/#psi}^{MCP} (GeV/c)", {HistType::kTH2F, {{40, 0., 40.}, {40, 0., 40.}}}}, + {"Matching/h_2D_prompt_tauxy", "Prompt dielectron #tau_{xy};#tau_{xy}^{MCD};#tau_{xy}^{MCP}", {HistType::kTH2F, {{200, -2., 2.}, {200, -2., 2.}}}}, + {"Matching/h_2D_nonprompt_z", "Nonprompt z comparison;z^{MCD};z^{MCP}", {HistType::kTH2F, {{20, 0., 1.}, {20, 0., 1.}}}}, + {"Matching/h_2D_nonprompt_jetpt", "Nonprompt jetpt;p_{T,jet}^{MCD};p_{T,jet}^{MCP}", {HistType::kTH2F, {{40, 0., 40.}, {40, 0., 40.}}}}, + {"Matching/h_2D_nonprompt_jpsipt", "Nonprompt J/#psi pt;p_{T,J/#psi}^{MCD};p_{T,J/#psi}^{MCP}", {HistType::kTH2F, {{40, 0., 40.}, {40, 0., 40.}}}}, + {"Matching/h_2D_nonprompt_tauxy", "Nonprompt dielectron #tau_{xy};#tau_{xy}^{MCD};#tau_{xy}^{MCP}", {HistType::kTH2F, {{200, -2., 2.}, {200, -2., 2.}}}}, + + {"MCD/Jet/h_MCD_jet_pt", "MCD jet p_{T};p_{T,jet} (GeV/c);Counts", {HistType::kTH1F, {{200, 0., 80.}}}}, + {"MCD/Jet/h_constituent_multiplicity_vs_jet_pt", "MCD jet constituent multiplicity vs p_{T};p_{T,jet} (GeV/c);N_{constituents}", {HistType::kTH2F, {{200, 0., 80.}, {101, -0.5, 100.5}}}}, + {"MCD/Jet/h_sum_constituent_pt_vs_jet_pt", "MCD scalar constituent p_{T} sum vs jet p_{T};p_{T,jet} (GeV/c);#Sigma p_{T,constituent} (GeV/c)", {HistType::kTH2F, {{100, 0., 40.}, {100, 0., 40.}}}}, + {"MCD/Jet/h_MCD_jet_eta", "MCD jet #eta;#eta_{jet};Counts", {HistType::kTH1F, {{200, -1., 1.}}}}, + {"MCD/Jet/h_MCD_jet_phi", "MCD jet #phi;#phi_{jet};Counts", {HistType::kTH1F, {{128, 0., o2::constants::math::TwoPI}}}}, + {"MCD/Jet/h_MCD_z", "MCD z;z;Counts", {HistType::kTH1F, {{100, 0., 1.}}}}, + {"MCD/Jet/h_MCD_jet_distance", "MCD J/#psi-jet distance;#DeltaR_{e^{+}e^{-},jet};Counts", {HistType::kTH1F, {{100, 0., 1.}}}}, + {"MCD/Jet/h_MCD_jet_distance_vs_projection", ";#DeltaR_{e^{+}e^{-},jet};z^{e^{+}e^{-},jet}", {HistType::kTH2F, {{100, 0., 1.}, {100, 0., 1.}}}}, + {"MCD/Jet/h_electron_pairs_per_jet", "Number of accepted electron pairs per jet;Electron pairs per jet;Jets", {HistType::kTH1F, {{10, -0.5, 9.5}}}}, + {"MCD/Dielectron/h_MCD_dielectron_pt", "MCD J/#psi p_{T};p_{T,J/#psi} (GeV/c);Counts", {HistType::kTH1F, {{200, 0., 80.}}}}, + {"MCD/Dielectron/h_dielectron_pt_vs_jet_pt", "MCD dielectron p_{T} vs jet p_{T};p_{T,jet} (GeV/c);p_{T,e^{+}e^{-}} (GeV/c)", {HistType::kTH2F, {{200, 0., 80.}, {200, 0., 80.}}}}, + {"MCD/Dielectron/h_MCD_dielectron_eta", "MCD dielectron #eta;#eta_{ee};Counts", {HistType::kTH1F, {{160, -0.8, 0.8}}}}, + {"MCD/Dielectron/h_MCD_dielectron_mass", "MCD J/#psi mass;m_{ee} (GeV/c^{2});Counts", {HistType::kTH1F, {{1000, 0., 10.}}}}, + {"MCD/Dielectron/h_MCD_dielectron_dcaxy", "MCD electron daughter DCA_{xy};DCA_{xy} (cm);Tracks", {HistType::kTH1F, {{200, -1.5, 1.5}}}}, + {"MCD/Dielectron/h_MCD_dielectron_dcaz", "MCD electron daughter DCA_{z};DCA_{z} (cm);Tracks", {HistType::kTH1F, {{200, -1.5, 1.5}}}}, + {"MCD/Dielectron/h_MCD_dielectron_origin", "MCD J/#psi origin;origin;Counts", {HistType::kTH1F, {{3, 0., 3.}}}}, + {"MCD/Dielectron/h_MCD_dielectron_Lxy", "MCD J/#psi L_{xy};L_{xy} (cm);Counts", {HistType::kTH1F, {{1000, -2., 2.}}}}, + {"MCD/Dielectron/h_MCD_dielectron_Lz", "MCD J/#psi L_{z};L_{z};Counts", {HistType::kTH1F, {{1000, -2., 2.}}}}, + {"MCD/Dielectron/h_MCD_dielectron_tau_xy", "MCD J/#psi #tau_{xy};#tau_{xy};Counts", {HistType::kTH1F, {{1000, -2., 2.}}}}, + {"MCD/Dielectron/h_MCD_dielectron_tau_z", "MCD J/#psi #tau_{z};#tau_{z};Counts", {HistType::kTH1F, {{1000, -2., 2.}}}}, + {"MCD/SingleTrack/h_MCD_electron_pt", "MCD electron daughter p_{T};p_{T,e} (GeV/c);Counts", {HistType::kTH1F, {{200, 0., 20.}}}}, + {"MCD/SingleTrack/h_MCD_electron_eta", "MCD electron daughter #eta;#eta_{e};Counts", {HistType::kTH1F, {{160, -0.8, 0.8}}}}, + {"MCD/SingleTrack/h_MCD_electron_phi", "MCD electron daughter #phi;#phi_{e};Counts", {HistType::kTH1F, {{128, 0., o2::constants::math::TwoPI}}}}, + {"MCD/SingleTrack/h_MCD_electron_tpc_nsigma", "MCD electron daughter TPC PID;TPC n#sigma_{e};Counts", {HistType::kTH1F, {{160, -8., 8.}}}}, + {"MCD/SingleTrack/h_MCD_electron_tpc_nsigma_vs_pt", "MCD electron daughter TPC PID;p_{T,e} (GeV/c);TPC n#sigma_{e}", {HistType::kTH2F, {{200, 0., 20.}, {160, -8., 8.}}}}, + {"MCD/SingleTrack/h_MCD_electron_tpc_ncls_found", "MCD electron daughter TPC clusters found;N_{TPC clusters};Tracks", {HistType::kTH1F, {{162, -0.5, 161.5}}}}, + {"MCD/SingleTrack/h_MCD_electron_tpc_chi2_per_cluster", "MCD electron daughter TPC #chi^{2}/cluster;TPC #chi^{2}/cluster;Tracks", {HistType::kTH1F, {{200, 0., 10.}}}}, + {"MCD/SingleTrack/h_MCD_electron_its_chi2_per_cluster", "MCD electron daughter ITS #chi^{2}/cluster;ITS #chi^{2}/cluster;Tracks", {HistType::kTH1F, {{200, 0., 10.}}}}, + {"MCD/SingleTrack/h_MCD_jet_track_pt", "MCD jet constituent p_{T};p_{T,track} (GeV/c);Counts", {HistType::kTH1F, {{200, 0., 80.}}}}, + {"MCD/SingleTrack/h_MCD_jet_track_eta", "MCD jet constituent #eta;#eta_{track};Counts", {HistType::kTH1F, {{180, -0.9, 0.9}}}}, + {"MCD/SingleTrack/h_MCD_n_tracks_per_jet", "MCD charged constituents per jet;N_{constituents};Counts", {HistType::kTH1F, {{101, -0.5, 100.5}}}}, + + {"MCP/Jet/h_MCP_jet_pt", "MCP jet p_{T};p_{T,jet} (GeV/c);Counts", {HistType::kTH1F, {{200, 0., 80.}}}}, + {"MCP/Jet/h_constituent_multiplicity_vs_jet_pt", "MCP jet constituent multiplicity vs p_{T};p_{T,jet} (GeV/c);N_{constituents}", {HistType::kTH2F, {{200, 0., 80.}, {101, -0.5, 100.5}}}}, + {"MCP/Jet/h_sum_constituent_pt_vs_jet_pt", "MCP scalar constituent p_{T} sum vs jet p_{T};p_{T,jet} (GeV/c);#Sigma p_{T,constituent} (GeV/c)", {HistType::kTH2F, {{100, 0., 40.}, {100, 0., 40.}}}}, + {"MCP/Jet/h_MCP_jet_eta", "MCP jet #eta;#eta_{jet};Counts", {HistType::kTH1F, {{200, -1., 1.}}}}, + {"MCP/Jet/h_MCP_jet_phi", "MCP jet #phi;#phi_{jet};Counts", {HistType::kTH1F, {{128, 0., o2::constants::math::TwoPI}}}}, + {"MCP/Jet/h_MCP_z", "MCP z;z;Counts", {HistType::kTH1F, {{100, 0., 1.}}}}, + {"MCP/Jet/h_MCP_jet_distance", "MCP J/#psi-jet distance;#DeltaR_{e^{+}e^{-},jet};Counts", {HistType::kTH1F, {{100, 0., 1.}}}}, + {"MCP/Jet/h_MCP_jet_distance_vs_projection", ";#DeltaR_{e^{+}e^{-},jet};z^{e^{+}e^{-},jet}", {HistType::kTH2F, {{100, 0., 1.}, {100, 0., 1.}}}}, + {"MCP/Jet/h_electron_pairs_per_jet", "Number of accepted electron pairs per jet;Electron pairs per jet;Jets", {HistType::kTH1F, {{10, -0.5, 9.5}}}}, + {"MCP/Dielectron/h_MCP_jpsi_pt", "MCP J/#psi p_{T};p_{T,J/#psi} (GeV/c);Counts", {HistType::kTH1F, {{200, 0., 80.}}}}, + {"MCP/Dielectron/h_dielectron_pt_vs_jet_pt", "MCP dielectron p_{T} vs jet p_{T};p_{T,jet} (GeV/c);p_{T,e^{+}e^{-}} (GeV/c)", {HistType::kTH2F, {{200, 0., 80.}, {200, 0., 80.}}}}, + {"MCP/Dielectron/h_MCP_dielectron_eta", "MCP dielectron #eta;#eta_{ee};Counts", {HistType::kTH1F, {{160, -0.8, 0.8}}}}, + {"MCP/Dielectron/h_MCP_dielectron_mass", "MCP dielectron mass;m_{ee} (GeV/c^{2});Counts", {HistType::kTH1F, {{1000, 0., 10.}}}}, + {"MCP/Dielectron/h_MCP_dielectron_origin", "MCP J/#psi origin;origin;Counts", {HistType::kTH1F, {{3, 0., 3.}}}}, + {"MCP/Dielectron/h_MCP_jpsi_lxy", "MCP L_{xy};L_{xy} (cm);Counts", {HistType::kTH1F, {{1000, -2., 2.}}}}, + {"MCP/Dielectron/h_MCP_jpsi_tauxy", "MCP #tau_{xy};#tau_{xy};Counts", {HistType::kTH1F, {{1000, -2., 2.}}}}, + {"MCP/SingleTrack/h_MCP_jet_particle_pt_ElectronPairInfoMissing", "MCP jet constituent p_{T};p_{T,particle} (GeV/c);Counts", {HistType::kTH1F, {{200, 0., 80.}}}}, + {"MCP/SingleTrack/h_MCP_jet_particle_eta_ElectronPairInfoMissing", "MCP jet constituent #eta;#eta_{particle};Counts", {HistType::kTH1F, {{180, -0.9, 0.9}}}}, + {"MCP/SingleTrack/h_MCP_n_particles_per_jet", "MCP constituents per jet;N_{constituents};Counts", {HistType::kTH1F, {{101, -0.5, 100.5}}}}}}; + + std::vector eventSelectionBits; + + void init(InitContext const&) + { + // Add histograms using configurable jet pT binning, placed in init so jetPtBins are applied correctly. + registry.add("Data/Multidimensional/h_4d_histogram", "Dielectron fragmentation;p_{T,jet} (GeV/c);z;m_{ee} (GeV/c^{2});#tau_{xy}", HistType::kTHnF, {AxisSpec{jetPtBins}, {20, 0., 1.}, {150, 2.5, 3.7}, {250, -1., 1.}}); + registry.add("Matching/h_response_prompt", "Prompt J/#psi response;p_{T,jet}^{MCD};z^{MCD};p_{T,jet}^{MCP};z^{MCP}", HistType::kTHnF, {AxisSpec{jetPtBins}, {20, 0., 1.}, AxisSpec{jetPtBins}, {20, 0., 1.}}); + registry.add("Matching/h_response_nonprompt", "Nonprompt J/#psi response;p_{T,jet}^{MCD};z^{MCD};p_{T,jet}^{MCP};z^{MCP}", HistType::kTHnF, {AxisSpec{jetPtBins}, {20, 0., 1.}, AxisSpec{jetPtBins}, {20, 0., 1.}}); + registry.add("MCD/Multidimensional/h_MCD_5d_allParticles", "MCD J/#psi fragmentation;p_{T,jet};z;m_{ee};Tau_{xy};origin", HistType::kTHnF, {AxisSpec{jetPtBins}, {20, 0., 1.}, {150, 2.5, 3.7}, {250, -1., 1.}, {3, 0., 3.}}); + registry.add("MCD/Multidimensional/h_MCD_4d_PromptJpsi", "MCD J/#psi fragmentation promptJpsi;p_{T,jet};z;m_{ee};Tau_{xy}", HistType::kTHnF, {AxisSpec{jetPtBins}, {20, 0., 1.}, {150, 2.5, 3.7}, {250, -1., 1.}}); + registry.add("MCD/Multidimensional/h_MCD_4d_NonPromptJpsi", "MCD J/#psi fragmentation NonPromptJpsi;p_{T,jet};z;m_{ee};Tau_{xy}", HistType::kTHnF, {AxisSpec{jetPtBins}, {20, 0., 1.}, {150, 2.5, 3.7}, {250, -1., 1.}}); + registry.add("MCP/Multidimensional/h_MCP_4d_allParticles", "MCP J/#psi fragmentation;p_{T,jet};z;Tau_{xy};origin", HistType::kTHnF, {AxisSpec{jetPtBins}, {20, 0., 1.}, {250, -1., 1.}, {3, 0., 3.}}); + registry.add("MCP/Multidimensional/h_MCP_4d_PromptJpsi", "MCP J/#psi fragmentation promptJpsi;p_{T,jet};z;m_{ee};Tau_{xy}", HistType::kTHnF, {AxisSpec{jetPtBins}, {20, 0., 1.}, {150, 2.5, 3.7}, {250, -1., 1.}}); + registry.add("MCP/Multidimensional/h_MCP_4d_NonPromptJpsi", "MCP J/#psi fragmentation NonPromptJpsi;p_{T,jet};z;m_{ee};Tau_{xy}", HistType::kTHnF, {AxisSpec{jetPtBins}, {20, 0., 1.}, {150, 2.5, 3.7}, {250, -1., 1.}}); + + // Initialize event selection + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits( + static_cast(eventSelections)); + + // Set matching counter labels + auto collisionCounter = registry.get(HIST("Matching/h_collision_counter")); + collisionCounter->GetXaxis()->SetBinLabel(1, "MCP collisions"); + collisionCounter->GetXaxis()->SetBinLabel(2, "MCP collisions after collision cuts (if enabled)"); + collisionCounter->GetXaxis()->SetBinLabel(3, "matched MCDcollisions to MCP collisions"); + collisionCounter->GetXaxis()->SetBinLabel(4, "matched MCD collisions after cut (if enabled)"); + + auto jetCounter = registry.get(HIST("Matching/h_jet_counter")); + jetCounter->GetXaxis()->SetBinLabel(1, "MCD jets"); + jetCounter->GetXaxis()->SetBinLabel(2, "MCD jets after jet cuts"); + jetCounter->GetXaxis()->SetBinLabel(3, "MCD jets matched to MCP"); + jetCounter->GetXaxis()->SetBinLabel(4, "MCD jets matched to MCP after MCP cut (if enabled)"); + + auto candidateCounter = registry.get(HIST("Matching/h_candidate_counter")); + candidateCounter->GetXaxis()->SetBinLabel(1, "MCD candidates in matched jets"); + candidateCounter->GetXaxis()->SetBinLabel(2, "Candidates passing z and optional e cuts"); + candidateCounter->GetXaxis()->SetBinLabel(3, "MCD candidates matched to MCP"); + candidateCounter->GetXaxis()->SetBinLabel(4, "MCD candidates matched to MCP after MCP cut (if enabled)"); + } + + using DielectronMCDJets = soa::Join< + aod::DielectronChargedMCDetectorLevelJets, + aod::DielectronChargedMCDetectorLevelJetConstituents, + aod::DielectronChargedMCDetectorLevelJetsMatchedToDielectronChargedMCParticleLevelJets>; + + using DielectronMCPJets = soa::Join< + aod::DielectronChargedMCParticleLevelJets, + aod::DielectronChargedMCParticleLevelJetConstituents, + aod::DielectronChargedMCParticleLevelJetsMatchedToDielectronChargedMCDetectorLevelJets>; + + // Preslice detector-level collisions belonging to each MC collision + PresliceUnsorted collisionsPerMCCollisionPreslice = aod::jmccollisionlb::mcCollisionId; + // Preslice detector-level jets belonging to each detector-level collision + Preslice mcdJetsPerCollisionPreslice = aod::jet::collisionId; + + template + double calculateZ( + JetType const& jet, + JpsiType const& jpsi) + { + double z = (jet.pt() > 0.) ? (jpsi.pt()) / jet.pt() : -1.; + // avoiding z=1.0 overflowing into next bin. + if (z > 0.999) { + z = 0.9991; + } + return z; + } + + template + bool passesElectronCuts(const Candidate& candidate, int electronIndex) + { + // electronIndex = 1: first electron + // electronIndex = 2: second electron + const double pt = electronIndex == 1 + ? candidate.pt1() + : candidate.pt2(); + + const double eta = electronIndex == 1 + ? candidate.eta1() + : candidate.eta2(); + + // Momentum magnitude: p = pT * cosh(eta) (because dqtrigger 2025 uses p not pt) + const double p = pt * std::cosh(eta); + + const double nSigmaEl = electronIndex == 1 + ? candidate.tpcNSigmaEl1() + : candidate.tpcNSigmaEl2(); + + const double tpcChi2NCl = electronIndex == 1 + ? candidate.tpcChi2NCl1() + : candidate.tpcChi2NCl2(); + + const int tpcNClsFound = electronIndex == 1 + ? candidate.tpcNClsFound1() + : candidate.tpcNClsFound2(); + + const double dcaXY = electronIndex == 1 + ? candidate.dcaXY1() + : candidate.dcaXY2(); + + const double dcaZ = electronIndex == 1 + ? candidate.dcaZ1() + : candidate.dcaZ2(); + + const double nSigmaPi = electronIndex == 1 + ? candidate.tpcNSigmaPi1() + : candidate.tpcNSigmaPi2(); + + const double nSigmaPr = electronIndex == 1 + ? candidate.tpcNSigmaPr1() + : candidate.tpcNSigmaPr2(); + + const unsigned char itsClusterMap = electronIndex == 1 + ? candidate.itsClusterMap1() + : candidate.itsClusterMap2(); + + // Electron kinematic selection + if (p < electronPMin.value || + std::abs(eta) > electronEtaMax.value) { + return false; + } + + // Momentum-dependent electron TPC PID + const double nSigmaElMin = + p < electronPIDMomentumThreshold.value + ? nSigmaElLowPMin.value + : nSigmaElHighPMin.value; + + if (nSigmaEl < nSigmaElMin || + nSigmaEl > nSigmaElMax.value) { + return false; + } + + // Pion and proton rejection: low momentum only + if (p < electronPIDMomentumThreshold.value) { + + if (std::abs(nSigmaPi) < pionNSigmaAbsMin.value) { + return false; + } + + if (std::abs(nSigmaPr) < protonNSigmaAbsMin.value) { + return false; + } + } + + // TPC track quality + if (tpcChi2NCl > tpcChi2Max.value) { + return false; + } + + if (tpcNClsFound < tpcClustersMin.value || + tpcNClsFound > tpcClustersMax.value) { + return false; + } + + // Distance of closest approach + if (std::abs(dcaXY) > dcaXYMax.value) { + return false; + } + + if (std::abs(dcaZ) > dcaZMax.value) { + return false; + } + + // Hits in ITS + // Count hits in the first three ITS layers (bits 0, 1, 2). + const int nITSIBHits = + ((itsClusterMap & (1u << 0)) != 0) + + ((itsClusterMap & (1u << 1)) != 0) + + ((itsClusterMap & (1u << 2)) != 0); + + if (nITSIBHits < nITSHitsFirst3.value) { + return false; + } + // Count hits in the all 7 ITS layers (bits 0, 1, 2, 3, 4, 5, 6). + const int nITSIBHitsAllLayers = + ((itsClusterMap & (1u << 0)) != 0) + + ((itsClusterMap & (1u << 1)) != 0) + + ((itsClusterMap & (1u << 2)) != 0) + + ((itsClusterMap & (1u << 3)) != 0) + + ((itsClusterMap & (1u << 4)) != 0) + + ((itsClusterMap & (1u << 5)) != 0) + + ((itsClusterMap & (1u << 6)) != 0); + + if (nITSIBHitsAllLayers < nITSHitsAll7.value) { + return false; + } + + // Require a hit in the first ITS layer (bit 0). + if (requireITSFirstLayer.value && + (itsClusterMap & (1u << 0)) == 0) { + return false; + } + + // All cuts passed + return true; + } + template + bool passesPairCuts(const Candidate& candidate) + { + if (candidate.pt() < electronPairPtMin.value || + std::abs(candidate.eta()) > electronPairEtaMax.value) { + return false; + } + + return true; + } + + void FragmentationFunctionData(aod::JetCollision const& collision, + soa::Join const& jets, + aod::CandidatesDielectronData const&, + aod::JetTracks const&) + { + if (useCuts && (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBgapEvents.value, applyRCTflag.value, rctLabel.value) || !(std::abs(collision.posZ()) < vertexZCut))) { + return; + } + for (const auto& jet : jets) { + + if (useCuts) { + if (jet.pt() < jetPtMin || jet.pt() > jetPtMax || + std::abs(jet.eta()) > jetEtaMax) { + continue; + } + } + + const auto jetTracks = jet.template tracks_as(); + const auto dielectronCandidates = jet.template candidates_as(); + double sumConstituentPt = 0.; + int electronPairs = 0; + for (const auto& dielectronCandidate : dielectronCandidates) { + if (useCuts && + (!passesPairCuts(dielectronCandidate) || + !passesElectronCuts(dielectronCandidate, 1) || + !passesElectronCuts(dielectronCandidate, 2))) { + continue; + } + + const double z = calculateZ(jet, dielectronCandidate); + if (z < 0.) { + continue; + } + ++electronPairs; + sumConstituentPt += dielectronCandidate.pt1(); + sumConstituentPt += dielectronCandidate.pt2(); + + // calculating angular distance in eta-phi plane + const double axisDistance = + jetutilities::deltaR(jet, dielectronCandidate); + + // filling candidate histograms + registry.fill(HIST("Data/SingleTrack/h_jet_track_pt"), dielectronCandidate.pt1()); + registry.fill(HIST("Data/SingleTrack/h_jet_track_pt"), dielectronCandidate.pt2()); + registry.fill(HIST("Data/SingleTrack/h_jet_track_eta"), dielectronCandidate.eta1()); + registry.fill(HIST("Data/SingleTrack/h_jet_track_eta"), dielectronCandidate.eta2()); + registry.fill(HIST("Data/Jet/h_dielectron_jet_Fragmentation_z"), z); + registry.fill(HIST("Data/Jet/h_dielectron_jet_distance_vs_projection"), axisDistance, z); + registry.fill(HIST("Data/Jet/h_dielectron_jet_distance"), axisDistance); + + registry.fill(HIST("Data/Dielectron/h_dielectron_mass"), dielectronCandidate.mass()); + registry.fill(HIST("Data/Dielectron/h_dielectron_dcaxy"), dielectronCandidate.dcaXY1()); + registry.fill(HIST("Data/Dielectron/h_dielectron_dcaxy"), dielectronCandidate.dcaXY2()); + registry.fill(HIST("Data/Dielectron/h_dielectron_dcaz"), dielectronCandidate.dcaZ1()); + registry.fill(HIST("Data/Dielectron/h_dielectron_dcaz"), dielectronCandidate.dcaZ2()); + registry.fill(HIST("Data/Dielectron/h_dielectron_pt"), dielectronCandidate.pt()); + registry.fill(HIST("Data/Dielectron/h_dielectron_pt_vs_jet_pt"), jet.pt(), dielectronCandidate.pt()); + registry.fill(HIST("Data/Dielectron/h_dielectron_eta"), dielectronCandidate.eta()); + registry.fill(HIST("Data/Dielectron/h_dielectron_phi"), dielectronCandidate.phi()); + registry.fill(HIST("Data/Dielectron/h_dielectron_Lz"), dielectronCandidate.lz()); + registry.fill(HIST("Data/Dielectron/h_dielectron_Lxy"), dielectronCandidate.lxy()); + registry.fill(HIST("Data/Dielectron/h_dielectron_Tau_z"), dielectronCandidate.tauz()); + registry.fill(HIST("Data/Dielectron/h_dielectron_Tau_xy"), dielectronCandidate.tauxy()); + registry.fill(HIST("Data/SingleTrack/h_electron_pt"), dielectronCandidate.pt1()); + registry.fill(HIST("Data/SingleTrack/h_electron_pt"), dielectronCandidate.pt2()); + registry.fill(HIST("Data/SingleTrack/h_electron_eta"), dielectronCandidate.eta1()); + registry.fill(HIST("Data/SingleTrack/h_electron_eta"), dielectronCandidate.eta2()); + registry.fill(HIST("Data/SingleTrack/h_electron_phi"), dielectronCandidate.phi1()); + registry.fill(HIST("Data/SingleTrack/h_electron_phi"), dielectronCandidate.phi2()); + registry.fill(HIST("Data/SingleTrack/h_electron_tpc_nsigma"), dielectronCandidate.tpcNSigmaEl1()); + registry.fill(HIST("Data/SingleTrack/h_electron_tpc_nsigma"), dielectronCandidate.tpcNSigmaEl2()); + registry.fill(HIST("Data/SingleTrack/h_electron_tpc_nsigma_vs_pt"), dielectronCandidate.pt1(), dielectronCandidate.tpcNSigmaEl1()); + registry.fill(HIST("Data/SingleTrack/h_electron_tpc_nsigma_vs_pt"), dielectronCandidate.pt2(), dielectronCandidate.tpcNSigmaEl2()); + registry.fill(HIST("Data/SingleTrack/h_electron_tpc_ncls_found"), dielectronCandidate.tpcNClsFound1()); + registry.fill(HIST("Data/SingleTrack/h_electron_tpc_ncls_found"), dielectronCandidate.tpcNClsFound2()); + registry.fill(HIST("Data/SingleTrack/h_electron_tpc_chi2_per_cluster"), dielectronCandidate.tpcChi2NCl1()); + registry.fill(HIST("Data/SingleTrack/h_electron_tpc_chi2_per_cluster"), dielectronCandidate.tpcChi2NCl2()); + registry.fill(HIST("Data/SingleTrack/h_electron_its_chi2_per_cluster"), dielectronCandidate.itsChi2NCl1()); + registry.fill(HIST("Data/SingleTrack/h_electron_its_chi2_per_cluster"), dielectronCandidate.itsChi2NCl2()); + registry.fill(HIST("Data/Multidimensional/h_4d_histogram"), jet.pt(), z, dielectronCandidate.mass(), dielectronCandidate.tauxy()); + } // end of loop over dielectron candidates + registry.fill(HIST("Data/Jet/h_electron_pairs_per_jet"), electronPairs); + if (electronPairs == 0) { + continue; + } + for (const auto& track : jetTracks) { + sumConstituentPt += track.pt(); + registry.fill(HIST("Data/SingleTrack/h_jet_track_pt"), track.pt()); + registry.fill(HIST("Data/SingleTrack/h_jet_track_eta"), track.eta()); + } + registry.fill(HIST("Data/Jet/h_dielectron_jet_pt"), jet.pt()); + registry.fill(HIST("Data/Jet/h_dielectron_jet_eta"), jet.eta()); + registry.fill(HIST("Data/Jet/h_dielectron_jet_phi"), jet.phi()); + const int constituentMultiplicity = static_cast(jetTracks.size()) + 2 * electronPairs; // adding 2 for the dielectron candidate + registry.fill(HIST("Data/Jet/h_constituent_multiplicity_vs_jet_pt"), jet.pt(), constituentMultiplicity); + registry.fill(HIST("Data/Jet/h_sum_constituent_pt_vs_jet_pt"), jet.pt(), sumConstituentPt); + registry.fill(HIST("Data/SingleTrack/h_n_tracks_per_jet"), constituentMultiplicity); + } // end of loop over jets + } + + PROCESS_SWITCH(DqFragmentationFunctionTask, FragmentationFunctionData, "fragmentation function data", false); + + void FragmentationFunctionMCD( + aod::JetCollisionMCD const& collision, + soa::Join const& jets, + aod::CandidatesDielectronMCD const&, + aod::JetTracksMCD const&) + { + // Event selection + if (useCuts && (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBgapEvents.value, applyRCTflag.value, rctLabel.value) || !(std::abs(collision.posZ()) < vertexZCut))) { + return; + } + + // Loop over detector-level jets + for (const auto& jet : jets) { + // Optional jet selection + if (useCuts) { + if (jet.pt() < jetPtMin || jet.pt() > jetPtMax || + std::abs(jet.eta()) > jetEtaMax) { + continue; + } + } + const auto jetTracks = jet.template tracks_as(); + const auto dielectronCandidates = jet.template candidates_as(); + double sumConstituentPt = 0.; + int electronPairs = 0; + for (const auto& dielectronCandidate : dielectronCandidates) { + if (useCuts && + (!passesPairCuts(dielectronCandidate) || + !passesElectronCuts(dielectronCandidate, 1) || + !passesElectronCuts(dielectronCandidate, 2))) { + continue; + } + const double z = calculateZ(jet, dielectronCandidate); + if (z < 0.) { + continue; + } + sumConstituentPt += dielectronCandidate.pt1(); + sumConstituentPt += dielectronCandidate.pt2(); + ++electronPairs; + + const double axisDistance = jetutilities::deltaR(jet, dielectronCandidate); + + // Fill candidate histograms + registry.fill(HIST("MCD/SingleTrack/h_MCD_jet_track_pt"), dielectronCandidate.pt1()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_jet_track_pt"), dielectronCandidate.pt2()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_jet_track_eta"), dielectronCandidate.eta1()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_jet_track_eta"), dielectronCandidate.eta2()); + registry.fill(HIST("MCD/Jet/h_MCD_z"), z); + registry.fill(HIST("MCD/Jet/h_MCD_jet_distance"), axisDistance); + registry.fill(HIST("MCD/Jet/h_MCD_jet_distance_vs_projection"), axisDistance, z); + registry.fill(HIST("MCD/Dielectron/h_MCD_dielectron_mass"), dielectronCandidate.mass()); + registry.fill(HIST("MCD/Dielectron/h_MCD_dielectron_dcaxy"), dielectronCandidate.dcaXY1()); + registry.fill(HIST("MCD/Dielectron/h_MCD_dielectron_dcaxy"), dielectronCandidate.dcaXY2()); + registry.fill(HIST("MCD/Dielectron/h_MCD_dielectron_dcaz"), dielectronCandidate.dcaZ1()); + registry.fill(HIST("MCD/Dielectron/h_MCD_dielectron_dcaz"), dielectronCandidate.dcaZ2()); + registry.fill(HIST("MCD/Dielectron/h_MCD_dielectron_pt"), dielectronCandidate.pt()); + registry.fill(HIST("MCD/Dielectron/h_dielectron_pt_vs_jet_pt"), jet.pt(), dielectronCandidate.pt()); + registry.fill(HIST("MCD/Dielectron/h_MCD_dielectron_eta"), dielectronCandidate.eta()); + registry.fill(HIST("MCD/Dielectron/h_MCD_dielectron_Lxy"), dielectronCandidate.lxy()); + registry.fill(HIST("MCD/Dielectron/h_MCD_dielectron_Lz"), dielectronCandidate.lz()); + registry.fill(HIST("MCD/Dielectron/h_MCD_dielectron_tau_xy"), dielectronCandidate.tauxy()); + registry.fill(HIST("MCD/Dielectron/h_MCD_dielectron_tau_z"), dielectronCandidate.tauz()); + registry.fill(HIST("MCD/Dielectron/h_MCD_dielectron_origin"), dielectronCandidate.mcDecision()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_electron_pt"), dielectronCandidate.pt1()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_electron_pt"), dielectronCandidate.pt2()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_electron_eta"), dielectronCandidate.eta1()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_electron_eta"), dielectronCandidate.eta2()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_electron_phi"), dielectronCandidate.phi1()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_electron_phi"), dielectronCandidate.phi2()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_electron_tpc_nsigma"), dielectronCandidate.tpcNSigmaEl1()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_electron_tpc_nsigma"), dielectronCandidate.tpcNSigmaEl2()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_electron_tpc_nsigma_vs_pt"), dielectronCandidate.pt1(), dielectronCandidate.tpcNSigmaEl1()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_electron_tpc_nsigma_vs_pt"), dielectronCandidate.pt2(), dielectronCandidate.tpcNSigmaEl2()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_electron_tpc_ncls_found"), dielectronCandidate.tpcNClsFound1()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_electron_tpc_ncls_found"), dielectronCandidate.tpcNClsFound2()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_electron_tpc_chi2_per_cluster"), dielectronCandidate.tpcChi2NCl1()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_electron_tpc_chi2_per_cluster"), dielectronCandidate.tpcChi2NCl2()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_electron_its_chi2_per_cluster"), dielectronCandidate.itsChi2NCl1()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_electron_its_chi2_per_cluster"), dielectronCandidate.itsChi2NCl2()); + registry.fill(HIST("MCD/Multidimensional/h_MCD_5d_allParticles"), jet.pt(), z, dielectronCandidate.mass(), dielectronCandidate.tauxy(), dielectronCandidate.mcDecision()); + if (dielectronCandidate.mcDecision() == 1) { + registry.fill(HIST("MCD/Multidimensional/h_MCD_4d_PromptJpsi"), jet.pt(), z, dielectronCandidate.mass(), dielectronCandidate.tauxy()); + } else if (dielectronCandidate.mcDecision() == 2) { + registry.fill(HIST("MCD/Multidimensional/h_MCD_4d_NonPromptJpsi"), jet.pt(), z, dielectronCandidate.mass(), dielectronCandidate.tauxy()); + } + } // end of loop over dielectron candidates + registry.fill(HIST("MCD/Jet/h_electron_pairs_per_jet"), electronPairs); + if (electronPairs == 0) { + continue; + } + for (const auto& track : jetTracks) { + sumConstituentPt += track.pt(); + registry.fill(HIST("MCD/SingleTrack/h_MCD_jet_track_pt"), track.pt()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_jet_track_eta"), track.eta()); + } + registry.fill(HIST("MCD/Jet/h_MCD_jet_pt"), jet.pt()); + registry.fill(HIST("MCD/Jet/h_MCD_jet_eta"), jet.eta()); + registry.fill(HIST("MCD/Jet/h_MCD_jet_phi"), jet.phi()); + const int constituentMultiplicity = static_cast(jetTracks.size()) + 2 * electronPairs; // adding 2 for the dielectron candidate + registry.fill(HIST("MCD/Jet/h_constituent_multiplicity_vs_jet_pt"), jet.pt(), constituentMultiplicity); + registry.fill(HIST("MCD/Jet/h_sum_constituent_pt_vs_jet_pt"), jet.pt(), sumConstituentPt); + registry.fill(HIST("MCD/SingleTrack/h_MCD_n_tracks_per_jet"), constituentMultiplicity); + } // end of loop over jets + } + + PROCESS_SWITCH(DqFragmentationFunctionTask, FragmentationFunctionMCD, "Jpsi fragmentation function detector-level MC", false); + + void FragmentationFunctionMCP( + aod::JetMcCollision const& mccollision, + soa::Join const& jets, + aod::CandidatesDielectronMCP const&, + aod::JetParticles const&) + { + // Event selection + if (useMCPCuts && (!jetderiveddatautilities::selectCollision(mccollision, eventSelectionBits, skipMBgapEvents.value, applyRCTflag.value, rctLabel.value) || !(std::abs(mccollision.posZ()) < vertexZCut))) { + return; + } + for (const auto& jet : jets) { + if (useMCPCuts) { + if (jet.pt() < jetPtMin || jet.pt() > jetPtMax || + std::abs(jet.eta()) > jetEtaMax) { + continue; + } + } + const auto jetParticles = jet.template tracks_as(); + const auto dielectronCandidates = jet.template candidates_as(); + int electronPairs = 0; + double sumConstituentPt = 0.; + for (const auto& dielectronCandidate : dielectronCandidates) { + if (useMCPCuts && !passesPairCuts(dielectronCandidate)) { + continue; + } + const double z = calculateZ(jet, dielectronCandidate); + if (z < 0.) { + continue; + } + ++electronPairs; + + // Because I dont have access to the individual electron pt, I will use the dielectron candidate pt as a proxy for the sum of the two electrons' pt. + sumConstituentPt += dielectronCandidate.pt(); + + const double axisDistance = jetutilities::deltaR(jet, dielectronCandidate); + + // Calculate Lxy for the J/psi candidate + const double dx = dielectronCandidate.vx() - mccollision.posX(); + const double dy = dielectronCandidate.vy() - mccollision.posY(); + + const double pt = std::sqrt( + dielectronCandidate.px() * dielectronCandidate.px() + + dielectronCandidate.py() * dielectronCandidate.py()); + + double lxyMC = 0.; + double tauxyProjected = 0.; + + if (pt > 0.) { + lxyMC = (dx * dielectronCandidate.px() + + dy * dielectronCandidate.py()) / + pt; + + tauxyProjected = lxyMC * dielectronCandidate.m() / pt; + } + registry.fill(HIST("MCP/Dielectron/h_MCP_jpsi_pt"), dielectronCandidate.pt()); + registry.fill(HIST("MCP/Dielectron/h_dielectron_pt_vs_jet_pt"), jet.pt(), dielectronCandidate.pt()); + registry.fill(HIST("MCP/Dielectron/h_MCP_jpsi_lxy"), lxyMC); + registry.fill(HIST("MCP/Dielectron/h_MCP_jpsi_tauxy"), tauxyProjected); + registry.fill(HIST("MCP/Jet/h_MCP_z"), z); + registry.fill(HIST("MCP/Jet/h_MCP_jet_distance"), axisDistance); + registry.fill(HIST("MCP/Jet/h_MCP_jet_distance_vs_projection"), axisDistance, z); + registry.fill(HIST("MCP/Dielectron/h_MCP_dielectron_mass"), dielectronCandidate.m()); + registry.fill(HIST("MCP/Dielectron/h_MCP_dielectron_eta"), dielectronCandidate.eta()); + registry.fill(HIST("MCP/Dielectron/h_MCP_dielectron_origin"), dielectronCandidate.origin()); + registry.fill(HIST("MCP/Multidimensional/h_MCP_4d_allParticles"), jet.pt(), z, tauxyProjected, dielectronCandidate.origin()); + if (dielectronCandidate.origin() == 1) { + registry.fill(HIST("MCP/Multidimensional/h_MCP_4d_PromptJpsi"), jet.pt(), z, dielectronCandidate.m(), tauxyProjected); + } else if (dielectronCandidate.origin() == 2) { + registry.fill(HIST("MCP/Multidimensional/h_MCP_4d_NonPromptJpsi"), jet.pt(), z, dielectronCandidate.m(), tauxyProjected); + } + } // end of loop over dielectron candidates + registry.fill(HIST("MCP/Jet/h_electron_pairs_per_jet"), electronPairs); + if (electronPairs == 0) { + continue; + } + for (const auto& particle : jetParticles) { + sumConstituentPt += particle.pt(); + registry.fill(HIST("MCP/SingleTrack/h_MCP_jet_particle_pt_ElectronPairInfoMissing"), particle.pt()); // named ElectronPairInfoMissing, because for MCP, I don't have access to the individual electron pt. + registry.fill(HIST("MCP/SingleTrack/h_MCP_jet_particle_eta_ElectronPairInfoMissing"), particle.eta()); + } + const int constituentMultiplicity = static_cast(jetParticles.size()) + 2 * electronPairs; + registry.fill(HIST("MCP/Jet/h_constituent_multiplicity_vs_jet_pt"), jet.pt(), constituentMultiplicity); + registry.fill(HIST("MCP/Jet/h_sum_constituent_pt_vs_jet_pt"), jet.pt(), sumConstituentPt); + registry.fill(HIST("MCP/Jet/h_MCP_jet_pt"), jet.pt()); + registry.fill(HIST("MCP/Jet/h_MCP_jet_eta"), jet.eta()); + registry.fill(HIST("MCP/Jet/h_MCP_jet_phi"), jet.phi()); + registry.fill(HIST("MCP/SingleTrack/h_MCP_n_particles_per_jet"), constituentMultiplicity); + } // end of loop over jets + } + + PROCESS_SWITCH(DqFragmentationFunctionTask, FragmentationFunctionMCP, "Jpsi fragmentation function particle-level MC", false); + + void processMcMatching( + aod::JetMcCollisions const& mccollisions, + aod::JetCollisionsMCD const& collisions, + DielectronMCDJets const& mcdjets, + DielectronMCPJets const&, + aod::CandidatesDielectronMCD const&, + aod::CandidatesDielectronMCP const&, + aod::JetTracksMCD const& tracksMCD, + aod::JetParticles const& particlesMCP) + { + for (const auto& mccollision : mccollisions) { + registry.fill(HIST("Matching/h_collision_counter"), 0.5); + + if (useMCPCuts && (!jetderiveddatautilities::selectCollision(mccollision, eventSelectionBits, skipMBgapEvents.value, applyRCTflag.value, rctLabel.value) || !(std::abs(mccollision.posZ()) < vertexZCut))) { + continue; + } + registry.fill(HIST("Matching/h_collision_counter"), 1.5); + + const auto collisionsPerMCCollision = + collisions.sliceBy(collisionsPerMCCollisionPreslice, mccollision.globalIndex()); + for (const auto& collision : collisionsPerMCCollision) { + registry.fill(HIST("Matching/h_collision_counter"), 2.5); + // Event selection + if (useCuts && (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBgapEvents.value, applyRCTflag.value, rctLabel.value) || !(std::abs(collision.posZ()) < vertexZCut))) { + continue; + } + registry.fill(HIST("Matching/h_collision_counter"), 3.5); + const auto mcdJetsPerCollision = + mcdjets.sliceBy(mcdJetsPerCollisionPreslice, collision.globalIndex()); + for (const auto& mcdjet : mcdJetsPerCollision) { + registry.fill(HIST("Matching/h_jet_counter"), 0.5); + if (useCuts) { + if (mcdjet.pt() < jetPtMin || mcdjet.pt() > jetPtMax || + std::abs(mcdjet.eta()) > jetEtaMax) { + continue; + } + } + registry.fill(HIST("Matching/h_jet_counter"), 1.5); + + if (!mcdjet.has_matchedJetCand()) { + continue; + } + registry.fill(HIST("Matching/h_jet_counter"), 2.5); + + const auto matchedMCPJets = + mcdjet.template matchedJetCand_as(); + for (const auto& mcpjet : matchedMCPJets) { + if (useMCPCuts) { + if (mcpjet.pt() < jetPtMin || mcpjet.pt() > jetPtMax || + std::abs(mcpjet.eta()) > jetEtaMax) { + continue; + } + } + registry.fill(HIST("Matching/h_jet_counter"), 3.5); + for (const auto& mcdCandidate : + mcdjet.template candidates_as()) { + registry.fill(HIST("Matching/h_candidate_counter"), 0.5); + + const double zMCD = calculateZ(mcdjet, mcdCandidate); + if (zMCD < 0.) { + continue; + } + if (useCuts && + (!passesPairCuts(mcdCandidate) || + !passesElectronCuts(mcdCandidate, 1) || + !passesElectronCuts(mcdCandidate, 2))) { + continue; + } + registry.fill(HIST("Matching/h_candidate_counter"), 1.5); + + const auto matchedParticleId = + jetcandidateutilities::matchedParticleId( + mcdCandidate, tracksMCD, particlesMCP); + + for (const auto& mcpCandidate : + mcpjet.template candidates_as()) { + if (mcpCandidate.mcParticleId() != matchedParticleId) { + continue; + } + + const double zMCP = calculateZ(mcpjet, mcpCandidate); + if (zMCP < 0.) { + continue; + } + registry.fill(HIST("Matching/h_candidate_counter"), 2.5); + if (useMCPCuts && !passesPairCuts(mcpCandidate)) { + continue; + } + registry.fill(HIST("Matching/h_candidate_counter"), 3.5); + const double mcpCandidatePt = std::sqrt(mcpCandidate.px() * mcpCandidate.px() + mcpCandidate.py() * mcpCandidate.py()); + double tauxyMCP = 0.; + if (mcpCandidatePt > 0.) { + const double dxMCP = mcpCandidate.vx() - mccollision.posX(); + const double dyMCP = mcpCandidate.vy() - mccollision.posY(); + const double lxyMCP = (dxMCP * mcpCandidate.px() + dyMCP * mcpCandidate.py()) / mcpCandidatePt; + tauxyMCP = lxyMCP * mcpCandidate.m() / mcpCandidatePt; + } + + if (mcpCandidate.origin() == 1) { + registry.fill(HIST("Matching/h_response_prompt"), mcdjet.pt(), zMCD, mcpjet.pt(), zMCP); + registry.fill(HIST("Matching/h_2D_prompt_z"), zMCD, zMCP); + registry.fill(HIST("Matching/h_2D_prompt_jetpt"), mcdjet.pt(), mcpjet.pt()); + registry.fill(HIST("Matching/h_2D_prompt_jpsipt"), mcdCandidate.pt(), mcpCandidate.pt()); + registry.fill(HIST("Matching/h_2D_prompt_tauxy"), mcdCandidate.tauxy(), tauxyMCP); + + } else if (mcpCandidate.origin() == 2) { + registry.fill(HIST("Matching/h_response_nonprompt"), mcdjet.pt(), zMCD, mcpjet.pt(), zMCP); + registry.fill(HIST("Matching/h_2D_nonprompt_z"), zMCD, zMCP); + registry.fill(HIST("Matching/h_2D_nonprompt_jetpt"), mcdjet.pt(), mcpjet.pt()); + registry.fill(HIST("Matching/h_2D_nonprompt_jpsipt"), mcdCandidate.pt(), mcpCandidate.pt()); + registry.fill(HIST("Matching/h_2D_nonprompt_tauxy"), mcdCandidate.tauxy(), tauxyMCP); + } + } + } + } + } + } + } + } + + PROCESS_SWITCH(DqFragmentationFunctionTask, processMcMatching, "MC collision, jet and candidate matching", false); +}; +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc, TaskName{"dq-fragmentation-function"})}; +} diff --git a/PWGJE/Tasks/baryonPairInJets.cxx b/PWGJE/Tasks/baryonPairInJets.cxx new file mode 100644 index 00000000000..5f17ed90de2 --- /dev/null +++ b/PWGJE/Tasks/baryonPairInJets.cxx @@ -0,0 +1,1691 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file baryonPairInJets.cxx +/// \brief Identified proton-pair correlations in charged jets +/// \author Hanseo Park + +#include "PWGJE/Core/JetDerivedDataUtilities.h" +#include "PWGJE/DataModel/Jet.h" +#include "PWGJE/DataModel/JetReducedData.h" + +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +using BaryonTracks = soa::Join; +using BaryonTracksMC = soa::Join; +using JetTracksWithOriginal = soa::Join; +using JetTracksWithOriginalAndMC = soa::Join; +using ChargedJetsWithConstituents = soa::Join; +using ChargedMCDJetsWithConstituents = soa::Join; +using ChargedMCPJetsWithConstituents = soa::Join; +using MatchedChargedMCDJets = soa::Join; +using MatchedChargedMCPJets = soa::Join; + +struct BaryonPairInJets { + enum class PairType : uint8_t { + SameSign = 0, + OppositeSign = 1 + }; + + enum class ChargePairType : uint8_t { + PositivePositive = 0, + NegativeNegative = 1, + OppositeSign = 2 + }; + + enum class MixedReference : uint8_t { + Nominal = 0, + Loose = 1, + Random = 2 + }; + + enum class ProtonPidMethod : int { + TpcOnly = 0, + TpcTofCircular = 1, + TpcTofRectangular = 2 + }; + + enum class ControlSpecies : uint8_t { + Pion = 0, + Kaon = 1 + }; + + enum class EfficiencyStage : uint8_t { + Generated = 0, + TrackSelected = 1, + PidSelected = 2 + }; + + // Species-independent selected-particle representation. Keep the full + // kinematics needed by pair and future jet-fragmentation observables. + struct SelectedParticle { + float pt; + float eta; + float phi; + int8_t sign; + }; + + struct SelectedParticles { + std::vector baryons; + std::vector pions; + std::vector kaons; + }; + + struct : ConfigurableGroup { + Configurable vertexZMax{"vertexZMax", 10.f, "Maximum absolute collision z vertex (cm)"}; + Configurable eventSelections{"eventSelections", "sel8", "Event selections"}; + Configurable applyRctSelection{"applyRctSelection", true, "Apply the PWGJE hadron-PID RCT selection"}; + Configurable rctLabel{"rctLabel", "CBT_hadronPID", "RCT selection label"}; + Configurable occupancyMin{"occupancyMin", -999999, "Minimum track occupancy in time range"}; + Configurable occupancyMax{"occupancyMax", 999999, "Maximum track occupancy in time range"}; + } eventCuts; + + struct : ConfigurableGroup { + Configurable jetRadius{"jetRadius", 0.4f, "Charged-jet resolution parameter"}; + Configurable jetPtMin{"jetPtMin", 5.f, "Minimum charged-jet transverse momentum (GeV/c); broad input range"}; + Configurable jetPtMax{"jetPtMax", 140.f, "Maximum charged-jet transverse momentum (GeV/c); broad input range"}; + Configurable jetEtaMax{"jetEtaMax", 0.5f, "Maximum absolute charged-jet pseudorapidity"}; + Configurable areaFractionMin{"areaFractionMin", -1.f, "Minimum jet area divided by pi R squared; negative disables the cut"}; + } jetCuts; + + struct : ConfigurableGroup { + Configurable protonPtMin{"protonPtMin", 0.5f, "Minimum proton transverse momentum (GeV/c)"}; + Configurable protonPtMax{"protonPtMax", 4.0f, "Maximum proton transverse momentum (GeV/c)"}; + Configurable protonEtaMax{"protonEtaMax", 0.8f, "Maximum absolute proton pseudorapidity"}; + Configurable requirePvContributor{"requirePvContributor", false, "Require the track to be a PV contributor"}; + Configurable tpcCrossedRowsMin{"tpcCrossedRowsMin", 70, "Minimum number of crossed TPC rows"}; + Configurable tpcChi2Max{"tpcChi2Max", 4.f, "Maximum TPC chi2 per cluster"}; + Configurable itsChi2Max{"itsChi2Max", 36.f, "Maximum ITS chi2 per cluster"}; + Configurable dcaXYMax{"dcaXYMax", 0.2f, "Maximum absolute transverse DCA (cm)"}; + Configurable dcaZMax{"dcaZMax", 0.1f, "Maximum absolute longitudinal DCA (cm)"}; + } trackCuts; + + struct : ConfigurableGroup { + Configurable method{"method", static_cast(ProtonPidMethod::TpcTofCircular), "PID method: 0 TPC only, 1 circular TPC-TOF, 2 rectangular TPC-TOF"}; + Configurable tofPtThreshold{"tofPtThreshold", 0.75f, "Require TOF above this transverse momentum (GeV/c)"}; + Configurable tpcNSigmaMax{"tpcNSigmaMax", 3.f, "Maximum absolute proton TPC n-sigma"}; + Configurable tofNSigmaMax{"tofNSigmaMax", 3.f, "Maximum absolute proton TOF n-sigma"}; + Configurable combinedNSigmaMax{"combinedNSigmaMax", 3.f, "Maximum circular TPC-TOF proton n-sigma"}; + Configurable rejectPionKaon{"rejectPionKaon", false, "Reject candidates compatible with the pion or kaon hypothesis"}; + Configurable rejectionNSigma{"rejectionNSigma", 3.f, "Pion and kaon rejection threshold"}; + Configurable pionTpcNSigmaMax{"pionTpcNSigmaMax", 3.f, "Maximum absolute pion TPC n-sigma"}; + Configurable pionTofNSigmaMax{"pionTofNSigmaMax", 3.f, "Maximum absolute pion TOF n-sigma"}; + Configurable pionCombinedNSigmaMax{"pionCombinedNSigmaMax", 3.f, "Maximum circular TPC-TOF pion n-sigma"}; + Configurable kaonTpcNSigmaMax{"kaonTpcNSigmaMax", 3.f, "Maximum absolute kaon TPC n-sigma"}; + Configurable kaonTofNSigmaMax{"kaonTofNSigmaMax", 3.f, "Maximum absolute kaon TOF n-sigma"}; + Configurable kaonCombinedNSigmaMax{"kaonCombinedNSigmaMax", 3.f, "Maximum circular TPC-TOF kaon n-sigma"}; + } pidCuts; + + struct : ConfigurableGroup { + Configurable numberOfMixedEvents{"numberOfMixedEvents", 5, "Number of mixed-event neighbours"}; + ConfigurableAxis vertexZBins{"vertexZBins", {VARIABLE_WIDTH, -10.f, -5.f, -2.f, 0.f, 2.f, 5.f, 10.f}, "Mixing bins in collision z vertex"}; + ConfigurableAxis multiplicityBins{"multiplicityBins", {VARIABLE_WIDTH, 0.f, 10.f, 20.f, 40.f, 80.f, 150.f, 300.f, 600.f, 2000.f}, "Mixing bins in FT0C multiplicity"}; + Configurable nominalJetPtDifferenceMax{"nominalJetPtDifferenceMax", 2.5f, "Maximum jet-pT difference for nominal mixing (GeV/c)"}; + Configurable nominalJetNchDifferenceMax{"nominalJetNchDifferenceMax", 3, "Maximum jet-constituent multiplicity difference for nominal mixing"}; + Configurable nominalJetEtaDifferenceMax{"nominalJetEtaDifferenceMax", 0.1f, "Maximum jet-eta difference for nominal mixing"}; + Configurable looseJetPtDifferenceMax{"looseJetPtDifferenceMax", 7.5f, "Maximum jet-pT difference for loose mixing (GeV/c)"}; + Configurable looseJetNchDifferenceMax{"looseJetNchDifferenceMax", 8, "Maximum jet-constituent multiplicity difference for loose mixing"}; + Configurable looseJetEtaDifferenceMax{"looseJetEtaDifferenceMax", 0.3f, "Maximum jet-eta difference for loose mixing"}; + } mixing; + + struct : ConfigurableGroup { + Configurable fillQa{"fillQa", true, "Fill event, jet, track, and PID QA histograms"}; + Configurable fillReferenceVariations{"fillReferenceVariations", true, "Fill loose and unmatched mixed-event references"}; + Configurable fillChargeSeparated{"fillChargeSeparated", true, "Fill charge-resolved pair histograms"}; + Configurable fillPionControl{"fillPionControl", false, "Fill same-species pion control correlations"}; + Configurable fillKaonControl{"fillKaonControl", false, "Fill same-species kaon control correlations"}; + } output; + + struct : ConfigurableGroup { + Configurable nearSideDeltaPhiMax{"nearSideDeltaPhiMax", 0.2f, "Maximum absolute delta phi for near-side pair yields"}; + Configurable nearSideDeltaEtaMax{"nearSideDeltaEtaMax", 0.6f, "Maximum absolute delta eta for near-side pair yields"}; + ConfigurableAxis jetPt{"jetPt", {VARIABLE_WIDTH, 0.f, 5.f, 10.f, 15.f, 20.f, 25.f, 30.f, 40.f, 50.f, 60.f, 80.f, 100.f, 120.f, 140.f}, "#it{p}_{T, jet} (GeV/#it{c})"}; + ConfigurableAxis jetEta{"jetEta", {40, -1.f, 1.f}, "#eta_{jet}"}; + ConfigurableAxis jetPhi{"jetPhi", {72, 0.f, o2::constants::math::TwoPI}, "#varphi_{jet}"}; + ConfigurableAxis jetNch{"jetNch", {61, -0.5f, 60.5f}, "#it{N}_{ch}^{jet}"}; + ConfigurableAxis baryonPt{"baryonPt", {50, 0.f, 5.f}, "#it{p}_{T} (GeV/#it{c})"}; + ConfigurableAxis pairPt{"pairPt", {VARIABLE_WIDTH, 0.5f, 0.6f, 0.75f, 1.f, 1.25f, 1.5f, 2.f, 2.5f, 3.f, 4.f, 5.f}, "Pair-particle #it{p}_{T} (GeV/#it{c})"}; + ConfigurableAxis baryonEta{"baryonEta", {48, -1.2f, 1.2f}, "#eta"}; + ConfigurableAxis baryonRapidity{"baryonRapidity", {48, -1.2f, 1.2f}, "#it{y}"}; + ConfigurableAxis nSigma{"nSigma", {100, -5.f, 5.f}, "n#sigma"}; + ConfigurableAxis dca{"dca", {100, -0.5f, 0.5f}, "DCA (cm)"}; + ConfigurableAxis deltaEta{"deltaEta", {80, -0.8f, 0.8f}, "#Delta#eta"}; + ConfigurableAxis deltaPhi{"deltaPhi", {80, -0.8f, 0.8f}, "#Delta#varphi"}; + ConfigurableAxis deltaR{"deltaR", {40, 0.f, 0.8f}, "#Delta#it{R}"}; + ConfigurableAxis particleJetDistance{"particleJetDistance", {40, 0.f, 0.4f}, "#Delta#it{R}(p, particle-level jet)"}; + ConfigurableAxis inclusiveDeltaEta{"inclusiveDeltaEta", {64, -1.6f, 1.6f}, "#Delta#eta"}; + ConfigurableAxis inclusiveDeltaPhi{"inclusiveDeltaPhi", {72, -o2::constants::math::PIHalf, 3.f * o2::constants::math::PIHalf}, "#Delta#varphi"}; + } axes; + + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; + + std::vector eventSelectionBits; + o2::aod::rctsel::RCTFlagsChecker inclusiveRctChecker; + SliceCache mixingCache; + SliceCache mcdMixingCache; + SliceCache mcpMixingCache; + SliceCache inclusiveMixingCache; + Preslice particleJetsPerMcCollision = aod::jet::mcCollisionId; + using MixingBinning = ColumnBinningPolicy; + using MCPMixingBinning = ColumnBinningPolicy; + using InclusiveSameEventCollisions = soa::Join; + using InclusiveMixedEventCollisions = soa::Join; + using InclusiveMixingBinning = ColumnBinningPolicy; + + void init(InitContext const&) + { + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventCuts.eventSelections)); + inclusiveRctChecker.init(static_cast(eventCuts.rctLabel)); + + AxisSpec axisPairType{2, -0.5, 1.5, "pair type"}; + AxisSpec axisChargePairType{3, -0.5, 2.5, "charge pair type"}; + AxisSpec axisCounter{8, 0.5, 8.5, "counter"}; + AxisSpec axisMultiplicity{11, -0.5, 10.5, "multiplicity per jet"}; + AxisSpec axisJetPt{axes.jetPt}; + AxisSpec axisJetEta{axes.jetEta}; + AxisSpec axisJetPhi{axes.jetPhi}; + AxisSpec axisJetNch{axes.jetNch}; + AxisSpec axisBaryonPt{axes.baryonPt}; + AxisSpec axisPairPtLow{axes.pairPt, "#it{p}_{T}^{low} (GeV/#it{c})"}; + AxisSpec axisPairPtHigh{axes.pairPt, "#it{p}_{T}^{high} (GeV/#it{c})"}; + AxisSpec axisBaryonEta{axes.baryonEta}; + AxisSpec axisBaryonRapidity{axes.baryonRapidity}; + AxisSpec axisNSigma{axes.nSigma}; + AxisSpec axisDca{axes.dca}; + AxisSpec axisDeltaEta{axes.deltaEta}; + AxisSpec axisDeltaPhi{axes.deltaPhi}; + AxisSpec axisDeltaR{axes.deltaR}; + AxisSpec axisParticleJetDistance{axes.particleJetDistance}; + AxisSpec axisInclusiveDeltaEta{axes.inclusiveDeltaEta}; + AxisSpec axisInclusiveDeltaPhi{axes.inclusiveDeltaPhi}; + AxisSpec axisOrigin{3, 0.5, 3.5, "origin"}; + + registry.add("event/cutFlow", "Event selection;selection step;entries", HistType::kTH1F, {axisCounter}); + if (output.fillQa.value) { + registry.add("event/zVertex", "Selected collision z vertex;#it{z}_{vtx} (cm);entries", HistType::kTH1F, {{80, -20.f, 20.f}}); + registry.add("event/multiplicityFT0C", "Selected collision FT0C multiplicity;FT0C multiplicity;entries", HistType::kTH1F, {{200, 0.f, 2000.f}}); + + registry.add("jet/ptBeforePtSelection", "Charged jets before the jet-pT selection;#it{p}_{T,jet} (GeV/#it{c});entries", HistType::kTH1F, {axisJetPt}); + registry.add("jet/pt", "Selected charged jets;#it{p}_{T,jet} (GeV/#it{c});entries", HistType::kTH1F, {axisJetPt}); + registry.add("jet/eta", "Selected charged jets;#eta_{jet};entries", HistType::kTH1F, {axisJetEta}); + registry.add("jet/phi", "Selected charged jets;#varphi_{jet};entries", HistType::kTH1F, {axisJetPhi}); + registry.add("jet/nConstituents", "Charged-jet constituent multiplicity;#it{N}_{ch}^{jet};entries", HistType::kTH1F, {axisJetNch}); + registry.add("jet/nSelectedBaryons", "Selected baryon multiplicity per jet;#it{N}_{p+#bar{p}};jets", HistType::kTH1F, {axisMultiplicity}); + registry.add("jet/nProtons", "Selected proton multiplicity per jet;#it{N}_{p};jets", HistType::kTH1F, {axisMultiplicity}); + registry.add("jet/nAntiprotons", "Selected antiproton multiplicity per jet;#it{N}_{#bar{p}};jets", HistType::kTH1F, {axisMultiplicity}); + registry.add("jet/nProtonsVsAntiprotons", "Proton and antiproton multiplicities per jet;#it{N}_{p};#it{N}_{#bar{p}}", HistType::kTH2F, {axisMultiplicity, axisMultiplicity}); + registry.add("jet/nConstituentsVsPt", "Constituent multiplicity versus jet momentum;#it{p}_{T,jet} (GeV/#it{c});#it{N}_{ch}^{jet}", HistType::kTH2F, {axisJetPt, axisJetNch}); + registry.add("jet/nBaryonsVsPt", "Selected baryon multiplicity versus jet momentum;#it{p}_{T,jet} (GeV/#it{c});#it{N}_{p+#bar{p}}", HistType::kTH2F, {axisJetPt, axisMultiplicity}); + registry.add("jet/sameSignPairsVsPt", "Same-sign pair count versus jet momentum;#it{p}_{T,jet} (GeV/#it{c});pairs", HistType::kTH1F, {axisJetPt}); + registry.add("jet/oppositeSignPairsVsPt", "Opposite-sign pair count versus jet momentum;#it{p}_{T,jet} (GeV/#it{c});pairs", HistType::kTH1F, {axisJetPt}); + + registry.add("proton/pt", "Selected proton and antiproton candidates;#it{p}_{T} (GeV/#it{c});entries", HistType::kTH1F, {axisBaryonPt}); + registry.add("proton/eta", "Selected proton and antiproton candidates;#eta;entries", HistType::kTH1F, {axisBaryonEta}); + registry.add("proton/rapidity", "Selected proton and antiproton candidates;#it{y};entries", HistType::kTH1F, {axisBaryonRapidity}); + registry.add("proton/tpcNSigmaVsPt", "Proton TPC PID;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}^{p}", HistType::kTH2F, {axisBaryonPt, axisNSigma}); + registry.add("proton/tofNSigmaVsPt", "Proton TOF PID;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}^{p}", HistType::kTH2F, {axisBaryonPt, axisNSigma}); + registry.add("proton/dcaXYVsPt", "Selected proton transverse DCA;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axisBaryonPt, axisDca}); + registry.add("proton/dcaZVsPt", "Selected proton longitudinal DCA;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axisBaryonPt, axisDca}); + registry.add("pid/before/tpcSignalVsSignedP", "TPC response before proton PID;charge #times #it{p} (GeV/#it{c});TPC signal", HistType::kTH2F, {{120, -6.f, 6.f}, {200, 0.f, 200.f}}); + registry.add("pid/before/tofBetaVsSignedP", "TOF response before proton PID;charge #times #it{p} (GeV/#it{c});#beta", HistType::kTH2F, {{120, -6.f, 6.f}, {120, 0.f, 1.2f}}); + registry.add("pid/before/tpcNSigmaPiVsPt", "Pion TPC PID before proton PID;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}^{#pi}", HistType::kTH2F, {axisBaryonPt, axisNSigma}); + registry.add("pid/before/tpcNSigmaKaVsPt", "Kaon TPC PID before proton PID;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}^{K}", HistType::kTH2F, {axisBaryonPt, axisNSigma}); + registry.add("pid/before/tpcNSigmaPrVsPt", "Proton TPC PID before proton PID;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}^{p}", HistType::kTH2F, {axisBaryonPt, axisNSigma}); + registry.add("pid/before/tofNSigmaPiVsPt", "Pion TOF PID before proton PID;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}^{#pi}", HistType::kTH2F, {axisBaryonPt, axisNSigma}); + registry.add("pid/before/tofNSigmaKaVsPt", "Kaon TOF PID before proton PID;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}^{K}", HistType::kTH2F, {axisBaryonPt, axisNSigma}); + registry.add("pid/before/tofNSigmaPrVsPt", "Proton TOF PID before proton PID;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}^{p}", HistType::kTH2F, {axisBaryonPt, axisNSigma}); + registry.add("pid/tofMatchDenominator", "Tracks entering PID QA;#it{p}_{T} (GeV/#it{c});tracks", HistType::kTH1F, {axisBaryonPt}); + registry.add("pid/tofMatchNumerator", "Tracks with TOF;#it{p}_{T} (GeV/#it{c});tracks", HistType::kTH1F, {axisBaryonPt}); + registry.add("pid/selectedPtByCharge", "Selected proton candidates;charge;#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {{2, -1.5f, 1.5f}, axisBaryonPt}); + } + + addPairHistograms("sameEvent", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); + addPairHistograms("mixedEvent/nominal", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); + addPairHistograms("mixedEvent/loose", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); + addPairHistograms("mixedEvent/random", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); + registry.add("sameEvent/chargePairSparse", "Charge-separated baryon pairs;charge pair type;#it{p}_{T,jet};#Delta#eta;#Delta#varphi;#Delta#it{R};#it{p}_{T}^{low};#it{p}_{T}^{high}", HistType::kTHnSparseF, {axisChargePairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR, axisPairPtLow, axisPairPtHigh}); + registry.add("mixedEvent/nominal/chargePairSparse", "Charge-separated nominal mixed pairs;charge pair type;#it{p}_{T,jet};#Delta#eta;#Delta#varphi;#Delta#it{R};#it{p}_{T}^{low};#it{p}_{T}^{high}", HistType::kTHnSparseF, {axisChargePairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR, axisPairPtLow, axisPairPtHigh}); + registry.add("mixedEvent/loose/chargePairSparse", "Charge-separated loose mixed pairs;charge pair type;#it{p}_{T,jet};#Delta#eta;#Delta#varphi;#Delta#it{R};#it{p}_{T}^{low};#it{p}_{T}^{high}", HistType::kTHnSparseF, {axisChargePairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR, axisPairPtLow, axisPairPtHigh}); + registry.add("mixedEvent/random/chargePairSparse", "Charge-separated random mixed pairs;charge pair type;#it{p}_{T,jet};#Delta#eta;#Delta#varphi;#Delta#it{R};#it{p}_{T}^{low};#it{p}_{T}^{high}", HistType::kTHnSparseF, {axisChargePairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR, axisPairPtLow, axisPairPtHigh}); + + addInclusivePairHistograms("inclusive/sameEvent", axisChargePairType, axisInclusiveDeltaEta, axisInclusiveDeltaPhi); + addInclusivePairHistograms("inclusive/mixedEvent", axisChargePairType, axisInclusiveDeltaEta, axisInclusiveDeltaPhi); + registry.add("inclusive/event/multiplicity", "Inclusive selected-event multiplicity;FT0C multiplicity;events", HistType::kTH1F, {{200, 0.f, 2000.f}}); + registry.add("inclusive/normalization/sameEvent", "Inclusive same-event normalization;counter;entries", HistType::kTH1F, {axisCounter}); + registry.add("inclusive/normalization/mixedEvent", "Inclusive mixed-event normalization;counter;weighted entries", HistType::kTH1F, {axisCounter}); + + registry.add("normalization/sameEvent", "Same-event normalization counters;counter;weighted entries", HistType::kTH1F, {axisCounter}); + registry.add("normalization/mixedEventNominal", "Nominal mixed-event normalization counters;counter;weighted entries", HistType::kTH1F, {axisCounter}); + registry.add("normalization/mixedEventLoose", "Loose mixed-event normalization counters;counter;weighted entries", HistType::kTH1F, {axisCounter}); + registry.add("normalization/mixedEventRandom", "Random mixed-event normalization counters;counter;weighted entries", HistType::kTH1F, {axisCounter}); + registry.add("mixedEvent/jetPtDifference", "Mixed-jet matching QA;|#Delta#it{p}_{T,jet}| (GeV/#it{c});jet pairs", HistType::kTH1F, {{80, 0.f, 40.f}}); + registry.add("mixedEvent/jetEtaDifference", "Mixed-jet matching QA;|#Delta#eta_{jet}|;jet pairs", HistType::kTH1F, {{40, 0.f, 1.f}}); + registry.add("mixedEvent/jetNchDifference", "Mixed-jet matching QA;|#Delta#it{N}_{ch}^{jet}|;jet pairs", HistType::kTH1F, {{61, -0.5f, 60.5f}}); + + registry.add("mc/jet/allDetectorPt", "Accepted detector-level charged jets;#it{p}_{T,jet}^{det} (GeV/#it{c});jets", HistType::kTH1F, {axisJetPt}); + registry.add("mc/jet/allParticlePt", "Accepted particle-level charged jets;#it{p}_{T,jet}^{part} (GeV/#it{c});jets", HistType::kTH1F, {axisJetPt}); + registry.add("mc/jet/matchedDetectorPt", "Matched detector-level charged jets;#it{p}_{T,jet}^{det} (GeV/#it{c});jets", HistType::kTH1F, {axisJetPt}); + registry.add("mc/jet/matchedParticlePt", "Matched particle-level charged jets;#it{p}_{T,jet}^{part} (GeV/#it{c});jets", HistType::kTH1F, {axisJetPt}); + registry.add("mc/jet/fakeDetectorPt", "Unmatched detector-level charged jets;#it{p}_{T,jet}^{det} (GeV/#it{c});jets", HistType::kTH1F, {axisJetPt}); + registry.add("mc/jet/responsePt", "Charged-jet response;#it{p}_{T,jet}^{part} (GeV/#it{c});#it{p}_{T,jet}^{det} (GeV/#it{c})", HistType::kTH2F, {axisJetPt, axisJetPt}); + registry.add("mc/jet/relativePtResolution", "Charged-jet momentum resolution;#it{p}_{T,jet}^{part} (GeV/#it{c});(#it{p}_{T}^{det}-#it{p}_{T}^{part})/#it{p}_{T}^{part}", HistType::kTH2F, {axisJetPt, {120, -1.5f, 1.5f}}); + registry.add("mc/jet/matchingDeltaR", "Detector-particle jet matching distance;#Delta#it{R}_{match};matched jets", HistType::kTH1F, {{100, 0.f, 1.f}}); + registry.add("mc/pid/generatedPrimaryProtonPt", "Generated primary protons in accepted particle-level jets;#it{p}_{T}^{part} (GeV/#it{c});particles", HistType::kTH1F, {axisBaryonPt}); + registry.add("mc/pid/reconstructedTrueProtonPt", "Reconstructed primary true protons before PID;#it{p}_{T}^{rec} (GeV/#it{c});tracks", HistType::kTH1F, {axisBaryonPt}); + registry.add("mc/pid/selectedTrueProtonPt", "PID-selected primary true protons;#it{p}_{T}^{rec} (GeV/#it{c});tracks", HistType::kTH1F, {axisBaryonPt}); + registry.add("mc/pid/selectedCandidateOriginVsPt", "Selected proton-candidate origin;#it{p}_{T}^{rec} (GeV/#it{c});origin", HistType::kTH2F, {axisBaryonPt, axisOrigin}); + registry.add("mc/pid/selectedCandidateTruePdgVsPt", "Selected proton-candidate contamination;#it{p}_{T}^{rec} (GeV/#it{c});true PDG code", HistType::kTH2F, {axisBaryonPt, {801, -4005.f, 4005.f}}); + registry.add("mc/pid/ptResponsePrimaryProton", "Primary-proton momentum response;#it{p}_{T}^{part} (GeV/#it{c});#it{p}_{T}^{rec} (GeV/#it{c})", HistType::kTH2F, {axisBaryonPt, axisBaryonPt}); + AxisSpec const axisCharge{2, -1.5f, 1.5f, "charge"}; + registry.add("mc/efficiency/generatedPrimaryProton", "Generated primary protons in matched particle-level jets;charge;#it{p}_{T}^{truth} (GeV/#it{c});#it{p}_{T,jet}^{det} (GeV/#it{c});#Delta#it{R}(p,jet_{part})", HistType::kTHnSparseF, {axisCharge, axisBaryonPt, axisJetPt, axisParticleJetDistance}); + registry.add("mc/efficiency/trackSelectedPrimaryProton", "Track-selected primary protons in matched jets;charge;#it{p}_{T}^{truth} (GeV/#it{c});#it{p}_{T,jet}^{det} (GeV/#it{c});#Delta#it{R}(p,jet_{part})", HistType::kTHnSparseF, {axisCharge, axisBaryonPt, axisJetPt, axisParticleJetDistance}); + registry.add("mc/efficiency/tofMatchedPrimaryProton", "TOF-matched primary protons in matched jets;charge;#it{p}_{T}^{truth} (GeV/#it{c});#it{p}_{T,jet}^{det} (GeV/#it{c});#Delta#it{R}(p,jet_{part})", HistType::kTHnSparseF, {axisCharge, axisBaryonPt, axisJetPt, axisParticleJetDistance}); + registry.add("mc/efficiency/pidSelectedPrimaryProton", "PID-selected primary protons in matched jets;charge;#it{p}_{T}^{truth} (GeV/#it{c});#it{p}_{T,jet}^{det} (GeV/#it{c});#Delta#it{R}(p,jet_{part})", HistType::kTHnSparseF, {axisCharge, axisBaryonPt, axisJetPt, axisParticleJetDistance}); + registry.add("mc/efficiency/generatedPair", "Generated primary-proton pairs in matched jets;pair type;#it{p}_{T,jet}^{det} (GeV/#it{c});#Delta#varphi^{truth};#Delta#it{R}^{truth};#it{p}_{T}^{low} (GeV/#it{c});#it{p}_{T}^{high} (GeV/#it{c})", HistType::kTHnSparseF, {axisPairType, axisJetPt, axisDeltaPhi, axisDeltaR, axisPairPtLow, axisPairPtHigh}); + registry.add("mc/efficiency/trackSelectedPair", "Track-selected primary-proton pairs in matched jets;pair type;#it{p}_{T,jet}^{det} (GeV/#it{c});#Delta#varphi^{truth};#Delta#it{R}^{truth};#it{p}_{T}^{low} (GeV/#it{c});#it{p}_{T}^{high} (GeV/#it{c})", HistType::kTHnSparseF, {axisPairType, axisJetPt, axisDeltaPhi, axisDeltaR, axisPairPtLow, axisPairPtHigh}); + registry.add("mc/efficiency/pidSelectedPair", "PID-selected primary-proton pairs in matched jets;pair type;#it{p}_{T,jet}^{det} (GeV/#it{c});#Delta#varphi^{truth};#Delta#it{R}^{truth};#it{p}_{T}^{low} (GeV/#it{c});#it{p}_{T}^{high} (GeV/#it{c})", HistType::kTHnSparseF, {axisPairType, axisJetPt, axisDeltaPhi, axisDeltaR, axisPairPtLow, axisPairPtHigh}); + addPairHistograms("mc/particle/sameEvent", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); + addPairHistograms("mc/particle/mixedEvent", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); + registry.add("mc/particle/normalizationSameEvent", "Particle-level same-event normalization;counter;weighted entries", HistType::kTH1F, {axisCounter}); + registry.add("mc/particle/normalizationMixedEvent", "Particle-level mixed-event normalization;counter;weighted entries", HistType::kTH1F, {axisCounter}); + + if (output.fillPionControl.value) { + addControlSpeciesHistograms("control/pion", "Pion", axisPairType, axisCounter, axisJetPt, axisBaryonPt, axisNSigma, axisOrigin, axisDeltaEta, axisDeltaPhi, axisDeltaR); + } + if (output.fillKaonControl.value) { + addControlSpeciesHistograms("control/kaon", "Kaon", axisPairType, axisCounter, axisJetPt, axisBaryonPt, axisNSigma, axisOrigin, axisDeltaEta, axisDeltaPhi, axisDeltaR); + } + + setHistogramLabels(); + } + + void addControlSpeciesHistograms(std::string const& directory, + std::string const& speciesName, + AxisSpec const& axisPairType, + AxisSpec const& axisCounter, + AxisSpec const& axisJetPt, + AxisSpec const& axisParticlePt, + AxisSpec const& axisNSigma, + AxisSpec const& axisOrigin, + AxisSpec const& axisDeltaEta, + AxisSpec const& axisDeltaPhi, + AxisSpec const& axisDeltaR) + { + auto addHistogram = [&](std::string const& suffix, std::string const& title, HistType type, std::vector const& histogramAxes) { + registry.add(directory + suffix, title.c_str(), type, histogramAxes); + }; + AxisSpec const axisPdg{801, -4005.f, 4005.f, "true PDG code"}; + addPairHistograms(directory + "/sameEvent", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); + addPairHistograms(directory + "/mixedEvent", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); + addHistogram("/normalizationSameEvent", speciesName + " same-event normalization;counter;weighted entries", HistType::kTH1F, {axisCounter}); + addHistogram("/normalizationMixedEvent", speciesName + " mixed-event normalization;counter;weighted entries", HistType::kTH1F, {axisCounter}); + if (output.fillQa.value) { + addHistogram("/qa/pt", "Selected " + speciesName + " candidates;#it{p}_{T} (GeV/#it{c});entries", HistType::kTH1F, {axisParticlePt}); + addHistogram("/qa/tpcNSigmaVsPt", speciesName + " TPC PID;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axisParticlePt, axisNSigma}); + addHistogram("/qa/tofNSigmaVsPt", speciesName + " TOF PID;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axisParticlePt, axisNSigma}); + } + addHistogram("/mc/generatedPrimaryPt", "Generated primary " + speciesName + " candidates;#it{p}_{T}^{part} (GeV/#it{c});particles", HistType::kTH1F, {axisParticlePt}); + addHistogram("/mc/reconstructedTruePt", "Reconstructed true primary " + speciesName + " candidates;#it{p}_{T}^{rec} (GeV/#it{c});tracks", HistType::kTH1F, {axisParticlePt}); + addHistogram("/mc/selectedTruePt", "PID-selected true primary " + speciesName + " candidates;#it{p}_{T}^{rec} (GeV/#it{c});tracks", HistType::kTH1F, {axisParticlePt}); + addHistogram("/mc/selectedCandidateOriginVsPt", "Selected " + speciesName + " candidate origin;#it{p}_{T}^{rec} (GeV/#it{c});origin", HistType::kTH2F, {axisParticlePt, axisOrigin}); + addHistogram("/mc/selectedCandidateTruePdgVsPt", "Selected " + speciesName + " candidate contamination;#it{p}_{T}^{rec} (GeV/#it{c});true PDG code", HistType::kTH2F, {axisParticlePt, axisPdg}); + addHistogram("/mc/ptResponsePrimary", "Primary " + speciesName + " momentum response;#it{p}_{T}^{part} (GeV/#it{c});#it{p}_{T}^{rec} (GeV/#it{c})", HistType::kTH2F, {axisParticlePt, axisParticlePt}); + addPairHistograms(directory + "/mc/particle/sameEvent", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); + addPairHistograms(directory + "/mc/particle/mixedEvent", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); + addHistogram("/mc/particle/normalizationSameEvent", speciesName + " particle-level same-event normalization;counter;weighted entries", HistType::kTH1F, {axisCounter}); + addHistogram("/mc/particle/normalizationMixedEvent", speciesName + " particle-level mixed-event normalization;counter;weighted entries", HistType::kTH1F, {axisCounter}); + } + + void addPairHistograms(std::string const& directory, + AxisSpec const& axisPairType, + AxisSpec const& axisJetPt, + AxisSpec const& axisDeltaEta, + AxisSpec const& axisDeltaPhi, + AxisSpec const& axisDeltaR) + { + AxisSpec const axisPairPtLow{axes.pairPt, "#it{p}_{T}^{low} (GeV/#it{c})"}; + AxisSpec const axisPairPtHigh{axes.pairPt, "#it{p}_{T}^{high} (GeV/#it{c})"}; + registry.add(directory + "/sameSignDeltaEtaDeltaPhi", "Same-sign baryon pairs;#Delta#eta;#Delta#varphi", HistType::kTH2F, {axisDeltaEta, axisDeltaPhi}); + registry.add(directory + "/oppositeSignDeltaEtaDeltaPhi", "Opposite-sign baryon pairs;#Delta#eta;#Delta#varphi", HistType::kTH2F, {axisDeltaEta, axisDeltaPhi}); + registry.add(directory + "/sameSignDeltaR", "Same-sign baryon pairs;#Delta#it{R};weighted pairs", HistType::kTH1F, {axisDeltaR}); + registry.add(directory + "/oppositeSignDeltaR", "Opposite-sign baryon pairs;#Delta#it{R};weighted pairs", HistType::kTH1F, {axisDeltaR}); + registry.add(directory + "/pairSparse", "Baryon-pair distribution;pair type;#it{p}_{T,jet};#Delta#eta;#Delta#varphi;#Delta#it{R};#it{p}_{T}^{low};#it{p}_{T}^{high}", HistType::kTHnSparseF, {axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR, axisPairPtLow, axisPairPtHigh}); + } + + void addInclusivePairHistograms(std::string const& directory, + AxisSpec const& axisChargePairType, + AxisSpec const& axisDeltaEta, + AxisSpec const& axisDeltaPhi) + { + AxisSpec const axisPairPtLow{axes.pairPt, "#it{p}_{T}^{low} (GeV/#it{c})"}; + AxisSpec const axisPairPtHigh{axes.pairPt, "#it{p}_{T}^{high} (GeV/#it{c})"}; + registry.add(directory + "/sameSignDeltaEtaDeltaPhi", "Inclusive same-sign baryon pairs;#Delta#eta;#Delta#varphi", HistType::kTH2F, {axisDeltaEta, axisDeltaPhi}); + registry.add(directory + "/oppositeSignDeltaEtaDeltaPhi", "Inclusive opposite-sign baryon pairs;#Delta#eta;#Delta#varphi", HistType::kTH2F, {axisDeltaEta, axisDeltaPhi}); + registry.add(directory + "/chargePairSparse", "Inclusive charge-separated baryon pairs;charge pair type;#Delta#eta;#Delta#varphi;#it{p}_{T}^{low};#it{p}_{T}^{high}", HistType::kTHnSparseF, {axisChargePairType, axisDeltaEta, axisDeltaPhi, axisPairPtLow, axisPairPtHigh}); + } + + void setHistogramLabels() + { + auto eventCutFlow = registry.get(HIST("event/cutFlow")); + eventCutFlow->GetXaxis()->SetBinLabel(1, "all"); + eventCutFlow->GetXaxis()->SetBinLabel(2, "z vertex"); + eventCutFlow->GetXaxis()->SetBinLabel(3, "occupancy"); + eventCutFlow->GetXaxis()->SetBinLabel(4, "event selection"); + + auto setNormalizationLabels = [](std::shared_ptr const& histogram) { + histogram->GetXaxis()->SetBinLabel(1, "events or event pairs"); + histogram->GetXaxis()->SetBinLabel(2, "selected jets or jet pairs"); + histogram->GetXaxis()->SetBinLabel(3, "same-sign pairs"); + histogram->GetXaxis()->SetBinLabel(4, "opposite-sign pairs"); + histogram->GetXaxis()->SetBinLabel(5, "near-side same-sign pairs"); + histogram->GetXaxis()->SetBinLabel(6, "near-side opposite-sign pairs"); + }; + setNormalizationLabels(registry.get(HIST("normalization/sameEvent"))); + setNormalizationLabels(registry.get(HIST("normalization/mixedEventNominal"))); + setNormalizationLabels(registry.get(HIST("normalization/mixedEventLoose"))); + setNormalizationLabels(registry.get(HIST("normalization/mixedEventRandom"))); + if (output.fillPionControl.value) { + setNormalizationLabels(registry.get(HIST("control/pion/normalizationSameEvent"))); + setNormalizationLabels(registry.get(HIST("control/pion/normalizationMixedEvent"))); + setNormalizationLabels(registry.get(HIST("control/pion/mc/particle/normalizationSameEvent"))); + setNormalizationLabels(registry.get(HIST("control/pion/mc/particle/normalizationMixedEvent"))); + } + if (output.fillKaonControl.value) { + setNormalizationLabels(registry.get(HIST("control/kaon/normalizationSameEvent"))); + setNormalizationLabels(registry.get(HIST("control/kaon/normalizationMixedEvent"))); + setNormalizationLabels(registry.get(HIST("control/kaon/mc/particle/normalizationSameEvent"))); + setNormalizationLabels(registry.get(HIST("control/kaon/mc/particle/normalizationMixedEvent"))); + } + } + + template + bool isSelectedCollision(CollisionType const& collision, bool fillCutFlow) + { + if (fillCutFlow) { + registry.fill(HIST("event/cutFlow"), 1.f); + } + if (std::abs(collision.posZ()) > eventCuts.vertexZMax.value) { + return false; + } + if (fillCutFlow) { + registry.fill(HIST("event/cutFlow"), 2.f); + } + if (collision.trackOccupancyInTimeRange() < eventCuts.occupancyMin.value || collision.trackOccupancyInTimeRange() > eventCuts.occupancyMax.value) { + return false; + } + if (fillCutFlow) { + registry.fill(HIST("event/cutFlow"), 3.f); + } + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, true, eventCuts.applyRctSelection.value, static_cast(eventCuts.rctLabel))) { + return false; + } + if (fillCutFlow) { + registry.fill(HIST("event/cutFlow"), 4.f); + } + return true; + } + + template + bool isSelectedJetGeometry(JetType const& jet) const + { + if (jet.r() != static_cast(std::lround(jetCuts.jetRadius.value * 100.f)) || std::abs(jet.eta()) > jetCuts.jetEtaMax.value) { + return false; + } + if (jetCuts.areaFractionMin.value >= 0.f) { + float const fullArea = o2::constants::math::PI * jetCuts.jetRadius.value * jetCuts.jetRadius.value; + if (jet.area() / fullArea < jetCuts.areaFractionMin.value) { + return false; + } + } + return true; + } + + template + bool isSelectedJet(JetType const& jet) const + { + return isSelectedJetGeometry(jet) && jet.pt() >= jetCuts.jetPtMin.value && jet.pt() < jetCuts.jetPtMax.value; + } + + template + bool isSelectedTrack(TrackType const& track) const + { + if (trackCuts.requirePvContributor.value && !track.isPVContributor()) { + return false; + } + if (!track.hasITS() || !track.hasTPC() || !track.isGlobalTrack()) { + return false; + } + if (track.tpcNClsCrossedRows() < trackCuts.tpcCrossedRowsMin.value || track.tpcChi2NCl() > trackCuts.tpcChi2Max.value || track.itsChi2NCl() > trackCuts.itsChi2Max.value) { + return false; + } + if (std::abs(track.eta()) > trackCuts.protonEtaMax.value || track.pt() < trackCuts.protonPtMin.value || track.pt() >= trackCuts.protonPtMax.value) { + return false; + } + if (std::abs(track.dcaXY()) > trackCuts.dcaXYMax.value || std::abs(track.dcaZ()) > trackCuts.dcaZMax.value) { + return false; + } + return true; + } + + template + bool isSelectedProton(TrackType const& track) const + { + auto const pidMethod = static_cast(pidCuts.method.value); + float const tpcNSigma = std::abs(track.tpcNSigmaPr()); + bool isProton = false; + + if (pidMethod == ProtonPidMethod::TpcOnly || track.pt() < pidCuts.tofPtThreshold.value) { + isProton = tpcNSigma < pidCuts.tpcNSigmaMax.value; + } else if (track.hasTOF()) { + float const tofNSigma = std::abs(track.tofNSigmaPr()); + if (pidMethod == ProtonPidMethod::TpcTofCircular) { + isProton = std::hypot(tpcNSigma, tofNSigma) < pidCuts.combinedNSigmaMax.value; + } else if (pidMethod == ProtonPidMethod::TpcTofRectangular) { + isProton = tpcNSigma < pidCuts.tpcNSigmaMax.value && tofNSigma < pidCuts.tofNSigmaMax.value; + } + } + + if (!isProton || !pidCuts.rejectPionKaon.value) { + return isProton; + } + + bool const isPionCompatible = std::abs(track.tpcNSigmaPi()) < pidCuts.rejectionNSigma.value && + (track.pt() < pidCuts.tofPtThreshold.value || (track.hasTOF() && std::abs(track.tofNSigmaPi()) < pidCuts.rejectionNSigma.value)); + bool const isKaonCompatible = std::abs(track.tpcNSigmaKa()) < pidCuts.rejectionNSigma.value && + (track.pt() < pidCuts.tofPtThreshold.value || (track.hasTOF() && std::abs(track.tofNSigmaKa()) < pidCuts.rejectionNSigma.value)); + return !isPionCompatible && !isKaonCompatible; + } + + bool passesControlPid(float pt, bool hasTof, float tpcNSigma, float tofNSigma, float tpcMaximum, float tofMaximum, float combinedMaximum) const + { + auto const pidMethod = static_cast(pidCuts.method.value); + tpcNSigma = std::abs(tpcNSigma); + tofNSigma = std::abs(tofNSigma); + if (pidMethod == ProtonPidMethod::TpcOnly || pt < pidCuts.tofPtThreshold.value) { + return tpcNSigma < tpcMaximum; + } + if (!hasTof) { + return false; + } + if (pidMethod == ProtonPidMethod::TpcTofCircular) { + return std::hypot(tpcNSigma, tofNSigma) < combinedMaximum; + } + return tpcNSigma < tpcMaximum && tofNSigma < tofMaximum; + } + + template + bool isSelectedPion(TrackType const& track) const + { + return passesControlPid(track.pt(), track.hasTOF(), track.tpcNSigmaPi(), track.tofNSigmaPi(), pidCuts.pionTpcNSigmaMax.value, pidCuts.pionTofNSigmaMax.value, pidCuts.pionCombinedNSigmaMax.value); + } + + template + bool isSelectedKaon(TrackType const& track) const + { + return passesControlPid(track.pt(), track.hasTOF(), track.tpcNSigmaKa(), track.tofNSigmaKa(), pidCuts.kaonTpcNSigmaMax.value, pidCuts.kaonTofNSigmaMax.value, pidCuts.kaonCombinedNSigmaMax.value); + } + + template + SelectedParticles collectSelectedParticles(JetType const& jet, bool fillQa, float weight = 1.f, bool collectControls = true) + { + SelectedParticles selected; + for (auto const& jetTrack : jet.template tracks_as()) { + auto track = jetTrack.template track_as(); + if (!isSelectedTrack(track)) { + continue; + } + + if (fillQa) { + float const signedMomentum = static_cast(track.sign()) * track.p(); + registry.fill(HIST("pid/before/tpcSignalVsSignedP"), signedMomentum, track.tpcSignal(), weight); + registry.fill(HIST("pid/before/tpcNSigmaPiVsPt"), track.pt(), track.tpcNSigmaPi(), weight); + registry.fill(HIST("pid/before/tpcNSigmaKaVsPt"), track.pt(), track.tpcNSigmaKa(), weight); + registry.fill(HIST("pid/before/tpcNSigmaPrVsPt"), track.pt(), track.tpcNSigmaPr(), weight); + registry.fill(HIST("pid/tofMatchDenominator"), track.pt(), weight); + if (track.hasTOF()) { + registry.fill(HIST("pid/tofMatchNumerator"), track.pt(), weight); + registry.fill(HIST("pid/before/tofBetaVsSignedP"), signedMomentum, track.beta(), weight); + registry.fill(HIST("pid/before/tofNSigmaPiVsPt"), track.pt(), track.tofNSigmaPi(), weight); + registry.fill(HIST("pid/before/tofNSigmaKaVsPt"), track.pt(), track.tofNSigmaKa(), weight); + registry.fill(HIST("pid/before/tofNSigmaPrVsPt"), track.pt(), track.tofNSigmaPr(), weight); + } + } + + SelectedParticle const particle{track.pt(), track.eta(), track.phi(), static_cast(track.sign())}; + if (isSelectedProton(track)) { + selected.baryons.push_back(particle); + if (fillQa) { + float const rapidity = RecoDecay::y(std::array{track.px(), track.py(), track.pz()}, o2::constants::physics::MassProton); + registry.fill(HIST("proton/pt"), particle.pt, weight); + registry.fill(HIST("proton/eta"), particle.eta, weight); + registry.fill(HIST("proton/rapidity"), rapidity, weight); + registry.fill(HIST("proton/tpcNSigmaVsPt"), track.pt(), track.tpcNSigmaPr(), weight); + if (track.hasTOF()) { + registry.fill(HIST("proton/tofNSigmaVsPt"), track.pt(), track.tofNSigmaPr(), weight); + } + registry.fill(HIST("proton/dcaXYVsPt"), track.pt(), track.dcaXY(), weight); + registry.fill(HIST("proton/dcaZVsPt"), track.pt(), track.dcaZ(), weight); + registry.fill(HIST("pid/selectedPtByCharge"), static_cast(particle.sign), particle.pt, weight); + } + } + if (collectControls && output.fillPionControl.value && isSelectedPion(track)) { + selected.pions.push_back(particle); + if (fillQa) { + registry.fill(HIST("control/pion/qa/pt"), particle.pt, weight); + registry.fill(HIST("control/pion/qa/tpcNSigmaVsPt"), particle.pt, track.tpcNSigmaPi(), weight); + if (track.hasTOF()) { + registry.fill(HIST("control/pion/qa/tofNSigmaVsPt"), particle.pt, track.tofNSigmaPi(), weight); + } + } + } + if (collectControls && output.fillKaonControl.value && isSelectedKaon(track)) { + selected.kaons.push_back(particle); + if (fillQa) { + registry.fill(HIST("control/kaon/qa/pt"), particle.pt, weight); + registry.fill(HIST("control/kaon/qa/tpcNSigmaVsPt"), particle.pt, track.tpcNSigmaKa(), weight); + if (track.hasTOF()) { + registry.fill(HIST("control/kaon/qa/tofNSigmaVsPt"), particle.pt, track.tofNSigmaKa(), weight); + } + } + } + } + return selected; + } + + static PairType getPairType(SelectedParticle const& baryon1, SelectedParticle const& baryon2) + { + return baryon1.sign == baryon2.sign ? PairType::SameSign : PairType::OppositeSign; + } + + static ChargePairType getChargePairType(SelectedParticle const& baryon1, SelectedParticle const& baryon2) + { + if (baryon1.sign > 0 && baryon2.sign > 0) { + return ChargePairType::PositivePositive; + } + if (baryon1.sign < 0 && baryon2.sign < 0) { + return ChargePairType::NegativeNegative; + } + return ChargePairType::OppositeSign; + } + + template + void fillControlNormalization(bool isMixed, float counter, float weight) + { + if constexpr (Species == ControlSpecies::Pion) { + if (isMixed) { + registry.fill(HIST("control/pion/normalizationMixedEvent"), counter, weight); + } else { + registry.fill(HIST("control/pion/normalizationSameEvent"), counter, weight); + } + } else { + if (isMixed) { + registry.fill(HIST("control/kaon/normalizationMixedEvent"), counter, weight); + } else { + registry.fill(HIST("control/kaon/normalizationSameEvent"), counter, weight); + } + } + } + + template + void fillControlParticleNormalization(bool isMixed, float counter, float weight) + { + if constexpr (Species == ControlSpecies::Pion) { + if (isMixed) { + registry.fill(HIST("control/pion/mc/particle/normalizationMixedEvent"), counter, weight); + } else { + registry.fill(HIST("control/pion/mc/particle/normalizationSameEvent"), counter, weight); + } + } else { + if (isMixed) { + registry.fill(HIST("control/kaon/mc/particle/normalizationMixedEvent"), counter, weight); + } else { + registry.fill(HIST("control/kaon/mc/particle/normalizationSameEvent"), counter, weight); + } + } + } + + template + void fillControlPair(bool isMixed, PairType pairType, float jetPt, float deltaEta, float deltaPhi, float deltaR, float pairPtLow, float pairPtHigh, float weight) + { + auto fillPairHistograms = [&](auto const& sameSignName, auto const& oppositeSignName, auto const& deltaRSameSignName, auto const& deltaROppositeSignName, auto const& sparseName) { + if (pairType == PairType::SameSign) { + registry.fill(sameSignName, deltaEta, deltaPhi, 0.5f * weight); + registry.fill(sameSignName, -deltaEta, -deltaPhi, 0.5f * weight); + registry.fill(deltaRSameSignName, deltaR, weight); + } else { + registry.fill(oppositeSignName, deltaEta, deltaPhi, 0.5f * weight); + registry.fill(oppositeSignName, -deltaEta, -deltaPhi, 0.5f * weight); + registry.fill(deltaROppositeSignName, deltaR, weight); + } + registry.fill(sparseName, static_cast(pairType), jetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(sparseName, static_cast(pairType), jetPt, -deltaEta, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + }; + + if constexpr (Species == ControlSpecies::Pion && !IsParticleLevel) { + if (isMixed) { + fillPairHistograms(HIST("control/pion/mixedEvent/sameSignDeltaEtaDeltaPhi"), HIST("control/pion/mixedEvent/oppositeSignDeltaEtaDeltaPhi"), HIST("control/pion/mixedEvent/sameSignDeltaR"), HIST("control/pion/mixedEvent/oppositeSignDeltaR"), HIST("control/pion/mixedEvent/pairSparse")); + } else { + fillPairHistograms(HIST("control/pion/sameEvent/sameSignDeltaEtaDeltaPhi"), HIST("control/pion/sameEvent/oppositeSignDeltaEtaDeltaPhi"), HIST("control/pion/sameEvent/sameSignDeltaR"), HIST("control/pion/sameEvent/oppositeSignDeltaR"), HIST("control/pion/sameEvent/pairSparse")); + } + } else if constexpr (Species == ControlSpecies::Kaon && !IsParticleLevel) { + if (isMixed) { + fillPairHistograms(HIST("control/kaon/mixedEvent/sameSignDeltaEtaDeltaPhi"), HIST("control/kaon/mixedEvent/oppositeSignDeltaEtaDeltaPhi"), HIST("control/kaon/mixedEvent/sameSignDeltaR"), HIST("control/kaon/mixedEvent/oppositeSignDeltaR"), HIST("control/kaon/mixedEvent/pairSparse")); + } else { + fillPairHistograms(HIST("control/kaon/sameEvent/sameSignDeltaEtaDeltaPhi"), HIST("control/kaon/sameEvent/oppositeSignDeltaEtaDeltaPhi"), HIST("control/kaon/sameEvent/sameSignDeltaR"), HIST("control/kaon/sameEvent/oppositeSignDeltaR"), HIST("control/kaon/sameEvent/pairSparse")); + } + } else if constexpr (Species == ControlSpecies::Pion) { + if (isMixed) { + fillPairHistograms(HIST("control/pion/mc/particle/mixedEvent/sameSignDeltaEtaDeltaPhi"), HIST("control/pion/mc/particle/mixedEvent/oppositeSignDeltaEtaDeltaPhi"), HIST("control/pion/mc/particle/mixedEvent/sameSignDeltaR"), HIST("control/pion/mc/particle/mixedEvent/oppositeSignDeltaR"), HIST("control/pion/mc/particle/mixedEvent/pairSparse")); + } else { + fillPairHistograms(HIST("control/pion/mc/particle/sameEvent/sameSignDeltaEtaDeltaPhi"), HIST("control/pion/mc/particle/sameEvent/oppositeSignDeltaEtaDeltaPhi"), HIST("control/pion/mc/particle/sameEvent/sameSignDeltaR"), HIST("control/pion/mc/particle/sameEvent/oppositeSignDeltaR"), HIST("control/pion/mc/particle/sameEvent/pairSparse")); + } + } else { + if (isMixed) { + fillPairHistograms(HIST("control/kaon/mc/particle/mixedEvent/sameSignDeltaEtaDeltaPhi"), HIST("control/kaon/mc/particle/mixedEvent/oppositeSignDeltaEtaDeltaPhi"), HIST("control/kaon/mc/particle/mixedEvent/sameSignDeltaR"), HIST("control/kaon/mc/particle/mixedEvent/oppositeSignDeltaR"), HIST("control/kaon/mc/particle/mixedEvent/pairSparse")); + } else { + fillPairHistograms(HIST("control/kaon/mc/particle/sameEvent/sameSignDeltaEtaDeltaPhi"), HIST("control/kaon/mc/particle/sameEvent/oppositeSignDeltaEtaDeltaPhi"), HIST("control/kaon/mc/particle/sameEvent/sameSignDeltaR"), HIST("control/kaon/mc/particle/sameEvent/oppositeSignDeltaR"), HIST("control/kaon/mc/particle/sameEvent/pairSparse")); + } + } + } + + template + void fillControlSameJetPairs(std::vector const& particles, float jetPt, float weight) + { + if constexpr (IsParticleLevel) { + fillControlParticleNormalization(false, 2.f, weight); + } else { + fillControlNormalization(false, 2.f, weight); + } + for (size_t first = 0; first < particles.size(); ++first) { + for (size_t second = first + 1; second < particles.size(); ++second) { + PairType const pairType = getPairType(particles[first], particles[second]); + float const deltaEta = particles[first].eta - particles[second].eta; + float const deltaPhi = RecoDecay::constrainAngle(particles[first].phi - particles[second].phi, -o2::constants::math::PI); + float const deltaR = std::hypot(deltaEta, deltaPhi); + float const pairPtLow = std::min(particles[first].pt, particles[second].pt); + float const pairPtHigh = std::max(particles[first].pt, particles[second].pt); + fillControlPair(false, pairType, jetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, weight); + if constexpr (IsParticleLevel) { + fillControlParticleNormalization(false, pairType == PairType::SameSign ? 3.f : 4.f, weight); + } else { + fillControlNormalization(false, pairType == PairType::SameSign ? 3.f : 4.f, weight); + } + } + } + } + + template + void fillControlMixedJetPairs(std::vector const& particles1, + std::vector const& particles2, + JetType1 const& jet1, + JetType2 const& jet2, + float weight) + { + if constexpr (IsParticleLevel) { + fillControlParticleNormalization(true, 2.f, weight); + } else { + fillControlNormalization(true, 2.f, weight); + } + float const mixedJetPt = 0.5f * (jet1.pt() + jet2.pt()); + for (auto const& particle1 : particles1) { + float const localEta1 = particle1.eta - jet1.eta(); + float const localPhi1 = RecoDecay::constrainAngle(particle1.phi - jet1.phi(), -o2::constants::math::PI); + for (auto const& particle2 : particles2) { + float const localEta2 = particle2.eta - jet2.eta(); + float const localPhi2 = RecoDecay::constrainAngle(particle2.phi - jet2.phi(), -o2::constants::math::PI); + float const deltaEta = localEta1 - localEta2; + float const deltaPhi = RecoDecay::constrainAngle(localPhi1 - localPhi2, -o2::constants::math::PI); + float const deltaR = std::hypot(deltaEta, deltaPhi); + PairType const pairType = getPairType(particle1, particle2); + float const pairPtLow = std::min(particle1.pt, particle2.pt); + float const pairPtHigh = std::max(particle1.pt, particle2.pt); + fillControlPair(true, pairType, mixedJetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, weight); + if constexpr (IsParticleLevel) { + fillControlParticleNormalization(true, pairType == PairType::SameSign ? 3.f : 4.f, weight); + } else { + fillControlNormalization(true, pairType == PairType::SameSign ? 3.f : 4.f, weight); + } + } + } + } + + template + std::vector collectInclusiveBaryons(TracksType const& tracks) + { + std::vector baryons; + for (auto const& track : tracks) { + if (!isSelectedTrack(track)) { + continue; + } + if (!isSelectedProton(track)) { + continue; + } + baryons.push_back({track.pt(), track.eta(), track.phi(), static_cast(track.sign())}); + } + return baryons; + } + + template + bool isSelectedInclusiveCollision(CollisionType const& collision) + { + if (std::abs(collision.posZ()) > eventCuts.vertexZMax.value || + collision.trackOccupancyInTimeRange() < eventCuts.occupancyMin.value || + collision.trackOccupancyInTimeRange() > eventCuts.occupancyMax.value) { + return false; + } + + uint16_t const collisionSelection = jetderiveddatautilities::setEventSelectionBit(collision); + bool isOrCondition = false; + bool passedOrCondition = false; + for (auto const& selectionBit : eventSelectionBits) { + if (selectionBit == -1) { + isOrCondition = true; + continue; + } + bool const passed = collisionSelection & (1U << selectionBit); + if (!isOrCondition && !passed) { + return false; + } + if (isOrCondition && passed) { + passedOrCondition = true; + } + } + if (isOrCondition && !passedOrCondition) { + return false; + } + return !eventCuts.applyRctSelection.value || inclusiveRctChecker.checkTable(collision); + } + + void fillInclusivePair(bool isMixed, + SelectedParticle const& baryon1, + SelectedParticle const& baryon2, + float weight) + { + PairType const pairType = getPairType(baryon1, baryon2); + ChargePairType const chargePairType = getChargePairType(baryon1, baryon2); + float const deltaEta = baryon1.eta - baryon2.eta; + float const deltaPhi = RecoDecay::constrainAngle(baryon1.phi - baryon2.phi, -o2::constants::math::PIHalf); + float const reflectedDeltaPhi = RecoDecay::constrainAngle(-deltaPhi, -o2::constants::math::PIHalf); + float const pairPtLow = std::min(baryon1.pt, baryon2.pt); + float const pairPtHigh = std::max(baryon1.pt, baryon2.pt); + if (isMixed) { + if (pairType == PairType::SameSign) { + registry.fill(HIST("inclusive/mixedEvent/sameSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); + registry.fill(HIST("inclusive/mixedEvent/sameSignDeltaEtaDeltaPhi"), -deltaEta, reflectedDeltaPhi, 0.5f * weight); + } else { + registry.fill(HIST("inclusive/mixedEvent/oppositeSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); + registry.fill(HIST("inclusive/mixedEvent/oppositeSignDeltaEtaDeltaPhi"), -deltaEta, reflectedDeltaPhi, 0.5f * weight); + } + if (output.fillChargeSeparated.value) { + registry.fill(HIST("inclusive/mixedEvent/chargePairSparse"), static_cast(chargePairType), deltaEta, deltaPhi, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("inclusive/mixedEvent/chargePairSparse"), static_cast(chargePairType), -deltaEta, reflectedDeltaPhi, pairPtLow, pairPtHigh, 0.5f * weight); + } + } else { + if (pairType == PairType::SameSign) { + registry.fill(HIST("inclusive/sameEvent/sameSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); + registry.fill(HIST("inclusive/sameEvent/sameSignDeltaEtaDeltaPhi"), -deltaEta, reflectedDeltaPhi, 0.5f * weight); + } else { + registry.fill(HIST("inclusive/sameEvent/oppositeSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); + registry.fill(HIST("inclusive/sameEvent/oppositeSignDeltaEtaDeltaPhi"), -deltaEta, reflectedDeltaPhi, 0.5f * weight); + } + if (output.fillChargeSeparated.value) { + registry.fill(HIST("inclusive/sameEvent/chargePairSparse"), static_cast(chargePairType), deltaEta, deltaPhi, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("inclusive/sameEvent/chargePairSparse"), static_cast(chargePairType), -deltaEta, reflectedDeltaPhi, pairPtLow, pairPtHigh, 0.5f * weight); + } + } + } + + template + bool isMatchedJetPair(float jetPtDifference, int jetNchDifference, float jetEtaDifference) const + { + if constexpr (Reference == MixedReference::Random) { + return true; + } else if constexpr (Reference == MixedReference::Nominal) { + return jetPtDifference <= mixing.nominalJetPtDifferenceMax.value && + jetNchDifference <= mixing.nominalJetNchDifferenceMax.value && + jetEtaDifference <= mixing.nominalJetEtaDifferenceMax.value; + } else { + return jetPtDifference <= mixing.looseJetPtDifferenceMax.value && + jetNchDifference <= mixing.looseJetNchDifferenceMax.value && + jetEtaDifference <= mixing.looseJetEtaDifferenceMax.value; + } + } + + template + void fillPair(PairType pairType, ChargePairType chargePairType, float jetPt, float deltaEta, float deltaPhi, float deltaR, float pairPtLow, float pairPtHigh, float weight) + { + auto const pairTypeValue = static_cast(pairType); + auto fillSymmetrized = [&](auto const& sameSignName, auto const& oppositeSignName, auto const& sparseName) { + if (pairType == PairType::SameSign) { + registry.fill(sameSignName, deltaEta, deltaPhi, 0.5f * weight); + registry.fill(sameSignName, -deltaEta, -deltaPhi, 0.5f * weight); + } else { + registry.fill(oppositeSignName, deltaEta, deltaPhi, 0.5f * weight); + registry.fill(oppositeSignName, -deltaEta, -deltaPhi, 0.5f * weight); + } + registry.fill(sparseName, pairTypeValue, jetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(sparseName, pairTypeValue, jetPt, -deltaEta, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + }; + + if constexpr (Reference == MixedReference::Nominal) { + fillSymmetrized(HIST("mixedEvent/nominal/sameSignDeltaEtaDeltaPhi"), HIST("mixedEvent/nominal/oppositeSignDeltaEtaDeltaPhi"), HIST("mixedEvent/nominal/pairSparse")); + if (pairType == PairType::SameSign) { + registry.fill(HIST("mixedEvent/nominal/sameSignDeltaR"), deltaR, weight); + } else { + registry.fill(HIST("mixedEvent/nominal/oppositeSignDeltaR"), deltaR, weight); + } + if (output.fillChargeSeparated.value) { + registry.fill(HIST("mixedEvent/nominal/chargePairSparse"), static_cast(chargePairType), jetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("mixedEvent/nominal/chargePairSparse"), static_cast(chargePairType), jetPt, -deltaEta, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + } + } else if constexpr (Reference == MixedReference::Loose) { + fillSymmetrized(HIST("mixedEvent/loose/sameSignDeltaEtaDeltaPhi"), HIST("mixedEvent/loose/oppositeSignDeltaEtaDeltaPhi"), HIST("mixedEvent/loose/pairSparse")); + if (pairType == PairType::SameSign) { + registry.fill(HIST("mixedEvent/loose/sameSignDeltaR"), deltaR, weight); + } else { + registry.fill(HIST("mixedEvent/loose/oppositeSignDeltaR"), deltaR, weight); + } + if (output.fillChargeSeparated.value) { + registry.fill(HIST("mixedEvent/loose/chargePairSparse"), static_cast(chargePairType), jetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("mixedEvent/loose/chargePairSparse"), static_cast(chargePairType), jetPt, -deltaEta, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + } + } else { + fillSymmetrized(HIST("mixedEvent/random/sameSignDeltaEtaDeltaPhi"), HIST("mixedEvent/random/oppositeSignDeltaEtaDeltaPhi"), HIST("mixedEvent/random/pairSparse")); + if (pairType == PairType::SameSign) { + registry.fill(HIST("mixedEvent/random/sameSignDeltaR"), deltaR, weight); + } else { + registry.fill(HIST("mixedEvent/random/oppositeSignDeltaR"), deltaR, weight); + } + if (output.fillChargeSeparated.value) { + registry.fill(HIST("mixedEvent/random/chargePairSparse"), static_cast(chargePairType), jetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("mixedEvent/random/chargePairSparse"), static_cast(chargePairType), jetPt, -deltaEta, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + } + } + } + + template + void fillMixedNormalization(float counter, float weight) + { + if constexpr (Reference == MixedReference::Nominal) { + registry.fill(HIST("normalization/mixedEventNominal"), counter, weight); + } else if constexpr (Reference == MixedReference::Loose) { + registry.fill(HIST("normalization/mixedEventLoose"), counter, weight); + } else { + registry.fill(HIST("normalization/mixedEventRandom"), counter, weight); + } + } + + void fillSameEventPair(PairType pairType, ChargePairType chargePairType, float jetPt, float deltaEta, float deltaPhi, float deltaR, float pairPtLow, float pairPtHigh, float weight) + { + auto const pairTypeValue = static_cast(pairType); + if (pairType == PairType::SameSign) { + registry.fill(HIST("sameEvent/sameSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); + registry.fill(HIST("sameEvent/sameSignDeltaEtaDeltaPhi"), -deltaEta, -deltaPhi, 0.5f * weight); + registry.fill(HIST("sameEvent/sameSignDeltaR"), deltaR, weight); + } else { + registry.fill(HIST("sameEvent/oppositeSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); + registry.fill(HIST("sameEvent/oppositeSignDeltaEtaDeltaPhi"), -deltaEta, -deltaPhi, 0.5f * weight); + registry.fill(HIST("sameEvent/oppositeSignDeltaR"), deltaR, weight); + } + registry.fill(HIST("sameEvent/pairSparse"), pairTypeValue, jetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("sameEvent/pairSparse"), pairTypeValue, jetPt, -deltaEta, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + if (output.fillChargeSeparated.value) { + registry.fill(HIST("sameEvent/chargePairSparse"), static_cast(chargePairType), jetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("sameEvent/chargePairSparse"), static_cast(chargePairType), jetPt, -deltaEta, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + } + } + + template + SelectedParticles collectParticleCandidates(JetType const& jet, bool fillQa, float weight) + { + SelectedParticles selected; + for (auto const& particle : jet.template tracks_as()) { + if (!particle.isPhysicalPrimary()) { + continue; + } + if (particle.pt() < trackCuts.protonPtMin.value || particle.pt() >= trackCuts.protonPtMax.value || + std::abs(particle.eta()) > trackCuts.protonEtaMax.value) { + continue; + } + int8_t const sign = particle.pdgCode() > 0 ? 1 : -1; + SelectedParticle const selectedParticle{particle.pt(), particle.eta(), particle.phi(), sign}; + int const absolutePdg = std::abs(particle.pdgCode()); + if (absolutePdg == PDG_t::kProton) { + selected.baryons.push_back(selectedParticle); + if (fillQa) { + registry.fill(HIST("mc/pid/generatedPrimaryProtonPt"), selectedParticle.pt, weight); + } + } else if (output.fillPionControl.value && absolutePdg == PDG_t::kPiPlus) { + selected.pions.push_back(selectedParticle); + if (fillQa) { + registry.fill(HIST("control/pion/mc/generatedPrimaryPt"), selectedParticle.pt, weight); + } + } else if (output.fillKaonControl.value && absolutePdg == PDG_t::kKPlus) { + selected.kaons.push_back(selectedParticle); + if (fillQa) { + registry.fill(HIST("control/kaon/mc/generatedPrimaryPt"), selectedParticle.pt, weight); + } + } + } + return selected; + } + + void fillParticlePair(bool isMixed, PairType pairType, float jetPt, float deltaEta, float deltaPhi, float deltaR, float pairPtLow, float pairPtHigh, float weight) + { + if (isMixed) { + if (pairType == PairType::SameSign) { + registry.fill(HIST("mc/particle/mixedEvent/sameSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); + registry.fill(HIST("mc/particle/mixedEvent/sameSignDeltaEtaDeltaPhi"), -deltaEta, -deltaPhi, 0.5f * weight); + registry.fill(HIST("mc/particle/mixedEvent/sameSignDeltaR"), deltaR, weight); + } else { + registry.fill(HIST("mc/particle/mixedEvent/oppositeSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); + registry.fill(HIST("mc/particle/mixedEvent/oppositeSignDeltaEtaDeltaPhi"), -deltaEta, -deltaPhi, 0.5f * weight); + registry.fill(HIST("mc/particle/mixedEvent/oppositeSignDeltaR"), deltaR, weight); + } + registry.fill(HIST("mc/particle/mixedEvent/pairSparse"), static_cast(pairType), jetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("mc/particle/mixedEvent/pairSparse"), static_cast(pairType), jetPt, -deltaEta, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + } else { + if (pairType == PairType::SameSign) { + registry.fill(HIST("mc/particle/sameEvent/sameSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); + registry.fill(HIST("mc/particle/sameEvent/sameSignDeltaEtaDeltaPhi"), -deltaEta, -deltaPhi, 0.5f * weight); + registry.fill(HIST("mc/particle/sameEvent/sameSignDeltaR"), deltaR, weight); + } else { + registry.fill(HIST("mc/particle/sameEvent/oppositeSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); + registry.fill(HIST("mc/particle/sameEvent/oppositeSignDeltaEtaDeltaPhi"), -deltaEta, -deltaPhi, 0.5f * weight); + registry.fill(HIST("mc/particle/sameEvent/oppositeSignDeltaR"), deltaR, weight); + } + registry.fill(HIST("mc/particle/sameEvent/pairSparse"), static_cast(pairType), jetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("mc/particle/sameEvent/pairSparse"), static_cast(pairType), jetPt, -deltaEta, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + } + } + + template + void fillEfficiencyPairs(std::vector const& particles, float detectorJetPt, float weight) + { + for (size_t first = 0; first < particles.size(); ++first) { + for (size_t second = first + 1; second < particles.size(); ++second) { + auto const pairType = static_cast(getPairType(particles[first], particles[second])); + float const deltaEta = particles[first].eta - particles[second].eta; + float const deltaPhi = RecoDecay::constrainAngle(particles[first].phi - particles[second].phi, -o2::constants::math::PI); + float const deltaR = std::hypot(deltaEta, deltaPhi); + float const pairPtLow = std::min(particles[first].pt, particles[second].pt); + float const pairPtHigh = std::max(particles[first].pt, particles[second].pt); + if constexpr (Stage == EfficiencyStage::Generated) { + registry.fill(HIST("mc/efficiency/generatedPair"), pairType, detectorJetPt, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("mc/efficiency/generatedPair"), pairType, detectorJetPt, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + } else if constexpr (Stage == EfficiencyStage::TrackSelected) { + registry.fill(HIST("mc/efficiency/trackSelectedPair"), pairType, detectorJetPt, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("mc/efficiency/trackSelectedPair"), pairType, detectorJetPt, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + } else { + registry.fill(HIST("mc/efficiency/pidSelectedPair"), pairType, detectorJetPt, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("mc/efficiency/pidSelectedPair"), pairType, detectorJetPt, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + } + } + } + } + + template + void fillControlMCPidQa(TrackType const& track, ParticleType const& particle, float weight) + { + int const expectedPdg = Species == ControlSpecies::Pion ? PDG_t::kPiPlus : PDG_t::kKPlus; + bool const isTruePrimary = std::abs(particle.pdgCode()) == expectedPdg && particle.isPhysicalPrimary(); + bool const isSelected = Species == ControlSpecies::Pion ? isSelectedPion(track) : isSelectedKaon(track); + int origin = 2; + if (particle.isPhysicalPrimary()) { + origin = 1; + } else if (particle.getProcess() == TMCProcess::kPDecay) { + origin = 3; + } + + if constexpr (Species == ControlSpecies::Pion) { + if (isTruePrimary) { + registry.fill(HIST("control/pion/mc/reconstructedTruePt"), track.pt(), weight); + } + if (!isSelected) { + return; + } + registry.fill(HIST("control/pion/mc/selectedCandidateOriginVsPt"), track.pt(), static_cast(origin), weight); + registry.fill(HIST("control/pion/mc/selectedCandidateTruePdgVsPt"), track.pt(), static_cast(particle.pdgCode()), weight); + if (isTruePrimary) { + registry.fill(HIST("control/pion/mc/selectedTruePt"), track.pt(), weight); + registry.fill(HIST("control/pion/mc/ptResponsePrimary"), particle.pt(), track.pt(), weight); + } + } else { + if (isTruePrimary) { + registry.fill(HIST("control/kaon/mc/reconstructedTruePt"), track.pt(), weight); + } + if (!isSelected) { + return; + } + registry.fill(HIST("control/kaon/mc/selectedCandidateOriginVsPt"), track.pt(), static_cast(origin), weight); + registry.fill(HIST("control/kaon/mc/selectedCandidateTruePdgVsPt"), track.pt(), static_cast(particle.pdgCode()), weight); + if (isTruePrimary) { + registry.fill(HIST("control/kaon/mc/selectedTruePt"), track.pt(), weight); + registry.fill(HIST("control/kaon/mc/ptResponsePrimary"), particle.pt(), track.pt(), weight); + } + } + } + + template + void fillMCPidQa(JetType const& jet, float weight) + { + for (auto const& jetTrack : jet.template tracks_as()) { + auto track = jetTrack.template track_as(); + if (!isSelectedTrack(track) || !track.has_mcParticle()) { + continue; + } + auto particle = track.template mcParticle_as(); + if (output.fillPionControl.value) { + fillControlMCPidQa(track, particle, weight); + } + if (output.fillKaonControl.value) { + fillControlMCPidQa(track, particle, weight); + } + bool const isTruePrimaryProton = std::abs(particle.pdgCode()) == PDG_t::kProton && particle.isPhysicalPrimary(); + if (isTruePrimaryProton) { + registry.fill(HIST("mc/pid/reconstructedTrueProtonPt"), track.pt(), weight); + } + if (!isSelectedProton(track)) { + continue; + } + int origin = 2; + if (particle.isPhysicalPrimary()) { + origin = 1; + } else if (particle.getProcess() == TMCProcess::kPDecay) { + origin = 3; + } + registry.fill(HIST("mc/pid/selectedCandidateOriginVsPt"), track.pt(), static_cast(origin), weight); + registry.fill(HIST("mc/pid/selectedCandidateTruePdgVsPt"), track.pt(), static_cast(particle.pdgCode()), weight); + if (isTruePrimaryProton) { + registry.fill(HIST("mc/pid/selectedTrueProtonPt"), track.pt(), weight); + registry.fill(HIST("mc/pid/ptResponsePrimaryProton"), particle.pt(), track.pt(), weight); + } + } + } + + template + void fillReconstructedSameEvent(CollisionType const& collision, JetsType const& jets, float weight = 1.f) + { + if (!isSelectedCollision(collision, true)) { + return; + } + + if (output.fillQa.value) { + registry.fill(HIST("event/zVertex"), collision.posZ(), weight); + registry.fill(HIST("event/multiplicityFT0C"), collision.multFT0C(), weight); + } + registry.fill(HIST("normalization/sameEvent"), 1.f, weight); + if (output.fillPionControl.value) { + fillControlNormalization(false, 1.f, weight); + } + if (output.fillKaonControl.value) { + fillControlNormalization(false, 1.f, weight); + } + + for (auto const& jet : jets) { + if (output.fillQa.value && isSelectedJetGeometry(jet)) { + registry.fill(HIST("jet/ptBeforePtSelection"), jet.pt(), weight); + } + if (!isSelectedJet(jet)) { + continue; + } + + registry.fill(HIST("normalization/sameEvent"), 2.f, weight); + if (output.fillQa.value) { + registry.fill(HIST("jet/pt"), jet.pt(), weight); + registry.fill(HIST("jet/eta"), jet.eta(), weight); + registry.fill(HIST("jet/phi"), jet.phi(), weight); + registry.fill(HIST("jet/nConstituents"), static_cast(jet.tracksIds().size()), weight); + registry.fill(HIST("jet/nConstituentsVsPt"), jet.pt(), static_cast(jet.tracksIds().size()), weight); + } + + auto selectedParticles = collectSelectedParticles(jet, output.fillQa.value, weight); + auto const& baryons = selectedParticles.baryons; + if (output.fillQa.value) { + int numberOfProtons = 0; + int numberOfAntiprotons = 0; + for (auto const& baryon : baryons) { + baryon.sign > 0 ? ++numberOfProtons : ++numberOfAntiprotons; + } + registry.fill(HIST("jet/nSelectedBaryons"), static_cast(baryons.size()), weight); + registry.fill(HIST("jet/nProtons"), numberOfProtons, weight); + registry.fill(HIST("jet/nAntiprotons"), numberOfAntiprotons, weight); + registry.fill(HIST("jet/nProtonsVsAntiprotons"), numberOfProtons, numberOfAntiprotons, weight); + registry.fill(HIST("jet/nBaryonsVsPt"), jet.pt(), static_cast(baryons.size()), weight); + } + + for (size_t first = 0; first < baryons.size(); ++first) { + for (size_t second = first + 1; second < baryons.size(); ++second) { + PairType const pairType = getPairType(baryons[first], baryons[second]); + ChargePairType const chargePairType = getChargePairType(baryons[first], baryons[second]); + float const deltaEta = baryons[first].eta - baryons[second].eta; + float const deltaPhi = RecoDecay::constrainAngle(baryons[first].phi - baryons[second].phi, -o2::constants::math::PI); + float const deltaR = std::hypot(deltaEta, deltaPhi); + float const pairPtLow = std::min(baryons[first].pt, baryons[second].pt); + float const pairPtHigh = std::max(baryons[first].pt, baryons[second].pt); + fillSameEventPair(pairType, chargePairType, jet.pt(), deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, weight); + if (output.fillQa.value) { + if (pairType == PairType::SameSign) { + registry.fill(HIST("jet/sameSignPairsVsPt"), jet.pt(), weight); + } else { + registry.fill(HIST("jet/oppositeSignPairsVsPt"), jet.pt(), weight); + } + } + + float const counterBin = pairType == PairType::SameSign ? 3.f : 4.f; + registry.fill(HIST("normalization/sameEvent"), counterBin, weight); + if (std::abs(deltaPhi) < axes.nearSideDeltaPhiMax.value && std::abs(deltaEta) < axes.nearSideDeltaEtaMax.value) { + registry.fill(HIST("normalization/sameEvent"), pairType == PairType::SameSign ? 5.f : 6.f, weight); + } + } + } + if (output.fillPionControl.value) { + fillControlSameJetPairs(selectedParticles.pions, jet.pt(), weight); + } + if (output.fillKaonControl.value) { + fillControlSameJetPairs(selectedParticles.kaons, jet.pt(), weight); + } + } + } + + void processSameEvent(aod::JetCollision const& collision, + ChargedJetsWithConstituents const& jets, + JetTracksWithOriginal const&, + BaryonTracks const&) + { + fillReconstructedSameEvent(collision, jets); + } + PROCESS_SWITCH(BaryonPairInJets, processSameEvent, "Same-event identified baryon pairs in charged jets", true); + + template + void fillMixedJetPair(JetType1 const& jet1, JetType2 const& jet2, float weight) + { + float const jetPtDifference = std::abs(jet1.pt() - jet2.pt()); + int const jetNchDifference = std::abs(static_cast(jet1.tracksIds().size()) - static_cast(jet2.tracksIds().size())); + float const jetEtaDifference = std::abs(jet1.eta() - jet2.eta()); + if (!isMatchedJetPair(jetPtDifference, jetNchDifference, jetEtaDifference)) { + return; + } + + if constexpr (Reference == MixedReference::Random) { + registry.fill(HIST("mixedEvent/jetPtDifference"), jetPtDifference, weight); + registry.fill(HIST("mixedEvent/jetEtaDifference"), jetEtaDifference, weight); + registry.fill(HIST("mixedEvent/jetNchDifference"), jetNchDifference, weight); + } + + constexpr bool CollectControls = Reference == MixedReference::Nominal; + auto selectedParticles1 = collectSelectedParticles(jet1, false, 1.f, CollectControls); + auto selectedParticles2 = collectSelectedParticles(jet2, false, 1.f, CollectControls); + auto const& baryons1 = selectedParticles1.baryons; + auto const& baryons2 = selectedParticles2.baryons; + float const mixedJetPt = 0.5f * (jet1.pt() + jet2.pt()); + fillMixedNormalization(2.f, weight); + + for (auto const& baryon1 : baryons1) { + float const localEta1 = baryon1.eta - jet1.eta(); + float const localPhi1 = RecoDecay::constrainAngle(baryon1.phi - jet1.phi(), -o2::constants::math::PI); + for (auto const& baryon2 : baryons2) { + float const localEta2 = baryon2.eta - jet2.eta(); + float const localPhi2 = RecoDecay::constrainAngle(baryon2.phi - jet2.phi(), -o2::constants::math::PI); + float const deltaEta = localEta1 - localEta2; + float const deltaPhi = RecoDecay::constrainAngle(localPhi1 - localPhi2, -o2::constants::math::PI); + float const deltaR = std::hypot(deltaEta, deltaPhi); + PairType const pairType = getPairType(baryon1, baryon2); + ChargePairType const chargePairType = getChargePairType(baryon1, baryon2); + float const pairPtLow = std::min(baryon1.pt, baryon2.pt); + float const pairPtHigh = std::max(baryon1.pt, baryon2.pt); + fillPair(pairType, chargePairType, mixedJetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, weight); + fillMixedNormalization(pairType == PairType::SameSign ? 3.f : 4.f, weight); + if (std::abs(deltaPhi) < axes.nearSideDeltaPhiMax.value && std::abs(deltaEta) < axes.nearSideDeltaEtaMax.value) { + fillMixedNormalization(pairType == PairType::SameSign ? 5.f : 6.f, weight); + } + } + } + if constexpr (Reference == MixedReference::Nominal) { + if (output.fillPionControl.value) { + fillControlMixedJetPairs(selectedParticles1.pions, selectedParticles2.pions, jet1, jet2, weight); + } + if (output.fillKaonControl.value) { + fillControlMixedJetPairs(selectedParticles1.kaons, selectedParticles2.kaons, jet1, jet2, weight); + } + } + } + + void processMixedEvent(aod::JetCollisions const& collisions, + ChargedJetsWithConstituents const& jets, + JetTracksWithOriginal const&, + BaryonTracks const&) + { + MixingBinning mixingBinning{{mixing.vertexZBins, mixing.multiplicityBins}}; + auto jetsTuple = std::make_tuple(jets); + SameKindPair mixedCollisionPairs{ + mixingBinning, mixing.numberOfMixedEvents.value, -1, collisions, jetsTuple, &mixingCache}; + + for (auto iterator = mixedCollisionPairs.begin(); iterator != mixedCollisionPairs.end(); ++iterator) { + auto& [collision1, jets1, collision2, jets2] = *iterator; + if (!isSelectedCollision(collision1, false) || !isSelectedCollision(collision2, false)) { + continue; + } + + float const weight = 1.f / static_cast(iterator.currentWindowNeighbours()); + registry.fill(HIST("normalization/mixedEventNominal"), 1.f, weight); + if (output.fillPionControl.value) { + fillControlNormalization(true, 1.f, weight); + } + if (output.fillKaonControl.value) { + fillControlNormalization(true, 1.f, weight); + } + if (output.fillReferenceVariations.value) { + registry.fill(HIST("normalization/mixedEventLoose"), 1.f, weight); + registry.fill(HIST("normalization/mixedEventRandom"), 1.f, weight); + } + + for (auto const& jet1 : jets1) { + if (!isSelectedJet(jet1)) { + continue; + } + for (auto const& jet2 : jets2) { + if (!isSelectedJet(jet2)) { + continue; + } + fillMixedJetPair(jet1, jet2, weight); + if (output.fillReferenceVariations.value) { + fillMixedJetPair(jet1, jet2, weight); + fillMixedJetPair(jet1, jet2, weight); + } + } + } + } + } + PROCESS_SWITCH(BaryonPairInJets, processMixedEvent, "Mixed-event identified baryon pairs in charged jets", true); + + void processMCD(aod::JetCollisionsMCD::iterator const& collision, + aod::JetMcCollisions const&, + ChargedMCDJetsWithConstituents const& jets, + JetTracksWithOriginal const&, + BaryonTracksMC const&, + aod::McParticles const&) + { + float const weight = collision.mcCollision().weight(); + fillReconstructedSameEvent(collision, jets, weight); + if (!isSelectedCollision(collision, false)) { + return; + } + for (auto const& jet : jets) { + if (isSelectedJet(jet)) { + fillMCPidQa(jet, weight); + } + } + } + PROCESS_SWITCH(BaryonPairInJets, processMCD, "Detector-level MC reconstructed baryon pairs and PID truth QA", false); + + void processMixedEventMCD(aod::JetCollisionsMCD const& collisions, + aod::JetMcCollisions const&, + ChargedMCDJetsWithConstituents const& jets, + JetTracksWithOriginal const&, + BaryonTracksMC const&, + aod::McParticles const&) + { + MixingBinning mixingBinning{{mixing.vertexZBins, mixing.multiplicityBins}}; + auto jetsTuple = std::make_tuple(jets); + SameKindPair mixedCollisionPairs{ + mixingBinning, mixing.numberOfMixedEvents.value, -1, collisions, jetsTuple, &mcdMixingCache}; + + for (auto iterator = mixedCollisionPairs.begin(); iterator != mixedCollisionPairs.end(); ++iterator) { + auto& [collision1, jets1, collision2, jets2] = *iterator; + if (!isSelectedCollision(collision1, false) || !isSelectedCollision(collision2, false)) { + continue; + } + float const weight = collision1.mcCollision().weight() / static_cast(iterator.currentWindowNeighbours()); + registry.fill(HIST("normalization/mixedEventNominal"), 1.f, weight); + if (output.fillPionControl.value) { + fillControlNormalization(true, 1.f, weight); + } + if (output.fillKaonControl.value) { + fillControlNormalization(true, 1.f, weight); + } + if (output.fillReferenceVariations.value) { + registry.fill(HIST("normalization/mixedEventLoose"), 1.f, weight); + registry.fill(HIST("normalization/mixedEventRandom"), 1.f, weight); + } + for (auto const& jet1 : jets1) { + if (!isSelectedJet(jet1)) { + continue; + } + for (auto const& jet2 : jets2) { + if (!isSelectedJet(jet2)) { + continue; + } + fillMixedJetPair(jet1, jet2, weight); + if (output.fillReferenceVariations.value) { + fillMixedJetPair(jet1, jet2, weight); + fillMixedJetPair(jet1, jet2, weight); + } + } + } + } + } + PROCESS_SWITCH(BaryonPairInJets, processMixedEventMCD, "Detector-level MC mixed-event baryon pairs", false); + + void processMCP(aod::JetMcCollision const& collision, + ChargedMCPJetsWithConstituents const& jets, + aod::JetParticles const&) + { + if (std::abs(collision.posZ()) > eventCuts.vertexZMax.value) { + return; + } + float const weight = collision.weight(); + registry.fill(HIST("mc/particle/normalizationSameEvent"), 1.f, weight); + if (output.fillPionControl.value) { + fillControlParticleNormalization(false, 1.f, weight); + } + if (output.fillKaonControl.value) { + fillControlParticleNormalization(false, 1.f, weight); + } + for (auto const& jet : jets) { + if (!isSelectedJet(jet)) { + continue; + } + registry.fill(HIST("mc/particle/normalizationSameEvent"), 2.f, weight); + auto selectedParticles = collectParticleCandidates(jet, true, weight); + auto const& baryons = selectedParticles.baryons; + for (size_t first = 0; first < baryons.size(); ++first) { + for (size_t second = first + 1; second < baryons.size(); ++second) { + PairType const pairType = getPairType(baryons[first], baryons[second]); + float const deltaEta = baryons[first].eta - baryons[second].eta; + float const deltaPhi = RecoDecay::constrainAngle(baryons[first].phi - baryons[second].phi, -o2::constants::math::PI); + float const deltaR = std::hypot(deltaEta, deltaPhi); + float const pairPtLow = std::min(baryons[first].pt, baryons[second].pt); + float const pairPtHigh = std::max(baryons[first].pt, baryons[second].pt); + fillParticlePair(false, pairType, jet.pt(), deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, weight); + registry.fill(HIST("mc/particle/normalizationSameEvent"), pairType == PairType::SameSign ? 3.f : 4.f, weight); + } + } + if (output.fillPionControl.value) { + fillControlSameJetPairs(selectedParticles.pions, jet.pt(), weight); + } + if (output.fillKaonControl.value) { + fillControlSameJetPairs(selectedParticles.kaons, jet.pt(), weight); + } + } + } + PROCESS_SWITCH(BaryonPairInJets, processMCP, "Particle-level MC baryon pairs", false); + + void processMixedEventMCP(aod::JetMcCollisions const& collisions, + ChargedMCPJetsWithConstituents const& jets, + aod::JetParticles const&) + { + MCPMixingBinning mixingBinning{{mixing.vertexZBins, mixing.multiplicityBins}}; + auto jetsTuple = std::make_tuple(jets); + SameKindPair mixedCollisionPairs{ + mixingBinning, mixing.numberOfMixedEvents.value, -1, collisions, jetsTuple, &mcpMixingCache}; + for (auto iterator = mixedCollisionPairs.begin(); iterator != mixedCollisionPairs.end(); ++iterator) { + auto& [collision1, jets1, collision2, jets2] = *iterator; + if (std::abs(collision1.posZ()) > eventCuts.vertexZMax.value || std::abs(collision2.posZ()) > eventCuts.vertexZMax.value) { + continue; + } + float const weight = collision1.weight() / static_cast(iterator.currentWindowNeighbours()); + registry.fill(HIST("mc/particle/normalizationMixedEvent"), 1.f, weight); + if (output.fillPionControl.value) { + fillControlParticleNormalization(true, 1.f, weight); + } + if (output.fillKaonControl.value) { + fillControlParticleNormalization(true, 1.f, weight); + } + for (auto const& jet1 : jets1) { + if (!isSelectedJet(jet1)) { + continue; + } + for (auto const& jet2 : jets2) { + if (!isSelectedJet(jet2)) { + continue; + } + auto selectedParticles1 = collectParticleCandidates(jet1, false, weight); + auto selectedParticles2 = collectParticleCandidates(jet2, false, weight); + auto const& baryons1 = selectedParticles1.baryons; + auto const& baryons2 = selectedParticles2.baryons; + registry.fill(HIST("mc/particle/normalizationMixedEvent"), 2.f, weight); + for (auto const& baryon1 : baryons1) { + float const localEta1 = baryon1.eta - jet1.eta(); + float const localPhi1 = RecoDecay::constrainAngle(baryon1.phi - jet1.phi(), -o2::constants::math::PI); + for (auto const& baryon2 : baryons2) { + float const localEta2 = baryon2.eta - jet2.eta(); + float const localPhi2 = RecoDecay::constrainAngle(baryon2.phi - jet2.phi(), -o2::constants::math::PI); + float const deltaEta = localEta1 - localEta2; + float const deltaPhi = RecoDecay::constrainAngle(localPhi1 - localPhi2, -o2::constants::math::PI); + float const deltaR = std::hypot(deltaEta, deltaPhi); + PairType const pairType = getPairType(baryon1, baryon2); + float const pairPtLow = std::min(baryon1.pt, baryon2.pt); + float const pairPtHigh = std::max(baryon1.pt, baryon2.pt); + fillParticlePair(true, pairType, 0.5f * (jet1.pt() + jet2.pt()), deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, weight); + registry.fill(HIST("mc/particle/normalizationMixedEvent"), pairType == PairType::SameSign ? 3.f : 4.f, weight); + } + } + if (output.fillPionControl.value) { + fillControlMixedJetPairs(selectedParticles1.pions, selectedParticles2.pions, jet1, jet2, weight); + } + if (output.fillKaonControl.value) { + fillControlMixedJetPairs(selectedParticles1.kaons, selectedParticles2.kaons, jet1, jet2, weight); + } + } + } + } + } + PROCESS_SWITCH(BaryonPairInJets, processMixedEventMCP, "Particle-level MC mixed-event baryon pairs", false); + + void processMatched(aod::JetCollisionsMCD::iterator const& collision, + aod::JetMcCollisions const&, + MatchedChargedMCDJets const& detectorJets, + MatchedChargedMCPJets const& particleJets, + JetTracksWithOriginalAndMC const&, + BaryonTracks const&, + aod::JetParticles const&) + { + if (!isSelectedCollision(collision, false) || !collision.has_mcCollision()) { + return; + } + float const weight = collision.mcCollision().weight(); + auto const particleJetsInCollision = particleJets.sliceBy(particleJetsPerMcCollision, collision.mcCollisionId()); + for (auto const& particleJet : particleJetsInCollision) { + if (isSelectedJet(particleJet)) { + registry.fill(HIST("mc/jet/allParticlePt"), particleJet.pt(), weight); + } + } + for (auto const& detectorJet : detectorJets) { + if (!isSelectedJet(detectorJet)) { + continue; + } + registry.fill(HIST("mc/jet/allDetectorPt"), detectorJet.pt(), weight); + bool hasAcceptedMatch = false; + for (auto const& particleJet : detectorJet.template matchedJetGeo_as()) { + if (!isSelectedJet(particleJet) || particleJet.mcCollisionId() != collision.mcCollisionId()) { + continue; + } + hasAcceptedMatch = true; + registry.fill(HIST("mc/jet/matchedDetectorPt"), detectorJet.pt(), weight); + registry.fill(HIST("mc/jet/matchedParticlePt"), particleJet.pt(), weight); + registry.fill(HIST("mc/jet/responsePt"), particleJet.pt(), detectorJet.pt(), weight); + if (particleJet.pt() > 0.f) { + registry.fill(HIST("mc/jet/relativePtResolution"), particleJet.pt(), (detectorJet.pt() - particleJet.pt()) / particleJet.pt(), weight); + } + float const deltaEta = detectorJet.eta() - particleJet.eta(); + float const deltaPhi = RecoDecay::constrainAngle(detectorJet.phi() - particleJet.phi(), -o2::constants::math::PI); + registry.fill(HIST("mc/jet/matchingDeltaR"), std::hypot(deltaEta, deltaPhi), weight); + + std::unordered_set generatedParticleIds; + std::vector generatedProtons; + for (auto const& particle : particleJet.template tracks_as()) { + if (!particle.isPhysicalPrimary() || std::abs(particle.pdgCode()) != PDG_t::kProton) { + continue; + } + if (particle.pt() < trackCuts.protonPtMin.value || particle.pt() >= trackCuts.protonPtMax.value || std::abs(particle.eta()) > trackCuts.protonEtaMax.value) { + continue; + } + float const particleDeltaPhi = RecoDecay::constrainAngle(particle.phi() - particleJet.phi(), -o2::constants::math::PI); + float const particleJetDistance = std::hypot(particle.eta() - particleJet.eta(), particleDeltaPhi); + int8_t const sign = particle.pdgCode() > 0 ? 1 : -1; + generatedParticleIds.insert(particle.globalIndex()); + generatedProtons.push_back({particle.pt(), particle.eta(), particle.phi(), sign}); + registry.fill(HIST("mc/efficiency/generatedPrimaryProton"), static_cast(sign), particle.pt(), detectorJet.pt(), particleJetDistance, weight); + } + fillEfficiencyPairs(generatedProtons, detectorJet.pt(), weight); + + std::unordered_set trackSelectedParticleIds; + std::unordered_set tofMatchedParticleIds; + std::unordered_set pidSelectedParticleIds; + std::vector trackSelectedProtons; + std::vector pidSelectedProtons; + for (auto const& jetTrack : detectorJet.template tracks_as()) { + if (!jetTrack.has_mcParticle()) { + continue; + } + auto particle = jetTrack.template mcParticle_as(); + auto const particleId = particle.globalIndex(); + if (!generatedParticleIds.contains(particleId)) { + continue; + } + auto track = jetTrack.template track_as(); + if (!isSelectedTrack(track)) { + continue; + } + float const particleDeltaPhi = RecoDecay::constrainAngle(particle.phi() - particleJet.phi(), -o2::constants::math::PI); + float const particleJetDistance = std::hypot(particle.eta() - particleJet.eta(), particleDeltaPhi); + int8_t const sign = particle.pdgCode() > 0 ? 1 : -1; + SelectedParticle const selectedParticle{ + .pt = particle.pt(), + .eta = particle.eta(), + .phi = particle.phi(), + .sign = sign}; + if (trackSelectedParticleIds.insert(particleId).second) { + trackSelectedProtons.push_back(selectedParticle); + registry.fill(HIST("mc/efficiency/trackSelectedPrimaryProton"), static_cast(sign), particle.pt(), detectorJet.pt(), particleJetDistance, weight); + } + if (track.hasTOF() && tofMatchedParticleIds.insert(particleId).second) { + registry.fill(HIST("mc/efficiency/tofMatchedPrimaryProton"), static_cast(sign), particle.pt(), detectorJet.pt(), particleJetDistance, weight); + } + if (isSelectedProton(track) && pidSelectedParticleIds.insert(particleId).second) { + pidSelectedProtons.push_back(selectedParticle); + registry.fill(HIST("mc/efficiency/pidSelectedPrimaryProton"), static_cast(sign), particle.pt(), detectorJet.pt(), particleJetDistance, weight); + } + } + fillEfficiencyPairs(trackSelectedProtons, detectorJet.pt(), weight); + fillEfficiencyPairs(pidSelectedProtons, detectorJet.pt(), weight); + break; + } + if (!hasAcceptedMatch) { + registry.fill(HIST("mc/jet/fakeDetectorPt"), detectorJet.pt(), weight); + } + } + } + PROCESS_SWITCH(BaryonPairInJets, processMatched, "Matched charged-jet response and proton efficiency", false); + + void processInclusiveSameEvent(InclusiveSameEventCollisions::iterator const& collision, BaryonTracks const& tracks) + { + if (!isSelectedInclusiveCollision(collision)) { + return; + } + registry.fill(HIST("inclusive/normalization/sameEvent"), 1.f); + auto baryons = collectInclusiveBaryons(tracks); + registry.fill(HIST("inclusive/normalization/sameEvent"), 2.f, static_cast(baryons.size())); + for (size_t first = 0; first < baryons.size(); ++first) { + for (size_t second = first + 1; second < baryons.size(); ++second) { + fillInclusivePair(false, baryons[first], baryons[second], 1.f); + registry.fill(HIST("inclusive/normalization/sameEvent"), getPairType(baryons[first], baryons[second]) == PairType::SameSign ? 3.f : 4.f); + } + } + } + PROCESS_SWITCH(BaryonPairInJets, processInclusiveSameEvent, "Inclusive same-event identified baryon pairs", true); + + void processInclusiveMixedEvent(InclusiveMixedEventCollisions const& collisions, BaryonTracks const& tracks) + { + for (auto const& collision : collisions) { + if (isSelectedInclusiveCollision(collision)) { + registry.fill(HIST("inclusive/event/multiplicity"), collision.multFT0C()); + } + } + InclusiveMixingBinning mixingBinning{{mixing.vertexZBins, mixing.multiplicityBins}}; + auto tracksTuple = std::make_tuple(tracks); + SameKindPair mixedCollisionPairs{ + mixingBinning, mixing.numberOfMixedEvents.value, -1, collisions, tracksTuple, &inclusiveMixingCache}; + + for (auto iterator = mixedCollisionPairs.begin(); iterator != mixedCollisionPairs.end(); ++iterator) { + auto& [collision1, tracks1, collision2, tracks2] = *iterator; + if (!isSelectedInclusiveCollision(collision1) || !isSelectedInclusiveCollision(collision2)) { + continue; + } + float const weight = 1.f / static_cast(iterator.currentWindowNeighbours()); + registry.fill(HIST("inclusive/normalization/mixedEvent"), 1.f, weight); + auto baryons1 = collectInclusiveBaryons(tracks1); + auto baryons2 = collectInclusiveBaryons(tracks2); + registry.fill(HIST("inclusive/normalization/mixedEvent"), 2.f, weight * static_cast(baryons1.size() + baryons2.size())); + for (auto const& baryon1 : baryons1) { + for (auto const& baryon2 : baryons2) { + fillInclusivePair(true, baryon1, baryon2, weight); + registry.fill(HIST("inclusive/normalization/mixedEvent"), getPairType(baryon1, baryon2) == PairType::SameSign ? 3.f : 4.f, weight); + } + } + } + } + PROCESS_SWITCH(BaryonPairInJets, processInclusiveMixedEvent, "Inclusive mixed-event identified baryon pairs", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& configContext) +{ + return WorkflowSpec{adaptAnalysisTask(configContext)}; +} diff --git a/PWGJE/Tasks/bjetCentMult.cxx b/PWGJE/Tasks/bjetCentMult.cxx index f7d7b2286e2..9188a4bc256 100644 --- a/PWGJE/Tasks/bjetCentMult.cxx +++ b/PWGJE/Tasks/bjetCentMult.cxx @@ -110,8 +110,8 @@ struct BjetCentMultTask { { numberOfJetFlavourSpecies = static_cast(numFlavourSpecies); - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); // Axis AxisSpec axisJetFlavour = {binJetFlavour, "Jet flavour"}; AxisSpec axisJetPt = {binJetPt, "#it{p}_{T, jet}"}; diff --git a/PWGJE/Tasks/bjetTaggingGnn.cxx b/PWGJE/Tasks/bjetTaggingGnn.cxx index 6b3196e04d9..35ace3cc4f4 100644 --- a/PWGJE/Tasks/bjetTaggingGnn.cxx +++ b/PWGJE/Tasks/bjetTaggingGnn.cxx @@ -246,16 +246,16 @@ struct BjetTaggingGnn { void init(InitContext const&) { - jetRadiiValues = (std::vector)jetRadii; + jetRadiiValues = jetRadii; - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); eventSelectionBitsTVX = jetderiveddatautilities::initialiseEventSelectionBits("TVX"); eventSelectionBitsSel8 = jetderiveddatautilities::initialiseEventSelectionBits("sel8"); eventSelectionBitsSelMC = jetderiveddatautilities::initialiseEventSelectionBits("selMC"); eventSelectionBitsSel8Full = jetderiveddatautilities::initialiseEventSelectionBits("sel8Full"); eventSelectionBitsSel8FullGood = jetderiveddatautilities::initialiseEventSelectionBits("sel8Full+IsGoodZvtxFT0vsPV"); - trackSelectionBits = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelectionBits = jetderiveddatautilities::initialiseTrackSelection(trackSelections); if (doprocessDataJetsTrig) { zorroSummary.setObject(zorro.getZorroSummary()); diff --git a/PWGJE/Tasks/bjetTaggingML.cxx b/PWGJE/Tasks/bjetTaggingML.cxx index 002f3b4bf02..ff58cac44e9 100644 --- a/PWGJE/Tasks/bjetTaggingML.cxx +++ b/PWGJE/Tasks/bjetTaggingML.cxx @@ -89,9 +89,9 @@ struct BJetTaggingML { // Seed the random number generator using current time std::srand(static_cast(std::time(nullptr))); - jetRadiiValues = (std::vector)jetRadii; + jetRadiiValues = jetRadii; - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); const AxisSpec axisDb{300, -10., 20., "#it{D}_{b}"}; const AxisSpec axisScore{120, -0.1, 1.1, "Score"}; diff --git a/PWGJE/Tasks/bjetTreeCreator.cxx b/PWGJE/Tasks/bjetTreeCreator.cxx index 7e3c70b70f5..7ab7bc11bc6 100644 --- a/PWGJE/Tasks/bjetTreeCreator.cxx +++ b/PWGJE/Tasks/bjetTreeCreator.cxx @@ -294,11 +294,11 @@ struct BJetTreeCreator { // Seed the random number generator using current time std::srand(static_cast(std::time(nullptr))); - jetRadiiValues = (std::vector)jetRadii; - jetPtBinsReduction = (std::vector)jetPtBins; - jetReductionFactorsPt = (std::vector)jetReductionFactors; + jetRadiiValues = jetRadii; + jetPtBinsReduction = jetPtBins; + jetReductionFactorsPt = jetReductionFactors; - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); registry.add("h_vertexZ", "Vertex Z;#it{Z} (cm)", {HistType::kTH1F, {{40, -20.0, 20.0}}}); diff --git a/PWGJE/Tasks/chargedJetHadron.cxx b/PWGJE/Tasks/chargedJetHadron.cxx index 6984a46a8e0..4423f41b081 100644 --- a/PWGJE/Tasks/chargedJetHadron.cxx +++ b/PWGJE/Tasks/chargedJetHadron.cxx @@ -116,9 +116,9 @@ struct ChargedJetHadron { using BinningTypePP = ColumnBinningPolicy; using BinningType = ColumnBinningPolicy; using BinningTypeMC = ColumnBinningPolicy; - BinningType corrBinningPP{{binsZVtx, binsCentrality}, true}; - BinningType corrBinning{{binsZVtx, binsCentrality}, true}; - BinningTypeMC corrBinningMC{{binsZVtx, binsCentrality}, true}; + BinningType corrBinningPP{{binsZVtx, binsCentrality}}; + BinningType corrBinning{{binsZVtx, binsCentrality}}; + BinningTypeMC corrBinningMC{{binsZVtx, binsCentrality}}; HistogramRegistry registry; // histogram registry std::vector eventSelectionBits; @@ -132,8 +132,8 @@ struct ChargedJetHadron { void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); if (cfgCentEstimator == 0) { eventCuts = (aod::jcollision::centFT0C >= centralityMin && aod::jcollision::centFT0C <= centralityMax); diff --git a/PWGJE/Tasks/dijetFinderQA.cxx b/PWGJE/Tasks/dijetFinderQA.cxx index defb8a6c926..6ddbcf44304 100644 --- a/PWGJE/Tasks/dijetFinderQA.cxx +++ b/PWGJE/Tasks/dijetFinderQA.cxx @@ -96,8 +96,8 @@ struct DijetFinderQATask { void init(o2::framework::InitContext&) { - eventSelection = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelection = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); // Add histogram for event counts auto dijetMassTemp = 0.0; diff --git a/PWGJE/Tasks/embeddingQA.cxx b/PWGJE/Tasks/embeddingQA.cxx new file mode 100644 index 00000000000..96952f42055 --- /dev/null +++ b/PWGJE/Tasks/embeddingQA.cxx @@ -0,0 +1,186 @@ +// Copyright 2019-2020 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. + +// jet tutorial task for hands on tutorial session (09/11/2023) +// +/// \author Nima Zardoshti +// + +#include "PWGJE/Core/JetDerivedDataUtilities.h" +#include "PWGJE/DataModel/Jet.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +#include + +struct EmbeddingQATask { + HistogramRegistry registry{"registry", + {{"h_collision_zdiff", "collision z diff;z_{Data}-z_{MC} (cm);entries", {HistType::kTH1F, {{200, -10., 10.}}}}, + {"h_tracks_all_pt", "all tracks;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_tracks_embedded_pt", "embedded tracks;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_tracks_sub_pt", "subtracted tracks;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_particles_pt", "particles;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_jets_pt", "jets;#it{p}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_jets_sub_pt", "subtracted jets;#it{p}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_jets_mcd_pt", "detector-level jets;#it{p}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_jets_mcp_pt", "particle-level jets;#it{p}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_jet_detector_response", "detector response matrix;#it{p}_{T,jet} particle (GeV/#it{c});#it{p}_{T,jet} detector (GeV/#it{c})", {HistType::kTH2F, {{200, 0., 200.}, {200, 0., 200.}}}}, + {"h_jet_response", "response matrix;#it{p}_{T,jet} particle (GeV/#it{c});#it{p}_{T,jet} embedded subtracted (GeV/#it{c})", {HistType::kTH2F, {{200, 0., 200.}, {200, 0., 200.}}}}, + {"h_jet_background_response", "background response matrix;#it{p}_{T,jet} detector (GeV/#it{c});#it{p}_{T,jet} embedded (GeV/#it{c})", {HistType::kTH2F, {{200, 0., 200.}, {200, 0., 200.}}}}, + {"h_jet_backgroundfluctuations_response", "background fluctuations response matrix;#it{p}_{T,jet} embedded subtracted (GeV/#it{c});#it{p}_{T,jet} embedded (GeV/#it{c})", {HistType::kTH2F, {{200, 0., 200.}, {200, 0., 200.}}}}, + {"h_tracks_data_pt", "tracks derived data;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_tracks_mcd_pt", "tracks derived detector level;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_tracks_mcp_pt", "particles derived particle level;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_tracks_data_original_pt", "tracks original data;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_tracks_mcd_original_pt", "tracks original detector level;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_tracks_mcp_original_pt", "particles original particle level;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}}}; + + Configurable eventSelections{"eventSelections", "sel8", "choose event selection"}; + Configurable trackSelections{"trackSelections", "globalTracks", "set track selections"}; + + std::vector eventSelection; + int trackSelection = -1; + + void init(o2::framework::InitContext&) + { + eventSelection = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); + } + + Preslice perBCCollisions = aod::collision::bcId; + + void processTracksOriginal(aod::Tracks const& tracks, aod::TracksFrom> const& tracksMCD, aod::McParticlesFrom> const& tracksMCP, aod::JetTracks const& tracksEmbedded, aod::JetParticles const& particles, aod::Collisions const& collisions, soa::Join>, aod::McCollisionLabelsFrom>> const& mcdCollisions, aod::McCollisionsFrom> const&, aod::BCs const& bcs, aod::BCsFrom> const&) + { + std::vector mcdCollisionGoodForEmbedding(mcdCollisions.size(), false); + for (auto const& mcdCollision : mcdCollisions) { + auto const& mcdCollisionBC = mcdCollision.bc_as>>(); + auto const& mcdGlobalBC = mcdCollisionBC.globalBC(); + for (auto const& bc : bcs) { + if (bc.globalBC() == mcdGlobalBC) { + auto const& collisionsPerBC = collisions.sliceBy(perBCCollisions, bc.globalIndex()); + auto const& mcCollision = mcdCollision.mcCollision_as>>(); + if (collisionsPerBC.size() > 0) { + mcdCollisionGoodForEmbedding[mcdCollision.globalIndex()] = true; + registry.fill(HIST("h_collision_zdiff"), collisionsPerBC.iteratorAt(0).posZ() - mcCollision.posZ()); + break; + } + } + } + } + + for (auto const& track : tracks) { + if (track.collisionId() >= 0) { + registry.fill(HIST("h_tracks_data_original_pt"), track.pt()); + } + } + for (auto const& track : tracksMCD) { + if (track.collisionId() >= 0) { + if (mcdCollisionGoodForEmbedding[track.collisionId()]) { + registry.fill(HIST("h_tracks_mcd_original_pt"), track.pt()); + } + } + } + for (auto const& track : tracksMCP) { + registry.fill(HIST("h_tracks_mcp_original_pt"), track.pt()); + } + for (auto const& track : tracksEmbedded) { + if (!(track.trackSel() & (1ULL << jetderiveddatautilities::JTrackSel::embeddedTrack))) { + registry.fill(HIST("h_tracks_data_pt"), track.pt()); + } + } + for (auto const& track : tracksEmbedded) { + if ((track.trackSel() & (1ULL << jetderiveddatautilities::JTrackSel::embeddedTrack))) { + registry.fill(HIST("h_tracks_mcd_pt"), track.pt()); + } + } + for (auto const& particle : particles) { + registry.fill(HIST("h_tracks_mcp_pt"), particle.pt()); + } + } + PROCESS_SWITCH(EmbeddingQATask, processTracksOriginal, "track QA", true); + + void processTracks(aod::JetCollision const&, aod::JetTracks const& tracks, aod::JetTracksSub const& subTracks) + { + for (auto const& track : tracks) { + if (jetderiveddatautilities::selectTrack(track, trackSelection)) { + registry.fill(HIST("h_tracks_all_pt"), track.pt()); + } + if (jetderiveddatautilities::selectTrack(track, trackSelection, true)) { + registry.fill(HIST("h_tracks_embedded_pt"), track.pt()); + } + } + for (auto const& track : subTracks) { + if (jetderiveddatautilities::selectTrack(track, trackSelection)) { + registry.fill(HIST("h_tracks_sub_pt"), track.pt()); + } + } + } + PROCESS_SWITCH(EmbeddingQATask, processTracks, "track QA", true); + + void processParticles(aod::JetMcCollision const&, aod::JetParticles const& particles) + { + for (auto const& particle : particles) { + registry.fill(HIST("h_particles_pt"), particle.pt()); + } + } + PROCESS_SWITCH(EmbeddingQATask, processParticles, "track QA", true); + + void processJets(aod::ChargedJets const& jets, aod::ChargedEventWiseSubtractedJets const& subJets, aod::ChargedMCDetectorLevelJets const& mcdJets, aod::ChargedMCParticleLevelJets const& mcpJets) + { + for (auto const& jet : jets) { + registry.fill(HIST("h_jets_pt"), jet.pt()); + } + for (auto const& jet : subJets) { + registry.fill(HIST("h_jets_sub_pt"), jet.pt()); + } + for (auto const& jet : mcdJets) { + registry.fill(HIST("h_jets_mcd_pt"), jet.pt()); + } + for (auto const& jet : mcpJets) { + registry.fill(HIST("h_jets_mcp_pt"), jet.pt()); + } + } + PROCESS_SWITCH(EmbeddingQATask, processJets, "jet QA", true); + + void processResponse(aod::ChargedJets const&, soa::Join const&, soa::Join const&, soa::Join const& mcpJets) + { + + for (auto const& mcpJet : mcpJets) { + for (auto const& mcdJet : mcpJet.matchedJetGeo_as>()) { + registry.fill(HIST("h_jet_detector_response"), mcpJet.pt(), mcdJet.pt()); + for (auto const& subJet : mcdJet.matchedJetGeo_as>()) { + registry.fill(HIST("h_jet_response"), mcpJet.pt(), subJet.pt()); + for (auto const& jet : subJet.matchedJetGeo_as()) { + registry.fill(HIST("h_jet_background_response"), mcdJet.pt(), jet.pt()); + registry.fill(HIST("h_jet_backgroundfluctuations_response"), subJet.pt(), jet.pt()); + } + } + } + } + } + PROCESS_SWITCH(EmbeddingQATask, processResponse, "jet response", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{adaptAnalysisTask(cfgc, TaskName{"embedding-qa"})}; } diff --git a/PWGJE/Tasks/emcTmMonitor.cxx b/PWGJE/Tasks/emcTmMonitor.cxx index 3d6baefda3b..c1af9d514da 100644 --- a/PWGJE/Tasks/emcTmMonitor.cxx +++ b/PWGJE/Tasks/emcTmMonitor.cxx @@ -19,56 +19,163 @@ /// - cluster energy over track momentum /// - difference in eta /// - difference in phi -/// Simple event selection using the flag doEventSel is provided, which selects INT7 events if set to 1 -/// For pilot beam data, instead of relying on the event selection, one can veto specific BC IDS using the flag -/// fDoVetoBCID. +/// The event selection (eventCuts) applies the standard cuts of the event selection task: sel8, z vertex, the evsel +/// flags (pile-up, ITS/TF frame borders, vertex quality, ITS layers), occupancy, RCT flags and a centrality or +/// multiplicity range with a configurable estimator. Optionally EMCal in the readout (Run 2: kINT7, Run 3: kTVXinEMC). +/// For pilot beam data, specific BC IDs can be vetoed (vetoBCID) or selected (selectBCID). +/// The MC process (processMCTruth) follows the leading MC particle of each cluster through the matching chain: +/// is its track reconstructed, extrapolated to the EMCal, a matching candidate (global track or V0 leg), +/// and matched to the cluster as primary or secondary track. +#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" // for the V0 legs used in the secondary matching #include "PWGJE/DataModel/EMCALClusters.h" +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" #include "Common/CCDB/TriggerAliases.h" +#include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" +#include +#include #include #include #include #include +#include #include +#include #include #include #include #include #include +#include #include #include -#include +#include +#include +#include +#include +#include +#include + +#include +#include #include +#include +#include #include #include #include +#include +#include +#include +#include #include using namespace o2::framework; using namespace o2::framework::expressions; -using CollisionEvSelIt = o2::soa::Join::iterator; +using CollisionEvSelIt = o2::soa::Join::iterator; using BcEvSelIt = o2::soa::Join::iterator; using SelectedClusters = o2::soa::Filtered; using TracksPID = o2::soa::Join; +using SelectedMcClusters = o2::soa::Filtered>; +using TracksMC = o2::soa::Join; + +/// \brief Truth class of the leading MC particle of a cluster +enum class TmTruthClass : uint8_t { + kConversionElectron = 0, // e+- with a photon as mother, produced in the transport (photon conversion) + kPrimaryElectron, // e+- produced by the generator (Dalitz, heavy flavour, ...) + kOtherElectron, // any other e+- (delta rays, Compton, weak decays in the transport, ...) + kOtherCharged, // charged particle that is not an e+- (hadrons, muons) + kPhoton, + kNeutral, // neutral particle that is not a photon + kOther, // no MC particle or unknown PDG code + kNClasses +}; + +/// \brief What happened to the track of the leading MC particle of a cluster in the track matching +enum class TmTruthStatus : uint8_t { + kNoRecoTrack = 0, // no reconstructed track of this particle in this collision + kNotExtrapolated, // track(s) exist, but none was extrapolated to the EMCal (trackEta/PhiEmcal = -999) + kNoMatchingCandidate, // extrapolated, but neither a global track nor a V0 leg: input to no matching + kCandidateNotMatched, // matching candidate, but not matched to this cluster (dR > maxMatchingDistance or not among the closest tracks) + kMatchedPrimary, // matched to this cluster as primary (global) track + kMatchedSecondary, // matched to this cluster as secondary track (V0 leg) + kNStatus +}; + +/// \brief Type of the reconstructed track +enum class TmTrackType : uint8_t { + kNoTrack = 0, + kGlobal, // passes isGlobalTrack() + kItsTpc, // ITS and TPC, but not a global track + kTpcOnly, + kItsOnly, + kOtherType, + kNTypes +}; + +/// \brief Steps of the event selection, bins of the cut flow histogram (a disabled cut passes every event) +enum class EventCut : uint8_t { + kAll = 0, + kBC, + kSel8, + kEmcalReadout, + kZVertex, + kNoTimeFrameBorder, + kNoITSROFrameBorder, + kNoSameBunchPileup, + kGoodZvtxFT0vsPV, + kVertexITSTPC, + kVertexTOFmatched, + kNoCollInTimeRangeStandard, + kNoCollInTimeRangeNarrow, + kNoCollInRofStandard, + kNoHighMultCollInPrevRof, + kGoodITSLayer3, + kGoodITSLayer0123, + kGoodITSLayersAll, + kTrackOccupancy, + kFT0COccupancy, + kRCT, + kCentMult, + kSelected, + kNCuts +}; + +/// \brief Estimator of the centrality / multiplicity cut +enum class CentMultEstimator : int { + kOff = 0, + kCentFT0C, + kCentFT0M, + kCentFT0A, + kMultFT0C, + kMultFT0A, + kMultFT0M, + kMultNTracksPV, + kNEstimators +}; + struct EmcTmMonitor { HistogramRegistry mHistManager{"TrackMatchingMonitorHistograms", {}, OutputObjHandlingPolicy::AnalysisObject}; o2::emcal::Geometry* mGeometry = nullptr; + Service mPdgDatabase{}; Preslice perCluster = o2::aod::emcalclustercell::emcalclusterId; Preslice perClusterAmb = o2::aod::emcalclustercell::emcalambiguousclusterId; Preslice perClusterMatchedTracks = o2::aod::emcalclustercell::emcalclusterId; + Preslice perClusterMatchedSecondaries = o2::aod::emcalclustercell::emcalclusterId; // configurable parameters - // TODO adapt doEventSel switch to also allow selection of other triggers (e.g. EMC7) - Configurable doEventSel{"doEventSel", 0, "demand kINT7"}; + Configurable doEventSel{"doEventSel", false, "deprecated, use eventCuts.requireEmcalReadout: demand kINT7 (Run 2) / kTVXinEMC (Run 3)"}; Configurable vetoBCID{"vetoBCID", "", "BC ID(s) to be excluded, this should be used as an alternative to the event selection"}; Configurable selectBCID{"selectBCID", "all", "BC ID(s) to be included, this should be used as an alternative to the event selection"}; - Configurable vertexCut{"vertexCut", -1, "apply z-vertex cut with value in cm"}; + Configurable vertexCut{"vertexCut", -1, "deprecated, use eventCuts.zVertexMax: if > 0, overrides it"}; Configurable clusterDefinition{"clusterDefinition", 10, "cluster definition to be selected, e.g. 10=kV3Default"}; ConfigurableAxis clusterTimeBinning{"clusterTimeBinning", {1500, -600, 900}, ""}; Configurable usePionRejection{"usePionRejection", false, "demand pion rection for electron signal with TPC PID"}; @@ -85,9 +192,42 @@ struct EmcTmMonitor { Configurable minDPhi{"minDPhi", 0.01, "Minimum dPhi between track and cluster"}; Configurable> eOverPRange{"eOverPRange", {0.9, 1.2}, "E/p range where one would search for electrons (first <= E/p <= second)"}; Configurable hasTRD{"hasTRD", false, "demand track to have a hit in TRD."}; + Configurable emcalRadius{"emcalRadius", 440.f, "MC truth: radius (cm) used to move the cluster eta from the vertex to the origin frame of the track extrapolation"}; + + struct : ConfigurableGroup { + std::string prefix = "eventCuts"; + Configurable requireSel8{"requireSel8", true, "require sel8"}; + Configurable requireEmcalReadout{"requireEmcalReadout", false, "require EMCal in the readout: kINT7 (Run 2) / kTVXinEMC (Run 3)"}; + Configurable zVertexMax{"zVertexMax", 10.f, "maximum |z| of the primary vertex (cm), <= 0: off"}; + Configurable requireNoTimeFrameBorder{"requireNoTimeFrameBorder", false, "require kNoTimeFrameBorder"}; + Configurable requireNoITSROFrameBorder{"requireNoITSROFrameBorder", false, "require kNoITSROFrameBorder"}; + Configurable requireNoSameBunchPileup{"requireNoSameBunchPileup", false, "require kNoSameBunchPileup"}; + Configurable requireGoodZvtxFT0vsPV{"requireGoodZvtxFT0vsPV", false, "require kIsGoodZvtxFT0vsPV"}; + Configurable requireVertexITSTPC{"requireVertexITSTPC", false, "require kIsVertexITSTPC"}; + Configurable requireVertexTOFmatched{"requireVertexTOFmatched", false, "require kIsVertexTOFmatched"}; + Configurable requireNoCollInTimeRangeStandard{"requireNoCollInTimeRangeStandard", false, "require kNoCollInTimeRangeStandard"}; + Configurable requireNoCollInTimeRangeNarrow{"requireNoCollInTimeRangeNarrow", false, "require kNoCollInTimeRangeNarrow"}; + Configurable requireNoCollInRofStandard{"requireNoCollInRofStandard", false, "require kNoCollInRofStandard"}; + Configurable requireNoHighMultCollInPrevRof{"requireNoHighMultCollInPrevRof", false, "require kNoHighMultCollInPrevRof"}; + Configurable requireGoodITSLayer3{"requireGoodITSLayer3", false, "require kIsGoodITSLayer3"}; + Configurable requireGoodITSLayer0123{"requireGoodITSLayer0123", false, "require kIsGoodITSLayer0123"}; + Configurable requireGoodITSLayersAll{"requireGoodITSLayersAll", false, "require kIsGoodITSLayersAll"}; + Configurable trackOccupancyMin{"trackOccupancyMin", -1.f, "minimum track occupancy (trackOccupancyInTimeRange), < 0: off"}; + Configurable trackOccupancyMax{"trackOccupancyMax", -1.f, "maximum track occupancy (trackOccupancyInTimeRange), < 0: off"}; + Configurable ft0cOccupancyMin{"ft0cOccupancyMin", -1.f, "minimum FT0C occupancy (ft0cOccupancyInTimeRange), < 0: off"}; + Configurable ft0cOccupancyMax{"ft0cOccupancyMax", -1.f, "maximum FT0C occupancy (ft0cOccupancyInTimeRange), < 0: off"}; + Configurable applyRCT{"applyRCT", false, "apply the RCT flags of rctLabel"}; + Configurable rctLabel{"rctLabel", "CBT_calo", "RCT selection label: CBT, CBT_hadronPID, CBT_electronPID, CBT_calo, CBT_muon, CBT_muon_glo"}; + Configurable rctCheckZDC{"rctCheckZDC", false, "RCT: also require good ZDC quality (Pb-Pb)"}; + Configurable rctTreatLimitedAcceptanceAsBad{"rctTreatLimitedAcceptanceAsBad", false, "RCT: reject runs flagged LimitedAcceptanceMCReproducible"}; + Configurable centMultEstimator{"centMultEstimator", 0, "centrality / multiplicity cut: 0 = off, 1 = cent FT0C, 2 = cent FT0M, 3 = cent FT0A, 4 = mult FT0C, 5 = mult FT0A, 6 = mult FT0M, 7 = mult NTracksPV"}; + Configurable centMultMin{"centMultMin", 0.f, "minimum centrality (%) / multiplicity"}; + Configurable centMultMax{"centMultMax", 100.f, "maximum centrality (%) / multiplicity"}; + } eventCuts; std::vector mVetoBCIDs; std::vector mSelectBCIDs; + o2::aod::rctsel::RCTFlagsChecker mRctChecker; /// \brief Create output histograms and initialize geometry void init(InitContext const&) @@ -122,6 +262,37 @@ struct EmcTmMonitor { mHistManager.add("eventsSelected", "Number of events", HistType::kTH1F, {{1, 0.5, 1.5}}); mHistManager.add("eventVertexZAll", "z-vertex of event (all events)", HistType::kTH1F, {{200, -20, 20}}); mHistManager.add("eventVertexZSelected", "z-vertex of event (selected events)", HistType::kTH1F, {{200, -20, 20}}); + auto hCutFlow = mHistManager.add("Event/hCutFlow", "events after each step of the event selection (a disabled cut passes every event);;events", HistType::kTH1D, {{static_cast(EventCut::kNCuts), -0.5, static_cast(EventCut::kNCuts) - 0.5}}); + static constexpr std::array(EventCut::kNCuts)> CutLabels = {"all", "BC ID", "sel8", "EMCal readout", "z vertex", "NoTimeFrameBorder", "NoITSROFrameBorder", "NoSameBunchPileup", "GoodZvtxFT0vsPV", "VertexITSTPC", "VertexTOFmatched", "NoCollInTimeRangeStandard", "NoCollInTimeRangeNarrow", "NoCollInRofStandard", "NoHighMultCollInPrevRof", "GoodITSLayer3", "GoodITSLayer0123", "GoodITSLayersAll", "track occupancy", "FT0C occupancy", "RCT", "cent/mult", "selected"}; + for (std::size_t i = 0; i < CutLabels.size(); ++i) { + hCutFlow->GetXaxis()->SetBinLabel(static_cast(i) + 1, CutLabels[i].data()); + } + const AxisSpec trackOccupancyAxis{200, 0., 20000., "track occupancy"}; + const AxisSpec ft0cOccupancyAxis{200, 0., 200000., "FT0C occupancy"}; + const bool isCentrality = eventCuts.centMultEstimator.value <= static_cast(CentMultEstimator::kCentFT0A); + const AxisSpec centMultAxis{isCentrality ? AxisSpec{110, -5., 105., "centrality (%)"} : AxisSpec{500, 0., 50000., "multiplicity"}}; + mHistManager.add("Event/hTrackOccupancyBefore", "track occupancy (before the event selection);;events", HistType::kTH1D, {trackOccupancyAxis}); + mHistManager.add("Event/hTrackOccupancyAfter", "track occupancy (after the event selection);;events", HistType::kTH1D, {trackOccupancyAxis}); + mHistManager.add("Event/hFT0COccupancyBefore", "FT0C occupancy (before the event selection);;events", HistType::kTH1D, {ft0cOccupancyAxis}); + mHistManager.add("Event/hFT0COccupancyAfter", "FT0C occupancy (after the event selection);;events", HistType::kTH1D, {ft0cOccupancyAxis}); + if (eventCuts.centMultEstimator.value != static_cast(CentMultEstimator::kOff)) { + mHistManager.add("Event/hCentMultBefore", "centrality / multiplicity of the cut (before the event selection);;events", HistType::kTH1D, {centMultAxis}); + mHistManager.add("Event/hCentMultAfter", "centrality / multiplicity of the cut (after the event selection);;events", HistType::kTH1D, {centMultAxis}); + } + mHistManager.add("Event/hBCId", "BC ID of the selected events;BC ID;events", HistType::kTH1D, {{o2::constants::lhc::LHCMaxBunches, -0.5, o2::constants::lhc::LHCMaxBunches - 0.5}}); + + if (eventCuts.centMultEstimator.value < static_cast(CentMultEstimator::kOff) || eventCuts.centMultEstimator.value >= static_cast(CentMultEstimator::kNEstimators)) { + LOG(fatal) << "eventCuts.centMultEstimator " << eventCuts.centMultEstimator.value << " unknown, see the configurable description"; + } + if (eventCuts.applyRCT.value) { + mRctChecker.init(eventCuts.rctLabel.value, eventCuts.rctCheckZDC.value, eventCuts.rctTreatLimitedAcceptanceAsBad.value); + } + if (doEventSel.value) { + LOG(warning) << "doEventSel is deprecated, use eventCuts.requireEmcalReadout. Requiring EMCal in the readout."; + } + if (vertexCut.value > 0.) { + LOG(warning) << "vertexCut is deprecated, use eventCuts.zVertexMax. Using |z| < " << vertexCut.value << " cm instead of eventCuts.zVertexMax."; + } // cluster properties (matched clusters) mHistManager.add("TrackEtaPhi", "#eta vs #varphi of all selected tracks", HistType::kTH2F, {etaAxis, phiAxis}); // eta vs phi of all selected tracks @@ -182,10 +353,67 @@ struct EmcTmMonitor { mHistManager.add("clusterTM_EoverP_hadron_ASide", "cluster E/hadron p in A-Side", HistType::kTH3F, {eoverpAxis, trackpAxis, nmatchedtrack}); // E over p vs track pT for the Nth closest hadron track in A-Side mHistManager.add("clusterTM_EoverP_hadron_CSide", "cluster E/hadron p in C-Side", HistType::kTH3F, {eoverpAxis, trackpAxis, nmatchedtrack}); // E over p vs track pT for the Nth closest hadron track in C-Side - if (vetoBCID->length()) { + // MC truth of the track matching, only booked if processMCTruth is enabled + if (doprocessMCTruth.value) { + static constexpr std::array(TmTruthClass::kNClasses)> ClassLabels = {"conversion e^{#pm}", "primary e^{#pm}", "other e^{#pm}", "charged (non e)", "#gamma", "neutral (non #gamma)", "other"}; + static constexpr std::array(TmTruthStatus::kNStatus)> StatusLabels = {"no reco track", "not extrapolated", "no matching candidate", "candidate not matched", "matched primary", "matched secondary"}; + static constexpr std::array(TmTrackType::kNTypes)> TypeLabels = {"no track", "global", "ITS-TPC non-global", "TPC only", "ITS only", "other"}; + const AxisSpec classAxis{static_cast(TmTruthClass::kNClasses), -0.5, static_cast(TmTruthClass::kNClasses) - 0.5, "leading MC particle"}; + const AxisSpec statusAxis{static_cast(TmTruthStatus::kNStatus), -0.5, static_cast(TmTruthStatus::kNStatus) - 0.5, "track matching status"}; + const AxisSpec typeAxis{static_cast(TmTrackType::kNTypes), -0.5, static_cast(TmTrackType::kNTypes) - 0.5, "track type"}; + const AxisSpec v0LegAxis{2, -0.5, 1.5, "V0 leg"}; + const AxisSpec mcPtAxis{std::vector{0., 0.25, 0.5, 0.75, 1., 1.5, 2., 3., 4., 6., 10., 20., 50.}, "#it{p}_{T,MC} (GeV/#it{c})"}; + // residuals: fine binning around 0, coarse binning up to MaxResidual, so the tails beyond the matching windows are visible + constexpr double MaxResidual = 0.4; // range of the residual axes + constexpr double FineRange = 0.04; // fine binning within +-FineRange + constexpr double FineWidth = 0.002; // bin width within +-FineRange + constexpr double CoarseWidth = 0.01; // bin width outside +-FineRange + const int nCoarse = static_cast(std::lround((MaxResidual - FineRange) / CoarseWidth)); + const int nFine = static_cast(std::lround((FineRange - (-FineRange)) / FineWidth)); + std::vector residualBins; + residualBins.reserve(nCoarse + nFine + nCoarse + 1); // negative coarse, fine, positive coarse edges incl. the upper end + for (int i = 0; i < nCoarse; ++i) { + residualBins.push_back(-MaxResidual + CoarseWidth * i); + } + for (int i = 0; i < nFine; ++i) { + residualBins.push_back(-FineRange + FineWidth * i); + } + for (int i = 0; i <= nCoarse; ++i) { + residualBins.push_back(FineRange + CoarseWidth * i); + } + const AxisSpec mcDEtaAxis{residualBins, "#Delta#eta (track - cluster)"}; + const AxisSpec mcDPhiAxis{residualBins, "#Delta#varphi (track - cluster) (rad)"}; + const AxisSpec radiusAxis{125, 0., 250., "#it{R}_{xy} of the conversion (cm)"}; + + auto setLabels = [](TAxis* axis, auto const& labels) { + for (std::size_t i = 0; i < labels.size(); ++i) { + axis->SetBinLabel(static_cast(i) + 1, labels[i].data()); + } + }; + // histograms with labelled axes are TH2 or THn: the TH3 label merge of ROOT 6.36 is broken + auto hLeadingClass = mHistManager.add("MCTruth/hLeadingClass", "leading MC particle of the cluster", HistType::kTH2F, {classAxis, energyAxis}); + setLabels(hLeadingClass->GetXaxis(), ClassLabels); + auto hStatus = mHistManager.add("MCTruth/hStatus", "track matching status of the leading MC particle", HistType::kTHnF, {statusAxis, classAxis, typeAxis, v0LegAxis, mcPtAxis}); + setLabels(hStatus->GetAxis(0), StatusLabels); + setLabels(hStatus->GetAxis(1), ClassLabels); + setLabels(hStatus->GetAxis(2), TypeLabels); + for (const auto& name : {"MCTruth/hResidualEta", "MCTruth/hResidualPhi"}) { + const bool isEta = std::string_view(name).ends_with("Eta"); + auto hRes = mHistManager.add(name, "residual of the extrapolated track of the leading MC particle (origin frame)", HistType::kTHnF, {isEta ? mcDEtaAxis : mcDPhiAxis, mcPtAxis, classAxis, typeAxis}); + setLabels(hRes->GetAxis(2), ClassLabels); + setLabels(hRes->GetAxis(3), TypeLabels); + } + mHistManager.add("MCTruth/hResidualConversion", "conversion e^{#pm}: residual of the extrapolated track", HistType::kTH3F, {mcDEtaAxis, mcDPhiAxis, mcPtAxis}); + mHistManager.add("MCTruth/hResidualVsStored", "matched tracks: own minus stored residual;#Delta#eta_{own} - #Delta#eta_{stored};#Delta#varphi_{own} - #Delta#varphi_{stored}", HistType::kTH2F, {{200, -0.02, 0.02}, {200, -0.02, 0.02}}); + auto hConvRadius = mHistManager.add("MCTruth/hConvRadius", "conversion e^{#pm}: matching status vs conversion radius", HistType::kTHnF, {statusAxis, radiusAxis, mcPtAxis}); + setLabels(hConvRadius->GetAxis(0), StatusLabels); + mHistManager.add("MCTruth/hEoverPConversion", "conversion e^{#pm}: #it{E}_{cls}/#it{p}_{track}", HistType::kTH2F, {eoverpAxis, trackptAxis}); + } + + if (!vetoBCID->empty()) { std::stringstream parser(vetoBCID.value); std::string token; - int bcid; + int bcid = -1; while (std::getline(parser, token, ',')) { bcid = std::stoi(token); LOG(info) << "Veto BCID " << bcid; @@ -195,7 +423,7 @@ struct EmcTmMonitor { if (selectBCID.value != "all") { std::stringstream parser(selectBCID.value); std::string token; - int bcid; + int bcid = -1; while (std::getline(parser, token, ',')) { bcid = std::stoi(token); LOG(info) << "Select BCID " << bcid; @@ -209,45 +437,169 @@ struct EmcTmMonitor { // an integer instead Filter clusterDefinitionSelection = (o2::aod::emcalcluster::definition == clusterDefinition) && (o2::aod::emcalcluster::time >= minTime) && (o2::aod::emcalcluster::time <= maxTime) && (o2::aod::emcalcluster::m02 > minM02) && (o2::aod::emcalcluster::m02 < maxM02); - /// \brief Process EMCAL clusters that are matched to a collisions - void processCollisions(CollisionEvSelIt const& theCollision, SelectedClusters const& clusters, o2::aod::EMCALClusterCells const&, o2::aod::Calos const&, o2::aod::EMCALMatchedTracks const& matchedtracks, TracksPID const& alltracks) + /// \brief Centrality / multiplicity of the collision for the estimator of eventCuts.centMultEstimator + static float getCentMult(CollisionEvSelIt const& collision, const CentMultEstimator estimator) { - mHistManager.fill(HIST("eventsAll"), 1); + switch (estimator) { + case CentMultEstimator::kCentFT0C: + return collision.centFT0C(); + case CentMultEstimator::kCentFT0M: + return collision.centFT0M(); + case CentMultEstimator::kCentFT0A: + return collision.centFT0A(); + case CentMultEstimator::kMultFT0C: + return collision.multFT0C(); + case CentMultEstimator::kMultFT0A: + return collision.multFT0A(); + case CentMultEstimator::kMultFT0M: + return collision.multFT0M(); + case CentMultEstimator::kMultNTracksPV: + return collision.multNTracksPV(); + default: + return 0.f; + } + } - // do event selection if doEventSel is specified - // currently the event selection is hard coded to kINT7 - // but other selections are possible that are defined in TriggerAliases.h - bool isSelected = true; - const int beginningRun3 = 300000; - float lowP = 0.75f; - float midP = 1.25f; - if (doEventSel) { - if (theCollision.bc().runNumber() < beginningRun3) { - if (!theCollision.alias_bit(kINT7)) { - isSelected = false; - } - } else { - if (!theCollision.alias_bit(kTVXinEMC)) { - isSelected = false; - } + /// \brief Whether a value lies in [min, max], a negative limit is off + static bool isInRange(const float value, const float min, const float max) + { + return (min < 0.f || value >= min) && (max < 0.f || value <= max); + } + + /// \brief BC ID veto / selection of vetoBCID and selectBCID + bool isBCSelected(const int bcId) const + { + if (std::find(mVetoBCIDs.begin(), mVetoBCIDs.end(), bcId) != mVetoBCIDs.end()) { + return false; + } + return mSelectBCIDs.empty() || std::find(mSelectBCIDs.begin(), mSelectBCIDs.end(), bcId) != mSelectBCIDs.end(); + } + + /// \brief Event selection of the monitor, see eventCuts. The cuts are applied in the order of EventCut. + /// \param theCollision collision to check + /// \param fillHistos fill the event histograms (only once if several process functions run) + bool isEventSelected(CollisionEvSelIt const& theCollision, const bool fillHistos) + { + // fill the cut flow bin of a passed step + auto passed = [&](const EventCut cut) { + if (fillHistos) { + mHistManager.fill(HIST("Event/hCutFlow"), static_cast(cut)); + } + }; + const auto estimator = static_cast(eventCuts.centMultEstimator.value); + const bool applyCentMult = estimator != CentMultEstimator::kOff; + if (fillHistos) { + mHistManager.fill(HIST("eventsAll"), 1); + mHistManager.fill(HIST("eventVertexZAll"), theCollision.posZ()); + mHistManager.fill(HIST("Event/hTrackOccupancyBefore"), theCollision.trackOccupancyInTimeRange()); + mHistManager.fill(HIST("Event/hFT0COccupancyBefore"), theCollision.ft0cOccupancyInTimeRange()); + if (applyCentMult) { + mHistManager.fill(HIST("Event/hCentMultBefore"), getCentMult(theCollision, estimator)); } } - if (!isSelected) { - LOG(debug) << "Event not selected because it is not kINT7 or does not have EMCAL in readout, skipping"; - return; + passed(EventCut::kAll); + + // BC ID veto / selection (pilot beams); needs aod::BCs in the process function, otherwise the bc() index is not bound + const auto bc = theCollision.bc(); + const int bcId = static_cast(bc.globalBC() % o2::constants::lhc::LHCMaxBunches); + if (!isBCSelected(bcId)) { + return false; + } + passed(EventCut::kBC); + + if (eventCuts.requireSel8.value && !theCollision.sel8()) { + return false; + } + passed(EventCut::kSel8); + + // EMCal in the readout: kINT7 in Run 2, kTVXinEMC in Run 3 + if (eventCuts.requireEmcalReadout.value || doEventSel.value) { + constexpr int BeginningRun3 = 300000; + const bool hasEmcal = bc.runNumber() < BeginningRun3 ? theCollision.alias_bit(kINT7) : theCollision.alias_bit(kTVXinEMC); + if (!hasEmcal) { + return false; + } + } + passed(EventCut::kEmcalReadout); + + const double zVertexMax = vertexCut.value > 0. ? vertexCut.value : static_cast(eventCuts.zVertexMax.value); + if (zVertexMax > 0. && std::abs(theCollision.posZ()) > zVertexMax) { + return false; + } + passed(EventCut::kZVertex); + + // evsel flags, in the order of EventCut + const std::array, static_cast(EventCut::kGoodITSLayersAll) - static_cast(EventCut::kNoTimeFrameBorder) + 1> flags = {{ + {eventCuts.requireNoTimeFrameBorder.value, o2::aod::evsel::kNoTimeFrameBorder}, + {eventCuts.requireNoITSROFrameBorder.value, o2::aod::evsel::kNoITSROFrameBorder}, + {eventCuts.requireNoSameBunchPileup.value, o2::aod::evsel::kNoSameBunchPileup}, + {eventCuts.requireGoodZvtxFT0vsPV.value, o2::aod::evsel::kIsGoodZvtxFT0vsPV}, + {eventCuts.requireVertexITSTPC.value, o2::aod::evsel::kIsVertexITSTPC}, + {eventCuts.requireVertexTOFmatched.value, o2::aod::evsel::kIsVertexTOFmatched}, + {eventCuts.requireNoCollInTimeRangeStandard.value, o2::aod::evsel::kNoCollInTimeRangeStandard}, + {eventCuts.requireNoCollInTimeRangeNarrow.value, o2::aod::evsel::kNoCollInTimeRangeNarrow}, + {eventCuts.requireNoCollInRofStandard.value, o2::aod::evsel::kNoCollInRofStandard}, + {eventCuts.requireNoHighMultCollInPrevRof.value, o2::aod::evsel::kNoHighMultCollInPrevRof}, + {eventCuts.requireGoodITSLayer3.value, o2::aod::evsel::kIsGoodITSLayer3}, + {eventCuts.requireGoodITSLayer0123.value, o2::aod::evsel::kIsGoodITSLayer0123}, + {eventCuts.requireGoodITSLayersAll.value, o2::aod::evsel::kIsGoodITSLayersAll}, + }}; + for (std::size_t i = 0; i < flags.size(); ++i) { + if (flags[i].first && !theCollision.selection_bit(flags[i].second)) { + return false; + } + passed(static_cast(static_cast(EventCut::kNoTimeFrameBorder) + static_cast(i))); + } + + // occupancy, track and FT0C based, each with its own range + if (!isInRange(static_cast(theCollision.trackOccupancyInTimeRange()), eventCuts.trackOccupancyMin.value, eventCuts.trackOccupancyMax.value)) { + return false; + } + passed(EventCut::kTrackOccupancy); + if (!isInRange(theCollision.ft0cOccupancyInTimeRange(), eventCuts.ft0cOccupancyMin.value, eventCuts.ft0cOccupancyMax.value)) { + return false; + } + passed(EventCut::kFT0COccupancy); + + if (eventCuts.applyRCT.value && !mRctChecker.checkTable(theCollision)) { + return false; + } + passed(EventCut::kRCT); + + if (applyCentMult) { + const float centMult = getCentMult(theCollision, estimator); + if (centMult < eventCuts.centMultMin.value || centMult > eventCuts.centMultMax.value) { + return false; + } } - mHistManager.fill(HIST("eventVertexZAll"), theCollision.posZ()); - if (vertexCut > 0 && std::abs(theCollision.posZ()) > vertexCut) { - LOG(debug) << "Event not selected because of z-vertex cut z= " << theCollision.posZ() << " > " << vertexCut << " cm, skipping"; + passed(EventCut::kCentMult); + + passed(EventCut::kSelected); + if (fillHistos) { + mHistManager.fill(HIST("eventsSelected"), 1); + mHistManager.fill(HIST("eventVertexZSelected"), theCollision.posZ()); + mHistManager.fill(HIST("Event/hTrackOccupancyAfter"), theCollision.trackOccupancyInTimeRange()); + mHistManager.fill(HIST("Event/hFT0COccupancyAfter"), theCollision.ft0cOccupancyInTimeRange()); + if (applyCentMult) { + mHistManager.fill(HIST("Event/hCentMultAfter"), getCentMult(theCollision, estimator)); + } + mHistManager.fill(HIST("Event/hBCId"), bcId); + } + return true; + } + + /// \brief Process EMCAL clusters that are matched to a collisions + void processCollisions(CollisionEvSelIt const& theCollision, o2::aod::BCs const&, SelectedClusters const& clusters, o2::aod::EMCALClusterCells const&, o2::aod::Calos const&, o2::aod::EMCALMatchedTracks const& matchedtracks, TracksPID const& alltracks) + { + if (!isEventSelected(theCollision, true)) { return; } - mHistManager.fill(HIST("eventsSelected"), 1); - mHistManager.fill(HIST("eventVertexZSelected"), theCollision.posZ()); + float lowP = 0.75f; + float midP = 1.25f; for (const auto& alltrack : alltracks) { - double trackEta, trackPhi; - trackEta = alltrack.trackEtaEmcal(); - trackPhi = alltrack.trackPhiEmcal(); + double trackEta = alltrack.trackEtaEmcal(); + double trackPhi = alltrack.trackPhiEmcal(); if (alltrack.isGlobalTrack()) { // NSigma of all global tracks without matching mHistManager.fill(HIST("TrackTM_NSigma"), alltrack.tpcNSigmaEl(), alltrack.pt()); mHistManager.fill(HIST("TrackEtaPhi"), trackEta, trackPhi); @@ -284,11 +636,11 @@ struct EmcTmMonitor { // match.track_as() with // using globTracks = o2::soa::Join; // In this example the counter t is just used to only look at the closest match - float dEta, dPhi, pT, abs_p, trackEta, trackPhi, nSigmaEl; - int supermoduleID; + float dEta{0}, dPhi{0}, pT{0}, absP{0}, trackEta{0}, trackPhi{0}, nSigmaEl{0}; + int supermoduleID = -1; try { supermoduleID = mGeometry->SuperModuleNumberFromEtaPhi(cluster.eta(), cluster.phi()); - } catch (o2::emcal::InvalidPositionException& e) { + } catch (o2::emcal::InvalidPositionException const& e) { // Imprecision of the position at the sector boundaries, mostly due to // vertex imprecision. Skip these clusters for the now. continue; @@ -304,7 +656,7 @@ struct EmcTmMonitor { nSigmaEl = match.track_as().tpcNSigmaEl(); // exmple of how to access any property of the matched tracks (tracks are sorted by how close they are to cluster) LOG(debug) << "Pt of match" << match.track_as().pt(); - abs_p = std::abs(match.track_as().p()); + absP = std::abs(match.track_as().p()); // only consider closest match trackEta = match.track_as().trackEtaEmcal(); trackPhi = match.track_as().trackPhiEmcal(); @@ -330,14 +682,14 @@ struct EmcTmMonitor { mHistManager.fill(HIST("clusterTM_dEtadPhi"), dEta, dPhi, supermoduleID); mHistManager.fill(HIST("clusterTM_dEtaPt"), dEta, pT, supermoduleID); } - double eOverP = cluster.energy() / abs_p; + double eOverP = cluster.energy() / absP; mHistManager.fill(HIST("clusterTM_dEtaTN"), dEta, t); mHistManager.fill(HIST("clusterTM_dPhiTN"), dPhi, t); mHistManager.fill(HIST("clusterTM_dRTN"), std::sqrt(dPhi * dPhi + dEta * dEta), t); mHistManager.fill(HIST("clusterTM_EoverP_E"), eOverP, cluster.energy(), t); mHistManager.fill(HIST("clusterTM_dEtadPhi"), dEta, dPhi, t); mHistManager.fill(HIST("clusterEMatched"), cluster.energy(), t); - mHistManager.fill(HIST("clusterTM_EvsP"), cluster.energy(), abs_p, t); + mHistManager.fill(HIST("clusterTM_EvsP"), cluster.energy(), absP, t); mHistManager.fill(HIST("clusterTM_EoverP_Pt"), eOverP, match.track_as().pt(), t); mHistManager.fill(HIST("clusterTM_NSigma"), nSigmaEl, match.track_as().pt(), t); mHistManager.fill(HIST("MatchedTrackEtaPhi"), trackEta, trackPhi); @@ -357,9 +709,9 @@ struct EmcTmMonitor { if (t == 0) { mHistManager.fill(HIST("clusterTM_PosdPhiPt"), dPhi, pT, supermoduleID); mHistManager.fill(HIST("clusterTM_PosdEtadPhi"), dEta, dPhi, supermoduleID); - if (abs_p < lowP) { + if (absP < lowP) { mHistManager.fill(HIST("clusterTM_PosdEtadPhi_Pl0_75"), dEta, dPhi, supermoduleID); - } else if (abs_p >= midP) { + } else if (absP >= midP) { mHistManager.fill(HIST("clusterTM_PosdEtadPhi_Pgeq1_25"), dEta, dPhi, supermoduleID); } else { mHistManager.fill(HIST("clusterTM_PosdEtadPhi_0_75leqPl1_25"), dEta, dPhi, supermoduleID); @@ -371,9 +723,9 @@ struct EmcTmMonitor { if (t == 0) { mHistManager.fill(HIST("clusterTM_NegdPhiPt"), dPhi, pT, supermoduleID); mHistManager.fill(HIST("clusterTM_NegdEtadPhi"), dEta, dPhi, supermoduleID); - if (abs_p < lowP) { + if (absP < lowP) { mHistManager.fill(HIST("clusterTM_NegdEtadPhi_Pl0_75"), dEta, dPhi, supermoduleID); - } else if (abs_p >= midP) { + } else if (absP >= midP) { mHistManager.fill(HIST("clusterTM_NegdEtadPhi_Pgeq1_25"), dEta, dPhi, supermoduleID); } else { mHistManager.fill(HIST("clusterTM_NegdEtadPhi_0_75leqPl1_25"), dEta, dPhi, supermoduleID); @@ -446,6 +798,191 @@ struct EmcTmMonitor { } PROCESS_SWITCH(EmcTmMonitor, processCollisions, "Process clusters from collision", true); + /// \brief Information of a reconstructed track needed for the MC truth of the matching + struct McTrackInfo { + int64_t id{-1}; + bool isGlobal{false}; + bool hasITS{false}; + bool hasTPC{false}; + float etaEmcal{-999.f}; + float phiEmcal{-999.f}; + float p{0.f}; + float pt{0.f}; + }; + + static constexpr float NotOnEmcal = -900.f; // trackEta/PhiEmcal of tracks that were not extrapolated to the EMCal are -999 + + /// \brief Truth class of an MC particle + template + TmTruthClass truthClassOf(TMcParticle const& mc, TMcParticles const& mcParticles) + { + const int pdg = mc.pdgCode(); + if (std::abs(pdg) == PDG_t::kElectron) { + if (mc.producedByGenerator()) { + return TmTruthClass::kPrimaryElectron; + } + if (mc.getProcess() == TMCProcess::kPPair && mc.has_mothers() && mcParticles.iteratorAt(mc.mothersIds()[0]).pdgCode() == PDG_t::kGamma) { + return TmTruthClass::kConversionElectron; + } + return TmTruthClass::kOtherElectron; + } + if (pdg == PDG_t::kGamma) { + return TmTruthClass::kPhoton; + } + const TParticlePDG* info = mPdgDatabase->GetParticle(pdg); + if (info == nullptr) { + return TmTruthClass::kOther; + } + return info->Charge() != 0. ? TmTruthClass::kOtherCharged : TmTruthClass::kNeutral; + } + + /// \brief Type of a reconstructed track + static TmTrackType trackTypeOf(McTrackInfo const& info) + { + if (info.isGlobal) { + return TmTrackType::kGlobal; + } + if (info.hasITS && info.hasTPC) { + return TmTrackType::kItsTpc; + } + if (info.hasTPC) { + return TmTrackType::kTpcOnly; + } + return info.hasITS ? TmTrackType::kItsOnly : TmTrackType::kOtherType; + } + + /// \brief MC truth of the track matching: follow the leading MC particle of each cluster through the matching chain. + /// Needs emcal-correction-task with processMCWithSecondaries and the photon conversion builder (aod::V0Legs). + void processMCTruth(CollisionEvSelIt const& theCollision, o2::aod::BCs const&, SelectedMcClusters const& clusters, o2::aod::EMCALMatchedTracks const& matchedTracks, o2::aod::EMCMatchSecs const& matchedSecondaries, TracksMC const& tracks, o2::aod::V0Legs const& v0Legs, o2::aod::McParticles const& mcParticles) + { + if (!isEventSelected(theCollision, !doprocessCollisions.value)) { + return; + } + + // reconstructed tracks of each MC particle in this collision, and the tracks used as V0 legs (secondary matching) + std::unordered_map> tracksOfMcParticle; + for (const auto& track : tracks) { + if (!track.has_mcParticle()) { + continue; + } + tracksOfMcParticle[track.mcParticleId()].push_back({track.globalIndex(), track.isGlobalTrack(), track.hasITS(), track.hasTPC(), track.trackEtaEmcal(), track.trackPhiEmcal(), track.p(), track.pt()}); + } + std::unordered_set v0LegTracks; + for (const auto& leg : v0Legs) { + v0LegTracks.insert(leg.trackId()); + } + + for (const auto& cluster : clusters) { + try { + mGeometry->SuperModuleNumberFromEtaPhi(cluster.eta(), cluster.phi()); + } catch (o2::emcal::InvalidPositionException const& e) { + continue; // same as in processCollisions + } + const float clusterPt = cluster.energy() / std::cosh(cluster.eta()); + if (m02highPt > 0. && cluster.m02() >= maxM02HighPt && clusterPt >= m02highPt) { + continue; + } + + // leading contributor: the labels are ordered by energy fraction (ClusterLabel::orderLabels in the ClusterFactory) + const auto mcIds = cluster.mcParticleIds(); + const int64_t leadId = mcIds.empty() ? -1 : mcIds[0]; + TmTruthClass truthClass = TmTruthClass::kOther; + float mcPt = 0.f; + float convRadius = -1.f; + if (leadId >= 0) { + const auto mc = mcParticles.iteratorAt(leadId); + mcPt = mc.pt(); + truthClass = truthClassOf(mc, mcParticles); + convRadius = std::hypot(mc.vx(), mc.vy()); + } + mHistManager.fill(HIST("MCTruth/hLeadingClass"), static_cast(truthClass), cluster.energy()); + const bool isConversion = truthClass == TmTruthClass::kConversionElectron; + if (truthClass != TmTruthClass::kConversionElectron && truthClass != TmTruthClass::kPrimaryElectron && truthClass != TmTruthClass::kOtherElectron && truthClass != TmTruthClass::kOtherCharged) { + continue; // neutral leading particle: no track to match + } + + // best reconstructed track of the leading particle: extrapolated and matching candidate first, then highest pT + const McTrackInfo* best = nullptr; + int bestScore = -1; + if (auto it = tracksOfMcParticle.find(leadId); it != tracksOfMcParticle.end()) { + for (const auto& info : it->second) { + const bool isExtrapolated = info.etaEmcal > NotOnEmcal && info.phiEmcal > NotOnEmcal; + const bool isCandidate = info.isGlobal || v0LegTracks.contains(info.id); + const int score = 2 * (isExtrapolated ? 1 : 0) + (isCandidate ? 1 : 0); + if (score > bestScore || (score == bestScore && info.pt > best->pt)) { + best = &info; + bestScore = score; + } + } + } + + TmTruthStatus status = TmTruthStatus::kNoRecoTrack; + TmTrackType type = TmTrackType::kNoTrack; + bool isV0Leg = false; + if (best != nullptr) { + type = trackTypeOf(*best); + isV0Leg = v0LegTracks.contains(best->id); + const bool isExtrapolated = best->etaEmcal > NotOnEmcal && best->phiEmcal > NotOnEmcal; + + // is the track among the primary or secondary matches of this cluster + bool isMatchedPrimary = false, isMatchedSecondary = false; + float storedDEta = 0.f, storedDPhi = 0.f; + for (const auto& match : matchedTracks.sliceBy(perClusterMatchedTracks, cluster.globalIndex())) { + if (match.trackId() == best->id) { + isMatchedPrimary = true; + storedDEta = match.deltaEta(); + storedDPhi = match.deltaPhi(); + break; + } + } + if (!isMatchedPrimary) { + for (const auto& match : matchedSecondaries.sliceBy(perClusterMatchedSecondaries, cluster.globalIndex())) { + if (match.trackId() == best->id) { + isMatchedSecondary = true; + storedDEta = match.deltaEta(); + storedDPhi = match.deltaPhi(); + break; + } + } + } + if (isMatchedPrimary) { + status = TmTruthStatus::kMatchedPrimary; + } else if (isMatchedSecondary) { + status = TmTruthStatus::kMatchedSecondary; + } else if (!isExtrapolated) { + status = TmTruthStatus::kNotExtrapolated; + } else if (!best->isGlobal && !isV0Leg) { + status = TmTruthStatus::kNoMatchingCandidate; + } else { + status = TmTruthStatus::kCandidateNotMatched; + } + + if (isExtrapolated) { + // the matching compares to the cluster position seen from the origin, the cluster table has eta seen from the vertex + const float clusterEtaOrigin = std::asinh(std::sinh(cluster.eta()) + theCollision.posZ() / emcalRadius); + const float dEta = best->etaEmcal - clusterEtaOrigin; + const float dPhi = std::remainder(best->phiEmcal - cluster.phi(), o2::constants::math::TwoPI); + mHistManager.fill(HIST("MCTruth/hResidualEta"), dEta, mcPt, static_cast(truthClass), static_cast(type)); + mHistManager.fill(HIST("MCTruth/hResidualPhi"), dPhi, mcPt, static_cast(truthClass), static_cast(type)); + if (isConversion) { + mHistManager.fill(HIST("MCTruth/hResidualConversion"), dEta, dPhi, mcPt); + } + if (isMatchedPrimary || isMatchedSecondary) { + mHistManager.fill(HIST("MCTruth/hResidualVsStored"), dEta - storedDEta, dPhi - storedDPhi); + } + } + if (isConversion && best->p > 0.f) { + mHistManager.fill(HIST("MCTruth/hEoverPConversion"), cluster.energy() / best->p, best->pt); + } + } + mHistManager.fill(HIST("MCTruth/hStatus"), static_cast(status), static_cast(truthClass), static_cast(type), static_cast(isV0Leg), mcPt); + if (isConversion) { + mHistManager.fill(HIST("MCTruth/hConvRadius"), static_cast(status), convRadius, mcPt); + } + } + } + PROCESS_SWITCH(EmcTmMonitor, processMCTruth, "MC truth of the track matching for clusters from charged particles", false); + /// \brief Create binning for cluster energy axis (variable bin size) /// \return vector with bin limits std::vector makeEnergyBinning() const @@ -493,7 +1030,7 @@ struct EmcTmMonitor { result.emplace_back(50. + 2.0 * (i - 190)); } else if (i < 235) { // o2-linter: disable=magic-number (just numbers for binning) result.emplace_back(100. + 5.0 * (i - 215)); - } else if (i < 245) { // o2-linter: disable=magic-number (just numbers for binning) + } else { result.emplace_back(200. + 10.0 * (i - 235)); } } @@ -529,9 +1066,9 @@ struct EmcTmMonitor { } }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { WorkflowSpec workflow{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(context)}; return workflow; } diff --git a/PWGJE/Tasks/fullJetSpectra.cxx b/PWGJE/Tasks/fullJetSpectra.cxx index 7c9fe31090a..5774d8d8fe0 100644 --- a/PWGJE/Tasks/fullJetSpectra.cxx +++ b/PWGJE/Tasks/fullJetSpectra.cxx @@ -355,11 +355,11 @@ struct FullJetSpectra { void init(o2::framework::InitContext&) { - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); - particleSelection = static_cast(particleSelections); - jetRadiiValues = (std::vector)jetRadii; + particleSelection = particleSelections; + jetRadiiValues = jetRadii; doSumw2 = doMBGapTrigger; /* if (doMcClosure) { // randGen.SetSeed(mcSplitSeed); @@ -391,7 +391,7 @@ struct FullJetSpectra { for (std::size_t iJetRadius = 0; iJetRadius < jetRadiiValues.size(); iJetRadius++) { filledJetR.push_back(0.0); } - auto jetRadiiBins = (std::vector)jetRadii; + auto jetRadiiBins = jetRadii.value; if (jetRadiiBins.size() > 1) { jetRadiiBins.push_back(jetRadiiBins[jetRadiiBins.size() - 1] + (std::abs(jetRadiiBins[jetRadiiBins.size() - 1] - jetRadiiBins[jetRadiiBins.size() - 2]))); } else { @@ -783,7 +783,7 @@ struct FullJetSpectra { Filter eventCuts = (nabs(aod::jcollision::posZ) < vertexZCut && aod::jcollision::centFT0M >= centralityMin && aod::jcollision::centFT0M < centralityMax); // Filter EMCeventCuts = (nabs(aod::collision::posZ) < vertexZCut && aod::collision::centrality >= centralityMin && aod::collision::centrality < centralityMax); Filter trackCuts = (aod::jtrack::pt >= trackpTMin && aod::jtrack::pt < trackpTMax && aod::jtrack::eta > trackEtaMin && aod::jtrack::eta < trackEtaMax && aod::jtrack::phi >= trackPhiMin && aod::jtrack::phi <= trackPhiMax); - aod::EMCALClusterDefinition clusterDefinition = aod::emcalcluster::getClusterDefinitionFromString(clusterDefinitionS.value); + aod::EMCALClusterDefinition clusterDefinition = aod::emcalcluster::getClusterDefinitionFromString(clusterDefinitionS); Filter clusterFilter = (aod::jcluster::definition == static_cast(clusterDefinition) && aod::jcluster::eta > clusterEtaMin && aod::jcluster::eta < clusterEtaMax && aod::jcluster::phi >= clusterPhiMin && aod::jcluster::phi <= clusterPhiMax && aod::jcluster::energy >= clusterEnergyMin && aod::jcluster::time > clusterTimeMin && aod::jcluster::time < clusterTimeMax && (clusterRejectExotics && aod::jcluster::isExotic != true)); Preslice JetMCPPerMcCollision = aod::jet::mcCollisionId; PresliceUnsorted> CollisionsPerMCPCollision = aod::jmccollisionlb::mcCollisionId; @@ -1381,16 +1381,16 @@ struct FullJetSpectra { if (jetderiveddatautilities::selectTrigger(collision, jetderiveddatautilities::JTrigSel::JetChLowPt)) { registry.fill(HIST("hDetTrigcollisionCounter"), 13.5); // EMCAcceptedDetTrigCollWithLowChargedJetTriggers - eventAccepted = true; + // eventAccepted = true; // FIXME: unused } if (jetderiveddatautilities::selectTrigger(collision, jetderiveddatautilities::JTrigSel::JetChHighPt)) { registry.fill(HIST("hDetTrigcollisionCounter"), 14.5); // EMCAcceptedDetTrigCollWithHighChargedJetTriggers - eventAccepted = true; + // eventAccepted = true; // FIXME: unused } if (jetderiveddatautilities::selectTrigger(collision, jetderiveddatautilities::JTrigSel::JetFullLowPt) && jetderiveddatautilities::selectTrigger(collision, jetderiveddatautilities::JTrigSel::JetFullHighPt)) { registry.fill(HIST("hDetTrigcollisionCounter"), 15.5); // EMCAcceptedDetTrigCollWithLow+HighFullJetTriggers - eventAccepted = true; + // eventAccepted = true; // FIXME: unused } for (auto const& jet : jets) { diff --git a/PWGJE/Tasks/gammaJetTreeProducer.cxx b/PWGJE/Tasks/gammaJetTreeProducer.cxx index 8096c260dc3..1f8751bcbfa 100644 --- a/PWGJE/Tasks/gammaJetTreeProducer.cxx +++ b/PWGJE/Tasks/gammaJetTreeProducer.cxx @@ -159,9 +159,9 @@ struct GammaJetTreeProducer { using O2HistType = HistType; using O2Axis = AxisSpec; - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); // for substructure calculation jetReclusterer.isReclustering = true; @@ -410,7 +410,7 @@ struct GammaJetTreeProducer { return false; } mHistograms.fill(HIST("eventQA"), 1); - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, true, true, rctLabel)) { return false; } mHistograms.fill(HIST("eventQA"), 2); @@ -809,9 +809,10 @@ struct GammaJetTreeProducer { // return recursive list of all daughter IDs /// \brief Gets all daughter particle IDs in the decay chain /// \param particle The particle to start from + /// \param mcParticles The MC particles collection /// \return Vector of daughter particle IDs - template - void getDaughtersInChain(const T& particle, std::vector& daughters, int depth = 0) + template + void getDaughtersInChain(const T& particle, U const& mcParticles, std::vector& daughters, int depth = 0) { // Limit recursion depth to avoid infinite loops if (depth > MaxRecursionDepth) { // 100 generations should be more than enough @@ -822,10 +823,12 @@ struct GammaJetTreeProducer { return; } - const auto& daughterParticles = particle.template daughters_as(); - for (const auto& daughter : daughterParticles) { - daughters.push_back(daughter.globalIndex()); - getDaughtersInChain(daughter, daughters, depth + 1); + // Daughter indices are a contiguous inclusive range. Resolve them on the full table + // so the IDs match cluster MC labels. + const auto daughterIds = particle.daughtersIds(); + for (int daughterId = daughterIds[0]; daughterId <= daughterIds[1]; ++daughterId) { + daughters.push_back(daughterId); + getDaughtersInChain(mcParticles.iteratorAt(daughterId), mcParticles, daughters, depth + 1); } } /// \brief Finds the first physical primary particle in the decay chain (upwards) @@ -885,34 +888,37 @@ struct GammaJetTreeProducer { if (motherIndex != -1) { const auto& mother = mcParticles.iteratorAt(motherIndex); - // get daughters of pi0 mother - auto daughtersMother = mother.template daughters_as(); - // check if there are two daughters that are both photons - if (daughtersMother.size() == 2) { // o2-linter: disable=magic-number (it is just counting number of daughters) - const auto& daughter1 = daughtersMother.iteratorAt(0); - const auto& daughter2 = daughtersMother.iteratorAt(1); - if (daughter1.pdgCode() == PDG_t::kGamma && daughter2.pdgCode() == PDG_t::kGamma) { - // get the full stack of particles that these daughters create - std::vector fullDecayChain1; - std::vector fullDecayChain2; - getDaughtersInChain(daughter1, fullDecayChain1); - getDaughtersInChain(daughter2, fullDecayChain2); - bool photon1Found = false; - bool photon2Found = false; - - // check if any of the particles in the fullDecayChain are leading or subleading in the cluster - for (const auto& particleID : fullDecayChain1) { - if (particleID == inducerIDs[0] || particleID == inducerIDs[1]) { - photon1Found = true; + // Daughter indices are the inclusive [first, last] range stored on the mother. + // Resolve them on the full MC table, same index space as the cluster labels. + if (mother.has_daughters()) { + const auto daughterIds = mother.daughtersIds(); + const bool hasTwoDaughters = daughterIds[1] == daughterIds[0] + 1; // o2-linter: disable=magic-number (two-body decay) + if (hasTwoDaughters) { + const auto& daughter1 = mcParticles.iteratorAt(daughterIds[0]); + const auto& daughter2 = mcParticles.iteratorAt(daughterIds[1]); + if (daughter1.pdgCode() == PDG_t::kGamma && daughter2.pdgCode() == PDG_t::kGamma) { + // include the decay photons themselves, since unconverted photons are usually the cluster inducers + std::vector fullDecayChain1{daughterIds[0]}; + std::vector fullDecayChain2{daughterIds[1]}; + getDaughtersInChain(daughter1, mcParticles, fullDecayChain1); + getDaughtersInChain(daughter2, mcParticles, fullDecayChain2); + bool photon1Found = false; + bool photon2Found = false; + + // check if any of the particles in the fullDecayChain are leading or subleading in the cluster + for (const auto& particleID : fullDecayChain1) { + if (particleID == inducerIDs[0] || particleID == inducerIDs[1]) { + photon1Found = true; + } } - } - for (const auto& particleID : fullDecayChain2) { - if (particleID == inducerIDs[0] || particleID == inducerIDs[1]) { - photon2Found = true; + for (const auto& particleID : fullDecayChain2) { + if (particleID == inducerIDs[0] || particleID == inducerIDs[1]) { + photon2Found = true; + } + } + if (photon1Found && photon2Found) { + isMerged = true; } - } - if (photon1Found && photon2Found) { - isMerged = true; } } } @@ -1242,7 +1248,7 @@ struct GammaJetTreeProducer { for (const auto& mcCluster : mcClusters) { mHistograms.fill(HIST("clusterMC_E_All"), mcCluster.energy()); auto [origin, mcIndex] = getClusterOrigin(mcCluster, mcParticles); - float leadingEnergyFraction = mcCluster.amplitudeA()[0] / mcCluster.energy(); + float leadingEnergyFraction = mcCluster.amplitudeA()[0]; // amplitudes are already stored as energy fractions // Fill MC origin QA histograms if (TESTBIT(origin, static_cast(gjanalysis::ClusterOrigin::kPhoton))) { mHistograms.fill(HIST("clusterMC_E_Photon"), mcCluster.energy()); diff --git a/PWGJE/Tasks/hadronPhotonCorrelation.cxx b/PWGJE/Tasks/hadronPhotonCorrelation.cxx index 7040898d6d1..18e73087ff7 100644 --- a/PWGJE/Tasks/hadronPhotonCorrelation.cxx +++ b/PWGJE/Tasks/hadronPhotonCorrelation.cxx @@ -152,8 +152,8 @@ struct HadronPhotonCorrelation { AxisSpec axisEtaAssoc = {etaBinsAssoc, -etaMaxAssoc, etaMaxAssoc, "#eta"}; // Axis for eta distribution AxisSpec axisDeltaEta = {etaBinsTrig + etaBinsAssoc, -(etaMaxTrig + etaMaxAssoc), etaMaxTrig + etaMaxAssoc, "#Delta #eta"}; // Axis for Delta eta in correlations - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); // Generated histograms registry.add("generated/events/hEventStats", "Event statistics", kTH1F, {axisEventStats}); @@ -298,10 +298,10 @@ struct HadronPhotonCorrelation { bool isHardQCDgg2gg(const P& particles) { for (const auto& particle : particles) { - if (std::abs(particle.getGenStatusCode()) == pythiaCodeIncomingHard && (PDG_t)std::abs(particle.pdgCode()) != kGluon) { + if (std::abs(particle.getGenStatusCode()) == pythiaCodeIncomingHard && std::abs(particle.pdgCode()) != kGluon) { return false; } - if (std::abs(particle.getGenStatusCode()) == pythiaCodeOutgoingHard && (PDG_t)std::abs(particle.pdgCode()) != kGluon) { + if (std::abs(particle.getGenStatusCode()) == pythiaCodeOutgoingHard && std::abs(particle.pdgCode()) != kGluon) { return false; } } @@ -396,7 +396,7 @@ struct HadronPhotonCorrelation { return false; } - if ((PDG_t)std::abs(particle->GetPdgCode()) != kGamma) { + if (std::abs(particle->GetPdgCode()) != kGamma) { return false; } @@ -712,7 +712,7 @@ struct HadronPhotonCorrelation { if (!initParticle(track_assoc, false)) { continue; } - if ((PDG_t)std::abs(track_assoc.pdgCode()) != kGamma) { + if (std::abs(track_assoc.pdgCode()) != kGamma) { continue; } if (!track_assoc.isPhysicalPrimary() && track_assoc.getGenStatusCode() < 0) { @@ -722,7 +722,7 @@ struct HadronPhotonCorrelation { // Iterate through mother particles until original mother is reached auto origPhoton = track_assoc; auto mother = track_assoc.mothers_as().at(0); - while ((PDG_t)mother.pdgCode() == kGamma) { + while (mother.pdgCode() == kGamma) { origPhoton = mother; mother = mother.mothers_as().at(0); } @@ -968,7 +968,7 @@ struct HadronPhotonCorrelation { if (!initParticle(track_assoc)) { continue; } - if ((PDG_t)std::abs(track_assoc.pdgCode()) != kPiPlus) { + if (std::abs(track_assoc.pdgCode()) != kPiPlus) { continue; } @@ -1050,7 +1050,7 @@ struct HadronPhotonCorrelation { if (!initTrack(track_assoc)) { continue; } - if ((PDG_t)std::abs(track_assoc.mcParticle().pdgCode()) != kPiPlus) { + if (std::abs(track_assoc.mcParticle().pdgCode()) != kPiPlus) { continue; } @@ -1101,7 +1101,7 @@ struct HadronPhotonCorrelation { if (pdgParticle->Charge() != 0.) { continue; } // remove charged particles - if (std::abs(track_assoc.pdgCode()) < pidCodeHadronCut || (PDG_t)track_assoc.pdgCode() == kNeutron) { + if (std::abs(track_assoc.pdgCode()) < pidCodeHadronCut || track_assoc.pdgCode() == kNeutron) { continue; } // remove non-hadrons and neutrons @@ -1121,7 +1121,7 @@ struct HadronPhotonCorrelation { } for (const auto& daughter : daughters) { - if ((PDG_t)std::abs(daughter.pdgCode()) != kGamma) { + if (std::abs(daughter.pdgCode()) != kGamma) { continue; } if (!daughter.isPhysicalPrimary() && daughter.getGenStatusCode() == -1) { diff --git a/PWGJE/Tasks/hfFragmentationFunction.cxx b/PWGJE/Tasks/hfFragmentationFunction.cxx index 054cae8ea42..25ffa86f906 100644 --- a/PWGJE/Tasks/hfFragmentationFunction.cxx +++ b/PWGJE/Tasks/hfFragmentationFunction.cxx @@ -235,7 +235,7 @@ struct HfFragmentationFunction { void init(InitContext const&) { // initialise event selection: - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); // create histograms // collision counter histograms diff --git a/PWGJE/Tasks/jetBackgroundAnalysis.cxx b/PWGJE/Tasks/jetBackgroundAnalysis.cxx index b58e698bb09..6a5a8aaaae3 100644 --- a/PWGJE/Tasks/jetBackgroundAnalysis.cxx +++ b/PWGJE/Tasks/jetBackgroundAnalysis.cxx @@ -81,8 +81,8 @@ struct JetBackgroundAnalysisTask { void init(o2::framework::InitContext&) { // selection settings initialisation - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); randomNumber.SetSeed(0); // Axes definitions @@ -143,7 +143,7 @@ struct JetBackgroundAnalysisTask { float randomConePt = 0; for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { - float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, (-o2::constants::math::PI)); float dEta = track.eta() - randomConeEta; if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { randomConePt += track.pt(); @@ -156,8 +156,8 @@ struct JetBackgroundAnalysisTask { float randomConePtRandomTrackDirection = 0; for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { - float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, static_cast(-o2::constants::math::PI)); // ignores actual phi of track - float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track + float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, (-o2::constants::math::PI)); // ignores actual phi of track + float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { randomConePtRandomTrackDirection += track.pt(); } @@ -170,7 +170,7 @@ struct JetBackgroundAnalysisTask { const bool hasSub = jets.size() >= 2; float randomConePtWithoutLeadingJet = randomConePt; if (hasLead) { - float dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + float dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, (-o2::constants::math::PI)); float dEtaLeadingJet = jets.iteratorAt(0).eta() - randomConeEta; bool jetWasInCone = false; @@ -178,14 +178,14 @@ struct JetBackgroundAnalysisTask { jetWasInCone = true; randomConeEta = randomNumber.Uniform(trackEtaMin + randomConeR, trackEtaMax - randomConeR); randomConePhi = randomNumber.Uniform(0.0, o2::constants::math::TwoPI); - dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, (-o2::constants::math::PI)); dEtaLeadingJet = jets.iteratorAt(0).eta() - randomConeEta; } if (jetWasInCone) { randomConePtWithoutLeadingJet = 0.0; for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { // if track selection is uniformTrack, dcaXY and dcaZ cuts need to be added as they aren't in the selection so that they can be studied here - float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, (-o2::constants::math::PI)); float dEta = track.eta() - randomConeEta; if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { randomConePtWithoutLeadingJet += track.pt(); @@ -204,8 +204,8 @@ struct JetBackgroundAnalysisTask { randomConePtWithoutTwoLeadJet = 0.0; for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { - float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, static_cast(-o2::constants::math::PI)); // ignores actual phi of track - float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track + float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, (-o2::constants::math::PI)); // ignores actual phi of track + float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { const bool inLead = trackIsInJet(track, jets.iteratorAt(0)); const bool inSub = hasSub && trackIsInJet(track, jets.iteratorAt(1)); @@ -230,7 +230,7 @@ struct JetBackgroundAnalysisTask { float randomConePhi = randomNumber.Uniform(0.0, o2::constants::math::TwoPI); float randomConePt = 0; for (auto const& track : tracks) { - float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, (-o2::constants::math::PI)); float dEta = track.eta() - randomConeEta; if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { randomConePt += track.pt(); @@ -241,8 +241,8 @@ struct JetBackgroundAnalysisTask { // randomised eta,phi for tracks, to assess part of fluctuations coming from statistically independently emitted particles float randomConePtRandomTrackDirection = 0; for (auto const& track : tracks) { - float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, static_cast(-o2::constants::math::PI)); // ignores actual phi of track - float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track + float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, (-o2::constants::math::PI)); // ignores actual phi of track + float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { randomConePtRandomTrackDirection += track.pt(); } @@ -254,7 +254,7 @@ struct JetBackgroundAnalysisTask { const bool hasSub = jets.size() >= 2; float randomConePtWithoutLeadingJet = randomConePt; if (hasLead) { - float dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + float dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, (-o2::constants::math::PI)); float dEtaLeadingJet = jets.iteratorAt(0).eta() - randomConeEta; bool jetWasInCone = false; @@ -262,13 +262,13 @@ struct JetBackgroundAnalysisTask { jetWasInCone = true; randomConeEta = randomNumber.Uniform(trackEtaMin + randomConeR, trackEtaMax - randomConeR); randomConePhi = randomNumber.Uniform(0.0, o2::constants::math::TwoPI); - dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, (-o2::constants::math::PI)); dEtaLeadingJet = jets.iteratorAt(0).eta() - randomConeEta; } if (jetWasInCone) { randomConePtWithoutLeadingJet = 0.0; for (auto const& track : tracks) { - float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, (-o2::constants::math::PI)); float dEta = track.eta() - randomConeEta; if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { randomConePtWithoutLeadingJet += track.pt(); @@ -285,8 +285,8 @@ struct JetBackgroundAnalysisTask { randomConePtWithoutOneLeadJet = 0.0; randomConePtWithoutTwoLeadJet = 0.0; for (auto const& track : tracks) { - float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, static_cast(-o2::constants::math::PI)); // ignores actual phi of track - float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track + float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, (-o2::constants::math::PI)); // ignores actual phi of track + float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { const bool inLead = trackIsInJet(track, jets.iteratorAt(0)); const bool inSub = hasSub && trackIsInJet(track, jets.iteratorAt(1)); diff --git a/PWGJE/Tasks/jetChargedV2.cxx b/PWGJE/Tasks/jetChargedV2.cxx index 05d897db26f..17a637da4f4 100644 --- a/PWGJE/Tasks/jetChargedV2.cxx +++ b/PWGJE/Tasks/jetChargedV2.cxx @@ -15,6 +15,7 @@ #include "PWGJE/Core/JetDerivedDataUtilities.h" #include "PWGJE/Core/JetFindingUtilities.h" +#include "PWGJE/Core/JetUtilities.h" #include "PWGJE/DataModel/Jet.h" #include "PWGJE/DataModel/JetReducedData.h" #include "PWGJE/DataModel/JetSubtraction.h" @@ -36,6 +37,7 @@ #include #include +#include #include #include #include @@ -45,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -71,7 +74,6 @@ struct JetChargedV2 { Configurable centralityMax{"centralityMax", 999.0, "maximum centrality"}; Configurable trackSelections{"trackSelections", "globalTracks", "set track selections"}; - Configurable trackDcaZmax{"trackDcaZmax", 99, "additional cut on dcaZ to PV for tracks; uniformTracks in particular don't cut on this at all"}; Configurable trackOccupancyInTimeRangeMax{"trackOccupancyInTimeRangeMax", 999999, "maximum occupancy of tracks in neighbouring collisions in a given time range; only applied to reconstructed collisions (data and mcd jets), not mc collisions (mcp jets)"}; Configurable trackOccupancyInTimeRangeMin{"trackOccupancyInTimeRangeMin", -999999, "minimum occupancy of tracks in neighbouring collisions in a given time range; only applied to reconstructed collisions (data and mcd jets), not mc collisions (mcp jets)"}; Configurable trackPtMin{"trackPtMin", 0.15, "minimum pT acceptance for tracks"}; @@ -83,34 +85,59 @@ struct JetChargedV2 { Configurable jetAreaFractionMin{"jetAreaFractionMin", -99.0, "used to make a cut on the jet areas"}; Configurable leadingConstituentPtMin{"leadingConstituentPtMin", -99.0, "minimum pT selection on jet constituent"}; Configurable leadingConstituentPtMax{"leadingConstituentPtMax", 9999.0, "maximum pT selection on jet constituent"}; - Configurable jetPtMin{"jetPtMin", 0.15, "minimum pT acceptance for jets"}; Configurable jetPtMax{"jetPtMax", 200.0, "maximum pT acceptance for jets"}; Configurable jetEtaMin{"jetEtaMin", -0.9, "minimum eta acceptance for jets"}; Configurable jetEtaMax{"jetEtaMax", 0.9, "maximum eta acceptance for jets"}; Configurable nBinsEta{"nBinsEta", 200, "number of bins for eta axes"}; Configurable randomConeLeadJetDeltaR{"randomConeLeadJetDeltaR", -99.0, "min distance between leading jet axis and random cone (RC) axis; if negative, min distance is set to automatic value of R_leadJet+R_RC "}; Configurable randomConeR{"randomConeR", 0.4, "size of random Cone for estimating background fluctuations"}; + Configurable nBinsFluct{"nBinsFluct", 1000, "number of bins for fluctuations axes"}; + Configurable deltaPtMinMaxFluct{"deltaPtMinMaxFluct", 200, "maximum and minimum delta pt value for fluctuation axes"}; + Configurable nBinsFluctCentrality{"nBinsFluctCentrality", 1100, "number of centrality bins for the fluctuation axes; reduce it (e.g. 110) to cut the memory of the fluctuation histograms"}; + Configurable nBinsDeltaPhi{"nBinsDeltaPhi", 100, "number of Delta phi bins for the fluctuation axes; 96 keeps the pi/4 in-/out-of-plane edges on bin boundaries"}; + Configurable fillFluctH3{"fillFluctH3", false, "fill the 3D centrality-vs-deltaPt-vs-DeltaPhi fluctuation histograms (memory heavy); the direct 2D in-/out-of-plane histograms are always filled"}; + Configurable fillQvecQA{"fillQvecQA", true, "fill the Q-vector QA histograms (histQvec*V#, 3D Qx-vs-Qy-vs-centrality, memory heavy); the event-plane QA histograms are always filled"}; //=====================< tracking efficiency >=====================// Configurable pTHatExponent{"pTHatExponent", 6.0, "exponent of the event weight for the calculation of pTHat"}; //=====================< evt pln >=====================// - Configurable> cfgnMods{"cfgnMods", {2}, "Modulation of interest"}; - Configurable cfgnTotalSystem{"cfgnTotalSystem", 7, "total qvector number"}; - Configurable cfgDetName{"cfgDetName", "FT0C", "The name of detector to be analyzed"}; - Configurable cfgDetCheckName{"cfgDetCheckName", "FT0M", "The name of detector to be analyzed"}; - Configurable cfgRefAName{"cfgRefAName", "TPCpos", "The name of detector for reference A"}; - Configurable cfgRefBName{"cfgRefBName", "TPCneg", "The name of detector for reference B"}; - Configurable detCheck{"detCheck", 4, "total qvector number"}; - - ConfigurableAxis cfgAxisQvecF{"cfgAxisQvecF", {300, -1, 1}, ""}; - ConfigurableAxis cfgAxisQvec{"cfgAxisQvec", {100, -3, 3}, ""}; - ConfigurableAxis cfgAxisCent{"cfgAxisCent", {90, 0, 90}, ""}; + struct EpQvecSettings : ConfigurableGroup { + Configurable cfgDetName{"cfgDetName", "FT0C", "The name of detector to be analyzed"}; + Configurable cfgDetCheckName{"cfgDetCheckName", "FT0M", "The name of detector to be analyzed"}; + Configurable cfgRefAName{"cfgRefAName", "TPCpos", "The name of detector for reference A"}; + Configurable cfgRefBName{"cfgRefBName", "TPCneg", "The name of detector for reference B"}; + Configurable> cfgnMods{"cfgnMods", {2}, "Modulation of interest"}; + Configurable cfgnTotalSystem{"cfgnTotalSystem", 7, "total qvector number"}; + Configurable detCheck{"detCheck", 4, "total qvector number"}; + ConfigurableAxis cfgAxisQvecF{"cfgAxisQvecF", {300, -1, 1}, ""}; + ConfigurableAxis cfgAxisQvec{"cfgAxisQvec", {100, -3, 3}, ""}; + ConfigurableAxis cfgAxisCent{"cfgAxisCent", {90, 0, 90}, ""}; + ConfigurableAxis cfgAxisEvtfit{"cfgAxisEvtfit", {10000, 0, 10000}, ""}; + } epQvec; + Configurable rhoFitRangeMargin{"rhoFitRangeMargin", 0.5, "extension of the rho modulation fit function range"}; + Configurable checkAbsDeltaPhi{"checkAbsDeltaPhi", false, "use 0-pi event-plane region for in/out-plane"}; ConfigurableAxis cfgAxisVnCent{"cfgAxisVnCent", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 50, 70, 100}, " % "}; - - ConfigurableAxis cfgAxisEvtfit{"cfgAxisEvtfit", {10000, 0, 10000}, ""}; EventPlaneHelper helperEP; + Configurable fillTHnSparse{"fillTHnSparse", false, "switch to fill the THnSparse"}; + // subsample the sparse filling: 1 = every jet/cone, N = only every N-th. At full statistics the + // sparse costs O(entries) * 40 B, so use e.g. 100 to keep the output at the MB level. + Configurable sparseSubsample{"sparseSubsample", 1, "fill every N-th jet/cone into the THnSparse (1 = all)"}; + // binning of the THnSparse histograms: coarse defaults are safe for a full production, the finer + // binning used for local tests can be set from the command line (or from the train configuration) + // all THnSparse axes packed into one configurable to stay within the framework member limit: + // (nbins, min, max) triples, in the order pT (also used for rho*A), centrality, rho, Delta phi + Configurable> sparseAxes{"sparseAxes", {105, 0., 210., 55, 0., 110., 110, 0., 110., 60, 0., 6.2831853}, "sparse axes as (nbins, min, max) triples: pT, centrality, rho, Delta phi"}; + // remaining sparse axes packed into one configurable to stay within the framework member limit: + // (nbins, min, max) triples for delta pT, area and number of tracks + Configurable> sparseExtraAxes{"sparseExtraAxes", {100.f, -200.f, 200.f, 8.f, 0.f, 0.8f, 20.f, 0.f, 100.f}, "extra sparse axes as (nbins, min, max) triples: delta pT, area, N tracks"}; + Configurable doSumw2Sparse{"doSumw2Sparse", true, "Flag whether to set Sumw2() for the sparse histograms"}; + std::shared_ptr hJetQA; //! per-jet QA quantities (pT, eta, phi, area, nTracks, centrality) + std::shared_ptr hRhoMedian; //! per-jet information using the event-averaged (median) rho + std::shared_ptr hRhoLocal; //! per-jet information using the local rho of that jet + std::shared_ptr hFluctQA; //! per-random-cone QA quantities + std::shared_ptr hFluctQAwoLeadingJet; //! per-random-cone QA, leading jet removed from the cone int detId = 0; int refAId = 0; int refBId = 0; @@ -129,6 +156,8 @@ struct JetChargedV2 { Configurable useMedianRho{"useMedianRho", false, "use median rho for subtract MCP Background"}; Configurable useLocalRho{"useLocalRho", false, "use local rho for subtract MCP Background"}; Configurable isMCGenOnly{"isMCGenOnly", false, "analysis is run over mcGen only"}; + Configurable kappa{"kappa", 1.0, "angularity kappa"}; + Configurable alpha{"alpha", 1.0, "angularity alpha"}; enum AcceptSplitCollisionsOptions { NonSplitOnly = 0, @@ -139,9 +168,6 @@ struct JetChargedV2 { template int getDetId(const T& name) { - if (name.value == "BPos" || name.value == "BNeg" || name.value == "BTot") { - LOGF(warning, "Using deprecated label: %s. Please use TPCpos, TPCneg, TPCall instead.", name.value); - } if (name.value == "FT0C") { return 0; } @@ -171,6 +197,7 @@ struct JetChargedV2 { std::vector eventSelectionBits; int trackSelection = -1; + bool doSumw2 = false; double evtnum = 0; float collQvecAmpDetId = 1e-8; TH1F* hPtsumSumptFit = nullptr; @@ -183,12 +210,12 @@ struct JetChargedV2 { void init(o2::framework::InitContext&) { - detId = getDetId(cfgDetName); - detIdCheck = getDetId(cfgDetCheckName); - refAId = getDetId(cfgRefAName); - refBId = getDetId(cfgRefBName); + doSumw2 = skipMBGapEvents; + detId = getDetId(epQvec.cfgDetName); + detIdCheck = getDetId(epQvec.cfgDetCheckName); + refAId = getDetId(epQvec.cfgRefAName); + refBId = getDetId(epQvec.cfgRefBName); if (detId == refAId || detId == refBId || refAId == refBId || detId == detIdCheck) { - LOGF(info, "Wrong detector configuration \n The FT0C will be used to get Q-Vector \n The TPCpos and TPCneg will be used as reference systems"); detId = 0; detIdCheck = 0; refAId = 4; @@ -229,14 +256,18 @@ struct JetChargedV2 { AxisSpec jetPtAxis = {200, 0., 200., "#it{p}_{T} (GeV/#it{c})"}; AxisSpec jetPtAxisRhoAreaSub = {400, -200., 200., "#it{p}_{T} (GeV/#it{c})"}; AxisSpec jetEtaAxis = {nBinsEta, -1.0, 1.0, "#eta"}; + // fluctuation axes, same as jetBackgroundAnalysis's h2_centrality_rhorandomcone* + AxisSpec fluctCentralityAxis = {nBinsFluctCentrality, 0.0, 110.0, "centrality (%)"}; + AxisSpec fluctDeltaPtAxis = {nBinsFluct, -deltaPtMinMaxFluct, deltaPtMinMaxFluct, "#delta #it{p}_{T} (GeV/#it{c})"}; + AxisSpec fluctDeltaPhiAxis = {nBinsDeltaPhi, 0., o2::constants::math::TwoPI, "#Delta#varphi_{jet}"}; AxisSpec axisPt = {40, 0.0, 4.0}; AxisSpec axisEta = {32, -0.8, 0.8}; AxisSpec axixCent = {20, 0, 100}; AxisSpec axisChID = {220, 0, 220}; - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); randomNumber.SetSeed(0); if (doprocessCollisions || doprocessCollisionsWeighted) { @@ -296,27 +327,29 @@ struct JetChargedV2 { registry.add("h_mcd_pt_before_matching_mcdprocess", "jet pT;#it{p}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); //=====================< evt pln plot >=====================// - AxisSpec axisCent{cfgAxisCent, "centrality"}; - AxisSpec axisQvec{cfgAxisQvec, "Q"}; - AxisSpec axisQvecF{cfgAxisQvecF, "Q"}; + AxisSpec axisCent{epQvec.cfgAxisCent, "centrality"}; + AxisSpec axisQvec{epQvec.cfgAxisQvec, "Q"}; + AxisSpec axisQvecF{epQvec.cfgAxisQvecF, "Q"}; AxisSpec axisEvtPl{360, -constants::math::PI, constants::math::PI}; - for (uint i = 0; i < cfgnMods->size(); i++) { - histosQA.add(Form("histQvecUncorV%d", cfgnMods->at(i)), "", {HistType::kTH3F, {axisQvecF, axisQvecF, axisCent}}); - histosQA.add(Form("histQvecRectrV%d", cfgnMods->at(i)), "", {HistType::kTH3F, {axisQvecF, axisQvecF, axisCent}}); - histosQA.add(Form("histQvecTwistV%d", cfgnMods->at(i)), "", {HistType::kTH3F, {axisQvecF, axisQvecF, axisCent}}); - histosQA.add(Form("histQvecFinalV%d", cfgnMods->at(i)), "", {HistType::kTH3F, {axisQvec, axisQvec, axisCent}}); + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { + if (fillQvecQA) { // 3D Qx-vs-Qy-vs-centrality, memory heavy + histosQA.add(Form("histQvecUncorV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH3F, {axisQvecF, axisQvecF, axisCent}}); + histosQA.add(Form("histQvecRectrV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH3F, {axisQvecF, axisQvecF, axisCent}}); + histosQA.add(Form("histQvecTwistV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH3F, {axisQvecF, axisQvecF, axisCent}}); + histosQA.add(Form("histQvecFinalV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH3F, {axisQvec, axisQvec, axisCent}}); + } - histosQA.add(Form("histEvtPlUncorV%d", cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); - histosQA.add(Form("histEvtPlRectrV%d", cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); - histosQA.add(Form("histEvtPlTwistV%d", cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); - histosQA.add(Form("histEvtPlFinalV%d", cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); + histosQA.add(Form("histEvtPlUncorV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); + histosQA.add(Form("histEvtPlRectrV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); + histosQA.add(Form("histEvtPlTwistV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); + histosQA.add(Form("histEvtPlFinalV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); - histosQA.add(Form("h_ep2_FT0CV%d", cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); - histosQA.add(Form("h_ep2_FT0MV%d", cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); + histosQA.add(Form("h_ep2_FT0CV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); + histosQA.add(Form("h_ep2_FT0MV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); - histosQA.add(Form("histEvtPlRes_SigRefAV%d", cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); - histosQA.add(Form("histEvtPlRes_SigRefBV%d", cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); - histosQA.add(Form("histEvtPlRes_RefARefBV%d", cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); + histosQA.add(Form("histEvtPlRes_SigRefAV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); + histosQA.add(Form("histEvtPlRes_SigRefBV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); + histosQA.add(Form("histEvtPlRes_RefARefBV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); } histosQA.add("histCent", "Centrality TrkProcess", HistType::kTH1F, {axisCent}); @@ -342,9 +375,12 @@ struct JetChargedV2 { registry.add("h_evtnum_centrlity", "eventNumber vs centrality ; #eventNumber", {HistType::kTH1F, {{1000, 0.0, 1000}}}); registry.add("h2_phi_rholocal", "#varphi vs #rho(#varphi); #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); - registry.add("h2_phi_rholocal_absDelta", "#varphi vs #rho(#varphi)absDelta, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); registry.add("h2_centrality_pT", "centrality vs p_{T}; p_{T}; centrality ", {HistType::kTH2F, {{210, -10.0, 200.0}, {100, 0., 100}}}); + registry.add("h2_rho_cent_median_in", "#rho vs centrality, in-plane, <#rho>; #centrality (%); <#rho> (GeV/#it{c})", {HistType::kTH2F, {{110, -10., 100.}, {210, -10., 200.}}}); + registry.add("h2_rho_cent_median_out", "#rho vs centrality, out-of-plane, <#rho>; #centrality (%); <#rho> (GeV/#it{c})", {HistType::kTH2F, {{110, -10., 100.}, {210, -10., 200.}}}); + registry.add("h2_rho_cent_local_in", "#rho vs centrality, in-plane, #rho(#varphi); #centrality (%); #rho(#varphi) (GeV/#it{c})", {HistType::kTH2F, {{110, -10., 100.}, {210, -10., 200.}}}); + registry.add("h2_rho_cent_local_out", "#rho vs centrality, out-of-plane, #rho(#varphi); #centrality (%); #rho(#varphi) (GeV/#it{c})", {HistType::kTH2F, {{110, -10., 100.}, {210, -10., 200.}}}); registry.add("h2_rholocal_cent", "#centrality vs #rho(#varphi); #centrality; #rho(#varphi) ", {HistType::kTH2F, {{110, -10., 100}, {210, -10.0, 200.0}}}); registry.add("h2_averagerho_cent", "#centrality vs #rho; #centrality; #rho ", {HistType::kTH2F, {{110, -10., 100}, {210, -10.0, 200.0}}}); @@ -353,24 +389,11 @@ struct JetChargedV2 { registry.add("h2_rholocal_pt_inplane", "#varphi vs #it{p}_{T}; #it{p}_{T}; #rho(#varphi) ", {HistType::kTH2F, {{110, -10., 100}, {210, -10.0, 200.0}}}); registry.add("h2_rholocal_pt_outplane", "#varphi vs #it{p}_{T}; #it{p}_{T}; #rho(#varphi) ", {HistType::kTH2F, {{110, -10., 100}, {210, -10.0, 200.0}}}); - registry.add("h2_phi_averagerho_absDelta", "#varphi vs #rho(0)absDelta, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); registry.add("h2_averagerho_pt", "#varphi vs #it{p}_{T}; #it{p}_{T}; <#rho> ", {HistType::kTH2F, {{110, -10., 100}, {210, -10.0, 200.0}}}); registry.add("h2_averagerho_raw_pt", "#varphi vs #it{p}_{T}; #it{p}_{T} - <#rho>Area; <#rho> ", {HistType::kTH2F, {{110, -10., 100}, {210, -10.0, 200.0}}}); registry.add("h2_averagerho_pt_inplane", "#varphi vs #it{p}_{T}; #it{p}_{T}; <#rho> ", {HistType::kTH2F, {{110, -10., 100}, {210, -10.0, 200.0}}}); registry.add("h2_averagerho_pt_outplane", "#varphi vs #it{p}_{T}; #it{p}_{T}; <#rho> ", {HistType::kTH2F, {{110, -10., 100}, {210, -10.0, 200.0}}}); - registry.add("h2_phi_rholocal_absDelta_low", "#varphi vs #rho(#varphi)absDeltaLow, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); - registry.add("h2_phi_rholocal_absDelta_medium", "#varphi vs #rho(#varphi)absDeltaMediun, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); - registry.add("h2_phi_rholocal_absDelta_high", "#varphi vs #rho(#varphi)absDeltahigh, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); - - registry.add("h2_phi_rholocal_absDelta_low_inplane", "#varphi vs #rho(#varphi)absDeltaLow inplane, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); - registry.add("h2_phi_rholocal_absDelta_medium_inplane", "#varphi vs #rho(#varphi)absDeltaMediun inplane, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); - registry.add("h2_phi_rholocal_absDelta_high_inplane", "#varphi vs #rho(#varphi)absDeltahigh inplane, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); - - registry.add("h2_phi_rholocal_absDelta_low_outplane", "#varphi vs #rho(#varphi)absDeltaLow outplane, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); - registry.add("h2_phi_rholocal_absDelta_medium_outplane", "#varphi vs #rho(#varphi)absDeltaMediun outplane, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); - registry.add("h2_phi_rholocal_absDelta_high_outplane", "#varphi vs #rho(#varphi)absDeltahigh outplane, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); - registry.add("h_good_bad_ugly", "local rho large than 0, less than 0 and total", {HistType::kTH1F, {{5, 0.0, 5.0}}}); registry.get(HIST("h_good_bad_ugly"))->GetXaxis()->SetBinLabel(1, "#rho_{local} > 0"); registry.get(HIST("h_good_bad_ugly"))->GetXaxis()->SetBinLabel(2, "#rho_{local} <= 0"); @@ -387,27 +410,56 @@ struct JetChargedV2 { registry.add("leadJetEta", "leadJet constituent #eta ", {HistType::kTH1F, {{100, -1.0, 1.0}}}); //< RC test plots >// - registry.add("h3_centrality_deltapT_RandomCornPhi_localrhovsphi", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {100, 0., o2::constants::math::TwoPI}}}); - registry.add("h3_centrality_deltapT_RandomCornPhi_localrhovsphi_abs", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {100, 0., o2::constants::math::TwoPI}}}); - registry.add("h3_centrality_deltapT_RandomCornPhi_localrhovsphi_abs_in", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {100, 0., o2::constants::math::TwoPI}}}); - registry.add("h3_centrality_deltapT_RandomCornPhi_localrhovsphi_abs_out", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {100, 0., o2::constants::math::TwoPI}}}); - - registry.add("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {100, 0., o2::constants::math::TwoPI}}}); - registry.add("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}(#varphi); #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {100, 0., o2::constants::math::TwoPI}}}); - registry.add("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_abs", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {100, 0., o2::constants::math::TwoPI}}}); - registry.add("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_abs", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}(#varphi); #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {100, 0., o2::constants::math::TwoPI}}}); - - registry.add("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_abs_in", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {100, 0., o2::constants::math::TwoPI}}}); - registry.add("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_abs_out", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {100, 0., o2::constants::math::TwoPI}}}); - registry.add("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_abs_in", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}(#varphi); #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {100, 0., o2::constants::math::TwoPI}}}); - registry.add("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_abs_out", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}(#varphi); #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {100, 0., o2::constants::math::TwoPI}}}); - - // registry.add("h1_distribution_RC", "RC #phi-#Psi_{2}", {HistType::kTH1F, {{72, 0.0, o2::constants::math::TwoPI}}}); - + if (fillFluctH3) { + registry.add("h3_centrality_deltapT_RandomCornPhi_localrhovsphi", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {fluctCentralityAxis, fluctDeltaPtAxis, fluctDeltaPhiAxis}}); + } + registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_in", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_out", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + if (fillFluctH3) { + registry.add("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {fluctCentralityAxis, fluctDeltaPtAxis, fluctDeltaPhiAxis}}); + } + registry.add("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + registry.add("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_in", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + registry.add("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_out", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + if (fillFluctH3) { + registry.add("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}(#varphi); #Delta#varphi_{jet}", {HistType::kTH3F, {fluctCentralityAxis, fluctDeltaPtAxis, fluctDeltaPhiAxis}}); + } + registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}(#varphi); #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_in", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}(#varphi); #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_out", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}(#varphi); #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + if (checkAbsDeltaPhi) { + registry.add("h2_phi_rholocal_absDelta", "#varphi vs #rho(#varphi)absDelta, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::PI}, {210, -10.0, 200.0}}}); + registry.add("h2_phi_averagerho_absDelta", "#varphi vs #rho(0)absDelta, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::PI}, {210, -10.0, 200.0}}}); + + registry.add("h2_phi_rholocal_absDelta_low", "#varphi vs #rho(#varphi)absDeltaLow, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::PI}, {210, -10.0, 200.0}}}); + registry.add("h2_phi_rholocal_absDelta_medium", "#varphi vs #rho(#varphi)absDeltaMediun, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::PI}, {210, -10.0, 200.0}}}); + registry.add("h2_phi_rholocal_absDelta_high", "#varphi vs #rho(#varphi)absDeltahigh, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::PI}, {210, -10.0, 200.0}}}); + + registry.add("h_jet_pt_in_plane_v2_abs", "jet pT;#it{p}^{in-plane}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); + registry.add("h_jet_pt_in_plane_v2_rho_abs", "jet pT;#it{p}^{in-plane}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); + registry.add("h_jet_pt_out_of_plane_v2_abs", "jet pT;#it{p}^{out-of-plane}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); + registry.add("h_jet_pt_out_of_plane_v2_rho_abs", "jet pT;#it{p}^{out-of-plane}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); + + registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_in_abs", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_out_abs", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_in_abs", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_out_abs", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + registry.add("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_in_abs", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + registry.add("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_out_abs", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + } //< bkg sub plot | end >// //< median rho >// registry.add("h_jet_pt_in_plane_v2", "jet pT;#it{p}^{in-plane}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); registry.add("h_jet_pt_out_of_plane_v2", "jet pT;#it{p}^{out-of-plane}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); + + registry.add("h2_phi_rholocal_Delta_low_inplane", "#varphi vs #rho(#varphi)absDeltaLow inplane, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); + registry.add("h2_phi_rholocal_Delta_medium_inplane", "#varphi vs #rho(#varphi)absDeltaMediun inplane, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); + registry.add("h2_phi_rholocal_Delta_high_inplane", "#varphi vs #rho(#varphi)absDeltahigh inplane, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); + + registry.add("h2_phi_rholocal_Delta_low_outplane", "#varphi vs #rho(#varphi)absDeltaLow outplane, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); + registry.add("h2_phi_rholocal_Delta_medium_outplane", "#varphi vs #rho(#varphi)absDeltaMediun outplane, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); + registry.add("h2_phi_rholocal_Delta_high_outplane", "#varphi vs #rho(#varphi)absDeltahigh outplane, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); + registry.add("h_jet_pt_in_plane_v3", "jet pT;#it{p}^{in-plane}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); registry.add("h_jet_pt_out_of_plane_v3", "jet pT;#it{p}^{out-of-plane}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); @@ -427,6 +479,15 @@ struct JetChargedV2 { registry.add("h2_centrality_jet_eta_rhoareasubtracted", "centrality vs. jet eta;centrality; #eta; counts", {HistType::kTH2F, {centralityAxis, jetEtaAxis}}); registry.add("h2_centrality_jet_phi_rhoareasubtracted", "centrality vs. jet phi;centrality; #varphi; counts", {HistType::kTH2F, {centralityAxis, phiAxis}}); registry.add("h2_jet_pt_track_pt_rhoareasubtracted", "jet #it{p}_{T,jet} vs. #it{p}_{T,track}; #it{p}_{T,jet} (GeV/#it{c}); #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxisRhoAreaSub, trackPtAxis}}); + + registry.add("h_jet_pt_rhoareasubtracted", "jet pT;#it{p}_{T,jet} (GeV/#it{c}); counts", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}, doSumw2); + registry.add("h_jet_eta_rhoareasubtracted", "jet eta;#eta; counts", {HistType::kTH1F, {jetEtaAxis}}, doSumw2); + registry.add("h_jet_phi_rhoareasubtracted", "jet phi;#phi; counts", {HistType::kTH1F, {phiAxis}}, doSumw2); + registry.add("h2_jet_pt_jet_area_rhoareasubtracted", "jet #it{p}_{T,jet} vs. Area_{jet}; #it{p}_{T,jet} (GeV/#it{c}); Area_{jet}", {HistType::kTH2F, {jetPtAxis, {150, 0., 1.5}}}, doSumw2); + registry.add("h2_jet_pt_jet_ntracks_rhoareasubtracted", "jet #it{p}_{T,jet} vs. N_{jet tracks}; #it{p}_{T,jet} (GeV/#it{c}); N_{jet, tracks}", {HistType::kTH2F, {jetPtAxis, {200, -0.5, 199.5}}}, doSumw2); + registry.add("h2_jet_pt_jet_corr_pt_rhoareasubtracted", "jet #it{p}_{T,jet} vs. #it{p}_{T,corr}; #it{p}_{T,jet} (GeV/#it{c}); #it{p}_{T,corr} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxis, jetPtAxisRhoAreaSub}}, doSumw2); + registry.add("h2_jet_pt_jet_angularity_rhoareasubtracted", "jet #it{p}_{T,jet} vs. angularity;#it{p}_{T,jet} (GeV/#it{c});angularity", {HistType::kTH2F, {jetPtAxisRhoAreaSub, {30, 0., 1.}}}, doSumw2); + registry.add("h3_jet_pt_jet_eta_jet_phi_rhoareasubtracted", "jet_pt_eta_phi_rhoareasubtracted", {HistType::kTH3F, {jetPtAxisRhoAreaSub, jetEtaAxis, phiAxis}}, doSumw2); } if (doprocessSigmaPtMCP || doprocessSigmaPtAreaSubMCP) { @@ -456,6 +517,14 @@ struct JetChargedV2 { registry.add("h2_mcp_centrality_rholocal", "#varphi vs #rho(#varphi); #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{120, -10.0, 110.0}, {210, -10.0, 200.0}}}); registry.add("h_mcp_jet_pt_rhoareasubtracted", "jet pT rholocal;#it{p}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); registry.add("h_mcp_jet_pt_rholocal", "jet pT rholocal;#it{p}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); + + //< MCP fit quality (independent from the data/MCD QA histograms) >// + registry.add("h_mcp_PvalueCDF_CombinFit", "cDF #chi^{2}; entries", {HistType::kTH1F, {{50, 0, 1}}}); + registry.add("h2_mcp_PvalueCDFCent_CombinFit", "p-value cDF vs centrality; centrality; p-value", {HistType::kTH2F, {{100, 0, 100}, {40, 0, 1}}}); + registry.add("h2_mcp_Chi2Cent_CombinFit", "Chi2 vs centrality; centrality; #tilde{#chi^{2}}", {HistType::kTH2F, {{100, 0, 100}, {100, 0, 5}}}); + registry.add("h2_mcp_PChi2_CombinFit", "p-value vs #tilde{#chi^{2}}; p-value; #tilde{#chi^{2}}", {HistType::kTH2F, {{100, 0, 1}, {100, 0, 5}}}); + registry.add("h2_mcp_PChi2_CombinFitA", "p-value vs #tilde{#chi^{2}}; p-value; #tilde{#chi^{2}}", {HistType::kTH2F, {{100, 0, 1}, {100, 0, 5}}}); + registry.add("h2_mcp_PChi2_CombinFitB", "p-value vs #tilde{#chi^{2}}; p-value; #tilde{#chi^{2}}", {HistType::kTH2F, {{100, 0, 1}, {100, 0, 5}}}); //< MCP fit test >// registry.add("h_mcp_evtnum_centrlity", "eventNumber vs centrality ; #eventNumber", {HistType::kTH1F, {{1000, 0.0, 1000}}}); registry.add("h_ep2_evtnum", "fitparameter #rho_{0} vs evtnum ; #eventnumber", {HistType::kTH1F, {{1000, 0.0, 1000}}}); @@ -549,6 +618,45 @@ struct JetChargedV2 { registry.add("h_track_pt", "track #it{p}_{T} ; #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH1F, {trackPtAxis}}); registry.add("h2_track_eta_track_phi", "track eta vs. track phi; #eta; #phi; counts", {HistType::kTH2F, {trackEtaAxis, phiAxis}}); + + // optional sparse storage of the analysis ingredients (booked always, filled only on request, + // so that the output merging always sees the same object names) + // binning follows the existing 2D outputs of this task, so that the sparses can be compared + const auto& axes = sparseAxes.value; + if (axes.size() < 12) { + LOGF(fatal, "sparseAxes needs 12 numbers (4 x (nbins, min, max)), got %d", static_cast(axes.size())); + } + // bin by bin with h2_rholocal_pt (rho 110 x pT 210), h2_phi_rholocal (phi 100 x rho 110) and + // h2_centrality_deltapT_RandomCornPhi_* (centrality 1100 x delta pT 1000) + const AxisSpec axisQAPt{static_cast(axes[0]), axes[1], axes[2], "#it{p}_{T,jet}^{raw} (GeV/#it{c})"}; + const AxisSpec axisQAEta{20, -1., 1., "#it{#eta}_{jet}"}; + const AxisSpec axisQAPhi{static_cast(axes[9]), axes[10], axes[11], "#it{#varphi}_{jet} (rad)"}; + const auto& extra = sparseExtraAxes.value; + if (extra.size() < 9) { + LOGF(fatal, "sparseExtraAxes needs 9 numbers (3 x (nbins, min, max)), got %d", static_cast(extra.size())); + } + const AxisSpec axisQAArea{static_cast(extra[3]), extra[4], extra[5], "area"}; + const AxisSpec axisQANtracks{static_cast(extra[6]), extra[7], extra[8], "N_{tracks}^{jet}"}; + const AxisSpec axisQACent{static_cast(axes[3]), axes[4], axes[5], "centrality (%)"}; + hJetQA = registry.add("hJetQA", "per-jet QA", {HistType::kTHnSparseF, {{axisQAPt}, {axisQAEta}, {axisQAPhi}, {axisQAArea}, {axisQANtracks}, {axisQACent}}}, doSumw2Sparse); + + const AxisSpec axisRhoValue{static_cast(axes[6]), axes[7], axes[8], "#it{#rho} (GeV)"}; + const AxisSpec axisRhoArea{static_cast(axes[0]), axes[1], axes[2], "#it{#rho} #upoint A (GeV)"}; + const AxisSpec axisRhoDphi{static_cast(axes[9]), axes[10], axes[11], "#varphi - #Psi_{EP,2} (rad)"}; + hRhoMedian = registry.add("hRhoMedian", "per-jet information, event-averaged (median) rho", {HistType::kTHnSparseF, {{axisQACent}, {axisQAPt}, {axisRhoValue}, {axisRhoArea}, {axisRhoDphi}, {axisQAArea}}}, doSumw2Sparse); + hRhoLocal = registry.add("hRhoLocal", "per-jet information, local rho", {HistType::kTHnSparseF, {{axisQACent}, {axisQAPt}, {axisRhoValue}, {axisRhoArea}, {axisRhoDphi}, {axisQAArea}}}, doSumw2Sparse); + + const AxisSpec axisFluctDpt{static_cast(extra[0]), extra[1], extra[2], "#delta#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisFluctAbsDphi{static_cast(axes[9]), axes[10], axes[11], "|#varphi_{cone} - #Psi_{EP,2}| (rad)"}; + const AxisSpec axisFluctArea{static_cast(extra[3]), extra[4], extra[5], "area_{cone}"}; + const AxisSpec axisFluctRho{static_cast(axes[6]), axes[7], axes[8], "#it{#rho}_{local}^{RC} (GeV)"}; + const AxisSpec axisFluctNtracks{static_cast(extra[6]), extra[7], extra[8], "N_{tracks}^{cone}"}; + hFluctQA = registry.add("hFluctQA", "per-random-cone QA", {HistType::kTHnSparseF, {{axisQACent}, {axisFluctDpt}, {axisFluctAbsDphi}, {axisFluctArea}, {axisFluctRho}, {axisFluctNtracks}}}, doSumw2Sparse); + hFluctQAwoLeadingJet = registry.add( + "hFluctQAwoLeadingJet", "per-random-cone QA, leading jet removed from the cone", + {HistType::kTHnSparseF, + {{axisQACent}, {axisFluctDpt}, {axisFluctAbsDphi}, {axisFluctArea}, {axisFluctRho}, {axisFluctNtracks}}}, + doSumw2Sparse); } Filter trackCuts = (aod::jtrack::pt >= trackPtMin && aod::jtrack::pt < trackPtMax && aod::jtrack::eta > trackEtaMin && aod::jtrack::eta < trackEtaMax); @@ -556,8 +664,6 @@ struct JetChargedV2 { Filter mcEventCuts = (nabs(aod::jmccollision::posZ) < vertexZCut); Preslice tracksPerJCollision = o2::aod::jtrack::collisionId; - Preslice mcdjetsPerJCollision = o2::aod::jet::collisionId; - PresliceUnsorted> collisionsPerMCPCollision = aod::jmccollisionlb::mcCollisionId; template bool isAcceptedJet(TJets const& jet, bool mcLevelIsParticleLevel = false) @@ -800,6 +906,38 @@ struct JetChargedV2 { } } + template + void fillJetAreaSubHistograms(TJets const& jet, float centrality, float rho, float weight = 1.0, float pTHat = 999.0) + { + if (jet.pt() > pTHatMaxMCD * pTHat || pTHat < pTHatAbsoluteMin) { + return; + } + double jetcorrpt = jet.pt() - (rho * jet.area()); + if (jet.r() == round(selectedJetsRadius * 100.0f)) { + // fill jet histograms after area-based subtraction + registry.fill(HIST("h_jet_pt_rhoareasubtracted"), jetcorrpt, weight); + registry.fill(HIST("h2_centrality_jet_pt_rhoareasubtracted"), centrality, jetcorrpt, weight); + registry.fill(HIST("h2_jet_pt_jet_corr_pt_rhoareasubtracted"), jet.pt(), jetcorrpt, weight); + registry.fill(HIST("h3_jet_pt_jet_eta_jet_phi_rhoareasubtracted"), jetcorrpt, jet.eta(), jet.phi(), weight); + if (jetcorrpt > 0) { + registry.fill(HIST("h_jet_eta_rhoareasubtracted"), jet.eta(), weight); + registry.fill(HIST("h_jet_phi_rhoareasubtracted"), jet.phi(), weight); + registry.fill(HIST("h2_centrality_jet_eta_rhoareasubtracted"), centrality, jet.eta(), weight); + registry.fill(HIST("h2_centrality_jet_phi_rhoareasubtracted"), centrality, jet.phi(), weight); + registry.fill(HIST("h2_jet_pt_jet_area_rhoareasubtracted"), jetcorrpt, jet.area(), weight); + registry.fill(HIST("h2_jet_pt_jet_ntracks_rhoareasubtracted"), jetcorrpt, jet.tracksIds().size(), weight); + } + } + + float angularity = 0.; + for (const auto& constituent : jet.template tracks_as()) { + registry.fill(HIST("h2_jet_pt_track_pt_rhoareasubtracted"), jetcorrpt, constituent.pt(), weight); + angularity += std::pow(constituent.pt(), kappa) * std::pow(jetutilities::deltaR(jet, constituent), alpha); + } + angularity /= (jet.pt() * (jet.r() / 100.f)); + registry.fill(HIST("h2_jet_pt_jet_angularity_rhoareasubtracted"), jetcorrpt, angularity, weight); + } + double chiSquareCDF(int nDF, double x) { return TMath::Gamma(nDF / 2., x / 2.); @@ -879,6 +1017,36 @@ struct JetChargedV2 { registry.fill(HIST("leadJetEtaMCP"), leadingJetEta); } + // Integrate the rho modulation fit over [phi - radius, phi + radius]. Both limits are + // wrapped into [0, 2pi) with RecoDecay::constrainAngle; when the wrapped interval + // crosses zero it is split into two pieces (a plain integral between the two wrapped + // limits would be negative there). + double getRhoLocalIntegral(TF1* fitFunc, double phi, double radius, float fitRangeMargin) + { + if (!fitFunc) { + return 0.0; + } + if (radius > fitRangeMargin) { + static bool warnOnce = true; + if (warnOnce) { + LOGF(warning, "Rho fit range margin (%.3f) is smaller than integration radius (%.3f)", fitRangeMargin, radius); + warnOnce = false; + } + } + const double twoPi = o2::constants::math::TwoPI; + const double low = RecoDecay::constrainAngle(phi - radius, 0.); + const double high = RecoDecay::constrainAngle(phi + radius, 0.); + if (high < low) { // wrapped interval crosses zero + return fitFunc->Integral(low, twoPi) + fitFunc->Integral(0., high); + } + return fitFunc->Integral(low, high); + } + + void getRhoLocalIntegral(TF1* fitFunc, double phi, double radius, float fitRangeMargin, double& integralValue) + { + integralValue = getRhoLocalIntegral(fitFunc, phi, radius, fitRangeMargin); + } + // Run General_Purpose MC MCP template void fitFncAreaSubMCP(U const& collision, J const& jets, TH1F* hPtsumSumptFitMCPFill, bool mcLevelIsParticleLevel, float weight = 1.0) @@ -903,9 +1071,9 @@ struct JetChargedV2 { int evtPlnAngleA = 7; int evtPlnAngleB = 3; int evtPlnAngleC = 5; - for (uint i = 0; i < cfgnMods->size(); i++) { - int nmode = cfgnMods->at(i); - int detInd = detId * 4 + cfgnTotalSystem * 4 * (nmode - 2); + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { + int nmode = epQvec.cfgnMods->at(i); + int detInd = detId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); if (nmode == cfgNmodA) { if (collision.qvecAmp()[detId] > collQvecAmpDetId) { ep2 = helperEP.GetEventPlane(collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], nmode); @@ -939,7 +1107,6 @@ struct JetChargedV2 { hPtsumSumptFitMCPFill->Fit(fFitModulationV2v3P, "Q", "ep", 0, o2::constants::math::TwoPI); - // double temppara[5]; std::array temppara{}; temppara[0] = fFitModulationV2v3P->GetParameter(0); temppara[1] = fFitModulationV2v3P->GetParameter(1); @@ -954,9 +1121,40 @@ struct JetChargedV2 { registry.fill(HIST("h_mcp_v3obs_centrality"), centrality, temppara[3]); registry.fill(HIST("h_mcp_evtnum_centrlity"), evtnum, centrality); - for (uint i = 0; i < cfgnMods->size(); i++) { - int nmode = cfgnMods->at(i); - int detInd = detId * 4 + cfgnTotalSystem * 4 * (nmode - 2); + const int numOfFreePara = 3; // psi2/psi3 are fixed with FixParameter -> [0], [1], [3] are free + int nBinsUsed = 0; + double chi2 = 0.; + for (int i = 0; i < hPtsumSumptFitMCPFill->GetXaxis()->GetNbins(); i++) { + if (hPtsumSumptFitMCPFill->GetBinContent(i + 1) <= 0.) { + continue; // empty bins do not enter the chi2 and must not count in the dof + } + nBinsUsed++; + chi2 += std::pow((hPtsumSumptFitMCPFill->GetBinContent(i + 1) - fFitModulationV2v3P->Eval(hPtsumSumptFitMCPFill->GetXaxis()->GetBinCenter(1 + i))), 2) / hPtsumSumptFitMCPFill->GetBinContent(i + 1); + } + const int nDF = nBinsUsed - numOfFreePara; // number of fitted points minus the free parameters + + int evtCentAreaMin = 0; + int evtCentAreaMax = 5; + int evtMidAreaMin = 30; + int evtMidAreaMax = 50; + + if (nDF > 0 && cfgChkFitQuality) { + const double chiSqr = chi2; + const double cDF = 1. - chiSquareCDF(nDF, chiSqr); + registry.fill(HIST("h_mcp_PvalueCDF_CombinFit"), cDF); + registry.fill(HIST("h2_mcp_PvalueCDFCent_CombinFit"), centrality, cDF); + registry.fill(HIST("h2_mcp_Chi2Cent_CombinFit"), centrality, chiSqr / static_cast(nDF)); + registry.fill(HIST("h2_mcp_PChi2_CombinFit"), cDF, chiSqr / static_cast(nDF)); + if (centrality >= evtCentAreaMin && centrality <= evtCentAreaMax) { + registry.fill(HIST("h2_mcp_PChi2_CombinFitA"), cDF, chiSqr / static_cast(nDF)); + } else if (centrality >= evtMidAreaMin && centrality <= evtMidAreaMax) { + registry.fill(HIST("h2_mcp_PChi2_CombinFitB"), cDF, chiSqr / static_cast(nDF)); + } + } + + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { + int nmode = epQvec.cfgnMods->at(i); + int detInd = detId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); for (auto const& jet : jets) { if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { @@ -969,7 +1167,7 @@ struct JetChargedV2 { continue; } - double integralValue = fFitModulationV2v3P->Integral(jet.phi() - selectedJetsRadius, jet.phi() + selectedJetsRadius); + double integralValue = getRhoLocalIntegral(fFitModulationV2v3P, jet.phi(), selectedJetsRadius, rhoFitRangeMargin); double rholocal = collision.rho() / (2 * selectedJetsRadius * temppara[0]) * integralValue; registry.fill(HIST("h2_mcp_phi_rholocal"), jet.phi() - ep2, rholocal, weight); registry.fill(HIST("h2_mcp_centrality_rholocal"), centrality, rholocal, weight); @@ -981,9 +1179,9 @@ struct JetChargedV2 { continue; } // phiMinusPsi2 = jet.phi() - ep2; - phiMinusPsi2 = RecoDecay::constrainAngle(jet.phi() - ep2, -o2::constants::math::PI); - float absDelta = std::abs(phiMinusPsi2); - if ((absDelta < o2::constants::math::PIQuarter) || (absDelta >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absDelta >= evtPlnAngleB * o2::constants::math::PIQuarter && absDelta < evtPlnAngleC * o2::constants::math::PIQuarter)) { + phiMinusPsi2 = RecoDecay::constrainAngle(jet.phi() - ep2, 0.0f); + // same in-/out-of-plane definition as in processSigmaPtData (unfolded phi - Psi2) + if ((phiMinusPsi2 < o2::constants::math::PIQuarter) || (phiMinusPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (phiMinusPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && phiMinusPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { registry.fill(HIST("h_mcp_jet_pt_in_plane_v2_rho"), jet.pt() - (rholocal * jet.area()), weight); registry.fill(HIST("h2_mcp_centrality_jet_pt_in_plane_v2_rho"), centrality, jet.pt() - (rholocal * jet.area()), weight); } else { @@ -1131,12 +1329,12 @@ struct JetChargedV2 { int evtPlnAngleA = 7; int evtPlnAngleB = 3; int evtPlnAngleC = 5; - double integralValue = fFitModulationRMFill->Integral(jetMCD.phi() - selectedJetsRadius, jetMCD.phi() + selectedJetsRadius); + double integralValue = getRhoLocalIntegral(fFitModulationRMFill, jetMCD.phi(), selectedJetsRadius, rhoFitRangeMargin); double rholocal = rho / (2 * selectedJetsRadius * tempparaA) * integralValue; double corrBasejetpt = jetMCD.pt() - (rholocal * jetMCD.area()); double corrTagjetpt = 0.0; if (subtractMCPBackgroundBool) { - double integralValueMCP = fFitModulationRMFill->Integral(jetMCP.phi() - selectedJetsRadius, jetMCP.phi() + selectedJetsRadius); + double integralValueMCP = getRhoLocalIntegral(fFitModulationRMFill, jetMCP.phi(), selectedJetsRadius, rhoFitRangeMargin); double rholocalMCP = mcrho / (2 * selectedJetsRadius * tempparaA) * integralValueMCP; corrTagjetpt = jetMCP.pt() - (rholocalMCP * jetMCP.area()); } else { @@ -1291,26 +1489,27 @@ struct JetChargedV2 { //=====================< evt pln [n=2->\Psi_2, n=3->\Psi_3] >=====================// int cfgNmodA = 2; int cfgNmodB = 3; - for (uint i = 0; i < cfgnMods->size(); i++) { - int nmode = cfgnMods->at(i); - int detInd = detId * 4 + cfgnTotalSystem * 4 * (nmode - 2); - int detIndFT0M = (detIdCheck + 2) * 4 + cfgnTotalSystem * 4 * (nmode - 2); - int refAInd = refAId * 4 + cfgnTotalSystem * 4 * (nmode - 2); - int refBInd = refBId * 4 + cfgnTotalSystem * 4 * (nmode - 2); + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { + int nmode = epQvec.cfgnMods->at(i); + int detInd = detId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); + int detIndFT0M = (detIdCheck + 2) * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); + int refAInd = refAId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); + int refBInd = refBId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); if (nmode == cfgNmodA) { if (collision.qvecAmp()[detId] > collQvecAmpDetId && collision.qvecAmp()[refAId] > collQvecAmpDetId && collision.qvecAmp()[refBId] > collQvecAmpDetId) { - histosQA.fill(HIST("histQvecUncorV2"), collision.qvecRe()[detInd], collision.qvecIm()[detInd], collision.cent()); - histosQA.fill(HIST("histQvecRectrV2"), collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], collision.cent()); - histosQA.fill(HIST("histQvecTwistV2"), collision.qvecRe()[detInd + 2], collision.qvecIm()[detInd + 2], collision.cent()); - histosQA.fill(HIST("histQvecFinalV2"), collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], collision.cent()); + if (fillQvecQA) { + histosQA.fill(HIST("histQvecUncorV2"), collision.qvecRe()[detInd], collision.qvecIm()[detInd], collision.cent()); + histosQA.fill(HIST("histQvecRectrV2"), collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], collision.cent()); + histosQA.fill(HIST("histQvecTwistV2"), collision.qvecRe()[detInd + 2], collision.qvecIm()[detInd + 2], collision.cent()); + histosQA.fill(HIST("histQvecFinalV2"), collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], collision.cent()); + } histosQA.fill(HIST("histEvtPlUncorV2"), helperEP.GetEventPlane(collision.qvecRe()[detInd], collision.qvecIm()[detInd], nmode), collision.cent()); histosQA.fill(HIST("histEvtPlRectrV2"), helperEP.GetEventPlane(collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], nmode), collision.cent()); histosQA.fill(HIST("histEvtPlTwistV2"), helperEP.GetEventPlane(collision.qvecRe()[detInd + 2], collision.qvecIm()[detInd + 2], nmode), collision.cent()); histosQA.fill(HIST("histEvtPlFinalV2"), helperEP.GetEventPlane(collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], nmode), collision.cent()); - // mark histosQA.fill(HIST("h_ep2_FT0CV2"), helperEP.GetEventPlane(collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], nmode), collision.cent()); histosQA.fill(HIST("h_ep2_FT0MV2"), helperEP.GetEventPlane(collision.qvecRe()[detIndFT0M + 3], collision.qvecIm()[detIndFT0M + 3], nmode), collision.cent()); histosQA.fill(HIST("h2_ep2_FT0C_FT0M"), helperEP.GetEventPlane(collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], nmode), helperEP.GetEventPlane(collision.qvecRe()[detIndFT0M + 3], collision.qvecIm()[detIndFT0M + 3], nmode), collision.cent()); @@ -1320,10 +1519,12 @@ struct JetChargedV2 { histosQA.fill(HIST("histEvtPlRes_RefARefBV2"), helperEP.GetResolution(helperEP.GetEventPlane(collision.qvecRe()[refAInd + 3], collision.qvecIm()[refAInd + 3], nmode), helperEP.GetEventPlane(collision.qvecRe()[refBInd + 3], collision.qvecIm()[refBInd + 3], nmode), nmode), collision.cent()); } } else if (nmode == cfgNmodB) { - histosQA.fill(HIST("histQvecUncorV3"), collision.qvecRe()[detInd], collision.qvecIm()[detInd], collision.cent()); - histosQA.fill(HIST("histQvecRectrV3"), collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], collision.cent()); - histosQA.fill(HIST("histQvecTwistV3"), collision.qvecRe()[detInd + 2], collision.qvecIm()[detInd + 2], collision.cent()); - histosQA.fill(HIST("histQvecFinalV3"), collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], collision.cent()); + if (fillQvecQA) { + histosQA.fill(HIST("histQvecUncorV3"), collision.qvecRe()[detInd], collision.qvecIm()[detInd], collision.cent()); + histosQA.fill(HIST("histQvecRectrV3"), collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], collision.cent()); + histosQA.fill(HIST("histQvecTwistV3"), collision.qvecRe()[detInd + 2], collision.qvecIm()[detInd + 2], collision.cent()); + histosQA.fill(HIST("histQvecFinalV3"), collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], collision.cent()); + } histosQA.fill(HIST("histEvtPlUncorV3"), helperEP.GetEventPlane(collision.qvecRe()[detInd], collision.qvecIm()[detInd], nmode), collision.cent()); histosQA.fill(HIST("histEvtPlRectrV3"), helperEP.GetEventPlane(collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], nmode), collision.cent()); @@ -1358,18 +1559,20 @@ struct JetChargedV2 { //=====================< evt pln [n=2->\Psi_2, n=3->\Psi_3] >=====================// int cfgNmodA = 2; int cfgNmodB = 3; - for (uint i = 0; i < cfgnMods->size(); i++) { - int nmode = cfgnMods->at(i); - int detInd = detId * 4 + cfgnTotalSystem * 4 * (nmode - 2); - int refAInd = refAId * 4 + cfgnTotalSystem * 4 * (nmode - 2); - int refBInd = refBId * 4 + cfgnTotalSystem * 4 * (nmode - 2); + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { + int nmode = epQvec.cfgnMods->at(i); + int detInd = detId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); + int refAInd = refAId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); + int refBInd = refBId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); if (nmode == cfgNmodA) { if (collision.qvecAmp()[detId] > collQvecAmpDetId && collision.qvecAmp()[refAId] > collQvecAmpDetId && collision.qvecAmp()[refBId] > collQvecAmpDetId) { - histosQA.fill(HIST("histQvecUncorV2"), collision.qvecRe()[detInd], collision.qvecIm()[detInd], collision.cent()); - histosQA.fill(HIST("histQvecRectrV2"), collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], collision.cent()); - histosQA.fill(HIST("histQvecTwistV2"), collision.qvecRe()[detInd + 2], collision.qvecIm()[detInd + 2], collision.cent()); - histosQA.fill(HIST("histQvecFinalV2"), collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], collision.cent()); + if (fillQvecQA) { + histosQA.fill(HIST("histQvecUncorV2"), collision.qvecRe()[detInd], collision.qvecIm()[detInd], collision.cent()); + histosQA.fill(HIST("histQvecRectrV2"), collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], collision.cent()); + histosQA.fill(HIST("histQvecTwistV2"), collision.qvecRe()[detInd + 2], collision.qvecIm()[detInd + 2], collision.cent()); + histosQA.fill(HIST("histQvecFinalV2"), collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], collision.cent()); + } histosQA.fill(HIST("histEvtPlUncorV2"), helperEP.GetEventPlane(collision.qvecRe()[detInd], collision.qvecIm()[detInd], nmode), collision.cent()); histosQA.fill(HIST("histEvtPlRectrV2"), helperEP.GetEventPlane(collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], nmode), collision.cent()); @@ -1381,10 +1584,12 @@ struct JetChargedV2 { histosQA.fill(HIST("histEvtPlRes_RefARefBV2"), helperEP.GetResolution(helperEP.GetEventPlane(collision.qvecRe()[refAInd + 3], collision.qvecIm()[refAInd + 3], nmode), helperEP.GetEventPlane(collision.qvecRe()[refBInd + 3], collision.qvecIm()[refBInd + 3], nmode), nmode), collision.cent()); } } else if (nmode == cfgNmodB) { - histosQA.fill(HIST("histQvecUncorV3"), collision.qvecRe()[detInd], collision.qvecIm()[detInd], collision.cent()); - histosQA.fill(HIST("histQvecRectrV3"), collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], collision.cent()); - histosQA.fill(HIST("histQvecTwistV3"), collision.qvecRe()[detInd + 2], collision.qvecIm()[detInd + 2], collision.cent()); - histosQA.fill(HIST("histQvecFinalV3"), collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], collision.cent()); + if (fillQvecQA) { + histosQA.fill(HIST("histQvecUncorV3"), collision.qvecRe()[detInd], collision.qvecIm()[detInd], collision.cent()); + histosQA.fill(HIST("histQvecRectrV3"), collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], collision.cent()); + histosQA.fill(HIST("histQvecTwistV3"), collision.qvecRe()[detInd + 2], collision.qvecIm()[detInd + 2], collision.cent()); + histosQA.fill(HIST("histQvecFinalV3"), collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], collision.cent()); + } histosQA.fill(HIST("histEvtPlUncorV3"), helperEP.GetEventPlane(collision.qvecRe()[detInd], collision.qvecIm()[detInd], nmode), collision.cent()); histosQA.fill(HIST("histEvtPlRectrV3"), helperEP.GetEventPlane(collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], nmode), collision.cent()); @@ -1426,6 +1631,7 @@ struct JetChargedV2 { if (!isAcceptedJet(jet)) { continue; } + fillJetAreaSubHistograms(jet, centrality, collision.rho()); } double leadingJetPt = -1; @@ -1437,7 +1643,8 @@ struct JetChargedV2 { int nTrk = 0; getNtrk(tracks, jets, nTrk, evtnum, leadingJetEta); if (nTrk <= 0) { - return; + registry.fill(HIST("h_collisions"), 4.5); + return; // otherwise the fit histogram below would be created with 0 bins } hPtsumSumptFit = new TH1F("h_ptsum_sumpt_fit", "h_ptsum_sumpt fit use", TMath::CeilNint(std::sqrt(nTrk)), 0., o2::constants::math::TwoPI); @@ -1447,12 +1654,19 @@ struct JetChargedV2 { double ep3 = 0.; int cfgNmodA = 2; int cfgNmodB = 3; - int evtPlnAngleA = 3; - // int evtPlnAngleB = 3; - // int evtPlnAngleC = 5; - for (uint i = 0; i < cfgnMods->size(); i++) { - int nmode = cfgnMods->at(i); - int detInd = detId * 4 + cfgnTotalSystem * 4 * (nmode - 2); + // In-plane / out-of-plane windows, identical to the downstream QA and unfolding + // macros (analysisCode_headFiles.cpp::GetInOutPlane2D), which cut + // Delta phi = phi - Psi_2 in [0, 2pi) at pi/4, 3pi/4, 5pi/4 and 7pi/4: + // in-plane : [0, pi/4) U [3pi/4, 5pi/4) U [7pi/4, 2pi) + // out-of-plane : [pi/4, 3pi/4) U [5pi/4, 7pi/4) + // The same composite expression also gives the correct region for a folded + // |Delta phi| in [0, pi], namely [0, pi/4) U [3pi/4, pi]. + int evtPlnAngleA = 7; + int evtPlnAngleB = 3; + int evtPlnAngleC = 5; + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { + int nmode = epQvec.cfgnMods->at(i); + int detInd = detId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); if (nmode == cfgNmodA) { if (collision.qvecAmp()[detId] > collQvecAmpDetId) { @@ -1502,54 +1716,41 @@ struct JetChargedV2 { registry.fill(HIST("h_v3obs_centrality"), centrality, temppara[3]); registry.fill(HIST("h_evtnum_centrlity"), evtnum, centrality); - int nDF = 1; - int numOfFreePara = 2; - nDF = static_cast(fFitModulationV2v3->GetXaxis()->GetNbins()) - numOfFreePara; - if (nDF == 0 || static_cast(nDF) <= 0.) { - return; - } + const int numOfFreePara = 3; // psi2/psi3 are fixed with FixParameter -> [0], [1], [3] are free + int nBinsUsed = 0; double chi2 = 0.; for (int i = 0; i < hPtsumSumptFit->GetXaxis()->GetNbins(); i++) { if (hPtsumSumptFit->GetBinContent(i + 1) <= 0.) { - continue; + continue; // empty bins do not enter the chi2 and must not count in the dof } + nBinsUsed++; chi2 += std::pow((hPtsumSumptFit->GetBinContent(i + 1) - fFitModulationV2v3->Eval(hPtsumSumptFit->GetXaxis()->GetBinCenter(1 + i))), 2) / hPtsumSumptFit->GetBinContent(i + 1); } - - double chiSqr = 999.; - double cDF = 1.; - - chiSqr = chi2; - cDF = 1. - chiSquareCDF(nDF, chiSqr); + const int nDF = nBinsUsed - numOfFreePara; // number of fitted points minus the free parameters int evtCentAreaMin = 0; int evtCentAreaMax = 5; int evtMidAreaMin = 30; int evtMidAreaMax = 50; - if (cfgChkFitQuality) { + if (nDF > 0 && cfgChkFitQuality) { + const double chiSqr = chi2; + const double cDF = 1. - chiSquareCDF(nDF, chiSqr); registry.fill(HIST("h_PvalueCDF_CombinFit"), cDF); registry.fill(HIST("h2_PvalueCDFCent_CombinFit"), centrality, cDF); - registry.fill(HIST("h2_Chi2Cent_CombinFit"), centrality, chiSqr / (static_cast(nDF))); - registry.fill(HIST("h2_PChi2_CombinFit"), cDF, chiSqr / (static_cast(nDF))); + registry.fill(HIST("h2_Chi2Cent_CombinFit"), centrality, chiSqr / static_cast(nDF)); + registry.fill(HIST("h2_PChi2_CombinFit"), cDF, chiSqr / static_cast(nDF)); if (centrality >= evtCentAreaMin && centrality <= evtCentAreaMax) { - registry.fill(HIST("h2_PChi2_CombinFitA"), cDF, chiSqr / (static_cast(nDF))); - + registry.fill(HIST("h2_PChi2_CombinFitA"), cDF, chiSqr / static_cast(nDF)); } else if (centrality >= evtMidAreaMin && centrality <= evtMidAreaMax) { - registry.fill(HIST("h2_PChi2_CombinFitB"), cDF, chiSqr / (static_cast(nDF))); - } - if (centrality >= evtCentAreaMin && centrality <= evtCentAreaMax) { - registry.fill(HIST("h2_PChi2_CombinFitA"), cDF, chiSqr / (static_cast(nDF))); - - } else if (centrality >= evtMidAreaMin && centrality <= evtMidAreaMax) { - registry.fill(HIST("h2_PChi2_CombinFitB"), cDF, chiSqr / (static_cast(nDF))); + registry.fill(HIST("h2_PChi2_CombinFitB"), cDF, chiSqr / static_cast(nDF)); } } - for (uint i = 0; i < cfgnMods->size(); i++) { - int nmode = cfgnMods->at(i); - int detInd = detId * 4 + cfgnTotalSystem * 4 * (nmode - 2); - int detIndFT0M = 4 * detCheck + cfgnTotalSystem * 4 * (nmode - 2); + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { + int nmode = epQvec.cfgnMods->at(i); + int detInd = detId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); + int detIndFT0M = 4 * epQvec.detCheck + epQvec.cfgnTotalSystem * 4 * (nmode - 2); int bumpLow = 75; int bumpUp = 125; @@ -1584,24 +1785,7 @@ struct JetChargedV2 { if (jet.r() != round(selectedJetsRadius * 100.0f)) { continue; } - double twoPi = o2::constants::math::TwoPI; - double phi = std::fmod(jet.phi(), twoPi); - if (phi < 0) { - phi += twoPi; - } - - double low = phi - selectedJetsRadius; - double high = phi + selectedJetsRadius; - double integralValue = 0.0; - if (low < 0) { - integralValue += fFitModulationV2v3->Integral(low + twoPi, twoPi); - integralValue += fFitModulationV2v3->Integral(0, high); - } else if (high > twoPi) { - integralValue += fFitModulationV2v3->Integral(low, twoPi); - integralValue += fFitModulationV2v3->Integral(0, high - twoPi); - } else { - integralValue += fFitModulationV2v3->Integral(low, high); - } + double integralValue = getRhoLocalIntegral(fFitModulationV2v3, jet.phi(), selectedJetsRadius, rhoFitRangeMargin); double rholocal = 0.0; if (integralValue <= 0) { @@ -1624,54 +1808,70 @@ struct JetChargedV2 { continue; } // phiMinusPsi2 = jet.phi() - ep2; - phiMinusPsi2 = RecoDecay::constrainAngle(jet.phi() - ep2, -o2::constants::math::PI); - float absDelta = std::abs(phiMinusPsi2); + phiMinusPsi2 = RecoDecay::constrainAngle(jet.phi() - ep2, 0.0f); + // folded |Delta phi| in [0, pi], only used by the checkAbsDeltaPhi cross-check + float absDelta = std::abs(RecoDecay::constrainAngle(jet.phi() - ep2, -o2::constants::math::PI)); registry.fill(HIST("h2_rholocal_cent"), centrality, rholocal, 1.0); registry.fill(HIST("h2_averagerho_cent"), centrality, collision.rho(), 1.0); registry.fill(HIST("h2_centrality_pT"), jet.pt(), centrality, 1.0); - - registry.fill(HIST("h2_phi_rholocal"), jet.phi() - ep2, rholocal, 1.0); - registry.fill(HIST("h2_phi_rholocal_absDelta"), absDelta, rholocal, 1.0); - registry.fill(HIST("h2_phi_averagerho_absDelta"), absDelta, collision.rho(), 1.0); - + registry.fill(HIST("h2_phi_rholocal"), phiMinusPsi2, rholocal, 1.0); registry.fill(HIST("h2_rholocal_pt"), jet.pt(), rholocal, 1.0); registry.fill(HIST("h2_rholocal_raw_pt"), jet.pt() - rholocal * jet.area(), rholocal, 1.0); registry.fill(HIST("h2_averagerho_pt"), jet.pt(), collision.rho(), 1.0); registry.fill(HIST("h2_averagerho_raw_pt"), jet.pt() - collision.rho() * jet.area(), collision.rho(), 1.0); - int lowPtCut = 20; - int mediumPtCut = 40; - int highPtCut = 70; - int highPtCutEnd = 100; - - if (jet.pt() >= lowPtCut && jet.pt() < mediumPtCut) { - registry.fill(HIST("h2_phi_rholocal_absDelta_low"), absDelta, rholocal, 1.0); - } else if (jet.pt() >= mediumPtCut && jet.pt() < highPtCut) { - registry.fill(HIST("h2_phi_rholocal_absDelta_medium"), absDelta, rholocal, 1.0); - } else if (jet.pt() >= highPtCut && jet.pt() < highPtCutEnd) { - registry.fill(HIST("h2_phi_rholocal_absDelta_high"), absDelta, rholocal, 1.0); + if (checkAbsDeltaPhi) { + int lowPtCut = 20; + int mediumPtCut = 40; + int highPtCut = 70; + int highPtCutEnd = 100; + registry.fill(HIST("h2_phi_rholocal_absDelta"), absDelta, rholocal, 1.0); + registry.fill(HIST("h2_phi_averagerho_absDelta"), absDelta, collision.rho(), 1.0); + if (jet.pt() >= lowPtCut && jet.pt() < mediumPtCut) { + registry.fill(HIST("h2_phi_rholocal_absDelta_low"), absDelta, rholocal, 1.0); + } else if (jet.pt() >= mediumPtCut && jet.pt() < highPtCut) { + registry.fill(HIST("h2_phi_rholocal_absDelta_medium"), absDelta, rholocal, 1.0); + } else if (jet.pt() >= highPtCut && jet.pt() < highPtCutEnd) { + registry.fill(HIST("h2_phi_rholocal_absDelta_high"), absDelta, rholocal, 1.0); + } } registry.fill(HIST("h_jet_pt_inclusive_v2"), jet.pt() - (collision.rho() * jet.area()), 1.0); registry.fill(HIST("h_jet_pt_inclusive_v2_rho"), jet.pt() - (rholocal * jet.area()), 1.0); + if (fillTHnSparse && sparseSubsample > 0 && (jet.globalIndex() % sparseSubsample) == 0) { + const std::array qaJet = {jet.pt(), jet.eta(), jet.phi(), jet.area(), static_cast(jet.tracksIds().size()), centrality}; + hJetQA->Fill(qaJet.data(), 1.0); + const std::array rhoMedianData = {centrality, jet.pt(), collision.rho(), collision.rho() * jet.area(), phiMinusPsi2, jet.area()}; + hRhoMedian->Fill(rhoMedianData.data(), 1.0); + const std::array rhoLocalData = {centrality, jet.pt(), rholocal, rholocal * jet.area(), phiMinusPsi2, jet.area()}; + hRhoLocal->Fill(rhoLocalData.data(), 1.0); + } if (jet.pt() > bumpLow && jet.pt() < bumpUp) { histosQA.fill(HIST("h2_ep2_FT0C_FT0M_bumpRegion"), helperEP.GetEventPlane(collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], nmode), helperEP.GetEventPlane(collision.qvecRe()[detIndFT0M + 3], collision.qvecIm()[detIndFT0M + 3], nmode), collision.cent()); } - // if ((absDelta < o2::constants::math::PIQuarter) || (absDelta >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absDelta >= evtPlnAngleB * o2::constants::math::PIQuarter && absDelta < evtPlnAngleC * o2::constants::math::PIQuarter)) { - if (absDelta < o2::constants::math::PIQuarter || absDelta >= evtPlnAngleA * o2::constants::math::PIQuarter) { + int lowPtCut = 20; + int mediumPtCut = 40; + int highPtCut = 70; + int highPtCutEnd = 100; + if ((phiMinusPsi2 < o2::constants::math::PIQuarter) || (phiMinusPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (phiMinusPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && phiMinusPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { registry.fill(HIST("h_jet_pt_in_plane_v2"), jet.pt() - (collision.rho() * jet.area()), 1.0); registry.fill(HIST("h2_centrality_jet_pt_in_plane_v2"), centrality, jet.pt() - (collision.rho() * jet.area()), 1.0); - registry.fill(HIST("h_jet_pt_in_plane_v2_rho"), jet.pt() - (rholocal * jet.area()), 1.0); registry.fill(HIST("h2_centrality_jet_pt_in_plane_v2_rho"), centrality, jet.pt() - (rholocal * jet.area()), 1.0); - registry.fill(HIST("h2_phi_rholocal_absDelta_low_inplane"), absDelta, rholocal, 1.0); - registry.fill(HIST("h2_phi_rholocal_absDelta_medium_inplane"), absDelta, rholocal, 1.0); - registry.fill(HIST("h2_phi_rholocal_absDelta_high_inplane"), absDelta, rholocal, 1.0); + if (jet.pt() >= lowPtCut && jet.pt() < mediumPtCut) { + registry.fill(HIST("h2_phi_rholocal_Delta_low_inplane"), phiMinusPsi2, rholocal, 1.0); + } else if (jet.pt() >= mediumPtCut && jet.pt() < highPtCut) { + registry.fill(HIST("h2_phi_rholocal_Delta_medium_inplane"), phiMinusPsi2, rholocal, 1.0); + } else if (jet.pt() >= highPtCut && jet.pt() < highPtCutEnd) { + registry.fill(HIST("h2_phi_rholocal_Delta_high_inplane"), phiMinusPsi2, rholocal, 1.0); + } registry.fill(HIST("h2_averagerho_pt_inplane"), jet.pt(), collision.rho(), 1.0); registry.fill(HIST("h2_rholocal_pt_inplane"), jet.pt(), rholocal, 1.0); + registry.fill(HIST("h2_rho_cent_median_in"), centrality, collision.rho(), 1.0); + registry.fill(HIST("h2_rho_cent_local_in"), centrality, rholocal, 1.0); } else { registry.fill(HIST("h_jet_pt_out_of_plane_v2"), jet.pt() - (collision.rho() * jet.area()), 1.0); registry.fill(HIST("h2_centrality_jet_pt_out_of_plane_v2"), centrality, jet.pt() - (collision.rho() * jet.area()), 1.0); @@ -1679,12 +1879,27 @@ struct JetChargedV2 { registry.fill(HIST("h_jet_pt_out_of_plane_v2_rho"), jet.pt() - (rholocal * jet.area()), 1.0); registry.fill(HIST("h2_centrality_jet_pt_out_of_plane_v2_rho"), centrality, jet.pt() - (rholocal * jet.area()), 1.0); - registry.fill(HIST("h2_phi_rholocal_absDelta_low_outplane"), absDelta, rholocal, 1.0); - registry.fill(HIST("h2_phi_rholocal_absDelta_medium_outplane"), absDelta, rholocal, 1.0); - registry.fill(HIST("h2_phi_rholocal_absDelta_high_outplane"), absDelta, rholocal, 1.0); + if (jet.pt() >= lowPtCut && jet.pt() < mediumPtCut) { + registry.fill(HIST("h2_phi_rholocal_Delta_low_outplane"), phiMinusPsi2, rholocal, 1.0); + } else if (jet.pt() >= mediumPtCut && jet.pt() < highPtCut) { + registry.fill(HIST("h2_phi_rholocal_Delta_medium_outplane"), phiMinusPsi2, rholocal, 1.0); + } else if (jet.pt() >= highPtCut && jet.pt() < highPtCutEnd) { + registry.fill(HIST("h2_phi_rholocal_Delta_high_outplane"), phiMinusPsi2, rholocal, 1.0); + } registry.fill(HIST("h2_averagerho_pt_outplane"), jet.pt(), collision.rho(), 1.0); registry.fill(HIST("h2_rholocal_pt_outplane"), jet.pt(), rholocal, 1.0); + registry.fill(HIST("h2_rho_cent_median_out"), centrality, collision.rho(), 1.0); + registry.fill(HIST("h2_rho_cent_local_out"), centrality, rholocal, 1.0); + } + if (checkAbsDeltaPhi) { + if ((absDelta < o2::constants::math::PIQuarter) || (absDelta >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absDelta >= evtPlnAngleB * o2::constants::math::PIQuarter && absDelta < evtPlnAngleC * o2::constants::math::PIQuarter)) { + registry.fill(HIST("h_jet_pt_in_plane_v2_abs"), jet.pt() - (collision.rho() * jet.area()), 1.0); + registry.fill(HIST("h_jet_pt_in_plane_v2_rho_abs"), jet.pt() - (rholocal * jet.area()), 1.0); + } else { + registry.fill(HIST("h_jet_pt_out_of_plane_v2_abs"), jet.pt() - (collision.rho() * jet.area()), 1.0); + registry.fill(HIST("h_jet_pt_out_of_plane_v2_rho_abs"), jet.pt() - (rholocal * jet.area()), 1.0); + } } } else if (nmode == cfgNmodB) { double phiMinusPsi3 = 0.0; @@ -1692,12 +1907,10 @@ struct JetChargedV2 { continue; } ep3 = helperEP.GetEventPlane(collision.qvecRe()[detInd], collision.qvecIm()[detInd], nmode); - // phiMinusPsi3 = jet.phi() - ep3; phiMinusPsi3 = RecoDecay::constrainAngle(jet.phi() - ep3, -o2::constants::math::PI); float absDelta3 = std::abs(phiMinusPsi3); - // if ((absDelta3 < o2::constants::math::PIQuarter) || (absDelta3 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absDelta3 >= evtPlnAngleB * o2::constants::math::PIQuarter && absDelta3 < evtPlnAngleC * o2::constants::math::PIQuarter)) { - if (absDelta3 < o2::constants::math::PIQuarter || absDelta3 >= evtPlnAngleA * o2::constants::math::PIQuarter) { + if ((absDelta3 < o2::constants::math::PIQuarter) || (absDelta3 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absDelta3 >= evtPlnAngleB * o2::constants::math::PIQuarter && absDelta3 < evtPlnAngleC * o2::constants::math::PIQuarter)) { registry.fill(HIST("h_jet_pt_in_plane_v3"), jet.pt() - (collision.rho() * jet.area()), 1.0); registry.fill(HIST("h_jet_pt_in_plane_v3_rho"), jet.pt() - (rholocal * jet.area()), 1.0); } else { @@ -1708,43 +1921,59 @@ struct JetChargedV2 { } } // RCpT - for (uint i = 0; i < cfgnMods->size(); i++) { + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { float randomConeEta = randomNumber.Uniform(trackEtaMin + randomConeR, trackEtaMax - randomConeR); float randomConePhi = randomNumber.Uniform(0.0, o2::constants::math::TwoPI); float randomConePt = 0; double integralValueRC = 0.0; double rholocalRC = 0.0; - integralValueRC = fFitModulationV2v3->Integral(randomConePhi - randomConeR, randomConePhi + randomConeR); + integralValueRC = getRhoLocalIntegral(fFitModulationV2v3, randomConePhi, randomConeR, rhoFitRangeMargin); rholocalRC = collision.rho() / (2 * randomConeR * temppara[0]) * integralValueRC; - int nmode = cfgnMods->at(i); + int nmode = epQvec.cfgnMods->at(i); if (nmode == cfgNmodA) { double rcPhiPsi2 = 0.0; - // double rcPhiPsi2Rand = 0.0; float absRcPhiPsi2 = 0.0; + rcPhiPsi2 = RecoDecay::constrainAngle(randomConePhi - ep2, 0.0); + // folded |Delta phi| in [0, pi], only used by the checkAbsDeltaPhi cross-check + absRcPhiPsi2 = std::abs(RecoDecay::constrainAngle(randomConePhi - ep2, -o2::constants::math::PI)); - // rcPhiPsi2 = randomConePhi - ep2; - rcPhiPsi2 = RecoDecay::constrainAngle(randomConePhi - ep2, -o2::constants::math::PI); - absRcPhiPsi2 = std::abs(rcPhiPsi2); - + int nTracksRandomCone = 0; for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, -o2::constants::math::PI); float dEta = track.eta() - randomConeEta; if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { randomConePt += track.pt(); + ++nTracksRandomCone; } } } - // registry.fill(HIST("h1_distribution_RC"), rcPhiPsi2); - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphi"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, rcPhiPsi2, 1.0); - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphi_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, absRcPhiPsi2, 1.0); - if (absRcPhiPsi2 < o2::constants::math::PIQuarter || absRcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) { - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphi_abs_in"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, absRcPhiPsi2, 1.0); + if (fillFluctH3) { + registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphi"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, rcPhiPsi2, 1.0); + } + + if ((rcPhiPsi2 < o2::constants::math::PIQuarter) || (rcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (rcPhiPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && rcPhiPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_in"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); } else { - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphi_abs_out"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, absRcPhiPsi2, 1.0); + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_out"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + if (fillTHnSparse && sparseSubsample > 0 && (collision.globalIndex() % sparseSubsample) == 0) { + const std::array qaFluct = {centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, + std::fabs(rcPhiPsi2), o2::constants::math::PI * randomConeR * randomConeR, rholocalRC, + static_cast(nTracksRandomCone)}; + hFluctQA->Fill(qaFluct.data(), 1.0); + } + } + if (checkAbsDeltaPhi) { + if ((absRcPhiPsi2 < o2::constants::math::PIQuarter) || (absRcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absRcPhiPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && absRcPhiPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_in_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + } else { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_out_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + } } + // removing the leading jet from the random cone + int nTracksRandomConeWoLeadingJet = 0; if (jets.size() > 0) { // if there are no jets in the acceptance (from the jetfinder cuts) then there can be no leading jet float dPhiLeadingJet = RecoDecay::constrainAngle(leadingJetPhi - randomConePhi, -o2::constants::math::PI); float dEtaLeadingJet = leadingJetEta - randomConeEta; @@ -1759,10 +1988,10 @@ struct JetChargedV2 { } if (jetWasInCone) { randomConePt = 0.0; - integralValueRC = fFitModulationV2v3->Integral(randomConePhi - randomConeR, randomConePhi + randomConeR); + integralValueRC = getRhoLocalIntegral(fFitModulationV2v3, randomConePhi, randomConeR, rhoFitRangeMargin); rholocalRC = collision.rho() / (2 * randomConeR * temppara[0]) * integralValueRC; - rcPhiPsi2 = RecoDecay::constrainAngle(randomConePhi - ep2, -o2::constants::math::PI); - absRcPhiPsi2 = std::abs(rcPhiPsi2); + rcPhiPsi2 = RecoDecay::constrainAngle(randomConePhi - ep2, 0.0); + absRcPhiPsi2 = std::abs(RecoDecay::constrainAngle(randomConePhi - ep2, -o2::constants::math::PI)); for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { // if track selection is uniformTrack, dcaXY and dcaZ cuts need to be added as they aren't in the selection so that they can be studied here @@ -1770,24 +1999,40 @@ struct JetChargedV2 { float dEta = track.eta() - randomConeEta; if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { randomConePt += track.pt(); + ++nTracksRandomConeWoLeadingJet; } } } } } - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, rcPhiPsi2, 1.0); - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), rcPhiPsi2, 1.0); - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, absRcPhiPsi2, 1.0); - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), absRcPhiPsi2, 1.0); - - if (absRcPhiPsi2 < o2::constants::math::PIQuarter || absRcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) { - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_abs_in"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, absRcPhiPsi2, 1.0); - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_abs_in"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), absRcPhiPsi2, 1.0); + if (fillFluctH3) { + registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, rcPhiPsi2, 1.0); + registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), rcPhiPsi2, 1.0); + } + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), 1.0); + if ((rcPhiPsi2 < o2::constants::math::PIQuarter) || (rcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (rcPhiPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && rcPhiPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_in"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_in"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), 1.0); } else { - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_abs_out"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, absRcPhiPsi2, 1.0); - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_abs_out"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), absRcPhiPsi2, 1.0); + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_out"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_out"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), 1.0); + } + if (fillTHnSparse && sparseSubsample > 0 && (collision.globalIndex() % sparseSubsample) == 0) { + const std::array qaFluctWo = {centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, + std::fabs(rcPhiPsi2), o2::constants::math::PI * randomConeR * randomConeR, rholocalRC, + static_cast(nTracksRandomConeWoLeadingJet)}; + hFluctQAwoLeadingJet->Fill(qaFluctWo.data(), 1.0); + } + if (checkAbsDeltaPhi) { + if ((absRcPhiPsi2 < o2::constants::math::PIQuarter) || (absRcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absRcPhiPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && absRcPhiPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_in_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_in_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), 1.0); + } else { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_out_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_out_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), 1.0); + } } - } else if (nmode == cfgNmodB) { continue; } @@ -1883,9 +2128,9 @@ struct JetChargedV2 { int evtPlnAngleA = 7; int evtPlnAngleB = 3; int evtPlnAngleC = 5; - for (uint i = 0; i < cfgnMods->size(); i++) { - int nmode = cfgnMods->at(i); - int detInd = detId * 4 + cfgnTotalSystem * 4 * (nmode - 2); + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { + int nmode = epQvec.cfgnMods->at(i); + int detInd = detId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); if (nmode == cfgNmodA) { if (collision.qvecAmp()[detId] > collQvecAmpDetId) { ep2 = helperEP.GetEventPlane(collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], nmode); @@ -1937,47 +2182,40 @@ struct JetChargedV2 { registry.fill(HIST("h_v3obs_centrality"), centrality, temppara[3]); registry.fill(HIST("h_evtnum_centrlity"), evtnum, centrality); - int nDF = 1; - int numOfFreePara = 2; - nDF = static_cast(fFitModulationV2v3->GetXaxis()->GetNbins()) - numOfFreePara; - if (nDF == 0 || static_cast(nDF) <= 0.) { - return; - } + const int numOfFreePara = 3; // psi2/psi3 are fixed with FixParameter -> [0], [1], [3] are free + int nBinsUsed = 0; double chi2 = 0.; for (int i = 0; i < hPtsumSumptFit->GetXaxis()->GetNbins(); i++) { if (hPtsumSumptFit->GetBinContent(i + 1) <= 0.) { - continue; + continue; // empty bins do not enter the chi2 and must not count in the dof } + nBinsUsed++; chi2 += std::pow((hPtsumSumptFit->GetBinContent(i + 1) - fFitModulationV2v3->Eval(hPtsumSumptFit->GetXaxis()->GetBinCenter(1 + i))), 2) / hPtsumSumptFit->GetBinContent(i + 1); } - - double chiSqr = 999.; - double cDF = 1.; - - chiSqr = chi2; - cDF = 1. - chiSquareCDF(nDF, chiSqr); + const int nDF = nBinsUsed - numOfFreePara; // number of fitted points minus the free parameters int evtCentAreaMin = 0; int evtCentAreaMax = 5; int evtMidAreaMin = 30; int evtMidAreaMax = 50; - if (cfgChkFitQuality) { + if (nDF > 0 && cfgChkFitQuality) { + const double chiSqr = chi2; + const double cDF = 1. - chiSquareCDF(nDF, chiSqr); registry.fill(HIST("h_PvalueCDF_CombinFit"), cDF); registry.fill(HIST("h2_PvalueCDFCent_CombinFit"), centrality, cDF); - registry.fill(HIST("h2_Chi2Cent_CombinFit"), centrality, chiSqr / (static_cast(nDF))); - registry.fill(HIST("h2_PChi2_CombinFit"), cDF, chiSqr / (static_cast(nDF))); + registry.fill(HIST("h2_Chi2Cent_CombinFit"), centrality, chiSqr / static_cast(nDF)); + registry.fill(HIST("h2_PChi2_CombinFit"), cDF, chiSqr / static_cast(nDF)); if (centrality >= evtCentAreaMin && centrality <= evtCentAreaMax) { - registry.fill(HIST("h2_PChi2_CombinFitA"), cDF, chiSqr / (static_cast(nDF))); - + registry.fill(HIST("h2_PChi2_CombinFitA"), cDF, chiSqr / static_cast(nDF)); } else if (centrality >= evtMidAreaMin && centrality <= evtMidAreaMax) { - registry.fill(HIST("h2_PChi2_CombinFitB"), cDF, chiSqr / (static_cast(nDF))); + registry.fill(HIST("h2_PChi2_CombinFitB"), cDF, chiSqr / static_cast(nDF)); } } - for (uint i = 0; i < cfgnMods->size(); i++) { - int nmode = cfgnMods->at(i); - int detInd = detId * 4 + cfgnTotalSystem * 4 * (nmode - 2); + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { + int nmode = epQvec.cfgnMods->at(i); + int detInd = detId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); for (auto const& jet : jets) { if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { @@ -1992,7 +2230,7 @@ struct JetChargedV2 { registry.fill(HIST("h_mcd_pt_before_matching_mcdprocess"), corrBasejetpt); } - double integralValue = fFitModulationV2v3->Integral(jet.phi() - selectedJetsRadius, jet.phi() + selectedJetsRadius); + double integralValue = getRhoLocalIntegral(fFitModulationV2v3, jet.phi(), selectedJetsRadius, rhoFitRangeMargin); double rholocal = collision.rho() / (2 * selectedJetsRadius * temppara[0]) * integralValue; registry.fill(HIST("h2_rholocal_cent"), centrality, rholocal, 1.0); @@ -2002,12 +2240,12 @@ struct JetChargedV2 { continue; } // phiMinusPsi2 = jet.phi() - ep2; - phiMinusPsi2 = RecoDecay::constrainAngle(jet.phi() - ep2, -o2::constants::math::PI); - float absDelta = std::abs(phiMinusPsi2); + phiMinusPsi2 = RecoDecay::constrainAngle(jet.phi() - ep2, 0.0f); + // same in-/out-of-plane definition as in processSigmaPtData (unfolded phi - Psi2) registry.fill(HIST("h2_phi_rholocal"), jet.phi() - ep2, rholocal, 1.0); registry.fill(HIST("h_jet_pt_inclusive_v2_rho"), jet.pt() - (rholocal * jet.area()), 1.0); - if ((absDelta < o2::constants::math::PIQuarter) || (absDelta >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absDelta >= evtPlnAngleB * o2::constants::math::PIQuarter && absDelta < evtPlnAngleC * o2::constants::math::PIQuarter)) { + if ((phiMinusPsi2 < o2::constants::math::PIQuarter) || (phiMinusPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (phiMinusPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && phiMinusPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { registry.fill(HIST("h_jet_pt_in_plane_v2_rho"), jet.pt() - (rholocal * jet.area()), 1.0); registry.fill(HIST("h2_centrality_jet_pt_in_plane_v2_rho"), centrality, jet.pt() - (rholocal * jet.area()), 1.0); } else { @@ -2033,18 +2271,21 @@ struct JetChargedV2 { } } // RCpT - for (uint i = 0; i < cfgnMods->size(); i++) { + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { float randomConeEta = randomNumber.Uniform(trackEtaMin + randomConeR, trackEtaMax - randomConeR); float randomConePhi = randomNumber.Uniform(0.0, o2::constants::math::TwoPI); float randomConePt = 0; - double integralValueRC = fFitModulationV2v3->Integral(randomConePhi - randomConeR, randomConePhi + randomConeR); + double integralValueRC = getRhoLocalIntegral(fFitModulationV2v3, randomConePhi, randomConeR, rhoFitRangeMargin); double rholocalRC = collision.rho() / (2 * randomConeR * temppara[0]) * integralValueRC; - int nmode = cfgnMods->at(i); + int nmode = epQvec.cfgnMods->at(i); if (nmode == cfgNmodA) { double rcPhiPsi2 = 0.0; + float absRcPhiPsi2 = 0.0; // rcPhiPsi2 = randomConePhi - ep2; - rcPhiPsi2 = RecoDecay::constrainAngle(randomConePhi - ep2, -o2::constants::math::PI); + rcPhiPsi2 = RecoDecay::constrainAngle(randomConePhi - ep2, 0.0); + // folded |Delta phi| in [0, pi], only used by the checkAbsDeltaPhi cross-check + absRcPhiPsi2 = std::abs(RecoDecay::constrainAngle(randomConePhi - ep2, -o2::constants::math::PI)); for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { @@ -2055,7 +2296,21 @@ struct JetChargedV2 { } } } - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphi"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, rcPhiPsi2, 1.0); + if (fillFluctH3) { + registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphi"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, rcPhiPsi2, 1.0); + } + if ((rcPhiPsi2 < o2::constants::math::PIQuarter) || (rcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (rcPhiPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && rcPhiPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_in"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + } else { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_out"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + } + if (checkAbsDeltaPhi) { + if ((absRcPhiPsi2 < o2::constants::math::PIQuarter) || (absRcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absRcPhiPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && absRcPhiPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_in_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + } else { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_out_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + } + } // removing the leading jet from the random cone if (jets.size() > 0) { // if there are no jets in the acceptance (from the jetfinder cuts) then there can be no leading jet @@ -2075,6 +2330,10 @@ struct JetChargedV2 { } if (jetWasInCone) { randomConePt = 0.0; + integralValueRC = getRhoLocalIntegral(fFitModulationV2v3, randomConePhi, randomConeR, rhoFitRangeMargin); + rholocalRC = collision.rho() / (2 * randomConeR * temppara[0]) * integralValueRC; + rcPhiPsi2 = RecoDecay::constrainAngle(randomConePhi - ep2, 0.0); + absRcPhiPsi2 = std::abs(RecoDecay::constrainAngle(randomConePhi - ep2, -o2::constants::math::PI)); for (auto const& track : tracks) { // if (jetderiveddatautilities::selectTrack(track, trackSelection) && (std::fabs(track.eta() - leadingJetEta) > randomConeR)) { // if track selection is uniformTrack, dcaXY and dcaZ cuts need to be added as they aren't in the selection so that they can be studied here // float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, -o2::constants::math::PI); @@ -2093,8 +2352,26 @@ struct JetChargedV2 { } } } - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, rcPhiPsi2, 1.0); - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), rcPhiPsi2, 1.0); + if (fillFluctH3) { + registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, rcPhiPsi2, 1.0); + registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), rcPhiPsi2, 1.0); + } + if ((rcPhiPsi2 < o2::constants::math::PIQuarter) || (rcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (rcPhiPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && rcPhiPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_in"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_in"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), 1.0); + } else { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_out"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_out"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), 1.0); + } + if (checkAbsDeltaPhi) { + if ((absRcPhiPsi2 < o2::constants::math::PIQuarter) || (absRcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absRcPhiPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && absRcPhiPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_in_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_in_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), 1.0); + } else { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_out_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_out_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), 1.0); + } + } } else if (nmode == cfgNmodB) { continue; } @@ -2281,9 +2558,9 @@ struct JetChargedV2 { int cfgNmodA = 2; int cfgNmodB = 3; - for (uint i = 0; i < cfgnMods->size(); i++) { - int nmode = cfgnMods->at(i); - int detInd = detId * 4 + cfgnTotalSystem * 4 * (nmode - 2); + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { + int nmode = epQvec.cfgnMods->at(i); + int detInd = detId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); if (nmode == cfgNmodA) { if (collision.qvecAmp()[detId] > collQvecAmpDetId) { ep2 = helperEP.GetEventPlane(collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], nmode); diff --git a/PWGJE/Tasks/jetCorrelationD0.cxx b/PWGJE/Tasks/jetCorrelationD0.cxx index eee51eb4ab4..c5ffa65bdd0 100644 --- a/PWGJE/Tasks/jetCorrelationD0.cxx +++ b/PWGJE/Tasks/jetCorrelationD0.cxx @@ -254,7 +254,7 @@ struct JetCorrelationD0 { } void init(InitContext const&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); // General Axes AxisSpec axisEta = {100, -1.0, 1.0, "#eta"}; AxisSpec axisPhi = {100, 0.0, o2::constants::math::TwoPI, "#phi"}; diff --git a/PWGJE/Tasks/jetCorrelationDmeson.cxx b/PWGJE/Tasks/jetCorrelationDmeson.cxx index 1a1b2406a85..666b88ab04b 100644 --- a/PWGJE/Tasks/jetCorrelationDmeson.cxx +++ b/PWGJE/Tasks/jetCorrelationDmeson.cxx @@ -280,7 +280,7 @@ struct JetCorrelationDmeson { void init(InitContext const&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); // General Axes AxisSpec axisEta = {100, -1.0, 1.0, "#eta"}; AxisSpec axisPhi = {100, 0.0, o2::constants::math::TwoPI, "#phi"}; diff --git a/PWGJE/Tasks/jetCrossSectionEfficiency.cxx b/PWGJE/Tasks/jetCrossSectionEfficiency.cxx index 9bcc1195454..35e3693f59b 100644 --- a/PWGJE/Tasks/jetCrossSectionEfficiency.cxx +++ b/PWGJE/Tasks/jetCrossSectionEfficiency.cxx @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -43,13 +44,20 @@ #include #include #include +#include +#include +#include #include #include #include #include #include +#include #include +#include +#include +#include #include using namespace o2; @@ -101,6 +109,7 @@ struct JetCrossSectionEfficiency { Configurable truthFT0CActivityThresholdTimeRange{"truthFT0CActivityThresholdTimeRange", 8000.0f, "multFT0C truth proxy threshold for NoCollInTimeRangeStandard; reco uses FT0C amplitude, so validate or tune this value"}; Configurable truthFT0CActivityThresholdROF{"truthFT0CActivityThresholdROF", 5000.0f, "multFT0C truth proxy threshold for NoCollInRofStandard; reco uses FT0C amplitude, so validate or tune this value"}; Configurable truthRofCloseVzMax{"truthRofCloseVzMax", 0.3f, "maximum truth |delta z| in cm for the NoCollInRofStandard track-presence proxy"}; + Configurable tvxEnvironmentRequireOtherBcBaseline{"tvxEnvironmentRequireOtherBcBaseline", false, "require RCT, TVX, NoTFB and NoITSROFB for other BCs in the TVX-BC environment QA"}; o2::aod::rctsel::RCTFlagsChecker rctChecker; uint64_t rctMask = 0; @@ -143,6 +152,16 @@ struct JetCrossSectionEfficiency { static constexpr float ConfigSwitchHigh = 9998.0f; static constexpr float BrokenPtHardSentinel = 1.0f; static constexpr int MinITSClustersForOccupancy = 5; + static constexpr int NumberOfHasCollSelectionConditionDiagnostics = 5; + static constexpr int HasCollQaMaxCount = 500; + static constexpr int HasCollQaMaxDeltaGlobalBC = 100000; + static constexpr int64_t NoOtherTruthCollision = -1; + + static std::vector getNearestAbsDeltaGlobalBCEdges() + { + // Keep the no-other-BC sentinel separate, then resolve the near-BC region most relevant for reconstruction losses. + return {-1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 7.5, 15.5, 31.5, 63.5, 127.5, 255.5, 511.5, 1000.5, 2000.5, 4000.5, 8000.5, 16000.5, 32000.5, 64000.5, static_cast(HasCollQaMaxDeltaGlobalBC) + 0.5}; + } struct TruthPbPbSelections { bool valid = false; @@ -150,12 +169,44 @@ struct JetCrossSectionEfficiency { bool noCollInRofStandard = false; }; + struct TruthHasCollEnvironment { + bool valid = false; + int nOtherSameTruthRof = 0; + int64_t nearestAbsDeltaGlobalBC = NoOtherTruthCollision; + }; + + struct VisibleBCEnvironment { + bool valid = false; + int nOtherTVXSameRof = 0; + int64_t nearestAbsDeltaTVXGlobalBC = NoOtherTruthCollision; + int64_t originalBCIndex = -1; + }; + + struct VisibleBCEnvironmentCache { + std::map byBCIndex; + std::map, VisibleBCEnvironment> byRunAndGlobalBC; + }; + using JetCollisionsMCDWithParent = soa::Join; + using JMcCollisionsWithParent = soa::Join; + using JetCollisionsWithParent = soa::Join; using CollisionsWithEvSels = soa::Join; + using OriginalBCsWithSels = soa::Join; + using OriginalCollisionsWithMcLabels = soa::Join; using FullTracksIU = soa::Join; + using FullTracksIUWithMcLabels = soa::Join; + using ChargedMCDJetsWithConstituents = soa::Join; + using ChargedMCPJetsWithConstituents = soa::Join; Partition pvTracks = ((aod::track::flags & static_cast(o2::aod::track::PVContributor)) == static_cast(o2::aod::track::PVContributor)); Preslice pvTracksPerCollision = aod::track::collisionId; + PresliceUnsorted collisionsPerFoundBC = aod::evsel::foundBCId; + PresliceUnsorted jetCollisionsPerMcCollision = aod::jmccollisionlb::mcCollisionId; + Preslice mcpJetsPerMcCollision = aod::jet::mcCollisionId; + Preslice mcParticlesPerMcCollision = aod::mcparticle::mcCollisionId; + PresliceUnsorted recoTracksPerMcParticle = aod::mctracklabel::mcParticleId; + PresliceUnsorted jetCollisionsPerOriginalCollision = aod::jcollision::collisionId; + Preslice detectorJetsPerJetCollision = aod::jet::collisionId; enum EventSelectionPreset { PresetSelTvx = 0, @@ -203,7 +254,7 @@ struct JetCrossSectionEfficiency { rctChecker.init(static_cast(rctSelectionsLabel)); rctMask = rctChecker.value(); - switch (getEventSelectionPreset(static_cast(eventSelections))) { + switch (getEventSelectionPreset(eventSelections.value)) { case PresetSelTvx: applyTFB = false; applyROFB = false; @@ -238,11 +289,28 @@ struct JetCrossSectionEfficiency { isSel8FullPbPb = true; break; default: - LOGF(fatal, "Configurable eventSelections=%s not supported; use selTVX, selMC, selMCFull, sel8, sel8Full, or sel8FullPbPb", static_cast(eventSelections).c_str()); + LOGF(fatal, "Configurable eventSelections=%s not supported; use selTVX, selMC, selMCFull, sel8, sel8Full, or sel8FullPbPb", eventSelections.value.c_str()); break; } - if (doprocessCrossSectionEfficiencyBcBitsFirst && isSel8FullPbPb && + const bool isDataTVXBCEnvironmentQAEnabled = doprocessDataTVXBCEnvironmentQA; + const bool isMCQAEnabled = doprocessMCTruthMultFT0CvsRecoAmplitude || + doprocessMCTruthMultFT0CvsRecoITS567 || + doprocessMCEnvironmentQA || + doprocessMCHasCollTrackFateQA || + doprocessMCHasCollJetSurvivalQA || + doprocessMCHasCollJetEnvironmentQA || + doprocessCrossSectionEfficiency || + doprocessCrossSectionEfficiencyBcBitsFirst; + if (isDataTVXBCEnvironmentQAEnabled && isMCQAEnabled) { + LOGF(fatal, "processDataTVXBCEnvironmentQA cannot run with MC-only QA or cross-section-efficiency processes; enable the data process alone for data AO2Ds"); + } + + const bool needsRofParameters = (doprocessCrossSectionEfficiencyBcBitsFirst && isSel8FullPbPb) || + doprocessMCEnvironmentQA || + doprocessMCHasCollJetEnvironmentQA || + doprocessDataTVXBCEnvironmentQA; + if (needsRofParameters && (truthRofOffsetInBC < 0 || truthRofLengthInBC <= 0)) { ccdb->setURL(static_cast(truthRofCcdbUrl)); ccdb->setCaching(true); @@ -304,6 +372,8 @@ struct JetCrossSectionEfficiency { if (isSel8FullPbPb) { AxisSpec axisPbPbTruthSelectionOnly = {PbPbTruthSelectionOnlyNBins, 0.5, static_cast(PbPbTruthSelectionOnlyNBins) + 0.5, "event selection (PbPb truth-only)"}; + AxisSpec selectionConditionAxis = {NumberOfHasCollSelectionConditionDiagnostics, 0.5, static_cast(NumberOfHasCollSelectionConditionDiagnostics) + 0.5, "selection condition"}; + AxisSpec hasCollSelectionStateAxis = {4, 0.5, 4.5, "hasColl and selection-condition state"}; std::vector timeRangeOnlyLabels = {"INEL", "+RCT_pass", "+kTVX(truth)", "+kNoTFB(truth)", "+kNoITSROFB(truth)", "+kNoCollInTimeRangeStandard(truth)", "+hasColl", "+|zReco|<10", "+noSplit"}; std::vector rofOnlyLabels = {"INEL", "+RCT_pass", "+kTVX(truth)", "+kNoTFB(truth)", "+kNoITSROFB(truth)", "+kNoCollInRofStandard(truth)", "+hasColl", "+|zReco|<10", "+noSplit"}; @@ -324,10 +394,23 @@ struct JetCrossSectionEfficiency { {HistType::kTH1F, {axisPbPbTruthSelectionOnly}}); setAxisLabels(registry.get(HIST("h2_jet_pt_part_eventselection_bcBitsFirst_rofOnly_truth"))->GetYaxis(), rofOnlyLabels); setAxisLabels(registry.get(HIST("h_mccollisions_eventselection_bcBitsFirst_rofOnly_truth"))->GetXaxis(), rofOnlyLabels); + + registry.add("h2_mccollisions_selection_bit_vs_has_coll_state", + "weighted MC collisions after common RCT+TVX baseline with valid PbPb truth-proxy evaluation;selection condition;hasColl and selection-condition state", + {HistType::kTH2F, {selectionConditionAxis, hasCollSelectionStateAxis}}); + registry.add("h2_mcpjets_selection_bit_vs_has_coll_state", + "weighted selected MCP jets after common RCT+TVX baseline with valid PbPb truth-proxy evaluation;selection condition;hasColl and selection-condition state", + {HistType::kTH2F, {selectionConditionAxis, hasCollSelectionStateAxis}}); + std::vector selectionConditionLabels = {"kNoTimeFrameBorder", "kNoITSROFrameBorder", "kNoSameBunchPileup (truth proxy)", "kNoCollInTimeRangeStandard (truth proxy)", "kNoCollInRofStandard (truth proxy)"}; + std::vector hasCollSelectionStateLabels = {"noColl, fail", "noColl, pass", "hasColl, fail", "hasColl, pass"}; + setAxisLabels(registry.get(HIST("h2_mccollisions_selection_bit_vs_has_coll_state"))->GetXaxis(), selectionConditionLabels); + setAxisLabels(registry.get(HIST("h2_mccollisions_selection_bit_vs_has_coll_state"))->GetYaxis(), hasCollSelectionStateLabels); + setAxisLabels(registry.get(HIST("h2_mcpjets_selection_bit_vs_has_coll_state"))->GetXaxis(), selectionConditionLabels); + setAxisLabels(registry.get(HIST("h2_mcpjets_selection_bit_vs_has_coll_state"))->GetYaxis(), hasCollSelectionStateLabels); } } - if (doprocessTruthMultFT0CvsRecoAmplitude) { + if (doprocessMCTruthMultFT0CvsRecoAmplitude) { AxisSpec truthMultFT0CAxis = {500, -0.5, 499.5, "truth multFT0C"}; AxisSpec recoFT0CAmplitudeAxis = {1000, 0., 20000., "A_{FT0C}^{reco}"}; registry.add("h2_mccollision_mult_ft0c_found_ft0_sum_amp_c", @@ -335,7 +418,7 @@ struct JetCrossSectionEfficiency { {HistType::kTH2F, {truthMultFT0CAxis, recoFT0CAmplitudeAxis}}); } - if (doprocessTruthMultFT0CvsRecoITS567) { + if (doprocessMCTruthMultFT0CvsRecoITS567) { AxisSpec truthMultFT0CAxis = {500, -0.5, 499.5, "truth multFT0C"}; AxisSpec recoNITS567Axis = {1000, -0.5, 999.5, "N_{ITS567}^{reco}"}; AxisSpec truthMultFT0CPositiveAxis = {2, -0.5, 1.5, "truth multFT0C > 0"}; @@ -352,6 +435,170 @@ struct JetCrossSectionEfficiency { binaryActivity->GetYaxis()->SetBinLabel(1, "N_{ITS567} = 0"); binaryActivity->GetYaxis()->SetBinLabel(2, "N_{ITS567} > 0"); } + + if (doprocessMCEnvironmentQA) { + AxisSpec nOtherSameTruthRofAxis = {HasCollQaMaxCount + 1, -0.5, static_cast(HasCollQaMaxCount) + 0.5, "N_{other same truth ROF}"}; + AxisSpec nearestAbsDeltaGlobalBCAxis = {getNearestAbsDeltaGlobalBCEdges(), "nearest |#Delta global BC|"}; + AxisSpec targetMultFT0CAxis = {500, -0.5, 499.5, "target truth multFT0C"}; + AxisSpec hasCollAxis = {2, -0.5, 1.5, "JetDerived hasColl"}; + + registry.add("h2_n_other_same_truth_rof_vs_has_coll", + "other MC collisions in the same truth ITS ROF vs hasColl;N_{other same truth ROF};JetDerived hasColl;weighted counts", + {HistType::kTH2F, {nOtherSameTruthRofAxis, hasCollAxis}}); + registry.add("h2_nearest_abs_delta_global_bc_vs_has_coll", + "nearest other MC collision |#Delta global BC| vs hasColl;nearest |#Delta global BC|;JetDerived hasColl;weighted counts", + {HistType::kTH2F, {nearestAbsDeltaGlobalBCAxis, hasCollAxis}}); + registry.add("h2_target_mult_ft0c_vs_has_coll", + "target truth multFT0C vs hasColl;target truth multFT0C;JetDerived hasColl;weighted counts", + {HistType::kTH2F, {targetMultFT0CAxis, hasCollAxis}}); + registry.add("h2_target_mult_ft0c_vs_n_other_same_truth_rof_all", + "target truth multFT0C vs other MC collisions in the same truth ITS ROF;target truth multFT0C;N_{other same truth ROF};weighted counts", + {HistType::kTH2F, {targetMultFT0CAxis, nOtherSameTruthRofAxis}}); + registry.add("h2_target_mult_ft0c_vs_n_other_same_truth_rof_has_coll", + "target truth multFT0C vs other MC collisions in the same truth ITS ROF for hasColl;target truth multFT0C;N_{other same truth ROF};weighted counts", + {HistType::kTH2F, {targetMultFT0CAxis, nOtherSameTruthRofAxis}}); + registry.add("h2_target_mult_ft0c_vs_nearest_abs_delta_global_bc_all", + "target truth multFT0C vs nearest other MC collision distance;target truth multFT0C;nearest |#Delta global BC|;weighted counts", + {HistType::kTH2F, {targetMultFT0CAxis, nearestAbsDeltaGlobalBCAxis}}); + registry.add("h2_target_mult_ft0c_vs_nearest_abs_delta_global_bc_has_coll", + "target truth multFT0C vs nearest other MC collision distance for hasColl;target truth multFT0C;nearest |#Delta global BC|;weighted counts", + {HistType::kTH2F, {targetMultFT0CAxis, nearestAbsDeltaGlobalBCAxis}}); + + auto nOtherSameTruthRof = registry.get(HIST("h2_n_other_same_truth_rof_vs_has_coll")); + nOtherSameTruthRof->GetYaxis()->SetBinLabel(1, "noColl"); + nOtherSameTruthRof->GetYaxis()->SetBinLabel(2, "hasColl"); + auto nearestAbsDeltaGlobalBC = registry.get(HIST("h2_nearest_abs_delta_global_bc_vs_has_coll")); + nearestAbsDeltaGlobalBC->GetXaxis()->SetBinLabel(1, "no other valid MC collision"); + nearestAbsDeltaGlobalBC->GetYaxis()->SetBinLabel(1, "noColl"); + nearestAbsDeltaGlobalBC->GetYaxis()->SetBinLabel(2, "hasColl"); + auto targetMultFT0C = registry.get(HIST("h2_target_mult_ft0c_vs_has_coll")); + targetMultFT0C->GetYaxis()->SetBinLabel(1, "noColl"); + targetMultFT0C->GetYaxis()->SetBinLabel(2, "hasColl"); + registry.get(HIST("h2_target_mult_ft0c_vs_nearest_abs_delta_global_bc_all"))->GetYaxis()->SetBinLabel(1, "no other valid MC collision"); + registry.get(HIST("h2_target_mult_ft0c_vs_nearest_abs_delta_global_bc_has_coll"))->GetYaxis()->SetBinLabel(1, "no other valid MC collision"); + } + + if (doprocessDataTVXBCEnvironmentQA || doprocessMCEnvironmentQA) { + AxisSpec nOtherTVXSameRofAxis = {HasCollQaMaxCount + 1, -0.5, static_cast(HasCollQaMaxCount) + 0.5, "N_{other TVX BCs in same ITS ROF}"}; + AxisSpec nearestAbsDeltaTVXGlobalBCEnvironmentAxis = {getNearestAbsDeltaGlobalBCEdges(), "nearest other TVX |#Delta global BC|"}; + AxisSpec hasRecoCollisionAtTargetBCAxis = {2, -0.5, 1.5, "reconstructed collision at target TVX BC"}; + AxisSpec targetFT0CAmplitudeAxis = {1000, 0., 20000., "A_{FT0C} at target TVX BC"}; + + registry.add("h2_n_other_tvx_same_rof_vs_nearest_abs_delta_global_bc_tvx", + "TVX-BC environment;N_{other TVX BCs in same ITS ROF};nearest other TVX |#Delta global BC|;BCs", + {HistType::kTH2F, {nOtherTVXSameRofAxis, nearestAbsDeltaTVXGlobalBCEnvironmentAxis}}); + registry.add("h2_n_other_tvx_same_rof_vs_nearest_abs_delta_global_bc_tvx_has_reco", + "TVX-BC environment for target BCs with a reconstructed collision;N_{other TVX BCs in same ITS ROF};nearest other TVX |#Delta global BC|;BCs", + {HistType::kTH2F, {nOtherTVXSameRofAxis, nearestAbsDeltaTVXGlobalBCEnvironmentAxis}}); + registry.add("h2_n_other_tvx_same_rof_vs_has_reco_collision", + "TVX-BC environment vs reconstructed-collision proxy;N_{other TVX BCs in same ITS ROF};reconstructed collision at target TVX BC;BCs", + {HistType::kTH2F, {nOtherTVXSameRofAxis, hasRecoCollisionAtTargetBCAxis}}); + registry.add("h2_nearest_abs_delta_global_bc_tvx_vs_has_reco_collision", + "TVX-BC environment vs reconstructed-collision proxy;nearest other TVX |#Delta global BC|;reconstructed collision at target TVX BC;BCs", + {HistType::kTH2F, {nearestAbsDeltaTVXGlobalBCEnvironmentAxis, hasRecoCollisionAtTargetBCAxis}}); + registry.add("h2_target_ft0c_amplitude_vs_n_other_tvx_same_rof", + "FT0C amplitude vs TVX-BC environment;A_{FT0C} at target TVX BC;N_{other TVX BCs in same ITS ROF};BCs", + {HistType::kTH2F, {targetFT0CAmplitudeAxis, nOtherTVXSameRofAxis}}); + + auto nOtherTVXSameRof = registry.get(HIST("h2_n_other_tvx_same_rof_vs_has_reco_collision")); + nOtherTVXSameRof->GetYaxis()->SetBinLabel(1, "no reco collision"); + nOtherTVXSameRof->GetYaxis()->SetBinLabel(2, "reco collision exists"); + auto nearestAbsDeltaTVXGlobalBC = registry.get(HIST("h2_nearest_abs_delta_global_bc_tvx_vs_has_reco_collision")); + nearestAbsDeltaTVXGlobalBC->GetXaxis()->SetBinLabel(1, "no other TVX BC in run"); + nearestAbsDeltaTVXGlobalBC->GetYaxis()->SetBinLabel(1, "no reco collision"); + nearestAbsDeltaTVXGlobalBC->GetYaxis()->SetBinLabel(2, "reco collision exists"); + registry.get(HIST("h2_n_other_tvx_same_rof_vs_nearest_abs_delta_global_bc_tvx"))->GetYaxis()->SetBinLabel(1, "no other TVX BC in run"); + registry.get(HIST("h2_n_other_tvx_same_rof_vs_nearest_abs_delta_global_bc_tvx_has_reco"))->GetYaxis()->SetBinLabel(1, "no other TVX BC in run"); + } + + if (doprocessMCEnvironmentQA) { + AxisSpec nOtherSameTruthRofAxis = {HasCollQaMaxCount + 1, -0.5, static_cast(HasCollQaMaxCount) + 0.5, "N_{other MC collisions in same truth ITS ROF}"}; + AxisSpec nOtherTVXSameRofAxis = {HasCollQaMaxCount + 1, -0.5, static_cast(HasCollQaMaxCount) + 0.5, "N_{other TVX BCs in same ITS ROF}"}; + AxisSpec nearestAbsDeltaGlobalBCEnvironmentAxis = {getNearestAbsDeltaGlobalBCEdges(), "nearest |#Delta global BC|"}; + AxisSpec hasRecoCollisionAtTargetBCAxis = {2, -0.5, 1.5, "reconstructed collision at target TVX BC"}; + AxisSpec hasCollAxis = {2, -0.5, 1.5, "JetDerived hasColl"}; + + registry.add("h2_n_other_truth_same_rof_vs_n_other_tvx_same_rof", + "truth and TVX-BC same-ROF environment;N_{other MC collisions in same truth ITS ROF};N_{other TVX BCs in same ITS ROF};weighted MC collisions", + {HistType::kTH2F, {nOtherSameTruthRofAxis, nOtherTVXSameRofAxis}}); + registry.add("h2_nearest_abs_delta_global_bc_truth_vs_nearest_abs_delta_global_bc_tvx", + "truth and TVX-BC nearest collision environment;truth nearest |#Delta global BC|;TVX nearest |#Delta global BC|;weighted MC collisions", + {HistType::kTH2F, {nearestAbsDeltaGlobalBCEnvironmentAxis, nearestAbsDeltaGlobalBCEnvironmentAxis}}); + registry.add("h2_reco_collision_proxy_vs_has_coll", + "reconstructed-collision proxy vs JetDerived hasColl;reconstructed collision at target TVX BC;JetDerived hasColl;weighted MC collisions", + {HistType::kTH2F, {hasRecoCollisionAtTargetBCAxis, hasCollAxis}}); + auto recoCollisionProxy = registry.get(HIST("h2_reco_collision_proxy_vs_has_coll")); + recoCollisionProxy->GetXaxis()->SetBinLabel(1, "no reco collision"); + recoCollisionProxy->GetXaxis()->SetBinLabel(2, "reco collision exists"); + recoCollisionProxy->GetYaxis()->SetBinLabel(1, "noColl"); + recoCollisionProxy->GetYaxis()->SetBinLabel(2, "hasColl"); + } + + if (doprocessMCHasCollJetEnvironmentQA) { + AxisSpec nOtherTVXSameRofAxis = {HasCollQaMaxCount + 1, -0.5, static_cast(HasCollQaMaxCount) + 0.5, "N_{other TVX BCs in same ITS ROF}"}; + AxisSpec nearestAbsDeltaTVXGlobalBCEnvironmentAxis = {getNearestAbsDeltaGlobalBCEdges(), "nearest other TVX |#Delta global BC|"}; + AxisSpec hasCollAxis = {2, -0.5, 1.5, "JetDerived hasColl"}; + registry.add("hs_mcp_jet_pt_tvx_environment_has_coll", + "selected MCP jets vs TVX-BC environment and hasColl;#it{p}_{T,jet}^{MCP} (GeV/#it{c});N_{other TVX BCs in same ITS ROF};nearest other TVX |#Delta global BC|;JetDerived hasColl;weighted jets", + {HistType::kTHnSparseF, {jetPtAxis, nOtherTVXSameRofAxis, nearestAbsDeltaTVXGlobalBCEnvironmentAxis, hasCollAxis}}, true); + auto jetEnvironment = registry.get(HIST("hs_mcp_jet_pt_tvx_environment_has_coll")); + jetEnvironment->GetAxis(3)->SetBinLabel(1, "noColl"); + jetEnvironment->GetAxis(3)->SetBinLabel(2, "hasColl"); + jetEnvironment->GetAxis(2)->SetBinLabel(1, "no other TVX BC in run"); + } + + if (doprocessMCHasCollTrackFateQA) { + AxisSpec trackFateCountAxis = {HasCollQaMaxCount + 1, -0.5, static_cast(HasCollQaMaxCount) + 0.5, "N_{target-origin reconstructed tracks}"}; + AxisSpec hasCollAxis = {2, -0.5, 1.5, "JetDerived hasColl"}; + registry.add("h2_n_reco_tracks_from_target_vs_has_coll", + "reconstructed tracks from target MC collision vs hasColl;N_{target-origin reconstructed tracks};JetDerived hasColl;weighted counts", + {HistType::kTH2F, {trackFateCountAxis, hasCollAxis}}); + registry.add("h2_n_unassigned_tracks_from_target_vs_has_coll", + "unassigned tracks from target MC collision vs hasColl;N_{target-origin unassigned tracks};JetDerived hasColl;weighted counts", + {HistType::kTH2F, {trackFateCountAxis, hasCollAxis}}); + registry.add("h2_n_target_tracks_attached_to_other_labeled_collision_vs_has_coll", + "target-origin tracks attached to other-labeled collision vs hasColl;N_{tracks attached to other-labeled collision};JetDerived hasColl;weighted counts", + {HistType::kTH2F, {trackFateCountAxis, hasCollAxis}}); + + auto setHasCollAxisLabels = [](const auto& histogram) { + histogram->GetYaxis()->SetBinLabel(1, "noColl"); + histogram->GetYaxis()->SetBinLabel(2, "hasColl"); + }; + setHasCollAxisLabels(registry.get(HIST("h2_n_reco_tracks_from_target_vs_has_coll"))); + setHasCollAxisLabels(registry.get(HIST("h2_n_unassigned_tracks_from_target_vs_has_coll"))); + setHasCollAxisLabels(registry.get(HIST("h2_n_target_tracks_attached_to_other_labeled_collision_vs_has_coll"))); + } + + if (doprocessMCHasCollJetSurvivalQA) { + AxisSpec detectorJetExistsAxis = {2, -0.5, 1.5, "detector jet with target-origin reco track"}; + AxisSpec constituentPtFractionAxis = {300, 0., 3., "p_{T}^{reco target tracks} / #Sigma p_{T}^{truth constituents}"}; + + registry.add("h2_mcp_jet_pt_vs_has_detector_jet_hasColl", + "MCP jet pT vs detector-level jet survival for hasColl;#it{p}_{T,jet}^{MCP} (GeV/#it{c});detector jet with target-origin reco track;weighted counts", + {HistType::kTH2F, {jetPtAxis, detectorJetExistsAxis}}); + registry.add("h2_mcp_jet_pt_vs_has_detector_jet_noColl", + "MCP jet pT vs detector-level jet survival for noColl;#it{p}_{T,jet}^{MCP} (GeV/#it{c});detector jet with target-origin reco track;weighted counts", + {HistType::kTH2F, {jetPtAxis, detectorJetExistsAxis}}); + registry.add("h2_mcp_jet_pt_vs_reco_constituent_pt_fraction_hasColl", + "MCP jet pT vs target-origin reconstructed constituent pT fraction for hasColl;#it{p}_{T,jet}^{MCP} (GeV/#it{c});#Sigma p_{T}^{reco target tracks} / #Sigma p_{T}^{truth constituents};weighted counts", + {HistType::kTH2F, {jetPtAxis, constituentPtFractionAxis}}); + registry.add("h2_mcp_jet_pt_vs_reco_constituent_pt_fraction_noColl", + "MCP jet pT vs target-origin reconstructed constituent pT fraction for noColl;#it{p}_{T,jet}^{MCP} (GeV/#it{c});#Sigma p_{T}^{reco target tracks} / #Sigma p_{T}^{truth constituents};weighted counts", + {HistType::kTH2F, {jetPtAxis, constituentPtFractionAxis}}); + registry.add("h2_mcp_jet_pt_vs_best_detector_jet_capture_fraction_hasColl", + "MCP jet pT vs best detector-jet target-track capture fraction for hasColl;#it{p}_{T,jet}^{MCP} (GeV/#it{c});best #Sigma p_{T}^{reco target tracks} / #Sigma p_{T}^{truth constituents};weighted counts", + {HistType::kTH2F, {jetPtAxis, constituentPtFractionAxis}}); + registry.add("h2_mcp_jet_pt_vs_best_detector_jet_capture_fraction_noColl", + "MCP jet pT vs best detector-jet target-track capture fraction for noColl;#it{p}_{T,jet}^{MCP} (GeV/#it{c});best #Sigma p_{T}^{reco target tracks} / #Sigma p_{T}^{truth constituents};weighted counts", + {HistType::kTH2F, {jetPtAxis, constituentPtFractionAxis}}); + + auto setDetectorJetExistsLabels = [](const auto& histogram) { + histogram->GetYaxis()->SetBinLabel(1, "no detector jet"); + histogram->GetYaxis()->SetBinLabel(2, "detector jet exists"); + }; + setDetectorJetExistsLabels(registry.get(HIST("h2_mcp_jet_pt_vs_has_detector_jet_hasColl"))); + setDetectorJetExistsLabels(registry.get(HIST("h2_mcp_jet_pt_vs_has_detector_jet_noColl"))); + } } template @@ -369,7 +616,7 @@ struct JetCrossSectionEfficiency { } template - bool configureTruthRofParameters(TBC const& truthBC) + bool configureRofParameters(TBC const& bc) { if (truthRofOffsetInBC >= 0 && truthRofLengthInBC > 0) { cachedTruthRofOffsetInBC = truthRofOffsetInBC; @@ -377,28 +624,28 @@ struct JetCrossSectionEfficiency { return true; } - if (cachedTruthRofRun == truthBC.runNumber() && cachedTruthRofLengthInBC > 0) { + if (cachedTruthRofRun == bc.runNumber() && cachedTruthRofLengthInBC > 0) { return true; } - auto alppar = ccdb->getForTimeStamp>("ITS/Config/AlpideParam", truthBC.timestamp()); + auto alppar = ccdb->getForTimeStamp>("ITS/Config/AlpideParam", bc.timestamp()); if (alppar == nullptr) { - LOGF(fatal, "Could not retrieve ITS/Config/AlpideParam for sel8FullPbPb truth selections (run %d, timestamp %" PRIu64 ")", truthBC.runNumber(), static_cast(truthBC.timestamp())); + LOGF(fatal, "Could not retrieve ITS/Config/AlpideParam for ITS ROF calculations (run %d, timestamp %" PRIu64 ")", bc.runNumber(), static_cast(bc.timestamp())); return false; } - cachedTruthRofRun = truthBC.runNumber(); + cachedTruthRofRun = bc.runNumber(); cachedTruthRofOffsetInBC = truthRofOffsetInBC >= 0 ? truthRofOffsetInBC : alppar->roFrameBiasInBC; cachedTruthRofLengthInBC = truthRofLengthInBC > 0 ? truthRofLengthInBC : alppar->roFrameLengthInBC; if (cachedTruthRofLengthInBC <= 0) { - LOGF(fatal, "Invalid ITS ROF length %" PRId64 " BC for sel8FullPbPb truth selections", static_cast(cachedTruthRofLengthInBC)); + LOGF(fatal, "Invalid ITS ROF length %" PRId64 " BC", static_cast(cachedTruthRofLengthInBC)); return false; } - LOGF(info, "sel8FullPbPb truth selections use ITS ROF offset %" PRId64 " and length %" PRId64 " BC for run %d", static_cast(cachedTruthRofOffsetInBC), static_cast(cachedTruthRofLengthInBC), cachedTruthRofRun); + LOGF(info, "ITS ROF calculations use offset %" PRId64 " and length %" PRId64 " BC for run %d", static_cast(cachedTruthRofOffsetInBC), static_cast(cachedTruthRofLengthInBC), cachedTruthRofRun); return true; } - int64_t truthRofId(uint64_t globalBC) const + int64_t rofId(uint64_t globalBC) const { // Match EventSelectionModule: use the ITS ROF bias and length from DPLAlpideParam, with one orbit added to avoid a negative numerator. return (static_cast(globalBC) + o2::constants::lhc::LHCMaxBunches - cachedTruthRofOffsetInBC) / cachedTruthRofLengthInBC; @@ -426,11 +673,11 @@ struct JetCrossSectionEfficiency { requireValidTruthFT0CActivity(mccollision); auto truthBC = mccollision.template bc_as(); const uint64_t currentGlobalBC = truthBC.globalBC(); - if (currentGlobalBC == std::numeric_limits::max() || !configureTruthRofParameters(truthBC)) { + if (currentGlobalBC == std::numeric_limits::max() || !configureRofParameters(truthBC)) { return selections; } - const int64_t currentRof = truthRofId(currentGlobalBC); + const int64_t currentRof = rofId(currentGlobalBC); bool hasNarrowActivity = false; bool hasHighActivityInTimeRange = false; bool hasHighActivityInSameRof = false; @@ -455,7 +702,7 @@ struct JetCrossSectionEfficiency { const float deltaTimeUs = static_cast(deltaGlobalBC) * o2::constants::lhc::LHCBunchSpacingNS / 1000.0f; const bool inNarrowWindow = std::abs(deltaTimeUs) < truthTimeRangeNarrowUs; const bool inStandardTimeWindow = deltaTimeUs > truthTimeRangeStandardMinUs && deltaTimeUs < truthTimeRangeStandardMaxUs; - const bool isSameRof = truthRofId(otherGlobalBC) == currentRof; + const bool isSameRof = rofId(otherGlobalBC) == currentRof; if (!inNarrowWindow && !inStandardTimeWindow && !isSameRof) { continue; } @@ -488,6 +735,177 @@ struct JetCrossSectionEfficiency { return selections; } + template + bool passesTVXBCEnvironmentBaseline(TBC const& bc) + { + const bool passesRct = !static_cast(applyRCT) || (bc.rct_raw() & rctMask) == 0; + return passesRct && + bc.selection_bit(aod::evsel::kIsTriggerTVX) && + bc.selection_bit(aod::evsel::kNoTimeFrameBorder) && + bc.selection_bit(aod::evsel::kNoITSROFrameBorder); + } + + template + bool isOtherTVXBCForEnvironment(TBC const& bc) + { + if (!bc.selection_bit(aod::evsel::kIsTriggerTVX)) { + return false; + } + return !static_cast(tvxEnvironmentRequireOtherBcBaseline) || passesTVXBCEnvironmentBaseline(bc); + } + + template + VisibleBCEnvironmentCache buildVisibleBCEnvironmentCache(TBCs const& bcs) + { + struct TVXBCInfo { + int64_t globalIndex = -1; + int runNumber = 0; + uint64_t globalBC = 0; + int64_t rof = -1; + bool passesTargetBaseline = false; + }; + + std::map> tvxBCsPerRun; + for (const auto& bc : bcs) { + if (!isOtherTVXBCForEnvironment(bc) || bc.globalBC() == std::numeric_limits::max()) { + continue; + } + if (!configureRofParameters(bc)) { + continue; + } + tvxBCsPerRun[bc.runNumber()].push_back({static_cast(bc.globalIndex()), bc.runNumber(), bc.globalBC(), rofId(bc.globalBC()), passesTVXBCEnvironmentBaseline(bc)}); + } + + VisibleBCEnvironmentCache cache; + for (auto& [runNumber, tvxBCs] : tvxBCsPerRun) { + std::sort(tvxBCs.begin(), tvxBCs.end(), [](const TVXBCInfo& lhs, const TVXBCInfo& rhs) { + return lhs.globalBC < rhs.globalBC; + }); + + std::unordered_map nTVXBCsPerRof; + for (const auto& tvxBC : tvxBCs) { + ++nTVXBCsPerRof[tvxBC.rof]; + } + + for (size_t i = 0; i < tvxBCs.size(); ++i) { + const auto& tvxBC = tvxBCs[i]; + if (!tvxBC.passesTargetBaseline) { + continue; + } + + uint64_t nearestAbsDeltaGlobalBC = std::numeric_limits::max(); + if (i > 0) { + nearestAbsDeltaGlobalBC = tvxBC.globalBC - tvxBCs[i - 1].globalBC; + } + if (i + 1 < tvxBCs.size()) { + const uint64_t nextAbsDeltaGlobalBC = tvxBCs[i + 1].globalBC - tvxBC.globalBC; + nearestAbsDeltaGlobalBC = std::min(nearestAbsDeltaGlobalBC, nextAbsDeltaGlobalBC); + } + + VisibleBCEnvironment environment; + environment.valid = true; + environment.nOtherTVXSameRof = nTVXBCsPerRof[tvxBC.rof] - 1; + environment.originalBCIndex = tvxBC.globalIndex; + if (nearestAbsDeltaGlobalBC != std::numeric_limits::max()) { + environment.nearestAbsDeltaTVXGlobalBC = static_cast(nearestAbsDeltaGlobalBC); + } + cache.byBCIndex[tvxBC.globalIndex] = environment; + cache.byRunAndGlobalBC[{runNumber, tvxBC.globalBC}] = environment; + } + } + return cache; + } + + template + std::map buildTruthHasCollEnvironmentCache(TMcCollisions const& mcCollisions) + { + struct TruthBCInfo { + int64_t mcCollisionIndex = -1; + int runNumber = 0; + uint64_t globalBC = 0; + int64_t rof = -1; + }; + + std::map> truthBCsPerRun; + for (const auto& mccollision : mcCollisions) { + const auto truthBC = mccollision.template bc_as(); + if (truthBC.globalBC() == std::numeric_limits::max() || !configureRofParameters(truthBC)) { + continue; + } + truthBCsPerRun[truthBC.runNumber()].push_back({static_cast(mccollision.globalIndex()), truthBC.runNumber(), truthBC.globalBC(), rofId(truthBC.globalBC())}); + } + + std::map cache; + for (auto& [runNumber, truthBCs] : truthBCsPerRun) { + (void)runNumber; + std::sort(truthBCs.begin(), truthBCs.end(), [](const TruthBCInfo& lhs, const TruthBCInfo& rhs) { + return lhs.globalBC < rhs.globalBC; + }); + + std::unordered_map nTruthBCsPerRof; + for (const auto& truthBC : truthBCs) { + ++nTruthBCsPerRof[truthBC.rof]; + } + + for (size_t i = 0; i < truthBCs.size(); ++i) { + const auto& truthBC = truthBCs[i]; + uint64_t nearestAbsDeltaGlobalBC = std::numeric_limits::max(); + if (i > 0) { + nearestAbsDeltaGlobalBC = truthBC.globalBC - truthBCs[i - 1].globalBC; + } + if (i + 1 < truthBCs.size()) { + const uint64_t nextAbsDeltaGlobalBC = truthBCs[i + 1].globalBC - truthBC.globalBC; + nearestAbsDeltaGlobalBC = std::min(nearestAbsDeltaGlobalBC, nextAbsDeltaGlobalBC); + } + + TruthHasCollEnvironment environment; + environment.valid = true; + environment.nOtherSameTruthRof = nTruthBCsPerRof[truthBC.rof] - 1; + if (nearestAbsDeltaGlobalBC != std::numeric_limits::max()) { + environment.nearestAbsDeltaGlobalBC = static_cast(nearestAbsDeltaGlobalBC); + } + cache[truthBC.mcCollisionIndex] = environment; + } + } + return cache; + } + + template + void fillTVXBCEnvironmentHistogramsFromCache(VisibleBCEnvironmentCache const& environments, + TBCs const& bcs, + CollisionsWithEvSels const& collisions, + aod::FT0s const& ft0s) + { + for (const auto& bc : bcs) { + const auto environment = environments.byBCIndex.find(static_cast(bc.globalIndex())); + if (environment == environments.byBCIndex.end()) { + continue; + } + + // This is a data-level TVX-BC to reconstructed-collision proxy, not the MC truth hasColl definition. + const auto collisionsAtTargetBC = collisions.sliceBy(collisionsPerFoundBC, bc.globalIndex()); + const bool hasRecoCollisionAtTargetBC = collisionsAtTargetBC.size() >= 1; + const auto& visibleEnvironment = environment->second; + registry.fill(HIST("h2_n_other_tvx_same_rof_vs_nearest_abs_delta_global_bc_tvx"), visibleEnvironment.nOtherTVXSameRof, visibleEnvironment.nearestAbsDeltaTVXGlobalBC); + if (hasRecoCollisionAtTargetBC) { + registry.fill(HIST("h2_n_other_tvx_same_rof_vs_nearest_abs_delta_global_bc_tvx_has_reco"), visibleEnvironment.nOtherTVXSameRof, visibleEnvironment.nearestAbsDeltaTVXGlobalBC); + } + registry.fill(HIST("h2_n_other_tvx_same_rof_vs_has_reco_collision"), visibleEnvironment.nOtherTVXSameRof, hasRecoCollisionAtTargetBC); + registry.fill(HIST("h2_nearest_abs_delta_global_bc_tvx_vs_has_reco_collision"), visibleEnvironment.nearestAbsDeltaTVXGlobalBC, hasRecoCollisionAtTargetBC); + if (bc.has_foundFT0()) { + const auto foundFT0 = ft0s.rawIteratorAt(bc.foundFT0Id()); + registry.fill(HIST("h2_target_ft0c_amplitude_vs_n_other_tvx_same_rof"), foundFT0.sumAmpC(), visibleEnvironment.nOtherTVXSameRof); + } + } + } + + template + void fillTVXBCEnvironmentHistograms(TBCs const& bcs, CollisionsWithEvSels const& collisions, aod::FT0s const& ft0s) + { + const auto environments = buildVisibleBCEnvironmentCache(bcs); + fillTVXBCEnvironmentHistogramsFromCache(environments, bcs, collisions, ft0s); + } + template bool isAcceptedJet(TJets const& jet) { @@ -519,10 +937,10 @@ struct JetCrossSectionEfficiency { return true; } - void processTruthMultFT0CvsRecoAmplitude(aod::JetMcCollisions::iterator const& mccollision, - soa::SmallGroups const& collisions, - CollisionsWithEvSels const&, - aod::FT0s const& ft0s) + void processMCTruthMultFT0CvsRecoAmplitude(aod::JetMcCollisions::iterator const& mccollision, + soa::SmallGroups const& collisions, + CollisionsWithEvSels const&, + aod::FT0s const& ft0s) { if (skipMBGapEvents && mccollision.getSubGeneratorId() == jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { return; @@ -543,13 +961,13 @@ struct JetCrossSectionEfficiency { auto foundFT0 = ft0s.rawIteratorAt(originalCollision.foundFT0Id()); registry.fill(HIST("h2_mccollision_mult_ft0c_found_ft0_sum_amp_c"), mccollision.multFT0C(), foundFT0.sumAmpC(), mccollision.weight()); } - PROCESS_SWITCH(JetCrossSectionEfficiency, processTruthMultFT0CvsRecoAmplitude, - "truth multFT0C vs reconstructed foundFT0 A_FT0C for one-to-one MC/reco collision associations", false); + PROCESS_SWITCH(JetCrossSectionEfficiency, processMCTruthMultFT0CvsRecoAmplitude, + "[MC ONLY] truth multFT0C vs reconstructed foundFT0 A_FT0C for one-to-one MC/reco collision associations", false); - void processTruthMultFT0CvsRecoITS567(aod::JetMcCollisions::iterator const& mccollision, - soa::SmallGroups const& collisions, - CollisionsWithEvSels const&, - FullTracksIU const&) + void processMCTruthMultFT0CvsRecoITS567(aod::JetMcCollisions::iterator const& mccollision, + soa::SmallGroups const& collisions, + CollisionsWithEvSels const&, + FullTracksIU const&) { if (skipMBGapEvents && mccollision.getSubGeneratorId() == jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { return; @@ -576,8 +994,290 @@ struct JetCrossSectionEfficiency { registry.fill(HIST("h2_mccollision_mult_ft0c_positive_vs_reco_its567_positive"), mccollision.multFT0C() > 0.0f, nITS567 > 0, weight); } - PROCESS_SWITCH(JetCrossSectionEfficiency, processTruthMultFT0CvsRecoITS567, - "truth multFT0C activity vs reconstructed PV-contributor ITS567 activity for one-to-one MC/reco collision associations", false); + PROCESS_SWITCH(JetCrossSectionEfficiency, processMCTruthMultFT0CvsRecoITS567, + "[MC ONLY] truth multFT0C activity vs reconstructed PV-contributor ITS567 activity for one-to-one MC/reco collision associations", false); + + void processMCEnvironmentQA(aod::JetMcCollisions const& mcCollisions, + aod::JetCollisionsMCD const& jetCollisions, + aod::JBCs const&, + OriginalBCsWithSels const& bcs, + CollisionsWithEvSels const& collisions, + aod::FT0s const& ft0s) + { + const auto visibleEnvironments = buildVisibleBCEnvironmentCache(bcs); + const auto truthEnvironments = buildTruthHasCollEnvironmentCache(mcCollisions); + // The visible-BC QA is deliberately unweighted so its definition remains directly comparable to data. + fillTVXBCEnvironmentHistogramsFromCache(visibleEnvironments, bcs, collisions, ft0s); + for (const auto& mccollision : mcCollisions) { + if (skipMBGapEvents && mccollision.getSubGeneratorId() == jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { + continue; + } + + const auto truthBC = mccollision.template bc_as(); + if (!passesTVXBCEnvironmentBaseline(truthBC)) { + continue; + } + + const auto environment = truthEnvironments.find(mccollision.globalIndex()); + if (environment == truthEnvironments.end() || !environment->second.valid) { + continue; + } + + const bool hasRecoColl = jetCollisions.sliceBy(jetCollisionsPerMcCollision, mccollision.globalIndex()).size() >= 1; + const float weight = mccollision.weight(); + registry.fill(HIST("h2_n_other_same_truth_rof_vs_has_coll"), environment->second.nOtherSameTruthRof, hasRecoColl, weight); + registry.fill(HIST("h2_nearest_abs_delta_global_bc_vs_has_coll"), environment->second.nearestAbsDeltaGlobalBC, hasRecoColl, weight); + + const auto visibleEnvironment = visibleEnvironments.byRunAndGlobalBC.find({truthBC.runNumber(), truthBC.globalBC()}); + if (visibleEnvironment != visibleEnvironments.byRunAndGlobalBC.end() && visibleEnvironment->second.valid) { + const auto collisionsAtTargetBC = collisions.sliceBy(collisionsPerFoundBC, visibleEnvironment->second.originalBCIndex); + const bool hasRecoCollisionAtTargetBC = collisionsAtTargetBC.size() >= 1; + registry.fill(HIST("h2_n_other_truth_same_rof_vs_n_other_tvx_same_rof"), environment->second.nOtherSameTruthRof, visibleEnvironment->second.nOtherTVXSameRof, weight); + registry.fill(HIST("h2_nearest_abs_delta_global_bc_truth_vs_nearest_abs_delta_global_bc_tvx"), environment->second.nearestAbsDeltaGlobalBC, visibleEnvironment->second.nearestAbsDeltaTVXGlobalBC, weight); + registry.fill(HIST("h2_reco_collision_proxy_vs_has_coll"), hasRecoCollisionAtTargetBC, hasRecoColl, weight); + } + + if (mccollision.multFT0C() < 0.0f) { + continue; + } + + registry.fill(HIST("h2_target_mult_ft0c_vs_has_coll"), mccollision.multFT0C(), hasRecoColl, weight); + registry.fill(HIST("h2_target_mult_ft0c_vs_n_other_same_truth_rof_all"), mccollision.multFT0C(), environment->second.nOtherSameTruthRof, weight); + registry.fill(HIST("h2_target_mult_ft0c_vs_nearest_abs_delta_global_bc_all"), mccollision.multFT0C(), environment->second.nearestAbsDeltaGlobalBC, weight); + if (hasRecoColl) { + registry.fill(HIST("h2_target_mult_ft0c_vs_n_other_same_truth_rof_has_coll"), mccollision.multFT0C(), environment->second.nOtherSameTruthRof, weight); + registry.fill(HIST("h2_target_mult_ft0c_vs_nearest_abs_delta_global_bc_has_coll"), mccollision.multFT0C(), environment->second.nearestAbsDeltaGlobalBC, weight); + } + } + } + PROCESS_SWITCH(JetCrossSectionEfficiency, processMCEnvironmentQA, + "[MC ONLY] truth and visible TVX collision-environment QA and JetDerived hasColl validation", false); + + void processMCHasCollTrackFateQA(JMcCollisionsWithParent::iterator const& mccollision, + soa::SmallGroups const& collisions, + OriginalCollisionsWithMcLabels const&, + FullTracksIUWithMcLabels const& recoTracks, + aod::McCollisions const&, + aod::McParticles const& mcParticles, + aod::JBCs const&) + { + if (skipMBGapEvents && mccollision.getSubGeneratorId() == jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { + return; + } + + auto truthBC = mccollision.bc_as(); + const bool passesRct = !applyRCT || (truthBC.rct_raw() & rctMask) == 0; + const bool passesTVXTruth = truthBC.selection_bit(aod::evsel::kIsTriggerTVX); + const bool passesNoTFBTruth = truthBC.selection_bit(aod::evsel::kNoTimeFrameBorder); + const bool passesNoITSROFBTruth = truthBC.selection_bit(aod::evsel::kNoITSROFrameBorder); + if (!passesRct || !passesTVXTruth || !passesNoTFBTruth || !passesNoITSROFBTruth) { + return; + } + + const auto originalMcCollision = mccollision.template mcCollision_as(); + const auto targetMcParticles = mcParticles.sliceBy(mcParticlesPerMcCollision, originalMcCollision.globalIndex()); + int nRecoTracksFromTarget = 0; + int nUnassignedTracksFromTarget = 0; + int nTracksAttachedToOtherLabeledRecoCollision = 0; + + for (const auto& mcParticle : targetMcParticles) { + const auto targetRecoTracks = recoTracks.sliceBy(recoTracksPerMcParticle, mcParticle.globalIndex()); + for (const auto& recoTrack : targetRecoTracks) { + if (!recoTrack.has_mcParticle()) { + continue; + } + + ++nRecoTracksFromTarget; + if (!recoTrack.has_collision()) { + ++nUnassignedTracksFromTarget; + continue; + } + + const auto recoCollision = recoTrack.template collision_as(); + if (recoCollision.has_mcCollision() && recoCollision.mcCollisionId() != originalMcCollision.globalIndex()) { + ++nTracksAttachedToOtherLabeledRecoCollision; + } + } + } + + const bool hasRecoColl = collisions.size() >= 1; + const float weight = mccollision.weight(); + registry.fill(HIST("h2_n_reco_tracks_from_target_vs_has_coll"), nRecoTracksFromTarget, hasRecoColl, weight); + registry.fill(HIST("h2_n_unassigned_tracks_from_target_vs_has_coll"), nUnassignedTracksFromTarget, hasRecoColl, weight); + registry.fill(HIST("h2_n_target_tracks_attached_to_other_labeled_collision_vs_has_coll"), nTracksAttachedToOtherLabeledRecoCollision, hasRecoColl, weight); + } + PROCESS_SWITCH(JetCrossSectionEfficiency, processMCHasCollTrackFateQA, + "[MC ONLY] track fate for target MC collisions before JetDerived hasColl", false); + + void processMCHasCollJetSurvivalQA(JMcCollisionsWithParent::iterator const& mccollision, + soa::SmallGroups const& collisions, + ChargedMCPJetsWithConstituents const& mcpJets, + ChargedMCDJetsWithConstituents const& detectorJets, + JetCollisionsWithParent const& jetCollisions, + FullTracksIUWithMcLabels const& recoTracks, + aod::JetTracks const&, + aod::JTrackPIs const& jTrackParents, + aod::JetParticles const&, + aod::JMcParticlePIs const& jMcParticleParents, + aod::JBCs const&) + { + if (skipMBGapEvents && mccollision.getSubGeneratorId() == jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { + return; + } + + auto truthBC = mccollision.bc_as(); + const bool passesRct = !applyRCT || (truthBC.rct_raw() & rctMask) == 0; + const bool passesTVXTruth = truthBC.selection_bit(aod::evsel::kIsTriggerTVX); + const bool passesNoTFBTruth = truthBC.selection_bit(aod::evsel::kNoTimeFrameBorder); + const bool passesNoITSROFBTruth = truthBC.selection_bit(aod::evsel::kNoITSROFrameBorder); + if (!passesRct || !passesTVXTruth || !passesNoTFBTruth || !passesNoITSROFBTruth) { + return; + } + + const float pTHat = computePtHat(mccollision); + if (pTHat < pTHatAbsoluteMin) { + return; + } + + const bool hasRecoColl = collisions.size() >= 1; + const float weight = mccollision.weight(); + for (const auto& mcpJet : mcpJets) { + if (!jetfindingutilities::isInEtaAcceptance(mcpJet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax) || + mcpJet.pt() < jetPtMin || mcpJet.pt() > pTHatMaxMCP * pTHat || + !isAcceptedJet(mcpJet)) { + continue; + } + + float sumTruthConstituentPt = 0.0f; + float sumRecoTrackPtFromTruthJet = 0.0f; + std::unordered_map recoTrackPtByOriginalTrackId; + std::unordered_set jetCollisionIdsReceivingTargetTracks; + for (const auto& constituent : mcpJet.template tracks_as()) { + sumTruthConstituentPt += constituent.pt(); + const auto originalMcParticle = jMcParticleParents.rawIteratorAt(constituent.globalIndex()); + const auto targetRecoTracks = recoTracks.sliceBy(recoTracksPerMcParticle, originalMcParticle.mcParticleId()); + for (const auto& recoTrack : targetRecoTracks) { + if (!recoTrack.has_mcParticle()) { + continue; + } + + const int64_t originalTrackId = recoTrack.globalIndex(); + if (!recoTrackPtByOriginalTrackId.emplace(originalTrackId, recoTrack.pt()).second) { + continue; + } + sumRecoTrackPtFromTruthJet += recoTrack.pt(); + + if (!recoTrack.has_collision()) { + continue; + } + const auto associatedJetCollisions = jetCollisions.sliceBy(jetCollisionsPerOriginalCollision, recoTrack.collisionId()); + for (const auto& jetCollision : associatedJetCollisions) { + jetCollisionIdsReceivingTargetTracks.insert(jetCollision.globalIndex()); + } + } + } + + if (sumTruthConstituentPt <= 0.0f) { + continue; + } + + std::unordered_map capturedRecoPtByDetectorJetId; + for (const int64_t jetCollisionId : jetCollisionIdsReceivingTargetTracks) { + const auto associatedDetectorJets = detectorJets.sliceBy(detectorJetsPerJetCollision, jetCollisionId); + for (const auto& detectorJet : associatedDetectorJets) { + float capturedRecoPt = 0.0f; + for (const auto& detectorConstituent : detectorJet.template tracks_as()) { + const auto originalTrack = jTrackParents.rawIteratorAt(detectorConstituent.globalIndex()); + const auto recoTrack = recoTrackPtByOriginalTrackId.find(originalTrack.trackId()); + if (recoTrack != recoTrackPtByOriginalTrackId.end()) { + capturedRecoPt += recoTrack->second; + } + } + if (capturedRecoPt > 0.0f) { + capturedRecoPtByDetectorJetId[detectorJet.globalIndex()] += capturedRecoPt; + } + } + } + + float bestDetectorJetCapturedTargetRecoPt = 0.0f; + for (const auto& [detectorJetId, capturedRecoPt] : capturedRecoPtByDetectorJetId) { + (void)detectorJetId; + if (capturedRecoPt > bestDetectorJetCapturedTargetRecoPt) { + bestDetectorJetCapturedTargetRecoPt = capturedRecoPt; + } + } + + const bool hasDetectorJetWithTargetTrack = !capturedRecoPtByDetectorJetId.empty(); + const float recoConstituentPtFraction = sumRecoTrackPtFromTruthJet / sumTruthConstituentPt; + const float bestDetectorJetCaptureFraction = bestDetectorJetCapturedTargetRecoPt / sumTruthConstituentPt; + if (hasRecoColl) { + registry.fill(HIST("h2_mcp_jet_pt_vs_has_detector_jet_hasColl"), mcpJet.pt(), hasDetectorJetWithTargetTrack, weight); + registry.fill(HIST("h2_mcp_jet_pt_vs_reco_constituent_pt_fraction_hasColl"), mcpJet.pt(), recoConstituentPtFraction, weight); + registry.fill(HIST("h2_mcp_jet_pt_vs_best_detector_jet_capture_fraction_hasColl"), mcpJet.pt(), bestDetectorJetCaptureFraction, weight); + } else { + registry.fill(HIST("h2_mcp_jet_pt_vs_has_detector_jet_noColl"), mcpJet.pt(), hasDetectorJetWithTargetTrack, weight); + registry.fill(HIST("h2_mcp_jet_pt_vs_reco_constituent_pt_fraction_noColl"), mcpJet.pt(), recoConstituentPtFraction, weight); + registry.fill(HIST("h2_mcp_jet_pt_vs_best_detector_jet_capture_fraction_noColl"), mcpJet.pt(), bestDetectorJetCaptureFraction, weight); + } + } + } + PROCESS_SWITCH(JetCrossSectionEfficiency, processMCHasCollJetSurvivalQA, + "[MC ONLY] truth-constituent overlap QA for detector-level charged jet survival before JetDerived hasColl", false); + + void processMCHasCollJetEnvironmentQA(aod::JetMcCollisions const& mcCollisions, + aod::JetCollisionsMCD const& jetCollisions, + ChargedMCPJetsWithConstituents const& mcpJets, + aod::JetParticles const&, + aod::JBCs const&, + OriginalBCsWithSels const& bcs) + { + const auto visibleEnvironments = buildVisibleBCEnvironmentCache(bcs); + for (const auto& mccollision : mcCollisions) { + if (skipMBGapEvents && mccollision.getSubGeneratorId() == jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { + continue; + } + + const auto truthBC = mccollision.template bc_as(); + if (!passesTVXBCEnvironmentBaseline(truthBC)) { + continue; + } + + const auto visibleEnvironment = visibleEnvironments.byRunAndGlobalBC.find({truthBC.runNumber(), truthBC.globalBC()}); + if (visibleEnvironment == visibleEnvironments.byRunAndGlobalBC.end() || !visibleEnvironment->second.valid) { + continue; + } + + const float pTHat = computePtHat(mccollision); + if (pTHat < pTHatAbsoluteMin) { + continue; + } + + const bool hasRecoColl = jetCollisions.sliceBy(jetCollisionsPerMcCollision, mccollision.globalIndex()).size() >= 1; + const float weight = mccollision.weight(); + const auto targetMcpJets = mcpJets.sliceBy(mcpJetsPerMcCollision, mccollision.globalIndex()); + for (const auto& mcpJet : targetMcpJets) { + if (!jetfindingutilities::isInEtaAcceptance(mcpJet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax) || + mcpJet.pt() < jetPtMin || mcpJet.pt() > pTHatMaxMCP * pTHat || + !isAcceptedJet(mcpJet)) { + continue; + } + + // hasRecoColl remains a histogram axis rather than a selection, so noColl MCP jets are part of the denominator. + registry.fill(HIST("hs_mcp_jet_pt_tvx_environment_has_coll"), mcpJet.pt(), visibleEnvironment->second.nOtherTVXSameRof, visibleEnvironment->second.nearestAbsDeltaTVXGlobalBC, hasRecoColl, weight); + } + } + } + PROCESS_SWITCH(JetCrossSectionEfficiency, processMCHasCollJetEnvironmentQA, + "[MC ONLY] selected MCP jets vs original-TVX-BC environment and JetDerived hasColl", false); + + void processDataTVXBCEnvironmentQA(OriginalBCsWithSels const& bcs, + CollisionsWithEvSels const& collisions, + aod::FT0s const& ft0s) + { + fillTVXBCEnvironmentHistograms(bcs, collisions, ft0s); + } + PROCESS_SWITCH(JetCrossSectionEfficiency, processDataTVXBCEnvironmentQA, + "[DATA ONLY] TVX-BC collision-environment QA using original BC and reconstructed-collision tables", false); void processCrossSectionEfficiency(aod::JetMcCollisions::iterator const& mccollision, soa::SmallGroups const& collisions, @@ -686,11 +1386,12 @@ struct JetCrossSectionEfficiency { bool passesNoTFBTruth = truthBC.selection_bit(aod::evsel::kNoTimeFrameBorder); bool passesNoITSROFBTruth = truthBC.selection_bit(aod::evsel::kNoITSROFrameBorder); - bool truthNoSBP = true; - if (applySBP) { + bool truthNoSBPDiagnostic = true; + if (applySBP || isSel8FullPbPb) { auto sameBC = allMcCollisions.sliceBy(mcCollsPerBC, mccollision.bcId()); - truthNoSBP = (sameBC.size() == 1); + truthNoSBPDiagnostic = (sameBC.size() == 1); } + bool truthNoSBP = applySBP ? truthNoSBPDiagnostic : true; bool hasRecoColl = (collisions.size() >= 1); bool passesZvtxCutReco = false; @@ -715,6 +1416,38 @@ struct JetCrossSectionEfficiency { } bool passesRct = applyRCT ? (truthBC.rct_raw() & rctMask) == 0 : true; + float weight = mccollision.weight(); + + if (isSel8FullPbPb && passesRct && passesTVXTruth && truthPbPbSelections.valid) { + // The five selection conditions are evaluated independently on the same RCT+TVX + // baseline with valid PbPb truth-proxy evaluation. + const std::array selectionConditionPasses = { + passesNoTFBTruth, + passesNoITSROFBTruth, + truthNoSBPDiagnostic, + truthPbPbSelections.noCollInTimeRangeStandard, + truthPbPbSelections.noCollInRofStandard}; + for (size_t conditionIndex = 0; conditionIndex < selectionConditionPasses.size(); ++conditionIndex) { + const int hasCollSelectionState = (hasRecoColl ? 3 : 1) + (selectionConditionPasses[conditionIndex] ? 1 : 0); + registry.fill(HIST("h2_mccollisions_selection_bit_vs_has_coll_state"), static_cast(conditionIndex + 1), hasCollSelectionState, weight); + } + + float pTHatDiagnostic = computePtHat(mccollision); + if (pTHatDiagnostic >= pTHatAbsoluteMin) { + for (const auto& jet : jets) { + if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax) || + jet.pt() < jetPtMin || jet.pt() > pTHatMaxMCP * pTHatDiagnostic || + !isAcceptedJet(jet)) { + continue; + } + for (size_t conditionIndex = 0; conditionIndex < selectionConditionPasses.size(); ++conditionIndex) { + const int hasCollSelectionState = (hasRecoColl ? 3 : 1) + (selectionConditionPasses[conditionIndex] ? 1 : 0); + registry.fill(HIST("h2_mcpjets_selection_bit_vs_has_coll_state"), static_cast(conditionIndex + 1), hasCollSelectionState, weight); + } + } + } + } + std::vector pass = {true, passesRct, passesTVXTruth, applyTFB ? passesNoTFBTruth : true, applyROFB ? passesNoITSROFBTruth : true}; @@ -731,8 +1464,6 @@ struct JetCrossSectionEfficiency { pass.push_back(passesZvtxCutReco); pass.push_back(hasRecoColl && noSplitPass); - float weight = mccollision.weight(); - int sMaxTimeRangeOnly = 0; int sMaxRofOnly = 0; if (isSel8FullPbPb) { diff --git a/PWGJE/Tasks/jetD0AngSubstructure.cxx b/PWGJE/Tasks/jetD0AngSubstructure.cxx index 8a14b780d36..2c0fa7a6562 100644 --- a/PWGJE/Tasks/jetD0AngSubstructure.cxx +++ b/PWGJE/Tasks/jetD0AngSubstructure.cxx @@ -344,8 +344,8 @@ struct JetD0AngSubstructure { void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); addHistograms(); } diff --git a/PWGJE/Tasks/jetDebug.cxx b/PWGJE/Tasks/jetDebug.cxx index d384f3a5e36..8d220b5665b 100644 --- a/PWGJE/Tasks/jetDebug.cxx +++ b/PWGJE/Tasks/jetDebug.cxx @@ -107,8 +107,8 @@ struct JetDebugTask { void init(o2::framework::InitContext&) { - eventSelection = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelection = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); } void processCollisionSelection(aod::JetCollisions const& collisions, soa::Join const& originalCollisions) diff --git a/PWGJE/Tasks/jetDsSpecSubs.cxx b/PWGJE/Tasks/jetDsSpecSubs.cxx index d1a5a634fc1..96da12c3ad6 100644 --- a/PWGJE/Tasks/jetDsSpecSubs.cxx +++ b/PWGJE/Tasks/jetDsSpecSubs.cxx @@ -488,8 +488,8 @@ struct JetDsSpecSubs { void init(InitContext const&) { // Initialise event and track selection criteria - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); // Prevent simultaneous execution of processes using alternative jet collections if (doprocessCollisions && doprocessCollisionsEWS) { diff --git a/PWGJE/Tasks/jetFinderFullQA.cxx b/PWGJE/Tasks/jetFinderFullQA.cxx index 69b18b6e4af..4802a5a333f 100644 --- a/PWGJE/Tasks/jetFinderFullQA.cxx +++ b/PWGJE/Tasks/jetFinderFullQA.cxx @@ -96,13 +96,13 @@ struct JetFinderFullQATask { void init(o2::framework::InitContext&) { - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); - jetRadiiValues = (std::vector)jetRadii; + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); + jetRadiiValues = jetRadii; for (std::size_t iJetRadius = 0; iJetRadius < jetRadiiValues.size(); iJetRadius++) { filledJetR.push_back(0.0); } - auto jetRadiiBins = (std::vector)jetRadii; + auto jetRadiiBins = jetRadii.value; if (jetRadiiBins.size() > 1) { jetRadiiBins.push_back(jetRadiiBins[jetRadiiBins.size() - 1] + (TMath::Abs(jetRadiiBins[jetRadiiBins.size() - 1] - jetRadiiBins[jetRadiiBins.size() - 2]))); } else { @@ -253,7 +253,7 @@ struct JetFinderFullQATask { Filter trackCuts = (aod::jtrack::pt >= trackPtMin && aod::jtrack::pt < trackPtMax && aod::jtrack::eta > trackEtaMin && aod::jtrack::eta < trackEtaMax); Filter eventCuts = (nabs(aod::jcollision::posZ) < vertexZCut && aod::jcollision::centFT0M >= centralityMin && aod::jcollision::centFT0M < centralityMax); - aod::EMCALClusterDefinition clusterDefinition = aod::emcalcluster::getClusterDefinitionFromString(clusterDefinitionS.value); + aod::EMCALClusterDefinition clusterDefinition = aod::emcalcluster::getClusterDefinitionFromString(clusterDefinitionS); Filter clusterFilter = (aod::jcluster::definition == static_cast(clusterDefinition) && aod::jcluster::eta > clusterEtaMin && aod::jcluster::eta < clusterEtaMax && aod::jcluster::phi >= clusterPhiMin && aod::jcluster::phi <= clusterPhiMax && aod::jcluster::energy >= clusterEnergyMin && aod::jcluster::time > clusterTimeMin && aod::jcluster::time < clusterTimeMax && (clusterRejectExotics && aod::jcluster::isExotic != true)); template diff --git a/PWGJE/Tasks/jetFinderHFQA.h b/PWGJE/Tasks/jetFinderHFQA.h index ad1c25a9aa1..3da47b8f636 100644 --- a/PWGJE/Tasks/jetFinderHFQA.h +++ b/PWGJE/Tasks/jetFinderHFQA.h @@ -16,7 +16,6 @@ #ifndef PWGJE_TASKS_JETFINDERHFQA_H_ #define PWGJE_TASKS_JETFINDERHFQA_H_ -#include "PWGHF/Core/HfHelper.h" #include "PWGJE/Core/JetDerivedDataUtilities.h" #include "PWGJE/Core/JetFindingUtilities.h" #include "PWGJE/Core/JetHFUtilities.h" @@ -73,7 +72,6 @@ struct JetFinderHFQATask { o2::framework::Configurable randomConeR{"randomConeR", 0.4, "size of random Cone for estimating background fluctuations"}; o2::framework::Configurable skipMBGapEvents{"skipMBGapEvents", false, "flag to choose to reject min. bias gap events; jet-level rejection applied at the jet finder level, here rejection is applied for collision and track process functions"}; - HfHelper hfHelper; std::vector filledJetR_Both; std::vector filledJetR_Low; std::vector filledJetR_High; @@ -90,10 +88,10 @@ struct JetFinderHFQATask { void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); - jetRadiiValues = (std::vector)jetRadii; + jetRadiiValues = jetRadii; for (std::size_t iJetRadius = 0; iJetRadius < jetRadiiValues.size(); iJetRadius++) { filledJetR_Both.push_back(0.0); @@ -103,7 +101,7 @@ struct JetFinderHFQATask { filledJetHFR_Low.push_back(0.0); filledJetHFR_High.push_back(0.0); } - auto jetRadiiBins = (std::vector)jetRadii; + auto jetRadiiBins = jetRadii.value; if (jetRadiiBins.size() > 1) { jetRadiiBins.push_back(jetRadiiBins[jetRadiiBins.size() - 1] + (TMath::Abs(jetRadiiBins[jetRadiiBins.size() - 1] - jetRadiiBins[jetRadiiBins.size() - 2]))); } else { diff --git a/PWGJE/Tasks/jetFinderQA.cxx b/PWGJE/Tasks/jetFinderQA.cxx index cd28f221ad9..a3114bc39ca 100644 --- a/PWGJE/Tasks/jetFinderQA.cxx +++ b/PWGJE/Tasks/jetFinderQA.cxx @@ -105,16 +105,16 @@ struct JetFinderQATask { void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); - jetRadiiValues = (std::vector)jetRadii; + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); + jetRadiiValues = jetRadii; for (std::size_t iJetRadius = 0; iJetRadius < jetRadiiValues.size(); iJetRadius++) { filledJetRBoth.push_back(0.0); filledJetRLow.push_back(0.0); filledJetRHigh.push_back(0.0); } - auto jetRadiiBins = (std::vector)jetRadii; + auto jetRadiiBins = jetRadii.value; if (jetRadiiBins.size() > 1) { jetRadiiBins.push_back(jetRadiiBins[jetRadiiBins.size() - 1] + (std::abs(jetRadiiBins[jetRadiiBins.size() - 1] - jetRadiiBins[jetRadiiBins.size() - 2]))); } else { @@ -819,7 +819,7 @@ struct JetFinderQATask { float randomConePt = 0; for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { - float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, (-o2::constants::math::PI)); float dEta = track.eta() - randomConeEta; if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { randomConePt += track.pt(); @@ -832,8 +832,8 @@ struct JetFinderQATask { randomConePt = 0; for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { - float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, static_cast(-o2::constants::math::PI)); // ignores actual phi of track - float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track + float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, (-o2::constants::math::PI)); // ignores actual phi of track + float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { randomConePt += track.pt(); } @@ -843,7 +843,7 @@ struct JetFinderQATask { // removing the leading jet from the random cone if (jets.size() > 0) { // if there are no jets in the acceptance (from the jetfinder cuts) then there can be no leading jet - float dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + float dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, (-o2::constants::math::PI)); float dEtaLeadingJet = jets.iteratorAt(0).eta() - randomConeEta; bool jetWasInCone = false; @@ -851,14 +851,14 @@ struct JetFinderQATask { jetWasInCone = true; randomConeEta = randomNumber.Uniform(trackEtaMin + randomConeR, trackEtaMax - randomConeR); randomConePhi = randomNumber.Uniform(0.0, o2::constants::math::TwoPI); - dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, (-o2::constants::math::PI)); dEtaLeadingJet = jets.iteratorAt(0).eta() - randomConeEta; } if (jetWasInCone) { randomConePt = 0.0; for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { // if track selection is uniformTrack, dcaXY and dcaZ cuts need to be added as they aren't in the selection so that they can be studied here - float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, (-o2::constants::math::PI)); float dEta = track.eta() - randomConeEta; if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { randomConePt += track.pt(); @@ -875,8 +875,8 @@ struct JetFinderQATask { double randomConePtWithoutTwoLeadJet = 0; for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { - float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, static_cast(-o2::constants::math::PI)); // ignores actual phi of track - float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track + float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, (-o2::constants::math::PI)); // ignores actual phi of track + float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { if (!trackIsInJet(track, jets.iteratorAt(0))) { randomConePtWithoutOneLeadJet += track.pt(); diff --git a/PWGJE/Tasks/jetFinderV0QA.cxx b/PWGJE/Tasks/jetFinderV0QA.cxx index 55036241730..dd4dc32a394 100644 --- a/PWGJE/Tasks/jetFinderV0QA.cxx +++ b/PWGJE/Tasks/jetFinderV0QA.cxx @@ -68,10 +68,10 @@ struct JetFinderV0QATask { void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); - auto jetRadiiBins = (std::vector)jetRadii; + auto jetRadiiBins = jetRadii.value; if (jetRadiiBins.size() > 1) { jetRadiiBins.push_back(jetRadiiBins[jetRadiiBins.size() - 1] + (TMath::Abs(jetRadiiBins[jetRadiiBins.size() - 1] - jetRadiiBins[jetRadiiBins.size() - 2]))); } else { diff --git a/PWGJE/Tasks/jetHFAngularity.cxx b/PWGJE/Tasks/jetHFAngularity.cxx index 6b16ff5af58..4850f1819fc 100644 --- a/PWGJE/Tasks/jetHFAngularity.cxx +++ b/PWGJE/Tasks/jetHFAngularity.cxx @@ -253,6 +253,24 @@ struct JetHFAngularityTask { "Event selection string"}; Configurable trackSelections{"trackSelections", "globalTracks", "Track selection string"}; + Configurable rejectReflections{"rejectReflections", true, + "Exclude reflections from response matrices and efficiency numerators"}; + // Shared axes + ConfigurableAxis binsMassD0{"binsMassD0", {500, 1.6f, 2.1f}, "m_{D^{0}} (GeV/#it{c}^{2})"}; + ConfigurableAxis binsPtD0{"binsPtD0", {200, 0.f, 100.f}, "#it{p}_{T,D^{0}} (GeV/#it{c})"}; + ConfigurableAxis binsZParallel{"binsZParallel", {200, 0.f, 2.f}, "z_{||}"}; + ConfigurableAxis binsDeltaR{"binsDeltaR", {200, 0.f, 1.f}, "#DeltaR"}; + ConfigurableAxis binsLambda11{"binsLambda11", {50, 0.f, 1.f}, "#lambda_{1}^{1}"}; + ConfigurableAxis binsLambda12{"binsLambda12", {50, 0.f, 1.f}, "#lambda_{2}^{1}"}; + + // Jet pT: data sparse vs. MC / response + ConfigurableAxis binsPtJetData{"binsPtJetData", {200, 0.f, 100.f}, "#it{p}_{T,jet} (GeV/#it{c})"}; + ConfigurableAxis binsPtJetMC{"binsPtJetMC", {40, 0.f, 200.f}, "#it{p}_{T,jet} (GeV/#it{c})"}; + + // Response-matrix axes + ConfigurableAxis binsPtD0Resp{"binsPtD0Resp", {50, 0.f, 50.f}, "#it{p}_{T,D^{0}} (GeV/#it{c})"}; + ConfigurableAxis binsLambda11Resp{"binsLambda11Resp", {50, 0.f, 1.f}, "#lambda_{1}^{1}"}; + ConfigurableAxis binsLambda12Resp{"binsLambda12Resp", {50, 0.f, 1.f}, "#lambda_{2}^{1}"}; Produces objJetTable; Produces objJetMCDTable; @@ -328,8 +346,7 @@ struct JetHFAngularityTask { {"h_d0_mlscore1", ";ML score 1 (prompt);entries", {HistType::kTH1F, {{100, 0., 1.}}}}, {"h_d0_mlscore2", ";ML score 2 (non-prompt);entries", {HistType::kTH1F, {{100, 0., 1.}}}}, - // ----- Sparse: (m_D0, pT_D0, pT_jet, z_parallel, DeltaR) ----- - {"hSparse_d0", ";m_{D^{0}};#it{p}_{T,D^{0}};#it{p}_{T,jet};z_{||};#DeltaR;#lambda_{1}^{1};#lambda_{2}^{1}", {HistType::kTHnSparseF, {{300, 1.7, 2.0}, {200, 0., 100.}, {200, 0., 100.}, {200, 0., 2.}, {200, 0., 1.}, {50, 0., 1.}, {50, 0., 1.}}}}, + // hSparse_d0 is booked in init() (configurable axes) // MC DETECTOR LEVEL (MCD) @@ -355,15 +372,11 @@ struct JetHFAngularityTask { {"h_d0_origin_mcd", ";origin (0=none,1=prompt,2=non-prompt);entries", {HistType::kTH1F, {{3, 0., 3.}}}}, - {"hSparse_d0_mcd", ";m_{D^{0}}^{det};#it{p}_{T,D^{0}}^{det};#it{p}_{T,jet}^{det};z_{||}^{det};#DeltaR^{det};#lambda_{1}^{1,det};#lambda_{2}^{1,det}", {HistType::kTHnSparseF, {{300, 1.7, 2.0}, {200, 0., 100.}, {200, 0., 100.}, {200, 0., 2.}, {200, 0., 1.}, {50, 0., 1.}, {50, 0., 1.}}}}, - - {"hSparse_d0_mcd_calib", ";m_{D^{0}}^{det};#it{p}_{T,D^{0}}^{det};#it{p}_{T,jet}^{det};z_{||}^{det};Origin(D^{0});#lambda_{1}^{1,det};#lambda_{2}^{1,det}", {HistType::kTHnSparseF, {{300, 1.7, 2.0}, {200, 0., 100.}, {200, 0., 100.}, {200, 0., 2.}, {3, -0.5, 2.5}, {50, 0., 1.}, {50, 0., 1.}}}}, + // hSparse_d0_mcd, hSparse_d0_mcd_calib, hSparse_d0_mcd_reflection are booked in init() {"h_d0_mass_mcd_signal", ";m_{K#pi}^{det} (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{300, 1.7, 2.0}}}}, {"h_d0_mass_mcd_reflection", ";m_{K#pi}^{det} (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{300, 1.7, 2.0}}}}, - {"hSparse_d0_mcd_reflection", ";m_{D^{0}}^{det};#it{p}_{T,D^{0}}^{det};#it{p}_{T,jet}^{det};z_{||}^{det};Origin(D^{0});#lambda_{1}^{1,det};#lambda_{2}^{1,det}", {HistType::kTHnSparseF, {{300, 1.7, 2.0}, {200, 0., 100.}, {200, 0., 100.}, {200, 0., 2.}, {3, -0.5, 2.5}, {50, 0., 1.}, {50, 0., 1.}}}}, - // MC PARTICLE LEVEL (MCP) {"h_collisions_mcp", "MCP event status;status;entries", {HistType::kTH1F, {{2, 0., 2.}}}}, @@ -406,14 +419,7 @@ struct JetHFAngularityTask { {"h_jet_pt_response_matrix_allcand", ";#it{p}_{T,jet}^{det} (GeV/#it{c});#it{p}_{T,jet}^{part} (GeV/#it{c}) (all geo candidates)", {HistType::kTH2F, {{200, 0., 200.}, {200, 0., 200.}}}}, // Detector-level vs matched particle-level jet pT - - {"h_jet_pt_response_matrix", ";#it{p}_{T,jet}^{det} (GeV/#it{c});#it{p}_{T,jet}^{part} (GeV/#it{c})", {HistType::kTH2F, {{200, 0., 200.}, {200, 0., 200.}}}}, - {"h_jet_pt_response_matrix_pr", ";#it{p}_{T,jet}^{det} (GeV/#it{c});#it{p}_{T,jet}^{part} (GeV/#it{c}) (prompt)", {HistType::kTH2F, {{200, 0., 200.}, {200, 0., 200.}}}}, - {"h_jet_pt_response_matrix_np", ";#it{p}_{T,jet}^{det} (GeV/#it{c});#it{p}_{T,jet}^{part} (GeV/#it{c}) (non-prompt)", {HistType::kTH2F, {{200, 0., 200.}, {200, 0., 200.}}}}, - - // Axes: pT_jet^det, pT_D0^det, pT_jet^part, pT_D0^part - {"h_response_d0pt_pr", ";#it{p}_{T,jet}^{det} (GeV/#it{c});#it{p}_{T,D^{0}}^{det} (GeV/#it{c});#it{p}_{T,jet}^{part} (GeV/#it{c});#it{p}_{T,D^{0}}^{part} (GeV/#it{c})", {HistType::kTHnSparseF, {{40, 0., 200.}, {50, 0., 50.}, {40, 0., 200.}, {50, 0., 50.}}}}, - {"h_response_d0pt_np", ";#it{p}_{T,jet}^{det} (GeV/#it{c});#it{p}_{T,D^{0}}^{det} (GeV/#it{c});#it{p}_{T,jet}^{part} (GeV/#it{c});#it{p}_{T,D^{0}}^{part} (GeV/#it{c})", {HistType::kTHnSparseF, {{40, 0., 200.}, {50, 0., 50.}, {40, 0., 200.}, {50, 0., 50.}}}}, + // Response matrices (h_jet_pt_response_matrix[_pr/_np], h_response_*) are booked in init() {"h_kineff_gen_num_pr", ";#it{p}_{T,jet}^{part} (GeV/#it{c});#it{p}_{T,D^{0}}^{part} (GeV/#it{c})", {HistType::kTH2F, {{40, 0., 200.}, {50, 0., 50.}}}}, {"h_kineff_gen_den_pr", ";#it{p}_{T,jet}^{part} (GeV/#it{c});#it{p}_{T,D^{0}}^{part} (GeV/#it{c})", {HistType::kTH2F, {{40, 0., 200.}, {50, 0., 50.}}}}, @@ -424,12 +430,6 @@ struct JetHFAngularityTask { {"h_kineff_det_den_pr", ";#it{p}_{T,jet}^{det} (GeV/#it{c});#it{p}_{T,D^{0}}^{det} (GeV/#it{c})", {HistType::kTH2F, {{40, 0., 200.}, {50, 0., 50.}}}}, {"h_kineff_det_num_np", ";#it{p}_{T,jet}^{det} (GeV/#it{c});#it{p}_{T,D^{0}}^{det} (GeV/#it{c})", {HistType::kTH2F, {{40, 0., 200.}, {50, 0., 50.}}}}, {"h_kineff_det_den_np", ";#it{p}_{T,jet}^{det} (GeV/#it{c});#it{p}_{T,D^{0}}^{det} (GeV/#it{c})", {HistType::kTH2F, {{40, 0., 200.}, {50, 0., 50.}}}}, - // ANGULARITY RESPONSE MATRIX - - {"h_response_angularity_pr", ";#it{p}_{T,jet}^{det} (GeV/#it{c});#lambda_{1}^{1,det};#it{p}_{T,jet}^{part} (GeV/#it{c});#lambda_{1}^{1,part}", {HistType::kTHnSparseF, {{40, 0., 200.}, {50, 0., 1.}, {40, 0., 200.}, {50, 0., 1.}}}}, - {"h_response_angularity_np", ";#it{p}_{T,jet}^{det} (GeV/#it{c});#lambda_{1}^{1,det};#it{p}_{T,jet}^{part} (GeV/#it{c});#lambda_{1}^{1,part}", {HistType::kTHnSparseF, {{40, 0., 200.}, {50, 0., 1.}, {40, 0., 200.}, {50, 0., 1.}}}}, - {"h_response_girth_pr", ";#it{p}_{T,jet}^{det} (GeV/#it{c});#lambda_{2}^{1,det};#it{p}_{T,jet}^{part} (GeV/#it{c});#lambda_{2}^{1,part}", {HistType::kTHnSparseF, {{40, 0., 200.}, {50, 0., 1.}, {40, 0., 200.}, {50, 0., 1.}}}}, - {"h_response_girth_np", ";#it{p}_{T,jet}^{det} (GeV/#it{c});#lambda_{2}^{1,det};#it{p}_{T,jet}^{part} (GeV/#it{c});#lambda_{2}^{1,part}", {HistType::kTHnSparseF, {{40, 0., 200.}, {50, 0., 1.}, {40, 0., 200.}, {50, 0., 1.}}}}, {"h_eff_run2_num_pr", ";#it{p}_{T,D^{0}}^{det} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 50.}}}}, {"h_eff_run2_num_np", ";#it{p}_{T,D^{0}}^{det} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 50.}}}}, @@ -441,9 +441,7 @@ struct JetHFAngularityTask { {"MC_CollisionCounter", "N_{collisions};", {HistType::kTH1F, {{4, 0., 4.}}}}, {"MC_JetCounter", "N_{jet};", {HistType::kTH1F, {{4, 0., 4.}}}}, - {"hSparse_d0_mcd_eff", ";m_{D^{0}}^{det};#it{p}_{T,D^{0}}^{det};#it{p}_{T,jet}^{det};z_{||}^{det};Origin(D^{0});Matching status;#lambda_{1}^{1,det};#lambda_{2}^{1,det};IsReflection", {HistType::kTHnSparseF, {{300, 1.7, 2.0}, {200, 0., 100.}, {200, 0., 100.}, {200, 0., 2.}, {3, -0.5, 2.5}, {2, -0.5, 1.5}, {50, 0., 1.}, {50, 0., 1.}, {2, -0.5, 1.5}}}}, - - {"hSparse_d0_mcp_eff", ";#it{p}_{T,D^{0}}^{part};#it{p}_{T,jet}^{part};z_{||}^{part};Origin(D^{0});Matching status;#lambda_{1}^{1,part};#lambda_{2}^{1,part}", {HistType::kTHnSparseF, {{200, 0., 100.}, {200, 0., 100.}, {200, 0., 2.}, {3, -0.5, 2.5}, {2, -0.5, 1.5}, {50, 0., 1.}, {50, 0., 1.}}}}, + // hSparse_d0_mcd_eff and hSparse_d0_mcp_eff are booked in init() }}; @@ -456,6 +454,94 @@ struct JetHFAngularityTask { massD0MCP = jetcandidateutilities::getTablePDGMass(); + // Histograms with configurable axes + // ---- Configurable axes ---- + const AxisSpec axMass{binsMassD0, "m_{D^{0}} (GeV/#it{c}^{2})"}; + const AxisSpec axPtD0{binsPtD0, "#it{p}_{T,D^{0}} (GeV/#it{c})"}; + const AxisSpec axZ{binsZParallel, "z_{||}"}; + const AxisSpec axDR{binsDeltaR, "#DeltaR"}; + const AxisSpec axL11{binsLambda11, "#lambda_{1}^{1}"}; + const AxisSpec axL12{binsLambda12, "#lambda_{2}^{1}"}; + + const AxisSpec axPtJetData{binsPtJetData, "#it{p}_{T,jet} (GeV/#it{c})"}; + const AxisSpec axPtJetDet{binsPtJetMC, "#it{p}_{T,jet}^{det} (GeV/#it{c})"}; + const AxisSpec axPtJetPart{binsPtJetMC, "#it{p}_{T,jet}^{part} (GeV/#it{c})"}; + + const AxisSpec axPtD0Det{binsPtD0Resp, "#it{p}_{T,D^{0}}^{det} (GeV/#it{c})"}; + const AxisSpec axPtD0Part{binsPtD0Resp, "#it{p}_{T,D^{0}}^{part} (GeV/#it{c})"}; + const AxisSpec axL11Det{binsLambda11Resp, "#lambda_{1}^{1,det}"}; + const AxisSpec axL11Part{binsLambda11Resp, "#lambda_{1}^{1,part}"}; + const AxisSpec axL12Det{binsLambda12Resp, "#lambda_{2}^{1,det}"}; + const AxisSpec axL12Part{binsLambda12Resp, "#lambda_{2}^{1,part}"}; + + // ---- Fixed categorical axes (not configurable) ---- + const AxisSpec axOrigin{3, -0.5, 2.5, "Origin(D^{0})"}; + const AxisSpec axMatch{2, -0.5, 1.5, "Matching status"}; + const AxisSpec axRefl{2, -0.5, 1.5, "IsReflection"}; + + // ---- DATA ---- + registry.add("hSparse_d0", "hSparse_d0", HistType::kTHnSparseF, + {axMass, axPtD0, axPtJetData, axZ, axDR, axL11, axL12}); + + // ---- MCD ---- + registry.add("hSparse_d0_mcd", "hSparse_d0_mcd", HistType::kTHnSparseF, + {axMass, axPtD0, axPtJetDet, axZ, axDR, axL11, axL12}); + registry.add("hSparse_d0_mcd_calib", "hSparse_d0_mcd_calib", HistType::kTHnSparseF, + {axMass, axPtD0, axPtJetDet, axZ, axOrigin, axL11, axL12}); + registry.add("hSparse_d0_mcd_reflection", "hSparse_d0_mcd_reflection", HistType::kTHnSparseF, + {axMass, axPtD0, axPtJetDet, axZ, axOrigin, axL11, axL12}); + + // ---- Jet pT response (detector level vs. matched particle level) ---- + registry.add("h_jet_pt_response_matrix", "", HistType::kTH2F, {axPtJetDet, axPtJetPart}); + registry.add("h_jet_pt_response_matrix_pr", "prompt", HistType::kTH2F, {axPtJetDet, axPtJetPart}); + registry.add("h_jet_pt_response_matrix_np", "non-prompt", HistType::kTH2F, {axPtJetDet, axPtJetPart}); + + // ---- D0 pT response. Axes: pT_jet^det, pT_D0^det, pT_jet^part, pT_D0^part ---- + registry.add("h_response_d0pt_pr", "", HistType::kTHnSparseF, + {axPtJetDet, axPtD0Det, axPtJetPart, axPtD0Part}); + registry.add("h_response_d0pt_np", "", HistType::kTHnSparseF, + {axPtJetDet, axPtD0Det, axPtJetPart, axPtD0Part}); + + // ---- Angularity / girth response matrices ---- + registry.add("h_response_angularity_pr", "", HistType::kTHnSparseF, + {axPtJetDet, axL11Det, axPtJetPart, axL11Part}); + registry.add("h_response_angularity_np", "", HistType::kTHnSparseF, + {axPtJetDet, axL11Det, axPtJetPart, axL11Part}); + registry.add("h_response_girth_pr", "", HistType::kTHnSparseF, + {axPtJetDet, axL12Det, axPtJetPart, axL12Part}); + registry.add("h_response_girth_np", "", HistType::kTHnSparseF, + {axPtJetDet, axL12Det, axPtJetPart, axL12Part}); + + // ---- Particle-level efficiency, consistent with the response matrices ---- + registry.add("h_eff_num_d0pt_pr", "", HistType::kTH2F, {axPtJetPart, axPtD0Part}); + registry.add("h_eff_num_d0pt_np", "", HistType::kTH2F, {axPtJetPart, axPtD0Part}); + registry.add("h_eff_den_d0pt_pr", "", HistType::kTH2F, {axPtJetPart, axPtD0Part}); + registry.add("h_eff_den_d0pt_np", "", HistType::kTH2F, {axPtJetPart, axPtD0Part}); + + registry.add("h_eff_num_angularity_pr", "", HistType::kTH2F, {axPtJetPart, axL11Part}); + registry.add("h_eff_num_angularity_np", "", HistType::kTH2F, {axPtJetPart, axL11Part}); + registry.add("h_eff_den_angularity_pr", "", HistType::kTH2F, {axPtJetPart, axL11Part}); + registry.add("h_eff_den_angularity_np", "", HistType::kTH2F, {axPtJetPart, axL11Part}); + + registry.add("h_eff_num_girth_pr", "", HistType::kTH2F, {axPtJetPart, axL12Part}); + registry.add("h_eff_num_girth_np", "", HistType::kTH2F, {axPtJetPart, axL12Part}); + registry.add("h_eff_den_girth_pr", "", HistType::kTH2F, {axPtJetPart, axL12Part}); + registry.add("h_eff_den_girth_np", "", HistType::kTH2F, {axPtJetPart, axL12Part}); + + // ---- Efficiency sparses ---- + registry.add("hSparse_d0_mcd_eff", "hSparse_d0_mcd_eff", HistType::kTHnSparseF, + {axMass, axPtD0, axPtJetDet, axZ, axOrigin, axMatch, axL11, axL12, axRefl}); + registry.add("hSparse_d0_mcp_eff", "hSparse_d0_mcp_eff", HistType::kTHnSparseF, + {axPtD0, axPtJetPart, axZ, axOrigin, axMatch, axL11, axL12}); + + // ---- Efficiency sparses, candidate-match version (match axis = candidate-match flag) ---- + registry.add("hSparse_d0_mcd_eff_cand", "hSparse_d0_mcd_eff_cand", HistType::kTHnSparseF, + {axMass, axPtD0, axPtJetDet, axZ, axOrigin, axMatch, axL11, axL12, axRefl}); + registry.add("hSparse_d0_mcp_eff_cand", "hSparse_d0_mcp_eff_cand", HistType::kTHnSparseF, + {axPtD0, axPtJetPart, axZ, axOrigin, axMatch, axL11, axL12}); + + // Bin labels + // ---- DATA bin labels ---- auto hColl = registry.get(HIST("h_collisions")); hColl->GetXaxis()->SetBinLabel(AllCollisions, "all"); @@ -541,6 +627,23 @@ struct JetHFAngularityTask { hSparseMCPEff->GetAxis(4)->SetBinLabel(1, "Unmatched"); hSparseMCPEff->GetAxis(4)->SetBinLabel(2, "Matched"); + // ---- Candidate-match efficiency sparse bin labels ---- + auto hSparseMCDEffCand = registry.get(HIST("hSparse_d0_mcd_eff_cand")); + hSparseMCDEffCand->GetAxis(4)->SetBinLabel(1, "none/bkg"); + hSparseMCDEffCand->GetAxis(4)->SetBinLabel(2, "prompt"); + hSparseMCDEffCand->GetAxis(4)->SetBinLabel(3, "non-prompt"); + hSparseMCDEffCand->GetAxis(5)->SetBinLabel(1, "Unmatched (cand)"); + hSparseMCDEffCand->GetAxis(5)->SetBinLabel(2, "Matched (cand)"); + hSparseMCDEffCand->GetAxis(8)->SetBinLabel(1, "Not reflection"); + hSparseMCDEffCand->GetAxis(8)->SetBinLabel(2, "Reflection"); + + auto hSparseMCPEffCand = registry.get(HIST("hSparse_d0_mcp_eff_cand")); + hSparseMCPEffCand->GetAxis(3)->SetBinLabel(1, "none/bkg"); + hSparseMCPEffCand->GetAxis(3)->SetBinLabel(2, "prompt"); + hSparseMCPEffCand->GetAxis(3)->SetBinLabel(3, "non-prompt"); + hSparseMCPEffCand->GetAxis(4)->SetBinLabel(1, "Unmatched (cand)"); + hSparseMCPEffCand->GetAxis(4)->SetBinLabel(2, "Matched (cand)"); + // ---- Reflection sparse bin labels ---- auto hSparseMCDRefl = registry.get(HIST("hSparse_d0_mcd_reflection")); hSparseMCDRefl->GetAxis(4)->SetBinLabel(1, "none/bkg"); @@ -1100,7 +1203,7 @@ struct JetHFAngularityTask { } if (isCleanMatched) { - registry.fill(HIST("h_jet_pt_response_matrix"), mcdjet.pt(), matchedPt); + // registry.fill(HIST("h_jet_pt_response_matrix"), mcdjet.pt(), matchedPt); registry.fill(HIST("h_jet_matching_dr_mcd"), matchedDR); } @@ -1151,31 +1254,63 @@ struct JetHFAngularityTask { girth, isReflectionFlag); - if (originMcd == kOriginMcPrompt) { - registry.fill(HIST("h_eff_run2_num_pr"), mcdD0cand.pt()); - registry.fill(HIST("h_kineff_det_den_pr"), mcdjet.pt(), mcdD0cand.pt()); - if (isGeoMatched) { - registry.fill(HIST("h_kineff_det_num_pr"), mcdjet.pt(), mcdD0cand.pt()); - } - } else if (originMcd == kOriginMcNonPrompt) { - registry.fill(HIST("h_eff_run2_num_np"), mcdD0cand.pt()); - registry.fill(HIST("h_kineff_det_den_np"), mcdjet.pt(), mcdD0cand.pt()); - if (isGeoMatched) { - registry.fill(HIST("h_kineff_det_num_np"), mcdjet.pt(), mcdD0cand.pt()); + // Same entry, but the match axis carries the candidate-match flag + registry.fill(HIST("hSparse_d0_mcd_eff_cand"), + mcdD0cand.m(), + mcdD0cand.pt(), + mcdjet.pt(), + zParallelMCD, + originMcd, + static_cast(isCandMatched), + angularity, + girth, + isReflectionFlag); + + // Reflection rows are a second copy of the same true D0: skip them (switchable). + const bool skipReflection = rejectReflections && isReflection; + + if (!skipReflection) { + if (originMcd == kOriginMcPrompt) { + registry.fill(HIST("h_eff_run2_num_pr"), mcdD0cand.pt()); + registry.fill(HIST("h_kineff_det_den_pr"), mcdjet.pt(), mcdD0cand.pt()); + if (isGeoMatched) { + registry.fill(HIST("h_kineff_det_num_pr"), mcdjet.pt(), mcdD0cand.pt()); + } + } else if (originMcd == kOriginMcNonPrompt) { + registry.fill(HIST("h_eff_run2_num_np"), mcdD0cand.pt()); + registry.fill(HIST("h_kineff_det_den_np"), mcdjet.pt(), mcdD0cand.pt()); + if (isGeoMatched) { + registry.fill(HIST("h_kineff_det_num_np"), mcdjet.pt(), mcdD0cand.pt()); + } } } - if (isCleanMatched) { + if (isCleanMatched && !skipReflection) { + registry.fill(HIST("h_jet_pt_response_matrix"), mcdjet.pt(), matchedPt); + + // The matched particle-level jet must lie in the range covered by the denominator. + const bool matchedInDenominatorRange = (matchedPt > jetPtMin); + if (originMcd == kOriginMcPrompt) { registry.fill(HIST("h_jet_pt_response_matrix_pr"), mcdjet.pt(), matchedPt); registry.fill(HIST("h_response_angularity_pr"), mcdjet.pt(), angularity, matchedPt, matchedAngularity); registry.fill(HIST("h_response_girth_pr"), mcdjet.pt(), girth, matchedPt, matchedGirth); registry.fill(HIST("h_response_d0pt_pr"), mcdjet.pt(), mcdD0cand.pt(), matchedPt, matchedD0Pt); + if (matchedInDenominatorRange) { + registry.fill(HIST("h_eff_num_d0pt_pr"), matchedPt, matchedD0Pt); + registry.fill(HIST("h_eff_num_angularity_pr"), matchedPt, matchedAngularity); + registry.fill(HIST("h_eff_num_girth_pr"), matchedPt, matchedGirth); + } } else if (originMcd == kOriginMcNonPrompt) { registry.fill(HIST("h_jet_pt_response_matrix_np"), mcdjet.pt(), matchedPt); registry.fill(HIST("h_response_angularity_np"), mcdjet.pt(), angularity, matchedPt, matchedAngularity); registry.fill(HIST("h_response_girth_np"), mcdjet.pt(), girth, matchedPt, matchedGirth); registry.fill(HIST("h_response_d0pt_np"), mcdjet.pt(), mcdD0cand.pt(), matchedPt, matchedD0Pt); + if (matchedInDenominatorRange) { + registry.fill(HIST("h_eff_num_d0pt_np"), matchedPt, matchedD0Pt); + registry.fill(HIST("h_eff_num_angularity_np"), matchedPt, matchedAngularity); + registry.fill(HIST("h_eff_num_girth_np"), matchedPt, matchedGirth); + } } } @@ -1219,10 +1354,6 @@ struct JetHFAngularityTask { registry.fill(HIST("MC_JetCounter"), getValFromBin(BinMCJetCntr::ParticleLevelJetInMCCollision)); - auto mcpD0cand = mcpjet.template candidates_first_as(); - - const int originMcp = mcpD0cand.originMcGen(); // 0 none/bkg, 1 prompt, 2 non-prompt - bool mcpIsCleanMatched = false; if (mcpjet.has_matchedJetGeo() && mcpjet.has_matchedJetCand()) { int mcpGeoTarget = -1; @@ -1242,6 +1373,10 @@ struct JetHFAngularityTask { if (mcpIsCleanMatched) { registry.fill(HIST("MC_JetCounter"), getValFromBin(BinMCJetCntr::ParticleLevelJetWithMatchedCandidate)); } + // ---- First generated D0 of the jet only: one entry per MCP jet ---- + auto mcpD0cand = mcpjet.template candidates_first_as(); + + const int originMcp = mcpD0cand.originMcGen(); // 0 none/bkg, 1 prompt, 2 non-prompt const std::array jetMomMCP{mcpjet.px(), mcpjet.py(), mcpjet.pz()}; const std::array candMomMCP{mcpD0cand.px(), mcpD0cand.py(), mcpD0cand.pz()}; @@ -1252,6 +1387,10 @@ struct JetHFAngularityTask { const float angularityMCP = computeLambda(mcpjet, mcpJetParticles, mcpJetCandidates, 1.f, 1.f); // λ_1^1 const float girthMCP = computeLambda(mcpjet, mcpJetParticles, mcpJetCandidates, 2.f, 1.f); // λ_2^1 + const int isCandMatchedMCP = mcpjet.has_matchedJetCand() ? 1 : 0; + const bool mcpIsGeoMatched = mcpjet.has_matchedJetGeo(); + + // clean-match version registry.fill(HIST("hSparse_d0_mcp_eff"), mcpD0cand.pt(), mcpjet.pt(), @@ -1261,7 +1400,26 @@ struct JetHFAngularityTask { angularityMCP, girthMCP); - const bool mcpIsGeoMatched = mcpjet.has_matchedJetGeo(); + // candidate-match version + registry.fill(HIST("hSparse_d0_mcp_eff_cand"), + mcpD0cand.pt(), + mcpjet.pt(), + zParallelMCP, + originMcp, + isCandMatchedMCP, + angularityMCP, + girthMCP); + + if (originMcp == kOriginMcPrompt) { + registry.fill(HIST("h_eff_den_d0pt_pr"), mcpjet.pt(), mcpD0cand.pt()); + registry.fill(HIST("h_eff_den_angularity_pr"), mcpjet.pt(), angularityMCP); + registry.fill(HIST("h_eff_den_girth_pr"), mcpjet.pt(), girthMCP); + } else if (originMcp == kOriginMcNonPrompt) { + registry.fill(HIST("h_eff_den_d0pt_np"), mcpjet.pt(), mcpD0cand.pt()); + registry.fill(HIST("h_eff_den_angularity_np"), mcpjet.pt(), angularityMCP); + registry.fill(HIST("h_eff_den_girth_np"), mcpjet.pt(), girthMCP); + } + if (originMcp == kOriginMcPrompt) { registry.fill(HIST("h_eff_run2_den_pr"), mcpD0cand.pt()); registry.fill(HIST("h_kineff_gen_den_pr"), mcpjet.pt(), mcpD0cand.pt()); diff --git a/PWGJE/Tasks/jetHadronRecoil.cxx b/PWGJE/Tasks/jetHadronRecoil.cxx index 0cfbcd2ea8d..8db28c34a44 100644 --- a/PWGJE/Tasks/jetHadronRecoil.cxx +++ b/PWGJE/Tasks/jetHadronRecoil.cxx @@ -137,8 +137,8 @@ struct JetHadronRecoil { void init(InitContext const&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); jetReclusterer.isReclustering = true; diff --git a/PWGJE/Tasks/jetHadronsPid.cxx b/PWGJE/Tasks/jetHadronsPid.cxx index 2bdce5fb820..e2522ef9c23 100644 --- a/PWGJE/Tasks/jetHadronsPid.cxx +++ b/PWGJE/Tasks/jetHadronsPid.cxx @@ -104,7 +104,10 @@ struct JetHadronsPid { Configurable deltaEtaEdge{"deltaEtaEdge", 0.05, "eta gap from the edge"}; Configurable requirePvContributor{"requirePvContributor", false, "require that the track is a PV contributor"}; - Configurable minTpcNcrossedRows{"minTpcNcrossedRows", 70, "minimum number of TPC crossed pad rows"}; + Configurable minTpcNcrossedRows{"minTpcNcrossedRows", 100, "minimum number of TPC crossed pad rows"}; + Configurable minTpcNclusters{"minTpcNclusters", 110, "Minimum number of clusters in TPC"}; + Configurable minItsNclusters{"minItsNclusters", 7, "Minimum number of clusters in ITS"}; + Configurable minItsIbNclusters{"minItsIbNclusters", 3, "Minimum number of clusters in inner barrel (max 3) of ITS"}; Configurable minChiSquareTpc{"minChiSquareTpc", 0.0, "minimum TPC chi^2/Ncls"}; Configurable maxChiSquareTpc{"maxChiSquareTpc", 4.0, "maximum TPC chi^2/Ncls"}; Configurable maxChiSquareIts{"maxChiSquareIts", 36.0, "maximum ITS chi^2/Ncls"}; @@ -112,8 +115,10 @@ struct JetHadronsPid { Configurable maxPt{"maxPt", 4.0, "maximum pt of the tracks for PID analysis"}; Configurable minEta{"minEta", -0.8, "minimum eta"}; Configurable maxEta{"maxEta", +0.8, "maximum eta"}; - Configurable maxDcaxy{"maxDcaxy", 0.2, "Maximum DCAxy"}; - Configurable maxDcaz{"maxDcaz", 0.1, "Maximum DCAz"}; + Configurable maxDcaxy{"maxDcaxy", 0.004, "constant term of the DCAxy cut -> |DCAxy| < maxDcaxy + dcaXyPtPart/pT, in cm"}; + Configurable maxDcaz{"maxDcaz", 0.004, "constant term of the DCAz cut -> |DCAz| < maxDcaz + dcaZPtPart/pT, in cm"}; + Configurable dcaXyPtPart{"dcaXyPtPart", 0.013, "pt-dependent term of the DCAxy cut -> |DCAxy| < maxDcaxy + dcaXyPtPart/pT"}; + Configurable dcaZPtPart{"dcaZPtPart", 0.013, "pt-dependent term of the DCAz cut -> |DCAz| < maxDcaz + dcaZPtPart/pT"}; Configurable setMCDefaultItsParams{"setMCDefaultItsParams", true, "set MC default parameters"}; struct : ConfigurableGroup { @@ -190,7 +195,7 @@ struct JetHadronsPid { itsResponse.setMCDefaultParameters(); } - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); // --- DATA: PURE TRACKS --- registryData.add("data/pure/general/tpc_signal_vs_p", "TPC n#sigma_{#pi} vs transverse momentum ;#it{p} (GeV/#it{c});TPC d#it{E}/d#it{x} signal (a.u.)", HistType::kTH2F, {axis.axisMomentum, axis.axisTPCSignal}); @@ -1139,6 +1144,7 @@ struct JetHadronsPid { constexpr int ClosestMatch = 0; constexpr int ExclusiveMatch = 1; constexpr int RejectionBased = 2; + constexpr int NoRejection = 3; double const buffer = 999.0; double pt = track.pt(); @@ -1208,8 +1214,17 @@ struct JetHadronsPid { bool isPrMatch = (dForProtonsPr <= cfg.nSigmaCutPr); PidResult res{.isPion = false, .isKaon = false, .isProton = false}; - - if (cfg.pidMethod == ClosestMatch) { + if (cfg.pidMethod == NoRejection) { + if (isPiMatch) { + res.isPion = true; + } + if (isKaMatch) { + res.isKaon = true; + } + if (isPrMatch) { + res.isProton = true; + } + } else if (cfg.pidMethod == ClosestMatch) { if (isPiMatch && dForPionsPi < dForPionsKa && dForPionsPi < dForPionsPr) { res.isPion = true; } @@ -1269,6 +1284,15 @@ struct JetHadronsPid { if (track.tpcNClsCrossedRows() < minTpcNcrossedRows) { return false; } + if (track.tpcNClsFound() < minTpcNclusters) { + return false; + } + if (track.itsNCls() < minItsNclusters) { + return false; + } + if (track.itsNClsInnerBarrel() < minItsIbNclusters) { + return false; + } if (!track.passedTPCCrossedRowsOverNCls()) { return false; } @@ -1287,7 +1311,9 @@ struct JetHadronsPid { if (track.pt() < minPt || track.pt() > maxPt) { return false; } - if (std::abs(track.dcaXY()) > maxDcaxy || std::abs(track.dcaZ()) > maxDcaz) { + const double maxDcaxyCut = maxDcaxy + dcaXyPtPart / track.pt(); + const double maxDcazCut = maxDcaz + dcaZPtPart / track.pt(); + if (std::abs(track.dcaXY()) > maxDcaxyCut || std::abs(track.dcaZ()) > maxDcazCut) { return false; } return true; diff --git a/PWGJE/Tasks/jetLundReclustering.cxx b/PWGJE/Tasks/jetLundReclustering.cxx index 95b5b027931..f85f34497ff 100644 --- a/PWGJE/Tasks/jetLundReclustering.cxx +++ b/PWGJE/Tasks/jetLundReclustering.cxx @@ -67,7 +67,7 @@ struct JetLundReclustering { void init(InitContext const&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); registry.add("PrimaryLundPlane_kT", "Primary Lund 3D plane;ln(R/Delta);ln(k_{t}/GeV);{p}_{t}", {HistType::kTH3F, {{100, 0, 10}, {100, -10, 10}, {20, 0, 200}}}); registry.add("PrimaryLundPlane_z", "Primary Lund 3D plane;ln(R/Delta);ln(1/z);{p}_{t}", {HistType::kTH3F, {{100, 0, 10}, {100, 0, 10}, {20, 0, 200}}}); diff --git a/PWGJE/Tasks/jetOutlierQA.cxx b/PWGJE/Tasks/jetOutlierQA.cxx index a9cf7fe6714..d37047e6008 100644 --- a/PWGJE/Tasks/jetOutlierQA.cxx +++ b/PWGJE/Tasks/jetOutlierQA.cxx @@ -93,8 +93,8 @@ struct JetOutlierQATask { void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); auto jetPtTemp = 0.0; jetPtBins.push_back(jetPtTemp); @@ -358,10 +358,10 @@ struct JetOutlierQATask { } } registry.fill(HIST("h3_jet_pt_frac_pt_ambiguous_pt_hat"), jet.pt(), pt_amb / pt_total, pTHat, weight); - registry.fill(HIST("h3_jet_pt_frac_constituents_ambiguous_pt_hat"), jet.pt(), double(nAmbTracks) / double(jet.template tracks_as().size()), pTHat, weight); + registry.fill(HIST("h3_jet_pt_frac_constituents_ambiguous_pt_hat"), jet.pt(), static_cast(nAmbTracks) / static_cast(jet.template tracks_as().size()), pTHat, weight); registry.fill(HIST("h3_jet_pt_frac_pt_unmatched_particle_pt_hat"), jet.pt(), pt_unmatched / pt_total, pTHat, weight); - registry.fill(HIST("h3_jet_pt_frac_constituents_unmatched_particle_pt_hat"), jet.pt(), double(nUnmatchedTracks) / double(jet.template tracks_as().size()), pTHat, weight); + registry.fill(HIST("h3_jet_pt_frac_constituents_unmatched_particle_pt_hat"), jet.pt(), static_cast(nUnmatchedTracks) / static_cast(jet.template tracks_as().size()), pTHat, weight); } void processJetsAmbiguous(soa::Filtered>::iterator const& collision, @@ -607,8 +607,8 @@ struct JetOutlierQATask { if (std::abs(diffColl) < 6) { float eventWeightOutlier = collisionOutlier.mcCollision().weight(); double pTHatOutlier = collisionOutlier.mcCollision().ptHard(); - registry.fill(HIST("h2_neighbour_pt_hat_outlier"), float(diffColl + 0.1), pTHatOutlier, eventWeightOutlier); - registry.fill(HIST("h2_neighbour_track_pt_outlier"), float(diffColl + 0.1), track.pt(), eventWeightOutlier); + registry.fill(HIST("h2_neighbour_pt_hat_outlier"), static_cast(diffColl + 0.1), pTHatOutlier, eventWeightOutlier); + registry.fill(HIST("h2_neighbour_track_pt_outlier"), static_cast(diffColl + 0.1), track.pt(), eventWeightOutlier); } } // now match tracks to their MC particle, check the MC collision ID of this particle, and @@ -658,20 +658,20 @@ struct JetOutlierQATask { registry.fill(HIST("h2_outlier_event_Ntracks_different_selected_JJ"), pTHat, nJJdifferentSelected); registry.fill(HIST("h2_outlier_event_Ntracks_different_selected_MB"), pTHat, nMBdifferentSelected); registry.fill(HIST("h2_outlier_event_Ntracks_same_selected_JJ"), pTHat, nJJsameSelected); - registry.fill(HIST("h2_outlier_event_tracks_frac_different_selected_MB"), pTHat, float(nMBdifferentSelected) / float(nJJdifferentSelected + nJJsameSelected + nMBdifferentSelected)); - registry.fill(HIST("h2_outlier_event_tracks_frac_different_JJ"), pTHat, float(nJJdifferent) / float(nJJdifferent + nJJsame + nMBdifferent)); - registry.fill(HIST("h2_outlier_event_tracks_frac_different_MB"), pTHat, float(nMBdifferent) / float(nJJdifferent + nJJsame + nMBdifferent)); - registry.fill(HIST("h2_outlier_event_tracks_frac_different_selected_JJ"), pTHat, float(nJJdifferentSelected) / float(nJJdifferentSelected + nJJsameSelected + nMBdifferentSelected)); - registry.fill(HIST("h2_outlier_event_tracks_frac_different_selected_MB"), pTHat, float(nMBdifferentSelected) / float(nJJdifferentSelected + nJJsameSelected + nMBdifferentSelected)); + registry.fill(HIST("h2_outlier_event_tracks_frac_different_selected_MB"), pTHat, static_cast(nMBdifferentSelected) / static_cast(nJJdifferentSelected + nJJsameSelected + nMBdifferentSelected)); + registry.fill(HIST("h2_outlier_event_tracks_frac_different_JJ"), pTHat, static_cast(nJJdifferent) / static_cast(nJJdifferent + nJJsame + nMBdifferent)); + registry.fill(HIST("h2_outlier_event_tracks_frac_different_MB"), pTHat, static_cast(nMBdifferent) / static_cast(nJJdifferent + nJJsame + nMBdifferent)); + registry.fill(HIST("h2_outlier_event_tracks_frac_different_selected_JJ"), pTHat, static_cast(nJJdifferentSelected) / static_cast(nJJdifferentSelected + nJJsameSelected + nMBdifferentSelected)); + registry.fill(HIST("h2_outlier_event_tracks_frac_different_selected_MB"), pTHat, static_cast(nMBdifferentSelected) / static_cast(nJJdifferentSelected + nJJsameSelected + nMBdifferentSelected)); // now check where outlier comes from // LOG(info) <<"outlier comes from " << (mcCollisionIDOutlier == mcCollisionIDcoll ? "same" : "different") << " event which is a " << (subGenIDOutlier == jetderiveddatautilities::JCollisionSubGeneratorId::mbGap ? " MB-gap" : " jet-jet") << " collision with mcCollisionID = " << mcCollisionIDOutlier; - registry.fill(HIST("h2_outlier_collision_ID_difference"), pTHat, float(outlierCollisionIDDifference)); + registry.fill(HIST("h2_outlier_collision_ID_difference"), pTHat, static_cast(outlierCollisionIDDifference)); // if outlier comes from different collision, check which type and set flags if (mcCollisionIDOutlier != mcCollisionIDcoll && subGenIDOutlier != jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { isOutlierEventDifferentJJCollision = true; float deltaZ = collisionMCOutlier.begin().posZ() - collision.mcCollision().posZ(); registry.fill(HIST("h_DeltaZ_Outlier"), deltaZ); - registry.fill(HIST("h2_DeltaZ_Outlier_difference"), deltaZ, float(outlierCollisionIDDifference)); + registry.fill(HIST("h2_DeltaZ_Outlier_difference"), deltaZ, static_cast(outlierCollisionIDDifference)); } else if (mcCollisionIDOutlier != mcCollisionIDcoll && subGenIDOutlier == jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { isOutlierEventDifferentMBCollision = true; } @@ -683,7 +683,7 @@ struct JetOutlierQATask { registry.fill(HIST("h_track_pt_same_collision"), track.pt(), weight); registry.fill(HIST("h_track_pt_eta_same_collision"), track.pt(), track.eta(), weight); registry.fill(HIST("h_track_pt_phi_same_collision"), track.pt(), track.phi(), weight); - registry.fill(HIST("h2_collision_ID_difference_same_collision"), pTHat, float(outlierCollisionIDDifference)); + registry.fill(HIST("h2_collision_ID_difference_same_collision"), pTHat, static_cast(outlierCollisionIDDifference)); registry.fill(HIST("h_pt_hard_track_pt_same_collision"), pTHat != 0.0 ? track.pt() / pTHat : 0.0, track.pt(), weight); // include selection on pThat of particle @@ -699,7 +699,7 @@ struct JetOutlierQATask { registry.fill(HIST("h_track_pt_no_JJ_different"), track.pt(), weight); registry.fill(HIST("h_track_pt_eta_no_JJ_different"), track.pt(), track.eta(), weight); registry.fill(HIST("h_track_pt_phi_no_JJ_different"), track.pt(), track.phi(), weight); - registry.fill(HIST("h2_collision_ID_difference_no_JJ_different"), pTHat, float(outlierCollisionIDDifference)); + registry.fill(HIST("h2_collision_ID_difference_no_JJ_different"), pTHat, static_cast(outlierCollisionIDDifference)); } else { registry.fill(HIST("h_track_pt_no_JJ_different_rejected"), track.pt(), weight); } @@ -711,8 +711,8 @@ struct JetOutlierQATask { if (std::abs(diffColl) < 6) { // LOG(info) << "pThat = " << pTHat << "pThat neighbour = "<(diffColl + 0.1), pTHatOutlier, eventWeightOutlier); + registry.fill(HIST("h2_neighbour_track_pt_all"), static_cast(diffColl + 0.1), track.pt(), eventWeightOutlier); } } } diff --git a/PWGJE/Tasks/jetPlanarFlow.cxx b/PWGJE/Tasks/jetPlanarFlow.cxx index 480dcc69a53..507d4ca7aee 100644 --- a/PWGJE/Tasks/jetPlanarFlow.cxx +++ b/PWGJE/Tasks/jetPlanarFlow.cxx @@ -134,9 +134,9 @@ struct JetPlanarFlowTask { void init(o2::framework::InitContext&) { - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - particleSelection = static_cast(particleSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + particleSelection = particleSelections; precisionMask = 0xFFFFFC00; AxisSpec centralityAxis = {1200, -10., 110., "Centrality"}; diff --git a/PWGJE/Tasks/jetShape.cxx b/PWGJE/Tasks/jetShape.cxx index c18834ab478..335ced51b66 100644 --- a/PWGJE/Tasks/jetShape.cxx +++ b/PWGJE/Tasks/jetShape.cxx @@ -38,8 +38,12 @@ #include #include +#include #include #include +#include +#include +#include #include #include @@ -85,13 +89,30 @@ struct JetShapeTask { Configurable tpcNSigmaPrMax{"tpcNSigmaPrMax", 2.0f, "Max value of tpcNsigmaProton"}; Configurable tpcNSigmaPiMin{"tpcNSigmaPiMin", -2.0f, "Min value of tpcNsigmaPion"}; Configurable tpcNSigmaPiMax{"tpcNSigmaPiMax", 2.0f, "Max value of tpcNsigmaPion"}; - Configurable randomConeDeltaPhiMin{"randomConeDeltaPhiMin", static_cast(o2::constants::math::PIThird), "Minimum delta phi used only when randomConeFullAzimuth=false"}; - Configurable randomConeDeltaPhiMax{"randomConeDeltaPhiMax", static_cast(2.0f * o2::constants::math::PIThird), "Maximum delta phi used only when randomConeFullAzimuth=false"}; + Configurable randomConeDeltaPhiMin{"randomConeDeltaPhiMin", +o2::constants::math::PIThird, "Minimum delta phi used only when randomConeFullAzimuth=false"}; + Configurable randomConeDeltaPhiMax{"randomConeDeltaPhiMax", 2.0f * o2::constants::math::PIThird, "Maximum delta phi used only when randomConeFullAzimuth=false"}; Configurable enableRandomCone{"enableRandomCone", true, "Fill one accepted random cone per selected jet"}; Configurable randomConeFullAzimuth{"randomConeFullAzimuth", true, "Generate random cones over the full azimuth instead of the old perpendicular-side window"}; Configurable randomConeMaxAttempts{"randomConeMaxAttempts", 100, "Maximum attempts for each random cone"}; Configurable randomConeSeed{"randomConeSeed", 0u, "TRandom3 seed; 0 lets ROOT choose a unique seed"}; Configurable fillDetailedQA{"fillDetailedQA", false, "Fill high-volume dE/dx, beta and distance QA histograms"}; + Configurable fillPidNsigmaQA{"fillPidNsigmaQA", true, "Fill the light centrality / TOF-matching / nSigma_TOF histograms used to validate the normalisation and the PID efficiency on data"}; + Configurable fillPid2D{"fillPid2D", true, "Fill the dense (p, nSigma_TPC^p, nSigma_TOF^p) map of inclusive tracks with TOF of ALL selected events (no jet bias), without any PID cut"}; + Configurable fillPid2DJet{"fillPid2DJet", true, "Fill dense (p, nSigma_TPC^p, nSigma_TOF^p, r x jet pT) maps for signal and perpendicular cones, plus the inclusive map of the same events, without any PID cut (shared-shape 2D fit)"}; + Configurable pid2DCentMin{"pid2DCentMin", 0.f, "Lower centrality edge for the pid2D jet maps"}; + Configurable pid2DCentMax{"pid2DCentMax", 10.f, "Upper centrality edge for the pid2D jet maps"}; + Configurable pid2DPMin{"pid2DPMin", 2.0f, "Lower momentum edge of all pid2D maps (GeV/c)"}; + Configurable pid2DPMax{"pid2DPMax", 7.4f, "Upper momentum edge of all pid2D maps (GeV/c); p_T <= 5 at |eta| < 0.9 needs p <= 7.2"}; + Configurable fillPid2DMismatch{"fillPid2DMismatch", true, "Fill TOF-mismatch templates: nSigma_TOF^p recomputed with the TOF time of another (random / nearest) TOF track of the same collision"}; + Configurable fillPid2DTpcOnly{"fillPid2DTpcOnly", true, "Fill the TPC-only (p, nSigma_TPC^p, hasTOF, region) map without TOF requirement (TOF matching of p+K relative to pi on data)"}; + Configurable fillPid2DMc{"fillPid2DMc", true, "MC: fill the pid2D maps split by true species, origin and TOF-match quality (closure of the 2D fit)"}; + Configurable pid2DMcTofBadNSigma{"pid2DMcTofBadNSigma", 5.f, "MC: flag the TOF hit as mismatched when |nSigma_TOF| under the true mass hypothesis exceeds this"}; + Configurable fillTofMismatchQA{"fillTofMismatchQA", true, "Fill the wide-range nSigma_TOF histograms used to measure the TOF-mismatch pedestal; the data side also needs fillPidNsigmaQA"}; + Configurable nSigmaWideMid{"nSigmaWideMid", 100.0f, "Boundary between the medium and the coarse part of the wide nSigma_TOF axis"}; + Configurable nSigmaWideMax{"nSigmaWideMax", 1000.0f, "Outer edge of the wide nSigma_TOF axis; one bunch crossing is about 625 sigma"}; + Configurable nBinsNSigmaWideMid{"nBinsNSigmaWideMid", 18, "Bins per side between nSigmaMax and nSigmaWideMid"}; + Configurable nBinsNSigmaWideOuter{"nBinsNSigmaWideOuter", 18, "Bins per side between nSigmaWideMid and nSigmaWideMax"}; + Configurable nBinsPForWide{"nBinsPForWide", 80, "Momentum bins of the wide nSigma_TOF histograms; nSigma depends on p, not on p_T, so these have to be fine"}; Configurable applyCentralityCut{"applyCentralityCut", true, "Apply centralityMinForCut <= centrality < centralityMaxForCut inside this task"}; Configurable useRapiditySelection{"useRapiditySelection", false, "Use species-dependent |y| cut in addition to the detector |eta| acceptance (cross-check only)"}; @@ -103,6 +124,8 @@ struct JetShapeTask { Configurable eventSelections{"eventSelections", "sel8", "choose event selection"}; Configurable trackSelections{"trackSelections", "globalTracks", "nominal track selection for yields and tracking efficiency"}; + Configurable skipMbGapEvents{"skipMbGapEvents", true, "reject events whose sub-generator id is the MB gap one"}; + Configurable applyRctSelection{"applyRctSelection", true, "apply the run-condition-table quality selection (has no meaning in MC)"}; Configurable trackSelectionsForDca{"trackSelectionsForDca", "QualityTracksWDCA", "wide-DCA track selection used only for data DCA distributions and MC DCA templates"}; Configurable jetAreaFractionMin{"jetAreaFractionMin", -99.0, "used to make a cut on the jet areas"}; @@ -132,18 +155,49 @@ struct JetShapeTask { TRandom3 randomNumber; std::vector eventSelectionBits; + std::vector emptyEventSelectionBits; int trackSelection = -1; int trackSelectionForDca = -1; std::vector triggerMaskBits; void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); trackSelectionForDca = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelectionsForDca)); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); randomNumber.SetSeed(randomConeSeed); + std::vector nSigmaWideEdges; + { + const double fineEdge = nSigmaMax.value; + const double midEdge = nSigmaWideMid.value; + const double outerEdge = nSigmaWideMax.value; + const int nFine = nBinsNSigma.value; + const int nMid = nBinsNSigmaWideMid.value; + const int nOuter = nBinsNSigmaWideOuter.value; + nSigmaWideEdges.reserve(2 * (nOuter + nMid) + nFine + 1); + for (int i = 0; i <= nOuter; ++i) { + nSigmaWideEdges.push_back(-outerEdge + (outerEdge - midEdge) * i / nOuter); + } + for (int i = 1; i <= nMid; ++i) { + nSigmaWideEdges.push_back(-midEdge + (midEdge - fineEdge) * i / nMid); + } + for (int i = 1; i <= nFine; ++i) { + nSigmaWideEdges.push_back(-fineEdge + 2.0 * fineEdge * i / nFine); + } + for (int i = 1; i <= nMid; ++i) { + nSigmaWideEdges.push_back(fineEdge + (midEdge - fineEdge) * i / nMid); + } + for (int i = 1; i <= nOuter; ++i) { + nSigmaWideEdges.push_back(midEdge + (outerEdge - midEdge) * i / nOuter); + } + } + const AxisSpec axisNSigmaWide{nSigmaWideEdges, "n#sigma_{TOF}"}; + const AxisSpec axisPWide{nBinsPForWide.value, 0., pMax.value, "#it{p} (GeV/#it{c})"}; + const AxisSpec axisPtWide{nBinsPt.value, 0., ptMax.value, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisCentWide{nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value, "centrality (%)"}; + // Histograms definition registry.add("tpcTofPi", "tpcTofPi", HistType::kTHnSparseD, {{nBinsPForCut.value, 0, pMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); registry.add("tpcTofPr", "tpcTofPr", HistType::kTHnSparseD, {{nBinsPForCut.value, 0, pMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); @@ -166,6 +220,85 @@ struct JetShapeTask { registry.add("trackTpcNClsFound", "trackTpcNClsFound", HistType::kTH1F, {{100, 0, 200}}); registry.add("trackItsNCls", "trackItsNCls", HistType::kTH1F, {{10, 0, 10}}); + if (fillPidNsigmaQA) { + // (1) Normalisation QA: every collision reaching processInclusiveProductionRatio, + // BEFORE passCentrality(). A percentile estimator must be flat here. + registry.add("centralityAllInc", "centFT0M of every collision entering the inclusive process (before the centrality cut);centrality (%);events", HistType::kTH1F, {{110, -5., 105.}}); + registry.add("eventCounterIncAll", "collisions entering the inclusive process, before the centrality cut", HistType::kTH1F, {{1, 0., 1.}}); + + // (2) PID-free charged-track yield and the data-driven TOF-matching efficiency. + registry.add("trackPtAllInc", "all nominal tracks (no PID, no TOF requirement);p_{T} (GeV/c);centrality (%)", HistType::kTH2F, {{nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("trackPtTofInc", "nominal tracks with a TOF hit;p_{T} (GeV/c);centrality (%)", HistType::kTH2F, {{nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + + // (3) Species-tagged TOF status: 0 = no TOF, 1 = finite nSigma_TOF, 2 = sentinel (no event time). + // Same definition as mcTofNSigmaStatus* on the MC side. + registry.add("tofStatusPi", "TPC-tagged pion candidates;p_{T} (GeV/c);TOF status;centrality (%)", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {3, 0., 3.}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("tofStatusPr", "TPC-tagged proton candidates;p_{T} (GeV/c);TOF status;centrality (%)", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {3, 0., 3.}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + + // (4) nSigma_TOF of TPC-tagged candidates, WITHOUT the TOF window -> eps_TOFwin on data. + registry.add("tofNSigmaTaggedPi", "TPC-tagged pion candidates with TOF;p_{T} (GeV/c);n#sigma_{TOF}^{#pi};centrality (%)", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("tofNSigmaTaggedPr", "TPC-tagged proton candidates with TOF;p_{T} (GeV/c);n#sigma_{TOF}^{p};centrality (%)", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + } + + if (fillTofMismatchQA) { + // (5) Same selection as (4) but on the wide axis: the mismatch pedestal is measured + // where there is no signal and interpolated under the core, and the species + // contamination peaks (whose positions follow from p_T and the path length) + // become visible instead of piling up in the underflow. + registry.add("tofNSigmaWidePi", "TPC-tagged pion candidates with TOF, wide axis", HistType::kTH3F, {axisPWide, axisNSigmaWide, axisCentWide}); + registry.add("tofNSigmaWidePr", "TPC-tagged proton candidates with TOF, wide axis", HistType::kTH3F, {axisPWide, axisNSigmaWide, axisCentWide}); + // Response matrix p -> p_T for exactly the same tracks, needed to bring the yields + // extracted in p bins back onto the p_T axis. pVsPtFor* cannot be used for this: + // it is filled only for tracks that passed both nSigma windows. + registry.add("pVsPtWidePi", "TPC-tagged pion candidates with TOF;#it{p};#it{p}_{T};centrality (%)", HistType::kTH3F, {axisPWide, axisPtWide, axisCentWide}); + registry.add("pVsPtWidePr", "TPC-tagged proton candidates with TOF;#it{p};#it{p}_{T};centrality (%)", HistType::kTH3F, {axisPWide, axisPtWide, axisCentWide}); + } + + // ---- 2D PID maps (nSigma_TPC^p x nSigma_TOF^p), dense THnF, no PID cut ---- + // Binned in p, not p_T, because both nSigma depend on p. One proton-hypothesis map holds p, K and pi + // (pi lies at nSigma_TOF^p > -12 for p > 2.8 GeV/c). Dense on purpose: at full statistics a THnSparse + // of this size is filled to >50 % and costs far more per bin. Centrality is a window + // (pid2DCentMin/Max), not an axis: a THn keeps under/overflow bins on every axis. + const AxisSpec axisP2D{pid2DPEdges(), "#it{p} (GeV/#it{c})"}; + const AxisSpec axisTpc2D{44, -4., 7., "n#sigma_{TPC}^{p}"}; + const AxisSpec axisTof2D{72, -12., 6., "n#sigma_{TOF}^{p}"}; + const AxisSpec axisRJet2D{28, 0., 28., "r bin + 7 #times jet p_{T} bin"}; + const bool anyPid2D = fillPid2D || fillPid2DJet; + + if (fillPid2D) { + registry.add("pid2DPr", "inclusive tracks with TOF, all events, no PID cut;#it{p};n#sigma_{TPC}^{p};n#sigma_{TOF}^{p}", HistType::kTHnF, {axisP2D, axisTpc2D, axisTof2D}); + } + + if (fillPid2DJet) { + registry.add("pid2DJetPr", "signal cone, tracks with TOF, no PID cut;#it{p};n#sigma_{TPC}^{p};n#sigma_{TOF}^{p};r bin + 7 jet bin", HistType::kTHnF, {axisP2D, axisTpc2D, axisTof2D, axisRJet2D}); + registry.add("pid2DPerpPr", "perpendicular cones, tracks with TOF, no PID cut;#it{p};n#sigma_{TPC}^{p};n#sigma_{TOF}^{p};r bin + 7 jet bin", HistType::kTHnF, {axisP2D, axisTpc2D, axisTof2D, axisRJet2D}); + registry.add("pid2DRandPr", "random cones, tracks with TOF, no PID cut;#it{p};n#sigma_{TPC}^{p};n#sigma_{TOF}^{p};r bin + 7 reference-jet bin", HistType::kTHnF, {axisP2D, axisTpc2D, axisTof2D, axisRJet2D}); + registry.add("pid2DIncPr", "all tracks with TOF in events with an accepted jet;#it{p};n#sigma_{TPC}^{p};n#sigma_{TOF}^{p}", HistType::kTHnF, {axisP2D, axisTpc2D, axisTof2D}); + } + + if (fillPid2DMismatch && anyPid2D) { + registry.add("pid2DMisPr", "TOF-mismatch template;#it{p};n#sigma_{TPC}^{p};n#sigma_{TOF}^{p} (borrowed time);source + 2 region", HistType::kTHnF, {axisP2D, axisTpc2D, axisTof2D, {10, 0., 10., "source + 2 #times region"}}); + } + + if (fillPid2DTpcOnly && anyPid2D) { + registry.add("pid2DTpcOnlyPr", "TPC only, no TOF requirement;#it{p};n#sigma_{TPC}^{p};hasTOF;region", HistType::kTHnF, {axisP2D, axisTpc2D, {2, 0., 2., "hasTOF"}, {3, 0., 3., "region"}}); + } + + if (anyPid2D) { + constexpr double PtMin2D = 1.0; + constexpr double PtBinWidth2D = 0.1; + const int nPt2D = static_cast(std::lround((pid2DPMax.value - PtMin2D) / PtBinWidth2D)); + registry.add("pid2DPVsPt", "tracks entering the pid2D maps;#it{p};#it{p}_{T};region", HistType::kTHnF, {axisP2D, {nPt2D, PtMin2D, PtMin2D + nPt2D * PtBinWidth2D, "#it{p}_{T} (GeV/#it{c})"}, {5, 0., 5., "region"}}); + } + + if (fillPid2DMc && doprocessEfficiencyAndPurity) { + const AxisSpec axisTruthInc2DMc{24, 0., 24., "species + 6 #times (origin + 2 #times tofBad)"}; + const AxisSpec axisTruthCone2DMc{24, 0., 24., "species4 + 4 #times (tofBad + 2 #times r bin)"}; + registry.add("mcPid2DIncPr", "MC: all tracks with TOF in events with a matched jet;#it{p};n#sigma_{TPC}^{p};n#sigma_{TOF}^{p};truth index", HistType::kTHnF, {axisP2D, axisTpc2D, axisTof2D, axisTruthInc2DMc}); + registry.add("mcPid2DJetPr", "MC: signal cone, tracks with TOF;#it{p};n#sigma_{TPC}^{p};n#sigma_{TOF}^{p};truth index", HistType::kTHnF, {axisP2D, axisTpc2D, axisTof2D, axisTruthCone2DMc}); + registry.add("mcPid2DPerpPr", "MC: perpendicular cones, tracks with TOF;#it{p};n#sigma_{TPC}^{p};n#sigma_{TOF}^{p};truth index", HistType::kTHnF, {axisP2D, axisTpc2D, axisTof2D, axisTruthCone2DMc}); + } + registry.add("jetTpcPi", "jetTpcPi", HistType::kTH2F, {{nBinsP.value, 0, pMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}}); registry.add("jetTofPi", "jetTofPi", HistType::kTH2F, {{nBinsPt.value, 0, ptMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}}); registry.add("jetTpcPr", "jetTpcPr", HistType::kTH2F, {{nBinsP.value, 0, pMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}}); @@ -226,6 +359,10 @@ struct JetShapeTask { registry.add("ptVsCentrality", "ptvscentrality", HistType::kTH2F, {{100, 0, 100}, {300, 0, 300}}); registry.add("ptResolution", "ptResolution", HistType::kTH2F, {{nBinsPt.value, 0, ptMax.value}, {100, -1.0, +1.0}}); registry.add("mcCentralityReco", "mcCentralityReco", HistType::kTH1F, {{100, 0, 100}}); + registry.add("mcCutFlow", "processEfficiencyAndPurity cut flow: 0 = called, 1 = selectCollision, 2 = has_mcCollision, 3 = passCentrality", HistType::kTH1F, {{4, 0., 4.}}); + registry.add("mcSelDetail", "selectCollision breakdown: 0 = called, 1 = not MB gap, 2 = RCT, 3 = event selection bits", HistType::kTH1F, {{4, 0., 4.}}); + registry.add("mcSubGeneratorId", "sub-generator id of the associated MC collision", HistType::kTH1F, {{6, -2.5, 3.5}}); + registry.add("mcCentralityRaw", "centFT0M seen by processEfficiencyAndPurity, before the cut", HistType::kTH1F, {{124, -12., 112.}}); // MC Efficiency Denominators // Jet (In-cone) @@ -256,6 +393,28 @@ struct JetShapeTask { registry.add("effNumPidPiInc", "True primary pion reconstructed with the full analysis PID selection", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); registry.add("effNumPidPrInc", "True primary proton reconstructed with the full analysis PID selection", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + // nSigma window efficiency breakdown (inclusive only, ~0.3 MB each) + registry.add("effNumKinePiInc", "True primary pion, TOF matched, passing the species kinematics (denominator of the nSigma windows)", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumTpcWinPiInc", "effNumKinePiInc and the |nSigma_TPC^pi| window ONLY", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumTofWinPiInc", "effNumKinePiInc and the |nSigma_TOF^pi| window ONLY", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumKinePrInc", "True primary proton, TOF matched, passing the species kinematics (denominator of the nSigma windows)", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumTpcWinPrInc", "effNumKinePrInc and the |nSigma_TPC^p| window ONLY", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumTofWinPrInc", "effNumKinePrInc and the |nSigma_TOF^p| window ONLY", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + + // nSigma response of true primary pions and protons in MC (~1.5 MB each) + registry.add("mcTpcNSigmaPi", "True primary pion: p vs nSigma_TPC^pi", HistType::kTHnSparseD, {{nBinsPForCut.value, 0, pMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("mcTofNSigmaPi", "True primary pion: pT vs nSigma_TOF^pi", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("mcTpcNSigmaPr", "True primary proton: p vs nSigma_TPC^p", HistType::kTHnSparseD, {{nBinsPForCut.value, 0, pMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("mcTofNSigmaPr", "True primary proton: pT vs nSigma_TOF^p", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("mcTofNSigmaStatusPi", "True primary pion: 0 = no TOF, 1 = finite nSigma_TOF, 2 = unphysical nSigma_TOF", HistType::kTH1F, {{3, 0., 3.}}); + registry.add("mcTofNSigmaStatusPr", "True primary proton: 0 = no TOF, 1 = finite nSigma_TOF, 2 = unphysical nSigma_TOF", HistType::kTH1F, {{3, 0., 3.}}); + + if (fillTofMismatchQA) { + registry.add("mcTofNSigmaWidePi", "True primary pion with TOF, wide axis", HistType::kTH3F, {axisPWide, axisNSigmaWide, axisCentWide}); + registry.add("mcTofNSigmaWidePr", "True primary proton with TOF, wide axis", HistType::kTH3F, {axisPWide, axisNSigmaWide, axisCentWide}); + registry.add("mcTofNSigmaWideKa", "True primary kaon with TOF, wide axis", HistType::kTH3F, {axisPWide, axisNSigmaWide, axisCentWide}); + } + // Jet (In-cone) registry.add("effNumTpcPiJet", "effNumTpcPiJet", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); registry.add("effNumTpcKaJet", "effNumTpcKaJet", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); @@ -420,6 +579,133 @@ struct JetShapeTask { return std::asinh(pz / mt); } + std::vector pid2DPEdges() const + { + constexpr double FineWidth = 0.2; + constexpr double CoarseWidth = 0.4; + constexpr double FineUpTo = 5.0; + constexpr double Tolerance = 1e-6; + constexpr double Rounding = 1000.; + std::vector edges{static_cast(pid2DPMin.value)}; + double p = pid2DPMin.value; + while (p < pid2DPMax.value - Tolerance) { + p += (p < FineUpTo - Tolerance) ? FineWidth : CoarseWidth; + p = std::min(std::round(p * Rounding) / Rounding, static_cast(pid2DPMax.value)); + edges.push_back(p); + } + return edges; + } + + // TOF hits of one collision, used to build the mismatch templates (8). + struct TofHit2D { + int64_t index; + float eta; + float phi; + float time; // t - t0 = L / (beta c), ps + }; + static constexpr float SpeedOfLightCmPerPs = 0.0299792458f; + + template + static bool tofTimeOfFlight(TrackT const& track, float& time) + { + if (!track.hasTOF() || track.beta() <= 0.f || track.length() <= 0.f) { + return false; + } + time = track.length() / (track.beta() * SpeedOfLightCmPerPs); + return true; + } + + template + void collectTofHits(JetTracksT const& tracks, std::vector& hits) + { + hits.clear(); + for (const auto& jetTrack : tracks) { + const auto track = jetTrack.template track_as(); + float time = 0.f; + if (tofTimeOfFlight(track, time)) { + hits.push_back({track.globalIndex(), track.eta(), track.phi(), time}); + } + } + } + + template + std::array mismatchNSigmaPr(TrackT const& track, std::vector const& hits) + { + constexpr int MaxAttempts = 10; + constexpr size_t MinHits = 2; + std::array out{std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()}; + float time = 0.f; + const float sigma = track.tofExpSigmaPr(); + if (hits.size() < MinHits || sigma <= 0.f || !tofTimeOfFlight(track, time)) { + return out; + } + const int64_t self = track.globalIndex(); + const float nSigma = track.tofNSigmaPr(); + for (int attempt = 0; attempt < MaxAttempts; ++attempt) { + const auto& hit = hits[randomNumber.Integer(static_cast(hits.size()))]; + if (hit.index != self) { + out[0] = nSigma + (hit.time - time) / sigma; + break; + } + } + float best = std::numeric_limits::max(); + const TofHit2D* nearest = nullptr; + for (const auto& hit : hits) { + if (hit.index == self) { + continue; + } + const float dEta = hit.eta - track.eta(); + const float dPhi = absDeltaPhi(hit.phi, track.phi()); + const float d2 = dEta * dEta + dPhi * dPhi; + if (d2 < best) { + best = d2; + nearest = &hit; + } + } + if (nearest != nullptr) { + out[1] = nSigma + (nearest->time - time) / sigma; + } + return out; + } + + // Regions of the pid2D maps (int constants, not an enum: they are combined with float bin centres). + static constexpr int RegIncAll = 0; // inclusive, all events + static constexpr int RegIncJet = 1; // inclusive, events with an accepted jet + static constexpr int RegSignal = 2; + static constexpr int RegPerp = 3; + static constexpr int RegRand = 4; + + void fillMismatchTemplate(float p, float tpcPr, std::array const& mis, int region) + { + constexpr int NSources = 2; + for (int src = 0; src < NSources; ++src) { + if (std::isfinite(mis[src])) { + registry.fill(HIST("pid2DMisPr"), p, tpcPr, mis[src], src + NSources * region + 0.5f); + } + } + } + + // Folded (r, jet pT) index of the pid2D jet maps; -1 if outside. + static int rJetIndex2D(float r, float jetPt) + { + constexpr int NRBins2D = 7; + constexpr int NJetPtBins2D = 4; + constexpr float RBinWidth2D = 0.1f; + constexpr float RMax2D = NRBins2D * RBinWidth2D; + constexpr int Outside = -1; + static constexpr std::array JetPtEdges2D = {20.f, 40.f, 60.f, 80.f, 140.f}; + if (r < 0.f || r >= RMax2D) { + return Outside; + } + const int ir = std::min(NRBins2D - 1, static_cast(r / RBinWidth2D)); + for (int k = 0; k < NJetPtBins2D; ++k) { + if (jetPt >= JetPtEdges2D[k] && jetPt < JetPtEdges2D[k + 1]) { + return ir + NRBins2D * k; + } + } + return Outside; + } + bool passSpeciesKinematics(float pt, float eta, float mass) const { if (std::abs(eta) >= etaTrUp.value) { @@ -504,7 +790,7 @@ struct JetShapeTask { for (int attempt = 0; attempt < randomConeMaxAttempts; ++attempt) { rcEta = randomNumber.Uniform(etaMin, etaMax); if (randomConeFullAzimuth) { - rcPhi = randomNumber.Uniform(0.0f, static_cast(o2::constants::math::TwoPI)); + rcPhi = randomNumber.Uniform(0.0f, o2::constants::math::TwoPI); } else { float dPhi = randomNumber.Uniform(randomConeDeltaPhiMin, randomConeDeltaPhiMax); if (randomNumber.Uniform() < RandomConeNegativeSideProbability) { @@ -792,9 +1078,11 @@ struct JetShapeTask { const float maxR2 = distanceMax * distanceMax; - // The jet finder remains configured with globalTracks. The nominal-yield - // sample and the wide-DCA sample are selected independently relative to - // the same reconstructed jet axes. + std::vector tofHits; + if (fillPid2DJet && fillPid2DMismatch) { + collectTofHits(tracks, tofHits); + } + for (const auto& jetTrack : tracks) { const bool passesNominalBit = jetderiveddatautilities::selectTrack(jetTrack, trackSelection); const bool passesWideDcaBit = jetderiveddatautilities::selectTrack(jetTrack, trackSelectionForDca); @@ -832,6 +1120,22 @@ struct JetShapeTask { const bool isTpcPiRange = (tpcPi > tpcNSigmaPiMin && tpcPi < tpcNSigmaPiMax); const bool isTpcPrRange = (tpcPr > tpcNSigmaPrMin && tpcPr < tpcNSigmaPrMax); + const bool inPid2DWindow = fillPid2DJet && passesNominal && passPrKinematics && + centrality >= pid2DCentMin && centrality < pid2DCentMax && + trkP >= pid2DPMin && trkP < pid2DPMax; + const bool fillPid2DTrack = inPid2DWindow && track.hasTOF(); + const bool fillPid2DTpcOnlyTrack = inPid2DWindow && fillPid2DTpcOnly; + const float hasTofBin = track.hasTOF() ? 1.5f : 0.5f; + std::array misNSigma{std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()}; + if (fillPid2DTrack) { + registry.fill(HIST("pid2DIncPr"), trkP, tpcPr, tofPr); + registry.fill(HIST("pid2DPVsPt"), trkP, trkPt, RegIncJet + 0.5f); + if (fillPid2DMismatch) { + misNSigma = mismatchNSigmaPr(track, tofHits); + fillMismatchTemplate(trkP, tpcPr, misNSigma, RegIncJet); + } + } + if (passesNominal && fillDetailedQA) { registry.fill(HIST("jetTpcPi"), trkP, tpcPi); registry.fill(HIST("jetTofPi"), trkPt, tofPi); @@ -857,6 +1161,22 @@ struct JetShapeTask { } const float distBg = std::sqrt(distBgSq); + if (fillPid2DTpcOnlyTrack || fillPid2DTrack) { + const int idx2D = rJetIndex2D(distBg, jet.ptCorr); + if (idx2D >= 0) { + if (fillPid2DTpcOnlyTrack) { + registry.fill(HIST("pid2DTpcOnlyPr"), trkP, tpcPr, hasTofBin, 2.5f); + } + if (fillPid2DTrack) { + registry.fill(HIST("pid2DPerpPr"), trkP, tpcPr, tofPr, idx2D + 0.5f); + registry.fill(HIST("pid2DPVsPt"), trkP, trkPt, RegPerp + 0.5f); + if (fillPid2DMismatch) { + fillMismatchTemplate(trkP, tpcPr, misNSigma, RegPerp); + } + } + } + } + if (passesWideDca) { if (hasTofPr && isTpcPrRange) { registry.fill(HIST("dcaPrPerpJet"), trkPt, track.dcaXY(), distBg, jet.ptCorr, centrality); @@ -893,6 +1213,22 @@ struct JetShapeTask { } const float distance = std::sqrt(distanceSq); + if (fillPid2DTpcOnlyTrack || fillPid2DTrack) { + const int idx2D = rJetIndex2D(distance, jet.ptCorr); + if (idx2D >= 0) { + if (fillPid2DTpcOnlyTrack) { + registry.fill(HIST("pid2DTpcOnlyPr"), trkP, tpcPr, hasTofBin, 1.5f); + } + if (fillPid2DTrack) { + registry.fill(HIST("pid2DJetPr"), trkP, tpcPr, tofPr, idx2D + 0.5f); + registry.fill(HIST("pid2DPVsPt"), trkP, trkPt, RegSignal + 0.5f); + if (fillPid2DMismatch) { + fillMismatchTemplate(trkP, tpcPr, misNSigma, RegSignal); + } + } + } + } + if (passesWideDca) { if (hasTofPr && isTpcPrRange) { registry.fill(HIST("jetDcaPr"), trkPt, track.dcaXY(), distance, jet.ptCorr, centrality); @@ -924,8 +1260,6 @@ struct JetShapeTask { } } - // Random cones are outside the jet loop, so every track-cone pair is - // filled exactly once. Each cone is associated with one selected jet. for (const auto& cone : randomCones) { const float dEtaRC = trkEta - cone.eta; if (std::abs(dEtaRC) >= distanceMax) { @@ -938,6 +1272,17 @@ struct JetShapeTask { } const float distRC = std::sqrt(distRCSq); + if (fillPid2DTrack) { + const int idx2D = rJetIndex2D(distRC, cone.referenceJetPtCorr); + if (idx2D >= 0) { + registry.fill(HIST("pid2DRandPr"), trkP, tpcPr, tofPr, idx2D + 0.5f); + registry.fill(HIST("pid2DPVsPt"), trkP, trkPt, RegRand + 0.5f); + if (fillPid2DMismatch) { + fillMismatchTemplate(trkP, tpcPr, misNSigma, RegRand); + } + } + } + if (passesWideDca) { if (hasTofPr && isTpcPrRange) { registry.fill(HIST("dcaPrRandCone"), trkPt, track.dcaXY(), distRC, cone.referenceJetPtCorr, centrality); @@ -991,11 +1336,20 @@ struct JetShapeTask { return; } const float centrality = collision.centFT0M(); + if (fillPidNsigmaQA) { + registry.fill(HIST("centralityAllInc"), centrality); + registry.fill(HIST("eventCounterIncAll"), 0.5); + } if (!passCentrality(centrality)) { return; } registry.fill(HIST("eventCounterInc"), centrality); + std::vector tofHits; + if (fillPid2D && fillPid2DMismatch) { + collectTofHits(tracks, tofHits); + } + for (const auto& jetTrack : tracks) { const bool passesNominalBit = jetderiveddatautilities::selectTrack(jetTrack, trackSelection); const bool passesWideDcaBit = jetderiveddatautilities::selectTrack(jetTrack, trackSelectionForDca); @@ -1044,6 +1398,59 @@ struct JetShapeTask { continue; } + if (fillPidNsigmaQA) { + constexpr float MaxSaneNSigma = 10.f; + constexpr float NoTofBin = 0.5f; + constexpr float FiniteNSigmaBin = 1.5f; + constexpr float UnphysicalNSigmaBin = 2.5f; + const bool trackHasTof = track.hasTOF(); + + registry.fill(HIST("trackPtAllInc"), track.pt(), centrality); + if (trackHasTof) { + registry.fill(HIST("trackPtTofInc"), track.pt(), centrality); + } + if (passPiKinematics && isTpcPiRange) { + registry.fill(HIST("tofStatusPi"), track.pt(), + !trackHasTof ? NoTofBin + : (std::abs(track.tofNSigmaPi()) < MaxSaneNSigma ? FiniteNSigmaBin : UnphysicalNSigmaBin), + centrality); + if (trackHasTof) { + registry.fill(HIST("tofNSigmaTaggedPi"), track.pt(), track.tofNSigmaPi(), centrality); + if (fillTofMismatchQA) { + registry.fill(HIST("tofNSigmaWidePi"), track.p(), track.tofNSigmaPi(), centrality); + registry.fill(HIST("pVsPtWidePi"), track.p(), track.pt(), centrality); + } + } + } + if (passPrKinematics && isTpcPrRange) { + registry.fill(HIST("tofStatusPr"), track.pt(), + !trackHasTof ? NoTofBin + : (std::abs(track.tofNSigmaPr()) < MaxSaneNSigma ? FiniteNSigmaBin : UnphysicalNSigmaBin), + centrality); + if (trackHasTof) { + registry.fill(HIST("tofNSigmaTaggedPr"), track.pt(), track.tofNSigmaPr(), centrality); + if (fillTofMismatchQA) { + registry.fill(HIST("tofNSigmaWidePr"), track.p(), track.tofNSigmaPr(), centrality); + registry.fill(HIST("pVsPtWidePr"), track.p(), track.pt(), centrality); + } + } + } + } + + if (fillPid2D && passPrKinematics && centrality >= pid2DCentMin && centrality < pid2DCentMax && + track.p() >= pid2DPMin && track.p() < pid2DPMax) { + if (fillPid2DTpcOnly) { + registry.fill(HIST("pid2DTpcOnlyPr"), track.p(), track.tpcNSigmaPr(), track.hasTOF() ? 1.5f : 0.5f, 0.5f); + } + if (track.hasTOF()) { + registry.fill(HIST("pid2DPr"), track.p(), track.tpcNSigmaPr(), track.tofNSigmaPr()); + registry.fill(HIST("pid2DPVsPt"), track.p(), track.pt(), RegIncAll + 0.5f); + if (fillPid2DMismatch) { + fillMismatchTemplate(track.p(), track.tpcNSigmaPr(), mismatchNSigmaPr(track, tofHits), RegIncAll); + } + } + } + if (fillDetailedQA) { registry.fill(HIST("tpcPi"), track.p(), track.tpcNSigmaPi()); registry.fill(HIST("tofPi"), track.pt(), track.tofNSigmaPi()); @@ -1070,6 +1477,99 @@ struct JetShapeTask { } PROCESS_SWITCH(JetShapeTask, processInclusiveProductionRatio, "inclusive Production ratio", false); + template + void fillPid2DMcTrack(JetTrackT const& jetTrack, TrackT const& track, std::vector const& jets, float maxR2) + { + constexpr int SpPi = 0; + constexpr int SpKa = 1; + constexpr int SpPr = 2; + constexpr int SpEl = 3; + constexpr int SpMu = 4; + constexpr int SpOther = 5; + constexpr int NSpecies = 6; + constexpr int NSpeciesCone = 4; + constexpr float JetPtMin2D = 20.f; + constexpr float JetPtMax2D = 140.f; + constexpr float RMax2D = 0.7f; + constexpr float REdge1 = 0.2f; + constexpr float REdge2 = 0.4f; + + const float trkP = track.p(); + const float tpcPr = track.tpcNSigmaPr(); + const float tofPr = track.tofNSigmaPr(); + + int species = SpOther; + int origin = 1; + float tofTrue = 0.f; + if (jetTrack.has_mcParticle()) { + const auto mcPart = jetTrack.template mcParticle_as(); + origin = mcPart.isPhysicalPrimary() ? 0 : 1; + switch (std::abs(mcPart.pdgCode())) { + case PDG_t::kPiPlus: + species = SpPi; + tofTrue = track.tofNSigmaPi(); + break; + case PDG_t::kKPlus: + species = SpKa; + tofTrue = track.tofNSigmaKa(); + break; + case PDG_t::kProton: + species = SpPr; + tofTrue = tofPr; + break; + case PDG_t::kElectron: // beta ~ 1 at p > 2 GeV/c: the pion hypothesis is within 0.1 sigma + species = SpEl; + tofTrue = track.tofNSigmaPi(); + break; + case PDG_t::kMuonMinus: + species = SpMu; + tofTrue = track.tofNSigmaPi(); + break; + default: + break; + } + } + const int tofBad = (species != SpOther && std::abs(tofTrue) > pid2DMcTofBadNSigma) ? 1 : 0; + registry.fill(HIST("mcPid2DIncPr"), trkP, tpcPr, tofPr, species + NSpecies * (origin + 2 * tofBad) + 0.5f); + + const int speciesCone = std::min(species, NSpeciesCone - 1); + auto coneIndex = [&](float r) { + const int ir = r < REdge1 ? 0 : (r < REdge2 ? 1 : 2); + return speciesCone + NSpeciesCone * (tofBad + 2 * ir) + 0.5f; + }; + + const float trkEta = track.eta(); + const float trkPhi = track.phi(); + for (const auto& jet : jets) { + if (jet.detPtCorr < JetPtMin2D || jet.detPtCorr >= JetPtMax2D) { + continue; + } + const float dEta = trkEta - jet.detEta; + if (std::abs(dEta) >= RMax2D) { + continue; + } + const float dPhi = absDeltaPhi(trkPhi, jet.detPhi); + const float distSq = dEta * dEta + dPhi * dPhi; + if (distSq < maxR2) { + const float r = std::sqrt(distSq); + if (r < RMax2D) { + registry.fill(HIST("mcPid2DJetPr"), trkP, tpcPr, tofPr, coneIndex(r)); + } + } + // Both perpendicular cones, as for pid2DPerpPr in data. + for (const auto& conePhi : {jet.detPhiBg1, jet.detPhiBg2}) { + const float dPhiBg = absDeltaPhi(trkPhi, conePhi); + const float distBgSq = dEta * dEta + dPhiBg * dPhiBg; + if (distBgSq < maxR2) { + const float rBg = std::sqrt(distBgSq); + if (rBg < RMax2D) { + registry.fill(HIST("mcPid2DPerpPr"), trkP, tpcPr, tofPr, coneIndex(rBg)); + } + } + } + } + } + void processEfficiencyAndPurity( soa::Filtered>::iterator const& collision, JetTracksWithPIMC const& jetTracks, @@ -1078,20 +1578,47 @@ struct JetShapeTask { aod::ChargedMCParticleLevelJets const& mcpJets, aod::JetParticles const& mcParticles) { - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { + constexpr float CutFlowCalled = 0.5f; + constexpr float CutFlowEventSelection = 1.5f; + constexpr float CutFlowHasMcCollision = 2.5f; + constexpr float CutFlowCentrality = 3.5f; + + constexpr float SelDetailCalled = 0.5f; + constexpr float SelDetailNotMbGap = 1.5f; + constexpr float SelDetailRct = 2.5f; + constexpr float SelDetailEventBits = 3.5f; + + registry.fill(HIST("mcCutFlow"), CutFlowCalled); + registry.fill(HIST("mcSelDetail"), SelDetailCalled); + registry.fill(HIST("mcSubGeneratorId"), collision.getSubGeneratorId()); + if (collision.getSubGeneratorId() != jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { + registry.fill(HIST("mcSelDetail"), SelDetailNotMbGap); + } + if (jetderiveddatautilities::selectCollision(collision, emptyEventSelectionBits, false, true)) { + registry.fill(HIST("mcSelDetail"), SelDetailRct); + } + if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, false, false)) { + registry.fill(HIST("mcSelDetail"), SelDetailEventBits); + } + + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMbGapEvents, applyRctSelection)) { return; } + registry.fill(HIST("mcCutFlow"), CutFlowEventSelection); if (!collision.has_mcCollision()) { return; } + registry.fill(HIST("mcCutFlow"), CutFlowHasMcCollision); const auto reducedMcCollisionId = collision.mcCollisionId(); (void)mcpJets; const float centrality = collision.centFT0M(); + registry.fill(HIST("mcCentralityRaw"), centrality); if (!passCentrality(centrality)) { return; } + registry.fill(HIST("mcCutFlow"), CutFlowCentrality); const float rho = collision.rho(); const float maxR2 = distanceMax * distanceMax; @@ -1255,6 +1782,13 @@ struct JetShapeTask { continue; } + if (fillPid2DMc && passesNominal && track.hasTOF() && !validJets.empty() && + centrality >= pid2DCentMin && centrality < pid2DCentMax && + track.p() >= pid2DPMin && track.p() < pid2DPMax && + passSpeciesKinematics(track.pt(), track.eta(), ProtonMass)) { + fillPid2DMcTrack(jetTrack, track, validJets, maxR2); + } + if (!jetTrack.has_mcParticle()) { continue; } @@ -1284,12 +1818,12 @@ struct JetShapeTask { track.tpcNSigmaPi() < tpcNSigmaPiMax; const bool passTpcPr = track.tpcNSigmaPr() > tpcNSigmaPrMin && track.tpcNSigmaPr() < tpcNSigmaPrMax; - const bool isRecoPi = passSpeciesKinematics(recoPt, track.eta(), PionMass) && - passTpcPi && hasTof && - std::abs(track.tofNSigmaPi()) < nSigmaTofCut; - const bool isRecoPr = passSpeciesKinematics(recoPt, track.eta(), ProtonMass) && - passTpcPr && hasTof && - std::abs(track.tofNSigmaPr()) < nSigmaTofCut; + const bool passKineRecoPi = passSpeciesKinematics(recoPt, track.eta(), PionMass); + const bool passKineRecoPr = passSpeciesKinematics(recoPt, track.eta(), ProtonMass); + const bool passTofPi = hasTof && std::abs(track.tofNSigmaPi()) < nSigmaTofCut; + const bool passTofPr = hasTof && std::abs(track.tofNSigmaPr()) < nSigmaTofCut; + const bool isRecoPi = passKineRecoPi && passTpcPi && passTofPi; + const bool isRecoPr = passKineRecoPr && passTpcPr && passTofPr; float originBin = 2.5f; if (isPrimary) { @@ -1478,10 +2012,30 @@ struct JetShapeTask { registry.fill(HIST("ptResolution"), track.pt(), track.pt() - mcPt); + constexpr float MaxSaneNSigma = 10.f; // nSigma_TOF is filled with a sentinel value when the event time is missing + constexpr float NoTofBin = 0.5f; + constexpr float FiniteNSigmaBin = 1.5f; + constexpr float UnphysicalNSigmaBin = 2.5f; + if (isTruePi) { registry.fill(HIST("effNumTpcPiInc"), mcPt, centrality); + registry.fill(HIST("mcTofNSigmaStatusPi"), !hasTof ? NoTofBin : (std::abs(track.tofNSigmaPi()) < MaxSaneNSigma ? FiniteNSigmaBin : UnphysicalNSigmaBin)); if (hasTof) { registry.fill(HIST("effNumTofPiInc"), mcPt, centrality); + registry.fill(HIST("mcTpcNSigmaPi"), track.p(), track.tpcNSigmaPi(), centrality); + registry.fill(HIST("mcTofNSigmaPi"), mcPt, track.tofNSigmaPi(), centrality); + if (fillTofMismatchQA) { + registry.fill(HIST("mcTofNSigmaWidePi"), track.p(), track.tofNSigmaPi(), centrality); + } + if (passKineRecoPi) { + registry.fill(HIST("effNumKinePiInc"), mcPt, centrality); + if (passTpcPi) { + registry.fill(HIST("effNumTpcWinPiInc"), mcPt, centrality); + } + if (passTofPi) { + registry.fill(HIST("effNumTofWinPiInc"), mcPt, centrality); + } + } } if (isRecoPi) { registry.fill(HIST("effNumPidPiInc"), mcPt, centrality); @@ -1490,11 +2044,29 @@ struct JetShapeTask { registry.fill(HIST("effNumTpcKaInc"), mcPt, centrality); if (hasTof) { registry.fill(HIST("effNumTofKaInc"), mcPt, centrality); + if (fillTofMismatchQA) { + registry.fill(HIST("mcTofNSigmaWideKa"), track.p(), track.tofNSigmaKa(), centrality); + } } } else { registry.fill(HIST("effNumTpcPrInc"), mcPt, centrality); + registry.fill(HIST("mcTofNSigmaStatusPr"), !hasTof ? NoTofBin : (std::abs(track.tofNSigmaPr()) < MaxSaneNSigma ? FiniteNSigmaBin : UnphysicalNSigmaBin)); if (hasTof) { registry.fill(HIST("effNumTofPrInc"), mcPt, centrality); + registry.fill(HIST("mcTpcNSigmaPr"), track.p(), track.tpcNSigmaPr(), centrality); + registry.fill(HIST("mcTofNSigmaPr"), mcPt, track.tofNSigmaPr(), centrality); + if (fillTofMismatchQA) { + registry.fill(HIST("mcTofNSigmaWidePr"), track.p(), track.tofNSigmaPr(), centrality); + } + if (passKineRecoPr) { + registry.fill(HIST("effNumKinePrInc"), mcPt, centrality); + if (passTpcPr) { + registry.fill(HIST("effNumTpcWinPrInc"), mcPt, centrality); + } + if (passTofPr) { + registry.fill(HIST("effNumTofWinPrInc"), mcPt, centrality); + } + } } if (isRecoPr) { registry.fill(HIST("effNumPidPrInc"), mcPt, centrality); diff --git a/PWGJE/Tasks/jetSpectraCharged.cxx b/PWGJE/Tasks/jetSpectraCharged.cxx index 44363723bf9..c7b6900977c 100644 --- a/PWGJE/Tasks/jetSpectraCharged.cxx +++ b/PWGJE/Tasks/jetSpectraCharged.cxx @@ -106,8 +106,8 @@ struct JetSpectraCharged { void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); doSumw2 = skipMBGapEvents; AxisSpec centralityAxis = {1200, -10., 110., "Centrality"}; diff --git a/PWGJE/Tasks/jetSpectraEseTask.cxx b/PWGJE/Tasks/jetSpectraEseTask.cxx index 18d5701a0ea..396a9acdef3 100644 --- a/PWGJE/Tasks/jetSpectraEseTask.cxx +++ b/PWGJE/Tasks/jetSpectraEseTask.cxx @@ -88,10 +88,11 @@ struct JetSpectraEseTask { Configurable cfgUseMCEventWeights{"cfgUseMCEventWeights", false, "Flag for using MC event weights"}; Configurable leadingConstituentPtMin{"leadingConstituentPtMin", -99.0, "minimum pT selection on jet constituent"}; Configurable leadingConstituentPtMax{"leadingConstituentPtMax", 9999.0, "maximum pT selection on jet constituent"}; - Configurable checkLeadConstituentMinPtForMcpJets{"checkLeadConstituentMinPtForMcpJets", false, "flag to choose whether particle level jets should have their lead track pt above leadingConstituentPtMin to be accepted; off by default, as leadingConstituentPtMin cut is only applied on MCD jets for the Pb-Pb analysis using pp MC anchored to Pb-Pb for the response matrix"}; + Configurable checkLeadConstituentPtForMcpJets{"checkLeadConstituentPtForMcpJets", false, "flag to choose whether particle level jets should have their lead track pt above leadingConstituentPtMin to be accepted; off by default, as leadingConstituentPtMin cut is only applied on MCD jets for the Pb-Pb analysis using pp MC anchored to Pb-Pb for the response matrix"}; Configurable pTHatMaxMCD{"pTHatMaxMCD", 999.0, "maximum fraction of hard scattering for jet acceptance in detector MC"}; Configurable pTHatMaxMCP{"pTHatMaxMCP", 999.0, "maximum fraction of hard scattering for jet acceptance in particle MC"}; Configurable pTHatExponent{"pTHatExponent", 6.0, "exponent of the event weight for the calculation of pTHat"}; + Configurable pTHatAbsoluteMin{"pTHatAbsoluteMin", -99.0f, "minimum value of pTHat"}; Configurable fLeadJetPtCut{"fLeadJetPtCut", false, "Flag for leading jet pT cut"}; Configurable leadJetPtMin{"leadJetPtMin", 20.0, "minimum leading jet pT cut"}; @@ -204,9 +205,9 @@ struct JetSpectraEseTask { using BinningTypeFT0C = ColumnBinningPolicy; using BinningTypeFT0M = ColumnBinningPolicy; using BinningTypeFT0CVariant1 = ColumnBinningPolicy; - BinningTypeFT0C corrBinningFT0C{{binsZVtx, binsCentrality}, true}; - BinningTypeFT0M corrBinningFT0M{{binsZVtx, binsCentrality}, true}; - BinningTypeFT0CVariant1 corrBinningFT0CVariant1{{binsZVtx, binsCentrality}, true}; + BinningTypeFT0C corrBinningFT0C{{binsZVtx, binsCentrality}}; + BinningTypeFT0M corrBinningFT0M{{binsZVtx, binsCentrality}}; + BinningTypeFT0CVariant1 corrBinningFT0CVariant1{{binsZVtx, binsCentrality}}; Service pdg{}; enum class DetID { FT0C, @@ -252,7 +253,7 @@ struct JetSpectraEseTask { static constexpr int NumSubSmpl = 5; static constexpr int NumSavedRhoFitEvents = 5; std::array, NumSubSmpl> hSameSub; - std::array, NumSubSmpl> hCentralityAnalyzedSub; + std::array, NumSubSmpl> hNtrigSub; void applySystematicPreset() { @@ -355,8 +356,8 @@ struct JetSpectraEseTask { int64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); ccdb->setCreatedNotAfter(now); - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); LOGF(info, "jetSpectraEse::init()"); @@ -428,8 +429,8 @@ struct JetSpectraEseTask { for (int i = 0; i < NumSubSmpl; ++i) { std::string n = fmt::format("subsamples/thn_jethad_corr_same_subsample{}", i); hSameSub[i] = registry.add(n, "same;...", o2::framework::HistType::kTHnSparseF, axes); - std::string n2 = fmt::format("subsamples/hCentralityAnalyzed_subsample{}", i); - hCentralityAnalyzedSub[i] = registry.add(n2, ";Centrality;entries", o2::framework::HistType::kTH1F, {{centAxis}}); + std::string n2 = fmt::format("subsamples/hNtrig_subsample{}", i); + hNtrigSub[i] = registry.add(n2, "", o2::framework::HistType::kTHnSparseF, {{centAxis}, {jetPtAxis}, {dPhiAxis}, {eseAxis}}); } registry.add("hNtrig", "", {HistType::kTHnSparseF, {{centAxis}, {jetPtAxis}, {dPhiAxis}, {eseAxis}}}); @@ -540,9 +541,13 @@ struct JetSpectraEseTask { registry.add("h3CentdeltapTRndmConePhi_localrhovsphi", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {dPhiAxis}}}); registry.add("h3CentdeltapTRndmConePhi_rhovsphi", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {dPhiAxis}}}); } + // Normalize MC spectra with hUsedEventWeights bin 1 content, not GetEntries(). if (doprocessMCParticleLevel) { LOGF(info, "JetSpectraEseTask::init() - MC Particle level"); registry.add("mcp/hEventCounter", "event status;event status;entries", {HistType::kTH1F, {{10, 0.0, 10.0}}}); + registry.add("mcp/hUsedEventWeights", "Used MC collisions;Used events;Sum of event weights", {HistType::kTH1D, {{1, 0.0, 1.0}}}); + registry.get(HIST("mcp/hUsedEventWeights"))->Sumw2(); + registry.get(HIST("mcp/hUsedEventWeights"))->GetXaxis()->SetBinLabel(1, "Sum of weights of used events"); registry.add("mcp/hCentralitySel", ";centr;entries", {HistType::kTH1F, {{centAxis}}}); // registry.add("mcp/hJetSparse", ";Centrality;#it{p}_{T,jet part}; #eta; #phi", {HistType::kTHnSparseF, {{centAxis}, {jetPtAxis}, {etaAxis}, {phiAxis}}}); registry.add("mcp/hJetSparse", ";Centrality;#it{p}_{T,jet part}; #eta; #phi", {HistType::kTHnSparseF, {{centAxis}, {jetPtAxis}, {etaAxis}, {phiAxis}}}); @@ -560,6 +565,9 @@ struct JetSpectraEseTask { if (doprocessMCDetectorLevel) { LOGF(info, "JetSpectraEseTask::init() - MC Detector level"); registry.add("mcd/hEventCounter", "event status;event status;entries", {HistType::kTH1F, {{10, 0.0, 10.0}}}); + registry.add("mcd/hUsedEventWeights", "Used reconstructed collisions;Used events;Sum of event weights", {HistType::kTH1D, {{1, 0.0, 1.0}}}); + registry.get(HIST("mcd/hUsedEventWeights"))->Sumw2(); + registry.get(HIST("mcd/hUsedEventWeights"))->GetXaxis()->SetBinLabel(1, "Sum of weights of used events"); registry.add("mcd/hCentralitySel", ";centr;entries", {HistType::kTH1F, {{centAxis}}}); // registry.add("mcd/hJetPt", "particle level jet pT;#it{p}_{T,jet part} (GeV/#it{c});entries", {HistType::kTH1F, {{jetPtAxis}}}); // registry.add("mcd/hJetSparse", ";Centrality;#it{p}_{T,jet det}; #eta; #phi", {HistType::kTHnSparseF, {{centAxis}, {jetPtAxis}, {etaAxis}, {phiAxis}}}); @@ -582,6 +590,9 @@ struct JetSpectraEseTask { registry.add("mcm/hMCEventCounter", "event status;event status;entries", {HistType::kTH1F, {{10, 0.0, 10.0}}}); registry.add("mcm/hMCDMatchedEventCounter", "event status;event status;entries", {HistType::kTH1F, {{10, 0.0, 10.0}}}); + registry.add("mcm/hUsedEventWeights", "Used reconstructed collisions with exactly one reconstructed collision per MC collision;Used events;Sum of event weights", {HistType::kTH1D, {{1, 0.0, 1.0}}}); + registry.get(HIST("mcm/hUsedEventWeights"))->Sumw2(); + registry.get(HIST("mcm/hUsedEventWeights"))->GetXaxis()->SetBinLabel(1, "Sum of weights of used events"); registry.add("mcm/hCentralityAnalyzed", ";Centrality;entries", {HistType::kTH1F, {{centAxis}}}); registry.add("mcm/hMatchedJetsPtDelta", "#it{p}_{T,jet part}; det - part", {HistType::kTH2F, {{jetPtAxis}, {100, -20., 20.0}}}); @@ -615,6 +626,12 @@ struct JetSpectraEseTask { registry.add("hSTrackPtPhiEtaOcc", ";cent;jpt;phi;eta;occ", {HistType::kTHnSparseF, {{centAxis}, {100, 0, 100}, {phiAxis}, {etaAxis}, {occAxis}}}); registry.add("hSJetPtPhiEtaOcc", ";cent;jpt;phi;eta;occ", {HistType::kTHnSparseF, {{centAxis}, {jetPtAxis}, {phiAxis}, {etaAxis}, {occAxis}}}); } + if (doprocessJetAlignment) { + LOGF(info, "JetSpectraEseTask::init() - Jet Alignment Process"); + registry.add("hCentq2", ";cent;#it{q}_{2};occ", {HistType::kTHnSparseF, {{centAxis}, {100, 0, 100}}}); + registry.add("hTrackPtPhiEta", ";cent;p_{T,track};#phi;#eta", {HistType::kTHnSparseF, {{centAxis}, {100, 0, 100}, {phiAxis}, {etaAxis}}}); + registry.add("hJetPtPhiEta", ";cent;p_{T,jet};jet #phi;jet #eta", {HistType::kTHnSparseF, {{centAxis}, {250, -50, 200}, {phiAxis}, {etaAxis}}}); + } if (doprocessMCGenTrack) { LOGF(info, "JetSpectraEseTask::init() - MCGen track"); registry.add("mcgen/h3TrackPtGen", ";#it{p}_{T,track}; vtxZ; #eta", {HistType::kTH3F, {{assocTrackPt}, {vertexZAxis}, {etaAxis}}}); @@ -730,7 +747,6 @@ struct JetSpectraEseTask { registry.fill(HIST("eventQA/hRho"), centrality, collision.rho()); registry.fill(HIST("eventQA/hCentralityAnalyzed"), centrality); int lRndInd = fRndm->Integer(NumSubSmpl); - hCentralityAnalyzedSub[lRndInd]->Fill(centrality); auto corrL = [&](const auto& j) { return j.pt() - evalRho(rhoFit.get(), jetR, j.phi(), collision.rho()) * j.area(); }; for (const auto& jet : jets) { @@ -763,6 +779,7 @@ struct JetSpectraEseTask { } registry.fill(HIST("hNtrig"), centrality, vCorrL, dPhi, qPerc[0]); + hNtrigSub[lRndInd]->Fill(centrality, vCorrL, dPhi, qPerc[0]); for (const auto& track : tracks) { if (!jetderiveddatautilities::selectTrack(track, trackSelection)) { continue; @@ -1084,6 +1101,57 @@ struct JetSpectraEseTask { } PROCESS_SWITCH(JetSpectraEseTask, processESEOccupancy, "process occupancy", false); + void processJetAlignment(soa::Join::iterator const& collision, + soa::Join const& tracks, soa::Filtered> const& jets) + { + const auto psi{procEP(collision)}; + if (!psi.isValid) { + return; + } + const auto qPerc{collision.qPERCFT0C()}; + if (qPerc[0] < 0) { + return; + } + + std::unique_ptr rhoFit{nullptr}; + if (cfgrhoPhi) { + rhoFit = fitRho(collision, psi, tracks, jets); + if (!rhoFit) { + return; + } + } + + if (!isVertexSelected(collision)) { + return; + } + + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { + return; + } + + registry.fill(HIST("hCentq2"), getCentrality(collision), qPerc[0]); + auto corrL = [&](const auto& j) { return j.pt() - evalRho(rhoFit.get(), jetR, j.phi(), collision.rho()) * j.area(); }; + + for (const auto& jet : jets) { + if (!jetfindingutilities::isInEtaAcceptance(jet, cfgJetEta->at(0), cfgJetEta->at(1), trackCuts.trackEtaMin, trackCuts.trackEtaMax)) { + continue; + } + if (!isAcceptedJet>(jet)) { + continue; + } + auto vCorrL = cfgrhoPhi ? corrL(jet) : corr(collision, jet); + registry.fill(HIST("hJetPtPhiEta"), getCentrality(collision), vCorrL, jet.phi(), jet.eta()); + } + + for (auto const& track : tracks) { + if (!jetderiveddatautilities::selectTrack(track, trackSelection)) { + continue; + } + registry.fill(HIST("hTrackPtPhiEta"), getCentrality(collision), track.pt(), track.phi(), track.eta()); + } + } + PROCESS_SWITCH(JetSpectraEseTask, processJetAlignment, "process jet+track spectra for QA checking alignment issues", false); + void processMCParticleLevel(soa::Filtered>::iterator const& mcCollision, soa::SmallGroups const& collisions, soa::Filtered> const& jets, @@ -1127,6 +1195,9 @@ struct JetSpectraEseTask { registry.fill(HIST("mcp/hCentralitySel"), centrality); const float eventWeight = cfgUseMCEventWeights ? mcCollision.weight() : 1.0f; const float pTHat = getPTHat(eventWeight); + if (!(pTHat < pTHatAbsoluteMin)) { + registry.fill(HIST("mcp/hUsedEventWeights"), 0.5, eventWeight); + } jetLoopMCP(jets, centrality, mcCollision.rho(), eventWeight, pTHat); } PROCESS_SWITCH(JetSpectraEseTask, processMCParticleLevel, "jets on particle level MC", false); @@ -1134,7 +1205,8 @@ struct JetSpectraEseTask { void processMCDetectorLevel(soa::Filtered>::iterator const& collision, ChargedMCDJets const& mcdjets, aod::JetTracks const&, - aod::JetParticles const&) + aod::JetParticles const&, + aod::JetMcCollisions const&) { float counter{0.5f}; registry.fill(HIST("mcd/hEventCounter"), counter++); @@ -1155,6 +1227,17 @@ struct JetSpectraEseTask { auto centrality = cfgisPbPb ? getCentrality(collision) : -1; registry.fill(HIST("mcd/hCentralitySel"), centrality); + float eventWeight = 1.0f; + if (cfgUseMCEventWeights) { + eventWeight = collision.has_mcCollision() ? collision.mcCollision_as().weight() : -1.0f; + } + if (eventWeight <= 0.0f) { + return; + } + const float pTHat = getPTHat(eventWeight); + if (!(pTHat < pTHatAbsoluteMin)) { + registry.fill(HIST("mcd/hUsedEventWeights"), 0.5, eventWeight); + } jetLoopMCD(mcdjets, centrality, collision.rho()); } PROCESS_SWITCH(JetSpectraEseTask, processMCDetectorLevel, "jets on detector level", false); @@ -1207,6 +1290,9 @@ struct JetSpectraEseTask { registry.fill(HIST("mcm/hCentralityAnalyzed"), centrality); float eventWeight = cfgUseMCEventWeights ? mcCol.weight() : 1.0; float pTHat = getPTHat(eventWeight); + if (!(pTHat < pTHatAbsoluteMin)) { + registry.fill(HIST("mcm/hUsedEventWeights"), 0.5, eventWeight); + } matchedJetLoop(mcdjets.sliceBy(mcdjetsPerJCollision, collision.globalIndex()), centrality, collision.rho(), mcCol.rho(), eventWeight, pTHat); registry.fill(HIST("mcm/hMCDMatchedEventCounter"), secCount++); @@ -1857,6 +1943,9 @@ struct JetSpectraEseTask { weight = jet.eventWeight(); } const float pTHat = getPTHat(weight); + if (pTHat < pTHatAbsoluteMin) { + continue; + } if (jet.pt() > pTHatMaxMCD * pTHat) { continue; } @@ -1867,6 +1956,9 @@ struct JetSpectraEseTask { template void jetLoopMCP(const Jets& jets, const float& centrality, const float& rho, float weight = 1.0f, float pTHat = 999.0f) { + if (pTHat < pTHatAbsoluteMin) { + return; + } bool mcLevelIsParticleLevel = true; for (const auto& jet : jets) { if (!jetfindingutilities::isInEtaAcceptance(jet, cfgJetEta->at(0), cfgJetEta->at(1), trackCuts.trackEtaMin, trackCuts.trackEtaMax)) { @@ -1889,6 +1981,9 @@ struct JetSpectraEseTask { template void matchedJetLoop(const Jets& jets, const float& centrality, const float& rho, const float& rho2, float weight = 1.0, float pTHat = 999.0) { + if (pTHat < pTHatAbsoluteMin) { + return; + } for (const auto& jet : jets) { if (!jetfindingutilities::isInEtaAcceptance(jet, cfgJetEta->at(0), cfgJetEta->at(1), trackCuts.trackEtaMin, trackCuts.trackEtaMax)) { continue; @@ -2020,6 +2115,9 @@ struct JetSpectraEseTask { if (!checkConstituentMinPt && !checkConstituentMaxPt) { checkConstituentPt = false; } + if (mcLevelIsParticleLevel && !checkLeadConstituentPtForMcpJets) { + checkConstituentPt = false; + } if (checkConstituentPt) { bool isMinLeadingConstituent = !checkConstituentMinPt; @@ -2028,7 +2126,7 @@ struct JetSpectraEseTask { for (const auto& constituent : jet.template tracks_as()) { double pt = constituent.pt(); - if ((!checkLeadConstituentMinPtForMcpJets && mcLevelIsParticleLevel) || (checkConstituentMinPt && pt >= leadingConstituentPtMin)) { // if the jet is mcp level and checkLeadConstituentMinPtForMcpJets is true, then the pt of the leading track of that jet does not need to be below the defined leadingConstituentPtMin cut + if ((checkConstituentMinPt && pt >= leadingConstituentPtMin)) { isMinLeadingConstituent = true; } if (checkConstituentMaxPt && pt > leadingConstituentPtMax) { diff --git a/PWGJE/Tasks/jetSubstructure.cxx b/PWGJE/Tasks/jetSubstructure.cxx index fe568b6c2a6..6169bd82f5c 100644 --- a/PWGJE/Tasks/jetSubstructure.cxx +++ b/PWGJE/Tasks/jetSubstructure.cxx @@ -80,6 +80,7 @@ struct JetSubstructureTask { Configurable doPairBkg{"doPairBkg", true, "save bkg pairs"}; Configurable pairConstituentPtMin{"pairConstituentPtMin", 1.0, "pt cut off for constituents going into pairs"}; Configurable trackSelections{"trackSelections", "globalTracks", "set track selections"}; + Configurable isEmbedding{"isEmbedding", false, "running on an embedded dataset"}; Service pdg; std::vector jetConstituents; @@ -129,7 +130,7 @@ struct JetSubstructureTask { jetReclusterer.algorithm = fastjet::JetAlgorithm::cambridge_algorithm; jetReclusterer.ghostRepeatN = 0; - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); } Preslice TracksPerCollision = aod::jtrack::collisionId; @@ -260,7 +261,7 @@ struct JetSubstructureTask { } if constexpr (!std::is_same_v, aod::JetParticles>) { - if (!jetfindingutilities::isTrackSelected(track, trackSelection)) { + if (!jetfindingutilities::isTrackSelected(track, trackSelection, isEmbedding)) { continue; } } diff --git a/PWGJE/Tasks/jetSubstructureHF.h b/PWGJE/Tasks/jetSubstructureHF.h index b0921af1f4e..8ac281fad77 100644 --- a/PWGJE/Tasks/jetSubstructureHF.h +++ b/PWGJE/Tasks/jetSubstructureHF.h @@ -83,6 +83,7 @@ struct JetSubstructureHFTask { o2::framework::Configurable pairConstituentPtMin{"pairConstituentPtMin", 1.0, "pt cut off for constituents going into pairs"}; o2::framework::Configurable trackSelections{"trackSelections", "globalTracks", "set track selections"}; o2::framework::Configurable recoilRegion{"recoilRegion", 0.6, "recoil acceptance in phi"}; + o2::framework::Configurable isEmbedding{"isEmbedding", false, "running on an embedded dataset"}; o2::framework::Service pdg; float candMass; @@ -136,7 +137,7 @@ struct JetSubstructureHFTask { candMass = jetcandidateutilities::getTablePDGMass(); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); } o2::framework::Preslice TracksPerCollision = o2::aod::jtrack::collisionId; @@ -356,7 +357,7 @@ struct JetSubstructureHFTask { } if constexpr (!std::is_same_v, o2::aod::JetParticles>) { - if (!jetfindingutilities::isTrackSelected(track, trackSelection)) { + if (!jetfindingutilities::isTrackSelected(track, trackSelection, isEmbedding)) { continue; } } diff --git a/PWGJE/Tasks/jetSubstructureHFOutput.h b/PWGJE/Tasks/jetSubstructureHFOutput.h index 2eb799a6708..4c33d2d989e 100644 --- a/PWGJE/Tasks/jetSubstructureHFOutput.h +++ b/PWGJE/Tasks/jetSubstructureHFOutput.h @@ -139,8 +139,8 @@ struct JetSubstructureHFOutputTask { void init(o2::framework::InitContext const&) { - jetRadiiValues = (std::vector)configs.jetRadii; - recoilJetRadiiValues = (std::vector)configs.recoilJetRadii; + jetRadiiValues = configs.jetRadii.value; + recoilJetRadiiValues = configs.recoilJetRadii.value; } struct : o2::framework::PresliceGroup { diff --git a/PWGJE/Tasks/jetSubstructureOutput.cxx b/PWGJE/Tasks/jetSubstructureOutput.cxx index 6dd90638faa..ef90a7e88c8 100644 --- a/PWGJE/Tasks/jetSubstructureOutput.cxx +++ b/PWGJE/Tasks/jetSubstructureOutput.cxx @@ -96,7 +96,7 @@ struct JetSubstructureOutputTask { void init(InitContext const&) { - jetRadiiValues = (std::vector)jetRadii; + jetRadiiValues = jetRadii; } Preslice> splittingsPerJetData = aod::chargedsplitting::jetId; diff --git a/PWGJE/Tasks/jetTaggerHFQA.cxx b/PWGJE/Tasks/jetTaggerHFQA.cxx index b7cbbe76f5d..b05c388d4ef 100644 --- a/PWGJE/Tasks/jetTaggerHFQA.cxx +++ b/PWGJE/Tasks/jetTaggerHFQA.cxx @@ -117,8 +117,8 @@ struct JetTaggerHFQA { { numberOfJetFlavourSpecies = static_cast(numFlavourSpecies); - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); // Axis AxisSpec axisJetFlavour = {binJetFlavour, "Jet flavour"}; AxisSpec axisJetPt = {binJetPt, "#it{p}_{T, jet}"}; diff --git a/PWGJE/Tasks/jetTriggerChargedQa.cxx b/PWGJE/Tasks/jetTriggerChargedQa.cxx index 22ee5b8306d..bb491523d27 100644 --- a/PWGJE/Tasks/jetTriggerChargedQa.cxx +++ b/PWGJE/Tasks/jetTriggerChargedQa.cxx @@ -97,7 +97,7 @@ struct JetTriggerChargedQa { { fiducialVolume = static_cast(cfgTPCVolume) - static_cast(cfgJetR); eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(evSel)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); // Basic histos spectra.add("vertexZ", "z vertex", kTH1F, {{60, -12., 12.}}); diff --git a/PWGJE/Tasks/jetTutorial.cxx b/PWGJE/Tasks/jetTutorial.cxx index c5544e14257..25e89677000 100644 --- a/PWGJE/Tasks/jetTutorial.cxx +++ b/PWGJE/Tasks/jetTutorial.cxx @@ -93,8 +93,8 @@ struct JetTutorial { void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); } diff --git a/PWGJE/Tasks/jetTutorialSkeleton.cxx b/PWGJE/Tasks/jetTutorialSkeleton.cxx index 763f2dc4c0e..e5de1f294d8 100644 --- a/PWGJE/Tasks/jetTutorialSkeleton.cxx +++ b/PWGJE/Tasks/jetTutorialSkeleton.cxx @@ -84,8 +84,8 @@ struct JetTutorialSkeletonTask { void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); } diff --git a/PWGJE/Tasks/jetUpcQa.cxx b/PWGJE/Tasks/jetUpcQa.cxx index fa1da836679..ed86c080f57 100644 --- a/PWGJE/Tasks/jetUpcQa.cxx +++ b/PWGJE/Tasks/jetUpcQa.cxx @@ -66,6 +66,7 @@ DECLARE_SOA_COLUMN(TimeZNC, timeZNC, float); DECLARE_SOA_COLUMN(NeutronClass, neutronClass, int); DECLARE_SOA_COLUMN(IsGapTagged, isGapTagged, bool); DECLARE_SOA_COLUMN(IsZDCTagged, isZDCTagged, bool); +DECLARE_SOA_COLUMN(CollisionIndex, collisionIndex, int64_t); } // namespace upcjetevent DECLARE_SOA_TABLE(UpcJetEvents, "AOD", "UPCJETEVENT", @@ -78,7 +79,8 @@ DECLARE_SOA_TABLE(UpcJetEvents, "AOD", "UPCJETEVENT", upcjetevent::TimeZNC, upcjetevent::NeutronClass, upcjetevent::IsGapTagged, - upcjetevent::IsZDCTagged); + upcjetevent::IsZDCTagged, + upcjetevent::CollisionIndex); using UpcJetEvent = UpcJetEvents::iterator; namespace upcjet @@ -106,13 +108,23 @@ DECLARE_SOA_INDEX_COLUMN_FULL(UpcJetEvent, upcJetEvent, int32_t, UpcJetEvents, " DECLARE_SOA_COLUMN(Pt, pt, float); DECLARE_SOA_COLUMN(Eta, eta, float); DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(TrackCutFlag, trackCutFlag, uint16_t); +DECLARE_SOA_COLUMN(TrackCutFlagFb5, trackCutFlagFb5, bool); +DECLARE_SOA_COLUMN(DcaXY, dcaXY, float); +DECLARE_SOA_COLUMN(DcaZ, dcaZ, float); +DECLARE_SOA_COLUMN(PassesConfiguredSelection, passesConfiguredSelection, bool); } // namespace upctrack DECLARE_SOA_TABLE(UpcTracks, "AOD", "UPCTRACK", upctrack::UpcJetEventId, upctrack::Pt, upctrack::Eta, - upctrack::Phi); + upctrack::Phi, + upctrack::TrackCutFlag, + upctrack::TrackCutFlagFb5, + upctrack::DcaXY, + upctrack::DcaZ, + upctrack::PassesConfiguredSelection); using UpcTrack = UpcTracks::iterator; namespace upcjettrack @@ -147,6 +159,7 @@ DECLARE_SOA_COLUMN(TimeZNC, timeZNC, float); DECLARE_SOA_COLUMN(NeutronClass, neutronClass, int); DECLARE_SOA_COLUMN(IsGapTagged, isGapTagged, bool); DECLARE_SOA_COLUMN(IsZDCTagged, isZDCTagged, bool); +DECLARE_SOA_COLUMN(CollisionIndex, collisionIndex, int64_t); } // namespace upcjeteventmcd DECLARE_SOA_TABLE(UpcJetEventsMCD, "AOD", "UPCJETEVENTMCD", @@ -159,7 +172,8 @@ DECLARE_SOA_TABLE(UpcJetEventsMCD, "AOD", "UPCJETEVENTMCD", upcjeteventmcd::TimeZNC, upcjeteventmcd::NeutronClass, upcjeteventmcd::IsGapTagged, - upcjeteventmcd::IsZDCTagged); + upcjeteventmcd::IsZDCTagged, + upcjeteventmcd::CollisionIndex); using UpcJetEventMCD = UpcJetEventsMCD::iterator; namespace upctrackmcd @@ -168,13 +182,23 @@ DECLARE_SOA_INDEX_COLUMN_FULL(UpcJetEventMCD, upcJetEventMCD, int32_t, UpcJetEve DECLARE_SOA_COLUMN(Pt, pt, float); DECLARE_SOA_COLUMN(Eta, eta, float); DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(TrackCutFlag, trackCutFlag, uint16_t); +DECLARE_SOA_COLUMN(TrackCutFlagFb5, trackCutFlagFb5, bool); +DECLARE_SOA_COLUMN(DcaXY, dcaXY, float); +DECLARE_SOA_COLUMN(DcaZ, dcaZ, float); +DECLARE_SOA_COLUMN(PassesConfiguredSelection, passesConfiguredSelection, bool); } // namespace upctrackmcd DECLARE_SOA_TABLE(UpcTracksMCD, "AOD", "UPCTRACKMCD", upctrackmcd::UpcJetEventMCDId, upctrackmcd::Pt, upctrackmcd::Eta, - upctrackmcd::Phi); + upctrackmcd::Phi, + upctrackmcd::TrackCutFlag, + upctrackmcd::TrackCutFlagFb5, + upctrackmcd::DcaXY, + upctrackmcd::DcaZ, + upctrackmcd::PassesConfiguredSelection); using UpcTrackMCD = UpcTracksMCD::iterator; namespace upcjetmcd @@ -332,8 +356,11 @@ struct JetUpcQa { Configurable cfgMaxNTracks{"cfgMaxNTracks", 100, "Max tracks"}; Configurable cfgTrackPtMin{"cfgTrackPtMin", 0.15f, "Min track pT (GeV/c)"}; + Configurable cfgTrackPtMax{"cfgTrackPtMax", 100.f, "Max track pT (GeV/c), matching derived-data kinematics"}; Configurable cfgTrackEtaMax{"cfgTrackEtaMax", 0.9f, "Max track |eta|"}; - Configurable cfgTrackSelection{"cfgTrackSelection", "kinematicsOnly", "Reconstructed-track selection: kinematicsOnly, globalTracks, or udDefault"}; + Configurable cfgTrackSelection{"cfgTrackSelection", "derivedDataSelection", "Reconstructed-track selection: derivedDataSelection, derivedQualityTracksWDCA, kinematicsOnly, globalTracks, or udDefault"}; + Configurable cfgDerivedTrackDCAZMax{"cfgDerivedTrackDCAZMax", 0.2f, "Maximum |DCAz| for the derived-data QualityTracksWDCA selection (cm)"}; + Configurable cfgVertexZCut{"cfgVertexZCut", 10.f, "Maximum absolute collision vertex z (cm)"}; Configurable cfgTrackDCAZMax{"cfgTrackDCAZMax", 2.f, "Maximum |DCAz| for udDefault track selection (cm)"}; Configurable cfgTrackDCAXYMax{"cfgTrackDCAXYMax", 0.f, "Maximum |DCAxy| for udDefault (cm); 0 uses the pT-dependent UD cut"}; Configurable cfgTrackTPCChi2NClMax{"cfgTrackTPCChi2NClMax", 4.f, "Maximum TPC chi2 per cluster for udDefault"}; @@ -367,23 +394,29 @@ struct JetUpcQa { HistogramRegistry registry{"registry"}; enum class TrackSelectionMode { + DerivedDataSelection, + DerivedQualityTracksWDCA, KinematicsOnly, GlobalTracks, UDDefault }; - TrackSelectionMode trackSelectionMode = TrackSelectionMode::KinematicsOnly; + TrackSelectionMode trackSelectionMode = TrackSelectionMode::DerivedDataSelection; // Init void init(InitContext&) { - if (cfgTrackSelection.value == "kinematicsOnly") { + if (cfgTrackSelection.value == "derivedDataSelection") { + trackSelectionMode = TrackSelectionMode::DerivedDataSelection; + } else if (cfgTrackSelection.value == "derivedQualityTracksWDCA") { + trackSelectionMode = TrackSelectionMode::DerivedQualityTracksWDCA; + } else if (cfgTrackSelection.value == "kinematicsOnly") { trackSelectionMode = TrackSelectionMode::KinematicsOnly; } else if (cfgTrackSelection.value == "globalTracks") { trackSelectionMode = TrackSelectionMode::GlobalTracks; } else if (cfgTrackSelection.value == "udDefault") { trackSelectionMode = TrackSelectionMode::UDDefault; } else { - LOGP(fatal, "Unknown cfgTrackSelection '{}'. Choose kinematicsOnly, globalTracks, or udDefault.", cfgTrackSelection.value); + LOGP(fatal, "Unknown cfgTrackSelection '{}'. Choose derivedDataSelection, derivedQualityTracksWDCA, kinematicsOnly, globalTracks, or udDefault.", cfgTrackSelection.value); } sgCuts.SetNDtcoll(cfgNDtcoll); @@ -420,12 +453,29 @@ struct JetUpcQa { registry.add("hSelectionBitsMCD", "Selection bits (MCD);0 all 1 gap 2 zdc 3 accepted;Counts", {HistType::kTH1F, {{4, -0.5, 3.5}}}); } + template + bool passesTrackKinematics(TTrack const& track) const + { + return std::abs(track.eta()) <= cfgTrackEtaMax.value && + track.pt() >= cfgTrackPtMin.value && + track.pt() <= cfgTrackPtMax.value; + } + template bool isSelectedTrack(TTrack const& track) const { - if (std::abs(track.eta()) > cfgTrackEtaMax.value || track.pt() < cfgTrackPtMin.value) { + if (!passesTrackKinematics(track)) { return false; } + if (trackSelectionMode == TrackSelectionMode::DerivedQualityTracksWDCA) { + // Mirror JetDerivedDataUtilities::setTrackSelectionBit(...): QualityTracksWDCA. + return track.isQualityTrack() && std::abs(track.dcaZ()) < cfgDerivedTrackDCAZMax.value; + } + if (trackSelectionMode == TrackSelectionMode::DerivedDataSelection) { + // Match the derived-data writer: retain tracks passing any stored selection bit + // (global, quality, or hybrid). QualityTracksWDCA is a subset of QualityTracks. + return track.isGlobalTrackWoPtEta() || track.isQualityTrack() || track.trackCutFlagFb5(); + } if (trackSelectionMode == TrackSelectionMode::KinematicsOnly) { return true; } @@ -505,9 +555,10 @@ struct JetUpcQa { return zdc.neutronClass > 0; } - bool isAcceptedEvent(bool isGapTagged, bool isZDCTagged) const + bool isAcceptedEvent(bool isGapTagged, bool isZDCTagged, float posZ) const { - return (!cfgUseGapTagging.value || isGapTagged) && + return std::abs(posZ) <= cfgVertexZCut.value && + (!cfgUseGapTagging.value || isGapTagged) && (!cfgUseZDCTagging.value || isZDCTagged); } @@ -748,7 +799,7 @@ struct JetUpcQa { int gapSide = gapInfo.gapSide; bool isGapTagged = isSelectedGap(gapSide); bool isZDCTagged = isSelectedZDC(gapInfo.zdc); - bool isAccepted = isAcceptedEvent(isGapTagged, isZDCTagged); + bool isAccepted = isAcceptedEvent(isGapTagged, isZDCTagged, collision.posZ()); registry.fill(HIST("hGapSide"), gapSide); registry.fill(HIST("hZNAEnergy"), gapInfo.zdc.energyCommonZNA); registry.fill(HIST("hZNCEnergy"), gapInfo.zdc.energyCommonZNC); @@ -773,7 +824,9 @@ struct JetUpcQa { gapInfo.zdc.timeZNC, gapInfo.zdc.neutronClass, isGapTagged, - isZDCTagged); + isZDCTagged, + collision.globalIndex()); + // This is the row index in our output event table, not the input collision index. int evtIdx = upcJetEventTable.lastIndex(); std::vector fjInput; @@ -783,12 +836,17 @@ struct JetUpcQa { if (t.collisionId() != collision.globalIndex()) { continue; } - if (!isSelectedTrack(t)) { + if (!passesTrackKinematics(t)) { continue; } + bool passesConfiguredSelection = isSelectedTrack(t); - // event-level track table - upcTrackTable(evtIdx, t.pt(), t.eta(), t.phi()); + // Store all kinematically accepted candidates and the current decision so + // alternate track selections can be reproduced from the output table. + upcTrackTable(evtIdx, t.pt(), t.eta(), t.phi(), t.trackCutFlag(), t.trackCutFlagFb5(), t.dcaXY(), t.dcaZ(), passesConfiguredSelection); + if (!passesConfiguredSelection) { + continue; + } // jet finding input fastjet::PseudoJet p; @@ -843,7 +901,7 @@ struct JetUpcQa { int gapSide = gapInfo.gapSide; bool isGapTagged = isSelectedGap(gapSide); bool isZDCTagged = isSelectedZDC(gapInfo.zdc); - bool isAccepted = isAcceptedEvent(isGapTagged, isZDCTagged); + bool isAccepted = isAcceptedEvent(isGapTagged, isZDCTagged, collision.posZ()); registry.fill(HIST("hGapSideMCD"), gapSide); registry.fill(HIST("hZNAEnergyMCD"), gapInfo.zdc.energyCommonZNA); registry.fill(HIST("hZNCEnergyMCD"), gapInfo.zdc.energyCommonZNC); @@ -868,7 +926,8 @@ struct JetUpcQa { gapInfo.zdc.timeZNC, gapInfo.zdc.neutronClass, isGapTagged, - isZDCTagged); + isZDCTagged, + collision.globalIndex()); int evtIdx = upcJetEventTableMCD.lastIndex(); std::vector fjInput; @@ -878,11 +937,15 @@ struct JetUpcQa { if (t.collisionId() != collision.globalIndex()) { continue; } - if (!isSelectedTrack(t)) { + if (!passesTrackKinematics(t)) { continue; } + bool passesConfiguredSelection = isSelectedTrack(t); - upcTrackTableMCD(evtIdx, t.pt(), t.eta(), t.phi()); + upcTrackTableMCD(evtIdx, t.pt(), t.eta(), t.phi(), t.trackCutFlag(), t.trackCutFlagFb5(), t.dcaXY(), t.dcaZ(), passesConfiguredSelection); + if (!passesConfiguredSelection) { + continue; + } fastjet::PseudoJet p; p.reset_PtYPhiM(t.pt(), t.eta(), t.phi(), o2::constants::physics::MassPiPlus); diff --git a/PWGJE/Tasks/jetValidationQA.cxx b/PWGJE/Tasks/jetValidationQA.cxx index 18942a5b59d..9eac3ed0651 100644 --- a/PWGJE/Tasks/jetValidationQA.cxx +++ b/PWGJE/Tasks/jetValidationQA.cxx @@ -56,7 +56,7 @@ struct jetTrackCollisionQa { void init(InitContext const&) { - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); // histograms const AxisSpec vtxZAxis{nBins, -20, 20, "Vtx_{z} (cm)"}; @@ -65,7 +65,7 @@ struct jetTrackCollisionQa { const AxisSpec ptAxis{BinsPt, "#it{p}_{T} (GeV/#it{c})"}; // set trackselections - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); // histograms // 1)Jetvalidation on data mHistManager.add("collisionVtxZ", "selected collsion VtxZ ", HistType::kTH1D, {vtxZAxis}); @@ -337,7 +337,7 @@ struct mcJetTrackCollisionQa { void init(InitContext const&) { // set trackselection - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); // histograms const AxisSpec vtxZAxis{nBins, -20, 20, "Vtx_{z} (cm)"}; const AxisSpec ptAxis{BinsPt, "#it{p}_{T} (GeV/#it{c})"}; diff --git a/PWGJE/Tasks/lfJetSpectra.cxx b/PWGJE/Tasks/lfJetSpectra.cxx new file mode 100644 index 00000000000..72fb9c3346f --- /dev/null +++ b/PWGJE/Tasks/lfJetSpectra.cxx @@ -0,0 +1,1346 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file lfJetSpectra.cxx +/// \brief pT spectra of K0s and Lambda/AntiLambda in jets and in the underlying event +/// +/// \author Francesca Ercolessi + +#include "PWGJE/Core/FastJetUtilities.h" +#include "PWGJE/Core/JetCandidateUtilities.h" +#include "PWGJE/Core/JetDerivedDataUtilities.h" +#include "PWGJE/Core/JetFinder.h" +#include "PWGJE/Core/JetFindingUtilities.h" +#include "PWGJE/Core/JetUtilities.h" +#include "PWGJE/DataModel/Jet.h" +#include "PWGJE/DataModel/JetReducedData.h" +#include "PWGJE/DataModel/JetReducedDataV0.h" +#include "PWGLF/DataModel/mcCentrality.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/Core/TableHelper.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +// Jets reconstructed by the PWGJE V0 jet finders (jet-finder-v0-{data,mcd,mcp}-charged): charged tracks (particles) + V0 candidates, +// where the V0 daughters are removed from the track (particle) list and the V0 enters the clustering as a single constituent +using V0ChargedJetsWithConstituents = soa::Join; +using MCDV0JetsWithConstituents = soa::Join; +using MCPV0JetsWithConstituents = soa::Join; +// Jets geometrically matched between detector and particle level (PWGJE jet-matching-mc-v0-ch), for the jet pT response +using MatchedMCDV0Jets = soa::Join; +using MatchedMCPV0JetsWithConstituents = soa::Join; + +struct LfJetSpectra { + HistogramRegistry registryData{"registryData", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry registryMC{"registryMC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry registryQC{"registryQC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + + enum V0Species { kK0s = 0, + kLambda, + kAntiLambda }; + + // Steps of the V0 selection, in the order in which they are applied (bins of the cut flow histogram) + enum V0SelectionStep { kStepAll = 0, + kStepJetFinderAcceptance, + kStepNotRejected, + kStepSignalFlag, + kStepRadius, + kStepDcaDauToPV, + kStepCosPA, + kStepDcaV0Dau, + kStepDauEta, + kStepCompetingMass, + kNSelectionSteps }; + static constexpr std::array StepLabels = {"all", "jet finder acc.", "not rejected", "signal flag", "radius", "DCA dau-PV", "cos PA", "DCA V0 dau", "dau #eta", "competing mass"}; + + // Folders of the V0 QA histograms: all the stored V0s (before selections) and the selected V0s of each species. + // The first three entries are also the species names used in the MC histograms + enum V0QAFolder { kQAK0s = 0, + kQALambda, + kQAAntiLambda, + kQAAllV0s }; + static constexpr std::array QAFolders = {"K0s", "Lambda", "AntiLambda", "AllV0s"}; + + // MC histograms (one per species, axes: multiplicity, pT). + // Reconstructed V0s: true V0s (PDG of the V0 and of the daughters) passing the selections, at reconstructed pT. + // Generated V0s: physical primary V0s, at generated pT. In particle-level jets (kGenJet, kGenUE) they are the V0s + // clustered by the particle-level jet finder (aod::CandidatesV0MCP, all decay channels with v0ChargedDecaysOnly = false), + // with the same in-jet / UE definition as in data. Otherwise they are taken from aod::JetParticles (all decay channels, + // so the efficiency includes the branching ratio) and, for the reco jets, "in jet" means within R from the jet axis + enum MCHistogram { kRecJet = 0, // reco primary V0s, constituents of a reco jet (efficiency numerator, jet) + kRecUE, // reco primary V0s in the UE of a reco jet (efficiency numerator, UE) + kRecJetIncl, // as kRecJet, primaries + secondaries (feed-down / primary fraction) + kRecUEIncl, // as kRecUE, primaries + secondaries (feed-down / primary fraction) + kRecFullEvent, // reco primary V0s in all the reconstructed events (inclusive efficiency numerator) + kRecEventWithJet, // reco primary V0s in the reconstructed events with at least one selected jet + kGenRecoJetJet, // generated V0s within R from the axis of a reco jet (efficiency denominator, jet) + kGenRecoJetUE, // generated V0s in the UE of a reco jet (efficiency denominator, UE) + kGenRecoEvent, // generated V0s in the reconstructed events (inclusive efficiency denominator) + kGenEventWithJet, // generated V0s in the reconstructed events with at least one selected jet + kGenJet, // generated V0s, constituents of a particle-level jet + kGenUE, // generated V0s in the UE of a particle-level jet + kGenFullEvent, // generated V0s in all the generated events (signal loss) + kNMCHistograms }; + static constexpr std::array MCHistSuffixes = {"_reconstructed_jet", "_reconstructed_ue", "_reconstructed_jet_incl", "_reconstructed_ue_incl", "_reconstructed_fullevent", "_reconstructed_eventwjet", + "_generated_recojet_jet", "_generated_recojet_ue", "_generated_recoevent", "_generated_eventwjet", + "_generated_jet", "_generated_ue", "_generated_fullevent"}; + + // MC origin of a reconstructed V0, from the PDG code of the common mother of the two daughters (0 if there is none). + // "Fake" V0s (not a K0s, Lambda or AntiLambda) are clustered in the jets as the real ones and add their momentum to the jet + enum V0Origin { kOriginK0s = 0, + kOriginLambda, + kOriginAntiLambda, + kOriginGammaConversion, + kOriginOtherMother, + kOriginNoCommonMother, + kNV0Origins }; + static constexpr std::array OriginLabels = {"K^{0}_{S}", "#Lambda", "#bar{#Lambda}", "#gamma conversion", "other common mother", "no common mother"}; + + // Source of a real V0 (K0s, Lambda, AntiLambda) clustered in a reconstructed jet: physical primary or secondary, classified + // by the PDG code of the mother. At particle level only the physical primary V0s are clustered: the momentum + // of the V0s from Xi+- and Omega+- is carried by their mother, a charged primary, while the V0s from Xi0 and from the + // interactions in the material have no counterpart in the particle-level jets + enum V0Source { kSourcePrimary = 0, + kSourceXiCharged, + kSourceXi0, + kSourceOmega, + kSourceOtherSecondary, + kNV0Sources }; + static constexpr std::array SourceLabels = {"primary", "from #Xi^{#pm}", "from #Xi^{0}", "from #Omega^{#pm}", "other secondary"}; + + Configurable fillV0QAHistograms{"fillV0QAHistograms", true, "fill the V0 QA histograms (cut flow and topological variables before/after selections)"}; + + // Event selection + Configurable evSel{"evSel", "sel8WithoutTimeFrameBorderCut", "choose event selection"}; + Configurable vertexZCut{"vertexZCut", 10.f, "vertex z cut"}; + + // Jet selection. The anti-kT algorithm and the resolution parameter are set in the V0 jet finders + // (jetAlgorithm = 2, jetRadius = {0.4}); here only the jets with R = rJet are analysed + Configurable jetFinderTaskName{"jetFinderTaskName", "jet-finder-v0-data-charged", "name of the V0 jet finder task in the workflow for data, from which the event, track and V0 selections are read"}; + Configurable jetFinderMCDTaskName{"jetFinderMCDTaskName", "jet-finder-v0-mcd-charged", "name of the V0 jet finder task in the workflow for MC detector level"}; + Configurable jetFinderMCPTaskName{"jetFinderMCPTaskName", "jet-finder-v0-mcp-charged", "name of the V0 jet finder task in the workflow for MC particle level"}; + Configurable derivedDataProducerTaskName{"derivedDataProducerTaskName", "jet-deriveddata-producer", "name of the PWGJE derived data producer in the MC workflow (producing the particle-level V0 table)"}; + Configurable rJet{"rJet", 0.4, "Jet resolution parameter (R)"}; + Configurable minJetPt{"minJetPt", 10.0, "Minimum background-subtracted pt of the jet (GeV/c)"}; + Configurable maxJetEta{"maxJetEta", 0.5, "Maximum |eta| of the jet axis (should be <= 0.9 - R so that jet and UE cones are fully in acceptance)"}; + + // V0 selections, applied in the same way to the V0s in jets and in the UE + struct : ConfigurableGroup { + std::string prefix = "v0Cuts"; + Configurable requireV0SignalFlags{"requireV0SignalFlags", false, "require also the species flag of the v0-selector (K0S/Lambda/AntiLambda candidate)"}; + // topological selections + Configurable minimumV0Radius{"minimumV0Radius", 0.9, "Minimum V0 transverse decay radius (cm)"}; + Configurable maximumV0Radius{"maximumV0Radius", 40.0, "Maximum V0 transverse decay radius (cm)"}; + Configurable dcaDaughterToPVmin{"dcaDaughterToPVmin", 0.06, "Minimum DCA of the V0 daughters to the primary vertex (cm)"}; + Configurable v0cospaMin{"v0cospaMin", 0.99, "Minimum V0 cosine of pointing angle"}; + Configurable dcaV0DaughtersMax{"dcaV0DaughtersMax", 0.5, "Maximum DCA between the V0 daughters (cm)"}; + Configurable daughterEtaMax{"daughterEtaMax", 0.8, "Maximum |eta| of the V0 daughters"}; + // competing V0 mass rejection + Configurable doCompetingMassRejection{"doCompetingMassRejection", true, "Apply the competing V0 mass rejection"}; + Configurable lambdaRejWindow{"lambdaRejWindow", 0.005, "Lambda mass window rejected for K0s candidates (GeV/c^2)"}; + Configurable k0sRejWindow{"k0sRejWindow", 0.010, "K0s mass window rejected for (Anti)Lambda candidates (GeV/c^2)"}; + } v0Cuts; + + // MC options + Configurable inclusiveEtaMax{"inclusiveEtaMax", 0.8, "Maximum |eta| (generated) of the V0s in the inclusive (full event) MC histograms"}; + + // Multiplicity binning + ConfigurableAxis multBinning{"multBinning", {VARIABLE_WIDTH, 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, "Binning of multiplicity axis"}; + + Service pdgDatabase; + + // phiExclusionMin at or below this value: no phi exclusion region (same convention as the V0 jet finder) + static constexpr float PhiExclusionUnset = -998.f; + + // Selections of a V0 jet finder, read from its configuration in init() so that the jet finder input + // (used for the rho estimate), the selected events and the V0s tested for the UE are exactly the ones of the clustering + struct JetFinderConfig { + std::string eventSelections = "sel8"; + bool skipMBGapEvents = true; + bool applyRCTSelections = true; + std::string trackSelections = "globalTracks"; + std::string particleSelections = "PhysicalPrimary"; + float trackPtMin = 0.15; + float trackPtMax = 1000.0; + float trackEtaMin = -0.9; + float trackEtaMax = 0.9; + float trackPhiMin = -999; + float trackPhiMax = 999; + float phiExclusionMin = -999; + float phiExclusionMax = 999; + bool isEmbedding = false; + float candPtMin = 0.0; + float candPtMax = 100.0; + float candYMin = -0.8; + float candYMax = 0.8; + float candPDG = static_cast(PDG_t::kK0Short); + bool useV0SignalFlags = true; + std::vector eventSelectionBits; + }; + JetFinderConfig jetFinderConfigDet; // data or MC detector level + JetFinderConfig jetFinderConfigGen; // MC particle level + int trackSelection = -1; + int candIndex = 0; + + std::vector eventSelectionBits; + std::vector inputParticles; + + Filter jetCollisionFilter = nabs(aod::jcollision::posZ) < vertexZCut; + Filter jetMcCollisionFilter = nabs(aod::jmccollision::posZ) < vertexZCut; + + // Particle-level jet finder input and jets of the MC collision associated to a reconstructed collision (jet pT response) + Preslice v0MCPPerMcCollision = aod::jv0indices::mcCollisionId; + Preslice particlesPerMcCollision = aod::jmcparticle::mcCollisionId; + Preslice mcpJetsPerMcCollision = aod::jet::mcCollisionId; + + // Read the configuration of a V0 jet finder running in the same workflow + void readJetFinderConfig(InitContext& initContext, const std::string& taskName, JetFinderConfig& config, bool isParticleLevel) + { + auto readJetFinderOption = [&](const std::string& optName, auto& value) { + if (!o2::common::core::getTaskOptionValue(initContext, taskName, optName, value, false)) { + LOG(fatal) << "Option '" << optName << "' not found in task '" << taskName << "': the V0 jet finder must run in the same workflow"; + } + }; + readJetFinderOption("eventSelections", config.eventSelections); + readJetFinderOption("skipMBGapEvents", config.skipMBGapEvents); + readJetFinderOption("applyRCTSelections", config.applyRCTSelections); + readJetFinderOption("trackPtMin", config.trackPtMin); + readJetFinderOption("trackPtMax", config.trackPtMax); + readJetFinderOption("trackEtaMin", config.trackEtaMin); + readJetFinderOption("trackEtaMax", config.trackEtaMax); + readJetFinderOption("trackPhiMin", config.trackPhiMin); + readJetFinderOption("trackPhiMax", config.trackPhiMax); + readJetFinderOption("phiExclusionMin", config.phiExclusionMin); + readJetFinderOption("phiExclusionMax", config.phiExclusionMax); + readJetFinderOption("candPtMin", config.candPtMin); + readJetFinderOption("candPtMax", config.candPtMax); + readJetFinderOption("candYMin", config.candYMin); + readJetFinderOption("candYMax", config.candYMax); + if (isParticleLevel) { + readJetFinderOption("particleSelections", config.particleSelections); + } else { + readJetFinderOption("trackSelections", config.trackSelections); + readJetFinderOption("isEmbedding", config.isEmbedding); + readJetFinderOption("candPDG", config.candPDG); + readJetFinderOption("useV0SignalFlags", config.useV0SignalFlags); + } + config.eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(config.eventSelections); + + // Sanity checks on the jet finder settings needed by this analysis + int jetAlgorithm = -1; + readJetFinderOption("jetAlgorithm", jetAlgorithm); + if (jetAlgorithm != static_cast(fastjet::antikt_algorithm)) { + LOG(fatal) << taskName << " must use the anti-kT algorithm (jetAlgorithm = 2), found " << jetAlgorithm; + } + // jetRadius is read by hand: an array option is stored in the Variant as a pointer + size, so + // getTaskOptionValue with a std::vector reinterprets the pointer as a vector (std::bad_alloc) + std::vector jetRadii; + for (const auto& device : initContext.services().get().devices) { + if (device.name != taskName) { + continue; + } + for (const auto& option : device.options) { + if (option.name == "jetRadius") { + const double* radii = option.defaultValue.get(); + jetRadii.assign(radii, radii + option.defaultValue.size()); + } + } + } + if (jetRadii.empty()) { + LOG(fatal) << "Option 'jetRadius' not found in task '" << taskName << "': the V0 jet finder must run in the same workflow"; + } + if (std::none_of(jetRadii.begin(), jetRadii.end(), [&](double r) { return std::round(r * 100.0) == std::round(rJet * 100.0f); })) { + LOG(fatal) << "R = " << rJet.value << " is not among the jetRadius values of " << taskName; + } + bool saveJetsWithCandidatesOnly = true; + readJetFinderOption("saveJetsWithCandidatesOnly", saveJetsWithCandidatesOnly); + if (saveJetsWithCandidatesOnly) { + LOG(fatal) << taskName << " must run with saveJetsWithCandidatesOnly = false, otherwise jets without V0s are not stored and the jet and UE yields are biased"; + } + int ghostRepeat = 0; + readJetFinderOption("ghostRepeat", ghostRepeat); + if (ghostRepeat == 0) { + LOG(fatal) << taskName << " must run with ghostRepeat > 0, the jet area is needed for the background subtraction"; + } + } + + void init(InitContext& initContext) + { + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(evSel)); + + if (doprocessData && doprocessMCReco) { + LOG(fatal) << "processData and processMCReco cannot be enabled at the same time"; + } + if (doprocessData) { + readJetFinderConfig(initContext, jetFinderTaskName.value, jetFinderConfigDet, false); + } + if (doprocessData && (doprocessMCResponse || doprocessMCResponseGen)) { + LOG(fatal) << "processData and processMCResponse(Gen) cannot be enabled at the same time"; + } + if (doprocessMCReco || doprocessMCResponse || doprocessMCResponseGen) { + readJetFinderConfig(initContext, jetFinderMCDTaskName.value, jetFinderConfigDet, false); + } + if (doprocessMCGen || doprocessMCResponse || doprocessMCResponseGen) { + readJetFinderConfig(initContext, jetFinderMCPTaskName.value, jetFinderConfigGen, true); + // The particle-level V0 table must contain all the decay channels, so that all the generated V0s are clustered by + // the particle-level jet finder and the generated V0 constituents of the jets include the branching ratio + bool v0ChargedDecaysOnly = true; + if (!o2::common::core::getTaskOptionValue(initContext, derivedDataProducerTaskName.value, "v0ChargedDecaysOnly", v0ChargedDecaysOnly, false)) { + LOG(fatal) << "Option 'v0ChargedDecaysOnly' not found in task '" << derivedDataProducerTaskName.value << "': the PWGJE derived data producer must run in the same workflow"; + } + if (v0ChargedDecaysOnly) { + LOG(fatal) << derivedDataProducerTaskName.value << " must run with v0ChargedDecaysOnly = false, otherwise the V0s decaying to neutral particles are not clustered in the particle-level jets"; + } + } + trackSelection = jetderiveddatautilities::initialiseTrackSelection(jetFinderConfigDet.trackSelections); + candIndex = (jetFinderConfigDet.candPDG == static_cast(PDG_t::kLambda0)) ? 1 : 0; // same convention as the V0 jet finder + + const AxisSpec multAxis{multBinning, "FT0M percentile"}; + const AxisSpec ptAxis{100, 0.0, 10.0, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec invMassK0sAxis{200, 0.44, 0.56, "m_{#pi#pi} (GeV/#it{c}^{2})"}; + const AxisSpec invMassLambdaAxis{200, 1.09, 1.14, "m_{p#pi} (GeV/#it{c}^{2})"}; + const AxisSpec rhoAxis{200, 0., 20., "#rho (GeV/#it{c})"}; + + if (doprocessData) { + // Normalisation and jet QA + registryData.add("number_of_events_vsmultiplicity", "number of events in data vs multiplicity", HistType::kTH1D, {multAxis}); + registryData.add("number_of_jets_vsmultiplicity", "number of jets in data vs multiplicity", HistType::kTH1D, {multAxis}); + registryData.add("rhoPerpCone", "Perpendicular cone #rho", HistType::kTH2F, {multAxis, rhoAxis}); + registryData.add("jetPtRawVsSub", "Jets; #it{p}_{T}^{raw} (GeV/#it{c}); #it{p}_{T}^{sub} (GeV/#it{c})", HistType::kTH2F, {{200, 0., 200.}, {220, -20., 200.}}); + registryData.add("jetPtEtaPhi", "Jets; #it{p}_{T}^{sub} (GeV/#it{c}); #eta; #phi", HistType::kTH3D, {{200, 0., 200.}, {20, -1.f, 1.f}, {18 * 8, 0.f, o2::constants::math::TwoPI}}); + + // V0s in jets and in the underlying event + registryData.add("Lambda_in_jet", "Lambda_in_jet", HistType::kTH3F, {multAxis, ptAxis, invMassLambdaAxis}); + registryData.add("AntiLambda_in_jet", "AntiLambda_in_jet", HistType::kTH3F, {multAxis, ptAxis, invMassLambdaAxis}); + registryData.add("Lambda_in_ue", "Lambda_in_ue", HistType::kTH3F, {multAxis, ptAxis, invMassLambdaAxis}); + registryData.add("AntiLambda_in_ue", "AntiLambda_in_ue", HistType::kTH3F, {multAxis, ptAxis, invMassLambdaAxis}); + registryData.add("K0s_in_jet", "K0s_in_jet", HistType::kTH3F, {multAxis, ptAxis, invMassK0sAxis}); + registryData.add("K0s_in_ue", "K0s_in_ue", HistType::kTH3F, {multAxis, ptAxis, invMassK0sAxis}); + } + + // V0 content of the selected jets (number of V0 constituents per jet vs background-subtracted jet pT) + const AxisSpec jetPtAxis{200, 0., 200., "#it{p}_{T,jet}^{sub} (GeV/#it{c})"}; + const AxisSpec nV0Axis{30, -0.5, 29.5, "number of V0s per jet"}; + if (doprocessData || doprocessMCReco) { + auto& registry = doprocessData ? registryData : registryMC; + registry.add("jetV0s/nV0Constituents", "V0 constituents (all V0s clustered by the jet finder)", HistType::kTH2F, {jetPtAxis, nV0Axis}); + registry.add("jetV0s/nK0sSelected", "V0 constituents passing the K^{0}_{S} selection", HistType::kTH2F, {jetPtAxis, nV0Axis}); + registry.add("jetV0s/nLambdaSelected", "V0 constituents passing the #Lambda selection", HistType::kTH2F, {jetPtAxis, nV0Axis}); + registry.add("jetV0s/nAntiLambdaSelected", "V0 constituents passing the #bar{#Lambda} selection", HistType::kTH2F, {jetPtAxis, nV0Axis}); + } + if (doprocessMCReco) { + const AxisSpec originAxis{kNV0Origins, -0.5, static_cast(kNV0Origins) - 0.5, "MC origin"}; + const AxisSpec hypothesisAxis{3, -0.5, 2.5, "selection hypothesis"}; + const AxisSpec fractionAxis{100, 0., 1., "#Sigma#it{p}_{T}^{fake V0} / #it{p}_{T,jet}^{raw}"}; + registryMC.add("jetV0s/nRealV0Constituents", "real V0 constituents (K^{0}_{S}, #Lambda, #bar{#Lambda})", HistType::kTH2F, {jetPtAxis, nV0Axis}); + registryMC.add("jetV0s/nFakeV0Constituents", "fake V0 constituents", HistType::kTH2F, {jetPtAxis, nV0Axis}); + registryMC.add("jetV0s/nTrueK0sSelected", "true K^{0}_{S} constituents passing the K^{0}_{S} selection", HistType::kTH2F, {jetPtAxis, nV0Axis}); + registryMC.add("jetV0s/nTrueLambdaSelected", "true #Lambda constituents passing the #Lambda selection", HistType::kTH2F, {jetPtAxis, nV0Axis}); + registryMC.add("jetV0s/nTrueAntiLambdaSelected", "true #bar{#Lambda} constituents passing the #bar{#Lambda} selection", HistType::kTH2F, {jetPtAxis, nV0Axis}); + registryMC.add("jetV0s/fakeV0PtFraction", "fraction of the jet pT carried by fake V0 constituents", HistType::kTH2F, {jetPtAxis, fractionAxis}); + registryMC.add("jetV0s/fakeSelectedV0PtFraction", "fraction of the jet pT carried by fake V0 constituents passing at least one selection", HistType::kTH2F, {jetPtAxis, fractionAxis}); + // Mean fraction vs jet pT, filled with the exact value (not limited by the binning of the fraction axis) + registryMC.add("jetV0s/fakeV0PtFractionProfile", "mean fraction of the jet pT carried by fake V0 constituents;#it{p}_{T,jet}^{sub} (GeV/#it{c});#LT #Sigma#it{p}_{T}^{fake V0} / #it{p}_{T,jet}^{raw} #GT", HistType::kTProfile, {jetPtAxis}); + registryMC.add("jetV0s/fakeSelectedV0PtFractionProfile", "mean fraction of the jet pT carried by fake V0 constituents passing at least one selection;#it{p}_{T,jet}^{sub} (GeV/#it{c});#LT #Sigma#it{p}_{T}^{fake V0} / #it{p}_{T,jet}^{raw} #GT", HistType::kTProfile, {jetPtAxis}); + registryMC.add("jetV0s/V0ConstituentOrigin", "MC origin of the V0 constituents", HistType::kTH3F, {jetPtAxis, ptAxis, originAxis}); + registryMC.add("jetV0s/SelectedV0ConstituentOrigin", "MC origin of the V0 constituents passing the selection", HistType::kTH3F, {hypothesisAxis, ptAxis, originAxis}); + auto hOrigin = registryMC.get(HIST("jetV0s/V0ConstituentOrigin")); + auto hSelectedOrigin = registryMC.get(HIST("jetV0s/SelectedV0ConstituentOrigin")); + hSelectedOrigin->GetXaxis()->SetBinLabel(1, "K^{0}_{S}"); + hSelectedOrigin->GetXaxis()->SetBinLabel(2, "#Lambda"); + hSelectedOrigin->GetXaxis()->SetBinLabel(3, "#bar{#Lambda}"); + for (int i = 0; i < kNV0Origins; i++) { + hOrigin->GetZaxis()->SetBinLabel(i + 1, std::string(OriginLabels[i]).c_str()); + hSelectedOrigin->GetZaxis()->SetBinLabel(i + 1, std::string(OriginLabels[i]).c_str()); + } + + // Real V0 constituents by source (primary / secondary from Xi+-, Xi0, Omega+-, other) and fraction of the (raw) jet pT + // they carry: the secondary ones are the detector-level counterpart of the momentum of their mother + const AxisSpec sourceAxis{kNV0Sources, -0.5, static_cast(kNV0Sources) - 0.5, "V0 source"}; + const AxisSpec sourceFractionAxis{100, 0., 1., "#Sigma#it{p}_{T}^{V0} / #it{p}_{T,jet}^{raw}"}; + registryMC.add("jetV0s/V0ConstituentSource", "source of the real V0 constituents", HistType::kTH3F, {jetPtAxis, ptAxis, sourceAxis}); + registryMC.add("jetV0s/V0SourcePtFraction", "fraction of the jet pT carried by the real V0 constituents of each source", HistType::kTH3F, {jetPtAxis, sourceAxis, sourceFractionAxis}); + registryMC.add("jetV0s/V0SourcePtFractionProfile", "mean fraction of the jet pT carried by the real V0 constituents of each source;#it{p}_{T,jet}^{sub} (GeV/#it{c});V0 source;#LT #Sigma#it{p}_{T}^{V0} / #it{p}_{T,jet}^{raw} #GT", HistType::kTProfile2D, {jetPtAxis, sourceAxis}); + for (const auto& axis : {registryMC.get(HIST("jetV0s/V0ConstituentSource"))->GetZaxis(), registryMC.get(HIST("jetV0s/V0SourcePtFraction"))->GetYaxis(), + registryMC.get(HIST("jetV0s/V0SourcePtFractionProfile"))->GetYaxis()}) { + for (int i = 0; i < kNV0Sources; i++) { + axis->SetBinLabel(i + 1, std::string(SourceLabels[i]).c_str()); + } + } + } + + // Jet pT response (detector vs particle level, geometrically matched jets with R = rJet and |eta| <= maxJetEta), with the + // background subtracted at both levels as in the analysis. The residual is also studied vs the fraction of the + // particle-level jet pT carried by Xi+- and Omega+-, whose momentum is carried by secondary V0s at detector level + const AxisSpec jetPtSubDetAxis{220, -20., 200., "#it{p}_{T,jet}^{sub, det} (GeV/#it{c})"}; + const AxisSpec jetPtSubGenAxis{220, -20., 200., "#it{p}_{T,jet}^{sub, gen} (GeV/#it{c})"}; + const AxisSpec residualAxis{200, -2., 2., "(#it{p}_{T,jet}^{det} - #it{p}_{T,jet}^{gen}) / #it{p}_{T,jet}^{gen}"}; + if (doprocessMCResponse) { + registryMC.add("jetResponse/ptSubDetVsGen", "jet pT response, background subtracted", HistType::kTH2F, {jetPtSubDetAxis, jetPtSubGenAxis}); + registryMC.add("jetResponse/ptRawDetVsGen", "jet pT response, no background subtraction;#it{p}_{T,jet}^{raw, det} (GeV/#it{c});#it{p}_{T,jet}^{raw, gen} (GeV/#it{c})", HistType::kTH2F, {{200, 0., 200.}, {200, 0., 200.}}); + registryMC.add("jetResponse/residualSub", "jet pT residual, background subtracted", HistType::kTH2F, {jetPtSubGenAxis, residualAxis}); + registryMC.add("jetResponse/residualRaw", "jet pT residual, no background subtraction;#it{p}_{T,jet}^{raw, gen} (GeV/#it{c})", HistType::kTH2F, {{200, 0., 200.}, residualAxis}); + registryMC.add("jetResponse/residualRawVsMultiStrangeFraction", "jet pT residual (no background subtraction) vs fraction of the particle-level jet pT carried by #Xi^{#pm} and #Omega^{#pm};#it{p}_{T,jet}^{raw, gen} (GeV/#it{c});#Sigma#it{p}_{T}^{#Xi^{#pm}, #Omega^{#pm}} / #it{p}_{T,jet}^{raw, gen}", + HistType::kTH3F, {{20, 0., 200.}, {50, 0., 0.5}, residualAxis}); + registryMC.add("jetResponse/detJets", "detector-level jets in acceptance", HistType::kTH1F, {jetPtSubDetAxis}); + registryMC.add("jetResponse/detJetsMatched", "detector-level jets in acceptance matched to a particle-level jet in acceptance", HistType::kTH1F, {jetPtSubDetAxis}); + } + if (doprocessMCResponseGen) { + registryMC.add("jetResponse/genJets", "particle-level jets in acceptance", HistType::kTH1F, {jetPtSubGenAxis}); + registryMC.add("jetResponse/genJetsMatched", "particle-level jets in acceptance matched to a detector-level jet in acceptance", HistType::kTH1F, {jetPtSubGenAxis}); + } + + // MC histograms for efficiency and corrections. The multiplicity is the generated FT0M percentile of the MC collision + if (doprocessMCReco) { + registryMC.add("number_of_events_vsmultiplicity_rec", "number of reconstructed events vs multiplicity", HistType::kTH1D, {multAxis}); + registryMC.add("number_of_events_wjet_vsmultiplicity_rec", "number of reconstructed events with at least one selected jet vs multiplicity", HistType::kTH1D, {multAxis}); + registryMC.add("number_of_jets_vsmultiplicity_rec", "number of reconstructed jets vs multiplicity", HistType::kTH1D, {multAxis}); + registryMC.add("rhoPerpCone_rec", "Perpendicular cone #rho, detector level", HistType::kTH2F, {multAxis, rhoAxis}); + } + if (doprocessMCGen) { + registryMC.add("number_of_events_vsmultiplicity_gen", "number of generated events vs multiplicity", HistType::kTH1D, {multAxis}); + registryMC.add("number_of_jets_vsmultiplicity_gen", "number of generated jets vs multiplicity", HistType::kTH1D, {multAxis}); + registryMC.add("rhoPerpCone_gen", "Perpendicular cone #rho, particle level", HistType::kTH2F, {multAxis, rhoAxis}); + } + for (int iHist = 0; iHist < kNMCHistograms; iHist++) { + const bool isGenLevelHistogram = (iHist == kGenJet || iHist == kGenUE || iHist == kGenFullEvent); + if ((isGenLevelHistogram && !doprocessMCGen) || (!isGenLevelHistogram && !doprocessMCReco)) { + continue; + } + for (int iSpecies = kK0s; iSpecies <= kAntiLambda; iSpecies++) { + const std::string name = std::string(QAFolders[iSpecies]) + std::string(MCHistSuffixes[iHist]); + registryMC.add(name.c_str(), name.c_str(), HistType::kTH2F, {multAxis, ptAxis}); + } + } + + // V0 QA + if (fillV0QAHistograms && (doprocessData || doprocessMCReco)) { + const std::array flagLabels = {"all", "K0S", "#Lambda", "#bar{#Lambda}", "rejected"}; + const int nFlagBins = static_cast(flagLabels.size()); + registryQC.add("QA/V0SignalFlags", "v0-selector flags of the stored V0s", HistType::kTH1D, {{nFlagBins, -0.5, nFlagBins - 0.5}}); + auto hFlags = registryQC.get(HIST("QA/V0SignalFlags")); + for (int i = 0; i < nFlagBins; i++) { + hFlags->GetXaxis()->SetBinLabel(i + 1, flagLabels[i].c_str()); + } + + registryQC.add("QA/V0CutFlow", "V0 cut flow", HistType::kTH2D, {{3, -0.5, 2.5}, {kNSelectionSteps, -0.5, static_cast(kNSelectionSteps) - 0.5}}); + auto hCutFlow = registryQC.get(HIST("QA/V0CutFlow")); + hCutFlow->GetXaxis()->SetBinLabel(1, "K^{0}_{S}"); + hCutFlow->GetXaxis()->SetBinLabel(2, "#Lambda"); + hCutFlow->GetXaxis()->SetBinLabel(3, "#bar{#Lambda}"); + for (int step = 0; step < kNSelectionSteps; step++) { + hCutFlow->GetYaxis()->SetBinLabel(step + 1, std::string(StepLabels[step]).c_str()); + } + + const AxisSpec radiusAxis{240, 0., 60., "V0 radius (cm)"}; + const AxisSpec dcaToPVAxis{200, 0., 1., "|DCA| to PV (cm)"}; + const AxisSpec cosPAAxis{250, 0.95, 1., "cos PA"}; + const AxisSpec dcaDauAxis{200, 0., 2., "DCA V0 daughters (cm)"}; + const AxisSpec etaAxis{100, -1., 1., "#eta_{daughter}"}; + const AxisSpec alphaAxis{200, -1., 1., "#alpha^{Arm}"}; + const AxisSpec qtAxis{150, 0., 0.3, "#it{q}_{T}^{Arm} (GeV/#it{c})"}; + const AxisSpec deltaMassAxis{200, 0., 0.1, "|m_{competing} - m_{PDG}| (GeV/#it{c}^{2})"}; + for (const auto& folder : QAFolders) { + const std::string dir = "QA/" + std::string(folder) + "/"; + registryQC.add((dir + "V0Radius").c_str(), "V0 radius", HistType::kTH2F, {ptAxis, radiusAxis}); + registryQC.add((dir + "DcaPosToPV").c_str(), "DCA positive daughter to PV", HistType::kTH2F, {ptAxis, dcaToPVAxis}); + registryQC.add((dir + "DcaNegToPV").c_str(), "DCA negative daughter to PV", HistType::kTH2F, {ptAxis, dcaToPVAxis}); + registryQC.add((dir + "V0CosPA").c_str(), "V0 cosine of pointing angle", HistType::kTH2F, {ptAxis, cosPAAxis}); + registryQC.add((dir + "DcaV0Daughters").c_str(), "DCA between V0 daughters", HistType::kTH2F, {ptAxis, dcaDauAxis}); + registryQC.add((dir + "PosDaughterEta").c_str(), "#eta of the positive daughter", HistType::kTH2F, {ptAxis, etaAxis}); + registryQC.add((dir + "NegDaughterEta").c_str(), "#eta of the negative daughter", HistType::kTH2F, {ptAxis, etaAxis}); + registryQC.add((dir + "Armenteros").c_str(), "Armenteros-Podolanski", HistType::kTH2F, {alphaAxis, qtAxis}); + } + registryQC.add("QA/K0s/Mass", "K^{0}_{S} invariant mass", HistType::kTH2F, {ptAxis, invMassK0sAxis}); + registryQC.add("QA/Lambda/Mass", "#Lambda invariant mass", HistType::kTH2F, {ptAxis, invMassLambdaAxis}); + registryQC.add("QA/AntiLambda/Mass", "#bar{#Lambda} invariant mass", HistType::kTH2F, {ptAxis, invMassLambdaAxis}); + registryQC.add("QA/K0s/CompetingMassDelta", "K^{0}_{S}: distance of m_{#Lambda} from the #Lambda mass", HistType::kTH2F, {ptAxis, deltaMassAxis}); + registryQC.add("QA/Lambda/CompetingMassDelta", "#Lambda: distance of m_{K^{0}_{S}} from the K^{0}_{S} mass", HistType::kTH2F, {ptAxis, deltaMassAxis}); + registryQC.add("QA/AntiLambda/CompetingMassDelta", "#bar{#Lambda}: distance of m_{K^{0}_{S}} from the K^{0}_{S} mass", HistType::kTH2F, {ptAxis, deltaMassAxis}); + } + } + + // --------------------------------------------------------------------------------------------------------------- + // Jet finder input, background and jet selection (common to data, MC detector level and MC particle level) + + // Event selection: the one of this task plus the one of the jet finder, so that events without jets are only + // the ones in which the jet finder did not find any jet + template + bool isSelectedCollision(T const& collision, JetFinderConfig const& config) + { + return jetderiveddatautilities::selectCollision(collision, eventSelectionBits) && + jetderiveddatautilities::selectCollision(collision, config.eventSelectionBits, config.skipMBGapEvents, config.applyRCTSelections); + } + + template + bool passedJetFinderKinematics(T const& particle, JetFinderConfig const& config) + { + if (particle.pt() < config.trackPtMin || particle.pt() >= config.trackPtMax || + particle.eta() < config.trackEtaMin || particle.eta() > config.trackEtaMax || + particle.phi() < config.trackPhiMin || particle.phi() > config.trackPhiMax) { + return false; + } + if (config.phiExclusionMin > PhiExclusionUnset && particle.phi() > config.phiExclusionMin && particle.phi() < config.phiExclusionMax) { + return false; + } + return true; + } + + // Track selection of the V0 jet finder (kinematic filter + track selection + removal of the V0 daughters) + template + bool isJetFinderTrack(T const& track, U const& v0s) + { + if (!passedJetFinderKinematics(track, jetFinderConfigDet)) { + return false; + } + if (!jetderiveddatautilities::selectTrack(track, trackSelection, jetFinderConfigDet.isEmbedding)) { + return false; + } + for (const auto& v0 : v0s) { + if (jetcandidateutilities::isDaughterTrack(track, v0)) { + return false; + } + } + return true; + } + + // Collect the indices of the V0 pseudojets (status candidate) in the jet finder input + std::unordered_set getJetFinderV0s() + { + std::unordered_set jetFinderV0s; + for (const auto& particle : inputParticles) { + if (particle.user_info().getStatus() == JetConstituentStatus::candidate) { + jetFinderV0s.insert(particle.user_info().getIndex()); + } + } + return jetFinderV0s; + } + + // Rebuild the detector-level jet finder input (V0s + tracks without V0 daughters), with the same selections used in the clustering. + // Returns the V0s accepted by the jet finder: only these can be jet constituents, so only these are tested for the UE + template + std::unordered_set buildDetectorLevelInput(TV0s const& v0s, TTracks const& tracks) + { + inputParticles.clear(); + jetfindingutilities::analyseV0s(inputParticles, v0s, jetFinderConfigDet.candPtMin, jetFinderConfigDet.candPtMax, jetFinderConfigDet.candYMin, jetFinderConfigDet.candYMax, candIndex, jetFinderConfigDet.useV0SignalFlags); + const auto jetFinderV0s = getJetFinderV0s(); + for (const auto& track : tracks) { + if (isJetFinderTrack(track, v0s)) { + fastjetutilities::fillTracks(track, inputParticles, track.globalIndex()); + } + } + return jetFinderV0s; + } + + // Rebuild the particle-level jet finder input (V0s + charged particles without V0 daughters), with the same selections used in the clustering. + // The secondary V0s (e.g. from Xi/Omega decays) are not clustered (analyseV0s keeps only the physical primary MC V0s): their + // momentum is already carried by the charged primary mother + template + std::unordered_set buildParticleLevelInput(TV0s const& v0s, TParticles const& particles) + { + inputParticles.clear(); + jetfindingutilities::analyseV0s(inputParticles, v0s, jetFinderConfigGen.candPtMin, jetFinderConfigGen.candPtMax, jetFinderConfigGen.candYMin, jetFinderConfigGen.candYMax, 0, false); + const auto jetFinderV0s = getJetFinderV0s(); + std::vector chargedParticles; + jetfindingutilities::analyseParticles(chargedParticles, jetFinderConfigGen.particleSelections, static_cast(JetType::charged), particles, pdgDatabase, &v0s); + for (const auto& particle : chargedParticles) { + if (passedJetFinderKinematics(particle, jetFinderConfigGen)) { + inputParticles.push_back(particle); + } + } + return jetFinderV0s; + } + + // Background: rho from the two cones perpendicular to the leading jet (PWGJE jetutilities::estimateRhoPerpCone), + // computed on the jet finder input. Returns a negative value if there are no jets with R = rJet + template + float estimateRho(TJets const& jets) + { + float leadingJetPt = -1.; + float leadingJetEta = 0.; + float leadingJetPhi = 0.; + for (const auto& jet : jets) { + if (jet.r() == std::round(rJet * 100.0f) && jet.pt() > leadingJetPt) { + leadingJetPt = jet.pt(); + leadingJetEta = jet.eta(); + leadingJetPhi = jet.phi(); + } + } + if (leadingJetPt < 0.) { + return -1.; + } + const auto [rho, rhoM] = jetutilities::estimateRhoPerpCone(inputParticles, fastjet::PtYPhiM(leadingJetPt, leadingJetEta, leadingJetPhi), rJet); + return rho; + } + + // Jet pT corrected for the background as pT,sub = pT,raw - rho * A + template + float subtractedPt(TJet const& jet, float rho) + { + return jet.pt() - rho * jet.area(); + } + + template + bool isJetAboveThreshold(TJet const& jet, float rho) + { + return jet.r() == std::round(rJet * 100.0f) && subtractedPt(jet, rho) >= minJetPt; + } + + template + bool isSelectedJet(TJet const& jet, float rho) + { + return isJetAboveThreshold(jet, rho) && std::abs(jet.eta()) <= maxJetEta; + } + + // Jet acceptance of the jet pT response (no pT threshold) + template + bool isInJetAcceptance(TJet const& jet) + { + return jet.r() == std::round(rJet * 100.0f) && std::abs(jet.eta()) <= maxJetEta; + } + + // Jets above the pT threshold after subtraction: their V0 constituents and their axes are used to exclude from the UE + // of all the other jets the V0s inside another jet (independently of the jet eta cut, to also catch jets partially + // overlapping with the UE cones) + template + void collectJetsAboveThreshold(TJets const& jets, float rho, std::unordered_set& v0sInJets, std::vector>& jetAxes) + { + for (const auto& jet : jets) { + if (!isJetAboveThreshold(jet, rho)) { + continue; + } + jetAxes.push_back({jet.eta(), jet.phi()}); + for (const auto& v0Id : jet.candidatesIds()) { + v0sInJets.insert(v0Id); + } + } + } + + // UE region: two cones of radius R centred on the axes perpendicular to the jet in azimuth (same eta, phi_jet +- pi/2) + template + bool isInUE(float eta, float phi, TJet const& jet) + { + return jetutilities::deltaR(eta, phi, jet.eta(), jet.phi() + o2::constants::math::PIHalf) < rJet || + jetutilities::deltaR(eta, phi, jet.eta(), jet.phi() - o2::constants::math::PIHalf) < rJet; + } + + bool isInAnyJetCone(float eta, float phi, std::vector> const& jetAxes) + { + return std::any_of(jetAxes.begin(), jetAxes.end(), [&](const auto& axis) { return jetutilities::deltaR(eta, phi, axis[0], axis[1]) < rJet; }); + } + + // --------------------------------------------------------------------------------------------------------------- + // V0 selection + + // Distance of the competing mass hypothesis from the PDG mass, used in the competing V0 mass rejection + template + float competingMassDelta(T const& v0, V0Species species) + { + if (species == kK0s) { + return std::min(std::abs(v0.mLambda() - o2::constants::physics::MassLambda), std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda)); + } + return std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short); + } + + // V0 selection for a given species hypothesis, applied step by step. Returns the last step passed + // (kStepCompetingMass = all the selections passed). It is used both for the selection and for the cut flow QA + template + int lastPassedSelectionStep(T const& v0, V0Species species, bool isJetFinderV0) + { + if (!isJetFinderV0) { + return kStepAll; + } + if (v0.isRejectedCandidate()) { + return kStepJetFinderAcceptance; + } + if (v0Cuts.requireV0SignalFlags) { + const bool hasFlag = (species == kK0s) ? v0.isK0SCandidate() : (species == kLambda) ? v0.isLambdaCandidate() + : v0.isAntiLambdaCandidate(); + if (!hasFlag) { + return kStepNotRejected; + } + } + if (v0.v0radius() < v0Cuts.minimumV0Radius || v0.v0radius() > v0Cuts.maximumV0Radius) { + return kStepSignalFlag; + } + if (std::abs(v0.dcapostopv()) < v0Cuts.dcaDaughterToPVmin || std::abs(v0.dcanegtopv()) < v0Cuts.dcaDaughterToPVmin) { + return kStepRadius; + } + if (v0.v0cosPA() < v0Cuts.v0cospaMin) { + return kStepDcaDauToPV; + } + if (std::abs(v0.dcaV0daughters()) > v0Cuts.dcaV0DaughtersMax) { + return kStepCosPA; + } + if (std::abs(v0.positiveeta()) > v0Cuts.daughterEtaMax || std::abs(v0.negativeeta()) > v0Cuts.daughterEtaMax) { + return kStepDcaV0Dau; + } + if (v0Cuts.doCompetingMassRejection && competingMassDelta(v0, species) < ((species == kK0s) ? v0Cuts.lambdaRejWindow : v0Cuts.k0sRejWindow)) { + return kStepDauEta; + } + return kStepCompetingMass; + } + + // Full selection of a V0 (accepted by the jet finder) for a given species hypothesis + template + bool passedV0Selection(T const& v0, V0Species species) + { + return lastPassedSelectionStep(v0, species, true) == kStepCompetingMass; + } + + // --------------------------------------------------------------------------------------------------------------- + // V0 QA + + // QA of the topological variables, for all the V0s (folder "AllV0s") or for the selected V0s of one species + template + void fillV0QAVariables(T const& v0) + { + constexpr auto Folder = HIST("QA/") + HIST(QAFolders[iFolder]); + registryQC.fill(Folder + HIST("/V0Radius"), v0.pt(), v0.v0radius()); + registryQC.fill(Folder + HIST("/DcaPosToPV"), v0.pt(), std::abs(v0.dcapostopv())); + registryQC.fill(Folder + HIST("/DcaNegToPV"), v0.pt(), std::abs(v0.dcanegtopv())); + registryQC.fill(Folder + HIST("/V0CosPA"), v0.pt(), v0.v0cosPA()); + registryQC.fill(Folder + HIST("/DcaV0Daughters"), v0.pt(), std::abs(v0.dcaV0daughters())); + registryQC.fill(Folder + HIST("/PosDaughterEta"), v0.pt(), v0.positiveeta()); + registryQC.fill(Folder + HIST("/NegDaughterEta"), v0.pt(), v0.negativeeta()); + registryQC.fill(Folder + HIST("/Armenteros"), v0.alpha(), v0.qtarm()); + if constexpr (iFolder == kQAK0s) { + registryQC.fill(Folder + HIST("/Mass"), v0.pt(), v0.mK0Short()); + registryQC.fill(Folder + HIST("/CompetingMassDelta"), v0.pt(), competingMassDelta(v0, kK0s)); + } else if constexpr (iFolder == kQALambda) { + registryQC.fill(Folder + HIST("/Mass"), v0.pt(), v0.mLambda()); + registryQC.fill(Folder + HIST("/CompetingMassDelta"), v0.pt(), competingMassDelta(v0, kLambda)); + } else if constexpr (iFolder == kQAAntiLambda) { + registryQC.fill(Folder + HIST("/Mass"), v0.pt(), v0.mAntiLambda()); + registryQC.fill(Folder + HIST("/CompetingMassDelta"), v0.pt(), competingMassDelta(v0, kAntiLambda)); + } + } + + // Inclusive V0 QA (once per V0 and per event, independently of the jets): v0-selector flags, topological variables + // before the selections, cut flow and topological variables after the selections for each species + template + void fillV0QA(T const& v0, bool isJetFinderV0) + { + registryQC.fill(HIST("QA/V0SignalFlags"), 0); + if (v0.isK0SCandidate()) { + registryQC.fill(HIST("QA/V0SignalFlags"), 1); + } + if (v0.isLambdaCandidate()) { + registryQC.fill(HIST("QA/V0SignalFlags"), 2); + } + if (v0.isAntiLambdaCandidate()) { + registryQC.fill(HIST("QA/V0SignalFlags"), 3); + } + if (v0.isRejectedCandidate()) { + registryQC.fill(HIST("QA/V0SignalFlags"), 4); + } + fillV0QAVariables(v0); + + for (const auto& species : {kK0s, kLambda, kAntiLambda}) { + const int lastStep = lastPassedSelectionStep(v0, species, isJetFinderV0); + for (int step = kStepAll; step <= lastStep; step++) { + registryQC.fill(HIST("QA/V0CutFlow"), species, step); + } + if (lastStep != kStepCompetingMass) { + continue; + } + if (species == kK0s) { + fillV0QAVariables(v0); + } else if (species == kLambda) { + fillV0QAVariables(v0); + } else { + fillV0QAVariables(v0); + } + } + } + + // --------------------------------------------------------------------------------------------------------------- + // Data + + // Fill the V0 histograms for one region (in-jet or UE). The same selection is applied in the two regions. + // A V0 can satisfy more than one species hypothesis + template + void fillV0(T const& v0, float multiplicity) + { + if (passedV0Selection(v0, kK0s)) { + if constexpr (isInJet) { + registryData.fill(HIST("K0s_in_jet"), multiplicity, v0.pt(), v0.mK0Short()); + } else { + registryData.fill(HIST("K0s_in_ue"), multiplicity, v0.pt(), v0.mK0Short()); + } + } + if (passedV0Selection(v0, kLambda)) { + if constexpr (isInJet) { + registryData.fill(HIST("Lambda_in_jet"), multiplicity, v0.pt(), v0.mLambda()); + } else { + registryData.fill(HIST("Lambda_in_ue"), multiplicity, v0.pt(), v0.mLambda()); + } + } + if (passedV0Selection(v0, kAntiLambda)) { + if constexpr (isInJet) { + registryData.fill(HIST("AntiLambda_in_jet"), multiplicity, v0.pt(), v0.mAntiLambda()); + } else { + registryData.fill(HIST("AntiLambda_in_ue"), multiplicity, v0.pt(), v0.mAntiLambda()); + } + } + } + + void processData(soa::Filtered::iterator const& jcoll, V0ChargedJetsWithConstituents const& v0jets, aod::CandidatesV0Data const& v0s, aod::JetTracks const& tracks) + { + if (!isSelectedCollision(jcoll, jetFinderConfigDet)) { + return; + } + const float multiplicity = jcoll.centFT0M(); + registryData.fill(HIST("number_of_events_vsmultiplicity"), multiplicity); + + const auto jetFinderV0s = buildDetectorLevelInput(v0s, tracks); + + // Inclusive V0 QA, filled once per V0 for all the selected events (also without jets) + if (fillV0QAHistograms) { + for (const auto& v0 : v0s) { + fillV0QA(v0, jetFinderV0s.contains(v0.globalIndex())); + } + } + + const float rho = estimateRho(v0jets); + if (rho < 0.) { + return; + } + registryData.fill(HIST("rhoPerpCone"), multiplicity, rho); + + std::unordered_set v0sInJets; + std::vector> jetAxes; + collectJetsAboveThreshold(v0jets, rho, v0sInJets, jetAxes); + + for (const auto& jet : v0jets) { + if (jet.r() == std::round(rJet * 100.0f)) { + registryData.fill(HIST("jetPtRawVsSub"), jet.pt(), subtractedPt(jet, rho)); + } + if (!isSelectedJet(jet, rho)) { + continue; + } + registryData.fill(HIST("number_of_jets_vsmultiplicity"), multiplicity); + registryData.fill(HIST("jetPtEtaPhi"), subtractedPt(jet, rho), jet.eta(), jet.phi()); + fillJetV0Content(jet, subtractedPt(jet, rho)); + + // V0-jet matching, in-jet region: a V0 is "in jet" if it is a constituent of the jet. + // The V0 jet finder clusters each V0 as a single pseudojet and removes its daughters from the track list, + // so the V0 is assigned to at most one jet and its daughters are never counted as separate jet constituents + for (const auto& v0 : jet.candidates_as()) { + fillV0(v0, multiplicity); + } + + // V0-jet matching, UE region: V0s in the perpendicular cones. The V0s are those accepted by the jet finder + // (same selection as the in-jet V0s), excluding the V0s that are constituents of another jet above threshold + for (const auto& v0 : v0s) { + if (!jetFinderV0s.contains(v0.globalIndex()) || v0sInJets.contains(v0.globalIndex())) { + continue; + } + if (isInUE(v0.eta(), v0.phi(), jet)) { + fillV0(v0, multiplicity); + } + } + } + } + PROCESS_SWITCH(LfJetSpectra, processData, "V0s in jets and in the UE in data", true); + + // --------------------------------------------------------------------------------------------------------------- + // MC + + template + void fillMCHistogram(float multiplicity, float pt, float weight) + { + registryMC.fill(HIST(QAFolders[iSpecies]) + HIST(MCHistSuffixes[iHist]), multiplicity, pt, weight); + } + + template + void fillMCHistogram(V0Species species, float multiplicity, float pt, float weight) + { + if (species == kK0s) { + fillMCHistogram(multiplicity, pt, weight); + } else if (species == kLambda) { + fillMCHistogram(multiplicity, pt, weight); + } else { + fillMCHistogram(multiplicity, pt, weight); + } + } + + // Reconstructed V0 matched to a true V0 of the given species: PDG code of the V0 and of the two daughters + template + bool isTrueV0(T const& v0, V0Species species) + { + switch (species) { + case kK0s: + return v0.pdgCode() == PDG_t::kK0Short && v0.pdgCodePositive() == PDG_t::kPiPlus && v0.pdgCodeNegative() == PDG_t::kPiMinus; + case kLambda: + return v0.pdgCode() == PDG_t::kLambda0 && v0.pdgCodePositive() == PDG_t::kProton && v0.pdgCodeNegative() == PDG_t::kPiMinus; + case kAntiLambda: + return v0.pdgCode() == PDG_t::kLambda0Bar && v0.pdgCodePositive() == PDG_t::kPiPlus && v0.pdgCodeNegative() == PDG_t::kProtonBar; + } + return false; + } + + // Species of a generated V0 (-1 if not a K0s, Lambda or AntiLambda) + template + int genV0Species(T const& v0) + { + switch (v0.pdgCode()) { + case PDG_t::kK0Short: + return kK0s; + case PDG_t::kLambda0: + return kLambda; + case PDG_t::kLambda0Bar: + return kAntiLambda; + } + return -1; + } + + // MC origin of a reconstructed V0, from the PDG code of the common mother of the two daughters (0 if there is none) + template + int v0Origin(T const& v0) + { + switch (v0.pdgCode()) { + case PDG_t::kK0Short: + return kOriginK0s; + case PDG_t::kLambda0: + return kOriginLambda; + case PDG_t::kLambda0Bar: + return kOriginAntiLambda; + case PDG_t::kGamma: + return kOriginGammaConversion; + case 0: + return kOriginNoCommonMother; // the two daughters have no common mother, e.g. one of them is a primary particle and the other is a secondary from a weak decay + } + return kOriginOtherMother; // the two daughters have a common mother, but it is not a K0s, Lambda, AntiLambda or gamma, e.g. material or multi-particle decay or + } + + // Source of a real V0 (see V0Source), from the PDG code of the mother of the V0 + template + int v0Source(T const& v0) + { + if (v0.isPhysicalPrimary()) { + return kSourcePrimary; + } + switch (std::abs(v0.pdgCodeMother())) { + case PDG_t::kXiMinus: + return kSourceXiCharged; + case o2::constants::physics::Pdg::kXi0: + return kSourceXi0; + case PDG_t::kOmegaMinus: + return kSourceOmega; + } + return kSourceOtherSecondary; + } + + // V0 content of a selected jet: number of V0 constituents (all, and passing the selection of each species). In MC also + // the number of real / fake constituents, the true selected constituents, the MC origin of the constituents and the + // fraction of the (raw) jet pT carried by fake V0s, which are clustered by the jet finder as the real ones + template + void fillJetV0Content(TJet const& jet, float ptSub, float weight = 1.) + { + int nV0s = 0; + int nReal = 0; + int nFake = 0; + std::array nSelected{0, 0, 0}; + std::array nTrueSelected{0, 0, 0}; + float fakePt = 0.; + float fakeSelectedPt = 0.; + std::array sourcePt{}; + for (const auto& v0 : jet.template candidates_as()) { + nV0s++; + bool passedAnySelection = false; + for (const auto& species : {kK0s, kLambda, kAntiLambda}) { + if (!passedV0Selection(v0, species)) { + continue; + } + passedAnySelection = true; + nSelected[species]++; + if constexpr (isMC) { + if (isTrueV0(v0, species)) { + nTrueSelected[species]++; + } + registryMC.fill(HIST("jetV0s/SelectedV0ConstituentOrigin"), species, v0.pt(), v0Origin(v0), weight); + } + } + if constexpr (isMC) { + const int origin = v0Origin(v0); + registryMC.fill(HIST("jetV0s/V0ConstituentOrigin"), ptSub, v0.pt(), origin, weight); + if (origin <= kOriginAntiLambda) { + nReal++; + const int source = v0Source(v0); + sourcePt[source] += v0.pt(); + registryMC.fill(HIST("jetV0s/V0ConstituentSource"), ptSub, v0.pt(), source, weight); + } else { + nFake++; + fakePt += v0.pt(); + if (passedAnySelection) { + fakeSelectedPt += v0.pt(); + } + } + } + } + + if constexpr (isMC) { + registryMC.fill(HIST("jetV0s/nV0Constituents"), ptSub, nV0s, weight); + registryMC.fill(HIST("jetV0s/nK0sSelected"), ptSub, nSelected[kK0s], weight); + registryMC.fill(HIST("jetV0s/nLambdaSelected"), ptSub, nSelected[kLambda], weight); + registryMC.fill(HIST("jetV0s/nAntiLambdaSelected"), ptSub, nSelected[kAntiLambda], weight); + registryMC.fill(HIST("jetV0s/nRealV0Constituents"), ptSub, nReal, weight); + registryMC.fill(HIST("jetV0s/nFakeV0Constituents"), ptSub, nFake, weight); + registryMC.fill(HIST("jetV0s/nTrueK0sSelected"), ptSub, nTrueSelected[kK0s], weight); + registryMC.fill(HIST("jetV0s/nTrueLambdaSelected"), ptSub, nTrueSelected[kLambda], weight); + registryMC.fill(HIST("jetV0s/nTrueAntiLambdaSelected"), ptSub, nTrueSelected[kAntiLambda], weight); + registryMC.fill(HIST("jetV0s/fakeV0PtFraction"), ptSub, fakePt / jet.pt(), weight); + registryMC.fill(HIST("jetV0s/fakeSelectedV0PtFraction"), ptSub, fakeSelectedPt / jet.pt(), weight); + registryMC.fill(HIST("jetV0s/fakeV0PtFractionProfile"), ptSub, fakePt / jet.pt(), weight); + registryMC.fill(HIST("jetV0s/fakeSelectedV0PtFractionProfile"), ptSub, fakeSelectedPt / jet.pt(), weight); + for (int source = 0; source < kNV0Sources; source++) { + registryMC.fill(HIST("jetV0s/V0SourcePtFraction"), ptSub, source, sourcePt[source] / jet.pt(), weight); + registryMC.fill(HIST("jetV0s/V0SourcePtFractionProfile"), ptSub, source, sourcePt[source] / jet.pt(), weight); + } + } else { + registryData.fill(HIST("jetV0s/nV0Constituents"), ptSub, nV0s); + registryData.fill(HIST("jetV0s/nK0sSelected"), ptSub, nSelected[kK0s]); + registryData.fill(HIST("jetV0s/nLambdaSelected"), ptSub, nSelected[kLambda]); + registryData.fill(HIST("jetV0s/nAntiLambdaSelected"), ptSub, nSelected[kAntiLambda]); + } + } + + // Reconstructed true V0s passing the selections: primaries in iHist, primaries + secondaries in iHistIncl (if >= 0) + template + void fillMCRecoV0(T const& v0, float multiplicity, float weight) + { + for (const auto& species : {kK0s, kLambda, kAntiLambda}) { + if (!isTrueV0(v0, species) || !passedV0Selection(v0, species)) { + continue; + } + if constexpr (iHistIncl >= 0) { + fillMCHistogram(species, multiplicity, v0.pt(), weight); + } + if (v0.isPhysicalPrimary()) { + fillMCHistogram(species, multiplicity, v0.pt(), weight); + } + } + } + + // Generated physical primary V0s from the PWGJE reduced MC particle table + template + void fillMCGenV0(T const& v0, float multiplicity, float weight) + { + const int species = genV0Species(v0); + if (species < 0 || !v0.isPhysicalPrimary()) { + return; + } + fillMCHistogram(static_cast(species), multiplicity, v0.pt(), weight); + } + + // MC detector level: reconstructed V0s (efficiency numerators, feed-down) and generated V0s in the reconstructed + // events (efficiency denominators), in jets and in the UE of the reconstructed jets + void processMCReco(soa::Filtered::iterator const& jcoll, aod::JetMcCollisions const&, MCDV0JetsWithConstituents const& v0jets, + aod::CandidatesV0MCD const& v0s, aod::JetTracks const& tracks, aod::JetParticles const& jetParticles) + { + if (!jcoll.has_mcCollision() || !isSelectedCollision(jcoll, jetFinderConfigDet)) { + return; + } + const auto mcCollision = jcoll.mcCollision_as(); + const float multiplicity = mcCollision.centFT0M(); + const float weight = mcCollision.weight(); + registryMC.fill(HIST("number_of_events_vsmultiplicity_rec"), multiplicity, weight); + + // Generated particles of the MC collision associated to the reconstructed collision + const auto genV0s = jetParticles.sliceBy(particlesPerMcCollision, mcCollision.globalIndex()); + + const auto jetFinderV0s = buildDetectorLevelInput(v0s, tracks); + + if (fillV0QAHistograms) { + for (const auto& v0 : v0s) { + fillV0QA(v0, jetFinderV0s.contains(v0.globalIndex())); + } + } + + // Inclusive efficiency: reconstructed and generated V0s with |eta_gen| < inclusiveEtaMax, in all the reconstructed events + for (const auto& v0 : v0s) { + if (std::abs(RecoDecay::eta(std::array{v0.pxMC(), v0.pyMC(), v0.pzMC()})) < inclusiveEtaMax) { + fillMCRecoV0(v0, multiplicity, weight); + } + } + for (const auto& genV0 : genV0s) { + if (std::abs(genV0.eta()) < inclusiveEtaMax) { + fillMCGenV0(genV0, multiplicity, weight); + } + } + + const float rho = estimateRho(v0jets); + if (rho < 0.) { + return; + } + registryMC.fill(HIST("rhoPerpCone_rec"), multiplicity, rho, weight); + + std::unordered_set v0sInJets; + std::vector> jetAxes; + collectJetsAboveThreshold(v0jets, rho, v0sInJets, jetAxes); + + bool hasSelectedJet = false; + for (const auto& jet : v0jets) { + if (!isSelectedJet(jet, rho)) { + continue; + } + hasSelectedJet = true; + registryMC.fill(HIST("number_of_jets_vsmultiplicity_rec"), multiplicity, weight); + fillJetV0Content(jet, subtractedPt(jet, rho), weight); + + // Reconstructed V0s, in-jet region: constituents of the reconstructed jet (same definition as in data) + for (const auto& v0 : jet.candidates_as()) { + fillMCRecoV0(v0, multiplicity, weight); + } + // Reconstructed V0s, UE region: same definition as in data + for (const auto& v0 : v0s) { + if (!jetFinderV0s.contains(v0.globalIndex()) || v0sInJets.contains(v0.globalIndex())) { + continue; + } + if (isInUE(v0.eta(), v0.phi(), jet)) { + fillMCRecoV0(v0, multiplicity, weight); + } + } + + // Generated V0s placed with respect to the reconstructed jet axis, using the generated momentum. The generated V0s + // are not jet constituents, so "in jet" means within R from the jet axis. In the UE, the generated V0s within R + // from the axis of another jet above threshold are excluded (as the reconstructed V0s constituents of another jet) + for (const auto& genV0 : genV0s) { + if (jetutilities::deltaR(genV0.eta(), genV0.phi(), jet.eta(), jet.phi()) < rJet) { + fillMCGenV0(genV0, multiplicity, weight); + } else if (isInUE(genV0.eta(), genV0.phi(), jet) && !isInAnyJetCone(genV0.eta(), genV0.phi(), jetAxes)) { + fillMCGenV0(genV0, multiplicity, weight); + } + } + } + + // Inclusive efficiency in the events with at least one selected jet + if (hasSelectedJet) { + registryMC.fill(HIST("number_of_events_wjet_vsmultiplicity_rec"), multiplicity, weight); + for (const auto& v0 : v0s) { + if (std::abs(RecoDecay::eta(std::array{v0.pxMC(), v0.pyMC(), v0.pzMC()})) < inclusiveEtaMax) { + fillMCRecoV0(v0, multiplicity, weight); + } + } + for (const auto& genV0 : genV0s) { + if (std::abs(genV0.eta()) < inclusiveEtaMax) { + fillMCGenV0(genV0, multiplicity, weight); + } + } + } + } + PROCESS_SWITCH(LfJetSpectra, processMCReco, "V0s in jets and in the UE, MC detector level (efficiency and feed-down)", false); + + // MC particle level: generated V0s in particle-level jets and in their UE (defined as in data), and in the full event + void processMCGen(soa::Filtered::iterator const& mcCollision, MCPV0JetsWithConstituents const& v0jets, + aod::CandidatesV0MCP const& v0s, aod::JetParticles const& particles) + { + if (!jetderiveddatautilities::selectCollision(mcCollision, jetFinderConfigGen.eventSelectionBits, jetFinderConfigGen.skipMBGapEvents, jetFinderConfigGen.applyRCTSelections)) { + return; + } + const float multiplicity = mcCollision.centFT0M(); + const float weight = mcCollision.weight(); + registryMC.fill(HIST("number_of_events_vsmultiplicity_gen"), multiplicity, weight); + + const auto genV0s = particles.sliceBy(particlesPerMcCollision, mcCollision.globalIndex()); + for (const auto& genV0 : genV0s) { + if (std::abs(genV0.eta()) < inclusiveEtaMax) { + fillMCGenV0(genV0, multiplicity, weight); + } + } + + const auto jetFinderV0s = buildParticleLevelInput(v0s, particles); + const float rho = estimateRho(v0jets); + if (rho < 0.) { + return; + } + registryMC.fill(HIST("rhoPerpCone_gen"), multiplicity, rho, weight); + + std::unordered_set v0sInJets; + std::vector> jetAxes; + collectJetsAboveThreshold(v0jets, rho, v0sInJets, jetAxes); + + for (const auto& jet : v0jets) { + if (!isSelectedJet(jet, rho)) { + continue; + } + registryMC.fill(HIST("number_of_jets_vsmultiplicity_gen"), multiplicity, weight); + + // Generated V0s, in-jet region: constituents of the particle-level jet (anti-kT clustering of the V0s as single + // pseudojets, daughters removed from the particle list, as at detector level) + for (const auto& v0 : jet.candidates_as()) { + fillMCGenV0(v0, multiplicity, weight); + } + // Generated V0s, UE region: accepted by the particle-level jet finder and not constituents of another jet above threshold + for (const auto& v0 : v0s) { + if (!jetFinderV0s.contains(v0.globalIndex()) || v0sInJets.contains(v0.globalIndex())) { + continue; + } + if (isInUE(v0.eta(), v0.phi(), jet)) { + fillMCGenV0(v0, multiplicity, weight); + } + } + } + } + PROCESS_SWITCH(LfJetSpectra, processMCGen, "V0s in jets and in the UE, MC particle level", false); + + // --------------------------------------------------------------------------------------------------------------- + // Jet pT response (requires jet-matching-mc-v0-ch) + + // Fraction of the (raw) pT of a particle-level jet carried by Xi+- and Omega+- (charged primaries, clustered as single + // particles at particle level, while at detector level their momentum is carried by the secondary V0 and the bachelor) + template + float multiStrangePtFraction(TJet const& mcpJet) + { + float pt = 0.; + for (const auto& particle : mcpJet.template tracks_as()) { + const int pdg = std::abs(particle.pdgCode()); + if (pdg == PDG_t::kXiMinus || pdg == PDG_t::kOmegaMinus) { + pt += particle.pt(); + } + } + return pt / mcpJet.pt(); + } + + // Detector-level jets of the reconstructed collision and particle-level jets matched to them. The background is + // estimated at each level from the jet finder input of that level (particle level: MC collision associated to the + // reconstructed collision), as in processMCReco and processMCGen. Detector-level side: response and matching purity + void processMCResponse(soa::Filtered::iterator const& jcoll, aod::JetMcCollisions const&, MatchedMCDV0Jets const& mcdJets, + aod::CandidatesV0MCD const& v0s, aod::JetTracks const& tracks, + MatchedMCPV0JetsWithConstituents const& mcpJets, aod::CandidatesV0MCP const& v0sMCP, aod::JetParticles const& particles) + { + if (!jcoll.has_mcCollision() || !isSelectedCollision(jcoll, jetFinderConfigDet)) { + return; + } + const auto mcCollision = jcoll.mcCollision_as(); + if (std::abs(mcCollision.posZ()) >= vertexZCut || + !jetderiveddatautilities::selectCollision(mcCollision, jetFinderConfigGen.eventSelectionBits, jetFinderConfigGen.skipMBGapEvents, jetFinderConfigGen.applyRCTSelections)) { + return; + } + const float weight = mcCollision.weight(); + + buildDetectorLevelInput(v0s, tracks); + const float rhoDet = estimateRho(mcdJets); + if (rhoDet < 0.) { + return; + } + const auto mcpJetsOfMcCollision = mcpJets.sliceBy(mcpJetsPerMcCollision, jcoll.mcCollisionId()); + buildParticleLevelInput(v0sMCP.sliceBy(v0MCPPerMcCollision, jcoll.mcCollisionId()), particles.sliceBy(particlesPerMcCollision, jcoll.mcCollisionId())); + const float rhoGen = estimateRho(mcpJetsOfMcCollision); // < 0 only if there are no particle-level jets, i.e. no matches + + for (const auto& mcdJet : mcdJets) { + if (!isInJetAcceptance(mcdJet)) { + continue; + } + const float ptSubDet = subtractedPt(mcdJet, rhoDet); + registryMC.fill(HIST("jetResponse/detJets"), ptSubDet, weight); + for (const auto& mcpJet : mcdJet.template matchedJetGeo_as()) { + if (!isInJetAcceptance(mcpJet)) { + continue; + } + const float ptSubGen = subtractedPt(mcpJet, rhoGen); + const float residualRaw = (mcdJet.pt() - mcpJet.pt()) / mcpJet.pt(); + registryMC.fill(HIST("jetResponse/detJetsMatched"), ptSubDet, weight); + registryMC.fill(HIST("jetResponse/ptSubDetVsGen"), ptSubDet, ptSubGen, weight); + registryMC.fill(HIST("jetResponse/ptRawDetVsGen"), mcdJet.pt(), mcpJet.pt(), weight); + if (ptSubGen > 0.) { + registryMC.fill(HIST("jetResponse/residualSub"), ptSubGen, (ptSubDet - ptSubGen) / ptSubGen, weight); + } + registryMC.fill(HIST("jetResponse/residualRaw"), mcpJet.pt(), residualRaw, weight); + registryMC.fill(HIST("jetResponse/residualRawVsMultiStrangeFraction"), mcpJet.pt(), multiStrangePtFraction(mcpJet), residualRaw, weight); + } + } + } + PROCESS_SWITCH(LfJetSpectra, processMCResponse, "jet pT response (detector-level side), requires jet-matching-mc-v0-ch", false); + + // Particle-level side of the jet pT response: matching efficiency of the particle-level jets in acceptance (matched to a + // detector-level jet in acceptance, in a reconstructed collision passing the event selection of processMCResponse) + void processMCResponseGen(soa::Filtered::iterator const& mcCollision, MatchedMCPV0JetsWithConstituents const& mcpJets, + aod::CandidatesV0MCP const& v0s, aod::JetParticles const& particles, + soa::SmallGroups const& collisions, MatchedMCDV0Jets const&) + { + if (!jetderiveddatautilities::selectCollision(mcCollision, jetFinderConfigGen.eventSelectionBits, jetFinderConfigGen.skipMBGapEvents, jetFinderConfigGen.applyRCTSelections)) { + return; + } + const float weight = mcCollision.weight(); + + // SmallGroups contains the reconstructed collisions linked to this JetMcCollision. + std::unordered_set selectedCollisions; + for (const auto& collision : collisions) { + if (std::abs(collision.posZ()) < vertexZCut && isSelectedCollision(collision, jetFinderConfigDet)) { + selectedCollisions.insert(collision.globalIndex()); + } + } + + buildParticleLevelInput(v0s, particles); + const float rho = estimateRho(mcpJets); + if (rho < 0.) { + return; + } + for (const auto& mcpJet : mcpJets) { + if (!isInJetAcceptance(mcpJet)) { + continue; + } + const float ptSub = subtractedPt(mcpJet, rho); + registryMC.fill(HIST("jetResponse/genJets"), ptSub, weight); + for (const auto& mcdJet : mcpJet.template matchedJetGeo_as()) { + if (isInJetAcceptance(mcdJet) && selectedCollisions.contains(mcdJet.collisionId())) { + registryMC.fill(HIST("jetResponse/genJetsMatched"), ptSub, weight); + break; + } + } + } + } + PROCESS_SWITCH(LfJetSpectra, processMCResponseGen, "jet pT response (particle-level side: matching efficiency), requires jet-matching-mc-v0-ch", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGJE/Tasks/mcGeneratorStudies.cxx b/PWGJE/Tasks/mcGeneratorStudies.cxx index fb7e2f081ec..1dccf464e99 100644 --- a/PWGJE/Tasks/mcGeneratorStudies.cxx +++ b/PWGJE/Tasks/mcGeneratorStudies.cxx @@ -80,7 +80,7 @@ struct MCGeneratorStudies { hBCCounter->GetXaxis()->SetBinLabel(2, "+TVX"); hBCCounter->GetXaxis()->SetBinLabel(3, "+Collision"); - TString mesonLatexString = (TString)mSelectedParticleCode; + TString mesonLatexString = TString(mSelectedParticleCode); switch (mSelectedParticleCode) { case 0: mesonLatexString = "particles"; diff --git a/PWGJE/Tasks/nucleiInJets.cxx b/PWGJE/Tasks/nucleiInJets.cxx index 1891dc04b65..88c47017811 100644 --- a/PWGJE/Tasks/nucleiInJets.cxx +++ b/PWGJE/Tasks/nucleiInJets.cxx @@ -23,6 +23,7 @@ #include "Common/Core/RecoDecay.h" #include "Common/Core/Zorro.h" #include "Common/Core/ZorroSummary.h" +#include "Common/DataModel/PIDResponseITS.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" @@ -42,13 +43,14 @@ #include #include #include +#include #include #include #include #include #include -#include +#include #include #include @@ -57,6 +59,7 @@ #include #include #include +#include #include #include @@ -90,9 +93,9 @@ struct nucleiInJets { case -321: // k- return -Particle::kKaon; - case 2212: // p + case PDG_t::kProton: // p return Particle::kProton; - case -2212: // antip + case PDG_t::kProtonBar: // antip return -Particle::kProton; case Pdg::kDeuteron: // Deuteron @@ -165,6 +168,17 @@ struct nucleiInJets { Configurable cfgnTPCPIDHeTOF{"cfgnTPCPIDHeTOF", 3, "nTPC PID He"}; Configurable cfgnTPCPIDTrTOF{"cfgnTPCPIDTrTOF", 3, "nTPC PID Tr"}; + // Enable an ITS nSigma veto on inclusive TPC PID selections; disabled by default. + struct ItsPid : ConfigurableGroup { + std::string prefix = "itsPid"; + Configurable applyItsPidInc{"applyItsPidInc", false, "apply ITS PID veto to inclusive TPC selections"}; + Configurable ptMaxItsPidProt{"ptMaxItsPidProt", 1.0, "maximum pT for proton ITS PID"}; + Configurable ptMaxItsPidDeut{"ptMaxItsPidDeut", 1.0, "maximum pT for deuteron ITS PID"}; + Configurable ptMaxItsPidHel{"ptMaxItsPidHel", 1.0, "maximum pT for helium ITS PID"}; + Configurable nSigmaItsMin{"nSigmaItsMin", -3.0, "minimum ITS nSigma"}; + Configurable nSigmaItsMax{"nSigmaItsMax", 3.0, "maximum ITS nSigma"}; + } itsPid; + Configurable cEnableProtonQA{"cEnableProtonQA", true, "nTPC PID Pr"}; Configurable cEnableDeuteronQA{"cEnableDeuteronQA", true, "nTPC PID De"}; Configurable cEnableHeliumQA{"cEnableHeliumQA", true, "nTPC PID He"}; @@ -177,6 +191,8 @@ struct nucleiInJets { Configurable useTOFVeto{"useTOFVeto", false, "true: use TOF veto, false: no TOF veto"}; Configurable isRequireHitsInITSLayers{"isRequireHitsInITSLayers", true, "true: at least one hit in the its inner layes"}; Configurable useMcC{"useMcC", true, "use mcC"}; + Configurable mcClosureTrainingFraction{"mcClosureTrainingFraction", 0.5f, "fraction of events assigned to MC correction training; the rest are data-like"}; + Configurable mcClosureSplitSeed{"mcClosureSplitSeed", 1234, "random seed for MC/data-like split (0: automatic seed)"}; Configurable usebkgSubractionMC{"usebkgSubractionMC", true, "use rho-area background subtraction for detector-level matched MC jets"}; Configurable cfgjetPtBkgSubMinMC{"cfgjetPtBkgSubMinMC", 10.0f, "minimum detector-level matched MC jet pT after optional background subtraction"}; Configurable useFullConeAcceptance{"useFullConeAcceptance", true, "require jet/perpendicular-cone axes to satisfy |eta_axis| < cfgtrkMaxEta - cfgjetR for jet and efficiency histograms"}; @@ -258,6 +274,8 @@ struct nucleiInJets { using JetMCPartTable = soa::Filtered>; using JetMCDetTable = soa::Filtered>; + Preslice> detectorJetsPerCollision = aod::jet::collisionId; + SliceCache cache; HistogramRegistry jetHist{"jetHist", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -265,13 +283,37 @@ struct nucleiInJets { OutputObj zorroSummary{"zorroSummary"}; Service ccdb; Service pdgDB; - TRandom3 randUniform; + uint64_t mcClosureSplitSeedEffective = 1; + + bool useTrainingHistForMcCollision(int64_t mcCollisionId) + { + // Use the MC collision ID as part of the seed, so both process functions + // obtain the same decision without storing one entry per collision. + const auto collisionId = static_cast(mcCollisionId); + std::mt19937_64 collisionRandom(mcClosureSplitSeedEffective ^ collisionId); + std::uniform_real_distribution uniform(0.0, 1.0); + return uniform(collisionRandom) < mcClosureTrainingFraction; + } + void init(o2::framework::InitContext&) { + if (isMC) { + o2::aod::ITSResponse::setMCDefaultParameters(); + } + if (doprocessJetTracksData && doprocessJetTracksDataLfPid) { LOGP(fatal, "only one process function should be enabled!!!"); } + if (!(mcClosureTrainingFraction >= 0.f) || !(mcClosureTrainingFraction <= 1.f)) { + LOGP(fatal, "mcClosureTrainingFraction must be between 0 and 1, inclusive"); + } + if (mcClosureSplitSeed == 0) { + std::random_device randomDevice; + mcClosureSplitSeedEffective = (static_cast(randomDevice()) << 32) ^ randomDevice(); + } else { + mcClosureSplitSeedEffective = static_cast(mcClosureSplitSeed); + } jetHist.print(); // const AxisSpec PtAxis = {100, 0, 10.0}; const AxisSpec PtJetAxis = {100, 0, 100.0}; @@ -284,6 +326,8 @@ struct nucleiInJets { const AxisSpec dedxAxis{binsdEdx, "d#it{E}/d#it{x} A.U."}; const AxisSpec vzAxis{300, -15.f, 15.f, "Vz (cm)"}; const AxisSpec EtaAxis{40, -1.f, 1.f, "#eta"}; + const AxisSpec RapidityQAAxis{60, -2.f, 2.f, "y"}; + const AxisSpec PtRapidityQAAxis{50, 0.f, 10.f, "#it{p}_{T} (GeV/#it{c})"}; const AxisSpec ParticleOriginAxis{3, 0.5, 3.5, "origin: 1 primary, 2 secondary/material, 3 weak decay"}; const AxisSpec betaAxis{binsBeta, "TOF #beta"}; @@ -322,13 +366,39 @@ struct nucleiInJets { jetHist.add("tracksInc/deuteron/h3PtVsDeuteronNSigmaTPCVsPt", "pT(d) vs NSigmaTPC (d) vs centrality; #it{p}_{T} (GeV/#it{c}); NSigmaTPC; centrality", HistType::kTH3F, {PtAxis, {200, -10, 10}, CentAxis}); jetHist.add("tracksInc/antiDeuteron/h3PtVsantiDeuteronNSigmaTPCVsPt", "pT(#bar{d}) vs NSigmaTPC (#bar{d}) vs centrality; #it{p}_{T} (GeV/#it{c}); NSigmaTPC; centrality", HistType::kTH3F, {PtAxis, {200, -10, 10}, CentAxis}); + // Compare lab and shifted rapidity before the rapidity selection, for pO QA. + if (isPOCollision) { + for (const auto& sample : {std::string("tracksInc"), std::string("recInc")}) { + for (const auto& species : {std::string("proton"), std::string("antiproton"), std::string("deuteron"), std::string("antideuteron")}) { + auto base = sample; + base += "/qa/rapidity/"; + base += species; + const bool isMcTruth = sample == "recInc"; + const char* yLabTitle = isMcTruth ? "matched primary MC truth; #it{p}_{T} (GeV/#it{c}); #eta; y_{lab}" : "inclusive data; #it{p}_{T} (GeV/#it{c}); #eta; y_{lab}"; + const char* yCmsTitle = isMcTruth ? "matched primary MC truth; #it{p}_{T} (GeV/#it{c}); #eta; y_{CMS}" : "inclusive data; #it{p}_{T} (GeV/#it{c}); #eta; y_{CMS}"; + jetHist.add(base + "YLabVsPtEta", yLabTitle, HistType::kTH3F, {PtRapidityQAAxis, EtaAxis, RapidityQAAxis}); + jetHist.add(base + "YCmsVsPtEta", yCmsTitle, HistType::kTH3F, {PtRapidityQAAxis, EtaAxis, RapidityQAAxis}); + } + } + } + + if (itsPid.applyItsPidInc) { + jetHist.add("tracksInc/qa/h2ITSNsigmaProtonVsPt", "inclusive proton ITS PID before veto; #it{p}_{T} (GeV/#it{c}); n#sigma_{ITS}(p)", HistType::kTH2F, {PtAxis, {200, -10, 10}}); + jetHist.add("tracksInc/qa/h2ITSNsigmaDeuteronVsPt", "inclusive deuteron ITS PID before veto; #it{p}_{T} (GeV/#it{c}); n#sigma_{ITS}(d)", HistType::kTH2F, {PtAxis, {200, -10, 10}}); + jetHist.add("tracksInc/qa/h2ITSNsigmaHeliumVsPt", "inclusive helium ITS PID before veto; #it{p}_{T}/|z| (GeV/#it{c}); n#sigma_{ITS}(He)", HistType::kTH2F, {PtAxis, {200, -10, 10}}); + jetHist.add("recInc/qa/h2ITSNsigmaProtonVsPt", "reconstructed MC proton ITS PID before veto; #it{p}_{T} (GeV/#it{c}); n#sigma_{ITS}(p)", HistType::kTH2F, {PtAxis, {200, -10, 10}}); + jetHist.add("recInc/qa/h2ITSNsigmaDeuteronVsPt", "reconstructed MC deuteron ITS PID before veto; #it{p}_{T} (GeV/#it{c}); n#sigma_{ITS}(d)", HistType::kTH2F, {PtAxis, {200, -10, 10}}); + jetHist.add("recInc/qa/h2ITSNsigmaHeliumVsPt", "reconstructed MC helium ITS PID before veto; #it{p}_{T}/|z| (GeV/#it{c}); n#sigma_{ITS}(He)", HistType::kTH2F, {PtAxis, {200, -10, 10}}); + } + // jet property jetHist.add("jet/h1JetPt", "jet_{p_{T}}", kTH1F, {PtJetAxis}); jetHist.add("jet/h1JetPtBkgSub", "jet_{p_{T}} background subtracted", kTH1F, {PtJetAxis}); jetHist.add("jet/h2JetPtVsBkgRho", "jet_{p_{T}} vs background #rho; jet_{p_{T}} (GeV/c); #rho (GeV/c/area)", kTH2F, {PtJetAxis, {100, 0, 20}}); jetHist.add("jet/h1BkgRho", "Background #rho; #rho (GeV/c/area); Entries", kTH1F, {{100, 0, 20}}); jetHist.add("jet/h1JetEvents", "NumbeOfJetEvents", kTH1F, {{1, 0, 1}}); - jetHist.add("jetBkgSub/h1JetEvents", "Number of jet events with leading p_{T}^{jet,bkg sub} above cfgjetPtBkgSubMin", kTH1F, {{1, 0, 1}}); + jetHist.add("jetBkgSub/h1JetEventsBeforeRhoSelection", "Number of jet events with leading p_{T}^{jet,bkg sub} above cfgjetPtBkgSubMin before rho selection", kTH1F, {{1, 0, 1}}); + jetHist.add("jetBkgSub/h1JetEvents", "Number of jet events with leading p_{T}^{jet,bkg sub} above cfgjetPtBkgSubMin and rho > 0", kTH1F, {{1, 0, 1}}); jetHist.add("jetBkgSub/h1BkgRhoLeadingJetPtBkgSubAboveCut", "Background #rho for events with leading p_{T}^{jet,bkg sub} above cfgjetPtBkgSubMin; #rho (GeV/#it{c}/area); Entries", kTH1F, {{100, 0, 20}}); jetHist.add("jet/h1JetEta", "jet_{#eta}", kTH1F, {{100, -1.0, 1.0}}); jetHist.add("jet/h1JetPhi", "jet_{#phi}", kTH1F, {{80, -1.0, 7.}}); @@ -639,6 +709,16 @@ struct nucleiInJets { jetHist.add(("tracksInc/dcaxy/rec/" + particle + "/tpcPtVsDcaxy3DPIDTOF").c_str(), "pT vs Dcaxy vs Centrality (PID TOF)", HistType::kTH3F, {{PtAxis}, {100, 0, 100}, dcaxyAxis}); } + // DCAxy templates are needed by both the data and reconstructed-MC processes. + const AxisSpec antiProtonTemplateOriginAxis{4, 0.5, 4.5, "1 primary #bar{p}, 2 material #bar{p}, 3 weak-decay #bar{p}, 4 other/fake"}; + for (const auto& region : {"jetCone", "perpCone"}) { + for (const auto& pid : {"TPC", "TPCTOF", "TPCTOFVeto"}) { + const std::string base = std::string("feeddown/antiProton/template/") + region + "/" + pid + "/"; + jetHist.add(base + "data", "data #bar{p} candidates before DCAxy cut; #it{p}_{T}^{rec}; DCA_{xy}", HistType::kTH2D, {{PtAxis}, {dcaxyAxis}}); + jetHist.add(base + "mc", "MC #bar{p} candidates before DCAxy cut; #it{p}_{T}^{rec}; DCA_{xy}; truth origin", HistType::kTH3D, {{PtAxis}, {dcaxyAxis}, {antiProtonTemplateOriginAxis}}); + } + } + if (isMC) { // inc jetHist.add("recInc/eventStat", "Event statistics (inclusive)", HistType::kTH1F, {{6, 0.f, 6.f}}); @@ -815,16 +895,37 @@ struct nucleiInJets { jetHist.add("eff/recmatched/mcC/gen/perpCone/pt/PtParticleType", "Pt (gen, mcC, perp cone) vs particletype", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); jetHist.add("eff/recmatched/mcCSpectra/gen/perpCone/pt/PtParticleType", "Pt (gen, mcCSpectra, perp cone) vs particletype", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); + jetHist.add("eff/recmatched/mcClosure/hSelectedRecoEvents", "matched reconstructed events per closure sample; sample; events", HistType::kTH1D, {{2, 0., 2.}}); + jetHist.get(HIST("eff/recmatched/mcClosure/hSelectedRecoEvents"))->GetXaxis()->SetBinLabel(1, "training"); + jetHist.get(HIST("eff/recmatched/mcClosure/hSelectedRecoEvents"))->GetXaxis()->SetBinLabel(2, "data-like"); + for (const auto& sample : {"training", "dataLike"}) { + for (const auto& region : {"jetCone", "perpCone"}) { + const std::string base = std::string("eff/recmatched/mcClosure/") + sample + "/" + region + "/"; + jetHist.add(base + "hRecAll", "matched primary tracks; reconstructed #it{p}_{T}; true #it{p}_{T}; particle type", HistType::kTH3D, {{PtAxis}, {PtAxis}, {14, -7, 7}}); + jetHist.add(base + "hRecTPCTOFVeto", "matched primary tracks with TPC PID and TOF veto; reconstructed #it{p}_{T}; true #it{p}_{T}; particle type", HistType::kTH3D, {{PtAxis}, {PtAxis}, {14, -7, 7}}); + jetHist.add(base + "hRecTPCTOF", "matched primary tracks with TPC and TOF PID; reconstructed #it{p}_{T}; true #it{p}_{T}; particle type", HistType::kTH3D, {{PtAxis}, {PtAxis}, {14, -7, 7}}); + jetHist.add(base + "hGen", "generated primary particles; true #it{p}_{T}; particle type", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); + } + } + jetHist.add("jetSelCorr/genSel/hLeadingJetPt", "particle-level leading jet selected for jet-selection correction; #it{p}_{T,lead}^{gen} (GeV/#it{c}); Entries", HistType::kTH1F, {{100, 0., 100.}}); - jetHist.add("jetSelCorr/recoSel/hLeadingJetPtBkgSub", "matched detector-level leading jet selected for jet-selection correction; #it{p}_{T,lead}^{reco,corr} (GeV/#it{c}); Entries", HistType::kTH1F, {{120, -20., 100.}}); + jetHist.add("jetSelCorr/recoSel/hLeadingJetPtBkgSub", "detector-level leading jet selected for jet-selection correction; #it{p}_{T,lead}^{reco,corr} (GeV/#it{c}); Entries", HistType::kTH1F, {{120, -20., 100.}}); jetHist.add("jetSelCorr/recoSel/hMatchedLeadingJetPt", "selected matched leading jet; #it{p}_{T,lead}^{reco,corr} (GeV/#it{c}); #it{p}_{T,lead}^{gen} (GeV/#it{c})", HistType::kTH2F, {{120, -20., 100.}, {100, 0., 100.}}); jetHist.add("jetSelCorr/eventNorm/hEventCounts", "event count for leading-jet event-normalization correction; event selection; Entries", HistType::kTH1D, {{2, 0., 2.}}); jetHist.get(HIST("jetSelCorr/eventNorm/hEventCounts"))->GetXaxis()->SetBinLabel(1, "gen lead #it{p}_{T} > cut"); jetHist.get(HIST("jetSelCorr/eventNorm/hEventCounts"))->GetXaxis()->SetBinLabel(2, "reco lead #it{p}_{T}^{corr} > cut"); + jetHist.add("jetSelCorr/recoSel/hMatchStatus", "selected detector leading-jet matching; status; reconstructed collisions", HistType::kTH1D, {{3, 0., 3.}}); + jetHist.get(HIST("jetSelCorr/recoSel/hMatchStatus"))->GetXaxis()->SetBinLabel(1, "no truth match in MC collision"); + jetHist.get(HIST("jetSelCorr/recoSel/hMatchStatus"))->GetXaxis()->SetBinLabel(2, "truth axis outside acceptance"); + jetHist.get(HIST("jetSelCorr/recoSel/hMatchStatus"))->GetXaxis()->SetBinLabel(3, "accepted truth axis"); + jetHist.add("jetSelCorr/eventNorm/hSelectedRecoCollisions", "selected reconstructed collisions per MC collision; selected reconstructed collisions; MC collisions", HistType::kTH1D, {{11, -0.5, 10.5}}); + jetHist.add("jetSelCorr/matchedTruthAxis/hEventCount", "selected reconstructed collisions with accepted matched truth axis; selection; reconstructed collisions", HistType::kTH1D, {{1, 0., 1.}}); + jetHist.add("jetSelCorr/matchedTruthAxis/jetCone/pt/PtParticleType", "generated primaries around the matched truth axis; #it{p}_{T}^{gen} (GeV/#it{c}); particle type", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); + jetHist.add("jetSelCorr/matchedTruthAxis/perpCone/pt/PtParticleType", "generated primaries in perpendicular cones of the matched truth jet; #it{p}_{T}^{gen} (GeV/#it{c}); particle type", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); jetHist.add("jetSelCorr/genSel/jetCone/pt/PtParticleType", "generated primaries in particle-level leading-jet cone; #it{p}_{T}^{gen} (GeV/#it{c}); particle type", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); jetHist.add("jetSelCorr/genSel/perpCone/pt/PtParticleType", "generated primaries in particle-level perpendicular cone; #it{p}_{T}^{gen} (GeV/#it{c}); particle type", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); - jetHist.add("jetSelCorr/recoSel/jetCone/pt/PtParticleType", "generated primaries in matched reco-selected leading-jet cone; #it{p}_{T}^{gen} (GeV/#it{c}); particle type", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); - jetHist.add("jetSelCorr/recoSel/perpCone/pt/PtParticleType", "generated primaries in matched reco-selected perpendicular cone; #it{p}_{T}^{gen} (GeV/#it{c}); particle type", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); + jetHist.add("jetSelCorr/recoSel/jetCone/pt/PtParticleType", "generated primaries around the selected detector leading-jet axis; #it{p}_{T}^{gen} (GeV/#it{c}); particle type", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); + jetHist.add("jetSelCorr/recoSel/perpCone/pt/PtParticleType", "generated primaries in perpendicular cones of the selected detector leading jet; #it{p}_{T}^{gen} (GeV/#it{c}); particle type", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); jetHist.add("feeddown/antiProton/jetCone/PtOrigin", "reconstructed #bar{p} origin in jet cone; #it{p}_{T}^{rec} (GeV/#it{c}); origin", HistType::kTH2D, {{PtAxis}, {ParticleOriginAxis}}); jetHist.add("feeddown/antiProton/jetCone/PtOriginTPC", "reconstructed #bar{p} origin in jet cone, TPC PID; #it{p}_{T}^{rec} (GeV/#it{c}); origin", HistType::kTH2D, {{PtAxis}, {ParticleOriginAxis}}); @@ -867,8 +968,8 @@ struct nucleiInJets { { std::array PerpendicularConeAxisPhi = {-999.0f, -999.0f}; // build 2 perp cones in phi around the leading jet (right and left of the jet) - PerpendicularConeAxisPhi[0] = RecoDecay::constrainAngle(jetPhi + (o2::constants::math::PIHalf)); // This will contrain the angel between 0-2Pi - PerpendicularConeAxisPhi[1] = RecoDecay::constrainAngle(jetPhi - (o2::constants::math::PIHalf)); // This will contrain the angel between 0-2Pi + PerpendicularConeAxisPhi[0] = RecoDecay::constrainAngle(jetPhi + o2::constants::math::PIHalf); // This will contrain the angel between 0-2Pi + PerpendicularConeAxisPhi[1] = RecoDecay::constrainAngle(jetPhi - o2::constants::math::PIHalf); // This will contrain the angel between 0-2Pi return PerpendicularConeAxisPhi; } @@ -932,16 +1033,43 @@ struct nucleiInJets { bool isOccupancyAccepted(const coll& collision) { auto occupancy{collision.trackOccupancyInTimeRange()}; - if (occupancy < cfgOccupancyRange->at(0) || occupancy > cfgOccupancyRange->at(1)) - return false; - else - return true; + return !(occupancy < cfgOccupancyRange->at(0) || occupancy > cfgOccupancyRange->at(1)); } int nEvents = 0; template void fillTrackInfo(const TracksType& trk, const JetType& jets, std::vector& leadingJetPtEtaPhi, float backgroundRho = -1.0f) { + if constexpr (!IsMC) { + if (isWithLeadingJet && leadingJetPtEtaPhi.size() >= 3 && isConeAxisAccepted(leadingJetPtEtaPhi[1]) && + backgroundRho > 0.f && leadingJetPtEtaPhi[0] - backgroundRho * M_PI * cfgjetR * cfgjetR > cfgjetPtBkgSubMin && + trk.sign() < 0 && trk.pt() <= cMaxPt && isTrackSelectedWithoutDcaxy(trk) && + (!useRapidityCutForPID || isRapiditySelectedForPID(trk.rapidity(o2::constants::physics::MassProton)))) { + const bool tpcPassed = std::abs(trk.tpcNSigmaPr()) < cfgnTPCPIDPr; + if (tpcPassed) { + const bool tofPassed = trk.hasTOF() && std::abs(trk.tofNSigmaPr()) < cfgnTPCPIDPrTOF; + const double deltaEta = leadingJetPtEtaPhi[1] - trk.eta(); + const double deltaPhi = TVector2::Phi_mpi_pi(leadingJetPtEtaPhi[2] - trk.phi()); + if (RecoDecay::sqrtSumOfSquares(deltaEta, deltaPhi) < cfgjetR) { + jetHist.fill(HIST("feeddown/antiProton/template/jetCone/TPC/data"), trk.pt(), trk.dcaXY()); + if (tofPassed) + jetHist.fill(HIST("feeddown/antiProton/template/jetCone/TPCTOF/data"), trk.pt(), trk.dcaXY()); + if (!trk.hasTOF() || tofPassed) + jetHist.fill(HIST("feeddown/antiProton/template/jetCone/TPCTOFVeto/data"), trk.pt(), trk.dcaXY()); + } + const auto perpPhi = getPerpendicuarPhi(leadingJetPtEtaPhi[2]); + const double deltaPhiPerp1 = TVector2::Phi_mpi_pi(perpPhi[0] - trk.phi()); + const double deltaPhiPerp2 = TVector2::Phi_mpi_pi(perpPhi[1] - trk.phi()); + if (RecoDecay::sqrtSumOfSquares(deltaEta, deltaPhiPerp1) < cfgjetR || RecoDecay::sqrtSumOfSquares(deltaEta, deltaPhiPerp2) < cfgjetR) { + jetHist.fill(HIST("feeddown/antiProton/template/perpCone/TPC/data"), trk.pt(), trk.dcaXY()); + if (tofPassed) + jetHist.fill(HIST("feeddown/antiProton/template/perpCone/TPCTOF/data"), trk.pt(), trk.dcaXY()); + if (!trk.hasTOF() || tofPassed) + jetHist.fill(HIST("feeddown/antiProton/template/perpCone/TPCTOFVeto/data"), trk.pt(), trk.dcaXY()); + } + } + } + } if (!isTrackSelected(trk)) return; if (trk.pt() > cMaxPt) @@ -1818,7 +1946,7 @@ struct nucleiInJets { jetHist.fill(HIST("jetBkgSub/h1LeadingJetPtBkgSub"), leadingJetPtBkgSub); jetHist.fill(HIST("jetBkgSub/h2LeadingJetPtBkgSubVsVertexZ"), leadingJetPtBkgSub, collision.posZ()); } - if (leadingJetPtBkgSub > cfgjetPtBkgSubMin) { + if (backgroundRho > 0 && leadingJetPtBkgSub > cfgjetPtBkgSubMin) { jetHist.fill(HIST("jetBkgSub/vertexZ"), collision.posZ()); jetHist.fill(HIST("jetBkgSub/h1BkgRhoLeadingJetPtBkgSubAboveCut"), backgroundRho); jetHist.fill(HIST("hNEvents"), 8.5); @@ -1829,7 +1957,10 @@ struct nucleiInJets { return; jetHist.fill(HIST("jet/h1JetEvents"), 0.5); if (leadingJetPtBkgSub > cfgjetPtBkgSubMin) { - jetHist.fill(HIST("jetBkgSub/h1JetEvents"), 0.5); + jetHist.fill(HIST("jetBkgSub/h1JetEventsBeforeRhoSelection"), 0.5); + if (backgroundRho > 0) { + jetHist.fill(HIST("jetBkgSub/h1JetEvents"), 0.5); + } } for (const auto& track : tracks) { auto trk = track.track_as(); @@ -1920,7 +2051,7 @@ struct nucleiInJets { jetHist.fill(HIST("jetBkgSub/h1LeadingJetPtBkgSub"), leadingJetPtBkgSub); jetHist.fill(HIST("jetBkgSub/h2LeadingJetPtBkgSubVsVertexZ"), leadingJetPtBkgSub, collision.posZ()); } - if (leadingJetPtBkgSub > cfgjetPtBkgSubMin) { + if (backgroundRho > 0 && leadingJetPtBkgSub > cfgjetPtBkgSubMin) { jetHist.fill(HIST("jetBkgSub/vertexZ"), collision.posZ()); jetHist.fill(HIST("jetBkgSub/h1BkgRhoLeadingJetPtBkgSubAboveCut"), backgroundRho); jetHist.fill(HIST("hNEvents"), 8.5); @@ -1931,7 +2062,10 @@ struct nucleiInJets { return; jetHist.fill(HIST("jet/h1JetEvents"), 0.5); if (leadingJetPtBkgSub > cfgjetPtBkgSubMin) { - jetHist.fill(HIST("jetBkgSub/h1JetEvents"), 0.5); + jetHist.fill(HIST("jetBkgSub/h1JetEventsBeforeRhoSelection"), 0.5); + if (backgroundRho > 0) { + jetHist.fill(HIST("jetBkgSub/h1JetEvents"), 0.5); + } } for (auto& track : tracks) { auto trk = track.track_as(); @@ -1986,21 +2120,36 @@ struct nucleiInJets { continue; } - auto rapidityData = [&](float m2z) { - return isRapiditySelectedForPID(trk.rapidity(m2z)); - }; + double nSigmaITSPr = 0.0; + double nSigmaITSDe = 0.0; + double nSigmaITSHe = 0.0; + if (itsPid.applyItsPidInc) { + nSigmaITSPr = o2::aod::ITSResponse::nSigmaITS(trk); + nSigmaITSDe = o2::aod::ITSResponse::nSigmaITS(trk); + nSigmaITSHe = o2::aod::ITSResponse::nSigmaITS(trk); + } + const bool passItsPidPr = !itsPid.applyItsPidInc || trk.pt() >= itsPid.ptMaxItsPidProt || (nSigmaITSPr >= itsPid.nSigmaItsMin && nSigmaITSPr <= itsPid.nSigmaItsMax); + const bool passItsPidDe = !itsPid.applyItsPidInc || trk.pt() >= itsPid.ptMaxItsPidDeut || (nSigmaITSDe >= itsPid.nSigmaItsMin && nSigmaITSDe <= itsPid.nSigmaItsMax); + const bool passItsPidHe = !itsPid.applyItsPidInc || 2.0 * trk.pt() >= itsPid.ptMaxItsPidHel || (nSigmaITSHe >= itsPid.nSigmaItsMin && nSigmaITSHe <= itsPid.nSigmaItsMax); - auto prRapidityWithinRange = rapidityData(o2::constants::physics::MassProton); - auto deRapidityWithinRange = rapidityData(o2::constants::physics::MassDeuteron); + const double yLabPr = trk.rapidity(o2::constants::physics::MassProton); + const double yLabDe = trk.rapidity(o2::constants::physics::MassDeuteron); + auto prRapidityWithinRange = isRapiditySelectedForPID(yLabPr); + auto deRapidityWithinRange = isRapiditySelectedForPID(yLabDe); if (std::fabs(trk.eta()) > cfgtrkMaxEta) continue; - bool hasTOF = trk.hasTOF(); + if (itsPid.applyItsPidInc) { + jetHist.fill(HIST("tracksInc/qa/h2ITSNsigmaProtonVsPt"), trk.pt(), nSigmaITSPr); + jetHist.fill(HIST("tracksInc/qa/h2ITSNsigmaDeuteronVsPt"), trk.pt(), nSigmaITSDe); + jetHist.fill(HIST("tracksInc/qa/h2ITSNsigmaHeliumVsPt"), 2.0 * trk.pt(), nSigmaITSHe); + } + bool hasTOF = trk.hasTOF(); if (trk.sign() > 0) { // particles - if (std::abs(trk.tpcNSigmaPr()) < cfgnTPCPIDPr) { + if (passItsPidPr && std::abs(trk.tpcNSigmaPr()) < cfgnTPCPIDPr) { jetHist.fill(HIST("tracksInc/dcaxy/rec/proton/tpcPtVsDcaxy3D"), trk.pt(), centrality, trk.dcaXY()); if (useTOFVeto && hasTOF) { if (std::abs(trk.tofNSigmaPr()) < cfgnTPCPIDPrTOF) { @@ -2012,7 +2161,7 @@ struct nucleiInJets { } } // proton - if (std::abs(trk.tpcNSigmaDe()) < cfgnTPCPIDDe) { + if (passItsPidDe && std::abs(trk.tpcNSigmaDe()) < cfgnTPCPIDDe) { jetHist.fill(HIST("tracksInc/dcaxy/rec/deuteron/tpcPtVsDcaxy3D"), trk.pt(), centrality, trk.dcaXY()); if (useTOFVeto && hasTOF) { if (std::abs(trk.tofNSigmaDe()) < cfgnTPCPIDDeTOF) { @@ -2036,7 +2185,7 @@ struct nucleiInJets { } } // Triton - if (std::abs(trk.tpcNSigmaHe()) < cfgnTPCPIDHe) { + if (passItsPidHe && std::abs(trk.tpcNSigmaHe()) < cfgnTPCPIDHe) { jetHist.fill(HIST("tracksInc/dcaxy/rec/helium/tpcPtVsDcaxy3D"), trk.pt(), centrality, trk.dcaXY()); if (useTOFVeto && hasTOF) { if (std::abs(trk.tofNSigmaHe()) < cfgnTPCPIDHeTOF) { @@ -2050,7 +2199,7 @@ struct nucleiInJets { } else { // antiparticles - if (std::abs(trk.tpcNSigmaPr()) < cfgnTPCPIDPr) { + if (passItsPidPr && std::abs(trk.tpcNSigmaPr()) < cfgnTPCPIDPr) { jetHist.fill(HIST("tracksInc/dcaxy/rec/antiProton/tpcPtVsDcaxy3D"), trk.pt(), centrality, trk.dcaXY()); if (useTOFVeto && hasTOF) { if (std::abs(trk.tofNSigmaPr()) < cfgnTPCPIDPrTOF) { @@ -2062,7 +2211,7 @@ struct nucleiInJets { } } // proton - if (std::abs(trk.tpcNSigmaDe()) < cfgnTPCPIDDe) { + if (passItsPidDe && std::abs(trk.tpcNSigmaDe()) < cfgnTPCPIDDe) { jetHist.fill(HIST("tracksInc/dcaxy/rec/antiDeuteron/tpcPtVsDcaxy3D"), trk.pt(), centrality, trk.dcaXY()); if (useTOFVeto && hasTOF) { if (std::abs(trk.tofNSigmaDe()) < cfgnTPCPIDDeTOF) { @@ -2086,7 +2235,7 @@ struct nucleiInJets { } } // Triton - if (std::abs(trk.tpcNSigmaHe()) < cfgnTPCPIDHe) { + if (passItsPidHe && std::abs(trk.tpcNSigmaHe()) < cfgnTPCPIDHe) { jetHist.fill(HIST("tracksInc/dcaxy/rec/antiHelium/tpcPtVsDcaxy3D"), trk.pt(), centrality, trk.dcaXY()); if (useTOFVeto && hasTOF) { if (std::abs(trk.tofNSigmaHe()) < cfgnTPCPIDHeTOF) { @@ -2106,38 +2255,52 @@ struct nucleiInJets { if (std::fabs(trk.dcaXY()) > dcaXYPtDepCut(trk.pt()) && useDcaxyPtDepCut) continue; + if (isPOCollision) { + if (trk.sign() > 0) { + jetHist.fill(HIST("tracksInc/qa/rapidity/protonYLabVsPtEta"), trk.pt(), trk.eta(), yLabPr); + jetHist.fill(HIST("tracksInc/qa/rapidity/protonYCmsVsPtEta"), trk.pt(), trk.eta(), rapidityInSelectedFrame(yLabPr)); + jetHist.fill(HIST("tracksInc/qa/rapidity/deuteronYLabVsPtEta"), trk.pt(), trk.eta(), yLabDe); + jetHist.fill(HIST("tracksInc/qa/rapidity/deuteronYCmsVsPtEta"), trk.pt(), trk.eta(), rapidityInSelectedFrame(yLabDe)); + } else { + jetHist.fill(HIST("tracksInc/qa/rapidity/antiprotonYLabVsPtEta"), trk.pt(), trk.eta(), yLabPr); + jetHist.fill(HIST("tracksInc/qa/rapidity/antiprotonYCmsVsPtEta"), trk.pt(), trk.eta(), rapidityInSelectedFrame(yLabPr)); + jetHist.fill(HIST("tracksInc/qa/rapidity/antideuteronYLabVsPtEta"), trk.pt(), trk.eta(), yLabDe); + jetHist.fill(HIST("tracksInc/qa/rapidity/antideuteronYCmsVsPtEta"), trk.pt(), trk.eta(), rapidityInSelectedFrame(yLabDe)); + } + } + if (trk.sign() > 0) { // particle info if (useTOFNsigmaPreSel && trk.hasTOF()) { - if (std::abs(trk.tofNSigmaPr()) < cfgnTPCPIDPrTOF && (!useRapidityCutForPID || prRapidityWithinRange)) { + if (passItsPidPr && std::abs(trk.tofNSigmaPr()) < cfgnTPCPIDPrTOF && (!useRapidityCutForPID || prRapidityWithinRange)) { jetHist.fill(HIST("tracksInc/proton/h3PtVsProtonNSigmaTPCVsPt"), trk.pt(), trk.tpcNSigmaPr(), centrality); } - if (std::abs(trk.tofNSigmaDe()) < cfgnTPCPIDDeTOF && (!useRapidityCutForPID || deRapidityWithinRange)) { + if (passItsPidDe && std::abs(trk.tofNSigmaDe()) < cfgnTPCPIDDeTOF && (!useRapidityCutForPID || deRapidityWithinRange)) { jetHist.fill(HIST("tracksInc/deuteron/h3PtVsDeuteronNSigmaTPCVsPt"), trk.pt(), trk.tpcNSigmaDe(), centrality); } } else if (!useTOFNsigmaPreSel && !useTOFVeto) { - if (!useRapidityCutForPID || prRapidityWithinRange) { + if (passItsPidPr && (!useRapidityCutForPID || prRapidityWithinRange)) { jetHist.fill(HIST("tracksInc/proton/h3PtVsProtonNSigmaTPCVsPt"), trk.pt(), trk.tpcNSigmaPr(), centrality); } - if (!useRapidityCutForPID || deRapidityWithinRange) { + if (passItsPidDe && (!useRapidityCutForPID || deRapidityWithinRange)) { jetHist.fill(HIST("tracksInc/deuteron/h3PtVsDeuteronNSigmaTPCVsPt"), trk.pt(), trk.tpcNSigmaDe(), centrality); } } else if (!useTOFNsigmaPreSel && useTOFVeto) { if (trk.hasTOF()) { if (std::abs(trk.tofNSigmaPr()) < cfgnTPCPIDPrTOF) { - if (!useRapidityCutForPID || prRapidityWithinRange) + if (passItsPidPr && (!useRapidityCutForPID || prRapidityWithinRange)) jetHist.fill(HIST("tracksInc/proton/h3PtVsProtonNSigmaTPCVsPt"), trk.pt(), trk.tpcNSigmaPr(), centrality); } } else { - if (!useRapidityCutForPID || prRapidityWithinRange) + if (passItsPidPr && (!useRapidityCutForPID || prRapidityWithinRange)) jetHist.fill(HIST("tracksInc/proton/h3PtVsProtonNSigmaTPCVsPt"), trk.pt(), trk.tpcNSigmaPr(), centrality); } if (trk.hasTOF()) { if (std::abs(trk.tofNSigmaDe()) < cfgnTPCPIDDeTOF) { - if (!useRapidityCutForPID || deRapidityWithinRange) + if (passItsPidDe && (!useRapidityCutForPID || deRapidityWithinRange)) jetHist.fill(HIST("tracksInc/deuteron/h3PtVsDeuteronNSigmaTPCVsPt"), trk.pt(), trk.tpcNSigmaDe(), centrality); } } else { - if (!useRapidityCutForPID || deRapidityWithinRange) + if (passItsPidDe && (!useRapidityCutForPID || deRapidityWithinRange)) jetHist.fill(HIST("tracksInc/deuteron/h3PtVsDeuteronNSigmaTPCVsPt"), trk.pt(), trk.tpcNSigmaDe(), centrality); } } @@ -2155,12 +2318,12 @@ struct nucleiInJets { jetHist.fill(HIST("tracksInc/deuteron/h2TofNsigmaDeuteronVsPt"), trk.tofNSigmaDe(), trk.pt(), centrality); } } else { - if (std::abs(trk.tpcNSigmaPr()) < cfgnTPCPIDPr && (!useRapidityCutForPID || prRapidityWithinRange)) { + if (passItsPidPr && std::abs(trk.tpcNSigmaPr()) < cfgnTPCPIDPr && (!useRapidityCutForPID || prRapidityWithinRange)) { jetHist.fill(HIST("tracksInc/proton/h2TOFmassProtonVsPt"), massTOF, trk.pt(), centrality); jetHist.fill(HIST("tracksInc/proton/h2TOFmass2ProtonVsPt"), massTOF * massTOF - MassProton * MassProton, trk.pt(), centrality); jetHist.fill(HIST("tracksInc/proton/h2TofNsigmaProtonVsPt"), trk.tofNSigmaPr(), trk.pt(), centrality); } - if (std::abs(trk.tpcNSigmaDe()) < cfgnTPCPIDDe && (!useRapidityCutForPID || deRapidityWithinRange)) { + if (passItsPidDe && std::abs(trk.tpcNSigmaDe()) < cfgnTPCPIDDe && (!useRapidityCutForPID || deRapidityWithinRange)) { jetHist.fill(HIST("tracksInc/deuteron/h2TOFmassDeuteronVsPt"), massTOF, trk.pt(), centrality); jetHist.fill(HIST("tracksInc/deuteron/h2TOFmass2DeuteronVsPt"), massTOF * massTOF - MassDeuteron * MassDeuteron, trk.pt(), centrality); jetHist.fill(HIST("tracksInc/deuteron/h2TofNsigmaDeuteronVsPt"), trk.tofNSigmaDe(), trk.pt(), centrality); @@ -2168,41 +2331,41 @@ struct nucleiInJets { } } - if (cEnableProtonQA && std::abs(trk.tpcNSigmaPr()) < cfgnTPCPIDPr && (!useRapidityCutForPID || prRapidityWithinRange)) { + if (cEnableProtonQA && passItsPidPr && std::abs(trk.tpcNSigmaPr()) < cfgnTPCPIDPr && (!useRapidityCutForPID || prRapidityWithinRange)) { jetHist.fill(HIST("tracksInc/proton/dca/after/hDCAxyVsPtProton"), trk.dcaXY(), trk.pt(), centrality); jetHist.fill(HIST("tracksInc/proton/dca/after/hDCAzVsPtProton"), trk.dcaZ(), trk.pt(), centrality); } - if (cEnableDeuteronQA && std::abs(trk.tpcNSigmaDe()) < cfgnTPCPIDDe && (!useRapidityCutForPID || deRapidityWithinRange)) { + if (cEnableDeuteronQA && passItsPidDe && std::abs(trk.tpcNSigmaDe()) < cfgnTPCPIDDe && (!useRapidityCutForPID || deRapidityWithinRange)) { jetHist.fill(HIST("tracksInc/deuteron/dca/after/hDCAxyVsPtDeuteron"), trk.dcaXY(), trk.pt(), centrality); jetHist.fill(HIST("tracksInc/deuteron/dca/after/hDCAzVsPtDeuteron"), trk.dcaZ(), trk.pt(), centrality); } } else { // anti-particle info if (useTOFNsigmaPreSel && trk.hasTOF()) { - if (std::abs(trk.tofNSigmaPr()) < cfgnTPCPIDPrTOF && (!useRapidityCutForPID || prRapidityWithinRange)) + if (passItsPidPr && std::abs(trk.tofNSigmaPr()) < cfgnTPCPIDPrTOF && (!useRapidityCutForPID || prRapidityWithinRange)) jetHist.fill(HIST("tracksInc/antiProton/h3PtVsantiProtonNSigmaTPCVsPt"), trk.pt(), trk.tpcNSigmaPr(), centrality); - if (std::abs(trk.tofNSigmaDe()) < cfgnTPCPIDDeTOF && (!useRapidityCutForPID || deRapidityWithinRange)) + if (passItsPidDe && std::abs(trk.tofNSigmaDe()) < cfgnTPCPIDDeTOF && (!useRapidityCutForPID || deRapidityWithinRange)) jetHist.fill(HIST("tracksInc/antiDeuteron/h3PtVsantiDeuteronNSigmaTPCVsPt"), trk.pt(), trk.tpcNSigmaDe(), centrality); } else if (!useTOFNsigmaPreSel && !useTOFVeto) { - if (!useRapidityCutForPID || prRapidityWithinRange) + if (passItsPidPr && (!useRapidityCutForPID || prRapidityWithinRange)) jetHist.fill(HIST("tracksInc/antiProton/h3PtVsantiProtonNSigmaTPCVsPt"), trk.pt(), trk.tpcNSigmaPr(), centrality); - if (!useRapidityCutForPID || deRapidityWithinRange) + if (passItsPidDe && (!useRapidityCutForPID || deRapidityWithinRange)) jetHist.fill(HIST("tracksInc/antiDeuteron/h3PtVsantiDeuteronNSigmaTPCVsPt"), trk.pt(), trk.tpcNSigmaDe(), centrality); } else if (!useTOFNsigmaPreSel && useTOFVeto) { if (trk.hasTOF()) { - if (std::abs(trk.tofNSigmaPr()) < cfgnTPCPIDPrTOF && (!useRapidityCutForPID || prRapidityWithinRange)) { + if (passItsPidPr && std::abs(trk.tofNSigmaPr()) < cfgnTPCPIDPrTOF && (!useRapidityCutForPID || prRapidityWithinRange)) { jetHist.fill(HIST("tracksInc/antiProton/h3PtVsantiProtonNSigmaTPCVsPt"), trk.pt(), trk.tpcNSigmaPr(), centrality); } } else { - if (!useRapidityCutForPID || prRapidityWithinRange) + if (passItsPidPr && (!useRapidityCutForPID || prRapidityWithinRange)) jetHist.fill(HIST("tracksInc/antiProton/h3PtVsantiProtonNSigmaTPCVsPt"), trk.pt(), trk.tpcNSigmaPr(), centrality); } if (trk.hasTOF()) { - if (std::abs(trk.tofNSigmaDe()) < cfgnTPCPIDDeTOF && (!useRapidityCutForPID || deRapidityWithinRange)) { + if (passItsPidDe && std::abs(trk.tofNSigmaDe()) < cfgnTPCPIDDeTOF && (!useRapidityCutForPID || deRapidityWithinRange)) { jetHist.fill(HIST("tracksInc/antiDeuteron/h3PtVsantiDeuteronNSigmaTPCVsPt"), trk.pt(), trk.tpcNSigmaDe(), centrality); } } else { - if (!useRapidityCutForPID || deRapidityWithinRange) + if (passItsPidDe && (!useRapidityCutForPID || deRapidityWithinRange)) jetHist.fill(HIST("tracksInc/antiDeuteron/h3PtVsantiDeuteronNSigmaTPCVsPt"), trk.pt(), trk.tpcNSigmaDe(), centrality); } } @@ -2220,12 +2383,12 @@ struct nucleiInJets { jetHist.fill(HIST("tracksInc/antiDeuteron/h2TofNsigmaantiDeuteronVsPt"), trk.tofNSigmaDe(), trk.pt(), centrality); } } else { - if (std::abs(trk.tpcNSigmaPr()) < cfgnTPCPIDPr && (!useRapidityCutForPID || prRapidityWithinRange)) { + if (passItsPidPr && std::abs(trk.tpcNSigmaPr()) < cfgnTPCPIDPr && (!useRapidityCutForPID || prRapidityWithinRange)) { jetHist.fill(HIST("tracksInc/antiProton/h2TOFmassantiProtonVsPt"), massTOF, trk.pt(), centrality); jetHist.fill(HIST("tracksInc/antiProton/h2TOFmass2antiProtonVsPt"), massTOF * massTOF - MassProton * MassProton, trk.pt(), centrality); jetHist.fill(HIST("tracksInc/antiProton/h2TofNsigmaantiProtonVsPt"), trk.tofNSigmaPr(), trk.pt(), centrality); } - if (std::abs(trk.tpcNSigmaDe()) < cfgnTPCPIDDe && (!useRapidityCutForPID || deRapidityWithinRange)) { + if (passItsPidDe && std::abs(trk.tpcNSigmaDe()) < cfgnTPCPIDDe && (!useRapidityCutForPID || deRapidityWithinRange)) { jetHist.fill(HIST("tracksInc/antiDeuteron/h2TOFmassantiDeuteronVsPt"), massTOF, trk.pt(), centrality); jetHist.fill(HIST("tracksInc/antiDeuteron/h2TOFmass2antiDeuteronVsPt"), massTOF * massTOF - MassDeuteron * MassDeuteron, trk.pt(), centrality); jetHist.fill(HIST("tracksInc/antiDeuteron/h2TofNsigmaantiDeuteronVsPt"), trk.tofNSigmaDe(), trk.pt(), centrality); @@ -2399,7 +2562,7 @@ struct nucleiInJets { return true; } - template + template void fillJetSelectionCorrectionParticles(const ParticlesType& mcParticles, double jetEta, double jetPhi) { const auto perpConePhiJet = getPerpendicuarPhi(jetPhi); @@ -2423,7 +2586,9 @@ struct nucleiInJets { const double delPhi = TVector2::Phi_mpi_pi(jetPhi - mcParticle.phi()); const double rJet = RecoDecay::sqrtSumOfSquares(delEta, delPhi); if (rJet < cfgjetR) { - if constexpr (RecoSelected) { + if constexpr (MatchedTruthAxis) { + jetHist.fill(HIST("jetSelCorr/matchedTruthAxis/jetCone/pt/PtParticleType"), mcParticle.pt(), particleType); + } else if constexpr (RecoSelected) { jetHist.fill(HIST("jetSelCorr/recoSel/jetCone/pt/PtParticleType"), mcParticle.pt(), particleType); } else { jetHist.fill(HIST("jetSelCorr/genSel/jetCone/pt/PtParticleType"), mcParticle.pt(), particleType); @@ -2435,7 +2600,9 @@ struct nucleiInJets { const double rPerpCone1 = RecoDecay::sqrtSumOfSquares(delEta, delPhiPerpCone1); const double rPerpCone2 = RecoDecay::sqrtSumOfSquares(delEta, delPhiPerpCone2); if (rPerpCone1 < cfgjetR || rPerpCone2 < cfgjetR) { - if constexpr (RecoSelected) { + if constexpr (MatchedTruthAxis) { + jetHist.fill(HIST("jetSelCorr/matchedTruthAxis/perpCone/pt/PtParticleType"), mcParticle.pt(), particleType); + } else if constexpr (RecoSelected) { jetHist.fill(HIST("jetSelCorr/recoSel/perpCone/pt/PtParticleType"), mcParticle.pt(), particleType); } else { jetHist.fill(HIST("jetSelCorr/genSel/perpCone/pt/PtParticleType"), mcParticle.pt(), particleType); @@ -2453,16 +2620,43 @@ struct nucleiInJets { if (!jetderiveddatautilities::selectCollision(collision, jetderiveddatautilities::initialiseEventSelectionBits("sel8"))) return; + if (selNoSameBunchPileup && !jetderiveddatautilities::selectCollision(collision, jetderiveddatautilities::initialiseEventSelectionBits("NoSameBunchPileup"))) + return; + if (selIsGoodZvtxFT0vsPV && !jetderiveddatautilities::selectCollision(collision, jetderiveddatautilities::initialiseEventSelectionBits("IsGoodZvtxFT0vsPV"))) + return; + if (useOccupancy && !isOccupancyAccepted(collision)) + return; + jetHist.fill(HIST("recmatched/vertexZ"), collision.posZ()); - // Event-wise random splitting for closure test: decide once per event - bool useDataLikeHist = (randUniform.Uniform(0, 1) < 0.5); + // One random choice is shared by the reconstructed and generated spectra of this event. + const bool useTrainingHist = useTrainingHistForMcCollision(collision.mcCollision().globalIndex()); const float backgroundRho = collision.rho(); + if (usebkgSubractionMC && backgroundRho <= 0.f) { + return; + } const float jetArea = M_PI * cfgjetR * cfgjetR; if (usebkgSubractionMC) { jetHist.fill(HIST("jet/h1BkgRho"), backgroundRho); } + // Select the accepted detector-level leading jet before considering matches. + int32_t leadingDetJetId = -1; + float leadingDetJetPt = -1.f; + if (isWithLeadingJet) { + for (const auto& mcdjet : mcdjets) { + const float mcdJetPtForResponseSel = usebkgSubractionMC ? mcdjet.pt() - backgroundRho * jetArea : mcdjet.pt(); + if (isConeAxisAccepted(mcdjet.eta()) && mcdJetPtForResponseSel > leadingDetJetPt) { + leadingDetJetPt = mcdJetPtForResponseSel; + leadingDetJetId = mcdjet.globalIndex(); + } + } + if (leadingDetJetId < 0) { + return; + } + } + + std::vector mcdJetIds{}; std::vector mcdJetPt{}; std::vector mcdJetPhi{}; std::vector mcdJetEta{}; @@ -2483,7 +2677,7 @@ struct nucleiInJets { continue; const float mcdJetPtForResponse = usebkgSubractionMC ? mcdjet.pt() - backgroundRho * jetArea : mcdjet.pt(); - if (mcdJetPtForResponse < cfgjetPtBkgSubMinMC) { + if (mcdJetPtForResponse <= cfgjetPtBkgSubMinMC) { continue; } const double jetAxisEtaForEff = isWithLeadingJet ? (useLeadingJetDetLevelValue ? mcdjet.eta() : mcpjet.eta()) : mcdjet.eta(); @@ -2496,6 +2690,7 @@ struct nucleiInJets { jetHist.fill(HIST("recmatched/qa/h2JetConeEtaPhiAfterAcceptance"), jetAxisEtaForEff, jetAxisPhiForEff); jetHist.fill(HIST("recmatched/qa/h2PerpConeEtaPhiAfterAcceptance"), jetAxisEtaForEff, perpConePhiJet[0]); jetHist.fill(HIST("recmatched/qa/h2PerpConeEtaPhiAfterAcceptance"), jetAxisEtaForEff, perpConePhiJet[1]); + mcdJetIds.push_back(mcdjet.globalIndex()); mcdJetPt.push_back(mcdJetPtForResponse); mcdJetPhi.push_back(mcdjet.phi()); mcdJetEta.push_back(mcdjet.eta()); @@ -2523,14 +2718,24 @@ struct nucleiInJets { } else { LOGP(fatal, "Error: Index {} is out of range for vectors!", indexJet); } + if (isWithLeadingJet) { + const auto leadingMatch = std::find(mcdJetIds.begin(), mcdJetIds.end(), leadingDetJetId); + if (leadingMatch == mcdJetIds.end()) { + return; // Do not replace an unmatched or rejected leading jet with a subleading jet. + } + indexJet = std::distance(mcdJetIds.begin(), leadingMatch); + } if (useMcC) { - if (useDataLikeHist) + if (useTrainingHist) jetHist.fill(HIST("recmatched/h2ResponseMatrixLeadingJet"), mcdJetPt.at(indexJet), mcpJetPt.at(indexJet)); else jetHist.fill(HIST("recmatched/mcC/h2ResponseMatrixLeadingJet"), mcdJetPt.at(indexJet), mcpJetPt.at(indexJet)); } else { jetHist.fill(HIST("recmatched/h2ResponseMatrixLeadingJet"), mcdJetPt.at(indexJet), mcpJetPt.at(indexJet)); } + if (useMcC) { + jetHist.fill(HIST("eff/recmatched/mcClosure/hSelectedRecoEvents"), useTrainingHist ? 0.5 : 1.5); + } if (useLeadingJetDetLevelValue) { leadingJetWithPtEtaPhi[0] = mcdJetPt.at(indexJet); leadingJetWithPtEtaPhi[1] = mcdJetEta.at(indexJet); @@ -2541,6 +2746,48 @@ struct nucleiInJets { leadingJetWithPtEtaPhi[2] = mcpJetPhi.at(indexJet); } + // Fill detector-level DCA templates before the DCAxy selection used by the spectra. + // mcdJetPt is already background-subtracted when usebkgSubractionMC is enabled. + const double templateJetPt = mcdJetPt.at(indexJet); + if (isWithLeadingJet && backgroundRho > 0.f && templateJetPt > cfgjetPtBkgSubMin) { + const double templateJetEta = mcdJetEta.at(indexJet); + const double templateJetPhi = mcdJetPhi.at(indexJet); + const auto perpPhi = getPerpendicuarPhi(templateJetPhi); + for (const auto& track : tracks) { + const auto completeTrack = track.track_as(); + if (completeTrack.sign() >= 0 || completeTrack.pt() > cMaxPt || !isTrackSelectedWithoutDcaxy(completeTrack) || + (useRapidityCutForPID && !isRapiditySelectedForPID(completeTrack.rapidity(o2::constants::physics::MassProton)))) + continue; + if (std::abs(completeTrack.tpcNSigmaPr()) >= cfgnTPCPIDPr) + continue; + int origin = 4; + if (track.has_mcParticle()) { + const auto mcTrack = track.mcParticle_as(); + if (mcTrack.pdgCode() == PDG_t::kProtonBar) + origin = getParticleOriginType(mcTrack); + } + const bool tofPassed = completeTrack.hasTOF() && std::abs(completeTrack.tofNSigmaPr()) < cfgnTPCPIDPrTOF; + const double deltaEta = templateJetEta - completeTrack.eta(); + const double deltaPhi = TVector2::Phi_mpi_pi(templateJetPhi - completeTrack.phi()); + if (RecoDecay::sqrtSumOfSquares(deltaEta, deltaPhi) < cfgjetR) { + jetHist.fill(HIST("feeddown/antiProton/template/jetCone/TPC/mc"), completeTrack.pt(), completeTrack.dcaXY(), origin); + if (tofPassed) + jetHist.fill(HIST("feeddown/antiProton/template/jetCone/TPCTOF/mc"), completeTrack.pt(), completeTrack.dcaXY(), origin); + if (!completeTrack.hasTOF() || tofPassed) + jetHist.fill(HIST("feeddown/antiProton/template/jetCone/TPCTOFVeto/mc"), completeTrack.pt(), completeTrack.dcaXY(), origin); + } + const double deltaPhiPerp1 = TVector2::Phi_mpi_pi(perpPhi[0] - completeTrack.phi()); + const double deltaPhiPerp2 = TVector2::Phi_mpi_pi(perpPhi[1] - completeTrack.phi()); + if (RecoDecay::sqrtSumOfSquares(deltaEta, deltaPhiPerp1) < cfgjetR || RecoDecay::sqrtSumOfSquares(deltaEta, deltaPhiPerp2) < cfgjetR) { + jetHist.fill(HIST("feeddown/antiProton/template/perpCone/TPC/mc"), completeTrack.pt(), completeTrack.dcaXY(), origin); + if (tofPassed) + jetHist.fill(HIST("feeddown/antiProton/template/perpCone/TPCTOF/mc"), completeTrack.pt(), completeTrack.dcaXY(), origin); + if (!completeTrack.hasTOF() || tofPassed) + jetHist.fill(HIST("feeddown/antiProton/template/perpCone/TPCTOFVeto/mc"), completeTrack.pt(), completeTrack.dcaXY(), origin); + } + } + } + for (const auto& track : tracks) { auto completeTrack = track.track_as(); if (std::fabs(completeTrack.eta()) > cfgtrkMaxEta) @@ -2550,7 +2797,7 @@ struct nucleiInJets { if (!track.has_mcParticle()) continue; auto mcTrack = track.mcParticle_as(); - if (!isRapiditySelectedForPID(mcTrack.y())) + if (useRapidityCutForPID && !isRapiditySelectedForPID(mcTrack.y())) continue; bool isTof(completeTrack.hasTOF()); @@ -2654,7 +2901,7 @@ struct nucleiInJets { const bool isTPCTOFVetoPassed = isTpcPassed && (!isTof || isTofPidPassed); jetHist.fill(HIST("eff/recmatched/pt/PtParticleType"), mcTrack.pt(), jetFlag, particleType); if (useMcC) { - if (useDataLikeHist) + if (useTrainingHist) jetHist.fill(HIST("eff/recmatched/mcC/pt/PtParticleType"), track.pt(), mcTrack.pt(), particleType); else jetHist.fill(HIST("eff/recmatched/mcCSpectra/pt/PtParticleType"), track.pt(), mcTrack.pt(), particleType); @@ -2670,6 +2917,21 @@ struct nucleiInJets { if (jetFlag) { jetHist.fill(HIST("eff/recmatched/jetCone/ptEta/PtEtaParticleType"), mcTrack.pt(), mcTrack.eta(), particleType); + if (useMcC) { + if (useTrainingHist) { + jetHist.fill(HIST("eff/recmatched/mcClosure/training/jetCone/hRecAll"), track.pt(), mcTrack.pt(), particleType); + if (isTPCTOFVetoPassed) + jetHist.fill(HIST("eff/recmatched/mcClosure/training/jetCone/hRecTPCTOFVeto"), track.pt(), mcTrack.pt(), particleType); + if (isTPCTOFPassed) + jetHist.fill(HIST("eff/recmatched/mcClosure/training/jetCone/hRecTPCTOF"), track.pt(), mcTrack.pt(), particleType); + } else { + jetHist.fill(HIST("eff/recmatched/mcClosure/dataLike/jetCone/hRecAll"), track.pt(), mcTrack.pt(), particleType); + if (isTPCTOFVetoPassed) + jetHist.fill(HIST("eff/recmatched/mcClosure/dataLike/jetCone/hRecTPCTOFVeto"), track.pt(), mcTrack.pt(), particleType); + if (isTPCTOFPassed) + jetHist.fill(HIST("eff/recmatched/mcClosure/dataLike/jetCone/hRecTPCTOF"), track.pt(), mcTrack.pt(), particleType); + } + } if (isTpcPassed) jetHist.fill(HIST("eff/recmatched/jetCone/ptEta/PtEtaParticleTypeTPC"), mcTrack.pt(), mcTrack.eta(), particleType); if (isTof) @@ -2684,7 +2946,22 @@ struct nucleiInJets { jetHist.fill(HIST("eff/recmatched/perpCone/pt/PtParticleType"), mcTrack.pt(), particleType); jetHist.fill(HIST("eff/recmatched/perpCone/ptEta/PtEtaParticleType"), mcTrack.pt(), mcTrack.eta(), particleType); if (useMcC) { - if (useDataLikeHist) + if (useTrainingHist) { + jetHist.fill(HIST("eff/recmatched/mcClosure/training/perpCone/hRecAll"), track.pt(), mcTrack.pt(), particleType); + if (isTPCTOFVetoPassed) + jetHist.fill(HIST("eff/recmatched/mcClosure/training/perpCone/hRecTPCTOFVeto"), track.pt(), mcTrack.pt(), particleType); + if (isTPCTOFPassed) + jetHist.fill(HIST("eff/recmatched/mcClosure/training/perpCone/hRecTPCTOF"), track.pt(), mcTrack.pt(), particleType); + } else { + jetHist.fill(HIST("eff/recmatched/mcClosure/dataLike/perpCone/hRecAll"), track.pt(), mcTrack.pt(), particleType); + if (isTPCTOFVetoPassed) + jetHist.fill(HIST("eff/recmatched/mcClosure/dataLike/perpCone/hRecTPCTOFVeto"), track.pt(), mcTrack.pt(), particleType); + if (isTPCTOFPassed) + jetHist.fill(HIST("eff/recmatched/mcClosure/dataLike/perpCone/hRecTPCTOF"), track.pt(), mcTrack.pt(), particleType); + } + } + if (useMcC) { + if (useTrainingHist) jetHist.fill(HIST("eff/recmatched/perpCone/mcC/pt/PtParticleType"), track.pt(), mcTrack.pt(), particleType); else jetHist.fill(HIST("eff/recmatched/perpCone/mcCSpectra/pt/PtParticleType"), track.pt(), mcTrack.pt(), particleType); @@ -2715,7 +2992,7 @@ struct nucleiInJets { continue; if (std::fabs(mcParticle.eta()) > cfgtrkMaxEta) continue; - if (!isRapiditySelectedForPID(mcParticle.y())) + if (useRapidityCutForPID && !isRapiditySelectedForPID(mcParticle.y())) continue; bool jetFlagMC = false; bool jetFlagPerpConeMC = false; @@ -2750,9 +3027,15 @@ struct nucleiInJets { jetHist.fill(HIST("eff/recmatched/gen/pt/PtParticleType"), mcParticle.pt(), jetFlagMC, particleType); if (jetFlagMC) { jetHist.fill(HIST("eff/recmatched/gen/jetCone/ptEta/PtEtaParticleType"), mcParticle.pt(), mcParticle.eta(), particleType); + if (useMcC) { + if (useTrainingHist) + jetHist.fill(HIST("eff/recmatched/mcClosure/training/jetCone/hGen"), mcParticle.pt(), particleType); + else + jetHist.fill(HIST("eff/recmatched/mcClosure/dataLike/jetCone/hGen"), mcParticle.pt(), particleType); + } } if (useMcC) { - if (useDataLikeHist) { + if (useTrainingHist) { jetHist.fill(HIST("eff/recmatched/mcC/gen/pt/PtParticleType"), mcParticle.pt(), jetFlagMC, particleType); } else { jetHist.fill(HIST("eff/recmatched/mcCSpectra/gen/pt/PtParticleType"), mcParticle.pt(), jetFlagMC, particleType); @@ -2762,7 +3045,13 @@ struct nucleiInJets { jetHist.fill(HIST("eff/recmatched/gen/perpCone/pt/PtParticleType"), mcParticle.pt(), particleType); jetHist.fill(HIST("eff/recmatched/gen/perpCone/ptEta/PtEtaParticleType"), mcParticle.pt(), mcParticle.eta(), particleType); if (useMcC) { - if (useDataLikeHist) { + if (useTrainingHist) + jetHist.fill(HIST("eff/recmatched/mcClosure/training/perpCone/hGen"), mcParticle.pt(), particleType); + else + jetHist.fill(HIST("eff/recmatched/mcClosure/dataLike/perpCone/hGen"), mcParticle.pt(), particleType); + } + if (useMcC) { + if (useTrainingHist) { jetHist.fill(HIST("eff/recmatched/mcC/gen/perpCone/pt/PtParticleType"), mcParticle.pt(), particleType); } else { jetHist.fill(HIST("eff/recmatched/mcCSpectra/gen/perpCone/pt/PtParticleType"), mcParticle.pt(), particleType); @@ -2776,7 +3065,7 @@ struct nucleiInJets { int nprocessSimJEEvents = 0; void processJetSelectionCorrection(aod::JetMcCollision const& collision, soa::SmallGroups> const& recocolls, - JetMCDetTable const&, JetMCPartTable const& mcpjets, aod::JetParticles const& mcParticles) + JetMCDetTable const& mcdjets, JetMCPartTable const& mcpjets, aod::JetParticles const& mcParticles) { if (std::abs(collision.posZ()) > cfgMaxZVertex) { return; @@ -2805,61 +3094,80 @@ struct nucleiInJets { fillJetSelectionCorrectionParticles(mcParticles, genLeadingJetEta, genLeadingJetPhi); } - bool hasRecoSelectedLeadingJet = false; - double recoSelectedLeadingJetPt = -999.; - double recoSelectedMatchedGenJetPt = -999.; - double recoSelectedMatchedGenJetEta = -999.; - double recoSelectedMatchedGenJetPhi = -999.; - - for (const auto& mcpjet : mcpjets) { - if (!mcpjet.has_matchedJetGeo()) { + // Count each reconstructed collision, as in data. The generated selection + // above is independent: neither branch requires the other to pass the cut. + int nSelectedRecoCollisions = 0; + const double jetArea = M_PI * cfgjetR * cfgjetR; + for (const auto& recocoll : recocolls) { + if (!isRecoCollisionSelectedForJetSelectionCorrection(recocoll)) { continue; } - if (!isConeAxisAccepted(mcpjet.eta())) { + if (usebkgSubractionMC && !(recocoll.rho() > 0.)) { continue; } - for (const auto& mcdjet : mcpjet.template matchedJetGeo_as()) { - if (!isConeAxisAccepted(mcdjet.eta())) { - continue; - } - double selectedRecoCollisionRho = -1.; - bool hasSelectedRecoCollision = false; - for (const auto& recocoll : recocolls) { - if (mcdjet.collisionId() != recocoll.globalIndex()) { - continue; - } - if (!isRecoCollisionSelectedForJetSelectionCorrection(recocoll)) { - continue; - } - selectedRecoCollisionRho = recocoll.rho(); - hasSelectedRecoCollision = true; - break; - } - if (!hasSelectedRecoCollision) { - continue; - } - const double jetArea = M_PI * cfgjetR * cfgjetR; - const double mcdJetPtBkgSub = usebkgSubractionMC ? mcdjet.pt() - selectedRecoCollisionRho * jetArea : mcdjet.pt(); - if (mcdJetPtBkgSub <= cfgjetPtBkgSubMinMC) { + const auto jetsInCollision = mcdjets.sliceBy(detectorJetsPerCollision, recocoll.globalIndex()); + auto leadingJet = jetsInCollision.begin(); + bool hasLeadingJet = false; + double leadingJetPt = cfgjetPtBkgSubMinMC; + for (auto jet = jetsInCollision.begin(); jet != jetsInCollision.end(); ++jet) { + if (!isConeAxisAccepted(jet.eta())) { continue; } - if (mcdJetPtBkgSub > recoSelectedLeadingJetPt) { - hasRecoSelectedLeadingJet = true; - recoSelectedLeadingJetPt = mcdJetPtBkgSub; - recoSelectedMatchedGenJetPt = mcpjet.pt(); - recoSelectedMatchedGenJetEta = mcpjet.eta(); - recoSelectedMatchedGenJetPhi = mcpjet.phi(); + const double correctedPt = usebkgSubractionMC ? jet.pt() - recocoll.rho() * jetArea : jet.pt(); + if (correctedPt > leadingJetPt) { + leadingJet = jet; + leadingJetPt = correctedPt; + hasLeadingJet = true; } } - } + if (!hasLeadingJet) { + continue; + } - if (hasRecoSelectedLeadingJet) { + ++nSelectedRecoCollisions; jetHist.fill(HIST("jetSelCorr/eventNorm/hEventCounts"), 1.5); - jetHist.fill(HIST("jetSelCorr/recoSel/hLeadingJetPtBkgSub"), recoSelectedLeadingJetPt); - jetHist.fill(HIST("jetSelCorr/recoSel/hMatchedLeadingJetPt"), recoSelectedLeadingJetPt, recoSelectedMatchedGenJetPt); - fillJetSelectionCorrectionParticles(mcParticles, recoSelectedMatchedGenJetEta, recoSelectedMatchedGenJetPhi); + jetHist.fill(HIST("jetSelCorr/recoSel/hLeadingJetPtBkgSub"), leadingJetPt); + // All selected detector jets, including unmatched jets, use detector axes + // Never substitute a matched subleading jet or mix truth and detector axes + fillJetSelectionCorrectionParticles(mcParticles, leadingJet.eta(), leadingJet.phi()); + + // Preserve a separate matched-truth-axis sample for efficiency comparisons + // Resolve multiple geometrical matches by the smallest angular distance + bool hasTruthMatch = false; + double closestDistance = 1.e9; + double truthPt = 0.; + double truthEta = 0.; + double truthPhi = 0.; + if (leadingJet.has_matchedJetGeo()) { + for (const auto& truthJet : leadingJet.template matchedJetGeo_as()) { + if (truthJet.mcCollisionId() != collision.globalIndex()) { + continue; + } + const double distance = RecoDecay::sqrtSumOfSquares(leadingJet.eta() - truthJet.eta(), TVector2::Phi_mpi_pi(leadingJet.phi() - truthJet.phi())); + if (distance < closestDistance) { + closestDistance = distance; + hasTruthMatch = true; + truthPt = truthJet.pt(); + truthEta = truthJet.eta(); + truthPhi = truthJet.phi(); + } + } + } + if (!hasTruthMatch) { + jetHist.fill(HIST("jetSelCorr/recoSel/hMatchStatus"), 0.5); + continue; + } + jetHist.fill(HIST("jetSelCorr/recoSel/hMatchedLeadingJetPt"), leadingJetPt, truthPt); + if (!isConeAxisAccepted(truthEta)) { + jetHist.fill(HIST("jetSelCorr/recoSel/hMatchStatus"), 1.5); + continue; + } + jetHist.fill(HIST("jetSelCorr/recoSel/hMatchStatus"), 2.5); + jetHist.fill(HIST("jetSelCorr/matchedTruthAxis/hEventCount"), 0.5); + fillJetSelectionCorrectionParticles(mcParticles, truthEta, truthPhi); } + jetHist.fill(HIST("jetSelCorr/eventNorm/hSelectedRecoCollisions"), nSelectedRecoCollisions); } void processGenMatched(aod::JetMcCollision const& collision, @@ -2882,8 +3190,7 @@ struct nucleiInJets { return; jetHist.fill(HIST("genmatched/vertexZ"), collision.posZ()); - // Event-wise random splitting for closure test: decide once per event - bool useDataLikeHist = (randUniform.Uniform(0, 1) < 0.5); + const bool useTrainingHist = useTrainingHistForMcCollision(collision.globalIndex()); std::vector mcpJetPt{}; std::vector mcpJetPhi{}; @@ -2935,7 +3242,7 @@ struct nucleiInJets { jetHist.fill(HIST("genmatched/hRecMatchedJetEta"), leadingMCPJet.eta(), leadingMCPJet.eta() - mcdjet.eta()); if (useMcC) { - if (useDataLikeHist) { + if (useTrainingHist) { jetHist.fill(HIST("genmatched/hRecMatchedVsGenJetPt"), mcdjet.pt(), leadingMCPJet.pt()); } else { jetHist.fill(HIST("genmatched/mcC/hRecMatchedVsGenJetPt"), mcdjet.pt(), leadingMCPJet.pt()); @@ -3017,11 +3324,28 @@ struct nucleiInJets { if (!isTrackSelectedWithoutDcaxy(track)) { continue; } - if (!track.has_mcParticle()) - continue; if (std::fabs(track.eta()) > cfgtrkMaxEta) continue; + double nSigmaITSPr = 0.0; + double nSigmaITSDe = 0.0; + double nSigmaITSHe = 0.0; + if (itsPid.applyItsPidInc) { + nSigmaITSPr = o2::aod::ITSResponse::nSigmaITS(track); + nSigmaITSDe = o2::aod::ITSResponse::nSigmaITS(track); + nSigmaITSHe = o2::aod::ITSResponse::nSigmaITS(track); + } + const bool passItsPidPr = !itsPid.applyItsPidInc || track.pt() >= itsPid.ptMaxItsPidProt || (nSigmaITSPr >= itsPid.nSigmaItsMin && nSigmaITSPr <= itsPid.nSigmaItsMax); + const bool passItsPidDe = !itsPid.applyItsPidInc || track.pt() >= itsPid.ptMaxItsPidDeut || (nSigmaITSDe >= itsPid.nSigmaItsMin && nSigmaITSDe <= itsPid.nSigmaItsMax); + const bool passItsPidHe = !itsPid.applyItsPidInc || 2.0 * track.pt() >= itsPid.ptMaxItsPidHel || (nSigmaITSHe >= itsPid.nSigmaItsMin && nSigmaITSHe <= itsPid.nSigmaItsMax); + if (itsPid.applyItsPidInc) { + jetHist.fill(HIST("recInc/qa/h2ITSNsigmaProtonVsPt"), track.pt(), nSigmaITSPr); + jetHist.fill(HIST("recInc/qa/h2ITSNsigmaDeuteronVsPt"), track.pt(), nSigmaITSDe); + jetHist.fill(HIST("recInc/qa/h2ITSNsigmaHeliumVsPt"), 2.0 * track.pt(), nSigmaITSHe); + } + if (!track.has_mcParticle()) + continue; + auto mcTrack = track.mcParticle_as(); // require mc getProcess to get Decay and Material secondaries @@ -3045,7 +3369,7 @@ struct nucleiInJets { // Fill DCAxy histograms jetHist.fill(HIST("recInc/dcaxy/rec/tpcPtVsDcaxy3D"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality, track.dcaXY(), particleOriginType); - if (std::abs(track.tpcNSigmaPr()) < cfgnTPCPIDPr) + if (passItsPidPr && std::abs(track.tpcNSigmaPr()) < cfgnTPCPIDPr) jetHist.fill(HIST("recInc/dcaxy/rec/tpcPtVsDcaxy3DPIDselected"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality, track.dcaXY(), particleOriginType); if (!isMcPrimary) @@ -3057,16 +3381,36 @@ struct nucleiInJets { if (std::fabs(track.dcaXY()) > dcaXYPtDepCut(track.pt()) && useDcaxyPtDepCut) continue; + if (isPOCollision && isMcPrimary) { + if (mcTrack.pdgCode() == PDG_t::kProton) { + const double yLab = mcTrack.y(); + jetHist.fill(HIST("recInc/qa/rapidity/protonYLabVsPtEta"), mcTrack.pt(), mcTrack.eta(), yLab); + jetHist.fill(HIST("recInc/qa/rapidity/protonYCmsVsPtEta"), mcTrack.pt(), mcTrack.eta(), rapidityInSelectedFrame(yLab)); + } else if (mcTrack.pdgCode() == PDG_t::kProtonBar) { + const double yLab = mcTrack.y(); + jetHist.fill(HIST("recInc/qa/rapidity/antiprotonYLabVsPtEta"), mcTrack.pt(), mcTrack.eta(), yLab); + jetHist.fill(HIST("recInc/qa/rapidity/antiprotonYCmsVsPtEta"), mcTrack.pt(), mcTrack.eta(), rapidityInSelectedFrame(yLab)); + } else if (mcTrack.pdgCode() == Pdg::kDeuteron) { + const double yLab = mcTrack.y(); + jetHist.fill(HIST("recInc/qa/rapidity/deuteronYLabVsPtEta"), mcTrack.pt(), mcTrack.eta(), yLab); + jetHist.fill(HIST("recInc/qa/rapidity/deuteronYCmsVsPtEta"), mcTrack.pt(), mcTrack.eta(), rapidityInSelectedFrame(yLab)); + } else if (mcTrack.pdgCode() == -Pdg::kDeuteron) { + const double yLab = mcTrack.y(); + jetHist.fill(HIST("recInc/qa/rapidity/antideuteronYLabVsPtEta"), mcTrack.pt(), mcTrack.eta(), yLab); + jetHist.fill(HIST("recInc/qa/rapidity/antideuteronYCmsVsPtEta"), mcTrack.pt(), mcTrack.eta(), rapidityInSelectedFrame(yLab)); + } + } + // auto mass = TDatabasePDG::Instance()->GetParticle(abs(mcTrack.pdgCode()))->Mass(); // auto rapidity = RecoDecay::y(std::array{track.px(), track.py(), track.pz()}, mass); if (!isRapiditySelectedForPID(mcTrack.y())) continue; // Proton - if (std::abs(mcTrack.pdgCode()) == 2212) { // Proton + if (std::abs(mcTrack.pdgCode()) == PDG_t::kProton) { // Proton jetHist.fill(HIST("recInc/eff/tpcTrack3D"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); if (track.hasTOF()) jetHist.fill(HIST("recInc/eff/tpcTofTrack3D"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); - if (std::abs(track.tpcNSigmaPr()) < cfgnTPCPIDPr) { + if (passItsPidPr && std::abs(track.tpcNSigmaPr()) < cfgnTPCPIDPr) { jetHist.fill(HIST("recInc/pt/PtParticleTypeTPC"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); } } @@ -3075,7 +3419,7 @@ struct nucleiInJets { jetHist.fill(HIST("recInc/eff/tpcTrack3D"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); if (track.hasTOF()) jetHist.fill(HIST("recInc/eff/tpcTofTrack3D"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); - if (std::abs(track.tpcNSigmaDe()) < cfgnTPCPIDDe) { + if (passItsPidDe && std::abs(track.tpcNSigmaDe()) < cfgnTPCPIDDe) { jetHist.fill(HIST("recInc/pt/PtParticleTypeTPC"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); } } @@ -3084,49 +3428,49 @@ struct nucleiInJets { jetHist.fill(HIST("recInc/eff/tpcTrack3D"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); if (track.hasTOF()) jetHist.fill(HIST("recInc/eff/tpcTofTrack3D"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); - if (std::abs(track.tpcNSigmaHe()) < cfgnTPCPIDHe) { + if (passItsPidHe && std::abs(track.tpcNSigmaHe()) < cfgnTPCPIDHe) { jetHist.fill(HIST("recInc/pt/PtParticleTypeTPC"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); } } // TPCTOF and TPCTOFVeto histograms // Proton - if (std::abs(track.tpcNSigmaPr()) < cfgnTPCPIDPr && track.hasTOF()) { - if (std::abs(mcTrack.pdgCode()) == 2212) { + if (passItsPidPr && std::abs(track.tpcNSigmaPr()) < cfgnTPCPIDPr && track.hasTOF()) { + if (std::abs(mcTrack.pdgCode()) == PDG_t::kProton) { jetHist.fill(HIST("recInc/pt/PtParticleTypeTPCTOF"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); if (std::abs(track.tofNSigmaPr()) < cfgnTPCPIDPrTOF) jetHist.fill(HIST("recInc/pt/PtParticleTypeTPCTOFVeto"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); } } else { - if (std::abs(track.tpcNSigmaPr()) < cfgnTPCPIDPr) { - if (std::abs(mcTrack.pdgCode()) == 2212) { + if (passItsPidPr && std::abs(track.tpcNSigmaPr()) < cfgnTPCPIDPr) { + if (std::abs(mcTrack.pdgCode()) == PDG_t::kProton) { jetHist.fill(HIST("recInc/pt/PtParticleTypeTPCTOFVeto"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); } } } // Deuteron - if (std::abs(track.tpcNSigmaDe()) < cfgnTPCPIDDe && track.hasTOF()) { + if (passItsPidDe && std::abs(track.tpcNSigmaDe()) < cfgnTPCPIDDe && track.hasTOF()) { if (std::abs(mcTrack.pdgCode()) == Pdg::kDeuteron) { jetHist.fill(HIST("recInc/pt/PtParticleTypeTPCTOF"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); if (std::abs(track.tofNSigmaDe()) < cfgnTPCPIDDeTOF) jetHist.fill(HIST("recInc/pt/PtParticleTypeTPCTOFVeto"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); } } else { - if (std::abs(track.tpcNSigmaDe()) < cfgnTPCPIDDe) { + if (passItsPidDe && std::abs(track.tpcNSigmaDe()) < cfgnTPCPIDDe) { if (std::abs(mcTrack.pdgCode()) == Pdg::kDeuteron) { jetHist.fill(HIST("recInc/pt/PtParticleTypeTPCTOFVeto"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); } } } // Helium - if (std::abs(track.tpcNSigmaHe()) < cfgnTPCPIDHe && track.hasTOF()) { + if (passItsPidHe && std::abs(track.tpcNSigmaHe()) < cfgnTPCPIDHe && track.hasTOF()) { if (std::abs(mcTrack.pdgCode()) == Pdg::kHelium3) { jetHist.fill(HIST("recInc/pt/PtParticleTypeTPCTOF"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); if (std::abs(track.tofNSigmaHe()) < cfgnTPCPIDHeTOF) jetHist.fill(HIST("recInc/pt/PtParticleTypeTPCTOFVeto"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); } } else { - if (std::abs(track.tpcNSigmaHe()) < cfgnTPCPIDHe) { + if (passItsPidHe && std::abs(track.tpcNSigmaHe()) < cfgnTPCPIDHe) { if (std::abs(mcTrack.pdgCode()) == Pdg::kHelium3) { jetHist.fill(HIST("recInc/pt/PtParticleTypeTPCTOFVeto"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); } diff --git a/PWGJE/Tasks/photonChargedTriggerCorrelation.cxx b/PWGJE/Tasks/photonChargedTriggerCorrelation.cxx index 9ab84d0638e..943bc742517 100644 --- a/PWGJE/Tasks/photonChargedTriggerCorrelation.cxx +++ b/PWGJE/Tasks/photonChargedTriggerCorrelation.cxx @@ -222,7 +222,7 @@ struct PhotonChargedTriggerCorrelation { resultVec.insert(resultVec.end(), vec.begin(), vec.end()); return resultVec; }; - BinningZPvMult binningZPvMult{{prependValueToVector(binsZPvBinning.value, VARIABLE_WIDTH), prependValueToVector(binsMultBinning.value, VARIABLE_WIDTH)}, true}; + BinningZPvMult binningZPvMult{{prependValueToVector(binsZPvBinning.value, VARIABLE_WIDTH), prependValueToVector(binsMultBinning.value, VARIABLE_WIDTH)}}; // declare analysis variables diff --git a/PWGJE/Tasks/photonChargedTriggerProducer.cxx b/PWGJE/Tasks/photonChargedTriggerProducer.cxx index 0fdaa6ca5b7..9e16a69e1cc 100644 --- a/PWGJE/Tasks/photonChargedTriggerProducer.cxx +++ b/PWGJE/Tasks/photonChargedTriggerProducer.cxx @@ -207,8 +207,8 @@ struct PhotonChargedTriggerProducer { void init(InitContext const&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); // histograms diff --git a/PWGJE/Tasks/photonIsolationQA.cxx b/PWGJE/Tasks/photonIsolationQA.cxx index d14e523d91e..fe822de10d1 100644 --- a/PWGJE/Tasks/photonIsolationQA.cxx +++ b/PWGJE/Tasks/photonIsolationQA.cxx @@ -104,8 +104,8 @@ struct PhotonIsolationQA { int trackSelection = -1; void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); mGeometry = o2::emcal::Geometry::GetInstanceFromRunNumber(300000); auto nCells = mGeometry->GetNCells(); diff --git a/PWGJE/Tasks/statPromptPhoton.cxx b/PWGJE/Tasks/statPromptPhoton.cxx index a09df380079..01aea57b2ac 100644 --- a/PWGJE/Tasks/statPromptPhoton.cxx +++ b/PWGJE/Tasks/statPromptPhoton.cxx @@ -1552,7 +1552,7 @@ struct statPromptPhoton { while (chase) { if (visited.count(chaseindex)) { - chase = false; + // chase = false; // FIXME: unused break; } visited.insert(chaseindex); @@ -1677,7 +1677,7 @@ struct statPromptPhoton { while (chase) { if (visited.count(chaseindex)) { - chase = false; + // chase = false; // FIXME: unused break; } visited.insert(chaseindex); diff --git a/PWGJE/Tasks/substructureDebug.cxx b/PWGJE/Tasks/substructureDebug.cxx index 0243cce3c1a..b5943283e73 100644 --- a/PWGJE/Tasks/substructureDebug.cxx +++ b/PWGJE/Tasks/substructureDebug.cxx @@ -84,8 +84,8 @@ struct SubstructureDebugTask { void init(o2::framework::InitContext&) { - eventSelection = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelection = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); } void processDataCharged(aod::CJetCOs::iterator const& collision, soa::Join const& jets) diff --git a/PWGJE/Tasks/trackEfficiency.cxx b/PWGJE/Tasks/trackEfficiency.cxx index 779f6df4805..05de663ba17 100644 --- a/PWGJE/Tasks/trackEfficiency.cxx +++ b/PWGJE/Tasks/trackEfficiency.cxx @@ -38,6 +38,7 @@ #include #include +#include #include #include @@ -95,6 +96,32 @@ struct TrackEfficiency { Configurable useTrueTrackWeight{"useTrueTrackWeight", true, "test configurable, should be set to 1 then config removed once well tested"}; + // Normalization FT0 by means + struct FT0A : ConfigurableGroup { + std::string prefix = "ft0a"; + Configurable mean{"mean", 3499., "Mean FT0A signal"}; + Configurable meanMCDetLevel{"meanMCDetLevel", 2640., "Mean FT0A signal at MC detector level"}; + } ft0a; + + struct FT0C : ConfigurableGroup { + std::string prefix = "ft0c"; + Configurable mean{"mean", 844.6, "Mean FT0C signal"}; + Configurable meanMCDetLevel{"meanMCDetLevel", 802.9, "Mean FT0C signal at MC detector level"}; + } ft0c; + + // FT0C event activity thresholds + struct Hist : ConfigurableGroup { + std::string prefix = "hist"; + + // Raw data + ConfigurableAxis multFT0CThresh{"multFT0CThresh", {VARIABLE_WIDTH, 0.0, 0.109421, 0.215022, 0.343214, 0.50515, 0.712277, 0.978245, 1.3189, 1.75744, 2.36001, 3.59696, 25.}, "Percentiles of scaled FT0C: 100%, 90%, 80%, 70%, 60%, 50%, 40%, 30%, 20%, 10%, 1%"}; + ConfigurableAxis multFT0MThresh{"multFT0MThresh", {VARIABLE_WIDTH, 0.0, 0.130987, 0.237916, 0.36664, 0.528441, 0.734705, 0.996611, 1.32785, 1.74896, 2.31024, 3.37852, 25.}, "Percentiles of scaled FT0M: 100%, 90%, 80%, 70%, 60%, 50%, 40%, 30%, 20%, 10%, 1%"}; + + // MC detector-level data + ConfigurableAxis multFT0CThreshMCDetLevel{"multFT0CThreshMCDetLevel", {VARIABLE_WIDTH, 0.0, 0.124992, 0.243769, 0.380291, 0.543998, 0.748078, 1.00424, 1.3237, 1.72648, 2.27373, 3.43144, 25.}, "Percentiles of scaled FT0C: 100%, 90%, 80%, 70%, 60%, 50%, 40%, 30%, 20%, 10%, 1%"}; + ConfigurableAxis multFT0MThreshMCDetLevel{"multFT0MThreshMCDetLevel", {VARIABLE_WIDTH, 0.0, 0.142622, 0.262095, 0.397069, 0.558622, 0.762008, 1.01685, 1.33219, 1.72377, 2.24008, 3.26309, 25.}, "Percentiles of scaled FT0M: 100%, 90%, 80%, 70%, 60%, 50%, 40%, 30%, 20%, 10%, 1%"}; + } hist; + // systematics variation - Run 2 guidelines: https://twiki.cern.ch/twiki/bin/view/ALICE/AliDPGtoolsTrackSystematicUncertainty TrackSelection customTrackSelection; Configurable useCustomTrackSelection{"useCustomTrackSelection", false, "whether to use the custom cuts (used for cut variation for tracking efficiency systematics)"}; @@ -117,6 +144,11 @@ struct TrackEfficiency { SplitOkCheckFirstAssocCollOnly // 2 }; + enum class TrackSample { + Data, + MCDetLevel + }; + template bool isAcceptedTrack(TJetTrack const& jetTrack) { @@ -147,19 +179,20 @@ struct TrackEfficiency { return std::abs(charge) >= chargeUnit; } - template + template void fillTrackHistograms(TCollision const& collision, TJetTracks const& jetTracks, float weight = 1.0) { + const float meanFT0A = sample == TrackSample::Data ? ft0a.mean.value : ft0a.meanMCDetLevel.value; + const float meanFT0C = sample == TrackSample::Data ? ft0c.mean.value : ft0c.meanMCDetLevel.value; + const auto ft0Metrics = getFT0Metrics(collision, meanFT0A, meanFT0C); + const auto scaledFT0C = ft0Metrics.scaledFT0C; + const auto scaledFT0M = ft0Metrics.scaledFT0M; + for (auto const& track : jetTracks) { if (!isAcceptedTrack(track)) { continue; } - float pTHat = simPtRef / (std::pow(weight, 1.0 / pTHatExponent)); - if (track.pt() > pTHatMaxFractionMCD * pTHat) { - continue; - } - float centrality = checkCentFT0M ? collision.centFT0M() : collision.centFT0C(); registry.fill(HIST("h2_centrality_track_pt"), centrality, track.pt(), weight); @@ -172,6 +205,14 @@ struct TrackEfficiency { registry.fill(HIST("h2_track_pt_high_track_sigmapt"), track.pt(), track.sigma1Pt() * track.pt(), weight); registry.fill(HIST("h3_intrate_centrality_track_pt"), collision.hadronicRate(), centrality, track.pt(), weight); registry.fill(HIST("h3_track_pt_track_eta_track_phi"), track.pt(), track.eta(), track.phi(), weight); + + if constexpr (sample == TrackSample::Data) { + registry.fill(HIST("hScaledFT0CTrackPt"), scaledFT0C, track.pt(), weight); + registry.fill(HIST("hScaledFT0MTrackPt"), scaledFT0M, track.pt(), weight); + } else { + registry.fill(HIST("hScaledFT0CTrackPt_MCDetLevel"), scaledFT0C, track.pt(), weight); + registry.fill(HIST("hScaledFT0MTrackPt_MCDetLevel"), scaledFT0M, track.pt(), weight); + } } } @@ -205,8 +246,14 @@ struct TrackEfficiency { LOGP(fatal, "Configurable acceptSplitCollisions has wrong input value; stopping workflow"); } - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + constexpr float minFT0Mean = std::numeric_limits::epsilon(); + if (std::fabs(ft0a.mean.value) <= minFT0Mean || std::fabs(ft0c.mean.value) <= minFT0Mean || + std::fabs(ft0a.meanMCDetLevel.value) <= minFT0Mean || std::fabs(ft0c.meanMCDetLevel.value) <= minFT0Mean) { + LOGP(fatal, "FT0A and FT0C means must have magnitude greater than {} to avoid near-zero denominators; stopping workflow", minFT0Mean); + } + + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); if (useCustomTrackSelection) { // Custom track cuts @@ -254,6 +301,10 @@ struct TrackEfficiency { AxisSpec etaAxisEff{etaEffBinning, "#eta"}; AxisSpec phiAxisEff{phiEffBinning, "#phi"}; AxisSpec centAxis = {centralityBinning, "centrality (%)"}; + AxisSpec scaledFT0C{hist.multFT0CThresh, "FT0C / #LT FT0C #GT"}; + AxisSpec scaledFT0M{hist.multFT0MThresh, "FT0M^{*}"}; + AxisSpec scaledFT0CMCDetLevel{hist.multFT0CThreshMCDetLevel, "FT0C / #LT FT0C #GT"}; + AxisSpec scaledFT0MMCDetLevel{hist.multFT0MThreshMCDetLevel, "FT0M^{*}"}; AxisSpec intRateAxis = {intRateBinning, "int. rate (kHz)"}; AxisSpec occupancyAxis = {140, -0.5, 13999.5, "occupancy"}; AxisSpec nTracksAxis = {16001, -1., 16000, "n tracks"}; @@ -346,6 +397,12 @@ struct TrackEfficiency { registry.add("h2_track_pt_high_track_sigma1overpt", "#sigma(1/#it{p}_{T}); #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH2F, {{90, 10., 100.}, {10000, 0.0, 1.0}}}); registry.add("h3_intrate_centrality_track_pt", "interaction rate vs centrality vs track pT; int. rate; centrality; #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH3F, {intRateAxis, centAxis, {200, 0., 200.}}}); registry.add("h3_track_pt_track_eta_track_phi", "track pT vs track #eta vs track #varphi; #it{p}_{T,track} (GeV/#it{c}); #eta_{track}; #varphi_{track}", {HistType::kTH3F, {{200, 0., 200.}, {100, -1.0, 1.0}, {160, -1.0, 7.}}}); + + // EA analysis + registry.add("hScaledFT0CTrackPt", "scaled FT0C vs track pT; FT0C / #LT FT0C #GT; #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH2F, {scaledFT0C, {200, 0., 200.}}}); + registry.add("hScaledFT0MTrackPt", "scaled FT0M vs track pT; FT0M^{*}; #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH2F, {scaledFT0M, {200, 0., 200.}}}); + registry.add("hScaledFT0CTrackPt_MCDetLevel", "MC detector-level scaled FT0C vs track pT; FT0C / #LT FT0C #GT; #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH2F, {scaledFT0CMCDetLevel, {200, 0., 200.}}}); + registry.add("hScaledFT0MTrackPt_MCDetLevel", "MC detector-level scaled FT0M vs track pT; FT0M^{*}; #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH2F, {scaledFT0MMCDetLevel, {200, 0., 200.}}}); } if (doprocessParticles || doprocessParticlesWeighted) { @@ -853,7 +910,7 @@ struct TrackEfficiency { return; } - fillTrackHistograms(collision, jetTracks); + fillTrackHistograms(collision, jetTracks); } PROCESS_SWITCH(TrackEfficiency, processTracksFromData, "QA for charged tracks in data", false); @@ -881,11 +938,11 @@ struct TrackEfficiency { return; } - fillTrackHistograms(collision, jetTracks); + fillTrackHistograms(collision, jetTracks); } PROCESS_SWITCH(TrackEfficiency, processTracksFromMc, "QA for charged tracks in MC without weights", false); - void processTracksFromMcWeighted(soa::Filtered>::iterator const& collision, + void processTracksFromMcWeighted(soa::Filtered>::iterator const& collision, aod::JetMcCollisions const&, soa::Filtered> const& jetTracks, soa::Join const&) @@ -893,10 +950,14 @@ struct TrackEfficiency { if (!collision.has_mcCollision()) { // the collision is fake and has no associated mc coll; skip as .mccollision() cannot be called return; } - float eventWeight = collision.mcCollision().weight(); if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { return; } + + if (collision.isOutlier()) { + return; + } + float centrality = checkCentFT0M ? collision.centFT0M() : collision.centFT0C(); if (cutCentrality && (centrality < centralityMin || centralityMax < centrality)) { return; @@ -905,12 +966,13 @@ struct TrackEfficiency { return; } + float eventWeight = collision.mcCollision().weight(); float pTHat = collision.mcCollision().ptHard() < pTHatSettingSentinelValue ? collision.mcCollision().ptHard() : simPtRef / (std::pow(eventWeight, 1.0 / pTHatExponent)); if (pTHat < ptHatMin || pTHat > ptHatMax) { // only allows mcCollisions with weight in between min and max return; } - fillTrackHistograms(collision, jetTracks, eventWeight); + fillTrackHistograms(collision, jetTracks, eventWeight); } PROCESS_SWITCH(TrackEfficiency, processTracksFromMcWeighted, "QA for charged tracks in weighted MC", false); @@ -1711,6 +1773,44 @@ struct TrackEfficiency { } } PROCESS_SWITCH(TrackEfficiency, processQcCheck, "Histograms for QC checks", false); + + // FT0 event activity helpers + struct FT0Metrics { + float multFT0A = 0.f; + float multFT0C = 0.f; + float multFT0M = 0.f; + float scaledFT0A = 0.f; + float scaledFT0C = 0.f; + float scaledFT0M = 0.f; + }; + + float getScaledFT0(const float& multFT0, const float& meanFT0) + { + return multFT0 / meanFT0; + } + + float getScaledFT0M(const float& scaledMultFT0A, const float& scaledMultFT0C) + { + return 0.5 * (scaledMultFT0A + scaledMultFT0C); + } + + FT0Metrics makeFT0Metrics(float multFT0A, float multFT0C, float meanFT0A, float meanFT0C) + { + FT0Metrics values; + values.multFT0A = multFT0A; + values.multFT0C = multFT0C; + values.multFT0M = multFT0A + multFT0C; + values.scaledFT0A = getScaledFT0(values.multFT0A, meanFT0A); + values.scaledFT0C = getScaledFT0(values.multFT0C, meanFT0C); + values.scaledFT0M = getScaledFT0M(values.scaledFT0A, values.scaledFT0C); + return values; + } + + template + FT0Metrics getFT0Metrics(const Collision& collision, float meanFT0A, float meanFT0C) + { + return makeFT0Metrics(collision.multFT0A(), collision.multFT0C(), meanFT0A, meanFT0C); + } }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGJE/Tasks/triggerCorrelations.cxx b/PWGJE/Tasks/triggerCorrelations.cxx index 2a32add9252..ba66fb5abac 100644 --- a/PWGJE/Tasks/triggerCorrelations.cxx +++ b/PWGJE/Tasks/triggerCorrelations.cxx @@ -26,6 +26,7 @@ #include +#include #include #include @@ -38,9 +39,9 @@ struct TriggerCorrelationsTask { HistogramRegistry registry; std::vector triggerMaskBits; - long unsigned int nChargedTriggers = 4; - long unsigned int nChargedHFTriggers = 4; - long unsigned int nFullTriggers = 13; + uint64_t nChargedTriggers = 4; + uint64_t nChargedHFTriggers = 4; + uint64_t nFullTriggers = 13; void init(o2::framework::InitContext&) { triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(jetderiveddatautilities::JTriggerMasks); diff --git a/PWGJE/Tasks/v0QA.cxx b/PWGJE/Tasks/v0QA.cxx index e32d56bc590..b448ce6b7c2 100644 --- a/PWGJE/Tasks/v0QA.cxx +++ b/PWGJE/Tasks/v0QA.cxx @@ -1601,7 +1601,7 @@ struct V0QA { if (!v0.has_mcParticle()) continue; - int pdg = v0.mcParticle().pdgCode(); + // int pdg = v0.mcParticle().pdgCode(); // FIXME: unused // Check V0 decay kinematics if (v0.isRejectedCandidate()) @@ -1612,7 +1612,7 @@ struct V0QA { auto pv0 = v0.mcParticle(); auto mother = v0.mcMotherParticle(); - pdg = mother.pdgCode(); + auto pdg = mother.pdgCode(); if (pdg == PDG_t::kXiMinus) { registry.fill(HIST("feeddown/XiMinusPtYLambdaPt"), mother.pt(), mother.y(), pv0.pt(), weight); @@ -1638,7 +1638,7 @@ struct V0QA { if (!v0.has_mcParticle()) continue; - int pdg = v0.mcParticle().pdgCode(); + // int pdg = v0.mcParticle().pdgCode(); // FIXME: unused // Check V0 decay kinematics if (v0.isRejectedCandidate()) @@ -1649,7 +1649,7 @@ struct V0QA { auto pv0 = v0.mcParticle(); auto mother = v0.mcMotherParticle(); - pdg = mother.pdgCode(); + auto pdg = mother.pdgCode(); if (pdg == PDG_t::kXiMinus) { registry.fill(HIST("feeddown/JetPtXiMinusPtLambdaPt"), mcdjet.pt(), mother.pt(), pv0.pt(), weight); @@ -1683,14 +1683,14 @@ struct V0QA { if (!v0sAreMatched(v0, pv0, jTracks)) continue; - int pdg = v0.mcParticle().pdgCode(); + // int pdg = v0.mcParticle().pdgCode(); // FIXME: unused // Check V0 decay kinematics if (v0.isRejectedCandidate()) continue; auto mother = v0.mcMotherParticle(); - pdg = mother.pdgCode(); + auto pdg = mother.pdgCode(); if (pdg == PDG_t::kXiMinus) { registry.fill(HIST("feeddown/JetsPtXiMinusPtLambdaPt"), mcpjet.pt(), mcdjet.pt(), mother.pt(), pv0.pt(), weight); } diff --git a/PWGLF/CMakeLists.txt b/PWGLF/CMakeLists.txt index 797ac01500f..2aa66ef9fb4 100644 --- a/PWGLF/CMakeLists.txt +++ b/PWGLF/CMakeLists.txt @@ -11,6 +11,7 @@ # add_subdirectory(Core) # add_subdirectory(DataModel) +# Utils first: it owns the LfStrangenessPCH carrier the workflows reuse. +add_subdirectory(Utils) add_subdirectory(Tasks) add_subdirectory(TableProducer) -add_subdirectory(Utils) diff --git a/PWGLF/Core/K1AnalysisMicroCore.h b/PWGLF/Core/K1AnalysisMicroCore.h new file mode 100644 index 00000000000..3b024afbf68 --- /dev/null +++ b/PWGLF/Core/K1AnalysisMicroCore.h @@ -0,0 +1,796 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file K1AnalysisMicroCore.h +/// \brief Shared K1(1270) selection, truth classification and candidate enumeration of the K1 resonance tasks +/// \author Su-Jeong Ji , Bong-Hwi Lim +/// +/// The core owns the selection and its cut-flow instrumentation (CutFlow/*, ML/*). The tasks own their +/// output histograms and fill them from the pair and candidate hooks of forEachCandidate(). + +#ifndef PWGLF_CORE_K1ANALYSISMICROCORE_H_ +#define PWGLF_CORE_K1ANALYSISMICROCORE_H_ + +#include "PWGLF/Core/K1MlFeatures.h" +#include "PWGLF/Core/ResoAnalysisSelectionCore.h" + +#include +#include +#include +#include +#include +#include + +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace o2::analysis::k1micro +{ + +using o2::analysis::resonance::EventCuts; +using o2::analysis::resonance::isCutEnabled; +using o2::analysis::resonance::isInRange; +using o2::analysis::resonance::isInWindow; +using o2::analysis::resonance::PIDCutConfig; +using o2::analysis::resonance::ResoAnalysisSelectionCore; +using o2::analysis::resonance::TrackCuts; +using o2::analysis::resonance::TrackStage; + +enum class K1TruthChannel { + None = 0, + RhoK = 1, + KStarPi = 2 +}; + +struct K1ParentTruth { + double pt = std::numeric_limits::quiet_NaN(); + double y = std::numeric_limits::quiet_NaN(); + int pdgCode = 0; + K1TruthChannel channel = K1TruthChannel::None; +}; +using K1ParentTruthMap = std::unordered_map; + +inline K1TruthChannel classifyGeneratedK1(int charge, int daughter1, int daughter2); + +// The value is the species index of the PID configuration in ResoAnalysisSelectionCore. +enum class Species : int { + Pion = 0, + Kaon = 1 +}; + +// Cumulative selection bits of an unlike-sign candidate handed to the export hook. +enum CandidatePassBit : uint16_t { + kPassLoose = 1, // valid canonical candidate inside the K1 rapidity window + kPassQuality = 2, // track quality of all three tracks + kPassPID = 4, // TOF requirement and PID of all three tracks + kPassPair = 8, // pion-pair pT and secondary mass window + kPassCandidate = 16 // candidate cuts +}; +inline constexpr uint16_t PassBitsSelected = kPassLoose | kPassQuality | kPassPID | kPassPair | kPassCandidate; + +inline constexpr double MassRho770 = 0.77526; // PDG 2024, not available in o2::constants::physics +inline constexpr int NCandidateStages = 12; +inline constexpr int NTruthChannels = 3; + +// Configurable groups without prefix: the JSON keys are the plain configurable names. + +/// Pion PID selection +struct PionPidCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cMaxTPCnSigmaPion{"cMaxTPCnSigmaPion", 3.0, "TPC nSigma cut for Pion (-999: off)"}; // TPC + o2::framework::Configurable cMaxTOFnSigmaPion{"cMaxTOFnSigmaPion", 3.0, "TOF nSigma cut for Pion (-999: off)"}; // TOF + o2::framework::Configurable nsigmaCutCombinedPion{"nsigmaCutCombinedPion", -999, "Combined nSigma cut for Pion"}; // Combined + o2::framework::Configurable cUseOnlyTOFTrackPi{"cUseOnlyTOFTrackPi", false, "Use only TOF track for PID selection"}; // Use only TOF track for Pion PID selection + o2::framework::Configurable cPionUsePtDepPID{"cPionUsePtDepPID", false, "Use pT-dependent PID cuts for pion"}; + o2::framework::Configurable> cPionPIDPtBins{"cPionPIDPtBins", {0.0f, 0.5f, 0.8f, 2.0f, 999.0f}, "pT bin edges for pion PID cuts"}; + o2::framework::Configurable> cPionTPCNSigmaCuts{"cPionTPCNSigmaCuts", {3.0f, 3.0f, 2.0f, 2.0f}, "TPC NSigma cuts per pT bin (pion)"}; + o2::framework::Configurable> cPionTOFNSigmaCuts{"cPionTOFNSigmaCuts", {3.0f, 3.0f, 3.0f, 3.0f}, "TOF NSigma cuts per pT bin (pion)"}; + o2::framework::Configurable> cPionTOFRequired{"cPionTOFRequired", {0, 0, 1, 1}, "Require TOF per pT bin (pion)"}; +}; + +/// Kaon PID selection +struct KaonPidCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cMaxTPCnSigmaKaon{"cMaxTPCnSigmaKaon", 3.0, "TPC nSigma cut for Kaon (-999: off)"}; // TPC + o2::framework::Configurable cMaxTOFnSigmaKaon{"cMaxTOFnSigmaKaon", 3.0, "TOF nSigma cut for Kaon (-999: off)"}; // TOF + o2::framework::Configurable nsigmaCutCombinedKaon{"nsigmaCutCombinedKaon", -999, "Combined nSigma cut for Kaon"}; // Combined + o2::framework::Configurable cUseOnlyTOFTrackKa{"cUseOnlyTOFTrackKa", false, "Use only TOF track for PID selection"}; // Use only TOF track for Kaon PID selection + o2::framework::Configurable cKaonUsePtDepPID{"cKaonUsePtDepPID", false, "Use pT-dependent PID cuts for kaon"}; + o2::framework::Configurable> cKaonPIDPtBins{"cKaonPIDPtBins", {0.0f, 0.5f, 0.8f, 2.0f, 999.0f}, "pT bin edges for kaon PID cuts"}; + o2::framework::Configurable> cKaonTPCNSigmaCuts{"cKaonTPCNSigmaCuts", {3.0f, 3.0f, 2.0f, 2.0f}, "TPC NSigma cuts per pT bin (kaon)"}; + o2::framework::Configurable> cKaonTOFNSigmaCuts{"cKaonTOFNSigmaCuts", {3.0f, 3.0f, 3.0f, 3.0f}, "TOF NSigma cuts per pT bin (kaon)"}; + o2::framework::Configurable> cKaonTOFRequired{"cKaonTOFRequired", {0, 0, 1, 1}, "Require TOF per pT bin (kaon)"}; +}; + +/// Secondary selection (-999 switches a cut off; the values it needs are then not computed) +struct SecondaryCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cMinSecondaryPtCut{"cMinSecondaryPtCut", 0.5, "Min pT cut for secondary selection"}; + o2::framework::Configurable cfgModeK892orRho{"cfgModeK892orRho", false, "Secondary scenario for K892 (true) or Rho (false)"}; + o2::framework::Configurable cSecondaryMasswindow{"cSecondaryMasswindow", -999, "Secondary inv mass selection window"}; + o2::framework::Configurable cMinAnotherSecondaryMassCut{"cMinAnotherSecondaryMassCut", -999, "Min inv. mass selection of another secondary scenario"}; + o2::framework::Configurable cMaxAnotherSecondaryMassCut{"cMaxAnotherSecondaryMassCut", -999, "MAx inv. mass selection of another secondary scenario"}; + o2::framework::Configurable cMinPiKaMassCut{"cMinPiKaMassCut", -999, "bPion-Kaon pair inv mass selection minimum"}; + o2::framework::Configurable cMaxPiKaMassCut{"cMaxPiKaMassCut", -999, "bPion-Kaon pair inv mass selection maximum"}; + o2::framework::Configurable cMinAngle{"cMinAngle", -999, "Minimum angle between the secondary resonance and the bachelor"}; + o2::framework::Configurable cMaxAngle{"cMaxAngle", -999, "Maximum angle between the secondary resonance and the bachelor"}; + o2::framework::Configurable cMinPairAsym{"cMinPairAsym", -999, "Minimum pair asymmetry"}; + o2::framework::Configurable cMaxPairAsym{"cMaxPairAsym", -999, "Maximum pair asymmetry"}; +}; + +/// Common TOF switch and K1 selection +struct CandidateCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cByPassTOF{"cByPassTOF", false, "Bypass the TOF nSigma selection"}; + o2::framework::Configurable cK1MaxRap{"cK1MaxRap", 0.5, "K1 maximum rapidity"}; + o2::framework::Configurable cK1MinRap{"cK1MinRap", -0.5, "K1 minimum rapidity"}; +}; + +/// Process functions enabled in the task; they decide the configuration checks and the registered histograms. +struct ProcessModes { + bool microTracks = false; // any process function reading micro tracks (quantised DCA and nSigma) + bool mcReco = false; // reconstructed MC with full or micro tracks + bool mcRecoMicro = false; // reconstructed MC with micro tracks + bool mcGen = false; // generated K1 parents in selected reconstructed events +}; + +/// Loose-stage traversal of unlike-sign micro candidates for the export hook. +/// With the defaults and without an export hook, the candidate loop applies only the conventional selection. +struct LooseStageOptions { + bool audit = false; // fill ML/looseCutflow and ML/looseMassPtActivity + bool exportSelected = false; // hand candidates to the export hook at the selected stage instead of the loose stage +}; + +/// Selected (pion, pion, kaon) triplet handed to the candidate hook. +/// mass13, mass23, angle and pairAsym are computed only inside the K1 rapidity window, and there only +/// when a candidate cut needs them or forEachCandidate() was asked for them; otherwise they are 0. +struct K1CandidateValues { + ROOT::Math::PxPyPzMVector k1; // pion1 + pion2 + kaon + ROOT::Math::PxPyPzMVector secondary; // pion1 + pion2 + double mass13 = 0.; // pion1 + kaon (K*0 candidate in the unlike-sign case) + double mass23 = 0.; // pion2 + kaon + double angle = 0.; // opening angle between the secondary resonance and the bachelor + double pairAsym = 0.; // energy asymmetry between the secondary resonance and the bachelor + bool isUnlikeSign = false; // opposite-sign pion pair + bool inRapidity = false; // K1 rapidity window + bool passesCandidateCuts = false; // secondary mass, pi-K mass, angle and asymmetry cuts (inside the rapidity window) +}; + +// Truth classification from the immediate mothers and the sibling IDs of the reconstructed daughters. +template +bool hasSibling(const Track& directDaughter, int resonanceId) +{ + if (resonanceId < 0) { + return false; + } + const auto siblings = directDaughter.siblingIds(); + return siblings[0] == resonanceId || siblings[1] == resonanceId; +} + +template +bool matchesKStarPi(const Track& resonancePion, const Track& directPion, const Kaon& kaon) +{ + const int charge = kaon.pdgCode() > 0 ? 1 : -1; + if (resonancePion.motherId() != kaon.motherId() || + resonancePion.motherId() == directPion.motherId()) { + return false; + } + if (resonancePion.motherPDG() != charge * o2::constants::physics::Pdg::kK0Star892 || kaon.motherPDG() != charge * o2::constants::physics::Pdg::kK0Star892) { + return false; + } + if (resonancePion.pdgCode() != -charge * kPiPlus || directPion.pdgCode() != charge * kPiPlus || + directPion.motherPDG() != charge * o2::constants::physics::Pdg::kK1_1270Plus) { + return false; + } + return hasSibling(directPion, kaon.motherId()); +} + +template +K1TruthChannel classifyK1Truth(const Track& pion1, const Track& pion2, const Kaon& kaon) +{ + if (std::abs(pion1.pdgCode()) != kPiPlus || std::abs(pion2.pdgCode()) != kPiPlus || + std::abs(kaon.pdgCode()) != kKPlus) { + return K1TruthChannel::None; + } + if (pion1.motherId() < 0 || pion2.motherId() < 0 || kaon.motherId() < 0) { + return K1TruthChannel::None; + } + const int charge = kaon.pdgCode() > 0 ? 1 : -1; + const bool rhoPions = pion1.motherId() == pion2.motherId() && + pion1.motherPDG() == kRho770_0 && pion2.motherPDG() == kRho770_0 && + pion1.pdgCode() == -pion2.pdgCode(); + if (rhoPions && kaon.motherPDG() == charge * o2::constants::physics::Pdg::kK1_1270Plus && + kaon.motherId() != pion1.motherId() && hasSibling(kaon, pion1.motherId())) { + return K1TruthChannel::RhoK; + } + if (matchesKStarPi(pion1, pion2, kaon) || matchesKStarPi(pion2, pion1, kaon)) { + return K1TruthChannel::KStarPi; + } + return K1TruthChannel::None; +} + +template +int k1MotherId(K1TruthChannel channel, const Kaon& kaon, const Pion& pion1, const Pion& pion2) +{ + if (channel == K1TruthChannel::RhoK) { + return kaon.motherId(); + } + if (channel == K1TruthChannel::KStarPi) { + return std::abs(pion1.motherPDG()) == o2::constants::physics::Pdg::kK1_1270Plus ? pion1.motherId() : pion2.motherId(); + } + return -1; +} + +template +K1ParentTruthMap makeK1ParentTruthMap(const ParentsType& parents) +{ + K1ParentTruthMap result; + for (const auto& part : parents) { + if (std::abs(part.pdgCode()) != o2::constants::physics::Pdg::kK1_1270Plus || part.originalMcParticleId() < 0) { + continue; + } + const auto id = static_cast(part.originalMcParticleId()); + const int charge = part.pdgCode() > 0 ? 1 : -1; + result.emplace(id, K1ParentTruth{part.pt(), part.y(), part.pdgCode(), classifyGeneratedK1(charge, part.daughterPDG1(), part.daughterPDG2())}); + } + return result; +} + +inline const K1ParentTruth* findK1ParentTruth(const K1ParentTruthMap& parents, int64_t motherId, + K1TruthChannel channel, int charge) +{ + const auto it = parents.find(motherId); + if (it == parents.end()) { + return nullptr; + } + const auto& parent = it->second; + const int expectedPdg = charge * o2::constants::physics::Pdg::kK1_1270Plus; + if (channel == K1TruthChannel::None || parent.channel != channel || parent.pdgCode != expectedPdg || + !std::isfinite(parent.pt) || parent.pt < 0. || !std::isfinite(parent.y)) { + return nullptr; + } + return &parent; +} + +// Immediate decay channel of a generated K1 from the PDG codes of its two daughters. +inline K1TruthChannel classifyGeneratedK1(int charge, int daughter1, int daughter2) +{ + if ((daughter1 == kRho770_0 && daughter2 == charge * kKPlus) || + (daughter2 == kRho770_0 && daughter1 == charge * kKPlus)) { + return K1TruthChannel::RhoK; + } + if ((daughter1 == charge * o2::constants::physics::Pdg::kK0Star892 && daughter2 == charge * kPiPlus) || + (daughter2 == charge * o2::constants::physics::Pdg::kK0Star892 && daughter1 == charge * kPiPlus)) { + return K1TruthChannel::KStarPi; + } + return K1TruthChannel::None; +} + +/// K1 selection and candidate enumeration shared by the K1 tasks. +/// The task owns the configurable groups and the histogram registry and passes them in init(). +class K1AnalysisMicroCore +{ + public: + void init(o2::framework::HistogramRegistry& histos, + EventCuts const& eventCuts, TrackCuts const& trackCuts, + PionPidCuts const& pionPidCuts, KaonPidCuts const& kaonPidCuts, + SecondaryCuts const& secondaryCuts, CandidateCuts const& candidateCuts, + ProcessModes const& modes, LooseStageOptions const& looseOptions = {}) + { + mSecondaryCuts = secondaryCuts; + mCandidateCuts = candidateCuts; + mLooseOptions = looseOptions; + + // The order follows Species + std::vector pid(2); + auto& pion = pid[static_cast(Species::Pion)]; + pion.species = "Pion"; + pion.maxTPCnSigma = pionPidCuts.cMaxTPCnSigmaPion.value; + pion.maxTOFnSigma = pionPidCuts.cMaxTOFnSigmaPion.value; + pion.combinedNSigma = pionPidCuts.nsigmaCutCombinedPion.value; + pion.onlyTOFTracks = pionPidCuts.cUseOnlyTOFTrackPi.value; + pion.usePtDependent = pionPidCuts.cPionUsePtDepPID.value; + pion.ptBins = pionPidCuts.cPionPIDPtBins.value; + pion.tpcNSigmaCuts = pionPidCuts.cPionTPCNSigmaCuts.value; + pion.tofNSigmaCuts = pionPidCuts.cPionTOFNSigmaCuts.value; + pion.tofRequired = pionPidCuts.cPionTOFRequired.value; + pion.maxTPCName = "cMaxTPCnSigmaPion"; + pion.maxTOFName = "cMaxTOFnSigmaPion"; + pion.tpcCutsName = "cPionTPCNSigmaCuts"; + pion.tofCutsName = "cPionTOFNSigmaCuts"; + auto& kaon = pid[static_cast(Species::Kaon)]; + kaon.species = "Kaon"; + kaon.maxTPCnSigma = kaonPidCuts.cMaxTPCnSigmaKaon.value; + kaon.maxTOFnSigma = kaonPidCuts.cMaxTOFnSigmaKaon.value; + kaon.combinedNSigma = kaonPidCuts.nsigmaCutCombinedKaon.value; + kaon.onlyTOFTracks = kaonPidCuts.cUseOnlyTOFTrackKa.value; + kaon.usePtDependent = kaonPidCuts.cKaonUsePtDepPID.value; + kaon.ptBins = kaonPidCuts.cKaonPIDPtBins.value; + kaon.tpcNSigmaCuts = kaonPidCuts.cKaonTPCNSigmaCuts.value; + kaon.tofNSigmaCuts = kaonPidCuts.cKaonTOFNSigmaCuts.value; + kaon.tofRequired = kaonPidCuts.cKaonTOFRequired.value; + kaon.maxTPCName = "cMaxTPCnSigmaKaon"; + kaon.maxTOFName = "cMaxTOFnSigmaKaon"; + kaon.tpcCutsName = "cKaonTPCNSigmaCuts"; + kaon.tofCutsName = "cKaonTOFNSigmaCuts"; + mSelection.init(eventCuts, trackCuts, std::move(pid), mCandidateCuts.cByPassTOF, modes.microTracks); + + mSecondaryWindowOn = isCutEnabled(mSecondaryCuts.cSecondaryMasswindow); + mAnotherMassCutOn = isCutEnabled(mSecondaryCuts.cMinAnotherSecondaryMassCut) || isCutEnabled(mSecondaryCuts.cMaxAnotherSecondaryMassCut); + mPiKaMassCutOn = isCutEnabled(mSecondaryCuts.cMinPiKaMassCut) || isCutEnabled(mSecondaryCuts.cMaxPiKaMassCut); + mAngleCutOn = isCutEnabled(mSecondaryCuts.cMinAngle) || isCutEnabled(mSecondaryCuts.cMaxAngle); + mPairAsymCutOn = isCutEnabled(mSecondaryCuts.cMinPairAsym) || isCutEnabled(mSecondaryCuts.cMaxPairAsym); + + registerHistograms(histos, modes); + } + + template + bool passesEventCuts(const CollisionType& collision) + { + return mSelection.passesEventCuts(collision); + } + + template + bool passesMCEventCuts(const CollisionType& collision) + { + return mSelection.passesMCEventCuts(collision); + } + + // Full selection stage of a track (quality, TOF requirement, PID) + template + int trackSelectionStage(const TrackType& track) + { + const int qualityStage = mSelection.trackQualityStage(track); + if (qualityStage < TrackStage::kTrkClusters) { + return qualityStage; + } + constexpr int SpeciesIndex = static_cast(S); + if (!mSelection.passesTOFRequired(SpeciesIndex, track)) { + return TrackStage::kTrkClusters; + } + const bool hasTOF = track.hasTOF(); + const double tpcNSigma = (S == Species::Pion) ? track.tpcNSigmaPi() : track.tpcNSigmaKa(); + double tofNSigma = std::numeric_limits::quiet_NaN(); // TOF value is only valid with hasTOF + if constexpr (S == Species::Pion) { + if (hasTOF) { + tofNSigma = track.tofNSigmaPi(); + } + } else { + if (hasTOF) { + tofNSigma = track.tofNSigmaKa(); + } + } + if (!mSelection.passesPID(SpeciesIndex, track.pt(), hasTOF, tpcNSigma, tofNSigma)) { + return TrackStage::kTrkTOFRequired; + } + return TrackStage::kTrkPID; + } + + // Unordered (pion, pion, kaon) candidate enumeration of one collision (or one mixed pair of collisions). + // dTracks1: bachelor kaons, dTracks2: pions. The core fills the cut-flow instrumentation; the hooks + // (nullptr to skip) receive + // - onPair(trk1, trk2, secondary, passesPairPt): every pion pair passing the pion selection, + // trk1 being the pion with the lower index, + // - onCandidate(kaon, pion1, pion2, K1CandidateValues): every triplet passing the pion, pair and kaon + // selection, with the canonical pion roles (pion1: opposite sign to the kaon in the unlike-sign case), + // - onExport(collision, kaon, same-sign pion, opposite-sign pion, truth channel, pass bits): the unlike-sign + // micro same-event candidates at the loose or selected stage configured by LooseStageOptions. + // computeValues requests mass13, mass23, angle and pairAsym for every candidate in the rapidity window. + template + void forEachCandidate(o2::framework::HistogramRegistry& histos, const CollisionType& collision, const TracksType& dTracks1, const TracksType& dTracks2, + bool computeValues, PairHook onPair = nullptr, CandidateHook onCandidate = nullptr, ExportHook onExport = nullptr) + { + if (dTracks1.size() == 0 || dTracks2.size() == 0) { + return; + } + constexpr bool HasPairHook = !std::is_same_v; + constexpr bool HasCandidateHook = !std::is_same_v; + constexpr bool HasExportHook = !std::is_same_v; + // Sets are local to this reconstructed collision: IDs cannot leak across DFs. + // Source-file/DF deduplication across split collisions belongs in the audit. + std::array, NTruthChannels> matchedMothers; + + // Selection cache: every track is selected once, not once per pair x bachelor. + // dTracks1: bachelor kaons, dTracks2: pions (different collisions in mixed events). + constexpr bool FillCutFlow = IsResoMicrotrack && !IsMix; + const int64_t firstKaonIndex = dTracks1.begin().index(); + const int64_t firstPionIndex = dTracks2.begin().index(); + const auto kaonSelected = buildSelectionCache(histos, dTracks1, firstKaonIndex); + const auto pionSelected = buildSelectionCache(histos, dTracks2, firstPionIndex); + + // Only micro same-event ML work needs traversal before conventional cuts. + // The canonical-candidate validity is also required before a selected-stage export. + bool visitLoose = false; + bool checkValidity = false; + if constexpr (IsResoMicrotrack && !IsMix) { + visitLoose = mLooseOptions.audit || (!mLooseOptions.exportSelected && HasExportHook); + checkValidity = visitLoose || (mLooseOptions.exportSelected && HasExportHook); + } + std::vector kaonQuality(dTracks1.size(), 0), pionQuality(dTracks2.size(), 0); + if (visitLoose) { + for (const auto& track : dTracks1) { + kaonQuality[getCacheIndex(track, firstKaonIndex, kaonQuality.size())] = mSelection.trackQualityStage(track) == TrackStage::kTrkClusters; + } + for (const auto& track : dTracks2) { + pionQuality[getCacheIndex(track, firstPionIndex, pionQuality.size())] = mSelection.trackQualityStage(track) == TrackStage::kTrkClusters; + } + } + + // Values needed only by switched-on cuts or by the task are computed only then + const bool isK892Mode = mSecondaryCuts.cfgModeK892orRho; + const bool needAngle = computeValues || mAngleCutOn; + const bool needPairAsym = computeValues || mPairAsymCutOn; + // K892 mode: the K* candidate is (trk1, K), rho mode: the rho is (trk1, trk2) + const bool needMass13 = computeValues || (isK892Mode ? mSecondaryWindowOn : mAnotherMassCutOn) || (isK892Mode && (needAngle || needPairAsym)); + const bool needMass23 = computeValues || mPiKaMassCutOn; + const bool rhoWindowOn = mSecondaryWindowOn && !isK892Mode; + + ROOT::Math::PxPyPzMVector lDecayDaughter1, lDecayDaughter2, lResonanceSecondary, lDecayDaughter_bach, lResonanceK1, lPair13, lPair23; + // Unordered pion pairs: each (pion, pion, kaon) triplet is visited once. + // Here trk1 is the pion with the lower index; the roles are assigned once the bachelor is known. + for (const auto& [trk1, trk2] : o2::soa::combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(dTracks2, dTracks2))) { + // trk1: pion, trk2: pion, bTrack: kaon + const bool pionsSelected = pionSelected[getCacheIndex(trk1, firstPionIndex, pionSelected.size())] && pionSelected[getCacheIndex(trk2, firstPionIndex, pionSelected.size())]; + bool pairPt = false; + bool rhoWindow = true; + if (pionsSelected || visitLoose) { + // Resonance reconstruction + lDecayDaughter1.SetCoordinates(trk1.px(), trk1.py(), trk1.pz(), o2::constants::physics::MassPionCharged); + lDecayDaughter2.SetCoordinates(trk2.px(), trk2.py(), trk2.pz(), o2::constants::physics::MassPionCharged); + lResonanceSecondary = lDecayDaughter1 + lDecayDaughter2; + pairPt = !(lResonanceSecondary.Pt() < mSecondaryCuts.cMinSecondaryPtCut); + rhoWindow = !rhoWindowOn || isInWindow(lResonanceSecondary.M(), MassRho770, mSecondaryCuts.cSecondaryMasswindow); + } + if constexpr (FillCutFlow) { + // Early stages count potential triplets: each pair carries N bachelor trials. + // This preserves the pair-first reconstruction and avoids a new cubic data loop. + // Distinct pion IDs are guaranteed by the strictly upper index policy (stage 1 is always passed). + const int lastStage = !pionsSelected ? 1 : !pairPt ? 3 + : !rhoWindow ? 4 + : 5; + for (int stage = 0; stage <= lastStage; ++stage) { + histos.fill(HIST("CutFlow/candidates"), stage, 0, static_cast(dTracks1.size())); + } + if constexpr (IsMC) { + // Match before rejecting quality/pT so both channels have an upstream numerator. + if (std::abs(trk1.pdgCode()) == kPiPlus && trk1.pdgCode() == -trk2.pdgCode()) { + for (const auto& bachelor : dTracks1) { + const auto channel = classifyK1Truth(trk1, trk2, bachelor); + if (channel != K1TruthChannel::None) { + for (int stage = 0; stage <= lastStage; ++stage) { + histos.fill(HIST("CutFlow/candidates"), stage, static_cast(channel)); + } + } + } + } + } + } + if (!pionsSelected && !visitLoose) { + continue; + } + if constexpr (HasPairHook) { + if (pionsSelected) { + onPair(trk1, trk2, lResonanceSecondary, pairPt); + } + } + if (!pairPt && !visitLoose) { + continue; + } + // Secondary mass window (rho mode): the bachelor loop is skipped for rejected pairs + if (!rhoWindow && !visitLoose) { + continue; + } + + for (const auto& bTrack : dTracks1) { + if (bTrack.index() == trk1.index() || bTrack.index() == trk2.index()) { + continue; + } + K1TruthChannel flowChannel = K1TruthChannel::None; + if constexpr (IsMC && IsResoMicrotrack && !IsMix) { + flowChannel = classifyK1Truth(trk1, trk2, bTrack); + } + auto countCandidate = [&](int stage) { + if constexpr (FillCutFlow) { + histos.fill(HIST("CutFlow/candidates"), stage, 0); + if constexpr (IsMC) { + if (flowChannel != K1TruthChannel::None) { + histos.fill(HIST("CutFlow/candidates"), stage, static_cast(flowChannel)); + } + } + } + }; + const bool pairSelected = pionsSelected && pairPt && rhoWindow; + const bool bachelorSelected = kaonSelected[getCacheIndex(bTrack, firstKaonIndex, kaonSelected.size())]; + if (pairSelected) { + countCandidate(6); + } + if ((!pairSelected || !bachelorSelected) && !visitLoose) { + continue; + } + const bool tripletSelected = pairSelected && bachelorSelected; + if (tripletSelected) { + countCandidate(7); + } + + // Canonical assignment of the pion roles, once the bachelor is known. + // Unlike-sign pair: the pion with the sign opposite to the kaon is pion 1 (K*0 partner), the other is pion 2. + // Like-sign pair (the rule is ambiguous): the pion with the lower index is pion 1. + const bool isUnlikeSign = trk1.sign() * trk2.sign() < 0; + const bool swapPions = isUnlikeSign && trk1.sign() == bTrack.sign(); + const auto& pion1 = swapPions ? trk2 : trk1; + const auto& pion2 = swapPions ? trk1 : trk2; + const auto& lPion1 = swapPions ? lDecayDaughter2 : lDecayDaughter1; + const auto& lPion2 = swapPions ? lDecayDaughter1 : lDecayDaughter2; + + // K1 reconstruction + lDecayDaughter_bach.SetCoordinates(bTrack.px(), bTrack.py(), bTrack.pz(), o2::constants::physics::MassKaonCharged); + lResonanceK1 = lResonanceSecondary + lDecayDaughter_bach; + K1CandidateValues values; + values.k1 = lResonanceK1; + values.secondary = lResonanceSecondary; + values.isUnlikeSign = isUnlikeSign; + + auto countMl = [&](int stage) { + if (mLooseOptions.audit && isUnlikeSign) { + histos.fill(HIST("ML/looseCutflow"), stage, 0); + if (flowChannel != K1TruthChannel::None) { + const int stratum = 2 * static_cast(flowChannel) - (bTrack.sign() > 0 ? 1 : 0); + histos.fill(HIST("ML/looseCutflow"), stage, stratum); + } + } + }; + bool validLoose = false; + if constexpr (IsResoMicrotrack && !IsMix) { + if (checkValidity && isUnlikeSign) { + const auto canonical = o2::analysis::k1ml::canonicalizeUS(o2::analysis::k1ml::makeTrackSnapshot(bTrack), o2::analysis::k1ml::makeTrackSnapshot(pion2), o2::analysis::k1ml::makeTrackSnapshot(pion1)); + validLoose = canonical.status == o2::analysis::k1ml::BuildStatus::Ok && + std::isfinite(lResonanceK1.M()) && std::isfinite(lResonanceK1.Pt()) && + std::isfinite(lResonanceK1.Rapidity()) && std::isfinite(lResonanceK1.Eta()) && + std::isfinite(lResonanceK1.Phi()) && + o2::analysis::k1ml::buildMasterFeatures(canonical.candidate).status == o2::analysis::k1ml::BuildStatus::Ok; + if (validLoose) { + countMl(0); + } + } + } + + // Stage L common acceptance uses the existing inclusive rapidity window. + values.inRapidity = lResonanceK1.Rapidity() >= mCandidateCuts.cK1MinRap && lResonanceK1.Rapidity() <= mCandidateCuts.cK1MaxRap; + if (!values.inRapidity) { + if constexpr (HasCandidateHook) { + if (tripletSelected) { + onCandidate(bTrack, pion1, pion2, values); + } + } + continue; + } + if (tripletSelected) { + countCandidate(8); + } + + if (needMass13) { + lPair13 = lPion1 + lDecayDaughter_bach; + values.mass13 = lPair13.M(); + } + if (needMass23) { + lPair23 = lPion2 + lDecayDaughter_bach; + values.mass23 = lPair23.M(); + } + // Rho mode: secondary = (trk1, trk2) against the bachelor. K892 mode: secondary = (trk1, K) against trk2. + if (needAngle) { + values.angle = isK892Mode ? ROOT::Math::VectorUtil::Angle(lPair13, lPion2) : ROOT::Math::VectorUtil::Angle(lResonanceSecondary, lDecayDaughter_bach); + } + if (needPairAsym) { + values.pairAsym = isK892Mode ? (lPair13.E() - lPion2.E()) / (lPair13.E() + lPion2.E()) + : (lResonanceSecondary.E() - lDecayDaughter_bach.E()) / (lResonanceSecondary.E() + lDecayDaughter_bach.E()); + } + + // Candidate cuts (each one is evaluated only if switched on) + values.passesCandidateCuts = + (!isK892Mode || !mSecondaryWindowOn || (isInWindow(values.mass13, o2::constants::physics::MassK0Star892, mSecondaryCuts.cSecondaryMasswindow) && pion1.sign() != bTrack.sign())) && + (!mAnotherMassCutOn || isInRange(isK892Mode ? lResonanceSecondary.M() : values.mass13, mSecondaryCuts.cMinAnotherSecondaryMassCut, mSecondaryCuts.cMaxAnotherSecondaryMassCut)) && + (!mPiKaMassCutOn || isInRange(values.mass23, mSecondaryCuts.cMinPiKaMassCut, mSecondaryCuts.cMaxPiKaMassCut)) && + (!mAngleCutOn || isInRange(values.angle, mSecondaryCuts.cMinAngle, mSecondaryCuts.cMaxAngle)) && + (!mPairAsymCutOn || isInRange(values.pairAsym, mSecondaryCuts.cMinPairAsym, mSecondaryCuts.cMaxPairAsym)); + auto exportCandidate = [&](uint16_t passBits) { + if constexpr (IsResoMicrotrack && !IsMix && HasExportHook) { + onExport(collision, bTrack, pion2, pion1, flowChannel, passBits); + } else { + static_cast(passBits); + } + }; + if (visitLoose && validLoose) { + countMl(1); + if (mLooseOptions.audit) { + histos.fill(HIST("ML/looseMassPtActivity"), lResonanceK1.M(), lResonanceK1.Pt(), collision.cent()); + } + const bool qualityPass = kaonQuality[getCacheIndex(bTrack, firstKaonIndex, kaonQuality.size())] && + pionQuality[getCacheIndex(trk1, firstPionIndex, pionQuality.size())] && + pionQuality[getCacheIndex(trk2, firstPionIndex, pionQuality.size())]; + uint16_t passBits = kPassLoose; + if (qualityPass) { + passBits |= kPassQuality; + countMl(2); + if (pionsSelected && bachelorSelected) { + passBits |= kPassPID; + countMl(3); + if (pairPt && rhoWindow) { + passBits |= kPassPair; + countMl(4); + if (values.passesCandidateCuts) { + passBits |= kPassCandidate; + countMl(5); + } + } + } + } + if (!mLooseOptions.exportSelected) { + exportCandidate(passBits); + } + } + // Stage C retains the frozen conventional selections. + if (!tripletSelected) { + continue; + } + if (values.passesCandidateCuts) { + countCandidate(9); + countCandidate(isUnlikeSign ? 10 : 11); + if (isUnlikeSign && mLooseOptions.exportSelected && validLoose) { + exportCandidate(PassBitsSelected); + } + if constexpr (IsMC && IsResoMicrotrack && !IsMix) { + if (flowChannel != K1TruthChannel::None) { + const int mother = flowChannel == K1TruthChannel::RhoK ? bTrack.motherId() : std::abs(pion1.motherPDG()) == o2::constants::physics::Pdg::kK1_1270Plus ? pion1.motherId() + : pion2.motherId(); + if (matchedMothers[static_cast(flowChannel)].insert(mother).second) { + histos.fill(HIST("CutFlow/uniqueMothersPerCollision"), static_cast(flowChannel)); + } + } + } + } + if constexpr (HasCandidateHook) { + onCandidate(bTrack, pion1, pion2, values); + } + } // bTrack + } + } // forEachCandidate + + // Generated K1 parents of a selected reconstructed MC collision. The optional callback receives + // (parent, immediate channel) for the parents inside the K1 rapidity window. + // Parents belong to selected reconstructed events; split reco collisions + // repeat parent sets. This is not an unconditional generated denominator. + template + void forEachGeneratedK1(o2::framework::HistogramRegistry& histos, const ParentsType& resoParents, Callback callback = nullptr) + { + for (const auto& part : resoParents) { + if (std::abs(part.pdgCode()) != o2::constants::physics::Pdg::kK1_1270Plus) { + continue; + } + const int charge = part.pdgCode() > 0 ? 1 : -1; + const K1TruthChannel channel = classifyGeneratedK1(charge, part.daughterPDG1(), part.daughterPDG2()); + histos.fill(HIST("CutFlow/generated"), 0, static_cast(channel)); + if (!std::isfinite(part.pt()) || part.pt() < 0. || !std::isfinite(part.y()) || + part.y() < mCandidateCuts.cK1MinRap || part.y() > mCandidateCuts.cK1MaxRap) { + continue; + } + histos.fill(HIST("CutFlow/generated"), 1, static_cast(channel)); + if constexpr (!std::is_same_v) { + callback(part, channel); + } + } + } + + private: + // Selection cache of one track slice. The row number of a grouped slice is global, hence the offset. + template + static std::size_t getCacheIndex(const TrackType& track, int64_t firstIndex, std::size_t size) + { + const int64_t index = static_cast(track.index()) - firstIndex; + if (index < 0 || index >= static_cast(size)) { + LOG(fatal) << "Track index " << track.index() << " is outside the selection cache [" << firstIndex << ", " << firstIndex + static_cast(size) << ")"; + } + return static_cast(index); + } + + template + std::vector buildSelectionCache(o2::framework::HistogramRegistry& histos, const TracksType& tracks, int64_t firstIndex) + { + std::vector selected(tracks.size(), 0); + for (const auto& track : tracks) { + const int stage = trackSelectionStage(track); + selected[getCacheIndex(track, firstIndex, selected.size())] = (stage == TrackStage::kTrkPID) ? 1 : 0; + if constexpr (FillCutFlow) { + for (int i = 0; i <= stage; ++i) { + histos.fill(HIST("CutFlow/tracks"), i, static_cast(S)); + } + } + } + return selected; + } + + // Cut-flow instrumentation of the selection; the output histograms belong to the tasks. + void registerHistograms(o2::framework::HistogramRegistry& histos, ProcessModes const& modes) + { + using o2::framework::HistType; + if (mLooseOptions.audit) { + auto flow = histos.add("ML/looseCutflow", "US triplets;stage;signal stratum", HistType::kTH2D, {{6, -0.5, 5.5}, {5, -0.5, 4.5}}); + const std::array labels{"structural US", "loose acceptance", "track quality", "TOF + PID", "pair requirements", "selected US"}; + const std::array strata{"all US", "rhoK+", "rhoK-", "KstarPi+", "KstarPi-"}; + for (std::size_t i = 0; i < labels.size(); ++i) { + flow->GetXaxis()->SetBinLabel(i + 1, labels[i]); + } + for (std::size_t i = 0; i < strata.size(); ++i) { + flow->GetYaxis()->SetBinLabel(i + 1, strata[i]); + } + histos.add("ML/looseMassPtActivity", "Loose US;mass (GeV/c^{2});pT (GeV/c);FT0M percentile", HistType::kTH3D, + {{300, 0.7, 3.7}, {{0., 0.5, 1., 2., 3., 5., 8., 15., 30., 100.}, "pT"}, {{0., 10., 30., 50., 70., 100., 110.}, "FT0M percentile"}}); + } + + // Micro-only instrumentation: category 0 includes all combinations, not just unmatched. + constexpr int NTrackStages = TrackStage::kTrkNStages; + auto trackFlow = histos.add("CutFlow/tracks", "Micro tracks, once per selected collision;stage;species", HistType::kTH2D, {{NTrackStages, -0.5, NTrackStages - 0.5}, {2, -0.5, 1.5}}); + const std::array trackLabels{"input", "pT", "eta", "DCAxy", "DCAz", "track flags", "clusters / crossed rows", "TOF required", "PID"}; + for (std::size_t i = 0; i < trackLabels.size(); ++i) { + trackFlow->GetXaxis()->SetBinLabel(i + 1, trackLabels[i]); + } + trackFlow->GetYaxis()->SetBinLabel(1, "pion"); + trackFlow->GetYaxis()->SetBinLabel(2, "kaon"); + auto candidateFlow = histos.add("CutFlow/candidates", "Unordered micro triplets;stage;category", HistType::kTH2D, {{NCandidateStages, -0.5, NCandidateStages - 0.5}, {3, -0.5, 2.5}}); + const std::array candidateLabels{"input unordered triplets", "distinct pion IDs", "pion selection (quality+PID)", "pion pair constructed", "pair pT", "secondary mass window (rho mode)", "three distinct IDs", "kaon selection (quality+PID)", "K1 rapidity", "candidate cuts", "final US", "final LS"}; + for (std::size_t i = 0; i < candidateLabels.size(); ++i) { + candidateFlow->GetXaxis()->SetBinLabel(i + 1, candidateLabels[i]); + } + candidateFlow->GetYaxis()->SetBinLabel(1, "all"); + candidateFlow->GetYaxis()->SetBinLabel(2, "rho K"); + candidateFlow->GetYaxis()->SetBinLabel(3, "K* pi"); + if (modes.mcRecoMicro) { + auto mothers = histos.add("CutFlow/uniqueMothersPerCollision", "Final unique K1 IDs summed over reconstructed collisions (not globally deduplicated)", HistType::kTH1D, {{2, 0.5, 2.5}}); + mothers->GetXaxis()->SetBinLabel(1, "rho K"); + mothers->GetXaxis()->SetBinLabel(2, "K* pi"); + } + if (modes.mcGen) { + auto generated = histos.add("CutFlow/generated", "K1 parent rows conditional on selected reconstructed events;stage;immediate channel", HistType::kTH2D, {{2, -0.5, 1.5}, {3, -0.5, 2.5}}); + generated->GetXaxis()->SetBinLabel(1, "all K1 parent rows"); + generated->GetXaxis()->SetBinLabel(2, "K1 rapidity window"); + generated->GetYaxis()->SetBinLabel(1, "other / unresolved"); + generated->GetYaxis()->SetBinLabel(2, "rho K"); + generated->GetYaxis()->SetBinLabel(3, "K* pi"); + } + } + + ResoAnalysisSelectionCore mSelection; + SecondaryCuts mSecondaryCuts; + CandidateCuts mCandidateCuts; + LooseStageOptions mLooseOptions; + + // Derived once in init(): which candidate cuts are switched on. + bool mSecondaryWindowOn = false; + bool mAnotherMassCutOn = false; + bool mPiKaMassCutOn = false; + bool mAngleCutOn = false; + bool mPairAsymCutOn = false; +}; + +} // namespace o2::analysis::k1micro + +#endif // PWGLF_CORE_K1ANALYSISMICROCORE_H_ diff --git a/PWGLF/Core/K1MlFeatures.h b/PWGLF/Core/K1MlFeatures.h new file mode 100644 index 00000000000..262fb0feff7 --- /dev/null +++ b/PWGLF/Core/K1MlFeatures.h @@ -0,0 +1,873 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file K1MlFeatures.h +/// \brief Canonical micro001 K1 candidate and feature contract helpers +/// \author Bong-Hwi Lim +/// + +#ifndef PWGLF_CORE_K1MLFEATURES_H_ +#define PWGLF_CORE_K1MLFEATURES_H_ + +#include "PWGLF/DataModel/LFResonanceTables.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace o2::analysis::k1ml +{ +inline constexpr std::size_t NItsLayers = 7; +inline constexpr std::size_t NCandidateTracks = 3; +inline constexpr std::size_t NMasterFeatures = 125; + +enum class Role : uint8_t { Kaon, + PionSame, + PionOpp }; +enum class Profile : uint8_t { DetectorV1, + RelationalV1, + SubstructureV1 }; +enum class BuildStatus : uint8_t { + Ok, + InvalidChargePattern, + ReusedTrack, + InvalidMomentum, + InvalidKinematics, + InvalidContract +}; + +struct TrackSnapshot { + int64_t sourceTrackId = -1; + int8_t charge = 0; + float px = 0.f, py = 0.f, pz = 0.f; + uint8_t pidNSigmaPiFlag = 0, pidNSigmaKaFlag = 0, pidNSigmaPrFlag = 0; + uint8_t trackSelectionFlags = 0, trackFlags = 0, tpcNClsCrossedRows = 0, itsClusterMap = 0; + std::array tpcNSigma{}; // pi, ka, pr + std::array tofNSigma{}; // pi, ka, pr + float dcaXY = std::numeric_limits::quiet_NaN(); + float dcaZ = std::numeric_limits::quiet_NaN(); + bool passedPtDependentDCAxy = false, passedPtDependentDCAz = false; + bool hasTOF = false, isPVContributor = false; + std::array itsHit{}; +}; + +struct CandidateSnapshot { + std::array tracks{}; // K, same-charge pion, opposite-charge pion +}; + +struct CanonicalizationResult { + CandidateSnapshot candidate{}; + BuildStatus status = BuildStatus::InvalidChargePattern; + explicit operator bool() const { return status == BuildStatus::Ok; } +}; + +struct KinematicAudit { + float massKPiPi = 0.f; + float massPiPi = 0.f; + float massKaonPiSame = 0.f; + float massKaonPiOpp = 0.f; + float candidatePt = 0.f; + float candidateEta = 0.f; + float candidatePhi = 0.f; + float scalarSumPt = 0.f; +}; + +struct FeaturePack { + std::array master{}; + BuildStatus status = BuildStatus::InvalidContract; + KinematicAudit kinematics{}; +}; + +// Adapt the real ResoMicroTracks_001 public columns and dynamic accessors. +// Keeping the raw bytes alongside decoded accessors makes the encoding auditable. +template +TrackSnapshot makeTrackSnapshot(MicroTrack const& row) +{ + TrackSnapshot out; + out.sourceTrackId = static_cast(row.trackId()); + out.charge = static_cast(row.sign()); + out.px = static_cast(row.px()); + out.py = static_cast(row.py()); + out.pz = static_cast(row.pz()); + out.pidNSigmaPiFlag = static_cast(row.pidNSigmaPiFlag()); + out.pidNSigmaKaFlag = static_cast(row.pidNSigmaKaFlag()); + out.pidNSigmaPrFlag = static_cast(row.pidNSigmaPrFlag()); + out.trackSelectionFlags = static_cast(row.trackSelectionFlags()); + out.trackFlags = static_cast(row.trackFlags()); + out.tpcNClsCrossedRows = static_cast(row.tpcNClsCrossedRows()); + out.itsClusterMap = static_cast(row.itsClusterMap()); + out.tpcNSigma = {static_cast(row.tpcNSigmaPi()), static_cast(row.tpcNSigmaKa()), static_cast(row.tpcNSigmaPr())}; + out.tofNSigma = {static_cast(row.tofNSigmaPi()), static_cast(row.tofNSigmaKa()), static_cast(row.tofNSigmaPr())}; + out.dcaXY = static_cast(row.dcaXY()); + out.dcaZ = static_cast(row.dcaZ()); + out.passedPtDependentDCAxy = row.passedPtDependentDCAxy(); + out.passedPtDependentDCAz = row.passedPtDependentDCAz(); + out.hasTOF = row.hasTOF(); + out.isPVContributor = row.isPVContributor(); + for (std::size_t layer = 0; layer < NItsLayers; ++layer) { + out.itsHit[layer] = row.hasITSHitInLayer(static_cast(layer)); + } + return out; +} + +inline bool hasFiniteMomentum(TrackSnapshot const& track) +{ + return std::isfinite(track.px) && std::isfinite(track.py) && std::isfinite(track.pz) && + std::hypot(track.px, track.py) > 0.f; +} + +inline CanonicalizationResult canonicalizeUS(TrackSnapshot const& kaon, + TrackSnapshot const& pionA, + TrackSnapshot const& pionB) +{ + CanonicalizationResult result; + if (kaon.sourceTrackId == pionA.sourceTrackId || kaon.sourceTrackId == pionB.sourceTrackId || + pionA.sourceTrackId == pionB.sourceTrackId) { + result.status = BuildStatus::ReusedTrack; + return result; + } + if ((kaon.charge != 1 && kaon.charge != -1) || (pionA.charge != 1 && pionA.charge != -1) || + (pionB.charge != 1 && pionB.charge != -1) || pionA.charge == pionB.charge || + (pionA.charge != kaon.charge && pionB.charge != kaon.charge)) { + result.status = BuildStatus::InvalidChargePattern; + return result; + } + if (!hasFiniteMomentum(kaon) || !hasFiniteMomentum(pionA) || !hasFiniteMomentum(pionB)) { + result.status = BuildStatus::InvalidMomentum; + return result; + } + const auto& same = pionA.charge == kaon.charge ? pionA : pionB; + const auto& opposite = pionA.charge == kaon.charge ? pionB : pionA; + result.candidate.tracks = {kaon, same, opposite}; + result.status = BuildStatus::Ok; + return result; +} + +namespace detail +{ +struct EncodedValue { + float value; + float valid; + float overflow; +}; + +inline EncodedValue encodePID(float decoded) +{ + if (std::isnan(decoded)) { + return {.value = 0.f, .valid = 0.f, .overflow = 0.f}; + } + if (std::isinf(decoded)) { + return {.value = std::signbit(decoded) ? -3.5f : 3.5f, .valid = 1.f, .overflow = 1.f}; + } + return {.value = decoded, .valid = 1.f, .overflow = 0.f}; +} + +inline EncodedValue encodeDCA(float decoded) +{ + if (!std::isfinite(decoded)) { + return {.value = 0.f, .valid = 0.f, .overflow = 0.f}; + } + const bool overflow = decoded == o2::aod::resomicrodaughter001::DCAEncoding::MaxDCA; + return {.value = decoded, .valid = 1.f, .overflow = overflow ? 1.f : 0.f}; +} + +struct Kinematics { + double pt; + double eta; + double phi; + double energy; +}; + +inline bool getKinematics(TrackSnapshot const& track, double mass, Kinematics& out) +{ + if (!hasFiniteMomentum(track)) { + return false; + } + const double px = track.px, py = track.py, pz = track.pz; + out.pt = std::hypot(px, py); + const double p = std::hypot(out.pt, pz); + out.eta = std::asinh(pz / out.pt); + out.phi = std::atan2(py, px); + out.energy = std::sqrt(p * p + mass * mass); + return std::isfinite(out.pt) && std::isfinite(out.eta) && std::isfinite(out.phi) && std::isfinite(out.energy); +} + +inline float invariantMass(Kinematics const& a, Kinematics const& b) +{ + const auto pxa = a.pt * std::cos(a.phi), pya = a.pt * std::sin(a.phi); + const auto pxb = b.pt * std::cos(b.phi), pyb = b.pt * std::sin(b.phi); + const double e = a.energy + b.energy; + const double px = pxa + pxb, py = pya + pyb; + // pz is recovered from pt*sinh(eta), matching the stored three-momentum. + const double pz = a.pt * std::sinh(a.eta) + b.pt * std::sinh(b.eta); + const double m2 = e * e - px * px - py * py - pz * pz; + return static_cast(std::sqrt(std::max(0.0, m2))); +} + +inline float invariantMass(std::array const& tracks, + std::array const& indices) +{ + double e = 0., px = 0., py = 0., pz = 0.; + for (const auto& i : indices) { + const auto& track = tracks[i]; + e += track.energy; + px += track.pt * std::cos(track.phi); + py += track.pt * std::sin(track.phi); + pz += track.pt * std::sinh(track.eta); + } + return static_cast(std::sqrt(std::max(0.0, e * e - px * px - py * py - pz * pz))); +} + +inline void append(std::array& out, std::size_t& index, EncodedValue value) +{ + out[index++] = value.value; + out[index++] = value.valid; + out[index++] = value.overflow; +} + +template +constexpr bool isStrictlyIncreasingBelow(std::array const& indices, std::size_t bound) +{ + for (std::size_t i = 0; i < N; ++i) { + if (indices[i] >= bound || (i > 0 && indices[i - 1] >= indices[i])) { + return false; + } + } + return true; +} +} // namespace detail + +// Names and projection indices are generated from feature_contract_v1.json. +inline constexpr std::array MasterFeatureNames{ + "kaon.tpc_nsigma_pi", + "kaon.tpc_nsigma_pi_valid", + "kaon.tpc_nsigma_pi_overflow", + "kaon.tpc_nsigma_ka", + "kaon.tpc_nsigma_ka_valid", + "kaon.tpc_nsigma_ka_overflow", + "kaon.tpc_nsigma_pr", + "kaon.tpc_nsigma_pr_valid", + "kaon.tpc_nsigma_pr_overflow", + "kaon.tof_nsigma_pi", + "kaon.tof_nsigma_pi_valid", + "kaon.tof_nsigma_pi_overflow", + "kaon.tof_nsigma_ka", + "kaon.tof_nsigma_ka_valid", + "kaon.tof_nsigma_ka_overflow", + "kaon.tof_nsigma_pr", + "kaon.tof_nsigma_pr_valid", + "kaon.tof_nsigma_pr_overflow", + "kaon.abs_dca_xy", + "kaon.abs_dca_xy_valid", + "kaon.abs_dca_xy_overflow", + "kaon.abs_dca_z", + "kaon.abs_dca_z_valid", + "kaon.abs_dca_z_overflow", + "kaon.passed_ptdep_dca_xy", + "kaon.passed_ptdep_dca_z", + "kaon.has_tof", + "kaon.tpc_crossed_rows", + "kaon.its_hit_l0", + "kaon.its_hit_l1", + "kaon.its_hit_l2", + "kaon.its_hit_l3", + "kaon.its_hit_l4", + "kaon.its_hit_l5", + "kaon.its_hit_l6", + "kaon.is_pv_contributor", + "kaon.pt_fraction", + "pion_same.tpc_nsigma_pi", + "pion_same.tpc_nsigma_pi_valid", + "pion_same.tpc_nsigma_pi_overflow", + "pion_same.tpc_nsigma_ka", + "pion_same.tpc_nsigma_ka_valid", + "pion_same.tpc_nsigma_ka_overflow", + "pion_same.tpc_nsigma_pr", + "pion_same.tpc_nsigma_pr_valid", + "pion_same.tpc_nsigma_pr_overflow", + "pion_same.tof_nsigma_pi", + "pion_same.tof_nsigma_pi_valid", + "pion_same.tof_nsigma_pi_overflow", + "pion_same.tof_nsigma_ka", + "pion_same.tof_nsigma_ka_valid", + "pion_same.tof_nsigma_ka_overflow", + "pion_same.tof_nsigma_pr", + "pion_same.tof_nsigma_pr_valid", + "pion_same.tof_nsigma_pr_overflow", + "pion_same.abs_dca_xy", + "pion_same.abs_dca_xy_valid", + "pion_same.abs_dca_xy_overflow", + "pion_same.abs_dca_z", + "pion_same.abs_dca_z_valid", + "pion_same.abs_dca_z_overflow", + "pion_same.passed_ptdep_dca_xy", + "pion_same.passed_ptdep_dca_z", + "pion_same.has_tof", + "pion_same.tpc_crossed_rows", + "pion_same.its_hit_l0", + "pion_same.its_hit_l1", + "pion_same.its_hit_l2", + "pion_same.its_hit_l3", + "pion_same.its_hit_l4", + "pion_same.its_hit_l5", + "pion_same.its_hit_l6", + "pion_same.is_pv_contributor", + "pion_same.pt_fraction", + "pion_opp.tpc_nsigma_pi", + "pion_opp.tpc_nsigma_pi_valid", + "pion_opp.tpc_nsigma_pi_overflow", + "pion_opp.tpc_nsigma_ka", + "pion_opp.tpc_nsigma_ka_valid", + "pion_opp.tpc_nsigma_ka_overflow", + "pion_opp.tpc_nsigma_pr", + "pion_opp.tpc_nsigma_pr_valid", + "pion_opp.tpc_nsigma_pr_overflow", + "pion_opp.tof_nsigma_pi", + "pion_opp.tof_nsigma_pi_valid", + "pion_opp.tof_nsigma_pi_overflow", + "pion_opp.tof_nsigma_ka", + "pion_opp.tof_nsigma_ka_valid", + "pion_opp.tof_nsigma_ka_overflow", + "pion_opp.tof_nsigma_pr", + "pion_opp.tof_nsigma_pr_valid", + "pion_opp.tof_nsigma_pr_overflow", + "pion_opp.abs_dca_xy", + "pion_opp.abs_dca_xy_valid", + "pion_opp.abs_dca_xy_overflow", + "pion_opp.abs_dca_z", + "pion_opp.abs_dca_z_valid", + "pion_opp.abs_dca_z_overflow", + "pion_opp.passed_ptdep_dca_xy", + "pion_opp.passed_ptdep_dca_z", + "pion_opp.has_tof", + "pion_opp.tpc_crossed_rows", + "pion_opp.its_hit_l0", + "pion_opp.its_hit_l1", + "pion_opp.its_hit_l2", + "pion_opp.its_hit_l3", + "pion_opp.its_hit_l4", + "pion_opp.its_hit_l5", + "pion_opp.its_hit_l6", + "pion_opp.is_pv_contributor", + "pion_opp.pt_fraction", + "kaon__pion_same.delta_eta", + "kaon__pion_same.sin_delta_phi", + "kaon__pion_same.cos_delta_phi", + "kaon__pion_same.z_pt", + "kaon__pion_opp.delta_eta", + "kaon__pion_opp.sin_delta_phi", + "kaon__pion_opp.cos_delta_phi", + "kaon__pion_opp.z_pt", + "pion_same__pion_opp.delta_eta", + "pion_same__pion_opp.sin_delta_phi", + "pion_same__pion_opp.cos_delta_phi", + "pion_same__pion_opp.z_pt", + "mass_pi_pi", + "mass_kaon_pion_opp"}; +inline constexpr std::array DetectorV1Projection{ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109}; +inline constexpr std::array RelationalV1Projection{ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122}; +inline constexpr std::array SubstructureV1Projection{ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124}; +static_assert(MasterFeatureNames.size() == NMasterFeatures, "master feature name count must match NMasterFeatures"); +static_assert(detail::isStrictlyIncreasingBelow(DetectorV1Projection, NMasterFeatures), "DetectorV1 projection indices must be strictly increasing and below NMasterFeatures"); +static_assert(detail::isStrictlyIncreasingBelow(RelationalV1Projection, NMasterFeatures), "RelationalV1 projection indices must be strictly increasing and below NMasterFeatures"); +static_assert(detail::isStrictlyIncreasingBelow(SubstructureV1Projection, NMasterFeatures), "SubstructureV1 projection indices must be strictly increasing and below NMasterFeatures"); +inline constexpr std::string_view FeatureContractSha256 = "39f38ece001581d8ebf57392fad045759a63ba49f57c411b9b566d7c1cc58b8a"; + +inline FeaturePack buildMasterFeatures(CandidateSnapshot const& candidate) +{ + FeaturePack pack; + for (auto const& track : candidate.tracks) { + if ((track.charge != 1 && track.charge != -1) || !hasFiniteMomentum(track)) { + pack.status = BuildStatus::InvalidMomentum; + return pack; + } + } + if (candidate.tracks[0].sourceTrackId == candidate.tracks[1].sourceTrackId || + candidate.tracks[0].sourceTrackId == candidate.tracks[2].sourceTrackId || + candidate.tracks[1].sourceTrackId == candidate.tracks[2].sourceTrackId || + candidate.tracks[0].charge != candidate.tracks[1].charge || + candidate.tracks[0].charge == candidate.tracks[2].charge) { + pack.status = BuildStatus::InvalidChargePattern; + return pack; + } + + std::array kin{}; + for (std::size_t i = 0; i < NCandidateTracks; ++i) { + if (!detail::getKinematics(candidate.tracks[i], i == 0 ? o2::constants::physics::MassKaonCharged : o2::constants::physics::MassPionCharged, kin[i])) { + pack.status = BuildStatus::InvalidKinematics; + return pack; + } + } + const double sumPt = kin[0].pt + kin[1].pt + kin[2].pt; + if (!std::isfinite(sumPt) || sumPt <= 0.) { + pack.status = BuildStatus::InvalidKinematics; + return pack; + } + const double totalPx = static_cast(candidate.tracks[0].px) + candidate.tracks[1].px + candidate.tracks[2].px; + const double totalPy = static_cast(candidate.tracks[0].py) + candidate.tracks[1].py + candidate.tracks[2].py; + const double totalPz = static_cast(candidate.tracks[0].pz) + candidate.tracks[1].pz + candidate.tracks[2].pz; + const double candidatePt = std::hypot(totalPx, totalPy); + if (!std::isfinite(candidatePt)) { + pack.status = BuildStatus::InvalidKinematics; + return pack; + } + pack.kinematics.scalarSumPt = static_cast(sumPt); + pack.kinematics.candidatePt = static_cast(candidatePt); + pack.kinematics.candidateEta = candidatePt > 0. ? static_cast(std::asinh(totalPz / candidatePt)) : 0.f; + pack.kinematics.candidatePhi = static_cast(std::atan2(totalPy, totalPx)); + pack.kinematics.massKPiPi = detail::invariantMass(kin, std::array{0, 1, 2}); + pack.kinematics.massPiPi = detail::invariantMass(kin[1], kin[2]); + pack.kinematics.massKaonPiSame = detail::invariantMass(kin[0], kin[1]); + pack.kinematics.massKaonPiOpp = detail::invariantMass(kin[0], kin[2]); + + std::size_t index = 0; + for (std::size_t i = 0; i < NCandidateTracks; ++i) { + const auto& track = candidate.tracks[i]; + for (const float& decoded : track.tpcNSigma) { + detail::append(pack.master, index, detail::encodePID(decoded)); + } + for (const float& decoded : track.tofNSigma) { + detail::append(pack.master, index, track.hasTOF ? detail::encodePID(decoded) : detail::EncodedValue{.value = 0.f, .valid = 0.f, .overflow = 0.f}); + } + detail::append(pack.master, index, detail::encodeDCA(track.dcaXY)); + detail::append(pack.master, index, detail::encodeDCA(track.dcaZ)); + pack.master[index++] = track.passedPtDependentDCAxy ? 1.f : 0.f; + pack.master[index++] = track.passedPtDependentDCAz ? 1.f : 0.f; + pack.master[index++] = track.hasTOF ? 1.f : 0.f; + pack.master[index++] = static_cast(track.tpcNClsCrossedRows); + for (const bool& hit : track.itsHit) { + pack.master[index++] = hit ? 1.f : 0.f; + } + pack.master[index++] = track.isPVContributor ? 1.f : 0.f; + pack.master[index++] = static_cast(kin[i].pt / sumPt); + } + + constexpr std::array, 3> PairIndices{{{{0, 1}}, {{0, 2}}, {{1, 2}}}}; + for (auto const& pair : PairIndices) { + const auto i = pair[0], j = pair[1]; + const double dphi = kin[i].phi - kin[j].phi; + const double zDenominator = kin[i].pt + kin[j].pt; + if (!std::isfinite(dphi) || zDenominator <= 0.) { + pack.status = BuildStatus::InvalidKinematics; + return pack; + } + pack.master[index++] = static_cast(kin[i].eta - kin[j].eta); + pack.master[index++] = static_cast(std::sin(dphi)); + pack.master[index++] = static_cast(std::cos(dphi)); + pack.master[index++] = static_cast(std::min(kin[i].pt, kin[j].pt) / zDenominator); + } + pack.master[index++] = pack.kinematics.massPiPi; + pack.master[index++] = pack.kinematics.massKaonPiOpp; + const bool allFinite = std::all_of(pack.master.begin(), pack.master.end(), [](float x) { return std::isfinite(x); }); + if (index != pack.master.size() || !allFinite) { + pack.status = BuildStatus::InvalidContract; + return pack; + } + pack.status = BuildStatus::Ok; + return pack; +} + +inline std::vector projectFeatures(FeaturePack const& pack, Profile profile) +{ + if (pack.status != BuildStatus::Ok) { + return {}; + } + std::vector projected; + switch (profile) { + case Profile::DetectorV1: + projected.reserve(DetectorV1Projection.size()); + for (const auto& i : DetectorV1Projection) { + projected.push_back(pack.master[i]); + } + break; + case Profile::RelationalV1: + projected.reserve(RelationalV1Projection.size()); + for (const auto& i : RelationalV1Projection) { + projected.push_back(pack.master[i]); + } + break; + case Profile::SubstructureV1: + projected.reserve(SubstructureV1Projection.size()); + for (const auto& i : SubstructureV1Projection) { + projected.push_back(pack.master[i]); + } + break; + default: + return {}; + } + return projected; +} +} // namespace o2::analysis::k1ml + +#endif // PWGLF_CORE_K1MLFEATURES_H_ diff --git a/PWGLF/Core/Omega2012AnalysisCore.h b/PWGLF/Core/Omega2012AnalysisCore.h new file mode 100644 index 00000000000..57444555806 --- /dev/null +++ b/PWGLF/Core/Omega2012AnalysisCore.h @@ -0,0 +1,1281 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file Omega2012AnalysisCore.h +/// \brief Omega(2012) selection, candidate enumeration and truth classification, separately for the two decay modes +/// \author Bong-Hwi Lim +/// +/// Mode A (DecayMode::XiK0s): Omega(2012)- -> Xi- K0S. +/// Mode B (DecayMode::Xi1530K): Omega(2012)- -> Xi(1530)0 K- -> Xi- pi+ K- (charged kaon track). +/// The two modes are never merged: each has its own enumeration, hooks, cut flow, pass bits and truth classification. +/// The Xi selection is common to both modes. The core owns the selection and its cut-flow instrumentation +/// (CutFlow/*, ML/*); the tasks own their output histograms and fill them from the hooks. + +#ifndef PWGLF_CORE_OMEGA2012ANALYSISCORE_H_ +#define PWGLF_CORE_OMEGA2012ANALYSISCORE_H_ + +#include "PWGLF/Core/Omega2012MlFeatures.h" +#include "PWGLF/Core/ResoAnalysisSelectionCore.h" + +#include +#include +#include +#include +#include +#include + +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace o2::analysis::omega2012 +{ + +using o2::analysis::resonance::EventCuts; +using o2::analysis::resonance::PIDCutConfig; +using o2::analysis::resonance::ResoAnalysisSelectionCore; +using o2::analysis::resonance::TrackCuts; +using o2::analysis::resonance::TrackStage; + +// Omega(2012) analysis and ML export share these event-level selection defaults. +// Keep the generic EventCuts defaults unchanged for other resonance tasks. +inline EventCuts makeOmega2012EventCuts() +{ + EventCuts cuts; + cuts.cRecoINELgt0.value = true; + cuts.cMCINELgt0.value = true; + cuts.cMCVtxIn10.value = true; + return cuts; +} + +enum class DecayMode : uint8_t { + XiK0s = 0, // Omega(2012)- -> Xi- K0S + Xi1530K = 1 // Omega(2012)- -> Xi(1530)0 K- -> Xi- pi+ K- +}; + +inline constexpr int PdgXi1530Zero = 3324; // Xi(1530)0, not in o2::constants::physics::Pdg +inline constexpr float SmallNumber = 1e-10f; // avoids division by zero (value of the original task) +inline constexpr float MaxDCAV0ToPV = 1.0f; // maximum K0S DCA to PV (value of the original task) +inline constexpr float CascDCAzPtExponent = -1.1f; +inline constexpr const char* TrackCutsPrefix = "trk."; // JSON prefix of the mode-B track selection +inline constexpr int NXiK0sCandidateStages = 4; +inline constexpr int NXi1530KCandidateStages = 4; +inline constexpr int NGeneratedChannels = 3; + +// Last stage passed by a cascade (Xi selection, common to both modes). +enum XiStage : int { + kXiInput = 0, // failed |eta| or pT + kXiKinematics = 1, // passed |eta| and pT + kXiDCA = 2, // passed the cascade DCA to PV + kXiV0Topology = 3, // passed the Lambda topology and mass window + kXiCascTopology = 4, // passed the cascade topology + kXiMass = 5, // passed the Xi mass window + kXiSelected = kXiMass, + kXiNStages = 6 +}; + +// Last stage passed by a V0 (K0S selection of mode A). +enum K0sStage : int { + kK0sInput = 0, // failed |eta| or pT + kK0sKinematics = 1, // passed |eta| and pT + kK0sTopology = 2, // passed cosPA, daughter DCAs, radius and DCA to PV + kK0sLifetime = 3, // passed the proper lifetime and the minimum qT + kK0sMass = 4, // passed the K0S mass window and the (anti)Lambda rejection + kK0sDaughters = 5, // passed the daughter pion TPC nSigma and crossed rows + kK0sArmenteros = 6, // passed the Armenteros qT > coefficient * |alpha| cut + kK0sSelected = kK0sArmenteros, + kK0sNStages = 7 +}; + +// The value is the species index of the PID configuration in ResoAnalysisSelectionCore. +enum class Species : int { + Pion = 0, + Kaon = 1 +}; + +// Cumulative pass bits of mode A (see LFOmega2012MlTables.h) +enum XiK0sPassBit : uint16_t { + kXiK0sPassLoose = 1, + kXiK0sPassXi = 2, + kXiK0sPassK0s = 4, + kXiK0sPassKinematic = 8 +}; +inline constexpr uint16_t XiK0sPassBitsSelected = kXiK0sPassLoose | kXiK0sPassXi | kXiK0sPassK0s | kXiK0sPassKinematic; + +// Cumulative pass bits of mode B (see LFOmega2012MlTables.h) +enum Xi1530KPassBit : uint16_t { + kXi1530KPassLoose = 1, + kXi1530KPassXi = 2, + kXi1530KPassQuality = 4, + kXi1530KPassPID = 8, + kXi1530KPassWindow = 16 +}; +inline constexpr uint16_t Xi1530KPassBitsSelected = kXi1530KPassLoose | kXi1530KPassXi | kXi1530KPassQuality | kXi1530KPassPID | kXi1530KPassWindow; + +enum class XiK0sTruth : uint8_t { + None = 0, + Matched = 1 +}; + +enum class Xi1530KTruth : uint8_t { + None = 0, + Matched = 1 +}; + +// Immediate decay channel of a generated Omega(2012) +enum class GeneratedChannel : uint8_t { + Other = 0, + XiK0s = 1, + Xi1530K = 2 +}; + +// Configurable groups without prefix: the JSON keys are the plain configurable names of the original task. + +/// Xi (cascade) selection, common to both modes. cMinPtcut is also the K0S minimum pT (as in the original task). +struct XiCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cMinPtcut{"cMinPtcut", 0.15, "Minimum pT for candidates"}; + o2::framework::Configurable cMaxEtaCut{"cMaxEtaCut", 0.8, "Maximum |eta|"}; + o2::framework::Configurable cDCAxyToPVByPtCascP0{"cDCAxyToPVByPtCascP0", 999., "Cascade DCAxy p0"}; + o2::framework::Configurable cDCAxyToPVByPtCascExp{"cDCAxyToPVByPtCascExp", 1., "Cascade DCAxy exp"}; + o2::framework::Configurable cDCAxyToPVAsPtForCasc{"cDCAxyToPVAsPtForCasc", true, "Use pt-dep DCAxy cut (casc)"}; + o2::framework::Configurable cDCAzToPVAsPtForCasc{"cDCAzToPVAsPtForCasc", true, "Use pt-dep DCAz cut (casc)"}; + // V0 topology inside cascade (Lambda) + o2::framework::Configurable cDCALambdaDaugtherscut{"cDCALambdaDaugtherscut", 0.7, "Λ daughters DCA cut"}; + o2::framework::Configurable cDCALambdaToPVcut{"cDCALambdaToPVcut", 0.02, "Λ DCA to PV min"}; + o2::framework::Configurable cDCAPionToPVcut{"cDCAPionToPVcut", 0.06, "π DCA to PV min"}; + o2::framework::Configurable cDCAProtonToPVcut{"cDCAProtonToPVcut", 0.07, "p DCA to PV min"}; + o2::framework::Configurable cV0CosPACutPtDepP0{"cV0CosPACutPtDepP0", 0.25, "V0 CosPA p0"}; + o2::framework::Configurable cV0CosPACutPtDepP1{"cV0CosPACutPtDepP1", 0.022, "V0 CosPA p1"}; + o2::framework::Configurable cMaxV0radiuscut{"cMaxV0radiuscut", 200., "V0 radius max"}; + o2::framework::Configurable cMinV0radiuscut{"cMinV0radiuscut", 2.5, "V0 radius min"}; + o2::framework::Configurable cMasswindowV0cut{"cMasswindowV0cut", 0.005, "Λ mass window for cascade V0"}; + // Cascade topology + o2::framework::Configurable cDCABachlorToPVcut{"cDCABachlorToPVcut", 0.06, "Bachelor DCA to PV min"}; + o2::framework::Configurable cDCAXiDaugthersCutPtRangeLower{"cDCAXiDaugthersCutPtRangeLower", 1., "Xi pt low boundary"}; + o2::framework::Configurable cDCAXiDaugthersCutPtRangeUpper{"cDCAXiDaugthersCutPtRangeUpper", 4., "Xi pt high boundary"}; + o2::framework::Configurable cDCAXiDaugthersCutPtDepLower{"cDCAXiDaugthersCutPtDepLower", 0.8, "Xi daugh DCA (pt cDCAXiDaugthersCutPtDepMiddle{"cDCAXiDaugthersCutPtDepMiddle", 0.5, "Xi daugh DCA (low<=pt cDCAXiDaugthersCutPtDepUpper{"cDCAXiDaugthersCutPtDepUpper", 0.2, "Xi daugh DCA (pt>=high)"}; + o2::framework::Configurable cCosPACascCutPtDepP0{"cCosPACascCutPtDepP0", 0.2, "Cascade CosPA p0"}; + o2::framework::Configurable cCosPACascCutPtDepP1{"cCosPACascCutPtDepP1", 0.022, "Cascade CosPA p1"}; + o2::framework::Configurable cMaxCascradiuscut{"cMaxCascradiuscut", 200., "Cascade radius max"}; + o2::framework::Configurable cMinCascradiuscut{"cMinCascradiuscut", 1.1, "Cascade radius min"}; + o2::framework::Configurable cMasswindowCasccut{"cMasswindowCasccut", 0.008, "Xi mass window"}; + o2::framework::Configurable cMassXiminus{"cMassXiminus", 1.32171, "Xi mass (GeV/c^2)"}; // PDG +}; + +/// K0S selection and Xi-K0S kinematic (opening-angle) cut of mode A +struct K0sCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cKinCuts{"cKinCuts", false, "Kinematic cuts for Xi-K0s opening angle"}; + o2::framework::Configurable> cKinCutsPt{"cKinCutsPt", {0.0, 0.4, 0.6, 0.8, 1.0, 1.4, 1.8, 2.2, 2.6, 3.0, 4.0, 5.0, 6.0, 1e10}, "Omega(2012) pT bins for kinematic cuts"}; + o2::framework::Configurable> cKinLowerCutsAlpha{"cKinLowerCutsAlpha", {1.5, 1.0, 0.5, 0.3, 0.2, 0.15, 0.1, 0.08, 0.07, 0.06, 0.04, 0.02, 0.02}, "Lower cut on Xi-K0s opening angle"}; + o2::framework::Configurable> cKinUpperCutsAlpha{"cKinUpperCutsAlpha", {3.0, 2.0, 1.5, 1.4, 1.0, 0.8, 0.6, 0.5, 0.45, 0.35, 0.3, 0.25, 0.2}, "Upper cut on Xi-K0s opening angle"}; + o2::framework::Configurable cK0sMinCosPA{"cK0sMinCosPA", 0.98, "K0s minimum pointing angle cosine"}; + o2::framework::Configurable cK0sMaxDaughDCA{"cK0sMaxDaughDCA", 0.5, "K0s daughter DCA Maximum"}; + o2::framework::Configurable cK0sMassWindow{"cK0sMassWindow", 0.025, "Mass window for K0s selection (GeV/c^2)"}; + o2::framework::Configurable cMaxV0Etacut{"cMaxV0Etacut", 0.8, "V0 maximum eta cut"}; + o2::framework::Configurable cK0sProperLifetimeMax{"cK0sProperLifetimeMax", 20.0, "K0s proper lifetime max (cm/c)"}; + o2::framework::Configurable cK0sArmenterosQtMin{"cK0sArmenterosQtMin", 0.0, "K0s Armenteros qt min"}; + o2::framework::Configurable cK0sArmenterosAlphaCoeff{"cK0sArmenterosAlphaCoeff", 0.2, "K0s Armenteros alpha coefficient"}; + o2::framework::Configurable cK0sDauPosDCAtoPVMin{"cK0sDauPosDCAtoPVMin", 0.05, "K0s positive daughter DCA to PV min"}; + o2::framework::Configurable cK0sDauNegDCAtoPVMin{"cK0sDauNegDCAtoPVMin", 0.05, "K0s negative daughter DCA to PV min"}; + o2::framework::Configurable cK0sRadiusMin{"cK0sRadiusMin", 0.5, "K0s decay radius min"}; + o2::framework::Configurable cK0sRadiusMax{"cK0sRadiusMax", 200.0, "K0s decay radius max"}; + o2::framework::Configurable cK0sCrossMassRejection{"cK0sCrossMassRejection", true, "Enable Lambda mass rejection for K0s"}; + o2::framework::Configurable cK0sCrossMassRejectionWindow{"cK0sCrossMassRejectionWindow", 0.01, "Lambda mass rejection window for K0s (GeV/c^2)"}; + o2::framework::Configurable cK0sDaughterPiTPCNSigmaMax{"cK0sDaughterPiTPCNSigmaMax", 5.0, "Maximum TPC NSigma for K0s daughter pions"}; + o2::framework::Configurable cK0sPosDaughterMinCrossedRows{"cK0sPosDaughterMinCrossedRows", 50, "Minimum TPC crossed rows for K0s positive daughter"}; + o2::framework::Configurable cK0sNegDaughterMinCrossedRows{"cK0sNegDaughterMinCrossedRows", 50, "Minimum TPC crossed rows for K0s negative daughter"}; +}; + +/// Xi(1530)0 K- selection of mode B +struct Xi1530KCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cXi1530Mass{"cXi1530Mass", 1.53, "Xi(1530) mass (GeV/c^2)"}; + o2::framework::Configurable cXi1530MassWindow{"cXi1530MassWindow", 0.01, "Xi(1530) mass window (GeV/c^2)"}; + o2::framework::Configurable cXi1530UseMassWindow{"cXi1530UseMassWindow", true, "Require m(Xi pi) inside the Xi(1530) mass window"}; + o2::framework::Configurable cXi1530KFillWrongSign{"cXi1530KFillWrongSign", true, "Fill the wrong-sign control histograms (xi1530K_wrongSign/)"}; + o2::framework::Configurable cByPassTOF{"cByPassTOF", false, "Bypass the TOF nSigma selection of the pion and the kaon"}; +}; + +/// Pion PID of mode B (keys of the original three-body pion selection) +struct PionPidCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cPionTPCNSigmaMax{"cPionTPCNSigmaMax", 3.0, "Maximum TPC NSigma for pion"}; + o2::framework::Configurable cPionTOFNSigmaMax{"cPionTOFNSigmaMax", 3.0, "Maximum TOF NSigma for pion"}; + o2::framework::Configurable cPionUsePtDepPID{"cPionUsePtDepPID", false, "Use pT-dependent PID cuts for pion"}; + o2::framework::Configurable> cPionPIDPtBins{"cPionPIDPtBins", {0.0f, 0.5f, 0.8f, 2.0f, 999.0f}, "pT bin edges for pion PID cuts"}; + o2::framework::Configurable> cPionTPCNSigmaCuts{"cPionTPCNSigmaCuts", {3.0f, 3.0f, 2.0f, 2.0f}, "TPC NSigma cuts per pT bin (pion)"}; + o2::framework::Configurable> cPionTOFNSigmaCuts{"cPionTOFNSigmaCuts", {3.0f, 3.0f, 3.0f, 3.0f}, "TOF NSigma cuts per pT bin (pion)"}; + o2::framework::Configurable> cPionTOFRequired{"cPionTOFRequired", {0, 0, 1, 1}, "Require TOF per pT bin (pion)"}; +}; + +/// Kaon PID of mode B (same shape as the pion keys) +struct KaonPidCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cKaonTPCNSigmaMax{"cKaonTPCNSigmaMax", 3.0, "Maximum TPC NSigma for kaon"}; + o2::framework::Configurable cKaonTOFNSigmaMax{"cKaonTOFNSigmaMax", 3.0, "Maximum TOF NSigma for kaon"}; + o2::framework::Configurable cKaonUsePtDepPID{"cKaonUsePtDepPID", false, "Use pT-dependent PID cuts for kaon"}; + o2::framework::Configurable> cKaonPIDPtBins{"cKaonPIDPtBins", {0.0f, 0.5f, 0.8f, 2.0f, 999.0f}, "pT bin edges for kaon PID cuts"}; + o2::framework::Configurable> cKaonTPCNSigmaCuts{"cKaonTPCNSigmaCuts", {3.0f, 3.0f, 2.0f, 2.0f}, "TPC NSigma cuts per pT bin (kaon)"}; + o2::framework::Configurable> cKaonTOFNSigmaCuts{"cKaonTOFNSigmaCuts", {3.0f, 3.0f, 3.0f, 3.0f}, "TOF NSigma cuts per pT bin (kaon)"}; + o2::framework::Configurable> cKaonTOFRequired{"cKaonTOFRequired", {0, 0, 1, 1}, "Require TOF per pT bin (kaon)"}; +}; + +/// Common candidate selection +struct CandidateCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cfgRapidityCut{"cfgRapidityCut", 0.5, "Rapidity cut"}; +}; + +/// Track selection of the mode-B pion and kaon: the common resonance TrackCuts with the JSON prefix "trk." +/// (the Xi selection owns cMinPtcut). The framework prefixes a group only through a `prefix` data member, which +/// TrackCuts does not have and a derived group cannot add (members must belong to one class for the structured +/// binding of the option registration); the names are therefore prefixed here in the same way ("trk.cMinPtcut"). +/// Defaults that differ from TrackCuts follow the original pion selection: |eta| < 0.8 (cPionEtaMax), +/// 70 found TPC clusters for ResoTracks (cPionTPCNClusMin) and DCAz < 0.15 cm (cPionDCAzMax was 0.2 cm, which is +/// beyond the 0.15 cm range of the micro001 DCA grid). +inline TrackCuts makeXi1530KTrackCuts() +{ + TrackCuts cuts; + cuts.cMaxEtacut.value = 0.8f; + cuts.cMaxDCAzToPVcut.value = 0.15; + cuts.cfgTPCcluster.value = 70; + o2::framework::homogeneous_apply_refs( + [](auto& option) { + if constexpr (requires { option.name; }) { + option.name.insert(0, TrackCutsPrefix); + } + return true; + }, + cuts); + return cuts; +} + +/// Process functions enabled in a task; they decide the configuration checks and the registered histograms. +struct ProcessModes { + bool xiK0s = false; // any mode-A process function + bool xi1530K = false; // any mode-B process function + bool microTracks = false; // any mode-B process function reading micro tracks (quantised DCA and nSigma) + bool mcReco = false; // reconstructed MC + bool mcGen = false; // generated Omega(2012) parents (ResoMCParents_001) + bool mixing = false; // event mixing +}; + +/// Loose-stage traversal for the export hook (as the K1 LooseStageOptions). +/// With the defaults and without an export hook, the enumeration applies only the conventional selection. +struct LooseStageOptions { + bool audit = false; // fill ML//looseCutflow and ML//looseMassPtActivity + bool exportSelected = false; // hand candidates to the export hook at the selected stage instead of the loose stage +}; + +/// Mode-A candidate handed to the candidate and export hooks. +/// alpha is computed when cKinCuts is on or the values were requested; passesKinCut is true when cKinCuts is off. +struct XiK0sCandidateValues { + ROOT::Math::PxPyPzEVector omega; // xi + k0s + ROOT::Math::PxPyPzEVector xi; // (pt, eta, phi, mXi) + ROOT::Math::PxPyPzEVector k0s; // (pt, eta, phi, MassK0Short) + float alpha = 0.f; // Xi-K0S opening angle + bool passesKinCut = true; + bool inRapidity = false; // |y| < cfgRapidityCut +}; + +/// Mode-B candidate handed to the candidate and export hooks. +/// massXiK, massPiK, openingAngleXi1530K and cosThetaStar are computed only when the values were requested. +struct Xi1530KCandidateValues { + ROOT::Math::PxPyPzEVector omega; // xi + pion + kaon + ROOT::Math::PxPyPzEVector xi; // (pt, eta, phi, mXi) + ROOT::Math::PxPyPzEVector pion; // (pt, eta, phi, MassPionCharged) + ROOT::Math::PxPyPzEVector kaon; // (pt, eta, phi, MassKaonCharged) + ROOT::Math::PxPyPzEVector xi1530; // xi + pion + float massXiPi = 0.f; + float massXiK = 0.f; + float massPiK = 0.f; + float openingAngleXi1530K = 0.f; + float cosThetaStar = 0.f; + uint8_t chargePattern = o2::analysis::omega2012ml::kSignalPattern; + bool inXi1530Window = false; + bool inRapidity = false; // |y| < cfgRapidityCut +}; + +// Truth classification, separately per mode + +/// Mode A: reconstructed Xi and K0S from the same Omega(2012) (criteria of the original task). +/// A K0S from an intermediate K0bar has motherPDG 311 and is not matched; the truth table keeps motherPDG for audit. +template +XiK0sTruth classifyXiK0sTruth(const Xi& xi, const V0& v0) +{ + if (std::abs(xi.pdgCode()) != kXiMinus || std::abs(v0.pdgCode()) != kK0Short) { + return XiK0sTruth::None; + } + if (xi.motherId() < 0 || xi.motherId() != v0.motherId()) { + return XiK0sTruth::None; + } + if (std::abs(xi.motherPDG()) != o2::constants::physics::Pdg::kOmega2012Minus || xi.motherPDG() != v0.motherPDG()) { + return XiK0sTruth::None; + } + return XiK0sTruth::Matched; +} + +template +bool hasSibling(const Track& track, int particleId) +{ + if (particleId < 0) { + return false; + } + const auto siblings = track.siblingIds(); + return siblings[0] == particleId || siblings[1] == particleId; +} + +/// Mode B: Xi and pion from the same Xi(1530)0, kaon from the Omega(2012) whose other daughter is that Xi(1530)0. +/// Cascades carry no sibling IDs, so the kaon carries the link (siblingIds contains the Xi(1530)0 ID). +template +Xi1530KTruth classifyXi1530KTruth(const Xi& xi, const Track& pion, const Track& kaon) +{ + if (std::abs(xi.pdgCode()) != kXiMinus) { + return Xi1530KTruth::None; + } + const int xiCharge = xi.pdgCode() > 0 ? -1 : 1; // Xi- has PDG code +3312 + if (pion.pdgCode() != -xiCharge * kPiPlus || kaon.pdgCode() != xiCharge * kKPlus) { + return Xi1530KTruth::None; + } + if (xi.motherId() < 0 || xi.motherId() != pion.motherId() || std::abs(xi.motherPDG()) != PdgXi1530Zero || pion.motherPDG() != xi.motherPDG()) { + return Xi1530KTruth::None; + } + // Omega(2012)- and Xi- both have positive PDG codes + if (kaon.motherId() < 0 || kaon.motherPDG() != -xiCharge * o2::constants::physics::Pdg::kOmega2012Minus) { + return Xi1530KTruth::None; + } + return hasSibling(kaon, xi.motherId()) ? Xi1530KTruth::Matched : Xi1530KTruth::None; +} + +/// Immediate channel of a generated Omega(2012) from the PDG codes of its two daughters (charge conjugates included). +inline GeneratedChannel classifyGeneratedOmega2012(int pdg, int daughter1, int daughter2) +{ + const int sign = pdg > 0 ? 1 : -1; + auto isPair = [&](int a, int b) { + if (a == sign * PdgXi1530Zero && b == -sign * kKPlus) { + return GeneratedChannel::Xi1530K; + } + if (a == sign * kXiMinus && (b == kK0Short || b == -sign * kK0)) { + return GeneratedChannel::XiK0s; + } + return GeneratedChannel::Other; + }; + const auto channel = isPair(daughter1, daughter2); + return channel != GeneratedChannel::Other ? channel : isPair(daughter2, daughter1); +} + +/// Omega(2012) selection and candidate enumeration of the Omega(2012) tasks. +/// The task owns the configurable groups and the histogram registry and passes them in init(). +class Omega2012AnalysisCore +{ + public: + void init(o2::framework::HistogramRegistry& histos, + EventCuts const& eventCuts, XiCuts const& xiCuts, K0sCuts const& k0sCuts, + Xi1530KCuts const& xi1530KCuts, TrackCuts const& trackCuts, + PionPidCuts const& pionPidCuts, KaonPidCuts const& kaonPidCuts, + CandidateCuts const& candidateCuts, ProcessModes const& modes, LooseStageOptions const& looseOptions = {}) + { + mXiCuts = xiCuts; + mK0sCuts = k0sCuts; + mXi1530KCuts = xi1530KCuts; + mCandidateCuts = candidateCuts; + mLooseOptions = looseOptions; + + // The order follows Species + std::vector pid(2); + auto& pion = pid[static_cast(Species::Pion)]; + pion.species = "Pion"; + pion.maxTPCnSigma = pionPidCuts.cPionTPCNSigmaMax.value; + pion.maxTOFnSigma = pionPidCuts.cPionTOFNSigmaMax.value; + pion.usePtDependent = pionPidCuts.cPionUsePtDepPID.value; + pion.ptBins = pionPidCuts.cPionPIDPtBins.value; + pion.tpcNSigmaCuts = pionPidCuts.cPionTPCNSigmaCuts.value; + pion.tofNSigmaCuts = pionPidCuts.cPionTOFNSigmaCuts.value; + pion.tofRequired = pionPidCuts.cPionTOFRequired.value; + pion.maxTPCName = "cPionTPCNSigmaMax"; + pion.maxTOFName = "cPionTOFNSigmaMax"; + pion.tpcCutsName = "cPionTPCNSigmaCuts"; + pion.tofCutsName = "cPionTOFNSigmaCuts"; + auto& kaon = pid[static_cast(Species::Kaon)]; + kaon.species = "Kaon"; + kaon.maxTPCnSigma = kaonPidCuts.cKaonTPCNSigmaMax.value; + kaon.maxTOFnSigma = kaonPidCuts.cKaonTOFNSigmaMax.value; + kaon.usePtDependent = kaonPidCuts.cKaonUsePtDepPID.value; + kaon.ptBins = kaonPidCuts.cKaonPIDPtBins.value; + kaon.tpcNSigmaCuts = kaonPidCuts.cKaonTPCNSigmaCuts.value; + kaon.tofNSigmaCuts = kaonPidCuts.cKaonTOFNSigmaCuts.value; + kaon.tofRequired = kaonPidCuts.cKaonTOFRequired.value; + kaon.maxTPCName = "cKaonTPCNSigmaMax"; + kaon.maxTOFName = "cKaonTOFNSigmaMax"; + kaon.tpcCutsName = "cKaonTPCNSigmaCuts"; + kaon.tofCutsName = "cKaonTOFNSigmaCuts"; + mSelection.init(eventCuts, trackCuts, std::move(pid), mXi1530KCuts.cByPassTOF.value, modes.microTracks); + + registerHistograms(histos, modes); + } + + template + bool passesEventCuts(const CollisionType& collision) + { + return mSelection.passesEventCuts(collision); + } + + template + bool passesMCEventCuts(const CollisionType& collision) + { + return mSelection.passesMCEventCuts(collision); + } + + [[nodiscard]] bool kinCutsEnabled() const { return mK0sCuts.cKinCuts.value; } + [[nodiscard]] bool fillWrongSign() const { return mXi1530KCuts.cXi1530KFillWrongSign.value; } + + // Xi selection (common to both modes); equivalent to cascprimaryTrackCut && casctopCut of the original task. + template + [[nodiscard]] int xiSelectionStage(const CascadeType& c) const + { + const auto& cut = mXiCuts; + if (std::abs(c.eta()) > cut.cMaxEtaCut.value || std::abs(c.pt()) < cut.cMinPtcut.value) { + return kXiInput; + } + if (cut.cDCAxyToPVAsPtForCasc.value && std::abs(c.dcaXYCascToPV()) > (cut.cDCAxyToPVByPtCascP0.value + cut.cDCAxyToPVByPtCascExp.value * c.pt())) { + return kXiKinematics; + } + // The DCAz cut uses the DCAxy parameters, as in the original task + if (cut.cDCAzToPVAsPtForCasc.value && std::abs(c.dcaZCascToPV()) > (cut.cDCAxyToPVByPtCascP0.value + cut.cDCAxyToPVByPtCascExp.value * std::pow(c.pt(), CascDCAzPtExponent))) { + return kXiKinematics; + } + + // V0 (Lambda) topology inside the cascade + if (std::abs(c.daughDCA()) > cut.cDCALambdaDaugtherscut.value || std::abs(c.dcav0topv()) < cut.cDCALambdaToPVcut.value) { + return kXiDCA; + } + if (c.sign() < 0) { // Xi- + if (std::abs(c.dcanegtopv()) < cut.cDCAPionToPVcut.value || std::abs(c.dcapostopv()) < cut.cDCAProtonToPVcut.value) { + return kXiDCA; + } + } else { // Anti-Xi + if (std::abs(c.dcanegtopv()) < cut.cDCAProtonToPVcut.value || std::abs(c.dcapostopv()) < cut.cDCAPionToPVcut.value) { + return kXiDCA; + } + } + if (c.v0CosPA() < std::cos(cut.cV0CosPACutPtDepP0.value - cut.cV0CosPACutPtDepP1.value * c.pt())) { + return kXiDCA; + } + if (c.transRadius() > cut.cMaxV0radiuscut.value || c.transRadius() < cut.cMinV0radiuscut.value) { + return kXiDCA; + } + if (std::abs(c.mLambda() - o2::constants::physics::MassLambda) > cut.cMasswindowV0cut.value) { + return kXiDCA; + } + + // Cascade topology + if (std::abs(c.dcabachtopv()) < cut.cDCABachlorToPVcut.value) { + return kXiV0Topology; + } + if (c.pt() < cut.cDCAXiDaugthersCutPtRangeLower.value) { + if (c.cascDaughDCA() > cut.cDCAXiDaugthersCutPtDepLower.value) { + return kXiV0Topology; + } + } else if (c.pt() < cut.cDCAXiDaugthersCutPtRangeUpper.value) { + if (c.cascDaughDCA() > cut.cDCAXiDaugthersCutPtDepMiddle.value) { + return kXiV0Topology; + } + } else { + if (c.cascDaughDCA() > cut.cDCAXiDaugthersCutPtDepUpper.value) { + return kXiV0Topology; + } + } + if (c.cascCosPA() < std::cos(cut.cCosPACascCutPtDepP0.value - cut.cCosPACascCutPtDepP1.value * c.pt())) { + return kXiV0Topology; + } + if (c.cascTransRadius() > cut.cMaxCascradiuscut.value || c.cascTransRadius() < cut.cMinCascradiuscut.value) { + return kXiV0Topology; + } + if (std::abs(c.mXi() - cut.cMassXiminus.value) > cut.cMasswindowCasccut.value) { + return kXiCascTopology; + } + return kXiMass; + } + + // K0S proper lifetime (cm/c), the expression of the original task + template + static double k0sProperLifetime(const CollisionType& collision, const V0Type& v0) + { + float dx = v0.decayVtxX() - collision.posX(); + float dy = v0.decayVtxY() - collision.posY(); + float dz = v0.decayVtxZ() - collision.posZ(); + float l = std::sqrt(dx * dx + dy * dy + dz * dz); + float p = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); + return (l / (p + SmallNumber)) * o2::constants::physics::MassK0Short; + } + + // K0S selection of mode A; equivalent to v0CutEnhanced of the original task. + // The collision is the one of the V0 (its primary vertex defines the proper lifetime). + template + [[nodiscard]] int k0sSelectionStage(const CollisionType& collision, const V0Type& v0) const + { + const auto& cut = mK0sCuts; + if (std::abs(v0.eta()) > cut.cMaxV0Etacut.value || v0.pt() < mXiCuts.cMinPtcut.value) { + return kK0sInput; + } + if (v0.v0CosPA() < cut.cK0sMinCosPA.value || v0.daughDCA() > cut.cK0sMaxDaughDCA.value) { + return kK0sKinematics; + } + if (std::abs(v0.dcapostopv()) < cut.cK0sDauPosDCAtoPVMin.value || std::abs(v0.dcanegtopv()) < cut.cK0sDauNegDCAtoPVMin.value) { + return kK0sKinematics; + } + auto radius = v0.transRadius(); + if (radius < cut.cK0sRadiusMin.value || radius > cut.cK0sRadiusMax.value) { + return kK0sKinematics; + } + if (std::abs(v0.dcav0topv()) > MaxDCAV0ToPV) { + return kK0sKinematics; + } + if (k0sProperLifetime(collision, v0) > cut.cK0sProperLifetimeMax.value) { + return kK0sTopology; + } + if (v0.qtarm() < cut.cK0sArmenterosQtMin.value) { + return kK0sTopology; + } + if (std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) > cut.cK0sMassWindow.value) { + return kK0sLifetime; + } + if (cut.cK0sCrossMassRejection.value) { + if (std::abs(v0.mLambda() - o2::constants::physics::MassLambda) < cut.cK0sCrossMassRejectionWindow.value || + std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda) < cut.cK0sCrossMassRejectionWindow.value) { + return kK0sLifetime; + } + } + if (std::abs(v0.daughterTPCNSigmaPosPi()) >= cut.cK0sDaughterPiTPCNSigmaMax.value || + std::abs(v0.daughterTPCNSigmaNegPi()) >= cut.cK0sDaughterPiTPCNSigmaMax.value) { + return kK0sMass; + } + if (v0.nCrossedRowsPos() <= cut.cK0sPosDaughterMinCrossedRows.value || v0.nCrossedRowsNeg() <= cut.cK0sNegDaughterMinCrossedRows.value) { + return kK0sMass; + } + if (v0.qtarm() < cut.cK0sArmenterosAlphaCoeff.value * std::fabs(v0.alpha())) { + return kK0sDaughters; + } + return kK0sArmenteros; + } + + // Xi-K0S opening angle and the pT-dependent opening-angle window of the original task (kinCuts) + template + [[nodiscard]] bool passesKinematicCut(const FirstVecT& firstDaughter, const SecondVecT& secondDaughter, const MotherVecT& mother, float& alpha) const + { + auto firstP = std::sqrt(firstDaughter.Px() * firstDaughter.Px() + firstDaughter.Py() * firstDaughter.Py() + firstDaughter.Pz() * firstDaughter.Pz()); + auto secondP = std::sqrt(secondDaughter.Px() * secondDaughter.Px() + secondDaughter.Py() * secondDaughter.Py() + secondDaughter.Pz() * secondDaughter.Pz()); + if (firstP < SmallNumber || secondP < SmallNumber) { + alpha = 0.f; + return false; + } + + auto cosAlpha = (firstDaughter.Px() * secondDaughter.Px() + firstDaughter.Py() * secondDaughter.Py() + firstDaughter.Pz() * secondDaughter.Pz()) / (firstP * secondP); + if (cosAlpha > 1.) { + cosAlpha = 1.; + } else if (cosAlpha < -1.) { + cosAlpha = -1.; + } + alpha = std::acos(cosAlpha); + + const auto& kinCutsPt = mK0sCuts.cKinCutsPt.value; + const auto& kinLowerCutsAlpha = mK0sCuts.cKinLowerCutsAlpha.value; + const auto& kinUpperCutsAlpha = mK0sCuts.cKinUpperCutsAlpha.value; + + int kinCutsSize = static_cast(kinUpperCutsAlpha.size()); + if (kinCutsSize > static_cast(kinLowerCutsAlpha.size())) { + kinCutsSize = static_cast(kinLowerCutsAlpha.size()); + } + if (kinCutsSize > static_cast(kinCutsPt.size()) - 1) { + kinCutsSize = static_cast(kinCutsPt.size()) - 1; + } + + for (int i = 0; i < kinCutsSize; ++i) { + if ((mother.Pt() > kinCutsPt[i] && mother.Pt() <= kinCutsPt[i + 1]) && (alpha < kinLowerCutsAlpha[i] || alpha > kinUpperCutsAlpha[i])) { + return false; + } + } + return true; + } + + // Full selection stage of a mode-B track (quality, TOF requirement, PID) + template + int trackSelectionStage(const TrackType& track) + { + return speciesStage(track, mSelection.trackQualityStage(track)); + } + + template + int pionSelectionStage(const TrackType& track) + { + return trackSelectionStage(track); + } + + template + int kaonSelectionStage(const TrackType& track) + { + return trackSelectionStage(track); + } + + // Mode A: (Xi, K0S) enumeration of one collision, or of one mixed pair (Xi from collision, K0S from v0Collision). + // The order of the original task is kept: K0S selection, Xi selection, then Xi (outer) x K0S (inner). + // Hooks (nullptr to skip): + // - onK0s(v0, properLifetime, selected): every V0, in table order, + // - onXi(xi, selected): every cascade, in table order, + // - onCandidate(xi, v0, XiK0sCandidateValues): every pair of selected objects without shared daughters + // (the daughter-ID check is skipped in mixed events); kinematic and rapidity flags are in the values, + // - onExport(collision, xi, v0, values, passBits): same-event candidates at the loose or selected stage. + template + void forEachXiK0sCandidate(o2::framework::HistogramRegistry& histos, const CollisionType& collision, const V0CollisionType& v0Collision, + const CascadesType& cascades, const V0sType& v0s, bool computeValues, + XiHook onXi = nullptr, K0sHook onK0s = nullptr, CandidateHook onCandidate = nullptr, ExportHook onExport = nullptr) + { + constexpr bool HasXiHook = !std::is_same_v; + constexpr bool HasK0sHook = !std::is_same_v; + constexpr bool HasCandidateHook = !std::is_same_v; + constexpr bool HasExportHook = !std::is_same_v; + constexpr bool FillCutFlow = !IsMix; + bool visitLoose = false; + bool checkValidity = false; + if constexpr (!IsMix) { + visitLoose = mLooseOptions.audit || (!mLooseOptions.exportSelected && HasExportHook); + checkValidity = visitLoose || (mLooseOptions.exportSelected && HasExportHook); + } + + // K0S selection cache + using V0Row = std::decay_t; + std::vector> k0sList; + k0sList.reserve(v0s.size()); + for (const auto& v0 : v0s) { + const int stage = k0sSelectionStage(v0Collision, v0); + const bool selected = stage == kK0sSelected; + if constexpr (HasK0sHook) { + onK0s(v0, k0sProperLifetime(v0Collision, v0), selected); + } + if constexpr (FillCutFlow) { + for (int i = 0; i <= stage; ++i) { + histos.fill(HIST("CutFlow/xiK0s/k0s"), i); + } + } + if (selected || visitLoose) { + const auto indices = v0.indices(); + k0sList.push_back({v0, {indices[0], indices[1]}, selected}); + } + } + + // Xi selection cache + using XiRow = std::decay_t; + std::vector> xiList; + xiList.reserve(cascades.size()); + for (const auto& xi : cascades) { + const int stage = xiSelectionStage(xi); + const bool selected = stage == kXiSelected; + if constexpr (HasXiHook) { + onXi(xi, selected); + } + if constexpr (FillCutFlow) { + for (int i = 0; i <= stage; ++i) { + histos.fill(HIST("CutFlow/xiK0s/xi"), i); + } + } + if (selected || visitLoose) { + const auto indices = xi.cascadeIndices(); + xiList.push_back({xi, {indices[0], indices[1], indices[2]}, selected}); + } + } + + const bool kinCutsOn = mK0sCuts.cKinCuts.value; + const float rapidityMax = mCandidateCuts.cfgRapidityCut.value; + for (const auto& cachedXi : xiList) { + const auto& xi = cachedXi.row; + for (const auto& cachedK0s : k0sList) { + const auto& v0 = cachedK0s.row; + const bool objectsSelected = cachedXi.selected && cachedK0s.selected; + bool truthMatched = false; + if constexpr (IsMC && !IsMix) { + truthMatched = classifyXiK0sTruth(xi, v0) == XiK0sTruth::Matched; + } + auto countCandidate = [&](int stage) { + if constexpr (FillCutFlow) { + if (objectsSelected) { + histos.fill(HIST("CutFlow/xiK0s/candidates"), stage, 0); + if (truthMatched) { + histos.fill(HIST("CutFlow/xiK0s/candidates"), stage, 1); + } + } + } + }; + countCandidate(0); + if constexpr (!IsMix) { + if (sharesAnyDaughterId(cachedXi.daughterIds, cachedK0s.daughterIds)) { + continue; + } + } + countCandidate(1); + + // 4-vectors (construction of the original task) + XiK0sCandidateValues values; + values.xi = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(xi.pt(), xi.eta(), xi.phi(), xi.mXi())); + values.k0s = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(v0.pt(), v0.eta(), v0.phi(), o2::constants::physics::MassK0Short)); + values.omega = values.xi + values.k0s; + if (kinCutsOn || computeValues || checkValidity) { + float alpha = 0.f; + const bool kinCutFlag = passesKinematicCut(values.xi, values.k0s, values.omega, alpha); + values.alpha = alpha; + if (kinCutsOn) { + values.passesKinCut = kinCutFlag; + } + } + values.inRapidity = !(std::abs(values.omega.Rapidity()) >= rapidityMax); + if (values.passesKinCut) { + countCandidate(2); + if (values.inRapidity) { + countCandidate(3); + } + } + + if constexpr (!IsMix) { + if (checkValidity) { + exportXiK0s(histos, collision, xi, v0, values, cachedXi.selected, cachedK0s.selected, truthMatched, visitLoose, onExport); + } + } + if constexpr (HasCandidateHook) { + if (objectsSelected) { + onCandidate(xi, v0, values); + } + } + } + } + } + + // Mode B: (Xi, pion, kaon) enumeration of one collision, or of one mixed pair (Xi from collision, tracks from the other). + // trackIds: ResoTrackTracks for full ResoTracks (positional source track IDs), unused for ResoMicroTracks_001 (trackId()). + // Hooks (nullptr to skip): + // - onXi(xi, selected): every cascade, in table order, + // - onTrack(track, pionStage, kaonStage): every track, in table order (TrackStage values), + // - onCandidate(xi, pion, kaon, Xi1530KCandidateValues): every triplet of selected objects with five distinct + // source track IDs (no Xi-daughter check in mixed events) inside the Xi(1530) window; all charge patterns, + // - onExport(collision, xi, pion, kaon, values, passBits): same-event micro candidates at the loose or selected stage. + template + void forEachXi1530KCandidate(o2::framework::HistogramRegistry& histos, const CollisionType& collision, + const CascadesType& cascades, const TracksType& tracks, const TrackIdsType& trackIds, bool computeValues, + XiHook onXi = nullptr, TrackHook onTrack = nullptr, CandidateHook onCandidate = nullptr, ExportHook onExport = nullptr) + { + constexpr bool HasXiHook = !std::is_same_v; + constexpr bool HasTrackHook = !std::is_same_v; + constexpr bool HasCandidateHook = !std::is_same_v; + constexpr bool HasExportHook = !std::is_same_v; + constexpr bool FillCutFlow = !IsMix; + bool visitLoose = false; + bool checkValidity = false; + if constexpr (IsResoMicrotrack && !IsMix) { + visitLoose = mLooseOptions.audit || (!mLooseOptions.exportSelected && HasExportHook); + checkValidity = visitLoose || (mLooseOptions.exportSelected && HasExportHook); + } + + // Xi selection cache + using XiRow = std::decay_t; + std::vector> xiList; + xiList.reserve(cascades.size()); + for (const auto& xi : cascades) { + const int stage = xiSelectionStage(xi); + const bool selected = stage == kXiSelected; + if constexpr (HasXiHook) { + onXi(xi, selected); + } + if constexpr (FillCutFlow) { + for (int i = 0; i <= stage; ++i) { + histos.fill(HIST("CutFlow/xi1530K/xi"), i); + } + } + if (selected || visitLoose) { + const auto indices = xi.cascadeIndices(); + xiList.push_back({xi, {indices[0], indices[1], indices[2]}, selected}); + } + } + + // Track selection cache: every track is selected once per collision, for both species + const std::size_t nTracks = tracks.size(); + if (nTracks == 0) { + return; + } + const int64_t firstIndex = tracks.begin().index(); + std::vector trackCache(nTracks); + for (const auto& track : tracks) { + auto& entry = trackCache[getCacheIndex(track, firstIndex, nTracks)]; + const int qualityStage = mSelection.trackQualityStage(track); + const int pionStage = speciesStage(track, qualityStage); + const int kaonStage = speciesStage(track, qualityStage); + entry.sourceId = sourceTrackId(track, trackIds); + entry.quality = qualityStage == TrackStage::kTrkClusters; + entry.pionSelected = pionStage == TrackStage::kTrkPID; + entry.kaonSelected = kaonStage == TrackStage::kTrkPID; + if constexpr (HasTrackHook) { + onTrack(track, pionStage, kaonStage); + } + if constexpr (FillCutFlow) { + for (int i = 0; i <= pionStage; ++i) { + histos.fill(HIST("CutFlow/xi1530K/tracks"), i, static_cast(Species::Pion)); + } + for (int i = 0; i <= kaonStage; ++i) { + histos.fill(HIST("CutFlow/xi1530K/tracks"), i, static_cast(Species::Kaon)); + } + } + } + + const float rapidityMax = mCandidateCuts.cfgRapidityCut.value; + const bool useWindow = mXi1530KCuts.cXi1530UseMassWindow.value; + const float windowCenter = mXi1530KCuts.cXi1530Mass.value; + const float windowWidth = mXi1530KCuts.cXi1530MassWindow.value; + for (const auto& cachedXi : xiList) { + const auto& xi = cachedXi.row; + const int xiSign = xi.sign() < 0 ? -1 : 1; + const ROOT::Math::PxPyPzEVector pXi(ROOT::Math::PtEtaPhiMVector(xi.pt(), xi.eta(), xi.phi(), xi.mXi())); + for (const auto& pion : tracks) { + const auto& pionEntry = trackCache[getCacheIndex(pion, firstIndex, nTracks)]; + if (!pionEntry.pionSelected && !visitLoose) { + continue; + } + if constexpr (!IsMix) { + if (sharesDaughterId(cachedXi.daughterIds, pionEntry.sourceId)) { + continue; + } + } + const bool pairSelected = cachedXi.selected && pionEntry.pionSelected; + const bool pionSignal = pion.sign() != xiSign; + const ROOT::Math::PxPyPzEVector pPion(ROOT::Math::PtEtaPhiMVector(pion.pt(), pion.eta(), pion.phi(), o2::constants::physics::MassPionCharged)); + const ROOT::Math::PxPyPzEVector pXi1530 = pXi + pPion; + const float massXiPi = pXi1530.M(); + const bool inWindow = !useWindow || std::abs(massXiPi - windowCenter) < windowWidth; + if constexpr (FillCutFlow) { + if (pairSelected) { + countXi1530K(histos, 0, pionSignal, false); + if (inWindow) { + countXi1530K(histos, 1, pionSignal, false); + } + } + } + if (!(pairSelected && inWindow) && !visitLoose) { + continue; + } + for (const auto& kaon : tracks) { + if (kaon.index() == pion.index()) { + continue; + } + const auto& kaonEntry = trackCache[getCacheIndex(kaon, firstIndex, nTracks)]; + if (!kaonEntry.kaonSelected && !visitLoose) { + continue; + } + if (kaonEntry.sourceId == pionEntry.sourceId) { + continue; + } + if constexpr (!IsMix) { + if (sharesDaughterId(cachedXi.daughterIds, kaonEntry.sourceId)) { + continue; + } + } + const bool tripletSelected = pairSelected && inWindow && kaonEntry.kaonSelected; + Xi1530KCandidateValues values; + values.xi = pXi; + values.pion = pPion; + values.kaon = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(kaon.pt(), kaon.eta(), kaon.phi(), o2::constants::physics::MassKaonCharged)); + values.xi1530 = pXi1530; + values.omega = pXi1530 + values.kaon; + values.massXiPi = massXiPi; + values.chargePattern = o2::analysis::omega2012ml::chargePattern(xiSign, pion.sign(), kaon.sign()); + values.inXi1530Window = inWindow; + values.inRapidity = !(std::abs(values.omega.Rapidity()) >= rapidityMax); + if (computeValues || checkValidity) { + values.massXiK = (pXi + values.kaon).M(); + values.massPiK = (pPion + values.kaon).M(); + values.openingAngleXi1530K = ROOT::Math::VectorUtil::Angle(pXi1530, values.kaon); + values.cosThetaStar = static_cast(o2::analysis::omega2012ml::detail::cosThetaStar( + o2::analysis::omega2012ml::detail::LorentzVector(pXi1530), o2::analysis::omega2012ml::detail::LorentzVector(values.omega))); + } + bool truthMatched = false; + if constexpr (IsMC && !IsMix) { + truthMatched = classifyXi1530KTruth(xi, pion, kaon) == Xi1530KTruth::Matched; + } + if constexpr (FillCutFlow) { + if (tripletSelected) { + const bool signal = values.chargePattern == o2::analysis::omega2012ml::kSignalPattern; + countXi1530K(histos, 2, signal, truthMatched); + if (values.inRapidity) { + countXi1530K(histos, 3, signal, truthMatched); + } + } + } + if constexpr (IsResoMicrotrack && !IsMix) { + if (checkValidity) { + exportXi1530K(histos, collision, xi, pion, kaon, values, cachedXi.selected, + pionEntry.quality && kaonEntry.quality, pionEntry.pionSelected && kaonEntry.kaonSelected, + truthMatched, visitLoose, onExport); + } + } + if constexpr (HasCandidateHook) { + if (tripletSelected) { + onCandidate(xi, pion, kaon, values); + } + } + } + } + } + } + + // Generated Omega(2012) parents of a selected reconstructed MC collision (ResoMCParents_001). The caller supplies + // the denominator rapidity limit so it can remain independent of a wider candidate-export window. The optional + // callback receives (parent, immediate channel) for the parents inside that rapidity window. Split reconstructed + // collisions repeat parent sets: this is not an unconditional generated denominator. + template + void forEachGeneratedOmega2012(o2::framework::HistogramRegistry& histos, const ParentsType& resoParents, float rapidityMax, Callback callback = nullptr) + { + for (const auto& part : resoParents) { + if (std::abs(part.pdgCode()) != o2::constants::physics::Pdg::kOmega2012Minus) { + continue; + } + const GeneratedChannel channel = classifyGeneratedOmega2012(part.pdgCode(), part.daughterPDG1(), part.daughterPDG2()); + histos.fill(HIST("CutFlow/generated"), 0, static_cast(channel)); + if (!(std::abs(part.y()) < rapidityMax)) { + continue; + } + histos.fill(HIST("CutFlow/generated"), 1, static_cast(channel)); + if constexpr (!std::is_same_v) { + callback(part, channel); + } + } + } + + template + static bool sharesAnyDaughterId(const std::array& first, const std::array& second) + { + for (const auto& firstId : first) { + for (const auto& secondId : second) { + if (firstId == secondId) { + return true; + } + } + } + return false; + } + + template + static bool sharesDaughterId(const std::array& daughters, int64_t trackId) + { + for (const auto& daughterId : daughters) { + if (static_cast(daughterId) == trackId) { + return true; + } + } + return false; + } + + // Source track ID: trackId() of ResoMicroTracks_001, or the positional ResoTrackTracks row of a ResoTracks row. + template + static int64_t sourceTrackId(const TrackType& track, const TrackIdsType& trackIds) + { + if constexpr (requires { track.trackId(); }) { + return static_cast(track.trackId()); + } else { + const auto rowIndex = track.globalIndex(); + if (rowIndex >= 0 && rowIndex < static_cast(trackIds.size())) { + return static_cast(trackIds.rawIteratorAt(rowIndex).trackId()); + } + return static_cast(rowIndex); + } + } + + private: + template + struct CachedObject { + Row row; + std::array daughterIds{}; + bool selected = false; + }; + + struct TrackCacheEntry { + int64_t sourceId = -1; + bool quality = false; + bool pionSelected = false; + bool kaonSelected = false; + }; + + template + int speciesStage(const TrackType& track, int qualityStage) + { + if (qualityStage < TrackStage::kTrkClusters) { + return qualityStage; + } + constexpr int SpeciesIndex = static_cast(S); + if (!mSelection.passesTOFRequired(SpeciesIndex, track)) { + return TrackStage::kTrkClusters; + } + const bool hasTOF = track.hasTOF(); + const double tpcNSigma = (S == Species::Pion) ? track.tpcNSigmaPi() : track.tpcNSigmaKa(); + double tofNSigma = std::numeric_limits::quiet_NaN(); // TOF value is only valid with hasTOF + if (hasTOF) { + tofNSigma = (S == Species::Pion) ? track.tofNSigmaPi() : track.tofNSigmaKa(); + } + if (!mSelection.passesPID(SpeciesIndex, track.pt(), hasTOF, tpcNSigma, tofNSigma)) { + return TrackStage::kTrkTOFRequired; + } + return TrackStage::kTrkPID; + } + + template + static std::size_t getCacheIndex(const TrackType& track, int64_t firstIndex, std::size_t size) + { + const int64_t index = static_cast(track.index()) - firstIndex; + if (index < 0 || index >= static_cast(size)) { + LOG(fatal) << "Track index " << track.index() << " is outside the selection cache [" << firstIndex << ", " << firstIndex + static_cast(size) << ")"; + } + return static_cast(index); + } + + static void countXi1530K(o2::framework::HistogramRegistry& histos, int stage, bool signalPattern, bool truthMatched) + { + histos.fill(HIST("CutFlow/xi1530K/candidates"), stage, 0); + if (signalPattern) { + histos.fill(HIST("CutFlow/xi1530K/candidates"), stage, 1); + } + if (truthMatched) { + histos.fill(HIST("CutFlow/xi1530K/candidates"), stage, 2); + } + } + + // Loose / selected stage of a mode-A same-event candidate + template + void exportXiK0s(o2::framework::HistogramRegistry& histos, const CollisionType& collision, const XiType& xi, const V0Type& v0, + XiK0sCandidateValues const& values, bool xiSelected, bool k0sSelected, bool truthMatched, + bool visitLoose, ExportHook onExport) + { + constexpr bool HasExportHook = !std::is_same_v; + namespace ml = o2::analysis::omega2012ml; + const auto canonical = ml::canonicalizeXiK0s(ml::makeCascadeSnapshot(collision, xi), ml::makeV0Snapshot(collision, v0)); + const bool valid = canonical.status == ml::BuildStatus::Ok && + std::isfinite(values.omega.M()) && std::isfinite(values.omega.Pt()) && std::isfinite(values.omega.Rapidity()) && + ml::buildXiK0sFeatures(canonical.candidate).status == ml::BuildStatus::Ok; + if (!valid) { + return; + } + auto countMl = [&](int stage) { + if (mLooseOptions.audit) { + histos.fill(HIST("ML/xiK0s/looseCutflow"), stage, 0); + if (truthMatched) { + histos.fill(HIST("ML/xiK0s/looseCutflow"), stage, 1); + } + } + }; + countMl(0); + if (!values.inRapidity) { + return; + } + const bool selected = xiSelected && k0sSelected && values.passesKinCut; + if (visitLoose) { + countMl(1); + if (mLooseOptions.audit) { + histos.fill(HIST("ML/xiK0s/looseMassPtActivity"), values.omega.M(), values.omega.Pt(), collision.cent()); + } + uint16_t passBits = kXiK0sPassLoose; + if (xiSelected) { + passBits |= kXiK0sPassXi; + countMl(2); + if (k0sSelected) { + passBits |= kXiK0sPassK0s; + countMl(3); + if (values.passesKinCut) { + passBits |= kXiK0sPassKinematic; + countMl(4); + } + } + } + if constexpr (HasExportHook) { + if (!mLooseOptions.exportSelected) { + onExport(collision, xi, v0, values, passBits); + } + } + } + if constexpr (HasExportHook) { + if (mLooseOptions.exportSelected && selected) { + onExport(collision, xi, v0, values, XiK0sPassBitsSelected); + } + } + } + + // Loose / selected stage of a mode-B same-event micro candidate + template + void exportXi1530K(o2::framework::HistogramRegistry& histos, const CollisionType& collision, const XiType& xi, + const TrackType& pion, const TrackType& kaon, Xi1530KCandidateValues const& values, + bool xiSelected, bool qualityBoth, bool pidBoth, bool truthMatched, bool visitLoose, ExportHook onExport) + { + constexpr bool HasExportHook = !std::is_same_v; + namespace ml = o2::analysis::omega2012ml; + const auto canonical = ml::canonicalizeXi1530K(ml::makeCascadeSnapshot(collision, xi), ml::makeTrackSnapshot(pion), ml::makeTrackSnapshot(kaon)); + const bool valid = canonical.status == ml::BuildStatus::Ok && + std::isfinite(values.omega.M()) && std::isfinite(values.omega.Pt()) && std::isfinite(values.omega.Rapidity()) && + ml::buildXi1530KFeatures(canonical.candidate).status == ml::BuildStatus::Ok; + if (!valid) { + return; + } + const int category = values.chargePattern == ml::kSignalPattern ? 0 : 1; + auto countMl = [&](int stage) { + if (mLooseOptions.audit) { + histos.fill(HIST("ML/xi1530K/looseCutflow"), stage, category); + if (truthMatched) { + histos.fill(HIST("ML/xi1530K/looseCutflow"), stage, 2); + } + } + }; + countMl(0); + if (!values.inRapidity) { + return; + } + const bool selected = xiSelected && qualityBoth && pidBoth && values.inXi1530Window; + if (visitLoose) { + countMl(1); + if (mLooseOptions.audit && category == 0) { + histos.fill(HIST("ML/xi1530K/looseMassPtActivity"), values.omega.M(), values.omega.Pt(), collision.cent()); + } + uint16_t passBits = kXi1530KPassLoose; + if (xiSelected) { + passBits |= kXi1530KPassXi; + countMl(2); + if (qualityBoth) { + passBits |= kXi1530KPassQuality; + countMl(3); + if (pidBoth) { + passBits |= kXi1530KPassPID; + countMl(4); + if (values.inXi1530Window) { + passBits |= kXi1530KPassWindow; + countMl(5); + } + } + } + } + if constexpr (HasExportHook) { + if (!mLooseOptions.exportSelected) { + onExport(collision, xi, pion, kaon, values, passBits); + } + } + } + if constexpr (HasExportHook) { + if (mLooseOptions.exportSelected && selected) { + onExport(collision, xi, pion, kaon, values, Xi1530KPassBitsSelected); + } + } + } + + // Cut-flow instrumentation of the selection, per decay mode; the output histograms belong to the tasks. + void registerHistograms(o2::framework::HistogramRegistry& histos, ProcessModes const& modes) + { + using o2::framework::HistType; + const std::array xiLabels{"input", "|#eta|, p_{T}", "DCA to PV", "#Lambda topology", "cascade topology", "#Xi mass"}; + if (modes.xiK0s) { + auto xiFlow = histos.add("CutFlow/xiK0s/xi", "XiK0s mode: cascades, once per selected collision;stage;cascades", HistType::kTH1D, {{kXiNStages, -0.5, static_cast(kXiNStages) - 0.5}}); + for (std::size_t i = 0; i < xiLabels.size(); ++i) { + xiFlow->GetXaxis()->SetBinLabel(i + 1, xiLabels[i]); + } + auto k0sFlow = histos.add("CutFlow/xiK0s/k0s", "XiK0s mode: V0s, once per selected collision;stage;V0s", HistType::kTH1D, {{kK0sNStages, -0.5, static_cast(kK0sNStages) - 0.5}}); + const std::array k0sLabels{"input", "|#eta|, p_{T}", "topology", "lifetime, min q_{T}", "mass, #Lambda rejection", "daughters", "Armenteros"}; + for (std::size_t i = 0; i < k0sLabels.size(); ++i) { + k0sFlow->GetXaxis()->SetBinLabel(i + 1, k0sLabels[i]); + } + auto candidateFlow = histos.add("CutFlow/xiK0s/candidates", "XiK0s mode: selected Xi x selected K0S, same event;stage;category", HistType::kTH2D, + {{NXiK0sCandidateStages, -0.5, NXiK0sCandidateStages - 0.5}, {2, -0.5, 1.5}}); + const std::array candidateLabels{"selected pairs", "distinct daughters", "kinematic cut", "rapidity"}; + for (std::size_t i = 0; i < candidateLabels.size(); ++i) { + candidateFlow->GetXaxis()->SetBinLabel(i + 1, candidateLabels[i]); + } + candidateFlow->GetYaxis()->SetBinLabel(1, "all"); + candidateFlow->GetYaxis()->SetBinLabel(2, "XiK0s truth"); + if (mLooseOptions.audit) { + auto flow = histos.add("ML/xiK0s/looseCutflow", "XiK0s mode: valid canonical candidates;stage;category", HistType::kTH2D, {{5, -0.5, 4.5}, {2, -0.5, 1.5}}); + const std::array labels{"valid canonical", "loose acceptance", "Xi selection", "K0S selection", "kinematic cut"}; + for (std::size_t i = 0; i < labels.size(); ++i) { + flow->GetXaxis()->SetBinLabel(i + 1, labels[i]); + } + flow->GetYaxis()->SetBinLabel(1, "all"); + flow->GetYaxis()->SetBinLabel(2, "XiK0s truth"); + histos.add("ML/xiK0s/looseMassPtActivity", "XiK0s mode loose candidates;mass (GeV/c^{2});pT (GeV/c);centrality", HistType::kTH3D, + {{300, 1.6, 2.8}, {{0., 0.5, 1., 2., 3., 5., 8., 15., 30., 100.}, "pT"}, {{0., 10., 30., 50., 70., 100., 110.}, "centrality"}}); + } + } + if (modes.xi1530K) { + auto xiFlow = histos.add("CutFlow/xi1530K/xi", "Xi1530K mode: cascades, once per selected collision;stage;cascades", HistType::kTH1D, {{kXiNStages, -0.5, static_cast(kXiNStages) - 0.5}}); + for (std::size_t i = 0; i < xiLabels.size(); ++i) { + xiFlow->GetXaxis()->SetBinLabel(i + 1, xiLabels[i]); + } + constexpr int NTrackStages = TrackStage::kTrkNStages; + auto trackFlow = histos.add("CutFlow/xi1530K/tracks", "Xi1530K mode: tracks, once per selected collision;stage;species", HistType::kTH2D, + {{NTrackStages, -0.5, NTrackStages - 0.5}, {2, -0.5, 1.5}}); + const std::array trackLabels{"input", "pT", "eta", "DCAxy", "DCAz", "track flags", "clusters / crossed rows", "TOF required", "PID"}; + for (std::size_t i = 0; i < trackLabels.size(); ++i) { + trackFlow->GetXaxis()->SetBinLabel(i + 1, trackLabels[i]); + } + trackFlow->GetYaxis()->SetBinLabel(1, "pion"); + trackFlow->GetYaxis()->SetBinLabel(2, "kaon"); + auto candidateFlow = histos.add("CutFlow/xi1530K/candidates", "Xi1530K mode: same event;stage;category", HistType::kTH2D, + {{NXi1530KCandidateStages, -0.5, NXi1530KCandidateStages - 0.5}, {3, -0.5, 2.5}}); + const std::array candidateLabels{"selected Xi #pi pairs", "Xi(1530) window", "selected Xi #pi K triplets", "rapidity"}; + for (std::size_t i = 0; i < candidateLabels.size(); ++i) { + candidateFlow->GetXaxis()->SetBinLabel(i + 1, candidateLabels[i]); + } + candidateFlow->GetYaxis()->SetBinLabel(1, "all"); + candidateFlow->GetYaxis()->SetBinLabel(2, "signal charge pattern"); + candidateFlow->GetYaxis()->SetBinLabel(3, "Xi1530K truth"); + if (mLooseOptions.audit) { + auto flow = histos.add("ML/xi1530K/looseCutflow", "Xi1530K mode: valid canonical candidates;stage;category", HistType::kTH2D, {{6, -0.5, 5.5}, {3, -0.5, 2.5}}); + const std::array labels{"valid canonical", "loose acceptance", "Xi selection", "track quality", "TOF + PID", "Xi(1530) window"}; + for (std::size_t i = 0; i < labels.size(); ++i) { + flow->GetXaxis()->SetBinLabel(i + 1, labels[i]); + } + flow->GetYaxis()->SetBinLabel(1, "signal charge pattern"); + flow->GetYaxis()->SetBinLabel(2, "wrong-sign control"); + flow->GetYaxis()->SetBinLabel(3, "Xi1530K truth"); + histos.add("ML/xi1530K/looseMassPtActivity", "Xi1530K mode loose signal-pattern candidates;mass (GeV/c^{2});pT (GeV/c);centrality", HistType::kTH3D, + {{300, 1.8, 3.0}, {{0., 0.5, 1., 2., 3., 5., 8., 15., 30., 100.}, "pT"}, {{0., 10., 30., 50., 70., 100., 110.}, "centrality"}}); + } + } + if (modes.mcGen) { + auto generated = histos.add("CutFlow/generated", "Generated Omega(2012) parent rows of selected reconstructed events;stage;immediate channel", HistType::kTH2D, + {{2, -0.5, 1.5}, {NGeneratedChannels, -0.5, NGeneratedChannels - 0.5}}); + generated->GetXaxis()->SetBinLabel(1, "all parent rows"); + generated->GetXaxis()->SetBinLabel(2, "rapidity window"); + generated->GetYaxis()->SetBinLabel(1, "other / unresolved"); + generated->GetYaxis()->SetBinLabel(2, "Xi K0S"); + generated->GetYaxis()->SetBinLabel(3, "Xi(1530) K"); + } + } + + ResoAnalysisSelectionCore mSelection; + XiCuts mXiCuts; + K0sCuts mK0sCuts; + Xi1530KCuts mXi1530KCuts; + CandidateCuts mCandidateCuts; + LooseStageOptions mLooseOptions; +}; + +} // namespace o2::analysis::omega2012 + +#endif // PWGLF_CORE_OMEGA2012ANALYSISCORE_H_ diff --git a/PWGLF/Core/Omega2012MlFeatures.h b/PWGLF/Core/Omega2012MlFeatures.h new file mode 100644 index 00000000000..345a6c70c38 --- /dev/null +++ b/PWGLF/Core/Omega2012MlFeatures.h @@ -0,0 +1,1387 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file Omega2012MlFeatures.h +/// \brief Snapshots, canonical candidates and the two separate feature contracts of the Omega(2012) decay modes +/// \author Bong-Hwi Lim +/// +/// Mode A (XiK0s): Omega(2012)- -> Xi- K0S. Mode B (Xi1530K): Omega(2012)- -> Xi(1530)0 K- -> Xi- pi+ K-. +/// The two modes have independent, separately versioned contracts (feature_contract_omega2012_v1.json); +/// no feature vector, status or projection is shared between them. + +#ifndef PWGLF_CORE_OMEGA2012MLFEATURES_H_ +#define PWGLF_CORE_OMEGA2012MLFEATURES_H_ + +#include "PWGLF/DataModel/LFResonanceTables.h" + +#include +#include + +#include +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace o2::analysis::omega2012ml +{ +inline constexpr std::size_t NItsLayers = 7; +inline constexpr std::size_t NCascadeDaughters = 3; +inline constexpr std::size_t NV0Daughters = 2; +inline constexpr std::size_t NPidSpecies = 3; +inline constexpr std::size_t XiK0sNMasterFeatures = 63; +inline constexpr std::size_t Xi1530KNMasterFeatures = 126; +inline constexpr int8_t SaturatedLow = std::numeric_limits::lowest(); // x10 nSigma code of -inf / missing TOF +inline constexpr int8_t SaturatedHigh = std::numeric_limits::max(); // x10 nSigma code of NaN / +inf +inline constexpr float NSigmaScale = 10.f; +inline constexpr float MicroPidOverflow = 3.5f; + +enum class Profile : uint8_t { DetectorV1, + RelationalV1, + StudyV1 }; +enum class BuildStatus : uint8_t { + Ok, + InvalidChargePattern, + ReusedDaughter, + InvalidMomentum, + InvalidKinematics, + InvalidContract +}; + +/// Charge pattern of a mode-B candidate relative to the Xi charge (metadata, never a feature). +enum ChargePattern : uint8_t { + kSignalPattern = 0, // pion opposite to the Xi charge, kaon equal to it (Xi- pi+ K- and conjugate) + kWrongSignPion = 1, // pion with the Xi charge + kWrongSignKaon = 2, // kaon opposite to the Xi charge + kWrongSignBoth = 3 // both wrong +}; + +/// All ResoCascades scalars of one cascade plus derived decay lengths. +/// Daughter arrays: index 0 positive, 1 negative, 2 bachelor; nSigma: [daughter * 3 + species], species pi, ka, pr. +struct CascadeSnapshot { + int64_t sourceRow = -1; // row of the cascade in the input ResoCascades table + int8_t sign = 0; + float px = 0.f, py = 0.f, pz = 0.f; + float mXi = 0.f, mLambda = 0.f; + float v0CosPA = 0.f, cascCosPA = 0.f, v0DaughDCA = 0.f, cascDaughDCA = 0.f; + float dcaPosToPV = 0.f, dcaNegToPV = 0.f, dcaBachToPV = 0.f, dcaV0ToPV = 0.f, dcaXYToPV = 0.f, dcaZToPV = 0.f; + float v0Radius = 0.f, cascRadius = 0.f; + float decayVtxX = 0.f, decayVtxY = 0.f, decayVtxZ = 0.f; + std::array tpcNSigma10{}; + std::array tofNSigma10{}; + std::array crossedRows{}; + std::array daughterIds{}; + float decayLength = 0.f; // 3D distance from the primary vertex to the decay vertex + float properLength = 0.f; // decayLength * MassXiMinus / p +}; + +/// All ResoV0s scalars of one V0 plus derived decay lengths. Daughter arrays: index 0 positive, 1 negative. +struct V0Snapshot { + int64_t sourceRow = -1; // row of the V0 in the input ResoV0s table + float px = 0.f, py = 0.f, pz = 0.f; + float mK0Short = 0.f, mLambda = 0.f, mAntiLambda = 0.f; + float cosPA = 0.f, daughDCA = 0.f, dcaPosToPV = 0.f, dcaNegToPV = 0.f, dcaV0ToPV = 0.f, radius = 0.f; + float decayVtxX = 0.f, decayVtxY = 0.f, decayVtxZ = 0.f; + float alpha = 0.f, qtarm = 0.f; + std::array tpcNSigma10{}; + std::array tofNSigma10{}; + std::array crossedRows{}; + std::array daughterIds{}; + float decayLength = 0.f; // 3D distance from the primary vertex to the decay vertex + float properLength = 0.f; // decayLength * MassK0Short / p (proper lifetime c*tau) +}; + +/// ResoMicroTracks_001 row: raw packed bytes and the decoded public accessors. +/// The encoding rules are those of the K1 micro001 contract; the type is separate so the K1 contract stays immutable. +struct TrackSnapshot { + int64_t sourceTrackId = -1; + int8_t charge = 0; + float px = 0.f, py = 0.f, pz = 0.f; + uint8_t pidNSigmaPiFlag = 0, pidNSigmaKaFlag = 0, pidNSigmaPrFlag = 0; + uint8_t trackSelectionFlags = 0, trackFlags = 0, tpcNClsCrossedRows = 0, itsClusterMap = 0; + std::array tpcNSigma{}; // pi, ka, pr + std::array tofNSigma{}; // pi, ka, pr + float dcaXY = std::numeric_limits::quiet_NaN(); + float dcaZ = std::numeric_limits::quiet_NaN(); + bool passedPtDependentDCAxy = false, passedPtDependentDCAz = false; + bool hasTOF = false, isPVContributor = false; + std::array itsHit{}; +}; + +struct XiK0sCandidateSnapshot { + CascadeSnapshot xi{}; + V0Snapshot k0s{}; +}; + +struct Xi1530KCandidateSnapshot { + CascadeSnapshot xi{}; + TrackSnapshot pion{}; + TrackSnapshot kaon{}; + uint8_t chargePattern = kSignalPattern; // metadata +}; + +template +struct CanonicalizationResult { + Snapshot candidate{}; + BuildStatus status = BuildStatus::InvalidChargePattern; + explicit operator bool() const { return status == BuildStatus::Ok; } +}; + +/// Candidate kinematics from the stored 3-momenta and PDG masses (audit only, never features) +struct KinematicAudit { + float mass = 0.f; + float pt = 0.f; + float eta = 0.f; + float phi = 0.f; + float scalarSumPt = 0.f; +}; + +template +struct FeaturePack { + std::array master{}; + BuildStatus status = BuildStatus::InvalidContract; + KinematicAudit kinematics{}; +}; +using XiK0sFeaturePack = FeaturePack; +using Xi1530KFeaturePack = FeaturePack; + +namespace detail +{ +template +float decayLength(Collision const& collision, Row const& row) +{ + const float dx = row.decayVtxX() - collision.posX(); + const float dy = row.decayVtxY() - collision.posY(); + const float dz = row.decayVtxZ() - collision.posZ(); + return std::sqrt(dx * dx + dy * dy + dz * dz); +} + +inline float properLength(float length, float px, float py, float pz, double mass) +{ + const float p = std::sqrt(px * px + py * py + pz * pz); + if (!(p > 0.f)) { + return std::numeric_limits::quiet_NaN(); + } + return static_cast(length * mass / p); +} +} // namespace detail + +template +CascadeSnapshot makeCascadeSnapshot(Collision const& collision, Cascade const& row) +{ + CascadeSnapshot out; + out.sourceRow = static_cast(row.globalIndex()); + out.sign = static_cast(row.sign()); + out.px = row.px(); + out.py = row.py(); + out.pz = row.pz(); + out.mXi = row.mXi(); + out.mLambda = row.mLambda(); + out.v0CosPA = row.v0CosPA(); + out.cascCosPA = row.cascCosPA(); + out.v0DaughDCA = row.daughDCA(); + out.cascDaughDCA = row.cascDaughDCA(); + out.dcaPosToPV = row.dcapostopv(); + out.dcaNegToPV = row.dcanegtopv(); + out.dcaBachToPV = row.dcabachtopv(); + out.dcaV0ToPV = row.dcav0topv(); + out.dcaXYToPV = row.dcaXYCascToPV(); + out.dcaZToPV = row.dcaZCascToPV(); + out.v0Radius = row.transRadius(); + out.cascRadius = row.cascTransRadius(); + out.decayVtxX = row.decayVtxX(); + out.decayVtxY = row.decayVtxY(); + out.decayVtxZ = row.decayVtxZ(); + out.tpcNSigma10 = {row.daughterTPCNSigmaPosPi10(), row.daughterTPCNSigmaPosKa10(), row.daughterTPCNSigmaPosPr10(), + row.daughterTPCNSigmaNegPi10(), row.daughterTPCNSigmaNegKa10(), row.daughterTPCNSigmaNegPr10(), + row.daughterTPCNSigmaBachPi10(), row.daughterTPCNSigmaBachKa10(), row.daughterTPCNSigmaBachPr10()}; + out.tofNSigma10 = {row.daughterTOFNSigmaPosPi10(), row.daughterTOFNSigmaPosKa10(), row.daughterTOFNSigmaPosPr10(), + row.daughterTOFNSigmaNegPi10(), row.daughterTOFNSigmaNegKa10(), row.daughterTOFNSigmaNegPr10(), + row.daughterTOFNSigmaBachPi10(), row.daughterTOFNSigmaBachKa10(), row.daughterTOFNSigmaBachPr10()}; + out.crossedRows = {row.nCrossedRowsPos(), row.nCrossedRowsNeg(), row.nCrossedRowsBach()}; + const auto indices = row.cascadeIndices(); + out.daughterIds = {indices[0], indices[1], indices[2]}; + out.decayLength = detail::decayLength(collision, row); + out.properLength = detail::properLength(out.decayLength, out.px, out.py, out.pz, o2::constants::physics::MassXiMinus); + return out; +} + +template +V0Snapshot makeV0Snapshot(Collision const& collision, V0 const& row) +{ + V0Snapshot out; + out.sourceRow = static_cast(row.globalIndex()); + out.px = row.px(); + out.py = row.py(); + out.pz = row.pz(); + out.mK0Short = row.mK0Short(); + out.mLambda = row.mLambda(); + out.mAntiLambda = row.mAntiLambda(); + out.cosPA = row.v0CosPA(); + out.daughDCA = row.daughDCA(); + out.dcaPosToPV = row.dcapostopv(); + out.dcaNegToPV = row.dcanegtopv(); + out.dcaV0ToPV = row.dcav0topv(); + out.radius = row.transRadius(); + out.decayVtxX = row.decayVtxX(); + out.decayVtxY = row.decayVtxY(); + out.decayVtxZ = row.decayVtxZ(); + out.alpha = row.alpha(); + out.qtarm = row.qtarm(); + out.tpcNSigma10 = {row.daughterTPCNSigmaPosPi10(), row.daughterTPCNSigmaPosKa10(), row.daughterTPCNSigmaPosPr10(), + row.daughterTPCNSigmaNegPi10(), row.daughterTPCNSigmaNegKa10(), row.daughterTPCNSigmaNegPr10()}; + out.tofNSigma10 = {row.daughterTOFNSigmaPosPi10(), row.daughterTOFNSigmaPosKa10(), row.daughterTOFNSigmaPosPr10(), + row.daughterTOFNSigmaNegPi10(), row.daughterTOFNSigmaNegKa10(), row.daughterTOFNSigmaNegPr10()}; + out.crossedRows = {row.nCrossedRowsPos(), row.nCrossedRowsNeg()}; + const auto indices = row.indices(); + out.daughterIds = {indices[0], indices[1]}; + out.decayLength = detail::decayLength(collision, row); + out.properLength = detail::properLength(out.decayLength, out.px, out.py, out.pz, o2::constants::physics::MassK0Short); + return out; +} + +template +TrackSnapshot makeTrackSnapshot(MicroTrack const& row) +{ + TrackSnapshot out; + out.sourceTrackId = static_cast(row.trackId()); + out.charge = static_cast(row.sign()); + out.px = static_cast(row.px()); + out.py = static_cast(row.py()); + out.pz = static_cast(row.pz()); + out.pidNSigmaPiFlag = static_cast(row.pidNSigmaPiFlag()); + out.pidNSigmaKaFlag = static_cast(row.pidNSigmaKaFlag()); + out.pidNSigmaPrFlag = static_cast(row.pidNSigmaPrFlag()); + out.trackSelectionFlags = static_cast(row.trackSelectionFlags()); + out.trackFlags = static_cast(row.trackFlags()); + out.tpcNClsCrossedRows = static_cast(row.tpcNClsCrossedRows()); + out.itsClusterMap = static_cast(row.itsClusterMap()); + out.tpcNSigma = {static_cast(row.tpcNSigmaPi()), static_cast(row.tpcNSigmaKa()), static_cast(row.tpcNSigmaPr())}; + out.tofNSigma = {static_cast(row.tofNSigmaPi()), static_cast(row.tofNSigmaKa()), static_cast(row.tofNSigmaPr())}; + out.dcaXY = static_cast(row.dcaXY()); + out.dcaZ = static_cast(row.dcaZ()); + out.passedPtDependentDCAxy = row.passedPtDependentDCAxy(); + out.passedPtDependentDCAz = row.passedPtDependentDCAz(); + out.hasTOF = row.hasTOF(); + out.isPVContributor = row.isPVContributor(); + for (std::size_t layer = 0; layer < NItsLayers; ++layer) { + out.itsHit[layer] = row.hasITSHitInLayer(static_cast(layer)); + } + return out; +} + +inline bool hasFiniteMomentum(float px, float py, float pz) +{ + return std::isfinite(px) && std::isfinite(py) && std::isfinite(pz) && std::hypot(px, py) > 0.f; +} + +inline bool sharesDaughter(CascadeSnapshot const& xi, int64_t trackId) +{ + return std::any_of(xi.daughterIds.begin(), xi.daughterIds.end(), [trackId](int id) { return static_cast(id) == trackId; }); +} + +/// Mode A canonical candidate [Xi, K0s]; the roles are fixed by the object types. +inline CanonicalizationResult canonicalizeXiK0s(CascadeSnapshot const& xi, V0Snapshot const& k0s) +{ + CanonicalizationResult result; + for (const auto& id : k0s.daughterIds) { + if (sharesDaughter(xi, id)) { + result.status = BuildStatus::ReusedDaughter; + return result; + } + } + if (xi.sign != 1 && xi.sign != -1) { + result.status = BuildStatus::InvalidChargePattern; + return result; + } + if (!hasFiniteMomentum(xi.px, xi.py, xi.pz) || !hasFiniteMomentum(k0s.px, k0s.py, k0s.pz)) { + result.status = BuildStatus::InvalidMomentum; + return result; + } + result.candidate.xi = xi; + result.candidate.k0s = k0s; + result.status = BuildStatus::Ok; + return result; +} + +/// Charge pattern of (Xi, pion, kaon); 0 is the Omega(2012) signal pattern. +inline uint8_t chargePattern(int xiSign, int pionSign, int kaonSign) +{ + uint8_t pattern = kSignalPattern; + if (pionSign == xiSign) { + pattern |= kWrongSignPion; + } + if (kaonSign != xiSign) { + pattern |= kWrongSignKaon; + } + return pattern; +} + +/// Mode B canonical candidate [Xi, pion, kaon]; the roles are fixed by the selected species, the charge pattern is metadata. +inline CanonicalizationResult canonicalizeXi1530K(CascadeSnapshot const& xi, TrackSnapshot const& pion, TrackSnapshot const& kaon) +{ + CanonicalizationResult result; + if (pion.sourceTrackId == kaon.sourceTrackId || sharesDaughter(xi, pion.sourceTrackId) || sharesDaughter(xi, kaon.sourceTrackId)) { + result.status = BuildStatus::ReusedDaughter; + return result; + } + if ((xi.sign != 1 && xi.sign != -1) || (pion.charge != 1 && pion.charge != -1) || (kaon.charge != 1 && kaon.charge != -1)) { + result.status = BuildStatus::InvalidChargePattern; + return result; + } + if (!hasFiniteMomentum(xi.px, xi.py, xi.pz) || !hasFiniteMomentum(pion.px, pion.py, pion.pz) || !hasFiniteMomentum(kaon.px, kaon.py, kaon.pz)) { + result.status = BuildStatus::InvalidMomentum; + return result; + } + result.candidate.xi = xi; + result.candidate.pion = pion; + result.candidate.kaon = kaon; + result.candidate.chargePattern = chargePattern(xi.sign, pion.charge, kaon.charge); + result.status = BuildStatus::Ok; + return result; +} + +namespace detail +{ +struct EncodedValue { + float value; + float valid; + float overflow; +}; + +// int8 x10 nSigma of ResoCascades/ResoV0s: saturated codes carry no usable value +inline EncodedValue encodeNSigma10(int8_t code) +{ + if (code == SaturatedLow || code == SaturatedHigh) { + return {.value = 0.f, .valid = 0.f, .overflow = 0.f}; + } + return {.value = static_cast(code) / NSigmaScale, .valid = 1.f, .overflow = 0.f}; +} + +// micro001 nSigma, as the K1 contract +inline EncodedValue encodePID(float decoded) +{ + if (std::isnan(decoded)) { + return {.value = 0.f, .valid = 0.f, .overflow = 0.f}; + } + if (std::isinf(decoded)) { + return {.value = std::signbit(decoded) ? -MicroPidOverflow : MicroPidOverflow, .valid = 1.f, .overflow = 1.f}; + } + return {.value = decoded, .valid = 1.f, .overflow = 0.f}; +} + +// micro001 DCA, as the K1 contract +inline EncodedValue encodeDCA(float decoded) +{ + if (!std::isfinite(decoded)) { + return {.value = 0.f, .valid = 0.f, .overflow = 0.f}; + } + const bool overflow = decoded == o2::aod::resomicrodaughter001::DCAEncoding::MaxDCA; + return {.value = decoded, .valid = 1.f, .overflow = overflow ? 1.f : 0.f}; +} + +template +class Writer +{ + public: + explicit Writer(std::array& output) : out(output) {} + std::size_t index = 0; + // A count beyond N is detected by finishPack(); nothing is written past the end. + void add(float value) + { + if (index < N) { + out[index] = value; + } + ++index; + } + void add(double value) { add(static_cast(value)); } + void add(bool value) { add(value ? 1.f : 0.f); } + void addValid(EncodedValue value) + { + add(value.value); + add(value.valid); + } + void addFull(EncodedValue value) + { + add(value.value); + add(value.valid); + add(value.overflow); + } + + private: + std::array& out; +}; + +using LorentzVector = ROOT::Math::PxPyPzMVector; + +inline LorentzVector fourMomentum(float px, float py, float pz, double mass) +{ + return {px, py, pz, mass}; +} + +template +void appendCascade(Writer& w, CascadeSnapshot const& xi) +{ + w.add(xi.v0CosPA); + w.add(xi.cascCosPA); + w.add(xi.v0DaughDCA); + w.add(xi.cascDaughDCA); + w.add(std::abs(xi.dcaPosToPV)); + w.add(std::abs(xi.dcaNegToPV)); + w.add(std::abs(xi.dcaBachToPV)); + w.add(std::abs(xi.dcaV0ToPV)); + w.add(std::abs(xi.dcaXYToPV)); + w.add(std::abs(xi.dcaZToPV)); + w.add(xi.v0Radius); + w.add(xi.cascRadius); + w.add(static_cast(xi.mLambda) - o2::constants::physics::MassLambda0); + w.add(static_cast(xi.mXi) - o2::constants::physics::MassXiMinus); + w.add(xi.decayLength); + w.add(xi.properLength); + // Roles: the (anti)proton is the positive daughter of a Xi-, the negative daughter of a Xi+ + const bool isXiMinus = xi.sign < 0; + constexpr std::size_t Pi = 0, Pr = 2, Pos = 0, Neg = 1, Bach = 2; + const std::size_t baryon = isXiMinus ? Pos : Neg; + const std::size_t meson = isXiMinus ? Neg : Pos; + w.addValid(encodeNSigma10(xi.tpcNSigma10[baryon * NPidSpecies + Pr])); + w.addValid(encodeNSigma10(xi.tpcNSigma10[meson * NPidSpecies + Pi])); + w.addValid(encodeNSigma10(xi.tpcNSigma10[Bach * NPidSpecies + Pi])); + w.addValid(encodeNSigma10(xi.tofNSigma10[baryon * NPidSpecies + Pr])); + w.addValid(encodeNSigma10(xi.tofNSigma10[meson * NPidSpecies + Pi])); + w.addValid(encodeNSigma10(xi.tofNSigma10[Bach * NPidSpecies + Pi])); + w.add(static_cast(xi.crossedRows[baryon])); + w.add(static_cast(xi.crossedRows[meson])); + w.add(static_cast(xi.crossedRows[Bach])); +} + +template +void appendV0(Writer& w, V0Snapshot const& k0s) +{ + w.add(k0s.cosPA); + w.add(k0s.daughDCA); + w.add(std::abs(k0s.dcaPosToPV)); + w.add(std::abs(k0s.dcaNegToPV)); + w.add(std::abs(k0s.dcaV0ToPV)); + w.add(k0s.radius); + w.add(static_cast(k0s.mK0Short) - o2::constants::physics::MassK0Short); + w.add(static_cast(k0s.mLambda) - o2::constants::physics::MassLambda0); + w.add(static_cast(k0s.mAntiLambda) - o2::constants::physics::MassLambda0); + w.add(k0s.alpha); + w.add(k0s.qtarm); + w.add(k0s.decayLength); + w.add(k0s.properLength); + constexpr std::size_t Pi = 0, Pos = 0, Neg = 1; + w.addValid(encodeNSigma10(k0s.tpcNSigma10[Pos * NPidSpecies + Pi])); + w.addValid(encodeNSigma10(k0s.tpcNSigma10[Neg * NPidSpecies + Pi])); + w.addValid(encodeNSigma10(k0s.tofNSigma10[Pos * NPidSpecies + Pi])); + w.addValid(encodeNSigma10(k0s.tofNSigma10[Neg * NPidSpecies + Pi])); + w.add(static_cast(k0s.crossedRows[Pos])); + w.add(static_cast(k0s.crossedRows[Neg])); +} + +template +void appendTrack(Writer& w, TrackSnapshot const& track) +{ + for (const float& decoded : track.tpcNSigma) { + w.addFull(encodePID(decoded)); + } + for (const float& decoded : track.tofNSigma) { + w.addFull(track.hasTOF ? encodePID(decoded) : EncodedValue{.value = 0.f, .valid = 0.f, .overflow = 0.f}); + } + w.addFull(encodeDCA(track.dcaXY)); + w.addFull(encodeDCA(track.dcaZ)); + w.add(track.passedPtDependentDCAxy); + w.add(track.passedPtDependentDCAz); + w.add(track.hasTOF); + w.add(static_cast(track.tpcNClsCrossedRows)); + for (const bool& hit : track.itsHit) { + w.add(hit); + } + w.add(track.isPVContributor); +} + +// delta eta, sin/cos delta phi, delta R, z_pT of two objects +template +bool appendPair(Writer& w, LorentzVector const& a, LorentzVector const& b) +{ + const double deta = a.Eta() - b.Eta(); + const double dphi = std::remainder(a.Phi() - b.Phi(), o2::constants::math::TwoPI); + const double sumPt = a.Pt() + b.Pt(); + if (!std::isfinite(deta) || !std::isfinite(dphi) || !(sumPt > 0.)) { + return false; + } + w.add(deta); + w.add(std::sin(dphi)); + w.add(std::cos(dphi)); + w.add(std::hypot(deta, dphi)); + w.add(std::min(a.Pt(), b.Pt()) / sumPt); + return true; +} + +// Cosine of the daughter direction in the mother rest frame w.r.t. the mother direction +inline double cosThetaStar(LorentzVector const& daughter, LorentzVector const& mother) +{ + const double p = mother.P(); + if (!(p > 0.) || !(mother.E() > p)) { + return std::numeric_limits::quiet_NaN(); + } + const ROOT::Math::Boost boost{mother.BoostToCM()}; + const auto inRest = boost(daughter); + const double pStar = inRest.P(); + if (!(pStar > 0.)) { + return std::numeric_limits::quiet_NaN(); + } + return inRest.Vect().Dot(mother.Vect()) / (pStar * p); +} + +template +bool finishPack(FeaturePack& pack, std::size_t index, LorentzVector const& mother, double scalarSumPt) +{ + const bool allFinite = std::all_of(pack.master.begin(), pack.master.end(), [](float x) { return std::isfinite(x); }); + if (index != N || !allFinite) { + pack.status = BuildStatus::InvalidContract; + return false; + } + pack.kinematics.mass = static_cast(mother.M()); + pack.kinematics.pt = static_cast(mother.Pt()); + pack.kinematics.eta = static_cast(mother.Eta()); + pack.kinematics.phi = static_cast(mother.Phi()); + pack.kinematics.scalarSumPt = static_cast(scalarSumPt); + pack.status = BuildStatus::Ok; + return true; +} + +template +constexpr bool isStrictlyIncreasingBelow(std::array const& indices, std::size_t bound) +{ + for (std::size_t i = 0; i < N; ++i) { + if (indices[i] >= bound || (i > 0 && indices[i - 1] >= indices[i])) { + return false; + } + } + return true; +} +} // namespace detail + +// Names, projections and SHA-256 are generated from feature_contract_omega2012_v1.json (one section per mode). +inline constexpr std::array XiK0sMasterFeatureNames{ + "xi.v0_cos_pa", + "xi.casc_cos_pa", + "xi.v0_daughter_dca", + "xi.casc_daughter_dca", + "xi.abs_dca_pos_to_pv", + "xi.abs_dca_neg_to_pv", + "xi.abs_dca_bach_to_pv", + "xi.abs_dca_v0_to_pv", + "xi.abs_dca_xy_to_pv", + "xi.abs_dca_z_to_pv", + "xi.v0_radius", + "xi.casc_radius", + "xi.delta_mass_lambda", + "xi.delta_mass_xi", + "xi.decay_length", + "xi.proper_length", + "xi.baryon_tpc_nsigma_pr", + "xi.baryon_tpc_nsigma_pr_valid", + "xi.meson_tpc_nsigma_pi", + "xi.meson_tpc_nsigma_pi_valid", + "xi.bach_tpc_nsigma_pi", + "xi.bach_tpc_nsigma_pi_valid", + "xi.baryon_tof_nsigma_pr", + "xi.baryon_tof_nsigma_pr_valid", + "xi.meson_tof_nsigma_pi", + "xi.meson_tof_nsigma_pi_valid", + "xi.bach_tof_nsigma_pi", + "xi.bach_tof_nsigma_pi_valid", + "xi.baryon_crossed_rows", + "xi.meson_crossed_rows", + "xi.bach_crossed_rows", + "k0s.cos_pa", + "k0s.daughter_dca", + "k0s.abs_dca_pos_to_pv", + "k0s.abs_dca_neg_to_pv", + "k0s.abs_dca_v0_to_pv", + "k0s.radius", + "k0s.delta_mass_k0s", + "k0s.delta_mass_lambda", + "k0s.delta_mass_antilambda", + "k0s.arm_alpha", + "k0s.arm_qt", + "k0s.decay_length", + "k0s.proper_length", + "k0s.pos_tpc_nsigma_pi", + "k0s.pos_tpc_nsigma_pi_valid", + "k0s.neg_tpc_nsigma_pi", + "k0s.neg_tpc_nsigma_pi_valid", + "k0s.pos_tof_nsigma_pi", + "k0s.pos_tof_nsigma_pi_valid", + "k0s.neg_tof_nsigma_pi", + "k0s.neg_tof_nsigma_pi_valid", + "k0s.pos_crossed_rows", + "k0s.neg_crossed_rows", + "xi.pt_fraction", + "k0s.pt_fraction", + "xi__k0s.delta_eta", + "xi__k0s.sin_delta_phi", + "xi__k0s.cos_delta_phi", + "xi__k0s.delta_r", + "xi__k0s.z_pt", + "xi__k0s.opening_angle", + "xi__k0s.cos_theta_star"}; +inline constexpr std::array XiK0sDetectorV1Projection{ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53}; +inline constexpr std::array XiK0sRelationalV1Projection{ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62}; +inline constexpr std::string_view XiK0sFeatureContractSha256 = "b767a5643da563246295f543ec7296c9e21b00fd921fef88acc7b19f3b24942c"; + +inline constexpr std::array Xi1530KMasterFeatureNames{ + "xi.v0_cos_pa", + "xi.casc_cos_pa", + "xi.v0_daughter_dca", + "xi.casc_daughter_dca", + "xi.abs_dca_pos_to_pv", + "xi.abs_dca_neg_to_pv", + "xi.abs_dca_bach_to_pv", + "xi.abs_dca_v0_to_pv", + "xi.abs_dca_xy_to_pv", + "xi.abs_dca_z_to_pv", + "xi.v0_radius", + "xi.casc_radius", + "xi.delta_mass_lambda", + "xi.delta_mass_xi", + "xi.decay_length", + "xi.proper_length", + "xi.baryon_tpc_nsigma_pr", + "xi.baryon_tpc_nsigma_pr_valid", + "xi.meson_tpc_nsigma_pi", + "xi.meson_tpc_nsigma_pi_valid", + "xi.bach_tpc_nsigma_pi", + "xi.bach_tpc_nsigma_pi_valid", + "xi.baryon_tof_nsigma_pr", + "xi.baryon_tof_nsigma_pr_valid", + "xi.meson_tof_nsigma_pi", + "xi.meson_tof_nsigma_pi_valid", + "xi.bach_tof_nsigma_pi", + "xi.bach_tof_nsigma_pi_valid", + "xi.baryon_crossed_rows", + "xi.meson_crossed_rows", + "xi.bach_crossed_rows", + "pion.tpc_nsigma_pi", + "pion.tpc_nsigma_pi_valid", + "pion.tpc_nsigma_pi_overflow", + "pion.tpc_nsigma_ka", + "pion.tpc_nsigma_ka_valid", + "pion.tpc_nsigma_ka_overflow", + "pion.tpc_nsigma_pr", + "pion.tpc_nsigma_pr_valid", + "pion.tpc_nsigma_pr_overflow", + "pion.tof_nsigma_pi", + "pion.tof_nsigma_pi_valid", + "pion.tof_nsigma_pi_overflow", + "pion.tof_nsigma_ka", + "pion.tof_nsigma_ka_valid", + "pion.tof_nsigma_ka_overflow", + "pion.tof_nsigma_pr", + "pion.tof_nsigma_pr_valid", + "pion.tof_nsigma_pr_overflow", + "pion.abs_dca_xy", + "pion.abs_dca_xy_valid", + "pion.abs_dca_xy_overflow", + "pion.abs_dca_z", + "pion.abs_dca_z_valid", + "pion.abs_dca_z_overflow", + "pion.passed_ptdep_dca_xy", + "pion.passed_ptdep_dca_z", + "pion.has_tof", + "pion.tpc_crossed_rows", + "pion.its_hit_l0", + "pion.its_hit_l1", + "pion.its_hit_l2", + "pion.its_hit_l3", + "pion.its_hit_l4", + "pion.its_hit_l5", + "pion.its_hit_l6", + "pion.is_pv_contributor", + "kaon.tpc_nsigma_pi", + "kaon.tpc_nsigma_pi_valid", + "kaon.tpc_nsigma_pi_overflow", + "kaon.tpc_nsigma_ka", + "kaon.tpc_nsigma_ka_valid", + "kaon.tpc_nsigma_ka_overflow", + "kaon.tpc_nsigma_pr", + "kaon.tpc_nsigma_pr_valid", + "kaon.tpc_nsigma_pr_overflow", + "kaon.tof_nsigma_pi", + "kaon.tof_nsigma_pi_valid", + "kaon.tof_nsigma_pi_overflow", + "kaon.tof_nsigma_ka", + "kaon.tof_nsigma_ka_valid", + "kaon.tof_nsigma_ka_overflow", + "kaon.tof_nsigma_pr", + "kaon.tof_nsigma_pr_valid", + "kaon.tof_nsigma_pr_overflow", + "kaon.abs_dca_xy", + "kaon.abs_dca_xy_valid", + "kaon.abs_dca_xy_overflow", + "kaon.abs_dca_z", + "kaon.abs_dca_z_valid", + "kaon.abs_dca_z_overflow", + "kaon.passed_ptdep_dca_xy", + "kaon.passed_ptdep_dca_z", + "kaon.has_tof", + "kaon.tpc_crossed_rows", + "kaon.its_hit_l0", + "kaon.its_hit_l1", + "kaon.its_hit_l2", + "kaon.its_hit_l3", + "kaon.its_hit_l4", + "kaon.its_hit_l5", + "kaon.its_hit_l6", + "kaon.is_pv_contributor", + "xi.pt_fraction", + "pion.pt_fraction", + "kaon.pt_fraction", + "xi__pion.delta_eta", + "xi__pion.sin_delta_phi", + "xi__pion.cos_delta_phi", + "xi__pion.delta_r", + "xi__pion.z_pt", + "xi__kaon.delta_eta", + "xi__kaon.sin_delta_phi", + "xi__kaon.cos_delta_phi", + "xi__kaon.delta_r", + "xi__kaon.z_pt", + "pion__kaon.delta_eta", + "pion__kaon.sin_delta_phi", + "pion__kaon.cos_delta_phi", + "pion__kaon.delta_r", + "pion__kaon.z_pt", + "xi1530__kaon.opening_angle", + "xi1530__kaon.cos_theta_star", + "mass_xi_pi", + "mass_xi_k", + "mass_pi_k"}; +inline constexpr std::array Xi1530KDetectorV1Projection{ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102}; +inline constexpr std::array Xi1530KRelationalV1Projection{ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123}; +inline constexpr std::array Xi1530KStudyV1Projection{ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125}; +inline constexpr std::string_view Xi1530KFeatureContractSha256 = "ca244fe167a599f9bc60d3909630edca5c797bb7b84d75d80944e6ad1a44aec5"; +static_assert(XiK0sMasterFeatureNames.size() == XiK0sNMasterFeatures, "XiK0s feature name count must match XiK0sNMasterFeatures"); +static_assert(Xi1530KMasterFeatureNames.size() == Xi1530KNMasterFeatures, "Xi1530K feature name count must match Xi1530KNMasterFeatures"); +static_assert(detail::isStrictlyIncreasingBelow(XiK0sDetectorV1Projection, XiK0sNMasterFeatures), "XiK0s DetectorV1 projection must be strictly increasing"); +static_assert(detail::isStrictlyIncreasingBelow(XiK0sRelationalV1Projection, XiK0sNMasterFeatures), "XiK0s RelationalV1 projection must be strictly increasing"); +static_assert(detail::isStrictlyIncreasingBelow(Xi1530KDetectorV1Projection, Xi1530KNMasterFeatures), "Xi1530K DetectorV1 projection must be strictly increasing"); +static_assert(detail::isStrictlyIncreasingBelow(Xi1530KRelationalV1Projection, Xi1530KNMasterFeatures), "Xi1530K RelationalV1 projection must be strictly increasing"); +static_assert(detail::isStrictlyIncreasingBelow(Xi1530KStudyV1Projection, Xi1530KNMasterFeatures), "Xi1530K StudyV1 projection must be strictly increasing"); + +/// Mode A master features (XiK0s contract) +inline XiK0sFeaturePack buildXiK0sFeatures(XiK0sCandidateSnapshot const& candidate) +{ + using o2::constants::physics::MassK0Short; + using o2::constants::physics::MassXiMinus; + XiK0sFeaturePack pack; + const auto& xi = candidate.xi; + const auto& k0s = candidate.k0s; + if (!hasFiniteMomentum(xi.px, xi.py, xi.pz) || !hasFiniteMomentum(k0s.px, k0s.py, k0s.pz)) { + pack.status = BuildStatus::InvalidMomentum; + return pack; + } + const auto pXi = detail::fourMomentum(xi.px, xi.py, xi.pz, MassXiMinus); + const auto pK0s = detail::fourMomentum(k0s.px, k0s.py, k0s.pz, MassK0Short); + const auto mother = pXi + pK0s; + const double sumPt = pXi.Pt() + pK0s.Pt(); + if (!(sumPt > 0.)) { + pack.status = BuildStatus::InvalidKinematics; + return pack; + } + detail::Writer w{pack.master}; + detail::appendCascade(w, xi); + detail::appendV0(w, k0s); + w.add(pXi.Pt() / sumPt); + w.add(pK0s.Pt() / sumPt); + if (!detail::appendPair(w, pXi, pK0s)) { + pack.status = BuildStatus::InvalidKinematics; + return pack; + } + w.add(ROOT::Math::VectorUtil::Angle(pXi, pK0s)); + w.add(detail::cosThetaStar(pXi, mother)); + detail::finishPack(pack, w.index, mother, sumPt); + return pack; +} + +/// Mode B master features (Xi1530K contract) +inline Xi1530KFeaturePack buildXi1530KFeatures(Xi1530KCandidateSnapshot const& candidate) +{ + using o2::constants::physics::MassKaonCharged; + using o2::constants::physics::MassPionCharged; + using o2::constants::physics::MassXiMinus; + Xi1530KFeaturePack pack; + const auto& xi = candidate.xi; + const auto& pion = candidate.pion; + const auto& kaon = candidate.kaon; + if (!hasFiniteMomentum(xi.px, xi.py, xi.pz) || !hasFiniteMomentum(pion.px, pion.py, pion.pz) || !hasFiniteMomentum(kaon.px, kaon.py, kaon.pz)) { + pack.status = BuildStatus::InvalidMomentum; + return pack; + } + if (pion.sourceTrackId == kaon.sourceTrackId || sharesDaughter(xi, pion.sourceTrackId) || sharesDaughter(xi, kaon.sourceTrackId)) { + pack.status = BuildStatus::ReusedDaughter; + return pack; + } + const auto pXi = detail::fourMomentum(xi.px, xi.py, xi.pz, MassXiMinus); + const auto pPion = detail::fourMomentum(pion.px, pion.py, pion.pz, MassPionCharged); + const auto pKaon = detail::fourMomentum(kaon.px, kaon.py, kaon.pz, MassKaonCharged); + const auto xi1530 = pXi + pPion; + const auto mother = xi1530 + pKaon; + const double sumPt = pXi.Pt() + pPion.Pt() + pKaon.Pt(); + if (!(sumPt > 0.)) { + pack.status = BuildStatus::InvalidKinematics; + return pack; + } + detail::Writer w{pack.master}; + detail::appendCascade(w, xi); + detail::appendTrack(w, pion); + detail::appendTrack(w, kaon); + w.add(pXi.Pt() / sumPt); + w.add(pPion.Pt() / sumPt); + w.add(pKaon.Pt() / sumPt); + if (!detail::appendPair(w, pXi, pPion) || !detail::appendPair(w, pXi, pKaon) || !detail::appendPair(w, pPion, pKaon)) { + pack.status = BuildStatus::InvalidKinematics; + return pack; + } + w.add(ROOT::Math::VectorUtil::Angle(xi1530, pKaon)); + w.add(detail::cosThetaStar(xi1530, mother)); + w.add(xi1530.M()); + w.add((pXi + pKaon).M()); + w.add((pPion + pKaon).M()); + detail::finishPack(pack, w.index, mother, sumPt); + return pack; +} + +template +std::vector project(FeaturePack const& pack, std::array const& indices) +{ + std::vector projected; + projected.reserve(M); + for (const auto& i : indices) { + projected.push_back(pack.master[i]); + } + return projected; +} + +inline std::vector projectFeatures(XiK0sFeaturePack const& pack, Profile profile) +{ + if (pack.status != BuildStatus::Ok) { + return {}; + } + switch (profile) { + case Profile::DetectorV1: + return project(pack, XiK0sDetectorV1Projection); + case Profile::RelationalV1: + return project(pack, XiK0sRelationalV1Projection); + default: + return {}; // mode A has no study-only features + } +} + +inline std::vector projectFeatures(Xi1530KFeaturePack const& pack, Profile profile) +{ + if (pack.status != BuildStatus::Ok) { + return {}; + } + switch (profile) { + case Profile::DetectorV1: + return project(pack, Xi1530KDetectorV1Projection); + case Profile::RelationalV1: + return project(pack, Xi1530KRelationalV1Projection); + case Profile::StudyV1: + return project(pack, Xi1530KStudyV1Projection); + default: + return {}; + } +} +} // namespace o2::analysis::omega2012ml + +#endif // PWGLF_CORE_OMEGA2012MLFEATURES_H_ diff --git a/PWGLF/Core/ResoAnalysisSelectionCore.h b/PWGLF/Core/ResoAnalysisSelectionCore.h new file mode 100644 index 00000000000..225f65124b7 --- /dev/null +++ b/PWGLF/Core/ResoAnalysisSelectionCore.h @@ -0,0 +1,462 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file ResoAnalysisSelectionCore.h +/// \brief Event, track-quality and PID selection of resonance daughters from the reduced v001 resonance tables +/// \author Bong-Hwi Lim +/// +/// The same selection code serves full ResoTracks (exact values) and ResoMicroTracks (quantised DCA and nSigma, +/// see LFResonanceTables.h); only the comparisons are quantisation aware. A cut set to DisabledCut is off. + +#ifndef PWGLF_CORE_RESOANALYSISSELECTIONCORE_H_ +#define PWGLF_CORE_RESOANALYSISSELECTIONCORE_H_ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace o2::analysis::resonance +{ + +inline constexpr float DisabledCut = -999.f; // an optional cut with this value is off and not evaluated +inline constexpr double DCAGridStep = 0.025; // v001 micro DCA encoding, lower-inclusive bins up to DCAGridMax +inline constexpr double DCAGridMax = 0.15; +inline constexpr double PIDGridStart = 2.0; // v001 micro nSigma encoding: 0.25 bins in [2.0, 3.5] +inline constexpr double PIDGridStep = 0.25; +inline constexpr double PIDGridMax = 3.5; +inline constexpr double GridTolerance = 1e-4; +inline constexpr std::size_t MinPtBinEdges = 2; // a pT dependent PID table needs at least one bin +inline constexpr float ProducerDCAPtP0 = 0.004f; // resonanceModuleInitializer cfgTightDCAOffset default +inline constexpr float ProducerDCAPtCoeff = 0.013f; // resonanceModuleInitializer cfgTightDCAPtCoefficient default +inline constexpr float ProducerDCAPtPower = 1.f; // resonanceModuleInitializer cfgTightDCAPtPower default +inline constexpr float ConfigTolerance = 1e-6f; + +// Last stage passed by a track; a cut-flow histogram can be filled directly from this value. +enum TrackStage : int { + kTrkInput = 0, + kTrkPt, + kTrkEta, + kTrkDCAxy, + kTrkDCAz, + kTrkFlags, + kTrkClusters, + kTrkTOFRequired, + kTrkPID, + kTrkNStages +}; + +// Resolved PID cut of one species at a given pT. +struct PIDCut { + double tpcMax = 0.; + double tofMax = 0.; + double combined = 0.; + bool tofRequired = false; +}; + +// A cut is on unless it carries the disabled value (tolerant to the float parsing of the JSON value). +inline bool isCutEnabled(float value) +{ + return value > DisabledCut + 1.f; +} + +// v001 micro values are lower-inclusive bin edges: a maximum cut on the grid keeps bins below it. +inline bool passesBinnedMax(double decoded, double cut) +{ + return decoded < cut - o2::constants::math::Epsilon; +} + +// Minimum cut on the grid keeps the bin starting at the cut. +inline bool passesBinnedMin(double decoded, double cut) +{ + return decoded >= cut - o2::constants::math::Epsilon; +} + +template +bool passesMax(double value, double cut) +{ + if constexpr (IsResoMicrotrack) { + return passesBinnedMax(value, cut); + } else { + return value < cut; + } +} + +inline bool isInRange(double value, double minimum, double maximum) +{ + if (isCutEnabled(minimum) && value < minimum) { + return false; + } + if (isCutEnabled(maximum) && value > maximum) { + return false; + } + return true; +} + +inline bool isInWindow(double value, double center, double width) +{ + return std::abs(value - center) < width; +} + +// Preserve pT-bin membership [low, high). +inline int getPtBinIndex(float pt, const std::vector& ptBins) +{ + for (std::size_t i = 1; i < ptBins.size(); ++i) { + if (pt >= ptBins[i - 1] && pt < ptBins[i]) { + return static_cast(i - 1); + } + } + return -1; +} + +// Configurable groups without prefix: the JSON keys are the plain configurable names. + +/// Event selection +struct EventCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cRecoINELgt0{"cRecoINELgt0", false, "Apply reconstructed INEL>0 selection"}; + o2::framework::Configurable cMCINELgt0{"cMCINELgt0", false, "Require generator INEL>0 in MC processes"}; + o2::framework::Configurable cMCVtxIn10{"cMCVtxIn10", false, "Require generator |vz| < 10 cm in MC processes"}; +}; + +/// Track selections (common for all daughter species, -999 switches an optional cut off) +struct TrackCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cMinPtcut{"cMinPtcut", 0.15, "Track minium pt cut"}; + o2::framework::Configurable cMaxEtacut{"cMaxEtacut", -999.f, "Track maximum |eta| cut (-999: off)"}; + // DCAr to PV + o2::framework::Configurable cMaxDCArToPVcut{"cMaxDCArToPVcut", 0.1, "Track DCAr cut to PV Maximum"}; + // DCAz to PV + o2::framework::Configurable cMaxDCAzToPVcut{"cMaxDCAzToPVcut", 0.1, "Track DCAz cut to PV Maximum"}; + o2::framework::Configurable cMinDCAzToPVcut{"cMinDCAzToPVcut", 0.0, "Track DCAz cut to PV Minimum"}; + o2::framework::Configurable cfgUsePtDepDCA{"cfgUsePtDepDCA", false, "Use pT dependent DCA cut instead of the fixed maximum"}; + o2::framework::Configurable cDCAToPVByPtP0{"cDCAToPVByPtP0", 0.004f, "pT dependent DCA cut = P0 + coefficient / pT^power (cm)"}; + o2::framework::Configurable cDCAToPVByPtCoeff{"cDCAToPVByPtCoeff", 0.013f, "Coefficient in the pT dependent DCA cut"}; + o2::framework::Configurable cDCAToPVByPtPower{"cDCAToPVByPtPower", 1.f, "Power in the pT dependent DCA cut"}; + o2::framework::Configurable cfgPrimaryTrack{"cfgPrimaryTrack", true, "Primary track selection"}; // kGoldenChi2 | kDCAxy | kDCAz + o2::framework::Configurable cfgGlobalWoDCATrack{"cfgGlobalWoDCATrack", true, "Global track selection without DCA"}; // kQualityTracks (kTrackType | kTPCNCls | kTPCCrossedRows | kTPCCrossedRowsOverNCls | kTPCChi2NDF | kTPCRefit | kITSNCls | kITSChi2NDF | kITSRefit | kITSHits) | kInAcceptanceTracks (kPtRange | kEtaRange) + o2::framework::Configurable cfgGlobalTrack{"cfgGlobalTrack", false, "Global track selection"}; // kGoldenChi2 | kDCAxy | kDCAz + o2::framework::Configurable cfgPVContributor{"cfgPVContributor", false, "PV contributor track selection"}; // PV Contriuibutor + o2::framework::Configurable cfgUseTPCRefit{"cfgUseTPCRefit", false, "Require TPC Refit"}; + o2::framework::Configurable cfgUseITSRefit{"cfgUseITSRefit", false, "Require ITS Refit"}; + o2::framework::Configurable cfgTPCcluster{"cfgTPCcluster", 0, "Number of TPC cluster (found clusters, ResoTracks only)"}; + o2::framework::Configurable cfgTPCCrossedRowsMin{"cfgTPCCrossedRowsMin", 0, "Minimum number of TPC crossed rows"}; + o2::framework::Configurable cfgITSNClsMin{"cfgITSNClsMin", 0, "Minimum number of ITS clusters (ResoMicroTracks only)"}; + o2::framework::Configurable cfgHasTOF{"cfgHasTOF", false, "Require TOF"}; +}; + +/// PID selection of one daughter species, filled by the task from its own (species-named) configurables. +/// The configurable names are used only in the configuration messages. +struct PIDCutConfig { + std::string species; // e.g. "Pion", used in messages + double maxTPCnSigma = DisabledCut; + double maxTOFnSigma = DisabledCut; + double combinedNSigma = DisabledCut; // combined TPC-TOF cut, on when > 0 + bool onlyTOFTracks = false; // require a TOF signal + bool usePtDependent = false; // use the pT binned cuts below instead of the fixed maxima + std::vector ptBins; // bin edges; the other vectors have one entry per bin + std::vector tpcNSigmaCuts; + std::vector tofNSigmaCuts; + std::vector tofRequired; + std::string maxTPCName; // configurable names for the messages + std::string maxTOFName; + std::string tpcCutsName; + std::string tofCutsName; +}; + +/// Event, track-quality, TOF-requirement and PID selection of resonance daughters. +/// The species index is the position of its PIDCutConfig in init(). +class ResoAnalysisSelectionCore +{ + public: + // byPassTOF skips the TOF nSigma cut and the pT binned TOF requirement; microTracks enables the grid checks. + void init(EventCuts const& eventCuts, TrackCuts const& trackCuts, std::vector pidCuts, bool byPassTOF, bool microTracks) + { + mEventCuts = eventCuts; + mTrackCuts = trackCuts; + mPID = std::move(pidCuts); + mByPassTOF = byPassTOF; + checkConfiguration(microTracks); + } + + template + bool passesEventCuts(const CollisionType& collision) + { + return !(mEventCuts.cRecoINELgt0 && !collision.isRecINELgt0()); + } + + template + bool passesMCEventCuts(const CollisionType& collision) + { + if (mEventCuts.cMCINELgt0 && !collision.isINELgt0()) { + return false; + } + if (mEventCuts.cMCVtxIn10 && !collision.isVtxIn10()) { + return false; + } + return true; + } + + // Resolve the PID cut of one species at a given pT; false if the pT is outside all pT-dependent bins. + bool getPIDCut(int species, float pt, PIDCut& cut) + { + const auto& config = mPID[species]; + cut.tpcMax = config.maxTPCnSigma; + cut.tofMax = config.maxTOFnSigma; + cut.combined = config.combinedNSigma; + cut.tofRequired = false; + if (config.usePtDependent) { + const int ptBin = getPtBinIndex(pt, config.ptBins); + if (ptBin < 0) { + return false; + } + const auto bin = static_cast(ptBin); + cut.tpcMax = config.tpcNSigmaCuts[bin]; + cut.tofMax = config.tofNSigmaCuts[bin]; + cut.tofRequired = config.tofRequired[bin] != 0; + } + return true; + } + + // Track quality selection shared by all species. Returns the last stage that was passed. + // Full tracks store exact values; micro tracks store quantised DCA (see LFResonanceTables.h). + template + int trackQualityStage(const TrackType& track) + { + const double pt = track.pt(); + const double dcaXY = track.dcaXY(); + const double dcaZ = track.dcaZ(); + // Invalid micro DCA codes decode to NaN + if (!std::isfinite(pt) || !std::isfinite(track.eta()) || !std::isfinite(dcaXY) || !std::isfinite(dcaZ)) { + return kTrkInput; + } + if (std::abs(pt) < mTrackCuts.cMinPtcut) { + return kTrkInput; + } + if (isCutEnabled(mTrackCuts.cMaxEtacut) && !(std::abs(track.eta()) < mTrackCuts.cMaxEtacut)) { + return kTrkPt; + } + + if (mTrackCuts.cfgUsePtDepDCA) { + if constexpr (IsResoMicrotrack) { + if (!track.passedPtDependentDCAxy()) { + return kTrkEta; + } + if (!track.passedPtDependentDCAz()) { + return kTrkDCAxy; + } + } else { + const double dcaPtCut = mTrackCuts.cDCAToPVByPtP0 + mTrackCuts.cDCAToPVByPtCoeff * std::pow(pt, -static_cast(mTrackCuts.cDCAToPVByPtPower)); + if (!(std::abs(dcaXY) < dcaPtCut)) { + return kTrkEta; + } + if (!(std::abs(dcaZ) < dcaPtCut)) { + return kTrkDCAxy; + } + } + } else { + if (isCutEnabled(mTrackCuts.cMaxDCArToPVcut)) { + if constexpr (IsResoMicrotrack) { + if (!passesBinnedMax(dcaXY, mTrackCuts.cMaxDCArToPVcut)) { + return kTrkEta; + } + } else { + if (!(std::abs(dcaXY) <= mTrackCuts.cMaxDCArToPVcut)) { + return kTrkEta; + } + } + } + if (isCutEnabled(mTrackCuts.cMaxDCAzToPVcut)) { + if constexpr (IsResoMicrotrack) { + if (!passesBinnedMax(dcaZ, mTrackCuts.cMaxDCAzToPVcut)) { + return kTrkDCAxy; + } + } else { + if (!(std::abs(dcaZ) <= mTrackCuts.cMaxDCAzToPVcut)) { + return kTrkDCAxy; + } + } + } + } + if (isCutEnabled(mTrackCuts.cMinDCAzToPVcut)) { + if constexpr (IsResoMicrotrack) { + if (!passesBinnedMin(dcaZ, mTrackCuts.cMinDCAzToPVcut)) { + return kTrkDCAxy; + } + } else { + if (!(std::abs(dcaZ) >= mTrackCuts.cMinDCAzToPVcut)) { + return kTrkDCAxy; + } + } + } + + // Track flags + if ((mTrackCuts.cfgPrimaryTrack && !track.isPrimaryTrack()) || + (mTrackCuts.cfgGlobalWoDCATrack && !track.isGlobalTrackWoDCA()) || + (mTrackCuts.cfgGlobalTrack && !track.isGlobalTrack()) || + (mTrackCuts.cfgPVContributor && !track.isPVContributor()) || + (mTrackCuts.cfgUseITSRefit && !track.passedITSRefit()) || + (mTrackCuts.cfgUseTPCRefit && !track.passedTPCRefit())) { + return kTrkDCAz; + } + + // Clusters: found clusters exist only in ResoTracks, ITS clusters only in ResoMicroTracks + if constexpr (!IsResoMicrotrack) { + if constexpr (requires { track.tpcNClsFound(); }) { + if (track.tpcNClsFound() < mTrackCuts.cfgTPCcluster) { + return kTrkFlags; + } + } + } + if constexpr (requires { track.tpcNClsCrossedRows(); }) { + if (track.tpcNClsCrossedRows() < mTrackCuts.cfgTPCCrossedRowsMin) { + return kTrkFlags; + } + } + if constexpr (IsResoMicrotrack) { + if constexpr (requires { track.itsNCls(); }) { + if (track.itsNCls() < mTrackCuts.cfgITSNClsMin) { + return kTrkFlags; + } + } + } + return kTrkClusters; + } + + // TOF signal requirement of the track (global, per species, or per pT bin) + template + bool passesTOFRequired(int species, const TrackType& track) + { + bool required = mTrackCuts.cfgHasTOF || mPID[species].onlyTOFTracks; + PIDCut cut; + // A pT outside all bins is rejected by passesPID + if (!mByPassTOF && getPIDCut(species, track.pt(), cut) && cut.tofRequired) { + required = true; + } + return !required || track.hasTOF(); + } + + // PID selection from the nSigma values of the species; tofNSigma is used only with hasTOF. + template + bool passesPID(int species, float pt, bool hasTOF, double tpcNSigma, double tofNSigma) + { + PIDCut cut; + if (!getPIDCut(species, pt, cut)) { + return false; + } + if (isCutEnabled(cut.tpcMax) && !passesMax(std::abs(tpcNSigma), cut.tpcMax)) { + return false; + } + // Missing TOF is handled by passesTOFRequired; here the TPC alone decides + if (mByPassTOF || !hasTOF) { + return true; + } + bool tofPassed = !isCutEnabled(cut.tofMax) || passesMax(std::abs(tofNSigma), cut.tofMax); + if (!tofPassed && cut.combined > 0 && tpcNSigma * tpcNSigma + tofNSigma * tofNSigma < cut.combined * cut.combined) { + tofPassed = true; + } + return tofPassed; + } + + private: + void checkConfiguration(bool microTracks) + { + // Consistency of the pT dependent PID configuration + bool anyOnlyTOF = false; + for (const auto& config : mPID) { + if (config.usePtDependent) { + const auto& bins = config.ptBins; + if (bins.size() < MinPtBinEdges || config.tpcNSigmaCuts.size() != bins.size() - 1 || + config.tofNSigmaCuts.size() != bins.size() - 1 || config.tofRequired.size() != bins.size() - 1) { + LOG(fatal) << config.species << " pT dependent PID vectors must have (number of pT bin edges - 1) entries"; + } + } + anyOnlyTOF = anyOnlyTOF || config.onlyTOFTracks; + } + if (mByPassTOF && anyOnlyTOF) { + LOG(warning) << "cByPassTOF skips the TOF nSigma selection, but cUseOnlyTOFTrack* still requires a TOF signal"; + } + + // Micro tracks store quantised DCA and nSigma: a cut off the grid would silently act as a different cut. + if (!microTracks) { + return; + } + auto checkDCAGrid = [](const char* name, double cut) { + const double nearest = std::min(std::max(std::round(cut / DCAGridStep) * DCAGridStep, 0.), DCAGridMax); + if (std::abs(cut - nearest) > GridTolerance) { + LOG(fatal) << name << " = " << cut << " is not on the quantised DCA grid (multiples of " << DCAGridStep << " up to " << DCAGridMax << "); nearest value: " << nearest; + } + }; + auto checkPIDGrid = [](const std::string& name, double cut) { + const double nearest = std::min(std::max(PIDGridStart + std::round((cut - PIDGridStart) / PIDGridStep) * PIDGridStep, PIDGridStart), PIDGridMax); + if (std::abs(cut - nearest) > GridTolerance) { + LOG(fatal) << name << " = " << cut << " is not on the quantised nSigma grid ({2.0, 2.25, ..., 3.5}); nearest value: " << nearest; + } + }; + if (mTrackCuts.cfgUsePtDepDCA) { + LOG(info) << "Micro tracks use the producer pT dependent DCA flags (0.004 + 0.013 / pT); cDCAToPVByPt* are ignored"; + if (std::abs(mTrackCuts.cDCAToPVByPtP0 - ProducerDCAPtP0) > ConfigTolerance || std::abs(mTrackCuts.cDCAToPVByPtCoeff - ProducerDCAPtCoeff) > ConfigTolerance || std::abs(mTrackCuts.cDCAToPVByPtPower - ProducerDCAPtPower) > ConfigTolerance) { + LOG(warning) << "cDCAToPVByPt* differ from the producer defaults, but micro tracks always use the producer formula"; + } + } else { + if (isCutEnabled(mTrackCuts.cMaxDCArToPVcut)) { + checkDCAGrid("cMaxDCArToPVcut", mTrackCuts.cMaxDCArToPVcut); + } + if (isCutEnabled(mTrackCuts.cMaxDCAzToPVcut)) { + checkDCAGrid("cMaxDCAzToPVcut", mTrackCuts.cMaxDCAzToPVcut); + } + } + if (isCutEnabled(mTrackCuts.cMinDCAzToPVcut)) { + checkDCAGrid("cMinDCAzToPVcut", mTrackCuts.cMinDCAzToPVcut); + } + bool anyCombined = false; + for (const auto& config : mPID) { + if (isCutEnabled(config.maxTPCnSigma) && !config.usePtDependent) { + checkPIDGrid(config.maxTPCName, config.maxTPCnSigma); + } + if (isCutEnabled(config.maxTOFnSigma) && !config.usePtDependent) { + checkPIDGrid(config.maxTOFName, config.maxTOFnSigma); + } + anyCombined = anyCombined || config.combinedNSigma > 0; + } + for (const auto& config : mPID) { + if (!config.usePtDependent) { + continue; + } + for (const auto& cut : config.tpcNSigmaCuts) { + if (isCutEnabled(cut)) { + checkPIDGrid(config.tpcCutsName, cut); + } + } + for (const auto& cut : config.tofNSigmaCuts) { + if (isCutEnabled(cut)) { + checkPIDGrid(config.tofCutsName, cut); + } + } + } + if (anyCombined) { + LOG(warning) << "nsigmaCutCombined* on micro tracks uses quantised nSigma values (approximate)"; + } + } + + EventCuts mEventCuts; + TrackCuts mTrackCuts; + std::vector mPID; + bool mByPassTOF = false; +}; + +} // namespace o2::analysis::resonance + +#endif // PWGLF_CORE_RESOANALYSISSELECTIONCORE_H_ diff --git a/PWGLF/DataModel/LFDoubleOmegaTables.h b/PWGLF/DataModel/LFDoubleOmegaTables.h index 493d0b6d262..83cee30b84a 100644 --- a/PWGLF/DataModel/LFDoubleOmegaTables.h +++ b/PWGLF/DataModel/LFDoubleOmegaTables.h @@ -21,7 +21,7 @@ namespace o2::aod namespace DoubleOmegaTables { -DECLARE_SOA_COLUMN(PtDoubleOmega, ptDoubleOmega, float); +DECLARE_SOA_COLUMN(PtDoubleOmega, ptDoubleOmega, float); // Signed transverse momentum of the double-Omega candidate, signed by the charge of the direct kaon. DECLARE_SOA_COLUMN(EtaDoubleOmega, etaDoubleOmega, float); DECLARE_SOA_COLUMN(PhiDoubleOmega, phiDoubleOmega, float); DECLARE_SOA_COLUMN(DecayVtxX, decayVtxX, float); @@ -55,13 +55,15 @@ DECLARE_SOA_COLUMN(MassDoubleOmega, massDoubleOmega, float); DECLARE_SOA_COLUMN(MassOmega, massOmega, float); DECLARE_SOA_COLUMN(MassXi, massXi, float); -DECLARE_SOA_COLUMN(GenPt, genPt, float); +DECLARE_SOA_COLUMN(GenPt, genPt, float); // Signed transverse momentum of the generated double-Omega candidate, signed by the charge of the direct kaon. DECLARE_SOA_COLUMN(GenEta, genEta, float); DECLARE_SOA_COLUMN(GenPhi, genPhi, float); DECLARE_SOA_COLUMN(GenDecayLength, genDecayLength, float); DECLARE_SOA_COLUMN(PdgDoubleOmega, pdgDoubleOmega, int); DECLARE_SOA_COLUMN(GenMotherId, genMotherId, int64_t); // MC particle index within the input dataframe. DECLARE_SOA_COLUMN(IsReco, isReco, bool); + +DECLARE_SOA_COLUMN(MixingType, mixingType, uint8_t); // 1: Omega, 2: Lambda, 3: kaon from another collision; 4: Omega, Lambda and kaon from three different collisions. } // namespace DoubleOmegaTables #define DOUBLE_OMEGA_RECO_COLUMNS \ @@ -107,8 +109,116 @@ DECLARE_SOA_TABLE(DoubleOmegaTableMC, "AOD", "DBLOMEGAMCTABLE", DoubleOmegaTables::GenMotherId, DoubleOmegaTables::IsReco); +DECLARE_SOA_TABLE(DoubleOmegaMixedTable, "AOD", "DBLOMEGAMIXTABLE", + DOUBLE_OMEGA_RECO_COLUMNS, + DoubleOmegaTables::MixingType); + #undef DOUBLE_OMEGA_RECO_COLUMNS +// Derived data: Omega, Lambda and kaon candidates of the collisions with at least one Omega, one +// Lambda and one kaon candidate not sharing tracks with it. The tables only index each other, so +// they are self-contained and dataframes can be merged. +namespace domega +{ +DECLARE_SOA_COLUMN(MultFT0M, multFT0M, float); //! FT0M multiplicity of the collision +DECLARE_SOA_COLUMN(Bz, bz, float); //! Nominal magnetic field (kG) +} // namespace domega + +DECLARE_SOA_TABLE(DOmegaColls, "AOD", "DOMEGACOLL", //! Collisions with double-Omega building blocks + o2::soa::Index<>, + collision::PosX, collision::PosY, collision::PosZ, + domega::MultFT0M, + bc::RunNumber, + domega::Bz); + +namespace domega +{ +DECLARE_SOA_INDEX_COLUMN_FULL(DOmegaColl, dOmegaColl, int, DOmegaColls, ""); //! Collision index + +// Track parametrisation covariance, in the order expected by getTrackParCov. +DECLARE_SOA_COLUMN(CYY, cYY, float); +DECLARE_SOA_COLUMN(CZY, cZY, float); +DECLARE_SOA_COLUMN(CZZ, cZZ, float); +DECLARE_SOA_COLUMN(CSnpY, cSnpY, float); +DECLARE_SOA_COLUMN(CSnpZ, cSnpZ, float); +DECLARE_SOA_COLUMN(CSnpSnp, cSnpSnp, float); +DECLARE_SOA_COLUMN(CTglY, cTglY, float); +DECLARE_SOA_COLUMN(CTglZ, cTglZ, float); +DECLARE_SOA_COLUMN(CTglSnp, cTglSnp, float); +DECLARE_SOA_COLUMN(CTglTgl, cTglTgl, float); +DECLARE_SOA_COLUMN(C1PtY, c1PtY, float); +DECLARE_SOA_COLUMN(C1PtZ, c1PtZ, float); +DECLARE_SOA_COLUMN(C1PtSnp, c1PtSnp, float); +DECLARE_SOA_COLUMN(C1PtTgl, c1PtTgl, float); +DECLARE_SOA_COLUMN(C1Pt21Pt2, c1Pt21Pt2, float); + +DECLARE_SOA_COLUMN(TPCNSigmaKa, tpcNSigmaKa, float); //! -999 when TPC is unavailable +DECLARE_SOA_COLUMN(TOFNSigmaKa, tofNSigmaKa, float); //! -999 when TOF is unavailable +DECLARE_SOA_COLUMN(DetectorMap, detectorMap, uint8_t); //! ITS, TPC, TOF bits +DECLARE_SOA_COLUMN(DcaXYToPV, dcaXYToPV, float); //! Transverse DCA to the primary vertex of its collision +DECLARE_SOA_COLUMN(IsKaon, isKaon, bool); //! Selected as a direct-kaon candidate +} // namespace domega + +#define DOMEGA_TRACK_PAR_COV_COLUMNS \ + track::X, track::Alpha, track::Y, track::Z, track::Snp, track::Tgl, track::Signed1Pt, \ + domega::CYY, domega::CZY, domega::CZZ, domega::CSnpY, domega::CSnpZ, domega::CSnpSnp, \ + domega::CTglY, domega::CTglZ, domega::CTglSnp, domega::CTglTgl, \ + domega::C1PtY, domega::C1PtZ, domega::C1PtSnp, domega::C1PtTgl, domega::C1Pt21Pt2 + +DECLARE_SOA_TABLE(DOmegaTracks, "AOD", "DOMEGATRACK", //! Direct-kaon candidates and Omega/Lambda daughters, at the innermost update + o2::soa::Index<>, + domega::DOmegaCollId, + DOMEGA_TRACK_PAR_COV_COLUMNS, + domega::TPCNSigmaKa, + domega::TOFNSigmaKa, + domega::DetectorMap, + domega::DcaXYToPV, + domega::IsKaon, + track::Sign); + +namespace domega +{ +DECLARE_SOA_INDEX_COLUMN_FULL(PosTrack, posTrack, int, DOmegaTracks, "_Pos"); //! Positive daughter +DECLARE_SOA_INDEX_COLUMN_FULL(NegTrack, negTrack, int, DOmegaTracks, "_Neg"); //! Negative daughter +DECLARE_SOA_INDEX_COLUMN_FULL(Bachelor, bachelor, int, DOmegaTracks, "_Bach"); //! Bachelor kaon of the Omega + +DECLARE_SOA_COLUMN(DecayX, decayX, float); +DECLARE_SOA_COLUMN(DecayY, decayY, float); +DECLARE_SOA_COLUMN(DecayZ, decayZ, float); +DECLARE_SOA_COLUMN(Px, px, float); +DECLARE_SOA_COLUMN(Py, py, float); +DECLARE_SOA_COLUMN(Pz, pz, float); +DECLARE_SOA_COLUMN(MassOmega, massOmega, float); +DECLARE_SOA_COLUMN(MassXi, massXi, float); +DECLARE_SOA_COLUMN(MassLambda, massLambda, float); +DECLARE_SOA_COLUMN(DcaDaughters, dcaDaughters, float); //! DCA between the daughters +DECLARE_SOA_COLUMN(Sign, sign, int8_t); //! Charge of the Omega (Omega table) or of the Omega the Lambda can be paired with (Lambda table) +} // namespace domega + +DECLARE_SOA_TABLE(DOmegaOmegas, "AOD", "DOMEGAOMEGA", //! Omega candidates; track parameters at the Omega decay vertex + o2::soa::Index<>, + domega::DOmegaCollId, + domega::PosTrackId, domega::NegTrackId, domega::BachelorId, + DOMEGA_TRACK_PAR_COV_COLUMNS, + domega::DecayX, domega::DecayY, domega::DecayZ, + domega::Px, domega::Py, domega::Pz, + domega::MassOmega, domega::MassXi, + domega::DcaDaughters, + domega::Sign); + +DECLARE_SOA_TABLE(DOmegaLambdas, "AOD", "DOMEGALAMBDA", //! Lambda candidates; neutral track parameters at the Lambda decay vertex + o2::soa::Index<>, + domega::DOmegaCollId, + domega::PosTrackId, domega::NegTrackId, + DOMEGA_TRACK_PAR_COV_COLUMNS, + domega::DecayX, domega::DecayY, domega::DecayZ, + domega::Px, domega::Py, domega::Pz, + domega::MassLambda, + domega::DcaDaughters, + domega::Sign); + +#undef DOMEGA_TRACK_PAR_COV_COLUMNS + } // namespace o2::aod #endif // PWGLF_DATAMODEL_LFDOUBLEOMEGATABLES_H_ diff --git a/PWGLF/DataModel/LFHResonanceCorrelationTables.h b/PWGLF/DataModel/LFHResonanceCorrelationTables.h index 2bc281daaf6..3caad772493 100644 --- a/PWGLF/DataModel/LFHResonanceCorrelationTables.h +++ b/PWGLF/DataModel/LFHResonanceCorrelationTables.h @@ -53,8 +53,12 @@ DECLARE_SOA_COLUMN(MCPhysicalPrimary, mcPhysicalPrimary, bool); // true phys DECLARE_SOA_INDEX_COLUMN_FULL(Track, track, int, Tracks, "_Assoc"); //! DECLARE_SOA_COLUMN(MCOriginalPt, mcOriginalPt, float); // true generated pt DECLARE_SOA_COLUMN(PDGCode, pdgCode, int); // pdg code of the MC particle +// Rapidity under the mass hypothesis of the species this entry was actually +// selected as (pion or kaon, from isValidAssocTrack()); -999 for +// the species-agnostic AssocHadron pool, which has no mass hypothesis to use. +DECLARE_SOA_COLUMN(Rapidity, rapidity, float); } // namespace assocHadrons -DECLARE_SOA_TABLE(AssocHadrons, "AOD", "ASSOCHADRONS", o2::soa::Index<>, assocHadrons::CollisionId, assocHadrons::MCPhysicalPrimary, assocHadrons::TrackId, assocHadrons::MCOriginalPt, assocHadrons::PDGCode); +DECLARE_SOA_TABLE(AssocHadrons, "AOD", "ASSOCHADRONS", o2::soa::Index<>, assocHadrons::CollisionId, assocHadrons::MCPhysicalPrimary, assocHadrons::TrackId, assocHadrons::MCOriginalPt, assocHadrons::PDGCode, assocHadrons::Rapidity); /// _________________________________________ /// Table for storing assoc track PID namespace assocPID @@ -86,6 +90,7 @@ DECLARE_SOA_COLUMN(Pt, pt, float); DECLARE_SOA_COLUMN(Eta, eta, float); DECLARE_SOA_COLUMN(Phi, phi, float); DECLARE_SOA_COLUMN(Mass, mass, float); +DECLARE_SOA_COLUMN(Rapidity, rapidity, float); // phi candidate's own rapidity (kaon-mass-pair system) DECLARE_SOA_INDEX_COLUMN_FULL(PosTrackDaughter, posTrackDaughter, int, Tracks, "_PhiPosDaughter"); DECLARE_SOA_INDEX_COLUMN_FULL(NegTrackDaughter, negTrackDaughter, int, Tracks, "_PhiNegDaughter"); @@ -104,6 +109,7 @@ DECLARE_SOA_TABLE( assocPhis::Eta, assocPhis::Phi, assocPhis::Mass, + assocPhis::Rapidity, assocPhis::PosTrackDaughterId, assocPhis::NegTrackDaughterId); @@ -127,6 +133,7 @@ DECLARE_SOA_COLUMN(Pt, pt, float); DECLARE_SOA_COLUMN(Eta, eta, float); DECLARE_SOA_COLUMN(Phi, phi, float); DECLARE_SOA_COLUMN(Mass, mass, float); +DECLARE_SOA_COLUMN(Rapidity, rapidity, float); // K*0 candidate's own rapidity (kaon+pion system) DECLARE_SOA_INDEX_COLUMN_FULL(PosTrackDaughter, posTrackDaughter, int, Tracks, "_KstarPosDaughter"); DECLARE_SOA_INDEX_COLUMN_FULL(NegTrackDaughter, negTrackDaughter, int, Tracks, "_KstarNegDaughter"); @@ -145,6 +152,7 @@ DECLARE_SOA_TABLE( assocKstars::Eta, assocKstars::Phi, assocKstars::Mass, + assocKstars::Rapidity, assocKstars::PosTrackDaughterId, assocKstars::NegTrackDaughterId); } // namespace o2::aod diff --git a/PWGLF/DataModel/LFK1MlTables.h b/PWGLF/DataModel/LFK1MlTables.h new file mode 100644 index 00000000000..7b2c20c8174 --- /dev/null +++ b/PWGLF/DataModel/LFK1MlTables.h @@ -0,0 +1,126 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file LFK1MlTables.h +/// \brief Derived K1 microtrack training and audit tables +/// \author Bong-Hwi Lim +/// +#ifndef PWGLF_DATAMODEL_LFK1MLTABLES_H_ +#define PWGLF_DATAMODEL_LFK1MLTABLES_H_ + +#include +#include + +#include + +namespace o2::aod +{ +namespace k1ml +{ +// Persisted relations target only these derived tables, so AO2D merge tools +// can relocate them. Source AO2D row numbers remain scalar audit values. +DECLARE_SOA_COLUMN(K1RecoCollisionId, k1RecoCollisionId, int64_t); //! row index of the reduced ResoCollisions_001 collision in the input DF +DECLARE_SOA_COLUMN(K1PosZ, k1PosZ, float); +DECLARE_SOA_COLUMN(K1BField, k1BField, float); +DECLARE_SOA_COLUMN(K1Centrality, k1Centrality, float); +DECLARE_SOA_COLUMN(K1Multiplicity, k1Multiplicity, float); +DECLARE_SOA_COLUMN(K1RecINELgt0, k1RecINELgt0, bool); +DECLARE_SOA_COLUMN(K1SourceTrackId, k1SourceTrackId, int64_t); //! trackId of the reduced micro track +DECLARE_SOA_COLUMN(K1Px, k1Px, float); +DECLARE_SOA_COLUMN(K1Py, k1Py, float); +DECLARE_SOA_COLUMN(K1Pz, k1Pz, float); +DECLARE_SOA_COLUMN(K1PidPi, k1PidPi, uint8_t); +DECLARE_SOA_COLUMN(K1PidKa, k1PidKa, uint8_t); +DECLARE_SOA_COLUMN(K1PidPr, k1PidPr, uint8_t); +DECLARE_SOA_COLUMN(K1SelectionFlags, k1SelectionFlags, uint8_t); +DECLARE_SOA_COLUMN(K1TrackFlags, k1TrackFlags, uint8_t); +DECLARE_SOA_COLUMN(K1CrossedRows, k1CrossedRows, uint8_t); +DECLARE_SOA_COLUMN(K1ItsClusterMap, k1ItsClusterMap, uint8_t); +DECLARE_SOA_COLUMN(K1Mass, k1Mass, float); +DECLARE_SOA_COLUMN(K1MassPiPi, k1MassPiPi, float); +DECLARE_SOA_COLUMN(K1MassKaPiSame, k1MassKaPiSame, float); +DECLARE_SOA_COLUMN(K1MassKaPiOpp, k1MassKaPiOpp, float); +DECLARE_SOA_COLUMN(K1ScalarSumPt, k1ScalarSumPt, float); +DECLARE_SOA_COLUMN(K1PiPiPt, k1PiPiPt, float); +DECLARE_SOA_COLUMN(K1Pt, k1Pt, float); +DECLARE_SOA_COLUMN(K1Y, k1Y, float); +DECLARE_SOA_COLUMN(K1Eta, k1Eta, float); +DECLARE_SOA_COLUMN(K1Phi, k1Phi, float); +DECLARE_SOA_COLUMN(K1Charge, k1Charge, int8_t); +// Cumulative selection bits of the unlike-sign candidate: +// 1 = valid canonical candidate inside the K1 rapidity window (loose stage), +// 2 = track quality of all three tracks, +// 4 = TOF requirement and PID of all three tracks, +// 8 = pion-pair pT and secondary mass window, +// 16 = candidate cuts. +// Candidates written at the "selected" export stage carry 31. +DECLARE_SOA_COLUMN(K1BaselinePassBits, k1BaselinePassBits, uint16_t); +// NOLINTNEXTLINE(modernize-avoid-c-arrays) -- persistent fixed-size AOD column +DECLARE_SOA_COLUMN(K1MasterFeatures, k1MasterFeatures, float[125]); //! 125 master features of the K1 ML feature contract (PWGLF/Core/K1MlFeatures.h) +DECLARE_SOA_COLUMN(K1FeatureStatus, k1FeatureStatus, uint8_t); //! o2::analysis::k1ml::BuildStatus; only Ok (0) rows are written +DECLARE_SOA_COLUMN(K1TruthStatus, k1TruthStatus, uint8_t); //! 0 data, 1 matched, 2 unmatched +DECLARE_SOA_COLUMN(K1TruthChannel, k1TruthChannel, uint8_t); //! 0 none, 1 rho K, 2 K* pi +DECLARE_SOA_COLUMN(K1MotherPdg, k1MotherPdg, int32_t); +DECLARE_SOA_COLUMN(K1MotherId, k1MotherId, int64_t); +DECLARE_SOA_COLUMN(K1GeneratedPt, k1GeneratedPt, float); //! Parent-table pT for a validated reconstructed MC match; NaN otherwise +DECLARE_SOA_COLUMN(K1GeneratedY, k1GeneratedY, float); //! Parent-table y for a validated reconstructed MC match; NaN otherwise +DECLARE_SOA_COLUMN(K1OriginalMcParticleId, k1OriginalMcParticleId, int64_t); +DECLARE_SOA_COLUMN(K1DaughterPdg1, k1DaughterPdg1, int32_t); +DECLARE_SOA_COLUMN(K1DaughterPdg2, k1DaughterPdg2, int32_t); +DECLARE_SOA_COLUMN(K1GenSelectedRecoEvent, k1GenSelectedRecoEvent, bool); //! true: parents are taken from selected reconstructed events +} // namespace k1ml + +DECLARE_SOA_TABLE(K1MlEvents, "AOD", "K1MLEVENT", + o2::soa::Index<>, k1ml::K1RecoCollisionId, k1ml::K1PosZ, k1ml::K1BField, + k1ml::K1Centrality, k1ml::K1Multiplicity, k1ml::K1RecINELgt0); +namespace k1ml +{ +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(K1MlEvent, k1MlEvent, int, K1MlEvents, "K1mlEvents", ""); +} // namespace k1ml +DECLARE_SOA_TABLE(K1MlTracks, "AOD", "K1MLTRACK", + o2::soa::Index<>, k1ml::K1MlEventId, k1ml::K1SourceTrackId, + k1ml::K1Px, k1ml::K1Py, k1ml::K1Pz, + k1ml::K1PidPi, k1ml::K1PidKa, k1ml::K1PidPr, + k1ml::K1SelectionFlags, k1ml::K1TrackFlags, + k1ml::K1CrossedRows, k1ml::K1ItsClusterMap); +namespace k1ml +{ +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(K1MlKaonTrack, k1MlKaonTrack, int, K1MlTracks, "K1mlTracks", "_Kaon"); +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(K1MlSamePionTrack, k1MlSamePionTrack, int, K1MlTracks, "K1mlTracks", "_Same"); +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(K1MlOppPionTrack, k1MlOppPionTrack, int, K1MlTracks, "K1mlTracks", "_Opp"); +} // namespace k1ml +DECLARE_SOA_TABLE(K1MlCandidates, "AOD", "K1MLCANDIDATE", + o2::soa::Index<>, k1ml::K1MlEventId, + k1ml::K1MlKaonTrackId, k1ml::K1MlSamePionTrackId, k1ml::K1MlOppPionTrackId, + k1ml::K1Mass, k1ml::K1MassPiPi, k1ml::K1MassKaPiSame, k1ml::K1MassKaPiOpp, + k1ml::K1ScalarSumPt, k1ml::K1PiPiPt, + k1ml::K1Pt, k1ml::K1Y, k1ml::K1Eta, k1ml::K1Phi, k1ml::K1Charge, + k1ml::K1BaselinePassBits); +namespace k1ml +{ +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(K1MlCandidate, k1MlCandidate, int, K1MlCandidates, "K1mlCandidates", ""); +} // namespace k1ml +DECLARE_SOA_TABLE(K1MlInputs, "AOD", "K1MLINPUT", + o2::soa::Index<>, k1ml::K1MlCandidateId, + k1ml::K1MasterFeatures, k1ml::K1FeatureStatus); +DECLARE_SOA_TABLE(K1MlTruth, "AOD", "K1MLTRUTH", + o2::soa::Index<>, k1ml::K1MlCandidateId, + k1ml::K1TruthStatus, k1ml::K1TruthChannel, + k1ml::K1MotherPdg, k1ml::K1MotherId, + k1ml::K1GeneratedPt, k1ml::K1GeneratedY); +DECLARE_SOA_TABLE(K1MlGenAudit, "AOD", "K1MLGENAUDIT", + o2::soa::Index<>, k1ml::K1RecoCollisionId, k1ml::K1OriginalMcParticleId, + k1ml::K1MotherPdg, k1ml::K1DaughterPdg1, k1ml::K1DaughterPdg2, + k1ml::K1TruthChannel, k1ml::K1GeneratedPt, k1ml::K1GeneratedY, + k1ml::K1GenSelectedRecoEvent); +} // namespace o2::aod + +#endif // PWGLF_DATAMODEL_LFK1MLTABLES_H_ diff --git a/PWGLF/DataModel/LFKinkDecayTables.h b/PWGLF/DataModel/LFKinkDecayTables.h index a8a5cffa8a3..c636358e20b 100644 --- a/PWGLF/DataModel/LFKinkDecayTables.h +++ b/PWGLF/DataModel/LFKinkDecayTables.h @@ -163,6 +163,12 @@ DECLARE_SOA_TABLE(SlimKinkCandsMC, "AOD", "SLIMKINKCANDSMC", namespace sigmapluscand { +DECLARE_SOA_INDEX_COLUMN(Collision, collision); //! Collision of the candidate (the proton's collision), -1 for generated-only MC rows +DECLARE_SOA_INDEX_COLUMN_FULL(ProtonTrack, protonTrack, int, TracksIU, "_Proton"); //! Proton track +DECLARE_SOA_INDEX_COLUMN_FULL(PhotonPosTrack, photonPosTrack, int, TracksIU, "_PosDau"); //! Positive daughter track of the measured photon +DECLARE_SOA_INDEX_COLUMN_FULL(PhotonNegTrack, photonNegTrack, int, TracksIU, "_NegDau"); //! Negative daughter track of the measured photon +DECLARE_SOA_INDEX_COLUMN(McParticle, mcParticle); //! MC particle of the true Sigma+ if proton and photon come from it, -1 otherwise + DECLARE_SOA_COLUMN(XDecVtx, xDecVtx, float); //! Decay vertex of the candidate (x direction) DECLARE_SOA_COLUMN(YDecVtx, yDecVtx, float); //! Decay vertex of the candidate (y direction) DECLARE_SOA_COLUMN(ZDecVtx, zDecVtx, float); //! Decay vertex of the candidate (z direction) @@ -191,11 +197,11 @@ DECLARE_SOA_COLUMN(PhotonQt, photonQt, float); //! Armente DECLARE_SOA_COLUMN(PhotonConvRadius, photonConvRadius, float); //! Conversion radius of the measured photon (cm) DECLARE_SOA_COLUMN(PhotonOpeningAngle, photonOpeningAngle, float); //! Opening angle between the photon's e+e- daughters (rad) DECLARE_SOA_COLUMN(PhotonPointingAngle, photonPointingAngle, float); //! Angle between the photon momentum and the line from its conversion point to the candidate decay vertex (rad) -DECLARE_SOA_COLUMN(PhotonDcaToPV, photonDcaToPV, float); //! DCA of the photon's flight line to the primary vertex (cm) DECLARE_SOA_COLUMN(RootCenter, rootCenter, float); //! -coefB/(2*coefA) of the missing-photon quadratic solve: negative flags an unphysical phase-space point DECLARE_SOA_COLUMN(AntiSigmaPointingAngle, antiSigmaPointingAngle, float); //! Angle between the field-unbent proton momentum (from its original reference point) and the PV->decay-vertex direction DECLARE_SOA_COLUMN(CandDcaToPV, candDcaToPV, float); //! DCA of the candidate's total reconstructed momentum line to the PV (cm) +DECLARE_SOA_COLUMN(FlightDirTilt, flightDirTilt, float); //! Tilt of the PV->decay-vertex direction needed for the missing photon to have a real solution (rad), 0 if none needed DECLARE_SOA_COLUMN(ProtonSign, protonSign, int); //! Charge sign of the proton track (= sign of the whole candidate, since the photon is neutral) DECLARE_SOA_COLUMN(ProtonItsNCls, protonItsNCls, uint8_t); //! Number of ITS clusters of the proton track @@ -208,10 +214,27 @@ DECLARE_SOA_COLUMN(PhotonPosTpcNCls, photonPosTpcNCls, int16_t); //! Number of f DECLARE_SOA_COLUMN(PhotonNegItsNCls, photonNegItsNCls, uint8_t); //! Number of ITS clusters of the photon's negative daughter DECLARE_SOA_COLUMN(PhotonNegTpcNCls, photonNegTpcNCls, int16_t); //! Number of found TPC clusters of the photon's negative daughter +DECLARE_SOA_COLUMN(PhotonDcaDau, photonDcaDau, float); //! DCA between the photon's e+e- daughters at the conversion point (cm) +DECLARE_SOA_COLUMN(PhotonCosPAToPV, photonCosPAToPV, float); //! Cosine of the angle between the photon momentum and the PV->conversion-point direction +DECLARE_SOA_COLUMN(PhotonDcaXYToPV, photonDcaXYToPV, float); //! Transverse DCA of the photon's flight line to the PV (cm) +DECLARE_SOA_COLUMN(PhotonDcaZToPV, photonDcaZToPV, float); //! Longitudinal distance of the photon's flight line to the PV at its transverse DCA (cm) +DECLARE_SOA_COLUMN(PhotonPsiPair, photonPsiPair, float); //! psi_pair of the e+e- legs, evaluated after propagating them outward, 999 if the propagation failed +DECLARE_SOA_COLUMN(PhotonPosDcaXY, photonPosDcaXY, float); //! DCAxy of the photon's positive daughter to the PV of its own collision (cm) +DECLARE_SOA_COLUMN(PhotonPosDcaZ, photonPosDcaZ, float); //! DCAz of the photon's positive daughter to the PV of its own collision (cm) +DECLARE_SOA_COLUMN(PhotonNegDcaXY, photonNegDcaXY, float); //! DCAxy of the photon's negative daughter to the PV of its own collision (cm) +DECLARE_SOA_COLUMN(PhotonNegDcaZ, photonNegDcaZ, float); //! DCAz of the photon's negative daughter to the PV of its own collision (cm) +DECLARE_SOA_COLUMN(PhotonPosTpcNClsFindable, photonPosTpcNClsFindable, int16_t); //! Number of findable TPC clusters of the photon's positive daughter +DECLARE_SOA_COLUMN(PhotonNegTpcNClsFindable, photonNegTpcNClsFindable, int16_t); //! Number of findable TPC clusters of the photon's negative daughter +DECLARE_SOA_COLUMN(PhotonPosTpcChi2NCl, photonPosTpcChi2NCl, float); //! TPC chi2 per cluster of the photon's positive daughter +DECLARE_SOA_COLUMN(PhotonNegTpcChi2NCl, photonNegTpcChi2NCl, float); //! TPC chi2 per cluster of the photon's negative daughter +DECLARE_SOA_COLUMN(MlScore, mlScore, float); //! BDT score of the candidate (signal probability), -1 if not computed + // MC columns DECLARE_SOA_COLUMN(CollisionIdCheck, collisionIdCheck, bool); //! True if the proton's collision ID matches the reconstructed collision ID -DECLARE_SOA_COLUMN(IsSignal, isSignal, bool); //! True if the proton and photon share the same true Sigma+ mother +DECLARE_SOA_COLUMN(IsSignal, isSignal, bool); //! True if the proton and photon share the same true Sigma+ mother +DECLARE_SOA_COLUMN(IsProtonFromSigma, isProtonFromSigma, bool); //! True if the proton is the daughter of a true Sigma+ -> p pi0 +DECLARE_SOA_COLUMN(IsPhotonFromSigma, isPhotonFromSigma, bool); //! True if the photon is from the pi0 of a true Sigma+ -> p pi0 DECLARE_SOA_COLUMN(XDecVtxMC, xDecVtxMC, float); //! MC-truth Sigma+ decay vertex (x direction) DECLARE_SOA_COLUMN(YDecVtxMC, yDecVtxMC, float); //! MC-truth Sigma+ decay vertex (y direction) @@ -265,6 +288,7 @@ DECLARE_SOA_DYNAMIC_COLUMN(YSigmaPlusMC, ySigmaPlusMC, //! True rapidity of the } // namespace sigmapluscand DECLARE_SOA_TABLE(SigmaPlusCands, "AOD", "SIGMAPLUSCANDS", + sigmapluscand::CollisionId, sigmapluscand::ProtonTrackId, sigmapluscand::PhotonPosTrackId, sigmapluscand::PhotonNegTrackId, sigmapluscand::XDecVtx, sigmapluscand::YDecVtx, sigmapluscand::ZDecVtx, sigmapluscand::FlightDistance, sigmapluscand::DcaProtonGamma, sigmapluscand::PxProton, sigmapluscand::PyProton, sigmapluscand::PzProton, @@ -273,11 +297,15 @@ DECLARE_SOA_TABLE(SigmaPlusCands, "AOD", "SIGMAPLUSCANDS", sigmapluscand::NSigmaTPCProton, sigmapluscand::NSigmaTOFProton, sigmapluscand::NSigmaTPCElPos, sigmapluscand::NSigmaTPCElNeg, sigmapluscand::PhotonMass, sigmapluscand::PhotonAlpha, sigmapluscand::PhotonQt, sigmapluscand::PhotonConvRadius, - sigmapluscand::PhotonOpeningAngle, sigmapluscand::PhotonPointingAngle, sigmapluscand::PhotonDcaToPV, - sigmapluscand::RootCenter, sigmapluscand::AntiSigmaPointingAngle, sigmapluscand::CandDcaToPV, + sigmapluscand::PhotonOpeningAngle, sigmapluscand::PhotonPointingAngle, + sigmapluscand::RootCenter, sigmapluscand::AntiSigmaPointingAngle, sigmapluscand::CandDcaToPV, sigmapluscand::FlightDirTilt, sigmapluscand::ProtonSign, sigmapluscand::ProtonItsNCls, sigmapluscand::ProtonTpcNCls, sigmapluscand::ProtonDcaXY, sigmapluscand::ProtonDcaZ, sigmapluscand::PhotonPosItsNCls, sigmapluscand::PhotonPosTpcNCls, sigmapluscand::PhotonNegItsNCls, sigmapluscand::PhotonNegTpcNCls, + sigmapluscand::PhotonDcaDau, sigmapluscand::PhotonCosPAToPV, sigmapluscand::PhotonDcaXYToPV, sigmapluscand::PhotonDcaZToPV, sigmapluscand::PhotonPsiPair, + sigmapluscand::PhotonPosDcaXY, sigmapluscand::PhotonPosDcaZ, sigmapluscand::PhotonNegDcaXY, sigmapluscand::PhotonNegDcaZ, + sigmapluscand::PhotonPosTpcNClsFindable, sigmapluscand::PhotonNegTpcNClsFindable, sigmapluscand::PhotonPosTpcChi2NCl, sigmapluscand::PhotonNegTpcChi2NCl, + sigmapluscand::MlScore, // dynamic columns sigmapluscand::Radius, @@ -289,6 +317,8 @@ DECLARE_SOA_TABLE(SigmaPlusCands, "AOD", "SIGMAPLUSCANDS", sigmapluscand::MassSigmaPlus); DECLARE_SOA_TABLE(SigmaPlusCandsMC, "AOD", "SIGMAPLUSMC", + sigmapluscand::CollisionId, sigmapluscand::ProtonTrackId, sigmapluscand::PhotonPosTrackId, sigmapluscand::PhotonNegTrackId, + sigmapluscand::McParticleId, sigmapluscand::XDecVtx, sigmapluscand::YDecVtx, sigmapluscand::ZDecVtx, sigmapluscand::FlightDistance, sigmapluscand::DcaProtonGamma, sigmapluscand::PxProton, sigmapluscand::PyProton, sigmapluscand::PzProton, @@ -297,13 +327,17 @@ DECLARE_SOA_TABLE(SigmaPlusCandsMC, "AOD", "SIGMAPLUSMC", sigmapluscand::NSigmaTPCProton, sigmapluscand::NSigmaTOFProton, sigmapluscand::NSigmaTPCElPos, sigmapluscand::NSigmaTPCElNeg, sigmapluscand::PhotonMass, sigmapluscand::PhotonAlpha, sigmapluscand::PhotonQt, sigmapluscand::PhotonConvRadius, - sigmapluscand::PhotonOpeningAngle, sigmapluscand::PhotonPointingAngle, sigmapluscand::PhotonDcaToPV, - sigmapluscand::RootCenter, sigmapluscand::AntiSigmaPointingAngle, sigmapluscand::CandDcaToPV, + sigmapluscand::PhotonOpeningAngle, sigmapluscand::PhotonPointingAngle, + sigmapluscand::RootCenter, sigmapluscand::AntiSigmaPointingAngle, sigmapluscand::CandDcaToPV, sigmapluscand::FlightDirTilt, sigmapluscand::ProtonSign, sigmapluscand::ProtonItsNCls, sigmapluscand::ProtonTpcNCls, sigmapluscand::ProtonDcaXY, sigmapluscand::ProtonDcaZ, sigmapluscand::PhotonPosItsNCls, sigmapluscand::PhotonPosTpcNCls, sigmapluscand::PhotonNegItsNCls, sigmapluscand::PhotonNegTpcNCls, + sigmapluscand::PhotonDcaDau, sigmapluscand::PhotonCosPAToPV, sigmapluscand::PhotonDcaXYToPV, sigmapluscand::PhotonDcaZToPV, sigmapluscand::PhotonPsiPair, + sigmapluscand::PhotonPosDcaXY, sigmapluscand::PhotonPosDcaZ, sigmapluscand::PhotonNegDcaXY, sigmapluscand::PhotonNegDcaZ, + sigmapluscand::PhotonPosTpcNClsFindable, sigmapluscand::PhotonNegTpcNClsFindable, sigmapluscand::PhotonPosTpcChi2NCl, sigmapluscand::PhotonNegTpcChi2NCl, + sigmapluscand::MlScore, sigmapluscand::CollisionIdCheck, - sigmapluscand::IsSignal, + sigmapluscand::IsSignal, sigmapluscand::IsProtonFromSigma, sigmapluscand::IsPhotonFromSigma, sigmapluscand::XDecVtxMC, sigmapluscand::YDecVtxMC, sigmapluscand::ZDecVtxMC, sigmapluscand::PxProtonMC, sigmapluscand::PyProtonMC, sigmapluscand::PzProtonMC, sigmapluscand::PxGammaMC, sigmapluscand::PyGammaMC, sigmapluscand::PzGammaMC, @@ -322,8 +356,9 @@ DECLARE_SOA_TABLE(SigmaPlusCandsMC, "AOD", "SIGMAPLUSMC", sigmapluscand::YSigmaPlusMC); DECLARE_SOA_TABLE(SlimSigmaPlusCands, "AOD", "SLIMSIGMAPLUS", + sigmapluscand::CollisionId, sigmapluscand::ProtonTrackId, sigmapluscand::PhotonPosTrackId, sigmapluscand::PhotonNegTrackId, sigmapluscand::TransDecayRadius, - sigmapluscand::CandDcaToPV, + sigmapluscand::CandDcaToPV, sigmapluscand::FlightDirTilt, sigmapluscand::DcaProtonGamma, sigmapluscand::ProtonSign, sigmapluscand::ProtonDcaXY, sigmapluscand::ProtonDcaZ, @@ -333,6 +368,10 @@ DECLARE_SOA_TABLE(SlimSigmaPlusCands, "AOD", "SLIMSIGMAPLUS", sigmapluscand::NSigmaTPCProton, sigmapluscand::NSigmaTOFProton, sigmapluscand::NSigmaTPCElPos, sigmapluscand::NSigmaTPCElNeg, sigmapluscand::PhotonMass, + sigmapluscand::PhotonDcaDau, sigmapluscand::PhotonCosPAToPV, sigmapluscand::PhotonDcaXYToPV, sigmapluscand::PhotonDcaZToPV, sigmapluscand::PhotonPsiPair, + sigmapluscand::PhotonPosDcaXY, sigmapluscand::PhotonPosDcaZ, sigmapluscand::PhotonNegDcaXY, sigmapluscand::PhotonNegDcaZ, + sigmapluscand::PhotonPosTpcNClsFindable, sigmapluscand::PhotonNegTpcNClsFindable, sigmapluscand::PhotonPosTpcChi2NCl, sigmapluscand::PhotonNegTpcChi2NCl, + sigmapluscand::MlScore, // dynamic columns sigmapluscand::PxSigmaPlus, @@ -342,8 +381,10 @@ DECLARE_SOA_TABLE(SlimSigmaPlusCands, "AOD", "SLIMSIGMAPLUS", sigmapluscand::MassSigmaPlus); DECLARE_SOA_TABLE(SlimSigmaPlusCandsMC, "AOD", "SLIMSIGMAPLUSMC", + sigmapluscand::CollisionId, sigmapluscand::ProtonTrackId, sigmapluscand::PhotonPosTrackId, sigmapluscand::PhotonNegTrackId, + sigmapluscand::McParticleId, sigmapluscand::TransDecayRadius, - sigmapluscand::CandDcaToPV, + sigmapluscand::CandDcaToPV, sigmapluscand::FlightDirTilt, sigmapluscand::DcaProtonGamma, sigmapluscand::ProtonSign, sigmapluscand::ProtonDcaXY, sigmapluscand::ProtonDcaZ, @@ -353,8 +394,12 @@ DECLARE_SOA_TABLE(SlimSigmaPlusCandsMC, "AOD", "SLIMSIGMAPLUSMC", sigmapluscand::NSigmaTPCProton, sigmapluscand::NSigmaTOFProton, sigmapluscand::NSigmaTPCElPos, sigmapluscand::NSigmaTPCElNeg, sigmapluscand::PhotonMass, + sigmapluscand::PhotonDcaDau, sigmapluscand::PhotonCosPAToPV, sigmapluscand::PhotonDcaXYToPV, sigmapluscand::PhotonDcaZToPV, sigmapluscand::PhotonPsiPair, + sigmapluscand::PhotonPosDcaXY, sigmapluscand::PhotonPosDcaZ, sigmapluscand::PhotonNegDcaXY, sigmapluscand::PhotonNegDcaZ, + sigmapluscand::PhotonPosTpcNClsFindable, sigmapluscand::PhotonNegTpcNClsFindable, sigmapluscand::PhotonPosTpcChi2NCl, sigmapluscand::PhotonNegTpcChi2NCl, + sigmapluscand::MlScore, sigmapluscand::CollisionIdCheck, - sigmapluscand::IsSignal, + sigmapluscand::IsSignal, sigmapluscand::IsProtonFromSigma, sigmapluscand::IsPhotonFromSigma, sigmapluscand::DecayRadiusMC, sigmapluscand::MassMC, sigmapluscand::PxSigmaPlusMC, sigmapluscand::PySigmaPlusMC, sigmapluscand::PzSigmaPlusMC, diff --git a/PWGLF/DataModel/LFLambda1405Table.h b/PWGLF/DataModel/LFLambda1405Table.h index 44339682b7c..571399d6b5a 100644 --- a/PWGLF/DataModel/LFLambda1405Table.h +++ b/PWGLF/DataModel/LFLambda1405Table.h @@ -45,6 +45,7 @@ DECLARE_SOA_COLUMN(NSigmaTPCPrKink, nSigmaTPCPrKink, float); //! Number of sigma DECLARE_SOA_COLUMN(NSigmaTOFPrKink, nSigmaTOFPrKink, float); //! Number of sigmas for the proton candidate from Sigma kink in TOF DECLARE_SOA_COLUMN(DcaKinkDauToPv, dcaKinkDauToPv, float); //! DCA of the kink daughter to the primary vertex DECLARE_SOA_COLUMN(DcaSigmaToPv, dcaSigmaToPv, float); //! DCA of the sigma to the primary vertex +DECLARE_SOA_COLUMN(KinkChi2PCA, kinkChi2PCA, float); //! Chi2 DCA fitter at the PCA DECLARE_SOA_COLUMN(NSigmaTPCPiDau, nSigmaTPCPiDau, float); //! Number of sigmas for the lambda1405 pion daughter in TPC DECLARE_SOA_COLUMN(NSigmaTOFPiDau, nSigmaTOFPiDau, float); //! Number of sigmas for the lambda1405 pion daughter in TOF @@ -111,7 +112,7 @@ DECLARE_SOA_TABLE(Lambda1405Cands, "AOD", "LAMBDA1405", lambda1405::PtKink, lambda1405::NSigmaTPCPiKink, lambda1405::NSigmaTOFPiKink, lambda1405::NSigmaTPCPrKink, lambda1405::NSigmaTOFPrKink, - lambda1405::DcaKinkDauToPv, + lambda1405::DcaKinkDauToPv, lambda1405::DcaSigmaToPv, lambda1405::KinkChi2PCA, lambda1405::NSigmaTPCPiDau, lambda1405::NSigmaTOFPiDau, lambda1405::ScalarProd, lambda1405::Centrality, lambda1405::Occupancy, lambda1405::PoolBin); @@ -125,7 +126,7 @@ DECLARE_SOA_TABLE(Lambda1405CandsMC, "AOD", "MCLAMBDA1405", lambda1405::PtKink, lambda1405::NSigmaTPCPiKink, lambda1405::NSigmaTOFPiKink, lambda1405::NSigmaTPCPrKink, lambda1405::NSigmaTOFPrKink, - lambda1405::DcaKinkDauToPv, + lambda1405::DcaKinkDauToPv, lambda1405::DcaSigmaToPv, lambda1405::KinkChi2PCA, lambda1405::NSigmaTPCPiDau, lambda1405::NSigmaTOFPiDau, lambda1405::PtMC, lambda1405::MassMC, lambda1405::SigmaPdgCode, lambda1405::KinkDauPdgCode, lambda1405::Centrality, lambda1405::Occupancy); @@ -151,7 +152,7 @@ DECLARE_SOA_TABLE(Lambda1405SigmaEffMC, "AOD", "MCL1405SIGEFF", lambda1405::ZKinkVtx, lambda1405::DeltaZKinkVtx, lambda1405::AlphaAPSigma, lambda1405::QtAPSigma, lambda1405::AlphaAPSigmaRecalc, lambda1405::QtAPSigmaRecalc, - lambda1405::DcaKinkDauToPv, lambda1405::DcaSigmaToPv, + lambda1405::DcaKinkDauToPv, lambda1405::DcaSigmaToPv, lambda1405::KinkChi2PCA, lambda1405::SigmaPdgCode, lambda1405::KinkDauPdgCode, lambda1405::Centrality, lambda1405::Occupancy, lambda1405::PvContrib); diff --git a/PWGLF/DataModel/LFNonPromptCascadeTables.h b/PWGLF/DataModel/LFNonPromptCascadeTables.h index 25449bc2f81..f8a532448f4 100644 --- a/PWGLF/DataModel/LFNonPromptCascadeTables.h +++ b/PWGLF/DataModel/LFNonPromptCascadeTables.h @@ -63,6 +63,9 @@ DECLARE_SOA_COLUMN(PionDCAz, pionDCAz, float); DECLARE_SOA_COLUMN(BachDCAxy, bachDCAxy, float); DECLARE_SOA_COLUMN(BachDCAz, bachDCAz, float); +DECLARE_SOA_COLUMN(CascDCADaughters, cascDCADaughters, float); // DCA between V0 and bachelor +DECLARE_SOA_COLUMN(V0DCADaughters, v0DCADaughters, float); // DCA between proton and pion + DECLARE_SOA_COLUMN(CascCosPA, casccosPA, float); DECLARE_SOA_COLUMN(V0CosPA, v0cosPA, float); @@ -167,6 +170,8 @@ DECLARE_SOA_TABLE(NPCascTable, "AOD", "NPCASCTABLE", NPCascadeTable::PionDCAz, NPCascadeTable::BachDCAxy, NPCascadeTable::BachDCAz, + NPCascadeTable::CascDCADaughters, + NPCascadeTable::V0DCADaughters, NPCascadeTable::CascCosPA, NPCascadeTable::V0CosPA, NPCascadeTable::MassXi, @@ -235,6 +240,8 @@ DECLARE_SOA_TABLE(NPCascTableNT, "AOD", "NPCASCTABLENT", NPCascadeTable::PionDCAz, NPCascadeTable::BachDCAxy, NPCascadeTable::BachDCAz, + NPCascadeTable::CascDCADaughters, + NPCascadeTable::V0DCADaughters, NPCascadeTable::CascCosPA, NPCascadeTable::V0CosPA, NPCascadeTable::MassXi, @@ -309,6 +316,8 @@ DECLARE_SOA_TABLE(NPCascTableMC, "AOD", "NPCASCTABLEMC", NPCascadeTable::PionDCAz, NPCascadeTable::BachDCAxy, NPCascadeTable::BachDCAz, + NPCascadeTable::CascDCADaughters, + NPCascadeTable::V0DCADaughters, NPCascadeTable::CascCosPA, NPCascadeTable::V0CosPA, NPCascadeTable::MassXi, @@ -396,6 +405,8 @@ DECLARE_SOA_TABLE(NPCascTableMCNT, "AOD", "NPCASCTABLEMCNT", NPCascadeTable::PionDCAz, NPCascadeTable::BachDCAxy, NPCascadeTable::BachDCAz, + NPCascadeTable::CascDCADaughters, + NPCascadeTable::V0DCADaughters, NPCascadeTable::CascCosPA, NPCascadeTable::V0CosPA, NPCascadeTable::MassXi, diff --git a/PWGLF/DataModel/LFOmega2012MlTables.h b/PWGLF/DataModel/LFOmega2012MlTables.h new file mode 100644 index 00000000000..e17bde5d569 --- /dev/null +++ b/PWGLF/DataModel/LFOmega2012MlTables.h @@ -0,0 +1,273 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file LFOmega2012MlTables.h +/// \brief Derived Omega(2012) training and audit tables, separate for the XiK0s and Xi1530K decay modes +/// \author Bong-Hwi Lim +/// +/// Mode A (XiK0s): Omega(2012)- -> Xi- K0S. Mode B (Xi1530K): Omega(2012)- -> Xi(1530)0 K- -> Xi- pi+ K-. +/// Only the input-object tables (events, cascades, V0s, tracks) are common; every event row belongs to one +/// decay mode (column omDecayMode), so the object rows of the two modes never mix either. +/// Event rows are written only for collisions with at least one written candidate. In reconstructed MC the +/// object MC labels and the pruned McParticles rows replace the source MC tables, which are not written. +/// +/// Cumulative pass bits (each bit requires all lower bits): +/// mode A: 1 loose (valid canonical candidate inside the rapidity window), 2 Xi selection, 4 K0s selection, +/// 8 Xi-K0s kinematic (opening-angle) cut; selected = 15. +/// mode B: 1 loose (valid canonical candidate inside the rapidity window), 2 Xi selection, 4 track quality of the +/// pion and the kaon, 8 TOF requirement and PID of the pion and the kaon, 16 Xi(1530) mass window; +/// selected = 31. +#ifndef PWGLF_DATAMODEL_LFOMEGA2012MLTABLES_H_ +#define PWGLF_DATAMODEL_LFOMEGA2012MLTABLES_H_ + +#include +#include + +#include + +namespace o2::aod +{ +namespace omega2012ml +{ +// Persisted relations target only these derived tables; source AO2D row numbers are scalar audit values. +// Events +DECLARE_SOA_COLUMN(OmDecayMode, omDecayMode, uint8_t); //! 0 XiK0s, 1 Xi1530K: the mode whose candidates reference this event row +DECLARE_SOA_COLUMN(OmRecoCollisionId, omRecoCollisionId, int64_t); //! row of the reduced ResoCollisions_001 collision in the input DF +DECLARE_SOA_COLUMN(OmPosZ, omPosZ, float); +DECLARE_SOA_COLUMN(OmBField, omBField, float); +DECLARE_SOA_COLUMN(OmCentrality, omCentrality, float); +DECLARE_SOA_COLUMN(OmMultiplicity, omMultiplicity, float); +DECLARE_SOA_COLUMN(OmRecINELgt0, omRecINELgt0, bool); +// Common object columns +DECLARE_SOA_COLUMN(OmSourceRow, omSourceRow, int64_t); //! row of the object in the input ResoCascades / ResoV0s table +DECLARE_SOA_COLUMN(OmPx, omPx, float); +DECLARE_SOA_COLUMN(OmPy, omPy, float); +DECLARE_SOA_COLUMN(OmPz, omPz, float); +DECLARE_SOA_COLUMN(OmV0CosPA, omV0CosPA, float); +DECLARE_SOA_COLUMN(OmV0DaughDCA, omV0DaughDCA, float); +DECLARE_SOA_COLUMN(OmDcaPosToPV, omDcaPosToPV, float); +DECLARE_SOA_COLUMN(OmDcaNegToPV, omDcaNegToPV, float); +DECLARE_SOA_COLUMN(OmDcaV0ToPV, omDcaV0ToPV, float); +DECLARE_SOA_COLUMN(OmV0Radius, omV0Radius, float); +DECLARE_SOA_COLUMN(OmMassLambda, omMassLambda, float); +DECLARE_SOA_COLUMN(OmDecayVtxX, omDecayVtxX, float); +DECLARE_SOA_COLUMN(OmDecayVtxY, omDecayVtxY, float); +DECLARE_SOA_COLUMN(OmDecayVtxZ, omDecayVtxZ, float); +DECLARE_SOA_COLUMN(OmTpcPosPi10, omTpcPosPi10, int8_t); //! TPC nSigma x10 of the positive daughter (pion hypothesis), as in the reduced tables +DECLARE_SOA_COLUMN(OmTpcPosKa10, omTpcPosKa10, int8_t); +DECLARE_SOA_COLUMN(OmTpcPosPr10, omTpcPosPr10, int8_t); +DECLARE_SOA_COLUMN(OmTpcNegPi10, omTpcNegPi10, int8_t); +DECLARE_SOA_COLUMN(OmTpcNegKa10, omTpcNegKa10, int8_t); +DECLARE_SOA_COLUMN(OmTpcNegPr10, omTpcNegPr10, int8_t); +DECLARE_SOA_COLUMN(OmTofPosPi10, omTofPosPi10, int8_t); //! TOF nSigma x10 of the positive daughter (pion hypothesis), as in the reduced tables +DECLARE_SOA_COLUMN(OmTofPosKa10, omTofPosKa10, int8_t); +DECLARE_SOA_COLUMN(OmTofPosPr10, omTofPosPr10, int8_t); +DECLARE_SOA_COLUMN(OmTofNegPi10, omTofNegPi10, int8_t); +DECLARE_SOA_COLUMN(OmTofNegKa10, omTofNegKa10, int8_t); +DECLARE_SOA_COLUMN(OmTofNegPr10, omTofNegPr10, int8_t); +DECLARE_SOA_COLUMN(OmCrossedRowsPos, omCrossedRowsPos, uint8_t); +DECLARE_SOA_COLUMN(OmCrossedRowsNeg, omCrossedRowsNeg, uint8_t); +// Cascade-only columns +DECLARE_SOA_COLUMN(OmSign, omSign, int8_t); +DECLARE_SOA_COLUMN(OmMassXi, omMassXi, float); +DECLARE_SOA_COLUMN(OmCascCosPA, omCascCosPA, float); +DECLARE_SOA_COLUMN(OmCascDaughDCA, omCascDaughDCA, float); +DECLARE_SOA_COLUMN(OmDcaBachToPV, omDcaBachToPV, float); +DECLARE_SOA_COLUMN(OmDcaXYCascToPV, omDcaXYCascToPV, float); +DECLARE_SOA_COLUMN(OmDcaZCascToPV, omDcaZCascToPV, float); +DECLARE_SOA_COLUMN(OmCascRadius, omCascRadius, float); +DECLARE_SOA_COLUMN(OmTpcBachPi10, omTpcBachPi10, int8_t); +DECLARE_SOA_COLUMN(OmTpcBachKa10, omTpcBachKa10, int8_t); +DECLARE_SOA_COLUMN(OmTpcBachPr10, omTpcBachPr10, int8_t); +DECLARE_SOA_COLUMN(OmTofBachPi10, omTofBachPi10, int8_t); +DECLARE_SOA_COLUMN(OmTofBachKa10, omTofBachKa10, int8_t); +DECLARE_SOA_COLUMN(OmTofBachPr10, omTofBachPr10, int8_t); +DECLARE_SOA_COLUMN(OmCrossedRowsBach, omCrossedRowsBach, uint8_t); +// The daughter-ID arrays mirror the persistent ResoCascades / ResoV0s columns. +DECLARE_SOA_COLUMN(OmCascadeIndices, omCascadeIndices, int[3]); //! source track IDs of the cascade daughters (positive, negative, bachelor) +// V0-only columns +DECLARE_SOA_COLUMN(OmMassK0Short, omMassK0Short, float); +DECLARE_SOA_COLUMN(OmMassAntiLambda, omMassAntiLambda, float); +DECLARE_SOA_COLUMN(OmArmAlpha, omArmAlpha, float); +DECLARE_SOA_COLUMN(OmArmQt, omArmQt, float); +DECLARE_SOA_COLUMN(OmV0Indices, omV0Indices, int[2]); //! source track IDs of the V0 daughters (positive, negative) +// Track columns +DECLARE_SOA_COLUMN(OmSourceTrackId, omSourceTrackId, int64_t); //! trackId of the reduced micro track +DECLARE_SOA_COLUMN(OmPidPi, omPidPi, uint8_t); +DECLARE_SOA_COLUMN(OmPidKa, omPidKa, uint8_t); +DECLARE_SOA_COLUMN(OmPidPr, omPidPr, uint8_t); +DECLARE_SOA_COLUMN(OmSelectionFlags, omSelectionFlags, uint8_t); +DECLARE_SOA_COLUMN(OmTrackFlags, omTrackFlags, uint8_t); +DECLARE_SOA_COLUMN(OmCrossedRows, omCrossedRows, uint8_t); +DECLARE_SOA_COLUMN(OmItsClusterMap, omItsClusterMap, uint8_t); +// Candidate columns +DECLARE_SOA_COLUMN(OmMass, omMass, float); +DECLARE_SOA_COLUMN(OmPt, omPt, float); +DECLARE_SOA_COLUMN(OmY, omY, float); +DECLARE_SOA_COLUMN(OmEta, omEta, float); +DECLARE_SOA_COLUMN(OmPhi, omPhi, float); +DECLARE_SOA_COLUMN(OmOpeningAngle, omOpeningAngle, float); //! Xi-K0s opening angle alpha_oa of the kinematic cut +DECLARE_SOA_COLUMN(OmCharge, omCharge, int8_t); //! sign of the Xi +DECLARE_SOA_COLUMN(OmMassXiPi, omMassXiPi, float); +DECLARE_SOA_COLUMN(OmMassXiK, omMassXiK, float); +DECLARE_SOA_COLUMN(OmMassPiK, omMassPiK, float); +DECLARE_SOA_COLUMN(OmChargePattern, omChargePattern, uint8_t); //! 0 signal (pion opposite to the Xi, kaon equal), 1 wrong-sign pion, 2 wrong-sign kaon, 3 both +DECLARE_SOA_COLUMN(OmPassBits, omPassBits, uint16_t); //! cumulative pass bits of the mode, see the file header +DECLARE_SOA_COLUMN(OmXiK0sFeatures, omXiK0sFeatures, float[63]); //! master features of the XiK0s contract (PWGLF/Core/Omega2012MlFeatures.h) +DECLARE_SOA_COLUMN(OmXi1530KFeatures, omXi1530KFeatures, float[126]); //! master features of the Xi1530K contract (PWGLF/Core/Omega2012MlFeatures.h) +DECLARE_SOA_COLUMN(OmFeatureStatus, omFeatureStatus, uint8_t); //! o2::analysis::omega2012ml::BuildStatus; only Ok (0) rows are written +// Truth and generated audit +DECLARE_SOA_COLUMN(OmTruthStatus, omTruthStatus, uint8_t); //! 0 data, 1 matched, 2 unmatched +DECLARE_SOA_COLUMN(OmMotherPdg, omMotherPdg, int32_t); +DECLARE_SOA_COLUMN(OmMotherId, omMotherId, int64_t); +DECLARE_SOA_COLUMN(OmXiMotherPdg, omXiMotherPdg, int32_t); //! immediate-mother PDG of the Xi (any candidate) +DECLARE_SOA_COLUMN(OmV0MotherPdg, omV0MotherPdg, int32_t); //! immediate-mother PDG of the V0 (any candidate; 311 shows a K0bar intermediate) +DECLARE_SOA_COLUMN(OmXi1530Id, omXi1530Id, int64_t); //! immediate-mother ID of the Xi (the Xi(1530)0 candidate) +DECLARE_SOA_COLUMN(OmOriginalMcParticleId, omOriginalMcParticleId, int64_t); +DECLARE_SOA_COLUMN(OmPdg, omPdg, int32_t); +DECLARE_SOA_COLUMN(OmDaughterPdg1, omDaughterPdg1, int32_t); +DECLARE_SOA_COLUMN(OmDaughterPdg2, omDaughterPdg2, int32_t); +DECLARE_SOA_COLUMN(OmGenChannel, omGenChannel, uint8_t); //! immediate channel of a generated Omega(2012): 0 other, 1 XiK0s, 2 Xi1530K +DECLARE_SOA_COLUMN(OmGenPt, omGenPt, float); +DECLARE_SOA_COLUMN(OmGenY, omGenY, float); +} // namespace omega2012ml + +DECLARE_SOA_TABLE(Omega2012MlEvents, "AOD", "OMMLEVENT", + o2::soa::Index<>, omega2012ml::OmDecayMode, omega2012ml::OmRecoCollisionId, + omega2012ml::OmPosZ, omega2012ml::OmBField, omega2012ml::OmCentrality, + omega2012ml::OmMultiplicity, omega2012ml::OmRecINELgt0); +namespace omega2012ml +{ +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Omega2012MlEvent, omega2012MlEvent, int, Omega2012MlEvents, "OmmlEvents", ""); +} // namespace omega2012ml + +DECLARE_SOA_TABLE(Omega2012MlCascades, "AOD", "OMMLCASCADE", + o2::soa::Index<>, omega2012ml::Omega2012MlEventId, omega2012ml::OmSourceRow, + omega2012ml::OmPx, omega2012ml::OmPy, omega2012ml::OmPz, omega2012ml::OmSign, + omega2012ml::OmMassXi, omega2012ml::OmMassLambda, + omega2012ml::OmV0CosPA, omega2012ml::OmCascCosPA, omega2012ml::OmV0DaughDCA, omega2012ml::OmCascDaughDCA, + omega2012ml::OmDcaPosToPV, omega2012ml::OmDcaNegToPV, omega2012ml::OmDcaBachToPV, omega2012ml::OmDcaV0ToPV, + omega2012ml::OmDcaXYCascToPV, omega2012ml::OmDcaZCascToPV, + omega2012ml::OmV0Radius, omega2012ml::OmCascRadius, + omega2012ml::OmDecayVtxX, omega2012ml::OmDecayVtxY, omega2012ml::OmDecayVtxZ, + omega2012ml::OmTpcPosPi10, omega2012ml::OmTpcPosKa10, omega2012ml::OmTpcPosPr10, + omega2012ml::OmTpcNegPi10, omega2012ml::OmTpcNegKa10, omega2012ml::OmTpcNegPr10, + omega2012ml::OmTpcBachPi10, omega2012ml::OmTpcBachKa10, omega2012ml::OmTpcBachPr10, + omega2012ml::OmTofPosPi10, omega2012ml::OmTofPosKa10, omega2012ml::OmTofPosPr10, + omega2012ml::OmTofNegPi10, omega2012ml::OmTofNegKa10, omega2012ml::OmTofNegPr10, + omega2012ml::OmTofBachPi10, omega2012ml::OmTofBachKa10, omega2012ml::OmTofBachPr10, + omega2012ml::OmCrossedRowsPos, omega2012ml::OmCrossedRowsNeg, omega2012ml::OmCrossedRowsBach, + omega2012ml::OmCascadeIndices); +DECLARE_SOA_TABLE(Omega2012MlV0s, "AOD", "OMMLV0", + o2::soa::Index<>, omega2012ml::Omega2012MlEventId, omega2012ml::OmSourceRow, + omega2012ml::OmPx, omega2012ml::OmPy, omega2012ml::OmPz, + omega2012ml::OmMassK0Short, omega2012ml::OmMassLambda, omega2012ml::OmMassAntiLambda, + omega2012ml::OmV0CosPA, omega2012ml::OmV0DaughDCA, + omega2012ml::OmDcaPosToPV, omega2012ml::OmDcaNegToPV, omega2012ml::OmDcaV0ToPV, omega2012ml::OmV0Radius, + omega2012ml::OmDecayVtxX, omega2012ml::OmDecayVtxY, omega2012ml::OmDecayVtxZ, + omega2012ml::OmArmAlpha, omega2012ml::OmArmQt, + omega2012ml::OmTpcPosPi10, omega2012ml::OmTpcPosKa10, omega2012ml::OmTpcPosPr10, + omega2012ml::OmTpcNegPi10, omega2012ml::OmTpcNegKa10, omega2012ml::OmTpcNegPr10, + omega2012ml::OmTofPosPi10, omega2012ml::OmTofPosKa10, omega2012ml::OmTofPosPr10, + omega2012ml::OmTofNegPi10, omega2012ml::OmTofNegKa10, omega2012ml::OmTofNegPr10, + omega2012ml::OmCrossedRowsPos, omega2012ml::OmCrossedRowsNeg, + omega2012ml::OmV0Indices); +DECLARE_SOA_TABLE(Omega2012MlTracks, "AOD", "OMMLTRACK", + o2::soa::Index<>, omega2012ml::Omega2012MlEventId, omega2012ml::OmSourceTrackId, + omega2012ml::OmPx, omega2012ml::OmPy, omega2012ml::OmPz, + omega2012ml::OmPidPi, omega2012ml::OmPidKa, omega2012ml::OmPidPr, + omega2012ml::OmSelectionFlags, omega2012ml::OmTrackFlags, + omega2012ml::OmCrossedRows, omega2012ml::OmItsClusterMap); +namespace omega2012ml +{ +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Omega2012MlCascade, omega2012MlCascade, int, Omega2012MlCascades, "OmmlCascades", ""); +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Omega2012MlV0, omega2012MlV0, int, Omega2012MlV0s, "OmmlV0s", ""); +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Omega2012MlPionTrack, omega2012MlPionTrack, int, Omega2012MlTracks, "OmmlTracks", "_Pion"); +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Omega2012MlKaonTrack, omega2012MlKaonTrack, int, Omega2012MlTracks, "OmmlTracks", "_Kaon"); +} // namespace omega2012ml + +// Mode A: Xi K0S +DECLARE_SOA_TABLE(Omega2012MlXiK0sCandidates, "AOD", "OMMLXIK0SCAND", + o2::soa::Index<>, omega2012ml::Omega2012MlEventId, + omega2012ml::Omega2012MlCascadeId, omega2012ml::Omega2012MlV0Id, + omega2012ml::OmMass, omega2012ml::OmPt, omega2012ml::OmY, omega2012ml::OmEta, omega2012ml::OmPhi, + omega2012ml::OmOpeningAngle, omega2012ml::OmCharge, omega2012ml::OmPassBits); +namespace omega2012ml +{ +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Omega2012MlXiK0sCandidate, omega2012MlXiK0sCandidate, int, Omega2012MlXiK0sCandidates, "OmmlXiK0sCands", ""); +} // namespace omega2012ml +DECLARE_SOA_TABLE(Omega2012MlXiK0sInputs, "AOD", "OMMLXIK0SINPUT", + o2::soa::Index<>, omega2012ml::Omega2012MlXiK0sCandidateId, + omega2012ml::OmXiK0sFeatures, omega2012ml::OmFeatureStatus); +DECLARE_SOA_TABLE(Omega2012MlXiK0sTruth, "AOD", "OMMLXIK0STRUTH", + o2::soa::Index<>, omega2012ml::Omega2012MlXiK0sCandidateId, + omega2012ml::OmTruthStatus, omega2012ml::OmMotherPdg, omega2012ml::OmMotherId, + omega2012ml::OmV0MotherPdg, omega2012ml::OmXiMotherPdg); + +// Mode B: Xi(1530)0 K- -> Xi- pi+ K- +DECLARE_SOA_TABLE(Omega2012MlXi1530KCandidates, "AOD", "OMMLXI1530KCAND", + o2::soa::Index<>, omega2012ml::Omega2012MlEventId, + omega2012ml::Omega2012MlCascadeId, omega2012ml::Omega2012MlPionTrackId, omega2012ml::Omega2012MlKaonTrackId, + omega2012ml::OmMass, omega2012ml::OmMassXiPi, omega2012ml::OmMassXiK, omega2012ml::OmMassPiK, + omega2012ml::OmPt, omega2012ml::OmY, omega2012ml::OmEta, omega2012ml::OmPhi, + omega2012ml::OmChargePattern, omega2012ml::OmPassBits); +namespace omega2012ml +{ +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Omega2012MlXi1530KCandidate, omega2012MlXi1530KCandidate, int, Omega2012MlXi1530KCandidates, "OmmlXi1530KCands", ""); +} // namespace omega2012ml +DECLARE_SOA_TABLE(Omega2012MlXi1530KInputs, "AOD", "OMMLXI1530KINP", + o2::soa::Index<>, omega2012ml::Omega2012MlXi1530KCandidateId, + omega2012ml::OmXi1530KFeatures, omega2012ml::OmFeatureStatus); +DECLARE_SOA_TABLE(Omega2012MlXi1530KTruth, "AOD", "OMMLXI1530KTRU", + o2::soa::Index<>, omega2012ml::Omega2012MlXi1530KCandidateId, + omega2012ml::OmTruthStatus, omega2012ml::OmMotherPdg, omega2012ml::OmMotherId, + omega2012ml::OmXi1530Id); + +// Generated Omega(2012) parents of selected reconstructed MC events (ResoMCParents_001) +DECLARE_SOA_TABLE(Omega2012MlGenAudit, "AOD", "OMMLGENAUDIT", + o2::soa::Index<>, omega2012ml::OmRecoCollisionId, omega2012ml::OmOriginalMcParticleId, + omega2012ml::OmPdg, omega2012ml::OmDaughterPdg1, omega2012ml::OmDaughterPdg2, + omega2012ml::OmGenChannel, omega2012ml::OmGenPt, omega2012ml::OmGenY); + +// MC truth of reconstructed MC: daughter labels of the written objects and the pruned source McParticles rows +// (Omega(2012) decay trees and ancestry of the daughter labels). Source rows are DF-local scalar values. +namespace omega2012ml +{ +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Omega2012MlTrack, omega2012MlTrack, int, Omega2012MlTracks, "OmmlTracks", ""); +DECLARE_SOA_COLUMN(OmMcCollisionRow, omMcCollisionRow, int64_t); //! row of the MC collision in the input McCollisions table +DECLARE_SOA_COLUMN(OmCascadeMcLabels, omCascadeMcLabels, int[3]); //! McParticles rows of the cascade daughters, order of omCascadeIndices; -1 if absent +DECLARE_SOA_COLUMN(OmV0McLabels, omV0McLabels, int[2]); //! McParticles rows of the V0 daughters, order of omV0Indices; -1 if absent +DECLARE_SOA_COLUMN(OmMcLabel, omMcLabel, int); //! McParticles row of the track; -1 if absent +DECLARE_SOA_COLUMN(OmMcRow, omMcRow, int64_t); //! row of the particle in the input McParticles table +DECLARE_SOA_COLUMN(OmMcSelection, omMcSelection, uint8_t); //! 1 Omega(2012) decay tree, 2 ancestry of a written daughter label +DECLARE_SOA_COLUMN(OmNMothers, omNMothers, uint8_t); //! number of mothers in the input row +DECLARE_SOA_COLUMN(OmMotherRows, omMotherRows, int[2]); //! first two McParticles mother rows; -1 if absent +DECLARE_SOA_COLUMN(OmDaughterRows, omDaughterRows, int[2]); //! McParticles daughter slice (first, last) as stored in the input row +DECLARE_SOA_COLUMN(OmE, omE, float); +DECLARE_SOA_COLUMN(OmWeight, omWeight, float); +} // namespace omega2012ml + +DECLARE_SOA_TABLE(Omega2012MlEventMcs, "AOD", "OMMLEVENTMC", + o2::soa::Index<>, omega2012ml::Omega2012MlEventId, omega2012ml::OmMcCollisionRow); +DECLARE_SOA_TABLE(Omega2012MlCascadeMcs, "AOD", "OMMLCASCMC", + o2::soa::Index<>, omega2012ml::Omega2012MlCascadeId, omega2012ml::OmCascadeMcLabels); +DECLARE_SOA_TABLE(Omega2012MlV0Mcs, "AOD", "OMMLV0MC", + o2::soa::Index<>, omega2012ml::Omega2012MlV0Id, omega2012ml::OmV0McLabels); +DECLARE_SOA_TABLE(Omega2012MlTrackMcs, "AOD", "OMMLTRACKMC", + o2::soa::Index<>, omega2012ml::Omega2012MlTrackId, omega2012ml::OmMcLabel); +DECLARE_SOA_TABLE(Omega2012MlMcParticles, "AOD", "OMMLMCPART", + o2::soa::Index<>, omega2012ml::OmMcRow, omega2012ml::OmMcSelection, omega2012ml::OmPdg, + omega2012ml::OmMcCollisionRow, omega2012ml::OmNMothers, omega2012ml::OmMotherRows, + omega2012ml::OmDaughterRows, omega2012ml::OmPx, omega2012ml::OmPy, omega2012ml::OmPz, + omega2012ml::OmE, omega2012ml::OmWeight); +} // namespace o2::aod + +#endif // PWGLF_DATAMODEL_LFOMEGA2012MLTABLES_H_ diff --git a/PWGLF/DataModel/LFSigmaTables.h b/PWGLF/DataModel/LFSigmaTables.h index de5d0f46acd..4428b3b2dff 100644 --- a/PWGLF/DataModel/LFSigmaTables.h +++ b/PWGLF/DataModel/LFSigmaTables.h @@ -1452,7 +1452,7 @@ DECLARE_SOA_DYNAMIC_COLUMN(Xi0Mass, xi0Mass, std::array pVecPi0{photon1Px + photon2Px, photon1Py + photon2Py, photon1Pz + photon2Pz}; std::array pVecLambda{lambdaPx, lambdaPy, lambdaPz}; auto arrMom = std::array{pVecPi0, pVecLambda}; - return RecoDecay::m(arrMom, std::array{o2::constants::physics::MassPi0, o2::constants::physics::MassXi0}); + return RecoDecay::m(arrMom, std::array{o2::constants::physics::MassPi0, o2::constants::physics::MassLambda0}); }); DECLARE_SOA_DYNAMIC_COLUMN(PxPi0, pxPi0, //! Pi0 px @@ -1607,11 +1607,12 @@ DECLARE_SOA_TABLE(Xi0Cores, "AOD", "XI0CORES", Xi0Core::Pz, Xi0Core::Pt, Xi0Core::P, + Xi0Core::Xi0Mass, Xi0Core::Rapidity, Xi0Core::Phi, Xi0Core::Eta, Xi0Core::Radius, - Xi0Core::CascCosPA, + Xi0Core::CascCosPA, // Dynamic columns for pi0 Xi0Core::PxPi0, @@ -1620,7 +1621,7 @@ DECLARE_SOA_TABLE(Xi0Cores, "AOD", "XI0CORES", Xi0Core::Pi0Mass, Xi0Core::RadiusPi0, Xi0Core::Pi0CosPA, - Xi0Core::DCAPi0ToPV, + Xi0Core::DCAPi0ToPV, // Dynamic columns for Lambda Xi0Core::RadiusLambda, @@ -1884,11 +1885,11 @@ DECLARE_SOA_TABLE(Xi0MCCores, "AOD", "XI0MCCORES", Xi0MCCore::MCRadius, Xi0MCCore::MCRadiusPi0, - Xi0MCCore::MCPx, - Xi0MCCore::MCPy, - Xi0MCCore::MCPz, - Xi0MCCore::MCPt, - Xi0MCCore::MCP, + Xi0MCCore::Pi0MCPx, + Xi0MCCore::Pi0MCPy, + Xi0MCCore::Pi0MCPz, + Xi0MCCore::Pi0MCPt, + Xi0MCCore::Pi0MCP, Xi0MCCore::Pi0MCMass, Xi0MCCore::Pi0RapidityMC, Xi0MCCore::Pi0MCPhi, diff --git a/PWGLF/DataModel/LFSlimHeLambda.h b/PWGLF/DataModel/LFSlimHeLambda.h index 6b3ae954cd9..a0737effbbb 100644 --- a/PWGLF/DataModel/LFSlimHeLambda.h +++ b/PWGLF/DataModel/LFSlimHeLambda.h @@ -54,12 +54,18 @@ DECLARE_SOA_COLUMN(DCAPVProton, dcaPVProton, float); DECLARE_SOA_COLUMN(DCAPVPion, dcaPVPion, float); DECLARE_SOA_COLUMN(V0Radius, v0Radius, float); DECLARE_SOA_COLUMN(Sign, sign, int8_t); +DECLARE_SOA_COLUMN(Ct, ct, float); +DECLARE_SOA_COLUMN(DCAPVPos, dcaPVPos, float); +DECLARE_SOA_COLUMN(DCAPVNeg, dcaPVNeg, float); +DECLARE_SOA_COLUMN(NsigmaTPCPosPion, nSigmaTPCPosPion, float); +DECLARE_SOA_COLUMN(NsigmaTPCNegPion, nSigmaTPCNegPion, float); } // namespace lfv0he3 DECLARE_SOA_TABLE_VERSIONED(LFHe3_000, "AOD", "LFHE3V0", 0, lfv0he3::LFEventId, lfv0he3::Pt, lfv0he3::Eta, lfv0he3::Phi, lfv0he3::DCAxy, lfv0he3::DCAz, lfv0he3::TPCnCls, lfv0he3::ITSClusterSizes, lfv0he3::NsigmaTPC, lfv0he3::Sign); DECLARE_SOA_TABLE_VERSIONED(LFLambda_000, "AOD", "LFLAMBDA", 0, lfv0he3::LFEventId, lfv0he3::Pt, lfv0he3::Eta, lfv0he3::Phi, lfv0he3::Mass, lfv0he3::CosPA, lfv0he3::DCAdaughters, lfv0he3::DCAPVProton, lfv0he3::DCAPVPion, lfv0he3::V0Radius, lfv0he3::Sign); DECLARE_SOA_TABLE_VERSIONED(LFHe3_001, "AOD", "LFHE3V0", 1, lfv0he3::LFEventId, lfv0he3::Pt, lfv0he3::Eta, lfv0he3::Phi, lfv0he3::DCAxy, lfv0he3::DCAz, lfv0he3::TPCnCls, lfv0he3::TPCnClsPID, lfv0he3::ITSClusterSizes, lfv0he3::NsigmaTPC, lfv0he3::Sign); DECLARE_SOA_TABLE_VERSIONED(LFLambda_001, "AOD", "LFLAMBDA", 1, lfv0he3::LFEventId, lfv0he3::Pt, lfv0he3::Eta, lfv0he3::Phi, lfv0he3::Mass, lfv0he3::CosPA, lfv0he3::DCAdaughters, lfv0he3::DCAPVProton, lfv0he3::DCAPVPion, lfv0he3::V0Radius, lfv0he3::NsigmaTPCProton, lfv0he3::NsigmaTPCPion, lfv0he3::Sign); +DECLARE_SOA_TABLE(LFK0s, "AOD", "LFK0S", lfv0he3::LFEventId, lfv0he3::Pt, lfv0he3::Eta, lfv0he3::Phi, lfv0he3::Mass, lfv0he3::Ct, lfv0he3::CosPA, lfv0he3::DCAdaughters, lfv0he3::DCAPVPos, lfv0he3::DCAPVNeg, lfv0he3::V0Radius, lfv0he3::NsigmaTPCPosPion, lfv0he3::NsigmaTPCNegPion); } // namespace o2::aod struct he3Candidate { @@ -87,4 +93,17 @@ struct lambdaCandidate { int8_t sign = 0; // Charge sign of the Lambda candidate }; +struct k0sCandidate { + ROOT::Math::LorentzVector> momentum; + float mass = -1.f; + float ct = -1.f; // Proper decay length in cm + float cosPA = -2.f; + float dcaV0Daughters = -999.f; + float dcaPosToPV = -999.f; + float dcaNegToPV = -999.f; + float v0Radius = -1.f; + float posPionNSigmaTPC = -999.f; + float negPionNSigmaTPC = -999.f; +}; + #endif // PWGLF_DATAMODEL_LFSLIMHELAMBDA_H_ diff --git a/PWGLF/DataModel/LFSpincorrelationTables.h b/PWGLF/DataModel/LFSpincorrelationTables.h index d4fdffd2442..fa7e2061f3e 100644 --- a/PWGLF/DataModel/LFSpincorrelationTables.h +++ b/PWGLF/DataModel/LFSpincorrelationTables.h @@ -53,6 +53,12 @@ DECLARE_SOA_COLUMN(ProtonPhi, protonPhi, float); //! Proton Ph DECLARE_SOA_COLUMN(ProtonIndex, protonIndex, int); //! Proton index DECLARE_SOA_COLUMN(PionIndex, pionIndex, int); //! Pion index DECLARE_SOA_COLUMN(DcaV0ToPV, dcaV0ToPV, float); //! DCA of V0 to primary vertex +DECLARE_SOA_COLUMN(TpcPosNClsCrossedRows, tpcPosNClsCrossedRows, float); +DECLARE_SOA_COLUMN(TpcNegNClsCrossedRows, tpcNegNClsCrossedRows, float); +DECLARE_SOA_COLUMN(Ctau, ctau, float); +DECLARE_SOA_COLUMN(TpcNsigmaPos, tpcNsigmaPos, float); +DECLARE_SOA_COLUMN(TpcNsigmaNeg, tpcNsigmaNeg, float); +DECLARE_SOA_COLUMN(MK0Short, mK0Short, float); } // namespace lambdapair DECLARE_SOA_TABLE(LambdaPairs, "AOD", "LAMBDAPAIR", @@ -74,7 +80,13 @@ DECLARE_SOA_TABLE(LambdaPairs, "AOD", "LAMBDAPAIR", lambdapair::ProtonPhi, lambdapair::ProtonIndex, lambdapair::PionIndex, - lambdapair::DcaV0ToPV); + lambdapair::DcaV0ToPV, + lambdapair::TpcPosNClsCrossedRows, + lambdapair::TpcNegNClsCrossedRows, + lambdapair::Ctau, + lambdapair::TpcNsigmaPos, + lambdapair::TpcNsigmaNeg, + lambdapair::MK0Short); using LambdaPair = LambdaPairs::iterator; @@ -109,6 +121,8 @@ DECLARE_SOA_COLUMN(ProtonPhimc, protonPhimc, float); //! Proto DECLARE_SOA_COLUMN(ProtonIndexmc, protonIndexmc, int); //! Proton index in montecarlo DECLARE_SOA_COLUMN(PionIndexmc, pionIndexmc, int); //! Pion index in montecarlo DECLARE_SOA_COLUMN(DcaV0ToPVmc, dcaV0ToPVmc, float); //! DCA of V0 to primary vertex + +//! TPC n sigma } // namespace lambdapairmc DECLARE_SOA_TABLE(LambdaPairmcs, "AOD", "LAMBDAPAIRMC", o2::soa::Index<>, diff --git a/PWGLF/DataModel/LFStrangenessMLTables.h b/PWGLF/DataModel/LFStrangenessMLTables.h index 7784a521dd7..983648f791d 100644 --- a/PWGLF/DataModel/LFStrangenessMLTables.h +++ b/PWGLF/DataModel/LFStrangenessMLTables.h @@ -180,6 +180,8 @@ DECLARE_SOA_COLUMN(DCACascDaughters, dcaCascDaughters, float); DECLARE_SOA_COLUMN(DCAv0topv, dcav0topv, float); DECLARE_SOA_COLUMN(V0PA, v0PA, float); DECLARE_SOA_COLUMN(CascPA, cascPA, float); +DECLARE_SOA_COLUMN(BachBaryonCosPA, bachBaryonCosPA, float); +DECLARE_SOA_COLUMN(BachBaryonDCAxyToPV, bachBaryonDCAxyToPV, float); DECLARE_SOA_COLUMN(IsXiMinus, isXiMinus, bool); DECLARE_SOA_COLUMN(IsXiPlus, isXiPlus, bool); @@ -231,6 +233,8 @@ DECLARE_SOA_TABLE(CascMLCandidates, "AOD", "CAMLCANDIDATES", cascmlcandidates::DCAv0topv, cascmlcandidates::V0PA, cascmlcandidates::CascPA, + cascmlcandidates::BachBaryonCosPA, + cascmlcandidates::BachBaryonDCAxyToPV, cascmlcandidates::IsXiMinus, cascmlcandidates::IsXiPlus, cascmlcandidates::IsOmegaMinus, diff --git a/PWGLF/DataModel/LFStrangenessTables.h b/PWGLF/DataModel/LFStrangenessTables.h index 56322a0e47c..e057f10550a 100644 --- a/PWGLF/DataModel/LFStrangenessTables.h +++ b/PWGLF/DataModel/LFStrangenessTables.h @@ -585,7 +585,7 @@ DECLARE_SOA_TABLE_VERSIONED(DauTrackExtras_003, "AOD", "DAUTRACKEXTRA", 3, //! d track::v001::ITSClsSizeInLayer, track::v001::ITSClusterMap, track::v001::ITSNCls, - track::v001::IsITSAfterburner, + track::v001::IsITSAfterburner, /*compatibility*/ dautrack::HasITSTracker, /*compatibility*/ dautrack::HasITSAfterburner, diff --git a/PWGLF/DataModel/cascqaanalysis.h b/PWGLF/DataModel/cascqaanalysis.h index eca673c683d..61783278069 100644 --- a/PWGLF/DataModel/cascqaanalysis.h +++ b/PWGLF/DataModel/cascqaanalysis.h @@ -118,6 +118,7 @@ DECLARE_SOA_COLUMN(Pt, pt, float); DECLARE_SOA_COLUMN(Eta, eta, float); DECLARE_SOA_COLUMN(Phi, phi, float); DECLARE_SOA_COLUMN(MassLambda, masslambda, float); +DECLARE_SOA_COLUMN(MassK0Short, massk0short, float); DECLARE_SOA_COLUMN(CtauLambda, ctaulambda, float); DECLARE_SOA_COLUMN(MassXi, massxi, float); DECLARE_SOA_COLUMN(MassOmega, massomega, float); @@ -183,7 +184,7 @@ DECLARE_SOA_TABLE(CascAnalysisFull, "AOD", "CascAnalysisFull", o2::soa::Index<>, cascadesflow::CosThetaStarLambdaFromOmega, cascadesflow::CosThetaStarLambdaFromXi, cascadesflow::CosThetaStarProton, mycascades::McPdgCode); DECLARE_SOA_TABLE(LambdaAnalysis, "AOD", "LambdaAnalysis", o2::soa::Index<>, - cascadesflow::CentFT0C, cascadesflow::HasEventPlane, cascadesflow::HasSpectatorPlane, cascadesflow::Sign, cascadesflow::Pt, cascadesflow::Phi, cascadesflow::Eta, cascadesflow::MassLambda, cascadesflow::CtauLambda, cascadesflow::V0Radius, cascadesflow::DcaPosToPV, cascadesflow::DcaNegToPV, cascadesflow::V0CosPA, cascadesflow::DcaV0Daughters, cascadesflow::V2CEP, cascadesflow::PsiT0C, cascadesflow::Pzs2Lambda, cascadesflow::Cos2ThetaLambda, cascadesflow::CosThetaLambda); + cascadesflow::CentFT0C, cascadesflow::CentFT0M, cascadesflow::HasEventPlane, cascadesflow::HasSpectatorPlane, cascadesflow::Sign, cascadesflow::Pt, cascadesflow::Phi, cascadesflow::Eta, cascadesflow::MassLambda, cascadesflow::MassK0Short, cascadesflow::CtauLambda, cascadesflow::V0Radius, cascadesflow::DcaPosToPV, cascadesflow::DcaNegToPV, cascadesflow::V0CosPA, cascadesflow::DcaV0Daughters, cascadesflow::V2CEP, cascadesflow::PsiT0C, cascadesflow::Pzs2Lambda, cascadesflow::Cos2ThetaLambda, cascadesflow::CosThetaLambda); namespace myMCcascades { diff --git a/PWGLF/TableProducer/CMakeLists.txt b/PWGLF/TableProducer/CMakeLists.txt index ec21b553563..745d984c81d 100644 --- a/PWGLF/TableProducer/CMakeLists.txt +++ b/PWGLF/TableProducer/CMakeLists.txt @@ -12,6 +12,7 @@ add_subdirectory(QC) add_subdirectory(Common) +add_subdirectory(GlobalEventProperties) add_subdirectory(Nuspex) add_subdirectory(Strangeness) add_subdirectory(Resonances) diff --git a/PWGLF/TableProducer/Common/lfTPCPID.cxx b/PWGLF/TableProducer/Common/lfTPCPID.cxx index 90c124bbbe8..d84c977c101 100644 --- a/PWGLF/TableProducer/Common/lfTPCPID.cxx +++ b/PWGLF/TableProducer/Common/lfTPCPID.cxx @@ -43,6 +43,7 @@ #include #include +#include #include #include #include @@ -62,34 +63,34 @@ static const std::vector parameterNames{"Use default tiny", "Set parameters", "bb1", "bb2", "bb3", "bb4", "bb5", "MIP value", "Charge exponent", "Resolution"}; -static constexpr float defaultParameters[nSpecies][nParameters]{{2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, - {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, - {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, - {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, - {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, - {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, - {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, - {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, - {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}}; +static constexpr std::array, nSpecies> defaultParameters{{{2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}}}; static constexpr int nOptions = 4; static const std::vector optionNames{"Bethe Bloch path", // If empty using the default/json values. Can be a CCDB path if the string starts with ccdb:// "Post calibration path", // If empty using the default/json values. Can be a CCDB path if the string starts with ccdb:// "Simple Bethe Bloch", // (true/false) "RequirePostCalib"}; // (true/false) -const std::string defaultOptions[nSpecies][nOptions]{{"", "", "false", "false"}, - {"", "", "false", "false"}, - {"", "", "false", "false"}, - {"", "", "false", "false"}, - {"", "", "false", "false"}, - {"", "", "false", "false"}, - {"", "", "false", "false"}, - {"", "", "false", "false"}, - {"", "", "false", "false"}}; +const std::array, nSpecies> defaultOptions{{{"", "", "false", "false"}, + {"", "", "false", "false"}, + {"", "", "false", "false"}, + {"", "", "false", "false"}, + {"", "", "false", "false"}, + {"", "", "false", "false"}, + {"", "", "false", "false"}, + {"", "", "false", "false"}, + {"", "", "false", "false"}}}; // Structure to hold the parameters struct bbParams { const std::string name; - bbParams(const std::string& n) : name(n) {} + explicit bbParams(const std::string& n) : name(n) {} // Parameters for the Bethe-Bloch parametrization float bb1 = 0.03209809958934784f; // Aleph Bethe Bloch parameter 1 float bb2 = 19.9768009185791f; // Aleph Bethe Bloch parameter 2 @@ -472,15 +473,15 @@ struct lfTpcPid { // Parameters setting from json Configurable> bbParameters{"bbParameters", - {defaultParameters[0], nSpecies, nParameters, particleNames, parameterNames}, + {defaultParameters.front().data(), nSpecies, nParameters, particleNames, parameterNames}, "Bethe Bloch parameters"}; // Parameter setting from input file (including the ccdb) Configurable> fileParamBbPositive{"fileParamBbPositive", - {defaultOptions[0], nSpecies, nOptions, particleNames, optionNames}, + {defaultOptions.front().data(), nSpecies, nOptions, particleNames, optionNames}, "Input for the parametrization for positive particles. If empty using the default/json values. Can be a CCDB path if the string starts with ccdb://"}; Configurable> fileParamBbNegative{"fileParamBbNegative", - {defaultOptions[0], nSpecies, nOptions, particleNames, optionNames}, + {defaultOptions.front().data(), nSpecies, nOptions, particleNames, optionNames}, "Input for the parametrization for negative particles. If empty using the default/json values. Can be a CCDB path if the string starts with ccdb://"}; Configurable url{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; diff --git a/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx b/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx index 6c686f336ec..7b87fecc482 100644 --- a/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx +++ b/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx @@ -78,10 +78,14 @@ struct zdc2stagecalibration { Configurable deriveSpatialCalib{"deriveSpatialCalib", false, "store spatial regression moments after gain correction"}; Configurable deriveQRecentering{"deriveQRecentering", false, "store Q-vector recentering regression moments after gain and spatial correction"}; Configurable useQRecentering{"useQRecentering", false, "apply Q-vector recentering calibration"}; - Configurable qRecenteringMethod{"qRecenteringMethod", 0, "Q recentering method: 0=current regression, 1=multidimensional cubic regression"}; + Configurable qRecenteringMethod{"qRecenteringMethod", 0, "Q recentering method: 0=current regression, 1=multidimensional cubic regression, 4=structured71 regression"}; Configurable confQRecenteringPath{"confQRecenteringPath", "", "CCDB path to the current run-wise Q-vector recentering coefficients"}; Configurable confQRecenteringMultidimPath{"confQRecenteringMultidimPath", "", "CCDB path to the multidimensional cubic Q-vector recentering coefficients"}; - + Configurable confQRecenteringStructured71Path{"confQRecenteringStructured71Path", "", "CCDB path to the 71-feature structured Q-vector recentering coefficients"}; + Configurable useQTwist{"useQTwist", false, "apply same-side Qx-Qy correlation de-twist after recentering (no width equalization)"}; + Configurable confQTwistPath{"confQTwistPath", "", "CCDB path to the run-wise Q-twist map"}; + Configurable useQShift{"useQShift", false, "apply ZDC azimuthal shift correction after twist"}; + Configurable confQShiftPath{"confQShiftPath", "", "CCDB path to the run-wise Q-shift Fourier map"}; Configurable cfgSpatialCalibBins{ "cfgSpatialCalibBins", 30, "number of bins per cross coordinate for spatial calibration"}; @@ -100,6 +104,8 @@ struct zdc2stagecalibration { TH2D* gainprofile = nullptr; TH3D* spatialprofile = nullptr; TH3D* qRecenteringProfile = nullptr; + TH2D* qTwistProfile = nullptr; + TH2D* qShiftProfile = nullptr; static constexpr int kQRecenteringNFeatures = 21; static constexpr int kQRecenteringNMatrixMoments = kQRecenteringNFeatures * (kQRecenteringNFeatures + 1) / 2; @@ -109,13 +115,33 @@ struct zdc2stagecalibration { static constexpr int kQRecenteringMultidimNMatrixMoments = kQRecenteringMultidimNFeatures * (kQRecenteringMultidimNFeatures + 1) / 2; static constexpr int kQRecenteringMultidimNMoments = kQRecenteringMultidimNMatrixMoments + kQRecenteringNComponents * kQRecenteringMultidimNFeatures; + // Per conditioning bin store 35 sums of the cubic features followed by + // 4 sums of QxA,QyA,QxC,QyC. Feature 0 is unity, so its sum is N. + static constexpr int kQRecenteringFlatnessNMoments = kQRecenteringMultidimNFeatures + kQRecenteringNComponents; + + // Structured regression diagnostic basis. The existing 35 cubic features are + // kept unchanged and augmented with truncated-cubic 1D basis functions for + // time, Vx, Vy and Vz. The additional basis is intentionally overcomplete so + // that the final subset and regularisation can be chosen offline without + // another derivation pass. + static constexpr int kQRecenteringStructuredNKnots = 9; + static constexpr int kQRecenteringStructuredNFeatures = kQRecenteringMultidimNFeatures + 4 * kQRecenteringStructuredNKnots; + static constexpr int kQRecenteringStructuredNMatrixMoments = kQRecenteringStructuredNFeatures * (kQRecenteringStructuredNFeatures + 1) / 2; + static constexpr int kQRecenteringStructuredNMoments = kQRecenteringStructuredNMatrixMoments + kQRecenteringNComponents * kQRecenteringStructuredNFeatures + kQRecenteringNComponents; + static constexpr int kQRecenteringStructuredFlatnessNMoments = kQRecenteringStructuredNFeatures + 2 * kQRecenteringNComponents; + void init(o2::framework::InitContext&) { rctChecker.init(rctCut.cfgEvtRCTFlagCheckerLabel, rctCut.cfgEvtRCTFlagCheckerZDCCheck, rctCut.cfgEvtRCTFlagCheckerLimitAcceptAsBad); - if (qRecenteringMethod.value < 0 || qRecenteringMethod.value > 1) { - LOGF(fatal, "Unsupported qRecenteringMethod=%d. Use 0 for the current regression or 1 for the multidimensional cubic regression.", qRecenteringMethod.value); + if (qRecenteringMethod.value != 0 && qRecenteringMethod.value != 1 && qRecenteringMethod.value != 4) { + LOGF(fatal, "Unsupported qRecenteringMethod=%d. Use 0 for the current regression, 1 for the multidimensional cubic regression or 4 for the structured71 regression.", qRecenteringMethod.value); + } + if (useQTwist.value && (calibrationStage.value != 2 || !useQRecentering.value || qRecenteringMethod.value != 4 || confQTwistPath.value.empty())) { + LOGF(fatal, "Twist correction requires stage=2, Structured71 recentering and confQTwistPath"); + } + if (useQShift.value && (!useQTwist.value || confQShiftPath.value.empty())) { + LOGF(fatal, "Shift correction requires twist correction and confQShiftPath"); } - const int nTimeBins = static_cast(std::ceil(cfgMaxRunHours.value * 60.f / cfgTimeSliceMinutes.value)); AxisSpec timeAxis = {nTimeBins, 0.0, cfgMaxRunHours.value, "time from SOR (h)"}; AxisSpec towerAxis = {4, 0.0, 4.0, "tower"}; @@ -124,15 +150,23 @@ struct zdc2stagecalibration { AxisSpec spatialMomentAxis = {6, 0.0, 6.0, "normalized pair regression moment"}; AxisSpec spatialCalibAxis = {cfgSpatialCalibBins.value, -1.0, 1.0, "cross asymmetry"}; AxisSpec phiAxis = {72, -3.14159265358979323846, 3.14159265358979323846, "#phi"}; + AxisSpec phiDerivationAxis = {720, -3.14159265358979323846, 3.14159265358979323846, "#phi"}; AxisSpec crossCoordAxis = {2, 0.0, 2.0, "coordinate"}; AxisSpec ratioAxis = {240, 0.0, 2.4, "#Sigma tower/common"}; AxisSpec qRecenteringMomentAxis = {kQRecenteringNMoments, 0.0, static_cast(kQRecenteringNMoments), "regression moment"}; AxisSpec qRecenteringMultidimMomentAxis = {kQRecenteringMultidimNMoments, 0.0, static_cast(kQRecenteringMultidimNMoments), "multidimensional regression moment"}; + AxisSpec qRecenteringFlatnessMomentAxis = {kQRecenteringFlatnessNMoments, 0.0, static_cast(kQRecenteringFlatnessNMoments), "flatness sufficient-statistic index"}; + AxisSpec qRecenteringStructuredMomentAxis = {kQRecenteringStructuredNMoments, 0.0, static_cast(kQRecenteringStructuredNMoments), "structured regression moment"}; + AxisSpec qRecenteringStructuredFlatnessMomentAxis = {kQRecenteringStructuredFlatnessNMoments, 0.0, static_cast(kQRecenteringStructuredFlatnessNMoments), "structured flatness sufficient-statistic index"}; AxisSpec centralityAxis = {80, 0.0, 80.0, "centrality (%)"}; - AxisSpec vertexXYAxis = {100, -0.5, 0.5, "vertex x/y (cm)"}; + AxisSpec resolutionCentralityAxis = {50, 0.0, 50.0, "centrality (%)"}; + AxisSpec resolutionVzAxis = {20, -10.0, 10.0, "v_{z} (cm)"}; + AxisSpec vertexXYAxis = {100, -0.1, 0.1, "vertex x/y (cm)"}; AxisSpec vertexZAxis = {100, -10.0, 10.0, "vertex z (cm)"}; AxisSpec qComponentAxis = {4, 0.0, 4.0, "Q component"}; + AxisSpec qTwistMomentAxis = {10, 0.0, 10.0, "Q moments (A: 0-4, C: 5-9)"}; AxisSpec qCorrelationComponentAxis = {4, 0.0, 4.0, "A-C correlation component"}; + AxisSpec phiHarmonicAxis = {8, 0.0, 8.0, "cos1,sin1,...,cos4,sin4"}; AxisSpec qValueAxis = {200, -4.0, 4.0, "Q"}; histos.add("hEvtSelInfo", "hEvtSelInfo", kTH1F, {{10, 0.0, 10.0}}); @@ -198,6 +232,30 @@ struct zdc2stagecalibration { histos.add("QRecenteringCalibration/hRegressionMoments", "Q recentering regression moments;centrality (%);moment index", kTH2D, {centralityAxis, qRecenteringMomentAxis}); histos.add("QRecenteringCalibration/hRegressionMomentsMultidim", "Multidimensional cubic Q recentering regression moments;centrality (%);moment index", kTH2D, {centralityAxis, qRecenteringMultidimMomentAxis}); + // Sufficient statistics used only when deriving the multidimensional map with + // an additional flatness penalty. They do not alter the 35-feature model or + // the application-side CCDB format. For each (centrality,conditioning) bin: + // indices 0..34 = sum(X_i), indices 35..38 = sum(QxA,QyA,QxC,QyC). + if (deriveQRecentering.value && qRecenteringMethod.value == 1) { + histos.add("QRecenteringCalibration/hFlatnessMomentsTime", "Flatness sufficient statistics vs time;centrality (%);time from SOR (h);statistic index", kTH3D, {centralityAxis, timeAxis, qRecenteringFlatnessMomentAxis}); + histos.add("QRecenteringCalibration/hFlatnessMomentsVx", "Flatness sufficient statistics vs v_{x};centrality (%);v_{x} (cm);statistic index", kTH3D, {centralityAxis, vertexXYAxis, qRecenteringFlatnessMomentAxis}); + histos.add("QRecenteringCalibration/hFlatnessMomentsVy", "Flatness sufficient statistics vs v_{y};centrality (%);v_{y} (cm);statistic index", kTH3D, {centralityAxis, vertexXYAxis, qRecenteringFlatnessMomentAxis}); + histos.add("QRecenteringCalibration/hFlatnessMomentsVz", "Flatness sufficient statistics vs v_{z};centrality (%);v_{z} (cm);statistic index", kTH3D, {centralityAxis, vertexZAxis, qRecenteringFlatnessMomentAxis}); + + // Additional train/validation sufficient statistics for the structured + // regression test. These do not modify the current 35-feature correction. + histos.add("QRecenteringCalibration/hRegressionMomentsStructuredTrain", "Structured Q recentering moments (train);centrality (%);moment index", kTH2D, {centralityAxis, qRecenteringStructuredMomentAxis}); + histos.add("QRecenteringCalibration/hRegressionMomentsStructuredValidation", "Structured Q recentering moments (validation);centrality (%);moment index", kTH2D, {centralityAxis, qRecenteringStructuredMomentAxis}); + histos.add("QRecenteringCalibration/hStructuredFlatnessMomentsTimeTrain", "Structured flatness statistics vs time (train);centrality (%);time from SOR (h);statistic index", kTH3D, {centralityAxis, timeAxis, qRecenteringStructuredFlatnessMomentAxis}); + histos.add("QRecenteringCalibration/hStructuredFlatnessMomentsVxTrain", "Structured flatness statistics vs v_{x} (train);centrality (%);v_{x} (cm);statistic index", kTH3D, {centralityAxis, vertexXYAxis, qRecenteringStructuredFlatnessMomentAxis}); + histos.add("QRecenteringCalibration/hStructuredFlatnessMomentsVyTrain", "Structured flatness statistics vs v_{y} (train);centrality (%);v_{y} (cm);statistic index", kTH3D, {centralityAxis, vertexXYAxis, qRecenteringStructuredFlatnessMomentAxis}); + histos.add("QRecenteringCalibration/hStructuredFlatnessMomentsVzTrain", "Structured flatness statistics vs v_{z} (train);centrality (%);v_{z} (cm);statistic index", kTH3D, {centralityAxis, vertexZAxis, qRecenteringStructuredFlatnessMomentAxis}); + histos.add("QRecenteringCalibration/hStructuredFlatnessMomentsTimeValidation", "Structured flatness statistics vs time (validation);centrality (%);time from SOR (h);statistic index", kTH3D, {centralityAxis, timeAxis, qRecenteringStructuredFlatnessMomentAxis}); + histos.add("QRecenteringCalibration/hStructuredFlatnessMomentsVxValidation", "Structured flatness statistics vs v_{x} (validation);centrality (%);v_{x} (cm);statistic index", kTH3D, {centralityAxis, vertexXYAxis, qRecenteringStructuredFlatnessMomentAxis}); + histos.add("QRecenteringCalibration/hStructuredFlatnessMomentsVyValidation", "Structured flatness statistics vs v_{y} (validation);centrality (%);v_{y} (cm);statistic index", kTH3D, {centralityAxis, vertexXYAxis, qRecenteringStructuredFlatnessMomentAxis}); + histos.add("QRecenteringCalibration/hStructuredFlatnessMomentsVzValidation", "Structured flatness statistics vs v_{z} (validation);centrality (%);v_{z} (cm);statistic index", kTH3D, {centralityAxis, vertexZAxis, qRecenteringStructuredFlatnessMomentAxis}); + } + histos.add("QRecenteringQA/pQBeforeVsCentrality", "Q before recentering vs centrality;centrality (%);Q component;", kTProfile2D, {centralityAxis, qComponentAxis}); histos.add("QRecenteringQA/pQBeforeVsTime", "Q before recentering vs time;time from SOR (h);Q component;", kTProfile2D, {timeAxis, qComponentAxis}); histos.add("QRecenteringQA/pQBeforeVsVx", "Q before recentering vs v_{x};v_{x} (cm);Q component;", kTProfile2D, {vertexXYAxis, qComponentAxis}); @@ -208,6 +266,22 @@ struct zdc2stagecalibration { histos.add("QRecenteringQA/pQAfterVsVx", "Q after recentering vs v_{x};v_{x} (cm);Q component;", kTProfile2D, {vertexXYAxis, qComponentAxis}); histos.add("QRecenteringQA/pQAfterVsVy", "Q after recentering vs v_{y};v_{y} (cm);Q component;", kTProfile2D, {vertexXYAxis, qComponentAxis}); histos.add("QRecenteringQA/pQAfterVsVz", "Q after recentering vs v_{z};v_{z} (cm);Q component;", kTProfile2D, {vertexZAxis, qComponentAxis}); + histos.add("ResolutionQA/pCosPsiAPsiCVsCentrality", "ZDC A-C spectator-plane correlation;centrality (%);<#cos(#Psi_{A}-#Psi_{C})>", kTProfile, {resolutionCentralityAxis}); + histos.add("ResolutionQA/pCosPsiAPsiCVsCentralityVz", "ZDC A-C correlation;centrality (%);v_{z} (cm); <#cos(#Psi_{A}-#Psi_{C})>", kTProfile2D, {resolutionCentralityAxis, resolutionVzAxis}); + // Fine-bin recentered azimuths allow both twist and shift maps to be derived + // offline from a single recentering-only pass. No Q-magnitude normalization. + histos.add("QTwistQA/hPhiBeforeTwistHighResVsCentralityZNA", "Recentered ZNA #phi for twist/shift derivation;centrality (%);#phi", kTH2F, {centralityAxis, phiDerivationAxis}); + histos.add("QTwistQA/hPhiBeforeTwistHighResVsCentralityZNC", "Recentered ZNC #phi for twist/shift derivation;centrality (%);#phi", kTH2F, {centralityAxis, phiDerivationAxis}); + histos.add("QTwistQA/pMomentsBeforeVsCentrality", "Q moments before twist;centrality (%);moment;", kTProfile2D, {centralityAxis, qTwistMomentAxis}); + histos.add("QTwistQA/pMomentsAfterVsCentrality", "Q moments after twist;centrality (%);moment;", kTProfile2D, {centralityAxis, qTwistMomentAxis}); + histos.add("PhiQA/hPhiAfterTwistVsCentralityZNA", "ZNA #phi after twist;centrality (%);#phi", kTH2F, {centralityAxis, phiAxis}); + histos.add("PhiQA/hPhiAfterTwistVsCentralityZNC", "ZNC #phi after twist;centrality (%);#phi", kTH2F, {centralityAxis, phiAxis}); + histos.add("QShiftQA/hPhiAfterShiftVsCentralityZNA", "ZNA #phi after shift;centrality (%);#phi", kTH2F, {centralityAxis, phiAxis}); + histos.add("QShiftQA/hPhiAfterShiftVsCentralityZNC", "ZNC #phi after shift;centrality (%);#phi", kTH2F, {centralityAxis, phiAxis}); + histos.add("QShiftQA/pPhiHarmonicsBeforeVsCentralityZNA", "ZNA harmonics before shift: cos,sin n=1..4;centrality (%);harmonic index;", kTProfile2D, {centralityAxis, phiHarmonicAxis}); + histos.add("QShiftQA/pPhiHarmonicsBeforeVsCentralityZNC", "ZNC harmonics before shift: cos,sin n=1..4;centrality (%);harmonic index;", kTProfile2D, {centralityAxis, phiHarmonicAxis}); + histos.add("QShiftQA/pPhiHarmonicsAfterVsCentralityZNA", "ZNA harmonics after shift: cos,sin n=1..4;centrality (%);harmonic index;", kTProfile2D, {centralityAxis, phiHarmonicAxis}); + histos.add("QShiftQA/pPhiHarmonicsAfterVsCentralityZNC", "ZNC harmonics after shift: cos,sin n=1..4;centrality (%);harmonic index;", kTProfile2D, {centralityAxis, phiHarmonicAxis}); histos.add("QRecentering2DQA/pQxAAfterVsCentralityTime", "QxA after recentering;centrality (%);time from SOR (h);", kTProfile2D, {centralityAxis, timeAxis}); histos.add("QRecentering2DQA/pQyAAfterVsCentralityTime", "QyA after recentering;centrality (%);time from SOR (h);", kTProfile2D, {centralityAxis, timeAxis}); histos.add("QRecentering2DQA/pQxCAfterVsCentralityTime", "QxC after recentering;centrality (%);time from SOR (h);", kTProfile2D, {centralityAxis, timeAxis}); @@ -245,6 +319,33 @@ struct zdc2stagecalibration { histos.add("QCorrelationQA/pQACorrelationBeforeVsCentrality", "A-C Q correlations before recentering;centrality (%);correlation component;", kTProfile2D, {centralityAxis, qCorrelationComponentAxis}); histos.add("QCorrelationQA/pQACorrelationAfterVsCentrality", "A-C Q correlations after recentering;centrality (%);correlation component;", kTProfile2D, {centralityAxis, qCorrelationComponentAxis}); + // Final-calibration QA: after recentering, correlation-only twist and optional shift. + histos.add("final_calibration/hQxAVsCentrality", "Final QxA vs centrality;centrality (%);Q_{x}^{A}", kTH2F, {centralityAxis, qValueAxis}); + histos.add("final_calibration/hQyAVsCentrality", "Final QyA vs centrality;centrality (%);Q_{y}^{A}", kTH2F, {centralityAxis, qValueAxis}); + histos.add("final_calibration/hQxCVsCentrality", "Final QxC vs centrality;centrality (%);Q_{x}^{C}", kTH2F, {centralityAxis, qValueAxis}); + histos.add("final_calibration/hQyCVsCentrality", "Final QyC vs centrality;centrality (%);Q_{y}^{C}", kTH2F, {centralityAxis, qValueAxis}); + histos.add("final_calibration/pQxAVsCentrality", "Final vs centrality;centrality (%);", kTProfile, {centralityAxis}); + histos.add("final_calibration/pQyAVsCentrality", "Final vs centrality;centrality (%);", kTProfile, {centralityAxis}); + histos.add("final_calibration/pQxCVsCentrality", "Final vs centrality;centrality (%);", kTProfile, {centralityAxis}); + histos.add("final_calibration/pQyCVsCentrality", "Final vs centrality;centrality (%);", kTProfile, {centralityAxis}); + histos.add("final_calibration/hPhiAVsCentrality", "Final ZNA #phi vs centrality;centrality (%);#phi_{A}", kTH2F, {centralityAxis, phiAxis}); + histos.add("final_calibration/hPhiCVsCentrality", "Final ZNC #phi vs centrality;centrality (%);#phi_{C}", kTH2F, {centralityAxis, phiAxis}); + histos.add("final_calibration/pCosPsiAPsiCVsCentrality", "Final A-C plane correlation;centrality (%);", kTProfile, {centralityAxis}); + histos.add("final_calibration/pCosPsiAPsiCVsCentralityVz", "Final A-C plane correlation vs centrality and v_{z};centrality (%);v_{z} (cm);", kTProfile2D, {centralityAxis, resolutionVzAxis}); + histos.add("final_calibration/pQxAQxCVsCentrality", "Final ;centrality (%);", kTProfile, {centralityAxis}); + histos.add("final_calibration/pQyAQyCVsCentrality", "Final ;centrality (%);", kTProfile, {centralityAxis}); + histos.add("final_calibration/pQxAQyCVsCentrality", "Final ;centrality (%);", kTProfile, {centralityAxis}); + histos.add("final_calibration/pQyAQxCVsCentrality", "Final ;centrality (%);", kTProfile, {centralityAxis}); + histos.add("final_calibration/pQxAVsCentralityVz", "Final vs centrality and v_{z};centrality (%);v_{z} (cm);", kTProfile2D, {centralityAxis, resolutionVzAxis}); + histos.add("final_calibration/pQyAVsCentralityVz", "Final vs centrality and v_{z};centrality (%);v_{z} (cm);", kTProfile2D, {centralityAxis, resolutionVzAxis}); + histos.add("final_calibration/pQxCVsCentralityVz", "Final vs centrality and v_{z};centrality (%);v_{z} (cm);", kTProfile2D, {centralityAxis, resolutionVzAxis}); + histos.add("final_calibration/pQyCVsCentralityVz", "Final vs centrality and v_{z};centrality (%);v_{z} (cm);", kTProfile2D, {centralityAxis, resolutionVzAxis}); + histos.add("final_calibration/pQMomentsVsCentrality", "Final Q moments: Qx,Qy,Qx^{2},Qy^{2},QxQy for A then C;centrality (%);moment index;", kTProfile2D, {centralityAxis, qTwistMomentAxis}); + histos.add("final_calibration/pPhiHarmonicsAVsCentrality", "Final ZNA #phi harmonics: cos(n#phi),sin(n#phi) for n=1..4;centrality (%);harmonic index;", kTProfile2D, {centralityAxis, phiHarmonicAxis}); + histos.add("final_calibration/pPhiHarmonicsCVsCentrality", "Final ZNC #phi harmonics: cos(n#phi),sin(n#phi) for n=1..4;centrality (%);harmonic index;", kTProfile2D, {centralityAxis, phiHarmonicAxis}); + histos.add("final_calibration/pQMagAVsCentrality", "Final |Q_{A}| vs centrality;centrality (%);<|Q_{A}|>", kTProfile, {centralityAxis}); + histos.add("final_calibration/pQMagCVsCentrality", "Final |Q_{C}| vs centrality;centrality (%);<|Q_{C}|>", kTProfile, {centralityAxis}); + ccdb->setURL(cfgCcdbParam.cfgURL); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); @@ -261,30 +362,64 @@ struct zdc2stagecalibration { auto runInfo = o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(o2::ccdb::BasicCCDBManager::instance(), runNumber); sorTimestamp = runInfo.sor; eorTimestamp = runInfo.eor; - bcSOR = static_cast(runInfo.orbitSOR) * static_cast(o2::constants::lhc::LHCMaxBunches); + bcSOR = runInfo.orbitSOR * static_cast(o2::constants::lhc::LHCMaxBunches); gainprofile = nullptr; spatialprofile = nullptr; qRecenteringProfile = nullptr; + qTwistProfile = nullptr; + qShiftProfile = nullptr; if (calibrationStage.value == 2 && useGainCallib.value && !confGainPath.value.empty()) { gainprofile = ccdb->getForTimeStamp(confGainPath.value, timestamp); if (!gainprofile) { - LOGF(warn, "No ZDC stage-1 gain map found for run %d at timestamp %llu", runNumber, static_cast(timestamp)); + LOGF(warn, "No ZDC stage-1 gain map found for run %d at timestamp %llu", runNumber, timestamp); } } if (calibrationStage.value == 2 && useSpatialCalib.value && !confSpatialPath.value.empty()) { spatialprofile = ccdb->getForTimeStamp(confSpatialPath.value, timestamp); if (!spatialprofile) { - LOGF(warn, "No ZDC spatial calibration found for run %d at timestamp %llu", runNumber, static_cast(timestamp)); + LOGF(warn, "No ZDC spatial calibration found for run %d at timestamp %llu", runNumber, timestamp); } } if (calibrationStage.value == 2 && useQRecentering.value) { - const std::string qRecenteringPath = qRecenteringMethod.value == 1 ? confQRecenteringMultidimPath.value : confQRecenteringPath.value; + const std::string qRecenteringPath = qRecenteringMethod.value == 4 ? confQRecenteringStructured71Path.value : (qRecenteringMethod.value == 1 ? confQRecenteringMultidimPath.value : confQRecenteringPath.value); if (!qRecenteringPath.empty()) { qRecenteringProfile = ccdb->getForTimeStamp(qRecenteringPath, timestamp); } if (!qRecenteringProfile) { - LOGF(warn, "No ZDC Q-recentering calibration found for run %d at timestamp %llu", runNumber, static_cast(timestamp)); + LOGF(warn, "No ZDC Q-recentering calibration found for run %d at timestamp %llu", runNumber, timestamp); + } else if (qRecenteringMethod.value == 4) { + const bool structuredMapOK = qRecenteringProfile->GetYaxis()->GetNbins() == kQRecenteringStructuredNFeatures && + std::string(qRecenteringProfile->GetYaxis()->GetBinLabel(kQRecenteringMultidimNFeatures + 1)) == "(T--0.8)_+^3" && + std::string(qRecenteringProfile->GetYaxis()->GetBinLabel(kQRecenteringStructuredNFeatures)) == "(Vz-+0.8)_+^3"; + if (!structuredMapOK) { + LOGF(fatal, "CCDB object at %s is not a valid structured71 map for run %d", qRecenteringPath.c_str(), runNumber); + } + } + } + if (calibrationStage.value == 2 && useQTwist.value) { + qTwistProfile = ccdb->getForTimeStamp(confQTwistPath.value, timestamp); + if (!qTwistProfile || qTwistProfile->GetNbinsX() != 80 || qTwistProfile->GetNbinsY() != 6) { + LOGF(fatal, "Missing/invalid ZDC twist map for run %d", runNumber); + } + constexpr std::array labels = {"ShearMxxA", "ShearMxyA", "ShearMyyA", "ShearMxxC", "ShearMxyC", "ShearMyyC"}; + for (int i = 0; i < 6; ++i) { + if (std::string(qTwistProfile->GetYaxis()->GetBinLabel(i + 1)) != labels[i]) { + LOGF(fatal, "Incorrect ZDC twist map component ordering for run %d", runNumber); + } + } + } + if (calibrationStage.value == 2 && useQShift.value) { + qShiftProfile = ccdb->getForTimeStamp(confQShiftPath.value, timestamp); + if (!qShiftProfile || qShiftProfile->GetNbinsX() != 80 || qShiftProfile->GetNbinsY() != 16) { + LOGF(fatal, "Missing/invalid ZDC shift map for run %d", runNumber); + } + constexpr std::array labels = {"cos1A", "sin1A", "cos2A", "sin2A", "cos3A", "sin3A", "cos4A", "sin4A", + "cos1C", "sin1C", "cos2C", "sin2C", "cos3C", "sin3C", "cos4C", "sin4C"}; + for (int i = 0; i < 16; ++i) { + if (std::string(qShiftProfile->GetYaxis()->GetBinLabel(i + 1)) != labels[i]) { + LOGF(fatal, "Incorrect ZDC shift map component ordering for run %d", runNumber); + } } } } @@ -356,6 +491,42 @@ struct zdc2stagecalibration { t * x * y, t * x * z, t * y * z, x * y * z}; } + std::array makeQRecenteringStructuredFeatures(float timeFromSOR, float vx, float vy, float vz) const + { + const auto baseFeatures = makeQRecenteringMultidimFeatures(timeFromSOR, vx, vy, vz); + std::array features{}; + + for (int i = 0; i < kQRecenteringMultidimNFeatures; ++i) { + features[i] = baseFeatures[i]; + } + + const double runHours = (eorTimestamp > sorTimestamp) ? static_cast(eorTimestamp - sorTimestamp) * 1.e-3 / 3600.0 : static_cast(cfgMaxRunHours.value); + const double t = (runHours > 0.0) ? (2.0 * static_cast(timeFromSOR) / runHours - 1.0) : 0.0; + const double x = static_cast(vx) / 0.1; + const double y = static_cast(vy) / 0.1; + const double z = static_cast(vz) / 10.0; + + // Internal knots in the normalized [-1,1] coordinate. Together with the + // existing 1,u,u^2,u^3 terms this is a cubic regression-spline basis. + constexpr std::array knots = { + -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8}; + + int index = kQRecenteringMultidimNFeatures; + auto appendSplineTerms = [&](double value) { + for (int i = 0; i < kQRecenteringStructuredNKnots; ++i) { + const double delta = value - knots[i]; + features[index++] = delta > 0.0 ? delta * delta * delta : 0.0; + } + }; + + appendSplineTerms(t); + appendSplineTerms(x); + appendSplineTerms(y); + appendSplineTerms(z); + + return features; + } + using MyCollisions = o2::soa::Join; void process(MyCollisions::iterator const& collision, o2::aod::FT0s const& /*ft0s*/, o2::aod::FV0As const& /*fv0s*/, BCsRun3 const& /*bcs*/, o2::aod::Zdcs const& /*zdcs*/) @@ -379,7 +550,7 @@ struct zdc2stagecalibration { }; if (!bc.has_zdc()) { - fillTable(false, 0.f, 0.f, 0.f, 0.f); + fillTable(false, -999.f, -999.f, -999.f, -999.f); return; } histos.fill(HIST("hEvtSelInfo"), 1.5); @@ -391,7 +562,7 @@ struct zdc2stagecalibration { const float zncEnergycommon = zdc.energyCommonZNC(); if (!std::isfinite(znaEnergycommon) || !std::isfinite(zncEnergycommon) || znaEnergycommon <= 0.f || zncEnergycommon <= 0.f) { - fillTable(false, 0.f, 0.f, 0.f, 0.f); + fillTable(false, -999.f, -999.f, -999.f, -999.f); return; } histos.fill(HIST("hEvtSelInfo"), 2.5); @@ -403,25 +574,25 @@ struct zdc2stagecalibration { } } if (!validTowers) { - fillTable(false, 0.f, 0.f, 0.f, 0.f); + fillTable(false, -999.f, -999.f, -999.f, -999.f); return; } histos.fill(HIST("hEvtSelInfo"), 3.5); if (rctCut.requireRCTFlagChecker && !rctChecker(collision)) { - fillTable(false, 0.f, 0.f, 0.f, 0.f); + fillTable(false, -999.f, -999.f, -999.f, -999.f); return; } histos.fill(HIST("hEvtSelInfo"), 4.5); if (!(collision.sel8() && collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) && std::isfinite(centrality) && centrality > cfgCutCentralityMin && centrality < cfgCutCentralityMax && std::abs(vz) < cfgCutVertex && collision.has_foundFT0())) { - fillTable(false, 0.f, 0.f, 0.f, 0.f); + fillTable(false, -999.f, -999.f, -999.f, -999.f); return; } histos.fill(HIST("hEvtSelInfo"), 5.5); if (timeFromSOR < 0.f || timeFromSOR >= cfgMaxRunHours.value) { - fillTable(false, 0.f, 0.f, 0.f, 0.f); + fillTable(false, -999.f, -999.f, -999.f, -999.f); return; } @@ -484,7 +655,7 @@ struct zdc2stagecalibration { zncCorr[i] = getGain(timeFromSOR, i + 4, gainOK) * zncEnergy[i]; } if (!gainOK) { - fillTable(false, 0.f, 0.f, 0.f, 0.f); + fillTable(false, -999.f, -999.f, -999.f, -999.f); return; } histos.fill(HIST("hEvtSelInfo"), 6.5); @@ -566,7 +737,7 @@ struct zdc2stagecalibration { } } if (!spatialOK) { - fillTable(false, 0.f, 0.f, 0.f, 0.f); + fillTable(false, -999.f, -999.f, -999.f, -999.f); return; } @@ -657,7 +828,7 @@ struct zdc2stagecalibration { } if (sumA <= 1.e-4 || sumC <= 1.e-4) { - fillTable(false, 0.f, 0.f, 0.f, 0.f); + fillTable(false, -999.f, -999.f, -999.f, -999.f); return; } @@ -677,7 +848,7 @@ struct zdc2stagecalibration { qyZDCC = cZNC * qyZDCC / sumC; if (!std::isfinite(qxZDCA) || !std::isfinite(qyZDCA) || !std::isfinite(qxZDCC) || !std::isfinite(qyZDCC)) { - fillTable(false, 0.f, 0.f, 0.f, 0.f); + fillTable(false, -999.f, -999.f, -999.f, -999.f); return; } @@ -732,6 +903,110 @@ struct zdc2stagecalibration { histos.fill(HIST("QRecenteringCalibration/hRegressionMomentsMultidim"), centrality, index + 0.5, qFeatures[i] * qValues[component]); } } + + // Store only the compact per-bin sums needed to add conditional-mean + // flatness penalties in the post-processing builder. The first feature + // is 1, therefore sum(X_0) is the number of events in that bin. + for (int i = 0; i < kQRecenteringMultidimNFeatures; ++i) { + const double value = qFeatures[i]; + histos.fill(HIST("QRecenteringCalibration/hFlatnessMomentsTime"), centrality, timeFromSOR, i + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hFlatnessMomentsVx"), centrality, vx, i + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hFlatnessMomentsVy"), centrality, vy, i + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hFlatnessMomentsVz"), centrality, vz, i + 0.5, value); + } + for (int component = 0; component < kQRecenteringNComponents; ++component) { + const int index = kQRecenteringMultidimNFeatures + component; + const double value = qValues[component]; + histos.fill(HIST("QRecenteringCalibration/hFlatnessMomentsTime"), centrality, timeFromSOR, index + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hFlatnessMomentsVx"), centrality, vx, index + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hFlatnessMomentsVy"), centrality, vy, index + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hFlatnessMomentsVz"), centrality, vz, index + 0.5, value); + } + + // Structured regression moments. Events are deterministically split + // 50/50 using a mixed global-BC key, avoiding any dependence on the + // regression variables themselves. + const auto structuredFeatures = makeQRecenteringStructuredFeatures(timeFromSOR, vx, vy, vz); + uint64_t splitKey = globalBC + 0x9e3779b97f4a7c15ULL; + splitKey = (splitKey ^ (splitKey >> 30)) * 0xbf58476d1ce4e5b9ULL; + splitKey = (splitKey ^ (splitKey >> 27)) * 0x94d049bb133111ebULL; + splitKey = splitKey ^ (splitKey >> 31); + const bool isValidation = (splitKey & 1ULL) != 0ULL; + + int structuredMoment = 0; + for (int i = 0; i < kQRecenteringStructuredNFeatures; ++i) { + for (int j = i; j < kQRecenteringStructuredNFeatures; ++j) { + const double value = structuredFeatures[i] * structuredFeatures[j]; + if (isValidation) { + histos.fill(HIST("QRecenteringCalibration/hRegressionMomentsStructuredValidation"), centrality, structuredMoment + 0.5, value); + } else { + histos.fill(HIST("QRecenteringCalibration/hRegressionMomentsStructuredTrain"), centrality, structuredMoment + 0.5, value); + } + ++structuredMoment; + } + } + + for (int component = 0; component < kQRecenteringNComponents; ++component) { + for (int i = 0; i < kQRecenteringStructuredNFeatures; ++i) { + const int index = kQRecenteringStructuredNMatrixMoments + component * kQRecenteringStructuredNFeatures + i; + const double value = structuredFeatures[i] * qValues[component]; + if (isValidation) { + histos.fill(HIST("QRecenteringCalibration/hRegressionMomentsStructuredValidation"), centrality, index + 0.5, value); + } else { + histos.fill(HIST("QRecenteringCalibration/hRegressionMomentsStructuredTrain"), centrality, index + 0.5, value); + } + } + + const int q2Index = kQRecenteringStructuredNMatrixMoments + kQRecenteringNComponents * kQRecenteringStructuredNFeatures + component; + const double q2Value = qValues[component] * qValues[component]; + if (isValidation) { + histos.fill(HIST("QRecenteringCalibration/hRegressionMomentsStructuredValidation"), centrality, q2Index + 0.5, q2Value); + } else { + histos.fill(HIST("QRecenteringCalibration/hRegressionMomentsStructuredTrain"), centrality, q2Index + 0.5, q2Value); + } + } + + for (int i = 0; i < kQRecenteringStructuredNFeatures; ++i) { + const double value = structuredFeatures[i]; + if (isValidation) { + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsTimeValidation"), centrality, timeFromSOR, i + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVxValidation"), centrality, vx, i + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVyValidation"), centrality, vy, i + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVzValidation"), centrality, vz, i + 0.5, value); + } else { + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsTimeTrain"), centrality, timeFromSOR, i + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVxTrain"), centrality, vx, i + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVyTrain"), centrality, vy, i + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVzTrain"), centrality, vz, i + 0.5, value); + } + } + + for (int component = 0; component < kQRecenteringNComponents; ++component) { + const int qIndex = kQRecenteringStructuredNFeatures + component; + const int q2Index = kQRecenteringStructuredNFeatures + kQRecenteringNComponents + component; + const double qValue = qValues[component]; + const double q2Value = qValue * qValue; + + if (isValidation) { + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsTimeValidation"), centrality, timeFromSOR, qIndex + 0.5, qValue); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVxValidation"), centrality, vx, qIndex + 0.5, qValue); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVyValidation"), centrality, vy, qIndex + 0.5, qValue); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVzValidation"), centrality, vz, qIndex + 0.5, qValue); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsTimeValidation"), centrality, timeFromSOR, q2Index + 0.5, q2Value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVxValidation"), centrality, vx, q2Index + 0.5, q2Value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVyValidation"), centrality, vy, q2Index + 0.5, q2Value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVzValidation"), centrality, vz, q2Index + 0.5, q2Value); + } else { + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsTimeTrain"), centrality, timeFromSOR, qIndex + 0.5, qValue); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVxTrain"), centrality, vx, qIndex + 0.5, qValue); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVyTrain"), centrality, vy, qIndex + 0.5, qValue); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVzTrain"), centrality, vz, qIndex + 0.5, qValue); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsTimeTrain"), centrality, timeFromSOR, q2Index + 0.5, q2Value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVxTrain"), centrality, vx, q2Index + 0.5, q2Value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVyTrain"), centrality, vy, q2Index + 0.5, q2Value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVzTrain"), centrality, vz, q2Index + 0.5, q2Value); + } + } } } @@ -757,6 +1032,24 @@ struct zdc2stagecalibration { } qValues[component] -= predictedBias; } + } else if (qRecenteringMethod.value == 4) { + const auto qFeatures = makeQRecenteringStructuredFeatures(timeFromSOR, vx, vy, vz); + for (int component = 0; component < kQRecenteringNComponents; ++component) { + double predictedBias = 0.0; + for (int i = 0; i < kQRecenteringStructuredNFeatures; ++i) { + const double coefficient = qRecenteringProfile->GetBinContent(qRecenteringProfile->FindBin(centrality, i + 0.5, component + 0.5)); + if (!std::isfinite(coefficient)) { + qRecenteringOK = false; + break; + } + predictedBias += coefficient * qFeatures[i]; + } + if (!qRecenteringOK || !std::isfinite(predictedBias)) { + qRecenteringOK = false; + break; + } + qValues[component] -= predictedBias; + } } else { const auto qFeatures = makeQRecenteringMultidimFeatures(timeFromSOR, vx, vy, vz); for (int component = 0; component < kQRecenteringNComponents; ++component) { @@ -778,7 +1071,7 @@ struct zdc2stagecalibration { } } if (!qRecenteringOK) { - fillTable(false, 0.f, 0.f, 0.f, 0.f); + fillTable(false, -999.f, -999.f, -999.f, -999.f); return; } @@ -822,6 +1115,154 @@ struct zdc2stagecalibration { histos.fill(HIST("PhiQA/hPhiAfterRecenteringZNC"), phiRecenteringC); histos.fill(HIST("PhiQA/hPhiAfterRecenteringVsCentralityZNA"), centrality, phiRecenteringA); histos.fill(HIST("PhiQA/hPhiAfterRecenteringVsCentralityZNC"), centrality, phiRecenteringC); + if (calibrationStage.value == 2 && useQRecentering.value) { + histos.fill(HIST("QTwistQA/hPhiBeforeTwistHighResVsCentralityZNA"), centrality, phiRecenteringA); + histos.fill(HIST("QTwistQA/hPhiBeforeTwistHighResVsCentralityZNC"), centrality, phiRecenteringC); + } + + const auto makeTwistMoments = [](const std::array& q) { + return std::array{q[0], q[1], q[0] * q[0], q[1] * q[1], q[0] * q[1], + q[2], q[3], q[2] * q[2], q[3] * q[3], q[2] * q[3]}; + }; + const auto momentsBeforeTwist = makeTwistMoments(qValues); + // Correlation moments are collected directly after recentering, BEFORE twist. + // Derive the run-wise shear map from these same-side moments (no width map). + if (calibrationStage.value == 2 && useQRecentering.value) { + for (int i = 0; i < 10; ++i) { + histos.fill(HIST("QTwistQA/pMomentsBeforeVsCentrality"), centrality, i + 0.5, momentsBeforeTwist[i]); + } + } + if (useQTwist.value) { + const int centBin = qTwistProfile->GetXaxis()->FindFixBin(centrality); + if (centBin < 1 || centBin > qTwistProfile->GetNbinsX()) { + fillTable(false, -999.f, -999.f, -999.f, -999.f); + return; + } + for (int side = 0; side < 2; ++side) { + const int offset = 3 * side; + const double mxx = qTwistProfile->GetBinContent(centBin, offset + 1); + const double mxy = qTwistProfile->GetBinContent(centBin, offset + 2); + const double myy = qTwistProfile->GetBinContent(centBin, offset + 3); + // Enforce the new shear-map format. Old covariance-whitening maps are incompatible. + if (!std::isfinite(mxx) || !std::isfinite(mxy) || !std::isfinite(myy) || + std::abs(mxx - 1.0) > 1.e-10 || std::abs(myy - 1.0) > 1.e-10 || std::abs(mxy) >= 1.0) { + fillTable(false, -999.f, -999.f, -999.f, -999.f); + return; + } + const int ix = 2 * side; + const double qx = qValues[ix]; + const double qy = qValues[ix + 1]; + qValues[ix] = mxx * qx + mxy * qy; + qValues[ix + 1] = mxy * qx + myy * qy; + } + for (int i = 0; i < 4; ++i) { + if (!std::isfinite(qValues[i]) || !std::isfinite(static_cast(qValues[i]))) { + fillTable(false, -999.f, -999.f, -999.f, -999.f); + return; + } + } + const auto momentsAfterTwist = makeTwistMoments(qValues); + for (int i = 0; i < 10; ++i) { + histos.fill(HIST("QTwistQA/pMomentsAfterVsCentrality"), centrality, i + 0.5, momentsAfterTwist[i]); + } + } + double phiFinalA = std::atan2(qValues[1], qValues[0]); + double phiFinalC = std::atan2(qValues[3], qValues[2]); + if (useQTwist.value) { + histos.fill(HIST("PhiQA/hPhiAfterTwistVsCentralityZNA"), centrality, phiFinalA); + histos.fill(HIST("PhiQA/hPhiAfterTwistVsCentralityZNC"), centrality, phiFinalC); + for (int n = 1; n <= 4; ++n) { + histos.fill(HIST("QShiftQA/pPhiHarmonicsBeforeVsCentralityZNA"), centrality, 2 * n - 1.5, std::cos(n * phiFinalA)); + histos.fill(HIST("QShiftQA/pPhiHarmonicsBeforeVsCentralityZNA"), centrality, 2 * n - 0.5, std::sin(n * phiFinalA)); + histos.fill(HIST("QShiftQA/pPhiHarmonicsBeforeVsCentralityZNC"), centrality, 2 * n - 1.5, std::cos(n * phiFinalC)); + histos.fill(HIST("QShiftQA/pPhiHarmonicsBeforeVsCentralityZNC"), centrality, 2 * n - 0.5, std::sin(n * phiFinalC)); + } + } + if (useQShift.value) { + const int centBin = qShiftProfile->GetXaxis()->FindFixBin(centrality); + if (centBin < 1 || centBin > qShiftProfile->GetNbinsX()) { + fillTable(false, -999.f, -999.f, -999.f, -999.f); + return; + } + for (int side = 0; side < 2; ++side) { + const int ix = 2 * side; + const double phi = std::atan2(qValues[ix + 1], qValues[ix]); + const double qMag = std::hypot(qValues[ix], qValues[ix + 1]); + double deltaPhi = 0.0; + for (int n = 1; n <= 4; ++n) { + const double cosMean = qShiftProfile->GetBinContent(centBin, 8 * side + 2 * n - 1); + const double sinMean = qShiftProfile->GetBinContent(centBin, 8 * side + 2 * n); + if (!std::isfinite(cosMean) || !std::isfinite(sinMean)) { + fillTable(false, -999.f, -999.f, -999.f, -999.f); + return; + } + deltaPhi += (2.0 / n) * (cosMean * std::sin(n * phi) - sinMean * std::cos(n * phi)); + } + const double phiNew = phi + deltaPhi; + // Preserve |Q| and rotate both Cartesian components to the shifted angle. + qValues[ix] = qMag * std::cos(phiNew); + qValues[ix + 1] = qMag * std::sin(phiNew); + if (!std::isfinite(qValues[ix]) || !std::isfinite(qValues[ix + 1]) || + !std::isfinite(static_cast(qValues[ix])) || !std::isfinite(static_cast(qValues[ix + 1]))) { + fillTable(false, -999.f, -999.f, -999.f, -999.f); + return; + } + } + phiFinalA = std::atan2(qValues[1], qValues[0]); + phiFinalC = std::atan2(qValues[3], qValues[2]); + histos.fill(HIST("QShiftQA/hPhiAfterShiftVsCentralityZNA"), centrality, phiFinalA); + histos.fill(HIST("QShiftQA/hPhiAfterShiftVsCentralityZNC"), centrality, phiFinalC); + for (int n = 1; n <= 4; ++n) { + histos.fill(HIST("QShiftQA/pPhiHarmonicsAfterVsCentralityZNA"), centrality, 2 * n - 1.5, std::cos(n * phiFinalA)); + histos.fill(HIST("QShiftQA/pPhiHarmonicsAfterVsCentralityZNA"), centrality, 2 * n - 0.5, std::sin(n * phiFinalA)); + histos.fill(HIST("QShiftQA/pPhiHarmonicsAfterVsCentralityZNC"), centrality, 2 * n - 1.5, std::cos(n * phiFinalC)); + histos.fill(HIST("QShiftQA/pPhiHarmonicsAfterVsCentralityZNC"), centrality, 2 * n - 0.5, std::sin(n * phiFinalC)); + } + } + // ZDC resolution after all enabled corrections + const double qMagA = std::hypot(qValues[0], qValues[1]); + const double qMagC = std::hypot(qValues[2], qValues[3]); + if (centrality >= 0.f && centrality < 50.f && qMagA > 0. && qMagC > 0.) { + const double correlation = std::cos(phiFinalA - phiFinalC); + histos.fill(HIST("ResolutionQA/pCosPsiAPsiCVsCentrality"), centrality, correlation); + histos.fill(HIST("ResolutionQA/pCosPsiAPsiCVsCentralityVz"), centrality, vz, correlation); + } + + if (calibrationStage.value == 2 && useQRecentering.value && useQTwist.value && qMagA > 0. && qMagC > 0.) { + histos.fill(HIST("final_calibration/hQxAVsCentrality"), centrality, qValues[0]); + histos.fill(HIST("final_calibration/hQyAVsCentrality"), centrality, qValues[1]); + histos.fill(HIST("final_calibration/hQxCVsCentrality"), centrality, qValues[2]); + histos.fill(HIST("final_calibration/hQyCVsCentrality"), centrality, qValues[3]); + histos.fill(HIST("final_calibration/pQxAVsCentrality"), centrality, qValues[0]); + histos.fill(HIST("final_calibration/pQyAVsCentrality"), centrality, qValues[1]); + histos.fill(HIST("final_calibration/pQxCVsCentrality"), centrality, qValues[2]); + histos.fill(HIST("final_calibration/pQyCVsCentrality"), centrality, qValues[3]); + histos.fill(HIST("final_calibration/hPhiAVsCentrality"), centrality, phiFinalA); + histos.fill(HIST("final_calibration/hPhiCVsCentrality"), centrality, phiFinalC); + const double finalCorrelation = std::cos(phiFinalA - phiFinalC); + histos.fill(HIST("final_calibration/pCosPsiAPsiCVsCentrality"), centrality, finalCorrelation); + histos.fill(HIST("final_calibration/pCosPsiAPsiCVsCentralityVz"), centrality, vz, finalCorrelation); + histos.fill(HIST("final_calibration/pQxAQxCVsCentrality"), centrality, qValues[0] * qValues[2]); + histos.fill(HIST("final_calibration/pQyAQyCVsCentrality"), centrality, qValues[1] * qValues[3]); + histos.fill(HIST("final_calibration/pQxAQyCVsCentrality"), centrality, qValues[0] * qValues[3]); + histos.fill(HIST("final_calibration/pQyAQxCVsCentrality"), centrality, qValues[1] * qValues[2]); + histos.fill(HIST("final_calibration/pQxAVsCentralityVz"), centrality, vz, qValues[0]); + histos.fill(HIST("final_calibration/pQyAVsCentralityVz"), centrality, vz, qValues[1]); + histos.fill(HIST("final_calibration/pQxCVsCentralityVz"), centrality, vz, qValues[2]); + histos.fill(HIST("final_calibration/pQyCVsCentralityVz"), centrality, vz, qValues[3]); + const auto finalMoments = makeTwistMoments(qValues); + for (int i = 0; i < 10; ++i) { + histos.fill(HIST("final_calibration/pQMomentsVsCentrality"), centrality, i + 0.5, finalMoments[i]); + } + for (int n = 1; n <= 4; ++n) { + histos.fill(HIST("final_calibration/pPhiHarmonicsAVsCentrality"), centrality, 2 * n - 1.5, std::cos(n * phiFinalA)); + histos.fill(HIST("final_calibration/pPhiHarmonicsAVsCentrality"), centrality, 2 * n - 0.5, std::sin(n * phiFinalA)); + histos.fill(HIST("final_calibration/pPhiHarmonicsCVsCentrality"), centrality, 2 * n - 1.5, std::cos(n * phiFinalC)); + histos.fill(HIST("final_calibration/pPhiHarmonicsCVsCentrality"), centrality, 2 * n - 0.5, std::sin(n * phiFinalC)); + } + histos.fill(HIST("final_calibration/pQMagAVsCentrality"), centrality, qMagA); + histos.fill(HIST("final_calibration/pQMagCVsCentrality"), centrality, qMagC); + } histos.fill(HIST("hEvtSelInfo"), 7.5); fillTable(true, static_cast(qValues[0]), static_cast(qValues[2]), static_cast(qValues[1]), static_cast(qValues[3])); diff --git a/PWGLF/TableProducer/Common/zdcvector.cxx b/PWGLF/TableProducer/Common/zdcvector.cxx index d1c5eb20766..ff67bf9f081 100644 --- a/PWGLF/TableProducer/Common/zdcvector.cxx +++ b/PWGLF/TableProducer/Common/zdcvector.cxx @@ -46,7 +46,6 @@ #include #include #include -#include #include using namespace o2; @@ -240,7 +239,7 @@ struct zdcvector { LOGF(info, "Run %d: SOR timestamp = %llu ms", currentRunNumber, - static_cast(sorTimestamp)); + sorTimestamp); } float timeInMinutes = 0.f; diff --git a/PWGLF/TableProducer/GlobalEventProperties/CMakeLists.txt b/PWGLF/TableProducer/GlobalEventProperties/CMakeLists.txt new file mode 100644 index 00000000000..a93df119cbc --- /dev/null +++ b/PWGLF/TableProducer/GlobalEventProperties/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright 2019-2020 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. + +o2physics_add_dpl_workflow(event-shape-coex + SOURCES eventShapeCoex.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB + COMPONENT_NAME Analysis) diff --git a/PWGLF/TableProducer/GlobalEventProperties/eventShapeCoex.cxx b/PWGLF/TableProducer/GlobalEventProperties/eventShapeCoex.cxx new file mode 100644 index 00000000000..c8f66fce50f --- /dev/null +++ b/PWGLF/TableProducer/GlobalEventProperties/eventShapeCoex.cxx @@ -0,0 +1,618 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file eventShapeCoex.cxx +/// \brief Moment sums of the forward/backward sub-event mean pT of mid-rapidity tracks +/// \author Cristian Andrei +/// +/// First O2Physics task of the AliPhysics PWGLF/SPECTRA/MultEvShape analyses. + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/ctpRateFetcher.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +namespace +{ +constexpr float PtMin = 0.15f; // GeV/c +constexpr float EtaMax = 0.8f; +constexpr double MeanPtShift = 0.6; // GeV/c + +// Sub-events are unions of |eta| x pT rings; the last pT ring is open-ended +constexpr int NEtaRings = 5; +constexpr std::array EtaRingEdges = {0.f, 0.2f, 0.3f, 0.4f, 0.5f, EtaMax}; +constexpr int NPtRings = 6; +constexpr std::array PtRingLowEdges = {PtMin, 1.f, 1.5f, 2.f, 3.f, 5.f}; +constexpr int EtaFirstNominal = 1; // |eta| > 0.2 +constexpr int PtEndNominal = 3; // pT < 2 GeV/c + +enum Side { kF = 0, + kB, + NSides }; + +// Monomial exponents of (xF, xB, vF, vB); the order is part of the output format +constexpr int NMono1 = 16; +constexpr int NMono = 81; +constexpr std::array, NMono> MonoExp = {{{0, 0, 0, 0}, {1, 0, 0, 0}, {0, 1, 0, 0}, {2, 0, 0, 0}, {0, 2, 0, 0}, {1, 1, 0, 0}, {2, 1, 0, 0}, {1, 2, 0, 0}, {2, 2, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}, {0, 1, 1, 0}, {1, 0, 0, 1}, {0, 2, 1, 0}, {2, 0, 0, 1}, {0, 0, 1, 1}, {0, 0, 0, 2}, {0, 0, 1, 2}, {0, 0, 2, 0}, {0, 0, 2, 1}, {0, 0, 2, 2}, {0, 1, 0, 1}, {0, 1, 1, 1}, {0, 1, 2, 0}, {0, 1, 2, 1}, {0, 2, 0, 1}, {0, 2, 1, 1}, {0, 2, 2, 0}, {0, 2, 2, 1}, {0, 3, 0, 0}, {0, 3, 1, 0}, {0, 3, 2, 0}, {0, 4, 0, 0}, {0, 4, 1, 0}, {0, 4, 2, 0}, {1, 0, 0, 2}, {1, 0, 1, 0}, {1, 0, 1, 1}, {1, 0, 1, 2}, {1, 1, 0, 1}, {1, 1, 1, 0}, {1, 1, 1, 1}, {1, 2, 0, 1}, {1, 2, 1, 0}, {1, 2, 1, 1}, {1, 3, 0, 0}, {1, 3, 1, 0}, {1, 4, 0, 0}, {1, 4, 1, 0}, {2, 0, 0, 2}, {2, 0, 1, 0}, {2, 0, 1, 1}, {2, 0, 1, 2}, {2, 1, 0, 1}, {2, 1, 1, 0}, {2, 1, 1, 1}, {2, 2, 0, 1}, {2, 2, 1, 0}, {2, 2, 1, 1}, {2, 3, 0, 0}, {2, 3, 1, 0}, {2, 4, 0, 0}, {2, 4, 1, 0}, {3, 0, 0, 0}, {3, 0, 0, 1}, {3, 0, 0, 2}, {3, 1, 0, 0}, {3, 1, 0, 1}, {3, 2, 0, 0}, {3, 2, 0, 1}, {3, 3, 0, 0}, {3, 4, 0, 0}, {4, 0, 0, 0}, {4, 0, 0, 1}, {4, 0, 0, 2}, {4, 1, 0, 0}, {4, 1, 0, 1}, {4, 2, 0, 0}, {4, 2, 0, 1}, {4, 3, 0, 0}, {4, 4, 0, 0}}}; + +// Event-selection bits and the pile-up selection variants built from them +constexpr int NQaBits = 12; +constexpr std::array QaBitOrder = { + o2::aod::evsel::kNoSameBunchPileup, o2::aod::evsel::kIsGoodZvtxFT0vsPV, + o2::aod::evsel::kIsVertexITSTPC, o2::aod::evsel::kIsVertexTOFmatched, + o2::aod::evsel::kNoCollInTimeRangeNarrow, o2::aod::evsel::kNoCollInTimeRangeStrict, + o2::aod::evsel::kNoCollInTimeRangeStandard, o2::aod::evsel::kNoCollInRofStrict, + o2::aod::evsel::kNoCollInRofStandard, o2::aod::evsel::kNoHighMultCollInPrevRof, + o2::aod::evsel::kNoITSROFrameBorder, o2::aod::evsel::kNoTimeFrameBorder}; +enum EvVariant { kV1 = 0, + kV0, + kV3, + kV6, + NEvVariants }; +constexpr std::array EvVariantMask = { + (1u << 0) | (1u << 1), + 0u, + (1u << 0) | (1u << 1) | (1u << 6), + (1u << 0) | (1u << 1) | (1u << 2) | (1u << 6) | (1u << 8) | (1u << 9)}; +constexpr uint16_t AllQaBits = 0xFFFF; + +// Track count defining the stratum +enum Conditioning { kCondOwn = 0, // own sub-events + kCondAll, // all tracks, gap included + kCondUnwindowed }; // nominal gap, no upper pT cut + +struct Config { + const char* name; + int etaFirst; // |eta| rings [etaFirst, NEtaRings) + int ptEnd; // pT rings [0, ptEnd) + Conditioning cond; // stratum count + int validEtaFirst; // sub-events that must hold MinTracksValid tracks + int validPtEnd; + EvVariant evVariant; // pile-up selection +}; +constexpr int NConfigs = 13; +constexpr int INominal = 0; +constexpr int IPairFirst = 1; // GAP02 +constexpr int IPairSecond = 4; // GAP05, same events and stratum count as GAP02 +constexpr std::array Configs = {{{.name = "NOM", .etaFirst = 1, .ptEnd = 3, .cond = kCondOwn, .validEtaFirst = 1, .validPtEnd = 3, .evVariant = kV1}, + {.name = "GAP02", .etaFirst = 1, .ptEnd = 3, .cond = kCondAll, .validEtaFirst = 4, .validPtEnd = 3, .evVariant = kV1}, + {.name = "GAP03", .etaFirst = 2, .ptEnd = 3, .cond = kCondAll, .validEtaFirst = 4, .validPtEnd = 3, .evVariant = kV1}, + {.name = "GAP04", .etaFirst = 3, .ptEnd = 3, .cond = kCondAll, .validEtaFirst = 4, .validPtEnd = 3, .evVariant = kV1}, + {.name = "GAP05", .etaFirst = 4, .ptEnd = 3, .cond = kCondAll, .validEtaFirst = 4, .validPtEnd = 3, .evVariant = kV1}, + {.name = "PT10", .etaFirst = 1, .ptEnd = 1, .cond = kCondUnwindowed, .validEtaFirst = 1, .validPtEnd = 1, .evVariant = kV1}, + {.name = "PT15", .etaFirst = 1, .ptEnd = 2, .cond = kCondUnwindowed, .validEtaFirst = 1, .validPtEnd = 1, .evVariant = kV1}, + {.name = "PT20", .etaFirst = 1, .ptEnd = 3, .cond = kCondUnwindowed, .validEtaFirst = 1, .validPtEnd = 1, .evVariant = kV1}, + {.name = "PT30", .etaFirst = 1, .ptEnd = 4, .cond = kCondUnwindowed, .validEtaFirst = 1, .validPtEnd = 1, .evVariant = kV1}, + {.name = "PT50", .etaFirst = 1, .ptEnd = 5, .cond = kCondUnwindowed, .validEtaFirst = 1, .validPtEnd = 1, .evVariant = kV1}, + {.name = "EVV0", .etaFirst = 1, .ptEnd = 3, .cond = kCondOwn, .validEtaFirst = 1, .validPtEnd = 3, .evVariant = kV0}, + {.name = "EVV3", .etaFirst = 1, .ptEnd = 3, .cond = kCondOwn, .validEtaFirst = 1, .validPtEnd = 3, .evVariant = kV3}, + {.name = "EVV6", .etaFirst = 1, .ptEnd = 3, .cond = kCondOwn, .validEtaFirst = 1, .validPtEnd = 3, .evVariant = kV6}}}; + +constexpr int NCountBins = 64; // larger counts in the overflow bin +constexpr int NSubCountBins = 32; // per sub-event +constexpr int MinTracksValid = 2; // per sub-event +constexpr int NCovariateQuantities = 7; + +// Data halves and subsample groups, defined on blocks of bunch crossings +constexpr int NHalves = 2; +constexpr std::array HalfName = {"A", "B_SEALED"}; +constexpr int HalfBlockShift = 26; // 2^26 BC = 1.7 s +constexpr int GroupBlockShift = 20; // 2^20 BC = 26 ms +constexpr int NGroups = 20; + +// MC only: nominal sums split by two hashes of the MC collision (run, globalBC) +constexpr int NSplitBins = 8; +constexpr int NSubsamples = 16; +constexpr uint64_t SplitSalt = 0x6a09e667f3bcc909ULL; + +/// splitmix64 finaliser +constexpr uint64_t mix64(uint64_t z) +{ + z += 0x9e3779b97f4a7c15ULL; + z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL; + z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL; + return z ^ (z >> 31); +} + +/// Hash of the (run, block of 2^shift bunch crossings) of an event +uint64_t blockHash(int run, uint64_t globalBC, int shift) +{ + return mix64(mix64(static_cast(run)) ^ (globalBC >> shift)); +} + +/// Per-event hash, independent of the block hashes +uint64_t eventHash(int run, uint64_t globalBC) +{ + return mix64(mix64(static_cast(run) ^ SplitSalt) ^ globalBC); +} + +struct SubEvent { + int n = 0; + double sumPt = 0.; + double sumPt2 = 0.; +}; + +/// Track sums of one event per (side, |eta| ring, pT ring) +struct Rings { + std::array ring{}; + + void add(float eta, double pt) + { + const float absEta = std::abs(eta); + int iEta = 0; + while (iEta < NEtaRings - 1 && absEta > EtaRingEdges[iEta + 1]) { + ++iEta; + } + int iPt = 0; + while (iPt < NPtRings - 1 && pt >= PtRingLowEdges[iPt + 1]) { + ++iPt; + } + auto& r = ring[((eta > 0.f ? kF : kB) * NEtaRings + iEta) * NPtRings + iPt]; + ++r.n; + r.sumPt += pt; + r.sumPt2 += pt * pt; + } + + /// Sub-event of one side: |eta| rings [etaFirst, NEtaRings), pT rings [0, ptEnd) + [[nodiscard]] SubEvent sum(int side, int etaFirst, int ptEnd) const + { + SubEvent s; + for (int iEta = etaFirst; iEta < NEtaRings; ++iEta) { + for (int iPt = 0; iPt < ptEnd; ++iPt) { + const auto& r = ring[(side * NEtaRings + iEta) * NPtRings + iPt]; + s.n += r.n; + s.sumPt += r.sumPt; + s.sumPt2 += r.sumPt2; + } + } + return s; + } + + [[nodiscard]] int count(int etaFirst, int ptEnd) const { return sum(kF, etaFirst, ptEnd).n + sum(kB, etaFirst, ptEnd).n; } +}; + +/// x = - MeanPtShift, v = pT variance / n (0 for n < 2) +void observe(const SubEvent& s, double& x, double& v) +{ + x = s.sumPt / s.n - MeanPtShift; + v = 0.; + if (s.n > 1) { + const double var = (s.sumPt2 - s.sumPt * s.sumPt / s.n) / (s.n - 1); + v = std::max(var, 0.) / s.n; + } +} + +/// The first nMono monomials of (xF, xB, vF, vB) +void monomials(double xF, double xB, double vF, double vB, int nMono, std::array& m) +{ + const std::array pxF = {1., xF, xF * xF, xF * xF * xF, xF * xF * xF * xF}; + const std::array pxB = {1., xB, xB * xB, xB * xB * xB, xB * xB * xB * xB}; + const std::array pvF = {1., vF, vF * vF}; + const std::array pvB = {1., vB, vB * vB}; + for (int k = 0; k < nMono; ++k) { + const auto& e = MonoExp[k]; + m[k] = pxF[e[0]] * pxB[e[1]] * pvF[e[2]] * pvB[e[3]]; + } +} + +/// Moment sums of one reconstructed half or of the generated events +struct Sums { + std::array, NConfigs> config{}; // (N_fwd, stratum count, monomial) + std::shared_ptr strata3; // (N_fwd, n_F, n_B, first-order monomial) + std::shared_ptr occupancy; // (configuration, N_fwd, stratum count) + std::shared_ptr paired; // (N_fwd, stratum count, GAP02 x GAP05 first-order monomials) +}; + +/// Nominal-configuration observables of an event +struct Nominal { + bool valid = false; + double nFwd = 0.; + int nMid = 0; + double xF = 0., xB = 0., vF = 0., vB = 0.; +}; + +enum EventFlowBin { kFlowAll = 0, + kFlowSel8, + kFlowVtxZ, + kFlowPileupV1, + kFlowNominalValid, + kFlowHalfA, + kFlowHalfB, + kFlowNoMcLabel, + NEventFlowBins }; +constexpr std::array EventFlowLabels = {"all", "sel8", "|z|", "pile-up V1", "nominal valid", "half A", "half B", "nominal valid, no MC label"}; +} // namespace + +struct EventShapeCoex { + HistogramRegistry registry{"registry"}; + Service pdg{}; + Service ccdb{}; + ctpRateFetcher rateFetcher; + + Configurable vtxZCut{"vtxZCut", 10.0f, "Max |PV z| (cm)"}; + Configurable etaFwdAMin{"etaFwdAMin", 3.5f, "FT0-A proxy: lower eta edge (generator level)"}; + Configurable etaFwdAMax{"etaFwdAMax", 4.9f, "FT0-A proxy: upper eta edge (generator level)"}; + Configurable etaFwdCMin{"etaFwdCMin", -3.3f, "FT0-C proxy: lower eta edge (generator level)"}; + Configurable etaFwdCMax{"etaFwdCMax", -2.1f, "FT0-C proxy: upper eta edge (generator level)"}; + ConfigurableAxis axisNFwd{"axisNFwd", {50, 1., 30000.}, "N_fwd = FT0A + FT0C amplitude (a fixed-width setting is made logarithmic)"}; + ConfigurableAxis axisNFwdGen{"axisNFwdGen", {50, 1., 1000.}, "generator-level N_fwd = charged primaries in the FT0 acceptance (a fixed-width setting is made logarithmic)"}; + ConfigurableAxis axisRun{"axisRun", {60000, 519999.5, 579999.5}, "run number (QA)"}; + ConfigurableAxis axisOccupancy{"axisOccupancy", {50, 0., 1000.}, "track occupancy in time range (QA; unset in underflow)"}; + ConfigurableAxis axisVtxZ{"axisVtxZ", {40, -10., 10.}, "PV z (cm, QA)"}; + ConfigurableAxis axisRate{"axisRate", {100, 0., 1000.}, "interaction rate (kHz, QA; unavailable in underflow)"}; + Configurable irSource{"irSource", "T0VTX", "interaction-rate estimator for QA; empty = not fetched"}; + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "CCDB url"}; + Configurable doSplitSums{"doSplitSums", true, "MC: nominal sums split by hashed event bins and subsamples"}; + + Filter trackFilter = (aod::track::pt > PtMin) && (nabs(aod::track::eta) < EtaMax) && requireGlobalTrackInFilter(); + + using MyCollisions = soa::Join; + using MyCollisionsMc = soa::Join; + using MyTracks = soa::Filtered>; + + std::array sumsReco{}; + std::array, NHalves> groupSums{}; // (N_fwd, stratum count, group x first-order monomial) + Sums sumsGen{}; + + /// Nominal sums of MC events split by hashed event bin (full) and by (subsample, event bin) (sub) + struct SplitSums { + std::shared_ptr full; // (N_fwd, stratum count, event bin x monomial) + std::shared_ptr sub; // (N_fwd, stratum count, (subsample x event bin) x first-order monomial) + }; + SplitSums splitReco{}, splitGen{}; + + void bookSplit(SplitSums& split, const std::string& dir, const AxisSpec& nFwdAxis) + { + const AxisSpec countAxis{NCountBins, -0.5, NCountBins - 0.5, "stratum count"}; + split.full = registry.add(dir + "/full", "NOM sums per event bin", HistType::kTH3D, {nFwdAxis, countAxis, {NSplitBins * NMono, -0.5, NSplitBins * NMono - 0.5, "event bin x monomial"}}); + split.sub = registry.add(dir + "/sub", "NOM first-order sums per subsample and event bin", HistType::kTH3D, {nFwdAxis, countAxis, {NSubsamples * NSplitBins * NMono1, -0.5, NSubsamples * NSplitBins * NMono1 - 0.5, "(subsample x event bin) x monomial"}}); + split.full->SetBit(TH1::kIsNotW); + split.sub->SetBit(TH1::kIsNotW); + } + + void fillSplit(SplitSums& split, const Nominal& nominal, int run, uint64_t globalBC) + { + const uint64_t h = eventHash(run, globalBC); + const int bin = static_cast(h % NSplitBins); + const int subsample = static_cast((h >> 32) % NSubsamples); + std::array m{}; + monomials(nominal.xF, nominal.xB, nominal.vF, nominal.vB, NMono, m); + for (int k = 0; k < NMono; ++k) { + split.full->Fill(nominal.nFwd, nominal.nMid, bin * NMono + k, m[k]); + } + for (int k = 0; k < NMono1; ++k) { + split.sub->Fill(nominal.nFwd, nominal.nMid, (subsample * NSplitBins + bin) * NMono1 + k, m[k]); + } + } + + /// Books the moment sums of one level under dir// + void bookSums(Sums& sums, const std::string& dir, const std::string& half, const AxisSpec& nFwdAxis, int nConfigs) + { + const AxisSpec countAxis{NCountBins, -0.5, NCountBins - 0.5, "stratum count"}; + const AxisSpec subCountAxis{NSubCountBins, -0.5, NSubCountBins - 0.5, "sub-event count"}; + const AxisSpec monoAxis{NMono, -0.5, NMono - 0.5, "monomial"}; + const AxisSpec mono1Axis{NMono1, -0.5, NMono1 - 0.5, "monomial"}; + const AxisSpec configAxis{NConfigs, -0.5, NConfigs - 0.5, "configuration"}; + std::string label = "[half "; + label += half; + label += "] moment sums "; + for (int c = 0; c < nConfigs; ++c) { + std::string name = dir; + name += "/"; + name += Configs[c].name; + name += "/"; + name += half; + std::string title = label; + title += Configs[c].name; + sums.config[c] = registry.add(name, title.c_str(), HistType::kTH3D, {nFwdAxis, countAxis, monoAxis}); + sums.config[c]->SetBit(TH1::kIsNotW); // no Sumw2 + } + sums.strata3 = registry.add(dir + "/NOM3/" + half, (label + "NOM in (n_F, n_B) strata").c_str(), HistType::kTHnD, {nFwdAxis, subCountAxis, subCountAxis, mono1Axis}); + sums.occupancy = registry.add(dir + "/occupancy/" + half, "event counts per configuration", HistType::kTH3D, {configAxis, nFwdAxis, countAxis}); + if (nConfigs > IPairSecond) { + std::string pairName = dir; + pairName += "/"; + pairName += Configs[IPairFirst].name; + pairName += "x"; + pairName += Configs[IPairSecond].name; + pairName += "/"; + pairName += half; + sums.paired = registry.add(pairName, (label + "cross products of two configurations").c_str(), HistType::kTH3D, {nFwdAxis, countAxis, {NMono1 * NMono1, -0.5, NMono1 * NMono1 - 0.5, "monomial pair"}}); + sums.paired->SetBit(TH1::kIsNotW); + } + } + + void init(InitContext const&) + { + if (doprocessReco && doprocessRecoMC) { + LOGF(fatal, "processReco and processRecoMC both fill the reconstructed-level sums; enable only one of them."); + } + + if (doprocessReco || doprocessRecoMC) { + AxisSpec nFwdAxis{axisNFwd, "#it{N}_{fwd} (FT0A+FT0C amplitude)"}; + if (nFwdAxis.nBins) { + nFwdAxis.makeLogarithmic(); + } + const AxisSpec countAxis{NCountBins, -0.5, NCountBins - 0.5, "stratum count"}; + const AxisSpec groupMonoAxis{NGroups * NMono1, -0.5, NGroups * NMono1 - 0.5, "group x monomial"}; + for (int h = 0; h < NHalves; ++h) { + bookSums(sumsReco[h], "accum", HalfName[h], nFwdAxis, NConfigs); + groupSums[h] = registry.add(std::string("groups/") + HalfName[h], "nominal first-order sums per block group", HistType::kTH3D, {nFwdAxis, countAxis, groupMonoAxis}); + groupSums[h]->SetBit(TH1::kIsNotW); + } + if (doprocessRecoMC && doSplitSums) { + bookSplit(splitReco, "split", nFwdAxis); + } + + // QA + constexpr int NFlowBins = NEventFlowBins; + auto hFlow = registry.add("qa/hEventFlow", "events", HistType::kTH1D, {{NFlowBins, -0.5, NFlowBins - 0.5}}); + for (int b = 0; b < NFlowBins; ++b) { + hFlow->GetXaxis()->SetBinLabel(b + 1, EventFlowLabels[b]); + } + registry.add("qa/hQaBits", "events with qaBits bit i set (order: QaBitOrder)", HistType::kTH1D, {{NQaBits, -0.5, NQaBits - 0.5}}); + registry.add("qa/hNFwd", "selected events", HistType::kTH1D, {nFwdAxis}); + AxisSpec ptAxis{100, PtMin, 20., "#it{p}_{T} (GeV/#it{c})"}; + ptAxis.makeLogarithmic(); + registry.add("qa/hTrackPt", "selected tracks", HistType::kTH1D, {ptAxis}); + registry.add("qa/hTrackEta", "selected tracks", HistType::kTH1D, {{160, -EtaMax, EtaMax, "#eta"}}); + registry.add("qa/hTrackPhi", "selected tracks", HistType::kTH1D, {{72, 0., o2::constants::math::TwoPI, "#varphi"}}); + // quantities: count, a_F, a_B, v_F, v_B + const AxisSpec quantityAxis{5, -0.5, 4.5, "quantity"}; + registry.add("qa/hMarginalSums", "nominal single-side sums", HistType::kTH3D, {nFwdAxis, {NCountBins, -0.5, NCountBins - 0.5, "n_{mid}"}, quantityAxis})->SetBit(TH1::kIsNotW); + registry.add("qa/hRunSums", "nominal single-side sums per run", HistType::kTH2D, {{axisRun, "run"}, quantityAxis})->SetBit(TH1::kIsNotW); + // quantities: count, a_F, a_B, a_F^2, a_B^2, v_F, v_B + const AxisSpec quantity2Axis{NCovariateQuantities, -0.5, NCovariateQuantities - 0.5, "quantity"}; + registry.add("qa/hOccupancySums", "nominal single-side sums vs occupancy", HistType::kTH2D, {{axisOccupancy, "track occupancy"}, quantity2Axis})->SetBit(TH1::kIsNotW); + registry.add("qa/hVtxZSums", "nominal single-side sums vs PV z", HistType::kTH2D, {{axisVtxZ, "PV z (cm)"}, quantity2Axis})->SetBit(TH1::kIsNotW); + registry.add("qa/hRateSums", "nominal single-side sums vs interaction rate", HistType::kTH2D, {{axisRate, "IR (kHz)"}, quantity2Axis})->SetBit(TH1::kIsNotW); + ccdb->setURL(ccdbUrl.value); + ccdb->setCaching(true); + ccdb->setFatalWhenNull(false); + } + + if (doprocessMC) { + AxisSpec nFwdGenAxis{axisNFwdGen, "#it{N}_{fwd} (charged primaries in FT0A+FT0C)"}; + if (nFwdGenAxis.nBins) { + nFwdGenAxis.makeLogarithmic(); + } + bookSums(sumsGen, "gen", "all", nFwdGenAxis, 1); // nominal configuration only + registry.add("gen/hEvents", "generated events", HistType::kTH1D, {{1, -0.5, 0.5}}); + if (doSplitSums) { + bookSplit(splitGen, "gen/split", nFwdGenAxis); + } + } + } + + /// Fills the sums of every valid configuration; returns the nominal observables + Nominal fillSums(Sums& sums, const Rings& rings, double nFwd, uint16_t qaBits, int nConfigs) + { + Nominal nominal; + std::array m{}; + std::array mPairFirst{}; + bool pairFirstValid = false; + double xF = 0., xB = 0., vF = 0., vB = 0.; + for (int c = 0; c < nConfigs; ++c) { + const auto& cfg = Configs[c]; + const uint16_t mask = EvVariantMask[cfg.evVariant]; + if ((qaBits & mask) != mask) { + continue; + } + if (rings.sum(kF, cfg.validEtaFirst, cfg.validPtEnd).n < MinTracksValid || rings.sum(kB, cfg.validEtaFirst, cfg.validPtEnd).n < MinTracksValid) { + continue; + } + const SubEvent fwd = rings.sum(kF, cfg.etaFirst, cfg.ptEnd); + const SubEvent bwd = rings.sum(kB, cfg.etaFirst, cfg.ptEnd); + int nCond = fwd.n + bwd.n; + if (cfg.cond == kCondAll) { + nCond = rings.count(0, PtEndNominal); + } else if (cfg.cond == kCondUnwindowed) { + nCond = rings.count(EtaFirstNominal, NPtRings); + } + observe(fwd, xF, vF); + observe(bwd, xB, vB); + monomials(xF, xB, vF, vB, NMono, m); + for (int k = 0; k < NMono; ++k) { + sums.config[c]->Fill(nFwd, nCond, k, m[k]); + } + sums.occupancy->Fill(c, nFwd, nCond); + if (c == IPairFirst) { + mPairFirst = m; + pairFirstValid = true; + } else if (c == IPairSecond && pairFirstValid) { + for (int k = 0; k < NMono1; ++k) { + for (int l = 0; l < NMono1; ++l) { + sums.paired->Fill(nFwd, nCond, k * NMono1 + l, mPairFirst[k] * m[l]); + } + } + } + if (c == INominal) { + nominal = {.valid = true, .nFwd = nFwd, .nMid = nCond, .xF = xF, .xB = xB, .vF = vF, .vB = vB}; + } + } + + // (n_F, n_B) strata, n_F, n_B >= 1 + const uint16_t maskNominal = EvVariantMask[Configs[INominal].evVariant]; + const SubEvent fwd = rings.sum(kF, EtaFirstNominal, PtEndNominal); + const SubEvent bwd = rings.sum(kB, EtaFirstNominal, PtEndNominal); + if ((qaBits & maskNominal) == maskNominal && fwd.n > 0 && bwd.n > 0) { + observe(fwd, xF, vF); + observe(bwd, xB, vB); + monomials(xF, xB, vF, vB, NMono1, m); + std::array coord = {nFwd, static_cast(fwd.n), static_cast(bwd.n), 0.}; + for (int k = 0; k < NMono1; ++k) { + coord[3] = k; + sums.strata3->Fill(coord.data(), m[k]); + } + } + return nominal; + } + + /// Fills the reconstructed-level sums; returns the nominal observables + template + Nominal fillReco(TCollision const& coll, MyTracks const& tracks) + { + registry.fill(HIST("qa/hEventFlow"), kFlowAll); + if (!coll.sel8()) { + return {}; + } + registry.fill(HIST("qa/hEventFlow"), kFlowSel8); + if (std::abs(coll.posZ()) > vtxZCut) { + return {}; + } + registry.fill(HIST("qa/hEventFlow"), kFlowVtxZ); + + uint16_t qaBits = 0; + for (int i = 0; i < NQaBits; ++i) { + if (coll.selection_bit(QaBitOrder[i])) { + qaBits |= static_cast(1u << i); + registry.fill(HIST("qa/hQaBits"), i); + } + } + if ((qaBits & EvVariantMask[kV1]) == EvVariantMask[kV1]) { + registry.fill(HIST("qa/hEventFlow"), kFlowPileupV1); + } + + Rings rings; + for (const auto& track : tracks) { + rings.add(track.eta(), track.pt()); + registry.fill(HIST("qa/hTrackPt"), track.pt()); + registry.fill(HIST("qa/hTrackEta"), track.eta()); + registry.fill(HIST("qa/hTrackPhi"), track.phi()); + } + + const auto& bc = coll.template bc_as(); + const double nFwd = coll.multFT0A() + coll.multFT0C(); + registry.fill(HIST("qa/hNFwd"), nFwd); + const int half = static_cast(blockHash(bc.runNumber(), bc.globalBC(), HalfBlockShift) & 1u); + const Nominal nominal = fillSums(sumsReco[half], rings, nFwd, qaBits, NConfigs); + if (!nominal.valid) { + return nominal; + } + registry.fill(HIST("qa/hEventFlow"), kFlowNominalValid); + registry.fill(HIST("qa/hEventFlow"), kFlowHalfA + half); + + // subsample groups + const int group = static_cast(blockHash(bc.runNumber(), bc.globalBC(), GroupBlockShift) % NGroups); + std::array m{}; + monomials(nominal.xF, nominal.xB, nominal.vF, nominal.vB, NMono1, m); + for (int k = 0; k < NMono1; ++k) { + groupSums[half]->Fill(nFwd, nominal.nMid, group * NMono1 + k, m[k]); + } + + const std::array quantities = {1., nominal.xF + MeanPtShift, nominal.xB + MeanPtShift, nominal.vF, nominal.vB}; + for (int q = 0; q < static_cast(quantities.size()); ++q) { + registry.fill(HIST("qa/hMarginalSums"), nFwd, nominal.nMid, q, quantities[q]); + registry.fill(HIST("qa/hRunSums"), bc.runNumber(), q, quantities[q]); + } + const double rate = irSource.value.empty() ? -1. : rateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource.value) * 1.e-3; + const std::array covariateQuantities = {1., quantities[1], quantities[2], quantities[1] * quantities[1], quantities[2] * quantities[2], nominal.vF, nominal.vB}; + for (int q = 0; q < NCovariateQuantities; ++q) { + registry.fill(HIST("qa/hOccupancySums"), coll.trackOccupancyInTimeRange(), q, covariateQuantities[q]); + registry.fill(HIST("qa/hVtxZSums"), coll.posZ(), q, covariateQuantities[q]); + registry.fill(HIST("qa/hRateSums"), rate, q, covariateQuantities[q]); + } + return nominal; + } + + void processReco(MyCollisions::iterator const& coll, MyTracks const& tracks, aod::BCsWithTimestamps const&) + { + fillReco(coll, tracks); + } + PROCESS_SWITCH(EventShapeCoex, processReco, "Reconstructed-level moment sums", true); + + void processRecoMC(MyCollisionsMc::iterator const& coll, MyTracks const& tracks, aod::McCollisions const&, aod::BCsWithTimestamps const&) + { + const Nominal nominal = fillReco(coll, tracks); + if (!nominal.valid || !doSplitSums) { + return; + } + if (!coll.has_mcCollision()) { + registry.fill(HIST("qa/hEventFlow"), kFlowNoMcLabel); + return; + } + const auto& bc = coll.mcCollision().bc_as(); + fillSplit(splitReco, nominal, bc.runNumber(), bc.globalBC()); + } + PROCESS_SWITCH(EventShapeCoex, processRecoMC, "Reconstructed-level moment sums on MC (MC-labelled collisions)", false); + + void processMC(aod::McCollision const& mcCollision, aod::McParticles const& particles, aod::BCs const&) + { + static constexpr double ChargeTolerance = 1.e-3; + + registry.fill(HIST("gen/hEvents"), 0); + uint32_t nFwdA = 0, nFwdC = 0; + Rings rings; + for (const auto& particle : particles) { + if (!particle.isPhysicalPrimary()) { + continue; + } + const auto* pdgParticle = pdg->GetParticle(particle.pdgCode()); + if (pdgParticle == nullptr || std::abs(pdgParticle->Charge()) < ChargeTolerance) { + continue; // unknown PDG code or neutral + } + const float eta = particle.eta(); + // forward counts: no pT window + if (eta > etaFwdAMin && eta < etaFwdAMax) { + ++nFwdA; + } + if (eta > etaFwdCMin && eta < etaFwdCMax) { + ++nFwdC; + } + if (particle.pt() <= PtMin || std::abs(eta) >= EtaMax) { + continue; + } + rings.add(eta, particle.pt()); + } + const Nominal nominal = fillSums(sumsGen, rings, nFwdA + nFwdC, AllQaBits, 1); + if (nominal.valid && doSplitSums) { + const auto& bc = mcCollision.bc_as(); + fillSplit(splitGen, nominal, bc.runNumber(), bc.globalBC()); + } + } + PROCESS_SWITCH(EventShapeCoex, processMC, "Generator-level moment sums (nominal configuration)", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/TableProducer/Nuspex/decay3bodybuilder.cxx b/PWGLF/TableProducer/Nuspex/decay3bodybuilder.cxx index 7466a973bcb..3e58c303ed4 100644 --- a/PWGLF/TableProducer/Nuspex/decay3bodybuilder.cxx +++ b/PWGLF/TableProducer/Nuspex/decay3bodybuilder.cxx @@ -1356,10 +1356,10 @@ struct decay3bodyBuilder { } if (decay3bodyBuilderOpts.useKFParticle) { - Binning3BodyKF binningOnRadPhiKF{{mixingOpts.bins3BodyRadius, mixingOpts.bins3BodyPhi}, true}; + Binning3BodyKF binningOnRadPhiKF{{mixingOpts.bins3BodyRadius, mixingOpts.bins3BodyPhi}}; buildMixedCandidates(decay3bodys, binningOnRadPhiKF); } else { - Binning3BodyDCAfitter binningOnRadPhiDCA{{mixingOpts.bins3BodyRadius, mixingOpts.bins3BodyPhi}, true}; + Binning3BodyDCAfitter binningOnRadPhiDCA{{mixingOpts.bins3BodyRadius, mixingOpts.bins3BodyPhi}}; buildMixedCandidates(decay3bodys, binningOnRadPhiDCA); } } diff --git a/PWGLF/TableProducer/Nuspex/deuteronInTriggeredEvents.cxx b/PWGLF/TableProducer/Nuspex/deuteronInTriggeredEvents.cxx index 9d855c71fda..59b015ffb81 100644 --- a/PWGLF/TableProducer/Nuspex/deuteronInTriggeredEvents.cxx +++ b/PWGLF/TableProducer/Nuspex/deuteronInTriggeredEvents.cxx @@ -40,7 +40,9 @@ #include "Common/Core/ZorroSummary.h" #include "Common/Core/trackUtilities.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseITS.h" #include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" #include @@ -266,11 +268,15 @@ struct DeuteronInTriggeredEvents { Produces nucleiTable; // For data Produces nucleiTableMCExtension; // For MC analysis Produces genEventMCSel; // For MC reco events + Produces nucleiTableMC; // For MC analysis as data Service ccdb; Service pdgDB; // For INELgt0 gen MC selection Zorro zorro; // Definition of Zorro: helpful for skimmed data OutputObj zorroSummary{"zorroSummary"}; + // Initialize ITS PID Response object + o2::aod::ITSResponse itsResponse; + Configurable cfgCompensatePIDinTracking{"cfgCompensatePIDinTracking", false, "If true, divide tpcInnerParam by the electric charge"}; struct : o2::framework::ConfigurableGroup { @@ -286,6 +292,12 @@ struct DeuteronInTriggeredEvents { Configurable tpcNClsMin{"tpcNClsMin", 80, "Minimum number of TPC clusters"}; Configurable tpcRigidityMin{"tpcRigidityMin", 0.5f, "Minimum TPC rigidity for tracks"}; Configurable> tpcNSigmaMax{"tpcNSigmaMax", {nuclei::NSigmaTPCdefault[0], 5, 2, nuclei::names, nuclei::nSigmaConfigName}, "TPC nsigma selection for light nuclei"}; + // Part related to ITS cut + Configurable applyItsPid{"applyItsPid", false, "Apply ITS PID"}; + Configurable setMCDefaultParams{"setMCDefaultParams", false, "Use params in MC ITS PID"}; + Configurable ptMaxItsPid{"ptMaxItsPid", 1.0, "Max pt for ITS PID"}; + Configurable nSigmaItsMin{"nSigmaItsMin", -3.0, "Min nsigma in ITS"}; + Configurable nSigmaItsMax{"nSigmaItsMax", 3.0, "Max nsigma in ITS"}; } cfgTrackCut; Configurable cfgCutPtMinTree{"cfgCutPtMinTree", 0.2f, "Minimum track transverse momentum for tree saving"}; @@ -302,6 +314,8 @@ struct DeuteronInTriggeredEvents { Configurable cfgFillGenSecondaries{"cfgFillGenSecondaries", 0, "Fill generated secondaries (0: no, 1: only weak decays, 2: all of them)"}; Configurable> cfgDCAHists{"cfgDCAHists", {nuclei::DCAHistDefault[0], 5, 2, nuclei::names, nuclei::dcaConfigNames}, "DCA hist configuration"}; + Configurable cfgRemoveTOFtrack{"cfgRemoveTOFtrack", false, "Remove TOF tracks with TPC PID incorrect"}; + Configurable cfgNsigmaTPCvetoLF{"cfgNsigmaTPCvetoLF", 3.f, "TPC nsigma cut for tracks compatible with light flavor hadrons"}; Configurable cfgNsigmaTPCcutDCAhists{"cfgNsigmaTPCcutDCAhists", 3., "TPC nsigma cut for DCA hists"}; Configurable cfgDeltaTOFmassCutDCAhists{"cfgDeltaTOFmassCutDCAhists", 0.2, "Delta TOF mass cut for DCA hists"}; Configurable cfgNsigmaTPCcutTOFhists{"cfgNsigmaTPCcutTOFhists", 3., "TPC nsigma cut for TOF analysis"}; @@ -350,7 +364,7 @@ struct DeuteronInTriggeredEvents { // Utility object for jet background subtraction methods JetBkgSubUtils backgroundSub; - using TrackCandidates = soa::Join; + using TrackCandidates = soa::Join; HistogramRegistry spectra{"spectra", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; @@ -472,6 +486,11 @@ struct DeuteronInTriggeredEvents { ccdb->setLocalObjectValidityChecking(); ccdb->setFatalWhenNull(false); + // Set default MC parametrization for ITS response + if (cfgTrackCut.setMCDefaultParams) { + itsResponse.setMCDefaultParameters(); + } + const AxisSpec nSigmaAxes[2]{{cfgNsigmaTPCbins, "n#sigma_{TPC}"}, {cfgNsigmaTOFbins, "n#sigma_{TOF}"}}; const AxisSpec tofMassAxis{cfgTOFmassBins, "TOF mass - PDG mass"}; const AxisSpec ptResAxis{cfgMomResBins, "#Delta#it{p}_{T}/#it{p}_{T}"}; @@ -524,6 +543,7 @@ struct DeuteronInTriggeredEvents { spectra.add("hTpcSignalData", "Specific energy loss", HistType::kTH2F, {{600, -6., 6., "#it{p} (GeV/#it{c})"}, {1400, 0, 1400, "d#it{E} / d#it{X} (a. u.)"}}); spectra.add("hTpcSignalDataSelected", "Specific energy loss for selected particles", HistType::kTH2F, {{600, -6., 6., "#it{p} (GeV/#it{c})"}, {1400, 0, 1400, "d#it{E} / d#it{X} (a. u.)"}}); spectra.add("hTofSignalData", "TOF beta", HistType::kTH2F, {{500, 0., 5., "#it{p} (GeV/#it{c})"}, {750, 0, 1.5, "TOF #beta"}}); + spectra.add("hItsNsigmaDeuteron", "n#sigma_{ITS}", HistType::kTH2F, {{1000, 0., 5., "#it{p}_{T} (GeV/#it{c})"}, {600, -6., 6., "n#sigma_{ITS}"}}); for (unsigned int iC{0}; iC < nuclei::matter.size(); ++iC) { nuclei::hGloTOFtracks[iC] = spectra.add(fmt::format("hTPCTOFtracks{}", nuclei::matter[iC]).data(), fmt::format("Global vs TOF matched {} tracks in a collision", nuclei::chargeLabelNames[iC]).data(), HistType::kTH2D, {{300, -0.5, 300.5, "Number of global tracks"}, {300, -0.5, 300.5, "Number of TOF matched tracks"}}); @@ -780,7 +800,16 @@ struct DeuteronInTriggeredEvents { track.itsChi2NCl() > cfgTrackCut.itsChi2ClusMax) { continue; } - // temporary fix: tpcInnerParam() returns the momentum in all the software tags before --- CM: what does it mean + + double nSigmaIts = static_cast(itsResponse.nSigmaITS(track)); + spectra.fill(HIST("hItsNsigmaDeuteron"), track.pt(), nSigmaIts); + + // ITS Logic for PID Selection + if (cfgTrackCut.applyItsPid && track.pt() < cfgTrackCut.ptMaxItsPid && (nSigmaIts < cfgTrackCut.nSigmaItsMin || nSigmaIts > cfgTrackCut.nSigmaItsMax)) { + continue; + } + + // temporary fix: tpcInnerParam() returns the momentum in all the software tags before bool heliumPID = track.pidForTracking() == o2::track::PID::Helium3 || track.pidForTracking() == o2::track::PID::Alpha; float correctedTpcInnerParam = (heliumPID && cfgCompensatePIDinTracking) ? track.tpcInnerParam() / 2 : track.tpcInnerParam(); @@ -798,6 +827,7 @@ struct DeuteronInTriggeredEvents { bool selectedTPC[5]{false}, goodToAnalyse{false}; std::array nSigmaTPC; + // This part apply a selection on tracks: // if the nsigma of the track is less than a certain value, it is accepted for (int iS{0}; iS < nuclei::Species; ++iS) { // The first index here is set to 0 because it is TPC analysis @@ -843,6 +873,15 @@ struct DeuteronInTriggeredEvents { flag |= kITSrof; } + // Part for identifying tracks which are compatible to proton, kaons and pions + bool isCompatibleWithLF{false}; + double nSigmaTpcPion = track.tpcNSigmaPi(); + double nSigmaTpcKaon = track.tpcNSigmaKa(); + double nSigmaTpcProt = track.tpcNSigmaPr(); + + if (cfgRemoveTOFtrack && (std::abs(nSigmaTpcPion) < cfgNsigmaTPCvetoLF || std::abs(nSigmaTpcKaon) < cfgNsigmaTPCvetoLF || std::abs(nSigmaTpcProt) < cfgNsigmaTPCvetoLF)) + isCompatibleWithLF = true; + for (int iS{0}; iS < nuclei::Species; ++iS) { bool selectedTOF{false}; if (std::abs(dcaInfo[1]) > cfgTrackCut.dcaMax->get(iS, 1)) { @@ -869,7 +908,7 @@ struct DeuteronInTriggeredEvents { nuclei::hNsigma[iPID][iS][iC]->Fill(fvector.pt(), nSigma[iPID][iS]); nuclei::hNsigmaEta[iPID][iS][iC]->Fill(fvector.eta(), fvector.pt(), nSigma[iPID][iS]); } - if (iPID && std::abs(nSigmaTPC[iS]) < cfgNsigmaTPCcutTOFhists) { // Apply nsigmaTPC cut on TOF tracks + if (iPID && std::abs(nSigmaTPC[iS]) < cfgNsigmaTPCcutTOFhists && !isCompatibleWithLF) { // Apply nsigmaTPC cut on TOF tracks nuclei::hTOFmass[iS][iC]->Fill(fvector.pt(), tofMasses[iS]); nuclei::hTOFmassEta[iS][iC]->Fill(fvector.eta(), fvector.pt(), tofMasses[iS]); } @@ -993,7 +1032,7 @@ struct DeuteronInTriggeredEvents { std::vector isReconstructed(particlesMC.size(), false); for (auto& c : nuclei::candidates) { // o2-linter: disable=const-ref-in-for-loop (candidate is modified in loop) auto label = tracks.iteratorAt(c.globalIndex); - if (label.mcParticleId() < -1 || label.mcParticleId() >= particlesMC.size()) { + if (label.mcParticleId() < 0 || label.mcParticleId() >= particlesMC.size()) { continue; } auto particle = particlesMC.iteratorAt(label.mcParticleId()); @@ -1001,7 +1040,7 @@ struct DeuteronInTriggeredEvents { for (int iS{0}; iS < nuclei::Species; ++iS) { if (std::abs(particle.pdgCode()) == nuclei::Codes[iS]) { if (c.fillTree && !storeIt) { - nuclei::hMomRes[iS][particle.pdgCode() < 0]->Fill(1., std::abs(c.pt * nuclei::Charges[iS]), 1. - std::abs(c.pt * nuclei::Charges[iS]) / particle.pt()); + nuclei::hMomRes[iS][particle.pdgCode() < 0]->Fill(std::abs(c.pt * nuclei::Charges[iS]), 1. - std::abs(c.pt * nuclei::Charges[iS]) / particle.pt()); storeIt = cfgTreeConfig->get(iS, 0u) || cfgTreeConfig->get(iS, 1u); /// store only the particles of interest } auto coll = collisions.iteratorAt(c.collTrackIndex); @@ -1070,7 +1109,7 @@ struct DeuteronInTriggeredEvents { float motherDecRadius = -1; if (particle.isPhysicalPrimary()) { flags |= kIsPhysicalPrimary; - nuclei::hGenNuclei[iS][particle.pdgCode() < 0]->Fill(1., particle.pt()); + nuclei::hGenNuclei[iS][particle.pdgCode() < 0]->Fill(particle.pt()); // antinuclei from B hadrons are classified as physical primaries if (particle.has_mothers()) { for (const auto& motherparticle : particle.mothers_as()) { @@ -1111,6 +1150,77 @@ struct DeuteronInTriggeredEvents { } } PROCESS_SWITCH(DeuteronInTriggeredEvents, processMC, "MC analysis", false); + + void processMCasData(soa::Join const& collisions, aod::McCollisions const& mcCollisions, soa::Join const& tracks, aod::McParticles const& particlesMC, aod::BCsWithTimestamps const&) + { + nuclei::candidates.clear(); + std::vector goodCollisions(mcCollisions.size(), false); + + for (const auto& collision : collisions) { + if (!eventSelectionWithHisto(collision)) { + continue; + } + + // Avoid unwanted memory leaks + if (!collision.has_mcCollision()) + continue; + + int mcId = collision.mcCollisionId(); + if (mcId < 0 || mcId >= static_cast(mcCollisions.size())) + continue; + + goodCollisions[collision.mcCollisionId()] = true; + const auto& slicedTracks = tracks.sliceBy(tracksPerCollisions, collision.globalIndex()); + fillDataInfo(collision, slicedTracks); + } + + std::vector isReconstructed(particlesMC.size(), false); + for (size_t i{0}; i < nuclei::candidates.size(); ++i) { + auto& c = nuclei::candidates[i]; + if (c.fillTree) { + auto label = tracks.iteratorAt(c.globalIndex); + + if (label.mcParticleId() < 0 || label.mcParticleId() >= particlesMC.size()) { + continue; + } + + auto particle = particlesMC.iteratorAt(label.mcParticleId()); + + int motherPdgCode = 0; + float motherDecRadius = -1; + isReconstructed[particle.globalIndex()] = true; + + if (particle.isPhysicalPrimary()) { + c.flags |= kIsPhysicalPrimary; + if (particle.has_mothers()) { + for (const auto& motherparticle : particle.mothers_as()) { + if (std::find(nuclei::hfMothCodes.begin(), nuclei::hfMothCodes.end(), std::abs(motherparticle.pdgCode())) != nuclei::hfMothCodes.end()) { + c.flags |= kIsSecondaryFromWeakDecay; + motherPdgCode = motherparticle.pdgCode(); + motherDecRadius = std::hypot(particle.vx() - motherparticle.vx(), particle.vy() - motherparticle.vy()); + break; + } + } + } + } else if (particle.getProcess() == TMCProcess::kPDecay) { + c.flags |= kIsSecondaryFromWeakDecay; + for (const auto& motherparticle : particle.mothers_as()) { + motherPdgCode = motherparticle.pdgCode(); + motherDecRadius = std::hypot(particle.vx() - motherparticle.vx(), particle.vy() - motherparticle.vy()); + } + } else { + c.flags |= kIsSecondaryFromMaterial; + } + + isReconstructed[particle.globalIndex()] = true; + float absoDecL = computeAbsoDecL(particle); + + nucleiTableMC(c.pt, c.eta, c.phi, c.tpcInnerParam, c.beta, c.zVertex, c.nContrib, c.dcaXY, c.dcaZ, c.tpcSignal, c.itsChi2, c.tpcChi2, c.tofChi2, c.flags, c.tpcFindableCls, c.tpcCrossedRows, c.itsClsMap, c.tpcNCls, c.tpcNClsShared, c.clusterSizesITS, goodCollisions[particle.mcCollisionId()], particle.pt(), particle.eta(), particle.phi(), particle.pdgCode(), motherPdgCode, motherDecRadius, absoDecL); + } + } + } + + PROCESS_SWITCH(DeuteronInTriggeredEvents, processMCasData, "MC as data analysis", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGLF/TableProducer/Nuspex/he3HadronFemto.cxx b/PWGLF/TableProducer/Nuspex/he3HadronFemto.cxx index d30182f35dc..c90bc73c6cb 100644 --- a/PWGLF/TableProducer/Nuspex/he3HadronFemto.cxx +++ b/PWGLF/TableProducer/Nuspex/he3HadronFemto.cxx @@ -386,7 +386,7 @@ struct he3HadronFemto { ConfigurableAxis axisVertex{"axisVertex", {30, -10, 10}, "Binning for multiplicity"}; ConfigurableAxis axisCentrality{"axisCentrality", {40, 0, 100}, "Binning for centrality"}; using BinningType = ColumnBinningPolicy; - BinningType binningPolicy{{axisVertex, axisCentrality}, true}; + BinningType binningPolicy{{axisVertex, axisCentrality}}; SliceCache cache; SameKindPair mPair{binningPolicy, settingNoMixedEvents, -1, &cache}; diff --git a/PWGLF/TableProducer/Nuspex/he3LambdaAnalysis.cxx b/PWGLF/TableProducer/Nuspex/he3LambdaAnalysis.cxx index 01411e4e566..293733ad9c5 100644 --- a/PWGLF/TableProducer/Nuspex/he3LambdaAnalysis.cxx +++ b/PWGLF/TableProducer/Nuspex/he3LambdaAnalysis.cxx @@ -56,7 +56,7 @@ using namespace o2::framework::expressions; using namespace o2::constants::physics; namespace { -constexpr double betheBlochDefault[1][6]{{-1.e32, -1.e32, -1.e32, -1.e32, -1.e32, -1.e32}}; +constexpr std::array betheBlochDefault[1]{{-1.e32, -1.e32, -1.e32, -1.e32, -1.e32, -1.e32}}; static const std::vector betheBlochParNames{"p0", "p1", "p2", "p3", "p4", "resolution"}; static const std::vector particleName{"He3"}; o2::base::MatLayerCylSet* matLUT = nullptr; @@ -101,6 +101,9 @@ struct he3LambdaAnalysis { Produces lfHe3V0Collision; Produces lfHe3; Produces lfLambda; + Produces lfK0s; + + Configurable cfgStoreK0s{"cfgStoreK0s", false, "Select and store K0s candidates, including He3-K0s events without a selected Lambda"}; // Configurables for event selection struct : ConfigurableGroup { @@ -122,7 +125,7 @@ struct he3LambdaAnalysis { Configurable dcazMax{"dcazMax", 0.5f, "Maximum He3 DCA z"}; Configurable tpcClusMin{"tpcClusMin", 100, "Minimum He3 TPC clusters"}; Configurable itsClusMin{"itsClusMin", 5, "Minimum He3 ITS clusters"}; - Configurable> betheBlochParams{"betheBlochParams", {betheBlochDefault[0], 1, 6, particleName, betheBlochParNames}, "TPC Bethe-Bloch parameterisation for He3"}; + Configurable> betheBlochParams{"betheBlochParams", {betheBlochDefault[0].data(), 1, 6, particleName, betheBlochParNames}, "TPC Bethe-Bloch parameterisation for He3"}; } cfgHe3; // Lambda selection criteria @@ -131,6 +134,8 @@ struct he3LambdaAnalysis { Configurable ptMin{"ptMin", 0.5f, "Minimum Lambda pT"}; Configurable ptMax{"ptMax", 10.0f, "Maximum Lambda pT"}; Configurable massWindow{"massWindow", 0.015f, "Lambda mass window"}; + Configurable ctMin{"ctMin", 0.f, "Minimum Lambda proper decay length (cm)"}; + Configurable ctMax{"ctMax", 30.f, "Maximum Lambda proper decay length (cm)"}; Configurable cosPAMin{"cosPAMin", 0.99f, "Minimum Lambda cosPA"}; Configurable dcaV0DaughtersMax{"dcaV0DaughtersMax", 0.5f, "Maximum Lambda DCA V0 daughters"}; Configurable v0RadiusMin{"v0RadiusMin", 0.5f, "Minimum Lambda V0 radius"}; @@ -140,6 +145,22 @@ struct he3LambdaAnalysis { Configurable pionNSigmaTPCMax{"pionNSigmaTPCMax", 4.0f, "Maximum pion TPC nSigma"}; } cfgLambda; + // K0s selection criteria + struct : ConfigurableGroup { + std::string prefix = "cfgK0s"; + Configurable ptMin{"ptMin", 0.5f, "Minimum K0s pT"}; + Configurable ptMax{"ptMax", 10.f, "Maximum K0s pT"}; + Configurable massWindow{"massWindow", 0.015f, "K0s mass window"}; + Configurable ctMin{"ctMin", 0.f, "Minimum K0s proper decay length (cm)"}; + Configurable ctMax{"ctMax", 20.f, "Maximum K0s proper decay length (cm)"}; + Configurable cosPAMin{"cosPAMin", 0.99f, "Minimum K0s cosPA"}; + Configurable dcaV0DaughtersMax{"dcaV0DaughtersMax", 0.5f, "Maximum K0s DCA V0 daughters"}; + Configurable v0RadiusMin{"v0RadiusMin", 0.5f, "Minimum K0s V0 radius"}; + Configurable v0RadiusMax{"v0RadiusMax", 35.f, "Maximum K0s V0 radius"}; + Configurable tpcNClsMin{"tpcNClsMin", 70, "Minimum TPC clusters for K0s daughters"}; + Configurable pionNSigmaTPCMax{"pionNSigmaTPCMax", 4.f, "Maximum pion TPC nSigma"}; + } cfgK0s; + // Pair selection criteria struct : ConfigurableGroup { std::string prefix = "cfgPair"; @@ -156,7 +177,7 @@ struct he3LambdaAnalysis { Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; } ccdbOptions; - std::array mBBparamsHe; + std::array mBBparamsHe = betheBlochDefault[0]; float mBz = 0.0f; // Magnetic field in T HistogramRegistry mRegistry{"He3LambdaAnalysis"}; int mRunNumber = 0; // Current run number @@ -186,7 +207,7 @@ struct he3LambdaAnalysis { zorroSummary.setObject(zorro.getZorroSummary()); mRegistry.add("hEventSelection", "Event Selection", {HistType::kTH1L, {{6, -.5, 5.5}}}); - std::vector labels{"Total Events", "Sel8 Events", "Z-Vertex OK", "Additional Event Selections", "He3 Candidates Found", "He3 and Lambda Candidates Found"}; + std::vector labels{"Total Events", "Sel8 Events", "Z-Vertex OK", "Additional Event Selections", "He3 Candidates Found", "He3 and V0 Candidates Found"}; for (size_t i = 1; i <= labels.size(); ++i) { mRegistry.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(i, labels[i - 1].c_str()); } @@ -203,6 +224,8 @@ struct he3LambdaAnalysis { hArmenterosPodolanskiAll = mRegistry.add("hArmenterosPodolanskiAll", "Armenteros-Podolanski All", {HistType::kTH2D, {{100, -1., 1.}, {100, 0., 0.5}}}); hArmenterosPodolanskiSelected = mRegistry.add("hArmenterosPodolanskiSelected", "Armenteros-Podolanski Selected", {HistType::kTH2D, {{100, -1., 1.}, {100, 0., 0.5}}}); + mRegistry.add("hMassK0s", "Selected K0s;pT (GeV/c);mass (GeV/c^{2})", {HistType::kTH2F, {{100, 0., 10.}, {100, MassK0Short - 0.025, MassK0Short + 0.025}}}); + constexpr double ConstituentsMass = o2::constants::physics::MassProton + o2::constants::physics::MassNeutron * 2 + o2::constants::physics::MassSigmaPlus; hInvariantMassUS = mRegistry.add("hInvariantMassUS", "Invariant Mass", {HistType::kTH2D, {{45, 1., 10}, {100, ConstituentsMass - 0.05, ConstituentsMass + 0.05}}}); hInvariantMassLS = mRegistry.add("hInvariantMassLS", "Invariant Mass", {HistType::kTH2D, {{45, 1., 10}, {100, ConstituentsMass - 0.05, ConstituentsMass + 0.05}}}); @@ -298,8 +321,9 @@ struct he3LambdaAnalysis { } mRegistry.get(HIST("hEventSelection"))->Fill(4); // He3 candidates found - // Process Lambda candidates + // Fit each V0 once and evaluate the K0s and Lambda hypotheses independently. std::vector lambdaCandidates; + std::vector k0sCandidates; for (auto const& v0 : v0s) { if (v0.v0Type() != 1) { continue; @@ -307,7 +331,9 @@ struct he3LambdaAnalysis { const auto posTrack = v0.posTrack_as(); const auto negTrack = v0.negTrack_as(); - if (posTrack.tpcNClsFound() < cfgLambda.tpcNClsMin || negTrack.tpcNClsFound() < cfgLambda.tpcNClsMin) { + const bool lambdaTrackQuality = posTrack.tpcNClsFound() >= cfgLambda.tpcNClsMin && negTrack.tpcNClsFound() >= cfgLambda.tpcNClsMin; + const bool k0sTrackQuality = cfgStoreK0s && posTrack.tpcNClsFound() >= cfgK0s.tpcNClsMin && negTrack.tpcNClsFound() >= cfgK0s.tpcNClsMin; + if (!lambdaTrackQuality && !k0sTrackQuality) { continue; // Skip V0s with insufficient TPC clusters } auto trackParPos = getTrackParCov(posTrack); @@ -332,6 +358,49 @@ struct he3LambdaAnalysis { float qt = qtAP(momV0, momPos); hArmenterosPodolanskiAll->Fill(alpha, qt); + const auto sv = fitter.getPCACandidate(0); + const float decayLength = std::hypot(sv[0] - collVtx.x(), sv[1] - collVtx.y(), sv[2] - collVtx.z()); + const float momentum = std::hypot(momV0[0], momV0[1], momV0[2]); + if (momentum <= 0.f || decayLength <= 0.f) { + continue; + } + const float cosPA = ((sv[0] - collVtx.x()) * momV0[0] + (sv[1] - collVtx.y()) * momV0[1] + (sv[2] - collVtx.z()) * momV0[2]) / (decayLength * momentum); + const float dcaDaughters = std::sqrt(fitter.getChi2AtPCACandidate(0)); + const float radius = std::hypot(sv[0], sv[1]); + std::array dcaInfoPos{}, dcaInfoNeg{}; + if (!o2::base::Propagator::Instance()->propagateToDCA(collVtx, trackParPos, mBz, 2.f, o2::base::Propagator::MatCorrType::USEMatCorrLUT, &dcaInfoPos) || + !o2::base::Propagator::Instance()->propagateToDCA(collVtx, trackParNeg, mBz, 2.f, o2::base::Propagator::MatCorrType::USEMatCorrLUT, &dcaInfoNeg)) { + continue; + } + + ROOT::Math::LorentzVector> posPionMom4D(momPos[0], momPos[1], momPos[2], MassPionCharged); + ROOT::Math::LorentzVector> negPionMom4D(momNeg[0], momNeg[1], momNeg[2], MassPionCharged); + const auto k0sMom4D = posPionMom4D + negPionMom4D; + const float ctK0s = decayLength * MassK0Short / momentum; + if (k0sTrackQuality && std::abs(posTrack.tpcNSigmaPi()) <= cfgK0s.pionNSigmaTPCMax && std::abs(negTrack.tpcNSigmaPi()) <= cfgK0s.pionNSigmaTPCMax && + std::abs(k0sMom4D.M() - MassK0Short) <= cfgK0s.massWindow && k0sMom4D.Pt() >= cfgK0s.ptMin && k0sMom4D.Pt() <= cfgK0s.ptMax && + ctK0s >= cfgK0s.ctMin && ctK0s <= cfgK0s.ctMax && cosPA >= cfgK0s.cosPAMin && dcaDaughters <= cfgK0s.dcaV0DaughtersMax && + radius >= cfgK0s.v0RadiusMin && radius <= cfgK0s.v0RadiusMax) { + k0sCandidate candidate; + candidate.momentum.SetCoordinates(k0sMom4D.Pt(), k0sMom4D.Eta(), k0sMom4D.Phi(), MassK0Short); + candidate.mass = k0sMom4D.M(); + candidate.ct = ctK0s; + candidate.cosPA = cosPA; + candidate.dcaV0Daughters = dcaDaughters; + candidate.dcaPosToPV = std::hypot(dcaInfoPos[0], dcaInfoPos[1]); + candidate.dcaNegToPV = std::hypot(dcaInfoNeg[0], dcaInfoNeg[1]); + candidate.v0Radius = radius; + candidate.posPionNSigmaTPC = posTrack.tpcNSigmaPi(); + candidate.negPionNSigmaTPC = negTrack.tpcNSigmaPi(); + k0sCandidates.push_back(candidate); + mRegistry.fill(HIST("hMassK0s"), k0sMom4D.Pt(), candidate.mass); + } + + const float ctLambda = decayLength * MassLambda0 / momentum; + if (!lambdaTrackQuality || ctLambda < cfgLambda.ctMin || ctLambda > cfgLambda.ctMax || cosPA < cfgLambda.cosPAMin || + dcaDaughters > cfgLambda.dcaV0DaughtersMax || radius < cfgLambda.v0RadiusMin || radius > cfgLambda.v0RadiusMax) { + continue; + } bool matter = alpha > 0; const auto& protonTrack = matter ? posTrack : negTrack; const auto& pionTrack = matter ? negTrack : posTrack; @@ -347,37 +416,30 @@ struct he3LambdaAnalysis { auto lambdaMom4D = protonMom4D + pionMom4D; float massLambda = lambdaMom4D.M(); - if (std::abs(massLambda - o2::constants::physics::MassLambda0) > cfgLambda.massWindow) { + if (std::abs(massLambda - o2::constants::physics::MassLambda0) > cfgLambda.massWindow || lambdaMom4D.Pt() < cfgLambda.ptMin || lambdaMom4D.Pt() > cfgLambda.ptMax) { continue; // Skip V0s outside mass window } hArmenterosPodolanskiSelected->Fill(alpha, qt); - std::array dcaInfoProton, dcaInfoPion; - o2::base::Propagator::Instance()->propagateToDCA(collVtx, matter ? trackParPos : trackParNeg, mBz, 2.f, o2::base::Propagator::MatCorrType::USEMatCorrLUT, &dcaInfoProton); - o2::base::Propagator::Instance()->propagateToDCA(collVtx, matter ? trackParNeg : trackParPos, mBz, 2.f, o2::base::Propagator::MatCorrType::USEMatCorrLUT, &dcaInfoPion); - - const auto sv = fitter.getPCACandidate(0); - + const auto& dcaInfoProton = matter ? dcaInfoPos : dcaInfoNeg; + const auto& dcaInfoPion = matter ? dcaInfoNeg : dcaInfoPos; lambdaCandidate candidate; - candidate.momentum.SetCoordinates(lambdaMom4D.Pt(), lambdaMom4D.Eta(), lambdaMom4D.Phi(), o2::constants::physics::MassLambda0); + candidate.momentum.SetCoordinates(lambdaMom4D.Pt(), lambdaMom4D.Eta(), lambdaMom4D.Phi(), MassLambda0); candidate.mass = massLambda; - candidate.cosPA = (sv[0] - collVtx.x()) * lambdaMom4D.Px() + - (sv[1] - collVtx.y()) * lambdaMom4D.Py() + - (sv[2] - collVtx.z()) * lambdaMom4D.Pz(); - candidate.cosPA /= std::hypot(sv[0] - collVtx.x(), sv[1] - collVtx.y(), sv[2] - collVtx.z()) * lambdaMom4D.P(); - candidate.dcaV0Daughters = std::sqrt(fitter.getChi2AtPCACandidate(0)); + candidate.cosPA = cosPA; + candidate.dcaV0Daughters = dcaDaughters; candidate.dcaProtonToPV = std::hypot(dcaInfoProton[0], dcaInfoProton[1]); candidate.dcaPionToPV = std::hypot(dcaInfoPion[0], dcaInfoPion[1]); - candidate.v0Radius = std::hypot(sv[0], sv[1]); + candidate.v0Radius = radius; candidate.protonNSigmaTPC = protonTrack.tpcNSigmaPr(); candidate.pionNSigmaTPC = pionTrack.tpcNSigmaPi(); candidate.sign = matter ? 1 : -1; // Positive sign for Lambda, negative for anti-Lambda lambdaCandidates.push_back(candidate); } - if (lambdaCandidates.empty()) { - return; // No valid Lambda candidates found + if (lambdaCandidates.empty() && (!cfgStoreK0s || k0sCandidates.empty())) { + return; // Require a Lambda, or a K0s when cfgStoreK0s is enabled } - mRegistry.get(HIST("hEventSelection"))->Fill(5); // He3 and Lambda candidates found + mRegistry.get(HIST("hEventSelection"))->Fill(5); // He3 and V0 candidates found mRegistry.get(HIST("hCentralitySelected"))->Fill(collision.centFT0C()); // Fill output tables @@ -386,6 +448,10 @@ struct he3LambdaAnalysis { lfHe3(lfHe3V0Collision.lastIndex(), he3.momentum.Pt(), he3.momentum.Eta(), he3.momentum.Phi(), he3.dcaXY, he3.dcaZ, he3.tpcNClsFound, he3.tpcNClsPID, he3.itsClusterSizes, he3.nSigmaTPC, he3.sign); } + for (const auto& k0s : k0sCandidates) { + lfK0s(lfHe3V0Collision.lastIndex(), k0s.momentum.Pt(), k0s.momentum.Eta(), k0s.momentum.Phi(), + k0s.mass, k0s.ct, k0s.cosPA, k0s.dcaV0Daughters, k0s.dcaPosToPV, k0s.dcaNegToPV, k0s.v0Radius, k0s.posPionNSigmaTPC, k0s.negPionNSigmaTPC); + } for (const auto& lambda : lambdaCandidates) { lfLambda(lfHe3V0Collision.lastIndex(), lambda.momentum.Pt(), lambda.momentum.Eta(), lambda.momentum.Phi(), lambda.mass, lambda.cosPA, lambda.dcaV0Daughters, lambda.dcaProtonToPV, lambda.dcaPionToPV, lambda.v0Radius, lambda.protonNSigmaTPC, lambda.pionNSigmaTPC, lambda.sign); diff --git a/PWGLF/TableProducer/Nuspex/hyhe4builder.cxx b/PWGLF/TableProducer/Nuspex/hyhe4builder.cxx index f4ddba0fdbe..50e450b7936 100644 --- a/PWGLF/TableProducer/Nuspex/hyhe4builder.cxx +++ b/PWGLF/TableProducer/Nuspex/hyhe4builder.cxx @@ -47,6 +47,7 @@ #include #include #include +#include using namespace std; using namespace o2; diff --git a/PWGLF/TableProducer/Nuspex/nucleiSpectra.cxx b/PWGLF/TableProducer/Nuspex/nucleiSpectra.cxx index 67837173150..17dcdd62ec5 100644 --- a/PWGLF/TableProducer/Nuspex/nucleiSpectra.cxx +++ b/PWGLF/TableProducer/Nuspex/nucleiSpectra.cxx @@ -230,6 +230,8 @@ std::shared_ptr hFlowHists[2][5]; std::shared_ptr hDCAHists[2][5]; std::shared_ptr hMatchingStudy[2]; std::shared_ptr hMatchingStudyHadrons[2]; +std::shared_ptr hMatchingCountsHadrons; +std::shared_ptr hMatchingCountsHe3; o2::base::MatLayerCylSet* lut = nullptr; std::vector candidates; @@ -366,6 +368,7 @@ struct nucleiSpectra { ConfigurableAxis cfgNITSClusBins{"cfgNITSClusBins", {3, 4.5, 7.5}, "N ITS clusters binning"}; ConfigurableAxis cfgNTPCClusBins{"cfgNTPCClusBins", {3, 89.5, 159.5}, "N TPC clusters binning"}; + Configurable cfgMatchingCounts{"cfgMatchingCounts", false, "Enable event-level ITS-TPC matching counts for binomiality studies"}; Configurable cfgSkimmedProcessing{"cfgSkimmedProcessing", false, "Skimmed dataset processing"}; Configurable cfgTriggerList{"cfgTriggerList", "fHe", "Trigger List"}; Configurable cfgSelectTrgEv{"cfgSelectTrgEv", false, "If true, select events with active trigger list"}; @@ -601,6 +604,10 @@ struct nucleiSpectra { if (doprocessMatching) { std::vector occBins{-0.5, 499.5, 999.5, 1999.5, 2999.5, 3999.5, 4999.5, 10000., 50000.}; AxisSpec occAxis{occBins, "Occupancy"}; + if (cfgMatchingCounts) { + nuclei::hMatchingCountsHadrons = spectra.add("hMatchingCountsHadrons", ";N_{ITS};N_{ITS+TPC};Centrality;Occupancy;Charge sign", HistType::kTHnSparseD, {{10001, -0.5, 10000.5}, {10001, -0.5, 10000.5}, {8, 0., 80.}, occAxis, {2, -0.5, 1.5}}); + nuclei::hMatchingCountsHe3 = spectra.add("hMatchingCountsHe3", ";N_{ITS};N_{ITS+TPC};Centrality;Occupancy;Charge sign", HistType::kTHnSparseD, {{10001, -0.5, 10000.5}, {10001, -0.5, 10000.5}, {8, 0., 80.}, occAxis, {2, -0.5, 1.5}}); + } for (unsigned int iC{0}; iC < nuclei::matter.size(); ++iC) { nuclei::hMatchingStudy[iC] = spectra.add(fmt::format("hMatchingStudy{}", nuclei::matter[iC]).data(), ";#it{p}_{T};#phi;#eta;n#sigma_{ITS};n#sigma{TPC};n#sigma_{TOF};Centrality", HistType::kTHnSparseF, {{20, 1., 9.}, {10, 0., o2::constants::math::TwoPI}, {10, -1., 1.}, {50, -5., 5.}, {50, -5., 5.}, {50, 0., 1.}, {8, 0., 80.}}); nuclei::hMatchingStudyHadrons[iC] = spectra.add(fmt::format("hMatchingStudyHadrons{}", nuclei::matter[iC]).data(), ";#it{p}_{T};#phi;#eta;Centrality;Track type; Occupancy", HistType::kTHnF, {{23, 0.4, 5.}, {20, 0., o2::constants::math::TwoPI}, {10, -1., 1.}, {8, 0., 80.}, {2, -0.5, 1.5}, occAxis}); @@ -1156,12 +1163,17 @@ struct nucleiSpectra { } PROCESS_SWITCH(nucleiSpectra, processMC, "MC analysis", false); - void processMatching(soa::Join::iterator const& collision, TrackCandidates const& tracks, aod::BCsWithTimestamps const&) + void processMatching(soa::Join::iterator const& collision, TrackCandidates const& tracks, aod::BCsWithTimestamps const&) { - if (!eventSelection(collision) || !collision.triggereventep()) { + if (!eventSelection(collision) || !collision.isCalibrated()) { return; } const float centrality = collision.centFT0C(); + const float psi2FT0C = std::atan2(collision.qvecFT0CIm(), collision.qvecFT0CRe()) / 2.f; + int nITSHadrons[2]{}; + int nITSTPCHadrons[2]{}; + int nITSHe3[2]{}; + int nITSTPCHe3[2]{}; o2::aod::ITSResponse itsResponse; for (const auto& track : tracks) { if (std::abs(track.eta()) > cfgTrackCut.EtaMax || @@ -1173,13 +1185,28 @@ struct nucleiSpectra { double expSigma{expBethe * cfgBetheBlochParams->get(4, 5u)}; double nSigmaTPC{(track.tpcSignal() - expBethe) / expSigma}; int iC = track.signed1Pt() > 0; + if (cfgMatchingCounts) { + ++nITSHadrons[iC]; + nITSTPCHadrons[iC] += track.hasTPC(); + } const float pt = track.pt(); - const float phi = 2.f * RecoDecay::constrainAngle(track.phi() - collision.psiFT0C(), 0.f, 2); + const float phi = 2.f * RecoDecay::constrainAngle(track.phi() - psi2FT0C, 0.f, 2); nuclei::hMatchingStudyHadrons[iC]->Fill(pt, phi, track.eta(), centrality, track.hasTPC(), collision.trackOccupancyInTimeRange()); - if (itsResponse.nSigmaITS(track) > -1.) { + if (itsResponse.nSigmaITS(track) > -2.) { + if (cfgMatchingCounts) { + ++nITSHe3[iC]; + nITSTPCHe3[iC] += track.hasTPC(); + } nuclei::hMatchingStudy[iC]->Fill(pt * 2, phi, track.eta(), itsResponse.nSigmaITS(track), nSigmaTPC, o2::pid::tof::Beta::GetBeta(track), centrality); } } + if (cfgMatchingCounts) { + // One entry per collision and charge sign, including zero-track events. + for (unsigned int iC{0}; iC < nuclei::matter.size(); ++iC) { + nuclei::hMatchingCountsHadrons->Fill(nITSHadrons[iC], nITSTPCHadrons[iC], centrality, collision.trackOccupancyInTimeRange(), iC); + nuclei::hMatchingCountsHe3->Fill(nITSHe3[iC], nITSTPCHe3[iC], centrality, collision.trackOccupancyInTimeRange(), iC); + } + } } PROCESS_SWITCH(nucleiSpectra, processMatching, "Matching analysis", false); diff --git a/PWGLF/TableProducer/Nuspex/particleCompositionCorrection.cxx b/PWGLF/TableProducer/Nuspex/particleCompositionCorrection.cxx index b2f5e201476..d9e868afca9 100644 --- a/PWGLF/TableProducer/Nuspex/particleCompositionCorrection.cxx +++ b/PWGLF/TableProducer/Nuspex/particleCompositionCorrection.cxx @@ -38,6 +38,8 @@ #include #include +// #include "Framework/Logger.h" + using namespace o2; using namespace o2::framework; using namespace o2::ml; @@ -64,6 +66,7 @@ struct ParticleCompositionCorrection { Configurable skipAll{"skipAll", false, "run table producer in dummy mode, i.e. skip all computations and fill with 1"}; Configurable skipSec{"skipSec", false, "dont calculate weights for secondaries"}; + Configurable skipNeutralSec{"skipNeutralSec", true, "dont calculate weights for neutral secondaries"}; Configurable skipNonPhysicalPrim{"skipNonPhysicalPrim", true, "dont calculate weights for particles that are not (originating from) physical primaries; i.e. reject (decays of) pi0 etc."}; Configurable etaCut{"etaCut", 0.8f, "eta cut"}; Configurable ptMinCut{"ptMinCut", 0.15f, "pt min cut"}; @@ -118,6 +121,7 @@ void ParticleCompositionCorrection::init(InitContext const&) const int maxMult = 100; const int nBinsMult = maxMult + 1; const AxisSpec multAxis = {nBinsMult, -0.5, nBinsMult - 0.5, "#it{N}_{ch}", "mult"}; + const AxisSpec processAxis = {51, -0.5, 50.5, "process", "process"}; histos.add("multDist_INELg0", "", kTH1D, {multAxis}); histos.add("multDist_fid", "", kTH1D, {multAxis}); @@ -127,25 +131,15 @@ void ParticleCompositionCorrection::init(InitContext const&) histos.add("frac/data/proton", "", kTProfile, {ptAxis}); histos.add("frac/data/sigma", "", kTProfile, {ptAxis}); - histos.add("frac/data/kaon_mult", "", kTProfile2D, {multAxis, ptAxis}); - histos.add("frac/data/proton_mult", "", kTProfile2D, {multAxis, ptAxis}); - histos.add("frac/data/sigma_mult", "", kTProfile2D, {multAxis, ptAxis}); - histos.addClone("frac/data/", "frac/mc/"); histos.add("weight/kaon", "", kTProfile, {ptAxis}); histos.add("weight/proton", "", kTProfile, {ptAxis}); histos.add("weight/sigma", "", kTProfile, {ptAxis}); - histos.add("weight/kaon_mult", "", kTProfile2D, {multAxis, ptAxis}); - histos.add("weight/proton_mult", "", kTProfile2D, {multAxis, ptAxis}); - histos.add("weight/sigma_mult", "", kTProfile2D, {multAxis, ptAxis}); - + histos.add("weight/prim", "", kTProfile, {ptAxis}); histos.add("weight/secDec", "", kTProfile, {ptAxis}); histos.add("weight/secMat", "", kTProfile, {ptAxis}); - - histos.add("weight/secDec_mult", "", kTProfile2D, {multAxis, ptAxis}); - histos.add("weight/secMat_mult", "", kTProfile2D, {multAxis, ptAxis}); } } @@ -153,21 +147,48 @@ std::tuple ParticleCompositionCorrection::getWeights(aod::M { static const std::tuple noWeights = {1.f, 1.f, 1.f}; - if (skipAll || std::abs(particle.eta()) > etaCut || particle.pt() < ptMinCut || particle.pt() > ptMaxCut) { + static constexpr int kXi0PDG = 3322; + + /* if (skipAll || std::abs(particle.eta()) > etaCut || particle.pt() < ptMinCut || particle.pt() > ptMaxCut) { + return noWeights; + } */ + + if (skipAll) { return noWeights; } - if (particle.producedByGenerator()) { - if (skipNonPhysicalPrim && !particle.isPhysicalPrimary()) { - return noWeights; - } + if (!particle.has_mothers()) { + return noWeights; + } + + auto particleMother = particle.mothersIds()[0] - particles.offset(); + + // if (particle.producedByGenerator()) { + + if (particleMother == 0. || particleMother == 1. || + (particle.isPhysicalPrimary() && skipNonPhysicalPrim)) { + auto absPDGCode = std::abs(particle.pdgCode()); - // translate abs PDG code to PID variable of neural networks (0: pion, 1: kaon, 2: proton, 3: sigma) if (absPDGCode == PDG_t::kPiPlus || absPDGCode == PDG_t::kPi0) { + + return noWeights; + } + + if (skipNonPhysicalPrim && !particle.isPhysicalPrimary()) { return noWeights; } + if (!particle.has_daughters()) { + + if (particle.eta() > etaCut || particle.pt() > ptMaxCut || particle.pt() < ptMinCut) { + + return noWeights; + } + } + + // translate abs PDG code to PID variable of neural networks (0: pion, 1: kaon, 2: proton, 3: sigma) + static const std::map mapPID = { {PDG_t::kPiPlus, 0.f}, {PDG_t::kPi0, 0.f}, @@ -181,79 +202,106 @@ std::tuple ParticleCompositionCorrection::getWeights(aod::M {PDG_t::kLambda0, 3.f}, {PDG_t::kSigma0, 3.f}, {PDG_t::kXiMinus, 3.f}, - // TODO: potentially extend by xi0/eta/omega/rho/phi/Delta... + {kXi0PDG, 3.f}, // Xi0 + {PDG_t::kOmegaMinus, 3.f}, + // TODO: potentially extend by eta/omega/rho/phi/Delta... // pdg codes defined in AliceO2/Common/Constants/include/CommonConstants/PhysicsConstants.h // e.g. o2::constants::physics::Pdg::kEta }; if (auto iterMapPID = mapPID.find(absPDGCode); iterMapPID != mapPID.end()) { - // LOGP(info, "scaling a {} with status code {} from process {}", particle.pdgCode(), particle.getGenStatusCode(), particle.getProcess()); - float pt = particle.pt(); // calculate particle fractions and corresponding weight for given reference particle - std::vector> input = {{dNdEta}, {pt}, {iterMapPID->second}}; + std::vector> input = {{dNdEta}, {particle.pt()}, {iterMapPID->second}}; float fracData = particleFractionsData.evalModel(input)[0]; float fracMC = particleFractionsMC.evalModel(input)[0]; - float weight = (fracMC) ? fracData / fracMC : 1.f; + float weight = (fracMC != 0.f) ? fracData / fracMC : 1.f; std::tuple weights = {weight, weight, weight}; if (!skipSec && particle.has_daughters()) { storedWeights[particle.index()] = weights; } - if (enableQAHistos && particle.isPhysicalPrimary() && std::abs(particle.eta()) < 0.8) { // o2-linter: disable=magic-number (usual range of charged-partilce measurements) + if (enableQAHistos && particle.isPhysicalPrimary() && std::abs(particle.eta()) < etaCut) { if (iterMapPID->first == PDG_t::kKPlus) { - histos.fill(HIST("frac/data/kaon"), pt, fracData); - histos.fill(HIST("frac/mc/kaon"), pt, fracMC); - histos.fill(HIST("frac/data/kaon_mult"), dNdEta, pt, fracData); - histos.fill(HIST("frac/mc/kaon_mult"), dNdEta, pt, fracMC); - histos.fill(HIST("weight/kaon"), pt, weight); - histos.fill(HIST("weight/kaon_mult"), dNdEta, pt, weight); + histos.fill(HIST("frac/data/kaon"), particle.pt(), fracData); + histos.fill(HIST("frac/mc/kaon"), particle.pt(), fracMC); + histos.fill(HIST("weight/kaon"), particle.pt(), weight); } if (iterMapPID->first == PDG_t::kProton) { - histos.fill(HIST("frac/data/proton"), pt, fracData); - histos.fill(HIST("frac/mc/proton"), pt, fracMC); - histos.fill(HIST("frac/data/proton_mult"), dNdEta, pt, fracData); - histos.fill(HIST("frac/mc/proton_mult"), dNdEta, pt, fracMC); - histos.fill(HIST("weight/proton"), pt, weight); - histos.fill(HIST("weight/proton_mult"), dNdEta, pt, weight); + histos.fill(HIST("frac/data/proton"), particle.pt(), fracData); + histos.fill(HIST("frac/mc/proton"), particle.pt(), fracMC); + histos.fill(HIST("weight/proton"), particle.pt(), weight); } if (iterMapPID->first == PDG_t::kSigmaPlus || iterMapPID->first == PDG_t::kSigmaMinus) { - histos.fill(HIST("frac/data/sigma"), pt, fracData); - histos.fill(HIST("frac/mc/sigma"), pt, fracMC); - histos.fill(HIST("frac/data/sigma_mult"), dNdEta, pt, fracData); - histos.fill(HIST("frac/mc/sigma_mult"), dNdEta, pt, fracMC); - histos.fill(HIST("weight/sigma"), pt, weight); - histos.fill(HIST("weight/sigma_mult"), dNdEta, pt, weight); + histos.fill(HIST("frac/data/sigma"), particle.pt(), fracData); + histos.fill(HIST("frac/mc/sigma"), particle.pt(), fracMC); + histos.fill(HIST("weight/sigma"), particle.pt(), weight); } + + histos.fill(HIST("weight/prim"), particle.pt(), weight); } + return weights; } + } else if (!skipSec) { + auto refParticleID = particle.index(); - // LOGP(error, "Particle [{}] {} from process {}", refParticleID, particle.pdgCode(), particle.getProcess()); - while (!particles.iteratorAt(refParticleID).producedByGenerator() && particles.iteratorAt(refParticleID).has_mothers()) { + auto pdgParticle = pdg->GetParticle(particle.pdgCode()); + + if (!pdgParticle) { + // LOGP(info, "Unknown PDG code: {}", particle.pdgCode()); + // return noWeights; + } else { + + if (skipNeutralSec && pdgParticle->Charge() == 0.) { + return noWeights; + } + } + + if (particle.eta() > etaCut || particle.pt() > ptMaxCut || particle.pt() < ptMinCut) { + return noWeights; + } + + while (particles.iteratorAt(refParticleID).has_mothers()) { + auto motherID = particles.iteratorAt(refParticleID).mothersIds()[0] - particles.offset(); - // LOGP(error, "-> mom [{}] {} from process {}", motherID, particles.iteratorAt(motherID).pdgCode(), particles.iteratorAt(motherID).getProcess()); + + auto mom = particles.iteratorAt(motherID); + + if (!mom.has_mothers()) { + break; + } + refParticleID = motherID; + + if (mom.isPhysicalPrimary() && skipNonPhysicalPrim) { + break; + } } - if (storedWeights.find(refParticleID) == storedWeights.end()) { - // LOGP(error, " no ref particle stored for particle {} from process {}!!", particle.pdgCode(), particle.getProcess()); + if (refParticleID == 0 || refParticleID == 1) { + LOGF(error, "GLOBAL MOTHER IS INCIDING PROTON"); + } + + if (!storedWeights.contains(refParticleID)) { + // LOGP(info, "MCB: no ref stored for mom {}, PDG: {}!!", particles.iteratorAt(refParticleID).index(),particles.iteratorAt(refParticleID).pdgCode()); return noWeights; } if (enableQAHistos) { + float weight = get<0>(storedWeights.at(refParticleID)); - auto pdgParticle = pdg->GetParticle(particle.pdgCode()); + if (pdgParticle && pdgParticle->Charge() != 0.) { - if (particle.getProcess() == TMCProcess::kPDecay) { + if (particle.getProcess() == TMCProcess::kPDecay || particle.getProcess() == TMCProcess::kPPrimary) { histos.fill(HIST("weight/secDec"), particle.pt(), weight); - histos.fill(HIST("weight/secDec_mult"), dNdEta, particle.pt(), weight); - } else if (particle.getProcess() == TMCProcess::kPHInhelastic || particle.getProcess() == TMCProcess::kPHadronic || particle.getProcess() == TMCProcess::kPHElastic) { + // } else if (particle.getProcess() == TMCProcess::kPHInhelastic || particle.getProcess() == TMCProcess::kPHadronic || particle.getProcess() == TMCProcess::kPHElastic) { + } else { histos.fill(HIST("weight/secMat"), particle.pt(), weight); - histos.fill(HIST("weight/secMat_mult"), dNdEta, particle.pt(), weight); } } } + return storedWeights.at(refParticleID); } return noWeights; @@ -266,6 +314,7 @@ void ParticleCompositionCorrection::process(aod::McCollisions::iterator const&, float dNdEtafid = 0.f; for (const auto& particle : particles) { + if (!particle.isPhysicalPrimary()) { continue; } @@ -288,6 +337,8 @@ void ParticleCompositionCorrection::process(aod::McCollisions::iterator const&, histos.fill(HIST("multDist_fidVsINELg0"), dNdEta, dNdEtafid); } + // LOGP(info, "MCB: ------- New event with dNdeta = {} in |eta| < 0.8 -------", dNdEtafid); + std::map> storedWeights; for (const auto& particle : particles) { auto [weight, weightSysUp, weightSysDown] = getWeights(particles, particle, storedWeights, dNdEta); diff --git a/PWGLF/TableProducer/Nuspex/piHypertritonFemto.cxx b/PWGLF/TableProducer/Nuspex/piHypertritonFemto.cxx index 784304adcd8..77de9f213f7 100644 --- a/PWGLF/TableProducer/Nuspex/piHypertritonFemto.cxx +++ b/PWGLF/TableProducer/Nuspex/piHypertritonFemto.cxx @@ -1164,7 +1164,7 @@ void PiHypertritonFemto::processPairs(const Tcollisions& collisions, const Ttrac { // Each invocation receives a new input-table scope; row indices can repeat in later frames. const uint64_t sourceFrameId = ++mSourceFrameId; - const BinningType configuredBinningPolicy{{axisVertex, axisCentrality}, true}; + const BinningType configuredBinningPolicy{{axisVertex, axisCentrality}}; for (const auto& collision : collisions) { if (!selectCollision(collision, bcs)) { continue; diff --git a/PWGLF/TableProducer/Resonances/cksspinalignment.cxx b/PWGLF/TableProducer/Resonances/cksspinalignment.cxx index c52483f28fe..8a62bb13ad4 100644 --- a/PWGLF/TableProducer/Resonances/cksspinalignment.cxx +++ b/PWGLF/TableProducer/Resonances/cksspinalignment.cxx @@ -139,11 +139,10 @@ struct cksspinalignment { } bool keepEvent(uint64_t eventIndex) const - { /* - if (cfgKeepFraction >= 1.0f) { - return true; - } - */ + { + // if (cfgKeepFraction >= 1.0f) { + // return true; + // } // SplitMix64 pseudo-random hash uint64_t x = eventIndex + 0x9e3779b97f4a7c15ULL; x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ULL; diff --git a/PWGLF/TableProducer/Resonances/doublephitable.cxx b/PWGLF/TableProducer/Resonances/doublephitable.cxx index 7845dbdf6b3..1cd5309b47e 100644 --- a/PWGLF/TableProducer/Resonances/doublephitable.cxx +++ b/PWGLF/TableProducer/Resonances/doublephitable.cxx @@ -459,7 +459,7 @@ struct doublephitable { auto* grpmag = ccdb->getForTimeStamp(ccdbPathGrpMag.value, bc.timestamp()); if (grpmag == nullptr) { - LOGF(fatal, "GRPMagField unavailable for run %d, timestamp %llu", bc.runNumber(), static_cast(bc.timestamp())); + LOGF(fatal, "GRPMagField unavailable for run %d, timestamp %llu", bc.runNumber(), bc.timestamp()); } o2::base::Propagator::initFieldFromGRP(grpmag); diff --git a/PWGLF/TableProducer/Resonances/hResonanceCorrelationFilter.cxx b/PWGLF/TableProducer/Resonances/hResonanceCorrelationFilter.cxx index 959014c61ce..bca62f39f87 100644 --- a/PWGLF/TableProducer/Resonances/hResonanceCorrelationFilter.cxx +++ b/PWGLF/TableProducer/Resonances/hResonanceCorrelationFilter.cxx @@ -18,9 +18,6 @@ #include "PWGLF/DataModel/LFHResonanceCorrelationTables.h" #include "Common/CCDB/EventSelectionParams.h" -#include "Common/CCDB/RCTSelectionFlags.h" -#include "Common/Core/Zorro.h" -#include "Common/Core/ZorroSummary.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" @@ -28,7 +25,6 @@ #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" -#include #include #include #include @@ -56,7 +52,6 @@ using namespace o2; using namespace o2::soa; using namespace o2::constants::math; -using namespace o2::aod::rctsel; using namespace o2::framework; using namespace o2::framework::expressions; @@ -72,64 +67,54 @@ enum PIDCutType { struct HResonanceCorrelationFilter { - Service ccdb; - - RCTFlagsChecker rctChecker; - HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - // master analysis switches - Configurable doPPAnalysis{"doPPAnalysis", true, "if in pp, set to true"}; - - // Operational - Configurable zorroMask{"zorroMask", "", "zorro trigger class to select on (empty: none)"}; - - Configurable cfgTPCNsigmaKaon{"cfgTPCNsigmaKaon", 3.0f, "TPC Kaon PID"}; - Configurable cfgTOFNsigmaKaon{"cfgTOFNsigmaKaon", 3.0f, "TOF Kaon PID"}; - - Configurable cfgRapidity{"cfgRapidity", 0.5f, "Rapidity cut"}; - - // used for event selections in Pb-Pb - Configurable cfgCutOccupancyHigh{"cfgCutOccupancyHigh", 3000, "High cut on TPC occupancy"}; - Configurable cfgCutOccupancyLow{"cfgCutOccupancyLow", 0, "Low cut on TPC occupancy"}; - - struct : ConfigurableGroup { - std::string prefix = "eventSelections"; - // event filtering - Configurable zVertexCut{"zVertexCut", 10, "Cut on PV position"}; - Configurable selectINELgtZERO{"selectINELgtZERO", true, "select INEL>0 events"}; - Configurable requireAllGoodITSLayers{"requireAllGoodITSLayers", false, " require that in the event all ITS are good"}; - Configurable requireGoodTriggerTVX{"requireGoodTriggerTVX", false, " require acceptable FT0C-FT0A time difference"}; - Configurable requireGoodZvtxFT0vsPV{"requireGoodZvtxFT0vsPV", false, " require small difference between z-vertex from PV and from FT0"}; - Configurable minCentPercent{"minCentPercent", 0, "minimum centrality percentage"}; - Configurable maxCentPercent{"maxCentPercent", 100, "maximum centrality percentage"}; - } eventSelections; - + // Single, shared event-selection cut list used by every process function + // (processTriggers, processAssocPions/Kaons/Hadrons, processPhis, + // processKstars, and their MC counterparts) via isSelectedEvents(), so the + // trigger table and every associated-track/resonance table are always + // built over the exact same set of selected collisions. struct : ConfigurableGroup { + std::string prefix = "configEvents"; Configurable cfgEvtZvtx{"cfgEvtZvtx", 10.0f, "Evt sel: Max. z-Vertex (cm)"}; Configurable cfgEvtTriggerTVXSel{"cfgEvtTriggerTVXSel", true, "Evt sel: triggerTVX selection (MB)"}; Configurable cfgEvtNoTFBorderCut{"cfgEvtNoTFBorderCut", true, "Evt sel: apply TF border cut"}; - Configurable cfgEvtNoITSROFrameBorderCut{"cfgEvtNoITSROFrameBorderCut", false, "Evt sel: apply NoITSRO border cut"}; - Configurable cfgEvtIsRCTFlagpassed{"cfgEvtIsRCTFlagpassed", false, "Evt sel: apply RCT flag selection"}; - Configurable cfgEvtRCTFlagCheckerLabel{"cfgEvtRCTFlagCheckerLabel", "CBT_hadronPID", "Evt sel: RCT flag checker label"}; - Configurable cfgEvtRCTFlagCheckerZDCCheck{"cfgEvtRCTFlagCheckerZDCCheck", false, "Evt sel: RCT flag checker ZDC check"}; - Configurable cfgEvtRCTFlagCheckerLimitAcceptAsBad{"cfgEvtRCTFlagCheckerLimitAcceptAsBad", true, "Evt sel: RCT flag checker treat Limited Acceptance As Bad"}; - Configurable cfgEvtSel8{"cfgEvtSel8", false, "Evt Sel 8 check for offline selection"}; - Configurable cfgEvtIsINELgt0{"cfgEvtIsINELgt0", false, "Evt sel: apply INEL>0 selection"}; + Configurable cfgEvtNoITSROFrameBorderCut{"cfgEvtNoITSROFrameBorderCut", true, "Evt sel: apply NoITSRO border cut"}; + Configurable cfgEvtSel8{"cfgEvtSel8", true, "Evt Sel 8 check for offline selection"}; + Configurable cfgEvtNoSameBunchPileupCut{"cfgEvtNoSameBunchPileupCut", true, "Evt sel: reject collisions associated with the same found-by-T0 bunch crossing"}; + Configurable cfgEvtGoodZvtxFT0vsPVCut{"cfgEvtGoodZvtxFT0vsPVCut", true, "Evt sel: require small difference between z-vertex from PV and from FT0"}; + // Centrality source switch for hCentralitySelected: O-O collisions have a + // well-defined FT0C centrality estimator; p-O (asymmetric system) does + // not, so FV0A is used there instead. Only affects which estimator is + // read for that one QA histogram -- doesn't change any selection cut. + Configurable cfgUseFV0ACentrality{"cfgUseFV0ACentrality", false, "Centrality estimator for hCentralitySelected: false = O-O (FT0C), true = p-O (FV0A)"}; } configEvents; + // Merged from the formerly separate configTracks/generalSelections/ + // trackSelections groups -- all three were "cuts applied to a track or + // track pair", just split by who last touched them. No member names + // collided, so every Configurable below keeps its original key; only the + // "." prefix at each call site changed, to configTracks. + // General, species-agnostic track-quality cuts: applied identically to + // every track that reaches trackCut() -- trigger, associated hadron, AND + // Phi/K*0 daughter candidates alike (see trackCut() below, and its call + // sites at isValidTrigger(), isValidAssocTrack(), and both + // processPhis()/processKstars()). cfgCutEta/cMinPtcut additionally gate + // the framework-level `acceptanceFilter` Filter, applied to every track + // in every process function before any of the above ever sees it. struct : ConfigurableGroup { + std::string prefix = "configTracks"; // Pre-selection Track cuts - Configurable trackSelection{"trackSelection", 0, "Track selection: 0 -> No Cut, 1 -> kGlobalTrack, 2 -> kGlobalTrackWoPtEta, 3 -> kGlobalTrackWoDCA, 4 -> kQualityTracks, 5 -> kInAcceptanceTracks"}; Configurable cMinPtcut{"cMinPtcut", 0.15f, "Minimal pT for tracks"}; Configurable cMinTPCNClsFound{"cMinTPCNClsFound", 120, "minimum TPCNClsFound value for good track"}; Configurable cfgCutEta{"cfgCutEta", 0.8f, "Eta range for tracks"}; - Configurable cfgCutRapidity{"cfgCutRapidity", 0.5f, "rapidity range for particles"}; Configurable cfgMinCrossedRows{"cfgMinCrossedRows", 70, "min crossed rows for good track"}; + Configurable cfgMaxTPCChi2NCl{"cfgMaxTPCChi2NCl", 4.0f, "max TPC chi2/clusters for good track"}; + Configurable cfgMaxITSChi2NCl{"cfgMaxITSChi2NCl", 36.0f, "max ITS chi2/clusters for good track"}; + Configurable cfgMinTPCCrossedRowsOverFindableCls{"cfgMinTPCCrossedRowsOverFindableCls", 0.8f, "min ratio of TPC crossed rows over findable clusters for good track"}; + Configurable cfgMinITSNCls{"cfgMinITSNCls", 5, "min number of ITS hits (clusters) for good track"}; // DCA Selections - // DCAr to PV - Configurable cMaxDCArToPVcut{"cMaxDCArToPVcut", 0.1f, "Track DCAr cut to PV Maximum"}; // DCAz to PV Configurable cMaxDCAzToPVcut{"cMaxDCAzToPVcut", 0.1f, "Track DCAz cut to PV Maximum"}; @@ -139,64 +124,63 @@ struct HResonanceCorrelationFilter { Configurable cfgGlobalTrack{"cfgGlobalTrack", false, "Global track selection"}; // kGoldenChi2 | kDCAxy | kDCAz Configurable cfgPVContributor{"cfgPVContributor", false, "PV contributor track selection"}; // PV Contriuibutor Configurable cfgHasTOF{"cfgHasTOF", false, "Require TOF"}; - Configurable cfgUseTPCRefit{"cfgUseTPCRefit", false, "Require TPC Refit"}; - Configurable cfgUseITSRefit{"cfgUseITSRefit", false, "Require ITS Refit"}; Configurable cTPCNClsFound{"cTPCNClsFound", false, "Switch to turn on/off TPCNClsFound cut"}; - Configurable cDCAr7SigCut{"cDCAr7SigCut", false, "Track DCAr 7 Sigma cut to PV Maximum"}; + + // Track quality shared by trigger AND assoc selection (isValidTrigger() + // and isValidAssocTrack() both read this same configurable -- + // it is NOT split into trigger-/assoc-only copies below). + Configurable minTPCNCrossedRows{"minTPCNCrossedRows", 70, "Minimum TPC crossed rows"}; + + // primary particle DCAxy selections (formerly trackSelections) + // formula: |DCAxy| < 0.004f + (0.013f / pt) + Configurable dcaXYconstant{"dcaXYconstant", 0.004, "[0] in |DCAxy| < [0]+[1]/pT"}; + Configurable dcaXYpTdep{"dcaXYpTdep", 0.013, "[1] in |DCAxy| < [0]+[1]/pT"}; } configTracks; + // Phi/K*0 resonance-candidate cuts: applied to the RECONSTRUCTED candidate + // (the K+K- or K-pi 4-vector), not to the daughter tracks themselves -- + // the daughters go through the shared configTracks/trackCut() cuts above + // like every other track. Currently holds just the one configurable; kept + // as its own group rather than folded into configTracks so it reads as + // "resonance-level", not "track-level". struct : ConfigurableGroup { - std::string prefix = "generalSelections"; + std::string prefix = "configResoDauTracks"; + Configurable cfgCutRapidity{"cfgCutRapidity", 0.5f, "rapidity range for the reconstructed Phi/K*0 candidate"}; + } configResoDauTracks; - // Associated particle selections in phase space - Configurable assocEtaMin{"assocEtaMin", -0.8, "triggeretamin"}; - Configurable assocEtaMax{"assocEtaMax", 0.8, "triggeretamax"}; - Configurable assocPtCutMin{"assocPtCutMin", 0.2, "assocptmin"}; - Configurable assocPtCutMax{"assocPtCutMax", 10, "assocptmax"}; - - // Trigger particle selections in phase space + // Trigger-hadron-only phase-space and quality cuts. + struct : ConfigurableGroup { + std::string prefix = "configTriggerTracks"; Configurable triggerEtaMin{"triggerEtaMin", -0.8, "triggeretamin"}; Configurable triggerEtaMax{"triggerEtaMax", 0.8, "triggeretamax"}; - Configurable triggerPtCutMin{"triggerPtCutMin", 3, "triggerptmin"}; + Configurable triggerPtCutMin{"triggerPtCutMin", 2, "triggerptmin"}; Configurable triggerPtCutMax{"triggerPtCutMax", 20, "triggerptmax"}; - } generalSelections; - - struct : ConfigurableGroup { - std::string prefix = "trackSelections"; - // Track quality - Configurable minTPCNCrossedRows{"minTPCNCrossedRows", 70, "Minimum TPC crossed rows"}; Configurable triggerRequireITS{"triggerRequireITS", true, "require ITS signal in trigger tracks"}; - Configurable assocRequireITS{"assocRequireITS", true, "require ITS signal in assoc tracks"}; Configurable triggerMaxTPCSharedClusters{"triggerMaxTPCSharedClusters", 200, "maximum number of shared TPC clusters (inclusive)"}; Configurable triggerRequireL0{"triggerRequireL0", false, "require ITS L0 cluster for trigger"}; - Configurable requireClusterInITS{"requireClusterInITS", false, "require cluster in ITS for phi daughter tracks"}; - Configurable minITSClustersForDaughterTracks{"minITSClustersForDaughterTracks", 1, "Minimum number of ITS clusters for phi daughter tracks"}; + } configTriggerTracks; - // Associated pion identification - Configurable pionMinBayesProb{"pionMinBayesProb", 0.95, "minimal Bayesian probability for pion ID"}; - Configurable assocPionNSigmaTPCFOF{"assocPionNSigmaTPCFOF", 3, "minimal n sigma in TOF and TPC for Pion ID"}; - Configurable rejectSigma{"rejectSigma", 1, "n sigma for rejecting pion candidates"}; - - // Associated kaon identification (mirrors the pion selection above, with - // the accept/reject roles swapped: accept kaon-consistent tracks, reject - // pion-/proton-consistent ones). Selected via the Species template tag on - // isValidAssocTrack() below -- production path (which table rows - // get made) is still separate per process function, only the PID gate - // logic itself is shared. - Configurable assocKaonNSigmaTPCFOF{"assocKaonNSigmaTPCFOF", 3, "minimal n sigma in TOF and TPC for Kaon ID"}; - - // primary particle DCAxy selections - // formula: |DCAxy| < 0.004f + (0.013f / pt) - Configurable dcaXYconstant{"dcaXYconstant", 0.004, "[0] in |DCAxy| < [0]+[1]/pT"}; - Configurable dcaXYpTdep{"dcaXYpTdep", 0.013, "[1] in |DCAxy| < [0]+[1]/pT"}; - } trackSelections; + // Associated-hadron-only phase-space and quality cuts. + struct : ConfigurableGroup { + std::string prefix = "configAssocTracks"; + Configurable assocEtaMin{"assocEtaMin", -0.8, "triggeretamin"}; + Configurable assocEtaMax{"assocEtaMax", 0.8, "triggeretamax"}; + Configurable assocPtCutMin{"assocPtCutMin", 0.2, "assocptmin"}; + Configurable assocPtCutMax{"assocPtCutMax", 10, "assocptmax"}; + Configurable assocRequireITS{"assocRequireITS", true, "require ITS signal in assoc tracks"}; + } configAssocTracks; struct : ConfigurableGroup { + std::string prefix = "configPID"; /// PID Selections - Configurable pidnSigmaPreSelectionCut{"pidnSigmaPreSelectionCut", 4.0f, "pidnSigma Cut for pre-selection of tracks"}; Configurable cByPassTOF{"cByPassTOF", false, "By pass TOF PID selection"}; // By pass TOF PID selection Configurable cPIDcutType{"cPIDcutType", 2, "cPIDcutType = 1 for square cut, 2 for circular cut"}; // By pass TOF PID selection Configurable ispTdepPID{"ispTdepPID", false, "enable pT dependent PID"}; + // Were loose top-level Configurables; folded in here as unused (no call + // sites reference them) and logically the same "Kaon PID" kind as the + // kaonTPCPIDcuts/kaonTOFPIDcuts group below. + Configurable cfgTPCNsigmaKaon{"cfgTPCNsigmaKaon", 3.0f, "TPC Kaon PID"}; + Configurable cfgTOFNsigmaKaon{"cfgTOFNsigmaKaon", 3.0f, "TOF Kaon PID"}; // Kaon Configurable> kaonTPCPIDpTintv{"kaonTPCPIDpTintv", {0.5f}, "pT intervals for Kaon TPC PID cuts"}; @@ -213,10 +197,19 @@ struct HResonanceCorrelationFilter { Configurable> pionTOFPIDcuts{"pionTOFPIDcuts", {2}, "nSigma list for Pion TOF PID cuts"}; Configurable> pionTPCTOFCombinedpTintv{"pionTPCTOFCombinedpTintv", {999.0f}, "pT intervals for Pion TPC-TOF PID cuts"}; Configurable> pionTPCTOFCombinedPIDcuts{"pionTPCTOFCombinedPIDcuts", {2}, "nSigma list for Pion TPC-TOF PID cuts"}; - } configPID; - Configurable ccdburl{"ccdburl", "http://alice-ccdb.cern.ch", "url of the ccdb repository to use"}; - Configurable parameterCCDBPath{"parameterCCDBPath", "Users/k/kcui/LHC25b4a/parameter", "Path of the mean and sigma"}; + // Associated kaon identification (formerly trackSelections; mirrors the + // pion selection above, with the accept/reject roles swapped: accept + // kaon-consistent tracks, reject pion-/proton-consistent ones). Selected + // via the Species template tag on isValidAssocTrack() below -- + // production path (which table rows get made) is still separate per + // process function, only the PID gate logic itself is shared. + Configurable assocKaonNSigmaTPCFOF{"assocKaonNSigmaTPCFOF", 3, "minimal n sigma in TOF and TPC for Kaon ID"}; + + // Associated pion identification (formerly trackSelections) + Configurable assocPionNSigmaTPCFOF{"assocPionNSigmaTPCFOF", 3, "minimal n sigma in TOF and TPC for Pion ID"}; + Configurable rejectSigma{"rejectSigma", 1, "n sigma for rejecting pion candidates"}; + } configPID; // must include windows for background and peak // @@ -229,7 +222,10 @@ struct HResonanceCorrelationFilter { // mirror the analysis task's own massWindowConfigurationsPhi/Kstar // (sigma = PDG Gamma/2.355 placeholder -- refit from your own peak and // update both files together, they must agree on what "sigma" means). - Configurable maxMassNSigma{"maxMassNSigma", 12.0f, "max mass region to be considered for further analysis, in units of sigma"}; + struct : ConfigurableGroup { + std::string prefix = "massWindowConfigurations"; + Configurable maxMassNSigma{"maxMassNSigma", 12.0f, "max mass region to be considered for further analysis, in units of sigma"}; + } massWindowConfigurations; struct : ConfigurableGroup { std::string prefix = "massWindowConfigurationsPhi"; @@ -254,24 +250,42 @@ struct HResonanceCorrelationFilter { // For extracting strangeness mass QA plots struct : ConfigurableGroup { + std::string prefix = "axesConfigurations"; ConfigurableAxis axisPtQA{"axisPtQA", {VARIABLE_WIDTH, 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, 2.0f, 2.2f, 2.4f, 2.6f, 2.8f, 3.0f, 3.2f, 3.4f, 3.6f, 3.8f, 4.0f, 4.4f, 4.8f, 5.2f, 5.6f, 6.0f, 6.5f, 7.0f, 7.5f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 17.0f, 19.0f, 21.0f, 23.0f, 25.0f, 30.0f, 35.0f, 40.0f, 50.0f}, "pt axis for QA histograms"}; - ConfigurableAxis axisPhiMass{"axisPhiMass", {200, 0.99f, 1.08f}, "M(K^{+}K^{-})"}; - ConfigurableAxis axisKstarMass{"axisKstarMass", {200, 0.75f, 1.05f}, "M(K#pi)"}; - ConfigurableAxis axisMult{"axisMult", {VARIABLE_WIDTH, 0.0f, 0.01f, 1.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 70.0f, 100.0f}, "Centrality percentile bins"}; + // Daughter/trigger-track QA axes (Phi->KK, K*0->Kpi, trigger hadrons): + // each is a 1D QA histogram's own axis, booked in init(). + ConfigurableAxis axisEtaQA{"axisEtaQA", {100, -1.0f, 1.0f}, "#eta"}; + ConfigurableAxis axisNSigmaQA{"axisNSigmaQA", {100, -10.0f, 10.0f}, "n#sigma"}; + ConfigurableAxis axisDCAxyQA{"axisDCAxyQA", {200, -0.5f, 0.5f}, "DCA_{xy} (cm)"}; + ConfigurableAxis axisDCAzQA{"axisDCAzQA", {200, -0.5f, 0.5f}, "DCA_{z} (cm)"}; + ConfigurableAxis axisTPCCrossedRowsQA{"axisTPCCrossedRowsQA", {160, 0, 160}, "TPC crossed rows"}; + ConfigurableAxis axisVertexZQA{"axisVertexZQA", {60, -15.0f, 15.0f}, "Vertex Z (cm)"}; + ConfigurableAxis axisCentralityQA{"axisCentralityQA", {100, 0.0f, 100.0f}, "Centrality (%)"}; } axesConfigurations; // QA - Configurable doTrueSelectionInMass{"doTrueSelectionInMass", false, "Fill mass histograms only with true primary Particles for MC"}; - // Do declarative selections for DCAs, if possible - Filter preFilterTracks = nabs(aod::track::dcaXY) < trackSelections.dcaXYconstant + trackSelections.dcaXYpTdep * nabs(aod::track::signed1Pt); + struct : ConfigurableGroup { + std::string prefix = "qaConfigurations"; + Configurable doTrueSelectionInMass{"doTrueSelectionInMass", false, "Fill mass histograms only with true primary Particles for MC"}; + } qaConfigurations; - using FullTracks = soa::Join; - using FullTracksMC = soa::Join; + // Do declarative selections for DCAs, if possible + Filter acceptanceFilter = (nabs(aod::track::eta) < configTracks.cfgCutEta && aod::track::pt > configTracks.cMinPtcut) && + (nabs(aod::track::dcaXY) < configTracks.dcaXYconstant + configTracks.dcaXYpTdep * nabs(aod::track::signed1Pt)) && + (nabs(aod::track::dcaZ) < configTracks.cMaxDCAzToPVcut); + + // All four aliases below join TrackSelection + TrackSelectionExtension so + // that trackCut() -- the shared "is this a good, well-reconstructed track" + // baseline -- is callable (and applied) on trigger and associated-particle + // tracks exactly as it already is on Phi/K*0 daughters (TrackCandidates), + // not just on the latter. + using FullTracks = soa::Join; + using FullTracksMC = soa::Join; using DauTracks = soa::Join; using DauTracksMC = soa::Join; // using IDTracks= soa::Join; // prepared for Bayesian PID - using IDTracks = soa::Join; - using IDTracksMC = soa::Join; + using IDTracks = soa::Join; + using IDTracksMC = soa::Join; Produces triggerTrack; Produces triggerTrackExtra; @@ -287,10 +301,6 @@ struct HResonanceCorrelationFilter { using MCEventCandidates = soa::Join; using MCTrackCandidates = soa::Filtered>; - Zorro zorro; - OutputObj zorroSummary{"zorroSummary"}; - int mRunNumber = -1; - struct TriggCandidate { float pt = 0.f; int collisionId = -1; @@ -304,44 +314,142 @@ struct HResonanceCorrelationFilter { void init(InitContext const&) { - rctChecker.init(configEvents.cfgEvtRCTFlagCheckerLabel, configEvents.cfgEvtRCTFlagCheckerZDCCheck, configEvents.cfgEvtRCTFlagCheckerLimitAcceptAsBad); - histos.add("CollCutCounts", "No. of event after cuts", kTH1I, {{10, 0, 10}}); + // Z-vertex and centrality of selected events (filled in isSelectedEvents() + // once a collision passes every cut, i.e. the same "All Passed Events" + // point as CollCutCounts bin 8). Centrality source for the latter is + // configEvents.cfgUseFV0ACentrality: FT0C for O-O, FV0A for p-O. + histos.add("hVertexZSelected", "Vertex Z of selected events", kTH1F, {axesConfigurations.axisVertexZQA}); + histos.add("hCentralitySelected", "Centrality of selected events", kTH1F, {axesConfigurations.axisCentralityQA}); histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(1, "All Events"); histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(2, "|Vz| < cut"); histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(3, "kIsTriggerTVX"); histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(4, "kNoTimeFrameBorder"); histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(5, "kNoITSROFrameBorder"); - histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(6, "rctChecker"); - histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(7, "sel8"); - histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(8, "IsINELgt0"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(6, "sel8"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(7, "kNoSameBunchPileup"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(8, "kIsGoodZvtxFT0vsPV"); histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(9, "All Passed Events"); - zorroSummary.setObject(zorro.getZorroSummary()); - mRunNumber = -1; - - mMinMassPhi = massWindowConfigurationsPhi.peakMass - maxMassNSigma * massWindowConfigurationsPhi.sigma; - mMaxMassPhi = massWindowConfigurationsPhi.peakMass + maxMassNSigma * massWindowConfigurationsPhi.sigma; - mMinMassKstar = massWindowConfigurationsKstar.peakMass - maxMassNSigma * massWindowConfigurationsKstar.sigma; - mMaxMassKstar = massWindowConfigurationsKstar.peakMass + maxMassNSigma * massWindowConfigurationsKstar.sigma; + mMinMassPhi = massWindowConfigurationsPhi.peakMass - massWindowConfigurations.maxMassNSigma * massWindowConfigurationsPhi.sigma; + mMaxMassPhi = massWindowConfigurationsPhi.peakMass + massWindowConfigurations.maxMassNSigma * massWindowConfigurationsPhi.sigma; + mMinMassKstar = massWindowConfigurationsKstar.peakMass - massWindowConfigurations.maxMassNSigma * massWindowConfigurationsKstar.sigma; + mMaxMassKstar = massWindowConfigurationsKstar.peakMass + massWindowConfigurations.maxMassNSigma * massWindowConfigurationsKstar.sigma; LOGF(info, "Assoc mass pre-cut windows: Phi [%.4f, %.4f] GeV, K*0 [%.4f, %.4f] GeV", mMinMassPhi, mMaxMassPhi, mMinMassKstar, mMaxMassKstar); + + // QA histograms for Phi (K+K-) and K*0 (K-pi) daughter tracks: booked once + // per daughter species (Phi's kaon, Kstar's kaon, Kstar's pion), filled at + // the point each candidate passes all cuts (including the mass window) in + // processPhis/processKstars (and their MC counterparts). + auto bookDaughterQA = [&](const std::string& dir) { + histos.add((dir + "/hPt").c_str(), "p_{T}", kTH1F, {axesConfigurations.axisPtQA}); + histos.add((dir + "/hEta").c_str(), "#eta", kTH1F, {axesConfigurations.axisEtaQA}); + histos.add((dir + "/hTPCNSigma").c_str(), "TPC n#sigma", kTH1F, {axesConfigurations.axisNSigmaQA}); + histos.add((dir + "/hTOFNSigma").c_str(), "TOF n#sigma", kTH1F, {axesConfigurations.axisNSigmaQA}); + histos.add((dir + "/hDCAxy").c_str(), "DCA_{xy}", kTH1F, {axesConfigurations.axisDCAxyQA}); + histos.add((dir + "/hDCAz").c_str(), "DCA_{z}", kTH1F, {axesConfigurations.axisDCAzQA}); + histos.add((dir + "/hTPCCrossedRows").c_str(), "TPC crossed rows", kTH1F, {axesConfigurations.axisTPCCrossedRowsQA}); + // 2D pT-binned QA, same variables as above, to see pT dependence + histos.add((dir + "/hPtVsDCAxy").c_str(), "p_{T} vs DCA_{xy}", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisDCAxyQA}); + histos.add((dir + "/hPtVsDCAz").c_str(), "p_{T} vs DCA_{z}", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisDCAzQA}); + histos.add((dir + "/hPtVsTPCNSigma").c_str(), "p_{T} vs TPC n#sigma", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisNSigmaQA}); + histos.add((dir + "/hPtVsTOFNSigma").c_str(), "p_{T} vs TOF n#sigma", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisNSigmaQA}); + // TPC vs TOF nSigma correlation, for the usual PID "banana plot" check + histos.add((dir + "/hTPCNSigmaVsTOFNSigma").c_str(), "TPC n#sigma vs TOF n#sigma", kTH2F, {axesConfigurations.axisNSigmaQA, axesConfigurations.axisNSigmaQA}); + }; + bookDaughterQA("QA/Phi/Kaon"); + bookDaughterQA("QA/Kstar/Kaon"); + bookDaughterQA("QA/Kstar/Pion"); + + // Trigger hadron QA: no PID applied to trigger tracks, so only the 5 + // non-PID variables are booked (pt, eta, dcaXY, dcaZ, TPC crossed rows), + // plus the pT-binned 2D versions of DCAxy/DCAz (no nSigma -- no PID here). + histos.add("QA/TriggerHadron/hPt", "p_{T}", kTH1F, {axesConfigurations.axisPtQA}); + histos.add("QA/TriggerHadron/hEta", "#eta", kTH1F, {axesConfigurations.axisEtaQA}); + histos.add("QA/TriggerHadron/hDCAxy", "DCA_{xy}", kTH1F, {axesConfigurations.axisDCAxyQA}); + histos.add("QA/TriggerHadron/hDCAz", "DCA_{z}", kTH1F, {axesConfigurations.axisDCAzQA}); + histos.add("QA/TriggerHadron/hTPCCrossedRows", "TPC crossed rows", kTH1F, {axesConfigurations.axisTPCCrossedRowsQA}); + histos.add("QA/TriggerHadron/hPtVsDCAxy", "p_{T} vs DCA_{xy}", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisDCAxyQA}); + histos.add("QA/TriggerHadron/hPtVsDCAz", "p_{T} vs DCA_{z}", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisDCAzQA}); } - void initCCDB(aod::BCsWithTimestamps::iterator const& bc) + // HIST() needs a compile-time literal path, so each daughter species gets + // its own explicit fill function rather than one generic/parameterized + // helper. Kaon QA uses the Ka PID accessors, pion QA uses the Pi ones. + template + void fillPhiKaonQA(const Track& track) { - if (mRunNumber == bc.runNumber()) { - return; + histos.fill(HIST("QA/Phi/Kaon/hPt"), track.pt()); + histos.fill(HIST("QA/Phi/Kaon/hEta"), track.eta()); + histos.fill(HIST("QA/Phi/Kaon/hTPCNSigma"), track.tpcNSigmaKa()); + histos.fill(HIST("QA/Phi/Kaon/hTOFNSigma"), track.tofNSigmaKa()); + histos.fill(HIST("QA/Phi/Kaon/hDCAxy"), track.dcaXY()); + histos.fill(HIST("QA/Phi/Kaon/hDCAz"), track.dcaZ()); + histos.fill(HIST("QA/Phi/Kaon/hTPCCrossedRows"), track.tpcNClsCrossedRows()); + histos.fill(HIST("QA/Phi/Kaon/hPtVsDCAxy"), track.pt(), track.dcaXY()); + histos.fill(HIST("QA/Phi/Kaon/hPtVsDCAz"), track.pt(), track.dcaZ()); + histos.fill(HIST("QA/Phi/Kaon/hPtVsTPCNSigma"), track.pt(), track.tpcNSigmaKa()); + histos.fill(HIST("QA/Phi/Kaon/hPtVsTOFNSigma"), track.pt(), track.tofNSigmaKa()); + if (track.hasTOF()) { + histos.fill(HIST("QA/Phi/Kaon/hTPCNSigmaVsTOFNSigma"), track.tpcNSigmaKa(), track.tofNSigmaKa()); + } + } + + template + void fillKstarKaonQA(const Track& track) + { + histos.fill(HIST("QA/Kstar/Kaon/hPt"), track.pt()); + histos.fill(HIST("QA/Kstar/Kaon/hEta"), track.eta()); + histos.fill(HIST("QA/Kstar/Kaon/hTPCNSigma"), track.tpcNSigmaKa()); + histos.fill(HIST("QA/Kstar/Kaon/hTOFNSigma"), track.tofNSigmaKa()); + histos.fill(HIST("QA/Kstar/Kaon/hDCAxy"), track.dcaXY()); + histos.fill(HIST("QA/Kstar/Kaon/hDCAz"), track.dcaZ()); + histos.fill(HIST("QA/Kstar/Kaon/hTPCCrossedRows"), track.tpcNClsCrossedRows()); + histos.fill(HIST("QA/Kstar/Kaon/hPtVsDCAxy"), track.pt(), track.dcaXY()); + histos.fill(HIST("QA/Kstar/Kaon/hPtVsDCAz"), track.pt(), track.dcaZ()); + histos.fill(HIST("QA/Kstar/Kaon/hPtVsTPCNSigma"), track.pt(), track.tpcNSigmaKa()); + histos.fill(HIST("QA/Kstar/Kaon/hPtVsTOFNSigma"), track.pt(), track.tofNSigmaKa()); + if (track.hasTOF()) { + histos.fill(HIST("QA/Kstar/Kaon/hTPCNSigmaVsTOFNSigma"), track.tpcNSigmaKa(), track.tofNSigmaKa()); } + } - zorro.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), zorroMask.value); - zorro.populateHistRegistry(histos, bc.runNumber()); + template + void fillKstarPionQA(const Track& track) + { + histos.fill(HIST("QA/Kstar/Pion/hPt"), track.pt()); + histos.fill(HIST("QA/Kstar/Pion/hEta"), track.eta()); + histos.fill(HIST("QA/Kstar/Pion/hTPCNSigma"), track.tpcNSigmaPi()); + histos.fill(HIST("QA/Kstar/Pion/hTOFNSigma"), track.tofNSigmaPi()); + histos.fill(HIST("QA/Kstar/Pion/hDCAxy"), track.dcaXY()); + histos.fill(HIST("QA/Kstar/Pion/hDCAz"), track.dcaZ()); + histos.fill(HIST("QA/Kstar/Pion/hTPCCrossedRows"), track.tpcNClsCrossedRows()); + histos.fill(HIST("QA/Kstar/Pion/hPtVsDCAxy"), track.pt(), track.dcaXY()); + histos.fill(HIST("QA/Kstar/Pion/hPtVsDCAz"), track.pt(), track.dcaZ()); + histos.fill(HIST("QA/Kstar/Pion/hPtVsTPCNSigma"), track.pt(), track.tpcNSigmaPi()); + histos.fill(HIST("QA/Kstar/Pion/hPtVsTOFNSigma"), track.pt(), track.tofNSigmaPi()); + if (track.hasTOF()) { + histos.fill(HIST("QA/Kstar/Pion/hTPCNSigmaVsTOFNSigma"), track.tpcNSigmaPi(), track.tofNSigmaPi()); + } + } - mRunNumber = bc.runNumber(); + // Trigger hadrons carry no PID selection, so only the 5 non-PID variables + // are filled here. + template + void fillTriggerHadronQA(const Track& track) + { + histos.fill(HIST("QA/TriggerHadron/hPt"), track.pt()); + histos.fill(HIST("QA/TriggerHadron/hEta"), track.eta()); + histos.fill(HIST("QA/TriggerHadron/hDCAxy"), track.dcaXY()); + histos.fill(HIST("QA/TriggerHadron/hDCAz"), track.dcaZ()); + histos.fill(HIST("QA/TriggerHadron/hTPCCrossedRows"), track.tpcNClsCrossedRows()); + histos.fill(HIST("QA/TriggerHadron/hPtVsDCAxy"), track.pt(), track.dcaXY()); + histos.fill(HIST("QA/TriggerHadron/hPtVsDCAz"), track.pt(), track.dcaZ()); } template - bool isSelected(const Coll& collision, bool fillHist = true) + bool isSelectedEvents(const Coll& collision, bool fillHist = true) { auto applyCut = [&](bool enabled, bool condition, int bin) { if (!enabled) { @@ -379,76 +487,32 @@ struct HResonanceCorrelationFilter { return false; } - if (!applyCut(configEvents.cfgEvtIsRCTFlagpassed, rctChecker(collision), 5)) { + if (!applyCut(configEvents.cfgEvtSel8, collision.sel8(), 5)) { return false; } - if (!applyCut(configEvents.cfgEvtSel8, collision.sel8(), 6)) { + if (!applyCut(configEvents.cfgEvtNoSameBunchPileupCut, + collision.selection_bit(aod::evsel::kNoSameBunchPileup), 6)) { return false; } - if (!applyCut(configEvents.cfgEvtIsINELgt0, collision.isInelGt0(), 7)) { + if (!applyCut(configEvents.cfgEvtGoodZvtxFT0vsPVCut, + collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV), 7)) { return false; } if (fillHist) { histos.fill(HIST("CollCutCounts"), 8); - } - - return true; - } - - // this function allows for all event selections to be done in a modular way - template - bool isCollisionSelected(TCollision const& collision) - { - // ________________________________________________ - // Perform basic event selection - if (!collision.sel8()) { - return false; - } - if (std::abs(collision.posZ()) > eventSelections.zVertexCut) { - return false; - } - if (collision.centFT0M() > eventSelections.maxCentPercent || collision.centFT0M() < eventSelections.minCentPercent) { - return false; - } - if (!collision.isInelGt0() && eventSelections.selectINELgtZERO) { - return false; - } - if (!collision.selection_bit(aod::evsel::kIsGoodITSLayersAll) && eventSelections.requireAllGoodITSLayers) { - return false; - } - if (zorroMask.value != "") { - auto bc = collision.template bc_as(); - initCCDB(bc); - bool zorroSelected = zorro.isSelected(collision.template bc_as().globalBC()); /// Just let Zorro do the accounting - if (!zorroSelected) { - return false; + histos.fill(HIST("hVertexZSelected"), collision.posZ()); + // Guarded with if constexpr: isSelectedEvents() is called with several + // different collision join types (some callers pass fillHist=false and + // don't join centrality tables at all), so this only compiles/fills for + // callers whose collision type actually has both estimators joined. + if constexpr (requires { collision.centFT0C(); collision.centFV0A(); }) { + histos.fill(HIST("hCentralitySelected"), configEvents.cfgUseFV0ACentrality ? collision.centFV0A() : collision.centFT0C()); } } - return true; - } - // more event selections in Pb-Pb - template - bool isCollisionSelectedPbPb(TCollision const& collision) - { - if (!collision.selection_bit(aod::evsel::kIsTriggerTVX) && eventSelections.requireGoodTriggerTVX) /* FT0 vertex (acceptable FT0C-FT0A time difference) collisions */ - return false; - if (!collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll) && eventSelections.requireAllGoodITSLayers) // cut time intervals with dead ITS staves - return false; - if (!collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV) && eventSelections.requireGoodZvtxFT0vsPV) // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference - return false; - auto occupancy = collision.trackOccupancyInTimeRange(); - if (occupancy < cfgCutOccupancyLow || occupancy > cfgCutOccupancyHigh) /* Below min occupancy and Above max occupancy*/ - return false; - if (!collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) // reject collisions close to Time Frame borders - return false; - if (!collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) // reject events affected by the ITS ROF border - return false; - if (!collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) // rejects collisions which are associated with the same "found-by-T0" bunch crossing - return false; return true; } @@ -456,29 +520,31 @@ struct HResonanceCorrelationFilter { bool trackCut(const TrackType& track) { // basic track cuts - if (configTracks.cDCAr7SigCut && std::abs(track.dcaXY()) > (0.004f + 0.013f / (track.pt()))) // 7 - Sigma cut - { + // NOTE: pT-dependent |DCAxy| cut is NOT re-applied here -- it's already + // enforced unconditionally, for every track in every process function, + // by acceptanceFilter (Filter on aod::track::dcaXY using the live + // configTracks.dcaXYconstant/dcaXYpTdep). A second check here would be + // either a no-op (same cut) or a stale one (if those configurables are + // ever retuned and this duplicate doesn't track them). + if (configTracks.cfgGlobalWoDCATrack && !track.isGlobalTrackWoDCA()) { return false; } - if (configTracks.cTPCNClsFound && (track.tpcNClsFound() < configTracks.cMinTPCNClsFound)) { + if (configTracks.cfgPVContributor && !track.isPVContributor()) { return false; } if (track.tpcNClsCrossedRows() < configTracks.cfgMinCrossedRows) { return false; } - if (configTracks.cfgHasTOF && !track.hasTOF()) { - return false; - } - if (configTracks.cfgPrimaryTrack && !track.isPrimaryTrack()) { + if (track.tpcChi2NCl() > configTracks.cfgMaxTPCChi2NCl) { return false; } - if (configTracks.cfgGlobalWoDCATrack && !track.isGlobalTrackWoDCA()) { + if (track.itsChi2NCl() > configTracks.cfgMaxITSChi2NCl) { return false; } - if (configTracks.cfgPVContributor && !track.isPVContributor()) { + if (track.tpcCrossedRowsOverFindableCls() < configTracks.cfgMinTPCCrossedRowsOverFindableCls) { return false; } - if (configTracks.cfgGlobalTrack && !track.isGlobalTrack()) { + if (track.itsNCls() < configTracks.cfgMinITSNCls) { return false; } @@ -669,23 +735,28 @@ struct HResonanceCorrelationFilter { template bool isValidTrigger(TTrack const& track) { - if (track.eta() > generalSelections.triggerEtaMax || track.eta() < generalSelections.triggerEtaMin) { + // Shared "good track" baseline, applied identically across trigger, + // associated-particle, and resonance-daughter tracks (see trackCut()). + if (!trackCut(track)) { + return false; + } + if (track.eta() > configTriggerTracks.triggerEtaMax || track.eta() < configTriggerTracks.triggerEtaMin) { return false; } // if (track.sign()= 1 ) {continue;} - if (track.pt() > generalSelections.triggerPtCutMax || track.pt() < generalSelections.triggerPtCutMin) { + if (track.pt() > configTriggerTracks.triggerPtCutMax || track.pt() < configTriggerTracks.triggerPtCutMin) { return false; } - if (track.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + if (track.tpcNClsCrossedRows() < configTracks.minTPCNCrossedRows) { return false; // crossed rows } - if (!track.hasITS() && trackSelections.triggerRequireITS) { + if (!track.hasITS() && configTriggerTracks.triggerRequireITS) { return false; // skip, doesn't have ITS signal (skips lots of TPC-only!) } - if (track.tpcNClsShared() > trackSelections.triggerMaxTPCSharedClusters) { + if (track.tpcNClsShared() > configTriggerTracks.triggerMaxTPCSharedClusters) { return false; // skip, has shared clusters } - if (!(BIT_CHECK(track.itsClusterMap(), 0)) && trackSelections.triggerRequireL0) { + if (!(BIT_CHECK(track.itsClusterMap(), 0)) && configTriggerTracks.triggerRequireL0) { return false; // skip, doesn't have cluster in ITS L0 } return true; @@ -715,16 +786,21 @@ struct HResonanceCorrelationFilter { { static_assert(Species == AssocPion || Species == AssocKaon || Species == AssocHadron, "isValidAssocTrack: unknown species tag"); - if (assoc.eta() > generalSelections.assocEtaMax || assoc.eta() < generalSelections.assocEtaMin) { + // Shared "good track" baseline, applied identically across trigger, + // associated-particle, and resonance-daughter tracks (see trackCut()). + if (!trackCut(assoc)) { return false; } - if (assoc.pt() > generalSelections.assocPtCutMax || assoc.pt() < generalSelections.assocPtCutMin) { + if (assoc.eta() > configAssocTracks.assocEtaMax || assoc.eta() < configAssocTracks.assocEtaMin) { return false; } - if (assoc.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + if (assoc.pt() > configAssocTracks.assocPtCutMax || assoc.pt() < configAssocTracks.assocPtCutMin) { + return false; + } + if (assoc.tpcNClsCrossedRows() < configTracks.minTPCNCrossedRows) { return false; // crossed rows } - if (!assoc.hasITS() && trackSelections.assocRequireITS) { + if (!assoc.hasITS() && configAssocTracks.assocRequireITS) { return false; // skip, doesn't have ITS signal (skips lots of TPC-only!) } @@ -733,26 +809,26 @@ struct HResonanceCorrelationFilter { if constexpr (requires { assoc.tofSignal(); } && !requires { assoc.mcParticle(); }) { if (assoc.tofSignal() > 0) { if constexpr (Species == AssocKaon) { - if (std::sqrt(assoc.tofNSigmaKa() * assoc.tofNSigmaKa() + assoc.tpcNSigmaKa() * assoc.tpcNSigmaKa()) > trackSelections.assocKaonNSigmaTPCFOF) + if (std::sqrt(assoc.tofNSigmaKa() * assoc.tofNSigmaKa() + assoc.tpcNSigmaKa() * assoc.tpcNSigmaKa()) > configPID.assocKaonNSigmaTPCFOF) return false; - if (assoc.tofNSigmaPr() < trackSelections.rejectSigma) + if (assoc.tofNSigmaPr() < configPID.rejectSigma) return false; - if (assoc.tpcNSigmaPr() < trackSelections.rejectSigma) + if (assoc.tpcNSigmaPr() < configPID.rejectSigma) return false; - if (assoc.tofNSigmaPi() < trackSelections.rejectSigma) + if (assoc.tofNSigmaPi() < configPID.rejectSigma) return false; - if (assoc.tpcNSigmaPi() < trackSelections.rejectSigma) + if (assoc.tpcNSigmaPi() < configPID.rejectSigma) return false; } else { - if (std::sqrt(assoc.tofNSigmaPi() * assoc.tofNSigmaPi() + assoc.tpcNSigmaPi() * assoc.tpcNSigmaPi()) > trackSelections.assocPionNSigmaTPCFOF) + if (std::sqrt(assoc.tofNSigmaPi() * assoc.tofNSigmaPi() + assoc.tpcNSigmaPi() * assoc.tpcNSigmaPi()) > configPID.assocPionNSigmaTPCFOF) return false; - if (assoc.tofNSigmaPr() < trackSelections.rejectSigma) + if (assoc.tofNSigmaPr() < configPID.rejectSigma) return false; - if (assoc.tpcNSigmaPr() < trackSelections.rejectSigma) + if (assoc.tpcNSigmaPr() < configPID.rejectSigma) return false; - if (assoc.tofNSigmaKa() < trackSelections.rejectSigma) + if (assoc.tofNSigmaKa() < configPID.rejectSigma) return false; - if (assoc.tpcNSigmaKa() < trackSelections.rejectSigma) + if (assoc.tpcNSigmaKa() < configPID.rejectSigma) return false; } nSigmaTPCTOF[4] = assoc.tofNSigmaPi(); @@ -761,18 +837,18 @@ struct HResonanceCorrelationFilter { nSigmaTPCTOF[7] = assoc.tofNSigmaEl(); } else { if constexpr (Species == AssocKaon) { - if (assoc.tpcNSigmaKa() > trackSelections.assocKaonNSigmaTPCFOF) + if (assoc.tpcNSigmaKa() > configPID.assocKaonNSigmaTPCFOF) return false; - if (assoc.tpcNSigmaPr() < trackSelections.rejectSigma) + if (assoc.tpcNSigmaPr() < configPID.rejectSigma) return false; - if (assoc.tpcNSigmaPi() < trackSelections.rejectSigma) + if (assoc.tpcNSigmaPi() < configPID.rejectSigma) return false; } else { - if (assoc.tpcNSigmaPi() > trackSelections.assocPionNSigmaTPCFOF) + if (assoc.tpcNSigmaPi() > configPID.assocPionNSigmaTPCFOF) return false; - if (assoc.tpcNSigmaPr() < trackSelections.rejectSigma) + if (assoc.tpcNSigmaPr() < configPID.rejectSigma) return false; - if (assoc.tpcNSigmaKa() < trackSelections.rejectSigma) + if (assoc.tpcNSigmaKa() < configPID.rejectSigma) return false; } } @@ -794,12 +870,26 @@ struct HResonanceCorrelationFilter { } } + // Rapidity under the species mass hypothesis; AssocHadron has no mass + // hypothesis to assume, so it's filled with a dummy value instead. + float rapidity = -999.f; + if constexpr (Species == AssocKaon) { + rapidity = LorentzVectorPtEtaPhiMass(assoc.pt(), assoc.eta(), assoc.phi(), + o2::constants::physics::MassKPlus) + .Rapidity(); + } else if constexpr (Species == AssocPion) { + rapidity = LorentzVectorPtEtaPhiMass(assoc.pt(), assoc.eta(), assoc.phi(), + o2::constants::physics::MassPiPlus) + .Rapidity(); + } + assocHadrons( assoc.collisionId(), physicalPrimary, assoc.globalIndex(), origPt, - code); + code, + rapidity); assocPID( nSigmaTPCTOF[0], nSigmaTPCTOF[1], @@ -813,10 +903,10 @@ struct HResonanceCorrelationFilter { } // for real data processing - void processTriggers(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::BCsWithTimestamps const&) + void processTriggers(soa::Join::iterator const& collision, soa::Filtered const& tracks) { triggerCandidates.clear(); - if (((doPPAnalysis && !isCollisionSelected(collision))) || (!doPPAnalysis && !isCollisionSelectedPbPb(collision))) { + if (!isSelectedEvents(collision)) { return; } @@ -827,6 +917,7 @@ struct HResonanceCorrelationFilter { for (auto const& track : tracks) { if (!isValidTrigger(track)) continue; + fillTriggerHadronQA(track); thisTrigg.pt = track.pt(); thisTrigg.trackId = track.globalIndex(); thisTrigg.collisionId = track.collisionId(); @@ -851,10 +942,10 @@ struct HResonanceCorrelationFilter { } // for MC processing - void processTriggersMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&, aod::BCsWithTimestamps const&) + void processTriggersMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&) { triggerCandidates.clear(); - if (((doPPAnalysis && !isCollisionSelected(collision))) || (!doPPAnalysis && !isCollisionSelectedPbPb(collision))) { + if (!isSelectedEvents(collision)) { return; } @@ -865,6 +956,7 @@ struct HResonanceCorrelationFilter { for (auto const& track : tracks) { if (!isValidTrigger(track)) continue; + fillTriggerHadronQA(track); thisTrigg.pt = track.pt(); thisTrigg.trackId = track.globalIndex(); thisTrigg.collisionId = track.collisionId(); @@ -892,25 +984,12 @@ struct HResonanceCorrelationFilter { } } - void processAssocPions(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::BCsWithTimestamps const&) + void processAssocPions(soa::Join::iterator const& collision, soa::Filtered const& tracks) { - // Load parameters for sideband subtraction - auto bc = collision.bc_as(); - // Perform basic event selection - if (!collision.sel8()) { + // Perform basic event selection (fillHist=false: processTriggers/processTriggersMC own the shared CollCutCounts fill) + if (!isSelectedEvents(collision, false)) { return; } - // No need to correlate stuff that's in far collisions - if (std::abs(collision.posZ()) > eventSelections.zVertexCut) { - return; - } - if (zorroMask.value != "") { - initCCDB(bc); - bool zorroSelected = zorro.isSelected(collision.bc_as().globalBC()); /// Just let Zorro do the accounting - if (!zorroSelected) { - return; - } - } /// _________________________________________________ /// Step 1: Populate table with trigger tracks @@ -920,25 +999,12 @@ struct HResonanceCorrelationFilter { } } - void processAssocPionsMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&, aod::BCsWithTimestamps const&) + void processAssocPionsMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&) { - // Load parameters for sideband subtraction - auto bc = collision.bc_as(); - // Perform basic event selection - if (!collision.sel8()) { + // Perform basic event selection (fillHist=false: processTriggers/processTriggersMC own the shared CollCutCounts fill) + if (!isSelectedEvents(collision, false)) { return; } - // No need to correlate stuff that's in far collisions - if (std::abs(collision.posZ()) > eventSelections.zVertexCut) { - return; - } - if (zorroMask.value != "") { - initCCDB(bc); - bool zorroSelected = zorro.isSelected(collision.bc_as().globalBC()); /// Just let Zorro do the accounting - if (!zorroSelected) { - return; - } - } /// _________________________________________________ /// Step 1: Populate table with trigger tracks @@ -948,25 +1014,12 @@ struct HResonanceCorrelationFilter { } } - void processAssocKaons(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::BCsWithTimestamps const&) + void processAssocKaons(soa::Join::iterator const& collision, soa::Filtered const& tracks) { - // Load parameters for sideband subtraction - auto bc = collision.bc_as(); - // Perform basic event selection - if (!collision.sel8()) { + // Perform basic event selection (fillHist=false: processTriggers/processTriggersMC own the shared CollCutCounts fill) + if (!isSelectedEvents(collision, false)) { return; } - // No need to correlate stuff that's in far collisions - if (std::abs(collision.posZ()) > eventSelections.zVertexCut) { - return; - } - if (zorroMask.value != "") { - initCCDB(bc); - bool zorroSelected = zorro.isSelected(collision.bc_as().globalBC()); /// Just let Zorro do the accounting - if (!zorroSelected) { - return; - } - } /// _________________________________________________ /// Step 1: Populate table with trigger tracks @@ -976,25 +1029,12 @@ struct HResonanceCorrelationFilter { } } - void processAssocKaonsMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&, aod::BCsWithTimestamps const&) + void processAssocKaonsMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&) { - // Load parameters for sideband subtraction - auto bc = collision.bc_as(); - // Perform basic event selection - if (!collision.sel8()) { + // Perform basic event selection (fillHist=false: processTriggers/processTriggersMC own the shared CollCutCounts fill) + if (!isSelectedEvents(collision, false)) { return; } - // No need to correlate stuff that's in far collisions - if (std::abs(collision.posZ()) > eventSelections.zVertexCut) { - return; - } - if (zorroMask.value != "") { - initCCDB(bc); - bool zorroSelected = zorro.isSelected(collision.bc_as().globalBC()); /// Just let Zorro do the accounting - if (!zorroSelected) { - return; - } - } /// _________________________________________________ /// Step 1: Populate table with trigger tracks @@ -1004,25 +1044,12 @@ struct HResonanceCorrelationFilter { } } - void processAssocHadrons(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::BCsWithTimestamps const&) + void processAssocHadrons(soa::Join::iterator const& collision, soa::Filtered const& tracks) { - // Load parameters for sideband subtraction - auto bc = collision.bc_as(); - // Perform basic event selection - if (!collision.sel8()) { + // Perform basic event selection (fillHist=false: processTriggers/processTriggersMC own the shared CollCutCounts fill) + if (!isSelectedEvents(collision, false)) { return; } - // No need to correlate stuff that's in far collisions - if (std::abs(collision.posZ()) > eventSelections.zVertexCut) { - return; - } - if (zorroMask.value != "") { - initCCDB(bc); - bool zorroSelected = zorro.isSelected(collision.bc_as().globalBC()); /// Just let Zorro do the accounting - if (!zorroSelected) { - return; - } - } /// _________________________________________________ /// Step 1: Populate table with trigger tracks @@ -1031,25 +1058,12 @@ struct HResonanceCorrelationFilter { continue; } } - void processAssocHadronsMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&, aod::BCsWithTimestamps const&) + void processAssocHadronsMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&) { - // Load parameters for sideband subtraction - auto bc = collision.bc_as(); - // Perform basic event selection - if (!collision.sel8()) { + // Perform basic event selection (fillHist=false: processTriggers/processTriggersMC own the shared CollCutCounts fill) + if (!isSelectedEvents(collision, false)) { return; } - // No need to correlate stuff that's in far collisions - if (std::abs(collision.posZ()) > eventSelections.zVertexCut) { - return; - } - if (zorroMask.value != "") { - initCCDB(bc); - bool zorroSelected = zorro.isSelected(collision.bc_as().globalBC()); /// Just let Zorro do the accounting - if (!zorroSelected) { - return; - } - } /// _________________________________________________ /// Step 1: Populate table with trigger tracks @@ -1062,7 +1076,7 @@ struct HResonanceCorrelationFilter { void processPhis(EventCandidates::iterator const& collision, TrackCandidates const& tracks) { - if (!isSelected(collision)) { + if (!isSelectedEvents(collision, false)) { return; } @@ -1124,7 +1138,7 @@ struct HResonanceCorrelationFilter { float invMass = phi.M(); // rapidity cut - if (std::abs(phi.Rapidity()) > configTracks.cfgCutRapidity) { + if (std::abs(phi.Rapidity()) > configResoDauTracks.cfgCutRapidity) { continue; } @@ -1133,6 +1147,10 @@ struct HResonanceCorrelationFilter { continue; } + // Daughter QA (both legs are kaons for Phi) + fillPhiKaonQA(trk1); + fillPhiKaonQA(trk2); + assocPhis( collision.globalIndex(), false, @@ -1141,6 +1159,7 @@ struct HResonanceCorrelationFilter { phi.Eta(), phi.Phi(), invMass, + phi.Rapidity(), trk1.globalIndex(), trk2.globalIndex()); } @@ -1156,7 +1175,7 @@ struct HResonanceCorrelationFilter { return; } - if (!isSelected(collision)) { + if (!isSelectedEvents(collision, false)) { return; } @@ -1210,7 +1229,7 @@ struct HResonanceCorrelationFilter { float invMass = phi.M(); // rapidity cut - if (std::abs(phi.Rapidity()) > configTracks.cfgCutRapidity) { + if (std::abs(phi.Rapidity()) > configResoDauTracks.cfgCutRapidity) { continue; } @@ -1219,6 +1238,10 @@ struct HResonanceCorrelationFilter { continue; } + // Daughter QA (both legs are kaons for Phi) + fillPhiKaonQA(trk1); + fillPhiKaonQA(trk2); + bool mcTruePhi = false; bool mcPhysicalPrimary = false; @@ -1259,6 +1282,7 @@ struct HResonanceCorrelationFilter { phi.Eta(), phi.Phi(), invMass, + phi.Rapidity(), trk1.globalIndex(), trk2.globalIndex()); } @@ -1271,7 +1295,7 @@ struct HResonanceCorrelationFilter { void processKstars(EventCandidates::iterator const& collision, TrackCandidates const& tracks) { - if (!isSelected(collision)) { + if (!isSelectedEvents(collision, false)) { return; } @@ -1323,7 +1347,7 @@ struct HResonanceCorrelationFilter { float invMass = kstar.M(); // rapidity cut - if (std::abs(kstar.Rapidity()) > configTracks.cfgCutRapidity) { + if (std::abs(kstar.Rapidity()) > configResoDauTracks.cfgCutRapidity) { continue; } @@ -1332,8 +1356,15 @@ struct HResonanceCorrelationFilter { continue; } + // Daughter QA (kaon leg and pion leg filled separately) + fillKstarKaonQA(kaonTrack); + fillKstarPionQA(pionTrack); + auto const& posTrack = (kaonTrack.sign() > 0) ? kaonTrack : pionTrack; auto const& negTrack = (kaonTrack.sign() > 0) ? pionTrack : kaonTrack; + // Rapidity under whichever mass hypothesis (kaon or pion) was + // actually assigned to that daughter for this hypothesis -- matches + // posTrack/negTrack above, not a fixed species per pos/neg slot. assocKstars( collision.globalIndex(), @@ -1343,6 +1374,7 @@ struct HResonanceCorrelationFilter { kstar.Eta(), kstar.Phi(), invMass, + kstar.Rapidity(), posTrack.globalIndex(), negTrack.globalIndex()); } @@ -1359,7 +1391,7 @@ struct HResonanceCorrelationFilter { return; } - if (!isSelected(collision)) { + if (!isSelectedEvents(collision, false)) { return; } @@ -1404,7 +1436,7 @@ struct HResonanceCorrelationFilter { float invMass = kstar.M(); // rapidity cut - if (std::abs(kstar.Rapidity()) > configTracks.cfgCutRapidity) { + if (std::abs(kstar.Rapidity()) > configResoDauTracks.cfgCutRapidity) { continue; } @@ -1413,6 +1445,10 @@ struct HResonanceCorrelationFilter { continue; } + // Daughter QA (kaon leg and pion leg filled separately) + fillKstarKaonQA(kaonTrack); + fillKstarPionQA(pionTrack); + bool mcTrueKstar = false; bool mcPhysicalPrimary = false; @@ -1456,6 +1492,7 @@ struct HResonanceCorrelationFilter { kstar.Eta(), kstar.Phi(), invMass, + kstar.Rapidity(), posTrack.globalIndex(), negTrack.globalIndex()); } diff --git a/PWGLF/TableProducer/Resonances/resonanceModuleInitializer.cxx b/PWGLF/TableProducer/Resonances/resonanceModuleInitializer.cxx index d5a43cf1028..57640b2d3fc 100644 --- a/PWGLF/TableProducer/Resonances/resonanceModuleInitializer.cxx +++ b/PWGLF/TableProducer/Resonances/resonanceModuleInitializer.cxx @@ -110,9 +110,9 @@ struct ResonanceModuleInitializer { static constexpr int PdgLambda1520 = o2::constants::physics::Pdg::kLambda1520_Py; static constexpr int Xi1530Code = 3324; static constexpr int PdgK1Plus1270 = o2::constants::physics::Pdg::kK1_1270Plus; - static constexpr int Xi1820NeutralCode = 123314; - static constexpr int Xi1820MinusCode = 123324; - static constexpr int Omega2012MinusCode = 123334; + static constexpr int Xi1820MinusCode = o2::constants::physics::Pdg::kXi1820Minus; + static constexpr int Xi1820ZeroCode = o2::constants::physics::Pdg::kXi1820Zero; + static constexpr int Omega2012MinusCode = o2::constants::physics::Pdg::kOmega2012Minus; static constexpr int PdgProton = PDG_t::kProton; static constexpr int PdgLambda0 = PDG_t::kLambda0; static constexpr int PdgXiMinus = PDG_t::kXiMinus; @@ -232,7 +232,7 @@ struct ResonanceModuleInitializer { || (nabs(aod::mcparticle::pdgCode) == PdgLambda1520) // Lambda(1520) || (nabs(aod::mcparticle::pdgCode) == Xi1530Code) // Xi(1530)0 || (nabs(aod::mcparticle::pdgCode) == PdgK1Plus1270) // K1(1270)+ - || (nabs(aod::mcparticle::pdgCode) == Xi1820NeutralCode) // Xi(1820)0 + || (nabs(aod::mcparticle::pdgCode) == Xi1820ZeroCode) // Xi(1820)0 || (nabs(aod::mcparticle::pdgCode) == Xi1820MinusCode) // Xi(1820)- || (nabs(aod::mcparticle::pdgCode) == Omega2012MinusCode) // Omega(2012)- || (nabs(aod::mcparticle::pdgCode) == PdgProton) // proton @@ -273,9 +273,25 @@ struct ResonanceModuleInitializer { } LOGF(info, "Stored collision multiplicity estimator: %d", EventConfig.cfgMultiplicityEstimator.value); + const int enabledGenProcesses = static_cast(doprocessMCgen) + + static_cast(doprocessMCgenFT0M) + + static_cast(doprocessMCgenFT0C) + + static_cast(doprocessMCgenFV0A) + + static_cast(doprocessMCgenNoCent); + const bool anyGenProcess = enabledGenProcesses > 0; + if (enabledGenProcesses > 1) { + LOGF(fatal, "Enable only one generator process: processMCgen, processMCgenFT0M, processMCgenFT0C, processMCgenFV0A, or processMCgenNoCent"); + } + if (doprocessMCgenNoCent && GenCuts.cfgGenMultPercentile) { + LOGF(fatal, "processMCgenNoCent requires cfgGenMultPercentile=false"); + } + if ((doprocessMCgenFT0M || doprocessMCgenFT0C || doprocessMCgenFV0A) && !GenCuts.cfgGenMultPercentile) { + LOGF(fatal, "The estimator-specific generator processes require cfgGenMultPercentile=true; use processMCgenNoCent otherwise"); + } + // Run 2 and Run 3 callbacks require different event-selection semantics. const bool anyRun2Process = doprocessRun2 || doprocessRun2MC; - const bool anyRun3Process = doprocessRun3 || doprocessRun3MC || doprocessMCgen; + const bool anyRun3Process = doprocessRun3 || doprocessRun3MC || anyGenProcess; if (anyRun2Process && anyRun3Process) { LOG(fatal) << "Run 2 and Run 3 processes cannot be enabled in the same ResonanceModuleInitializer"; } @@ -290,13 +306,13 @@ struct ResonanceModuleInitializer { static_cast(GenCuts.cfgGenMultFT0M.value) + static_cast(GenCuts.cfgGenMultFT0C.value) + static_cast(GenCuts.cfgGenMultFV0A.value); - if ((doprocessMCgen || doprocessRun2MC || doprocessRun3MC) && enabledGenMultiplicityEstimators > 1) { + if ((anyGenProcess || doprocessRun2MC || doprocessRun3MC) && enabledGenMultiplicityEstimators > 1) { LOG(fatal) << "Only one generator multiplicity estimator can be enabled: cfgGenMult05, cfgGenMult10, cfgGenMultFT0M, cfgGenMultFT0C, or cfgGenMultFV0A"; } - if (doprocessMCgen) { - if (GenCuts.cfgGenMultPercentile && multEstimator != CentralityFT0M && + if (anyGenProcess) { + if (doprocessMCgen && GenCuts.cfgGenMultPercentile && multEstimator != CentralityFT0M && multEstimator != CentralityFT0C && multEstimator != CentralityFV0A) { - LOGF(fatal, "cfgGenMultPercentile supports cfgMultName=FT0M, FT0C, or FV0A"); + LOGF(fatal, "processMCgen with cfgGenMultPercentile supports cfgMultName=FT0M, FT0C, or FV0A"); } if (GenCuts.isZvtxcutGen && (!std::isfinite(GenCuts.cutzvertexGen.value) || GenCuts.cutzvertexGen.value <= 0.f)) { @@ -362,7 +378,7 @@ struct ResonanceModuleInitializer { ccdb->setCreatedNotAfter(now); // TODO must become global parameter from the train creation time } - if (doprocessMCgen) { + if (anyGenProcess) { constexpr std::array MCEventLabels{"All", "z vertex", "BC RCT", "INEL", "INEL>0"}; AxisSpec centAxisGen = {GenCuts.binsCentGen, "Centrality (%)"}; AxisSpec eventTypeAxis = {2, 0.f, 2.f, "Event type"}; @@ -533,7 +549,14 @@ struct ResonanceModuleInitializer { fillDetailedCollisionQA(collision, o2::analysis::CollisonCuts::kAllpassed); } + // Keep the original input for existing processMCgen configurations. The + // dedicated callbacks select the MC percentile independently of cfgMultName + // (reconstructed centrality) and cfgGenMult* (generator multiplicity). using GenMCCollisions = soa::Join; + using GenMCCollisionsFT0M = soa::Join; + using GenMCCollisionsFT0C = soa::Join; + using GenMCCollisionsFV0A = soa::Join; + using GenMCCollisionsNoCent = soa::Join; using Run3MCCollisions = soa::Join; using Run2MCCollisions = soa::Join; using GenRecoCollisions = soa::Join; @@ -846,19 +869,19 @@ struct ResonanceModuleInitializer { * reduced AOD tables. RCT quality is evaluated through the generator * collision's associated BC because it is a run-condition property. */ - void processMCgen(GenMCCollisions::iterator const& mcCollision, - aod::McParticles const& mcParticles, - soa::SmallGroups const& collisions, - BCsWithRCT const&) + template + void processMCgenQA(MCCollision const& mcCollision, + aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions, + float generatorCentrality) { - auto bc = mcCollision.bc_as(); + auto bc = mcCollision.template bc_as(); initCCDB(bc); const auto getReconstructedCentrality = [&](auto const& collision) { return centEst(collision); }; - const float generatorCentrality = getMCCentrality(mcCollision); const float impactParameter = mcCollision.impactParameter(); const float multiplicity = getMCMultiplicity(mcCollision); @@ -910,7 +933,51 @@ struct ResonanceModuleInitializer { qaRegistry.fill(HIST("EventGen/h4MultCent_recMC"), eventType, reconstructedCentrality, multiplicity, impactParameter); } } - PROCESS_SWITCH(ResonanceModuleInitializer, processMCgen, "Process generator-level MC QA", false); + + void processMCgen(GenMCCollisions::iterator const& mcCollision, + aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions, + BCsWithRCT const&) + { + processMCgenQA(mcCollision, mcParticles, collisions, getMCCentrality(mcCollision)); + } + PROCESS_SWITCH(ResonanceModuleInitializer, processMCgen, "Process generator-level MC QA with all MC percentile inputs (legacy)", false); + + void processMCgenFT0M(GenMCCollisionsFT0M::iterator const& mcCollision, + aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions, + BCsWithRCT const&) + { + processMCgenQA(mcCollision, mcParticles, collisions, mcCollision.centFT0M()); + } + PROCESS_SWITCH(ResonanceModuleInitializer, processMCgenFT0M, "Process generator-level MC QA with only the FT0M MC percentile input", false); + + void processMCgenFT0C(GenMCCollisionsFT0C::iterator const& mcCollision, + aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions, + BCsWithRCT const&) + { + processMCgenQA(mcCollision, mcParticles, collisions, mcCollision.centFT0C()); + } + PROCESS_SWITCH(ResonanceModuleInitializer, processMCgenFT0C, "Process generator-level MC QA with only the FT0C MC percentile input", false); + + void processMCgenFV0A(GenMCCollisionsFV0A::iterator const& mcCollision, + aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions, + BCsWithRCT const&) + { + processMCgenQA(mcCollision, mcParticles, collisions, mcCollision.centFV0A()); + } + PROCESS_SWITCH(ResonanceModuleInitializer, processMCgenFV0A, "Process generator-level MC QA with only the FV0A MC percentile input", false); + + void processMCgenNoCent(GenMCCollisionsNoCent::iterator const& mcCollision, + aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions, + BCsWithRCT const&) + { + processMCgenQA(mcCollision, mcParticles, collisions, 100.5f); + } + PROCESS_SWITCH(ResonanceModuleInitializer, processMCgenNoCent, "Process generator-level MC QA using reconstructed centrality without MC percentile inputs", false); /** * @brief Processes Run3 MC data diff --git a/PWGLF/TableProducer/Resonances/xi1530kaonreducedtable.cxx b/PWGLF/TableProducer/Resonances/xi1530kaonreducedtable.cxx index 149879b75d5..a13f5a57ae2 100644 --- a/PWGLF/TableProducer/Resonances/xi1530kaonreducedtable.cxx +++ b/PWGLF/TableProducer/Resonances/xi1530kaonreducedtable.cxx @@ -27,14 +27,18 @@ #include #include #include +#include #include #include #include +#include #include #include +#include #include -#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include #include #include @@ -508,8 +512,7 @@ struct xi1530kaonreducedtable { { auto* grpo = ccdb->getForTimeStamp("/GLO/Config/GRPMagField", timestamp); if (grpo == nullptr) { - LOGF(fatal, "GRPMagField object not found in CCDB for timestamp %llu", - static_cast(timestamp)); + LOGF(fatal, "GRPMagField object not found in CCDB for timestamp %llu", timestamp); return 0.f; } return 0.1f * grpo->getNominalL3Field(); // kG -> T diff --git a/PWGLF/TableProducer/Strangeness/CMakeLists.txt b/PWGLF/TableProducer/Strangeness/CMakeLists.txt index a61a01e6a56..a0a64e95190 100644 --- a/PWGLF/TableProducer/Strangeness/CMakeLists.txt +++ b/PWGLF/TableProducer/Strangeness/CMakeLists.txt @@ -13,26 +13,31 @@ add_subdirectory(Converters) o2physics_add_dpl_workflow(cascadefinder SOURCES cascadefinder.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::DCAFitter O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(cascadeflow SOURCES cascadeflow.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(cascademcbuilder SOURCES cascademcbuilder.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(cascademcfinder SOURCES cascademcfinder.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(strangenesstofpid SOURCES strangenesstofpid.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) @@ -43,36 +48,48 @@ o2physics_add_dpl_workflow(cascadespawner o2physics_add_dpl_workflow(cascqaanalysis SOURCES cascqaanalysis.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(cascderivedqaanalysis SOURCES cascderivedqaanalysis.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(hstrangecorrelationfilter # o2-linter: disable=name/o2-workflow (Keep historical workflow name for backward compatibility.) SOURCES hStrangeCorrelationFilter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::DCAFitter O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(double-omega-tree-creator SOURCES doubleOmegaTreeCreator.cxx + REUSE_FROM LfStrangenessPCH + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(double-omega-builder + SOURCES doubleOmegaBuilder.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdakzerofinder SOURCES lambdakzerofinder.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::DCAFitter O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdakzeromcbuilder SOURCES lambdakzeromcbuilder.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdakzeromcfinder SOURCES lambdakzeromcfinder.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -88,7 +105,8 @@ o2physics_add_dpl_workflow(sigmaminus-task # o2-linter: disable=name/o2-workflow o2physics_add_dpl_workflow(sigmaplusbuilder SOURCES sigmaplusbuilder.cxx - PUBLIC_LINK_LIBRARIES O2::DCAFitter O2Physics::AnalysisCore + REUSE_FROM LfStrangenessPCH + PUBLIC_LINK_LIBRARIES O2::DCAFitter O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(strange-tree-creator @@ -98,77 +116,92 @@ o2physics_add_dpl_workflow(strange-tree-creator o2physics_add_dpl_workflow(st-coll-ids SOURCES stCollIds.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(strangederivedbuilder SOURCES strangederivedbuilder.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(strangenessbuilder SOURCES strangenessbuilder.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter KFParticle::KFParticle O2Physics::TPCDriftManager O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(v0-selector # o2-linter: disable=name/o2-workflow (Keep historical workflow name for backward compatibility.) SOURCES v0selector.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(v0qaanalysis SOURCES v0qaanalysis.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdalambdatable # o2-linter: disable=name/o2-workflow (Keep historical workflow name for backward compatibility.) SOURCES LambdaLambdatable.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DetectorsVertexing COMPONENT_NAME Analysis) # ML selection o2physics_add_dpl_workflow(lambdakzeromlselectiontreecreator # o2-linter: disable=name/o2-workflow (Keep historical workflow name for backward compatibility.) SOURCES lambdakzeroMLSelectionTreeCreator.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(cascademlselectiontreecreator # o2-linter: disable=name/o2-workflow (Keep historical workflow name for backward compatibility.) SOURCES cascadeMLSelectionTreeCreator.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdakzeromlselection SOURCES lambdakzeromlselection.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(cascademlselection SOURCES cascademlselection.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sigma0builder SOURCES sigma0builder.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::Framework O2::EMCALBase O2Physics::AnalysisCore O2Physics::MLCore O2Physics::AnalysisCCDB O2::DCAFitter COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdajetpolarizationbuilder # o2-linter: disable=name/o2-workflow (Keep historical workflow name for backward compatibility.) SOURCES lambdaJetpolarizationbuilder.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdajetpolarizationions # o2-linter: disable=name/o2-workflow (Keep historical workflow name for backward compatibility.) SOURCES lambdaJetPolarizationIons.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGJECore FastJet::FastJet O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stracents SOURCES stracents.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdaspincorrelation SOURCES lambdaspincorrelation.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -180,16 +213,19 @@ o2physics_add_dpl_workflow(sigmahadcorr # o2-linter: disable=name/o2-workflow (K if(FastJet_FOUND) o2physics_add_dpl_workflow(lfinjets SOURCES lfinjets.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGJECore FastJet::FastJet FastJet::Contrib O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) endif() o2physics_add_dpl_workflow(phi-strange-correlator SOURCES phiStrangeCorrelator.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(strangeness-mc-centrality SOURCES strangenessMcCentrality.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) diff --git a/PWGLF/TableProducer/Strangeness/Converters/CMakeLists.txt b/PWGLF/TableProducer/Strangeness/Converters/CMakeLists.txt index e1d42ea534a..64bcc3a249d 100644 --- a/PWGLF/TableProducer/Strangeness/Converters/CMakeLists.txt +++ b/PWGLF/TableProducer/Strangeness/Converters/CMakeLists.txt @@ -11,6 +11,7 @@ o2physics_add_dpl_workflow(stradautracksconverter SOURCES stradautracksconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -21,155 +22,186 @@ o2physics_add_dpl_workflow(stradautrackstpcpidconverter o2physics_add_dpl_workflow(stradautrackstofpidconverter SOURCES stradautrackstofpidconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stradautrackstofpidconverter2 SOURCES stradautrackstofpidconverter2.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stradautrackstofpidconverter3 SOURCES stradautrackstofpidconverter3.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stradautrackstofpidconverter4 SOURCES stradautrackstofpidconverter4.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stradautracksextraconverter SOURCES stradautracksextraconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stradautracksextraconverter2 SOURCES stradautracksextraconverter2.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stradautracksextraconverter3 SOURCES stradautracksextraconverter3.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(strarawcentsconverter SOURCES strarawcentsconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(strarawcentsconverter2v4 SOURCES strarawcentsconverter2v4.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(straevselsconverter SOURCES straevselsconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(straevselsconverter2 SOURCES straevselsconverter2.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::ReconstructionDataFormats COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(straevselsconverter3 SOURCES straevselsconverter3.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::ReconstructionDataFormats COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(straevselsconverter4 SOURCES straevselsconverter4.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(straevselsconverter5 SOURCES straevselsconverter5.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(straevselsconverter6 SOURCES straevselsconverter6.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(straevselextrasconverter SOURCES straevselextrasconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(straevselextrasconverter2 SOURCES straevselextrasconverter2.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(straevselsconverter2rawcents SOURCES straevselsconverter2rawcents.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(straevselsconverter2rawcents2 SOURCES straevselsconverter2rawcents2.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(straevselsconverter2rawcents3 SOURCES straevselsconverter2rawcents3.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(v0coresconverter SOURCES v0coresconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(v0coresconverter2 SOURCES v0coresconverter2.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(v0mlscoresconverter SOURCES v0mlscoresconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stramccollisionconverter SOURCES stramccollisionconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stramccollisionconverter2 SOURCES stramccollisionconverter2.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(strastampsconverter SOURCES strastampsconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stracentconverter SOURCES stracentconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stracentconverter2 SOURCES stracentconverter2.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stramccollmultconverter SOURCES stramccollmultconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stramccollmultconverter2 SOURCES stramccollmultconverter2.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(zdcneutronsconverter SOURCES zdcneutronsconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) diff --git a/PWGLF/TableProducer/Strangeness/cascadeMLSelectionTreeCreator.cxx b/PWGLF/TableProducer/Strangeness/cascadeMLSelectionTreeCreator.cxx index a97f2dad659..7fe676a81e0 100644 --- a/PWGLF/TableProducer/Strangeness/cascadeMLSelectionTreeCreator.cxx +++ b/PWGLF/TableProducer/Strangeness/cascadeMLSelectionTreeCreator.cxx @@ -139,13 +139,15 @@ struct cascadeMLSelectionTreeCreator { float dcav0topv; float v0CosPA; float cascCosPA; + float bachBaryonCosPA; + float bachBaryonDCAxyToPV; // reserved for MC operation bool isXiMinus; bool isXiPlus; bool isOmegaMinus; bool isOmegaPlus; - } cascadeCandidate; + } cascadeCandidate{}; // Process candidate and store properties in object template @@ -161,10 +163,10 @@ struct cascadeMLSelectionTreeCreator { // Track quality cascadeCandidate.posITSCls = posTrackExtra.itsNCls(); cascadeCandidate.negITSCls = negTrackExtra.itsNCls(); - cascadeCandidate.bachITSCls = negTrackExtra.itsNCls(); + cascadeCandidate.bachITSCls = bachTrackExtra.itsNCls(); cascadeCandidate.posITSClsSizes = posTrackExtra.itsClusterSizes(); cascadeCandidate.negITSClsSizes = negTrackExtra.itsClusterSizes(); - cascadeCandidate.bachITSClsSizes = negTrackExtra.itsClusterSizes(); + cascadeCandidate.bachITSClsSizes = bachTrackExtra.itsClusterSizes(); cascadeCandidate.posTPCRows = posTrackExtra.tpcCrossedRows(); cascadeCandidate.negTPCRows = negTrackExtra.tpcCrossedRows(); cascadeCandidate.bachTPCRows = bachTrackExtra.tpcCrossedRows(); @@ -207,6 +209,8 @@ struct cascadeMLSelectionTreeCreator { cascadeCandidate.dcav0topv = cand.dcav0topv(coll.posX(), coll.posY(), coll.posZ()); cascadeCandidate.v0CosPA = cand.v0cosPA(coll.posX(), coll.posY(), coll.posZ()); cascadeCandidate.cascCosPA = cand.casccosPA(coll.posX(), coll.posY(), coll.posZ()); + cascadeCandidate.bachBaryonCosPA = cand.bachBaryonCosPA(); + cascadeCandidate.bachBaryonDCAxyToPV = cand.bachBaryonDCAxyToPV(); // MC flags cascadeCandidate.isXiMinus = false; @@ -214,36 +218,50 @@ struct cascadeMLSelectionTreeCreator { cascadeCandidate.isOmegaMinus = false; cascadeCandidate.isOmegaPlus = false; - if constexpr (requires { cand.pdgCode(); }) { - cascadeCandidate.isXiMinus = (cand.pdgCode() == 3312); - cascadeCandidate.isXiPlus = (cand.pdgCode() == -3312); - cascadeCandidate.isOmegaMinus = (cand.pdgCode() == 3334); - cascadeCandidate.isOmegaPlus = (cand.pdgCode() == -3334); + if constexpr (requires { cand.has_cascMCCore(); }) { + if (cand.has_cascMCCore()) { + auto cascMC = cand.template cascMCCore_as>(); + cascadeCandidate.isXiMinus = (cascMC.pdgCode() == 3312); + cascadeCandidate.isXiPlus = (cascMC.pdgCode() == -3312); + cascadeCandidate.isOmegaMinus = (cascMC.pdgCode() == 3334); + cascadeCandidate.isOmegaPlus = (cascMC.pdgCode() == -3334); + } } // base topological selections - if (cascadeCandidate.v0CosPA < v0setting_cospa) + if (cascadeCandidate.v0CosPA < v0setting_cospa) { return; - if (cascadeCandidate.dcaV0Daughters > v0setting_dcav0dau) + } + if (cascadeCandidate.dcaV0Daughters > v0setting_dcav0dau) { return; - if (cascadeCandidate.dcapostopv < v0setting_dcapostopv) + } + if (std::abs(cascadeCandidate.dcapostopv) < v0setting_dcapostopv) { return; - if (cascadeCandidate.dcapostopv < v0setting_dcanegtopv) + } + if (std::abs(cascadeCandidate.dcanegtopv) < v0setting_dcanegtopv) { return; - if (cascadeCandidate.dcabachtopv < cascadesetting_dcabachtopv) + } + if (std::abs(cascadeCandidate.dcabachtopv) < cascadesetting_dcabachtopv) { return; - if (cascadeCandidate.v0radius < v0setting_radius) + } + if (cascadeCandidate.v0radius < v0setting_radius) { return; - if (cascadeCandidate.cascCosPA < cascadesetting_cospa) + } + if (cascadeCandidate.cascCosPA < cascadesetting_cospa) { return; - if (cascadeCandidate.dcaCascDaughters > cascadesetting_dcacascdau) + } + if (cascadeCandidate.dcaCascDaughters > cascadesetting_dcacascdau) { return; - if (cascadeCandidate.cascradius < cascadesetting_cascradius) + } + if (cascadeCandidate.cascradius < cascadesetting_cascradius) { return; - if (std::abs(cascadeCandidate.mLambdaDaughter - 1.116) > cascadesetting_v0masswindow) + } + if (std::abs(cascadeCandidate.mLambdaDaughter - 1.116) > cascadesetting_v0masswindow) { return; - if (cascadeCandidate.dcav0topv < cascadesetting_mindcav0topv) + } + if (cascadeCandidate.dcav0topv < cascadesetting_mindcav0topv) { return; + } // Mass window selections cascadeCandidate.massWindows = 0; @@ -312,6 +330,8 @@ struct cascadeMLSelectionTreeCreator { cascadeCandidate.dcav0topv, TMath::ACos(cascadeCandidate.v0CosPA), TMath::ACos(cascadeCandidate.cascCosPA), + cascadeCandidate.bachBaryonCosPA, + cascadeCandidate.bachBaryonDCAxyToPV, // MC identity cascadeCandidate.isXiMinus, @@ -320,17 +340,17 @@ struct cascadeMLSelectionTreeCreator { cascadeCandidate.isOmegaPlus); } - void processRealData(soa::Join::iterator const& coll, soa::Join const& cascades, dauTracks const&) + void processRealData(soa::Join::iterator const& coll, soa::Join const& cascades, dauTracks const&) { histos.fill(HIST("hEventCentrality"), coll.centFT0C()); - for (auto& casc : cascades) { // looping over lambdas + for (const auto& casc : cascades) { // looping over cascades processCandidate(coll, casc); } } - void processSimData(soa::Join::iterator const& coll, soa::Join const& cascades, dauTracks const&) + void processSimData(soa::Join::iterator const& coll, soa::Join const& cascades, dauTracks const&, soa::Join const&) { histos.fill(HIST("hEventCentrality"), coll.centFT0C()); - for (auto& casc : cascades) { // looping over lambdas + for (const auto& casc : cascades) { // looping over cascades processCandidate(coll, casc); } } diff --git a/PWGLF/TableProducer/Strangeness/cascadeflow.cxx b/PWGLF/TableProducer/Strangeness/cascadeflow.cxx index 6d825f80728..92472e75394 100644 --- a/PWGLF/TableProducer/Strangeness/cascadeflow.cxx +++ b/PWGLF/TableProducer/Strangeness/cascadeflow.cxx @@ -308,6 +308,8 @@ struct cascadeFlow { Configurable isStoreTrueCascOnly{"isStoreTrueCascOnly", 1, ""}; Configurable etaCascMCGen{"etaCascMCGen", 0.8, "etaCascMCGen"}; Configurable yCascMCGen{"yCascMCGen", 0.5, "yCascMCGen"}; + Configurable etaLambdaFromXiMCGen{"etaLambdaFromXiMCGen", 0.8, "etaLambdaFromXiMCGen"}; + Configurable yXiToLambdaMCGen{"yXiToLambdaMCGen", 1.0, "yXiToLambdaMCGen"}; struct : ConfigurableGroup { Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; @@ -588,6 +590,9 @@ struct cascadeFlow { template double ComputeEPResolutionwShifts(const TCollision& coll, double psiT0C, double psiV0A, double psiT0A, double psiTPCA, double psiTPCC, TProfile3D* shiftprofileA, TProfile3D* shiftprofileB, TProfile3D* shiftprofileC, TProfile3D* shiftprofileD, TProfile3D* shiftprofileE) { + float collcentrality = coll.centFT0C(); + if (isCollisionCentrality == 1) + collcentrality = coll.centFT0M(); int nmode = 2; auto deltapsiFT0C = 0.0; auto deltapsiFV0A = 0.0; @@ -595,36 +600,36 @@ struct cascadeFlow { auto deltapsiTPCA = 0.0; auto deltapsiTPCC = 0.0; for (int ishift = 1; ishift <= 10; ishift++) { - auto coeffshiftxFT0C = shiftprofileA->GetBinContent(shiftprofileA->FindBin(coll.centFT0C(), 0.5, ishift - 0.5)); - auto coeffshiftyFT0C = shiftprofileA->GetBinContent(shiftprofileA->FindBin(coll.centFT0C(), 1.5, ishift - 0.5)); - auto coeffshiftxTPCA = shiftprofileB->GetBinContent(shiftprofileB->FindBin(coll.centFT0C(), 0.5, ishift - 0.5)); - auto coeffshiftyTPCA = shiftprofileB->GetBinContent(shiftprofileB->FindBin(coll.centFT0C(), 1.5, ishift - 0.5)); - auto coeffshiftxTPCC = shiftprofileC->GetBinContent(shiftprofileC->FindBin(coll.centFT0C(), 0.5, ishift - 0.5)); - auto coeffshiftyTPCC = shiftprofileC->GetBinContent(shiftprofileC->FindBin(coll.centFT0C(), 1.5, ishift - 0.5)); - auto coeffshiftxFV0A = shiftprofileD->GetBinContent(shiftprofileD->FindBin(coll.centFT0C(), 0.5, ishift - 0.5)); - auto coeffshiftyFV0A = shiftprofileD->GetBinContent(shiftprofileD->FindBin(coll.centFT0C(), 1.5, ishift - 0.5)); - auto coeffshiftxFT0A = shiftprofileE->GetBinContent(shiftprofileE->FindBin(coll.centFT0C(), 0.5, ishift - 0.5)); - auto coeffshiftyFT0A = shiftprofileE->GetBinContent(shiftprofileE->FindBin(coll.centFT0C(), 1.5, ishift - 0.5)); + auto coeffshiftxFT0C = shiftprofileA->GetBinContent(shiftprofileA->FindBin(collcentrality, 0.5, ishift - 0.5)); + auto coeffshiftyFT0C = shiftprofileA->GetBinContent(shiftprofileA->FindBin(collcentrality, 1.5, ishift - 0.5)); + auto coeffshiftxTPCA = shiftprofileB->GetBinContent(shiftprofileB->FindBin(collcentrality, 0.5, ishift - 0.5)); + auto coeffshiftyTPCA = shiftprofileB->GetBinContent(shiftprofileB->FindBin(collcentrality, 1.5, ishift - 0.5)); + auto coeffshiftxTPCC = shiftprofileC->GetBinContent(shiftprofileC->FindBin(collcentrality, 0.5, ishift - 0.5)); + auto coeffshiftyTPCC = shiftprofileC->GetBinContent(shiftprofileC->FindBin(collcentrality, 1.5, ishift - 0.5)); + auto coeffshiftxFV0A = shiftprofileD->GetBinContent(shiftprofileD->FindBin(collcentrality, 0.5, ishift - 0.5)); + auto coeffshiftyFV0A = shiftprofileD->GetBinContent(shiftprofileD->FindBin(collcentrality, 1.5, ishift - 0.5)); + auto coeffshiftxFT0A = shiftprofileE->GetBinContent(shiftprofileE->FindBin(collcentrality, 0.5, ishift - 0.5)); + auto coeffshiftyFT0A = shiftprofileE->GetBinContent(shiftprofileE->FindBin(collcentrality, 1.5, ishift - 0.5)); deltapsiFT0C += ((1 / (1.0 * ishift)) * (-coeffshiftxFT0C * std::cos(ishift * static_cast(nmode) * psiT0C) + coeffshiftyFT0C * TMath::Sin(ishift * static_cast(nmode) * psiT0C))); deltapsiFV0A += ((1 / (1.0 * ishift)) * (-coeffshiftxFV0A * std::cos(ishift * static_cast(nmode) * psiV0A) + coeffshiftyFV0A * TMath::Sin(ishift * static_cast(nmode) * psiV0A))); deltapsiFT0A += ((1 / (1.0 * ishift)) * (-coeffshiftxFT0A * std::cos(ishift * static_cast(nmode) * psiT0A) + coeffshiftyFT0A * TMath::Sin(ishift * static_cast(nmode) * psiT0A))); deltapsiTPCA += ((1 / (1.0 * ishift)) * (-coeffshiftxTPCA * std::cos(ishift * static_cast(nmode) * psiTPCA) + coeffshiftyTPCA * TMath::Sin(ishift * static_cast(nmode) * psiTPCA))); deltapsiTPCC += ((1 / (1.0 * ishift)) * (-coeffshiftxTPCC * std::cos(ishift * static_cast(nmode) * psiTPCC) + coeffshiftyTPCC * TMath::Sin(ishift * static_cast(nmode) * psiTPCC))); } - histos.fill(HIST("Psi_EP_FT0C_shifted"), coll.centFT0C(), psiT0C + deltapsiFT0C); - histos.fill(HIST("Psi_EP_FV0A_shifted"), coll.centFT0C(), psiV0A + deltapsiFV0A); - histos.fill(HIST("Psi_EP_FT0A_shifted"), coll.centFT0C(), psiT0A + deltapsiFT0A); - histos.fill(HIST("Psi_EP_TPCA_shifted"), coll.centFT0C(), psiTPCA + deltapsiTPCA); - histos.fill(HIST("Psi_EP_TPCC_shifted"), coll.centFT0C(), psiTPCC + deltapsiTPCC); - resolution.fill(HIST("QVectorsT0CTPCA_Shifted"), std::cos(static_cast(nmode) * (psiT0C + deltapsiFT0C - psiTPCA - deltapsiTPCA)), coll.centFT0C()); - resolution.fill(HIST("QVectorsT0CTPCC_Shifted"), std::cos(static_cast(nmode) * (psiT0C + deltapsiFT0C - psiTPCC - deltapsiTPCC)), coll.centFT0C()); - resolution.fill(HIST("QVectorsT0CV0A_Shifted"), std::cos(static_cast(nmode) * (psiT0C + deltapsiFT0C - psiV0A - deltapsiFV0A)), coll.centFT0C()); - resolution.fill(HIST("QVectorsT0CT0A_Shifted"), std::cos(static_cast(nmode) * (psiT0C + deltapsiFT0C - psiT0A - deltapsiFT0A)), coll.centFT0C()); - resolution.fill(HIST("QVectorsV0ATPCC_Shifted"), std::cos(static_cast(nmode) * (psiV0A + deltapsiFV0A - psiTPCC - deltapsiTPCC)), coll.centFT0C()); - resolution.fill(HIST("QVectorsV0ATPCA_Shifted"), std::cos(static_cast(nmode) * (psiV0A + deltapsiFV0A - psiTPCA - deltapsiTPCA)), coll.centFT0C()); - resolution.fill(HIST("QVectorsT0ATPCC_Shifted"), std::cos(static_cast(nmode) * (psiT0A + deltapsiFT0A - psiTPCC - deltapsiTPCC)), coll.centFT0C()); - resolution.fill(HIST("QVectorsT0ATPCA_Shifted"), std::cos(static_cast(nmode) * (psiT0A + deltapsiFT0A - psiTPCA - deltapsiTPCA)), coll.centFT0C()); - resolution.fill(HIST("QVectorsTPCAC_Shifted"), std::cos(static_cast(nmode) * (psiTPCA + deltapsiTPCA - psiTPCC - deltapsiTPCC)), coll.centFT0C()); + histos.fill(HIST("Psi_EP_FT0C_shifted"), collcentrality, psiT0C + deltapsiFT0C); + histos.fill(HIST("Psi_EP_FV0A_shifted"), collcentrality, psiV0A + deltapsiFV0A); + histos.fill(HIST("Psi_EP_FT0A_shifted"), collcentrality, psiT0A + deltapsiFT0A); + histos.fill(HIST("Psi_EP_TPCA_shifted"), collcentrality, psiTPCA + deltapsiTPCA); + histos.fill(HIST("Psi_EP_TPCC_shifted"), collcentrality, psiTPCC + deltapsiTPCC); + resolution.fill(HIST("QVectorsT0CTPCA_Shifted"), std::cos(static_cast(nmode) * (psiT0C + deltapsiFT0C - psiTPCA - deltapsiTPCA)), collcentrality); + resolution.fill(HIST("QVectorsT0CTPCC_Shifted"), std::cos(static_cast(nmode) * (psiT0C + deltapsiFT0C - psiTPCC - deltapsiTPCC)), collcentrality); + resolution.fill(HIST("QVectorsT0CV0A_Shifted"), std::cos(static_cast(nmode) * (psiT0C + deltapsiFT0C - psiV0A - deltapsiFV0A)), collcentrality); + resolution.fill(HIST("QVectorsT0CT0A_Shifted"), std::cos(static_cast(nmode) * (psiT0C + deltapsiFT0C - psiT0A - deltapsiFT0A)), collcentrality); + resolution.fill(HIST("QVectorsV0ATPCC_Shifted"), std::cos(static_cast(nmode) * (psiV0A + deltapsiFV0A - psiTPCC - deltapsiTPCC)), collcentrality); + resolution.fill(HIST("QVectorsV0ATPCA_Shifted"), std::cos(static_cast(nmode) * (psiV0A + deltapsiFV0A - psiTPCA - deltapsiTPCA)), collcentrality); + resolution.fill(HIST("QVectorsT0ATPCC_Shifted"), std::cos(static_cast(nmode) * (psiT0A + deltapsiFT0A - psiTPCC - deltapsiTPCC)), collcentrality); + resolution.fill(HIST("QVectorsT0ATPCA_Shifted"), std::cos(static_cast(nmode) * (psiT0A + deltapsiFT0A - psiTPCA - deltapsiTPCA)), collcentrality); + resolution.fill(HIST("QVectorsTPCAC_Shifted"), std::cos(static_cast(nmode) * (psiTPCA + deltapsiTPCA - psiTPCC - deltapsiTPCC)), collcentrality); return true; } @@ -802,6 +807,7 @@ struct cascadeFlow { invMassLambda = v0.mLambda(); double ctauLambda = v0.distovertotmom(coll.posX(), coll.posY(), coll.posZ()) * o2::constants::physics::MassLambda0; analysisLambdaSample(coll.centFT0C(), + coll.centFT0M(), hasEventPlane, hasSpectatorPlane, chargeIndex, @@ -809,6 +815,7 @@ struct cascadeFlow { v0.phi(), v0.eta(), invMassLambda, + v0.mK0Short(), ctauLambda, v0.v0radius(), v0.dcapostopv(), @@ -1129,6 +1136,8 @@ struct cascadeFlow { histosMCGen.add("h2DGenLambdaY05", "h2DGenLambdaY05", HistType::kTH2F, {{100, 0, 100}, {400, 0, 20}}); histosMCGen.add("h2DGenAntiLambdaEta08", "h2DGenAntiLambdaEta08", HistType::kTH2F, {{100, 0, 100}, {400, 0, 20}}); histosMCGen.add("h2DGenAntiLambdaY05", "h2DGenAntiLambdaY05", HistType::kTH2F, {{100, 0, 100}, {400, 0, 20}}); + histosMCGen.add("h2DGenXiVsPtLambda", "h2DGenXiVsPtLambda", HistType::kTH2F, {{100, 0, 100}, {400, 0, 20}}); + histosMCGen.add("h2DGenLambdaFromXiPtMatrix", "h2DGenLambdaFromXiPtMatrix", HistType::kTH2F, {{200, 0, 20}, {200, 0, 20}}); histosMCGen.add("hGenXiY", "hGenXiY", HistType::kTH1F, {{100, -1, 1}}); histosMCGen.add("hGenOmegaY", "hGenOmegaY", HistType::kTH1F, {{100, -1, 1}}); histosMCGen.add("hGenLambdaY", "hGenLambdaY", HistType::kTH1F, {{100, -2, 2}}); @@ -1149,6 +1158,10 @@ struct cascadeFlow { histosMCReco.add("h2DRecoTrueLambda", "h2DRecoTrueLambda", HistType::kTH2F, {{100, 0, 100}, {400, 0, 20}}); histosMCReco.add("h2DRecoTrueAntiLambda", "h2DRecoTrueAntiLambda", HistType::kTH2F, {{100, 0, 100}, {400, 0, 20}}); + histosMCReco.add("h2DRecoTrueLambdaSec", "h2DRecoTrueLambdaSec", HistType::kTH2F, {{100, 0, 100}, {400, 0, 20}}); + histosMCReco.add("h2DRecoTrueAntiLambdaSec", "h2DRecoTrueAntiLambdaSec", HistType::kTH2F, {{100, 0, 100}, {400, 0, 20}}); + histosMCReco.add("h2DRecoTrueLambdaFromXi", "h2DRecoTrueLambdaFromXi", HistType::kTH2F, {{100, 0, 100}, {400, 0, 20}}); + histosMCReco.add("h2DRecoTrueAntiLambdaFromXi", "h2DRecoTrueAntiLambdaFromXi", HistType::kTH2F, {{100, 0, 100}, {400, 0, 20}}); for (int iS{0}; iS < nParticles; ++iS) { cascadev2::hMassBeforeSelVsPt[iS] = histos.add(Form("hMassBeforeSelVsPt%s", cascadev2::speciesNames[iS].data()), "hMassBeforeSelVsPt", HistType::kTH2F, {massCascAxis[iS], ptAxisCasc}); @@ -2092,15 +2105,15 @@ struct cascadeFlow { resolution.fill(HIST("QVectorsT0ATPCC"), eventplaneVecT0A.Dot(eventplaneVecTPCC), collisionCentrality); resolution.fill(HIST("QVectorsT0ATPCA"), eventplaneVecT0A.Dot(eventplaneVecTPCA), collisionCentrality); - resolution.fill(HIST("EP_T0CTPCA"), std::cos(2 * (psiT0C - psiTPCA)), coll.centFT0C()); - resolution.fill(HIST("EP_T0CTPCC"), std::cos(2 * (psiT0C - psiTPCC)), coll.centFT0C()); - resolution.fill(HIST("EP_TPCAC"), std::cos(2 * (psiTPCA - psiTPCC)), coll.centFT0C()); - resolution.fill(HIST("EP_T0CV0A"), std::cos(2 * (psiT0C - psiV0A)), coll.centFT0C()); - resolution.fill(HIST("EP_V0ATPCC"), std::cos(2 * (psiV0A - psiTPCC)), coll.centFT0C()); - resolution.fill(HIST("EP_V0ATPCA"), std::cos(2 * (psiV0A - psiTPCA)), coll.centFT0C()); - resolution.fill(HIST("EP_T0CT0A"), std::cos(2 * (psiT0C - psiT0A)), coll.centFT0C()); - resolution.fill(HIST("EP_T0ATPCC"), std::cos(2 * (psiT0A - psiTPCC)), coll.centFT0C()); - resolution.fill(HIST("EP_T0ATPCA"), std::cos(2 * (psiT0A - psiTPCA)), coll.centFT0C()); + resolution.fill(HIST("EP_T0CTPCA"), std::cos(2 * (psiT0C - psiTPCA)), collisionCentrality); + resolution.fill(HIST("EP_T0CTPCC"), std::cos(2 * (psiT0C - psiTPCC)), collisionCentrality); + resolution.fill(HIST("EP_TPCAC"), std::cos(2 * (psiTPCA - psiTPCC)), collisionCentrality); + resolution.fill(HIST("EP_T0CV0A"), std::cos(2 * (psiT0C - psiV0A)), collisionCentrality); + resolution.fill(HIST("EP_V0ATPCC"), std::cos(2 * (psiV0A - psiTPCC)), collisionCentrality); + resolution.fill(HIST("EP_V0ATPCA"), std::cos(2 * (psiV0A - psiTPCA)), collisionCentrality); + resolution.fill(HIST("EP_T0CT0A"), std::cos(2 * (psiT0C - psiT0A)), collisionCentrality); + resolution.fill(HIST("EP_T0ATPCC"), std::cos(2 * (psiT0A - psiTPCC)), collisionCentrality); + resolution.fill(HIST("EP_T0ATPCA"), std::cos(2 * (psiT0A - psiTPCA)), collisionCentrality); resolution.fill(HIST("QVectorsNormT0CTPCA"), eventplaneVecT0C.Dot(eventplaneVecTPCA) / (coll.qTPCR() * coll.sumAmplFT0C()), collisionCentrality); resolution.fill(HIST("QVectorsNormT0CTPCC"), eventplaneVecT0C.Dot(eventplaneVecTPCC) / (coll.qTPCL() * coll.sumAmplFT0C()), collisionCentrality); @@ -2687,6 +2700,7 @@ struct cascadeFlow { float ptmc = RecoDecay::sqrtSumOfSquares(v0mc.pxMC(), v0mc.pyMC()); float lambdaMCeta = RecoDecay::eta(std::array{v0mc.pxMC(), v0mc.pyMC(), v0mc.pzMC()}); float lambdaMCy = 0; + if (std::abs(v0mc.pdgCode()) == PDG_t::kLambda0) { lambdaMCy = RecoDecay::y(std::array{v0mc.pxMC(), v0mc.pyMC(), v0mc.pzMC()}, constants::physics::MassLambda); if (std::abs(lambdaMCeta) < etaCascMCGen) { @@ -2740,6 +2754,19 @@ struct cascadeFlow { theta1 = o2::constants::math::PI + theta; // pi/2 < theta1 < pi --> pi/4 < theta1/2 < pi/2 --> 1 < tan (theta1/2) --> negative eta float cascMCeta = -std::log(std::tan(theta1 / 2)); + + float pxLambda = cascmc.pxPosMC() + cascmc.pxNegMC(); + float pyLambda = cascmc.pyPosMC() + cascmc.pyNegMC(); + float pzLambda = cascmc.pzPosMC() + cascmc.pzNegMC(); + float ptmcLambda = RecoDecay::sqrtSumOfSquares(pxLambda, pyLambda); + float thetaLambda = std::atan(ptmcLambda / pzLambda); + float theta1Lambda = 0; + if (thetaLambda > 0) + theta1Lambda = thetaLambda; + else + theta1Lambda = o2::constants::math::PI + thetaLambda; + float lambdaMCeta = -std::log(std::tan(theta1Lambda / 2)); + float cascMCy = 0; if (std::abs(cascmc.pdgCode()) == PDG_t::kXiMinus) { cascMCy = RecoDecay::y(std::array{cascmc.pxMC(), cascmc.pyMC(), cascmc.pzMC()}, constants::physics::MassXiMinus); @@ -2750,6 +2777,10 @@ struct cascadeFlow { if (std::abs(cascMCy) < yCascMCGen) histosMCGen.fill(HIST("h2DGenXiY05"), centrality, ptmc); histosMCGen.fill(HIST("hGenXiY"), cascMCy); + if (std::abs(cascMCy) < yXiToLambdaMCGen && std::abs(lambdaMCeta) < etaLambdaFromXiMCGen) { + histosMCGen.fill(HIST("h2DGenXiVsPtLambda"), centrality, ptmcLambda); // to compute secondary lambda efficiency + histosMCGen.fill(HIST("h2DGenLambdaFromXiPtMatrix"), ptmc, ptmcLambda); + } } else if (std::abs(cascmc.pdgCode()) == PDG_t::kOmegaMinus) { cascMCy = RecoDecay::y(std::array{cascmc.pxMC(), cascmc.pyMC(), cascmc.pzMC()}, constants::physics::MassOmegaMinus); if (std::abs(cascMCeta) < etaCascMCGen) { @@ -2871,6 +2902,9 @@ struct cascadeFlow { } else { histos.fill(HIST("hCentvsPtvsPrimaryFracLambda"), collisionCentrality, v0.pt(), 1); histos.fill(HIST("hCentvsPrimaryFracLambda"), collisionCentrality, 1); + histosMCReco.fill(HIST("h2DRecoTrueLambdaSec"), collisionCentrality, ptmc); + if (v0MC.pdgCodeMother() == PDG_t::kXiMinus) + histosMCReco.fill(HIST("h2DRecoTrueLambdaFromXi"), collisionCentrality, ptmc); } } else if (isTrueALambda) { if (isPrimary) { @@ -2880,6 +2914,9 @@ struct cascadeFlow { } else { histos.fill(HIST("hCentvsPtvsPrimaryFracLambda"), collisionCentrality, v0.pt(), 3); histos.fill(HIST("hCentvsPrimaryFracLambda"), collisionCentrality, 3); + histosMCReco.fill(HIST("h2DRecoTrueAntiLambdaSec"), collisionCentrality, ptmc); + if (v0MC.pdgCodeMother() == -PDG_t::kXiMinus) + histosMCReco.fill(HIST("h2DRecoTrueAntiLambdaFromXi"), collisionCentrality, ptmc); } } } diff --git a/PWGLF/TableProducer/Strangeness/doubleOmegaBuilder.cxx b/PWGLF/TableProducer/Strangeness/doubleOmegaBuilder.cxx new file mode 100644 index 00000000000..459997de976 --- /dev/null +++ b/PWGLF/TableProducer/Strangeness/doubleOmegaBuilder.cxx @@ -0,0 +1,321 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file doubleOmegaBuilder.cxx +/// \brief Builds the Omega, Lambda and direct-kaon candidates of the double-Omega analysis and stores +/// them in derived tables, consumed on the fly (or from saved derived data) by doubleOmegaTreeCreator +/// for the same-event candidates and the event mixing. + +#include "PWGLF/DataModel/LFDoubleOmegaTables.h" +#include "PWGLF/Utils/doubleOmegaBuilderHelper.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/Zorro.h" +#include "Common/Core/ZorroSummary.h" +#include "Common/Core/trackUtilities.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::pwglf::doubleomega; + +using CollisionsTable = soa::Join; +using TracksFull = soa::Join; + +struct doubleOmegaBuilder { + Produces dOmegaColls; + Produces dOmegaTracks; + Produces dOmegaOmegas; + Produces dOmegaLambdas; + Service ccdb{}; + + enum CollisionStep : uint8_t { + kAllCollisions, + kSelectedCollisions, + kWithOmega, + kStoredCollisions, + kNCollisionSteps + }; + + Preslice tracksPerCollision = aod::track::collisionId; + Preslice cascadesPerCollision = aod::cascade::collisionId; + Preslice v0sPerCollision = aod::v0::collisionId; + + DoubleOmegaBuilderHelper helper; + int mRunNumber = 0; + float mBz = 0.f; + + Zorro zorro; + OutputObj zorroSummary{"zorroSummary"}; + + Configurable cfgSkimmedProcessing{"cfgSkimmedProcessing", false, "Skimmed dataset processing"}; + Configurable cfgApplyEventSelection{"cfgApplyEventSelection", true, "Apply the standard collision event selection"}; + Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable cfgMaterialCorrection{"cfgMaterialCorrection", static_cast(o2::base::Propagator::MatCorrType::USEMatCorrLUT), "Material correction for the V0/cascade fits"}; + + ConfigurableAxis zVtxAxis{"zVtxBins", {100, -20.f, 20.f}, "Binning for the vertex z in cm"}; + ConfigurableAxis massOmegaAxis{"massOmegaAxis", {400, o2::constants::physics::MassOmegaMinus - 0.05f, o2::constants::physics::MassOmegaMinus + 0.05f}, "binning for the Omega invariant-mass"}; + ConfigurableAxis massXiAxis{"massXiAxis", {400, o2::constants::physics::MassXiMinus - 0.05f, o2::constants::physics::MassXiMinus + 0.05f}, "binning for the Xi invariant-mass"}; + ConfigurableAxis massLambdaAxis{"massLambdaAxis", {400, o2::constants::physics::MassLambda0 - 0.05f, o2::constants::physics::MassLambda0 + 0.05f}, "binning for the Lambda invariant-mass"}; + ConfigurableAxis momAxis{"momAxisFine", {5.e2, 0.f, 5.f}, "momentum axis binning"}; + + // Production selections: loose, they can be tightened when the derived tables are read. + Configurable zVtxMax{"zVtxMax", 10.0f, "maximum z position of the primary vertex"}; + Configurable etaMax{"etaMax", 0.9f, "maximum eta"}; + Configurable minNCrossedRows{"minNCrossedRows", 100, "Minimum number of crossed TPC rows"}; + Configurable minNITSClus{"minNITSClus", 0., "Minimum number of ITS clusters"}; + Configurable minNTPCClus{"minNTPCClus", 80, "Minimum number of TPC clusters"}; + Configurable maxNSharedTPCClus{"maxNSharedTPCClus", 5, "Maximum number of shared TPC clusters"}; + Configurable nSigmaTPCCut{"nSigmaTPCCut", 3.f, "Number of sigmas for the TPC PID"}; + Configurable dcaBachToPV{"dcaBachToPV", 0.05f, "Minimum DCA of a cascade bachelor to the primary vertex"}; + Configurable dcaV0DauToPV{"dcaV0DauToPV", 0.05f, "Minimum DCA of Lambda daughters to the primary vertex"}; + Configurable dcaV0Bach{"dcaV0Bach", 1.f, "Maximum DCA between the V0 and cascade bachelor"}; + Configurable dcaLambdaDaughters{"dcaLambdaDaughters", 1.f, "Maximum DCA between Lambda daughters"}; + Configurable mOmegaWindow{"mOmegaWindow", 0.01f, "Omega mass window"}; + Configurable mLambdaWindow{"mLambdaWindow", 0.01f, "Lambda mass window"}; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + void init(InitContext const&) + { + ccdb->setURL(ccdburl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + + auto& selections = helper.selections; + selections.etaMax = etaMax; + selections.minNITSClus = minNITSClus; + selections.minNTPCClus = minNTPCClus; + selections.minNCrossedRows = minNCrossedRows; + selections.maxNSharedTPCClus = maxNSharedTPCClus; + selections.nSigmaTPCCut = nSigmaTPCCut; + selections.dcaBachToPV = dcaBachToPV; + selections.dcaV0DauToPV = dcaV0DauToPV; + selections.dcaV0Bach = dcaV0Bach; + selections.dcaLambdaDaughters = dcaLambdaDaughters; + selections.mOmegaWindow = mOmegaWindow; + selections.mLambdaWindow = mLambdaWindow; + helper.initFitters(); + helper.setMatCorrType(static_cast(cfgMaterialCorrection.value)); + + zorroSummary.setObject(zorro.getZorroSummary()); + + histos.add("QA/zVtx", ";#it{z}_{vtx} (cm);Entries", HistType::kTH1F, {zVtxAxis}); + histos.add("QA/massXi", ";#it{p}_{T} (GeV/#it{c});#it{M}(#Lambda + #pi) (GeV/#it{c}^{2});Entries", HistType::kTH2F, {momAxis, massXiAxis}); + histos.add("QA/massOmega", ";#it{p}_{T} (GeV/#it{c});#it{M}(#Lambda + K) (GeV/#it{c}^{2});Entries", HistType::kTH2F, {momAxis, massOmegaAxis}); + histos.add("QA/massLambda", ";#it{p}_{T} (GeV/#it{c});#it{M}(p + #pi) (GeV/#it{c}^{2});Entries", HistType::kTH2F, {momAxis, massLambdaAxis}); + histos.add("QA/collisions", ";;Collisions", HistType::kTH1F, {{kNCollisionSteps, -0.5f, static_cast(kNCollisionSteps) - 0.5f}}); + auto collisions = histos.get(HIST("QA/collisions")); + collisions->GetXaxis()->SetBinLabel(kAllCollisions + 1, "All"); + collisions->GetXaxis()->SetBinLabel(kSelectedCollisions + 1, "Event selection"); + collisions->GetXaxis()->SetBinLabel(kWithOmega + 1, "With #Omega"); + collisions->GetXaxis()->SetBinLabel(kStoredCollisions + 1, "With #Omega, #Lambda and K (stored)"); + } + + template + void initCCDB(Bc const& bc) + { + if (mRunNumber == bc.runNumber()) { + return; + } + auto* grpmag = ccdb->getForTimeStamp("GLO/Config/GRPMagField", bc.timestamp()); + if (!grpmag) { + LOG(fatal) << "Could not retrieve GRPMagField for timestamp " << bc.timestamp(); + } + o2::base::Propagator::initFieldFromGRP(grpmag); + mBz = o2::base::Propagator::Instance()->getNominalBz(); + helper.setBz(mBz); + + if (static_cast(cfgMaterialCorrection.value) == o2::base::Propagator::MatCorrType::USEMatCorrLUT) { + auto* lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->getForTimeStamp("GLO/Param/MatLUT", bc.timestamp())); + if (!lut) { + LOG(fatal) << "Could not retrieve material LUT for timestamp " << bc.timestamp(); + } + o2::base::Propagator::Instance()->setMatLUT(lut); + } + + mRunNumber = bc.runNumber(); + if (cfgSkimmedProcessing) { + zorro.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), "fDoubleOmega,fOmegaXi"); + zorro.populateHistRegistry(histos, bc.runNumber()); + } + } + + template + bool acceptCollision(C const& collision) + { + auto bc = collision.template bc_as(); + initCCDB(bc); + + if (cfgApplyEventSelection && + (!collision.sel8() || + std::abs(collision.posZ()) > zVtxMax || + !collision.selection_bit(aod::evsel::kNoITSROFrameBorder) || + !collision.selection_bit(aod::evsel::kNoTimeFrameBorder))) { + return false; + } + if (cfgSkimmedProcessing) { + zorro.isSelected(bc.globalBC()); + } + histos.fill(HIST("QA/zVtx"), collision.posZ()); + return true; + } + + void fillCandidateQA(CollisionCandidates const& candidates) + { + for (const auto& omega : candidates.omegas) { + const float omegaPt = std::hypot(omega.px, omega.py); + histos.fill(HIST("QA/massXi"), omegaPt, omega.massXi); + histos.fill(HIST("QA/massOmega"), omegaPt, omega.massOmega); + } + for (const auto& lambdas : candidates.lambdas) { + for (const auto& lambda : lambdas) { + histos.fill(HIST("QA/massLambda"), std::hypot(lambda.candidate.px, lambda.candidate.py), lambda.candidate.mass); + } + } + } + + // Writes the candidates of a collision. Tracks are written once per collision: the direct-kaon + // candidates first, then the Omega and Lambda daughters not written yet. + template + void writeCollision(C const& collision, CollisionCandidates const& candidates, T const& tracks) + { + dOmegaColls(collision.posX(), collision.posY(), collision.posZ(), collision.multFT0M(), mRunNumber, mBz); + const auto collisionIndex = static_cast(dOmegaColls.lastIndex()); + + std::unordered_map trackRows; // Track index -> DOmegaTracks row. + const auto writeTrack = [&](int64_t trackId, bool isKaon) { + const auto [row, inserted] = trackRows.try_emplace(trackId, -1); + if (!inserted) { + return row->second; + } + const auto track = tracks.rawIteratorAt(trackId); + float dcaXY = -999.f; + helper.dcaXYToPV(collision, track, dcaXY); + const auto trackParCov = getTrackParCov(track); + const auto& cov = trackParCov.getCov(); + dOmegaTracks(collisionIndex, + trackParCov.getX(), trackParCov.getAlpha(), trackParCov.getY(), trackParCov.getZ(), trackParCov.getSnp(), trackParCov.getTgl(), trackParCov.getQ2Pt(), + cov[0], cov[1], cov[2], cov[3], cov[4], cov[5], cov[6], cov[7], cov[8], cov[9], cov[10], cov[11], cov[12], cov[13], cov[14], + track.hasTPC() ? track.tpcNSigmaKa() : -999.f, + track.hasTOF() ? track.tofNSigmaKa() : -999.f, + static_cast(DoubleOmegaBuilderHelper::detectorMap(track)), + dcaXY, + isKaon); + row->second = static_cast(dOmegaTracks.lastIndex()); + return row->second; + }; + + for (const auto& kaons : candidates.kaons) { + for (const auto& kaon : kaons) { + writeTrack(kaon.trackId, true); + } + } + for (const auto& omega : candidates.omegas) { + const auto posTrackRow = writeTrack(omega.posTrackId, false); + const auto negTrackRow = writeTrack(omega.negTrackId, false); + const auto bachelorRow = writeTrack(omega.bachelorId, false); + const auto& parent = omega.parentTrack; + const auto& cov = parent.getCov(); + dOmegaOmegas(collisionIndex, posTrackRow, negTrackRow, bachelorRow, + parent.getX(), parent.getAlpha(), parent.getY(), parent.getZ(), parent.getSnp(), parent.getTgl(), parent.getQ2Pt(), + cov[0], cov[1], cov[2], cov[3], cov[4], cov[5], cov[6], cov[7], cov[8], cov[9], cov[10], cov[11], cov[12], cov[13], cov[14], + omega.x, omega.y, omega.z, + omega.px, omega.py, omega.pz, + omega.massOmega, omega.massXi, + omega.dcaDaughters, + omega.sign); + } + for (const int8_t charge : {int8_t{-1}, int8_t{1}}) { + for (const auto& lambda : candidates.lambdas[DoubleOmegaBuilderHelper::chargeIndex(charge)]) { + const auto posTrackRow = writeTrack(lambda.posTrackId, false); + const auto negTrackRow = writeTrack(lambda.negTrackId, false); + const auto& parent = lambda.parentTrack; + const auto& cov = parent.getCov(); + dOmegaLambdas(collisionIndex, posTrackRow, negTrackRow, + parent.getX(), parent.getAlpha(), parent.getY(), parent.getZ(), parent.getSnp(), parent.getTgl(), parent.getQ2Pt(), + cov[0], cov[1], cov[2], cov[3], cov[4], cov[5], cov[6], cov[7], cov[8], cov[9], cov[10], cov[11], cov[12], cov[13], cov[14], + lambda.decayVertex[0], lambda.decayVertex[1], lambda.decayVertex[2], + lambda.candidate.px, lambda.candidate.py, lambda.candidate.pz, + lambda.candidate.mass, + lambda.dcaDaughters, + charge); + } + } + } + + void processData(CollisionsTable const& collisions, + TracksFull const& tracks, + aod::V0s const& v0s, + aod::Cascades const& cascades, + aod::BCsWithTimestamps const&) + { + for (const auto& collision : collisions) { + histos.fill(HIST("QA/collisions"), kAllCollisions); + if (!acceptCollision(collision)) { + continue; + } + histos.fill(HIST("QA/collisions"), kSelectedCollisions); + auto tracksThisCollision = tracks.sliceBy(tracksPerCollision, collision.globalIndex()); + auto cascadesThisCollision = cascades.sliceBy(cascadesPerCollision, collision.globalIndex()); + auto v0sThisCollision = v0s.sliceBy(v0sPerCollision, collision.globalIndex()); + v0sThisCollision.bindExternalIndices(&tracks); + cascadesThisCollision.bindExternalIndices(&tracks); + cascadesThisCollision.bindExternalIndices(&v0s); + const auto candidates = helper.buildCollisionCandidates(collision, tracksThisCollision, cascadesThisCollision, v0sThisCollision, true); + fillCandidateQA(candidates); + if (candidates.omegas.empty()) { + continue; + } + histos.fill(HIST("QA/collisions"), kWithOmega); + if (!DoubleOmegaBuilderHelper::hasOmegaLambdaKaon(candidates)) { + continue; + } + histos.fill(HIST("QA/collisions"), kStoredCollisions); + writeCollision(collision, candidates, tracks); + } + } + PROCESS_SWITCH(doubleOmegaBuilder, processData, "Build the Omega, Lambda and kaon candidates and store them in derived tables", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/TableProducer/Strangeness/doubleOmegaTreeCreator.cxx b/PWGLF/TableProducer/Strangeness/doubleOmegaTreeCreator.cxx index 9ca484967f6..8622f2e626a 100644 --- a/PWGLF/TableProducer/Strangeness/doubleOmegaTreeCreator.cxx +++ b/PWGLF/TableProducer/Strangeness/doubleOmegaTreeCreator.cxx @@ -11,6 +11,7 @@ #include "PWGLF/DataModel/LFDoubleOmegaTables.h" #include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGLF/Utils/doubleOmegaBuilderHelper.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/Core/RecoDecay.h" @@ -21,17 +22,18 @@ #include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" -#include "Common/DataModel/TrackSelectionTables.h" #include +#include #include -#include #include #include #include +#include #include #include #include +#include #include #include #include @@ -44,20 +46,29 @@ #include #include +#include #include #include +#include #include #include #include +#include +#include #include +#include #include #include +#include +#include +#include #include using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; +using namespace o2::pwglf::doubleomega; using CollisionsTable = soa::Join; using Collisions = CollisionsTable::iterator; @@ -68,37 +79,6 @@ using FullCascades = aod::CascDataExt; using TracksFull = soa::Join; using TracksFullMC = soa::Join; -struct DoubleOmegaCandidate { - float pt = -999.f; - float eta = -999.f; - float phi = -999.f; - float x = -999.f; - float y = -999.f; - float z = -999.f; - float pvX = -999.f; - float pvY = -999.f; - float pvZ = -999.f; - float cosPAOmega = -999.f; - float cosPADirectLambda = -999.f; - float cosPADoubleOmega = -999.f; - float cosPAOmegaToPV = -999.f; - float cosPADirectLambdaToPV = -999.f; - float dcaXYOmegaToPV = -999.f; - float dcaXYDirectLambdaToPV = -999.f; - float dcaXYDirectKaonToPV = -999.f; - float dcaXYOmegaKaonToPV = -999.f; - float dcaXYOmegaProtonToPV = -999.f; - float dcaXYOmegaPionToPV = -999.f; - float dcaXYDirectProtonToPV = -999.f; - float dcaXYDirectPionToPV = -999.f; - float tpcNSigmaDirectKaon = -999.f; - float tofNSigmaDirectKaon = -999.f; - uint32_t daughterDetectorMap = 0; - float mass = -999.f; - float massOmega = -999.f; - float massXi = -999.f; -}; - struct DoubleOmegaMCInfo { int64_t motherId = -1; float pt = -999.f; @@ -111,49 +91,25 @@ struct DoubleOmegaMCInfo { std::array genProductionVertex{-999.f, -999.f, -999.f}; std::array genDecayVertex{-999.f, -999.f, -999.f}; std::array genOmegaDecayVertex{-999.f, -999.f, -999.f}; + int8_t sign = 0; }; -struct LambdaCandidate { - float px = 0.f; - float py = 0.f; - float pz = 0.f; - float mass = -999.f; -}; - -struct BuiltLambdaCandidate { - LambdaCandidate candidate; - o2::track::TrackParCov parentTrack; - std::array decayVertex{}; - int64_t posTrackId = -1; - int64_t negTrackId = -1; - uint32_t daughterDetectorMap = 0; // Proton, pion; three detector bits each. - float dcaXYProtonToPV = -999.f; - float dcaXYPionToPV = -999.f; -}; - -struct BuiltOmegaCandidate { - float px = 0.f; - float py = 0.f; - float pz = 0.f; - float x = 0.f; - float y = 0.f; - float z = 0.f; - float massOmega = -999.f; - float massXi = -999.f; - int8_t sign = 0; - int64_t posTrackId = -1; - int64_t negTrackId = -1; - int64_t bachelorId = -1; - o2::track::TrackParCov parentTrack; - uint32_t daughterDetectorMap = 0; // Kaon, proton, pion; three detector bits each. - float dcaXYKaonToPV = -999.f; - float dcaXYProtonToPV = -999.f; - float dcaXYPionToPV = -999.f; +// Same-event double-Omega candidate used as a seed of the candidate-level event mixing. Seeds are +// kept in a pool across dataframes, so they store copies of their daughters, with the collision and +// track indices made unique across dataframes (see uniqueIndex). +struct MixingSeed { + int64_t collisionId = -1; + std::array primaryVertex{}; + float bz = 0.f; + BuiltOmegaCandidate omega; + BuiltLambdaCandidate lambda; + BuiltKaonCandidate kaon; }; struct doubleOmegaTreeCreator { Produces doubleOmegaTable; Produces doubleOmegaTableMC; + Produces doubleOmegaMixedTable; Service ccdb{}; static constexpr int kDoubleOmegaPdg = 1060020020; @@ -168,16 +124,26 @@ struct doubleOmegaTreeCreator { kFindableCascadeAndV0SelectedTracks, kNFindabilitySteps }; + enum MixingType : uint8_t { + kSameEvent = 0, + kMixedOmega, // Lambda and kaon from the same collision, Omega from another one + kMixedLambda, // Omega and kaon from the same collision, Lambda from another one + kMixedKaon, // Omega and Lambda from the same collision, kaon from another one + kMixedAll, // Omega, Lambda and kaon from three different collisions + kNMixingTypes + }; std::vector reconstructedDoubleOmegaIds; Preslice tracksPerCollision = aod::track::collisionId; Preslice rawCascadesPerCollision = aod::cascade::collisionId; Preslice rawV0sPerCollision = aod::v0::collisionId; + Preslice derivedTracksPerCollision = aod::domega::dOmegaCollId; + Preslice derivedOmegasPerCollision = aod::domega::dOmegaCollId; + Preslice derivedLambdasPerCollision = aod::domega::dOmegaCollId; int mRunNumber = 0; float mBz = 0.f; - o2::vertexing::DCAFitterN<2> fitter; - o2::vertexing::DCAFitterN<3> fitter3Body; + DoubleOmegaBuilderHelper helper; Zorro zorro; OutputObj zorroSummary{"zorroSummary"}; @@ -220,32 +186,25 @@ struct doubleOmegaTreeCreator { Configurable minCosPADoubleOmega{"minCosPADoubleOmega", -1.f, "Minimum double-Omega cosPA relative to the primary vertex"}; Configurable minDoubleOmegaDecayRadius{"minDoubleOmegaDecayRadius", 1.f, "Minimum double-Omega transverse decay radius in cm"}; - HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - - template - static uint32_t detectorMap(Track const& track) - { - return static_cast(track.hasITS()) | - (static_cast(track.hasTPC()) << 1) | - (static_cast(track.hasTOF()) << 2); - } + struct : ConfigurableGroup { + std::string prefix = "mixingOpts"; + Configurable nMixingCollisions{"nMixingCollisions", 5, "Number of neighbouring collisions in the same bin each collision is mixed with"}; + Configurable mixingTypes{"mixingTypes", 0b1111, "Bitmask of the mixed backgrounds: 1 Omega, 2 Lambda, 4 kaon from another collision, 8 all from different collisions"}; + Configurable maxDeltaPVz{"maxDeltaPVz", 1.f, "Maximum difference of the primary-vertex z among the mixed collisions (cm)"}; + ConfigurableAxis binsVtxZ{"binsVtxZ", {VARIABLE_WIDTH, -10.f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - primary-vertex z (cm)"}; + ConfigurableAxis binsMultiplicity{"binsMultiplicity", {VARIABLE_WIDTH, 0.f, 20.f, 50.f, 100.f, 200.f, 500.f, 1.e5f}, "Mixing bins - FT0M multiplicity"}; + // Candidate-level mixing (processDerivedMixingCandidates) + Configurable nMixingCandidates{"nMixingCandidates", 10, "Number of same-event candidates from other collisions each candidate is mixed with, per (R, phi, charge) bin"}; + Configurable poolDepth{"poolDepth", 50, "Number of same-event candidates kept in the mixing pool per (R, phi, charge) bin, across dataframes"}; + Configurable monitoringInterval{"monitoringInterval", 50, "Log the mixing pool size, memory and timing every this many dataframes (0: never)"}; + ConfigurableAxis binsRadius{"binsRadius", {VARIABLE_WIDTH, 0.f, 0.25f, 0.5f, 1.f, 2.f, 4.f, 10.f, 40.f}, "Mixing bins - double-Omega decay radius (cm)"}; + ConfigurableAxis binsPhi{"binsPhi", {VARIABLE_WIDTH, -o2::constants::math::PI, -2.f * o2::constants::math::PI / 3.f, -o2::constants::math::PI / 3.f, 0.f, o2::constants::math::PI / 3.f, 2.f * o2::constants::math::PI / 3.f, o2::constants::math::PI}, "Mixing bins - double-Omega momentum azimuth (rad)"}; + } mixingOpts; + + using DerivedMixingBinning = ColumnBinningPolicy; + static constexpr float kBzTolerance = 1.e-3f; // kG - template - bool selectTrack(T const& track) - { - if (std::abs(track.eta()) > etaMax) { - return false; - } - if (track.itsNCls() < minNITSClus || - track.tpcNClsFound() < minNTPCClus || - track.tpcNClsCrossedRows() < minNCrossedRows || - track.tpcNClsCrossedRows() < 0.8f * track.tpcNClsFindable() || - track.tpcNClsShared() > maxNSharedTPCClus) { - LOG(debug) << "Track failed selection: itsNCls=" << static_cast(track.itsNCls()) << ", tpcNClsFound=" << track.tpcNClsFound() << ", tpcNClsCrossedRows=" << static_cast(track.tpcNClsCrossedRows()) << ", tpcNClsFindable=" << static_cast(track.tpcNClsFindable()) << ", tpcNClsShared=" << static_cast(track.tpcNClsShared()); - return false; - } - return true; - } + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; template void initCCDB(Bc const& bc) @@ -259,8 +218,7 @@ struct doubleOmegaTreeCreator { } o2::base::Propagator::initFieldFromGRP(grpmag); mBz = o2::base::Propagator::Instance()->getNominalBz(); - fitter.setBz(mBz); - fitter3Body.setBz(mBz); + helper.setBz(mBz); if (static_cast(cfgMaterialCorrection.value) == o2::base::Propagator::MatCorrType::USEMatCorrLUT) { auto* lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->getForTimeStamp("GLO/Param/MatLUT", bc.timestamp())); @@ -269,8 +227,7 @@ struct doubleOmegaTreeCreator { } o2::base::Propagator::Instance()->setMatLUT(lut); } - fitter.setMatCorrType(static_cast(cfgMaterialCorrection.value)); - fitter3Body.setMatCorrType(static_cast(cfgMaterialCorrection.value)); + helper.setMatCorrType(static_cast(cfgMaterialCorrection.value)); mRunNumber = bc.runNumber(); if (cfgSkimmedProcessing) { @@ -331,10 +288,23 @@ struct doubleOmegaTreeCreator { printValue(minCosPADirectLambda); printValue(minCosPADoubleOmega); printValue(minDoubleOmegaDecayRadius); + printValue(mixingOpts.nMixingCollisions); + printValue(mixingOpts.mixingTypes); + printValue(mixingOpts.maxDeltaPVz); + printAxis(mixingOpts.binsVtxZ); + printAxis(mixingOpts.binsMultiplicity); + printValue(mixingOpts.nMixingCandidates); + printValue(mixingOpts.poolDepth); + printValue(mixingOpts.monitoringInterval); + printAxis(mixingOpts.binsRadius); + printAxis(mixingOpts.binsPhi); printValue(doprocessData); printValue(doprocessDataFromCascades); printValue(doprocessMC); printValue(doprocessFindableTracks); + printValue(doprocessDerivedData); + printValue(doprocessDerivedMixing); + printValue(doprocessDerivedMixingCandidates); } void init(InitContext const&) @@ -346,19 +316,29 @@ struct doubleOmegaTreeCreator { ccdb->setLocalObjectValidityChecking(); ccdb->setFatalWhenNull(false); - fitter.setPropagateToPCA(true); - fitter.setMaxR(200.); - fitter.setMaxDZIni(4.); - fitter.setMinParamChange(1.e-3); - fitter.setMinRelChi2Change(0.9); - fitter.setUseAbsDCA(true); - - fitter3Body.setPropagateToPCA(true); - fitter3Body.setMaxR(200.); - fitter3Body.setMaxDZIni(4.); - fitter3Body.setMinParamChange(1.e-3); - fitter3Body.setMinRelChi2Change(0.9); - fitter3Body.setUseAbsDCA(true); + auto& selections = helper.selections; + selections.etaMax = etaMax; + selections.minNITSClus = minNITSClus; + selections.minNTPCClus = minNTPCClus; + selections.minNCrossedRows = minNCrossedRows; + selections.maxNSharedTPCClus = maxNSharedTPCClus; + selections.nSigmaTPCCut = nSigmaTPCCut; + selections.dcaBachToPV = dcaBachToPV; + selections.dcaKaonToPV = dcaKaonToPV; + selections.dcaOmegaToPV = dcaOmegaToPV; + selections.dcaDirectLambdaToPV = dcaDirectLambdaToPV; + selections.dcaV0DauToPV = dcaV0DauToPV; + selections.dcaV0Bach = dcaV0Bach; + selections.dcaLambdaDaughters = dcaLambdaDaughters; + selections.mOmegaWindow = mOmegaWindow; + selections.mLambdaWindow = mLambdaWindow; + selections.maxDoubleOmegaMass = maxDoubleOmegaMass; + selections.minCosPAOmega = minCosPAOmega; + selections.minCosPADirectLambda = minCosPADirectLambda; + selections.minCosPADoubleOmega = minCosPADoubleOmega; + selections.minDoubleOmegaDecayRadius = minDoubleOmegaDecayRadius; + helper.initFitters(); + helper.setMatCorrType(static_cast(cfgMaterialCorrection.value)); zorroSummary.setObject(zorro.getZorroSummary()); @@ -376,301 +356,29 @@ struct doubleOmegaTreeCreator { generatedAndFindable->GetXaxis()->SetBinLabel(kFindableSelectedTracks + 1, "Findable, selected tracks"); generatedAndFindable->GetXaxis()->SetBinLabel(kFindableCascadeAndV0 + 1, "Findable cascade + V0"); generatedAndFindable->GetXaxis()->SetBinLabel(kFindableCascadeAndV0SelectedTracks + 1, "Findable cascade + V0, selected tracks"); - } - - template - bool isSelectedKaon(Track const& track, int8_t charge) - { - return track.sign() == charge && - selectTrack(track) && - std::abs(track.tpcNSigmaKa()) <= nSigmaTPCCut; - } - - static float invariantMass2Body(std::array const& momentum1, float mass1, - std::array const& momentum2, float mass2) - { - const float momentum1Squared = momentum1[0] * momentum1[0] + momentum1[1] * momentum1[1] + momentum1[2] * momentum1[2]; - const float momentum2Squared = momentum2[0] * momentum2[0] + momentum2[1] * momentum2[1] + momentum2[2] * momentum2[2]; - const float energy = std::sqrt(momentum1Squared + mass1 * mass1) + std::sqrt(momentum2Squared + mass2 * mass2); - const std::array totalMomentum{ - momentum1[0] + momentum2[0], momentum1[1] + momentum2[1], momentum1[2] + momentum2[2]}; - const float massSquared = energy * energy - totalMomentum[0] * totalMomentum[0] - totalMomentum[1] * totalMomentum[1] - totalMomentum[2] * totalMomentum[2]; - return std::sqrt(std::max(0.f, massSquared)); - } - - // Transverse DCA of a track to the collision primary vertex, computed by propagating - // a copy of the track parameters. Returns false if the propagation fails. - template - bool dcaXYToPV(C const& collision, Track const& track, float& dcaXY) const - { - auto trackParCov = getTrackParCov(track); - const o2::math_utils::Point3D primaryVertex{collision.posX(), collision.posY(), collision.posZ()}; - const auto matCorr = static_cast(cfgMaterialCorrection.value); - std::array dca{}; - if (!o2::base::Propagator::Instance()->propagateToDCABxByBz(primaryVertex, trackParCov, 2.f, matCorr, &dca)) { - return false; - } - dcaXY = dca[0]; - return true; - } - - template - bool buildLambda(C const& collision, Track const& posTrack, Track const& negTrack, int8_t charge, BuiltLambdaCandidate& builtLambda) - { - if (posTrack.sign() <= 0 || negTrack.sign() >= 0 || - !selectTrack(posTrack) || !selectTrack(negTrack)) { - return false; - } - - const bool isMatter = charge < 0; - const float protonNSigma = isMatter ? posTrack.tpcNSigmaPr() : negTrack.tpcNSigmaPr(); - const float pionNSigma = isMatter ? negTrack.tpcNSigmaPi() : posTrack.tpcNSigmaPi(); - if (std::abs(protonNSigma) > nSigmaTPCCut || std::abs(pionNSigma) > nSigmaTPCCut) { - return false; - } - float dcaXYPos = -999.f; - float dcaXYNeg = -999.f; - if (!dcaXYToPV(collision, posTrack, dcaXYPos) || !dcaXYToPV(collision, negTrack, dcaXYNeg)) { - LOG(debug) << "Propagation to the PV failed for Lambda daughters " << posTrack.globalIndex() << ", " << negTrack.globalIndex(); - return false; - } - if (std::abs(dcaXYPos) < dcaV0DauToPV || std::abs(dcaXYNeg) < dcaV0DauToPV) { - LOG(debug) << "Lambda dau don't pass selections: " << "nsigmaPR: " << protonNSigma << ", pionNSigma: " << pionNSigma << ", dcaXYPos: " << dcaXYPos << ", dcaXYNeg: " << dcaXYNeg; - return false; - } - - auto posTrackParCov = getTrackParCov(posTrack); - auto negTrackParCov = getTrackParCov(negTrack); - int nCandidates = 0; - try { - nCandidates = fitter.process(posTrackParCov, negTrackParCov); - } catch (...) { - LOG(error) << "Exception while fitting Lambda daughter tracks " << posTrack.globalIndex() << ", " << negTrack.globalIndex(); - return false; - } - if (nCandidates == 0) { - return false; - } - - std::array posMomentum{}; - std::array negMomentum{}; - fitter.getTrack(0).getPxPyPzGlo(posMomentum); - fitter.getTrack(1).getPxPyPzGlo(negMomentum); - const std::array lambdaMomentum{ - posMomentum[0] + negMomentum[0], - posMomentum[1] + negMomentum[1], - posMomentum[2] + negMomentum[2]}; - const auto& fittedVertex = fitter.getPCACandidate(); - const std::array decayVertex{ - static_cast(fittedVertex[0]), static_cast(fittedVertex[1]), static_cast(fittedVertex[2])}; - const float mass = isMatter ? invariantMass2Body(posMomentum, o2::constants::physics::MassProton, - negMomentum, o2::constants::physics::MassPionCharged) - : invariantMass2Body(posMomentum, o2::constants::physics::MassPionCharged, - negMomentum, o2::constants::physics::MassProton); - const float dcaDaughters = std::sqrt(std::abs(fitter.getChi2AtPCACandidate())); - const float eta = etaFromMomentum(lambdaMomentum[0], lambdaMomentum[1], lambdaMomentum[2]); - if (std::abs(eta) > etaMax || - dcaDaughters > dcaLambdaDaughters || - std::abs(mass - o2::constants::physics::MassLambda0) > mLambdaWindow) { - return false; - } - - builtLambda.candidate = { - .px = lambdaMomentum[0], - .py = lambdaMomentum[1], - .pz = lambdaMomentum[2], - .mass = mass}; - builtLambda.parentTrack = fitter.createParentTrackParCov(0); - builtLambda.decayVertex = decayVertex; - builtLambda.posTrackId = posTrack.globalIndex(); - builtLambda.negTrackId = negTrack.globalIndex(); - // Keep particle-role ordering for matter and antimatter alike. - builtLambda.daughterDetectorMap = isMatter ? detectorMap(posTrack) | (detectorMap(negTrack) << 3) - : detectorMap(negTrack) | (detectorMap(posTrack) << 3); - builtLambda.dcaXYProtonToPV = isMatter ? dcaXYPos : dcaXYNeg; - builtLambda.dcaXYPionToPV = isMatter ? dcaXYNeg : dcaXYPos; - return true; - } - - template - bool buildOmega(C const& collision, Track const& posTrack, Track const& negTrack, Track const& bachelor, BuiltOmegaCandidate& builtOmega) - { - if (bachelor.sign() == 0 || - !selectTrack(bachelor) || - std::abs(bachelor.tpcNSigmaKa()) > nSigmaTPCCut) { - return false; - } - float dcaXYBachelor = -999.f; - if (!dcaXYToPV(collision, bachelor, dcaXYBachelor) || std::abs(dcaXYBachelor) < dcaBachToPV) { - return false; - } - - BuiltLambdaCandidate lambda; - if (!buildLambda(collision, posTrack, negTrack, bachelor.sign(), lambda)) { - return false; - } - auto v0TrackParCov = lambda.parentTrack; - auto bachelorTrackParCov = getTrackParCov(bachelor); - int nCandidates = 0; - try { - nCandidates = fitter.process(v0TrackParCov, bachelorTrackParCov); - } catch (...) { - LOG(error) << "Exception while fitting Omega daughter tracks " << posTrack.globalIndex() << ", " << negTrack.globalIndex() << ", " << bachelor.globalIndex(); - return false; - } - if (nCandidates == 0) { - return false; + if (doprocessDerivedMixingCandidates) { + histos.add("Mixing/hSeedRadiusPhi", ";Double-#Omega decay radius (cm);Double-#Omega #varphi (rad);Same-event candidates", HistType::kTH2F, {mixingOpts.binsRadius, mixingOpts.binsPhi}); + // Resource monitoring of the candidate-level mixing, one entry per dataframe. + histos.add("Mixing/hProcessingTime", ";Processing time per dataframe (ms);Dataframes", HistType::kTH1F, {{500, 0.f, 5000.f}}); + histos.add("Mixing/hPoolSeeds", ";Seeds in the mixing pool;Dataframes", HistType::kTH1F, {{500, 0.f, 50000.f}}); + histos.add("Mixing/hPoolMemory", ";Estimated mixing-pool memory (MB);Dataframes", HistType::kTH1F, {{500, 0.f, 50.f}}); + histos.add("Mixing/hProcessingTimeVsPoolSeeds", ";Seeds in the mixing pool;Processing time per dataframe (ms)", HistType::kTH2F, {{100, 0.f, 50000.f}, {100, 0.f, 5000.f}}); } - - std::array lambdaMomentum{}; - std::array bachelorMomentum{}; - fitter.getTrack(0).getPxPyPzGlo(lambdaMomentum); - fitter.getTrack(1).getPxPyPzGlo(bachelorMomentum); - const std::array omegaMomentum{ - lambdaMomentum[0] + bachelorMomentum[0], - lambdaMomentum[1] + bachelorMomentum[1], - lambdaMomentum[2] + bachelorMomentum[2]}; - const auto& fittedVertex = fitter.getPCACandidate(); - const std::array decayVertex{ - static_cast(fittedVertex[0]), static_cast(fittedVertex[1]), static_cast(fittedVertex[2])}; - const float massOmega = invariantMass2Body(lambdaMomentum, o2::constants::physics::MassLambda0, - bachelorMomentum, o2::constants::physics::MassKaonCharged); - const float massXi = invariantMass2Body(lambdaMomentum, o2::constants::physics::MassLambda0, - bachelorMomentum, o2::constants::physics::MassPionCharged); - const float dcaDaughters = std::sqrt(std::abs(fitter.getChi2AtPCACandidate())); - const float eta = etaFromMomentum(omegaMomentum[0], omegaMomentum[1], omegaMomentum[2]); - if (dcaDaughters > dcaV0Bach || - std::abs(eta) > etaMax || - std::abs(massOmega - o2::constants::physics::MassOmegaMinus) > mOmegaWindow) { - return false; + if (doprocessDerivedMixing || doprocessDerivedMixingCandidates) { + histos.add("Mixing/hMixedCollisions", ";;Entries", HistType::kTH1F, {{4, -0.5f, 3.5f}}); + auto mixedCollisions = histos.get(HIST("Mixing/hMixedCollisions")); + mixedCollisions->GetXaxis()->SetBinLabel(1, "Pairs"); + mixedCollisions->GetXaxis()->SetBinLabel(2, "Pairs, #Delta#it{z}_{PV}, same #it{B}"); + mixedCollisions->GetXaxis()->SetBinLabel(3, "Triples"); + mixedCollisions->GetXaxis()->SetBinLabel(4, "Triples, #Delta#it{z}_{PV}, same #it{B}"); + histos.add("Mixing/hMixedCandidates", ";;Entries", HistType::kTH1F, {{kNMixingTypes - 1, 0.5f, static_cast(kNMixingTypes) - 0.5f}}); + auto mixedCandidates = histos.get(HIST("Mixing/hMixedCandidates")); + mixedCandidates->GetXaxis()->SetBinLabel(kMixedOmega, "#Omega mixed"); + mixedCandidates->GetXaxis()->SetBinLabel(kMixedLambda, "#Lambda mixed"); + mixedCandidates->GetXaxis()->SetBinLabel(kMixedKaon, "K mixed"); + mixedCandidates->GetXaxis()->SetBinLabel(kMixedAll, "All mixed"); } - - builtOmega = { - omegaMomentum[0], - omegaMomentum[1], - omegaMomentum[2], - decayVertex[0], - decayVertex[1], - decayVertex[2], - massOmega, - massXi, - static_cast(bachelor.sign()), - posTrack.globalIndex(), - negTrack.globalIndex(), - bachelor.globalIndex(), - fitter.createParentTrackParCov(), - detectorMap(bachelor) | (lambda.daughterDetectorMap << 3), - dcaXYBachelor, - lambda.dcaXYProtonToPV, - lambda.dcaXYPionToPV}; - return true; - } - - static float etaFromMomentum(float px, float py, float pz) - { - const float pt = std::hypot(px, py); - return pt > 0.f ? std::asinh(pz / pt) : 0.f; - } - - template - bool buildDoubleOmega(C const& collision, BuiltOmegaCandidate const& omega, BuiltLambdaCandidate const& directLambda, Kaon const& kaon, DoubleOmegaCandidate& cand) - { - auto omegaTrackParCov = omega.parentTrack; - auto lambdaTrackParCov = directLambda.parentTrack; - auto kaonTrackParCov = getTrackParCov(kaon); - int nCandidates = 0; - try { - nCandidates = fitter3Body.process(omegaTrackParCov, lambdaTrackParCov, kaonTrackParCov); - } catch (...) { - LOG(error) << "Exception while fitting the double-Omega candidate"; - return false; - } - if (nCandidates == 0) { - return false; - } - - auto omegaTrackAtVertex = fitter3Body.getTrack(0); - auto lambdaTrackAtVertex = fitter3Body.getTrack(1); - auto kaonTrackAtVertex = fitter3Body.getTrack(2); - std::array omegaMomentum{}; - std::array lambdaMomentum{}; - std::array kaonMomentum{}; - omegaTrackAtVertex.getPxPyPzGlo(omegaMomentum); - lambdaTrackAtVertex.getPxPyPzGlo(lambdaMomentum); - kaonTrackAtVertex.getPxPyPzGlo(kaonMomentum); - - const std::array totalMomentum{ - omegaMomentum[0] + lambdaMomentum[0] + kaonMomentum[0], - omegaMomentum[1] + lambdaMomentum[1] + kaonMomentum[1], - omegaMomentum[2] + lambdaMomentum[2] + kaonMomentum[2]}; - const auto& fittedVertex = fitter3Body.getPCACandidate(); - const std::array decayVertex{ - static_cast(fittedVertex[0]), static_cast(fittedVertex[1]), static_cast(fittedVertex[2])}; - const std::array primaryVertex{collision.posX(), collision.posY(), collision.posZ()}; - const o2::math_utils::Point3D primaryVertexPoint{collision.posX(), collision.posY(), collision.posZ()}; - const std::array omegaDecayVertex{omega.x, omega.y, omega.z}; - - std::array dcaOmega{}; - std::array dcaDirectLambda{}; - std::array dcaDirectKaon{}; - const auto matCorr = static_cast(cfgMaterialCorrection.value); - if (!o2::base::Propagator::Instance()->propagateToDCABxByBz(primaryVertexPoint, omegaTrackAtVertex, 2.f, matCorr, &dcaOmega) || - !o2::base::Propagator::Instance()->propagateToDCABxByBz(primaryVertexPoint, lambdaTrackAtVertex, 2.f, matCorr, &dcaDirectLambda) || - !o2::base::Propagator::Instance()->propagateToDCABxByBz(primaryVertexPoint, kaonTrackAtVertex, 2.f, matCorr, &dcaDirectKaon)) { - return false; - } - - const auto momentumSquared = [](std::array const& momentum) { - return momentum[0] * momentum[0] + momentum[1] * momentum[1] + momentum[2] * momentum[2]; - }; - const float energy = std::sqrt(momentumSquared(omegaMomentum) + o2::constants::physics::MassOmegaMinus * o2::constants::physics::MassOmegaMinus) + - std::sqrt(momentumSquared(lambdaMomentum) + o2::constants::physics::MassLambda0 * o2::constants::physics::MassLambda0) + - std::sqrt(momentumSquared(kaonMomentum) + o2::constants::physics::MassKaonCharged * o2::constants::physics::MassKaonCharged); - const float massSquared = energy * energy - totalMomentum[0] * totalMomentum[0] - totalMomentum[1] * totalMomentum[1] - totalMomentum[2] * totalMomentum[2]; - - cand.pt = std::hypot(totalMomentum[0], totalMomentum[1]); - cand.eta = etaFromMomentum(totalMomentum[0], totalMomentum[1], totalMomentum[2]); - cand.phi = std::atan2(totalMomentum[1], totalMomentum[0]); - cand.x = decayVertex[0]; - cand.y = decayVertex[1]; - cand.z = decayVertex[2]; - cand.pvX = primaryVertex[0]; - cand.pvY = primaryVertex[1]; - cand.pvZ = primaryVertex[2]; - cand.cosPAOmega = RecoDecay::cpa(decayVertex, omegaDecayVertex, omegaMomentum); - cand.cosPADirectLambda = RecoDecay::cpa(decayVertex, directLambda.decayVertex, lambdaMomentum); - cand.cosPADoubleOmega = RecoDecay::cpa(primaryVertex, decayVertex, totalMomentum); - // PV pointing angles use the daughter momenta at their own decay vertices. - cand.cosPAOmegaToPV = RecoDecay::cpa(primaryVertex, omegaDecayVertex, std::array{omega.px, omega.py, omega.pz}); - cand.cosPADirectLambdaToPV = RecoDecay::cpa(primaryVertex, directLambda.decayVertex, std::array{directLambda.candidate.px, directLambda.candidate.py, directLambda.candidate.pz}); - cand.dcaXYOmegaToPV = dcaOmega[0]; - cand.dcaXYDirectLambdaToPV = dcaDirectLambda[0]; - cand.dcaXYDirectKaonToPV = dcaDirectKaon[0]; - cand.dcaXYOmegaKaonToPV = omega.dcaXYKaonToPV; - cand.dcaXYOmegaProtonToPV = omega.dcaXYProtonToPV; - cand.dcaXYOmegaPionToPV = omega.dcaXYPionToPV; - cand.dcaXYDirectProtonToPV = directLambda.dcaXYProtonToPV; - cand.dcaXYDirectPionToPV = directLambda.dcaXYPionToPV; - cand.tpcNSigmaDirectKaon = kaon.hasTPC() ? kaon.tpcNSigmaKa() : -999.f; - cand.tofNSigmaDirectKaon = kaon.hasTOF() ? kaon.tofNSigmaKa() : -999.f; - cand.daughterDetectorMap = omega.daughterDetectorMap | - (directLambda.daughterDetectorMap << 9) | - (detectorMap(kaon) << 15); - cand.mass = std::sqrt(std::max(0.f, massSquared)); - if (cand.cosPAOmega < minCosPAOmega || - cand.cosPADirectLambda < minCosPADirectLambda || - cand.cosPADoubleOmega < minCosPADoubleOmega || - std::hypot(decayVertex[0], decayVertex[1]) < minDoubleOmegaDecayRadius || - std::abs(cand.dcaXYOmegaToPV) < dcaOmegaToPV || - std::abs(cand.dcaXYDirectLambdaToPV) < dcaDirectLambdaToPV || - std::abs(cand.dcaXYDirectKaonToPV) < dcaKaonToPV || - cand.mass > maxDoubleOmegaMass) { - return false; - } - cand.massOmega = omega.massOmega; - cand.massXi = omega.massXi; - return true; } template @@ -682,9 +390,9 @@ struct doubleOmegaTreeCreator { auto mcPosTrack = posTrack.template mcParticle_as(); auto mcNegTrack = negTrack.template mcParticle_as(); - const int expectedPosPdg = sign < 0 ? 2212 : 211; - const int expectedNegPdg = sign < 0 ? -211 : -2212; - const int expectedLambdaPdg = sign < 0 ? 3122 : -3122; + const int expectedPosPdg = sign < 0 ? PDG_t::kProton : PDG_t::kPiPlus; + const int expectedNegPdg = sign < 0 ? PDG_t::kPiMinus : PDG_t::kProtonBar; + const int expectedLambdaPdg = sign < 0 ? PDG_t::kLambda0 : PDG_t::kLambda0Bar; if (mcPosTrack.pdgCode() != expectedPosPdg || mcNegTrack.pdgCode() != expectedNegPdg) { return -1; } @@ -713,8 +421,8 @@ struct doubleOmegaTreeCreator { auto mcLambda = mcParticles.rawIteratorAt(lambdaLabel); auto mcBachelor = bachelorTrack.template mcParticle_as(); - const int expectedBachelorPdg = sign < 0 ? -321 : 321; - const int expectedOmegaPdg = sign < 0 ? 3334 : -3334; + const int expectedBachelorPdg = sign < 0 ? PDG_t::kKMinus : PDG_t::kKPlus; + const int expectedOmegaPdg = sign < 0 ? PDG_t::kOmegaMinus : PDG_t::kOmegaPlusBar; if (mcBachelor.pdgCode() != expectedBachelorPdg) { return -1; } @@ -747,7 +455,7 @@ struct doubleOmegaTreeCreator { auto mcOmega = mcParticles.rawIteratorAt(omegaLabel); auto mcLambda = mcParticles.rawIteratorAt(lambdaLabel); auto mcKaon = directKaonTrack.template mcParticle_as(); - const int expectedKaonPdg = sign < 0 ? -321 : 321; + const int expectedKaonPdg = sign < 0 ? PDG_t::kKMinus : PDG_t::kKPlus; const int expectedDoubleOmegaPdg = sign < 0 ? kDoubleOmegaPdg : -kDoubleOmegaPdg; if (mcKaon.pdgCode() != expectedKaonPdg) { return false; @@ -771,6 +479,7 @@ struct doubleOmegaTreeCreator { mcOmega.vy() - omegaMother.vy(), mcOmega.vz() - omegaMother.vz()); mcInfo.pdgCode = omegaMother.pdgCode(); + mcInfo.sign = sign; return true; } } @@ -787,9 +496,9 @@ struct doubleOmegaTreeCreator { } const bool isMatter = particle.pdgCode() > 0; - const int expectedOmegaPdg = isMatter ? 3334 : -3334; - const int expectedLambdaPdg = isMatter ? 3122 : -3122; - const int expectedKaonPdg = isMatter ? -321 : 321; + const int expectedOmegaPdg = isMatter ? PDG_t::kOmegaMinus : PDG_t::kOmegaPlusBar; + const int expectedLambdaPdg = isMatter ? PDG_t::kLambda0 : PDG_t::kLambda0Bar; + const int expectedKaonPdg = isMatter ? PDG_t::kKMinus : PDG_t::kKPlus; bool foundOmega = false; bool foundLambda = false; bool foundKaon = false; @@ -826,6 +535,7 @@ struct doubleOmegaTreeCreator { info.genProductionVertex = {particle.vx(), particle.vy(), particle.vz()}; info.genDecayVertex = decayVertex; info.genOmegaDecayVertex = omegaDecayVertex; + info.sign = isMatter ? -1 : 1; return true; } @@ -848,7 +558,7 @@ struct doubleOmegaTreeCreator { void writeDataCandidate(DoubleOmegaCandidate const& cand) { - doubleOmegaTable(cand.pt, + doubleOmegaTable(cand.pt * cand.sign, cand.eta, cand.phi, cand.x, @@ -878,9 +588,42 @@ struct doubleOmegaTreeCreator { cand.massXi); } + void writeMixedCandidate(DoubleOmegaCandidate const& cand, MixingType mixingType) + { + doubleOmegaMixedTable(cand.pt * cand.sign, + cand.eta, + cand.phi, + cand.x, + cand.y, + cand.z, + cand.pvX, + cand.pvY, + cand.pvZ, + cand.cosPAOmega, + cand.cosPADirectLambda, + cand.cosPADoubleOmega, + cand.cosPAOmegaToPV, + cand.cosPADirectLambdaToPV, + cand.dcaXYOmegaToPV, + cand.dcaXYDirectLambdaToPV, + cand.dcaXYDirectKaonToPV, + cand.dcaXYOmegaKaonToPV, + cand.dcaXYOmegaProtonToPV, + cand.dcaXYOmegaPionToPV, + cand.dcaXYDirectProtonToPV, + cand.dcaXYDirectPionToPV, + cand.tpcNSigmaDirectKaon, + cand.tofNSigmaDirectKaon, + cand.daughterDetectorMap, + cand.mass, + cand.massOmega, + cand.massXi, + mixingType); + } + void writeMCCandidate(DoubleOmegaCandidate const& cand, DoubleOmegaMCInfo const& mcInfo, bool isReco) { - doubleOmegaTableMC(cand.pt, + doubleOmegaTableMC(cand.pt * cand.sign, cand.eta, cand.phi, cand.x, @@ -908,7 +651,7 @@ struct doubleOmegaTreeCreator { cand.mass, cand.massOmega, cand.massXi, - mcInfo.pt, + mcInfo.pt * mcInfo.sign, mcInfo.eta, mcInfo.phi, mcInfo.decayLength, @@ -938,74 +681,34 @@ struct doubleOmegaTreeCreator { LOG(debug) << "collision " << collision.globalIndex() << " (direct kaon), numContrib = " << collision.numContrib(); } - template - void fillFromRawTables(C const& collision, T const& tracks, Cascades const& cascades, V0s const& v0s, - aod::McParticles const* mcParticles = nullptr) + void fillCandidateQA(CollisionCandidates const& candidates) { - for (const auto& rawCascade : cascades) { - const auto omegaV0 = rawCascade.template v0_as(); - const auto omegaPosTrack = omegaV0.template posTrack_as(); - const auto omegaNegTrack = omegaV0.template negTrack_as(); - const auto omegaBachelorTrack = rawCascade.template bachelor_as(); - BuiltOmegaCandidate omega; - if (!buildOmega(collision, omegaPosTrack, omegaNegTrack, omegaBachelorTrack, omega)) { - continue; - } + for (const auto& omega : candidates.omegas) { const float omegaPt = std::hypot(omega.px, omega.py); histos.fill(HIST("QA/massXi"), omegaPt, omega.massXi); histos.fill(HIST("QA/massOmega"), omegaPt, omega.massOmega); - const std::array omegaTrackIds{omega.posTrackId, omega.negTrackId, omega.bachelorId}; - - for (const auto& rawV0 : v0s) { - const auto lambdaPosTrack = rawV0.template posTrack_as(); - const auto lambdaNegTrack = rawV0.template negTrack_as(); - BuiltLambdaCandidate lambda; - if (!buildLambda(collision, lambdaPosTrack, lambdaNegTrack, omega.sign, lambda)) { - continue; - } - if (std::find(omegaTrackIds.begin(), omegaTrackIds.end(), lambda.posTrackId) != omegaTrackIds.end() || - std::find(omegaTrackIds.begin(), omegaTrackIds.end(), lambda.negTrackId) != omegaTrackIds.end()) { - continue; - } + } + for (const auto& lambdas : candidates.lambdas) { + for (const auto& lambda : lambdas) { histos.fill(HIST("QA/massLambda"), std::hypot(lambda.candidate.px, lambda.candidate.py), lambda.candidate.mass); - - for (const auto& kaon : tracks) { - if (!isSelectedKaon(kaon, omega.sign) || - kaon.globalIndex() == lambda.posTrackId || - kaon.globalIndex() == lambda.negTrackId || - std::find(omegaTrackIds.begin(), omegaTrackIds.end(), kaon.globalIndex()) != omegaTrackIds.end()) { - continue; - } - DoubleOmegaCandidate cand; - if (!buildDoubleOmega(collision, omega, lambda, kaon, cand)) { - continue; - } - if constexpr (isMC) { - if (mcParticles == nullptr) { - continue; - } - DoubleOmegaMCInfo mcInfo; - if (getMCInfo(mcInfo, - omegaPosTrack, omegaNegTrack, omegaBachelorTrack, - lambdaPosTrack, lambdaNegTrack, kaon, - omega.sign, *mcParticles)) { - reconstructedDoubleOmegaIds.push_back(mcInfo.motherId); - writeMCCandidate(cand, mcInfo, true); - } - } else { - writeDataCandidate(cand); - } - } } } } + template + CollisionCandidates buildCollisionCandidates(C const& collision, T const& tracks, Cascades const& cascades, V0s const& v0s, bool requireOmega) + { + auto candidates = helper.buildCollisionCandidates(collision, tracks, cascades, v0s, requireOmega); + fillCandidateQA(candidates); + return candidates; + } + template void fillFromCascades(C const& collision, T const&, Cascades const& cascades) { for (const auto& omegaRow : cascades) { BuiltOmegaCandidate omega; - if (!buildOmega(collision, omegaRow.template posTrack_as(), omegaRow.template negTrack_as(), omegaRow.template bachelor_as(), omega)) { + if (!helper.buildOmega(collision, omegaRow.template posTrack_as(), omegaRow.template negTrack_as(), omegaRow.template bachelor_as(), omega)) { continue; } const float omegaPt = std::hypot(omega.px, omega.py); @@ -1033,16 +736,16 @@ struct doubleOmegaTreeCreator { } auto kaon = lambdaKaonSource.template bachelor_as(); - if (!isSelectedKaon(kaon, omega.sign)) { + if (!helper.isSelectedKaon(kaon, omega.sign)) { continue; } BuiltLambdaCandidate lambda; - if (!buildLambda(collision, lambdaKaonSource.template posTrack_as(), lambdaKaonSource.template negTrack_as(), omega.sign, lambda)) { + if (!helper.buildLambda(collision, lambdaKaonSource.template posTrack_as(), lambdaKaonSource.template negTrack_as(), omega.sign, lambda)) { continue; } histos.fill(HIST("QA/massLambda"), std::hypot(lambda.candidate.px, lambda.candidate.py), lambda.candidate.mass); DoubleOmegaCandidate cand; - if (buildDoubleOmega(collision, omega, lambda, kaon, cand)) { + if (helper.buildDoubleOmega({collision.posX(), collision.posY(), collision.posZ()}, omega, lambda, DoubleOmegaBuilderHelper::buildKaon(kaon), cand)) { writeDataCandidate(cand); } } @@ -1074,7 +777,7 @@ struct doubleOmegaTreeCreator { const auto lambdaPosTrack = tracks.rawIteratorAt(daughterTrackIds[isMatter ? 3 : 4]); const auto lambdaNegTrack = tracks.rawIteratorAt(daughterTrackIds[isMatter ? 4 : 3]); const auto kaon = tracks.rawIteratorAt(daughterTrackIds[5]); - if (!isSelectedKaon(kaon, charge)) { + if (!helper.isSelectedKaon(kaon, charge)) { return false; } const int64_t collisionId = selectFindableCollision(kaon); @@ -1086,19 +789,19 @@ struct doubleOmegaTreeCreator { initCCDB(bc); BuiltOmegaCandidate omega; - if (!buildOmega(collision, omegaPosTrack, omegaNegTrack, bachelor, omega)) { + if (!helper.buildOmega(collision, omegaPosTrack, omegaNegTrack, bachelor, omega)) { return false; } const float omegaPt = std::hypot(omega.px, omega.py); histos.fill(HIST("QA/massXi"), omegaPt, omega.massXi); histos.fill(HIST("QA/massOmega"), omegaPt, omega.massOmega); BuiltLambdaCandidate lambda; - if (!buildLambda(collision, lambdaPosTrack, lambdaNegTrack, charge, lambda)) { + if (!helper.buildLambda(collision, lambdaPosTrack, lambdaNegTrack, charge, lambda)) { return false; } histos.fill(HIST("QA/massLambda"), std::hypot(lambda.candidate.px, lambda.candidate.py), lambda.candidate.mass); DoubleOmegaCandidate cand; - if (!buildDoubleOmega(collision, omega, lambda, kaon, cand)) { + if (!helper.buildDoubleOmega({collision.posX(), collision.posY(), collision.posZ()}, omega, lambda, DoubleOmegaBuilderHelper::buildKaon(kaon), cand)) { return false; } logVertexComparison(collision, cand, omega, mcInfo); @@ -1126,15 +829,54 @@ struct doubleOmegaTreeCreator { return true; } + void mix(CollisionCandidates const& reference, CollisionCandidates const& omegaSource, + CollisionCandidates const& lambdaSource, CollisionCandidates const& kaonSource, MixingType mixingType) + { + if (!(mixingOpts.mixingTypes & (1 << (mixingType - 1)))) { + return; + } + helper.combine(reference, omegaSource, lambdaSource, kaonSource, [&](DoubleOmegaCandidate const& cand, auto const&...) { + histos.fill(HIST("Mixing/hMixedCandidates"), mixingType); + writeMixedCandidate(cand, mixingType); + }); + } + + // Two candidates from one collision and one from the other, in both directions. The collision + // providing two candidates is the reference one. + void mixPair(CollisionCandidates const& candidates0, CollisionCandidates const& candidates1) + { + mix(candidates0, candidates1, candidates0, candidates0, kMixedOmega); + mix(candidates1, candidates0, candidates1, candidates1, kMixedOmega); + mix(candidates0, candidates0, candidates1, candidates0, kMixedLambda); + mix(candidates1, candidates1, candidates0, candidates1, kMixedLambda); + mix(candidates0, candidates0, candidates0, candidates1, kMixedKaon); + mix(candidates1, candidates1, candidates1, candidates0, kMixedKaon); + } + + // All the assignments of the Omega, Lambda and kaon to three different collisions. The collision + // providing the Omega is the reference one. + void mixTriple(CollisionCandidates const& candidates0, CollisionCandidates const& candidates1, CollisionCandidates const& candidates2) + { + const std::array sources{&candidates0, &candidates1, &candidates2}; + std::array roles{0, 1, 2}; // Sources of the Omega, Lambda and kaon. + do { + const auto& omegaSource = *sources[roles[0]]; + mix(omegaSource, omegaSource, *sources[roles[1]], *sources[roles[2]], kMixedAll); + } while (std::next_permutation(roles.begin(), roles.end())); + } + void processData(Collisions const& collision, TracksFull const& tracks, aod::V0s const& v0s, aod::Cascades const& cascades, aod::BCsWithTimestamps const& bcs) { - if (acceptCollision(collision, bcs)) { - fillFromRawTables(collision, tracks, cascades, v0s); + if (!acceptCollision(collision, bcs)) { + return; } + const auto candidates = buildCollisionCandidates(collision, tracks, cascades, v0s, true); + helper.combine(candidates, candidates, candidates, candidates, + [&](DoubleOmegaCandidate const& cand, auto const&...) { writeDataCandidate(cand); }); } PROCESS_SWITCH(doubleOmegaTreeCreator, processData, "Reconstruct Omega + Lambda + kaon from raw V0/cascade indices", true); @@ -1170,7 +912,18 @@ struct doubleOmegaTreeCreator { v0sThisCollision.bindExternalIndices(&tracks); cascadesThisCollision.bindExternalIndices(&tracks); cascadesThisCollision.bindExternalIndices(&v0s); - fillFromRawTables(collision, tracksThisCollision, cascadesThisCollision, v0sThisCollision, &mcParticles); + const auto candidates = buildCollisionCandidates(collision, tracksThisCollision, cascadesThisCollision, v0sThisCollision, true); + helper.combine(candidates, candidates, candidates, candidates, + [&](DoubleOmegaCandidate const& cand, BuiltOmegaCandidate const& omega, BuiltLambdaCandidate const& lambda, BuiltKaonCandidate const& kaon) { + DoubleOmegaMCInfo mcInfo; + if (getMCInfo(mcInfo, + tracks.rawIteratorAt(omega.posTrackId), tracks.rawIteratorAt(omega.negTrackId), tracks.rawIteratorAt(omega.bachelorId), + tracks.rawIteratorAt(lambda.posTrackId), tracks.rawIteratorAt(lambda.negTrackId), tracks.rawIteratorAt(kaon.trackId), + omega.sign, mcParticles)) { + reconstructedDoubleOmegaIds.push_back(mcInfo.motherId); + writeMCCandidate(cand, mcInfo, true); + } + }); } for (const auto& mcParticle : mcParticles) { DoubleOmegaMCInfo mcInfo; @@ -1274,7 +1027,7 @@ struct doubleOmegaTreeCreator { if (daughterTrackIds[daughterIndex] < 0) { daughterTrackIds[daughterIndex] = track.globalIndex(); } - if (selectTrack(track)) { + if (helper.selectTrack(track)) { selectedReconstructedDaughters[daughterIndex] = true; if (selectedTrackIds[daughterIndex] < 0) { selectedTrackIds[daughterIndex] = track.globalIndex(); @@ -1380,6 +1133,384 @@ struct doubleOmegaTreeCreator { } } PROCESS_SWITCH(doubleOmegaTreeCreator, processFindableTracks, "Count findable double-Omega decays and reconstruct selected daughter tracks", false); + + // Bin index of value on a ConfigurableAxis (variable or fixed width), -1 outside the axis. + static int findBin(std::vector const& axis, double value) + { + if (axis.size() < 3) { + return -1; + } + if (axis[0] == VARIABLE_WIDTH) { + const auto edge = std::upper_bound(axis.begin() + 1, axis.end(), value); + if (edge == axis.begin() + 1 || edge == axis.end()) { + return -1; + } + return static_cast(std::distance(axis.begin() + 2, edge)); + } + const int nBins = static_cast(axis[0]); + if (value < axis[1] || value >= axis[2]) { + return -1; + } + return static_cast((value - axis[1]) / (axis[2] - axis[1]) * nBins); + } + + static int nBins(std::vector const& axis) + { + return axis[0] == VARIABLE_WIDTH ? static_cast(axis.size()) - 2 : static_cast(axis[0]); + } + + // Daughter track indices identifying a combination. Seeds sharing a daughter would produce the same + // combination more than once: each combination is written only once. + using MixedCombination = std::array; + + // Mixing pool of same-event candidates per (radius, phi, charge) bin, kept across dataframes. + std::unordered_map> mSeedPool; + std::size_t mPoolSeeds = 0; + int64_t mDataframeCounter = 0; + double mProcessingTimeSum = 0.; + + // Indices are only unique within a dataframe: the dataframe counter goes in the upper bits. + int64_t uniqueIndex(int64_t index) const + { + return index < 0 ? index : (mDataframeCounter << 32) | index; + } + + // The track identifiers read from the derived tables (see trackFingerprint) already identify a track + // in any dataframe; the collision index is made unique across dataframes. + MixingSeed makeSeed(int64_t collisionId, std::array const& primaryVertex, BuiltOmegaCandidate const& omega, + BuiltLambdaCandidate const& lambda, BuiltKaonCandidate const& kaon) const + { + return {uniqueIndex(collisionId), primaryVertex, mBz, omega, lambda, kaon}; + } + + // Builds and writes one mixed candidate, with the primary vertex of the reference seed. + void mixSeedDaughters(MixingSeed const& reference, BuiltOmegaCandidate const& omega, BuiltLambdaCandidate const& lambda, + BuiltKaonCandidate const& kaon, MixingType mixingType, std::set& written) + { + if (!(mixingOpts.mixingTypes & (1 << (mixingType - 1)))) { + return; + } + const MixedCombination combination{omega.posTrackId, omega.negTrackId, omega.bachelorId, lambda.posTrackId, lambda.negTrackId, kaon.trackId}; + if (!written.insert(combination).second || DoubleOmegaBuilderHelper::sharesTracks(omega, lambda, kaon)) { + return; + } + DoubleOmegaCandidate cand; + if (!helper.buildDoubleOmega(reference.primaryVertex, omega, lambda, kaon, cand)) { + return; + } + histos.fill(HIST("Mixing/hMixedCandidates"), mixingType); + writeMixedCandidate(cand, mixingType); + } + + // One daughter of each seed replaced by the corresponding daughter of the other seed. The seed + // keeping two of its daughters provides the primary vertex. + void mixSeedPair(MixingSeed const& seed0, MixingSeed const& seed1, std::set& written) + { + mixSeedDaughters(seed0, seed1.omega, seed0.lambda, seed0.kaon, kMixedOmega, written); + mixSeedDaughters(seed1, seed0.omega, seed1.lambda, seed1.kaon, kMixedOmega, written); + mixSeedDaughters(seed0, seed0.omega, seed1.lambda, seed0.kaon, kMixedLambda, written); + mixSeedDaughters(seed1, seed1.omega, seed0.lambda, seed1.kaon, kMixedLambda, written); + mixSeedDaughters(seed0, seed0.omega, seed0.lambda, seed1.kaon, kMixedKaon, written); + mixSeedDaughters(seed1, seed1.omega, seed1.lambda, seed0.kaon, kMixedKaon, written); + } + + // Two daughters of a seed replaced by those of two other seeds: Omega, Lambda and kaon from three + // different collisions, in all the assignments. The seed providing the Omega is the reference. + void mixSeedTriple(MixingSeed const& seed0, MixingSeed const& seed1, MixingSeed const& seed2, std::set& written) + { + const std::array seeds{&seed0, &seed1, &seed2}; + std::array roles{0, 1, 2}; // Seeds providing the Omega, Lambda and kaon. + do { + const auto& omegaSeed = *seeds[roles[0]]; + mixSeedDaughters(omegaSeed, omegaSeed.omega, seeds[roles[1]]->lambda, seeds[roles[2]]->kaon, kMixedAll, written); + } while (std::next_permutation(roles.begin(), roles.end())); + } + + // Seeds can be mixed if they come from different collisions with close primary vertices, recorded + // with the same magnetic field. + bool canMix(MixingSeed const& seed0, MixingSeed const& seed1) const + { + return seed0.collisionId != seed1.collisionId && + std::abs(seed0.bz - seed1.bz) < 1.e-3f && + std::abs(seed0.primaryVertex[2] - seed1.primaryVertex[2]) <= mixingOpts.maxDeltaPVz.value; + } + + // Mixes a new seed with the most recent compatible seeds of its pool bin: pairs with each of them, + // triples with each pair of them coming from different collisions. + void mixWithPool(MixingSeed const& seed, std::deque const& pool, std::set& written) + { + const auto nPartners = static_cast(mixingOpts.nMixingCandidates.value); + std::vector partners; + for (auto partner = pool.rbegin(); partner != pool.rend() && partners.size() < nPartners; ++partner) { + histos.fill(HIST("Mixing/hMixedCollisions"), 0); + if (!canMix(seed, *partner)) { + continue; + } + histos.fill(HIST("Mixing/hMixedCollisions"), 1); + partners.push_back(&*partner); + mixSeedPair(seed, *partner, written); + } + if (!(mixingOpts.mixingTypes & (1 << (kMixedAll - 1)))) { + return; + } + for (std::size_t j = 0; j < partners.size(); ++j) { + for (std::size_t k = j + 1; k < partners.size(); ++k) { + histos.fill(HIST("Mixing/hMixedCollisions"), 2); + if (!canMix(*partners[j], *partners[k])) { + continue; + } + histos.fill(HIST("Mixing/hMixedCollisions"), 3); + mixSeedTriple(seed, *partners[j], *partners[k], written); + } + } + } + + // Same-event candidates of a collision become seeds, binned in decay radius, momentum azimuth and + // charge (mixingOpts.binsRadius, mixingOpts.binsPhi). The seeds are mixed with the pool first and + // added to it afterwards, so seeds of the same collision are never mixed with each other. + void mixCollisionSeeds(int64_t collisionId, CollisionCandidates const& candidates, std::set& written) + { + const int nPhiBins = nBins(mixingOpts.binsPhi.value); + const auto poolDepth = static_cast(mixingOpts.poolDepth.value); + std::vector> newSeeds; + helper.combine(candidates, candidates, candidates, candidates, + [&](DoubleOmegaCandidate const& cand, BuiltOmegaCandidate const& omega, BuiltLambdaCandidate const& lambda, BuiltKaonCandidate const& kaon) { + const float radius = std::hypot(cand.x, cand.y); + const int radiusBin = findBin(mixingOpts.binsRadius.value, radius); + const int phiBin = findBin(mixingOpts.binsPhi.value, cand.phi); + if (radiusBin < 0 || phiBin < 0) { + return; + } + histos.fill(HIST("Mixing/hSeedRadiusPhi"), radius, cand.phi); + const int key = (radiusBin * nPhiBins + phiBin) * 2 + DoubleOmegaBuilderHelper::chargeIndex(cand.sign); + newSeeds.emplace_back(key, makeSeed(collisionId, candidates.primaryVertex, omega, lambda, kaon)); + }); + for (const auto& [key, seed] : newSeeds) { + mixWithPool(seed, mSeedPool[key], written); + } + for (auto& [key, seed] : newSeeds) { + auto& pool = mSeedPool[key]; + pool.push_back(std::move(seed)); + ++mPoolSeeds; + if (pool.size() > poolDepth) { + pool.pop_front(); + --mPoolSeeds; + } + } + } + + void monitorMixing(std::chrono::steady_clock::time_point const& start) + { + const double processingTime = std::chrono::duration(std::chrono::steady_clock::now() - start).count(); + mProcessingTimeSum += processingTime; + // Lower bound: deque and hash-map bookkeeping are not included. + const double poolMemory = static_cast(mPoolSeeds * sizeof(MixingSeed)) / (1024. * 1024.); + histos.fill(HIST("Mixing/hProcessingTime"), processingTime); + histos.fill(HIST("Mixing/hPoolSeeds"), mPoolSeeds); + histos.fill(HIST("Mixing/hPoolMemory"), poolMemory); + histos.fill(HIST("Mixing/hProcessingTimeVsPoolSeeds"), mPoolSeeds, processingTime); + if (mixingOpts.monitoringInterval > 0 && mDataframeCounter % mixingOpts.monitoringInterval == 0) { + LOGP(info, "Candidate mixing, dataframe {}: {} seeds in {} pool bins ({:.2f} MB), {:.1f} ms in this dataframe, {:.1f} ms on average", + mDataframeCounter, mPoolSeeds, mSeedPool.size(), poolMemory, processingTime, mProcessingTimeSum / mDataframeCounter); + } + } + + // --------------------------------------------------------------------------------------------- + // Derived data produced by doubleOmegaBuilder, read on the fly or from saved (merged) derived data. + + // The derived tables store a track once per collision, so the same track can appear in the rows of + // different collisions (V0s and cascades can use tracks associated to another collision). Tracks are + // identified across collisions by their parameters, which are bitwise identical for the same track. + template + static int64_t trackFingerprint(Row const& row) + { + std::size_t hash = 0; + for (const float value : {row.x(), row.alpha(), row.y(), row.z(), row.snp(), row.tgl(), row.signed1Pt()}) { + hash ^= std::hash{}(value) + 0x9e3779b97f4a7c15ULL + (hash << 6) + (hash >> 2); + } + return static_cast(hash); + } + + // Derived collisions store the run number and the magnetic field: the propagator, the material LUT + // and the fitters are configured again only when the field changes. + void initCCDBForDerived(int runNumber, float bz) + { + if (mRunNumber != 0 && std::abs(bz - mBz) < kBzTolerance) { + return; + } + auto* grpmag = ccdb->getForRun("GLO/Config/GRPMagField", runNumber); + if (!grpmag) { + LOG(fatal) << "Could not retrieve GRPMagField for run " << runNumber; + } + o2::base::Propagator::initFieldFromGRP(grpmag); + mBz = o2::base::Propagator::Instance()->getNominalBz(); + helper.setBz(mBz); + if (static_cast(cfgMaterialCorrection.value) == o2::base::Propagator::MatCorrType::USEMatCorrLUT) { + auto* lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->getForRun("GLO/Param/MatLUT", runNumber)); + if (!lut) { + LOG(fatal) << "Could not retrieve material LUT for run " << runNumber; + } + o2::base::Propagator::Instance()->setMatLUT(lut); + } + mRunNumber = runNumber; + } + + // Reads the candidates of a derived collision back, applying again the selections that can be + // tightened with respect to the production ones. + template + CollisionCandidates readCollisionCandidates(C const& collision, aod::DOmegaTracks const& tracks, + aod::DOmegaOmegas const& omegas, aod::DOmegaLambdas const& lambdas) + { + CollisionCandidates candidates; + candidates.primaryVertex = {collision.posX(), collision.posY(), collision.posZ()}; + + for (const auto& row : omegas.sliceBy(derivedOmegasPerCollision, collision.globalIndex())) { + const auto posTrack = tracks.rawIteratorAt(row.posTrackId()); + const auto negTrack = tracks.rawIteratorAt(row.negTrackId()); + const auto bachelor = tracks.rawIteratorAt(row.bachelorId()); + const bool isMatter = row.sign() < 0; + const auto& proton = isMatter ? posTrack : negTrack; + const auto& pion = isMatter ? negTrack : posTrack; + const uint32_t lambdaDetectorMap = static_cast(proton.detectorMap()) | (static_cast(pion.detectorMap()) << 3); + const BuiltOmegaCandidate omega{row.px(), row.py(), row.pz(), + row.decayX(), row.decayY(), row.decayZ(), + row.massOmega(), row.massXi(), + row.sign(), + trackFingerprint(posTrack), trackFingerprint(negTrack), trackFingerprint(bachelor), + getTrackParCov(row), + static_cast(bachelor.detectorMap()) | (lambdaDetectorMap << 3), + bachelor.dcaXYToPV(), proton.dcaXYToPV(), pion.dcaXYToPV(), + row.dcaDaughters()}; + if (helper.isSelectedOmega(omega)) { + candidates.omegas.push_back(omega); + } + } + + for (const auto& row : lambdas.sliceBy(derivedLambdasPerCollision, collision.globalIndex())) { + const auto posTrack = tracks.rawIteratorAt(row.posTrackId()); + const auto negTrack = tracks.rawIteratorAt(row.negTrackId()); + const bool isMatter = row.sign() < 0; + const auto& proton = isMatter ? posTrack : negTrack; + const auto& pion = isMatter ? negTrack : posTrack; + BuiltLambdaCandidate lambda; + lambda.candidate = {.px = row.px(), .py = row.py(), .pz = row.pz(), .mass = row.massLambda()}; + lambda.parentTrack = getTrackParCov(row); + lambda.parentTrack.setAbsCharge(0); // Neutral, as created by the V0 fit. + lambda.decayVertex = {row.decayX(), row.decayY(), row.decayZ()}; + lambda.posTrackId = trackFingerprint(posTrack); + lambda.negTrackId = trackFingerprint(negTrack); + lambda.daughterDetectorMap = static_cast(proton.detectorMap()) | (static_cast(pion.detectorMap()) << 3); + lambda.dcaXYProtonToPV = proton.dcaXYToPV(); + lambda.dcaXYPionToPV = pion.dcaXYToPV(); + lambda.dcaDaughters = row.dcaDaughters(); + if (helper.isSelectedLambda(lambda)) { + candidates.lambdas[DoubleOmegaBuilderHelper::chargeIndex(row.sign())].push_back(lambda); + } + } + + for (const auto& row : tracks.sliceBy(derivedTracksPerCollision, collision.globalIndex())) { + if (!row.isKaon()) { + continue; + } + const BuiltKaonCandidate kaon{getTrackParCov(row), + trackFingerprint(row), + static_cast(row.sign()), + row.tpcNSigmaKa(), + row.tofNSigmaKa(), + row.detectorMap()}; + if (helper.isSelectedKaon(kaon)) { + candidates.kaons[DoubleOmegaBuilderHelper::chargeIndex(kaon.sign)].push_back(kaon); + } + } + fillCandidateQA(candidates); + return candidates; + } + + void processDerivedData(aod::DOmegaColls const& collisions, + aod::DOmegaTracks const& tracks, + aod::DOmegaOmegas const& omegas, + aod::DOmegaLambdas const& lambdas) + { + for (const auto& collision : collisions) { + initCCDBForDerived(collision.runNumber(), collision.bz()); + histos.fill(HIST("QA/zVtx"), collision.posZ()); + const auto candidates = readCollisionCandidates(collision, tracks, omegas, lambdas); + helper.combine(candidates, candidates, candidates, candidates, + [&](DoubleOmegaCandidate const& cand, auto const&...) { writeDataCandidate(cand); }); + } + } + PROCESS_SWITCH(doubleOmegaTreeCreator, processDerivedData, "Same-event Omega + Lambda + kaon from the derived tables of doubleOmegaBuilder", false); + + // Mixed-event background from the derived tables. Saved derived data can be merged into large + // dataframes, which provides many more mixing partners than the input AO2D dataframes. Only + // collisions recorded with the same magnetic field are mixed. + void processDerivedMixing(aod::DOmegaColls const& collisions, + aod::DOmegaTracks const& tracks, + aod::DOmegaOmegas const& omegas, + aod::DOmegaLambdas const& lambdas) + { + std::unordered_map candidatesPerCollision; + for (const auto& collision : collisions) { + candidatesPerCollision.emplace(collision.globalIndex(), readCollisionCandidates(collision, tracks, omegas, lambdas)); + } + + DerivedMixingBinning binning{{mixingOpts.binsVtxZ, mixingOpts.binsMultiplicity}, true}; + for (const auto& [collision0, collision1] : selfPairCombinations(binning, mixingOpts.nMixingCollisions, -1, collisions)) { + histos.fill(HIST("Mixing/hMixedCollisions"), 0); + if (std::abs(collision0.posZ() - collision1.posZ()) > mixingOpts.maxDeltaPVz || + std::abs(collision0.bz() - collision1.bz()) > kBzTolerance) { + continue; + } + histos.fill(HIST("Mixing/hMixedCollisions"), 1); + initCCDBForDerived(collision0.runNumber(), collision0.bz()); + mixPair(candidatesPerCollision.at(collision0.globalIndex()), candidatesPerCollision.at(collision1.globalIndex())); + } + + if (!(mixingOpts.mixingTypes & (1 << (kMixedAll - 1)))) { + return; + } + for (const auto& [collision0, collision1, collision2] : selfTripleCombinations(binning, mixingOpts.nMixingCollisions, -1, collisions)) { + histos.fill(HIST("Mixing/hMixedCollisions"), 2); + const auto [minPVz, maxPVz] = std::minmax({collision0.posZ(), collision1.posZ(), collision2.posZ()}); + if (maxPVz - minPVz > mixingOpts.maxDeltaPVz || + std::abs(collision0.bz() - collision1.bz()) > kBzTolerance || + std::abs(collision0.bz() - collision2.bz()) > kBzTolerance) { + continue; + } + histos.fill(HIST("Mixing/hMixedCollisions"), 3); + initCCDBForDerived(collision0.runNumber(), collision0.bz()); + mixTriple(candidatesPerCollision.at(collision0.globalIndex()), candidatesPerCollision.at(collision1.globalIndex()), + candidatesPerCollision.at(collision2.globalIndex())); + } + } + PROCESS_SWITCH(doubleOmegaTreeCreator, processDerivedMixing, "Mixed-event Omega + Lambda + kaon background from the derived tables of doubleOmegaBuilder", false); + + // Candidate-level mixed-event background from the derived tables of doubleOmegaBuilder, as in + // decay3bodybuilder. The same-event candidates (seeds) are binned in decay radius, momentum azimuth + // and charge, so seeds in the same bin have their decay vertex in the same region of the detector and + // point in the same direction. Mixed candidates are built by swapping one (or two) daughters between + // seeds of different collisions, which keeps the vertex topology without moving any track. Seeds are + // pooled across dataframes. + void processDerivedMixingCandidates(aod::DOmegaColls const& collisions, + aod::DOmegaTracks const& tracks, + aod::DOmegaOmegas const& omegas, + aod::DOmegaLambdas const& lambdas) + { + const auto start = std::chrono::steady_clock::now(); + ++mDataframeCounter; + std::set written; // Every combination has a daughter from this dataframe. + + for (const auto& collision : collisions) { + // Seeds are mixed only with seeds recorded with the same field (canMix), so the field of the + // current collision is the right one for all its mixed candidates. + initCCDBForDerived(collision.runNumber(), collision.bz()); + histos.fill(HIST("QA/zVtx"), collision.posZ()); + const auto candidates = readCollisionCandidates(collision, tracks, omegas, lambdas); + mixCollisionSeeds(collision.globalIndex(), candidates, written); + } + monitorMixing(start); + } + PROCESS_SWITCH(doubleOmegaTreeCreator, processDerivedMixingCandidates, "Candidate-level mixed-event background from the derived tables of doubleOmegaBuilder, seeds binned in decay radius and azimuth", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGLF/TableProducer/Strangeness/hStrangeCorrelationFilter.cxx b/PWGLF/TableProducer/Strangeness/hStrangeCorrelationFilter.cxx index 927c13ae023..0f32bc668d0 100644 --- a/PWGLF/TableProducer/Strangeness/hStrangeCorrelationFilter.cxx +++ b/PWGLF/TableProducer/Strangeness/hStrangeCorrelationFilter.cxx @@ -138,6 +138,10 @@ struct HStrangeCorrelationFilter { // formula: |DCAxy| < 0.004f + (0.013f / pt) Configurable dcaXYconstant{"dcaXYconstant", 0.004, "[0] in |DCAxy| < [0]+[1]/pT"}; Configurable dcaXYpTdep{"dcaXYpTdep", 0.013, "[1] in |DCAxy| < [0]+[1]/pT"}; + + // perform P-Lambda correlation analysis + Configurable performPLambdacorrelation{"performPLambdacorrelation", 3, "do p-Lambda correlation study"}; + Configurable triggerProtonNSigmaTPCFOF{"triggerProtonNSigmaTPCFOF", 3, "minimal n sigma in TOF and TPC for Proton ID"}; } trackSelections; struct : ConfigurableGroup { // V0 selections @@ -213,8 +217,8 @@ struct HStrangeCorrelationFilter { // using V0LinkedTagged = soa::Join; // using CascadesLinkedTagged = soa::Join; - using FullTracks = soa::Join; - using FullTracksMC = soa::Join; + using FullTracks = soa::Join; + using FullTracksMC = soa::Join; using DauTracks = soa::Join; using DauTracksMC = soa::Join; // using IDTracks= soa::Join; // prepared for Bayesian PID @@ -447,7 +451,8 @@ struct HStrangeCorrelationFilter { if (track.pt() > generalSelections.triggerPtCutMax || track.pt() < generalSelections.triggerPtCutMin) { return false; } - if (track.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + // hasTPC() is needed: since O2-7038 (Oct 2026) tracks without TPC report 256 crossed rows + if (!track.hasTPC() || track.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { return false; // crossed rows } if (!track.hasITS() && trackSelections.triggerRequireITS) { @@ -459,6 +464,13 @@ struct HStrangeCorrelationFilter { if (!(TESTBIT(track.itsClusterMap(), 0)) && trackSelections.triggerRequireL0) { return false; // skip, doesn't have cluster in ITS L0 } + bool isProton = std::sqrt(track.tofNSigmaPr() * track.tofNSigmaPr() + track.tpcNSigmaPr() * track.tpcNSigmaPr()) <= trackSelections.triggerProtonNSigmaTPCFOF; + if (trackSelections.performPLambdacorrelation <= 1 && !isProton) { + return false; + } + if (trackSelections.performPLambdacorrelation == 2 && isProton) { + return false; + } return true; } template @@ -470,7 +482,7 @@ struct HStrangeCorrelationFilter { if (assoc.pt() > generalSelections.assocPtCutMax || assoc.pt() < generalSelections.assocPtCutMin) { return false; } - if (assoc.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + if (!assoc.hasTPC() || assoc.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { return false; // crossed rows } if (!assoc.hasITS() && trackSelections.assocRequireITS) { @@ -837,10 +849,10 @@ struct HStrangeCorrelationFilter { auto posdau = v0.posTrack_as(); auto negdau = v0.negTrack_as(); - if (negdau.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + if (!negdau.hasTPC() || negdau.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { continue; } - if (posdau.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + if (!posdau.hasTPC() || posdau.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { continue; } if (trackSelections.requireClusterInITS && (posdau.itsNCls() < trackSelections.minITSClustersForDaughterTracks || negdau.itsNCls() < trackSelections.minITSClustersForDaughterTracks)) { @@ -976,10 +988,10 @@ struct HStrangeCorrelationFilter { auto posdau = v0.posTrack_as(); auto negdau = v0.negTrack_as(); - if (negdau.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + if (!negdau.hasTPC() || negdau.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { continue; } - if (posdau.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + if (!posdau.hasTPC() || posdau.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { continue; } if (trackSelections.requireClusterInITS && (posdau.itsNCls() < trackSelections.minITSClustersForDaughterTracks || negdau.itsNCls() < trackSelections.minITSClustersForDaughterTracks)) { @@ -1134,13 +1146,13 @@ struct HStrangeCorrelationFilter { auto negTrackCast = casc.negTrack_as(); // minimum TPC crossed rows - if (bachTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + if (!bachTrackCast.hasTPC() || bachTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { continue; } - if (posTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + if (!posTrackCast.hasTPC() || posTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { continue; } - if (negTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + if (!negTrackCast.hasTPC() || negTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { continue; } if (rejectAmbiguousAssoc && (isAmbiguousTrack(bachTrackCast) || isAmbiguousTrack(posTrackCast) || isAmbiguousTrack(negTrackCast))) { @@ -1307,13 +1319,13 @@ struct HStrangeCorrelationFilter { auto negTrackCast = casc.negTrack_as(); // minimum TPC crossed rows - if (bachTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + if (!bachTrackCast.hasTPC() || bachTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { continue; } - if (posTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + if (!posTrackCast.hasTPC() || posTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { continue; } - if (negTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + if (!negTrackCast.hasTPC() || negTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { continue; } if (rejectAmbiguousAssoc && (isAmbiguousTrack(bachTrackCast) || isAmbiguousTrack(posTrackCast) || isAmbiguousTrack(negTrackCast))) { diff --git a/PWGLF/TableProducer/Strangeness/lambdakzerofinder.cxx b/PWGLF/TableProducer/Strangeness/lambdakzerofinder.cxx index 6c20a736eda..e1329901149 100644 --- a/PWGLF/TableProducer/Strangeness/lambdakzerofinder.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdakzerofinder.cxx @@ -61,6 +61,7 @@ #include #include #include +#include using namespace o2; using namespace o2::framework; diff --git a/PWGLF/TableProducer/Strangeness/lambdakzeromlselection.cxx b/PWGLF/TableProducer/Strangeness/lambdakzeromlselection.cxx index b27cb24c68c..3fd13a7aa95 100644 --- a/PWGLF/TableProducer/Strangeness/lambdakzeromlselection.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdakzeromlselection.cxx @@ -210,19 +210,19 @@ struct lambdakzeromlselection { // calculate classifier output if (PredictLambda) { - float* LambdaProbability = lambda_bdt.evalModel(inputFeatures); + const std::vector LambdaProbability = lambda_bdt.evalModel(inputFeatures); lambdaMLSelections(LambdaProbability[1]); } if (PredictGamma) { - float* GammaProbability = gamma_bdt.evalModel(inputFeatures); + const std::vector GammaProbability = gamma_bdt.evalModel(inputFeatures); gammaMLSelections(GammaProbability[1]); } if (PredictAntiLambda) { - float* AntiLambdaProbability = antilambda_bdt.evalModel(inputFeatures); + const std::vector AntiLambdaProbability = antilambda_bdt.evalModel(inputFeatures); antiLambdaMLSelections(AntiLambdaProbability[1]); } if (PredictKZeroShort) { - float* KZeroShortProbability = kzeroshort_bdt.evalModel(inputFeatures); + const std::vector KZeroShortProbability = kzeroshort_bdt.evalModel(inputFeatures); kzeroShortMLSelections(KZeroShortProbability[1]); } } diff --git a/PWGLF/TableProducer/Strangeness/lambdaspincorrelation.cxx b/PWGLF/TableProducer/Strangeness/lambdaspincorrelation.cxx index fcb3bf21aff..615c4be269d 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaspincorrelation.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaspincorrelation.cxx @@ -299,6 +299,13 @@ struct lambdaspincorrelation { std::vector negativeIndex = {}; std::vector dcaBetweenDaughter = {}; std::vector dcaV0ToPV = {}; + std::vector tpcPosNClsCrossedRows = {}; + std::vector tpcNegNClsCrossedRows = {}; + std::vector ctau = {}; + std::vector tpcNsigmaPos = {}; + std::vector tpcNsigmaNeg = {}; + std::vector mK0Short = {}; + int numbV0 = 0; // LOGF(info, "event collisions: (%d)", collision.index()); auto centrality = collision.centFT0C(); @@ -350,6 +357,9 @@ struct lambdaspincorrelation { if (aLambdaTag) { histos.fill(HIST("hV0Info"), 3.5); } + + float ctauLambda = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * (o2::constants::physics::MassLambda); + // LOGF(info, "v0 index2: (%d)", v0.index()); auto postrack1 = v0.template posTrack_as(); auto negtrack1 = v0.template negTrack_as(); @@ -357,6 +367,16 @@ struct lambdaspincorrelation { negativeIndex.push_back(negtrack1.globalIndex()); v0Cospa.push_back(v0.v0cosPA()); dcaV0ToPV.push_back(std::abs(v0.dcav0topv())); + + tpcPosNClsCrossedRows.push_back(postrack1.tpcNClsCrossedRows()); + tpcNegNClsCrossedRows.push_back(negtrack1.tpcNClsCrossedRows()); + ctau.push_back(ctauLambda); + + tpcNsigmaPos.push_back(std::abs(postrack1.tpcNSigmaPr())); + tpcNsigmaNeg.push_back(std::abs(negtrack1.tpcNSigmaPi())); + + mK0Short.push_back(v0.mK0Short()); + v0Radius.push_back(v0.v0radius()); dcaPositive.push_back(std::abs(v0.dcapostopv())); dcaNegative.push_back(std::abs(v0.dcanegtopv())); @@ -393,7 +413,7 @@ struct lambdaspincorrelation { lambdaDummy = lambdaMother.at(i5); protonDummy = protonDaughter.at(i5); pionDummy = pionDaughter.at(i5); - lambdaPair(indexEvent, v0Status.at(i5), doubleStatus.at(i5), v0Cospa.at(i5), v0Radius.at(i5), dcaPositive.at(i5), dcaNegative.at(i5), dcaBetweenDaughter.at(i5), lambdaDummy.Pt(), lambdaDummy.Eta(), lambdaDummy.Phi(), lambdaDummy.M(), protonDummy.Pt(), protonDummy.Eta(), protonDummy.Phi(), positiveIndex.at(i5), negativeIndex.at(i5), dcaV0ToPV.at(i5)); + lambdaPair(indexEvent, v0Status.at(i5), doubleStatus.at(i5), v0Cospa.at(i5), v0Radius.at(i5), dcaPositive.at(i5), dcaNegative.at(i5), dcaBetweenDaughter.at(i5), lambdaDummy.Pt(), lambdaDummy.Eta(), lambdaDummy.Phi(), lambdaDummy.M(), protonDummy.Pt(), protonDummy.Eta(), protonDummy.Phi(), positiveIndex.at(i5), negativeIndex.at(i5), dcaV0ToPV.at(i5), tpcPosNClsCrossedRows.at(i5), tpcNegNClsCrossedRows.at(i5), ctau.at(i5), tpcNsigmaPos.at(i5), tpcNsigmaNeg.at(i5), mK0Short.at(i5)); } } } diff --git a/PWGLF/TableProducer/Strangeness/sigma0builder.cxx b/PWGLF/TableProducer/Strangeness/sigma0builder.cxx index 2808e65d893..cb51f93b6d3 100644 --- a/PWGLF/TableProducer/Strangeness/sigma0builder.cxx +++ b/PWGLF/TableProducer/Strangeness/sigma0builder.cxx @@ -43,6 +43,7 @@ #include #include #include +#include #include #include // IWYU pragma: keep (do not replace with Math/Vector3Dfwd.h) @@ -1511,7 +1512,7 @@ struct sigma0builder { if (MCMother_pi0.globalIndex() == MCMother_lambda.globalIndex()) { // check that lambda and pi0 have the same mother. MCinfo.xi0PDGCode = MCMother_pi0.pdgCode(); MCinfo.xi0MCProcess = MCMother_pi0.getProcess(); - MCinfo.pi0IsPhysicalPrimary = MCMother_pi0.isPhysicalPrimary(); + MCinfo.xi0IsPhysicalPrimary = MCMother_pi0.isPhysicalPrimary(); MCinfo.xi0X = MCMother_pi0.vx(); MCinfo.xi0Y = MCMother_pi0.vy(); MCinfo.xi0Z = MCMother_pi0.vz(); @@ -2703,7 +2704,7 @@ struct sigma0builder { //_______________________________________________ // Build Xi0 candidate for analysis template - bool buildXi0(TV0Object const& gamma1, TV0Object const& gamma2, TV0Object const& lambda, TCollision const& collision, TMCParticles const& mcparticles) + bool buildXi0(TV0Object const& gamma1, TV0Object const& gamma2, TV0Object const& lambda, V0PairTopoInfo& pi0Info, TCollision const& collision, TMCParticles const& mcparticles) { Xi0Info cascade; //_______________________________________________ @@ -2764,6 +2765,8 @@ struct sigma0builder { return false; } + cascade.pi0DaughterDCA = pi0Info.daughterDCA; + lGamma1Track.getPxPyPzGlo(cascade.gamma1Momentum); lGamma2Track.getPxPyPzGlo(cascade.gamma2Momentum); lLambdaTrack.getPxPyPzGlo(cascade.lambdaMomentum); @@ -2776,6 +2779,10 @@ struct sigma0builder { return false; } + cascade.pi0Position[0] = pi0Info.position[0]; + cascade.pi0Position[1] = pi0Info.position[1]; + cascade.pi0Position[2] = pi0Info.position[2]; + double cosPA = RecoDecay::cpa( std::array{collision.posX(), collision.posY(), collision.posZ()}, std::array{cascade.cascadePosition[0], cascade.cascadePosition[1], cascade.cascadePosition[2]}, @@ -3418,7 +3425,7 @@ struct sigma0builder { auto gamma1 = fullV0s.rawIteratorAt(pi0Candidates[j].v01Index); auto gamma2 = fullV0s.rawIteratorAt(pi0Candidates[j].v02Index); // Building pi0 candidate & filling tables - if (!buildXi0(gamma1, gamma2, lambda, coll, mcparticles)) + if (!buildXi0(gamma1, gamma2, lambda, pi0Candidates[j], coll, mcparticles)) continue; } } diff --git a/PWGLF/TableProducer/Strangeness/sigmaHadCorr.cxx b/PWGLF/TableProducer/Strangeness/sigmaHadCorr.cxx index 6f2a8a2336c..782f8c68bfc 100644 --- a/PWGLF/TableProducer/Strangeness/sigmaHadCorr.cxx +++ b/PWGLF/TableProducer/Strangeness/sigmaHadCorr.cxx @@ -663,7 +663,7 @@ struct SigmaHadCorr { { if (useMultNTracksPV.value) { for (auto const& [collision1, collision2] : - selfCombinations(BinningTypeMultNTracksPV{{cfgVtxBins, cfgMultBins}, true}, nEvtMixingBkg, -1, collisions, collisions)) { + selfCombinations(BinningTypeMultNTracksPV{{cfgVtxBins, cfgMultBins}}, nEvtMixingBkg, -1, collisions, collisions)) { if (collision1.index() == collision2.index()) continue; if (std::abs(collision1.posZ()) > cutZVertex || !collision1.sel8()) @@ -685,7 +685,7 @@ struct SigmaHadCorr { } } else { for (auto const& [collision1, collision2] : - selfCombinations(BinningTypeNumContrib{{cfgVtxBins, cfgMultBins}, true}, nEvtMixingBkg, -1, collisions, collisions)) { + selfCombinations(BinningTypeNumContrib{{cfgVtxBins, cfgMultBins}}, nEvtMixingBkg, -1, collisions, collisions)) { if (collision1.index() == collision2.index()) continue; if (std::abs(collision1.posZ()) > cutZVertex || !collision1.sel8()) @@ -835,7 +835,7 @@ struct SigmaHadCorr { { if (useMultNTracksPV.value) { for (auto const& [collision1, collision2] : - selfCombinations(BinningTypeMultNTracksPV{{cfgVtxBins, cfgMultBins}, true}, nEvtMixingBkg, -1, collisions, collisions)) { + selfCombinations(BinningTypeMultNTracksPV{{cfgVtxBins, cfgMultBins}}, nEvtMixingBkg, -1, collisions, collisions)) { if (collision1.index() == collision2.index()) continue; if (std::abs(collision1.posZ()) > cutZVertex || !collision1.sel8()) @@ -883,7 +883,7 @@ struct SigmaHadCorr { } } else { for (auto const& [collision1, collision2] : - selfCombinations(BinningTypeNumContrib{{cfgVtxBins, cfgMultBins}, true}, nEvtMixingBkg, -1, collisions, collisions)) { + selfCombinations(BinningTypeNumContrib{{cfgVtxBins, cfgMultBins}}, nEvtMixingBkg, -1, collisions, collisions)) { if (collision1.index() == collision2.index()) continue; if (std::abs(collision1.posZ()) > cutZVertex || !collision1.sel8()) diff --git a/PWGLF/TableProducer/Strangeness/sigmaplusbuilder.cxx b/PWGLF/TableProducer/Strangeness/sigmaplusbuilder.cxx index 7969042b519..6d8ba51c21f 100644 --- a/PWGLF/TableProducer/Strangeness/sigmaplusbuilder.cxx +++ b/PWGLF/TableProducer/Strangeness/sigmaplusbuilder.cxx @@ -13,43 +13,61 @@ /// \brief Task for Sigma+ -> p + pi0 reconstruction, pi0 reconstructed via one converted photon (PCM) /// \author Henrik Fribert (TUM) +#include "PWGEM/Dilepton/Utils/PairUtilities.h" +#include "PWGEM/PhotonMeson/Utils/PCMUtilities.h" #include "PWGLF/DataModel/LFKinkDecayTables.h" #include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGLF/Utils/svPoolCreator.h" #include "Common/Core/RecoDecay.h" +#include "Common/Core/TPCVDriftManager.h" #include "Common/Core/trackUtilities.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" +#include "Tools/ML/MlResponse.h" #include +#include +#include #include #include #include #include +#include #include #include #include #include +#include #include +#include #include #include #include #include #include +#include #include #include -#include +#include +#include +#include #include #include -#include #include #include #include +#include +#include +#include +#include #include +#include +#include #include using namespace o2; @@ -67,27 +85,34 @@ struct Sigmaplusbuilder { Configurable cutZVertex{"cutZVertex", 10.0f, "Accepted z-vertex range (cm)"}; // photon (PCM) selection - Configurable photonMaxMass{"photonMaxMass", 0.20, "Max photon mass (GeV/c^2)"}; - Configurable photonMinRapidity{"photonMinRapidity", -0.8, "Min photon rapidity"}; - Configurable photonMaxRapidity{"photonMaxRapidity", 0.8, "Max photon rapidity"}; - Configurable cutRapMotherMC{"cutRapMotherMC", 1.0f, "Rapidity cut for generated mother Sigma+ in MC"}; - Configurable cutPtGenMC{"cutPtGenMC", 0.5f, "Minimum pT for generated Sigma+ in MC"}; - Configurable photonDauEtaMin{"photonDauEtaMin", -0.8, "Min eta of photon daughter tracks"}; - Configurable photonDauEtaMax{"photonDauEtaMax", 0.8, "Max eta of photon daughter tracks"}; + Configurable photonMaxMass{"photonMaxMass", 0.05, "Max photon mass (GeV/c^2)"}; + Configurable photonMinRapidity{"photonMinRapidity", -1.0, "Min photon rapidity"}; + Configurable photonMaxRapidity{"photonMaxRapidity", 1.0, "Max photon rapidity"}; + Configurable photonDauEtaMin{"photonDauEtaMin", -1.0, "Min eta of photon daughter tracks"}; + Configurable photonDauEtaMax{"photonDauEtaMax", 1.0, "Max eta of photon daughter tracks"}; Configurable photonMinRadius{"photonMinRadius", 3.0, "Min photon conversion radius (cm)"}; Configurable photonMaxRadius{"photonMaxRadius", 115., "Max photon conversion radius (cm)"}; - Configurable photonMinV0cospa{"photonMinV0cospa", 0.80, "Min V0 CosPA"}; - Configurable photonMaxDCAV0Dau{"photonMaxDCAV0Dau", 3.5, "Max DCA between photon daughters (cm)"}; + Configurable photonMinV0cospa{"photonMinV0cospa", 0.9, "Min photon cosine of pointing angle to the PV"}; + Configurable photonMaxDCAV0Dau{"photonMaxDCAV0Dau", 1.5, "Max DCA between photon daughters (cm)"}; Configurable photonMaxOpeningAngle{"photonMaxOpeningAngle", 0.4, "Max opening angle between the photon's e+/e- daughter momenta (rad)"}; Configurable photonMaxDeltaTheta{"photonMaxDeltaTheta", 0.15, "Max |theta_pos - theta_neg| of the photon's daughter tracks (rad)"}; - Configurable photonMaxQt{"photonMaxQt", 0.15, "Max Armenteros qT for photons (GeV/c)"}; - Configurable photonMaxAlpha{"photonMaxAlpha", 1.0, "Max |Armenteros alpha| for photons"}; - Configurable photonDauMinTPCNSigmaEl{"photonDauMinTPCNSigmaEl", -5., "Min TPC nSigma_el of the photon daughters"}; - Configurable photonDauMaxTPCNSigmaEl{"photonDauMaxTPCNSigmaEl", 5., "Max TPC nSigma_el of the photon daughters"}; - Configurable photonDauMinTpcNCls{"photonDauMinTpcNCls", 30, "Min number of found TPC clusters for the photon (V0) daughter tracks"}; + Configurable photonMaxQt{"photonMaxQt", 0.05, "Max Armenteros qT for photons (GeV/c)"}; + Configurable photonMaxAlpha{"photonMaxAlpha", 0.95, "Max |Armenteros alpha| for photons"}; + Configurable photonDauMinTPCNSigmaEl{"photonDauMinTPCNSigmaEl", -3., "Min TPC nSigma_el of the photon daughters"}; + Configurable photonDauMaxTPCNSigmaEl{"photonDauMaxTPCNSigmaEl", 3., "Max TPC nSigma_el of the photon daughters"}; + Configurable photonDauMaxPt{"photonDauMaxPt", 0.5, "Max pT of the photon daughters (GeV/c)"}; + Configurable photonDauMinTpcNCls{"photonDauMinTpcNCls", 30, "Min number of found TPC clusters for the photon daughter tracks"}; + + // photon daughters paired by this task instead of taken from V0Datas (V0Datas assumes photons from primary vertex) + Configurable photonSkipAmbiTracks{"photonSkipAmbiTracks", false, "Skip ambiguous tracks when pairing photon daughters"}; + Configurable photonPoolTimeMarginNS{"photonPoolTimeMarginNS", 800., "Time margin (ns) added to a daughter track's time range when matching it to collisions"}; + Configurable photonMaxDXYIni{"photonMaxDXYIni", 4., "Max xy distance (cm) between the two daughter tracks at the start of the photon vertex fit"}; + Configurable photonMaxCircleTouchDist{"photonMaxCircleTouchDist", 4., "Max |circle centre distance - (R1 + R2)| (cm) of the two daughter tracks, applied before the fit"}; + Configurable photonPrefitWithoutMatCorr{"photonPrefitWithoutMatCorr", true, "Before the photon vertex fit with material correction, fit without it and reject the pair if that DCA is above photonPrefitMaxDca"}; + Configurable photonPrefitMaxDca{"photonPrefitMaxDca", 3., "Max daughter DCA (cm) of the fit without material correction (photonPrefitWithoutMatCorr)"}; // proton selection - Configurable protonMinPt{"protonMinPt", 0.3, "Minimum proton pT (GeV/c)"}; + Configurable protonMinPt{"protonMinPt", 0.4, "Minimum proton pT (GeV/c)"}; Configurable protonMaxEta{"protonMaxEta", 0.9, "Maximum |eta| for proton track"}; Configurable protonMinTpcNCls{"protonMinTpcNCls", 80, "Min number of found TPC clusters for the proton track"}; Configurable protonMaxTPCNSigma{"protonMaxTPCNSigma", 4, "Max |TPC nSigma_pr| for proton"}; @@ -96,38 +121,50 @@ struct Sigmaplusbuilder { Configurable protonRequireTofHit{"protonRequireTofHit", false, "Above protonPtMinRequireTOF, reject a proton with no TOF hit at all"}; Configurable protonMinDcaToPV{"protonMinDcaToPV", 0.005, "Min DCAxy of the proton track to the PV (cm)"}; Configurable protonMaxDcaToPV{"protonMaxDcaToPV", 5.0, "Max DCAxy of the proton track to the PV (cm)"}; + Configurable protonMaxDcaZToPV{"protonMaxDcaZToPV", 0.3, "Max |DCAz| of the proton track to the PV (cm)"}; // proton-photon candidate selection - Configurable candMaxDcaProtonGamma{"candMaxDcaProtonGamma", 0.5, "Max DCA between proton and photon at the fitted vertex (cm)"}; - Configurable candMaxDcaToPV{"candMaxDcaToPV", 0.1, "Max DCA of the candidate's total (reconstructed) momentum line to the PV (cm)"}; + Configurable candVertexProtonWeight{"candVertexProtonWeight", 1.0, "Sigma+ vertex between the proton's (1) and the photon's (0) point of closest approach"}; + Configurable candMaxDcaProtonGamma{"candMaxDcaProtonGamma", 1.5, "Max DCA between proton and photon at the fitted vertex (cm)"}; + Configurable candMaxDcaToPV{"candMaxDcaToPV", 20., "Max DCA of the candidate's total (reconstructed) momentum line to the PV (cm)"}; Configurable candRejectNegRootCenter{"candRejectNegRootCenter", true, "Reject candidates with rootCenter<0"}; Configurable candMaxRootCenter{"candMaxRootCenter", 15, "Max rootCenter=-coefB/(2*coefA) (GeV/c)"}; Configurable candMinAntiSigmaPointingAngle{"candMinAntiSigmaPointingAngle", 0.0, "Min AntiSigmaPointingAngle (rad)"}; - Configurable candMaxAntiSigmaPointingAngle{"candMaxAntiSigmaPointingAngle", 0.3, "Max AntiSigmaPointingAngle (rad)"}; + Configurable candMaxAntiSigmaPointingAngle{"candMaxAntiSigmaPointingAngle", 10., "Max AntiSigmaPointingAngle (rad)"}; Configurable candMaxSigmaMass{"candMaxSigmaMass", 1.35, "Max reconstructed Sigma+ candidate mass (GeV/c^2)"}; Configurable candMaxRapidity{"candMaxRapidity", 0.9, "Max |rapidity| of the reconstructed Sigma+ candidate"}; Configurable candMinRadius{"candMinRadius", 1.0, "Min candidate decay radius (cm)"}; Configurable candMaxRadius{"candMaxRadius", 100., "Max candidate decay radius (cm)"}; Configurable candMinFlightDistance{"candMinFlightDistance", 0.0, "Min 3D distance from PV to candidate decay vertex (cm)"}; Configurable candMaxFlightDistance{"candMaxFlightDistance", 250.0, "Max 3D distance from PV to candidate decay vertex (cm)"}; - Configurable candMaxPhotonOpeningAngle{"candMaxPhotonOpeningAngle", 3.15, "Max photon opening angle (rad), recomputed from the daughters' raw track momenta"}; - Configurable candMaxPhotonPointingAngle{"candMaxPhotonPointingAngle", 0.5, "Max angle between the photon's fitted momentum and the decay-vertex-to-conversion-point line (rad)"}; + Configurable candMaxPhotonOpeningAngle{"candMaxPhotonOpeningAngle", 3.15, "Max photon opening angle (rad), recomputed from the daughters' track momenta"}; + Configurable candMaxPhotonPointingAngle{"candMaxPhotonPointingAngle", 0.5, "Max angle between the photon momentum and the decay-vertex-to-conversion-point line (rad)"}; Configurable candMaxPhotonDcaToPV{"candMaxPhotonDcaToPV", 250.0, "Max DCA of the photon's flight line to the PV (cm)"}; + Configurable candMaxPhotonDcaZToPV{"candMaxPhotonDcaZToPV", 2.0, "Max |DCAz| of the photon's flight line to the PV at its transverse DCA (cm), applied to the photon before the pairing with the proton"}; + Configurable candDeduplicatePhotons{"candDeduplicatePhotons", true, "Per timeframe, write only the candidate with the smallest DCA to PV among candidates sharing the same photon"}; - // missing-photon discriminant retry - // resolution-shaped function and tuned parameters from Run 2 used - Configurable discrRetryMaxIter{"discrRetryMaxIter", 10, "Max attempts to recover a negative discriminant by perturbing the flight direction"}; - Configurable discrRetryThetaRange{"discrRetryThetaRange", 0.02, "Max |delta-theta| perturbation of the flight direction per retry (rad)"}; - Configurable discrRetryThetaPar0{"discrRetryThetaPar0", 0.000133299, "par0 of the delta-theta resolution function"}; - Configurable discrRetryThetaPar1{"discrRetryThetaPar1", 0.0016761, "par1 of the delta-theta resolution function"}; - Configurable discrRetryPhiRange{"discrRetryPhiRange", 0.02, "Max |delta-phi| perturbation of the flight direction per retry (rad)"}; - Configurable discrRetryPhiPar0{"discrRetryPhiPar0", 0.000129845, "par0 of the delta-phi resolution function"}; - Configurable discrRetryPhiPar1{"discrRetryPhiPar1", 0.00199688, "par1 of the delta-phi resolution function"}; + // tilt search: if the measured flight direction gives no real solution for the missing photon, + // tilted flight directions are tried starting with the smallest tilt + Configurable candMaxTilt{"candMaxTilt", 0.05, "Max tilt of the flight direction searched for a real root (rad)"}; + Configurable candTiltStep{"candTiltStep", 0.005, "Tilt step between the rings of tried flight directions (rad)"}; + Configurable candTiltNAzimuth{"candTiltNAzimuth", 36, "Flight directions tried per ring"}; + + // MC + Configurable cutRapMotherMC{"cutRapMotherMC", 1.0f, "Rapidity cut for generated mother Sigma+ in MC"}; + Configurable cutPtGenMC{"cutPtGenMC", 0.5f, "Minimum pT for generated Sigma+ in MC"}; Configurable fillSlimTables{"fillSlimTables", false, "write the slim candidate tables instead of the full ones"}; + // BDT score (MlScore column) of every written candidate, inputs in the order of the training (see computeMlScore) + Configurable applyMl{"applyMl", false, "Compute the BDT score of every written candidate (otherwise MlScore = -1)"}; + Configurable mlLoadModelFromCcdb{"mlLoadModelFromCcdb", true, "Load the ONNX model from the CCDB instead of a local file"}; + Configurable mlModelPathCcdb{"mlModelPathCcdb", "Users/h/hfribert/SigmaPlusPcm/BDT_nominalB", "CCDB path of the ONNX model (nominal field: .../BDT_nominalB, low field: .../BDT_lowB)"}; + Configurable mlOnnxFileName{"mlOnnxFileName", "model.onnx", "ONNX file name (local file, or name of the file retrieved from the CCDB)"}; + Configurable mlTimestampCcdb{"mlTimestampCcdb", -1, "CCDB timestamp of the ONNX model (-1: now)"}; + Configurable ccdbPath{"ccdbPath", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "CCDB path of the material budget LUT"}; Produces sigmaPlusCands; Produces sigmaPlusCandsMC; @@ -138,8 +175,84 @@ struct Sigmaplusbuilder { o2::vertexing::DCAFitterN<2> fitter; int mRunNumber = 0; float mBz = 0; - TF1 mThetaResoFunc; - TF1 mPhiResoFunc; + o2::base::MatLayerCylSet* mLut = nullptr; + o2::aod::common::TPCVDriftManager mVDriftMgr; + o2::analysis::MlResponse mlResponse; + + // daughter pairing (electrons and positrons with the range of collisions each is compatible with) + svPoolCreator svPhotonPoolCreator{PDG_t::kElectron, PDG_t::kPositron}; + std::vector mElectronPool; + std::vector mPositronPool; + std::vector mGoodCollision; // collisions passing the event selection + + // values of a candidate, kept until the end of the timeframe and then written to the tables + struct SigmaPlusCandidate { + uint64_t photonId = 0; + int collisionId = -1; + int protonTrackId = -1; + int photonPosTrackId = -1; + int photonNegTrackId = -1; + bool isSignal = false; + int photonLegsWithoutIts = 0; + int matchedSigmaId = -1; + std::array decVtx{}; + float radius = 0.f; + float flightDistance = 0.f; + float dcaProtonGamma = 0.f; + float dcaToPV = 0.f; + float tiltAngle = 0.f; + float rootCenter = 0.f; + float antiSigmaPointingAngle = 0.f; + std::array pProton{}; + std::array pGamma1{}; + std::array pGamma2{}; + float protonTpcNSigma = 0.f; + float protonTofNSigma = 0.f; + int protonSign = 0; + uint8_t protonItsNCls = 0; + int16_t protonTpcNCls = 0; + float protonDcaXY = 0.f; + float protonDcaZ = 0.f; + float photonMass = 0.f; + float photonAlpha = 0.f; + float photonQt = 0.f; + float photonRadius = 0.f; + float photonOpeningAngle = 0.f; + float photonPointingAngle = 0.f; + float photonDcaDau = 0.f; + float photonCosPAToPV = 0.f; + float photonDcaXYToPV = 0.f; + float photonDcaZToPV = 0.f; + float photonPsiPair = 0.f; + float posTpcNSigmaEl = 0.f; + float negTpcNSigmaEl = 0.f; + uint8_t posItsNCls = 0; + uint8_t negItsNCls = 0; + int16_t posTpcNCls = 0; + int16_t negTpcNCls = 0; + int16_t posTpcNClsFindable = 0; + int16_t negTpcNClsFindable = 0; + float posTpcChi2NCl = 0.f; + float negTpcChi2NCl = 0.f; + float posDcaXY = 0.f; + float posDcaZ = 0.f; + float negDcaXY = 0.f; + float negDcaZ = 0.f; + // MC + bool collisionIdCheck = false; + bool protonIsSignal = false; + bool photonIsSignal = false; + std::array decVtxMC{}; + std::array pProtonMC{}; + std::array pGammaMC{}; + std::array pSigmaPlusMC{}; + float decayRadiusMC = -999.f; + float massMC = -999.f; + }; + std::vector mCandidatesOfTimeframe; + + // MC: generated Sigma+ -> p pi0 in acceptance of current timeframe and whether a true candidate of it was written + std::unordered_map mGenSigmaWritten; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -162,10 +275,24 @@ struct Sigmaplusbuilder { fitter.setMaxChi2(1e9); fitter.setUseAbsDCA(true); - mThetaResoFunc = TF1("thetaResoFunc", "[0]/(abs(x)+[1])", -discrRetryThetaRange, discrRetryThetaRange); - mThetaResoFunc.SetParameters(discrRetryThetaPar0.value, discrRetryThetaPar1.value); - mPhiResoFunc = TF1("phiResoFunc", "[0]/(abs(x)+[1])", -discrRetryPhiRange, discrRetryPhiRange); - mPhiResoFunc.SetParameters(discrRetryPhiPar0.value, discrRetryPhiPar1.value); + mVDriftMgr.init(&ccdb->instance()); + if (applyMl) { + // one model with two classes (background, signal) and no cut on the scores + const std::array noCuts{0., 0.}; + mlResponse.configure({0., 1.e6}, LabeledArray(noCuts.data(), 1, 2), {o2::cuts_ml::CutNot, o2::cuts_ml::CutNot}, 2); + if (mlLoadModelFromCcdb) { + o2::ccdb::CcdbApi ccdbApi; + ccdbApi.init(ccdbPath.value); + mlResponse.setModelPathsCCDB({mlOnnxFileName.value}, ccdbApi, {mlModelPathCcdb.value}, mlTimestampCcdb.value); + } else { + mlResponse.setModelPathsLocal({mlOnnxFileName.value}); + } + mlResponse.init(); + } + svPhotonPoolCreator.setTimeMargin(photonPoolTimeMarginNS); + if (photonSkipAmbiTracks) { + svPhotonPoolCreator.setSkipAmbiTracks(); + } const AxisSpec axisVertexZ{100, -15., 15., "vrtx_{Z} (cm)"}; @@ -180,221 +307,103 @@ struct Sigmaplusbuilder { const AxisSpec axisTpcNCls{160, -0.5, 159.5, "TPC clusters"}; const AxisSpec axisPhotonOpeningAngle{180, 0., 3.15, "opening angle (rad)"}; const AxisSpec axisPhotonDeltaTheta{200, -1., 1., "#Delta#theta (rad)"}; + const AxisSpec axisPhotonDcaV0Dau{200, 0., 10., "DCA between photon daughters (cm)"}; + const AxisSpec axisCollisionsPerPhoton{100, 0.5, 100.5, "collisions a photon is kept in"}; + const AxisSpec axisTpcTimeRangeNColl{200, 0.5, 200.5, "collisions in the TPC time range of a TPC-only daughter"}; const AxisSpec axisProtonSel{7, -0.5, 6.5, "selection step"}; const AxisSpec axisProtonPt{100, 0., 5., "#it{p}_{T,p} (GeV/c)"}; const AxisSpec axisNSigma{100, -5., 5., "n#sigma"}; const AxisSpec axisProtonDcaToPV{1000, 0., 5., "DCA_{xy,p} to PV (cm)"}; - histos.add("hVertexZ", "hVertexZ", kTH1F, {axisVertexZ}); - - histos.add("Photon/hSelectionCounter", "Photon/hSelectionCounter", kTH1F, {axisPhotonSel}); - auto hPhotonSel = histos.get(HIST("Photon/hSelectionCounter")); - hPhotonSel->GetXaxis()->SetBinLabel(1, "All"); - hPhotonSel->GetXaxis()->SetBinLabel(2, "Neg eta"); - hPhotonSel->GetXaxis()->SetBinLabel(3, "Pos eta"); - hPhotonSel->GetXaxis()->SetBinLabel(4, "TPC clusters"); - hPhotonSel->GetXaxis()->SetBinLabel(5, "TPC nSigma_{el}"); - hPhotonSel->GetXaxis()->SetBinLabel(6, "DCA daughters"); - hPhotonSel->GetXaxis()->SetBinLabel(7, "Radius"); - hPhotonSel->GetXaxis()->SetBinLabel(8, "Opening angle"); - hPhotonSel->GetXaxis()->SetBinLabel(9, "Delta theta"); - hPhotonSel->GetXaxis()->SetBinLabel(10, "CosPA"); - hPhotonSel->GetXaxis()->SetBinLabel(11, "Rapidity"); - hPhotonSel->GetXaxis()->SetBinLabel(12, "Qt"); - hPhotonSel->GetXaxis()->SetBinLabel(13, "Alpha"); - hPhotonSel->GetXaxis()->SetBinLabel(14, "Mass"); - - histos.add("Photon/hMass", "Photon/hMass", kTH1F, {axisPhotonMass}); - histos.add("Photon/hPt", "Photon/hPt", kTH1F, {axisPhotonPt}); - histos.add("Photon/hRadius", "Photon/hRadius", kTH1F, {axisPhotonRadius}); - histos.add("Photon/h2ArmenterosPodolanski", "Photon/h2ArmenterosPodolanski", kTH2F, {axisAlpha, axisQt}); - histos.add("Photon/h2ConvPointXY", "Photon/h2ConvPointXY", kTH2F, {axisConvXY, axisConvXY}); - histos.add("Photon/h2TPCNSigmaElPosVsPt", "Photon/h2TPCNSigmaElPosVsPt", kTH2F, {axisPhotonPt, axisNSigmaEl}); - histos.add("Photon/h2TPCNSigmaElNegVsPt", "Photon/h2TPCNSigmaElNegVsPt", kTH2F, {axisPhotonPt, axisNSigmaEl}); - histos.add("Photon/h2TPCNClsPosVsPt", "Photon/h2TPCNClsPosVsPt", kTH2F, {axisPhotonPt, axisTpcNCls}); - histos.add("Photon/h2TPCNClsNegVsPt", "Photon/h2TPCNClsNegVsPt", kTH2F, {axisPhotonPt, axisTpcNCls}); - histos.add("Photon/hOpeningAngle", "Photon/hOpeningAngle", kTH1F, {axisPhotonOpeningAngle}); - histos.add("Photon/hDeltaTheta", "Photon/hDeltaTheta", kTH1F, {axisPhotonDeltaTheta}); - - histos.add("Proton/hSelectionCounter", "Proton/hSelectionCounter", kTH1F, {axisProtonSel}); - auto hProtonSel = histos.get(HIST("Proton/hSelectionCounter")); - hProtonSel->GetXaxis()->SetBinLabel(1, "All"); - hProtonSel->GetXaxis()->SetBinLabel(2, "Pt"); - hProtonSel->GetXaxis()->SetBinLabel(3, "Eta"); - hProtonSel->GetXaxis()->SetBinLabel(4, "TPC clusters"); - hProtonSel->GetXaxis()->SetBinLabel(5, "TPC nSigma"); - hProtonSel->GetXaxis()->SetBinLabel(6, "TOF nSigma"); - hProtonSel->GetXaxis()->SetBinLabel(7, "DCA to PV"); - - histos.add("Proton/hPt", "Proton/hPt", kTH1F, {axisProtonPt}); - histos.add("Proton/h2TPCNSigmaVsPt", "Proton/h2TPCNSigmaVsPt", kTH2F, {axisProtonPt, axisNSigma}); - histos.add("Proton/h2TOFNSigmaVsPt", "Proton/h2TOFNSigmaVsPt", kTH2F, {axisProtonPt, axisNSigma}); - histos.add("Proton/h2TPCNClsVsPt", "Proton/h2TPCNClsVsPt", kTH2F, {axisProtonPt, axisTpcNCls}); - histos.add("Proton/h2DcaToPVVsPt", "Proton/h2DcaToPVVsPt", kTH2F, {axisProtonPt, axisProtonDcaToPV}); - const AxisSpec axisCandSel{15, -0.5, 14.5, "selection step"}; + const AxisSpec axisPhotonLegType{3, -0.5, 2.5, "photon daughters without ITS"}; const AxisSpec axisCandPhotonDcaToPV{250, 0., 250., "DCA_{#gamma-line} to PV (cm)"}; const AxisSpec axisDca{100, 0., 10., "DCA(p,#gamma) (cm)"}; const AxisSpec axisDcaToPV{500, 0., 0.5, "DCA_{cand} to PV (cm)"}; const AxisSpec axisCandRadius{200, 0., 200., "R_{dec} (cm)"}; const AxisSpec axisFlightDistance{250, 0., 250., "|SV-PV| (cm)"}; - const AxisSpec axisDiscriminant{200, -1., 1., "discriminant (GeV^{4}/#it{c}^{4})"}; + const AxisSpec axisTilt{200, 0., 0.5, "flight direction tilt for a real root (rad)"}; const AxisSpec axisRootCenter{400, -10., 10., "-b/(2a) (GeV/#it{c})"}; const AxisSpec axisAntiSigmaPA{180, 0., 3.15, "AntiPA (rad)"}; const AxisSpec axisMassSigma{200, 1.0, 1.4, "m_{p#gamma#gamma} (GeV/#it{c}^{2})"}; const AxisSpec axisRapidity{200, -2., 2., "y_{#Sigma^{+}}"}; const AxisSpec axisSigmaPt{100, 0., 6., "#it{p}_{T,#Sigma^{+}} (GeV/#it{c})"}; - const AxisSpec axisMomentum{100, 0., 10., "#it{p} (GeV/#it{c})"}; - - histos.add("Candidate/hSelectionCounter", "Candidate/hSelectionCounter", kTH1F, {axisCandSel}); - auto hCandSel = histos.get(HIST("Candidate/hSelectionCounter")); - hCandSel->GetXaxis()->SetBinLabel(1, "All pairs"); - hCandSel->GetXaxis()->SetBinLabel(2, "Autocorrelation"); - hCandSel->GetXaxis()->SetBinLabel(3, "Vertex fit"); - hCandSel->GetXaxis()->SetBinLabel(4, "DCA(p,#gamma)"); - hCandSel->GetXaxis()->SetBinLabel(5, "Radius"); - hCandSel->GetXaxis()->SetBinLabel(6, "Flight distance"); - hCandSel->GetXaxis()->SetBinLabel(7, "Real root"); - hCandSel->GetXaxis()->SetBinLabel(8, "Valid root"); - hCandSel->GetXaxis()->SetBinLabel(9, "DCA to PV"); - hCandSel->GetXaxis()->SetBinLabel(10, "Mass"); - hCandSel->GetXaxis()->SetBinLabel(11, "Rapidity"); - hCandSel->GetXaxis()->SetBinLabel(12, "Photon opening angle"); - hCandSel->GetXaxis()->SetBinLabel(13, "Photon pointing angle"); - hCandSel->GetXaxis()->SetBinLabel(14, "Photon DCA to PV"); - hCandSel->GetXaxis()->SetBinLabel(15, "Filled"); - - histos.add("Candidate/hDcaProtonGamma", "Candidate/hDcaProtonGamma", kTH1F, {axisDca}); - histos.add("Candidate/hDcaToPV", "Candidate/hDcaToPV", kTH1F, {axisDcaToPV}); - histos.add("Candidate/hRadius", "Candidate/hRadius", kTH1F, {axisCandRadius}); - histos.add("Candidate/hFlightDistance", "Candidate/hFlightDistance", kTH1F, {axisFlightDistance}); - histos.add("Candidate/hPhotonOpeningAngle", "Candidate/hPhotonOpeningAngle", kTH1F, {axisPhotonOpeningAngle}); - histos.add("Candidate/hPhotonPointingAngle", "Candidate/hPhotonPointingAngle", kTH1F, {axisPhotonOpeningAngle}); - histos.add("Candidate/hPhotonDcaToPV", "Candidate/hPhotonDcaToPV", kTH1F, {axisCandPhotonDcaToPV}); - histos.add("Candidate/hDiscriminant", "Candidate/hDiscriminant", kTH1F, {axisDiscriminant}); - histos.add("Candidate/hRootCenter", "Candidate/hRootCenter", kTH1F, {axisRootCenter}); - histos.add("Candidate/hMassSigmaPlus", "Candidate/hMassSigmaPlus", kTH1F, {axisMassSigma}); - histos.add("Candidate/hRapidity", "Candidate/hRapidity", kTH1F, {axisRapidity}); - histos.add("Candidate/h2MassVsPt", "Candidate/h2MassVsPt", kTH2F, {axisSigmaPt, axisMassSigma}); - histos.add("Candidate/h2MassVsRootCenter", "Candidate/h2MassVsRootCenter", kTH2F, {axisRootCenter, axisMassSigma}); - histos.add("Candidate/hAntiSigmaPointingAngle", "Candidate/hAntiSigmaPointingAngle", kTH1F, {axisAntiSigmaPA}); - histos.add("Candidate/h2MassVsAntiSigmaPointingAngle", "Candidate/h2MassVsAntiSigmaPointingAngle", kTH2F, {axisAntiSigmaPA, axisMassSigma}); - - const AxisSpec axisDiscrIter{discrRetryMaxIter + 2, -0.5, discrRetryMaxIter + 1.5, "discriminant retry iteration"}; - histos.add("Candidate/hDiscriminantRetryIter", "Candidate/hDiscriminantRetryIter", kTH1F, {axisDiscrIter}); - - if (doprocessMc || doprocessFindable) { - histos.add("Photon/True/hSelectionCounter", "Photon/True/hSelectionCounter", kTH1F, {axisPhotonSel}); - auto hPhotonSelSignal = histos.get(HIST("Photon/True/hSelectionCounter")); - hPhotonSelSignal->GetXaxis()->SetBinLabel(1, "All"); - hPhotonSelSignal->GetXaxis()->SetBinLabel(2, "Neg eta"); - hPhotonSelSignal->GetXaxis()->SetBinLabel(3, "Pos eta"); - hPhotonSelSignal->GetXaxis()->SetBinLabel(4, "TPC clusters"); - hPhotonSelSignal->GetXaxis()->SetBinLabel(5, "TPC nSigma_{el}"); - hPhotonSelSignal->GetXaxis()->SetBinLabel(6, "DCA daughters"); - hPhotonSelSignal->GetXaxis()->SetBinLabel(7, "Radius"); - hPhotonSelSignal->GetXaxis()->SetBinLabel(8, "Opening angle"); - hPhotonSelSignal->GetXaxis()->SetBinLabel(9, "Delta theta"); - hPhotonSelSignal->GetXaxis()->SetBinLabel(10, "CosPA"); - hPhotonSelSignal->GetXaxis()->SetBinLabel(11, "Rapidity"); - hPhotonSelSignal->GetXaxis()->SetBinLabel(12, "Qt"); - hPhotonSelSignal->GetXaxis()->SetBinLabel(13, "Alpha"); - hPhotonSelSignal->GetXaxis()->SetBinLabel(14, "Mass"); - - histos.add("Photon/True/h2TPCNSigmaElPosVsPt", "Photon/True/h2TPCNSigmaElPosVsPt", kTH2F, {axisPhotonPt, axisNSigmaEl}); - histos.add("Photon/True/h2TPCNSigmaElNegVsPt", "Photon/True/h2TPCNSigmaElNegVsPt", kTH2F, {axisPhotonPt, axisNSigmaEl}); - histos.add("Photon/True/h2TPCNClsPosVsPt", "Photon/True/h2TPCNClsPosVsPt", kTH2F, {axisPhotonPt, axisTpcNCls}); - histos.add("Photon/True/h2TPCNClsNegVsPt", "Photon/True/h2TPCNClsNegVsPt", kTH2F, {axisPhotonPt, axisTpcNCls}); - } - - if (doprocessMc) { - histos.add("Photon/True/hMass", "Photon/True/hMass", kTH1F, {axisPhotonMass}); - histos.add("Photon/True/hPt", "Photon/True/hPt", kTH1F, {axisPhotonPt}); - histos.add("Photon/True/hRadius", "Photon/True/hRadius", kTH1F, {axisPhotonRadius}); - histos.add("Photon/True/h2ArmenterosPodolanski", "Photon/True/h2ArmenterosPodolanski", kTH2F, {axisAlpha, axisQt}); - histos.add("Photon/True/h2ConvPointXY", "Photon/True/h2ConvPointXY", kTH2F, {axisConvXY, axisConvXY}); - histos.add("Photon/True/hOpeningAngle", "Photon/True/hOpeningAngle", kTH1F, {axisPhotonOpeningAngle}); - histos.add("Photon/True/hDeltaTheta", "Photon/True/hDeltaTheta", kTH1F, {axisPhotonDeltaTheta}); - - histos.add("Proton/True/hPt", "Proton/True/hPt", kTH1F, {axisProtonPt}); - histos.add("Proton/True/h2TPCNSigmaVsPt", "Proton/True/h2TPCNSigmaVsPt", kTH2F, {axisProtonPt, axisNSigma}); - histos.add("Proton/True/h2TOFNSigmaVsPt", "Proton/True/h2TOFNSigmaVsPt", kTH2F, {axisProtonPt, axisNSigma}); - histos.add("Proton/True/h2TPCNClsVsPt", "Proton/True/h2TPCNClsVsPt", kTH2F, {axisProtonPt, axisTpcNCls}); - histos.add("Proton/True/h2DcaToPVVsPt", "Proton/True/h2DcaToPVVsPt", kTH2F, {axisProtonPt, axisProtonDcaToPV}); - - histos.add("Proton/True/hSelectionCounter", "Proton/True/hSelectionCounter", kTH1F, {axisProtonSel}); - auto hProtonSelSignal = histos.get(HIST("Proton/True/hSelectionCounter")); - hProtonSelSignal->GetXaxis()->SetBinLabel(1, "All"); - hProtonSelSignal->GetXaxis()->SetBinLabel(2, "Pt"); - hProtonSelSignal->GetXaxis()->SetBinLabel(3, "Eta"); - hProtonSelSignal->GetXaxis()->SetBinLabel(4, "TPC clusters"); - hProtonSelSignal->GetXaxis()->SetBinLabel(5, "TPC nSigma"); - hProtonSelSignal->GetXaxis()->SetBinLabel(6, "TOF nSigma"); - hProtonSelSignal->GetXaxis()->SetBinLabel(7, "DCA to PV"); - - histos.add("Candidate/True/hSelectionCounter", "Candidate/True/hSelectionCounter", kTH1F, {axisCandSel}); - auto hCandSelSignal = histos.get(HIST("Candidate/True/hSelectionCounter")); - hCandSelSignal->GetXaxis()->SetBinLabel(1, "All pairs"); - hCandSelSignal->GetXaxis()->SetBinLabel(2, "Autocorrelation"); - hCandSelSignal->GetXaxis()->SetBinLabel(3, "Vertex fit"); - hCandSelSignal->GetXaxis()->SetBinLabel(4, "DCA(p,#gamma)"); - hCandSelSignal->GetXaxis()->SetBinLabel(5, "Radius"); - hCandSelSignal->GetXaxis()->SetBinLabel(6, "Flight distance"); - hCandSelSignal->GetXaxis()->SetBinLabel(7, "Real root"); - hCandSelSignal->GetXaxis()->SetBinLabel(8, "Valid root"); - hCandSelSignal->GetXaxis()->SetBinLabel(9, "DCA to PV"); - hCandSelSignal->GetXaxis()->SetBinLabel(10, "Mass"); - hCandSelSignal->GetXaxis()->SetBinLabel(11, "Rapidity"); - hCandSelSignal->GetXaxis()->SetBinLabel(12, "Photon opening angle"); - hCandSelSignal->GetXaxis()->SetBinLabel(13, "Photon pointing angle"); - hCandSelSignal->GetXaxis()->SetBinLabel(14, "Photon DCA to PV"); - hCandSelSignal->GetXaxis()->SetBinLabel(15, "Filled"); - - histos.add("Candidate/True/hDcaProtonGamma", "Candidate/True/hDcaProtonGamma", kTH1F, {axisDca}); - histos.add("Candidate/True/hDcaToPV", "Candidate/True/hDcaToPV", kTH1F, {axisDcaToPV}); - histos.add("Candidate/True/hRadius", "Candidate/True/hRadius", kTH1F, {axisCandRadius}); - histos.add("Candidate/True/hFlightDistance", "Candidate/True/hFlightDistance", kTH1F, {axisFlightDistance}); - histos.add("Candidate/True/hPhotonOpeningAngle", "Candidate/True/hPhotonOpeningAngle", kTH1F, {axisPhotonOpeningAngle}); - histos.add("Candidate/True/hPhotonPointingAngle", "Candidate/True/hPhotonPointingAngle", kTH1F, {axisPhotonOpeningAngle}); - histos.add("Candidate/True/hPhotonDcaToPV", "Candidate/True/hPhotonDcaToPV", kTH1F, {axisCandPhotonDcaToPV}); - const AxisSpec axisVtxRes{200, 0., 20., "|vtx_{fit} - vtx_{MC}| (cm)"}; - histos.add("Candidate/True/hVertexResFromMcTruth", "Candidate/True/hVertexResFromMcTruth", kTH1F, {axisVtxRes}); - const AxisSpec axisMomRes{200, -1., 1., "(p_{fit} - p_{MC}) / p_{MC}"}; - histos.add("Candidate/True/hProtonMomResFromMcTruth", "Candidate/True/hProtonMomResFromMcTruth", kTH1F, {axisMomRes}); - histos.add("Candidate/True/hPhotonMomResFromMcTruth", "Candidate/True/hPhotonMomResFromMcTruth", kTH1F, {axisMomRes}); - const AxisSpec axisProtonFlightAngle{180, 0., 3.15, "angle(p_{proton}, n) (rad)"}; - histos.add("Candidate/True/hProtonFlightAngle", "Candidate/True/hProtonFlightAngle", kTH1F, {axisProtonFlightAngle}); - histos.add("Candidate/True/hDiscriminant", "Candidate/True/hDiscriminant", kTH1F, {axisDiscriminant}); - histos.add("Candidate/True/hDiscriminantRetryIter", "Candidate/True/hDiscriminantRetryIter", kTH1F, {axisDiscrIter}); - histos.add("Candidate/True/hRootCenter", "Candidate/True/hRootCenter", kTH1F, {axisRootCenter}); - histos.add("Candidate/True/hMassSigmaPlus", "Candidate/True/hMassSigmaPlus", kTH1F, {axisMassSigma}); - histos.add("Candidate/True/hRapidity", "Candidate/True/hRapidity", kTH1F, {axisRapidity}); - histos.add("Candidate/True/h2MassVsPt", "Candidate/True/h2MassVsPt", kTH2F, {axisSigmaPt, axisMassSigma}); - histos.add("Candidate/True/h2MassVsRootCenter", "Candidate/True/h2MassVsRootCenter", kTH2F, {axisRootCenter, axisMassSigma}); - histos.add("Candidate/True/hAntiSigmaPointingAngle", "Candidate/True/hAntiSigmaPointingAngle", kTH1F, {axisAntiSigmaPA}); - histos.add("Candidate/True/h2MassVsAntiSigmaPointingAngle", "Candidate/True/h2MassVsAntiSigmaPointingAngle", kTH2F, {axisAntiSigmaPA, axisMassSigma}); + const AxisSpec axisCandidatesPerPhoton{50, 0.5, 50.5, "candidates sharing one photon"}; + + const AxisSpec axisSigmaSel{6, -0.5, 5.5, "step"}; + + const std::vector photonSteps{"All", "Neg eta", "Pos eta", "TPC clusters", "TPC nSigma_{el}, p_{T}", "DCA daughters", "Radius", + "Opening angle", "Delta theta", "CosPA, DCA_{z}", "Rapidity", "Qt", "Alpha", "Mass"}; + const std::vector protonSteps{"All", "Pt", "Eta", "TPC clusters", "TPC nSigma", "TOF nSigma", "DCA to PV"}; + const std::vector candSteps{"All pairs", "Autocorrelation", "Vertex fit", "DCA(p,#gamma)", "Radius", "Flight distance", "Real root", + "Valid root", "DCA to PV", "Mass", "Rapidity", "Photon opening angle", "Photon pointing angle", "Photon DCA to PV", "Written"}; + const std::vector sigmaSteps{"Generated", "Proton reconstructed", "Proton selected", "Photon daughters reconstructed", "Photon selected", "Candidate written"}; + auto setBinLabels = [](TAxis* axis, const std::vector& labels) { + for (size_t i = 0; i < labels.size(); ++i) { + axis->SetBinLabel(i + 1, labels[i].c_str()); + } + }; + histos.add("hVertexZ", "hVertexZ", kTH1F, {axisVertexZ}); + setBinLabels(histos.add("Photon/Inclusive/hSelectionCounter", "hSelectionCounter", kTH1D, {axisPhotonSel})->GetXaxis(), photonSteps); + histos.add("Photon/Inclusive/h2TPCNClsPosVsPt", "h2TPCNClsPosVsPt", kTH2F, {axisPhotonPt, axisTpcNCls}); + histos.add("Photon/Inclusive/h2TPCNClsNegVsPt", "h2TPCNClsNegVsPt", kTH2F, {axisPhotonPt, axisTpcNCls}); + histos.add("Photon/Inclusive/h2TPCNSigmaElPosVsPt", "h2TPCNSigmaElPosVsPt", kTH2F, {axisPhotonPt, axisNSigmaEl}); + histos.add("Photon/Inclusive/h2TPCNSigmaElNegVsPt", "h2TPCNSigmaElNegVsPt", kTH2F, {axisPhotonPt, axisNSigmaEl}); + histos.add("Photon/Inclusive/hDcaV0Daughters", "hDcaV0Daughters", kTH1F, {axisPhotonDcaV0Dau}); + histos.add("Photon/Inclusive/hOpeningAngle", "hOpeningAngle", kTH1F, {axisPhotonOpeningAngle}); + histos.add("Photon/Inclusive/hDeltaTheta", "hDeltaTheta", kTH1F, {axisPhotonDeltaTheta}); + histos.add("Photon/Inclusive/hMass", "hMass", kTH1F, {axisPhotonMass}); + histos.add("Photon/Inclusive/hPt", "hPt", kTH1F, {axisPhotonPt}); + histos.add("Photon/Inclusive/hRadius", "hRadius", kTH1F, {axisPhotonRadius}); + histos.add("Photon/Inclusive/h2ArmenterosPodolanski", "h2ArmenterosPodolanski", kTH2F, {axisAlpha, axisQt}); + histos.add("Photon/Inclusive/h2ConvPointXY", "h2ConvPointXY", kTH2F, {axisConvXY, axisConvXY}); + histos.add("Photon/Inclusive/hCollisionsPerPhoton", "hCollisionsPerPhoton", kTH1F, {axisCollisionsPerPhoton}); + + setBinLabels(histos.add("Proton/Inclusive/hSelectionCounter", "hSelectionCounter", kTH1D, {axisProtonSel})->GetXaxis(), protonSteps); + histos.add("Proton/Inclusive/hPt", "hPt", kTH1F, {axisProtonPt}); + histos.add("Proton/Inclusive/h2TPCNSigmaVsPt", "h2TPCNSigmaVsPt", kTH2F, {axisProtonPt, axisNSigma}); + histos.add("Proton/Inclusive/h2TOFNSigmaVsPt", "h2TOFNSigmaVsPt", kTH2F, {axisProtonPt, axisNSigma}); + histos.add("Proton/Inclusive/h2TPCNClsVsPt", "h2TPCNClsVsPt", kTH2F, {axisProtonPt, axisTpcNCls}); + histos.add("Proton/Inclusive/h2DcaToPVVsPt", "h2DcaToPVVsPt", kTH2F, {axisProtonPt, axisProtonDcaToPV}); + + setBinLabels(histos.add("Candidate/Inclusive/hSelectionCounter", "hSelectionCounter", kTH1D, {axisCandSel})->GetXaxis(), candSteps); + setBinLabels(histos.add("Candidate/Inclusive/h2SelectionCounterVsLegType", "h2SelectionCounterVsLegType", kTH2D, {axisCandSel, axisPhotonLegType})->GetXaxis(), candSteps); + histos.add("Candidate/Inclusive/hDcaProtonGamma", "hDcaProtonGamma", kTH1F, {axisDca}); + histos.add("Candidate/Inclusive/hRadius", "hRadius", kTH1F, {axisCandRadius}); + histos.add("Candidate/Inclusive/hFlightDistance", "hFlightDistance", kTH1F, {axisFlightDistance}); + histos.add("Candidate/Inclusive/hTiltAngle", "hTiltAngle", kTH1F, {axisTilt}); + histos.add("Candidate/Inclusive/hRootCenter", "hRootCenter", kTH1F, {axisRootCenter}); + histos.add("Candidate/Inclusive/hDcaToPV", "hDcaToPV", kTH1F, {axisDcaToPV}); + histos.add("Candidate/Inclusive/hAntiSigmaPointingAngle", "hAntiSigmaPointingAngle", kTH1F, {axisAntiSigmaPA}); + histos.add("Candidate/Inclusive/hMassSigmaPlus", "hMassSigmaPlus", kTH1F, {axisMassSigma}); + histos.add("Candidate/Inclusive/h2MassVsPt", "h2MassVsPt", kTH2F, {axisSigmaPt, axisMassSigma}); + histos.add("Candidate/Inclusive/h2MassVsTilt", "h2MassVsTilt", kTH2F, {axisTilt, axisMassSigma}); + histos.add("Candidate/Inclusive/hRapidity", "hRapidity", kTH1F, {axisRapidity}); + histos.add("Candidate/Inclusive/hPhotonOpeningAngle", "hPhotonOpeningAngle", kTH1F, {axisPhotonOpeningAngle}); + histos.add("Candidate/Inclusive/hPhotonPointingAngle", "hPhotonPointingAngle", kTH1F, {axisPhotonOpeningAngle}); + histos.add("Candidate/Inclusive/hPhotonDcaToPV", "hPhotonDcaToPV", kTH1F, {axisCandPhotonDcaToPV}); + + const bool doMc = doprocessMc || doprocessMcCustomVertexer; + if (doMc || doprocessFindable) { + histos.addClone("Photon/Inclusive/", "Photon/True/"); + } + if (doMc) { + histos.addClone("Proton/Inclusive/", "Proton/True/"); + histos.addClone("Candidate/Inclusive/", "Candidate/True/"); + } + + histos.add("Photon/Inclusive/hTpcTimeRangeNColl", "hTpcTimeRangeNColl", kTH1F, {axisTpcTimeRangeNColl}); + histos.add("Candidate/Inclusive/hCandidatesPerPhoton", "hCandidatesPerPhoton", kTH1F, {axisCandidatesPerPhoton}); + + if (doMc) { histos.add("MC/hGenSigmaPlusPt", "MC/hGenSigmaPlusPt", kTH1F, {axisSigmaPt}); - - const AxisSpec axisProtonTruthQA{3, -0.5, 2.5, "step"}; - histos.add("MC/hProtonTruthQA", "MC/hProtonTruthQA", kTH1F, {axisProtonTruthQA}); - auto hProtonTruthQA = histos.get(HIST("MC/hProtonTruthQA")); - hProtonTruthQA->GetXaxis()->SetBinLabel(1, "Accepted"); - hProtonTruthQA->GetXaxis()->SetBinLabel(2, "Has mcParticle"); - hProtonTruthQA->GetXaxis()->SetBinLabel(3, "Sigma+ mother"); - - const AxisSpec axisPhotonTruthQA{5, -0.5, 4.5, "step"}; - histos.add("MC/hPhotonTruthQA", "MC/hPhotonTruthQA", kTH1F, {axisPhotonTruthQA}); - auto hPhotonTruthQA = histos.get(HIST("MC/hPhotonTruthQA")); - hPhotonTruthQA->GetXaxis()->SetBinLabel(1, "Accepted"); - hPhotonTruthQA->GetXaxis()->SetBinLabel(2, "Both legs have mcParticle"); - hPhotonTruthQA->GetXaxis()->SetBinLabel(3, "Shared real gamma mother"); - hPhotonTruthQA->GetXaxis()->SetBinLabel(4, "Gamma's mother is pi0"); - hPhotonTruthQA->GetXaxis()->SetBinLabel(5, "Pi0's mother is Sigma+"); + setBinLabels(histos.add("MC/hSigmaPlusCounter", "MC/hSigmaPlusCounter", kTH1D, {axisSigmaSel})->GetXaxis(), sigmaSteps); } if (doprocessFindable) { + const AxisSpec axisMomentum{100, 0., 10., "#it{p} (GeV/#it{c})"}; const AxisSpec axisDetectorPresence{3, -0.5, 2.5, "detector"}; const AxisSpec axisDuplicateTrack{2, -0.5, 1.5, "track"}; const AxisSpec axisTPCClusters{160, -0.5, 159.5, "TPC clusters"}; @@ -418,23 +427,11 @@ struct Sigmaplusbuilder { histos.add("Findable/hElectronDetectorPresence", "Findable/hElectronDetectorPresence", kTH1F, {axisDetectorPresence}); histos.add("Findable/hPositronDetectorPresence", "Findable/hPositronDetectorPresence", kTH1F, {axisDetectorPresence}); - auto hConversionPairV0Presence = histos.get(HIST("Findable/hConversionPairV0Presence")); - hConversionPairV0Presence->GetXaxis()->SetBinLabel(1, "valid pair"); - hConversionPairV0Presence->GetXaxis()->SetBinLabel(2, "in V0"); - auto hPhotonSearchPresence = histos.get(HIST("Findable/hPhotonSearchPresence")); - hPhotonSearchPresence->GetXaxis()->SetBinLabel(1, "valid pair"); - hPhotonSearchPresence->GetXaxis()->SetBinLabel(2, "passed photon selection"); - auto hDuplicateConversionTrackCounter = histos.get(HIST("Findable/hDuplicateConversionTrackCounter")); - hDuplicateConversionTrackCounter->GetXaxis()->SetBinLabel(1, "e^{-}"); - hDuplicateConversionTrackCounter->GetXaxis()->SetBinLabel(2, "e^{+}"); - auto hElectronDetectorPresence = histos.get(HIST("Findable/hElectronDetectorPresence")); - auto hPositronDetectorPresence = histos.get(HIST("Findable/hPositronDetectorPresence")); - hElectronDetectorPresence->GetXaxis()->SetBinLabel(1, "ITS"); - hElectronDetectorPresence->GetXaxis()->SetBinLabel(2, "TPC"); - hElectronDetectorPresence->GetXaxis()->SetBinLabel(3, "TOF"); - hPositronDetectorPresence->GetXaxis()->SetBinLabel(1, "ITS"); - hPositronDetectorPresence->GetXaxis()->SetBinLabel(2, "TPC"); - hPositronDetectorPresence->GetXaxis()->SetBinLabel(3, "TOF"); + setBinLabels(histos.get(HIST("Findable/hConversionPairV0Presence"))->GetXaxis(), {"valid pair", "in V0"}); + setBinLabels(histos.get(HIST("Findable/hPhotonSearchPresence"))->GetXaxis(), {"valid pair", "passed photon selection"}); + setBinLabels(histos.get(HIST("Findable/hDuplicateConversionTrackCounter"))->GetXaxis(), {"e^{-}", "e^{+}"}); + setBinLabels(histos.get(HIST("Findable/hElectronDetectorPresence"))->GetXaxis(), {"ITS", "TPC", "TOF"}); + setBinLabels(histos.get(HIST("Findable/hPositronDetectorPresence"))->GetXaxis(), {"ITS", "TPC", "TOF"}); } } @@ -447,11 +444,56 @@ struct Sigmaplusbuilder { float alpha = 0.f; float qtarm = 0.f; float radius = 0.f; + float dcaDau = 0.f; + float cosPAToPV = 0.f; TTrack negTrack; TTrack posTrack; }; - // photon candidates + // longitudinal distance of the photon's flight line to the PV at its transverse DCA (cm) + static float photonDcaZ(const std::array& conv, const std::array& pGamma, const std::array& pv) + { + float pt2 = pGamma[0] * pGamma[0] + pGamma[1] * pGamma[1]; + float tAtDcaXY = pt2 > 0.f ? ((pv[0] - conv[0]) * pGamma[0] + (pv[1] - conv[1]) * pGamma[1]) / pt2 : 0.f; + return conv[2] + tAtDcaXY * pGamma[2] - pv[2]; + } + + // MC counter of generated Sigma+ -> p pi0 in acceptance + void fillSigmaCounter(int sigmaId, int step) + { + if (mGenSigmaWritten.contains(sigmaId)) { + histos.fill(HIST("MC/hSigmaPlusCounter"), step); + } + } + + template + void fillPhotonHist(const TName& name, bool isSignal, Ts... values) + { + histos.fill(HIST("Photon/Inclusive/") + name, values...); + if (isSignal) { + histos.fill(HIST("Photon/True/") + name, values...); + } + } + + template + void fillProtonHist(const TName& name, bool isSignal, Ts... values) + { + histos.fill(HIST("Proton/Inclusive/") + name, values...); + if (isSignal) { + histos.fill(HIST("Proton/True/") + name, values...); + } + } + + template + void fillCandHist(const TName& name, bool isSignal, Ts... values) + { + histos.fill(HIST("Candidate/Inclusive/") + name, values...); + if (isSignal) { + histos.fill(HIST("Candidate/True/") + name, values...); + } + } + + // photon candidates from V0Datas template std::vector> findPhotonsFromV0s(const TV0s& v0s, const TTracks&, const std::array& pv) { @@ -461,22 +503,16 @@ struct Sigmaplusbuilder { auto posTrack = v0.template posTrack_as(); auto negTrack = v0.template negTrack_as(); - bool isSignal = false; + int sigmaId = -1; if constexpr (IsMC) { if (posTrack.has_mcParticle() && negTrack.has_mcParticle()) { - auto mcPos = posTrack.template mcParticle_as(); - auto mcNeg = negTrack.template mcParticle_as(); - isSignal = findSigmaPlusMotherOfPhoton(mcPos, mcNeg) >= 0; + sigmaId = findSigmaPlusMotherOfPhoton(posTrack.template mcParticle_as(), negTrack.template mcParticle_as()); } } + bool isSignal = sigmaId >= 0; auto fillPhotonStep = [&](int step) { - histos.fill(HIST("Photon/hSelectionCounter"), step); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Photon/True/hSelectionCounter"), step); - } - } + fillPhotonHist(HIST("hSelectionCounter"), isSignal, step); }; fillPhotonStep(0); @@ -490,33 +526,25 @@ struct Sigmaplusbuilder { } fillPhotonStep(2); - histos.fill(HIST("Photon/h2TPCNClsPosVsPt"), posTrack.pt(), posTrack.tpcNClsFound()); - histos.fill(HIST("Photon/h2TPCNClsNegVsPt"), negTrack.pt(), negTrack.tpcNClsFound()); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Photon/True/h2TPCNClsPosVsPt"), posTrack.pt(), posTrack.tpcNClsFound()); - histos.fill(HIST("Photon/True/h2TPCNClsNegVsPt"), negTrack.pt(), negTrack.tpcNClsFound()); - } - } + fillPhotonHist(HIST("h2TPCNClsPosVsPt"), isSignal, posTrack.pt(), posTrack.tpcNClsFound()); + fillPhotonHist(HIST("h2TPCNClsNegVsPt"), isSignal, negTrack.pt(), negTrack.tpcNClsFound()); if (posTrack.tpcNClsFound() < photonDauMinTpcNCls || negTrack.tpcNClsFound() < photonDauMinTpcNCls) { continue; } fillPhotonStep(3); - histos.fill(HIST("Photon/h2TPCNSigmaElPosVsPt"), posTrack.pt(), posTrack.tpcNSigmaEl()); - histos.fill(HIST("Photon/h2TPCNSigmaElNegVsPt"), negTrack.pt(), negTrack.tpcNSigmaEl()); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Photon/True/h2TPCNSigmaElPosVsPt"), posTrack.pt(), posTrack.tpcNSigmaEl()); - histos.fill(HIST("Photon/True/h2TPCNSigmaElNegVsPt"), negTrack.pt(), negTrack.tpcNSigmaEl()); - } - } + fillPhotonHist(HIST("h2TPCNSigmaElPosVsPt"), isSignal, posTrack.pt(), posTrack.tpcNSigmaEl()); + fillPhotonHist(HIST("h2TPCNSigmaElNegVsPt"), isSignal, negTrack.pt(), negTrack.tpcNSigmaEl()); if (posTrack.tpcNSigmaEl() < photonDauMinTPCNSigmaEl || posTrack.tpcNSigmaEl() > photonDauMaxTPCNSigmaEl || negTrack.tpcNSigmaEl() < photonDauMinTPCNSigmaEl || negTrack.tpcNSigmaEl() > photonDauMaxTPCNSigmaEl) { continue; } + if (posTrack.pt() > photonDauMaxPt || negTrack.pt() > photonDauMaxPt) { + continue; + } fillPhotonStep(4); + fillPhotonHist(HIST("hDcaV0Daughters"), isSignal, v0.dcaV0daughters()); if (v0.dcaV0daughters() > photonMaxDCAV0Dau) { continue; } @@ -532,41 +560,24 @@ struct Sigmaplusbuilder { std::array pNeg{v0.pxneg(), v0.pyneg(), v0.pzneg()}; std::array pPos{v0.pxpos(), v0.pypos(), v0.pzpos()}; - // opening angle between the daughter momenta - float photonOpeningAngleV0 = std::acos(std::clamp(dot3(pPos, pNeg) / std::sqrt(dot3(pPos, pPos) * dot3(pNeg, pNeg)), -1.f, 1.f)); - histos.fill(HIST("Photon/hOpeningAngle"), photonOpeningAngleV0); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Photon/True/hOpeningAngle"), photonOpeningAngleV0); - } - } - if (photonOpeningAngleV0 > photonMaxOpeningAngle) { + float photonOpeningAngle = o2::aod::pwgem::dilepton::utils::pairutil::getOpeningAngle(pPos[0], pPos[1], pPos[2], pNeg[0], pNeg[1], pNeg[2]); + fillPhotonHist(HIST("hOpeningAngle"), isSignal, photonOpeningAngle); + if (photonOpeningAngle > photonMaxOpeningAngle) { continue; } fillPhotonStep(7); - // delta theta between the daughter tracks' own polar angles - float posTheta = 2.f * std::atan(std::exp(-posTrack.eta())); - float negTheta = 2.f * std::atan(std::exp(-negTrack.eta())); - float photonDeltaTheta = posTheta - negTheta; - histos.fill(HIST("Photon/hDeltaTheta"), photonDeltaTheta); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Photon/True/hDeltaTheta"), photonDeltaTheta); - } - } + // difference of the daughter tracks' polar angles + float photonDeltaTheta = 2.f * std::atan(std::exp(-posTrack.eta())) - 2.f * std::atan(std::exp(-negTrack.eta())); + fillPhotonHist(HIST("hDeltaTheta"), isSignal, photonDeltaTheta); if (std::abs(photonDeltaTheta) > photonMaxDeltaTheta) { continue; } fillPhotonStep(8); std::array pGamma{pNeg[0] + pPos[0], pNeg[1] + pPos[1], pNeg[2] + pPos[2]}; - float gammaP = std::sqrt(dot3(pGamma, pGamma)); - - std::array flightVec{secVtx[0] - pv[0], secVtx[1] - pv[1], secVtx[2] - pv[2]}; - float flightNorm = std::sqrt(dot3(flightVec, flightVec)); - float cosPA = dot3(flightVec, pGamma) / (flightNorm * gammaP); - if (cosPA < photonMinV0cospa) { + float cosPA = RecoDecay::cpa(pv, secVtx, pGamma); + if (cosPA < photonMinV0cospa || std::abs(photonDcaZ(secVtx, pGamma, pv)) > candMaxPhotonDcaZToPV) { continue; } fillPhotonStep(9); @@ -594,19 +605,453 @@ struct Sigmaplusbuilder { continue; } fillPhotonStep(13); + fillSigmaCounter(sigmaId, 4); - histos.fill(HIST("Photon/hMass"), mGamma); - histos.fill(HIST("Photon/hPt"), std::hypot(pGamma[0], pGamma[1])); - histos.fill(HIST("Photon/hRadius"), radius); - histos.fill(HIST("Photon/h2ArmenterosPodolanski"), alpha, qtarm); - histos.fill(HIST("Photon/h2ConvPointXY"), secVtx[0], secVtx[1]); + fillPhotonHist(HIST("hMass"), isSignal, mGamma); + fillPhotonHist(HIST("hPt"), isSignal, std::hypot(pGamma[0], pGamma[1])); + fillPhotonHist(HIST("hRadius"), isSignal, radius); + fillPhotonHist(HIST("h2ArmenterosPodolanski"), isSignal, alpha, qtarm); + fillPhotonHist(HIST("h2ConvPointXY"), isSignal, secVtx[0], secVtx[1]); - photons.push_back({secVtx[0], secVtx[1], secVtx[2], pGamma[0], pGamma[1], pGamma[2], mGamma, alpha, qtarm, radius, negTrack, posTrack}); + photons.push_back({secVtx[0], secVtx[1], secVtx[2], pGamma[0], pGamma[1], pGamma[2], mGamma, alpha, qtarm, radius, v0.dcaV0daughters(), cosPA, negTrack, posTrack}); } return photons; } + static uint64_t photonId(int64_t posTrackId, int64_t negTrackId) + { + return (static_cast(posTrackId) << 32) | static_cast(negTrackId); + } + + // psi_pair of the photon daughters (similar to PsiPair in PWGEM) + template + float photonPsiPair(const TTrack& posTrack, const TTrack& negTrack, float convRadius) + { + for (const float& offsetR : {60.f, 30.f, 10.f}) { + auto posTrackPar = getTrackParCov(posTrack); + auto negTrackPar = getTrackParCov(negTrack); + posTrackPar.setPID(o2::track::PID::Electron); + negTrackPar.setPID(o2::track::PID::Electron); + if (!o2::base::Propagator::Instance()->propagateToR(posTrackPar, convRadius + offsetR) || !o2::base::Propagator::Instance()->propagateToR(negTrackPar, convRadius + offsetR)) { + continue; + } + std::array pPos{}; + std::array pNeg{}; + posTrackPar.getPxPyPzGlo(pPos); + negTrackPar.getPxPyPzGlo(pNeg); + return o2::aod::pwgem::dilepton::utils::pairutil::getPsiPair(pPos[0], pPos[1], pPos[2], pNeg[0], pNeg[1], pNeg[2]); + } + return 999.f; + } + + template + void markGoodCollisions(const TCollisions& collisions) + { + mGoodCollision.assign(collisions.size(), false); + for (const auto& collision : collisions) { + if (std::abs(collision.posZ()) > cutZVertex || !collision.sel8()) { + continue; + } + mGoodCollision[collision.globalIndex()] = true; + } + } + + // photon daughter selection of the self-pairing (TPC histograms filled before cuts) + template + bool selectPhotonDaughter(const TTrack& track, bool isSignalLeg) + { + if (track.eta() < photonDauEtaMin || track.eta() > photonDauEtaMax) { + return false; + } + if (track.sign() > 0) { + fillPhotonHist(HIST("h2TPCNClsPosVsPt"), isSignalLeg, track.pt(), track.tpcNClsFound()); + } else { + fillPhotonHist(HIST("h2TPCNClsNegVsPt"), isSignalLeg, track.pt(), track.tpcNClsFound()); + } + if (track.tpcNClsFound() < photonDauMinTpcNCls) { + return false; + } + if (track.sign() > 0) { + fillPhotonHist(HIST("h2TPCNSigmaElPosVsPt"), isSignalLeg, track.pt(), track.tpcNSigmaEl()); + } else { + fillPhotonHist(HIST("h2TPCNSigmaElNegVsPt"), isSignalLeg, track.pt(), track.tpcNSigmaEl()); + } + if (track.tpcNSigmaEl() < photonDauMinTPCNSigmaEl || track.tpcNSigmaEl() > photonDauMaxTPCNSigmaEl) { + return false; + } + return track.pt() <= photonDauMaxPt; + } + + // collisions of a track with only TPC time (all inside time range [t - backward, t + forward] plus a margin) + template + TrackCand tpcOnlyTrackCand(const TTrack& track, const std::vector& collisionBcNS, const std::vector& collisionTimeNS) + { + o2::aod::track::extensions::TPCTimeErrEncoding timeEncoding{}; + timeEncoding.encoding.timeErr = track.trackTimeRes(); + double trackTimeNS = collisionBcNS[track.collisionId()] + track.trackTime(); // trackTime() is relative to the BC of its collision + double timeMin = trackTimeNS - timeEncoding.getDeltaTBwd() - photonPoolTimeMarginNS; + double timeMax = trackTimeNS + timeEncoding.getDeltaTFwd() + photonPoolTimeMarginNS; + int firstCollIdx = track.collisionId(); + int lastCollIdx = firstCollIdx; + for (int collIdx = 0; collIdx < static_cast(collisionTimeNS.size()); ++collIdx) { + if (collisionTimeNS[collIdx] >= timeMin && collisionTimeNS[collIdx] <= timeMax) { + firstCollIdx = std::min(firstCollIdx, collIdx); + lastCollIdx = std::max(lastCollIdx, collIdx); + } + } + histos.fill(HIST("Photon/Inclusive/hTpcTimeRangeNColl"), lastCollIdx - firstCollIdx + 1); + return TrackCand{.Idxtr = static_cast(track.globalIndex()), .collBracket = {firstCollIdx, lastCollIdx}}; + } + + // Finds electron-positron pairs with similar polar angles (|theta+ - theta-| <= photonMaxDeltaTheta) that are compatible with + // at least one common collision. Only pairs accepted by acceptPair (the cut before the fit) are returned. + // nPairsInWindow counts all pairs in the theta window, also the ones acceptPair rejects. + template + std::vector findPairsByPolarAngle(const std::vector& thetaOfTrack, int nCollisions, const TAccept& acceptPair, uint64_t& nPairsInWindow) + { + // electrons of each collision sorted by polar angle, so a positron only looks at those inside its theta window + std::vector>> electronsByCollision(nCollisions); + for (int iElectron = 0; iElectron < static_cast(mElectronPool.size()); ++iElectron) { + const auto& electron = mElectronPool[iElectron]; + for (int collIdx = electron.collBracket.getMin(); collIdx <= electron.collBracket.getMax(); ++collIdx) { + electronsByCollision[collIdx].push_back({thetaOfTrack[electron.Idxtr], iElectron}); + } + } + for (size_t collIdx = 0; collIdx < electronsByCollision.size(); ++collIdx) { + std::sort(electronsByCollision[collIdx].begin(), electronsByCollision[collIdx].end()); + } + + std::vector pairs; + std::vector lastPositronPaired(mElectronPool.size(), -1); // an electron in several collisions is paired once per positron + for (int iPositron = 0; iPositron < static_cast(mPositronPool.size()); ++iPositron) { + const auto& positron = mPositronPool[iPositron]; + float thetaMin = thetaOfTrack[positron.Idxtr] - photonMaxDeltaTheta; + float thetaMax = thetaOfTrack[positron.Idxtr] + photonMaxDeltaTheta; + for (int collIdx = positron.collBracket.getMin(); collIdx <= positron.collBracket.getMax(); ++collIdx) { + const auto& electronsOfCollision = electronsByCollision[collIdx]; + auto electronIt = std::lower_bound(electronsOfCollision.begin(), electronsOfCollision.end(), std::make_pair(thetaMin, -1)); + for (; electronIt != electronsOfCollision.end() && electronIt->first <= thetaMax; ++electronIt) { + int iElectron = electronIt->second; + if (lastPositronPaired[iElectron] == iPositron) { + continue; + } + lastPositronPaired[iElectron] = iPositron; + const auto& electron = mElectronPool[iElectron]; + ++nPairsInWindow; + if (!acceptPair(electron.Idxtr, positron.Idxtr)) { + continue; + } + pairs.push_back(SVCand{.tr0Idx = electron.Idxtr, .tr1Idx = positron.Idxtr, .collBracket = electron.collBracket.getOverlap(positron.collBracket)}); + } + } + } + return pairs; + } + + // Cut before the fit: in the transverse plane the circles of the two daughter tracks must touch at the conversion point. + // The circles are computed once per track. Similar polar angles are already required by the pairing. + bool passesCircleTouch(const o2::math_utils::CircleXYf_t& posCircle, const o2::math_utils::CircleXYf_t& negCircle) const + { + float circleTouchDist = std::abs(std::hypot(posCircle.xC - negCircle.xC, posCircle.yC - negCircle.yC) - (posCircle.rC + negCircle.rC)); + return circleTouchDist <= photonMaxCircleTouchDist.value; + } + + static constexpr int LastDaughterCutStep = 4; + + // photon vertex fitted in one collision + struct PhotonFit { + float dca = 0.f; + std::array secVtx{}; + std::array pPos{}; + std::array pNeg{}; + float posTrackZ = 0.f; // z of the positive daughter at the fit, after moving it to the collision's time + }; + + // moves a track with only a TPC time to the time of the collision + template + bool moveToCollision(const TCollision& collision, const TTrack& track, o2::track::TrackParCov& trackParCov) + { + if (!(track.flags() & o2::aod::track::TrackTimeAsym)) { + return true; + } + return mVDriftMgr.moveTPCTrack(collision, track, trackParCov); + } + + // vertex fit of the photon daughters, in the collinear mode if a daughter has no ITS + bool fitPhoton(o2::track::TrackParCov posTrackParCov, o2::track::TrackParCov negTrackParCov, bool collinear, PhotonFit& fit) + { + posTrackParCov.setPID(o2::track::PID::Electron); + negTrackParCov.setPID(o2::track::PID::Electron); + + // photon fit settings, reset afterwards since the fitter is shared with the Sigma+ vertex fit + fitter.setMaxDXYIni(photonMaxDXYIni); + fitter.setCollinear(collinear); + int nCand = 0; + if (photonPrefitWithoutMatCorr) { + // Quick fit without material correction. Pairs that are clearly too far apart are rejected before the full fit. + fitter.setMatCorrType(o2::base::Propagator::MatCorrType::USEMatCorrNONE); + try { + nCand = fitter.process(posTrackParCov, negTrackParCov); + } catch (...) { + nCand = 0; + } + if (nCand == 0 || std::sqrt(fitter.getChi2AtPCACandidate()) > photonPrefitMaxDca) { + fitter.setMaxDXYIni(1e9); + fitter.setCollinear(false); + return false; + } + } + fitter.setMatCorrType(o2::base::Propagator::MatCorrType::USEMatCorrLUT); + try { + nCand = fitter.process(posTrackParCov, negTrackParCov); + } catch (...) { + nCand = 0; + } + fitter.setMatCorrType(o2::base::Propagator::MatCorrType::USEMatCorrNONE); + fitter.setMaxDXYIni(1e9); + fitter.setCollinear(false); + if (nCand == 0 || !fitter.propagateTracksToVertex()) { + return false; + } + + fit.dca = std::sqrt(fitter.getChi2AtPCACandidate()); + fit.secVtx = fitter.getPCACandidatePos(); + fitter.getTrack(0).getPxPyPzGlo(fit.pPos); + fitter.getTrack(1).getPxPyPzGlo(fit.pNeg); + fit.posTrackZ = posTrackParCov.getZ(); + return true; + } + + // a photon is kept in every compatible collision where it passes the cuts, and the proton of that collision then decides + template + std::vector>> findPhotonsSelfPaired(const TCollisions& collisions, const TTracks& tracks, aod::AmbiguousTracks const& ambiguousTracks, aod::BCsWithTimestamps const& bcs) + { + std::vector>> photonsByCollision(collisions.size()); + + svPhotonPoolCreator.clearPools(); + svPhotonPoolCreator.fillBC2Coll(collisions, bcs); + mElectronPool.clear(); + mPositronPool.clear(); + + // BC start and time of every collision, in ns relative to the BC of the first collision + std::vector collisionBcNS(collisions.size()); + std::vector collisionTimeNS(collisions.size()); + uint64_t firstGlobalBC = collisions.begin().template bc_as().globalBC(); + for (const auto& collision : collisions) { + int64_t bcDiff = static_cast(collision.template bc_as().globalBC()) - static_cast(firstGlobalBC); + collisionBcNS[collision.globalIndex()] = bcDiff * o2::constants::lhc::LHCBunchSpacingNS; + collisionTimeNS[collision.globalIndex()] = collisionBcNS[collision.globalIndex()] + collision.collisionTime(); + } + + // select the daughter tracks and find the collisions each is compatible with + std::vector thetaOfTrack(tracks.size(), 0.f); + std::vector mcPhotonOfTrack(tracks.size(), -1); // MC: Sigma+ photon the track comes from + std::vector mcSigmaOfTrack(tracks.size(), -1); // MC: Sigma+ the track comes from + std::vector circleOfTrack(tracks.size()); // xy circle of each selected daughter, for the pre-fit cut + for (const auto& track : tracks) { + int mcPhoton = -1; + int mcSigma = -1; + if constexpr (IsMC) { + if (track.has_mcParticle()) { + mcSigma = findSigmaPlusAncestorOfPhotonDaughter(track.template mcParticle_as(), mcPhoton); + } + } + if (!selectPhotonDaughter(track, mcSigma >= 0)) { + continue; + } + thetaOfTrack[track.globalIndex()] = 2.f * std::atan(std::exp(-track.eta())); + float sna = 0.f; + float csa = 0.f; + getTrackParCov(track).getCircleParams(mBz, circleOfTrack[track.globalIndex()], sna, csa); + mcPhotonOfTrack[track.globalIndex()] = mcPhoton; + mcSigmaOfTrack[track.globalIndex()] = mcSigma; + + if (track.flags() & o2::aod::track::TrackTimeAsym) { + // track with only a TPC time + if (!track.has_collision()) { + continue; + } + if (track.sign() < 0) { + mElectronPool.push_back(tpcOnlyTrackCand(track, collisionBcNS, collisionTimeNS)); + } else { + mPositronPool.push_back(tpcOnlyTrackCand(track, collisionBcNS, collisionTimeNS)); + } + } else { + // track with an ITS time (matched to collisions by svPoolCreator) + svPhotonPoolCreator.appendTrackCand(track, collisions, track.sign() < 0 ? PDG_t::kElectron : PDG_t::kPositron, ambiguousTracks, bcs); + } + } + + // svPoolCreator pools are ordered dau0 pos, dau0 neg, dau1 pos, dau1 neg: electrons are in 1, positrons are in 2 + auto timeMatchedPools = svPhotonPoolCreator.getTrackCandPool(); + mElectronPool.insert(mElectronPool.end(), timeMatchedPools[1].begin(), timeMatchedPools[1].end()); + mPositronPool.insert(mPositronPool.end(), timeMatchedPools[2].begin(), timeMatchedPools[2].end()); + + // MC: Sigma+ of a pair whose two tracks come from the same Sigma+ photon, -1 otherwise + auto sigmaOfPair = [&](int electronIdx, int positronIdx) { + if constexpr (IsMC) { + if (mcPhotonOfTrack[electronIdx] >= 0 && mcPhotonOfTrack[electronIdx] == mcPhotonOfTrack[positronIdx]) { + return mcSigmaOfTrack[electronIdx]; + } + } + return -1; + }; + uint64_t nTruePairs = 0; + uint64_t nPairsInWindow = 0; + auto acceptPair = [&](int electronIdx, int positronIdx) { + bool isSignal = sigmaOfPair(electronIdx, positronIdx) >= 0; + if (isSignal) { + nTruePairs++; + } + fillPhotonHist(HIST("hDeltaTheta"), isSignal, thetaOfTrack[positronIdx] - thetaOfTrack[electronIdx]); + return passesCircleTouch(circleOfTrack[positronIdx], circleOfTrack[electronIdx]); + }; + auto pairs = findPairsByPolarAngle(thetaOfTrack, collisions.size(), acceptPair, nPairsInWindow); + + // fit and select each pair in every good collision it is compatible with + for (const auto& pair : pairs) { + int electronIdx = pair.tr0Idx; + int positronIdx = pair.tr1Idx; + int sigmaId = sigmaOfPair(electronIdx, positronIdx); + bool isSignal = sigmaId >= 0; + + auto negTrack = tracks.rawIteratorAt(electronIdx); + auto posTrack = tracks.rawIteratorAt(positronIdx); + + // both daughters with only a TPC time on the same TPC side: moving to another collision shifts both by the same z, + // so the pair is fitted once and the vertex is only shifted in z for the other collisions + bool posTpcTime = posTrack.flags() & o2::aod::track::TrackTimeAsym; + bool negTpcTime = negTrack.flags() & o2::aod::track::TrackTimeAsym; + bool sameShiftForAllCollisions = posTpcTime && negTpcTime && ((posTrack.tgl() > 0.f) == (negTrack.tgl() > 0.f)); + bool collinearFit = !posTrack.hasITS() || !negTrack.hasITS(); + std::optional referenceFit; + + int maxStepReached = LastDaughterCutStep; // furthest selection step reached in any collision (for selection counter histograms) + auto passStep = [&](int step) { maxStepReached = std::max(maxStepReached, step); }; + bool anyFitOk = false; + float smallestDca = 1e9f; + bool openingAngleFilled = false; + int nCopies = 0; + + for (int collIdx = pair.collBracket.getMin(); collIdx <= pair.collBracket.getMax(); ++collIdx) { + if (!mGoodCollision[collIdx]) { + continue; + } + auto collision = collisions.rawIteratorAt(collIdx); + + PhotonFit fit; + if (sameShiftForAllCollisions && referenceFit) { + auto posTrackParCov = getTrackParCov(posTrack); + if (!moveToCollision(collision, posTrack, posTrackParCov)) { + continue; + } + fit = *referenceFit; + fit.secVtx[2] += posTrackParCov.getZ() - referenceFit->posTrackZ; + } else { + auto posTrackParCov = getTrackParCov(posTrack); + auto negTrackParCov = getTrackParCov(negTrack); + if (!moveToCollision(collision, posTrack, posTrackParCov) || !moveToCollision(collision, negTrack, negTrackParCov)) { + continue; + } + bool fitOk = fitPhoton(posTrackParCov, negTrackParCov, collinearFit, fit); + if (fitOk) { + anyFitOk = true; + smallestDca = std::min(smallestDca, fit.dca); + } + if (!fitOk || fit.dca > photonMaxDCAV0Dau) { + if (sameShiftForAllCollisions) { + break; // same result in every collision + } + continue; + } + if (sameShiftForAllCollisions) { + referenceFit = fit; + } + } + passStep(5); + + float radius = std::hypot(fit.secVtx[0], fit.secVtx[1]); + if (radius < photonMinRadius || radius > photonMaxRadius) { + continue; + } + passStep(6); + + float photonOpeningAngle = o2::aod::pwgem::dilepton::utils::pairutil::getOpeningAngle(fit.pPos[0], fit.pPos[1], fit.pPos[2], fit.pNeg[0], fit.pNeg[1], fit.pNeg[2]); + if (!openingAngleFilled) { // once per pair + openingAngleFilled = true; + fillPhotonHist(HIST("hOpeningAngle"), isSignal, photonOpeningAngle); + } + if (photonOpeningAngle > photonMaxOpeningAngle) { + continue; + } + passStep(8); // opening angle, and delta theta that is cut before the fit + + std::array pGamma{fit.pNeg[0] + fit.pPos[0], fit.pNeg[1] + fit.pPos[1], fit.pNeg[2] + fit.pPos[2]}; + std::array pv{collision.posX(), collision.posY(), collision.posZ()}; + float cosPA = RecoDecay::cpa(pv, fit.secVtx, pGamma); + if (cosPA < photonMinV0cospa || std::abs(photonDcaZ(fit.secVtx, pGamma, pv)) > candMaxPhotonDcaZToPV) { + continue; + } + passStep(9); + + float photonY = RecoDecay::y(pGamma, o2::constants::physics::MassGamma); + if (photonY < photonMinRapidity || photonY > photonMaxRapidity) { + continue; + } + passStep(10); + + float qtarm = v0Qt(fit.pPos[0], fit.pPos[1], fit.pPos[2], fit.pNeg[0], fit.pNeg[1], fit.pNeg[2]); + float alpha = v0Alpha(fit.pPos[0], fit.pPos[1], fit.pPos[2], fit.pNeg[0], fit.pNeg[1], fit.pNeg[2]); + if (qtarm > photonMaxQt) { + continue; + } + passStep(11); + + if (std::abs(alpha) > photonMaxAlpha) { + continue; + } + passStep(12); + + float mGamma = RecoDecay::m(std::array{fit.pPos, fit.pNeg}, std::array{o2::constants::physics::MassElectron, o2::constants::physics::MassElectron}); + if (mGamma > photonMaxMass) { + continue; + } + passStep(13); + + fillPhotonHist(HIST("hMass"), isSignal, mGamma); + fillPhotonHist(HIST("hPt"), isSignal, std::hypot(pGamma[0], pGamma[1])); + fillPhotonHist(HIST("hRadius"), isSignal, radius); + fillPhotonHist(HIST("h2ArmenterosPodolanski"), isSignal, alpha, qtarm); + fillPhotonHist(HIST("h2ConvPointXY"), isSignal, fit.secVtx[0], fit.secVtx[1]); + + nCopies++; + photonsByCollision[collIdx].push_back({fit.secVtx[0], fit.secVtx[1], fit.secVtx[2], pGamma[0], pGamma[1], pGamma[2], mGamma, alpha, qtarm, radius, fit.dca, cosPA, negTrack, posTrack}); + } + + if (anyFitOk) { + fillPhotonHist(HIST("hDcaV0Daughters"), isSignal, smallestDca); + } + if (nCopies > 0) { + fillPhotonHist(HIST("hCollisionsPerPhoton"), isSignal, nCopies); + fillSigmaCounter(sigmaId, 4); + } + for (int step = 5; step <= maxStepReached; ++step) { + fillPhotonHist(HIST("hSelectionCounter"), isSignal, step); + } + } + + // steps 0-4 are the daughter-track cuts applied before the pairing, so they count all pairs in the theta window + for (int step = 0; step <= LastDaughterCutStep; ++step) { + histos.fill(HIST("Photon/Inclusive/hSelectionCounter"), step, static_cast(nPairsInWindow)); + if constexpr (IsMC) { + histos.fill(HIST("Photon/True/hSelectionCounter"), step, static_cast(nTruePairs)); + } + } + + return photonsByCollision; + } + // proton candidate selection template bool selectProton(const TTrack& track) @@ -614,17 +1059,11 @@ struct Sigmaplusbuilder { bool isSignal = false; if constexpr (IsMC) { if (track.has_mcParticle()) { - auto mcProton = track.template mcParticle_as(); - isSignal = findSigmaPlusMotherOfProton(mcProton) >= 0; + isSignal = findSigmaPlusMotherOfProton(track.template mcParticle_as()) >= 0; } } auto fillProtonStep = [&](int step) { - histos.fill(HIST("Proton/hSelectionCounter"), step); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Proton/True/hSelectionCounter"), step); - } - } + fillProtonHist(HIST("hSelectionCounter"), isSignal, step); }; fillProtonStep(0); @@ -638,12 +1077,7 @@ struct Sigmaplusbuilder { } fillProtonStep(2); - histos.fill(HIST("Proton/h2TPCNClsVsPt"), track.pt(), track.tpcNClsFound()); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Proton/True/h2TPCNClsVsPt"), track.pt(), track.tpcNClsFound()); - } - } + fillProtonHist(HIST("h2TPCNClsVsPt"), isSignal, track.pt(), track.tpcNClsFound()); if (track.tpcNClsFound() < protonMinTpcNCls) { return false; } @@ -654,8 +1088,6 @@ struct Sigmaplusbuilder { } fillProtonStep(4); - // A track with no TOF hit at all is tolerated even above protonPtMinRequireTOF - only an existing-but-failing - // TOF nSigma gets rejected. Set protonRequireTofHit=true to instead mandate a TOF hit above the threshold. if (track.pt() > protonPtMinRequireTOF) { if (track.hasTOF()) { if (std::abs(track.tofNSigmaPr()) > protonMaxTOFNSigma) { @@ -667,37 +1099,21 @@ struct Sigmaplusbuilder { } fillProtonStep(5); - // proton DCA to PV - histos.fill(HIST("Proton/h2DcaToPVVsPt"), track.pt(), std::abs(track.dcaXY())); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Proton/True/h2DcaToPVVsPt"), track.pt(), std::abs(track.dcaXY())); - } - } - if (std::abs(track.dcaXY()) < protonMinDcaToPV || std::abs(track.dcaXY()) > protonMaxDcaToPV) { + fillProtonHist(HIST("h2DcaToPVVsPt"), isSignal, track.pt(), std::abs(track.dcaXY())); + if (std::abs(track.dcaXY()) < protonMinDcaToPV || std::abs(track.dcaXY()) > protonMaxDcaToPV || std::abs(track.dcaZ()) > protonMaxDcaZToPV) { return false; } fillProtonStep(6); - histos.fill(HIST("Proton/hPt"), track.pt()); - histos.fill(HIST("Proton/h2TPCNSigmaVsPt"), track.pt(), track.tpcNSigmaPr()); + fillProtonHist(HIST("hPt"), isSignal, track.pt()); + fillProtonHist(HIST("h2TPCNSigmaVsPt"), isSignal, track.pt(), track.tpcNSigmaPr()); if (track.hasTOF()) { - histos.fill(HIST("Proton/h2TOFNSigmaVsPt"), track.pt(), track.tofNSigmaPr()); - } - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Proton/True/hPt"), track.pt()); - histos.fill(HIST("Proton/True/h2TPCNSigmaVsPt"), track.pt(), track.tpcNSigmaPr()); - if (track.hasTOF()) { - histos.fill(HIST("Proton/True/h2TOFNSigmaVsPt"), track.pt(), track.tofNSigmaPr()); - } - } + fillProtonHist(HIST("h2TOFNSigmaVsPt"), isSignal, track.pt(), track.tofNSigmaPr()); } return true; } - // small vector helpers used in the Sigma+ reconstruction static float dot3(const std::array& a, const std::array& b) { return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; @@ -726,31 +1142,38 @@ struct Sigmaplusbuilder { return mothers.front().globalIndex(); } + // MC: for an e+ or e- from Sigma+ -> pi0 -> photon -> e+e-, returns the Sigma+ index and sets photonIndex (-1 otherwise) template - int findSigmaPlusMotherOfPhoton(const TMcPart& mcPos, const TMcPart& mcNeg) + int findSigmaPlusAncestorOfPhotonDaughter(const TMcPart& mcDaughter, int& photonIndex) { - auto const& posMothers = mcPos.template mothers_as(); - auto const& negMothers = mcNeg.template mothers_as(); - if (posMothers.empty() || negMothers.empty()) { - return -1; - } - auto mcGamma = posMothers.front(); - if (mcGamma.globalIndex() != negMothers.front().globalIndex() || mcGamma.pdgCode() != PDG_t::kGamma) { + auto const& mothers = mcDaughter.template mothers_as(); + if (mothers.empty() || mothers.front().pdgCode() != PDG_t::kGamma) { return -1; } - - auto const& pi0Mothers = mcGamma.template mothers_as(); + auto const& pi0Mothers = mothers.front().template mothers_as(); if (pi0Mothers.empty() || std::abs(pi0Mothers.front().pdgCode()) != PDG_t::kPi0) { return -1; } - auto const& sigmaMothers = pi0Mothers.front().template mothers_as(); if (sigmaMothers.empty() || std::abs(sigmaMothers.front().pdgCode()) != PDG_t::kSigmaPlus) { return -1; } + photonIndex = mothers.front().globalIndex(); return sigmaMothers.front().globalIndex(); } + template + int findSigmaPlusMotherOfPhoton(const TMcPart& mcPos, const TMcPart& mcNeg) + { + int posPhoton = -1; + int negPhoton = -1; + int sigmaId = findSigmaPlusAncestorOfPhotonDaughter(mcPos, posPhoton); + if (sigmaId < 0 || findSigmaPlusAncestorOfPhotonDaughter(mcNeg, negPhoton) < 0 || posPhoton != negPhoton) { + return -1; + } + return sigmaId; + } + template bool isSigmaPlusToProtonPi0(const TMcPart& mcPart) { @@ -823,21 +1246,23 @@ struct Sigmaplusbuilder { } // Build a Sigma+ -> p pi0 candidate from a proton track and a PCM photon - // Returns the MC index of the matched true Sigma+ mother if a signal candidate was built, -1 otherwise template - int buildSigmaPlusCandidate(const TTrack& protonTrack, const PhotonCand& photon, const std::array& pv) + void buildSigmaPlusCandidate(const TTrack& protonTrack, const PhotonCand& photon, int collisionIndex, const std::array& pv) { auto posTrack = photon.posTrack; auto negTrack = photon.negTrack; + int photonLegsWithoutIts = (posTrack.hasITS() ? 0 : 1) + (negTrack.hasITS() ? 0 : 1); bool isSignal = false; - bool collisionIdCheck = false; // MC only: true if the proton's true MC collision matches the reconstructed collision - std::array mcTrueVtx{}; // Sigma+ decay vertex - std::array mcTrueMomProton{}; // true MC proton momentum - std::array mcTrueMomGamma{}; // true MC momentum of the measured photon - std::array mcTrueMomSigmaPlus{}; // true MC momentum of the Sigma+ mother - float decayRadiusMC = -999.f; // MC-truth decay radius - float massMC = -999.f; // MC-truth Sigma+ mass + bool protonIsSignal = false; + bool photonIsSignal = false; + bool collisionIdCheck = false; // MC only: true if the proton's MC collision matches the reconstructed collision + std::array mcTrueVtx{}; + std::array mcTrueMomProton{}; + std::array mcTrueMomGamma{}; + std::array mcTrueMomSigmaPlus{}; + float decayRadiusMC = -999.f; + float massMC = -999.f; int matchedSigmaId = -1; if constexpr (IsMC) { if (protonTrack.has_mcParticle()) { @@ -847,9 +1272,9 @@ struct Sigmaplusbuilder { if (protonCollision.has_mcCollision()) { collisionIdCheck = protonCollision.mcCollision().globalIndex() == mcProton.mcCollisionId(); } - auto const& protonMothers = mcProton.template mothers_as(); int protonSigmaIdx = findSigmaPlusMotherOfProton(mcProton); + protonIsSignal = protonSigmaIdx >= 0; if (posTrack.has_mcParticle() && negTrack.has_mcParticle()) { auto mcPos = posTrack.template mcParticle_as(); @@ -862,13 +1287,14 @@ struct Sigmaplusbuilder { } int photonSigmaIdx = findSigmaPlusMotherOfPhoton(mcPos, mcNeg); - isSignal = (protonSigmaIdx >= 0 && photonSigmaIdx >= 0 && photonSigmaIdx == protonSigmaIdx); + photonIsSignal = photonSigmaIdx >= 0; + isSignal = protonIsSignal && photonIsSignal && photonSigmaIdx == protonSigmaIdx; } if (isSignal) { + auto mcSigmaPlusMother = mcProton.template mothers_as().front(); mcTrueVtx = {mcProton.vx(), mcProton.vy(), mcProton.vz()}; mcTrueMomProton = {mcProton.px(), mcProton.py(), mcProton.pz()}; - auto mcSigmaPlusMother = protonMothers.front(); mcTrueMomSigmaPlus = {mcSigmaPlusMother.px(), mcSigmaPlusMother.py(), mcSigmaPlusMother.pz()}; matchedSigmaId = mcSigmaPlusMother.globalIndex(); decayRadiusMC = std::hypot(mcTrueVtx[0] - mcSigmaPlusMother.vx(), mcTrueVtx[1] - mcSigmaPlusMother.vy()); @@ -878,18 +1304,14 @@ struct Sigmaplusbuilder { } auto fillCandStep = [&](int step) { - histos.fill(HIST("Candidate/hSelectionCounter"), step); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Candidate/True/hSelectionCounter"), step); - } - } + fillCandHist(HIST("hSelectionCounter"), isSignal, step); + fillCandHist(HIST("h2SelectionCounterVsLegType"), isSignal, step, photonLegsWithoutIts); }; fillCandStep(0); // all pairs - // Reject the pair if the proton track is one of the photon's own e+/e- daughter tracks. + // reject the pair if the proton track is one of the photon's own daughter tracks if (protonTrack.globalIndex() == posTrack.globalIndex() || protonTrack.globalIndex() == negTrack.globalIndex()) { - return -1; + return; } fillCandStep(1); // autocorrelation @@ -906,54 +1328,43 @@ struct Sigmaplusbuilder { try { nCand = fitter.process(protonTrackParCov, photonTrackParCov); } catch (...) { - return -1; + return; } if (nCand == 0 || !fitter.propagateTracksToVertex()) { - return -1; + return; } - fillCandStep(2); // Vertex fit + fillCandStep(2); // vertex fit + std::array protonAtPca{}; + std::array photonAtPca{}; + fitter.getTrack(0).getXYZGlo(protonAtPca); + fitter.getTrack(1).getXYZGlo(photonAtPca); - float fitChi2 = fitter.getChi2AtPCACandidate(); - float dcaProtonGamma = std::sqrt(fitChi2); - histos.fill(HIST("Candidate/hDcaProtonGamma"), dcaProtonGamma); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Candidate/True/hDcaProtonGamma"), dcaProtonGamma); - } - } + float dcaProtonGamma = std::sqrt(fitter.getChi2AtPCACandidate()); + fillCandHist(HIST("hDcaProtonGamma"), isSignal, dcaProtonGamma); if (dcaProtonGamma > candMaxDcaProtonGamma) { - return -1; + return; } fillCandStep(3); // DCA(p,gamma) - std::array secVtx = fitter.getPCACandidatePos(); - float radius = std::hypot(secVtx[0], secVtx[1]); - histos.fill(HIST("Candidate/hRadius"), radius); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Candidate/True/hRadius"), radius); - float vtxResFromMc = std::hypot(secVtx[0] - mcTrueVtx[0], secVtx[1] - mcTrueVtx[1], secVtx[2] - mcTrueVtx[2]); - histos.fill(HIST("Candidate/True/hVertexResFromMcTruth"), vtxResFromMc); - } + // decay vertex between the proton's and the photon's point of closest approach (relative weight given by candVertexProtonWeight) + std::array secVtx{}; + for (size_t i = 0; i < secVtx.size(); ++i) { + secVtx[i] = candVertexProtonWeight * protonAtPca[i] + (1.f - candVertexProtonWeight) * photonAtPca[i]; } + float radius = std::hypot(secVtx[0], secVtx[1]); + fillCandHist(HIST("hRadius"), isSignal, radius); if (radius < candMinRadius || radius > candMaxRadius) { - return -1; + return; } fillCandStep(4); // radius - // flight direction n and the decay-plane basis n, eIn, eOut std::array flightVec{secVtx[0] - pv[0], secVtx[1] - pv[1], secVtx[2] - pv[2]}; - std::array nHat = normalize3(flightVec); + std::array nHatOrig = normalize3(flightVec); float flightDistance = std::sqrt(dot3(flightVec, flightVec)); - histos.fill(HIST("Candidate/hFlightDistance"), flightDistance); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Candidate/True/hFlightDistance"), flightDistance); - } - } + fillCandHist(HIST("hFlightDistance"), isSignal, flightDistance); if (flightDistance < candMinFlightDistance || flightDistance > candMaxFlightDistance) { - return -1; + return; } fillCandStep(5); // flight distance @@ -962,110 +1373,93 @@ struct Sigmaplusbuilder { fitter.getTrack(0).getPxPyPzGlo(pProton); fitter.getTrack(1).getPxPyPzGlo(pGamma1); - if constexpr (IsMC) { - if (isSignal) { - float trueProtonP = std::sqrt(dot3(mcTrueMomProton, mcTrueMomProton)); - float fitProtonP = std::sqrt(dot3(pProton, pProton)); - histos.fill(HIST("Candidate/True/hProtonMomResFromMcTruth"), (fitProtonP - trueProtonP) / trueProtonP); - - float trueGammaP = std::sqrt(dot3(mcTrueMomGamma, mcTrueMomGamma)); - float fitGammaP = std::sqrt(dot3(pGamma1, pGamma1)); - histos.fill(HIST("Candidate/True/hPhotonMomResFromMcTruth"), (fitGammaP - trueGammaP) / trueGammaP); - } - } - - float e1 = std::sqrt(dot3(pGamma1, pGamma1)); // |p_gamma1| + float e1 = std::sqrt(dot3(pGamma1, pGamma1)); float massPi0 = o2::constants::physics::MassPionNeutral; + constexpr float CoefADegenerateThreshold = 1e-6f; + std::array nHat{}; std::array eOut{}; std::array eIn{}; - float coefA = 0.f, coefB = 0.f, coefK = 0.f, coefL = 0.f; + float coefA = 0.f, coefB = 0.f; float pGamma2In = 0.f, pGamma2Out = 0.f, tPerp2 = 0.f; - float discriminant = -1.f; - std::array nHatOrig = nHat; // each retry perturbs around this - constexpr float CoefADegenerateThreshold = 1e-6f; // guards against dividing by a near-zero quadratic coefficient - - if constexpr (IsMC) { - if (isSignal) { - float cosProtonFlight = dot3(pProton, nHatOrig) / std::sqrt(dot3(pProton, pProton)); - float protonFlightAngle = std::acos(std::clamp(cosProtonFlight, -1.f, 1.f)); - histos.fill(HIST("Candidate/True/hProtonFlightAngle"), protonFlightAngle); - } - } - - int discrIter = 0; - for (; discrIter <= discrRetryMaxIter; ++discrIter) { - if (discrIter > 0) { - TVector3 nHatVec(nHatOrig[0], nHatOrig[1], nHatOrig[2]); - float dTheta = mThetaResoFunc.GetRandom(); - float dPhi = mPhiResoFunc.GetRandom(); - nHatVec.SetMagThetaPhi(1., nHatVec.Theta() + dTheta, nHatVec.Phi() + dPhi); - nHat = {static_cast(nHatVec.X()), static_cast(nHatVec.Y()), static_cast(nHatVec.Z())}; - } + auto solveForFlightDir = [&](const std::array& nHatUse) { + nHat = nHatUse; eOut = normalize3(cross3(pProton, nHat)); // normal to the decay plane - eIn = cross3(eOut, nHat); // in-plane, transverse to n + eIn = cross3(eOut, nHat); // in the decay plane, transverse to n - float pProtonIn = dot3(pProton, eIn); // proton has no out-of-plane component, by construction + float pProtonIn = dot3(pProton, eIn); float pGamma1Long = dot3(pGamma1, nHat); float pGamma1In = dot3(pGamma1, eIn); float pGamma1Out = dot3(pGamma1, eOut); - // in-plane transverse momentum cancels between p, gamma1, gamma2 pGamma2In = -(pProtonIn + pGamma1In); - // out-of-plane momentum cancels between gamma1 and gamma2 alone pGamma2Out = -pGamma1Out; tPerp2 = pGamma2In * pGamma2In + pGamma2Out * pGamma2Out; - // m_pi0^2 = 2*(|p_gamma1||p_gamma2| - p_gamma1.p_gamma2), solved for - // the remaining unknown (p_gamma2 along n) -> quadratic coefA*x^2+coefB*x+coefC=0 - float dot1 = pGamma1In * pGamma2In + pGamma1Out * pGamma2Out; - coefK = massPi0 * massPi0 + 2.f * dot1; - coefL = 2.f * pGamma1Long; - + // m_pi0^2 = 2*(|p_gamma1||p_gamma2| - p_gamma1.p_gamma2) as coefA*x^2 + coefB*x + coefC = 0 + float coefK = massPi0 * massPi0 + 2.f * (pGamma1In * pGamma2In + pGamma1Out * pGamma2Out); + float coefL = 2.f * pGamma1Long; coefA = 4.f * e1 * e1 - coefL * coefL; if (std::abs(coefA) < CoefADegenerateThreshold) { - continue; + return -1.f; } coefB = -2.f * coefK * coefL; float coefC = 4.f * e1 * e1 * tPerp2 - coefK * coefK; + return coefB * coefB - 4.f * coefA * coefC; + }; - discriminant = coefB * coefB - 4.f * coefA * coefC; - if (discriminant >= 0.f) { - break; + // if the measured flight direction gives no real solution, flight directions on rings of increasing + // tilt around it are tried (the first ring with a real solution is taken, and on it the direction with the largest discriminant) + float tiltAngle = 0.f; + float discriminant = solveForFlightDir(nHatOrig); + if (discriminant < 0.f) { + constexpr float MaxAbsNzForZHelperAxis = 0.9f; + std::array helperAxis = std::abs(nHatOrig[2]) < MaxAbsNzForZHelperAxis ? std::array{0.f, 0.f, 1.f} : std::array{1.f, 0.f, 0.f}; + std::array tiltAxisU = normalize3(cross3(nHatOrig, helperAxis)); + std::array tiltAxisV = cross3(nHatOrig, tiltAxisU); + int nRings = static_cast(std::lround(candMaxTilt / candTiltStep)); + for (int ring = 1; ring <= nRings && discriminant < 0.f; ++ring) { + float tilt = ring * candTiltStep; + float bestRingDisc = -1.f; + std::array bestRingDir{}; + for (int iAzimuth = 0; iAzimuth < candTiltNAzimuth; ++iAzimuth) { + float azimuth = o2::constants::math::TwoPI * iAzimuth / candTiltNAzimuth; + std::array tiltDir{}; + for (size_t i = 0; i < tiltDir.size(); ++i) { + tiltDir[i] = std::cos(tilt) * nHatOrig[i] + std::sin(tilt) * (std::cos(azimuth) * tiltAxisU[i] + std::sin(azimuth) * tiltAxisV[i]); + } + float disc = solveForFlightDir(tiltDir); + if (disc >= 0.f && disc > bestRingDisc) { + bestRingDisc = disc; + bestRingDir = tiltDir; + } + } + if (bestRingDisc >= 0.f) { + discriminant = solveForFlightDir(bestRingDir); + tiltAngle = tilt; + } } } - histos.fill(HIST("Candidate/hDiscriminantRetryIter"), discrIter); - histos.fill(HIST("Candidate/hDiscriminant"), discriminant); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Candidate/True/hDiscriminantRetryIter"), discrIter); - histos.fill(HIST("Candidate/True/hDiscriminant"), discriminant); - } - } + float tiltForHist = discriminant >= 0.f ? tiltAngle : 0.4999f; // no real solution + fillCandHist(HIST("hTiltAngle"), isSignal, tiltForHist); if (discriminant < 0.f) { - return -1; + return; } fillCandStep(6); // real root - // two roots from the quadratic, among both we keep the mass closest to the nominal Sigma+ mass + // of the two roots, keep the one giving the mass closest to the nominal Sigma+ mass float sqrtDisc = std::sqrt(discriminant); std::array roots{(-coefB + sqrtDisc) / (2.f * coefA), (-coefB - sqrtDisc) / (2.f * coefA)}; - // perturbation-free center of the two roots float rootCenter = -coefB / (2.f * coefA); - histos.fill(HIST("Candidate/hRootCenter"), rootCenter); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Candidate/True/hRootCenter"), rootCenter); - } - } + fillCandHist(HIST("hRootCenter"), isSignal, rootCenter); if (candRejectNegRootCenter && rootCenter < 0.f) { - return -1; + return; } if (rootCenter > candMaxRootCenter) { - return -1; + return; } bool haveCandidate = false; @@ -1092,31 +1486,26 @@ struct Sigmaplusbuilder { } } if (!haveCandidate) { - return -1; + return; } fillCandStep(7); // valid root std::array pSigma{pProton[0] + pGamma1[0] + bestMomGamma2[0], pProton[1] + pGamma1[1] + bestMomGamma2[1], pProton[2] + pGamma1[2] + bestMomGamma2[2]}; float ptSigma = std::hypot(pSigma[0], pSigma[1]); - // candidate DCA to PV o2::track::TrackPar sigmaTrackPar({secVtx[0], secVtx[1], secVtx[2]}, {pSigma[0], pSigma[1], pSigma[2]}, protonTrack.sign(), true); std::array dcaSigmaToPv{}; o2::base::Propagator::Instance()->propagateToDCA(o2::math_utils::Point3D{pv[0], pv[1], pv[2]}, sigmaTrackPar, mBz, 2.f, o2::base::Propagator::MatCorrType::USEMatCorrNONE, &dcaSigmaToPv); float candDcaToPV = std::abs(dcaSigmaToPv[0]); - histos.fill(HIST("Candidate/hDcaToPV"), candDcaToPV); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Candidate/True/hDcaToPV"), candDcaToPV); - } - } + fillCandHist(HIST("hDcaToPV"), isSignal, candDcaToPV); if (candDcaToPV > candMaxDcaToPV) { - return -1; + return; } fillCandStep(8); // DCA to PV - // AntiSigmaPointingAngle - Fake pointing angle + // angle between the proton momentum and the flight direction, both rotated back to undo the bending + // of the proton between its reference point and the decay vertex std::array protonPath{secVtx[0] - protonOrigPos[0], secVtx[1] - protonOrigPos[1], secVtx[2] - protonOrigPos[2]}; float protonPathLength = std::sqrt(dot3(protonPath, protonPath)); float svRadiusFromPv = std::hypot(secVtx[0] - pv[0], secVtx[1] - pv[1]); @@ -1138,309 +1527,343 @@ struct Sigmaplusbuilder { if (flightDistance / (2.f * rCurve) < 1.f) { alphaVtxRot = -propDir * qProton * bzSign * std::asin(flightDistance / (2.f * rCurve)); } - std::array sigmaVertexVec{secVtx[0] - pv[0], secVtx[1] - pv[1], secVtx[2] - pv[2]}; std::array sigmaVertexVecRot{ - sigmaVertexVec[0] * std::cos(alphaVtxRot) - sigmaVertexVec[1] * std::sin(alphaVtxRot), - sigmaVertexVec[0] * std::sin(alphaVtxRot) + sigmaVertexVec[1] * std::cos(alphaVtxRot), - sigmaVertexVec[2]}; + flightVec[0] * std::cos(alphaVtxRot) - flightVec[1] * std::sin(alphaVtxRot), + flightVec[0] * std::sin(alphaVtxRot) + flightVec[1] * std::cos(alphaVtxRot), + flightVec[2]}; float antiSigmaPointingAngle = std::acos(std::clamp(dot3(pProtonRot, sigmaVertexVecRot) / std::sqrt(dot3(pProtonRot, pProtonRot) * dot3(sigmaVertexVecRot, sigmaVertexVecRot)), -1.f, 1.f)); - histos.fill(HIST("Candidate/hAntiSigmaPointingAngle"), antiSigmaPointingAngle); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Candidate/True/hAntiSigmaPointingAngle"), antiSigmaPointingAngle); - } - } - // window cut + fillCandHist(HIST("hAntiSigmaPointingAngle"), isSignal, antiSigmaPointingAngle); if (antiSigmaPointingAngle < candMinAntiSigmaPointingAngle || antiSigmaPointingAngle > candMaxAntiSigmaPointingAngle) { - return -1; - } - - histos.fill(HIST("Candidate/hMassSigmaPlus"), bestMass); - histos.fill(HIST("Candidate/h2MassVsPt"), ptSigma, bestMass); - histos.fill(HIST("Candidate/h2MassVsRootCenter"), rootCenter, bestMass); - histos.fill(HIST("Candidate/h2MassVsAntiSigmaPointingAngle"), antiSigmaPointingAngle, bestMass); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Candidate/True/hMassSigmaPlus"), bestMass); - histos.fill(HIST("Candidate/True/h2MassVsPt"), ptSigma, bestMass); - histos.fill(HIST("Candidate/True/h2MassVsAntiSigmaPointingAngle"), antiSigmaPointingAngle, bestMass); - histos.fill(HIST("Candidate/True/h2MassVsRootCenter"), rootCenter, bestMass); - } + return; } + fillCandHist(HIST("hMassSigmaPlus"), isSignal, bestMass); + fillCandHist(HIST("h2MassVsPt"), isSignal, ptSigma, bestMass); + fillCandHist(HIST("h2MassVsTilt"), isSignal, tiltAngle, bestMass); if (bestMass > candMaxSigmaMass) { - return -1; + return; } fillCandStep(9); // mass float candRapidity = RecoDecay::y(pSigma, o2::constants::physics::MassSigmaPlus); - histos.fill(HIST("Candidate/hRapidity"), candRapidity); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Candidate/True/hRapidity"), candRapidity); - } - } + fillCandHist(HIST("hRapidity"), isSignal, candRapidity); if (std::abs(candRapidity) > candMaxRapidity) { - return -1; + return; } fillCandStep(10); // rapidity - // photon (V0) opening angle, recomputed here from the daughters' own raw track momenta - std::array pPosDau{posTrack.px(), posTrack.py(), posTrack.pz()}; - std::array pNegDau{negTrack.px(), negTrack.py(), negTrack.pz()}; - float photonOpeningAngle = std::acos(std::clamp(dot3(pPosDau, pNegDau) / std::sqrt(dot3(pPosDau, pPosDau) * dot3(pNegDau, pNegDau)), -1.f, 1.f)); - histos.fill(HIST("Candidate/hPhotonOpeningAngle"), photonOpeningAngle); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Candidate/True/hPhotonOpeningAngle"), photonOpeningAngle); - } - } + // photon opening angle from the daughters' track momenta + float photonOpeningAngle = o2::aod::pwgem::dilepton::utils::pairutil::getOpeningAngle(posTrack.px(), posTrack.py(), posTrack.pz(), negTrack.px(), negTrack.py(), negTrack.pz()); + fillCandHist(HIST("hPhotonOpeningAngle"), isSignal, photonOpeningAngle); if (photonOpeningAngle > candMaxPhotonOpeningAngle) { - return -1; + return; } fillCandStep(11); // photon opening angle - // photon pointing angle + // angle between the photon momentum and the line from the decay vertex to the conversion point std::array decVtxToConv{photon.x - secVtx[0], photon.y - secVtx[1], photon.z - secVtx[2]}; float photonPointingAngle = std::acos(std::clamp(dot3(pGamma1, decVtxToConv) / std::sqrt(dot3(pGamma1, pGamma1) * dot3(decVtxToConv, decVtxToConv)), -1.f, 1.f)); - histos.fill(HIST("Candidate/hPhotonPointingAngle"), photonPointingAngle); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Candidate/True/hPhotonPointingAngle"), photonPointingAngle); - } - } + fillCandHist(HIST("hPhotonPointingAngle"), isSignal, photonPointingAngle); if (photonPointingAngle > candMaxPhotonPointingAngle) { - return -1; + return; } fillCandStep(12); // photon pointing angle - // photon DCA to PV - std::array convPoint{photon.x, photon.y, photon.z}; std::array photonDir = normalize3({photon.px, photon.py, photon.pz}); - std::array pvToConv{pv[0] - convPoint[0], pv[1] - convPoint[1], pv[2] - convPoint[2]}; + std::array pvToConv{pv[0] - photon.x, pv[1] - photon.y, pv[2] - photon.z}; std::array pvToConvCrossDir = cross3(pvToConv, photonDir); float photonDcaToPV = std::sqrt(dot3(pvToConvCrossDir, pvToConvCrossDir)); - histos.fill(HIST("Candidate/hPhotonDcaToPV"), photonDcaToPV); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Candidate/True/hPhotonDcaToPV"), photonDcaToPV); - } - } - if (photonDcaToPV > candMaxPhotonDcaToPV) { - return -1; + fillCandHist(HIST("hPhotonDcaToPV"), isSignal, photonDcaToPV); + float photonPt2 = photon.px * photon.px + photon.py * photon.py; + float tAtDcaXY = photonPt2 > 0.f ? ((pv[0] - photon.x) * photon.px + (pv[1] - photon.y) * photon.py) / photonPt2 : 0.f; + float photonDcaXYToPV = std::hypot(photon.x + tAtDcaXY * photon.px - pv[0], photon.y + tAtDcaXY * photon.py - pv[1]); + float photonDcaZToPV = photon.z + tAtDcaXY * photon.pz - pv[2]; + if (photonDcaToPV > candMaxPhotonDcaToPV || std::abs(photonDcaZToPV) > candMaxPhotonDcaZToPV) { + return; } fillCandStep(13); // photon DCA to PV - fillCandStep(14); // filled + float psiPair = photonPsiPair(posTrack, negTrack, photon.radius); + + SigmaPlusCandidate cand; + cand.photonId = photonId(posTrack.globalIndex(), negTrack.globalIndex()); + cand.collisionId = collisionIndex; + cand.protonTrackId = protonTrack.globalIndex(); + cand.photonPosTrackId = posTrack.globalIndex(); + cand.photonNegTrackId = negTrack.globalIndex(); + cand.isSignal = isSignal; + cand.photonLegsWithoutIts = photonLegsWithoutIts; + cand.matchedSigmaId = matchedSigmaId; + cand.decVtx = secVtx; + cand.radius = radius; + cand.flightDistance = flightDistance; + cand.dcaProtonGamma = dcaProtonGamma; + cand.dcaToPV = candDcaToPV; + cand.tiltAngle = tiltAngle; + cand.rootCenter = rootCenter; + cand.antiSigmaPointingAngle = antiSigmaPointingAngle; + cand.pProton = pProton; + cand.pGamma1 = pGamma1; + cand.pGamma2 = bestMomGamma2; + cand.protonTpcNSigma = protonTrack.tpcNSigmaPr(); + cand.protonTofNSigma = protonTrack.tofNSigmaPr(); + cand.protonSign = protonTrack.sign(); + cand.protonItsNCls = protonTrack.itsNCls(); + cand.protonTpcNCls = protonTrack.tpcNClsFound(); + cand.protonDcaXY = protonTrack.dcaXY(); + cand.protonDcaZ = protonTrack.dcaZ(); + cand.photonMass = photon.mGamma; + cand.photonAlpha = photon.alpha; + cand.photonQt = photon.qtarm; + cand.photonRadius = photon.radius; + cand.photonOpeningAngle = photonOpeningAngle; + cand.photonPointingAngle = photonPointingAngle; + cand.photonDcaDau = photon.dcaDau; + cand.photonCosPAToPV = photon.cosPAToPV; + cand.photonDcaXYToPV = photonDcaXYToPV; + cand.photonDcaZToPV = photonDcaZToPV; + cand.photonPsiPair = psiPair; + cand.posTpcNSigmaEl = posTrack.tpcNSigmaEl(); + cand.negTpcNSigmaEl = negTrack.tpcNSigmaEl(); + cand.posItsNCls = posTrack.itsNCls(); + cand.negItsNCls = negTrack.itsNCls(); + cand.posTpcNCls = posTrack.tpcNClsFound(); + cand.negTpcNCls = negTrack.tpcNClsFound(); + cand.posTpcNClsFindable = posTrack.tpcNClsFindable(); + cand.negTpcNClsFindable = negTrack.tpcNClsFindable(); + cand.posTpcChi2NCl = posTrack.tpcChi2NCl(); + cand.negTpcChi2NCl = negTrack.tpcChi2NCl(); + cand.posDcaXY = posTrack.dcaXY(); + cand.posDcaZ = posTrack.dcaZ(); + cand.negDcaXY = negTrack.dcaXY(); + cand.negDcaZ = negTrack.dcaZ(); + cand.collisionIdCheck = collisionIdCheck; + cand.protonIsSignal = protonIsSignal; + cand.photonIsSignal = photonIsSignal; + cand.decVtxMC = mcTrueVtx; + cand.pProtonMC = mcTrueMomProton; + cand.pGammaMC = mcTrueMomGamma; + cand.pSigmaPlusMC = mcTrueMomSigmaPlus; + cand.decayRadiusMC = decayRadiusMC; + cand.massMC = massMC; + mCandidatesOfTimeframe.push_back(cand); + } + // BDT score + float computeMlScore(const SigmaPlusCandidate& cand) + { + constexpr float NoTofNSigmaThreshold = -900.f; // tofNSigma is -999 without TOF, NaN for the BDT + std::vector inputs{std::abs(cand.photonDcaZToPV), std::abs(cand.protonDcaXY), std::abs(cand.protonDcaZ), cand.dcaToPV, + cand.protonTpcNSigma, cand.protonTofNSigma < NoTofNSigmaThreshold ? std::numeric_limits::quiet_NaN() : cand.protonTofNSigma, + cand.antiSigmaPointingAngle, cand.dcaProtonGamma, cand.photonDcaXYToPV, cand.photonCosPAToPV, cand.tiltAngle}; + return mlResponse.getModelOutput(inputs, 0)[1]; + } + + template + void fillCandidateTables(const SigmaPlusCandidate& cand) + { + const float mlScore = applyMl ? computeMlScore(cand) : -1.f; if constexpr (IsMC) { if (fillSlimTables) { - slimSigmaPlusCandsMC(radius, - candDcaToPV, dcaProtonGamma, - protonTrack.sign(), - protonTrack.dcaXY(), protonTrack.dcaZ(), - pProton[0], pProton[1], pProton[2], - pGamma1[0], pGamma1[1], pGamma1[2], - bestMomGamma2[0], bestMomGamma2[1], bestMomGamma2[2], - protonTrack.tpcNSigmaPr(), protonTrack.tofNSigmaPr(), - posTrack.tpcNSigmaEl(), negTrack.tpcNSigmaEl(), - photon.mGamma, - collisionIdCheck, - isSignal, - decayRadiusMC, massMC, - mcTrueMomSigmaPlus[0], mcTrueMomSigmaPlus[1], mcTrueMomSigmaPlus[2]); - return matchedSigmaId; + slimSigmaPlusCandsMC(cand.collisionId, cand.protonTrackId, cand.photonPosTrackId, cand.photonNegTrackId, + cand.matchedSigmaId, + cand.radius, + cand.dcaToPV, cand.tiltAngle, cand.dcaProtonGamma, + cand.protonSign, + cand.protonDcaXY, cand.protonDcaZ, + cand.pProton[0], cand.pProton[1], cand.pProton[2], + cand.pGamma1[0], cand.pGamma1[1], cand.pGamma1[2], + cand.pGamma2[0], cand.pGamma2[1], cand.pGamma2[2], + cand.protonTpcNSigma, cand.protonTofNSigma, + cand.posTpcNSigmaEl, cand.negTpcNSigmaEl, + cand.photonMass, + cand.photonDcaDau, cand.photonCosPAToPV, cand.photonDcaXYToPV, cand.photonDcaZToPV, cand.photonPsiPair, + cand.posDcaXY, cand.posDcaZ, cand.negDcaXY, cand.negDcaZ, + cand.posTpcNClsFindable, cand.negTpcNClsFindable, cand.posTpcChi2NCl, cand.negTpcChi2NCl, + mlScore, + cand.collisionIdCheck, + cand.isSignal, cand.protonIsSignal, cand.photonIsSignal, + cand.decayRadiusMC, cand.massMC, + cand.pSigmaPlusMC[0], cand.pSigmaPlusMC[1], cand.pSigmaPlusMC[2]); + return; } - sigmaPlusCandsMC(secVtx[0], secVtx[1], secVtx[2], - flightDistance, dcaProtonGamma, - pProton[0], pProton[1], pProton[2], - pGamma1[0], pGamma1[1], pGamma1[2], - bestMomGamma2[0], bestMomGamma2[1], bestMomGamma2[2], - protonTrack.tpcNSigmaPr(), protonTrack.tofNSigmaPr(), - posTrack.tpcNSigmaEl(), negTrack.tpcNSigmaEl(), - photon.mGamma, photon.alpha, photon.qtarm, photon.radius, - photonOpeningAngle, photonPointingAngle, photonDcaToPV, - rootCenter, antiSigmaPointingAngle, candDcaToPV, - protonTrack.sign(), - protonTrack.itsNCls(), protonTrack.tpcNClsFound(), protonTrack.dcaXY(), protonTrack.dcaZ(), - posTrack.itsNCls(), posTrack.tpcNClsFound(), negTrack.itsNCls(), negTrack.tpcNClsFound(), - collisionIdCheck, - isSignal, - mcTrueVtx[0], mcTrueVtx[1], mcTrueVtx[2], - mcTrueMomProton[0], mcTrueMomProton[1], mcTrueMomProton[2], - mcTrueMomGamma[0], mcTrueMomGamma[1], mcTrueMomGamma[2], - mcTrueMomSigmaPlus[0], mcTrueMomSigmaPlus[1], mcTrueMomSigmaPlus[2], - decayRadiusMC, massMC); + sigmaPlusCandsMC(cand.collisionId, cand.protonTrackId, cand.photonPosTrackId, cand.photonNegTrackId, + cand.matchedSigmaId, + cand.decVtx[0], cand.decVtx[1], cand.decVtx[2], + cand.flightDistance, cand.dcaProtonGamma, + cand.pProton[0], cand.pProton[1], cand.pProton[2], + cand.pGamma1[0], cand.pGamma1[1], cand.pGamma1[2], + cand.pGamma2[0], cand.pGamma2[1], cand.pGamma2[2], + cand.protonTpcNSigma, cand.protonTofNSigma, + cand.posTpcNSigmaEl, cand.negTpcNSigmaEl, + cand.photonMass, cand.photonAlpha, cand.photonQt, cand.photonRadius, + cand.photonOpeningAngle, cand.photonPointingAngle, + cand.rootCenter, cand.antiSigmaPointingAngle, cand.dcaToPV, cand.tiltAngle, + cand.protonSign, + cand.protonItsNCls, cand.protonTpcNCls, cand.protonDcaXY, cand.protonDcaZ, + cand.posItsNCls, cand.posTpcNCls, cand.negItsNCls, cand.negTpcNCls, + cand.photonDcaDau, cand.photonCosPAToPV, cand.photonDcaXYToPV, cand.photonDcaZToPV, cand.photonPsiPair, + cand.posDcaXY, cand.posDcaZ, cand.negDcaXY, cand.negDcaZ, + cand.posTpcNClsFindable, cand.negTpcNClsFindable, cand.posTpcChi2NCl, cand.negTpcChi2NCl, + mlScore, + cand.collisionIdCheck, + cand.isSignal, cand.protonIsSignal, cand.photonIsSignal, + cand.decVtxMC[0], cand.decVtxMC[1], cand.decVtxMC[2], + cand.pProtonMC[0], cand.pProtonMC[1], cand.pProtonMC[2], + cand.pGammaMC[0], cand.pGammaMC[1], cand.pGammaMC[2], + cand.pSigmaPlusMC[0], cand.pSigmaPlusMC[1], cand.pSigmaPlusMC[2], + cand.decayRadiusMC, cand.massMC); } else { if (fillSlimTables) { - slimSigmaPlusCands(radius, - candDcaToPV, dcaProtonGamma, - protonTrack.sign(), - protonTrack.dcaXY(), protonTrack.dcaZ(), - pProton[0], pProton[1], pProton[2], - pGamma1[0], pGamma1[1], pGamma1[2], - bestMomGamma2[0], bestMomGamma2[1], bestMomGamma2[2], - protonTrack.tpcNSigmaPr(), protonTrack.tofNSigmaPr(), - posTrack.tpcNSigmaEl(), negTrack.tpcNSigmaEl(), - photon.mGamma); - return matchedSigmaId; + slimSigmaPlusCands(cand.collisionId, cand.protonTrackId, cand.photonPosTrackId, cand.photonNegTrackId, + cand.radius, + cand.dcaToPV, cand.tiltAngle, cand.dcaProtonGamma, + cand.protonSign, + cand.protonDcaXY, cand.protonDcaZ, + cand.pProton[0], cand.pProton[1], cand.pProton[2], + cand.pGamma1[0], cand.pGamma1[1], cand.pGamma1[2], + cand.pGamma2[0], cand.pGamma2[1], cand.pGamma2[2], + cand.protonTpcNSigma, cand.protonTofNSigma, + cand.posTpcNSigmaEl, cand.negTpcNSigmaEl, + cand.photonMass, + cand.photonDcaDau, cand.photonCosPAToPV, cand.photonDcaXYToPV, cand.photonDcaZToPV, cand.photonPsiPair, + cand.posDcaXY, cand.posDcaZ, cand.negDcaXY, cand.negDcaZ, + cand.posTpcNClsFindable, cand.negTpcNClsFindable, cand.posTpcChi2NCl, cand.negTpcChi2NCl, + mlScore); + return; } - sigmaPlusCands(secVtx[0], secVtx[1], secVtx[2], - flightDistance, dcaProtonGamma, - pProton[0], pProton[1], pProton[2], - pGamma1[0], pGamma1[1], pGamma1[2], - bestMomGamma2[0], bestMomGamma2[1], bestMomGamma2[2], - protonTrack.tpcNSigmaPr(), protonTrack.tofNSigmaPr(), - posTrack.tpcNSigmaEl(), negTrack.tpcNSigmaEl(), - photon.mGamma, photon.alpha, photon.qtarm, photon.radius, - photonOpeningAngle, photonPointingAngle, photonDcaToPV, - rootCenter, antiSigmaPointingAngle, candDcaToPV, - protonTrack.sign(), - protonTrack.itsNCls(), protonTrack.tpcNClsFound(), protonTrack.dcaXY(), protonTrack.dcaZ(), - posTrack.itsNCls(), posTrack.tpcNClsFound(), negTrack.itsNCls(), negTrack.tpcNClsFound()); - } - return matchedSigmaId; + sigmaPlusCands(cand.collisionId, cand.protonTrackId, cand.photonPosTrackId, cand.photonNegTrackId, + cand.decVtx[0], cand.decVtx[1], cand.decVtx[2], + cand.flightDistance, cand.dcaProtonGamma, + cand.pProton[0], cand.pProton[1], cand.pProton[2], + cand.pGamma1[0], cand.pGamma1[1], cand.pGamma1[2], + cand.pGamma2[0], cand.pGamma2[1], cand.pGamma2[2], + cand.protonTpcNSigma, cand.protonTofNSigma, + cand.posTpcNSigmaEl, cand.negTpcNSigmaEl, + cand.photonMass, cand.photonAlpha, cand.photonQt, cand.photonRadius, + cand.photonOpeningAngle, cand.photonPointingAngle, + cand.rootCenter, cand.antiSigmaPointingAngle, cand.dcaToPV, cand.tiltAngle, + cand.protonSign, + cand.protonItsNCls, cand.protonTpcNCls, cand.protonDcaXY, cand.protonDcaZ, + cand.posItsNCls, cand.posTpcNCls, cand.negItsNCls, cand.negTpcNCls, + cand.photonDcaDau, cand.photonCosPAToPV, cand.photonDcaXYToPV, cand.photonDcaZToPV, cand.photonPsiPair, + cand.posDcaXY, cand.posDcaZ, cand.negDcaXY, cand.negDcaZ, + cand.posTpcNClsFindable, cand.negTpcNClsFindable, cand.posTpcChi2NCl, cand.negTpcChi2NCl, + mlScore); + } } - void initCCDB(aod::BCs::iterator const& bc) + // The same photon can be in candidates of several collisions + template + void deduplicateAndWriteCandidates() + { + std::unordered_map> candsByPhoton; + for (int iCand = 0; iCand < static_cast(mCandidatesOfTimeframe.size()); ++iCand) { + candsByPhoton[mCandidatesOfTimeframe[iCand].photonId].push_back(iCand); + } + std::vector keep(mCandidatesOfTimeframe.size(), !candDeduplicatePhotons); + for (const auto& [photon, cands] : candsByPhoton) { + histos.fill(HIST("Candidate/Inclusive/hCandidatesPerPhoton"), cands.size()); + if (candDeduplicatePhotons) { + int best = cands[0]; + for (const int& iCand : cands) { + if (mCandidatesOfTimeframe[iCand].dcaToPV < mCandidatesOfTimeframe[best].dcaToPV) { + best = iCand; + } + } + keep[best] = true; + } + } + + for (int iCand = 0; iCand < static_cast(mCandidatesOfTimeframe.size()); ++iCand) { + if (!keep[iCand]) { + continue; + } + const auto& cand = mCandidatesOfTimeframe[iCand]; + fillCandHist(HIST("hSelectionCounter"), cand.isSignal, 14); + fillCandHist(HIST("h2SelectionCounterVsLegType"), cand.isSignal, 14, cand.photonLegsWithoutIts); + if constexpr (IsMC) { + if (cand.isSignal) { + fillSigmaCounter(cand.matchedSigmaId, 5); + auto genIt = mGenSigmaWritten.find(cand.matchedSigmaId); + if (genIt != mGenSigmaWritten.end()) { + genIt->second = true; + } + } + } + fillCandidateTables(cand); + } + mCandidatesOfTimeframe.clear(); + } + + void initCCDB(aod::BCsWithTimestamps::iterator const& bc) { if (mRunNumber == bc.runNumber()) { return; } mRunNumber = bc.runNumber(); - o2::parameters::GRPMagField* grpmag = ccdb->getForRun(grpmagPath, mRunNumber); + auto* grpmag = ccdb->getForRun(grpmagPath, mRunNumber); o2::base::Propagator::initFieldFromGRP(grpmag); mBz = grpmag->getNominalL3Field(); fitter.setBz(mBz); + if (!mLut) { + mLut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->get(lutPath)); + } + o2::base::Propagator::Instance()->setMatLUT(mLut); LOG(info) << "Task initialized for run " << mRunNumber << " with magnetic field " << mBz << " kZG"; } - void processData(CollisionsFull const& collisions, aod::V0Datas const& v0s, TracksFull const& tracks, aod::BCs const&) + // MC: generated Sigma+ -> p pi0 in acceptance and reconstructed daughters, for the Sigma+ counter + template + void countGeneratedMc(TTracks const& tracks, aod::McParticles const& mcParticles) { - for (const auto& collision : collisions) { - if (std::abs(collision.posZ()) > cutZVertex || !collision.sel8()) { + // generated Sigma+ -> p pi0 in acceptance + mGenSigmaWritten.clear(); + for (const auto& mcPart : mcParticles) { + if (!isSigmaPlusToProtonPi0(mcPart) || std::abs(mcPart.y()) > cutRapMotherMC || mcPart.pt() < cutPtGenMC) { continue; } - initCCDB(collision.bc_as()); - histos.fill(HIST("hVertexZ"), collision.posZ()); - std::array pv{collision.posX(), collision.posY(), collision.posZ()}; - - auto tracksThisCollision = tracks.sliceBy(tracksPerCollision, collision.globalIndex()); - auto v0sThisCollision = v0s.sliceBy(v0PerCollision, collision.globalIndex()); - - auto acceptedPhotons = findPhotonsFromV0s(v0sThisCollision, tracksThisCollision, pv); - - std::vector acceptedProtons; - for (const auto& track : tracksThisCollision) { - if (selectProton(track)) { - acceptedProtons.push_back(track); - } - } - - for (const auto& photon : acceptedPhotons) { - for (const auto& proton : acceptedProtons) { - buildSigmaPlusCandidate(proton, photon, pv); - } - } + mGenSigmaWritten[mcPart.globalIndex()] = false; + histos.fill(HIST("MC/hGenSigmaPlusPt"), mcPart.pt()); + histos.fill(HIST("MC/hSigmaPlusCounter"), 0); } - } - PROCESS_SWITCH(Sigmaplusbuilder, processData, "Process data", true); - - void processMc(CollisionsFullMC const& collisions, aod::V0Datas const& v0s, TracksFullMC const& tracks, aod::BCs const&, aod::McParticles const& mcParticles, aod::McCollisions const&) - { - std::vector matchedSigmaPlusMcIds; // Sigma+ MC indices that got at least one signal candidate - for (const auto& collision : collisions) { - if (std::abs(collision.posZ()) > cutZVertex || !collision.sel8()) { + constexpr int ElectronLeg = 1; + constexpr int PositronLeg = 2; + std::unordered_map> legsOfPhoton; + for (const auto& track : tracks) { + if (!track.has_mcParticle()) { continue; } - initCCDB(collision.bc_as()); - histos.fill(HIST("hVertexZ"), collision.posZ()); - std::array pv{collision.posX(), collision.posY(), collision.posZ()}; - - auto tracksThisCollision = tracks.sliceBy(tracksPerCollisionMC, collision.globalIndex()); - auto v0sThisCollision = v0s.sliceBy(v0PerCollision, collision.globalIndex()); - - auto acceptedPhotons = findPhotonsFromV0s(v0sThisCollision, tracksThisCollision, pv); - for (const auto& photon : acceptedPhotons) { - histos.fill(HIST("MC/hPhotonTruthQA"), 0); - auto posTrack = photon.posTrack; - auto negTrack = photon.negTrack; - if (posTrack.has_mcParticle() && negTrack.has_mcParticle()) { - histos.fill(HIST("MC/hPhotonTruthQA"), 1); - auto mcPos = posTrack.template mcParticle_as(); - auto mcNeg = negTrack.template mcParticle_as(); - - auto const& posMothers = mcPos.template mothers_as(); - auto const& negMothers = mcNeg.template mothers_as(); - if (!posMothers.empty() && !negMothers.empty()) { - auto mcGamma = posMothers.front(); - if (mcGamma.globalIndex() == negMothers.front().globalIndex() && mcGamma.pdgCode() == PDG_t::kGamma) { - histos.fill(HIST("MC/hPhotonTruthQA"), 2); - auto const& pi0Mothers = mcGamma.template mothers_as(); - if (!pi0Mothers.empty() && std::abs(pi0Mothers.front().pdgCode()) == PDG_t::kPi0) { - histos.fill(HIST("MC/hPhotonTruthQA"), 3); - auto const& sigmaMothers = pi0Mothers.front().template mothers_as(); - if (!sigmaMothers.empty() && std::abs(sigmaMothers.front().pdgCode()) == PDG_t::kSigmaPlus) { - histos.fill(HIST("MC/hPhotonTruthQA"), 4); - - histos.fill(HIST("Photon/True/hMass"), photon.mGamma); - histos.fill(HIST("Photon/True/hPt"), std::hypot(photon.px, photon.py)); - histos.fill(HIST("Photon/True/hRadius"), photon.radius); - histos.fill(HIST("Photon/True/h2ArmenterosPodolanski"), photon.alpha, photon.qtarm); - histos.fill(HIST("Photon/True/h2ConvPointXY"), photon.x, photon.y); - } - } - } - } + auto mcParticle = track.template mcParticle_as(); + if (std::abs(mcParticle.pdgCode()) == PDG_t::kProton) { + fillSigmaCounter(findSigmaPlusMotherOfProton(mcParticle), 1); + } else if (std::abs(mcParticle.pdgCode()) == PDG_t::kElectron) { + int photonIndex = -1; + std::array conversionVertex{}; + int sigmaId = findSigmaPlusMotherOfConversionElectron(mcParticle, photonIndex, conversionVertex); + if (sigmaId >= 0) { + auto& legs = legsOfPhoton[photonIndex]; + legs.first = sigmaId; + legs.second |= (mcParticle.pdgCode() == PDG_t::kElectron ? ElectronLeg : PositronLeg); } } - - std::vector acceptedProtons; - for (const auto& track : tracksThisCollision) { - if (selectProton(track)) { - acceptedProtons.push_back(track); - - histos.fill(HIST("MC/hProtonTruthQA"), 0); - if (track.has_mcParticle()) { - histos.fill(HIST("MC/hProtonTruthQA"), 1); - auto mcProton = track.template mcParticle_as(); - if (findSigmaPlusMotherOfProton(mcProton) >= 0) { - histos.fill(HIST("MC/hProtonTruthQA"), 2); - } - } - } - } - - for (const auto& photon : acceptedPhotons) { - for (const auto& proton : acceptedProtons) { - int matchedId = buildSigmaPlusCandidate(proton, photon, pv); - if (matchedId >= 0) { - matchedSigmaPlusMcIds.push_back(matchedId); - } - } + } + for (const auto& [photonIndex, legs] : legsOfPhoton) { + if (legs.second == (ElectronLeg | PositronLeg)) { + fillSigmaCounter(legs.first, 3); } } + } - // all generated Sigma+ -> p pi0 decays, regardless of reconstruction + // MC: a table row with the true values for each generated Sigma+ in acceptance without a written candidate + void writeGeneratedOnlyMc(aod::McParticles const& mcParticles) + { for (const auto& mcPart : mcParticles) { - if (!isSigmaPlusToProtonPi0(mcPart)) { - continue; - } - if (std::abs(mcPart.y()) > cutRapMotherMC) { - continue; - } - if (mcPart.pt() < cutPtGenMC) { - continue; - } - histos.fill(HIST("MC/hGenSigmaPlusPt"), mcPart.pt()); - - bool wasReconstructed = std::find(matchedSigmaPlusMcIds.begin(), matchedSigmaPlusMcIds.end(), mcPart.globalIndex()) != matchedSigmaPlusMcIds.end(); - if (wasReconstructed) { + auto genIt = mGenSigmaWritten.find(mcPart.globalIndex()); + if (genIt == mGenSigmaWritten.end() || genIt->second) { continue; } - // this true Sigma+ never made it into any signal candidate: still record its truth info, - // with the reconstructed-side columns set to -999 int pdgProton = mcPart.pdgCode() > 0 ? PDG_t::kProton : PDG_t::kProtonBar; std::array genDecVtx{-999.f, -999.f, -999.f}; std::array genMomProton{-999.f, -999.f, -999.f}; @@ -1455,8 +1878,10 @@ struct Sigmaplusbuilder { float genMassMC = std::sqrt(mcPart.e() * mcPart.e() - mcPart.p() * mcPart.p()); if (fillSlimTables) { - slimSigmaPlusCandsMC(-999.f, - -999.f, -999.f, + slimSigmaPlusCandsMC(-1, -1, -1, -1, + mcPart.globalIndex(), + -999.f, + -999.f, -999.f, -999.f, 0, -999.f, -999.f, -999.f, -999.f, -999.f, @@ -1465,14 +1890,20 @@ struct Sigmaplusbuilder { -999.f, -999.f, -999.f, -999.f, -999.f, + -999.f, -999.f, -999.f, -999.f, -999.f, + -999.f, -999.f, -999.f, -999.f, + 0, 0, -999.f, -999.f, + -1.f, false, - false, + false, false, false, genDecayRadiusMC, genMassMC, mcPart.px(), mcPart.py(), mcPart.pz()); continue; } - sigmaPlusCandsMC(-999.f, -999.f, -999.f, + sigmaPlusCandsMC(-1, -1, -1, -1, + mcPart.globalIndex(), + -999.f, -999.f, -999.f, -999.f, -999.f, -999.f, -999.f, -999.f, -999.f, -999.f, -999.f, @@ -1480,13 +1911,17 @@ struct Sigmaplusbuilder { -999.f, -999.f, -999.f, -999.f, -999.f, -999.f, -999.f, -999.f, - -999.f, -999.f, -999.f, - -999.f, -999.f, -999.f, + -999.f, -999.f, + -999.f, -999.f, -999.f, -999.f, 0, 0, -999, -999.f, -999.f, 0, -999, 0, -999, + -999.f, -999.f, -999.f, -999.f, -999.f, + -999.f, -999.f, -999.f, -999.f, + 0, 0, -999.f, -999.f, + -1.f, false, - false, + false, false, false, genDecVtx[0], genDecVtx[1], genDecVtx[2], genMomProton[0], genMomProton[1], genMomProton[2], -999.f, -999.f, -999.f, @@ -1494,16 +1929,106 @@ struct Sigmaplusbuilder { genDecayRadiusMC, genMassMC); } } - PROCESS_SWITCH(Sigmaplusbuilder, processMc, "Process MC", false); - void processFindable(CollisionsFullMC const& collisions, aod::V0Datas const& v0s, TracksFullMC const& tracks, aod::McParticles const&, aod::BCs const&) + // Collision loop shared by all process functions. With UseV0Datas the photons come from V0Datas, otherwise from pairing + // the daughter tracks in this task. + template + void runCollisions(TCollisions const& collisions, TV0s const& v0s, TTracks const& tracks, aod::AmbiguousTracks const& ambiguousTracks, aod::BCsWithTimestamps const& bcs) + { + using TTrack = typename TTracks::iterator; + markGoodCollisions(collisions); + std::vector>> photonsByCollision; + if constexpr (!UseV0Datas) { + if (collisions.size() > 0) { + auto firstBC = collisions.begin().template bc_as(); + initCCDB(firstBC); + mVDriftMgr.update(firstBC.timestamp()); + photonsByCollision = findPhotonsSelfPaired(collisions, tracks, ambiguousTracks, bcs); + } + } + + for (const auto& collision : collisions) { + if (std::abs(collision.posZ()) > cutZVertex || !collision.sel8()) { + continue; + } + initCCDB(collision.template bc_as()); + histos.fill(HIST("hVertexZ"), collision.posZ()); + std::array pv{collision.posX(), collision.posY(), collision.posZ()}; + + auto tracksThisCollision = [&]() { + if constexpr (IsMC) { + return tracks.sliceBy(tracksPerCollisionMC, collision.globalIndex()); + } else { + return tracks.sliceBy(tracksPerCollision, collision.globalIndex()); + } + }(); + + std::vector> acceptedPhotons; + if constexpr (UseV0Datas) { + auto v0sThisCollision = v0s.sliceBy(v0PerCollision, collision.globalIndex()); + acceptedPhotons = findPhotonsFromV0s(v0sThisCollision, tracksThisCollision, pv); + } else { + acceptedPhotons = photonsByCollision[collision.globalIndex()]; + } + + std::vector acceptedProtons; + for (const auto& track : tracksThisCollision) { + if (selectProton(track)) { + acceptedProtons.push_back(track); + if constexpr (IsMC) { + if (track.has_mcParticle()) { + fillSigmaCounter(findSigmaPlusMotherOfProton(track.template mcParticle_as()), 2); + } + } + } + } + + for (const auto& photon : acceptedPhotons) { + for (const auto& proton : acceptedProtons) { + buildSigmaPlusCandidate(proton, photon, collision.globalIndex(), pv); + } + } + } + deduplicateAndWriteCandidates(); + } + + void processData(CollisionsFull const& collisions, aod::V0Datas const& v0s, TracksFull const& tracks, aod::AmbiguousTracks const& ambiguousTracks, aod::BCsWithTimestamps const& bcs) + { + runCollisions(collisions, v0s, tracks, ambiguousTracks, bcs); + } + PROCESS_SWITCH(Sigmaplusbuilder, processData, "Process data, photons from V0Datas", false); + + void processDataCustomVertexer(CollisionsFull const& collisions, TracksFull const& tracks, aod::AmbiguousTracks const& ambiguousTracks, aod::BCsWithTimestamps const& bcs) + { + runCollisions(collisions, nullptr, tracks, ambiguousTracks, bcs); + } + PROCESS_SWITCH(Sigmaplusbuilder, processDataCustomVertexer, "Process data, photons from pairing the daughter tracks in this task (no V0Datas)", true); + + void processMc(CollisionsFullMC const& collisions, aod::V0Datas const& v0s, TracksFullMC const& tracks, aod::AmbiguousTracks const& ambiguousTracks, aod::BCsWithTimestamps const& bcs, aod::McParticles const& mcParticles, aod::McCollisions const&) + { + countGeneratedMc(tracks, mcParticles); + runCollisions(collisions, v0s, tracks, ambiguousTracks, bcs); + writeGeneratedOnlyMc(mcParticles); + } + PROCESS_SWITCH(Sigmaplusbuilder, processMc, "Process MC, photons from V0Datas", false); + + void processMcCustomVertexer(CollisionsFullMC const& collisions, TracksFullMC const& tracks, aod::AmbiguousTracks const& ambiguousTracks, aod::BCsWithTimestamps const& bcs, aod::McParticles const& mcParticles, aod::McCollisions const&) + { + countGeneratedMc(tracks, mcParticles); + runCollisions(collisions, nullptr, tracks, ambiguousTracks, bcs); + writeGeneratedOnlyMc(mcParticles); + } + PROCESS_SWITCH(Sigmaplusbuilder, processMcCustomVertexer, "Process MC, photons from pairing the daughter tracks in this task (no V0Datas)", false); + + void processFindable(CollisionsFullMC const& collisions, aod::V0Datas const& v0s, TracksFullMC const& tracks, aod::McParticles const&, aod::BCsWithTimestamps const&) { + mGenSigmaWritten.clear(); constexpr int MinDauTpcCls = 90; for (const auto& collision : collisions) { if (std::abs(collision.posZ()) > cutZVertex || !collision.sel8()) { continue; } - initCCDB(collision.bc_as()); + initCCDB(collision.bc_as()); auto tracksThisCollision = tracks.sliceBy(tracksPerCollisionMC, collision.globalIndex()); auto v0sThisCollision = v0s.sliceBy(v0PerCollision, collision.globalIndex()); std::array pv{collision.posX(), collision.posY(), collision.posZ()}; diff --git a/PWGLF/TableProducer/Strangeness/strangederivedbuilder.cxx b/PWGLF/TableProducer/Strangeness/strangederivedbuilder.cxx index 57ebfa938a0..8007e538f89 100644 --- a/PWGLF/TableProducer/Strangeness/strangederivedbuilder.cxx +++ b/PWGLF/TableProducer/Strangeness/strangederivedbuilder.cxx @@ -249,7 +249,7 @@ struct strangederivedbuilder { Preslice V0perCollision = o2::aod::v0data::collisionId; Preslice CascperCollision = o2::aod::cascdata::collisionId; Preslice KFCascperCollision = o2::aod::cascdata::collisionId; - // Preslice TraCascperCollision = o2::aod::cascdata::collisionId; + Preslice TraCascperCollision = o2::aod::cascdata::collisionId; Preslice mcParticlePerMcCollision = o2::aod::mcparticle::mcCollisionId; Preslice udCollisionsPerCollision = o2::aod::udcollision::collisionId; @@ -576,12 +576,11 @@ struct strangederivedbuilder { auto V0Table_thisColl = V0s.sliceBy(V0perCollision, collIdx); auto CascTable_thisColl = Cascades.sliceBy(CascperCollision, collIdx); auto KFCascTable_thisColl = KFCascades.sliceBy(KFCascperCollision, collIdx); - // auto TraCascTable_thisColl = TraCascades.sliceBy(TraCascperCollision, collIdx); + auto TraCascTable_thisColl = TraCascades.sliceBy(TraCascperCollision, collIdx); bool strange = V0Table_thisColl.size() > 0 || CascTable_thisColl.size() > 0 || - KFCascTable_thisColl.size() > 0 /* || - TraCascTable_thisColl.size() > 0*/ - ; + KFCascTable_thisColl.size() > 0 || + TraCascTable_thisColl.size() > 0; auto bc = collision.template bc_as(); @@ -762,8 +761,8 @@ struct strangederivedbuilder { CascadeCollIndices[casc.globalIndex()] = products.strangeColl.lastIndex(); for (const auto& casc : KFCascTable_thisColl) KFCascadeCollIndices[casc.globalIndex()] = products.strangeColl.lastIndex(); - // for (const auto& casc : TraCascTable_thisColl) - // TraCascadeCollIndices[casc.globalIndex()] = products.strangeColl.lastIndex(); + for (const auto& casc : TraCascTable_thisColl) + TraCascadeCollIndices[casc.globalIndex()] = products.strangeColl.lastIndex(); } // +-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+ diff --git a/PWGLF/TableProducer/Strangeness/strangenessbuilder.cxx b/PWGLF/TableProducer/Strangeness/strangenessbuilder.cxx index 14f37772f44..493244cb856 100644 --- a/PWGLF/TableProducer/Strangeness/strangenessbuilder.cxx +++ b/PWGLF/TableProducer/Strangeness/strangenessbuilder.cxx @@ -687,7 +687,7 @@ struct StrangenessBuilder { LOGF(info, "Configuring tables to generate"); auto& workflows = context.services().get(); - TString listOfRequestors[nTables]; + std::array listOfRequestors{}; for (int i = 0; i < nTables; i++) { // adjust bookkeeping histogram h->GetXaxis()->SetBinLabel(i + 1, tableNames[i].c_str()); @@ -1006,7 +1006,7 @@ struct StrangenessBuilder { AvgPA, // 6. Avg Pointing Angle static_cast(v0zRanks[ic])}; // 7. V0 Vtx z Rank - float* BDTProbability = deduplication_bdt.evalModel(inputFeatures); + const std::vector BDTProbability = deduplication_bdt.evalModel(inputFeatures); if (BDTProbability[1] > bestMLScore) { bestMLScore = BDTProbability[1]; diff --git a/PWGLF/Tasks/GlobalEventProperties/PseudorapidityDensityMFT.cxx b/PWGLF/Tasks/GlobalEventProperties/PseudorapidityDensityMFT.cxx index 83d6ef873c2..8390f501389 100644 --- a/PWGLF/Tasks/GlobalEventProperties/PseudorapidityDensityMFT.cxx +++ b/PWGLF/Tasks/GlobalEventProperties/PseudorapidityDensityMFT.cxx @@ -50,7 +50,9 @@ #include #include #include +#include #include +#include #include #include @@ -96,7 +98,7 @@ const AxisSpec centAxis = {{0, 10, 20, 30, 40, 50, 60, 70, 80, 100}}; const AxisSpec vxAxis = {200, -0.5, 0.5, "V_{x} (cm)"}; const AxisSpec vyAxis = {200, -0.5, 0.5, "V_{y} (cm)"}; // Status axis for reco/truth (1=reco, 2=true) -AxisSpec recoTruthStatusAxis = {2, 0.5, 2.5, "status"}; +const AxisSpec recoTruthStatusAxis = {2, 0.5, 2.5, "status"}; // Delta-vertex axes (reco - true) in cm const AxisSpec deltaVxAxis = {400, -0.5, 0.5, "#DeltaV_{x} = V_{x}^{rec}-V_{x}^{true} (cm)"}; @@ -394,6 +396,11 @@ struct PseudorapidityDensityMFT { Configurable useNoITSROFrameBorderCut{"useNoITSROFrameBorderCut", true, "Require kNoITSROFrameBorder in processGenReco"}; AxisSpec multAxisRecoMFT = {multBinning, "N_{ch}^{reco,MFT}"}; AxisSpec multAxisGenMFT = {multBinning, "N_{ch}^{gen,MFT}"}; + // Namespace plus invocation/reco IDs makes offline event keys independent of + // local collision-index reuse across dataframes and separate worker outputs. + TUUID associationKeyNamespace; + uint64_t associationInvocation{0}; + HistogramRegistry registry{ "registry", { @@ -497,6 +504,40 @@ struct PseudorapidityDensityMFT { {HistType::kTH1F, {dcaXyAxis}}}); if (doprocessGenReco3d || doprocessGenReco2d || doprocessGenRecoTimeCom) { + // Additional inputs for offline vertex/DCA studies. Existing histograms are unchanged. + if (doprocessGenReco3d) { + registry.add({"Association/RightTrackSparseBeforeDCA", + ";#eta;z_{chosen}^{reco} (cm);DCA_{xy} (cm);DCA_{z} (cm);category (0: nonamb, 1: amb)", + {HistType::kTHnSparseD, {etaBinning, zAxis, dcaXyAxis, dcaZAxis, {2, -0.5, 1.5}}}}); + registry.add({"Association/WrongTrackSparseBeforeDCA", + ";#eta;z_{chosen}^{reco} (cm);DCA_{xy} (cm);DCA_{z} (cm);category (0: nonamb, 1: amb)", + {HistType::kTHnSparseD, {etaBinning, zAxis, dcaXyAxis, dcaZAxis, {2, -0.5, 1.5}}}}); + registry.add({"Association/UnclassifiedTrackSparseBeforeDCA", + ";#eta;z_{chosen}^{reco} (cm);DCA_{xy} (cm);DCA_{z} (cm);category (0: nonamb, 1: amb)", + {HistType::kTHnSparseD, {etaBinning, zAxis, dcaXyAxis, dcaZAxis, {2, -0.5, 1.5}}}}); + // Key components are exact 16-bit integers. Six namespace components + // retain 96 UUID bits; then two invocation and two chosen-reco-ID parts. + const AxisSpec key16{65536, -0.5, 65535.5}; + registry.add({"Association/EventTrackKeysBeforeDCA", + ";#eta;z_{chosen}^{reco};DCAxy;DCAz;track category;association (0:right,1:wrong,2:unclassified);namespace0;namespace1;namespace2;namespace3;namespace4;namespace5;callLow;callHigh;recoLow;recoHigh", + {HistType::kTHnSparseD, {etaBinning, zAxis, dcaXyAxis, dcaZAxis, {2, -0.5, 1.5}, {3, -0.5, 2.5}, key16, key16, key16, key16, key16, key16, key16, key16, key16, key16}}}); + registry.add({"Association/hNumRecoEventsWithRightTracks", ";Selected reco collisions with right-associated tracks;Count", {HistType::kTH1D, {{1, 0.5, 1.5}}}}); + registry.add({"Association/hNumRecoEventsWithWrongTracks", ";Selected reco collisions with wrong-associated tracks;Count", {HistType::kTH1D, {{1, 0.5, 1.5}}}}); + registry.add({"Association/RecoEventVzWithRightTracks", ";z_{chosen}^{reco} (cm);Selected reco collisions", {HistType::kTH1D, {zAxis}}}); + registry.add({"Association/RecoEventVzWithWrongTracks", ";z_{chosen}^{reco} (cm);Selected reco collisions", {HistType::kTH1D, {zAxis}}}); + registry.add({"OfflineVariations/RecoTrackSparseBeforeDCA", + ";#eta;z_{vtx}^{reco} (cm);DCA_{xy} (cm);DCA_{z} (cm);category (0: nonamb, 1: amb)", + {HistType::kTHnSparseF, {etaBinning, zAxis, dcaXyAxis, dcaZAxis, {2, -0.5, 1.5}}}}); + registry.add({"OfflineVariations/RecoStrangeSparseBeforeDCA", + ";#eta;z_{vtx}^{reco} (cm);DCA_{xy} (cm);DCA_{z} (cm);category (0: nonamb, 1: amb)", + {HistType::kTHnSparseF, {etaBinning, zAxis, dcaXyAxis, dcaZAxis, {2, -0.5, 1.5}}}}); + } + registry.add({"OfflineVariations/SelectedRecoEventVertexResponse", + ";z_{vtx}^{true} (cm);z_{vtx}^{reco} (cm);selected reco collisions", + {HistType::kTHnSparseD, {zAxis, zAxis}}}); + registry.add({"OfflineVariations/GenPrimaryEtaTrueZRecoZ", + ";#eta_{gen};z_{vtx}^{true} (cm);z_{vtx}^{reco} (cm)", + {HistType::kTHnSparseD, {etaBinning, zAxis, zAxis}}}); registry.add({"EventsRecoCuts_GenReco", ";cut;events", {HistType::kTH1F, {{16, 0.5, 16.5}}}}); @@ -1061,6 +1102,14 @@ struct PseudorapidityDensityMFT { {HistType::kTHnSparseF, {dcaXyAxis, dcaZAxis, dcaXAxis, dcaYAxis}}}); + // Category-specific selected reconstructed-collision counts (not generated events). + registry.add({"Purity/hNumCollisionsNonAmb_InelMFT", + ";Selected reco collisions with nonambiguous tracks;Count", + {HistType::kTH1D, {{1, 0.5, 1.5}}}}); + registry.add({"Purity/hNumCollisionsAmb_InelMFT", + ";Selected reco collisions with ambiguous tracks;Count", + {HistType::kTH1D, {{1, 0.5, 1.5}}}}); + registry.add({"Purity/RecoSparseAll_EventCount", ";events;counts", {HistType::kTH1F, {{1, 0.5, 1.5}}}}); @@ -1166,6 +1215,16 @@ struct PseudorapidityDensityMFT { } if (doprocessMultReassoc || doprocessMultReassoc3d) { + // Offline DCA variations need tracks before either DCA rejection. + // Only the 3D process supplies a measured bestDCAZ; do not fill fake zeros + // for the 2D process. Category 0 = nonambiguous, 1 = ambiguous; no orphans. + if (doprocessMultReassoc3d) { + registry.add({"Tracks/2Danalysis/DataTrackSparseBeforeDCA", + ";#eta;z_{vtx}^{reco} (cm);DCA_{xy} (cm);DCA_{z} (cm);track category (0: nonamb, 1: amb)", + {HistType::kTHnSparseF, + {etaBinning, zAxis, dcaXyAxis, dcaZAxis, {2, -0.5, 1.5}}}}); + } + // Purity denominator histograms (reco side after DCAxy selection) registry.add({"Tracks/Control/DeltaZ", @@ -1317,8 +1376,8 @@ struct PseudorapidityDensityMFT { // {HistType::kTH1F,{{100000, 0.5, 100000.0}}}}); // {HistType::kTH1F, {{100000, -50000.0, 50000.0}}}}); // registry.add({"NonambEventCounts", " ; EventCounts Nonamb", {HistType::kTH1F, {{1, 0.5, 1.5}}}}); // - registry.add({"hNumCollisionsNonAmb_InelMFT", " ; Number of Collisions with Non-Ambiguous Tracks;Count;Frequency", {HistType::kTH1F, {{1, 0.5, 1.5}}}}); // - registry.add({"hNumCollisionsAmb_InelMFT", " ; Number of Collisions with Non-Ambiguous Tracks;Count;Frequency", {HistType::kTH1F, {{1, 0.5, 1.5}}}}); // + registry.add({"hNumCollisionsNonAmb_InelMFT", ";Selected collisions with nonambiguous tracks;Count", {HistType::kTH1D, {{1, 0.5, 1.5}}}}); + registry.add({"hNumCollisionsAmb_InelMFT", ";Selected collisions with ambiguous tracks;Count", {HistType::kTH1D, {{1, 0.5, 1.5}}}}); registry.add({"hNumCollisions_InelMFT", " ; Number of selected events with Inel>0 and MFT>0;Count;Frequency", {HistType::kTH1F, {{1, 0.5, 1.5}}}}); // registry.add({"hNumCollisions_Inel", " ; Number of selected events with Inel>0;Count;Frequency", {HistType::kTH1F, {{1, 0.5, 1.5}}}}); // registry.add({"ambEventCounts", " ; EventCounts Nonamb", {HistType::kTH1F, {{1, 0.5, 1.5}}}}); // @@ -1566,8 +1625,6 @@ struct PseudorapidityDensityMFT { { std::unordered_set uniqueEvents; std::unordered_set uniqueEventsAmb; - std::unordered_set uniqueCollisions; - std::unordered_set uniqueCollisionsAmb; std::unordered_set eventsInelMFT; std::unordered_set eventsInel; @@ -1673,7 +1730,7 @@ struct PseudorapidityDensityMFT { o2::math_utils::bringTo02Pi(phi); const float dcaXyCut = retrack.bestDCAXY(); - bool failTrackCuts = + const bool failTrackQualityCuts = track.nClusters() < cfgnCluster || track.eta() <= cfgnEta1 || track.eta() >= cfgnEta2 || @@ -1685,8 +1742,23 @@ struct PseudorapidityDensityMFT { ((phi <= PhiVetoLow) || ((phi >= PhiVetoPiMin) && (phi <= PhiVetoPiMax)) || (phi >= PhiVetoHigh))) || - (useDCAxyCut && std::abs(dcaXyCut) > maxDCAxy) || (usePtCut && ptCut > cfgnPt); + (usePtCut && ptCut > cfgnPt); + + if (failTrackQualityCuts) { + continue; + } + // Selected INEL>0 events and quality-selected non-orphan tracks, before + // both DCA cuts. This keeps the existing post-DCA spectra unchanged. + if constexpr (std::is_same_v>) { + if (hasMidtracks) { + const int category = retrack.ambDegree() == SingleCompatibleCollision ? 0 : 1; + registry.fill(HIST("Tracks/2Danalysis/DataTrackSparseBeforeDCA"), + track.eta(), z, dcaXyCut, retrack.bestDCAZ(), category); + } + } + + bool failTrackCuts = useDCAxyCut && std::abs(dcaXyCut) > maxDCAxy; if constexpr (std::is_same_v>) { const float dcaZCut = retrack.bestDCAZ(); failTrackCuts = failTrackCuts || (useDCAzCut && std::abs(dcaZCut) > maxDCAz); @@ -1775,9 +1847,6 @@ struct PseudorapidityDensityMFT { } uniqueEventsAmb.insert(retrack.bestCollisionId()); } - if (midtracks.size() > 0 && retrack.ambDegree() > 1) { - uniqueCollisionsAmb.insert(collision.globalIndex()); - } registry.fill(HIST("Tracks/Control/TrackIsAmb"), isAmbiguous); if (retrack.ambDegree() == 1) { ++j; @@ -1794,9 +1863,6 @@ struct PseudorapidityDensityMFT { } uniqueEvents.insert(retrack.bestCollisionId()); } - if (midtracks.size() > 0 && retrack.ambDegree() == 1) { - uniqueCollisions.insert(collision.globalIndex()); - } registry.fill(HIST("Tracks/Control/woOrp/woOrpTracksEtaZvtx"), track.eta(), z); registry.fill(HIST("Tracks/Control/woOrp/woOrpTracksPhiEta"), phi, @@ -1809,11 +1875,20 @@ struct PseudorapidityDensityMFT { } registry.fill(HIST("ambEventCounts"), 1, uniqueEventsAmb.size()); registry.fill(HIST("NonambEventCounts"), 1, uniqueEvents.size()); - registry.fill(HIST("hNumCollisionsNonAmb_InelMFT"), 1, uniqueCollisions.size()); - registry.fill(HIST("hNumCollisionsAmb_InelMFT"), 1, uniqueCollisionsAmb.size()); registry.fill(HIST("hNumCollisions_InelMFT"), 1, eventsInelMFT.size()); } + // Count selected reconstructed collisions once, not once per track. + // Mixed events contribute to both categories; orphan tracks were rejected above. + if (midtracks.size() > 0) { + if (nMFTSelectedNonAmb > 0) { + registry.fill(HIST("hNumCollisionsNonAmb_InelMFT"), 1.); + } + if (nMFTSelectedAmb > 0) { + registry.fill(HIST("hNumCollisionsAmb_InelMFT"), 1.); + } + } + registry.fill(HIST("Tracks/Control/amb/nTrkAmb"), i); registry.fill(HIST("Tracks/Control/nonamb/nTrkNonAmb"), j); registry.fill(HIST("Tracks/Control/woOrp/nTrk"), k); @@ -2206,7 +2281,7 @@ struct PseudorapidityDensityMFT { const auto countAndPassEvSelGenReco = [&](auto const& collision) { struct EvSelStep { bool enabled{false}; - decltype(aod::evsel::kIsTriggerTVX) bit; + decltype(aod::evsel::kIsTriggerTVX) bit{aod::evsel::kIsTriggerTVX}; GenRecoCutBin bin{GenRecoCutBin::AllRecoCollisions}; }; @@ -2533,7 +2608,7 @@ struct PseudorapidityDensityMFT { const auto countAndPassEvSelGenReco = [&](auto const& collision) { struct EvSelStep { bool enabled{false}; - decltype(aod::evsel::kIsTriggerTVX) bit; + decltype(aod::evsel::kIsTriggerTVX) bit{aod::evsel::kIsTriggerTVX}; GenRecoCutBin bin{GenRecoCutBin::AllRecoCollisions}; }; @@ -2624,7 +2699,7 @@ struct PseudorapidityDensityMFT { // The reconstructed event has now passed the z window, reconstructed // INEL>0, reco-gen z matching and the complete event selection. if (!atLeastOneSel8VzGt0) { - selectedRecoNtrkForEventLoss = static_cast(perCollisionSample.size()); + selectedRecoNtrkForEventLoss = perCollisionSample.size(); selectedRecoZvtxForEventLoss = collision.posZ(); } atLeastOneSel8VzGt0 = true; @@ -2695,6 +2770,10 @@ struct PseudorapidityDensityMFT { recoVtxByRecoId[recoCol] = {collision.posX(), collision.posY(), collision.posZ()}; recoVtxByMcId[mcCol] = {mcCollision.posX(), mcCollision.posY(), mcCollision.posZ()}; + // One entry per accepted reconstructed collision, including zero-MFT events. + registry.fill(HIST("OfflineVariations/SelectedRecoEventVertexResponse"), + mcCollision.posZ(), collision.posZ()); + registry.fill(HIST("Purity/xReco"), collision.posX()); registry.fill(HIST("Purity/xTrue"), mcCollision.posX()); registry.fill(HIST("Purity/yReco"), collision.posY()); @@ -2716,6 +2795,107 @@ struct PseudorapidityDensityMFT { int64_t nRecoMFTSelectedForPNch{0}; int64_t nGenPrimaryChargedMFT{0}; std::unordered_set uniqueBestRecoCols; + std::unordered_set recoColsWithNonAmbTracks; + std::unordered_set recoColsWithAmbTracks; + + std::unordered_set recoEventsWithRightTracks; + std::unordered_set recoEventsWithWrongTracks; + if (associationInvocation >= 0xffffffffULL) { + LOGF(fatal, "Association offline event-key invocation range exhausted"); + } + const auto associationCall = static_cast(associationInvocation++); + std::array associationUuid{}; + associationKeyNamespace.GetUUID(associationUuid.data()); + std::array associationNamespaceParts{}; + for (size_t part = 0; part < associationNamespaceParts.size(); ++part) { + associationNamespaceParts[part] = + (static_cast(associationUuid[2 * part]) << 8) | associationUuid[2 * part + 1]; + } + + // Separate pre-DCA pass: intentionally does not alter any existing track + // cuts, counters or fills. The 3D process supplies both measured DCA values. + if constexpr (requires { tracks.begin().bestDCAZ(); }) { + for (const auto& offlineTrack : tracks) { + const auto original = offlineTrack.template mfttrack_as(); + float offlinePhi = original.phi(); + o2::math_utils::bringTo02Pi(offlinePhi); + const auto compatibleCount = offlineTrack.compatibleCollIds().size(); + if (compatibleCount == 0 || original.nClusters() < cfgnCluster || + original.eta() <= cfgnEta1 || original.eta() >= cfgnEta2 || + original.chi2() / getTrackNdf(original) >= cfgChi2NDFMax || + offlinePhi <= cfgPhiCut1 || offlinePhi >= cfgPhiCut2 || + (usePhiCut && (offlinePhi <= PhiVetoLow || + (offlinePhi >= PhiVetoPiMin && offlinePhi <= PhiVetoPiMax) || + offlinePhi >= PhiVetoHigh)) || + (usePtCut && original.pt() > cfgnPt) || !passGenRecoTrackMode(offlineTrack)) { + continue; + } + // Same original-collision association as the existing reco denominator. + const int offlineRecoId = original.collisionId(); + if (!acceptedRecoCols.contains(offlineRecoId)) { + continue; + } + auto offlineVz = recoVtxZ.find(offlineRecoId); + if (offlineVz == recoVtxZ.end()) { + continue; + } + const int category = compatibleCount == SingleCompatibleCollision ? 0 : 1; + registry.fill(HIST("OfflineVariations/RecoTrackSparseBeforeDCA"), + original.eta(), offlineVz->second, + offlineTrack.bestDCAXY(), offlineTrack.bestDCAZ(), category); + const int chosenId = offlineTrack.bestCollisionId(); + auto chosenVz = recoVtxZ.find(chosenId); + auto chosenMc = recoToMc.find(chosenId); + // Only selected chosen reco collisions have a meaningful event denominator. + if (chosenId >= 0 && chosenVz != recoVtxZ.end() && chosenMc != recoToMc.end()) { + int association = 2; // Neither fake nor missing labels are forced into "wrong". + if (offlineTrack.has_mcParticle() && offlineTrack.mcMask() == 0) { + association = chosenMc->second == offlineTrack.mcParticle().mcCollisionId() ? 0 : 1; + } + if (association == 0) { + registry.fill(HIST("Association/RightTrackSparseBeforeDCA"), original.eta(), chosenVz->second, + offlineTrack.bestDCAXY(), offlineTrack.bestDCAZ(), category); + } else if (association == 1) { + registry.fill(HIST("Association/WrongTrackSparseBeforeDCA"), original.eta(), chosenVz->second, + offlineTrack.bestDCAXY(), offlineTrack.bestDCAZ(), category); + } else { + registry.fill(HIST("Association/UnclassifiedTrackSparseBeforeDCA"), original.eta(), chosenVz->second, + offlineTrack.bestDCAXY(), offlineTrack.bestDCAZ(), category); + } + const auto chosenKey = static_cast(chosenId); + registry.fill(HIST("Association/EventTrackKeysBeforeDCA"), + original.eta(), chosenVz->second, offlineTrack.bestDCAXY(), offlineTrack.bestDCAZ(), + category, association, + associationNamespaceParts[0], associationNamespaceParts[1], associationNamespaceParts[2], + associationNamespaceParts[3], associationNamespaceParts[4], associationNamespaceParts[5], + associationCall & 0xffffU, associationCall >> 16, + chosenKey & 0xffffU, chosenKey >> 16); + const bool passesConfiguredDCA = + (!useDCAxyCut || std::abs(offlineTrack.bestDCAXY()) <= maxDCAxy) && + (!useDCAzCut || std::abs(offlineTrack.bestDCAZ()) <= maxDCAz); + if (passesConfiguredDCA) { + if (association == 0) { + recoEventsWithRightTracks.insert(chosenId); + } + if (association == 1) { + recoEventsWithWrongTracks.insert(chosenId); + } + } + } + + if (offlineTrack.has_mcParticle() && offlineTrack.mcMask() == 0) { + const auto particle = offlineTrack.mcParticle(); + if (!particle.isPhysicalPrimary() && particle.has_mothers()) { + const auto mother = particle.template mothers_as().front(); + if (mother.pdgCode() == PDG_t::kK0Short || std::abs(mother.pdgCode()) == PDG_t::kLambda0) { + registry.fill(HIST("OfflineVariations/RecoStrangeSparseBeforeDCA"), + original.eta(), offlineVz->second, + offlineTrack.bestDCAXY(), offlineTrack.bestDCAZ(), category); + } + } + } + } + } if (tracks.size() > 0) { for (const auto& track : tracks) { @@ -2803,6 +2983,16 @@ struct PseudorapidityDensityMFT { continue; } + // Match the inclusive reconstructed-MC denominator below: all accepted + // tracks, including fakes/no-label tracks, grouped by original recoCol. + // Event selection, track mode and both configured DCA cuts have passed. + const auto nCompatibleCollisions = track.compatibleCollIds().size(); + if (nCompatibleCollisions == SingleCompatibleCollision) { + recoColsWithNonAmbTracks.insert(recoCol); + } else if (nCompatibleCollisions > SingleCompatibleCollision) { + recoColsWithAmbTracks.insert(recoCol); + } + ++nRecoMFTSelectedForPNch; // Inclusive reconstructed-MC denominator: fill every selected track, @@ -3234,6 +3424,33 @@ struct PseudorapidityDensityMFT { } // hasmclable } // track loop } // track>mid + if constexpr (requires { tracks.begin().bestDCAZ(); }) { + if (!recoEventsWithRightTracks.empty()) { + registry.fill(HIST("Association/hNumRecoEventsWithRightTracks"), 1., + static_cast(recoEventsWithRightTracks.size())); + } + if (!recoEventsWithWrongTracks.empty()) { + registry.fill(HIST("Association/hNumRecoEventsWithWrongTracks"), 1., + static_cast(recoEventsWithWrongTracks.size())); + } + for (const int id : recoEventsWithRightTracks) { + registry.fill(HIST("Association/RecoEventVzWithRightTracks"), recoVtxZ.at(id)); + } + for (const int id : recoEventsWithWrongTracks) { + registry.fill(HIST("Association/RecoEventVzWithWrongTracks"), recoVtxZ.at(id)); + } + } + + // Sets contain distinct reconstructed collisions, even when several reco + // collisions belong to this generated event. Mixed events enter both sets. + if (!recoColsWithNonAmbTracks.empty()) { + registry.fill(HIST("Purity/hNumCollisionsNonAmb_InelMFT"), 1., + static_cast(recoColsWithNonAmbTracks.size())); + } + if (!recoColsWithAmbTracks.empty()) { + registry.fill(HIST("Purity/hNumCollisionsAmb_InelMFT"), 1., + static_cast(recoColsWithAmbTracks.size())); + } registry.fill(HIST("Purity/HashTableRowCounts"), static_cast(HashTableRowCountsBin::UniqueBestRecoCols), uniqueBestRecoCols.size()); registry.fill(HIST("Purity/reco/woOrp/nTrk"), woOrpCount); @@ -3323,6 +3540,10 @@ struct PseudorapidityDensityMFT { registry.fill(HIST("Purity/mc/PrimaryAllEta"), particle.eta()); registry.fill(HIST("Purity/mc/PrimaryTracksEtaZvtx"), particle.eta(), mcCollision.posZ()); registry.fill(HIST("Purity/mc/PrimaryTracksPhiEta"), particle.phi(), particle.eta()); + for (const auto& [offlineRecoId, offlineRecoZ] : recoVtxZ) { + registry.fill(HIST("OfflineVariations/GenPrimaryEtaTrueZRecoZ"), + particle.eta(), mcCollision.posZ(), offlineRecoZ); + } } if (atLeastOneSel8VzGt0) { diff --git a/PWGLF/Tasks/GlobalEventProperties/dndetaHi.cxx b/PWGLF/Tasks/GlobalEventProperties/dndetaHi.cxx index 2da575aed40..de366208ddb 100644 --- a/PWGLF/Tasks/GlobalEventProperties/dndetaHi.cxx +++ b/PWGLF/Tasks/GlobalEventProperties/dndetaHi.cxx @@ -14,11 +14,13 @@ /// \author hyungjun lee , joonsuk bae #include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGLF/DataModel/mcCentrality.h" #include "PWGMM/Mult/DataModel/Index.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/McCollisionExtra.h" #include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" @@ -40,9 +42,12 @@ #include #include +#include #include #include #include +#include +#include #include using namespace o2; @@ -51,13 +56,15 @@ using namespace o2::framework::expressions; using namespace o2::aod::track; using namespace o2::aod; -using BCsRun3 = soa::Join; +using BCsRun3Cent = soa::Join; using MyCollisions = soa::Join; using MyCollisionsCent = soa::Join; using FullBCs = soa::Join; using DaughterTrack = soa::Join; using ExTracks = soa::Join; using FiTracks = soa::Filtered; +using ExDataTracks = soa::Join; +using FiDataTracks = soa::Filtered; using Particles = soa::Join; using LabeledTracks = soa::Join; using LabeledTracksEx = soa::Join; @@ -74,6 +81,12 @@ enum EventCategory { EvtDd, EvtSd, EvtNd, + // Append only: preserve existing category indices for nominal results. + // Combined with TrigSel8g0 these give MB>0 process-resolved corrections + // with the same generated INEL>0 target as the nominal correction. + EvtInelg0Dd, + EvtInelg0Sd, + EvtInelg0Nd, EvtEnd }; @@ -153,6 +166,67 @@ constexpr float PtWeightInterceptDown = 0.5f; // Sentinel value used as centrality when no centrality information is available constexpr float NoCentrality = -1.0f; +// Source used to assign the detector-level centrality of an MC collision. +// The BC fallback is deliberately restricted to generated collisions with no +// reconstructed collision. All other nominal entries retain the official +// collision-level (vertex-z equalized) FT0M percentile. +enum CentralitySource { + CentSourceRecoFT0M = 1, + CentSourceZeroRecoBCFT0M, + CentSourceZeroRecoNoFT0, + CentSourceZeroRecoInvalidBCFT0M, + CentSourceZeroRecoNoBC, + CentSourceInvalidRecoFT0M +}; + +enum ZeroRecoCentralityClass { + ZeroRecoCent0To1 = 1, + ZeroRecoCent1To5, + ZeroRecoCent5To10, + ZeroRecoCent10To15, + ZeroRecoCent15To20, + ZeroRecoCent20To30, + ZeroRecoCent30To40, + ZeroRecoCent40To50, + ZeroRecoCent50To70, + ZeroRecoCent70To100, + ZeroRecoCentNoFT0, + ZeroRecoCentInvalid, + ZeroRecoCentNoBC +}; + +ZeroRecoCentralityClass getZeroRecoCentralityClass(float cent) +{ + if (cent < 1.f) { + return ZeroRecoCent0To1; + } + if (cent < 5.f) { + return ZeroRecoCent1To5; + } + if (cent < 10.f) { + return ZeroRecoCent5To10; + } + if (cent < 15.f) { + return ZeroRecoCent10To15; + } + if (cent < 20.f) { + return ZeroRecoCent15To20; + } + if (cent < 30.f) { + return ZeroRecoCent20To30; + } + if (cent < 40.f) { + return ZeroRecoCent30To40; + } + if (cent < 50.f) { + return ZeroRecoCent40To50; + } + if (cent < 70.f) { + return ZeroRecoCent50To70; + } + return ZeroRecoCent70To100; +} + // Bin indices of the "Selection" counter histogram — values must match SetBinLabel calls in init() enum SelectionStep { SelBegin = 0, @@ -191,13 +265,16 @@ static constexpr TrackSelectionFlags::flagtype TrackSelectionDCA = struct DndetaHi { SliceCache cache; Preslice perMCCol = aod::mcparticle::mcCollisionId; - Preslice perCol = aod::track::collisionId; + Preslice perDataCol = aod::track::collisionId; Service pdg; Configurable estimatorEta{"estimatorEta", 2.0, "eta range for INEL>0 sample definition"}; Configurable useEvSel{"useEvSel", true, "use event selection"}; Configurable isPbPb{"isPbPb", false, "Is Pb-Pb"}; + Configurable bookFactorizedCorrections{"bookFactorizedCorrections", true, "Additional event/signal-loss inputs in MCCounting; leaves nominal corrections unchanged"}; + Configurable bookMultiplicityCalibration{"bookMultiplicityCalibration", false, "Independent raw generated-FT0 multiplicity calibration and correction inputs; no change to nominal classes"}; + ConfigurableAxis calibrationRecoCentrality{"calibrationRecoCentrality", {1000, 0., 100.}, "Data FT0M percentile axis for offline class boundaries"}; Configurable dcaV0Dau{"dcaV0Dau", 1.5, "DCA V0 Daughters"}; Configurable dcaNegToPV{"dcaNegToPV", 0.06, "DCA Neg To PV"}; @@ -208,6 +285,10 @@ struct DndetaHi { Configurable v0Rapidity{"v0Rapidity", 0.5, "V0 rapidity"}; ConfigurableAxis centrality{"centrality", {VARIABLE_WIDTH, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24, 26, 28, 30, 35, 40, 45, 50, 60, 70, 80, 90, 100}, ""}; Configurable zvtxcut{"zvtxcut", false, "z vtx cut < 10cm"}; + // Only used by processMCUnfolding. Neither the nominal centrality nor any + // nominal histogram depends on these options or on McCentFT0Ms. + ConfigurableAxis unfoldingCentrality{"unfoldingCentrality", {VARIABLE_WIDTH, 0, 1, 5, 10, 15, 20, 30, 40, 50, 70, 100}, "Independent generator/reconstructed FT0M response binning"}; + Configurable unfoldingZMax{"unfoldingZMax", 10.f, "Truth AND measured |z| limit for the separate unfolding sample"}; HistogramRegistry registry{ "registry", @@ -245,12 +326,91 @@ struct DndetaHi { Partition mcSample = nabs(aod::mcparticle::eta) < estimatorEta; Partition tSample = nabs(aod::track::eta) < estimatorEta; - Partition tSample3 = nabs(aod::track::eta) < estimatorEta; + Partition tSampleData = nabs(aod::track::eta) < estimatorEta; void init(InitContext&) { + if (doprocessMCCounting && doprocessMCCountingPVCalibrated) { + LOGP(fatal, "Choose either detector-level MCCounting or PV-calibrated MCCounting, never both"); + } + if (doprocessMCCountingPVCalibrated) { + if (isPbPb || doprocessGen || doprocessMCUnfolding) { + LOGP(fatal, "PV-matched classes support pp corrections only; disable processGen and quantile unfolding"); + } + registry.add("pvMatched/hGenClassINELgt0", "PV-matched class, generated INEL>0;class;events", HistType::kTH1D, {{centrality, "PV-matched FT0M class (%)"}}); + } + if (bookMultiplicityCalibration) { + if (isPbPb) { + LOGP(fatal, "bookMultiplicityCalibration currently supports pp FT0M only"); + } + const AxisSpec nForwardAxis{5001, -0.5, 5000.5, "N_{FT0M,gen} (charged physical primaries)"}; + const AxisSpec nPVAxis{1001, -0.5, 1000.5, "Reconstructed PV contributors"}; + const AxisSpec calibCentAxis{calibrationRecoCentrality, "Reconstructed FT0M percentile (%)"}; + const AxisSpec recoCountAxis{11, -0.5, 10.5, "Number of reconstructed collisions"}; + // Raw integer N_forward, NOT a preassigned percentile. Retain all event + // classes (including non-INEL>0) so event contamination can be audited. + if (doprocessCounting) { + registry.add("multCalib/hDataEvents", "Selected data;trigger;z_rec;cent_rec;N_PV", HistType::kTHnSparseD, {trigClassAxis, zAxis, calibCentAxis, nPVAxis}); + registry.add("multCalib/hDataTracks", "Selected data tracks;trigger;z_rec;eta_rec;cent_rec", HistType::kTHnSparseD, {trigClassAxis, zAxis, etaAxis, calibCentAxis}); + } + if (doprocessMCCounting || doprocessMCCountingPVCalibrated) { + registry.add("multCalib/hGenEvents", "All truth including zero reco;eventClass;z_gen;N_forward;N_reco", HistType::kTHnSparseD, {evtClassAxis, zAxis, nForwardAxis, recoCountAxis}); + registry.add("multCalib/hGenParticles", "All charged primaries;eventClass;z_gen;eta_gen;N_forward", HistType::kTHnSparseD, {evtClassAxis, zAxis, etaAxis, nForwardAxis}); + registry.add("multCalib/hSelectedEvents", "Selected best reco;eventClass;trigger;z_gen;z_rec;N_forward;N_PV", HistType::kTHnSparseD, {evtClassAxis, trigClassAxis, zAxis, zAxis, nForwardAxis, nPVAxis}); + registry.add("multCalib/hSelectedGenParticles", "All charged primaries in selected event;eventClass;trigger;z_gen;z_rec;eta_gen;N_forward", HistType::kTHnSparseD, {evtClassAxis, trigClassAxis, zAxis, zAxis, etaAxis, nForwardAxis}); + registry.add("multCalib/hSelectedRecTracks", "All filtered tracks in selected best reco;eventClass;trigger;z_gen;z_rec;eta_rec;N_forward", HistType::kTHnSparseD, {evtClassAxis, trigClassAxis, zAxis, zAxis, etaAxis, nForwardAxis}); + registry.add("multCalib/hClassCorrelation", "Selected best reco;trigger;z_gen;z_rec;N_forward;cent_rec", HistType::kTHnSparseD, {trigClassAxis, zAxis, zAxis, nForwardAxis, calibCentAxis}); + } + } + if (doprocessMCUnfoldingCalib) { + const AxisSpec trueMultAxis{5001, -0.5, 5000.5, "Generated charged-primary FT0A+FT0C multiplicity"}; + registry.add("unfoldingCalib/hFT0MInelGt0", "Generated INEL>0, all z", HistType::kTH1D, {trueMultAxis}); + registry.add("unfoldingCalib/hFT0MInelGt0Z10", "Generated INEL>0, |z_gen|<10 cm", HistType::kTH1D, {trueMultAxis}); + } + if (doprocessMCCounting && doprocessGen) { + LOGP(fatal, "processMCCounting and processGen both fill the nominal generator histograms; enable only processMCCounting for MC corrections"); + } + if (doprocessMCUnfolding) { + if (isPbPb || !std::isfinite(unfoldingZMax.value) || unfoldingZMax <= 0.f || unfoldingZMax > 30.f || + !std::isfinite(estimatorEta.value) || estimatorEta <= 0.f || estimatorEta > 4.f) { + LOGP(fatal, "processMCUnfolding requires pp, 0 < unfoldingZMax <= 30 cm and 0 < estimatorEta <= 4"); + } + const AxisSpec responseCentAxis = {unfoldingCentrality, "FT0M percentile (%)"}; + const AxisSpec extraReasonAxis = {9, 0.5, 9.5, "1:no collision label;2:outside truth event sample;3:split non-best;4:invalid reco percentile;5:no particle label;6:other MC collision;7:not charged primary;8:outside truth eta;9:duplicate track"}; + // Full truth denominators include zero-reco and failed-selection events. + // Primary particles use unit weights; this is an independent nominal + // unfolding input, not a replacement for the legacy pT-variation maps. + registry.add("unfolding/hGenEvents", "Generated INEL>0;z_gen;cent_gen", HistType::kTHnSparseD, {zAxis, responseCentAxis}); + registry.add("unfolding/hGenParticles", "Generated charged primaries;z_gen;eta_gen;cent_gen;species", HistType::kTHnSparseD, {zAxis, etaAxis, responseCentAxis, particleTypeAxis}); + registry.add("unfolding/hRecEvents", "All selected MC reconstructed collisions;trigger;z_rec;cent_rec", HistType::kTHnSparseD, {trigClassAxis, zAxis, responseCentAxis}); + registry.add("unfolding/hRecParticles", "All selected MC tracks;trigger;z_rec;eta_rec;cent_rec", HistType::kTHnSparseD, {trigClassAxis, zAxis, etaAxis, responseCentAxis}); + registry.add("unfolding/hEventResponse", "One best collision per generated event;trigger;z_gen;z_rec;cent_gen;cent_rec", HistType::kTHnSparseD, {trigClassAxis, zAxis, zAxis, responseCentAxis, responseCentAxis}); + registry.add("unfolding/hParticleResponse", "Unique primary track matches;trigger;z_gen;z_rec;eta_gen;eta_rec;cent_gen;cent_rec;species", HistType::kTHnSparseD, {trigClassAxis, zAxis, zAxis, etaAxis, etaAxis, responseCentAxis, responseCentAxis, particleTypeAxis}); + registry.add("unfolding/hMissEvents", "Truth events without a selected match;trigger;z_gen;cent_gen", HistType::kTHnSparseD, {trigClassAxis, zAxis, responseCentAxis}); + registry.add("unfolding/hMissParticles", "Truth particles without a selected match;trigger;z_gen;eta_gen;cent_gen;species", HistType::kTHnSparseD, {trigClassAxis, zAxis, etaAxis, responseCentAxis, particleTypeAxis}); + registry.add("unfolding/hExtraEvents", "Measured collisions outside the one-to-one signal response;trigger;z_rec;cent_rec;reason", HistType::kTHnSparseD, {trigClassAxis, zAxis, responseCentAxis, extraReasonAxis}); + registry.add("unfolding/hExtraParticles", "Measured tracks outside the one-to-one signal response;trigger;z_rec;eta_rec;cent_rec;reason", HistType::kTHnSparseD, {trigClassAxis, zAxis, etaAxis, responseCentAxis, extraReasonAxis}); + } AxisSpec centAxis = {centrality, "", "centrality"}; AxisSpec centAxisPbPb = {centrality, "", "centrality"}; + if ((doprocessMCCounting || doprocessMCCountingPVCalibrated) && bookFactorizedCorrections) { + // Independent inputs for detector correction followed by f_event/f_signal. + // All truth includes zero-reco. Selected truth counts ALL charged primaries + // in the selected best collision's MC event, not only track-matched ones. + // Keep both vertex coordinates to permit explicit, consistent acceptance. + const AxisSpec lossCentAxis = {centrality, "Nominal detector percentile (%)"}; + registry.add("factorized/hGenEvents", "INEL>0 before selection;z_gen;cent", HistType::kTHnSparseD, {zAxis, lossCentAxis}); + registry.add("factorized/hGenParticles", "Charged primaries before selection;z_gen;eta_gen;cent", HistType::kTHnSparseD, {zAxis, etaAxis, lossCentAxis}); + registry.add("factorized/hSelectedEvents", "Unique INEL>0 events with selected best reco;trigger;z_gen;z_rec;cent", HistType::kTHnSparseD, {trigClassAxis, zAxis, zAxis, lossCentAxis}); + registry.add("factorized/hSelectedGenParticles", "ALL charged primaries in selected events;trigger;z_gen;z_rec;eta_gen;cent", HistType::kTHnSparseD, {trigClassAxis, zAxis, zAxis, etaAxis, lossCentAxis}); + registry.add("factorized/hSelectedRecParticles", "ALL filtered tracks in selected events;trigger;z_gen;z_rec;eta_rec;cent", HistType::kTHnSparseD, {trigClassAxis, zAxis, zAxis, etaAxis, lossCentAxis}); + } + if (doprocessMCCounting || doprocessMCCountingPVCalibrated) { + registry.add("selectionLoss/hAllEvents", "All INEL>0;z_gen;cent", HistType::kTHnSparseD, {zAxis, centAxis}); + registry.add("selectionLoss/hSelectedEvents", "Selected INEL>0;trigger;z_gen;z_rec;cent", HistType::kTHnSparseD, {trigClassAxis, zAxis, zAxis, centAxis}); + registry.add("selectionLoss/hAllParticles", "All INEL>0 primaries;z_gen;eta;cent;species;ptvar;phi", HistType::kTHnSparseD, {zAxis, etaAxis, centAxis, particleTypeAxis, ptVarAxis, phiBin}); + registry.add("selectionLoss/hSelectedParticles", "All primaries in selected INEL>0 events;trigger;z_gen;z_rec;eta;cent;species;ptvar;phi", HistType::kTHnSparseD, {trigClassAxis, zAxis, zAxis, etaAxis, centAxis, particleTypeAxis, ptVarAxis, phiBin}); + } registry.add({"hetaresponse", ";etaresponse", {HistType::kTH2D, {{80, -4, 4}, {80, -4, 4}}}}); registry.add({"hft0multiplicity", ";multiplicity", {HistType::kTH1D, {{10000, 0, 100000}}}}); registry.add({"hcentrality", isPbPb ? " ; centrality_FT0C (%) " : "; centrality_FT0M", {HistType::kTH1F, {{10000, 0, 100}}}}); @@ -274,6 +434,15 @@ struct DndetaHi { registry.add({"genzINELg0", ";z", {HistType::kTH1D, {zAxis}}}); registry.add({"hcentmult", ";status;events", {HistType::kTH1D, {{100, 0, 100}}}}); + // MC-only bookkeeping for the zero-reconstructed-vertex FT0M fallback. + // These histograms are filled for generated INEL>0 collisions with + // |z_gen| < 10 cm, i.e. the event sample relevant to the correction. + registry.add({"hMCRecoCollisionsPerGenINELg0", ";N_{reco collisions};events", {HistType::kTH1D, {{6, -0.5, 5.5}}}}); + registry.add({"hMCCentralitySourceINELg0", ";centrality source;events", {HistType::kTH1D, {{6, 0.5, 6.5}}}}); + registry.add({"hZeroRecoBCFT0MPercentileINELg0", ";BC FT0M percentile;events", {HistType::kTH1D, {{103, -1.5, 101.5}}}}); + registry.add({"hZeroRecoBCFT0MClassINELg0", ";BC FT0M class;events", {HistType::kTH1D, {{13, 0.5, 13.5}}}}); + registry.add({"hBCFT0MvsRecoFT0MINELg0", ";collision FT0M percentile;BC FT0M percentile", {HistType::kTH2D, {{100, 0, 100}, {102, 0, 102}}}}); + constexpr int NLogBins = 50; constexpr double PtLogLow = 0.01; constexpr double PtLogHigh = 10.0; @@ -284,6 +453,17 @@ struct DndetaHi { } AxisSpec ptBins = {logBins, "pT (GeV/c)", "pt bin"}; + if (doprocessMCCounting || doprocessMCCountingPVCalibrated) { + std::vector trackingPtEdges; + for (int i = 0; i <= 100; ++i) { + trackingPtEdges.push_back(0.01 * std::pow(200.0 / 0.01, i / 100.0)); + } + const AxisSpec trackingPtAxis{trackingPtEdges, "p_{T}^{gen} (GeV/c)"}; + registry.add("trackingPt/hAllGenerated", "Charged physical primaries;eventClass;FT0M;pt_gen", HistType::kTHnSparseD, {evtClassAxis, isPbPb ? centAxisPbPb : centAxis, trackingPtAxis}); + registry.add("trackingPt/hSelectedGenerated", "Denominator: same selected best collision;eventClass;trigger;FT0M;pt_gen", HistType::kTHnSparseD, {evtClassAxis, trigClassAxis, isPbPb ? centAxisPbPb : centAxis, trackingPtAxis}); + registry.add("trackingPt/hMatchedPrimary", "Numerator: unique matched physical primaries;eventClass;trigger;FT0M;pt_gen", HistType::kTHnSparseD, {evtClassAxis, trigClassAxis, isPbPb ? centAxisPbPb : centAxis, trackingPtAxis}); + } + registry.add({"hrecdndpt", " pt", {HistType::kTH1D, {ptBins}}}); registry.add({"hdndptefficiency", " pt", {HistType::kTH1D, {ptBins}}}); registry.add({"hgendndpt", " pt", {HistType::kTH1D, {ptBins}}}); @@ -309,6 +489,29 @@ struct DndetaHi { x->SetBinLabel(SelInelSel8Recz10, "INELSel8recz10"); x->SetBinLabel(SelInelg0Sel8g0Recz10, "INELg0Sel8g0recz10"); x->SetBinLabel(SelK0sz10Eta05, "K0Sz10eta05"); + + auto* centSourceAxis = registry.get(HIST("hMCCentralitySourceINELg0"))->GetXaxis(); + centSourceAxis->SetBinLabel(CentSourceRecoFT0M, "reco FT0M"); + centSourceAxis->SetBinLabel(CentSourceZeroRecoBCFT0M, "zero reco: BC FT0M"); + centSourceAxis->SetBinLabel(CentSourceZeroRecoNoFT0, "zero reco: no FT0"); + centSourceAxis->SetBinLabel(CentSourceZeroRecoInvalidBCFT0M, "zero reco: invalid BC FT0M"); + centSourceAxis->SetBinLabel(CentSourceZeroRecoNoBC, "zero reco: no BC"); + centSourceAxis->SetBinLabel(CentSourceInvalidRecoFT0M, "invalid reco FT0M"); + + auto* zeroRecoClassAxis = registry.get(HIST("hZeroRecoBCFT0MClassINELg0"))->GetXaxis(); + zeroRecoClassAxis->SetBinLabel(ZeroRecoCent0To1, "0-1%"); + zeroRecoClassAxis->SetBinLabel(ZeroRecoCent1To5, "1-5%"); + zeroRecoClassAxis->SetBinLabel(ZeroRecoCent5To10, "5-10%"); + zeroRecoClassAxis->SetBinLabel(ZeroRecoCent10To15, "10-15%"); + zeroRecoClassAxis->SetBinLabel(ZeroRecoCent15To20, "15-20%"); + zeroRecoClassAxis->SetBinLabel(ZeroRecoCent20To30, "20-30%"); + zeroRecoClassAxis->SetBinLabel(ZeroRecoCent30To40, "30-40%"); + zeroRecoClassAxis->SetBinLabel(ZeroRecoCent40To50, "40-50%"); + zeroRecoClassAxis->SetBinLabel(ZeroRecoCent50To70, "50-70%"); + zeroRecoClassAxis->SetBinLabel(ZeroRecoCent70To100, "70-100%"); + zeroRecoClassAxis->SetBinLabel(ZeroRecoCentNoFT0, "no FT0"); + zeroRecoClassAxis->SetBinLabel(ZeroRecoCentInvalid, "invalid percentile"); + zeroRecoClassAxis->SetBinLabel(ZeroRecoCentNoBC, "no associated BC"); } void processEventStat( @@ -346,13 +549,13 @@ struct DndetaHi { template void runCounting( const C& collisions, - const FiTracks& /*tracks*/) + const FiDataTracks& /*tracks*/) { for (const auto& collision : collisions) { BoolVector btrigc(TrigEnd, false); registry.fill(HIST("Selection"), SelAll); auto z = collision.posZ(); - auto pertracks = tSample3->sliceBy(perCol, collision.globalIndex()); + auto pertracks = tSampleData->sliceBy(perDataCol, collision.globalIndex()); auto nTrk = 0; if (collision.sel8()) { @@ -392,6 +595,19 @@ struct DndetaHi { } } + if (bookMultiplicityCalibration) { + const auto calibCent = std::isfinite(cent) && cent >= 0.f && cent <= 100.f ? std::min(cent, std::nextafter(100.f, 0.f)) : -1.f; + for (auto trigger = 1u; trigger < static_cast(TrigEnd); ++trigger) { + if (!btrigc[trigger]) { + continue; + } + registry.fill(HIST("multCalib/hDataEvents"), trigger, z, calibCent, collision.multNTracksPV()); + for (const auto& track : pertracks) { + registry.fill(HIST("multCalib/hDataTracks"), trigger, z, track.eta(), calibCent); + } + } + } + if (isPbPb) { if (std::abs(z) < CutZ && btrigc[TrigSel8]) { registry.fill(HIST("hcentrality"), cent); @@ -428,11 +644,21 @@ struct DndetaHi { PresliceUnsorted> perMcCol = o2::aod::mccollisionlabel::mcCollisionId; Preslice perMCColparticles = aod::mcparticle::mcCollisionId; Preslice perColFiLTracks = aod::track::collisionId; - using MCex = soa::Join; - - void processMCCounting( - const MCex& mcCollisions, + // McCollsExtra provides one deterministic reconstructed collision for split + // MC collisions (the collision with the largest number of contributors). + // Select exactly one correction mode through the process switches below: + // processMCCounting: official reconstructed FT0M + zero-reco BC fallback. + // processMCCountingPVCalibrated: McCentFT0Ms for both truth and reco. + // Both modes always fill ND/SD/DD INEL>0 categories, selectionLoss and + // trackingPt inputs; no separate booking switches are required. + using MCex = soa::Join; + + template + void runMCCounting( + const MCCollisions& mcCollisions, const soa::Join& collisions, + const BCsRun3Cent& /*bcs*/, + const aod::FT0s& /*ft0s*/, const Particles& mcParticles, const FiLTracks& tracks) { @@ -452,6 +678,7 @@ struct DndetaHi { auto mcz = mcCollision.posZ(); auto genz = mcz; auto nTrkGen = 0; + int nForwardGen = 0; auto particles = mcParticles.sliceBy(perMCCol, mcCollision.globalIndex()); for (const auto& particle : particles) { @@ -461,6 +688,9 @@ struct DndetaHi { auto kp = pdg->GetParticle(particle.pdgCode()); if (kp != nullptr) { if (std::abs(kp->Charge()) >= CutCharge) { + if (bookMultiplicityCalibration && ((particle.eta() > 3.5f && particle.eta() < 4.9f) || (particle.eta() > -3.3f && particle.eta() < -2.1f))) { + ++nForwardGen; + } if (std::abs(particle.eta()) < CutEta1) { nTrkGen++; } @@ -470,6 +700,10 @@ struct DndetaHi { if (nTrkGen > 0) { registry.fill(HIST("Selection"), SelGeneratedInelGt0); bevtc[EvtInelg0] = true; + // Mandatory process-resolved INEL>0 correction categories in BOTH modes. + bevtc[EvtInelg0Dd] = bevtc[EvtDd]; + bevtc[EvtInelg0Sd] = bevtc[EvtSd]; + bevtc[EvtInelg0Nd] = bevtc[EvtNd]; } if (bevtc[EvtInel]) { registry.fill(HIST("Selection"), SelInel); @@ -506,30 +740,119 @@ struct DndetaHi { } auto collisionsample = collisions.sliceBy(perMcCol, mcCollision.globalIndex()); - auto centMC = -1.f; - if (collisionsample.size() != 1) { - centMC = -1.0; - } else { + const auto nRecoCollisions = collisionsample.size(); + const bool hasRecoCollision = nRecoCollisions > 0; + const bool isCorrectionSample = bevtc[EvtInelg0] && std::abs(mcz) < CutZ; + + if (isCorrectionSample) { + registry.fill(HIST("hMCRecoCollisionsPerGenINELg0"), nRecoCollisions); + } + + // Detector-level definition: + // * >=1 reconstructed collision: official collision FT0M percentile; + // * 0 reconstructed collisions: use valid FT0M information from the + // associated BC, so these generated events and particles remain in + // a detector-estimator class even though reconstruction failed. + // If no usable BC FT0M exists, retain underflow and record the reason. + // BC FT0M and collision FT0M have different vertex-equalization inputs; + // the fallback applies only to zero-reco events and is monitored below. + // For split vertices only the deterministic best reconstructed collision + // defines the nominal class and contributes to reconstructed histograms. + auto centForCorrection = NoCentrality; + auto centSource = CentSourceInvalidRecoFT0M; + auto zeroRecoClass = ZeroRecoCentInvalid; + auto rawZeroRecoBCCent = NoCentrality; + + if (hasRecoCollision && mcCollision.bestCollisionIndex() >= 0) { for (const auto& collision : collisionsample) { - if (isPbPb) { - if constexpr (MyCollisionsCent::template contains()) { - centMC = collision.centFT0C(); + if (collision.globalIndex() == mcCollision.bestCollisionIndex()) { + if (isPbPb) { + if constexpr (MyCollisionsCent::template contains()) { + centForCorrection = collision.centFT0C(); + centSource = CentSourceRecoFT0M; + } + } else { + if constexpr (MyCollisionsCent::template contains()) { + const auto centBest = collision.centFT0M(); + if (std::isfinite(centBest) && centBest >= 0.f && centBest <= 100.f) { + centForCorrection = std::min(centBest, std::nextafter(100.f, 0.f)); + centSource = CentSourceRecoFT0M; + } else { + LOGP(warn, "Invalid reconstructed FT0M percentile {} for best collision {}; keeping this event in the centrality underflow", centBest, collision.globalIndex()); + } + } } + break; + } + } + } else if (!hasRecoCollision && !isPbPb) { + // As in the Run-2 event-level estimator approach, a failed vertex + // reconstruction must not discard otherwise available detector + // percentile information from the generated correction denominator. + // Use the BC estimator only when it has a matched FT0 and a valid + // percentile; do not invent a class for missing detector information. + centSource = CentSourceZeroRecoNoBC; + zeroRecoClass = ZeroRecoCentNoBC; + if (mcCollision.has_bc()) { + const auto mcBC = mcCollision.template bc_as(); + rawZeroRecoBCCent = mcBC.centFT0M(); + if (!mcBC.has_foundFT0()) { + centSource = CentSourceZeroRecoNoFT0; + zeroRecoClass = ZeroRecoCentNoFT0; + } else if (std::isfinite(rawZeroRecoBCCent) && rawZeroRecoBCCent >= 0.f && rawZeroRecoBCCent <= 100.f) { + centForCorrection = std::min(rawZeroRecoBCCent, std::nextafter(100.f, 0.f)); + centSource = CentSourceZeroRecoBCFT0M; + zeroRecoClass = getZeroRecoCentralityClass(rawZeroRecoBCCent); } else { - if constexpr (MyCollisionsCent::template contains()) { - centMC = collision.centFT0M(); - } + centSource = CentSourceZeroRecoInvalidBCFT0M; + zeroRecoClass = ZeroRecoCentInvalid; + } + } + } + + if (isCorrectionSample) { + registry.fill(HIST("hMCCentralitySourceINELg0"), centSource); + if (!hasRecoCollision) { + if (std::isfinite(rawZeroRecoBCCent)) { + registry.fill(HIST("hZeroRecoBCFT0MPercentileINELg0"), rawZeroRecoBCCent); + } + registry.fill(HIST("hZeroRecoBCFT0MClassINELg0"), zeroRecoClass); + } else if (!isPbPb && centSource == CentSourceRecoFT0M && mcCollision.has_bc()) { + const auto mcBC = mcCollision.template bc_as(); + const auto bcCent = mcBC.centFT0M(); + if (mcBC.has_foundFT0() && std::isfinite(bcCent) && bcCent >= 0.f && bcCent <= 100.f) { + registry.fill(HIST("hBCFT0MvsRecoFT0MINELg0"), centForCorrection, std::min(bcCent, std::nextafter(100.f, 0.f))); } } } + // Preserve detector QA and raw calibration correlation. In the optional + // PV-matched branch, ALL correction inputs use one MC-table class, + // including events with no reconstructed collision. Nominal path unchanged. + const auto detectorCentrality = centForCorrection; + if constexpr (MCCollisions::template contains()) { + const auto calibrated = mcCollision.centFT0M(); + if (!std::isfinite(calibrated) || calibrated < 0.f || calibrated >= 100.f) { + LOGP(fatal, "Invalid PV-matched MC centrality {} for MC collision {}", calibrated, mcCollision.globalIndex()); + } + centForCorrection = calibrated; + if (bevtc[EvtInelg0]) { + registry.fill(HIST("pvMatched/hGenClassINELgt0"), calibrated); + } + } + for (auto ievtc = 1u; ievtc < static_cast(EvtEnd); ievtc++) { if (bevtc[ievtc]) { - registry.fill(HIST("hgenzvtx"), ievtc, genz, centMC); + registry.fill(HIST("hgenzvtx"), ievtc, genz, centForCorrection); } } int pid = 0; + struct LossParticle { + double eta, phi, pt; + int species; + }; + std::vector lossParticles; std::vector particleetas; for (const auto& particle : particles) { auto p = pdg->GetParticle(particle.pdgCode()); @@ -558,34 +881,73 @@ struct DndetaHi { if (std::abs(p->Charge()) >= CutCharge) { for (auto ievtc = 1u; ievtc < static_cast(EvtEnd); ievtc++) { if (bevtc[ievtc]) { - registry.fill(HIST("hgendndeta"), ievtc, genz, particle.eta(), centMC, pid, PtVarNone, particle.phi()); + if (std::abs(genz) < CutZ && std::abs(particle.eta()) < CutEta08) { + registry.fill(HIST("trackingPt/hAllGenerated"), ievtc, centForCorrection, particle.pt()); + } + registry.fill(HIST("hgendndeta"), ievtc, genz, particle.eta(), centForCorrection, pid, PtVarNone, particle.phi()); if (particle.pt() < CutPt) { - registry.fill(HIST("hgendndeta"), ievtc, genz, particle.eta(), centMC, pid, PtVarUp, particle.phi(), PtWeightSlopeUp * particle.pt() + PtWeightInterceptUp); - registry.fill(HIST("hgendndeta"), ievtc, genz, particle.eta(), centMC, pid, PtVarDown, particle.phi(), PtWeightSlopeDown * particle.pt() + PtWeightInterceptDown); + const auto weightUp = PtWeightSlopeUp * particle.pt() + PtWeightInterceptUp; + const auto weightDown = PtWeightSlopeDown * particle.pt() + PtWeightInterceptDown; + registry.fill(HIST("hgendndeta"), ievtc, genz, particle.eta(), centForCorrection, pid, PtVarUp, particle.phi(), weightUp); + registry.fill(HIST("hgendndeta"), ievtc, genz, particle.eta(), centForCorrection, pid, PtVarDown, particle.phi(), weightDown); } else { - registry.fill(HIST("hgendndeta"), ievtc, genz, particle.eta(), centMC, pid, PtVarUp, particle.phi()); - registry.fill(HIST("hgendndeta"), ievtc, genz, particle.eta(), centMC, pid, PtVarDown, particle.phi()); + registry.fill(HIST("hgendndeta"), ievtc, genz, particle.eta(), centForCorrection, pid, PtVarUp, particle.phi()); + registry.fill(HIST("hgendndeta"), ievtc, genz, particle.eta(), centForCorrection, pid, PtVarDown, particle.phi()); } } } if (pid >= PartPion && pid <= PartOther) { particleetas.push_back(particle.eta()); + if (bevtc[EvtInelg0]) { + lossParticles.push_back({particle.eta(), particle.phi(), particle.pt(), pid}); + registry.fill(HIST("selectionLoss/hAllParticles"), genz, particle.eta(), centForCorrection, pid, PtVarNone, particle.phi()); + const double up = particle.pt() < CutPt ? PtWeightSlopeUp * particle.pt() + PtWeightInterceptUp : 1.; + const double down = particle.pt() < CutPt ? PtWeightSlopeDown * particle.pt() + PtWeightInterceptDown : 1.; + registry.fill(HIST("selectionLoss/hAllParticles"), genz, particle.eta(), centForCorrection, pid, PtVarUp, particle.phi(), up); + registry.fill(HIST("selectionLoss/hAllParticles"), genz, particle.eta(), centForCorrection, pid, PtVarDown, particle.phi(), down); + } } } } } - for (const auto& collision : collisionsample) { - auto centRec = -1.f; - if (isPbPb) { - if constexpr (MyCollisionsCent::template contains()) { - centRec = collision.centFT0C(); + // Full generated INEL>0 denominator. This must not depend on whether the + // event has zero, one, or several reconstructed collisions. + if (bookMultiplicityCalibration) { + for (auto eventClass = 1u; eventClass < static_cast(EvtEnd); ++eventClass) { + if (!bevtc[eventClass]) { + continue; } - } else { - if constexpr (MyCollisionsCent::template contains()) { - centRec = collision.centFT0M(); + registry.fill(HIST("multCalib/hGenEvents"), eventClass, genz, nForwardGen, nRecoCollisions); + for (const auto& eta : particleetas) { + registry.fill(HIST("multCalib/hGenParticles"), eventClass, genz, eta, nForwardGen); + } + } + } + if (bevtc[EvtInelg0]) { + for (const auto& eta : particleetas) { + registry.fill(HIST("genetaINELg0genz10"), eta, mcz); + } + registry.fill(HIST("genzINELg0"), genz); + registry.fill(HIST("selectionLoss/hAllEvents"), genz, centForCorrection); + if (bookFactorizedCorrections) { + registry.fill(HIST("factorized/hGenEvents"), genz, centForCorrection); + for (const auto& eta : particleetas) { + registry.fill(HIST("factorized/hGenParticles"), genz, eta, centForCorrection); } } + } + + for (const auto& collision : collisionsample) { + // A generated collision contributes at most one reconstructed + // collision to the correction sample. + const bool isBestCollision = collision.globalIndex() == mcCollision.bestCollisionIndex(); + if (!isBestCollision) { + continue; + } + // The loop contains only the best reconstructed collision, so use the + // same sanitized official collision centrality as the MC denominator. + const auto centRec = centForCorrection; BoolVector btrigc(TrigEnd, false); auto z = collision.posZ(); @@ -600,7 +962,7 @@ struct DndetaHi { registry.fill(HIST("hft0multiplicity"), collision.multFT0C()); registry.fill(HIST("Selection"), SelInelSel8Mcz10); } - if (collisionsample.size() == 1 && bevtc[EvtInelg0] && btrigc[TrigSel8]) { + if (isBestCollision && bevtc[EvtInelg0] && btrigc[TrigSel8]) { for (const auto& eta : particleetas) { registry.fill(HIST("genetaINELg0Sel8recz10"), eta, z); registry.fill(HIST("genetaINELg0Sel8genz10"), eta, mcz); @@ -608,12 +970,6 @@ struct DndetaHi { registry.fill(HIST("reczINELg0Sel8"), z); registry.fill(HIST("genzINELg0Sel8"), genz); } - if (collisionsample.size() == 1 && bevtc[EvtInelg0]) { - for (const auto& eta : particleetas) { - registry.fill(HIST("genetaINELg0genz10"), eta, mcz); - } - registry.fill(HIST("genzINELg0"), genz); - } auto nTrkRec = 0; auto trackspart = tracks.sliceBy(perColFiLTracks, collision.globalIndex()); @@ -628,6 +984,101 @@ struct DndetaHi { btrigc[TrigSel8g0] = true; } } + if (bookMultiplicityCalibration) { + for (auto trigger = 1u; trigger < static_cast(TrigEnd); ++trigger) { + if (!btrigc[trigger]) { + continue; + } + registry.fill(HIST("multCalib/hClassCorrelation"), trigger, genz, z, nForwardGen, detectorCentrality); + for (auto eventClass = 1u; eventClass < static_cast(EvtEnd); ++eventClass) { + if (!bevtc[eventClass]) { + continue; + } + registry.fill(HIST("multCalib/hSelectedEvents"), eventClass, trigger, genz, z, nForwardGen, collision.multNTracksPV()); + for (const auto& eta : particleetas) { + registry.fill(HIST("multCalib/hSelectedGenParticles"), eventClass, trigger, genz, z, eta, nForwardGen); + } + for (const auto& track : trackspart) { + registry.fill(HIST("multCalib/hSelectedRecTracks"), eventClass, trigger, genz, z, track.eta(), nForwardGen); + } + } + } + } + + if (bevtc[EvtInelg0]) { + for (unsigned trigger = 1; trigger < static_cast(TrigEnd); ++trigger) { + if (!btrigc[trigger]) + continue; + registry.fill(HIST("selectionLoss/hSelectedEvents"), trigger, genz, z, centRec); + for (const auto& p : lossParticles) { + registry.fill(HIST("selectionLoss/hSelectedParticles"), trigger, genz, z, p.eta, centRec, p.species, PtVarNone, p.phi); + const double up = p.pt < CutPt ? PtWeightSlopeUp * p.pt + PtWeightInterceptUp : 1.; + const double down = p.pt < CutPt ? PtWeightSlopeDown * p.pt + PtWeightInterceptDown : 1.; + registry.fill(HIST("selectionLoss/hSelectedParticles"), trigger, genz, z, p.eta, centRec, p.species, PtVarUp, p.phi, up); + registry.fill(HIST("selectionLoss/hSelectedParticles"), trigger, genz, z, p.eta, centRec, p.species, PtVarDown, p.phi, down); + } + } + } + + // Independent diagnostic: identical selected event and truth fiducial + // denominator for every reconstructed numerator particle. Deduplicate + // only these new counts; do not change nominal track categorization. + if (std::abs(genz) < CutZ && std::abs(z) < CutZ) { + std::unordered_set matchedPrimaryIds; + for (const auto& track : trackspart) { + if (!track.has_mcParticle() || std::abs(track.eta()) >= CutEta08) { + continue; + } + const auto particle = track.template mcParticle_as(); + if (particle.mcCollisionId() != mcCollision.globalIndex() || !particle.isPhysicalPrimary() || std::abs(particle.eta()) >= CutEta08) { + continue; + } + const auto kp = pdg->GetParticle(particle.pdgCode()); + if (kp != nullptr && std::abs(kp->Charge()) >= CutCharge) { + matchedPrimaryIds.insert(particle.globalIndex()); + } + } + for (const auto& particle : particles) { + if (!particle.isPhysicalPrimary() || std::abs(particle.eta()) >= CutEta08) { + continue; + } + const auto kp = pdg->GetParticle(particle.pdgCode()); + if (kp == nullptr || std::abs(kp->Charge()) < CutCharge) { + continue; + } + const bool matched = matchedPrimaryIds.count(particle.globalIndex()) != 0; + for (auto ievtc = 1u; ievtc < static_cast(EvtEnd); ++ievtc) { + if (!bevtc[ievtc]) + continue; + for (auto itrigc = 1u; itrigc < static_cast(TrigEnd); ++itrigc) { + if (!btrigc[itrigc]) + continue; + registry.fill(HIST("trackingPt/hSelectedGenerated"), ievtc, itrigc, centRec, particle.pt()); + if (matched) { + registry.fill(HIST("trackingPt/hMatchedPrimary"), ievtc, itrigc, centRec, particle.pt()); + } + } + } + } + } + if (bookFactorizedCorrections && bevtc[EvtInelg0]) { + // Best-collision guard above ensures each generated event is counted + // at most once per trigger. No additional vertex/centrality cuts here. + for (auto itrigc = 1u; itrigc < static_cast(TrigEnd); ++itrigc) { + if (!btrigc[itrigc]) { + continue; + } + registry.fill(HIST("factorized/hSelectedEvents"), itrigc, genz, z, centRec); + for (const auto& eta : particleetas) { + registry.fill(HIST("factorized/hSelectedGenParticles"), itrigc, genz, z, eta, centRec); + } + // Include backgrounds, duplicates and unlabelled tracks, as in raw + // data. Their net effect belongs in the detector correction ratio. + for (const auto& track : trackspart) { + registry.fill(HIST("factorized/hSelectedRecParticles"), itrigc, genz, z, track.eta(), centRec); + } + } + } if (isPbPb) { if (std::abs(z) < CutZ && btrigc[TrigSel8]) { registry.fill(HIST("hcentrality"), centRec); @@ -724,7 +1175,232 @@ struct DndetaHi { } } - PROCESS_SWITCH(DndetaHi, processMCCounting, "MC Count tracks", false); + void processMCCounting(const MCex& mcCollisions, + const soa::Join& collisions, + const BCsRun3Cent& bcs, const aod::FT0s& ft0s, + const Particles& particles, const FiLTracks& tracks) + { + runMCCounting(mcCollisions, collisions, bcs, ft0s, particles, tracks); + } + PROCESS_SWITCH(DndetaHi, processMCCounting, "MC Count tracks (detector FT0M with BC fallback)", false); + + using MCWithPVCalibration = soa::Join; + void processMCCountingPVCalibrated(const MCWithPVCalibration& mcCollisions, + const soa::Join& collisions, + const BCsRun3Cent& bcs, const aod::FT0s& ft0s, + const Particles& particles, const FiLTracks& tracks) + { + runMCCounting(mcCollisions, collisions, bcs, ft0s, particles, tracks); + } + PROCESS_SWITCH(DndetaHi, processMCCountingPVCalibrated, "MC corrections using PV-matched McCentFT0Ms for both generated and reconstructed entries", false); + + // Optional second-pass branch. Enabling this alongside processMCCounting + // records BOTH methods' inputs without filling any nominal histogram twice. + // Only this signature requests the generator-percentile table/calibration. + using MCWithUnfoldingCent = soa::Join; + + // First pass: independent of reconstructed vertices and percentile tables. + void processMCUnfoldingCalib(aod::McCollisions const& collisions, aod::McParticles const& particles) + { + std::unordered_map> counts; + for (const auto& collision : collisions) { + counts.emplace(collision.globalIndex(), std::array{0, 0}); + } + for (const auto& particle : particles) { + auto event = counts.find(particle.mcCollisionId()); + if (event == counts.end() || !particle.isPhysicalPrimary()) { + continue; + } + const auto* definition = pdg->GetParticle(particle.pdgCode()); + if (!definition || std::abs(definition->Charge()) < CutCharge) { + continue; + } + const auto eta = particle.eta(); + if (std::abs(eta) < CutEta1) { + ++event->second[0]; + } + if ((eta > 3.5f && eta < 4.9f) || (eta > -3.3f && eta < -2.1f)) { + ++event->second[1]; + } + } + for (const auto& collision : collisions) { + const auto& count = counts.at(collision.globalIndex()); + if (count[0] > 0) { + registry.fill(HIST("unfoldingCalib/hFT0MInelGt0"), count[1]); + if (std::abs(collision.posZ()) < CutZ) { + registry.fill(HIST("unfoldingCalib/hFT0MInelGt0Z10"), count[1]); + } + } + } + } + PROCESS_SWITCH(DndetaHi, processMCUnfoldingCalib, "First pass: generated INEL>0 FT0 multiplicity without percentile calibration", false); + + void processMCUnfolding( + const MCWithUnfoldingCent& mcCollisions, + const soa::Join& collisions, + const Particles& mcParticles, + const FiLTracks& tracks) + { + struct TruthEvent { + float z = 0.f; + float cent = NoCentrality; + int64_t bestCollision = -1; + bool inelg0 = false; + bool fiducial = false; + std::array matched{}; + }; + std::unordered_map truth; + truth.reserve(mcCollisions.size()); + std::array, TrigEnd> matchedParticles; + + auto isChargedPrimary = [this](const auto& particle) { + if (!particle.isPhysicalPrimary()) { + return false; + } + const auto* definition = pdg->GetParticle(particle.pdgCode()); + return definition != nullptr && std::abs(definition->Charge()) >= CutCharge; + }; + auto species = [](const auto& particle) { + switch (std::abs(particle.pdgCode())) { + case kPiPlus: + return PartPion; + case kKPlus: + return PartKaon; + case kProton: + return PartProton; + default: + return PartOther; + } + }; + auto validPercentile = [](float percentile) { + return std::isfinite(percentile) && percentile >= 0.f && percentile <= 100.f; + }; + auto insidePercentile = [](float percentile) { + return std::min(percentile, std::nextafter(100.f, 0.f)); + }; + + // Use global IDs, not row numbers: input slices may have nonzero offsets. + for (const auto& collision : mcCollisions) { + truth.emplace(collision.globalIndex(), TruthEvent{collision.posZ(), collision.centFT0M(), collision.bestCollisionIndex()}); + } + for (const auto& particle : mcParticles) { + auto event = truth.find(particle.mcCollisionId()); + if (event != truth.end() && isChargedPrimary(particle) && std::abs(particle.eta()) < CutEta1) { + event->second.inelg0 = true; + } + } + for (auto& [id, event] : truth) { + event.fiducial = event.inelg0 && std::abs(event.z) < unfoldingZMax; + if (!event.fiducial) { + continue; + } + if (!validPercentile(event.cent)) { + LOGP(fatal, "Invalid generated INEL>0 percentile {} for MC collision {}: processMCUnfolding requires the FIRST-PASS FT0M INEL>0 calibration, not reconstructed/BC fallback percentiles", event.cent, id); + } + event.cent = insidePercentile(event.cent); + registry.fill(HIST("unfolding/hGenEvents"), event.z, event.cent); + } + + // Traverse ALL measured collisions, including non-best split collisions + // and collisions with no MC label. Data cannot apply the MC best label. + for (const auto& collision : collisions) { + const auto z = collision.posZ(); + if (!collision.sel8() || !(std::abs(z) < unfoldingZMax)) { + continue; + } + const auto perTracks = tracks.sliceBy(perColFiLTracks, collision.globalIndex()); + bool hasTrackEta1 = false; + for (const auto& track : perTracks) { + if (std::abs(track.eta()) < estimatorEta && std::abs(track.eta()) < CutEta1) { + hasTrackEta1 = true; + break; + } + } + const auto rawCent = collision.centFT0M(); + const bool validRecoCent = validPercentile(rawCent); + const auto centRec = validRecoCent ? insidePercentile(rawCent) : NoCentrality; + auto event = truth.find(collision.mcCollisionId()); + int eventExtraReason = 0; + if (!collision.has_mcCollision() || event == truth.end()) { + eventExtraReason = 1; + } else if (!event->second.fiducial) { + eventExtraReason = 2; + } else if (collision.globalIndex() != event->second.bestCollision) { + eventExtraReason = 3; + } else if (!validRecoCent) { + eventExtraReason = 4; + } + for (int trigger = TrigSel8; trigger < TrigEnd; ++trigger) { + if (trigger == TrigSel8g0 && !hasTrackEta1) { + continue; + } + registry.fill(HIST("unfolding/hRecEvents"), trigger, z, centRec); + if (eventExtraReason != 0) { + registry.fill(HIST("unfolding/hExtraEvents"), trigger, z, centRec, eventExtraReason); + } else { + event->second.matched[trigger] = true; + registry.fill(HIST("unfolding/hEventResponse"), trigger, event->second.z, z, event->second.cent, centRec); + } + for (const auto& track : perTracks) { + // Identical measured eta acceptance to runCounting/tSampleData. + if (!(std::abs(track.eta()) < estimatorEta)) { + continue; + } + registry.fill(HIST("unfolding/hRecParticles"), trigger, z, track.eta(), centRec); + int extraReason = eventExtraReason; + if (extraReason == 0 && !track.has_mcParticle()) { + extraReason = 5; + } + if (extraReason == 0) { + const auto particle = track.template mcParticle_as(); + if (particle.mcCollisionId() != collision.mcCollisionId()) { + extraReason = 6; + } else if (!isChargedPrimary(particle)) { + extraReason = 7; + } else if (!(std::abs(particle.eta()) < estimatorEta)) { + extraReason = 8; + } else if (!matchedParticles[trigger].insert(particle.globalIndex()).second) { + extraReason = 9; + } else { + registry.fill(HIST("unfolding/hParticleResponse"), trigger, event->second.z, z, particle.eta(), track.eta(), event->second.cent, centRec, species(particle)); + } + } + if (extraReason != 0) { + registry.fill(HIST("unfolding/hExtraParticles"), trigger, z, track.eta(), centRec, extraReason); + } + } + } + } + + // Explicit miss histograms make the identities testable for EACH trigger: + // truth = response projected on truth + misses; + // measured MC = response projected on reco + extras. + for (const auto& [id, event] : truth) { + if (!event.fiducial) { + continue; + } + for (int trigger = TrigSel8; trigger < TrigEnd; ++trigger) { + if (!event.matched[trigger]) { + registry.fill(HIST("unfolding/hMissEvents"), trigger, event.z, event.cent); + } + } + } + for (const auto& particle : mcParticles) { + const auto event = truth.find(particle.mcCollisionId()); + if (event == truth.end() || !event->second.fiducial || !isChargedPrimary(particle) || !(std::abs(particle.eta()) < estimatorEta)) { + continue; + } + const auto pid = species(particle); + registry.fill(HIST("unfolding/hGenParticles"), event->second.z, particle.eta(), event->second.cent, pid); + for (int trigger = TrigSel8; trigger < TrigEnd; ++trigger) { + if (matchedParticles[trigger].find(particle.globalIndex()) == matchedParticles[trigger].end()) { + registry.fill(HIST("unfolding/hMissParticles"), trigger, event->second.z, particle.eta(), event->second.cent, pid); + } + } + } + } + + PROCESS_SWITCH(DndetaHi, processMCUnfolding, "Also store independent generated-INEL>0/reconstructed-FT0M unfolding inputs (requires McCentFT0Ms)", false); // void processTrackEfficiencyGeneral( // MyCollisionsMcLabels::iterator const& collision, @@ -746,7 +1422,7 @@ struct DndetaHi { void processCounting( const MyCollisionsCent& collisions, - const FiTracks& tracks) + const FiDataTracks& tracks) { runCounting(collisions, tracks); } diff --git a/PWGLF/Tasks/GlobalEventProperties/ptmultCorr.cxx b/PWGLF/Tasks/GlobalEventProperties/ptmultCorr.cxx index a819b5c808c..6de0a7a250f 100644 --- a/PWGLF/Tasks/GlobalEventProperties/ptmultCorr.cxx +++ b/PWGLF/Tasks/GlobalEventProperties/ptmultCorr.cxx @@ -120,12 +120,12 @@ const AxisSpec axisPhi{{0, o2::constants::math::PIQuarter, o2::constants::math:: const AxisSpec axisPhi2{629, 0, o2::constants::math::TwoPI, "#phi"}; const AxisSpec axisCent{100, 0, 100, "#Cent"}; const AxisSpec axisDeltaPt{50, -1.0, +1.0, "#Delta(pT)"}; -const AxisSpec axisDCAxy{600, -3.0, 3.0, "DCA_{xy} (cm)", "DCAxyAxis"}; // range ±3 cm for TPC-only case const AxisSpec axisGenPtVary = {kGenpTend - 1, +kGenpTbegin + 0.5, +kGenpTend - 0.5, "", "GenpTVaryAxis"}; const AxisSpec axisGenTrkType = {kGenTrkTypeend - 1, +kGenTrkTypebegin + 0.5, +kGenTrkTypeend - 0.5, "", "GenTrackTypeAxis"}; const AxisSpec axisRecTrkType = {kRecTrkTypeend - 1, +kRecTrkTypebegin + 0.5, +kRecTrkTypeend - 0.5, "", "RecTrackTypeAxis"}; const AxisSpec axisTrackType = {kTrackTypeend - 1, +kTrackTypebegin + 0.5, +kTrackTypeend - 0.5, "", "TrackTypeAxis"}; auto static constexpr KminCharge = 3.f; +static constexpr float kZeroFloat{0.f}; struct PtmultCorr { @@ -150,6 +150,69 @@ struct PtmultCorr { Configurable cfgMaxChi2ClsITS{"cfgMaxChi2ClsITS", 36.0f, "Maximum ITS chi2/cls"}; Configurable cfgDCAz{"cfgDCAz", 0.04, "DCAz cut in cm"}; + // ── pT-dependent DCAxy cut ────────────────────────────────────────────────── + // The THnSparses keep the FULL DCAxy distribution (needed for the template fit). + // When the flag is on, additional *DcaCut histograms (no DCAxy axis) are + // filled only with tracks passing |DCAxy| < N * (A + B/pT^C), using reco pT. + Configurable cfgApplyPtDepDcaXY{"cfgApplyPtDepDcaXY", false, "fill *DcaCut spectra with |DCAxy| < N*(A + B/pT^C)"}; + Configurable cfgDcaXYSigmaA{"cfgDcaXYSigmaA", 0.0015f, "sigma_DCAxy(pT) = A + B/pT^C : A (cm)"}; + Configurable cfgDcaXYSigmaB{"cfgDcaXYSigmaB", 0.0050f, "sigma_DCAxy(pT) = A + B/pT^C : B (cm (GeV/c)^C)"}; + Configurable cfgDcaXYSigmaC{"cfgDcaXYSigmaC", 1.1f, "sigma_DCAxy(pT) = A + B/pT^C : C"}; + Configurable cfgDcaXYNSigma{"cfgDcaXYNSigma", 7.f, "number of sigmas for the DCAxy cut"}; + + // DCAxy binning of the THnSparses, by regions in |DCAxy| from 0 outwards: + // region i spans (edge[i-1], edge[i]] with bin width width[i]. + // Default = previous uniform binning (20 um over +-5 cm). + // Finer core, e.g.: edges {0.05, 0.5, 5.0}, widths {0.0005, 0.005, 0.1} + Configurable> cfgDcaXYRegionEdges{"cfgDcaXYRegionEdges", {5.0}, "DCAxy binning: outer |DCAxy| edge of each region (cm), increasing"}; + Configurable> cfgDcaXYRegionWidths{"cfgDcaXYRegionWidths", {0.002}, "DCAxy binning: bin width in each region (cm)"}; + + // Fine DCAxy vs reco pT of primaries (sigma(pT) extraction, MC only) + ConfigurableAxis binsDcaXYFine{"binsDcaXYFine", {4000, -0.4, 0.4}, "fine DCAxy binning for sigma(pT) (cm)"}; + ConfigurableAxis binsPtDcaFine{"binsPtDcaFine", {VARIABLE_WIDTH, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1.0, 1.1, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.6, 4.0, 5.0, 6.0, 8.0, 10.0, 13.0, 20.0, 30.0, 50.0}, "reco pT binning of the fine DCAxy histograms"}; + + bool passDcaXY(float pt, float dcaXY) const + { + if (pt <= kZeroFloat) { + return false; + } + const float sigma = cfgDcaXYSigmaA.value + cfgDcaXYSigmaB.value / std::pow(pt, cfgDcaXYSigmaC.value); + return std::fabs(dcaXY) < cfgDcaXYNSigma.value * sigma; + } + + static std::vector makeDcaXYEdges(const std::vector& regionEdges, const std::vector& regionWidths) + { + if (regionEdges.empty() || regionEdges.size() != regionWidths.size()) { + LOGF(fatal, "DCAxy binning: %zu region edges vs %zu widths", regionEdges.size(), regionWidths.size()); + } + std::vector pos{0.}; + double x0 = 0.; + for (size_t i = 0; i < regionEdges.size(); ++i) { + const double xMax = regionEdges[i]; + const double w = regionWidths[i]; + if (xMax <= x0 || w <= kZeroFloat) { + LOGF(fatal, "DCAxy binning: region %zu invalid (edge %g after %g, width %g)", i, xMax, x0, w); + } + const int n = std::max(1, static_cast(std::lround((xMax - x0) / w))); + for (int j = 1; j <= n; ++j) { + pos.push_back(x0 + j * (xMax - x0) / n); // integer steps: region edges are exact + } + if (std::fabs((xMax - x0) / n - w) > kWidthRelTol * w) { + LOGF(warning, "DCAxy binning: region %zu width adjusted from %g to %g cm to fit the range", i, w, (xMax - x0) / n); + } + x0 = xMax; + } + std::vector edges; + for (auto it = pos.rbegin(); it != pos.rend(); ++it) { + if (*it > kZeroFloat) { + edges.push_back(-*it); + } + } + edges.insert(edges.end(), pos.begin(), pos.end()); + LOGF(info, "DCAxy axis: %zu bins in [%g, %g] cm", edges.size() - 1, edges.front(), edges.back()); + return edges; + } + ConfigurableAxis ptHistBin{"ptHistBin", {200, 0., 20.}, ""}; ConfigurableAxis centralityBinning{"centralityBinning", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, ""}; ConfigurableAxis binsImpactPar{"binsImpactPar", {VARIABLE_WIDTH, 0.0, 3.00065, 4.28798, 6.14552, 7.6196, 8.90942, 10.0897, 11.2002, 12.2709, 13.3167, 14.4173, 23.2518}, "Binning of the impact parameter axis"}; @@ -183,6 +246,7 @@ struct PtmultCorr { static constexpr int kZeroInt{0}; static constexpr float kOne{1.0f}; + static constexpr double kWidthRelTol{1e-3}; void init(InitContext const&) { @@ -192,6 +256,9 @@ struct PtmultCorr { AxisSpec centAxis = {centralityBinning, "Centrality", "CentralityAxis"}; AxisSpec axisPt = {ptHistBin, "pT", "pTAxis"}; AxisSpec impactParAxis = {binsImpactPar, "Impact Parameter"}; + const AxisSpec axisDCAxy{makeDcaXYEdges(cfgDcaXYRegionEdges.value, cfgDcaXYRegionWidths.value), "DCA_{xy} (cm)", "DCAxyAxis"}; + const AxisSpec axisDcaXYFine{binsDcaXYFine, "DCA_{xy} (cm)", "DCAxyFineAxis"}; + const AxisSpec axisPtDcaFine{binsPtDcaFine, "#it{p}_{T}^{rec} (GeV/#it{c})", "pTDcaFineAxis"}; histos.add("EventHist", "EventHist", kTH1D, {axisEvent}, false); histos.add("VtxZHist", "VtxZHist", kTH1D, {axisVtxZ}, false); @@ -202,10 +269,16 @@ struct PtmultCorr { if (doprocessDataPbPb) { histos.add("hdatazvtxcent", "hdatazvtxcent", kTH2D, {axisVtxZ, centAxis}, false); histos.add("hdatahistPbPb", "hdatahistPbPb", kTHnSparseD, {axisVtxZ, centAxis, axisPt, axisPhi, axisTrackType, axisDCAxy}, false); + if (cfgApplyPtDepDcaXY) { + histos.add("hdatahistPbPbDcaCut", "hdatahistPbPbDcaCut", kTHnSparseD, {axisVtxZ, centAxis, axisPt, axisPhi, axisTrackType}, false); + } } if (doprocessDatapp) { histos.add("hdatahistpp", "hdatahistpp", kTHnSparseD, {axisVtxZ, axisPt, axisPhi, axisTrackType, axisDCAxy}, false); + if (cfgApplyPtDepDcaXY) { + histos.add("hdatahistppDcaCut", "hdatahistppDcaCut", kTHnSparseD, {axisVtxZ, axisPt, axisPhi, axisTrackType}, false); + } } if (doprocessMCeffPbPb) { @@ -221,7 +294,14 @@ struct PtmultCorr { histos.add("hPbPbRecMCvtxzcent", "hPbPbRecMCvtxzcent", kTH2D, {axisVtxZ, centAxis}, false); histos.add("hPbPbRecMCcent", "hPbPbRecMCcent", kTH1D, {axisCent}, false); histos.add("hPbPbRecMCdndpt", "hPbPbRecMCdndpt", kTHnSparseD, {axisVtxZ, centAxis, axisPt, axisPhi, axisRecTrkType, axisTrackType, axisDCAxy}, false); + if (cfgApplyPtDepDcaXY) { + histos.add("hPbPbRecMCdndptDcaCut", "hPbPbRecMCdndptDcaCut", kTHnSparseD, {axisVtxZ, centAxis, axisPt, axisPhi, axisRecTrkType, axisTrackType}, false); + } histos.add("hPbPbEtaReso", "hPbPbEtaReso", kTH2D, {axisPt, axisDeltaPt}); + // DCAxy of reco primaries (global tracks, same MC collision) vs RECO pT and centrality + histos.add("hPbPbDcaXYPrimFine", "hPbPbDcaXYPrimFine", kTH3F, {axisPtDcaFine, axisDcaXYFine, centAxis}, false); + // primaries whose MC particle belongs to another MC collision (mis-association) + histos.add("hPbPbDcaXYPrimWrongColl", "hPbPbDcaXYPrimWrongColl", kTH2F, {axisPtDcaFine, axisDcaXYFine}, false); } if (doprocessMCeffpp) { @@ -233,7 +313,12 @@ struct PtmultCorr { histos.add("hppRecMCvtxz", "hppRecMCvtxz", kTH1D, {axisVtxZ}, false); histos.add("hppRecMCdndpt", "hppRecMCdndpt", kTHnSparseD, {axisVtxZ, axisPt, axisPhi, axisRecTrkType, axisTrackType, axisDCAxy}, false); + if (cfgApplyPtDepDcaXY) { + histos.add("hppRecMCdndptDcaCut", "hppRecMCdndptDcaCut", kTHnSparseD, {axisVtxZ, axisPt, axisPhi, axisRecTrkType, axisTrackType}, false); + } histos.add("hppEtaReso", "hppEtaReso", kTH2D, {axisPt, axisDeltaPt}); + histos.add("hppDcaXYPrimFine", "hppDcaXYPrimFine", kTH2F, {axisPtDcaFine, axisDcaXYFine}, false); + histos.add("hppDcaXYPrimWrongColl", "hppDcaXYPrimWrongColl", kTH2F, {axisPtDcaFine, axisDcaXYFine}, false); } if (doprocessEvtLossSigLossMCpp || doprocessEvtLossSigLossMCPbPb) { @@ -389,6 +474,9 @@ struct PtmultCorr { return true; } + // All track cuts EXCEPT the pT-dependent DCAxy cut (the flat DCAz cut is included). + // The DCAxy cut is applied at the fill points via passDcaXY(), so that the + // THnSparses keep the full DCAxy distribution for the template fit. template bool isTrackSelected(CheckTrack const& track) { @@ -512,11 +600,14 @@ struct PtmultCorr { if (!isTrackSelected(track)) { continue; } + auto trkType = track.hasTPC() ? kGlobalonly : kITSonly; + // full DCAxy distribution (template fit) histos.fill(HIST("hdatahistPbPb"), cols.posZ(), cols.centFT0C(), track.pt(), track.phi(), kGlobalplusITS, track.dcaXY()); - if (track.hasTPC()) { - histos.fill(HIST("hdatahistPbPb"), cols.posZ(), cols.centFT0C(), track.pt(), track.phi(), kGlobalonly, track.dcaXY()); - } else { - histos.fill(HIST("hdatahistPbPb"), cols.posZ(), cols.centFT0C(), track.pt(), track.phi(), kITSonly, track.dcaXY()); + histos.fill(HIST("hdatahistPbPb"), cols.posZ(), cols.centFT0C(), track.pt(), track.phi(), trkType, track.dcaXY()); + // spectrum with the pT-dependent DCAxy cut + if (cfgApplyPtDepDcaXY && passDcaXY(track.pt(), track.dcaXY())) { + histos.fill(HIST("hdatahistPbPbDcaCut"), cols.posZ(), cols.centFT0C(), track.pt(), track.phi(), kGlobalplusITS); + histos.fill(HIST("hdatahistPbPbDcaCut"), cols.posZ(), cols.centFT0C(), track.pt(), track.phi(), trkType); } } } @@ -539,11 +630,14 @@ struct PtmultCorr { if (!isTrackSelected(track)) { continue; } + auto trkType = track.hasTPC() ? kGlobalonly : kITSonly; + // full DCAxy distribution (template fit) histos.fill(HIST("hdatahistpp"), cols.posZ(), track.pt(), track.phi(), kGlobalplusITS, track.dcaXY()); - if (track.hasTPC()) { - histos.fill(HIST("hdatahistpp"), cols.posZ(), track.pt(), track.phi(), kGlobalonly, track.dcaXY()); - } else { - histos.fill(HIST("hdatahistpp"), cols.posZ(), track.pt(), track.phi(), kITSonly, track.dcaXY()); + histos.fill(HIST("hdatahistpp"), cols.posZ(), track.pt(), track.phi(), trkType, track.dcaXY()); + // spectrum with the pT-dependent DCAxy cut + if (cfgApplyPtDepDcaXY && passDcaXY(track.pt(), track.dcaXY())) { + histos.fill(HIST("hdatahistppDcaCut"), cols.posZ(), track.pt(), track.phi(), kGlobalplusITS); + histos.fill(HIST("hdatahistppDcaCut"), cols.posZ(), track.pt(), track.phi(), trkType); } } } @@ -651,23 +745,41 @@ struct PtmultCorr { auto recTracksPart = RecTracks.sliceBy(perCollision, RecCol.globalIndex()); std::vector mclabels; for (const auto& Rectrack : recTracksPart) { - if (!isTrackSelected(Rectrack)) { + if (!isTrackSelected(Rectrack)) { // all cuts except the pT-dependent DCAxy cut continue; } auto trkType = Rectrack.hasTPC() ? kGlobalonly : kITSonly; + // DCAxy cut always evaluated with RECO pT (as in data) + const bool passCut = cfgApplyPtDepDcaXY && passDcaXY(Rectrack.pt(), Rectrack.dcaXY()); + + // fills the full-DCAxy THnSparse (templates) and, for tracks passing + // the DCAxy cut, the *DcaCut copy (spectrum / efficiency numerator) + auto fillRec = [&](float pt, float phi, int cls) { + histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), pt, phi, static_cast(cls), kGlobalplusITS, Rectrack.dcaXY()); + histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), pt, phi, static_cast(cls), trkType, Rectrack.dcaXY()); + if (passCut) { + histos.fill(HIST("hPbPbRecMCdndptDcaCut"), RecCol.posZ(), RecCol.centFT0C(), pt, phi, static_cast(cls), kGlobalplusITS); + histos.fill(HIST("hPbPbRecMCdndptDcaCut"), RecCol.posZ(), RecCol.centFT0C(), pt, phi, static_cast(cls), trkType); + } + }; - histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), Rectrack.pt(), Rectrack.phi(), static_cast(kRecoAll), kGlobalplusITS, Rectrack.dcaXY()); - histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), Rectrack.pt(), Rectrack.phi(), static_cast(kRecoAll), trkType, Rectrack.dcaXY()); + fillRec(Rectrack.pt(), Rectrack.phi(), kRecoAll); if (Rectrack.has_mcParticle()) { int pid = 0; auto mcpart = Rectrack.mcParticle(); histos.fill(HIST("hPbPbEtaReso"), Rectrack.pt(), Rectrack.pt() - mcpart.pt()); - histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), mcpart.pt(), mcpart.phi(), static_cast(kRecoHasmc), kGlobalplusITS, Rectrack.dcaXY()); - histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), mcpart.pt(), mcpart.phi(), static_cast(kRecoHasmc), trkType, Rectrack.dcaXY()); + fillRec(mcpart.pt(), mcpart.phi(), kRecoHasmc); if (mcpart.isPhysicalPrimary()) { - histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), mcpart.pt(), mcpart.phi(), static_cast(kRecoPrimary), kGlobalplusITS, Rectrack.dcaXY()); - histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), mcpart.pt(), mcpart.phi(), static_cast(kRecoPrimary), trkType, Rectrack.dcaXY()); + // fine DCAxy for sigma(pT): global tracks, reco pT, no DCAxy cut + if (Rectrack.hasTPC()) { + if (mcpart.mcCollisionId() == mcCollision.globalIndex()) { + histos.fill(HIST("hPbPbDcaXYPrimFine"), Rectrack.pt(), Rectrack.dcaXY(), RecCol.centFT0C()); + } else { + histos.fill(HIST("hPbPbDcaXYPrimWrongColl"), Rectrack.pt(), Rectrack.dcaXY()); + } + } + fillRec(mcpart.pt(), mcpart.phi(), kRecoPrimary); switch (std::abs(mcpart.pdgCode())) { case PDG_t::kPiPlus: pid = kRecoPion; @@ -690,8 +802,7 @@ struct PtmultCorr { pid = kRecoMaterial; } // also fill the union bin as a cross-check (weak + material) - histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), mcpart.pt(), mcpart.phi(), static_cast(kRecoSecondary), kGlobalplusITS, Rectrack.dcaXY()); - histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), mcpart.pt(), mcpart.phi(), static_cast(kRecoSecondary), trkType, Rectrack.dcaXY()); + fillRec(mcpart.pt(), mcpart.phi(), kRecoSecondary); } // duplicate-track (split) relabel — overrides origin, as before @@ -700,11 +811,9 @@ struct PtmultCorr { } mclabels.push_back(Rectrack.mcParticleId()); - histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), mcpart.pt(), mcpart.phi(), static_cast(pid), kGlobalplusITS, Rectrack.dcaXY()); - histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), mcpart.pt(), mcpart.phi(), static_cast(pid), trkType, Rectrack.dcaXY()); + fillRec(mcpart.pt(), mcpart.phi(), pid); } else { - histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), Rectrack.pt(), Rectrack.phi(), static_cast(kRecoBkg), kGlobalplusITS, Rectrack.dcaXY()); - histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), Rectrack.pt(), Rectrack.phi(), static_cast(kRecoBkg), trkType, Rectrack.dcaXY()); + fillRec(Rectrack.pt(), Rectrack.phi(), kRecoBkg); } } } // collision loop @@ -797,21 +906,39 @@ struct PtmultCorr { auto recTracksPart = RecTracks.sliceBy(perCollision, RecCol.globalIndex()); std::vector mclabels; for (const auto& Rectrack : recTracksPart) { - if (!isTrackSelected(Rectrack)) { + if (!isTrackSelected(Rectrack)) { // all cuts except the pT-dependent DCAxy cut continue; } auto trkType = Rectrack.hasTPC() ? kGlobalonly : kITSonly; - histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), Rectrack.pt(), Rectrack.phi(), static_cast(kRecoAll), kGlobalplusITS, Rectrack.dcaXY()); - histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), Rectrack.pt(), Rectrack.phi(), static_cast(kRecoAll), trkType, Rectrack.dcaXY()); + // DCAxy cut always evaluated with RECO pT (as in data) + const bool passCut = cfgApplyPtDepDcaXY && passDcaXY(Rectrack.pt(), Rectrack.dcaXY()); + + auto fillRec = [&](float pt, float phi, int cls) { + histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), pt, phi, static_cast(cls), kGlobalplusITS, Rectrack.dcaXY()); + histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), pt, phi, static_cast(cls), trkType, Rectrack.dcaXY()); + if (passCut) { + histos.fill(HIST("hppRecMCdndptDcaCut"), RecCol.posZ(), pt, phi, static_cast(cls), kGlobalplusITS); + histos.fill(HIST("hppRecMCdndptDcaCut"), RecCol.posZ(), pt, phi, static_cast(cls), trkType); + } + }; + + fillRec(Rectrack.pt(), Rectrack.phi(), kRecoAll); + if (Rectrack.has_mcParticle()) { int pid = 0; auto mcpart = Rectrack.mcParticle(); histos.fill(HIST("hppEtaReso"), Rectrack.pt(), Rectrack.pt() - mcpart.pt()); - histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), mcpart.pt(), mcpart.phi(), static_cast(kRecoHasmc), kGlobalplusITS, Rectrack.dcaXY()); - histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), mcpart.pt(), mcpart.phi(), static_cast(kRecoHasmc), trkType, Rectrack.dcaXY()); + fillRec(mcpart.pt(), mcpart.phi(), kRecoHasmc); if (mcpart.isPhysicalPrimary()) { - histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), mcpart.pt(), mcpart.phi(), static_cast(kRecoPrimary), kGlobalplusITS, Rectrack.dcaXY()); - histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), mcpart.pt(), mcpart.phi(), static_cast(kRecoPrimary), trkType, Rectrack.dcaXY()); + // fine DCAxy for sigma(pT): global tracks, reco pT, no DCAxy cut + if (Rectrack.hasTPC()) { + if (mcpart.mcCollisionId() == mcCollision.globalIndex()) { + histos.fill(HIST("hppDcaXYPrimFine"), Rectrack.pt(), Rectrack.dcaXY()); + } else { + histos.fill(HIST("hppDcaXYPrimWrongColl"), Rectrack.pt(), Rectrack.dcaXY()); + } + } + fillRec(mcpart.pt(), mcpart.phi(), kRecoPrimary); switch (std::abs(mcpart.pdgCode())) { case PDG_t::kPiPlus: pid = kRecoPion; @@ -834,19 +961,16 @@ struct PtmultCorr { pid = kRecoMaterial; } // union cross-check bin (weak + material) - histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), mcpart.pt(), mcpart.phi(), static_cast(kRecoSecondary), kGlobalplusITS, Rectrack.dcaXY()); - histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), mcpart.pt(), mcpart.phi(), static_cast(kRecoSecondary), trkType, Rectrack.dcaXY()); + fillRec(mcpart.pt(), mcpart.phi(), kRecoSecondary); } // duplicate-track (split) relabel — overrides origin, as before if (find(mclabels.begin(), mclabels.end(), Rectrack.mcParticleId()) != mclabels.end()) { pid = kRecoFake; } mclabels.push_back(Rectrack.mcParticleId()); - histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), mcpart.pt(), mcpart.phi(), static_cast(pid), kGlobalplusITS, Rectrack.dcaXY()); - histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), mcpart.pt(), mcpart.phi(), static_cast(pid), trkType, Rectrack.dcaXY()); + fillRec(mcpart.pt(), mcpart.phi(), pid); } else { - histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), Rectrack.pt(), Rectrack.phi(), static_cast(kRecoBkg), kGlobalplusITS, Rectrack.dcaXY()); - histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), Rectrack.pt(), Rectrack.phi(), static_cast(kRecoBkg), trkType, Rectrack.dcaXY()); + fillRec(Rectrack.pt(), Rectrack.phi(), kRecoBkg); } } // track (mcrec) loop } // collision loop diff --git a/PWGLF/Tasks/Nuspex/AntimatterAbsorptionHMPID.cxx b/PWGLF/Tasks/Nuspex/AntimatterAbsorptionHMPID.cxx index 50dd28efbdc..ce410a7dd57 100644 --- a/PWGLF/Tasks/Nuspex/AntimatterAbsorptionHMPID.cxx +++ b/PWGLF/Tasks/Nuspex/AntimatterAbsorptionHMPID.cxx @@ -44,6 +44,7 @@ #include #include #include +#include using namespace o2; using namespace o2::framework; diff --git a/PWGLF/Tasks/Nuspex/antinucleiInJets.cxx b/PWGLF/Tasks/Nuspex/antinucleiInJets.cxx index 624b5bebb22..ec1b776da41 100644 --- a/PWGLF/Tasks/Nuspex/antinucleiInJets.cxx +++ b/PWGLF/Tasks/Nuspex/antinucleiInJets.cxx @@ -17,12 +17,10 @@ #include "PWGJE/Core/JetBkgSubUtils.h" #include "PWGJE/Core/JetUtilities.h" -#include "PWGLF/DataModel/mcCentrality.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/Core/Zorro.h" #include "Common/Core/ZorroSummary.h" -#include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/PIDResponseITS.h" #include "Common/DataModel/PIDResponseTOF.h" @@ -30,7 +28,6 @@ #include "Common/DataModel/TrackSelectionTables.h" #include -#include #include #include #include @@ -64,10 +61,8 @@ #include #include -#include #include #include -#include #include #include #include @@ -83,9 +78,8 @@ using namespace o2::constants::math; using std::array; // Define convenient aliases for commonly used table joins -using SelectedCollisions = soa::Join; -using RecCollisionsMc = soa::Join; -using GenCollisionsMc = soa::Join; +using SelectedCollisions = soa::Join; +using RecCollisionsMc = soa::Join; using AntiNucleiTracks = soa::Join; using AntiNucleiTracksMc = soa::Join; using LorentzVector = ROOT::Math::PxPyPzEVector; @@ -136,12 +130,11 @@ struct AntinucleiInJets { std::mt19937 rng; std::uniform_int_distribution generateRandomNr{0, 1}; - // Histogram registries for data, MC, quality control, multiplicity and correlations + // Histogram registries for data, MC, quality control, and multiplicity HistogramRegistry registryData{"registryData", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry registryMC{"registryMC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry registryQC{"registryQC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry registryMult{"registryMult", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - HistogramRegistry registryCorr{"registryCorr", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; // Random generator for subsample assignment TRandom3 mRand; @@ -167,14 +160,13 @@ struct AntinucleiInJets { // Jet selection and event filtering parameters Configurable minJetPt{"minJetPt", 10.0, "Minimum pt of the jet after bkg subtraction"}; - Configurable maxJetPt{"maxJetPt", 1e+06, "Maximum pt of the jet after bkg subtraction"}; + Configurable maxJetPt{"maxJetPt", 1e+08, "Maximum pt of the jet after bkg subtraction"}; Configurable ptLeadingMin{"ptLeadingMin", 5.0, "pt Leading Min"}; Configurable rJet{"rJet", 0.4, "Jet resolution parameter R"}; Configurable zVtx{"zVtx", 10.0, "Maximum zVertex"}; - Configurable applyAreaCut{"applyAreaCut", false, "apply area cut A > f * pi R^2"}; - Configurable deltaEtaEdge{"deltaEtaEdge", 0.05, "eta gap from the edge"}; + Configurable applyAreaCut{"applyAreaCut", true, "apply area cut A > f * pi R^2"}; + Configurable deltaEtaEdge{"deltaEtaEdge", 0.0, "eta gap from the edge"}; Configurable nSyst{"nSyst", 50, "number of systematic variations"}; - Configurable nSubsamples{"nSubsamples", 50, "number of subsamples"}; // Track quality, kinematic, and PID selection parameters Configurable requirePvContributor{"requirePvContributor", false, "require that the track is a PV contributor"}; @@ -207,13 +199,6 @@ struct AntinucleiInJets { Configurable urlToCcdb{"urlToCcdb", "http://alice-ccdb.cern.ch/", "url of the personal ccdb"}; Configurable pathToFile{"pathToFile", "Users/a/alcaliva/reweightingHistogramsAntinucleiInJets/", "path to file"}; - // Number of events - Configurable shrinkInterval{"shrinkInterval", 1000, "variable that controls how often shrinking happens"}; - - // Range of systematic variations to be processed - Configurable systIndexStart{"systIndexStart", 0, "First systematic index (inclusive)"}; - Configurable systIndexEnd{"systIndexEnd", 50, "Last systematic index (exclusive)"}; - // Coalescence momentum Configurable coalescenceMomentum{"coalescenceMomentum", 0.15, "p0 (GeV/c)"}; @@ -227,13 +212,12 @@ struct AntinucleiInJets { TH1F* antiprotonsPerpCone = nullptr; TH1F* antideuteronsInsideJets = nullptr; TH1F* antideuteronsPerpCone = nullptr; + TH1F* recoJetPtReweightingFactor = nullptr; + TH1F* genJetPtReweightingFactor = nullptr; // CCDB manager service for accessing condition data Service ccdb{}; - // Direct interface to the CCDB API for manual data access - o2::ccdb::CcdbApi ccdbApi; - // Instantiate the main Zorro processing object and define an output to store summary information Zorro zorro; OutputObj zorroSummary{"zorroSummary"}; @@ -273,7 +257,7 @@ struct AntinucleiInJets { rng.seed(12345); // Load reweighting histograms from CCDB if antinuclei efficiency processing is enabled - if (doprocessAntinucleiEfficiency || doprocessJetsMCgen || doprocessJetsMCrec) { + if (applyReweighting && (doprocessAntinucleiEfficiency || doprocessJetsMCgen || doprocessJetsMCrec)) { ccdb->setURL(urlToCcdb.value); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); @@ -282,7 +266,7 @@ struct AntinucleiInJets { getReweightingHistograms(ccdb, TString(pathToFile)); } - // Binning + // pt binning double min = 0.0; double max = 6.0; int nbins = 120; @@ -326,6 +310,9 @@ struct AntinucleiInJets { registryData.add("jetEffectiveAreaOverPiR2", "jet effective area / piR^2", HistType::kTH1F, {{2000, 0, 2, "Area/#piR^{2}"}}); registryData.add("jetArea", "jetArea", HistType::kTH1F, {{5000, 0, 5, "area"}}); + // Jet pt distribution in data + registryData.add("jetPtData", "jet pt distribution in data", HistType::kTH1F, {{2000, 0, 200, "#it{p}^{jet}_{T} (GeV/#it{c})"}}); + // angle between track and jet axis registryData.add("theta_track_jet", "theta_track_jet", HistType::kTH2F, {{100, 0, 100, "#it{p}^{jet}_{T} (GeV/#it{c})"}, {400, 0, 20.0, "#theta_{track-jet} (deg)"}}); @@ -379,6 +366,9 @@ struct AntinucleiInJets { registryMC.add("genEvents", "number of generated events in mc", HistType::kTH1F, {{10, 0, 10, "counter"}}); registryMC.add("genJets", "number of generated jets", HistType::kTH1F, {{10, 0, 10, "counter"}}); + // Jet pt distribution in gen MC + registryMC.add("jetPtMCgenerated", "jet pt distribution in gen MC", HistType::kTH1F, {{2000, 0, 200, "#it{p}^{jet}_{T} (GeV/#it{c})"}}); + // Size of particle array registryMC.add("sizeParticleArray", "number of particles", HistType::kTH1F, {{1000, 0, 1000, "#it{N}_{part}"}}); @@ -415,6 +405,9 @@ struct AntinucleiInJets { registryMC.add("recEvents", "number of reconstructed events in mc", HistType::kTH1F, {{20, 0, 20, "counter"}}); registryMC.add("recJets", "number of reconstructed jets", HistType::kTH1F, {{10, 0, 10, "counter"}}); + // Jet pt distribution in rec MC + registryMC.add("jetPtMCreconstructed", "jet pt distribution in rec MC", HistType::kTH1F, {{2000, 0, 200, "#it{p}^{jet}_{T} (GeV/#it{c})"}}); + // Configuration registryMC.add("settingMC", "settingMC", HistType::kTH2F, {{100, 0.0, 50.0, "min #it{p}^{jet}_{T} (GeV/#it{c})"}, {20, 0.0, 1.0, "#it{R}_{jet}"}}); @@ -540,38 +533,6 @@ struct AntinucleiInJets { registryMC.add("jetPtResolution", "jet Pt Resolution", HistType::kTH2F, {{1000, 0, 100, "#it{p}^{jet}_{T,true} (GeV/#it{c})"}, {1000, -20, 20, "#Delta #it{p}^{jet}_{T} (GeV/#it{c})"}}); } - // Coalescence and Correlation analysis - if (doprocessCoalescenceCorr) { - - // Axes definitions for multidimensional histogram binning - const AxisSpec multiplicityAxis{100, 0.0, 100.0, "multiplicity percentile"}; - const AxisSpec ptPerNucleonAxis{5, 0.4, 0.9, "{p}_{T}/A (GeV/#it{c})"}; - const AxisSpec nAntideuteronsAxis{10, 0.0, 10.0, "N_{#bar{d}}"}; - const AxisSpec nAntiprotonsAxis{10, 0.0, 10.0, "N_{#bar{p}}"}; - const AxisSpec nBarD2Axis{100, 0.0, 100.0, "N_{#bar{d}}^{i} #times N_{#bar{d}}^{j}"}; - const AxisSpec nBarP2Axis{100, 0.0, 100.0, "N_{#bar{p}}^{i} #times N_{#bar{p}}^{j}"}; - const AxisSpec nBarDnBarPAxis{100, 0.0, 100.0, "N_{#bar{d}}^{i} #times N_{#bar{p}}^{j}"}; - - registryMC.add("genEventsCoalescenceCorr", "genEventsCoalescenceCorr", HistType::kTH1F, {{20, 0, 20, "counter"}}); - registryMC.add("antideuteron_fullEvent_CoalescenceCorr", "antideuteron_fullEvent_CoalescenceCorr", HistType::kTH1F, {{nbins, 2 * min, 2 * max, "#it{p}_{T} (GeV/#it{c})"}}); - registryMC.add("antiproton_fullEvent_CoalescenceCorr", "antiproton_fullEvent_CoalescenceCorr", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); - - // Counter histograms - registryCorr.add("eventCounter_CoalescenceCorr", "number of events in Coalescence simulation", HistType::kTH1F, {{20, 0, 20, "counter"}}); - registryCorr.add("eventCounter_centrality_fullEvent_CoalescenceCorr", "Number of events per centrality (Full Event) in Coalescence simulation", HistType::kTH1F, {multiplicityAxis}); - - // Correlation histograms - registryCorr.add("rho_fullEvent_CoalescenceCorr", "rho_fullEvent_CoalescenceCorr", HistType::kTH3F, {nAntideuteronsAxis, nAntiprotonsAxis, multiplicityAxis}); - registryCorr.add("rho_netP_netD_fullEvent_CoalescenceCorr", "rho_netP_netD_fullEvent_CoalescenceCorr", HistType::kTH2F, {nAntideuteronsAxis, nAntiprotonsAxis}); - - // Efficiency histograms full event - registryCorr.add("q1d_fullEvent_CoalescenceCorr", "q1d_fullEvent_CoalescenceCorr", HistType::kTH3F, {nAntideuteronsAxis, ptPerNucleonAxis, multiplicityAxis}); - registryCorr.add("q1p_fullEvent_CoalescenceCorr", "q1p_fullEvent_CoalescenceCorr", HistType::kTH3F, {nAntiprotonsAxis, ptPerNucleonAxis, multiplicityAxis}); - registryCorr.add("q1d_square_fullEvent_CoalescenceCorr", "q1d_square_fullEvent_CoalescenceCorr", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarD2Axis, multiplicityAxis}); - registryCorr.add("q1p_square_fullEvent_CoalescenceCorr", "q1p_square_fullEvent_CoalescenceCorr", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarP2Axis, multiplicityAxis}); - registryCorr.add("q1d_q1p_fullEvent_CoalescenceCorr", "q1d_q1p_fullEvent_CoalescenceCorr", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarDnBarPAxis, multiplicityAxis}); - } - // Systematic uncertainties (Data) if (doprocessSystData) { registryData.add("number_of_events_data_syst", "event counter", HistType::kTH1F, {{20, 0, 20, "counter"}}); @@ -602,92 +563,13 @@ struct AntinucleiInJets { registryMC.add("antiproton_incl_syst", "antiproton_incl_syst", HistType::kTH2F, {{50, 0, 50, "systematic uncertainty"}, {nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); registryMC.add("antiproton_prim_syst", "antiproton_prim_syst", HistType::kTH2F, {{50, 0, 50, "systematic uncertainty"}, {nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); } - - // Correlation analysis - if (doprocessCorr) { - - // Axes definitions for multidimensional histogram binning - const AxisSpec multiplicityAxis{100, 0.0, 100.0, "multiplicity percentile"}; - const AxisSpec ptPerNucleonAxis{5, 0.4, 0.9, "{p}_{T}/A (GeV/#it{c})"}; - const AxisSpec nAntideuteronsAxis{10, 0.0, 10.0, "N_{#bar{d}}"}; - const AxisSpec nAntiprotonsAxis{10, 0.0, 10.0, "N_{#bar{p}}"}; - const AxisSpec nBarD2Axis{100, 0.0, 100.0, "N_{#bar{d}}^{i} #times N_{#bar{d}}^{j}"}; - const AxisSpec nBarP2Axis{100, 0.0, 100.0, "N_{#bar{p}}^{i} #times N_{#bar{p}}^{j}"}; - const AxisSpec nBarDnBarPAxis{100, 0.0, 100.0, "N_{#bar{d}}^{i} #times N_{#bar{p}}^{j}"}; - const AxisSpec subsampleAxis{nSubsamples, 0, static_cast(nSubsamples), "Subsample Index"}; - - // Event counter - registryCorr.add("eventCounter", "number of events", HistType::kTH1F, {{20, 0, 20, "counter"}}); - registryCorr.add("eventCounter_centrality_fullEvent", "Number of events per centrality (Full Event)", HistType::kTH2F, {multiplicityAxis, subsampleAxis}); - // registryCorr.add("eventCounter_centrality_jet", "Number of events per centrality (Jet)", HistType::kTH1F, {multiplicityAxis}); - // registryCorr.add("eventCounter_centrality_ue", "Number of events per centrality (Underlying Event)", HistType::kTH1F, {multiplicityAxis}); - - // Correlation histograms: antiproton vs. antideuteron number vs. event multiplicity - // registryCorr.add("rho_jet", "rho_jet", HistType::kTH3F, {nAntideuteronsAxis, nAntiprotonsAxis, multiplicityAxis}); - // registryCorr.add("rho_ue", "rho_ue", HistType::kTH3F, {nAntideuteronsAxis, nAntiprotonsAxis, multiplicityAxis}); - registryCorr.add("rho_fullEvent", "rho_fullEvent", HistType::kTHnSparseD, {nAntideuteronsAxis, nAntiprotonsAxis, multiplicityAxis, subsampleAxis}); - - // Correlation histograms: net antiproton vs. net antideuteron numbers - // registryCorr.add("rho_netP_netD_jet", "rho_netP_netD_jet", HistType::kTH2F, {nAntideuteronsAxis, nAntiprotonsAxis}); - // registryCorr.add("rho_netP_netD_ue", "rho_netP_netD_ue", HistType::kTH2F, {nAntideuteronsAxis, nAntiprotonsAxis}); - registryCorr.add("rho_netP_netD_fullEvent", "rho_netP_netD_fullEvent", HistType::kTH3F, {nAntideuteronsAxis, nAntiprotonsAxis, subsampleAxis}); - - // Efficiency histograms jet - // registryCorr.add("q1d_jet", "q1d_jet", HistType::kTH3F, {nAntideuteronsAxis, ptPerNucleonAxis, multiplicityAxis}); - // registryCorr.add("q1p_jet", "q1p_jet", HistType::kTH3F, {nAntiprotonsAxis, ptPerNucleonAxis, multiplicityAxis}); - // registryCorr.add("q1d_square_jet", "q1d_square_jet", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarD2Axis, multiplicityAxis}); - // registryCorr.add("q1p_square_jet", "q1p_square_jet", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarP2Axis, multiplicityAxis}); - // registryCorr.add("q1d_q1p_jet", "q1d_q1p_jet", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarDnBarPAxis, multiplicityAxis}); - - // Efficiency histograms UE - // registryCorr.add("q1d_ue", "q1d_ue", HistType::kTH3F, {nAntideuteronsAxis, ptPerNucleonAxis, multiplicityAxis}); - // registryCorr.add("q1p_ue", "q1p_ue", HistType::kTH3F, {nAntiprotonsAxis, ptPerNucleonAxis, multiplicityAxis}); - // registryCorr.add("q1d_square_ue", "q1d_square_ue", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarD2Axis, multiplicityAxis}); - // registryCorr.add("q1p_square_ue", "q1p_square_ue", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarP2Axis, multiplicityAxis}); - // registryCorr.add("q1d_q1p_ue", "q1d_q1p_ue", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarDnBarPAxis, multiplicityAxis}); - - // Efficiency histograms full event - registryCorr.add("q1d_fullEvent", "q1d_fullEvent", HistType::kTHnSparseD, {nAntideuteronsAxis, ptPerNucleonAxis, multiplicityAxis, subsampleAxis}); - registryCorr.add("q1p_fullEvent", "q1p_fullEvent", HistType::kTHnSparseD, {nAntiprotonsAxis, ptPerNucleonAxis, multiplicityAxis, subsampleAxis}); - registryCorr.add("q1d_square_fullEvent", "q1d_square_fullEvent", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarD2Axis, multiplicityAxis, subsampleAxis}); - registryCorr.add("q1p_square_fullEvent", "q1p_square_fullEvent", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarP2Axis, multiplicityAxis, subsampleAxis}); - registryCorr.add("q1d_q1p_fullEvent", "q1d_q1p_fullEvent", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarDnBarPAxis, multiplicityAxis, subsampleAxis}); - } - - // Systematic uncertainties on correlation analysis - if (doprocessSystCorr) { - - // Axes definitions for multidimensional histogram binning - const AxisSpec multiplicityAxis{100, 0.0, 100.0, "multiplicity percentile"}; - const AxisSpec ptPerNucleonAxis{5, 0.4, 0.9, "{p}_{T}/A (GeV/#it{c})"}; - const AxisSpec nAntideuteronsAxis{10, 0.0, 10.0, "N_{#bar{d}}"}; - const AxisSpec nAntiprotonsAxis{10, 0.0, 10.0, "N_{#bar{p}}"}; - const AxisSpec nBarD2Axis{100, 0.0, 100.0, "N_{#bar{d}}^{i} #times N_{#bar{d}}^{j}"}; - const AxisSpec nBarP2Axis{100, 0.0, 100.0, "N_{#bar{p}}^{i} #times N_{#bar{p}}^{j}"}; - const AxisSpec nBarDnBarPAxis{100, 0.0, 100.0, "N_{#bar{d}}^{i} #times N_{#bar{p}}^{j}"}; - const AxisSpec systAxis{nSyst, 0, static_cast(nSyst), "Systematic Variation Index"}; - - registryCorr.add("eventCounter_syst", "number of events syst", HistType::kTH1F, {{20, 0, 20, "counter"}}); - registryCorr.add("eventCounter_centrality_fullEvent_syst", "Number of events per centrality (Full Event) Syst", HistType::kTH2F, {multiplicityAxis, systAxis}); - - // Correlation histograms - registryCorr.add("rho_fullEvent_syst", "rho_fullEvent_syst", HistType::kTHnSparseD, {nAntideuteronsAxis, nAntiprotonsAxis, multiplicityAxis, systAxis}); - registryCorr.add("rho_netP_netD_fullEvent_syst", "rho_netP_netD_fullEvent_syst", HistType::kTH3F, {nAntideuteronsAxis, nAntiprotonsAxis, systAxis}); - - // Efficiency histograms full event - registryCorr.add("q1d_fullEvent_syst", "q1d_fullEvent_syst", HistType::kTHnSparseD, {nAntideuteronsAxis, ptPerNucleonAxis, multiplicityAxis, systAxis}); - registryCorr.add("q1p_fullEvent_syst", "q1p_fullEvent_syst", HistType::kTHnSparseD, {nAntiprotonsAxis, ptPerNucleonAxis, multiplicityAxis, systAxis}); - registryCorr.add("q1d_square_fullEvent_syst", "q1d_square_fullEvent_syst", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarD2Axis, multiplicityAxis, systAxis}); - registryCorr.add("q1p_square_fullEvent_syst", "q1p_square_fullEvent_syst", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarP2Axis, multiplicityAxis, systAxis}); - registryCorr.add("q1d_q1p_fullEvent_syst", "q1d_q1p_fullEvent_syst", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarDnBarPAxis, multiplicityAxis, systAxis}); - } } void getReweightingHistograms(o2::framework::Service const& ccdbObj, TString const& filepath) { TList* list = ccdbObj->get(filepath.Data()); if (!list) { - LOGP(error, "Could not retrieve the list from CCDB"); + LOGP(fatal, "Could not retrieve the list from CCDB"); return; } @@ -699,21 +581,28 @@ struct AntinucleiInJets { primaryAntiOmega = dynamic_cast(list->FindObject("weightsOmega")); if (!primaryAntiprotons || !primaryAntiSigma || !primaryAntiLambda || !primaryAntiXi || !primaryAntiOmega) { - LOGP(error, "Missing one or more reweighting histograms for primary fraction in CCDB list"); + LOGP(fatal, "Missing one or more reweighting histograms for primary fraction in CCDB list"); } // Get reweighting histograms for antiproton efficiency antiprotonsInsideJets = dynamic_cast(list->FindObject("weightsJet")); antiprotonsPerpCone = dynamic_cast(list->FindObject("weightsUe")); if (!antiprotonsInsideJets || !antiprotonsPerpCone) { - LOGP(error, "Missing one or more reweighting histograms for antiproton efficiency in CCDB list"); + LOGP(fatal, "Missing one or more reweighting histograms for antiproton efficiency in CCDB list"); } // Get reweighting histograms for antideuteron efficiency antideuteronsInsideJets = dynamic_cast(list->FindObject("weightsAntidJet")); antideuteronsPerpCone = dynamic_cast(list->FindObject("weightsAntidUe")); if (!antideuteronsInsideJets || !antideuteronsPerpCone) { - LOGP(error, "Missing one or more reweighting histograms for antideuteron efficiency in CCDB list"); + LOGP(fatal, "Missing one or more reweighting histograms for antideuteron efficiency in CCDB list"); + } + + // Get reco jet pt reweighting histogram + recoJetPtReweightingFactor = dynamic_cast(list->FindObject("recoJetPtReweightingFactor")); + genJetPtReweightingFactor = dynamic_cast(list->FindObject("genJetPtReweightingFactor")); + if (!recoJetPtReweightingFactor || !genJetPtReweightingFactor) { + LOGP(fatal, "Missing one of jet pt reweighting histograms in CCDB list"); } LOGP(info, "Successfully loaded reweighting histograms from CCDB path"); @@ -772,6 +661,31 @@ struct AntinucleiInJets { return (particle.isPhysicalPrimary() || fromHF); } + // Check if particle is from heavy-flavor hadron decay + bool isFromHF(aod::McParticle const& particle, aod::McParticles const& mcParticles) + { + // Keep only pi, K, p, d, e, mu + int pdg = std::abs(particle.pdgCode()); + if (!(pdg == PDG_t::kPiPlus || pdg == PDG_t::kKPlus || pdg == PDG_t::kProton || pdg == o2::constants::physics::Pdg::kDeuteron || pdg == PDG_t::kElectron || pdg == PDG_t::kMuonMinus)) + return false; + + // Constants for identifying heavy-flavor (charm and bottom) content from PDG codes + static constexpr int CharmQuark = 4; + static constexpr int BottomQuark = 5; + static constexpr int Hundreds = 100; + static constexpr int Thousands = 1000; + + // Check if particle is from heavy-flavor decay + bool fromHF = false; + if (particle.has_mothers()) { + auto mother = mcParticles.iteratorAt(particle.mothersIds()[0]); + int motherPdg = std::abs(mother.pdgCode()); + fromHF = (motherPdg / Hundreds == CharmQuark || motherPdg / Hundreds == BottomQuark || motherPdg / Thousands == CharmQuark || motherPdg / Thousands == BottomQuark); + } + + return fromHF; + } + // Evaluate proton–neutron coalescence for deuteron formation template bool passDeuteronCoalescence(const ReducedPart& p, const ReducedPart& n, double p0, TRandom3& random) @@ -899,17 +813,6 @@ struct AntinucleiInJets { return deltaPhi; } - // Find bin - int findBin(const std::vector& edges, double value) - { - auto it = std::upper_bound(edges.begin(), edges.end(), value); - int index = static_cast(it - edges.begin()) - 1; - if (index < 0 || index >= static_cast(edges.size()) - 1) { - return -1; // value is out of bounds - } - return index; - } - // ITS hit template bool hasITSHit(const TrackIts& track, int layer) @@ -1057,104 +960,6 @@ struct AntinucleiInJets { return false; } - // Selection of (anti)protons - template - bool isProton(const ProtonTrack& track) - { - // Constants - static constexpr double PtThreshold = 0.6; - static constexpr double NsigmaMax = 3.0; - - // PID variables and transverse momentum of the track - const double nsigmaTPC = track.tpcNSigmaPr(); - const double nsigmaTOF = track.tofNSigmaPr(); - const double pt = track.pt(); - - // Apply TPC PID cut - if (std::abs(nsigmaTPC) > NsigmaMax) - return false; - - // Low-pt: TPC PID is sufficient - if (pt < PtThreshold) - return true; - - // High-pt: require valid TOF match and pass TOF PID - return (track.hasTOF() && std::abs(nsigmaTOF) < NsigmaMax); - } - - // Selection of (anti)deuterons - template - bool isDeuteron(const DeuteronTrack& track) - { - // Constants - static constexpr double PtThreshold = 1.0; - static constexpr double NsigmaMax = 3.0; - - // PID variables and transverse momentum of the track - const double nsigmaTPC = track.tpcNSigmaDe(); - const double nsigmaTOF = track.tofNSigmaDe(); - const double pt = track.pt(); - - // Apply TPC PID cut - if (std::abs(nsigmaTPC) > NsigmaMax) - return false; - - // Low-pt: TPC PID is sufficient - if (pt < PtThreshold) - return true; - - // High-pt: require valid TOF match and pass TOF PID - return (track.hasTOF() && std::abs(nsigmaTOF) < NsigmaMax); - } - - // Selection of (anti)protons with systematic variations - template - bool isProtonSyst(const ProtonTrack& track, double minSigTPC, double maxSigTPC, double minSigTOF, double maxSigTOF) - { - // Constant - static constexpr double PtThreshold = 0.6; - - // PID variables and transverse momentum of the track - const double nsigmaTPC = track.tpcNSigmaPr(); - const double nsigmaTOF = track.tofNSigmaPr(); - const double pt = track.pt(); - - // Apply TPC PID cut (with systematic variations) - if (nsigmaTPC < minSigTPC || nsigmaTPC > maxSigTPC) - return false; - - // Low-pt: TPC PID is sufficient - if (pt < PtThreshold) - return true; - - // High-pt: require valid TOF match and pass TOF PID (with systematic variations) - return (track.hasTOF() && nsigmaTOF > minSigTOF && nsigmaTOF < maxSigTOF); - } - - // Selection of (anti)deuterons with systematic variations - template - bool isDeuteronSyst(const DeuteronTrack& track, double minSigTPC, double maxSigTPC, double minSigTOF, double maxSigTOF) - { - // Constant - static constexpr double PtThreshold = 1.0; - - // PID variables and transverse momentum of the track - const double nsigmaTPC = track.tpcNSigmaDe(); - const double nsigmaTOF = track.tofNSigmaDe(); - const double pt = track.pt(); - - // Apply TPC PID cut (with systematic variations) - if (nsigmaTPC < minSigTPC || nsigmaTPC > maxSigTPC) - return false; - - // Low-pt: TPC PID is sufficient - if (pt < PtThreshold) - return true; - - // High-pt: require valid TOF match and pass TOF PID (with systematic variations) - return (track.hasTOF() && nsigmaTOF > minSigTOF && nsigmaTOF < maxSigTOF); - } - // Process Data void processData(SelectedCollisions::iterator const& collision, AntiNucleiTracks const& tracks, aod::BCsWithTimestamps const&) { @@ -1303,6 +1108,10 @@ struct AntinucleiInJets { // Jet pt must be larger than threshold auto jetForSub = jet; fastjet::PseudoJet jetMinusBkg = backgroundSub.doRhoAreaSub(jetForSub, rhoPerp, rhoMPerp); + + // Fill jet pt distribution + registryData.fill(HIST("jetPtData"), jetMinusBkg.pt()); + if (isppRefAnalysis && (jet.pt() < minJetPt || jet.pt() > maxJetPt)) continue; if (!isppRefAnalysis && (jetMinusBkg.pt() < minJetPt || jetMinusBkg.pt() > maxJetPt)) @@ -1729,7 +1538,7 @@ struct AntinucleiInJets { Preslice mcParticlesPerMcCollision = o2::aod::mcparticle::mcCollisionId; // Antinuclei reconstruction efficiency - void processAntinucleiEfficiency(GenCollisionsMc const& genCollisions, RecCollisionsMc const& recCollisions, AntiNucleiTracksMc const& mcTracks, aod::McParticles const& mcParticles) + void processAntinucleiEfficiency(aod::McCollisions const& genCollisions, RecCollisionsMc const& recCollisions, AntiNucleiTracksMc const& mcTracks, aod::McParticles const& mcParticles) { // Loop over generated collisions for (const auto& collision : genCollisions) { @@ -2041,16 +1850,13 @@ struct AntinucleiInJets { PROCESS_SWITCH(AntinucleiInJets, processAntinucleiEfficiency, "process antinuclei efficiency", false); // Generated events - void processJetsMCgen(GenCollisionsMc const& collisions, aod::McParticles const& mcParticles) + void processJetsMCgen(aod::McCollisions const& collisions, aod::McParticles const& mcParticles) { // Define per-event particle containers std::vector fjParticles; std::vector protonMomentum; std::vector deuteronMomentum; - // Event counter - int eventCounter = 0; - // Jet and area definitions fastjet::JetDefinition jetDef(fastjet::antikt_algorithm, rJet); fastjet::AreaDefinition areaDef(fastjet::active_area, fastjet::GhostedAreaSpec(1.0)); @@ -2058,9 +1864,6 @@ struct AntinucleiInJets { // Loop over all simulated collisions for (const auto& collision : collisions) { - // Increment event counter - eventCounter++; - // Clear containers at the start of the event loop fjParticles.clear(); protonMomentum.clear(); @@ -2141,6 +1944,20 @@ struct AntinucleiInJets { // Jet pt must be larger than threshold auto jetForSub = jet; fastjet::PseudoJet jetMinusBkg = backgroundSub.doRhoAreaSub(jetForSub, rhoPerp, rhoMPerp); + + // Get generated jet pt reweighting factor + double genJetPtWeight(1.0); + if (applyReweighting) { + double jetPt = jetMinusBkg.pt(); + if (jetPt >= genJetPtReweightingFactor->GetXaxis()->GetXmin() && jetPt < genJetPtReweightingFactor->GetXaxis()->GetXmax()) { + int jetPtBin = genJetPtReweightingFactor->FindBin(jetPt); + genJetPtWeight = genJetPtReweightingFactor->GetBinContent(jetPtBin); + } + } + + // Fill jet pt distribution + registryMC.fill(HIST("jetPtMCgenerated"), jetMinusBkg.pt()); + if (isppRefAnalysis && (jet.pt() < minJetPt || jet.pt() > maxJetPt)) continue; if (!isppRefAnalysis && (jetMinusBkg.pt() < minJetPt || jetMinusBkg.pt() > maxJetPt)) @@ -2172,10 +1989,12 @@ struct AntinucleiInJets { registryMC.fill(HIST("antiproton_deltay_deltaphi_jet"), particle.eta() - jet.eta(), getDeltaPhi(particle.phi(), jet.phi())); // Calculate weight - double weightJet(1.0); - if (applyReweighting && particle.pt() < antiprotonsInsideJets->GetXaxis()->GetXmax()) { - int ipt = antiprotonsInsideJets->FindBin(particle.pt()); - weightJet = antiprotonsInsideJets->GetBinContent(ipt); + double weightJet = genJetPtWeight; + if (applyReweighting) { + if (particle.pt() >= antiprotonsInsideJets->GetXaxis()->GetXmin() && particle.pt() < antiprotonsInsideJets->GetXaxis()->GetXmax()) { + int ipt = antiprotonsInsideJets->FindBin(particle.pt()); + weightJet *= antiprotonsInsideJets->GetBinContent(ipt); + } } // Fill histogram for generated antiprotons @@ -2194,10 +2013,12 @@ struct AntinucleiInJets { // Fill antideuteron spectra if (isAntid) { - double weightJetAntid(1.0); - if (applyReweighting && particle.pt() < antideuteronsInsideJets->GetXaxis()->GetXmax()) { - int ipt = antideuteronsInsideJets->FindBin(particle.pt()); - weightJetAntid = antideuteronsInsideJets->GetBinContent(ipt); + double weightJetAntid = genJetPtWeight; + if (applyReweighting) { + if (particle.pt() >= antideuteronsInsideJets->GetXaxis()->GetXmin() && particle.pt() < antideuteronsInsideJets->GetXaxis()->GetXmax()) { + int ipt = antideuteronsInsideJets->FindBin(particle.pt()); + weightJetAntid *= antideuteronsInsideJets->GetBinContent(ipt); + } } registryMC.fill(HIST("antideuteron_gen_jet"), particle.pt(), weightJetAntid); @@ -2233,10 +2054,12 @@ struct AntinucleiInJets { registryMC.fill(HIST("antiproton_deltay_deltaphi_ue"), protonVec.Eta() - ueAxis2.Eta(), getDeltaPhi(protonVec.Phi(), ueAxis2.Phi())); // Calculate weight - double weightUe(1.0); - if (applyReweighting && protonVec.Pt() < antiprotonsPerpCone->GetXaxis()->GetXmax()) { - int ipt = antiprotonsPerpCone->FindBin(protonVec.Pt()); - weightUe = antiprotonsPerpCone->GetBinContent(ipt); + double weightUe = genJetPtWeight; + if (applyReweighting) { + if (protonVec.Pt() >= antiprotonsPerpCone->GetXaxis()->GetXmin() && protonVec.Pt() < antiprotonsPerpCone->GetXaxis()->GetXmax()) { + int ipt = antiprotonsPerpCone->FindBin(protonVec.Pt()); + weightUe *= antiprotonsPerpCone->GetBinContent(ipt); + } } // Fill histogram for antiprotons in the UE @@ -2269,10 +2092,12 @@ struct AntinucleiInJets { continue; // Calculate weight - double weightUeAntid(1.0); - if (applyReweighting && deuteronVec.Pt() < antideuteronsPerpCone->GetXaxis()->GetXmax()) { - int ipt = antideuteronsPerpCone->FindBin(deuteronVec.Pt()); - weightUeAntid = antideuteronsPerpCone->GetBinContent(ipt); + double weightUeAntid = genJetPtWeight; + if (applyReweighting) { + if (deuteronVec.Pt() >= antideuteronsPerpCone->GetXaxis()->GetXmin() && deuteronVec.Pt() < antideuteronsPerpCone->GetXaxis()->GetXmax()) { + int ipt = antideuteronsPerpCone->FindBin(deuteronVec.Pt()); + weightUeAntid *= antideuteronsPerpCone->GetBinContent(ipt); + } } // Fill histogram for antideuterons in the UE @@ -2288,12 +2113,6 @@ struct AntinucleiInJets { registryMC.fill(HIST("genEvents"), 5.5); } } - - // Shrink large vectors - if (eventCounter % shrinkInterval == 0) { - std::vector().swap(fjParticles); - std::vector().swap(protonMomentum); - } } } PROCESS_SWITCH(AntinucleiInJets, processJetsMCgen, "process jets mc gen", false); @@ -2310,18 +2129,12 @@ struct AntinucleiInJets { fastjet::JetDefinition jetDef(fastjet::antikt_algorithm, rJet); fastjet::AreaDefinition areaDef(fastjet::active_area, fastjet::GhostedAreaSpec(1.0)); - // Event counter - int eventCounter = 0; - // Loop over all reconstructed collisions for (const auto& collision : collisions) { // Configuration registryMC.fill(HIST("settingMC"), minJetPt.value, rJet.value); - // Increment event counter - eventCounter++; - // Clear containers at the start of the event loop fjParticles.clear(); antiprotonTrackIndex.clear(); @@ -2440,6 +2253,20 @@ struct AntinucleiInJets { // Jet pt must be larger than threshold auto jetForSub = jet; fastjet::PseudoJet jetMinusBkg = backgroundSub.doRhoAreaSub(jetForSub, rhoPerp, rhoMPerp); + + // Get reconstructed jet pt reweighting factor + double recoJetPtWeight(1.0); + if (applyReweighting) { + double jetPt = jetMinusBkg.pt(); + if (jetPt >= recoJetPtReweightingFactor->GetXaxis()->GetXmin() && jetPt < recoJetPtReweightingFactor->GetXaxis()->GetXmax()) { + int jetPtBin = recoJetPtReweightingFactor->FindBin(jetPt); + recoJetPtWeight = recoJetPtReweightingFactor->GetBinContent(jetPtBin); + } + } + + // Fill jet pt distribution + registryMC.fill(HIST("jetPtMCreconstructed"), jetMinusBkg.pt()); + if (isppRefAnalysis && (jet.pt() < minJetPt || jet.pt() > maxJetPt)) continue; if (!isppRefAnalysis && (jetMinusBkg.pt() < minJetPt || jetMinusBkg.pt() > maxJetPt)) @@ -2524,20 +2351,22 @@ struct AntinucleiInJets { } // Fill inclusive antiproton spectrum - registryMC.fill(HIST("antiproton_incl_jet"), pt); + registryMC.fill(HIST("antiproton_incl_jet"), pt, recoJetPtWeight); // Select physical primary antiprotons if (!mcparticle.isPhysicalPrimary()) continue; // Fill antiproton spectrum for physical primaries - registryMC.fill(HIST("antiproton_prim_jet"), pt); + registryMC.fill(HIST("antiproton_prim_jet"), pt, recoJetPtWeight); // Calculate weights - double weightJet(1.0); - if (applyReweighting && mcparticle.pt() < antiprotonsInsideJets->GetXaxis()->GetXmax()) { - int ipt = antiprotonsInsideJets->FindBin(mcparticle.pt()); - weightJet = antiprotonsInsideJets->GetBinContent(ipt); + double weightJet = recoJetPtWeight; + if (applyReweighting) { + if (mcparticle.pt() >= antiprotonsInsideJets->GetXaxis()->GetXmin() && mcparticle.pt() < antiprotonsInsideJets->GetXaxis()->GetXmax()) { + int ipt = antiprotonsInsideJets->FindBin(mcparticle.pt()); + weightJet *= antiprotonsInsideJets->GetBinContent(ipt); + } } // Fill histograms (TPC and TOF) only for selected candidates @@ -2575,10 +2404,12 @@ struct AntinucleiInJets { continue; // Calculate weight - double weightJetAntid(1.0); - if (applyReweighting && mcparticle.pt() < antideuteronsInsideJets->GetXaxis()->GetXmax()) { - int ipt = antideuteronsInsideJets->FindBin(mcparticle.pt()); - weightJetAntid = antideuteronsInsideJets->GetBinContent(ipt); + double weightJetAntid = recoJetPtWeight; + if (applyReweighting) { + if (mcparticle.pt() >= antideuteronsInsideJets->GetXaxis()->GetXmin() && mcparticle.pt() < antideuteronsInsideJets->GetXaxis()->GetXmax()) { + int ipt = antideuteronsInsideJets->FindBin(mcparticle.pt()); + weightJetAntid *= antideuteronsInsideJets->GetBinContent(ipt); + } } // Fill histograms (TPC and TOF) only for selected candidates @@ -2643,20 +2474,22 @@ struct AntinucleiInJets { } // Fill inclusive antiproton spectrum - registryMC.fill(HIST("antiproton_incl_ue"), pt); + registryMC.fill(HIST("antiproton_incl_ue"), pt, recoJetPtWeight); // Select physical primary antiprotons if (!mcparticle.isPhysicalPrimary()) continue; // Fill antiproton spectrum for physical primaries - registryMC.fill(HIST("antiproton_prim_ue"), pt); + registryMC.fill(HIST("antiproton_prim_ue"), pt, recoJetPtWeight); // Calculate weights - double weightUe(1.0); - if (applyReweighting && mcparticle.pt() < antiprotonsPerpCone->GetXaxis()->GetXmax()) { - int ipt = antiprotonsPerpCone->FindBin(mcparticle.pt()); - weightUe = antiprotonsPerpCone->GetBinContent(ipt); + double weightUe = recoJetPtWeight; + if (applyReweighting) { + if (mcparticle.pt() >= antiprotonsPerpCone->GetXaxis()->GetXmin() && mcparticle.pt() < antiprotonsPerpCone->GetXaxis()->GetXmax()) { + int ipt = antiprotonsPerpCone->FindBin(mcparticle.pt()); + weightUe *= antiprotonsPerpCone->GetBinContent(ipt); + } } // Fill histograms (TPC and TOF) only for selected candidates @@ -2722,10 +2555,12 @@ struct AntinucleiInJets { continue; // Calculate weight - double weightUeAntid(1.0); - if (applyReweighting && mcparticle.pt() < antideuteronsPerpCone->GetXaxis()->GetXmax()) { - int ipt = antideuteronsPerpCone->FindBin(mcparticle.pt()); - weightUeAntid = antideuteronsPerpCone->GetBinContent(ipt); + double weightUeAntid = recoJetPtWeight; + if (applyReweighting) { + if (mcparticle.pt() >= antideuteronsPerpCone->GetXaxis()->GetXmin() && mcparticle.pt() < antideuteronsPerpCone->GetXaxis()->GetXmax()) { + int ipt = antideuteronsPerpCone->FindBin(mcparticle.pt()); + weightUeAntid *= antideuteronsPerpCone->GetBinContent(ipt); + } } // Fill histograms (TPC and TOF) only for selected candidates @@ -2746,12 +2581,6 @@ struct AntinucleiInJets { registryMC.fill(HIST("recEvents"), 11.5); } } - - // Shrink large vectors - if (eventCounter % shrinkInterval == 0) { - std::vector().swap(fjParticles); - std::vector().swap(antiprotonTrackIndex); - } } } PROCESS_SWITCH(AntinucleiInJets, processJetsMCrec, "process jets MC rec", false); @@ -2906,7 +2735,7 @@ struct AntinucleiInJets { PROCESS_SWITCH(AntinucleiInJets, processSystData, "Process syst data", false); // Process MC with systematic variations of analysis parameters - void processSystEff(GenCollisionsMc const& genCollisions, RecCollisionsMc const& recCollisions, AntiNucleiTracksMc const& mcTracks, aod::McParticles const& mcParticles) + void processSystEff(aod::McCollisions const& genCollisions, RecCollisionsMc const& recCollisions, AntiNucleiTracksMc const& mcTracks, aod::McParticles const& mcParticles) { // Cut settings static std::vector maxDcaxySyst = { @@ -3118,743 +2947,161 @@ struct AntinucleiInJets { } PROCESS_SWITCH(AntinucleiInJets, processSystEff, "process syst mc", false); - // Process correlation - void processCorr(SelectedCollisions::iterator const& collision, AntiNucleiTracks const& tracks) + // Process coalescence + void processCoalescence(aod::McCollisions const& collisions, aod::McParticles const& mcParticles) { - // Event counter: before event selection - registryCorr.fill(HIST("eventCounter"), 0.5); + // Deuteron Mass and minimum pt + double massDeut = o2::constants::physics::MassDeuteron; + static constexpr double MinPtParticle = 0.1; - // Apply standard event selection - if (!collision.sel8() || std::fabs(collision.posZ()) > zVtx) - return; + // Define per-event particle containers + std::vector chargedParticles; + std::vector protons; + std::vector protonsFromHF; + std::vector neutrons; + std::vector fjParticles; - // Event counter: after event selection - registryCorr.fill(HIST("eventCounter"), 1.5); + // Jet and area definitions + fastjet::JetDefinition jetDef(fastjet::antikt_algorithm, rJet); + fastjet::AreaDefinition areaDef(fastjet::active_area, fastjet::GhostedAreaSpec(1.0)); - // Reject events near the ITS Read-Out Frame border - if (rejectITSROFBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) - return; - registryCorr.fill(HIST("eventCounter"), 2.5); + // Loop over all simulated collisions + for (const auto& collision : collisions) { - // Reject events at the Time Frame border - if (rejectTFBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) - return; - registryCorr.fill(HIST("eventCounter"), 3.5); + // Clear containers at the start of the event loop + chargedParticles.clear(); + protons.clear(); + protonsFromHF.clear(); + neutrons.clear(); + fjParticles.clear(); - // Require at least one ITS-TPC matched track - if (requireVtxITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) - return; - registryCorr.fill(HIST("eventCounter"), 4.5); + // Event counter: before event selection + registryMC.fill(HIST("genEventsCoalescence"), 0.5); - // Reject events with same-bunch pileup - if (rejectSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) - return; - registryCorr.fill(HIST("eventCounter"), 5.5); + // Apply event selection: require vertex position to be within the allowed z range + if (std::fabs(collision.posZ()) > zVtx) + continue; - // Require consistent FT0 vs PV z-vertex - if (requireIsGoodZvtxFT0VsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) - return; - registryCorr.fill(HIST("eventCounter"), 6.5); + // Event counter: after event selection + registryMC.fill(HIST("genEventsCoalescence"), 1.5); - // Require TOF match for at least one vertex track - if (requireIsVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) - return; - registryCorr.fill(HIST("eventCounter"), 7.5); + // Get particles in this MC collision + const auto mcParticlesThisMcColl = mcParticles.sliceBy(mcParticlesPerMcCollision, collision.globalIndex()); - // Assign event to a random subsample (0-19) - double sampleId = mRand.Integer(nSubsamples) + 0.5; + // Loop over MC particles + for (const auto& particle : mcParticlesThisMcColl) { - // Multiplicity percentile - const float multiplicity = collision.centFT0M(); + // Monte Carlo index + int mcId = particle.globalIndex(); - // Fill event counter vs centrality (full Event region) - registryCorr.fill(HIST("eventCounter_centrality_fullEvent"), multiplicity, sampleId); + // Store Protons + if (particle.isPhysicalPrimary() && std::abs(particle.pdgCode()) == PDG_t::kProton) { + protons.push_back({particle.px(), particle.py(), particle.pz(), particle.pdgCode(), mcId, false}); + } - // pt/A bins - std::vector ptOverAbins = {0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0}; - const int nBins = ptOverAbins.size() - 1; + // Store Protons from HF decays + if (isFromHF(particle, mcParticles) && std::abs(particle.pdgCode()) == PDG_t::kProton) { + protonsFromHF.push_back({particle.px(), particle.py(), particle.pz(), particle.pdgCode(), mcId, false}); + } - // Particle counters - std::vector nAntiprotonFullEvent(nBins, 0); - std::vector nAntideuteronFullEvent(nBins, 0); - int nTotProtonFullEvent(0); - int nTotDeuteronFullEvent(0); - int nTotAntiprotonFullEvent(0); - int nTotAntideuteronFullEvent(0); + // Store Neutrons + if (particle.isPhysicalPrimary() && std::abs(particle.pdgCode()) == PDG_t::kNeutron) { + neutrons.push_back({particle.px(), particle.py(), particle.pz(), particle.pdgCode(), mcId, false}); + } - // Loop over reconstructed tracks - for (auto const& track : tracks) { + // Select physical primary particles or HF decay products + if (!isPhysicalPrimaryOrFromHF(particle, mcParticles)) + continue; - // Apply track selection - if (!passedTrackSelection(track)) - continue; + // Select particles within acceptance and skip protons (will be added after coalescence) + if (particle.eta() < minEta || particle.eta() > maxEta || particle.pt() < MinPtParticle || std::abs(particle.pdgCode()) == PDG_t::kProton) + continue; + chargedParticles.push_back({particle.px(), particle.py(), particle.pz(), particle.pdgCode(), mcId, false}); + } - // Apply DCA selections - if (std::fabs(track.dcaXY()) > maxDcaxy || std::fabs(track.dcaZ()) > maxDcaz) - continue; + // Build deuterons + for (int ip = 0; ip < static_cast(protons.size()); ip++) { + auto& proton = protons[ip]; + if (proton.used) + continue; - // Particle identification using the ITS cluster size - bool passedItsPidProt(true), passedItsPidDeut(true); - double nSigmaITSprot = static_cast(itsResponse.nSigmaITS(track)); - double nSigmaITSdeut = static_cast(itsResponse.nSigmaITS(track)); + for (int in = 0; in < static_cast(neutrons.size()); in++) { + auto& neutron = neutrons[in]; + if (neutron.used) + continue; - if (applyItsPid && track.pt() < ptMaxItsPidProt && (nSigmaITSprot < nSigmaItsMin || nSigmaITSprot > nSigmaItsMax)) { - passedItsPidProt = false; - } - if (applyItsPid && track.pt() < ptMaxItsPidDeut && (nSigmaITSdeut < nSigmaItsMin || nSigmaITSdeut > nSigmaItsMax)) { - passedItsPidDeut = false; + if (passDeuteronCoalescence(proton, neutron, coalescenceMomentum, mRand)) { + + // If proton and neutron are used by coalescence they are removed + neutron.used = true; + proton.used = true; + + int sign = (proton.pdgCode > 0) ? +1 : -1; + int deuteronPdg = sign * o2::constants::physics::Pdg::kDeuteron; + + double pxDeut = proton.px + neutron.px; + double pyDeut = proton.py + neutron.py; + double pzDeut = proton.pz + neutron.pz; + double energyDeut = std::sqrt(pxDeut * pxDeut + pyDeut * pyDeut + pzDeut * pzDeut + massDeut * massDeut); + LorentzVector pd(pxDeut, pyDeut, pzDeut, energyDeut); + if (pd.Eta() < minEta || pd.Eta() > maxEta || pd.Pt() < MinPtParticle) + break; + + // Store Deuteron + chargedParticles.push_back({pxDeut, pyDeut, pzDeut, deuteronPdg, proton.mcIndex, false}); + break; + } + } } - // Kinematic range selection - if (isProton(track) && passedItsPidProt) { - if (track.pt() < ptOverAbins[0] || track.pt() >= ptOverAbins[nBins]) { + // Add unused protons + for (int ip = 0; ip < static_cast(protons.size()); ip++) { + auto& proton = protons[ip]; + if (proton.used) continue; - } - } else if (isDeuteron(track) && passedItsPidDeut) { - double ptPerNucleon = 0.5 * track.pt(); - if (ptPerNucleon < ptOverAbins[0] || ptPerNucleon >= ptOverAbins[nBins]) { + if (proton.eta() < minEta || proton.eta() > maxEta || proton.pt() < MinPtParticle) continue; - } - } else { - continue; + chargedParticles.push_back({proton.px, proton.py, proton.pz, proton.pdgCode, proton.mcIndex, proton.used}); } - // (Anti)protons - if (isProton(track) && passedItsPidProt) { - if (track.sign() > 0) { - nTotProtonFullEvent++; - } else if (track.sign() < 0) { - nTotAntiprotonFullEvent++; - int ibin = findBin(ptOverAbins, track.pt()); - nAntiprotonFullEvent[ibin]++; - } + // Add protons from HF decays + for (int ip = 0; ip < static_cast(protonsFromHF.size()); ip++) { + auto& proton = protonsFromHF[ip]; + if (proton.used) + continue; + if (proton.eta() < minEta || proton.eta() > maxEta || proton.pt() < MinPtParticle) + continue; + chargedParticles.push_back({proton.px, proton.py, proton.pz, proton.pdgCode, proton.mcIndex, proton.used}); } - // (Anti)deuterons - if (isDeuteron(track) && passedItsPidDeut) { - const double ptPerNucleon = 0.5 * track.pt(); + // Reject empty events + if (chargedParticles.empty()) + continue; + registryMC.fill(HIST("genEventsCoalescence"), 2.5); + + for (const auto& part : chargedParticles) { + if (part.used) + continue; - if (track.sign() > 0) { - nTotDeuteronFullEvent++; - } else if (track.sign() < 0) { - nTotAntideuteronFullEvent++; - int ibin = findBin(ptOverAbins, ptPerNucleon); - nAntideuteronFullEvent[ibin]++; + // Fill histograms for Full Event + if (part.pdgCode == PDG_t::kProtonBar) { + registryMC.fill(HIST("antiproton_coal_fullEvent"), part.pt()); + } + if (part.pdgCode == -o2::constants::physics::Pdg::kDeuteron) { + registryMC.fill(HIST("antideuteron_coal_fullEvent"), part.pt()); } } - } - - // Fill correlation histograms - int netProtonFullEvent = nTotProtonFullEvent - nTotAntiprotonFullEvent; - int netDeuteronFullEvent = nTotDeuteronFullEvent - nTotAntideuteronFullEvent; - registryCorr.fill(HIST("rho_fullEvent"), nTotAntideuteronFullEvent, nTotAntiprotonFullEvent, multiplicity, sampleId); - registryCorr.fill(HIST("rho_netP_netD_fullEvent"), netDeuteronFullEvent, netProtonFullEvent, sampleId); + // Fill particle array to feed to Fastjet + for (const auto& part : chargedParticles) { - // Fill efficiency histograms - for (int i = 0; i < nBins; i++) { - double ptAcenteri = 0.5 * (ptOverAbins[i] + ptOverAbins[i + 1]); + if (part.used) + continue; - registryCorr.fill(HIST("q1d_fullEvent"), nAntideuteronFullEvent[i], ptAcenteri, multiplicity, sampleId); - registryCorr.fill(HIST("q1p_fullEvent"), nAntiprotonFullEvent[i], ptAcenteri, multiplicity, sampleId); - for (int j = 0; j < nBins; j++) { - double ptAcenterj = 0.5 * (ptOverAbins[j] + ptOverAbins[j + 1]); - registryCorr.fill(HIST("q1d_square_fullEvent"), ptAcenteri, ptAcenterj, (nAntideuteronFullEvent[i] * nAntideuteronFullEvent[j]), multiplicity, sampleId); - registryCorr.fill(HIST("q1p_square_fullEvent"), ptAcenteri, ptAcenterj, (nAntiprotonFullEvent[i] * nAntiprotonFullEvent[j]), multiplicity, sampleId); - registryCorr.fill(HIST("q1d_q1p_fullEvent"), ptAcenteri, ptAcenterj, (nAntideuteronFullEvent[i] * nAntiprotonFullEvent[j]), multiplicity, sampleId); - } - } - - /* - // Loop over reconstructed tracks (refactoring: this part can be incorporated above) - int id(-1); - std::vector fjParticles; - for (auto const& track : tracks) { - id++; - if (!passedTrackSelectionForJetReconstruction(track)) - continue; - - // 4-momentum representation of a particle - fastjet::PseudoJet fourMomentum(track.px(), track.py(), track.pz(), track.energy(MassPionCharged)); - fourMomentum.set_user_index(id); - fjParticles.emplace_back(fourMomentum); - } - - // Reject empty events - if (fjParticles.empty()) - return; - registryCorr.fill(HIST("eventCounter"), 8.5); - - // Cluster particles using the anti-kt algorithm - fastjet::JetDefinition jetDef(fastjet::antikt_algorithm, rJet); - fastjet::AreaDefinition areaDef(fastjet::active_area, fastjet::GhostedAreaSpec(1.0)); - fastjet::ClusterSequenceArea cs(fjParticles, jetDef, areaDef); - std::vector jets = fastjet::sorted_by_pt(cs.inclusive_jets()); - auto [rhoPerp, rhoMPerp] = jetutilities::estimateRhoPerpCone(fjParticles, jets[0], rJet); - - // Loop over reconstructed jets - bool isAtLeastOneJetSelected = false; - for (const auto& jet : jets) { - - // Jet must be fully contained in the acceptance - if ((std::fabs(jet.eta()) + rJet) > (maxEta - deltaEtaEdge)) - continue; - - // Jet pt must be larger than threshold - auto jetForSub = jet; - fastjet::PseudoJet jetMinusBkg = backgroundSub.doRhoAreaSub(jetForSub, rhoPerp, rhoMPerp); - if (jetMinusBkg.pt() < minJetPt || jetMinusBkg.pt() > maxJetPt) - continue; - - // Apply area cut if required - double normalizedJetArea = jet.area() / (PI * rJet * rJet); - if (applyAreaCut && normalizedJetArea > maxNormalizedJetArea) - continue; - isAtLeastOneJetSelected = true; - - // Perpendicular cones - double coneRadius = std::sqrt(jet.area() / PI); - TVector3 jetAxis(jet.px(), jet.py(), jet.pz()); - TVector3 ueAxis1(0, 0, 0), ueAxis2(0, 0, 0); - getPerpendicularDirections(jetAxis, ueAxis1, ueAxis2); - if (ueAxis1.Mag() == 0 || ueAxis2.Mag() == 0) { - continue; - } - - // Get jet constituents - std::vector jetConstituents = jet.constituents(); - - // Particle counters - std::vector nAntiprotonJet(nBins, 0); - std::vector nAntideuteronJet(nBins, 0); - int nTotProtonJet(0); - int nTotDeuteronJet(0); - int nTotAntiprotonJet(0); - int nTotAntideuteronJet(0); - - // Loop over jet constituents - for (const auto& particle : jetConstituents) { - - // Get corresponding track and apply track selection criteria - auto const& track = tracks.iteratorAt(particle.user_index()); - if (!passedTrackSelection(track)) - continue; - - // Apply DCA selections - if (std::fabs(track.dcaXY()) > maxDcaxy || std::fabs(track.dcaZ()) > maxDcaz) - continue; - - // Particle identification using the ITS cluster size - bool passedItsPidProt(true), passedItsPidDeut(true); - double nSigmaITSprot = static_cast(itsResponse.nSigmaITS(track)); - double nSigmaITSdeut = static_cast(itsResponse.nSigmaITS(track)); - - if (applyItsPid && track.pt() < ptMaxItsPidProt && (nSigmaITSprot < nSigmaItsMin || nSigmaITSprot > nSigmaItsMax)) { - passedItsPidProt = false; - } - if (applyItsPid && track.pt() < ptMaxItsPidDeut && (nSigmaITSdeut < nSigmaItsMin || nSigmaITSdeut > nSigmaItsMax)) { - passedItsPidDeut = false; - } - - // Kinematic range selection - if (isProton(track) && passedItsPidProt) { - if (track.pt() < ptOverAbins[0] || track.pt() >= ptOverAbins[nBins]) { - continue; - } - } else if (isDeuteron(track) && passedItsPidDeut) { - double ptPerNucleon = 0.5 * track.pt(); - if (ptPerNucleon < ptOverAbins[0] || ptPerNucleon >= ptOverAbins[nBins]) { - continue; - } - } else { - continue; - } - - // (Anti)protons - if (isProton(track) && passedItsPidProt) { - if (track.sign() > 0) { - nTotProtonJet++; - } else if (track.sign() < 0) { - nTotAntiprotonJet++; - int ibin = findBin(ptOverAbins, track.pt()); - nAntiprotonJet[ibin]++; - } - } - - // (Anti)deuterons - if (isDeuteron(track) && passedItsPidDeut) { - const double ptPerNucleon = 0.5 * track.pt(); - - if (track.sign() > 0) { - nTotDeuteronJet++; - } else if (track.sign() < 0) { - nTotAntideuteronJet++; - int ibin = findBin(ptOverAbins, ptPerNucleon); - nAntideuteronJet[ibin]++; - } - } - } // end of loop over constituents - - // Fill correlation histograms - int netProtonJet = nTotProtonJet - nTotAntiprotonJet; - int netDeuteronJet = nTotDeuteronJet - nTotAntideuteronJet; - registryCorr.fill(HIST("rho_jet"), nTotAntideuteronJet, nTotAntiprotonJet, multiplicity); - registryCorr.fill(HIST("rho_netP_netD_jet"), netDeuteronJet, netProtonJet); - - // Fill efficiency histograms - for (int i = 0; i < nBins; i++) { - double ptAcenteri = 0.5 * (ptOverAbins[i] + ptOverAbins[i + 1]); - - registryCorr.fill(HIST("q1d_jet"), nAntideuteronJet[i], ptAcenteri, multiplicity); - registryCorr.fill(HIST("q1p_jet"), nAntiprotonJet[i], ptAcenteri, multiplicity); - for (int j = 0; j < nBins; j++) { - double ptAcenterj = 0.5 * (ptOverAbins[j] + ptOverAbins[j + 1]); - registryCorr.fill(HIST("q1d_square_jet"), ptAcenteri, ptAcenterj, nAntideuteronJet[i] * nAntideuteronJet[j], multiplicity); - registryCorr.fill(HIST("q1p_square_jet"), ptAcenteri, ptAcenterj, nAntiprotonJet[i] * nAntiprotonJet[j], multiplicity); - registryCorr.fill(HIST("q1d_q1p_jet"), ptAcenteri, ptAcenterj, nAntideuteronJet[i] * nAntiprotonJet[j], multiplicity); - } - } - - // Particle counters - std::vector nAntiprotonUE(nBins, 0); - std::vector nAntideuteronUE(nBins, 0); - int nTotProtonUE(0); - int nTotDeuteronUE(0); - int nTotAntiprotonUE(0); - int nTotAntideuteronUE(0); - - // Loop over tracks in the underlying event - for (auto const& track : tracks) { - - // Get corresponding track and apply track selection criteria - if (!passedTrackSelection(track)) - continue; - - // Apply DCA selections - if (std::fabs(track.dcaXY()) > maxDcaxy || std::fabs(track.dcaZ()) > maxDcaz) - continue; - - // Calculate the angular distance between the track and underlying event axes in eta-phi space - double deltaEtaUe1 = track.eta() - ueAxis1.Eta(); - double deltaPhiUe1 = getDeltaPhi(track.phi(), ueAxis1.Phi()); - double deltaRUe1 = std::sqrt(deltaEtaUe1 * deltaEtaUe1 + deltaPhiUe1 * deltaPhiUe1); - double deltaEtaUe2 = track.eta() - ueAxis2.Eta(); - double deltaPhiUe2 = getDeltaPhi(track.phi(), ueAxis2.Phi()); - double deltaRUe2 = std::sqrt(deltaEtaUe2 * deltaEtaUe2 + deltaPhiUe2 * deltaPhiUe2); - - // Determine the maximum allowed distance from UE axes for particle selection - double maxConeRadius = coneRadius; - if (applyAreaCut) { - maxConeRadius = std::sqrt(maxNormalizedJetArea) * rJet; - } - - // Reject tracks that lie outside the maxConeRadius from both UE axes - if (deltaRUe1 > maxConeRadius && deltaRUe2 > maxConeRadius) - continue; - - // Particle identification using the ITS cluster size - bool passedItsPidProt(true), passedItsPidDeut(true); - double nSigmaITSprot = static_cast(itsResponse.nSigmaITS(track)); - double nSigmaITSdeut = static_cast(itsResponse.nSigmaITS(track)); - - if (applyItsPid && track.pt() < ptMaxItsPidProt && (nSigmaITSprot < nSigmaItsMin || nSigmaITSprot > nSigmaItsMax)) { - passedItsPidProt = false; - } - if (applyItsPid && track.pt() < ptMaxItsPidDeut && (nSigmaITSdeut < nSigmaItsMin || nSigmaITSdeut > nSigmaItsMax)) { - passedItsPidDeut = false; - } - - // Kinematic range selection - if (isProton(track) && passedItsPidProt) { - if (track.pt() < ptOverAbins[0] || track.pt() >= ptOverAbins[nBins]) { - continue; - } - } else if (isDeuteron(track) && passedItsPidDeut) { - double ptPerNucleon = 0.5 * track.pt(); - if (ptPerNucleon < ptOverAbins[0] || ptPerNucleon >= ptOverAbins[nBins]) { - continue; - } - } else { - continue; - } - - // (Anti)protons - if (isProton(track) && passedItsPidProt) { - if (track.sign() > 0) { - nTotProtonUE++; - } else if (track.sign() < 0) { - nTotAntiprotonUE++; - int ibin = findBin(ptOverAbins, track.pt()); - nAntiprotonUE[ibin]++; - } - } - - // (Anti)deuterons - if (isDeuteron(track) && passedItsPidDeut) { - const double ptPerNucleon = 0.5 * track.pt(); - - if (track.sign() > 0) { - nTotDeuteronUE++; - } else if (track.sign() < 0) { - nTotAntideuteronUE++; - int ibin = findBin(ptOverAbins, ptPerNucleon); - nAntideuteronUE[ibin]++; - } - } - } - - // Fill correlation histograms - int netProtonUE = nTotProtonUE - nTotAntiprotonUE; - int netDeuteronUE = nTotDeuteronUE - nTotAntideuteronUE; - registryCorr.fill(HIST("rho_ue"), nTotAntideuteronUE, nTotAntiprotonUE, multiplicity); - registryCorr.fill(HIST("rho_netP_netD_ue"), netDeuteronUE, netProtonUE); - - // Fill efficiency histograms - for (int i = 0; i < nBins; i++) { - double ptAcenteri = 0.5 * (ptOverAbins[i] + ptOverAbins[i + 1]); - - registryCorr.fill(HIST("q1d_ue"), nAntideuteronUE[i], ptAcenteri, multiplicity); - registryCorr.fill(HIST("q1p_ue"), nAntiprotonUE[i], ptAcenteri, multiplicity); - for (int j = 0; j < nBins; j++) { - double ptAcenterj = 0.5 * (ptOverAbins[j] + ptOverAbins[j + 1]); - registryCorr.fill(HIST("q1d_square_ue"), ptAcenteri, ptAcenterj, nAntideuteronUE[i] * nAntideuteronUE[j], multiplicity); - registryCorr.fill(HIST("q1p_square_ue"), ptAcenteri, ptAcenterj, nAntiprotonUE[i] * nAntiprotonUE[j], multiplicity); - registryCorr.fill(HIST("q1d_q1p_ue"), ptAcenteri, ptAcenterj, nAntideuteronUE[i] * nAntiprotonUE[j], multiplicity); - } - } - // Fill event counter vs centrality (ue region) - registryCorr.fill(HIST("eventCounter_centrality_ue"), multiplicity); - } - // Event counter: events with at least one jet selected - if (isAtLeastOneJetSelected) { - registryCorr.fill(HIST("eventCounter"), 9.5); - registryCorr.fill(HIST("eventCounter_centrality_jet"), multiplicity); - } - */ - } - PROCESS_SWITCH(AntinucleiInJets, processCorr, "Process Correlation analysis", false); - - // Process correlation analysis with systematic variations of analysis parameters - void processSystCorr(SelectedCollisions::iterator const& collision, AntiNucleiTracks const& tracks) - { - // cut settings (from processSystData) - static std::vector maxDcaxySyst = { - 0.071, 0.060, 0.066, 0.031, 0.052, 0.078, 0.045, 0.064, 0.036, 0.074, - 0.079, 0.043, 0.067, 0.059, 0.032, 0.070, 0.048, 0.077, 0.062, 0.034, - 0.057, 0.055, 0.073, 0.038, 0.050, 0.075, 0.041, 0.061, 0.033, 0.069, - 0.035, 0.044, 0.076, 0.049, 0.037, 0.054, 0.072, 0.046, 0.058, 0.040, - 0.068, 0.042, 0.056, 0.039, 0.047, 0.065, 0.051, 0.053, 0.063, 0.030}; - - static std::vector maxDcazSyst = { - 0.064, 0.047, 0.032, 0.076, 0.039, 0.058, 0.043, 0.069, 0.050, 0.035, - 0.074, 0.061, 0.045, 0.033, 0.068, 0.055, 0.037, 0.071, 0.042, 0.053, - 0.077, 0.038, 0.065, 0.049, 0.036, 0.059, 0.044, 0.067, 0.041, 0.034, - 0.073, 0.052, 0.040, 0.063, 0.046, 0.031, 0.070, 0.054, 0.037, 0.062, - 0.048, 0.035, 0.075, 0.051, 0.039, 0.066, 0.043, 0.060, 0.032, 0.056}; - - static std::vector nSigmaItsMinSyst = { - -2.9, -2.8, -3.0, -3.4, -2.7, -3.3, -3.0, -3.1, -3.4, -3.1, - -3.0, -2.8, -3.2, -2.6, -2.7, -3.4, -2.9, -3.0, -3.0, -2.7, - -2.9, -3.3, -3.0, -3.1, -3.2, -3.0, -2.9, -2.7, -3.3, -3.0, - -2.8, -3.3, -2.7, -3.3, -2.8, -3.4, -2.8, -3.4, -2.9, -3.1, - -3.2, -2.6, -3.1, -2.9, -3.1, -2.8, -2.9, -3.3, -3.0, -2.8}; - - static std::vector nSigmaItsMaxSyst = { - 2.9, 2.8, 3.0, 3.4, 2.7, 3.3, 3.0, 3.1, 3.4, 3.1, 3.0, 2.8, 3.2, - 2.6, 2.7, 3.4, 2.9, 3.0, 3.0, 2.7, 2.9, 3.3, 3.0, 3.1, 3.2, 3.0, - 2.9, 2.7, 3.3, 3.0, 2.8, 3.3, 2.7, 3.3, 2.8, 3.4, 2.8, 3.4, 2.9, - 3.1, 3.2, 2.6, 3.1, 2.9, 3.1, 2.8, 2.9, 3.3, 3.0, 2.8}; - - static std::vector minNsigmaTpcSyst = { - -3.2, -2.9, -3.1, -2.9, -3.5, -2.6, -3.3, -3.0, -3.5, -2.7, - -3.0, -2.6, -3.3, -3.4, -2.8, -3.1, -2.6, -3.2, -3.1, -2.8, - -3.4, -2.7, -3.4, -2.9, -3.0, -2.5, -3.3, -2.8, -3.1, -2.7, - -3.4, -2.8, -3.3, -2.6, -3.1, -2.5, -3.4, -3.0, -3.2, -2.6, - -3.4, -2.8, -3.1, -2.6, -3.3, -2.7, -3.2, -2.7, -3.4, -2.9}; - - static std::vector maxNsigmaTpcSyst = { - 3.2, 2.9, 3.1, 2.9, 3.5, 2.6, 3.3, 3.0, 3.5, 2.7, 3.0, 2.6, 3.3, - 3.4, 2.8, 3.1, 2.6, 3.2, 3.1, 2.8, 3.4, 2.7, 3.4, 2.9, 3.0, 2.5, - 3.3, 2.8, 3.1, 2.7, 3.4, 2.8, 3.3, 2.6, 3.1, 2.5, 3.4, 3.0, 3.2, - 2.6, 3.4, 2.8, 3.1, 2.6, 3.3, 2.7, 3.2, 2.7, 3.4, 2.9}; - - static std::vector minNsigmaTofSyst = { - -3.2, -2.9, -3.1, -2.9, -3.5, -2.6, -3.3, -3.0, -3.5, -2.7, - -3.0, -2.6, -3.3, -3.4, -2.8, -3.1, -2.6, -3.2, -3.1, -2.8, - -3.4, -2.7, -3.4, -2.9, -3.0, -2.5, -3.3, -2.8, -3.1, -2.7, - -3.4, -2.8, -3.3, -2.6, -3.1, -2.5, -3.4, -3.0, -3.2, -2.6, - -3.4, -2.8, -3.1, -2.6, -3.3, -2.7, -3.2, -2.7, -3.4, -2.9}; - - static std::vector maxNsigmaTofSyst = { - 3.9, 3.6, 3.8, 3.2, 3.2, 3.5, 3.1, 3.8, 3.5, 3.4, 3.9, 3.8, 3.7, - 3.0, 3.6, 3.1, 3.7, 3.4, 4.0, 3.0, 3.7, 3.3, 3.9, 3.1, 3.3, 3.5, - 3.6, 3.2, 3.5, 3.3, 3.9, 3.0, 3.4, 3.2, 3.1, 3.9, 3.6, 3.1, 3.2, - 4.0, 3.1, 3.7, 3.6, 3.1, 3.3, 3.5, 3.3, 3.4, 3.1, 3.8}; - - // Event counter: before event selection - registryCorr.fill(HIST("eventCounter_syst"), 0.5); - - // Apply standard event selection (Same as processCorr) - if (!collision.sel8() || std::fabs(collision.posZ()) > zVtx) - return; - - registryCorr.fill(HIST("eventCounter_syst"), 1.5); - - if (rejectITSROFBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) - return; - registryCorr.fill(HIST("eventCounter_syst"), 2.5); - - if (rejectTFBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) - return; - registryCorr.fill(HIST("eventCounter_syst"), 3.5); - - if (requireVtxITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) - return; - registryCorr.fill(HIST("eventCounter_syst"), 4.5); - - if (rejectSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) - return; - registryCorr.fill(HIST("eventCounter_syst"), 5.5); - - if (requireIsGoodZvtxFT0VsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) - return; - registryCorr.fill(HIST("eventCounter_syst"), 6.5); - - if (requireIsVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) - return; - registryCorr.fill(HIST("eventCounter_syst"), 7.5); - - const float multiplicity = collision.centFT0M(); - - // Bins setup - static const std::vector ptOverAbins = {0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0}; - const int nBins = ptOverAbins.size() - 1; - - // Clamp systematic index range - const int systStart = std::max(0, static_cast(systIndexStart.value)); - const int systEnd = std::min(static_cast(nSyst), static_cast(systIndexEnd.value)); - - // Loop over systematic variations - for (int isyst = systStart; isyst < systEnd; isyst++) { - - // Fill event counter for this systematic - registryCorr.fill(HIST("eventCounter_centrality_fullEvent_syst"), multiplicity, static_cast(isyst)); - - // Particle counters for this specific cut setting - std::vector nAntiprotonFullEvent(nBins, 0); - std::vector nAntideuteronFullEvent(nBins, 0); - int nTotProtonFullEvent(0); - int nTotDeuteronFullEvent(0); - int nTotAntiprotonFullEvent(0); - int nTotAntideuteronFullEvent(0); - - // Loop over tracks - for (auto const& track : tracks) { - - // Apply track selection (from processSystData) - if (!passedTrackSelectionSyst(track, isyst)) - continue; - - // Apply DCA selections (from processSystData) - if (std::fabs(track.dcaXY()) > maxDcaxySyst[isyst] || std::fabs(track.dcaZ()) > maxDcazSyst[isyst]) - continue; - - // Particle identification using the ITS cluster size (vary also PID ITS) (from processSystData) - bool passedItsPidProt(true), passedItsPidDeut(true); - double nSigmaITSprot = static_cast(itsResponse.nSigmaITS(track)); - double nSigmaITSdeut = static_cast(itsResponse.nSigmaITS(track)); - - if (applyItsPid && track.pt() < ptMaxItsPidProt && (nSigmaITSprot < nSigmaItsMinSyst[isyst] || nSigmaITSprot > nSigmaItsMaxSyst[isyst])) { - passedItsPidProt = false; - } - if (applyItsPid && track.pt() < ptMaxItsPidDeut && (nSigmaITSdeut < nSigmaItsMinSyst[isyst] || nSigmaITSdeut > nSigmaItsMaxSyst[isyst])) { - passedItsPidDeut = false; - } - - // Check Identity (Proton/Deuteron) using TPC/TOF - bool isProt = isProtonSyst(track, minNsigmaTpcSyst[isyst], maxNsigmaTpcSyst[isyst], minNsigmaTofSyst[isyst], maxNsigmaTofSyst[isyst]); - bool isDeut = isDeuteronSyst(track, minNsigmaTpcSyst[isyst], maxNsigmaTpcSyst[isyst], minNsigmaTofSyst[isyst], maxNsigmaTofSyst[isyst]); - - // Kinematic range selection & counting - // (Anti)protons - if (isProt && passedItsPidProt) { - if (track.pt() >= ptOverAbins[0] && track.pt() < ptOverAbins[nBins]) { - if (track.sign() > 0) { - nTotProtonFullEvent++; - } else if (track.sign() < 0) { - nTotAntiprotonFullEvent++; - int ibin = findBin(ptOverAbins, track.pt()); - nAntiprotonFullEvent[ibin]++; - } - } - } - - // (Anti)deuterons - if (isDeut && passedItsPidDeut) { - double ptPerNucleon = 0.5 * track.pt(); - if (ptPerNucleon >= ptOverAbins[0] && ptPerNucleon < ptOverAbins[nBins]) { - if (track.sign() > 0) { - nTotDeuteronFullEvent++; - } else if (track.sign() < 0) { - nTotAntideuteronFullEvent++; - int ibin = findBin(ptOverAbins, ptPerNucleon); - nAntideuteronFullEvent[ibin]++; - } - } - } - } - - // Fill Correlation Histograms for systematic variations - int netProtonFullEvent = nTotProtonFullEvent - nTotAntiprotonFullEvent; - int netDeuteronFullEvent = nTotDeuteronFullEvent - nTotAntideuteronFullEvent; - - registryCorr.fill(HIST("rho_fullEvent_syst"), nTotAntideuteronFullEvent, nTotAntiprotonFullEvent, multiplicity, static_cast(isyst)); - registryCorr.fill(HIST("rho_netP_netD_fullEvent_syst"), netDeuteronFullEvent, netProtonFullEvent, static_cast(isyst)); - - // Fill efficiency histograms for systematic variations - for (int i = 0; i < nBins; i++) { - double ptAcenteri = 0.5 * (ptOverAbins[i] + ptOverAbins[i + 1]); - - registryCorr.fill(HIST("q1d_fullEvent_syst"), nAntideuteronFullEvent[i], ptAcenteri, multiplicity, static_cast(isyst)); - registryCorr.fill(HIST("q1p_fullEvent_syst"), nAntiprotonFullEvent[i], ptAcenteri, multiplicity, static_cast(isyst)); - for (int j = 0; j < nBins; j++) { - double ptAcenterj = 0.5 * (ptOverAbins[j] + ptOverAbins[j + 1]); - registryCorr.fill(HIST("q1d_square_fullEvent_syst"), ptAcenteri, ptAcenterj, (nAntideuteronFullEvent[i] * nAntideuteronFullEvent[j]), multiplicity, static_cast(isyst)); - registryCorr.fill(HIST("q1p_square_fullEvent_syst"), ptAcenteri, ptAcenterj, (nAntiprotonFullEvent[i] * nAntiprotonFullEvent[j]), multiplicity, static_cast(isyst)); - registryCorr.fill(HIST("q1d_q1p_fullEvent_syst"), ptAcenteri, ptAcenterj, (nAntideuteronFullEvent[i] * nAntiprotonFullEvent[j]), multiplicity, static_cast(isyst)); - } - } - } - } - PROCESS_SWITCH(AntinucleiInJets, processSystCorr, "Process Correlation systematics", false); - - // Process coalescence - void processCoalescence(GenCollisionsMc const& collisions, aod::McParticles const& mcParticles) - { - // Deuteron Mass and minimum pt - double massDeut = o2::constants::physics::MassDeuteron; - static constexpr double MinPtParticle = 0.1; - - // Define per-event particle containers - std::vector chargedParticles; - std::vector protons; - std::vector neutrons; - std::vector fjParticles; - - // Jet and area definitions - fastjet::JetDefinition jetDef(fastjet::antikt_algorithm, rJet); - fastjet::AreaDefinition areaDef(fastjet::active_area, fastjet::GhostedAreaSpec(1.0)); - - // Loop over all simulated collisions - for (const auto& collision : collisions) { - - // Clear containers at the start of the event loop - chargedParticles.clear(); - protons.clear(); - neutrons.clear(); - fjParticles.clear(); - - // Event counter: before event selection - registryMC.fill(HIST("genEventsCoalescence"), 0.5); - - // Apply event selection: require vertex position to be within the allowed z range - if (std::fabs(collision.posZ()) > zVtx) - continue; - - // Event counter: after event selection - registryMC.fill(HIST("genEventsCoalescence"), 1.5); - - // Get particles in this MC collision - const auto mcParticlesThisMcColl = mcParticles.sliceBy(mcParticlesPerMcCollision, collision.globalIndex()); - - // Loop over MC particles - for (const auto& particle : mcParticlesThisMcColl) { - - // Monte Carlo index - int mcId = particle.globalIndex(); - - // Store Protons - if (particle.isPhysicalPrimary() && std::abs(particle.pdgCode()) == PDG_t::kProton) { - protons.push_back({particle.px(), particle.py(), particle.pz(), particle.pdgCode(), mcId, false}); - } - - // Store Neutrons - if (particle.isPhysicalPrimary() && std::abs(particle.pdgCode()) == PDG_t::kNeutron) { - neutrons.push_back({particle.px(), particle.py(), particle.pz(), particle.pdgCode(), mcId, false}); - } - - // Select physical primary particles or HF decay products - if (!isPhysicalPrimaryOrFromHF(particle, mcParticles)) - continue; - - // Select particles within acceptance - if (particle.eta() < minEta || particle.eta() > maxEta || particle.pt() < MinPtParticle) - continue; - chargedParticles.push_back({particle.px(), particle.py(), particle.pz(), particle.pdgCode(), mcId, false}); - } - - // Reject empty events - if (chargedParticles.empty()) - continue; - registryMC.fill(HIST("genEventsCoalescence"), 2.5); - - // Build deuterons - for (int ip = 0; ip < static_cast(protons.size()); ip++) { - auto& proton = protons[ip]; - if (proton.used) - continue; - - for (int in = 0; in < static_cast(neutrons.size()); in++) { - auto& neutron = neutrons[in]; - if (neutron.used) - continue; - - if (passDeuteronCoalescence(proton, neutron, coalescenceMomentum, mRand)) { - - int sign = (proton.pdgCode > 0) ? +1 : -1; - int deuteronPdg = sign * o2::constants::physics::Pdg::kDeuteron; - - double pxDeut = proton.px + neutron.px; - double pyDeut = proton.py + neutron.py; - double pzDeut = proton.pz + neutron.pz; - double energyDeut = std::sqrt(pxDeut * pxDeut + pyDeut * pyDeut + pzDeut * pzDeut + massDeut * massDeut); - LorentzVector pd(pxDeut, pyDeut, pzDeut, energyDeut); - if (pd.Eta() < minEta || pd.Eta() > maxEta || pd.Pt() < MinPtParticle) - continue; - - // Store Deuteron - chargedParticles.push_back({pxDeut, pyDeut, pzDeut, deuteronPdg, proton.mcIndex, false}); - - neutron.used = true; - proton.used = true; - break; - } - } - } - - for (const auto& part : chargedParticles) { - if (part.used) - continue; - - // Fill histograms for Full Event - if (part.pdgCode == PDG_t::kProtonBar) { - registryMC.fill(HIST("antiproton_coal_fullEvent"), part.pt()); - } - if (part.pdgCode == -o2::constants::physics::Pdg::kDeuteron) { - registryMC.fill(HIST("antideuteron_coal_fullEvent"), part.pt()); - } - } - - // Fill particle array to feed to Fastjet - for (const auto& part : chargedParticles) { - - if (part.used) - continue; - - double energy = std::sqrt(part.px * part.px + part.py * part.py + part.pz * part.pz + MassPionCharged * MassPionCharged); - fastjet::PseudoJet fourMomentum(part.px, part.py, part.pz, energy); - fourMomentum.set_user_index(part.pdgCode); - fjParticles.emplace_back(fourMomentum); + double energy = std::sqrt(part.px * part.px + part.py * part.py + part.pz * part.pz + MassPionCharged * MassPionCharged); + fastjet::PseudoJet fourMomentum(part.px, part.py, part.pz, energy); + fourMomentum.set_user_index(part.pdgCode); + fjParticles.emplace_back(fourMomentum); } // Reject empty events @@ -3892,10 +3139,29 @@ struct AntinucleiInJets { // Analyze jet constituents std::vector jetConstituents = jet.constituents(); for (const auto& particle : jetConstituents) { + + // Calculate weight for antiprotons + double wAntipJet = 1.0; + if (applyReweighting) { + if (particle.pt() >= antiprotonsInsideJets->GetXaxis()->GetXmin() && particle.pt() < antiprotonsInsideJets->GetXaxis()->GetXmax()) { + int ipt = antiprotonsInsideJets->FindBin(particle.pt()); + wAntipJet = antiprotonsInsideJets->GetBinContent(ipt); + } + } + + // Calculate weight for antideuterons + double wAntidJet = 1.0; + if (applyReweighting) { + if (0.5 * particle.pt() >= antiprotonsInsideJets->GetXaxis()->GetXmin() && 0.5 * particle.pt() < antiprotonsInsideJets->GetXaxis()->GetXmax()) { + int ipt = antiprotonsInsideJets->FindBin(0.5 * particle.pt()); + wAntidJet = antiprotonsInsideJets->GetBinContent(ipt) * antiprotonsInsideJets->GetBinContent(ipt); + } + } + if (particle.user_index() == PDG_t::kProtonBar) - registryMC.fill(HIST("antiproton_coal_jet"), particle.pt()); + registryMC.fill(HIST("antiproton_coal_jet"), particle.pt(), wAntipJet); if (particle.user_index() == -o2::constants::physics::Pdg::kDeuteron) - registryMC.fill(HIST("antideuteron_coal_jet"), particle.pt()); + registryMC.fill(HIST("antideuteron_coal_jet"), particle.pt(), wAntidJet); } // Set up two perpendicular cone axes for underlying event estimation @@ -3925,11 +3191,29 @@ struct AntinucleiInJets { if (deltaRUe1 > rJet && deltaRUe2 > rJet) continue; + // Calculate weight for antiprotons + double wAntipUE = 1.0; + if (applyReweighting) { + if (chParticle.pt() >= antiprotonsPerpCone->GetXaxis()->GetXmin() && chParticle.pt() < antiprotonsPerpCone->GetXaxis()->GetXmax()) { + int ipt = antiprotonsPerpCone->FindBin(chParticle.pt()); + wAntipUE = antiprotonsPerpCone->GetBinContent(ipt); + } + } + + // Calculate weight for antideuterons + double wAntidUE = 1.0; + if (applyReweighting) { + if (0.5 * chParticle.pt() >= antiprotonsPerpCone->GetXaxis()->GetXmin() && 0.5 * chParticle.pt() < antiprotonsPerpCone->GetXaxis()->GetXmax()) { + int ipt = antiprotonsPerpCone->FindBin(0.5 * chParticle.pt()); + wAntidUE = antiprotonsPerpCone->GetBinContent(ipt) * antiprotonsPerpCone->GetBinContent(ipt); + } + } + // Fill histograms for UE if (chParticle.pdgCode == PDG_t::kProtonBar) - registryMC.fill(HIST("antiproton_coal_ue"), chParticle.pt()); + registryMC.fill(HIST("antiproton_coal_ue"), chParticle.pt(), wAntipUE); if (chParticle.pdgCode == -o2::constants::physics::Pdg::kDeuteron) - registryMC.fill(HIST("antideuteron_coal_ue"), chParticle.pt()); + registryMC.fill(HIST("antideuteron_coal_ue"), chParticle.pt(), wAntidUE); } } if (isAtLeastOneJetSelected) { @@ -3939,209 +3223,6 @@ struct AntinucleiInJets { } PROCESS_SWITCH(AntinucleiInJets, processCoalescence, "process coalescence", false); - // process Coalescence and Correlation Analysis - void processCoalescenceCorr(GenCollisionsMc const& collisions, aod::McParticles const& mcParticles) - { - // Deuteron Mass and minimum pt - double massDeut = o2::constants::physics::MassDeuteron; - static constexpr double MinPtPerNucleon = 0.1; // Cut on pt/A - - // Containers for candidates (before coalescence) - std::vector protonCandidates; - std::vector neutronCandidates; - - // Final containers for analysis (after coalescence) - std::vector finalProtons; - std::vector finalDeuterons; - - // pt/A bins - std::vector ptOverAbins = {0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0}; - const int nBins = ptOverAbins.size() - 1; - - // Loop over all simulated collisions - for (const auto& collision : collisions) { - - // Clear containers - protonCandidates.clear(); - neutronCandidates.clear(); - finalProtons.clear(); - finalDeuterons.clear(); - - // Event counter: before event selection - registryCorr.fill(HIST("eventCounter_CoalescenceCorr"), 0.5); - registryMC.fill(HIST("genEventsCoalescenceCorr"), 0.5); - - // Apply event selection: require vertex position to be within the allowed z range - if (std::fabs(collision.posZ()) > zVtx) - continue; - - // Event counter: after event selection - registryCorr.fill(HIST("eventCounter_CoalescenceCorr"), 1.5); - registryMC.fill(HIST("genEventsCoalescenceCorr"), 1.5); - - // Multiplicity percentile - const float multiplicity = collision.centFT0M(); - - // Fill event counter vs centrality - registryCorr.fill(HIST("eventCounter_centrality_fullEvent_CoalescenceCorr"), multiplicity); - - // Get particles in this MC collision - const auto mcParticlesThisMcColl = mcParticles.sliceBy(mcParticlesPerMcCollision, collision.globalIndex()); - - // Loop over MC particles - for (const auto& particle : mcParticlesThisMcColl) { - - // Monte Carlo index - int mcId = particle.globalIndex(); - int pdg = particle.pdgCode(); - int absPdg = std::abs(pdg); - - // Store Protons - if (particle.isPhysicalPrimary()) { - if (absPdg == PDG_t::kProton) { - protonCandidates.push_back({particle.px(), particle.py(), particle.pz(), pdg, mcId, false}); - } else if (absPdg == PDG_t::kNeutron) { // Store Neutrons - neutronCandidates.push_back({particle.px(), particle.py(), particle.pz(), pdg, mcId, false}); - } - } - } - - // Reject empty events - if (protonCandidates.empty() && neutronCandidates.empty()) - continue; - - registryMC.fill(HIST("genEventsCoalescenceCorr"), 2.5); - - // Build deuterons - for (size_t iP = 0; iP < protonCandidates.size(); ++iP) { - if (protonCandidates[iP].used) - continue; - - for (size_t iN = 0; iN < neutronCandidates.size(); ++iN) { - if (neutronCandidates[iN].used) - continue; - - // Physics consistency check - if (protonCandidates[iP].pdgCode * neutronCandidates[iN].pdgCode < 0) - continue; - - if (passDeuteronCoalescence(protonCandidates[iP], neutronCandidates[iN], coalescenceMomentum, mRand)) { - neutronCandidates[iN].used = true; - protonCandidates[iP].used = true; - - int sign = (protonCandidates[iP].pdgCode > 0) ? +1 : -1; - int deuteronPdg = sign * o2::constants::physics::Pdg::kDeuteron; - - double pxDeut = protonCandidates[iP].px + neutronCandidates[iN].px; - double pyDeut = protonCandidates[iP].py + neutronCandidates[iN].py; - double pzDeut = protonCandidates[iP].pz + neutronCandidates[iN].pz; - double energyDeut = std::sqrt(pxDeut * pxDeut + pyDeut * pyDeut + pzDeut * pzDeut + massDeut * massDeut); - LorentzVector pd(pxDeut, pyDeut, pzDeut, energyDeut); - if (pd.Eta() >= minEta && pd.Eta() <= maxEta && (0.5 * pd.Pt()) >= MinPtPerNucleon) { - // Store Deuteron - finalDeuterons.push_back({pxDeut, pyDeut, pzDeut, deuteronPdg, protonCandidates[iP].mcIndex, false}); - } - - break; - } - } - } - - // Add unused protons to final vectors - for (const auto& proton : protonCandidates) { - if (!proton.used) { - finalProtons.push_back(proton); - } - } - - // Correlation Analysis - std::vector nAntiprotonFullEvent(nBins, 0); - std::vector nAntideuteronFullEvent(nBins, 0); - int nTotProtonFullEvent(0); - int nTotDeuteronFullEvent(0); - int nTotAntiprotonFullEvent(0); - int nTotAntideuteronFullEvent(0); - - // Loop over final protons - for (const auto& part : finalProtons) { - double pt = std::hypot(part.px, part.py); - - if (part.eta() < minEta || part.eta() > maxEta) - continue; - - // Standard histograms for antiprotons - if (part.pdgCode == PDG_t::kProtonBar) { - registryMC.fill(HIST("antiproton_fullEvent_CoalescenceCorr"), pt); - } - - // Kinematic selection and Multiplicity counting - if (pt < ptOverAbins[0] || pt >= ptOverAbins[nBins]) - continue; - - if (part.pdgCode > 0) { - nTotProtonFullEvent++; - } else { - nTotAntiprotonFullEvent++; - int ibin = findBin(ptOverAbins, pt); - if (ibin >= 0 && ibin < nBins) - nAntiprotonFullEvent[ibin]++; - } - } - - // Loop over final deuterons - for (const auto& part : finalDeuterons) { - double pt = std::hypot(part.px, part.py); - double ptPerNucleon = 0.5 * pt; - - // Apply detector acceptance cuts (to match real data) - if (part.eta() < minEta || part.eta() > maxEta) - continue; - - // Standard histograms for antideuterons - if (part.pdgCode == -o2::constants::physics::Pdg::kDeuteron) { - registryMC.fill(HIST("antideuteron_fullEvent_CoalescenceCorr"), pt); - } - - // Kinematic selection and Multiplicity counting - if (ptPerNucleon < ptOverAbins[0] || ptPerNucleon >= ptOverAbins[nBins]) - continue; - - if (part.pdgCode > 0) { - nTotDeuteronFullEvent++; - } else { - nTotAntideuteronFullEvent++; - int ibin = findBin(ptOverAbins, ptPerNucleon); - if (ibin >= 0 && ibin < nBins) - nAntideuteronFullEvent[ibin]++; - } - } - - // Fill correlation histograms - int netProtonFullEvent = nTotProtonFullEvent - nTotAntiprotonFullEvent; - int netDeuteronFullEvent = nTotDeuteronFullEvent - nTotAntideuteronFullEvent; - - registryCorr.fill(HIST("rho_fullEvent_CoalescenceCorr"), nTotAntideuteronFullEvent, nTotAntiprotonFullEvent, multiplicity); - registryCorr.fill(HIST("rho_netP_netD_fullEvent_CoalescenceCorr"), netDeuteronFullEvent, netProtonFullEvent); - - // Fill efficiency histograms - for (int i = 0; i < nBins; i++) { - double ptAcenteri = 0.5 * (ptOverAbins[i] + ptOverAbins[i + 1]); - - registryCorr.fill(HIST("q1d_fullEvent_CoalescenceCorr"), nAntideuteronFullEvent[i], ptAcenteri, multiplicity); - registryCorr.fill(HIST("q1p_fullEvent_CoalescenceCorr"), nAntiprotonFullEvent[i], ptAcenteri, multiplicity); - - for (int j = 0; j < nBins; j++) { - double ptAcenterj = 0.5 * (ptOverAbins[j] + ptOverAbins[j + 1]); - - registryCorr.fill(HIST("q1d_square_fullEvent_CoalescenceCorr"), ptAcenteri, ptAcenterj, static_cast(nAntideuteronFullEvent[i] * nAntideuteronFullEvent[j]), multiplicity); - registryCorr.fill(HIST("q1p_square_fullEvent_CoalescenceCorr"), ptAcenteri, ptAcenterj, static_cast(nAntiprotonFullEvent[i] * nAntiprotonFullEvent[j]), multiplicity); - registryCorr.fill(HIST("q1d_q1p_fullEvent_CoalescenceCorr"), ptAcenteri, ptAcenterj, static_cast(nAntideuteronFullEvent[i] * nAntiprotonFullEvent[j]), multiplicity); - } - } - } - } - PROCESS_SWITCH(AntinucleiInJets, processCoalescenceCorr, "process coalescence correlation", false); - // Jet Pt resolution void processJetPtResolution(RecCollisionsMc const& collisions, AntiNucleiTracksMc const& mcTracks, aod::McParticles const& mcParticles) { diff --git a/PWGLF/Tasks/Nuspex/dedxPidAnalysis.cxx b/PWGLF/Tasks/Nuspex/dedxPidAnalysis.cxx index b4f80254804..4eda03292a3 100644 --- a/PWGLF/Tasks/Nuspex/dedxPidAnalysis.cxx +++ b/PWGLF/Tasks/Nuspex/dedxPidAnalysis.cxx @@ -76,13 +76,18 @@ struct DedxPidAnalysis { static constexpr int CentralityClasses = 10; std::array, CentralityClasses> hDedxVsMomentumVsCentPos{}; std::array, CentralityClasses> hDedxVsMomentumVsCentNeg{}; + std::array, CentralityClasses> hDedxVsMomentumVsCent{}; std::array, ParticlesType> hDedxvsMomentumPos{}; std::array, ParticlesType> hDedxvsMomentumNeg{}; - std::array, CentralityClasses + 1> hDedxVspTMomentumVsCent{}; + std::array, ParticlesType> hDedxvsMomentum{}; + std::array, CentralityClasses + 1> hDedxVspTVsCent{}; + std::array, CentralityClasses + 1> hDedxVspTVsCentCh{}; std::array, CentralityClasses + 1> hMomentumVsEtaPos{}; std::array, CentralityClasses + 1> hMomentumVsEtaNeg{}; + std::array, CentralityClasses + 1> hMomentumVsEta{}; std::array, CentralityClasses + 1> hpTVsEtaPos{}; std::array, CentralityClasses + 1> hpTVsEtaNeg{}; + std::array, CentralityClasses + 1> hpTVsEta{}; std::array, EtaIntervals> hNclFoundTPCPosBefore; std::array, EtaIntervals> hNclFoundTPCNegBefore; std::array, EtaIntervals> hNclFoundTPCPosAfter; @@ -254,6 +259,7 @@ struct DedxPidAnalysis { Configurable invMassCutLambda{"invMassCutLambda", 0.015f, "invariant Mass Cut for Lambda"}; Configurable invMassCutGamma{"invMassCutGamma", 0.015f, "invariant Mass Cut for Gamma"}; Configurable calibrationMode{"calibrationMode", false, "calibration mode"}; + Configurable separateCharges{"separateCharges", true, "true: fill Pos and Neg separately; false: fill both charges in one histogram"}; Configurable phiVarCut{"phiVarCut", true, "phi var cut"}; Configurable nClTPCFoundCut{"nClTPCFoundCut", false, "number of found clusters in TPC cut"}; Configurable nClTPCPIDCut{"nClTPCPIDCut", true, "number of PID clusters in TPC cut"}; @@ -385,6 +391,12 @@ struct DedxPidAnalysis { LOGF(info, "Running data applying calibration values"); } + if (separateCharges) { + LOGF(info, "Charge mode: Pos and Neg histograms filled separately"); + } else { + LOGF(info, "Charge mode: Pos and Neg combined in a single histogram"); + } + if (phiVarCut) { LOGF(info, "Applying phi prime cut"); } else { @@ -482,96 +494,161 @@ struct DedxPidAnalysis { "all", "Pi_v0", "Pr_v0", "El_v0"}; if (calibrationMode) { - // MIP for pions - registryDeDx.add( - "hdEdx_vs_eta_Neg_Pi", "dE/dx", HistType::kTH2F, - {{etaAxis}, {dedxAxis}}); - registryDeDx.add( - "hdEdx_vs_eta_Pos_Pi", "dE/dx", HistType::kTH2F, - {{etaAxis}, {dedxAxis}}); - // MIP for electrons - registryDeDx.add( - "hdEdx_vs_eta_vs_p_Neg_El", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); - registryDeDx.add( - "hdEdx_vs_eta_vs_p_Pos_El", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); - // Pions from TOF - registryDeDx.add( - "hdEdx_vs_eta_vs_p_Neg_TOF", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); - registryDeDx.add( - "hdEdx_vs_eta_vs_p_Pos_TOF", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); - - } else { - // MIP for pions - registryDeDx.add( - "hdEdx_vs_eta_Neg_calibrated_Pi", "dE/dx", HistType::kTH2F, - {{etaAxis}, {dedxAxis}}); - - registryDeDx.add( - "hdEdx_vs_eta_Pos_calibrated_Pi", "dE/dx", HistType::kTH2F, - {{etaAxis}, {dedxAxis}}); + if (separateCharges) { + // MIP for pions + registryDeDx.add( + "hdEdx_vs_eta_Neg_Pi", "dE/dx", HistType::kTH2F, + {{etaAxis}, {dedxAxis}}); + registryDeDx.add( + "hdEdx_vs_eta_Pos_Pi", "dE/dx", HistType::kTH2F, + {{etaAxis}, {dedxAxis}}); + + // MIP for electrons + registryDeDx.add( + "hdEdx_vs_eta_vs_p_Neg_El", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + registryDeDx.add( + "hdEdx_vs_eta_vs_p_Pos_El", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + + // Pions from TOF + registryDeDx.add( + "hdEdx_vs_eta_vs_p_Neg_TOF", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + registryDeDx.add( + "hdEdx_vs_eta_vs_p_Pos_TOF", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); - // MIP for electrons - registryDeDx.add( - "hdEdx_vs_eta_vs_p_Neg_calibrated_El", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); - - registryDeDx.add( - "hdEdx_vs_eta_vs_p_Pos_calibrated_El", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); - - // Pions from TOF - registryDeDx.add( - "hdEdx_vs_eta_vs_p_Neg_calibrated_TOF", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); + } else { + // MIP for pions + registryDeDx.add( + "hdEdx_vs_eta_Pi", "dE/dx", HistType::kTH2F, + {{etaAxis}, {dedxAxis}}); + // MIP for electrons + registryDeDx.add( + "hdEdx_vs_eta_vs_p_El", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + // Pions from TOF + registryDeDx.add( + "hdEdx_vs_eta_vs_p_TOF", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + } - registryDeDx.add( - "hdEdx_vs_eta_vs_p_Pos_calibrated_TOF", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); + } else { + if (separateCharges) { + + // MIP for pions + registryDeDx.add( + "hdEdx_vs_eta_Neg_calibrated_Pi", "dE/dx", HistType::kTH2F, + {{etaAxis}, {dedxAxis}}); + + registryDeDx.add( + "hdEdx_vs_eta_Pos_calibrated_Pi", "dE/dx", HistType::kTH2F, + {{etaAxis}, {dedxAxis}}); + + // MIP for electrons + registryDeDx.add( + "hdEdx_vs_eta_vs_p_Neg_calibrated_El", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + + registryDeDx.add( + "hdEdx_vs_eta_vs_p_Pos_calibrated_El", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + + // Pions from TOF + registryDeDx.add( + "hdEdx_vs_eta_vs_p_Neg_calibrated_TOF", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + + registryDeDx.add( + "hdEdx_vs_eta_vs_p_Pos_calibrated_TOF", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + + registryDeDx.add( + "hdEdx_vs_eta_vs_p_Neg_calibrated_TOF2", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + + registryDeDx.add( + "hdEdx_vs_eta_vs_p_Pos_calibrated_TOF2", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + + // pt vs p + registryDeDx.add( + "heta_vs_pt_vs_p_all_Neg", "eta_vs_pT_vs_p", HistType::kTH3F, + {{etaAxis}, {ptAxis}, {pAxisTrack}}); + registryDeDx.add( + "heta_vs_pt_vs_p_all_Pos", "eta_vs_pT_vs_p", HistType::kTH3F, + {{etaAxis}, {ptAxis}, {pAxisTrack}}); + + // De/Dx for ch and v0 particles + for (int i = 0; i < ParticlesType; ++i) { + const auto& part = v0Names[i]; + hDedxvsMomentumPos[i] = registryDeDx.add(Form("dEdx_vs_Momentum_%s_Pos", part.c_str()), + "dE/dx vs Momentum Positive", kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); + hDedxvsMomentumNeg[i] = registryDeDx.add(Form("dEdx_vs_Momentum_%s_Neg", part.c_str()), + "dE/dx vs Momentum Negative", kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); + } - registryDeDx.add( - "hdEdx_vs_eta_vs_p_Neg_calibrated_TOF2", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); + for (int i = 0; i < CentralityClasses; ++i) { + const auto& cent = centNames[i]; + hDedxVsMomentumVsCentPos[i] = registryDeDx.add(Form("dEdx_vs_Momentum_%s_Pos", cent.c_str()), "dE/dx vs Momentum Positive", HistType::kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); + hDedxVsMomentumVsCentNeg[i] = registryDeDx.add(Form("dEdx_vs_Momentum_%s_Neg", cent.c_str()), "dE/dx vs Momentum Negative", HistType::kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); + } - registryDeDx.add( - "hdEdx_vs_eta_vs_p_Pos_calibrated_TOF2", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); + for (int i = 0; i < CentralityClasses + 1; ++i) { + const auto& cent = centNames[i]; + hDedxVspTVsCent[i] = registryDeDx.add(Form("dEdx_vs_pT_%s", cent.c_str()), "dE/dx vs pT", HistType::kTH3F, {{ptAxis}, {dedxAxis}, {etaAxis}}); + hMomentumVsEtaPos[i] = registryDeDx.add(Form("p_vs_eta_%s_Pos", cent.c_str()), "p vs eta", HistType::kTH2F, {{etaAxis}, {pFineAxis}}); + hMomentumVsEtaNeg[i] = registryDeDx.add(Form("p_vs_eta_%s_Neg", cent.c_str()), "p vs eta", HistType::kTH2F, {{etaAxis}, {pFineAxis}}); + hpTVsEtaPos[i] = registryDeDx.add(Form("pT_vs_eta_%s_Pos", cent.c_str()), "pT vs eta", HistType::kTH2F, {{etaAxis}, {pTFineAxis}}); + hpTVsEtaNeg[i] = registryDeDx.add(Form("pT_vs_eta_%s_Neg", cent.c_str()), "pT vs eta", HistType::kTH2F, {{etaAxis}, {pTFineAxis}}); + } - // pt vs p - registryDeDx.add( - "heta_vs_pt_vs_p_all_Neg", "eta_vs_pT_vs_p", HistType::kTH3F, - {{etaAxis}, {ptAxis}, {pAxisTrack}}); - registryDeDx.add( - "heta_vs_pt_vs_p_all_Pos", "eta_vs_pT_vs_p", HistType::kTH3F, - {{etaAxis}, {ptAxis}, {pAxisTrack}}); - - // De/Dx for ch and v0 particles - for (int i = 0; i < ParticlesType; ++i) { - const auto& part = v0Names[i]; - hDedxvsMomentumPos[i] = registryDeDx.add(Form("dEdx_vs_Momentum_%s_Pos", part.c_str()), - "dE/dx vs Momentum Positive", kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); - hDedxvsMomentumNeg[i] = registryDeDx.add(Form("dEdx_vs_Momentum_%s_Neg", part.c_str()), - "dE/dx vs Momentum Negative", kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); - } + } else { + // MIP for pions + registryDeDx.add( + "hdEdx_vs_eta_calibrated_Pi", "dE/dx", HistType::kTH2F, + {{etaAxis}, {dedxAxis}}); + + // MIP for electrons + registryDeDx.add( + "hdEdx_vs_eta_vs_p_calibrated_El", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + + // Pions from TOF + registryDeDx.add( + "hdEdx_vs_eta_vs_p_calibrated_TOF", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + + registryDeDx.add( + "hdEdx_vs_eta_vs_p_calibrated_TOF2", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + + // pt vs p + + registryDeDx.add( + "heta_vs_pt_vs_p_all", "eta_vs_pT_vs_p", HistType::kTH3F, + {{etaAxis}, {ptAxis}, {pAxisTrack}}); + + // De/Dx for ch and v0 particles + for (int i = 0; i < ParticlesType; ++i) { + const auto& part = v0Names[i]; + hDedxvsMomentum[i] = registryDeDx.add(Form("dEdx_vs_Momentum_%s", part.c_str()), + "dE/dx vs Momentum", kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); + } - for (int i = 0; i < CentralityClasses; ++i) { - const auto& cent = centNames[i]; - hDedxVsMomentumVsCentPos[i] = registryDeDx.add(Form("dEdx_vs_Momentum_%s_Pos", cent.c_str()), "dE/dx vs Momentum Positive", HistType::kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); - hDedxVsMomentumVsCentNeg[i] = registryDeDx.add(Form("dEdx_vs_Momentum_%s_Neg", cent.c_str()), "dE/dx vs Momentum Negative", HistType::kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); - } + for (int i = 0; i < CentralityClasses; ++i) { + const auto& cent = centNames[i]; + hDedxVsMomentumVsCent[i] = registryDeDx.add(Form("dEdx_vs_Momentum_%s", cent.c_str()), "dE/dx vs Momentum", HistType::kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); + } - for (int i = 0; i < CentralityClasses + 1; ++i) { - const auto& cent = centNames[i]; - hDedxVspTMomentumVsCent[i] = registryDeDx.add(Form("dEdx_vs_pT_%s", cent.c_str()), "dE/dx vs pT", HistType::kTH3F, {{ptAxis}, {dedxAxis}, {etaAxis}}); - hMomentumVsEtaPos[i] = registryDeDx.add(Form("p_vs_eta_%s_Pos", cent.c_str()), "p vs eta", HistType::kTH2F, {{etaAxis}, {pFineAxis}}); - hMomentumVsEtaNeg[i] = registryDeDx.add(Form("p_vs_eta_%s_Neg", cent.c_str()), "p vs eta", HistType::kTH2F, {{etaAxis}, {pFineAxis}}); - hpTVsEtaPos[i] = registryDeDx.add(Form("pT_vs_eta_%s_Pos", cent.c_str()), "pT vs eta", HistType::kTH2F, {{etaAxis}, {pTFineAxis}}); - hpTVsEtaNeg[i] = registryDeDx.add(Form("pT_vs_eta_%s_Neg", cent.c_str()), "pT vs eta", HistType::kTH2F, {{etaAxis}, {pTFineAxis}}); + for (int i = 0; i < CentralityClasses + 1; ++i) { + const auto& cent = centNames[i]; + hDedxVspTVsCentCh[i] = registryDeDx.add(Form("dEdx_vs_pT_%s", cent.c_str()), "dE/dx vs pT", HistType::kTH3F, {{ptAxis}, {dedxAxis}, {etaAxis}}); + hMomentumVsEta[i] = registryDeDx.add(Form("p_vs_eta_%s", cent.c_str()), "p vs eta", HistType::kTH2F, {{etaAxis}, {pFineAxis}}); + hpTVsEta[i] = registryDeDx.add(Form("pT_vs_eta_%s", cent.c_str()), "pT vs eta", HistType::kTH2F, {{etaAxis}, {pTFineAxis}}); + } } - // Invariant Mass registryDeDx.add("hMassK0s", "Invariant mass K0s;m_{#pi#pi} (GeV/c^{2});Counts", HistType::kTH1F, {{200, 0.4, 0.6}}); @@ -765,13 +842,20 @@ struct DedxPidAnalysis { {{pAxisTrack}, {100, 0, 160, "N_{cl, PID}"}}); // beta plot - registryDeDx.add( - "hbeta_vs_p_Neg", "beta", HistType::kTH2F, - {{pAxis}, {100, 0.0, 1.1, "#beta"}}); + if (separateCharges) { + registryDeDx.add( + "hbeta_vs_p_Neg", "beta", HistType::kTH2F, + {{pAxis}, {100, 0.0, 1.1, "#beta"}}); - registryDeDx.add( - "hbeta_vs_p_Pos", "beta", HistType::kTH2F, - {{pAxis}, {100, 0.0, 1.1, "#beta"}}); + registryDeDx.add( + "hbeta_vs_p_Pos", "beta", HistType::kTH2F, + {{pAxis}, {100, 0.0, 1.1, "#beta"}}); + } else { + + registryDeDx.add( + "hbeta_vs_p", "beta", HistType::kTH2F, + {{pAxis}, {100, 0.0, 1.1, "#beta"}}); + } // Event Counter registryDeDx.add("histRecVtxZData", "collision z position", HistType::kTH1F, {{100, -20.0, +20.0, "z_{vtx} (cm)"}}); @@ -1234,8 +1318,6 @@ struct DedxPidAnalysis { return false; } - const float gammaMass = 2 * MassElectron; // GeV/c^2 - if (fillHist) registryDeDx.fill(HIST("trackselSec"), TrkSecCutLabel::SingleTrackSelectionGamma); @@ -1250,7 +1332,7 @@ struct DedxPidAnalysis { if (fillHist) registryDeDx.fill(HIST("trackselSec"), TrkSecCutLabel::V0RapidityGamma); - if (std::abs(v0.mGamma() - gammaMass) > invMassCutGamma) // Invarian mass + if (std::abs(v0.mGamma() - MassGamma) > invMassCutGamma) // Invarian mass return false; if (fillHist) registryDeDx.fill(HIST("trackselSec"), TrkSecCutLabel::MassCutGamma); @@ -1588,51 +1670,80 @@ struct DedxPidAnalysis { registryDeDx.fill(HIST("hTPCClustersAfter"), trk.tpcNClsFound()); registryDeDx.fill(HIST("hTPCPIDAfter"), trk.tpcNClsPID()); - float signedP = trk.sign() * getMomentum(trk); - float signedpT = trk.sign() * trk.pt(); + float p = getMomentum(trk); + float pT = trk.pt(); // MIP calibration for pions if (getMomentum(trk) >= pionMin && getMomentum(trk) <= pionMax) { if (calibrationMode) { - if (signedP < 0) { - registryDeDx.fill(HIST("hdEdx_vs_eta_Neg_Pi"), trk.eta(), trk.tpcSignal()); + if (separateCharges) { + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_Neg_Pi"), trk.eta(), trk.tpcSignal()); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_Pos_Pi"), trk.eta(), trk.tpcSignal()); + } } else { - registryDeDx.fill(HIST("hdEdx_vs_eta_Pos_Pi"), trk.eta(), trk.tpcSignal()); + registryDeDx.fill(HIST("hdEdx_vs_eta_Pi"), trk.eta(), trk.tpcSignal()); } } else { for (int i = 0; i < EtaIntervals; ++i) { if (trk.eta() >= EtaCut[i] && trk.eta() < EtaCut[i + 1]) { - if (signedP < 0) { - registryDeDx.fill(HIST("hdEdx_vs_eta_Neg_calibrated_Pi"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i)); + if (separateCharges) { + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_Neg_calibrated_Pi"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i)); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_Pos_calibrated_Pi"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i)); + } } else { - registryDeDx.fill(HIST("hdEdx_vs_eta_Pos_calibrated_Pi"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i)); + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_calibrated_Pi"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i)); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_calibrated_Pi"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i)); + } } } } } } // Beta from TOF - if (signedP < 0) { - registryDeDx.fill(HIST("hbeta_vs_p_Neg"), std::abs(signedP), trk.beta()); + if (separateCharges) { + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hbeta_vs_p_Neg"), p, trk.beta()); + } else { + registryDeDx.fill(HIST("hbeta_vs_p_Pos"), p, trk.beta()); + } } else { - registryDeDx.fill(HIST("hbeta_vs_p_Pos"), signedP, trk.beta()); + registryDeDx.fill(HIST("hbeta_vs_p"), p, trk.beta()); } + // Electrons from TOF if (std::abs(trk.beta() - 1) < elTofCut) { // beta cut if (calibrationMode) { - if (signedP < 0) { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_El"), trk.eta(), trk.tpcSignal(), std::abs(signedP)); + if (separateCharges) { + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_El"), trk.eta(), trk.tpcSignal(), p); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_El"), trk.eta(), trk.tpcSignal(), p); + } } else { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_El"), trk.eta(), trk.tpcSignal(), signedP); + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_El"), trk.eta(), trk.tpcSignal(), p); } } else { for (int i = 0; i < EtaIntervals; ++i) { if (trk.eta() >= EtaCut[i] && trk.eta() < EtaCut[i + 1]) { - if (signedP < 0) { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_calibrated_El"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), std::abs(signedP)); + if (separateCharges) { + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_calibrated_El"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), p); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_calibrated_El"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i), p); + } } else { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_calibrated_El"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i), signedP); + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_calibrated_El"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), p); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_calibrated_El"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i), p); + } } } } @@ -1641,18 +1752,30 @@ struct DedxPidAnalysis { // pions from TOF if (trk.beta() > pionTofCut && trk.beta() < pionTofCut + 0.05) { // beta cut if (calibrationMode) { - if (signedP < 0) { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_TOF"), trk.eta(), trk.tpcSignal(), std::abs(signedP)); + if (separateCharges) { + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_TOF"), trk.eta(), trk.tpcSignal(), p); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_TOF"), trk.eta(), trk.tpcSignal(), p); + } } else { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_TOF"), trk.eta(), trk.tpcSignal(), signedP); + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_TOF"), trk.eta(), trk.tpcSignal(), p); } } else { for (int i = 0; i < EtaIntervals; ++i) { if (trk.eta() >= EtaCut[i] && trk.eta() < EtaCut[i + 1]) { - if (signedP < 0) { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_calibrated_TOF"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), std::abs(signedP)); + if (separateCharges) { + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_calibrated_TOF"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), p); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_calibrated_TOF"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i), p); + } } else { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_calibrated_TOF"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i), signedP); + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_calibrated_TOF"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), p); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_calibrated_TOF"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i), p); + } } } } @@ -1666,10 +1789,18 @@ struct DedxPidAnalysis { if (std::abs(sigmaPi) < nsigmaMaxTOF) { for (int i = 0; i < EtaIntervals; ++i) { if (trk.eta() >= EtaCut[i] && trk.eta() < EtaCut[i + 1]) { - if (signedP < 0) { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_calibrated_TOF2"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), std::abs(signedP)); + if (separateCharges) { + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_calibrated_TOF2"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), p); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_calibrated_TOF2"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i), p); + } } else { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_calibrated_TOF2"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i), signedP); + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_calibrated_TOF2"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), p); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_calibrated_TOF2"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i), p); + } } } } @@ -1692,26 +1823,45 @@ struct DedxPidAnalysis { for (int i = 0; i < EtaIntervals; ++i) { if (trk.eta() >= EtaCut[i] && trk.eta() < EtaCut[i + 1]) { - if (signedP > 0) { - hDedxvsMomentumPos[0]->Fill(signedP, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); - registryDeDx.fill(HIST("heta_vs_pt_vs_p_all_Pos"), trk.eta(), trk.pt(), trk.p()); - hDedxVsMomentumVsCentPos[centIndex]->Fill(signedP, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); - hDedxVspTMomentumVsCent[centIndex]->Fill(signedpT, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); - hDedxVspTMomentumVsCent[10]->Fill(signedpT, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); - hMomentumVsEtaPos[centIndex]->Fill(trk.eta(), signedP); - hMomentumVsEtaPos[10]->Fill(trk.eta(), signedP); - hpTVsEtaPos[centIndex]->Fill(trk.eta(), signedpT); - hpTVsEtaPos[10]->Fill(trk.eta(), signedpT); + if (separateCharges) { + if (trk.sign() > 0) { + hDedxvsMomentumPos[0]->Fill(p, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); + registryDeDx.fill(HIST("heta_vs_pt_vs_p_all_Pos"), trk.eta(), trk.pt(), trk.p()); + hDedxVsMomentumVsCentPos[centIndex]->Fill(p, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); + hDedxVspTVsCent[centIndex]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); + hDedxVspTVsCent[10]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); + hMomentumVsEtaPos[centIndex]->Fill(trk.eta(), p); + hMomentumVsEtaPos[10]->Fill(trk.eta(), p); + hpTVsEtaPos[centIndex]->Fill(trk.eta(), pT); + hpTVsEtaPos[10]->Fill(trk.eta(), pT); + } else { + hDedxvsMomentumNeg[0]->Fill(p, trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); + registryDeDx.fill(HIST("heta_vs_pt_vs_p_all_Neg"), trk.eta(), trk.pt(), trk.p()); + hDedxVsMomentumVsCentNeg[centIndex]->Fill(p, trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); + hDedxVspTVsCent[centIndex]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); + hDedxVspTVsCent[10]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); + hMomentumVsEtaNeg[centIndex]->Fill(trk.eta(), p); + hMomentumVsEtaNeg[10]->Fill(trk.eta(), p); + hpTVsEtaNeg[centIndex]->Fill(trk.eta(), pT); + hpTVsEtaNeg[10]->Fill(trk.eta(), pT); + } } else { - hDedxvsMomentumNeg[0]->Fill(std::abs(signedP), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); - registryDeDx.fill(HIST("heta_vs_pt_vs_p_all_Neg"), trk.eta(), trk.pt(), trk.p()); - hDedxVsMomentumVsCentNeg[centIndex]->Fill(std::abs(signedP), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); - hDedxVspTMomentumVsCent[centIndex]->Fill(std::abs(signedpT), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); - hDedxVspTMomentumVsCent[10]->Fill(std::abs(signedpT), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); - hMomentumVsEtaNeg[centIndex]->Fill(trk.eta(), std::abs(signedP)); - hMomentumVsEtaNeg[10]->Fill(trk.eta(), std::abs(signedP)); - hpTVsEtaNeg[centIndex]->Fill(trk.eta(), std::abs(signedpT)); - hpTVsEtaNeg[10]->Fill(trk.eta(), std::abs(signedpT)); + registryDeDx.fill(HIST("heta_vs_pt_vs_p_all"), trk.eta(), trk.pt(), trk.p()); + hMomentumVsEta[centIndex]->Fill(trk.eta(), p); + hMomentumVsEta[10]->Fill(trk.eta(), p); + hpTVsEta[centIndex]->Fill(trk.eta(), pT); + hpTVsEta[10]->Fill(trk.eta(), pT); + if (trk.sign() > 0) { + hDedxvsMomentum[0]->Fill(p, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); + hDedxVsMomentumVsCent[centIndex]->Fill(p, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); + hDedxVspTVsCentCh[centIndex]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); + hDedxVspTVsCentCh[10]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); + } else { + hDedxvsMomentum[0]->Fill(p, trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); + hDedxVsMomentumVsCent[centIndex]->Fill(p, trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); + hDedxVspTVsCentCh[centIndex]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); + hDedxVspTVsCentCh[10]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); + } } } } @@ -1747,7 +1897,7 @@ struct DedxPidAnalysis { if (!passedPhiCutSecondaries(negTrack, magField)) continue; - registryDeDx.fill(HIST("trackselSec"), TrkSecCutLabel ::PhiVarCutSec); + registryDeDx.fill(HIST("trackselSec"), TrkSecCutLabel::PhiVarCutSec); } fillHist = false; @@ -1787,7 +1937,7 @@ struct DedxPidAnalysis { if (!passedNClTPCFoundCutSecondaries(negTrack)) continue; - registryDeDx.fill(HIST("trackselSec"), TrkSecCutLabel ::NClTPCFoundCutSec); + registryDeDx.fill(HIST("trackselSec"), TrkSecCutLabel::NClTPCFoundCutSec); } if (nClTPCPIDCut) { @@ -1797,7 +1947,7 @@ struct DedxPidAnalysis { if (!passedNClTPCPIDCutSecondaries(negTrack)) continue; - registryDeDx.fill(HIST("trackselSec"), TrkSecCutLabel ::NClTPCPIDCutSec); + registryDeDx.fill(HIST("trackselSec"), TrkSecCutLabel::NClTPCPIDCutSec); } float signedPpos = posTrack.sign() * getMomentum(posTrack); @@ -1818,10 +1968,18 @@ struct DedxPidAnalysis { registryDeDx.fill(HIST("hArmenterosK0s"), v0.alpha(), v0.qtarm()); for (int i = 0; i < EtaIntervals; ++i) { if (negTrack.eta() > EtaCut[i] && negTrack.eta() < EtaCut[i + 1]) { - hDedxvsMomentumNeg[1]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + if (separateCharges) { + hDedxvsMomentumNeg[1]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + } else { + hDedxvsMomentum[1]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + } } if (posTrack.eta() > EtaCut[i] && posTrack.eta() < EtaCut[i + 1]) { - hDedxvsMomentumPos[1]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + if (separateCharges) { + hDedxvsMomentumPos[1]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + } else { + hDedxvsMomentum[1]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + } } } } @@ -1836,10 +1994,18 @@ struct DedxPidAnalysis { registryDeDx.fill(HIST("hArmenterosLambda"), v0.alpha(), v0.qtarm()); for (int i = 0; i < EtaIntervals; ++i) { if (negTrack.eta() > EtaCut[i] && negTrack.eta() < EtaCut[i + 1]) { - hDedxvsMomentumNeg[1]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + if (separateCharges) { + hDedxvsMomentumNeg[1]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + } else { + hDedxvsMomentum[1]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + } } if (posTrack.eta() > EtaCut[i] && posTrack.eta() < EtaCut[i + 1]) { - hDedxvsMomentumPos[2]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + if (separateCharges) { + hDedxvsMomentumPos[2]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + } else { + hDedxvsMomentum[2]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + } } } } @@ -1854,10 +2020,18 @@ struct DedxPidAnalysis { registryDeDx.fill(HIST("hArmenterosAntiLambda"), v0.alpha(), v0.qtarm()); for (int i = 0; i < EtaIntervals; ++i) { if (negTrack.eta() > EtaCut[i] && negTrack.eta() < EtaCut[i + 1]) { - hDedxvsMomentumNeg[2]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + if (separateCharges) { + hDedxvsMomentumNeg[2]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + } else { + hDedxvsMomentum[2]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + } } if (posTrack.eta() > EtaCut[i] && posTrack.eta() < EtaCut[i + 1]) { - hDedxvsMomentumPos[1]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + if (separateCharges) { + hDedxvsMomentumPos[1]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + } else { + hDedxvsMomentum[1]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + } } } } @@ -1872,10 +2046,18 @@ struct DedxPidAnalysis { registryDeDx.fill(HIST("hArmenterosGamma"), v0.alpha(), v0.qtarm()); for (int i = 0; i < EtaIntervals; ++i) { if (negTrack.eta() > EtaCut[i] && negTrack.eta() < EtaCut[i + 1]) { - hDedxvsMomentumNeg[3]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + if (separateCharges) { + hDedxvsMomentumNeg[3]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + } else { + hDedxvsMomentum[3]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + } } if (posTrack.eta() > EtaCut[i] && posTrack.eta() < EtaCut[i + 1]) { - hDedxvsMomentumPos[3]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + if (separateCharges) { + hDedxvsMomentumPos[3]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + } else { + hDedxvsMomentum[3]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + } } } } diff --git a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx index 50afd592225..71d3dcd51ce 100644 --- a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx +++ b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx @@ -46,6 +46,7 @@ #include #include +#include #include #include #include @@ -91,11 +92,20 @@ struct HadronNucleiCorrelation { Configurable doQA{"doQA", true, "save QA histograms"}; Configurable doMCQA{"doMCQA", false, "save MC QA histograms"}; Configurable isMC{"isMC", false, "is MC"}; - Configurable isMCGen{"isMCGen", false, "is isMCGen"}; Configurable isPrim{"isPrim", true, "is isPrim"}; Configurable doCorrection{"doCorrection", false, "do efficiency correction"}; Configurable doQuadraticPID{"doQuadraticPID", false, "do PID with sum in quadrature of TOF and TPC"}; + struct : ConfigurableGroup { + std::string prefix = "Coalescence"; // JSON group name + Configurable doMCGenCoalescence{"doMCGenCoalescence", false, "do a simple coalescence on the generated level"}; + // Coalescence parameters used in Eur. Phys. J. A 59 (2023) 72: + // p_max = 0.148 GeV/c + // r_max = 2 fm + Configurable pMax{"pMax", 0.148, "maximum momentum for coalescence"}; + Configurable rMax{"rMax", 2.0, "maximum radius for coalescence"}; + } settingsCoalescence; + Configurable fCorrectionPath{"fCorrectionPath", "", "Correction path to file"}; Configurable fCorrectionHisto{"fCorrectionHisto", "", "Correction histogram"}; Configurable cfgUrl{"cfgUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; @@ -105,7 +115,7 @@ struct HadronNucleiCorrelation { Configurable removeSameBunchPileup{"removeSameBunchPileup", false, "remove Same Bunch Pileup"}; // Track selection - Configurable doClosePairRejection{"doClosePairRejection", false, "doClosePairRejection"}; + Configurable doClosePairRejection{"doClosePairRejection", false, "doClosePairRejection for non identical particles"}; Configurable dcaPar0{"dcaPar0", 0.004, "par 0"}; Configurable dcaPar1{"dcaPar1", 0.013, "par 1"}; Configurable doDCAZ{"doDCAZ", true, "do DCA z cut"}; @@ -120,12 +130,14 @@ struct HadronNucleiCorrelation { Configurable nsigmaElPr{"nsigmaElPr", 1.0f, "cut nsigma TPC El for protons"}; Configurable nsigmaElDe{"nsigmaElDe", 3.0f, "cut nsigma TPC El for protons"}; Configurable nsigmaTOF{"nsigmaTOF", 3.5f, "cut nsigma TOF"}; + Configurable nsigmaTPCPrRej{"nsigmaTPCPrRej", 3.f, "cut nsigma TPC for proton rejection"}; + Configurable nsigmaTOFPrRej{"nsigmaTOFPrRej", 3.f, "cut nsigma TOF for proton rejection"}; + Configurable doPrRej{"doPrRej", true, "do TPC proton rejection"}; Configurable nsigmaQuadratic{"nsigmaQuadratic", 3.0f, "cut on sqrt(nsigmaTPC^2 + nsigmaTOF^2), used above the TOF pT threshold when doQuadraticPID is on"}; Configurable nsigmaITSPr{"nsigmaITSPr", -2.0f, "cut nsigma ITS Pr"}; Configurable nsigmaITSDe{"nsigmaITSDe", -2.0f, "cut nsigma ITS De"}; Configurable doITSPID{"doITSPID", true, "do ITS PID"}; Configurable pTthrprTOF{"pTthrprTOF", 0.8f, "threshold pT proton to use TOF"}; - Configurable pTthrprTPCEl{"pTthrprTPCEl", 1.0f, "threshold pT proton to use TPC El rejection"}; Configurable pTthrdeTOF{"pTthrdeTOF", 1.0f, "threshold pT deuteron to use TOF"}; Configurable pTthrdeTPCEl{"pTthrdeTPCEl", 1.0f, "threshold pT deuteron to use TPC El rejection"}; Configurable rejectionEl{"rejectionEl", true, "use TPC El rejection"}; @@ -140,7 +152,7 @@ struct HadronNucleiCorrelation { // Mixing parameters ConfigurableAxis confMultBins{"confMultBins", {VARIABLE_WIDTH, 0.0f, 4.0f, 8.0f, 12.0f, 16.0f, 20.0f, 24.0f, 28.0f, 50.0f, 100.0f, 99999.f}, "Mixing bins - multiplicity"}; ConfigurableAxis confVtxBins{"confVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; // pT/A bins Configurable> pTBins{"pTBins", {0.6f, 1.0f, 1.2f, 2.f}, "p_{T} bins"}; @@ -397,6 +409,8 @@ struct HadronNucleiCorrelation { registryQa.add("QA/h3dTPCTOF_AntiPr", "n#sigma TPC vs n#sigma TOF", {HistType::kTH3D, {tpcNSigmaAxis, tofNSigmaAxis, ptAxis}}); registryQa.add("QA/h3dTPCTOF_De", "n#sigma TPC vs n#sigma TOF", {HistType::kTH3D, {tpcNSigmaAxis, tofNSigmaAxis, ptAxis}}); registryQa.add("QA/h3dTPCTOF_AntiDe", "n#sigma TPC vs n#sigma TOF", {HistType::kTH3D, {tpcNSigmaAxis, tofNSigmaAxis, ptAxis}}); + registryQa.add("QA/h3dTPCTOF_De_Rej", "n#sigma TPC vs n#sigma TOF", {HistType::kTH3D, {tpcNSigmaAxis, tofNSigmaAxis, ptAxis}}); + registryQa.add("QA/h3dTPCTOF_AntiDe_Rej", "n#sigma TPC vs n#sigma TOF", {HistType::kTH3D, {tpcNSigmaAxis, tofNSigmaAxis, ptAxis}}); registryQa.add("QA/hnSigmaTPCVsPt_El", "n#sigma TPC vs p_{T} for e hypothesis (all tracks)", {HistType::kTH2D, {ptAxis, tpcNSigmaAxis}}); registryQa.add("QA/hnSigmaTPCVsPt_Pr", "n#sigma TPC vs p_{T} for p hypothesis (all tracks)", {HistType::kTH2D, {ptAxis, tpcNSigmaAxis}}); registryQa.add("QA/hnSigmaTPCVsPt_De", "n#sigma TPC vs p_{T} for d hypothesis (all tracks)", {HistType::kTH2D, {ptAxis, tpcNSigmaAxis}}); @@ -516,11 +530,11 @@ struct HadronNucleiCorrelation { registry.add("hReco_Pt_Proton_TPCEl", "Reco (anti)protons in reco collisions", {HistType::kTH1F, {ptAxisSmall}}); registry.add("hReco_Pt_Proton_TPCEl_or_TOF", "Reco (anti)protons in reco collisions", {HistType::kTH1F, {ptAxisSmall}}); registry.add("hReco_Pt_Deuteron_TPCEl", "Reco (anti)deuterons in reco collisions", {HistType::kTH1F, {ptAxisSmall}}); - registry.add("hReco_Pt_Deuteron_TPCEl_or_TOF", "Reco (anti)protons in reco collisions", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hReco_Pt_Deuteron_TPCEl_or_TOF", "Reco (anti)deuterons in reco collisions", {HistType::kTH1F, {ptAxisSmall}}); } } - if (isMCGen) { + if (doprocessMixedEventGen || doprocessSameEventGen) { registry.add("Generated/hNEventsMC", "hNEventsMC", {HistType::kTH1D, {{1, 0.f, 1.f}}}); registry.get(HIST("Generated/hNEventsMC"))->GetXaxis()->SetBinLabel(1, "All"); @@ -549,6 +563,8 @@ struct HadronNucleiCorrelation { registry.add("Generated/hAntiDeuteronsVsPt", "hAntiDeuteronsVsPt", {HistType::kTH1D, {ptAxisGen}}); registry.add("Generated/hProtonsVsPt", "hProtonsVsPt", {HistType::kTH1D, {ptAxisGen}}); registry.add("Generated/hAntiProtonsVsPt", "hAntiProtonsVsPt", {HistType::kTH1D, {ptAxisGen}}); + } else if (settingsCoalescence.doMCGenCoalescence.value) { + LOG(fatal) << "This is not a Gen Run but the Gen coalescence is required. Turn off doMCGenCoalescence"; } } @@ -569,7 +585,6 @@ struct HadronNucleiCorrelation { { const bool isTPCPID = std::abs(track.tpcNSigmaPr()) < nsigmaTPC.value; const bool isTOFPID = std::abs(track.tofNSigmaPr()) < nsigmaTOF.value; - const bool isTPCElRejection = rejectionEl.value && track.beta() < BetahasTOFthr && track.pt() < pTthrprTPCEl.value && track.tpcNSigmaEl() >= nsigmaElPr.value; const bool isITSPID = track.itsNSigmaPr() > nsigmaITSPr.value; const bool isQuadraticPID = std::hypot(track.tpcNSigmaPr(), track.tofNSigmaPr()) < nsigmaQuadratic.value; @@ -582,7 +597,7 @@ struct HadronNucleiCorrelation { if (!doITSPID.value || isITSPID) { return signCheck; } - } else if (isTPCElRejection || isTOFPID) { + } else if (isTOFPID) { return signCheck; } } @@ -607,18 +622,22 @@ struct HadronNucleiCorrelation { const bool isTOFPID = std::abs(track.tofNSigmaDe()) < nsigmaTOF.value; const bool isTPCElRejection = rejectionEl.value && track.beta() < BetahasTOFthr && track.pt() < pTthrdeTPCEl.value && track.tpcNSigmaEl() >= nsigmaElDe.value; const bool isITSPID = track.itsNSigmaDe() > nsigmaITSDe.value; + const bool isPr = (std::abs(track.tpcNSigmaPr()) < nsigmaTPCPrRej.value && std::abs(track.tofNSigmaPr()) < nsigmaTOFPrRej.value) && doPrRej.value; const bool isQuadraticPID = std::hypot(track.tpcNSigmaDe(), track.tofNSigmaDe()) < nsigmaQuadratic.value; // Check if the sign of the track matches the expected sign for deuterons or antideuterons const bool signCheck = (sign > 0 && track.sign() > 0) || (sign < 0 && track.sign() < 0); + if (isPr) { + return false; + } if (!doQuadraticPID.value) { if (isTPCPID) { if (track.pt() < pTthrdeTOF.value) { if (!doITSPID.value || isITSPID) { return signCheck; } - } else if (isTPCElRejection || isTOFPID) { + } else if (isTOFPID || (isTPCElRejection && (!doITSPID.value || isITSPID))) { return signCheck; } } @@ -655,14 +674,13 @@ struct HadronNucleiCorrelation { pair->SetPair(&part0, &part1); pair->SetIdentical(isIdentical); if (isIdentical && pair->IsClosePair(dEta, dPhi, radiusTPC)) { - registryQa.fill(HIST("QA/hdEtadPhistar"), pair->GetPhiStarDiff(radiusTPC), pair->GetEtaDiff()); return; } if (doClosePairRejection && pair->IsClosePair(dEta, dPhi, radiusTPC)) { - registryQa.fill(HIST("QA/hdEtadPhistar"), pair->GetPhiStarDiff(radiusTPC), pair->GetEtaDiff()); return; } + registryQa.fill(HIST("QA/hdEtadPhistar"), pair->GetPhiStarDiff(radiusTPC), pair->GetEtaDiff()); const float deltaEta = part0.eta() - part1.eta(); const float deltaPhi = RecoDecay::constrainAngle(part0.phi() - part1.phi(), -1 * o2::constants::math::PIHalf); @@ -1009,13 +1027,37 @@ struct HadronNucleiCorrelation { registryQa.fill(HIST("QA/hnSigmaTOFVsPt_De"), track.pt() * track.sign(), track.tofNSigmaDe()); registryQa.fill(HIST("QA/hnSigmaITSVsPt_Pr"), track.pt() * track.sign(), track.itsNSigmaPr()); registryQa.fill(HIST("QA/hnSigmaITSVsPt_De"), track.pt() * track.sign(), track.itsNSigmaDe()); + + const bool isTPCElRejection = rejectionEl.value && track.beta() < BetahasTOFthr && track.pt() < pTthrdeTPCEl.value && track.tpcNSigmaEl() >= nsigmaElDe.value; + const bool isTOFPID = std::abs(track.tofNSigmaDe()) < nsigmaTOF.value; + const bool isPr = (std::abs(track.tpcNSigmaPr()) < nsigmaTPCPrRej.value && std::abs(track.tofNSigmaPr()) < nsigmaTOFPrRej.value) && doPrRej.value; + if (track.sign() > 0) { - registryQa.fill(HIST("QA/h3dTPCTOF_Pr"), track.tpcNSigmaPr(), track.tofNSigmaPr(), track.pt()); - registryQa.fill(HIST("QA/h3dTPCTOF_De"), track.tpcNSigmaDe(), track.tofNSigmaDe(), track.pt()); + + if (track.pt() >= pTthrprTOF.value || !doITSPID.value || track.itsNSigmaPr() > nsigmaITSPr.value) { + registryQa.fill(HIST("QA/h3dTPCTOF_Pr"), track.tpcNSigmaPr(), track.tofNSigmaPr(), track.pt()); + } + if (track.pt() >= pTthrdeTOF.value || !doITSPID.value || track.itsNSigmaDe() > nsigmaITSDe.value) { + if (!isPr) { + registryQa.fill(HIST("QA/h3dTPCTOF_De"), track.tpcNSigmaDe(), track.tofNSigmaDe(), track.pt()); + if (isTOFPID || (isTPCElRejection && (!doITSPID.value || track.itsNSigmaDe() > nsigmaITSDe.value))) { + registryQa.fill(HIST("QA/h3dTPCTOF_De_Rej"), track.tpcNSigmaDe(), track.tofNSigmaDe(), track.pt()); + } + } + } } if (track.sign() < 0) { - registryQa.fill(HIST("QA/h3dTPCTOF_AntiPr"), track.tpcNSigmaPr(), track.tofNSigmaPr(), track.pt()); - registryQa.fill(HIST("QA/h3dTPCTOF_AntiDe"), track.tpcNSigmaDe(), track.tofNSigmaDe(), track.pt()); + if (track.pt() >= pTthrprTOF.value || !doITSPID.value || track.itsNSigmaPr() > nsigmaITSPr.value) { + registryQa.fill(HIST("QA/h3dTPCTOF_AntiPr"), track.tpcNSigmaPr(), track.tofNSigmaPr(), track.pt()); + } + if (track.pt() >= pTthrdeTOF.value || !doITSPID.value || track.itsNSigmaDe() > nsigmaITSDe.value) { + if (!isPr) { + registryQa.fill(HIST("QA/h3dTPCTOF_AntiDe"), track.tpcNSigmaDe(), track.tofNSigmaDe(), track.pt()); + if (isTOFPID || (isTPCElRejection && (!doITSPID.value || track.itsNSigmaDe() > nsigmaITSDe.value))) { + registryQa.fill(HIST("QA/h3dTPCTOF_AntiDe_Rej"), track.tpcNSigmaDe(), track.tofNSigmaDe(), track.pt()); + } + } + } } if (isProton(track, -1)) { @@ -1695,10 +1737,253 @@ struct HadronNucleiCorrelation { registry.fill(HIST("Generated/hNEventsMC"), 0.5); + // Local representation of a generated particle. + struct GenCoalescenceCandidate { + GenCoalescenceCandidate(float pt, + float eta, + float phi, + int pdg, + float vx, + float vy, + float vz, + float vt, + bool physicalPrimary) + : mKinematics{pt, eta, phi}, + mPdg{pdg}, + mVx{vx}, + mVy{vy}, + mVz{vz}, + mVt{vt}, + mPhysicalPrimary{physicalPrimary} + { + } + [[nodiscard]] float pt() const { return mKinematics.pt(); } + [[nodiscard]] float eta() const { return mKinematics.eta(); } + [[nodiscard]] float phi() const { return mKinematics.phi(); } + + [[nodiscard]] float px() const { return pt() * std::cos(phi()); } + [[nodiscard]] float py() const { return pt() * std::sin(phi()); } + [[nodiscard]] float pz() const { return mKinematics.pz(); } + + [[nodiscard]] float vx() const { return mVx; } + [[nodiscard]] float vy() const { return mVy; } + [[nodiscard]] float vz() const { return mVz; } + [[nodiscard]] float vt() const { return mVt; } + + [[nodiscard]] float mass() const + { + switch (std::abs(mPdg)) { + case PDG_t::kProton: + return o2::track::PID::getMass(o2::track::PID::Proton); + case PDG_t::kNeutron: + return o2::constants::physics::MassNeutron; + case o2::constants::physics::Pdg::kDeuteron: + return o2::track::PID::getMass(o2::track::PID::Deuteron); + default: + LOG(fatal) << "Unhandled pdg " << mPdg; + return 0.f; + } + } + [[nodiscard]] float energy() const { return std::hypot(mKinematics.p(), mass()); } + [[nodiscard]] float rapidity() const { return mKinematics.rapidityForMass(mass()); } + [[nodiscard]] float y() const { return rapidity(); } + [[nodiscard]] bool isPhysicalPrimary() const { return mPhysicalPrimary; } + [[nodiscard]] int pdgCode() const { return mPdg; } + + private: + GenCandidate mKinematics; + + int mPdg = 0; + float mVx = 0.f; + float mVy = 0.f; + float mVz = 0.f; + float mVt = 0.f; + + bool mPhysicalPrimary = false; + }; + + std::vector particlesToProcess; + particlesToProcess.reserve(mcParticles.size()); + + for (const auto& particle : mcParticles) { + switch (particle.pdgCode()) { + case PDG_t::kProton: + case PDG_t::kProtonBar: + case PDG_t::kNeutron: + case PDG_t::kNeutronBar: + case o2::constants::physics::Pdg::kDeuteron: + case -o2::constants::physics::Pdg::kDeuteron: + particlesToProcess.emplace_back(particle.pt(), + particle.eta(), + particle.phi(), + particle.pdgCode(), + particle.vx(), + particle.vy(), + particle.vz(), + particle.vt(), + particle.isPhysicalPrimary()); + break; + default: + break; + } + } + + if (settingsCoalescence.doMCGenCoalescence.value) { + std::vector consumed(particlesToProcess.size(), false); + std::vector deuterons; + + // Try p+n -> d and pbar+nbar -> dbar independently. + for (const int sign : {+1, -1}) { + const int protonPDG = sign * PDG_t::kProton; + const int neutronPDG = sign * PDG_t::kNeutron; + const int deuteronPDG = sign * o2::constants::physics::Pdg::kDeuteron; + + for (size_t ip = 0; ip < particlesToProcess.size(); ++ip) { + if (consumed[ip] || particlesToProcess[ip].pdgCode() != protonPDG) { + continue; + } + + const auto& proton = particlesToProcess[ip]; + + for (size_t in = 0; in < particlesToProcess.size(); ++in) { + if (consumed[in] || particlesToProcess[in].pdgCode() != neutronPDG) { + continue; + } + + const auto& neutron = particlesToProcess[in]; + + const float ep = proton.energy(); + const float en = neutron.energy(); + + // Propagate the particle freezing out first to the freeze-out time of the other particle + float xp = proton.vx(); + float yp = proton.vy(); + float zp = proton.vz(); + const float tp = proton.vt(); + + float xn = neutron.vx(); + float yn = neutron.vy(); + float zn = neutron.vz(); + const float tn = neutron.vt(); + + const float commonTime = std::max(static_cast(tp), static_cast(tn)); + + if (tp < commonTime) { + const float dt = commonTime - tp; + xp += proton.px() / ep * dt; + yp += proton.py() / ep * dt; + zp += proton.pz() / ep * dt; + } + + if (tn < commonTime) { + const float dt = commonTime - tn; + xn += neutron.px() / en * dt; + yn += neutron.py() / en * dt; + zn += neutron.pz() / en * dt; + } + + // Velocity of the p-n centre-of-mass frame. + const float totalE = ep + en; + const float totalPx = proton.px() + neutron.px(); + const float totalPy = proton.py() + neutron.py(); + const float totalPz = proton.pz() + neutron.pz(); + + const float bx = totalPx / totalE; + const float by = totalPy / totalE; + const float bz = totalPz / totalE; + + const float beta2 = bx * bx + by * by + bz * bz; + if (beta2 >= 1.) { + continue; + } + + const float gamma = 1. / std::sqrt(1. - beta2); + + // Boost the proton momentum into the p-n rest frame. + // In that frame p_p* = -p_n*, therefore |p_p*| is the relative momentum entering the coalescence cut. + float pxStar = proton.px(); + float pyStar = proton.py(); + float pzStar = proton.pz(); + + if (beta2 > 0.) { + const float betaDotP = bx * proton.px() + by * proton.py() + bz * proton.pz(); + + const float factor = ((gamma - 1.) * betaDotP / beta2) - gamma * ep; + + pxStar += factor * bx; + pyStar += factor * by; + pzStar += factor * bz; + } + + const float pRelative = std::sqrt(pxStar * pxStar + pyStar * pyStar + pzStar * pzStar); + + if (pRelative >= settingsCoalescence.pMax.value) { + continue; + } + + // The two particles have already been propagated to equal time in the lab. Transform their spatial separation to the pair CM frame. + float dx = xp - xn; + float dy = yp - yn; + float dz = zp - zn; + + if (beta2 > 0.) { + const float betaDotR = bx * dx + by * dy + bz * dz; + const float factor = (gamma - 1.) * betaDotR / beta2; + + dx += factor * bx; + dy += factor * by; + dz += factor * bz; + } + + const float rRelative = std::sqrt(dx * dx + dy * dy + dz * dz); + + if (rRelative >= settingsCoalescence.rMax.value) { + continue; + } + + // Successful coalescence: remove the constituent proton and neutron and replace them by a deuteron carrying their total three-momentum + consumed[ip] = true; + consumed[in] = true; + + const float deuteronPt = std::hypot(totalPx, totalPy); + const float deuteronPhi = std::atan2(totalPy, totalPx); + const float deuteronEta = std::asinh(totalPz / std::max(deuteronPt, 1.e-12f)); + + deuterons.emplace_back(deuteronPt, + deuteronEta, + deuteronPhi, + deuteronPDG, + 0.5f * (xp + xn), + 0.5f * (yp + yn), + 0.5f * (zp + zn), + commonTime, + proton.isPhysicalPrimary() && neutron.isPhysicalPrimary()); + break; + } + } + } + + // Construct the post-coalescence particle list. + std::vector coalescedParticles; + coalescedParticles.reserve(particlesToProcess.size() + deuterons.size()); + + for (size_t i = 0; i < particlesToProcess.size(); ++i) { + if (!consumed[i]) { + coalescedParticles.push_back(std::move(particlesToProcess[i])); + } + } + + for (auto& deuteron : deuterons) { + coalescedParticles.push_back(std::move(deuteron)); + } + + particlesToProcess = std::move(coalescedParticles); + } + // Pairing candidates are collected during the QA loop below (which already visits every particle) GenCollisionCache genCache; - for (const auto& particle : mcParticles) { + for (const auto& particle : particlesToProcess) { auto fillGeneratedQa = [this, &particle](const float binPosition) { switch (particle.pdgCode()) { case PDG_t::kProton: @@ -1822,7 +2107,7 @@ struct HadronNucleiCorrelation { }; using BinningTypeMC = FlexibleBinningPolicy, aod::mccollision::PosZ, decltype(getMultiplicity)>; - BinningTypeMC colBinningGen{{getMultiplicity}, {confVtxBins, confMultBins}, true}; + BinningTypeMC colBinningGen{{getMultiplicity}, {confVtxBins, confMultBins}}; for (const auto& [collision1, collision2] : soa::selfCombinations(colBinningGen, maxmixcollsGen.value, -1, mcCollisions, mcCollisions)) { const auto& cache1 = genCaches.at(collision1.globalIndex()); diff --git a/PWGLF/Tasks/Nuspex/he3LambdaDerivedAnalysis.cxx b/PWGLF/Tasks/Nuspex/he3LambdaDerivedAnalysis.cxx index afe3814afd9..52b0e7cc552 100644 --- a/PWGLF/Tasks/Nuspex/he3LambdaDerivedAnalysis.cxx +++ b/PWGLF/Tasks/Nuspex/he3LambdaDerivedAnalysis.cxx @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -42,6 +43,11 @@ std::shared_ptr hInvariantMassLambda[2]; std::shared_ptr hCosPALambda; std::shared_ptr hNsigmaHe3; std::shared_ptr hNsigmaProton; +std::shared_ptr hInvariantMassHe3K0s[2]; // 0: anti-He3, 1: He3 +std::shared_ptr hRotationInvariantMassHe3K0s[2]; +std::shared_ptr hInvariantMassK0s[2]; // Before and after selection +std::shared_ptr hCosPAK0s; +std::shared_ptr hCtK0s; }; // namespace using namespace o2; @@ -59,6 +65,15 @@ struct he3LambdaDerivedAnalysis { Configurable cfgMinLambdaPt{"cfgMinLambdaPt", 0.4, "Minimum pT for Lambda candidates"}; Configurable cfgMaxLambdaDeltaM{"cfgMaxLambdaDeltaM", 10.0e-3, "Maximum deltaM for Lambda candidates"}; + Configurable cfgMinK0sPt{"cfgMinK0sPt", 0.5f, "Minimum K0s pT"}; + Configurable cfgMaxK0sPt{"cfgMaxK0sPt", 10.f, "Maximum K0s pT"}; + Configurable cfgMinK0sCt{"cfgMinK0sCt", 0.f, "Minimum K0s proper decay length (cm)"}; + Configurable cfgMaxK0sCt{"cfgMaxK0sCt", 20.f, "Maximum K0s proper decay length (cm)"}; + Configurable cfgMinK0sCosPA{"cfgMinK0sCosPA", 0.99f, "Minimum K0s cosPA"}; + Configurable cfgMaxK0sDeltaM{"cfgMaxK0sDeltaM", 10.e-3f, "Maximum K0s mass deviation (GeV/c^2)"}; + Configurable cfgMaxNSigmaTPCPionK0s{"cfgMaxNSigmaTPCPionK0s", 4.f, "Maximum daughter pion TPC nSigma for K0s"}; + ConfigurableAxis cfgHe3K0sMassAxis{"cfgHe3K0sMassAxis", {200, MassHelium3 + MassK0Short, MassHelium3 + MassK0Short + 1.}, "He3-K0s pair invariant mass (GeV/c^2)"}; + void init(InitContext const&) { constexpr double ConstituentsMass = o2::constants::physics::MassProton + o2::constants::physics::MassNeutron * 2 + o2::constants::physics::MassSigmaPlus; @@ -70,12 +85,26 @@ struct he3LambdaDerivedAnalysis { hInvariantMassLambda[i] = mRegistry.add(Form("hInvariantMassLambda%i", i), "Invariant Mass Lambda", {HistType::kTH2D, {{50, 0., 10.}, {30, o2::constants::physics::MassLambda0 - 0.015, o2::constants::physics::MassLambda0 + 0.015}}}); hRotationInvariantMassAntiLSeta[i] = mRegistry.add(Form("hRotationInvariantMassAntiLSeta%i", i), "Rotation Invariant Mass Anti-Lambda", {HistType::kTH2D, {{45, 1., 10}, {100, ConstituentsMass - 0.05, ConstituentsMass + 0.05}}}); } + if (doprocessSameEvent && doprocessSameEventLegacy) { + LOG(fatal) << "Enable only one Lambda table version: processSameEvent or processSameEventLegacy"; + } + if (doprocessK0s) { + const AxisSpec pairMassAxis{cfgHe3K0sMassAxis, "m(He3 K0s) (GeV/c^{2})"}; + for (int i = 0; i < 2; ++i) { + hInvariantMassHe3K0s[i] = mRegistry.add(Form("hInvariantMassHe3K0s%i", i), "Same-event He3-K0s", {HistType::kTH2D, {{45, 1., 10.}, pairMassAxis}}); + hRotationInvariantMassHe3K0s[i] = mRegistry.add(Form("hRotationInvariantMassHe3K0s%i", i), "Rotated He3-K0s", {HistType::kTH2D, {{45, 1., 10.}, pairMassAxis}}); + hInvariantMassK0s[i] = mRegistry.add(Form("hInvariantMassK0s%i", i), "K0s;pT (GeV/c);mass (GeV/c^{2})", {HistType::kTH2D, {{50, 0., 10.}, {100, MassK0Short - 0.015, MassK0Short + 0.015}}}); + } + hCosPAK0s = mRegistry.add("hCosPAK0s", "K0s;pT (GeV/c);cosPA", {HistType::kTH2D, {{50, 0., 10.}, {500, 0.9, 1.}}}); + hCtK0s = mRegistry.add("hCtK0s", "K0s;pT (GeV/c);ct (cm)", {HistType::kTH2D, {{50, 0., 10.}, {100, 0., 30.}}}); + } hCosPALambda = mRegistry.add("hCosPALambda", "Cosine of Pointing Angle for Lambda", {HistType::kTH2D, {{50, 0., 10.}, {500, 0.9, 1.}}}); hNsigmaHe3 = mRegistry.add("hNsigmaHe3", "nSigma TPC for He3", {HistType::kTH2D, {{100, -10., 10.}, {200, -5, 5.}}}); hNsigmaProton = mRegistry.add("hNsigmaProton", "nSigma TPC for Proton", {HistType::kTH2D, {{100, -10., 10.}, {200, -5, 5.}}}); } - void processSameEvent(o2::aod::LFEvents::iterator const& collision, o2::aod::LFHe3_000 const& he3s, o2::aod::LFLambda_000 const& lambdas) + template + void analyseLambda(o2::aod::LFEvents::iterator const& collision, He3Table const& he3s, LambdaTable const& lambdas) { std::vector he3Candidates; he3Candidates.reserve(he3s.size()); @@ -111,6 +140,7 @@ struct he3LambdaDerivedAnalysis { candidate.mass = lambda.mass(); candidate.cosPA = lambda.cosPA(); candidate.dcaV0Daughters = lambda.dcaDaughters(); + candidate.sign = lambda.sign(); hCosPALambda->Fill(lambda.pt(), candidate.cosPA); // hNsigmaProton->Fill(lambda.pt() * lambda.sign(), lambda.protonNSigmaTPC()); hInvariantMassLambda[0]->Fill(lambda.pt(), lambda.mass()); @@ -128,7 +158,7 @@ struct he3LambdaDerivedAnalysis { (he3.sign * lambda.sign > 0 ? hInvariantMassLS : hInvariantMassUS)[he3.sign > 0]->Fill(pairMomentum.Pt(), pairMomentum.M()); } for (int iEta{0}; iEta <= cfgMirrorEta; ++iEta) { - for (int iR{0}; iR <= cfgNrotations; ++iR) { + for (int iR{0}; cfgNrotations > 0 && iR <= cfgNrotations; ++iR) { auto he3Momentum = ROOT::Math::LorentzVector>(he3.momentum.Pt(), (1. - iEta * 2.) * he3.momentum.Eta(), he3.momentum.Phi() + TMath::Pi() * (0.75 + 0.5 * iR / cfgNrotations), he3.momentum.M()); for (const auto& lambda : lambdaCandidates) { auto pairMomentum = lambda.momentum + he3Momentum; // Calculate invariant mass @@ -141,7 +171,63 @@ struct he3LambdaDerivedAnalysis { } } } - PROCESS_SWITCH(he3LambdaDerivedAnalysis, processSameEvent, "Process same event", true); + void processSameEvent(o2::aod::LFEvents::iterator const& collision, o2::aod::LFHe3_001 const& he3s, o2::aod::LFLambda_001 const& lambdas) + { + analyseLambda(collision, he3s, lambdas); + } + PROCESS_SWITCH(he3LambdaDerivedAnalysis, processSameEvent, "Process same-event He3-Lambda pairs (version 001)", true); + + void processSameEventLegacy(o2::aod::LFEvents::iterator const& collision, o2::aod::LFHe3_000 const& he3s, o2::aod::LFLambda_000 const& lambdas) + { + analyseLambda(collision, he3s, lambdas); + } + PROCESS_SWITCH(he3LambdaDerivedAnalysis, processSameEventLegacy, "Process same-event He3-Lambda pairs (version 000)", false); + + void processK0s(o2::aod::LFEvents::iterator const&, o2::aod::LFHe3_001 const& he3s, o2::aod::LFK0s const& k0ss) + { + std::vector k0sCandidates; + k0sCandidates.reserve(k0ss.size()); + for (const auto& k0s : k0ss) { + hInvariantMassK0s[0]->Fill(k0s.pt(), k0s.mass()); + hCosPAK0s->Fill(k0s.pt(), k0s.cosPA()); + hCtK0s->Fill(k0s.pt(), k0s.ct()); + if (k0s.pt() < cfgMinK0sPt || k0s.pt() > cfgMaxK0sPt || k0s.ct() < cfgMinK0sCt || k0s.ct() > cfgMaxK0sCt || + k0s.cosPA() < cfgMinK0sCosPA || std::abs(k0s.mass() - MassK0Short) > cfgMaxK0sDeltaM || + std::abs(k0s.nSigmaTPCPosPion()) > cfgMaxNSigmaTPCPionK0s || std::abs(k0s.nSigmaTPCNegPion()) > cfgMaxNSigmaTPCPionK0s) { + continue; + } + k0sCandidate candidate; + // Use the nominal K0s mass for pair kinematics, as for Lambda. + candidate.momentum.SetCoordinates(k0s.pt(), k0s.eta(), k0s.phi(), MassK0Short); + k0sCandidates.push_back(candidate); + hInvariantMassK0s[1]->Fill(k0s.pt(), k0s.mass()); + } + for (const auto& he3 : he3s) { + if (!doprocessSameEvent && !doprocessSameEventLegacy) { + hNsigmaHe3->Fill(he3.pt() * he3.sign(), he3.nSigmaTPC()); + } + if (std::abs(he3.nSigmaTPC()) > cfgMaxNSigmaTPCHe3) { + continue; + } + const ROOT::Math::LorentzVector> he3Momentum(he3.pt(), he3.eta(), he3.phi(), MassHelium3); + const int signIndex = he3.sign() > 0; + for (const auto& k0s : k0sCandidates) { + const auto pairMomentum = he3Momentum + k0s.momentum; + hInvariantMassHe3K0s[signIndex]->Fill(pairMomentum.Pt(), pairMomentum.M()); + } + // Match the Lambda background's rotation angles and optional eta mirroring. + for (int iEta = 0; iEta <= cfgMirrorEta; ++iEta) { + for (int iR = 0; cfgNrotations > 0 && iR <= cfgNrotations; ++iR) { + const ROOT::Math::LorentzVector> rotatedHe3(he3.pt(), (1. - iEta * 2.) * he3.eta(), he3.phi() + TMath::Pi() * (0.75 + 0.5 * iR / cfgNrotations), MassHelium3); + for (const auto& k0s : k0sCandidates) { + const auto pairMomentum = rotatedHe3 + k0s.momentum; + hRotationInvariantMassHe3K0s[signIndex]->Fill(pairMomentum.Pt(), pairMomentum.M()); + } + } + } + } + } + PROCESS_SWITCH(he3LambdaDerivedAnalysis, processK0s, "Process same-event He3-K0s pairs (version 001)", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGLF/Tasks/Nuspex/hypertritonAnalysis.cxx b/PWGLF/Tasks/Nuspex/hypertritonAnalysis.cxx index 1d2d5c11ec0..bbb966da90c 100644 --- a/PWGLF/Tasks/Nuspex/hypertritonAnalysis.cxx +++ b/PWGLF/Tasks/Nuspex/hypertritonAnalysis.cxx @@ -46,7 +46,9 @@ #include #include +#include #include +#include using namespace o2; using namespace o2::framework; @@ -191,8 +193,8 @@ struct hypertritonAnalysis { kEvSelAllSteps }; // Helper to do bookkeeping and late filling of QA histos - std::array stats; - std::array evselstats; + std::array stats; + std::array evselstats; void resetHistos() { diff --git a/PWGLF/Tasks/Nuspex/pccQa.cxx b/PWGLF/Tasks/Nuspex/pccQa.cxx index fe04b0ffc4e..4469c8cee2d 100644 --- a/PWGLF/Tasks/Nuspex/pccQa.cxx +++ b/PWGLF/Tasks/Nuspex/pccQa.cxx @@ -29,19 +29,83 @@ #include #include +#include +#include #include +#include + +#include #include +#include +#include +#include #include using namespace o2; using namespace o2::framework; using aod::track::TrackSelectionFlags; +int getMotherCategory(int pdg) +{ + switch (std::abs(pdg)) { + case 310: + return 1; // K0S + case 130: + return 2; // K0L + case 3122: + return 3; // Lambda + + case 3222: // Sigma+ + case 3112: + return 4; // Sigma- + + case 3312: + return 5; // Xi- + + case 3334: + return 6; // Omega + + case 211: + return 7; // Pion + case 321: + return 8; // Kaon + case 2112: + return 9; // Neutron + case 13: + return 10; // Muon + + default: + return 11; // Other + } +} + +static std::string getMotherName(int category) +{ + static const std::vector names = { + "K0S", + "K0L", + "Lambda", + "SigmaCharged", + "XiCharged", + "Omega", + "Pi", + "K", + "n", + "Muon", + "Other"}; + + return names.at(category - 1); +} + struct PccQa { HistogramRegistry histos; Service pdg; + std::vector> motherDCA; + std::vector> motherDCAWeighted; + std::vector> motherWeights; + static constexpr float MaxVtxZ = 10.f; Configurable maxMult{"maxMult", 100, "max multiplicity"}; @@ -73,44 +137,76 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) void PccQa::init(InitContext const&) { - histos.add("eventCounter", "", kTH1D, {{1, 0.5, 1.5}}); - const AxisSpec dcaAxis = {1000, -1., 1., "#it{DCA}_{xy}", "dca"}; + // const AxisSpec dcaAxis = {1000, -1., 1., "#it{DCA}_{xy}", "dca"}; + const AxisSpec dcaAxis = {99, -1., 1., "#it{DCA}_{xy}", "dca"}; std::vector ptBinEdges = {0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.5, 3.0}; const AxisSpec ptAxis{ptBinEdges, "#it{p}_{T} (GeV/#it{c})", "pt"}; - const int nBinsMult = 30; - const AxisSpec nchAxis = {nBinsMult, 0., static_cast(maxMult), "#it{N}_{ch}", "nch"}; + const int nBinsMult = 25; + const AxisSpec nchAxis = {nBinsMult, 1., static_cast(maxMult), "#it{N}_{ch}", "nch"}; + + histos.add("multDist", "", kTH1D, {nchAxis}); - histos.add("DCAxyVsPt", "", kTH2D, {ptAxis, dcaAxis}); histos.add("DCAxyVsPtVsMult", "", kTH3D, {nchAxis, ptAxis, dcaAxis}); if (doprocessMC) { - histos.add("DCAxyVsPt_weighted", "", kTH2D, {ptAxis, dcaAxis}); histos.add("DCAxyVsPtVsMult_weighted", "", kTH3D, {nchAxis, ptAxis, dcaAxis}); - histos.add("prim/DCAxyVsPt", "", kTH2D, {ptAxis, dcaAxis}); histos.add("prim/DCAxyVsPtVsMult", "", kTH3D, {nchAxis, ptAxis, dcaAxis}); - histos.add("prim/DCAxyVsPtVsMult_weighted", "", kTH3D, {nchAxis, ptAxis, dcaAxis}); - histos.add("prim/DCAxyVsPt_weighted", "", kTH2D, {ptAxis, dcaAxis}); - histos.add("sec/DCAxyVsPt", "", kTH2D, {ptAxis, dcaAxis}); histos.add("sec/DCAxyVsPtVsMult", "", kTH3D, {nchAxis, ptAxis, dcaAxis}); - - histos.add("sec/DCAxyVsPt_weighted", "", kTH2D, {ptAxis, dcaAxis}); histos.add("sec/DCAxyVsPtVsMult_weighted", "", kTH3D, {nchAxis, ptAxis, dcaAxis}); - histos.add("sec/dec/DCAxyVsPt", "", kTH2D, {ptAxis, dcaAxis}); histos.add("sec/dec/DCAxyVsPtVsMult", "", kTH3D, {nchAxis, ptAxis, dcaAxis}); - - histos.add("sec/dec/DCAxyVsPt_weighted", "", kTH2D, {ptAxis, dcaAxis}); histos.add("sec/dec/DCAxyVsPtVsMult_weighted", "", kTH3D, {nchAxis, ptAxis, dcaAxis}); - histos.add("sec/mat/DCAxyVsPt", "", kTH2D, {ptAxis, dcaAxis}); histos.add("sec/mat/DCAxyVsPtVsMult", "", kTH3D, {nchAxis, ptAxis, dcaAxis}); - - histos.add("sec/mat/DCAxyVsPt_weighted", "", kTH2D, {ptAxis, dcaAxis}); histos.add("sec/mat/DCAxyVsPtVsMult_weighted", "", kTH3D, {nchAxis, ptAxis, dcaAxis}); + + histos.add("prim/weights", "", kTProfile, {ptAxis}); + histos.add("sec/weights", "", kTProfile, {ptAxis}); + histos.add("sec/dec/weights", "", kTProfile, {ptAxis}); + histos.add("sec/mat/weights", "", kTProfile, {ptAxis}); + + std::vector labels = { + "K^{0}_{S}", "K^{0}_{L}", "#Lambda", + "#Sigma^{#pm}", + "#Xi^{#pm}", + "#Omega^{#pm}", + "#pi^{#pm}", "K^{#pm}", "n", + "#mu^{#pm}", + "Other"}; + + const int nMotherCategories = static_cast(labels.size()); + + histos.add( + "sec/dec/motherPDG", + "Mother particles of decay secondaries", + kTH1D, + {{nMotherCategories, 0.5, nMotherCategories + 0.5}}); + + auto hMother = histos.get(HIST("sec/dec/motherPDG")); + + for (int i = 0; i < nMotherCategories; ++i) { + hMother->GetXaxis()->SetBinLabel(i + 1, labels[i].c_str()); + } + + for (int category = 1; category <= nMotherCategories; ++category) { + const auto mother = getMotherName(category); + + motherDCA.push_back(histos.add( + fmt::format("sec/dec/{}/DCAxyVsPt", mother), + "", kTH2D, {ptAxis, dcaAxis})); + + motherDCAWeighted.push_back(histos.add( + fmt::format("sec/dec/{}/DCAxyVsPt_weighted", mother), + "", kTH2D, {ptAxis, dcaAxis})); + + motherWeights.push_back(histos.add( + fmt::format("sec/dec/{}/weights", mother), + "", kTProfile, {ptAxis})); + } } } @@ -135,44 +231,109 @@ void PccQa::processMeas(const C& collision, const T& tracks) if ((std::abs(collision.posZ()) > MaxVtxZ) || !collision.sel8()) { return; } - histos.fill(HIST("eventCounter"), 1); + + auto MultCounterMeas = 0; + + for (const auto& track : tracks) { + + if (!TrackSelectionFlags::checkFlag(track.trackCutFlag(), TrackSelectionFlags::kGlobalTrackWoDCA)) { + continue; + } + + MultCounterMeas++; + } + + /* LOGP(info, "MCB: --------------------------"); + LOGP(info, "MCB: ++ New event with Nch = {} ",MultCounterMeas); + LOGP(info, "MCB: --------------------------"); */ + + histos.fill(HIST("multDist"), MultCounterMeas); for (const auto& track : tracks) { if (!TrackSelectionFlags::checkFlag(track.trackCutFlag(), TrackSelectionFlags::kGlobalTrackWoDCA)) { continue; } - histos.fill(HIST("DCAxyVsPt"), track.pt(), track.dcaXY()); - histos.fill(HIST("DCAxyVsPtVsMult"), tracks.size(), track.pt(), track.dcaXY()); + + histos.fill(HIST("DCAxyVsPtVsMult"), MultCounterMeas, track.pt(), track.dcaXY()); if constexpr (IS_MC) { if (!track.has_mcParticle()) { continue; } + const auto& particle = track.template mcParticle_as(); - histos.fill(HIST("DCAxyVsPt_weighted"), track.pt(), track.dcaXY(), particle.pccWeight()); - histos.fill(HIST("DCAxyVsPtVsMult_weighted"), tracks.size(), track.pt(), track.dcaXY(), particle.pccWeight()); + histos.fill(HIST("DCAxyVsPtVsMult_weighted"), MultCounterMeas, track.pt(), track.dcaXY(), particle.pccWeight()); if (particle.isPhysicalPrimary()) { - histos.fill(HIST("prim/DCAxyVsPt"), track.pt(), track.dcaXY()); - histos.fill(HIST("prim/DCAxyVsPt_weighted"), track.pt(), track.dcaXY(), particle.pccWeight()); - histos.fill(HIST("prim/DCAxyVsPtVsMult"), tracks.size(), track.pt(), track.dcaXY()); - histos.fill(HIST("prim/DCAxyVsPtVsMult_weighted"), tracks.size(), track.pt(), track.dcaXY(), particle.pccWeight()); + + histos.fill(HIST("prim/weights"), particle.pt(), particle.pccWeight()); + histos.fill(HIST("prim/DCAxyVsPtVsMult"), MultCounterMeas, track.pt(), track.dcaXY()); + histos.fill(HIST("prim/DCAxyVsPtVsMult_weighted"), MultCounterMeas, track.pt(), track.dcaXY(), particle.pccWeight()); + } else { - histos.fill(HIST("sec/DCAxyVsPt"), track.pt(), track.dcaXY()); - histos.fill(HIST("sec/DCAxyVsPt_weighted"), track.pt(), track.dcaXY(), particle.pccWeight()); - histos.fill(HIST("sec/DCAxyVsPtVsMult"), tracks.size(), track.pt(), track.dcaXY()); - histos.fill(HIST("sec/DCAxyVsPtVsMult_weighted"), tracks.size(), track.pt(), track.dcaXY(), particle.pccWeight()); - if (particle.getProcess() == TMCProcess::kPDecay) { - histos.fill(HIST("sec/dec/DCAxyVsPt"), track.pt(), track.dcaXY()); - histos.fill(HIST("sec/dec/DCAxyVsPt_weighted"), track.pt(), track.dcaXY(), particle.pccWeight()); - histos.fill(HIST("sec/dec/DCAxyVsPtVsMult"), tracks.size(), track.pt(), track.dcaXY()); - histos.fill(HIST("sec/dec/DCAxyVsPtVsMult_weighted"), tracks.size(), track.pt(), track.dcaXY(), particle.pccWeight()); - } else if (particle.getProcess() == TMCProcess::kPHInhelastic || particle.getProcess() == TMCProcess::kPHadronic || particle.getProcess() == TMCProcess::kPHElastic) { - histos.fill(HIST("sec/mat/DCAxyVsPt"), track.pt(), track.dcaXY()); - histos.fill(HIST("sec/mat/DCAxyVsPt_weighted"), track.pt(), track.dcaXY(), particle.pccWeight()); - histos.fill(HIST("sec/mat/DCAxyVsPtVsMult"), tracks.size(), track.pt(), track.dcaXY()); - histos.fill(HIST("sec/mat/DCAxyVsPtVsMult_weighted"), tracks.size(), track.pt(), track.dcaXY(), particle.pccWeight()); + + if (!particle.has_mothers()) { + continue; + } + + histos.fill(HIST("sec/weights"), particle.pt(), particle.pccWeight()); + + histos.fill(HIST("sec/DCAxyVsPtVsMult"), MultCounterMeas, track.pt(), track.dcaXY()); + histos.fill(HIST("sec/DCAxyVsPtVsMult_weighted"), MultCounterMeas, track.pt(), track.dcaXY(), particle.pccWeight()); + + if (particle.getProcess() == TMCProcess::kPDecay || particle.getProcess() == TMCProcess::kPPrimary) { + + histos.fill(HIST("sec/dec/DCAxyVsPtVsMult"), + MultCounterMeas, track.pt(), track.dcaXY()); + + histos.fill(HIST("sec/dec/DCAxyVsPtVsMult_weighted"), + MultCounterMeas, track.pt(), track.dcaXY(), + particle.pccWeight()); + + histos.fill(HIST("sec/dec/weights"), + particle.pt(), particle.pccWeight()); + + /* LOGP(info, + " -> Particle={}, PDG={}, process={}, " + "nMoms={}, 1. mom ID={}", + particle.globalIndex(), + particle.pdgCode(), + particle.getProcess(), + particle.mothersIds().size(), + particle.mothersIds()[0]); */ + + // Identify the immediate mother + // auto mother = particle.template mothers_first_as(); + auto mother = particle.template mothers_first_as(); + + const int motherCategory = getMotherCategory(mother.pdgCode()); + // const std::string motherName = getMotherName(motherCategory); + + histos.fill( + HIST("sec/dec/motherPDG"), + motherCategory); + + const int i = motherCategory - 1; + + /* LOGP(info, + " --> Mother PDG={}", + mother.pdgCode() + ); */ + + motherDCA[i]->Fill(track.pt(), track.dcaXY()); + + motherDCAWeighted[i]->Fill( + track.pt(), track.dcaXY(), particle.pccWeight()); + + motherWeights[i]->Fill( + particle.pt(), particle.pccWeight()); + + } else { + + histos.fill(HIST("sec/mat/weights"), particle.pt(), particle.pccWeight()); + histos.fill(HIST("sec/mat/DCAxyVsPtVsMult"), MultCounterMeas, track.pt(), track.dcaXY()); + histos.fill(HIST("sec/mat/DCAxyVsPtVsMult_weighted"), MultCounterMeas, track.pt(), track.dcaXY(), particle.pccWeight()); } } } diff --git a/PWGLF/Tasks/Nuspex/piKpRAA.cxx b/PWGLF/Tasks/Nuspex/piKpRAA.cxx index 03f52ce8c5d..3288501f999 100644 --- a/PWGLF/Tasks/Nuspex/piKpRAA.cxx +++ b/PWGLF/Tasks/Nuspex/piKpRAA.cxx @@ -128,6 +128,8 @@ struct PiKpRAA { static constexpr float KmaxdEdxMIP{60.0f}; static constexpr float KmindEdxMIPPlateau{70.0f}; static constexpr float KmaxdEdxMIPPlateau{90.0f}; + static constexpr float KminFV0A{2.2f}; + static constexpr float KmaxFV0A{5.1f}; static constexpr float KminFT0A{3.5f}; static constexpr float KmaxFT0A{4.9f}; static constexpr float KminFT0C{-3.3f}; @@ -257,6 +259,7 @@ struct PiKpRAA { ConfigurableAxis axisPtFineFixedWidth{"axisPtFineFixedWidth", {250, 0.1f, 20.1f}, "DCAxy axis"}; ConfigurableAxis axisNPV{"axisNPV", {600, 0.0f, 600.0f}, "Number of Primary Vertex Contributors"}; ConfigurableAxis axisNch{"axisNch", {400, 0.0f, 400.0f}, "N_{ch} Multiplicity"}; + ConfigurableAxis axisFineCent{"axisFineCent", {100, 0.0, 100.0}, "Fine centrality bins"}; Configurable> vecLowEta{"vecLowEta", {-0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6}, "lower eta cuts"}; Configurable> vecUpEta{"vecUpEta", {-0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8}, "upper eta cuts"}; @@ -499,88 +502,9 @@ struct PiKpRAA { } } - if (doprocessSim) { - registry.add("EventCounterMC", "From 2nd bin, corresponds to number of MC Coll with associated Rec Coll.;;Events", kTH1F, {{5, 0, 5}}); - registry.add("zPosMC", "From the association between the MC event & the Rec. Coll with largest number of PV contributors & NO Evt. Sel.;;Entries;", kTH1F, {axisZpos}); - registry.add("zPosMCAll", "All MC Events;;Entries;", kTH1F, {axisZpos}); - registry.add("EtaMCParAllMCColl", "#eta from all MC particles in all MC collisions;#eta;Entries;", kTH1F, {{50, -10.0, 10.0}}); - registry.add("EtaMCParAllMCColl_Pion", "#eta from all MC particles in all MC collisions;#eta;Entries;", kTH1F, {{50, -10.0, 10.0}}); - registry.add("EtaMCParAllMCColl_Kaon", "#eta from all MC particles in all MC collisions;#eta;Entries;", kTH1F, {{50, -10.0, 10.0}}); - registry.add("EtaMCParAllMCColl_Proton", "#eta from all MC particles in all MC collisions;#eta;Entries;", kTH1F, {{50, -10.0, 10.0}}); - registry.add("EtaMCParAllMCColl_Electron", "#eta from all MC particles in all MC collisions;#eta;Entries;", kTH1F, {{50, -10.0, 10.0}}); - registry.add("EtaMCParAllMCColl_Rest", "#eta from all MC particles in all MC collisions;#eta;Entries;", kTH1F, {{50, -10.0, 10.0}}); - registry.add("dcaVsPtPiDec", "Secondary pions from decays;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm);Centrality Perc.;", kTH3F, {axisPt, axisDCAxy, axisCent}); - registry.add("dcaVsPtPrDec", "Secondary protons from decays;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm);Centrality Perc.;", kTH3F, {axisPt, axisDCAxy, axisCent}); - registry.add("dcaVsPtPiMat", "Secondary pions from material interactions;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm);Centrality Perc.;", kTH3F, {axisPt, axisDCAxy, axisCent}); - registry.add("dcaVsPtPrMat", "Secondary protons from material interactions;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm);Centrality Perc.;", kTH3F, {axisPt, axisDCAxy, axisCent}); - registry.add("DCAxyVsPtWithSelection", ";#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm);", kTH2F, {{axisPtFineFixedWidth}, {axisDCAxy}}); - registry.add("DCAzVsPtWithSelection", ";#it{p}_{T} (GeV/#it{c});DCA_{z} (cm);", kTH2F, {{axisPtFineFixedWidth}, {axisDCAxy}}); - - registry.add("CentralityVsBCVsFT0VsTVXVsEvSel", "All=1 | BC=2 | FT0=3 | TVX=4 | EvSel=5;;Status;", kTH2F, {{axisCent}, {5, 0.5, 5.5}}); - registry.add("NumOfRecColl", "Num. of times a MC evt. is reconstructed;N;Entries", kTH1F, {{5, -0.5, 4.5}}); - registry.add("NumOfRecCollVsNContri", "Num. of times a MC evt. is reconstructed VS Num. of PV contributors;Number of times a MC event is reconstructed;Number of tracks used for the PV", kTH2F, {{5, -0.5, 4.5}, axisNch}); - // registry.add("NumOfRecCollVsNContriWithEvtSel", "Num. of times a MC evt. is reconstructed VS Num. of PV contributors WITH EVT SEL;Number of times a MC event is reconstructed;Number of tracks used for the PV", kTH2F, {{5, -0.5, 4.5}, axisNch}); - - // Pt resolution - registry.add("PtResolution", "p_{T} resolution;;(pt_{rec} - pt_{gen})/pt_{gen};", kTH2F, {axisPt, {100, -1.0, 1.0}}); - - // Needed to calculate the numerator of the Acceptance X Efficiency - registry.add("PtPiVsCent_WithRecoEvt", "Generated Events With at least One Rec. Collision + Sel. criteria;;;", kTH2F, {axisPt, axisCent}); - registry.add("PtKaVsCent_WithRecoEvt", "Generated Events With at least One Rec. Collision + Sel. criteria;;;", kTH2F, {axisPt, axisCent}); - registry.add("PtPrVsCent_WithRecoEvt", "Generated Events With at least One Rec. Collision + Sel. criteria;;;", kTH2F, {axisPt, axisCent}); - - registry.add("PtGenPiVsCent_WithRecoEvt", "Generated Events With at least One Rec. Collision + Sel. criteria;;;", kTH2F, {axisPt, axisCent}); - registry.add("PtGenKaVsCent_WithRecoEvt", "Generated Events With at least One Rec. Collision + Sel. criteria;;;", kTH2F, {axisPt, axisCent}); - registry.add("PtGenPrVsCent_WithRecoEvt", "Generated Events With at least One Rec. Collision + Sel. criteria;;;", kTH2F, {axisPt, axisCent}); - - // Needed to calculate the denominator of the Acceptance X Efficiency - registry.add("PtPiVsCentMC_WithRecoEvt", "Generated Events With at least One Rec. Collision;;;", kTH2F, {axisPt, axisCent}); - registry.add("PtKaVsCentMC_WithRecoEvt", "Generated Events With at least One Rec. Collision;;;", kTH2F, {axisPt, axisCent}); - registry.add("PtPrVsCentMC_WithRecoEvt", "Generated Events With at least One Rec. Collision;;;", kTH2F, {axisPt, axisCent}); - - // Needed for the Gen. Nch to Centrality conversion - registry.add("NchMCVsCent", "Generated Nch v.s. Centrality (At least Once Rec. Coll. + Sel. criteria);;Gen. Nch MC", kTH2F, {axisCent, axisNch}); - - // Needed to measure Event Loss - registry.add("NchMC_WithRecoEvt", "MC Nch from the event associated with the Rec. Coll. (with largest Num. of PV contributors) + Evt. Sel.;Gen Nch MC;Entries", kTH1F, {axisNch}); - registry.add("NchMC_AllGen", "MC Nch of ALL events;Gen. Nch;Entries", kTH1F, {axisNch}); - registry.add("NchMC_WithRecColl", "MC Nch from the event associated with at least one Rec. Coll.;Gen. Nch;Entries", kTH1F, {axisNch}); - registry.add("NchMC_BestCollIdx", "MC Nch from the event associated with ONLY ONE Rec. Coll. with largest Num. of PV contributors;Gen. Nch;Entries", kTH1F, {axisNch}); - - // Needed to measure Event Splitting - // registry.add("Centrality_WRecoEvt", "From the association between the MC event & the Rec. Coll with largest number of PV contributors & NO Evt. Sel.;;Entries", kTH1F, {axisCent}); - registry.add("Centrality_WRecoEvtWSelCri", "From the association between the MC event & the Rec. Coll with largest number of PV contributors + Evt. Sel.;;Entries", kTH1F, {axisCent}); - registry.add("Centrality_AllRecoEvt", "All Rec. Coll. Irrespective of the times it was reconstructed + Evt. Sel.;;Entries", kTH1F, {axisCent}); - - // Needed to calculate the numerator of the Signal Loss correction - registry.add("PtPiVsNchMC_WithRecoEvt", "Generated Events With at least One Rec. Collision;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - registry.add("PtKaVsNchMC_WithRecoEvt", "Generated Events With at least One Rec. Collision;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - registry.add("PtPrVsNchMC_WithRecoEvt", "Generated Events With at least One Rec. Collision;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - - // Needed to calculate the denominator of the Signal Loss correction - registry.add("PtPiVsNchMC_AllGen", "All Generated Events;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - registry.add("PtKaVsNchMC_AllGen", "All Generated Events;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - registry.add("PtPrVsNchMC_AllGen", "All Generated Events;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - - registry.add("MCclosure_PtMCPiVsNchMC", "All Generated Events 4 MC closure;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - registry.add("MCclosure_PtMCKaVsNchMC", "All Generated Events 4 MC closure;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - registry.add("MCclosure_PtMCPrVsNchMC", "All Generated Events 4 MC closure;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - - registry.add("MCclosure_PtPiVsNchMC", "Gen Evts With at least one Rec. Coll. + Sel. criteria 4 MC closure;;Gen. Nch (|#eta|<0.8);", kTH2F, {axisPt, axisNch}); - registry.add("MCclosure_PtKaVsNchMC", "Gen Evts With at least one Rec. Coll. + Sel. criteria 4 MC closure;;Gen. Nch (|#eta|<0.8);", kTH2F, {axisPt, axisNch}); - registry.add("MCclosure_PtPrVsNchMC", "Gen Evts With at least one Rec. Coll. + Sel. criteria 4 MC closure;;Gen. Nch (|#eta|<0.8);", kTH2F, {axisPt, axisNch}); - - auto hMcCouter = registry.get(HIST("EventCounterMC")); - auto* xMcCounter = hMcCouter->GetXaxis(); - xMcCounter->SetBinLabel(1, "All MC Coll"); - xMcCounter->SetBinLabel(2, "Rec Coll w/ nContributors > 0"); - xMcCounter->SetBinLabel(3, "Rec Coll W/ nContributors > 0 + HasFT0"); - xMcCounter->SetBinLabel(4, "Rec Coll W/ nContributors > 0 + HasFT0 + HasTVX"); - } - if (doprocessSignalLoss) { - registry.add("EventCounterMC", "From 2nd bin, corresponds to number of MC Coll with associated Rec Coll.;;Events", kTH1F, {{5, 0, 5}}); - registry.add("zPosMC", "From the association between the MC event & the Rec. Coll with largest number of PV contributors & NO Evy. Sel.;;Entries;", kTH1F, {axisZpos}); + registry.add("EventCounterMC", "From 2nd bin, corresponds to number of MC Coll with associated Rec Coll.;;Events", kTH1F, {{7, 0, 7}}); + registry.add("zPosMC", "From the association between the MC event & the Rec. Coll with largest number of PV contributors & NO Evt. Sel.;;Entries;", kTH1F, {axisZpos}); registry.add("zPosMCAll", "All MC Events;;Entries;", kTH1F, {axisZpos}); registry.add("EtaMCParAllMCColl", "#eta from all MC particles in all MC collisions;#eta;Entries;", kTH1F, {{50, -10.0, 10.0}}); registry.add("EtaMCParAllMCColl_Pion", "#eta from all MC particles in all MC collisions;#eta;Entries;", kTH1F, {{50, -10.0, 10.0}}); @@ -597,7 +521,7 @@ struct PiKpRAA { registry.add("NumOfRecColl", "Num. of times a MC evt. is reconstructed;N;Entries", kTH1F, {{5, -0.5, 4.5}}); registry.add("NumOfRecCollVsNContri", "Num. of times a MC evt. is reconstructed VS Num. of PV contributors;Number of times a MC event is reconstructed;Number of tracks used for the PV", kTH2F, {{8, -0.5, 7.5}, axisNch}); - registry.add("NumOfRecCollVsCentrality", "Num. of times a MC evt. is reconstructed VS Centrality WITH EVT SEL;Number of times a MC event is reconstructed;Number of tracks used for the PV", kTH2F, {{8, -0.5, 7.5}, axisCent}); + registry.add("NumOfRecCollVsCentrality", "Num. of times a MC evt. is reconstructed VS Centrality WITH EVT SEL;Number of times a MC event is reconstructed;Number of tracks used for the PV", kTH2F, {axisCent, {8, -0.5, 7.5}}); // PT RESOLUTION registry.add("PtResolution", "p_{T} resolution;;(pt_{rec} - pt_{gen})/pt_{gen};", kTH2F, {axisPt, {100, -1.0, 1.0}}); @@ -618,51 +542,79 @@ struct PiKpRAA { registry.add("PtPrVsCentMC_WithRecoEvt", "Generated Events With at least One Rec. Collision;;;", kTH2F, {axisPt, axisCent}); // FOR MAPPING BETWEN MC NCH AND CENTRALITY. BASED ONLY ON THE COLLISION WITH THE LARGEST NUMBER OF CONTRIBUTORS TO PV - registry.add("NchMCVsCent", "Gen Nch vs Cent (Rec. Coll. W/Largest Contributors + Evt. Sel.);;Gen. Nch MC", kTH2F, {axisCent, axisNch}); + registry.add("NchMCVsCent_WithRecoEvt_BestCollIdx", "Gen Nch vs Cent (Evt. Sel. + W/Largest N PV Contributors);;Gen. Nch MC", kTH2F, {axisCent, axisNch}); // FOR MAPPING BETWEN MC NCH AND CENTRALITY. ALL THE RECONSTRUCTED COLLISIONS EVENT IF RECONSTRUCTED MULTIPLE TIMES - registry.add("NchMCVsCent_AllRecoEvt", "Gen Nch vs Cent (All Rec. Coll. + Evt. Sel.);;Gen. Nch MC", kTH2F, {axisCent, axisNch}); + registry.add("NchMCVsCent_WithRecoEvt", "Gen Nch vs Cent (All Rec. Coll. + Evt. Sel.);;Gen. Nch MC", kTH2F, {axisCent, axisNch}); // DENOMINATOR OF THE EVENT LOSS. ALL GEN EVENTS registry.add("NchMC_AllGen", "MC Nch of ALL events;Gen. Nch;Entries", kTH1F, {axisNch}); // NUMERATOR OF THE EVENT LOOS. BASED ONLY ON THE COLLISION WITH THE LARGEST NUMBER OF CONTRIBUTORS TO PV - registry.add("NchMC_WithRecoEvt", "Gen Evts W/best Coll Idx + Evt. Sel.;Gen Nch;Entries", kTH1F, {axisNch}); + registry.add("NchMC_WithRecoEvt_BestCollIdx", "Gen Evts W/best Coll Idx + Evt. Sel.;Gen Nch;Entries", kTH1F, {axisNch}); // NUMERATOR OF THE EVENT LOOS. ALL THE RECONSTRUCTED COLLISIONS EVENT IF RECONSTRUCTED MULTIPLE TIMES - registry.add("NchMC_AllRecoEvt", "Gen Evts W/at leas One Rec. Coll + Evt. Sel.;Gen. Nch;Entries", kTH1F, {axisNch}); - // MC NCH DISTRIBUTION. BASED ONLY ON THE COLLISION WITH THE LARGEST NUMBER OF CONTRIBUTORS TO PV & NO EVT SEL. (QA PURPOSES) - registry.add("NchMC_BestCollIdx", "MC Nch from the event associated with ONLY ONE Rec. Coll. with largest Num. of PV contributors;Gen. Nch;Entries", kTH1F, {axisNch}); - // MC NCH DISTRIBUTION ASSOCIATED TO ALL THE RECONSTRUCTED COLLISIONS & NO EVT. SEL. (QA PURPOSES) - registry.add("NchMC_WithRecColl", "MC Nch from the event associated with at least one Rec. Coll.;Gen. Nch;Entries", kTH1F, {axisNch}); + registry.add("NchMC_WithRecoEvt", "Gen Evts W/at leas One Rec. Coll + Evt. Sel.;Gen. Nch;Entries", kTH1F, {axisNch}); + // DENOMINATOR OF THE EVENT LOOS. ALL THE RECONSTRUCTED COLLISIONS EVENT IF RECONSTRUCTED MULTIPLE TIMES WITH FT0 INFO + TVX-TRIGGERED + registry.add("NchMC_WithRecoEvt_HasFT0AndTVX", "Gen Evts W/at leas One Rec. Coll With FT0 Info + TVX;Gen. Nch;Entries", kTH1F, {axisNch}); + // NUMERATOR OF THE EVENT LOOS. BASED ONLY ON THE COLLISION WITH THE LARGEST NUMBER OF CONTRIBUTORS TO PV + registry.add("NchMC_WithRecoEvt_HasFT0AndTVX_BestCollIdx", "Gen Evts W/best Coll Idx With FT0 Info + TVX;Gen Nch;Entries", kTH1F, {axisNch}); // NUMERATOR OF THE EVENT SPLITTING. registry.add("Centrality_WRecoEvtWSelCri", "From the association between the MC event & the Rec. Coll with largest number of PV contributors + Evt. Sel.;;Entries", kTH1F, {axisCent}); // DENOMINATOR OF THE EVENT SPLITTING. registry.add("Centrality_AllRecoEvt", "All Rec. Coll. Irrespective of the times it was reconstructed + Evt. Sel.;;Entries", kTH1F, {axisCent}); + // ======================================= + + // USED FOR CALCULATING EVENT LOSS AND SIGNAL LOSS AS IN THE DN/DETA ANALYSIS + registry.add("Centrality_FineBins_WithRecoEvt", "Rec. Coll. Irrespective of the times it was reconstructed + Evt. Sel.;Centrality;Entries", kTH1F, {axisFineCent}); + registry.add("Centrality_FineBins_WithRecoEvt_BestCollIdx", "Rec. Coll. W/best Coll Indx + Evt. Sel.;Centrality;Entries", kTH1F, {axisFineCent}); + registry.add("NchMC_Fwd_AllGen", "MC Nch of ALL events;Gen. Nch (Forward);Entries", kTH1F, {axisNch}); + registry.add("NchMC_Fwd_WithRecoEvt", "True Nch from MC events W/at leas One Rec. Coll + Evt. Sel.;Gen. Nch (Forward);Entries", kTH1F, {axisNch}); + registry.add("NchMC_Fwd_WithRecoEvt_BestCollIdx", "True Nch from MC events W/best Coll Idx + Evt. Sel.;Gen. Nch (Forward);Entries", kTH1F, {axisNch}); + registry.add("NchMC_Fwd_WithRecoEvt_HasFT0AndTVX", "Gen Evts W/at leas One Rec. Coll With FT0 Info + TVX;Gen. Nch;Entries", kTH1F, {axisNch}); + registry.add("NchMC_Fwd_WithRecoEvt_HasFT0AndTVX_BestCollIdx", "Gen Evts W/best Coll Idx With FT0 Info + TVX;Gen Nch;Entries", kTH1F, {axisNch}); + + // NUMERATOR OF THE SIGNAL LOSS. BASED ONLY ON THE COLLISION WITH THE LARGEST NUMBER OF CONTRIBUTORS TO PV + registry.add("PtPiVsNchMCFwd_WithRecoEvt_BestCollIdx", "Gen Evts W/best Coll Idx + Evt. Sel.;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + registry.add("PtKaVsNchMCFwd_WithRecoEvt_BestCollIdx", "Gen Evts W/best Coll Idx + Evt. Sel.;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + registry.add("PtPrVsNchMCFwd_WithRecoEvt_BestCollIdx", "Gen Evts W/best Coll Idx + Evt. Sel.;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + + // NUMERATOR OF THE SIGNAL LOSS. USES ALL THE RECONSTRUCTED COLLISIONS EVEN IF RECONSTRUCTED MULTIPLE TIMES + registry.add("PtPiVsNchMCFwd_WithRecoEvt", "Gen Evts W/at leas One Rec. Coll + Evt. Sel.;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + registry.add("PtKaVsNchMCFwd_WithRecoEvt", "Gen Evts W/at leas One Rec. Coll + Evt. Sel.;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + registry.add("PtPrVsNchMCFwd_WithRecoEvt", "Gen Evts W/at leas One Rec. Coll + Evt. Sel.;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + + // DENOMINATOR OF THE SIGNAL LOSS + registry.add("PtPiVsNchMCFwd_AllGen", "All Generated Events;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + registry.add("PtKaVsNchMCFwd_AllGen", "All Generated Events;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + registry.add("PtPrVsNchMCFwd_AllGen", "All Generated Events;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + + // NUMERATOR OF THE SIGNAL LOSS. USES ALL THE RECONSTRUCTED COLLISIONS EVENT IF RECONSTRUCTED MULTIPLE TIMES (A LA PI0) + registry.add("PtPiVsNchMCFwd_HasFT0AndTVX", "Gen Evts W/at leas One Rec. Coll + FT0 Info & TVX triggered;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + registry.add("PtKaVsNchMCFwd_HasFT0AndTVX", "Gen Evts W/at leas One Rec. Coll + FT0 Info & TVX triggered;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + registry.add("PtPrVsNchMCFwd_HasFT0AndTVX", "Gen Evts W/at leas One Rec. Coll + FT0 Info & TVX triggered;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + + // DENOMITOR OF THE SIGNAL LOSS. USES ALL THE RECONSTRUCTED COLLISIONS EVENT IF RECONSTRUCTED MULTIPLE TIMES (A LA PI0) + registry.add("PtPiVsNchMCFwd_HasFT0AndTVX_BestCollIdx", "Gen Evts W/best Coll Idx + FT0 Info & TVX triggered;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + registry.add("PtKaVsNchMCFwd_HasFT0AndTVX_BestCollIdx", "Gen Evts W/best Coll Idx + FT0 Info & TVX triggered;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + registry.add("PtPrVsNchMCFwd_HasFT0AndTVX_BestCollIdx", "Gen Evts W/best Coll Idx + FT0 Info & TVX triggered;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + + // ======================================= + // NUMERATOR OF THE SIGNAL LOSS. BASED ONLY ON THE COLLISION WITH THE LARGEST NUMBER OF CONTRIBUTORS TO PV - registry.add("PtPiVsNchMC_WithRecoEvt", "Gen Evts W/best Coll Idx + Evt. Sel.;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - registry.add("PtKaVsNchMC_WithRecoEvt", "Gen Evts W/best Coll Idx + Evt. Sel.;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - registry.add("PtPrVsNchMC_WithRecoEvt", "Gen Evts W/best Coll Idx + Evt. Sel.;;Gen. Nch;", kTH2F, {axisPt, axisNch}); + registry.add("PtPiVsNchMC_WithRecoEvt_BestCollIdx", "Gen Evts W/best Coll Idx + Evt. Sel.;;Gen. Nch;", kTH2F, {axisPt, axisNch}); + registry.add("PtKaVsNchMC_WithRecoEvt_BestCollIdx", "Gen Evts W/best Coll Idx + Evt. Sel.;;Gen. Nch;", kTH2F, {axisPt, axisNch}); + registry.add("PtPrVsNchMC_WithRecoEvt_BestCollIdx", "Gen Evts W/best Coll Idx + Evt. Sel.;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - // NUMERATOR OF THE SIGNAL LOSS. USES ALL THE RECONSTRUCTED COLLISIONS EVENT IF RECONSTRUCTED MULTIPLE TIMES - registry.add("PtPiVsNchMC_AllRecoEvt", "Gen Evts W/at leas One Rec. Coll + Evt. Sel.;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - registry.add("PtKaVsNchMC_AllRecoEvt", "Gen Evts W/at leas One Rec. Coll + Evt. Sel.;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - registry.add("PtPrVsNchMC_AllRecoEvt", "Gen Evts W/at leas One Rec. Coll + Evt. Sel.;;Gen. Nch;", kTH2F, {axisPt, axisNch}); + // NUMERATOR OF THE SIGNAL LOSS. USES ALL THE RECONSTRUCTED COLLISIONS EVEN IF RECONSTRUCTED MULTIPLE TIMES + registry.add("PtPiVsNchMC_WithRecoEvt", "Gen Evts W/at leas One Rec. Coll + Evt. Sel.;;Gen. Nch;", kTH2F, {axisPt, axisNch}); + registry.add("PtKaVsNchMC_WithRecoEvt", "Gen Evts W/at leas One Rec. Coll + Evt. Sel.;;Gen. Nch;", kTH2F, {axisPt, axisNch}); + registry.add("PtPrVsNchMC_WithRecoEvt", "Gen Evts W/at leas One Rec. Coll + Evt. Sel.;;Gen. Nch;", kTH2F, {axisPt, axisNch}); // DENOMINATOR OF THE SIGNAL LOSS registry.add("PtPiVsNchMC_AllGen", "All Generated Events;;Gen. Nch;", kTH2F, {axisPt, axisNch}); registry.add("PtKaVsNchMC_AllGen", "All Generated Events;;Gen. Nch;", kTH2F, {axisPt, axisNch}); registry.add("PtPrVsNchMC_AllGen", "All Generated Events;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - // DENOMINATOR OF THE SIGNAL LOSS. USES ALL THE RECONSTRUCTED COLLISIONS EVENT IF RECONSTRUCTED MULTIPLE TIMES (A LA PI0) - registry.add("PtPiVsNchMC_HasFT0", "Gen Evts W/at leas One Rec. Coll + FT0 Info;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - registry.add("PtKaVsNchMC_HasFT0", "Gen Evts W/at leas One Rec. Coll + FT0 Info;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - registry.add("PtPrVsNchMC_HasFT0", "Gen Evts W/at leas One Rec. Coll + FT0 Info;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - - // DENOMINATOR OF THE SIGNAL LOSS. BASED ONLY ON THE COLLISION WITH THE LARGEST NUMBER OF CONTRIBUTORS TO PV (A LA PI0) - registry.add("PtPiVsNchMC_HasFT0_BestCollIdx", "Gen Evts W/best Coll Idx + FT0 Info;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - registry.add("PtKaVsNchMC_HasFT0_BestCollIdx", "Gen Evts W/best Coll Idx + FT0 Info;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - registry.add("PtPrVsNchMC_HasFT0_BestCollIdx", "Gen Evts W/best Coll Idx + FT0 Info;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - // NUMERATOR OF THE SIGNAL LOSS. USES ALL THE RECONSTRUCTED COLLISIONS EVENT IF RECONSTRUCTED MULTIPLE TIMES (A LA PI0) registry.add("PtPiVsNchMC_HasFT0AndTVX", "Gen Evts W/at leas One Rec. Coll + FT0 Info & TVX triggered;;Gen. Nch;", kTH2F, {axisPt, axisNch}); registry.add("PtKaVsNchMC_HasFT0AndTVX", "Gen Evts W/at leas One Rec. Coll + FT0 Info & TVX triggered;;Gen. Nch;", kTH2F, {axisPt, axisNch}); @@ -673,14 +625,6 @@ struct PiKpRAA { registry.add("PtKaVsNchMC_HasFT0AndTVX_BestCollIdx", "Gen Evts W/best Coll Idx + FT0 Info & TVX triggered;;Gen. Nch;", kTH2F, {axisPt, axisNch}); registry.add("PtPrVsNchMC_HasFT0AndTVX_BestCollIdx", "Gen Evts W/best Coll Idx + FT0 Info & TVX triggered;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - // registry.add("MCclosure_PtMCPiVsNchMC", "All Generated Events 4 MC closure;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - // registry.add("MCclosure_PtMCKaVsNchMC", "All Generated Events 4 MC closure;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - // registry.add("MCclosure_PtMCPrVsNchMC", "All Generated Events 4 MC closure;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - - // registry.add("MCclosure_PtPiVsNchMC", "Gen Evts With at least one Rec. Coll. + Sel. criteria 4 MC closure;;Gen. Nch (|#eta|<0.8);", kTH2F, {axisPt, axisNch}); - // registry.add("MCclosure_PtKaVsNchMC", "Gen Evts With at least one Rec. Coll. + Sel. criteria 4 MC closure;;Gen. Nch (|#eta|<0.8);", kTH2F, {axisPt, axisNch}); - // registry.add("MCclosure_PtPrVsNchMC", "Gen Evts With at least one Rec. Coll. + Sel. criteria 4 MC closure;;Gen. Nch (|#eta|<0.8);", kTH2F, {axisPt, axisNch}); - auto hMcCouter = registry.get(HIST("EventCounterMC")); auto* xMcCounter = hMcCouter->GetXaxis(); xMcCounter->SetBinLabel(1, "All MC Coll"); @@ -688,6 +632,7 @@ struct PiKpRAA { xMcCounter->SetBinLabel(3, "nContributors > 0 + HasFT0"); xMcCounter->SetBinLabel(4, "nContributors > 0 + HasFT0 & HasTVX"); xMcCounter->SetBinLabel(5, "nContributors > 0 + HasFT0 & HasTVX & Only largest Contributors"); + xMcCounter->SetBinLabel(6, "W/Asso Rec. Coll + Evt. Sel + bestCollIdx"); } LOG(info) << "\trequireGoodRct=" << requireGoodRct.value; @@ -1351,7 +1296,7 @@ struct PiKpRAA { Preslice perCollision = aod::track::collisionId; Service pdg; - void processSim(aod::McCollisions::iterator const& mccollision, soa::SmallGroups const& collisions, BCsRun3 const& /*bcs*/, aod::FT0s const& /*ft0s*/, aod::McParticles const& mcParticles, TracksMC const& tracksMC) + void processSignalLoss(aod::McCollisions::iterator const& mccollision, soa::SmallGroups const& collisions, BCsRun3 const& /*bcs*/, aod::FT0s const& /*ft0s*/, aod::McParticles const& mcParticles, TracksMC const& tracksMC) { const auto& kLowEta = (std::vector)vecLowEta; @@ -1359,13 +1304,14 @@ struct PiKpRAA { registry.fill(HIST("EventCounterMC"), 0.5); //--------------------------- - // Nch counters in different pseudorapidity intervals + // Nch COUNTER IN THE TPC ACCEPTANCE + // ONLY ETA SELECTION IS IMPOSED //--------------------------- - int nChMC{0}; int nChMCTPCAcc{0}; - int nChFT0A{0}; - int nChFT0C{0}; - + int nChMCFV0A{0}; + int nChMCFT0C{0}; + int nChMCFT0M{0}; + int nChMCForward{0}; for (const auto& particle : mcParticles) { auto charge{0.}; @@ -1418,68 +1364,46 @@ struct PiKpRAA { registry.fill(HIST("EtaMCParAllMCColl_Rest"), eta); } - // TVX requirement - if (eta > KminFT0A && eta < KmaxFT0A) { - nChFT0A++; - } - - if (eta > KminFT0C && eta < KmaxFT0C) { - nChFT0C++; - } - if (std::abs(eta) < tpcNchAcceptance) { nChMCTPCAcc++; } - - // INEL > 0 - if (std::abs(eta) > Kone) { - continue; + if (eta > KminFV0A && eta < KmaxFV0A) { + nChMCFV0A++; + } + if (eta > KminFT0C && eta < KmaxFT0C) { + nChMCFT0C++; + } + if ((eta > KminFT0A && eta < KmaxFT0A) || (eta > KminFT0C && eta < KmaxFT0C)) { + nChMCFT0M++; } - - nChMC++; } - //--------------------------- - // Selects MC events with at least one charged particle in the FT0A & FT0C - //--------------------------- - if (selTVXMC) { - if (!(nChFT0A > KzeroInt && nChFT0C > KzeroInt)) { - return; - } + // DEFINE THE MC NCH MULTIPLICITY BASED ON THE CENTRALITY ESTIMATOR + if (centralitySelector.value == "FT0C") { + nChMCForward = nChMCFT0C; + } else if (centralitySelector.value == "FT0M") { + nChMCForward = nChMCFT0M; + } else if (centralitySelector.value == "FV0A") { + nChMCForward = nChMCFV0A; + } else { + nChMCForward = -999; } //--------------------------- - // Selects MC events with |Zvtx| < zVtx_Cut + // SELECTS MC EVENTS WITH |ZVTX| < ZVTX_CUT //--------------------------- if (isZvtxPosSelMC && (std::fabs(mccollision.posZ()) > posZcut)) { return; } //--------------------------- - // Selects INEL > 0 MC evets - //--------------------------- - if (selINELgt0) { - if (!(nChMC > KzeroInt)) { - return; - } - } - - //--------------------------- - // All Generated events irrespective of whether there is an associated reconstructed collision - // Consequently, the centrality being a reconstructed quantity, is not always be available - // Therefore it is expressed as a function of the generated pT and the generated Nch in ∣eta∣ < 0.8 - // This is used for the denominator of the SIGNAL LOSS correction - // Also for MC closure: True Pt vs Generated Nch + // DENOMINATOR OF THE SIGNAL LOSS: ALL MC EVENTS //--------------------------- for (const auto& particle : mcParticles) { if (particle.eta() < trackSelections.minEta || particle.eta() > trackSelections.maxEta) { continue; } - if (particle.pt() < trackSelections.minPt || particle.pt() > trackSelections.maxPt) { - continue; - } - auto charge{0.}; // Get the MC particle auto* pdgParticle = pdg->GetParticle(particle.pdgCode()); @@ -1516,127 +1440,209 @@ struct PiKpRAA { } if (particle.pdgCode() == PDG_t::kPiPlus || particle.pdgCode() == PDG_t::kPiMinus) { - registry.fill(HIST("PtPiVsNchMC_AllGen"), particle.pt(), nChMCTPCAcc); - registry.fill(HIST("MCclosure_PtMCPiVsNchMC"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtPiVsNchMC_AllGen"), particle.pt(), nChMCTPCAcc); // DENOMINATOR OF SIGNAL LOSS + registry.fill(HIST("PtPiVsNchMCFwd_AllGen"), particle.pt(), nChMCForward); // DENOMINATOR OF SIGNAL LOSS } else if (particle.pdgCode() == PDG_t::kKPlus || particle.pdgCode() == PDG_t::kKMinus) { - registry.fill(HIST("PtKaVsNchMC_AllGen"), particle.pt(), nChMCTPCAcc); - registry.fill(HIST("MCclosure_PtMCKaVsNchMC"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtKaVsNchMC_AllGen"), particle.pt(), nChMCTPCAcc); // DENOMINATOR OF SIGNAL LOSS + registry.fill(HIST("PtKaVsNchMCFwd_AllGen"), particle.pt(), nChMCForward); // DENOMINATOR OF SIGNAL LOSS } else if (particle.pdgCode() == PDG_t::kProton || particle.pdgCode() == PDG_t::kProtonBar) { - registry.fill(HIST("PtPrVsNchMC_AllGen"), particle.pt(), nChMCTPCAcc); - registry.fill(HIST("MCclosure_PtMCPrVsNchMC"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtPrVsNchMC_AllGen"), particle.pt(), nChMCTPCAcc); // DENOMINATOR OF SIGNAL LOSS + registry.fill(HIST("PtPrVsNchMCFwd_AllGen"), particle.pt(), nChMCForward); // DENOMINATOR OF SIGNAL LOSS } else { continue; } } // Loop over Generated Particles //--------------------------- - // This is used for the denominator of the EVENT LOSS correction - // Only charge particles within tpcNchAcceptance and without pT selection + // nChMCTPCAc = CHARGEd PARTICLES WITHIN THE TPC ACCEPTANCE AND WITHOUT PT SELECTION //--------------------------- + + // DENOMINATOR OF THE EVENT LOSS: ALL THE GENERATED EVENTS + registry.fill(HIST("NchMC_Fwd_AllGen"), nChMCForward); registry.fill(HIST("NchMC_AllGen"), nChMCTPCAcc); registry.fill(HIST("zPosMCAll"), mccollision.posZ()); //--------------------------- - // How many times was the MC event reconstrued? + // HOW MANY TIMES WAS THE MC EVENT RECONSTRUED? //--------------------------- const auto& nRecColls{collisions.size()}; registry.fill(HIST("NumOfRecColl"), nRecColls); //--------------------------- - // Only Generated evets with at least one reconstrued collision + // MC EVENTS WITH AT LEAST ONE ASSOCIATED RECONSTRUCTED COLLISION //--------------------------- if (nRecColls > KzeroInt) { //--------------------------- - // Looks for the collision with the largest number of tracks contributing to the PV reconstruction - // The selected collision is identified by its global index (bestCollisionIndex) + // THIS COLLISIONS-LOOP GETS THE INDEX (bestCollisionIndex) OF THE RECONSTRUCTED COLLISION WITH THE LARGEST NUMBER OF PV CONTRIBUTORS + // NO EVENT SELECTIONS APPLIED //--------------------------- int biggestNContribs{-1}; int bestCollisionIndex{-1}; - for (const auto& collision : collisions) { + float centrality{-999.0}; + if (centralitySelector.value == "FT0C") { + centrality = collision.centFT0C(); + } else if (centralitySelector.value == "FT0M") { + centrality = collision.centFT0M(); + } else if (centralitySelector.value == "FV0A") { + centrality = collision.centFV0A(); + } else { + centrality = -999.0; + } registry.fill(HIST("NumOfRecCollVsNContri"), nRecColls, collision.numContrib()); + // CENTRALITY FROM THE MULTIPLE TIMES THE MC EVENT WAS RECONSTRUCTED + registry.fill(HIST("NumOfRecCollVsCentrality"), centrality, nRecColls); //--------------------------- - // Gets the index of the collision with the largest number of contributors + // GETS THE BESTCOLLISION INDEX //--------------------------- if (biggestNContribs < collision.numContrib()) { biggestNContribs = collision.numContrib(); bestCollisionIndex = collision.globalIndex(); } + const bool hasft0{collision.has_foundFT0()}; + const bool hastvx{collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)}; + + //--------------------------- + // ALL COLLISIONS WITH N CONTRIBUTORS > 0 + //--------------------------- + registry.fill(HIST("EventCounterMC"), 1.5); + + //--------------------------- + // COLLISIONS WITH FT0 INFO + //--------------------------- + if (hasft0) { + registry.fill(HIST("EventCounterMC"), 2.5); + } + + //--------------------------- + // TVX-triggered collision + //--------------------------- + if (hasft0 && hastvx) { + registry.fill(HIST("EventCounterMC"), 3.5); + } + } + + //--------------------------- + // EVENT SELECTIONS APPLIED: ONLY FT0-INFO AND TVX-TRIGGERED REQUIRED + //--------------------------- + for (const auto& collision : collisions) { + + bool isBestCollIndex{false}; + if (bestCollisionIndex == collision.globalIndex()) { + isBestCollIndex = true; + } + + bool hasbc{false}; + bool hasft0{false}; + bool hastvx{false}; + bool hasBcFT0TVX{false}; + + hasbc = collision.has_foundBC(); + hasft0 = collision.has_foundFT0(); + hastvx = collision.selection_bit(o2::aod::evsel::kIsTriggerTVX); + hasBcFT0TVX = (hasbc && hasft0 && hastvx); + + if (hasBcFT0TVX) { + // NUMERATOR/DENOMINATOR OF THE EVENT LOSS + registry.fill(HIST("NchMC_WithRecoEvt_HasFT0AndTVX"), nChMCTPCAcc); + registry.fill(HIST("NchMC_Fwd_WithRecoEvt_HasFT0AndTVX"), nChMCForward); + + // TVX-triggered + bestCollisionIndex + if (isBestCollIndex) { + registry.fill(HIST("EventCounterMC"), 4.5); + // NUMERATOR/DENOMINATOR OF THE EVENT LOSS + registry.fill(HIST("NchMC_WithRecoEvt_HasFT0AndTVX_BestCollIdx"), nChMCTPCAcc); + registry.fill(HIST("NchMC_Fwd_WithRecoEvt_HasFT0AndTVX_BestCollIdx"), nChMCForward); + } + } + //--------------------------- - // This is only for QA + // LOOPING OVER THE MC EVENT //--------------------------- - int mcNchInTPCAcc{0}; for (const auto& particle : mcParticles) { + if (particle.eta() < trackSelections.minEta || particle.eta() > trackSelections.maxEta) { + continue; + } auto charge{0.}; - // Get the MC particle - const auto* pdgParticle = pdg->GetParticle(particle.pdgCode()); + // GET THE MC PARTICLE + auto* pdgParticle = pdg->GetParticle(particle.pdgCode()); if (pdgParticle != nullptr) { charge = pdgParticle->Charge(); } else { continue; } - // Is it a charged particle? + // IS IT A CHARGED PARTICLE? if (std::abs(charge) < KminCharge) { continue; } - // Is it a primary particle? + // SELECTS PARTICLE BASED ON ITS CHARGE + if (trackSelections.signCharge.value == "Positive" && charge < Kzero) { + continue; + } + if (trackSelections.signCharge.value == "Negative" && charge > Kzero) { + continue; + } + + // SELECTS ONLY PRIMARY PARTICLES if (!particle.isPhysicalPrimary()) { continue; } - // Selects particles based on their vertex creation - // This is added to reject loopers + // SELECTS PARTICLES BASED ON THEIR VERTEX CREATION + // THIS IS ADDED TO REJECT LOOPERS const float vX{particle.vx()}; const float vY{particle.vy()}; if (std::hypot(vX, vY) > trackSelections.particleVtxSel) { continue; } - const float eta{particle.eta()}; - if (std::abs(eta) < tpcNchAcceptance) { - mcNchInTPCAcc++; + // TO COMPUTE SIGNAL LOSS RELATIVE TO THE MC SIGNAL OF EVENTS WITH FT0 INFO + TVX-TRIGGERED + if (particle.pdgCode() == PDG_t::kPiPlus || particle.pdgCode() == PDG_t::kPiMinus) { + if (hasBcFT0TVX) { + registry.fill(HIST("PtPiVsNchMC_HasFT0AndTVX"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtPiVsNchMCFwd_HasFT0AndTVX"), particle.pt(), nChMCForward); + if (isBestCollIndex) { + registry.fill(HIST("PtPiVsNchMC_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtPiVsNchMCFwd_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCForward); + } + } + } else if (particle.pdgCode() == PDG_t::kKPlus || particle.pdgCode() == PDG_t::kKMinus) { + if (hasBcFT0TVX) { + registry.fill(HIST("PtKaVsNchMC_HasFT0AndTVX"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtKaVsNchMCFwd_HasFT0AndTVX"), particle.pt(), nChMCForward); + if (isBestCollIndex) { + registry.fill(HIST("PtKaVsNchMC_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtKaVsNchMCFwd_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCForward); + } + } + } else if (particle.pdgCode() == PDG_t::kProton || particle.pdgCode() == PDG_t::kProtonBar) { + if (hasBcFT0TVX) { + registry.fill(HIST("PtPrVsNchMC_HasFT0AndTVX"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtPrVsNchMCFwd_HasFT0AndTVX"), particle.pt(), nChMCForward); + if (isBestCollIndex) { + registry.fill(HIST("PtPrVsNchMC_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtPrVsNchMCFwd_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCForward); + } + } + } else { + continue; } - } - - //--------------------------- - // All collisions with N contributors > 0 - //--------------------------- - if (collision.numContrib() > KzeroInt) { - registry.fill(HIST("EventCounterMC"), 1.5); - // To calculate the amount of charged-primary MC particles (|eta|<0.5) - // that belongs to a MC event that was reconstructed at least once - registry.fill(HIST("NchMC_WithRecColl"), mcNchInTPCAcc); - } - - //--------------------------- - // Records only one collision with the largest number of contributors - //--------------------------- - if (bestCollisionIndex == collision.globalIndex()) { - registry.fill(HIST("EventCounterMC"), 2.5); - // To calculate the amount of charged-primary MC particles (|eta|<0.5) - // that belongs to a MC event that was reconstructed - // and only from the with the largest number of contributors - registry.fill(HIST("NchMC_BestCollIdx"), mcNchInTPCAcc); - } - - //--------------------------- - // TVX-triggered collision - //--------------------------- - if (collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { - registry.fill(HIST("EventCounterMC"), 3.5); - } + } // LOOP OVER TRUE MC EVENTS } //--------------------------- - // Needed to calculate denominator of the EVENT SPLITTING correction - // MC Collisions that are reconstructed more than one will be stored here + // MC EVENTS RECONSTRUCTED MULTIPLE TIMES + // EVENT SELECTION APPLIED MANUALLY TO AVOID DOUBLE FILLING IN THE EVENT COUNTER + // *) DENOMINATOR OF THE EVENT SPLITTING + // *) NUMERATOR OF THE EVENT LOSS + // *) NUMERATOR OF SIGNAL LOSS //--------------------------- for (const auto& collision : collisions) { @@ -1651,33 +1657,28 @@ struct PiKpRAA { centrality = -999.0; } - //--------------------------- - // Event Selection - // Applied manually to avoid double filling the EventCounter - //--------------------------- - - // Has BC? + // HAS BC? if (selHasBC) { if (!collision.has_foundBC()) { continue; } } - // Has FT0 information? + // HAS FT0 INFORMATION? if (selHasFT0) { if (!collision.has_foundFT0()) { continue; } } - // Use sel8? + // USE SEL8? if (useSel8) { if (!collision.sel8()) { continue; } } - // kIsTriggerTVX + // KISTRIGGERTVX if (selTriggerTVX) { if (!collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { continue; @@ -1730,867 +1731,15 @@ struct PiKpRAA { } } - //--------------------------- - // Needed to calculate denominator of the EVENT SPLITTING correction - // MC Collisions that are reconstructed more than one will be stored here - //--------------------------- - registry.fill(HIST("Centrality_AllRecoEvt"), centrality); - // registry.fill(HIST("NumOfRecCollVsNContriWithEvtSel"), nRecColls, collision.numContrib()); - } - - //--------------------------- - // Loop over the reconstructed collisions - // Only that one with the largest number of contributors is considered - //--------------------------- - for (const auto& collision : collisions) { - - float centrality{-999.0}; - if (centralitySelector.value == "FT0C") { - centrality = collision.centFT0C(); - } else if (centralitySelector.value == "FT0M") { - centrality = collision.centFT0M(); - } else if (centralitySelector.value == "FV0A") { - centrality = collision.centFV0A(); - } else { - centrality = -999.0; - } - - //--------------------------- - // Pick the collisions with the largest number of contributors - //--------------------------- - if (bestCollisionIndex != collision.globalIndex()) { - continue; - } + registry.fill(HIST("NchMCVsCent_WithRecoEvt"), centrality, nChMCTPCAcc); // FOR MAPPING BETWEEN MC NCH AND CENTRALITY + registry.fill(HIST("Centrality_AllRecoEvt"), centrality); // DENOMINATOR OF EVENT SPLITTING + registry.fill(HIST("NchMC_WithRecoEvt"), nChMCTPCAcc); // NUMERATOR OF EVENT LOSS CORRECTION + registry.fill(HIST("Centrality_FineBins_WithRecoEvt"), centrality); // CENTRALITY OF RECONSTRUED COLLISIONS + EVENT SELECTION + BESTCOLLISIONINDEX + registry.fill(HIST("NchMC_Fwd_WithRecoEvt"), nChMCForward); // Nch MC Forward - // Needed to load the Phi selection from the CCDB - const auto& foundBC = collision.foundBC_as(); - uint64_t timeStamp{foundBC.timestamp()}; - // const int magField{getMagneticField(timeStamp)}; - - if (trackSelections.applyPhiCut) { - const int nextRunNumber{foundBC.runNumber()}; - if (currentRunNumberPhiSel != nextRunNumber) { - loadPhiCutSelections(timeStamp); - currentRunNumberPhiSel = nextRunNumber; - LOG(info) << "\tcurrentRunNumberPhiSel= " << currentRunNumberPhiSel << " timeStamp = " << timeStamp; - } - - // return if phi cut objects are nullptr - if (!(phiCut.hPhiCutHigh && phiCut.hPhiCutLow)) { - return; - } - } - - // registry.fill(HIST("Centrality_WRecoEvt"), centrality); - registry.fill(HIST("zPosMC"), mccollision.posZ()); - - registry.fill(HIST("CentralityVsBCVsFT0VsTVXVsEvSel"), centrality, 1.0); - registry.fill(HIST("HasBCVsFT0VsTVXVsEvSel"), 1.0); - - if (collision.has_foundBC()) { - registry.fill(HIST("CentralityVsBCVsFT0VsTVXVsEvSel"), centrality, 2.0); - registry.fill(HIST("HasBCVsFT0VsTVXVsEvSel"), 2.0); - } - - if (collision.has_foundBC() && collision.has_foundFT0()) { - registry.fill(HIST("CentralityVsBCVsFT0VsTVXVsEvSel"), centrality, 3.0); - registry.fill(HIST("HasBCVsFT0VsTVXVsEvSel"), 3.0); - } - - if (collision.has_foundBC() && collision.has_foundFT0() && collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { - registry.fill(HIST("CentralityVsBCVsFT0VsTVXVsEvSel"), centrality, 4.0); - registry.fill(HIST("HasBCVsFT0VsTVXVsEvSel"), 4.0); - } - - //--------------------------- - // RCT Selection - //--------------------------- - if (requireGoodRct) { - // Checks if collisions passes RCT selection - const bool isFT0Bad{requireBCRct ? foundBC.rct_bit(kFT0Bad) : collision.rct_bit(kFT0Bad)}; - const bool isITSBad{requireBCRct ? foundBC.rct_bit(kITSBad) : collision.rct_bit(kITSBad)}; - const bool isITSLimAcc{requireBCRct ? foundBC.rct_bit(kITSLimAccMCRepr) : collision.rct_bit(kITSLimAccMCRepr)}; - const bool isTOFBad{requireBCRct ? foundBC.rct_bit(kTOFBad) : collision.rct_bit(kTOFBad)}; - const bool isTOFLimAcc{requireBCRct ? foundBC.rct_bit(kTOFLimAccMCRepr) : collision.rct_bit(kTOFLimAccMCRepr)}; - const bool isTPCTrackingBad{requireBCRct ? foundBC.rct_bit(kTPCBadTracking) : collision.rct_bit(kTPCBadTracking)}; - const bool isTPCPIDBad{requireBCRct ? foundBC.rct_bit(kTPCBadPID) : collision.rct_bit(kTPCBadPID)}; - const bool isTPCLimAcc{requireBCRct ? foundBC.rct_bit(kTPCLimAccMCRepr) : collision.rct_bit(kTPCLimAccMCRepr)}; - - registry.fill(HIST("CentralityVsRCTSel"), centrality, 1.0); - if (!isFT0Bad) { - registry.fill(HIST("CentralityVsRCTSel"), centrality, 2.0); - } - if (!isITSBad) { - registry.fill(HIST("CentralityVsRCTSel"), centrality, 3.0); - } - if (!isITSLimAcc) { - registry.fill(HIST("CentralityVsRCTSel"), centrality, 4.0); - } - if (!isTOFBad) { - registry.fill(HIST("CentralityVsRCTSel"), centrality, 5.0); - } - if (!isTOFLimAcc) { - registry.fill(HIST("CentralityVsRCTSel"), centrality, 6.0); - } - if (!isTPCTrackingBad) { - registry.fill(HIST("CentralityVsRCTSel"), centrality, 7.0); - } - if (!isTPCPIDBad) { - registry.fill(HIST("CentralityVsRCTSel"), centrality, 8.0); - } - if (!isTPCLimAcc) { - registry.fill(HIST("CentralityVsRCTSel"), centrality, 9.0); - } - - registry.fill(HIST("RCTSel"), 1.0); - if (!rctChecker(collision)) { - return; - } - - registry.fill(HIST("RCTSel"), 2.0); - } - - //--------------------------- - // Event Selection - //--------------------------- - if (!isEventSelected(collision)) { - return; - } - - //--------------------------- - // Needed to construct the correlation between MC Nch v.s. centrality - //--------------------------- - registry.fill(HIST("Centrality_WRecoEvtWSelCri"), centrality); // To calculate numerator of EVENT SPLITTING - registry.fill(HIST("NchMCVsCent"), centrality, nChMCTPCAcc); // Needed for the mapping betwee MC Nch and centrality - registry.fill(HIST("NchMC_WithRecoEvt"), nChMCTPCAcc); // Numerator of EVENT LOSS correction - registry.fill(HIST("zPos"), collision.posZ()); - registry.fill(HIST("Centrality"), centrality); - - //--------------------------- - // has_foundFT0() ? - //--------------------------- - - if (collision.has_foundBC() && collision.has_foundFT0() && collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { - registry.fill(HIST("CentralityVsBCVsFT0VsTVXVsEvSel"), centrality, 5.0); - registry.fill(HIST("HasBCVsFT0VsTVXVsEvSel"), 5.0); - } - - if (collision.has_foundFT0()) { - registry.fill(HIST("CentralityVsFoundFT0"), centrality, 1.5); - } else { - registry.fill(HIST("CentralityVsFoundFT0"), centrality, 0.5); - } - - //--------------------------- - // All Generated events with at least one associated reconstructed collision - // The Generated events are not subjected to any selection criteria - // However, the associated reconstructed collisions pass the selection criteria - // This histograms are used for the denominator of the tracking efficiency - //--------------------------- - for (const auto& particle : mcParticles) { - if (particle.eta() < trackSelections.minEta || particle.eta() > trackSelections.maxEta) { - continue; - } - - if (particle.pt() < trackSelections.minPt || particle.pt() > trackSelections.maxPt) { - continue; - } - - auto charge{0.}; - // Get the MC particle - auto* pdgParticle = pdg->GetParticle(particle.pdgCode()); - if (pdgParticle != nullptr) { - charge = pdgParticle->Charge(); - } else { - continue; - } - - // Is it a charged particle? - if (std::abs(charge) < KminCharge) { - continue; - } - - // Select particle based on its charge - if (trackSelections.signCharge.value == "Positive" && charge < Kzero) { - continue; - } - if (trackSelections.signCharge.value == "Negative" && charge > Kzero) { - continue; - } - - // Is it a primary particle? - if (!particle.isPhysicalPrimary()) { - continue; - } - - // Selects particles based on their vertex creation - // This is added to reject loopers - const float vX{particle.vx()}; - const float vY{particle.vy()}; - if (std::hypot(vX, vY) > trackSelections.particleVtxSel) { - continue; - } - - if (particle.pdgCode() == PDG_t::kPiPlus || particle.pdgCode() == PDG_t::kPiMinus) { - registry.fill(HIST("PtPiVsCentMC_WithRecoEvt"), particle.pt(), centrality); // Denominator of tracking efficiency - registry.fill(HIST("PtPiVsNchMC_WithRecoEvt"), particle.pt(), nChMCTPCAcc); // Numerator of signal loss - } else if (particle.pdgCode() == PDG_t::kKPlus || particle.pdgCode() == PDG_t::kKMinus) { - registry.fill(HIST("PtKaVsCentMC_WithRecoEvt"), particle.pt(), centrality); - registry.fill(HIST("PtKaVsNchMC_WithRecoEvt"), particle.pt(), nChMCTPCAcc); - } else if (particle.pdgCode() == PDG_t::kProton || particle.pdgCode() == PDG_t::kProtonBar) { - registry.fill(HIST("PtPrVsCentMC_WithRecoEvt"), particle.pt(), centrality); - registry.fill(HIST("PtPrVsNchMC_WithRecoEvt"), particle.pt(), nChMCTPCAcc); - } else { - continue; - } - } // Loop over generated particles per generated collision - - const auto& groupedTracks{tracksMC.sliceBy(perCollision, collision.globalIndex())}; - - // ================ - // Track selection WITHOUT DCAxy and DCAz selections - // Needed for the Secondary Particle Correction - // ================ - for (const auto& track : groupedTracks) { - - const bool applyDca{false}; - if (!selectPrimary(track, applyDca)) { - continue; - } - - if (!track.has_mcParticle()) { - continue; - } - - // Get the MC particle - const auto& particle{track.mcParticle()}; - auto charge{0.}; - auto* pdgParticle = pdg->GetParticle(particle.pdgCode()); - if (pdgParticle != nullptr) { - charge = pdgParticle->Charge(); - } else { - continue; - } - - // Is it a charged particle? - if (std::abs(charge) < KminCharge) { - continue; - } - - // Select particle based on its charge - if (trackSelections.signCharge.value == "Positive" && charge < Kzero) { - continue; - } - if (trackSelections.signCharge.value == "Negative" && charge > Kzero) { - continue; - } - - registry.fill(HIST("DCAxyVsPt"), track.pt(), track.dcaXY()); - registry.fill(HIST("DCAzVsPt"), track.pt(), track.dcaZ()); - - // float phiPrime{track.phi()}; - // phiPrimeFunc(phiPrime, magField, charge); - - bool isPrimary{false}; - bool isDecay{false}; - bool isMaterial{false}; - if (particle.isPhysicalPrimary()) { - isPrimary = true; - } else if (particle.getProcess() == TMCProcess::kPDecay) { - isDecay = true; - } else { - isMaterial = true; - } - - bool isPi{false}; - bool isPr{false}; - if (particle.pdgCode() == PDG_t::kPiPlus || particle.pdgCode() == PDG_t::kPiMinus) { - isPi = true; - } else if (particle.pdgCode() == PDG_t::kProton || particle.pdgCode() == PDG_t::kProtonBar) { - isPr = true; - } else { - continue; - } - - if (isPrimary && !isDecay && !isMaterial) { - - // Selects particles based on their vertex creation - // This is added to reject loopers - const float vX{particle.vx()}; - const float vY{particle.vy()}; - if (std::hypot(vX, vY) > trackSelections.particleVtxSel) { - continue; - } - - if (isPi && !isPr) { - registry.fill(HIST("dcaVsPtPi"), track.pt(), track.dcaXY(), centrality); - } - if (isPr && !isPi) { - registry.fill(HIST("dcaVsPtPr"), track.pt(), track.dcaXY(), centrality); - } - } - - if (isDecay && !isPrimary && !isMaterial) { - if (isPi && !isPr) { - registry.fill(HIST("dcaVsPtPiDec"), track.pt(), track.dcaXY(), centrality); - } - if (isPr && !isPi) { - registry.fill(HIST("dcaVsPtPrDec"), track.pt(), track.dcaXY(), centrality); - } - } - - if (isMaterial && !isPrimary && !isDecay) { - if (isPi && !isPr) { - registry.fill(HIST("dcaVsPtPiMat"), track.pt(), track.dcaXY(), centrality); - } - if (isPr && !isPi) { - registry.fill(HIST("dcaVsPtPrMat"), track.pt(), track.dcaXY(), centrality); - } - } - } - - // ================ - // Track selection WITH DCAxy and DCAz selections - // Needed for the numerator in the tracking efficiency - // ================ - int nCh{0}; - for (const auto& track : groupedTracks) { - - const bool applyDca{true}; - if (!selectPrimary(track, applyDca)) { - continue; - } - - // Has MC particle? - if (!track.has_mcParticle()) { - continue; - } - - // Get the MC particle - const auto& particle{track.mcParticle()}; - auto charge{0.}; - auto* pdgParticle = pdg->GetParticle(particle.pdgCode()); - if (pdgParticle != nullptr) { - charge = pdgParticle->Charge(); - } else { - continue; - } - - // Is it a charged particle? - if (std::abs(charge) < KminCharge) { - continue; - } - - // Select particle based on its charge - if (trackSelections.signCharge.value == "Positive" && charge < Kzero) { - continue; - } - if (trackSelections.signCharge.value == "Negative" && charge > Kzero) { - continue; - } - - // float phiPrime{track.phi()}; - // phiPrimeFunc(phiPrime, magField, charge); - - int indexEta{-999}; - const float eta{track.eta()}; - for (int i = 0; i < KnEtaHists; ++i) { - if (eta >= kLowEta[i] && eta < kHighEta[i]) { - indexEta = i; - break; - } - } - - if (indexEta < KzeroInt || indexEta > KsevenInt) { - continue; - } - - nCh++; - - registry.fill(HIST("MomentumTPCVsP"), track.p(), track.tpcInnerParam()); - registry.fill(HIST("DCAxyVsPtWithSelection"), track.pt(), track.dcaXY()); - registry.fill(HIST("DCAzVsPtWithSelection"), track.pt(), track.dcaZ()); - registry.fill(HIST("EtaVsPhi"), track.eta(), track.phi()); - registry.fill(HIST("NclVsPhiHist"), track.phi(), track.tpcNClsFound()); - registry.fill(HIST("NclVsPhiProfile"), track.phi(), track.tpcNClsFound()); - registry.fill(HIST("NclVsEta"), track.eta(), track.tpcNClsFound()); - registry.fill(HIST("NclVsEtap"), track.eta(), track.tpcNClsFound()); - registry.fill(HIST("NclVsEtaPID"), track.eta(), track.tpcNClsPID()); - registry.fill(HIST("NclVsEtaPIDp"), track.eta(), track.tpcNClsPID()); - - // Rejects non-physical primary particle - if (!particle.isPhysicalPrimary()) { - continue; - } - - // Selects particles based on their vertex creation - // This is added to reject loopers - const float vX{particle.vx()}; - const float vY{particle.vy()}; - if (std::hypot(vX, vY) > trackSelections.particleVtxSel) { - continue; - } - - bool isPi{false}; - bool isKa{false}; - bool isPr{false}; - - if (particle.pdgCode() == PDG_t::kPiPlus || particle.pdgCode() == PDG_t::kPiMinus) { - isPi = true; - } else if (particle.pdgCode() == PDG_t::kKPlus || particle.pdgCode() == PDG_t::kKMinus) { - isKa = true; - } else if (particle.pdgCode() == PDG_t::kProton || particle.pdgCode() == PDG_t::kProtonBar) { - isPr = true; - } else { - continue; - } - - if (isPi && !isKa && !isPr) { - registry.fill(HIST("PtPiVsCent_WithRecoEvt"), track.pt(), centrality); // Numerator of tracking efficiency - registry.fill(HIST("PtGenPiVsCent_WithRecoEvt"), particle.pt(), centrality); // Numerator of tracking efficiency - registry.fill(HIST("MCclosure_PtPiVsNchMC"), track.pt(), nChMCTPCAcc); - } - if (isKa && !isPi && !isPr) { - registry.fill(HIST("PtKaVsCent_WithRecoEvt"), track.pt(), centrality); - registry.fill(HIST("PtGenKaVsCent_WithRecoEvt"), particle.pt(), centrality); - registry.fill(HIST("MCclosure_PtKaVsNchMC"), track.pt(), nChMCTPCAcc); - } - if (isPr && !isPi && !isKa) { - registry.fill(HIST("PtPrVsCent_WithRecoEvt"), track.pt(), centrality); - registry.fill(HIST("PtGenPrVsCent_WithRecoEvt"), particle.pt(), centrality); - registry.fill(HIST("MCclosure_PtPrVsNchMC"), track.pt(), nChMCTPCAcc); - } - registry.fill(HIST("PtResolution"), particle.pt(), (track.pt() - particle.pt()) / particle.pt()); - } // Loop over reconstructed tracks - registry.fill(HIST("NchVsCent"), centrality, nCh); - } // Loop over Reco. Collisions: Only the collisions with the largest number of contributors - } // If condition: Only simulated evets with at least one reconstrued collision - } - PROCESS_SWITCH(PiKpRAA, processSim, "Process Sim", false); - - void processSignalLoss(aod::McCollisions::iterator const& mccollision, soa::SmallGroups const& collisions, BCsRun3 const& /*bcs*/, aod::FT0s const& /*ft0s*/, aod::McParticles const& mcParticles, TracksMC const& tracksMC) - { - - const auto& kLowEta = (std::vector)vecLowEta; - const auto& kHighEta = (std::vector)vecUpEta; - registry.fill(HIST("EventCounterMC"), 0.5); - - //--------------------------- - // Nch COUNTER IN THE TPC ACCEPTANCE - // ONLY ETA SELECTION IS IMPOSED - //--------------------------- - int nChMCTPCAcc{0}; - for (const auto& particle : mcParticles) { - - auto charge{0.}; - // Get the MC particle - const auto* pdgParticle = pdg->GetParticle(particle.pdgCode()); - if (pdgParticle != nullptr) { - charge = pdgParticle->Charge(); - } else { - continue; - } - - // Is it a charged particle? - if (std::abs(charge) < KminCharge) { - continue; - } - - // Select particle based on its charge - if (trackSelections.signCharge.value == "Positive" && charge < Kzero) { - continue; - } - if (trackSelections.signCharge.value == "Negative" && charge > Kzero) { - continue; - } - - // Is it a primary particle? - if (!particle.isPhysicalPrimary()) { - continue; - } - - // Selects particles based on their vertex creation - // This is added to reject loopers - const float vX{particle.vx()}; - const float vY{particle.vy()}; - if (std::hypot(vX, vY) > trackSelections.particleVtxSel) { - continue; - } - - const float eta{particle.eta()}; - registry.fill(HIST("EtaMCParAllMCColl"), eta); - - if (particle.pdgCode() == PDG_t::kPiPlus || particle.pdgCode() == PDG_t::kPiMinus) { - registry.fill(HIST("EtaMCParAllMCColl_Pion"), eta); - } else if (particle.pdgCode() == PDG_t::kKPlus || particle.pdgCode() == PDG_t::kKMinus) { - registry.fill(HIST("EtaMCParAllMCColl_Kaon"), eta); - } else if (particle.pdgCode() == PDG_t::kProton || particle.pdgCode() == PDG_t::kProtonBar) { - registry.fill(HIST("EtaMCParAllMCColl_Proton"), eta); - } else if (particle.pdgCode() == PDG_t::kElectron || particle.pdgCode() == PDG_t::kPositron) { - registry.fill(HIST("EtaMCParAllMCColl_Electron"), eta); - } else { - registry.fill(HIST("EtaMCParAllMCColl_Rest"), eta); - } - - if (std::abs(eta) < tpcNchAcceptance) { - nChMCTPCAcc++; - } - } - - //--------------------------- - // SELECTS MC EVENTS WITH |ZVTX| < ZVTX_CUT - //--------------------------- - if (isZvtxPosSelMC && (std::fabs(mccollision.posZ()) > posZcut)) { - return; - } - - //--------------------------- - // *) DENOMINATOR OF THE SIGNAL LOSS - //--------------------------- - for (const auto& particle : mcParticles) { - if (particle.eta() < trackSelections.minEta || particle.eta() > trackSelections.maxEta) { - continue; - } - - auto charge{0.}; - // Get the MC particle - auto* pdgParticle = pdg->GetParticle(particle.pdgCode()); - if (pdgParticle != nullptr) { - charge = pdgParticle->Charge(); - } else { - continue; - } - - // Is it a charged particle? - if (std::abs(charge) < KminCharge) { - continue; - } - - // Selects particle based on its charge - if (trackSelections.signCharge.value == "Positive" && charge < Kzero) { - continue; - } - if (trackSelections.signCharge.value == "Negative" && charge > Kzero) { - continue; - } - - // Selects only primary particles - if (!particle.isPhysicalPrimary()) { - continue; - } - - // Selects particles based on their vertex creation - // This is added to reject loopers - const float vX{particle.vx()}; - const float vY{particle.vy()}; - if (std::hypot(vX, vY) > trackSelections.particleVtxSel) { - continue; - } - - if (particle.pdgCode() == PDG_t::kPiPlus || particle.pdgCode() == PDG_t::kPiMinus) { - registry.fill(HIST("PtPiVsNchMC_AllGen"), particle.pt(), nChMCTPCAcc); // DENOMINATOR OF SIGNAL LOSS - } else if (particle.pdgCode() == PDG_t::kKPlus || particle.pdgCode() == PDG_t::kKMinus) { - registry.fill(HIST("PtKaVsNchMC_AllGen"), particle.pt(), nChMCTPCAcc); // DENOMINATOR OF SIGNAL LOSS - } else if (particle.pdgCode() == PDG_t::kProton || particle.pdgCode() == PDG_t::kProtonBar) { - registry.fill(HIST("PtPrVsNchMC_AllGen"), particle.pt(), nChMCTPCAcc); // DENOMINATOR OF SIGNAL LOSS - } else { - continue; - } - } // Loop over Generated Particles - - //--------------------------- - // nChMCTPCAc = CHARGEd PARTICLES WITHIN THE TPC ACCEPTANCE AND WITHOUT PT SELECTION - //--------------------------- - registry.fill(HIST("NchMC_AllGen"), nChMCTPCAcc); // DENOMINATOR OF THE EVENT LOSS - registry.fill(HIST("zPosMCAll"), mccollision.posZ()); - - //--------------------------- - // HOW MANY TIMES WAS THE MC EVENT RECONSTRUED? - //--------------------------- - const auto& nRecColls{collisions.size()}; - registry.fill(HIST("NumOfRecColl"), nRecColls); - - //--------------------------- - // MC EVENTS WITH AT LEAST ONE ASSOCIATED RECONSTRUCTED COLLISION - //--------------------------- - if (nRecColls > KzeroInt) { - - //--------------------------- - // THIS COLLISIONS LOOP ONLY FINDS THE ONE WITH THE LARGEST NUMBER OF CONTRIBUTORS - // IT IS IDENTIFIED BY ITS GLOBAL INDEX (bestCollisionIndex) - // NO EVENT SELECTIONS APPLIED - //--------------------------- - int biggestNContribs{-1}; - int bestCollisionIndex{-1}; - for (const auto& collision : collisions) { - - float centrality{-999.0}; - if (centralitySelector.value == "FT0C") { - centrality = collision.centFT0C(); - } else if (centralitySelector.value == "FT0M") { - centrality = collision.centFT0M(); - } else if (centralitySelector.value == "FV0A") { - centrality = collision.centFV0A(); - } else { - centrality = -999.0; - } - - registry.fill(HIST("NumOfRecCollVsNContri"), nRecColls, collision.numContrib()); - registry.fill(HIST("NumOfRecCollVsCentrality"), nRecColls, centrality); // CENTRALITY FROM THE MULTIPLE TIMES THE MC EVENT WAS RECONSTRUCTED - - //--------------------------- - // GETS THE BESTCOLLISIONINDEX - //--------------------------- - if (biggestNContribs < collision.numContrib()) { - biggestNContribs = collision.numContrib(); - bestCollisionIndex = collision.globalIndex(); - } - - const bool hasft0{collision.has_foundFT0()}; - const bool hastvx{collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)}; - - //--------------------------- - // ALL COLLISIONS WITH N CONTRIBUTORS > 0 - //--------------------------- - registry.fill(HIST("EventCounterMC"), 1.5); - - // NCH IN |ETA| < TPC_ACCEPTANCE - // BELONGING TO A MC EVENT, WHICH POSSIBLY WAS RECONSTRUCTED MULTIPLE TIMES - registry.fill(HIST("NchMC_WithRecColl"), nChMCTPCAcc); - - //--------------------------- - // COLLISIONS WITH FT0 INFO - //--------------------------- - if (hasft0) { - registry.fill(HIST("EventCounterMC"), 2.5); - } - - //--------------------------- - // TVX-triggered collision - //--------------------------- - if (hasft0 && hastvx) { - registry.fill(HIST("EventCounterMC"), 3.5); - } - } - - //--------------------------- - // NO EVENT SELECTIONS APPLIED: ONLY FT0-INFO AND TVX-TRIGGERED REQUIRED - //--------------------------- - for (const auto& collision : collisions) { - - bool isBestCollIndex{false}; - if (bestCollisionIndex == collision.globalIndex()) { - isBestCollIndex = true; - } - - const bool hasft0{collision.has_foundFT0()}; - const bool hastvx{collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)}; - - //--------------------------- - // TVX-triggered collision - //--------------------------- - if (hasft0 && hastvx && isBestCollIndex) { - registry.fill(HIST("EventCounterMC"), 4.5); - } - - // TO CALCULATE NCH IN |ETA| < TPC_ACCEPTANCE - // BELONGING TO A MC EVENT ASSOCIATED WITH - // THE ONLY COLLISION WITH THE LARGEST NUMBER OF CONTRIBUTORS - if (isBestCollIndex) { - registry.fill(HIST("NchMC_BestCollIdx"), nChMCTPCAcc); - } - - //--------------------------- - // LOOPING OVER THE MC TRUTH EVENT - //--------------------------- - for (const auto& particle : mcParticles) { - if (particle.eta() < trackSelections.minEta || particle.eta() > trackSelections.maxEta) { - continue; - } - - auto charge{0.}; - // GET THE MC PARTICLE - auto* pdgParticle = pdg->GetParticle(particle.pdgCode()); - if (pdgParticle != nullptr) { - charge = pdgParticle->Charge(); - } else { - continue; - } - - // IS IT A CHARGED PARTICLE? - if (std::abs(charge) < KminCharge) { - continue; - } - - // SELECTS PARTICLE BASED ON ITS CHARGE - if (trackSelections.signCharge.value == "Positive" && charge < Kzero) { - continue; - } - if (trackSelections.signCharge.value == "Negative" && charge > Kzero) { - continue; - } - - // SELECTS ONLY PRIMARY PARTICLES - if (!particle.isPhysicalPrimary()) { - continue; - } - - // SELECTS PARTICLES BASED ON THEIR VERTEX CREATION - // THIS IS ADDED TO REJECT LOOPERS - const float vX{particle.vx()}; - const float vY{particle.vy()}; - if (std::hypot(vX, vY) > trackSelections.particleVtxSel) { - continue; - } - - // TO COMPUTE SIGNAL LOSS AS THE RATIO BETWEEN MC EVENTS WITH FT0 INFO - // AND THOSE WITH FT0 INFO AND TVX-TRIGGERED - if (particle.pdgCode() == PDG_t::kPiPlus || particle.pdgCode() == PDG_t::kPiMinus) { - if (hasft0) { - registry.fill(HIST("PtPiVsNchMC_HasFT0"), particle.pt(), nChMCTPCAcc); - if (isBestCollIndex) { - registry.fill(HIST("PtPiVsNchMC_HasFT0_BestCollIdx"), particle.pt(), nChMCTPCAcc); - } - } - if (hasft0 && hastvx) { - registry.fill(HIST("PtPiVsNchMC_HasFT0AndTVX"), particle.pt(), nChMCTPCAcc); - if (isBestCollIndex) { - registry.fill(HIST("PtPiVsNchMC_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCTPCAcc); - } - } - } else if (particle.pdgCode() == PDG_t::kKPlus || particle.pdgCode() == PDG_t::kKMinus) { - if (hasft0) { - registry.fill(HIST("PtKaVsNchMC_HasFT0"), particle.pt(), nChMCTPCAcc); - if (isBestCollIndex) { - registry.fill(HIST("PtKaVsNchMC_HasFT0_BestCollIdx"), particle.pt(), nChMCTPCAcc); - } - } - if (hasft0 && hastvx) { - registry.fill(HIST("PtKaVsNchMC_HasFT0AndTVX"), particle.pt(), nChMCTPCAcc); - if (isBestCollIndex) { - registry.fill(HIST("PtKaVsNchMC_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCTPCAcc); - } - } - } else if (particle.pdgCode() == PDG_t::kProton || particle.pdgCode() == PDG_t::kProtonBar) { - if (hasft0) { - registry.fill(HIST("PtPrVsNchMC_HasFT0"), particle.pt(), nChMCTPCAcc); - if (isBestCollIndex) { - registry.fill(HIST("PtPrVsNchMC_HasFT0_BestCollIdx"), particle.pt(), nChMCTPCAcc); - } - } - if (hasft0 && hastvx) { - registry.fill(HIST("PtPrVsNchMC_HasFT0AndTVX"), particle.pt(), nChMCTPCAcc); - if (isBestCollIndex) { - registry.fill(HIST("PtPrVsNchMC_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCTPCAcc); - } - } - } else { - continue; - } - } // LOOP OVER TRUE MC EVENTS - } - - //--------------------------- - // MC EVENTS RECONSTRUCTED MULTIPLE TIMES ARE STORED HERE - // EVENT SELECTION APPLIED MANUALLY TO AVOID DOUBLE FILLING IN THE EVENTCOUNTER - // *) DENOMINATOR OF THE EVENT SPLITTING - // *) NUMERATOR OF THE EVENT LOSS - // *) NUMERATOR OF SIGNAL LOSS - //--------------------------- - for (const auto& collision : collisions) { - - float centrality{-999.0}; - if (centralitySelector.value == "FT0C") { - centrality = collision.centFT0C(); - } else if (centralitySelector.value == "FT0M") { - centrality = collision.centFT0M(); - } else if (centralitySelector.value == "FV0A") { - centrality = collision.centFV0A(); - } else { - centrality = -999.0; - } - - // HAS BC? - if (selHasBC) { - if (!collision.has_foundBC()) { - continue; - } - } - - // HAS FT0 INFORMATION? - if (selHasFT0) { - if (!collision.has_foundFT0()) { - continue; - } - } - - // USE SEL8? - if (useSel8) { - if (!collision.sel8()) { - continue; - } - } - - // KISTRIGGERTVX - if (selTriggerTVX) { - if (!collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { - continue; - } - } - - // kNoITSROFrameBorder - if (selNoITSROFrameBorder) { - if (!collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { - continue; - } - } - - // kNoTimeFrameBorder - if (selNoTimeFrameBorder) { - if (!collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { - continue; - } - } - - // Zvtx - if (isZvtxPosSel) { - if (std::fabs(collision.posZ()) > posZcut) { - continue; - } - } - - // Short distance between FT0 & PV vertices - if (selIsGoodZvtxFT0vsPV) { - if (!collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { - continue; - } - } - - if (selNoSameBunchPileup) { - if (!collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { - continue; - } - } - - if (isCentSel) { - if (centrality < minCentCut || centrality > maxCentCut) { - continue; - } - } - - if (selINELgt0) { - if (!collision.isInelGt0()) { - continue; - } - } - - registry.fill(HIST("NchMCVsCent_AllRecoEvt"), centrality, nChMCTPCAcc); // FOR MAPPING BETWEEN MC NCH AND CENTRALITY - registry.fill(HIST("Centrality_AllRecoEvt"), centrality); // DENOMINATOR OF EVENT SPLITTING - registry.fill(HIST("NchMC_AllRecoEvt"), nChMCTPCAcc); // NUMERATOR OF EVENT LOSS CORRECTION - - for (const auto& particle : mcParticles) { - if (particle.eta() < trackSelections.minEta || particle.eta() > trackSelections.maxEta) { - continue; + for (const auto& particle : mcParticles) { + if (particle.eta() < trackSelections.minEta || particle.eta() > trackSelections.maxEta) { + continue; } auto charge{0.}; @@ -2629,11 +1778,14 @@ struct PiKpRAA { } if (particle.pdgCode() == PDG_t::kPiPlus || particle.pdgCode() == PDG_t::kPiMinus) { - registry.fill(HIST("PtPiVsNchMC_AllRecoEvt"), particle.pt(), nChMCTPCAcc); // NUMERATOR OF SIGNAL LOSS + registry.fill(HIST("PtPiVsNchMC_WithRecoEvt"), particle.pt(), nChMCTPCAcc); // NUMERATOR OF SIGNAL LOSS + registry.fill(HIST("PtPiVsNchMCFwd_WithRecoEvt"), particle.pt(), nChMCForward); // NUMERATOR OF SIGNAL LOSS } else if (particle.pdgCode() == PDG_t::kKPlus || particle.pdgCode() == PDG_t::kKMinus) { - registry.fill(HIST("PtKaVsNchMC_AllRecoEvt"), particle.pt(), nChMCTPCAcc); // NUMERATOR OF SIGNAL LOSS + registry.fill(HIST("PtKaVsNchMC_WithRecoEvt"), particle.pt(), nChMCTPCAcc); // NUMERATOR OF SIGNAL LOSS + registry.fill(HIST("PtKaVsNchMCFwd_WithRecoEvt"), particle.pt(), nChMCForward); // NUMERATOR OF SIGNAL LOSS } else if (particle.pdgCode() == PDG_t::kProton || particle.pdgCode() == PDG_t::kProtonBar) { - registry.fill(HIST("PtPrVsNchMC_AllRecoEvt"), particle.pt(), nChMCTPCAcc); // NUMERATOR OF SIGNAL LOSS + registry.fill(HIST("PtPrVsNchMC_WithRecoEvt"), particle.pt(), nChMCTPCAcc); // NUMERATOR OF SIGNAL LOSS + registry.fill(HIST("PtPrVsNchMCFwd_WithRecoEvt"), particle.pt(), nChMCForward); // NUMERATOR OF SIGNAL LOSS } else { continue; } @@ -2670,7 +1822,6 @@ struct PiKpRAA { } const auto& foundBC = collision.foundBC_as(); - registry.fill(HIST("zPosMC"), mccollision.posZ()); //--------------------------- @@ -2731,11 +1882,14 @@ struct PiKpRAA { //--------------------------- // NEEDED TO CONSTRUCT THE CORRELATION BETWEEN MC NCH V.S. CENTRALITY //--------------------------- - registry.fill(HIST("Centrality_WRecoEvtWSelCri"), centrality); // NUMERATOR OF EVENT SPLITTING - registry.fill(HIST("NchMC_WithRecoEvt"), nChMCTPCAcc); // NUMERATOR OF EVENT LOSS CORRECTION - registry.fill(HIST("NchMCVsCent"), centrality, nChMCTPCAcc); // FOR MAPPING BETWEEN MC NCH AND CENTRALITY - registry.fill(HIST("zPos"), collision.posZ()); // VTX-Z OF RECONSTRUED COLLISIONS + EVENT SELECTION - registry.fill(HIST("Centrality"), centrality); // CENTRALITY OF RECONSTRUED COLLISIONS + EVENT SELECTION + registry.fill(HIST("Centrality_WRecoEvtWSelCri"), centrality); // NUMERATOR OF EVENT SPLITTING + registry.fill(HIST("NchMC_WithRecoEvt_BestCollIdx"), nChMCTPCAcc); // NUMERATOR OF EVENT LOSS CORRECTION + registry.fill(HIST("NchMCVsCent_WithRecoEvt_BestCollIdx"), centrality, nChMCTPCAcc); // FOR MAPPING BETWEEN MC NCH AND CENTRALITY + registry.fill(HIST("zPos"), collision.posZ()); // VTX-Z OF RECONSTRUED COLLISIONS + EVENT SELECTION + registry.fill(HIST("Centrality"), centrality); // CENTRALITY OF RECONSTRUED COLLISIONS + EVENT SELECTION + registry.fill(HIST("Centrality_FineBins_WithRecoEvt_BestCollIdx"), centrality); // CENTRALITY OF RECONSTRUED COLLISIONS + EVENT SELECTION + BESTCOLLISIONINDEX + registry.fill(HIST("EventCounterMC"), 5.5); // COUNTER: ASSOC. REC. COLL + EVT SEL + BESTCOLLISIONINDEX + registry.fill(HIST("NchMC_Fwd_WithRecoEvt_BestCollIdx"), nChMCForward); // Nch MC Forward //--------------------------- // THE GENERATED EVENTS ARE NOT SUBJECTED TO ANY SELECTION CRITERIA @@ -2783,14 +1937,17 @@ struct PiKpRAA { } if (particle.pdgCode() == PDG_t::kPiPlus || particle.pdgCode() == PDG_t::kPiMinus) { - registry.fill(HIST("PtPiVsCentMC_WithRecoEvt"), particle.pt(), centrality); // DENOMINATOR OF TRACKING EFFICIENCY - registry.fill(HIST("PtPiVsNchMC_WithRecoEvt"), particle.pt(), nChMCTPCAcc); // NUMERATOR OF SIGNAL LOSS + registry.fill(HIST("PtPiVsCentMC_WithRecoEvt"), particle.pt(), centrality); // DENOMINATOR OF TRACKING EFFICIENCY + registry.fill(HIST("PtPiVsNchMC_WithRecoEvt_BestCollIdx"), particle.pt(), nChMCTPCAcc); // NUMERATOR OF SIGNAL LOSS + registry.fill(HIST("PtPiVsNchMCFwd_WithRecoEvt_BestCollIdx"), particle.pt(), nChMCForward); // NUMERATOR OF SIGNAL LOSS } else if (particle.pdgCode() == PDG_t::kKPlus || particle.pdgCode() == PDG_t::kKMinus) { registry.fill(HIST("PtKaVsCentMC_WithRecoEvt"), particle.pt(), centrality); - registry.fill(HIST("PtKaVsNchMC_WithRecoEvt"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtKaVsNchMC_WithRecoEvt_BestCollIdx"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtKaVsNchMCFwd_WithRecoEvt_BestCollIdx"), particle.pt(), nChMCForward); // NUMERATOR OF SIGNAL LOSS } else if (particle.pdgCode() == PDG_t::kProton || particle.pdgCode() == PDG_t::kProtonBar) { registry.fill(HIST("PtPrVsCentMC_WithRecoEvt"), particle.pt(), centrality); - registry.fill(HIST("PtPrVsNchMC_WithRecoEvt"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtPrVsNchMC_WithRecoEvt_BestCollIdx"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtPrVsNchMCFwd_WithRecoEvt_BestCollIdx"), particle.pt(), nChMCForward); // NUMERATOR OF SIGNAL LOSS } else { continue; } diff --git a/PWGLF/Tasks/Nuspex/spectraTOF.cxx b/PWGLF/Tasks/Nuspex/spectraTOF.cxx index 6ab1dd8a82d..39e0f6c027e 100644 --- a/PWGLF/Tasks/Nuspex/spectraTOF.cxx +++ b/PWGLF/Tasks/Nuspex/spectraTOF.cxx @@ -1534,10 +1534,9 @@ struct tofSpectra { continue; } // trackwoCut++; - if (std::abs(track.dcaXY()) > 0.05) { // Skipping tracks that don't pass the standard cuts - return; + if (!passesDCAxyCut(track)) { // Skipping tracks that don't pass the standard cuts + continue; } - // trackwCut++; const auto& mcLabel = mcTrackLabels.iteratorAt(track.globalIndex()); const auto& mcParticle = mcParticles.iteratorAt(mcLabel.mcParticleId()); @@ -1562,7 +1561,7 @@ struct tofSpectra { double rapidityKa = track.rapidity(PID::getMass(3)); double rapidityPr = track.rapidity(PID::getMass(4)); if (track.eta() < trkselOptions.cfgCutEtaMin || track.eta() > trkselOptions.cfgCutEtaMax) { - return; + continue; } if (mcParticle.isPhysicalPrimary()) { if (isTPCPion && rapidityPi >= trkselOptions.cfgCutYMin && rapidityPi <= trkselOptions.cfgCutYMax) { @@ -1603,6 +1602,70 @@ struct tofSpectra { } // TOF Selection and Histogram Filling + // RL TOF selections: same cut for TRD as inclusive analysis. + if (!track.hasTOF()) { + continue; + } + + if (trkselOptions.requireTrdOnly && !track.hasTRD()) { + continue; + } + + if (trkselOptions.requireNoTrd && track.hasTRD()) { + continue; + } + + if (track.hasTRD() && trkselOptions.lastRequiredTrdCluster > 0) { + int lastLayer = 0; + for (int layer = 7; layer >= 0; --layer) { + if (track.trdPattern() & (1 << layer)) { + lastLayer = layer; + break; + } + } + + if (lastLayer < trkselOptions.lastRequiredTrdCluster) { + continue; + } + } + + // RL: add an selection in the selection event time + + switch (trkselOptions.selectEvTime) { + case 0: + break; // No selection + case 1: + if (!track.isEvTimeDefined()) { + continue; + } + break; + case 2: + if (!track.isEvTimeTOF()) { + continue; + } + break; + case 3: + if (!track.isEvTimeT0AC()) { + continue; + } + break; + case 4: + if (!track.isEvTimeTOFT0AC()) { + continue; + } + break; + case 5: + if (track.isEvTimeDefined()) { + continue; + } + break; + default: + LOG(fatal) << "Valore selectEvTime non riconosciuto: " + << trkselOptions.selectEvTime; + } + + // + if (isTOFPion && rapidityPi >= trkselOptions.cfgCutYMin && rapidityPi <= trkselOptions.cfgCutYMax) { if (usePDGcode) { if (pdgCode == kPiPlus) { @@ -2149,9 +2212,7 @@ struct tofSpectra { if (!mcParticle.isPhysicalPrimary()) { // Is not physical primary if (mcParticle.getProcess() == kPDecay) { // Is from decay if (includeCentralityMC) { - if (includeCentralityMC) { - histos.fill(HIST(hpt_num_str[i]), track.pt(), multiplicity, track.dcaXY()); - } + histos.fill(HIST(hpt_num_str[i]), track.pt(), multiplicity, track.dcaXY()); } else { histos.fill(HIST(hpt_num_str[i]), track.pt(), multiplicity); } @@ -2686,8 +2747,6 @@ struct tofSpectra { } histos.fill(HIST("MC/Multiplicity"), getMultiplicityMC(mcCollision)); const auto& particlesInCollision = mcParticles.sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); - bool hasParticleInFT0C = false; - bool hasParticleInFT0A = false; if (evselOptions.cfgINELCut.value == EvSelInelGt0Cut) { if (!o2::pwglf::isINELgt0mc(particlesInCollision, pdgDB)) { continue; @@ -2714,9 +2773,6 @@ struct tofSpectra { if (mcCollision.isInelGt1()) { histos.fill(HIST("MC/GenRecoCollisions"), 4.f); } - if (hasParticleInFT0C && hasParticleInFT0A) { - histos.fill(HIST("MC/GenRecoCollisions"), 5.f); - } } } PROCESS_SWITCH(tofSpectra, processMC, "Process MC", false); diff --git a/PWGLF/Tasks/QC/CMakeLists.txt b/PWGLF/Tasks/QC/CMakeLists.txt index e1618a43d06..8185c1c91e4 100644 --- a/PWGLF/Tasks/QC/CMakeLists.txt +++ b/PWGLF/Tasks/QC/CMakeLists.txt @@ -11,6 +11,7 @@ o2physics_add_dpl_workflow(v0cascades-qa SOURCES v0cascadesqa.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) @@ -31,11 +32,13 @@ o2physics_add_dpl_workflow(pid-qa o2physics_add_dpl_workflow(strarecostudy SOURCES straRecoStudy.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::DetectorsBase O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(kfperformancestudy SOURCES kfPerformanceStudy.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::DetectorsBase O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -46,6 +49,7 @@ o2physics_add_dpl_workflow(its-tpc-matching-qa o2physics_add_dpl_workflow(strangeness-tracking-qc SOURCES strangenessTrackingQC.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::Framework O2::ReconstructionDataFormats O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) @@ -62,11 +66,13 @@ o2physics_add_dpl_workflow(lfpropstudy o2physics_add_dpl_workflow(tpc-dedx-postcalibration SOURCES tpc_dEdx_postcalibration.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tpc-dedx-qa SOURCES tpc_dedx_qa.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -87,21 +93,25 @@ o2physics_add_dpl_workflow(strangenessqcpp o2physics_add_dpl_workflow(strangepidqa SOURCES strangepidqa.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stqa SOURCES stqa.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::Framework O2::ReconstructionDataFormats O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(kfstrangenessstudy SOURCES kfStrangenessStudy.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::DetectorsBase O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(findable-study SOURCES findableStudy.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::DetectorsBase O2Physics::AnalysisCore O2Physics::v0SelectionGroup COMPONENT_NAME Analysis) @@ -112,6 +122,7 @@ o2physics_add_dpl_workflow(mcinelgt0 o2physics_add_dpl_workflow(tracked-cascade-properties SOURCES trackedCascadeProperties.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) @@ -122,26 +133,31 @@ o2physics_add_dpl_workflow(mc-particle-predictions o2physics_add_dpl_workflow(strange-derived-qa SOURCES strangederivedqa.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(str-derived-genqa SOURCES strderivedGenQA.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(its-tpc-matching-vzeros SOURCES lfITSTPCMatchingSecondaryTracksQA.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(v0assoqa SOURCES v0assoqa.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter KFParticle::KFParticle O2Physics::TPCDriftManager COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(systematics-mapping SOURCES systematicsMapping.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) diff --git a/PWGLF/Tasks/QC/resonanceqa.cxx b/PWGLF/Tasks/QC/resonanceqa.cxx index c30eba9fb9b..aae477086e9 100644 --- a/PWGLF/Tasks/QC/resonanceqa.cxx +++ b/PWGLF/Tasks/QC/resonanceqa.cxx @@ -264,7 +264,7 @@ struct resonanceqa { ConfigurableAxis axisVertex{"axisVertex", {20, -10, 10}, "vertex axis for bin"}; ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0.0, 2.750, 5.250, 7.750, 12.750, 17.750, 22.750, 27.750, 32.750, 37.750, 42.750, 47.750, 52.750, 57.750, 62.750, 67.750, 72.750, 77.750, 82.750, 87.750, 92.750, 97.750, 250.1}, "multiplicity axis for histograms"}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{axisVertex, axisMultiplicity}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{axisVertex, axisMultiplicity}}; SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, &cache}; void processSameEvent(EventCandidates::iterator const& collision, TrackCandidates const& tracks, aod::BCs const&) { diff --git a/PWGLF/Tasks/QC/straRecoStudy.cxx b/PWGLF/Tasks/QC/straRecoStudy.cxx index b191f182677..4eddea21f14 100644 --- a/PWGLF/Tasks/QC/straRecoStudy.cxx +++ b/PWGLF/Tasks/QC/straRecoStudy.cxx @@ -45,6 +45,7 @@ #include #include +#include #include #include #include @@ -328,7 +329,7 @@ struct straRecoStudy { kEvSelVtxZ, kEvSelAllSteps }; - std::array evselstats; + std::array evselstats; void resetCounters() { diff --git a/PWGLF/Tasks/Resonances/CMakeLists.txt b/PWGLF/Tasks/Resonances/CMakeLists.txt index 06d226e887c..3105aa36c43 100644 --- a/PWGLF/Tasks/Resonances/CMakeLists.txt +++ b/PWGLF/Tasks/Resonances/CMakeLists.txt @@ -79,6 +79,11 @@ o2physics_add_dpl_workflow(k1analysismicro PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(k1-training-table + SOURCES k1TrainingTable.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(phianalysisrun3 SOURCES phianalysisrun3.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore @@ -94,6 +99,11 @@ o2physics_add_dpl_workflow(f0980pbpbanalysis PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(f0980-flow-sp + SOURCES f0980FlowSP.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(lambda1520spherocityanalysis SOURCES lambda1520SpherocityAnalysis.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore @@ -134,6 +144,11 @@ o2physics_add_dpl_workflow(kshortkshort PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(k0s-k0s-flow + SOURCES k0sK0sFlow.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(phipbpb SOURCES phipbpb.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore @@ -184,6 +199,11 @@ o2physics_add_dpl_workflow(omega2012analysis PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(omega2012-training-table + SOURCES omega2012TrainingTable.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(xi1820analysis SOURCES xi1820Analysis.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore @@ -254,8 +274,8 @@ o2physics_add_dpl_workflow(double-resonance-scan PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(kstarinoo - SOURCES kstarInOO.cxx +o2physics_add_dpl_workflow(kstarinjets + SOURCES kstarInJets.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -316,7 +336,7 @@ o2physics_add_dpl_workflow(k892hadronphoton o2physics_add_dpl_workflow(k892hadronphotonbkg SOURCES k892hadronphotonBkg.cxx - PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(chk892li @@ -353,3 +373,13 @@ o2physics_add_dpl_workflow(hresonancecorrelation SOURCES hResonanceCorrelation.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(phiphi-mc-injected + SOURCES phiphimcinjected.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(phiphi-mc-trigger + SOURCES phiphimctrigger.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGLF/Tasks/Resonances/cha01710analysis.cxx b/PWGLF/Tasks/Resonances/cha01710analysis.cxx index 8a75cd96a48..6a23c0dec06 100644 --- a/PWGLF/Tasks/Resonances/cha01710analysis.cxx +++ b/PWGLF/Tasks/Resonances/cha01710analysis.cxx @@ -19,6 +19,7 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseITS.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/Qvectors.h" @@ -37,6 +38,7 @@ #include #include #include +#include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include @@ -70,6 +72,8 @@ struct Cha01710analysis { Service ccdb{}; + o2::aod::ITSResponse itsResponse; + Configurable cfgUrl{"cfgUrl", "http://alice-ccdb.cern.ch", "CCDB URL"}; struct : ConfigurableGroup { @@ -93,10 +97,15 @@ struct Cha01710analysis { Configurable cfgV0DaughterPtMin{"cfgV0DaughterPtMin", 0.1f, "minimum daughter pT"}; Configurable cfgV0DaughterTPCNClsMin{"cfgV0DaughterTPCNClsMin", 70, "minimum daughter TPC clusters"}; Configurable cfgV0DaughterTPCNSigmaPiMax{"cfgV0DaughterTPCNSigmaPiMax", 5.f, "maximum daughter |TPC nSigma(pi)|"}; + Configurable cfgApplyV0DaughterTOFPID{"cfgApplyV0DaughterTOFPID", false, "Apply pion TOF PID to V0 daughters with TOF"}; Configurable cfgV0DaughterTOFNSigmaPiMax{"cfgV0DaughterTOFNSigmaPiMax", 5.f, "maximum daughter |TOF nSigma(pi)|"}; Configurable cfgKsMassWindow{"cfgKsMassWindow", 0.01, "K short mass window"}; Configurable cfgRejectLambda{"cfgRejectLambda", true, "reject Lambda/anti-Lambda competitors"}; Configurable cfgLambdaMassWindow{"cfgLambdaMassWindow", 0.005f, "Lambda rejection window (GeV/c2)"}; + Configurable cfgApplyArmenterosCut{"cfgApplyArmenterosCut", false, "Apply Armenteros-Podolanski cut"}; + Configurable cfgArmenterosCut{"cfgArmenterosCut", 0.2f, "Minimum qT / |alpha|"}; + Configurable cfgApplyV0DaughterDeltaR{"cfgApplyV0DaughterDeltaR", false, "Apply minimum deltaR between V0 daughters"}; + Configurable cfgV0DaughterDeltaRMin{"cfgV0DaughterDeltaRMin", 0.001f, "Minimum deltaR between pion daughters"}; } v0Cuts; struct : ConfigurableGroup { @@ -110,6 +119,8 @@ struct Cha01710analysis { Configurable cfgDcaZMax{"cfgDcaZMax", 2.f, "maximum |DCAz| (cm)"}; Configurable cfgTPCNSigmaMax{"cfgTPCNSigmaMax", 3.f, "maximum |TPC nSigma(K)| without TOF"}; Configurable cfgCombinedNSigmaMax{"cfgCombinedNSigmaMax", 3.f, "maximum combined TPC-TOF nSigma(K)"}; + Configurable cfgPIDITSFlag{"cfgPIDITSFlag", true, "PID ITS flag"}; + Configurable cfgPIDMaxITSnSigmaKaon{"cfgPIDMaxITSnSigmaKaon", 3.0, "ITS nSigma selection"}; } kaonCuts; struct : ConfigurableGroup { @@ -191,6 +202,10 @@ struct Cha01710analysis { histos.add("Kaon/hTPCNSigma", "charged kaon TPC PID", HistType::kTH2F, {cfgAxisPt, {120, -6., 6.}}); histos.add("Kaon/hTOFNSigma", "charged kaon TOF PID", HistType::kTH2F, {cfgAxisPt, {120, -6., 6.}}); + histos.add("Kaon/hTPCNSigmaSelected", "charged kaon TPC PID", HistType::kTH2F, {cfgAxisPt, {120, -6., 6.}}); + histos.add("Kaon/hTOFNSigmaSelected", "charged kaon TOF PID", HistType::kTH2F, {cfgAxisPt, {120, -6., 6.}}); + histos.add("Kaon/hITSNSigma", "charged kaon ITS PID", HistType::kTH2F, {cfgAxisPt, {120, -6., 6.}}); + histos.add("Kaon/hITSNSigmaSelected", "charged kaon ITS PID", HistType::kTH2F, {cfgAxisPt, {120, -6., 6.}}); histos.add("Pair/hSignalPlus", "K0S K+ same-event versus EP", HistType::kTHnSparseF, {cfgAxisMass, cfgAxisPt, cfgAxisCent, cfgAxisEP}); histos.add("Pair/hSignalMinus", "K0S K- same-event versus EP", HistType::kTHnSparseF, {cfgAxisMass, cfgAxisPt, cfgAxisCent, cfgAxisEP}); @@ -252,6 +267,9 @@ struct Cha01710analysis { if (!track.isPVContributor() || !track.isGlobalTrackWoDCA() || !track.isPrimaryTrack()) { return false; } + if (track.pt() < kaonCuts.cfgPtMin || std::abs(track.eta()) > kaonCuts.cfgEtaMax) { + return false; + } if (track.tpcNClsCrossedRows() < kaonCuts.cfgTPCCrossedRowsMin || track.tpcCrossedRowsOverFindableCls() < kaonCuts.cfgCrossedRowsRatioMin || track.tpcChi2NCl() > kaonCuts.cfgTPCChi2Max || track.itsChi2NCl() > kaonCuts.cfgITSChi2Max || @@ -261,15 +279,46 @@ struct Cha01710analysis { histos.fill(HIST("Kaon/hTPCNSigma"), track.pt(), track.tpcNSigmaKa()); if (track.hasTOF()) { histos.fill(HIST("Kaon/hTOFNSigma"), track.pt(), track.tofNSigmaKa()); - return std::hypot(track.tpcNSigmaKa(), track.tofNSigmaKa()) < kaonCuts.cfgCombinedNSigmaMax; } - return std::abs(track.tpcNSigmaKa()) < kaonCuts.cfgTPCNSigmaMax; + histos.fill(HIST("Kaon/hITSNSigma"), track.pt(), itsResponse.nSigmaITS(track)); + + Configurable cfgPIDITSFlag{"cfgPIDITSFlag", true, "PID ITS flag"}; + Configurable cfgPIDMaxITSnSigmaKaon{"cfgPIDMaxITSnSigmaKaon", 3.0, "ITS nSigma selection"}; + + if (track.hasTOF()) { + if (std::hypot(track.tpcNSigmaKa(), track.tofNSigmaKa()) > kaonCuts.cfgCombinedNSigmaMax) { + return false; + } + if (kaonCuts.cfgPIDITSFlag && std::abs(itsResponse.nSigmaITS(track)) > kaonCuts.cfgPIDMaxITSnSigmaKaon) { + return false; + } + return true; + } + if (std::abs(track.tpcNSigmaKa()) > kaonCuts.cfgTPCNSigmaMax) { + return false; + } + if (kaonCuts.cfgPIDITSFlag && std::abs(itsResponse.nSigmaITS(track)) > kaonCuts.cfgPIDMaxITSnSigmaKaon) { + return false; + } + return true; } template bool selectPionDaughter(T const& track) { - return !(track.tpcNClsFound() < v0Cuts.cfgV0DaughterTPCNClsMin || track.pt() < v0Cuts.cfgV0DaughterPtMin || std::abs(track.eta()) > v0Cuts.cfgV0DaughterEtaMax || std::abs(track.tpcNSigmaPi()) > v0Cuts.cfgV0DaughterTPCNSigmaPiMax); + if (track.tpcNClsFound() < v0Cuts.cfgV0DaughterTPCNClsMin) { + return false; + } + if (track.pt() < v0Cuts.cfgV0DaughterPtMin) { + return false; + } + if (std::abs(track.eta()) > v0Cuts.cfgV0DaughterEtaMax) { + return false; + } + if (std::abs(track.tpcNSigmaPi()) > v0Cuts.cfgV0DaughterTPCNSigmaPiMax) { + return false; + } + return true; } template @@ -288,15 +337,44 @@ struct Cha01710analysis { std::abs(v0.mAntiLambda() - constants::physics::MassLambda0) < v0Cuts.cfgLambdaMassWindow)) { return V0MassRegion::kReject; } + if (v0Cuts.cfgApplyArmenterosCut && v0.qtarm() < v0Cuts.cfgArmenterosCut) { + return V0MassRegion::kReject; + } + auto pos = v0.template posTrack_as(); auto neg = v0.template negTrack_as(); if (!selectPionDaughter(pos) || !selectPionDaughter(neg)) { return V0MassRegion::kReject; } + if (v0Cuts.cfgApplyV0DaughterTOFPID) { + if (!v0.positiveHasTOF()) { + return V0MassRegion::kReject; + } + if (!v0.negativeHasTOF()) { + return V0MassRegion::kReject; + } + if (std::abs(v0.tofNSigmaK0PiPlus()) > v0Cuts.cfgV0DaughterTOFNSigmaPiMax) { + return V0MassRegion::kReject; + } + if (std::abs(v0.tofNSigmaK0PiMinus()) > v0Cuts.cfgV0DaughterTOFNSigmaPiMax) { + return V0MassRegion::kReject; + } + } + ROOT::Math::PxPyPzMVector k0(v0.px(), v0.py(), v0.pz(), constants::physics::MassK0Short); if (std::abs(k0.Rapidity()) > v0Cuts.cfgV0RapidityMax) { return V0MassRegion::kReject; } + + if (v0Cuts.cfgApplyV0DaughterDeltaR) { + float dEta = pos.eta() - neg.eta(); + float dPhi = TVector2::Phi_mpi_pi(pos.phi() - neg.phi()); + float deltaR = std::hypot(dEta, dPhi); + if (deltaR < v0Cuts.cfgV0DaughterDeltaRMin) { + return V0MassRegion::kReject; + } + } + float dm = std::abs(v0.mK0Short() - constants::physics::MassK0Short); histos.fill(HIST("V0/hMassSelected"), v0.mK0Short(), v0.pt()); @@ -339,6 +417,9 @@ struct Cha01710analysis { if (track.globalIndex() == pos.globalIndex() || track.globalIndex() == neg.globalIndex() || !selectKaon(track)) { continue; } + histos.fill(HIST("Kaon/hTPCNSigmaSelected"), track.pt(), track.tpcNSigmaKa()); + histos.fill(HIST("Kaon/hTOFNSigmaSelected"), track.pt(), track.tofNSigmaKa()); + ROOT::Math::PxPyPzMVector kaon(track.px(), track.py(), track.pz(), constants::physics::MassKaonCharged); auto mother = k0 + kaon; if (std::abs(mother.Rapidity()) > cfgMotherRapidityMax) { diff --git a/PWGLF/Tasks/Resonances/chargedkstaranalysis.cxx b/PWGLF/Tasks/Resonances/chargedkstaranalysis.cxx index 2020bacdaa4..364b677d2a0 100644 --- a/PWGLF/Tasks/Resonances/chargedkstaranalysis.cxx +++ b/PWGLF/Tasks/Resonances/chargedkstaranalysis.cxx @@ -1328,7 +1328,7 @@ struct Chargedkstaranalysis { PROCESS_SWITCH(Chargedkstaranalysis, processDataSE, "Process Event for data without Partitioning", true); using BinningTypeVertexContributor = ColumnBinningPolicy; - BinningTypeVertexContributor binningOnPositions{{axisCfgs.cfgvtxbins, axisCfgs.cfgmultbins}, true}; + BinningTypeVertexContributor binningOnPositions{{axisCfgs.cfgvtxbins, axisCfgs.cfgmultbins}}; Pair pair{binningOnPositions, nEvtMixing, -1, &cache}; void processDataME(EventCandidates const& /*collisions*/, TrackCandidates const& /*tracks*/, V0Candidates const& /*V0s*/) diff --git a/PWGLF/Tasks/Resonances/cksspinalignder.cxx b/PWGLF/Tasks/Resonances/cksspinalignder.cxx index 0570217b7e0..55456c551a1 100644 --- a/PWGLF/Tasks/Resonances/cksspinalignder.cxx +++ b/PWGLF/Tasks/Resonances/cksspinalignder.cxx @@ -384,7 +384,7 @@ struct cksspinalignder { aod::KShortTracks const& k0sTracks, aod::PionTracks const& pionTracks) { - BinningType colBinning{{cfgVtxBins, cfgCentBins}, true}; + BinningType colBinning{{cfgVtxBins, cfgCentBins}}; for (const auto& [collision1, collision2] : selfCombinations(colBinning, nEvtMixing.value, -1, collisions, collisions)) { diff --git a/PWGLF/Tasks/Resonances/deltaAnalysis.cxx b/PWGLF/Tasks/Resonances/deltaAnalysis.cxx index 0d39c167ce7..81f28e2b513 100644 --- a/PWGLF/Tasks/Resonances/deltaAnalysis.cxx +++ b/PWGLF/Tasks/Resonances/deltaAnalysis.cxx @@ -94,6 +94,7 @@ struct DeltaAnalysis { Configurable cfgRequireRecoINELgt0{"cfgRequireRecoINELgt0", true, "Require reconstructed INEL>0 event class (at least one PV track in |eta|<1)"}; Configurable cfgUseNoSameBunchPileupCut{"cfgUseNoSameBunchPileupCut", false, "Apply kNoSameBunchPileup event selection"}; Configurable cfgUseGoodZvtxFT0vsPVCut{"cfgUseGoodZvtxFT0vsPVCut", false, "Apply kIsGoodZvtxFT0vsPV event selection"}; + Configurable cfgUseGoodITSLayersAll{"cfgUseGoodITSLayersAll", false, "Apply kIsGoodITSLayersAll event selection"}; Configurable applyOccupancyInTimeRangeCut{"applyOccupancyInTimeRangeCut", false, "Apply occupancy-in-time-range cut"}; Configurable cfgOccupancyMin{"cfgOccupancyMin", 0, "Minimum track occupancy in time range"}; Configurable cfgOccupancyMax{"cfgOccupancyMax", 9999, "Maximum track occupancy in time range"}; @@ -101,6 +102,7 @@ struct DeltaAnalysis { Configurable cfgCentMin{"cfgCentMin", 0.f, "Minimum centrality percentile"}; Configurable cfgCentMax{"cfgCentMax", 100.f, "Maximum centrality percentile"}; Configurable cfgUseMCTruthCentrality{"cfgUseMCTruthCentrality", false, "Use MC truth centrality for generated Delta histograms"}; + Configurable cfgApplyMCTruthINELgt0{"cfgApplyMCTruthINELgt0", true, "Apply truth-level INEL>0 selection to MC generated events"}; } evSel; struct : ConfigurableGroup { @@ -125,7 +127,7 @@ struct DeltaAnalysis { Configurable applyTPCChi2NClCut{"applyTPCChi2NClCut", true, "Enable/disable maximum TPC chi2/NCl cut"}; Configurable applyITSChi2NClCut{"applyITSChi2NClCut", true, "Enable/disable maximum ITS chi2/NCl cut"}; Configurable requirePrimaryTrack{"requirePrimaryTrack", true, "Require isPrimaryTrack flag"}; - Configurable requireGlobalTrackNoDCA{"requireGlobalTrackNoDCA", true, "Require isGlobalTrackWoDCA flag"}; + Configurable requireGlobalTrack{"requireGlobalTrack", true, "Require global track"}; Configurable requirePVContributor{"requirePVContributor", true, "Require PV-contributor flag"}; } trackCuts; @@ -135,6 +137,10 @@ struct DeltaAnalysis { Configurable> protonMaxDCAxyPerPtBin{"protonMaxDCAxyPerPtBin", {0.10f, 0.08f, 0.05f, 0.05f}, "Max |DCAxy| for proton per pT bin [cm]"}; Configurable> pionDCAPtBinEdges{"pionDCAPtBinEdges", {0.0f, 0.5f, 1.0f, 2.0f, 1000.f}, "Pion pT bin edges for DCAxy cut [GeV/c]"}; Configurable> pionMaxDCAxyPerPtBin{"pionMaxDCAxyPerPtBin", {0.20f, 0.15f, 0.10f, 0.08f}, "Max |DCAxy| for pion per pT bin [cm]"}; + Configurable isApplyPtDepDCAxyCut{"isApplyPtDepDCAxyCut", false, "Use continuous pT-dependent DCAxy cut"}; + Configurable dcaXYConst{"dcaXYConst", 0.0105f, "Constant term of pT-dependent DCAxy cut [cm]"}; + Configurable dcaXYPtCoeff{"dcaXYPtCoeff", 0.035f, "pT-dependent coefficient of DCAxy cut [cm]"}; + Configurable dcaXYPtExponent{"dcaXYPtExponent", 1.1f, "pT exponent of DCAxy cut"}; } dcaCuts; struct : ConfigurableGroup { @@ -155,6 +161,7 @@ struct DeltaAnalysis { Configurable> protonTPCNSigmaCutPerBin{"protonTPCNSigmaCutPerBin", {5.f, 3.5f, 2.5f}, "Maximum TPC nSigma for proton per momentum bin"}; Configurable> protonTOFPIDMomentumBins{"protonTOFPIDMomentumBins", {0.f, 999.f}, "Proton TOF PID momentum bin edges [GeV/c]"}; Configurable> protonTOFNSigmaCutPerBin{"protonTOFNSigmaCutPerBin", {3.0f}, "Maximum TOF nSigma for proton per momentum bin"}; + Configurable shiftInNsigmaTOFProton{"shiftInNsigmaTOFProton", 0.0f, "Shift applied to proton TOF nSigma"}; } protonPID; struct : ConfigurableGroup { @@ -169,6 +176,7 @@ struct DeltaAnalysis { Configurable> pionTPCNSigmaCutPerBin{"pionTPCNSigmaCutPerBin", {5.f, 3.5f, 2.5f}, "Maximum TPC nSigma for pion per momentum bin"}; Configurable> pionTOFPIDMomentumBins{"pionTOFPIDMomentumBins", {0.f, 999.f}, "Pion TOF PID momentum bin edges [GeV/c]"}; Configurable> pionTOFNSigmaCutPerBin{"pionTOFNSigmaCutPerBin", {3.0f}, "Maximum TOF nSigma for pion per momentum bin"}; + Configurable shiftInNsigmaTOFPion{"shiftInNsigmaTOFPion", 0.0f, "Shift applied to pion TOF nSigma"}; } pionPID; struct : ConfigurableGroup { @@ -238,15 +246,15 @@ struct DeltaAnalysis { mPionDCAPtEdges = dcaCuts.pionDCAPtBinEdges.value; mPionMaxDCAxy = dcaCuts.pionMaxDCAxyPerPtBin.value; - const AxisSpec ptAxis{200, 0., 10., "p_{T} (GeV/c)"}; - const AxisSpec massAxis{trackCuts.numberOfInvMassBins, 1.0, 8.0, "M_{inv} (GeV/#it{c}^{2})"}; + const AxisSpec ptAxis{50, 0., 20., "p_{T} (GeV/c)"}; + const AxisSpec massAxis{trackCuts.numberOfInvMassBins, 1.0, 6.0, "M_{inv} (GeV/#it{c}^{2})"}; const AxisSpec centAxis{axes.cfgCentAxis, "Centrality (%)"}; const AxisSpec vtxAxis{axes.cfgVtxAxis, "Vertex z [cm]"}; const AxisSpec rapAxis{axes.cfgRapAxis, "Rapidity y"}; - const AxisSpec nSigmaTPCaxis{100, -10., 10., "n#sigma^{TPC}"}; - const AxisSpec nSigmaTOFaxis{100, -10., 10., "n#sigma^{TOF}"}; - const AxisSpec momentumAxis{200, 0., 10., "p (GeV/#it{c})"}; - const AxisSpec ptForPIDAxis{200, 0., 10., "p_{T} (GeV/#it{c})"}; + const AxisSpec nSigmaTPCaxis{100, -5., 5., "n#sigma^{TPC}"}; + const AxisSpec nSigmaTOFaxis{100, -5., 5., "n#sigma^{TOF}"}; + const AxisSpec momentumAxis{50, 0., 20., "p (GeV/#it{c})"}; + const AxisSpec ptForPIDAxis{50, 0., 20., "p_{T} (GeV/#it{c})"}; const AxisSpec dcaXYaxis{200, -0.2f, 0.2f, "DCA_{xy} (cm)"}; const AxisSpec dcaZaxis{200, -1.2f, 1.2f, "DCA_{z} (cm)"}; const AxisSpec tpcClusAxis{200, 0, 200, "N_{clusters}^{TPC}"}; @@ -432,10 +440,7 @@ struct DeltaAnalysis { histos.add("MCRecoEvent/centralitydistribution", "Centrality distribution (MC);vCentFT0M;Entries", kTH1F, {centAxis}); } - // ── MC reconstructed event mixing: histograms (gated by the dedicated MC mixing switch, - // independent from doprocessMC / doprocessMixedEvent). Mirrors the DATA EM histograms - // one-to-one but lives under its own AnalysisMCReco / THnSparseMCReco keys so downstream - // scripts can never confuse DATA-EM with MCReco-EM. ───────────────────────────────────── + // ── MC reconstructed event mixing: histograms (gated by the dedicated MC mixing switch) if (mixingCfg.enableMCEventMixing) { histos.add("AnalysisMCReco/hDeltaPlusPlusInvMassEM", "#Delta^{++} invariant mass - MC reconstructed event mixing", kTH2F, {ptAxis, massAxis}); histos.add("AnalysisMCReco/hAntiDeltaPlusPlusInvMassEM", "#bar{#Delta}^{++} invariant mass - MC reconstructed event mixing", kTH2F, {ptAxis, massAxis}); @@ -447,7 +452,6 @@ struct DeltaAnalysis { histos.add("THnSparseMCReco/hDeltaZeroEM", "THnSparse #Delta^{0} MC reconstructed event mixing", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); histos.add("THnSparseMCReco/hAntiDeltaZeroEM", "THnSparse #bar{#Delta}^{0} MC reconstructed event mixing", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); - // Minimal debug QA: verifies MC mixing actually ran (non-zero when enabled and MC data flows through it). histos.add("QAMC/EventMixing/hMixedEventPairs", "Number of MC mixed-event collision pairs processed", kTH1F, {{1, 0.5f, 1.5f}}); histos.add("QAMC/EventMixing/hMixedEventPairsByCentrality", "MC mixed-event collision pairs vs centrality", kTH1F, {centAxis}); } @@ -472,9 +476,6 @@ struct DeltaAnalysis { histos.add("MCGenQA/hGenPionAntiDeltaPlusPlus", "Gen pion from #bar{#Delta}^{++}", kTH1F, {ptAxis}); histos.add("MCGenQA/hGenPionDeltaZero", "Gen pion from #Delta^{0}", kTH1F, {ptAxis}); histos.add("MCGenQA/hGenPionAntiDeltaZero", "Gen pion from #bar{#Delta}^{0}", kTH1F, {ptAxis}); - - // Generated-Delta cut flow. 0=All generated, 1=producedByGenerator, 2=truth vertex-z, - // 3=truth INEL>0, 4=PDG, 5=rapidity, 6=decay channel, 7=final generated histogram. histos.add("MCGenQA/hGenDeltaCutFlow", "Generated #Delta cut flow", kTH1F, {{8, -0.5f, 7.5f}}); histos.add("MCGen/GeneratedDelta_EventAccepted/hDeltaPlusPlusInvMass", "#Delta^{++} generated, event-accepted", kTH2F, {ptAxis, massAxis}); @@ -497,9 +498,6 @@ struct DeltaAnalysis { } // ── Event-level cut-flow (QA-only) histograms ─────────────────────────────────────────── - // These are pure bookkeeping: they record how many events survive each existing selection - // step (already implemented in passesEventSelectionImpl(), isTruthInelGt0(), and - // processEventFactor()) and do not themselves apply, add, or alter any selection. { histos.add("CutFlow/Data/hEventCutFlow", "Data event cut flow", kTH1F, {{9, -0.5f, 8.5f}}); auto hDataCutFlow = histos.get(HIST("CutFlow/Data/hEventCutFlow")); @@ -622,8 +620,6 @@ struct DeltaAnalysis { return collision.multNTracksPVeta1() > 0; } - // Truth-level charged-primary identification, used only to build the truth-level INEL>0 event - // class (isTruthInelGt0 below). template bool isChargedPrimaryMC(McParticleType const& mcPart) { @@ -637,8 +633,6 @@ struct DeltaAnalysis { return std::abs(pdgParticle->Charge()) > MinAbsCharge; } - // Truth-level analogue of isRecoInelGt0() above - at least one charged primary within - // |eta| < 1 among the generated particles of a given MC collision. template bool isTruthInelGt0(McParticleTableType const& mcParticlesInCollision) { @@ -710,6 +704,11 @@ struct DeltaAnalysis { } fillEventCutFlowBin(7.f); // GoodZvtxFT0vsPV + if (evSel.cfgUseGoodITSLayersAll && + !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + return false; + } + fillEventCutFlowBin(8.f); // Final accepted event return true; } @@ -755,7 +754,7 @@ struct DeltaAnalysis { if (trackCuts.requirePrimaryTrack && !track.isPrimaryTrack()) { return false; } - if (trackCuts.requireGlobalTrackNoDCA && !track.isGlobalTrackWoDCA()) { + if (trackCuts.requireGlobalTrack && !track.isGlobalTrack()) { return false; } if (trackCuts.requirePVContributor && !track.isPVContributor()) { @@ -767,6 +766,14 @@ struct DeltaAnalysis { template bool passesProtonDCASelection(TrackType const& track) { + if (dcaCuts.isApplyPtDepDCAxyCut) { + const float dcaConst = dcaCuts.dcaXYConst; + const float dcaCoeff = dcaCuts.dcaXYPtCoeff; + const float dcaExp = dcaCuts.dcaXYPtExponent; + const float maxDCAxy = dcaConst + dcaCoeff / std::pow(track.pt(), dcaExp); + return (std::abs(track.dcaXY()) < maxDCAxy) && (std::abs(track.dcaZ()) < dcaCuts.cfgCutDCAz); + } + const int nBins = static_cast(mProtonDCAPtEdges.size()) - 1; const float pt = track.pt(); bool passed = false; @@ -783,6 +790,14 @@ struct DeltaAnalysis { template bool passesPionDCASelection(TrackType const& track) { + if (dcaCuts.isApplyPtDepDCAxyCut) { + const float dcaConst = dcaCuts.dcaXYConst; + const float dcaCoeff = dcaCuts.dcaXYPtCoeff; + const float dcaExp = dcaCuts.dcaXYPtExponent; + const float maxDCAxy = dcaConst + dcaCoeff / std::pow(track.pt(), dcaExp); + return (std::abs(track.dcaXY()) < maxDCAxy) && (std::abs(track.dcaZ()) < dcaCuts.cfgCutDCAz); + } + const int nBins = static_cast(mPionDCAPtEdges.size()) - 1; const float pt = track.pt(); bool passed = false; @@ -831,7 +846,8 @@ struct DeltaAnalysis { } // Circular mode: joint TPC+TOF nSigma radius. - const float combinedNSigPr = tpcNSigPr * tpcNSigPr + tofNSigPr * tofNSigPr; + const float shiftedTofNSigPr = track.tofNSigmaPr() - static_cast(protonPID.shiftInNsigmaTOFProton); + const float combinedNSigPr = tpcNSigPr * tpcNSigPr + shiftedTofNSigPr * shiftedTofNSigPr; const float combinedNSigPi = tpcNSigPi * tpcNSigPi + tofNSigPi * tofNSigPi; const auto circularCutSq = static_cast(protonPID.combinedNSigmaCutProton * protonPID.combinedNSigmaCutProton); const float circularVetoCutSq = pidShared.tpcNSigmaVetoThreshold * pidShared.tpcNSigmaVetoThreshold + @@ -888,7 +904,8 @@ struct DeltaAnalysis { } // Circular mode: joint TPC+TOF nSigma radius. - const float combinedNSigPi = tpcNSigPi * tpcNSigPi + tofNSigPi * tofNSigPi; + const float shiftedTofNSigPi = track.tofNSigmaPi() - static_cast(pionPID.shiftInNsigmaTOFPion); + const float combinedNSigPi = tpcNSigPi * tpcNSigPi + shiftedTofNSigPi * shiftedTofNSigPi; const float combinedNSigPr = tpcNSigPr * tpcNSigPr + tofNSigPr * tofNSigPr; const auto circularCutSq = static_cast(pionPID.combinedNSigmaCutPion * pionPID.combinedNSigmaCutPion); const float circularVetoCutSq = pidShared.tpcNSigmaVetoThreshold * pidShared.tpcNSigmaVetoThreshold + @@ -928,7 +945,7 @@ struct DeltaAnalysis { histos.fill(HIST("QAafter/Proton/tpcCrossedRowsVsPt"), pt, track.tpcNClsCrossedRows()); histos.fill(HIST("QAafter/Proton/tpcClustersFoundVsPt"), pt, track.tpcNClsFound()); if (!pidShared.useTPCOnlyPID && track.hasTOF()) { - const float tofNSigPr = track.tofNSigmaPr(); + const float tofNSigPr = track.tofNSigmaPr() - static_cast(protonPID.shiftInNsigmaTOFProton); histos.fill(HIST("QAafter/Proton/tofNSigmaVsMomentum"), totalMomentum, tofNSigPr); histos.fill(HIST("QAafter/Proton/tofNSigmaVsPt"), pt, tofNSigPr); histos.fill(HIST("QAafter/Proton/tofNSigmaVsCentrality"), centralityPercent, tofNSigPr); @@ -954,7 +971,7 @@ struct DeltaAnalysis { histos.fill(HIST("QAafter/Pion/tpcCrossedRowsVsPt"), pt, track.tpcNClsCrossedRows()); histos.fill(HIST("QAafter/Pion/tpcClustersFoundVsPt"), pt, track.tpcNClsFound()); if (!pidShared.useTPCOnlyPID && track.hasTOF()) { - const float tofNSigPi = track.tofNSigmaPi(); + const float tofNSigPi = track.tofNSigmaPi() - static_cast(pionPID.shiftInNsigmaTOFPion); histos.fill(HIST("QAafter/Pion/tofNSigmaVsMomentum"), totalMomentum, tofNSigPi); histos.fill(HIST("QAafter/Pion/tofNSigmaVsPt"), pt, tofNSigPi); histos.fill(HIST("QAafter/Pion/tofNSigmaVsCentrality"), centralityPercent, tofNSigPi); @@ -980,7 +997,7 @@ struct DeltaAnalysis { histos.fill(HIST("QAMC/Proton/tpcCrossedRowsVsPt"), pt, track.tpcNClsCrossedRows()); histos.fill(HIST("QAMC/Proton/tpcClustersFoundVsPt"), pt, track.tpcNClsFound()); if (!pidShared.useTPCOnlyPID && track.hasTOF()) { - const float tofNSigPr = track.tofNSigmaPr(); + const float tofNSigPr = track.tofNSigmaPr() - static_cast(protonPID.shiftInNsigmaTOFProton); histos.fill(HIST("QAMC/Proton/tofNSigmaVsMomentum"), totalMomentum, tofNSigPr); histos.fill(HIST("QAMC/Proton/tofNSigmaVsPt"), pt, tofNSigPr); histos.fill(HIST("QAMC/Proton/tofNSigmaVsCentrality"), centralityPercent, tofNSigPr); @@ -1006,7 +1023,7 @@ struct DeltaAnalysis { histos.fill(HIST("QAMC/Pion/tpcCrossedRowsVsPt"), pt, track.tpcNClsCrossedRows()); histos.fill(HIST("QAMC/Pion/tpcClustersFoundVsPt"), pt, track.tpcNClsFound()); if (!pidShared.useTPCOnlyPID && track.hasTOF()) { - const float tofNSigPi = track.tofNSigmaPi(); + const float tofNSigPi = track.tofNSigmaPi() - static_cast(pionPID.shiftInNsigmaTOFPion); histos.fill(HIST("QAMC/Pion/tofNSigmaVsMomentum"), totalMomentum, tofNSigPi); histos.fill(HIST("QAMC/Pion/tofNSigmaVsPt"), pt, tofNSigPi); histos.fill(HIST("QAMC/Pion/tofNSigmaVsCentrality"), centralityPercent, tofNSigPi); @@ -1412,11 +1429,11 @@ struct DeltaAnalysis { using BinningTypeFV0A = ColumnBinningPolicy; using BinningTypeNTPV = ColumnBinningPolicy; - BinningTypeFT0M binningFT0M{{axes.cfgVtxAxis, axes.cfgCentAxis}, true}; - BinningTypeFT0A binningFT0A{{axes.cfgVtxAxis, axes.cfgCentAxis}, true}; - BinningTypeFT0C binningFT0C{{axes.cfgVtxAxis, axes.cfgCentAxis}, true}; - BinningTypeFV0A binningFV0A{{axes.cfgVtxAxis, axes.cfgCentAxis}, true}; - BinningTypeNTPV binningNTPV{{axes.cfgVtxAxis, axes.cfgCentAxis}, true}; + BinningTypeFT0M binningFT0M{{axes.cfgVtxAxis, axes.cfgCentAxis}}; + BinningTypeFT0A binningFT0A{{axes.cfgVtxAxis, axes.cfgCentAxis}}; + BinningTypeFT0C binningFT0C{{axes.cfgVtxAxis, axes.cfgCentAxis}}; + BinningTypeFV0A binningFV0A{{axes.cfgVtxAxis, axes.cfgCentAxis}}; + BinningTypeNTPV binningNTPV{{axes.cfgVtxAxis, axes.cfgCentAxis}}; SameKindPair pairFT0M{binningFT0M, mixingCfg.cfgNoMixedEvents, -1, &cache}; SameKindPair pairFT0A{binningFT0A, mixingCfg.cfgNoMixedEvents, -1, &cache}; @@ -1889,7 +1906,7 @@ struct DeltaAnalysis { histos.fill(HIST("EfficiencyQA/hGeneratedEventCutFlow"), 1.f); // |Vz| accepted const bool truthInelGt0 = isTruthInelGt0(mcParticles); - if (evSel.cfgRequireRecoINELgt0 && !truthInelGt0) { + if (evSel.cfgApplyMCTruthINELgt0 && !truthInelGt0) { return; } histos.fill(HIST("CutFlow/MCGen/hEventCutFlow"), 2.f); // Truth INEL>0 @@ -1898,8 +1915,6 @@ struct DeltaAnalysis { histos.fill(HIST("CutFlow/MCGen/hEventCutFlow"), 4.f); // Final generated event histos.fill(HIST("EfficiencyQA/hGeneratedEventCutFlow"), 3.f); // Final generated event - // ── MODIFIED BLOCK (per user request): centrality-source switch for generated Delta ──── - // Added: evSel.cfgUseMCTruthCentrality (see Configurable added in evSel group above). bool hasAcceptedReco = false; float genCentrality = mcCollision.centFT0M(); @@ -2062,7 +2077,7 @@ struct DeltaAnalysis { histos.fill(HIST("CutFlow/EventFactor/hEventAcceptedCutFlow"), 1.f); // |Vz| < cfgCutVertex auto mcPartsThisColl = mcParticles.sliceBy(perMcCollisionDelta, mcCollision.globalIndex()); - if (evSel.cfgRequireRecoINELgt0 && !isTruthInelGt0(mcPartsThisColl)) { + if (evSel.cfgApplyMCTruthINELgt0 && !isTruthInelGt0(mcPartsThisColl)) { continue; } histos.fill(HIST("CutFlow/EventFactor/hEventAcceptedCutFlow"), 2.f); // Truth INEL>0 @@ -2083,10 +2098,7 @@ struct DeltaAnalysis { if (hasAcceptedReco) { histos.fill(HIST("CutFlow/EventFactor/hEventAcceptedCutFlow"), 4.f); // Associated reco collision passes event selection histos.fill(HIST("CutFlow/EventFactor/hEventAcceptedCutFlow"), 5.f); // Final EventAccepted - // Truth-binned: denominator for event_loss = hEventsGenAccepted / hEventsGenAll (unchanged). histos.fill(HIST("EventFactor/hEventsGenAccepted"), truthCentrality); - // NEW - reco-binned: numerator for event_splitting = hEventsGenAcceptedReco / hRecoEvents, - // now on the same (reconstructed) centrality axis as hRecoEvents. histos.fill(HIST("EventFactor/hEventsGenAcceptedReco"), itCent->second); } } diff --git a/PWGLF/Tasks/Resonances/doublephimeson.cxx b/PWGLF/Tasks/Resonances/doublephimeson.cxx index b027bfdd2d3..585d6a92173 100644 --- a/PWGLF/Tasks/Resonances/doublephimeson.cxx +++ b/PWGLF/Tasks/Resonances/doublephimeson.cxx @@ -2329,7 +2329,7 @@ struct doublephimeson { { auto tracksTuple = std::make_tuple(phitracks); - BinningTypeVertexContributor binningOnPositions{{CfgVtxBins, CfgMultBins}, true}; + BinningTypeVertexContributor binningOnPositions{{CfgVtxBins, CfgMultBins}}; SameKindPair pair{ binningOnPositions, nEvtMixing, -1, collisions, tracksTuple, &cache}; @@ -2587,7 +2587,7 @@ struct doublephimeson { void processMixedEvent(aod::RedPhiEvents& collisions, aod::PhiTracks& phitracks) { auto tracksTuple = std::make_tuple(phitracks); - BinningTypeVertexContributor binningOnPositions{{CfgVtxBins, CfgMultBins}, true}; + BinningTypeVertexContributor binningOnPositions{{CfgVtxBins, CfgMultBins}}; SameKindPair pair{ binningOnPositions, nEvtMixing, -1, collisions, tracksTuple, &cache}; diff --git a/PWGLF/Tasks/Resonances/f0980FlowSP.cxx b/PWGLF/Tasks/Resonances/f0980FlowSP.cxx new file mode 100644 index 00000000000..b868aca99f1 --- /dev/null +++ b/PWGLF/Tasks/Resonances/f0980FlowSP.cxx @@ -0,0 +1,934 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file f0980FlowSP.cxx +/// \brief pi+pi- invariant-mass analysis and f0(980) v2 with scalar-product and event-plane methods + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/Qvectors.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; + +namespace +{ +using P4 = ROOT::Math::PxPyPzMVector; +constexpr float InvalidQ = -900.f; +constexpr int ProtonMassNumber = 1; +constexpr int OxygenMassNumber = 16; +} // namespace + +struct F0980FlowSP { + SliceCache cache; + HistogramRegistry registry{"registry"}; + Service ccdb{}; + + enum class CollisionSystem : int { + PP = 0, + OO = 1, + PO = 2, + Unsupported = 3 + }; + + enum class PionPidMode : int { + TPCWithTOFWhenAvailable = 0, + TPCOnly = 1, + TPCTOFRequired = 2, + MomentumDependentTOF = 4 + }; + + enum class EventSelectionStep : int { + All, + Sel8, + NoSameBunchPileup, + NoTimeFrameBorder, + NoITSROFrameBorder, + ITSTPCVertex, + Centrality, + QVectorCalibrated, + QVectorValid, + Count + }; + + enum class PPEventSelectionStep : int { + All, + Sel8, + NoSameBunchPileup, + NoTimeFrameBorder, + NoITSROFrameBorder, + ITSTPCVertex, + Inelastic, + QVectorValid, + Count + }; + + enum class QVectorStatus { + IndexUnavailable, + Uncalibrated, + Invalid, + Valid + }; + + struct QVectorData { + double qAx = 0.; + double qAy = 0.; + double qBx = 0.; + double qBy = 0.; + double qCx = 0.; + double qCy = 0.; + double qAMagnitude = 0.; + double qBMagnitude = 0.; + double qCMagnitude = 0.; + + bool hasValidEventPlanes() const + { + return qAMagnitude > 0. && qBMagnitude > 0. && qCMagnitude > 0.; + } + }; + + int cachedRunNumber = -1; + CollisionSystem cachedCollisionSystem = CollisionSystem::Unsupported; + + // Pair and daughter selection. Defaults follow rho770analysis.cxx. + Configurable cfgMinPt{"cfgMinPt", 0.15f, "Minimum daughter transverse momentum"}; + Configurable cfgMaxEta{"cfgMaxEta", 0.8f, "Maximum daughter pseudorapidity"}; + Configurable cfgMinRap{"cfgMinRap", -0.5f, "Minimum pair rapidity"}; + Configurable cfgMaxRap{"cfgMaxRap", 0.5f, "Maximum pair rapidity"}; + Configurable cfgPrimaryTrack{"cfgPrimaryTrack", true, "Require primary track"}; + Configurable cfgGlobalWoDCA{"cfgGlobalWoDCA", true, "Require global track without DCA cuts"}; + Configurable cfgPVContributor{"cfgPVContributor", true, "Require PV contributor"}; + Configurable cfgMaxDCAxy{"cfgMaxDCAxy", 0.1f, "Maximum |DCAxy| (cm)"}; + Configurable cfgMaxDCAz{"cfgMaxDCAz", 0.1f, "Maximum |DCAz| (cm)"}; + Configurable cfgMaxTPCChi2NCl{"cfgMaxTPCChi2NCl", 4.f, "Maximum TPC chi2 per cluster"}; + Configurable cfgMaxITSChi2NCl{"cfgMaxITSChi2NCl", 36.f, "Maximum ITS chi2 per cluster"}; + Configurable cfgMinTPCRows{"cfgMinTPCRows", 70, "Minimum TPC crossed rows"}; + Configurable cfgMinITSClusters{"cfgMinITSClusters", 5, "Minimum ITS clusters"}; + + Configurable cfgRequireSel8{"cfgRequireSel8", true, "Require sel8"}; + Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", true, "Require kNoSameBunchPileup"}; + Configurable cfgRequireNoTimeFrameBorder{"cfgRequireNoTimeFrameBorder", true, "Require kNoTimeFrameBorder"}; + Configurable cfgRequireNoITSROFrameBorder{"cfgRequireNoITSROFrameBorder", true, "Require kNoITSROFrameBorder"}; + Configurable cfgRequireITSTPCVertex{"cfgRequireITSTPCVertex", true, "Require kIsVertexITSTPC"}; + Configurable cfgCollisionSystem{"cfgCollisionSystem", -1, "Collision system: -1 auto from GRPLHCIF, 0 pp, 1 OO, 2 pO"}; + + Configurable cfgPIDMode{"cfgPIDMode", 0, "Pion PID: 0 TPC plus TOF when available, 1 TPC only, 2 require both TPC and TOF, 4 TPC-only below momentum threshold and require TOF above it"}; + Configurable cfgTPCNSigmaPi{"cfgTPCNSigmaPi", 3.f, "TPC pion n-sigma with TOF"}; + Configurable cfgTPCNSigmaPiNoTOF{"cfgTPCNSigmaPiNoTOF", 3.f, "TPC pion n-sigma without TOF"}; + Configurable cfgTOFNSigmaPi{"cfgTOFNSigmaPi", 3.f, "TOF pion n-sigma"}; + Configurable cfgTOFRequiredAboveP{"cfgTOFRequiredAboveP", 0.6f, "For PID mode 4, require TOF at and above this total momentum (GeV/c)"}; + // The vector tables contain one entry per harmonic requested from qVectorsTable. + Configurable cfgHarmonicIndex{"cfgHarmonicIndex", 0, "Index of n=2 in Q-vector vector columns"}; + Configurable cfgCentMin{"cfgCentMin", 0.f, "Minimum FT0C centrality for OO"}; + Configurable cfgCentMax{"cfgCentMax", 100.f, "Maximum FT0C centrality for OO"}; + + ConfigurableAxis axisMass{"axisMass", {400, 0.2, 2.2}, "m_{pi pi} (GeV/c^{2})"}; + ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0., 0.5, 1., 1.5, 2., 2.5, 3., 4., 6., 8., 10., 13., 20.}, "pair pT (GeV/c)"}; + ConfigurableAxis axisCent{"axisCent", {VARIABLE_WIDTH, 0., 5., 10., 20., 30., 40., 50., 60., 70., 80., 100.}, "FT0C centrality (%)"}; + ConfigurableAxis axisMult{"axisMult", {VARIABLE_WIDTH, 0., 1., 5., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100., 105.}, "N_{PV contributors}"}; + ConfigurableAxis axisRun{"axisRun", {2001, 563999.5, 566000.5}, "run number"}; + + using PPCollisions = soa::Join; + using OOCollisions = soa::Join; + using POCollisions = OOCollisions; + using TracksTPC = soa::Join; + using TracksTPCTOF = soa::Join; + + template + CollisionSystem collisionSystem(Collision const& collision) + { + if (cfgCollisionSystem.value >= static_cast(CollisionSystem::PP) && cfgCollisionSystem.value <= static_cast(CollisionSystem::PO)) { + return static_cast(cfgCollisionSystem.value); + } + + const auto bc = collision.template bc_as(); + if (bc.runNumber() == cachedRunNumber) { + return cachedCollisionSystem; + } + + auto* grplhcif = ccdb->getForTimeStamp("GLO/Config/GRPLHCIF", bc.timestamp()); + if (grplhcif == nullptr) { + LOGF(fatal, "Cannot load GLO/Config/GRPLHCIF for run %d and timestamp %" PRIu64, bc.runNumber(), bc.timestamp()); + return CollisionSystem::Unsupported; + } + + const int beamAA = grplhcif->getBeamA(o2::constants::lhc::BeamA); + const int beamCA = grplhcif->getBeamA(o2::constants::lhc::BeamC); + if (beamAA == ProtonMassNumber && beamCA == ProtonMassNumber) { + cachedCollisionSystem = CollisionSystem::PP; + } else if (beamAA == OxygenMassNumber && beamCA == OxygenMassNumber) { + cachedCollisionSystem = CollisionSystem::OO; + } else if ((beamAA == ProtonMassNumber && beamCA == OxygenMassNumber) || + (beamAA == OxygenMassNumber && beamCA == ProtonMassNumber)) { + cachedCollisionSystem = CollisionSystem::PO; + } else { + cachedCollisionSystem = CollisionSystem::Unsupported; + } + cachedRunNumber = bc.runNumber(); + LOGF(info, "Automatically identified collision system for run %d: beam A values %d and %d, mode %d", cachedRunNumber, beamAA, beamCA, static_cast(cachedCollisionSystem)); + return cachedCollisionSystem; + } + + template + QVectorStatus getQVectorData(Collision const& collision, QVectorData& qVector) const + { + const size_t harmonicIndex = cfgHarmonicIndex.value; + if (harmonicIndex >= collision.qvecFT0CReVec().size() || + harmonicIndex >= collision.qvecFT0CImVec().size() || + harmonicIndex >= collision.qvecFT0AReVec().size() || + harmonicIndex >= collision.qvecFT0AImVec().size() || + harmonicIndex >= collision.qvecTPCposReVec().size() || + harmonicIndex >= collision.qvecTPCposImVec().size()) { + return QVectorStatus::IndexUnavailable; + } + if (!collision.isCalibrated()) { + return QVectorStatus::Uncalibrated; + } + + qVector.qAx = collision.qvecFT0CReVec()[harmonicIndex]; + qVector.qAy = collision.qvecFT0CImVec()[harmonicIndex]; + qVector.qBx = collision.qvecFT0AReVec()[harmonicIndex]; + qVector.qBy = collision.qvecFT0AImVec()[harmonicIndex]; + qVector.qCx = collision.qvecTPCposReVec()[harmonicIndex]; + qVector.qCy = collision.qvecTPCposImVec()[harmonicIndex]; + if (qVector.qAx < InvalidQ || qVector.qAy < InvalidQ || + qVector.qBx < InvalidQ || qVector.qBy < InvalidQ || + qVector.qCx < InvalidQ || qVector.qCy < InvalidQ) { + return QVectorStatus::Invalid; + } + + qVector.qAMagnitude = std::hypot(qVector.qAx, qVector.qAy); + qVector.qBMagnitude = std::hypot(qVector.qBx, qVector.qBy); + qVector.qCMagnitude = std::hypot(qVector.qCx, qVector.qCy); + return QVectorStatus::Valid; + } + + void init(InitContext const&) + { + // Counts and weighted SP numerators have identical axes. Their ratio is + // in each (mass, pT, centrality) bin. + registry.add("PP/hMassUS", "pp unlike-sign;mass;pT;multiplicity", HistType::kTHnSparseF, {axisMass, axisPt, axisMult}); + registry.add("PP/hMassLSpp", "pp ++;mass;pT;multiplicity", HistType::kTHnSparseF, {axisMass, axisPt, axisMult}); + registry.add("PP/hMassLSmm", "pp --;mass;pT;multiplicity", HistType::kTHnSparseF, {axisMass, axisPt, axisMult}); + registry.add("PP/hSpNumUS", "sum Re(u2 qFT0C*), unlike-sign;mass;pT;multiplicity", HistType::kTHnSparseF, {axisMass, axisPt, axisMult}); + registry.add("PP/hSpNumLSpp", "sum Re(u2 qFT0C*), ++;mass;pT;multiplicity", HistType::kTHnSparseF, {axisMass, axisPt, axisMult}); + registry.add("PP/hSpNumLSmm", "sum Re(u2 qFT0C*), --;mass;pT;multiplicity", HistType::kTHnSparseF, {axisMass, axisPt, axisMult}); + registry.add("PP/hEpNumUS", "sum cos(2(phi-Psi2FT0C)), unlike-sign;mass;pT;multiplicity", HistType::kTHnSparseF, {axisMass, axisPt, axisMult}); + registry.add("PP/hEpNumLSpp", "sum cos(2(phi-Psi2FT0C)), ++;mass;pT;multiplicity", HistType::kTHnSparseF, {axisMass, axisPt, axisMult}); + registry.add("PP/hEpNumLSmm", "sum cos(2(phi-Psi2FT0C)), --;mass;pT;multiplicity", HistType::kTHnSparseF, {axisMass, axisPt, axisMult}); + registry.add("PP/pQAB", ";multiplicity", HistType::kTProfile, {axisMult}); + registry.add("PP/pQAC", ";multiplicity", HistType::kTProfile, {axisMult}); + registry.add("PP/pQBC", ";multiplicity", HistType::kTProfile, {axisMult}); + registry.add("PP/pQABIm", "Im(qFT0C qFT0A*);multiplicity", HistType::kTProfile, {axisMult}); + registry.add("PP/pQACIm", "Im(qFT0C qTPCpos*);multiplicity", HistType::kTProfile, {axisMult}); + registry.add("PP/pQBCIm", "Im(qFT0A qTPCpos*);multiplicity", HistType::kTProfile, {axisMult}); + registry.add("PP/pCosPsiAB", ";multiplicity", HistType::kTProfile, {axisMult}); + registry.add("PP/pCosPsiAC", ";multiplicity", HistType::kTProfile, {axisMult}); + registry.add("PP/pCosPsiBC", ";multiplicity", HistType::kTProfile, {axisMult}); + registry.add("PP/QA/Event/hSelection", "pp event-selection cut flow;selection step;events", HistType::kTH1F, {{static_cast(PPEventSelectionStep::Count), -0.5, static_cast(PPEventSelectionStep::Count) - 0.5}}); + registry.add("PP/QA/Event/hMultiplicityBefore", "pp events before selection;N_{PV contributors};events", HistType::kTH1F, {{106, -0.5, 105.5}}); + registry.add("PP/QA/Event/hMultiplicityAfter", "pp events after selection;N_{PV contributors};events", HistType::kTH1F, {{106, -0.5, 105.5}}); + registry.add("PP/QA/Event/hVertexZBefore", "pp events before selection;z_{vtx} (cm);events", HistType::kTH1F, {{240, -30., 30.}}); + registry.add("PP/QA/Event/hVertexZAfter", "pp events after selection;z_{vtx} (cm);events", HistType::kTH1F, {{240, -30., 30.}}); + registry.add("PP/QA/Qvector/hFT0C", "corrected q_{2,FT0C};q_{x};q_{y}", HistType::kTH2F, {{200, -1., 1.}, {200, -1., 1.}}); + registry.add("PP/QA/Qvector/hFT0A", "corrected q_{2,FT0A};q_{x};q_{y}", HistType::kTH2F, {{200, -1., 1.}, {200, -1., 1.}}); + registry.add("PP/QA/Qvector/hTPCpos", "corrected q_{2,TPCpos};q_{x};q_{y}", HistType::kTH2F, {{200, -1., 1.}, {200, -1., 1.}}); + registry.add("PP/QA/Qvector/hMagnitudeFT0C", "FT0C q_{2} magnitude;multiplicity;|q_{2}|", HistType::kTH2F, {axisMult, {200, 0., 1.5}}); + registry.add("PP/QA/Qvector/hMagnitudeFT0A", "FT0A q_{2} magnitude;multiplicity;|q_{2}|", HistType::kTH2F, {axisMult, {200, 0., 1.5}}); + registry.add("PP/QA/Qvector/hMagnitudeTPCpos", "TPCpos q_{2} magnitude;multiplicity;|q_{2}|", HistType::kTH2F, {axisMult, {200, 0., 1.5}}); + registry.add("PP/QA/Qvector/hPsi2FT0C", "FT0C event-plane angle;multiplicity;#Psi_{2}", HistType::kTH2F, {axisMult, {180, -1.5708, 1.5708}}); + registry.add("PP/QA/Qvector/hPsi2FT0A", "FT0A event-plane angle;multiplicity;#Psi_{2}", HistType::kTH2F, {axisMult, {180, -1.5708, 1.5708}}); + registry.add("PP/QA/Qvector/hPsi2TPCpos", "TPCpos event-plane angle;multiplicity;#Psi_{2}", HistType::kTH2F, {axisMult, {180, -1.5708, 1.5708}}); + registry.add("PP/QA/Qvector/hDotAB", "Re(q_{FT0C}q_{FT0A}^{*});multiplicity;q_{A}#upointq_{B}", HistType::kTH2F, {axisMult, {240, -1.2, 1.2}}); + registry.add("PP/QA/Qvector/hDotAC", "Re(q_{FT0C}q_{TPCpos}^{*});multiplicity;q_{A}#upointq_{C}", HistType::kTH2F, {axisMult, {240, -1.2, 1.2}}); + registry.add("PP/QA/Qvector/hDotBC", "Re(q_{FT0A}q_{TPCpos}^{*});multiplicity;q_{B}#upointq_{C}", HistType::kTH2F, {axisMult, {240, -1.2, 1.2}}); + registry.add("PP/QA/Qvector/pQxFT0CVsMult", "mean corrected q_{x,FT0C};multiplicity;<#it{q}_{x}>", HistType::kTProfile, {axisMult}); + registry.add("PP/QA/Qvector/pQyFT0CVsMult", "mean corrected q_{y,FT0C};multiplicity;<#it{q}_{y}>", HistType::kTProfile, {axisMult}); + registry.add("PP/QA/Qvector/pQxFT0AVsMult", "mean corrected q_{x,FT0A};multiplicity;<#it{q}_{x}>", HistType::kTProfile, {axisMult}); + registry.add("PP/QA/Qvector/pQyFT0AVsMult", "mean corrected q_{y,FT0A};multiplicity;<#it{q}_{y}>", HistType::kTProfile, {axisMult}); + registry.add("PP/QA/Qvector/pQxTPCposVsMult", "mean corrected q_{x,TPCpos};multiplicity;<#it{q}_{x}>", HistType::kTProfile, {axisMult}); + registry.add("PP/QA/Qvector/pQyTPCposVsMult", "mean corrected q_{y,TPCpos};multiplicity;<#it{q}_{y}>", HistType::kTProfile, {axisMult}); + registry.add("PP/QA/Qvector/pQxFT0CVsRun", "mean corrected q_{x,FT0C};run number;<#it{q}_{x}>", HistType::kTProfile, {axisRun}); + registry.add("PP/QA/Qvector/pQyFT0CVsRun", "mean corrected q_{y,FT0C};run number;<#it{q}_{y}>", HistType::kTProfile, {axisRun}); + registry.add("PP/QA/Qvector/pQxFT0AVsRun", "mean corrected q_{x,FT0A};run number;<#it{q}_{x}>", HistType::kTProfile, {axisRun}); + registry.add("PP/QA/Qvector/pQyFT0AVsRun", "mean corrected q_{y,FT0A};run number;<#it{q}_{y}>", HistType::kTProfile, {axisRun}); + registry.add("PP/QA/Qvector/pQxTPCposVsRun", "mean corrected q_{x,TPCpos};run number;<#it{q}_{x}>", HistType::kTProfile, {axisRun}); + registry.add("PP/QA/Qvector/pQyTPCposVsRun", "mean corrected q_{y,TPCpos};run number;<#it{q}_{y}>", HistType::kTProfile, {axisRun}); + + registry.add("OO/hMassUS", "OO unlike-sign;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("OO/hMassLSpp", "OO ++;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("OO/hMassLSmm", "OO --;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("OO/hSpNumUS", "sum Re(u2 qFT0C*), unlike-sign;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("OO/hSpNumLSpp", "sum Re(u2 qFT0C*), ++;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("OO/hSpNumLSmm", "sum Re(u2 qFT0C*), --;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("OO/hEpNumUS", "sum cos(2(phi-Psi2FT0C)), unlike-sign;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("OO/hEpNumLSpp", "sum cos(2(phi-Psi2FT0C)), ++;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("OO/hEpNumLSmm", "sum cos(2(phi-Psi2FT0C)), --;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + + registry.add("OO/pQAB", ";centrality", HistType::kTProfile, {axisCent}); + registry.add("OO/pQAC", ";centrality", HistType::kTProfile, {axisCent}); + registry.add("OO/pQBC", ";centrality", HistType::kTProfile, {axisCent}); + registry.add("OO/pQABIm", "Im(qFT0C qFT0A*);centrality", HistType::kTProfile, {axisCent}); + registry.add("OO/pQACIm", "Im(qFT0C qTPCpos*);centrality", HistType::kTProfile, {axisCent}); + registry.add("OO/pQBCIm", "Im(qFT0A qTPCpos*);centrality", HistType::kTProfile, {axisCent}); + registry.add("OO/pCosPsiAB", ";centrality", HistType::kTProfile, {axisCent}); + registry.add("OO/pCosPsiAC", ";centrality", HistType::kTProfile, {axisCent}); + registry.add("OO/pCosPsiBC", ";centrality", HistType::kTProfile, {axisCent}); + + registry.add("PO/hMassUS", "pO unlike-sign;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("PO/hMassLSpp", "pO ++;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("PO/hMassLSmm", "pO --;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("PO/hSpNumUS", "sum Re(u2 qFT0C*), unlike-sign;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("PO/hSpNumLSpp", "sum Re(u2 qFT0C*), ++;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("PO/hSpNumLSmm", "sum Re(u2 qFT0C*), --;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("PO/hEpNumUS", "sum cos(2(phi-Psi2FT0C)), unlike-sign;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("PO/hEpNumLSpp", "sum cos(2(phi-Psi2FT0C)), ++;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("PO/hEpNumLSmm", "sum cos(2(phi-Psi2FT0C)), --;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("PO/pQAB", ";centrality", HistType::kTProfile, {axisCent}); + registry.add("PO/pQAC", ";centrality", HistType::kTProfile, {axisCent}); + registry.add("PO/pQBC", ";centrality", HistType::kTProfile, {axisCent}); + registry.add("PO/pQABIm", "Im(qFT0C qFT0A*);centrality", HistType::kTProfile, {axisCent}); + registry.add("PO/pQACIm", "Im(qFT0C qTPCpos*);centrality", HistType::kTProfile, {axisCent}); + registry.add("PO/pQBCIm", "Im(qFT0A qTPCpos*);centrality", HistType::kTProfile, {axisCent}); + registry.add("PO/pCosPsiAB", ";centrality", HistType::kTProfile, {axisCent}); + registry.add("PO/pCosPsiAC", ";centrality", HistType::kTProfile, {axisCent}); + registry.add("PO/pCosPsiBC", ";centrality", HistType::kTProfile, {axisCent}); + + registry.add("QA/Event/hSelection", "ion-collision event-selection cut flow;selection step;events", HistType::kTH1F, {{static_cast(EventSelectionStep::Count), -0.5, static_cast(EventSelectionStep::Count) - 0.5}}); + registry.add("QA/Event/hCentrality", "selected ion collisions;FT0C centrality (%);events", HistType::kTH1F, {{100, 0., 100.}}); + registry.add("QA/Event/hCentralityBefore", "ion-collision events before selection;FT0C centrality (%);events", HistType::kTH1F, {{100, 0., 100.}}); + registry.add("QA/Event/hCentralityAfter", "ion-collision events after selection;FT0C centrality (%);events", HistType::kTH1F, {{100, 0., 100.}}); + registry.add("QA/Event/hVertexZBefore", "ion-collision events before selection;z_{vtx} (cm);events", HistType::kTH1F, {{240, -30., 30.}}); + registry.add("QA/Event/hVertexZAfter", "ion-collision events after selection;z_{vtx} (cm);events", HistType::kTH1F, {{240, -30., 30.}}); + + registry.add("QA/Qvector/hFT0C", "corrected q_{2,FT0C};q_{x};q_{y}", HistType::kTH2F, {{200, -1., 1.}, {200, -1., 1.}}); + registry.add("QA/Qvector/hFT0A", "corrected q_{2,FT0A};q_{x};q_{y}", HistType::kTH2F, {{200, -1., 1.}, {200, -1., 1.}}); + registry.add("QA/Qvector/hTPCpos", "corrected q_{2,TPCpos};q_{x};q_{y}", HistType::kTH2F, {{200, -1., 1.}, {200, -1., 1.}}); + registry.add("QA/Qvector/hMagnitudeFT0C", "FT0C q_{2} magnitude;FT0C centrality (%);|q_{2}|", HistType::kTH2F, {axisCent, {200, 0., 1.5}}); + registry.add("QA/Qvector/hMagnitudeFT0A", "FT0A q_{2} magnitude;FT0C centrality (%);|q_{2}|", HistType::kTH2F, {axisCent, {200, 0., 1.5}}); + registry.add("QA/Qvector/hMagnitudeTPCpos", "TPCpos q_{2} magnitude;FT0C centrality (%);|q_{2}|", HistType::kTH2F, {axisCent, {200, 0., 1.5}}); + registry.add("QA/Qvector/hPsi2FT0C", "FT0C event-plane angle;FT0C centrality (%);#Psi_{2}", HistType::kTH2F, {axisCent, {180, -1.5708, 1.5708}}); + registry.add("QA/Qvector/hPsi2FT0A", "FT0A event-plane angle;FT0C centrality (%);#Psi_{2}", HistType::kTH2F, {axisCent, {180, -1.5708, 1.5708}}); + registry.add("QA/Qvector/hPsi2TPCpos", "TPCpos event-plane angle;FT0C centrality (%);#Psi_{2}", HistType::kTH2F, {axisCent, {180, -1.5708, 1.5708}}); + registry.add("QA/Qvector/hDotAB", "Re(q_{FT0C}q_{FT0A}^{*});FT0C centrality (%);q_{A}#upointq_{B}", HistType::kTH2F, {axisCent, {240, -1.2, 1.2}}); + registry.add("QA/Qvector/hDotAC", "Re(q_{FT0C}q_{TPCpos}^{*});FT0C centrality (%);q_{A}#upointq_{C}", HistType::kTH2F, {axisCent, {240, -1.2, 1.2}}); + registry.add("QA/Qvector/hDotBC", "Re(q_{FT0A}q_{TPCpos}^{*});FT0C centrality (%);q_{B}#upointq_{C}", HistType::kTH2F, {axisCent, {240, -1.2, 1.2}}); + registry.add("QA/Qvector/pQxFT0CVsCent", "mean corrected q_{x,FT0C};FT0C centrality (%);<#it{q}_{x}>", HistType::kTProfile, {axisCent}); + registry.add("QA/Qvector/pQyFT0CVsCent", "mean corrected q_{y,FT0C};FT0C centrality (%);<#it{q}_{y}>", HistType::kTProfile, {axisCent}); + registry.add("QA/Qvector/pQxFT0AVsCent", "mean corrected q_{x,FT0A};FT0C centrality (%);<#it{q}_{x}>", HistType::kTProfile, {axisCent}); + registry.add("QA/Qvector/pQyFT0AVsCent", "mean corrected q_{y,FT0A};FT0C centrality (%);<#it{q}_{y}>", HistType::kTProfile, {axisCent}); + registry.add("QA/Qvector/pQxTPCposVsCent", "mean corrected q_{x,TPCpos};FT0C centrality (%);<#it{q}_{x}>", HistType::kTProfile, {axisCent}); + registry.add("QA/Qvector/pQyTPCposVsCent", "mean corrected q_{y,TPCpos};FT0C centrality (%);<#it{q}_{y}>", HistType::kTProfile, {axisCent}); + registry.add("QA/Qvector/pQxFT0CVsRun", "mean corrected q_{x,FT0C};run number;<#it{q}_{x}>", HistType::kTProfile, {axisRun}); + registry.add("QA/Qvector/pQyFT0CVsRun", "mean corrected q_{y,FT0C};run number;<#it{q}_{y}>", HistType::kTProfile, {axisRun}); + registry.add("QA/Qvector/pQxFT0AVsRun", "mean corrected q_{x,FT0A};run number;<#it{q}_{x}>", HistType::kTProfile, {axisRun}); + registry.add("QA/Qvector/pQyFT0AVsRun", "mean corrected q_{y,FT0A};run number;<#it{q}_{y}>", HistType::kTProfile, {axisRun}); + registry.add("QA/Qvector/pQxTPCposVsRun", "mean corrected q_{x,TPCpos};run number;<#it{q}_{x}>", HistType::kTProfile, {axisRun}); + registry.add("QA/Qvector/pQyTPCposVsRun", "mean corrected q_{y,TPCpos};run number;<#it{q}_{y}>", HistType::kTProfile, {axisRun}); + + auto eventSelection = registry.get(HIST("QA/Event/hSelection")); + constexpr std::array eventSelectionLabels = { + "all", "sel8", "no same-bunch pileup", "no TF border", + "no ITS ROF border", "ITS-TPC vertex", "centrality", "Q calibrated", "Q valid"}; + for (size_t i = 0; i < eventSelectionLabels.size(); ++i) { + eventSelection->GetXaxis()->SetBinLabel(i + 1, eventSelectionLabels[i]); + } + auto ppEventSelection = registry.get(HIST("PP/QA/Event/hSelection")); + constexpr std::array ppEventSelectionLabels = { + "all", "sel8", "no same-bunch pileup", "no TF border", + "no ITS ROF border", "ITS-TPC vertex", "INEL > 0", "Q valid"}; + for (size_t i = 0; i < ppEventSelectionLabels.size(); ++i) { + ppEventSelection->GetXaxis()->SetBinLabel(i + 1, ppEventSelectionLabels[i]); + } + registry.add("QA/TrackSelected/hPt", "track-selected candidates;p_{T} (GeV/c);counts", HistType::kTH1F, {{200, 0., 20.}}); + registry.add("QA/TrackSelected/hEta", "track-selected candidates;#eta;counts", HistType::kTH1F, {{160, -0.8, 0.8}}); + registry.add("QA/TrackSelected/hDCAxy", "track-selected candidates;DCA_{xy} (cm);counts", HistType::kTH1F, {{200, -0.1, 0.1}}); + registry.add("QA/TrackSelected/hDCAz", "track-selected candidates;DCA_{z} (cm);counts", HistType::kTH1F, {{200, -0.1, 0.1}}); + registry.add("QA/TrackSelected/hTPCCrossedRows", "track-selected candidates;TPC crossed rows;counts", HistType::kTH1F, {{165, -0.5, 164.5}}); + registry.add("QA/TrackSelected/hTPCNSigmaPiVsP", "track-selected candidates before pion PID;p (GeV/c);n#sigma_{TPC}^{#pi}", HistType::kTH2F, {{200, 0., 20.}, {120, -6., 6.}}); + registry.add("QA/TrackSelected/hTPCSignalVsP", "track-selected candidates before pion PID;p (GeV/c);TPC dE/dx", HistType::kTH2F, {{200, 0., 20.}, {500, 0., 500.}}); + registry.add("QA/TrackSelected/hTOFNSigmaPiVsP", "track-selected candidates with TOF before pion PID;p (GeV/c);n#sigma_{TOF}^{#pi}", HistType::kTH2F, {{200, 0., 20.}, {120, -6., 6.}}); + registry.add("QA/TrackSelected/hTOFBetaVsP", "track-selected candidates with TOF before pion PID;p (GeV/c);#beta_{TOF}", HistType::kTH2F, {{200, 0., 20.}, {300, 0., 1.5}}); + registry.add("QA/SelectedPions/hTPCNSigmaPiVsP", "selected pion candidates;p (GeV/c);n#sigma_{TPC}^{#pi}", HistType::kTH2F, {{200, 0., 20.}, {120, -6., 6.}}); + registry.add("QA/SelectedPions/hTPCSignalVsP", "selected pion candidates after pion PID;p (GeV/c);TPC dE/dx", HistType::kTH2F, {{200, 0., 20.}, {500, 0., 500.}}); + registry.add("QA/SelectedPions/hTOFNSigmaPiVsP", "selected pion candidates with TOF after pion PID;p (GeV/c);n#sigma_{TOF}^{#pi}", HistType::kTH2F, {{200, 0., 20.}, {120, -6., 6.}}); + registry.add("QA/SelectedPions/hTOFBetaVsP", "selected pion candidates with TOF after pion PID;p (GeV/c);#beta_{TOF}", HistType::kTH2F, {{200, 0., 20.}, {300, 0., 1.5}}); + } + + template + bool selectTrack(T const& track) const + { + if (track.pt() < cfgMinPt.value || std::abs(track.eta()) > cfgMaxEta.value) { + return false; + } + if (std::abs(track.dcaXY()) >= cfgMaxDCAxy.value || std::abs(track.dcaZ()) >= cfgMaxDCAz.value) { + return false; + } + if (cfgPrimaryTrack.value && !track.isPrimaryTrack()) { + return false; + } + if (cfgGlobalWoDCA.value && !track.isGlobalTrackWoDCA()) { + return false; + } + if (cfgPVContributor.value && !track.isPVContributor()) { + return false; + } + return track.tpcChi2NCl() < cfgMaxTPCChi2NCl.value && + track.itsChi2NCl() < cfgMaxITSChi2NCl.value && + track.tpcNClsCrossedRows() > cfgMinTPCRows.value && + track.itsNCls() > cfgMinITSClusters.value; + } + + template + bool selectEvent(C const& collision) const + { + return (!cfgRequireSel8.value || collision.sel8()) && + (!cfgRequireNoSameBunchPileup.value || collision.selection_bit(aod::evsel::kNoSameBunchPileup)) && + (!cfgRequireNoTimeFrameBorder.value || collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) && + (!cfgRequireNoITSROFrameBorder.value || collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) && + (!cfgRequireITSTPCVertex.value || collision.selection_bit(aod::evsel::kIsVertexITSTPC)); + } + + template + bool selectOOEventWithQA(C const& collision, float centrality) + { + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::All)); + registry.fill(HIST("QA/Event/hCentralityBefore"), centrality); + registry.fill(HIST("QA/Event/hVertexZBefore"), collision.posZ()); + if (cfgRequireSel8.value && !collision.sel8()) { + return false; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::Sel8)); + if (cfgRequireNoSameBunchPileup.value && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + return false; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::NoSameBunchPileup)); + if (cfgRequireNoTimeFrameBorder.value && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::NoTimeFrameBorder)); + if (cfgRequireNoITSROFrameBorder.value && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::NoITSROFrameBorder)); + if (cfgRequireITSTPCVertex.value && !collision.selection_bit(aod::evsel::kIsVertexITSTPC)) { + return false; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::ITSTPCVertex)); + if (centrality < cfgCentMin.value || centrality >= cfgCentMax.value) { + return false; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::Centrality)); + registry.fill(HIST("QA/Event/hCentrality"), centrality); + registry.fill(HIST("QA/Event/hCentralityAfter"), centrality); + registry.fill(HIST("QA/Event/hVertexZAfter"), collision.posZ()); + return true; + } + + void fillQVectorQA(float centrality, int runNumber, + double qAx, double qAy, + double qBx, double qBy, + double qCx, double qCy) + { + registry.fill(HIST("QA/Qvector/hFT0C"), qAx, qAy); + registry.fill(HIST("QA/Qvector/hFT0A"), qBx, qBy); + registry.fill(HIST("QA/Qvector/hTPCpos"), qCx, qCy); + registry.fill(HIST("QA/Qvector/hMagnitudeFT0C"), centrality, std::hypot(qAx, qAy)); + registry.fill(HIST("QA/Qvector/hMagnitudeFT0A"), centrality, std::hypot(qBx, qBy)); + registry.fill(HIST("QA/Qvector/hMagnitudeTPCpos"), centrality, std::hypot(qCx, qCy)); + registry.fill(HIST("QA/Qvector/hPsi2FT0C"), centrality, 0.5 * std::atan2(qAy, qAx)); + registry.fill(HIST("QA/Qvector/hPsi2FT0A"), centrality, 0.5 * std::atan2(qBy, qBx)); + registry.fill(HIST("QA/Qvector/hPsi2TPCpos"), centrality, 0.5 * std::atan2(qCy, qCx)); + registry.fill(HIST("QA/Qvector/hDotAB"), centrality, qAx * qBx + qAy * qBy); + registry.fill(HIST("QA/Qvector/hDotAC"), centrality, qAx * qCx + qAy * qCy); + registry.fill(HIST("QA/Qvector/hDotBC"), centrality, qBx * qCx + qBy * qCy); + registry.fill(HIST("QA/Qvector/pQxFT0CVsCent"), centrality, qAx); + registry.fill(HIST("QA/Qvector/pQyFT0CVsCent"), centrality, qAy); + registry.fill(HIST("QA/Qvector/pQxFT0AVsCent"), centrality, qBx); + registry.fill(HIST("QA/Qvector/pQyFT0AVsCent"), centrality, qBy); + registry.fill(HIST("QA/Qvector/pQxTPCposVsCent"), centrality, qCx); + registry.fill(HIST("QA/Qvector/pQyTPCposVsCent"), centrality, qCy); + registry.fill(HIST("QA/Qvector/pQxFT0CVsRun"), runNumber, qAx); + registry.fill(HIST("QA/Qvector/pQyFT0CVsRun"), runNumber, qAy); + registry.fill(HIST("QA/Qvector/pQxFT0AVsRun"), runNumber, qBx); + registry.fill(HIST("QA/Qvector/pQyFT0AVsRun"), runNumber, qBy); + registry.fill(HIST("QA/Qvector/pQxTPCposVsRun"), runNumber, qCx); + registry.fill(HIST("QA/Qvector/pQyTPCposVsRun"), runNumber, qCy); + } + + void fillPPQVectorQA(float multiplicity, int runNumber, + double qAx, double qAy, + double qBx, double qBy, + double qCx, double qCy) + { + registry.fill(HIST("PP/QA/Qvector/hFT0C"), qAx, qAy); + registry.fill(HIST("PP/QA/Qvector/hFT0A"), qBx, qBy); + registry.fill(HIST("PP/QA/Qvector/hTPCpos"), qCx, qCy); + registry.fill(HIST("PP/QA/Qvector/hMagnitudeFT0C"), multiplicity, std::hypot(qAx, qAy)); + registry.fill(HIST("PP/QA/Qvector/hMagnitudeFT0A"), multiplicity, std::hypot(qBx, qBy)); + registry.fill(HIST("PP/QA/Qvector/hMagnitudeTPCpos"), multiplicity, std::hypot(qCx, qCy)); + registry.fill(HIST("PP/QA/Qvector/hPsi2FT0C"), multiplicity, 0.5 * std::atan2(qAy, qAx)); + registry.fill(HIST("PP/QA/Qvector/hPsi2FT0A"), multiplicity, 0.5 * std::atan2(qBy, qBx)); + registry.fill(HIST("PP/QA/Qvector/hPsi2TPCpos"), multiplicity, 0.5 * std::atan2(qCy, qCx)); + registry.fill(HIST("PP/QA/Qvector/hDotAB"), multiplicity, qAx * qBx + qAy * qBy); + registry.fill(HIST("PP/QA/Qvector/hDotAC"), multiplicity, qAx * qCx + qAy * qCy); + registry.fill(HIST("PP/QA/Qvector/hDotBC"), multiplicity, qBx * qCx + qBy * qCy); + registry.fill(HIST("PP/QA/Qvector/pQxFT0CVsMult"), multiplicity, qAx); + registry.fill(HIST("PP/QA/Qvector/pQyFT0CVsMult"), multiplicity, qAy); + registry.fill(HIST("PP/QA/Qvector/pQxFT0AVsMult"), multiplicity, qBx); + registry.fill(HIST("PP/QA/Qvector/pQyFT0AVsMult"), multiplicity, qBy); + registry.fill(HIST("PP/QA/Qvector/pQxTPCposVsMult"), multiplicity, qCx); + registry.fill(HIST("PP/QA/Qvector/pQyTPCposVsMult"), multiplicity, qCy); + registry.fill(HIST("PP/QA/Qvector/pQxFT0CVsRun"), runNumber, qAx); + registry.fill(HIST("PP/QA/Qvector/pQyFT0CVsRun"), runNumber, qAy); + registry.fill(HIST("PP/QA/Qvector/pQxFT0AVsRun"), runNumber, qBx); + registry.fill(HIST("PP/QA/Qvector/pQyFT0AVsRun"), runNumber, qBy); + registry.fill(HIST("PP/QA/Qvector/pQxTPCposVsRun"), runNumber, qCx); + registry.fill(HIST("PP/QA/Qvector/pQyTPCposVsRun"), runNumber, qCy); + } + + template + bool selectPion(T const& track) const + { + const float tpc = std::abs(track.tpcNSigmaPi()); + const float tof = std::abs(track.tofNSigmaPi()); + switch (static_cast(cfgPIDMode.value)) { + case PionPidMode::TPCWithTOFWhenAvailable: + if (track.hasTOF()) { + return tpc < cfgTPCNSigmaPi.value && + tof < cfgTOFNSigmaPi.value; + } + return tpc < cfgTPCNSigmaPiNoTOF.value; + case PionPidMode::TPCOnly: + return tpc < cfgTPCNSigmaPiNoTOF.value; + case PionPidMode::TPCTOFRequired: + return track.hasTOF() && + tpc < cfgTPCNSigmaPi.value && + tof < cfgTOFNSigmaPi.value; + case PionPidMode::MomentumDependentTOF: + if (track.p() < cfgTOFRequiredAboveP.value) { + return tpc < cfgTPCNSigmaPiNoTOF.value; + } + return track.hasTOF() && + tpc < cfgTPCNSigmaPi.value && + tof < cfgTOFNSigmaPi.value; + default: + return false; + } + } + + template + bool selectPionTPCOnly(T const& track) const + { + return std::abs(track.tpcNSigmaPi()) < cfgTPCNSigmaPiNoTOF.value; + } + + template + void loopPairsSelected(Tracks const& tracks, TrackSelect trackSelect, PionSelect pionSelect, FillPair fillPair) + { + struct SelectedPion { + float px = 0.f; + float py = 0.f; + float pz = 0.f; + int8_t sign = 0; + }; + + // Apply daughter-quality and PID selections once per track before building + // pairs. In OO events this avoids constructing all unfiltered O(N^2) + // combinations and repeatedly evaluating the same track cuts. + std::vector pions; + pions.reserve(tracks.size()); + for (auto const& track : tracks) { + if (!trackSelect(track)) { + continue; + } + registry.fill(HIST("QA/TrackSelected/hPt"), track.pt()); + registry.fill(HIST("QA/TrackSelected/hEta"), track.eta()); + registry.fill(HIST("QA/TrackSelected/hDCAxy"), track.dcaXY()); + registry.fill(HIST("QA/TrackSelected/hDCAz"), track.dcaZ()); + registry.fill(HIST("QA/TrackSelected/hTPCCrossedRows"), track.tpcNClsCrossedRows()); + registry.fill(HIST("QA/TrackSelected/hTPCNSigmaPiVsP"), track.p(), track.tpcNSigmaPi()); + registry.fill(HIST("QA/TrackSelected/hTPCSignalVsP"), track.p(), track.tpcSignal()); + if constexpr (requires { track.tofNSigmaPi(); }) { + if (track.hasTOF()) { + registry.fill(HIST("QA/TrackSelected/hTOFNSigmaPiVsP"), track.p(), track.tofNSigmaPi()); + registry.fill(HIST("QA/TrackSelected/hTOFBetaVsP"), track.p(), track.beta()); + } + } + if (!pionSelect(track)) { + continue; + } + registry.fill(HIST("QA/SelectedPions/hTPCNSigmaPiVsP"), track.p(), track.tpcNSigmaPi()); + registry.fill(HIST("QA/SelectedPions/hTPCSignalVsP"), track.p(), track.tpcSignal()); + if constexpr (requires { track.tofNSigmaPi(); }) { + if (track.hasTOF()) { + registry.fill(HIST("QA/SelectedPions/hTOFNSigmaPiVsP"), track.p(), track.tofNSigmaPi()); + registry.fill(HIST("QA/SelectedPions/hTOFBetaVsP"), track.p(), track.beta()); + } + } + pions.push_back({track.px(), track.py(), track.pz(), static_cast(track.sign())}); + } + + const double mpi = o2::constants::physics::MassPionCharged; + for (size_t i = 0; i < pions.size(); ++i) { + for (size_t j = i + 1; j < pions.size(); ++j) { + auto const& track1 = pions[i]; + auto const& track2 = pions[j]; + P4 pion1(track1.px, track1.py, track1.pz, mpi); + P4 pion2(track2.px, track2.py, track2.pz, mpi); + auto pair = pion1 + pion2; + if (pair.Rapidity() < cfgMinRap.value || pair.Rapidity() > cfgMaxRap.value) { + continue; + } + fillPair(track1.sign, track2.sign, pair); + } + } + } + + template + void loopPairs(Tracks const& tracks, FillPair&& fillPair) + { + loopPairsSelected(tracks, [&](auto const& track) { return selectTrack(track); }, [&](auto const& track) { return selectPion(track); }, std::forward(fillPair)); + } + + void processPP(PPCollisions::iterator const& collision, TracksTPCTOF const& tracks, + aod::BCsWithTimestamps const&) + { + if (collisionSystem(collision) != CollisionSystem::PP) { + return; + } + const float multiplicity = collision.numContrib(); + registry.fill(HIST("PP/QA/Event/hSelection"), static_cast(PPEventSelectionStep::All)); + registry.fill(HIST("PP/QA/Event/hMultiplicityBefore"), multiplicity); + registry.fill(HIST("PP/QA/Event/hVertexZBefore"), collision.posZ()); + if (cfgRequireSel8.value && !collision.sel8()) { + return; + } + registry.fill(HIST("PP/QA/Event/hSelection"), static_cast(PPEventSelectionStep::Sel8)); + if (cfgRequireNoSameBunchPileup.value && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + return; + } + registry.fill(HIST("PP/QA/Event/hSelection"), static_cast(PPEventSelectionStep::NoSameBunchPileup)); + if (cfgRequireNoTimeFrameBorder.value && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return; + } + registry.fill(HIST("PP/QA/Event/hSelection"), static_cast(PPEventSelectionStep::NoTimeFrameBorder)); + if (cfgRequireNoITSROFrameBorder.value && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return; + } + registry.fill(HIST("PP/QA/Event/hSelection"), static_cast(PPEventSelectionStep::NoITSROFrameBorder)); + if (cfgRequireITSTPCVertex.value && !collision.selection_bit(aod::evsel::kIsVertexITSTPC)) { + return; + } + registry.fill(HIST("PP/QA/Event/hSelection"), static_cast(PPEventSelectionStep::ITSTPCVertex)); + if (collision.numContrib() < 1) { + return; + } + registry.fill(HIST("PP/QA/Event/hSelection"), static_cast(PPEventSelectionStep::Inelastic)); + registry.fill(HIST("PP/QA/Event/hMultiplicityAfter"), multiplicity); + registry.fill(HIST("PP/QA/Event/hVertexZAfter"), collision.posZ()); + + QVectorData qVector; + if (getQVectorData(collision, qVector) != QVectorStatus::Valid) { + return; + } + registry.fill(HIST("PP/QA/Event/hSelection"), static_cast(PPEventSelectionStep::QVectorValid)); + + const auto [qAx, qAy, qBx, qBy, qCx, qCy, qAMagnitude, qBMagnitude, qCMagnitude] = qVector; + + const int runNumber = collision.bc_as().runNumber(); + fillPPQVectorQA(multiplicity, runNumber, qAx, qAy, qBx, qBy, qCx, qCy); + + registry.fill(HIST("PP/pQAB"), multiplicity, qAx * qBx + qAy * qBy); + registry.fill(HIST("PP/pQAC"), multiplicity, qAx * qCx + qAy * qCy); + registry.fill(HIST("PP/pQBC"), multiplicity, qBx * qCx + qBy * qCy); + registry.fill(HIST("PP/pQABIm"), multiplicity, qAy * qBx - qAx * qBy); + registry.fill(HIST("PP/pQACIm"), multiplicity, qAy * qCx - qAx * qCy); + registry.fill(HIST("PP/pQBCIm"), multiplicity, qBy * qCx - qBx * qCy); + const bool validEventPlanes = qVector.hasValidEventPlanes(); + if (validEventPlanes) { + registry.fill(HIST("PP/pCosPsiAB"), multiplicity, (qAx * qBx + qAy * qBy) / (qAMagnitude * qBMagnitude)); + registry.fill(HIST("PP/pCosPsiAC"), multiplicity, (qAx * qCx + qAy * qCy) / (qAMagnitude * qCMagnitude)); + registry.fill(HIST("PP/pCosPsiBC"), multiplicity, (qBx * qCx + qBy * qCy) / (qBMagnitude * qCMagnitude)); + } + + loopPairs(tracks, [&](int8_t sign1, int8_t sign2, P4 const& pair) { + const double ux = std::cos(2. * pair.Phi()); + const double uy = std::sin(2. * pair.Phi()); + const double sp = ux * qAx + uy * qAy; + const double ep = validEventPlanes ? sp / qAMagnitude : 0.; + if (sign1 * sign2 < 0) { + registry.fill(HIST("PP/hMassUS"), pair.M(), pair.Pt(), multiplicity); + registry.fill(HIST("PP/hSpNumUS"), pair.M(), pair.Pt(), multiplicity, sp); + if (validEventPlanes) { + registry.fill(HIST("PP/hEpNumUS"), pair.M(), pair.Pt(), multiplicity, ep); + } + } else if (sign1 > 0 && sign2 > 0) { + registry.fill(HIST("PP/hMassLSpp"), pair.M(), pair.Pt(), multiplicity); + registry.fill(HIST("PP/hSpNumLSpp"), pair.M(), pair.Pt(), multiplicity, sp); + if (validEventPlanes) { + registry.fill(HIST("PP/hEpNumLSpp"), pair.M(), pair.Pt(), multiplicity, ep); + } + } else if (sign1 < 0 && sign2 < 0) { + registry.fill(HIST("PP/hMassLSmm"), pair.M(), pair.Pt(), multiplicity); + registry.fill(HIST("PP/hSpNumLSmm"), pair.M(), pair.Pt(), multiplicity, sp); + if (validEventPlanes) { + registry.fill(HIST("PP/hEpNumLSmm"), pair.M(), pair.Pt(), multiplicity, ep); + } + } + }); + } + PROCESS_SWITCH(F0980FlowSP, processPP, "Process pp invariant-mass scalar-product and event-plane analyses", true); + + void processOO(OOCollisions::iterator const& collision, TracksTPCTOF const& tracks, + aod::BCsWithTimestamps const&) + { + if (collisionSystem(collision) != CollisionSystem::OO) { + return; + } + const float centrality = collision.centFT0C(); + if (!selectOOEventWithQA(collision, centrality)) { + return; + } + + QVectorData qVector; + const auto qVectorStatus = getQVectorData(collision, qVector); + if (qVectorStatus == QVectorStatus::IndexUnavailable || qVectorStatus == QVectorStatus::Uncalibrated) { + return; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::QVectorCalibrated)); + if (qVectorStatus != QVectorStatus::Valid) { + return; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::QVectorValid)); + + const auto [qAx, qAy, qBx, qBy, qCx, qCy, qAMagnitude, qBMagnitude, qCMagnitude] = qVector; + + const int runNumber = collision.bc_as().runNumber(); + fillQVectorQA(centrality, runNumber, qAx, qAy, qBx, qBy, qCx, qCy); + registry.fill(HIST("OO/pQAB"), centrality, qAx * qBx + qAy * qBy); + registry.fill(HIST("OO/pQAC"), centrality, qAx * qCx + qAy * qCy); + registry.fill(HIST("OO/pQBC"), centrality, qBx * qCx + qBy * qCy); + registry.fill(HIST("OO/pQABIm"), centrality, qAy * qBx - qAx * qBy); + registry.fill(HIST("OO/pQACIm"), centrality, qAy * qCx - qAx * qCy); + registry.fill(HIST("OO/pQBCIm"), centrality, qBy * qCx - qBx * qCy); + const bool validEventPlanes = qVector.hasValidEventPlanes(); + if (validEventPlanes) { + registry.fill(HIST("OO/pCosPsiAB"), centrality, (qAx * qBx + qAy * qBy) / (qAMagnitude * qBMagnitude)); + registry.fill(HIST("OO/pCosPsiAC"), centrality, (qAx * qCx + qAy * qCy) / (qAMagnitude * qCMagnitude)); + registry.fill(HIST("OO/pCosPsiBC"), centrality, (qBx * qCx + qBy * qCy) / (qBMagnitude * qCMagnitude)); + } + + loopPairs(tracks, [&](int8_t sign1, int8_t sign2, P4 const& pair) { + const double ux = std::cos(2. * pair.Phi()); + const double uy = std::sin(2. * pair.Phi()); + const double sp = ux * qAx + uy * qAy; + const double ep = validEventPlanes ? (ux * qAx + uy * qAy) / qAMagnitude : 0.; + if (sign1 * sign2 < 0) { + registry.fill(HIST("OO/hMassUS"), pair.M(), pair.Pt(), centrality); + registry.fill(HIST("OO/hSpNumUS"), pair.M(), pair.Pt(), centrality, sp); + if (validEventPlanes) { + registry.fill(HIST("OO/hEpNumUS"), pair.M(), pair.Pt(), centrality, ep); + } + } else if (sign1 > 0 && sign2 > 0) { + registry.fill(HIST("OO/hMassLSpp"), pair.M(), pair.Pt(), centrality); + registry.fill(HIST("OO/hSpNumLSpp"), pair.M(), pair.Pt(), centrality, sp); + if (validEventPlanes) { + registry.fill(HIST("OO/hEpNumLSpp"), pair.M(), pair.Pt(), centrality, ep); + } + } else if (sign1 < 0 && sign2 < 0) { + registry.fill(HIST("OO/hMassLSmm"), pair.M(), pair.Pt(), centrality); + registry.fill(HIST("OO/hSpNumLSmm"), pair.M(), pair.Pt(), centrality, sp); + if (validEventPlanes) { + registry.fill(HIST("OO/hEpNumLSmm"), pair.M(), pair.Pt(), centrality, ep); + } + } + }); + } + PROCESS_SWITCH(F0980FlowSP, processOO, "Process OO invariant-mass scalar-product and event-plane analyses", true); + + void processPO(POCollisions::iterator const& collision, TracksTPCTOF const& tracks, + aod::BCsWithTimestamps const&) + { + if (collisionSystem(collision) != CollisionSystem::PO) { + return; + } + const float centrality = collision.centFT0C(); + if (!selectOOEventWithQA(collision, centrality)) { + return; + } + + QVectorData qVector; + const auto qVectorStatus = getQVectorData(collision, qVector); + if (qVectorStatus == QVectorStatus::IndexUnavailable || qVectorStatus == QVectorStatus::Uncalibrated) { + return; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::QVectorCalibrated)); + if (qVectorStatus != QVectorStatus::Valid) { + return; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::QVectorValid)); + + const auto [qAx, qAy, qBx, qBy, qCx, qCy, qAMagnitude, qBMagnitude, qCMagnitude] = qVector; + + const int runNumber = collision.bc_as().runNumber(); + fillQVectorQA(centrality, runNumber, qAx, qAy, qBx, qBy, qCx, qCy); + registry.fill(HIST("PO/pQAB"), centrality, qAx * qBx + qAy * qBy); + registry.fill(HIST("PO/pQAC"), centrality, qAx * qCx + qAy * qCy); + registry.fill(HIST("PO/pQBC"), centrality, qBx * qCx + qBy * qCy); + registry.fill(HIST("PO/pQABIm"), centrality, qAy * qBx - qAx * qBy); + registry.fill(HIST("PO/pQACIm"), centrality, qAy * qCx - qAx * qCy); + registry.fill(HIST("PO/pQBCIm"), centrality, qBy * qCx - qBx * qCy); + const bool validEventPlanes = qVector.hasValidEventPlanes(); + if (validEventPlanes) { + registry.fill(HIST("PO/pCosPsiAB"), centrality, (qAx * qBx + qAy * qBy) / (qAMagnitude * qBMagnitude)); + registry.fill(HIST("PO/pCosPsiAC"), centrality, (qAx * qCx + qAy * qCy) / (qAMagnitude * qCMagnitude)); + registry.fill(HIST("PO/pCosPsiBC"), centrality, (qBx * qCx + qBy * qCy) / (qBMagnitude * qCMagnitude)); + } + + loopPairs(tracks, [&](int8_t sign1, int8_t sign2, P4 const& pair) { + const double ux = std::cos(2. * pair.Phi()); + const double uy = std::sin(2. * pair.Phi()); + const double sp = ux * qAx + uy * qAy; + const double ep = validEventPlanes ? sp / qAMagnitude : 0.; + if (sign1 * sign2 < 0) { + registry.fill(HIST("PO/hMassUS"), pair.M(), pair.Pt(), centrality); + registry.fill(HIST("PO/hSpNumUS"), pair.M(), pair.Pt(), centrality, sp); + if (validEventPlanes) { + registry.fill(HIST("PO/hEpNumUS"), pair.M(), pair.Pt(), centrality, ep); + } + } else if (sign1 > 0 && sign2 > 0) { + registry.fill(HIST("PO/hMassLSpp"), pair.M(), pair.Pt(), centrality); + registry.fill(HIST("PO/hSpNumLSpp"), pair.M(), pair.Pt(), centrality, sp); + if (validEventPlanes) { + registry.fill(HIST("PO/hEpNumLSpp"), pair.M(), pair.Pt(), centrality, ep); + } + } else if (sign1 < 0 && sign2 < 0) { + registry.fill(HIST("PO/hMassLSmm"), pair.M(), pair.Pt(), centrality); + registry.fill(HIST("PO/hSpNumLSmm"), pair.M(), pair.Pt(), centrality, sp); + if (validEventPlanes) { + registry.fill(HIST("PO/hEpNumLSmm"), pair.M(), pair.Pt(), centrality, ep); + } + } + }); + } + PROCESS_SWITCH(F0980FlowSP, processPO, "Process pO invariant-mass scalar-product and event-plane analyses", true); + + void processOOTPCOnly(OOCollisions::iterator const& collision, TracksTPC const& tracks, + aod::BCsWithTimestamps const&) + { + if (collisionSystem(collision) != CollisionSystem::OO) { + return; + } + const float centrality = collision.centFT0C(); + if (!selectOOEventWithQA(collision, centrality)) { + return; + } + + QVectorData qVector; + const auto qVectorStatus = getQVectorData(collision, qVector); + if (qVectorStatus == QVectorStatus::IndexUnavailable || qVectorStatus == QVectorStatus::Uncalibrated) { + return; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::QVectorCalibrated)); + if (qVectorStatus != QVectorStatus::Valid) { + return; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::QVectorValid)); + + const auto [qAx, qAy, qBx, qBy, qCx, qCy, qAMagnitude, qBMagnitude, qCMagnitude] = qVector; + + const int runNumber = collision.bc_as().runNumber(); + fillQVectorQA(centrality, runNumber, qAx, qAy, qBx, qBy, qCx, qCy); + registry.fill(HIST("OO/pQAB"), centrality, qAx * qBx + qAy * qBy); + registry.fill(HIST("OO/pQAC"), centrality, qAx * qCx + qAy * qCy); + registry.fill(HIST("OO/pQBC"), centrality, qBx * qCx + qBy * qCy); + registry.fill(HIST("OO/pQABIm"), centrality, qAy * qBx - qAx * qBy); + registry.fill(HIST("OO/pQACIm"), centrality, qAy * qCx - qAx * qCy); + registry.fill(HIST("OO/pQBCIm"), centrality, qBy * qCx - qBx * qCy); + const bool validEventPlanes = qVector.hasValidEventPlanes(); + if (validEventPlanes) { + registry.fill(HIST("OO/pCosPsiAB"), centrality, (qAx * qBx + qAy * qBy) / (qAMagnitude * qBMagnitude)); + registry.fill(HIST("OO/pCosPsiAC"), centrality, (qAx * qCx + qAy * qCy) / (qAMagnitude * qCMagnitude)); + registry.fill(HIST("OO/pCosPsiBC"), centrality, (qBx * qCx + qBy * qCy) / (qBMagnitude * qCMagnitude)); + } + + loopPairsSelected(tracks, [&](auto const& track) { return selectTrack(track); }, [&](auto const& track) { return selectPionTPCOnly(track); }, [&](int8_t sign1, int8_t sign2, P4 const& pair) { + const double ux = std::cos(2. * pair.Phi()); + const double uy = std::sin(2. * pair.Phi()); + const double sp = ux * qAx + uy * qAy; + const double ep = validEventPlanes ? (ux * qAx + uy * qAy) / qAMagnitude : 0.; + if (sign1 * sign2 < 0) { + registry.fill(HIST("OO/hMassUS"), pair.M(), pair.Pt(), centrality); + registry.fill(HIST("OO/hSpNumUS"), pair.M(), pair.Pt(), centrality, sp); + if (validEventPlanes) { + registry.fill(HIST("OO/hEpNumUS"), pair.M(), pair.Pt(), centrality, ep); + } + } else if (sign1 > 0 && sign2 > 0) { + registry.fill(HIST("OO/hMassLSpp"), pair.M(), pair.Pt(), centrality); + registry.fill(HIST("OO/hSpNumLSpp"), pair.M(), pair.Pt(), centrality, sp); + if (validEventPlanes) { + registry.fill(HIST("OO/hEpNumLSpp"), pair.M(), pair.Pt(), centrality, ep); + } + } else if (sign1 < 0 && sign2 < 0) { + registry.fill(HIST("OO/hMassLSmm"), pair.M(), pair.Pt(), centrality); + registry.fill(HIST("OO/hSpNumLSmm"), pair.M(), pair.Pt(), centrality, sp); + if (validEventPlanes) { + registry.fill(HIST("OO/hEpNumLSmm"), pair.M(), pair.Pt(), centrality, ep); + } + } }); + } + PROCESS_SWITCH(F0980FlowSP, processOOTPCOnly, "Process OO with TPC-only pion PID", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/Tasks/Resonances/f0980pbpbanalysis.cxx b/PWGLF/Tasks/Resonances/f0980pbpbanalysis.cxx index 832b4e47a1a..6166c39a314 100644 --- a/PWGLF/Tasks/Resonances/f0980pbpbanalysis.cxx +++ b/PWGLF/Tasks/Resonances/f0980pbpbanalysis.cxx @@ -17,6 +17,7 @@ #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseITS.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/Qvectors.h" @@ -38,6 +39,7 @@ #include #include #include +#include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include @@ -68,8 +70,9 @@ struct F0980pbpbanalysis { {}, OutputObjHandlingPolicy::AnalysisObject}; - Service ccdb; + Service ccdb{}; o2::ccdb::CcdbApi ccdbApi; + o2::aod::ITSResponse itsResponse; Configurable cfgURL{"cfgURL", "http://alice-ccdb.cern.ch", "Address of the CCDB to browse"}; Configurable ccdbNoLaterThan{"ccdbNoLaterThan", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "Latest acceptable timestamp of creation for the object"}; @@ -130,6 +133,8 @@ struct F0980pbpbanalysis { Configurable cfgPIDMaxTPCnSigmaPion{"cfgPIDMaxTPCnSigmaPion", 5.0, "TPC nSigma cut for Pion"}; // TPC Configurable cfgPIDMaxTPCnSigmaPionS{"cfgPIDMaxTPCnSigmaPionS", 3.0, "TPC nSigma cut for Pion as a standalone"}; Configurable cfgPIDMaxTiednSigmaPion{"cfgPIDMaxTiednSigmaPion", 3.0, "Combined nSigma cut for Pion"}; + Configurable cfgPIDITSFlag{"cfgPIDITSFlag", true, "PID ITS flag"}; + Configurable cfgPIDMaxITSnSigmaPion{"cfgPIDMaxITSnSigmaPion", 3.0, "ITS nSigma selection"}; } PIDConfig; // Flow Configurables @@ -198,27 +203,27 @@ struct F0980pbpbanalysis { int nmode = FlowConfig.cfgQvecNMods; static constexpr double QvecAmpMin = 1e-4; - int detId; - int refAId; - int refBId; + int detId = 0; + int refAId = 4; + int refBId = 5; - int qVecDetInd; - int qVecRefAInd; - int qVecRefBInd; + int qVecDetInd = 0; + int qVecRefAInd = 0; + int qVecRefBInd = 0; - double eventPlaneDet; - double eventPlaneRefA; - double eventPlaneRefB; + double eventPlaneDet = 0.; + double eventPlaneRefA = 0.; + double eventPlaneRefB = 0.; - float centrality; + float centrality = 1.; - double angle; - double relPhi; - double relPhiRot; - double relPhiMix; + double angle = -1.; + double relPhi = -1.; + double relPhiRot = -1.; + double relPhiMix = -1.; // double massPi = o2::constants::physics::MassPionCharged; - double massPtl; + double massPtl = 0.; int nTotalEvents = 0; @@ -231,6 +236,7 @@ struct F0980pbpbanalysis { PIDRun3 = 0, PIDRun2 = 1, PIDTest = 2, + PIDAddITS = 3, }; enum PtlList { @@ -262,19 +268,23 @@ struct F0980pbpbanalysis { { if (name.value == "FT0C") { return 0; - } else if (name.value == "FT0A") { + } + if (name.value == "FT0A") { return 1; - } else if (name.value == "FT0M") { + } + if (name.value == "FT0M") { return 2; - } else if (name.value == "FV0A") { + } + if (name.value == "FV0A") { return 3; - } else if (name.value == "TPCpos") { + } + if (name.value == "TPCpos") { return 4; - } else if (name.value == "TPCneg") { + } + if (name.value == "TPCneg") { return 5; - } else { - return 0; } + return 0; } template @@ -372,45 +382,45 @@ struct F0980pbpbanalysis { if (std::abs(collision.posZ()) > EventConfig.cfgEventCutVertex) { return 0; } - if (QAConfig.cfgQAEventFlowCut && QA) + if (QAConfig.cfgQAEventFlowCut && QA) { histos.fill(HIST("EventQA/hnEvents"), 1); // Vertex cut - + } if (!collision.sel8()) { return 0; } - if (QAConfig.cfgQAEventFlowCut && QA) + if (QAConfig.cfgQAEventFlowCut && QA) { histos.fill(HIST("EventQA/hnEvents"), 2); // sel8 cut - + } if (EventConfig.cfgEventGoodZvtxSel && !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { return 0; } - if (QAConfig.cfgQAEventFlowCut && QA) + if (QAConfig.cfgQAEventFlowCut && QA) { histos.fill(HIST("EventQA/hnEvents"), 3); // Good Zvtx cut - + } if (EventConfig.cfgEventNSamePileupSel && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { return 0; } - if (QAConfig.cfgQAEventFlowCut && QA) + if (QAConfig.cfgQAEventFlowCut && QA) { histos.fill(HIST("EventQA/hnEvents"), 4); // Same bunch pileup cut - + } if (EventConfig.cfgEventNCollinTRSel && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { return 0; } - if (QAConfig.cfgQAEventFlowCut && QA) + if (QAConfig.cfgQAEventFlowCut && QA) { histos.fill(HIST("EventQA/hnEvents"), 5); // Collision time range standard cut - + } if (EventConfig.cfgEventQvecSel && (collision.qvecAmp()[detId] < QvecAmpMin || collision.qvecAmp()[refAId] < QvecAmpMin || collision.qvecAmp()[refBId] < QvecAmpMin)) { return 0; } - if (QAConfig.cfgQAEventFlowCut && QA) + if (QAConfig.cfgQAEventFlowCut && QA) { histos.fill(HIST("EventQA/hnEvents"), 6); // Qvector cut - + } if (EventConfig.cfgEventOccupancySel && (collision.trackOccupancyInTimeRange() > EventConfig.cfgEventOccupancyMax || collision.trackOccupancyInTimeRange() < EventConfig.cfgEventOccupancyMin)) { return 0; } - if (QAConfig.cfgQAEventFlowCut && QA) + if (QAConfig.cfgQAEventFlowCut && QA) { histos.fill(HIST("EventQA/hnEvents"), 7); // Occupancy cut - + } if (EventConfig.cfgEventCentMax < centrality) { return 0; } @@ -423,9 +433,9 @@ struct F0980pbpbanalysis { return 0; } */ - if (QAConfig.cfgQAEventFlowCut && QA) + if (QAConfig.cfgQAEventFlowCut && QA) { histos.fill(HIST("EventQA/hnEvents"), 8); // Centrality cut - + } if (EventConfig.cfgEventPVSel && std::abs(collision.posZ()) > EventConfig.cfgEventPV) { return 0; } @@ -440,27 +450,31 @@ struct F0980pbpbanalysis { template bool trackSelected(const TrackType& track, const bool QA) { - if (QAConfig.cfgQATrackCut && QA) + if (QAConfig.cfgQATrackCut && QA) { fillQA(false, track, 3); - if (QAConfig.cfgQATrackFlowCut && QA) + } + if (QAConfig.cfgQATrackFlowCut && QA) { histos.fill(HIST("TrackQA/hnTracks"), 0); // All passed // // Kinematic + } if (std::abs(track.pt()) < TrackConfig.cfgTrackPtMin) { return 0; } if (std::abs(track.eta()) > TrackConfig.cfgTrackEtaMax) { return 0; } - if (QAConfig.cfgQATrackFlowCut && QA) + if (QAConfig.cfgQATrackFlowCut && QA) { histos.fill(HIST("TrackQA/hnTracks"), 1); // Kinematic // Primary Vertex(PV) Contributing to the PV fit + } if (TrackConfig.cfgTrackIsPVContributor && !track.isPVContributor()) { return 0; } - if (QAConfig.cfgQATrackFlowCut && QA) + if (QAConfig.cfgQATrackFlowCut && QA) { histos.fill(HIST("TrackQA/hnTracks"), 2); // PV Contributor // Global Track without DCA + } if (TrackConfig.cfgTrackIsGlobalWoDCATrack && !track.isGlobalTrackWoDCA()) { return 0; } @@ -476,9 +490,10 @@ struct F0980pbpbanalysis { if (TrackConfig.cfgTrackITSChi2 > 0 && track.itsChi2NCl() > TrackConfig.cfgTrackITSChi2) { return 0; } - if (QAConfig.cfgQATrackFlowCut && QA) + if (QAConfig.cfgQATrackFlowCut && QA) { histos.fill(HIST("TrackQA/hnTracks"), 4); // GlobalWoDCATrack // DCA cuts + } if (TrackConfig.cfgTrackDCArDepPTSel) { if (std::abs(track.dcaXY()) > (TrackConfig.cfgTrackDCArDepPTP0 + (TrackConfig.cfgTrackDCArDepPTExp / track.pt()))) { return 0; @@ -571,6 +586,16 @@ struct F0980pbpbanalysis { return 0; } } + } else if (cfgListPID == PIDList::PIDAddITS) { + if (!track.hasTOF()) { + return 0; + } + if ((getTpcNSigma(track) * getTpcNSigma(track) + getTofNSigma(track) * getTofNSigma(track)) > (PIDConfig.cfgPIDMaxTiednSigmaPion * PIDConfig.cfgPIDMaxTiednSigmaPion)) { + return 0; + } + if (!PIDConfig.cfgPIDITSFlag || std::abs(itsResponse.nSigmaITS(track)) > PIDConfig.cfgPIDMaxITSnSigmaPion) { + return 0; + } } if (QAConfig.cfgQATrackFlowCut && QA) { histos.fill(HIST("TrackQA/hnTracks"), 8); // PID @@ -582,15 +607,22 @@ struct F0980pbpbanalysis { template bool pairAngleSelection(const TrackType1& track1, const TrackType2& track2) { - double pt1, pt2, pz1, pz2, p1, p2, angle; + double pt1 = 0.; + double pt2 = 0.; + double pz1 = 0.; + double pz2 = 0.; + double p1 = 0.; + double p2 = 0.; + double angle12 = 0.; + pt1 = track1.pt(); pt2 = track2.pt(); pz1 = track1.pz(); pz2 = track2.pz(); p1 = track1.p(); p2 = track2.p(); - angle = std::acos((pt1 * pt2 + pz1 * pz2) / (p1 * p2)); - if (PhiTestConfig.cfgPhiDeepAngleSel && angle < PhiTestConfig.cfgPhiDeepAngle) { + angle12 = std::acos((pt1 * pt2 + pz1 * pz2) / (p1 * p2)); + if (PhiTestConfig.cfgPhiDeepAngleSel && angle12 < PhiTestConfig.cfgPhiDeepAngle) { return 0; } return 1; @@ -601,9 +633,8 @@ struct F0980pbpbanalysis { { if (cfgListPtl == PtlList::PtlPion) { return track.tpcNSigmaPi(); - } else { - return track.tpcNSigmaKa(); } + return track.tpcNSigmaKa(); } template @@ -611,9 +642,8 @@ struct F0980pbpbanalysis { { if (cfgListPtl == PtlList::PtlPion) { return track.tofNSigmaPi(); - } else { - return track.tofNSigmaKa(); } + return track.tofNSigmaKa(); } template @@ -634,9 +664,9 @@ struct F0980pbpbanalysis { if (!trackSelected(trk1, true)) { continue; } - if (QAConfig.cfgQATrackCut) + if (QAConfig.cfgQATrackCut) { fillQA(true, trk1, 3); - + } if (!selectionPID(trk1, true)) { continue; } @@ -706,7 +736,7 @@ struct F0980pbpbanalysis { qVecDetInd = detId * 4 + 3 + (nmode - 2) * FlowConfig.cfgQvecNum * 4; auto trackTuple = std::make_tuple(tracks); - BinningTypeVertexContributor binningOnPositions{{mixAxisVertex, mixAxisCent}, true}; + BinningTypeVertexContributor binningOnPositions{{mixAxisVertex, mixAxisCent}}; SameKindPair pair{binningOnPositions, BkgMethodConfig.cfgBkgMixedNum, -1, collisions, trackTuple, &cache}; ROOT::Math::PxPyPzMVector ptl1, ptl2, recoPtl; for (const auto& [c1, t1, c2, t2] : pair) { @@ -721,7 +751,7 @@ struct F0980pbpbanalysis { if (c1.bcId() == c2.bcId()) { continue; } - double eventPlaneDet = std::atan2(c1.qvecIm()[qVecDetInd], c1.qvecRe()[qVecDetInd]) / static_cast(nmode); + eventPlaneDet = std::atan2(c1.qvecIm()[qVecDetInd], c1.qvecRe()[qVecDetInd]) / static_cast(nmode); for (const auto& trk1 : t1) { if (!trackSelected(trk1, false)) { diff --git a/PWGLF/Tasks/Resonances/f0phiproxy.cxx b/PWGLF/Tasks/Resonances/f0phiproxy.cxx index b4f97e7180f..1792f20da65 100644 --- a/PWGLF/Tasks/Resonances/f0phiproxy.cxx +++ b/PWGLF/Tasks/Resonances/f0phiproxy.cxx @@ -10,9 +10,11 @@ // or submit itself to any jurisdiction. /// \file f0phiproxy.cxx -/// \brief task for dipion f0 candidates with a half-momentum kaon proxy and bachelor kaon. +/// \brief Dipion-based half-momentum kaon proxies with kaon/phi and pion/K*0 bachelors, in data and MC. /// \author Sushanta Tripathy +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseTOF.h" @@ -22,23 +24,38 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include #include +#include +#include #include +#include #include +#include +#include #include #include @@ -46,289 +63,826 @@ using namespace o2; using namespace o2::framework; struct F0phiproxy { - using Collisions = soa::Join; - using Tracks = - soa::Join; + using Collisions = soa::Join; + using Tracks = soa::Join; + using CollisionsMC = soa::Join; + using TracksMC = soa::Join; using FourVector = ROOT::Math::PxPyPzMVector; Preslice perCollision = aod::track::collisionId; - HistogramRegistry histos{ - "histos", - {}, - OutputObjHandlingPolicy::AnalysisObject}; - Configurable vertexMax{"vertexMax", 10.f, - "Maximum absolute vertex z (cm)"}; + Preslice perCollisionMC = aod::track::collisionId; + Service pdgDB{}; + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + Configurable vertexMax{"vertexMax", 10.f, "Maximum absolute vertex z (cm)"}; Configurable requireSel8{"requireSel8", true, "Require sel8"}; - Configurable requireINELgt0{"requireINELgt0", true, - "Require at least one PV track in |eta|<1"}; - Configurable trackPtMin{"trackPtMin", 0.15f, - "Minimum track pT (GeV/c)"}; + Configurable requireINELgt0{"requireINELgt0", true, "Require at least one PV track in |eta|<1; MC gen uses charged physical primaries"}; + Configurable requireNoSameBunchPileup{"requireNoSameBunchPileup", false, "Require kNoSameBunchPileup, as in Lambda-star trigger mode 4"}; + Configurable requireGoodZvtxFT0vsPV{"requireGoodZvtxFT0vsPV", false, "Require kIsGoodZvtxFT0vsPV, as in Lambda-star trigger mode 4"}; + Configurable useFT0M{"useFT0M", true, "Use calibrated FT0M percentiles for reconstructed data/MC; false retains legacy NPV axes and mixing"}; + ConfigurableAxis ft0mAxis{"ft0mAxis", {0., 0., 1., 5., 10., 15., 20., 30., 40., 50., 70., 100.}, "FT0M percentile classes; first zero selects variable-width bins; must cover [0,100]"}; + Configurable trackPtMin{"trackPtMin", 0.15f, "Minimum track pT (GeV/c)"}; Configurable trackEtaMax{"trackEtaMax", 0.8f, "Maximum track |eta|"}; - Configurable requireGlobalTrack{"requireGlobalTrack", true, - "Require global track selection"}; - Configurable requirePVContributor{"requirePVContributor", true, - "Require PV contributor"}; + Configurable requireGlobalTrack{"requireGlobalTrack", true, "Require global track selection"}; + Configurable requirePVContributor{"requirePVContributor", true, "Require PV contributor"}; Configurable pionTPC{"pionTPC", 3.f, "Pion TPC nSigma cut"}; Configurable pionTOF{"pionTOF", 3.f, "Pion TOF veto when matched"}; Configurable kaonTPC{"kaonTPC", 3.f, "Kaon TPC nSigma cut"}; Configurable kaonTOF{"kaonTOF", 3.f, "Kaon TOF veto when matched"}; Configurable requireTOF{"requireTOF", false, "Require TOF for both species instead of veto-only"}; Configurable dipionYMax{"dipionYMax", 0.5f, "Maximum dipion |y|"}; - Configurable proxyYMax{"proxyYMax", 0.5f, - "Maximum proxy+bachelor |y|"}; - Configurable dipionMassMin{"dipionMassMin", 0.6f, - "Lower dipion mass for proxy construction"}; - Configurable dipionMassMax{"dipionMassMax", 1.4f, - "Upper dipion mass for proxy construction"}; - Configurable mixDepth{"mixDepth", 5, "Previous events per mixing bin; zero disables mixing"}; + Configurable proxyYMax{"proxyYMax", 0.5f, "Maximum proxy+bachelor and standard-control |y|"}; + Configurable dipionMassMin{"dipionMassMin", 0.6f, "Lower dipion mass for proxy construction"}; + Configurable dipionMassMax{"dipionMassMax", 1.4f, "Upper dipion mass for proxy construction"}; + Configurable mixDepth{"mixDepth", 5, "Previous events per mixing bin; zero disables ME booking and mixing"}; Configurable mixZWidth{"mixZWidth", 2.f, "Vertex bin width (cm)"}; - Configurable mixMultWidth{"mixMultWidth", 20.f, - "PV multiplicity bin width"}; + Configurable mixMultWidth{"mixMultWidth", 20.f, "PV multiplicity bin width"}; + Configurable mixFT0MWidth{"mixFT0MWidth", 1.f, "FT0M mixing width in percentage points; pools also respect analysis-class boundaries"}; + + Configurable enablePhiProxy{"enablePhiProxy", true, "Dipion/2 as on-shell K plus bachelor K"}; + Configurable enableKstarProxy{"enableKstarProxy", false, "Dipion/2 as on-shell K plus bachelor pion; use the dedicated Kstar configuration"}; + Configurable enablePhiControls{"enablePhiControls", true, "Standard KK SE/ME OS/LS spectra"}; + Configurable enableKstarControls{"enableKstarControls", false, "Standard K pi SE/ME OS/LS spectra"}; + Configurable enableCompactProxy{"enableCompactProxy", true, "Proxy maps retaining mass, pT, dipion mass, grouped multiplicity, charges and TOF"}; + Configurable enableFullProxySparse{"enableFullProxySparse", false, "Diagnostic 7D maps including multiplicity, bachelor charge and TOF; expensive to merge"}; + Configurable enablePidQA{"enablePidQA", false, "Book/fill the four PID QA histograms"}; + Configurable multiplicityBins{"multiplicityBins", 100, "Multiplicity bins in dipion/control and full diagnostic maps; identical settings required for merging"}; + Configurable proxyDipionMassBins{"proxyDipionMassBins", 300, "Dipion mass bins in compact proxy maps; default preserves original 5 MeV resolution"}; + ConfigurableAxis proxyMultiplicityAxis{"proxyMultiplicityAxis", {0., 0., 20., 50., 200., 1000000.}, "Compact proxy NPV intervals; first zero selects variable-width bins"}; + ConfigurableAxis proxyPtAxis{"proxyPtAxis", {0., 0., 0.3, 1., 2., 3., 4., 6., 10.}, "Compact proxy pT intervals covering both the data and Kstar-study selections"}; + Configurable kstarMassBins{"kstarMassBins", 200, "K pi and Kstar-proxy mass bins over 0.6-1.4 GeV; 200 gives the study's 4 MeV bins"}; + Configurable mcPrimaryOnly{"mcPrimaryOnly", true, "Generated daughters must be physical primaries; strong-decay daughters are included"}; + Configurable mcF0PdgCode{"mcF0PdgCode", 0, "0 resolves f0_980 from the O2 PDG service; override only for a verified generator convention"}; + static constexpr size_t Phi = 0; + static constexpr size_t Kstar = 1; + static constexpr size_t NChannels = 2; + static constexpr size_t NCharges = 3; + static constexpr size_t NMixClasses = 2; + static constexpr size_t NTruthClasses = 4; + static constexpr double ProxyMomentumScale = 0.5; + static constexpr int PdgPion = static_cast(PDG_t::kPiPlus); + static constexpr int PdgKaon = static_cast(PDG_t::kKPlus); + static constexpr int PdgPhi = static_cast(constants::physics::Pdg::kPhi); + static constexpr int PdgKstar = static_cast(constants::physics::Pdg::kK0Star892); + static constexpr size_t TwoBodyDaughters = 2; + static constexpr int IonCodeThreshold = 1000000000; + static constexpr int IonZDivisor = 10000; + static constexpr int IonZModulo = 1000; + static constexpr float FT0MPercentileMax = 100.f; + static constexpr size_t MinAxisSpecificationSize = 3; + static constexpr double MaxUniformFT0MBins = 10000.; + int resolvedF0Code = 0; + std::vector ft0mEdges{}; + + struct TruthInfo { + int64_t id = -1; + int collisionId = -1; + int pdg = 0; + double px = 0., py = 0., pz = 0.; + bool primary = false; + std::vector> parents{}; + }; struct TrackCandidate { - int64_t id; - int sign; - double px, py, pz; - bool hasTOF; - [[nodiscard]] FourVector vector(double mass) const - { - return {px, py, pz, mass}; - } + int64_t id = -1; + int sign = 0; + double px = 0., py = 0., pz = 0.; + bool hasTOF = false; + TruthInfo truth{}; + [[nodiscard]] FourVector vector(double mass) const { return {px, py, pz, mass}; } }; struct Event { - std::vector pions, kaons; + std::vector pions{}, kaons{}; }; + using MixingKey = std::tuple; + enum EventStage : int { Received = 1, + VertexAccepted, + Sel8Accepted, + InelAccepted, + QualityAccepted, + ActivityAccepted, + NEventStages = ActivityAccepted }; + std::shared_ptr eventSelection{}, eventStages{}, selectedEvents{}, ft0mStatusQA{}, mixPartners{}; + std::shared_ptr eventStagesByActivity{}, activityVsNPV{}, activityVsVertex{}, mixPartnersByActivity{}; + std::array, 4> pidQA{}; + std::array, 4> pidQAByActivity{}; + + std::array, NMixClasses> dipionSpectra{}; + std::array, NMixClasses>, NChannels> compactProxy{}, fullProxy{}; + std::array, NCharges>, NMixClasses>, NChannels> controls{}; + std::array, NTruthClasses>, NChannels> recoComponents{}; + std::array, NChannels> recoTrueParent{}, recoPtResponse{}, proxyPtResponse{}; + std::shared_ptr recoTrueF0{}; + std::array, NTruthClasses>, NChannels> recoComponentsByActivity{}; + std::array, NChannels> recoTrueParentByActivity{}, recoPtResponseByActivity{}, proxyPtResponseByActivity{}; + std::shared_ptr recoTrueF0ByActivity{}; + enum GenSpectrum : size_t { GenDipion, + GenF0, + GenPhi, + GenPhiTrue, + GenKstar, + GenKstarTrue, + GenProxyPhi, + GenProxyPhiF0, + GenProxyKstar, + GenProxyKstarF0, + NGenSpectra }; + std::array, NGenSpectra> generated{}; + + bool mixingEnabled() const { return mixDepth.value > 0; } + bool channelEnabled(size_t channel) const { return channel == Phi ? enablePhiProxy.value : enableKstarProxy.value; } + bool controlEnabled(size_t channel) const { return channel == Phi ? enablePhiControls.value : enableKstarControls.value; } + static double bachelorMass(size_t channel) { return channel == Phi ? constants::physics::MassKaonCharged : constants::physics::MassPionCharged; } + static int bachelorPdg(size_t channel) { return channel == Phi ? PdgKaon : PdgPion; } + + std::string recoName(std::string const& name) const + { + return useFT0M.value ? std::string("FT0M/").append(name) : name; + } + + static int ft0mStatus(float percentile) + { + if (!std::isfinite(percentile)) { + return 1; + } + if (percentile < 0.f) { + return 2; + } + return percentile < FT0MPercentileMax ? 0 : 3; + } + + static std::vector resolveFT0MEdges(std::vector const& specification) + { + if (specification.size() < MinAxisSpecificationSize || + !std::all_of(specification.begin(), specification.end(), [](double x) { return std::isfinite(x); })) { + throw std::runtime_error("Invalid FT0M axis specification"); + } + std::vector edges; + if (specification.front() == 0.) { + edges.assign(specification.begin() + 1, specification.end()); + } else { + const double bins = specification.front(); + if (specification.size() != MinAxisSpecificationSize || bins < 1. || bins > MaxUniformFT0MBins || std::floor(bins) != bins) { + throw std::runtime_error("Invalid uniform FT0M axis specification"); + } + for (int i = 0; i <= static_cast(bins); ++i) { + edges.push_back(specification[1] + (specification[2] - specification[1]) * i / bins); + } + } + if (edges.front() != 0. || edges.back() != FT0MPercentileMax || + std::adjacent_find(edges.begin(), edges.end(), [](double a, double b) { return a >= b; }) != edges.end()) { + throw std::runtime_error("FT0M classes must have increasing edges covering [0,100]"); + } + return edges; + } + + AxisSpec activityAxis(bool compact = false) const + { + if (useFT0M.value) { + return AxisSpec{ft0mEdges, "FT0M percentile (MB>0), calibrated"}; + } + return compact ? AxisSpec{proxyMultiplicityAxis, "N_{PV}(|#eta|<1), grouped"} : AxisSpec{multiplicityBins.value, 0., 200., "N_{PV}(|#eta|<1)"}; + } + + int ft0mClass(float percentile) const + { + if (ft0mStatus(percentile) != 0) { + return -1; + } + return static_cast(std::upper_bound(ft0mEdges.begin(), ft0mEdges.end(), percentile) - ft0mEdges.begin()) - 1; + } + + MixingKey mixingKey(int run, float vertex, float mult) const + { + const int activityClass = useFT0M.value ? ft0mClass(mult) : 0; + if (useFT0M.value && activityClass < 0) { + throw std::runtime_error("Invalid FT0M event cannot enter a mixing pool"); + } + const float width = useFT0M.value ? mixFT0MWidth.value : mixMultWidth.value; + auto bin = [](float value, float binWidth) { + const double index = std::floor(value / binWidth); + if (!std::isfinite(index) || index < std::numeric_limits::min() || index > std::numeric_limits::max()) { + throw std::runtime_error("Mixing coordinate cannot be represented by the configured bin width"); + } + return static_cast(index); + }; + return {run, bin(vertex, mixZWidth.value), activityClass, bin(mult, width)}; + } + void init(InitContext const&) { - if (mixDepth < 0 || mixZWidth <= 0.f || mixMultWidth <= 0.f || - vertexMax <= 0.f || pionTPC <= 0.f || pionTOF <= 0.f || - kaonTPC <= 0.f || kaonTOF <= 0.f || dipionMassMin >= dipionMassMax) { + if (doprocess && doprocessMCReco) { + throw std::runtime_error("Enable either process (data) or processMCReco, never both"); + } + if (mixDepth.value < 0 || !std::isfinite(mixZWidth.value) || mixZWidth.value <= 0.f || + !std::isfinite(mixMultWidth.value) || mixMultWidth.value <= 0.f || + !std::isfinite(mixFT0MWidth.value) || mixFT0MWidth.value <= 0.f || + vertexMax.value <= 0.f || trackPtMin.value < 0.f || trackEtaMax.value <= 0.f || + pionTPC.value <= 0.f || pionTOF.value <= 0.f || kaonTPC.value <= 0.f || kaonTOF.value <= 0.f || + dipionMassMin.value >= dipionMassMax.value || dipionYMax.value <= 0.f || proxyYMax.value <= 0.f || + multiplicityBins.value <= 0 || proxyDipionMassBins.value <= 0 || kstarMassBins.value <= 0 || mcF0PdgCode.value < 0) { throw std::runtime_error("Invalid F0PhiProxy configuration"); } - AxisSpec massPi{300, 0.2, 1.7, "m_{#pi#pi} (GeV/c^{2})"}; - AxisSpec massProxy{600, 0.9, 1.5, "m_{K,proxy K} (GeV/c^{2})"}; - AxisSpec pt{100, 0., 10., "p_{T} (GeV/c)"}; - AxisSpec mult{100, 0., 200., "N_{PV}(|#eta|<1)"}; - AxisSpec charge{3, -0.5, 2.5, "dipion: 0=unlike, 1=++, 2=--"}; - AxisSpec bachelorCharge{2, -1.5, 1.5, "bachelor charge"}; - AxisSpec tof{2, -0.5, 1.5, "bachelor has TOF"}; - histos.add("hEventSelection", "Received; selected", HistType::kTH1D, - {{2, 0.5, 2.5}}); - histos.add("hMixPartners", "Mixing partners;N", HistType::kTH1D, - {{101, -0.5, 100.5}}); - histos.add("hPionTPC", "Quality-selected tracks;pT;nSigmaTPC pion", - HistType::kTH2F, {pt, {100, -10., 10.}}); - histos.add("hPionTOF", "TOF-matched quality tracks;pT;nSigmaTOF pion", - HistType::kTH2F, {pt, {100, -10., 10.}}); - histos.add("hKaonTPC", "Quality-selected tracks;pT;nSigmaTPC kaon", - HistType::kTH2F, {pt, {100, -10., 10.}}); - histos.add("hKaonTOF", "TOF-matched quality tracks;pT;nSigmaTOF kaon", - HistType::kTH2F, {pt, {100, -10., 10.}}); - histos.add("hDipionSE", "Dipion SE", HistType::kTHnSparseF, - {massPi, pt, mult, charge}); - histos.add("hDipionME", "Dipion ME (raw)", HistType::kTHnSparseF, - {massPi, pt, mult, charge}); - // Keep measured dipion mass: no f0 identification by a mass window alone. - histos.add("hProxySE", "Dipion plus bachelor proxy SE", - HistType::kTHnSparseF, - {massProxy, pt, massPi, mult, charge, bachelorCharge, tof}); - histos.add("hProxyME", "Intact same-event dipion plus mixed bachelor (raw)", - HistType::kTHnSparseF, - {massProxy, pt, massPi, mult, charge, bachelorCharge, tof}); - AxisSpec massKK{600, 0.9, 1.5, "m_{KK} (GeV/c^{2})"}; - histos.add("hPhiKK", "Unlike-sign kaon control SE", HistType::kTHnSparseF, - {massKK, pt, mult}); - histos.add("hPhiKKLikePP", "K+K+ control SE", HistType::kTHnSparseF, - {massKK, pt, mult}); - histos.add("hPhiKKLikeMM", "K-K- control SE", HistType::kTHnSparseF, - {massKK, pt, mult}); - histos.add("hPhiKKMixed", "Unlike-sign kaon control ME (raw)", HistType::kTHnSparseF, - {massKK, pt, mult}); - histos.add("hPhiKKMixedLikePP", "K+K+ control ME (raw)", HistType::kTHnSparseF, - {massKK, pt, mult}); - histos.add("hPhiKKMixedLikeMM", "K-K- control ME (raw)", HistType::kTHnSparseF, - {massKK, pt, mult}); + if (useFT0M.value) { + ft0mEdges = resolveFT0MEdges(ft0mAxis.value); + } + if (doprocess || doprocessMCReco) { + if ((enablePhiProxy.value || enableKstarProxy.value) && !enableCompactProxy.value && !enableFullProxySparse.value) { + throw std::runtime_error("Enable compact or full proxy output when a proxy channel is enabled"); + } + bookReco(); + } + if (doprocessMCReco || doprocessMCGen) { + resolvedF0Code = mcF0PdgCode.value; + if (resolvedF0Code == 0) { + auto const* particle = pdgDB->GetParticle("f0_980"); + if (!particle) { + throw std::runtime_error("f0_980 not found in O2 PDG service; configure mcF0PdgCode explicitly"); + } + resolvedF0Code = particle->PdgCode(); + } + bookMC(); + } } - bool passPID(float tpc, float tof, bool matched, float tpcCut, - float tofCut) const + void bookReco() { - return std::isfinite(tpc) && std::abs(tpc) < tpcCut && - (!requireTOF.value || matched) && - (!matched || (std::isfinite(tof) && std::abs(tof) < tofCut)); + const AxisSpec massPi{300, 0.2, 1.7, "m_{#pi#pi} (GeV/c^{2})"}; + const AxisSpec compactMassPi{proxyDipionMassBins.value, 0.2, 1.7, "m_{#pi#pi} (GeV/c^{2})"}; + const AxisSpec pt{100, 0., 10., "p_{T} (GeV/c)"}; + const AxisSpec mult = activityAxis(); + const AxisSpec compactMult = activityAxis(true); + const AxisSpec compactPt{proxyPtAxis, "proxy pair p_{T} (GeV/c), grouped"}; + const AxisSpec charge{3, -0.5, 2.5, "dipion: 0=unlike, 1=++, 2=--"}; + const AxisSpec bachelorCharge{2, -1.5, 1.5, "bachelor charge"}; + const AxisSpec tof{2, -0.5, 1.5, "bachelor has TOF"}; + eventSelection = histos.add(recoName("hEventSelection").c_str(), "Received; selected including activity validity", HistType::kTH1D, {{2, 0.5, 2.5}}); + eventSelection->GetXaxis()->SetBinLabel(1, "Received"); + eventSelection->GetXaxis()->SetBinLabel(2, "Selected"); + const AxisSpec stages{NEventStages, 0.5, static_cast(NEventStages) + 0.5, "Event selection stage"}; + eventStages = histos.add(recoName("hEventSelectionSteps").c_str(), "Reconstructed event cut flow", HistType::kTH1D, {stages}); + eventStagesByActivity = histos.add(recoName("hEventSelectionVsMultiplicity").c_str(), "Valid-activity collisions;activity;selection stage", HistType::kTH2D, {compactMult, stages}); + const std::array stageNames{"Received", "Vertex", "sel8 or disabled", "INEL>0 or disabled", "Quality flags or disabled", "Valid activity selected"}; + for (int i = 0; i < NEventStages; ++i) { + eventStages->GetXaxis()->SetBinLabel(i + 1, stageNames[i]); + eventStagesByActivity->GetYaxis()->SetBinLabel(i + 1, stageNames[i]); + } + selectedEvents = histos.add(recoName("hSelectedEvents").c_str(), "Selected collisions counted once, including zero-candidate events;activity;Events", HistType::kTH1D, {compactMult}); + activityVsNPV = histos.add(recoName("hMultiplicityVsNPV").c_str(), "Selected collisions;activity;N_{PV}(|#eta|<1), overflow retained", HistType::kTH2D, {compactMult, {400, 0., 400.}}); + activityVsVertex = histos.add(recoName("hMultiplicityVsVertex").c_str(), "Selected collisions;activity;vertex z (cm)", HistType::kTH2D, {compactMult, {100, -vertexMax.value, vertexMax.value}}); + auto estimator = histos.add("hMultiplicityEstimator", "Configured reconstructed estimator;mode;Workers", HistType::kTH1D, {{2, -0.5, 1.5}}); + estimator->GetXaxis()->SetBinLabel(1, "NPV"); + estimator->GetXaxis()->SetBinLabel(2, "FT0M"); + estimator->Fill(useFT0M.value ? 1. : 0.); + if (useFT0M.value) { + ft0mStatusQA = histos.add("FT0M/hPercentileStatus", "After base event selection;FT0M status;Events", HistType::kTH1D, {{4, -0.5, 3.5}}); + const std::array statusNames{"Valid [0,100)", "Nonfinite", "Below zero", "At or above 100"}; + for (size_t i = 0; i < statusNames.size(); ++i) { + ft0mStatusQA->GetXaxis()->SetBinLabel(static_cast(i + 1), statusNames[i]); + } + } + if (mixingEnabled()) { + mixPartnersByActivity = histos.add(recoName("hMixPartnersVsMultiplicity").c_str(), "Available previous events;activity;mixing partners", HistType::kTH2D, {compactMult, {101, -0.5, 100.5}}); + mixPartners = histos.add(recoName("hMixPartners").c_str(), "Mixing partners;N", HistType::kTH1D, {{101, -0.5, 100.5}}); + } + if (enablePidQA.value) { + const std::array names{"hPionTPC", "hPionTOF", "hKaonTPC", "hKaonTOF"}; + for (size_t i = 0; i < names.size(); ++i) { + pidQA[i] = histos.add(recoName(names[i]).c_str(), "Quality-selected tracks, before PID;pT;nSigma", HistType::kTH2F, {pt, {100, -10., 10.}}); + if (useFT0M.value) { + const std::string name = std::string(names[i]).append("ByMultiplicity"); + pidQAByActivity[i] = histos.add(recoName(name).c_str(), "Quality-selected tracks, before PID;pT;nSigma;FT0M percentile", HistType::kTH3F, {pt, {100, -10., 10.}, compactMult}); + } + } + } + const std::array dipionNames{"hDipionSE", "hDipionME"}; + for (size_t mix = 0; mix < dipionNames.size(); ++mix) { + if (mix != 0 && !mixingEnabled()) { + continue; + } + dipionSpectra[mix] = histos.add(recoName(dipionNames[mix]).c_str(), "Dipion SE or raw ME", HistType::kTHnSparseF, {massPi, pt, mult, charge}); + } + const std::array, NChannels> compactNames{{{"hProxyCompactSE", "hProxyCompactME"}, {"hKstarProxyCompactSE", "hKstarProxyCompactME"}}}; + const std::array, NChannels> fullNames{{{"hProxySE", "hProxyME"}, {"hKstarProxySE", "hKstarProxyME"}}}; + const std::array, NMixClasses>, NChannels> controlNames{{{{{"hPhiKK", "hPhiKKLikePP", "hPhiKKLikeMM"}, {"hPhiKKMixed", "hPhiKKMixedLikePP", "hPhiKKMixedLikeMM"}}}, + {{{"hKstarKPi", "hKstarKPiLikePP", "hKstarKPiLikeMM"}, {"hKstarKPiMixed", "hKstarKPiMixedLikePP", "hKstarKPiMixedLikeMM"}}}}}; + for (size_t channel = 0; channel < NChannels; ++channel) { + const AxisSpec mass = channel == Phi ? AxisSpec{600, 0.9, 1.5, "m_{K,proxy K} (GeV/c^{2})"} : AxisSpec{kstarMassBins.value, 0.6, 1.4, "m_{K,proxy #pi} (GeV/c^{2})"}; + for (size_t mix = 0; mix < NMixClasses; ++mix) { + if (mix != 0 && !mixingEnabled()) { + continue; + } + if (channelEnabled(channel)) { + // The measured dipion mass is retained; a mass window is not an f0 tag. + if (enableCompactProxy.value) { + compactProxy[channel][mix] = histos.add(recoName(compactNames[channel][mix]).c_str(), "Grouped-pT/multiplicity proxy; no constituent charge is measured", HistType::kTHnSparseF, {mass, compactPt, compactMassPi, compactMult, charge, bachelorCharge, tof}); + } + if (enableFullProxySparse.value) { + fullProxy[channel][mix] = histos.add(recoName(fullNames[channel][mix]).c_str(), "Diagnostic full proxy", HistType::kTHnSparseF, {mass, pt, massPi, mult, charge, bachelorCharge, tof}); + } + } + if (controlEnabled(channel)) { + for (size_t sign = 0; sign < NCharges; ++sign) { + controls[channel][mix][sign] = histos.add(recoName(controlNames[channel][mix][sign]).c_str(), "Standard control SE or raw ME", HistType::kTHnSparseF, {mass, pt, mult}); + } + } + } + } + } + + void bookMC() + { + const AxisSpec pt{100, 0., 10., "pair pT (GeV/c)"}; + const AxisSpec pionMass{300, 0.2, 1.7, "dipion mass (GeV/c2)"}; + const std::array mass{AxisSpec{600, 0.9, 1.5, "KK mass (GeV/c2)"}, AxisSpec{kstarMassBins.value, 0.6, 1.4, "K pi mass (GeV/c2)"}}; + if (doprocessMCGen) { + histos.add("hMCGenEventSelection", "Generated events;1=all,2=vertex,3=truth INEL>0", HistType::kTH1D, {{3, 0.5, 3.5}}); + const std::array names{"hMCGenDipion", "hMCGenTrueF0", "hMCGenPhiKK", "hMCGenTruePhi", "hMCGenKstarKPi", "hMCGenTrueKstar", "hMCGenProxyPhi", "hMCGenProxyPhiTrueF0", "hMCGenProxyKstar", "hMCGenProxyKstarTrueF0"}; + for (size_t i = 0; i < names.size(); ++i) { + const AxisSpec axis = i <= GenF0 ? pionMass : (i == GenKstar || i == GenKstarTrue || i >= GenProxyKstar ? mass[Kstar] : mass[Phi]); + generated[i] = histos.add(names[i], "Generated unweighted fiducial spectrum; no detector FT0M selection", HistType::kTH2D, {axis, pt}, true); + } + } + if (doprocessMCReco) { + recoTrueF0 = histos.add(recoName("hMCRecoTrueF0").c_str(), "Selected common-parent f0 -> pi pi", HistType::kTH2D, {pionMass, pt}, true); + if (useFT0M.value) { + recoTrueF0ByActivity = histos.add("FT0M/hMCRecoTrueF0ByMultiplicity", "Selected common-parent f0;dipion mass;pair pT;FT0M percentile", HistType::kTHnSparseD, {pionMass, pt, activityAxis(true)}, true); + } + const std::array channelNames{"Phi", "Kstar"}; + const std::array classNames{"TrueF0", "Combinatorial", "MisID", "Unmatched"}; + for (size_t channel = 0; channel < NChannels; ++channel) { + if (channelEnabled(channel)) { + for (size_t category = 0; category < classNames.size(); ++category) { + const std::string name = std::string("hMCRecoProxy").append(channelNames[channel]).append(classNames[category]); + recoComponents[channel][category] = histos.add(recoName(name).c_str(), "Exclusive proxy truth component; not a coalescence ancestry tag", HistType::kTH2D, {mass[channel], pt}, true); + if (useFT0M.value) { + recoComponentsByActivity[channel][category] = histos.add(recoName(std::string(name).append("ByMultiplicity")).c_str(), "Exclusive proxy truth component; not coalescence ancestry", HistType::kTHnSparseD, {mass[channel], pt, activityAxis(true)}, true); + } + } + const std::string name = std::string("hMCRecoProxy").append(channelNames[channel]).append("PtResponse"); + proxyPtResponse[channel] = histos.add(recoName(name).c_str(), "Primary true f0 daughters and true bachelor;generated proxy pair pT;reconstructed proxy pair pT", HistType::kTH2D, {pt, pt}, true); + if (useFT0M.value) { + proxyPtResponseByActivity[channel] = histos.add(recoName(std::string(name).append("ByMultiplicity")).c_str(), "Selected true-f0 proxy response;generated pair pT;reconstructed pair pT;FT0M percentile", HistType::kTHnSparseD, {pt, pt, activityAxis(true)}, true); + } + } + if (controlEnabled(channel)) { + const std::string truthName = std::string("hMCRecoTrue").append(channelNames[channel]); + const std::string responseName = std::string("hMCReco").append(channelNames[channel]).append("PtResponse"); + recoTrueParent[channel] = histos.add(recoName(truthName).c_str(), "Selected exact common-parent standard control", HistType::kTH2D, {mass[channel], pt}, true); + recoPtResponse[channel] = histos.add(recoName(responseName).c_str(), "Selected standard control;generated pair pT;reconstructed pair pT", HistType::kTH2D, {pt, pt}, true); + if (useFT0M.value) { + recoTrueParentByActivity[channel] = histos.add(recoName(std::string(truthName).append("ByMultiplicity")).c_str(), "Selected exact common-parent standard control", HistType::kTHnSparseD, {mass[channel], pt, activityAxis(true)}, true); + recoPtResponseByActivity[channel] = histos.add(recoName(std::string(responseName).append("ByMultiplicity")).c_str(), "Selected standard response;generated pair pT;reconstructed pair pT;FT0M percentile", HistType::kTHnSparseD, {pt, pt, activityAxis(true)}, true); + } + } + } + } } - static int chargeCategory(int a, int b) + static void fillSparse(std::shared_ptr const& histogram, std::initializer_list coordinates) { - return a != b ? 0 : (a > 0 ? 1 : 2); + if (histogram) { + histogram->Fill(coordinates.begin()); + } } - void fillProxy(FourVector const& dipion, TrackCandidate const& first, - TrackCandidate const& second, - std::vector const& kaons, float mult, - bool mixed) + bool passPID(float tpc, float tof, bool matched, float tpcCut, float tofCut) const + { + return std::isfinite(tpc) && std::abs(tpc) < tpcCut && (!requireTOF.value || matched) && + (!matched || (std::isfinite(tof) && std::abs(tof) < tofCut)); + } + static int chargeCategory(int a, int b) { return a != b ? 0 : (a > 0 ? 1 : 2); } + static FourVector constituent(FourVector const& dipion) + { + return {ProxyMomentumScale * dipion.Px(), ProxyMomentumScale * dipion.Py(), ProxyMomentumScale * dipion.Pz(), constants::physics::MassKaonCharged}; + } + int chargeFromPdg(int code) const + { + const int absolute = std::abs(code); + if (absolute >= IonCodeThreshold) { + const int z = (absolute / IonZDivisor) % IonZModulo; + return code > 0 ? z : -z; + } + if (auto const* particle = pdgDB->GetParticle(code)) { + return static_cast(std::lround(particle->Charge() / 3.)); + } + return 0; + } + template + TruthInfo particleTruth(TParticle const& particle) const + { + TruthInfo truth{.id = particle.globalIndex(), .collisionId = particle.mcCollisionId(), .pdg = particle.pdgCode(), .px = particle.px(), .py = particle.py(), .pz = particle.pz(), .primary = particle.isPhysicalPrimary(), .parents = {}}; + for (auto const& mother : particle.template mothers_as()) { + const int code = std::abs(mother.pdgCode()); + if (mother.mcCollisionId() == truth.collisionId && (code == resolvedF0Code || code == PdgPhi || code == PdgKstar)) { + // The complementary-channel study uses exact two-body parent tags. + if (mother.template daughters_as().size() == TwoBodyDaughters) { + truth.parents.emplace_back(mother.globalIndex(), code); + } + } + } + return truth; + } + template + TruthInfo trackTruth(TTrack const& track, int collisionId) const { - const FourVector constituent(dipion.Px() * 0.5, dipion.Py() * 0.5, - dipion.Pz() * 0.5, - constants::physics::MassKaonCharged); + if (collisionId < 0 || !track.has_mcParticle()) { + return {}; + } + auto particle = track.template mcParticle_as(); + return particle.mcCollisionId() == collisionId ? particleTruth(particle) : TruthInfo{}; + } + static bool commonParent(TruthInfo const& first, TruthInfo const& second, int parentCode) + { + if (first.id < 0 || second.id < 0 || first.id == second.id || first.collisionId != second.collisionId) { + return false; + } + return std::any_of(first.parents.begin(), first.parents.end(), [&second, parentCode](auto const& parent) { + return parent.second == parentCode && std::find(second.parents.begin(), second.parents.end(), parent) != second.parents.end(); + }); + } + static bool identified(TrackCandidate const& candidate, int positiveCode) + { + return candidate.truth.id >= 0 && candidate.truth.pdg == candidate.sign * positiveCode; + } + bool trueF0(TrackCandidate const& first, TrackCandidate const& second) const + { + return first.sign != second.sign && identified(first, PdgPion) && identified(second, PdgPion) && commonParent(first.truth, second.truth, resolvedF0Code); + } + static double truthPairPt(TruthInfo const& first, TruthInfo const& second) + { + return std::hypot(first.px + second.px, first.py + second.py); + } + size_t proxyTruthClass(TrackCandidate const& first, TrackCandidate const& second, TrackCandidate const& bachelor, size_t channel) const + { + if (first.truth.id < 0 || second.truth.id < 0 || bachelor.truth.id < 0 || + first.truth.collisionId != second.truth.collisionId || first.truth.collisionId != bachelor.truth.collisionId) { + return NTruthClasses - 1; + } + if (!identified(first, PdgPion) || !identified(second, PdgPion) || !identified(bachelor, bachelorPdg(channel))) { + return 2; + } + const bool distinctBachelor = bachelor.truth.id != first.truth.id && bachelor.truth.id != second.truth.id; + return distinctBachelor && trueF0(first, second) ? 0 : 1; + } + + void fillProxy(FourVector const& dipion, TrackCandidate const& first, TrackCandidate const& second, + std::vector const& bachelors, float mult, bool mixed, size_t channel, bool mc = false) + { + if (!channelEnabled(channel)) { + return; + } + const FourVector proxy = constituent(dipion); const int category = chargeCategory(first.sign, second.sign); - for (auto const& kaon : kaons) { - if (!mixed && (kaon.id == first.id || kaon.id == second.id)) { + for (auto const& bachelor : bachelors) { + if (!mixed && (bachelor.id == first.id || bachelor.id == second.id)) { continue; } - const auto pair = - constituent + kaon.vector(constants::physics::MassKaonCharged); - if (std::abs(pair.Rapidity()) >= proxyYMax) { + const auto pair = proxy + bachelor.vector(bachelorMass(channel)); + if (std::abs(pair.Rapidity()) >= proxyYMax.value) { continue; } - // The neutral dipion has no measured constituent-kaon charge: fill once - // per bachelor. - if (mixed) { - histos.fill(HIST("hProxyME"), pair.M(), pair.Pt(), dipion.M(), mult, - category, kaon.sign, kaon.hasTOF); - } else { - histos.fill(HIST("hProxySE"), pair.M(), pair.Pt(), dipion.M(), mult, - category, kaon.sign, kaon.hasTOF); + // Neutral dipion: one entry per bachelor. There is no measured proxy-kaon charge. + fillSparse(compactProxy[channel][mixed], {pair.M(), pair.Pt(), dipion.M(), mult, static_cast(category), static_cast(bachelor.sign), static_cast(bachelor.hasTOF)}); + fillSparse(fullProxy[channel][mixed], {pair.M(), pair.Pt(), dipion.M(), mult, static_cast(category), static_cast(bachelor.sign), static_cast(bachelor.hasTOF)}); + if (mc && !mixed) { + const size_t truthClass = proxyTruthClass(first, second, bachelor, channel); + recoComponents[channel][truthClass]->Fill(pair.M(), pair.Pt()); + fillSparse(recoComponentsByActivity[channel][truthClass], {pair.M(), pair.Pt(), mult}); + if (truthClass == 0 && first.truth.primary && second.truth.primary && bachelor.truth.primary && + bachelor.truth.id != first.truth.id && bachelor.truth.id != second.truth.id) { + const double px = ProxyMomentumScale * (first.truth.px + second.truth.px) + bachelor.truth.px; + const double py = ProxyMomentumScale * (first.truth.py + second.truth.py) + bachelor.truth.py; + proxyPtResponse[channel]->Fill(std::hypot(px, py), pair.Pt()); + fillSparse(proxyPtResponseByActivity[channel], {std::hypot(px, py), pair.Pt(), mult}); + } } } } - void fillPhiPair(TrackCandidate const& first, TrackCandidate const& second, float mult, bool mixed) + void fillControlPair(TrackCandidate const& first, TrackCandidate const& second, float mult, bool mixed, size_t channel, bool mc = false) { - const auto pair = first.vector(constants::physics::MassKaonCharged) + - second.vector(constants::physics::MassKaonCharged); + if ((!mixed && first.id == second.id) || !controlEnabled(channel)) { + return; + } + const auto pair = first.vector(constants::physics::MassKaonCharged) + second.vector(bachelorMass(channel)); if (std::abs(pair.Rapidity()) >= proxyYMax.value) { return; } - if (mixed) { - if (first.sign != second.sign) { - histos.fill(HIST("hPhiKKMixed"), pair.M(), pair.Pt(), mult); - } else if (first.sign > 0) { - histos.fill(HIST("hPhiKKMixedLikePP"), pair.M(), pair.Pt(), mult); - } else { - histos.fill(HIST("hPhiKKMixedLikeMM"), pair.M(), pair.Pt(), mult); - } - } else { - if (first.sign != second.sign) { - histos.fill(HIST("hPhiKK"), pair.M(), pair.Pt(), mult); - } else if (first.sign > 0) { - histos.fill(HIST("hPhiKKLikePP"), pair.M(), pair.Pt(), mult); - } else { - histos.fill(HIST("hPhiKKLikeMM"), pair.M(), pair.Pt(), mult); - } + fillSparse(controls[channel][mixed][chargeCategory(first.sign, second.sign)], {pair.M(), pair.Pt(), mult}); + const int parent = channel == Phi ? PdgPhi : PdgKstar; + if (mc && !mixed && first.sign != second.sign && identified(first, PdgKaon) && identified(second, bachelorPdg(channel)) && commonParent(first.truth, second.truth, parent)) { + recoTrueParent[channel]->Fill(pair.M(), pair.Pt()); + recoPtResponse[channel]->Fill(truthPairPt(first.truth, second.truth), pair.Pt()); + fillSparse(recoTrueParentByActivity[channel], {pair.M(), pair.Pt(), mult}); + fillSparse(recoPtResponseByActivity[channel], {truthPairPt(first.truth, second.truth), pair.Pt(), mult}); } } - - void fillPhiControls(Event const& event, std::deque const& pool, float mult) + void fillControls(Event const& event, std::deque const& pool, float mult, bool mc) { for (size_t i = 0; i < event.kaons.size(); ++i) { - // Each same-event unordered pair is counted once. - for (size_t j = i + 1; j < event.kaons.size(); ++j) { - fillPhiPair(event.kaons[i], event.kaons[j], mult, false); + auto const& kaon = event.kaons[i]; + if (enablePhiControls.value) { + for (size_t j = i + 1; j < event.kaons.size(); ++j) { + fillControlPair(kaon, event.kaons[j], mult, false, Phi, mc); + } + } + if (enableKstarControls.value) { + for (auto const& pion : event.pions) { + fillControlPair(kaon, pion, mult, false, Kstar, mc); + } } - // Includes K+current K-previous and K-current K+previous once each. - // Pool insertion occurs after this call; no event is mixed with itself. for (auto const& previous : pool) { - for (auto const& second : previous.kaons) { - fillPhiPair(event.kaons[i], second, mult, true); + if (enablePhiControls.value) { + for (auto const& other : previous.kaons) { + fillControlPair(kaon, other, mult, true, Phi); + } + } + if (enableKstarControls.value) { + for (auto const& pion : previous.pions) { + fillControlPair(kaon, pion, mult, true, Kstar); + } + } + } + } + if (enableKstarControls.value) { + // Both K(current)-pi(previous) and K(previous)-pi(current) are included. + for (auto const& previous : pool) { + for (auto const& kaon : previous.kaons) { + for (auto const& pion : event.pions) { + fillControlPair(kaon, pion, mult, true, Kstar); + } } } } } - void process(Collisions const& collisions, Tracks const& tracks, - aod::BCsWithTimestamps const&) + template + void processReco(TCollisions const& collisions, TTracks const& tracks, TSlice const& slice) { - // Local pools cannot retain track IDs across dataframes. Run is part of the - // key. - std::map>, std::deque> pools; + // Pools reset at each dataframe; the key also separates runs, z and multiplicity. + std::map> pools; + const std::deque emptyPool; for (auto const& collision : collisions) { - histos.fill(HIST("hEventSelection"), 1.); - if (std::abs(collision.posZ()) >= vertexMax || - (requireSel8 && !collision.sel8()) || - (requireINELgt0 && collision.multNTracksPVeta1() < 1)) { + const float npv = collision.multNTracksPVeta1(); + const float mult = useFT0M.value ? collision.centFT0M() : npv; + const bool validActivity = useFT0M.value ? ft0mStatus(mult) == 0 : std::isfinite(mult) && mult >= 0.f; + auto stage = [&](int step) { + eventStages->Fill(step); + if (validActivity) { + eventStagesByActivity->Fill(mult, step); + } + }; + eventSelection->Fill(1.); + stage(Received); + if (!std::isfinite(collision.posZ()) || std::abs(collision.posZ()) >= vertexMax.value) { + continue; + } + stage(VertexAccepted); + if (requireSel8.value && !collision.sel8()) { + continue; + } + stage(Sel8Accepted); + if (requireINELgt0.value && npv < 1.f) { + continue; + } + stage(InelAccepted); + if ((requireNoSameBunchPileup.value && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) || + (requireGoodZvtxFT0vsPV.value && !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV))) { continue; } - histos.fill(HIST("hEventSelection"), 2.); - const float mult = collision.multNTracksPVeta1(); - const int run = collision.bc_as().runNumber(); - auto& pool = - pools[{run, - {static_cast(std::floor(collision.posZ() / mixZWidth)), - static_cast(std::floor(mult / mixMultWidth))}}]; + stage(QualityAccepted); + if (useFT0M.value) { + // Missing calibration (105) and non-INEL markers (100.5) are not percentiles. + // Never substitute NPV in an FT0M histogram or pool. + ft0mStatusQA->Fill(ft0mStatus(mult)); + } + if (!validActivity) { + continue; + } + stage(ActivityAccepted); + eventSelection->Fill(2.); + selectedEvents->Fill(mult); + activityVsNPV->Fill(mult, npv); + activityVsVertex->Fill(mult, collision.posZ()); + std::deque* activePool = nullptr; + if (mixingEnabled()) { + const int run = collision.template bc_as().runNumber(); + activePool = &pools[mixingKey(run, collision.posZ(), mult)]; + mixPartners->Fill(activePool->size()); + mixPartnersByActivity->Fill(mult, activePool->size()); + } + auto const& pool = activePool ? *activePool : emptyPool; Event event; - auto selected = tracks.sliceBy(perCollision, collision.globalIndex()); + auto selected = tracks.sliceBy(slice, collision.globalIndex()); for (auto const& track : selected) { - if (track.sign() == 0 || track.pt() < trackPtMin || - std::abs(track.eta()) >= trackEtaMax || - (requireGlobalTrack && !track.isGlobalTrack()) || - (requirePVContributor && !track.isPVContributor())) { + if (track.sign() == 0 || track.pt() < trackPtMin.value || std::abs(track.eta()) >= trackEtaMax.value || + (requireGlobalTrack.value && !track.isGlobalTrack()) || (requirePVContributor.value && !track.isPVContributor())) { continue; } const bool tof = track.hasTOF(); - histos.fill(HIST("hPionTPC"), track.pt(), track.tpcNSigmaPi()); - histos.fill(HIST("hKaonTPC"), track.pt(), track.tpcNSigmaKa()); - if (tof) { - histos.fill(HIST("hPionTOF"), track.pt(), track.tofNSigmaPi()); - histos.fill(HIST("hKaonTOF"), track.pt(), track.tofNSigmaKa()); - } - TrackCandidate candidate{.id = track.globalIndex(), .sign = track.sign(), .px = track.px(), .py = track.py(), .pz = track.pz(), .hasTOF = tof}; - if (passPID(track.tpcNSigmaPi(), track.tofNSigmaPi(), tof, pionTPC, - pionTOF)) { + if (enablePidQA.value) { + auto fillPID = [&](size_t index, float value) { + pidQA[index]->Fill(track.pt(), value); + if (pidQAByActivity[index]) { + pidQAByActivity[index]->Fill(track.pt(), value, mult); + } + }; + fillPID(0, track.tpcNSigmaPi()); + fillPID(2, track.tpcNSigmaKa()); + if (tof) { + fillPID(1, track.tofNSigmaPi()); + fillPID(3, track.tofNSigmaKa()); + } + } + TrackCandidate candidate{.id = track.globalIndex(), .sign = track.sign(), .px = track.px(), .py = track.py(), .pz = track.pz(), .hasTOF = tof, .truth = {}}; + if constexpr (IsMC) { + candidate.truth = trackTruth(track, collision.mcCollisionId()); + } + if (passPID(track.tpcNSigmaPi(), track.tofNSigmaPi(), tof, pionTPC.value, pionTOF.value)) { event.pions.push_back(candidate); } - if (passPID(track.tpcNSigmaKa(), track.tofNSigmaKa(), tof, kaonTPC, - kaonTOF)) { + if (passPID(track.tpcNSigmaKa(), track.tofNSigmaKa(), tof, kaonTPC.value, kaonTOF.value)) { event.kaons.push_back(candidate); } } - histos.fill(HIST("hMixPartners"), pool.size()); - for (size_t i = 0; i < event.pions.size(); ++i) { - auto const& first = event.pions[i]; - for (size_t j = i + 1; j < event.pions.size(); ++j) { - auto const& second = event.pions[j]; - const auto pair = first.vector(constants::physics::MassPionCharged) + - second.vector(constants::physics::MassPionCharged); - if (std::abs(pair.Rapidity()) >= dipionYMax) { - continue; - } - histos.fill(HIST("hDipionSE"), pair.M(), pair.Pt(), mult, - chargeCategory(first.sign, second.sign)); - if (pair.M() < dipionMassMin || pair.M() >= dipionMassMax) { - continue; - } - fillProxy(pair, first, second, event.kaons, mult, false); - for (auto const& previous : pool) { - fillProxy(pair, first, second, previous.kaons, mult, true); + fillDipions(event, pool, mult, IsMC); + fillControls(event, pool, mult, IsMC); + if (activePool) { + // Mixed events never use ancestry; release that storage before caching. + if constexpr (IsMC) { + for (auto const& collection : {&event.pions, &event.kaons}) { + for (size_t i = 0; i < collection->size(); ++i) { + (*collection)[i].truth = {}; + } } } + activePool->push_back(std::move(event)); + if (activePool->size() > static_cast(mixDepth.value)) { + activePool->pop_front(); + } + } + } + } + + void fillDipions(Event const& event, std::deque const& pool, float mult, bool mc) + { + for (size_t i = 0; i < event.pions.size(); ++i) { + auto const& first = event.pions[i]; + for (size_t j = i + 1; j < event.pions.size(); ++j) { + auto const& second = event.pions[j]; + const auto pair = first.vector(constants::physics::MassPionCharged) + second.vector(constants::physics::MassPionCharged); + if (std::abs(pair.Rapidity()) >= dipionYMax.value) { + continue; + } + fillSparse(dipionSpectra[0], {pair.M(), pair.Pt(), mult, static_cast(chargeCategory(first.sign, second.sign))}); + if (mc && trueF0(first, second)) { + recoTrueF0->Fill(pair.M(), pair.Pt()); + fillSparse(recoTrueF0ByActivity, {pair.M(), pair.Pt(), mult}); + } + if (pair.M() < dipionMassMin.value || pair.M() >= dipionMassMax.value) { + continue; + } + fillProxy(pair, first, second, event.kaons, mult, false, Phi, mc); + fillProxy(pair, first, second, event.pions, mult, false, Kstar, mc); for (auto const& previous : pool) { - for (auto const& second : previous.pions) { - const auto pair = - first.vector(constants::physics::MassPionCharged) + - second.vector(constants::physics::MassPionCharged); - if (std::abs(pair.Rapidity()) < dipionYMax) { - histos.fill(HIST("hDipionME"), pair.M(), pair.Pt(), mult, - chargeCategory(first.sign, second.sign)); + fillProxy(pair, first, second, previous.kaons, mult, true, Phi); + fillProxy(pair, first, second, previous.pions, mult, true, Kstar); + } + } + for (auto const& previous : pool) { + for (auto const& second : previous.pions) { + const auto pair = first.vector(constants::physics::MassPionCharged) + second.vector(constants::physics::MassPionCharged); + if (std::abs(pair.Rapidity()) < dipionYMax.value) { + fillSparse(dipionSpectra[1], {pair.M(), pair.Pt(), mult, static_cast(chargeCategory(first.sign, second.sign))}); + } + } + } + } + } + + void fillGenerated(Event const& event) + { + for (size_t i = 0; i < event.pions.size(); ++i) { + auto const& first = event.pions[i]; + for (size_t j = i + 1; j < event.pions.size(); ++j) { + auto const& second = event.pions[j]; + if (first.sign == second.sign) { + continue; + } + const auto pair = first.vector(constants::physics::MassPionCharged) + second.vector(constants::physics::MassPionCharged); + if (std::abs(pair.Rapidity()) >= dipionYMax.value) { + continue; + } + const bool tagged = trueF0(first, second); + generated[GenDipion]->Fill(pair.M(), pair.Pt()); + if (tagged) { + generated[GenF0]->Fill(pair.M(), pair.Pt()); + } + if (pair.M() < dipionMassMin.value || pair.M() >= dipionMassMax.value) { + continue; + } + const FourVector proxy = constituent(pair); + for (size_t channel = 0; channel < NChannels; ++channel) { + if (!channelEnabled(channel)) { + continue; + } + auto const& bachelors = channel == Phi ? event.kaons : event.pions; + for (auto const& bachelor : bachelors) { + if (bachelor.id == first.id || bachelor.id == second.id) { + continue; + } + const auto candidate = proxy + bachelor.vector(bachelorMass(channel)); + if (std::abs(candidate.Rapidity()) >= proxyYMax.value) { + continue; + } + generated[channel == Phi ? GenProxyPhi : GenProxyKstar]->Fill(candidate.M(), candidate.Pt()); + if (tagged) { + generated[channel == Phi ? GenProxyPhiF0 : GenProxyKstarF0]->Fill(candidate.M(), candidate.Pt()); } } } } - fillPhiControls(event, pool, mult); - if (mixDepth > 0) { - pool.push_back(std::move(event)); - if (pool.size() > static_cast(mixDepth.value)) { - pool.pop_front(); + } + for (size_t channel = 0; channel < NChannels; ++channel) { + if (!controlEnabled(channel)) { + continue; + } + for (size_t i = 0; i < event.kaons.size(); ++i) { + auto const& first = event.kaons[i]; + auto const& others = channel == Phi ? event.kaons : event.pions; + const size_t start = channel == Phi ? i + 1 : 0; + for (size_t j = start; j < others.size(); ++j) { + auto const& second = others[j]; + if (first.id == second.id || first.sign == second.sign) { + continue; + } + const auto pair = first.vector(constants::physics::MassKaonCharged) + second.vector(bachelorMass(channel)); + if (std::abs(pair.Rapidity()) >= proxyYMax.value) { + continue; + } + generated[channel == Phi ? GenPhi : GenKstar]->Fill(pair.M(), pair.Pt()); + if (commonParent(first.truth, second.truth, channel == Phi ? PdgPhi : PdgKstar)) { + generated[channel == Phi ? GenPhiTrue : GenKstarTrue]->Fill(pair.M(), pair.Pt()); + } } } } } + + void processData(Collisions const& collisions, Tracks const& tracks, aod::BCsWithTimestamps const&) + { + processReco(collisions, tracks, perCollision); + } + // Keep the original JSON option "process"; no unconditional process() method. + PROCESS_SWITCH_FULL(F0phiproxy, processData, process, "Dipion phi/Kstar proxies in data", true); + + void processMCReco(CollisionsMC const& collisions, TracksMC const& tracks, aod::McParticles const&, aod::BCsWithTimestamps const&) + { + processReco(collisions, tracks, perCollisionMC); + } + PROCESS_SWITCH(F0phiproxy, processMCReco, "Data-identical reconstruction with truth components and responses", false); + + void processMCGen(aod::McCollision const& collision, aod::McParticles const& particles) + { + histos.fill(HIST("hMCGenEventSelection"), 1.); + if (std::abs(collision.posZ()) >= vertexMax.value) { + return; + } + histos.fill(HIST("hMCGenEventSelection"), 2.); + bool inel = false; + for (auto const& particle : particles) { + // O2 ASoA row iterators and their end sentinel are not STL input iterators. + // cppcheck-suppress useStlAlgorithm + if (particle.isPhysicalPrimary() && std::abs(particle.eta()) < 1.f && chargeFromPdg(particle.pdgCode()) != 0) { + inel = true; + break; + } + } + if (requireINELgt0.value && !inel) { + return; + } + histos.fill(HIST("hMCGenEventSelection"), 3.); + Event event; + for (auto const& particle : particles) { + const int absolute = std::abs(particle.pdgCode()); + if ((absolute != PdgPion && absolute != PdgKaon) || particle.pt() < trackPtMin.value || + std::abs(particle.eta()) >= trackEtaMax.value || (mcPrimaryOnly.value && !particle.isPhysicalPrimary())) { + continue; + } + TrackCandidate candidate{.id = particle.globalIndex(), .sign = particle.pdgCode() > 0 ? 1 : -1, .px = particle.px(), .py = particle.py(), .pz = particle.pz(), .hasTOF = false, .truth = particleTruth(particle)}; + (absolute == PdgPion ? event.pions : event.kaons).push_back(std::move(candidate)); + } + // Unweighted primary-daughter fiducial spectra, with no detector selection. + // Native f0 truth is not a resonance-fed coalescence ancestry tag. + fillGenerated(event); + } + PROCESS_SWITCH(F0phiproxy, processMCGen, "Independent generated fiducial spectra and exact common-parent tags", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{ - adaptAnalysisTask(context)}; + return WorkflowSpec{adaptAnalysisTask(context)}; } diff --git a/PWGLF/Tasks/Resonances/f1protoncorrelation.cxx b/PWGLF/Tasks/Resonances/f1protoncorrelation.cxx index 193cc5e105b..ead7c9b294f 100644 --- a/PWGLF/Tasks/Resonances/f1protoncorrelation.cxx +++ b/PWGLF/Tasks/Resonances/f1protoncorrelation.cxx @@ -668,7 +668,7 @@ struct f1protoncorrelation { // Processing Event Mixing SliceCache cache; using BinningType = ColumnBinningPolicy; - BinningType colBinning{{CfgVtxBins, CfgMultBins}, true}; + BinningType colBinning{{CfgVtxBins, CfgMultBins}}; Preslice tracksPerCollisionPresliceF1 = aod::f1protondaughter::redF1PEventId; Preslice tracksPerCollisionPresliceP = aod::f1protondaughter::redF1PEventId; void processME(aod::RedF1PEvents& collisions, diff --git a/PWGLF/Tasks/Resonances/hResonanceCorrelation.cxx b/PWGLF/Tasks/Resonances/hResonanceCorrelation.cxx index 35f54137039..5d8989567fe 100644 --- a/PWGLF/Tasks/Resonances/hResonanceCorrelation.cxx +++ b/PWGLF/Tasks/Resonances/hResonanceCorrelation.cxx @@ -24,8 +24,6 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/Core/RecoDecay.h" -#include "Common/Core/Zorro.h" -#include "Common/Core/ZorroSummary.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" @@ -89,15 +87,8 @@ struct HResonanceCorrelation { HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - // event filtering - Configurable zorroMask{"zorroMask", "", "zorro trigger class to select on (empty: none)"}; - - Zorro zorro; - OutputObj zorroSummary{"zorroSummary"}; - struct : ConfigurableGroup { std::string prefix = "masterConfigurations"; - Configurable doPPAnalysis{"doPPAnalysis", true, "if in pp, set to true"}; Configurable collisionHasTriggOrAssoc{"collisionHasTriggOrAssoc", 0, "require the collisions containing (0:no requirement 1:trig 2:assoc 3:trig or assoc 4:trig and assoc"}; Configurable doFullCorrelationStudy{"doFullCorrelationStudy", true, "if true, do full correlation study by creating all THnSparse histograms for the correlation function"}; Configurable doCorrelationHadron{"doCorrelationHadron", false, "do Hadron correlation"}; @@ -106,11 +97,14 @@ struct HResonanceCorrelation { Configurable doCorrelationPion{"doCorrelationPion", false, "do Pion correlation"}; Configurable doCorrelationKaon{"doCorrelationKaon", false, "do Kaon correlation"}; Configurable doGenEventSelection{"doGenEventSelection", true, "use event selections when performing closure test for the gen events"}; - Configurable selectINELgtZERO{"selectINELgtZERO", true, "select INEL>0 events"}; - Configurable zVertexCut{"zVertexCut", 10, "Cut on PV position"}; - Configurable requireAllGoodITSLayers{"requireAllGoodITSLayers", false, " require that in the event all ITS are good"}; - Configurable requireGoodTriggerTVX{"requireGoodTriggerTVX", false, " require acceptable FT0C-FT0A time difference"}; - Configurable requireGoodZvtxFT0vsPV{"requireGoodZvtxFT0vsPV", false, " require small difference between z-vertex from PV and from FT0"}; + // Used by the generator-level (MC-truth) selection in processPrediction, + // not by isSelectedEvents() below -- that's a different, reconstructed- + // level selection now driven entirely by configEvents.* to match the + // filter task (hResonanceCorrelationFilter.cxx) exactly. + Configurable selectINELgtZERO{"selectINELgtZERO", true, "select INEL>0 events (generator level, see processPrediction)"}; + // Still used directly by the closure-test/prediction blocks (reconstructed + // vs. generated z-vertex checks), independent of isSelectedEvents(). + Configurable zVertexCut{"zVertexCut", 10, "Cut on PV position (closure test / prediction only)"}; Configurable skipUnderOverflowInTHn{"skipUnderOverflowInTHn", false, "skip under/overflow in THns"}; Configurable mixingParameter{"mixingParameter", 10, "how many events are mixed"}; Configurable doMCassociation{"doMCassociation", false, "fill everything only for MC associated"}; @@ -122,23 +116,57 @@ struct HResonanceCorrelation { Configurable fillCorrelationHistWithMass{"fillCorrelationHistWithMass", false, "if true, fill correlation histograms with particle mass"}; } masterConfigurations; - // master analysis switches - Configurable doAssocPhysicalPrimary{"doAssocPhysicalPrimary", false, "require physical primary for associated particles"}; - Configurable doAssocPhysicalPrimaryInGen{"doAssocPhysicalPrimaryInGen", false, "require physical primary for associated particles in Generated Partilces"}; - Configurable doAutocorrelationRejection{"doAutocorrelationRejection", true, "reject pairs where trigger Id is the same as daughter particle Id"}; - Configurable doMixingQAandEventQA{"doMixingQAandEventQA", true, "if true, add EvnetQA and MixingQA hist to histos"}; - Configurable doITSClustersQA{"doITSClustersQA", true, "if true, add ITSCluster hist to histos"}; - Configurable doDeltaPhiStarCheck{"doDeltaPhiStarCheck", false, "if true, create and fill delta phi star histograms"}; - - Configurable triggerBinToSelect{"triggerBinToSelect", 0, "trigger bin to select on if processSelectEventWithTrigger enabled"}; - Configurable triggerParticleCharge{"triggerParticleCharge", 0, "For checks, if 0 all charged tracks, if -1 only neg., if 1 only positive"}; - Configurable etaSel{"etaSel", 0.8, "Selection in eta for trigger and associated particles"}; - Configurable ySel{"ySel", 0.5, "Selection in rapidity for consistency checks"}; + // Trigger-selection-mode flags: which trigger-pT bin to require (if + // processSelectEventWithTrigger is enabled), a charge cut on the trigger + // particle, and whether to use the event's leading particle as the sole + // trigger instead of every trigger candidate. + struct : ConfigurableGroup { + std::string prefix = "triggerSelection"; + Configurable triggerBinToSelect{"triggerBinToSelect", 0, "trigger bin to select on if processSelectEventWithTrigger enabled"}; + Configurable triggerParticleCharge{"triggerParticleCharge", 0, "For checks, if 0 all charged tracks, if -1 only neg., if 1 only positive"}; + Configurable useTheLeadingParticleAsTrigger{"useTheLeadingParticleAsTrigger", false, "if true, use the leading particle in the event as trigger particle"}; + } triggerSelection; + + // MC-truth physical-primary requirements, reconstructed-level and + // generator-level respectively. + struct : ConfigurableGroup { + std::string prefix = "mcSelections"; + Configurable doAssocPhysicalPrimary{"doAssocPhysicalPrimary", false, "require physical primary for associated particles"}; + Configurable doAssocPhysicalPrimaryInGen{"doAssocPhysicalPrimaryInGen", false, "require physical primary for associated particles in Generated Partilces"}; + } mcSelections; - Configurable useTheLeadingParticleAsTrigger{"useTheLeadingParticleAsTrigger", false, "if true, use the leading particle in the event as trigger particle"}; - // used for event selections in Pb-Pb - Configurable cfgCutOccupancyHigh{"cfgCutOccupancyHigh", 3000, "High cut on TPC occupancy"}; - Configurable cfgCutOccupancyLow{"cfgCutOccupancyLow", 0, "Low cut on TPC occupancy"}; + // Histogram-booking/filling toggles, not physics cuts. + struct : ConfigurableGroup { + std::string prefix = "qaSwitches"; + Configurable doMixingQAandEventQA{"doMixingQAandEventQA", true, "if true, add EvnetQA and MixingQA hist to histos"}; + Configurable doDeltaPhiStarCheck{"doDeltaPhiStarCheck", false, "if true, create and fill delta phi star histograms"}; + } qaSwitches; + + // Generic phase-space cuts used across trigger, associated AND gen-level + // (MC-truth) particles alike -- not species- or reco/gen-specific, so not + // folded into configTriggerTracks/configAssocTracks. + struct : ConfigurableGroup { + std::string prefix = "phaseSpaceSelections"; + Configurable etaSel{"etaSel", 0.8, "Selection in eta for trigger and associated particles"}; + Configurable ySel{"ySel", 0.5, "Selection in rapidity for consistency checks"}; + } phaseSpaceSelections; + + // Reconstructed-event selection cuts -- deliberately the same cut set and + // names as configEvents in hResonanceCorrelationFilter.cxx, so a collision + // that was selected to produce the trigger/associated/candidate tables is + // selected identically here (previously this task re-derived its own, + // different cut list via isisCollisionSelect()/isisCollisionSelectPbPb(), + // which could disagree with the filter task's selection). + struct : ConfigurableGroup { + std::string prefix = "configEvents"; + Configurable cfgEvtZvtx{"cfgEvtZvtx", 10.0f, "Evt sel: Max. z-Vertex (cm)"}; + Configurable cfgEvtTriggerTVXSel{"cfgEvtTriggerTVXSel", true, "Evt sel: triggerTVX selection (MB)"}; + Configurable cfgEvtNoTFBorderCut{"cfgEvtNoTFBorderCut", true, "Evt sel: apply TF border cut"}; + Configurable cfgEvtNoITSROFrameBorderCut{"cfgEvtNoITSROFrameBorderCut", true, "Evt sel: apply NoITSRO border cut"}; + Configurable cfgEvtSel8{"cfgEvtSel8", true, "Evt Sel 8 check for offline selection"}; + Configurable cfgEvtNoSameBunchPileupCut{"cfgEvtNoSameBunchPileupCut", true, "Evt sel: reject collisions associated with the same found-by-T0 bunch crossing"}; + Configurable cfgEvtGoodZvtxFT0vsPVCut{"cfgEvtGoodZvtxFT0vsPVCut", true, "Evt sel: require small difference between z-vertex from PV and from FT0"}; + } configEvents; // Axes - configurable for smaller sizes struct : ConfigurableGroup { @@ -220,41 +248,51 @@ struct HResonanceCorrelation { Configurable applyEffAsFunctionOfMult{"applyEffAsFunctionOfMult", false, "apply efficiency as a function of multiplicity as well"}; Configurable applyEffAsFunctionOfMultAndPhi{"applyEffAsFunctionOfMultAndPhi", false, "apply efficiency as a function of multiplicity and phi"}; } efficiencyFlags; - Configurable ccdburl{"ccdburl", "http://alice-ccdb.cern.ch", "url of the ccdb repository to use"}; - Configurable efficiencyCCDBPath{"efficiencyCCDBPath", "GLO/Config/GeometryAligned", "Path of the efficiency corrections"}; - - // Configurables for doing subwagon systematics struct : ConfigurableGroup { - std::string prefix = "trackSelection"; - // --- Track quality variations (single track, both trigger and assoc daughters) + std::string prefix = "ccdbConfigurations"; + Configurable ccdburl{"ccdburl", "http://alice-ccdb.cern.ch", "url of the ccdb repository to use"}; + Configurable efficiencyCCDBPath{"efficiencyCCDBPath", "GLO/Config/GeometryAligned", "Path of the efficiency corrections"}; + } ccdbConfigurations; + + // Configurables for doing subwagon systematics. + // Split by who they apply to (trigger hadron vs. associated particle). + // requireDCAzCut is the one genuinely shared switch (it toggles the DCAz + // cut for BOTH isValidTrigger() and the assoc selection); rather than its + // own single-entry group, it lives in `checks` below alongside the other + // small behavioral toggles. + struct : ConfigurableGroup { + std::string prefix = "configTriggerTracks"; Configurable minTPCNCrossedRowsTrigger{"minTPCNCrossedRowsTrigger", 70, "Minimum TPC crossed rows (trigger)"}; - Configurable minTPCNCrossedRowsAssociated{"minTPCNCrossedRowsAssociated", 70, "Minimum TPC crossed rows (associated)"}; Configurable triggerRequireITS{"triggerRequireITS", true, "require ITS signal in trigger tracks"}; - Configurable assocRequireITS{"assocRequireITS", true, "require ITS signal in associated primary tracks"}; Configurable triggerMaxTPCSharedClusters{"triggerMaxTPCSharedClusters", 200, "maximum number of shared TPC clusters (inclusive)"}; - Configurable assocMaxTPCSharedClusters{"assocMaxTPCSharedClusters", 200, "maximum number of shared TPC clusters (inclusive) for assoc primary tracks"}; Configurable triggerRequireL0{"triggerRequireL0", false, "require ITS L0 cluster for trigger"}; - Configurable assocRequireL0{"assocRequireL0", true, "require ITS L0 cluster for assoc primary track"}; - Configurable requireDCAzCut{"requireDCAzCut", false, "require DCAz cut for trigger and associated primary tracks"}; - - // --- Trigger: DCA variation from basic formula: |DCAxy| < 0.004f + (0.013f / pt) + // DCA variation from basic formula: |DCAxy| < 0.004f + (0.013f / pt) Configurable dcaXYconstant{"dcaXYconstant", 0.004, "[0] in |DCAxy| < [0]+[1]/pT"}; Configurable dcaXYpTdep{"dcaXYpTdep", 0.013, "[1] in |DCAxy| < [0]+[1]/pT"}; - // --- Assoc track: DCA variation from basic formula: |DCAxy| < 0.004f + (0.013f / pt) - Configurable dcaXYconstantAssoc{"dcaXYconstantAssoc", 0.004, "[0] in |DCAxy| < [0]+[1]/pT"}; - Configurable dcaXYpTdepAssoc{"dcaXYpTdepAssoc", 0.013, "[1] in |DCAxy| < [0]+[1]/pT"}; - Configurable dcaZconstant{"dcaZconstant", 0.004, "[0] in |DCAz| < [0]+[1]/pT"}; Configurable dcaZpTdep{"dcaZpTdep", 0.013, "[1] in |DCAz| < [0]+[1]/pT"}; + } configTriggerTracks; + + struct : ConfigurableGroup { + std::string prefix = "configAssocTracks"; + Configurable minTPCNCrossedRowsAssociated{"minTPCNCrossedRowsAssociated", 70, "Minimum TPC crossed rows (associated)"}; + Configurable assocRequireITS{"assocRequireITS", true, "require ITS signal in associated primary tracks"}; + Configurable assocMaxTPCSharedClusters{"assocMaxTPCSharedClusters", 200, "maximum number of shared TPC clusters (inclusive) for assoc primary tracks"}; + Configurable assocRequireL0{"assocRequireL0", true, "require ITS L0 cluster for assoc primary track"}; + // DCA variation from basic formula: |DCAxy| < 0.004f + (0.013f / pt) + Configurable dcaXYconstantAssoc{"dcaXYconstantAssoc", 0.004, "[0] in |DCAxy| < [0]+[1]/pT"}; + Configurable dcaXYpTdepAssoc{"dcaXYpTdepAssoc", 0.013, "[1] in |DCAxy| < [0]+[1]/pT"}; Configurable dcaZconstantAssoc{"dcaZconstantAssoc", 0.004, "[0] in |DCAz| < [0]+[1]/pT"}; Configurable dcaZpTdepAssoc{"dcaZpTdepAssoc", 0.013, "[1] in |DCAz| < [0]+[1]/pT"}; - } trackSelection; + } configAssocTracks; struct : ConfigurableGroup { std::string prefix = "checks"; // on the fly correction instead of mixingParameter Configurable doOnTheFlyFlattening{"doOnTheFlyFlattening", 0, "enable an on-the-fly correction instead of using mixing"}; + Configurable doAutocorrelationRejection{"doAutocorrelationRejection", true, "reject pairs where trigger Id is the same as daughter particle Id"}; + Configurable requireDCAzCut{"requireDCAzCut", false, "require DCAz cut for trigger and associated primary tracks"}; } checks; struct ValidCollision { @@ -348,8 +386,12 @@ struct HResonanceCorrelation { static constexpr int IndexHadron = 4; uint16_t doCorrelation = 0; + // Which species' processSameEventH() is responsible for filling the + // shared CollCutCounts/EventQA histograms for a given collision, computed + // once in init() -- see the comment there. -1 means none of the same-event + // process functions are actually enabled (bit + switch), so nothing fills them. + int eventQAOwnerIndex = -1; int mRunNumber = 0; - int mRunNumberZorro = 0; std::vector> axisRanges; @@ -371,20 +413,6 @@ struct HResonanceCorrelation { return shiftedDeltaPhi; } - /// Function to load zorro - /// \param bc provided such that the run number + timestamp can be used - void initZorro(aod::BCsWithTimestamps::iterator const& bc) - { - if (mRunNumberZorro == bc.runNumber()) { - return; - } - - zorro.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), zorroMask.value); - zorro.populateHistRegistry(histos, bc.runNumber()); - - mRunNumberZorro = bc.runNumber(); - } - /// Function to load efficiencies to memory from CCDB /// \param bc provided such that the run number can be used void initEfficiencyFromCCDB(aod::BCsWithTimestamps::iterator const& bc) @@ -396,7 +424,7 @@ struct HResonanceCorrelation { LOG(info) << "Loading efficiencies from CCDB for run " << mRunNumber << " now..."; auto timeStamp = bc.timestamp(); - TList* listEfficiencies = ccdb->getForTimeStamp(efficiencyCCDBPath, timeStamp); + TList* listEfficiencies = ccdb->getForTimeStamp(ccdbConfigurations.efficiencyCCDBPath, timeStamp); if (!listEfficiencies) { LOG(fatal) << "Problem getting TList object with efficiencies!"; @@ -434,9 +462,7 @@ struct HResonanceCorrelation { void init(InitContext const&) { - zorroSummary.setObject(zorro.getZorroSummary()); mRunNumber = 0; - mRunNumberZorro = 0; hEfficiencyPion = 0x0; hEfficiencyKaon = 0x0; hEfficiencyPhi = 0x0; @@ -465,6 +491,27 @@ struct HResonanceCorrelation { if (masterConfigurations.doCorrelationHadron) SETBIT(doCorrelation, IndexHadron); + // Pick exactly one "owner" species for the shared CollCutCounts/EventQA + // fills: the highest-priority species (Phi > Kstar > Pion > Kaon > + // Hadron) that will actually run its processSameEventH() (both its + // doCorrelation bit AND its PROCESS_SWITCH on). Every processSameEventH + // calls isSelectedEvents(collision, eventQAOwnerIndex == IndexX) -- only + // the owner's call passes fillHist=true, so a collision examined by + // several same-event process functions in the same run (e.g. Phi and + // K*0 both enabled) is still only counted once, instead of once per + // enabled species. + if (TESTBIT(doCorrelation, IndexPhi) && doprocessSameEventHPhis) { + eventQAOwnerIndex = IndexPhi; + } else if (TESTBIT(doCorrelation, IndexKstar) && doprocessSameEventHKstars) { + eventQAOwnerIndex = IndexKstar; + } else if (TESTBIT(doCorrelation, IndexPion) && doprocessSameEventHPions) { + eventQAOwnerIndex = IndexPion; + } else if (TESTBIT(doCorrelation, IndexKaon) && doprocessSameEventHKaons) { + eventQAOwnerIndex = IndexKaon; + } else if (TESTBIT(doCorrelation, IndexHadron) && doprocessSameEventHHadrons) { + eventQAOwnerIndex = IndexHadron; + } + // Store axis ranges to prevent spurious filling // axis status: // --- Delta-phi is safe -> math forbids insanity @@ -642,13 +689,20 @@ struct HResonanceCorrelation { validCollisions[i].reserve(masterConfigurations.mixingParameter); } } - if (!masterConfigurations.doPPAnalysis) { - // event selections in Pb-Pb - histos.add("hEventSelection", "hEventSelection", kTH1F, {{10, 0, 10}}); - TString eventSelLabel[] = {"all", "sel8", "kIsTriggerTVX", "PV_{z}", "kIsGoodITSLayersAll", "kIsGoodZvtxFT0vsPV", "OccupCut", "kNoTimeFrameBorder", "kNoITSROFrameBorder", "kNoSameBunchPileup "}; - for (int i = 1; i <= histos.get(HIST("hEventSelection"))->GetNbinsX(); i++) { - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(i, eventSelLabel[i - 1]); - } + // Event-selection cut-flow, same cut set/order/labels as CollCutCounts in + // hResonanceCorrelationFilter.cxx, filled by isSelectedEvents() -- see + // eventQAOwnerIndex above for why only one process function fills it. + if (eventQAOwnerIndex != -1) { + histos.add("CollCutCounts", "No. of event after cuts", kTH1I, {{10, 0, 10}}); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(1, "All Events"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(2, "|Vz| < cut"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(3, "kIsTriggerTVX"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(4, "kNoTimeFrameBorder"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(5, "kNoITSROFrameBorder"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(6, "sel8"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(7, "kNoSameBunchPileup"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(8, "kIsGoodZvtxFT0vsPV"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(9, "All Passed Events"); } // ======================================================================== @@ -657,7 +711,7 @@ struct HResonanceCorrelation { bool anySameEvent = doprocessSameEventHPhis || doprocessSameEventHKstars || doprocessSameEventHPions || doprocessSameEventHKaons || doprocessSameEventHHadrons; bool anyMixedEvent = doprocessMixedEventHPhis || doprocessMixedEventHPhisInBuffer || doprocessMixedEventHKstars || doprocessMixedEventHKstarsInBuffer || doprocessMixedEventHPions || doprocessMixedEventHKaons || doprocessMixedEventHHadrons; - if (doMixingQAandEventQA && (anySameEvent || anyMixedEvent)) { + if (qaSwitches.doMixingQAandEventQA && (anySameEvent || anyMixedEvent)) { if (anySameEvent) histos.add("MixingQA/hSECollisionBins", ";bin;Entries", kTH1F, {{140, -0.5, 139.5}}); if (anyMixedEvent) { @@ -708,6 +762,24 @@ struct HResonanceCorrelation { doME = doprocessMixedEventHHadrons; } + // Same-/mixed-event invariant-mass cross-check (Phi/K*0 only). Booked + // independent of doFullCorrelationStudy/fillCorrelationHistWithMass + // since these are standalone QA histograms, not part of the + // Signal/LeftBg/RightBg THnF family -- just need doSE/doME (the + // process switch(es) for this species) to actually get filled. + if (i == IndexPhi && doSE) { + histos.add("hPhiInvMass", "Phi candidate invariant mass, same event", kTH3F, {axesConfigurations.axisPhiMass, axesConfigurations.axisPtAssoc, axesConfigurations.axisMult}); + } + if (i == IndexKstar && doSE) { + histos.add("hKstarInvMass", "K*0 candidate invariant mass, same event", kTH3F, {axesConfigurations.axisKstarMass, axesConfigurations.axisPtAssoc, axesConfigurations.axisMult}); + } + if (i == IndexPhi && doME) { + histos.add("hPhiMixedEventInvMass", "Phi candidate invariant mass, mixed event", kTH3F, {axesConfigurations.axisPhiMass, axesConfigurations.axisPtAssoc, axesConfigurations.axisMult}); + } + if (i == IndexKstar && doME) { + histos.add("hKstarMixedEventInvMass", "K*0 candidate invariant mass, mixed event", kTH3F, {axesConfigurations.axisKstarMass, axesConfigurations.axisPtAssoc, axesConfigurations.axisMult}); + } + // Kinematic QA (Only for Phi) if (i < IndexPion && (doSE || doME || doprocessPrediction || doprocessClosureTest)) { if (!efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { @@ -754,7 +826,7 @@ struct HResonanceCorrelation { } else { histos.add(fmt::format("sameEvent/Signal/{}", Particlenames[i]).c_str(), "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); } - if (doDeltaPhiStarCheck) + if (qaSwitches.doDeltaPhiStarCheck) histos.add(fmt::format("sameEvent/Signal/{}DeltaPhiStar", Particlenames[i]).c_str(), "", kTH3F, {{100, -0.3, 0.3}, {50, -0.05, 0.05}, {2, -1, 1}}); if ((i == IndexPhi || i == IndexKstar) && !masterConfigurations.fillCorrelationHistWithMass) { needsSignalBgCloneSE = true; @@ -771,7 +843,7 @@ struct HResonanceCorrelation { } else { histos.add(fmt::format("mixedEvent/Signal/{}", Particlenames[i]).c_str(), "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); } - if (doDeltaPhiStarCheck) + if (qaSwitches.doDeltaPhiStarCheck) histos.add(fmt::format("mixedEvent/Signal/{}DeltaPhiStar", Particlenames[i]).c_str(), "", kTH3F, {{100, -0.3, 0.3}, {50, -0.05, 0.05}, {2, -1, 1}}); if ((i == IndexPhi || i == IndexKstar) && !masterConfigurations.fillCorrelationHistWithMass) { needsSignalBgCloneME = true; @@ -901,7 +973,7 @@ struct HResonanceCorrelation { if (doprocessPrediction) { mCounter.mPdgDatabase = pdgDB.service; - mCounter.mSelectPrimaries = doAssocPhysicalPrimary.value; + mCounter.mSelectPrimaries = mcSelections.doAssocPhysicalPrimary.value; histos.add("Prediction/hEventSelection", "hEventSelection", kTH1F, {{3, 0, 3}}); TString eventSelLabel[] = {"Read", "INELgt0", "|Z|<10"}; for (int i = 1; i <= histos.get(HIST("Prediction/hEventSelection"))->GetNbinsX(); i++) { @@ -948,158 +1020,123 @@ struct HResonanceCorrelation { // initialize CCDB *only* if efficiency correction requested // skip if not requested, saves a bit of time if (efficiencyFlags.applyEfficiencyCorrection) { - ccdb->setURL(ccdburl); + ccdb->setURL(ccdbConfigurations.ccdburl); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); ccdb->setFatalWhenNull(false); } } - // this function allows for all event selections to be done in a modular way + // Single event-selection predicate, cut-for-cut and bin-for-bin the same as + // isSelectedEvents() in hResonanceCorrelationFilter.cxx, so a collision is + // selected identically whether the filter task is producing its tables or + // this task is correlating them. Replaces the former isisCollisionSelect() + // (pp) / isisCollisionSelectPbPb() (Pb-Pb) pair, which carried their own, + // different cut lists (centrality-range, INEL>0, ITS-good-layers, TPC + // occupancy) that could disagree with the filter task's selection. + // + // fillHist controls the CollCutCounts cut-flow and EventQA/hMult+hPvz + // fills: pass true only from the one process function that "owns" this + // collision for QA purposes (see eventQAOwnerIndex in init()) so a + // collision examined by several enabled same-event process functions is + // still only counted once. Mixed-event callers always pass false, since ME + // revisits the same collisions many times per mixing pool and has its own + // separate MixingQA histograms. template - bool isisCollisionSelect(TCollision const& collision) + bool isSelectedEvents(TCollision const& collision, bool fillHist = true) { - // ________________________________________________ - // Perform basic event selection - if (!collision.sel8()) { - return false; - } - if (std::abs(collision.posZ()) > masterConfigurations.zVertexCut) { - return false; - } - if (collision.centFT0M() > axisRanges[5][1] || collision.centFT0M() < axisRanges[5][0]) { - return false; - } - if (!collision.isInelGt0() && masterConfigurations.selectINELgtZERO) { - return false; - } - if (!collision.selection_bit(aod::evsel::kIsGoodITSLayersAll) && masterConfigurations.requireAllGoodITSLayers) { - return false; - } - if (zorroMask.value != "") { - auto bc = collision.template bc_as(); - initZorro(bc); - bool zorroSelected = zorro.isSelected(collision.template bc_as().globalBC()); /// Just let Zorro do the accounting - if (!zorroSelected) { + auto applyCut = [&](bool enabled, bool condition, int bin) { + if (!enabled) { + return true; + } + if (!condition) { return false; } - } - return true; - } - - // event selections in Pb-Pb - template - bool isisCollisionSelectPbPb(TCollision const& collision, bool fillHists) - { - if (fillHists) - histos.fill(HIST("hEventSelection"), 0.5 /* all collisions */); + if (fillHist) { + histos.fill(HIST("CollCutCounts"), bin); + } + return true; + }; - // Perform basic event selection - if (!collision.sel8()) { - return false; + if (fillHist) { + histos.fill(HIST("CollCutCounts"), 0); } - if (fillHists) - histos.fill(HIST("hEventSelection"), 1.5 /* collisions after sel8*/); - if (!collision.selection_bit(aod::evsel::kIsTriggerTVX) && masterConfigurations.requireGoodTriggerTVX) { + if (!applyCut(true, std::abs(collision.posZ()) <= configEvents.cfgEvtZvtx, 1)) { return false; } - if (fillHists) - histos.fill(HIST("hEventSelection"), 2.5 /* FT0 vertex (acceptable FT0C-FT0A time difference) collisions */); - if (std::abs(collision.posZ()) > masterConfigurations.zVertexCut) { + if (!applyCut(configEvents.cfgEvtTriggerTVXSel, + collision.selection_bit(aod::evsel::kIsTriggerTVX), 2)) { return false; } - if (fillHists) - histos.fill(HIST("hEventSelection"), 3.5 /* collisions after sel pvz sel*/); - if (!collision.selection_bit(aod::evsel::kIsGoodITSLayersAll) && masterConfigurations.requireAllGoodITSLayers) { - // cut time intervals with dead ITS staves + if (!applyCut(configEvents.cfgEvtNoTFBorderCut, + collision.selection_bit(aod::evsel::kNoTimeFrameBorder), 3)) { return false; } - if (fillHists) - histos.fill(HIST("hEventSelection"), 4.5 /* collisions after cut time intervals with dead ITS staves*/); - if (!collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV) && masterConfigurations.requireGoodZvtxFT0vsPV) { - // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference - // use this cut at low multiplicities with caution + if (!applyCut(configEvents.cfgEvtNoITSROFrameBorderCut, + collision.selection_bit(aod::evsel::kNoITSROFrameBorder), 4)) { return false; } - if (fillHists) - histos.fill(HIST("hEventSelection"), 5.5 /* removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference*/); - - auto occupancy = collision.trackOccupancyInTimeRange(); - if (occupancy < cfgCutOccupancyLow || occupancy > cfgCutOccupancyHigh) - return false; - if (fillHists) - histos.fill(HIST("hEventSelection"), 6.5 /* Below min occupancy and Above max occupancy*/); - /* - if (collision.alias_bit(kTVXinTRD)) { - // TRD triggered + if (!applyCut(configEvents.cfgEvtSel8, collision.sel8(), 5)) { return false; } - */ - if (!collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { - // reject collisions close to Time Frame borders - // O2-4623 + if (!applyCut(configEvents.cfgEvtNoSameBunchPileupCut, + collision.selection_bit(aod::evsel::kNoSameBunchPileup), 6)) { return false; } - if (fillHists) - histos.fill(HIST("hEventSelection"), 7.5 /* reject collisions close to Time Frame borders*/); - if (!collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { - // reject events affected by the ITS ROF border - // O2-4309 + if (!applyCut(configEvents.cfgEvtGoodZvtxFT0vsPVCut, + collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV), 7)) { return false; } - if (fillHists) - histos.fill(HIST("hEventSelection"), 8.5 /* reject events affected by the ITS ROF border*/); - if (!collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { - // rejects collisions which are associated with the same "found-by-T0" bunch crossing - // https://indico.cern.ch/event/1396220/#1-event-selection-with-its-rof - return false; + if (fillHist) { + histos.fill(HIST("CollCutCounts"), 8); + histos.fill(HIST("EventQA/hMult"), collision.centFT0M()); + histos.fill(HIST("EventQA/hPvz"), collision.posZ()); } - if (fillHists) - histos.fill(HIST("hEventSelection"), 9.5 /* rejects collisions which are associated with the same "found-by-T0" bunch crossing*/); + return true; } template bool isValidTrigger(TTrack const& track, bool isLeading) { - if (track.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsTrigger) { + if (track.tpcNClsCrossedRows() < configTriggerTracks.minTPCNCrossedRowsTrigger) { return false; // crossed rows } - if (!track.hasITS() && trackSelection.triggerRequireITS) { + if (!track.hasITS() && configTriggerTracks.triggerRequireITS) { return false; // skip, doesn't have ITS signal (skips lots of TPC-only!) } - if (track.tpcNClsShared() > trackSelection.triggerMaxTPCSharedClusters) { + if (track.tpcNClsShared() > configTriggerTracks.triggerMaxTPCSharedClusters) { return false; // skip, has shared clusters } - if (!(TESTBIT(track.itsClusterMap(), 0)) && trackSelection.triggerRequireL0) { + if (!(TESTBIT(track.itsClusterMap(), 0)) && configTriggerTracks.triggerRequireL0) { return false; // skip, doesn't have cluster in ITS L0 } // systematic variations: trigger DCAxy - if (std::abs(track.dcaXY()) > trackSelection.dcaXYconstant + trackSelection.dcaXYpTdep * std::abs(track.signed1Pt())) { + if (std::abs(track.dcaXY()) > configTriggerTracks.dcaXYconstant + configTriggerTracks.dcaXYpTdep * std::abs(track.signed1Pt())) { return false; } // systematic variations: trigger DCAz - if (trackSelection.requireDCAzCut && std::abs(track.dcaZ()) > trackSelection.dcaZconstant + trackSelection.dcaZpTdep * std::abs(track.signed1Pt())) { + if (checks.requireDCAzCut && std::abs(track.dcaZ()) > configTriggerTracks.dcaZconstant + configTriggerTracks.dcaZpTdep * std::abs(track.signed1Pt())) { return false; } if (track.pt() > axisRanges[3][1] || track.pt() < axisRanges[3][0]) { return false; } - if (triggerParticleCharge > 0 && track.sign() < 0) { + if (triggerSelection.triggerParticleCharge > 0 && track.sign() < 0) { return false; } - if (triggerParticleCharge < 0 && track.sign() > 0) { + if (triggerSelection.triggerParticleCharge < 0 && track.sign() > 0) { return false; } - if (useTheLeadingParticleAsTrigger && !isLeading) { + if (triggerSelection.useTheLeadingParticleAsTrigger && !isLeading) { return false; } return true; @@ -1107,24 +1144,24 @@ struct HResonanceCorrelation { template bool isValidAssocHadron(TTrack const& track) { - if (track.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { + if (track.tpcNClsCrossedRows() < configAssocTracks.minTPCNCrossedRowsAssociated) { return false; // crossed rows } - if (!track.hasITS() && trackSelection.assocRequireITS) { + if (!track.hasITS() && configAssocTracks.assocRequireITS) { return false; // skip, doesn't have ITS signal (skips lots of TPC-only!) } - if (track.tpcNClsShared() > trackSelection.assocMaxTPCSharedClusters) { + if (track.tpcNClsShared() > configAssocTracks.assocMaxTPCSharedClusters) { return false; // skip, has shared clusters } - if (!(TESTBIT(track.itsClusterMap(), 0)) && trackSelection.assocRequireL0) { + if (!(TESTBIT(track.itsClusterMap(), 0)) && configAssocTracks.assocRequireL0) { return false; // skip, doesn't have cluster in ITS L0 } // systematic variations: trigger DCAxy - if (std::abs(track.dcaXY()) > trackSelection.dcaXYconstantAssoc + trackSelection.dcaXYpTdepAssoc * std::abs(track.signed1Pt())) { + if (std::abs(track.dcaXY()) > configAssocTracks.dcaXYconstantAssoc + configAssocTracks.dcaXYpTdepAssoc * std::abs(track.signed1Pt())) { return false; } // systematic variations: trigger DCAz - if (trackSelection.requireDCAzCut && std::abs(track.dcaZ()) > trackSelection.dcaZconstantAssoc + trackSelection.dcaZpTdepAssoc * std::abs(track.signed1Pt())) { + if (checks.requireDCAzCut && std::abs(track.dcaZ()) > configAssocTracks.dcaZconstantAssoc + configAssocTracks.dcaZpTdepAssoc * std::abs(track.signed1Pt())) { return false; } if (track.pt() > axisRanges[2][1] || track.pt() < axisRanges[2][0]) { @@ -1241,7 +1278,7 @@ struct HResonanceCorrelation { auto assoc = assocTrack.template track_as(); //---] removing autocorrelations [--- - if (doAutocorrelationRejection) { + if (checks.doAutocorrelationRejection) { if (trigg.globalIndex() == assoc.globalIndex()) { if constexpr (Species == IndexPion) histos.fill(HIST("hNumberOfRejectedPairsPion"), 0.5); @@ -1255,7 +1292,7 @@ struct HResonanceCorrelation { //---] track quality check [--- if (!isValidAssocHadron(assoc)) continue; - if (doAssocPhysicalPrimary && !assocTrack.mcPhysicalPrimary()) { + if (mcSelections.doAssocPhysicalPrimary && !assocTrack.mcPhysicalPrimary()) { continue; } float deltaphi = computeDeltaPhi(trigg.phi(), assoc.phi()); @@ -1337,22 +1374,22 @@ struct HResonanceCorrelation { if (!mixing) { if constexpr (Species == IndexPion) { fillCorrelationHistogram(histos.get(HIST("sameEvent/Signal/Pion")), binFillThn, etaWeight, efficiency * efficiencyTrigger, totalEffUncert, purity * purityTrigger, totalPurityUncert); - if (triggSign == assocSign && doDeltaPhiStarCheck) { + if (triggSign == assocSign && qaSwitches.doDeltaPhiStarCheck) { histos.fill(HIST("sameEvent/Signal/Pion") + HIST("DeltaPhiStar"), deltaPhiStar, trigg.eta() - assoc.eta(), 0.5); - } else if (doDeltaPhiStarCheck) { + } else if (qaSwitches.doDeltaPhiStarCheck) { histos.fill(HIST("sameEvent/Signal/Pion") + HIST("DeltaPhiStar"), deltaPhiStar, trigg.eta() - assoc.eta(), -0.5); } } else if constexpr (Species == IndexKaon) { fillCorrelationHistogram(histos.get(HIST("sameEvent/Signal/Kaon")), binFillThn, etaWeight, efficiency * efficiencyTrigger, totalEffUncert, purity * purityTrigger, totalPurityUncert); - if (triggSign == assocSign && doDeltaPhiStarCheck) { + if (triggSign == assocSign && qaSwitches.doDeltaPhiStarCheck) { histos.fill(HIST("sameEvent/Signal/Kaon") + HIST("DeltaPhiStar"), deltaPhiStar, trigg.eta() - assoc.eta(), 0.5); - } else if (doDeltaPhiStarCheck) { + } else if (qaSwitches.doDeltaPhiStarCheck) { histos.fill(HIST("sameEvent/Signal/Kaon") + HIST("DeltaPhiStar"), deltaPhiStar, trigg.eta() - assoc.eta(), -0.5); } } else { - if (triggSign == assocSign && doDeltaPhiStarCheck) { + if (triggSign == assocSign && qaSwitches.doDeltaPhiStarCheck) { histos.fill(HIST("sameEvent/Signal/Hadron") + HIST("DeltaPhiStar"), deltaPhiStar, trigg.eta() - assoc.eta(), 0.5); - } else if (doDeltaPhiStarCheck) { + } else if (qaSwitches.doDeltaPhiStarCheck) { histos.fill(HIST("sameEvent/Signal/Hadron") + HIST("DeltaPhiStar"), deltaPhiStar, trigg.eta() - assoc.eta(), -0.5); } fillCorrelationHistogram(histos.get(HIST("sameEvent/Signal/Hadron")), binFillThn, etaWeight, efficiency * efficiencyTrigger, totalEffUncert, purity * purityTrigger, totalPurityUncert); @@ -1400,7 +1437,7 @@ struct HResonanceCorrelation { int32_t globalIndexPos, globalIndexNeg; float efficiency, efficiencyError; bool isLeftBg, isSignal, isRightBg; - bool passesMcSelection; // (!doMCassociation || mcTruePhi()) && (!doAssocPhysicalPrimary || mcPhysicalPrimary()) + bool passesMcSelection; // (!doMCassociation || mcTruePhi()) && (!mcSelections.doAssocPhysicalPrimary || mcPhysicalPrimary()) }; std::vector cache; cache.reserve(assocs.size()); @@ -1448,7 +1485,7 @@ struct HResonanceCorrelation { c.isRightBg = (massWindowConfigurationsPhi.minBgNSigma * sig < delta && delta < massWindowConfigurationsPhi.maxBgNSigma * sig); c.passesMcSelection = (!masterConfigurations.doMCassociation || assocCandidate.mcTruePhi()) && - (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()); + (!mcSelections.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()); // Candidate-level kinematic/spectrum QA, ported from the equivalent // block in hStrangeCorrelation.cxx (h3dSpectrum[Y], @@ -1458,13 +1495,13 @@ struct HResonanceCorrelation { // correlation-pair QA, so filling them again per mixed pair would // overcount each candidate by the mixing-pool depth). // "SpectrumY" mirrors the V0 analog's rapidity cut (std::abs(rapidity) < - // ySel); AssocPhis has no exposed rapidity() accessor in this file, so + // phaseSpaceSelections.ySel); AssocPhis has no exposed rapidity() accessor in this file, so // pseudorapidity (c.eta) is used as a stand-in -- swap in a real // rapidity() call here if/when the table exposes one. if (!mixing) { float nSigmaVal = delta / sig; histos.fill(HIST("h3dPhiSpectrum"), c.pt, mult, nSigmaVal); - if (std::abs(c.eta) < ySel) { + if (std::abs(c.eta) < phaseSpaceSelections.ySel) { histos.fill(HIST("h3dPhiSpectrumY"), c.pt, mult, nSigmaVal); } // matches the two registration branches above (TH3F vs THnF-with-mult, @@ -1543,7 +1580,7 @@ struct HResonanceCorrelation { firstLoop = true; //---] removing autocorrelations [--- - if (doAutocorrelationRejection) { + if (checks.doAutocorrelationRejection) { if (trigg.globalIndex() == c.globalIndexPos || trigg.globalIndex() == c.globalIndexNeg) { histos.fill(HIST("hNumberOfRejectedPairsPhi"), 0.5); continue; @@ -1591,7 +1628,7 @@ struct HResonanceCorrelation { if (!mixing && c.isLeftBg && !masterConfigurations.fillCorrelationHistWithMass) { fillCorrelationHistogram(histos.get(HIST("sameEvent/LeftBg/Phi")), binFillThn, etaWeight, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); - if (doDeltaPhiStarCheck) { + if (qaSwitches.doDeltaPhiStarCheck) { double deltaPhiStarPlus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarPlus, bField); double deltaPhiStarMinus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarMinus, bField); if (triggSign > 0) { @@ -1605,7 +1642,7 @@ struct HResonanceCorrelation { } if (!mixing && (masterConfigurations.fillCorrelationHistWithMass || c.isSignal)) { fillCorrelationHistogram(histos.get(HIST("sameEvent/Signal/Phi")), masterConfigurations.fillCorrelationHistWithMass ? binFillThnMass : binFillThn, etaWeight, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); - if (doDeltaPhiStarCheck) { + if (qaSwitches.doDeltaPhiStarCheck) { double deltaPhiStarPlus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarPlus, bField); double deltaPhiStarMinus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarMinus, bField); if (triggSign > 0) { @@ -1619,7 +1656,7 @@ struct HResonanceCorrelation { } if (!mixing && c.isRightBg && !masterConfigurations.fillCorrelationHistWithMass) { fillCorrelationHistogram(histos.get(HIST("sameEvent/RightBg/Phi")), binFillThn, etaWeight, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); - if (doDeltaPhiStarCheck) { + if (qaSwitches.doDeltaPhiStarCheck) { double deltaPhiStarPlus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarPlus, bField); double deltaPhiStarMinus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarMinus, bField); if (triggSign > 0) { @@ -1675,7 +1712,7 @@ struct HResonanceCorrelation { return; for (const auto& collision : validCollisions[binnumb]) { - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; histos.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision.pvz, collision.mult})); for (const auto& trigger : collision.trigParticles) { @@ -1762,7 +1799,7 @@ struct HResonanceCorrelation { int32_t globalIndexPos, globalIndexNeg; float efficiency, efficiencyError; bool isLeftBg, isSignal, isRightBg; - bool passesMcSelection; // (!doMCassociation || mcTrueKstar()) && (!doAssocPhysicalPrimary || mcPhysicalPrimary()) + bool passesMcSelection; // (!doMCassociation || mcTrueKstar()) && (!mcSelections.doAssocPhysicalPrimary || mcPhysicalPrimary()) }; std::vector cache; cache.reserve(assocs.size()); @@ -1810,14 +1847,14 @@ struct HResonanceCorrelation { c.isRightBg = (massWindowConfigurationsKstar.minBgNSigma * sig < delta && delta < massWindowConfigurationsKstar.maxBgNSigma * sig); c.passesMcSelection = (!masterConfigurations.doMCassociation || assocCandidate.mcTrueKstar()) && - (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()); + (!mcSelections.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()); // Same candidate-level Spectrum/EtaVsPtVsPhi QA as fillCorrelationsPhi // above -- see the comment there for rationale. if (!mixing) { float nSigmaVal = delta / sig; histos.fill(HIST("h3dKstar0Spectrum"), c.pt, mult, nSigmaVal); - if (std::abs(c.eta) < ySel) { + if (std::abs(c.eta) < phaseSpaceSelections.ySel) { histos.fill(HIST("h3dKstar0SpectrumY"), c.pt, mult, nSigmaVal); } if (!efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { @@ -1894,7 +1931,7 @@ struct HResonanceCorrelation { firstLoop = true; //---] removing autocorrelations [--- - if (doAutocorrelationRejection) { + if (checks.doAutocorrelationRejection) { if (trigg.globalIndex() == c.globalIndexPos || trigg.globalIndex() == c.globalIndexNeg) { histos.fill(HIST("hNumberOfRejectedPairsKstar0"), 0.5); continue; @@ -1942,7 +1979,7 @@ struct HResonanceCorrelation { if (!mixing && c.isLeftBg && !masterConfigurations.fillCorrelationHistWithMass) { fillCorrelationHistogram(histos.get(HIST("sameEvent/LeftBg/Kstar0")), binFillThn, etaWeight, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); - if (doDeltaPhiStarCheck) { + if (qaSwitches.doDeltaPhiStarCheck) { double deltaPhiStarPlus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarPlus, bField); double deltaPhiStarMinus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarMinus, bField); if (triggSign > 0) { @@ -1956,7 +1993,7 @@ struct HResonanceCorrelation { } if (!mixing && (masterConfigurations.fillCorrelationHistWithMass || c.isSignal)) { fillCorrelationHistogram(histos.get(HIST("sameEvent/Signal/Kstar0")), masterConfigurations.fillCorrelationHistWithMass ? binFillThnMass : binFillThn, etaWeight, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); - if (doDeltaPhiStarCheck) { + if (qaSwitches.doDeltaPhiStarCheck) { double deltaPhiStarPlus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarPlus, bField); double deltaPhiStarMinus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarMinus, bField); if (triggSign > 0) { @@ -1970,7 +2007,7 @@ struct HResonanceCorrelation { } if (!mixing && c.isRightBg && !masterConfigurations.fillCorrelationHistWithMass) { fillCorrelationHistogram(histos.get(HIST("sameEvent/RightBg/Kstar0")), binFillThn, etaWeight, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); - if (doDeltaPhiStarCheck) { + if (qaSwitches.doDeltaPhiStarCheck) { double deltaPhiStarPlus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarPlus, bField); double deltaPhiStarMinus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarMinus, bField); if (triggSign > 0) { @@ -2029,7 +2066,7 @@ struct HResonanceCorrelation { return; for (const auto& collision : validCollisions[binnumb]) { - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; histos.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision.pvz, collision.mult})); for (const auto& trigger : collision.trigParticles) { @@ -2106,7 +2143,7 @@ struct HResonanceCorrelation { for (auto const& collision : collisions) { // ________________________________________________ // Perform basic event selection - if (!isisCollisionSelect(collision)) { + if (!isSelectedEvents(collision, false)) { continue; } @@ -2126,13 +2163,22 @@ struct HResonanceCorrelation { void processSameEventHHadrons(soa::Join::iterator const& collision, aod::AssocHadrons const& assocHadrons, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { - LOGF(info, "SameEventHadron: collisions=%d triggers=%zu assocHadrons=%zu", collision.globalIndex(), triggerTracks.size(), assocHadrons.size()); + // Guard against the fatal "histogram not found" crash: the per-species + // histograms this function fills are only booked in init() when + // TESTBIT(doCorrelation, IndexHadron) is also set (see init(), ~line 831) + // -- doCorrelation defaults to 0 (masterConfigurations.doCorrelationHadron + // defaults false) while this PROCESS_SWITCH defaults true, so without this + // guard the default config fills unbooked histograms and crashes. + if (!TESTBIT(doCorrelation, IndexHadron)) { + return; + } + // LOGF(info, "SameEventHadron: collisions=%d triggers=%zu assocHadrons=%zu", collision.globalIndex(), triggerTracks.size(), assocHadrons.size()); - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerSelection.triggerBinToSelect)) { return; } @@ -2144,15 +2190,15 @@ struct HResonanceCorrelation { } // ________________________________________________ - // Perform basic event selection - if (!isisCollisionSelect(collision)) { + // Perform basic event selection. fillHist=true only from the owning + // species (see eventQAOwnerIndex in init()) so CollCutCounts/EventQA + // aren't multi-counted when several same-event species run together. + if (!isSelectedEvents(collision, eventQAOwnerIndex == IndexHadron)) { return; } // ________________________________________________ - if (!doprocessSameEventHPhis && !doprocessSameEventHKstars && !doprocessSameEventHPions && !doprocessSameEventHKaons && doMixingQAandEventQA) { + if (eventQAOwnerIndex == IndexHadron && qaSwitches.doMixingQAandEventQA) { histos.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({collision.posZ(), collision.centFT0M()})); - histos.fill(HIST("EventQA/hMult"), collision.centFT0M()); - histos.fill(HIST("EventQA/hPvz"), collision.posZ()); } // Do basic QA @@ -2198,7 +2244,7 @@ struct HResonanceCorrelation { float weight = efficiencyFlags.applyEfficiencyCorrection ? purity / efficiency : 1.0f; histos.fill(HIST("hAssocHadronsAllSelectedEtaVsPt"), assoc.pt(), assoc.eta(), collision.centFT0M(), weight); histos.fill(HIST("hAssocPtResolution"), assoc.pt(), assocTrack.mcOriginalPt()); - if (doAssocPhysicalPrimary && !assocTrack.mcPhysicalPrimary()) + if (mcSelections.doAssocPhysicalPrimary && !assocTrack.mcPhysicalPrimary()) continue; histos.fill(HIST("hAssocPrimaryEtaVsPt"), assoc.pt(), assoc.eta(), collision.centFT0M()); histos.fill(HIST("hAsssocTrackEtaVsPtVsPhi"), assoc.pt(), assoc.eta(), assoc.phi(), weight); @@ -2212,12 +2258,18 @@ struct HResonanceCorrelation { void processSameEventHPions(soa::Join::iterator const& collision, soa::Join const& associatedPions, soa::Join const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { - LOGF(info, "SameEventPion: collisions=%d triggers=%zu assocPions=%zu", collision.globalIndex(), triggerTracks.size(), associatedPions.size()); + // See the matching comment in processSameEventHHadrons: without this, + // the default config (doCorrelationPion=false, this switch=true) fills + // hPionEtaVsPtAllSelected etc. before they're booked -> fatal. + if (!TESTBIT(doCorrelation, IndexPion)) { + return; + } + // LOGF(info, "SameEventPion: collisions=%d triggers=%zu assocPions=%zu", collision.globalIndex(), triggerTracks.size(), associatedPions.size()); - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerSelection.triggerBinToSelect)) { return; } auto bc = collision.bc_as(); @@ -2228,15 +2280,14 @@ struct HResonanceCorrelation { } // ________________________________________________ - // Perform basic event selection - if (!isisCollisionSelect(collision)) { + // Perform basic event selection. See the matching comment in + // processSameEventHHadrons for fillHist/eventQAOwnerIndex. + if (!isSelectedEvents(collision, eventQAOwnerIndex == IndexPion)) { return; } // ________________________________________________ - if (!doprocessSameEventHPhis && !doprocessSameEventHKstars && doMixingQAandEventQA) { + if (eventQAOwnerIndex == IndexPion && qaSwitches.doMixingQAandEventQA) { histos.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({collision.posZ(), collision.centFT0M()})); - histos.fill(HIST("EventQA/hMult"), collision.centFT0M()); - histos.fill(HIST("EventQA/hPvz"), collision.posZ()); } // Do basic QA for (auto const& pion : associatedPions) { @@ -2245,7 +2296,7 @@ struct HResonanceCorrelation { continue; histos.fill(HIST("hPionEtaVsPtAllSelected"), pionTrack.pt(), pionTrack.eta(), collision.centFT0M()); - if (doAssocPhysicalPrimary && !pion.mcPhysicalPrimary()) + if (mcSelections.doAssocPhysicalPrimary && !pion.mcPhysicalPrimary()) continue; if (masterConfigurations.doMCassociation && std::abs(pion.pdgCode()) != PdgCodes[IndexPion]) continue; @@ -2277,12 +2328,19 @@ struct HResonanceCorrelation { void processSameEventHKaons(soa::Join::iterator const& collision, soa::Join const& associatedKaons, soa::Join const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { - LOGF(info, "SameEventKaon: collisions=%d triggers=%zu assocKaons=%zu", collision.globalIndex(), triggerTracks.size(), associatedKaons.size()); + // See the matching comment in processSameEventHHadrons: without this, + // the default config (doCorrelationKaon=false, this switch=true) fills + // hKaonEtaVsPtAllSelected etc. before they're booked -> fatal (this is + // the exact FATAL reported: "hKaonEtaVsPtAllSelected" not found). + if (!TESTBIT(doCorrelation, IndexKaon)) { + return; + } + // LOGF(info, "SameEventKaon: collisions=%d triggers=%zu assocKaons=%zu", collision.globalIndex(), triggerTracks.size(), associatedKaons.size()); - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerSelection.triggerBinToSelect)) { return; } auto bc = collision.bc_as(); @@ -2293,15 +2351,14 @@ struct HResonanceCorrelation { } // ________________________________________________ - // Perform basic event selection - if (!isisCollisionSelect(collision)) { + // Perform basic event selection. See the matching comment in + // processSameEventHHadrons for fillHist/eventQAOwnerIndex. + if (!isSelectedEvents(collision, eventQAOwnerIndex == IndexKaon)) { return; } // ________________________________________________ - if (!doprocessSameEventHPhis && !doprocessSameEventHKstars && !doprocessSameEventHPions && doMixingQAandEventQA) { + if (eventQAOwnerIndex == IndexKaon && qaSwitches.doMixingQAandEventQA) { histos.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({collision.posZ(), collision.centFT0M()})); - histos.fill(HIST("EventQA/hMult"), collision.centFT0M()); - histos.fill(HIST("EventQA/hPvz"), collision.posZ()); } // Do basic QA for (auto const& kaon : associatedKaons) { @@ -2310,7 +2367,7 @@ struct HResonanceCorrelation { continue; histos.fill(HIST("hKaonEtaVsPtAllSelected"), kaonTrack.pt(), kaonTrack.eta(), collision.centFT0M()); - if (doAssocPhysicalPrimary && !kaon.mcPhysicalPrimary()) + if (mcSelections.doAssocPhysicalPrimary && !kaon.mcPhysicalPrimary()) continue; if (masterConfigurations.doMCassociation && std::abs(kaon.pdgCode()) != PdgCodes[IndexKaon]) continue; @@ -2342,12 +2399,18 @@ struct HResonanceCorrelation { void processSameEventHPhis(soa::Join::iterator const& collision, aod::AssocPhis const& associatedPhis, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { - LOGF(info, "SameEventPhi: collisions=%d triggers=%zu assocPhi=%zu", collision.globalIndex(), triggerTracks.size(), associatedPhis.size()); + // See the matching comment in processSameEventHHadrons: without this, + // the default config (doCorrelationPhi=false, this switch=true) fills + // sameEvent/Signal/Phi etc. before they're booked -> fatal. + if (!TESTBIT(doCorrelation, IndexPhi)) { + return; + } + // LOGF(info, "SameEventPhi: collisions=%d triggers=%zu assocPhi=%zu", collision.globalIndex(), triggerTracks.size(), associatedPhis.size()); - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; // Skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerSelection.triggerBinToSelect)) { return; } @@ -2358,14 +2421,14 @@ struct HResonanceCorrelation { initEfficiencyFromCCDB(bc); } - if (!isisCollisionSelect(collision)) { + // See the matching comment in processSameEventHHadrons for + // fillHist/eventQAOwnerIndex. + if (!isSelectedEvents(collision, eventQAOwnerIndex == IndexPhi)) { return; } - if (doMixingQAandEventQA) { + if (eventQAOwnerIndex == IndexPhi && qaSwitches.doMixingQAandEventQA) { histos.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({collision.posZ(), collision.centFT0M()})); - histos.fill(HIST("EventQA/hMult"), collision.centFT0M()); - histos.fill(HIST("EventQA/hPvz"), collision.posZ()); } // ----------------------------- @@ -2391,20 +2454,40 @@ struct HResonanceCorrelation { histos.fill(HIST("hTrackEtaVsPtVsPhi"), track.pt(), track.eta(), track.phi()); } + // Same-event invariant-mass cross-check: raw per-candidate mass, read + // straight off the already-computed Mass column -- no kinematics + // recomputed here. See the matching comment at hPhiMixedEventInvMass's + // booking in init() for why this is the mixed-event counterpart. + for (auto const& cand : associatedPhis) { + histos.fill(HIST("hPhiInvMass"), cand.mass(), cand.pt(), collision.centFT0M()); + } + // ----------------------------- // h-phi correlation // ----------------------------- - fillCorrelationsPhi(triggerTracks, associatedPhis, false, false, collision.posZ(), collision.centFT0M(), bField); + // Guarded by doFullCorrelationStudy to match how sameEvent/Signal/Phi is + // booked in init() (~line 753) -- this call used to be unconditional, + // which would fill that THnF even when doFullCorrelationStudy=false (and + // it was therefore never booked). + if (masterConfigurations.doFullCorrelationStudy) { + fillCorrelationsPhi(triggerTracks, associatedPhis, false, false, collision.posZ(), collision.centFT0M(), bField); + } } void processSameEventHKstars(soa::Join::iterator const& collision, aod::AssocKstars const& associatedKstars, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { - LOGF(info, "SameEventKstar: collisions=%d triggers=%zu assocKstar=%zu", collision.globalIndex(), triggerTracks.size(), associatedKstars.size()); + // See the matching comment in processSameEventHHadrons: without this, + // the default config (doCorrelationKstar=false, this switch=true) fills + // sameEvent/Signal/Kstar0 etc. before they're booked -> fatal. + if (!TESTBIT(doCorrelation, IndexKstar)) { + return; + } + // LOGF(info, "SameEventKstar: collisions=%d triggers=%zu assocKstar=%zu", collision.globalIndex(), triggerTracks.size(), associatedKstars.size()); - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; // Skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerSelection.triggerBinToSelect)) { return; } @@ -2415,14 +2498,14 @@ struct HResonanceCorrelation { initEfficiencyFromCCDB(bc); } - if (!isisCollisionSelect(collision)) { + // See the matching comment in processSameEventHHadrons for + // fillHist/eventQAOwnerIndex. + if (!isSelectedEvents(collision, eventQAOwnerIndex == IndexKstar)) { return; } - if (doMixingQAandEventQA) { + if (eventQAOwnerIndex == IndexKstar && qaSwitches.doMixingQAandEventQA) { histos.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({collision.posZ(), collision.centFT0M()})); - histos.fill(HIST("EventQA/hMult"), collision.centFT0M()); - histos.fill(HIST("EventQA/hPvz"), collision.posZ()); } // ----------------------------- @@ -2448,16 +2531,31 @@ struct HResonanceCorrelation { histos.fill(HIST("hTrackEtaVsPtVsPhi"), track.pt(), track.eta(), track.phi()); } + // Same-event invariant-mass cross-check -- see the matching comment in + // processSameEventHPhis. + for (auto const& cand : associatedKstars) { + histos.fill(HIST("hKstarInvMass"), cand.mass(), cand.pt(), collision.centFT0M()); + } + // ----------------------------- // h-K*0 correlation // ----------------------------- - fillCorrelationsKstar(triggerTracks, associatedKstars, false, false, collision.posZ(), collision.centFT0M(), bField); + // Guarded by doFullCorrelationStudy for the same reason as the Phi call + // above -- matches how sameEvent/Signal/Kstar0 is booked in init(). + if (masterConfigurations.doFullCorrelationStudy) { + fillCorrelationsKstar(triggerTracks, associatedKstars, false, false, collision.posZ(), collision.centFT0M(), bField); + } } void processMixedEventHHadrons(soa::Join const& collisions, aod::AssocHadrons const& assocHadrons, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + // See the matching comment in processSameEventHHadrons -- mixedEvent/Signal/Hadron + // is booked only when TESTBIT(doCorrelation, IndexHadron) is set. + if (!TESTBIT(doCorrelation, IndexHadron)) { + return; + } + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { auto bc = collision1.bc_as(); auto bField = getMagneticField(bc.timestamp()); @@ -2467,20 +2565,20 @@ struct HResonanceCorrelation { } // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { + if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerSelection.triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerSelection.triggerBinToSelect))) { return; } // ________________________________________________ // Perform basic event selection on both collisions - if (!isisCollisionSelect(collision1) || !isisCollisionSelect(collision2)) { + if (!isSelectedEvents(collision1, false) || !isSelectedEvents(collision2, false)) { continue; } if (collision1.centFT0M() > axisRanges[5][1] || collision1.centFT0M() < axisRanges[5][0]) continue; if (collision2.centFT0M() > axisRanges[5][1] || collision2.centFT0M() < axisRanges[5][0]) continue; - if (doMixingQAandEventQA) { + if (qaSwitches.doMixingQAandEventQA) { if (collision1.globalIndex() == collision2.globalIndex()) { histos.fill(HIST("MixingQA/hMixingQA"), 0.0f); // same-collision pair counting } @@ -2501,7 +2599,12 @@ struct HResonanceCorrelation { void processMixedEventHPions(soa::Join const& collisions, soa::Join const& assocPions, soa::Join const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + // See the matching comment in processSameEventHHadrons -- mixedEvent/Signal/Pion + // is booked only when TESTBIT(doCorrelation, IndexPion) is set. + if (!TESTBIT(doCorrelation, IndexPion)) { + return; + } + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { auto bc = collision1.bc_as(); auto bField = getMagneticField(bc.timestamp()); @@ -2511,20 +2614,20 @@ struct HResonanceCorrelation { } // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { + if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerSelection.triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerSelection.triggerBinToSelect))) { continue; } // ________________________________________________ // Perform basic event selection on both collisions - if (!isisCollisionSelect(collision1) || !isisCollisionSelect(collision2)) { + if (!isSelectedEvents(collision1, false) || !isSelectedEvents(collision2, false)) { continue; } if (collision1.centFT0M() > axisRanges[5][1] || collision1.centFT0M() < axisRanges[5][0]) continue; if (collision2.centFT0M() > axisRanges[5][1] || collision2.centFT0M() < axisRanges[5][0]) continue; - if (doMixingQAandEventQA) { + if (qaSwitches.doMixingQAandEventQA) { if (collision1.globalIndex() == collision2.globalIndex()) { histos.fill(HIST("MixingQA/hMixingQA"), 0.0f); // same-collision pair counting } @@ -2545,7 +2648,12 @@ struct HResonanceCorrelation { void processMixedEventHKaons(soa::Join const& collisions, soa::Join const& assocKaons, soa::Join const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + // See the matching comment in processSameEventHHadrons -- mixedEvent/Signal/Kaon + // is booked only when TESTBIT(doCorrelation, IndexKaon) is set. + if (!TESTBIT(doCorrelation, IndexKaon)) { + return; + } + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { auto bc = collision1.bc_as(); auto bField = getMagneticField(bc.timestamp()); @@ -2555,20 +2663,20 @@ struct HResonanceCorrelation { } // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { + if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerSelection.triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerSelection.triggerBinToSelect))) { continue; } // ________________________________________________ // Perform basic event selection on both collisions - if (!isisCollisionSelect(collision1) || !isisCollisionSelect(collision2)) { + if (!isSelectedEvents(collision1, false) || !isSelectedEvents(collision2, false)) { continue; } if (collision1.centFT0M() > axisRanges[5][1] || collision1.centFT0M() < axisRanges[5][0]) continue; if (collision2.centFT0M() > axisRanges[5][1] || collision2.centFT0M() < axisRanges[5][0]) continue; - if (doMixingQAandEventQA) { + if (qaSwitches.doMixingQAandEventQA) { if (collision1.globalIndex() == collision2.globalIndex()) { histos.fill(HIST("MixingQA/hMixingQA"), 0.0f); // same-collision pair counting } @@ -2589,7 +2697,12 @@ struct HResonanceCorrelation { void processMixedEventHPhis(soa::Join const& collisions, aod::AssocPhis const& assocPhis, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + // See the matching comment in processSameEventHHadrons -- mixedEvent/Signal/Phi + // is booked only when TESTBIT(doCorrelation, IndexPhi) is set. + if (!TESTBIT(doCorrelation, IndexPhi)) { + return; + } + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { @@ -2600,11 +2713,11 @@ struct HResonanceCorrelation { initEfficiencyFromCCDB(bc); } - if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { + if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerSelection.triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerSelection.triggerBinToSelect))) { continue; } - if (!isisCollisionSelect(collision1) || !isisCollisionSelect(collision2)) { + if (!isSelectedEvents(collision1, false) || !isSelectedEvents(collision2, false)) { continue; } @@ -2616,7 +2729,7 @@ struct HResonanceCorrelation { continue; } - if (doMixingQAandEventQA) { + if (qaSwitches.doMixingQAandEventQA) { histos.fill(HIST("MixingQA/hMEpvz1"), collision1.posZ()); histos.fill(HIST("MixingQA/hMEpvz2"), collision2.posZ()); histos.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), collision1.centFT0M()})); @@ -2626,6 +2739,14 @@ struct HResonanceCorrelation { auto slicedAssocPhis = assocPhis.sliceBy(collisionSlicePhis, collision2.globalIndex()); + // Mixed-event invariant-mass cross-check: reuses the Mass/Rapidity + // columns already stored by the filter task (no kinematics + // recomputed here) and this same mixing pool, so it's directly + // comparable to the same-event mass QA and to the real ME background. + for (auto const& cand : slicedAssocPhis) { + histos.fill(HIST("hPhiMixedEventInvMass"), cand.mass(), cand.pt(), collision1.centFT0M()); + } + if (masterConfigurations.doFullCorrelationStudy) { // Use the per-collision slices computed above, not the full unsliced // triggerTracks/assocPhis tables -- passing the whole dataframe here @@ -2638,7 +2759,12 @@ struct HResonanceCorrelation { void processMixedEventHKstars(soa::Join const& collisions, aod::AssocKstars const& assocKstars, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + // See the matching comment in processSameEventHHadrons -- mixedEvent/Signal/Kstar0 + // is booked only when TESTBIT(doCorrelation, IndexKstar) is set. + if (!TESTBIT(doCorrelation, IndexKstar)) { + return; + } + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { @@ -2649,11 +2775,11 @@ struct HResonanceCorrelation { initEfficiencyFromCCDB(bc); } - if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { + if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerSelection.triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerSelection.triggerBinToSelect))) { continue; } - if (!isisCollisionSelect(collision1) || !isisCollisionSelect(collision2)) { + if (!isSelectedEvents(collision1, false) || !isSelectedEvents(collision2, false)) { continue; } @@ -2665,7 +2791,7 @@ struct HResonanceCorrelation { continue; } - if (doMixingQAandEventQA) { + if (qaSwitches.doMixingQAandEventQA) { histos.fill(HIST("MixingQA/hMEpvz1"), collision1.posZ()); histos.fill(HIST("MixingQA/hMEpvz2"), collision2.posZ()); histos.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), collision1.centFT0M()})); @@ -2675,6 +2801,12 @@ struct HResonanceCorrelation { auto slicedAssocKstars = assocKstars.sliceBy(collisionSliceKstars, collision2.globalIndex()); + // Mixed-event invariant-mass cross-check -- see the matching comment + // in processMixedEventHPhis. + for (auto const& cand : slicedAssocKstars) { + histos.fill(HIST("hKstarMixedEventInvMass"), cand.mass(), cand.pt(), collision1.centFT0M()); + } + if (masterConfigurations.doFullCorrelationStudy) { // Same reasoning as processMixedEventHPhis above -- use this collision // pair's own slices, not the full unsliced triggerTracks/assocKstars. @@ -2693,7 +2825,7 @@ struct HResonanceCorrelation { for (auto const& mcParticle : mcParticles) { - if (std::abs(mcParticle.eta()) > etaSel) + if (std::abs(mcParticle.eta()) > phaseSpaceSelections.etaSel) continue; float pt = mcParticle.pt(); @@ -2709,7 +2841,7 @@ struct HResonanceCorrelation { if (std::abs(mcParticle.pdgCode()) == Pdg::kPhi && masterConfigurations.doCorrelationPhi) { - // if (!doAssocPhysicalPrimaryInGen || + // if (!mcSelections.doAssocPhysicalPrimaryInGen || // mcParticle.isPhysicalPrimary()) { histos.fill(HIST("hGeneratedQAPtAssociatedPhi"), pt, 0.0f); @@ -2729,7 +2861,7 @@ struct HResonanceCorrelation { } } - if (doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) + if (mcSelections.doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) continue; // NOTE: intentionally restricted to Pion only (IndexPion..IndexPion); Kstar0 is // not extended to processMCGenerated/ClosureTest/Prediction in this version. @@ -2776,7 +2908,11 @@ struct HResonanceCorrelation { bestCollisionFT0Cpercentile = collision.centFT0C(); if (masterConfigurations.applyNewMCSelection) { - isCollisionSelect = (masterConfigurations.doPPAnalysis && isisCollisionSelect(collision)) || (!masterConfigurations.doPPAnalysis && isisCollisionSelectPbPb(collision, false)); + // isSelectedEvents() is the one selection used everywhere in this + // task now (pp and Pb-Pb alike), matching the filter task; see its + // own comment for why -- no more doPPAnalysis branch to a different + // cut set here. + isCollisionSelect = isSelectedEvents(collision, false); } else { bestCollisionSel8 = collision.sel8(); bestCollisionVtxZ = collision.posZ(); @@ -2790,13 +2926,13 @@ struct HResonanceCorrelation { if (collisions.size() > 1) { for (auto const& mcParticle : mcParticles) { - if (std::abs(mcParticle.y()) > ySel) + if (std::abs(mcParticle.y()) > phaseSpaceSelections.ySel) continue; if (std::abs(mcParticle.pdgCode()) == Pdg::kPhi) { histos.fill(HIST("GeneratedWithPV/hPhi") + HIST("_MidYVsMult_TwoPVsOrMore"), mcParticle.pt(), bestCollisionFT0Mpercentile); } - if (doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) + if (mcSelections.doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) continue; // NOTE: Pion only, see note above; Kstar0 not extended here. @@ -2813,7 +2949,7 @@ struct HResonanceCorrelation { // Collision selection //--------------------------------------------- - if (triggerPresenceMap.size() > 0 && !TESTBIT(bestCollisionTriggerPresenceMap, triggerBinToSelect)) + if (triggerPresenceMap.size() > 0 && !TESTBIT(bestCollisionTriggerPresenceMap, triggerSelection.triggerBinToSelect)) return; if (masterConfigurations.applyNewMCSelection) { @@ -2841,7 +2977,7 @@ struct HResonanceCorrelation { for (auto const& mcParticle : mcParticles) { - if (std::abs(mcParticle.eta()) > etaSel) + if (std::abs(mcParticle.eta()) > phaseSpaceSelections.etaSel) continue; float pt = mcParticle.pt(); @@ -2860,7 +2996,7 @@ struct HResonanceCorrelation { if (std::abs(mcParticle.pdgCode()) != Pdg::kPhi) continue; - // if (doAssocPhysicalPrimaryInGen && + // if (mcSelections.doAssocPhysicalPrimaryInGen && // !mcParticle.isPhysicalPrimary()) // continue; @@ -2883,11 +3019,11 @@ struct HResonanceCorrelation { } else { histos.fill(HIST("GeneratedWithPV/hPhi"), pt, mcParticle.eta(), bestCollisionFT0Mpercentile); } - if (std::abs(mcParticle.y()) < ySel) + if (std::abs(mcParticle.y()) < phaseSpaceSelections.ySel) histos.fill(HIST("GeneratedWithPV/hPhi") + HIST("_MidYVsMult"), pt, bestCollisionFT0Mpercentile); } - if (doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { + if (mcSelections.doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { continue; } if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kProton || std::abs(mcParticle.pdgCode()) == PDG_t::kElectron || std::abs(mcParticle.pdgCode()) == PDG_t::kMuonMinus) { @@ -2917,7 +3053,7 @@ struct HResonanceCorrelation { } else { histos.fill(HIST("GeneratedWithPV/h") + HIST(Particlenames[Index]), pt, eta, bestCollisionFT0Mpercentile); } - if (std::abs(mcParticle.y()) < ySel) + if (std::abs(mcParticle.y()) < phaseSpaceSelections.ySel) histos.fill(HIST("GeneratedWithPV/h") + HIST(Particlenames[Index]) + HIST("_MidYVsMult"), pt, bestCollisionFT0Mpercentile); } }); @@ -2937,7 +3073,7 @@ struct HResonanceCorrelation { for (auto const& mcParticle : mcParticles) { double geta = mcParticle.eta(); - if (std::abs(geta) > etaSel) { + if (std::abs(geta) > phaseSpaceSelections.etaSel) { continue; } double gpt = mcParticle.pt(); @@ -2947,7 +3083,7 @@ struct HResonanceCorrelation { } } - // if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + // if (!mcSelections.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { if (std::abs(mcParticle.pdgCode()) == Pdg::kPhi && masterConfigurations.doCorrelationPhi) { histos.fill(HIST("hClosureQAPtAssociatedPhi"), gpt, 0.0f); // step 1: no event selection whatsoever } @@ -2976,7 +3112,7 @@ struct HResonanceCorrelation { } // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(bestCollisionTriggerPresenceMap, triggerBinToSelect)) { + if (triggerPresenceMap.size() > 0 && !TESTBIT(bestCollisionTriggerPresenceMap, triggerSelection.triggerBinToSelect)) { return; } @@ -2996,7 +3132,7 @@ struct HResonanceCorrelation { for (auto const& mcParticle : mcParticles) { double geta = mcParticle.eta(); - if (std::abs(geta) > etaSel) { + if (std::abs(geta) > phaseSpaceSelections.etaSel) { continue; } double gpt = mcParticle.pt(); @@ -3006,7 +3142,7 @@ struct HResonanceCorrelation { } } - // if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + // if (!mcSelections.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { if (std::abs(mcParticle.pdgCode()) == Pdg::kPhi && masterConfigurations.doCorrelationPhi) { histos.fill(HIST("hClosureQAPtAssociatedPhi"), gpt, 1.0f); // step 2: after event selection } @@ -3019,7 +3155,7 @@ struct HResonanceCorrelation { double geta = mcParticle.eta(); double gpt = mcParticle.pt(); double gphi = mcParticle.phi(); - if (std::abs(geta) > etaSel) { + if (std::abs(geta) > phaseSpaceSelections.etaSel) { continue; } if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kProton || std::abs(mcParticle.pdgCode()) == PDG_t::kElectron || std::abs(mcParticle.pdgCode()) == PDG_t::kMuonMinus) { @@ -3028,13 +3164,13 @@ struct HResonanceCorrelation { histos.fill(HIST("ClosureTest/hTrigger"), gpt, geta, bestCollisionFT0Mpercentile); } if (masterConfigurations.doCorrelationHadron) { - if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (!mcSelections.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { assocHadronIndices.emplace_back(iteratorNum); histos.fill(HIST("ClosureTest/hHadron"), gpt, geta, gphi); } } } - if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (!mcSelections.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus && masterConfigurations.doCorrelationPion) { piIndices.emplace_back(iteratorNum); histos.fill(HIST("ClosureTest/hPion"), gpt, geta, gphi); @@ -3092,6 +3228,11 @@ struct HResonanceCorrelation { void processMixedEventHPhisInBuffer(soa::Join const& collisions, aod::TriggerTracks const& triggerTracks, aod::AssocPhis const& assocPhis, TracksComplete const&, aod::BCsWithTimestamps const&) { + // See the matching comment in processSameEventHHadrons -- mixedEvent/Signal/Phi + // is booked only when TESTBIT(doCorrelation, IndexPhi) is set. + if (!TESTBIT(doCorrelation, IndexPhi)) { + return; + } for (auto const& collision : collisions) { auto bc = collision.bc_as(); @@ -3104,12 +3245,21 @@ struct HResonanceCorrelation { auto slicedTriggerTracks = triggerTracks.sliceBy(collisionSliceTracks, collision.globalIndex()); auto slicedAssocPhis = assocPhis.sliceBy(collisionSlicePhis, collision.globalIndex()); - fillCorrelationsPhi(slicedTriggerTracks, slicedAssocPhis, true, true, collision.posZ(), collision.centFT0M(), dBz); + // Guarded by doFullCorrelationStudy -- see the matching comment in + // processSameEventHPhis; this call used to be unconditional. + if (masterConfigurations.doFullCorrelationStudy) { + fillCorrelationsPhi(slicedTriggerTracks, slicedAssocPhis, true, true, collision.posZ(), collision.centFT0M(), dBz); + } } } void processMixedEventHKstarsInBuffer(soa::Join const& collisions, aod::TriggerTracks const& triggerTracks, aod::AssocKstars const& assocKstars, TracksComplete const&, aod::BCsWithTimestamps const&) { + // See the matching comment in processSameEventHHadrons -- mixedEvent/Signal/Kstar0 + // is booked only when TESTBIT(doCorrelation, IndexKstar) is set. + if (!TESTBIT(doCorrelation, IndexKstar)) { + return; + } for (auto const& collision : collisions) { auto bc = collision.bc_as(); @@ -3118,7 +3268,10 @@ struct HResonanceCorrelation { auto slicedTriggerTracks = triggerTracks.sliceBy(collisionSliceTracks, collision.globalIndex()); auto slicedAssocKstars = assocKstars.sliceBy(collisionSliceKstars, collision.globalIndex()); - fillCorrelationsKstar(slicedTriggerTracks, slicedAssocKstars, true, true, collision.posZ(), collision.centFT0M(), dBz); + // Guarded by doFullCorrelationStudy -- see the matching comment above. + if (masterConfigurations.doFullCorrelationStudy) { + fillCorrelationsKstar(slicedTriggerTracks, slicedAssocKstars, true, true, collision.posZ(), collision.centFT0M(), dBz); + } } } @@ -3175,7 +3328,7 @@ struct HResonanceCorrelation { double geta = mcParticle.eta(); double gpt = mcParticle.pt(); double gphi = mcParticle.phi(); - if (std::abs(geta) > etaSel) { + if (std::abs(geta) > phaseSpaceSelections.etaSel) { continue; } if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kProton || std::abs(mcParticle.pdgCode()) == PDG_t::kElectron || std::abs(mcParticle.pdgCode()) == PDG_t::kMuonMinus) { @@ -3196,13 +3349,13 @@ struct HResonanceCorrelation { } } if (masterConfigurations.doCorrelationHadron) { - if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (!mcSelections.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { assocHadronIndices.emplace_back(iteratorNum); histos.fill(HIST("Prediction/hHadron"), gpt, geta, gphi); } } } - if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (!mcSelections.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus && masterConfigurations.doCorrelationPion) { piIndices.emplace_back(iteratorNum); histos.fill(HIST("Prediction/hPion"), gpt, geta, gphi); diff --git a/PWGLF/Tasks/Resonances/higherMassResonances.cxx b/PWGLF/Tasks/Resonances/higherMassResonances.cxx index 342a8dfa83d..30c22f10d14 100644 --- a/PWGLF/Tasks/Resonances/higherMassResonances.cxx +++ b/PWGLF/Tasks/Resonances/higherMassResonances.cxx @@ -1335,13 +1335,13 @@ struct HigherMassResonances { using BinningTypeFT0C = ColumnBinningPolicy; using BinningTypeFV0A = ColumnBinningPolicy; - BinningTypeFT0M binningOnFT0M{{axisVertex, axisMultiplicity}, true}; - BinningTypeFT0A binningOnFT0A{{axisVertex, axisMultiplicity}, true}; - BinningTypeFT0C binningOnFT0C{{axisVertex, axisMultiplicity}, true}; - BinningTypeFV0A binningOnFV0A{{axisVertex, axisMultiplicity}, true}; + BinningTypeFT0M binningOnFT0M{{axisVertex, axisMultiplicity}}; + BinningTypeFT0A binningOnFT0A{{axisVertex, axisMultiplicity}}; + BinningTypeFT0C binningOnFT0C{{axisVertex, axisMultiplicity}}; + BinningTypeFV0A binningOnFV0A{{axisVertex, axisMultiplicity}}; using BinningType = ColumnBinningPolicy; - BinningType colBinning{{axisVertex, axisMultiplicity}, true}; // for derived data only + BinningType colBinning{{axisVertex, axisMultiplicity}}; // for derived data only Preslice tracksPerCollisionV0Mixed = o2::aod::v0data::straCollisionId; // for derived data only void processME(EventCandidates const& collisions, TrackCandidates const& /*tracks*/, V0TrackCandidate const& v0s) diff --git a/PWGLF/Tasks/Resonances/highmasslambda.cxx b/PWGLF/Tasks/Resonances/highmasslambda.cxx index cf2df918881..dae9c6c0d82 100644 --- a/PWGLF/Tasks/Resonances/highmasslambda.cxx +++ b/PWGLF/Tasks/Resonances/highmasslambda.cxx @@ -714,7 +714,7 @@ struct highmasslambda { void processMixedEventOpti(EventCandidates const& collisions, TrackCandidates const& tracks, AllTrackCandidates const&, ResoV0s const& V0s) { auto tracksV0sTuple = std::make_tuple(tracks, V0s); - BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}, true}; + BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}}; Pair pairs{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksV0sTuple, &cache}; // -1 is the number of the bin to skip for (auto& [collision1, tracks1, collision2, tracks2] : pairs) { if (!collision1.sel8() || !collision1.triggereventep() || !collision1.selection_bit(aod::evsel::kNoTimeFrameBorder) || !collision1.selection_bit(aod::evsel::kNoITSROFrameBorder)) { diff --git a/PWGLF/Tasks/Resonances/highmasslambdasvx.cxx b/PWGLF/Tasks/Resonances/highmasslambdasvx.cxx index c3ece46c3c8..33d5b697660 100644 --- a/PWGLF/Tasks/Resonances/highmasslambdasvx.cxx +++ b/PWGLF/Tasks/Resonances/highmasslambdasvx.cxx @@ -808,7 +808,7 @@ struct highmasslambdasvx { /* void processMixedEventOpti(EventCandidates const& collisions, TrackCandidates const& tracks, AllTrackCandidates const&, ResoV0s const& V0s) { auto tracksV0sTuple = std::make_tuple(tracks, V0s); - BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}, true}; + BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}}; Pair pairs{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksV0sTuple, &cache}; // -1 is the number of the bin to skip for (auto& [collision1, tracks1, collision2, tracks2] : pairs) { if (!collision1.sel8() || !collision2.sel8()) { diff --git a/PWGLF/Tasks/Resonances/k0sK0sFlow.cxx b/PWGLF/Tasks/Resonances/k0sK0sFlow.cxx new file mode 100644 index 00000000000..583ef56a6d4 --- /dev/null +++ b/PWGLF/Tasks/Resonances/k0sK0sFlow.cxx @@ -0,0 +1,926 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file k0sK0sFlow.cxx +/// \brief K0S and K0S-K0S spectra, angular distributions, SP and EP observables +/// \author Satoshi Yano + +#include "PWGLF/DataModel/LFStrangenessTables.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/RecoDecay.h" +#include "Common/Core/trackUtilities.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/Qvectors.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include // IWYU pragma: keep (do not replace with Math/Vector3Dfwd.h) +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; + +struct K0sK0sFlow { + enum class PionPidMode : int { + TpcWithTofIfAvailable = 0, + TpcOnly = 1, + TpcAndTof = 2, + TofRequiredAboveMomentum = 4 + }; + + enum class EventSelectionStep : int { + All = 1, + Vertex, + Sel8, + NoSameBunchPileup, + NoTimeFrameBorder, + NoItsRofBorder, + Centrality, + NSteps + }; + + enum class TrackSelectionStep : int { + All = 1, + Kinematics, + TpcClusters, + TrackQuality, + TpcPid, + TofPid, + NSteps + }; + + enum class K0sSelectionStep : int { + All = 1, + Daughters, + Kinematics, + Topology, + DaughterDca, + DecayGeometry, + Armenteros, + LambdaRejection, + NSteps + }; + + enum class PairSelectionStep : int { + All = 1, + NoSharedDaughters, + SelectedK0s, + ClosePair, + Kinematics, + SignalMass, + NSteps + }; + + enum class LikeSignCategory : int { + PositivePositive, + NegativeNegative, + PositiveNegative + }; + + enum class SidebandRegion : int { + Lower = -1, + Outside = 0, + Upper = 1 + }; + + static constexpr double VectorTolerance = 1.e-12; + static constexpr int QVectorComponents = 4; + + template + static constexpr auto enumValue(E value) + { + return static_cast>(value); + } + + HistogramRegistry registry{"registry"}; + SliceCache cache; + o2::vertexing::DCAFitterN<2> lsFitter; + + struct : ConfigurableGroup { + Configurable maxAbsZvtx{"maxAbsZvtx", 10.f, "maximum |z vertex| (cm)"}; + Configurable requireSel8{"requireSel8", true, "require sel8"}; + Configurable requireNoSameBunchPileup{"requireNoSameBunchPileup", true, "reject same-bunch pileup"}; + Configurable requireNoTimeFrameBorder{"requireNoTimeFrameBorder", true, "reject TF borders"}; + Configurable requireNoITSROFrameBorder{"requireNoITSROFrameBorder", true, "reject ITS ROF borders"}; + Configurable minCentrality{"minCentrality", 0.f, "minimum FT0C centrality"}; + Configurable maxCentrality{"maxCentrality", 100.f, "maximum FT0C centrality"}; + } eventCuts; + + struct : ConfigurableGroup { + Configurable daughterMinPt{"daughterMinPt", 0.1f, "daughter minimum pT"}; + Configurable maxAbsEta{"maxAbsEta", 0.8f, "daughter maximum |eta|"}; + Configurable minTPCRows{"minTPCRows", 70, "minimum TPC crossed rows"}; + Configurable minTPCRowsOverFindable{"minTPCRowsOverFindable", 0.8f, "minimum crossed/findable ratio"}; + Configurable maxTPCChi2NCl{"maxTPCChi2NCl", 4.f, "maximum TPC chi2/cluster"}; + Configurable maxITSChi2NCl{"maxITSChi2NCl", 36.f, "maximum ITS chi2/cluster"}; + Configurable minITSClusters{"minITSClusters", 0, "minimum ITS clusters; zero disables"}; + Configurable pidMode{"pidMode", static_cast(PionPidMode::TpcWithTofIfAvailable), "0 TPC and TOF if present; 1 TPC only; 2 require TOF; 4 require TOF above momentum threshold"}; + Configurable maxTPCNSigmaPi{"maxTPCNSigmaPi", 5.f, "maximum pion TPC |n sigma|"}; + Configurable maxTOFNSigmaPi{"maxTOFNSigmaPi", 3.f, "maximum pion TOF |n sigma|"}; + Configurable tofRequiredAboveP{"tofRequiredAboveP", 0.6f, "momentum threshold for PID mode 4"}; + } daughterCuts; + + struct : ConfigurableGroup { + Configurable k0sMinPt{"k0sMinPt", 0.f, "K0S minimum pT"}; + Configurable k0sMaxPt{"k0sMaxPt", 20.f, "K0S maximum pT"}; + Configurable k0sMaxAbsY{"k0sMaxAbsY", 0.5f, "K0S maximum |y|"}; + Configurable minCosPA{"minCosPA", 0.98f, "minimum cos(PA)"}; + Configurable maxDCADaughters{"maxDCADaughters", 0.3f, "maximum daughter DCA (cm)"}; + Configurable minDCAPosToPV{"minDCAPosToPV", 0.05f, "minimum positive daughter |DCA to PV| (cm)"}; + Configurable minDCANegToPV{"minDCANegToPV", 0.05f, "minimum negative daughter |DCA to PV| (cm)"}; + Configurable minRadius{"minRadius", 0.5f, "minimum decay radius (cm)"}; + Configurable maxRadius{"maxRadius", 200.f, "maximum decay radius (cm)"}; + Configurable maxCtau{"maxCtau", 20.f, "maximum c tau (cm)"}; + Configurable useArmenterosCut{"useArmenterosCut", true, "apply K0S Armenteros cut"}; + Configurable armenterosSlope{"armenterosSlope", 0.2f, "require qT > slope |alpha|"}; + Configurable rejectLambda{"rejectLambda", true, "reject Lambda and anti-Lambda"}; + Configurable lambdaRejectionWindow{"lambdaRejectionWindow", 0.01f, "Lambda rejection half-width"}; + Configurable k0sMassMin{"k0sMassMin", 0.47f, "K0S pair-building mass minimum"}; + Configurable k0sMassMax{"k0sMassMax", 0.525f, "K0S pair-building mass maximum"}; + Configurable k0sLowerSidebandMin{"k0sLowerSidebandMin", 0.42f, "K0S lower sideband minimum"}; + Configurable k0sLowerSidebandMax{"k0sLowerSidebandMax", 0.46f, "K0S lower sideband maximum"}; + Configurable k0sUpperSidebandMin{"k0sUpperSidebandMin", 0.535f, "K0S upper sideband minimum"}; + Configurable k0sUpperSidebandMax{"k0sUpperSidebandMax", 0.575f, "K0S upper sideband maximum"}; + } k0Cuts; + + struct : ConfigurableGroup { + Configurable pairMinPt{"pairMinPt", 0.f, "pair minimum pT"}; + Configurable pairMaxPt{"pairMaxPt", 20.f, "pair maximum pT"}; + Configurable pairMaxAbsY{"pairMaxAbsY", 0.5f, "pair maximum |y|"}; + Configurable pairMassMin{"pairMassMin", 0.99f, "pair mass minimum"}; + Configurable pairMassMax{"pairMassMax", 3.f, "pair mass maximum"}; + Configurable enableClosePairRejection{"enableClosePairRejection", false, "enable minimum DeltaR cut"}; + Configurable minDeltaR{"minDeltaR", 0.0f, "minimum K0S-K0S DeltaR"}; + Configurable mixingDepth{"mixingDepth", 5, "number of mixed events"}; + } pairCuts; + + struct : ConfigurableGroup { + Configurable enableLikeSign{"enableLikeSign", true, "build like-sign V0 and V0-V0 background candidates"}; + Configurable likeSignBz{"likeSignBz", 5.f, "magnetic field Bz used by the like-sign DCA fitter (kG)"}; + Configurable likeSignFitterMaxR{"likeSignFitterMaxR", 200.f, "maximum radius used by the like-sign DCA fitter (cm)"}; + Configurable likeSignFitterMaxChi2{"likeSignFitterMaxChi2", 1.e9f, "maximum fitter chi2"}; + } likeSign; + + struct : ConfigurableGroup { + Configurable harmonic{"harmonic", 2, "flow harmonic"}; + Configurable correctionStep{"correctionStep", 3, "Q-vector correction step (0-3)"}; + Configurable numberOfDetectors{"numberOfDetectors", 7, "detectors stored in Qvectors"}; + Configurable ft0cId{"ft0cId", 0, "FT0C detector index"}; + Configurable ft0aId{"ft0aId", 1, "FT0A detector index"}; + Configurable tpcPosId{"tpcPosId", 4, "TPC positive-eta detector index"}; + Configurable minQAmplitude{"minQAmplitude", 1.e-4f, "minimum Q-vector amplitude"}; + } flow; + + ConfigurableAxis axisCent{"axisCent", {VARIABLE_WIDTH, 0., 5., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100.}, "centrality"}; + ConfigurableAxis axisK0Pt{"axisK0Pt", {VARIABLE_WIDTH, 0., 0.5, 1., 1.5, 2., 2.5, 3., 4., 6., 8., 10., 13., 20.}, "K0S pT"}; + ConfigurableAxis axisPairPt{"axisPairPt", {VARIABLE_WIDTH, 0., 0.5, 1., 1.5, 2., 2.5, 3., 4., 6., 8., 10., 13., 20.}, "pair pT"}; + ConfigurableAxis axisK0Mass{"axisK0Mass", {200, 0.4, 0.6}, "pi-pi mass"}; + ConfigurableAxis axisPairMass{"axisPairMass", {402, 0.99, 3.0}, "K0S-K0S mass"}; + ConfigurableAxis axisAbsCosTheta{"axisAbsCosTheta", {10, 0., 1.}, "|cos(theta*)|"}; + ConfigurableAxis axisPhiStar{"axisPhiStar", {12, -3.141592653589793, 3.141592653589793}, "phi*"}; + ConfigurableAxis axisMixZvtx{"axisMixZvtx", {VARIABLE_WIDTH, -10., -5., 0., 5., 10.}, "event-mixing vertex-z bins"}; + ConfigurableAxis axisMixCent{"axisMixCent", {VARIABLE_WIDTH, 0., 5., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100.}, "event-mixing centrality bins"}; + + using Collisions = soa::Join; + using DaughterTracks = soa::Join; + Preslice perCollisionV0 = aod::v0data::collisionId; + Preslice perCollisionTrack = aod::track::collisionId; + using MixingBinning = ColumnBinningPolicy; + + void init(InitContext const&) + { + lsFitter.setPropagateToPCA(true); + lsFitter.setMaxR(likeSign.likeSignFitterMaxR); + lsFitter.setMinParamChange(1.e-3f); + lsFitter.setMinRelChi2Change(0.9f); + lsFitter.setMaxDZIni(1.e9f); + lsFitter.setMaxChi2(likeSign.likeSignFitterMaxChi2); + lsFitter.setUseAbsDCA(true); + lsFitter.setBz(likeSign.likeSignBz); + AxisSpec cent{axisCent, "FT0C centrality (%)"}, k0pt{axisK0Pt, "p_{T}(K^{0}_{S})"}, pairpt{axisPairPt, "p_{T}(K^{0}_{S}K^{0}_{S})"}; + AxisSpec k0mass{axisK0Mass, "m_{#pi#pi}"}, pairmass{axisPairMass, "m_{K^{0}_{S}K^{0}_{S}}"}; + AxisSpec absct{axisAbsCosTheta, "|cos#theta*|"}, phistar{axisPhiStar, "#phi*"}; + constexpr int NEventSelectionSteps = enumValue(EventSelectionStep::NSteps) - 1; + constexpr int NTrackSelectionSteps = enumValue(TrackSelectionStep::NSteps) - 1; + constexpr int NK0sSelectionSteps = enumValue(K0sSelectionStep::NSteps) - 1; + constexpr int NPairSelectionSteps = enumValue(PairSelectionStep::NSteps) - 1; + registry.add("Event/hEventCutFlow", "event cut flow", HistType::kTH1F, {{NEventSelectionSteps, 0.5, NEventSelectionSteps + 0.5}}); + registry.add("Event/hVertexZBefore", "vertex z before", HistType::kTH1F, {{120, -30., 30.}}); + registry.add("Event/hVertexZAfter", "vertex z after", HistType::kTH1F, {{120, -30., 30.}}); + registry.add("Event/hCentralityBefore", "centrality before", HistType::kTH1F, {{100, 0., 100.}}); + registry.add("Event/hCentralityAfter", "centrality after", HistType::kTH1F, {{100, 0., 100.}}); + registry.add("QA/Tracks/hCutFlow", "daughter cut flow", HistType::kTH1F, {{NTrackSelectionSteps, 0.5, NTrackSelectionSteps + 0.5}}); + registry.add("QA/Tracks/BeforeSelection/hPt", "daughter pT before", HistType::kTH1F, {{200, 0., 20.}}); + registry.add("QA/Tracks/BeforeSelection/hEta", "daughter eta before", HistType::kTH1F, {{100, -1., 1.}}); + registry.add("QA/Tracks/BeforeSelection/hDCAxyVsPt", "DCAxy vs pT before", HistType::kTH2F, {{200, 0., 20.}, {200, -5., 5.}}); + registry.add("QA/Tracks/BeforeSelection/hDCAzVsPt", "DCAz vs pT before", HistType::kTH2F, {{200, 0., 20.}, {200, -5., 5.}}); + registry.add("QA/Tracks/BeforeSelection/hTPCCrossedRowsVsPt", "TPC rows vs pT before", HistType::kTH2F, {{200, 0., 20.}, {170, 0., 170.}}); + registry.add("QA/Tracks/BeforeSelection/hTPCSignalVsP", "TPC signal before", HistType::kTH2F, {{200, 0., 10.}, {300, 0., 300.}}); + registry.add("QA/Tracks/BeforeSelection/hTPCNSigmaPiVsP", "TPC pion PID before", HistType::kTH2F, {{200, 0., 10.}, {120, -6., 6.}}); + registry.add("QA/Tracks/BeforeSelection/hTOFBetaVsP", "TOF beta before", HistType::kTH2F, {{200, 0., 10.}, {140, 0., 1.4}}); + registry.add("QA/Tracks/BeforeSelection/hTOFNSigmaPiVsP", "TOF pion PID before", HistType::kTH2F, {{200, 0., 10.}, {120, -6., 6.}}); + registry.add("QA/Tracks/AfterSelection/hPt", "daughter pT", HistType::kTH1F, {{200, 0., 20.}}); + registry.add("QA/Tracks/AfterSelection/hEta", "daughter eta", HistType::kTH1F, {{100, -1., 1.}}); + registry.add("QA/Tracks/AfterSelection/hDCAxyVsPt", "DCAxy vs pT", HistType::kTH2F, {{200, 0., 20.}, {200, -5., 5.}}); + registry.add("QA/Tracks/AfterSelection/hDCAzVsPt", "DCAz vs pT", HistType::kTH2F, {{200, 0., 20.}, {200, -5., 5.}}); + registry.add("QA/Tracks/AfterSelection/hTPCCrossedRowsVsPt", "TPC rows vs pT", HistType::kTH2F, {{200, 0., 20.}, {170, 0., 170.}}); + registry.add("QA/Tracks/AfterSelection/hTPCSignalVsP", "TPC signal", HistType::kTH2F, {{200, 0., 10.}, {300, 0., 300.}}); + registry.add("QA/Tracks/AfterSelection/hTPCNSigmaPiVsP", "TPC pion PID", HistType::kTH2F, {{200, 0., 10.}, {120, -6., 6.}}); + registry.add("QA/Tracks/AfterSelection/hTOFBetaVsP", "TOF beta", HistType::kTH2F, {{200, 0., 10.}, {140, 0., 1.4}}); + registry.add("QA/Tracks/AfterSelection/hTOFNSigmaPiVsP", "TOF pion PID", HistType::kTH2F, {{200, 0., 10.}, {120, -6., 6.}}); + registry.add("QA/K0s/hCutFlow", "K0S cut flow", HistType::kTH1F, {{NK0sSelectionSteps, 0.5, NK0sSelectionSteps + 0.5}}); + registry.add("QA/K0s/BeforeSelection/hMassVsPt", "K0S before", HistType::kTH2F, {k0mass, k0pt}); + registry.add("QA/K0s/BeforeSelection/hArmenteros", "Armenteros before", HistType::kTH2F, {{100, -1., 1.}, {100, 0., 0.3}}); + registry.add("QA/K0s/BeforeSelection/hCosPA", "cosPA before", HistType::kTH1F, {{200, 0.9, 1.}}); + registry.add("QA/K0s/BeforeSelection/hDCADaughters", "DCA daughters before", HistType::kTH1F, {{200, 0., 2.}}); + registry.add("QA/K0s/BeforeSelection/hCtau", "c tau before", HistType::kTH1F, {{200, 0., 50.}}); + registry.add("QA/K0s/AfterSelection/hMassVsPt", "K0S after (no mass window)", HistType::kTH2F, {k0mass, k0pt}); + registry.add("QA/K0s/AfterSelection/hArmenteros", "Armenteros", HistType::kTH2F, {{100, -1., 1.}, {100, 0., 0.3}}); + registry.add("QA/K0s/AfterSelection/hCosPA", "cosPA", HistType::kTH1F, {{200, 0.9, 1.}}); + registry.add("QA/K0s/AfterSelection/hDCADaughters", "DCA daughters", HistType::kTH1F, {{200, 0., 2.}}); + registry.add("QA/K0s/AfterSelection/hCtau", "c tau", HistType::kTH1F, {{200, 0., 50.}}); + registry.add("QA/K0sK0s/hPairCutFlow", "pair cut flow", HistType::kTH1F, {{NPairSelectionSteps, 0.5, NPairSelectionSteps + 0.5}}); + registry.add("QA/K0sK0s/hPairCutFlowVsCent", "pair cut flow vs centrality", HistType::kTH2F, {{NPairSelectionSteps, 0.5, NPairSelectionSteps + 0.5}, cent}); + registry.add("QA/K0sK0s/SameEvent/hMass", "same-event pair mass", HistType::kTH1F, {pairmass}); + registry.add("QA/K0sK0s/SameEvent/hDeltaR", "same-event DeltaR", HistType::kTH1F, {{200, 0., 5.}}); + registry.add("QA/K0sK0s/SameEvent/hPt1VsPt2", "K0S pT correlation", HistType::kTH2F, {k0pt, k0pt}); + registry.add("QA/K0sK0s/SameEvent/hK0Mass1VsMass2", "K0S mass correlation", HistType::kTH2F, {k0mass, k0mass}); + registry.add("QA/K0sK0s/MixedEvent/hMass", "mixed-event pair mass", HistType::kTH1F, {pairmass}); + registry.add("QA/Flow/QVectors/hQxQyFT0C", "corrected FT0C q2", HistType::kTH2F, {{200, -1., 1.}, {200, -1., 1.}}); + registry.add("QA/Flow/QVectors/hQxQyFT0A", "corrected FT0A q2", HistType::kTH2F, {{200, -1., 1.}, {200, -1., 1.}}); + registry.add("QA/Flow/QVectors/hQxQyTPCpos", "corrected TPCpos q2", HistType::kTH2F, {{200, -1., 1.}, {200, -1., 1.}}); + registry.add("QA/Flow/EventPlane/hPsi2VsCentFT0C", "FT0C event plane", HistType::kTH2F, {cent, {120, -1.5708, 1.5708}}); + registry.add("QA/Flow/Calibration/hCalibrationFlag", "Q-vector calibration flag", HistType::kTH1F, {{5, -0.5, 4.5}}); + registry.add("K0s/hCounts", "K0S counts", HistType::kTHnSparseF, {k0mass, k0pt, cent}); + registry.add("K0s/hCos2Weighted", "K0S EP numerator", HistType::kTHnSparseF, {k0mass, k0pt, cent}, true); + registry.add("K0s/hSPWeightedFT0C", "K0S SP numerator FT0C", HistType::kTHnSparseF, {k0mass, k0pt, cent}, true); + registry.add("K0s/hSPWeightedFT0A", "K0S SP numerator FT0A", HistType::kTHnSparseF, {k0mass, k0pt, cent}, true); + registry.add("K0s/LikeSign/PP/hCounts", "like-sign pi+pi+ candidates", HistType::kTHnSparseF, {k0mass, k0pt, cent}); + registry.add("K0s/LikeSign/MM/hCounts", "like-sign pi-pi- candidates", HistType::kTHnSparseF, {k0mass, k0pt, cent}); + for (auto const& base : {std::string("K0sK0s/SameEvent/"), std::string("K0sK0s/MixedEvent/"), std::string("K0sK0s/Sideband/Combined/"), std::string("K0sK0s/Sideband/LowerLower/"), std::string("K0sK0s/Sideband/LowerUpper/"), std::string("K0sK0s/Sideband/UpperUpper/")}) { + registry.add((base + "hCounts").c_str(), "K0S-K0S counts", HistType::kTHnSparseF, {pairmass, pairpt, cent, absct, phistar}); + registry.add((base + "hP2Weighted").c_str(), "pair angular moment P2(cos theta*)", HistType::kTHnSparseF, {pairmass, pairpt, cent, absct, phistar}, true); + registry.add((base + "hP4Weighted").c_str(), "pair angular moment P4(cos theta*)", HistType::kTHnSparseF, {pairmass, pairpt, cent, absct, phistar}, true); + } + registry.add("K0sK0s/SameEvent/hCos2Weighted", "pair EP numerator", HistType::kTHnSparseF, {pairmass, pairpt, cent, absct, phistar}, true); + registry.add("K0sK0s/SameEvent/hSPWeightedFT0C", "pair SP numerator FT0C", HistType::kTHnSparseF, {pairmass, pairpt, cent, absct, phistar}, true); + registry.add("K0sK0s/SameEvent/hSPWeightedFT0A", "pair SP numerator FT0A", HistType::kTHnSparseF, {pairmass, pairpt, cent, absct, phistar}, true); + for (auto const& base : {std::string("K0sK0s/LikeSign/PPPP/"), std::string("K0sK0s/LikeSign/MMMM/"), std::string("K0sK0s/LikeSign/PPMM/"), std::string("K0sK0s/LikeSign/Combined/")}) { + registry.add((base + "hCounts").c_str(), "like-sign K0S-K0S mimic counts", HistType::kTHnSparseF, {pairmass, pairpt, cent, absct, phistar}); + registry.add((base + "hP2Weighted").c_str(), "like-sign pair angular moment P2", HistType::kTHnSparseF, {pairmass, pairpt, cent, absct, phistar}, true); + registry.add((base + "hP4Weighted").c_str(), "like-sign pair angular moment P4", HistType::kTHnSparseF, {pairmass, pairpt, cent, absct, phistar}, true); + } + registry.add("Resolution/SP/pFT0CFT0AVsCent", "FT0C-FT0A SP correlation", HistType::kTProfile, {cent}); + registry.add("Resolution/SP/pFT0CTPCposVsCent", "FT0C-TPCpos SP correlation", HistType::kTProfile, {cent}); + registry.add("Resolution/SP/pFT0ATPCposVsCent", "FT0A-TPCpos SP correlation", HistType::kTProfile, {cent}); + registry.add("Resolution/EP/pCos2PsiFT0CFT0AVsCent", "FT0C-FT0A EP correlation", HistType::kTProfile, {cent}); + registry.add("Resolution/EP/pCos2PsiFT0CTPCposVsCent", "FT0C-TPCpos EP correlation", HistType::kTProfile, {cent}); + registry.add("Resolution/EP/pCos2PsiFT0ATPCposVsCent", "FT0A-TPCpos EP correlation", HistType::kTProfile, {cent}); + + constexpr std::array EventSelectionLabels{"all", "vertex", "sel8", "no same-bunch pileup", "no TF border", "no ITS ROF border", "centrality"}; + constexpr std::array TrackSelectionLabels{"all", "kinematics", "TPC clusters", "track quality", "TPC PID", "TOF PID"}; + constexpr std::array K0sSelectionLabels{"all", "daughters", "kinematics", "topology", "daughter DCA", "decay geometry", "Armenteros", "Lambda rejection"}; + constexpr std::array PairSelectionLabels{"all", "no shared daughters", "selected K0S", "close pair", "kinematics", "signal mass"}; + auto eventCutFlow = registry.get(HIST("Event/hEventCutFlow")); + auto trackCutFlow = registry.get(HIST("QA/Tracks/hCutFlow")); + auto k0sCutFlow = registry.get(HIST("QA/K0s/hCutFlow")); + auto pairCutFlow = registry.get(HIST("QA/K0sK0s/hPairCutFlow")); + auto pairCutFlowVsCent = registry.get(HIST("QA/K0sK0s/hPairCutFlowVsCent")); + for (size_t index = 0; index < EventSelectionLabels.size(); ++index) { + eventCutFlow->GetXaxis()->SetBinLabel(index + 1, EventSelectionLabels[index]); + } + for (size_t index = 0; index < TrackSelectionLabels.size(); ++index) { + trackCutFlow->GetXaxis()->SetBinLabel(index + 1, TrackSelectionLabels[index]); + } + for (size_t index = 0; index < K0sSelectionLabels.size(); ++index) { + k0sCutFlow->GetXaxis()->SetBinLabel(index + 1, K0sSelectionLabels[index]); + } + for (size_t index = 0; index < PairSelectionLabels.size(); ++index) { + pairCutFlow->GetXaxis()->SetBinLabel(index + 1, PairSelectionLabels[index]); + pairCutFlowVsCent->GetXaxis()->SetBinLabel(index + 1, PairSelectionLabels[index]); + } + } + + SidebandRegion sidebandRegion(float mass) const + { + if (mass >= k0Cuts.k0sLowerSidebandMin.value && mass < k0Cuts.k0sLowerSidebandMax.value) { + return SidebandRegion::Lower; + } + if (mass >= k0Cuts.k0sUpperSidebandMin.value && mass < k0Cuts.k0sUpperSidebandMax.value) { + return SidebandRegion::Upper; + } + return SidebandRegion::Outside; + } + + void fillSidebandPair(ROOT::Math::PxPyPzMVector const& d1, ROOT::Math::PxPyPzMVector const& m, float cent, SidebandRegion side1, SidebandRegion side2) + { + auto ang = decayAngles(d1, m); + const double cosTheta2 = ang[0] * ang[0]; + const double p2Weight = 0.5 * (3. * cosTheta2 - 1.); + const double p4Weight = 0.125 * (35. * cosTheta2 * cosTheta2 - 30. * cosTheta2 + 3.); + registry.fill(HIST("K0sK0s/Sideband/Combined/hCounts"), m.M(), m.Pt(), cent, ang[0], ang[1]); + registry.fill(HIST("K0sK0s/Sideband/Combined/hP2Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], p2Weight); + registry.fill(HIST("K0sK0s/Sideband/Combined/hP4Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], p4Weight); + if (side1 == SidebandRegion::Lower && side2 == SidebandRegion::Lower) { + registry.fill(HIST("K0sK0s/Sideband/LowerLower/hCounts"), m.M(), m.Pt(), cent, ang[0], ang[1]); + registry.fill(HIST("K0sK0s/Sideband/LowerLower/hP2Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], p2Weight); + registry.fill(HIST("K0sK0s/Sideband/LowerLower/hP4Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], p4Weight); + } else if (side1 == SidebandRegion::Upper && side2 == SidebandRegion::Upper) { + registry.fill(HIST("K0sK0s/Sideband/UpperUpper/hCounts"), m.M(), m.Pt(), cent, ang[0], ang[1]); + registry.fill(HIST("K0sK0s/Sideband/UpperUpper/hP2Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], p2Weight); + registry.fill(HIST("K0sK0s/Sideband/UpperUpper/hP4Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], p4Weight); + } else { + registry.fill(HIST("K0sK0s/Sideband/LowerUpper/hCounts"), m.M(), m.Pt(), cent, ang[0], ang[1]); + registry.fill(HIST("K0sK0s/Sideband/LowerUpper/hP2Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], p2Weight); + registry.fill(HIST("K0sK0s/Sideband/LowerUpper/hP4Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], p4Weight); + } + } + + template + bool selectEvent(C const& c, bool fill) + { + float cent = c.centFT0C(); + if (fill) { + registry.fill(HIST("Event/hVertexZBefore"), c.posZ()); + registry.fill(HIST("Event/hCentralityBefore"), cent); + registry.fill(HIST("Event/hEventCutFlow"), enumValue(EventSelectionStep::All)); + } + if (std::abs(c.posZ()) > eventCuts.maxAbsZvtx) { + return false; + } + if (fill) { + registry.fill(HIST("Event/hEventCutFlow"), enumValue(EventSelectionStep::Vertex)); + } + if (eventCuts.requireSel8 && !c.sel8()) { + return false; + } + if (fill) { + registry.fill(HIST("Event/hEventCutFlow"), enumValue(EventSelectionStep::Sel8)); + } + if (eventCuts.requireNoSameBunchPileup && !c.selection_bit(aod::evsel::kNoSameBunchPileup)) { + return false; + } + if (fill) { + registry.fill(HIST("Event/hEventCutFlow"), enumValue(EventSelectionStep::NoSameBunchPileup)); + } + if (eventCuts.requireNoTimeFrameBorder && !c.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + if (fill) { + registry.fill(HIST("Event/hEventCutFlow"), enumValue(EventSelectionStep::NoTimeFrameBorder)); + } + if (eventCuts.requireNoITSROFrameBorder && !c.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + if (fill) { + registry.fill(HIST("Event/hEventCutFlow"), enumValue(EventSelectionStep::NoItsRofBorder)); + } + if (cent < eventCuts.minCentrality || cent >= eventCuts.maxCentrality) { + return false; + } + if (fill) { + registry.fill(HIST("Event/hEventCutFlow"), enumValue(EventSelectionStep::Centrality)); + registry.fill(HIST("Event/hVertexZAfter"), c.posZ()); + registry.fill(HIST("Event/hCentralityAfter"), cent); + } + return true; + } + + template + bool selectDaughter(T const& t, bool qa) + { + bool hasTOF = t.hasTOF(); + if (qa) { + registry.fill(HIST("QA/Tracks/hCutFlow"), enumValue(TrackSelectionStep::All)); + registry.fill(HIST("QA/Tracks/BeforeSelection/hPt"), t.pt()); + registry.fill(HIST("QA/Tracks/BeforeSelection/hEta"), t.eta()); + registry.fill(HIST("QA/Tracks/BeforeSelection/hDCAxyVsPt"), t.pt(), t.dcaXY()); + registry.fill(HIST("QA/Tracks/BeforeSelection/hDCAzVsPt"), t.pt(), t.dcaZ()); + registry.fill(HIST("QA/Tracks/BeforeSelection/hTPCCrossedRowsVsPt"), t.pt(), t.tpcNClsCrossedRows()); + registry.fill(HIST("QA/Tracks/BeforeSelection/hTPCSignalVsP"), t.p(), t.tpcSignal()); + registry.fill(HIST("QA/Tracks/BeforeSelection/hTPCNSigmaPiVsP"), t.p(), t.tpcNSigmaPi()); + if (hasTOF) { + registry.fill(HIST("QA/Tracks/BeforeSelection/hTOFBetaVsP"), t.p(), t.beta()); + registry.fill(HIST("QA/Tracks/BeforeSelection/hTOFNSigmaPiVsP"), t.p(), t.tofNSigmaPi()); + } + } + if (t.pt() < daughterCuts.daughterMinPt || std::abs(t.eta()) > daughterCuts.maxAbsEta) { + return false; + } + if (qa) { + registry.fill(HIST("QA/Tracks/hCutFlow"), enumValue(TrackSelectionStep::Kinematics)); + } + if (t.tpcNClsCrossedRows() < daughterCuts.minTPCRows || t.tpcCrossedRowsOverFindableCls() < daughterCuts.minTPCRowsOverFindable) { + return false; + } + if (qa) { + registry.fill(HIST("QA/Tracks/hCutFlow"), enumValue(TrackSelectionStep::TpcClusters)); + } + if (t.tpcChi2NCl() > daughterCuts.maxTPCChi2NCl || t.itsChi2NCl() > daughterCuts.maxITSChi2NCl) { + return false; + } + if (daughterCuts.minITSClusters > 0 && t.itsNCls() < daughterCuts.minITSClusters) { + return false; + } + if (qa) { + registry.fill(HIST("QA/Tracks/hCutFlow"), enumValue(TrackSelectionStep::TrackQuality)); + } + if (std::abs(t.tpcNSigmaPi()) > daughterCuts.maxTPCNSigmaPi) { + return false; + } + if (qa) { + registry.fill(HIST("QA/Tracks/hCutFlow"), enumValue(TrackSelectionStep::TpcPid)); + } + const auto pidMode = static_cast(daughterCuts.pidMode.value); + bool requireTOF = pidMode == PionPidMode::TpcAndTof || (pidMode == PionPidMode::TofRequiredAboveMomentum && t.p() >= daughterCuts.tofRequiredAboveP); + bool useTOF = requireTOF || (pidMode == PionPidMode::TpcWithTofIfAvailable && hasTOF); + if (requireTOF && !hasTOF) { + return false; + } + if (useTOF && std::abs(t.tofNSigmaPi()) > daughterCuts.maxTOFNSigmaPi) { + return false; + } + if (qa) { + registry.fill(HIST("QA/Tracks/hCutFlow"), enumValue(TrackSelectionStep::TofPid)); + registry.fill(HIST("QA/Tracks/AfterSelection/hPt"), t.pt()); + registry.fill(HIST("QA/Tracks/AfterSelection/hEta"), t.eta()); + registry.fill(HIST("QA/Tracks/AfterSelection/hDCAxyVsPt"), t.pt(), t.dcaXY()); + registry.fill(HIST("QA/Tracks/AfterSelection/hDCAzVsPt"), t.pt(), t.dcaZ()); + registry.fill(HIST("QA/Tracks/AfterSelection/hTPCCrossedRowsVsPt"), t.pt(), t.tpcNClsCrossedRows()); + registry.fill(HIST("QA/Tracks/AfterSelection/hTPCSignalVsP"), t.p(), t.tpcSignal()); + registry.fill(HIST("QA/Tracks/AfterSelection/hTPCNSigmaPiVsP"), t.p(), t.tpcNSigmaPi()); + if (hasTOF) { + registry.fill(HIST("QA/Tracks/AfterSelection/hTOFBetaVsP"), t.p(), t.beta()); + registry.fill(HIST("QA/Tracks/AfterSelection/hTOFNSigmaPiVsP"), t.p(), t.tofNSigmaPi()); + } + } + return true; + } + + template + bool selectK0(C const& c, V const& v, DaughterTracks const&, bool qa) + { + float ctau = v.distovertotmom(c.posX(), c.posY(), c.posZ()) * o2::constants::physics::MassK0Short; + if (qa) { + registry.fill(HIST("QA/K0s/hCutFlow"), enumValue(K0sSelectionStep::All)); + registry.fill(HIST("QA/K0s/BeforeSelection/hMassVsPt"), v.mK0Short(), v.pt()); + registry.fill(HIST("QA/K0s/BeforeSelection/hArmenteros"), v.alpha(), v.qtarm()); + registry.fill(HIST("QA/K0s/BeforeSelection/hCosPA"), v.v0cosPA()); + registry.fill(HIST("QA/K0s/BeforeSelection/hDCADaughters"), v.dcaV0daughters()); + registry.fill(HIST("QA/K0s/BeforeSelection/hCtau"), ctau); + } + auto pos = v.template posTrack_as(); + auto neg = v.template negTrack_as(); + if (!selectDaughter(pos, qa)) { + return false; + } + if (!selectDaughter(neg, qa)) { + return false; + } + if (qa) { + registry.fill(HIST("QA/K0s/hCutFlow"), enumValue(K0sSelectionStep::Daughters)); + } + if (v.pt() < k0Cuts.k0sMinPt || v.pt() >= k0Cuts.k0sMaxPt || std::abs(v.rapidity(0)) >= k0Cuts.k0sMaxAbsY) { + return false; + } + if (qa) { + registry.fill(HIST("QA/K0s/hCutFlow"), enumValue(K0sSelectionStep::Kinematics)); + } + if (v.v0cosPA() < k0Cuts.minCosPA || v.dcaV0daughters() > k0Cuts.maxDCADaughters) { + return false; + } + if (qa) { + registry.fill(HIST("QA/K0s/hCutFlow"), enumValue(K0sSelectionStep::Topology)); + } + if (std::abs(v.dcapostopv()) < k0Cuts.minDCAPosToPV || std::abs(v.dcanegtopv()) < k0Cuts.minDCANegToPV) { + return false; + } + if (qa) { + registry.fill(HIST("QA/K0s/hCutFlow"), enumValue(K0sSelectionStep::DaughterDca)); + } + if (v.v0radius() < k0Cuts.minRadius || v.v0radius() >= k0Cuts.maxRadius) { + return false; + } + if (std::abs(ctau) > k0Cuts.maxCtau) { + return false; + } + if (qa) { + registry.fill(HIST("QA/K0s/hCutFlow"), enumValue(K0sSelectionStep::DecayGeometry)); + } + if (k0Cuts.useArmenterosCut && v.qtarm() <= k0Cuts.armenterosSlope * std::abs(v.alpha())) { + return false; + } + if (qa) { + registry.fill(HIST("QA/K0s/hCutFlow"), enumValue(K0sSelectionStep::Armenteros)); + } + if (k0Cuts.rejectLambda && (std::abs(v.mLambda() - o2::constants::physics::MassLambda0) < k0Cuts.lambdaRejectionWindow || std::abs(v.mAntiLambda() - o2::constants::physics::MassLambda0) < k0Cuts.lambdaRejectionWindow)) { + return false; + } + if (qa) { + registry.fill(HIST("QA/K0s/hCutFlow"), enumValue(K0sSelectionStep::LambdaRejection)); + registry.fill(HIST("QA/K0s/AfterSelection/hMassVsPt"), v.mK0Short(), v.pt()); + registry.fill(HIST("QA/K0s/AfterSelection/hArmenteros"), v.alpha(), v.qtarm()); + registry.fill(HIST("QA/K0s/AfterSelection/hCosPA"), v.v0cosPA()); + registry.fill(HIST("QA/K0s/AfterSelection/hDCADaughters"), v.dcaV0daughters()); + registry.fill(HIST("QA/K0s/AfterSelection/hCtau"), ctau); + } + return true; + } + + template + bool getQ(C const& c, int det, float& qx, float& qy) const + { + const int rawIndex = det * QVectorComponents + flow.correctionStep.value + (flow.harmonic.value - 2) * flow.numberOfDetectors.value * QVectorComponents; + if (rawIndex < 0 || det < 0) { + return false; + } + const auto index = static_cast(rawIndex); + const auto detectorIndex = static_cast(det); + if (index >= c.qvecRe().size() || index >= c.qvecIm().size() || detectorIndex >= c.qvecAmp().size() || c.qvecAmp()[detectorIndex] < flow.minQAmplitude.value) { + return false; + } + qx = c.qvecRe()[index]; + qy = c.qvecIm()[index]; + return std::isfinite(qx) && std::isfinite(qy); + } + + std::array decayAngles(ROOT::Math::PxPyPzMVector const& d, ROOT::Math::PxPyPzMVector const& mother) const + { + ROOT::Math::Boost boost{mother.BoostToCM()}; + auto rest = boost(d); + ROOT::Math::XYZVector z(mother.Px(), mother.Py(), mother.Pz()); + if (z.R() == 0.) { + z.SetZ(1.); + } + z = z.Unit(); + ROOT::Math::XYZVector beam(0., 0., 1.); + ROOT::Math::XYZVector y = beam.Cross(z); + if (y.R() < VectorTolerance) { + y.SetY(1.); + } + y = y.Unit(); + ROOT::Math::XYZVector x = y.Cross(z).Unit(); + ROOT::Math::XYZVector k(rest.Px(), rest.Py(), rest.Pz()); + k = k.Unit(); + return {std::abs(k.Dot(z)), std::atan2(k.Dot(y), k.Dot(x))}; + } + + template + void fillResolution(C const& c, float cent, float cX, float cY, float aX, float aY, float tX, float tY) + { + auto dot = [](float x1, float y1, float x2, float y2) { return x1 * x2 + y1 * y2; }; + double psiC = std::atan2(cY, cX) / flow.harmonic, psiA = std::atan2(aY, aX) / flow.harmonic, psiT = std::atan2(tY, tX) / flow.harmonic; + registry.fill(HIST("Resolution/SP/pFT0CFT0AVsCent"), cent, dot(cX, cY, aX, aY)); + registry.fill(HIST("Resolution/SP/pFT0CTPCposVsCent"), cent, dot(cX, cY, tX, tY)); + registry.fill(HIST("Resolution/SP/pFT0ATPCposVsCent"), cent, dot(aX, aY, tX, tY)); + registry.fill(HIST("Resolution/EP/pCos2PsiFT0CFT0AVsCent"), cent, std::cos(flow.harmonic * (psiC - psiA))); + registry.fill(HIST("Resolution/EP/pCos2PsiFT0CTPCposVsCent"), cent, std::cos(flow.harmonic * (psiC - psiT))); + registry.fill(HIST("Resolution/EP/pCos2PsiFT0ATPCposVsCent"), cent, std::cos(flow.harmonic * (psiA - psiT))); + registry.fill(HIST("QA/Flow/QVectors/hQxQyFT0C"), cX, cY); + registry.fill(HIST("QA/Flow/QVectors/hQxQyFT0A"), aX, aY); + registry.fill(HIST("QA/Flow/QVectors/hQxQyTPCpos"), tX, tY); + registry.fill(HIST("QA/Flow/EventPlane/hPsi2VsCentFT0C"), cent, psiC); + registry.fill(HIST("QA/Flow/Calibration/hCalibrationFlag"), c.isCalibrated()); + } + + struct LikeSignCandidate { + ROOT::Math::PxPyPzMVector p4; + uint64_t firstTrackId{}; + uint64_t secondTrackId{}; + }; + + template + bool buildLikeSignCandidate(C const& c, T const& t1, T const& t2, LikeSignCandidate& candidate) + { + auto track1 = getTrackParCov(t1); + auto track2 = getTrackParCov(t2); + int nCandidates = 0; + try { + nCandidates = lsFitter.process(track1, track2); + } catch (std::runtime_error const&) { + return false; + } + if (nCandidates == 0) { + return false; + } + auto const& vertex = lsFitter.getPCACandidate(); + std::array p1{}; + std::array p2{}; + lsFitter.getTrack(0).getPxPyPzGlo(p1); + lsFitter.getTrack(1).getPxPyPzGlo(p2); + ROOT::Math::PxPyPzMVector pion1(p1[0], p1[1], p1[2], o2::constants::physics::MassPionCharged); + ROOT::Math::PxPyPzMVector pion2(p2[0], p2[1], p2[2], o2::constants::physics::MassPionCharged); + auto pair = pion1 + pion2; + if (pair.Pt() < k0Cuts.k0sMinPt || pair.Pt() >= k0Cuts.k0sMaxPt || std::abs(pair.Rapidity()) >= k0Cuts.k0sMaxAbsY) { + return false; + } + const std::array pv{c.posX(), c.posY(), c.posZ()}; + const std::array sv{static_cast(vertex[0]), static_cast(vertex[1]), static_cast(vertex[2])}; + const std::array momentum{static_cast(pair.Px()), static_cast(pair.Py()), static_cast(pair.Pz())}; + const float cosPA = RecoDecay::cpa(pv, sv, momentum); + const float dcaDaughters = lsFitter.getChi2AtPCACandidate(); + if (cosPA < k0Cuts.minCosPA || dcaDaughters > k0Cuts.maxDCADaughters) { + return false; + } + if (std::abs(t1.dcaXY()) < k0Cuts.minDCAPosToPV || std::abs(t2.dcaXY()) < k0Cuts.minDCANegToPV) { + return false; + } + const float radius = std::hypot(vertex[0], vertex[1]); + if (radius < k0Cuts.minRadius || radius >= k0Cuts.maxRadius) { + return false; + } + const float decayLength = std::sqrt((vertex[0] - c.posX()) * (vertex[0] - c.posX()) + (vertex[1] - c.posY()) * (vertex[1] - c.posY()) + (vertex[2] - c.posZ()) * (vertex[2] - c.posZ())); + const float ctau = decayLength * o2::constants::physics::MassK0Short / pair.P(); + if (std::abs(ctau) > k0Cuts.maxCtau) { + return false; + } + ROOT::Math::XYZVector total(pair.Px(), pair.Py(), pair.Pz()); + if (total.R() <= 0.) { + return false; + } + total = total.Unit(); + ROOT::Math::XYZVector daughter1(p1[0], p1[1], p1[2]); + ROOT::Math::XYZVector daughter2(p2[0], p2[1], p2[2]); + const double longitudinal1 = daughter1.Dot(total); + const double longitudinal2 = daughter2.Dot(total); + const double denominator = longitudinal1 + longitudinal2; + if (std::abs(denominator) < VectorTolerance) { + return false; + } + const double alpha = (longitudinal1 - longitudinal2) / denominator; + const double qt = (daughter1 - longitudinal1 * total).R(); + if (k0Cuts.useArmenterosCut && qt <= k0Cuts.armenterosSlope * std::abs(alpha)) { + return false; + } + candidate = {.p4 = pair, .firstTrackId = static_cast(t1.globalIndex()), .secondTrackId = static_cast(t2.globalIndex())}; + return true; + } + + void fillLikeSignPair(LikeSignCandidate const& first, LikeSignCandidate const& second, float cent, LikeSignCategory category) + { + if (first.firstTrackId == second.firstTrackId || first.firstTrackId == second.secondTrackId || first.secondTrackId == second.firstTrackId || first.secondTrackId == second.secondTrackId) { + return; + } + ROOT::Math::PxPyPzMVector d1(first.p4.Px(), first.p4.Py(), first.p4.Pz(), o2::constants::physics::MassK0Short); + ROOT::Math::PxPyPzMVector d2(second.p4.Px(), second.p4.Py(), second.p4.Pz(), o2::constants::physics::MassK0Short); + auto pair = d1 + d2; + if (std::abs(pair.Rapidity()) >= pairCuts.pairMaxAbsY || pair.Pt() < pairCuts.pairMinPt || pair.Pt() >= pairCuts.pairMaxPt || pair.M() < pairCuts.pairMassMin || pair.M() >= pairCuts.pairMassMax) { + return; + } + const auto angles = decayAngles(d1, pair); + const double cosTheta2 = angles[0] * angles[0]; + const double p2Weight = 0.5 * (3. * cosTheta2 - 1.); + const double p4Weight = 0.125 * (35. * cosTheta2 * cosTheta2 - 30. * cosTheta2 + 3.); + registry.fill(HIST("K0sK0s/LikeSign/Combined/hCounts"), pair.M(), pair.Pt(), cent, angles[0], angles[1]); + registry.fill(HIST("K0sK0s/LikeSign/Combined/hP2Weighted"), pair.M(), pair.Pt(), cent, angles[0], angles[1], p2Weight); + registry.fill(HIST("K0sK0s/LikeSign/Combined/hP4Weighted"), pair.M(), pair.Pt(), cent, angles[0], angles[1], p4Weight); + if (category == LikeSignCategory::PositivePositive) { + registry.fill(HIST("K0sK0s/LikeSign/PPPP/hCounts"), pair.M(), pair.Pt(), cent, angles[0], angles[1]); + registry.fill(HIST("K0sK0s/LikeSign/PPPP/hP2Weighted"), pair.M(), pair.Pt(), cent, angles[0], angles[1], p2Weight); + registry.fill(HIST("K0sK0s/LikeSign/PPPP/hP4Weighted"), pair.M(), pair.Pt(), cent, angles[0], angles[1], p4Weight); + } else if (category == LikeSignCategory::NegativeNegative) { + registry.fill(HIST("K0sK0s/LikeSign/MMMM/hCounts"), pair.M(), pair.Pt(), cent, angles[0], angles[1]); + registry.fill(HIST("K0sK0s/LikeSign/MMMM/hP2Weighted"), pair.M(), pair.Pt(), cent, angles[0], angles[1], p2Weight); + registry.fill(HIST("K0sK0s/LikeSign/MMMM/hP4Weighted"), pair.M(), pair.Pt(), cent, angles[0], angles[1], p4Weight); + } else { + registry.fill(HIST("K0sK0s/LikeSign/PPMM/hCounts"), pair.M(), pair.Pt(), cent, angles[0], angles[1]); + registry.fill(HIST("K0sK0s/LikeSign/PPMM/hP2Weighted"), pair.M(), pair.Pt(), cent, angles[0], angles[1], p2Weight); + registry.fill(HIST("K0sK0s/LikeSign/PPMM/hP4Weighted"), pair.M(), pair.Pt(), cent, angles[0], angles[1], p4Weight); + } + } + + template + void fillLikeSign(C const& c, DaughterTracks const& tracks) + { + auto tracksInCollision = tracks.sliceBy(perCollisionTrack, c.globalIndex()); + std::vector positiveTrackRows; + std::vector negativeTrackRows; + positiveTrackRows.reserve(tracksInCollision.size()); + negativeTrackRows.reserve(tracksInCollision.size()); + + // Apply all inexpensive daughter-quality and PID selections before forming + // pairs. Running the DCA fitter over every track pair scales as N^2 and + // dominated the processing time in high-multiplicity events. + for (auto const& track : tracksInCollision) { + if (!selectDaughter(track, false)) { + continue; + } + const auto localRow = static_cast(track.globalIndex()) - tracks.offset(); + (track.signed1Pt() > 0.f ? positiveTrackRows : negativeTrackRows).push_back(localRow); + } + + std::vector positiveCandidates; + std::vector negativeCandidates; + auto buildCandidates = [&](std::vector const& selectedRows, std::vector& candidates, bool positive) { + for (size_t i = 0; i < selectedRows.size(); ++i) { + auto track1 = tracks.iteratorAt(selectedRows[i]); + for (size_t j = i + 1; j < selectedRows.size(); ++j) { + auto track2 = tracks.iteratorAt(selectedRows[j]); + LikeSignCandidate candidate; + if (!buildLikeSignCandidate(c, track1, track2, candidate)) { + continue; + } + if (positive) { + registry.fill(HIST("K0s/LikeSign/PP/hCounts"), candidate.p4.M(), candidate.p4.Pt(), c.centFT0C()); + } else { + registry.fill(HIST("K0s/LikeSign/MM/hCounts"), candidate.p4.M(), candidate.p4.Pt(), c.centFT0C()); + } + if (candidate.p4.M() >= k0Cuts.k0sMassMin && candidate.p4.M() < k0Cuts.k0sMassMax) { + candidates.push_back(candidate); + } + } + } + }; + buildCandidates(positiveTrackRows, positiveCandidates, true); + buildCandidates(negativeTrackRows, negativeCandidates, false); + for (size_t i = 0; i < positiveCandidates.size(); ++i) { + for (size_t j = i + 1; j < positiveCandidates.size(); ++j) { + fillLikeSignPair(positiveCandidates[i], positiveCandidates[j], c.centFT0C(), LikeSignCategory::PositivePositive); + } + } + for (size_t i = 0; i < negativeCandidates.size(); ++i) { + for (size_t j = i + 1; j < negativeCandidates.size(); ++j) { + fillLikeSignPair(negativeCandidates[i], negativeCandidates[j], c.centFT0C(), LikeSignCategory::NegativeNegative); + } + } + for (auto const& positive : positiveCandidates) { + for (auto const& negative : negativeCandidates) { + fillLikeSignPair(positive, negative, c.centFT0C(), LikeSignCategory::PositiveNegative); + } + } + } + + template + void fillSame(C const& c, V const& v0s, DaughterTracks const& tracks) + { + float cent = c.centFT0C(), cX = 0.f, cY = 0.f, aX = 0.f, aY = 0.f, tX = 0.f, tY = 0.f; + bool qok = getQ(c, flow.ft0cId, cX, cY) && getQ(c, flow.ft0aId, aX, aY) && getQ(c, flow.tpcPosId, tX, tY); + if (qok) { + fillResolution(c, cent, cX, cY, aX, aY, tX, tY); + } + for (auto const& v : v0s) { + if (!selectK0(c, v, tracks, true)) { + continue; + } + double phi = v.phi(), psi = qok ? std::atan2(cY, cX) / flow.harmonic : 0.; + double ux = std::cos(flow.harmonic * phi), uy = std::sin(flow.harmonic * phi); + registry.fill(HIST("K0s/hCounts"), v.mK0Short(), v.pt(), cent); + if (qok) { + registry.fill(HIST("K0s/hCos2Weighted"), v.mK0Short(), v.pt(), cent, std::cos(flow.harmonic * (phi - psi))); + registry.fill(HIST("K0s/hSPWeightedFT0C"), v.mK0Short(), v.pt(), cent, ux * cX + uy * cY); + registry.fill(HIST("K0s/hSPWeightedFT0A"), v.mK0Short(), v.pt(), cent, ux * aX + uy * aY); + } + } + for (auto const& [v1, v2] : combinations(CombinationsStrictlyUpperIndexPolicy(v0s, v0s))) { + registry.fill(HIST("QA/K0sK0s/hPairCutFlow"), enumValue(PairSelectionStep::All)); + registry.fill(HIST("QA/K0sK0s/hPairCutFlowVsCent"), enumValue(PairSelectionStep::All), cent); + if (v1.posTrackId() == v2.posTrackId() || v1.posTrackId() == v2.negTrackId() || v1.negTrackId() == v2.posTrackId() || v1.negTrackId() == v2.negTrackId()) { + continue; + } + registry.fill(HIST("QA/K0sK0s/hPairCutFlow"), enumValue(PairSelectionStep::NoSharedDaughters)); + registry.fill(HIST("QA/K0sK0s/hPairCutFlowVsCent"), enumValue(PairSelectionStep::NoSharedDaughters), cent); + if (!selectK0(c, v1, tracks, false) || !selectK0(c, v2, tracks, false)) { + continue; + } + registry.fill(HIST("QA/K0sK0s/hPairCutFlow"), enumValue(PairSelectionStep::SelectedK0s)); + registry.fill(HIST("QA/K0sK0s/hPairCutFlowVsCent"), enumValue(PairSelectionStep::SelectedK0s), cent); + ROOT::Math::PxPyPzMVector d1(v1.px(), v1.py(), v1.pz(), o2::constants::physics::MassK0Short), d2(v2.px(), v2.py(), v2.pz(), o2::constants::physics::MassK0Short), m = d1 + d2; + double dr = std::hypot(v1.eta() - v2.eta(), TVector2::Phi_mpi_pi(v1.phi() - v2.phi())); + if (pairCuts.enableClosePairRejection && dr < pairCuts.minDeltaR) { + continue; + } + registry.fill(HIST("QA/K0sK0s/hPairCutFlow"), enumValue(PairSelectionStep::ClosePair)); + registry.fill(HIST("QA/K0sK0s/hPairCutFlowVsCent"), enumValue(PairSelectionStep::ClosePair), cent); + if (std::abs(m.Rapidity()) >= pairCuts.pairMaxAbsY || m.Pt() < pairCuts.pairMinPt || m.Pt() >= pairCuts.pairMaxPt || m.M() < pairCuts.pairMassMin || m.M() >= pairCuts.pairMassMax) { + continue; + } + registry.fill(HIST("QA/K0sK0s/hPairCutFlow"), enumValue(PairSelectionStep::Kinematics)); + registry.fill(HIST("QA/K0sK0s/hPairCutFlowVsCent"), enumValue(PairSelectionStep::Kinematics), cent); + const bool signalPair = v1.mK0Short() >= k0Cuts.k0sMassMin && v1.mK0Short() < k0Cuts.k0sMassMax && v2.mK0Short() >= k0Cuts.k0sMassMin && v2.mK0Short() < k0Cuts.k0sMassMax; + const auto side1 = sidebandRegion(v1.mK0Short()); + const auto side2 = sidebandRegion(v2.mK0Short()); + if (side1 != SidebandRegion::Outside && side2 != SidebandRegion::Outside) { + fillSidebandPair(d1, m, cent, side1, side2); + } + if (!signalPair) { + continue; + } + registry.fill(HIST("QA/K0sK0s/hPairCutFlow"), enumValue(PairSelectionStep::SignalMass)); + registry.fill(HIST("QA/K0sK0s/hPairCutFlowVsCent"), enumValue(PairSelectionStep::SignalMass), cent); + registry.fill(HIST("QA/K0sK0s/SameEvent/hMass"), m.M()); + registry.fill(HIST("QA/K0sK0s/SameEvent/hDeltaR"), dr); + registry.fill(HIST("QA/K0sK0s/SameEvent/hPt1VsPt2"), v1.pt(), v2.pt()); + registry.fill(HIST("QA/K0sK0s/SameEvent/hK0Mass1VsMass2"), v1.mK0Short(), v2.mK0Short()); + auto ang = decayAngles(d1, m); + registry.fill(HIST("K0sK0s/SameEvent/hCounts"), m.M(), m.Pt(), cent, ang[0], ang[1]); + const double cosTheta = ang[0]; + const double cosTheta2 = cosTheta * cosTheta; + const double p2Weight = 0.5 * (3. * cosTheta2 - 1.); + const double p4Weight = 0.125 * (35. * cosTheta2 * cosTheta2 - 30. * cosTheta2 + 3.); + registry.fill(HIST("K0sK0s/SameEvent/hP2Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], p2Weight); + registry.fill(HIST("K0sK0s/SameEvent/hP4Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], p4Weight); + if (qok) { + double ux = std::cos(flow.harmonic * m.Phi()), uy = std::sin(flow.harmonic * m.Phi()), psi = std::atan2(cY, cX) / flow.harmonic; + registry.fill(HIST("K0sK0s/SameEvent/hCos2Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], std::cos(flow.harmonic * (m.Phi() - psi))); + registry.fill(HIST("K0sK0s/SameEvent/hSPWeightedFT0C"), m.M(), m.Pt(), cent, ang[0], ang[1], ux * cX + uy * cY); + registry.fill(HIST("K0sK0s/SameEvent/hSPWeightedFT0A"), m.M(), m.Pt(), cent, ang[0], ang[1], ux * aX + uy * aY); + } + } + if (likeSign.enableLikeSign) { + fillLikeSign(c, tracks); + } + } + + void processSame(Collisions::iterator const& collision, aod::V0Datas const& v0s, DaughterTracks const& tracks) + { + if (!selectEvent(collision, true)) { + return; + } + fillSame(collision, v0s, tracks); + } + PROCESS_SWITCH(K0sK0sFlow, processSame, "same-event K0S-K0S and flow", true); + + void processMixed(Collisions const& collisions, aod::V0Datas const& v0s, DaughterTracks const& tracks) + { + MixingBinning binning{{axisMixZvtx, axisMixCent}, true}; + for (auto const& [c1, c2] : selfCombinations(binning, pairCuts.mixingDepth, -1, collisions, collisions)) { + if (c1.index() == c2.index()) { + continue; + } + if (!selectEvent(c1, false) || !selectEvent(c2, false)) { + continue; + } + auto s1 = v0s.sliceBy(perCollisionV0, c1.globalIndex()); + auto s2 = v0s.sliceBy(perCollisionV0, c2.globalIndex()); + if (s1.size() == 0 || s2.size() == 0) { + continue; + } + for (auto const& [v1, v2] : combinations(CombinationsFullIndexPolicy(s1, s2))) { + if (!selectK0(c1, v1, tracks, false) || !selectK0(c2, v2, tracks, false) || v1.mK0Short() < k0Cuts.k0sMassMin || v1.mK0Short() >= k0Cuts.k0sMassMax || v2.mK0Short() < k0Cuts.k0sMassMin || v2.mK0Short() >= k0Cuts.k0sMassMax) { + continue; + } + ROOT::Math::PxPyPzMVector d1(v1.px(), v1.py(), v1.pz(), o2::constants::physics::MassK0Short), d2(v2.px(), v2.py(), v2.pz(), o2::constants::physics::MassK0Short), m = d1 + d2; + if (std::abs(m.Rapidity()) >= pairCuts.pairMaxAbsY || m.Pt() < pairCuts.pairMinPt || m.Pt() >= pairCuts.pairMaxPt || m.M() < pairCuts.pairMassMin || m.M() >= pairCuts.pairMassMax) { + continue; + } + auto ang = decayAngles(d1, m); + registry.fill(HIST("K0sK0s/MixedEvent/hCounts"), m.M(), m.Pt(), c1.centFT0C(), ang[0], ang[1]); + const double cosTheta = ang[0]; + const double cosTheta2 = cosTheta * cosTheta; + const double p2Weight = 0.5 * (3. * cosTheta2 - 1.); + const double p4Weight = 0.125 * (35. * cosTheta2 * cosTheta2 - 30. * cosTheta2 + 3.); + registry.fill(HIST("K0sK0s/MixedEvent/hP2Weighted"), m.M(), m.Pt(), c1.centFT0C(), ang[0], ang[1], p2Weight); + registry.fill(HIST("K0sK0s/MixedEvent/hP4Weighted"), m.M(), m.Pt(), c1.centFT0C(), ang[0], ang[1], p4Weight); + registry.fill(HIST("QA/K0sK0s/MixedEvent/hMass"), m.M()); + } + } + } + PROCESS_SWITCH(K0sK0sFlow, processMixed, "mixed-event K0S-K0S (optional cross-check)", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/Tasks/Resonances/k1AnalysisMicro.cxx b/PWGLF/Tasks/Resonances/k1AnalysisMicro.cxx index 0cbab0d427c..efd75953055 100644 --- a/PWGLF/Tasks/Resonances/k1AnalysisMicro.cxx +++ b/PWGLF/Tasks/Resonances/k1AnalysisMicro.cxx @@ -14,12 +14,11 @@ /// \author Su-Jeong Ji , Bong-Hwi Lim /// +#include "PWGLF/Core/K1AnalysisMicroCore.h" +#include "PWGLF/Core/ResoAnalysisSelectionCore.h" #include "PWGLF/DataModel/LFResonanceTables.h" -#include -#include #include -#include #include #include #include @@ -28,129 +27,135 @@ #include #include #include +#include #include #include #include -#include -#include // FIXME +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include +#include #include using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::soa; -using namespace o2::constants::physics; -using namespace o2::constants::math; -; +using namespace o2::analysis::resonance; +using namespace o2::analysis::k1micro; + +/// Histogram binning, QA and debug output +struct HistogramOptions : ConfigurableGroup { + Configurable cNbinsDiv{"cNbinsDiv", 1, "Integer to divide the number of bins"}; + Configurable additionalQAplots{"additionalQAplots", true, "Additional QA plots"}; + Configurable cfgTruthDebug{"cfgTruthDebug", 0, "Maximum logged matched candidates per truth channel"}; + Configurable cUseTruthRapidity{"cUseTruthRapidity", true, "Require generated K1 rapidity in the configured window for MC efficiency numerators"}; +}; + +enum BinAnti : unsigned int { + kNormal = 0, + kAnti, + kNAEnd +}; + +enum BinType : unsigned int { + kK1P = 0, + kK1N, + kK1P_Mix, + kK1N_Mix, + kK1P_GenINEL10, + kK1N_GenINEL10, + kK1P_GenINELgt10, + kK1N_GenINELgt10, + kK1P_GenTrig10, + kK1N_GenTrig10, + kK1P_GenEvtSel, + kK1N_GenEvtSel, + kK1P_Rec, + kK1N_Rec, + kTYEnd +}; + +enum class QAFolder { + Before, // QA/*: before the candidate cuts + After, // QAcut/*: after the candidate cuts + MC // QAMC/*: matched K1 truth candidates +}; struct K1AnalysisMicro { - enum BinAnti : unsigned int { - kNormal = 0, - kAnti, - kNAEnd - }; - enum BinType : unsigned int { - kK1P = 0, - kK1N, - kK1P_Mix, - kK1N_Mix, - kK1P_GenINEL10, - kK1N_GenINEL10, - kK1P_GenINELgt10, - kK1N_GenINELgt10, - kK1P_GenTrig10, - kK1N_GenTrig10, - kK1P_GenEvtSel, - kK1N_GenEvtSel, - kK1P_Rec, - kK1N_Rec, - kTYEnd - }; + // Module-initializer v001 tables; full tracks keep their unversioned schema as a fallback. + using ResoCollisions = aod::ResoCollisions_001; + using ResoMCCols = soa::Join; + using ResoTracks = aod::ResoTracks; // no v001 exists; K1 does not need ResoTrackTracks (trackId unused) + using ResoMicroTracks = aod::ResoMicroTracks_001; + using ResoMCTracks = soa::Join; + using ResoMCMicroTracks = soa::Join; + using ResoMCParents = aod::ResoMCParents_001; + SliceCache cache; - Preslice perRCol = aod::resodaughter::resoCollisionId; - Preslice perCollision = aod::track::collisionId; + // Registered only to enable the slice cache that SameKindPair (event mixing) needs, as in Xi1820Analysis + Preslice perResoCollisionTrack = aod::resodaughter::resoCollisionId; + Preslice perResoCollisionMicroTrack = aod::resodaughter::resoCollisionId; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - using ResoMCCols = soa::Join; - //// Configurables - Configurable cNbinsDiv{"cNbinsDiv", 1, "Integer to divide the number of bins"}; + // Selection shared with the K1 training-table task (plain JSON keys, no group prefix) + EventCuts eventCuts; + TrackCuts trackCuts; + PionPidCuts pionPID; + KaonPidCuts kaonPID; + SecondaryCuts secondaryCuts; + CandidateCuts candidateCuts; + HistogramOptions histogramOptions; + /// Event Mixing Configurable nEvtMixing{"nEvtMixing", 5, "Number of events to mix"}; ConfigurableAxis cfgVtxBins{"cfgVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; ConfigurableAxis cfgMultBins{"cfgMultBins", {VARIABLE_WIDTH, 0.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; - /// Pre-selection cuts - Configurable cMinPtcut{"cMinPtcut", 0.15, "Track minium pt cut"}; - - /// DCA Selections - // DCAr to PV - Configurable cMaxDCArToPVcut{"cMaxDCArToPVcut", 0.1, "Track DCAr cut to PV Maximum"}; - // DCAz to PV - Configurable cMaxDCAzToPVcut{"cMaxDCAzToPVcut", 0.1, "Track DCAz cut to PV Maximum"}; - Configurable cMinDCAzToPVcut{"cMinDCAzToPVcut", 0.0, "Track DCAz cut to PV Minimum"}; - - /// PID Selections - Configurable cMaxTPCnSigmaPion{"cMaxTPCnSigmaPion", 3.0, "TPC nSigma cut for Pion"}; // TPC - Configurable cMaxTOFnSigmaPion{"cMaxTOFnSigmaPion", 3.0, "TOF nSigma cut for Pion"}; // TOF - Configurable nsigmaCutCombinedPion{"nsigmaCutCombinedPion", -999, "Combined nSigma cut for Pion"}; // Combined - Configurable cTOFVeto{"cTOFVeto", true, "TOF Veto, if false, TOF is nessessary for PID selection"}; // TOF Veto - Configurable cUseOnlyTOFTrackPi{"cUseOnlyTOFTrackPi", false, "Use only TOF track for PID selection"}; // Use only TOF track for Pion PID selection - // Kaon - Configurable cMaxTPCnSigmaKaon{"cMaxTPCnSigmaKaon", 3.0, "TPC nSigma cut for Kaon"}; // TPC - Configurable cMaxTOFnSigmaKaon{"cMaxTOFnSigmaKaon", 3.0, "TOF nSigma cut for Kaon"}; // TOF - Configurable nsigmaCutCombinedKaon{"nsigmaCutCombinedKaon", -999, "Combined nSigma cut for Kaon"}; // Combined - Configurable cUseOnlyTOFTrackKa{"cUseOnlyTOFTrackKa", false, "Use only TOF track for PID selection"}; // Use only TOF track for Kaon PID selection - // Track selections - Configurable cfgPrimaryTrack{"cfgPrimaryTrack", true, "Primary track selection"}; // kGoldenChi2 | kDCAxy | kDCAz - Configurable cfgGlobalWoDCATrack{"cfgGlobalWoDCATrack", true, "Global track selection without DCA"}; // kQualityTracks (kTrackType | kTPCNCls | kTPCCrossedRows | kTPCCrossedRowsOverNCls | kTPCChi2NDF | kTPCRefit | kITSNCls | kITSChi2NDF | kITSRefit | kITSHits) | kInAcceptanceTracks (kPtRange | kEtaRange) - Configurable cfgGlobalTrack{"cfgGlobalTrack", false, "Global track selection"}; // kGoldenChi2 | kDCAxy | kDCAz - Configurable cfgPVContributor{"cfgPVContributor", false, "PV contributor track selection"}; // PV Contriuibutor - Configurable additionalQAplots{"additionalQAplots", true, "Additional QA plots"}; - Configurable additionalEvsel{"additionalEvsel", true, "Additional event selcection"}; - Configurable cfgTPCcluster{"cfgTPCcluster", 0, "Number of TPC cluster"}; - Configurable cfgUseTPCRefit{"cfgUseTPCRefit", false, "Require TPC Refit"}; - Configurable cfgUseITSRefit{"cfgUseITSRefit", false, "Require ITS Refit"}; - Configurable cfgHasTOF{"cfgHasTOF", false, "Require TOF"}; - - // Secondary selection - Configurable cMinSecondaryPtCut{"cMinSecondaryPtCut", 0.5, "Min pT cut for secondary selection"}; - /* - Configurable cfgModeK892orRho{"cfgModeK892orRho", false, "Secondary scenario for K892 (true) or Rho (false)"}; - Configurable cSecondaryMasswindow{"cSecondaryMasswindow", 0.1, "Secondary inv mass selection window"}; - Configurable cMinAnotherSecondaryMassCut{"cMinAnotherSecondaryMassCut", 0, "Min inv. mass selection of another secondary scenario"}; - Configurable cMaxAnotherSecondaryMassCut{"cMaxAnotherSecondaryMassCut", 999, "MAx inv. mass selection of another secondary scenario"}; - Configurable cMinPiKaMassCut{"cMinPiKaMassCut", 0, "bPion-Kaon pair inv mass selection minimum"}; - Configurable cMaxPiKaMassCut{"cMaxPiKaMassCut", 999, "bPion-Kaon pair inv mass selection maximum"}; - Configurable cMinAngle{"cMinAngle", 0, "Minimum angle between K(892)0 and bachelor pion"}; - Configurable cMaxAngle{"cMaxAngle", 4, "Maximum angle between K(892)0 and bachelor pion"}; - Configurable cMinPairAsym{"cMinPairAsym", -1, "Minimum pair asymmetry"}; - Configurable cMaxPairAsym{"cMaxPairAsym", 1, "Maximum pair asymmetry"}; -*/ - - // K1 selection - Configurable cK1MaxRap{"cK1MaxRap", 0.5, "K1 maximum rapidity"}; - Configurable cK1MinRap{"cK1MinRap", -0.5, "K1 minimum rapidity"}; - - void init(o2::framework::InitContext&) + + K1AnalysisMicroCore core; + std::array truthDebugCounts{}; + + void init(InitContext&) + { + const int sameEventModes = static_cast(doprocessResoTracks) + static_cast(doprocessResoMicroTracks) + + static_cast(doprocessMC) + static_cast(doprocessMCMicro); + const int mixedEventModes = static_cast(doprocessME) + static_cast(doprocessMEMicro); + if (sameEventModes > 1 || mixedEventModes > 1) { + LOG(fatal) << "Enable at most one same-event mode and one mixing mode"; + } + + ProcessModes modes; + modes.microTracks = doprocessResoMicroTracks || doprocessMCMicro || doprocessMEMicro; + modes.mcReco = doprocessMC || doprocessMCMicro; + modes.mcRecoMicro = doprocessMCMicro; + modes.mcGen = doprocessMCTrue; + core.init(histos, eventCuts, trackCuts, pionPID, kaonPID, secondaryCuts, candidateCuts, modes); + registerHistograms(modes); + + // Print output histograms statistics + LOG(info) << "Size of the histograms in K1 Analysis Task"; + histos.print(); + } + + void registerHistograms(ProcessModes const& modes) { + const int nBinsDiv = histogramOptions.cNbinsDiv; std::vector centBinning = {0., 1., 5., 10., 15., 20., 25., 30., 35., 40., 45., 50., 55., 60., 65., 70., 80., 90., 100., 200.}; AxisSpec centAxis = {centBinning, "T0M (%)"}; AxisSpec ptAxis = {150, 0, 15, "#it{p}_{T} (GeV/#it{c})"}; AxisSpec dcaxyAxis = {300, 0, 3, "DCA_{#it{xy}} (cm)"}; AxisSpec dcazAxis = {500, 0, 5, "DCA_{#it{z}} (cm)"}; - AxisSpec invMassAxisK892 = {1400 / cNbinsDiv, 0.6, 2.0, "Invariant Mass (GeV/#it{c}^2)"}; // K(892)0 - AxisSpec invMassAxisRho = {2000 / cNbinsDiv, 0.0, 2.0, "Invariant Mass (GeV/#it{c}^2)"}; // rho - AxisSpec invMassAxisReso = {1600 / cNbinsDiv, 0.9f, 2.5f, "Invariant Mass (GeV/#it{c}^2)"}; // K1 - AxisSpec invMassAxisScan = {250, 0, 2.5, "Invariant Mass (GeV/#it{c}^2)"}; // For selection + AxisSpec invMassAxisK892 = {1400 / nBinsDiv, 0.6, 2.0, "Invariant Mass (GeV/#it{c}^2)"}; // K(892)0 + AxisSpec invMassAxisRho = {2000 / nBinsDiv, 0.0, 2.0, "Invariant Mass (GeV/#it{c}^2)"}; // rho + AxisSpec invMassAxisReso = {1600 / nBinsDiv, 0.9f, 2.5f, "Invariant Mass (GeV/#it{c}^2)"}; // K1 AxisSpec pidQAAxis = {130, -6.5, 6.5}; - AxisSpec dataTypeAxis = {9, 0, 9, "Histogram types"}; - AxisSpec mcTypeAxis = {4, 0, 4, "Histogram types"}; // THnSparse AxisSpec axisAnti = {BinAnti::kNAEnd, 0, BinAnti::kNAEnd, "Type of bin: Normal or Anti"}; AxisSpec axisType = {BinType::kTYEnd, 0, BinType::kTYEnd, "Type of bin with charge and mix"}; - AxisSpec mcLabelAxis = {5, -0.5, 4.5, "MC Label"}; // DCA QA // Primary pion @@ -223,7 +228,19 @@ struct K1AnalysisMicro { histos.add("k1invmass_Mix", "Invariant mass of K1(1270) (ME)", HistType::kTH1F, {invMassAxisReso}); // MC - if (doprocessMC) { + if (modes.mcReco) { + AxisSpec channelAxis = {3, -0.5, 2.5, "0: non-K1, 1: rho K, 2: K* pi"}; + histos.add("MCReco/collisions", "Selected reconstructed MC collisions", HistType::kTH1D, {{1, 0, 1}}); + histos.add("MCReco/microTracks", "Input micro tracks in selected MC collisions", HistType::kTH1D, {{1, 0, 1}}); + // These channel and pT histograms are pre-truth-fiducial QA; MCReco/response is the validated numerator. + histos.add("MCReco/channel", "Pre-truth-fiducial QA: selected pi-pi-K combinations by truth channel", HistType::kTH1D, {channelAxis}); + histos.add("MCReco/mass", "Reconstructed mass by truth channel", HistType::kTH2D, {channelAxis, invMassAxisReso}); + histos.add("MCReco/pt", "Pre-truth-fiducial QA: reconstructed pT by truth channel", HistType::kTH2D, {channelAxis, ptAxis}); + histos.add("MCReco/piPiMass", "pi-pi mass by truth channel", HistType::kTH2D, {channelAxis, invMassAxisRho}); + histos.add("MCReco/pi1KMass", "First pion-kaon mass by truth channel", HistType::kTH2D, {channelAxis, invMassAxisK892}); + histos.add("MCReco/pi2KMass", "Second pion-kaon mass by truth channel", HistType::kTH2D, {channelAxis, invMassAxisK892}); + histos.add("MCReco/response", "Selected MC candidates;reconstructed pT;generated pT;channel;charge;centrality", HistType::kTHnSparseD, + {ptAxis, ptAxis, channelAxis, AxisSpec{2, -1.5, 1.5, "K1 charge"}, centAxis}); histos.add("k1invmass_MC", "Invariant mass of K1(1270)", HistType::kTH1F, {invMassAxisReso}); histos.add("k1invmass_MC_noK1", "Invariant mass of K1(1270)", HistType::kTH1F, {invMassAxisReso}); @@ -254,494 +271,340 @@ struct K1AnalysisMicro { histos.add("QAMC/hInvmassSecon_PiKa", "Invariant mass of secondary resonance vs pion-kaon", HistType::kTH2F, {invMassAxisRho, invMassAxisK892}); histos.add("QAMC/hInvmassSecon", "Invariant mass of secondary resonance", HistType::kTH1F, {invMassAxisRho}); histos.add("QAMC/hpT_Secondary", "pT distribution of secondary resonance", HistType::kTH1F, {ptAxis}); - } // doprocessMC - // Print output histograms statistics - LOG(info) << "Size of the histograms in K1 Analysis Task"; - histos.print(); - } // init - - // PDG code - int kPDGRho770 = 113; - int kK1Plus = 10323; - - template - bool trackCut(const TrackType& track) - { - if constexpr (!IsResoMicrotrack) { - // basic track cuts - if (std::abs(track.pt()) < cMinPtcut) - return false; - if (std::abs(track.dcaXY()) > cMaxDCArToPVcut) - return false; - if (std::abs(track.dcaZ()) > cMaxDCAzToPVcut) - return false; - if (track.tpcNClsFound() < cfgTPCcluster) - return false; - if (cfgHasTOF && !track.hasTOF()) - return false; - if (cfgUseITSRefit && !track.passedITSRefit()) - return false; - if (cfgUseTPCRefit && !track.passedTPCRefit()) - return false; - if (cfgPVContributor && !track.isPVContributor()) - return false; - if (cfgPrimaryTrack && !track.isPrimaryTrack()) - return false; - if (cfgGlobalWoDCATrack && !track.isGlobalTrackWoDCA()) - return false; - if (cfgGlobalTrack && !track.isGlobalTrack()) - return false; - } else { - if (std::abs(track.pt()) < cMinPtcut) - return false; - if (o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAxy(track.trackSelectionFlags()) > cMaxDCArToPVcut - Epsilon) - return false; - if (o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAz(track.trackSelectionFlags()) > cMaxDCAzToPVcut - Epsilon) - return false; - if (cfgPrimaryTrack && !track.isPrimaryTrack()) - return false; - if (cfgGlobalWoDCATrack && !track.isGlobalTrackWoDCA()) - return false; - if (cfgPVContributor && !track.isPVContributor()) - return false; + } // mcReco + if (modes.mcGen) { + AxisSpec channelAxis = {3, -0.5, 2.5, "0: other/unresolved, 1: rho K, 2: K* pi"}; + histos.add("MCGen/chargeChannel", "K1 parents in selected reconstructed events, inside the K1 rapidity window", HistType::kTH2D, {{2, -1.5, 1.5, "K1 charge"}, channelAxis}); + histos.add("MCGen/ptChannel", "Generated K1 pT by immediate decay channel", HistType::kTH2D, {channelAxis, ptAxis}); + histos.add("MCGen/response", "Generated K1 parents;generated pT;channel;charge;centrality", HistType::kTHnSparseD, + {ptAxis, channelAxis, AxisSpec{2, -1.5, 1.5, "K1 charge"}, centAxis}); } - return true; } - // Pion PID selection tools - template - bool selectionPIDpion(const T& candidate) + // Track QA of a pion; isPrimary selects the trkppion (first) or trkspion (second) histograms + template + void fillPionQA(const TrackType& track, bool isPrimary) { - if constexpr (!IsResoMicrotrack) { - bool tpcPIDPassed{false}, tofPIDPassed{false}; - if (std::abs(candidate.tpcNSigmaPi()) < cMaxTPCnSigmaPion) { - tpcPIDPassed = true; - } else { - return false; - } - if (candidate.hasTOF()) { - if (std::abs(candidate.tofNSigmaPi()) < cMaxTOFnSigmaPion) { - tofPIDPassed = true; + const bool hasTOF = track.hasTOF(); + if (isPrimary) { + if constexpr (Folder == QAFolder::Before) { + histos.fill(HIST("QA/trkppionTPCPID"), track.pt(), track.tpcNSigmaPi()); + if (hasTOF) { + histos.fill(HIST("QA/trkppionTOFPID"), track.pt(), track.tofNSigmaPi()); + histos.fill(HIST("QA/trkppionTPCTOFPID"), track.tpcNSigmaPi(), track.tofNSigmaPi()); } - if ((nsigmaCutCombinedPion > 0) && (candidate.tpcNSigmaPi() * candidate.tpcNSigmaPi() + candidate.tofNSigmaPi() * candidate.tofNSigmaPi() < nsigmaCutCombinedPion * nsigmaCutCombinedPion)) { - tofPIDPassed = true; + histos.fill(HIST("QA/trkppionpT"), track.pt()); + histos.fill(HIST("QA/trkppionDCAxy"), track.dcaXY()); + histos.fill(HIST("QA/trkppionDCAz"), track.dcaZ()); + } else if constexpr (Folder == QAFolder::After) { + histos.fill(HIST("QAcut/trkppionTPCPID"), track.pt(), track.tpcNSigmaPi()); + if (hasTOF) { + histos.fill(HIST("QAcut/trkppionTOFPID"), track.pt(), track.tofNSigmaPi()); + histos.fill(HIST("QAcut/trkppionTPCTOFPID"), track.tpcNSigmaPi(), track.tofNSigmaPi()); } + histos.fill(HIST("QAcut/trkppionpT"), track.pt()); + histos.fill(HIST("QAcut/trkppionDCAxy"), track.dcaXY()); + histos.fill(HIST("QAcut/trkppionDCAz"), track.dcaZ()); } else { - if (!cTOFVeto) { - return false; + histos.fill(HIST("QAMC/trkppionTPCPID"), track.pt(), track.tpcNSigmaPi()); + if (hasTOF) { + histos.fill(HIST("QAMC/trkppionTOFPID"), track.pt(), track.tofNSigmaPi()); + histos.fill(HIST("QAMC/trkppionTPCTOFPID"), track.tpcNSigmaPi(), track.tofNSigmaPi()); } - tofPIDPassed = true; - } - if (tpcPIDPassed && tofPIDPassed) { - return true; + histos.fill(HIST("QAMC/trkppionpT"), track.pt()); + histos.fill(HIST("QAMC/trkppionDCAxy"), track.dcaXY()); + histos.fill(HIST("QAMC/trkppionDCAz"), track.dcaZ()); } } else { - bool tpcPIDPassed{false}, tofPIDPassed{false}; - tpcPIDPassed = std::abs(o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(candidate.pidNSigmaPiFlag())) < cMaxTPCnSigmaPion + Epsilon; - tofPIDPassed = candidate.hasTOF() ? std::abs(o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(candidate.pidNSigmaPiFlag())) < cMaxTOFnSigmaPion + Epsilon : true; - if (tpcPIDPassed && tofPIDPassed) { - return true; + if constexpr (Folder == QAFolder::Before) { + histos.fill(HIST("QA/trkspionTPCPID"), track.pt(), track.tpcNSigmaPi()); + if (hasTOF) { + histos.fill(HIST("QA/trkspionTOFPID"), track.pt(), track.tofNSigmaPi()); + histos.fill(HIST("QA/trkspionTPCTOFPID"), track.tpcNSigmaPi(), track.tofNSigmaPi()); + } + histos.fill(HIST("QA/trkspionpT"), track.pt()); + histos.fill(HIST("QA/trkspionDCAxy"), track.dcaXY()); + histos.fill(HIST("QA/trkspionDCAz"), track.dcaZ()); + } else if constexpr (Folder == QAFolder::After) { + histos.fill(HIST("QAcut/trkspionTPCPID"), track.pt(), track.tpcNSigmaPi()); + if (hasTOF) { + histos.fill(HIST("QAcut/trkspionTOFPID"), track.pt(), track.tofNSigmaPi()); + histos.fill(HIST("QAcut/trkspionTPCTOFPID"), track.tpcNSigmaPi(), track.tofNSigmaPi()); + } + histos.fill(HIST("QAcut/trkspionpT"), track.pt()); + histos.fill(HIST("QAcut/trkspionDCAxy"), track.dcaXY()); + histos.fill(HIST("QAcut/trkspionDCAz"), track.dcaZ()); + } else { + histos.fill(HIST("QAMC/trkspionTPCPID"), track.pt(), track.tpcNSigmaPi()); + if (hasTOF) { + histos.fill(HIST("QAMC/trkspionTOFPID"), track.pt(), track.tofNSigmaPi()); + histos.fill(HIST("QAMC/trkspionTPCTOFPID"), track.tpcNSigmaPi(), track.tofNSigmaPi()); + } + histos.fill(HIST("QAMC/trkspionpT"), track.pt()); + histos.fill(HIST("QAMC/trkspionDCAxy"), track.dcaXY()); + histos.fill(HIST("QAMC/trkspionDCAz"), track.dcaZ()); } } - return false; } - // Kaon PID selection tools - template - bool selectionPIDkaon(const T& candidate) + // Track QA of the bachelor kaon + template + void fillKaonQA(const TrackType& track) { - if constexpr (!IsResoMicrotrack) { - bool tpcPIDPassed{false}, tofPIDPassed{false}; - if (std::abs(candidate.tpcNSigmaKa()) < cMaxTPCnSigmaKaon) { - tpcPIDPassed = true; - } else { - return false; + const bool hasTOF = track.hasTOF(); + if constexpr (Folder == QAFolder::Before) { + histos.fill(HIST("QA/trkkaonTPCPID"), track.pt(), track.tpcNSigmaKa()); + if (hasTOF) { + histos.fill(HIST("QA/trkkaonTOFPID"), track.pt(), track.tofNSigmaKa()); + histos.fill(HIST("QA/trkkaonTPCTOFPID"), track.tpcNSigmaKa(), track.tofNSigmaKa()); } - if (candidate.hasTOF()) { - if (std::abs(candidate.tofNSigmaKa()) < cMaxTOFnSigmaKaon) { - tofPIDPassed = true; - } - if ((nsigmaCutCombinedKaon > 0) && (candidate.tpcNSigmaKa() * candidate.tpcNSigmaKa() + candidate.tofNSigmaKa() * candidate.tofNSigmaKa() < nsigmaCutCombinedKaon * nsigmaCutCombinedKaon)) { - tofPIDPassed = true; - } - } else { - if (!cTOFVeto) { - return false; - } - tofPIDPassed = true; - } - if (tpcPIDPassed && tofPIDPassed) { - return true; + histos.fill(HIST("QA/trkkaonpT"), track.pt()); + histos.fill(HIST("QA/trkkaonDCAxy"), track.dcaXY()); + histos.fill(HIST("QA/trkkaonDCAz"), track.dcaZ()); + } else if constexpr (Folder == QAFolder::After) { + histos.fill(HIST("QAcut/trkkaonTPCPID"), track.pt(), track.tpcNSigmaKa()); + if (hasTOF) { + histos.fill(HIST("QAcut/trkkaonTOFPID"), track.pt(), track.tofNSigmaKa()); + histos.fill(HIST("QAcut/trkkaonTPCTOFPID"), track.tpcNSigmaKa(), track.tofNSigmaKa()); } + histos.fill(HIST("QAcut/trkkaonpT"), track.pt()); + histos.fill(HIST("QAcut/trkkaonDCAxy"), track.dcaXY()); + histos.fill(HIST("QAcut/trkkaonDCAz"), track.dcaZ()); } else { - bool tpcPIDPassed{false}, tofPIDPassed{false}; - tpcPIDPassed = std::abs(o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(candidate.pidNSigmaKaFlag())) < cMaxTPCnSigmaKaon + Epsilon; - tofPIDPassed = candidate.hasTOF() ? std::abs(o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(candidate.pidNSigmaKaFlag())) < cMaxTOFnSigmaKaon + Epsilon : true; - if (tpcPIDPassed && tofPIDPassed) { - return true; + histos.fill(HIST("QAMC/trkkaonTPCPID"), track.pt(), track.tpcNSigmaKa()); + if (hasTOF) { + histos.fill(HIST("QAMC/trkkaonTOFPID"), track.pt(), track.tofNSigmaKa()); + histos.fill(HIST("QAMC/trkkaonTPCTOFPID"), track.tpcNSigmaKa(), track.tofNSigmaKa()); } + histos.fill(HIST("QAMC/trkkaonpT"), track.pt()); + histos.fill(HIST("QAMC/trkkaonDCAxy"), track.dcaXY()); + histos.fill(HIST("QAMC/trkkaonDCAz"), track.dcaZ()); } - return false; } - template - bool isTrueK1(const T& trk1, const T& trk2, const T2& bTrack) - { - if (std::abs(trk1.pdgCode()) != kPiPlus || std::abs(trk2.pdgCode()) != kPiPlus) - return false; - if (std::abs(bTrack.pdgCode()) != kKPlus) - return false; - auto mother1 = trk1.motherId(); - auto mother2 = trk2.motherId(); - if (mother1 != mother2) - return false; - if (((std::abs(trk1.motherPDG()) && std::abs(trk2.motherPDG()) != kPDGRho770) && (std::abs(bTrack.motherPDG()) != kK1Plus)) || (std::abs(trk1.motherPDG()) && std::abs(bTrack.motherPDG()) != kK0Star892 && (std::abs(trk2.motherPDG()) != kK1Plus)) || (std::abs(trk2.motherPDG()) && std::abs(bTrack.motherPDG()) != kK0Star892 && (std::abs(trk1.motherPDG()) != kK1Plus))) - return false; - auto siblings = bTrack.siblingIds(); - if (siblings[0] != mother1 && siblings[1] != mother2) - return false; - return true; - } // isTrueK1 - - template - bool isTrueK892(const T& trk1, const T& trk2) - { - if (std::abs(trk1.pdgCode()) != kPiPlus || std::abs(trk2.pdgCode()) != kKPlus) - return false; - auto mother1 = trk1.motherId(); - auto mother2 = trk2.motherId(); - if (mother1 != mother2) - return false; - if (std::abs(trk1.motherPDG()) != kK0Star892) - return false; - return true; - } - - template - bool isTrueRho(const T& trk1, const T& trk2) - { - if (std::abs(trk1.pdgCode()) != kPiPlus || std::abs(trk2.pdgCode()) != kPiPlus) - return false; - auto mother1 = trk1.motherId(); - auto mother2 = trk2.motherId(); - if (mother1 != mother2) - return false; - if (std::abs(trk1.motherPDG()) != kPDGRho770) - return false; - return true; - } + // Histograms of the selected pion pairs and (pion, pion, kaon) candidates of one collision + // (or one mixed pair of collisions). The selection itself is the shared K1 core. + // dTracks1: bachelor kaons, dTracks2: pions. template - void fillHistograms(const CollisionType& collision, const TracksType& dTracks1, const TracksType& dTracks2) + void fillHistograms(const CollisionType& collision, const TracksType& dTracks1, const TracksType& dTracks2, + const K1ParentTruthMap* parentTruth = nullptr) { - auto multiplicity = collision.cent(); - TLorentzVector lDecayDaughter1, lDecayDaughter2, lResonanceSecondary, lDecayDaughter_bach, lResonanceK1; - for (const auto& [trk1, trk2] : combinations(CombinationsFullIndexPolicy(dTracks2, dTracks2))) { - // Full index policy is needed to consider all possible combinations - if (trk1.index() == trk2.index()) - continue; // We need to run (0,1), (1,0) pairs too. But the same id pairs are not needed. - // trk1: pion, trk2: pion, bTrack: kaon - if (!trackCut(trk1) || !trackCut(trk2)) - continue; + const bool fillQA = !IsMix && histogramOptions.additionalQAplots; + const auto multiplicity = collision.cent(); + + // Pion pair passing the pion selection; trk1 is the pion with the lower index + auto onPair = [&](auto const& trk1, auto const& trk2, ROOT::Math::PxPyPzMVector const& secondary, bool passesPairPt) { + if (fillQA) { + fillPionQA(trk1, true); + fillPionQA(trk2, false); + } + if (!passesPairPt) { + return; + } + if (fillQA) { + histos.fill(HIST("QA/hInvmassSecon"), secondary.M()); + } + if constexpr (IsMC) { + histos.fill(HIST("QAMC/hpT_Secondary"), secondary.Pt()); + } + }; - auto trk1pt = trk1.pt(); - auto trk2pt = trk2.pt(); - auto isTrk1hasTOF = trk1.hasTOF(); - auto isTrk2hasTOF = trk2.hasTOF(); - - if constexpr (!IsResoMicrotrack) { - auto trk1NSigmaPiTPC = trk1.tpcNSigmaPi(); - auto trk1NSigmaPiTOF = (isTrk1hasTOF) ? trk1.tofNSigmaPi() : -999.; - auto trk2NSigmaPiTPC = trk2.tpcNSigmaPi(); - auto trk2NSigmaPiTOF = (isTrk2hasTOF) ? trk2.tofNSigmaPi() : -999.; - - if (cUseOnlyTOFTrackPi && !isTrk1hasTOF) - continue; - if (!selectionPIDpion(trk1) || !selectionPIDpion(trk2)) - continue; - - if constexpr (!IsMix) { - - histos.fill(HIST("QA/trkppionTPCPID"), trk1pt, trk1NSigmaPiTPC); - if (isTrk1hasTOF) { - histos.fill(HIST("QA/trkppionTOFPID"), trk1pt, trk1NSigmaPiTOF); - histos.fill(HIST("QA/trkppionTPCTOFPID"), trk1NSigmaPiTPC, trk1NSigmaPiTOF); - } - histos.fill(HIST("QA/trkppionpT"), trk1pt); - histos.fill(HIST("QA/trkppionDCAxy"), trk1.dcaXY()); - histos.fill(HIST("QA/trkppionDCAz"), trk1.dcaZ()); - - histos.fill(HIST("QA/trkspionTPCPID"), trk2pt, trk2NSigmaPiTPC); - if (isTrk2hasTOF) { - histos.fill(HIST("QA/trkspionTOFPID"), trk2pt, trk2NSigmaPiTOF); - histos.fill(HIST("QA/trkspionTPCTOFPID"), trk2NSigmaPiTPC, trk2NSigmaPiTOF); - } - histos.fill(HIST("QA/trkspionpT"), trk2pt); - histos.fill(HIST("QA/trkspionDCAxy"), trk2.dcaXY()); - histos.fill(HIST("QA/trkspionDCAz"), trk2.dcaZ()); - } - } else { - histos.fill(HIST("QA/trkppionTPCPID"), trk1pt, o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(trk1.pidNSigmaPiFlag())); - if (isTrk1hasTOF) { - histos.fill(HIST("QA/trkppionTOFPID"), trk1pt, o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(trk1.pidNSigmaPiFlag())); - histos.fill(HIST("QA/trkppionTPCTOFPID"), o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(trk1.pidNSigmaPiFlag()), o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(trk1.pidNSigmaPiFlag())); - } - histos.fill(HIST("QA/trkppionpT"), trk1pt); - histos.fill(HIST("QA/trkppionDCAxy"), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAxy(trk1.trackSelectionFlags())); - histos.fill(HIST("QA/trkppionDCAz"), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAz(trk1.trackSelectionFlags())); - - histos.fill(HIST("QA/trkspionTPCPID"), trk2pt, o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(trk2.pidNSigmaPiFlag())); - if (isTrk2hasTOF) { - histos.fill(HIST("QA/trkspionTOFPID"), trk2pt, o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(trk2.pidNSigmaPiFlag())); - histos.fill(HIST("QA/trkspionTPCTOFPID"), o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(trk2.pidNSigmaPiFlag()), o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(trk2.pidNSigmaPiFlag())); - } - histos.fill(HIST("QA/trkspionpT"), trk2pt); - histos.fill(HIST("QA/trkspionDCAxy"), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAxy(trk2.trackSelectionFlags())); - histos.fill(HIST("QA/trkspionDCAz"), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAz(trk2.trackSelectionFlags())); + // Candidate passing the pion, pair and kaon selection; pion1 is the K*0 partner in the unlike-sign case + auto onCandidate = [&](auto const& kaon, auto const& pion1, auto const& pion2, K1CandidateValues const& c) { + if (fillQA) { + fillKaonQA(kaon); + } + if (!c.inRapidity) { + return; } - // Resonance reconstruction - lDecayDaughter1.SetXYZM(trk1.px(), trk1.py(), trk1.pz(), MassPionCharged); - lDecayDaughter2.SetXYZM(trk2.px(), trk2.py(), trk2.pz(), MassPionCharged); - lResonanceSecondary = lDecayDaughter1 + lDecayDaughter2; + // QA histogram before the candidate cuts + if (fillQA) { + histos.fill(HIST("QA/K1OA"), c.angle); + histos.fill(HIST("QA/K1PairAsym"), c.pairAsym); + histos.fill(HIST("QA/hInvmassK892_Rho"), c.mass13, c.secondary.M()); + histos.fill(HIST("QA/hInvmassSecon_PiKa"), c.secondary.M(), c.mass23); + histos.fill(HIST("QA/hpT_Secondary"), c.secondary.Pt()); + } + if (!c.passesCandidateCuts) { + return; + } - if (lResonanceSecondary.Pt() < cMinSecondaryPtCut) - continue; + // QA histograms after the candidate cuts + if (fillQA) { + fillPionQA(pion1, true); + fillPionQA(pion2, false); + fillKaonQA(kaon); + histos.fill(HIST("QAcut/K1OA"), c.angle); + histos.fill(HIST("QAcut/K1PairAsym"), c.pairAsym); + histos.fill(HIST("QAcut/hInvmassK892_Rho"), c.mass13, c.secondary.M()); + histos.fill(HIST("QAcut/hInvmassSecon_PiKa"), c.secondary.M(), c.mass23); + histos.fill(HIST("QAcut/hInvmassSecon"), c.secondary.M()); + histos.fill(HIST("QAcut/hpT_Secondary"), c.secondary.Pt()); + } - if constexpr (!IsMix) { - histos.fill(HIST("QA/hInvmassSecon"), lResonanceSecondary.M()); + const unsigned int typeNormal = BinAnti::kNormal; + if constexpr (IsMix) { + const unsigned int typeK1 = kaon.sign() > 0 ? BinType::kK1P_Mix : BinType::kK1N_Mix; + histos.fill(HIST("hInvmass_K1_Mix"), typeNormal, typeK1, multiplicity, c.k1.Pt(), c.k1.M()); + histos.fill(HIST("k1invmass_Mix"), c.k1.M()); + return; } - if constexpr (IsMC) { - /* - if (isTrueK892(trk1, trk2)) - histos.fill(HIST("QAMC/hpT_Secondary"), lResonanceSecondary.Pt()); - } else { - if (isTrueRho(trk1, trk2)) - histos.fill(HIST("QAMC/hpT_Secondary"), lResonanceSecondary.Pt()); - } - */ - histos.fill(HIST("QAMC/hpT_Secondary"), lResonanceSecondary.Pt()); + unsigned int typeK1 = kaon.sign() > 0 ? BinType::kK1P : BinType::kK1N; + if (c.isUnlikeSign) { + histos.fill(HIST("k1invmass"), c.k1.M()); + histos.fill(HIST("hInvmass_K1"), typeNormal, typeK1, multiplicity, c.k1.Pt(), c.k1.M()); + } else { + histos.fill(HIST("k1invmass_LS"), c.k1.M()); + histos.fill(HIST("hInvmass_K1_LS"), typeNormal, typeK1, multiplicity, c.k1.Pt(), c.k1.M()); } - // Mass Window cut is removed - - for (const auto& bTrack : dTracks1) { - if (bTrack.index() == trk1.index() || bTrack.index() == trk2.index()) - continue; - if (!trackCut(bTrack)) - continue; - if (!selectionPIDkaon(bTrack)) - continue; - - // K1 reconstruction - lDecayDaughter_bach.SetXYZM(bTrack.px(), bTrack.py(), bTrack.pz(), MassKaonCharged); - lResonanceK1 = lResonanceSecondary + lDecayDaughter_bach; - - // Cuts - if (lResonanceK1.Rapidity() > cK1MaxRap || lResonanceK1.Rapidity() < cK1MinRap) - continue; - - auto lK1Angle = lResonanceSecondary.Angle(lDecayDaughter_bach.Vect()); - auto lPairAsym = (lResonanceSecondary.E() - lDecayDaughter_bach.E()) / (lResonanceSecondary.E() + lDecayDaughter_bach.E()); - - TLorentzVector temp13 = lDecayDaughter1 + lDecayDaughter_bach; - TLorentzVector temp23 = lDecayDaughter2 + lDecayDaughter_bach; - - // QA histogram - if constexpr (!IsMix) { - histos.fill(HIST("QA/K1OA"), lK1Angle); - histos.fill(HIST("QA/K1PairAsym"), lPairAsym); - histos.fill(HIST("QA/hInvmassK892_Rho"), temp13.M(), lResonanceSecondary.M()); - histos.fill(HIST("QA/hInvmassSecon_PiKa"), lResonanceSecondary.M(), temp23.M()); - histos.fill(HIST("QA/hpT_Secondary"), lResonanceSecondary.Pt()); + + if constexpr (IsMC) { + const auto channel = classifyK1Truth(pion1, pion2, kaon); + const int channelBin = static_cast(channel); + histos.fill(HIST("MCReco/channel"), channelBin); + histos.fill(HIST("MCReco/mass"), channelBin, c.k1.M()); + histos.fill(HIST("MCReco/pt"), channelBin, c.k1.Pt()); + histos.fill(HIST("MCReco/piPiMass"), channelBin, c.secondary.M()); + histos.fill(HIST("MCReco/pi1KMass"), channelBin, c.mass13); + histos.fill(HIST("MCReco/pi2KMass"), channelBin, c.mass23); + if (channel == K1TruthChannel::None) { + histos.fill(HIST("k1invmass_MC_noK1"), c.k1.M()); + return; } - // Selection cuts are removed - // QA histograms after the cuts are removed as no cuts are applied - - if constexpr (!IsMix) { - unsigned int typeK1 = bTrack.sign() > 0 ? BinType::kK1P : BinType::kK1N; - unsigned int typeNormal = BinAnti::kNormal; - if (trk1.sign() * trk2.sign() < 0) { - histos.fill(HIST("k1invmass"), lResonanceK1.M()); - histos.fill(HIST("hInvmass_K1"), typeNormal, typeK1, multiplicity, lResonanceK1.Pt(), lResonanceK1.M()); - } else { - histos.fill(HIST("k1invmass_LS"), lResonanceK1.M()); - histos.fill(HIST("hInvmass_K1_LS"), typeNormal, typeK1, multiplicity, lResonanceK1.Pt(), lResonanceK1.M()); - } - - if constexpr (IsMC) { - if (isTrueK1(trk1, trk2, bTrack)) { - typeK1 = bTrack.sign() > 0 ? BinType::kK1P_Rec : BinType::kK1N_Rec; - histos.fill(HIST("hInvmass_K1"), typeNormal, typeK1, multiplicity, lResonanceK1.Pt(), lResonanceK1.M()); - histos.fill(HIST("k1invmass_MC"), lResonanceK1.M()); - histos.fill(HIST("QAMC/K1OA"), lK1Angle); - histos.fill(HIST("QAMC/K1PairAsym"), lPairAsym); - histos.fill(HIST("QAMC/hInvmassK892_Rho"), temp13.M(), lResonanceSecondary.M()); - histos.fill(HIST("QAMC/hInvmassSecon_PiKa"), lResonanceSecondary.M(), temp23.M()); - histos.fill(HIST("QAMC/hInvmassSecon"), lResonanceSecondary.M()); - histos.fill(HIST("QAMC/hpT_Seocondary"), lResonanceSecondary.Pt()); - - if constexpr (!IsResoMicrotrack) { - - auto trk1NSigmaPiTPC = trk1.tpcNSigmaPi(); - auto trk1NSigmaPiTOF = (isTrk1hasTOF) ? trk1.tofNSigmaPi() : -999.; - auto trk2NSigmaPiTPC = trk2.tpcNSigmaPi(); - auto trk2NSigmaPiTOF = (isTrk2hasTOF) ? trk2.tofNSigmaPi() : -999.; - - // PID QA primary pion - histos.fill(HIST("QAMC/trkppionTPCPID"), trk1pt, trk1NSigmaPiTPC); - if (isTrk1hasTOF) { - histos.fill(HIST("QAMC/trkppionTOFPID"), trk1pt, trk1NSigmaPiTOF); - histos.fill(HIST("QAMC/trkppionTPCTOFPID"), trk1NSigmaPiTPC, trk1NSigmaPiTOF); - } - histos.fill(HIST("QAMC/trkppionpT"), trk1pt); - histos.fill(HIST("QAMC/trkppionDCAxy"), trk1.dcaXY()); - histos.fill(HIST("QAMC/trkppionDCAz"), trk1.dcaZ()); - - // PID QA secondary pion - histos.fill(HIST("QAMC/trkspionTPCPID"), trk2pt, trk2NSigmaPiTPC); - if (isTrk2hasTOF) { - histos.fill(HIST("QAMC/trkspionTOFPID"), trk2pt, trk2NSigmaPiTOF); - histos.fill(HIST("QAMC/trkspionTPCTOFPID"), trk2NSigmaPiTPC, trk2NSigmaPiTOF); - } - histos.fill(HIST("QAMC/trkspionpT"), trk2pt); - histos.fill(HIST("QAMC/trkspionDCAxy"), trk2.dcaXY()); - histos.fill(HIST("QAMC/trkspionDCAz"), trk2.dcaZ()); - - } else { - - histos.fill(HIST("QAMC/trkppionTPCPID"), trk1pt, o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(trk1.pidNSigmaSelectionFlags())); - if (isTrk1hasTOF) { - histos.fill(HIST("QAMC/trkppionTOFPID"), trk1pt, o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(trk1.pidNSigmaSelectionFlags())); - histos.fill(HIST("QAMC/trkppionTPCTOFPID"), o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(trk1.pidNSigmaSelectionFlags()), o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(trk1.pidNSigmaSelectionFlags())); - } - histos.fill(HIST("QAMC/trkppionpT"), trk1pt); - histos.fill(HIST("QAMC/trkppionDCAxy"), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAxy(trk1.trackSelectionFlags())); - histos.fill(HIST("QAMC/trkppionDCAz"), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAz(trk1.trackSelectionFlags())); - - // PID QA secondary pion - histos.fill(HIST("QAMC/trkspionTPCPID"), trk2pt, o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(trk2.pidNSigmaSelectionFlags())); - if (isTrk2hasTOF) { - histos.fill(HIST("QAMC/trkspionTOFPID"), trk2pt, o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(trk2.pidNSigmaSelectionFlags())); - histos.fill(HIST("QAMC/trkspionTPCTOFPID"), o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(trk2.pidNSigmaSelectionFlags()), o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(trk2.pidNSigmaSelectionFlags())); - } - histos.fill(HIST("QAMC/trkspionpT"), trk2pt); - histos.fill(HIST("QAMC/trkspionDCAxy"), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAxy(trk2.trackSelectionFlags())); - histos.fill(HIST("QAMC/trkspionDCAz"), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAz(trk2.trackSelectionFlags())); - } - } else { - histos.fill(HIST("k1invmass_MC_noK1"), lResonanceK1.M()); - } - } // IsMC - } else { - unsigned int typeK1 = bTrack.sign() > 0 ? BinType::kK1P_Mix : BinType::kK1N_Mix; - unsigned int typeNormal = BinAnti::kNormal; - histos.fill(HIST("hInvmass_K1_Mix"), typeNormal, typeK1, multiplicity, lResonanceK1.Pt(), lResonanceK1.M()); - histos.fill(HIST("k1invmass_Mix"), lResonanceK1.M()); + if (truthDebugCounts[channelBin] < histogramOptions.cfgTruthDebug) { + ++truthDebugCounts[channelBin]; + LOGP(info, "K1Truth channel={} collision={} tracks=({},{},{}) pdg=({},{},{}) mothers=({},{},{}) motherPDG=({},{},{}) siblings=(({},{}),({},{}),({},{}))", + channelBin, collision.globalIndex(), + pion1.globalIndex(), pion2.globalIndex(), kaon.globalIndex(), + pion1.pdgCode(), pion2.pdgCode(), kaon.pdgCode(), pion1.motherId(), pion2.motherId(), kaon.motherId(), + pion1.motherPDG(), pion2.motherPDG(), kaon.motherPDG(), + pion1.siblingIds()[0], pion1.siblingIds()[1], pion2.siblingIds()[0], pion2.siblingIds()[1], kaon.siblingIds()[0], kaon.siblingIds()[1]); } - } // bTrack - } - } // fillHistograms + const int motherId = k1MotherId(channel, kaon, pion1, pion2); + const int charge = kaon.pdgCode() > 0 ? 1 : -1; + const auto* parent = parentTruth ? findK1ParentTruth(*parentTruth, motherId, channel, charge) : nullptr; + const bool truthRapidity = c.isUnlikeSign && parent && (!histogramOptions.cUseTruthRapidity || (parent->y >= candidateCuts.cK1MinRap && parent->y <= candidateCuts.cK1MaxRap)); + if (truthRapidity) { + typeK1 = kaon.sign() > 0 ? BinType::kK1P_Rec : BinType::kK1N_Rec; + histos.fill(HIST("hInvmass_K1"), typeNormal, typeK1, multiplicity, c.k1.Pt(), c.k1.M()); + histos.fill(HIST("k1invmass_MC"), c.k1.M()); + const int parentCharge = parent->pdgCode > 0 ? 1 : -1; + histos.fill(HIST("MCReco/response"), c.k1.Pt(), parent->pt, channelBin, parentCharge, multiplicity); + } + histos.fill(HIST("QAMC/K1OA"), c.angle); + histos.fill(HIST("QAMC/K1PairAsym"), c.pairAsym); + histos.fill(HIST("QAMC/hInvmassK892_Rho"), c.mass13, c.secondary.M()); + histos.fill(HIST("QAMC/hInvmassSecon_PiKa"), c.secondary.M(), c.mass23); + histos.fill(HIST("QAMC/hInvmassSecon"), c.secondary.M()); + histos.fill(HIST("QAMC/hpT_Secondary"), c.secondary.Pt()); + + // PID QA primary and secondary pion + fillPionQA(pion1, true); + fillPionQA(pion2, false); + fillKaonQA(kaon); + } + }; - void processResoTracks(aod::ResoCollision const& collision, - aod::ResoTracks const& resotracks) + core.forEachCandidate(histos, collision, dTracks1, dTracks2, IsMC || fillQA, onPair, onCandidate); + } + + void processResoTracks(ResoCollisions::iterator const& collision, + ResoTracks const& resotracks) { + if (!core.passesEventCuts(collision)) { + return; + } fillHistograms(collision, resotracks, resotracks); } PROCESS_SWITCH(K1AnalysisMicro, processResoTracks, "Process ResoTracks", false); - void processResoMicroTracks(aod::ResoCollision const& collision, - aod::ResoMicroTracks const& resomicrotracks) + void processResoMicroTracks(ResoCollisions::iterator const& collision, + ResoMicroTracks const& resomicrotracks) { + if (!core.passesEventCuts(collision)) { + return; + } fillHistograms(collision, resomicrotracks, resomicrotracks); } PROCESS_SWITCH(K1AnalysisMicro, processResoMicroTracks, "Process ResoMicroTracks", true); - void processMC(aod::ResoCollision const& collision, - soa::Join const& resotracks) + void processMC(ResoMCCols::iterator const& collision, + ResoMCTracks const& resotracks, ResoMCParents const& resoParents) { - fillHistograms(collision, resotracks, resotracks); + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; + } + histos.fill(HIST("MCReco/collisions"), 0.5); + const auto parentTruth = makeK1ParentTruthMap(resoParents); + fillHistograms(collision, resotracks, resotracks, &parentTruth); } PROCESS_SWITCH(K1AnalysisMicro, processMC, "Process Event for MC", false); - void processMCTrue(ResoMCCols::iterator const& collision, aod::ResoMCParents const& resoParents) + void processMCMicro(ResoMCCols::iterator const& collision, ResoMCMicroTracks const& tracks, ResoMCParents const& resoParents) { - auto multiplicity = collision.cent(); - for (const auto& part : resoParents) { - if (std::abs(part.pdgCode()) != kK1Plus) - continue; - if (std::abs(part.y()) > 0.5) { - continue; - } - bool pass1 = false; - bool pass2 = false; - bool pass3 = false; - bool pass4 = false; - if (std::abs(part.daughterPDG1()) == 313 || std::abs(part.daughterPDG2()) == 313) { // At least one decay into K892 - pass2 = true; - } - if (std::abs(part.daughterPDG1()) == kPiPlus || std::abs(part.daughterPDG2()) == kPiPlus) { // At lest one decay into pion - pass1 = true; - } - if (std::abs(part.daughterPDG1()) == kPDGRho770 || std::abs(part.daughterPDG2()) == kPDGRho770) { - pass4 = true; - } - if (std::abs(part.daughterPDG1()) == kKPlus || std::abs(part.daughterPDG2()) == kKPlus) { - pass3 = true; - } - if (!pass1 || !pass2 || !pass3 || !pass4) // If we have both decay products - continue; - auto typeNormal = part.pdgCode() > 0 ? BinAnti::kNormal : BinAnti::kAnti; - if (collision.isVtxIn10()) // INEL>10 - { - auto typeK1 = part.pdgCode() > 0 ? BinType::kK1P_GenINEL10 : BinType::kK1N_GenINEL10; - histos.fill(HIST("hInvmass_K1"), typeNormal, typeK1, multiplicity, part.pt(), 1); - } - if (collision.isVtxIn10() && collision.isInSel8()) // INEL>10, vtx10 - { - auto typeK1 = part.pdgCode() > 0 ? BinType::kK1P_GenINELgt10 : BinType::kK1N_GenINELgt10; - histos.fill(HIST("hInvmass_K1"), typeNormal, typeK1, multiplicity, part.pt(), 1); - } - if (collision.isVtxIn10() && collision.isTriggerTVX()) // vtx10, TriggerTVX - { - auto typeK1 = part.pdgCode() > 0 ? BinType::kK1P_GenTrig10 : BinType::kK1N_GenTrig10; - histos.fill(HIST("hInvmass_K1"), typeNormal, typeK1, multiplicity, part.pt(), 1); - } - if (collision.isInAfterAllCuts()) // after all event selection - { - auto typeK1 = part.pdgCode() > 0 ? BinType::kK1P_GenEvtSel : BinType::kK1N_GenEvtSel; - histos.fill(HIST("hInvmass_K1"), typeNormal, typeK1, multiplicity, part.pt(), 1); - } + // The modular producer already selected these reconstructed collisions. + // Apply precisely the same reconstruction loop as the data baseline. + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; } + histos.fill(HIST("MCReco/collisions"), 0.5); + histos.fill(HIST("MCReco/microTracks"), 0.5, tracks.size()); + const auto parentTruth = makeK1ParentTruthMap(resoParents); + fillHistograms(collision, tracks, tracks, &parentTruth); } - PROCESS_SWITCH(K1AnalysisMicro, processMCTrue, "Process Event for MC", false); + PROCESS_SWITCH(K1AnalysisMicro, processMCMicro, "Process reconstructed MC with micro v001 tables", false); + + void processMCTrue(ResoMCCols::iterator const& collision, ResoMCParents const& resoParents) + { + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; + } + // Keep other/unresolved immediate decays too; never require both pairs. + core.forEachGeneratedK1(histos, resoParents, [&](auto const& part, K1TruthChannel channel) { + const int charge = part.pdgCode() > 0 ? 1 : -1; + histos.fill(HIST("MCGen/chargeChannel"), charge, static_cast(channel)); + histos.fill(HIST("MCGen/ptChannel"), static_cast(channel), part.pt()); + if (channel != K1TruthChannel::None) { + histos.fill(HIST("MCGen/response"), part.pt(), static_cast(channel), charge, collision.cent()); + } + }); + } + PROCESS_SWITCH(K1AnalysisMicro, processMCTrue, "Process generated K1 in selected events with v001 parents", false); // Processing Event Mixing using BinningTypeVtxZT0M = ColumnBinningPolicy; - void processME(o2::aod::ResoCollisions const& collisions, aod::ResoTracks const& resotracks) + void processME(ResoCollisions const& collisions, ResoTracks const& resotracks) { auto tracksTuple = std::make_tuple(resotracks); - BinningTypeVtxZT0M colBinning{{cfgVtxBins, cfgMultBins}, true}; - SameKindPair pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + BinningTypeVtxZT0M colBinning{{cfgVtxBins, cfgMultBins}}; + SameKindPair pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { + if (!core.passesEventCuts(collision1) || !core.passesEventCuts(collision2)) { + continue; + } fillHistograms(collision1, tracks1, tracks2); } }; PROCESS_SWITCH(K1AnalysisMicro, processME, "Process EventMixing light without partition", false); // Processing Event Mixing -- Micro - // using BinningTypeVtxZT0M = ColumnBinningPolicy; - void processMEMicro(o2::aod::ResoCollisions const& collisions, aod::ResoMicroTracks const& resomicrotracks) + void processMEMicro(ResoCollisions const& collisions, ResoMicroTracks const& resomicrotracks) { auto tracksTuple = std::make_tuple(resomicrotracks); - BinningTypeVtxZT0M colBinning{{cfgVtxBins, cfgMultBins}, true}; - SameKindPair pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + BinningTypeVtxZT0M colBinning{{cfgVtxBins, cfgMultBins}}; + SameKindPair pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { + if (!core.passesEventCuts(collision1) || !core.passesEventCuts(collision2)) { + continue; + } fillHistograms(collision1, tracks1, tracks2); } }; - PROCESS_SWITCH(K1AnalysisMicro, processMEMicro, "Process EventMixing light without partition", true); -}; // struct + PROCESS_SWITCH(K1AnalysisMicro, processMEMicro, "Process EventMixing light without partition", false); +}; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { diff --git a/PWGLF/Tasks/Resonances/k1TrainingTable.cxx b/PWGLF/Tasks/Resonances/k1TrainingTable.cxx new file mode 100644 index 00000000000..008984a0112 --- /dev/null +++ b/PWGLF/Tasks/Resonances/k1TrainingTable.cxx @@ -0,0 +1,285 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file k1TrainingTable.cxx +/// \brief Derived K1 training table workflow +/// \author Bong-Hwi Lim +/// + +#include "PWGLF/Core/K1AnalysisMicroCore.h" +#include "PWGLF/Core/K1MlFeatures.h" +#include "PWGLF/Core/ResoAnalysisSelectionCore.h" +#include "PWGLF/DataModel/LFK1MlTables.h" +#include "PWGLF/DataModel/LFResonanceTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::constants::physics; +using namespace o2::analysis::resonance; +using namespace o2::analysis::k1micro; + +static_assert(std::extent_v == o2::analysis::k1ml::NMasterFeatures, + "K1MasterFeatures column size must match the K1 ML feature contract"); + +/// Writes the unlike-sign K1 micro candidates of the shared K1 selection, with the +/// canonical (kaon, same-sign pion, opposite-sign pion) tracks and the master features. +struct K1TrainingTable { + using ResoCollisions = aod::ResoCollisions_001; + using ResoMCCols = soa::Join; + using ResoMicroTracks = aod::ResoMicroTracks_001; + using ResoMCMicroTracks = soa::Join; + using ResoMCParents = aod::ResoMCParents_001; + + // FNV-1a over the bit patterns of the master features, for parity logs + static constexpr uint64_t FnvOffsetBasis = 14695981039346656037ULL; + static constexpr uint64_t FnvPrime = 1099511628211ULL; + static constexpr unsigned int BitsPerByte = 8; + static constexpr unsigned int BitsPerFloat = 32; + static constexpr uint32_t ByteMask = 0xffU; + + Produces k1MlEvents; + Produces k1MlTracks; + Produces k1MlCandidates; + Produces k1MlInputs; + Produces k1MlTruth; + Produces k1MlGenAudit; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // Selection shared with the K1 histogram task (plain JSON keys, no group prefix) + EventCuts eventCuts; + TrackCuts trackCuts; + PionPidCuts pionPID; + KaonPidCuts kaonPID; + SecondaryCuts secondaryCuts; + CandidateCuts candidateCuts; + + Configurable k1MlExportStage{"k1MlExportStage", "loose", "Candidate export stage: loose (pass bits >= 1) or selected (pass bits = 31)"}; + Configurable k1MlLooseAudit{"k1MlLooseAudit", true, "Record loose US cutflow and mass/pT/activity spectrum"}; + Configurable k1MlParityRows{"k1MlParityRows", 0, "Log feature hashes of the first N exported candidates"}; + + K1AnalysisMicroCore core; + int64_t k1MlEventRow = -1; + std::unordered_map k1MlTrackRows; + int k1MlParityLogged = 0; + + void init(InitContext&) + { + if (static_cast(doprocessResoMicroTracks) + static_cast(doprocessMCMicro) != 1 || + (doprocessMCTrue && !doprocessMCMicro)) { + LOG(fatal) << "K1 training table requires exactly one of processResoMicroTracks and processMCMicro; processMCTrue requires processMCMicro"; + } + if (k1MlParityRows < 0) { + LOG(fatal) << "k1MlParityRows must not be negative"; + } + if (k1MlExportStage.value != "loose" && k1MlExportStage.value != "selected") { + LOG(fatal) << "k1MlExportStage must be loose or selected"; + } + + ProcessModes modes; + modes.microTracks = true; + modes.mcReco = doprocessMCMicro; + modes.mcRecoMicro = doprocessMCMicro; + modes.mcGen = doprocessMCTrue; + LooseStageOptions looseOptions; + looseOptions.audit = k1MlLooseAudit; + looseOptions.exportSelected = k1MlExportStage.value == "selected"; + core.init(histos, eventCuts, trackCuts, pionPID, kaonPID, secondaryCuts, candidateCuts, modes, looseOptions); + if (doprocessMCMicro) { + histos.add("MCReco/collisions", "Selected reconstructed MC collisions", HistType::kTH1D, {{1, 0, 1}}); + histos.add("MCReco/microTracks", "Input micro tracks in selected MC collisions", HistType::kTH1D, {{1, 0, 1}}); + } + + // Candidates that violate the canonical or feature contract are skipped, never written. + auto skipped = histos.add("ML/exportSkipped", "Skipped candidates;K1 ML build status;candidates", HistType::kTH1D, {{6, -0.5, 5.5}}); + const std::array statusLabels{"Ok", "InvalidChargePattern", "ReusedTrack", "InvalidMomentum", "InvalidKinematics", "InvalidContract"}; + for (std::size_t i = 0; i < statusLabels.size(); ++i) { + skipped->GetXaxis()->SetBinLabel(i + 1, statusLabels[i]); + } + + LOG(info) << "Size of the histograms in K1 training table task"; + histos.print(); + } + + template + int64_t writeK1MlTrack(Track const& track) + { + const auto id = static_cast(track.globalIndex()); + if (auto it = k1MlTrackRows.find(id); it != k1MlTrackRows.end()) { + return it->second; + } + k1MlTracks(k1MlEventRow, static_cast(track.trackId()), track.px(), track.py(), track.pz(), + track.pidNSigmaPiFlag(), track.pidNSigmaKaFlag(), track.pidNSigmaPrFlag(), + track.trackSelectionFlags(), track.trackFlags(), track.tpcNClsCrossedRows(), track.itsClusterMap()); + const int64_t row = k1MlTracks.lastIndex(); + k1MlTrackRows.emplace(id, row); + return row; + } + + template + void writeK1MlEvent(Collision const& collision) + { + k1MlTrackRows.clear(); + // ResoCollisions_001 carries no run number or BC; the reduced collision row identifies the event within its DF. + k1MlEvents(collision.globalIndex(), + collision.posZ(), collision.bMagField(), collision.cent(), collision.multiplicity(), collision.isRecINELgt0()); + k1MlEventRow = k1MlEvents.lastIndex(); + } + + template + void logParity(Collision const& collision, o2::analysis::k1ml::CandidateSnapshot const& candidate, o2::analysis::k1ml::FeaturePack const& pack) + { + if (k1MlParityLogged >= k1MlParityRows) { + return; + } + uint64_t hash = FnvOffsetBasis; + for (const auto& value : pack.master) { + const auto bits = std::bit_cast(value); + for (unsigned int shift = 0; shift < BitsPerFloat; shift += BitsPerByte) { + hash = (hash ^ ((bits >> shift) & ByteMask)) * FnvPrime; + } + } + LOGP(info, "K1MLPARITY collision={} tracks={},{},{} featureHash={}", + collision.globalIndex(), candidate.tracks[0].sourceTrackId, candidate.tracks[1].sourceTrackId, + candidate.tracks[2].sourceTrackId, hash); + ++k1MlParityLogged; + } + + template + void writeK1MlCandidate(Collision const& collision, Kaon const& kaon, Pion const& samePion, Pion const& oppPion, + K1TruthChannel channel, uint16_t passBits, const K1ParentTruthMap* parentTruth = nullptr) + { + using o2::analysis::k1ml::BuildStatus; + const auto canonical = o2::analysis::k1ml::canonicalizeUS(o2::analysis::k1ml::makeTrackSnapshot(kaon), o2::analysis::k1ml::makeTrackSnapshot(samePion), o2::analysis::k1ml::makeTrackSnapshot(oppPion)); + if (canonical.status != BuildStatus::Ok) { + histos.fill(HIST("ML/exportSkipped"), static_cast(canonical.status)); + return; + } + const auto pack = o2::analysis::k1ml::buildMasterFeatures(canonical.candidate); + if (pack.status != BuildStatus::Ok) { + histos.fill(HIST("ML/exportSkipped"), static_cast(pack.status)); + return; + } + logParity(collision, canonical.candidate, pack); + const auto kaonRow = writeK1MlTrack(kaon); + const auto sameRow = writeK1MlTrack(samePion); + const auto oppRow = writeK1MlTrack(oppPion); + ROOT::Math::PxPyPzMVector k{kaon.px(), kaon.py(), kaon.pz(), MassKaonCharged}; + ROOT::Math::PxPyPzMVector s{samePion.px(), samePion.py(), samePion.pz(), MassPionCharged}; + ROOT::Math::PxPyPzMVector o{oppPion.px(), oppPion.py(), oppPion.pz(), MassPionCharged}; + const auto mother = k + s + o; + k1MlCandidates(k1MlEventRow, kaonRow, sameRow, oppRow, + static_cast(mother.M()), static_cast((s + o).M()), + static_cast((k + s).M()), static_cast((k + o).M()), + pack.kinematics.scalarSumPt, static_cast((s + o).Pt()), + static_cast(mother.Pt()), static_cast(mother.Rapidity()), + static_cast(mother.Eta()), static_cast(mother.Phi()), + static_cast(kaon.sign()), passBits); + const int64_t row = k1MlCandidates.lastIndex(); + k1MlInputs(row, pack.master.data(), static_cast(pack.status)); + if constexpr (IsMC) { + const bool matched = channel != K1TruthChannel::None; + int motherId = -1; + const K1ParentTruth* parent = nullptr; + if (matched) { + motherId = k1MotherId(channel, kaon, samePion, oppPion); + const int charge = kaon.pdgCode() > 0 ? 1 : -1; + parent = parentTruth ? findK1ParentTruth(*parentTruth, motherId, channel, charge) : nullptr; + } + // Immediate-mother information alone does not prove a physical UID. + k1MlTruth(row, matched ? uint8_t{1} : uint8_t{2}, static_cast(channel), + parent ? parent->pdgCode : (matched ? kaon.sign() * Pdg::kK1_1270Plus : 0), static_cast(motherId), + parent ? static_cast(parent->pt) : std::numeric_limits::quiet_NaN(), + parent ? static_cast(parent->y) : std::numeric_limits::quiet_NaN()); + } else { + k1MlTruth(row, uint8_t{0}, uint8_t{0}, 0, int64_t{-1}, + std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()); + } + } + + void processResoMicroTracks(ResoCollisions::iterator const& collision, ResoMicroTracks const& tracks) + { + if (!core.passesEventCuts(collision)) { + return; + } + writeK1MlEvent(collision); + // Selection only: the K1 analysis histograms belong to the K1 analysis task + core.forEachCandidate(histos, collision, tracks, tracks, false, nullptr, nullptr, + [this](auto const& coll, auto const& kaon, auto const& samePion, auto const& oppPion, + K1TruthChannel channel, uint16_t passBits) { + writeK1MlCandidate(coll, kaon, samePion, oppPion, channel, passBits); + }); + } + PROCESS_SWITCH(K1TrainingTable, processResoMicroTracks, "Write K1 candidates from data micro v001 tables", true); + + void processMCMicro(ResoMCCols::iterator const& collision, ResoMCMicroTracks const& tracks, ResoMCParents const& resoParents) + { + // The modular producer already selected these reconstructed collisions. + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; + } + histos.fill(HIST("MCReco/collisions"), 0.5); + histos.fill(HIST("MCReco/microTracks"), 0.5, tracks.size()); + writeK1MlEvent(collision); + const auto parentTruth = makeK1ParentTruthMap(resoParents); + core.forEachCandidate(histos, collision, tracks, tracks, false, nullptr, nullptr, + [this, &parentTruth](auto const& coll, auto const& kaon, auto const& samePion, auto const& oppPion, + K1TruthChannel channel, uint16_t passBits) { + writeK1MlCandidate(coll, kaon, samePion, oppPion, channel, passBits, &parentTruth); + }); + } + PROCESS_SWITCH(K1TrainingTable, processMCMicro, "Write K1 candidates with truth from reconstructed MC micro v001 tables", false); + + void processMCTrue(ResoMCCols::iterator const& collision, ResoMCParents const& resoParents) + { + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; + } + core.forEachGeneratedK1(histos, resoParents, [&](auto const& part, K1TruthChannel channel) { + k1MlGenAudit(collision.globalIndex(), static_cast(part.originalMcParticleId()), + part.pdgCode(), part.daughterPDG1(), part.daughterPDG2(), static_cast(channel), + part.pt(), part.y(), true); + }); + } + PROCESS_SWITCH(K1TrainingTable, processMCTrue, "Write generated K1 parents of selected reconstructed MC events", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/Tasks/Resonances/k892SpherocityAnalysis.cxx b/PWGLF/Tasks/Resonances/k892SpherocityAnalysis.cxx index 5508bb7cf9a..5af74594dc6 100644 --- a/PWGLF/Tasks/Resonances/k892SpherocityAnalysis.cxx +++ b/PWGLF/Tasks/Resonances/k892SpherocityAnalysis.cxx @@ -511,8 +511,8 @@ struct k892Analysis { { LOGF(debug, "Event Mixing Started"); - BinningType1 binningPositions1{{cMixVtxBins, cMixMultBins, cMixSphBins}, true}; - BinningType2 binningPositions2{{cMixVtxBins, cMixMultBins}, true}; + BinningType1 binningPositions1{{cMixVtxBins, cMixMultBins, cMixSphBins}}; + BinningType2 binningPositions2{{cMixVtxBins, cMixMultBins}}; auto tracksTuple = std::make_tuple(tracks); if (cMixSph) { SameKindPair pairs{binningPositions1, cNumMixEv, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip diff --git a/PWGLF/Tasks/Resonances/k892analysis.cxx b/PWGLF/Tasks/Resonances/k892analysis.cxx index ee26e771a1e..f80fbb3b5d7 100644 --- a/PWGLF/Tasks/Resonances/k892analysis.cxx +++ b/PWGLF/Tasks/Resonances/k892analysis.cxx @@ -862,7 +862,7 @@ struct K892analysis { void processMELight(o2::aod::ResoCollisions const& collisions, aod::ResoTracks const& resotracks) { auto tracksTuple = std::make_tuple(resotracks); - BinningTypeVtxZT0M colBinning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{cfgVtxBins, cfgMultBins}}; SameKindPair pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { diff --git a/PWGLF/Tasks/Resonances/k892analysispbpb.cxx b/PWGLF/Tasks/Resonances/k892analysispbpb.cxx index 05d1d4e0c30..a49fd403148 100644 --- a/PWGLF/Tasks/Resonances/k892analysispbpb.cxx +++ b/PWGLF/Tasks/Resonances/k892analysispbpb.cxx @@ -982,7 +982,7 @@ struct K892analysispbpb { void processMixedEvent(EventCandidates const& collisions, TrackCandidates const& tracks) { auto tracksTuple = std::make_tuple(tracks); - BinningTypeVtxCent colBinning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVtxCent colBinning{{cfgVtxBins, cfgMultBins}}; SameKindPair pairs{colBinning, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { @@ -1009,7 +1009,7 @@ struct K892analysispbpb { void processMixedEventRun2(Run2Events const& collisions, TrackCandidates const& tracks, BCsWithRun2Info const& bcs) { auto tracksTuple = std::make_tuple(tracks); - BinningTypeVtxCentRun2 colBinning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVtxCentRun2 colBinning{{cfgVtxBins, cfgMultBins}}; SameKindPair pairs{colBinning, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { @@ -1034,7 +1034,7 @@ struct K892analysispbpb { void processMixedEventMC(EventCandidatesMCrec const& recCollisions, TrackCandidatesMCrec const& RecTracks, aod::McParticles const&) { auto tracksTuple = std::make_tuple(RecTracks); - BinningTypeVtxCent colBinning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVtxCent colBinning{{cfgVtxBins, cfgMultBins}}; SameKindPair pairs{colBinning, cfgNoMixedEvents, -1, recCollisions, tracksTuple, &cache}; for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { @@ -1059,7 +1059,7 @@ struct K892analysispbpb { void processMixedEventMCRun2(EventCandidatesMCrecRun2 const& recCollisions, TrackCandidatesMCrec const& RecTracks, BCsWithRun2Info const& bcs, aod::McParticles const&) { auto tracksTuple = std::make_tuple(RecTracks); - BinningTypeVtxCentRun2 colBinning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVtxCentRun2 colBinning{{cfgVtxBins, cfgMultBins}}; SameKindPair pairs{colBinning, cfgNoMixedEvents, -1, recCollisions, tracksTuple, &cache}; for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { diff --git a/PWGLF/Tasks/Resonances/k892hadronphotonBkg.cxx b/PWGLF/Tasks/Resonances/k892hadronphotonBkg.cxx index 07e7490a3a4..206f8861590 100644 --- a/PWGLF/Tasks/Resonances/k892hadronphotonBkg.cxx +++ b/PWGLF/Tasks/Resonances/k892hadronphotonBkg.cxx @@ -16,19 +16,23 @@ #include "PWGLF/DataModel/LFStrangenessMLTables.h" #include "PWGLF/DataModel/LFStrangenessPIDTables.h" #include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGLF/Utils/ResonanceMlResponse.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/ctpRateFetcher.h" #include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" +#include "Tools/ML/MlResponse.h" #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -50,6 +54,7 @@ #include using namespace o2; +using namespace o2::ml; using namespace o2::framework; using namespace o2::framework::expressions; using std::array; @@ -73,7 +78,10 @@ enum BkgResonance { struct k892hadronphotonBkg { Service ccdb{}; + o2::ccdb::CcdbApi ccdbApi; ctpRateFetcher rateFetcher; + o2::analysis::ResonanceMlResponse mlResponse; + TRandom3 rotRng{12345}; // struct member; fixed seed for reproducibility across grid jobs // Histogram registry @@ -119,6 +127,22 @@ struct k892hadronphotonBkg { Configurable rotGamma{"rotGamma", false, "Flag to rotate the photon direction"}; } lstarBkgConfig; + struct : ConfigurableGroup { + std::string prefix = "bdt"; // JSON group name + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable> onnxFileNames{"onnxFileNames", std::vector{"BDTModel.onnx"}, "Local .onnx file names, one per pT bin"}; + Configurable> modelPathsCCDB{"modelPathsCCDB", std::vector{"Users/o/obenchik/MLModels/BDT"}, "Model paths on CCDB, one per pT bin (each model needs its own folder)"}; + Configurable timestampCCDB{"timestampCCDB", 1695750420200, "timestamp of the ONNX file for ML model used to query in CCDB. Please use 1695750420200"}; + Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; + Configurable enableOptimizations{"enableOptimizations", false, "Enables the ONNX extended model-optimization: sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED)"}; + Configurable numThreads{"numThreads", 1, "ONNX intra-op threads. 0 lets ONNX Runtime default to one thread per physical core"}; + Configurable enableML{"enableML", false, "Enables bdt model"}; + Configurable> ptBinEdges{"ptBinEdges", {0., 30.}, "Pair-pT bin edges of the BDT models, one model per bin (pairs outside are rejected)"}; + Configurable> scoreCuts{"scoreCuts", {std::array{0., 0.}.data(), 1, 2, {"pT bin 0"}, {"Background score", "Signal score"}}, "BDT score cuts, one row per pT bin"}; + Configurable> cutDir{"cutDir", std::vector{o2::cuts_ml::CutNot, o2::cuts_ml::CutNot}, "Cut direction per class: 0 = keep score < cut, 1 = keep score >= cut, 2 = no cut"}; + Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"lambdaDCADau", "lambdaAlpha", "lambdaDCANegPV", "lambdaDCAPosPV", "lambdaQt", "photonAlpha", "photonCosPA", "photonDCADau", "photonDCANegPV", "photonDCAPosPV", "photonQt", "photonRadius", "opAngle"}, "Names and order of the BDT input features (see ResonanceMlResponse.h): must match FeaturesToTrain"}; + } bdt; + ConfigurableAxis axisVertexMixBkg{"axisVertexMixBkg", {VARIABLE_WIDTH, -10.f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "z-vertex bins for mixing"}; ConfigurableAxis axisCentralityMixBkg{"axisCentralityMixBkg", {VARIABLE_WIDTH, 0.0f, 1.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.0f, 110.0f}, "centrality bins for mixing"}; @@ -250,6 +274,14 @@ struct k892hadronphotonBkg { ConfigurableAxis axisAPQt{"axisAPQt", {220, 0.0f, 1.1f}, "Resonance AP q_{T} (GeV/c)"}; ConfigurableAxis axisCandSel{"axisCandSel", {15, 0.5f, +15.5f}, "Candidate Selection"}; ConfigurableAxis axisOPAngle{"axisOPAngle", {140, 0.0f, 7.0f}, "Opening angle (rad)"}; + // BDT QA axes + ConfigurableAxis mlProb{"mlProb", {100, 0.0f, 1.0f}, "BDT signal score"}; + ConfigurableAxis axisCosPA{"axisCosPA", {200, 0.5f, 1.0f}, "Cosine of pointing angle"}; + ConfigurableAxis axisDCAdau{"axisDCAdau", {50, 0.0f, 5.0f}, "DCA (cm)"}; + ConfigurableAxis axisSignedDCAtoPV{"axisSignedDCAtoPV", {1000, -50.0f, 50.0f}, "signed DCA (cm)"}; + ConfigurableAxis axisSignedDCAtoPVLambda{"axisSignedDCAtoPVLambda", {500, -10.0f, 10.0f}, "signed DCA (cm)"}; + ConfigurableAxis axisV0APQt{"axisV0APQt", {220, 0.0f, 0.5f}, "V0 AP q_{T} (GeV/c)"}; + ConfigurableAxis axisV0Radius{"axisV0Radius", {240, 0.0f, 120.0f}, "V0 radius (cm)"}; } axisConfig; void init(InitContext const&) @@ -259,6 +291,60 @@ struct k892hadronphotonBkg { ccdb->setCaching(true); ccdb->setFatalWhenNull(false); + if (bdt.enableML) { + ccdb->setURL(bdt.ccdbUrl.value); + + // One model per pair-pT bin. MlResponse checks the model files and cutDir, not the rows of scoreCuts + constexpr uint8_t NClassesML = 2; // background, signal + if (bdt.scoreCuts.value.rows() != bdt.ptBinEdges.value.size() - 1 || bdt.scoreCuts.value.cols() != NClassesML) { + LOG(fatal) << "bdt.scoreCuts needs one row per pT bin and " << static_cast(NClassesML) << " columns"; + } + mlResponse.configure(bdt.ptBinEdges.value, bdt.scoreCuts.value, bdt.cutDir.value, NClassesML); + mlResponse.cacheInputFeaturesIndices(bdt.namesInputFeatures); + + if (bdt.loadModelsFromCCDB) { + ccdbApi.init(bdt.ccdbUrl); + LOG(info) << "Fetching models for timestamp: " << bdt.timestampCCDB.value; + mlResponse.setModelPathsCCDB(bdt.onnxFileNames.value, ccdbApi, bdt.modelPathsCCDB.value, bdt.timestampCCDB.value); + } else { + mlResponse.setModelPathsLocal(bdt.onnxFileNames.value); + } + mlResponse.init(bdt.enableOptimizations.value, bdt.numThreads.value); + + // It is applied to the Lambda(1520) MIXED background only (for now!!) + if (!lstarBkgConfig.doSameEvtRotation && !lstarBkgConfig.doEvtMixing) { + LOG(warning) << "bdt.enableML is set but no Lambda(1520) background is requested: the BDT will not be applied."; + } + if (kstarBkgConfig.doSameEvtRotation || kstarBkgConfig.doEvtMixing) { + LOG(info) << "The BDT (gamma + Lambda features) is not applied to the K*(892) background."; + } + + histos.add("BDT/hScoreSignal", "hScoreSignal", kTH1D, {axisConfig.mlProb}); + histos.add("BDT/hScoreBackground", "hScoreBackground", kTH1D, {axisConfig.mlProb}); + histos.add("BDT/h2dScoreVsMassSignal", "h2dScoreVsMassSignal", kTH2D, {axisConfig.axisLambdaStarMass, axisConfig.mlProb}); + histos.add("BDT/h2dScoreVsPtSignal", "h2dScoreVsPtSignal", kTH2D, {axisConfig.axisPt, axisConfig.mlProb}); + histos.add("BDT/h3dScoreSignal", "h3dScoreSignal", kTH3D, {axisConfig.axisPt, axisConfig.axisLambdaStarMass, axisConfig.mlProb}); + histos.add("BDT/h2dScoreVsMassBackground", "h2dScoreVsMassBackground", kTH2D, {axisConfig.axisLambdaStarMass, axisConfig.mlProb}); + histos.add("BDT/h2dScoreVsPtBackground", "h2dScoreVsPtBackground", kTH2D, {axisConfig.axisPt, axisConfig.mlProb}); + histos.add("BDT/h3dScoreBackground", "h3dScoreBackground", kTH3D, {axisConfig.axisPt, axisConfig.axisLambdaStarMass, axisConfig.mlProb}); + histos.add("BDT/h2dLambdaDCADaughters", "h2dLambdaDCADaughters", kTH2D, {axisConfig.mlProb, axisConfig.axisDCAdau}); + + histos.add("BDT/h2dLambdaAlpha", "h2dLambdaAlpha", kTH2D, {axisConfig.mlProb, axisConfig.axisAPAlpha}); + histos.add("BDT/h2dLambdaDCANegPV", "h2dLambdaDCANegPV", kTH2D, {axisConfig.mlProb, axisConfig.axisSignedDCAtoPVLambda}); + histos.add("BDT/h2dLambdaDCAPosPV", "h2dLambdaDCAPosPV", kTH2D, {axisConfig.mlProb, axisConfig.axisSignedDCAtoPVLambda}); + histos.add("BDT/h2dLambdaQt", "h2dLambdaQt", kTH2D, {axisConfig.mlProb, axisConfig.axisV0APQt}); + histos.add("BDT/h2dPhotonAlpha", "h2dPhotonAlpha", kTH2D, {axisConfig.mlProb, axisConfig.axisAPAlpha}); + histos.add("BDT/h2dPhotonCosPA", "h2dPhotonCosPA", kTH2D, {axisConfig.mlProb, axisConfig.axisCosPA}); + histos.add("BDT/h2dPhotonDCADau", "h2dPhotonDCADau", kTH2D, {axisConfig.mlProb, axisConfig.axisDCAdau}); + histos.add("BDT/h2dPhotonDCANegPV", "h2dPhotonDCANegPV", kTH2D, {axisConfig.mlProb, axisConfig.axisSignedDCAtoPV}); + histos.add("BDT/h2dPhotonDCAPosPV", "h2dPhotonDCAPosPV", kTH2D, {axisConfig.mlProb, axisConfig.axisSignedDCAtoPV}); + histos.add("BDT/h2dPhotonQt", "h2dPhotonQt", kTH2D, {axisConfig.mlProb, axisConfig.axisV0APQt}); + histos.add("BDT/h2dPhotonRadius", "h2dPhotonRadius", kTH2D, {axisConfig.mlProb, axisConfig.axisV0Radius}); + histos.add("BDT/h2dOPAngle", "h2dOPAngle", kTH2D, {axisConfig.mlProb, axisConfig.axisOPAngle}); + histos.add("BDT/h2dAPAlpha", "h2dAPAlpha", kTH2D, {axisConfig.mlProb, axisConfig.axisAPAlpha}); + histos.add("BDT/h2dAPQt", "h2dAPQt", kTH2D, {axisConfig.mlProb, axisConfig.axisAPQt}); + } + histos.add("hEventCentrality", "hEventCentrality", kTH1D, {axisConfig.axisCentrality}); if (eventSelections.fUseEventSelection) { @@ -351,6 +437,8 @@ struct k892hadronphotonBkg { histos.add("LambdaStarBkg/h4dMixedLambdaStarPtVsAPAlphaVsAPQt", "h4dMixedLambdaStarPtVsAPAlphaVsAPQt", kTHnD, {axisConfig.axisAPAlpha, axisConfig.axisAPQt, axisConfig.axisPt, axisConfig.axisLambdaStarMass}); } } + + histos.print(); } //_______________________________________________ @@ -793,6 +881,75 @@ struct k892hadronphotonBkg { return std::sqrt(RecoDecay::p2(hadronP) - dp * dp / momTot2); } + //_______________________________________________ + // The two V0s share a daughter track (or are the same V0): rejected by the builder + template + static bool shareDaughters(TV0Object const& photon, TV0Object const& hadron) + { + return photon.globalIndex() == hadron.globalIndex() || + photon.posTrackExtraId() == hadron.posTrackExtraId() || + photon.negTrackExtraId() == hadron.negTrackExtraId() || + photon.posTrackExtraId() == hadron.negTrackExtraId() || + photon.negTrackExtraId() == hadron.posTrackExtraId(); + } + + //_______________________________________________ + // Fill BDT performance QA + template + void fillBDTPerformance(TV0Object const& lambda, TV0Object const& photon, float openAngle, float apAlpha, float apQt, float score, float pt, float mass) + { + float bkgScore = 1.0f - score; + + // Signal-probability output + histos.fill(HIST("BDT/hScoreSignal"), score); + histos.fill(HIST("BDT/h2dScoreVsMassSignal"), mass, score); + histos.fill(HIST("BDT/h2dScoreVsPtSignal"), pt, score); + histos.fill(HIST("BDT/h3dScoreSignal"), pt, mass, score); + + // Background-probability output + histos.fill(HIST("BDT/hScoreBackground"), bkgScore); + histos.fill(HIST("BDT/h2dScoreVsMassBackground"), mass, bkgScore); + histos.fill(HIST("BDT/h2dScoreVsPtBackground"), pt, bkgScore); + histos.fill(HIST("BDT/h3dScoreBackground"), pt, mass, bkgScore); + + // Signal score vs the main topological variables + histos.fill(HIST("BDT/h2dLambdaDCADaughters"), score, lambda.dcaV0daughters()); + histos.fill(HIST("BDT/h2dLambdaAlpha"), score, lambda.alpha()); + histos.fill(HIST("BDT/h2dLambdaDCANegPV"), score, lambda.dcanegtopv()); + histos.fill(HIST("BDT/h2dLambdaDCAPosPV"), score, lambda.dcapostopv()); + histos.fill(HIST("BDT/h2dLambdaQt"), score, lambda.qtarm()); + histos.fill(HIST("BDT/h2dPhotonAlpha"), score, photon.alpha()); + histos.fill(HIST("BDT/h2dPhotonCosPA"), score, photon.v0cosPA()); + histos.fill(HIST("BDT/h2dPhotonDCADau"), score, photon.dcaV0daughters()); + histos.fill(HIST("BDT/h2dPhotonDCANegPV"), score, photon.dcanegtopv()); + histos.fill(HIST("BDT/h2dPhotonDCAPosPV"), score, photon.dcapostopv()); + histos.fill(HIST("BDT/h2dPhotonQt"), score, photon.qtarm()); + histos.fill(HIST("BDT/h2dPhotonRadius"), score, photon.v0radius()); + histos.fill(HIST("BDT/h2dOPAngle"), score, openAngle); + histos.fill(HIST("BDT/h2dAPAlpha"), score, apAlpha); + histos.fill(HIST("BDT/h2dAPQt"), score, apQt); + } + + //_______________________________________________ + // BDT selection of a Lambda + photon pair + template + bool selectML(TV0Object const& lambda, TV0Object const& photon, + float openAngle, float apAlpha, float apQt, float pt, float mass) + { + // No model outside the bdt.ptBinEdges range + if (pt < bdt.ptBinEdges.value.front() || pt >= bdt.ptBinEdges.value.back()) + return false; + + // Features in the order of bdt.namesInputFeatures + auto inputFeatures = mlResponse.getInputFeatures(lambda, photon, openAngle, apAlpha, apQt); + std::vector outputMl; + const bool isSelected = mlResponse.isSelectedMl(inputFeatures, pt, outputMl); // model and cut of the pT bin + + fillBDTPerformance(lambda, photon, openAngle, apAlpha, apQt, outputMl[1], pt, mass); + + return isSelected; + } + //_______________________________________________ // Compute same-event rotational background within a single collision. template @@ -820,6 +977,10 @@ struct k892hadronphotonBkg { for (const int& pIdx : photonIndices) { const auto& photon = fullV0s.rawIteratorAt(pIdx); + // Same pair rejection + if (shareDaughters(photon, hadron)) + continue; + // photon as a massless 4-vector ROOT::Math::PtEtaPhiMVector pGamma(photon.pt(), photon.eta(), @@ -853,6 +1014,16 @@ struct k892hadronphotonBkg { // Opening angle between photon and hadron double cosOA = gammaLeg.Vect().Dot(hadronLeg.Vect()) / (gammaLeg.P() * hadronLeg.P()); double openAngle = std::acos(cosOA); + // double pt = reso.Pt(); + // double mass = reso.M(); + + // // To:Do BDT selection (Lambda(1520)) + // if constexpr (resonance == kResoLambdaStar) { + // if (bdt.enableML) { + // if (!selectML(hadron, photon, openAngle, pt, mass)) + // continue; + // } + // } // Armenteros-Podolanski of the rotated pair const std::array gammaMom{static_cast(gammaLeg.Px()), static_cast(gammaLeg.Py()), static_cast(gammaLeg.Pz())}; @@ -882,9 +1053,9 @@ struct k892hadronphotonBkg { //_______________________________________________ // Mixed-event pairing: hadrons and photons come from two different collisions. - // Centrality is taken from the reference collision (the first of the pair) - template - void calculateMixedBackground(TCollision const& refColl, + // Centrality is taken from the reference collision (the first of the pair). + template + void calculateMixedBackground(TRefColl const& refColl, std::vector const& hadronIndices, std::vector const& photonIndices, TV0s const& fullV0s) @@ -909,6 +1080,11 @@ struct k892hadronphotonBkg { for (const int& pIdx : photonIndices) { const auto& photon = fullV0s.rawIteratorAt(pIdx); + + // Same pair rejection as the builder + if (shareDaughters(photon, hadron)) + continue; + float pP = std::hypot(photon.px(), photon.py(), photon.pz()); ROOT::Math::PxPyPzEVector fourMomPhoton( photon.px(), photon.py(), photon.pz(), pP); @@ -937,6 +1113,14 @@ struct k892hadronphotonBkg { const float apAlpha = armenterosAlpha(gammaMom, hadronMom); const float apQt = armenterosQt(gammaMom, hadronMom); + // BDT selection (Lambda(1520) only) + if constexpr (resonance == kResoLambdaStar) { + if (bdt.enableML) { + if (!selectML(hadron, photon, openAngle, apAlpha, apQt, pt, mass)) + continue; + } + } + if constexpr (resonance == kResoKStar) { histos.fill(HIST("KStarBkg/h2dMixedKStarMassVsPt"), mass, pt); histos.fill(HIST("KStarBkg/h3dMixedKStarMassVsPt"), centrality, pt, mass); @@ -1024,7 +1208,7 @@ struct k892hadronphotonBkg { // Build the mixing binning locally: a struct member initialized from a // ConfigurableAxis captures the default bins at task construction time - BkgBinningType bkgColBinning{{axisVertexMixBkg, axisCentralityMixBkg}, true}; + BkgBinningType bkgColBinning{{axisVertexMixBkg, axisCentralityMixBkg}}; for (const auto& [coll1, coll2] : selfCombinations(bkgColBinning, kstarBkgConfig.nMix, -1, collisions, collisions)) { diff --git a/PWGLF/Tasks/Resonances/k892pmanalysis.cxx b/PWGLF/Tasks/Resonances/k892pmanalysis.cxx index f30d7c1ae28..abe0ae5f7f3 100644 --- a/PWGLF/Tasks/Resonances/k892pmanalysis.cxx +++ b/PWGLF/Tasks/Resonances/k892pmanalysis.cxx @@ -492,7 +492,7 @@ struct k892pmanalysis { { auto tracksV0sTuple = std::make_tuple(resotracks, resov0s); // auto V0sTuple = std::make_tuple(resov0s); - BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}}; Pair pairs{colBinning, nEvtMixing, -1, collisions, tracksV0sTuple, &cache}; // -1 is the number of the bin to skip for (auto& [collision1, resotracks1, collision2, resov0s2] : pairs) { diff --git a/PWGLF/Tasks/Resonances/kaonkaonanalysis.cxx b/PWGLF/Tasks/Resonances/kaonkaonanalysis.cxx index 19abecac1e1..c746e7c082f 100644 --- a/PWGLF/Tasks/Resonances/kaonkaonanalysis.cxx +++ b/PWGLF/Tasks/Resonances/kaonkaonanalysis.cxx @@ -504,8 +504,8 @@ struct kaonkaonAnalysisRun3 { { auto tracksTuple = std::make_tuple(tracks); //////// currently mixing the event with similar TPC multiplicity //////// - BinningTypeVertexContributor1 binningOnPositions1{{axisVertex, axisMultiplicity}, true}; // for pp - BinningTypeVertexContributor2 binningOnPositions2{{axisVertex, axisMultiplicity}, true}; // for PbPb + BinningTypeVertexContributor1 binningOnPositions1{{axisVertex, axisMultiplicity}}; // for pp + BinningTypeVertexContributor2 binningOnPositions2{{axisVertex, axisMultiplicity}}; // for PbPb SameKindPair pair1{binningOnPositions1, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; SameKindPair pair2{binningOnPositions2, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; diff --git a/PWGLF/Tasks/Resonances/kshortlambda.cxx b/PWGLF/Tasks/Resonances/kshortlambda.cxx index 0ddf5ade8bf..64b0278d835 100644 --- a/PWGLF/Tasks/Resonances/kshortlambda.cxx +++ b/PWGLF/Tasks/Resonances/kshortlambda.cxx @@ -714,8 +714,8 @@ struct Kshortlambda { void processME(EventCandidates const& collisions, TrackCandidates const& /*tracks*/, V0TrackCandidate const& v0s) { auto tracksTuple = std::make_tuple(v0s); - BinningTypeVertexContributor binningOnPositions1{{mevz, memult}, true}; - BinningTypeCentralityM binningOnPositions2{{mevz, memult}, true}; + BinningTypeVertexContributor binningOnPositions1{{mevz, memult}}; + BinningTypeCentralityM binningOnPositions2{{mevz, memult}}; SameKindPair pair1{binningOnPositions1, cfgNmixedEvents, -1, collisions, tracksTuple, &cache}; // for PbPb SameKindPair pair2{binningOnPositions2, cfgNmixedEvents, -1, collisions, tracksTuple, &cache}; // for pp diff --git a/PWGLF/Tasks/Resonances/kstar0analysis.cxx b/PWGLF/Tasks/Resonances/kstar0analysis.cxx index 4755b2e0a0b..ad544f85608 100644 --- a/PWGLF/Tasks/Resonances/kstar0analysis.cxx +++ b/PWGLF/Tasks/Resonances/kstar0analysis.cxx @@ -1133,7 +1133,7 @@ struct Kstar0analysis { { auto tracksTuple = std::make_tuple(tracks); - BinningTypeVtxZT0M colBinning{{configBkg.cfgVtxBins, configBkg.cfgMultPercentileBins}, true}; + BinningTypeVtxZT0M colBinning{{configBkg.cfgVtxBins, configBkg.cfgMultPercentileBins}}; SameKindPair pairs{colBinning, configBkg.nEvtMixing, -1, collision, tracksTuple, &cache}; // -1 is the number of the bin to skip for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { diff --git a/PWGLF/Tasks/Resonances/kstar892LightIon.cxx b/PWGLF/Tasks/Resonances/kstar892LightIon.cxx index cdfb55fc3ba..24ad4ba2a94 100644 --- a/PWGLF/Tasks/Resonances/kstar892LightIon.cxx +++ b/PWGLF/Tasks/Resonances/kstar892LightIon.cxx @@ -1539,10 +1539,10 @@ struct Kstar892LightIon { using BinningTypeFT0C = ColumnBinningPolicy; using BinningTypeFV0A = ColumnBinningPolicy; - BinningTypeFT0M binningOnFT0M{{axisVertex, axisCentrality}, true}; - BinningTypeFT0A binningOnFT0A{{axisVertex, axisCentrality}, true}; - BinningTypeFT0C binningOnFT0C{{axisVertex, axisCentrality}, true}; - BinningTypeFV0A binningOnFV0A{{axisVertex, axisCentrality}, true}; + BinningTypeFT0M binningOnFT0M{{axisVertex, axisCentrality}}; + BinningTypeFT0A binningOnFT0A{{axisVertex, axisCentrality}}; + BinningTypeFT0C binningOnFT0C{{axisVertex, axisCentrality}}; + BinningTypeFV0A binningOnFV0A{{axisVertex, axisCentrality}}; SameKindPair pair1{binningOnFT0M, selectionConfig.cfgNoMixedEvents, -1, &cache}; SameKindPair pair2{binningOnFT0A, selectionConfig.cfgNoMixedEvents, -1, &cache}; @@ -1612,10 +1612,10 @@ struct Kstar892LightIon { using BinningTypeMCFT0C = ColumnBinningPolicy; using BinningTypeMCFV0A = ColumnBinningPolicy; - BinningTypeMCFT0M binningOnMCFT0M{{axisVertex, axisCentrality}, true}; - BinningTypeMCFT0A binningOnMCFT0A{{axisVertex, axisCentrality}, true}; - BinningTypeMCFT0C binningOnMCFT0C{{axisVertex, axisCentrality}, true}; - BinningTypeMCFV0A binningOnMCFV0A{{axisVertex, axisCentrality}, true}; + BinningTypeMCFT0M binningOnMCFT0M{{axisVertex, axisCentrality}}; + BinningTypeMCFT0A binningOnMCFT0A{{axisVertex, axisCentrality}}; + BinningTypeMCFT0C binningOnMCFT0C{{axisVertex, axisCentrality}}; + BinningTypeMCFV0A binningOnMCFV0A{{axisVertex, axisCentrality}}; SameKindPair pairmc1{binningOnMCFT0M, selectionConfig.cfgNoMixedEvents, -1, &cache}; SameKindPair pairmc2{binningOnMCFT0A, selectionConfig.cfgNoMixedEvents, -1, &cache}; diff --git a/PWGLF/Tasks/Resonances/kstar892analysis.cxx b/PWGLF/Tasks/Resonances/kstar892analysis.cxx index 3e7626203df..88fd873b977 100644 --- a/PWGLF/Tasks/Resonances/kstar892analysis.cxx +++ b/PWGLF/Tasks/Resonances/kstar892analysis.cxx @@ -886,7 +886,7 @@ struct kstar892analysis { void processMELight(o2::aod::ResoCollisions& collisions, soa::Filtered const& resotracks) { auto tracksTuple = std::make_tuple(resotracks); - BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}}; SameKindPair, BinningTypeVtxZT0M> pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto& [collision1, tracks1, collision2, tracks2] : pairs) { @@ -904,7 +904,7 @@ struct kstar892analysis { void processMELightWithTof(o2::aod::ResoCollisions& collisions, soa::Filtered const& resotracks) { auto tracksTuple = std::make_tuple(resotracks); - BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}}; SameKindPair, BinningTypeVtxZT0M> pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto& [collision1, tracks1, collision2, tracks2] : pairs) { @@ -922,7 +922,7 @@ struct kstar892analysis { void processMELightTPCLowPt(o2::aod::ResoCollisions& collisions, soa::Filtered const& resotracks) { auto tracksTuple = std::make_tuple(resotracks); - BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}}; SameKindPair, BinningTypeVtxZT0M> pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto& [collision1, tracks1, collision2, tracks2] : pairs) { @@ -940,7 +940,7 @@ struct kstar892analysis { void processMELightTOFHighPt(o2::aod::ResoCollisions& collisions, soa::Filtered const& resotracks) { auto tracksTuple = std::make_tuple(resotracks); - BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}}; SameKindPair, BinningTypeVtxZT0M> pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto& [collision1, tracks1, collision2, tracks2] : pairs) { diff --git a/PWGLF/Tasks/Resonances/kstarInOO.cxx b/PWGLF/Tasks/Resonances/kstarInJets.cxx similarity index 82% rename from PWGLF/Tasks/Resonances/kstarInOO.cxx rename to PWGLF/Tasks/Resonances/kstarInJets.cxx index d16f1be1818..d62d47cd5dc 100644 --- a/PWGLF/Tasks/Resonances/kstarInOO.cxx +++ b/PWGLF/Tasks/Resonances/kstarInJets.cxx @@ -8,8 +8,8 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file kstarInOO.cxx -/// \brief the pT spectra of k*0(892) resonance analysis in OO collisions +/// \file kstarInJets.cxx +/// \brief the pT spectra of k*0(892) resonance analysis in Jets collisions /// \author Jimun Lee #include "PWGJE/Core/JetDerivedDataUtilities.h" @@ -28,11 +28,8 @@ #include #include #include -#include #include -#include #include -#include #include #include #include @@ -46,6 +43,7 @@ #include #include +#include #include #include #include @@ -59,8 +57,9 @@ using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; +using namespace ROOT::Math; -struct kstarInOO { +struct kstarInJets { SliceCache cache; Preslice perCollision = aod::track::collisionId; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -70,6 +69,8 @@ struct kstarInOO { //| Selection //| //================================== + Configurable cDebugLevel{"cDebugLevel", 0, "Resolution of Debug"}; + // Event Selection Configurable cfgEventSelections{"cfgEventSelections", "sel8", "Set event selection"}; Configurable cfgEventVtxCut{"cfgEventVtxCut", 10.0, "V_z cut selection"}; @@ -86,11 +87,11 @@ struct kstarInOO { Configurable cfgTrackPrimaryTrack{"cfgTrackPrimaryTrack", true, "Primary track selection"}; // kGoldenChi2 | kDCAxy | kDCAz Configurable cfgTrackConnectedToPV{"cfgTrackConnectedToPV", true, "PV contributor track selection"}; // PV Contriuibutor Configurable cfgTrackGlobalWoDCATrack{"cfgTrackGlobalWoDCATrack", true, "Global track selection without DCA"}; - // kQualityTracks (kTrackType | kTPCNCls | kTPCCrossedRows | kTPCCrossedRowsOverNCls | kTPCChi2NDF | kTPCRefit | kITSNCls | kITSChi2NDF | kITSRefit | kITSHits) | kInAcceptanceTracks (kPtRange | kEtaRange) + // kQualityTracks (kTrackType | kTPCNCls | kTPCCrossedRows | kTPCCrossedRowsOverNCls | kTPCChi2NDF | kTPCRefit | kITSNTCls | kITSChi2NDF | kITSRefit | kITSHits) | kInAcceptanceTracks (kPtRange | kEtaRange) // TPC - Configurable cfgTrackFindableTPCClusters{"cfgTrackFindableTPCClusters", 50, "nFindable TPC Clusters"}; + Configurable cfgTrackFindableTPCClusters{"cfgTrackFindableTPCClusters", 0, "nFindable TPC Clusters"}; // origin = 50 Configurable cfgTrackTPCCrossedRows{"cfgTrackTPCCrossedRows", 70, "nCrossed TPC Rows"}; - Configurable cfgTrackRowsOverFindable{"cfgTrackRowsOverFindable", 1.2, "nRowsOverFindable TPC CLusters"}; + Configurable cfgTrackRowsOverFindable{"cfgTrackRowsOverFindable", 0, "nRowsOverFindable TPC CLusters"}; // origin = 1.2 Configurable cfgTrackTPCChi2{"cfgTrackTPCChi2", 4.0, "nTPC Chi2 per Cluster"}; // ITS @@ -103,24 +104,12 @@ struct kstarInOO { Configurable cfgTrackCirclePIDCut{"cfgTrackCirclePIDCut", true, "Enables PID cut shape circle switch"}; Configurable cfgTrackCircleValue{"cfgTrackCircleValue", 2, "Enables TOF TPC PID circle cut value"}; Configurable cfgTrackTOFHard{"cfgTrackTOFHard", false, "Enables TOF Hard"}; - Configurable isMIDCut{"isMIDCut", false, "Enables Mis-IDentified PID cut"}; - Configurable cfgTrackTPCPIDnSig{"cfgTrackTPCPIDnSig", 4.0, "nTPC PID sigma"}; Configurable cfgTrackTOFPIDnSig{"cfgTrackTOFPIDnSig", 4.0, "nTOF PID sigma"}; - Configurable cfgMIDnSig{"cfgMIDnSig", 1.5, "nTPC Mis-IDentification Particle sigma"}; - - Configurable cDebugLevel{"cDebugLevel", 0, "Resolution of Debug"}; - - // Mixing - ConfigurableAxis cfgBinsMixMult{"cfgBinsCent", {VARIABLE_WIDTH, 0.0, 1.0, 5.0, 10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0, 110.0}, "Binning of the centrality axis"}; - ConfigurableAxis cfgBinsMixVtx{"cfgBinsMixVtx", {VARIABLE_WIDTH, -10.0f, -5.f, 0.f, 5.f, 10.f}, "Mixing bins - z-vertex"}; - Configurable cfgMixNMixedEvents{"cfgMixNMixedEvents", 10, "Number of mixed events per event"}; - Configurable cfgVtxMixCut{"cfgVtxMixCut", 10, "Vertex Mix Cut"}; // MCGen Configurable cfgForceGenReco{"cfgForceGenReco", false, "Only consider events which are reconstructed (neglect event-loss)"}; Configurable cfgReqMcEffPID{"cfgReqMcEffPID", false, "Request McEfficiency PID"}; - Configurable cfgReqMcEffTrackQA{"cfgRecMcEffTrackQA", false, "Enable Jet Cut on Trig"}; // Pair Configurable cfgMinvNBins{"cfgMinvNBins", 300, "Number of bins for Minv axis"}; @@ -135,10 +124,11 @@ struct kstarInOO { Configurable cfgJetQAHistos{"cfgJetQAHistos", false, "Enable Jet QA Histos"}; Configurable cfgMCHistos{"cfgMCHistos", false, "Enable MC Histos"}; - Configurable cfgMixedHistos{"cfgMixedHistos", false, "Enable Mixed Histos"}; Configurable cfgJetHistos{"cfgJetHistos", false, "Enable Jet Histos"}; Configurable cfgJetMCHistos{"cfgJetMCHistos", false, "Enable Jet MC Histos"}; Configurable cfgJetdRHistos{"cfgJetdRHistos", false, "Enable Jet dR QA Histos"}; + Configurable cfgMatchedGenHistos{"cfgMatchedGenHistos", false, "Enable Matched Gen Histos"}; + Configurable cfgCutonTrig{"cfgCutonTrig", false, "Enable Jet Cut on Trig"}; //====================== @@ -168,9 +158,10 @@ struct kstarInOO { void init(o2::framework::InitContext&) { // HISTOGRAMS - const AxisSpec axisEta{30, -1.5, +1.5, "#eta"}; - const AxisSpec axisPhi{200, -1, +7, "#phi"}; - const AxisSpec ptAxis = {200, 0, 20.0}; + const AxisSpec axisEta{30, -1.5, 1.5, "#eta"}; + const AxisSpec axisPhi{200, -1, 7, "#phi"}; + const AxisSpec axisPt{100, 0, 100, "#it{p}_{T}"}; // Jet pT + const AxisSpec ptAxis = {100, 0, 100.0}; // Particle pT const AxisSpec pidAxis = {120, -6, 6}; const AxisSpec minvAxis = {cfgMinvNBins, cfgMinvMin, cfgMinvMax}; const AxisSpec axisDCAz{binsDCAz, "DCA_{z}"}; @@ -219,9 +210,6 @@ struct kstarInOO { histos.add("QA_kaon_TPC_TOF_BC", "QA_kaon_TPC_TOF_BC", {HistType::kTH2F, {pidAxis, pidAxis}}); histos.add("QA_track_pT_BC", "QA_track_pT_BC", kTH1F, {{13, 0.0, 13.0}}); - histos.add("Possible_MID_Pion_TPC", "Possible_MID_Pion_TPC", {HistType::kTH2F, {pidAxis, pidAxis}}); - histos.add("Possible_MID_Kaon_TPC", "Possible_MID_Kaon_TPC", {HistType::kTH2F, {pidAxis, pidAxis}}); - histos.add("hDCArToPv_AC", "DCArToPv_AC", kTH1F, {axisDCAxy}); histos.add("hDCAzToPv_AC", "DCAzToPv_AC", kTH1F, {axisDCAz}); histos.add("hIsPrim_AC", "hIsPrim_AC", kTH1F, {{2, -0.5, 1.5}}); @@ -240,6 +228,7 @@ struct kstarInOO { histos.add("QA_kaon_TPC_TOF_AC", "QA_kaon_TPC_TOF_AC", {HistType::kTH2F, {pidAxis, pidAxis}}); histos.add("QA_track_pT_AC", "QA_track_pT_AC", kTH1F, {{13, 0.0, 13.0}}); } + if (cfgJetQAHistos) { histos.add("nTriggerQA", "nTriggerQA", kTH1F, {{8, 0.0, 8.0}}); @@ -258,8 +247,6 @@ struct kstarInOO { histos.add("mcpjet_eta", "MC particle-level Jet Eta", kTH1F, {{100, -1.0, 1.0}}); histos.add("mcpjet_phi", "MC particle-level Jet Phi", kTH1F, {{80, -1.0, 7.0}}); - histos.add("nJet_6_8", "MC particle-level nJet pT 6 < jet pT < 8 (GeV/c)", kTH1F, {{2000, 0., 100.}}); - histos.add("Gen_KstarPt_All_BR", "Generated K*0 All pT (GeV/c)", kTH1F, {{2000, 0., 100.}}); histos.add("Gen_Kstar_BR", "Generated K*0 BR", kTH1F, {minvAxis}); histos.add("Gen_KstarPt_BR", "Generated K*0 pT (GeV/c)", kTH1F, {{2000, 0., 100.}}); @@ -268,6 +255,9 @@ struct kstarInOO { histos.add("dR_taggedjet_pion", "dR between tagged jet and pion wo pT cut", {HistType::kTH2F, {{100, 0., 3.0}, {150, 0., 30.}}}); histos.add("dR_taggedjet_all", "dR between tagged jet and kpi", {HistType::kTH2F, {{100, 0., 3.0}, {150, 0., 30.}}}); histos.add("dR_taggedjet_all_6_8", "dR between tagged jet and kpi 6 < jet pT < 8", {HistType::kTH2F, {{100, 0., 3.0}, {150, 0., 30.}}}); + histos.add("dR_taggedjet_all_8_12", "dR between tagged jet and kpi 8 < jet pT < 12", {HistType::kTH2F, {{100, 0., 3.0}, {150, 0., 30.}}}); + + histos.add("dR_taggedjet_0_8", "dR between tagged jet and kpi 0 < jet pT < 8", {HistType::kTH2F, {{100, 0., 3.0}, {150, 0., 30.}}}); histos.add("dR_kstar_pion", "dR between kstar and pion", {HistType::kTH2F, {{100, 0., 3.0}, {150, 0., 30.}}}); histos.add("dR_kstar_kaon", "dR between kstar and kaon", {HistType::kTH2F, {{100, 0., 3.0}, {150, 0., 30.}}}); @@ -275,6 +265,7 @@ struct kstarInOO { histos.add("Kstar_pT_INJet_6_8", "K*0 pT Inside 6-8 Jet", kTH1F, {{1000, 0., 50.}}); histos.add("Kstar_pT_INJet_8_10", "K*0 pT Inside 8-10 Jet", kTH1F, {{1000, 0., 50.}}); histos.add("Kstar_pT_INJet_10_12", "K*0 pT Inside 10-12 Jet", kTH1F, {{1000, 0., 50.}}); + histos.add("Kstar_pT_INJet_8_12", "K*0 pT Inside 8-12 Jet", kTH1F, {{1000, 0., 50.}}); histos.add("JetpT_8to", "Jet pT 8 to", kTH1F, {{2000, 0., 100.}}); histos.add("recoveredJetpT_6_8to8", "Recovered Jet pT 6-8 --> 8", kTH1F, {{2000, 0., 100.}}); @@ -292,17 +283,13 @@ struct kstarInOO { histos.add("JetMigration", "bin to bin migration", {HistType::kTH2F, {{150, 0.0, 15.0, "True jet pT (GeV/c)"}, {150, 0., 15., "Recovered jet pT (GeV/c)"}}}); } - //////////////////////////////////// + /////////////////////////////////// histos.add("nEvents", "nEvents", kTH1F, {{7, 0.0, 7.0}}); histos.add("hUSS_KPi", "hUSS_KPi", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); histos.add("hLSS_KPi", "hLSS_KPi", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); histos.add("hUSS_PiK", "hUSS_PiK", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); histos.add("hLSS_PiK", "hLSS_PiK", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); - if (cfgMixedHistos) { - histos.add("hUSS_KPi_Mix", "hUSS_KPi_Mix", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); - histos.add("hUSS_PiK_Mix", "hUSS_PiK_Mix", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); - } if (cfgMCHistos) { histos.add("nEvents_Gen", "nEvents_Gen", kTH1F, {{7, 0.0, 7.0}}); histos.add("hUSS_TrueRec", "hUSS_TrueRec", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); @@ -322,23 +309,26 @@ struct kstarInOO { histos.add("hGen_pT_Kstar", "Gen_pT_Kstar (GeV/c)", kTH1F, {{800, 0., 40.}}); histos.add("hRec_pT_Kstar", "Rec_pT_Kstar (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hEffRec_pT", "EffRec_pT (GeV/c)", kTH1F, {{1600, 0., 80.}}); - histos.add("hEffRecTest0_pT", "EffRecTest0_pT (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hEffRecTest1_pT", "EffRecTest1_pT (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hEffRecTest2_pT", "EffRecTest2_pT (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hEffRecTest3_pT", "EffRecTest3_pT (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hEffRecTest4_pT", "EffRecTest4_pT (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hEffRecTest5_pT", "EffRecTest5_pT (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hEffRecTest6_pT", "EffRecTest6_pT (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hEffRecTest7_pT", "EffRecTest7_pT (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hEffRecTest8_pT", "EffRecTest8_pT (GeV/c)", kTH1F, {{800, 0., 40.}}); + } + + if (cfgMatchedGenHistos) { + histos.add("nEvents_MCGen_MATCHED", "nEvents_MCGen_MATCHED", kTH1F, {{7, 0.0, 7.0}}); + histos.add("matchedJets_pT", "#it{p}_{T,jet part}; #it{p}_{T,jet det}", {HistType::kTH2F, {{100, 0., 20.}, {400, -20., 20.0}}}); + histos.add("matchedJets_eta", "#eta_{jet part}; #eta_{jet det}", {HistType::kTH2F, {{100, -1.0, 1.0}, {400, -20.0, 20.0}}}); + histos.add("matchedJets_phi", "#phi_{jet part}; #phi_{jet det}", {HistType::kTH2F, {{80, -1.0, 7.}, {400, -20.0, 20.}}}); + + histos.add("NoMatchedJets_pT", "Gen. No Matched detector-level Jet", kTHnSparseD, {axisPt}); + histos.add("RespGen_Matched_rand0", "Gen. Response Matrix Matched 0", kTHnSparseD, {ptAxis, axisPt, ptAxis, axisPt}); + histos.add("RespGen_Matched_rand1", "Gen. Response Matrix Matched 1", kTHnSparseD, {ptAxis, axisPt, ptAxis, axisPt}); - histos.add("hEffRecLowPtKstarDaughter", "EffRecLow Kstar daughter_pT (GeV/c)", kTH1F, {{800, 0., 40.}}); + // ALL Jets + histos.add("nEvents_MCGen_MATCHED_allJet", "nEvents_MCGen_MATCHED_allJet", kTH1F, {{7, 0.0, 7.0}}); + histos.add("NoMatchedJets_pT_allJet", "Gen. No Matched detector-level ALL Jet", kTHnSparseD, {axisPt}); + histos.add("RespGen_MatchedJet_TEST_allJet", "Gen. Response Matrix Matched ALL Jet Test", kTHnSparseD, {axisPt, axisPt}); + histos.add("RespGen_Matrix_Matched_allJet", "Gen. Response Matrix Matched ALL Jet", kTHnSparseD, {ptAxis, axisPt, ptAxis, axisPt}); - histos.add("hEffGen_pT", "EffGen_pT (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hMotherPdg1", "hMotherPdg1", kTH1F, {{5000, 0., 5000.}}); - histos.add("hMotherPdg2", "hMotherPdg2", kTH1F, {{5000, 0., 5000.}}); + histos.add("RespGen_MatchedJet_TEST_rand0_allJet", "Gen. Response Matrix Matched ALL Jet TEST", kTHnSparseD, {axisPt, axisPt}); } // Jet @@ -368,14 +358,6 @@ struct kstarInOO { using JetTrackCandidatesMC = soa::Join; - // For Mixed Event - using BinningType = ColumnBinningPolicy; - - Partition kaon = !cfgTrackTPCPID || (nabs(aod::pidtpc::tpcNSigmaKa) <= cfgTrackTPCPIDnSig); - Partition pion = !cfgTrackTPCPID || (nabs(aod::pidtpc::tpcNSigmaPi) <= cfgTrackTPCPIDnSig); - Partition kaonMC = !cfgTrackTPCPID || (nabs(aod::pidtpc::tpcNSigmaKa) <= cfgTrackTPCPIDnSig); - Partition pionMC = !cfgTrackTPCPID || (nabs(aod::pidtpc::tpcNSigmaPi) <= cfgTrackTPCPIDnSig); - double massKa = o2::constants::physics::MassKPlus; double massPi = o2::constants::physics::MassPiMinus; @@ -459,10 +441,13 @@ struct kstarInOO { } // pion pid Selection } // fill QA + // enum class objectType { Inclusive, + // Jets, + // RecoParticle, + // JetsRecoParticle }; + enum class objectType { Inclusive, - Jets, - RecoParticle, - JetsRecoParticle }; + Jets }; template void fillMinv(objectType type, const TrackType& trk1, const TrackType& trk2, const ROOT::Math::PxPyPzMVector& lReso, double centrality, double jetpt, bool flip) { @@ -502,21 +487,6 @@ struct kstarInOO { } break; - case objectType::RecoParticle: - if (cfgMCHistos) { - if (conjugate < 0) { - histos.fill(HIST("hUSS_TrueRec"), centrality, lReso.Pt(), lReso.M()); - } - } - break; - - case objectType::JetsRecoParticle: - if (cfgJetMCHistos) { - if (conjugate < 0) { - histos.fill(HIST("hUSS_TrueRec_INSIDE"), centrality, jetpt, lReso.Pt(), lReso.M()); - } - } - break; } // switch } // fillMinv //====================================================================== @@ -658,24 +628,6 @@ struct kstarInOO { return true; }; - template - bool isMIDPion(const TrackPID& candidate, const bool QA) - { - if (cfgTrackCutQA && QA) { - histos.fill(HIST("Possible_MID_Pion_TPC"), candidate.tpcNSigmaKa(), candidate.tofNSigmaKa()); - } - return std::abs(candidate.tpcNSigmaKa()) < cfgMIDnSig && std::abs(candidate.tpcNSigmaPi()) < cfgMIDnSig; - } - - template - bool isMIDKaon(const TrackPID& candidate, const bool QA) - { - if (cfgTrackCutQA && QA) { - histos.fill(HIST("Possible_MID_Kaon_TPC"), candidate.tpcNSigmaPi(), candidate.tofNSigmaPi()); - } - return std::abs(candidate.tpcNSigmaPi()) < cfgMIDnSig && std::abs(candidate.tpcNSigmaKa()) < cfgMIDnSig; - } - template bool trackPIDKaon(const TrackPID& candidate, const bool QA) { @@ -727,8 +679,8 @@ struct kstarInOO { // TPC & TOF if (tpcPIDPassed && tofPIDPassed) { - // if (cfgTrackCutQA && QA) - // fillQA(true, candidate, 3); + if (cfgTrackCutQA && QA) + fillQA(true, candidate, 3); return true; } return false; @@ -786,8 +738,8 @@ struct kstarInOO { // TPC & TOF if (tpcPIDPassed && tofPIDPassed) { - // if (cfgTrackCutQA && QA) - // fillQA(true, candidate, 4); + if (cfgTrackCutQA && QA) + fillQA(true, candidate, 4); return true; } return false; @@ -806,18 +758,6 @@ struct kstarInOO { return {}; } - if (isMIDCut && isMIDPion(trk1, QA)) { - return {}; - } - if (isMIDCut && isMIDKaon(trk2, QA)) { - return {}; - } - - if (cfgTrackCutQA && QA) { - fillQA(true, trk1, 3); - fillQA(true, trk2, 4); - } - ROOT::Math::PxPyPzMVector lDecayDaughter1, lDecayDaughter2, lResonance; if (!flip) { lDecayDaughter1 = ROOT::Math::PxPyPzMVector(trk1.px(), trk1.py(), trk1.pz(), massKa); @@ -867,10 +807,10 @@ struct kstarInOO { mothers2PDG.push_back(particle2_mom.pdgCode()); } - if (mothers1PDG[0] != Kstar0PDG) { + if (std::abs(mothers1PDG[0]) != Kstar0PDG) { return {}; // mother not K*0 } - if (mothers2PDG[0] != Kstar0PDG) { + if (std::abs(mothers2PDG[0]) != Kstar0PDG) { return {}; // mothers not K*0 } if (mothers1[0] != mothers2[0]) { @@ -883,7 +823,8 @@ struct kstarInOO { return {}; } - double track1_mass, track2_mass; + double track1_mass = 0; + double track2_mass = 0; if (std::abs(particle1.pdgCode()) == PionPDG) { track1_mass = massPi; } else { @@ -916,13 +857,10 @@ struct kstarInOO { } // TrueReconstruction template - void TrackSlicing(const CollisionType& collision1, const TracksType&, const CollisionType& collision2, const TracksType&, const bool QA) + void TrackSlicing(const CollisionType& collision, const TracksType& tracks, const bool QA) { - auto tracks1 = kaon->sliceByCached(aod::track::collisionId, collision1.globalIndex(), cache); - auto tracks2 = pion->sliceByCached(aod::track::collisionId, collision2.globalIndex(), cache); - auto centrality = collision1.centFT0C(); - - for (const auto& [trk1, trk2] : combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + auto centrality = collision.centFT0C(); + for (const auto& [trk1, trk2] : combinations(o2::soa::CombinationsUpperIndexPolicy(tracks, tracks))) { for (bool flip : {false, true}) { auto lReso = minvReconstruction(trk1, trk2, QA, flip); if (lReso.M() < 0) { @@ -935,13 +873,10 @@ struct kstarInOO { } // TrackSlicing template - void TrackSlicingMC(const CollisionType& collision1, const TracksType&, const CollisionType& collision2, const TracksType&, const bool QA) + void TrackSlicingMC(const CollisionType& collision, const TracksType& tracks, const bool QA) { - auto tracks1 = kaonMC->sliceByCached(aod::track::collisionId, collision1.globalIndex(), cache); - auto tracks2 = pionMC->sliceByCached(aod::track::collisionId, collision2.globalIndex(), cache); - auto centrality = collision1.centFT0C(); - - for (const auto& [trk1, trk2] : combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + auto centrality = collision.centFT0C(); + for (const auto& [trk1, trk2] : combinations(o2::soa::CombinationsUpperIndexPolicy(tracks, tracks))) { if (!trk1.has_mcParticle() || !trk2.has_mcParticle()) { continue; } @@ -958,12 +893,15 @@ struct kstarInOO { //| True Reconstruction //============================ auto lTrueReso = TrueReconstruction(trk1, trk2); - fillMinv(objectType::RecoParticle, trk1, trk2, lTrueReso, centrality, -1.0, false); + if (cfgMCHistos) { + histos.fill(HIST("hUSS_TrueRec"), centrality, lTrueReso.Pt(), lTrueReso.M()); + } + } // tracks } // TrackSlicingMC template - double DistinguishJets(const JetType& jets, const ROOT::Math::PxPyPzMVector& lResonance) + double DistinguishJets(const JetType& jets, ROOT::Math::PxPyPzMVector& lResonance) { if (cDebugLevel > 0) { LOG(info) << "Found multiple jets to the same phi."; @@ -975,10 +913,7 @@ struct kstarInOO { double phidiff = TVector2::Phi_mpi_pi(jet.phi() - lResonance.Phi()); double etadiff = jet.eta() - lResonance.Eta(); double R = TMath::Sqrt((phidiff * phidiff) + (etadiff * etadiff)); - if (R < cfgJetR && bestR == 0) { - bestR = R; - bestJetpT = jet.pt(); - } else if (R < bestR) { + if (R < cfgJetR && (bestR == 0 || R < bestR)) { bestR = R; bestJetpT = jet.pt(); } @@ -986,6 +921,28 @@ struct kstarInOO { return bestJetpT; } + template + double DistinguishJetsMC(const Jet_pt& jet_pt, const Jet_phi& jet_phi, const Jet_eta& jet_eta, const Resonance& lResonance) + { + if (cDebugLevel > 0) { + LOG(info) << "Multiple jets fit to the same K*0. Time to find the best K*0-jet"; + } + + double best_R = 0; + double best_jetpt = 0; + for (std::vector::size_type i = 0; i < jet_pt.size(); i++) { + double phidiff = TVector2::Phi_mpi_pi(jet_phi[i] - lResonance.Phi()); + double etadiff = jet_eta[i] - lResonance.Eta(); + double R = TMath::Sqrt((phidiff * phidiff) + (etadiff * etadiff)); + + if (R < cfgJetR && (best_R == 0 || R < best_R)) { + best_R = R; + best_jetpt = jet_pt[i]; + } + } // jet loop + return best_jetpt; + } + template void JetTrackSlicing(aod::JetCollision const& collision, TracksType const& jetTracks, const JetType& chargedjets, const bool QA) { @@ -1060,8 +1017,8 @@ struct kstarInOO { if (lResonance.M() < 0) { continue; } - fillMinv(objectType::Inclusive, trk1, trk2, lResonance, centrality, -1.0, flip); + //============================== //| Jets: Track Reconstruction //============================== @@ -1091,6 +1048,7 @@ struct kstarInOO { fillMinv(objectType::Jets, trk1, trk2, lResonance, centrality, jetpt, flip); } // jetFlag } // filp + } // Tracks loop } // JetTrackSlicingMC @@ -1102,15 +1060,16 @@ struct kstarInOO { using JetTrackCandidates = soa::Join; using JetFilteredJets = soa::Filtered>; - int nJetEvents = 0; void processDataJets(o2::aod::JetCollision const& collision, JetFilteredJets const& chargedjets, JetTrackCandidates const& jetTracks, TrackCandidates const&) { + static int nJetEvents = 0; if (cDebugLevel > 0) { nJetEvents++; if ((nJetEvents + 1) % 10000 == 0) { LOG(info) << "Processed Jet Data Events: " << nJetEvents; } } + histos.fill(HIST("nEvents"), 0.5); // Raw event bool INELgt0 = false; @@ -1176,7 +1135,7 @@ struct kstarInOO { bool HasJets = false; int nJets = 0; - for (const auto& chargedjet : chargedjets) { + for (auto& chargedjet : chargedjets) { if (std::abs(chargedjet.eta()) > (cfgJetMaxEta - cfgJetdR)) { continue; } @@ -1220,16 +1179,16 @@ struct kstarInOO { JetTrackSlicing(collision, jetTracks, chargedjets, true); } // ProcessDataJets - PROCESS_SWITCH(kstarInOO, processDataJets, "process Data Jets", false); + PROCESS_SWITCH(kstarInJets, processDataJets, "process Data Jets", false); //======================================================= //| //| JET MC STUFF //| //======================================================= - int nJetMCEvents = 0; void processMCJets(o2::aod::JetCollision const& collision, JetTrackCandidatesMC const& jetTracks, soa::Filtered const& mcdjets, TrackCandidatesMC const&, aod::McParticles const&, aod::JetParticles const&) { + static int nJetMCEvents = 0; if (cDebugLevel > 0) { nJetMCEvents++; if ((nJetMCEvents + 1) % 10000 == 0) { @@ -1263,7 +1222,7 @@ struct kstarInOO { bool HasJets = false; int nJets = 0; - for (const auto& mcdjet : mcdjets) { + for (auto& mcdjet : mcdjets) { if (std::abs(mcdjet.eta()) > cfgJetMaxEta - cfgJetdR) { continue; } @@ -1291,62 +1250,37 @@ struct kstarInOO { } histos.fill(HIST("nEvents"), 3.5); // Jet event - // JetTrackSlicingMC(collision, jetTracks, mcdjets, true); + JetTrackSlicingMC(collision, jetTracks, mcdjets, true); // Instead of using "JetTrackSlicingMC", Once we have to test showing the distribution each condition + for (auto& [track1, track2] : combinations(o2::soa::CombinationsUpperIndexPolicy(jetTracks, jetTracks))) { - auto trk1 = track1.track_as(); - auto trk2 = track2.track_as(); + auto trk1 = track1.template track_as(); + auto trk2 = track2.template track_as(); // Each section, test pT. which section we lost the entries - ROOT::Math::PxPyPzMVector lDecayDaughterTest1, lDecayDaughterTest2, lResonanceTest1; - lDecayDaughterTest1 = ROOT::Math::PxPyPzMVector(trk1.px(), trk1.py(), trk1.pz(), massKa); - lDecayDaughterTest2 = ROOT::Math::PxPyPzMVector(trk2.px(), trk2.py(), trk2.pz(), massPi); - lResonanceTest1 = lDecayDaughterTest1 + lDecayDaughterTest2; - - if (cfgJetMCHistos) { - histos.fill(HIST("hEffRecTest0_pT"), lResonanceTest1.Pt()); - } + ROOT::Math::PxPyPzMVector lDecayDaughter1, lDecayDaughter2, lResonance; + lDecayDaughter1 = ROOT::Math::PxPyPzMVector(trk1.px(), trk1.py(), trk1.pz(), massKa); + lDecayDaughter2 = ROOT::Math::PxPyPzMVector(trk2.px(), trk2.py(), trk2.pz(), massPi); + lResonance = lDecayDaughter1 + lDecayDaughter2; if (!trk1.has_mcParticle() || !trk2.has_mcParticle()) { continue; } - if (cfgJetMCHistos) { - histos.fill(HIST("hEffRecTest1_pT"), lResonanceTest1.Pt()); - } - ////////////////////////////// - ////////////////////////////// - if (cfgReqMcEffTrackQA) { // false, true - if (!trackSelection(trk1, true) || !trackSelection(trk2, false)) { - continue; - } - } - ////////////////////////////// - ////////////////////////////// - - if (cfgJetMCHistos) { - histos.fill(HIST("hEffRecTest2_pT"), lResonanceTest1.Pt()); + if (!trackSelection(trk1, true) || !trackSelection(trk2, false)) { + continue; } if (cfgReqMcEffPID) { // false, true if (!trackPIDKaon(trk1, true) || !trackPIDPion(trk2, true)) { continue; } } - if (cfgJetMCHistos) { - histos.fill(HIST("hEffRecTest3_pT"), lResonanceTest1.Pt()); - } + auto particle1 = trk1.mcParticle(); auto particle2 = trk2.mcParticle(); - //////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////// if (!particle1.has_mothers() || !particle2.has_mothers()) { continue; } - if (cfgJetMCHistos) { - histos.fill(HIST("hEffRecTest4_pT"), lResonanceTest1.Pt()); - } - //////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////// std::vector mothers1{}; std::vector mothers1PDG{}; @@ -1355,69 +1289,46 @@ struct kstarInOO { mothers1PDG.push_back(particle1_mom.pdgCode()); } - if (cfgJetMCHistos) { - histos.fill(HIST("hMotherPdg1"), std::abs(mothers1PDG[0])); - } - std::vector mothers2{}; std::vector mothers2PDG{}; for (auto& particle2_mom : particle2.template mothers_as()) { mothers2.push_back(particle2_mom.globalIndex()); mothers2PDG.push_back(particle2_mom.pdgCode()); } - if (cfgJetMCHistos) { - histos.fill(HIST("hMotherPdg2"), std::abs(mothers2PDG[0])); - } + if (mothers1[0] != mothers2[0]) { continue; // Kaon and pion not from the same K*0 } - if (cfgJetMCHistos) { - histos.fill(HIST("hEffRecTest5_pT"), lResonanceTest1.Pt()); - } - // kaon if (std::abs(particle1.pdgCode()) != KaonPDG) { continue; } - - if (cfgJetMCHistos) { - histos.fill(HIST("hEffRecTest6_pT"), lResonanceTest1.Pt()); - } - // pion if (std::abs(particle2.pdgCode()) != PionPDG) { continue; } - if (cfgJetMCHistos) { - histos.fill(HIST("hEffRecTest7_pT"), lResonanceTest1.Pt()); - } + if (std::abs(mothers1PDG[0]) != Kstar0PDG) { continue; // mother not K*0 } - if (cfgJetMCHistos) { - histos.fill(HIST("hEffRecTest8_pT"), lResonanceTest1.Pt()); - if (lResonanceTest1.Pt() < 0.4) { - histos.fill(HIST("hEffRecLowPtKstarDaughter"), lDecayDaughterTest1.Pt()); - histos.fill(HIST("hEffRecLowPtKstarDaughter"), lDecayDaughterTest2.Pt()); - } - } - if (std::abs(mothers2PDG[0]) != Kstar0PDG) { continue; // mothers not K*0 } if (cfgJetMCHistos) { - histos.fill(HIST("hEffRec_pT"), lResonanceTest1.Pt()); + histos.fill(HIST("hEffRec_pT"), lResonance.Pt()); } + } // track loop + } // process loop - PROCESS_SWITCH(kstarInOO, processMCJets, "process MC Jets", false); + PROCESS_SWITCH(kstarInJets, processMCJets, "process MC Jets", false); //======================================================= //| //| DATA STUFF (SE) //| //======================================================= - int nEvents = 0; void processDataSameEvent(EventCandidates::iterator const& collision, TrackCandidates const& tracks) { + static int nEvents = 0; if (cDebugLevel > 0) { nEvents++; if ((nEvents + 1) % 10000 == 0) { @@ -1451,63 +1362,18 @@ struct kstarInOO { } } - TrackSlicing(collision, tracks, collision, tracks, true); + TrackSlicing(collision, tracks, true); } // processSameEvents - PROCESS_SWITCH(kstarInOO, processDataSameEvent, "process Data Same Event", false); - - //======================================================= - //| - //| DATA STUFF (ME) - //| - //======================================================= - int nEventsMix = 0; - void processDataMixedEvent(EventCandidates const& collisions, TrackCandidates const& tracks) - { - auto tracksTuple = std::make_tuple(tracks); - BinningType colBinning{{cfgBinsMixVtx, cfgBinsMixMult}, true}; // true is for 'ignore overflows' (true by default) - SameKindPair pairs{colBinning, cfgMixNMixedEvents, -1, collisions, tracksTuple, &cache}; - for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { - if (cDebugLevel > 0) { - nEventsMix++; - if ((nEventsMix + 1) % 10000 == 0) { - LOG(info) << "Processed DATA Mixed Events : " << nEventsMix; - } - } - auto [goodEv1, code1] = eventSelection(collision1, false); - auto [goodEv2, code2] = eventSelection(collision2, false); - bool VtxMixFlag = false; - bool CentMixFlag = false; - // bool OccupanacyMixFlag = false; - if (std::abs(collision1.posZ() - collision2.posZ()) <= cfgVtxMixCut) { // set default to maybe 10 - VtxMixFlag = true; - } - if (std::abs(collision1.centFT0C() - collision2.centFT0C()) <= cfgVtxMixCut) { // set default to maybe 10 - CentMixFlag = true; - } - - if (!goodEv1 || !goodEv2) { - continue; - } - if (!CentMixFlag) { - continue; - } - if (!VtxMixFlag) { - continue; - } - - TrackSlicing(collision1, tracks1, collision2, tracks2, false); - } - } - PROCESS_SWITCH(kstarInOO, processDataMixedEvent, "process DATA Mixed Event", false); + PROCESS_SWITCH(kstarInJets, processDataSameEvent, "process Data Same Event", false); //======================================================= //| //| MC STUFF (SE) //| //========================================================= - int nEventsMC = 0; void processSameEventMC(EventCandidates::iterator const& collision, TrackCandidatesMC const& tracks, aod::McParticles const&) { + static int nEventsMC = 0; if (cDebugLevel > 0) { nEventsMC++; if ((nEventsMC + 1) % 10000 == 0) { @@ -1543,47 +1409,18 @@ struct kstarInOO { } } - TrackSlicingMC(collision, tracks, collision, tracks, true); + TrackSlicingMC(collision, tracks, true); } // processSameEvents_MC - PROCESS_SWITCH(kstarInOO, processSameEventMC, "process Same Event MC", false); - - //======================================================= - //| - //| MC STUFF (ME) - //| - //======================================================= - int nEventsMCMix = 0; - void processMixedEventMC(EventCandidates const& collisions, TrackCandidatesMC const& tracks, aod::McParticles const&) - { - auto tracksTuple = std::make_tuple(tracks); - BinningType colBinning{{cfgBinsMixVtx, cfgBinsMixMult}, true}; // true is for 'ignore overflows' (true by default) - SameKindPair pairs{colBinning, cfgMixNMixedEvents, -1, collisions, tracksTuple, &cache}; - for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { - if (cDebugLevel > 0) { - nEventsMCMix++; - if ((nEventsMCMix + 1) % 10000 == 0) { - LOG(info) << "Processed Mixed Events: " << nEventsMCMix; - } - } - auto [goodEv1, code1] = eventSelection(collision1, false); - auto [goodEv2, code2] = eventSelection(collision2, false); - if (!goodEv1 || !goodEv2) { - continue; - } - - TrackSlicingMC(collision1, tracks1, collision2, tracks2, false); - } // mixing - } // processMixedEvent_MC - PROCESS_SWITCH(kstarInOO, processMixedEventMC, "process Mixed Event MC", false); + PROCESS_SWITCH(kstarInJets, processSameEventMC, "process Same Event MC", false); //====================================== //| //| GENERATED STUFF //| //====================================== - int nEventsGen = 0; void processGen(EventCandidatesTrue::iterator const& collision, soa::SmallGroups> const& recocolls, aod::McParticles const& mcParticles) { + static int nEventsGen = 0; if (cDebugLevel > 0) { ++nEventsGen; if (nEventsGen % 10000 == 0) { @@ -1678,16 +1515,16 @@ struct kstarInOO { } } // loop over particles } // processGen - PROCESS_SWITCH(kstarInOO, processGen, "process Generated Particles", false); + PROCESS_SWITCH(kstarInJets, processGen, "process Generated Particles", false); //============================================== //| //| GENERATED STUFF (INCLUSIVE & JETS) //| //============================================== - int nprocessGenEvents = 0; void processGenJets(o2::aod::JetMcCollision const& collision, soa::SmallGroups> const& recocolls, aod::JetParticles const& mcParticles) { + static int nprocessGenEvents = 0; if (cDebugLevel > 0) { ++nprocessGenEvents; if (nprocessGenEvents % 10000 == 0) { @@ -1796,11 +1633,11 @@ struct kstarInOO { } } // loop over particles } // end of process - PROCESS_SWITCH(kstarInOO, processGenJets, "Process Generated Particles Inclusive&Jets", false); + PROCESS_SWITCH(kstarInJets, processGenJets, "Process Generated Particles Inclusive&Jets", false); - int ndRtest = 0; void processJetQA(o2::aod::JetMcCollision const& collision, soa::Filtered const& mcpjets, aod::JetParticles const& mcParticles, aod::McParticles const&) { + static int ndRtest = 0; if (cDebugLevel > 0) { ++ndRtest; if (ndRtest % 10000 == 0) { @@ -1894,12 +1731,6 @@ struct kstarInOO { continue; } - if (bestJet.Pt() > 6.0 && bestJet.Pt() < 8.0) { - if (cfgJetdRHistos) { - histos.fill(HIST("nJet_6_8"), bestJet.Pt()); - } - } - //================== // daughters double dR_kaon = 0; @@ -1960,6 +1791,11 @@ struct kstarInOO { histos.fill(HIST("dR_taggedjet_kaon"), dR_kaon, lResonance.Pt()); histos.fill(HIST("dR_taggedjet_pion"), dR_pion, lResonance.Pt()); + if (bestJet.Pt() < 8.0) { + histos.fill(HIST("dR_taggedjet_0_8"), dR_kaon, lResonance.Pt()); + histos.fill(HIST("dR_taggedjet_0_8"), dR_pion, lResonance.Pt()); + } + if (bestJet.Pt() > 6.0 && bestJet.Pt() < 8.0) { histos.fill(HIST("dR_taggedjet_all_6_8"), dR_kaon, lResonance.Pt()); histos.fill(HIST("dR_taggedjet_all_6_8"), dR_pion, lResonance.Pt()); @@ -1974,6 +1810,11 @@ struct kstarInOO { if (bestJet.Pt() > 10.0 && bestJet.Pt() < 12.0) { histos.fill(HIST("Kstar_pT_INJet_10_12"), lResonance.Pt()); } + if (bestJet.Pt() > 8.0 && bestJet.Pt() < 12.0) { + histos.fill(HIST("dR_taggedjet_all_8_12"), dR_kaon, lResonance.Pt()); + histos.fill(HIST("dR_taggedjet_all_8_12"), dR_pion, lResonance.Pt()); + histos.fill(HIST("Kstar_pT_INJet_8_12"), lResonance.Pt()); + } // Daughters out of jets if (!isKaonInJet || !isPionInJet) { @@ -2013,9 +1854,262 @@ struct kstarInOO { } // cfgJetdRHistos } // mcParticles }; - PROCESS_SWITCH(kstarInOO, processJetQA, "Process dR of K*0 Inclusive and Inside jet", false); + PROCESS_SWITCH(kstarInJets, processJetQA, "Process dR of K*0 Inclusive and Inside jet", false); + + //======================== + // + // Response matrix + // + //======================== + + using JetMCDTable = soa::Filtered>; + using JetMCPTable = soa::Filtered>; + + void processMatchedJet(aod::JetMcCollision const& collision, + soa::SmallGroups> const& recocolls, + JetMCPTable const& mcpjets, JetMCDTable const&, + JetTrackCandidatesMC const& tracks, TrackCandidatesMC const&, + aod::JetParticles const& mcParticles, aod::McParticles const&) + { + static int nMatched = 0; + if (cDebugLevel > 0) { + ++nMatched; + if (nMatched % 10000 == 0) { + LOG(info) << "Processed Matched test: " << nMatched; + } + } + + if (cfgMatchedGenHistos) { + histos.fill(HIST("nEvents_MCGen_MATCHED"), 0.5); + } + if (std::fabs(collision.posZ()) > cfgEventVtxCut) { + return; + } + + if (recocolls.size() <= 0) { + return; + } + for (auto& recocoll : recocolls) { + if (!jetderiveddatautilities::selectCollision(recocoll, eventSelectionBits)) { + return; + } + } + + bool INELgt0 = false; + for (const auto& mcParticle : mcParticles) { + if (std::abs(mcParticle.eta()) < cfgEventMaxEta) { + INELgt0 = true; + break; + } + } // mcParticles + if (!INELgt0) { + return; + } + if (cfgMatchedGenHistos) { + histos.fill(HIST("nEvents_MCGen_MATCHED"), 1.5); + } + + // 1. Gen jet --> Reco jet + + std::vector mcd_pt{}; + std::vector mcd_phi{}; + std::vector mcd_eta{}; + + std::vector mcp_pt{}; + std::vector mcp_phi{}; + std::vector mcp_eta{}; + + bool hasJets = false; + for (const auto& mcpjet : mcpjets) { + if (!mcpjet.has_matchedJetGeo()) { + + if (cfgMatchedGenHistos) { + histos.fill(HIST("NoMatchedJets_pT"), mcpjet.pt()); + } + continue; + } + + for (const auto& mcdjet : mcpjet.template matchedJetGeo_as()) { + if (!mcdjet.has_matchedJetGeo()) { + continue; + } + hasJets = true; + + // QA + // double dPhi = TVector2::Phi_mpi_pi(mcdjet.phi() - mcpjet.phi()); + // double dEta = mcdjet.eta() - mcpjet.eta(); + // double dR = std::sqrt((dPhi * dPhi) + (dEta * dEta)); + + if (cfgMatchedGenHistos) { + histos.fill(HIST("matchedJets_pT"), mcpjet.pt(), mcdjet.pt() - mcpjet.pt()); + histos.fill(HIST("matchedJets_phi"), mcpjet.phi(), mcdjet.phi() - mcpjet.phi()); + histos.fill(HIST("matchedJets_eta"), mcpjet.eta(), mcdjet.eta() - mcpjet.eta()); + } + + mcd_pt.push_back(mcdjet.pt()); + mcd_phi.push_back(mcdjet.phi()); + mcd_eta.push_back(mcdjet.eta()); + + mcp_pt.push_back(mcpjet.pt()); + mcp_phi.push_back(mcpjet.phi()); + mcp_eta.push_back(mcpjet.eta()); + + } // mcdjet + } // mcpjet + + if (hasJets) { + if (cfgMatchedGenHistos) { + histos.fill(HIST("nEvents_MCGen_MATCHED"), 2.5); + } + } + + // 2. Find Gen K*0 + + // True K*0 + for (const auto& mcParticle : mcParticles) { + if (std::fabs(mcParticle.eta()) > cfgTrackMaxEta) { + continue; + } + if (std::fabs(mcParticle.pdgCode()) != Kstar0PDG) { + continue; + } + if (!mcParticle.has_daughters()) { + continue; + } + + // K*0 --> Kpi + int nKaon = 0; + int nPion = 0; + + int kaonIndex = -1; + int pionIndex = -1; + + double kaonPx = 0; + double kaonPy = 0; + double kaonPz = 0; + + double pionPx = 0; + double pionPy = 0; + double pionPz = 0; + + // Truth K*0 --> Keep the KPi Index + for (const auto& daughter : mcParticle.daughters_as()) { + + if (std::abs(daughter.pdgCode()) == KaonPDG) { + ++nKaon; + kaonIndex = daughter.globalIndex(); + } else if (std::abs(daughter.pdgCode()) == PionPDG) { + ++nPion; + pionIndex = daughter.globalIndex(); + } + } + if (nKaon != 1 || nPion != 1) { + continue; + } + + //======================= + // Track KPi --> Reco K*0 + + bool kaon_daughter = false; + bool pion_daughter = false; + + for (const auto& track : tracks) { + auto trk = track.track_as(); + + if (!trk.has_mcParticle()) { + continue; + } + if (!trackSelection(trk, true)) { + continue; + } + if (std::fabs(trk.eta()) > cfgTrackMaxEta) { + continue; + } + if (!trackPIDKaon(trk, false) || !trackPIDPion(trk, false)) { + continue; + } + + auto particle1 = trk.mcParticle(); + auto particle2 = trk.mcParticle(); + + if (particle1.globalIndex() == kaonIndex) { + kaonPx = trk.px(); + kaonPy = trk.py(); + kaonPz = trk.pz(); + kaon_daughter = true; + } + if (particle2.globalIndex() == pionIndex) { + pionPx = trk.px(); + pionPy = trk.py(); + pionPz = trk.pz(); + pion_daughter = true; + } + } // track + + if (!kaon_daughter || !pion_daughter) { + continue; + } + + // 3. Prepare Gen and Rec K*0 + + ROOT::Math::PxPyPzEVector lResonance = ROOT::Math::PxPyPzEVector(mcParticle.px(), mcParticle.py(), mcParticle.pz(), mcParticle.e()); + ROOT::Math::PxPyPzMVector lDecayKaon_Rec = ROOT::Math::PxPyPzMVector(kaonPx, kaonPy, kaonPz, massKa); + ROOT::Math::PxPyPzMVector lDecayPion_Rec = ROOT::Math::PxPyPzMVector(pionPx, pionPy, pionPz, massPi); + ROOT::Math::PxPyPzMVector lResonance_Rec = lDecayKaon_Rec + lDecayPion_Rec; + + // 4. Calculate dR between jet and K*0 + + int goodjets = 0; + bool jetFlag = false; + double jetpt_mcp = 0; + double jetpt_mcd = 0; + + for (std::vector::size_type i = 0; i < mcp_pt.size(); i++) { + double dphi = TVector2::Phi_mpi_pi(mcp_phi[i] - lResonance.Phi()); + double deta = mcp_eta[i] - lResonance.Eta(); + double dR = TMath::Sqrt((deta * deta) + (dphi * dphi)); + + if (dR < cfgJetdR) { + jetFlag = true; + jetpt_mcp = mcp_pt[i]; + jetpt_mcd = mcd_pt[i]; + + goodjets++; + } + } // mcp jet + + if (cfgSingleJet) { + if (goodjets > 1) { + jetpt_mcp = DistinguishJetsMC(mcp_pt, mcp_phi, mcp_eta, lResonance); + jetpt_mcd = DistinguishJetsMC(mcd_pt, mcd_phi, mcd_eta, lResonance_Rec); + } + } + + if (!jetFlag) { + continue; + } + + if (cfgMatchedGenHistos) { + unsigned int seed = static_cast(std::chrono::system_clock::now().time_since_epoch().count()); + int dice = rand_r(&seed) % 2; + + // for checking + if (dice > 0) { + // Train + histos.fill(HIST("RespGen_Matched_rand0"), lResonance_Rec.Pt(), jetpt_mcd, lResonance.Pt(), jetpt_mcp); + } else { + // Test + histos.fill(HIST("RespGen_Matched_rand1"), lResonance_Rec.Pt(), jetpt_mcd, lResonance.Pt(), jetpt_mcp); + } + + } // cfgMatchedGenHistos + + } // mcParticles + }; // processJetUnfolding + PROCESS_SWITCH(kstarInJets, processMatchedJet, "Process Unfolding of K*0 jets", false); }; + WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(cfgc)}; } diff --git a/PWGLF/Tasks/Resonances/kstarpbpb.cxx b/PWGLF/Tasks/Resonances/kstarpbpb.cxx index 4211976dc32..46c1a5ba640 100644 --- a/PWGLF/Tasks/Resonances/kstarpbpb.cxx +++ b/PWGLF/Tasks/Resonances/kstarpbpb.cxx @@ -131,13 +131,14 @@ struct Kstarpbpb { Configurable nBkgRotations{"nBkgRotations", 9, "Number of rotated copies (background) per each original candidate"}; Configurable fillRotation{"fillRotation", true, "fill rotation"}; Configurable fillSA{"fillSA", true, "same event SA"}; + Configurable doClosureTest{"doClosureTest", false, "Fill dedicated MC closure histograms for spin alignment"}; Configurable fillLikeSignSA{"fillLikeSignSA", false, "fill the same-event like-sign SA sparse (needs fillSA)"}; Configurable useWeight{"useWeight", false, "use EP dep effi weight"}; Configurable useSP{"useSP", false, "use SP"}; // spin-alignment quantization axis Configurable cfgSAFrame{"cfgSAFrame", 0, "SA quantization axis: 0 = event plane (FT0C), 1 = production plane, 2 = random event plane"}; Configurable cfgRndSeed{"cfgRndSeed", 0, "Seed for random event plane (0 = unique seed per job)"}; - Configurable cfgEPNormalHarmonic{"cfgEPNormalHarmonic", 2.0f, "EP normal = (sin(n*Psi), -cos(n*Psi), 0); n = 2 is the previous behaviour, n = 1 is the normal to the Psi2 plane"}; + Configurable cfgEPNormalHarmonic{"cfgEPNormalHarmonic", 1.0f, "EP normal = (sin(n*Psi), -cos(n*Psi), 0); n = 1 is the normal to the Psi2 plane (default), n = 2 is the old behaviour (axis at 2*Psi2 - pi/2, not the normal; the R2 resolution correction does not apply)"}; // phi(1020) -> K+K- reflection veto for K* (off by default: the task then behaves exactly as before) Configurable cfgPhiVeto{"cfgPhiVeto", false, "Reject K pi pairs whose KK mass (pion candidate given the kaon mass) is near the phi(1020) mass"}; Configurable cfgPhiVetoWindow{"cfgPhiVetoWindow", 0.010f, "Half-width of the phi(1020) veto window (GeV/c^2)"}; @@ -174,6 +175,9 @@ struct Kstarpbpb { Configurable confRapidity{"confRapidity", 0.5, "Rapidity cut"}; Configurable genacceptancecut{"genacceptancecut", true, "use acceptance cut for generated"}; Configurable avoidsplitrackMC{"avoidsplitrackMC", false, "avoid split track in MC"}; + // same-event backgrounds, as for K* (off by default: processSEPhi then behaves exactly as before) + Configurable fillRotation{"fillRotation", false, "Fill the same-event rotated K+K- background (K+ rotated, with confMinRot, confMaxRot and nBkgRotations as for K*)"}; + Configurable fillLikeSign{"fillLikeSign", false, "Fill the same-event like-sign (K+K+ and K-K-) background"}; ConfigurableAxis configThnAxisInvMass{"configThnAxisInvMass", {120, 0.98, 1.1}, "#it{M} (GeV/#it{c}^{2})"}; ConfigurableAxis configThnAxisPt{"configThnAxisPt", {100, 0.0, 10.}, "#it{p}_{T} (GeV/#it{c})"}; ConfigurableAxis configThnAxisCosThetaStar{"configThnAxisCosThetaStar", {10, -1.0, 1.}, "cos(#vartheta)"}; @@ -181,6 +185,37 @@ struct Kstarpbpb { ConfigurableAxis configThnAxisRapidity{"configThnAxisRapidity", {8, 0, 0.8}, "Rapidity"}; ConfigurableAxis configThnAxisSA{"configThnAxisSA", {200, -1, 1}, "SA"}; } phiSA; + + // MC closure test of the cos(theta*) chain (processMCClosure, processMCClosureME, processMCClosurePhi, processMCClosureMEPhi) + // Step 1: SE unlike-sign minus normalised ME (same cuts as data) -> raw yield vs cos(theta*) + // Step 2: raw yield compared with the true reconstructed yield (hSparseRecTrue) + // Step 3: raw yield / (Acc x Eff)(cos theta*), with Acc x Eff = hSparseRecTrueEff / hSparseGenEff + // Step 4: corrected yield and rho00 compared with the generated input (hSparseGen) + struct : ConfigurableGroup { + std::string prefix = "closure"; + Configurable injectPolarization{"injectPolarization", false, "Reweight the true K*/phi with W(cos theta*) so that the sample has rho00 = rho00Inj"}; + Configurable rho00Inj{"rho00Inj", 1.0f / 3.0f, "Injected rho00, in [0,1] (1/3 = unpolarized)"}; + Configurable useTrueEP{"useTrueEP", true, "Use the MC true event plane angle as Psi (false: Psi = 0, fixed axis as in processMC with mcEffEP.psiSource = 0)"}; + Configurable splitSample{"splitSample", true, "Even MC events build Acc x Eff, odd MC events are the test sample (false: same events for both)"}; + Configurable fillRotBkg{"fillRotBkg", false, "Fill the rotated-pair background (weight 1), cross-check of the ME background"}; + Configurable genAcceptanceCut{"genAcceptanceCut", false, "Daughter pT/eta cut on the generated K*/phi (false: denominator is the full |y| < cut sample, so the correction is the full Acc x Eff in cos theta*)"}; + Configurable requirePrimaryTracks{"requirePrimaryTracks", false, "SE, ME and rotated pairs only from tracks matched to physical primaries (as in phianalysisrun3pbpb / kstar892LightIon); false: all tracks, as in data"}; + Configurable meRequireSingleReco{"meRequireSingleReco", true, "ME: both events must come from an MC collision with exactly one reco collision, as required in the SE (processMCClosure / processMCClosurePhi)"}; + ConfigurableAxis axisCosThetaStar{"axisCosThetaStar", {10, -1.0, 1.0}, "cos(#vartheta*)"}; + } closure; + + // Psi of the EP-frame MC efficiency (processMC, processMCPhi). In data the axis follows Psi_FT0C, which is flat in the lab; + // with a fixed Psi = 0 the axis is fixed in the lab, so azimuthal holes of the detector enter Acc x Eff(cos theta*). + // psiSource = 1 follows the thesis (S. Kundu, App. B.10.4): Psi2 from the generated tracks in the forward EP-detector acceptance + // (V0C there, FT0C here), one Psi per MC event, for both the generated and the reconstructed K*/phi. + struct : ConfigurableGroup { + std::string prefix = "mcEffEP"; + Configurable psiSource{"psiSource", 1, "Psi of the EP-frame MC efficiency: 0 = fixed Psi = 0 (old: axis -y for K*, in-plane x for phi), 1 = Psi2 from generated charged primaries in [etaMin, etaMax] (thesis), 2 = MC true event plane (mcCollision.eventPlaneAngle())"}; + Configurable etaMin{"etaMin", -3.3f, "eta min of the generated-track Q2 vector (FT0C acceptance)"}; + Configurable etaMax{"etaMax", -2.1f, "eta max of the generated-track Q2 vector (FT0C acceptance)"}; + Configurable fillPhiVMinusPsi{"fillPhiVMinusPsi", false, "Fill gen/rec Acc x Eff maps vs pT, cos theta* and phi_V - Psi (thesis Fig. B.69 / Table B.1 check)"}; + ConfigurableAxis axisPhiVMinusPsi{"axisPhiVMinusPsi", {6, 0.0, o2::constants::math::PI}, "#varphi_{V} - #Psi"}; + } mcEffEP; Configurable cfgMinTrackPt{"cfgMinTrackPt", 0.15f, "Minimum track pT"}; @@ -211,6 +246,16 @@ struct Kstarpbpb { using FilTrackMCRecTable = soa::Filtered; Preslice perCollision = aod::track::collisionId; + // reconstructed MC collisions for the closure: adds EPCalibrationTables (psiFT0C etc.) on top of CollisionMCRecTableCentFT0C, + // so the closure can optionally use the reconstructed (finite-resolution) Psi_FT0C instead of the MC true event plane + using CollisionMCRecTableClosure = soa::SmallGroups>; + // reconstructed MC collisions for the closure mixed event (posZ and centrality filters as in data) + using CollisionMCRecMixTable = soa::Filtered>; + // same as CollisionMCRecMixTable, plus EPCalibrationTables (psiFT0C etc.) for the reconstructed-event-plane closure + using CollisionMCRecMixTableRecoEP = soa::Filtered>; + using BinningTypeMCClosure = ColumnBinningPolicy; + // same as BinningTypeMCClosure, plus Psi_FT0C (as in data's BinningTypeVertexContributor), for the reconstructed-EP closure ME + using BinningTypeMCClosureRecoEP = ColumnBinningPolicy; SliceCache cache; // Partition posTracks = aod::track::signed1Pt > cfgCutCharge; @@ -235,6 +280,9 @@ struct Kstarpbpb { if (cfgSAFrame.value < kEventPlane || cfgSAFrame.value > kRandomEventPlane) { LOGF(fatal, "cfgSAFrame = %d not supported (0 = event plane, 1 = production plane, 2 = random event plane)", cfgSAFrame.value); } + if (cfgSAFrame.value != kProductionPlane && cfgEPNormalHarmonic.value != 1.0f) { + LOGF(warning, "cfgEPNormalHarmonic = %.1f: the SA axis is not the normal to the Psi2 plane, so the R2 = resolution correction does not close", cfgEPNormalHarmonic.value); + } rndGen.SetSeed(cfgRndSeed); AxisSpec phiAxis = {500, -6.28, 6.28, "phi"}; @@ -286,6 +334,37 @@ struct Kstarpbpb { histos.add("hSparseKstarMCGenCosThetaStar_effy", "hSparseKstarMCGenCosThetaStar_effy", HistType::kTHnSparseD, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}); histos.add("hSparseKstarMCRecSA", "hSparseKstarMCRecSA", HistType::kTHnSparseD, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}); histos.add("hSparseKstarMCRecCosThetaStar_effy", "hSparseKstarMCRecCosThetaStar_effy", HistType::kTHnSparseD, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}); + histos.add("hMCEffPsi", "Psi of the MC efficiency;centrality (%);#Psi", kTH2F, {configThnAxisCentrality, {36, -o2::constants::math::PI, o2::constants::math::PI}}); + if (mcEffEP.fillPhiVMinusPsi) { + const AxisSpec phiVMinusPsiAxis{mcEffEP.axisPhiVMinusPsi, "#varphi_{V} - #Psi"}; + histos.add("hSparseKstarMCGenCosThetaStarPhiVPsi", "Generated K*: p_{T}, cos#theta*, #varphi_{V} - #Psi, centrality", HistType::kTHnSparseD, {configThnAxisPt, configThnAxisV2, phiVMinusPsiAxis, configThnAxisCentrality}); + histos.add("hSparseKstarMCRecCosThetaStarPhiVPsi", "Reconstructed K*: p_{T}, cos#theta*, #varphi_{V} - #Psi, centrality", HistType::kTHnSparseD, {configThnAxisPt, configThnAxisV2, phiVMinusPsiAxis, configThnAxisCentrality}); + } + } + + if (doClosureTest) { + histos.add("closure/KstarSameEventCosThetaStar", "K*0 closure: same-event unlike-sign;M (GeV/c^{2});p_{T} (GeV/c);cos(#vartheta);|y|;centrality", HistType::kTHnSparseF, + {configThnAxisInvMass, configThnAxisPt, {20, -1.0, 1.0, "cos(#vartheta)"}, configrapAxis, configThnAxisCentrality}); + histos.add("closure/KstarMixedEventCosThetaStar", "K*0 closure: mixed-event unlike-sign;M (GeV/c^{2});p_{T} (GeV/c);cos(#vartheta);|y|;centrality", HistType::kTHnSparseF, + {configThnAxisInvMass, configThnAxisPt, {20, -1.0, 1.0, "cos(#vartheta)"}, configrapAxis, configThnAxisCentrality}); + histos.add("closure/KstarMCRecTrueCosThetaStar", "K*0 closure: true matched reconstructed;M (GeV/c^{2});p_{T} (GeV/c);cos(#vartheta);|y|;centrality", HistType::kTHnSparseF, + {configThnAxisInvMass, configThnAxisPt, {20, -1.0, 1.0, "cos(#vartheta)"}, configrapAxis, configThnAxisCentrality}); + histos.add("closure/KstarMCGenCosThetaStar", "K*0 closure: generated;M (GeV/c^{2});p_{T} (GeV/c);cos(#vartheta);|y|;centrality", HistType::kTHnSparseF, + {configThnAxisInvMass, configThnAxisPt, {20, -1.0, 1.0, "cos(#vartheta)"}, configrapAxis, configThnAxisCentrality}); + + const AxisSpec closurePhiMass{phiSA.configThnAxisInvMass, "#it{M} (GeV/#it{c}^{2})"}; + const AxisSpec closurePhiPt{phiSA.configThnAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec closurePhiCos{20, -1.0, 1.0, "cos(#vartheta)"}; + const AxisSpec closurePhiRap{phiSA.configThnAxisRapidity, "|y|"}; + const AxisSpec closurePhiCent{phiSA.configThnAxisCentrality, "Centrality (%)"}; + histos.add("closure/PhiSameEventCosThetaStar", "phi closure: same-event unlike-sign;M (GeV/c^{2});p_{T} (GeV/c);cos(#vartheta);|y|;centrality", HistType::kTHnSparseF, + {closurePhiMass, closurePhiPt, closurePhiCos, closurePhiRap, closurePhiCent}); + histos.add("closure/PhiMixedEventCosThetaStar", "phi closure: mixed-event unlike-sign;M (GeV/c^{2});p_{T} (GeV/c);cos(#vartheta);|y|;centrality", HistType::kTHnSparseF, + {closurePhiMass, closurePhiPt, closurePhiCos, closurePhiRap, closurePhiCent}); + histos.add("closure/PhiMCRecTrueCosThetaStar", "phi closure: true matched reconstructed;M (GeV/c^{2});p_{T} (GeV/c);cos(#vartheta);|y|;centrality", HistType::kTHnSparseF, + {closurePhiMass, closurePhiPt, closurePhiCos, closurePhiRap, closurePhiCent}); + histos.add("closure/PhiMCGenCosThetaStar", "phi closure: generated;M (GeV/c^{2});p_{T} (GeV/c);cos(#vartheta);|y|;centrality", HistType::kTHnSparseF, + {closurePhiMass, closurePhiPt, closurePhiCos, closurePhiRap, closurePhiCent}); } if (doprocessMCkstarWeight) { @@ -380,6 +459,16 @@ struct Kstarpbpb { if (doprocessSEPhi) { histos.add("phi/hSparseV2SameEventSA", "hSparseV2SameEventSA", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisSAPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); histos.add("phi/hSparseV2SameEventCosThetaStar", "hSparseV2SameEventCosThetaStar", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisCosThetaStarPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + if (phiSA.fillRotation) { + histos.add("phi/hSparseV2RotationSA", "hSparseV2RotationSA", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisSAPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + histos.add("phi/hSparseV2RotationCosThetaStar", "hSparseV2RotationCosThetaStar", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisCosThetaStarPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + } + if (phiSA.fillLikeSign) { + histos.add("phi/hSparseV2SameEventLikeSA", "hSparseV2SameEventLikeSA", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisSAPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + histos.add("phi/hSparseV2SameEventLikeCosThetaStar", "hSparseV2SameEventLikeCosThetaStar", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisCosThetaStarPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + histos.add("phi/hMassSameEventLikePP", "Same-event like-sign (++) mass", kTH2F, {thnAxisInvMassPhi, thnAxisCentralityPhi}); + histos.add("phi/hMassSameEventLikeNN", "Same-event like-sign (--) mass", kTH2F, {thnAxisInvMassPhi, thnAxisCentralityPhi}); + } } if (doprocessMEPhi) { histos.add("phi/hSparseV2MixedEventSA", "hSparseV2MixedEventSA", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisSAPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); @@ -393,6 +482,12 @@ struct Kstarpbpb { histos.add("phi/hSparseV2MCGenCosThetaStar_effy", "hSparseV2SameEventCosThetaStar_effy", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisCosThetaStarPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); histos.add("phi/hSparseV2MCRecSA", "hSparseV2SameEventSA", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisSAPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); histos.add("phi/hSparseV2MCRecCosThetaStar_effy", "hSparseV2SameEventCosThetaStar_effy", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisCosThetaStarPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + histos.add("phi/hMCEffPsi", "Psi of the MC efficiency;centrality (%);#Psi", kTH2F, {thnAxisCentralityPhi, {36, -o2::constants::math::PI, o2::constants::math::PI}}); + if (mcEffEP.fillPhiVMinusPsi) { + const AxisSpec phiVMinusPsiAxis{mcEffEP.axisPhiVMinusPsi, "#varphi_{V} - #Psi"}; + histos.add("phi/hSparseV2MCGenCosThetaStarPhiVPsi", "Generated #phi: p_{T}, cos#theta*, #varphi_{V} - #Psi, centrality", HistType::kTHnSparseF, {thnAxisPtPhi, thnAxisCosThetaStarPhi, phiVMinusPsiAxis, thnAxisCentralityPhi}); + histos.add("phi/hSparseV2MCRecCosThetaStarPhiVPsi", "Reconstructed #phi: p_{T}, cos#theta*, #varphi_{V} - #Psi, centrality", HistType::kTHnSparseF, {thnAxisPtPhi, thnAxisCosThetaStarPhi, phiVMinusPsiAxis, thnAxisCentralityPhi}); + } } } @@ -431,6 +526,128 @@ struct Kstarpbpb { histos.add("evtSigLoss/hPhiGenAfterEvtSelVsCent", "Generated phi, events with selected reco event", kTH2F, {ptPhiAxis, centLossAxis}); } + // MC closure test of the cos(theta*) chain + if (doprocessMCClosure || doprocessMCClosureME || doprocessMCClosurePhi || doprocessMCClosureMEPhi) { + if (closure.rho00Inj.value < 0.f || closure.rho00Inj.value > 1.f) { + LOGF(fatal, "closure.rho00Inj = %f is outside [0,1]", closure.rho00Inj.value); + } + const AxisSpec cosAxisClosure{closure.axisCosThetaStar, "cos(#vartheta*)"}; + const AxisSpec cosFineAxis{100, -1.0, 1.0, "cos(#vartheta*)"}; + if (doprocessMCClosure || doprocessMCClosureME) { + const AxisSpec massAxis{configThnAxisInvMass, "#it{M}_{K#pi} (GeV/#it{c}^{2})"}; + const AxisSpec ptAxis{configThnAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec centAxisClosureKstar{configThnAxisCentrality, "Centrality (%)"}; + if (doprocessMCClosure) { + // bins: 0 all MC events, 1 no reco event, 2 more than one reco event, 3 selected (eff sample), 4 selected (test sample) + histos.add("closureKstar/hMC", "MC event statistics", kTH1F, {{5, 0.0f, 5.0f}}); + // Acc x Eff sample + histos.add("closureKstar/hSparseGenEff", "Generated K*, efficiency sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + histos.add("closureKstar/hSparseRecTrueEff", "Reconstructed true K*, efficiency sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + // test sample + histos.add("closureKstar/hSparseGen", "Generated K* (input), test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + histos.add("closureKstar/hSparseRecTrue", "Reconstructed true K*, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + histos.add("closureKstar/hSparseSE", "Same-event unlike-sign K#pi pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + if (closure.fillRotBkg) { + histos.add("closureKstar/hSparseRot", "Rotated unlike-sign K#pi pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + } + histos.add("closureKstar/hCosThetaStarGenVsRec", "cos(#vartheta*) gen vs rec, true K*", kTH2F, {cosFineAxis, cosFineAxis}); + } + if (doprocessMCClosureME) { + histos.add("closureKstar/hSparseME", "Mixed-event unlike-sign K#pi pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + } + } + if (doprocessMCClosurePhi || doprocessMCClosureMEPhi) { + const AxisSpec massAxis{phiSA.configThnAxisInvMass, "#it{M}_{KK} (GeV/#it{c}^{2})"}; + const AxisSpec ptAxis{phiSA.configThnAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec centAxisClosurePhi{phiSA.configThnAxisCentrality, "Centrality (%)"}; + if (doprocessMCClosurePhi) { + histos.add("closurePhi/hMC", "MC event statistics", kTH1F, {{5, 0.0f, 5.0f}}); + histos.add("closurePhi/hSparseGenEff", "Generated phi, efficiency sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + histos.add("closurePhi/hSparseRecTrueEff", "Reconstructed true phi, efficiency sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + histos.add("closurePhi/hSparseGen", "Generated phi (input), test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + histos.add("closurePhi/hSparseRecTrue", "Reconstructed true phi, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + histos.add("closurePhi/hSparseSE", "Same-event K^{+}K^{-} pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + if (closure.fillRotBkg) { + histos.add("closurePhi/hSparseRot", "Rotated K^{+}K^{-} pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + } + histos.add("closurePhi/hCosThetaStarGenVsRec", "cos(#vartheta*) gen vs rec, true phi", kTH2F, {cosFineAxis, cosFineAxis}); + } + if (doprocessMCClosureMEPhi) { + histos.add("closurePhi/hSparseME", "Mixed-event K^{+}K^{-} pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + } + } + } + + // MC closure test of the cos(theta*) chain, using the reconstructed (finite-resolution) Psi_FT0C instead of the MC true event plane. + // Needs EPCalibrationTables on the MC sample (i.e. the epvector producer run on it), unlike the true-EP closure above. + if (doprocessMCClosureRecoEP || doprocessMCClosureMERecoEP || doprocessMCClosureRecoEPPhi || doprocessMCClosureMERecoEPPhi) { + if (closure.rho00Inj.value < 0.f || closure.rho00Inj.value > 1.f) { + LOGF(fatal, "closure.rho00Inj = %f is outside [0,1]", closure.rho00Inj.value); + } + const AxisSpec cosAxisClosure{closure.axisCosThetaStar, "cos(#vartheta*)"}; + const AxisSpec cosFineAxis{100, -1.0, 1.0, "cos(#vartheta*)"}; + if (doprocessMCClosureRecoEP || doprocessMCClosureMERecoEP) { + const AxisSpec massAxis{configThnAxisInvMass, "#it{M}_{K#pi} (GeV/#it{c}^{2})"}; + const AxisSpec ptAxis{configThnAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec centAxisClosureKstar{configThnAxisCentrality, "Centrality (%)"}; + if (doprocessMCClosureRecoEP) { + // bins: 0 all MC events, 1 no reco event, 2 more than one reco event, 3 selected (eff sample), 4 selected (test sample) + histos.add("closureKstarRecoEP/hMC", "MC event statistics", kTH1F, {{5, 0.0f, 5.0f}}); + // Acc x Eff sample + histos.add("closureKstarRecoEP/hSparseGenEff", "Generated K*, efficiency sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + histos.add("closureKstarRecoEP/hSparseRecTrueEff", "Reconstructed true K*, efficiency sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + // test sample + histos.add("closureKstarRecoEP/hSparseGen", "Generated K* (input), test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + histos.add("closureKstarRecoEP/hSparseRecTrue", "Reconstructed true K*, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + histos.add("closureKstarRecoEP/hSparseSE", "Same-event unlike-sign K#pi pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + if (closure.fillRotBkg) { + histos.add("closureKstarRecoEP/hSparseRot", "Rotated unlike-sign K#pi pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + } + histos.add("closureKstarRecoEP/hCosThetaStarGenVsRec", "cos(#vartheta*) gen vs rec, true K*", kTH2F, {cosFineAxis, cosFineAxis}); + // sub-event resolution of the closure sample, needed to derive R2 and un-smear rho00 downstream; same definition as the data ResFT0C*SP + histos.add("closureKstarRecoEP/ResFT0CTPCSP", "ResFT0CTPCSP", kTH2F, {centAxis, resAxis}); + histos.add("closureKstarRecoEP/ResFT0CFT0ASP", "ResFT0CFT0ASP", kTH2F, {centAxis, resAxis}); + histos.add("closureKstarRecoEP/ResFT0ATPCSP", "ResFT0ATPCSP", kTH2F, {centAxis, resAxis}); + // plain (unweighted) sub-event resolution, as needed by the EP-method correction formula; same definition as the data ResFT0CTPC etc. + histos.add("closureKstarRecoEP/ResFT0CTPC", "ResFT0CTPC", kTH2F, {centAxis, resAxis}); + histos.add("closureKstarRecoEP/ResFT0CFT0A", "ResFT0CFT0A", kTH2F, {centAxis, resAxis}); + histos.add("closureKstarRecoEP/ResFT0ATPC", "ResFT0ATPC", kTH2F, {centAxis, resAxis}); + // closure-only: Psi_FT0C vs the MC true event plane, to cross-check R from sub-events against the true R + histos.add("closureKstarRecoEP/ResTrueFT0C", "ResTrueFT0C", kTH2F, {centAxis, resAxis}); + } + if (doprocessMCClosureMERecoEP) { + histos.add("closureKstarRecoEP/hSparseME", "Mixed-event unlike-sign K#pi pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + } + } + if (doprocessMCClosureRecoEPPhi || doprocessMCClosureMERecoEPPhi) { + const AxisSpec massAxis{phiSA.configThnAxisInvMass, "#it{M}_{KK} (GeV/#it{c}^{2})"}; + const AxisSpec ptAxis{phiSA.configThnAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec centAxisClosurePhi{phiSA.configThnAxisCentrality, "Centrality (%)"}; + if (doprocessMCClosureRecoEPPhi) { + histos.add("closurePhiRecoEP/hMC", "MC event statistics", kTH1F, {{5, 0.0f, 5.0f}}); + histos.add("closurePhiRecoEP/hSparseGenEff", "Generated phi, efficiency sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + histos.add("closurePhiRecoEP/hSparseRecTrueEff", "Reconstructed true phi, efficiency sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + histos.add("closurePhiRecoEP/hSparseGen", "Generated phi (input), test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + histos.add("closurePhiRecoEP/hSparseRecTrue", "Reconstructed true phi, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + histos.add("closurePhiRecoEP/hSparseSE", "Same-event K^{+}K^{-} pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + if (closure.fillRotBkg) { + histos.add("closurePhiRecoEP/hSparseRot", "Rotated K^{+}K^{-} pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + } + histos.add("closurePhiRecoEP/hCosThetaStarGenVsRec", "cos(#vartheta*) gen vs rec, true phi", kTH2F, {cosFineAxis, cosFineAxis}); + histos.add("closurePhiRecoEP/ResFT0CTPCSP", "ResFT0CTPCSP", kTH2F, {centAxis, resAxis}); + histos.add("closurePhiRecoEP/ResFT0CFT0ASP", "ResFT0CFT0ASP", kTH2F, {centAxis, resAxis}); + histos.add("closurePhiRecoEP/ResFT0ATPCSP", "ResFT0ATPCSP", kTH2F, {centAxis, resAxis}); + histos.add("closurePhiRecoEP/ResFT0CTPC", "ResFT0CTPC", kTH2F, {centAxis, resAxis}); + histos.add("closurePhiRecoEP/ResFT0CFT0A", "ResFT0CFT0A", kTH2F, {centAxis, resAxis}); + histos.add("closurePhiRecoEP/ResFT0ATPC", "ResFT0ATPC", kTH2F, {centAxis, resAxis}); + histos.add("closurePhiRecoEP/ResTrueFT0C", "ResTrueFT0C", kTH2F, {centAxis, resAxis}); + } + if (doprocessMCClosureMERecoEPPhi) { + histos.add("closurePhiRecoEP/hSparseME", "Mixed-event K^{+}K^{-} pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + } + } + } + ccdb->setURL(cfgCcdbParam.cfgURL); ccdbApi.init("http://alice-ccdb.cern.ch"); ccdb->setCaching(true); @@ -689,14 +906,47 @@ struct Kstarpbpb { } // phipbpb.cxx builds the EP-frame MC efficiency with the in-plane vector at Psi = 0 (x axis); kept for identical results + // with mcEffEP.psiSource = 0 only. With an event-by-event Psi the axis must be the plane normal, as in data (getSAAxis). ROOT::Math::XYZVector getSAAxisPhiMC(const ROOT::Math::PxPyPzMVector& mother, double psiSA) { - if (cfgSAFrame.value == kEventPlane) { + if (cfgSAFrame.value == kEventPlane && mcEffEP.psiSource == 0) { return {std::cos(2.0 * psiSA), std::sin(2.0 * psiSA), 0.}; } return getSAAxis(mother, psiSA); } + // Psi2 = atan2(Q2y, Q2x) / 2 from the generated charged physical primaries in [etaMin, etaMax] (thesis Eq. B.16, unit weights) + template + double getGenPsi2(TParticles const& particles) + { + double qx = 0., qy = 0.; + for (const auto& particle : particles) { + if (!particle.isPhysicalPrimary() || particle.eta() < mcEffEP.etaMin || particle.eta() > mcEffEP.etaMax) { + continue; + } + const int absPdg = std::abs(particle.pdgCode()); + if (absPdg != PDG_t::kPiPlus && absPdg != PDG_t::kKPlus && absPdg != PDG_t::kProton && absPdg != PDG_t::kElectron && absPdg != PDG_t::kMuonMinus) { + continue; + } + qx += std::cos(2.0 * particle.phi()); + qy += std::sin(2.0 * particle.phi()); + } + return 0.5 * std::atan2(qy, qx); + } + + // Psi of the EP-frame MC efficiency, one per MC event (see mcEffEP) + template + double getMCEffPsi(TMcCollision const& mcCollision, TParticles const& particles) + { + if (mcEffEP.psiSource == 1) { + return getGenPsi2(particles); + } + if (mcEffEP.psiSource == 2) { + return mcCollision.eventPlaneAngle(); + } + return 0.0; + } + template bool isFakeKaon(T const& track, int /*PID*/) { @@ -893,6 +1143,10 @@ struct Kstarpbpb { auto sa = std::cos(2.0 * cosPhistarminuspsi); auto cosThetaStar = eventplaneVecNorm.Dot(threeVecDauCM) / std::sqrt(threeVecDauCM.Mag2()) / std::sqrt(eventplaneVecNorm.Mag2()); + if (doClosureTest && track1Sign * track2Sign < 0) { + histos.fill(HIST("closure/KstarSameEventCosThetaStar"), kstarMother.M(), kstarMother.Pt(), cosThetaStar, std::abs(kstarMother.Rapidity()), centrality); + } + if (track1Sign * track2Sign < 0) { if (usepolar) { histos.fill(HIST("hSparseSAvsrapsameunlike"), kstarMother.M(), kstarMother.Pt(), cosThetaStar, kstarMother.Rapidity(), centrality); @@ -970,7 +1224,7 @@ struct Kstarpbpb { double v2 = 0.; auto tracksTuple = std::make_tuple(tracks); - BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}, true}; + BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}}; SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (const auto& [collision1, tracks1, collision2, tracks2] : pair) { if (!selectionEventPairME(collision1, collision2)) { @@ -1040,6 +1294,9 @@ struct Kstarpbpb { auto cosPhistarminuspsi = getPhiInRange(fourVecDauCM.Phi() - getSAPlaneAngle(kstarMother, psiSA1)); auto sa = std::cos(2.0 * cosPhistarminuspsi); auto cosThetaStar = eventplaneVecNorm.Dot(threeVecDauCM) / std::sqrt(threeVecDauCM.Mag2()) / std::sqrt(eventplaneVecNorm.Mag2()); + if (doClosureTest) { + histos.fill(HIST("closure/KstarMixedEventCosThetaStar"), kstarMother.M(), kstarMother.Pt(), cosThetaStar, std::abs(kstarMother.Rapidity()), centrality); + } if (usepolar) { histos.fill(HIST("hSparseSAvsrapmix"), kstarMother.M(), kstarMother.Pt(), cosThetaStar, kstarMother.Rapidity(), centrality); } else { @@ -1059,7 +1316,7 @@ struct Kstarpbpb { } PROCESS_SWITCH(Kstarpbpb, processMixedEvent, "Process Mixed event", true); - void processMC(CollisionMCTrueTable::iterator const& /*TrueCollision*/, CollisionMCRecTableCentFT0C const& RecCollisions, TrackMCTrueTable const& GenParticles, FilTrackMCRecTable const& RecTracks) + void processMC(CollisionMCTrueTable::iterator const& TrueCollision, CollisionMCRecTableCentFT0C const& RecCollisions, TrackMCTrueTable const& GenParticles, FilTrackMCRecTable const& RecTracks) { // scratch vectors: local to this call, never carry state across particles ROOT::Math::PxPyPzMVector kstarMother, kaonPlus, pionMinus; @@ -1075,7 +1332,7 @@ struct Kstarpbpb { return; } for (const auto& RecCollision : RecCollisions) { - auto psiFT0C = 0.0; + auto psiFT0C = getMCEffPsi(TrueCollision, GenParticles); // one Psi per MC event (mcEffEP.psiSource; 0 = old fixed Psi = 0) histos.fill(HIST("hMC"), 3); if (!RecCollision.sel8()) { histos.fill(HIST("hMC"), 4); @@ -1093,6 +1350,7 @@ struct Kstarpbpb { auto centrality = RecCollision.centFT0C(); histos.fill(HIST("CentPercentileMCRecHist"), centrality); auto psiSA = getSAEventAngle(psiFT0C); // same angle for rec and gen of this event + histos.fill(HIST("hMCEffPsi"), centrality, psiSA); auto oldindex = -999; auto rectrackspart = RecTracks.sliceBy(perCollision, RecCollision.globalIndex()); // loop over reconstructed particle @@ -1200,6 +1458,12 @@ struct Kstarpbpb { histos.fill(HIST("hSparseKstarMCRecSA"), kstarMother.M(), kstarMother.Pt(), saRec, std::abs(kstarMother.Rapidity()), centrality); histos.fill(HIST("hSparseKstarMCRecCosThetaStar_effy"), kstarMother.M(), kstarMother.Pt(), cosThetaStarRec, std::abs(kstarMother.Rapidity()), centrality); + if (mcEffEP.fillPhiVMinusPsi) { + histos.fill(HIST("hSparseKstarMCRecCosThetaStarPhiVPsi"), kstarMother.Pt(), cosThetaStarRec, getPhiInRange(kstarMother.Phi() - psiSA), centrality); + } + if (doClosureTest) { + histos.fill(HIST("closure/KstarMCRecTrueCosThetaStar"), kstarMother.M(), kstarMother.Pt(), cosThetaStarRec, std::abs(kstarMother.Rapidity()), centrality); + } } } } @@ -1269,6 +1533,12 @@ struct Kstarpbpb { histos.fill(HIST("hSparseKstarMCGenSA"), kstarMother.M(), kstarMother.Pt(), saGen, std::abs(kstarMother.Rapidity()), centrality); histos.fill(HIST("hSparseKstarMCGenCosThetaStar_effy"), kstarMother.M(), kstarMother.Pt(), cosThetaStarGen, std::abs(kstarMother.Rapidity()), centrality); + if (mcEffEP.fillPhiVMinusPsi) { + histos.fill(HIST("hSparseKstarMCGenCosThetaStarPhiVPsi"), kstarMother.Pt(), cosThetaStarGen, getPhiInRange(kstarMother.Phi() - psiSA), centrality); + } + if (doClosureTest) { + histos.fill(HIST("closure/KstarMCGenCosThetaStar"), kstarMother.M(), kstarMother.Pt(), cosThetaStarGen, std::abs(kstarMother.Rapidity()), centrality); + } } } } @@ -1469,7 +1739,7 @@ struct Kstarpbpb { histos.fill(HIST("phi/hPsiRandom"), centrality, psiSA); } - ROOT::Math::PxPyPzMVector kaonPlusPhi, kaonMinusPhi, phiMother; + ROOT::Math::PxPyPzMVector kaonPlusPhi, kaonMinusPhi, phiMother, kaonPlusRot, phiRot; for (const auto& track1 : tracks) { if (!(track1.signed1Pt() > cfgCutCharge.value)) { // positive kaon continue; @@ -1498,8 +1768,73 @@ struct Kstarpbpb { continue; } auto [cosThetaStar, sa] = getSAValuesPhi(phiMother, kaonMinusPhi, getSAAxis(phiMother, psiSA), psiSA); + if (doClosureTest) { + histos.fill(HIST("closure/PhiSameEventCosThetaStar"), phiMother.M(), phiMother.Pt(), cosThetaStar, absRapidity, centrality); + } histos.fill(HIST("phi/hSparseV2SameEventSA"), phiMother.M(), phiMother.Pt(), sa, absRapidity, centrality); histos.fill(HIST("phi/hSparseV2SameEventCosThetaStar"), phiMother.M(), phiMother.Pt(), cosThetaStar, absRapidity, centrality); + + // rotated background, same rotation as for K* in processSE: rotate the K+, cos(theta*) of the K- in the rotated pair + if (phiSA.fillRotation && nBkgRotations.value > 1) { + const double angleStart = confMinRot.value; + const double angleStep = (confMaxRot.value - angleStart) / (nBkgRotations.value - 1.0); + for (int irot = 0; irot < nBkgRotations.value; irot++) { + const double rotAngle = angleStart + irot * angleStep; + kaonPlusRot = ROOT::Math::PxPyPzMVector(track1.px() * std::cos(rotAngle) - track1.py() * std::sin(rotAngle), track1.px() * std::sin(rotAngle) + track1.py() * std::cos(rotAngle), track1.pz(), MassKa); + phiRot = kaonPlusRot + kaonMinusPhi; + auto absRapidityRot = std::abs(phiRot.Rapidity()); + if (absRapidityRot > phiSA.confRapidity) { + continue; + } + // production-plane axis must follow the rotated candidate + auto [cosThetaStarRot, saRot] = getSAValuesPhi(phiRot, kaonMinusPhi, getSAAxis(phiRot, psiSA), psiSA); + histos.fill(HIST("phi/hSparseV2RotationSA"), phiRot.M(), phiRot.Pt(), saRot, absRapidityRot, centrality); + histos.fill(HIST("phi/hSparseV2RotationCosThetaStar"), phiRot.M(), phiRot.Pt(), cosThetaStarRot, absRapidityRot, centrality); + } + } + } + } + + // like-sign background (K+K+ and K-K-): same track, PID and pair cuts as the unlike-sign pairs, each pair taken once; + // cos(theta*) of the first kaon of the pair (the two kaons are back to back in the pair rest frame, so the choice only flips the sign) + if (phiSA.fillLikeSign) { + ROOT::Math::PxPyPzMVector kaonLike1, kaonLike2, pairLike; + for (const auto& track1 : tracks) { + if (!selectionTrackPhi(track1) || !selectionPIDPhi(track1)) { + continue; + } + for (const auto& track2 : tracks) { + if (track2.globalIndex() <= track1.globalIndex()) { + continue; + } + if (track1.sign() * track2.sign() <= 0) { + continue; + } + if (!selectionTrackPhi(track2) || !selectionPIDPhi(track2)) { + continue; + } + if (!selectionPairPhi(track1, track2)) { + continue; + } + if (phiSA.removeFakeTrack && (isFakeKaonPhi(track1) || isFakeKaonPhi(track2))) { + continue; + } + kaonLike1 = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + kaonLike2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); + pairLike = kaonLike1 + kaonLike2; + auto absRapidityLike = std::abs(pairLike.Rapidity()); + if (absRapidityLike > phiSA.confRapidity) { + continue; + } + auto [cosThetaStarLike, saLike] = getSAValuesPhi(pairLike, kaonLike1, getSAAxis(pairLike, psiSA), psiSA); + histos.fill(HIST("phi/hSparseV2SameEventLikeSA"), pairLike.M(), pairLike.Pt(), saLike, absRapidityLike, centrality); + histos.fill(HIST("phi/hSparseV2SameEventLikeCosThetaStar"), pairLike.M(), pairLike.Pt(), cosThetaStarLike, absRapidityLike, centrality); + if (track1.sign() > 0) { + histos.fill(HIST("phi/hMassSameEventLikePP"), pairLike.M(), centrality); + } else { + histos.fill(HIST("phi/hMassSameEventLikeNN"), pairLike.M(), centrality); + } + } } } } @@ -1508,7 +1843,7 @@ struct Kstarpbpb { void processMEPhi(EventCandidates const& collisions, TrackCandidates const& tracks) { auto tracksTuple = std::make_tuple(tracks); - BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}, true}; + BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}}; SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; ROOT::Math::PxPyPzMVector kaonPlusPhi, kaonMinusPhi, phiMother; for (const auto& [collision1, tracks1, collision2, tracks2] : pair) { @@ -1546,6 +1881,9 @@ struct Kstarpbpb { continue; } auto [cosThetaStar, sa] = getSAValuesPhi(phiMother, kaonMinusPhi, getSAAxis(phiMother, psiSA), psiSA); + if (doClosureTest) { + histos.fill(HIST("closure/PhiMixedEventCosThetaStar"), phiMother.M(), phiMother.Pt(), cosThetaStar, absRapidity, centrality); + } histos.fill(HIST("phi/hSparseV2MixedEventSA"), phiMother.M(), phiMother.Pt(), sa, absRapidity, centrality); histos.fill(HIST("phi/hSparseV2MixedEventCosThetaStar"), phiMother.M(), phiMother.Pt(), cosThetaStar, absRapidity, centrality); } @@ -1553,7 +1891,7 @@ struct Kstarpbpb { } PROCESS_SWITCH(Kstarpbpb, processMEPhi, "Process mixed event phi(1020) spin alignment", false); - void processMCPhi(CollisionMCTrueTable::iterator const& /*TrueCollision*/, CollisionMCRecTableCentFT0C const& RecCollisions, TrackMCTrueTable const& GenParticles, FilTrackMCRecTable const& RecTracks) + void processMCPhi(CollisionMCTrueTable::iterator const& TrueCollision, CollisionMCRecTableCentFT0C const& RecCollisions, TrackMCTrueTable const& GenParticles, FilTrackMCRecTable const& RecTracks) { histos.fill(HIST("phi/hMC"), 0); if (RecCollisions.size() == 0) { @@ -1566,7 +1904,7 @@ struct Kstarpbpb { } ROOT::Math::PxPyPzMVector kaonPlusPhi, kaonMinusPhi, phiMother; for (const auto& RecCollision : RecCollisions) { - auto psiFT0C = 0.0; + auto psiFT0C = getMCEffPsi(TrueCollision, GenParticles); // one Psi per MC event (mcEffEP.psiSource; 0 = old fixed Psi = 0) histos.fill(HIST("phi/hMC"), 3); if (!RecCollision.sel8()) { histos.fill(HIST("phi/hMC"), 4); @@ -1584,6 +1922,7 @@ struct Kstarpbpb { auto centrality = RecCollision.centFT0C(); histos.fill(HIST("phi/CentPercentileMCRecHist"), centrality); auto psiSA = getSAEventAngle(psiFT0C); // same angle for rec and gen of this event + histos.fill(HIST("phi/hMCEffPsi"), centrality, psiSA); auto oldindex = -999; auto rectrackspart = RecTracks.sliceBy(perCollision, RecCollision.globalIndex()); // loop over reconstructed particle @@ -1659,6 +1998,12 @@ struct Kstarpbpb { } auto [cosThetaStar, sa] = getSAValuesPhi(phiMother, kaonMinusPhi, getSAAxisPhiMC(phiMother, psiSA), psiSA); histos.fill(HIST("phi/hSparseV2MCRecCosThetaStar_effy"), phiMother.M(), phiMother.Pt(), cosThetaStar, std::abs(phiMother.Rapidity()), centrality); + if (mcEffEP.fillPhiVMinusPsi) { + histos.fill(HIST("phi/hSparseV2MCRecCosThetaStarPhiVPsi"), phiMother.Pt(), cosThetaStar, getPhiInRange(phiMother.Phi() - psiSA), centrality); + } + if (doClosureTest) { + histos.fill(HIST("closure/PhiMCRecTrueCosThetaStar"), phiMother.M(), phiMother.Pt(), cosThetaStar, std::abs(phiMother.Rapidity()), centrality); + } histos.fill(HIST("phi/hSparseV2MCRecSA"), phiMother.M(), phiMother.Pt(), sa, std::abs(phiMother.Rapidity()), centrality); } } @@ -1696,6 +2041,12 @@ struct Kstarpbpb { phiMother = kaonPlusPhi + kaonMinusPhi; auto [cosThetaStar, sa] = getSAValuesPhi(phiMother, kaonMinusPhi, getSAAxisPhiMC(phiMother, psiSA), psiSA); histos.fill(HIST("phi/hSparseV2MCGenCosThetaStar_effy"), phiMother.M(), phiMother.Pt(), cosThetaStar, std::abs(phiMother.Rapidity()), centrality); + if (mcEffEP.fillPhiVMinusPsi) { + histos.fill(HIST("phi/hSparseV2MCGenCosThetaStarPhiVPsi"), phiMother.Pt(), cosThetaStar, getPhiInRange(phiMother.Phi() - psiSA), centrality); + } + if (doClosureTest) { + histos.fill(HIST("closure/PhiMCGenCosThetaStar"), phiMother.M(), phiMother.Pt(), cosThetaStar, std::abs(phiMother.Rapidity()), centrality); + } histos.fill(HIST("phi/hSparseV2MCGenSA"), phiMother.M(), phiMother.Pt(), sa, std::abs(phiMother.Rapidity()), centrality); } } @@ -1814,6 +2165,1045 @@ struct Kstarpbpb { } } PROCESS_SWITCH(Kstarpbpb, processEvtLossSigLossMC, "Process event loss and signal loss for K* and phi(1020)", false); + + // ================= MC closure test of the cos(theta*) chain ================= + // Test sample (reconstructed MC analysed like data): + // hSparseSE all same-event unlike-sign pairs (signal + background), true pairs weighted by W + // hSparseME mixed-event unlike-sign pairs (weight 1) -> step 1: SE - norm * ME + // hSparseRecTrue reconstructed true K* (phi), weighted by W -> step 2: truth for the raw yield + // hSparseGen generated K* (phi), daughters in acceptance, weighted -> step 4: input yield and rho00 + // Efficiency sample (independent events if closure.splitSample): + // hSparseRecTrueEff / hSparseGenEff = Acc x Eff(cos theta*) -> step 3 + // W(cos) = 3/2 [(1 - rho00) + (3 rho00 - 1) cos^2] with the GENERATED cos(theta*) (W = 1 if injectPolarization is off) + + // W(cos) = 3/2 [(1 - rho) + (3 rho - 1) cos^2]; = 1 over cos in [-1,1]; W = 1 for rho = 1/3 + double getInjectionWeight(double cosThetaStar) + { + if (!closure.injectPolarization) { + return 1.0; + } + const double rho = closure.rho00Inj.value; + return 1.5 * ((1.0 - rho) + (3.0 * rho - 1.0) * cosThetaStar * cosThetaStar); + } + + // cos(theta*) of 'dau' in the rest frame of 'mother' with respect to 'axis' + double getCosThetaStar(const ROOT::Math::PxPyPzMVector& mother, const ROOT::Math::PxPyPzMVector& dau, const ROOT::Math::XYZVector& axis) + { + ROOT::Math::Boost boost{mother.BoostToCM()}; + auto threeVecDau = boost(dau).Vect(); + return axis.Dot(threeVecDau) / std::sqrt(threeVecDau.Mag2()) / std::sqrt(axis.Mag2()); + } + + // one Psi per MC event (call once per event: the random-EP option draws a new angle at each call) + double getClosurePsi(double trueEventPlaneAngle) + { + return getSAEventAngle(closure.useTrueEP ? trueEventPlaneAngle : 0.0); + } + + // even MC events -> Acc x Eff sample, odd MC events -> test sample (both if splitSample is off) + static constexpr int64_t SplitSampleModulus = 2; + bool isClosureEffSample(int64_t mcCollisionIndex) + { + return !closure.splitSample || (mcCollisionIndex % SplitSampleModulus == 0); + } + bool isClosureTestSample(int64_t mcCollisionIndex) + { + return !closure.splitSample || (mcCollisionIndex % SplitSampleModulus == 1); + } + + // track enters the SE / ME / rotated pairs: always if requirePrimaryTracks is off, else only if matched to a physical primary + template + bool selectionClosurePrimary(const TTrack& track) + { + return !closure.requirePrimaryTracks || (track.has_mcParticle() && track.mcParticle().isPhysicalPrimary()); + } + + // number of reco collisions per MC collision (for the single-reco requirement of the ME, same as the SE) + std::vector countRecoPerMcCollision(aod::McCollisionLabels const& mcLabels, int64_t nMcCollisions) + { + std::vector nRecPerMc(nMcCollisions, 0); + for (const auto& label : mcLabels) { + if (label.mcCollisionId() >= 0 && label.mcCollisionId() < nMcCollisions) { + nRecPerMc[label.mcCollisionId()]++; + } + } + return nRecPerMc; + } + + // mixed-event pair of the closure test sample: same event class as the SE + template + bool selectionClosurePairME(const TCollision& collision1, const TCollision& collision2, const std::vector& nRecPerMc) + { + if (!selectionEventMC(collision1) || !selectionEventMC(collision2) || collision1.bcId() == collision2.bcId()) { + return false; + } + if (!collision1.has_mcCollision() || !collision2.has_mcCollision()) { + return false; + } + if (!isClosureTestSample(collision1.mcCollisionId()) || !isClosureTestSample(collision2.mcCollisionId())) { + return false; + } + if (closure.meRequireSingleReco && (nRecPerMc[collision1.mcCollisionId()] != 1 || nRecPerMc[collision2.mcCollisionId()] != 1)) { + return false; + } + return true; + } + + // reco-event selection of processMC / processMCPhi, plus the data centrality cut (identical for SE and ME) + template + bool selectionEventMC(const TCollision& collision) + { + return collision.sel8() && selectionEventBits(collision) && std::abs(collision.posZ()) < cfgCutVertex && std::abs(collision.centFT0C()) < cfgCutCentrality; + } + + // reco-event selection of the reconstructed-Psi_FT0C closure: selectionEventMC plus the EP-calibration trigger required + // on data (processSE / processSEPhi via selectionEventCuts), only available once EPCalibrationTables is joined + template + bool selectionEventMCRecoEP(const TCollision& collision) + { + return selectionEventMC(collision) && collision.triggereventep(); + } + + // mixed-event pair of the reconstructed-Psi_FT0C closure test sample: same as selectionClosurePairME, with the EP trigger + template + bool selectionClosurePairMERecoEP(const TCollision& collision1, const TCollision& collision2, const std::vector& nRecPerMc) + { + if (!selectionEventMCRecoEP(collision1) || !selectionEventMCRecoEP(collision2) || collision1.bcId() == collision2.bcId()) { + return false; + } + if (!collision1.has_mcCollision() || !collision2.has_mcCollision()) { + return false; + } + if (!isClosureTestSample(collision1.mcCollisionId()) || !isClosureTestSample(collision2.mcCollisionId())) { + return false; + } + if (closure.meRequireSingleReco && (nRecPerMc[collision1.mcCollisionId()] != 1 || nRecPerMc[collision2.mcCollisionId()] != 1)) { + return false; + } + return true; + } + + // K pi pair from the decay of one primary K*0 / anti-K*0 (|y| < confRapidity) + template + bool isTrueKstarPair(const TTrack& trackKaon, const TTrack& trackPion) + { + if (!trackKaon.has_mcParticle() || !trackPion.has_mcParticle()) { + return false; + } + const auto mcKaon = trackKaon.mcParticle(); + const auto mcPion = trackPion.mcParticle(); + if (!mcKaon.isPhysicalPrimary() || !mcPion.isPhysicalPrimary()) { + return false; + } + if (std::abs(mcKaon.pdgCode()) != PDG_t::kKPlus || std::abs(mcPion.pdgCode()) != PDG_t::kPiPlus) { + return false; + } + for (const auto& motherKaon : mcKaon.template mothers_as()) { + for (const auto& motherPion : mcPion.template mothers_as()) { + if (motherKaon != motherPion) { + continue; + } + if (std::abs(motherKaon.pdgCode()) != o2::constants::physics::kK0Star892) { + continue; + } + if (std::abs(motherKaon.y()) > confRapidity) { + continue; + } + return true; + } + } + return false; + } + + // K+ K- pair from the decay of one primary phi(1020) (|y| < phiSA.confRapidity) + template + bool isTruePhiPair(const TTrack& trackPlus, const TTrack& trackMinus) + { + if (!trackPlus.has_mcParticle() || !trackMinus.has_mcParticle()) { + return false; + } + const auto mcPlus = trackPlus.mcParticle(); + const auto mcMinus = trackMinus.mcParticle(); + if (!mcPlus.isPhysicalPrimary() || !mcMinus.isPhysicalPrimary()) { + return false; + } + if (mcPlus.pdgCode() != PDG_t::kKPlus || mcMinus.pdgCode() != PDG_t::kKMinus) { + return false; + } + for (const auto& motherPlus : mcPlus.template mothers_as()) { + for (const auto& motherMinus : mcMinus.template mothers_as()) { + if (motherPlus != motherMinus) { + continue; + } + if (motherPlus.pdgCode() != o2::constants::physics::kPhi) { + continue; + } + if (std::abs(motherPlus.y()) > phiSA.confRapidity) { + continue; + } + return true; + } + } + return false; + } + + // ---------------- K*0: same event, true reco, generated ---------------- + // track1 = kaon candidate, track2 = pion candidate, unlike-sign, same cuts as processSE. + // The axis is built with getSAAxis, as in processMC (efficiency) and processSE (data). + void processMCClosure(CollisionMCTrueTable::iterator const& trueCollision, CollisionMCRecTableCentFT0C const& recCollisions, TrackMCTrueTable const& genParticles, FilTrackMCRecTable const& recTracks) + { + ROOT::Math::PxPyPzMVector kaon, pion, kstar, kaonRot, kstarRot, kaonGen, pionGen, kstarGen; + static constexpr std::size_t NumberOfDaughters = 2; + + histos.fill(HIST("closureKstar/hMC"), 0.5); + if (recCollisions.size() == 0) { + histos.fill(HIST("closureKstar/hMC"), 1.5); + return; + } + if (recCollisions.size() > 1) { + histos.fill(HIST("closureKstar/hMC"), 2.5); + return; + } + const bool isEffSample = isClosureEffSample(trueCollision.globalIndex()); + const bool isTestSample = isClosureTestSample(trueCollision.globalIndex()); + const double psi = getClosurePsi(trueCollision.eventPlaneAngle()); // same angle for the rec and the gen part of this event + for (const auto& recCollision : recCollisions) { + if (!selectionEventMC(recCollision)) { + continue; + } + if (isEffSample) { + histos.fill(HIST("closureKstar/hMC"), 3.5); + } + if (isTestSample) { + histos.fill(HIST("closureKstar/hMC"), 4.5); + } + const auto centrality = recCollision.centFT0C(); + auto recTracksThisColl = recTracks.sliceBy(perCollision, recCollision.globalIndex()); + + // ---- reconstructed pairs ---- + for (const auto& track1 : recTracksThisColl) { + if (!selectionTrack(track1) || !selectionClosurePrimary(track1)) { + continue; + } + if (!isTOFOnly && !strategySelectionPID(track1, 0, strategyPID)) { + continue; + } + if (isTOFOnly && !selectionPID2(track1, 0)) { + continue; + } + for (const auto& track2 : recTracksThisColl) { + if (track2.globalIndex() == track1.globalIndex()) { + continue; + } + if (track1.sign() * track2.sign() > 0) { + continue; + } + if (!selectionTrack(track2) || !selectionClosurePrimary(track2)) { + continue; + } + if (!isTOFOnly && !strategySelectionPID(track2, 1, strategyPID)) { + continue; + } + if (isTOFOnly && !selectionPID2(track2, 1)) { + continue; + } + kaon = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + pion = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassPi); + kstar = kaon + pion; + if (std::abs(kstar.Rapidity()) > confRapidity) { + continue; + } + if (isPhiReflection(kaon, pion)) { + continue; + } + const double cosRec = getCosThetaStar(kstar, kaon, getSAAxis(kstar, psi)); + + double weight = 1.0; + if (isTrueKstarPair(track1, track2)) { + const auto mcKaon = track1.mcParticle(); + const auto mcPion = track2.mcParticle(); + kaonGen = ROOT::Math::PxPyPzMVector(mcKaon.px(), mcKaon.py(), mcKaon.pz(), MassKa); + pionGen = ROOT::Math::PxPyPzMVector(mcPion.px(), mcPion.py(), mcPion.pz(), MassPi); + kstarGen = kaonGen + pionGen; + const double cosGen = getCosThetaStar(kstarGen, kaonGen, getSAAxis(kstarGen, psi)); + weight = getInjectionWeight(cosGen); + // Acc x Eff must come from an unweighted (unpolarized) sample, independent of the injected rho00 under test + if (isEffSample) { + histos.fill(HIST("closureKstar/hSparseRecTrueEff"), kstar.M(), kstar.Pt(), cosRec, centrality, 1.0); + } + if (isTestSample) { + histos.fill(HIST("closureKstar/hSparseRecTrue"), kstar.M(), kstar.Pt(), cosRec, centrality, weight); + histos.fill(HIST("closureKstar/hCosThetaStarGenVsRec"), cosGen, cosRec); + } + } + if (!isTestSample) { + continue; + } + histos.fill(HIST("closureKstar/hSparseSE"), kstar.M(), kstar.Pt(), cosRec, centrality, weight); + + // rotated background, same rotation as in processSE (weight 1) + if (closure.fillRotBkg && nBkgRotations.value > 1) { + const double angleStart = confMinRot.value; + const double angleStep = (confMaxRot.value - angleStart) / (nBkgRotations.value - 1.0); + for (int irot = 0; irot < nBkgRotations.value; irot++) { + const double rotAngle = angleStart + irot * angleStep; + kaonRot = ROOT::Math::PxPyPzMVector(track1.px() * std::cos(rotAngle) - track1.py() * std::sin(rotAngle), track1.px() * std::sin(rotAngle) + track1.py() * std::cos(rotAngle), track1.pz(), MassKa); + kstarRot = kaonRot + pion; + if (std::abs(kstarRot.Rapidity()) > confRapidity) { + continue; + } + if (isPhiReflection(kaonRot, pion)) { + continue; + } + const double cosRot = getCosThetaStar(kstarRot, kaonRot, getSAAxis(kstarRot, psi)); + histos.fill(HIST("closureKstar/hSparseRot"), kstarRot.M(), kstarRot.Pt(), cosRot, centrality); + } + } + } + } + + // ---- generated K*0 / anti-K*0: Acc x Eff denominator (eff sample) and input truth (test sample) ---- + for (const auto& mcParticle : genParticles) { + if (std::abs(mcParticle.y()) > confRapidity) { + continue; + } + if (std::abs(mcParticle.pdgCode()) != o2::constants::physics::kK0Star892) { + continue; + } + auto daughters = mcParticle.daughters_as(); + if (daughters.size() != NumberOfDaughters) { + continue; + } + bool hasKaon = false; + bool hasPion = false; + for (const auto& dau : daughters) { + if (!dau.isPhysicalPrimary()) { + continue; + } + const bool inAcceptance = !closure.genAcceptanceCut || (dau.pt() > cfgCutPT && std::abs(dau.eta()) < cfgCutEta); + if (std::abs(dau.pdgCode()) == PDG_t::kKPlus) { + hasKaon = hasKaon || inAcceptance; + kaonGen = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), MassKa); + } else if (std::abs(dau.pdgCode()) == PDG_t::kPiPlus) { + hasPion = hasPion || inAcceptance; + pionGen = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), MassPi); + } + } + if (!hasKaon || !hasPion) { + continue; + } + kstarGen = kaonGen + pionGen; + if (std::abs(kstarGen.Rapidity()) > confRapidity) { + continue; + } + const double cosGen = getCosThetaStar(kstarGen, kaonGen, getSAAxis(kstarGen, psi)); + // Acc x Eff must come from an unweighted (unpolarized) sample, independent of the injected rho00 under test + if (isEffSample) { + histos.fill(HIST("closureKstar/hSparseGenEff"), kstarGen.M(), kstarGen.Pt(), cosGen, centrality, 1.0); + } + if (isTestSample) { + const double weight = getInjectionWeight(cosGen); + histos.fill(HIST("closureKstar/hSparseGen"), kstarGen.M(), kstarGen.Pt(), cosGen, centrality, weight); + } + } + } // rec collision loop + } + PROCESS_SWITCH(Kstarpbpb, processMCClosure, "Process MC closure of cos(theta*) for K*: SE, true rec, gen", false); + + // ---------------- K*0: mixed event of the test sample ---------------- + // Same pairing as processMixedEvent (kaon from event 1, pion from event 2, unlike-sign), axis from event 1. + void processMCClosureME(CollisionMCRecMixTable const& collisions, FilTrackMCRecTable const& tracks, aod::McCollisions const& mcCollisions, aod::McCollisionLabels const& mcLabels, aod::McParticles const&) + { + ROOT::Math::PxPyPzMVector kaon, pion, kstar; + const auto nRecPerMc = countRecoPerMcCollision(mcLabels, mcCollisions.size()); + auto tracksTuple = std::make_tuple(tracks); + BinningTypeMCClosure binningOnPositions{{axisVertex, axisMultiplicityClass}}; + SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; + for (const auto& [collision1, tracks1, collision2, tracks2] : pair) { + if (!selectionClosurePairME(collision1, collision2, nRecPerMc)) { + continue; + } + const auto centrality = collision1.centFT0C(); + const double psi = getClosurePsi(collision1.mcCollision().eventPlaneAngle()); + for (const auto& [track1, track2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + if (track1.sign() * track2.sign() > 0) { + continue; + } + if (!selectionTrack(track1) || !selectionTrack(track2)) { + continue; + } + if (!selectionClosurePrimary(track1) || !selectionClosurePrimary(track2)) { + continue; + } + if (!isTOFOnly && (!strategySelectionPID(track1, 0, strategyPID) || !strategySelectionPID(track2, 1, strategyPID))) { + continue; + } + if (isTOFOnly && (!selectionPID2(track1, 0) || !selectionPID2(track2, 1))) { + continue; + } + kaon = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + pion = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassPi); + kstar = kaon + pion; + if (std::abs(kstar.Rapidity()) > confRapidity) { + continue; + } + if (isPhiReflection(kaon, pion)) { + continue; + } + const double cosRec = getCosThetaStar(kstar, kaon, getSAAxis(kstar, psi)); + histos.fill(HIST("closureKstar/hSparseME"), kstar.M(), kstar.Pt(), cosRec, centrality); + } + } + } + PROCESS_SWITCH(Kstarpbpb, processMCClosureME, "Process MC closure of cos(theta*) for K*: mixed event", false); + + // ---------------- phi(1020): same event, true reco, generated ---------------- + // track1 = K+, track2 = K-, as in processSEPhi; cos(theta*) of the K-. + // The axis is built with getSAAxis, as in processSEPhi/processMEPhi (the real data analysis). + void processMCClosurePhi(CollisionMCTrueTable::iterator const& trueCollision, CollisionMCRecTableCentFT0C const& recCollisions, TrackMCTrueTable const& genParticles, FilTrackMCRecTable const& recTracks) + { + ROOT::Math::PxPyPzMVector kaonPlus, kaonMinus, phi, kaonPlusRot, phiRot, kaonPlusGen, kaonMinusGen, phiGen; + static constexpr std::size_t NumberOfDaughters = 2; + + histos.fill(HIST("closurePhi/hMC"), 0.5); + if (recCollisions.size() == 0) { + histos.fill(HIST("closurePhi/hMC"), 1.5); + return; + } + if (recCollisions.size() > 1) { + histos.fill(HIST("closurePhi/hMC"), 2.5); + return; + } + const bool isEffSample = isClosureEffSample(trueCollision.globalIndex()); + const bool isTestSample = isClosureTestSample(trueCollision.globalIndex()); + const double psi = getClosurePsi(trueCollision.eventPlaneAngle()); + for (const auto& recCollision : recCollisions) { + if (!selectionEventMC(recCollision)) { + continue; + } + if (isEffSample) { + histos.fill(HIST("closurePhi/hMC"), 3.5); + } + if (isTestSample) { + histos.fill(HIST("closurePhi/hMC"), 4.5); + } + const auto centrality = recCollision.centFT0C(); + auto recTracksThisColl = recTracks.sliceBy(perCollision, recCollision.globalIndex()); + + // ---- reconstructed pairs ---- + for (const auto& track1 : recTracksThisColl) { + if (!(track1.signed1Pt() > cfgCutCharge.value)) { // K+ + continue; + } + if (!selectionTrackPhi(track1) || !selectionPIDPhi(track1) || !selectionClosurePrimary(track1)) { + continue; + } + for (const auto& track2 : recTracksThisColl) { + if (!(track2.signed1Pt() < cfgCutCharge.value)) { // K- + continue; + } + if (!selectionTrackPhi(track2) || !selectionPIDPhi(track2) || !selectionClosurePrimary(track2)) { + continue; + } + if (!selectionPairPhi(track1, track2)) { + continue; + } + if (phiSA.removeFakeTrack && (isFakeKaonPhi(track1) || isFakeKaonPhi(track2))) { + continue; + } + kaonPlus = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + kaonMinus = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); + phi = kaonPlus + kaonMinus; + if (std::abs(phi.Rapidity()) > phiSA.confRapidity) { + continue; + } + const double cosRec = getCosThetaStar(phi, kaonMinus, getSAAxis(phi, psi)); + + double weight = 1.0; + if (isTruePhiPair(track1, track2)) { + const auto mcPlus = track1.mcParticle(); + const auto mcMinus = track2.mcParticle(); + kaonPlusGen = ROOT::Math::PxPyPzMVector(mcPlus.px(), mcPlus.py(), mcPlus.pz(), MassKa); + kaonMinusGen = ROOT::Math::PxPyPzMVector(mcMinus.px(), mcMinus.py(), mcMinus.pz(), MassKa); + phiGen = kaonPlusGen + kaonMinusGen; + const double cosGen = getCosThetaStar(phiGen, kaonMinusGen, getSAAxis(phiGen, psi)); + weight = getInjectionWeight(cosGen); + // Acc x Eff must come from an unweighted (unpolarized) sample, independent of the injected rho00 under test + if (isEffSample) { + histos.fill(HIST("closurePhi/hSparseRecTrueEff"), phi.M(), phi.Pt(), cosRec, centrality, 1.0); + } + if (isTestSample) { + histos.fill(HIST("closurePhi/hSparseRecTrue"), phi.M(), phi.Pt(), cosRec, centrality, weight); + histos.fill(HIST("closurePhi/hCosThetaStarGenVsRec"), cosGen, cosRec); + } + } + if (!isTestSample) { + continue; + } + histos.fill(HIST("closurePhi/hSparseSE"), phi.M(), phi.Pt(), cosRec, centrality, weight); + + // rotated background: rotate the K+ (weight 1) + if (closure.fillRotBkg && nBkgRotations.value > 1) { + const double angleStart = confMinRot.value; + const double angleStep = (confMaxRot.value - angleStart) / (nBkgRotations.value - 1.0); + for (int irot = 0; irot < nBkgRotations.value; irot++) { + const double rotAngle = angleStart + irot * angleStep; + kaonPlusRot = ROOT::Math::PxPyPzMVector(track1.px() * std::cos(rotAngle) - track1.py() * std::sin(rotAngle), track1.px() * std::sin(rotAngle) + track1.py() * std::cos(rotAngle), track1.pz(), MassKa); + phiRot = kaonPlusRot + kaonMinus; + if (std::abs(phiRot.Rapidity()) > phiSA.confRapidity) { + continue; + } + const double cosRot = getCosThetaStar(phiRot, kaonMinus, getSAAxis(phiRot, psi)); + histos.fill(HIST("closurePhi/hSparseRot"), phiRot.M(), phiRot.Pt(), cosRot, centrality); + } + } + } + } + + // ---- generated phi: Acc x Eff denominator (eff sample) and input truth (test sample) ---- + for (const auto& mcParticle : genParticles) { + if (std::abs(mcParticle.y()) > phiSA.confRapidity) { + continue; + } + if (mcParticle.pdgCode() != o2::constants::physics::kPhi) { + continue; + } + auto daughters = mcParticle.daughters_as(); + if (daughters.size() != NumberOfDaughters) { + continue; + } + bool hasKPlus = false; + bool hasKMinus = false; + for (const auto& dau : daughters) { + if (!dau.isPhysicalPrimary()) { + continue; + } + const bool inAcceptance = !closure.genAcceptanceCut || (dau.pt() > cfgCutPT && std::abs(dau.eta()) < cfgCutEta); + if (dau.pdgCode() == PDG_t::kKPlus) { + hasKPlus = hasKPlus || inAcceptance; + kaonPlusGen = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), MassKa); + } else if (dau.pdgCode() == PDG_t::kKMinus) { + hasKMinus = hasKMinus || inAcceptance; + kaonMinusGen = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), MassKa); + } + } + if (!hasKPlus || !hasKMinus) { + continue; + } + phiGen = kaonPlusGen + kaonMinusGen; + const double cosGen = getCosThetaStar(phiGen, kaonMinusGen, getSAAxis(phiGen, psi)); + // Acc x Eff must come from an unweighted (unpolarized) sample, independent of the injected rho00 under test + if (isEffSample) { + histos.fill(HIST("closurePhi/hSparseGenEff"), phiGen.M(), phiGen.Pt(), cosGen, centrality, 1.0); + } + if (isTestSample) { + const double weight = getInjectionWeight(cosGen); + histos.fill(HIST("closurePhi/hSparseGen"), phiGen.M(), phiGen.Pt(), cosGen, centrality, weight); + } + } + } // rec collision loop + } + PROCESS_SWITCH(Kstarpbpb, processMCClosurePhi, "Process MC closure of cos(theta*) for phi(1020): SE, true rec, gen", false); + + // ---------------- phi(1020): mixed event of the test sample ---------------- + // Same pairing as processMEPhi (K+K- from two events), cos(theta*) of the K-, axis from event 1. + void processMCClosureMEPhi(CollisionMCRecMixTable const& collisions, FilTrackMCRecTable const& tracks, aod::McCollisions const& mcCollisions, aod::McCollisionLabels const& mcLabels, aod::McParticles const&) + { + ROOT::Math::PxPyPzMVector kaonPlus, kaonMinus, phi; + const auto nRecPerMc = countRecoPerMcCollision(mcLabels, mcCollisions.size()); + auto tracksTuple = std::make_tuple(tracks); + BinningTypeMCClosure binningOnPositions{{axisVertex, axisMultiplicityClass}}; + SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; + for (const auto& [collision1, tracks1, collision2, tracks2] : pair) { + if (!selectionClosurePairME(collision1, collision2, nRecPerMc)) { + continue; + } + const auto centrality = collision1.centFT0C(); + const double psi = getClosurePsi(collision1.mcCollision().eventPlaneAngle()); + for (const auto& [track1, track2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + if (track1.sign() * track2.sign() > 0) { + continue; + } + if (!selectionTrackPhi(track1) || !selectionTrackPhi(track2)) { + continue; + } + if (!selectionClosurePrimary(track1) || !selectionClosurePrimary(track2)) { + continue; + } + if (!selectionPIDPhi(track1) || !selectionPIDPhi(track2)) { + continue; + } + if (!selectionPairPhi(track1, track2)) { + continue; + } + if (phiSA.removeFakeTrack && (isFakeKaonPhi(track1) || isFakeKaonPhi(track2))) { + continue; + } + if (track1.sign() > 0) { + kaonPlus = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + kaonMinus = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); + } else { + kaonMinus = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + kaonPlus = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); + } + phi = kaonPlus + kaonMinus; + if (std::abs(phi.Rapidity()) > phiSA.confRapidity) { + continue; + } + const double cosRec = getCosThetaStar(phi, kaonMinus, getSAAxis(phi, psi)); + histos.fill(HIST("closurePhi/hSparseME"), phi.M(), phi.Pt(), cosRec, centrality); + } + } + } + PROCESS_SWITCH(Kstarpbpb, processMCClosureMEPhi, "Process MC closure of cos(theta*) for phi(1020): mixed event", false); + + // ================= MC closure test of the cos(theta*) chain, reconstructed (finite-resolution) Psi_FT0C ================= + // Same as processMCClosure(Phi)/processMCClosureME(Phi) above, but Psi is the reconstructed Psi_FT0C (as on data) instead of + // the MC true event plane, and the sub-event resolution of the sample is filled too (ResFT0C*SP, needed to derive R2 and + // un-smear the fitted raw rho00 downstream). Kept as separate process functions (own histogram directories, own collision + // tables joined with EPCalibrationTables) so the true-EP closure above keeps zero dependency on the EP-calibration producer. + + // ---------------- K*0: same event, true reco, generated (reconstructed Psi_FT0C) ---------------- + void processMCClosureRecoEP(CollisionMCTrueTable::iterator const& trueCollision, CollisionMCRecTableClosure const& recCollisions, TrackMCTrueTable const& genParticles, FilTrackMCRecTable const& recTracks) + { + ROOT::Math::PxPyPzMVector kaon, pion, kstar, kaonRot, kstarRot, kaonGen, pionGen, kstarGen; + static constexpr std::size_t NumberOfDaughters = 2; + + histos.fill(HIST("closureKstarRecoEP/hMC"), 0.5); + if (recCollisions.size() == 0) { + histos.fill(HIST("closureKstarRecoEP/hMC"), 1.5); + return; + } + if (recCollisions.size() > 1) { + histos.fill(HIST("closureKstarRecoEP/hMC"), 2.5); + return; + } + const bool isEffSample = isClosureEffSample(trueCollision.globalIndex()); + const bool isTestSample = isClosureTestSample(trueCollision.globalIndex()); + // true axis: defines the injected weight and the Gen/GenEff truth, so the test is not tautological against the smeared axis + const double psiTrue = getSAEventAngle(trueCollision.eventPlaneAngle()); + for (const auto& recCollision : recCollisions) { + if (!selectionEventMCRecoEP(recCollision)) { + continue; + } + if (isEffSample) { + histos.fill(HIST("closureKstarRecoEP/hMC"), 3.5); + } + if (isTestSample) { + histos.fill(HIST("closureKstarRecoEP/hMC"), 4.5); + } + const auto centrality = recCollision.centFT0C(); + const double recoPsiFT0C = recCollision.psiFT0C(); + const double psi = getSAEventAngle(recoPsiFT0C); // measured axis: defines cosRec (SE/ME/RecTrue/RecTrueEff/Rot) only + { + const double psiTPC = recCollision.psiTPC(); + const double psiFT0A = recCollision.psiFT0A(); + const double qFT0C = recCollision.qFT0C(); + const double qTPC = recCollision.qTPC(); + const double qFT0A = recCollision.qFT0A(); + histos.fill(HIST("closureKstarRecoEP/ResFT0CTPCSP"), centrality, qFT0C * qTPC * std::cos(2.0 * (recoPsiFT0C - psiTPC))); + histos.fill(HIST("closureKstarRecoEP/ResFT0CFT0ASP"), centrality, qFT0C * qFT0A * std::cos(2.0 * (recoPsiFT0C - psiFT0A))); + histos.fill(HIST("closureKstarRecoEP/ResFT0ATPCSP"), centrality, qTPC * qFT0A * std::cos(2.0 * (psiTPC - psiFT0A))); + // plain (unweighted) sub-event resolution, as needed by the EP-method correction formula; same definition as the data ResFT0CTPC etc. + histos.fill(HIST("closureKstarRecoEP/ResFT0CTPC"), centrality, std::cos(2.0 * (recoPsiFT0C - psiTPC))); + histos.fill(HIST("closureKstarRecoEP/ResFT0CFT0A"), centrality, std::cos(2.0 * (recoPsiFT0C - psiFT0A))); + histos.fill(HIST("closureKstarRecoEP/ResFT0ATPC"), centrality, std::cos(2.0 * (psiTPC - psiFT0A))); + // closure-only: Psi_FT0C vs the MC true event plane, to cross-check R from sub-events against the true R + histos.fill(HIST("closureKstarRecoEP/ResTrueFT0C"), centrality, std::cos(2.0 * (recoPsiFT0C - trueCollision.eventPlaneAngle()))); + } + auto recTracksThisColl = recTracks.sliceBy(perCollision, recCollision.globalIndex()); + + // ---- reconstructed pairs ---- + for (const auto& track1 : recTracksThisColl) { + if (!selectionTrack(track1) || !selectionClosurePrimary(track1)) { + continue; + } + if (!isTOFOnly && !strategySelectionPID(track1, 0, strategyPID)) { + continue; + } + if (isTOFOnly && !selectionPID2(track1, 0)) { + continue; + } + for (const auto& track2 : recTracksThisColl) { + if (track2.globalIndex() == track1.globalIndex()) { + continue; + } + if (track1.sign() * track2.sign() > 0) { + continue; + } + if (!selectionTrack(track2) || !selectionClosurePrimary(track2)) { + continue; + } + if (!isTOFOnly && !strategySelectionPID(track2, 1, strategyPID)) { + continue; + } + if (isTOFOnly && !selectionPID2(track2, 1)) { + continue; + } + kaon = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + pion = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassPi); + kstar = kaon + pion; + if (std::abs(kstar.Rapidity()) > confRapidity) { + continue; + } + if (isPhiReflection(kaon, pion)) { + continue; + } + const double cosRec = getCosThetaStar(kstar, kaon, getSAAxis(kstar, psi)); + + double weight = 1.0; + if (isTrueKstarPair(track1, track2)) { + const auto mcKaon = track1.mcParticle(); + const auto mcPion = track2.mcParticle(); + kaonGen = ROOT::Math::PxPyPzMVector(mcKaon.px(), mcKaon.py(), mcKaon.pz(), MassKa); + pionGen = ROOT::Math::PxPyPzMVector(mcPion.px(), mcPion.py(), mcPion.pz(), MassPi); + kstarGen = kaonGen + pionGen; + const double cosGen = getCosThetaStar(kstarGen, kaonGen, getSAAxis(kstarGen, psiTrue)); + weight = getInjectionWeight(cosGen); + // Acc x Eff must come from an unweighted (unpolarized) sample, independent of the injected rho00 under test + if (isEffSample) { + histos.fill(HIST("closureKstarRecoEP/hSparseRecTrueEff"), kstar.M(), kstar.Pt(), cosRec, centrality, 1.0); + } + if (isTestSample) { + histos.fill(HIST("closureKstarRecoEP/hSparseRecTrue"), kstar.M(), kstar.Pt(), cosRec, centrality, weight); + histos.fill(HIST("closureKstarRecoEP/hCosThetaStarGenVsRec"), cosGen, cosRec); + } + } + if (!isTestSample) { + continue; + } + histos.fill(HIST("closureKstarRecoEP/hSparseSE"), kstar.M(), kstar.Pt(), cosRec, centrality, weight); + + // rotated background, same rotation as in processSE (weight 1) + if (closure.fillRotBkg && nBkgRotations.value > 1) { + const double angleStart = confMinRot.value; + const double angleStep = (confMaxRot.value - angleStart) / (nBkgRotations.value - 1.0); + for (int irot = 0; irot < nBkgRotations.value; irot++) { + const double rotAngle = angleStart + irot * angleStep; + kaonRot = ROOT::Math::PxPyPzMVector(track1.px() * std::cos(rotAngle) - track1.py() * std::sin(rotAngle), track1.px() * std::sin(rotAngle) + track1.py() * std::cos(rotAngle), track1.pz(), MassKa); + kstarRot = kaonRot + pion; + if (std::abs(kstarRot.Rapidity()) > confRapidity) { + continue; + } + if (isPhiReflection(kaonRot, pion)) { + continue; + } + const double cosRot = getCosThetaStar(kstarRot, kaonRot, getSAAxis(kstarRot, psi)); + histos.fill(HIST("closureKstarRecoEP/hSparseRot"), kstarRot.M(), kstarRot.Pt(), cosRot, centrality); + } + } + } + } + + // ---- generated K*0 / anti-K*0: Acc x Eff denominator (eff sample) and input truth (test sample) ---- + for (const auto& mcParticle : genParticles) { + if (std::abs(mcParticle.y()) > confRapidity) { + continue; + } + if (std::abs(mcParticle.pdgCode()) != o2::constants::physics::kK0Star892) { + continue; + } + auto daughters = mcParticle.daughters_as(); + if (daughters.size() != NumberOfDaughters) { + continue; + } + bool hasKaon = false; + bool hasPion = false; + for (const auto& dau : daughters) { + if (!dau.isPhysicalPrimary()) { + continue; + } + const bool inAcceptance = !closure.genAcceptanceCut || (dau.pt() > cfgCutPT && std::abs(dau.eta()) < cfgCutEta); + if (std::abs(dau.pdgCode()) == PDG_t::kKPlus) { + hasKaon = hasKaon || inAcceptance; + kaonGen = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), MassKa); + } else if (std::abs(dau.pdgCode()) == PDG_t::kPiPlus) { + hasPion = hasPion || inAcceptance; + pionGen = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), MassPi); + } + } + if (!hasKaon || !hasPion) { + continue; + } + kstarGen = kaonGen + pionGen; + if (std::abs(kstarGen.Rapidity()) > confRapidity) { + continue; + } + const double cosGen = getCosThetaStar(kstarGen, kaonGen, getSAAxis(kstarGen, psiTrue)); + // Acc x Eff must come from an unweighted (unpolarized) sample, independent of the injected rho00 under test + if (isEffSample) { + histos.fill(HIST("closureKstarRecoEP/hSparseGenEff"), kstarGen.M(), kstarGen.Pt(), cosGen, centrality, 1.0); + } + if (isTestSample) { + const double weight = getInjectionWeight(cosGen); + histos.fill(HIST("closureKstarRecoEP/hSparseGen"), kstarGen.M(), kstarGen.Pt(), cosGen, centrality, weight); + } + } + } // rec collision loop + } + PROCESS_SWITCH(Kstarpbpb, processMCClosureRecoEP, "Process MC closure of cos(theta*) for K*, reconstructed Psi_FT0C: SE, true rec, gen", false); + + // ---------------- K*0: mixed event of the test sample (reconstructed Psi_FT0C) ---------------- + void processMCClosureMERecoEP(CollisionMCRecMixTableRecoEP const& collisions, FilTrackMCRecTable const& tracks, aod::McCollisions const& mcCollisions, aod::McCollisionLabels const& mcLabels, aod::McParticles const&) + { + ROOT::Math::PxPyPzMVector kaon, pion, kstar; + const auto nRecPerMc = countRecoPerMcCollision(mcLabels, mcCollisions.size()); + auto tracksTuple = std::make_tuple(tracks); + BinningTypeMCClosureRecoEP binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}}; + SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; + for (const auto& [collision1, tracks1, collision2, tracks2] : pair) { + if (!selectionClosurePairMERecoEP(collision1, collision2, nRecPerMc)) { + continue; + } + const auto centrality = collision1.centFT0C(); + const double psi = getSAEventAngle(collision1.psiFT0C()); + for (const auto& [track1, track2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + if (track1.sign() * track2.sign() > 0) { + continue; + } + if (!selectionTrack(track1) || !selectionTrack(track2)) { + continue; + } + if (!selectionClosurePrimary(track1) || !selectionClosurePrimary(track2)) { + continue; + } + if (!isTOFOnly && (!strategySelectionPID(track1, 0, strategyPID) || !strategySelectionPID(track2, 1, strategyPID))) { + continue; + } + if (isTOFOnly && (!selectionPID2(track1, 0) || !selectionPID2(track2, 1))) { + continue; + } + kaon = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + pion = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassPi); + kstar = kaon + pion; + if (std::abs(kstar.Rapidity()) > confRapidity) { + continue; + } + if (isPhiReflection(kaon, pion)) { + continue; + } + const double cosRec = getCosThetaStar(kstar, kaon, getSAAxis(kstar, psi)); + histos.fill(HIST("closureKstarRecoEP/hSparseME"), kstar.M(), kstar.Pt(), cosRec, centrality); + } + } + } + PROCESS_SWITCH(Kstarpbpb, processMCClosureMERecoEP, "Process MC closure of cos(theta*) for K*, reconstructed Psi_FT0C: mixed event", false); + + // ---------------- phi(1020): same event, true reco, generated (reconstructed Psi_FT0C) ---------------- + void processMCClosureRecoEPPhi(CollisionMCTrueTable::iterator const& trueCollision, CollisionMCRecTableClosure const& recCollisions, TrackMCTrueTable const& genParticles, FilTrackMCRecTable const& recTracks) + { + ROOT::Math::PxPyPzMVector kaonPlus, kaonMinus, phi, kaonPlusRot, phiRot, kaonPlusGen, kaonMinusGen, phiGen; + static constexpr std::size_t NumberOfDaughters = 2; + + histos.fill(HIST("closurePhiRecoEP/hMC"), 0.5); + if (recCollisions.size() == 0) { + histos.fill(HIST("closurePhiRecoEP/hMC"), 1.5); + return; + } + if (recCollisions.size() > 1) { + histos.fill(HIST("closurePhiRecoEP/hMC"), 2.5); + return; + } + const bool isEffSample = isClosureEffSample(trueCollision.globalIndex()); + const bool isTestSample = isClosureTestSample(trueCollision.globalIndex()); + // true axis: defines the injected weight and the Gen/GenEff truth, so the test is not tautological against the smeared axis + const double psiTrue = getSAEventAngle(trueCollision.eventPlaneAngle()); + for (const auto& recCollision : recCollisions) { + if (!selectionEventMCRecoEP(recCollision)) { + continue; + } + if (isEffSample) { + histos.fill(HIST("closurePhiRecoEP/hMC"), 3.5); + } + if (isTestSample) { + histos.fill(HIST("closurePhiRecoEP/hMC"), 4.5); + } + const auto centrality = recCollision.centFT0C(); + const double recoPsiFT0C = recCollision.psiFT0C(); + const double psi = getSAEventAngle(recoPsiFT0C); // measured axis: defines cosRec (SE/ME/RecTrue/RecTrueEff/Rot) only + { + const double psiTPC = recCollision.psiTPC(); + const double psiFT0A = recCollision.psiFT0A(); + const double qFT0C = recCollision.qFT0C(); + const double qTPC = recCollision.qTPC(); + const double qFT0A = recCollision.qFT0A(); + histos.fill(HIST("closurePhiRecoEP/ResFT0CTPCSP"), centrality, qFT0C * qTPC * std::cos(2.0 * (recoPsiFT0C - psiTPC))); + histos.fill(HIST("closurePhiRecoEP/ResFT0CFT0ASP"), centrality, qFT0C * qFT0A * std::cos(2.0 * (recoPsiFT0C - psiFT0A))); + histos.fill(HIST("closurePhiRecoEP/ResFT0ATPCSP"), centrality, qTPC * qFT0A * std::cos(2.0 * (psiTPC - psiFT0A))); + // plain (unweighted) sub-event resolution, as needed by the EP-method correction formula; same definition as the data ResFT0CTPC etc. + histos.fill(HIST("closurePhiRecoEP/ResFT0CTPC"), centrality, std::cos(2.0 * (recoPsiFT0C - psiTPC))); + histos.fill(HIST("closurePhiRecoEP/ResFT0CFT0A"), centrality, std::cos(2.0 * (recoPsiFT0C - psiFT0A))); + histos.fill(HIST("closurePhiRecoEP/ResFT0ATPC"), centrality, std::cos(2.0 * (psiTPC - psiFT0A))); + // closure-only: Psi_FT0C vs the MC true event plane, to cross-check R from sub-events against the true R + histos.fill(HIST("closurePhiRecoEP/ResTrueFT0C"), centrality, std::cos(2.0 * (recoPsiFT0C - trueCollision.eventPlaneAngle()))); + } + auto recTracksThisColl = recTracks.sliceBy(perCollision, recCollision.globalIndex()); + + // ---- reconstructed pairs ---- + for (const auto& track1 : recTracksThisColl) { + if (!(track1.signed1Pt() > cfgCutCharge.value)) { // K+ + continue; + } + if (!selectionTrackPhi(track1) || !selectionPIDPhi(track1) || !selectionClosurePrimary(track1)) { + continue; + } + for (const auto& track2 : recTracksThisColl) { + if (!(track2.signed1Pt() < cfgCutCharge.value)) { // K- + continue; + } + if (!selectionTrackPhi(track2) || !selectionPIDPhi(track2) || !selectionClosurePrimary(track2)) { + continue; + } + if (!selectionPairPhi(track1, track2)) { + continue; + } + if (phiSA.removeFakeTrack && (isFakeKaonPhi(track1) || isFakeKaonPhi(track2))) { + continue; + } + kaonPlus = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + kaonMinus = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); + phi = kaonPlus + kaonMinus; + if (std::abs(phi.Rapidity()) > phiSA.confRapidity) { + continue; + } + const double cosRec = getCosThetaStar(phi, kaonMinus, getSAAxis(phi, psi)); + + double weight = 1.0; + if (isTruePhiPair(track1, track2)) { + const auto mcPlus = track1.mcParticle(); + const auto mcMinus = track2.mcParticle(); + kaonPlusGen = ROOT::Math::PxPyPzMVector(mcPlus.px(), mcPlus.py(), mcPlus.pz(), MassKa); + kaonMinusGen = ROOT::Math::PxPyPzMVector(mcMinus.px(), mcMinus.py(), mcMinus.pz(), MassKa); + phiGen = kaonPlusGen + kaonMinusGen; + const double cosGen = getCosThetaStar(phiGen, kaonMinusGen, getSAAxis(phiGen, psiTrue)); + weight = getInjectionWeight(cosGen); + // Acc x Eff must come from an unweighted (unpolarized) sample, independent of the injected rho00 under test + if (isEffSample) { + histos.fill(HIST("closurePhiRecoEP/hSparseRecTrueEff"), phi.M(), phi.Pt(), cosRec, centrality, 1.0); + } + if (isTestSample) { + histos.fill(HIST("closurePhiRecoEP/hSparseRecTrue"), phi.M(), phi.Pt(), cosRec, centrality, weight); + histos.fill(HIST("closurePhiRecoEP/hCosThetaStarGenVsRec"), cosGen, cosRec); + } + } + if (!isTestSample) { + continue; + } + histos.fill(HIST("closurePhiRecoEP/hSparseSE"), phi.M(), phi.Pt(), cosRec, centrality, weight); + + // rotated background: rotate the K+ (weight 1) + if (closure.fillRotBkg && nBkgRotations.value > 1) { + const double angleStart = confMinRot.value; + const double angleStep = (confMaxRot.value - angleStart) / (nBkgRotations.value - 1.0); + for (int irot = 0; irot < nBkgRotations.value; irot++) { + const double rotAngle = angleStart + irot * angleStep; + kaonPlusRot = ROOT::Math::PxPyPzMVector(track1.px() * std::cos(rotAngle) - track1.py() * std::sin(rotAngle), track1.px() * std::sin(rotAngle) + track1.py() * std::cos(rotAngle), track1.pz(), MassKa); + phiRot = kaonPlusRot + kaonMinus; + if (std::abs(phiRot.Rapidity()) > phiSA.confRapidity) { + continue; + } + const double cosRot = getCosThetaStar(phiRot, kaonMinus, getSAAxis(phiRot, psi)); + histos.fill(HIST("closurePhiRecoEP/hSparseRot"), phiRot.M(), phiRot.Pt(), cosRot, centrality); + } + } + } + } + + // ---- generated phi: Acc x Eff denominator (eff sample) and input truth (test sample) ---- + for (const auto& mcParticle : genParticles) { + if (std::abs(mcParticle.y()) > phiSA.confRapidity) { + continue; + } + if (mcParticle.pdgCode() != o2::constants::physics::kPhi) { + continue; + } + auto daughters = mcParticle.daughters_as(); + if (daughters.size() != NumberOfDaughters) { + continue; + } + bool hasKPlus = false; + bool hasKMinus = false; + for (const auto& dau : daughters) { + if (!dau.isPhysicalPrimary()) { + continue; + } + const bool inAcceptance = !closure.genAcceptanceCut || (dau.pt() > cfgCutPT && std::abs(dau.eta()) < cfgCutEta); + if (dau.pdgCode() == PDG_t::kKPlus) { + hasKPlus = hasKPlus || inAcceptance; + kaonPlusGen = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), MassKa); + } else if (dau.pdgCode() == PDG_t::kKMinus) { + hasKMinus = hasKMinus || inAcceptance; + kaonMinusGen = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), MassKa); + } + } + if (!hasKPlus || !hasKMinus) { + continue; + } + phiGen = kaonPlusGen + kaonMinusGen; + const double cosGen = getCosThetaStar(phiGen, kaonMinusGen, getSAAxis(phiGen, psiTrue)); + // Acc x Eff must come from an unweighted (unpolarized) sample, independent of the injected rho00 under test + if (isEffSample) { + histos.fill(HIST("closurePhiRecoEP/hSparseGenEff"), phiGen.M(), phiGen.Pt(), cosGen, centrality, 1.0); + } + if (isTestSample) { + const double weight = getInjectionWeight(cosGen); + histos.fill(HIST("closurePhiRecoEP/hSparseGen"), phiGen.M(), phiGen.Pt(), cosGen, centrality, weight); + } + } + } // rec collision loop + } + PROCESS_SWITCH(Kstarpbpb, processMCClosureRecoEPPhi, "Process MC closure of cos(theta*) for phi(1020), reconstructed Psi_FT0C: SE, true rec, gen", false); + + // ---------------- phi(1020): mixed event of the test sample (reconstructed Psi_FT0C) ---------------- + void processMCClosureMERecoEPPhi(CollisionMCRecMixTableRecoEP const& collisions, FilTrackMCRecTable const& tracks, aod::McCollisions const& mcCollisions, aod::McCollisionLabels const& mcLabels, aod::McParticles const&) + { + ROOT::Math::PxPyPzMVector kaonPlus, kaonMinus, phi; + const auto nRecPerMc = countRecoPerMcCollision(mcLabels, mcCollisions.size()); + auto tracksTuple = std::make_tuple(tracks); + BinningTypeMCClosureRecoEP binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}}; + SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; + for (const auto& [collision1, tracks1, collision2, tracks2] : pair) { + if (!selectionClosurePairMERecoEP(collision1, collision2, nRecPerMc)) { + continue; + } + const auto centrality = collision1.centFT0C(); + const double psi = getSAEventAngle(collision1.psiFT0C()); + for (const auto& [track1, track2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + if (track1.sign() * track2.sign() > 0) { + continue; + } + if (!selectionTrackPhi(track1) || !selectionTrackPhi(track2)) { + continue; + } + if (!selectionClosurePrimary(track1) || !selectionClosurePrimary(track2)) { + continue; + } + if (!selectionPIDPhi(track1) || !selectionPIDPhi(track2)) { + continue; + } + if (!selectionPairPhi(track1, track2)) { + continue; + } + if (phiSA.removeFakeTrack && (isFakeKaonPhi(track1) || isFakeKaonPhi(track2))) { + continue; + } + if (track1.sign() > 0) { + kaonPlus = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + kaonMinus = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); + } else { + kaonMinus = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + kaonPlus = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); + } + phi = kaonPlus + kaonMinus; + if (std::abs(phi.Rapidity()) > phiSA.confRapidity) { + continue; + } + const double cosRec = getCosThetaStar(phi, kaonMinus, getSAAxis(phi, psi)); + histos.fill(HIST("closurePhiRecoEP/hSparseME"), phi.M(), phi.Pt(), cosRec, centrality); + } + } + } + PROCESS_SWITCH(Kstarpbpb, processMCClosureMERecoEPPhi, "Process MC closure of cos(theta*) for phi(1020), reconstructed Psi_FT0C: mixed event", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { diff --git a/PWGLF/Tasks/Resonances/kstarqa.cxx b/PWGLF/Tasks/Resonances/kstarqa.cxx index 042672d9c41..95ca4f38765 100644 --- a/PWGLF/Tasks/Resonances/kstarqa.cxx +++ b/PWGLF/Tasks/Resonances/kstarqa.cxx @@ -173,6 +173,7 @@ struct Kstarqa { HistogramRegistry hInvMass{"hInvMass", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry hPID{"hPID", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry hOthers{"hOthers", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hShift{"hShift", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; // Confugrable for QA histograms Configurable calcLikeSign{"calcLikeSign", true, "Calculate Like Sign"}; @@ -224,6 +225,8 @@ struct Kstarqa { rEventSelection.add("hEventCut", "No. of event after cuts", kTH1D, {{20, 0, 20}}); std::shared_ptr hEventSelection = rEventSelection.get(HIST("hEventCut")); + rEventSelection.add("hEventCutMC", "No. of event after cuts in MC", kTH1D, {{10, 0, 10}}); + std::shared_ptr hEventSelectionMC = rEventSelection.get(HIST("hEventCutMC")); rEventSelection.add("hTrackCut", "No. of tracks after cuts", kTH1D, {{20, 0, 20}}); std::shared_ptr hTrackSelection = rEventSelection.get(HIST("hTrackCut")); @@ -231,21 +234,34 @@ struct Kstarqa { std::vector eveCutLabels = { "All Events", - "|Vz| < 10", - std::string("Sel8") + check(configGp.isSel8.value), std::string("kIsTriggerTVX") + check(configGp.isTriggerTVX.value), + "|Vz| < 10", std::string("NoTimeFrameBorder") + check(configGp.isNoTimeFrameBorder.value), std::string("NoITSROFrameBorder") + check(configGp.isNoITSROFrameBorder.value), + std::string("Sel8") + check(configGp.isSel8.value), std::string("rctChecker") + check(rctCut.requireRCTFlagChecker.value), std::string("IsINELgt0") + check(configGp.isINELgt0.value), std::string("IsGoodITSLayersAll") + check(configGp.isGoodITSLayersAll.value), std::string("isVertexITSTPC") + check(configGp.isVertexITSTPC.value), }; + + std::vector eveCutLabelsMC = { + "All Events", + "RecCollision", + "TimeFrameBorder", + "ITSROFrameBorder", + "Vz", + "TriggerTVX"}; + // assign labels for (size_t i = 0; i < eveCutLabels.size(); ++i) { hEventSelection->GetXaxis()->SetBinLabel(i + 1, eveCutLabels[i].c_str()); } + for (size_t i = 0; i < eveCutLabelsMC.size(); ++i) { + hEventSelectionMC->GetXaxis()->SetBinLabel(i + 1, eveCutLabelsMC[i].c_str()); + } + std::vector trackCutLabels = { "All Tracks", std::string("GlobalTracks") + check(configGp.isGlobalTracks.value), @@ -349,16 +365,38 @@ struct Kstarqa { if (doprocessEvtLossSigLossMC || doprocessEvtLossSigLossMCPhi) { hInvMass.add("MCcorrections/hSignalLossDenominator", "Kstar generated before event selection", kTH2F, {{ptAxis}, {multiplicityAxis}}); + hInvMass.add("MCcorrections/hSignalLossDenominatorRec", "Kstar generated before event selection", kTH2F, {{ptAxis}, {multiplicityAxis}}); hInvMass.add("MCcorrections/hSignalLossNumerator", "Kstar generated after event selection", kTH2F, {{ptAxis}, {multiplicityAxis}}); + hInvMass.add("MCcorrections/hSignalLossNumeratorINEL", "Kstar generated after event selection", kTH2F, {{ptAxis}, {multiplicityAxis}}); hInvMass.add("MCcorrections/hImpactParameterRec", "Impact parameter in reconstructed MC", kTH1F, {impactParAxis}); hInvMass.add("MCcorrections/MultiplicityRec", "Multiplicity in generated MC with at least 1 reconstruction", kTH1F, {multiplicityAxis}); hInvMass.add("MCcorrections/hImpactParameterGen", "Impact parameter in generated MC", kTH1F, {impactParAxis}); hInvMass.add("MCcorrections/MultiplicityGen", "Multiplicity in generated MC", kTH1F, {multiplicityAxis}); + hInvMass.add("MCcorrections/MultiplicityRecINEL", "Multiplicity in generated MC", kTH1F, {multiplicityAxis}); hInvMass.add("MCcorrections/hImpactParametervsMultiplicity", "Impact parameter vs multiplicity in reconstructed MC", kTH2F, {{impactParAxis}, {multiplicityAxis}}); } if (doprocessRecPhi) { hInvMass.add("PhiMCChecks", "PhiMCChecks", kTHnSparseF, {ptAxis, etaAxis, phiAxis, subAxis, subAxis, subAxis}); // pT, eta, phi, deltaEta, deltaPhi, deltaR + + hShift.add("h2D_dpx_vs_px_KaonFromPhi", "Kaon from Phi: dpx vs px", kTH2F, {{subAxis}, {ptAxis}}); + hShift.add("h2D_dpy_vs_py_KaonFromPhi", "Kaon from Phi: dpy vs py", kTH2F, {{subAxis}, {ptAxis}}); + hShift.add("h2D_dpz_vs_pz_KaonFromPhi", "Kaon from Phi: dpz vs pz", kTH2F, {{subAxis}, {ptAxis}}); + hShift.add("h2D_dpt_vs_pt_KaonFromPhi", "Kaon from Phi: dpt vs pt", kTH2F, {{subAxis}, {ptAxis}}); + + hShift.add("h2D_dpx_vs_px_AllKaons", "Kaon from Phi: dpx vs px", kTH2F, {{subAxis}, {ptAxis}}); + hShift.add("h2D_dpy_vs_py_AllKaons", "Kaon from Phi: dpy vs py", kTH2F, {{subAxis}, {ptAxis}}); + hShift.add("h2D_dpz_vs_pz_AllKaons", "Kaon from Phi: dpz vs pz", kTH2F, {{subAxis}, {ptAxis}}); + hShift.add("h2D_dpt_vs_pt_AllKaons", "Kaon from Phi: dpt vs pt", kTH2F, {{subAxis}, {ptAxis}}); + + hShift.add("h2D_dpx_vs_px_Phi", "Pion from Phi: dpx vs px", kTH2F, {{subAxis}, {ptAxis}}); + hShift.add("h2D_dpy_vs_py_Phi", "Pion from Phi: dpy vs py", kTH2F, {{subAxis}, {ptAxis}}); + hShift.add("h2D_dpz_vs_pz_Phi", "Pion from Phi: dpz vs pz", kTH2F, {{subAxis}, {ptAxis}}); + hShift.add("h2D_dpt_vs_pt_Phi", "Pion from Phi: dpt vs pt", kTH2F, {{subAxis}, {ptAxis}}); + + hShift.add("h1D_MassPhiGen", "Invariant mass of generated Phi meson", kTH1F, {{300, 1.0, 1.06}}); + hShift.add("h1D_MassPhiRec", "Invariant mass of reconstructed Phi meson", kTH1F, {{300, 1.0, 1.06}}); + hShift.add("h2D_pt_massShiftPhi", "pT vs mass shift of Phi meson", kTH2F, {{ptAxis}, {300, -0.1, 0.1}}); } // Signal Loss & Event Loss in Light Ion Collisions @@ -453,27 +491,27 @@ struct Kstarqa { if (fillHist) rEventSelection.fill(HIST("hEventCut"), 0); - if (std::abs(collision.posZ()) > configGp.cutzvertex) + if (configGp.isTriggerTVX && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) return false; if (fillHist) rEventSelection.fill(HIST("hEventCut"), 1); - if (configGp.isSel8 && !collision.sel8()) + if (std::abs(collision.posZ()) > configGp.cutzvertex) return false; if (fillHist) rEventSelection.fill(HIST("hEventCut"), 2); - if (configGp.isTriggerTVX && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) + if (configGp.isNoTimeFrameBorder && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) return false; if (fillHist) rEventSelection.fill(HIST("hEventCut"), 3); - if (configGp.isNoTimeFrameBorder && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) + if (configGp.isNoITSROFrameBorder && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) return false; if (fillHist) rEventSelection.fill(HIST("hEventCut"), 4); - if (configGp.isNoITSROFrameBorder && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) + if (configGp.isSel8 && !collision.sel8()) return false; if (fillHist) rEventSelection.fill(HIST("hEventCut"), 5); @@ -1050,21 +1088,22 @@ struct Kstarqa { // Processed events will be already fulfilling the event selection // requirements // Filter eventFilter = (o2::aod::evsel::sel8 == true); - Filter posZFilter = (nabs(o2::aod::collision::posZ) < configGp.cutzvertex); + // Filter posZFilter = (nabs(o2::aod::collision::posZ) < configGp.cutzvertex); - // Filter acceptanceFilter = (nabs(aod::track::eta) < configGp.cfgCutEtaMax && nabs(aod::track::pt) > configGp.cfgCutPT) && (nabs(aod::track::eta) > configGp.cfgCutEtaMin); Filter acceptanceFilter = (nabs(aod::track::eta) < configGp.cfgCutEtaMax && nabs(aod::track::pt) > configGp.cfgCutPT); - // Filter fDCAcutFilter = (nabs(aod::track::dcaXY) < configGp.cfgCutDCAxyMax) && (nabs(aod::track::dcaZ) < configGp.cfgCutDCAz) && (nabs(aod::track::dcaXY) > configGp.cfgCutDCAxyMin); Filter fDCAcutFilter = (nabs(aod::track::dcaXY) < configGp.cfgCutDCAxyMax) && (nabs(aod::track::dcaZ) < configGp.cfgCutDCAz); - using EventCandidates = soa::Filtered>; // aod::CentNGlobals, aod::CentNTPVs, aod::CentMFTs - using EventCandidatesMix = soa::Filtered>; // aod::CentNGlobals, aod::CentNTPVs, aod::CentMFTs + // using EventCandidates = soa::Filtered>; // aod::CentNGlobals, aod::CentNTPVs, aod::CentMFTs + // using EventCandidatesMix = soa::Filtered>; // aod::CentNGlobals, aod::CentNTPVs, aod::CentMFTs + + using EventCandidates = soa::Join; // aod::CentNGlobals, aod::CentNTPVs, aod::CentMFTs + using EventCandidatesMix = soa::Join; // aod::CentNGlobals, aod::CentNTPVs, aod::CentMFTs + using TrackCandidates = soa::Filtered>; using EventCandidatesMC = soa::Join; // using EventCandidatesMC = soa::Filtered>; using TrackCandidatesMC = soa::Filtered>; - // using EventMCGenerated = soa::Join; // aod::CentNGlobals, aod::CentNTPVs, aod::CentMFTs using EventMCGenerated = soa::Join; using McCollisionMults = soa::Join; @@ -1337,15 +1376,15 @@ struct Kstarqa { using BinningTypeMCFT0C = ColumnBinningPolicy; using BinningTypeMCFV0A = ColumnBinningPolicy; - BinningTypeFT0M binningOnFT0M{{axisVertex, axisMultiplicity}, true}; - BinningTypeFT0A binningOnFT0A{{axisVertex, axisMultiplicity}, true}; - BinningTypeFT0C binningOnFT0C{{axisVertex, axisMultiplicity}, true}; - BinningTypeFV0A binningOnFV0A{{axisVertex, axisMultiplicity}, true}; + BinningTypeFT0M binningOnFT0M{{axisVertex, axisMultiplicity}}; + BinningTypeFT0A binningOnFT0A{{axisVertex, axisMultiplicity}}; + BinningTypeFT0C binningOnFT0C{{axisVertex, axisMultiplicity}}; + BinningTypeFV0A binningOnFV0A{{axisVertex, axisMultiplicity}}; - BinningTypeMCFT0M binningOnMCFT0M{{axisVertex, axisMultiplicity}, true}; - BinningTypeMCFT0A binningOnMCFT0A{{axisVertex, axisMultiplicity}, true}; - BinningTypeMCFT0C binningOnMCFT0C{{axisVertex, axisMultiplicity}, true}; - BinningTypeMCFV0A binningOnMCFV0A{{axisVertex, axisMultiplicity}, true}; + BinningTypeMCFT0M binningOnMCFT0M{{axisVertex, axisMultiplicity}}; + BinningTypeMCFT0A binningOnMCFT0A{{axisVertex, axisMultiplicity}}; + BinningTypeMCFT0C binningOnMCFT0C{{axisVertex, axisMultiplicity}}; + BinningTypeMCFV0A binningOnMCFV0A{{axisVertex, axisMultiplicity}}; SameKindPair pair1{binningOnFT0M, configGp.cfgNoMixedEvents, -1, &cache}; SameKindPair pair2{binningOnFT0A, configGp.cfgNoMixedEvents, -1, &cache}; @@ -1891,6 +1930,8 @@ struct Kstarqa { { // auto multiplicityRec = -1; bool isSelectedEvent = false; + bool isRecCollision = false; + bool isSelectedEventINEL = false; auto multiplicity1 = -999.; if (configGp.isINELgt0Gen && !mcCollision.isInelGt0()) { @@ -1903,6 +1944,8 @@ struct Kstarqa { for (const auto& RecCollision : recCollisions) { if (!RecCollision.has_mcCollision()) continue; + isRecCollision = true; + if (!selectionEvent(RecCollision, false)) // don't fill event cut histogram continue; @@ -1922,6 +1965,60 @@ struct Kstarqa { isSelectedEvent = true; } + for (const auto& RecCollision : recCollisions) { + if (!RecCollision.has_mcCollision()) + continue; + + // if (configGp.isTriggerTVX && !RecCollision.selection_bit(aod::evsel::kIsTriggerTVX)) + // continue; + + if (std::abs(RecCollision.posZ()) > configGp.cutzvertex) + continue; + + if (configGp.isNoTimeFrameBorder && !RecCollision.selection_bit(aod::evsel::kNoTimeFrameBorder)) + continue; + + if (configGp.isNoITSROFrameBorder && !RecCollision.selection_bit(aod::evsel::kNoITSROFrameBorder)) + continue; + + // if (configGp.isSel8 && !RecCollision.sel8()) + // continue; + + if (rctCut.requireRCTFlagChecker && !rctChecker(RecCollision)) + continue; + + isSelectedEventINEL = true; + } + + for (const auto& RecCollision : recCollisions) { + rEventSelection.fill(HIST("hEventCutMC"), 0.5); + + if (!RecCollision.has_mcCollision()) + continue; + + rEventSelection.fill(HIST("hEventCutMC"), 1.5); + + if (configGp.isNoTimeFrameBorder && !RecCollision.selection_bit(aod::evsel::kNoTimeFrameBorder)) + continue; + + rEventSelection.fill(HIST("hEventCutMC"), 2.5); + + if (configGp.isNoITSROFrameBorder && !RecCollision.selection_bit(aod::evsel::kNoITSROFrameBorder)) + continue; + + rEventSelection.fill(HIST("hEventCutMC"), 3.5); + + if (std::abs(RecCollision.posZ()) > configGp.cutzvertex) + continue; + + rEventSelection.fill(HIST("hEventCutMC"), 4.5); + + if (configGp.isTriggerTVX && !RecCollision.selection_bit(aod::evsel::kIsTriggerTVX)) + continue; + + rEventSelection.fill(HIST("hEventCutMC"), 5.5); + } + bool isINELgt0true = false; if (pwglf::isINELgtNmc(mcParticles, 0, pdgDB)) { @@ -1937,6 +2034,10 @@ struct Kstarqa { hInvMass.fill(HIST("MCcorrections/hImpactParameterGen"), impactPar); hInvMass.fill(HIST("MCcorrections/MultiplicityGen"), multiplicityGen); + if (isSelectedEventINEL) { + hInvMass.fill(HIST("MCcorrections/MultiplicityRecINEL"), multiplicityGen); + } + // Event loss if (isSelectedEvent) { hInvMass.fill(HIST("MCcorrections/hImpactParameterRec"), impactPar); @@ -1950,9 +2051,16 @@ struct Kstarqa { continue; hInvMass.fill(HIST("MCcorrections/hSignalLossDenominator"), mcPart.pt(), multiplicityGen); + if (isRecCollision) { + hInvMass.fill(HIST("MCcorrections/hSignalLossDenominatorRec"), mcPart.pt(), multiplicityGen); + } + if (isSelectedEvent) { hInvMass.fill(HIST("MCcorrections/hSignalLossNumerator"), mcPart.pt(), multiplicityGen); } + if (isSelectedEventINEL) { + hInvMass.fill(HIST("MCcorrections/hSignalLossNumeratorINEL"), mcPart.pt(), multiplicityGen); + } } // end loop on gen particles } PROCESS_SWITCH(Kstarqa, processEvtLossSigLossMC, "Process Signal Loss, Event Loss", false); @@ -3055,6 +3163,20 @@ struct Kstarqa { continue; } + // All Kaons (Filling single-track kaon resolution histograms) + const auto mctrack1_all = track1.mcParticle(); + if (std::abs(mctrack1_all.pdgCode()) == PDG_t::kKPlus) { + double dpx_all = mctrack1_all.px() - track1.px(); + double dpy_all = mctrack1_all.py() - track1.py(); + double dpz_all = mctrack1_all.pz() - track1.pz(); + double dpt_all = mctrack1_all.pt() - track1.pt(); + + hShift.fill(HIST("h2D_dpx_vs_px_AllKaons"), dpx_all, track1.px()); + hShift.fill(HIST("h2D_dpy_vs_py_AllKaons"), dpy_all, track1.py()); + hShift.fill(HIST("h2D_dpz_vs_pz_AllKaons"), dpz_all, track1.pz()); + hShift.fill(HIST("h2D_dpt_vs_pt_AllKaons"), dpt_all, track1.pt()); + } + auto track1ID = track1.index(); for (const auto& track2 : tracks) { rEventSelection.fill(HIST("recMCparticles"), 0.5); @@ -3162,13 +3284,57 @@ struct Kstarqa { if (track1.sign() * track2.sign() < 0) { daughter1 = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massKa); daughter2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massKa); - mother = daughter1 + daughter2; // Phi meson + + genDaughter1 = ROOT::Math::PxPyPzMVector(mctrack1.px(), mctrack1.py(), mctrack1.pz(), massKa); + genDaughter2 = ROOT::Math::PxPyPzMVector(mctrack2.px(), mctrack2.py(), mctrack2.pz(), massKa); + + mother = daughter1 + daughter2; // Phi meson + genMother = genDaughter1 + genDaughter2; // Phi meson + + recMass = mother.M(); + genMass = genMother.M(); + + hShift.fill(HIST("h1D_MassPhiGen"), genMass); + hShift.fill(HIST("h1D_MassPhiRec"), recMass); + hShift.fill(HIST("h2D_pt_massShiftPhi"), mother.pt(), genMass - recMass); auto genpTPhi = mothertrack1.pt(); auto genEtaPhi = mothertrack1.eta(); auto genPhiPhi = mothertrack1.phi(); hInvMass.fill(HIST("PhiMCChecks"), mother.pt(), mother.eta(), mother.phi(), genpTPhi - mother.pt(), genEtaPhi - mother.eta(), genPhiPhi - mother.phi()); + + // Kaons from Phi daughter (Filling for daughter track1 & daughter track2) + double dpx_k1 = mctrack1.px() - track1.px(); + double dpy_k1 = mctrack1.py() - track1.py(); + double dpz_k1 = mctrack1.pz() - track1.pz(); + double dpt_k1 = mctrack1.pt() - track1.pt(); + + hShift.fill(HIST("h2D_dpx_vs_px_KaonFromPhi"), dpx_k1, track1.px()); + hShift.fill(HIST("h2D_dpy_vs_py_KaonFromPhi"), dpy_k1, track1.py()); + hShift.fill(HIST("h2D_dpz_vs_pz_KaonFromPhi"), dpz_k1, track1.pz()); + hShift.fill(HIST("h2D_dpt_vs_pt_KaonFromPhi"), dpt_k1, track1.pt()); + + double dpx_k2 = mctrack2.px() - track2.px(); + double dpy_k2 = mctrack2.py() - track2.py(); + double dpz_k2 = mctrack2.pz() - track2.pz(); + double dpt_k2 = mctrack2.pt() - track2.pt(); + + hShift.fill(HIST("h2D_dpx_vs_px_KaonFromPhi"), dpx_k2, track2.px()); + hShift.fill(HIST("h2D_dpy_vs_py_KaonFromPhi"), dpy_k2, track2.py()); + hShift.fill(HIST("h2D_dpz_vs_pz_KaonFromPhi"), dpz_k2, track2.pz()); + hShift.fill(HIST("h2D_dpt_vs_pt_KaonFromPhi"), dpt_k2, track2.pt()); + + // Phi Meson + double dpx_phi = mothertrack1.px() - mother.Px(); + double dpy_phi = mothertrack1.py() - mother.Py(); + double dpz_phi = mothertrack1.pz() - mother.Pz(); + double dpt_phi = mothertrack1.pt() - mother.Pt(); + + hShift.fill(HIST("h2D_dpx_vs_px_Phi"), dpx_phi, mother.Px()); + hShift.fill(HIST("h2D_dpy_vs_py_Phi"), dpy_phi, mother.Py()); + hShift.fill(HIST("h2D_dpz_vs_pz_Phi"), dpz_phi, mother.Pz()); + hShift.fill(HIST("h2D_dpt_vs_pt_Phi"), dpt_phi, mother.Pt()); } } } diff --git a/PWGLF/Tasks/Resonances/lambda1405analysis.cxx b/PWGLF/Tasks/Resonances/lambda1405analysis.cxx index 06385d1a370..f186a29e35b 100644 --- a/PWGLF/Tasks/Resonances/lambda1405analysis.cxx +++ b/PWGLF/Tasks/Resonances/lambda1405analysis.cxx @@ -102,6 +102,7 @@ struct lambda1405candidate { float kinkPrNSigTpc = -1; // Number of sigmas for the proton candidate from Sigma kink in Tpc float kinkPrNSigTof = -1; // Number of sigmas for the proton candidate from Sigma kink in Tof float kinkDcaDauToPv = -1; // DCA of the kink daughter to the primary vertex + float chi2KinkPCA = -1; // Chi2 of the Sigma candidate at the PCA float bachPiPt = -1; // pT of the pion daughter float bachPiNSigTpc = -1; // Number of sigmas for the pion candidate @@ -149,6 +150,7 @@ struct lambda1405analysis { Configurable cutUpperMass{"cutUpperMass", 1.6f, "Upper mass cut for Lambda(1405) candidates (GeV/c^2)"}; Configurable cutSigmaRadius{"cutSigmaRadius", 20.f, "Minimum radius for Sigma candidates (cm)"}; Configurable cutSigmaMass{"cutSigmaMass", 0.1, "Sigma mass window (MeV/c^2)"}; + Configurable cutSigmaChi2PCA{"cutSigmaChi2PCA", 100.f, "Maximum Chi2 PCA for Sigma candidates"}; Configurable cutItsNClusKinkMin{"cutItsNClusKinkMin", 1, "Minimum number of ITS clusters for kink daughter"}; Configurable cutItsNClusKinkMax{"cutItsNClusKinkMax", 3, "Maximum number of ITS clusters for kink daughter"}; Configurable cutNTpcClus{"cutNTpcClus", 90, "Minimum number of Tpc clusters for pion candidate"}; @@ -220,7 +222,7 @@ struct lambda1405analysis { ConfigurableAxis axisPtResolution{"axisPtResolution", {100, -0.5, 0.5}, ""}; ConfigurableAxis axisMassL1405{"axisMassL1405", {500, 1.3, 1.8}, ""}; ConfigurableAxis axisXi1530Mass{"axisXi1530Mass", {500, 1.3, 1.8}, ""}; - ConfigurableAxis axisMassResolution{"axisMassResolution", {100, -0.1, 0.1}, ""}; + ConfigurableAxis axisMassReso{"axisMassReso", {100, -0.1, 0.1}, ""}; ConfigurableAxis axisNSig{"axisNSig", {100, -5., 5.}, ""}; ConfigurableAxis axisSigmaMass{"axisSigmaMass", {300, 1.1, 1.4}, ""}; ConfigurableAxis axisXiMass{"axisXiMass", {300, 1.2, 1.5}, ""}; @@ -251,7 +253,7 @@ struct lambda1405analysis { const AxisSpec ptResolutionAxis{axisPtResolution, "#it{p}_{T}^{rec} - #it{p}_{T}^{gen} (GeV/#it{c})"}; const AxisSpec lambda1405MassAxis{axisMassL1405, "m(#Sigma#pi) (GeV/#it{c}^{2})"}; const AxisSpec xi1530MassAxis{axisXi1530Mass, "m(#Xi#pi) (GeV/#it{c}^{2})"}; - const AxisSpec massResolutionAxis{axisMassResolution, "m_{rec} - m_{gen} (GeV/#it{c}^{2})"}; + const AxisSpec massResoAxis{axisMassReso, "m_{rec} - m_{gen} (GeV/#it{c}^{2})"}; const AxisSpec nSigmaAxis{axisNSig, "n#sigma_{#pi}"}; const AxisSpec sigmaMassAxis{axisSigmaMass, "m(#Sigma) (GeV/#it{c}^{2})"}; const AxisSpec xiMassAxis{axisXiMass, "m(#Xi) (GeV/#it{c}^{2})"}; @@ -367,7 +369,7 @@ struct lambda1405analysis { rSelections.get(HIST("hSelectionsL1405"))->GetXaxis()->SetBinLabel(4, "Upper mass sel"); rSelections.get(HIST("hSelectionsL1405"))->GetXaxis()->SetBinLabel(5, "#it{p}_{T} Correl"); rSelections.get(HIST("hSelectionsL1405"))->GetXaxis()->SetBinLabel(6, "Accepted"); - rSelections.add("hSelectionsSigmaPlus", "hSelectionsSigmaPlus", {HistType::kTH1D, {{8, -0.f, 7.5f}}}); + rSelections.add("hSelectionsSigmaPlus", "hSelectionsSigmaPlus", {HistType::kTH1D, {{9, -0.f, 8.5f}}}); rSelections.get(HIST("hSelectionsSigmaPlus"))->GetXaxis()->SetBinLabel(1, "All"); rSelections.get(HIST("hSelectionsSigmaPlus"))->GetXaxis()->SetBinLabel(2, "Kink"); rSelections.get(HIST("hSelectionsSigmaPlus"))->GetXaxis()->SetBinLabel(3, "Mom recomp."); @@ -376,7 +378,8 @@ struct lambda1405analysis { rSelections.get(HIST("hSelectionsSigmaPlus"))->GetXaxis()->SetBinLabel(6, "DCA PV #pi #leftarrow #Sigma"); rSelections.get(HIST("hSelectionsSigmaPlus"))->GetXaxis()->SetBinLabel(7, "#Sigma Radius"); rSelections.get(HIST("hSelectionsSigmaPlus"))->GetXaxis()->SetBinLabel(8, "#Sigma Qt"); - rSelections.add("hSelectionsSigmaMinus", "hSelectionsSigmaMinus", {HistType::kTH1D, {{8, -0.f, 7.5f}}}); + rSelections.get(HIST("hSelectionsSigmaPlus"))->GetXaxis()->SetBinLabel(9, "Chi2 PCA"); + rSelections.add("hSelectionsSigmaMinus", "hSelectionsSigmaMinus", {HistType::kTH1D, {{9, -0.f, 8.5f}}}); rSelections.get(HIST("hSelectionsSigmaMinus"))->GetXaxis()->SetBinLabel(1, "All"); rSelections.get(HIST("hSelectionsSigmaMinus"))->GetXaxis()->SetBinLabel(2, "Kink"); rSelections.get(HIST("hSelectionsSigmaMinus"))->GetXaxis()->SetBinLabel(3, "Mom recomp."); @@ -385,6 +388,7 @@ struct lambda1405analysis { rSelections.get(HIST("hSelectionsSigmaMinus"))->GetXaxis()->SetBinLabel(6, "DCA PV Pi #leftarrow #Sigma"); rSelections.get(HIST("hSelectionsSigmaMinus"))->GetXaxis()->SetBinLabel(7, "#Sigma Radius"); rSelections.get(HIST("hSelectionsSigmaMinus"))->GetXaxis()->SetBinLabel(8, "#Sigma Qt"); + rSelections.get(HIST("hSelectionsSigmaMinus"))->GetXaxis()->SetBinLabel(9, "Chi2 PCA"); rSelections.add("hRecalcSigmaPlusMom", "hRecalcSigmaPlusMom", {HistType::kTH1D, {{5, -0.f, 4.5f}}}); rSelections.get(HIST("hRecalcSigmaPlusMom"))->GetXaxis()->SetBinLabel(1, "All"); rSelections.get(HIST("hRecalcSigmaPlusMom"))->GetXaxis()->SetBinLabel(2, "Non-null mom."); @@ -438,18 +442,24 @@ struct lambda1405analysis { rSigmaPlus.add("h2DeltaGenRecoPtSigmaPlus", "h2DeltaGenRecoPtSigmaPlus", {HistType::kTH2F, {{600, -3, 3}, ptAxis}}); rSigmaPlus.add("h2GenSigmaPlusPvContribPt", "h2GenSigmaPlusPvContribPt", {HistType::kTH2F, {pvContribAxis, ptAxis}}); + rSigmaPlus.add("hGenSigmaPlusRadius", "hGenSigmaPlusRadius", {HistType::kTH2F, {sigmaRadiusAxis, ptAxis}}); + rSigmaMinus.add("hGenSigmaMinusRadius", "hGenSigmaMinusRadius", {HistType::kTH2F, {sigmaRadiusAxis, ptAxis}}); rSigmaMinus.add("h2DeltaGenRecoPtSigmaMinus", "h2DeltaGenRecoPtSigmaMinus", {HistType::kTH2F, {{600, -3, 3}, ptAxis}}); rSigmaMinus.add("h2GenSigmaMinusPvContribPt", "h2GenSigmaMinusPvContribPt", {HistType::kTH2F, {pvContribAxis, ptAxis}}); - rLambda1405.add("hRecoL1405", "hRecoL1405;;Counts", {HistType::kTH2F, {{4, -0.5, 3.5}, ptAxis}}); - rLambda1405.get(HIST("hRecoL1405"))->GetXaxis()->SetBinLabel(1, "Reco #Lambda(1405)"); - rLambda1405.get(HIST("hRecoL1405"))->GetXaxis()->SetBinLabel(2, "Has bach #pi"); - rLambda1405.get(HIST("hRecoL1405"))->GetXaxis()->SetBinLabel(3, "Has mothers"); - rLambda1405.get(HIST("hRecoL1405"))->GetXaxis()->SetBinLabel(4, "Matched"); + rLambda1405.add("hQARecoL1405", "hQARecoL1405;;Counts", {HistType::kTH2F, {{4, -0.5, 3.5}, ptAxis}}); + rLambda1405.get(HIST("hQARecoL1405"))->GetXaxis()->SetBinLabel(1, "Reco #Lambda(1405)"); + rLambda1405.get(HIST("hQARecoL1405"))->GetXaxis()->SetBinLabel(2, "Has bach #pi"); + rLambda1405.get(HIST("hQARecoL1405"))->GetXaxis()->SetBinLabel(3, "Has mothers"); + rLambda1405.get(HIST("hQARecoL1405"))->GetXaxis()->SetBinLabel(4, "Matched"); rLambda1405.add("hGenL1405", "hGenL1405;;Counts", {HistType::kTH2F, {{3, -0.5, 2.5}, ptAxis}}); rLambda1405.get(HIST("hGenL1405"))->GetXaxis()->SetBinLabel(1, "#Lambda(1405) #rightarrow #Sigma^{-} #pi^{+} #rightarrow n #pi^{-} #pi^{+}"); rLambda1405.get(HIST("hGenL1405"))->GetXaxis()->SetBinLabel(2, "#Lambda(1405) #rightarrow #Sigma^{+} #pi^{-} #rightarrow n #pi^{+} #pi^{-}"); rLambda1405.get(HIST("hGenL1405"))->GetXaxis()->SetBinLabel(3, "#Lambda(1405) #rightarrow #Sigma^{+} #pi^{-} #rightarrow p #pi^{0} #pi^{-}"); + rLambda1405.add("hRecoL1405", "hRecoL1405;;Counts", {HistType::kTH2F, {{3, -0.5, 2.5}, ptAxis}}); + rLambda1405.get(HIST("hRecoL1405"))->GetXaxis()->SetBinLabel(1, "#Lambda(1405) #rightarrow #Sigma^{-} #pi^{+} #rightarrow n #pi^{-} #pi^{+}"); + rLambda1405.get(HIST("hRecoL1405"))->GetXaxis()->SetBinLabel(2, "#Lambda(1405) #rightarrow #Sigma^{+} #pi^{-} #rightarrow n #pi^{+} #pi^{-}"); + rLambda1405.get(HIST("hRecoL1405"))->GetXaxis()->SetBinLabel(3, "#Lambda(1405) #rightarrow #Sigma^{+} #pi^{-} #rightarrow p #pi^{0} #pi^{-}"); rLambda1405.add("h2GenL1405PvContribPt", "h2GenL1405PvContribPt", {HistType::kTH2F, {pvContribAxis, ptAxis}}); rLambda1405.add("h2RecL1405PvContribPt", "h2RecL1405PvContribPt", {HistType::kTH2F, {pvContribAxis, ptAxis}}); rLambda1405.add("h2GenSigmaMinusArmPod", "h2GenSigmaMinusArmPod", {HistType::kTH2F, {alphaAxis, qtAxis}}); @@ -463,10 +473,10 @@ struct lambda1405analysis { rLambda1405.add("h2GenSigmaPtVsKinkPtSigmaMinus", "h2GenSigmaPtVsKinkPtSigmaMinus;#Sigma #it{p}_{T} (GeV/c); Kink #it{p}_{T} (GeV/c)", {HistType::kTH2F, {ptAxis, ptAxis}}); rLambda1405.add("h2GenSigmaPtVsPiKinkPt", "h2GenSigmaPtVsPiKinkPt;#Sigma #it{p}_{T} (GeV/c); Kink #it{p}_{T} (GeV/c)", {HistType::kTH2F, {ptAxis, ptAxis}}); rLambda1405.add("h2GenSigmaPtVsPrKinkPt", "h2GenSigmaPtVsPrKinkPt;#Sigma #it{p}_{T} (GeV/c); Kink #it{p}_{T} (GeV/c)", {HistType::kTH2F, {ptAxis, ptAxis}}); - rLambda1405.add("h2MassResolutionFromSigmaMinus", "h2MassResolutionFromSigmaMinus", {HistType::kTH2F, {lambda1405MassAxis, massResolutionAxis}}); - rLambda1405.add("h2PtResolutionFromSigmaMinus", "h2PtResolutionFromSigmaMinus", {HistType::kTH2F, {ptAxis, ptResolutionAxis}}); - rLambda1405.add("h2MassResolutionFromSigmaPlus", "h2MassResolutionFromSigmaPlus", {HistType::kTH2F, {lambda1405MassAxis, massResolutionAxis}}); - rLambda1405.add("h2PtResolutionFromSigmaPlus", "h2PtResolutionFromSigmaPlus", {HistType::kTH2F, {ptAxis, ptResolutionAxis}}); + rLambda1405.add("h2MassResoFromSigmaMinus", "h2MassResoFromSigmaMinus", {HistType::kTH2F, {lambda1405MassAxis, massResoAxis}}); + rLambda1405.add("h2PtResoFromSigmaMinus", "h2PtResoFromSigmaMinus", {HistType::kTH2F, {ptAxis, ptResolutionAxis}}); + rLambda1405.add("h2MassResoFromSigmaPlus", "h2MassResoFromSigmaPlus", {HistType::kTH2F, {lambda1405MassAxis, massResoAxis}}); + rLambda1405.add("h2PtResoFromSigmaPlus", "h2PtResoFromSigmaPlus", {HistType::kTH2F, {ptAxis, ptResolutionAxis}}); rLambda1405.add("h2PtMassMC", "h2PtMassMC", {HistType::kTH2F, {ptAxis, lambda1405MassAxis}}); } @@ -964,6 +974,15 @@ struct lambda1405analysis { rSelections.fill(HIST("hSelectionsSigmaPlus"), 6); // Passed radius sel } + if (lambda1405Cand.chi2KinkPCA > selections.cutSigmaChi2PCA) { + return; + } + if (lambda1405Cand.isSigmaMinus) { + rSelections.fill(HIST("hSelectionsSigmaMinus"), 7); // Passed radius sel + } else { + rSelections.fill(HIST("hSelectionsSigmaPlus"), 7); // Passed radius sel + } + auto kinkDauMom = std::array{sigmaCand.pxDaug(), sigmaCand.pyDaug(), sigmaCand.pzDaug()}; // Sigma properties lambda1405Cand.sigmaId = sigmaCand.globalIndex(); @@ -995,6 +1014,7 @@ struct lambda1405analysis { lambda1405Cand.kinkPrNSigTpc = kinkDauTrack.tpcNSigmaPr(); lambda1405Cand.kinkPrNSigTof = kinkDauTrack.tofNSigmaPr(); lambda1405Cand.kinkDcaDauToPv = sigmaCand.dcaDaugPv(); + lambda1405Cand.chi2KinkPCA = sigmaCand.dcaKinkTopo(); rSelections.fill(HIST("hSelectionsL1405"), 1); // Passed Sigma sel @@ -1094,12 +1114,12 @@ struct lambda1405analysis { template bool checkSigmaKinkMC(const mcTrack& mcTrackSigma, const mcTrack& mcTrackKinkDau, float sigmaAbsPDG, float kinkAbsPDG, aod::McParticles const&) { - if (std::abs(mcTrackSigma.pdgCode()) != sigmaAbsPDG || std::abs(mcTrackKinkDau.pdgCode()) != kinkAbsPDG) { - return false; // Not a valid Sigma kink decay - } if (!mcTrackKinkDau.has_mothers()) { return false; // No mothers found } + if (std::abs(mcTrackSigma.pdgCode()) != sigmaAbsPDG || std::abs(mcTrackKinkDau.pdgCode()) != kinkAbsPDG) { + return false; // Not a valid Sigma kink decay + } // Check if the kink comes from the Sigma bool isKinkFromSigma = false; for (const auto& mcMother : mcTrackKinkDau.template mothers_as()) { @@ -1151,7 +1171,7 @@ struct lambda1405analysis { lambda1405Cand.kinkPt, lambda1405Cand.kinkPiNSigTpc, lambda1405Cand.kinkPiNSigTof, lambda1405Cand.kinkPrNSigTpc, lambda1405Cand.kinkPrNSigTof, - lambda1405Cand.kinkDcaDauToPv, + lambda1405Cand.kinkDcaDauToPv, lambda1405Cand.dcaSigmaToPv, lambda1405Cand.chi2KinkPCA, lambda1405Cand.bachPiNSigTpc, lambda1405Cand.bachPiNSigTof, lambda1405Cand.scalarProd, lambda1405Cand.centMult, lambda1405Cand.occupancy, lambda1405Cand.poolBin); // 24 @@ -1182,10 +1202,10 @@ struct lambda1405analysis { const aod::BCs&) { if (useCentrality) { - BinningCentPosZ binPolicy{{centMultPoolBins, zPoolBins}, true}; + BinningCentPosZ binPolicy{{centMultPoolBins, zPoolBins}}; fillOutputData(collision, kinkCands, tracks, binPolicy); } else { - BinningMultPosZ binPolicy{{centMultPoolBins, zPoolBins}, true}; + BinningMultPosZ binPolicy{{centMultPoolBins, zPoolBins}}; fillOutputData(collision, kinkCands, tracks, binPolicy); } } @@ -1196,7 +1216,7 @@ struct lambda1405analysis { TracksFull const& tracks, const aod::BCs&) { - BinningCentPosZ binPolicy{{centMultPoolBins, zPoolBins}, true}; + BinningCentPosZ binPolicy{{centMultPoolBins, zPoolBins}}; fillOutputData(collision, kinkCands, tracks, binPolicy); } PROCESS_SWITCH(lambda1405analysis, processDataWQVecsSameEvent, "Data processing with centrality and Q vectors info", false); @@ -1208,7 +1228,7 @@ struct lambda1405analysis { { auto pairsTuple = std::make_tuple(kinkCands, tracks); if (useCentrality) { - BinningCentPosZ binPolicy{{centMultPoolBins, zPoolBins}, true}; + BinningCentPosZ binPolicy{{centMultPoolBins, zPoolBins}}; Pair const pairs{binPolicy, numberEventsMixed, -1, collisions, pairsTuple, &cache}; for (const auto& [sigmasColl, sigmas, bachPisColl, bachPis] : pairs) { if (sigmas.size() == 0 || bachPis.size() == 0) { @@ -1223,7 +1243,7 @@ struct lambda1405analysis { fillOutputData(sigmasColl, sigmas, bachPis, binPolicy); } } else { - BinningMultPosZ binPolicy{{centMultPoolBins, zPoolBins}, true}; + BinningMultPosZ binPolicy{{centMultPoolBins, zPoolBins}}; Pair const pairs{binPolicy, numberEventsMixed, -1, collisions, pairsTuple, &cache}; for (const auto& [sigmasColl, sigmas, bachPisColl, bachPis] : pairs) { if (sigmas.size() == 0 || bachPis.size() == 0) { @@ -1247,7 +1267,7 @@ struct lambda1405analysis { const aod::BCs&) { auto pairsTuple = std::make_tuple(kinkCands, tracks); - BinningCentPosZ binPolicy{{centMultPoolBins, zPoolBins}, true}; + BinningCentPosZ binPolicy{{centMultPoolBins, zPoolBins}}; Pair const pairs{binPolicy, numberEventsMixed, -1, collisions, pairsTuple, &cache}; for (const auto& [sigmasColl, sigmas, bachPisColl, bachPis] : pairs) { if (sigmas.size() == 0 || bachPis.size() == 0) { @@ -1275,6 +1295,7 @@ struct lambda1405analysis { std::vector arrL1405Daugs = {}; std::array finalState; int decayChannel = -1; + bool isMatched = false; // Match L(1405) --> n pi- pi+ final state if (pdgMother > 0) { // Change sign of neutral decay products @@ -1283,6 +1304,7 @@ struct lambda1405analysis { finalState = {-PDG_t::kNeutron, PDG_t::kPiMinus, PDG_t::kPiPlus}; } if (RecoDecay::isMatchedMCGen(mcParticles, motherPart, lambda1405PdgCode, finalState, true, &sign, MaxDepthFinState)) { + isMatched = true; // Match the intermediate Sigma RecoDecay::getDaughters(motherPart, &arrL1405Daugs, std::array{0}, 1); for (auto iProng = 0u; iProng < arrL1405Daugs.size(); ++iProng) { @@ -1309,6 +1331,7 @@ struct lambda1405analysis { } if (RecoDecay::isMatchedMCGen(mcParticles, motherPart, lambda1405PdgCode, finalState, true, &sign, MaxDepthFinState)) { + isMatched = true; // Match the intermediate Sigma RecoDecay::getDaughters(motherPart, &arrL1405Daugs, std::array{0}, 1); for (auto iProng = 0u; iProng < arrL1405Daugs.size(); ++iProng) { @@ -1323,6 +1346,10 @@ struct lambda1405analysis { } } + if (!isMatched) { + return decayChannel; // No valid decay channel found + } + std::vector arrSigmaDaugs = {}; auto sigmaDaug = mcParticles.rawIteratorAt(daugsIdxs[1]); RecoDecay::getDaughters(sigmaDaug, &arrSigmaDaugs, std::array{0}, 1); @@ -1366,9 +1393,11 @@ struct lambda1405analysis { auto labelKinkDaug = trackLabelsMC.rawIteratorAt(sigmaCand.trackDaugId()); if (!labelSigma.has_mcParticle()) { rSelections.fill(HIST("hRecoNotMatchedCounter"), 1); // Sigma not matched + continue; // Skip if no valid MC association } if (!labelKinkDaug.has_mcParticle()) { rSelections.fill(HIST("hRecoNotMatchedCounter"), 2); // Kink daughter not matched + continue; // Skip if no valid MC association } auto genSigma = labelSigma.template mcParticle_as(); auto genKinkDaug = labelKinkDaug.template mcParticle_as(); @@ -1390,7 +1419,7 @@ struct lambda1405analysis { const int dummyPoolBin{-1}; // Not used in MC, but required by the function signature constructCollCandidates(collision, sigmaCand, tracksPerCol, selectedCandidates, dummyPoolBin); for (const auto& lambda1405Cand : selectedCandidates) { - rLambda1405.fill(HIST("hRecoL1405"), 0., lambda1405Cand.pt()); // All reconstructed + rLambda1405.fill(HIST("hQARecoL1405"), 0., lambda1405Cand.pt()); // All reconstructed // Do MC association auto labelBachPi = trackLabelsMC.rawIteratorAt(lambda1405Cand.bachPiId); @@ -1403,12 +1432,12 @@ struct lambda1405analysis { if (std::abs(genBachPi.pdgCode()) != PDG_t::kPiPlus) { continue; // Skip if not a valid pion candidate } - rLambda1405.fill(HIST("hRecoL1405"), 1., lambda1405Cand.pt()); // Has bach pi + rLambda1405.fill(HIST("hQARecoL1405"), 1., lambda1405Cand.pt()); // Has bach pi if (!genSigma.has_mothers() || !genBachPi.has_mothers()) { continue; // Skip if no mothers found } - rLambda1405.fill(HIST("hRecoL1405"), 2., lambda1405Cand.pt()); // Has mothers for Sigma and Pi + rLambda1405.fill(HIST("hQARecoL1405"), 2., lambda1405Cand.pt()); // Has mothers for Sigma and Pi // check that labpi and labsigma have the same mother (a lambda1405 candidate) int lambda1405Id = -1; @@ -1423,19 +1452,28 @@ struct lambda1405analysis { if (lambda1405Id == -1) { continue; // Skip if the Sigma and pion do not share the same lambda1405 candidate } - rLambda1405.fill(HIST("hRecoL1405"), 3., lambda1405Cand.pt()); // Has same mother + rLambda1405.fill(HIST("hQARecoL1405"), 3., lambda1405Cand.pt()); // Matched + + std::array idxsSigmaKinkBachPi{-1, -1, -1}; + auto genLambda1405 = particlesMC.rawIteratorAt(lambda1405Id); + int decayChannel = matchGenDecay(genLambda1405, particlesMC, idxsSigmaKinkBachPi); + if (decayChannel == -1) { + continue; // Skip if it doesn't match the decay channels of interest + } + rLambda1405.fill(HIST("hRecoL1405"), decayChannel, genLambda1405.pt()); auto lambda1405Mother = particlesMC.rawIteratorAt(lambda1405Id); float lambda1405Mass = std::sqrt(lambda1405Mother.e() * lambda1405Mother.e() - lambda1405Mother.p() * lambda1405Mother.p()); + rLambda1405.fill(HIST("h2RecL1405PvContribPt"), collision.numContrib(), lambda1405Cand.pt()); if (lambda1405Cand.isSigmaMinus) { rLambda1405.fill(HIST("h2SigmaMinusMassVsLambdaMass"), lambda1405Cand.massL1405, lambda1405Cand.sigmaMinusMass); - rLambda1405.fill(HIST("h2MassResolutionFromSigmaMinus"), lambda1405Mass, lambda1405Mass - lambda1405Cand.massL1405); - rLambda1405.fill(HIST("h2PtResolutionFromSigmaMinus"), lambda1405Cand.pt(), lambda1405Cand.pt() - lambda1405Mother.pt()); + rLambda1405.fill(HIST("h2MassResoFromSigmaMinus"), lambda1405Mass, lambda1405Mass - lambda1405Cand.massL1405); + rLambda1405.fill(HIST("h2PtResoFromSigmaMinus"), lambda1405Cand.pt(), lambda1405Cand.pt() - lambda1405Mother.pt()); } if (lambda1405Cand.isSigmaPlus) { rLambda1405.fill(HIST("h2SigmaPlusMassVsLambdaMass"), lambda1405Cand.massL1405, lambda1405Cand.sigmaPlusMass); - rLambda1405.fill(HIST("h2MassResolutionFromSigmaPlus"), lambda1405Mass, lambda1405Mass - lambda1405Cand.massL1405); - rLambda1405.fill(HIST("h2PtResolutionFromSigmaPlus"), lambda1405Cand.pt(), lambda1405Cand.pt() - lambda1405Mother.pt()); + rLambda1405.fill(HIST("h2MassResoFromSigmaPlus"), lambda1405Mass, lambda1405Mass - lambda1405Cand.massL1405); + rLambda1405.fill(HIST("h2PtResoFromSigmaPlus"), lambda1405Cand.pt(), lambda1405Cand.pt() - lambda1405Mother.pt()); } if (fillOutputTree) { @@ -1446,7 +1484,7 @@ struct lambda1405analysis { lambda1405Cand.kinkPt, lambda1405Cand.kinkPiNSigTpc, lambda1405Cand.kinkPiNSigTof, lambda1405Cand.kinkPrNSigTpc, lambda1405Cand.kinkPrNSigTof, - lambda1405Cand.kinkDcaDauToPv, + lambda1405Cand.kinkDcaDauToPv, lambda1405Cand.dcaSigmaToPv, lambda1405Cand.chi2KinkPCA, lambda1405Cand.bachPiNSigTpc, lambda1405Cand.bachPiNSigTof, lambda1405Mother.pt(), lambda1405Mass, genSigma.pdgCode(), genBachPi.pdgCode(), lambda1405Cand.centMult, lambda1405Cand.occupancy); @@ -1505,18 +1543,21 @@ struct lambda1405analysis { rLambda1405.fill(HIST("h2GenPtVsBachPtSigmaMinus"), mcPart.pt(), bachPi.pt()); rLambda1405.fill(HIST("h2GenPtVsSigmaMinusPt"), mcPart.pt(), sigmaDaug.pt()); rLambda1405.fill(HIST("h2GenSigmaPtVsKinkPtSigmaMinus"), sigmaDaug.pt(), sigmaKinkDaug.pt()); + rSigmaMinus.fill(HIST("hGenSigmaMinusRadius"), std::hypot(sigmaKinkDaug.vx(), sigmaKinkDaug.vy()), sigmaDaug.pt()); } if (decayChannel == kSigmaPlusPiToPiPiNeutron) { rLambda1405.fill(HIST("h2GenSigmaPlusArmPod"), genSigmaAlphaAP, genSigmaQtAP); rLambda1405.fill(HIST("h2GenPtVsBachPtSigmaPlusToPi"), mcPart.pt(), bachPi.pt()); rLambda1405.fill(HIST("h2GenPtVsSigmaPlusToPiPt"), mcPart.pt(), sigmaDaug.pt()); rLambda1405.fill(HIST("h2GenSigmaPtVsPiKinkPt"), sigmaDaug.pt(), sigmaKinkDaug.pt()); + rSigmaPlus.fill(HIST("hGenSigmaPlusRadius"), std::hypot(sigmaKinkDaug.vx(), sigmaKinkDaug.vy()), sigmaDaug.pt()); } if (decayChannel == kSigmaPlusPiToPiPiProton) { rLambda1405.fill(HIST("h2GenSigmaPlusArmPod"), genSigmaAlphaAP, genSigmaQtAP); rLambda1405.fill(HIST("h2GenPtVsBachPtSigmaPlusToPr"), mcPart.pt(), bachPi.pt()); rLambda1405.fill(HIST("h2GenPtVsSigmaPlusToPrPt"), mcPart.pt(), sigmaDaug.pt()); rLambda1405.fill(HIST("h2GenSigmaPtVsPrKinkPt"), sigmaDaug.pt(), sigmaKinkDaug.pt()); + rSigmaPlus.fill(HIST("hGenSigmaPlusRadius"), std::hypot(sigmaKinkDaug.vx(), sigmaKinkDaug.vy()), sigmaDaug.pt()); } } } @@ -1619,7 +1660,6 @@ struct lambda1405analysis { sigmaMomReco[1] *= scale; sigmaMomReco[2] *= scale; - sigmaPt = std::sqrt(sigmaMomReco[0] * sigmaMomReco[0] + sigmaMomReco[1] * sigmaMomReco[1]); if (isSigmaMinusKink) { rSigmaMinus.fill(HIST("hDeltaPxRecalcSigmaMinus"), sigmaMomReco[0] - sigmaCand.pxMoth(), sigmaCand.pxMoth()); rSigmaMinus.fill(HIST("hDeltaPyRecalcSigmaMinus"), sigmaMomReco[1] - sigmaCand.pyMoth(), sigmaCand.pyMoth()); @@ -1675,7 +1715,7 @@ struct lambda1405analysis { sigmaCand.zDecVtx(), sigmaCand.zDecVtx() - genKinkDaug.vz(), recoSigmaAlphaAP, recoSigmaQtAP, recoRecalcPtSigmaAlphaAP, recoRecalcPtSigmaQtAP, - sigmaCand.dcaDaugPv(), sigmaCand.dcaMothPv(), + sigmaCand.dcaDaugPv(), sigmaCand.dcaMothPv(), sigmaCand.dcaKinkTopo(), genSigma.pdgCode(), genKinkDaug.pdgCode(), centMult, collision.ft0cOccupancyInTimeRange(), collision.numContrib()); } diff --git a/PWGLF/Tasks/Resonances/lambda1520SpherocityAnalysis.cxx b/PWGLF/Tasks/Resonances/lambda1520SpherocityAnalysis.cxx index d5543744f89..d84c295aa29 100644 --- a/PWGLF/Tasks/Resonances/lambda1520SpherocityAnalysis.cxx +++ b/PWGLF/Tasks/Resonances/lambda1520SpherocityAnalysis.cxx @@ -653,8 +653,8 @@ struct lambdaAnalysis { { LOGF(debug, "Event Mixing Started"); - BinningType1 binningPositions1{{cMixVtxBins, cMixMultBins, cMixSphBins}, true}; - BinningType2 binningPositions2{{cMixVtxBins, cMixMultBins}, true}; + BinningType1 binningPositions1{{cMixVtxBins, cMixMultBins, cMixSphBins}}; + BinningType2 binningPositions2{{cMixVtxBins, cMixMultBins}}; auto tracksTuple = std::make_tuple(tracks); if (cMixSph) { SameKindPair pairs{binningPositions1, cNumMixEv, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip diff --git a/PWGLF/Tasks/Resonances/lambda1520analysis.cxx b/PWGLF/Tasks/Resonances/lambda1520analysis.cxx index b45c29e6070..b59bbf3a1d2 100644 --- a/PWGLF/Tasks/Resonances/lambda1520analysis.cxx +++ b/PWGLF/Tasks/Resonances/lambda1520analysis.cxx @@ -1107,7 +1107,7 @@ struct Lambda1520analysis { void processME(o2::aod::ResoCollisions const& collisions, aod::ResoTracks const& resotracks) { auto tracksTuple = std::make_tuple(resotracks); - BinningTypeVtxZT0M colBinning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{cfgVtxBins, cfgMultBins}}; SameKindPair pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { diff --git a/PWGLF/Tasks/Resonances/lambda1520analysisinOO.cxx b/PWGLF/Tasks/Resonances/lambda1520analysisinOO.cxx index 1aa84ce8085..413690fc58c 100644 --- a/PWGLF/Tasks/Resonances/lambda1520analysisinOO.cxx +++ b/PWGLF/Tasks/Resonances/lambda1520analysisinOO.cxx @@ -1154,7 +1154,7 @@ struct Lstaranalysis { // Processing Event Mixing using BinningTypeVtxZT0M = ColumnBinningPolicy; - BinningTypeVtxZT0M colBinning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{cfgVtxBins, cfgMultBins}}; void processME(EventCandidates const& collision, TrackCandidates const& tracks) diff --git a/PWGLF/Tasks/Resonances/lambda1520analysisinpp.cxx b/PWGLF/Tasks/Resonances/lambda1520analysisinpp.cxx index a9ffd4f6082..20f2d5dd2ca 100644 --- a/PWGLF/Tasks/Resonances/lambda1520analysisinpp.cxx +++ b/PWGLF/Tasks/Resonances/lambda1520analysisinpp.cxx @@ -1248,7 +1248,7 @@ struct Lambda1520analysisinpp { { auto tracksTuple = std::make_tuple(tracks); - BinningTypeVtxZT0M colBinning{{configBkg.cfgVtxBins, configBkg.cfgMultPercentileBins}, true}; + BinningTypeVtxZT0M colBinning{{configBkg.cfgVtxBins, configBkg.cfgMultPercentileBins}}; SameKindPair pairs{colBinning, configBkg.nEvtMixing, -1, collision, tracksTuple, &cache}; // -1 is the number of the bin to skip for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { diff --git a/PWGLF/Tasks/Resonances/lambda1520analysispo.cxx b/PWGLF/Tasks/Resonances/lambda1520analysispo.cxx index 435a104dbdb..b2e09900706 100644 --- a/PWGLF/Tasks/Resonances/lambda1520analysispo.cxx +++ b/PWGLF/Tasks/Resonances/lambda1520analysispo.cxx @@ -43,7 +43,6 @@ using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::constants::physics; -// FIX name/namespace: use snake_case for namespace namespace lambda_analysis { // Named PDG codes — fixes pdg/explicit-code and magic-number errors @@ -55,13 +54,11 @@ static constexpr int PdgXi0{3322}; static constexpr int PdgOmegaMinus{3334}; } // namespace lambda_analysis -// FIX name/struct: use UpperCamelCase for struct names struct lambda1520analysispo { SliceCache sliceCache; // Preslice helpers - Preslice tracksPerResonanceCollision = - aod::resodaughter::resoCollisionId; + Preslice tracksPerResonanceCollision = aod::resodaughter::resoCollisionId; Preslice tracksPerStandardCollision = aod::track::collisionId; // Pointer to MC parent particle table (used only in MC processing) @@ -192,7 +189,7 @@ struct lambda1520analysispo { const AxisSpec axisTPCdEdx(380, 10, 200, "#frac{dE}{dx}"); const AxisSpec axisVertexZ(120, -12, 12, "v_{z} (cm)"); const AxisSpec axisEventPlaneAngle(120, -3.14, 3.14, "#theta (rad)"); - const AxisSpec axisInvariantMass(numberOfInvMassBins, 1.4, 2.0, "M_{inv} (GeV/c^{2})"); + const AxisSpec axisInvariantMass(numberOfInvMassBins, 1.4, 3.0, "M_{inv} (GeV/c^{2})"); AxisSpec axisOccupancy = {occupancyBins, "Track occupancy [-40,100]"}; AxisSpec axisDCAz = {dcaZMixingBins, "DCA_{z} (cm)"}; @@ -321,7 +318,7 @@ struct lambda1520analysispo { // passesProtonDCASelection() // ============================================================ template - bool passesProtonDCASelection(TrackType const& track, float totalMomentum) + bool passesProtonDCASelection(TrackType const& track, float trackPt) { auto ptBinEdges = static_cast>(protonDCAPtBinEdges); auto maxDCAxyValues = @@ -330,8 +327,8 @@ struct lambda1520analysispo { bool dcaXYPassed = false; for (int iBin = 0; iBin < numberOfBins; iBin++) { - if (totalMomentum >= ptBinEdges[iBin] && - totalMomentum < ptBinEdges[iBin + 1] && + if (trackPt >= ptBinEdges[iBin] && + trackPt < ptBinEdges[iBin + 1] && std::abs(track.dcaXY()) < maxDCAxyValues[iBin]) dcaXYPassed = true; } @@ -346,7 +343,7 @@ struct lambda1520analysispo { // passesKaonDCASelection() // ============================================================ template - bool passesKaonDCASelection(TrackType const& track, float totalMomentum) + bool passesKaonDCASelection(TrackType const& track, float trackPt) { auto ptBinEdges = static_cast>(kaonDCAPtBinEdges); auto maxDCAxyValues = static_cast>(kaonMaxDCAxyPerPtBin); @@ -354,8 +351,8 @@ struct lambda1520analysispo { bool dcaXYPassed = false; for (int iBin = 0; iBin < numberOfBins; iBin++) { - if (totalMomentum >= ptBinEdges[iBin] && - totalMomentum < ptBinEdges[iBin + 1] && + if (trackPt >= ptBinEdges[iBin] && + trackPt < ptBinEdges[iBin + 1] && std::abs(track.dcaXY()) < maxDCAxyValues[iBin]) dcaXYPassed = true; } @@ -406,7 +403,7 @@ struct lambda1520analysispo { if (track.tofNSigmaPr() < minTOFNSigmaProton) return false; - if (combinedNSigmaCutProton < 0 && totalMomentum >= minProtonMomentum) { + if (combinedNSigmaCutProton < 0) { for (int i = 0; i < nTOFBins - 1; ++i) { if (totalMomentum >= tofMomBins[i] && totalMomentum < tofMomBins[i + 1] && @@ -423,7 +420,7 @@ struct lambda1520analysispo { tpcPIDPassed = true; } - if ((combinedNSigmaCutProton > 0) && totalMomentum >= minProtonMomentum && + if ((combinedNSigmaCutProton > 0) && (combinedNSigProton < circularCutSquared && combinedNSigPion > circularRejCutSquared && combinedNSigKaon > circularRejCutSquared)) { @@ -431,13 +428,9 @@ struct lambda1520analysispo { tpcPIDPassed = true; } - if (totalMomentum < minProtonMomentum && - tpcNSigProton < maxTPCNSigmaProton) { - tofPIDPassed = true; - tpcPIDPassed = true; - } - } else { + if (!track.hasTOF() && totalMomentum >= minProtonMomentum) + return false; tofPIDPassed = true; if (track.tpcNSigmaPr() < minTPCNSigmaProton) return false; @@ -490,7 +483,7 @@ struct lambda1520analysispo { if (track.tofNSigmaKa() < minTOFNSigmaKaon) return false; - if (combinedNSigmaCutKaon < 0 && totalMomentum >= minKaonMomentum) { + if (combinedNSigmaCutKaon < 0) { for (int i = 0; i < nTOFBins - 1; ++i) { if (totalMomentum >= tofMomBins[i] && totalMomentum < tofMomBins[i + 1] && @@ -507,7 +500,7 @@ struct lambda1520analysispo { tpcPIDPassed = true; } - if ((combinedNSigmaCutKaon > 0) && totalMomentum >= minKaonMomentum && + if ((combinedNSigmaCutKaon > 0) && (combinedNSigKaon < circularCutSquared && combinedNSigPion > circularRejCutSquared && combinedNSigProton > circularRejCutSquared)) { @@ -515,12 +508,9 @@ struct lambda1520analysispo { tpcPIDPassed = true; } - if (totalMomentum < minKaonMomentum && tpcNSigKaon < maxTPCNSigmaKaon) { - tofPIDPassed = true; - tpcPIDPassed = true; - } - } else { + if (!track.hasTOF() && totalMomentum >= minKaonMomentum) + return false; tofPIDPassed = true; if (track.tpcNSigmaKa() < minTPCNSigmaKaon) return false; @@ -537,6 +527,87 @@ struct lambda1520analysispo { return (tpcPIDPassed && tofPIDPassed); } + template + void fillTrackQAHistograms(TrackType const& track, float centralityPercent) + { + if (!passesBasicTrackSelection(track)) + return; + + const float trackPt = track.pt(); + const float totalMomentum = RecoDecay::p(track.px(), track.py(), track.pz()); + + const auto tpcNSigProtonRaw = track.tpcNSigmaPr(); + allHistograms.fill(HIST("QAbefore/Proton/tpcNSigmaVsMomentum"), totalMomentum, tpcNSigProtonRaw); + const auto tpcNSigKaonRaw = track.tpcNSigmaKa(); + allHistograms.fill(HIST("QAbefore/Kaon/tpcNSigmaVsMomentum"), totalMomentum, tpcNSigKaonRaw); + if (track.hasTOF()) { + const auto tofNSigProtonRaw = track.tofNSigmaPr(); + allHistograms.fill(HIST("QAbefore/Proton/tofNSigmaVsMomentum"), totalMomentum, tofNSigProtonRaw); + allHistograms.fill(HIST("QAbefore/Proton/tofNSigmaVsTPCNSigma"), tpcNSigProtonRaw, tofNSigProtonRaw); + const auto tofNSigKaonRaw = track.tofNSigmaKa(); + allHistograms.fill(HIST("QAbefore/Kaon/tofNSigmaVsMomentum"), totalMomentum, tofNSigKaonRaw); + allHistograms.fill(HIST("QAbefore/Kaon/tofNSigmaVsTPCNSigma"), tpcNSigKaonRaw, tofNSigKaonRaw); + } + + const bool isProtonCandidate = + (!requireTOFForProton || track.hasTOF()) && + passesProtonPID(track, totalMomentum) && + passesProtonDCASelection(track, trackPt); + + if (isProtonCandidate) { + allHistograms.fill(HIST("QAafter/Proton/ptVsCentrality"), trackPt, centralityPercent); + allHistograms.fill(HIST("QAafter/Proton/dcaZVsPt"), trackPt, track.dcaZ()); + allHistograms.fill(HIST("QAafter/Proton/dcaXYVsPt"), trackPt, track.dcaXY()); + allHistograms.fill(HIST("QAafter/Proton/tpcDedxVsMomentum"), totalMomentum, track.tpcSignal()); + allHistograms.fill(HIST("QAafter/Proton/tpcNSigmaPionContamVsPt"), totalMomentum, track.tpcNSigmaPi()); + allHistograms.fill(HIST("QAafter/Proton/tpcNSigmaKaonContamVsPt"), totalMomentum, track.tpcNSigmaKa()); + allHistograms.fill(HIST("QAafter/Proton/tpcNSigmaVsMomentum"), totalMomentum, tpcNSigProtonRaw); + allHistograms.fill(HIST("QAafter/Proton/tpcNSigmaVsCentrality"), centralityPercent, tpcNSigProtonRaw); + allHistograms.fill(HIST("QAafter/Proton/tpcNSigmaVsPt"), trackPt, tpcNSigProtonRaw); + allHistograms.fill(HIST("QAafter/Proton/tpcCrossedRowsVsPt"), trackPt, track.tpcNClsCrossedRows()); + allHistograms.fill(HIST("QAafter/Proton/tpcClustersFoundVsPt"), trackPt, track.tpcNClsFound()); + + if (!useTPCOnlyPID && track.hasTOF()) { + const auto tofNSigProton = track.tofNSigmaPr(); + allHistograms.fill(HIST("QAafter/Proton/tofNSigmaVsMomentum"), totalMomentum, tofNSigProton); + allHistograms.fill(HIST("QAafter/Proton/tofNSigmaVsCentrality"), centralityPercent, tofNSigProton); + allHistograms.fill(HIST("QAafter/Proton/tofNSigmaVsPt"), trackPt, tofNSigProton); + allHistograms.fill(HIST("QAafter/Proton/tofNSigmaPionContamVsMomentum"), totalMomentum, track.tofNSigmaPi()); + allHistograms.fill(HIST("QAafter/Proton/tofNSigmaKaonContamVsMomentum"), totalMomentum, track.tofNSigmaKa()); + allHistograms.fill(HIST("QAafter/Proton/tofNSigmaVsTPCNSigma"), tpcNSigProtonRaw, tofNSigProton); + } + } + + const bool isKaonCandidate = + (!requireTOFForKaon || track.hasTOF()) && + passesKaonPID(track, totalMomentum) && + passesKaonDCASelection(track, trackPt); + + if (isKaonCandidate) { + allHistograms.fill(HIST("QAafter/Kaon/ptVsCentrality"), trackPt, centralityPercent); + allHistograms.fill(HIST("QAafter/Kaon/dcaZVsPt"), trackPt, track.dcaZ()); + allHistograms.fill(HIST("QAafter/Kaon/dcaXYVsPt"), trackPt, track.dcaXY()); + allHistograms.fill(HIST("QAafter/Kaon/tpcDedxVsMomentum"), totalMomentum, track.tpcSignal()); + allHistograms.fill(HIST("QAafter/Kaon/tpcNSigmaPionContamVsPt"), totalMomentum, track.tpcNSigmaPi()); + allHistograms.fill(HIST("QAafter/Kaon/tpcNSigmaProtonContamVsMomentum"), totalMomentum, track.tpcNSigmaPr()); + allHistograms.fill(HIST("QAafter/Kaon/tpcNSigmaVsMomentum"), totalMomentum, tpcNSigKaonRaw); + allHistograms.fill(HIST("QAafter/Kaon/tpcNSigmaVsCentrality"), centralityPercent, tpcNSigKaonRaw); + allHistograms.fill(HIST("QAafter/Kaon/tpcNSigmaVsPt"), trackPt, tpcNSigKaonRaw); + allHistograms.fill(HIST("QAafter/Kaon/tpcCrossedRowsVsPt"), trackPt, track.tpcNClsCrossedRows()); + allHistograms.fill(HIST("QAafter/Kaon/tpcClustersFoundVsPt"), trackPt, track.tpcNClsFound()); + + if (!useTPCOnlyPID && track.hasTOF()) { + const auto tofNSigKaon = track.tofNSigmaKa(); + allHistograms.fill(HIST("QAafter/Kaon/tofNSigmaVsMomentum"), totalMomentum, tofNSigKaon); + allHistograms.fill(HIST("QAafter/Kaon/tofNSigmaVsPt"), trackPt, tofNSigKaon); + allHistograms.fill(HIST("QAafter/Kaon/tofNSigmaVsCentrality"), centralityPercent, tofNSigKaon); + allHistograms.fill(HIST("QAafter/Kaon/tofNSigmaPionContamVsMomentum"), totalMomentum, track.tofNSigmaPi()); + allHistograms.fill(HIST("QAafter/Kaon/tofNSigmaProtonContamVsMomentum"), totalMomentum, track.tofNSigmaPr()); + allHistograms.fill(HIST("QAafter/Kaon/tofNSigmaVsTPCNSigma"), tpcNSigKaonRaw, tofNSigKaon); + } + } + } + // ============================================================ // fillInvariantMassHistograms() // ============================================================ @@ -566,23 +637,6 @@ struct lambda1520analysispo { protonTotalMomentum = RecoDecay::p(pxProton, pyProton, pzProton); kaonTotalMomentum = RecoDecay::p(pxKaon, pyKaon, pzKaon); - if (!isMixedEvent) { - auto tpcNSigProton = protonTrack.tpcNSigmaPr(); - allHistograms.fill(HIST("QAbefore/Proton/tpcNSigmaVsMomentum"), protonTotalMomentum, tpcNSigProton); - if (protonTrack.hasTOF()) { - auto tofNSigProton = protonTrack.tofNSigmaPr(); - allHistograms.fill(HIST("QAbefore/Proton/tofNSigmaVsMomentum"), protonTotalMomentum, tofNSigProton); - allHistograms.fill(HIST("QAbefore/Proton/tofNSigmaVsTPCNSigma"), tpcNSigProton, tofNSigProton); - } - auto tpcNSigKaon = kaonTrack.tpcNSigmaKa(); - allHistograms.fill(HIST("QAbefore/Kaon/tpcNSigmaVsMomentum"), kaonTotalMomentum, tpcNSigKaon); - if (kaonTrack.hasTOF()) { - auto tofNSigKaon = kaonTrack.tofNSigmaKa(); - allHistograms.fill(HIST("QAbefore/Kaon/tofNSigmaVsMomentum"), kaonTotalMomentum, tofNSigKaon); - allHistograms.fill(HIST("QAbefore/Kaon/tofNSigmaVsTPCNSigma"), tpcNSigKaon, tofNSigKaon); - } - } - if (requireTOFForProton && !protonTrack.hasTOF()) continue; if (requireTOFForKaon && !kaonTrack.hasTOF()) @@ -592,8 +646,8 @@ struct lambda1520analysispo { !passesKaonPID(kaonTrack, kaonTotalMomentum)) continue; - if (!passesProtonDCASelection(protonTrack, protonTotalMomentum) || - !passesKaonDCASelection(kaonTrack, kaonTotalMomentum)) + if (!passesProtonDCASelection(protonTrack, protonTrack.pt()) || + !passesKaonDCASelection(kaonTrack, kaonTrack.pt())) continue; // FIX root/entity: replaced TMath::ACos with std::acos @@ -603,58 +657,6 @@ struct lambda1520analysispo { deepAngleCutValue) continue; - if constexpr (!isMixedEvent) { - auto ptProton = protonTrack.pt(); - auto tpcNSigProton = protonTrack.tpcNSigmaPr(); - - allHistograms.fill(HIST("QAafter/Proton/ptVsCentrality"), ptProton, centralityPercent); - allHistograms.fill(HIST("QAafter/Proton/dcaZVsPt"), ptProton, protonTrack.dcaZ()); - allHistograms.fill(HIST("QAafter/Proton/dcaXYVsPt"), ptProton, protonTrack.dcaXY()); - allHistograms.fill(HIST("QAafter/Proton/tpcDedxVsMomentum"), protonTotalMomentum, protonTrack.tpcSignal()); - allHistograms.fill(HIST("QAafter/Proton/tpcNSigmaPionContamVsPt"), protonTotalMomentum, protonTrack.tpcNSigmaPi()); - allHistograms.fill(HIST("QAafter/Proton/tpcNSigmaKaonContamVsPt"), protonTotalMomentum, protonTrack.tpcNSigmaKa()); - allHistograms.fill(HIST("QAafter/Proton/tpcNSigmaVsMomentum"), protonTotalMomentum, tpcNSigProton); - allHistograms.fill(HIST("QAafter/Proton/tpcNSigmaVsCentrality"), tpcNSigProton, centralityPercent); - allHistograms.fill(HIST("QAafter/Proton/tpcNSigmaVsPt"), ptProton, tpcNSigProton); - allHistograms.fill(HIST("QAafter/Proton/tpcCrossedRowsVsPt"), ptProton, protonTrack.tpcNClsCrossedRows()); - allHistograms.fill(HIST("QAafter/Proton/tpcClustersFoundVsPt"), ptProton, protonTrack.tpcNClsFound()); - - if (!useTPCOnlyPID && protonTrack.hasTOF()) { - auto tofNSigProton = protonTrack.tofNSigmaPr(); - allHistograms.fill(HIST("QAafter/Proton/tofNSigmaVsMomentum"), protonTotalMomentum, tofNSigProton); - allHistograms.fill(HIST("QAafter/Proton/tofNSigmaVsCentrality"), tofNSigProton, centralityPercent); - allHistograms.fill(HIST("QAafter/Proton/tofNSigmaVsPt"), ptProton, tofNSigProton); - allHistograms.fill(HIST("QAafter/Proton/tofNSigmaPionContamVsMomentum"), protonTotalMomentum, protonTrack.tofNSigmaPi()); - allHistograms.fill(HIST("QAafter/Proton/tofNSigmaKaonContamVsMomentum"), protonTotalMomentum, protonTrack.tofNSigmaKa()); - allHistograms.fill(HIST("QAafter/Proton/tofNSigmaVsTPCNSigma"), tpcNSigProton, tofNSigProton); - } - - auto ptKaon = kaonTrack.pt(); - auto tpcNSigKaon = kaonTrack.tpcNSigmaKa(); - - allHistograms.fill(HIST("QAafter/Kaon/ptVsCentrality"), ptKaon, centralityPercent); - allHistograms.fill(HIST("QAafter/Kaon/dcaZVsPt"), ptKaon, kaonTrack.dcaZ()); - allHistograms.fill(HIST("QAafter/Kaon/dcaXYVsPt"), ptKaon, kaonTrack.dcaXY()); - allHistograms.fill(HIST("QAafter/Kaon/tpcDedxVsMomentum"), kaonTotalMomentum, kaonTrack.tpcSignal()); - allHistograms.fill(HIST("QAafter/Kaon/tpcNSigmaPionContamVsPt"), kaonTotalMomentum, kaonTrack.tpcNSigmaPi()); - allHistograms.fill(HIST("QAafter/Kaon/tpcNSigmaProtonContamVsMomentum"), kaonTotalMomentum, kaonTrack.tpcNSigmaPr()); - allHistograms.fill(HIST("QAafter/Kaon/tpcNSigmaVsMomentum"), kaonTotalMomentum, tpcNSigKaon); - allHistograms.fill(HIST("QAafter/Kaon/tpcNSigmaVsCentrality"), tpcNSigKaon, centralityPercent); - allHistograms.fill(HIST("QAafter/Kaon/tpcNSigmaVsPt"), ptKaon, tpcNSigKaon); - allHistograms.fill(HIST("QAafter/Kaon/tpcCrossedRowsVsPt"), ptKaon, kaonTrack.tpcNClsCrossedRows()); - allHistograms.fill(HIST("QAafter/Kaon/tpcClustersFoundVsPt"), ptKaon, kaonTrack.tpcNClsFound()); - - if (!useTPCOnlyPID && kaonTrack.hasTOF()) { - auto tofNSigKaon = kaonTrack.tofNSigmaKa(); - allHistograms.fill(HIST("QAafter/Kaon/tofNSigmaVsMomentum"), kaonTotalMomentum, tofNSigKaon); - allHistograms.fill(HIST("QAafter/Kaon/tofNSigmaVsPt"), ptKaon, tofNSigKaon); - allHistograms.fill(HIST("QAafter/Kaon/tofNSigmaVsCentrality"), tofNSigKaon, centralityPercent); - allHistograms.fill(HIST("QAafter/Kaon/tofNSigmaPionContamVsMomentum"), kaonTotalMomentum, kaonTrack.tofNSigmaPi()); - allHistograms.fill(HIST("QAafter/Kaon/tofNSigmaProtonContamVsMomentum"), kaonTotalMomentum, kaonTrack.tofNSigmaPr()); - allHistograms.fill(HIST("QAafter/Kaon/tofNSigmaVsTPCNSigma"), tpcNSigKaon, tofNSigKaon); - } - } - if (runQualityChecksOnly) continue; @@ -836,6 +838,9 @@ struct lambda1520analysispo { allHistograms.fill(HIST("Event/centralityVsOccupancy"), collision.cent(), 100); allHistograms.fill(HIST("Event/primaryVertexZ"), collision.posZ()); + for (const auto& track : tracks) { + fillTrackQAHistograms(track, collision.cent()); + } fillInvariantMassHistograms(tracks, tracks, collision.cent()); } PROCESS_SWITCH(lambda1520analysispo, processData, @@ -900,6 +905,8 @@ struct lambda1520analysispo { if (!passesBasicTrackSelection(track)) continue; + fillTrackQAHistograms(track, centralityPercent); + // FIX root/entity: replaced TMath::Sqrt with RecoDecay::p float totalMom = RecoDecay::p(track.px(), track.py(), track.pz()); @@ -1067,7 +1074,7 @@ struct lambda1520analysispo { { LOGF(debug, "Event mixing started"); MixingBinningVtxZAndCentrality mixingBins{ - {vertexZMixingBins, centralityMixingBins}, true}; + {vertexZMixingBins, centralityMixingBins}}; auto trackPool = std::make_tuple(tracks); SameKindPair @@ -1077,7 +1084,7 @@ struct lambda1520analysispo { // FIX const-ref-in-for-loop: const auto& structured binding for (const auto& [col1, tracks1, col2, tracks2] : eventPairs) { if (cEvtRecINELgt0 && !col1.isRecINELgt0()) // Check reco INELgt0 (at least one PV track in |eta| < 1) about the collision - return; + continue; allHistograms.fill(HIST("Event/mixingBins_centralityVsVtxZVsEventPlane"), col1.cent(), col1.posZ(), col1.evtPl()); fillInvariantMassHistograms(tracks1, tracks2, col1.cent()); @@ -1110,6 +1117,9 @@ struct lambda1520analysispo { allHistograms.fill(HIST("Event/centralityVsOccupancy"), collision.cent(), occupancyValue); + for (const auto& track : tracks) { + fillTrackQAHistograms(track, collision.cent()); + } fillInvariantMassHistograms(tracks, tracks, collision.cent(), occupancyValue); } @@ -1131,7 +1141,7 @@ struct lambda1520analysispo { LOG(fatal) << "Disable processMix() first when using processMixDF()!"; LOGF(debug, "Event mixing (DF format) started"); - MixingBinningDF mixingBins{{vertexZMixingBins, centralityMixingBins}, true}; + MixingBinningDF mixingBins{{vertexZMixingBins, centralityMixingBins}}; auto trackPool = std::make_tuple(tracks); SameKindPair eventPairs{mixingBins, numberOfEventsToMix, -1, @@ -1140,7 +1150,7 @@ struct lambda1520analysispo { // FIX const-ref-in-for-loop: const auto& structured binding for (const auto& [col1, tracks1, col2, tracks2] : eventPairs) { if (cEvtRecINELgt0 && !col1.isRecINELgt0()) // Check reco INELgt0 (at least one PV track in |eta| < 1) about the collision - return; + continue; auto occupancyValue = 100; if (applyOccupancyInTimeRangeCut) occupancyValue = col1.trackOccupancyInTimeRange(); @@ -1171,7 +1181,7 @@ struct lambda1520analysispo { LOGF(debug, "Event-plane-dependent event mixing (DF format) started"); MixingBinningWithEventPlane mixingBins{ - {vertexZMixingBins, centralityMixingBins, eventPlaneMixingBins}, true}; + {vertexZMixingBins, centralityMixingBins, eventPlaneMixingBins}}; auto trackPool = std::make_tuple(tracks); SameKindPair diff --git a/PWGLF/Tasks/Resonances/lambda1520pbpb.cxx b/PWGLF/Tasks/Resonances/lambda1520pbpb.cxx index abcfe683f88..8e06ab022dd 100644 --- a/PWGLF/Tasks/Resonances/lambda1520pbpb.cxx +++ b/PWGLF/Tasks/Resonances/lambda1520pbpb.cxx @@ -1253,7 +1253,7 @@ struct Lambda1520pbpb { void processMix(ResoCols const& collisions, ResoTracks const& tracks) { LOGF(debug, "Event Mixing Started"); - BinningType2 binningPositions2{{cMixVtxBins, cMixMultBins}, true}; + BinningType2 binningPositions2{{cMixVtxBins, cMixMultBins}}; auto tracksTuple = std::make_tuple(tracks); SameKindPair pairs{binningPositions2, cNumMixEv, -1, collisions, tracksTuple, &cache}; @@ -1295,7 +1295,7 @@ struct Lambda1520pbpb { LOG(fatal) << "Disable processMix() first!"; LOGF(debug, "Event Mixing Started"); - BinningTypeDF binningPositions2{{cMixVtxBins, cMixMultBins}, true}; + BinningTypeDF binningPositions2{{cMixVtxBins, cMixMultBins}}; auto tracksTuple = std::make_tuple(tracks); SameKindPair pairs{binningPositions2, cNumMixEv, -1, collisions, tracksTuple, &cache}; @@ -1320,7 +1320,7 @@ struct Lambda1520pbpb { if (doprocessMix || doprocessMixDF) LOG(fatal) << "Disable processMix() or processMixDF() first!"; LOGF(debug, "Event Mixing Started"); - BinningTypeEP binningPositions2{{cMixVtxBins, cMixMultBins, cMixEPAngle}, true}; + BinningTypeEP binningPositions2{{cMixVtxBins, cMixMultBins, cMixEPAngle}}; auto tracksTuple = std::make_tuple(tracks); SameKindPair pairs{binningPositions2, cNumMixEv, -1, collisions, tracksTuple, &cache}; diff --git a/PWGLF/Tasks/Resonances/lambdastarproxy.cxx b/PWGLF/Tasks/Resonances/lambdastarproxy.cxx index 9ec02766b4c..b923950622e 100644 --- a/PWGLF/Tasks/Resonances/lambdastarproxy.cxx +++ b/PWGLF/Tasks/Resonances/lambdastarproxy.cxx @@ -24,20 +24,28 @@ #include #include #include +#include #include #include #include #include #include +#include +#include #include +#include +#include #include #include +#include #include #include +#include #include #include +#include #include #include #include @@ -48,6 +56,8 @@ using namespace o2::framework::expressions; using namespace constants::math; struct Lambdastarproxy { + Service pdgDB{}; + // ---- Ion/nucleus PDG encoding helpers (10LZZZAAAI) ---- // Note: these are *format* constants (not particle PDG species codes) static constexpr int IonCodeThreshold = 1000000000; // 10^9 @@ -64,6 +74,16 @@ struct Lambdastarproxy { static constexpr float CutPtMinDefault = 0.5f; static constexpr float CutEtaMaxDefault = 0.8f; + static constexpr int FT0MPercentileBins = 100; + static constexpr float FT0MPercentileMax = 100.f; + enum class FT0MEventStatus : int { + Valid = 1, + Negative, + OutOfRange, + NonFinite, + Unavailable + }; + static constexpr float CutMCPtMinDefault = 0.1f; static constexpr float CutMCEtaMaxDefault = 0.8f; static constexpr int FillMCTruthDefault = 1; @@ -182,25 +202,59 @@ struct Lambdastarproxy { Configurable lstarEnableSparse{"lstarEnableSparse", 1, "Enable THnSparse invariant-mass histograms (#Lambda^{*} pK and proxy); 1 = ON, 0 = OFF"}; Configurable lstarLambdaAbsYMax{"lstarLambdaAbsYMax", 0.5f, "Max |y_{pK}| (or y_{proxy K}) for #Lambda^{*} candidates"}; + // Booking and filling use the same switches. Full mass maps grow with + // occupied mass x mass x pT x multiplicity cells and are diagnostic only. + Configurable lstarEnableFullPKMaps{"lstarEnableFullPKMaps", 0, "Book/fill 4D and 8D proxy/full-pK maps; enable only for diagnostic productions"}; + Configurable lstarEnableProtonLikeSparse{"lstarEnableProtonLikeSparse", 0, "Book/fill redundant proton-like proxy spectra (also obtainable from compact controls)"}; + Configurable lstarSparseMultBins{"lstarSparseMultBins", 100, "Multiplicity-axis bins in all invariant-mass sparse histograms; keep identical across merge inputs"}; + Configurable lstarMCPrimaryOnly{"lstarMCPrimaryOnly", 1, "Generated p/K/d fiducial spectra: require physical primaries (strong-decay daughters included)"}; + + // Optional diagnostics leave the original selections and histograms intact. + Configurable lstarEnableMCProxySpecies{"lstarEnableMCProxySpecies", 0, "Book selected MC proxy species x TOF/proton/multiplicity states and retained-parent controls"}; + Configurable lstarEnableProxyPIDProbes{"lstarEnableProxyPIDProbes", 0, "Book separate loose-TPC/TOF matched pair probes for PID-versus-proxy-mass fits (data or reconstructed MC)"}; + Configurable lstarProxyProbeTPCDeMax{"lstarProxyProbeTPCDeMax", 6.f, "Loose d-slot TPC envelope for PID probes; 0 < cut <= 6"}; + Configurable lstarProxyProbeTPCKaMax{"lstarProxyProbeTPCKaMax", 6.f, "Loose K-slot TPC envelope for PID probes; 0 < cut <= 6"}; + Configurable lstarProxyProbeTOFDeMax{"lstarProxyProbeTOFDeMax", 8.f, "Loose d-slot TOF envelope for PID probes; 0 < cut <= 8"}; + Configurable lstarProxyProbeTOFKaMax{"lstarProxyProbeTOFKaMax", 8.f, "Loose K-slot TOF envelope for PID probes; 0 < cut <= 8"}; + + struct RetainedMother { + int64_t id = -1; + int pdg = 0; + }; + + struct TruthInfo { + int64_t id = -1; + int collisionId = -1; + int pdg = 0; + float px = 0.f, py = 0.f, pz = 0.f; + bool primary = false; + std::vector lambdaMothers; + std::vector diagnosticMothers; + }; + struct KaonCand { - float px, py, pz; - int charge; - int tid; + float px = 0.f, py = 0.f, pz = 0.f; + bool hasTOF = false; + int charge = 0; + int tid = -1; + TruthInfo truth{}; }; // PID flags describe detector compatibility, not truth-level purity. struct ProxyCand { - float px, py, pz; - float pxFull, pyFull, pzFull; - bool protonLike; - bool hasTOF; - bool protonTPCCompatible; - int charge; - int tid; + float px = 0.f, py = 0.f, pz = 0.f; + float pxFull = 0.f, pyFull = 0.f, pzFull = 0.f; + bool protonLike = false; + bool hasTOF = false; + bool protonTPCCompatible = false; + int charge = 0; + int tid = -1; + TruthInfo truth{}; }; struct ProtonCand { - float px, py, pz; - int charge; - int tid; + float px = 0.f, py = 0.f, pz = 0.f; + int charge = 0; + int tid = -1; + TruthInfo truth{}; }; // Helpers for invariant-mass kinematics @@ -226,7 +280,6 @@ struct Lambdastarproxy { float zvtx = 0.f; std::vector kaons; std::vector protons; - std::vector proxies; }; // Keep last N events for event-mixing @@ -309,7 +362,38 @@ struct Lambdastarproxy { return fallback; } - static int chargeFromPdg(int pdg) + template + void fillEventMultiplicityCounters(TCollision const& collision, float eventMult) + { + // Use the exact estimator of the pair maps, including the legacy track-count + // fallback. Nonfinite values have no percentile and are counted in overflow. + histos.fill(HIST("hEventMultiplicity"), std::isfinite(eventMult) ? eventMult : FT0MPercentileMax); + + // Keep the raw FT0M denominator separate: fallback track counts must never + // acquire an FT0M percentile label. Missing/nonfinite FT0M goes to overflow; + // finite negative and >=100 values retain their underflow/overflow meaning. + float ft0m = FT0MPercentileMax; + auto status = FT0MEventStatus::Unavailable; + if constexpr (requires { collision.centFT0M(); }) { + const float value = collision.centFT0M(); + if (!std::isfinite(value)) { + status = FT0MEventStatus::NonFinite; + } else { + ft0m = value; + if (value < 0.f) { + status = FT0MEventStatus::Negative; + } else if (value >= FT0MPercentileMax) { + status = FT0MEventStatus::OutOfRange; + } else { + status = FT0MEventStatus::Valid; + } + } + } + histos.fill(HIST("hEventFT0M"), ft0m); + histos.fill(HIST("hEventFT0MStatus"), static_cast(status)); + } + + int chargeFromPdg(int pdg) const { const int apdg = std::abs(pdg); @@ -322,11 +406,15 @@ struct Lambdastarproxy { switch (apdg) { case PdgElectron: // e case PdgMuon: // mu - case PdgPion: // pi - case PdgKaon: // K - case PdgProton: // p + return (pdg >= 0) ? -1 : 1; + case PdgPion: // pi + case PdgKaon: // K + case PdgProton: // p return (pdg >= 0) ? 1 : -1; default: + if (auto* particle = pdgDB->GetParticle(pdg)) { + return static_cast(std::lround(particle->Charge() / 3.)); + } return 0; } } @@ -460,6 +548,12 @@ struct Lambdastarproxy { void init(o2::framework::InitContext&) { + if (doprocess && doprocessMCReco) { + LOGF(fatal, "Enable either process (data) or processMCReco, never both"); + } + if (lstarNoMixedEvents.value < 0 || lstarSparseMultBins.value <= 0) { + LOGF(fatal, "lstarNoMixedEvents must be nonnegative; lstarSparseMultBins must be positive"); + } AxisSpec massAxis{200, 1.4, 1.9, "M_{pK} (GeV/c^{2})"}; AxisSpec ptAxis{100, 0., 10., "p_{T} (GeV/c)"}; AxisSpec nsAxis{500, -50., 50., "n#sigma"}; @@ -467,7 +561,7 @@ struct Lambdastarproxy { AxisSpec pAxis{100, 0., 10., "p (GeV/c)"}; AxisSpec etaAxis{80, -2., 2., "#eta"}; AxisSpec phiAxis{64, 0., o2::constants::math::TwoPI, "#varphi"}; - AxisSpec centAxis{100, 0., 100., "multiplicity/centrality"}; + AxisSpec centAxis{lstarSparseMultBins.value, 0., 100., "multiplicity/centrality"}; AxisSpec pdgAxis{10001, -5000.5, 5000.5, "PDG code"}; @@ -476,13 +570,28 @@ struct Lambdastarproxy { AxisSpec dcaXYAxis{200, -0.2, 0.2, "DCA_{xy} (cm)"}; AxisSpec dcaZAxis{200, -0.2, 0.2, "DCA_{z} (cm)"}; - // Count reconstructed collisions received by the data process, before any + // Count reconstructed collisions received by the data or reco-MC process, before any // task event cuts and after all of them, independently of track candidates. histos.add("hEventSelection", "Proxy event selection;Selection stage;Events", HistType::kTH1D, {AxisSpec{2, 0.5, 2.5, "Selection stage"}}); histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(1, "Before event selection"); histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(2, "After event selection"); + // Small event-level denominators, independent of candidate and QA switches. + // Double storage and Sumw2 preserve large merged event counts and errors. + histos.add("hEventFT0M", "Selected reconstructed events;Raw FT0M percentile;Events", + HistType::kTH1D, {AxisSpec{FT0MPercentileBins, 0., FT0MPercentileMax, "Raw FT0M percentile"}}, true); + histos.add("hEventMultiplicity", "Selected reconstructed events (same estimator as pair maps);Multiplicity/centrality;Events", + HistType::kTH1D, {centAxis}, true); + histos.add("hEventFT0MStatus", "Selected reconstructed events;Raw FT0M status;Events", + HistType::kTH1D, {AxisSpec{static_cast(FT0MEventStatus::Unavailable), 0.5, static_cast(FT0MEventStatus::Unavailable) + 0.5, "Raw FT0M status"}}, true); + auto* ft0mStatusAxis = histos.get(HIST("hEventFT0MStatus"))->GetXaxis(); + ft0mStatusAxis->SetBinLabel(static_cast(FT0MEventStatus::Valid), "Finite FT0M [0,100)"); + ft0mStatusAxis->SetBinLabel(static_cast(FT0MEventStatus::Negative), "Negative FT0M"); + ft0mStatusAxis->SetBinLabel(static_cast(FT0MEventStatus::OutOfRange), "FT0M >= 100"); + ft0mStatusAxis->SetBinLabel(static_cast(FT0MEventStatus::NonFinite), "Nonfinite FT0M"); + ft0mStatusAxis->SetBinLabel(static_cast(FT0MEventStatus::Unavailable), "FT0M unavailable"); + // Invariant-mass spectra histos.add("hInvMassPKUnlike", "pK invariant mass (unlike-sign);M_{pK} (GeV/c^{2});Counts", @@ -511,61 +620,74 @@ struct Lambdastarproxy { mapAxes.emplace_back(1000, 1.4, 2.4, "M_{pK}^{full} (GeV/c^{2})"); mapAxes.emplace_back(2, -0.5, 1.5, "passes full-pK rapidity (0/1)"); for (auto const& pairClass : {"Unlike", "Like", "Mixed"}) { + if (std::strcmp(pairClass, "Mixed") == 0 && lstarNoMixedEvents.value == 0) { + continue; + } const std::string spectrumName = std::string("hLambdaStarProxyControl") + pairClass + "Sparse"; const std::string mapName = std::string("hLambdaStarProxyControlFullPK") + pairClass + "Sparse"; histos.add(spectrumName.c_str(), "Inclusive proxy acceptance; PID compatibility controls", HistType::kTHnSparseF, controlAxes, true); - histos.add(mapName.c_str(), "Inclusive proxy acceptance; full-pK control map", HistType::kTHnSparseF, mapAxes, true); + if (lstarEnableFullPKMaps.value != 0) { + histos.add(mapName.c_str(), "Inclusive proxy acceptance; full-pK control map", HistType::kTHnSparseF, mapAxes, true); + } } } - histos.add( - "hLambdaStarProxyVsFullPKMixedSparse", - "d-selected mixed-event pairs: full-p proton hypothesis vs (d/2)K proxy;" - "M_{(d/2)K} (GeV/c^{2});" - "M_{pK}^{full} (GeV/c^{2});" - "p_{T}^{(d/2)K} (GeV/c);" - "multiplicity/centrality", - HistType::kTHnSparseF, - {AxisSpec{400, 1.4, 1.8, - "M_{(d/2)K} (GeV/c^{2})"}, + if (lstarEnableFullPKMaps.value != 0) { + if (lstarNoMixedEvents.value > 0) { + histos.add( + "hLambdaStarProxyVsFullPKMixedSparse", + "d-selected mixed-event pairs: full-p proton hypothesis vs (d/2)K proxy;" + "M_{(d/2)K} (GeV/c^{2});" + "M_{pK}^{full} (GeV/c^{2});" + "p_{T}^{(d/2)K} (GeV/c);" + "multiplicity/centrality", + HistType::kTHnSparseF, + {AxisSpec{400, 1.4, 1.8, + "M_{(d/2)K} (GeV/c^{2})"}, - AxisSpec{400, 1.4, 1.8, - "M_{pK}^{full} (GeV/c^{2})"}, + AxisSpec{400, 1.4, 1.8, + "M_{pK}^{full} (GeV/c^{2})"}, - AxisSpec{100, 0., 10., - "p_{T}^{(d/2)K} (GeV/c)"}, + AxisSpec{100, 0., 10., + "p_{T}^{(d/2)K} (GeV/c)"}, - centAxis}); + centAxis}); + } + } - histos.add( - "hLambdaStarProxyProtonLikeUnlikeSparse", - "d-selected and proton-like OS proxy candidates;" - "M_{(d/2)K} (GeV/c^{2});" - "p_{T}^{(d/2)K} (GeV/c);" - "multiplicity/centrality", - HistType::kTHnSparseF, - {AxisSpec{400, 1.4, 1.8, - "M_{(d/2)K} (GeV/c^{2})"}, + if (lstarEnableProtonLikeSparse.value != 0) { + histos.add( + "hLambdaStarProxyProtonLikeUnlikeSparse", + "d-selected and proton-like OS proxy candidates;" + "M_{(d/2)K} (GeV/c^{2});" + "p_{T}^{(d/2)K} (GeV/c);" + "multiplicity/centrality", + HistType::kTHnSparseF, + {AxisSpec{400, 1.4, 1.8, + "M_{(d/2)K} (GeV/c^{2})"}, - AxisSpec{100, 0., 10., - "p_{T}^{(d/2)K} (GeV/c)"}, + AxisSpec{100, 0., 10., + "p_{T}^{(d/2)K} (GeV/c)"}, - centAxis}); + centAxis}); + } - histos.add( - "hLambdaStarProxyProtonLikeLikeSparse", - "d-selected and proton-like LS proxy candidates;" - "M_{(d/2)K} (GeV/c^{2});" - "p_{T}^{(d/2)K} (GeV/c);" - "multiplicity/centrality", - HistType::kTHnSparseF, - {AxisSpec{400, 1.4, 1.8, - "M_{(d/2)K} (GeV/c^{2})"}, + if (lstarEnableProtonLikeSparse.value != 0) { + histos.add( + "hLambdaStarProxyProtonLikeLikeSparse", + "d-selected and proton-like LS proxy candidates;" + "M_{(d/2)K} (GeV/c^{2});" + "p_{T}^{(d/2)K} (GeV/c);" + "multiplicity/centrality", + HistType::kTHnSparseF, + {AxisSpec{400, 1.4, 1.8, + "M_{(d/2)K} (GeV/c^{2})"}, - AxisSpec{100, 0., 10., - "p_{T}^{(d/2)K} (GeV/c)"}, + AxisSpec{100, 0., 10., + "p_{T}^{(d/2)K} (GeV/c)"}, - centAxis}); + centAxis}); + } histos.add("hLambdaStarPKUnlikeSparse", "#Lambda^{*}(1520) pK unlike-sign candidates;M_{pK} (GeV/c^{2});p_{T}^{pK} (GeV/c);multiplicity/centrality", @@ -579,11 +701,13 @@ struct Lambdastarproxy { {AxisSpec{400, 1.4, 1.8, "M_{pK} (GeV/c^{2})"}, AxisSpec{100, 0., 10., "p_{T}^{pK} (GeV/c)"}, centAxis}); - histos.add("hLambdaStarPKMixedSparse", - "#Lambda^{*}(1520) pK mixed-event candidates;M_{pK} (GeV/c^{2});p_{T}^{pK} (GeV/c);multiplicity/centrality", - HistType::kTHnSparseF, - {AxisSpec{400, 1.4, 1.8, "M_{pK} (GeV/c^{2})"}, - AxisSpec{100, 0., 10., "p_{T}^{pK} (GeV/c)"}, centAxis}); + if (lstarNoMixedEvents.value > 0) { + histos.add("hLambdaStarPKMixedSparse", + "#Lambda^{*}(1520) pK mixed-event candidates;M_{pK} (GeV/c^{2});p_{T}^{pK} (GeV/c);multiplicity/centrality", + HistType::kTHnSparseF, + {AxisSpec{400, 1.4, 1.8, "M_{pK} (GeV/c^{2})"}, + AxisSpec{100, 0., 10., "p_{T}^{pK} (GeV/c)"}, centAxis}); + } // THnSparse for deuteron-proxy invariant-mass analysis (mass, pT, multiplicity/centrality) histos.add("hLambdaStarProxySparse", @@ -598,49 +722,55 @@ struct Lambdastarproxy { {AxisSpec{400, 1.4, 1.8, "M_{p_{proxy}K} (GeV/c^{2})"}, AxisSpec{100, 0., 10., "p_{T}^{p_{proxy}K} (GeV/c)"}, centAxis}); - histos.add("hLambdaStarProxyMixedSparse", - "#Lambda^{*}(1520) deuteron-proxy mixed-event candidates;M_{p_{proxy}K} (GeV/c^{2});p_{T}^{p_{proxy}K} (GeV/c);multiplicity/centrality", - HistType::kTHnSparseF, - {AxisSpec{400, 1.4, 1.8, "M_{p_{proxy}K} (GeV/c^{2})"}, - AxisSpec{100, 0., 10., "p_{T}^{p_{proxy}K} (GeV/c)"}, centAxis}); + if (lstarNoMixedEvents.value > 0) { + histos.add("hLambdaStarProxyMixedSparse", + "#Lambda^{*}(1520) deuteron-proxy mixed-event candidates;M_{p_{proxy}K} (GeV/c^{2});p_{T}^{p_{proxy}K} (GeV/c);multiplicity/centrality", + HistType::kTHnSparseF, + {AxisSpec{400, 1.4, 1.8, "M_{p_{proxy}K} (GeV/c^{2})"}, + AxisSpec{100, 0., 10., "p_{T}^{p_{proxy}K} (GeV/c)"}, centAxis}); + } - histos.add( - "hLambdaStarProxyVsFullPKUnlikeSparse", - "d-selected OS pairs: full-p proton hypothesis vs (d/2)K proxy;" - "M_{(d/2)K} (GeV/c^{2});" - "M_{pK}^{full} (GeV/c^{2});" - "p_{T}^{(d/2)K} (GeV/c);" - "multiplicity/centrality", - HistType::kTHnSparseF, - {AxisSpec{400, 1.4, 1.8, - "M_{(d/2)K} (GeV/c^{2})"}, + if (lstarEnableFullPKMaps.value != 0) { + histos.add( + "hLambdaStarProxyVsFullPKUnlikeSparse", + "d-selected OS pairs: full-p proton hypothesis vs (d/2)K proxy;" + "M_{(d/2)K} (GeV/c^{2});" + "M_{pK}^{full} (GeV/c^{2});" + "p_{T}^{(d/2)K} (GeV/c);" + "multiplicity/centrality", + HistType::kTHnSparseF, + {AxisSpec{400, 1.4, 1.8, + "M_{(d/2)K} (GeV/c^{2})"}, - AxisSpec{400, 1.4, 1.8, - "M_{pK}^{full} (GeV/c^{2})"}, + AxisSpec{400, 1.4, 1.8, + "M_{pK}^{full} (GeV/c^{2})"}, - AxisSpec{100, 0., 10., - "p_{T}^{(d/2)K} (GeV/c)"}, + AxisSpec{100, 0., 10., + "p_{T}^{(d/2)K} (GeV/c)"}, - centAxis}); + centAxis}); + } - histos.add( - "hLambdaStarProxyVsFullPKLikeSparse", - "d-selected LS pairs: full-p proton hypothesis vs (d/2)K proxy;" - "M_{(d/2)K} (GeV/c^{2});" - "M_{pK}^{full} (GeV/c^{2});" - "p_{T}^{(d/2)K} (GeV/c);" - "multiplicity/centrality", - HistType::kTHnSparseF, - {AxisSpec{400, 1.4, 1.8, - "M_{(d/2)K} (GeV/c^{2})"}, + if (lstarEnableFullPKMaps.value != 0) { + histos.add( + "hLambdaStarProxyVsFullPKLikeSparse", + "d-selected LS pairs: full-p proton hypothesis vs (d/2)K proxy;" + "M_{(d/2)K} (GeV/c^{2});" + "M_{pK}^{full} (GeV/c^{2});" + "p_{T}^{(d/2)K} (GeV/c);" + "multiplicity/centrality", + HistType::kTHnSparseF, + {AxisSpec{400, 1.4, 1.8, + "M_{(d/2)K} (GeV/c^{2})"}, - AxisSpec{400, 1.4, 1.8, - "M_{pK}^{full} (GeV/c^{2})"}, + AxisSpec{400, 1.4, 1.8, + "M_{pK}^{full} (GeV/c^{2})"}, - AxisSpec{100, 0., 10., - "p_{T}^{(d/2)K} (GeV/c)"}, + AxisSpec{100, 0., 10., + "p_{T}^{(d/2)K} (GeV/c)"}, - centAxis}); + centAxis}); + } } // Deuteron-proxy invariant mass (p_{proxy} from d/2 combined with K) @@ -648,248 +778,783 @@ struct Lambdastarproxy { "#Lambda^{*} proxy invariant mass from (d/2 + K);M_{p_{proxy}K} (GeV/c^{2});Counts", HistType::kTH1F, {massAxis}); - // Inclusive PID QA before final candidate PID cuts: after track-quality cuts only - // TPC dE/dx vs total momentum - histos.add("hTPCdEdxVsP", - "TPC dE/dx vs p;p (GeV/c);dE/dx (arb. units);Counts", - HistType::kTH2F, {pAxis, dEdxAxis}); - - // TOF #beta vs total momentum - histos.add("hTOFBetaVsP", - "TOF #beta vs p;p (GeV/c);#beta_{TOF};Counts", - HistType::kTH2F, {pAxis, betaAxis}); - - histos.add("hHasTOFVsP", - "TOF matching flag vs p;p (GeV/c);has TOF match;Counts", - HistType::kTH2F, {pAxis, tofMatchAxis}); - - histos.add("hHasTOFVsPt", - "TOF matching flag vs p_{T};p_{T} (GeV/c);has TOF match;Counts", - HistType::kTH2F, {ptAxis, tofMatchAxis}); - - // --- Per-species inclusive PID QA before final candidate PID cuts --- - // Species tagging here uses classifyPidSpecies() and is meant only for QA. - histos.add("hTPCdEdxVsP_Pi", - "TPC dE/dx vs p (tagged #pi);p (GeV/c);dE/dx (arb. units);Counts", - HistType::kTH2F, {pAxis, dEdxAxis}); - histos.add("hTPCdEdxVsP_K", - "TPC dE/dx vs p (tagged K);p (GeV/c);dE/dx (arb. units);Counts", - HistType::kTH2F, {pAxis, dEdxAxis}); - histos.add("hTPCdEdxVsP_P", - "TPC dE/dx vs p (tagged p);p (GeV/c);dE/dx (arb. units);Counts", - HistType::kTH2F, {pAxis, dEdxAxis}); - histos.add("hTPCdEdxVsP_D", - "TPC dE/dx vs p (tagged d);p (GeV/c);dE/dx (arb. units);Counts", - HistType::kTH2F, {pAxis, dEdxAxis}); - - histos.add("hTOFBetaVsP_Pi", - "TOF #beta vs p (tagged #pi);p (GeV/c);#beta_{TOF};Counts", - HistType::kTH2F, {pAxis, betaAxis}); - histos.add("hTOFBetaVsP_K", - "TOF #beta vs p (tagged K);p (GeV/c);#beta_{TOF};Counts", - HistType::kTH2F, {pAxis, betaAxis}); - histos.add("hTOFBetaVsP_P", - "TOF #beta vs p (tagged p);p (GeV/c);#beta_{TOF};Counts", - HistType::kTH2F, {pAxis, betaAxis}); - histos.add("hTOFBetaVsP_D", - "TOF #beta vs p (tagged d);p (GeV/c);#beta_{TOF};Counts", - HistType::kTH2F, {pAxis, betaAxis}); - - // Inclusive tagged nSigma QA before final candidate PID cuts: after track-quality cuts only. - // These are separate from the final-candidate PID QA histograms. - histos.add("hNsigmaTPCPionTaggedVsP", - "TPC n#sigma_{#pi} vs p for tagged #pi;p (GeV/c);n#sigma^{TPC}_{#pi};Counts", - HistType::kTH2F, {pAxis, nsAxis}); - histos.add("hNsigmaTOFPionTaggedVsP", - "TOF n#sigma_{#pi} vs p for tagged #pi;p (GeV/c);n#sigma^{TOF}_{#pi};Counts", - HistType::kTH2F, {pAxis, nsAxis}); - - histos.add("hNsigmaTPCKaonTaggedVsP", - "TPC n#sigma_{K} vs p for tagged K;p (GeV/c);n#sigma^{TPC}_{K};Counts", - HistType::kTH2F, {pAxis, nsAxis}); - histos.add("hNsigmaTOFKaonTaggedVsP", - "TOF n#sigma_{K} vs p for tagged K;p (GeV/c);n#sigma^{TOF}_{K};Counts", - HistType::kTH2F, {pAxis, nsAxis}); - - histos.add("hNsigmaTPCProtonTaggedVsP", - "TPC n#sigma_{p} vs p for tagged p;p (GeV/c);n#sigma^{TPC}_{p};Counts", - HistType::kTH2F, {pAxis, nsAxis}); - histos.add("hNsigmaTOFProtonTaggedVsP", - "TOF n#sigma_{p} vs p for tagged p;p (GeV/c);n#sigma^{TOF}_{p};Counts", - HistType::kTH2F, {pAxis, nsAxis}); - - histos.add("hNsigmaTPCDeuteronTaggedVsP", - "TPC n#sigma_{d} vs p for tagged d;p (GeV/c);n#sigma^{TPC}_{d};Counts", - HistType::kTH2F, {pAxis, nsAxis}); - histos.add("hNsigmaTOFDeuteronTaggedVsP", - "TOF n#sigma_{d} vs p for tagged d;p (GeV/c);n#sigma^{TOF}_{d};Counts", - HistType::kTH2F, {pAxis, nsAxis}); - - // --- MC QA (AO2D-MC mode) --- - histos.add("hMcPrimariesPtEta", - "MC charged physical primaries; p_{T} (GeV/c); #eta; Counts", - HistType::kTH2F, {ptAxis, etaAxis}); - - histos.add("hRecoMatchedPdg", - "Reco tracks matched to MC (PDG); PDG code; Counts", - HistType::kTH1F, {pdgAxis}); - - histos.add("hRecoFakePtEta", - "Reco tracks without MC label (fakes / unmatched); p_{T} (GeV/c); #eta; Counts", - HistType::kTH2F, {ptAxis, etaAxis}); - - // Deuteron-proxy kinematics and PID QA - histos.add("hDeuteronProxyPt", - "Deuteron proxy p_{T};p_{T} (GeV/c);Counts", - HistType::kTH1F, {ptAxis}); - histos.add("hDeuteronProxyEta", - "Deuteron proxy #eta;#eta;Counts", - HistType::kTH1F, {etaAxis}); - histos.add("hDeuteronProxyPhi", - "Deuteron proxy #varphi;#varphi;Counts", - HistType::kTH1F, {phiAxis}); - - histos.add("hNsigmaTPCDeuteron", - "TPC n#sigma_{d};n#sigma^{TPC}_{d};Counts", - HistType::kTH1F, {nsAxis}); - histos.add("hNsigmaTOFDeuteron", - "TOF n#sigma_{d};n#sigma^{TOF}_{d};Counts", - HistType::kTH1F, {nsAxis}); - - histos.add("hNsigmaTPCDeuteronVsP", - "TPC n#sigma_{d} vs p; p (GeV/c); n#sigma^{TPC}_{d};Counts", - HistType::kTH2F, {pAxis, nsAxis}); - histos.add("hNsigmaTOFDeuteronVsP", - "TOF n#sigma_{d} vs p; p (GeV/c); n#sigma^{TOF}_{d};Counts", - HistType::kTH2F, {pAxis, nsAxis}); - - // Kaon kinematics and PID QA - histos.add("hKaonPt", - "Kaon p_{T};p_{T} (GeV/c);Counts", - HistType::kTH1F, {ptAxis}); - histos.add("hKaonEta", - "Kaon #eta;#eta;Counts", - HistType::kTH1F, {etaAxis}); - histos.add("hKaonPhi", - "Kaon #varphi;#varphi;Counts", - HistType::kTH1F, {phiAxis}); - - histos.add("hNsigmaTPCKaon", - "TPC n#sigma_{K};n#sigma^{TPC}_{K};Counts", - HistType::kTH1F, {nsAxis}); - histos.add("hNsigmaTOFKaon", - "TOF n#sigma_{K};n#sigma^{TOF}_{K};Counts", - HistType::kTH1F, {nsAxis}); - - histos.add("hNsigmaTPCKaonVsP", - "TPC n#sigma_{K} vs p; p (GeV/c); n#sigma^{TPC}_{K};Counts", - HistType::kTH2F, {pAxis, nsAxis}); - histos.add("hNsigmaTOFKaonVsP", - "TOF n#sigma_{K} vs p; p (GeV/c); n#sigma^{TOF}_{K};Counts", - HistType::kTH2F, {pAxis, nsAxis}); - - // --- Candidate PID QA after final selected K/p/d PID cuts --- - // These histograms are needed for PID studies in the same pT intervals used by the analysis. - histos.add("hTOFBetaVsPt_K", - "TOF #beta vs p_{T} for selected K;p_{T} (GeV/c);#beta_{TOF};Counts", - HistType::kTH2F, {ptAxis, betaAxis}); - histos.add("hTOFBetaVsPt_P", - "TOF #beta vs p_{T} for selected p;p_{T} (GeV/c);#beta_{TOF};Counts", - HistType::kTH2F, {ptAxis, betaAxis}); - histos.add("hTOFBetaVsPt_D", - "TOF #beta vs p_{T} for selected d;p_{T} (GeV/c);#beta_{TOF};Counts", - HistType::kTH2F, {ptAxis, betaAxis}); - - histos.add("hNsigmaTPCKaonVsPt", - "TPC n#sigma_{K} vs p_{T};p_{T} (GeV/c);n#sigma^{TPC}_{K};Counts", - HistType::kTH2F, {ptAxis, nsAxis}); - histos.add("hNsigmaTOFKaonVsPt", - "TOF n#sigma_{K} vs p_{T};p_{T} (GeV/c);n#sigma^{TOF}_{K};Counts", - HistType::kTH2F, {ptAxis, nsAxis}); - - histos.add("hNsigmaTPCProtonVsP", - "TPC n#sigma_{p} vs p;p (GeV/c);n#sigma^{TPC}_{p};Counts", - HistType::kTH2F, {pAxis, nsAxis}); - histos.add("hNsigmaTOFProtonVsP", - "TOF n#sigma_{p} vs p;p (GeV/c);n#sigma^{TOF}_{p};Counts", - HistType::kTH2F, {pAxis, nsAxis}); - histos.add("hNsigmaTPCProtonVsPt", - "TPC n#sigma_{p} vs p_{T};p_{T} (GeV/c);n#sigma^{TPC}_{p};Counts", - HistType::kTH2F, {ptAxis, nsAxis}); - histos.add("hNsigmaTOFProtonVsPt", - "TOF n#sigma_{p} vs p_{T};p_{T} (GeV/c);n#sigma^{TOF}_{p};Counts", - HistType::kTH2F, {ptAxis, nsAxis}); - - histos.add("hNsigmaTPCDeuteronVsPt", - "TPC n#sigma_{d} vs p_{T};p_{T} (GeV/c);n#sigma^{TPC}_{d};Counts", - HistType::kTH2F, {ptAxis, nsAxis}); - histos.add("hNsigmaTOFDeuteronVsPt", - "TOF n#sigma_{d} vs p_{T};p_{T} (GeV/c);n#sigma^{TOF}_{d};Counts", - HistType::kTH2F, {ptAxis, nsAxis}); - histos.add( - "hNsigmaTPCProtonForSelectedDeuteronVsPt", - "TPC n#sigma_{p} for selected d candidates vs p_{T};" - "p_{T} (GeV/c);" - "n#sigma^{TPC}_{p};Counts", - HistType::kTH2F, - {ptAxis, nsAxis}); - - histos.add( - "hNsigmaTOFProtonForSelectedDeuteronVsPt", - "TOF n#sigma_{p} for selected d candidates vs p_{T};" - "p_{T} (GeV/c);" - "n#sigma^{TOF}_{p};Counts", - HistType::kTH2F, - {ptAxis, nsAxis}); - - histos.add( - "hTPCNsigmaDeVsPrForSelectedDeuteron", - "TPC PID overlap for selected d candidates;" - "n#sigma^{TPC}_{d};" - "n#sigma^{TPC}_{p};Counts", - HistType::kTH2F, - {nsAxis, nsAxis}); - - histos.add( - "hTOFNsigmaDeVsPrForSelectedDeuteron", - "TOF PID overlap for selected d candidates;" - "n#sigma^{TOF}_{d};" - "n#sigma^{TOF}_{p};Counts", - HistType::kTH2F, - {nsAxis, nsAxis}); - - histos.add("hTPCvsTOFNsigma_K", - "TPC vs TOF n#sigma for selected K;n#sigma^{TPC}_{K};n#sigma^{TOF}_{K};Counts", - HistType::kTH2F, {nsAxis, nsAxis}); - histos.add("hTPCvsTOFNsigma_P", - "TPC vs TOF n#sigma for selected p;n#sigma^{TPC}_{p};n#sigma^{TOF}_{p};Counts", - HistType::kTH2F, {nsAxis, nsAxis}); - histos.add("hTPCvsTOFNsigma_D", - "TPC vs TOF n#sigma for selected d;n#sigma^{TPC}_{d};n#sigma^{TOF}_{d};Counts", - HistType::kTH2F, {nsAxis, nsAxis}); - - // --- DCA QA for final selected K/p/d candidates --- - // Filled only when lstarEnablePidQA = 1. - histos.add("hDCAxyVsPt_K", - "DCA_{xy} vs p_{T} distribution for selected K;p_{T} (GeV/c);DCA_{xy} (cm);Counts", - HistType::kTH2F, {ptAxis, dcaXYAxis}); - histos.add("hDCAzVsPt_K", - "DCA_{z} vs p_{T} distribution for selected K;p_{T} (GeV/c);DCA_{z} (cm);Counts", - HistType::kTH2F, {ptAxis, dcaZAxis}); - - histos.add("hDCAxyVsPt_P", - "DCA_{xy} vs p_{T} distribution for selected p;p_{T} (GeV/c);DCA_{xy} (cm);Counts", - HistType::kTH2F, {ptAxis, dcaXYAxis}); - histos.add("hDCAzVsPt_P", - "DCA_{z} vs p_{T} distribution for selected p;p_{T} (GeV/c);DCA_{z} (cm);Counts", - HistType::kTH2F, {ptAxis, dcaZAxis}); - - histos.add("hDCAxyVsPt_D", - "DCA_{xy} vs p_{T} distribution for selected d;p_{T} (GeV/c);DCA_{xy} (cm);Counts", - HistType::kTH2F, {ptAxis, dcaXYAxis}); - histos.add("hDCAzVsPt_D", - "DCA_{z} vs p_{T} distribution for selected d;p_{T} (GeV/c);DCA_{z} (cm);Counts", - HistType::kTH2F, {ptAxis, dcaZAxis}); + if (lstarEnablePidQA.value != 0) { + // Inclusive PID QA before final candidate PID cuts: after track-quality cuts only + // TPC dE/dx vs total momentum + histos.add("hTPCdEdxVsP", + "TPC dE/dx vs p;p (GeV/c);dE/dx (arb. units);Counts", + HistType::kTH2F, {pAxis, dEdxAxis}); + + // TOF #beta vs total momentum + histos.add("hTOFBetaVsP", + "TOF #beta vs p;p (GeV/c);#beta_{TOF};Counts", + HistType::kTH2F, {pAxis, betaAxis}); + + histos.add("hHasTOFVsP", + "TOF matching flag vs p;p (GeV/c);has TOF match;Counts", + HistType::kTH2F, {pAxis, tofMatchAxis}); + + histos.add("hHasTOFVsPt", + "TOF matching flag vs p_{T};p_{T} (GeV/c);has TOF match;Counts", + HistType::kTH2F, {ptAxis, tofMatchAxis}); + + // --- Per-species inclusive PID QA before final candidate PID cuts --- + // Species tagging here uses classifyPidSpecies() and is meant only for QA. + histos.add("hTPCdEdxVsP_Pi", + "TPC dE/dx vs p (tagged #pi);p (GeV/c);dE/dx (arb. units);Counts", + HistType::kTH2F, {pAxis, dEdxAxis}); + histos.add("hTPCdEdxVsP_K", + "TPC dE/dx vs p (tagged K);p (GeV/c);dE/dx (arb. units);Counts", + HistType::kTH2F, {pAxis, dEdxAxis}); + histos.add("hTPCdEdxVsP_P", + "TPC dE/dx vs p (tagged p);p (GeV/c);dE/dx (arb. units);Counts", + HistType::kTH2F, {pAxis, dEdxAxis}); + histos.add("hTPCdEdxVsP_D", + "TPC dE/dx vs p (tagged d);p (GeV/c);dE/dx (arb. units);Counts", + HistType::kTH2F, {pAxis, dEdxAxis}); + + histos.add("hTOFBetaVsP_Pi", + "TOF #beta vs p (tagged #pi);p (GeV/c);#beta_{TOF};Counts", + HistType::kTH2F, {pAxis, betaAxis}); + histos.add("hTOFBetaVsP_K", + "TOF #beta vs p (tagged K);p (GeV/c);#beta_{TOF};Counts", + HistType::kTH2F, {pAxis, betaAxis}); + histos.add("hTOFBetaVsP_P", + "TOF #beta vs p (tagged p);p (GeV/c);#beta_{TOF};Counts", + HistType::kTH2F, {pAxis, betaAxis}); + histos.add("hTOFBetaVsP_D", + "TOF #beta vs p (tagged d);p (GeV/c);#beta_{TOF};Counts", + HistType::kTH2F, {pAxis, betaAxis}); + + // Inclusive tagged nSigma QA before final candidate PID cuts: after track-quality cuts only. + // These are separate from the final-candidate PID QA histograms. + histos.add("hNsigmaTPCPionTaggedVsP", + "TPC n#sigma_{#pi} vs p for tagged #pi;p (GeV/c);n#sigma^{TPC}_{#pi};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + histos.add("hNsigmaTOFPionTaggedVsP", + "TOF n#sigma_{#pi} vs p for tagged #pi;p (GeV/c);n#sigma^{TOF}_{#pi};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + + histos.add("hNsigmaTPCKaonTaggedVsP", + "TPC n#sigma_{K} vs p for tagged K;p (GeV/c);n#sigma^{TPC}_{K};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + histos.add("hNsigmaTOFKaonTaggedVsP", + "TOF n#sigma_{K} vs p for tagged K;p (GeV/c);n#sigma^{TOF}_{K};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + + histos.add("hNsigmaTPCProtonTaggedVsP", + "TPC n#sigma_{p} vs p for tagged p;p (GeV/c);n#sigma^{TPC}_{p};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + histos.add("hNsigmaTOFProtonTaggedVsP", + "TOF n#sigma_{p} vs p for tagged p;p (GeV/c);n#sigma^{TOF}_{p};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + + histos.add("hNsigmaTPCDeuteronTaggedVsP", + "TPC n#sigma_{d} vs p for tagged d;p (GeV/c);n#sigma^{TPC}_{d};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + histos.add("hNsigmaTOFDeuteronTaggedVsP", + "TOF n#sigma_{d} vs p for tagged d;p (GeV/c);n#sigma^{TOF}_{d};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + } + + if (doprocessMCQA && lstarFillMCTruth.value != 0) { + // --- MC QA (AO2D-MC mode) --- + histos.add("hMcPrimariesPtEta", + "MC charged physical primaries; p_{T} (GeV/c); #eta; Counts", + HistType::kTH2F, {ptAxis, etaAxis}); + + histos.add("hRecoMatchedPdg", + "Reco tracks matched to MC (PDG); PDG code; Counts", + HistType::kTH1F, {pdgAxis}); + + histos.add("hRecoFakePtEta", + "Reco tracks without MC label (fakes / unmatched); p_{T} (GeV/c); #eta; Counts", + HistType::kTH2F, {ptAxis, etaAxis}); + } + + if (lstarEnablePidQA.value != 0) { + // Deuteron-proxy kinematics and PID QA + histos.add("hDeuteronProxyPt", + "Deuteron proxy p_{T};p_{T} (GeV/c);Counts", + HistType::kTH1F, {ptAxis}); + histos.add("hDeuteronProxyEta", + "Deuteron proxy #eta;#eta;Counts", + HistType::kTH1F, {etaAxis}); + histos.add("hDeuteronProxyPhi", + "Deuteron proxy #varphi;#varphi;Counts", + HistType::kTH1F, {phiAxis}); + + histos.add("hNsigmaTPCDeuteron", + "TPC n#sigma_{d};n#sigma^{TPC}_{d};Counts", + HistType::kTH1F, {nsAxis}); + histos.add("hNsigmaTOFDeuteron", + "TOF n#sigma_{d};n#sigma^{TOF}_{d};Counts", + HistType::kTH1F, {nsAxis}); + + histos.add("hNsigmaTPCDeuteronVsP", + "TPC n#sigma_{d} vs p; p (GeV/c); n#sigma^{TPC}_{d};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + histos.add("hNsigmaTOFDeuteronVsP", + "TOF n#sigma_{d} vs p; p (GeV/c); n#sigma^{TOF}_{d};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + + // Kaon kinematics and PID QA + histos.add("hKaonPt", + "Kaon p_{T};p_{T} (GeV/c);Counts", + HistType::kTH1F, {ptAxis}); + histos.add("hKaonEta", + "Kaon #eta;#eta;Counts", + HistType::kTH1F, {etaAxis}); + histos.add("hKaonPhi", + "Kaon #varphi;#varphi;Counts", + HistType::kTH1F, {phiAxis}); + + histos.add("hNsigmaTPCKaon", + "TPC n#sigma_{K};n#sigma^{TPC}_{K};Counts", + HistType::kTH1F, {nsAxis}); + histos.add("hNsigmaTOFKaon", + "TOF n#sigma_{K};n#sigma^{TOF}_{K};Counts", + HistType::kTH1F, {nsAxis}); + + histos.add("hNsigmaTPCKaonVsP", + "TPC n#sigma_{K} vs p; p (GeV/c); n#sigma^{TPC}_{K};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + histos.add("hNsigmaTOFKaonVsP", + "TOF n#sigma_{K} vs p; p (GeV/c); n#sigma^{TOF}_{K};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + + // --- Candidate PID QA after final selected K/p/d PID cuts --- + // These histograms are needed for PID studies in the same pT intervals used by the analysis. + histos.add("hTOFBetaVsPt_K", + "TOF #beta vs p_{T} for selected K;p_{T} (GeV/c);#beta_{TOF};Counts", + HistType::kTH2F, {ptAxis, betaAxis}); + histos.add("hTOFBetaVsPt_P", + "TOF #beta vs p_{T} for selected p;p_{T} (GeV/c);#beta_{TOF};Counts", + HistType::kTH2F, {ptAxis, betaAxis}); + histos.add("hTOFBetaVsPt_D", + "TOF #beta vs p_{T} for selected d;p_{T} (GeV/c);#beta_{TOF};Counts", + HistType::kTH2F, {ptAxis, betaAxis}); + + histos.add("hNsigmaTPCKaonVsPt", + "TPC n#sigma_{K} vs p_{T};p_{T} (GeV/c);n#sigma^{TPC}_{K};Counts", + HistType::kTH2F, {ptAxis, nsAxis}); + histos.add("hNsigmaTOFKaonVsPt", + "TOF n#sigma_{K} vs p_{T};p_{T} (GeV/c);n#sigma^{TOF}_{K};Counts", + HistType::kTH2F, {ptAxis, nsAxis}); + + histos.add("hNsigmaTPCProtonVsP", + "TPC n#sigma_{p} vs p;p (GeV/c);n#sigma^{TPC}_{p};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + histos.add("hNsigmaTOFProtonVsP", + "TOF n#sigma_{p} vs p;p (GeV/c);n#sigma^{TOF}_{p};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + histos.add("hNsigmaTPCProtonVsPt", + "TPC n#sigma_{p} vs p_{T};p_{T} (GeV/c);n#sigma^{TPC}_{p};Counts", + HistType::kTH2F, {ptAxis, nsAxis}); + histos.add("hNsigmaTOFProtonVsPt", + "TOF n#sigma_{p} vs p_{T};p_{T} (GeV/c);n#sigma^{TOF}_{p};Counts", + HistType::kTH2F, {ptAxis, nsAxis}); + + histos.add("hNsigmaTPCDeuteronVsPt", + "TPC n#sigma_{d} vs p_{T};p_{T} (GeV/c);n#sigma^{TPC}_{d};Counts", + HistType::kTH2F, {ptAxis, nsAxis}); + histos.add("hNsigmaTOFDeuteronVsPt", + "TOF n#sigma_{d} vs p_{T};p_{T} (GeV/c);n#sigma^{TOF}_{d};Counts", + HistType::kTH2F, {ptAxis, nsAxis}); + histos.add( + "hNsigmaTPCProtonForSelectedDeuteronVsPt", + "TPC n#sigma_{p} for selected d candidates vs p_{T};" + "p_{T} (GeV/c);" + "n#sigma^{TPC}_{p};Counts", + HistType::kTH2F, + {ptAxis, nsAxis}); + + histos.add( + "hNsigmaTOFProtonForSelectedDeuteronVsPt", + "TOF n#sigma_{p} for selected d candidates vs p_{T};" + "p_{T} (GeV/c);" + "n#sigma^{TOF}_{p};Counts", + HistType::kTH2F, + {ptAxis, nsAxis}); + + histos.add( + "hTPCNsigmaDeVsPrForSelectedDeuteron", + "TPC PID overlap for selected d candidates;" + "n#sigma^{TPC}_{d};" + "n#sigma^{TPC}_{p};Counts", + HistType::kTH2F, + {nsAxis, nsAxis}); + + histos.add( + "hTOFNsigmaDeVsPrForSelectedDeuteron", + "TOF PID overlap for selected d candidates;" + "n#sigma^{TOF}_{d};" + "n#sigma^{TOF}_{p};Counts", + HistType::kTH2F, + {nsAxis, nsAxis}); + + histos.add("hTPCvsTOFNsigma_K", + "TPC vs TOF n#sigma for selected K;n#sigma^{TPC}_{K};n#sigma^{TOF}_{K};Counts", + HistType::kTH2F, {nsAxis, nsAxis}); + histos.add("hTPCvsTOFNsigma_P", + "TPC vs TOF n#sigma for selected p;n#sigma^{TPC}_{p};n#sigma^{TOF}_{p};Counts", + HistType::kTH2F, {nsAxis, nsAxis}); + histos.add("hTPCvsTOFNsigma_D", + "TPC vs TOF n#sigma for selected d;n#sigma^{TPC}_{d};n#sigma^{TOF}_{d};Counts", + HistType::kTH2F, {nsAxis, nsAxis}); + + // --- DCA QA for final selected K/p/d candidates --- + // Filled only when lstarEnablePidQA = 1. + histos.add("hDCAxyVsPt_K", + "DCA_{xy} vs p_{T} distribution for selected K;p_{T} (GeV/c);DCA_{xy} (cm);Counts", + HistType::kTH2F, {ptAxis, dcaXYAxis}); + histos.add("hDCAzVsPt_K", + "DCA_{z} vs p_{T} distribution for selected K;p_{T} (GeV/c);DCA_{z} (cm);Counts", + HistType::kTH2F, {ptAxis, dcaZAxis}); + + histos.add("hDCAxyVsPt_P", + "DCA_{xy} vs p_{T} distribution for selected p;p_{T} (GeV/c);DCA_{xy} (cm);Counts", + HistType::kTH2F, {ptAxis, dcaXYAxis}); + histos.add("hDCAzVsPt_P", + "DCA_{z} vs p_{T} distribution for selected p;p_{T} (GeV/c);DCA_{z} (cm);Counts", + HistType::kTH2F, {ptAxis, dcaZAxis}); + + histos.add("hDCAxyVsPt_D", + "DCA_{xy} vs p_{T} distribution for selected d;p_{T} (GeV/c);DCA_{xy} (cm);Counts", + HistType::kTH2F, {ptAxis, dcaXYAxis}); + histos.add("hDCAzVsPt_D", + "DCA_{z} vs p_{T} distribution for selected d;p_{T} (GeV/c);DCA_{z} (cm);Counts", + HistType::kTH2F, {ptAxis, dcaZAxis}); + } + bookMC(); + bookProxyDiagnostics(); + } + + static constexpr int PdgLambdaStar = static_cast(PDG_t::kLambda1520); + static constexpr int PdgDeuteron = static_cast(o2::constants::physics::Pdg::kDeuteron); + // MC outputs are 2D mass/pT histograms. They add no high-dimensional maps. + std::array, 5> mcGenSpectra{}; + std::array, 2>, 4> mcRecoProxy{}; + std::shared_ptr mcRecoPKTrue, mcPKResponse, mcProxyResponse; + + // Fixed bounded diagnostics: the four analysis pT bins plus ROOT flow bins. + // Existing native 1-MeV/0.1-GeV histograms are not changed or replaced. + static constexpr std::array DiagnosticPairPtEdges{0.6, 1.3, 1.4, 1.9, 3.6}; + static constexpr std::array ProbeMomentumEdges{0., 0.5, 0.8, 1.2, 1.8, 2.5, 3.5, 5., 10.}; + static constexpr std::array SpeciesNames{"e", "mu", "pi", "K", "p", "d", "remaining"}; + static constexpr int SpeciesCount = SpeciesNames.size(); + static constexpr int SpeciesPairs = SpeciesCount * SpeciesCount; + static constexpr int UnmatchedSpeciesTag = SpeciesPairs; + static constexpr int ChargeMismatchSpeciesTag = SpeciesPairs + 1; + static constexpr int SpeciesTagCount = SpeciesPairs + 2; + static constexpr int ProtonSpecies = 4; + static constexpr int DeuteronSpecies = 5; + static constexpr int KaonSpecies = 3; + static constexpr int SelectedPIDStates = 16; + static constexpr int ProbeRegions = 4; + static constexpr int ProbePairPtBins = DiagnosticPairPtEdges.size() - 1; + static constexpr int ProbeMomentumBins = ProbeMomentumEdges.size(); // includes p >= 10 + static constexpr int ProbeTagCount = ProbePairPtBins * ProbeMomentumBins * ProbeRegions; + static constexpr int ParentClasses = 10; + static constexpr int DiagnosticMassBins = 200; + static constexpr int ProbeMassBins = 100; + static constexpr float ProbeTPCMax = 6.f; + static constexpr float ProbeTOFMax = 8.f; + static constexpr double MultMin = 0.; + static constexpr double MultMax = 100.; + static constexpr std::array PairSigns{"Unlike", "Like"}; + static constexpr std::array ProbeRoles{"De", "Ka"}; + // Parent tags: 0=no common mother, 1=K0S, 2=Lambda1115, 3=Kstar, 4=phi, + // 5=Lambda1520, 6=other common mother, 7=multiple common mothers, + // 8=same MC particle, 9=invalid truth labels. + + std::array, 2> mcProxySpecies{}; + std::array, 2> mcProxyOtherParents{}; + std::array, 2>, 2> probeMass2{}, probeTPC{}, probeTOF{}; + std::array, 2> probePairRegions{}, mcProbeSpecies{}; + std::shared_ptr probeTrackStatus; + std::shared_ptr probeEventScope; + + struct PIDProbeCand { + float px = 0.f, py = 0.f, pz = 0.f; + double momentum = 0., mass2 = 0.; + float nsTPC = 0.f, nsTOF = 0.f; + bool nominal = false; + int charge = 0; + int64_t tid = -1; + TruthInfo truth{}; + }; + + static bool regularMultiplicity(float mult) + { + return std::isfinite(mult) && mult >= MultMin && mult < MultMax; + } + + static int speciesGroup(int pdg) + { + switch (std::abs(pdg)) { + case PdgElectron: + return 0; + case PdgMuon: + return 1; + case PdgPion: + return 2; + case PdgKaon: + return KaonSpecies; + case PdgProton: + return ProtonSpecies; + case PdgDeuteron: + return DeuteronSpecies; + default: + return SpeciesCount - 1; + } + } + + int speciesPairTag(TruthInfo const& a, TruthInfo const& b, int chargeA, int chargeB) + { + if (a.id < 0 || b.id < 0) { + return UnmatchedSpeciesTag; + } + if (chargeFromPdg(a.pdg) != chargeA || chargeFromPdg(b.pdg) != chargeB) { + return ChargeMismatchSpeciesTag; + } + return SpeciesCount * speciesGroup(a.pdg) + speciesGroup(b.pdg); + } + + static bool otherSpeciesTag(int tag) + { + return tag != UnmatchedSpeciesTag && tag != SpeciesCount * DeuteronSpecies + KaonSpecies && tag != SpeciesCount * ProtonSpecies + KaonSpecies; + } + + static int retainedParentClass(TruthInfo const& a, TruthInfo const& b) + { + if (a.id < 0 || b.id < 0) { + return ParentClasses - 1; + } + if (a.id == b.id) { + return ParentClasses - 2; + } + int64_t commonId = -1; + int commonPdg = 0; + for (auto const& ma : a.diagnosticMothers) { + for (auto const& mb : b.diagnosticMothers) { + if (ma.id != mb.id) { + continue; + } + if (commonId >= 0 && commonId != ma.id) { + return ParentClasses - 3; + } + commonId = ma.id; + commonPdg = std::abs(ma.pdg); + } + } + if (commonId < 0) { + return 0; // absence of a retained link does not establish absence of ancestry + } + if (commonPdg == static_cast(PDG_t::kK0Short)) { + return 1; + } + if (commonPdg == static_cast(PDG_t::kLambda0)) { + return 2; + } + if (commonPdg == static_cast(o2::constants::physics::Pdg::kK0Star892) || commonPdg == static_cast(o2::constants::physics::Pdg::kKPlusStar892)) { + return 3; + } + if (commonPdg == static_cast(o2::constants::physics::Pdg::kPhi)) { + return 4; + } + if (commonPdg == PdgLambdaStar) { + return 5; + } + return 6; + } + + static int probePairPtBin(float pt) + { + if (!std::isfinite(pt) || pt < DiagnosticPairPtEdges.front() || pt >= DiagnosticPairPtEdges.back()) { + return -1; + } + return static_cast(std::upper_bound(DiagnosticPairPtEdges.begin(), DiagnosticPairPtEdges.end(), pt) - DiagnosticPairPtEdges.begin()) - 1; + } + + static int probeMomentumBin(double momentum) + { + return std::clamp(static_cast(std::upper_bound(ProbeMomentumEdges.begin(), ProbeMomentumEdges.end(), momentum) - ProbeMomentumEdges.begin()) - 1, 0, ProbeMomentumBins - 1); + } + + static int probeTag(int pairBin, double momentum, int region) + { + return pairBin + ProbePairPtBins * (probeMomentumBin(momentum) + ProbeMomentumBins * region); + } + + static std::string speciesTagName(int tag) + { + if (tag == UnmatchedSpeciesTag) { + return "unmatched"; + } + if (tag == ChargeMismatchSpeciesTag) { + return "charge-inconsistent"; + } + return std::string(SpeciesNames[tag / SpeciesCount]) + ":" + SpeciesNames[tag % SpeciesCount]; + } + + void bookProxyDiagnostics() + { + if (lstarEnableMCProxySpecies.value != 0 && !doprocessMCReco) { + LOGF(fatal, "lstarEnableMCProxySpecies requires processMCReco"); + } + if (lstarEnableProxyPIDProbes.value != 0) { + if (!doprocess && !doprocessMCReco) { + LOGF(fatal, "PID probes require the data or reconstructed-MC process"); + } + for (auto const& cut : {lstarProxyProbeTPCDeMax.value, lstarProxyProbeTPCKaMax.value}) { + if (!std::isfinite(cut) || cut <= 0.f || cut > ProbeTPCMax) { + LOGF(fatal, "Probe TPC limits must lie in (0,6]"); + } + } + for (auto const& cut : {lstarProxyProbeTOFDeMax.value, lstarProxyProbeTOFKaMax.value}) { + if (!std::isfinite(cut) || cut <= 0.f || cut > ProbeTOFMax) { + LOGF(fatal, "Probe TOF limits must lie in (0,8]"); + } + } + } + const AxisSpec pairPt{std::vector(DiagnosticPairPtEdges.begin(), DiagnosticPairPtEdges.end()), "Proxy pair pT (GeV/c)"}; + const AxisSpec mcMass{DiagnosticMassBins, 1.4, 1.8, "M_(d/2)K (GeV/c2)"}; + const AxisSpec probeMass{ProbeMassBins, 1.4, 1.8, "M_(d/2)K (GeV/c2)"}; + uint64_t payload = 0; + // Keep these numerically filled TH3 axes unlabelled. + // For SpeciesNames see comment above, the parent-tag comments and packed-axis formulas define the decoder. + auto add3 = [&](std::string const& name, std::string const& title, std::vector const& axes) { + auto h = histos.add(name.c_str(), title.c_str(), HistType::kTH3D, axes, true); + payload += static_cast(h->GetNcells()) * 2 * sizeof(double); + return h; + }; + for (size_t sign = 0; sign < PairSigns.size(); ++sign) { + const std::string suffix = PairSigns[sign]; + if (lstarEnableMCProxySpecies.value != 0) { + mcProxySpecies[sign] = add3("hMCProxySpecies" + suffix, "Selected proxy;species+51*(dTOF+2*kTOF+4*pTPC+8*regularMult)", + {mcMass, pairPt, AxisSpec{SpeciesTagCount * SelectedPIDStates, -0.5, SpeciesTagCount * SelectedPIDStates - 0.5, "Species and detector state"}}); + mcProxyOtherParents[sign] = add3("hMCProxyOtherParents" + suffix, "Selected Other: retained immediate common-mother class only", + {mcMass, pairPt, AxisSpec{ParentClasses, -0.5, ParentClasses - 0.5, "Retained parent class"}}); + } + if (lstarEnableProxyPIDProbes.value == 0) { + continue; + } + probePairRegions[sign] = add3("hProxyPIDProbeRegions" + suffix, "Loose matched PID probes;regular multiplicity only;region=dNominal+2*kNominal", + {probeMass, pairPt, AxisSpec{ProbeRegions, -0.5, ProbeRegions - 0.5, "Nominal-selection region"}}); + for (size_t role = 0; role < ProbeRoles.size(); ++role) { + const std::string stem = std::string("hProxyPIDProbe") + ProbeRoles[role]; + const AxisSpec m2 = role == 0 ? AxisSpec{120, -1., 7., "Full-track TOF mass2 (GeV2/c4)"} : AxisSpec{80, -1., 2., "Full-track TOF mass2 (GeV2/c4)"}; + const AxisSpec packed{ProbeTagCount, -0.5, ProbeTagCount - 0.5, "pairBin+4*(momentumBin+9*region)"}; + probeMass2[role][sign] = add3(std::string(stem).append("Mass2").append(suffix), "Loose both-TOF proxy probes;TPC/TOF envelopes;regular multiplicity", {probeMass, m2, packed}); + probeTPC[role][sign] = add3(std::string(stem).append("TPC").append(suffix), "Same loose both-TOF proxy probes;TPC nSigma;regular multiplicity", {probeMass, AxisSpec{60, -ProbeTPCMax, ProbeTPCMax, "TPC nSigma for slot hypothesis"}, packed}); + probeTOF[role][sign] = add3(std::string(stem).append("TOF").append(suffix), "Same loose both-TOF proxy probes;TOF nSigma;regular multiplicity", {probeMass, AxisSpec{80, -ProbeTOFMax, ProbeTOFMax, "TOF nSigma for slot hypothesis"}, packed}); + } + if (doprocessMCReco) { + mcProbeSpecies[sign] = add3("hMCProxyPIDProbeSpecies" + suffix, "Same loose probe pairs;species+51*nominalRegion;regular multiplicity", + {probeMass, pairPt, AxisSpec{SpeciesTagCount * ProbeRegions, -0.5, SpeciesTagCount * ProbeRegions - 0.5, "Species plus nominal region"}}); + } + } + if (lstarEnableProxyPIDProbes.value != 0) { + probeTrackStatus = histos.add("hProxyPIDProbeTrackStatus", "After quality and loose TPC;slot;stage", HistType::kTH2D, + {AxisSpec{2, -0.5, 1.5, "Slot: d=0,K=1"}, AxisSpec{5, -0.5, 4.5, "Stage"}}, true); + const std::array labels{"loose TPC", "no TOF", "invalid TOF response", "outside loose TOF", "accepted probe track"}; + for (size_t i = 0; i < labels.size(); ++i) { + probeTrackStatus->GetYaxis()->SetBinLabel(i + 1, labels[i]); + } + probeEventScope = histos.add("hProxyPIDProbeEventScope", "Selected events;regular multiplicity flag;events", HistType::kTH1D, {AxisSpec{2, -0.5, 1.5, "0=outside,1=regular [0,100)"}}, true); + } + if (payload > 0) { + LOGF(info, "Proxy diagnostic TH3 content+Sumw2 payload: %.2f MiB per wagon (fixed bins; object/axis overhead extra)", static_cast(payload) / (1024. * 1024.)); + } + } + + void fillMCProxySpecies(ProxyCand const& d, KaonCand const& k, double mass, float pt, float mult) + { + const int tag = speciesPairTag(d.truth, k.truth, d.charge, k.charge); + const int state = static_cast(d.hasTOF) + 2 * static_cast(k.hasTOF) + 4 * static_cast(d.protonTPCCompatible) + 8 * static_cast(regularMultiplicity(mult)); + const size_t sign = d.charge * k.charge < 0 ? 0 : 1; + mcProxySpecies[sign]->Fill(mass, pt, tag + SpeciesTagCount * state); + if (otherSpeciesTag(tag)) { + mcProxyOtherParents[sign]->Fill(mass, pt, retainedParentClass(d.truth, k.truth)); + } + } + + template + void fillProxyPIDProbes(TTracks const& tracks, float mult, int collisionId) + { + // This separate path must not feed nominal candidate vectors or mixing pools. + const bool regular = regularMultiplicity(mult); + probeEventScope->Fill(regular); + if (!regular) { + return; + } + std::array, 2> candidates; + for (auto const& track : tracks) { + if (track.pt() < lstarCutPtMin.value || std::abs(track.eta()) > lstarCutEtaMax.value || !passTrackQuality(track) || track.sign() == 0) { + continue; + } + const std::array tpc{track.tpcNSigmaDe(), track.tpcNSigmaKa()}; + const std::array tof{track.tofNSigmaDe(), track.tofNSigmaKa()}; + const std::array tpcMax{lstarProxyProbeTPCDeMax.value, lstarProxyProbeTPCKaMax.value}; + const std::array tofMax{lstarProxyProbeTOFDeMax.value, lstarProxyProbeTOFKaMax.value}; + for (size_t role = 0; role < ProbeRoles.size(); ++role) { + if (!std::isfinite(tpc[role]) || std::abs(tpc[role]) >= tpcMax[role]) { + continue; + } + probeTrackStatus->Fill(role, 0); + if (!track.hasTOF()) { + probeTrackStatus->Fill(role, 1); + continue; + } + const double beta = track.beta(); + const double momentum = static_cast(track.pt()) * std::cosh(static_cast(track.eta())); + if (!std::isfinite(beta) || beta <= TofBetaMin || beta >= TofBetaMax || !std::isfinite(momentum) || momentum <= 0. || !std::isfinite(tof[role])) { + probeTrackStatus->Fill(role, 2); + continue; + } + if (std::abs(tof[role]) >= tofMax[role]) { + probeTrackStatus->Fill(role, 3); + continue; + } + probeTrackStatus->Fill(role, 4); + PIDProbeCand probe; + probe.px = track.pt() * std::cos(track.phi()); + probe.py = track.pt() * std::sin(track.phi()); + probe.pz = track.pt() * std::sinh(track.eta()); + probe.momentum = momentum; + probe.mass2 = momentum * momentum * (1. / (beta * beta) - 1.); + probe.nsTPC = tpc[role]; + probe.nsTOF = tof[role]; + probe.charge = static_cast(track.sign()); + probe.tid = track.globalIndex(); + if (role == 0) { + const bool passProton = passFinalCandidatePID(track.pt(), track.tpcNSigmaPr(), track.tofNSigmaPr(), true, + lstarCutNsigmaTPCPr.value, lstarCutNsigmaTOFPr.value, lstarPidCircularCutPr.value, lstarPidPtRefPr.value); + probe.nominal = passFinalCandidatePID(track.pt(), tpc[role], tof[role], true, lstarCutNsigmaTPCDe.value, lstarCutNsigmaTOFDe.value, lstarPidCircularCutDe.value, lstarPidPtRefDe.value, true) && + passOptionalDeuteronTOFExtras(track) && (lstarProxyUseProtonPIDAsDeuteron.value == 0 || passProton); + } else { + probe.nominal = passFinalCandidatePID(track.pt(), tpc[role], tof[role], true, lstarCutNsigmaTPCKaon.value, lstarCutNsigmaTOFKaon.value, lstarPidCircularCutKaon.value, lstarPidPtRefKaon.value); + } + if constexpr (IsMC) { + probe.truth = trackTruth(track, collisionId); + } + candidates[role].push_back(std::move(probe)); + } + } + for (auto const& d : candidates[0]) { + for (auto const& k : candidates[1]) { + if (d.tid == k.tid) { + continue; + } + const double mass = invariantMass(ProxyMomentumScale * d.px, ProxyMomentumScale * d.py, ProxyMomentumScale * d.pz, o2::constants::physics::MassProton, + k.px, k.py, k.pz, o2::constants::physics::MassKaonCharged); + const float px = ProxyMomentumScale * d.px + k.px, py = ProxyMomentumScale * d.py + k.py, pz = ProxyMomentumScale * d.pz + k.pz; + const float pt = ptFromPxPy(px, py); + const int pairBin = probePairPtBin(pt); + const float y = rapidityFromMomentumAndMass(px, py, pz, mass); + if (pairBin < 0 || !std::isfinite(mass) || !std::isfinite(y) || std::abs(y) > lstarLambdaAbsYMax.value) { + continue; + } + const size_t sign = d.charge * k.charge < 0 ? 0 : 1; + const int region = static_cast(d.nominal) + 2 * static_cast(k.nominal); + probePairRegions[sign]->Fill(mass, pt, region); + probeMass2[0][sign]->Fill(mass, d.mass2, probeTag(pairBin, d.momentum, region)); + probeMass2[1][sign]->Fill(mass, k.mass2, probeTag(pairBin, k.momentum, region)); + probeTPC[0][sign]->Fill(mass, d.nsTPC, probeTag(pairBin, d.momentum, region)); + probeTPC[1][sign]->Fill(mass, k.nsTPC, probeTag(pairBin, k.momentum, region)); + probeTOF[0][sign]->Fill(mass, d.nsTOF, probeTag(pairBin, d.momentum, region)); + probeTOF[1][sign]->Fill(mass, k.nsTOF, probeTag(pairBin, k.momentum, region)); + if constexpr (IsMC) { + mcProbeSpecies[sign]->Fill(mass, pt, speciesPairTag(d.truth, k.truth, d.charge, k.charge) + SpeciesTagCount * region); + } + } + } + } + + bool mixingEnabled() const + { + return lstarEnableSparse.value != 0 && lstarNoMixedEvents.value > 0; + } + + void bookMC() + { + const std::vector massPt{ + AxisSpec{400, 1.4, 1.8, "Invariant mass (GeV/c^{2})"}, + AxisSpec{100, 0., 10., "Pair p_{T} (GeV/c)"}}; + const std::vector response{ + AxisSpec{100, 0., 10., "Generated pair p_{T} (GeV/c)"}, + AxisSpec{100, 0., 10., "Reconstructed pair p_{T} (GeV/c)"}}; + if (doprocessMCGen) { + histos.add("hMCGenEventSelection", "Generated events;Stage;Events", HistType::kTH1D, + {AxisSpec{3, 0.5, 3.5, "1=all, 2=vertex, 3=vertex+truth INEL>0"}}); + const std::array names{ + "hMCGenPKUnlike", "hMCGenPKLike", "hMCGenProxyUnlike", "hMCGenProxyLike", "hMCGenPKTrueLambdaStar"}; + for (size_t i = 0; i < names.size(); ++i) { + mcGenSpectra[i] = histos.add(names[i], "Generated fiducial pairs", HistType::kTH2D, massPt, true); + } + } + if (doprocessMCReco) { + const std::array categories{"TrueD", "ProtonMisID", "Other", "Unmatched"}; + for (size_t i = 0; i < categories.size(); ++i) { + for (size_t sign = 0; sign < mcRecoProxy[i].size(); ++sign) { + const std::string name = std::string("hMCRecoProxy") + categories[i] + (sign == 0 ? "Unlike" : "Like"); + mcRecoProxy[i][sign] = histos.add(name.c_str(), "Reconstructed proxy truth component", HistType::kTH2D, massPt, true); + } + } + mcRecoPKTrue = histos.add("hMCRecoPKTrueLambdaStar", "Selected true common-parent pK", HistType::kTH2D, massPt, true); + mcPKResponse = histos.add("hMCRecoPKPtResponse", "Selected true common-parent pK;Generated pair pT;Reconstructed pair pT", HistType::kTH2D, response, true); + mcProxyResponse = histos.add("hMCRecoProxyPtResponse", "Selected true primary dK pairs;Generated proxy pair pT;Reconstructed proxy pair pT", HistType::kTH2D, response, true); + } + } + + template + static TruthInfo particleTruth(TParticle const& particle, bool retainDiagnosticMothers = false) + { + TruthInfo info; + info.id = particle.globalIndex(); + info.collisionId = particle.mcCollisionId(); + info.pdg = particle.pdgCode(); + info.px = particle.px(); + info.py = particle.py(); + info.pz = particle.pz(); + info.primary = particle.isPhysicalPrimary(); + for (auto const& mother : particle.template mothers_as()) { + if (retainDiagnosticMothers && mother.mcCollisionId() == info.collisionId) { + info.diagnosticMothers.push_back({mother.globalIndex(), mother.pdgCode()}); + } + if (std::abs(mother.pdgCode()) == PdgLambdaStar && mother.mcCollisionId() == info.collisionId) { + info.lambdaMothers.push_back(mother.globalIndex()); + } + } + return info; + } + + template + static TruthInfo trackTruth(TTrack const& track, int collisionId, bool retainDiagnosticMothers = false) + { + if (collisionId < 0 || !track.has_mcParticle()) { + return {}; + } + auto particle = track.template mcParticle_as(); + if (particle.mcCollisionId() != collisionId) { + return {}; // pileup or wrong-collision labels do not enter truth components + } + return particleTruth(particle, retainDiagnosticMothers); + } + + static bool commonLambdaStar(TruthInfo const& proton, TruthInfo const& kaon) + { + if (std::abs(proton.pdg) != PdgProton || std::abs(kaon.pdg) != PdgKaon || + proton.pdg * kaon.pdg >= 0 || proton.id == kaon.id || + proton.collisionId != kaon.collisionId) { + return false; + } + for (auto const& id : proton.lambdaMothers) { + if (std::find(kaon.lambdaMothers.begin(), kaon.lambdaMothers.end(), id) != kaon.lambdaMothers.end()) { + return true; + } + } + return false; + } + + template + void fillRecoMC(TCandidate const& candidate, KaonCand const& kaon, double mass, float pt, bool proxy) + { + const auto& a = candidate.truth; + const auto& b = kaon.truth; + if (!proxy) { + if (commonLambdaStar(a, b) && chargeFromPdg(a.pdg) == candidate.charge && chargeFromPdg(b.pdg) == kaon.charge) { + mcRecoPKTrue->Fill(mass, pt); + mcPKResponse->Fill(ptFromPxPy(a.px + b.px, a.py + b.py), pt); + } + return; + } + size_t category = 2; // all other labelled species, secondaries included + if (a.id < 0 || b.id < 0) { + category = 3; + } else if (std::abs(b.pdg) == PdgKaon && chargeFromPdg(b.pdg) == kaon.charge && chargeFromPdg(a.pdg) == candidate.charge) { + if (std::abs(a.pdg) == PdgDeuteron) { + category = 0; + } else if (std::abs(a.pdg) == PdgProton) { + category = 1; + } + } + const size_t sign = candidate.charge * kaon.charge < 0 ? 0 : 1; + mcRecoProxy[category][sign]->Fill(mass, pt); + if (category == 0 && a.primary && b.primary && a.id != b.id) { + mcProxyResponse->Fill(ptFromPxPy(ProxyMomentumScale * a.px + b.px, ProxyMomentumScale * a.py + b.py), pt); + } + // TrueD means true d + true K, not a truth tag of resonance-fed coalescence. + } + + template + void fillGenerated(TCollision const& collision, TParticles const& particles) + { + histos.fill(HIST("hMCGenEventSelection"), 1.); + if (!(std::abs(collision.posZ()) < lstarCutVertex.value)) { + return; + } + histos.fill(HIST("hMCGenEventSelection"), 2.); + // Truth INEL>0 uses charged physical primaries in |eta|<1, with no pT cut. + // It is independent of detector trigger/vertex reconstruction efficiency. + bool inel = false; + for (auto const& particle : particles) { + if (particle.isPhysicalPrimary() && std::abs(particle.eta()) < 1.f && chargeFromPdg(particle.pdgCode()) != 0) { + inel = true; + break; + } + } + if (lstarRequireINELgt0.value != 0 && !inel) { + return; + } + histos.fill(HIST("hMCGenEventSelection"), 3.); + std::vector protons, kaons, deuterons; + for (auto const& particle : particles) { + // Use the DATA daughter kinematic acceptance for generated pair spectra. + // lstarCutMC* remains specific to the optional legacy track-QA process. + if (!(particle.pt() > lstarCutPtMin.value) || !(std::abs(particle.eta()) < lstarCutEtaMax.value) || + (lstarMCPrimaryOnly.value != 0 && !particle.isPhysicalPrimary())) { + continue; + } + switch (std::abs(particle.pdgCode())) { + case PdgProton: + protons.push_back(particleTruth(particle)); + break; + case PdgKaon: + kaons.push_back(particleTruth(particle)); + break; + case PdgDeuteron: + deuterons.push_back(particleTruth(particle)); + break; + default: + break; + } + } + auto fillPairs = [&](auto const& candidates, bool proxy) { + const float scale = proxy ? ProxyMomentumScale : 1.f; + for (auto const& a : candidates) { + for (auto const& b : kaons) { + if (a.id == b.id) { + continue; + } + const double mass = invariantMass(scale * a.px, scale * a.py, scale * a.pz, o2::constants::physics::MassProton, + b.px, b.py, b.pz, o2::constants::physics::MassKaonCharged); + const float px = scale * a.px + b.px, py = scale * a.py + b.py, pz = scale * a.pz + b.pz; + if (std::abs(rapidityFromMomentumAndMass(px, py, pz, mass)) > lstarLambdaAbsYMax.value) { + continue; + } + const float pt = ptFromPxPy(px, py); + const size_t sign = chargeFromPdg(a.pdg) * chargeFromPdg(b.pdg) < 0 ? 0 : 1; + mcGenSpectra[(proxy ? 2 : 0) + sign]->Fill(mass, pt); + if (!proxy && commonLambdaStar(a, b)) { + mcGenSpectra[4]->Fill(mass, pt); + } + } + } + }; + fillPairs(protons, false); + fillPairs(deuterons, true); } // AO2D-MC QA: truth primaries + reco-to-MC matching sanity plots @@ -1074,7 +1739,7 @@ struct Lambdastarproxy { // the track has a valid TOF match. Tracks without TOF are kept with // the TPC-only decision, avoiding an additional TOF-matching efficiency loss. if (isDeuteron) { - if (std::abs(nsTPC) >= tpcCut) { + if (!std::isfinite(nsTPC) || std::abs(nsTPC) >= tpcCut) { return false; } if (lstarEnableTOFNsigmaCutDe.value != 0 && hasTof) { @@ -1187,7 +1852,8 @@ struct Lambdastarproxy { std::array{m1, m2}); } - void process(CollisionsWithEvSel::iterator const& collision, FilteredTracks const& tracks) + template + void processReco(TCollision const& collision, TTracks const& tracks) { histos.fill(HIST("hEventSelection"), 1.); // Use unfiltered collisions here so the first bin includes vertex rejects. @@ -1311,6 +1977,15 @@ struct Lambdastarproxy { // Compute event multiplicity (FT0M or fallback) const float eventMult = eventMultiplicityFT0MOrFallback(collision, eventMultFallback); + // Exactly once after event cuts, before PID probes or candidate requirements. + fillEventMultiplicityCounters(collision, eventMult); + if (lstarEnableProxyPIDProbes.value != 0) { + if constexpr (IsMC) { + fillProxyPIDProbes(tracks, eventMult, collision.mcCollisionId()); + } else { + fillProxyPIDProbes(tracks, eventMult, -1); + } + } // Deuteron candidates -> proton-proxy candidates for (auto const& trkD : tracks) { @@ -1467,9 +2142,11 @@ struct Lambdastarproxy { static_cast( trkD.sign()), - .tid = - static_cast( - trkD.globalIndex())}); + .tid = static_cast(trkD.globalIndex()), + .truth = {}}); + if constexpr (IsMC) { + proxyCands.back().truth = trackTruth(trkD, collision.mcCollisionId(), lstarEnableMCProxySpecies.value != 0); + } } // Proton candidates (for genuine pK #Lambda^{*} reconstruction) @@ -1529,7 +2206,16 @@ struct Lambdastarproxy { const float pyP = ptP * std::sin(phiP); const float pzP = ptP * std::sinh(etaP); - protonCands.push_back(ProtonCand{.px = pxP, .py = pyP, .pz = pzP, .charge = static_cast(trkP.sign()), .tid = static_cast(trkP.globalIndex())}); + protonCands.push_back(ProtonCand{ + .px = pxP, + .py = pyP, + .pz = pzP, + .charge = static_cast(trkP.sign()), + .tid = static_cast(trkP.globalIndex()), + .truth = {}}); + if constexpr (IsMC) { + protonCands.back().truth = trackTruth(trkP, collision.mcCollisionId()); + } } // Kaon candidates @@ -1597,22 +2283,34 @@ struct Lambdastarproxy { const float pyK = ptK * std::sin(phiK); const float pzK = ptK * std::sinh(etaK); - kaonCands.push_back(KaonCand{.px = pxK, .py = pyK, .pz = pzK, .charge = static_cast(trkK.sign()), .tid = static_cast(trkK.globalIndex())}); + kaonCands.push_back(KaonCand{ + .px = pxK, + .py = pyK, + .pz = pzK, + .hasTOF = hasTofK, + .charge = static_cast(trkK.sign()), + .tid = static_cast(trkK.globalIndex()), + .truth = {}}); + if constexpr (IsMC) { + kaonCands.back().truth = trackTruth(trkK, collision.mcCollisionId(), lstarEnableMCProxySpecies.value != 0); + } } if (kaonCands.empty()) { // Still update mixing buffer so that later events can mix with this one. // The pK mixed-event background needs stored protons, while the proxy background // needs stored proxy candidates. Therefore, do not require proxy candidates here. - LStarMixEventEntry entry; - entry.mult = eventMult; - entry.zvtx = collision.posZ(); - entry.kaons = std::move(kaonCands); - entry.protons = std::move(protonCands); - entry.proxies = std::move(proxyCands); - mLStarMixEvents.push_front(std::move(entry)); - if (mLStarMixEvents.size() > static_cast(lstarNoMixedEvents.value)) { - mLStarMixEvents.pop_back(); + if (mixingEnabled()) { + LStarMixEventEntry entry; + entry.mult = eventMult; + entry.zvtx = collision.posZ(); + entry.kaons = std::move(kaonCands); + entry.protons = std::move(protonCands); + + mLStarMixEvents.push_front(std::move(entry)); + if (mLStarMixEvents.size() > static_cast(lstarNoMixedEvents.value)) { + mLStarMixEvents.pop_back(); + } } return; } @@ -1641,6 +2339,9 @@ struct Lambdastarproxy { continue; } + if constexpr (IsMC) { + fillRecoMC(pr, k, mass, ptPair, false); + } const bool unlikeSignPK = (pr.charge * k.charge) < 0; if (unlikeSignPK) { histos.fill(HIST("hInvMassPKUnlike"), mass); @@ -1713,6 +2414,12 @@ struct Lambdastarproxy { continue; } + if constexpr (IsMC) { + fillRecoMC(pr, k, mass, ptPair, true); + if (lstarEnableMCProxySpecies.value != 0) { + fillMCProxySpecies(pr, k, mass, ptPair, eventMult); + } + } const bool unlikeSignProxy = (pr.charge * k.charge) < 0; @@ -1723,10 +2430,14 @@ struct Lambdastarproxy { const bool passFullY = std::abs(yFullPK) <= lstarLambdaAbsYMax.value; if (unlikeSignProxy) { histos.fill(HIST("hLambdaStarProxyControlUnlikeSparse"), mass, ptPair, eventMult, pr.hasTOF, pr.protonLike, pr.protonTPCCompatible); - histos.fill(HIST("hLambdaStarProxyControlFullPKUnlikeSparse"), mass, ptPair, eventMult, pr.hasTOF, pr.protonLike, pr.protonTPCCompatible, massFullPK, passFullY); + if (lstarEnableFullPKMaps.value != 0) { + histos.fill(HIST("hLambdaStarProxyControlFullPKUnlikeSparse"), mass, ptPair, eventMult, pr.hasTOF, pr.protonLike, pr.protonTPCCompatible, massFullPK, passFullY); + } } else { histos.fill(HIST("hLambdaStarProxyControlLikeSparse"), mass, ptPair, eventMult, pr.hasTOF, pr.protonLike, pr.protonTPCCompatible); - histos.fill(HIST("hLambdaStarProxyControlFullPKLikeSparse"), mass, ptPair, eventMult, pr.hasTOF, pr.protonLike, pr.protonTPCCompatible, massFullPK, passFullY); + if (lstarEnableFullPKMaps.value != 0) { + histos.fill(HIST("hLambdaStarProxyControlFullPKLikeSparse"), mass, ptPair, eventMult, pr.hasTOF, pr.protonLike, pr.protonTPCCompatible, massFullPK, passFullY); + } } } @@ -1753,42 +2464,50 @@ struct Lambdastarproxy { if (unlikeSignProxy) { - histos.fill( - HIST( - "hLambdaStarProxyVsFullPKUnlikeSparse"), - mass, - massFullPK, - ptPair, - eventMult); + if (lstarEnableFullPKMaps.value != 0) { + histos.fill( + HIST( + "hLambdaStarProxyVsFullPKUnlikeSparse"), + mass, + massFullPK, + ptPair, + eventMult); + } } else { - histos.fill( - HIST( - "hLambdaStarProxyVsFullPKLikeSparse"), - mass, - massFullPK, - ptPair, - eventMult); + if (lstarEnableFullPKMaps.value != 0) { + histos.fill( + HIST( + "hLambdaStarProxyVsFullPKLikeSparse"), + mass, + massFullPK, + ptPair, + eventMult); + } } } if (pr.protonLike) { if (unlikeSignProxy) { - histos.fill( - HIST("hLambdaStarProxyProtonLikeUnlikeSparse"), - mass, - ptPair, - eventMult); + if (lstarEnableProtonLikeSparse.value != 0) { + histos.fill( + HIST("hLambdaStarProxyProtonLikeUnlikeSparse"), + mass, + ptPair, + eventMult); + } } else { - histos.fill( - HIST("hLambdaStarProxyProtonLikeLikeSparse"), - mass, - ptPair, - eventMult); + if (lstarEnableProtonLikeSparse.value != 0) { + histos.fill( + HIST("hLambdaStarProxyProtonLikeLikeSparse"), + mass, + ptPair, + eventMult); + } } } } @@ -1796,100 +2515,9 @@ struct Lambdastarproxy { } } - // --- MIXED-EVENT: current kaons + previous-event real protons --- - // This fills the standard pK mixed-event background. - for (auto const& prev : mLStarMixEvents) { - - if (std::abs(prev.zvtx - collision.posZ()) > - lstarMixZvtxMax.value) { - continue; - } - - if (std::abs(prev.mult - eventMult) > - lstarMixMultMax.value) { - continue; - } - - if (prev.protons.empty()) { - continue; - } - - for (auto const& pr : prev.protons) { - - for (auto const& k : kaonCands) { - - // Unlike-sign pK mixed-event background. - if ((pr.charge * k.charge) >= 0) { - continue; - } - - const double mass = - invariantMass( - pr.px, - pr.py, - pr.pz, - MassProton, - k.px, - k.py, - k.pz, - MassKaonCharged); - - const float pxTot = - pr.px + k.px; - - const float pyTot = - pr.py + k.py; - - const float pzTot = - pr.pz + k.pz; - - const float ptPair = - ptFromPxPy( - pxTot, - pyTot); - - const float yPair = - rapidityFromMomentumAndMass( - pxTot, - pyTot, - pzTot, - mass); - - if (std::abs(yPair) > - lstarLambdaAbsYMax.value) { - continue; - } - - if (lstarEnableSparse.value != 0) { - - histos.fill( - HIST( - "hLambdaStarPKMixedSparse"), - mass, - ptPair, - eventMult); - } - } - } - } - - // --- MIXED-EVENT: current proxies + previous-event kaons --- - // This fills the deuteron-proxy mixed-event background. - // - // For every d-selected proxy candidate we calculate: - // - // 1) M_{(d/2)K} - // using p_proxy = p_track / 2 - // - // 2) M_{pK}^{full} - // using the FULL measured momentum of the same d-selected track, - // but assigning the proton mass. - // - // The standard proxy mixed spectrum requires only the proxy rapidity - // acceptance. The 2D proxy-vs-full-pK map additionally requires the - // full-pK hypothesis to satisfy the same rapidity acceptance. - if (hasProxyCandidates) { - + if (mixingEnabled()) { + // --- MIXED-EVENT: current kaons + previous-event real protons --- + // This fills the standard pK mixed-event background. for (auto const& prev : mLStarMixEvents) { if (std::abs(prev.zvtx - collision.posZ()) > @@ -1902,23 +2530,19 @@ struct Lambdastarproxy { continue; } - if (prev.kaons.empty()) { + if (prev.protons.empty()) { continue; } - for (auto const& pr : proxyCands) { + for (auto const& pr : prev.protons) { - for (auto const& k : prev.kaons) { + for (auto const& k : kaonCands) { - // Unlike-sign proxy-K mixed-event background. + // Unlike-sign pK mixed-event background. if ((pr.charge * k.charge) >= 0) { continue; } - // ---------------------------------------------------------- - // Proxy hypothesis: p_proxy = p_d / 2 - // ---------------------------------------------------------- - const double mass = invariantMass( pr.px, @@ -1944,107 +2568,236 @@ struct Lambdastarproxy { pxTot, pyTot); - const float yProxy = + const float yPair = rapidityFromMomentumAndMass( pxTot, pyTot, pzTot, mass); - // Standard proxy rapidity acceptance. - if (std::abs(yProxy) > + if (std::abs(yPair) > lstarLambdaAbsYMax.value) { continue; } - // ---------------------------------------------------------- - // Full-momentum proton hypothesis for the SAME d-selected - // track. - // ---------------------------------------------------------- + if (lstarEnableSparse.value != 0) { - const double massFullPK = - invariantMass( - pr.pxFull, - pr.pyFull, - pr.pzFull, - MassProton, - k.px, - k.py, - k.pz, - MassKaonCharged); + if (lstarNoMixedEvents.value > 0) { + histos.fill( + HIST( + "hLambdaStarPKMixedSparse"), + mass, + ptPair, + eventMult); + } + } + } + } + } - const float pxTotFullPK = - pr.pxFull + k.px; + // --- MIXED-EVENT: current proxies + previous-event kaons --- + // This fills the deuteron-proxy mixed-event background. + // + // For every d-selected proxy candidate we calculate: + // + // 1) M_{(d/2)K} + // using p_proxy = p_track / 2 + // + // 2) M_{pK}^{full} + // using the FULL measured momentum of the same d-selected track, + // but assigning the proton mass. + // + // The standard proxy mixed spectrum requires only the proxy rapidity + // acceptance. The 2D proxy-vs-full-pK map additionally requires the + // full-pK hypothesis to satisfy the same rapidity acceptance. + if (hasProxyCandidates) { + + for (auto const& prev : mLStarMixEvents) { + + if (std::abs(prev.zvtx - collision.posZ()) > + lstarMixZvtxMax.value) { + continue; + } - const float pyTotFullPK = - pr.pyFull + k.py; + if (std::abs(prev.mult - eventMult) > + lstarMixMultMax.value) { + continue; + } - const float pzTotFullPK = - pr.pzFull + k.pz; + if (prev.kaons.empty()) { + continue; + } - const float yFullPK = - rapidityFromMomentumAndMass( - pxTotFullPK, - pyTotFullPK, - pzTotFullPK, - massFullPK); + for (auto const& pr : proxyCands) { - if (lstarEnableSparse.value != 0) { + for (auto const& k : prev.kaons) { - if (lstarEnableProxyControls.value != 0) { - const bool passFullY = std::abs(yFullPK) <= lstarLambdaAbsYMax.value; - histos.fill(HIST("hLambdaStarProxyControlMixedSparse"), mass, ptPair, eventMult, pr.hasTOF, pr.protonLike, pr.protonTPCCompatible); - histos.fill(HIST("hLambdaStarProxyControlFullPKMixedSparse"), mass, ptPair, eventMult, pr.hasTOF, pr.protonLike, pr.protonTPCCompatible, massFullPK, passFullY); + // Unlike-sign proxy-K mixed-event background. + if ((pr.charge * k.charge) >= 0) { + continue; } - // Standard mixed-event proxy spectrum. - // - // This requires only the proxy hypothesis to satisfy - // |y| < lstarLambdaAbsYMax. - histos.fill( - HIST( - "hLambdaStarProxyMixedSparse"), - mass, - ptPair, - eventMult); - - // Mixed-event 2D map. - // - // Since this histogram explicitly represents both - // hypotheses, require the full-pK hypothesis to satisfy - // the same configurable rapidity acceptance as well. - if (std::abs(yFullPK) <= + // ---------------------------------------------------------- + // Proxy hypothesis: p_proxy = p_d / 2 + // ---------------------------------------------------------- + + const double mass = + invariantMass( + pr.px, + pr.py, + pr.pz, + MassProton, + k.px, + k.py, + k.pz, + MassKaonCharged); + + const float pxTot = + pr.px + k.px; + + const float pyTot = + pr.py + k.py; + + const float pzTot = + pr.pz + k.pz; + + const float ptPair = + ptFromPxPy( + pxTot, + pyTot); + + const float yProxy = + rapidityFromMomentumAndMass( + pxTot, + pyTot, + pzTot, + mass); + + // Standard proxy rapidity acceptance. + if (std::abs(yProxy) > lstarLambdaAbsYMax.value) { + continue; + } - histos.fill( - HIST( - "hLambdaStarProxyVsFullPKMixedSparse"), - mass, - massFullPK, - ptPair, - eventMult); + // ---------------------------------------------------------- + // Full-momentum proton hypothesis for the SAME d-selected + // track. + // ---------------------------------------------------------- + + const double massFullPK = + invariantMass( + pr.pxFull, + pr.pyFull, + pr.pzFull, + MassProton, + k.px, + k.py, + k.pz, + MassKaonCharged); + + const float pxTotFullPK = + pr.pxFull + k.px; + + const float pyTotFullPK = + pr.pyFull + k.py; + + const float pzTotFullPK = + pr.pzFull + k.pz; + + const float yFullPK = + rapidityFromMomentumAndMass( + pxTotFullPK, + pyTotFullPK, + pzTotFullPK, + massFullPK); + + if (lstarEnableSparse.value != 0) { + + if (lstarEnableProxyControls.value != 0) { + const bool passFullY = std::abs(yFullPK) <= lstarLambdaAbsYMax.value; + histos.fill(HIST("hLambdaStarProxyControlMixedSparse"), mass, ptPair, eventMult, pr.hasTOF, pr.protonLike, pr.protonTPCCompatible); + if (lstarEnableFullPKMaps.value != 0) { + histos.fill(HIST("hLambdaStarProxyControlFullPKMixedSparse"), mass, ptPair, eventMult, pr.hasTOF, pr.protonLike, pr.protonTPCCompatible, massFullPK, passFullY); + } + } + + // Standard mixed-event proxy spectrum. + // + // This requires only the proxy hypothesis to satisfy + // |y| < lstarLambdaAbsYMax. + if (lstarNoMixedEvents.value > 0) { + histos.fill( + HIST( + "hLambdaStarProxyMixedSparse"), + mass, + ptPair, + eventMult); + } + + // Mixed-event 2D map. + // + // Since this histogram explicitly represents both + // hypotheses, require the full-pK hypothesis to satisfy + // the same configurable rapidity acceptance as well. + if (std::abs(yFullPK) <= + lstarLambdaAbsYMax.value) { + + if (lstarEnableFullPKMaps.value != 0) { + if (lstarNoMixedEvents.value > 0) { + histos.fill( + HIST( + "hLambdaStarProxyVsFullPKMixedSparse"), + mass, + massFullPK, + ptPair, + eventMult); + } + } + } } } } } } - } - // --- Update mixing buffer with current event --- - LStarMixEventEntry entry; - entry.mult = eventMult; - entry.zvtx = collision.posZ(); - entry.kaons = std::move(kaonCands); - entry.protons = std::move(protonCands); - entry.proxies = std::move(proxyCands); + // --- Update mixing buffer with current event --- + LStarMixEventEntry entry; + entry.mult = eventMult; + entry.zvtx = collision.posZ(); + entry.kaons = std::move(kaonCands); + entry.protons = std::move(protonCands); - mLStarMixEvents.push_front(std::move(entry)); - if (mLStarMixEvents.size() > static_cast(lstarNoMixedEvents.value)) { - mLStarMixEvents.pop_back(); + mLStarMixEvents.push_front(std::move(entry)); + if (mLStarMixEvents.size() > static_cast(lstarNoMixedEvents.value)) { + mLStarMixEvents.pop_back(); + } } } - PROCESS_SWITCH(Lambdastarproxy, process, "Lambda* proxy via (d/2)+K", true); + void processData(CollisionsWithEvSel::iterator const& collision, FilteredTracks const& tracks) + { + processReco(collision, tracks); + } + + // A literal process() is always invoked by O2 in addition to optional methods. + // Keep the established JSON key "process" while registering data only once. + PROCESS_SWITCH_FULL(Lambdastarproxy, processData, process, "Lambda* proxy via (d/2)+K", true); + + void processMCReco(CollisionsWithEvSelMC::iterator const& collision, + FilteredTracksMC const& tracks, aod::McParticles const&, + aod::McCollisions const&) + { + // Inclusive reconstructed spectra include unmatched tracks and collisions, + // just as in data. Truth components require a valid same-event MC label. + processReco(collision, tracks); + } + PROCESS_SWITCH(Lambdastarproxy, processMCReco, "MC: data-identical reconstruction and truth-component spectra", false); + + void processMCGen(aod::McCollision const& collision, aod::McParticles const& particles) + { + fillGenerated(collision, particles); + } + PROCESS_SWITCH(Lambdastarproxy, processMCGen, "MC: generated pK and (d/2)K spectra, without reconstructed-event requirements", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& context) diff --git a/PWGLF/Tasks/Resonances/lambdav2.cxx b/PWGLF/Tasks/Resonances/lambdav2.cxx index 59d6e11bd2c..cf1cdfb8ef3 100644 --- a/PWGLF/Tasks/Resonances/lambdav2.cxx +++ b/PWGLF/Tasks/Resonances/lambdav2.cxx @@ -209,8 +209,7 @@ struct lambdav2 { template bool SelectionPID(const T& candidate, int PID) { - if (PID == 0) // pion - { + if (PID == 0) { // pion auto combPIDPi = TMath::Sqrt(TMath::Abs(candidate.tofNSigmaPi() * candidate.tofNSigmaPi() + candidate.tpcNSigmaPi() * candidate.tpcNSigmaPi())); if (!candidate.hasTOF() && candidate.tpcInnerParam() < 0.6 && TMath::Abs(candidate.tpcNSigmaPi()) < nsigmaCutTPCPi) { return true; @@ -218,8 +217,7 @@ struct lambdav2 { if (candidate.hasTOF() && candidate.beta() > cfgCutTOFBeta && combPIDPi < nsigmaCutTOFPi) { return true; } - } else if (PID == 1) // kaon - { + } else if (PID == 1) { // kaon auto combPIDKa = TMath::Sqrt(TMath::Abs(candidate.tofNSigmaKa() * candidate.tofNSigmaKa() + candidate.tpcNSigmaKa() * candidate.tpcNSigmaKa())); if (!candidate.hasTOF() && candidate.tpcInnerParam() < 0.45 && TMath::Abs(candidate.tpcNSigmaKa()) < nsigmaCutTPCKa) { return true; @@ -227,8 +225,7 @@ struct lambdav2 { if (candidate.hasTOF() && candidate.beta() > cfgCutTOFBeta && combPIDKa < nsigmaCutTOFKa) { return true; } - } else // proton - { + } else { // proton auto combPIDPr = TMath::Sqrt(TMath::Abs(candidate.tofNSigmaPr() * candidate.tofNSigmaPr() + candidate.tpcNSigmaPr() * candidate.tpcNSigmaPr())); if (!candidate.hasTOF() && candidate.tpcInnerParam() < 0.6 && TMath::Abs(candidate.tpcNSigmaPr()) < nsigmaCutTPCPr) { return true; diff --git a/PWGLF/Tasks/Resonances/omega2012Analysis.cxx b/PWGLF/Tasks/Resonances/omega2012Analysis.cxx index 2f9db446f51..bc7b2dd433e 100644 --- a/PWGLF/Tasks/Resonances/omega2012Analysis.cxx +++ b/PWGLF/Tasks/Resonances/omega2012Analysis.cxx @@ -12,7 +12,27 @@ /// \file omega2012Analysis.cxx /// \brief Invariant Mass Reconstruction of Omega(2012) Resonance /// \author Bong-Hwi Lim - +/// +/// Two decay modes, analysed separately (never merged in any histogram or process function): +/// - mode A (XiK0s): Omega(2012)- -> Xi- K0S. processData, processMixedEvent, processMC, processMCGen, processMCGenerated. +/// Histograms: Event/, QAbefore/, QAafter/, omega2012/, MC/ (unchanged). +/// - mode B (Xi1530K): Omega(2012)- -> Xi(1530)0 K- -> Xi- pi+ K- with a charged kaon track. +/// processXi1530KMicro, processXi1530KTracks, processXi1530KMCMicro, processXi1530KMCGen, processXi1530KMixedMicro. +/// Histograms: xi1530K/ (signal charge pattern) and xi1530K_wrongSign/ (charge-pattern controls). +/// Selection, candidate enumeration and truth classification: PWGLF/Core/Omega2012AnalysisCore.h. +/// +/// Configuration keys: all mode-A keys are unchanged. The former three-body (Xi pi K0S) process functions +/// processThreeBodyWithTracks / processThreeBodyWithMicroTracks are removed (that final state cannot be an Omega(2012)-). +/// Mode-B pion/kaon track selection: the common resonance TrackCuts with the prefix "trk.": +/// cPionPtMin -> trk.cMinPtcut, cPionEtaMax -> trk.cMaxEtacut, cPionDCAxyMax -> trk.cMaxDCArToPVcut, +/// cPionDCAzMax -> trk.cMaxDCAzToPVcut (default 0.15 cm), cPionTPCNClusMin -> trk.cfgTPCcluster; +/// further trk.* keys as in PWGLF/Core/ResoAnalysisSelectionCore.h. +/// Pion PID keys cPion* are unchanged; the kaon PID keys cKaon* have the same shape. +/// New mode-B keys: cXi1530UseMassWindow, cXi1530KFillWrongSign, cByPassTOF. + +#include "PWGLF/Core/Omega2012AnalysisCore.h" +#include "PWGLF/Core/Omega2012MlFeatures.h" +#include "PWGLF/Core/ResoAnalysisSelectionCore.h" #include "PWGLF/DataModel/LFResonanceTables.h" #include @@ -25,37 +45,35 @@ #include #include #include +#include #include #include #include +#include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include #include -#include #include -#include -#include -#include +#include +#include using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::soa; using namespace o2::constants::physics; +using namespace o2::analysis::omega2012; struct Omega2012Analysis { // Constants - static constexpr float kSmallNumber = 1e-10f; // Small number to avoid division by zero - static constexpr float kMaxDCAV0ToPV = 1.0f; // Maximum DCA of V0 to PV - static constexpr int kNumExpectedDaughters = 2; // Expected number of daughters for 2-body decay - static constexpr int kPlaceholderPdgCode = 3335; + static constexpr int NumExpectedDaughters = 2; // Expected number of daughters for 2-body decay SliceCache cache; Preslice perResoCollisionCasc = aod::resodaughter::resoCollisionId; Preslice perResoCollisionV0 = aod::resodaughter::resoCollisionId; Preslice perResoCollisionTrack = aod::resodaughter::resoCollisionId; - Preslice perResoCollisionMicroTrack = aod::resodaughter::resoCollisionId; + Preslice perResoCollisionMicroTrack = aod::resodaughter::resoCollisionId; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; // Axes @@ -63,102 +81,67 @@ struct Omega2012Analysis { ConfigurableAxis binsPtQA{"binsPtQA", {VARIABLE_WIDTH, 0.0, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0}, "pT (QA)"}; ConfigurableAxis binsCent{"binsCent", {VARIABLE_WIDTH, 0., 1., 5., 10., 30., 50., 70., 100., 110.}, "Centrality"}; - // Invariant mass range for Omega(2012) → Xi + K0s + // Invariant mass range for Omega(2012) Configurable cInvMassStart{"cInvMassStart", 1.6, "Invariant mass start (GeV/c^2)"}; Configurable cInvMassEnd{"cInvMassEnd", 2.2, "Invariant mass end (GeV/c^2)"}; Configurable cInvMassBins{"cInvMassBins", 600, "Invariant mass bins"}; - // Basic pre-selections (mirroring refs) - Configurable cMinPtcut{"cMinPtcut", 0.15, "Minimum pT for candidates"}; - Configurable cMaxEtaCut{"cMaxEtaCut", 0.8, "Maximum |eta|"}; - Configurable cfgRapidityCut{"cfgRapidityCut", 0.5, "Rapidity cut"}; - Configurable cRecoINELgt0{"cRecoINELgt0", false, "Apply Reco INEL>0 event selection"}; - Configurable cKinCuts{"cKinCuts", false, "Kinematic cuts for Xi-K0s opening angle"}; - Configurable> cKinCutsPt{"cKinCutsPt", {0.0, 0.4, 0.6, 0.8, 1.0, 1.4, 1.8, 2.2, 2.6, 3.0, 4.0, 5.0, 6.0, 1e10}, "Omega(2012) pT bins for kinematic cuts"}; - Configurable> cKinLowerCutsAlpha{"cKinLowerCutsAlpha", {1.5, 1.0, 0.5, 0.3, 0.2, 0.15, 0.1, 0.08, 0.07, 0.06, 0.04, 0.02, 0.02}, "Lower cut on Xi-K0s opening angle"}; - Configurable> cKinUpperCutsAlpha{"cKinUpperCutsAlpha", {3.0, 2.0, 1.5, 1.4, 1.0, 0.8, 0.6, 0.5, 0.45, 0.35, 0.3, 0.25, 0.2}, "Upper cut on Xi-K0s opening angle"}; - // V0 selections (K0s) - Configurable cK0sMinCosPA{"cK0sMinCosPA", 0.98, "K0s minimum pointing angle cosine"}; - Configurable cK0sMaxDaughDCA{"cK0sMaxDaughDCA", 0.5, "K0s daughter DCA Maximum"}; - Configurable cK0sMassWindow{"cK0sMassWindow", 0.025, "Mass window for K0s selection (GeV/c^2)"}; - Configurable cMaxV0Etacut{"cMaxV0Etacut", 0.8, "V0 maximum eta cut"}; - - // Xi (cascade) selections from xi1530Analysisqa.cxx - Configurable cDCAxyToPVByPtCascP0{"cDCAxyToPVByPtCascP0", 999., "Cascade DCAxy p0"}; - Configurable cDCAxyToPVByPtCascExp{"cDCAxyToPVByPtCascExp", 1., "Cascade DCAxy exp"}; - Configurable cDCAxyToPVAsPtForCasc{"cDCAxyToPVAsPtForCasc", true, "Use pt-dep DCAxy cut (casc)"}; - - Configurable cDCAzToPVAsPtForCasc{"cDCAzToPVAsPtForCasc", true, "Use pt-dep DCAz cut (casc)"}; - - // V0 topology inside cascade (Λ) - Configurable cDCALambdaDaugtherscut{"cDCALambdaDaugtherscut", 0.7, "Λ daughters DCA cut"}; - Configurable cDCALambdaToPVcut{"cDCALambdaToPVcut", 0.02, "Λ DCA to PV min"}; - Configurable cDCAPionToPVcut{"cDCAPionToPVcut", 0.06, "π DCA to PV min"}; - Configurable cDCAProtonToPVcut{"cDCAProtonToPVcut", 0.07, "p DCA to PV min"}; - Configurable cV0CosPACutPtDepP0{"cV0CosPACutPtDepP0", 0.25, "V0 CosPA p0"}; - Configurable cV0CosPACutPtDepP1{"cV0CosPACutPtDepP1", 0.022, "V0 CosPA p1"}; - Configurable cMaxV0radiuscut{"cMaxV0radiuscut", 200., "V0 radius max"}; - Configurable cMinV0radiuscut{"cMinV0radiuscut", 2.5, "V0 radius min"}; - Configurable cMasswindowV0cut{"cMasswindowV0cut", 0.005, "Λ mass window for cascade V0"}; - - // Cascade topology - Configurable cDCABachlorToPVcut{"cDCABachlorToPVcut", 0.06, "Bachelor DCA to PV min"}; - Configurable cDCAXiDaugthersCutPtRangeLower{"cDCAXiDaugthersCutPtRangeLower", 1., "Xi pt low boundary"}; - Configurable cDCAXiDaugthersCutPtRangeUpper{"cDCAXiDaugthersCutPtRangeUpper", 4., "Xi pt high boundary"}; - Configurable cDCAXiDaugthersCutPtDepLower{"cDCAXiDaugthersCutPtDepLower", 0.8, "Xi daugh DCA (pt cDCAXiDaugthersCutPtDepMiddle{"cDCAXiDaugthersCutPtDepMiddle", 0.5, "Xi daugh DCA (low<=pt cDCAXiDaugthersCutPtDepUpper{"cDCAXiDaugthersCutPtDepUpper", 0.2, "Xi daugh DCA (pt>=high)"}; - Configurable cCosPACascCutPtDepP0{"cCosPACascCutPtDepP0", 0.2, "Cascade CosPA p0"}; - Configurable cCosPACascCutPtDepP1{"cCosPACascCutPtDepP1", 0.022, "Cascade CosPA p1"}; - Configurable cMaxCascradiuscut{"cMaxCascradiuscut", 200., "Cascade radius max"}; - Configurable cMinCascradiuscut{"cMinCascradiuscut", 1.1, "Cascade radius min"}; - Configurable cMasswindowCasccut{"cMasswindowCasccut", 0.008, "Xi mass window"}; - Configurable cMassXiminus{"cMassXiminus", 1.32171, "Xi mass (GeV/c^2)"}; // PDG + // Selection shared with the Omega(2012) training-table task (plain JSON keys; the mode-B track cuts carry "trk.") + o2::analysis::resonance::EventCuts eventCuts = makeOmega2012EventCuts(); + XiCuts xiCuts; + K0sCuts k0sCuts; + Xi1530KCuts xi1530KCuts; + o2::analysis::resonance::TrackCuts trackCuts = makeXi1530KTrackCuts(); + PionPidCuts pionPID; + KaonPidCuts kaonPID; + CandidateCuts candidateCuts; + Configurable cUseTruthRapidity{"cUseTruthRapidity", true, "Require the matched Omega(2012) truth rapidity inside cfgRapidityCut"}; + Configurable cMCInjection{"cMCInjection", false, "Restrict MC numerator and denominator to Omega parents with generator status -11"}; + + struct OmegaParentTruth { + float pt = 0.f; + float y = 0.f; + int pdgCode = 0; + bool injected = false; + }; + using OmegaParentTruthMap = std::unordered_map; + + template + static bool isInjectedOmegaParent(const Parent& parent) + { + constexpr int InjectedGeneratorStatus = -11; + return o2::mcgenstatus::getGenStatusCode(parent.statusCode()) == InjectedGeneratorStatus; + } + + template + OmegaParentTruthMap omegaParentTruth(const Parents& parents) + { + OmegaParentTruthMap parentTruth; + for (const auto& parent : parents) { + if (std::abs(parent.pdgCode()) == kOmega2012Minus) { + parentTruth.emplace(parent.originalMcParticleId(), OmegaParentTruth{parent.pt(), parent.y(), parent.pdgCode(), isInjectedOmegaParent(parent)}); + } + } + return parentTruth; + } // Event Mixing Configurable nEvtMixing{"nEvtMixing", 10, "Number of events to mix"}; ConfigurableAxis cfgVtxBins{"cfgVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; ConfigurableAxis cfgMultBins{"cfgMultBins", {VARIABLE_WIDTH, 0.0f, 1.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.0f, 110.0f}, "Mixing bins - centrality"}; - // Enhanced K0s selections - Configurable cK0sProperLifetimeMax{"cK0sProperLifetimeMax", 20.0, "K0s proper lifetime max (cm/c)"}; - Configurable cK0sArmenterosQtMin{"cK0sArmenterosQtMin", 0.0, "K0s Armenteros qt min"}; - Configurable cK0sArmenterosAlphaCoeff{"cK0sArmenterosAlphaCoeff", 0.2, "K0s Armenteros alpha coefficient"}; - Configurable cK0sDauPosDCAtoPVMin{"cK0sDauPosDCAtoPVMin", 0.05, "K0s positive daughter DCA to PV min"}; - Configurable cK0sDauNegDCAtoPVMin{"cK0sDauNegDCAtoPVMin", 0.05, "K0s negative daughter DCA to PV min"}; - Configurable cK0sRadiusMin{"cK0sRadiusMin", 0.5, "K0s decay radius min"}; - Configurable cK0sRadiusMax{"cK0sRadiusMax", 200.0, "K0s decay radius max"}; - Configurable cK0sCrossMassRejection{"cK0sCrossMassRejection", true, "Enable Lambda mass rejection for K0s"}; - Configurable cK0sCrossMassRejectionWindow{"cK0sCrossMassRejectionWindow", 0.01, "Lambda mass rejection window for K0s (GeV/c^2)"}; - Configurable cK0sDaughterPiTPCNSigmaMax{"cK0sDaughterPiTPCNSigmaMax", 5.0, "Maximum TPC NSigma for K0s daughter pions"}; - Configurable cK0sPosDaughterMinCrossedRows{"cK0sPosDaughterMinCrossedRows", 50, "Minimum TPC crossed rows for K0s positive daughter"}; - Configurable cK0sNegDaughterMinCrossedRows{"cK0sNegDaughterMinCrossedRows", 50, "Minimum TPC crossed rows for K0s negative daughter"}; - - // Pion track selections for 3-body decay - Configurable cPionPtMin{"cPionPtMin", 0.15, "Minimum pion pT"}; - Configurable cPionEtaMax{"cPionEtaMax", 0.8, "Maximum pion |eta|"}; - Configurable cPionDCAxyMax{"cPionDCAxyMax", 0.1, "Maximum pion DCAxy to PV"}; - Configurable cPionDCAzMax{"cPionDCAzMax", 0.2, "Maximum pion DCAz to PV"}; - Configurable cPionTPCNClusMin{"cPionTPCNClusMin", 70, "Minimum TPC clusters for pion"}; - - // Pion PID selections - Configurable cPionTPCNSigmaMax{"cPionTPCNSigmaMax", 3.0, "Maximum TPC NSigma for pion"}; - Configurable cPionTOFNSigmaMax{"cPionTOFNSigmaMax", 3.0, "Maximum TOF NSigma for pion"}; - Configurable cPionUsePtDepPID{"cPionUsePtDepPID", false, "Use pT-dependent PID cuts for pion"}; - Configurable> cPionPIDPtBins{"cPionPIDPtBins", {0.0f, 0.5f, 0.8f, 2.0f, 999.0f}, "pT bin edges for pion PID cuts"}; - Configurable> cPionTPCNSigmaCuts{"cPionTPCNSigmaCuts", {3.0f, 3.0f, 2.0f, 2.0f}, "TPC NSigma cuts per pT bin (pion)"}; - Configurable> cPionTOFNSigmaCuts{"cPionTOFNSigmaCuts", {3.0f, 3.0f, 3.0f, 3.0f}, "TOF NSigma cuts per pT bin (pion)"}; - Configurable> cPionTOFRequired{"cPionTOFRequired", {0, 0, 1, 1}, "Require TOF per pT bin (pion)"}; - - // Xi1530 mass window cut - Configurable cXi1530Mass{"cXi1530Mass", 1.53, "Xi(1530) mass (GeV/c^2)"}; - Configurable cXi1530MassWindow{"cXi1530MassWindow", 0.01, "Xi(1530) mass window (GeV/c^2)"}; - - // PDG masses - double massK0 = MassK0Short; + // Module-initializer collision and daughter tables (matches the resonance-module-initializer producer) + using ResoCollisions = aod::ResoCollisions_001; + using ResoMCCollisions = soa::Join; + using ResoMicroTracks = aod::ResoMicroTracks_001; + using ResoMCCascades = soa::Join; + using ResoMCV0s = soa::Join; + using ResoMCMicroTracks = soa::Join; using BinningTypeVertexContributor = ColumnBinningPolicy; - BinningTypeVertexContributor colBinning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVertexContributor colBinning{{cfgVtxBins, cfgMultBins}}; + + Omega2012AnalysisCore core; void init(InitContext&) { @@ -182,13 +165,15 @@ struct Omega2012Analysis { AxisSpec omegaKinPtAxis = {500, 0, 10, "#it{p}_{T} (GeV/#it{c})"}; // Event QA histograms + // Event counters are double precision (kTH1D): a single-precision bin stops counting at 2^24 entries per job, + // which truncated the zero-multiplicity bins of the multiplicity counters in large pp productions. histos.add("Event/posZ", "Event vertex Z position", kTH1F, {{200, -20., 20., "V_{z} (cm)"}}); - histos.add("Event/centrality", "Event centrality distribution", kTH1F, {centAxis}); + histos.add("Event/centrality", "Event centrality distribution", kTH1D, {centAxis}); histos.add("Event/posZvsCent", "Vertex Z vs Centrality", kTH2F, {{200, -20., 20., "V_{z} (cm)"}, centAxis}); - histos.add("Event/nCascades", "Number of cascades per event", kTH1F, {{100, 0., 100., "N_{cascades}"}}); - histos.add("Event/nV0s", "Number of V0s per event", kTH1F, {{200, 0., 200., "N_{V0s}"}}); - histos.add("Event/nCascadesAfterCuts", "Number of cascades per event after cuts", kTH1F, {{50, 0., 50., "N_{cascades}"}}); - histos.add("Event/nV0sAfterCuts", "Number of V0s per event after cuts", kTH1F, {{100, 0., 100., "N_{V0s}"}}); + histos.add("Event/nCascades", "Number of cascades per event", kTH1D, {{100, 0., 100., "N_{cascades}"}}); + histos.add("Event/nV0s", "Number of V0s per event", kTH1D, {{200, 0., 200., "N_{V0s}"}}); + histos.add("Event/nCascadesAfterCuts", "Number of cascades per event after cuts", kTH1D, {{50, 0., 50., "N_{cascades}"}}); + histos.add("Event/nV0sAfterCuts", "Number of V0s per event after cuts", kTH1D, {{100, 0., 100., "N_{V0s}"}}); // Xi QA histograms histos.add("QAbefore/xiMass", "Xi mass before cuts", kTH1F, {xiMassAxis}); @@ -251,32 +236,11 @@ struct Omega2012Analysis { // Resonance (2-body decay: Xi + K0s) histos.add("omega2012/invmass", "Invariant mass of Omega(2012) → Xi + K0s", kTH1F, {invMassAxis}); histos.add("omega2012/invmass_Mix", "Mixed event Invariant mass of Omega(2012) → Xi + K0s", kTH1F, {invMassAxis}); - histos.add("omega2012/massPtCent", "Omega(2012) mass vs pT vs cent", kTH3F, {invMassAxis, ptAxis, centAxis}); - histos.add("omega2012/massPtCent_Mix", "Mixed event Omega(2012) mass vs pT vs cent", kTH3F, {invMassAxis, ptAxis, centAxis}); + histos.add("omega2012/massPtCent", "Omega(2012) mass vs pT vs cent", kTHnSparseD, {invMassAxis, ptAxis, centAxis}); + histos.add("omega2012/massPtCent_Mix", "Mixed event Omega(2012) mass vs pT vs cent", kTHnSparseD, {invMassAxis, ptAxis, centAxis}); histos.add("QAbefore/omegaAlphaVsPt", "#alpha_{oa} vs p_{T} before kinematic cuts", kTH2F, {omegaKinPtAxis, openingAngleAxis}); histos.add("QAafter/omegaAlphaVsPt", "#alpha_{oa} vs p_{T} after kinematic cuts", kTH2F, {omegaKinPtAxis, openingAngleAxis}); - // 3-body decay: Xi + pi + K0s - histos.add("omega2012_3body/invmass", "Invariant mass of Omega(2012) → Xi + #pi + K^{0}_{S}", kTH1F, {invMassAxis}); - histos.add("omega2012_3body/massPtCent", "Omega(2012) 3-body mass vs pT vs cent", kTH3F, {invMassAxis, ptAxis, centAxis}); - - // Pion QA histograms for 3-body - histos.add("QAbefore/pionPt", "Pion pT before cuts", kTH1F, {ptAxisQA}); - histos.add("QAbefore/pionEta", "Pion eta before cuts", kTH1F, {{100, -2.0, 2.0, "#eta"}}); - histos.add("QAbefore/pionDCAxy", "Pion DCAxy before cuts", kTH2F, {ptAxisQA, dcaxyAxis}); - histos.add("QAbefore/pionDCAz", "Pion DCAz before cuts", kTH2F, {ptAxisQA, dcazAxis}); - histos.add("QAbefore/pionTPCNcls", "Pion TPC clusters before cuts", kTH1F, {{160, 0, 160, "N_{TPC clusters}"}}); - histos.add("QAbefore/pionTPCNSigma", "Pion TPC NSigma before cuts", kTH2F, {ptAxisQA, nsigmaAxis}); - histos.add("QAbefore/pionTOFNSigma", "Pion TOF NSigma before cuts", kTH2F, {ptAxisQA, nsigmaAxis}); - - histos.add("QAafter/pionPt", "Pion pT after cuts", kTH1F, {ptAxisQA}); - histos.add("QAafter/pionEta", "Pion eta after cuts", kTH1F, {{100, -2.0, 2.0, "#eta"}}); - histos.add("QAafter/pionDCAxy", "Pion DCAxy after cuts", kTH2F, {ptAxisQA, dcaxyAxis}); - histos.add("QAafter/pionDCAz", "Pion DCAz after cuts", kTH2F, {ptAxisQA, dcazAxis}); - histos.add("QAafter/pionTPCNcls", "Pion TPC clusters after cuts", kTH1F, {{160, 0, 160, "N_{TPC clusters}"}}); - histos.add("QAafter/pionTPCNSigma", "Pion TPC NSigma after cuts", kTH2F, {ptAxisQA, nsigmaAxis}); - histos.add("QAafter/pionTOFNSigma", "Pion TOF NSigma after cuts", kTH2F, {ptAxisQA, nsigmaAxis}); - // MC truth histograms AxisSpec etaAxis = {100, -2.0, 2.0, "#eta"}; AxisSpec rapidityAxis = {100, -2.0, 2.0, "y"}; @@ -286,6 +250,20 @@ struct Omega2012Analysis { histos.add("MC/hMCGenOmega2012Y", "MC Generated Omega(2012) rapidity", kTH1F, {rapidityAxis}); histos.add("MC/hMCRecOmega2012Pt", "MC Reconstructed Omega(2012) pT", kTH1F, {ptAxis}); histos.add("MC/hMCRecOmega2012PtEta", "MC Reconstructed Omega(2012) pT vs eta", kTH2F, {ptAxis, etaAxis}); + if (doprocessMCGen) { + histos.add("MC/hMCGenSelectedXiK0sPt", "MC generated Omega(2012) #rightarrow #Xi K^{0}_{S} in selected events and rapidity window;p_{T} (GeV/c)", kTH1F, {ptAxis}); + const AxisSpec truthPtAxis{binsPt, "p_{T}^{truth} (GeV/c)"}; + const AxisSpec signAxis{2, -1.5, 1.5, "Omega PDG sign (+1 particle, -1 antiparticle)"}; + histos.add("MC/hMCGenSelectedXiK0sResponse", "Selected-event generated #Omega #rightarrow #Xi K^{0}_{S}", kTHnSparseD, + {truthPtAxis, centAxis, {500, 0., 500., "MC multiplicity"}, signAxis}); + } + if (doprocessMC) { + const AxisSpec recoPtAxis{binsPt, "p_{T}^{reco} (GeV/c)"}; + const AxisSpec truthPtAxis{binsPt, "p_{T}^{truth} (GeV/c)"}; + const AxisSpec signAxis{2, -1.5, 1.5, "Omega PDG sign (+1 particle, -1 antiparticle)"}; + histos.add("MC/hMCRecXiK0sResponse", "Truth-matched selected #Omega #rightarrow #Xi K^{0}_{S}", kTHnSparseD, + {recoPtAxis, truthPtAxis, centAxis, {500, 0., 500., "MC multiplicity"}, signAxis}); + } // MC truth invariant mass (from MC particles) histos.add("MC/hMCTruthInvMassXiK0s", "MC Truth Inv Mass Xi + K^{0}_{S}", kTH1F, {invMassAxis}); @@ -296,590 +274,188 @@ struct Omega2012Analysis { histos.add("MC/hMCRecK0sPt", "MC Reconstructed K0s pT", kTH1F, {ptAxis}); histos.add("MC/hMCTrueXiPt", "MC True Xi pT", kTH1F, {ptAxis}); histos.add("MC/hMCTrueK0sPt", "MC True K0s pT", kTH1F, {ptAxis}); - } - - template - static std::array cascadeDaughterIds(const CascT& xi) - { - auto indices = xi.cascadeIndices(); - return {indices[0], indices[1], indices[2]}; - } - - template - static std::array v0DaughterIds(const V0Type& v0) - { - auto indices = v0.indices(); - return {indices[0], indices[1]}; - } - - template - static bool sharesAnyDaughterId(const std::array& first, const std::array& second) - { - for (const auto& firstId : first) { - for (const auto& secondId : second) { - if (firstId == secondId) { - return true; - } - } - } - return false; - } - - template - static bool sharesDaughterId(const std::array& daughters, int trackId) - { - for (const auto& daughterId : daughters) { - if (daughterId == trackId) { - return true; - } - } - return false; - } - - template - static int trackSourceId(const TrackT& track, const TrackIdsT& trackIds) - { - auto rowIndex = track.globalIndex(); - if (rowIndex >= 0 && rowIndex < trackIds.size()) { - return trackIds.rawIteratorAt(rowIndex).trackId(); - } - return rowIndex; - } - - template - bool kinCuts(const FirstVecT& firstDaughter, const SecondVecT& secondDaughter, const MotherVecT& mother, float& alpha) - { - auto firstP = std::sqrt(firstDaughter.Px() * firstDaughter.Px() + firstDaughter.Py() * firstDaughter.Py() + firstDaughter.Pz() * firstDaughter.Pz()); - auto secondP = std::sqrt(secondDaughter.Px() * secondDaughter.Px() + secondDaughter.Py() * secondDaughter.Py() + secondDaughter.Pz() * secondDaughter.Pz()); - if (firstP < kSmallNumber || secondP < kSmallNumber) { - alpha = 0.f; - return false; - } - - auto cosAlpha = (firstDaughter.Px() * secondDaughter.Px() + firstDaughter.Py() * secondDaughter.Py() + firstDaughter.Pz() * secondDaughter.Pz()) / (firstP * secondP); - if (cosAlpha > 1.) { - cosAlpha = 1.; - } else if (cosAlpha < -1.) { - cosAlpha = -1.; - } - alpha = std::acos(cosAlpha); - - std::vector kinCutsPt = static_cast>(cKinCutsPt); - std::vector kinLowerCutsAlpha = static_cast>(cKinLowerCutsAlpha); - std::vector kinUpperCutsAlpha = static_cast>(cKinUpperCutsAlpha); - - int kinCutsSize = static_cast(kinUpperCutsAlpha.size()); - if (kinCutsSize > static_cast(kinLowerCutsAlpha.size())) { - kinCutsSize = static_cast(kinLowerCutsAlpha.size()); - } - if (kinCutsSize > static_cast(kinCutsPt.size()) - 1) { - kinCutsSize = static_cast(kinCutsPt.size()) - 1; - } - - for (int i = 0; i < kinCutsSize; ++i) { - if ((mother.Pt() > kinCutsPt[i] && mother.Pt() <= kinCutsPt[i + 1]) && (alpha < kinLowerCutsAlpha[i] || alpha > kinUpperCutsAlpha[i])) { - return false; - } - } - - return true; - } - - // Enhanced V0 selection (K0s) with detailed criteria - template - bool v0CutEnhanced(const CollisionType& collision, const V0Type& v0) - { - // Basic kinematic cuts - if (std::abs(v0.eta()) > cMaxV0Etacut) - return false; - if (v0.pt() < cMinPtcut) - return false; - - // Topological cuts - if (v0.v0CosPA() < cK0sMinCosPA) - return false; - if (v0.daughDCA() > cK0sMaxDaughDCA) - return false; - - // Enhanced selections from chk892Flow - // Daughter DCA to PV cuts - if (std::abs(v0.dcapostopv()) < cK0sDauPosDCAtoPVMin) - return false; - if (std::abs(v0.dcanegtopv()) < cK0sDauNegDCAtoPVMin) - return false; - - // Radius cuts - use transRadius instead of v0radius - auto radius = v0.transRadius(); - if (radius < cK0sRadiusMin || radius > cK0sRadiusMax) - return false; - - // DCA to PV - if (std::abs(v0.dcav0topv()) > kMaxDCAV0ToPV) - return false; // max DCA to PV - - // Proper lifetime cut - calculate manually - float dx = v0.decayVtxX() - collision.posX(); - float dy = v0.decayVtxY() - collision.posY(); - float dz = v0.decayVtxZ() - collision.posZ(); - float l = std::sqrt(dx * dx + dy * dy + dz * dz); - float p = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); - auto properLifetime = (l / (p + kSmallNumber)) * MassK0Short; - if (properLifetime > cK0sProperLifetimeMax) - return false; - - if (v0.qtarm() < cK0sArmenterosQtMin) - return false; - - // Mass window - if (std::abs(v0.mK0Short() - MassK0Short) > cK0sMassWindow) - return false; - - // Competing V0 rejection: remove (Anti)Λ - if (cK0sCrossMassRejection) { - if (std::abs(v0.mLambda() - MassLambda) < cK0sCrossMassRejectionWindow) - return false; - if (std::abs(v0.mAntiLambda() - MassLambda) < cK0sCrossMassRejectionWindow) - return false; - } - - if (std::abs(v0.daughterTPCNSigmaPosPi()) >= cK0sDaughterPiTPCNSigmaMax) - return false; - if (std::abs(v0.daughterTPCNSigmaNegPi()) >= cK0sDaughterPiTPCNSigmaMax) - return false; - - if (v0.nCrossedRowsPos() <= cK0sPosDaughterMinCrossedRows) - return false; - if (v0.nCrossedRowsNeg() <= cK0sNegDaughterMinCrossedRows) - return false; - - if (v0.qtarm() < cK0sArmenterosAlphaCoeff * std::fabs(v0.alpha())) - return false; - - return true; - } - - // Helper function to find pT bin index - int getPtBinIndex(float pt) - { - auto ptBins = static_cast>(cPionPIDPtBins); - for (size_t i = 0; i < ptBins.size() - 1; i++) { - if (pt >= ptBins[i] && pt < ptBins[i + 1]) { - return i; - } - } - return -1; - } - - // Pion PID selection - template - bool pionPidCut(const TrackType& track) - { - float pt = track.pt(); - - if constexpr (IsResoMicrotrack) { - // For ResoMicroTracks - decode PID from flags - float tpcNSigma = o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(track.pidNSigmaPiFlag()); - float tofNSigma = track.hasTOF() ? o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(track.pidNSigmaPiFlag()) : 999.f; - - if (cPionUsePtDepPID) { - int ptBin = getPtBinIndex(pt); - if (ptBin < 0) - return false; - - auto tpcCuts = static_cast>(cPionTPCNSigmaCuts); - auto tofCuts = static_cast>(cPionTOFNSigmaCuts); - auto tofRequired = static_cast>(cPionTOFRequired); - - if (ptBin >= static_cast(tpcCuts.size()) || - ptBin >= static_cast(tofCuts.size()) || - ptBin >= static_cast(tofRequired.size())) { - return false; - } - - if (std::abs(tpcNSigma) >= tpcCuts[ptBin]) - return false; - - if (tofRequired[ptBin] != 0) { - if (!track.hasTOF()) - return false; - if (std::abs(tofNSigma) >= tofCuts[ptBin]) - return false; - } else { - if (track.hasTOF() && std::abs(tofNSigma) >= tofCuts[ptBin]) - return false; - } - - return true; - } else { - bool tpcPass = std::abs(tpcNSigma) < cPionTPCNSigmaMax; - bool tofPass = track.hasTOF() ? std::abs(tofNSigma) < cPionTOFNSigmaMax : true; - return tpcPass && tofPass; - } - } else { - // For ResoTracks - direct access - float tpcNSigma = track.tpcNSigmaPi(); - float tofNSigma = track.hasTOF() ? track.tofNSigmaPi() : 999.f; - - if (cPionUsePtDepPID) { - int ptBin = getPtBinIndex(pt); - if (ptBin < 0) - return false; - - auto tpcCuts = static_cast>(cPionTPCNSigmaCuts); - auto tofCuts = static_cast>(cPionTOFNSigmaCuts); - auto tofRequired = static_cast>(cPionTOFRequired); - - if (ptBin >= static_cast(tpcCuts.size()) || - ptBin >= static_cast(tofCuts.size()) || - ptBin >= static_cast(tofRequired.size())) { - return false; - } - - if (std::abs(tpcNSigma) >= tpcCuts[ptBin]) - return false; - - if (tofRequired[ptBin] != 0) { - if (!track.hasTOF()) - return false; - if (std::abs(tofNSigma) >= tofCuts[ptBin]) - return false; - } else { - if (track.hasTOF() && std::abs(tofNSigma) >= tofCuts[ptBin]) - return false; - } - - return true; - } else { - bool tpcPass = std::abs(tpcNSigma) < cPionTPCNSigmaMax; - bool tofPass = track.hasTOF() ? std::abs(tofNSigma) < cPionTOFNSigmaMax : true; - return tpcPass && tofPass; - } - } - } - - // Pion track selection (for both ResoTracks and ResoMicroTracks) - template - bool pionCut(const TrackType& track) - { - // Basic kinematic cuts - if (track.pt() < cPionPtMin) - return false; - if (std::abs(track.eta()) > cPionEtaMax) - return false; - - // DCA cuts - different access for ResoMicroTracks - if constexpr (IsResoMicrotrack) { - if (o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAxy(track.trackSelectionFlags()) > cPionDCAxyMax) - return false; - if (o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAz(track.trackSelectionFlags()) > cPionDCAzMax) - return false; - } else { - if (std::abs(track.dcaXY()) > cPionDCAxyMax) - return false; - if (std::abs(track.dcaZ()) > cPionDCAzMax) - return false; - } - - // Track quality cuts - only for ResoTracks - if constexpr (!IsResoMicrotrack) { - if constexpr (requires { track.tpcNClsFound(); }) { - if (track.tpcNClsFound() < cPionTPCNClusMin) - return false; - } - } - - // PID selection - if (!pionPidCut(track)) - return false; - - return true; - } - - // Xi1530 mass window cut - template - bool xi1530MassCut(const XiType& xi, const PionType& pion) - { - // Calculate Xi + pion invariant mass - ROOT::Math::PxPyPzEVector pXi, pPion, pXi1530; - pXi = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(xi.pt(), xi.eta(), xi.phi(), xi.mXi())); - pPion = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(pion.pt(), pion.eta(), pion.phi(), MassPionCharged)); - pXi1530 = pXi + pPion; - - // Check if mass is within Xi(1530) window - float massDiff = std::abs(pXi1530.M() - cXi1530Mass); - return massDiff < cXi1530MassWindow; - } - - // Primary-level cascade kinematics - template - bool cascprimaryTrackCut(const CascT& c) - { - if (std::abs(c.eta()) > cMaxEtaCut) - return false; - if (std::abs(c.pt()) < cMinPtcut) - return false; - if (cDCAxyToPVAsPtForCasc) { - if (std::abs(c.dcaXYCascToPV()) > (cDCAxyToPVByPtCascP0 + cDCAxyToPVByPtCascExp * c.pt())) - return false; - } - if (cDCAzToPVAsPtForCasc) { - if (std::abs(c.dcaZCascToPV()) > (cDCAxyToPVByPtCascP0 + cDCAxyToPVByPtCascExp * std::pow(c.pt(), -1.1f))) - return false; - } - return true; - } - - // Cascade topological selections adapted from xi1530Analysisqa - template - bool casctopCut(const CascT& c) - { - // V0 (Λ) topology inside cascade - if (std::abs(c.daughDCA()) > cDCALambdaDaugtherscut) - return false; - if (std::abs(c.dcav0topv()) < cDCALambdaToPVcut) - return false; - - if (c.sign() < 0) { // Xi- - if (std::abs(c.dcanegtopv()) < cDCAPionToPVcut) - return false; - if (std::abs(c.dcapostopv()) < cDCAProtonToPVcut) - return false; - } else { // Anti-Xi - if (std::abs(c.dcanegtopv()) < cDCAProtonToPVcut) - return false; - if (std::abs(c.dcapostopv()) < cDCAPionToPVcut) - return false; - } - - if (c.v0CosPA() < std::cos(cV0CosPACutPtDepP0 - cV0CosPACutPtDepP1 * c.pt())) - return false; - if (c.transRadius() > cMaxV0radiuscut || c.transRadius() < cMinV0radiuscut) - return false; - if (std::abs(c.mLambda() - MassLambda) > cMasswindowV0cut) - return false; - - // Cascade-level topology - if (std::abs(c.dcabachtopv()) < cDCABachlorToPVcut) - return false; - - if (c.pt() < cDCAXiDaugthersCutPtRangeLower) { - if (c.cascDaughDCA() > cDCAXiDaugthersCutPtDepLower) - return false; - } else if (c.pt() < cDCAXiDaugthersCutPtRangeUpper) { - if (c.cascDaughDCA() > cDCAXiDaugthersCutPtDepMiddle) - return false; - } else { - if (c.cascDaughDCA() > cDCAXiDaugthersCutPtDepUpper) - return false; - } - - if (c.cascCosPA() < std::cos(cCosPACascCutPtDepP0 - cCosPACascCutPtDepP1 * c.pt())) - return false; - if (c.cascTransRadius() > cMaxCascradiuscut || c.cascTransRadius() < cMinCascradiuscut) - return false; - if (std::abs(c.mXi() - cMassXiminus) > cMasswindowCasccut) - return false; - - return true; - } - - template - struct SelectedXiCandidate { - CandidateT candidate; - std::array daughterIds; - }; - - template - struct SelectedK0sCandidate { - CandidateT candidate; - std::array daughterIds; - }; - - template - auto selectK0sCandidates(const CollisionT& collision, const V0sT& v0s, int& nV0sAfterCuts) - { - using V0Candidate = std::decay_t; - std::vector> selectedK0s; - selectedK0s.reserve(v0s.size()); - - for (const auto& v0 : v0s) { - float dx = v0.decayVtxX() - collision.posX(); - float dy = v0.decayVtxY() - collision.posY(); - float dz = v0.decayVtxZ() - collision.posZ(); - float l = std::sqrt(dx * dx + dy * dy + dz * dz); - float p = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); - auto properLifetime = (l / (p + kSmallNumber)) * MassK0Short; - - if constexpr (FillQA) { - histos.fill(HIST("QAbefore/k0sMassPt"), v0.pt(), v0.mK0Short()); - histos.fill(HIST("QAbefore/k0sPt"), v0.pt()); - histos.fill(HIST("QAbefore/k0sEta"), v0.eta()); - histos.fill(HIST("QAbefore/k0sCosPA"), v0.pt(), v0.v0CosPA()); - histos.fill(HIST("QAbefore/k0sRadius"), v0.pt(), v0.transRadius()); - histos.fill(HIST("QAbefore/k0sDauDCA"), v0.pt(), v0.daughDCA()); - histos.fill(HIST("QAbefore/k0sDCAtoPV"), v0.pt(), std::abs(v0.dcav0topv())); - histos.fill(HIST("QAbefore/k0sProperLifetime"), v0.pt(), properLifetime); - histos.fill(HIST("QAbefore/k0sArmenteros"), v0.alpha(), v0.qtarm()); - histos.fill(HIST("QAbefore/k0sDauPosDCA"), v0.pt(), std::abs(v0.dcapostopv())); - histos.fill(HIST("QAbefore/k0sDauNegDCA"), v0.pt(), std::abs(v0.dcanegtopv())); - histos.fill(HIST("QAbefore/k0sDauTPCNsigmaPosPi"), v0.pt(), v0.daughterTPCNSigmaPosPi()); - histos.fill(HIST("QAbefore/k0sDauTPCNsigmaNegPi"), v0.pt(), v0.daughterTPCNSigmaNegPi()); - histos.fill(HIST("QAbefore/k0sNCrossedRowsPos"), v0.pt(), v0.nCrossedRowsPos()); - histos.fill(HIST("QAbefore/k0sNCrossedRowsNeg"), v0.pt(), v0.nCrossedRowsNeg()); - } - - if (!v0CutEnhanced(collision, v0)) - continue; - - if constexpr (FillQA) { - nV0sAfterCuts++; - histos.fill(HIST("QAafter/k0sMassPt"), v0.pt(), v0.mK0Short()); - histos.fill(HIST("QAafter/k0sPt"), v0.pt()); - histos.fill(HIST("QAafter/k0sEta"), v0.eta()); - histos.fill(HIST("QAafter/k0sCosPA"), v0.pt(), v0.v0CosPA()); - histos.fill(HIST("QAafter/k0sRadius"), v0.pt(), v0.transRadius()); - histos.fill(HIST("QAafter/k0sDauDCA"), v0.pt(), v0.daughDCA()); - histos.fill(HIST("QAafter/k0sDCAtoPV"), v0.pt(), std::abs(v0.dcav0topv())); - histos.fill(HIST("QAafter/k0sProperLifetime"), v0.pt(), properLifetime); - histos.fill(HIST("QAafter/k0sArmenteros"), v0.alpha(), v0.qtarm()); - histos.fill(HIST("QAafter/k0sDauPosDCA"), v0.pt(), std::abs(v0.dcapostopv())); - histos.fill(HIST("QAafter/k0sDauNegDCA"), v0.pt(), std::abs(v0.dcanegtopv())); - histos.fill(HIST("QAafter/k0sDauTPCNsigmaPosPi"), v0.pt(), v0.daughterTPCNSigmaPosPi()); - histos.fill(HIST("QAafter/k0sDauTPCNsigmaNegPi"), v0.pt(), v0.daughterTPCNSigmaNegPi()); - histos.fill(HIST("QAafter/k0sNCrossedRowsPos"), v0.pt(), v0.nCrossedRowsPos()); - histos.fill(HIST("QAafter/k0sNCrossedRowsNeg"), v0.pt(), v0.nCrossedRowsNeg()); - } - - selectedK0s.push_back({v0, v0DaughterIds(v0)}); - } - - return selectedK0s; - } - template - auto selectXiCandidates(const CascadesT& cascades, int& nCascAfterCuts) - { - using XiCandidate = std::decay_t; - std::vector> selectedXis; - selectedXis.reserve(cascades.size()); - - for (const auto& xi : cascades) { - if constexpr (FillQA) { - histos.fill(HIST("QAbefore/xiMass"), xi.mXi()); - histos.fill(HIST("QAbefore/xiPt"), xi.pt()); - histos.fill(HIST("QAbefore/xiEta"), xi.eta()); - histos.fill(HIST("QAbefore/xiDCAxy"), xi.pt(), xi.dcaXYCascToPV()); - histos.fill(HIST("QAbefore/xiDCAz"), xi.pt(), xi.dcaZCascToPV()); - histos.fill(HIST("QAbefore/xiV0CosPA"), xi.pt(), xi.v0CosPA()); - histos.fill(HIST("QAbefore/xiCascCosPA"), xi.pt(), xi.cascCosPA()); - histos.fill(HIST("QAbefore/xiV0Radius"), xi.pt(), xi.transRadius()); - histos.fill(HIST("QAbefore/xiCascRadius"), xi.pt(), xi.cascTransRadius()); - histos.fill(HIST("QAbefore/xiV0DauDCA"), xi.pt(), xi.daughDCA()); - histos.fill(HIST("QAbefore/xiCascDauDCA"), xi.pt(), xi.cascDaughDCA()); + ProcessModes modes; + modes.xiK0s = doprocessData || doprocessMixedEvent || doprocessMC; + modes.xi1530K = doprocessXi1530KMicro || doprocessXi1530KTracks || doprocessXi1530KMCMicro || doprocessXi1530KMCGen || doprocessXi1530KMixedMicro; + modes.microTracks = doprocessXi1530KMicro || doprocessXi1530KMCMicro || doprocessXi1530KMixedMicro; + modes.mcReco = doprocessMC || doprocessXi1530KMCMicro; + modes.mixing = doprocessMixedEvent || doprocessXi1530KMixedMicro; + core.init(histos, eventCuts, xiCuts, k0sCuts, xi1530KCuts, trackCuts, pionPID, kaonPID, candidateCuts, modes); + + if (modes.xi1530K) { + AxisSpec xiPiMassAxis = {300, 1.4, 1.7, "M_{#Xi#pi} (GeV/#it{c}^{2})"}; + AxisSpec patternAxis = {3, 0.5, 3.5, "charge pattern (1: wrong-sign #pi, 2: wrong-sign K, 3: both)"}; + AxisSpec etaAxisQA = {100, -2.0, 2.0, "#eta"}; + + // Signal charge pattern: Xi- pi+ K- and Xi+ pi- K+ + histos.add("xi1530K/invmass", "Invariant mass of Omega(2012) → #Xi(1530)^{0} K → #Xi #pi K", kTH1F, {invMassAxis}); + histos.add("xi1530K/massPtCent", "Omega(2012) → #Xi(1530)^{0} K mass vs pT vs cent", kTHnSparseD, {invMassAxis, ptAxis, centAxis}); + histos.add("xi1530K/invmass_Mix", "Mixed event invariant mass of Omega(2012) → #Xi(1530)^{0} K", kTH1F, {invMassAxis}); + histos.add("xi1530K/massPtCent_Mix", "Mixed event Omega(2012) → #Xi(1530)^{0} K mass vs pT vs cent", kTHnSparseD, {invMassAxis, ptAxis, centAxis}); + histos.add("xi1530K/massXiPi", "#Xi #pi mass of selected candidates (before the rapidity cut)", kTH1F, {xiPiMassAxis}); + histos.add("xi1530K/massXiPiVsMass", "#Xi #pi mass vs #Xi #pi K mass", kTH2F, {invMassAxis, xiPiMassAxis}); + + // QA of the mode-B inputs (once per object and collision) + histos.add("xi1530K/QAbefore/xiMass", "Xi mass before cuts", kTH1F, {xiMassAxis}); + histos.add("xi1530K/QAbefore/xiPt", "Xi pT before cuts", kTH1F, {ptAxisQA}); + histos.add("xi1530K/QAbefore/xiEta", "Xi eta before cuts", kTH1F, {etaAxisQA}); + histos.add("xi1530K/QAafter/xiMass", "Xi mass after cuts", kTH1F, {xiMassAxis}); + histos.add("xi1530K/QAafter/xiPt", "Xi pT after cuts", kTH1F, {ptAxisQA}); + histos.add("xi1530K/QAafter/xiEta", "Xi eta after cuts", kTH1F, {etaAxisQA}); + histos.add("xi1530K/QAbefore/trackPt", "Track pT before cuts", kTH1F, {ptAxisQA}); + histos.add("xi1530K/QAbefore/trackEta", "Track eta before cuts", kTH1F, {etaAxisQA}); + histos.add("xi1530K/QAbefore/trackDCAxy", "Track DCAxy before cuts", kTH2F, {ptAxisQA, dcaxyAxis}); + histos.add("xi1530K/QAbefore/trackDCAz", "Track DCAz before cuts", kTH2F, {ptAxisQA, dcazAxis}); + histos.add("xi1530K/QAbefore/pionTPCNSigma", "Pion TPC NSigma before cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("xi1530K/QAbefore/pionTOFNSigma", "Pion TOF NSigma before cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("xi1530K/QAbefore/kaonTPCNSigma", "Kaon TPC NSigma before cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("xi1530K/QAbefore/kaonTOFNSigma", "Kaon TOF NSigma before cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("xi1530K/QAafter/pionPt", "Pion pT after cuts", kTH1F, {ptAxisQA}); + histos.add("xi1530K/QAafter/pionEta", "Pion eta after cuts", kTH1F, {etaAxisQA}); + histos.add("xi1530K/QAafter/pionDCAxy", "Pion DCAxy after cuts", kTH2F, {ptAxisQA, dcaxyAxis}); + histos.add("xi1530K/QAafter/pionDCAz", "Pion DCAz after cuts", kTH2F, {ptAxisQA, dcazAxis}); + histos.add("xi1530K/QAafter/pionTPCNSigma", "Pion TPC NSigma after cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("xi1530K/QAafter/pionTOFNSigma", "Pion TOF NSigma after cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("xi1530K/QAafter/kaonPt", "Kaon pT after cuts", kTH1F, {ptAxisQA}); + histos.add("xi1530K/QAafter/kaonEta", "Kaon eta after cuts", kTH1F, {etaAxisQA}); + histos.add("xi1530K/QAafter/kaonDCAxy", "Kaon DCAxy after cuts", kTH2F, {ptAxisQA, dcaxyAxis}); + histos.add("xi1530K/QAafter/kaonDCAz", "Kaon DCAz after cuts", kTH2F, {ptAxisQA, dcazAxis}); + histos.add("xi1530K/QAafter/kaonTPCNSigma", "Kaon TPC NSigma after cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("xi1530K/QAafter/kaonTOFNSigma", "Kaon TOF NSigma after cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + + // Charge-pattern controls (never part of the signal) + histos.add("xi1530K_wrongSign/invmassPattern", "Wrong-sign #Xi #pi K mass by charge pattern", kTH2F, {patternAxis, invMassAxis}); + histos.add("xi1530K_wrongSign/massPtPattern", "Wrong-sign #Xi #pi K mass vs pT by charge pattern", kTH3F, {invMassAxis, ptAxis, patternAxis}); + + if (doprocessXi1530KMCMicro) { + histos.add("xi1530K/MC/hMCRecOmega2012Pt", "MC reconstructed Omega(2012) → #Xi(1530)^{0} K pT", kTH1F, {ptAxis}); + histos.add("xi1530K/MC/hMCRecOmega2012PtEta", "MC reconstructed Omega(2012) → #Xi(1530)^{0} K pT vs eta", kTH2F, {ptAxis, etaAxis}); + histos.add("xi1530K/MC/hMCRecMass", "MC reconstructed Omega(2012) → #Xi(1530)^{0} K mass", kTH1F, {invMassAxis}); + histos.add("xi1530K/MC/hMCRecMassXiPi", "MC reconstructed #Xi #pi mass", kTH1F, {xiPiMassAxis}); + histos.add("xi1530K/MC/hMCRecXiPt", "MC reconstructed Xi pT", kTH1F, {ptAxis}); + histos.add("xi1530K/MC/hMCRecPionPt", "MC reconstructed pion pT", kTH1F, {ptAxis}); + histos.add("xi1530K/MC/hMCRecKaonPt", "MC reconstructed kaon pT", kTH1F, {ptAxis}); + const AxisSpec recoPtAxis{binsPt, "p_{T}^{reco} (GeV/c)"}; + const AxisSpec truthPtAxis{binsPt, "p_{T}^{truth} (GeV/c)"}; + const AxisSpec signAxis{2, -1.5, 1.5, "Omega PDG sign (+1 particle, -1 antiparticle)"}; + histos.add("xi1530K/MC/hMCRecXi1530KResponse", "Truth-matched selected #Omega #rightarrow #Xi(1530) K", kTHnSparseD, + {recoPtAxis, truthPtAxis, centAxis, {500, 0., 500., "MC multiplicity"}, signAxis}); } - - if (!cascprimaryTrackCut(xi)) - continue; - if (!casctopCut(xi)) - continue; - - if constexpr (FillQA) { - nCascAfterCuts++; - histos.fill(HIST("QAafter/xiMass"), xi.mXi()); - histos.fill(HIST("QAafter/xiPt"), xi.pt()); - histos.fill(HIST("QAafter/xiEta"), xi.eta()); - histos.fill(HIST("QAafter/xiDCAxy"), xi.pt(), xi.dcaXYCascToPV()); - histos.fill(HIST("QAafter/xiDCAz"), xi.pt(), xi.dcaZCascToPV()); - histos.fill(HIST("QAafter/xiV0CosPA"), xi.pt(), xi.v0CosPA()); - histos.fill(HIST("QAafter/xiCascCosPA"), xi.pt(), xi.cascCosPA()); - histos.fill(HIST("QAafter/xiV0Radius"), xi.pt(), xi.transRadius()); - histos.fill(HIST("QAafter/xiCascRadius"), xi.pt(), xi.cascTransRadius()); - histos.fill(HIST("QAafter/xiV0DauDCA"), xi.pt(), xi.daughDCA()); - histos.fill(HIST("QAafter/xiCascDauDCA"), xi.pt(), xi.cascDaughDCA()); + if (doprocessXi1530KMCGen) { + const AxisSpec truthPtAxis{binsPt, "p_{T}^{truth} (GeV/c)"}; + const AxisSpec signAxis{2, -1.5, 1.5, "Omega PDG sign (+1 particle, -1 antiparticle)"}; + histos.add("xi1530K/MC/hMCGenSelectedXi1530KResponse", "Selected-event generated #Omega #rightarrow #Xi(1530) K", kTHnSparseD, + {truthPtAxis, centAxis, {500, 0., 500., "MC multiplicity"}, signAxis}); } - - selectedXis.push_back({xi, cascadeDaughterIds(xi)}); } - return selectedXis; + LOG(info) << "Size of the histograms in Omega(2012) analysis task"; + histos.print(); } - template - void fill(const CollisionT& collision, const CascadesT& cascades, const V0sT& v0s) + // Mode A, same event (data): event QA, Xi and K0S QA, kinematic-cut QA and the Xi K0S mass + template + void fillXiK0s(const CollisionT& collision, const CascadesT& cascades, const V0sT& v0s) { auto cent = collision.cent(); - // Fill event QA histograms (only for same-event) - if constexpr (!IsMix) { - histos.fill(HIST("Event/posZ"), collision.posZ()); - histos.fill(HIST("Event/centrality"), cent); - histos.fill(HIST("Event/posZvsCent"), collision.posZ(), cent); - histos.fill(HIST("Event/nCascades"), cascades.size()); - histos.fill(HIST("Event/nV0s"), v0s.size()); - } + histos.fill(HIST("Event/posZ"), collision.posZ()); + histos.fill(HIST("Event/centrality"), cent); + histos.fill(HIST("Event/posZvsCent"), collision.posZ(), cent); + histos.fill(HIST("Event/nCascades"), cascades.size()); + histos.fill(HIST("Event/nV0s"), v0s.size()); // Count candidates after cuts int nCascAfterCuts = 0; int nV0sAfterCuts = 0; - auto selectedK0s = selectK0sCandidates(collision, v0s, nV0sAfterCuts); - auto selectedXis = selectXiCandidates(cascades, nCascAfterCuts); - - for (const auto& selectedXi : selectedXis) { - const auto& xi = selectedXi.candidate; - - // Build Xi + K0s - for (const auto& selectedK0 : selectedK0s) { - const auto& v0 = selectedK0.candidate; - - if constexpr (!IsMix) { - if (sharesAnyDaughterId(selectedXi.daughterIds, selectedK0.daughterIds)) { - continue; - } - } - - // 4-vectors - ROOT::Math::PxPyPzEVector pXi, pK0s, pRes; - pXi = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(xi.pt(), xi.eta(), xi.phi(), xi.mXi())); - pK0s = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(v0.pt(), v0.eta(), v0.phi(), massK0)); - pRes = pXi + pK0s; - - float alpha = 0.f; - bool kinCutFlag = true; - if (cKinCuts) { - kinCutFlag = kinCuts(pXi, pK0s, pRes, alpha); - if constexpr (!IsMix) { - histos.fill(HIST("QAbefore/omegaAlphaVsPt"), pRes.Pt(), alpha); - } - } - - if (cKinCuts && !kinCutFlag) - continue; - - if constexpr (!IsMix) { - if (cKinCuts) { - histos.fill(HIST("QAafter/omegaAlphaVsPt"), pRes.Pt(), alpha); - } - } - - if (std::abs(pRes.Rapidity()) >= cfgRapidityCut) - continue; - - if constexpr (!IsMix) { - histos.fill(HIST("omega2012/invmass"), pRes.M()); - histos.fill(HIST("omega2012/massPtCent"), pRes.M(), pRes.Pt(), cent); - } else { - histos.fill(HIST("omega2012/invmass_Mix"), pRes.M()); - histos.fill(HIST("omega2012/massPtCent_Mix"), pRes.M(), pRes.Pt(), cent); + auto onK0s = [&](auto const& v0, double properLifetime, bool selected) { + histos.fill(HIST("QAbefore/k0sMassPt"), v0.pt(), v0.mK0Short()); + histos.fill(HIST("QAbefore/k0sPt"), v0.pt()); + histos.fill(HIST("QAbefore/k0sEta"), v0.eta()); + histos.fill(HIST("QAbefore/k0sCosPA"), v0.pt(), v0.v0CosPA()); + histos.fill(HIST("QAbefore/k0sRadius"), v0.pt(), v0.transRadius()); + histos.fill(HIST("QAbefore/k0sDauDCA"), v0.pt(), v0.daughDCA()); + histos.fill(HIST("QAbefore/k0sDCAtoPV"), v0.pt(), std::abs(v0.dcav0topv())); + histos.fill(HIST("QAbefore/k0sProperLifetime"), v0.pt(), properLifetime); + histos.fill(HIST("QAbefore/k0sArmenteros"), v0.alpha(), v0.qtarm()); + histos.fill(HIST("QAbefore/k0sDauPosDCA"), v0.pt(), std::abs(v0.dcapostopv())); + histos.fill(HIST("QAbefore/k0sDauNegDCA"), v0.pt(), std::abs(v0.dcanegtopv())); + histos.fill(HIST("QAbefore/k0sDauTPCNsigmaPosPi"), v0.pt(), v0.daughterTPCNSigmaPosPi()); + histos.fill(HIST("QAbefore/k0sDauTPCNsigmaNegPi"), v0.pt(), v0.daughterTPCNSigmaNegPi()); + histos.fill(HIST("QAbefore/k0sNCrossedRowsPos"), v0.pt(), v0.nCrossedRowsPos()); + histos.fill(HIST("QAbefore/k0sNCrossedRowsNeg"), v0.pt(), v0.nCrossedRowsNeg()); + if (!selected) { + return; + } + nV0sAfterCuts++; + histos.fill(HIST("QAafter/k0sMassPt"), v0.pt(), v0.mK0Short()); + histos.fill(HIST("QAafter/k0sPt"), v0.pt()); + histos.fill(HIST("QAafter/k0sEta"), v0.eta()); + histos.fill(HIST("QAafter/k0sCosPA"), v0.pt(), v0.v0CosPA()); + histos.fill(HIST("QAafter/k0sRadius"), v0.pt(), v0.transRadius()); + histos.fill(HIST("QAafter/k0sDauDCA"), v0.pt(), v0.daughDCA()); + histos.fill(HIST("QAafter/k0sDCAtoPV"), v0.pt(), std::abs(v0.dcav0topv())); + histos.fill(HIST("QAafter/k0sProperLifetime"), v0.pt(), properLifetime); + histos.fill(HIST("QAafter/k0sArmenteros"), v0.alpha(), v0.qtarm()); + histos.fill(HIST("QAafter/k0sDauPosDCA"), v0.pt(), std::abs(v0.dcapostopv())); + histos.fill(HIST("QAafter/k0sDauNegDCA"), v0.pt(), std::abs(v0.dcanegtopv())); + histos.fill(HIST("QAafter/k0sDauTPCNsigmaPosPi"), v0.pt(), v0.daughterTPCNSigmaPosPi()); + histos.fill(HIST("QAafter/k0sDauTPCNsigmaNegPi"), v0.pt(), v0.daughterTPCNSigmaNegPi()); + histos.fill(HIST("QAafter/k0sNCrossedRowsPos"), v0.pt(), v0.nCrossedRowsPos()); + histos.fill(HIST("QAafter/k0sNCrossedRowsNeg"), v0.pt(), v0.nCrossedRowsNeg()); + }; + + auto onXi = [&](auto const& xi, bool selected) { + histos.fill(HIST("QAbefore/xiMass"), xi.mXi()); + histos.fill(HIST("QAbefore/xiPt"), xi.pt()); + histos.fill(HIST("QAbefore/xiEta"), xi.eta()); + histos.fill(HIST("QAbefore/xiDCAxy"), xi.pt(), xi.dcaXYCascToPV()); + histos.fill(HIST("QAbefore/xiDCAz"), xi.pt(), xi.dcaZCascToPV()); + histos.fill(HIST("QAbefore/xiV0CosPA"), xi.pt(), xi.v0CosPA()); + histos.fill(HIST("QAbefore/xiCascCosPA"), xi.pt(), xi.cascCosPA()); + histos.fill(HIST("QAbefore/xiV0Radius"), xi.pt(), xi.transRadius()); + histos.fill(HIST("QAbefore/xiCascRadius"), xi.pt(), xi.cascTransRadius()); + histos.fill(HIST("QAbefore/xiV0DauDCA"), xi.pt(), xi.daughDCA()); + histos.fill(HIST("QAbefore/xiCascDauDCA"), xi.pt(), xi.cascDaughDCA()); + if (!selected) { + return; + } + nCascAfterCuts++; + histos.fill(HIST("QAafter/xiMass"), xi.mXi()); + histos.fill(HIST("QAafter/xiPt"), xi.pt()); + histos.fill(HIST("QAafter/xiEta"), xi.eta()); + histos.fill(HIST("QAafter/xiDCAxy"), xi.pt(), xi.dcaXYCascToPV()); + histos.fill(HIST("QAafter/xiDCAz"), xi.pt(), xi.dcaZCascToPV()); + histos.fill(HIST("QAafter/xiV0CosPA"), xi.pt(), xi.v0CosPA()); + histos.fill(HIST("QAafter/xiCascCosPA"), xi.pt(), xi.cascCosPA()); + histos.fill(HIST("QAafter/xiV0Radius"), xi.pt(), xi.transRadius()); + histos.fill(HIST("QAafter/xiCascRadius"), xi.pt(), xi.cascTransRadius()); + histos.fill(HIST("QAafter/xiV0DauDCA"), xi.pt(), xi.daughDCA()); + histos.fill(HIST("QAafter/xiCascDauDCA"), xi.pt(), xi.cascDaughDCA()); + }; + + const bool kinCutsOn = core.kinCutsEnabled(); + auto onCandidate = [&](auto const& /*xi*/, auto const& /*v0*/, XiK0sCandidateValues const& c) { + if (kinCutsOn) { + histos.fill(HIST("QAbefore/omegaAlphaVsPt"), c.omega.Pt(), c.alpha); + if (!c.passesKinCut) { + return; } + histos.fill(HIST("QAafter/omegaAlphaVsPt"), c.omega.Pt(), c.alpha); } - } + if (!c.inRapidity) { + return; + } + histos.fill(HIST("omega2012/invmass"), c.omega.M()); + histos.fill(HIST("omega2012/massPtCent"), c.omega.M(), c.omega.Pt(), cent); + }; - // Fill event QA for after-cuts counters (only for same-event) - if constexpr (!IsMix) { - histos.fill(HIST("Event/nCascadesAfterCuts"), nCascAfterCuts); - histos.fill(HIST("Event/nV0sAfterCuts"), nV0sAfterCuts); - } + core.forEachXiK0sCandidate(histos, collision, collision, cascades, v0s, false, onXi, onK0s, onCandidate); + + histos.fill(HIST("Event/nCascadesAfterCuts"), nCascAfterCuts); + histos.fill(HIST("Event/nV0sAfterCuts"), nV0sAfterCuts); } void processDummy(aod::ResoCollision const& /*collision*/) @@ -888,92 +464,126 @@ struct Omega2012Analysis { } PROCESS_SWITCH(Omega2012Analysis, processDummy, "Process Dummy", true); - void processData(const aod::ResoCollision& collision, + void processData(ResoCollisions::iterator const& collision, aod::ResoCascades const& resocasc, aod::ResoV0s const& resov0s) { - if (cRecoINELgt0 && !collision.isRecINELgt0()) + if (!core.passesEventCuts(collision)) { return; - - fill(collision, resocasc, resov0s); + } + fillXiK0s(collision, resocasc, resov0s); } PROCESS_SWITCH(Omega2012Analysis, processData, "Process Event for data", false); - void processMixedEvent(const aod::ResoCollisions& collisions, + void processMixedEvent(ResoCollisions const& collisions, aod::ResoCascades const& resocasc, aod::ResoV0s const& resov0s) { - auto cascV0sTuple = std::make_tuple(resocasc, resov0s); - Pair pairs{colBinning, nEvtMixing, -1, collisions, cascV0sTuple, &cache}; + Pair pairs{colBinning, nEvtMixing, -1, collisions, cascV0sTuple, &cache}; + const bool kinCutsOn = core.kinCutsEnabled(); for (const auto& [collision1, casc1, collision2, v0s2] : pairs) { - if (cRecoINELgt0 && (!collision1.isRecINELgt0() || !collision2.isRecINELgt0())) + if (!core.passesEventCuts(collision1) || !core.passesEventCuts(collision2)) { continue; - + } auto cent = collision1.cent(); - int unusedXiCount = 0; - int unusedK0sCount = 0; - auto selectedXis = selectXiCandidates(casc1, unusedXiCount); - auto selectedK0s = selectK0sCandidates(collision2, v0s2, unusedK0sCount); - - for (const auto& selectedXi : selectedXis) { - const auto& xi = selectedXi.candidate; - - for (const auto& selectedK0 : selectedK0s) { - const auto& v0 = selectedK0.candidate; - ROOT::Math::PxPyPzEVector pXi, pK0s, pRes; - pXi = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(xi.pt(), xi.eta(), xi.phi(), xi.mXi())); - pK0s = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(v0.pt(), v0.eta(), v0.phi(), massK0)); - pRes = pXi + pK0s; - - float alpha = 0.f; - bool kinCutFlag = true; - if (cKinCuts) { - kinCutFlag = kinCuts(pXi, pK0s, pRes, alpha); - } - - if (cKinCuts && !kinCutFlag) - continue; - - if (std::abs(pRes.Rapidity()) >= cfgRapidityCut) - continue; - - histos.fill(HIST("omega2012/invmass_Mix"), pRes.M()); - histos.fill(HIST("omega2012/massPtCent_Mix"), pRes.M(), pRes.Pt(), cent); + // Xi from collision 1, K0s from collision 2 (selected with the vertex of collision 2) + auto onCandidate = [&](auto const& /*xi*/, auto const& /*v0*/, XiK0sCandidateValues const& c) { + if (kinCutsOn && !c.passesKinCut) { + return; } - } + if (!c.inRapidity) { + return; + } + histos.fill(HIST("omega2012/invmass_Mix"), c.omega.M()); + histos.fill(HIST("omega2012/massPtCent_Mix"), c.omega.M(), c.omega.Pt(), cent); + }; + core.forEachXiK0sCandidate(histos, collision1, collision2, casc1, v0s2, false, nullptr, nullptr, onCandidate); } } PROCESS_SWITCH(Omega2012Analysis, processMixedEvent, "Process Mixed Event", false); - // MC processes - placeholder for future implementation - void processMC(const aod::ResoCollision& /*collision*/, - aod::ResoCascades const& /*resocasc*/, - aod::ResoV0s const& /*resov0s*/, - aod::McParticles const& /*mcParticles*/) + // MC reconstructed processing: match reconstructed Xi + K0s pairs to a common Omega(2012) mother + void processMC(ResoMCCollisions::iterator const& collision, + ResoMCCascades const& resocasc, + ResoMCV0s const& resov0s, + aod::ResoMCParents_001 const& resoParents) + { + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; + } + const auto omegaParents = omegaParentTruth(resoParents); + auto onCandidate = [&](auto const& xi, auto const& v0, XiK0sCandidateValues const& c) { + if (classifyXiK0sTruth(xi, v0) != XiK0sTruth::Matched) { + return; + } + const int motherId = xi.motherId(); + const auto parentIt = omegaParents.find(motherId); + if (parentIt == omegaParents.end() || (cMCInjection && !parentIt->second.injected)) { + return; + } + if (cUseTruthRapidity && (!(std::abs(xi.motherRap()) < candidateCuts.cfgRapidityCut.value) || !std::isfinite(xi.motherRap()))) { + return; + } + if ((core.kinCutsEnabled() && !c.passesKinCut) || !c.inRapidity) { + return; + } + histos.fill(HIST("MC/hMCRecOmega2012Pt"), c.omega.Pt()); + histos.fill(HIST("MC/hMCRecOmega2012PtEta"), c.omega.Pt(), c.omega.Eta()); + histos.fill(HIST("MC/hMCRecXiPt"), xi.pt()); + histos.fill(HIST("MC/hMCRecK0sPt"), v0.pt()); + const double sign = xi.motherPDG() > 0 ? 1. : -1.; + histos.fill(HIST("MC/hMCRecXiK0sResponse"), c.omega.Pt(), xi.motherPt(), collision.cent(), collision.mcMultiplicity(), sign); + }; + core.forEachXiK0sCandidate(histos, collision, collision, resocasc, resov0s, false, nullptr, nullptr, onCandidate); + } + PROCESS_SWITCH(Omega2012Analysis, processMC, "Process MC with truth matching", false); + + // Acceptance x efficiency denominator, counted per selected reconstructed collision, + // as in the Xi(1530) and Xi(1820) tasks. Split collisions repeat the parent set; + // this is not a unique-MC-event denominator or a signal-loss correction. + void processMCGen(ResoMCCollisions::iterator const& collision, + aod::ResoMCParents_001 const& resoParents) { - // TODO: Implement MC truth matching for Xi + K0s - // - Match reconstructed Xi to MC Xi - // - Match reconstructed K0s to MC K0s - // - Fill MC truth histograms - // - Fill reconstruction efficiency histograms - // - Check if the Xi and K0s come from same Omega(2012) mother + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; + } + for (const auto& part : resoParents) { + if (std::abs(part.pdgCode()) != kOmega2012Minus) { + continue; + } + if (!(std::abs(part.y()) < candidateCuts.cfgRapidityCut.value) || !std::isfinite(part.y())) { + continue; + } + if (cMCInjection && !isInjectedOmegaParent(part)) { + continue; + } + // Require the same immediate Xi + K0S channel as processMCGenerated. + if ((std::abs(part.daughterPDG1()) == kXiMinus && part.daughterPDG2() == kK0Short) || + (std::abs(part.daughterPDG2()) == kXiMinus && part.daughterPDG1() == kK0Short)) { + histos.fill(HIST("MC/hMCGenSelectedXiK0sPt"), part.pt()); + const double sign = part.pdgCode() > 0 ? 1. : -1.; + histos.fill(HIST("MC/hMCGenSelectedXiK0sResponse"), part.pt(), collision.cent(), collision.mcMultiplicity(), sign); + } + } } - PROCESS_SWITCH(Omega2012Analysis, processMC, "Process MC with truth matching (placeholder)", false); + PROCESS_SWITCH(Omega2012Analysis, processMCGen, "Process MC generated XiK0s parents in selected events", false); + // Inclusive MC-particle QA. The rapidity-limited source histogram is a legacy all-event QA denominator; + // selected-event efficiency denominators are filled by processMCGen and processXi1530KMCGen. void processMCGenerated(aod::McParticles const& mcParticles) { // Process MC generated particles (no reconstruction requirement) // This resonance decays to Xi + K0s for (const auto& mcParticle : mcParticles) { - // Look for Omega(2012) - PDG code may vary by generator + // Look for Omega(2012) int pdg = mcParticle.pdgCode(); - // TODO: Update the PDG code library to include Omega(2012) codes - if (std::abs(pdg) != kPlaceholderPdgCode) + if (std::abs(pdg) != kOmega2012Minus) { continue; + } // Fill generated level histograms auto pt = mcParticle.pt(); @@ -986,8 +596,9 @@ struct Omega2012Analysis { // Get daughters auto daughters = mcParticle.daughters_as(); - if (daughters.size() != kNumExpectedDaughters) + if (daughters.size() != NumExpectedDaughters) { continue; + } int daughter1PDG = 0, daughter2PDG = 0; ROOT::Math::PxPyPzEVector p1, p2, pMother; @@ -1014,143 +625,212 @@ struct Omega2012Analysis { if ((std::abs(daughter1PDG) == kXiMinus && daughter2PDG == kK0Short) || (std::abs(daughter2PDG) == kXiMinus && daughter1PDG == kK0Short)) { histos.fill(HIST("MC/hMCTruthInvMassXiK0s"), motherM); - histos.fill(HIST("MC/hMCTruthMassPtXiK0s"), motherM, motherPt); + // Legacy all-event rapidity-limited QA histogram; selected-event efficiency + // denominators are written by processMCGen and processXi1530KMCGen. + if (std::abs(y) < candidateCuts.cfgRapidityCut.value) { + histos.fill(HIST("MC/hMCTruthMassPtXiK0s"), motherM, motherPt); + } + + const bool isDaughter1Xi = std::abs(daughter1PDG) == kXiMinus; + const auto& pXiTruth = isDaughter1Xi ? p1 : p2; + const auto& pK0sTruth = isDaughter1Xi ? p2 : p1; + histos.fill(HIST("MC/hMCTrueXiPt"), pXiTruth.Pt()); + histos.fill(HIST("MC/hMCTrueK0sPt"), pK0sTruth.Pt()); } } } - PROCESS_SWITCH(Omega2012Analysis, processMCGenerated, "Process MC generated particles (placeholder)", false); + PROCESS_SWITCH(Omega2012Analysis, processMCGenerated, "Process MC generated particles", false); - // Fill function for 3-body decay analysis - template - void fillThreeBody(const CollisionT& collision, const CascadesT& cascades, const V0sT& v0s, const TracksT& tracks, const TrackIdsT& trackIds) + // Mode B: Xi(1530)0 K- -> Xi- pi+ K- (and charge conjugate) from one collision, or Xi and tracks from two mixed collisions + template + void fillXi1530K(const CollisionT& collision, float cent, const CascadesT& cascades, const TracksT& tracks, const TrackIdsT& trackIds, + const OmegaParentTruthMap* omegaParents = nullptr) { - auto cent = collision.cent(); - int unusedXiCount = 0; - int unusedK0sCount = 0; - auto selectedXis = selectXiCandidates(cascades, unusedXiCount); - auto selectedK0s = selectK0sCandidates(collision, v0s, unusedK0sCount); - - // First loop: xi + pion to check xi1530 mass window - for (const auto& selectedXi : selectedXis) { - const auto& xi = selectedXi.candidate; - - for (const auto& pion : tracks) { - auto pionTrackId = trackSourceId(pion, trackIds); - if (sharesDaughterId(selectedXi.daughterIds, pionTrackId)) { - continue; + // Input QA: same event only (each object once per collision) + auto onXi = [&](auto const& xi, bool selected) { + if constexpr (IsMix) { + return; + } + histos.fill(HIST("xi1530K/QAbefore/xiMass"), xi.mXi()); + histos.fill(HIST("xi1530K/QAbefore/xiPt"), xi.pt()); + histos.fill(HIST("xi1530K/QAbefore/xiEta"), xi.eta()); + if (!selected) { + return; + } + histos.fill(HIST("xi1530K/QAafter/xiMass"), xi.mXi()); + histos.fill(HIST("xi1530K/QAafter/xiPt"), xi.pt()); + histos.fill(HIST("xi1530K/QAafter/xiEta"), xi.eta()); + }; + + auto onTrack = [&](auto const& track, int pionStage, int kaonStage) { + if constexpr (IsMix) { + return; + } + const bool hasTOF = track.hasTOF(); + histos.fill(HIST("xi1530K/QAbefore/trackPt"), track.pt()); + histos.fill(HIST("xi1530K/QAbefore/trackEta"), track.eta()); + histos.fill(HIST("xi1530K/QAbefore/trackDCAxy"), track.pt(), track.dcaXY()); + histos.fill(HIST("xi1530K/QAbefore/trackDCAz"), track.pt(), track.dcaZ()); + histos.fill(HIST("xi1530K/QAbefore/pionTPCNSigma"), track.pt(), track.tpcNSigmaPi()); + histos.fill(HIST("xi1530K/QAbefore/kaonTPCNSigma"), track.pt(), track.tpcNSigmaKa()); + if (hasTOF) { + histos.fill(HIST("xi1530K/QAbefore/pionTOFNSigma"), track.pt(), track.tofNSigmaPi()); + histos.fill(HIST("xi1530K/QAbefore/kaonTOFNSigma"), track.pt(), track.tofNSigmaKa()); + } + if (pionStage == o2::analysis::resonance::kTrkPID) { + histos.fill(HIST("xi1530K/QAafter/pionPt"), track.pt()); + histos.fill(HIST("xi1530K/QAafter/pionEta"), track.eta()); + histos.fill(HIST("xi1530K/QAafter/pionDCAxy"), track.pt(), track.dcaXY()); + histos.fill(HIST("xi1530K/QAafter/pionDCAz"), track.pt(), track.dcaZ()); + histos.fill(HIST("xi1530K/QAafter/pionTPCNSigma"), track.pt(), track.tpcNSigmaPi()); + if (hasTOF) { + histos.fill(HIST("xi1530K/QAafter/pionTOFNSigma"), track.pt(), track.tofNSigmaPi()); } - - // Pion QA before cuts - histos.fill(HIST("QAbefore/pionPt"), pion.pt()); - histos.fill(HIST("QAbefore/pionEta"), pion.eta()); - - if constexpr (IsResoMicrotrack) { - histos.fill(HIST("QAbefore/pionDCAxy"), pion.pt(), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAxy(pion.trackSelectionFlags())); - histos.fill(HIST("QAbefore/pionDCAz"), pion.pt(), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAz(pion.trackSelectionFlags())); - histos.fill(HIST("QAbefore/pionTPCNSigma"), pion.pt(), o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(pion.pidNSigmaPiFlag())); - if (pion.hasTOF()) { - histos.fill(HIST("QAbefore/pionTOFNSigma"), pion.pt(), o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(pion.pidNSigmaPiFlag())); - } - } else { - histos.fill(HIST("QAbefore/pionDCAxy"), pion.pt(), pion.dcaXY()); - histos.fill(HIST("QAbefore/pionDCAz"), pion.pt(), pion.dcaZ()); - histos.fill(HIST("QAbefore/pionTPCNSigma"), pion.pt(), pion.tpcNSigmaPi()); - if (pion.hasTOF()) { - histos.fill(HIST("QAbefore/pionTOFNSigma"), pion.pt(), pion.tofNSigmaPi()); - } - if constexpr (requires { pion.tpcNClsFound(); }) { - histos.fill(HIST("QAbefore/pionTPCNcls"), pion.tpcNClsFound()); - } + } + if (kaonStage == o2::analysis::resonance::kTrkPID) { + histos.fill(HIST("xi1530K/QAafter/kaonPt"), track.pt()); + histos.fill(HIST("xi1530K/QAafter/kaonEta"), track.eta()); + histos.fill(HIST("xi1530K/QAafter/kaonDCAxy"), track.pt(), track.dcaXY()); + histos.fill(HIST("xi1530K/QAafter/kaonDCAz"), track.pt(), track.dcaZ()); + histos.fill(HIST("xi1530K/QAafter/kaonTPCNSigma"), track.pt(), track.tpcNSigmaKa()); + if (hasTOF) { + histos.fill(HIST("xi1530K/QAafter/kaonTOFNSigma"), track.pt(), track.tofNSigmaKa()); } + } + }; - if (!pionCut(pion)) - continue; - - // Pion QA after cuts - histos.fill(HIST("QAafter/pionPt"), pion.pt()); - histos.fill(HIST("QAafter/pionEta"), pion.eta()); - - if constexpr (IsResoMicrotrack) { - histos.fill(HIST("QAafter/pionDCAxy"), pion.pt(), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAxy(pion.trackSelectionFlags())); - histos.fill(HIST("QAafter/pionDCAz"), pion.pt(), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAz(pion.trackSelectionFlags())); - histos.fill(HIST("QAafter/pionTPCNSigma"), pion.pt(), o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(pion.pidNSigmaPiFlag())); - if (pion.hasTOF()) { - histos.fill(HIST("QAafter/pionTOFNSigma"), pion.pt(), o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(pion.pidNSigmaPiFlag())); - } - } else { - histos.fill(HIST("QAafter/pionDCAxy"), pion.pt(), pion.dcaXY()); - histos.fill(HIST("QAafter/pionDCAz"), pion.pt(), pion.dcaZ()); - histos.fill(HIST("QAafter/pionTPCNSigma"), pion.pt(), pion.tpcNSigmaPi()); - if (pion.hasTOF()) { - histos.fill(HIST("QAafter/pionTOFNSigma"), pion.pt(), pion.tofNSigmaPi()); - } - if constexpr (requires { pion.tpcNClsFound(); }) { - histos.fill(HIST("QAafter/pionTPCNcls"), pion.tpcNClsFound()); + const bool fillWrongSign = core.fillWrongSign(); + auto onCandidate = [&]([[maybe_unused]] auto const& xi, [[maybe_unused]] auto const& pion, [[maybe_unused]] auto const& kaon, Xi1530KCandidateValues const& c) { + if (c.chargePattern != o2::analysis::omega2012ml::kSignalPattern) { + // Charge-pattern controls: same event only, never in the signal histograms + if constexpr (!IsMix) { + if (fillWrongSign && c.inRapidity) { + histos.fill(HIST("xi1530K_wrongSign/invmassPattern"), c.chargePattern, c.omega.M()); + histos.fill(HIST("xi1530K_wrongSign/massPtPattern"), c.omega.M(), c.omega.Pt(), c.chargePattern); } } - - // Check xi1530 mass window cut - if (!xi1530MassCut(xi, pion)) - continue; - - // Second loop: v0 for the selected xi-pion pair - for (const auto& selectedK0 : selectedK0s) { - const auto& v0 = selectedK0.candidate; - if (sharesAnyDaughterId(selectedXi.daughterIds, selectedK0.daughterIds)) { - continue; - } - if (sharesDaughterId(selectedK0.daughterIds, pionTrackId)) { - continue; - } - - // 4-vectors for 3-body decay: Xi + K0s + pion - ROOT::Math::PxPyPzEVector pXi, pK0s, pPion, pRes; - pXi = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(xi.pt(), xi.eta(), xi.phi(), xi.mXi())); - pK0s = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(v0.pt(), v0.eta(), v0.phi(), massK0)); - pPion = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(pion.pt(), pion.eta(), pion.phi(), MassPionCharged)); - - pRes = pXi + pK0s + pPion; - - if (std::abs(pRes.Rapidity()) >= cfgRapidityCut) - continue; - - histos.fill(HIST("omega2012_3body/invmass"), pRes.M()); - histos.fill(HIST("omega2012_3body/massPtCent"), pRes.M(), pRes.Pt(), cent); + return; + } + if constexpr (IsMix) { + if (c.inRapidity) { + histos.fill(HIST("xi1530K/invmass_Mix"), c.omega.M()); + histos.fill(HIST("xi1530K/massPtCent_Mix"), c.omega.M(), c.omega.Pt(), cent); + } + return; + } + histos.fill(HIST("xi1530K/massXiPi"), c.massXiPi); + if (!c.inRapidity) { + return; + } + histos.fill(HIST("xi1530K/invmass"), c.omega.M()); + histos.fill(HIST("xi1530K/massPtCent"), c.omega.M(), c.omega.Pt(), cent); + histos.fill(HIST("xi1530K/massXiPiVsMass"), c.omega.M(), c.massXiPi); + if constexpr (IsMC) { + if (classifyXi1530KTruth(xi, pion, kaon) != Xi1530KTruth::Matched) { + return; + } + if (!omegaParents) { + return; + } + const auto parentIt = omegaParents->find(kaon.motherId()); + if (parentIt == omegaParents->end() || (cMCInjection && !parentIt->second.injected)) { + return; + } + if (cUseTruthRapidity && (!std::isfinite(parentIt->second.y) || !(std::abs(parentIt->second.y) < candidateCuts.cfgRapidityCut.value))) { + return; } + histos.fill(HIST("xi1530K/MC/hMCRecOmega2012Pt"), c.omega.Pt()); + histos.fill(HIST("xi1530K/MC/hMCRecOmega2012PtEta"), c.omega.Pt(), c.omega.Eta()); + histos.fill(HIST("xi1530K/MC/hMCRecMass"), c.omega.M()); + histos.fill(HIST("xi1530K/MC/hMCRecMassXiPi"), c.massXiPi); + histos.fill(HIST("xi1530K/MC/hMCRecXiPt"), xi.pt()); + histos.fill(HIST("xi1530K/MC/hMCRecPionPt"), pion.pt()); + histos.fill(HIST("xi1530K/MC/hMCRecKaonPt"), kaon.pt()); + const double sign = parentIt->second.pdgCode > 0 ? 1. : -1.; + histos.fill(HIST("xi1530K/MC/hMCRecXi1530KResponse"), c.omega.Pt(), parentIt->second.pt, cent, collision.mcMultiplicity(), sign); } + }; + + core.forEachXi1530KCandidate(histos, collision, cascades, tracks, trackIds, false, onXi, onTrack, onCandidate); + } + + void processXi1530KMicro(ResoCollisions::iterator const& collision, + aod::ResoCascades const& resocasc, + ResoMicroTracks const& resomicrotracks) + { + if (!core.passesEventCuts(collision)) { + return; } + fillXi1530K(collision, collision.cent(), resocasc, resomicrotracks, nullptr); } + PROCESS_SWITCH(Omega2012Analysis, processXi1530KMicro, "Process Xi(1530)0 K mode with ResoMicroTracks_001", false); - // 3-body decay analysis: Xi + pi + K0s with ResoTracks - void processThreeBodyWithTracks(const aod::ResoCollision& collision, - aod::ResoCascades const& resocasc, - aod::ResoV0s const& resov0s, - aod::ResoTracks const& resotracks, - aod::ResoTrackTracks const& resotrackids) + void processXi1530KTracks(ResoCollisions::iterator const& collision, + aod::ResoCascades const& resocasc, + aod::ResoTracks const& resotracks, + aod::ResoTrackTracks const& resotrackids) { - if (cRecoINELgt0 && !collision.isRecINELgt0()) + if (!core.passesEventCuts(collision)) { return; + } + fillXi1530K(collision, collision.cent(), resocasc, resotracks, resotrackids); + } + PROCESS_SWITCH(Omega2012Analysis, processXi1530KTracks, "Process Xi(1530)0 K mode with ResoTracks", false); - fillThreeBody(collision, resocasc, resov0s, resotracks, resotrackids); + void processXi1530KMCMicro(ResoMCCollisions::iterator const& collision, + ResoMCCascades const& resocasc, + ResoMCMicroTracks const& resomicrotracks, + aod::ResoMCParents_001 const& resoParents) + { + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; + } + const auto omegaParents = omegaParentTruth(resoParents); + fillXi1530K(collision, collision.cent(), resocasc, resomicrotracks, nullptr, &omegaParents); } - PROCESS_SWITCH(Omega2012Analysis, processThreeBodyWithTracks, "Process 3-body decay with ResoTracks", false); - - // 3-body decay analysis: Xi + pi + K0s with ResoMicroTracks - void processThreeBodyWithMicroTracks(const aod::ResoCollision& collision, - aod::ResoCascades const& resocasc, - aod::ResoV0s const& resov0s, - aod::ResoMicroTracks const& resomicrotracks, - aod::ResoMicroTrackTracks const& resomicrotrackids) + PROCESS_SWITCH(Omega2012Analysis, processXi1530KMCMicro, "Process Xi(1530)0 K mode with truth matching (ResoMicroTracks_001)", false); + + // Selected-event generated denominator for Omega -> Xi(1530) K, counted once + // per selected reconstructed collision. Split collisions repeat the MC parents. + void processXi1530KMCGen(ResoMCCollisions::iterator const& collision, + aod::ResoMCParents_001 const& resoParents) { - if (cRecoINELgt0 && !collision.isRecINELgt0()) + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { return; + } + for (const auto& parent : resoParents) { + if (std::abs(parent.pdgCode()) != kOmega2012Minus || + !std::isfinite(parent.y()) || !(std::abs(parent.y()) < candidateCuts.cfgRapidityCut.value) || + (cMCInjection && !isInjectedOmegaParent(parent))) { + continue; + } + if (classifyGeneratedOmega2012(parent.pdgCode(), parent.daughterPDG1(), parent.daughterPDG2()) != GeneratedChannel::Xi1530K) { + continue; + } + const double sign = parent.pdgCode() > 0 ? 1. : -1.; + histos.fill(HIST("xi1530K/MC/hMCGenSelectedXi1530KResponse"), parent.pt(), collision.cent(), collision.mcMultiplicity(), sign); + } + } + PROCESS_SWITCH(Omega2012Analysis, processXi1530KMCGen, "Process generated Xi(1530) K parents in selected events", false); - fillThreeBody(collision, resocasc, resov0s, resomicrotracks, resomicrotrackids); + // Mixed events: Xi from collision 1, pion and kaon from collision 2 + void processXi1530KMixedMicro(ResoCollisions const& collisions, + aod::ResoCascades const& resocasc, + ResoMicroTracks const& resomicrotracks) + { + auto cascTracksTuple = std::make_tuple(resocasc, resomicrotracks); + Pair pairs{colBinning, nEvtMixing, -1, collisions, cascTracksTuple, &cache}; + for (const auto& [collision1, casc1, collision2, tracks2] : pairs) { + if (!core.passesEventCuts(collision1) || !core.passesEventCuts(collision2)) { + continue; + } + fillXi1530K(collision1, collision1.cent(), casc1, tracks2, nullptr); + } } - PROCESS_SWITCH(Omega2012Analysis, processThreeBodyWithMicroTracks, "Process 3-body decay with ResoMicroTracks", false); + PROCESS_SWITCH(Omega2012Analysis, processXi1530KMixedMicro, "Process mixed events of the Xi(1530)0 K mode (ResoMicroTracks_001)", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(context)}; } diff --git a/PWGLF/Tasks/Resonances/omega2012TrainingTable.cxx b/PWGLF/Tasks/Resonances/omega2012TrainingTable.cxx new file mode 100644 index 00000000000..d6c7eb317a3 --- /dev/null +++ b/PWGLF/Tasks/Resonances/omega2012TrainingTable.cxx @@ -0,0 +1,593 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file omega2012TrainingTable.cxx +/// \brief Derived Omega(2012) training tables, separately for the XiK0s and Xi1530K decay modes +/// \author Bong-Hwi Lim +/// +/// Mode A (XiK0s): processXiK0s, processXiK0sMC. Mode B (Xi1530K, Xi- pi+ K- with micro tracks): +/// processXi1530KMicro, processXi1530KMCMicro. Generated parents: processMCTrue. MC truth of reconstructed MC: processMcTruth. +/// Each candidate is written only to the tables of its own mode (PWGLF/DataModel/LFOmega2012MlTables.h); +/// the selection is the one of omega2012Analysis.cxx (PWGLF/Core/Omega2012AnalysisCore.h, same JSON keys). +/// Only collisions with written candidates get an event row. Generated Omega(2012) mothers are written separately to +/// OMMLGENAUDIT for every selected reconstructed MC collision, including collisions with no exported candidates. +/// In reconstructed MC, daughter labels of written objects and pruned McParticles rows carry candidate truth, so the +/// candidate output needs no source table. + +#include "PWGLF/Core/Omega2012AnalysisCore.h" +#include "PWGLF/Core/Omega2012MlFeatures.h" +#include "PWGLF/Core/ResoAnalysisSelectionCore.h" +#include "PWGLF/DataModel/LFOmega2012MlTables.h" +#include "PWGLF/DataModel/LFResonanceTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::analysis::omega2012; + +namespace ml = o2::analysis::omega2012ml; + +static_assert(std::extent_v == ml::XiK0sNMasterFeatures, + "OmXiK0sFeatures column size must match the XiK0s feature contract"); +static_assert(std::extent_v == ml::Xi1530KNMasterFeatures, + "OmXi1530KFeatures column size must match the Xi1530K feature contract"); + +struct Omega2012TrainingTable { + using ResoCollisions = aod::ResoCollisions_001; + using ResoMCCols = soa::Join; + using ResoMicroTracks = aod::ResoMicroTracks_001; + using ResoMCMicroTracks = soa::Join; + using ResoMCCascades = soa::Join; + using ResoMCV0s = soa::Join; + using ResoMCParents = aod::ResoMCParents_001; + + // FNV-1a over the bit patterns of the master features, for parity logs + static constexpr uint64_t FnvOffsetBasis = 14695981039346656037ULL; + static constexpr uint64_t FnvPrime = 1099511628211ULL; + static constexpr unsigned int BitsPerByte = 8; + static constexpr unsigned int BitsPerFloat = 32; + static constexpr uint32_t ByteMask = 0xffU; + static constexpr int NBuildStatus = 6; + // Pruned McParticles: generations of Omega(2012) descendants and mother steps above a daughter label + static constexpr int McTreeDepth = 3; + static constexpr int MaxAncestorSteps = 8; + static constexpr uint8_t McSelectionTree = 1; + static constexpr uint8_t McSelectionAncestry = 2; + static constexpr std::size_t NStoredMothers = 2; + + Produces mlEvents; + Produces mlCascades; + Produces mlV0s; + Produces mlTracks; + Produces mlXiK0sCandidates; + Produces mlXiK0sInputs; + Produces mlXiK0sTruth; + Produces mlXi1530KCandidates; + Produces mlXi1530KInputs; + Produces mlXi1530KTruth; + Produces mlGenAudit; + Produces mlEventMcs; + Produces mlCascadeMcs; + Produces mlV0Mcs; + Produces mlTrackMcs; + Produces mlMcParticles; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // Selection shared with the Omega(2012) histogram task (same JSON keys) + o2::analysis::resonance::EventCuts eventCuts = makeOmega2012EventCuts(); + XiCuts xiCuts; + K0sCuts k0sCuts; + Xi1530KCuts xi1530KCuts; + o2::analysis::resonance::TrackCuts trackCuts = makeXi1530KTrackCuts(); + PionPidCuts pionPID; + KaonPidCuts kaonPID; + CandidateCuts candidateCuts; + + Configurable omega2012MlExportStage{"omega2012MlExportStage", "loose", "Candidate export stage: loose (pass bits >= 1) or selected (all pass bits of the mode)"}; + Configurable omega2012MlLooseAudit{"omega2012MlLooseAudit", true, "Record the loose cut flow and mass/pT/activity spectrum per mode"}; + Configurable omega2012MlParityRows{"omega2012MlParityRows", 0, "Log feature hashes of the first N exported candidates of each mode"}; + Configurable omega2012MlExportWrongSign{"omega2012MlExportWrongSign", false, "Also export the wrong-sign charge-pattern controls of the Xi1530K mode"}; + Configurable omega2012MlGenRapidityCut{"omega2012MlGenRapidityCut", 0.5f, "Generated-mother denominator rapidity limit; independent of candidate cfgRapidityCut"}; + + // Event row written before the first candidate of the collision + struct PendingEvent { + DecayMode mode = DecayMode::XiK0s; + int64_t recoCollision = -1; + float posZ = 0.f; + float bField = 0.f; + float centrality = 0.f; + float multiplicity = 0.f; + bool recINELgt0 = false; + bool isMC = false; + int64_t mcCollision = -1; + }; + + Omega2012AnalysisCore core; + PendingEvent pendingEvent; + int64_t mlEventRow = -1; + // MC tables of the current DF, set only by the reconstructed-MC process functions + aod::McParticles const* mcParticlesDf = nullptr; + aod::McTrackLabels const* mcTrackLabelsDf = nullptr; + std::unordered_set mlAncestryRows; + std::unordered_map mlCascadeRows; + std::unordered_map mlV0Rows; + std::unordered_map mlTrackRows; + int parityLoggedXiK0s = 0; + int parityLoggedXi1530K = 0; + + void init(InitContext&) + { + const bool xiK0s = doprocessXiK0s || doprocessXiK0sMC; + const bool xi1530K = doprocessXi1530KMicro || doprocessXi1530KMCMicro; + if ((doprocessXiK0s && doprocessXiK0sMC) || (doprocessXi1530KMicro && doprocessXi1530KMCMicro)) { + LOG(fatal) << "Enable at most one of processXiK0s/processXiK0sMC and one of processXi1530KMicro/processXi1530KMCMicro"; + } + if (!xiK0s && !xi1530K) { + LOG(fatal) << "Enable at least one candidate process function"; + } + if (doprocessMCTrue && !doprocessXiK0sMC && !doprocessXi1530KMCMicro) { + LOG(fatal) << "processMCTrue requires processXiK0sMC or processXi1530KMCMicro"; + } + if (doprocessMcTruth != (doprocessXiK0sMC || doprocessXi1530KMCMicro)) { + LOG(fatal) << "processMcTruth must be enabled exactly when processXiK0sMC or processXi1530KMCMicro is enabled"; + } + if (omega2012MlParityRows < 0) { + LOG(fatal) << "omega2012MlParityRows must not be negative"; + } + if (doprocessMCTrue && (!std::isfinite(omega2012MlGenRapidityCut.value) || omega2012MlGenRapidityCut.value <= 0.f)) { + LOG(fatal) << "omega2012MlGenRapidityCut must be finite and positive when processMCTrue is enabled"; + } + if (omega2012MlExportStage.value != "loose" && omega2012MlExportStage.value != "selected") { + LOG(fatal) << "omega2012MlExportStage must be loose or selected"; + } + + ProcessModes modes; + modes.xiK0s = xiK0s; + modes.xi1530K = xi1530K; + modes.microTracks = xi1530K; + modes.mcReco = doprocessXiK0sMC || doprocessXi1530KMCMicro; + modes.mcGen = doprocessMCTrue; + LooseStageOptions looseOptions; + looseOptions.audit = omega2012MlLooseAudit; + looseOptions.exportSelected = omega2012MlExportStage.value == "selected"; + core.init(histos, eventCuts, xiCuts, k0sCuts, xi1530KCuts, trackCuts, pionPID, kaonPID, candidateCuts, modes, looseOptions); + + // Candidates that violate the canonical or feature contract are skipped, never written. + const std::array statusLabels{"Ok", "InvalidChargePattern", "ReusedDaughter", "InvalidMomentum", "InvalidKinematics", "InvalidContract"}; + if (xiK0s) { + auto skipped = histos.add("ML/xiK0s/exportSkipped", "XiK0s mode skipped candidates;build status;candidates", HistType::kTH1D, {{NBuildStatus, -0.5, NBuildStatus - 0.5}}); + for (std::size_t i = 0; i < statusLabels.size(); ++i) { + skipped->GetXaxis()->SetBinLabel(i + 1, statusLabels[i]); + } + } + if (xi1530K) { + auto skipped = histos.add("ML/xi1530K/exportSkipped", "Xi1530K mode skipped candidates;build status;candidates", HistType::kTH1D, {{NBuildStatus, -0.5, NBuildStatus - 0.5}}); + for (std::size_t i = 0; i < statusLabels.size(); ++i) { + skipped->GetXaxis()->SetBinLabel(i + 1, statusLabels[i]); + } + histos.add("ML/xi1530K/chargePattern", "Xi1530K mode candidates handed to the export;charge pattern (0 signal);candidates", HistType::kTH1D, {{4, -0.5, 3.5}}); + } + + LOG(info) << "Size of the histograms in Omega(2012) training table task"; + histos.print(); + } + + template + static uint64_t featureHash(std::array const& features) + { + uint64_t hash = FnvOffsetBasis; + for (const auto& value : features) { + const auto bits = std::bit_cast(value); + for (unsigned int shift = 0; shift < BitsPerFloat; shift += BitsPerByte) { + hash = (hash ^ ((bits >> shift) & ByteMask)) * FnvPrime; + } + } + return hash; + } + + template + void beginEvent(Collision const& collision, DecayMode mode) + { + mlCascadeRows.clear(); + mlV0Rows.clear(); + mlTrackRows.clear(); + mlEventRow = -1; + // ResoCollisions_001 carries no run number or BC; the reduced collision row identifies the event within its DF. + pendingEvent = PendingEvent{mode, static_cast(collision.globalIndex()), collision.posZ(), collision.bMagField(), + collision.cent(), collision.multiplicity(), collision.isRecINELgt0(), false, -1}; + if constexpr (requires { collision.originalMcCollisionId(); }) { + pendingEvent.isMC = true; + pendingEvent.mcCollision = static_cast(collision.originalMcCollisionId()); + } + } + + void ensureEventRow() + { + if (mlEventRow >= 0) { + return; + } + mlEvents(static_cast(pendingEvent.mode), pendingEvent.recoCollision, pendingEvent.posZ, pendingEvent.bField, + pendingEvent.centrality, pendingEvent.multiplicity, pendingEvent.recINELgt0); + mlEventRow = mlEvents.lastIndex(); + if (pendingEvent.isMC) { + mlEventMcs(mlEventRow, pendingEvent.mcCollision); + } + } + + template + void writeMcParticle(Particle const& particle, uint8_t selection) + { + const auto mothers = particle.mothersIds(); + std::array motherRows{-1, -1}; + for (std::size_t i = 0; i < NStoredMothers && i < mothers.size(); ++i) { + motherRows[i] = mothers[i]; + } + const auto daughters = particle.daughtersIds(); + std::array daughterRows{daughters[0], daughters[1]}; + const auto nMothers = static_cast(std::min(mothers.size(), std::numeric_limits::max())); + mlMcParticles(static_cast(particle.globalIndex()), selection, particle.pdgCode(), static_cast(particle.mcCollisionId()), + nMothers, motherRows.data(), daughterRows.data(), particle.px(), particle.py(), particle.pz(), particle.e(), particle.weight()); + } + + // McParticles row of a source track, -1 without MC tables or label + int mcLabel(int64_t trackId) const + { + if (mcTrackLabelsDf == nullptr) { + return -1; + } + const auto row = trackId - static_cast(mcTrackLabelsDf->offset()); + if (row < 0 || row >= mcTrackLabelsDf->size()) { + return -1; + } + return mcTrackLabelsDf->rawIteratorAt(row).mcParticleId(); + } + + // Writes the label particle and its first-mother chain, once per DF + void writeAncestry(int label) + { + if (mcParticlesDf == nullptr) { + return; + } + int64_t row = label; + for (int step = 0; step <= MaxAncestorSteps; ++step) { + const auto local = row - static_cast(mcParticlesDf->offset()); + if (row < 0 || local < 0 || local >= mcParticlesDf->size()) { + return; + } + const auto particle = mcParticlesDf->rawIteratorAt(local); + if (mlAncestryRows.insert(row).second) { + writeMcParticle(particle, McSelectionAncestry); + } + const auto mothers = particle.mothersIds(); + if (mothers.empty()) { + return; + } + row = mothers[0]; + } + } + + template + int64_t writeCascade(Cascade const& xi) + { + const auto id = static_cast(xi.globalIndex()); + if (auto it = mlCascadeRows.find(id); it != mlCascadeRows.end()) { + return it->second; + } + const auto& indices = xi.cascadeIndices(); + const std::array daughterIds{indices[0], indices[1], indices[2]}; + mlCascades(mlEventRow, id, xi.px(), xi.py(), xi.pz(), static_cast(xi.sign()), xi.mXi(), xi.mLambda(), + xi.v0CosPA(), xi.cascCosPA(), xi.daughDCA(), xi.cascDaughDCA(), + xi.dcapostopv(), xi.dcanegtopv(), xi.dcabachtopv(), xi.dcav0topv(), xi.dcaXYCascToPV(), xi.dcaZCascToPV(), + xi.transRadius(), xi.cascTransRadius(), xi.decayVtxX(), xi.decayVtxY(), xi.decayVtxZ(), + xi.daughterTPCNSigmaPosPi10(), xi.daughterTPCNSigmaPosKa10(), xi.daughterTPCNSigmaPosPr10(), + xi.daughterTPCNSigmaNegPi10(), xi.daughterTPCNSigmaNegKa10(), xi.daughterTPCNSigmaNegPr10(), + xi.daughterTPCNSigmaBachPi10(), xi.daughterTPCNSigmaBachKa10(), xi.daughterTPCNSigmaBachPr10(), + xi.daughterTOFNSigmaPosPi10(), xi.daughterTOFNSigmaPosKa10(), xi.daughterTOFNSigmaPosPr10(), + xi.daughterTOFNSigmaNegPi10(), xi.daughterTOFNSigmaNegKa10(), xi.daughterTOFNSigmaNegPr10(), + xi.daughterTOFNSigmaBachPi10(), xi.daughterTOFNSigmaBachKa10(), xi.daughterTOFNSigmaBachPr10(), + xi.nCrossedRowsPos(), xi.nCrossedRowsNeg(), xi.nCrossedRowsBach(), daughterIds.data()); + const auto row = mlCascades.lastIndex(); + mlCascadeRows.emplace(id, row); + if (mcTrackLabelsDf != nullptr) { + std::array labels{}; + for (std::size_t i = 0; i < labels.size(); ++i) { + labels[i] = mcLabel(daughterIds[i]); + writeAncestry(labels[i]); + } + mlCascadeMcs(row, labels.data()); + } + return row; + } + + template + int64_t writeV0(V0 const& v0) + { + const auto id = static_cast(v0.globalIndex()); + if (auto it = mlV0Rows.find(id); it != mlV0Rows.end()) { + return it->second; + } + const auto& indices = v0.indices(); + const std::array daughterIds{indices[0], indices[1]}; + mlV0s(mlEventRow, id, v0.px(), v0.py(), v0.pz(), v0.mK0Short(), v0.mLambda(), v0.mAntiLambda(), + v0.v0CosPA(), v0.daughDCA(), v0.dcapostopv(), v0.dcanegtopv(), v0.dcav0topv(), v0.transRadius(), + v0.decayVtxX(), v0.decayVtxY(), v0.decayVtxZ(), v0.alpha(), v0.qtarm(), + v0.daughterTPCNSigmaPosPi10(), v0.daughterTPCNSigmaPosKa10(), v0.daughterTPCNSigmaPosPr10(), + v0.daughterTPCNSigmaNegPi10(), v0.daughterTPCNSigmaNegKa10(), v0.daughterTPCNSigmaNegPr10(), + v0.daughterTOFNSigmaPosPi10(), v0.daughterTOFNSigmaPosKa10(), v0.daughterTOFNSigmaPosPr10(), + v0.daughterTOFNSigmaNegPi10(), v0.daughterTOFNSigmaNegKa10(), v0.daughterTOFNSigmaNegPr10(), + v0.nCrossedRowsPos(), v0.nCrossedRowsNeg(), daughterIds.data()); + const auto row = mlV0s.lastIndex(); + mlV0Rows.emplace(id, row); + if (mcTrackLabelsDf != nullptr) { + std::array labels{}; + for (std::size_t i = 0; i < labels.size(); ++i) { + labels[i] = mcLabel(daughterIds[i]); + writeAncestry(labels[i]); + } + mlV0Mcs(row, labels.data()); + } + return row; + } + + template + int64_t writeTrack(Track const& track) + { + const auto id = static_cast(track.globalIndex()); + if (auto it = mlTrackRows.find(id); it != mlTrackRows.end()) { + return it->second; + } + mlTracks(mlEventRow, static_cast(track.trackId()), track.px(), track.py(), track.pz(), + track.pidNSigmaPiFlag(), track.pidNSigmaKaFlag(), track.pidNSigmaPrFlag(), + track.trackSelectionFlags(), track.trackFlags(), track.tpcNClsCrossedRows(), track.itsClusterMap()); + const auto row = mlTracks.lastIndex(); + mlTrackRows.emplace(id, row); + if (mcTrackLabelsDf != nullptr) { + const auto label = mcLabel(static_cast(track.trackId())); + writeAncestry(label); + mlTrackMcs(row, label); + } + return row; + } + + template + void writeXiK0sCandidate(Collision const& collision, Cascade const& xi, V0 const& v0, XiK0sCandidateValues const& values, uint16_t passBits) + { + const auto canonical = ml::canonicalizeXiK0s(ml::makeCascadeSnapshot(collision, xi), ml::makeV0Snapshot(collision, v0)); + if (canonical.status != ml::BuildStatus::Ok) { + histos.fill(HIST("ML/xiK0s/exportSkipped"), static_cast(canonical.status)); + return; + } + const auto pack = ml::buildXiK0sFeatures(canonical.candidate); + if (pack.status != ml::BuildStatus::Ok) { + histos.fill(HIST("ML/xiK0s/exportSkipped"), static_cast(pack.status)); + return; + } + if (parityLoggedXiK0s < omega2012MlParityRows) { + LOGP(info, "OMEGA2012MLPARITY mode=XiK0s collision={} cascade={} v0={} passBits={} featureHash={}", + collision.globalIndex(), xi.globalIndex(), v0.globalIndex(), passBits, featureHash(pack.master)); + ++parityLoggedXiK0s; + } + ensureEventRow(); + const auto cascadeRow = writeCascade(xi); + const auto v0Row = writeV0(v0); + mlXiK0sCandidates(mlEventRow, cascadeRow, v0Row, + static_cast(values.omega.M()), static_cast(values.omega.Pt()), + static_cast(values.omega.Rapidity()), static_cast(values.omega.Eta()), + static_cast(values.omega.Phi()), values.alpha, static_cast(xi.sign()), passBits); + const auto row = mlXiK0sCandidates.lastIndex(); + mlXiK0sInputs(row, pack.master.data(), static_cast(pack.status)); + if constexpr (IsMC) { + const bool matched = classifyXiK0sTruth(xi, v0) == XiK0sTruth::Matched; + mlXiK0sTruth(row, matched ? uint8_t{1} : uint8_t{2}, matched ? xi.motherPDG() : 0, + matched ? static_cast(xi.motherId()) : int64_t{-1}, v0.motherPDG(), xi.motherPDG()); + } else { + mlXiK0sTruth(row, uint8_t{0}, 0, int64_t{-1}, 0, 0); + } + } + + template + void writeXi1530KCandidate(Collision const& collision, Cascade const& xi, Track const& pion, Track const& kaon, + Xi1530KCandidateValues const& values, uint16_t passBits) + { + histos.fill(HIST("ML/xi1530K/chargePattern"), values.chargePattern); + if (values.chargePattern != ml::kSignalPattern && !omega2012MlExportWrongSign) { + return; // charge-pattern controls are exported only on request + } + const auto canonical = ml::canonicalizeXi1530K(ml::makeCascadeSnapshot(collision, xi), ml::makeTrackSnapshot(pion), ml::makeTrackSnapshot(kaon)); + if (canonical.status != ml::BuildStatus::Ok) { + histos.fill(HIST("ML/xi1530K/exportSkipped"), static_cast(canonical.status)); + return; + } + const auto pack = ml::buildXi1530KFeatures(canonical.candidate); + if (pack.status != ml::BuildStatus::Ok) { + histos.fill(HIST("ML/xi1530K/exportSkipped"), static_cast(pack.status)); + return; + } + if (parityLoggedXi1530K < omega2012MlParityRows) { + LOGP(info, "OMEGA2012MLPARITY mode=Xi1530K collision={} cascade={} tracks={},{} chargePattern={} passBits={} featureHash={}", + collision.globalIndex(), xi.globalIndex(), canonical.candidate.pion.sourceTrackId, canonical.candidate.kaon.sourceTrackId, + values.chargePattern, passBits, featureHash(pack.master)); + ++parityLoggedXi1530K; + } + ensureEventRow(); + const auto cascadeRow = writeCascade(xi); + const auto pionRow = writeTrack(pion); + const auto kaonRow = writeTrack(kaon); + mlXi1530KCandidates(mlEventRow, cascadeRow, pionRow, kaonRow, + static_cast(values.omega.M()), values.massXiPi, values.massXiK, values.massPiK, + static_cast(values.omega.Pt()), static_cast(values.omega.Rapidity()), + static_cast(values.omega.Eta()), static_cast(values.omega.Phi()), + values.chargePattern, passBits); + const auto row = mlXi1530KCandidates.lastIndex(); + mlXi1530KInputs(row, pack.master.data(), static_cast(pack.status)); + if constexpr (IsMC) { + const bool matched = classifyXi1530KTruth(xi, pion, kaon) == Xi1530KTruth::Matched; + mlXi1530KTruth(row, matched ? uint8_t{1} : uint8_t{2}, matched ? kaon.motherPDG() : 0, + matched ? static_cast(kaon.motherId()) : int64_t{-1}, static_cast(xi.motherId())); + } else { + mlXi1530KTruth(row, uint8_t{0}, 0, int64_t{-1}, int64_t{-1}); + } + } + + template + void exportXiK0s(Collision const& collision, Cascades const& cascades, V0s const& v0s) + { + beginEvent(collision, DecayMode::XiK0s); + // Selection only: the analysis histograms belong to the Omega(2012) analysis task + core.forEachXiK0sCandidate(histos, collision, collision, cascades, v0s, true, nullptr, nullptr, nullptr, + [&](auto const& coll, auto const& xi, auto const& v0, XiK0sCandidateValues const& values, uint16_t passBits) { + writeXiK0sCandidate(coll, xi, v0, values, passBits); + }); + } + + template + void exportXi1530K(Collision const& collision, Cascades const& cascades, Tracks const& tracks) + { + beginEvent(collision, DecayMode::Xi1530K); + core.forEachXi1530KCandidate(histos, collision, cascades, tracks, nullptr, true, nullptr, nullptr, nullptr, + [&](auto const& coll, auto const& xi, auto const& pion, auto const& kaon, + Xi1530KCandidateValues const& values, uint16_t passBits) { + writeXi1530KCandidate(coll, xi, pion, kaon, values, passBits); + }); + } + + void processXiK0s(ResoCollisions::iterator const& collision, aod::ResoCascades const& cascades, aod::ResoV0s const& v0s) + { + if (!core.passesEventCuts(collision)) { + return; + } + exportXiK0s(collision, cascades, v0s); + } + PROCESS_SWITCH(Omega2012TrainingTable, processXiK0s, "Write XiK0s-mode candidates from data", true); + + void processXiK0sMC(ResoMCCols::iterator const& collision, ResoMCCascades const& cascades, ResoMCV0s const& v0s, + aod::McParticles const& mcParticles, aod::McTrackLabels const& mcTrackLabels) + { + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; + } + mcParticlesDf = &mcParticles; + mcTrackLabelsDf = &mcTrackLabels; + exportXiK0s(collision, cascades, v0s); + // cppcheck-suppress redundantAssignment + mcParticlesDf = nullptr; + // cppcheck-suppress redundantAssignment + mcTrackLabelsDf = nullptr; + } + PROCESS_SWITCH(Omega2012TrainingTable, processXiK0sMC, "Write XiK0s-mode candidates with truth from reconstructed MC", false); + + void processXi1530KMicro(ResoCollisions::iterator const& collision, aod::ResoCascades const& cascades, ResoMicroTracks const& tracks) + { + if (!core.passesEventCuts(collision)) { + return; + } + exportXi1530K(collision, cascades, tracks); + } + PROCESS_SWITCH(Omega2012TrainingTable, processXi1530KMicro, "Write Xi1530K-mode candidates from data micro v001 tables", false); + + void processXi1530KMCMicro(ResoMCCols::iterator const& collision, ResoMCCascades const& cascades, ResoMCMicroTracks const& tracks, + aod::McParticles const& mcParticles, aod::McTrackLabels const& mcTrackLabels) + { + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; + } + mcParticlesDf = &mcParticles; + mcTrackLabelsDf = &mcTrackLabels; + exportXi1530K(collision, cascades, tracks); + // cppcheck-suppress redundantAssignment + mcParticlesDf = nullptr; + // cppcheck-suppress redundantAssignment + mcTrackLabelsDf = nullptr; + } + PROCESS_SWITCH(Omega2012TrainingTable, processXi1530KMCMicro, "Write Xi1530K-mode candidates with truth from reconstructed MC micro v001 tables", false); + + void processMCTrue(ResoMCCols::iterator const& collision, ResoMCParents const& resoParents) + { + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; + } + core.forEachGeneratedOmega2012(histos, resoParents, omega2012MlGenRapidityCut.value, [&](auto const& part, GeneratedChannel channel) { + mlGenAudit(collision.globalIndex(), static_cast(part.originalMcParticleId()), + part.pdgCode(), part.daughterPDG1(), part.daughterPDG2(), static_cast(channel), part.pt(), part.y()); + }); + } + PROCESS_SWITCH(Omega2012TrainingTable, processMCTrue, "Write generated Omega(2012) parents of selected reconstructed MC events", false); + + // Once per DF: every Omega(2012) with McTreeDepth generations of descendants. The ancestry set is reset here, so + // the result does not depend on the order of this call relative to the per-collision process functions. + void processMcTruth(aod::McParticles const& mcParticles) + { + mlAncestryRows.clear(); + const auto offset = static_cast(mcParticles.offset()); + const auto nRows = mcParticles.size(); + std::unordered_set treeRows; + std::vector> stack; + for (const auto& parent : mcParticles) { + if (std::abs(parent.pdgCode()) != o2::constants::physics::Pdg::kOmega2012Minus) { + continue; + } + stack.assign(1, {parent.globalIndex(), 0}); + while (!stack.empty()) { + const auto [row, depth] = stack.back(); + stack.pop_back(); + const auto local = row - offset; + if (local < 0 || local >= nRows || !treeRows.insert(row).second) { + continue; + } + const auto particle = mcParticles.rawIteratorAt(local); + writeMcParticle(particle, McSelectionTree); + const auto daughters = particle.daughtersIds(); + if (depth >= McTreeDepth || daughters[0] < 0 || daughters[1] < daughters[0]) { + continue; + } + for (int64_t daughter = daughters[0]; daughter <= daughters[1]; ++daughter) { + stack.emplace_back(daughter, depth + 1); + } + } + } + } + PROCESS_SWITCH(Omega2012TrainingTable, processMcTruth, "Write the pruned McParticles rows of the Omega(2012) decay trees; required with the reconstructed-MC process functions", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/Tasks/Resonances/phi1020SpherocityAnalysis.cxx b/PWGLF/Tasks/Resonances/phi1020SpherocityAnalysis.cxx index d1aae772d1b..29f2a75f55f 100644 --- a/PWGLF/Tasks/Resonances/phi1020SpherocityAnalysis.cxx +++ b/PWGLF/Tasks/Resonances/phi1020SpherocityAnalysis.cxx @@ -453,8 +453,8 @@ struct Phi1020SpherocityAnalysis { void processMix(ResoCols const& collisions, ResoTracks const& tracks) { LOGF(debug, "Event Mixing Started"); - BinningType1 binningPositions1{{mixVtxBins, mixMultBins, mixSphBins}, true}; - BinningType2 binningPositions2{{mixVtxBins, mixMultBins}, true}; + BinningType1 binningPositions1{{mixVtxBins, mixMultBins, mixSphBins}}; + BinningType2 binningPositions2{{mixVtxBins, mixMultBins}}; auto tracksTuple = std::make_tuple(tracks); if (mixSph) { SameKindPair pairs{binningPositions1, numMixEv, -1, collisions, tracksTuple, &cache}; diff --git a/PWGLF/Tasks/Resonances/phi1020analysis.cxx b/PWGLF/Tasks/Resonances/phi1020analysis.cxx index 9aee9ebd3e1..672e84c15b7 100644 --- a/PWGLF/Tasks/Resonances/phi1020analysis.cxx +++ b/PWGLF/Tasks/Resonances/phi1020analysis.cxx @@ -1028,7 +1028,7 @@ struct Phi1020analysis { { auto tracksTuple = std::make_tuple(tracks); - BinningTypeVtxZT0M colBinning{{configBkg.cfgVtxBins, configBkg.cfgMultPercentileBins}, true}; + BinningTypeVtxZT0M colBinning{{configBkg.cfgVtxBins, configBkg.cfgMultPercentileBins}}; SameKindPair pairs{colBinning, configBkg.nEvtMixing, -1, collision, tracksTuple, &cache}; // -1 is the number of the bin to skip for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { diff --git a/PWGLF/Tasks/Resonances/phianalysis.cxx b/PWGLF/Tasks/Resonances/phianalysis.cxx index 43a2c49027a..1363e182fa6 100644 --- a/PWGLF/Tasks/Resonances/phianalysis.cxx +++ b/PWGLF/Tasks/Resonances/phianalysis.cxx @@ -328,7 +328,7 @@ struct phianalysis { void processMELight(o2::aod::ResoCollisions& collisions, aod::ResoTracks const& resotracks) { auto tracksTuple = std::make_tuple(resotracks); - BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}}; SameKindPair pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto& [collision1, tracks1, collision2, tracks2] : pairs) { diff --git a/PWGLF/Tasks/Resonances/phianalysisTHnSparse.cxx b/PWGLF/Tasks/Resonances/phianalysisTHnSparse.cxx index 85a1bcdb45e..04bfa017916 100644 --- a/PWGLF/Tasks/Resonances/phianalysisTHnSparse.cxx +++ b/PWGLF/Tasks/Resonances/phianalysisTHnSparse.cxx @@ -18,6 +18,7 @@ #include "PWGLF/Utils/rsnOutput.h" #include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" @@ -62,6 +63,7 @@ using namespace o2; using namespace o2::analysis; using namespace o2::framework; using namespace o2::framework::expressions; +using namespace o2::aod::rctsel; struct PhianalysisTHnSparse { @@ -98,11 +100,15 @@ struct PhianalysisTHnSparse { Configurable etatrack{"etatrack", 1.0f, "Cut: Maximal value of tracks eta."}; Configurable dcaXY{"dcaXY", 1.0f, "Cut: Maximal value of tracks DCA XY."}; Configurable dcaZ{"dcaZ", 1.0f, "Cut: Maximal value of tracks DCA Z."}; + Configurable ptDependentDCA{"ptDependentDCA", false, "Use pT dependent DCA cut according to formula dcaXY = n_sigma * (0.0105 + 0.0350 * pow(pt, -1.1))."}; + Configurable dcaNSigma{"dcaNSigma", 7.0f, "Cut: Maximal value of tracks DCA NSigma."}; Configurable tpcnSigmaPos{"tpcnSigmaPos", 10.0f, "Cut: Maximal value of TPC NSigma of the positive particle."}; Configurable tpcnSigmaNeg{"tpcnSigmaNeg", 10.0f, "Cut: Maximal value of TPC NSigma of the negative particle."}; Configurable tpcPidOnly{"tpcPidOnly", false, "Use TPC only for PID."}; Configurable combinedNSigma{"combinedNSigma", 3.0f, "Cut: Maximal value of NSigma for combined TPC and TOF NSigma cut."}; Configurable ptTOFThreshold{"ptTOFThreshold", 0.5f, "Cut: Minimal value of tracks pt for using TOF PID."}; + Configurable tofNSigmaPosShift{"tofNSigmaPosShift", 0.0f, "Shift of TOF NSigma of the positive particle."}; + Configurable tofNSigmaNegShift{"tofNSigmaNegShift", 0.0f, "Shift of TOF NSigma of the negative particle."}; Configurable tpcNClsFound{"tpcNClsFound", 0, "Cut: Minimal value of found TPC clusters"}; Configurable tpcNClsCrossedRows{"tpcNClsCrossedRows", 0, "Cut: Minimal value of crossed rows in TPC"}; Configurable globalTrack{"globalTrack", false, "Use isGlobalTrack track selection."}; @@ -111,6 +117,17 @@ struct PhianalysisTHnSparse { Configurable rapidity{"rapidity", 0.5f, "Cut: Maximal value of particle rapidity."}; } trackCuts; + struct : ConfigurableGroup { + Configurable bcBorderCutsGen{"bcBorderCutsGen", false, "Use BC TF/ITS ROF border cuts on Gen events."}; + Configurable rctCheckGen{"rctCheckGen", false, "Use RCT flag cut on Gen events."}; + Configurable rctCheckerLabel{"rctCheckerLabel", "CBT_hadronPID", "RCT flag checker label"}; + Configurable rctCheckerZDC{"rctCheckerZDC", false, "RCT flag checker ZDC check"}; + Configurable rctCheckerLimitAcceptAsBad{"rctCheckerLimitAcceptAsBad", false, "RCT flag checker treat Limited Acceptance As Bad"}; + + } mcCuts; + + RCTFlagsChecker rctChecker; + Configurable> sparseAxes{"sparseAxes", std::vector{o2::analysis::rsn::pair_axis::names}, "Axes."}; Configurable> sysAxes{"sysAxes", std::vector{o2::analysis::rsn::systematic_axis::names}, "Axes."}; @@ -148,8 +165,8 @@ struct PhianalysisTHnSparse { // Axes specifications AxisSpec vzQAaxis = {200, -20., 20., "V_{z} (cm)"}; - AxisSpec dcaXYQAaxis = {200, -0.5, 0.5, "DCA_{xy} (cm)"}; - AxisSpec dcaZQAaxis = {200, -0.5, 0.5, "DCA_{z} (cm)"}; + AxisSpec dcaXYQAaxis = {2000, -0.2, 0.2, "DCA_{xy} (cm)"}; + AxisSpec dcaZQAaxis = {2000, -0.2, 0.2, "DCA_{z} (cm)"}; AxisSpec etaQAaxis = {200, -1.0, 1.0, "#eta"}; AxisSpec rapidityQAaxis = {200, -1.0, 1.0, "y"}; AxisSpec tpcNClsQAaxis = {200, 0., 200., "TPC NClusters"}; @@ -238,6 +255,8 @@ struct PhianalysisTHnSparse { rsnOutput = new o2::analysis::rsn::OutputSparse(); rsnOutput->init(sparseAxes, allAxes, sysAxes, allAxesSys, static_cast(produce.produceMC), mixingType, static_cast(produce.produceLikesign), static_cast(produce.produceRotational), static_cast(eventCuts.inelGt0), ®istry); + rctChecker.init(mcCuts.rctCheckerLabel, mcCuts.rctCheckerZDC, mcCuts.rctCheckerLimitAcceptAsBad, mcCuts.rctCheckGen); + // Print summary of configuration LOGF(info, "=== PhianalysisTHnSparse configuration summary ==="); LOGF(info, "produceMC: %s", static_cast(produce.produceMC) ? "true" : "false"); @@ -261,6 +280,8 @@ struct PhianalysisTHnSparse { LOGF(info, "tpcPidOnly: %s", static_cast(trackCuts.tpcPidOnly) ? "true" : "false"); LOGF(info, "combinedNSigma: %.2f", static_cast(trackCuts.combinedNSigma)); LOGF(info, "ptTOFThreshold: %.2f", static_cast(trackCuts.ptTOFThreshold)); + LOGF(info, "tofNSigmaPosShift: %.2f", static_cast(trackCuts.tofNSigmaPosShift)); + LOGF(info, "tofNSigmaNegShift: %.2f", static_cast(trackCuts.tofNSigmaNegShift)); LOGF(info, "tpcNClsFound: %d", static_cast(trackCuts.tpcNClsFound)); LOGF(info, "tpcNClsCrossedRows: %d", static_cast(trackCuts.tpcNClsCrossedRows)); LOGF(info, "globalTrack: %s", static_cast(trackCuts.globalTrack) ? "true" : "false"); @@ -446,24 +467,28 @@ struct PhianalysisTHnSparse { // ----------------------- MC Corrections ----------------------- - registry.add("QAMC/Gen/hNEvents", "Number of MC Gen Events", kTH1F, {{6, 0.0f, 6.0f}}); + registry.add("QAMC/Gen/hNEvents", "Number of MC Gen Events", kTH1F, {{8, 0.0f, 8.0f}}); auto hNEventsGen = registry.get(HIST("QAMC/Gen/hNEvents")); - hNEventsGen->GetXaxis()->SetBinLabel(1, "all"); // All generated events - hNEventsGen->GetXaxis()->SetBinLabel(2, "V_z cut"); // Generated events passing Vz cut - hNEventsGen->GetXaxis()->SetBinLabel(3, "INEL"); // Generated events passing INEL cut - hNEventsGen->GetXaxis()->SetBinLabel(4, "INEL>0"); // Passing INELgt0 cut --> EL numerator for INEL>0 - hNEventsGen->GetXaxis()->SetBinLabel(5, "ALORE INEL"); // Passing ALORE INEL cut - hNEventsGen->GetXaxis()->SetBinLabel(6, "ALORE INEL>0"); // Passing ALORE INEL>0 cut + hNEventsGen->GetXaxis()->SetBinLabel(1, "all"); // All generated events + hNEventsGen->GetXaxis()->SetBinLabel(2, "V_z cut"); // Generated events passing Vz cut + hNEventsGen->GetXaxis()->SetBinLabel(3, "BC TF/ITS ROF border"); // Passing BC TF/ITS ROF border cut + hNEventsGen->GetXaxis()->SetBinLabel(4, "RCTFlagsChecker"); // Passing RCTFlagsChecker cut + hNEventsGen->GetXaxis()->SetBinLabel(5, "INEL"); // Generated events passing INEL cut + hNEventsGen->GetXaxis()->SetBinLabel(6, "INEL>0"); // Passing INELgt0 cut --> EL numerator for INEL>0 + hNEventsGen->GetXaxis()->SetBinLabel(7, "ALORE INEL"); // Passing ALORE INEL cut + hNEventsGen->GetXaxis()->SetBinLabel(8, "ALORE INEL>0"); // Passing ALORE INEL>0 cut hNEventsGen->SetMinimum(0.1); - registry.add("QAMC/Gen/hNEventsCent", "Generated events", HistType::kTH2F, {centQAAxis, {6, 0, 6}}); + registry.add("QAMC/Gen/hNEventsCent", "Generated events", HistType::kTH2F, {centQAAxis, {8, 0, 8}}); auto hNEventsGenCent = registry.get(HIST("QAMC/Gen/hNEventsCent")); hNEventsGenCent->GetYaxis()->SetBinLabel(1, "all"); hNEventsGenCent->GetYaxis()->SetBinLabel(2, "V_z cut"); - hNEventsGenCent->GetYaxis()->SetBinLabel(3, "INEL"); - hNEventsGenCent->GetYaxis()->SetBinLabel(4, "INEL>0"); - hNEventsGenCent->GetYaxis()->SetBinLabel(5, "ALORE INEL"); - hNEventsGenCent->GetYaxis()->SetBinLabel(6, "ALORE INEL>0"); + hNEventsGenCent->GetYaxis()->SetBinLabel(3, "BC TF/ITS ROF border"); + hNEventsGenCent->GetYaxis()->SetBinLabel(4, "RCTFlagsChecker"); + hNEventsGenCent->GetYaxis()->SetBinLabel(5, "INEL"); + hNEventsGenCent->GetYaxis()->SetBinLabel(6, "INEL>0"); + hNEventsGenCent->GetYaxis()->SetBinLabel(7, "ALORE INEL"); + hNEventsGenCent->GetYaxis()->SetBinLabel(8, "ALORE INEL>0"); registry.add("QAMC/Rec/hNEvents", "Number of MC Rec Events", kTH1F, {{3, 0.0f, 3.0f}}); auto hNEventsRec = registry.get(HIST("QAMC/Rec/hNEvents")); @@ -600,13 +625,14 @@ struct PhianalysisTHnSparse { { float tofNsigma = 0.0f; int particleType = (track.sign() > 0) ? static_cast(daughterPos) : static_cast(daughterNeg); + float tofNSigmaShift = (track.sign() > 0) ? static_cast(trackCuts.tofNSigmaPosShift) : static_cast(trackCuts.tofNSigmaNegShift); if (particleType == pion) { - tofNsigma = track.tofNSigmaPi(); + tofNsigma = track.tofNSigmaPi() + tofNSigmaShift; } else if (particleType == kaon) { - tofNsigma = track.tofNSigmaKa(); + tofNsigma = track.tofNSigmaKa() + tofNSigmaShift; } else if (particleType == proton) { - tofNsigma = track.tofNSigmaPr(); + tofNsigma = track.tofNSigmaPr() + tofNSigmaShift; } return tofNsigma; } @@ -631,7 +657,13 @@ struct PhianalysisTHnSparse { registry.fill(HIST("QA/Track/hSelection"), 2.5); } - if (std::abs(track.dcaXY()) >= static_cast(trackCuts.dcaXY) || + float dcaXY = static_cast(trackCuts.dcaXY); + if (static_cast(trackCuts.ptDependentDCA)) { + double dcaNSigma = static_cast(trackCuts.dcaNSigma); + dcaXY = (dcaNSigma / 7) * (0.0105 + 0.0350 / std::pow(track.pt(), 1.1)); + } + + if (std::abs(track.dcaXY()) >= dcaXY || std::abs(track.dcaZ()) >= static_cast(trackCuts.dcaZ)) { return false; } @@ -731,6 +763,30 @@ struct PhianalysisTHnSparse { return pointPair; } + template + bool selectedGenEventBcBorderCuts(T const& bc) + { + if (!static_cast(mcCuts.bcBorderCutsGen)) { + return true; + } + if (static_cast(eventCuts.noTimeFrameBorder) && !bc.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + if (static_cast(eventCuts.noITSROFrameBorder) && !bc.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + return true; + } + + template + bool selectedGenEventRCT(TBC const& bc) + { + if (!static_cast(mcCuts.rctCheckGen)) { + return true; + } + return !static_cast(mcCuts.rctCheckGen) || rctChecker(bc); + } + void processQA(EventCandidate const& collision, TrackCandidates const& tracks) { if (!static_cast(produce.produceQA)) { @@ -1365,10 +1421,10 @@ struct PhianalysisTHnSparse { auto tracksTuple = std::make_tuple(tracks); - BinningTypeVzCe binningVzCe{{axisVertexMixing, axisCentralityMixing}, true}; + BinningTypeVzCe binningVzCe{{axisVertexMixing, axisCentralityMixing}}; SameKindPair pairVzCe{binningVzCe, static_cast(nMixedEvents), -1, collisions, tracksTuple, &cacheME}; - BinningTypeVzMu binningVzMu{{axisVertexMixing, axisMultiplicityMixing}, true}; + BinningTypeVzMu binningVzMu{{axisVertexMixing, axisMultiplicityMixing}}; SameKindPair pairVzMu{binningVzMu, static_cast(nMixedEvents), -1, collisions, tracksTuple, &cacheME}; if (mixingType == rsn::MixingType::ce) { @@ -1569,14 +1625,29 @@ struct PhianalysisTHnSparse { registry.fill(HIST("QAMC/Gen/hNEvents"), 1.5); registry.fill(HIST("QAMC/Gen/hNEventsCent"), mcCollision.centFT0M(), 1.5); + // Accepted generated border cuts + const auto bc = mcCollision.bc_as>(); + if (!selectedGenEventBcBorderCuts(bc)) { + return; + } registry.fill(HIST("QAMC/Gen/hNEvents"), 2.5); registry.fill(HIST("QAMC/Gen/hNEventsCent"), mcCollision.centFT0M(), 2.5); + // Accepted generated event RCT flags + if (!selectedGenEventRCT(bc)) { + return; + } + registry.fill(HIST("QAMC/Gen/hNEvents"), 3.5); + registry.fill(HIST("QAMC/Gen/hNEventsCent"), mcCollision.centFT0M(), 3.5); + + registry.fill(HIST("QAMC/Gen/hNEvents"), 4.5); + registry.fill(HIST("QAMC/Gen/hNEventsCent"), mcCollision.centFT0M(), 4.5); + bool isINELgt0 = false; if (pwglf::isINELgtNmc(mcParticles, 0, pdg)) { isINELgt0 = true; - registry.fill(HIST("QAMC/Gen/hNEvents"), 3.5); - registry.fill(HIST("QAMC/Gen/hNEventsCent"), mcCollision.centFT0M(), 3.5); // Event Loss Denominator + registry.fill(HIST("QAMC/Gen/hNEvents"), 5.5); + registry.fill(HIST("QAMC/Gen/hNEventsCent"), mcCollision.centFT0M(), 5.5); // Event Loss Denominator } for (const auto& mcParticle : mcParticles) { @@ -1641,12 +1712,12 @@ struct PhianalysisTHnSparse { return; } - registry.fill(HIST("QAMC/Gen/hNEvents"), 4.5); - registry.fill(HIST("QAMC/Gen/hNEventsCent"), mcCollision.centFT0M(), 4.5); + registry.fill(HIST("QAMC/Gen/hNEvents"), 6.5); + registry.fill(HIST("QAMC/Gen/hNEventsCent"), mcCollision.centFT0M(), 6.5); if (nRecoInelGt0Coll > 0) { - registry.fill(HIST("QAMC/Gen/hNEvents"), 5.5); - registry.fill(HIST("QAMC/Gen/hNEventsCent"), mcCollision.centFT0M(), 5.5); // Event Loss Numerator + registry.fill(HIST("QAMC/Gen/hNEvents"), 7.5); + registry.fill(HIST("QAMC/Gen/hNEventsCent"), mcCollision.centFT0M(), 7.5); // Event Loss Numerator } for (const auto& mcParticle : mcParticles) { diff --git a/PWGLF/Tasks/Resonances/phianalysisrun3.cxx b/PWGLF/Tasks/Resonances/phianalysisrun3.cxx index 2d0218171ae..084f4a5aae4 100644 --- a/PWGLF/Tasks/Resonances/phianalysisrun3.cxx +++ b/PWGLF/Tasks/Resonances/phianalysisrun3.cxx @@ -241,14 +241,14 @@ struct phianalysisrun3 { ConfigurableAxis axisMultiplicity{"axisMultiplicity", {2000, 0, 10000}, "TPC multiplicity for bin"}; // using BinningType = BinningPolicy>; - // BinningType binningOnPositions{{axisVertex, axisMultiplicityClass}, true}; + // BinningType binningOnPositions{{axisVertex, axisMultiplicityClass}}; // using BinningTypeTPCMultiplicity = ColumnBinningPolicy; using BinningTypeVertexContributor = ColumnBinningPolicy; // using BinningTypeCentrality = ColumnBinningPolicy; // using BinningType = ColumnBinningPolicy; - // BinningType binningOnPositions{{axisVertex, axisMultiplicity}, true}; + // BinningType binningOnPositions{{axisVertex, axisMultiplicity}}; void processSameEvent(EventCandidates::iterator const& collision, TrackCandidates const& tracks, aod::BCs const&) { @@ -306,7 +306,7 @@ struct phianalysisrun3 { { auto tracksTuple = std::make_tuple(tracks); //////// currently mixing the event with similar TPC multiplicity //////// - BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicity}, true}; + BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicity}}; SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (auto& [c1, tracks1, c2, tracks2] : pair) { if (applyEvsel && !c1.sel8()) { diff --git a/PWGLF/Tasks/Resonances/phianalysisrun3pbpb.cxx b/PWGLF/Tasks/Resonances/phianalysisrun3pbpb.cxx index 26184b5f79f..c73ae59c466 100644 --- a/PWGLF/Tasks/Resonances/phianalysisrun3pbpb.cxx +++ b/PWGLF/Tasks/Resonances/phianalysisrun3pbpb.cxx @@ -722,7 +722,7 @@ struct Phianalysisrun3pbpb { const int kCentFT0M = 2; const int kCentFV0A = 3; if (centestimator == kCentFT0C) { - BinningTypeVertexContributor1 binningOnPositions{{axisVertex, axisMultiplicity}, true}; + BinningTypeVertexContributor1 binningOnPositions{{axisVertex, axisMultiplicity}}; SameKindPair pair{binningOnPositions, selectionConfig.cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (const auto& [c1, tracks1, c2, tracks2] : pair) { if (rctCut.requireRCTFlagChecker && (!rctChecker(c1) || !rctChecker(c2))) { @@ -734,7 +734,7 @@ struct Phianalysisrun3pbpb { fillMixedPairs(tracks1, tracks2, c1.centFT0C()); } } else if (centestimator == kCentFT0A) { - BinningTypeVertexContributor2 binningOnPositions{{axisVertex, axisMultiplicity}, true}; + BinningTypeVertexContributor2 binningOnPositions{{axisVertex, axisMultiplicity}}; SameKindPair pair{binningOnPositions, selectionConfig.cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (const auto& [c1, tracks1, c2, tracks2] : pair) { if (rctCut.requireRCTFlagChecker && (!rctChecker(c1) || !rctChecker(c2))) { @@ -746,7 +746,7 @@ struct Phianalysisrun3pbpb { fillMixedPairs(tracks1, tracks2, c1.centFT0A()); } } else if (centestimator == kCentFT0M) { - BinningTypeVertexContributor3 binningOnPositions{{axisVertex, axisMultiplicity}, true}; + BinningTypeVertexContributor3 binningOnPositions{{axisVertex, axisMultiplicity}}; SameKindPair pair{binningOnPositions, selectionConfig.cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (const auto& [c1, tracks1, c2, tracks2] : pair) { if (rctCut.requireRCTFlagChecker && (!rctChecker(c1) || !rctChecker(c2))) { @@ -758,7 +758,7 @@ struct Phianalysisrun3pbpb { fillMixedPairs(tracks1, tracks2, c1.centFT0M()); } } else if (centestimator == kCentFV0A) { - BinningTypeVertexContributor4 binningOnPositions{{axisVertex, axisMultiplicity}, true}; + BinningTypeVertexContributor4 binningOnPositions{{axisVertex, axisMultiplicity}}; SameKindPair pair{binningOnPositions, selectionConfig.cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (const auto& [c1, tracks1, c2, tracks2] : pair) { if (rctCut.requireRCTFlagChecker && (!rctChecker(c1) || !rctChecker(c2))) { @@ -1236,7 +1236,7 @@ struct Phianalysisrun3pbpb { { auto tracksTuple = std::make_tuple(RecTracks); - BinningTypeVertexContributor1 binningOnPositions{{axisVertex, axisMultiplicity}, true}; + BinningTypeVertexContributor1 binningOnPositions{{axisVertex, axisMultiplicity}}; SameKindPair pairs{binningOnPositions, selectionConfig.cfgNoMixedEvents, -1, recCollisions, tracksTuple, &cache}; for (const auto& [c1, tracks1, c2, tracks2] : pairs) { diff --git a/PWGLF/Tasks/Resonances/phiflowder.cxx b/PWGLF/Tasks/Resonances/phiflowder.cxx index a76e1dc7fdc..8a855a5c1c2 100644 --- a/PWGLF/Tasks/Resonances/phiflowder.cxx +++ b/PWGLF/Tasks/Resonances/phiflowder.cxx @@ -430,12 +430,12 @@ struct phiflowder { // Processing Event Mixing using BinningType = ColumnBinningPolicy; - BinningType colBinning{{cfgVtxBins, cfgCentBins}, true}; + BinningType colBinning{{cfgVtxBins, cfgCentBins}}; void processMixedData(EventCandidates const& collisions, aod::KaonTracks const& /*kaontracks*/) { - BinningType colBinning{{cfgVtxBins, cfgCentBins}, true}; + BinningType colBinning{{cfgVtxBins, cfgCentBins}}; for (const auto& [collision1, collision2] : selfCombinations(colBinning, nEvtMixing.value, -1, collisions, collisions)) { if (collision1.globalIndex() == collision2.globalIndex()) { @@ -524,8 +524,7 @@ struct phiflowder { {getSPAngle}, {cfgVtxBins, cfgCentBins, - cfgSPAngleBins}, - true}; + cfgSPAngleBins}}; for (const auto& [collision1, collision2] : selfCombinations(binningOnSPAngle, diff --git a/PWGLF/Tasks/Resonances/phipbpb.cxx b/PWGLF/Tasks/Resonances/phipbpb.cxx index ca124250f26..e02230387f5 100644 --- a/PWGLF/Tasks/Resonances/phipbpb.cxx +++ b/PWGLF/Tasks/Resonances/phipbpb.cxx @@ -941,8 +941,8 @@ struct phipbpb { } PROCESS_SWITCH(phipbpb, processSameEventv1, "Process Same event for v1", false); - BinningTypeVertexContributor binningOnEPAngle{{axisVertex, axisMultiplicityClass, axisEPAngle}, true}; - BinningTypeVertexContributorv1 binningOnSPAngle{{axisVertex, axisMultiplicityClass, axisSPAngle}, true}; + BinningTypeVertexContributor binningOnEPAngle{{axisVertex, axisMultiplicityClass, axisEPAngle}}; + BinningTypeVertexContributorv1 binningOnSPAngle{{axisVertex, axisMultiplicityClass, axisSPAngle}}; Preslice tracksPerCollision = aod::track::collisionId; void processMEAcc(EventCandidates const& collisions, TrackCandidates const& tracks) { @@ -1188,7 +1188,7 @@ struct phipbpb { void processMixedEventOpti(EventCandidates const& collisions, TrackCandidates const& tracks) { auto tracksTuple = std::make_tuple(tracks); - BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}, true}; + BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}}; SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (auto& [collision1, tracks1, collision2, tracks2] : pair) { // if (!collision1.sel8() || !collision1.triggereventep() || !collision1.selection_bit(aod::evsel::kNoTimeFrameBorder) || !collision1.selection_bit(aod::evsel::kNoITSROFrameBorder) || !collision1.selection_bit(aod::evsel::kNoSameBunchPileup) || !collision1.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) || !collision1.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { @@ -1310,7 +1310,7 @@ struct phipbpb { void processMixedEventOptiv1(EventCandidatesv1 const& collisions, TrackCandidates const& tracks) { auto tracksTuple = std::make_tuple(tracks); - BinningTypeVertexContributorv1 binningOnPositions{{axisVertex, axisMultiplicityClass, axisSPAngle}, true}; + BinningTypeVertexContributorv1 binningOnPositions{{axisVertex, axisMultiplicityClass, axisSPAngle}}; SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (auto& [collision1, tracks1, collision2, tracks2] : pair) { if (!collision1.sel8() || !collision1.triggereventsp() || !collision1.selection_bit(aod::evsel::kNoSameBunchPileup)) { diff --git a/PWGLF/Tasks/Resonances/phiphimcinjected.cxx b/PWGLF/Tasks/Resonances/phiphimcinjected.cxx new file mode 100644 index 00000000000..b5791fac21b --- /dev/null +++ b/PWGLF/Tasks/Resonances/phiphimcinjected.cxx @@ -0,0 +1,508 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file phiphimcinjected.cxx +/// \brief Code for injected doublephi resonance MC +/// \author Sawan + +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include // std::abs +#include // size_t / std::size_t +#include + +using namespace o2; +using namespace o2::framework; + +struct PhiCandidateInjected { + ROOT::Math::PxPyPzMVector p4; + int t1Index = -1; + int t2Index = -1; + + // MC Truth attributes + bool isValidMcPhi = false; + bool isFrom999999 = false; + int mc999999Index = -1; +}; + +struct PhiPhiMCInjected { + + // Configurables + struct : ConfigurableGroup { + Configurable isSel8{"isSel8", true, "Event selection sel8"}; + Configurable cutDCAxy{"cutDCAxy", 0.1f, "DCAxy max cut"}; + Configurable cutDCAz{"cutDCAz", 0.1f, "DCAz max cut"}; + Configurable cutNsigmaTPC{"cutNsigmaTPC", 3.0f, "TPC Nsigma cut for Kaon"}; + Configurable isApplyPIDSelection{"isApplyPIDSelection", true, "Apply PID selection for Kaons"}; + Configurable trackEtaCut{"trackEtaCut", 0.8f, "Track Eta Cut"}; + Configurable phiMassCutLow{"phiMassCutLow", 0.9f, "Lower Cut for Phi Mass"}; + Configurable phiMassCutHigh{"phiMassCutHigh", 1.1f, "Upper Cut for Phi Mass"}; + + } config; + + const int injectedMotherPDG = 999999; + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; + double normFactor = 1.0; + + ConfigurableAxis phiMassAxis{"phiMassAxis", {200, 0.98, 1.22}, "Phi Mass Axis"}; + ConfigurableAxis phiPtAxis{"phiPtAxis", {200, 0.0, 100.0}, "Phi Pt Axis"}; + ConfigurableAxis phiPhiMassAxis{"phiPhiMassAxis", {200, 2.0, 4.0}, "Phi-Phi Mass Axis"}; + ConfigurableAxis phiPhiPtAxis{"phiPhiPtAxis", {200, 0.0, 50.0}, "Phi-Phi Pt Axis"}; + + enum RecoSelectionStage { + kLoop1Track = 0, + kLoop1DCA, + kLoop1PID, + kLoop1MCLabel, + kLoop1Eta, + kLoop2Track, + kLoop2Index, + kLoop2Eta, + kLoop2DCA, + kLoop2PID, + kLoop2MCLabel, + kLoop2Charge, + kLoop2KaonDaughters, + kCommonMotherLoop, + kCommonMother, + kPhiMother, + kPhiMassCut, + kInjectedMother, + kLoop2PhiPairs, + kUniqueTracks, + kValidPhiPhiPair + }; + + void init(InitContext const&) + { + + // Event histogram + registry.add("h1_TrackCount", "Track Count;Track Count;Counts", kTH1F, {{25, -0.5, 24.5}}); + registry.add("h1_EventCount", "Event Count;Event Count;Counts", kTH1F, {{5, -0.5, 4.5}}); + // Generated MC Histograms + registry.add("Generated/h2_InvMassPhiPhi", "Gen Phi-Phi: Mass vs pT;Mass (GeV/#it{c}^{2});p_{T} (GeV/#it{c})", kTHnSparseF, {{phiPhiMassAxis}, {phiPhiPtAxis}}); + registry.add("Generated/h2_InvMassPhiPhi2", "Gen Phi-Phi: Mass vs pT;Mass (GeV/#it{c}^{2});p_{T} (GeV/#it{c})", kTHnSparseF, {{phiPhiMassAxis}, {phiPhiPtAxis}}); + registry.add("Generated/h1_PhiPhi_Phi", "Gen Phi-Phi: #phi Angle;#phi;Counts", kTH1F, {{100, -o2::constants::math::PI, o2::constants::math::PI}}); + registry.add("Generated/h1_PhiPhi_Eta", "Gen Phi-Phi: #eta;#eta;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Generated/h1_PhiPhi_Y", "Gen Phi-Phi: Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + + registry.add("Generated/h2_InvMassPhiFromT4s", "Daughter phi from T4s decay: Mass vs pT;Mass (GeV/#it{c}^{2});p_{T} (GeV/#it{c})", kTHnSparseF, {{phiMassAxis}, {phiPtAxis}}); + registry.add("Generated/h2_InvMassPhiMinBias", "phi from minimum bias: Mass vs pT;Mass (GeV/#it{c}^{2});p_{T} (GeV/#it{c})", kTHnSparseF, {{phiMassAxis}, {phiPtAxis}}); + + // Reconstructed MC Histograms - 2D Mass vs pT of Mother Phi-Phi + registry.add("Reconstructed/AllPhiPairs/h2_PhiPhi_All", "Rec Phi-Phi All Pairs: Mass vs pT;Mass (GeV/#it{c}^{2});p_{T} (GeV/#it{c})", kTHnSparseF, {{phiPhiMassAxis}, {phiPhiPtAxis}}); + registry.add("Reconstructed/AllPhiPairs/h1_PhiPhi_Pt", "Rec Phi-Phi: pT;p_{T} (GeV/#it{c});Counts", kTH1F, {{phiPtAxis}}); + registry.add("Reconstructed/AllPhiPairs/h1_PhiPhi_Eta", "Rec Phi-Phi: #eta;#eta;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Reconstructed/AllPhiPairs/h1_PhiPhi_Y", "Rec Phi Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + + registry.add("Reconstructed/AllPhiPairs/h1_Phi1_Mass", "Rec Phi Mass: Mass;Mass (GeV/#it{c}^{2});Counts", kTH1F, {{phiMassAxis}}); + registry.add("Reconstructed/AllPhiPairs/h1_Phi1_Pt", "Rec Phi pT: pT;p_{T} (GeV/#it{c});Counts", kTH1F, {{phiPtAxis}}); + registry.add("Reconstructed/AllPhiPairs/h1_Phi1_Eta", "Rec Phi Eta: Eta;#eta;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Reconstructed/AllPhiPairs/h1_Phi1_Y", "Rec Phi Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + + registry.add("Reconstructed/PhiFromT4s/h2_PhiPhi_T4s", "Rec Phi-Phi Mother 999999: Mass vs pT;Mass (GeV/#it{c}^{2});p_{T} (GeV/#it{c})", kTHnSparseF, {{phiPhiMassAxis}, {phiPhiPtAxis}}); + registry.add("Reconstructed/PhiFromT4s/h1_PhiPhi_Pt", "Rec Phi-Phi: pT;p_{T} (GeV/#it{c});Counts", kTH1F, {{phiPtAxis}}); + registry.add("Reconstructed/PhiFromT4s/h1_PhiPhi_Eta", "Rec Phi-Phi: #eta;#eta;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Reconstructed/PhiFromT4s/h1_PhiPhi_Y", "Rec Phi Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + + registry.add("Reconstructed/PhiFromT4s/h1_Phi1_Mass", "Rec Phi Mass: Mass;Mass (GeV/#it{c}^{2});Counts", kTH1F, {{phiMassAxis}}); + registry.add("Reconstructed/PhiFromT4s/h1_Phi1_Pt", "Rec Phi pT: pT;p_{T} (GeV/#it{c});Counts", kTH1F, {{phiPtAxis}}); + registry.add("Reconstructed/PhiFromT4s/h1_Phi1_Eta", "Rec Phi Eta: Eta;#eta;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Reconstructed/PhiFromT4s/h1_Phi1_Y", "Rec Phi Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + + registry.add("Reconstructed/MinimumBiasPhi/h2_PhiPhi_MinBias", "Rec Phi-Phi Minimum Bias: Mass vs pT;Mass (GeV/#it{c}^{2});p_{T} (GeV/#it{c})", kTHnSparseF, {{phiPhiMassAxis}, {phiPhiPtAxis}}); + registry.add("Reconstructed/MinimumBiasPhi/h1_PhiPhi_Pt", "Rec Phi-Phi: pT;p_{T} (GeV/#it{c});Counts", kTH1F, {{phiPtAxis}}); + registry.add("Reconstructed/MinimumBiasPhi/h1_PhiPhi_Eta", "Rec Phi-Phi: #eta;#eta;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Reconstructed/MinimumBiasPhi/h1_PhiPhi_Y", "Rec Phi Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + + registry.add("Reconstructed/MinimumBiasPhi/h1_Phi1_Mass", "Rec Phi Mass: Mass;Mass (GeV/#it{c}^{2});Counts", kTH1F, {{phiMassAxis}}); + registry.add("Reconstructed/MinimumBiasPhi/h1_Phi1_Pt", "Rec Phi pT: pT;p_{T} (GeV/#it{c});Counts", kTH1F, {{phiPtAxis}}); + registry.add("Reconstructed/MinimumBiasPhi/h1_Phi1_Eta", "Rec Phi Eta: Eta;#eta;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Reconstructed/MinimumBiasPhi/h1_Phi1_Y", "Rec Phi Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + } + + // PID Selection function + bool checkPID(float ptcand, float nsigmaTPC, float nsigmaTOF, bool TOFHit) + { + constexpr float RadiusMax = 2.5f * 2.5f; + const bool hasTOF = (TOFHit == 1); + + if (!hasTOF) { + if (ptcand < 0.5f) { + return std::abs(nsigmaTPC) < config.cutNsigmaTPC; + } else { + return nsigmaTPC > -2.0f && nsigmaTPC < config.cutNsigmaTPC; + } + } else { + const float radius2 = nsigmaTPC * nsigmaTPC + nsigmaTOF * nsigmaTOF; + if (radius2 >= RadiusMax) { + return false; + } + if (ptcand < 2.0f) { + return true; + } + + float slope = 0.0f; + float intercept = 0.0f; + + if (ptcand < 2.5f) { + slope = 1.29f; + intercept = 3.34f; + } else if (ptcand < 3.0f) { + slope = 0.94f; + intercept = 2.45f; + } else if (ptcand < 4.0f) { + slope = 0.62f; + intercept = 1.55f; + } else if (ptcand < 5.0f) { + slope = 0.32f; + intercept = 1.45f; + } else if (ptcand < 7.0f) { + slope = 0.22f; + intercept = 1.35f; + } else { + slope = 0.10f; + intercept = 1.25f; + } + + return nsigmaTPC < slope * nsigmaTOF + intercept; + } + } + + // Define data table bindings + using EventCandidatesMC = soa::Join; + using TrackCandidatesMC = soa::Join; + + ROOT::Math::PxPyPzEVector vecGen, vecDau1, vecDau2; + ROOT::Math::PxPyPzMVector daughter1Gen, daughter2Gen, motherGen, daughter1Rec, daughter2Rec, motherRec; + + // Process Generated MC + void processGen(aod::McCollision const&, aod::McParticles const& mcParticles) + { + for (const auto& mcParticle : mcParticles) { + if (mcParticle.pdgCode() == injectedMotherPDG) { + + auto phiPhiDaughters = mcParticle.daughters_as(); + if (phiPhiDaughters.size() != 2) { + continue; + } + + int phiCount = 0; + for (const auto& kCurrentDaughter : phiPhiDaughters) { + + if (kCurrentDaughter.pdgCode() != o2::constants::physics::kPhi) { + continue; + } + + if (phiCount == 0) { + vecDau1 = ROOT::Math::PxPyPzEVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), kCurrentDaughter.e()); // Phi meson mass + daughter1Gen = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), o2::constants::physics::MassPhi); // Phi meson mass + phiCount++; + registry.fill(HIST("Generated/h2_InvMassPhiFromT4s"), vecDau1.M(), daughter1Gen.Pt()); + } else if (phiCount == 1) { + daughter2Gen = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), o2::constants::physics::MassPhi); // Phi meson mass + phiCount++; + } + } + if (phiCount != 2) { + continue; + } + motherGen = daughter1Gen + daughter2Gen; + vecGen = ROOT::Math::PxPyPzEVector(motherGen.Px(), motherGen.Py(), motherGen.Pz(), motherGen.E()); + + registry.fill(HIST("Generated/h2_InvMassPhiPhi"), motherGen.M(), motherGen.Pt()); + registry.fill(HIST("Generated/h2_InvMassPhiPhi2"), vecGen.M(), mcParticle.pt()); + + registry.fill(HIST("Generated/h1_PhiPhi_Phi"), vecGen.Phi()); + registry.fill(HIST("Generated/h1_PhiPhi_Eta"), vecGen.Eta()); + registry.fill(HIST("Generated/h1_PhiPhi_Y"), vecGen.Rapidity()); + } + + if (mcParticle.pdgCode() == o2::constants::physics::kPhi) { + + auto phiDaughters = mcParticle.daughters_as(); + if (phiDaughters.size() != 2) { + continue; + } + + bool passPosKaon = false; + bool passNegKaon = false; + for (const auto& kCurrentDaughter : phiDaughters) { + + if (kCurrentDaughter.pdgCode() == PDG_t::kKPlus) { + passPosKaon = true; + daughter1Gen = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), o2::constants::physics::MassKPlus); + + } else if (kCurrentDaughter.pdgCode() == PDG_t::kKMinus) { + passNegKaon = true; + daughter2Gen = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), o2::constants::physics::MassKPlus); + } + } + + // Ensure that the mother is not 999999 + auto mothers = mcParticle.mothers_as(); + bool isFrom999999 = false; + for (const auto& mother : mothers) { + if (mother.pdgCode() == injectedMotherPDG) { + isFrom999999 = true; + break; + } + } + + if (passPosKaon && passNegKaon && !isFrom999999) { + motherGen = daughter1Gen + daughter2Gen; + vecGen = ROOT::Math::PxPyPzEVector(motherGen.px(), motherGen.py(), motherGen.pz(), motherGen.E()); + + registry.fill(HIST("Generated/h2_InvMassPhiMinBias"), vecGen.M(), mcParticle.pt()); + } + } + } + } + PROCESS_SWITCH(PhiPhiMCInjected, processGen, "Process Generated MC", true); + + void processRec(EventCandidatesMC::iterator const& collision, TrackCandidatesMC const& tracks, aod::McParticles const&) + { + + registry.fill(HIST("h1_EventCount"), 0.0); + + if (collision.sel8()) { + registry.fill(HIST("h1_EventCount"), 1.0); + } + + if (!collision.sel8() && config.isSel8) { + return; + } + + std::vector phiCandidates; + + // STEP 1: Reconstruct individual Phi candidates (Kaon track pairs) + for (const auto& t1 : tracks) { + registry.fill(HIST("h1_TrackCount"), kLoop1Track); + + if (std::abs(t1.dcaXY()) > config.cutDCAxy || std::abs(t1.dcaZ()) > config.cutDCAz) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop1DCA); + + if (config.isApplyPIDSelection && !checkPID(t1.pt(), t1.tpcNSigmaKa(), t1.tofNSigmaKa(), t1.hasTOF())) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop1PID); + + if (!t1.has_mcParticle()) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop1MCLabel); + + if (std::abs(t1.eta()) > config.trackEtaCut) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop1Eta); + + for (const auto& t2 : tracks) { + registry.fill(HIST("h1_TrackCount"), kLoop2Track); + + if (t2.index() <= t1.index()) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2Index); + + if (std::abs(t2.eta()) > config.trackEtaCut) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2Eta); + + if (std::abs(t2.dcaXY()) > config.cutDCAxy || std::abs(t2.dcaZ()) > config.cutDCAz) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2DCA); + + if (config.isApplyPIDSelection && !checkPID(t2.pt(), t2.tpcNSigmaKa(), t2.tofNSigmaKa(), t2.hasTOF())) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2PID); + + if (!t2.has_mcParticle()) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2MCLabel); + + if (t1.sign() * t2.sign() > 0) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2Charge); + + auto mcKaTrk1 = t1.mcParticle(); + auto mcKaTrk2 = t2.mcParticle(); + + if (std::abs(mcKaTrk1.pdgCode()) != PDG_t::kKPlus || + std::abs(mcKaTrk2.pdgCode()) != PDG_t::kKPlus) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2KaonDaughters); + + bool foundMother = false; + + // Search for common mother + for (const auto& KaMotherPhi1 : mcKaTrk1.template mothers_as()) { + + for (const auto& KaMotherPhi2 : mcKaTrk2.template mothers_as()) { + + registry.fill(HIST("h1_TrackCount"), kCommonMotherLoop); + + if (KaMotherPhi1.globalIndex() != KaMotherPhi2.globalIndex()) + continue; + registry.fill(HIST("h1_TrackCount"), kCommonMother); + + int motherPdg = KaMotherPhi1.pdgCode(); + + if (motherPdg != o2::constants::physics::kPhi) + continue; + registry.fill(HIST("h1_TrackCount"), kPhiMother); + + daughter1Rec = ROOT::Math::PxPyPzMVector(t1.px(), t1.py(), t1.pz(), o2::constants::physics::MassKPlus); + daughter2Rec = ROOT::Math::PxPyPzMVector(t2.px(), t2.py(), t2.pz(), o2::constants::physics::MassKPlus); + motherRec = daughter1Rec + daughter2Rec; + + // Phi invariant mass window cut (0.9 - 1.1 GeV/c^2) + if (motherRec.M() < config.phiMassCutLow || motherRec.M() > config.phiMassCutHigh) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kPhiMassCut); + + PhiCandidateInjected phiCand; + + phiCand.p4 = motherRec; + phiCand.t1Index = t1.globalIndex(); + phiCand.t2Index = t2.globalIndex(); + phiCand.isValidMcPhi = true; + + for (const auto& gm : KaMotherPhi1.template mothers_as()) { + + if (gm.pdgCode() == injectedMotherPDG) { + phiCand.isFrom999999 = true; + phiCand.mc999999Index = gm.globalIndex(); + registry.fill(HIST("h1_TrackCount"), kInjectedMother); + } + } + + phiCandidates.push_back(phiCand); + foundMother = true; + break; + } + if (foundMother) { + break; + } + } + } + } + + // Fill Single Reconstructed Phi Histograms (Loop ONCE per candidate) + for (const auto& phi : phiCandidates) { + // All Reconstructed Phi Candidates + if (phi.isValidMcPhi) { + + registry.fill(HIST("Reconstructed/AllPhiPairs/h1_Phi1_Mass"), phi.p4.M()); + registry.fill(HIST("Reconstructed/AllPhiPairs/h1_Phi1_Pt"), phi.p4.Pt()); + registry.fill(HIST("Reconstructed/AllPhiPairs/h1_Phi1_Eta"), phi.p4.Eta()); + registry.fill(HIST("Reconstructed/AllPhiPairs/h1_Phi1_Y"), phi.p4.Rapidity()); + } + + // Phis from Mother 999999 + if (phi.isFrom999999 && phi.isValidMcPhi) { + registry.fill(HIST("Reconstructed/PhiFromT4s/h1_Phi1_Mass"), phi.p4.M()); + registry.fill(HIST("Reconstructed/PhiFromT4s/h1_Phi1_Pt"), phi.p4.Pt()); + registry.fill(HIST("Reconstructed/PhiFromT4s/h1_Phi1_Eta"), phi.p4.Eta()); + registry.fill(HIST("Reconstructed/PhiFromT4s/h1_Phi1_Y"), phi.p4.Rapidity()); + } + + // Minimum Bias Phis (not from 999999) + if (!phi.isFrom999999 && phi.isValidMcPhi) { + registry.fill(HIST("Reconstructed/MinimumBiasPhi/h1_Phi1_Mass"), phi.p4.M()); + registry.fill(HIST("Reconstructed/MinimumBiasPhi/h1_Phi1_Pt"), phi.p4.Pt()); + registry.fill(HIST("Reconstructed/MinimumBiasPhi/h1_Phi1_Eta"), phi.p4.Eta()); + registry.fill(HIST("Reconstructed/MinimumBiasPhi/h1_Phi1_Y"), phi.p4.Rapidity()); + } + } + + // Reconstruct Phi-Phi Mother Candidates (Double Loop for Pairs Only) + for (size_t i = 0; i < phiCandidates.size(); ++i) { + const auto& phi1 = phiCandidates[i]; + + for (size_t j = i + 1; j < phiCandidates.size(); ++j) { + const auto& phi2 = phiCandidates[j]; + + registry.fill(HIST("h1_TrackCount"), kLoop2PhiPairs); + + // Ensure all 4 kaon tracks are unique + if (phi1.t1Index == phi2.t1Index || phi1.t1Index == phi2.t2Index || + phi1.t2Index == phi2.t1Index || phi1.t2Index == phi2.t2Index) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kUniqueTracks); + + if (!phi1.isValidMcPhi || !phi2.isValidMcPhi) { + continue; + } + + registry.fill(HIST("h1_TrackCount"), kValidPhiPhiPair); + + // Combined 4-momentum of the Mother Phi-Phi System + ROOT::Math::PxPyPzMVector phiPhiMother = phi1.p4 + phi2.p4; + + // All available Phi-Phi pairs + registry.fill(HIST("Reconstructed/AllPhiPairs/h2_PhiPhi_All"), phiPhiMother.M(), phiPhiMother.Pt()); + registry.fill(HIST("Reconstructed/AllPhiPairs/h1_PhiPhi_Pt"), phiPhiMother.Pt()); + registry.fill(HIST("Reconstructed/AllPhiPairs/h1_PhiPhi_Eta"), phiPhiMother.Eta()); + registry.fill(HIST("Reconstructed/AllPhiPairs/h1_PhiPhi_Y"), phiPhiMother.Rapidity()); + + // Phi-Phi pairs from same Mother 999999 + if (phi1.isFrom999999 && phi2.isFrom999999 && (phi1.mc999999Index == phi2.mc999999Index)) { + registry.fill(HIST("Reconstructed/PhiFromT4s/h2_PhiPhi_T4s"), phiPhiMother.M(), phiPhiMother.Pt()); + registry.fill(HIST("Reconstructed/PhiFromT4s/h1_PhiPhi_Pt"), phiPhiMother.Pt()); + registry.fill(HIST("Reconstructed/PhiFromT4s/h1_PhiPhi_Eta"), phiPhiMother.Eta()); + registry.fill(HIST("Reconstructed/PhiFromT4s/h1_PhiPhi_Y"), phiPhiMother.Rapidity()); + } + + if (!phi1.isFrom999999 && !phi2.isFrom999999) { + registry.fill(HIST("Reconstructed/MinimumBiasPhi/h2_PhiPhi_MinBias"), phiPhiMother.M(), phiPhiMother.Pt()); + registry.fill(HIST("Reconstructed/MinimumBiasPhi/h1_PhiPhi_Pt"), phiPhiMother.Pt()); + registry.fill(HIST("Reconstructed/MinimumBiasPhi/h1_PhiPhi_Eta"), phiPhiMother.Eta()); + registry.fill(HIST("Reconstructed/MinimumBiasPhi/h1_PhiPhi_Y"), phiPhiMother.Rapidity()); + } + } + } + } + PROCESS_SWITCH(PhiPhiMCInjected, processRec, "Process Reconstructed MC", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/Tasks/Resonances/phiphimctrigger.cxx b/PWGLF/Tasks/Resonances/phiphimctrigger.cxx new file mode 100644 index 00000000000..52704c1537c --- /dev/null +++ b/PWGLF/Tasks/Resonances/phiphimctrigger.cxx @@ -0,0 +1,434 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file phiphimctrigger.cxx +/// \brief Code for triggered doublephi resonance MC +/// \author Sawan + +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include // std::abs +#include // size_t / std::size_t +#include + +using namespace o2; +using namespace o2::framework; + +struct PhiCandidateTrigger { + ROOT::Math::PxPyPzMVector p4; + int t1Index = -1; + int t2Index = -1; + int mcPhiIndex = -1; + + // MC Truth attributes + bool isValidMcPhi = false; +}; + +struct PhiCandidateGen { + ROOT::Math::PxPyPzMVector p4; +}; + +struct PhiPhiMCTrigger { + + // Configurables + struct : ConfigurableGroup { + Configurable isSel8{"isSel8", true, "Event selection sel8"}; + Configurable cutDCAxy{"cutDCAxy", 0.1f, "DCAxy max cut"}; + Configurable cutDCAz{"cutDCAz", 0.1f, "DCAz max cut"}; + Configurable cutNsigmaTPC{"cutNsigmaTPC", 3.0f, "TPC Nsigma cut for Kaon"}; + Configurable isApplyPIDSelection{"isApplyPIDSelection", true, "Apply PID selection for Kaons"}; + Configurable trackEtaCut{"trackEtaCut", 0.8f, "Track Eta Cut"}; + Configurable phiMassCutLow{"phiMassCutLow", 0.9f, "Lower Cut for Phi Mass"}; + Configurable phiMassCutHigh{"phiMassCutHigh", 1.1f, "Upper Cut for Phi Mass"}; + } config; + + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; + ConfigurableAxis phiMassAxis{"phiMassAxis", {200, 0.98, 1.22}, "Phi Mass Axis"}; + ConfigurableAxis phiPtAxis{"phiPtAxis", {200, 0.0, 100.0}, "Phi Pt Axis"}; + ConfigurableAxis phiPhiMassAxis{"phiPhiMassAxis", {200, 2.0, 4.0}, "Phi-Phi Mass Axis"}; + ConfigurableAxis phiPhiPtAxis{"phiPhiPtAxis", {200, 0.0, 50.0}, "Phi-Phi Pt Axis"}; + + enum RecoSelectionStage { + kLoop1Track = 0, + kLoop1DCA, + kLoop1PID, + kLoop1MCLabel, + kLoop1Eta, + kLoop2Track, + kLoop2Index, + kLoop2Eta, + kLoop2DCA, + kLoop2PID, + kLoop2MCLabel, + kLoop2Charge, + kLoop2TrackSelected, + kLoop2KaonDaughters, + kCommonMotherLoop, + kCommonMother, + kPhiMother, + kPhiMassCut, + kLoop2PhiPairs, + kUniqueTracks, + kDifferentPhiMothers, + kValidPhiPhiPair + }; + + void init(InitContext const&) + { + + // Event histogram + registry.add("h1_TrackCount", "Track Count;Track Count;Counts", kTH1F, {{30, -0.5, 29.5}}); + registry.add("h1_EventCount", "Event Count;Event Count;Counts", kTH1F, {{15, -0.5, 14.5}}); + // Generated MC Histograms + registry.add("Generated/h1_GenEventAndParticleCount", "Gen Level Summary;Category;Counts", kTH1F, {{4, -0.5, 3.5}}); + registry.add("Generated/h2_InvMassPhiPhi", "Gen Phi-Phi: Mass vs pT;Mass (GeV/#it{c}^{2});p_{T} (GeV/#it{c})", kTHnSparseF, {{phiPhiMassAxis}, {phiPhiPtAxis}}); + registry.add("Generated/h1_PhiPhi_Phi", "Gen Phi-Phi: #phi Angle;#phi;Counts", kTH1F, {{100, -o2::constants::math::PI, o2::constants::math::PI}}); + registry.add("Generated/h1_PhiPhi_Eta", "Gen Phi-Phi: #eta;#eta;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Generated/h1_PhiPhi_Y", "Gen Phi-Phi: Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Generated/h1_InvMassPhi", "Gen Phi: Mass;Mass (GeV/#it{c}^{2});Counts", kTH1F, {{phiMassAxis}}); + registry.add("Generated/h1_PhiPt", "Gen Phi: pT;p_{T} (GeV/#it{c});Counts", kTH1F, {{phiPtAxis}}); + registry.add("Generated/h1_PhiEta", "Gen Phi: #eta;#eta;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Generated/h1_PhiY", "Gen Phi: Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + + // Reconstructed MC Histograms - 2D Mass vs pT of Mother Phi-Phi + registry.add("Reconstructed/h2_PhiPhi_InvMass", "Rec Phi-Phi All Pairs: Mass vs pT;Mass (GeV/#it{c}^{2});p_{T} (GeV/#it{c})", kTHnSparseF, {{phiPhiMassAxis}, {phiPhiPtAxis}}); + registry.add("Reconstructed/h1_Phi1_Mass", "Rec Phi Mass: Mass;Mass (GeV/#it{c}^{2});Counts", kTH1F, {{phiMassAxis}}); + registry.add("Reconstructed/h1_Phi1_Pt", "Rec Phi pT: pT;p_{T} (GeV/#it{c});Counts", kTH1F, {{phiPtAxis}}); + registry.add("Reconstructed/h1_Phi1_Eta", "Rec Phi Eta: Eta;#eta;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Reconstructed/h1_Phi1_Y", "Rec Phi Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Reconstructed/h1_PhiPhi_Y", "Rec Phi Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + } + + // PID Selection function + bool checkPID(float ptcand, float nsigmaTPC, float nsigmaTOF, bool TOFHit) + { + constexpr float Radius2Max = 2.5f * 2.5f; + const bool hasTOF = (TOFHit == 1); + + if (!hasTOF) { + if (ptcand < 0.5f) { + return std::abs(nsigmaTPC) < config.cutNsigmaTPC; + } else { + return nsigmaTPC > -2.0f && nsigmaTPC < config.cutNsigmaTPC; + } + } else { + const float radius2 = nsigmaTPC * nsigmaTPC + nsigmaTOF * nsigmaTOF; + if (radius2 >= Radius2Max) { + return false; + } + if (ptcand < 2.0f) { + return true; + } + + float slope = 0.0f; + float intercept = 0.0f; + + if (ptcand < 2.5f) { + slope = 1.29f; + intercept = 3.34f; + } else if (ptcand < 3.0f) { + slope = 0.94f; + intercept = 2.45f; + } else if (ptcand < 4.0f) { + slope = 0.62f; + intercept = 1.55f; + } else if (ptcand < 5.0f) { + slope = 0.32f; + intercept = 1.45f; + } else if (ptcand < 7.0f) { + slope = 0.22f; + intercept = 1.35f; + } else { + slope = 0.10f; + intercept = 1.25f; + } + + return nsigmaTPC < slope * nsigmaTOF + intercept; + } + } + + // Define data table bindings + using EventCandidatesMC = soa::Join; + using TrackCandidatesMC = soa::Join; + + ROOT::Math::PxPyPzEVector vecGen, vecDau1, vecDau2; + ROOT::Math::PxPyPzMVector daughter1Gen, daughter2Gen, motherGen, daughter1Rec, daughter2Rec, motherRec; + + // Process Generated MC + void processGen(aod::McCollision const&, aod::McParticles const& mcParticles) + { + // Fill total generated events counter + registry.fill(HIST("Generated/h1_GenEventAndParticleCount"), 0.0); + + std::vector phiCandidatesGen; + for (const auto& mcParticle : mcParticles) { + + if (mcParticle.pdgCode() == o2::constants::physics::kPhi) { + + // Fill total phi produced counter + registry.fill(HIST("Generated/h1_GenEventAndParticleCount"), 1.0); + + auto phiDaughters = mcParticle.daughters_as(); + if (phiDaughters.size() != 2) { + continue; + } + + bool passPosKaon = false; + bool passNegKaon = false; + for (const auto& kCurrentDaughter : phiDaughters) { + + if (kCurrentDaughter.pdgCode() == PDG_t::kKPlus) { + passPosKaon = true; + daughter1Gen = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), o2::constants::physics::MassKPlus); + + // Fill total phi decay to K+ + registry.fill(HIST("Generated/h1_GenEventAndParticleCount"), 2.0); + + } else if (kCurrentDaughter.pdgCode() == PDG_t::kKMinus) { + passNegKaon = true; + daughter2Gen = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), o2::constants::physics::MassKPlus); + + // Fill total phi decay to K- + registry.fill(HIST("Generated/h1_GenEventAndParticleCount"), 3.0); + } + } + if (passPosKaon && passNegKaon) { + motherGen = daughter1Gen + daughter2Gen; + PhiCandidateGen phiCand; + phiCand.p4 = motherGen; + phiCandidatesGen.push_back(phiCand); + + registry.fill(HIST("Generated/h1_InvMassPhi"), motherGen.M()); + registry.fill(HIST("Generated/h1_PhiPt"), motherGen.Pt()); + registry.fill(HIST("Generated/h1_PhiEta"), motherGen.Eta()); + registry.fill(HIST("Generated/h1_PhiY"), motherGen.Rapidity()); + } + } + } + + for (size_t i = 0; i < phiCandidatesGen.size(); ++i) { + for (size_t j = i + 1; j < phiCandidatesGen.size(); ++j) { + const auto& phi1 = phiCandidatesGen[i]; + const auto& phi2 = phiCandidatesGen[j]; + + ROOT::Math::PxPyPzMVector motherPhiPhi = phi1.p4 + phi2.p4; + + registry.fill(HIST("Generated/h2_InvMassPhiPhi"), motherPhiPhi.M(), motherPhiPhi.Pt()); + registry.fill(HIST("Generated/h1_PhiPhi_Phi"), motherPhiPhi.Phi()); + registry.fill(HIST("Generated/h1_PhiPhi_Eta"), motherPhiPhi.Eta()); + registry.fill(HIST("Generated/h1_PhiPhi_Y"), motherPhiPhi.Rapidity()); + } + } + } + PROCESS_SWITCH(PhiPhiMCTrigger, processGen, "Process Generated MC", true); + + // Process Reconstructed MC + void processRec(EventCandidatesMC::iterator const& collision, TrackCandidatesMC const& tracks, aod::McParticles const&) + { + + registry.fill(HIST("h1_EventCount"), 0.0); + if (collision.sel8()) { + registry.fill(HIST("h1_EventCount"), 1.0); + } + + if (config.isSel8 && !collision.sel8()) { + return; + } + + std::vector phiCandidates; + + // STEP 1: Reconstruct individual Phi candidates (Kaon track pairs) + for (const auto& t1 : tracks) { + registry.fill(HIST("h1_TrackCount"), kLoop1Track); + + if (std::abs(t1.dcaXY()) > config.cutDCAxy || std::abs(t1.dcaZ()) > config.cutDCAz) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop1DCA); + + if (config.isApplyPIDSelection && !checkPID(t1.pt(), t1.tpcNSigmaKa(), t1.tofNSigmaKa(), t1.hasTOF())) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop1PID); + + if (!t1.has_mcParticle()) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop1MCLabel); + + if (std::abs(t1.eta()) > config.trackEtaCut) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop1Eta); + + for (const auto& t2 : tracks) { + registry.fill(HIST("h1_TrackCount"), kLoop2Track); + + if (t2.index() <= t1.index()) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2Index); + + if (std::abs(t2.eta()) > config.trackEtaCut) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2Eta); + + if (std::abs(t2.dcaXY()) > config.cutDCAxy || std::abs(t2.dcaZ()) > config.cutDCAz) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2DCA); + + if (config.isApplyPIDSelection && !checkPID(t2.pt(), t2.tpcNSigmaKa(), t2.tofNSigmaKa(), t2.hasTOF())) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2PID); + + if (!t2.has_mcParticle()) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2MCLabel); + + if (t1.sign() * t2.sign() > 0) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2Charge); + + registry.fill(HIST("h1_TrackCount"), kLoop2TrackSelected); + + auto mcKaTrk1 = t1.mcParticle(); + auto mcKaTrk2 = t2.mcParticle(); + + if (std::abs(mcKaTrk1.pdgCode()) != PDG_t::kKPlus || + std::abs(mcKaTrk2.pdgCode()) != PDG_t::kKPlus) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2KaonDaughters); + + bool foundMother = false; + + // Search for common mother + for (const auto& KaMotherPhi1 : mcKaTrk1.template mothers_as()) { + + for (const auto& KaMotherPhi2 : mcKaTrk2.template mothers_as()) { + + registry.fill(HIST("h1_TrackCount"), kCommonMotherLoop); + + if (KaMotherPhi1.globalIndex() != KaMotherPhi2.globalIndex()) + continue; + + registry.fill(HIST("h1_TrackCount"), kCommonMother); + + int motherPdg = KaMotherPhi1.pdgCode(); + + if (motherPdg != o2::constants::physics::kPhi) + continue; + + registry.fill(HIST("h1_TrackCount"), kPhiMother); + + daughter1Rec = ROOT::Math::PxPyPzMVector(t1.px(), t1.py(), t1.pz(), o2::constants::physics::MassKPlus); + daughter2Rec = ROOT::Math::PxPyPzMVector(t2.px(), t2.py(), t2.pz(), o2::constants::physics::MassKPlus); + motherRec = daughter1Rec + daughter2Rec; + + if (motherRec.M() < config.phiMassCutLow || motherRec.M() > config.phiMassCutHigh) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kPhiMassCut); + + PhiCandidateTrigger phiCand; + + phiCand.p4 = motherRec; + phiCand.t1Index = t1.globalIndex(); + phiCand.t2Index = t2.globalIndex(); + phiCand.isValidMcPhi = true; + phiCand.mcPhiIndex = KaMotherPhi1.globalIndex(); + + phiCandidates.push_back(phiCand); + foundMother = true; + break; + } + if (foundMother) { + break; + } + } + } + } + + // STEP 2: Fill Single Reconstructed Phi Histograms (Loop ONCE per candidate) + for (const auto& phi : phiCandidates) { + // All Reconstructed Phi Candidates + if (phi.isValidMcPhi) { + + registry.fill(HIST("Reconstructed/h1_Phi1_Mass"), phi.p4.M()); + registry.fill(HIST("Reconstructed/h1_Phi1_Pt"), phi.p4.Pt()); + registry.fill(HIST("Reconstructed/h1_Phi1_Eta"), phi.p4.Eta()); + registry.fill(HIST("Reconstructed/h1_Phi1_Y"), phi.p4.Rapidity()); + } + } + + // STEP 3: Reconstruct Phi-Phi Mother Candidates (Double Loop for Pairs Only) + for (size_t i = 0; i < phiCandidates.size(); ++i) { + const auto& phi1 = phiCandidates[i]; + + for (size_t j = i + 1; j < phiCandidates.size(); ++j) { + const auto& phi2 = phiCandidates[j]; + + registry.fill(HIST("h1_TrackCount"), kLoop2PhiPairs); + + // Ensure all 4 kaon tracks are unique + if (phi1.t1Index == phi2.t1Index || phi1.t1Index == phi2.t2Index || + phi1.t2Index == phi2.t1Index || phi1.t2Index == phi2.t2Index) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kUniqueTracks); + + if (phi1.mcPhiIndex == phi2.mcPhiIndex) + continue; + registry.fill(HIST("h1_TrackCount"), kDifferentPhiMothers); + + if (!phi1.isValidMcPhi || !phi2.isValidMcPhi) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kValidPhiPhiPair); + + // Combined 4-momentum of the Mother Phi-Phi System + ROOT::Math::PxPyPzMVector phiPhiMother = phi1.p4 + phi2.p4; + + registry.fill(HIST("Reconstructed/h2_PhiPhi_InvMass"), phiPhiMother.M(), phiPhiMother.Pt()); + registry.fill(HIST("Reconstructed/h1_PhiPhi_Y"), phiPhiMother.Rapidity()); + } + } + } + PROCESS_SWITCH(PhiPhiMCTrigger, processRec, "Process Reconstructed MC", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/Tasks/Resonances/phispectrapbpbqa.cxx b/PWGLF/Tasks/Resonances/phispectrapbpbqa.cxx index d26a5b66045..27d024611de 100644 --- a/PWGLF/Tasks/Resonances/phispectrapbpbqa.cxx +++ b/PWGLF/Tasks/Resonances/phispectrapbpbqa.cxx @@ -494,7 +494,7 @@ struct phispectrapbpbqa { void processMixedEventOpti(EventCandidates const& collisions, TrackCandidates const& tracks) { auto tracksTuple = std::make_tuple(tracks); - BinningTypeVertexContributor binningOnPositions{{axisVertex, cnfgaxis.configThnAxisCentrality, axisOccupancy}, true}; + BinningTypeVertexContributor binningOnPositions{{axisVertex, cnfgaxis.configThnAxisCentrality, axisOccupancy}}; SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (auto& [collision1, tracks1, collision2, tracks2] : pair) { if (!collision1.sel8() || !collision1.selection_bit(aod::evsel::kNoTimeFrameBorder) || !collision1.selection_bit(aod::evsel::kNoITSROFrameBorder) || !collision1.selection_bit(aod::evsel::kNoSameBunchPileup) || !collision1.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) || !collision1.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { diff --git a/PWGLF/Tasks/Resonances/phitutorial.cxx b/PWGLF/Tasks/Resonances/phitutorial.cxx index d60c63819e6..84c591b9edb 100644 --- a/PWGLF/Tasks/Resonances/phitutorial.cxx +++ b/PWGLF/Tasks/Resonances/phitutorial.cxx @@ -187,7 +187,7 @@ struct phitutorial { std::vector zBins{10, -10, 10}; std::vector multBins{VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 100.1}; using BinningType = ColumnBinningPolicy; - BinningType binning{{zBins, multBins}, true}; + BinningType binning{{zBins, multBins}}; SameKindPair pair{binning, 5, -1, &cache}; void processDataMixedEvent(EventCandidates const& collisions, TrackCandidates const& tracks) diff --git a/PWGLF/Tasks/Resonances/phitutorial_step3.cxx b/PWGLF/Tasks/Resonances/phitutorial_step3.cxx index c2f072e37d0..739c339f90b 100644 --- a/PWGLF/Tasks/Resonances/phitutorial_step3.cxx +++ b/PWGLF/Tasks/Resonances/phitutorial_step3.cxx @@ -188,7 +188,7 @@ struct phitutorial_step3 { std::vector zBins{10, -10, 10}; std::vector multBins{VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 100.1}; using BinningType = ColumnBinningPolicy; - BinningType binning{{zBins, multBins}, true}; + BinningType binning{{zBins, multBins}}; SameKindPair pair{binning, 5, -1, &cache}; void processDataMixedEvent(EventCandidates const& collisions, TrackCandidates const& tracks) // notice the collisions subscrition, it is not an iterator here! diff --git a/PWGLF/Tasks/Resonances/rhoanalysis.cxx b/PWGLF/Tasks/Resonances/rhoanalysis.cxx index 77f4bd5c1e0..47367bd4ee4 100644 --- a/PWGLF/Tasks/Resonances/rhoanalysis.cxx +++ b/PWGLF/Tasks/Resonances/rhoanalysis.cxx @@ -267,7 +267,7 @@ struct rhoanalysis { void processMixedEvent(Event const& events, TrackPi const& tracks) { auto tracksTuple = std::make_tuple(tracks); - BinningType binningOnPositions{{axisVertex, axisMultiplicity}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{axisVertex, axisMultiplicity}}; SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, events, tracksTuple, &cache}; float massPiplus = Ipdg->Mass(kPiPlus); float massPiminus = Ipdg->Mass(kPiMinus); diff --git a/PWGLF/Tasks/Resonances/sigma.cxx b/PWGLF/Tasks/Resonances/sigma.cxx index 487a967e93d..61639e5f506 100644 --- a/PWGLF/Tasks/Resonances/sigma.cxx +++ b/PWGLF/Tasks/Resonances/sigma.cxx @@ -257,7 +257,7 @@ struct sigma { using BinningTypeCentralityM = ColumnBinningPolicy; using BinningTypeVertexContributor = ColumnBinningPolicy; - BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicity}, true}; + BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicity}}; Pair pair{binningOnPositions, cfgNoMixedEvents, -1, &cache}; void processSE(EventCandidates::iterator const& collision, TrackCandidates const& tracks, aod::V0Datas const& V0s, aod::BCs const&) diff --git a/PWGLF/Tasks/Resonances/xi1530Analysisqa.cxx b/PWGLF/Tasks/Resonances/xi1530Analysisqa.cxx index 3ed15c1328d..ff27e613f62 100644 --- a/PWGLF/Tasks/Resonances/xi1530Analysisqa.cxx +++ b/PWGLF/Tasks/Resonances/xi1530Analysisqa.cxx @@ -31,6 +31,7 @@ #include #include #include +#include #include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) @@ -49,6 +50,7 @@ #include #include #include +#include #include using namespace o2; @@ -81,6 +83,12 @@ struct Xi1530Analysisqa { using ResoMicroTracks = aod::ResoMicroTracks_001; using ResoMCCols = soa::Join; + // This native generator status is a production-specific injection tag. + static constexpr auto IsInjectedParent = [](auto const& mcparent) { + constexpr int InjectedGeneratorStatus = -11; + return o2::mcgenstatus::getGenStatusCode(mcparent.statusCode()) == InjectedGeneratorStatus; + }; + // Own just the event rows: zero-copy slices would retain entire DF buffers. struct MixingCollision { float x, y, z, centrality, multiplicity; @@ -142,8 +150,8 @@ struct Xi1530Analysisqa { struct : ConfigurableGroup { // Primary track selections - Configurable cMinPtcut{"cMinPtcut", 0.15, "Track minium pt cut"}; - Configurable cMaxetacut{"cMaxetacut", 0.8, "Track maximum eta cut"}; + Configurable cMinPtcut{"cMinPtcut", 0.00, "Track minium pt cut"}; + Configurable cMaxetacut{"cMaxetacut", 999., "Track maximum eta cut"}; Configurable cfgPrimaryTrack{"cfgPrimaryTrack", true, "Primary track selection"}; Configurable cfgPVContributor{"cfgPVContributor", true, "PV contributor track selection"}; @@ -152,6 +160,11 @@ struct Xi1530Analysisqa { Configurable cfgTPCRows{"cfgTPCRows", 80, "Minimum Number of TPC Crossed Rows "}; + // Micro001 quality cuts use strict lower bounds; -1 preserves the previous selection. + // Full ResoTracks has no ITS cluster map and retains cfgTPCcluster/cfgTPCRows. + Configurable cfgITSNCls{"cfgITSNCls", -1, "Require ITS NCls > cut; -1 disables the cut"}; + Configurable cfgITSNClsInnerBarrel{"cfgITSNClsInnerBarrel", -1, "Require inner-barrel ITS NCls > cut; -1 disables the cut"}; + Configurable cfgRapidityMinCut{"cfgRapidityMinCut", -0.5, "Rapidity cut for tracks"}; Configurable cfgRapidityMaxCut{"cfgRapidityMaxCut", 0.5, "Rapidity cut for tracks"}; @@ -315,6 +328,8 @@ struct Xi1530Analysisqa { Configurable cfgMaxRot{"cfgMaxRot", 7.0 * constants::math::PI / 6.0, "Maximum of rotation"}; Configurable cfgRotPion{"cfgRotPion", true, "Rotate pion"}; Configurable cfgNrotBkg{"cfgNrotBkg", 4, "Number of rotated copies (background) per each original candidate"}; + + Configurable cMCInjection{"cMCInjection", true, "Select generated MC parents with native status -11 (production-specific injection tag)"}; } additionalConfig; //*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*// @@ -589,7 +604,13 @@ struct Xi1530Analysisqa { if (std::abs(track.dcaXY()) >= dcaXYCut || std::abs(track.dcaZ()) >= dcaZCut) { return false; } - if constexpr (!IsResoMicrotrack) { + if constexpr (IsResoMicrotrack) { + if (track.itsNCls() <= primarytrackConfig.cfgITSNCls || + track.itsNClsInnerBarrel() <= primarytrackConfig.cfgITSNClsInnerBarrel || + track.tpcNClsCrossedRows() <= primarytrackConfig.cfgTPCRows) { + return false; + } + } else { if (track.tpcNClsFound() <= primarytrackConfig.cfgTPCcluster) { return false; } @@ -808,9 +829,25 @@ struct Xi1530Analysisqa { return properLifetime; } + // Build once per selected collision, not once per reconstructed pair. + std::unordered_set getInjectedMotherIds(aod::ResoMCParents_001 const& parents) + { + std::unordered_set motherIds; + if (!additionalConfig.cMCInjection) { + return motherIds; + } + for (auto const& parent : parents) { + if (std::abs(parent.pdgCode()) == kXiStar && IsInjectedParent(parent)) { + motherIds.insert(parent.originalMcParticleId()); + } + } + return motherIds; + } + template void fillHistograms(const CollisionType& collision, const centType& inCent, const TracksType& dTracks1, const TracksTypeCasc& dTracks2, - const MixingCollision* cascadeCollision = nullptr) // Order: ResoColl, ResoTrack, ResoCascTrack + const MixingCollision* cascadeCollision = nullptr, + const std::unordered_set& injectedMotherIds = {}) // Order: ResoColl, ResoTrack, ResoCascTrack { auto cent = inCent; @@ -1194,6 +1231,9 @@ struct Xi1530Analysisqa { if (xiCand.motherId() < 0 || pionCand.motherId() != xiCand.motherId()) { continue; } + if (additionalConfig.cMCInjection && !injectedMotherIds.contains(xiCand.motherId())) { + continue; + } auto lResonancePtMC = xiCand.motherPt(); if (additionalConfig.cUseTruthRapidity) { const bool passesTruthRapidity = xiCand.motherRap() > primarytrackConfig.cfgRapidityMinCut && xiCand.motherRap() < primarytrackConfig.cfgRapidityMaxCut; @@ -1352,7 +1392,8 @@ struct Xi1530Analysisqa { // Calculate numerator for the Acceptance x Efficiency void processMC(ResoMCCols::iterator const& resoCollision, soa::Join const& cascTracks, - soa::Join const& resoTracks) + soa::Join const& resoTracks, + aod::ResoMCParents_001 const& resoParents) { auto inCent = resoCollision.cent(); const auto multiplicity = resoCollision.multiplicity(); @@ -1365,12 +1406,14 @@ struct Xi1530Analysisqa { if (histoConfig.multQA) { histos.fill(HIST("multQA/h2MultCent"), inCent, multiplicity); } - fillHistograms(resoCollision, inCent, resoTracks, cascTracks); + const auto injectedMotherIds = getInjectedMotherIds(resoParents); + fillHistograms(resoCollision, inCent, resoTracks, cascTracks, nullptr, injectedMotherIds); } void processMCMicro(ResoMCCols::iterator const& resoCollision, soa::Join const& cascTracks, - soa::Join const& resoTracks) + soa::Join const& resoTracks, + aod::ResoMCParents_001 const& reParent) { if ((additionalConfig.cRecoINELgt0 && !resoCollision.isRecINELgt0()) || (additionalConfig.cMCINELgt0 && !resoCollision.isINELgt0()) || @@ -1380,7 +1423,8 @@ struct Xi1530Analysisqa { if (histoConfig.multQA) { histos.fill(HIST("multQA/h2MultCent"), resoCollision.cent(), resoCollision.multiplicity()); } - fillHistograms(resoCollision, resoCollision.cent(), resoTracks, cascTracks); + const auto injectedMotherIds = getInjectedMotherIds(reParent); + fillHistograms(resoCollision, resoCollision.cent(), resoTracks, cascTracks, nullptr, injectedMotherIds); } // Calculate denominator for the Acceptance x Efficiency, actually it is not Trueth info... @@ -1402,6 +1446,9 @@ struct Xi1530Analysisqa { if (std::abs(part.pdgCode()) != kXiStar) { continue; } + if (additionalConfig.cMCInjection && !IsInjectedParent(part)) { + continue; + } if ((part.y() <= primarytrackConfig.cfgRapidityMinCut) || (part.y() >= primarytrackConfig.cfgRapidityMaxCut)) { continue; } @@ -1445,7 +1492,7 @@ struct Xi1530Analysisqa { } auto tracksTuple = std::make_tuple(resomicrotracks, cascTracks); - BinningTypeVtxZT0M colBinning{{mixingConfig.cfgVtxBins, mixingConfig.cfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{mixingConfig.cfgVtxBins, mixingConfig.cfgMultBins}}; Pair pairs{colBinning, mixingConfig.nEvtMixing, -1, resoCollisions, tracksTuple, &cache}; for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { @@ -1493,7 +1540,7 @@ struct Xi1530Analysisqa { mixingPools.clear(); mixingBField = collision.bMagField(); } - BinningTypeVtxZT0M binning{{mixingConfig.cfgVtxBins, mixingConfig.cfgMultBins}, true}; + BinningTypeVtxZT0M binning{{mixingConfig.cfgVtxBins, mixingConfig.cfgMultBins}}; const int bin = binning.getBin(std::make_tuple(collision.posZ(), collision.cent())); if (bin < 0) { return; diff --git a/PWGLF/Tasks/Resonances/xi1530kaoncorrelation.cxx b/PWGLF/Tasks/Resonances/xi1530kaoncorrelation.cxx index b48a6b13948..347058612ad 100644 --- a/PWGLF/Tasks/Resonances/xi1530kaoncorrelation.cxx +++ b/PWGLF/Tasks/Resonances/xi1530kaoncorrelation.cxx @@ -177,7 +177,7 @@ struct xi1530kaoncorrelation { using MixingBinning = ColumnBinningPolicy; - MixingBinning mixingBinning{{cfgMixVtxBins, cfgMixFT0MBins, cfgMixBzBins}, true}; + MixingBinning mixingBinning{{cfgMixVtxBins, cfgMixFT0MBins, cfgMixBzBins}}; Preslice xiStarsPerEvent = aod::redxistarcandidate::redXiStarKEventId; Preslice kaonsPerEvent = aod::redxistarkaon::redXiStarKEventId; diff --git a/PWGLF/Tasks/Resonances/xi1820Analysis.cxx b/PWGLF/Tasks/Resonances/xi1820Analysis.cxx index 12b89e89024..e922020b6d8 100644 --- a/PWGLF/Tasks/Resonances/xi1820Analysis.cxx +++ b/PWGLF/Tasks/Resonances/xi1820Analysis.cxx @@ -103,8 +103,6 @@ struct Xi1820Analysis { // Constants static constexpr float SmallMomentumDenominator = 1e-10f; // Small value to avoid division by zero - static constexpr int PdgChargedXi1820 = 123314; // o2-linter: disable=pdg/explicit-code (Xi(1820) PDG code not available in PDG_t or o2::constants::physics::Pdg) - static constexpr int PdgXi1820Zero = 123324; // o2-linter: disable=pdg/explicit-code (Xi(1820) PDG code not available in PDG_t or o2::constants::physics::Pdg) static constexpr int ExpectedDaughters = 2; // Expected number of daughters for two-body decay // Axes @@ -1116,7 +1114,7 @@ struct Xi1820Analysis { } if constexpr (IsMC) { // Calculate Acceptance x efficiency for "the particle" channel - if (v0.motherPDG() != -PdgChargedXi1820 || kaon.motherPDG() != v0.motherPDG()) { + if (v0.motherPDG() != -Pdg::kXi1820Minus || kaon.motherPDG() != v0.motherPDG()) { continue; } if (kaon.pdgCode() != PDG_t::kKPlus || v0.pdgCode() != PDG_t::kLambda0Bar) { @@ -1193,7 +1191,7 @@ struct Xi1820Analysis { } } if constexpr (IsMC) { // Calculate Acceptance x efficiency for "the particle" channel - if (v0.motherPDG() != PdgChargedXi1820 || kaon.motherPDG() != v0.motherPDG()) { + if (v0.motherPDG() != Pdg::kXi1820Minus || kaon.motherPDG() != v0.motherPDG()) { continue; } if (kaon.pdgCode() != PDG_t::kKMinus || v0.pdgCode() != PDG_t::kLambda0) { @@ -1398,7 +1396,7 @@ struct Xi1820Analysis { } if constexpr (IsMC) { // Calculate Acceptance x efficiency - if (lambda.motherPDG() != PdgXi1820Zero || k0s.motherPDG() != lambda.motherPDG()) { + if (lambda.motherPDG() != Pdg::kXi1820Zero || k0s.motherPDG() != lambda.motherPDG()) { continue; } if (std::abs(k0s.pdgCode()) != PDG_t::kK0Short || lambda.pdgCode() != PDG_t::kLambda0) { @@ -1468,7 +1466,7 @@ struct Xi1820Analysis { } if constexpr (IsMC) { // Calculate Acceptance x efficiency - if (lambda.motherPDG() != -PdgXi1820Zero || k0s.motherPDG() != lambda.motherPDG()) { + if (lambda.motherPDG() != -Pdg::kXi1820Zero || k0s.motherPDG() != lambda.motherPDG()) { continue; } if (std::abs(k0s.pdgCode()) != PDG_t::kK0Short || lambda.pdgCode() != PDG_t::kLambda0Bar) { @@ -1556,7 +1554,7 @@ struct Xi1820Analysis { return; } auto tracksTuple = std::make_tuple(resoTracks, resoV0s); - BinningTypeVertexContributor binning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVertexContributor binning{{cfgVtxBins, cfgMultBins}}; Pair pairs{binning, nEvtMixing, -1, resoCollisions, tracksTuple, &cache}; for (auto& [collision1, tracks1, collision2, v0s2] : pairs) { // o2-linter: disable=const-ref-in-for-loop (structured bindings from Pair iterator cannot be const) if (!acceptsMixedCollisions(collision1, collision2)) { @@ -1575,7 +1573,7 @@ struct Xi1820Analysis { return; } auto tracksTuple = std::make_tuple(resoMicroTracks, resoV0s); - BinningTypeVertexContributor binning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVertexContributor binning{{cfgVtxBins, cfgMultBins}}; Pair pairs{binning, nEvtMixing, -1, resoCollisions, tracksTuple, &cache}; for (auto& [collision1, tracks1, collision2, v0s2] : pairs) { // o2-linter: disable=const-ref-in-for-loop (structured bindings from Pair iterator cannot be const) if (!acceptsMixedCollisions(collision1, collision2)) { @@ -1615,7 +1613,7 @@ struct Xi1820Analysis { mixingPools.clear(); mixingBField = collision.bMagField(); } - BinningTypeVertexContributor binning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVertexContributor binning{{cfgVtxBins, cfgMultBins}}; const int bin = binning.getBin(std::make_tuple(collision.posZ(), collision.cent())); if (bin < 0) { return; @@ -1656,7 +1654,7 @@ struct Xi1820Analysis { return; } auto v0sV0sTuple = std::make_tuple(resoV0s, resoV0s); - BinningTypeVertexContributor colBinning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVertexContributor colBinning{{cfgVtxBins, cfgMultBins}}; Pair pairs{colBinning, nEvtMixing, -1, resoCollisions, v0sV0sTuple, &cache}; for (auto& [collision1, k0s1, collision2, lambda2] : pairs) { // o2-linter: disable=const-ref-in-for-loop (structured bindings from Pair iterator cannot be const) @@ -1680,7 +1678,7 @@ struct Xi1820Analysis { neutralMixingPools.clear(); neutralMixingBField = collision.bMagField(); } - BinningTypeVertexContributor binning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVertexContributor binning{{cfgVtxBins, cfgMultBins}}; const int bin = binning.getBin(std::make_tuple(collision.posZ(), collision.cent())); if (bin < 0) { return; @@ -1770,7 +1768,7 @@ struct Xi1820Analysis { histos.fill(HIST("multQA/h2MultCentMC"), inCent, multiplicity); for (const auto& part : resoParents) { // loop over all pre-filtered Gen particle on selected events auto pdgMother = part.pdgCode(); - if (std::abs(pdgMother) != PdgChargedXi1820 && std::abs(pdgMother) != PdgXi1820Zero) { + if (std::abs(pdgMother) != Pdg::kXi1820Minus && std::abs(pdgMother) != Pdg::kXi1820Zero) { continue; } if (!passesRapidity(part.y())) { @@ -1780,7 +1778,7 @@ struct Xi1820Analysis { auto daughter1PDG = part.daughterPDG1(); auto daughter2PDG = part.daughterPDG2(); - if (std::abs(pdgMother) == PdgChargedXi1820) { // Explicity check for the safety. + if (std::abs(pdgMother) == Pdg::kXi1820Minus) { // Explicity check for the safety. // K- + Anti-Lambda, K+ + Anti-Lambda if ((daughter1PDG == PDG_t::kKMinus && daughter2PDG == PDG_t::kLambda0) || (daughter1PDG == PDG_t::kLambda0 && daughter2PDG == PDG_t::kKMinus)) { @@ -1813,7 +1811,7 @@ struct Xi1820Analysis { int pdg = mcParticle.pdgCode(); // Select only the configured charged and neutral Xi(1820) states. - if (std::abs(pdg) != PdgChargedXi1820 && std::abs(pdg) != PdgXi1820Zero) { + if (std::abs(pdg) != Pdg::kXi1820Minus && std::abs(pdg) != Pdg::kXi1820Zero) { continue; } diff --git a/PWGLF/Tasks/Strangeness/CMakeLists.txt b/PWGLF/Tasks/Strangeness/CMakeLists.txt index 9b08a271008..5ddab116838 100644 --- a/PWGLF/Tasks/Strangeness/CMakeLists.txt +++ b/PWGLF/Tasks/Strangeness/CMakeLists.txt @@ -11,26 +11,31 @@ o2physics_add_dpl_workflow(hyperon-reco-test SOURCES hyperon-reco-test.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derivedlambdakzeroanalysis SOURCES derivedlambdakzeroanalysis.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdakzeroanalysis-mc SOURCES lambdakzeroanalysisMC.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(cascadeanalysis SOURCES cascadeanalysis.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(cascadeanalysismc SOURCES cascadeanalysisMC.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -41,11 +46,13 @@ o2physics_add_dpl_workflow(v0postprocessing o2physics_add_dpl_workflow(cascadecorrelations SOURCES cascadecorrelations.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(non-prompt-cascade SOURCES nonPromptCascade.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::Framework O2::ReconstructionDataFormats O2Physics::AnalysisCore O2::DetectorsBase O2::DetectorsVertexing O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) @@ -56,19 +63,28 @@ o2physics_add_dpl_workflow(k0mixedevents o2physics_add_dpl_workflow(xi-lambda-corr-task SOURCES xiLambdaCorr.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(vzero-cascade-absorption SOURCES vzero_cascade_absorption.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derivedcascadeanalysis SOURCES derivedcascadeanalysis.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(derivedxi0analysis + SOURCES derivedXi0analysis.cxx + REUSE_FROM LfStrangenessPCH + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(cascpostprocessing SOURCES cascpostprocessing.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore @@ -76,16 +92,19 @@ o2physics_add_dpl_workflow(cascpostprocessing o2physics_add_dpl_workflow(hstrangecorrelation SOURCES hStrangeCorrelation.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sigmaanalysis SOURCES sigmaanalysis.cxx - PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB + REUSE_FROM LfStrangenessPCH + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(phik0shortanalysis SOURCES phik0shortanalysis.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -96,37 +115,44 @@ o2physics_add_dpl_workflow(phi-strange-correlation o2physics_add_dpl_workflow(lambdapolarization SOURCES lambdapolarization.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdapolsp SOURCES lambdapolsp.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdak0seff SOURCES lambdak0seff.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-lambda-spin-corr SOURCES taskLambdaSpinCorr.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(cascpolsp SOURCES cascpolsp.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) if(FastJet_FOUND) o2physics_add_dpl_workflow(strangeness-in-jets SOURCES strangenessInJets.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGJECore FastJet::FastJet FastJet::Contrib O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(strangeness-in-jets-ions SOURCES strangenessInJetsIons.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGJECore FastJet::FastJet FastJet::Contrib O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) @@ -138,31 +164,37 @@ endif() o2physics_add_dpl_workflow(v0topologicalcuts SOURCES v0topologicalcuts.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(v0ptinvmassplots SOURCES v0ptinvmassplots.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derivedupcanalysis SOURCES derivedupcanalysis.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdak0sflattenicity SOURCES lambdak0sflattenicity.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdalambda SOURCES lambdalambda.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdajetpolarization SOURCES lambdaJetpolarization.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGJECore FastJet::FastJet FastJet::Contrib O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) @@ -178,21 +210,25 @@ o2physics_add_dpl_workflow(lambdaspincorrderived o2physics_add_dpl_workflow(strangenessderivedbinnedinfo SOURCES strangenessderivedbinnedinfo.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambda-two-part-polarization SOURCES lambdaTwoPartPolarization.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(cascadeanalysislightions SOURCES cascadeAnalysisLightIonsDerivedData.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(strange-casc-track SOURCES strangeCascTrack.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) diff --git a/PWGLF/Tasks/Strangeness/cascadeAnalysisLightIonsDerivedData.cxx b/PWGLF/Tasks/Strangeness/cascadeAnalysisLightIonsDerivedData.cxx index 33d3c9c51b0..bde34b181eb 100644 --- a/PWGLF/Tasks/Strangeness/cascadeAnalysisLightIonsDerivedData.cxx +++ b/PWGLF/Tasks/Strangeness/cascadeAnalysisLightIonsDerivedData.cxx @@ -17,6 +17,7 @@ #include "PWGLF/DataModel/LFStrangenessPIDTables.h" #include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGLF/DataModel/mcCentrality.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/RCTSelectionFlags.h" @@ -56,7 +57,7 @@ using SimCollisions = soa::Join; using CascadeMCCandidates = soa::Join; using DaughterTracks = soa::Join; -using CollisionMCTrueTable = soa::Join; +using CollisionMCTrueTable = soa::Join; using CascadeMCCores = soa::Join; struct CascadeAnalysisLightIonsDerivedData { @@ -151,6 +152,7 @@ struct CascadeAnalysisLightIonsDerivedData { // Centrality estimator Configurable centralityEstimator{"centralityEstimator", 0, "0 = FT0C, 1 = FTOM, 2 = FV0A, 3 = NGlobal"}; + Configurable useMcCentrality{"useMcCentrality", false, "Use MC centrality?"}; // List of estimators enum Option { kFT0C, @@ -299,6 +301,8 @@ struct CascadeAnalysisLightIonsDerivedData { // Multiplicity Histograms registryMC.add("hCentEstimator_truerec", "hCentEstimator_truerec", HistType::kTH1D, {{101, 0.0f, 101.0f}}); registryMC.add("hCentralityVsNch_truerec", "hCentralityVsNch_truerec", HistType::kTH2D, {{101, 0.0f, 101.0f}, axisNch}); + if (useMcCentrality) + registryMC.add("hEventVsGenCentrality_truerec", "hEventVsGenCentrality_truerec", HistType::kTH2D, {{101, 0.0f, 101.0f}, {101, 0.0f, 101.0f}}); if (centralityEstimator == Option::kNGlobal) registryMC.add("hCentralityVsMultiplicity_truerec", "hCentralityVsMultiplicity_truerec", HistType::kTH2D, {{101, 0.0f, 101.0f}, axisNch}); else @@ -322,27 +326,46 @@ struct CascadeAnalysisLightIonsDerivedData { // QC Histograms registryQC.add("hVertexZGen", "hVertexZGen", HistType::kTH1D, {{vertexZAxis}}); // Histograms for xi (mc) - registryMC.add("h2dGenXiMinusVsMultMC_RecoedEvt", "h2dGenXiMinusVsMultMC_RecoedEvt", HistType::kTH2D, {axisNch, ptAxis}); - registryMC.add("h2dGenXiPlusVsMultMC_RecoedEvt", "h2dGenXiPlusVsMultMC_RecoedEvt", HistType::kTH2D, {axisNch, ptAxis}); - registryMC.add("h2dGenXiMinusVsMultMC", "h2dGenXiMinusVsMultMC", HistType::kTH2D, {axisNch, ptAxis}); - registryMC.add("h2dGenXiPlusVsMultMC", "h2dGenXiPlusVsMultMC", HistType::kTH2D, {axisNch, ptAxis}); registryMC.add("h2dGenXiMinus", "h2dGenXiMinus", HistType::kTH2D, {centAxis, ptAxis}); registryMC.add("h2dGenXiPlus", "h2dGenXiPlus", HistType::kTH2D, {centAxis, ptAxis}); // Histograms for omega (mc) - registryMC.add("h2dGenOmegaMinusVsMultMC_RecoedEvt", "h2dGenOmegaMinusVsMultMC_RecoedEvt", HistType::kTH2D, {axisNch, ptAxis}); - registryMC.add("h2dGenOmegaPlusVsMultMC_RecoedEvt", "h2dGenOmegaPlusVsMultMC_RecoedEvt", HistType::kTH2D, {axisNch, ptAxis}); - registryMC.add("h2dGenOmegaMinusVsMultMC", "h2dGenOmegaMinusVsMultMC", HistType::kTH2D, {axisNch, ptAxis}); - registryMC.add("h2dGenOmegaPlusVsMultMC", "h2dGenOmegaPlusVsMultMC", HistType::kTH2D, {axisNch, ptAxis}); registryMC.add("h2dGenOmegaMinus", "h2dGenOmegaMinus", HistType::kTH2D, {centAxis, ptAxis}); registryMC.add("h2dGenOmegaPlus", "h2dGenOmegaPlus", HistType::kTH2D, {centAxis, ptAxis}); - // Histograms for event loss/splitting - registryMC.add("hGenEvents", "hGenEvents", HistType::kTH2D, {{axisNch}, {4, -0.5f, +3.5f}}); - registryMC.get(HIST("hGenEvents"))->GetYaxis()->SetBinLabel(1, "All gen. events"); - registryMC.get(HIST("hGenEvents"))->GetYaxis()->SetBinLabel(2, "All gen. events in INEL > 0"); - registryMC.get(HIST("hGenEvents"))->GetYaxis()->SetBinLabel(3, "Gen. with at least 1 rec. events"); - registryMC.get(HIST("hGenEvents"))->GetYaxis()->SetBinLabel(4, "Gen. with at least 1 rec. events in INEL > 0"); + if (useMcCentrality) { + registryMC.add("h2dGenXiMinusVsMcCentrality_RecoedEvt", "h2dGenXiMinusVsMcCentrality_RecoedEvt", HistType::kTH2D, {axisCentEstimator, ptAxis}); + registryMC.add("h2dGenXiPlusVsMcCentrality_RecoedEvt", "h2dGenXiPlusVsMcCentrality_RecoedEvt", HistType::kTH2D, {axisCentEstimator, ptAxis}); + registryMC.add("h2dGenXiMinusVsMcCentrality", "h2dGenXiMinusVsMcCentrality", HistType::kTH2D, {axisCentEstimator, ptAxis}); + registryMC.add("h2dGenXiPlusVsMcCentrality", "h2dGenXiPlusVsMcCentrality", HistType::kTH2D, {axisCentEstimator, ptAxis}); + + registryMC.add("h2dGenOmegaMinusVsMcCentrality_RecoedEvt", "h2dGenOmegaMinusVsMcCentrality_RecoedEvt", HistType::kTH2D, {axisCentEstimator, ptAxis}); + registryMC.add("h2dGenOmegaPlusVsMcCentrality_RecoedEvt", "h2dGenOmegaPlusVsMcCentrality_RecoedEvt", HistType::kTH2D, {axisCentEstimator, ptAxis}); + registryMC.add("h2dGenOmegaMinusVsMcCentrality", "h2dGenOmegaMinusVsMcCentrality", HistType::kTH2D, {axisCentEstimator, ptAxis}); + registryMC.add("h2dGenOmegaPlusVsMcCentrality", "h2dGenOmegaPlusVsMcCentrality", HistType::kTH2D, {axisCentEstimator, ptAxis}); + + registryMC.add("hGenEventsVsMcCentrality", "hGenEventsVsMcCentrality", HistType::kTH2D, {axisCentEstimator, {4, -0.5f, +3.5f}}); + registryMC.get(HIST("hGenEventsVsMcCentrality"))->GetYaxis()->SetBinLabel(1, "All gen. events"); + registryMC.get(HIST("hGenEventsVsMcCentrality"))->GetYaxis()->SetBinLabel(2, "All gen. events in INEL > 0"); + registryMC.get(HIST("hGenEventsVsMcCentrality"))->GetYaxis()->SetBinLabel(3, "Gen. with at least 1 rec. events"); + registryMC.get(HIST("hGenEventsVsMcCentrality"))->GetYaxis()->SetBinLabel(4, "Gen. with at least 1 rec. events in INEL > 0"); + } else { + registryMC.add("h2dGenXiMinusVsMultMC_RecoedEvt", "h2dGenXiMinusVsMultMC_RecoedEvt", HistType::kTH2D, {axisNch, ptAxis}); + registryMC.add("h2dGenXiPlusVsMultMC_RecoedEvt", "h2dGenXiPlusVsMultMC_RecoedEvt", HistType::kTH2D, {axisNch, ptAxis}); + registryMC.add("h2dGenXiMinusVsMultMC", "h2dGenXiMinusVsMultMC", HistType::kTH2D, {axisNch, ptAxis}); + registryMC.add("h2dGenXiPlusVsMultMC", "h2dGenXiPlusVsMultMC", HistType::kTH2D, {axisNch, ptAxis}); + + registryMC.add("h2dGenOmegaMinusVsMultMC_RecoedEvt", "h2dGenOmegaMinusVsMultMC_RecoedEvt", HistType::kTH2D, {axisNch, ptAxis}); + registryMC.add("h2dGenOmegaPlusVsMultMC_RecoedEvt", "h2dGenOmegaPlusVsMultMC_RecoedEvt", HistType::kTH2D, {axisNch, ptAxis}); + registryMC.add("h2dGenOmegaMinusVsMultMC", "h2dGenOmegaMinusVsMultMC", HistType::kTH2D, {axisNch, ptAxis}); + registryMC.add("h2dGenOmegaPlusVsMultMC", "h2dGenOmegaPlusVsMultMC", HistType::kTH2D, {axisNch, ptAxis}); + + registryMC.add("hGenEvents", "hGenEvents", HistType::kTH2D, {axisNch, {4, -0.5f, +3.5f}}); + registryMC.get(HIST("hGenEvents"))->GetYaxis()->SetBinLabel(1, "All gen. events"); + registryMC.get(HIST("hGenEvents"))->GetYaxis()->SetBinLabel(2, "All gen. events in INEL > 0"); + registryMC.get(HIST("hGenEvents"))->GetYaxis()->SetBinLabel(3, "Gen. with at least 1 rec. events"); + registryMC.get(HIST("hGenEvents"))->GetYaxis()->SetBinLabel(4, "Gen. with at least 1 rec. events in INEL > 0"); + } registryMC.add("hGenEventCentrality", "hGenEventCentrality", kTH1D, {{101, 0.0f, 101.0f}}); registryMC.add("hCentralityVsNcoll_beforeEvSel", "hCentralityVsNcoll_beforeEvSel", HistType::kTH2D, {centAxis, {50, -0.5f, 49.5f}}); @@ -684,12 +707,29 @@ struct CascadeAnalysisLightIonsDerivedData { if (applyZVtxSelOnMCPV && std::fabs(mcCollision.posZ()) > zVtx) continue; - registryMC.fill(HIST("hGenEvents"), mcCollision.multMCNParticlesEta05(), 0 /* all gen. events*/); + float centralityMC = -1.f; + if (centralityEstimator == Option::kFT0C) + centralityMC = mcCollision.centFT0C(); + if (centralityEstimator == Option::kFT0M) + centralityMC = mcCollision.centFT0M(); + if (centralityEstimator == Option::kFV0A) + centralityMC = mcCollision.centFV0A(); + if (centralityEstimator == Option::kNGlobal) + centralityMC = mcCollision.centNGlobal(); + + if (useMcCentrality) + registryMC.fill(HIST("hGenEventsVsMcCentrality"), centralityMC, 0 /* all gen. events*/); + else + registryMC.fill(HIST("hGenEvents"), mcCollision.multMCNParticlesEta05(), 0 /* all gen. events*/); if (requireInel0OnMC && mcCollision.multMCNParticlesEta10() < 1) { continue; } - registryMC.fill(HIST("hGenEvents"), mcCollision.multMCNParticlesEta05(), 1 /* all gen. events in INEL > 0*/); + + if (useMcCentrality) + registryMC.fill(HIST("hGenEventsVsMcCentrality"), centralityMC, 1 /* all gen. events in INEL > 0*/); + else + registryMC.fill(HIST("hGenEvents"), mcCollision.multMCNParticlesEta05(), 1 /* all gen. events in INEL > 0*/); auto groupedCollisions = getGroupedCollisions(collisions, mcCollision.globalIndex()); // Check if there is at least one of the reconstructed collisions associated to this MC collision @@ -740,14 +780,18 @@ struct CascadeAnalysisLightIonsDerivedData { if (biggestNContribs < collision.multPVTotalContributors()) { biggestNContribs = collision.multPVTotalContributors(); - if (centralityEstimator == Option::kFT0C) - centralitydata = collision.centFT0C(); - if (centralityEstimator == Option::kFT0M) - centralitydata = collision.centFT0M(); - if (centralityEstimator == Option::kFV0A) - centralitydata = collision.centFV0A(); - if (centralityEstimator == Option::kNGlobal) - centralitydata = collision.centNGlobal(); + if (useMcCentrality) { + centralitydata = centralityMC; + } else { + if (centralityEstimator == Option::kFT0C) + centralitydata = collision.centFT0C(); + if (centralityEstimator == Option::kFT0M) + centralitydata = collision.centFT0M(); + if (centralityEstimator == Option::kFV0A) + centralitydata = collision.centFV0A(); + if (centralityEstimator == Option::kNGlobal) + centralitydata = collision.centNGlobal(); + } } nCollisions++; @@ -761,8 +805,13 @@ struct CascadeAnalysisLightIonsDerivedData { registryQC.fill(HIST("hVertexZGen"), mcCollision.posZ()); if (atLeastOne) { - registryMC.fill(HIST("hGenEvents"), mcCollision.multMCNParticlesEta05(), 2 /* at least 1 rec. event*/); - registryMC.fill(HIST("hGenEvents"), mcCollision.multMCNParticlesEta05(), 3 /* at least 1 rec. event in INEL > 0*/); + if (useMcCentrality) { + registryMC.fill(HIST("hGenEventsVsMcCentrality"), centralityMC, 2 /* at least 1 rec. event*/); + registryMC.fill(HIST("hGenEventsVsMcCentrality"), centralityMC, 3 /* at least 1 rec. event in INEL > 0*/); + } else { + registryMC.fill(HIST("hGenEvents"), mcCollision.multMCNParticlesEta05(), 2 /* at least 1 rec. event*/); + registryMC.fill(HIST("hGenEvents"), mcCollision.multMCNParticlesEta05(), 3 /* at least 1 rec. event in INEL > 0*/); + } registryMC.fill(HIST("hGenEventCentrality"), centralitydata); } } @@ -955,6 +1004,25 @@ struct CascadeAnalysisLightIonsDerivedData { multiplicityMcRec = RecCol.multNTracksGlobal(); } + if (useMcCentrality) { + const float recoCentrality = centralityMcRec; + centralityMcRec = -1.f; + + if (RecCol.has_straMCCollision()) { + auto mcCollision = RecCol.template straMCCollision_as(); + if (centralityEstimator == Option::kFT0C) + centralityMcRec = mcCollision.centFT0C(); + if (centralityEstimator == Option::kFT0M) + centralityMcRec = mcCollision.centFT0M(); + if (centralityEstimator == Option::kFV0A) + centralityMcRec = mcCollision.centFV0A(); + if (centralityEstimator == Option::kNGlobal) + centralityMcRec = mcCollision.centNGlobal(); + } + + registryMC.fill(HIST("hEventVsGenCentrality_truerec"), recoCentrality, centralityMcRec); + } + registryMC.fill(HIST("number_of_events_mc_rec_vs_centrality"), 0, centralityMcRec); // event selections @@ -1148,47 +1216,86 @@ struct CascadeAnalysisLightIonsDerivedData { } float centralityMC = 100.5f; + float centralityMCtrue = -1.f; + + if (centralityEstimator == Option::kFT0C) + centralityMCtrue = mcCollision.centFT0C(); + if (centralityEstimator == Option::kFT0M) + centralityMCtrue = mcCollision.centFT0M(); + if (centralityEstimator == Option::kFV0A) + centralityMCtrue = mcCollision.centFV0A(); + if (centralityEstimator == Option::kNGlobal) + centralityMCtrue = mcCollision.centNGlobal(); if (listBestCollisionIdx[mcCollision.globalIndex()] > -1) { auto collision = RecCols.iteratorAt(listBestCollisionIdx[mcCollision.globalIndex()]); - if (centralityEstimator == Option::kFT0C) - centralityMC = collision.centFT0C(); - if (centralityEstimator == Option::kFT0M) - centralityMC = collision.centFT0M(); - if (centralityEstimator == Option::kFV0A) - centralityMC = collision.centFV0A(); - if (centralityEstimator == Option::kNGlobal) - centralityMC = collision.centNGlobal(); + + if (useMcCentrality) { + centralityMC = centralityMCtrue; + } else { + if (centralityEstimator == Option::kFT0C) + centralityMC = collision.centFT0C(); + if (centralityEstimator == Option::kFT0M) + centralityMC = collision.centFT0M(); + if (centralityEstimator == Option::kFV0A) + centralityMC = collision.centFV0A(); + if (centralityEstimator == Option::kNGlobal) + centralityMC = collision.centNGlobal(); + } if (cascMC.pdgCode() == kXiMinus && std::abs(cascMC.rapidityMC(0)) < rapcut) { - registryMC.fill(HIST("h2dGenXiMinusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + if (useMcCentrality) + registryMC.fill(HIST("h2dGenXiMinusVsMcCentrality_RecoedEvt"), centralityMCtrue, ptmc); + else + registryMC.fill(HIST("h2dGenXiMinusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); } if (cascMC.pdgCode() == kXiPlusBar && std::abs(cascMC.rapidityMC(0)) < rapcut) { - registryMC.fill(HIST("h2dGenXiPlusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + if (useMcCentrality) + registryMC.fill(HIST("h2dGenXiPlusVsMcCentrality_RecoedEvt"), centralityMCtrue, ptmc); + else + registryMC.fill(HIST("h2dGenXiPlusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); } if (cascMC.pdgCode() == kOmegaMinus && std::abs(cascMC.rapidityMC(2)) < rapcut) { - registryMC.fill(HIST("h2dGenOmegaMinusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + if (useMcCentrality) + registryMC.fill(HIST("h2dGenOmegaMinusVsMcCentrality_RecoedEvt"), centralityMCtrue, ptmc); + else + registryMC.fill(HIST("h2dGenOmegaMinusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); } if (cascMC.pdgCode() == kOmegaPlusBar && std::abs(cascMC.rapidityMC(2)) < rapcut) { - registryMC.fill(HIST("h2dGenOmegaPlusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + if (useMcCentrality) + registryMC.fill(HIST("h2dGenOmegaPlusVsMcCentrality_RecoedEvt"), centralityMCtrue, ptmc); + else + registryMC.fill(HIST("h2dGenOmegaPlusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); } } if (cascMC.pdgCode() == kXiMinus && std::abs(cascMC.rapidityMC(0)) < rapcut) { registryMC.fill(HIST("h2dGenXiMinus"), centralityMC, ptmc); - registryMC.fill(HIST("h2dGenXiMinusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + if (useMcCentrality) + registryMC.fill(HIST("h2dGenXiMinusVsMcCentrality"), centralityMCtrue, ptmc); + else + registryMC.fill(HIST("h2dGenXiMinusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); } if (cascMC.pdgCode() == kXiPlusBar && std::abs(cascMC.rapidityMC(0)) < rapcut) { registryMC.fill(HIST("h2dGenXiPlus"), centralityMC, ptmc); - registryMC.fill(HIST("h2dGenXiPlusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + if (useMcCentrality) + registryMC.fill(HIST("h2dGenXiPlusVsMcCentrality"), centralityMCtrue, ptmc); + else + registryMC.fill(HIST("h2dGenXiPlusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); } if (cascMC.pdgCode() == kOmegaMinus && std::abs(cascMC.rapidityMC(2)) < rapcut) { registryMC.fill(HIST("h2dGenOmegaMinus"), centralityMC, ptmc); - registryMC.fill(HIST("h2dGenOmegaMinusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + if (useMcCentrality) + registryMC.fill(HIST("h2dGenOmegaMinusVsMcCentrality"), centralityMCtrue, ptmc); + else + registryMC.fill(HIST("h2dGenOmegaMinusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); } if (cascMC.pdgCode() == kOmegaPlusBar && std::abs(cascMC.rapidityMC(2)) < rapcut) { registryMC.fill(HIST("h2dGenOmegaPlus"), centralityMC, ptmc); - registryMC.fill(HIST("h2dGenOmegaPlusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + if (useMcCentrality) + registryMC.fill(HIST("h2dGenOmegaPlusVsMcCentrality"), centralityMCtrue, ptmc); + else + registryMC.fill(HIST("h2dGenOmegaPlusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); } } // cascMC loop } diff --git a/PWGLF/Tasks/Strangeness/cascadecorrelations.cxx b/PWGLF/Tasks/Strangeness/cascadecorrelations.cxx index 48f11561b4f..d8fbf39ca88 100644 --- a/PWGLF/Tasks/Strangeness/cascadecorrelations.cxx +++ b/PWGLF/Tasks/Strangeness/cascadecorrelations.cxx @@ -932,9 +932,9 @@ struct CascadeCorrelations { ConfigurableAxis axisVtxZ{"axisVtxZ", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; // ConfigurableAxis axisMult{"axisMult", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 100, 1000}, "Mixing bins - multiplicity"}; // using BinningType = ColumnBinningPolicy; - // BinningType colBinning{{axisVtxZ, axisMult}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + // BinningType colBinning{{axisVtxZ, axisMult}}; using BinningType = ColumnBinningPolicy; - BinningType colBinning{{axisVtxZ}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + BinningType colBinning{{axisVtxZ}}; void processSameEvent(MyCollisionsMult::iterator const& collision, MyCascades const& Cascades, FullTracksExtIU const&, aod::BCsWithTimestamps const&) { diff --git a/PWGLF/Tasks/Strangeness/cascpostprocessing.cxx b/PWGLF/Tasks/Strangeness/cascpostprocessing.cxx index 2b6c5d094c3..00bb3a69e10 100644 --- a/PWGLF/Tasks/Strangeness/cascpostprocessing.cxx +++ b/PWGLF/Tasks/Strangeness/cascpostprocessing.cxx @@ -271,11 +271,17 @@ struct LfCascpostprocessing { registry.add("hTPCNSigmaBachPi", "hTPCNSigmaBachPi", {HistType::kTH2F, {ptAxisPID, {120, -6.0f, 6.0f}}}); registry.add("hTPCNSigmaBachKa", "hTPCNSigmaBachKa", {HistType::kTH2F, {ptAxisPID, {120, -6.0f, 6.0f}}}); registry.add("hTOFNSigmaPosPi", "hTOFNSigmaPosPi", {HistType::kTH1F, {{120, -6.0f, 6.0f}}}); + registry.add("hTOFNSigmaPosPiVsPt", "hTOFNSigmaPosPiVsPt", {HistType::kTH2F, {ptAxisPID, {120, -6.0f, 6.0f}}}); registry.add("hTOFNSigmaNegPi", "hTOFNSigmaNegPi", {HistType::kTH1F, {{120, -6.0f, 6.0f}}}); + registry.add("hTOFNSigmaNegPiVsPt", "hTOFNSigmaNegPiVsPt", {HistType::kTH2F, {ptAxisPID, {120, -6.0f, 6.0f}}}); registry.add("hTOFNSigmaPosPr", "hTOFNSigmaPosPr", {HistType::kTH1F, {{120, -6.0f, 6.0f}}}); + registry.add("hTOFNSigmaPosPrVsPt", "hTOFNSigmaPosPrVsPt", {HistType::kTH2F, {ptAxisPID, {120, -6.0f, 6.0f}}}); registry.add("hTOFNSigmaNegPr", "hTOFNSigmaNegPr", {HistType::kTH1F, {{120, -6.0f, 6.0f}}}); + registry.add("hTOFNSigmaNegPrVsPt", "hTOFNSigmaNegPrVsPt", {HistType::kTH2F, {ptAxisPID, {120, -6.0f, 6.0f}}}); registry.add("hTOFNSigmaBachPi", "hTOFNSigmaBachPi", {HistType::kTH1F, {{120, -6.0f, 6.0f}}}); + registry.add("hTOFNSigmaBachPiVsPt", "hTOFNSigmaBachPiVsPt", {HistType::kTH2F, {ptAxisPID, {120, -6.0f, 6.0f}}}); registry.add("hTOFNSigmaBachKa", "hTOFNSigmaBachKa", {HistType::kTH1F, {{120, -6.0f, 6.0f}}}); + registry.add("hTOFNSigmaBachKaVsPt", "hTOFNSigmaBachKaVsPt", {HistType::kTH2F, {ptAxisPID, {120, -6.0f, 6.0f}}}); registry.add("hCascMinusEtaPos", "hCascMinusEtaPos", {HistType::kTH1F, {{100, -1.0f, 1.0f}}}); registry.add("hCascMinusEtaNeg", "hCascMinusEtaNeg", {HistType::kTH1F, {{100, -1.0f, 1.0f}}}); registry.add("hCascMinusEtaBach", "hCascMinusEtaBach", {HistType::kTH1F, {{100, -1.0f, 1.0f}}}); @@ -550,7 +556,7 @@ struct LfCascpostprocessing { if (TMath::Abs(candidate.massxi() - o2::constants::physics::MassXiMinus) < masswintpc) { isCandidate = 1; } - } else if (!isXi) { + } else { if (isMC) { isCorrectlyRec = ((TMath::Abs(candidate.mcPdgCode()) == PDG_t::kOmegaMinus) && (candidate.isPrimary() == 1)) ? 1 : 0; } @@ -563,7 +569,13 @@ struct LfCascpostprocessing { registry.fill(HIST("hTPCNSigmaPosPr"), candidate.pt(), candidate.ntpcsigmapospr()); registry.fill(HIST("hTPCNSigmaNegPi"), candidate.pt(), candidate.ntpcsigmanegpi()); registry.fill(HIST("hTOFNSigmaPosPr"), candidate.ntofsigmapospr()); + if (candidate.poshastof()) { + registry.fill(HIST("hTOFNSigmaPosPrVsPt"), candidate.pt(), candidate.ntofsigmapospr()); + } registry.fill(HIST("hTOFNSigmaNegPi"), candidate.ntofsigmanegpi()); + if (candidate.neghastof()) { + registry.fill(HIST("hTOFNSigmaNegPiVsPt"), candidate.pt(), candidate.ntofsigmanegpi()); + } registry.fill(HIST("hCascMinusEtaPos"), candidate.poseta()); registry.fill(HIST("hCascMinusEtaNeg"), candidate.negeta()); registry.fill(HIST("hCascMinusEtaBach"), candidate.bacheta()); @@ -571,14 +583,26 @@ struct LfCascpostprocessing { registry.fill(HIST("hTPCNSigmaPosPi"), candidate.pt(), candidate.ntpcsigmapospi()); registry.fill(HIST("hTPCNSigmaNegPr"), candidate.pt(), candidate.ntpcsigmanegpr()); registry.fill(HIST("hTOFNSigmaPosPi"), candidate.ntofsigmapospi()); + if (candidate.poshastof()) { + registry.fill(HIST("hTOFNSigmaPosPiVsPt"), candidate.pt(), candidate.ntofsigmapospi()); + } registry.fill(HIST("hTOFNSigmaNegPr"), candidate.ntofsigmanegpr()); + if (candidate.neghastof()) { + registry.fill(HIST("hTOFNSigmaNegPrVsPt"), candidate.pt(), candidate.ntofsigmanegpr()); + } } if (isXi) { registry.fill(HIST("hTPCNSigmaBachPi"), candidate.pt(), candidate.ntpcsigmabachpi()); registry.fill(HIST("hTOFNSigmaBachPi"), candidate.ntofsigmabachpi()); + if (candidate.bachhastof()) { + registry.fill(HIST("hTOFNSigmaBachPiVsPt"), candidate.pt(), candidate.ntofsigmabachpi()); + } } else { registry.fill(HIST("hTPCNSigmaBachKa"), candidate.pt(), candidate.ntpcsigmabachka()); registry.fill(HIST("hTOFNSigmaBachKa"), candidate.ntofsigmabachka()); + if (candidate.bachhastof()) { + registry.fill(HIST("hTOFNSigmaBachKaVsPt"), candidate.pt(), candidate.ntofsigmabachka()); + } } } // registry.fill(HIST("hPosITSHits"), candidate.positshits()); diff --git a/PWGLF/Tasks/Strangeness/derivedXi0analysis.cxx b/PWGLF/Tasks/Strangeness/derivedXi0analysis.cxx new file mode 100644 index 00000000000..bf84dd619ba --- /dev/null +++ b/PWGLF/Tasks/Strangeness/derivedXi0analysis.cxx @@ -0,0 +1,1271 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file derivedXi0Analysis.cxx +/// \brief Xi0 (--> Lambda pi0 --> Lambda gamma gamma) analysis task using strangeness derived data (produced with the Xi0 builder) +/// +/// \author Romain Schotter , Austrian Academy of Sciences & MBI +// +// Xi0 analysis task +// ================= +// +// This code loops over the Xi0Cores table produced by the sigma0builder +// (PWGLF/TableProducer/Strangeness/sigma0builder.cxx, fillXi0Tables = true) +// and produces some standard analysis output. It is meant to be run over +// strangeness derived data. +// +// The Xi0 candidate is built out of three V0s: two photon conversions +// (forming the pi0) and one Lambda. The daughter V0s are reached by +// de-referencing the V0Cores table through the Xi0Indices table. +// +// Three process functions are provided: +// - processRealData : real data +// - processMonteCarlo : reconstructed information in MC +// - processGenerated : pure generated information in MC (from CascMCCores) +// +// N.B.: when running over reconstructed MC information, the rapidity and the +// pT used both in the selections and in the histograms are the generated +// ones, so that the numerator and the denominator of the +// acceptance x efficiency share exactly the same definition. +// +// Comments, questions, complaints, suggestions? +// Please write to: +// romain.schotter@cern.ch +// + +#include "PWGLF/DataModel/LFSigmaTables.h" +#include "PWGLF/DataModel/LFStrangenessPIDTables.h" +#include "PWGLF/DataModel/LFStrangenessTables.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/ctpRateFetcher.h" +#include "Common/Core/RecoDecay.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +// simple checkers, but ensure 64 bit integers +#define BITSET(var, nbit) ((var) |= (static_cast(1) << static_cast(nbit))) +#define BITCHECK(var, nbit) ((var) & (static_cast(1) << static_cast(nbit))) + +// Xi0 candidates: cores + collision reference + indices to the daughter V0s. +// N.B.: aod::Xi0Indices and aod::Xi0MCIndices must never be joined together, +// since they share the very same column names (photon1Index, ...). +using Xi0Candidates = soa::Join; +using Xi0McCandidates = soa::Join; + +using V0Candidates = soa::Join; +using DauTracks = soa::Join; + +enum CentEstimator { + kCentFT0C = 0, + kCentFT0M, + kCentFT0CVariant1, + kCentMFT, + kCentNGlobal, + kCentFV0A +}; + +struct DerivedXi0Analysis { + Service ccdb; + ctpRateFetcher rateFetcher; + + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // Xi0 proper decay length, in cm (PDG: tau = 2.90e-10 s) + static constexpr float CtauXi0 = 8.71f; + // number of QA histogram sets: before and after the candidate selections + static constexpr int NSelectionStages = 2; + + //__________________________________________________ + // Selection bits: one per selection criterion, so that the full selection is + // a single mask operation and the bookkeeping fits in a single histogram + enum Selectionbits : int { // o2-linter: disable=name/enum (bit names follow derivedlambdakzeroanalysis) + // photon selections (both photons of the pi0 have to pass) + selPhotonV0Type = 0, + selPhotonMass, + selPhotonRapidity, + selPhotonDauEta, + selPhotonDCADauToPV, + selPhotonDCADau, + selPhotonRadius, + selPhotonCosPA, + selPhotonArmenteros, + selPhotonPt, + selPhotonTPCCrossedRows, + selPhotonTPCPID, + // Lambda selections, common to Lambda and anti-Lambda + selLambdaV0Type, + selLambdaRapidity, + selLambdaDauEta, + selLambdaDCAPosToPV, + selLambdaDCANegToPV, + selLambdaDCADau, + selLambdaRadius, + selLambdaCosPA, + selLambdaLifetime, + selLambdaTPCCrossedRows, + selLambdaITSClusters, + // Lambda selections, specific to one of the two mass hypotheses + selLambdaMass, + selAntiLambdaMass, + selLambdaArmenteros, // Armenteros alpha > 0 --> the positive daughter is the baryon + selAntiLambdaArmenteros, // Armenteros alpha < 0 --> the negative daughter is the baryon + selTPCPIDPositiveProton, + selTPCPIDNegativePion, + selTPCPIDNegativeProton, + selTPCPIDPositivePion, + // pi0 and Xi0 (cascade) selections + selPi0Mass, + selPi0Radius, + selPi0CosPA, + selPi0DCADau, + selXi0Radius, + selXi0CosPA, + selXi0DCADau, + selXi0DCAxyToPV, + selXi0DCAzToPV, + selXi0Lifetime, + selXi0Rapidity, + selXi0Pt, + // MC tagging + selConsiderXi0, + selConsiderAntiXi0, + selPhysPrimXi0, + selPhysPrimAntiXi0, + }; + + uint64_t maskTopological = 0; + uint64_t maskXi0Specific = 0; + uint64_t maskAntiXi0Specific = 0; + uint64_t maskSelectionXi0 = 0; + uint64_t maskSelectionAntiXi0 = 0; + + //__________________________________________________ + // Event level + Configurable centralityEstimator{"centralityEstimator", kCentFT0M, "Run 3 centrality estimator (0:CentFT0C, 1:CentFT0M, 2:CentFT0CVariant1, 3:CentMFT, 4:CentNGlobal, 5:CentFV0A)"}; + Configurable fGetIR{"fGetIR", false, "Flag to retrieve the IR info."}; + Configurable fIRCrashOnNull{"fIRCrashOnNull", false, "Flag to avoid CTP RateFetcher crash."}; + Configurable irSource{"irSource", "T0VTX", "Estimator of the interaction rate (Recommended: pp --> T0VTX, Pb-Pb --> ZNC hadronic)"}; + + struct : ConfigurableGroup { + std::string prefix = "eventSelections"; // JSON group name + Configurable requireSel8{"requireSel8", true, "require sel8 event selection"}; + Configurable requireTriggerTVX{"requireTriggerTVX", true, "require FT0 vertex (acceptable FT0C-FT0A time difference) at trigger level"}; + Configurable rejectITSROFBorder{"rejectITSROFBorder", true, "reject events at ITS ROF border"}; + Configurable rejectTFBorder{"rejectTFBorder", true, "reject events at TF border"}; + Configurable requireIsVertexITSTPC{"requireIsVertexITSTPC", false, "require events with at least one ITS-TPC track"}; + Configurable requireIsGoodZvtxFT0VsPV{"requireIsGoodZvtxFT0VsPV", true, "require events with PV position along z consistent (within 1 cm) between PV reconstructed using tracks and PV using FT0 A-C time difference"}; + Configurable requireIsVertexTOFmatched{"requireIsVertexTOFmatched", false, "require events with at least one of vertex contributors matched to TOF"}; + Configurable requireIsVertexTRDmatched{"requireIsVertexTRDmatched", false, "require events with at least one of vertex contributors matched to TRD"}; + Configurable rejectSameBunchPileup{"rejectSameBunchPileup", true, "reject collisions in case of pileup with another collision in the same foundBC"}; + Configurable requireNoCollInTimeRangeStd{"requireNoCollInTimeRangeStd", false, "reject collisions corrupted by the cannibalism, with other collisions within +/- 2 microseconds or mult above a certain threshold in -4 - -2 microseconds"}; + Configurable requireNoCollInTimeRangeStrict{"requireNoCollInTimeRangeStrict", false, "reject collisions corrupted by the cannibalism, with other collisions within +/- 10 microseconds"}; + Configurable requireNoCollInTimeRangeNarrow{"requireNoCollInTimeRangeNarrow", false, "reject collisions corrupted by the cannibalism, with other collisions within +/- 2 microseconds"}; + Configurable requireNoCollInROFStd{"requireNoCollInROFStd", false, "reject collisions corrupted by the cannibalism, with other collisions within the same ITS ROF with mult. above a certain threshold"}; + Configurable requireNoCollInROFStrict{"requireNoCollInROFStrict", false, "reject collisions corrupted by the cannibalism, with other collisions within the same ITS ROF"}; + Configurable requireINEL0{"requireINEL0", true, "require INEL>0 event selection"}; + Configurable requireINEL1{"requireINEL1", false, "require INEL>1 event selection"}; + Configurable maxZVtxPosition{"maxZVtxPosition", 10., "max Z vtx position"}; + Configurable useEvtSelInDenomEff{"useEvtSelInDenomEff", false, "Consider event selections in the recoed <-> gen collision association for the denominator (or numerator) of the acc. x eff. (or signal loss)?"}; + Configurable applyZVtxSelOnMCPV{"applyZVtxSelOnMCPV", false, "Apply Z-vtx cut on the PV of the generated collision?"}; + Configurable useFT0CbasedOccupancy{"useFT0CbasedOccupancy", false, "Use sum of FT0-C amplitudes for estimating occupancy? (if not, use track-based definition)"}; + Configurable minOccupancy{"minOccupancy", -1, "minimum occupancy from neighbouring collisions"}; + Configurable maxOccupancy{"maxOccupancy", -1, "maximum occupancy from neighbouring collisions"}; + Configurable minIR{"minIR", -1, "minimum IR collisions"}; + Configurable maxIR{"maxIR", -1, "maximum IR collisions"}; + } eventSelections; + + //__________________________________________________ + // Photon (from the pi0) selections, applied on the de-referenced V0Cores + struct : ConfigurableGroup { + std::string prefix = "photonSelections"; // JSON group name + Configurable v0TypeSelection{"v0TypeSelection", 7, "select on a certain V0 type (leave negative if no selection desired)"}; + Configurable maxMass{"maxMass", 0.1, "Max photon mass (GeV/c^2)"}; + Configurable minRapidity{"minRapidity", -0.8, "Min photon rapidity"}; + Configurable maxRapidity{"maxRapidity", 0.8, "Max photon rapidity"}; + Configurable maxDauEta{"maxDauEta", 0.8, "Max |eta| of the daughter tracks"}; + Configurable minDCADauToPV{"minDCADauToPV", 0.0, "Min DCA of the daughter tracks to PV (cm)"}; + Configurable maxDCADau{"maxDCADau", 3.5, "Max DCA between the V0 daughters (cm)"}; + Configurable minRadius{"minRadius", 0.0, "Min photon conversion radius (cm)"}; + Configurable maxRadius{"maxRadius", 240.0, "Max photon conversion radius (cm)"}; + Configurable minCosPA{"minCosPA", 0.8, "Min photon cosine of pointing angle"}; + Configurable maxQt{"maxQt", 0.05, "Max qt (Armenteros-Podolanski) (GeV/c)"}; + Configurable maxAlpha{"maxAlpha", 0.95, "Max |alpha| (Armenteros-Podolanski)"}; + Configurable minPt{"minPt", 0.0, "Min photon pT (GeV/c)"}; + Configurable maxPt{"maxPt", 50.0, "Max photon pT (GeV/c)"}; + Configurable minTPCCrossedRows{"minTPCCrossedRows", 30, "Min number of TPC crossed rows of the daughter tracks"}; + Configurable minTPCNSigmaEl{"minTPCNSigmaEl", -7, "Min TPC NSigma (electron) of the daughter tracks"}; + Configurable maxTPCNSigmaEl{"maxTPCNSigmaEl", +7, "Max TPC NSigma (electron) of the daughter tracks"}; + } photonSelections; + + //__________________________________________________ + // Lambda selections, applied on the de-referenced V0Cores + struct : ConfigurableGroup { + std::string prefix = "lambdaSelections"; // JSON group name + Configurable v0TypeSelection{"v0TypeSelection", 1, "select on a certain V0 type (leave negative if no selection desired)"}; + Configurable massWindow{"massWindow", 0.015, "Lambda mass window around the PDG value (GeV/c^2)"}; + Configurable minRapidity{"minRapidity", -0.8, "Min Lambda rapidity"}; + Configurable maxRapidity{"maxRapidity", 0.8, "Max Lambda rapidity"}; + Configurable maxDauEta{"maxDauEta", 0.8, "Max |eta| of the daughter tracks"}; + Configurable minDCAPosToPV{"minDCAPosToPV", 0.05, "Min DCA of the positive daughter to PV (cm)"}; + Configurable minDCANegToPV{"minDCANegToPV", 0.05, "Min DCA of the negative daughter to PV (cm)"}; + Configurable maxDCADau{"maxDCADau", 1.0, "Max DCA between the V0 daughters (cm)"}; + Configurable minRadius{"minRadius", 0.5, "Min Lambda decay radius (cm)"}; + Configurable maxRadius{"maxRadius", 200.0, "Max Lambda decay radius (cm)"}; + Configurable minCosPA{"minCosPA", 0.95, "Min Lambda cosine of pointing angle (w.r.t. the PV)"}; + Configurable maxLifetime{"maxLifetime", 30.0, "Max Lambda proper lifetime, m*L/p (cm)"}; + Configurable minTPCCrossedRows{"minTPCCrossedRows", 70, "Min number of TPC crossed rows of the daughter tracks"}; + Configurable minITSclusters{"minITSclusters", -1, "Min number of ITS clusters of the daughter tracks (leave negative if no selection desired)"}; + Configurable maxTPCNSigmaPr{"maxTPCNSigmaPr", 5, "Max |TPC NSigma| (proton) of the baryon daughter"}; + Configurable maxTPCNSigmaPi{"maxTPCNSigmaPi", 5, "Max |TPC NSigma| (pion) of the meson daughter"}; + // Lambda / anti-Lambda are told apart with the Armenteros-Podolanski alpha: + // the baryon takes most of the momentum, so alpha > 0 for Lambda (positive + // daughter = proton) and alpha < 0 for anti-Lambda (negative daughter = anti-proton) + Configurable minAbsAlpha{"minAbsAlpha", 0.25, "Min |alpha| (Armenteros-Podolanski) to tag the Lambda charge"}; + Configurable maxAbsAlpha{"maxAbsAlpha", 1.0, "Max |alpha| (Armenteros-Podolanski)"}; + } lambdaSelections; + + //__________________________________________________ + // Xi0 selections, applied on the cascade itself + struct : ConfigurableGroup { + std::string prefix = "xi0Selections"; // JSON group name + Configurable minRapidity{"minRapidity", -0.5, "Min Xi0 rapidity"}; + Configurable maxRapidity{"maxRapidity", 0.5, "Max Xi0 rapidity"}; + Configurable minPt{"minPt", 0.0, "Min Xi0 pT (GeV/c)"}; + Configurable maxPt{"maxPt", 50.0, "Max Xi0 pT (GeV/c)"}; + Configurable pi0MassWindow{"pi0MassWindow", 0.035, "pi0 mass window around the PDG value (GeV/c^2)"}; + Configurable minPi0Radius{"minPi0Radius", -1, "Min pi0 decay radius (cm), leave negative if no selection desired"}; + Configurable maxPi0Radius{"maxPi0Radius", -1, "Max pi0 decay radius (cm), leave negative if no selection desired"}; + Configurable minPi0CosPA{"minPi0CosPA", -2, "Min pi0 cosine of pointing angle, leave below -1 if no selection desired"}; + Configurable maxDCAPi0Daughters{"maxDCAPi0Daughters", -1, "Max DCA between the two photons (cm), leave negative if no selection desired"}; + Configurable minCascRadius{"minCascRadius", 0.5, "Min Xi0 decay radius (cm)"}; + Configurable maxCascRadius{"maxCascRadius", 200.0, "Max Xi0 decay radius (cm)"}; + Configurable minCascCosPA{"minCascCosPA", 0.98, "Min Xi0 cosine of pointing angle"}; + Configurable maxDCACascDaughters{"maxDCACascDaughters", 1.0, "Max DCA between the Xi0 daughters (cm)"}; + Configurable maxDCAxyCascToPV{"maxDCAxyCascToPV", 1.0, "Max |DCAxy| of the Xi0 to the PV (cm), leave negative if no selection desired"}; + Configurable maxDCAzCascToPV{"maxDCAzCascToPV", -1, "Max |DCAz| of the Xi0 to the PV (cm), leave negative if no selection desired"}; + Configurable maxLifetime{"maxLifetime", 3.0, "Max Xi0 proper lifetime, in units of c*tau"}; + } xi0Selections; + + //__________________________________________________ + // MC-specific selections + struct : ConfigurableGroup { + std::string prefix = "mcSelections"; // JSON group name + Configurable doMCAssociation{"doMCAssociation", false, "Keep only candidates whose full decay chain is matched to a true Xi0/anti-Xi0"}; + Configurable requirePhysicalPrimary{"requirePhysicalPrimary", true, "Keep only physical primary Xi0, at reconstructed and generated level"}; + } mcSelections; + + //__________________________________________________ + // QA switches + Configurable doEventQA{"doEventQA", true, "do event QA histograms"}; + Configurable doCandidateQA{"doCandidateQA", true, "do candidate-level QA histograms"}; + Configurable doMCQA{"doMCQA", true, "do MC-specific QA histograms (pT resolution, background composition)"}; + + //__________________________________________________ + // Axes + ConfigurableAxis axisCentrality{"axisCentrality", {VARIABLE_WIDTH, 0.0f, 1.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.0f}, "Centrality (%)"}; + ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.0f, 0.5f, 1.0f, 1.5f, 2.0f, 2.5f, 3.0f, 3.5f, 4.0f, 4.5f, 5.0f, 6.0f, 7.0f, 8.0f, 10.0f, 12.0f, 15.0f, 20.0f}, "#it{p}_{T} (GeV/#it{c})"}; + ConfigurableAxis axisXi0Mass{"axisXi0Mass", {200, 1.2f, 1.5f}, "#it{M}_{#Lambda#pi^{0}} (GeV/#it{c}^{2})"}; + ConfigurableAxis axisPi0Mass{"axisPi0Mass", {200, 0.0f, 0.4f}, "#it{M}_{#gamma#gamma} (GeV/#it{c}^{2})"}; + ConfigurableAxis axisLambdaMass{"axisLambdaMass", {200, 1.07f, 1.17f}, "#it{M}_{p#pi} (GeV/#it{c}^{2})"}; + ConfigurableAxis axisRadius{"axisRadius", {200, 0.0f, 100.0f}, "Decay radius (cm)"}; + ConfigurableAxis axisCosPA{"axisCosPA", {200, 0.9f, 1.0f}, "cos(#theta_{PA})"}; + ConfigurableAxis axisDCADau{"axisDCADau", {200, 0.0f, 5.0f}, "DCA between daughters (cm)"}; + ConfigurableAxis axisDCAToPV{"axisDCAToPV", {200, -5.0f, 5.0f}, "DCA to PV (cm)"}; + ConfigurableAxis axisLifetime{"axisLifetime", {200, 0.0f, 50.0f}, "Proper lifetime (cm)"}; + ConfigurableAxis axisRapidity{"axisRapidity", {200, -1.0f, 1.0f}, "#it{y}"}; + ConfigurableAxis axisArmAlpha{"axisArmAlpha", {200, -1.0f, 1.0f}, "#alpha (Armenteros-Podolanski)"}; + ConfigurableAxis axisArmQt{"axisArmQt", {200, 0.0f, 0.5f}, "#it{q}_{T} (GeV/#it{c})"}; + ConfigurableAxis axisNch{"axisNch", {300, 0.0f, 3000.0f}, "#it{N}_{ch}"}; + ConfigurableAxis axisPtResolution{"axisPtResolution", {200, -1.0f, 1.0f}, "(#it{p}_{T}^{rec} - #it{p}_{T}^{gen}) / #it{p}_{T}^{gen}"}; + ConfigurableAxis axisVtxZ{"axisVtxZ", {40, -20.0f, 20.0f}, "PV #it{z} (cm)"}; + ConfigurableAxis axisPDGCode{"axisPDGCode", {10001, -5000.5f, 5000.5f}, "PDG code"}; + ConfigurableAxis axisNCandidates{"axisNCandidates", {10, -0.5f, 9.5f}, "Number of candidates"}; + + void init(InitContext const&) + { + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + + //_______________________________________________ + // Assemble the selection masks once and for all + maskTopological = 0; + BITSET(maskTopological, selPhotonV0Type); + BITSET(maskTopological, selPhotonMass); + BITSET(maskTopological, selPhotonRapidity); + BITSET(maskTopological, selPhotonDauEta); + BITSET(maskTopological, selPhotonDCADauToPV); + BITSET(maskTopological, selPhotonDCADau); + BITSET(maskTopological, selPhotonRadius); + BITSET(maskTopological, selPhotonCosPA); + BITSET(maskTopological, selPhotonArmenteros); + BITSET(maskTopological, selPhotonPt); + BITSET(maskTopological, selPhotonTPCCrossedRows); + BITSET(maskTopological, selPhotonTPCPID); + BITSET(maskTopological, selLambdaV0Type); + BITSET(maskTopological, selLambdaRapidity); + BITSET(maskTopological, selLambdaDauEta); + BITSET(maskTopological, selLambdaDCAPosToPV); + BITSET(maskTopological, selLambdaDCANegToPV); + BITSET(maskTopological, selLambdaDCADau); + BITSET(maskTopological, selLambdaRadius); + BITSET(maskTopological, selLambdaCosPA); + BITSET(maskTopological, selLambdaLifetime); + BITSET(maskTopological, selLambdaTPCCrossedRows); + BITSET(maskTopological, selLambdaITSClusters); + BITSET(maskTopological, selPi0Mass); + BITSET(maskTopological, selPi0Radius); + BITSET(maskTopological, selPi0CosPA); + BITSET(maskTopological, selPi0DCADau); + BITSET(maskTopological, selXi0Radius); + BITSET(maskTopological, selXi0CosPA); + BITSET(maskTopological, selXi0DCADau); + BITSET(maskTopological, selXi0DCAxyToPV); + BITSET(maskTopological, selXi0DCAzToPV); + BITSET(maskTopological, selXi0Lifetime); + BITSET(maskTopological, selXi0Rapidity); + BITSET(maskTopological, selXi0Pt); + + maskXi0Specific = 0; + BITSET(maskXi0Specific, selLambdaMass); + BITSET(maskXi0Specific, selLambdaArmenteros); + BITSET(maskXi0Specific, selTPCPIDPositiveProton); + BITSET(maskXi0Specific, selTPCPIDNegativePion); + BITSET(maskXi0Specific, selConsiderXi0); + + maskAntiXi0Specific = 0; + BITSET(maskAntiXi0Specific, selAntiLambdaMass); + BITSET(maskAntiXi0Specific, selAntiLambdaArmenteros); + BITSET(maskAntiXi0Specific, selTPCPIDNegativeProton); + BITSET(maskAntiXi0Specific, selTPCPIDPositivePion); + BITSET(maskAntiXi0Specific, selConsiderAntiXi0); + + if (mcSelections.requirePhysicalPrimary) { + BITSET(maskXi0Specific, selPhysPrimXi0); + BITSET(maskAntiXi0Specific, selPhysPrimAntiXi0); + } + + maskSelectionXi0 = maskTopological | maskXi0Specific; + maskSelectionAntiXi0 = maskTopological | maskAntiXi0Specific; + + //_______________________________________________ + // Event selection bookkeeping + auto hEventSelection = histos.add("hEventSelection", "hEventSelection", kTH1D, {{23, -0.5f, +22.5f}}); + hEventSelection->GetXaxis()->SetBinLabel(1, "All collisions"); + hEventSelection->GetXaxis()->SetBinLabel(2, "sel8 cut"); + hEventSelection->GetXaxis()->SetBinLabel(3, "kIsTriggerTVX"); + hEventSelection->GetXaxis()->SetBinLabel(4, "kNoITSROFrameBorder"); + hEventSelection->GetXaxis()->SetBinLabel(5, "kNoTimeFrameBorder"); + hEventSelection->GetXaxis()->SetBinLabel(6, "posZ cut"); + hEventSelection->GetXaxis()->SetBinLabel(7, "kIsVertexITSTPC"); + hEventSelection->GetXaxis()->SetBinLabel(8, "kIsGoodZvtxFT0vsPV"); + hEventSelection->GetXaxis()->SetBinLabel(9, "kIsVertexTOFmatched"); + hEventSelection->GetXaxis()->SetBinLabel(10, "kIsVertexTRDmatched"); + hEventSelection->GetXaxis()->SetBinLabel(11, "kNoSameBunchPileup"); + hEventSelection->GetXaxis()->SetBinLabel(12, "kNoCollInTimeRangeStd"); + hEventSelection->GetXaxis()->SetBinLabel(13, "kNoCollInTimeRangeStrict"); + hEventSelection->GetXaxis()->SetBinLabel(14, "kNoCollInTimeRangeNarrow"); + hEventSelection->GetXaxis()->SetBinLabel(15, "kNoCollInRofStd"); + hEventSelection->GetXaxis()->SetBinLabel(16, "kNoCollInRofStrict"); + hEventSelection->GetXaxis()->SetBinLabel(17, "INEL>0"); + hEventSelection->GetXaxis()->SetBinLabel(18, "INEL>1"); + hEventSelection->GetXaxis()->SetBinLabel(19, "Below min occup."); + hEventSelection->GetXaxis()->SetBinLabel(20, "Above max occup."); + hEventSelection->GetXaxis()->SetBinLabel(21, "Below min IR"); + hEventSelection->GetXaxis()->SetBinLabel(22, "Above max IR"); + hEventSelection->GetXaxis()->SetBinLabel(23, "Selected collisions"); + + histos.add("hEventCentrality", "hEventCentrality", kTH1D, {axisCentrality}); + histos.add("hEventPVz", "hEventPVz", kTH1D, {axisVtxZ}); + if (doEventQA) { + histos.add("EventQA/hCentralityVsNch", "hCentralityVsNch", kTH2D, {axisCentrality, axisNch}); + histos.add("EventQA/hCentralityVsPVz", "hCentralityVsPVz", kTH2D, {axisCentrality, axisVtxZ}); + } + + //_______________________________________________ + // Single candidate-selection bookkeeping histogram + auto hSelections = histos.add("hSelections", "hSelections", kTH1D, {{static_cast(selPhysPrimAntiXi0) + 3, -0.5f, static_cast(selPhysPrimAntiXi0) + 2.5f}}); + hSelections->GetXaxis()->SetBinLabel(1, "All"); + hSelections->GetXaxis()->SetBinLabel(selPhotonV0Type + 2, "#gamma V0 type"); + hSelections->GetXaxis()->SetBinLabel(selPhotonMass + 2, "#gamma mass"); + hSelections->GetXaxis()->SetBinLabel(selPhotonRapidity + 2, "#gamma rapidity"); + hSelections->GetXaxis()->SetBinLabel(selPhotonDauEta + 2, "#gamma dau. #eta"); + hSelections->GetXaxis()->SetBinLabel(selPhotonDCADauToPV + 2, "#gamma DCA dau. to PV"); + hSelections->GetXaxis()->SetBinLabel(selPhotonDCADau + 2, "#gamma DCA dau."); + hSelections->GetXaxis()->SetBinLabel(selPhotonRadius + 2, "#gamma radius"); + hSelections->GetXaxis()->SetBinLabel(selPhotonCosPA + 2, "#gamma cosPA"); + hSelections->GetXaxis()->SetBinLabel(selPhotonArmenteros + 2, "#gamma Arm. pod."); + hSelections->GetXaxis()->SetBinLabel(selPhotonPt + 2, "#gamma #it{p}_{T}"); + hSelections->GetXaxis()->SetBinLabel(selPhotonTPCCrossedRows + 2, "#gamma TPC rows"); + hSelections->GetXaxis()->SetBinLabel(selPhotonTPCPID + 2, "#gamma TPC PID"); + hSelections->GetXaxis()->SetBinLabel(selLambdaV0Type + 2, "#Lambda V0 type"); + hSelections->GetXaxis()->SetBinLabel(selLambdaRapidity + 2, "#Lambda rapidity"); + hSelections->GetXaxis()->SetBinLabel(selLambdaDauEta + 2, "#Lambda dau. #eta"); + hSelections->GetXaxis()->SetBinLabel(selLambdaDCAPosToPV + 2, "#Lambda DCA pos. to PV"); + hSelections->GetXaxis()->SetBinLabel(selLambdaDCANegToPV + 2, "#Lambda DCA neg. to PV"); + hSelections->GetXaxis()->SetBinLabel(selLambdaDCADau + 2, "#Lambda DCA dau."); + hSelections->GetXaxis()->SetBinLabel(selLambdaRadius + 2, "#Lambda radius"); + hSelections->GetXaxis()->SetBinLabel(selLambdaCosPA + 2, "#Lambda cosPA"); + hSelections->GetXaxis()->SetBinLabel(selLambdaLifetime + 2, "#Lambda lifetime"); + hSelections->GetXaxis()->SetBinLabel(selLambdaTPCCrossedRows + 2, "#Lambda TPC rows"); + hSelections->GetXaxis()->SetBinLabel(selLambdaITSClusters + 2, "#Lambda ITS clusters"); + hSelections->GetXaxis()->SetBinLabel(selLambdaMass + 2, "#Lambda mass"); + hSelections->GetXaxis()->SetBinLabel(selAntiLambdaMass + 2, "#bar{#Lambda} mass"); + hSelections->GetXaxis()->SetBinLabel(selLambdaArmenteros + 2, "#Lambda Arm. pod."); + hSelections->GetXaxis()->SetBinLabel(selAntiLambdaArmenteros + 2, "#bar{#Lambda} Arm. pod."); + hSelections->GetXaxis()->SetBinLabel(selTPCPIDPositiveProton + 2, "TPC PID p"); + hSelections->GetXaxis()->SetBinLabel(selTPCPIDNegativePion + 2, "TPC PID #pi^{-}"); + hSelections->GetXaxis()->SetBinLabel(selTPCPIDNegativeProton + 2, "TPC PID #bar{p}"); + hSelections->GetXaxis()->SetBinLabel(selTPCPIDPositivePion + 2, "TPC PID #pi^{+}"); + hSelections->GetXaxis()->SetBinLabel(selPi0Mass + 2, "#pi^{0} mass"); + hSelections->GetXaxis()->SetBinLabel(selPi0Radius + 2, "#pi^{0} radius"); + hSelections->GetXaxis()->SetBinLabel(selPi0CosPA + 2, "#pi^{0} cosPA"); + hSelections->GetXaxis()->SetBinLabel(selPi0DCADau + 2, "#pi^{0} DCA dau."); + hSelections->GetXaxis()->SetBinLabel(selXi0Radius + 2, "#Xi^{0} radius"); + hSelections->GetXaxis()->SetBinLabel(selXi0CosPA + 2, "#Xi^{0} cosPA"); + hSelections->GetXaxis()->SetBinLabel(selXi0DCADau + 2, "#Xi^{0} DCA dau."); + hSelections->GetXaxis()->SetBinLabel(selXi0DCAxyToPV + 2, "#Xi^{0} DCA_{xy} to PV"); + hSelections->GetXaxis()->SetBinLabel(selXi0DCAzToPV + 2, "#Xi^{0} DCA_{z} to PV"); + hSelections->GetXaxis()->SetBinLabel(selXi0Lifetime + 2, "#Xi^{0} lifetime"); + hSelections->GetXaxis()->SetBinLabel(selXi0Rapidity + 2, "#Xi^{0} rapidity"); + hSelections->GetXaxis()->SetBinLabel(selXi0Pt + 2, "#Xi^{0} #it{p}_{T}"); + hSelections->GetXaxis()->SetBinLabel(selConsiderXi0 + 2, "True #Xi^{0}"); + hSelections->GetXaxis()->SetBinLabel(selConsiderAntiXi0 + 2, "True #bar{#Xi^{0}}"); + hSelections->GetXaxis()->SetBinLabel(selPhysPrimXi0 + 2, "Phys. prim. #Xi^{0}"); + hSelections->GetXaxis()->SetBinLabel(selPhysPrimAntiXi0 + 2, "Phys. prim. #bar{#Xi^{0}}"); + hSelections->GetXaxis()->SetBinLabel(selPhysPrimAntiXi0 + 3, "Cand. selected"); + + //_______________________________________________ + // Main analysis output + histos.add("h3dMassXi0", "h3dMassXi0", kTH3D, {axisCentrality, axisPt, axisXi0Mass}); + histos.add("h3dMassAntiXi0", "h3dMassAntiXi0", kTH3D, {axisCentrality, axisPt, axisXi0Mass}); + histos.add("h2dNbrOfXi0VsCentrality", "h2dNbrOfXi0VsCentrality", kTH2D, {axisCentrality, axisNCandidates}); + histos.add("h2dNbrOfAntiXi0VsCentrality", "h2dNbrOfAntiXi0VsCentrality", kTH2D, {axisCentrality, axisNCandidates}); + + //_______________________________________________ + // Candidate QA, before and after the selections + if (doCandidateQA) { + for (int mode = 0; mode < NSelectionStages; mode++) { + const std::string dir = (mode == 0) ? "QA/BeforeSel/" : "QA/AfterSel/"; + histos.add(dir + "h3dMass", "h3dMass", kTH3D, {axisCentrality, axisPt, axisXi0Mass}); + histos.add(dir + "hMassPi0", "hMassPi0", kTH1D, {axisPi0Mass}); + histos.add(dir + "hMassLambda", "hMassLambda", kTH1D, {axisLambdaMass}); + histos.add(dir + "hPt", "hPt", kTH1D, {axisPt}); + histos.add(dir + "hRapidity", "hRapidity", kTH1D, {axisRapidity}); + histos.add(dir + "hCascRadius", "hCascRadius", kTH1D, {axisRadius}); + histos.add(dir + "hCascCosPA", "hCascCosPA", kTH1D, {axisCosPA}); + histos.add(dir + "hDCACascDaughters", "hDCACascDaughters", kTH1D, {axisDCADau}); + histos.add(dir + "hDCAxyCascToPV", "hDCAxyCascToPV", kTH1D, {axisDCAToPV}); + histos.add(dir + "hDCAzCascToPV", "hDCAzCascToPV", kTH1D, {axisDCAToPV}); + histos.add(dir + "hLifetime", "hLifetime", kTH1D, {axisLifetime}); + histos.add(dir + "hLambdaRadius", "hLambdaRadius", kTH1D, {axisRadius}); + histos.add(dir + "hLambdaCosPA", "hLambdaCosPA", kTH1D, {axisCosPA}); + histos.add(dir + "hPi0Radius", "hPi0Radius", kTH1D, {axisRadius}); + histos.add(dir + "hPi0CosPA", "hPi0CosPA", kTH1D, {axisCosPA}); + histos.add(dir + "hDCAPi0Daughters", "hDCAPi0Daughters", kTH1D, {axisDCADau}); + histos.add(dir + "h2dArmenterosLambda", "h2dArmenterosLambda", kTH2D, {axisArmAlpha, axisArmQt}); + } + } + + //_______________________________________________ + // MC-specific histograms + if (doprocessMonteCarlo && doMCQA) { + histos.add("MCQA/h2dPtResolution", "h2dPtResolution", kTH2D, {axisPt, axisPtResolution}); + histos.add("MCQA/h2dPtRecoVsPtGen", "h2dPtRecoVsPtGen", kTH2D, {axisPt, axisPt}); + histos.add("MCQA/h3dMassXi0VsPtReco", "h3dMassXi0VsPtReco", kTH3D, {axisCentrality, axisPt, axisXi0Mass}); + histos.add("MCQA/h3dMassAntiXi0VsPtReco", "h3dMassAntiXi0VsPtReco", kTH3D, {axisCentrality, axisPt, axisXi0Mass}); + histos.add("MCQA/hXi0PDGCode", "hXi0PDGCode", kTH1D, {axisPDGCode}); + histos.add("MCQA/hXi0PDGCodeMother", "hXi0PDGCodeMother", kTH1D, {axisPDGCode}); + histos.add("MCQA/h2dPhoton1VsPhoton2PDGCode", "h2dPhoton1VsPhoton2PDGCode", kTH2D, {axisPDGCode, axisPDGCode}); + histos.add("MCQA/hLambdaPDGCode", "hLambdaPDGCode", kTH1D, {axisPDGCode}); + } + + //_______________________________________________ + // Generated-level histograms + if (doprocessGenerated) { + histos.add("Gen/hGenEvents", "hGenEvents", kTH2D, {axisNch, {2, -0.5f, +1.5f}}); + histos.add("Gen/hGenEventCentrality", "hGenEventCentrality", kTH1D, {axisCentrality}); + histos.add("Gen/hCentralityVsNcoll_beforeEvSel", "hCentralityVsNcoll_beforeEvSel", kTH2D, {axisCentrality, {50, -0.5f, +49.5f}}); + histos.add("Gen/hCentralityVsNcoll_afterEvSel", "hCentralityVsNcoll_afterEvSel", kTH2D, {axisCentrality, {50, -0.5f, +49.5f}}); + histos.add("Gen/hCentralityVsMultMC", "hCentralityVsMultMC", kTH2D, {axisCentrality, axisNch}); + + histos.add("Gen/h2dGenXi0", "h2dGenXi0", kTH2D, {axisCentrality, axisPt}); + histos.add("Gen/h2dGenAntiXi0", "h2dGenAntiXi0", kTH2D, {axisCentrality, axisPt}); + histos.add("Gen/h2dGenXi0VsMultMC", "h2dGenXi0VsMultMC", kTH2D, {axisNch, axisPt}); + histos.add("Gen/h2dGenAntiXi0VsMultMC", "h2dGenAntiXi0VsMultMC", kTH2D, {axisNch, axisPt}); + histos.add("Gen/h2dGenXi0_RecoedEvt", "h2dGenXi0_RecoedEvt", kTH2D, {axisCentrality, axisPt}); + histos.add("Gen/h2dGenAntiXi0_RecoedEvt", "h2dGenAntiXi0_RecoedEvt", kTH2D, {axisCentrality, axisPt}); + histos.add("Gen/hGenXi0Rapidity", "hGenXi0Rapidity", kTH1D, {axisRapidity}); + } + } + + //_______________________________________________ + // Check that all the bits of a mask are set in a bitmap + bool verifyMask(uint64_t bitmap, uint64_t mask) + { + return (bitmap & mask) == mask; + } + + //_______________________________________________ + // Centrality getter (Run 3) + template + float getCentralityRun3(TCollision const& collision) + { + if (centralityEstimator == kCentFT0C) + return collision.centFT0C(); + else if (centralityEstimator == kCentFT0M) + return collision.centFT0M(); + else if (centralityEstimator == kCentFT0CVariant1) + return collision.centFT0CVariant1(); + else if (centralityEstimator == kCentMFT) + return collision.centMFT(); + else if (centralityEstimator == kCentNGlobal) + return collision.centNGlobal(); + else if (centralityEstimator == kCentFV0A) + return collision.centFV0A(); + + return -1.f; + } + + //_______________________________________________ + // Check whether the collision passes our collision selections + template + bool isEventAccepted(TCollision const& collision, bool fillHists) + { + if (fillHists) + histos.fill(HIST("hEventSelection"), 0. /* all collisions */); + if (eventSelections.requireSel8 && !collision.sel8()) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 1 /* sel8 collisions */); + if (eventSelections.requireTriggerTVX && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 2 /* FT0 vertex (acceptable FT0C-FT0A time difference) collisions */); + if (eventSelections.rejectITSROFBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 3 /* Not at ITS ROF border */); + if (eventSelections.rejectTFBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 4 /* Not at TF border */); + if (std::abs(collision.posZ()) > eventSelections.maxZVtxPosition) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 5 /* vertex-Z selected */); + if (eventSelections.requireIsVertexITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 6 /* Contains at least one ITS-TPC track */); + if (eventSelections.requireIsGoodZvtxFT0VsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 7 /* PV position consistency check */); + if (eventSelections.requireIsVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 8 /* PV with at least one contributor matched with TOF */); + if (eventSelections.requireIsVertexTRDmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTRDmatched)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 9 /* PV with at least one contributor matched with TRD */); + if (eventSelections.rejectSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 10 /* Not at same bunch pile-up */); + if (eventSelections.requireNoCollInTimeRangeStd && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 11 /* No other collision within +/- 2 microseconds or mult above a certain threshold in -4 - -2 microseconds */); + if (eventSelections.requireNoCollInTimeRangeStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStrict)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 12 /* No other collision within +/- 10 microseconds */); + if (eventSelections.requireNoCollInTimeRangeNarrow && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeNarrow)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 13 /* No other collision within +/- 2 microseconds */); + if (eventSelections.requireNoCollInROFStd && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 14 /* No other collision within the same ITS ROF with mult. above a certain threshold */); + if (eventSelections.requireNoCollInROFStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStrict)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 15 /* No other collision within the same ITS ROF */); + if (eventSelections.requireINEL0 && collision.multNTracksPVeta1() < 1) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 16 /* INEL > 0 */); + if (eventSelections.requireINEL1 && collision.multNTracksPVeta1() < 2) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 17 /* INEL > 1 */); + + float collisionOccupancy = eventSelections.useFT0CbasedOccupancy ? collision.ft0cOccupancyInTimeRange() : collision.trackOccupancyInTimeRange(); + if (eventSelections.minOccupancy >= 0 && collisionOccupancy < eventSelections.minOccupancy) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 18 /* Below min occupancy */); + if (eventSelections.maxOccupancy >= 0 && collisionOccupancy > eventSelections.maxOccupancy) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 19 /* Above max occupancy */); + + // Fetch interaction rate only if required (in order to limit ccdb calls) + float interactionRate = (fGetIR) ? rateFetcher.fetch(ccdb.service, collision.timestamp(), collision.runNumber(), irSource, fIRCrashOnNull) * 1.e-3 : -1; + if (eventSelections.minIR >= 0 && interactionRate < eventSelections.minIR) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 20 /* Below min IR */); + if (eventSelections.maxIR >= 0 && interactionRate > eventSelections.maxIR) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 21 /* Above max IR */); + + if (fillHists) { + histos.fill(HIST("hEventSelection"), 22 /* selected collisions */); + float centrality = getCentralityRun3(collision); + histos.fill(HIST("hEventCentrality"), centrality); + histos.fill(HIST("hEventPVz"), collision.posZ()); + if (doEventQA) { + histos.fill(HIST("EventQA/hCentralityVsNch"), centrality, collision.multNTracksPVeta1()); + histos.fill(HIST("EventQA/hCentralityVsPVz"), centrality, collision.posZ()); + } + } + + return true; + } + + //_______________________________________________ + // Photon (pi0 daughter) selection bitmap, computed on the de-referenced V0Cores entry. + // The bitmaps of the two photons are AND-ed, so that a bit is set only if + // both photons of the pi0 satisfy the corresponding criterion. + template + uint64_t computePhotonBitmap(TV0Object const& gamma) + { + uint64_t bitMap = 0; + + if (photonSelections.v0TypeSelection < 0 || gamma.v0Type() == photonSelections.v0TypeSelection) + BITSET(bitMap, selPhotonV0Type); + + if (gamma.mGamma() > 0 && gamma.mGamma() < photonSelections.maxMass) + BITSET(bitMap, selPhotonMass); + + const float photonY = RecoDecay::y(std::array{gamma.px(), gamma.py(), gamma.pz()}, o2::constants::physics::MassGamma); + if (photonY > photonSelections.minRapidity && photonY < photonSelections.maxRapidity) + BITSET(bitMap, selPhotonRapidity); + + if (std::abs(gamma.positiveeta()) < photonSelections.maxDauEta && std::abs(gamma.negativeeta()) < photonSelections.maxDauEta) + BITSET(bitMap, selPhotonDauEta); + + if (std::abs(gamma.dcapostopv()) > photonSelections.minDCADauToPV && std::abs(gamma.dcanegtopv()) > photonSelections.minDCADauToPV) + BITSET(bitMap, selPhotonDCADauToPV); + + if (std::abs(gamma.dcaV0daughters()) < photonSelections.maxDCADau) + BITSET(bitMap, selPhotonDCADau); + + if (gamma.v0radius() > photonSelections.minRadius && gamma.v0radius() < photonSelections.maxRadius) + BITSET(bitMap, selPhotonRadius); + + if (gamma.v0cosPA() > photonSelections.minCosPA) + BITSET(bitMap, selPhotonCosPA); + + if (gamma.qtarm() < photonSelections.maxQt && std::abs(gamma.alpha()) < photonSelections.maxAlpha) + BITSET(bitMap, selPhotonArmenteros); + + if (gamma.pt() > photonSelections.minPt && gamma.pt() < photonSelections.maxPt) + BITSET(bitMap, selPhotonPt); + + auto posTrack = gamma.template posTrackExtra_as(); + auto negTrack = gamma.template negTrackExtra_as(); + + if (posTrack.tpcCrossedRows() >= photonSelections.minTPCCrossedRows && negTrack.tpcCrossedRows() >= photonSelections.minTPCCrossedRows) + BITSET(bitMap, selPhotonTPCCrossedRows); + + if (posTrack.tpcNSigmaEl() > photonSelections.minTPCNSigmaEl && posTrack.tpcNSigmaEl() < photonSelections.maxTPCNSigmaEl && + negTrack.tpcNSigmaEl() > photonSelections.minTPCNSigmaEl && negTrack.tpcNSigmaEl() < photonSelections.maxTPCNSigmaEl) + BITSET(bitMap, selPhotonTPCPID); + + return bitMap; + } + + //_______________________________________________ + // Lambda selection bitmap, computed on the de-referenced V0Cores entry + template + uint64_t computeLambdaBitmap(TV0Object const& lambda, TCollision const& collision) + { + uint64_t bitMap = 0; + + if (lambdaSelections.v0TypeSelection < 0 || lambda.v0Type() == lambdaSelections.v0TypeSelection) + BITSET(bitMap, selLambdaV0Type); + + if (lambda.yLambda() > lambdaSelections.minRapidity && lambda.yLambda() < lambdaSelections.maxRapidity) + BITSET(bitMap, selLambdaRapidity); + + if (std::abs(lambda.positiveeta()) < lambdaSelections.maxDauEta && std::abs(lambda.negativeeta()) < lambdaSelections.maxDauEta) + BITSET(bitMap, selLambdaDauEta); + + if (std::abs(lambda.dcapostopv()) > lambdaSelections.minDCAPosToPV) + BITSET(bitMap, selLambdaDCAPosToPV); + + if (std::abs(lambda.dcanegtopv()) > lambdaSelections.minDCANegToPV) + BITSET(bitMap, selLambdaDCANegToPV); + + if (std::abs(lambda.dcaV0daughters()) < lambdaSelections.maxDCADau) + BITSET(bitMap, selLambdaDCADau); + + if (lambda.v0radius() > lambdaSelections.minRadius && lambda.v0radius() < lambdaSelections.maxRadius) + BITSET(bitMap, selLambdaRadius); + + if (lambda.v0cosPA() > lambdaSelections.minCosPA) + BITSET(bitMap, selLambdaCosPA); + + const float lambdaLifetime = lambda.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0; + if (lambdaSelections.maxLifetime < 0 || lambdaLifetime < lambdaSelections.maxLifetime) + BITSET(bitMap, selLambdaLifetime); + + // Lambda / anti-Lambda separation via the Armenteros-Podolanski alpha: + // in the Lambda decay the baryon carries most of the longitudinal momentum + if (lambda.alpha() > lambdaSelections.minAbsAlpha && lambda.alpha() < lambdaSelections.maxAbsAlpha) + BITSET(bitMap, selLambdaArmenteros); + if (lambda.alpha() < -lambdaSelections.minAbsAlpha && lambda.alpha() > -lambdaSelections.maxAbsAlpha) + BITSET(bitMap, selAntiLambdaArmenteros); + + if (std::abs(lambda.mLambda() - o2::constants::physics::MassLambda0) < lambdaSelections.massWindow) + BITSET(bitMap, selLambdaMass); + if (std::abs(lambda.mAntiLambda() - o2::constants::physics::MassLambda0) < lambdaSelections.massWindow) + BITSET(bitMap, selAntiLambdaMass); + + auto posTrack = lambda.template posTrackExtra_as(); + auto negTrack = lambda.template negTrackExtra_as(); + + if (posTrack.tpcCrossedRows() >= lambdaSelections.minTPCCrossedRows && negTrack.tpcCrossedRows() >= lambdaSelections.minTPCCrossedRows) + BITSET(bitMap, selLambdaTPCCrossedRows); + + if (lambdaSelections.minITSclusters < 0 || + (posTrack.itsNCls() >= lambdaSelections.minITSclusters && negTrack.itsNCls() >= lambdaSelections.minITSclusters)) + BITSET(bitMap, selLambdaITSClusters); + + // TPC PID, for both mass hypotheses + if (std::abs(posTrack.tpcNSigmaPr()) < lambdaSelections.maxTPCNSigmaPr) + BITSET(bitMap, selTPCPIDPositiveProton); + if (std::abs(negTrack.tpcNSigmaPi()) < lambdaSelections.maxTPCNSigmaPi) + BITSET(bitMap, selTPCPIDNegativePion); + if (std::abs(negTrack.tpcNSigmaPr()) < lambdaSelections.maxTPCNSigmaPr) + BITSET(bitMap, selTPCPIDNegativeProton); + if (std::abs(posTrack.tpcNSigmaPi()) < lambdaSelections.maxTPCNSigmaPi) + BITSET(bitMap, selTPCPIDPositivePion); + + return bitMap; + } + + //_______________________________________________ + // pi0 and Xi0 (cascade) selection bitmap. + // rapidity and pt are the generated ones when running over MC. + template + uint64_t computeCascadeBitmap(TXi0Object const& xi0, TCollision const& collision, float rapidity, float pt) + { + uint64_t bitMap = 0; + + if (std::abs(xi0.pi0Mass() - o2::constants::physics::MassPi0) < xi0Selections.pi0MassWindow) + BITSET(bitMap, selPi0Mass); + + if ((xi0Selections.minPi0Radius < 0 || xi0.radiusPi0() > xi0Selections.minPi0Radius) && + (xi0Selections.maxPi0Radius < 0 || xi0.radiusPi0() < xi0Selections.maxPi0Radius)) + BITSET(bitMap, selPi0Radius); + + if (xi0Selections.minPi0CosPA < -1 || xi0.pi0CosPA(collision.posX(), collision.posY(), collision.posZ()) > xi0Selections.minPi0CosPA) + BITSET(bitMap, selPi0CosPA); + + if (xi0Selections.maxDCAPi0Daughters < 0 || xi0.dcadaughtersPi0() < xi0Selections.maxDCAPi0Daughters) + BITSET(bitMap, selPi0DCADau); + + if (xi0.radius() > xi0Selections.minCascRadius && xi0.radius() < xi0Selections.maxCascRadius) + BITSET(bitMap, selXi0Radius); + + if (xi0.cascCosPA(collision.posX(), collision.posY(), collision.posZ()) > xi0Selections.minCascCosPA) + BITSET(bitMap, selXi0CosPA); + + if (xi0.dcadaughters() < xi0Selections.maxDCACascDaughters) + BITSET(bitMap, selXi0DCADau); + + if (xi0Selections.maxDCAxyCascToPV < 0 || std::abs(xi0.dcaXYCascToPV()) < xi0Selections.maxDCAxyCascToPV) + BITSET(bitMap, selXi0DCAxyToPV); + + if (xi0Selections.maxDCAzCascToPV < 0 || std::abs(xi0.dcaZCascToPV()) < xi0Selections.maxDCAzCascToPV) + BITSET(bitMap, selXi0DCAzToPV); + + if (xi0Selections.maxLifetime < 0 || getProperLifetime(xi0, collision) < xi0Selections.maxLifetime * CtauXi0) + BITSET(bitMap, selXi0Lifetime); + + if (rapidity > xi0Selections.minRapidity && rapidity < xi0Selections.maxRapidity) + BITSET(bitMap, selXi0Rapidity); + + if (pt > xi0Selections.minPt && pt < xi0Selections.maxPt) + BITSET(bitMap, selXi0Pt); + + return bitMap; + } + + //_______________________________________________ + // MC association bitmap. + // The builder sets the Xi0 PDG code only if the full decay chain is matched, + // i.e. two true photons from the same true pi0 and a true Lambda sharing the + // same true Xi0 mother, so a single PDG check is enough here. + template + uint64_t computeMCAssociation(TXi0Object const& xi0) + { + uint64_t bitMap = 0; + + if (xi0.pdgCode() == o2::constants::physics::Pdg::kXi0) { + BITSET(bitMap, selConsiderXi0); + if (xi0.isPhysicalPrimary()) + BITSET(bitMap, selPhysPrimXi0); + } + if (xi0.pdgCode() == -o2::constants::physics::Pdg::kXi0) { + BITSET(bitMap, selConsiderAntiXi0); + if (xi0.isPhysicalPrimary()) + BITSET(bitMap, selPhysPrimAntiXi0); + } + + return bitMap; + } + + //_______________________________________________ + // Xi0 proper lifetime, m*L/p, in cm + template + float getProperLifetime(TXi0Object const& xi0, TCollision const& collision) + { + const float decayLength = RecoDecay::distance(std::array{collision.posX(), collision.posY(), collision.posZ()}, + std::array{xi0.x(), xi0.y(), xi0.z()}); + const float totalMomentum = xi0.p(); + return (totalMomentum > 0) ? o2::constants::physics::MassXi0 * decayLength / totalMomentum : 1e6f; + } + + //_______________________________________________ + // Fill the candidate QA histograms. mode = 0: before selections, 1: after selections + template + void fillCandidateQA(TXi0Object const& xi0, TV0Object const& lambda, TCollision const& collision, float pt, float rapidity, float centrality, float massLambda) + { + static constexpr std::string_view MainDir[] = {"QA/BeforeSel", "QA/AfterSel"}; + + histos.fill(HIST(MainDir[mode]) + HIST("/h3dMass"), centrality, pt, xi0.xi0Mass()); + histos.fill(HIST(MainDir[mode]) + HIST("/hMassPi0"), xi0.pi0Mass()); + histos.fill(HIST(MainDir[mode]) + HIST("/hMassLambda"), massLambda); + histos.fill(HIST(MainDir[mode]) + HIST("/hPt"), pt); + histos.fill(HIST(MainDir[mode]) + HIST("/hRapidity"), rapidity); + histos.fill(HIST(MainDir[mode]) + HIST("/hCascRadius"), xi0.radius()); + histos.fill(HIST(MainDir[mode]) + HIST("/hCascCosPA"), xi0.cascCosPA(collision.posX(), collision.posY(), collision.posZ())); + histos.fill(HIST(MainDir[mode]) + HIST("/hDCACascDaughters"), xi0.dcadaughters()); + histos.fill(HIST(MainDir[mode]) + HIST("/hDCAxyCascToPV"), xi0.dcaXYCascToPV()); + histos.fill(HIST(MainDir[mode]) + HIST("/hDCAzCascToPV"), xi0.dcaZCascToPV()); + histos.fill(HIST(MainDir[mode]) + HIST("/hLifetime"), getProperLifetime(xi0, collision)); + histos.fill(HIST(MainDir[mode]) + HIST("/hLambdaRadius"), xi0.radiusLambda()); + histos.fill(HIST(MainDir[mode]) + HIST("/hLambdaCosPA"), xi0.lambdaCosPA(collision.posX(), collision.posY(), collision.posZ())); + histos.fill(HIST(MainDir[mode]) + HIST("/hPi0Radius"), xi0.radiusPi0()); + histos.fill(HIST(MainDir[mode]) + HIST("/hPi0CosPA"), xi0.pi0CosPA(collision.posX(), collision.posY(), collision.posZ())); + histos.fill(HIST(MainDir[mode]) + HIST("/hDCAPi0Daughters"), xi0.dcadaughtersPi0()); + histos.fill(HIST(MainDir[mode]) + HIST("/h2dArmenterosLambda"), lambda.alpha(), lambda.qtarm()); + } + + //_______________________________________________ + // Main reconstructed-level analysis function, common to data and MC + template + void analyseRecoedXi0s(TCollisions const& collisions, TXi0s const& fullXi0s, TV0s const& fullV0s) + { + // Custom grouping: the Xi0 candidates are not sorted per collision + std::vector> xi0grouped(collisions.size()); + for (const auto& xi0 : fullXi0s) { + xi0grouped[xi0.straCollisionId()].push_back(xi0.globalIndex()); + } + + const int64_t nV0s = fullV0s.size(); + + for (const auto& coll : collisions) { + // Event selection + if (!isEventAccepted(coll, true)) { + continue; + } + const float centrality = getCentralityRun3(coll); + + int nXi0s = 0; + int nAntiXi0s = 0; + + // Xi0 candidates loop + for (size_t i = 0; i < xi0grouped[coll.globalIndex()].size(); i++) { + auto xi0 = fullXi0s.rawIteratorAt(xi0grouped[coll.globalIndex()][i]); + + histos.fill(HIST("hSelections"), 0 /* all candidates */); + + //_______________________________________________ + // De-reference the three daughter V0s + if (xi0.photon1Index() < 0 || xi0.photon1Index() >= nV0s || + xi0.photon2Index() < 0 || xi0.photon2Index() >= nV0s || + xi0.lambdaIndex() < 0 || xi0.lambdaIndex() >= nV0s) { + continue; + } + auto photon1 = fullV0s.rawIteratorAt(xi0.photon1Index()); + auto photon2 = fullV0s.rawIteratorAt(xi0.photon2Index()); + auto lambda = fullV0s.rawIteratorAt(xi0.lambdaIndex()); + + //_______________________________________________ + // Kinematics entering both the selections and the histograms. + // Over MC, the generated rapidity and pT are used instead of the + // reconstructed ones, so that the acceptance x efficiency numerator + // and denominator are defined identically. + const float ptRecoed = xi0.pt(); + float pt = ptRecoed; + float rapidity = xi0.rapidity(); + bool hasMCInfo = false; + if constexpr (requires { xi0.pdgCode(); }) { + // the MC information is available only if the three daughter V0s + // could be associated to a V0MCCore entry by the builder + hasMCInfo = (xi0.photon1PDGCode() != 0 && xi0.photon2PDGCode() != 0 && xi0.lambdaPDGCode() != 0); + if (hasMCInfo) { + pt = xi0.mcpt(); + rapidity = xi0.rapidityMC(); + } + } + + //_______________________________________________ + // Assemble the selection bitmap + uint64_t selMap = computePhotonBitmap(photon1) & computePhotonBitmap(photon2); + selMap |= computeLambdaBitmap(lambda, coll); + selMap |= computeCascadeBitmap(xi0, coll, rapidity, pt); + + if constexpr (requires { xi0.pdgCode(); }) { + selMap |= computeMCAssociation(xi0); + + if (doMCQA) { + histos.fill(HIST("MCQA/hXi0PDGCode"), xi0.pdgCode()); + histos.fill(HIST("MCQA/hXi0PDGCodeMother"), xi0.pdgCodeMother()); + histos.fill(HIST("MCQA/h2dPhoton1VsPhoton2PDGCode"), xi0.photon1PDGCode(), xi0.photon2PDGCode()); + histos.fill(HIST("MCQA/hLambdaPDGCode"), xi0.lambdaPDGCode()); + } + + // disregard the MC association if not explicitly asked for + if (!mcSelections.doMCAssociation) { + BITSET(selMap, selConsiderXi0); + BITSET(selMap, selConsiderAntiXi0); + BITSET(selMap, selPhysPrimXi0); + BITSET(selMap, selPhysPrimAntiXi0); + } + } else { + // real data: every candidate is considered for both hypotheses, + // the Armenteros bits alone tell Xi0 and anti-Xi0 apart + BITSET(selMap, selConsiderXi0); + BITSET(selMap, selConsiderAntiXi0); + BITSET(selMap, selPhysPrimXi0); + BITSET(selMap, selPhysPrimAntiXi0); + } + + // Selection bookkeeping: one bin per criterion + for (int bit = 0; bit <= static_cast(selPhysPrimAntiXi0); bit++) { + if (BITCHECK(selMap, bit)) { + histos.fill(HIST("hSelections"), bit + 1); + } + } + + const bool passXi0 = verifyMask(selMap, maskSelectionXi0); + const bool passAntiXi0 = verifyMask(selMap, maskSelectionAntiXi0); + // the sign of the Armenteros alpha already tells the two hypotheses apart + const float massLambda = (lambda.alpha() < 0) ? lambda.mAntiLambda() : lambda.mLambda(); + + if (doCandidateQA) { + fillCandidateQA<0>(xi0, lambda, coll, pt, rapidity, centrality, massLambda); + } + + if (!passXi0 && !passAntiXi0) { + continue; + } + histos.fill(HIST("hSelections"), static_cast(selPhysPrimAntiXi0) + 2 /* candidate selected */); + + if (doCandidateQA) { + fillCandidateQA<1>(xi0, lambda, coll, pt, rapidity, centrality, massLambda); + } + + if (passXi0) { + nXi0s++; + histos.fill(HIST("h3dMassXi0"), centrality, pt, xi0.xi0Mass()); + } + if (passAntiXi0) { + nAntiXi0s++; + histos.fill(HIST("h3dMassAntiXi0"), centrality, pt, xi0.xi0Mass()); + } + + //_______________________________________________ + // MC-specific QA + if constexpr (requires { xi0.pdgCode(); }) { + if (doMCQA) { + if (passXi0) { + histos.fill(HIST("MCQA/h3dMassXi0VsPtReco"), centrality, ptRecoed, xi0.xi0Mass()); + } + if (passAntiXi0) { + histos.fill(HIST("MCQA/h3dMassAntiXi0VsPtReco"), centrality, ptRecoed, xi0.xi0Mass()); + } + if (hasMCInfo && pt > 0) { + histos.fill(HIST("MCQA/h2dPtResolution"), pt, (ptRecoed - pt) / pt); + histos.fill(HIST("MCQA/h2dPtRecoVsPtGen"), pt, ptRecoed); + } + } + } + } + + histos.fill(HIST("h2dNbrOfXi0VsCentrality"), centrality, nXi0s); + histos.fill(HIST("h2dNbrOfAntiXi0VsCentrality"), centrality, nAntiXi0s); + } + } + + //_______________________________________________ + // Generated-level processing + // Return the list of indices of the reconstructed collision associated to each MC collision + template + std::vector getListOfRecoCollIndices(TMCCollisions const& mcCollisions, TCollisions const& collisions) + { + std::vector listBestCollisionIdx(mcCollisions.size(), -1); + std::vector> groupedCollisions(mcCollisions.size()); + + for (const auto& coll : collisions) { + if (coll.straMCCollisionId() < 0) { + continue; + } + groupedCollisions[coll.straMCCollisionId()].push_back(coll.globalIndex()); + } + + for (auto const& mcCollision : mcCollisions) { + int biggestNContribs = -1; + int bestCollisionIndex = -1; + for (size_t i = 0; i < groupedCollisions[mcCollision.globalIndex()].size(); i++) { + auto collision = collisions.rawIteratorAt(groupedCollisions[mcCollision.globalIndex()][i]); + + if (eventSelections.useEvtSelInDenomEff && !isEventAccepted(collision, false)) { + continue; + } + if (biggestNContribs < collision.multPVTotalContributors()) { + biggestNContribs = collision.multPVTotalContributors(); + bestCollisionIndex = collision.globalIndex(); + } + } + listBestCollisionIdx[mcCollision.globalIndex()] = bestCollisionIndex; + } + return listBestCollisionIdx; + } + + //_______________________________________________ + // Event selection applied at generated level + template + bool isGeneratedEventAccepted(TMCCollision const& mcCollision) + { + if (eventSelections.applyZVtxSelOnMCPV && std::abs(mcCollision.posZ()) > eventSelections.maxZVtxPosition) { + return false; + } + if (eventSelections.requireINEL0 && mcCollision.multMCNParticlesEta10() < 1) { + return false; + } + if (eventSelections.requireINEL1 && mcCollision.multMCNParticlesEta10() < 2) { + return false; + } + return true; + } + + //_______________________________________________ + // Generated-level processing: generated event properties (event loss/splitting) + template + void fillGeneratedEventProperties(TMCCollisions const& mcCollisions, TCollisions const& collisions) + { + std::vector> groupedCollisions(mcCollisions.size()); + for (const auto& coll : collisions) { + if (coll.straMCCollisionId() < 0) { + continue; + } + groupedCollisions[coll.straMCCollisionId()].push_back(coll.globalIndex()); + } + + for (auto const& mcCollision : mcCollisions) { + if (!isGeneratedEventAccepted(mcCollision)) { + continue; + } + + histos.fill(HIST("Gen/hGenEvents"), mcCollision.multMCNParticlesEta05(), 0 /* all gen. events */); + + bool atLeastOne = false; + int biggestNContribs = -1; + float centrality = 100.5f; + int nCollisions = 0; + for (size_t i = 0; i < groupedCollisions[mcCollision.globalIndex()].size(); i++) { + auto collision = collisions.rawIteratorAt(groupedCollisions[mcCollision.globalIndex()][i]); + + if (!isEventAccepted(collision, false)) { + continue; + } + if (biggestNContribs < collision.multPVTotalContributors()) { + biggestNContribs = collision.multPVTotalContributors(); + centrality = getCentralityRun3(collision); + } + nCollisions++; + atLeastOne = true; + } + + histos.fill(HIST("Gen/hCentralityVsNcoll_beforeEvSel"), centrality, static_cast(groupedCollisions[mcCollision.globalIndex()].size())); + histos.fill(HIST("Gen/hCentralityVsNcoll_afterEvSel"), centrality, nCollisions); + histos.fill(HIST("Gen/hCentralityVsMultMC"), centrality, mcCollision.multMCNParticlesEta05()); + + if (atLeastOne) { + histos.fill(HIST("Gen/hGenEvents"), mcCollision.multMCNParticlesEta05(), 1 /* at least 1 rec. event */); + histos.fill(HIST("Gen/hGenEventCentrality"), centrality); + } + } + } + + //_______________________________________________ + // Main generated-level analysis function. + // The generated Xi0 are read from the CascMCCores table. + template + void analyseGeneratedXi0s(TMCCollisions const& mcCollisions, TCollisions const& collisions, TCascMCs const& cascMCCores) + { + fillGeneratedEventProperties(mcCollisions, collisions); + std::vector listBestCollisionIdx = getListOfRecoCollIndices(mcCollisions, collisions); + + for (auto const& cascMC : cascMCCores) { + if (std::abs(cascMC.pdgCode()) != o2::constants::physics::Pdg::kXi0) { + continue; + } + if (!cascMC.has_straMCCollision()) { + continue; + } + if (mcSelections.requirePhysicalPrimary && !cascMC.isPhysicalPrimary()) { + continue; + } + + // N.B.: cascdata::RapidityMC only knows about the Xi- and Omega- masses, + // so the Xi0 rapidity is computed here explicitly + const float ptMC = cascMC.ptMC(); + const float rapidityMC = RecoDecay::y(std::array{cascMC.pxMC(), cascMC.pyMC(), cascMC.pzMC()}, o2::constants::physics::MassXi0); + + histos.fill(HIST("Gen/hGenXi0Rapidity"), rapidityMC); + + // same rapidity window as at reconstructed level + if (rapidityMC < xi0Selections.minRapidity || rapidityMC > xi0Selections.maxRapidity) { + continue; + } + + auto mcCollision = cascMC.template straMCCollision_as(); + if (!isGeneratedEventAccepted(mcCollision)) { + continue; + } + + float centrality = 100.5f; + const int bestCollisionIdx = listBestCollisionIdx[mcCollision.globalIndex()]; + if (bestCollisionIdx > -1) { + auto collision = collisions.rawIteratorAt(bestCollisionIdx); + centrality = getCentralityRun3(collision); + + if (cascMC.pdgCode() > 0) { + histos.fill(HIST("Gen/h2dGenXi0_RecoedEvt"), centrality, ptMC); + } else { + histos.fill(HIST("Gen/h2dGenAntiXi0_RecoedEvt"), centrality, ptMC); + } + } + + if (cascMC.pdgCode() > 0) { + histos.fill(HIST("Gen/h2dGenXi0"), centrality, ptMC); + histos.fill(HIST("Gen/h2dGenXi0VsMultMC"), mcCollision.multMCNParticlesEta05(), ptMC); + } else { + histos.fill(HIST("Gen/h2dGenAntiXi0"), centrality, ptMC); + histos.fill(HIST("Gen/h2dGenAntiXi0VsMultMC"), mcCollision.multMCNParticlesEta05(), ptMC); + } + } + } + + //_______________________________________________ + // Process functions + void processRealData(soa::Join const& collisions, + Xi0Candidates const& fullXi0s, + V0Candidates const& fullV0s, + DauTracks const&) + { + analyseRecoedXi0s(collisions, fullXi0s, fullV0s); + } + + void processMonteCarlo(soa::Join const& collisions, + Xi0McCandidates const& fullXi0s, + V0Candidates const& fullV0s, + DauTracks const&) + { + analyseRecoedXi0s(collisions, fullXi0s, fullV0s); + } + + void processGenerated(soa::Join const& mcCollisions, + soa::Join const& collisions, + soa::Join const& cascMCCores) + { + analyseGeneratedXi0s(mcCollisions, collisions, cascMCCores); + } + + PROCESS_SWITCH(DerivedXi0Analysis, processRealData, "process as if real data", true); + PROCESS_SWITCH(DerivedXi0Analysis, processMonteCarlo, "process reconstructed information in MC", false); + PROCESS_SWITCH(DerivedXi0Analysis, processGenerated, "process pure generated information in MC", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/Tasks/Strangeness/derivedcascadeanalysis.cxx b/PWGLF/Tasks/Strangeness/derivedcascadeanalysis.cxx index 7f5e082d9d5..5e70a91ec90 100644 --- a/PWGLF/Tasks/Strangeness/derivedcascadeanalysis.cxx +++ b/PWGLF/Tasks/Strangeness/derivedcascadeanalysis.cxx @@ -264,10 +264,10 @@ struct Derivedcascadeanalysis { TH1F* hOmegaLambdaPointingAngleSelection = nullptr; o2::ccdb::CcdbApi ccdbApi; - Service ccdb; + Service ccdb{}; ctpRateFetcher rateFetcher; - Service pdgDB; + Service pdgDB{}; uint16_t selectionCheckMask = 0; double selectionCheck = -1; @@ -581,7 +581,7 @@ struct Derivedcascadeanalysis { } } } - void initCosPASelectionHistograms(float timeStamp) + void initCosPASelectionHistograms(long timeStamp) { auto* listHistograms = ccdb->getForTimeStamp(histoCCDBPath, timeStamp); @@ -1170,15 +1170,23 @@ struct Derivedcascadeanalysis { return true; } - void fillHistOccupancyCheck(double pt, double mass, float occup, float centrality, bool isPos) + void fillHistOccupancyCheck(double pt, double mass, float occup, float centrality, bool isPos, bool isMC) { static_for<0, 9>([&](auto i) { constexpr int In = i.value; if (centrality < CentralityIntervals[In + 1] && centrality > CentralityIntervals[In]) { if (isPos) { - histos.fill(HIST("InvMassAfterSelCent") + HIST(CentIndex[In]) + HIST("/hPositiveCascade"), pt, mass, occup); + if (isMC) { + histos.fill(HIST("InvMassAfterSelCent") + HIST(CentIndex[In]) + HIST("/hPositiveCascadeMCTruth"), pt, mass, occup); + } else { + histos.fill(HIST("InvMassAfterSelCent") + HIST(CentIndex[In]) + HIST("/hPositiveCascade"), pt, mass, occup); + } } else { - histos.fill(HIST("InvMassAfterSelCent") + HIST(CentIndex[In]) + HIST("/hNegativeCascade"), pt, mass, occup); + if (isMC) { + histos.fill(HIST("InvMassAfterSelCent") + HIST(CentIndex[In]) + HIST("/hNegativeCascadeMCTruth"), pt, mass, occup); + } else { + histos.fill(HIST("InvMassAfterSelCent") + HIST(CentIndex[In]) + HIST("/hNegativeCascade"), pt, mass, occup); + } } } }); @@ -1209,6 +1217,19 @@ struct Derivedcascadeanalysis { } }); } + void fillHistOccupancyCheckMC(double pt, double mass, float occup, float centrality, bool isPos) + { + static_for<0, 9>([&](auto i) { + constexpr int In = i.value; + if (centrality < CentralityIntervals[In + 1] && centrality > CentralityIntervals[In]) { + if (isPos) { + histos.fill(HIST("InvMassAfterSelCent") + HIST(CentIndex[In]) + HIST("/hPositiveCascadeMCTruth"), pt, mass, occup); + } else { + histos.fill(HIST("InvMassAfterSelCent") + HIST(CentIndex[In]) + HIST("/hNegativeCascadeMCTruth"), pt, mass, occup); + } + } + }); + } void fillHistPtDepSelectionStudy(double pt, double mass, double sel, uint64_t selectionCheckMask, bool isPos) { static_for<0, 11>([&](auto i) { @@ -1624,7 +1645,10 @@ struct Derivedcascadeanalysis { } if (qaFlags.doOccupancyCheck) { - fillHistOccupancyCheck(recoPt, invmass, occupancy, centrality, isPositive); + fillHistOccupancyCheck(recoPt, invmass, occupancy, centrality, isPositive, false); + if (isTrueMCCascadeDecay) { + fillHistOccupancyCheck(ptmc, invmass, occupancy, centrality, isPositive, true); + } } if (isPositive) { histos.fill(HIST("InvMassAfterSel/hPositiveCascade"), recoPt, invmass, centrality); @@ -1893,8 +1917,8 @@ struct Derivedcascadeanalysis { PROCESS_SWITCH(Derivedcascadeanalysis, processCascadesMCforEffRun2, "cascade analysis, run2 rec MC", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(context)}; } diff --git a/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx b/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx index 9638d3954f6..1d7a4877abd 100644 --- a/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx +++ b/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx @@ -60,6 +60,7 @@ #include #include +#include #include #include @@ -131,9 +132,10 @@ struct derivedlambdakzeroanalysis { // for MC Configurable doMCAssociation{"doMCAssociation", true, "if MC, do MC association"}; - Configurable doTreatPiToMuon{"doTreatPiToMuon", false, "Take pi decay into muon into account in MC"}; + Configurable doTreatPiToMuon{"doTreatPiToMuon", true, "Take pi decay into muon into account in MC"}; Configurable doCollisionAssociationQA{"doCollisionAssociationQA", true, "check collision association"}; Configurable doSecondaryV0s{"doSecondaryV0s", 0, "Look at secondary V0s? 0: No; 1: yes via a loop on V0MCCores; 2: yes via a loop on CascMCCores"}; + Configurable doQAfeeddown{"doQAfeeddown", true, "if MC, fill feeddown QA histograms"}; struct : ConfigurableGroup { std::string prefix = "eventSelections"; // JSON group name @@ -374,6 +376,8 @@ struct derivedlambdakzeroanalysis { // MC coll assoc QA axis ConfigurableAxis axisMonteCarloNch{"axisMonteCarloNch", {300, 0.0f, 3000.0f}, "N_{ch} MC"}; + + ConfigurableAxis axisPDGCodeMothers{"axisPDGCodeMothers", {7001, -3500.5, 3500.5}, "PDG code of mother particle"}; } axisConfigurations; // UPC selections @@ -457,6 +461,7 @@ struct derivedlambdakzeroanalysis { uint64_t maskSelectionLambda = 0; uint64_t maskSelectionAntiLambda = 0; + uint64_t secondaryMaskSelectionK0Short = 0; uint64_t secondaryMaskSelectionLambda = 0; uint64_t secondaryMaskSelectionAntiLambda = 0; @@ -669,6 +674,7 @@ struct derivedlambdakzeroanalysis { BITSET(maskSelectionAntiLambda, selPhysPrimAntiLambda); // No primary requirement for feeddown matrix + secondaryMaskSelectionK0Short = maskTopological | maskTrackProperties | maskK0ShortSpecific; secondaryMaskSelectionLambda = maskTopological | maskTrackProperties | maskLambdaSpecific; secondaryMaskSelectionAntiLambda = maskTopological | maskTrackProperties | maskAntiLambdaSpecific; @@ -1115,6 +1121,25 @@ struct derivedlambdakzeroanalysis { } } + if (analyseK0Short && doQAfeeddown && (doprocessMonteCarloRun3 || doprocessMonteCarloRun2)) { + histos.add("h2dK0ShortMothers", "h2dK0ShortMothers", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + histos.add("h2dK0ShortMothersIsPrimary", "h2dK0ShortMothersIsPrimary", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + histos.add("h3dK0ShortMothers", "h3dK0ShortMothers", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + histos.add("h3dK0ShortMothersIsPrimary", "h3dK0ShortMothersIsPrimary", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + } + if (analyseLambda && doQAfeeddown && (doprocessMonteCarloRun3 || doprocessMonteCarloRun2)) { + histos.add("h2dLambdaMothers", "h2dLambdaMothers", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + histos.add("h2dLambdaMothersIsPrimary", "h2dLambdaMothersIsPrimary", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + histos.add("h3dLambdaMothers", "h3dLambdaMothers", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + histos.add("h3dLambdaMothersIsPrimary", "h3dLambdaMothersIsPrimary", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + } + if (analyseAntiLambda && doQAfeeddown && (doprocessMonteCarloRun3 || doprocessMonteCarloRun2)) { + histos.add("h2dAntiLambdaMothers", "h2dAntiLambdaMothers", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + histos.add("h2dAntiLambdaMothersIsPrimary", "h2dAntiLambdaMothersIsPrimary", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + histos.add("h3dAntiLambdaMothers", "h3dAntiLambdaMothers", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + histos.add("h3dAntiLambdaMothersIsPrimary", "h3dAntiLambdaMothersIsPrimary", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + } + if (analyseK0Short) histos.add("hMassK0Short", "hMassK0Short", kTH1D, {axisConfigurations.axisK0Mass}); if (analyseLambda) @@ -1432,7 +1457,7 @@ struct derivedlambdakzeroanalysis { } template - uint64_t computeReconstructionBitmap(TV0 const& v0, TCollision const& collision, float rapidityLambda, float rapidityK0Short, float /*pT*/) + uint64_t computeReconstructionBitmap(TV0 const& v0, TCollision const& collision, float rapidityLambda, float rapidityK0Short) // precalculate this information so that a check is one mask operation, not many { uint64_t bitMap = 0; @@ -1706,18 +1731,21 @@ struct derivedlambdakzeroanalysis { if (v0.pdgCode() == PDG_t::kK0Short && isPositivePion && isNegativePion) { BITSET(bitMap, selConsiderK0Short); - if (v0.isPhysicalPrimary()) + if (v0.isPhysicalPrimary()) { BITSET(bitMap, selPhysPrimK0Short); + } } if (v0.pdgCode() == PDG_t::kLambda0 && isPositiveProton && isNegativePion) { BITSET(bitMap, selConsiderLambda); - if (v0.isPhysicalPrimary()) + if (v0.isPhysicalPrimary()) { BITSET(bitMap, selPhysPrimLambda); + } } if (v0.pdgCode() == PDG_t::kLambda0Bar && isPositivePion && isNegativeProton) { BITSET(bitMap, selConsiderAntiLambda); - if (v0.isPhysicalPrimary()) + if (v0.isPhysicalPrimary()) { BITSET(bitMap, selPhysPrimAntiLambda); + } } return bitMap; } @@ -1893,7 +1921,7 @@ struct derivedlambdakzeroanalysis { float k0shortScore = -1; if (mlConfigurations.calculateK0ShortScores) { // evaluate machine-learning scores - float* k0shortProbability = mlCustomModelK0Short.evalModel(inputFeatures); + const std::vector k0shortProbability = mlCustomModelK0Short.evalModel(inputFeatures); k0shortScore = k0shortProbability[1]; } else { k0shortScore = v0.k0ShortBDTScore(); @@ -1908,7 +1936,7 @@ struct derivedlambdakzeroanalysis { float lambdaScore = -1; if (mlConfigurations.calculateLambdaScores) { // evaluate machine-learning scores - float* lambdaProbability = mlCustomModelLambda.evalModel(inputFeatures); + const std::vector lambdaProbability = mlCustomModelLambda.evalModel(inputFeatures); lambdaScore = lambdaProbability[1]; } else { lambdaScore = v0.lambdaBDTScore(); @@ -1923,7 +1951,7 @@ struct derivedlambdakzeroanalysis { float antiLambdaScore = -1; if (mlConfigurations.calculateAntiLambdaScores) { // evaluate machine-learning scores - float* antilambdaProbability = mlCustomModelAntiLambda.evalModel(inputFeatures); + const std::vector antilambdaProbability = mlCustomModelAntiLambda.evalModel(inputFeatures); antiLambdaScore = antilambdaProbability[1]; } else { antiLambdaScore = v0.antiLambdaBDTScore(); @@ -2375,6 +2403,43 @@ struct derivedlambdakzeroanalysis { } } + template + void fillFeeddownQA(TV0 const& v0, float pt, float centrality, uint64_t selMap) + // fill feeddown matrix for Lambdas or AntiLambdas + // fixme: a potential improvement would be to consider mass windows for the l/al + { + if (!v0.has_motherMCPart()) + return; // does not have mother particle in record, skip + + auto v0mother = v0.motherMCPart(); + + // __________________________________________ + if (verifyMask(selMap, secondaryMaskSelectionK0Short) && analyseK0Short) { + histos.fill(HIST("h2dK0ShortMothers"), pt, v0mother.pdgCode()); + histos.fill(HIST("h3dK0ShortMothers"), centrality, pt, v0mother.pdgCode()); + if (v0mother.isPhysicalPrimary()) { + histos.fill(HIST("h2dK0ShortMothersIsPrimary"), pt, v0mother.pdgCode()); + histos.fill(HIST("h3dK0ShortMothersIsPrimary"), centrality, pt, v0mother.pdgCode()); + } + } + if (verifyMask(selMap, secondaryMaskSelectionLambda) && analyseLambda) { + histos.fill(HIST("h2dLambdaMothers"), pt, v0mother.pdgCode()); + histos.fill(HIST("h3dLambdaMothers"), centrality, pt, v0mother.pdgCode()); + if (v0mother.isPhysicalPrimary()) { + histos.fill(HIST("h2dLambdaMothersIsPrimary"), pt, v0mother.pdgCode()); + histos.fill(HIST("h3dLambdaMothersIsPrimary"), centrality, pt, v0mother.pdgCode()); + } + } + if (verifyMask(selMap, secondaryMaskSelectionAntiLambda) && analyseAntiLambda) { + histos.fill(HIST("h2dAntiLambdaMothers"), pt, v0mother.pdgCode()); + histos.fill(HIST("h3dAntiLambdaMothers"), centrality, pt, v0mother.pdgCode()); + if (v0mother.isPhysicalPrimary()) { + histos.fill(HIST("h2dAntiLambdaMothersIsPrimary"), pt, v0mother.pdgCode()); + histos.fill(HIST("h3dAntiLambdaMothersIsPrimary"), centrality, pt, v0mother.pdgCode()); + } + } + } + template bool isEventAccepted(TCollision const& collision, bool fillHists) // check whether the collision passes our collision selections @@ -2926,9 +2991,9 @@ struct derivedlambdakzeroanalysis { } // ______________________________________________________ - // Real data processing - no MC subscription - template - void analyzeRecoedV0sInRealData(TCollision const& collision, TV0s const& fullV0s) + // Recoed data and MC processing (can use MC info if subscribed to it) + template + void analyzeRecoedV0s(TCollision const& collision, TV0s const& fullV0s, TMCCollisions const& /*mcCollisions*/) { // Fire up CCDB if ((mlConfigurations.useK0ShortScores && mlConfigurations.calculateK0ShortScores) || @@ -2951,8 +3016,6 @@ struct derivedlambdakzeroanalysis { // Fill recoed event properties fillReconstructedEventProperties(collision, centrality, collisionOccupancy, interactionRate, gapSide, selGapSide); - histos.fill(HIST("hInteractionRateVsOccupancy"), interactionRate, collisionOccupancy); - // __________________________________________ // perform main analysis int nK0Shorts = 0; @@ -2965,99 +3028,58 @@ struct derivedlambdakzeroanalysis { if (v0.v0Type() != v0Selections.v0TypeSelection && v0Selections.v0TypeSelection > -1) continue; // skip V0s that are not standard - // fill AP plot for all V0s - histos.fill(HIST("GeneralQA/h2dArmenterosAll"), v0.alpha(), v0.qtarm()); - - uint64_t selMap = computeReconstructionBitmap(v0, collision, v0.yLambda(), v0.yK0Short(), v0.pt()); - - // consider for histograms for all species - BITSET(selMap, selConsiderK0Short); - BITSET(selMap, selConsiderLambda); - BITSET(selMap, selConsiderAntiLambda); - - BITSET(selMap, selPhysPrimK0Short); - BITSET(selMap, selPhysPrimLambda); - BITSET(selMap, selPhysPrimAntiLambda); - - analyseCandidate(v0, v0.pt(), v0.yLambda(), v0.yK0Short(), centrality, selMap, selGapSide, nK0Shorts, nLambdas, nAntiLambdas); - } // end v0 loop - - // fill the histograms with the number of reconstructed K0s/Lambda/antiLambda per collision - if (analyseK0Short) { - histos.fill(HIST("h2dNbrOfK0ShortVsCentrality"), centrality, nK0Shorts); - } - if (analyseLambda) { - histos.fill(HIST("h2dNbrOfLambdaVsCentrality"), centrality, nLambdas); - } - if (analyseAntiLambda) { - histos.fill(HIST("h2dNbrOfAntiLambdaVsCentrality"), centrality, nAntiLambdas); - } - } - - // ______________________________________________________ - // Simulated processing (subscribes to MC information too) - template - void analyzeRecoedV0sInMonteCarlo(TCollision const& collision, TV0s const& fullV0s, TMCCollisions const& /*mcCollisions*/) - { - // Fire up CCDB - if ((mlConfigurations.useK0ShortScores && mlConfigurations.calculateK0ShortScores) || - (mlConfigurations.useLambdaScores && mlConfigurations.calculateLambdaScores) || - (mlConfigurations.useAntiLambdaScores && mlConfigurations.calculateAntiLambdaScores) || - v0Selections.rejectTPCsectorBoundary) { - initCCDB(collision); - } - - if (!isEventAccepted(collision, true)) { - return; - } - - float centrality = -1; - float collisionOccupancy = -2; // -1 already taken for the case where occupancy cannot be evaluated - double interactionRate = -1; - // gap side - int gapSide = -1; - int selGapSide = -1; // -1 --> Hadronic ; 0 --> Single Gap - A side ; 1 --> Single Gap - C side ; 2 --> Double Gap - both A & C sides - // Fill recoed event properties - fillReconstructedEventProperties(collision, centrality, collisionOccupancy, interactionRate, gapSide, selGapSide); - - histos.fill(HIST("hInteractionRateVsOccupancy"), interactionRate, collisionOccupancy); + float pt = v0.pt(); + float ptMC = -1.f; + float yK0Short = v0.yK0Short(); + float yLambda = v0.yLambda(); + bool correctCollision = false; + int mcNch = -1; + uint64_t selMapMCassociation = 0; + + if constexpr (requires { v0.v0MCCoreId(); }) { + if (doMCAssociation) { + if (!v0.has_v0MCCore()) + continue; - // __________________________________________ - // perform main analysis - int nK0Shorts = 0; - int nLambdas = 0; - int nAntiLambdas = 0; - for (auto const& v0 : fullV0s) { - if (std::abs(v0.negativeeta()) > v0Selections.daughterEtaCut || std::abs(v0.positiveeta()) > v0Selections.daughterEtaCut) - continue; // remove acceptance that's badly reproduced by MC / superfluous in future + auto v0MC = v0.template v0MCCore_as>(); - if (v0.v0Type() != v0Selections.v0TypeSelection && v0Selections.v0TypeSelection > -1) - continue; // skip V0s that are not standard + ptMC = RecoDecay::sqrtSumOfSquares(v0MC.pxPosMC() + v0MC.pxNegMC(), v0MC.pyPosMC() + v0MC.pyNegMC()); + pt = ptMC; + yK0Short = RecoDecay::y(std::array{v0MC.pxPosMC() + v0MC.pxNegMC(), v0MC.pyPosMC() + v0MC.pyNegMC(), v0MC.pzPosMC() + v0MC.pzNegMC()}, o2::constants::physics::MassKaonNeutral); + yLambda = RecoDecay::y(std::array{v0MC.pxPosMC() + v0MC.pxNegMC(), v0MC.pyPosMC() + v0MC.pyNegMC(), v0MC.pzPosMC() + v0MC.pzNegMC()}, o2::constants::physics::MassLambda); - if (!v0.has_v0MCCore()) - continue; + selMapMCassociation = computeMCAssociation(v0MC); - auto v0MC = v0.template v0MCCore_as>(); + if constexpr (requires { collision.straMCCollisionId(); }) { + // check collision association explicitly + if (collision.has_straMCCollision()) { + auto mcCollision = collision.template straMCCollision_as(); + mcNch = mcCollision.multMCNParticlesEta05(); + correctCollision = (v0MC.straMCCollisionId() == mcCollision.globalIndex()); + } + } + } + } // fill AP plot for all V0s histos.fill(HIST("GeneralQA/h2dArmenterosAll"), v0.alpha(), v0.qtarm()); - float ptmc = RecoDecay::sqrtSumOfSquares(v0MC.pxPosMC() + v0MC.pxNegMC(), v0MC.pyPosMC() + v0MC.pyNegMC()); - float ymc = 1e3; - if (v0MC.pdgCode() == PDG_t::kK0Short) - ymc = RecoDecay::y(std::array{v0MC.pxPosMC() + v0MC.pxNegMC(), v0MC.pyPosMC() + v0MC.pyNegMC(), v0MC.pzPosMC() + v0MC.pzNegMC()}, o2::constants::physics::MassKaonNeutral); - else if (std::abs(v0MC.pdgCode()) == PDG_t::kLambda0) - ymc = RecoDecay::y(std::array{v0MC.pxPosMC() + v0MC.pxNegMC(), v0MC.pyPosMC() + v0MC.pyNegMC(), v0MC.pzPosMC() + v0MC.pzNegMC()}, o2::constants::physics::MassLambda); - - uint64_t selMap = computeReconstructionBitmap(v0, collision, ymc, ymc, ptmc); - selMap = selMap | computeMCAssociation(v0MC); + uint64_t selMap = computeReconstructionBitmap(v0, collision, yLambda, yK0Short); + selMap |= selMapMCassociation; // feeddown matrix always with association - if (calculateFeeddownMatrix) - fillFeeddownMatrix(v0, ptmc, centrality, selMap); + if constexpr (requires { v0.motherMCPartId(); }) { + if (calculateFeeddownMatrix) { + fillFeeddownMatrix(v0, ptMC, centrality, selMap); + } + if (doQAfeeddown) { + fillFeeddownQA(v0, ptMC, centrality, selMap); + } + } // consider only associated candidates if asked to do so, disregard association - if (!doMCAssociation) { + // do not perform MC association when running over real data + if (doprocessRealDataRun3 || doprocessRealDataRun2 || !doMCAssociation) { BITSET(selMap, selConsiderK0Short); BITSET(selMap, selConsiderLambda); BITSET(selMap, selConsiderAntiLambda); @@ -3067,18 +3089,13 @@ struct derivedlambdakzeroanalysis { BITSET(selMap, selPhysPrimAntiLambda); } - analyseCandidate(v0, ptmc, ymc, ymc, centrality, selMap, selGapSide, nK0Shorts, nLambdas, nAntiLambdas); + analyseCandidate(v0, pt, yLambda, yK0Short, centrality, selMap, selGapSide, nK0Shorts, nLambdas, nAntiLambdas); - if (doCollisionAssociationQA) { - // check collision association explicitly - bool correctCollision = false; - int mcNch = -1; - if (collision.has_straMCCollision()) { - auto mcCollision = collision.template straMCCollision_as(); - mcNch = mcCollision.multMCNParticlesEta05(); - correctCollision = (v0MC.straMCCollisionId() == mcCollision.globalIndex()); + if constexpr (requires { collision.straMCCollisionId(); }) { + if (doCollisionAssociationQA) { + // check collision association explicitly + analyseCollisionAssociation(v0, ptMC, mcNch, correctCollision, selMap); } - analyseCollisionAssociation(v0, ptmc, mcNch, correctCollision, selMap); } } // end v0 loop @@ -3535,28 +3552,28 @@ struct derivedlambdakzeroanalysis { // Real data processing in Run 3 - no MC subscription void processRealDataRun3(soa::Join::iterator const& collision, V0Candidates const& fullV0s, DauTracks const&) { - analyzeRecoedV0sInRealData(collision, fullV0s); + analyzeRecoedV0s(collision, fullV0s, static_cast(nullptr)); } // ______________________________________________________ // Real data processing in Run 2 - no MC subscription void processRealDataRun2(soa::Join::iterator const& collision, V0Candidates const& fullV0s, DauTracks const&) { - analyzeRecoedV0sInRealData(collision, fullV0s); + analyzeRecoedV0s(collision, fullV0s, static_cast(nullptr)); } // ______________________________________________________ // Simulated processing in Run 3 (subscribes to MC information too) void processMonteCarloRun3(soa::Join::iterator const& collision, V0McCandidates const& fullV0s, DauTracks const&, aod::MotherMCParts const&, soa::Join const& mccollisions, soa::Join const&) { - analyzeRecoedV0sInMonteCarlo(collision, fullV0s, mccollisions); + analyzeRecoedV0s(collision, fullV0s, mccollisions); } // ______________________________________________________ // Simulated processing in Run 2 (subscribes to MC information too) void processMonteCarloRun2(soa::Join::iterator const& collision, V0McCandidates const& fullV0s, DauTracks const&, aod::MotherMCParts const&, soa::Join const& mccollisions, soa::Join const&) { - analyzeRecoedV0sInMonteCarlo(collision, fullV0s, mccollisions); + analyzeRecoedV0s(collision, fullV0s, mccollisions); } // ______________________________________________________ diff --git a/PWGLF/Tasks/Strangeness/derivedupcanalysis.cxx b/PWGLF/Tasks/Strangeness/derivedupcanalysis.cxx index 1741b68afa0..5470d63e14a 100644 --- a/PWGLF/Tasks/Strangeness/derivedupcanalysis.cxx +++ b/PWGLF/Tasks/Strangeness/derivedupcanalysis.cxx @@ -23,6 +23,7 @@ #include "Common/Core/RecoDecay.h" #include +#include #include #include #include @@ -33,6 +34,7 @@ #include #include #include +#include #include #include @@ -93,7 +95,7 @@ struct Derivedupcanalysis { Configurable analyseOmega{"analyseOmega", true, "process Omega-like candidates"}; Configurable analyseAntiOmega{"analyseAntiOmega", true, "process AntiOmega-like candidates"}; - Configurable> generatorIds{"generatorIds", std::vector{-1}, "MC generatorIds to process"}; + Configurable> generatorIds{"generatorIds", std::vector{-1}, "generator IDs for candidate truth origins and generated particles; event QA uses vertex labels"}; // Event selections struct : ConfigurableGroup { @@ -177,6 +179,7 @@ struct Derivedupcanalysis { static constexpr int kOmegaPartID = 5; static constexpr int kAntiOmegaPartID = 6; static constexpr float kNoPidNsigmaCut = 1e5f; + static constexpr float kNoTofDeltaTimeCut = 1e9f; static constexpr float kDefaultInvalidRapidity = 1e6f; static constexpr float kDefaultInvalidCtau = 1e6f; static constexpr float kDisabledArmenterosCut = 1e-4f; @@ -258,7 +261,7 @@ struct Derivedupcanalysis { } axisDetectors; // for MC - Configurable doMCAssociation{"doMCAssociation", true, "if MC, do MC association"}; + Configurable doMCAssociation{"doMCAssociation", true, "identify candidate species and generator from MC truth; if disabled, select events by vertex generator"}; Configurable doTreatPiToMuon{"doTreatPiToMuon", false, "Take pi decay into muon into account in MC"}; Configurable calculateFeeddownMatrix{"calculateFeeddownMatrix", true, "fill feeddown matrix if MC"}; ConfigurableAxis axisGeneratorIds{"axisGeneratorIds", {256, -0.5f, 255.5f}, "axis for generatorIds"}; @@ -348,144 +351,154 @@ struct Derivedupcanalysis { } } + void setTOFSelection(std::bitset& mask, float nsigmaCut, float deltaTimeCut, int nsigmaSelection, int deltaTimeSelection) + { + if (nsigmaCut < kNoPidNsigmaCut) + mask.set(nsigmaSelection); + if (deltaTimeCut < kNoTofDeltaTimeCut) + mask.set(deltaTimeSelection); + } + template - void addTopoHistograms(HistogramRegistry& histos) + void addTopoHistograms(HistogramRegistry& registry) { - const bool isCascade = partID >= kFirstCascadePartID; - if (isCascade) { - histos.add(Form("%s/hCascCosPA", kParticlenames[partID].data()), "hCascCosPA", kTH2F, {axisPtCoarse, {100, 0.9f, 1.0f}}); - histos.add(Form("%s/hDCACascDaughters", kParticlenames[partID].data()), "hDCACascDaughters", kTH2F, {axisPtCoarse, {44, 0.0f, 2.2f}}); - histos.add(Form("%s/hCascRadius", kParticlenames[partID].data()), "hCascRadius", kTH2D, {axisPtCoarse, {500, 0.0f, 50.0f}}); - histos.add(Form("%s/hMesonDCAToPV", kParticlenames[partID].data()), "hMesonDCAToPV", kTH2F, {axisPtCoarse, axisDCAtoPV}); - histos.add(Form("%s/hBaryonDCAToPV", kParticlenames[partID].data()), "hBaryonDCAToPV", kTH2F, {axisPtCoarse, axisDCAtoPV}); - histos.add(Form("%s/hBachDCAToPV", kParticlenames[partID].data()), "hBachDCAToPV", kTH2F, {axisPtCoarse, {200, -1.0f, 1.0f}}); - histos.add(Form("%s/hV0CosPA", kParticlenames[partID].data()), "hV0CosPA", kTH2F, {axisPtCoarse, {100, 0.9f, 1.0f}}); - histos.add(Form("%s/hV0Radius", kParticlenames[partID].data()), "hV0Radius", kTH2D, {axisPtCoarse, axisV0Radius}); - histos.add(Form("%s/hDCAV0Daughters", kParticlenames[partID].data()), "hDCAV0Daughters", kTH2F, {axisPtCoarse, axisDCAdau}); - histos.add(Form("%s/hDCAV0ToPV", kParticlenames[partID].data()), "hDCAV0ToPV", kTH2F, {axisPtCoarse, {44, 0.0f, 2.2f}}); - histos.add(Form("%s/hMassLambdaDau", kParticlenames[partID].data()), "hMassLambdaDau", kTH2F, {axisPtCoarse, axisLambdaMass}); - histos.add(Form("%s/hCtau", kParticlenames[partID].data()), "hCtau", kTH2F, {axisPtCoarse, axisCtau}); + // Template instantiations intentionally make this particle-type comparison constant. + // cppcheck-suppress knownConditionTrueFalse + if constexpr (partID >= kFirstCascadePartID) { + registry.add(Form("%s/hCascCosPA", kParticlenames[partID].data()), "hCascCosPA", kTH2F, {axisPtCoarse, {100, 0.9f, 1.0f}}); + registry.add(Form("%s/hDCACascDaughters", kParticlenames[partID].data()), "hDCACascDaughters", kTH2F, {axisPtCoarse, {44, 0.0f, 2.2f}}); + registry.add(Form("%s/hCascRadius", kParticlenames[partID].data()), "hCascRadius", kTH2D, {axisPtCoarse, {500, 0.0f, 50.0f}}); + registry.add(Form("%s/hMesonDCAToPV", kParticlenames[partID].data()), "hMesonDCAToPV", kTH2F, {axisPtCoarse, axisDCAtoPV}); + registry.add(Form("%s/hBaryonDCAToPV", kParticlenames[partID].data()), "hBaryonDCAToPV", kTH2F, {axisPtCoarse, axisDCAtoPV}); + registry.add(Form("%s/hBachDCAToPV", kParticlenames[partID].data()), "hBachDCAToPV", kTH2F, {axisPtCoarse, {200, -1.0f, 1.0f}}); + registry.add(Form("%s/hV0CosPA", kParticlenames[partID].data()), "hV0CosPA", kTH2F, {axisPtCoarse, {100, 0.9f, 1.0f}}); + registry.add(Form("%s/hV0Radius", kParticlenames[partID].data()), "hV0Radius", kTH2D, {axisPtCoarse, axisV0Radius}); + registry.add(Form("%s/hDCAV0Daughters", kParticlenames[partID].data()), "hDCAV0Daughters", kTH2F, {axisPtCoarse, axisDCAdau}); + registry.add(Form("%s/hDCAV0ToPV", kParticlenames[partID].data()), "hDCAV0ToPV", kTH2F, {axisPtCoarse, {44, 0.0f, 2.2f}}); + registry.add(Form("%s/hMassLambdaDau", kParticlenames[partID].data()), "hMassLambdaDau", kTH2F, {axisPtCoarse, axisLambdaMass}); + registry.add(Form("%s/hCtau", kParticlenames[partID].data()), "hCtau", kTH2F, {axisPtCoarse, axisCtau}); if (doBachelorBaryonCut) { - histos.add(Form("%s/hBachBaryonCosPA", kParticlenames[partID].data()), "hBachBaryonCosPA", kTH2F, {axisPtCoarse, {100, 0.0f, 1.0f}}); - histos.add(Form("%s/hBachBaryonDCAxyToPV", kParticlenames[partID].data()), "hBachBaryonDCAxyToPV", kTH2F, {axisPtCoarse, {300, -3.0f, 3.0f}}); + registry.add(Form("%s/hBachBaryonCosPA", kParticlenames[partID].data()), "hBachBaryonCosPA", kTH2F, {axisPtCoarse, {100, 0.0f, 1.0f}}); + registry.add(Form("%s/hBachBaryonDCAxyToPV", kParticlenames[partID].data()), "hBachBaryonDCAxyToPV", kTH2F, {axisPtCoarse, {300, -3.0f, 3.0f}}); } } else { - histos.add(Form("%s/hPosDCAToPV", kParticlenames[partID].data()), "hPosDCAToPV", kTH1F, {axisDCAtoPV}); - histos.add(Form("%s/hNegDCAToPV", kParticlenames[partID].data()), "hNegDCAToPV", kTH1F, {axisDCAtoPV}); - histos.add(Form("%s/hDCADaughters", kParticlenames[partID].data()), "hDCADaughters", kTH1F, {axisDCAdau}); - histos.add(Form("%s/hPointingAngle", kParticlenames[partID].data()), "hPointingAngle", kTH1F, {axisPointingAngle}); - histos.add(Form("%s/hCosPA", kParticlenames[partID].data()), "hCosPA", kTH1F, {axisCosPA}); - histos.add(Form("%s/hV0Radius", kParticlenames[partID].data()), "hV0Radius", kTH1F, {axisV0Radius}); - histos.add(Form("%s/h2dPositiveITSvsTPCpts", kParticlenames[partID].data()), "h2dPositiveITSvsTPCpts", kTH2F, {axisTPCrows, axisITSclus}); - histos.add(Form("%s/h2dNegativeITSvsTPCpts", kParticlenames[partID].data()), "h2dNegativeITSvsTPCpts", kTH2F, {axisTPCrows, axisITSclus}); - histos.add(Form("%s/hCtau", kParticlenames[partID].data()), "hCtau", kTH2F, {axisPtCoarse, axisCtau}); + registry.add(Form("%s/hPosDCAToPV", kParticlenames[partID].data()), "hPosDCAToPV", kTH1F, {axisDCAtoPV}); + registry.add(Form("%s/hNegDCAToPV", kParticlenames[partID].data()), "hNegDCAToPV", kTH1F, {axisDCAtoPV}); + registry.add(Form("%s/hDCADaughters", kParticlenames[partID].data()), "hDCADaughters", kTH1F, {axisDCAdau}); + registry.add(Form("%s/hPointingAngle", kParticlenames[partID].data()), "hPointingAngle", kTH1F, {axisPointingAngle}); + registry.add(Form("%s/hCosPA", kParticlenames[partID].data()), "hCosPA", kTH1F, {axisCosPA}); + registry.add(Form("%s/hV0Radius", kParticlenames[partID].data()), "hV0Radius", kTH1F, {axisV0Radius}); + registry.add(Form("%s/h2dPositiveITSvsTPCpts", kParticlenames[partID].data()), "h2dPositiveITSvsTPCpts", kTH2F, {axisTPCrows, axisITSclus}); + registry.add(Form("%s/h2dNegativeITSvsTPCpts", kParticlenames[partID].data()), "h2dNegativeITSvsTPCpts", kTH2F, {axisTPCrows, axisITSclus}); + registry.add(Form("%s/hCtau", kParticlenames[partID].data()), "hCtau", kTH2F, {axisPtCoarse, axisCtau}); } } template - void addTPCQAHistograms(HistogramRegistry& histos) + void addTPCQAHistograms(HistogramRegistry& registry) { - const bool isCascade = partID >= kFirstCascadePartID; - histos.add(Form("%s/h3dPosNsigmaTPC", kParticlenames[partID].data()), "h3dPosNsigmaTPC", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); - histos.add(Form("%s/h3dNegNsigmaTPC", kParticlenames[partID].data()), "h3dNegNsigmaTPC", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); - histos.add(Form("%s/h3dPosTPCsignal", kParticlenames[partID].data()), "h3dPosTPCsignal", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); - histos.add(Form("%s/h3dNegTPCsignal", kParticlenames[partID].data()), "h3dNegTPCsignal", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); + registry.add(Form("%s/h3dPosNsigmaTPC", kParticlenames[partID].data()), "h3dPosNsigmaTPC", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); + registry.add(Form("%s/h3dNegNsigmaTPC", kParticlenames[partID].data()), "h3dNegNsigmaTPC", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); + registry.add(Form("%s/h3dPosTPCsignal", kParticlenames[partID].data()), "h3dPosTPCsignal", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); + registry.add(Form("%s/h3dNegTPCsignal", kParticlenames[partID].data()), "h3dNegTPCsignal", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); - histos.add(Form("%s/h3dPosNsigmaTPCvsTrackPtot", kParticlenames[partID].data()), "h3dPosNsigmaTPCvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); - histos.add(Form("%s/h3dNegNsigmaTPCvsTrackPtot", kParticlenames[partID].data()), "h3dNegNsigmaTPCvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); + registry.add(Form("%s/h3dPosNsigmaTPCvsTrackPtot", kParticlenames[partID].data()), "h3dPosNsigmaTPCvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); + registry.add(Form("%s/h3dNegNsigmaTPCvsTrackPtot", kParticlenames[partID].data()), "h3dNegNsigmaTPCvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); - histos.add(Form("%s/h3dPosTPCsignalVsTrackPtot", kParticlenames[partID].data()), "h3dPosTPCsignalVsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); - histos.add(Form("%s/h3dNegTPCsignalVsTrackPtot", kParticlenames[partID].data()), "h3dNegTPCsignalVsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); + registry.add(Form("%s/h3dPosTPCsignalVsTrackPtot", kParticlenames[partID].data()), "h3dPosTPCsignalVsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); + registry.add(Form("%s/h3dNegTPCsignalVsTrackPtot", kParticlenames[partID].data()), "h3dNegTPCsignalVsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); - histos.add(Form("%s/h3dPosNsigmaTPCvsTrackPt", kParticlenames[partID].data()), "h3dPosNsigmaTPCvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); - histos.add(Form("%s/h3dNegNsigmaTPCvsTrackPt", kParticlenames[partID].data()), "h3dNegNsigmaTPCvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); + registry.add(Form("%s/h3dPosNsigmaTPCvsTrackPt", kParticlenames[partID].data()), "h3dPosNsigmaTPCvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); + registry.add(Form("%s/h3dNegNsigmaTPCvsTrackPt", kParticlenames[partID].data()), "h3dNegNsigmaTPCvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); - histos.add(Form("%s/h3dPosTPCsignalVsTrackPt", kParticlenames[partID].data()), "h3dPosTPCsignalVsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); - histos.add(Form("%s/h3dNegTPCsignalVsTrackPt", kParticlenames[partID].data()), "h3dNegTPCsignalVsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); + registry.add(Form("%s/h3dPosTPCsignalVsTrackPt", kParticlenames[partID].data()), "h3dPosTPCsignalVsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); + registry.add(Form("%s/h3dNegTPCsignalVsTrackPt", kParticlenames[partID].data()), "h3dNegTPCsignalVsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); - if (isCascade) { - histos.add(Form("%s/h3dBachTPCsignal", kParticlenames[partID].data()), "h3dBachTPCsignal", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); - histos.add(Form("%s/h3dBachNsigmaTPC", kParticlenames[partID].data()), "h3dBachNsigmaTPC", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); - histos.add(Form("%s/h3dBachNsigmaTPCvsTrackPtot", kParticlenames[partID].data()), "h3dBachNsigmaTPCvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); - histos.add(Form("%s/h3dBachTPCsignalVsTrackPtot", kParticlenames[partID].data()), "h3dBachTPCsignalVsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); - histos.add(Form("%s/h3dBachNsigmaTPCvsTrackPt", kParticlenames[partID].data()), "h3dBachNsigmaTPCvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); - histos.add(Form("%s/h3dBachTPCsignalVsTrackPt", kParticlenames[partID].data()), "h3dBachTPCsignalVsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); + // cppcheck-suppress knownConditionTrueFalse + if constexpr (partID >= kFirstCascadePartID) { + registry.add(Form("%s/h3dBachTPCsignal", kParticlenames[partID].data()), "h3dBachTPCsignal", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); + registry.add(Form("%s/h3dBachNsigmaTPC", kParticlenames[partID].data()), "h3dBachNsigmaTPC", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); + registry.add(Form("%s/h3dBachNsigmaTPCvsTrackPtot", kParticlenames[partID].data()), "h3dBachNsigmaTPCvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); + registry.add(Form("%s/h3dBachTPCsignalVsTrackPtot", kParticlenames[partID].data()), "h3dBachTPCsignalVsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); + registry.add(Form("%s/h3dBachNsigmaTPCvsTrackPt", kParticlenames[partID].data()), "h3dBachNsigmaTPCvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); + registry.add(Form("%s/h3dBachTPCsignalVsTrackPt", kParticlenames[partID].data()), "h3dBachTPCsignalVsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); } } template - void addTOFQAHistograms(HistogramRegistry& histos) + void addTOFQAHistograms(HistogramRegistry& registry) { - const bool isCascade = partID >= kFirstCascadePartID; - histos.add(Form("%s/h3dPosTOFdeltaT", kParticlenames[partID].data()), "h3dPosTOFdeltaT", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); - histos.add(Form("%s/h3dNegTOFdeltaT", kParticlenames[partID].data()), "h3dNegTOFdeltaT", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); - histos.add(Form("%s/h3dPosTOFdeltaTvsTrackPtot", kParticlenames[partID].data()), "h3dPosTOFdeltaTvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); - histos.add(Form("%s/h3dNegTOFdeltaTvsTrackPtot", kParticlenames[partID].data()), "h3dNegTOFdeltaTvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); - histos.add(Form("%s/h3dPosTOFdeltaTvsTrackPt", kParticlenames[partID].data()), "h3dPosTOFdeltaTvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); - histos.add(Form("%s/h3dNegTOFdeltaTvsTrackPt", kParticlenames[partID].data()), "h3dNegTOFdeltaTvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); - if (isCascade) { - histos.add(Form("%s/h3dBachTOFdeltaT", kParticlenames[partID].data()), "h3dBachTOFdeltaT", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); - histos.add(Form("%s/h3dBachTOFdeltaTvsTrackPtot", kParticlenames[partID].data()), "h3dBachTOFdeltaTvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); - histos.add(Form("%s/h3dBachTOFdeltaTvsTrackPt", kParticlenames[partID].data()), "h3dBachTOFdeltaTvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); + registry.add(Form("%s/h3dPosTOFdeltaT", kParticlenames[partID].data()), "h3dPosTOFdeltaT", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); + registry.add(Form("%s/h3dNegTOFdeltaT", kParticlenames[partID].data()), "h3dNegTOFdeltaT", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); + registry.add(Form("%s/h3dPosTOFdeltaTvsTrackPtot", kParticlenames[partID].data()), "h3dPosTOFdeltaTvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); + registry.add(Form("%s/h3dNegTOFdeltaTvsTrackPtot", kParticlenames[partID].data()), "h3dNegTOFdeltaTvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); + registry.add(Form("%s/h3dPosTOFdeltaTvsTrackPt", kParticlenames[partID].data()), "h3dPosTOFdeltaTvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); + registry.add(Form("%s/h3dNegTOFdeltaTvsTrackPt", kParticlenames[partID].data()), "h3dNegTOFdeltaTvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); + // cppcheck-suppress knownConditionTrueFalse + if constexpr (partID >= kFirstCascadePartID) { + registry.add(Form("%s/h3dBachTOFdeltaT", kParticlenames[partID].data()), "h3dBachTOFdeltaT", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); + registry.add(Form("%s/h3dBachTOFdeltaTvsTrackPtot", kParticlenames[partID].data()), "h3dBachTOFdeltaTvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); + registry.add(Form("%s/h3dBachTOFdeltaTvsTrackPt", kParticlenames[partID].data()), "h3dBachTOFdeltaTvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); } } template - void addKinematicQAHistograms(HistogramRegistry& histos) + void addKinematicQAHistograms(HistogramRegistry& registry) { - const bool isCascade = partID >= kFirstCascadePartID; - histos.add(Form("%s/h3dPosEtaPt", kParticlenames[partID].data()), "h3dPosEtaPt", kTH3F, {axisPtCoarse, axisEta, axisSelGap}); - histos.add(Form("%s/h3dNegEtaPt", kParticlenames[partID].data()), "h3dNegEtaPt", kTH3F, {axisPtCoarse, axisEta, axisSelGap}); - histos.add(Form("%s/h3dRapPt", kParticlenames[partID].data()), "h3dRapPt", kTH3F, {axisPtCoarse, axisRap, axisSelGap}); - if (isCascade) { - histos.add(Form("%s/h3dBachEtaPt", kParticlenames[partID].data()), "h3dBachEtaPt", kTH3F, {axisPtCoarse, axisEta, axisSelGap}); + registry.add(Form("%s/h3dPosEtaPt", kParticlenames[partID].data()), "h3dPosEtaPt", kTH3F, {axisPtCoarse, axisEta, axisSelGap}); + registry.add(Form("%s/h3dNegEtaPt", kParticlenames[partID].data()), "h3dNegEtaPt", kTH3F, {axisPtCoarse, axisEta, axisSelGap}); + registry.add(Form("%s/h3dRapPt", kParticlenames[partID].data()), "h3dRapPt", kTH3F, {axisPtCoarse, axisRap, axisSelGap}); + // cppcheck-suppress knownConditionTrueFalse + if constexpr (partID >= kFirstCascadePartID) { + registry.add(Form("%s/h3dBachEtaPt", kParticlenames[partID].data()), "h3dBachEtaPt", kTH3F, {axisPtCoarse, axisEta, axisSelGap}); } } template - void addDetectorPropHistograms(HistogramRegistry& histos) + void addDetectorPropHistograms(HistogramRegistry& registry) { - const bool isCascade = partID >= kFirstCascadePartID; if (doDetectPropQA == kDetectPropQABase) { - if (isCascade) { - histos.add(Form("%s/h8dDetectPropVsCentrality", kParticlenames[partID].data()), "h8dDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisPtCoarse}); + // cppcheck-suppress knownConditionTrueFalse + if constexpr (partID >= kFirstCascadePartID) { + registry.add(Form("%s/h8dDetectPropVsCentrality", kParticlenames[partID].data()), "h8dDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisPtCoarse}); } else { - histos.add(Form("%s/h6dDetectPropVsCentrality", kParticlenames[partID].data()), "h6dDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisPtCoarse}); + registry.add(Form("%s/h6dDetectPropVsCentrality", kParticlenames[partID].data()), "h6dDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisPtCoarse}); } - histos.add(Form("%s/h4dPosDetectPropVsCentrality", kParticlenames[partID].data()), "h4dPosDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse}); - histos.add(Form("%s/h4dNegDetectPropVsCentrality", kParticlenames[partID].data()), "h4dNegDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse}); - histos.add(Form("%s/h4dBachDetectPropVsCentrality", kParticlenames[partID].data()), "h4dBachDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse}); + registry.add(Form("%s/h4dPosDetectPropVsCentrality", kParticlenames[partID].data()), "h4dPosDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse}); + registry.add(Form("%s/h4dNegDetectPropVsCentrality", kParticlenames[partID].data()), "h4dNegDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse}); + registry.add(Form("%s/h4dBachDetectPropVsCentrality", kParticlenames[partID].data()), "h4dBachDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse}); } if (doDetectPropQA == kDetectPropQAWithMass) { - if (isCascade) { - histos.add(Form("%s/h9dDetectPropVsCentrality", kParticlenames[partID].data()), "h9dDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisPtCoarse, axisInvMass.at(partID)}); + // cppcheck-suppress knownConditionTrueFalse + if constexpr (partID >= kFirstCascadePartID) { + registry.add(Form("%s/h9dDetectPropVsCentrality", kParticlenames[partID].data()), "h9dDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisPtCoarse, axisInvMass.at(partID)}); } else { - histos.add(Form("%s/h7dDetectPropVsCentrality", kParticlenames[partID].data()), "h7dDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisPtCoarse, axisInvMass.at(partID)}); + registry.add(Form("%s/h7dDetectPropVsCentrality", kParticlenames[partID].data()), "h7dDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisPtCoarse, axisInvMass.at(partID)}); } - histos.add(Form("%s/h5dPosDetectPropVsCentrality", kParticlenames[partID].data()), "h5dPosDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse, axisInvMass.at(partID)}); - histos.add(Form("%s/h5dNegDetectPropVsCentrality", kParticlenames[partID].data()), "h5dNegDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse, axisInvMass.at(partID)}); - histos.add(Form("%s/h5dBachDetectPropVsCentrality", kParticlenames[partID].data()), "h5dBachDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse, axisInvMass.at(partID)}); + registry.add(Form("%s/h5dPosDetectPropVsCentrality", kParticlenames[partID].data()), "h5dPosDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse, axisInvMass.at(partID)}); + registry.add(Form("%s/h5dNegDetectPropVsCentrality", kParticlenames[partID].data()), "h5dNegDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse, axisInvMass.at(partID)}); + registry.add(Form("%s/h5dBachDetectPropVsCentrality", kParticlenames[partID].data()), "h5dBachDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse, axisInvMass.at(partID)}); } } template - void addHistograms(HistogramRegistry& histos) + void addHistograms(HistogramRegistry& registry) { - histos.add(Form("%s/h7dMass", kParticlenames[partID].data()), "h7dMass", kTHnSparseF, {axisDetectors.axisFT0ampl, axisPt, axisInvMass.at(partID), axisSelGap, axisNchInvMass, axisRap, axisEta}); - histos.add(Form("%s/h2dMass", kParticlenames[partID].data()), "h2dMass", kTH2F, {axisInvMass.at(partID), axisSelGap}); + registry.add(Form("%s/h7dMass", kParticlenames[partID].data()), "h7dMass", kTHnSparseF, {axisDetectors.axisFT0ampl, axisPt, axisInvMass.at(partID), axisSelGap, axisNchInvMass, axisRap, axisEta}); + registry.add(Form("%s/h2dMass", kParticlenames[partID].data()), "h2dMass", kTH2F, {axisInvMass.at(partID), axisSelGap}); if (doPlainTopoQA) { - addTopoHistograms(histos); + addTopoHistograms(registry); } if (PIDConfigurations.doTPCQA) { - addTPCQAHistograms(histos); + addTPCQAHistograms(registry); } if (PIDConfigurations.doTOFQA) { - addTOFQAHistograms(histos); + addTOFQAHistograms(registry); } if (doKienmaticQA) { - addKinematicQAHistograms(histos); + addKinematicQAHistograms(registry); } - addDetectorPropHistograms(histos); + addDetectorPropHistograms(registry); } template @@ -521,7 +534,8 @@ struct Derivedupcanalysis { int negITSclusMap = computeITSclusBitmap(negTrackExtra.itsClusterMap(), negIsFromAfterburner); if (partID == kK0ShortPartID) { - histos.fill(HIST("generalQA/h2dArmenterosSelected"), cand.alpha(), cand.qtarm()); + if (doPlainTopoQA) + histos.fill(HIST("generalQA/h2dArmenterosSelected"), cand.alpha(), cand.qtarm()); invMass = cand.mK0Short(); rapidity = cand.yK0Short(); ctau = cand.distovertotmom(coll.posX(), coll.posY(), coll.posZ()) * o2::constants::physics::MassK0Short; @@ -596,8 +610,8 @@ struct Derivedupcanalysis { histos.fill(HIST(kParticlenames[partID]) + HIST("/h3dNegTPCsignalVsTrackPtot"), ft0ampl, cand.negativept() * std::cosh(cand.negativeeta()), negTrackExtra.tpcSignal()); histos.fill(HIST(kParticlenames[partID]) + HIST("/h3dPosTPCsignalVsTrackPt"), ft0ampl, cand.positivept(), posTrackExtra.tpcSignal()); histos.fill(HIST(kParticlenames[partID]) + HIST("/h3dNegTPCsignalVsTrackPt"), ft0ampl, cand.negativept(), negTrackExtra.tpcSignal()); - histos.fill(HIST(kParticlenames[partID]) + HIST("/h3dPosNsigmaTPCvsTrackPt"), ft0ampl, cand.positivept(), posTrackExtra.tpcNSigmaPi()); - histos.fill(HIST(kParticlenames[partID]) + HIST("/h3dNegNsigmaTPCvsTrackPt"), ft0ampl, cand.negativept(), negTrackExtra.tpcNSigmaPi()); + histos.fill(HIST(kParticlenames[partID]) + HIST("/h3dPosNsigmaTPCvsTrackPt"), ft0ampl, cand.positivept(), tpcNsigmaPos); + histos.fill(HIST(kParticlenames[partID]) + HIST("/h3dNegNsigmaTPCvsTrackPt"), ft0ampl, cand.negativept(), tpcNsigmaNeg); histos.fill(HIST(kParticlenames[partID]) + HIST("/h3dPosNsigmaTPCvsTrackPtot"), ft0ampl, cand.positivept() * std::cosh(cand.positiveeta()), tpcNsigmaPos); histos.fill(HIST(kParticlenames[partID]) + HIST("/h3dNegNsigmaTPCvsTrackPtot"), ft0ampl, cand.negativept() * std::cosh(cand.negativeeta()), tpcNsigmaNeg); histos.fill(HIST(kParticlenames[partID]) + HIST("/h3dPosNsigmaTPC"), ft0ampl, pT, tpcNsigmaPos); @@ -657,7 +671,7 @@ struct Derivedupcanalysis { if (partID == kXiPartID) { invMass = cand.mXi(); - ctau = totalMom != 0 ? o2::constants::physics::MassXiMinus * decayPos / (totalMom * ctauxiPDG) : kDefaultInvalidCtau; + ctau = totalMom != 0 ? o2::constants::physics::MassXiMinus * decayPos / totalMom : kDefaultInvalidCtau; rapidity = cand.yXi(); if (PIDConfigurations.doTPCQA) { @@ -672,7 +686,7 @@ struct Derivedupcanalysis { } } else if (partID == kAntiXiPartID) { invMass = cand.mXi(); - ctau = totalMom != 0 ? o2::constants::physics::MassXiPlusBar * decayPos / (totalMom * ctauxiPDG) : kDefaultInvalidCtau; + ctau = totalMom != 0 ? o2::constants::physics::MassXiPlusBar * decayPos / totalMom : kDefaultInvalidCtau; rapidity = cand.yXi(); if (PIDConfigurations.doTPCQA) { @@ -688,7 +702,7 @@ struct Derivedupcanalysis { } else if (partID == kOmegaPartID) { invMass = cand.mOmega(); - ctau = totalMom != 0 ? o2::constants::physics::MassOmegaMinus * decayPos / (totalMom * ctauomegaPDG) : kDefaultInvalidCtau; + ctau = totalMom != 0 ? o2::constants::physics::MassOmegaMinus * decayPos / totalMom : kDefaultInvalidCtau; rapidity = cand.yOmega(); if (PIDConfigurations.doTPCQA) { @@ -697,14 +711,14 @@ struct Derivedupcanalysis { tpcNsigmaBach = bachTrackExtra.tpcNSigmaKa(); } if (PIDConfigurations.doTOFQA) { - tofDeltaTPos = cand.posTOFDeltaTOmPi(); - tofDeltaTNeg = cand.posTOFDeltaTOmPr(); + tofDeltaTPos = cand.posTOFDeltaTOmPr(); + tofDeltaTNeg = cand.negTOFDeltaTOmPi(); tofDeltaTBach = cand.bachTOFDeltaTOmKa(); } } else if (partID == kAntiOmegaPartID) { invMass = cand.mOmega(); - ctau = totalMom != 0 ? o2::constants::physics::MassOmegaPlusBar * decayPos / (totalMom * ctauomegaPDG) : kDefaultInvalidCtau; + ctau = totalMom != 0 ? o2::constants::physics::MassOmegaPlusBar * decayPos / totalMom : kDefaultInvalidCtau; rapidity = cand.yOmega(); if (PIDConfigurations.doTPCQA) { @@ -713,8 +727,8 @@ struct Derivedupcanalysis { tpcNsigmaBach = bachTrackExtra.tpcNSigmaKa(); } if (PIDConfigurations.doTOFQA) { - tofDeltaTPos = cand.posTOFDeltaTOmPr(); - tofDeltaTNeg = cand.posTOFDeltaTOmPi(); + tofDeltaTPos = cand.posTOFDeltaTOmPi(); + tofDeltaTNeg = cand.negTOFDeltaTOmPr(); tofDeltaTBach = cand.bachTOFDeltaTOmKa(); } } @@ -730,8 +744,8 @@ struct Derivedupcanalysis { histos.fill(HIST(kParticlenames[partID]) + HIST("/hCascCosPA"), pT, cand.casccosPA(coll.posX(), coll.posY(), coll.posZ())); histos.fill(HIST(kParticlenames[partID]) + HIST("/hDCACascDaughters"), pT, cand.dcacascdaughters()); histos.fill(HIST(kParticlenames[partID]) + HIST("/hCascRadius"), pT, cand.cascradius()); - histos.fill(HIST(kParticlenames[partID]) + HIST("/hMesonDCAToPV"), pT, cand.dcanegtopv()); - histos.fill(HIST(kParticlenames[partID]) + HIST("/hBaryonDCAToPV"), pT, cand.dcapostopv()); + histos.fill(HIST(kParticlenames[partID]) + HIST("/hMesonDCAToPV"), pT, cand.sign() > 0 ? cand.dcapostopv() : cand.dcanegtopv()); + histos.fill(HIST(kParticlenames[partID]) + HIST("/hBaryonDCAToPV"), pT, cand.sign() > 0 ? cand.dcanegtopv() : cand.dcapostopv()); histos.fill(HIST(kParticlenames[partID]) + HIST("/hBachDCAToPV"), pT, cand.dcabachtopv()); histos.fill(HIST(kParticlenames[partID]) + HIST("/hV0CosPA"), pT, cand.v0cosPA(coll.posX(), coll.posY(), coll.posZ())); histos.fill(HIST(kParticlenames[partID]) + HIST("/hV0Radius"), pT, cand.v0radius()); @@ -787,7 +801,7 @@ struct Derivedupcanalysis { void init(InitContext const&) { - if (doprocessV0s && doprocessCascades) { + if ((doprocessV0s && doprocessCascades) || (doprocessV0sMC && doprocessCascadesMC)) { LOG(fatal) << "Unable to analyze both v0s and cascades simultaneously. Please enable only one process at a time"; } @@ -842,20 +856,14 @@ struct Derivedupcanalysis { maskOmegaSpecific.set(selTPCPIDPositiveProton); maskAntiOmegaSpecific.set(selTPCPIDPositivePion); } - // TOF PID - if (PIDConfigurations.tofPidNsigmaCutK0Pi < kNoPidNsigmaCut) { // safeguard for no cut - setBits(maskK0ShortSpecific, {selTOFNSigmaPositivePionK0Short, selTOFDeltaTPositivePionK0Short}); - } - if (PIDConfigurations.tofPidNsigmaCutLaPr < kNoPidNsigmaCut) { // safeguard for no cut - setBits(maskLambdaSpecific, {selTOFNSigmaPositiveProtonLambda, selTOFDeltaTPositiveProtonLambda}); - setBits(maskXiSpecific, {selTOFNSigmaPositiveProtonLambdaXi, selTOFDeltaTPositiveProtonLambdaXi}); - setBits(maskOmegaSpecific, {selTOFNSigmaPositiveProtonLambdaOmega, selTOFDeltaTPositiveProtonLambdaOmega}); - } - if (PIDConfigurations.tofPidNsigmaCutLaPi < kNoPidNsigmaCut) { // safeguard for no cut - setBits(maskAntiLambdaSpecific, {selTOFNSigmaPositivePionLambda, selTOFDeltaTPositivePionLambda}); - setBits(maskAntiXiSpecific, {selTOFNSigmaPositivePionLambdaXi, selTOFDeltaTPositivePionLambdaXi}); - setBits(maskAntiOmegaSpecific, {selTOFNSigmaPositivePionLambdaOmega, selTOFDeltaTPositivePionLambdaOmega}); - } + // TOF PID: nsigma and time selections can be enabled independently. + setTOFSelection(maskK0ShortSpecific, PIDConfigurations.tofPidNsigmaCutK0Pi, PIDConfigurations.maxDeltaTimePion, selTOFNSigmaPositivePionK0Short, selTOFDeltaTPositivePionK0Short); + setTOFSelection(maskLambdaSpecific, PIDConfigurations.tofPidNsigmaCutLaPr, PIDConfigurations.maxDeltaTimeProton, selTOFNSigmaPositiveProtonLambda, selTOFDeltaTPositiveProtonLambda); + setTOFSelection(maskXiSpecific, PIDConfigurations.tofPidNsigmaCutLaPr, PIDConfigurations.maxDeltaTimeProton, selTOFNSigmaPositiveProtonLambdaXi, selTOFDeltaTPositiveProtonLambdaXi); + setTOFSelection(maskOmegaSpecific, PIDConfigurations.tofPidNsigmaCutLaPr, PIDConfigurations.maxDeltaTimeProton, selTOFNSigmaPositiveProtonLambdaOmega, selTOFDeltaTPositiveProtonLambdaOmega); + setTOFSelection(maskAntiLambdaSpecific, PIDConfigurations.tofPidNsigmaCutLaPi, PIDConfigurations.maxDeltaTimePion, selTOFNSigmaPositivePionLambda, selTOFDeltaTPositivePionLambda); + setTOFSelection(maskAntiXiSpecific, PIDConfigurations.tofPidNsigmaCutLaPi, PIDConfigurations.maxDeltaTimePion, selTOFNSigmaPositivePionLambdaXi, selTOFDeltaTPositivePionLambdaXi); + setTOFSelection(maskAntiOmegaSpecific, PIDConfigurations.tofPidNsigmaCutLaPi, PIDConfigurations.maxDeltaTimePion, selTOFNSigmaPositivePionLambdaOmega, selTOFDeltaTPositivePionLambdaOmega); } // negative track if (TrackConfigurations.requireNegITSonly) { @@ -873,20 +881,14 @@ struct Derivedupcanalysis { maskOmegaSpecific.set(selTPCPIDNegativePion); maskAntiOmegaSpecific.set(selTPCPIDNegativeProton); } - // TOF PID - if (PIDConfigurations.tofPidNsigmaCutK0Pi < kNoPidNsigmaCut) { // safeguard for no cut - setBits(maskK0ShortSpecific, {selTOFNSigmaNegativePionK0Short, selTOFDeltaTNegativePionK0Short}); - } - if (PIDConfigurations.tofPidNsigmaCutLaPr < kNoPidNsigmaCut) { // safeguard for no cut - setBits(maskAntiLambdaSpecific, {selTOFNSigmaNegativeProtonLambda, selTOFDeltaTNegativeProtonLambda}); - setBits(maskAntiXiSpecific, {selTOFNSigmaNegativeProtonLambdaXi, selTOFDeltaTNegativeProtonLambdaXi}); - setBits(maskAntiOmegaSpecific, {selTOFNSigmaNegativeProtonLambdaOmega, selTOFDeltaTNegativeProtonLambdaOmega}); - } - if (PIDConfigurations.tofPidNsigmaCutLaPi < kNoPidNsigmaCut) { // safeguard for no cut - setBits(maskLambdaSpecific, {selTOFNSigmaNegativePionLambda, selTOFDeltaTNegativePionLambda}); - setBits(maskXiSpecific, {selTOFNSigmaNegativePionLambdaXi, selTOFDeltaTNegativePionLambdaXi}); - setBits(maskOmegaSpecific, {selTOFNSigmaNegativePionLambdaOmega, selTOFDeltaTNegativePionLambdaOmega}); - } + // TOF PID: nsigma and time selections can be enabled independently. + setTOFSelection(maskK0ShortSpecific, PIDConfigurations.tofPidNsigmaCutK0Pi, PIDConfigurations.maxDeltaTimePion, selTOFNSigmaNegativePionK0Short, selTOFDeltaTNegativePionK0Short); + setTOFSelection(maskAntiLambdaSpecific, PIDConfigurations.tofPidNsigmaCutLaPr, PIDConfigurations.maxDeltaTimeProton, selTOFNSigmaNegativeProtonLambda, selTOFDeltaTNegativeProtonLambda); + setTOFSelection(maskAntiXiSpecific, PIDConfigurations.tofPidNsigmaCutLaPr, PIDConfigurations.maxDeltaTimeProton, selTOFNSigmaNegativeProtonLambdaXi, selTOFDeltaTNegativeProtonLambdaXi); + setTOFSelection(maskAntiOmegaSpecific, PIDConfigurations.tofPidNsigmaCutLaPr, PIDConfigurations.maxDeltaTimeProton, selTOFNSigmaNegativeProtonLambdaOmega, selTOFDeltaTNegativeProtonLambdaOmega); + setTOFSelection(maskLambdaSpecific, PIDConfigurations.tofPidNsigmaCutLaPi, PIDConfigurations.maxDeltaTimePion, selTOFNSigmaNegativePionLambda, selTOFDeltaTNegativePionLambda); + setTOFSelection(maskXiSpecific, PIDConfigurations.tofPidNsigmaCutLaPi, PIDConfigurations.maxDeltaTimePion, selTOFNSigmaNegativePionLambdaXi, selTOFDeltaTNegativePionLambdaXi); + setTOFSelection(maskOmegaSpecific, PIDConfigurations.tofPidNsigmaCutLaPi, PIDConfigurations.maxDeltaTimePion, selTOFNSigmaNegativePionLambdaOmega, selTOFDeltaTNegativePionLambdaOmega); } // bachelor track maskTrackPropertiesCasc = maskTrackPropertiesV0; @@ -901,15 +903,11 @@ struct Derivedupcanalysis { maskOmegaSpecific.set(selTPCPIDBachKaon); maskAntiOmegaSpecific.set(selTPCPIDBachKaon); } - // TOF PID - if (PIDConfigurations.tofPidNsigmaCutXiPi < kNoPidNsigmaCut) { // safeguard for no cut - setBits(maskXiSpecific, {selTOFNSigmaBachPionXi, selTOFDeltaTBachPionXi}); - setBits(maskAntiXiSpecific, {selTOFNSigmaBachPionXi, selTOFDeltaTBachPionXi}); - } - if (PIDConfigurations.tofPidNsigmaCutOmegaKaon < kNoPidNsigmaCut) { // safeguard for no cut - setBits(maskOmegaSpecific, {selTOFNSigmaBachKaonOmega, selTOFDeltaTBachKaonOmega}); - setBits(maskAntiOmegaSpecific, {selTOFNSigmaBachKaonOmega, selTOFDeltaTBachKaonOmega}); - } + // TOF PID: nsigma and time selections can be enabled independently. + setTOFSelection(maskXiSpecific, PIDConfigurations.tofPidNsigmaCutXiPi, PIDConfigurations.maxDeltaTimePion, selTOFNSigmaBachPionXi, selTOFDeltaTBachPionXi); + setTOFSelection(maskAntiXiSpecific, PIDConfigurations.tofPidNsigmaCutXiPi, PIDConfigurations.maxDeltaTimePion, selTOFNSigmaBachPionXi, selTOFDeltaTBachPionXi); + setTOFSelection(maskOmegaSpecific, PIDConfigurations.tofPidNsigmaCutOmegaKaon, PIDConfigurations.maxDeltaTimeKaon, selTOFNSigmaBachKaonOmega, selTOFDeltaTBachKaonOmega); + setTOFSelection(maskAntiOmegaSpecific, PIDConfigurations.tofPidNsigmaCutOmegaKaon, PIDConfigurations.maxDeltaTimeKaon, selTOFNSigmaBachKaonOmega, selTOFDeltaTBachKaonOmega); } if (TrackConfigurations.skipTPConly) { @@ -1007,6 +1005,14 @@ struct Derivedupcanalysis { histos.add("eventQA/mc/hSelGeneratorsId", "hSelGeneratorsId", kTH1D, {axisGeneratorIds}); } + if (doprocessV0sMC || doprocessCascadesMC || doprocessGenerated) { + histos.add("generalQA/mc/hInvalidMCReferences", "Out-of-range MC references; Reference type; Entries", kTH1D, {{7, -0.5, 6.5}}); + const std::array referenceLabels{"Vertex collision", "V0 core", "V0 collision", "Cascade core", "Cascade collision", "Generated V0 collision", "Generated cascade collision"}; + for (std::size_t i = 0; i < referenceLabels.size(); ++i) { + histos.get(HIST("generalQA/mc/hInvalidMCReferences"))->GetXaxis()->SetBinLabel(i + 1, referenceLabels[i]); + } + } + if (doprocessV0sMC || doprocessCascadesMC) { // Event QA histos.add("eventQA/mc/hFakeEvents", "hFakeEvents", {kTH1D, {{1, -0.5f, 0.5f}}}); @@ -1018,16 +1024,29 @@ struct Derivedupcanalysis { } if (doprocessV0sMC) { - if (analyseLambda && calculateFeeddownMatrix) + histos.add("generalQA/mc/h3dV0CollisionAssociation", "V0 truth candidates before candidate selection; Candidate generator; Vertex generator; Same MC collision", kTH3D, {axisGeneratorIds, axisGeneratorIds, {2, -0.5f, 1.5f}}); + if (analyseLambda && calculateFeeddownMatrix) { histos.add(Form("%s/h3dLambdaFeeddown", kParticlenames[kLambdaPartID].data()), "h3dLambdaFeeddown", kTH3F, {axisNTracksGlobal, axisPt, axisPt}); - if (analyseAntiLambda && calculateFeeddownMatrix) + histos.add(Form("%s/h5dLambdaFeeddown", kParticlenames[kLambdaPartID].data()), "h5dLambdaFeeddown", kTHnSparseF, {axisNTracksGlobal, axisPt, axisPt, axisSelGap, axisInvMass.at(kLambdaPartID)}); + } + if (analyseAntiLambda && calculateFeeddownMatrix) { histos.add(Form("%s/h3dAntiLambdaFeeddown", kParticlenames[kAntiLambdaPartID].data()), "h3dAntiLambdaFeeddown", kTH3F, {axisNTracksGlobal, axisPt, axisPt}); + histos.add(Form("%s/h5dAntiLambdaFeeddown", kParticlenames[kAntiLambdaPartID].data()), "h5dAntiLambdaFeeddown", kTHnSparseF, {axisNTracksGlobal, axisPt, axisPt, axisSelGap, axisInvMass.at(kAntiLambdaPartID)}); + } + } + + if (doprocessCascadesMC) { + histos.add("generalQA/mc/h3dCascadeCollisionAssociation", "Cascade truth candidates before candidate selection; Candidate generator; Vertex generator; Same MC collision", kTH3D, {axisGeneratorIds, axisGeneratorIds, {2, -0.5f, 1.5f}}); } if (doprocessGenerated) { for (int partID = kK0ShortPartID; partID <= kAntiOmegaPartID; partID++) { histos.add(Form("%s/mc/h7dGen", kParticlenames[partID].data()), "h7dGen", kTHnSparseF, {axisDetectors.axisFT0ampl, axisNchInvMass, axisNchInvMass, axisPt, axisSelGap, axisRap, axisGeneratorIds}); } + if (analyseLambda && calculateFeeddownMatrix) + histos.add(Form("%s/mc/h4dGenXiFeeddown", kParticlenames[kLambdaPartID].data()), "h4dGenXiFeeddown", kTHnSparseF, {axisNTracksGlobal, axisPt, axisRap, axisGeneratorIds}); + if (analyseAntiLambda && calculateFeeddownMatrix) + histos.add(Form("%s/mc/h4dGenAntiXiFeeddown", kParticlenames[kAntiLambdaPartID].data()), "h4dGenAntiXiFeeddown", kTHnSparseF, {axisNTracksGlobal, axisPt, axisRap, axisGeneratorIds}); } if (doprocessV0s || doprocessV0sMC) { @@ -1063,6 +1082,9 @@ struct Derivedupcanalysis { } if (doprocessCascades || doprocessCascadesMC) { + histos.add("eventQA/hCascadeChargeSelection", "Selected Xi charge; Charge; Entries", kTH1D, {{2, -0.5f, 1.5f}}); + histos.get(HIST("eventQA/hCascadeChargeSelection"))->GetXaxis()->SetBinLabel(1, "Xi-"); + histos.get(HIST("eventQA/hCascadeChargeSelection"))->GetXaxis()->SetBinLabel(2, "AntiXi+"); // For all candidates if (doPlainTopoQA) { histos.add("generalQA/hPt", "hPt", kTH1F, {axisPtCoarse}); @@ -1220,8 +1242,7 @@ struct Derivedupcanalysis { template int getGapSideWithoutRecoZDC(TCollision const& collision) { - int selGapSide = o2::aod::sgselector::NoGap; - selGapSide = sgSelector.trueGap(collision, upcCuts.fv0a, upcCuts.ft0a, upcCuts.ft0c, std::numeric_limits::infinity()); + int selGapSide = sgSelector.trueGap(collision, upcCuts.fv0a, upcCuts.ft0a, upcCuts.ft0c, std::numeric_limits::infinity()); return applyFITTiming(selGapSide, collision); } @@ -1282,8 +1303,7 @@ struct Derivedupcanalysis { template int getGapSide(TCollision const& collision) { - int selGapSide = o2::aod::sgselector::NoGap; - selGapSide = sgSelector.trueGap(collision, upcCuts.fv0a, upcCuts.ft0a, upcCuts.ft0c, upcCuts.zdc); + int selGapSide = sgSelector.trueGap(collision, upcCuts.fv0a, upcCuts.ft0a, upcCuts.ft0c, upcCuts.zdc); selGapSide = applyZDCTiming(selGapSide, collision); return applyFITTiming(selGapSide, collision); } @@ -1422,10 +1442,11 @@ struct Derivedupcanalysis { } // Additional check for UPC collision flag - if (evSels.useUPCflag && collision.flags() < 1) { + const bool hasUPCFlag = (collision.flags() & o2::dataformats::Vertex>::Flags::UPCMode) != 0; + if (evSels.useUPCflag && !hasUPCFlag) { return false; } - if (collision.flags() >= 1 && fillQA) { + if (hasUPCFlag && fillQA) { histos.fill(HIST("eventQA/hEventSelection"), 16.0); // UPC event } @@ -1577,9 +1598,9 @@ struct Derivedupcanalysis { if (std::fabs(rapidityOmega) < rapidityCut) bitMap.set(selOmegaRapidity); if (std::fabs(poseta) < daughterEtaCut) - bitMap.set(selNegEta); - if (std::fabs(negeta) < daughterEtaCut) bitMap.set(selPosEta); + if (std::fabs(negeta) < daughterEtaCut) + bitMap.set(selNegEta); if (std::fabs(bacheta) < daughterEtaCut) bitMap.set(selBachEta); @@ -1673,8 +1694,8 @@ struct Derivedupcanalysis { bitMap.set(selTOFNSigmaPositiveProtonLambdaXi); } if (std::fabs(casc.tofNSigmaOmLaPr()) < PIDConfigurations.tofPidNsigmaCutLaPr) { - bitMap.set(selTOFNSigmaNegativePionLambdaOmega); - bitMap.set(selTOFNSigmaPositivePionLambdaOmega); + bitMap.set(selTOFNSigmaNegativeProtonLambdaOmega); + bitMap.set(selTOFNSigmaPositiveProtonLambdaOmega); } // bachelor track if (std::fabs(casc.tofNSigmaXiPi()) < PIDConfigurations.tofPidNsigmaCutXiPi) { @@ -1853,8 +1874,8 @@ struct Derivedupcanalysis { histos.fill(HIST("generalQA/hCascCosPA"), casc.pt(), casc.casccosPA(coll.posX(), coll.posY(), coll.posZ())); histos.fill(HIST("generalQA/hDCACascDaughters"), casc.pt(), casc.dcacascdaughters()); histos.fill(HIST("generalQA/hCascRadius"), casc.pt(), casc.cascradius()); - histos.fill(HIST("generalQA/hMesonDCAToPV"), casc.pt(), casc.dcanegtopv()); - histos.fill(HIST("generalQA/hBaryonDCAToPV"), casc.pt(), casc.dcapostopv()); + histos.fill(HIST("generalQA/hMesonDCAToPV"), casc.pt(), casc.sign() > 0 ? casc.dcapostopv() : casc.dcanegtopv()); + histos.fill(HIST("generalQA/hBaryonDCAToPV"), casc.pt(), casc.sign() > 0 ? casc.dcanegtopv() : casc.dcapostopv()); histos.fill(HIST("generalQA/hBachDCAToPV"), casc.pt(), casc.dcabachtopv()); histos.fill(HIST("generalQA/hV0CosPA"), casc.pt(), casc.v0cosPA(coll.posX(), coll.posY(), coll.posZ())); histos.fill(HIST("generalQA/hV0Radius"), casc.pt(), casc.v0radius()); @@ -1867,22 +1888,24 @@ struct Derivedupcanalysis { } // Xi - if (verifyMask(selMap, maskSelectionXi) && analyseXi) { + if (casc.sign() < 0 && verifyMask(selMap, maskSelectionXi) && analyseXi) { + histos.fill(HIST("eventQA/hCascadeChargeSelection"), 0.); fillHistogramsCasc<3>(casc, coll, gap); } // Anti-Xi - if (verifyMask(selMap, maskSelectionAntiXi) && analyseAntiXi) { + if (casc.sign() > 0 && verifyMask(selMap, maskSelectionAntiXi) && analyseAntiXi) { + histos.fill(HIST("eventQA/hCascadeChargeSelection"), 1.); fillHistogramsCasc<4>(casc, coll, gap); } // Omega - if (verifyMask(selMap, maskSelectionOmega) && analyseOmega) { + if (casc.sign() < 0 && verifyMask(selMap, maskSelectionOmega) && analyseOmega) { fillHistogramsCasc<5>(casc, coll, gap); } // Anti-Omega - if (verifyMask(selMap, maskSelectionAntiOmega) && analyseAntiOmega) { + if (casc.sign() > 0 && verifyMask(selMap, maskSelectionAntiOmega) && analyseAntiOmega) { fillHistogramsCasc<6>(casc, coll, gap); } } @@ -1936,13 +1959,13 @@ struct Derivedupcanalysis { const bool isPositiveProton = (pdgPos == PDG_t::kProton); - const bool isPositivePion = (pdgPos == PDG_t::kPiPlus); - const bool isBachelorPositivePion = (pdgBach == PDG_t::kPiPlus); + const bool isPositivePion = (pdgPos == PDG_t::kPiPlus) || (doTreatPiToMuon && pdgPos == PDG_t::kMuonPlus); + const bool isBachelorPositivePion = (pdgBach == PDG_t::kPiPlus) || (doTreatPiToMuon && pdgBach == PDG_t::kMuonPlus); const bool isNegativeProton = (pdgNeg == kProtonBar); - const bool isNegativePion = (pdgNeg == PDG_t::kPiMinus); - const bool isBachelorNegativePion = (pdgBach == PDG_t::kPiMinus); + const bool isNegativePion = (pdgNeg == PDG_t::kPiMinus) || (doTreatPiToMuon && pdgNeg == PDG_t::kMuonMinus); + const bool isBachelorNegativePion = (pdgBach == PDG_t::kPiMinus) || (doTreatPiToMuon && pdgBach == PDG_t::kMuonMinus); const bool isBachelorPositiveKaon = (pdgBach == PDG_t::kKPlus); const bool isBachelorNegativeKaon = (pdgBach == PDG_t::kKMinus); @@ -1996,10 +2019,9 @@ struct Derivedupcanalysis { histos.fill(HIST("generalQA/hV0Radius"), v0.v0radius()); histos.fill(HIST("generalQA/h2dPositiveITSvsTPCpts"), posTrackExtra.tpcCrossedRows(), posTrackExtra.itsNCls()); histos.fill(HIST("generalQA/h2dNegativeITSvsTPCpts"), negTrackExtra.tpcCrossedRows(), negTrackExtra.itsNCls()); + histos.fill(HIST("generalQA/h2dArmenterosAll"), v0.alpha(), v0.qtarm()); } - histos.fill(HIST("generalQA/h2dArmenterosAll"), v0.alpha(), v0.qtarm()); - // K0s if (verifyMask(selMap, maskSelectionK0Short) && analyseK0Short) { fillHistogramsV0<0>(v0, coll, gap); @@ -2026,6 +2048,9 @@ struct Derivedupcanalysis { std::vector listBestCollisionIds(mcCollisions.size(), -1); for (auto const& mcCollision : mcCollisions) { + if (std::abs(mcCollision.posZ()) > maxZVtxPosition) + continue; + if (std::find(generatorIds->begin(), generatorIds->end(), mcCollision.generatorsID()) == generatorIds->end()) { continue; } @@ -2033,6 +2058,9 @@ struct Derivedupcanalysis { // Group collisions and neutrons by MC collision index auto groupedCollisions = collisions.sliceBy(perMcCollision, mcCollision.globalIndex()); auto groupedNeutrons = neutrons.sliceBy(neutronsPerMcCollision, mcCollision.globalIndex()); + if (!acceptGeneratedNeutronSelection(groupedNeutrons)) + continue; + // Find the collision with the biggest nbr of PV contributors // Follows what was done here: https://github.com/AliceO2Group/O2Physics/blob/master/Common/TableProducer/mcCollsExtra.cxx#L93 int biggestNContribs = -1; @@ -2070,6 +2098,10 @@ struct Derivedupcanalysis { continue; } + auto groupedNeutrons = neutrons.sliceBy(neutronsPerMcCollision, mcCollision.globalIndex()); + if (!acceptGeneratedNeutronSelection(groupedNeutrons)) + continue; + histos.fill(HIST("eventQA/mc/hSelGeneratorsId"), mcCollision.generatorsID()); histos.fill(HIST("eventQA/mc/hEventSelectionMC"), 0.0, mcCollision.multMCNParticlesEta08(), mcCollision.generatorsID()); @@ -2081,7 +2113,6 @@ struct Derivedupcanalysis { // Group collisions and neutrons by MC collision index auto groupedCollisions = collisions.sliceBy(perMcCollision, mcCollision.globalIndex()); - auto groupedNeutrons = neutrons.sliceBy(neutronsPerMcCollision, mcCollision.globalIndex()); bool atLeastOne = false; float centrality = -1.f; @@ -2133,7 +2164,7 @@ struct Derivedupcanalysis { } template - void fillFeeddownMatrix(TCollision const& collision, TV0 const& v0, std::bitset const& selMap) + void fillFeeddownMatrix(TCollision const& collision, TV0 const& v0, std::bitset const& selMap, int gap) { if (!v0.has_motherMCPart()) { return; @@ -2156,11 +2187,13 @@ struct Derivedupcanalysis { if (analyseLambda && verifyMask(selMap, secondaryMaskSelectionLambda) && (v0mother.pdgCode() == PDG_t::kXiMinus) && v0mother.isPhysicalPrimary()) { histos.fill(HIST(kParticlenames[1]) + HIST("/h3dLambdaFeeddown"), mult, v0pt, motherPt); + histos.fill(HIST(kParticlenames[1]) + HIST("/h5dLambdaFeeddown"), mult, v0pt, motherPt, gap, v0.mLambda()); } if (analyseAntiLambda && verifyMask(selMap, secondaryMaskSelectionAntiLambda) && (v0mother.pdgCode() == PDG_t::kXiPlusBar) && v0mother.isPhysicalPrimary()) { histos.fill(HIST(kParticlenames[2]) + HIST("/h3dAntiLambdaFeeddown"), mult, v0pt, motherPt); + histos.fill(HIST(kParticlenames[2]) + HIST("/h5dAntiLambdaFeeddown"), mult, v0pt, motherPt, gap, v0.mAntiLambda()); } } @@ -2205,12 +2238,25 @@ struct Derivedupcanalysis { } } + bool hasValidMCReference(int64_t index, int64_t tableSize, int referenceType) + { + if (index < 0) { + return false; + } + // has_*() only checks the sign; older derived files can contain stale positive indices. + if (index >= tableSize) { + histos.fill(HIST("generalQA/mc/hInvalidMCReferences"), referenceType); + return false; + } + return true; + } + void processV0sMC(StraCollisonsFullMC const& collisions, V0CandidatesMC const& fullV0s, DauTracks const&, aod::MotherMCParts const&, - StraMCCollisionsFull const&, - V0MCCoresFull const&, + StraMCCollisionsFull const& mcCollisions, + V0MCCoresFull const& v0MCCores, NeutronsMC const& neutrons) { v0sGrouped.clear(); @@ -2220,40 +2266,51 @@ struct Derivedupcanalysis { } for (const auto& collision : collisions) { - if (!collision.has_straMCCollision()) { + if (!hasValidMCReference(collision.straMCCollisionId(), mcCollisions.size(), 0)) { histos.fill(HIST("eventQA/mc/hFakeEvents"), 0); // no assoc. MC collisions continue; } const auto& mcCollision = collision.straMCCollision_as(); // take gen. collision associated to the rec. collision - if (std::find(generatorIds->begin(), generatorIds->end(), mcCollision.generatorsID()) == generatorIds->end()) { + // Vertex labels classify event QA, not the origin of particles at this vertex. + const bool selectedVertexGenerator = std::find(generatorIds->begin(), generatorIds->end(), mcCollision.generatorsID()) != generatorIds->end(); + if (!doMCAssociation && !selectedVertexGenerator) { continue; } - if (!acceptEvent(collision, true)) { + if (!acceptEvent(collision, selectedVertexGenerator)) { continue; } // event is accepted - if (collision.isUPC()) { - fillPreSelTimingHistograms(collision); + if (selectedVertexGenerator) { + if (collision.isUPC()) { + fillPreSelTimingHistograms(collision); + } + histos.fill(HIST("eventQA/hRawGapSide"), collision.gapSide()); } - histos.fill(HIST("eventQA/hRawGapSide"), collision.gapSide()); auto groupedNeutrons = neutrons.sliceBy(neutronsPerMcCollision, mcCollision.globalIndex()); int selGapSideNoNeutrons = collision.isUPC() ? getGapSideWithoutRecoZDC(collision) : o2::aod::sgselector::NoGap; + + if (evSels.studyUPConly && (selGapSideNoNeutrons < 0)) + continue; + + if (selectedVertexGenerator) + histos.fill(HIST("eventQA/hSelGapSideNoNeutrons"), selGapSideNoNeutrons); + int selGapSide = applyGeneratedNeutronSelection(selGapSideNoNeutrons, groupedNeutrons); if (evSels.studyUPConly && (selGapSide < 0)) continue; - histos.fill(HIST("eventQA/hSelGapSideNoNeutrons"), selGapSideNoNeutrons); - fillHistogramsQA(collision, selGapSide); - - histos.fill(HIST("eventQA/mc/hNTracksGlobalvsMCNParticlesEta08rec"), collision.multNTracksGlobal(), mcCollision.multMCNParticlesEta08()); - histos.fill(HIST("eventQA/mc/hNTracksPVeta1vsMCNParticlesEta10rec"), collision.multNTracksPVeta1(), mcCollision.multMCNParticlesEta10()); - histos.fill(HIST("eventQA/mc/hNTracksGlobalvstotalMultMCParticles"), collision.multNTracksGlobal(), mcCollision.totalMultMCParticles()); - histos.fill(HIST("eventQA/mc/hNTracksPVeta1vstotalMultMCParticles"), collision.multNTracksPVeta1(), mcCollision.totalMultMCParticles()); + if (selectedVertexGenerator) { + fillHistogramsQA(collision, selGapSide); + histos.fill(HIST("eventQA/mc/hNTracksGlobalvsMCNParticlesEta08rec"), collision.multNTracksGlobal(), mcCollision.multMCNParticlesEta08()); + histos.fill(HIST("eventQA/mc/hNTracksPVeta1vsMCNParticlesEta10rec"), collision.multNTracksPVeta1(), mcCollision.multMCNParticlesEta10()); + histos.fill(HIST("eventQA/mc/hNTracksGlobalvstotalMultMCParticles"), collision.multNTracksGlobal(), mcCollision.totalMultMCParticles()); + histos.fill(HIST("eventQA/mc/hNTracksPVeta1vstotalMultMCParticles"), collision.multNTracksPVeta1(), mcCollision.totalMultMCParticles()); + } std::size_t nV0sThisColl = v0sGrouped[collision.globalIndex()].size(); @@ -2265,11 +2322,17 @@ struct Derivedupcanalysis { std::bitset selMap = computeBitmapV0(v0, collision); if (doMCAssociation) { - if (v0.has_v0MCCore()) { + if (hasValidMCReference(v0.v0MCCoreId(), v0MCCores.size(), 1)) { const auto& v0MC = v0.v0MCCore_as(); - computeV0MCAssociation(v0MC, selMap); - if (calculateFeeddownMatrix) { - fillFeeddownMatrix(collision, v0, selMap); + if (hasValidMCReference(v0MC.straMCCollisionId(), mcCollisions.size(), 2) && v0MC.pdgCode() != -1) { + const auto& candidateCollision = v0MC.straMCCollision_as(); + if (std::find(generatorIds->begin(), generatorIds->end(), candidateCollision.generatorsID()) != generatorIds->end()) { + histos.fill(HIST("generalQA/mc/h3dV0CollisionAssociation"), candidateCollision.generatorsID(), mcCollision.generatorsID(), v0MC.straMCCollisionId() == mcCollision.globalIndex()); + computeV0MCAssociation(v0MC, selMap); + if (calculateFeeddownMatrix) { + fillFeeddownMatrix(collision, v0, selMap, selGapSide); + } + } } } } else { @@ -2327,8 +2390,8 @@ struct Derivedupcanalysis { CascadeCandidatesMC const& fullCascades, DauTracks const&, aod::MotherMCParts const&, - StraMCCollisionsFull const&, - CascMCCoresFull const&, + StraMCCollisionsFull const& mcCollisions, + CascMCCoresFull const& cascMCCores, NeutronsMC const& neutrons) { cascadesGrouped.clear(); @@ -2338,40 +2401,51 @@ struct Derivedupcanalysis { } for (const auto& collision : collisions) { - if (!collision.has_straMCCollision()) { + if (!hasValidMCReference(collision.straMCCollisionId(), mcCollisions.size(), 0)) { histos.fill(HIST("eventQA/mc/hFakeEvents"), 0); // no assoc. MC collisions continue; } const auto& mcCollision = collision.straMCCollision_as(); // take gen. collision associated to the rec. collision - if (std::find(generatorIds->begin(), generatorIds->end(), mcCollision.generatorsID()) == generatorIds->end()) { + // Vertex labels classify event QA, not the origin of particles at this vertex. + const bool selectedVertexGenerator = std::find(generatorIds->begin(), generatorIds->end(), mcCollision.generatorsID()) != generatorIds->end(); + if (!doMCAssociation && !selectedVertexGenerator) { continue; } - if (!acceptEvent(collision, true)) { + if (!acceptEvent(collision, selectedVertexGenerator)) { continue; } // event is accepted - if (collision.isUPC()) { - fillPreSelTimingHistograms(collision); + if (selectedVertexGenerator) { + if (collision.isUPC()) { + fillPreSelTimingHistograms(collision); + } + histos.fill(HIST("eventQA/hRawGapSide"), collision.gapSide()); } - histos.fill(HIST("eventQA/hRawGapSide"), collision.gapSide()); auto groupedNeutrons = neutrons.sliceBy(neutronsPerMcCollision, mcCollision.globalIndex()); int selGapSideNoNeutrons = collision.isUPC() ? getGapSideWithoutRecoZDC(collision) : o2::aod::sgselector::NoGap; + + if (evSels.studyUPConly && (selGapSideNoNeutrons < 0)) + continue; + + if (selectedVertexGenerator) + histos.fill(HIST("eventQA/hSelGapSideNoNeutrons"), selGapSideNoNeutrons); + int selGapSide = applyGeneratedNeutronSelection(selGapSideNoNeutrons, groupedNeutrons); if (evSels.studyUPConly && (selGapSide < 0)) continue; - histos.fill(HIST("eventQA/hSelGapSideNoNeutrons"), selGapSideNoNeutrons); - fillHistogramsQA(collision, selGapSide); - - histos.fill(HIST("eventQA/mc/hNTracksGlobalvsMCNParticlesEta08rec"), collision.multNTracksGlobal(), mcCollision.multMCNParticlesEta08()); - histos.fill(HIST("eventQA/mc/hNTracksPVeta1vsMCNParticlesEta10rec"), collision.multNTracksPVeta1(), mcCollision.multMCNParticlesEta10()); - histos.fill(HIST("eventQA/mc/hNTracksGlobalvstotalMultMCParticles"), collision.multNTracksGlobal(), mcCollision.totalMultMCParticles()); - histos.fill(HIST("eventQA/mc/hNTracksPVeta1vstotalMultMCParticles"), collision.multNTracksPVeta1(), mcCollision.totalMultMCParticles()); + if (selectedVertexGenerator) { + fillHistogramsQA(collision, selGapSide); + histos.fill(HIST("eventQA/mc/hNTracksGlobalvsMCNParticlesEta08rec"), collision.multNTracksGlobal(), mcCollision.multMCNParticlesEta08()); + histos.fill(HIST("eventQA/mc/hNTracksPVeta1vsMCNParticlesEta10rec"), collision.multNTracksPVeta1(), mcCollision.multMCNParticlesEta10()); + histos.fill(HIST("eventQA/mc/hNTracksGlobalvstotalMultMCParticles"), collision.multNTracksGlobal(), mcCollision.totalMultMCParticles()); + histos.fill(HIST("eventQA/mc/hNTracksPVeta1vstotalMultMCParticles"), collision.multNTracksPVeta1(), mcCollision.totalMultMCParticles()); + } std::size_t nCascadesThisColl = cascadesGrouped[collision.globalIndex()].size(); @@ -2380,9 +2454,15 @@ struct Derivedupcanalysis { std::bitset selMap = computeBitmapCascade(casc, collision); if (doMCAssociation) { - if (casc.has_cascMCCore()) { + if (hasValidMCReference(casc.cascMCCoreId(), cascMCCores.size(), 3)) { const auto& cascMC = casc.cascMCCore_as(); - computeCascadeMCAssociation(cascMC, selMap); + if (hasValidMCReference(cascMC.straMCCollisionId(), mcCollisions.size(), 4) && cascMC.pdgCode() != -1) { + const auto& candidateCollision = cascMC.straMCCollision_as(); + if (std::find(generatorIds->begin(), generatorIds->end(), candidateCollision.generatorsID()) != generatorIds->end()) { + histos.fill(HIST("generalQA/mc/h3dCascadeCollisionAssociation"), candidateCollision.generatorsID(), mcCollision.generatorsID(), cascMC.straMCCollisionId() == mcCollision.globalIndex()); + computeCascadeMCAssociation(cascMC, selMap); + } + } } } else { // the candidate may belong to any particle species @@ -2406,7 +2486,7 @@ struct Derivedupcanalysis { // V0 start for (auto const& v0MC : V0MCCores) { // Consider only primaries - if (!v0MC.has_straMCCollision() || !v0MC.isPhysicalPrimary()) + if (!v0MC.isPhysicalPrimary() || !hasValidMCReference(v0MC.straMCCollisionId(), mcCollisions.size(), 5)) continue; // Kinematics (|y| < rapidityCut) @@ -2473,7 +2553,7 @@ struct Derivedupcanalysis { // Cascade start for (auto const& cascMC : CascMCCores) { // Consider only primaries - if (!cascMC.has_straMCCollision() || !cascMC.isPhysicalPrimary()) + if (!cascMC.isPhysicalPrimary() || !hasValidMCReference(cascMC.straMCCollisionId(), mcCollisions.size(), 6)) continue; // Kinematics (|y| < rapidityCut) float pTmc = cascMC.ptMC(); @@ -2483,8 +2563,6 @@ struct Derivedupcanalysis { } else if ((cascMC.pdgCode() == PDG_t::kOmegaMinus) || (cascMC.pdgCode() == PDG_t::kOmegaPlusBar)) { ymc = cascMC.rapidityMC(2); } - if (std::abs(ymc) > rapidityCut) - continue; const auto& mcCollision = cascMC.straMCCollision_as(); // take gen. collision if (std::abs(mcCollision.posZ()) > maxZVtxPosition) @@ -2511,8 +2589,23 @@ struct Derivedupcanalysis { } else if (static_cast(upcCuts.genGapSide) == 1) { ft0ampl = collision.totalFT0AmplitudeA(); } + nTracksGlobal = collision.multNTracksGlobal(); + } + + // All generated Xi parents, before daughter topology cuts. Requires asymmetric + // CascMCCores populated with generated Xi of both charges in this rapidity range. + if (calculateFeeddownMatrix && nTracksGlobal >= 0 && std::abs(ymc) <= kMotherRapidityMax) { + if (analyseLambda && cascMC.pdgCode() == PDG_t::kXiMinus) { + histos.fill(HIST(kParticlenames[kLambdaPartID]) + HIST("/mc/h4dGenXiFeeddown"), nTracksGlobal, pTmc, ymc, mcCollision.generatorsID()); + } + if (analyseAntiLambda && cascMC.pdgCode() == PDG_t::kXiPlusBar) { + histos.fill(HIST(kParticlenames[kAntiLambdaPartID]) + HIST("/mc/h4dGenAntiXiFeeddown"), nTracksGlobal, pTmc, ymc, mcCollision.generatorsID()); + } } + if (std::abs(ymc) > rapidityCut) + continue; + const int pdgPos = cascMC.pdgCodePositive(); const int pdgNeg = cascMC.pdgCodeNegative(); const int pdgBach = cascMC.pdgCodeBachelor(); @@ -2520,13 +2613,13 @@ struct Derivedupcanalysis { const bool isPositiveProton = (pdgPos == PDG_t::kProton); - const bool isPositivePion = (pdgPos == PDG_t::kPiPlus); - const bool isBachelorPositivePion = (pdgBach == PDG_t::kPiPlus); + const bool isPositivePion = (pdgPos == PDG_t::kPiPlus) || (doTreatPiToMuon && pdgPos == PDG_t::kMuonPlus); + const bool isBachelorPositivePion = (pdgBach == PDG_t::kPiPlus) || (doTreatPiToMuon && pdgBach == PDG_t::kMuonPlus); const bool isNegativeProton = (pdgNeg == kProtonBar); - const bool isNegativePion = (pdgNeg == PDG_t::kPiMinus); - const bool isBachelorNegativePion = (pdgBach == PDG_t::kPiMinus); + const bool isNegativePion = (pdgNeg == PDG_t::kPiMinus) || (doTreatPiToMuon && pdgNeg == PDG_t::kMuonMinus); + const bool isBachelorNegativePion = (pdgBach == PDG_t::kPiMinus) || (doTreatPiToMuon && pdgBach == PDG_t::kMuonMinus); const bool isBachelorPositiveKaon = (pdgBach == PDG_t::kKPlus); const bool isBachelorNegativeKaon = (pdgBach == PDG_t::kKMinus); diff --git a/PWGLF/Tasks/Strangeness/forwardlambdakzeroanalysis.cxx b/PWGLF/Tasks/Strangeness/forwardlambdakzeroanalysis.cxx index c73a5926a7a..f524b7dfa8e 100644 --- a/PWGLF/Tasks/Strangeness/forwardlambdakzeroanalysis.cxx +++ b/PWGLF/Tasks/Strangeness/forwardlambdakzeroanalysis.cxx @@ -359,7 +359,7 @@ struct forwardlambdakzeroanalysis { // Build the mixing binning locally: a struct member initialized from a // ConfigurableAxis captures the default bins at task construction time - ColumnBinningPolicy bkgColBinning{{eventMixingConfigurations.axisVertexMixing, eventMixingConfigurations.axisCentralityMixing}, true}; + ColumnBinningPolicy bkgColBinning{{eventMixingConfigurations.axisVertexMixing, eventMixingConfigurations.axisCentralityMixing}}; // Taken from https://github.com/AliceO2Group/O2Physics/blob/master/PWGLF/TableProducer/Strangeness/sigma0builder.cxx#L319 // Thanks Gianni! diff --git a/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx b/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx index 587d5ba73d5..9d1960bd563 100644 --- a/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx +++ b/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx @@ -76,6 +76,7 @@ #include #include #include +#include #include #include #include @@ -97,9 +98,9 @@ using V0DatasWithoutTrackXWithMCCores = soa::Join ccdb; + Service ccdb{}; - Service pdgDB; + Service pdgDB{}; o2::pwglf::ParticleCounter mCounter; HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -127,7 +128,8 @@ struct HStrangeCorrelation { Configurable doGenEventSelection{"doGenEventSelection", true, "use event selections when performing closure test for the gen events"}; Configurable doClosureTestPureMC{"doClosureTestPureMC", false, "fill regular ClosureTest histograms without event or reconstructed-trigger selection, using MC vertex z and centrality 0.05; keep truth-particle selections"}; Configurable selectINELgtZERO{"selectINELgtZERO", true, "select INEL>0 events"}; - Configurable selectINELgtONE{"selectINELgtONE", false, "select INEL>1 events (at least 2 charged particles in |eta| < 1)"}; + Configurable selectINELgtN{"selectINELgtN", false, "select INEL>N events (more than N charged particles in |eta| < 1), N = inelGtNThreshold"}; + Configurable inelGtNThreshold{"inelGtNThreshold", 1, "N of the INEL>N selection (reco: PV contributors, gen: charged physical primaries, both in |eta| < 1)"}; Configurable zVertexCut{"zVertexCut", 10, "Cut on PV position"}; Configurable requireAllGoodITSLayers{"requireAllGoodITSLayers", false, " require that in the event all ITS are good"}; Configurable rejectSameBunchPileup{"rejectSameBunchPileup", false, "reject collisions associated with the same found-by-T0 bunch crossing"}; @@ -146,12 +148,15 @@ struct HStrangeCorrelation { Configurable doCorrelationsHadronV0daughter{"doCorrelationsHadronV0daughter", false, "if true, do correlations of hadrons with V0 daughters"}; Configurable doLocalDensityStudy{"doLocalDensityStudy", false, "if true, create and fill the pt vs eta vs local density spectra of triggers and V0s"}; Configurable localDensityConeRadius{"localDensityConeRadius", 0.4, "radius of the cone in which the local density is counted"}; + Configurable doMultEstimatorResponse{"doMultEstimatorResponse", false, "processSameEventHV0s: fill MultEstimatorResponse, multNTracksPVeta1 vs FT0M percentile and amplitude, with the event selection of PairLossK0/EventRecoEfficiency (no INEL cuts)"}; + Configurable performPLambdacorrelation{"performPLambdacorrelation", 3, "do p-Lambda correlation study"}; + Configurable mixingEventForClosureTest{"mixingEventForClosureTest", false, "if true, mix events for closure test"}; + Configurable doAssocPhysicalPrimary{"doAssocPhysicalPrimary", false, "require physical primary for associated particles"}; + Configurable doAssocPhysicalPrimaryInGen{"doAssocPhysicalPrimaryInGen", false, "require physical primary for associated particles in Generated Partilces"}; + Configurable doLambdaPrimary{"doLambdaPrimary", false, "do primary selection for lambda"}; } masterConfigurations; // master analysis switches - Configurable doAssocPhysicalPrimary{"doAssocPhysicalPrimary", false, "require physical primary for associated particles"}; - Configurable doAssocPhysicalPrimaryInGen{"doAssocPhysicalPrimaryInGen", false, "require physical primary for associated particles in Generated Partilces"}; - Configurable doLambdaPrimary{"doLambdaPrimary", false, "do primary selection for lambda"}; Configurable doAutocorrelationRejection{"doAutocorrelationRejection", true, "reject pairs where trigger Id is the same as daughter particle Id"}; Configurable doMixingQAandEventQA{"doMixingQAandEventQA", true, "if true, add EvnetQA and MixingQA hist to histos"}; Configurable doITSClustersQA{"doITSClustersQA", true, "if true, add ITSCluster hist to histos"}; @@ -185,6 +190,11 @@ struct HStrangeCorrelation { ConfigurableAxis axisLambdaMass{"axisLambdaMass", {200, 1.01f, 1.21f}, "Inv. Mass (GeV/c^{2})"}; ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 20, 40, 60, 100, 120, 140, 160, 180, 200, 220, 240, 260, 280, 300}, "Binning of the Multiplicity axis in model prediction process"}; ConfigurableAxis axisMidrapidityMultiplicity{"axisMidrapidityMultiplicity", {VARIABLE_WIDTH, 0, 20, 40, 60, 100, 120, 140, 160, 180, 200, 220, 240, 260, 280, 300}, "Binning of the Midrapidity Multiplicity axis in model prediction process"}; + ConfigurableAxis axisNchGen{"axisNchGen", {151, -0.5f, 150.5f}, "generated charged primaries in |eta| < 1"}; + ConfigurableAxis axisNRecoCollisions{"axisNRecoCollisions", {6, -0.5f, 5.5f}, "reconstructed collisions per MC collision"}; + // finer than axisMult but containing all its default edges, so the analysis classes can be recovered by merging bins + ConfigurableAxis axisCentFT0M{"axisCentFT0M", {VARIABLE_WIDTH, 0.0f, 0.01f, 0.1f, 0.5f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 15.0f, 20.0f, 25.0f, 30.0f, 35.0f, 40.0f, 45.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.0f}, "FT0M percentile of the multNTracksPVeta1 vs FT0M response"}; + ConfigurableAxis axisMultFT0M{"axisMultFT0M", {1000, 0.0f, 20000.0f}, "FT0M amplitude of the multNTracksPVeta1 vs FT0M response"}; } axesConfigurations; @@ -323,9 +333,40 @@ struct HStrangeCorrelation { // processSameEventHV0s must be off when it is on. // doGenLevelStudy PairLossK0/GenStudy: generator-level only, see the // comment on runGenLevelStudy in processPairLossK0MC + // doEventRecoEfficiencyStudy + // PairLossK0/EventRecoEfficiency: MC-collision + // reconstruction efficiency, Nch^gen vs Nch^reco and + // Nch^reco vs FT0M (percentile and amplitude) responses, + // see runEventRecoEfficiencyStudy + // doPVQualityStudy PairLossK0/PVQuality: best-collision PV position and + // reco - MC residuals vs multNTracksPVeta1, and the + // MC-collision reconstruction and selection stages vs + // Nch^gen, split by the truth h-K0 content of the event, + // see runPVQualityStudy Configurable doStageDiagnostics{"doStageDiagnostics", true, "part 1: fill the PairLossK0 truth-pair reconstruction ladder and its diagnostics"}; Configurable doRecComparison{"doRecComparison", false, "part 2: fill the PairLossK0/Comparison cumulative Rec/Truth/Gen/Final ladder (runs the exact reconstructed path internally)"}; + Configurable recComparisonBestCollisionOnly{"recComparisonBestCollisionOnly", false, "part 2: run the Rec path of PairLossK0/Comparison only in the best reconstructed collision (most PV contributors) of each MC collision, the one the Gen, Truth and Final stages are selected on. Set false to run it in every reconstructed collision, as the data path does"}; Configurable doGenLevelStudy{"doGenLevelStudy", false, "part 3: fill the PairLossK0/GenStudy generated-vs-reconstructed split, using generator-level event selection only"}; + Configurable doEventRecoEfficiencyStudy{"doEventRecoEfficiencyStudy", false, "fill PairLossK0/EventRecoEfficiency: Nch^gen (charged primaries, |eta| < 1) of all, reconstructed and selected MC collisions, Nch^gen vs multNTracksPVeta1 and multNTracksPVeta1 vs FT0M percentile and amplitude"}; + Configurable nchGenResponseBestCollisionOnly{"nchGenResponseBestCollisionOnly", false, "fill PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1 with the best reconstructed collision (most PV contributors) of each MC collision only, instead of with every reconstructed collision; the multNTracksPVeta1 vs FT0M responses stay per reconstructed collision"}; + Configurable doPVQualityStudy{"doPVQualityStudy", false, "fill PairLossK0/PVQuality: best-collision PV x, y and reco - MC residuals vs multNTracksPVeta1, and the MC-collision reconstruction and selection stages vs Nch^gen, split by the truth h-K0 content of the event"}; + // Coarse on purpose: the PV histograms are dense THnF with a fine position axis + ConfigurableAxis axisPVQualityNTracks{"axisPVQualityNTracks", {VARIABLE_WIDTH, -0.5f, 0.5f, 1.5f, 2.5f, 3.5f, 4.5f, 5.5f, 6.5f, 8.5f, 10.5f, 12.5f, 15.5f, 20.5f, 25.5f, 30.5f, 40.5f, 50.5f, 70.5f, 100.5f, 150.5f}, "multNTracksPVeta1 classes of PairLossK0/PVQuality"}; + ConfigurableAxis axisPVPosition{"axisPVPosition", {400, -0.2f, 0.2f}, "reconstructed PV x and y (cm) of PairLossK0/PVQuality"}; + ConfigurableAxis axisPVResidual{"axisPVResidual", {200, -0.1f, 0.1f}, "reconstructed minus generated PV x, y and z (cm) of PairLossK0/PVQuality"}; + Configurable applyINELSelectionAtGenLevel{"applyINELSelectionAtGenLevel", false, "part 2 (pp only): replace the reconstructed INEL cuts by Nch^gen >= 1 of the MC collision in the Rec, Gen, Truth and Final stages (selectINELgtN is ignored) and fill only PairLossK0/Comparison/NchGen (Rec and Gen vs Nch^gen, integrated over vertex z and multiplicity); requires doRecComparison and applyNewMCSelection"}; + ConfigurableAxis axisComparisonNchGen{"axisComparisonNchGen", {VARIABLE_WIDTH, 0.5f, 4.5f, 9.5f, 19.5f, 1000.5f}, "Nch^gen classes (charged primaries in |eta| < 1) of PairLossK0/Comparison/NchGen"}; + // MC-only event quality cut on the Nch^gen vs multNTracksPVeta1 response + // (PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1): a reconstructed collision + // is kept only if |multNTracksPVeta1 - mu(Nch^gen)| <= nSigma * max(sigma(Nch^gen), sigmaMin), + // with mu a polynomial in Nch^gen and sigma a polynomial in sqrt(Nch^gen), e.g. fits of + // the mean and width of each Nch^gen column of that response. It uses the generated + // multiplicity, so it has no data counterpart + Configurable applyNchResponseBandCut{"applyNchResponseBandCut", false, "part 2 (MC only): keep only reconstructed collisions inside the mu +- nSigma*sigma band of the Nch^gen vs multNTracksPVeta1 response, in the Rec (every collision), Gen, Truth and Final (best collision) stages of PairLossK0/Comparison"}; + Configurable> nchResponseBandMeanPars{"nchResponseBandMeanPars", {0.0, 0.9}, "mean multNTracksPVeta1 = p0 + p1*x + p2*x^2 + ..., x = Nch^gen"}; + Configurable> nchResponseBandSigmaPars{"nchResponseBandSigmaPars", {0.0, 0.35}, "multNTracksPVeta1 width = p0 + p1*sqrt(x) + p2*x + p3*x^1.5 + ..., x = Nch^gen"}; + Configurable nchResponseBandNSigma{"nchResponseBandNSigma", 3.0f, "half-width of the band in units of sigma"}; + Configurable nchResponseBandSigmaMin{"nchResponseBandSigmaMin", 1.0f, "lower bound of sigma, which keeps the band open at low Nch^gen"}; // Generated charged multiplicity of the MC collision, counted in |eta| < 0.8 // by mCounter. Plain ConfigurableAxis: unlike the correlation axes it is NOT // trimmed by skipUnderOverflowInTHn, so what you configure is what you get. @@ -376,8 +417,6 @@ struct HStrangeCorrelation { float efficiencyError; int type; }; - float pvz = 0.0f; - float mult = 0.0f; std::vector trigParticles; std::vector assocParticles; void addValidParticle(float eta, float phi, float pt, int region, float efficiency, float efficiencyError, int type) @@ -395,6 +434,13 @@ struct HStrangeCorrelation { using ValidCollisions = std::vector>; ValidCollisions validCollisions; + struct ValidTriggerInGen { + double eta; + double phi; + double pt; + }; + using GenLevelCollisions = std::vector>>; + GenLevelCollisions genLevelCollisions; // objects to use for efficiency corrections TH2F* hEfficiencyTrigger = nullptr; TH3F* hEfficiencyTriggerMult = nullptr; @@ -612,6 +658,73 @@ struct HStrangeCorrelation { "Not in table, no cut explains it", "In table, but replica would reject"}; + // Breakdown of the Rec variant 0 -> 1 step of PairLossK0/Comparison ("+ truth-trigger + // match/acceptance"). Every Rec trigger is counted in the All bin and in exactly one + // other bin: Matched if it passes variant 1, otherwise the first condition it fails. + // The order follows the variant 1 lookup -- MC label, MC collision of the label, then + // the truth-trigger acceptance in the order pairLossTruthTriggerRejection checks it -- + // so All = Matched + the sum of the rejection bins. + enum PairLossTriggerMatchQABin : int { + PairLossTriggerMatchAll = 0, + PairLossTriggerMatchMatched, + PairLossTriggerMatchNoMcLabel, + PairLossTriggerMatchOtherMcCollision, + PairLossTriggerMatchFailPdg, + PairLossTriggerMatchFailEta, + PairLossTriggerMatchFailPt, + PairLossTriggerMatchFailCharge, + PairLossTriggerMatchNBins + }; + + static constexpr std::array PairLossTriggerMatchQANames = { + "All Rec (variant 0)", + "Matched (variant 1)", + "No MC label", + "Label in another MC collision", + "Truth PDG not pi/K/p/e/mu", + "|eta truth| > etaSel", + "pT truth outside trigger axis", + "Truth charge"}; + + // Truth content of an MC collision for PairLossK0/PVQuality. Trigger and K0 follow + // the acceptance, primary and leading-trigger requirements of PairLossK0/Comparison/Truth, + // and a pair excludes a trigger that is one of the K0 daughters. An MC collision enters + // every class it belongs to, so the classes overlap and must not be summed: + // HasTrigger, HasPair and K0DaughterPVContributor are subsets of All, NearPair and + // AwayPair subsets of HasPair, and one MC collision can be both NearPair and AwayPair. + // K0DaughterPVContributor needs the best reconstructed collision, so it is empty at the + // first stage of hEventStage. + enum PairLossPVQualityClass : int { + PairLossPVQualityAll = 0, + PairLossPVQualityHasTrigger, + PairLossPVQualityHasPair, + PairLossPVQualityNearPair, + PairLossPVQualityAwayPair, + PairLossPVQualityK0DaughterPVContributor, + PairLossPVQualityNClasses + }; + + static constexpr std::array PairLossPVQualityClassNames = { + "all", + "truth trigger", + "truth h-K^{0}_{S} pair", + "near-side pair (|#Delta#varphi| < #pi/3)", + "away-side pair (|#Delta#varphi - #pi| < #pi/3)", + "K^{0}_{S} daughter is a PV contributor"}; + + // MC-collision stages of PairLossK0/PVQuality/hEventStage, each a subset of the previous one + enum PairLossPVQualityStage : int { + PairLossPVQualityStageAll = 0, + PairLossPVQualityStageHasRecoColl, + PairLossPVQualityStageSelected, + PairLossPVQualityNStages + }; + + static constexpr std::array PairLossPVQualityStageNames = { + "MC collision", + "has reconstructed collision", + "best collision selected"}; + struct PairLossV0Info { int64_t globalIndex = -1; int64_t positiveTrackId = -1; @@ -692,6 +805,9 @@ struct HStrangeCorrelation { int64_t bestCollisionId = -1; float vtxZ = 0.0f; float multiplicity = 0.0f; + // Nch^gen of the MC collision; >= 0 only with applyINELSelectionAtGenLevel, + // then the Rec branches fill only PairLossK0/Comparison/NchGen + int nChGen = -1; std::unordered_map trackToMc; std::unordered_map v0ToMc; std::unordered_map truthTriggers; @@ -705,6 +821,9 @@ struct HStrangeCorrelation { std::unordered_set finalTruthPairs; std::unordered_set countedPairs; std::unordered_set countedTriggers; + // PairLossTriggerMatchQABin of the MC particle behind every labelled trigger + // track, keyed by MC particle. Only particles of this MC collision are present. + std::unordered_map triggerTruthRejection; void clear() { @@ -712,6 +831,7 @@ struct HStrangeCorrelation { bestCollisionId = -1; vtxZ = 0.0f; multiplicity = 0.0f; + nChGen = -1; trackToMc.clear(); v0ToMc.clear(); truthTriggers.clear(); @@ -723,6 +843,7 @@ struct HStrangeCorrelation { finalTruthPairs.clear(); countedPairs.clear(); countedTriggers.clear(); + triggerTruthRejection.clear(); } }; static thread_local PairLossComparisonContext pairLossComparison; @@ -802,6 +923,48 @@ struct HStrangeCorrelation { return absPdg == PDG_t::kPiPlus || absPdg == PDG_t::kKPlus || absPdg == PDG_t::kProton || absPdg == PDG_t::kElectron || absPdg == PDG_t::kMuonMinus; } + // Truth-trigger acceptance of the PairLoss Rec comparison (comparisonTruthTriggers): + // species, |eta|, pT and charge, checked in this order. Returns + // PairLossTriggerMatchMatched if the generated particle passes all of them, otherwise + // the PairLossTriggerMatchQABin of the first one it fails. sign is set to the charge + // sign once species, |eta| and pT have passed, and to 0 before. + template + int pairLossTruthTriggerRejection(TMcParticle const& mcParticle, int& sign) + { + sign = 0; + if (!isPairLossTriggerPdg(mcParticle.pdgCode())) { + return PairLossTriggerMatchFailPdg; + } + if (std::abs(mcParticle.eta()) > etaSel) { + return PairLossTriggerMatchFailEta; + } + if (mcParticle.pt() < axisRanges[3][0] || mcParticle.pt() > axisRanges[3][1]) { + return PairLossTriggerMatchFailPt; + } + auto const* pdgParticle = pdgDB->GetParticle(mcParticle.pdgCode()); + const double charge = pdgParticle != nullptr ? pdgParticle->Charge() : 0.0; + sign = charge > 0.0 ? 1 : (charge < 0.0 ? -1 : 0); + if (sign == 0 || (triggerParticleCharge > 0 && sign < 0) || (triggerParticleCharge < 0 && sign > 0)) { + return PairLossTriggerMatchFailCharge; + } + return PairLossTriggerMatchMatched; + } + + // PairLossTriggerMatchQABin of a Rec trigger track while the PairLoss Rec comparison + // runs: the two lookups of Rec variant 1, resolved into the first one that fails + static int pairLossTriggerMatchBin(int64_t trackGlobalIndex) + { + const auto mcIt = pairLossComparison.trackToMc.find(trackGlobalIndex); + if (mcIt == pairLossComparison.trackToMc.end()) { + return PairLossTriggerMatchNoMcLabel; + } + const auto rejectionIt = pairLossComparison.triggerTruthRejection.find(mcIt->second); + if (rejectionIt == pairLossComparison.triggerTruthRejection.end()) { + return PairLossTriggerMatchOtherMcCollision; + } + return rejectionIt->second; + } + template PairLossTrackInfo makePairLossTrackInfo(TTrack const& track) const { @@ -811,7 +974,7 @@ struct HStrangeCorrelation { .eta = track.eta(), .phi = track.phi(), .sign = track.sign(), - .tpcCrossedRows = track.tpcNClsCrossedRows(), + .tpcCrossedRows = track.hasTPC() ? static_cast(track.tpcNClsCrossedRows()) : 0, // 0 without TPC, so classifyTriggerTracksFailure rejects it as the filter does .tpcSharedClusters = track.tpcNClsShared(), .itsClusters = track.itsNCls(), .hasLayer0 = static_cast(TESTBIT(track.itsClusterMap(), 0)), @@ -866,7 +1029,11 @@ struct HStrangeCorrelation { const double maxRadius = pairLossK0Configurations.maxRadius; const double radiusStep = pairLossK0Configurations.radiusStep; - for (double radius = startRadius; radius <= maxRadius + 0.5 * radiusStep; radius += radiusStep) { + // radii startRadius + i * radiusStep up to maxRadius, with half a step of tolerance; + // nRadii <= 0 (no radius) if startRadius is beyond that + const int nRadii = static_cast(std::floor((maxRadius - startRadius) / radiusStep + 0.5)) + 1; + for (int iRadius = 0; iRadius < nRadii; ++iRadius) { + const double radius = startRadius + iRadius * radiusStep; const double daughterArgument = daughterPhase * radius; const double triggerArgument = triggerPhase * radius; if (std::abs(daughterArgument) > 1.0 || std::abs(triggerArgument) > 1.0) { @@ -1091,7 +1258,8 @@ struct HStrangeCorrelation { template bool isValidTrigger(TTrack const& track, bool isLeading) { - if (track.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsTrigger) { + // hasTPC() is needed: since O2-7038 (Oct 2026) tracks without TPC report 256 crossed rows + if (!track.hasTPC() || track.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsTrigger) { return false; // crossed rows } if (!track.hasITS() && trackSelection.triggerRequireITS) { @@ -1131,7 +1299,7 @@ struct HStrangeCorrelation { template bool isValidAssocHadron(TTrack const& track) { - if (track.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { + if (!track.hasTPC() || track.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { return false; // crossed rows } if (!track.hasITS() && trackSelection.assocRequireITS) { @@ -1242,9 +1410,13 @@ struct HStrangeCorrelation { double phaseProton = (-0.3 * B * assoc[2]) / (2 * assoc[1]); double phaseTrack = (-0.3 * B * trigg[2]) / (2 * trigg[1]); - for (double r = MinRadiusTPC; r <= MaxRadiusTPC; r += 0.05) { + // radii MinRadiusTPC + i * RadiusStep below MaxRadiusTPC: 34 radii, 0.80 to 2.45 m + constexpr double RadiusStep = 0.05; + constexpr int NRadii = static_cast((MaxRadiusTPC - MinRadiusTPC) / RadiusStep + 0.5); + for (int iRadius = 0; iRadius < NRadii; ++iRadius) { + const double r = MinRadiusTPC + iRadius * RadiusStep; dPhiStar = dPhi + std::asin(phaseProton * r) - std::asin(phaseTrack * r); - dPhiStarMean += (dPhiStar / 34); + dPhiStarMean += (dPhiStar / NRadii); } return dPhiStarMean; @@ -1260,7 +1432,8 @@ struct HStrangeCorrelation { hist->SetBinContent(binx, biny, newContent); hist->SetBinError(binx, biny, newUncert); } - void fillCorrelationHistogram(std::shared_ptr const& hist, std::array const& binFillThn, float etaWeight, float efficiency, float totalEffUncert, float purity, float totalPurityUncert) + template + void fillCorrelationHistogram(std::shared_ptr const& hist, std::array const& binFillThn, float etaWeight, float efficiency, float totalEffUncert, float purity, float totalPurityUncert) { float previousContent = 0.0f, previousError2 = 0.0f, currentContent = 0.0f, currentError2 = 0.0f; int bin = hist->GetBin(binFillThn.data()); @@ -1271,21 +1444,16 @@ struct HStrangeCorrelation { hist->SetBinContent(bin, currentContent); hist->SetBinError2(bin, currentError2); } + // Rec coordinates for the PairLossK0/Comparison/NchGen histograms: the vertex-z + // and multiplicity coordinates are replaced by the Nch^gen of the current + // PairLoss MC collision + static std::array toPairLossNchGenCoordinates(std::array const& binFillThn) + { + return {binFillThn[0], binFillThn[1], binFillThn[2], binFillThn[3], static_cast(pairLossComparison.nChGen)}; + } void fillCorrelationsV0(aod::TriggerTracks const& triggers, aod::AssocV0s const& assocs, bool mixing, bool mixingInBf, float pvx, float pvy, float pvz, float mult, double bField) { ValidCollision currentCollision; - int binMult = 0; - int nBinsMult = 0; - int nBinsVtxZ = 0; - int binVtxZ = 0; - currentCollision.pvz = pvz; - currentCollision.mult = mult; - if (mixingInBf) { - nBinsMult = histos.get(HIST("axes/hMultAxis"))->GetNbinsX(); - binMult = histos.get(HIST("axes/hMultAxis"))->GetXaxis()->FindBin(mult) - 1; - nBinsVtxZ = histos.get(HIST("axes/hVertexZAxis"))->GetNbinsX(); - binVtxZ = histos.get(HIST("axes/hVertexZAxis"))->GetXaxis()->FindBin(pvz) - 1; - } bool firstLoop = false; for (auto const& triggerTrack : triggers) { if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) { @@ -1326,20 +1494,30 @@ struct HStrangeCorrelation { efficiencyTriggError = 0; } } + // PairLossTriggerMatchQABin of this trigger, set only while the PairLoss Rec + // comparison fills its variants, and reused for every pair of the trigger + int pairLossTriggerMatch = PairLossTriggerMatchAll; if (!mixing) { fillTriggerHistogram(histos.get(HIST("sameEvent/TriggerParticlesV0")), trigg.pt(), mult, efficiencyTrigg, efficiencyTriggError, purityTrigg, purityTriggErr); + if (pairLossComparison.active && pairLossComparison.nChGen >= 0) { + fillTriggerHistogram(histos.get(HIST("PairLossK0/Comparison/NchGen/TriggerRec")), trigg.pt(), pairLossComparison.nChGen, efficiencyTrigg, efficiencyTriggError, purityTrigg, purityTriggErr); + } // Trigger-side control chain, filled from the exact Rec trigger loop so // that it is the same denominator the ordinary Rec correlation uses. // Each pair variant needs the matching trigger variant, otherwise only - // shapes and not per-trigger amplitudes can be compared. - if (pairLossComparison.active) { + // shapes and not per-trigger amplitudes can be compared. Not booked with + // applyINELSelectionAtGenLevel (pairLossComparison.nChGen >= 0). + if (pairLossComparison.active && pairLossComparison.nChGen < 0) { const double triggerWeight = purityTrigg / efficiencyTrigg; auto fillTriggerVariant = [&](int variant, double fillPtTrigger, double fillVtxZ, double fillMult, double weight) { histos.fill(HIST("PairLossK0/Comparison/TriggerVariants"), variant, fillPtTrigger, fillVtxZ, fillMult, weight); }; fillTriggerVariant(0, trigg.pt(), pvz, mult, triggerWeight); + pairLossTriggerMatch = pairLossTriggerMatchBin(trigg.globalIndex()); + histos.fill(HIST("PairLossK0/Comparison/TriggerMatchQA/hTrigger"), PairLossTriggerMatchAll, trigg.pt(), triggerWeight); + histos.fill(HIST("PairLossK0/Comparison/TriggerMatchQA/hTrigger"), pairLossTriggerMatch, trigg.pt(), triggerWeight); const auto triggerLadderMcIt = pairLossComparison.trackToMc.find(trigg.globalIndex()); if (triggerLadderMcIt != pairLossComparison.trackToMc.end()) { const auto triggerLadderTruthIt = pairLossComparison.truthTriggers.find(triggerLadderMcIt->second); @@ -1407,7 +1585,7 @@ struct HStrangeCorrelation { } //---] track quality check [--- - if (postrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || negtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { + if (!postrack.hasTPC() || postrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || !negtrack.hasTPC() || negtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { continue; } if (trackSelection.checksRequireTPCChi2 && (postrack.tpcChi2NCl() > trackSelection.maxTPCChi2PerClusterAssociated || negtrack.tpcChi2NCl() > trackSelection.maxTPCChi2PerClusterAssociated)) { @@ -1485,6 +1663,12 @@ struct HStrangeCorrelation { float efficiency = 1.0f; float totalEffUncert = 0.0; float efficiencyError = 0.0f; + if (masterConfigurations.performPLambdacorrelation == 0 && ((Index == 1 && triggSign == 1) || (Index == 2 && triggSign == -1))) { + return; + } + if (masterConfigurations.performPLambdacorrelation == 1 && ((Index == 1 && triggSign == -1) || (Index == 2 && triggSign == 1))) { + return; + } if (efficiencyFlags.applyEfficiencyCorrection) { if (efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { std::array bin = {ptassoc, assoc.eta(), assoc.phi(), mult}; @@ -1516,7 +1700,7 @@ struct HStrangeCorrelation { } std::array binFillThn = {deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult}; if (TESTBIT(doCorrelation, Index) && (!efficiencyFlags.applyEfficiencyCorrection || efficiency != 0) && (masterConfigurations.doPPAnalysis || (TESTBIT(selMap, Index) && TESTBIT(selMap, Index + 3)))) { - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && ((-massWindowConfigurations.maxBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < -massWindowConfigurations.minBgNSigma) || (-massWindowConfigurations.maxPeakNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxPeakNSigma) || (+massWindowConfigurations.minBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxBgNSigma))) { @@ -1530,12 +1714,16 @@ struct HStrangeCorrelation { histos.fill(HIST("sameEvent/InvariantMass/") + HIST(V0names[Index]) + HIST("/hUnderlyingEvent"), ptassoc, pttrigger, getV0InvariantMass(assoc)); } } - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && -massWindowConfigurations.maxBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < -massWindowConfigurations.minBgNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && -massWindowConfigurations.maxBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < -massWindowConfigurations.minBgNSigma) { fillCorrelationHistogram(histos.get(HIST("sameEvent/LeftBg/") + HIST(V0names[Index])), binFillThn, etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); if (pairLossComparison.active && Index == IndexK0) { // Exact Rec left sideband, with the same reconstructed // selections, coordinates and weight as the ordinary Rec path. - fillCorrelationHistogram(histos.get(HIST("PairLossK0/Comparison/RecLeftBg")), binFillThn, etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + if (pairLossComparison.nChGen >= 0) { + fillCorrelationHistogram(histos.get(HIST("PairLossK0/Comparison/NchGen/RecLeftBg")), toPairLossNchGenCoordinates(binFillThn), etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + } else { + fillCorrelationHistogram(histos.get(HIST("PairLossK0/Comparison/RecLeftBg")), binFillThn, etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + } } if (doDeltaPhiStarCheck) { double deltaPhiStar = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStar, bField); @@ -1553,7 +1741,7 @@ struct HStrangeCorrelation { } } } - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && ((-massWindowConfigurations.maxPeakNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxPeakNSigma))) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && ((-massWindowConfigurations.maxPeakNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxPeakNSigma))) { if (masterConfigurations.doCorrelationsHadronV0daughter) { fillCorrelationHistogram(histos.get(HIST("sameEvent/Signal/") + HIST(V0names[Index]) + HIST("_hSameSign")), binFillThnSameSignDaugher, 1, 1, 1, 1, 1); fillCorrelationHistogram(histos.get(HIST("sameEvent/Signal/") + HIST(V0names[Index]) + HIST("_hOppositeSign")), binFillThnOppositeSignDaugher, 1, 1, 1, 1, 1); @@ -1563,8 +1751,15 @@ struct HStrangeCorrelation { // PairLoss Rec control study. Stage 0 is filled from this // exact Rec signal branch, so it is not a hand-written // approximation of the reconstructed pair selection. Each next - // stage adds exactly one cumulative condition. - if (pairLossComparison.active && Index == IndexK0) { + // stage adds exactly one cumulative condition. With + // applyINELSelectionAtGenLevel (pairLossComparison.nChGen >= 0) + // only the Nch^gen-binned Rec is filled; fake K0 = Rec - RecPeakTrueK0. + if (pairLossComparison.active && Index == IndexK0 && pairLossComparison.nChGen >= 0) { + fillCorrelationHistogram(histos.get(HIST("PairLossK0/Comparison/NchGen/Rec")), toPairLossNchGenCoordinates(binFillThn), etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + if (assocCandidate.mcTrue(IndexK0)) { + fillCorrelationHistogram(histos.get(HIST("PairLossK0/Comparison/NchGen/RecPeakTrueK0")), toPairLossNchGenCoordinates(binFillThn), etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + } + } else if (pairLossComparison.active && Index == IndexK0) { fillCorrelationHistogram(histos.get(HIST("PairLossK0/Comparison/Rec")), binFillThn, etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); // Rec is the complete peak-window sample. Split it into true // and fake K0 components without changing any reconstructed @@ -1583,6 +1778,8 @@ struct HStrangeCorrelation { // 0: the exact Rec entry, including reconstructed coordinates // and the ordinary Rec weight. fillWeightedRecVariant(0, deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult); + histos.fill(HIST("PairLossK0/Comparison/TriggerMatchQA/hPair"), PairLossTriggerMatchAll, deltaphi, deltaeta, ptassoc, pttrigger, pairWeight); + histos.fill(HIST("PairLossK0/Comparison/TriggerMatchQA/hPair"), pairLossTriggerMatch, deltaphi, deltaeta, ptassoc, pttrigger, pairWeight); const auto triggerMcIdIt = pairLossComparison.trackToMc.find(trigg.globalIndex()); if (triggerMcIdIt != pairLossComparison.trackToMc.end()) { @@ -1672,7 +1869,7 @@ struct HStrangeCorrelation { // weight. Reconstructed matches remain separate. histos.fill(HIST("PairLossK0/Comparison/RecVariants"), 13, truthDeltaPhi, truthDeltaEta, truthK0.pt, truthTrigger.pt, pairLossComparison.vtxZ, pairLossComparison.multiplicity); - const PairLossPairKey truthPairKey{truthTrigger.globalIndex, truthK0.globalIndex}; + const PairLossPairKey truthPairKey{.triggerId = truthTrigger.globalIndex, .k0Id = truthK0.globalIndex}; if (pairLossComparison.countedPairs.insert(truthPairKey).second) { // 14 additionally keeps one reconstructed // match per truth pair. Stage 15 and Final @@ -1719,12 +1916,16 @@ struct HStrangeCorrelation { } } } - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && +massWindowConfigurations.minBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxBgNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && +massWindowConfigurations.minBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxBgNSigma) { fillCorrelationHistogram(histos.get(HIST("sameEvent/RightBg/") + HIST(V0names[Index])), binFillThn, etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); if (pairLossComparison.active && Index == IndexK0) { // Exact Rec right sideband, with the same reconstructed // selections, coordinates and weight as the ordinary Rec path. - fillCorrelationHistogram(histos.get(HIST("PairLossK0/Comparison/RecRightBg")), binFillThn, etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + if (pairLossComparison.nChGen >= 0) { + fillCorrelationHistogram(histos.get(HIST("PairLossK0/Comparison/NchGen/RecRightBg")), toPairLossNchGenCoordinates(binFillThn), etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + } else { + fillCorrelationHistogram(histos.get(HIST("PairLossK0/Comparison/RecRightBg")), binFillThn, etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + } } if (doDeltaPhiStarCheck) { double deltaPhiStar = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStar, bField); @@ -1742,14 +1943,14 @@ struct HStrangeCorrelation { } } } - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && -massWindowConfigurations.maxBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < -massWindowConfigurations.minBgNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && -massWindowConfigurations.maxBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < -massWindowConfigurations.minBgNSigma) { if (mixingInBf) { currentCollision.addValidParticle(assoc.eta(), assoc.phi(), assoc.pt(), 0, efficiency, efficiencyError, Index); } else { fillCorrelationHistogram(histos.get(HIST("mixedEvent/LeftBg/") + HIST(V0names[Index])), binFillThn, 1, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); } } - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && -massWindowConfigurations.maxPeakNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxPeakNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && -massWindowConfigurations.maxPeakNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxPeakNSigma) { if (mixingInBf) { currentCollision.addValidParticle(assoc.eta(), assoc.phi(), assoc.pt(), 1, efficiency, efficiencyError, Index); } else { @@ -1760,7 +1961,7 @@ struct HStrangeCorrelation { fillCorrelationHistogram(histos.get(HIST("mixedEvent/Signal/") + HIST(V0names[Index]) + HIST("_hOppositeSign")), binFillThnOppositeSignDaugher, 1, 1, 1, 1, 1); } } - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && +massWindowConfigurations.minBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxBgNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && +massWindowConfigurations.minBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxBgNSigma) { if (mixingInBf) { currentCollision.addValidParticle(assoc.eta(), assoc.phi(), assoc.pt(), 2, efficiency, efficiencyError, Index); } else { @@ -1771,10 +1972,16 @@ struct HStrangeCorrelation { }); } } - if (!mixingInBf || binVtxZ < 0 || binVtxZ > nBinsVtxZ - 1 || binMult < 0 || binMult > nBinsMult - 1) { + if (!mixingInBf) { return; } - int binnumb = binMult * nBinsVtxZ + binVtxZ; + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; + // When 'collisionHasTriggOrAssoc' = 0: + // binContent(hMECollisionBins) = Σ(Submitted jobs(done))[binContent(the same bin of hSECollisionBins) * masterConfigurations.mixingParameter - Σ(k=0 to min(masterConfigurations.mixingParameter,binContent)) k] + // When 'collisionHasTriggOrAssoc' = 3 + // More collision loss at higher peripheral centrality (fewer target particles); higher bincontent of HIST("mixedEvent/Signal/") + HIST(Cascadenames[Index]) + // due to avoiding vector occupancy by collisions with no trigger&associated particles + int binnumb = colBinning.getBin({pvz, mult}); int hastirgorassoc = masterConfigurations.collisionHasTriggOrAssoc; if ((hastirgorassoc == 1 && currentCollision.trigParticles.empty()) || (hastirgorassoc == 2 && currentCollision.assocParticles.empty()) || @@ -1783,13 +1990,7 @@ struct HStrangeCorrelation { return; } for (const auto& collision : validCollisions[binnumb]) { - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; - // When 'collisionHasTriggOrAssoc' = 0: - // binContent(hMECollisionBins) = Σ(Submitted jobs(done))[binContent(the same bin of hSECollisionBins) * masterConfigurations.mixingParameter - Σ(k=0 to min(masterConfigurations.mixingParameter,binContent)) k] - // When 'collisionHasTriggOrAssoc' = 3 - // More collision loss at higher peripheral centrality (fewer target particles); higher bincontent of HIST("mixedEvent/Signal/") + HIST(Cascadenames[Index]) - // due to avoiding vector occupancy by collisions with no trigger&associated particles - histos.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision.pvz, collision.mult})); + histos.fill(HIST("MixingQA/hMECollisionBins"), binnumb); for (const auto& trigger : collision.trigParticles) { for (const auto& assoc : currentCollision.assocParticles) { float deltaeta = trigger.eta - assoc.eta; @@ -1834,18 +2035,6 @@ struct HStrangeCorrelation { void fillCorrelationsCascade(aod::TriggerTracks const& triggers, aod::AssocCascades const& assocs, bool mixing, bool mixingInBf, float pvx, float pvy, float pvz, float mult, double bField) { ValidCollision currentCollision; - int nBinsMult = 0; - int binMult = 0; - int nBinsVtxZ = 0; - int binVtxZ = 0; - currentCollision.pvz = pvz; - currentCollision.mult = mult; - if (mixingInBf) { - nBinsMult = histos.get(HIST("axes/hMultAxis"))->GetNbinsX(); - binMult = histos.get(HIST("axes/hMultAxis"))->GetXaxis()->FindBin(mult) - 1; - nBinsVtxZ = histos.get(HIST("axes/hVertexZAxis"))->GetNbinsX(); - binVtxZ = histos.get(HIST("axes/hVertexZAxis"))->GetXaxis()->FindBin(pvz) - 1; - } bool firstLoop = false; for (auto const& triggerTrack : triggers) { if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) { @@ -1964,7 +2153,7 @@ struct HStrangeCorrelation { } std::array assocForDeltaPhiStar = {phiProton, ptProton, signProton}; //---] track quality check [--- - if (postrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || negtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || bachtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { + if (!postrack.hasTPC() || postrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || !negtrack.hasTPC() || negtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || !bachtrack.hasTPC() || bachtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { continue; } if (trackSelection.checksRequireTPCChi2 && (postrack.tpcChi2NCl() > trackSelection.maxTPCChi2PerClusterAssociated || negtrack.tpcChi2NCl() > trackSelection.maxTPCChi2PerClusterAssociated || bachtrack.tpcChi2NCl() > trackSelection.maxTPCChi2PerClusterAssociated)) { @@ -2051,7 +2240,7 @@ struct HStrangeCorrelation { std::array binFillThn = {deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult}; if (TESTBIT(doCorrelation, Index + 3) && (!efficiencyFlags.applyEfficiencyCorrection || efficiency != 0) && (masterConfigurations.doPPAnalysis || (TESTBIT(cascselMap, Index) && TESTBIT(cascselMap, Index + 4) && TESTBIT(cascselMap, Index + 8) && TESTBIT(cascselMap, Index + 12)))) { - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && -massWindowConfigurations.maxBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < -massWindowConfigurations.minBgNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && -massWindowConfigurations.maxBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < -massWindowConfigurations.minBgNSigma) { fillCorrelationHistogram(histos.get(HIST("sameEvent/LeftBg/") + HIST(Cascadenames[Index])), binFillThn, etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); if (doDeltaPhiStarCheck) { double deltaPhiStar = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStar, bField); @@ -2062,7 +2251,7 @@ struct HStrangeCorrelation { } } } - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && -massWindowConfigurations.maxPeakNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxPeakNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && -massWindowConfigurations.maxPeakNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxPeakNSigma) { fillCorrelationHistogram(histos.get(HIST("sameEvent/Signal/") + HIST(Cascadenames[Index])), binFillThn, etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); if (doDeltaPhiStarCheck) { double deltaPhiStar = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStar, bField); @@ -2073,7 +2262,7 @@ struct HStrangeCorrelation { } } } - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && +massWindowConfigurations.minBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxBgNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && +massWindowConfigurations.minBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxBgNSigma) { fillCorrelationHistogram(histos.get(HIST("sameEvent/RightBg/") + HIST(Cascadenames[Index])), binFillThn, etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); if (doDeltaPhiStarCheck) { double deltaPhiStar = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStar, bField); @@ -2084,21 +2273,21 @@ struct HStrangeCorrelation { } } } - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && -massWindowConfigurations.maxBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < -massWindowConfigurations.minBgNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && -massWindowConfigurations.maxBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < -massWindowConfigurations.minBgNSigma) { if (mixingInBf) { currentCollision.addValidParticle(assoc.eta(), assoc.phi(), assoc.pt(), 0, efficiency, efficiencyError, Index); } else { fillCorrelationHistogram(histos.get(HIST("mixedEvent/LeftBg/") + HIST(Cascadenames[Index])), binFillThn, 1, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); } } - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && -massWindowConfigurations.maxPeakNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxPeakNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && -massWindowConfigurations.maxPeakNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxPeakNSigma) { if (mixingInBf) { currentCollision.addValidParticle(assoc.eta(), assoc.phi(), assoc.pt(), 1, efficiency, efficiencyError, Index); } else { fillCorrelationHistogram(histos.get(HIST("mixedEvent/Signal/") + HIST(Cascadenames[Index])), binFillThn, 1, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); } } - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && +massWindowConfigurations.minBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxBgNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && +massWindowConfigurations.minBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxBgNSigma) { if (mixingInBf) { currentCollision.addValidParticle(assoc.eta(), assoc.phi(), assoc.pt(), 2, efficiency, efficiencyError, Index); } else { @@ -2109,10 +2298,11 @@ struct HStrangeCorrelation { }); } } - if (!mixingInBf || binVtxZ < 0 || binVtxZ > nBinsVtxZ - 1 || binMult < 0 || binMult > nBinsMult - 1) { + if (!mixingInBf) { return; } - int binnumb = binMult * nBinsVtxZ + binVtxZ; + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; + int binnumb = colBinning.getBin({pvz, mult}); int hastirgorassoc = masterConfigurations.collisionHasTriggOrAssoc; if ((hastirgorassoc == 1 && currentCollision.trigParticles.empty()) || (hastirgorassoc == 2 && currentCollision.assocParticles.empty()) || @@ -2121,8 +2311,7 @@ struct HStrangeCorrelation { return; } for (const auto& collision : validCollisions[binnumb]) { - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; - histos.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision.pvz, collision.mult})); + histos.fill(HIST("MixingQA/hMECollisionBins"), binnumb); for (const auto& trigger : collision.trigParticles) { for (const auto& assoc : currentCollision.assocParticles) { float deltaeta = trigger.eta - assoc.eta; @@ -2245,7 +2434,7 @@ struct HStrangeCorrelation { if (trackSelection.checkForITSTPCMissmatchMC && bitcheck(assocTrack.mcMask(), 13)) { continue; } - if (doAssocPhysicalPrimary && !assocTrack.mcPhysicalPrimary()) { + if (masterConfigurations.doAssocPhysicalPrimary && !assocTrack.mcPhysicalPrimary()) { continue; } float deltaphi = computeDeltaPhi(trigg.phi(), assoc.phi()); @@ -2756,73 +2945,208 @@ struct HStrangeCorrelation { } } + if (pairLossK0Configurations.applyINELSelectionAtGenLevel) { + if (!doprocessPairLossK0MC || !pairLossK0Configurations.doRecComparison) { + LOGF(fatal, "pairLossK0Configurations.applyINELSelectionAtGenLevel acts on the Rec/Gen/Truth/Final stages of part 2; enable processPairLossK0MC and pairLossK0Configurations.doRecComparison"); + } + if (!masterConfigurations.doPPAnalysis) { + LOGF(fatal, "pairLossK0Configurations.applyINELSelectionAtGenLevel is only defined for pp: the Pb-Pb event selection has no INEL cut"); + } + if (!masterConfigurations.applyNewMCSelection) { + LOGF(fatal, "pairLossK0Configurations.applyINELSelectionAtGenLevel requires masterConfigurations.applyNewMCSelection=true so that the Gen stage uses the same event selection function as the Rec stage"); + } + if (masterConfigurations.selectINELgtN) { + LOGF(warning, "pairLossK0Configurations.applyINELSelectionAtGenLevel: masterConfigurations.selectINELgtN is ignored in the PairLossK0/Comparison stages, which select Nch^gen >= 1 only"); + } + } + + if (pairLossK0Configurations.applyNchResponseBandCut && !doprocessPairLossK0MC) { + LOGF(fatal, "pairLossK0Configurations.applyNchResponseBandCut needs the generated multiplicity; enable processPairLossK0MC"); + } + + if (doprocessPairLossK0MC && pairLossK0Configurations.doEventRecoEfficiencyStudy) { + // Per MC collision with |vz_gen| <= zVertexCut, each histogram a subset of + // the previous one: HasRecoColl/All is the MC-collision reconstruction + // efficiency vs Nch^gen. The event selection is the one of the Rec path + // (trigger presence + isCollisionSelected) evaluated on the best collision + histos.add("PairLossK0/EventRecoEfficiency/hNchGenAll", "all MC collisions;#it{N}_{ch}^{gen} (|#eta| < 1);MC collisions", kTH1D, {axesConfigurations.axisNchGen}); + histos.add("PairLossK0/EventRecoEfficiency/hNchGenHasRecoColl", "MC collisions with at least one reconstructed collision;#it{N}_{ch}^{gen} (|#eta| < 1);MC collisions", kTH1D, {axesConfigurations.axisNchGen}); + histos.add("PairLossK0/EventRecoEfficiency/hNchGenEvSelNoInel", "best reconstructed collision passes the Rec event selection without INEL cuts;#it{N}_{ch}^{gen} (|#eta| < 1);MC collisions", kTH1D, {axesConfigurations.axisNchGen}); + histos.add("PairLossK0/EventRecoEfficiency/hNchGenEvSel", "best reconstructed collision passes the Rec event selection with reconstructed-level INEL cuts;#it{N}_{ch}^{gen} (|#eta| < 1);MC collisions", kTH1D, {axesConfigurations.axisNchGen}); + histos.add("PairLossK0/EventRecoEfficiency/hNchGenVsNRecoColl", "all MC collisions;#it{N}_{ch}^{gen} (|#eta| < 1);reconstructed collisions", kTH2D, {axesConfigurations.axisNchGen, axesConfigurations.axisNRecoCollisions}); + // Per reconstructed collision passing the Rec event selection without INEL + // cuts, no |vz_gen| cut: the same collisions the Rec path sees before its INEL cut. + // With nchGenResponseBestCollisionOnly the Nch^gen response takes only the best + // collision, which drops the split and duplicate vertices of the MC collision + histos.add("PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1", + pairLossK0Configurations.nchGenResponseBestCollisionOnly ? "best reconstructed collision passing the Rec event selection without INEL cuts;#it{N}_{ch}^{gen} (|#eta| < 1);multNTracksPVeta1" + : "reconstructed collisions passing the Rec event selection without INEL cuts;#it{N}_{ch}^{gen} (|#eta| < 1);multNTracksPVeta1", + kTH2D, {axesConfigurations.axisNchGen, axesConfigurations.axisNchGen}); + histos.add("PairLossK0/EventRecoEfficiency/hNTracksPVeta1VsCentFT0M", "reconstructed collisions passing the Rec event selection without INEL cuts;multNTracksPVeta1;FT0M percentile", kTH2D, {axesConfigurations.axisNchGen, axesConfigurations.axisCentFT0M}); + histos.add("PairLossK0/EventRecoEfficiency/hNTracksPVeta1VsMultFT0M", "reconstructed collisions passing the Rec event selection without INEL cuts;multNTracksPVeta1;FT0M amplitude", kTH2D, {axesConfigurations.axisNchGen, axesConfigurations.axisMultFT0M}); + if (pairLossK0Configurations.applyNchResponseBandCut) { + // Checks of applyNchResponseBandCut: the part of the Nch^gen response it keeps, + // and hNchGenEvSelNoInelInBand / hNchGenEvSelNoInel, its efficiency vs Nch^gen + histos.add("PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1InBand", "hNchGenVsNTracksPVeta1 inside the Nch^gen vs multNTracksPVeta1 band;#it{N}_{ch}^{gen} (|#eta| < 1);multNTracksPVeta1", kTH2D, {axesConfigurations.axisNchGen, axesConfigurations.axisNchGen}); + histos.add("PairLossK0/EventRecoEfficiency/hNchGenEvSelNoInelInBand", "best reconstructed collision passes the Rec event selection without INEL cuts and lies inside the Nch^gen vs multNTracksPVeta1 band;#it{N}_{ch}^{gen} (|#eta| < 1);MC collisions", kTH1D, {axesConfigurations.axisNchGen}); + } + } + + if (doprocessPairLossK0MC && pairLossK0Configurations.doPVQualityStudy) { + // One entry per MC collision with |vz_gen| <= zVertexCut, in every truth-content + // class it belongs to (PairLossPVQualityClass): the classes overlap. + // hEventStage: stages nested as in PairLossK0/EventRecoEfficiency, so + // HasRecoColl/All and Selected/HasRecoColl per class give whether events with a + // trigger and a K0 lose their PV, or have it rejected, more often. + // hPos*: the data-like check -- the beam spot is common to all collisions, so a + // broadening or tail of the best-collision x and y towards low multNTracksPVeta1 + // is a reconstruction effect. hRes*: the same with the generated vertex + // subtracted. Selection bin 0 holds every best collision, bin 1 only those passing + // the event selection of PairLossK0/Comparison, so bin 1 is a subset of bin 0. + const AxisSpec axisPVQualityClass{PairLossPVQualityNClasses, -0.5, static_cast(PairLossPVQualityNClasses) - 0.5, "truth content of the MC collision"}; + const AxisSpec axisPVQualityStage{PairLossPVQualityNStages, -0.5, static_cast(PairLossPVQualityNStages) - 0.5, "MC-collision stage"}; + const AxisSpec axisPVQualitySelection{2, -0.5, 1.5, "best collision"}; + const AxisSpec axisPVQualityNchGen{axesConfigurations.axisNchGen, "#it{N}_{ch}^{gen} (|#eta| < 1)"}; + const AxisSpec axisPVQualityNTracksPVeta1{pairLossK0Configurations.axisPVQualityNTracks, "multNTracksPVeta1"}; + const AxisSpec axisPVQualityPosX{pairLossK0Configurations.axisPVPosition, "#it{x}_{PV}^{reco} (cm)"}; + const AxisSpec axisPVQualityPosY{pairLossK0Configurations.axisPVPosition, "#it{y}_{PV}^{reco} (cm)"}; + const AxisSpec axisPVQualityResX{pairLossK0Configurations.axisPVResidual, "#it{x}_{PV}^{reco} #minus #it{x}_{PV}^{gen} (cm)"}; + const AxisSpec axisPVQualityResY{pairLossK0Configurations.axisPVResidual, "#it{y}_{PV}^{reco} #minus #it{y}_{PV}^{gen} (cm)"}; + const AxisSpec axisPVQualityResZ{pairLossK0Configurations.axisPVResidual, "#it{z}_{PV}^{reco} #minus #it{z}_{PV}^{gen} (cm)"}; + histos.add("PairLossK0/PVQuality/hEventStage", "MC collisions by truth content and stage", kTH3D, {axisPVQualityClass, axisPVQualityStage, axisPVQualityNchGen}); + histos.add("PairLossK0/PVQuality/hPosX", "best-collision PV x", kTHnF, {axisPVQualityClass, axisPVQualitySelection, axisPVQualityNTracksPVeta1, axisPVQualityPosX}); + histos.add("PairLossK0/PVQuality/hPosY", "best-collision PV y", kTHnF, {axisPVQualityClass, axisPVQualitySelection, axisPVQualityNTracksPVeta1, axisPVQualityPosY}); + histos.add("PairLossK0/PVQuality/hResX", "best-collision PV x residual", kTHnF, {axisPVQualityClass, axisPVQualitySelection, axisPVQualityNTracksPVeta1, axisPVQualityResX}); + histos.add("PairLossK0/PVQuality/hResY", "best-collision PV y residual", kTHnF, {axisPVQualityClass, axisPVQualitySelection, axisPVQualityNTracksPVeta1, axisPVQualityResY}); + histos.add("PairLossK0/PVQuality/hResZ", "best-collision PV z residual", kTHnF, {axisPVQualityClass, axisPVQualitySelection, axisPVQualityNTracksPVeta1, axisPVQualityResZ}); + + auto eventStage = histos.get(HIST("PairLossK0/PVQuality/hEventStage")); + for (int i = 0; i < PairLossPVQualityNClasses; ++i) { + eventStage->GetXaxis()->SetBinLabel(i + 1, PairLossPVQualityClassNames[i].data()); + } + for (int i = 0; i < PairLossPVQualityNStages; ++i) { + eventStage->GetYaxis()->SetBinLabel(i + 1, PairLossPVQualityStageNames[i].data()); + } + for (auto const& pvHistogram : {histos.get(HIST("PairLossK0/PVQuality/hPosX")), histos.get(HIST("PairLossK0/PVQuality/hPosY")), + histos.get(HIST("PairLossK0/PVQuality/hResX")), histos.get(HIST("PairLossK0/PVQuality/hResY")), + histos.get(HIST("PairLossK0/PVQuality/hResZ"))}) { + for (int i = 0; i < PairLossPVQualityNClasses; ++i) { + pvHistogram->GetAxis(0)->SetBinLabel(i + 1, PairLossPVQualityClassNames[i].data()); + } + pvHistogram->GetAxis(1)->SetBinLabel(1, "all"); + pvHistogram->GetAxis(1)->SetBinLabel(2, "event selection"); + } + } + + if (doprocessSameEventHV0s && masterConfigurations.doMultEstimatorResponse) { + // Same histograms and event selection as the multNTracksPVeta1 vs FT0M responses of + // PairLossK0/EventRecoEfficiency, filled per reconstructed collision in data + // (or in MC reco, which then reproduces the PairLossK0 ones) + histos.add("MultEstimatorResponse/hNTracksPVeta1VsCentFT0M", "reconstructed collisions passing the Rec event selection without INEL cuts;multNTracksPVeta1;FT0M percentile", kTH2D, {axesConfigurations.axisNchGen, axesConfigurations.axisCentFT0M}); + histos.add("MultEstimatorResponse/hNTracksPVeta1VsMultFT0M", "reconstructed collisions passing the Rec event selection without INEL cuts;multNTracksPVeta1;FT0M amplitude", kTH2D, {axesConfigurations.axisNchGen, axesConfigurations.axisMultFT0M}); + } + if (doprocessPairLossK0MC && pairLossK0Configurations.doRecComparison) { if (doprocessSameEventHV0s) { LOGF(fatal, "pairLossK0Configurations.doRecComparison already runs the exact Rec path internally; set processSameEventHV0s=false to avoid double filling"); } - constexpr int PairLossComparisonNVariants = 16; - const AxisSpec axisPairLossComparisonVariant{PairLossComparisonNVariants, -0.5, PairLossComparisonNVariants - 0.5, "cumulative Rec control variant"}; - constexpr int PairLossComparisonNTriggerVariants = 10; - const AxisSpec axisPairLossComparisonTriggerVariant{PairLossComparisonNTriggerVariants, -0.5, PairLossComparisonNTriggerVariants - 0.5, "cumulative Rec trigger control variant"}; - histos.add("PairLossK0/Comparison/Rec", "exact Rec peak-window h-K0 pairs; all candidates", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); - histos.add("PairLossK0/Comparison/RecPeakTrueK0", "exact Rec peak-window pairs with a true K0", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); - histos.add("PairLossK0/Comparison/RecPeakFakeK0", "exact Rec peak-window pairs without a true K0", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); - histos.add("PairLossK0/Comparison/RecLeftBg", "exact Rec left invariant-mass sideband pairs", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); - histos.add("PairLossK0/Comparison/RecRightBg", "exact Rec right invariant-mass sideband pairs", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); - histos.add("PairLossK0/Comparison/Truth", "PairLoss truth pairs; same axes as Rec", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); - histos.add("PairLossK0/Comparison/Gen", "Closure-test Gen pairs; same axes as Rec", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); - histos.add("PairLossK0/Comparison/Final", "PairLoss final truth-matched pairs; same axes as Rec", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); - histos.add("PairLossK0/Comparison/RecVariants", "cumulative controlled variants of the Rec pair definition", kTHnF, {axisPairLossComparisonVariant, axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); - // The two directional set differences between Rec variant 14 and the - // exact old Final definition. They make non-nested pair definitions - // explicit instead of hiding the mismatch in a single yield ratio. - histos.add("PairLossK0/Comparison/FinalNotInRec", "old-Final truth pairs absent from Rec variant 14", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); - histos.add("PairLossK0/Comparison/RecNotInFinal", "Rec variant-14 truth pairs absent from old Final", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); - // Trigger-side counterpart of the pair ladder. Pair variant i has to be - // divided by the trigger variant listed in the label to obtain a - // per-trigger yield that is narrowed by the same cumulative conditions. - histos.add("PairLossK0/Comparison/TriggerVariants", "cumulative controlled variants of the Rec trigger normalisation", kTHnF, {axisPairLossComparisonTriggerVariant, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); - - const std::array variantLabels = { - "0 exact Rec", - "1 + truth-trigger match/acceptance", - "2 + true K0 (remove peak background)", - "3 + truth-K0 match/acceptance", - "4 + truth daughter exclusion", - "5 + truth-trigger primary", - "6 + truth-K0 primary", - "7 + old truth-leading choice", - "8 + best collision", - "9 + truth pT coordinates only", - "10 + truth event coordinates (vtxZ, mult)", - "11 + truth delta eta only", - "12 + truth delta phi and truth angular range", - "13 + unit pair weight", - "14 + one entry per truth pair", - "15 exact old Final reference"}; - auto recVariants = histos.get(HIST("PairLossK0/Comparison/RecVariants")); - for (size_t i = 0; i < variantLabels.size(); ++i) { - recVariants->GetAxis(0)->SetBinLabel(i + 1, variantLabels[i].data()); - } - - // Pair variant -> trigger variant pairing for per-trigger yields: - // pair 0-3 with trigger 0-1, pair 4-6 with trigger 2, pair 7 with - // trigger 3, pair 8-9 with trigger 4, pair 10-12 with trigger 6, - // pair 13-14 with trigger 7-8, pair 15 with trigger 9. - const std::array triggerVariantLabels = { - "0 exact Rec trigger", - "1 + truth-trigger match/acceptance", - "2 + truth-trigger primary", - "3 + old truth-leading choice", - "4 + best collision", - "5 + truth pT coordinate only", - "6 + truth event coordinates (vtxZ, mult)", - "7 + unit trigger weight", - "8 + one entry per truth trigger", - "9 exact old Final trigger reference"}; - auto triggerVariants = histos.get(HIST("PairLossK0/Comparison/TriggerVariants")); - for (size_t i = 0; i < triggerVariantLabels.size(); ++i) { - triggerVariants->GetAxis(0)->SetBinLabel(i + 1, triggerVariantLabels[i].data()); + if (pairLossK0Configurations.applyINELSelectionAtGenLevel) { + // Only Rec and Gen, with the Nch^gen of the MC collision in place of the + // vertex-z and multiplicity axes (memory), for a closure test per Nch^gen + // class; the rest of the Comparison ladder (Truth, Final, variants, set + // differences) is neither booked nor filled + const AxisSpec axisNchGenClass{pairLossK0Configurations.axisComparisonNchGen, "#it{N}_{ch}^{gen} (|#eta| < 1)"}; + histos.add("PairLossK0/Comparison/NchGen/Rec", "Comparison/Rec vs Nch^gen, integrated over vertex z and multiplicity", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisNchGenClass}); + histos.add("PairLossK0/Comparison/NchGen/RecPeakTrueK0", "Comparison/RecPeakTrueK0 vs Nch^gen, integrated over vertex z and multiplicity", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisNchGenClass}); + histos.add("PairLossK0/Comparison/NchGen/RecLeftBg", "Comparison/RecLeftBg vs Nch^gen, integrated over vertex z and multiplicity", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisNchGenClass}); + histos.add("PairLossK0/Comparison/NchGen/RecRightBg", "Comparison/RecRightBg vs Nch^gen, integrated over vertex z and multiplicity", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisNchGenClass}); + histos.add("PairLossK0/Comparison/NchGen/Gen", "Comparison/Gen vs Nch^gen, integrated over vertex z and multiplicity", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisNchGenClass}); + // Trigger counts for the per-trigger normalisation of the pairs above, with + // the same trigger and event selections: TriggerRec next to + // sameEvent/TriggerParticlesV0 with the same trigger weight, TriggerGen in + // the Gen trigger loop, including events without a K0 + histos.add("PairLossK0/Comparison/NchGen/TriggerRec", "Rec triggers;#it{p}_{T}^{trigger} (GeV/#it{c});#it{N}_{ch}^{gen} (|#eta| < 1)", kTH2D, {axesConfigurations.axisPtQA, axisNchGenClass}); + histos.add("PairLossK0/Comparison/NchGen/TriggerGen", "Gen triggers;#it{p}_{T}^{trigger} (GeV/#it{c});#it{N}_{ch}^{gen} (|#eta| < 1)", kTH2D, {axesConfigurations.axisPtQA, axisNchGenClass}); + } else { + constexpr int PairLossComparisonNVariants = 16; + const AxisSpec axisPairLossComparisonVariant{PairLossComparisonNVariants, -0.5, PairLossComparisonNVariants - 0.5, "cumulative Rec control variant"}; + constexpr int PairLossComparisonNTriggerVariants = 10; + const AxisSpec axisPairLossComparisonTriggerVariant{PairLossComparisonNTriggerVariants, -0.5, PairLossComparisonNTriggerVariants - 0.5, "cumulative Rec trigger control variant"}; + histos.add("PairLossK0/Comparison/Rec", "exact Rec peak-window h-K0 pairs; all candidates", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("PairLossK0/Comparison/RecPeakTrueK0", "exact Rec peak-window pairs with a true K0", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("PairLossK0/Comparison/RecPeakFakeK0", "exact Rec peak-window pairs without a true K0", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("PairLossK0/Comparison/RecLeftBg", "exact Rec left invariant-mass sideband pairs", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("PairLossK0/Comparison/RecRightBg", "exact Rec right invariant-mass sideband pairs", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("PairLossK0/Comparison/Truth", "PairLoss truth pairs; same axes as Rec", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("PairLossK0/Comparison/Gen", "Closure-test Gen pairs; same axes as Rec", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("PairLossK0/Comparison/Final", "PairLoss final truth-matched pairs; same axes as Rec", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("PairLossK0/Comparison/RecVariants", "cumulative controlled variants of the Rec pair definition", kTHnF, {axisPairLossComparisonVariant, axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + // The two directional set differences between Rec variant 14 and the + // exact old Final definition. They make non-nested pair definitions + // explicit instead of hiding the mismatch in a single yield ratio. + histos.add("PairLossK0/Comparison/FinalNotInRec", "old-Final truth pairs absent from Rec variant 14", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("PairLossK0/Comparison/RecNotInFinal", "Rec variant-14 truth pairs absent from old Final", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + // Trigger-side counterpart of the pair ladder. Pair variant i has to be + // divided by the trigger variant listed in the label to obtain a + // per-trigger yield that is narrowed by the same cumulative conditions. + histos.add("PairLossK0/Comparison/TriggerVariants", "cumulative controlled variants of the Rec trigger normalisation", kTHnF, {axisPairLossComparisonTriggerVariant, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + + const std::array variantLabels = { + "0 exact Rec", + "1 + truth-trigger match/acceptance", + "2 + true K0 (remove peak background)", + "3 + truth-K0 match/acceptance", + "4 + truth daughter exclusion", + "5 + truth-trigger primary", + "6 + truth-K0 primary", + "7 + old truth-leading choice", + "8 + best collision", + "9 + truth pT coordinates only", + "10 + truth event coordinates (vtxZ, mult)", + "11 + truth delta eta only", + "12 + truth delta phi and truth angular range", + "13 + unit pair weight", + "14 + one entry per truth pair", + "15 exact old Final reference"}; + auto recVariants = histos.get(HIST("PairLossK0/Comparison/RecVariants")); + for (size_t i = 0; i < variantLabels.size(); ++i) { + recVariants->GetAxis(0)->SetBinLabel(i + 1, variantLabels[i].data()); + } + + // Pair variant -> trigger variant pairing for per-trigger yields: + // pair 0-3 with trigger 0-1, pair 4-6 with trigger 2, pair 7 with + // trigger 3, pair 8-9 with trigger 4, pair 10-12 with trigger 6, + // pair 13-14 with trigger 7-8, pair 15 with trigger 9. + const std::array triggerVariantLabels = { + "0 exact Rec trigger", + "1 + truth-trigger match/acceptance", + "2 + truth-trigger primary", + "3 + old truth-leading choice", + "4 + best collision", + "5 + truth pT coordinate only", + "6 + truth event coordinates (vtxZ, mult)", + "7 + unit trigger weight", + "8 + one entry per truth trigger", + "9 exact old Final trigger reference"}; + auto triggerVariants = histos.get(HIST("PairLossK0/Comparison/TriggerVariants")); + for (size_t i = 0; i < triggerVariantLabels.size(); ++i) { + triggerVariants->GetAxis(0)->SetBinLabel(i + 1, triggerVariantLabels[i].data()); + } + + // Where the Rec triggers lost at the variant 0 -> 1 step fail, per trigger and per + // pair, with the weights and reconstructed coordinates of the variants: the All and + // Matched bins of hTrigger integrate to TriggerVariants 0 and 1, those of hPair to + // RecVariants 0 and 1. hPair keeps the angular axes so that the shape of the lost + // pairs (correlated or flat) is visible. + const AxisSpec axisPairLossTriggerMatchQA{PairLossTriggerMatchNBins, -0.5, static_cast(PairLossTriggerMatchNBins) - 0.5, "Rec variant 0 #rightarrow 1 trigger match"}; + histos.add("PairLossK0/Comparison/TriggerMatchQA/hTrigger", "Rec triggers by first failing truth-trigger condition;Rec variant 0 #rightarrow 1 trigger match;#it{p}_{T}^{trigger, reco} (GeV/#it{c})", kTH2D, {axisPairLossTriggerMatchQA, axesConfigurations.axisPtQA}); + histos.add("PairLossK0/Comparison/TriggerMatchQA/hPair", "Rec peak-window h-K0 pairs by first failing truth-trigger condition of their trigger", kTHnF, {axisPairLossTriggerMatchQA, axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim}); + auto triggerMatchQATrigger = histos.get(HIST("PairLossK0/Comparison/TriggerMatchQA/hTrigger")); + auto triggerMatchQAPair = histos.get(HIST("PairLossK0/Comparison/TriggerMatchQA/hPair")); + for (int i = 0; i < PairLossTriggerMatchNBins; ++i) { + triggerMatchQATrigger->GetXaxis()->SetBinLabel(i + 1, PairLossTriggerMatchQANames[i].data()); + triggerMatchQAPair->GetAxis(0)->SetBinLabel(i + 1, PairLossTriggerMatchQANames[i].data()); + } } } @@ -2881,6 +3205,13 @@ struct HStrangeCorrelation { validCollisions[i].reserve(masterConfigurations.mixingParameter); } } + if (masterConfigurations.mixingEventForClosureTest) { + genLevelCollisions.resize(histos.get(HIST("axes/hMultAxis"))->GetNbinsX() * + histos.get(HIST("axes/hVertexZAxis"))->GetNbinsX()); + for (size_t i = 0; i < genLevelCollisions.size(); ++i) { + genLevelCollisions[i].reserve(masterConfigurations.mixingParameter); + } + } if (!masterConfigurations.doPPAnalysis) { // event selections in Pb-Pb histos.add("hEventSelection", "hEventSelection", kTH1F, {{11, 0, 11}}); @@ -3170,6 +3501,9 @@ struct HStrangeCorrelation { for (int i = 0; i < AssocParticleTypes; i++) { if (TESTBIT(doCorrelation, i)) { histos.add(fmt::format("ClosureTest/sameEvent/{}", Particlenames[i]).c_str(), "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + if (masterConfigurations.mixingEventForClosureTest) { + histos.add(fmt::format("ClosureTest/mixedEvent/{}", Particlenames[i]).c_str(), "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + } if (masterConfigurations.doClosureTestTriggerWithRecoTrackMatch) { histos.add(fmt::format("ClosureTest/TriggerWithRecoTrackMatch/sameEvent/{}", Particlenames[i]).c_str(), "truth pairs whose trigger has at least one reconstructed track with a matching MC label", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); } @@ -3186,7 +3520,7 @@ struct HStrangeCorrelation { histos.add(fmt::format("ClosureTest/h{}", Particlenames[i]).c_str(), "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi}); } } - histos.add("ClosureTest/hTrigger", "Trigger Tracks", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + histos.add("ClosureTest/hTrigger", "Trigger Tracks", kTHnF, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult, axesConfigurations.axisPhi}); if (masterConfigurations.doClosureTestTriggerWithRecoTrackMatch) { histos.add("ClosureTest/TriggerWithRecoTrackMatch/hTrigger", "Truth triggers with at least one reconstructed track with a matching MC label;#it{p}_{T}^{truth} (GeV/c);#eta^{truth};centrality (%)", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); } @@ -3209,7 +3543,7 @@ struct HStrangeCorrelation { } if (doprocessPrediction) { mCounter.mPdgDatabase = pdgDB.service; - mCounter.mSelectPrimaries = doAssocPhysicalPrimary.value; + mCounter.mSelectPrimaries = masterConfigurations.doAssocPhysicalPrimary.value; histos.add("Prediction/hEventSelection", "hEventSelection", kTH1F, {{3, 0, 3}}); std::array eventSelLabel = {"Read", "INELgt0", "|Z|<10"}; for (int i = 1; i <= histos.get(HIST("Prediction/hEventSelection"))->GetNbinsX(); i++) { @@ -3266,9 +3600,91 @@ struct HStrangeCorrelation { } } + // INEL>N at reconstructed level: more than N PV contributors in |eta| < 1. + // multNTracksPVeta1 is exactly this count (filled by multcenttable), so this is + // collision.isInelGt0() / isInelGt1() with a free N and needs no track table + template + bool isInelGtNReco(TCollision const& collision, int threshold) + { + return collision.multNTracksPVeta1() > threshold; + } + + // Number of charged physical primaries in |eta| < 1 at generated level, + // counted the same way as multcenttable fills multMCNParticlesEta10 + template + int countNchGenEta1(TMcParticles const& mcParticles) + { + constexpr double ChargeTolerance = 1e-3; // |charge| below this counts as neutral + int nChEta1 = 0; + for (auto const& mcParticle : mcParticles) { + if (!mcParticle.isPhysicalPrimary()) { + continue; + } + auto const* pdgParticle = pdgDB->GetParticle(mcParticle.pdgCode()); + if (pdgParticle == nullptr || std::abs(pdgParticle->Charge()) < ChargeTolerance) { + continue; + } + if (std::abs(mcParticle.eta()) < 1.0f) { + ++nChEta1; + } + } + return nChEta1; + } + + // INEL>N at generated level: more than N charged physical primaries in |eta| < 1 + template + bool isInelGtNGen(TMcParticles const& mcParticles, int threshold) + { + return countNchGenEta1(mcParticles) > threshold; + } + + // reconstructed INEL>0 / INEL>N cuts of the pp event selection + template + bool isCollisionInelSelected(TCollision const& collision) + { + if (!collision.isInelGt0() && masterConfigurations.selectINELgtZERO) { + return false; + } + if (!isInelGtNReco(collision, masterConfigurations.inelGtNThreshold) && masterConfigurations.selectINELgtN) { + return false; + } + return true; + } + + // generated-level INEL>0 of the PairLoss Comparison stages with + // applyINELSelectionAtGenLevel: Nch^gen (countNchGenEta1) >= 1 + static bool isGenInelGt0(int nChGen) + { + return nChGen > 0; + } + + // MC-only event quality cut of the PairLoss Comparison stages (applyNchResponseBandCut): + // multNTracksPVeta1 inside mu(Nch^gen) +- nSigma * max(sigma(Nch^gen), sigmaMin), with mu + // a polynomial in Nch^gen and sigma one in sqrt(Nch^gen). Evaluated here rather than + // through TF1 members: the task struct is at the StructToTuple member limit + bool isInNchResponseBand(int nChGen, int nTracksPVeta1) + { + const double sqrtNchGen = std::sqrt(static_cast(std::max(nChGen, 0))); + double mean = 0.0; + double power = 1.0; + for (const double par : pairLossK0Configurations.nchResponseBandMeanPars.value) { + mean += par * power; + power *= nChGen; + } + double sigma = 0.0; + power = 1.0; + for (const double par : pairLossK0Configurations.nchResponseBandSigmaPars.value) { + sigma += par * power; + power *= sqrtNchGen; + } + sigma = std::max(sigma, static_cast(pairLossK0Configurations.nchResponseBandSigmaMin)); + return std::abs(nTracksPVeta1 - mean) <= pairLossK0Configurations.nchResponseBandNSigma * sigma; + } + // this function allows for all event selections to be done in a modular way + // applyInelSelection = false skips only the reconstructed INEL>0 / INEL>N cuts template - bool isCollisionSelected(TCollision const& collision) + bool isCollisionSelected(TCollision const& collision, bool applyInelSelection = true) { // ________________________________________________ // Perform basic event selection @@ -3285,10 +3701,7 @@ struct HStrangeCorrelation { if (collision.centFT0M() > axisRanges[5][1] || collision.centFT0M() < axisRanges[5][0]) { return false; } - if (!collision.isInelGt0() && masterConfigurations.selectINELgtZERO) { - return false; - } - if (!collision.isInelGt1() && masterConfigurations.selectINELgtONE) { + if (applyInelSelection && !isCollisionInelSelected(collision)) { return false; } if (!collision.selection_bit(aod::evsel::kIsGoodITSLayersAll) && masterConfigurations.requireAllGoodITSLayers) { @@ -3303,7 +3716,7 @@ struct HStrangeCorrelation { // rejects collisions which are associated with the same "found-by-T0" bunch crossing return false; } - if (zorroMask.value != "") { + if (!zorroMask.value.empty()) { auto bc = collision.template bc_as(); initZorro(bc); bool zorroSelected = zorro.isSelected(collision.template bc_as().globalBC()); /// Just let Zorro do the accounting @@ -3412,6 +3825,17 @@ struct HStrangeCorrelation { return true; } + // event selection of runSameEventHV0s before its INEL cut: trigger presence + + // isCollisionSelected (pp) or isCollisionSelectedPbPb, without filling hEventSelection + template + bool isCollisionSelectedNoInel(TCollision const& collision) + { + if (!triggerPresenceMap.empty() && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + return false; + } + return masterConfigurations.doPPAnalysis ? isCollisionSelected(collision, false) : isCollisionSelectedPbPb(collision, false); + } + double getMagneticField(uint64_t timestamp) { static parameters::GRPMagField* grpo = nullptr; @@ -3458,8 +3882,12 @@ struct HStrangeCorrelation { for (auto const& collision : collisions) { // ________________________________________________ - // Perform basic event selection - if (!isCollisionSelected(collision)) { + // Perform basic event selection. The INEL cuts are left to the consumers of + // triggerPresenceMap, which apply their own event selection afterwards, so that + // a generated-level INEL selection (applyINELSelectionAtGenLevel) is not + // overridden. Only the generated-level closure paths with applyNewMCSelection + // and doGenEventSelection both false have no event selection of their own + if (!isCollisionSelected(collision, false)) { continue; } @@ -3484,11 +3912,11 @@ struct HStrangeCorrelation { aod::AssocHadrons const& assocHadrons, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + if (!triggerPresenceMap.empty() && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { return; } @@ -3564,7 +3992,7 @@ struct HStrangeCorrelation { float weight = efficiencyFlags.applyEfficiencyCorrection ? purity / efficiency : 1.0f; histos.fill(HIST("hAssocHadronsAllSelectedEtaVsPt"), assoc.pt(), assoc.eta(), collision.centFT0M(), weight); histos.fill(HIST("hAssocPtResolution"), assoc.pt(), assocTrack.mcOriginalPt()); - if (doAssocPhysicalPrimary && !assocTrack.mcPhysicalPrimary()) { + if (masterConfigurations.doAssocPhysicalPrimary && !assocTrack.mcPhysicalPrimary()) { continue; } histos.fill(HIST("hAssocPrimaryEtaVsPt"), assoc.pt(), assoc.eta(), collision.centFT0M()); @@ -3584,8 +4012,8 @@ struct HStrangeCorrelation { std::variant colBinning = masterConfigurations.doPPAnalysis ? std::variant{ - BinningTypePP{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}} - : std::variant{BinningTypePbPb{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}}; + BinningTypePP{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}} + : std::variant{BinningTypePbPb{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}}; if (!doprocessSameEventHCascades && doMixingQAandEventQA) { std::visit([&](auto const& binning) { @@ -3616,7 +4044,7 @@ struct HStrangeCorrelation { //---] track quality check [--- auto postrack = v0Data.posTrack_as(); auto negtrack = v0Data.negTrack_as(); - if (postrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || negtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { + if (!postrack.hasTPC() || postrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || !negtrack.hasTPC() || negtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { continue; } if (trackSelection.checksRequireTPCChi2 && (postrack.tpcChi2NCl() > trackSelection.maxTPCChi2PerClusterAssociated || negtrack.tpcChi2NCl() > trackSelection.maxTPCChi2PerClusterAssociated)) { @@ -3654,7 +4082,7 @@ struct HStrangeCorrelation { efficiency = 1; } float weight = efficiencyFlags.applyEfficiencyCorrection ? 1. / efficiency : 1.0f; - if (v0.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || v0.mcTrue(Index)) && (!doAssocPhysicalPrimary || v0.mcPhysicalPrimary()) && (!efficiencyFlags.applyEfficiencyCorrection || efficiency != 0)) { + if (v0.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || v0.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || v0.mcPhysicalPrimary()) && (!efficiencyFlags.applyEfficiencyCorrection || efficiency != 0)) { if ((TESTBIT(doCorrelation, Index)) && (masterConfigurations.doPPAnalysis || (TESTBIT(selMap, Index) && TESTBIT(selMap, Index + 3)))) { histos.fill(HIST("h3d") + HIST(V0names[Index]) + HIST("Spectrum"), v0Data.pt(), cent, v0.invMassNSigma(Index), weight); if (masterConfigurations.doMassSpectrumCheck) { @@ -3724,19 +4152,25 @@ struct HStrangeCorrelation { } template - void runSameEventHV0s(TCollision const& collision, aod::AssocV0s const& associatedV0s, aod::TriggerTracks const& triggerTracks, TracksComplete const& tracks) + void runSameEventHV0s(TCollision const& collision, aod::AssocV0s const& associatedV0s, aod::TriggerTracks const& triggerTracks, TracksComplete const& tracks, + std::optional nChGenForInelSelection = std::nullopt) { const float cent = masterConfigurations.doPPAnalysis ? collision.centFT0M() : collision.centFT0C(); // Keep the trigger-presence and reconstructed-event decisions identical - // for the ordinary Rec process and for its PairLoss replica. - if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + // for the ordinary Rec process and for its PairLoss replica. When + // nChGenForInelSelection is given (PairLoss replica only), the reconstructed + // INEL cuts are replaced by Nch^gen >= 1 of that generated multiplicity + if (!triggerPresenceMap.empty() && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { return; } - if ((masterConfigurations.doPPAnalysis && !isCollisionSelected(collision)) || + if ((masterConfigurations.doPPAnalysis && !isCollisionSelected(collision, !nChGenForInelSelection.has_value())) || (!masterConfigurations.doPPAnalysis && !isCollisionSelectedPbPb(collision, true))) { return; } + if (nChGenForInelSelection.has_value() && !isGenInelGt0(*nChGenForInelSelection)) { + return; + } auto bc = collision.template bc_as(); const auto bField = getMagneticField(bc.timestamp()); @@ -3747,10 +4181,15 @@ struct HStrangeCorrelation { collision.posX(), collision.posY(), collision.posZ(), cent, collision.multNTracksPVeta1(), bField); } - void processSameEventHV0s(soa::Join::iterator const& collision, + void processSameEventHV0s(soa::Join::iterator const& collision, aod::AssocV0s const& associatedV0s, aod::TriggerTracks const& triggerTracks, V0DatasWithoutTrackX const&, TracksComplete const& tracks, aod::BCsWithTimestamps const&) { + // filled here and not in runSameEventHV0s, which the PairLoss Rec replica also runs + if (masterConfigurations.doMultEstimatorResponse && isCollisionSelectedNoInel(collision)) { + histos.fill(HIST("MultEstimatorResponse/hNTracksPVeta1VsCentFT0M"), collision.multNTracksPVeta1(), collision.centFT0M()); + histos.fill(HIST("MultEstimatorResponse/hNTracksPVeta1VsMultFT0M"), collision.multNTracksPVeta1(), collision.multFT0M()); + } runSameEventHV0s(collision, associatedV0s, triggerTracks, tracks); } @@ -3761,13 +4200,13 @@ struct HStrangeCorrelation { std::variant colBinning = masterConfigurations.doPPAnalysis ? std::variant{ - BinningTypePP{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}} - : std::variant{BinningTypePbPb{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}}; + BinningTypePP{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}} + : std::variant{BinningTypePbPb{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}}; double cent = masterConfigurations.doPPAnalysis ? collision.centFT0M() : collision.centFT0C(); // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + if (!triggerPresenceMap.empty() && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { return; } @@ -3830,7 +4269,7 @@ struct HStrangeCorrelation { auto postrack = cascData.posTrack_as(); auto negtrack = cascData.negTrack_as(); auto bachtrack = cascData.bachelor_as(); - if (postrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || negtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || bachtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { + if (!postrack.hasTPC() || postrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || !negtrack.hasTPC() || negtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || !bachtrack.hasTPC() || bachtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { continue; } if (trackSelection.checksRequireTPCChi2 && (postrack.tpcChi2NCl() > trackSelection.maxTPCChi2PerClusterAssociated || negtrack.tpcChi2NCl() > trackSelection.maxTPCChi2PerClusterAssociated || bachtrack.tpcChi2NCl() > trackSelection.maxTPCChi2PerClusterAssociated)) { @@ -3858,7 +4297,7 @@ struct HStrangeCorrelation { efficiency = 1; } float weight = efficiencyFlags.applyEfficiencyCorrection ? 1. / efficiency : 1.0f; - if (casc.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || casc.mcTrue(Index)) && (!doAssocPhysicalPrimary || casc.mcPhysicalPrimary()) && (!efficiencyFlags.applyEfficiencyCorrection || efficiency != 0)) { + if (casc.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || casc.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || casc.mcPhysicalPrimary()) && (!efficiencyFlags.applyEfficiencyCorrection || efficiency != 0)) { if (TESTBIT(doCorrelation, Index + 3) && (masterConfigurations.doPPAnalysis || (TESTBIT(cascselMap, Index) && TESTBIT(cascselMap, Index + 4) && TESTBIT(cascselMap, Index + 8) && TESTBIT(cascselMap, Index + 12)))) { histos.fill(HIST("h3d") + HIST(Cascadenames[Index]) + HIST("Spectrum"), cascData.pt(), cent, casc.invMassNSigma(Index), weight); if (std::abs(cascData.rapidity(Index)) < ySel) { @@ -3919,10 +4358,10 @@ struct HStrangeCorrelation { soa::Join const& associatedPions, soa::Join const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + if (!triggerPresenceMap.empty() && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { return; } auto bc = collision.bc_as(); @@ -3954,7 +4393,7 @@ struct HStrangeCorrelation { } histos.fill(HIST("hPionEtaVsPtAllSelected"), pionTrack.pt(), pionTrack.eta(), collision.centFT0M()); - if (doAssocPhysicalPrimary && !pion.mcPhysicalPrimary()) { + if (masterConfigurations.doAssocPhysicalPrimary && !pion.mcPhysicalPrimary()) { continue; } if (masterConfigurations.doMCassociation && std::abs(pion.pdgCode()) != PdgCodes[IndexPion]) { @@ -3997,7 +4436,7 @@ struct HStrangeCorrelation { aod::AssocHadrons const& assocHadrons, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { auto bc = collision1.bc_as(); auto bField = getMagneticField(bc.timestamp()); @@ -4007,7 +4446,7 @@ struct HStrangeCorrelation { } // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { + if (!triggerPresenceMap.empty() && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { return; } @@ -4049,8 +4488,8 @@ struct HStrangeCorrelation { std::variant colBinning = masterConfigurations.doPPAnalysis ? std::variant{ - BinningTypePP{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}} - : std::variant{BinningTypePbPb{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}}; + BinningTypePP{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}} + : std::variant{BinningTypePbPb{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}}; std::visit([&](auto const& binning) { for (auto const& [collision1, collision2] : soa::selfCombinations(binning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { @@ -4064,7 +4503,7 @@ struct HStrangeCorrelation { } // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { + if (!triggerPresenceMap.empty() && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { continue; } @@ -4108,8 +4547,8 @@ struct HStrangeCorrelation { std::variant colBinning = masterConfigurations.doPPAnalysis ? std::variant{ - BinningTypePP{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}} - : std::variant{BinningTypePbPb{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}}; + BinningTypePP{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}} + : std::variant{BinningTypePbPb{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}}; std::visit([&](auto const& binning) { for (auto const& [collision1, collision2] : soa::selfCombinations(binning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { @@ -4123,7 +4562,7 @@ struct HStrangeCorrelation { } // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { + if (!triggerPresenceMap.empty() && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { continue; } @@ -4163,7 +4602,7 @@ struct HStrangeCorrelation { soa::Join const& assocPions, soa::Join const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { auto bc = collision1.bc_as(); auto bField = getMagneticField(bc.timestamp()); @@ -4173,7 +4612,7 @@ struct HStrangeCorrelation { } // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { + if (!triggerPresenceMap.empty() && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { continue; } @@ -4224,7 +4663,7 @@ struct HStrangeCorrelation { } } - if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (!masterConfigurations.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { if (mcParticle.pdgCode() == PDG_t::kK0Short && masterConfigurations.doCorrelationK0Short) { histos.fill(HIST("hGeneratedQAPtAssociatedK0"), gpt, 0.0f); // step 1: before all selections } @@ -4232,7 +4671,7 @@ struct HStrangeCorrelation { } for (auto const& mcParticle : mcParticles) { - if (doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { + if (masterConfigurations.doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { continue; } static_for<0, 7>([&](auto i) { @@ -4262,7 +4701,7 @@ struct HStrangeCorrelation { float bestCollisionVtxZ = 0.0f; bool bestCollisionSel8 = false; bool bestCollisionINELgtZERO = false; - bool bestCollisionINELgtONE = false; + bool bestCollisionINELgtN = false; bool bestCollisionNoSameBunchPileup = false; bool bestCollisionGoodTriggerTVX = false; bool bestCollisionGoodZvtxFT0vsPV = false; @@ -4281,12 +4720,12 @@ struct HStrangeCorrelation { bestCollisionSel8 = collision.sel8(); bestCollisionVtxZ = collision.posZ(); bestCollisionINELgtZERO = collision.isInelGt0(); - bestCollisionINELgtONE = collision.isInelGt1(); + bestCollisionINELgtN = isInelGtNReco(collision, masterConfigurations.inelGtNThreshold); bestCollisionNoSameBunchPileup = collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup); bestCollisionGoodTriggerTVX = collision.selection_bit(aod::evsel::kIsTriggerTVX); bestCollisionGoodZvtxFT0vsPV = collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV); } - if (triggerPresenceMap.size() > 0) { + if (!triggerPresenceMap.empty()) { bestCollisionTriggerPresenceMap = triggerPresenceMap[collision.globalIndex()]; } } @@ -4294,7 +4733,7 @@ struct HStrangeCorrelation { if (collisions.size() > 1) { for (auto const& mcParticle : mcParticles) { - if (doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { + if (masterConfigurations.doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { continue; } if (std::abs(mcParticle.y()) > ySel) { @@ -4316,7 +4755,7 @@ struct HStrangeCorrelation { // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(bestCollisionTriggerPresenceMap, triggerBinToSelect)) { + if (!triggerPresenceMap.empty() && !TESTBIT(bestCollisionTriggerPresenceMap, triggerBinToSelect)) { return; } if (masterConfigurations.applyNewMCSelection) { @@ -4333,7 +4772,7 @@ struct HStrangeCorrelation { if (!bestCollisionINELgtZERO) { return; } - if (masterConfigurations.selectINELgtONE && !bestCollisionINELgtONE) { + if (masterConfigurations.selectINELgtN && !bestCollisionINELgtN) { return; } if (masterConfigurations.rejectSameBunchPileup && !bestCollisionNoSameBunchPileup) { @@ -4361,7 +4800,7 @@ struct HStrangeCorrelation { } } - if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (!masterConfigurations.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { if (mcParticle.pdgCode() == PDG_t::kK0Short && masterConfigurations.doCorrelationK0Short) { histos.fill(HIST("hGeneratedQAPtAssociatedK0"), gpt, 1.0f); // step 2: before all selections } @@ -4373,7 +4812,7 @@ struct HStrangeCorrelation { const auto bestCollisionTracks = tracks.sliceBy(pairLossTracksPerCollision, bestCollisionId); for (auto const& mcParticle : mcParticles) { - if (doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { + if (masterConfigurations.doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { continue; } double geta = mcParticle.eta(); @@ -4394,7 +4833,7 @@ struct HStrangeCorrelation { } } - if (mcParticle.pdgCode() == PDG_t::kLambda0 && !doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { + if (mcParticle.pdgCode() == PDG_t::kLambda0 && !masterConfigurations.doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { if (std::abs(geta) > etaSel) { continue; } @@ -4415,7 +4854,7 @@ struct HStrangeCorrelation { } } } - if (mcParticle.pdgCode() == PDG_t::kLambda0Bar && !doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { + if (mcParticle.pdgCode() == PDG_t::kLambda0Bar && !masterConfigurations.doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { if (std::abs(geta) > etaSel) { continue; } @@ -4460,7 +4899,7 @@ struct HStrangeCorrelation { } } void processPairLossK0MC(aod::McCollision const& mcCollision, - soa::SmallGroups> const& recCollisions, + soa::SmallGroups> const& recCollisions, aod::McParticles const& mcParticles, aod::McTrackLabels const& trackLabels, aod::V0MCCores const& v0MCCores, @@ -4470,11 +4909,18 @@ struct HStrangeCorrelation { V0DatasWithoutTrackX const& v0Candidates, TracksComplete const& tracks) { + // Nch^gen of this MC collision (charged primaries in |eta| < 1), shared by the + // event-reconstruction-efficiency and PV-quality studies, the generated-level INEL + // selection and the Nch^gen vs multNTracksPVeta1 band cut + const bool applyGenInel = pairLossK0Configurations.applyINELSelectionAtGenLevel; + const bool applyBandCut = pairLossK0Configurations.applyNchResponseBandCut; + const int nChGen = (pairLossK0Configurations.doEventRecoEfficiencyStudy || pairLossK0Configurations.doPVQualityStudy || applyGenInel || applyBandCut) ? countNchGenEta1(mcParticles) : -1; + // Part 3: a self-contained generator-level study. It deliberately touches no // reconstructed quantity in its event selection or in any of its axes: the - // event is selected on generated INEL>0 and the generated vertex only, the - // multiplicity is counted from generated particles, and every object is - // filled with generated coordinates. + // event is selected on generated INEL>0 (INEL>N if enabled) and the generated + // vertex only, the multiplicity is counted from generated particles, and every + // object is filled with generated coordinates. // // Reconstruction enters in exactly one place -- whether a generated object // has a reconstructed counterpart at all -- and that splits the very same @@ -4495,9 +4941,9 @@ struct HStrangeCorrelation { histos.fill(HIST("PairLossK0/GenStudy/hEventCounter"), 0.0f); // Generated-level event selection. No reconstructed variable is used. - // INEL>1 implies INEL>0, so only the tighter enabled selection has to be evaluated - if (masterConfigurations.selectINELgtONE) { - if (!o2::pwglf::isINELgt1mc(mcParticles, pdgDB)) { + // INEL>N (N >= 0) implies INEL>0, so only the tighter enabled selection has to be evaluated + if (masterConfigurations.selectINELgtN) { + if (!isInelGtNGen(mcParticles, masterConfigurations.inelGtNThreshold)) { return; } } else if (masterConfigurations.selectINELgtZERO) { @@ -4521,7 +4967,7 @@ struct HStrangeCorrelation { // // Deliberately a local counter rather than a member: the shared mCounter // only gets its PDG database wired up when processPrediction runs, and its - // mSelectPrimaries follows doAssocPhysicalPrimary. Adding a second counter + // mSelectPrimaries follows masterConfigurations.doAssocPhysicalPrimary. Adding a second counter // as a task member is not an option either -- the struct is already at the // member limit that Framework/StructToTuple.h can destructure. The object // is a bool and a pointer, so building it per MC collision costs nothing. @@ -4574,9 +5020,9 @@ struct HStrangeCorrelation { auto const* pdgParticle = pdgDB->GetParticle(mcParticle.pdgCode()); const double charge = pdgParticle != nullptr ? pdgParticle->Charge() : 0.0; const int sign = charge > 0.0 ? 1 : (charge < 0.0 ? -1 : 0); - if (!((triggerParticleCharge > 0 && sign < 0) || (triggerParticleCharge < 0 && sign > 0) || sign == 0)) { + if ((triggerParticleCharge <= 0 || sign >= 0) && (triggerParticleCharge >= 0 || sign <= 0) && sign != 0) { histos.fill(HIST("PairLossK0/GenStudy/Gen/hTrigger"), genPt, genEta, genPhi, generatedNch); - if (reconstructedTrackMcIds.count(mcParticle.globalIndex()) > 0) { + if (reconstructedTrackMcIds.contains(mcParticle.globalIndex())) { histos.fill(HIST("PairLossK0/GenStudy/Reconstructed/hTrigger"), genPt, genEta, genPhi, generatedNch); } else { histos.fill(HIST("PairLossK0/GenStudy/NotReconstructed/hTrigger"), genPt, genEta, genPhi, generatedNch); @@ -4585,15 +5031,15 @@ struct HStrangeCorrelation { .pt = genPt, .eta = genEta, .phi = genPhi, - .globalIndex = static_cast(mcParticle.globalIndex()), - .motherIndex = mcParticle.has_mothers() ? static_cast(mcParticle.mothers_first_as().globalIndex()) : -1, - .reconstructed = reconstructedTrackMcIds.count(mcParticle.globalIndex()) > 0}); + .globalIndex = mcParticle.globalIndex(), + .motherIndex = mcParticle.has_mothers() ? mcParticle.mothers_first_as().globalIndex() : -1, + .reconstructed = reconstructedTrackMcIds.contains(mcParticle.globalIndex())}); } } if (mcParticle.pdgCode() == PDG_t::kK0Short && genPt >= axisRanges[2][0] && genPt <= axisRanges[2][1] && - (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary())) { + (!masterConfigurations.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary())) { // Same findability definition the stage ladder uses: decayed to // pi+ pi- with both charged daughters inside the tracking acceptance. // A K0 that is not findable could never have been reconstructed, so @@ -4620,7 +5066,7 @@ struct HStrangeCorrelation { const float k0Findable = (hasPositiveDaughter && hasNegativeDaughter && daughtersInAcceptance) ? 1.0f : 0.0f; histos.fill(HIST("PairLossK0/GenStudy/Gen/hK0Short"), genPt, genEta, genPhi, generatedNch, k0Findable); - if (reconstructedV0McIds.count(mcParticle.globalIndex()) > 0) { + if (reconstructedV0McIds.contains(mcParticle.globalIndex())) { histos.fill(HIST("PairLossK0/GenStudy/Reconstructed/hK0Short"), genPt, genEta, genPhi, generatedNch, k0Findable); } else { histos.fill(HIST("PairLossK0/GenStudy/NotReconstructed/hK0Short"), genPt, genEta, genPhi, generatedNch, k0Findable); @@ -4629,9 +5075,9 @@ struct HStrangeCorrelation { .pt = genPt, .eta = genEta, .phi = genPhi, - .globalIndex = static_cast(mcParticle.globalIndex()), + .globalIndex = mcParticle.globalIndex(), .motherIndex = -1, - .reconstructed = reconstructedV0McIds.count(mcParticle.globalIndex()) > 0}); + .reconstructed = reconstructedV0McIds.contains(mcParticle.globalIndex())}); } } @@ -4685,8 +5131,13 @@ struct HStrangeCorrelation { bestCollisionVtxY = collision.posY(); bestCollisionVtxZ = collision.posZ(); bestCollisionMultiplicity = masterConfigurations.doPPAnalysis ? collision.centFT0M() : collision.centFT0C(); - pairLossEventSelected = !pairLossK0Configurations.applyRecoEventSelection || - (masterConfigurations.doPPAnalysis ? isCollisionSelected(collision) : isCollisionSelectedPbPb(collision, false)); + // with applyGenInel (pp only) the reconstructed INEL cuts are replaced by + // Nch^gen >= 1, as in the Rec and Gen stages; the band cut acts on top, + // independently of applyRecoEventSelection + pairLossEventSelected = (!pairLossK0Configurations.applyRecoEventSelection || + (masterConfigurations.doPPAnalysis ? isCollisionSelected(collision, !applyGenInel) && (!applyGenInel || isGenInelGt0(nChGen)) + : isCollisionSelectedPbPb(collision, false))) && + (!applyBandCut || isInNchResponseBand(nChGen, collision.multNTracksPVeta1())); } if (bestCollisionId < 0) { return; @@ -4699,20 +5150,16 @@ struct HStrangeCorrelation { std::vector comparisonTruthTriggers; std::vector comparisonTruthK0s; for (auto const& mcParticle : mcParticles) { - if (isPairLossTriggerPdg(mcParticle.pdgCode()) && std::abs(mcParticle.eta()) <= etaSel && - mcParticle.pt() >= axisRanges[3][0] && mcParticle.pt() <= axisRanges[3][1]) { - auto const* pdgParticle = pdgDB->GetParticle(mcParticle.pdgCode()); - const double charge = pdgParticle != nullptr ? pdgParticle->Charge() : 0.0; - const int sign = charge > 0.0 ? 1 : (charge < 0.0 ? -1 : 0); - if (!((triggerParticleCharge > 0 && sign < 0) || (triggerParticleCharge < 0 && sign > 0) || sign == 0)) { - comparisonTruthTriggers.push_back(PairLossTruthTrackInfo{ - .globalIndex = static_cast(mcParticle.globalIndex()), - .pt = mcParticle.pt(), - .eta = mcParticle.eta(), - .phi = mcParticle.phi(), - .sign = sign, - .physicalPrimary = mcParticle.isPhysicalPrimary()}); - } + // same acceptance as the TriggerMatchQA rejection bins + int sign = 0; + if (pairLossTruthTriggerRejection(mcParticle, sign) == PairLossTriggerMatchMatched) { + comparisonTruthTriggers.push_back(PairLossTruthTrackInfo{ + .globalIndex = mcParticle.globalIndex(), + .pt = mcParticle.pt(), + .eta = mcParticle.eta(), + .phi = mcParticle.phi(), + .sign = sign, + .physicalPrimary = mcParticle.isPhysicalPrimary()}); } if (mcParticle.pdgCode() != PDG_t::kK0Short || std::abs(mcParticle.eta()) > etaSel || @@ -4720,7 +5167,7 @@ struct HStrangeCorrelation { continue; } PairLossTruthK0Info truthK0{ - .globalIndex = static_cast(mcParticle.globalIndex()), + .globalIndex = mcParticle.globalIndex(), .pt = mcParticle.pt(), .eta = mcParticle.eta(), .phi = mcParticle.phi(), @@ -4731,7 +5178,7 @@ struct HStrangeCorrelation { .negativeDaughter = {}}; for (auto const& daughter : mcParticle.daughters_as()) { PairLossTruthTrackInfo daughterInfo{ - .globalIndex = static_cast(daughter.globalIndex()), + .globalIndex = daughter.globalIndex(), .pt = daughter.pt(), .eta = daughter.eta(), .phi = daughter.phi(), @@ -4758,12 +5205,13 @@ struct HStrangeCorrelation { truthTriggers.push_back(leadingTriggerCopy); } auto truthK0s = comparisonTruthK0s; - if (doAssocPhysicalPrimary) { + if (masterConfigurations.doAssocPhysicalPrimary) { std::erase_if(truthK0s, [](auto const& k0) { return !k0.physicalPrimary; }); } // Truth: identical pair definition to the original PairLoss truth stage. - if (pairLossEventSelected) { + // Not booked with applyGenInel, like the rest of the ladder except Rec and Gen. + if (pairLossEventSelected && !applyGenInel) { for (auto const& truthTrigger : truthTriggers) { for (auto const& truthK0 : truthK0s) { if (truthTrigger.globalIndex == truthK0.positiveDaughter.globalIndex || truthTrigger.globalIndex == truthK0.negativeDaughter.globalIndex) { @@ -4788,12 +5236,13 @@ struct HStrangeCorrelation { float genBestCollisionVtxZ = 0.0f; bool genBestCollisionSel8 = false; bool genBestCollisionINELgtZERO = false; - bool genBestCollisionINELgtONE = false; + bool genBestCollisionINELgtN = false; bool genBestCollisionNoSameBunchPileup = false; bool genBestCollisionGoodTriggerTVX = false; bool genBestCollisionGoodZvtxFT0vsPV = false; bool genCollisionSelected = false; int genLargestNContributors = -1; + int genBestCollisionNTracksPVeta1 = -1; uint32_t genBestCollisionTriggerPresenceMap = 0; for (auto const& recCollision : recCollisions) { if (genLargestNContributors >= recCollision.numContrib()) { @@ -4801,33 +5250,39 @@ struct HStrangeCorrelation { } genLargestNContributors = recCollision.numContrib(); genBestCollisionMultiplicity = masterConfigurations.doPPAnalysis ? recCollision.centFT0M() : recCollision.centFT0C(); + // Both branches fill the Gen THn with this vertex position + genBestCollisionVtxZ = recCollision.posZ(); + genBestCollisionNTracksPVeta1 = recCollision.multNTracksPVeta1(); if (masterConfigurations.applyNewMCSelection) { - genCollisionSelected = (masterConfigurations.doPPAnalysis && isCollisionSelected(recCollision)) || + genCollisionSelected = (masterConfigurations.doPPAnalysis && isCollisionSelected(recCollision, !applyGenInel)) || (!masterConfigurations.doPPAnalysis && isCollisionSelectedPbPb(recCollision, false)); } else { genBestCollisionSel8 = recCollision.sel8(); - genBestCollisionVtxZ = recCollision.posZ(); genBestCollisionINELgtZERO = recCollision.isInelGt0(); - genBestCollisionINELgtONE = recCollision.isInelGt1(); + genBestCollisionINELgtN = isInelGtNReco(recCollision, masterConfigurations.inelGtNThreshold); genBestCollisionNoSameBunchPileup = recCollision.selection_bit(o2::aod::evsel::kNoSameBunchPileup); genBestCollisionGoodTriggerTVX = recCollision.selection_bit(aod::evsel::kIsTriggerTVX); genBestCollisionGoodZvtxFT0vsPV = recCollision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV); } - if (triggerPresenceMap.size() > 0) { + if (!triggerPresenceMap.empty()) { genBestCollisionTriggerPresenceMap = triggerPresenceMap[recCollision.globalIndex()]; } } - bool genEventSelected = triggerPresenceMap.size() == 0 || TESTBIT(genBestCollisionTriggerPresenceMap, triggerBinToSelect); + bool genEventSelected = triggerPresenceMap.empty() || TESTBIT(genBestCollisionTriggerPresenceMap, triggerBinToSelect); if (masterConfigurations.applyNewMCSelection) { - genEventSelected = genEventSelected && genCollisionSelected; + genEventSelected = genEventSelected && genCollisionSelected && (!applyGenInel || isGenInelGt0(nChGen)); } else if (masterConfigurations.doGenEventSelection) { genEventSelected = genEventSelected && genBestCollisionSel8 && std::abs(genBestCollisionVtxZ) <= masterConfigurations.zVertexCut && - genBestCollisionINELgtZERO && (!masterConfigurations.selectINELgtONE || genBestCollisionINELgtONE) && + genBestCollisionINELgtZERO && (!masterConfigurations.selectINELgtN || genBestCollisionINELgtN) && (!masterConfigurations.rejectSameBunchPileup || genBestCollisionNoSameBunchPileup) && (!masterConfigurations.requireGoodTriggerTVX || genBestCollisionGoodTriggerTVX) && (!masterConfigurations.requireGoodZvtxFT0vsPV || genBestCollisionGoodZvtxFT0vsPV) && genBestCollisionMultiplicity >= axisRanges[5][0] && genBestCollisionMultiplicity <= axisRanges[5][1]; } + // the band cut acts on top of either Gen event selection, on the best collision + if (applyBandCut) { + genEventSelected = genEventSelected && isInNchResponseBand(nChGen, genBestCollisionNTracksPVeta1); + } if (genEventSelected && masterConfigurations.doCorrelationK0Short && TESTBIT(doCorrelation, IndexK0)) { std::vector genTriggerIndices; @@ -4841,7 +5296,7 @@ struct HStrangeCorrelation { if (isPairLossTriggerPdg(mcParticle.pdgCode()) && (!masterConfigurations.doTriggPhysicalPrimary || mcParticle.isPhysicalPrimary())) { genTriggerIndices.push_back(iteratorNumber); } - if (mcParticle.pdgCode() == PDG_t::kK0Short && masterConfigurations.doCorrelationK0Short && (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary())) { + if (mcParticle.pdgCode() == PDG_t::kK0Short && masterConfigurations.doCorrelationK0Short && (!masterConfigurations.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary())) { genK0Indices.push_back(iteratorNumber); } } @@ -4850,6 +5305,9 @@ struct HStrangeCorrelation { if (triggerParticle.pt() > axisRanges[3][1] || triggerParticle.pt() < axisRanges[3][0]) { continue; } + if (applyGenInel) { + histos.fill(HIST("PairLossK0/Comparison/NchGen/TriggerGen"), triggerParticle.pt(), nChGen); + } auto const& triggerMother = triggerParticle.mothers_first_as(); const auto triggerMotherIndex = triggerMother.globalIndex(); for (auto const& k0Index : genK0Indices) { @@ -4863,7 +5321,11 @@ struct HStrangeCorrelation { k0Particle.pt() < axisRanges[2][0] || k0Particle.pt() > axisRanges[2][1]) { continue; } - histos.fill(HIST("PairLossK0/Comparison/Gen"), deltaPhi, deltaEta, k0Particle.pt(), triggerParticle.pt(), genBestCollisionVtxZ, genBestCollisionMultiplicity); + if (applyGenInel) { + histos.fill(HIST("PairLossK0/Comparison/NchGen/Gen"), deltaPhi, deltaEta, k0Particle.pt(), triggerParticle.pt(), nChGen); + } else { + histos.fill(HIST("PairLossK0/Comparison/Gen"), deltaPhi, deltaEta, k0Particle.pt(), triggerParticle.pt(), genBestCollisionVtxZ, genBestCollisionMultiplicity); + } } } } @@ -4916,8 +5378,8 @@ struct HStrangeCorrelation { const bool passesDaughterDcaAndArmenteros = std::abs(v0.dcapostopv()) > dcaCut && std::abs(v0.dcanegtopv()) > dcaCut && v0.qtarm() * v0Selection.armPodCut > std::abs(v0.alpha()); passesFinalSelection = v0SelectedPbPb(v0) && passesLifetime && passesDaughterDcaAndArmenteros; } - passesFinalSelection = passesFinalSelection && positiveTrack.tpcNClsCrossedRows() >= trackSelection.minTPCNCrossedRowsAssociated && - negativeTrack.tpcNClsCrossedRows() >= trackSelection.minTPCNCrossedRowsAssociated; + passesFinalSelection = passesFinalSelection && positiveTrack.hasTPC() && positiveTrack.tpcNClsCrossedRows() >= trackSelection.minTPCNCrossedRowsAssociated && + negativeTrack.hasTPC() && negativeTrack.tpcNClsCrossedRows() >= trackSelection.minTPCNCrossedRowsAssociated; if (trackSelection.checksRequireTPCChi2) { passesFinalSelection = passesFinalSelection && positiveTrack.tpcChi2NCl() <= trackSelection.maxTPCChi2PerClusterAssociated && negativeTrack.tpcChi2NCl() <= trackSelection.maxTPCChi2PerClusterAssociated; @@ -4927,7 +5389,7 @@ struct HStrangeCorrelation { negativeTrack.itsNCls() >= trackSelection.minITSClustersForDaughterTracks; } passesFinalSelection = passesFinalSelection && assocEntry.compatible(IndexK0, trackSelection.dEdxCompatibility) && - (!doAssocPhysicalPrimary || assocEntry.mcPhysicalPrimary()) && + (!masterConfigurations.doAssocPhysicalPrimary || assocEntry.mcPhysicalPrimary()) && assocEntry.invMassNSigma(IndexK0) > -massWindowConfigurations.maxPeakNSigma && assocEntry.invMassNSigma(IndexK0) < massWindowConfigurations.maxPeakNSigma && v0.pt() >= axisRanges[2][0] && v0.pt() <= axisRanges[2][1]; @@ -4935,9 +5397,9 @@ struct HStrangeCorrelation { continue; } oldFinalV0s[v0MC.particleIdMC()].push_back(PairLossV0Info{ - .globalIndex = static_cast(v0.globalIndex()), - .positiveTrackId = static_cast(positiveTrack.globalIndex()), - .negativeTrackId = static_cast(negativeTrack.globalIndex()), + .globalIndex = v0.globalIndex(), + .positiveTrackId = positiveTrack.globalIndex(), + .negativeTrackId = negativeTrack.globalIndex(), .pt = v0.pt(), .eta = v0.eta(), .phi = v0.phi(), @@ -4958,9 +5420,11 @@ struct HStrangeCorrelation { }; std::vector finalPairRecords; - if (pairLossEventSelected) { + // Not filled with applyGenInel: finalPairRecords then stays empty, so + // FinalNotInRec below is not filled either. + if (pairLossEventSelected && !applyGenInel) { for (auto const& truthTrigger : truthTriggers) { - if (oldFinalTriggers.find(truthTrigger.globalIndex) == oldFinalTriggers.end()) { + if (!oldFinalTriggers.contains(truthTrigger.globalIndex)) { continue; } histos.fill(HIST("PairLossK0/Comparison/TriggerVariants"), 9, truthTrigger.pt, bestCollisionVtxZ, bestCollisionMultiplicity); @@ -5014,8 +5478,11 @@ struct HStrangeCorrelation { histos.fill(HIST("PairLossK0/Comparison/Final"), truthDeltaPhi, truthDeltaEta, truthK0.pt, truthTrigger.pt, bestCollisionVtxZ, bestCollisionMultiplicity); histos.fill(HIST("PairLossK0/Comparison/RecVariants"), 15, truthDeltaPhi, truthDeltaEta, truthK0.pt, truthTrigger.pt, bestCollisionVtxZ, bestCollisionMultiplicity); finalPairRecords.push_back(PairLossFinalRecord{ - PairLossPairKey{truthTrigger.globalIndex, truthK0.globalIndex}, - truthDeltaPhi, truthDeltaEta, truthK0.pt, truthTrigger.pt}); + .key = PairLossPairKey{.triggerId = truthTrigger.globalIndex, .k0Id = truthK0.globalIndex}, + .deltaPhi = truthDeltaPhi, + .deltaEta = truthDeltaEta, + .k0Pt = truthK0.pt, + .triggerPt = truthTrigger.pt}); } } } @@ -5037,6 +5504,25 @@ struct HStrangeCorrelation { } } } + // Truth-trigger rejection bin of the MC particle behind every labelled trigger + // track, for PairLossK0/Comparison/TriggerMatchQA. mcParticles holds only the + // particles of this MC collision, so a label missing from the map points to + // another MC collision. + std::unordered_set triggerLabelIds; + for (auto const& collision : recCollisions) { + for (auto const& triggerEntry : triggerTracks.sliceBy(collisionSliceTracks, collision.globalIndex())) { + const auto triggerMcIt = pairLossComparison.trackToMc.find(triggerEntry.trackId()); + if (triggerMcIt != pairLossComparison.trackToMc.end()) { + triggerLabelIds.insert(triggerMcIt->second); + } + } + } + for (auto const& mcParticle : mcParticles) { + if (triggerLabelIds.contains(mcParticle.globalIndex())) { + int sign = 0; + pairLossComparison.triggerTruthRejection[mcParticle.globalIndex()] = pairLossTruthTriggerRejection(mcParticle, sign); + } + } for (auto const& truthTrigger : comparisonTruthTriggers) { pairLossComparison.truthTriggers[truthTrigger.globalIndex] = truthTrigger; } @@ -5061,12 +5547,24 @@ struct HStrangeCorrelation { pairLossComparison.bestCollisionId = bestCollisionId; pairLossComparison.vtxZ = bestCollisionVtxZ; pairLossComparison.multiplicity = bestCollisionMultiplicity; + pairLossComparison.nChGen = applyGenInel ? nChGen : -1; pairLossComparison.active = true; for (auto const& recCollision : recCollisions) { + // recComparisonBestCollisionOnly: one reconstructed collision per MC collision, + // the best one, as in the Gen stage + if (pairLossK0Configurations.recComparisonBestCollisionOnly && recCollision.globalIndex() != bestCollisionId) { + continue; + } + // The band cut tests every reconstructed collision against the Nch^gen of + // this MC collision, so it also rejects split and duplicate vertices + if (applyBandCut && !isInNchResponseBand(nChGen, recCollision.multNTracksPVeta1())) { + continue; + } const auto recTriggerSlice = triggerTracks.sliceBy(collisionSliceTracks, recCollision.globalIndex()); const auto recV0Slice = associatedV0s.sliceBy(collisionSliceV0s, recCollision.globalIndex()); const auto recTrackSlice = tracks.sliceBy(pairLossTracksPerCollision, recCollision.globalIndex()); - runSameEventHV0s(recCollision, recV0Slice, recTriggerSlice, recTrackSlice); + runSameEventHV0s(recCollision, recV0Slice, recTriggerSlice, recTrackSlice, + applyGenInel ? std::optional{nChGen} : std::nullopt); } // Final-minus-Rec counterpart of RecNotInFinal. Both set differences use @@ -5081,6 +5579,238 @@ struct HStrangeCorrelation { pairLossComparison.clear(); }; + // MC-collision reconstruction efficiency vs Nch^gen and the Nch^gen vs + // multNTracksPVeta1 and multNTracksPVeta1 vs FT0M (percentile and amplitude) + // responses. The event selection is the one runSameEventHV0s applies before + // its INEL cut: trigger presence + isCollisionSelected (isCollisionSelectedNoInel) + auto runEventRecoEfficiencyStudy = [&]() { + // Best collision: the one with the most PV contributors, as in the Rec comparison + int largestNContributors = -1; + bool bestCollisionSelectedNoInel = false; + bool bestCollisionSelected = false; + int bestCollisionNTracksPVeta1 = -1; + for (auto const& collision : recCollisions) { + if (collision.numContrib() <= largestNContributors) { + continue; + } + largestNContributors = collision.numContrib(); + bestCollisionSelectedNoInel = isCollisionSelectedNoInel(collision); + // reconstructed-level INEL cuts; the Pb-Pb event selection has none + bestCollisionSelected = bestCollisionSelectedNoInel && (!masterConfigurations.doPPAnalysis || isCollisionInelSelected(collision)); + bestCollisionNTracksPVeta1 = collision.multNTracksPVeta1(); + } + + // Nch^gen response, and with applyNchResponseBandCut the part of it inside the band + auto fillNchGenResponse = [&](int nTracksPVeta1) { + histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1"), nChGen, nTracksPVeta1); + if (applyBandCut && isInNchResponseBand(nChGen, nTracksPVeta1)) { + histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1InBand"), nChGen, nTracksPVeta1); + } + }; + + // Responses: one entry per reconstructed collision, as in the Rec path, except + // the Nch^gen response with nchGenResponseBestCollisionOnly (best collision only) + for (auto const& collision : recCollisions) { + if (isCollisionSelectedNoInel(collision)) { + if (!pairLossK0Configurations.nchGenResponseBestCollisionOnly) { + fillNchGenResponse(collision.multNTracksPVeta1()); + } + histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNTracksPVeta1VsCentFT0M"), collision.multNTracksPVeta1(), collision.centFT0M()); + histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNTracksPVeta1VsMultFT0M"), collision.multNTracksPVeta1(), collision.multFT0M()); + } + } + if (pairLossK0Configurations.nchGenResponseBestCollisionOnly && bestCollisionSelectedNoInel) { + fillNchGenResponse(bestCollisionNTracksPVeta1); + } + + // Efficiency: one entry per MC collision, evaluated on the best collision + if (std::abs(mcCollision.posZ()) > masterConfigurations.zVertexCut) { + return; + } + histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNchGenAll"), nChGen); + histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNchGenVsNRecoColl"), nChGen, recCollisions.size()); + if (recCollisions.size() == 0) { + return; + } + histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNchGenHasRecoColl"), nChGen); + if (bestCollisionSelectedNoInel) { + histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNchGenEvSelNoInel"), nChGen); + if (applyBandCut && isInNchResponseBand(nChGen, bestCollisionNTracksPVeta1)) { + histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNchGenEvSelNoInelInBand"), nChGen); + } + } + if (bestCollisionSelected) { + histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNchGenEvSel"), nChGen); + } + }; + + // PV quality vs midrapidity multiplicity, split by the truth h-K0 content of the MC + // collision (PairLossPVQualityClass). Tests whether PVs built from few tracks are + // biased or badly defined, and whether a trigger and a K0 in the event -- whose + // displaced daughters can enter the vertex fit -- make it worse: in the PV position + // and residuals of the best collision, and in how often that collision is missing + // or fails the event selection of PairLossK0/Comparison + auto runPVQualityStudy = [&]() { + if (std::abs(mcCollision.posZ()) > masterConfigurations.zVertexCut) { + return; + } + + // Truth triggers and K0s as in PairLossK0/Comparison/Truth: same acceptance, + // primary requirements applied before the leading-trigger choice + struct PVQualityTrigger { + int64_t globalIndex = -1; + float pt = 0.0f; + float phi = 0.0f; + }; + struct PVQualityK0 { + float phi = 0.0f; + int64_t positiveDaughterId = -1; + int64_t negativeDaughterId = -1; + }; + std::vector pvTriggers; + std::vector pvK0s; + for (auto const& mcParticle : mcParticles) { + int sign = 0; + if (pairLossTruthTriggerRejection(mcParticle, sign) == PairLossTriggerMatchMatched && + (!masterConfigurations.doTriggPhysicalPrimary || mcParticle.isPhysicalPrimary())) { + pvTriggers.push_back(PVQualityTrigger{mcParticle.globalIndex(), mcParticle.pt(), mcParticle.phi()}); + } + if (mcParticle.pdgCode() != PDG_t::kK0Short || std::abs(mcParticle.eta()) > etaSel || + mcParticle.pt() < axisRanges[2][0] || mcParticle.pt() > axisRanges[2][1] || + (masterConfigurations.doAssocPhysicalPrimary && !mcParticle.isPhysicalPrimary())) { + continue; + } + PVQualityK0 pvK0{mcParticle.phi(), -1, -1}; + for (auto const& daughter : mcParticle.daughters_as()) { + if (daughter.pdgCode() == PDG_t::kPiPlus) { + pvK0.positiveDaughterId = daughter.globalIndex(); + } else if (daughter.pdgCode() == -PDG_t::kPiPlus) { + pvK0.negativeDaughterId = daughter.globalIndex(); + } + } + pvK0s.push_back(pvK0); + } + if (useTheLeadingParticleAsTrigger && pvTriggers.size() > 1) { + const auto leadingTrigger = *std::max_element(pvTriggers.begin(), pvTriggers.end(), [](auto const& lhs, auto const& rhs) { return lhs.pt < rhs.pt; }); + pvTriggers.assign(1, leadingTrigger); + } + + constexpr double PVQualitySideHalfWidth = PI / 3.0; + std::array inClass{}; + inClass[PairLossPVQualityAll] = true; + inClass[PairLossPVQualityHasTrigger] = !pvTriggers.empty(); + for (auto const& trigger : pvTriggers) { + for (auto const& k0 : pvK0s) { + if (trigger.globalIndex == k0.positiveDaughterId || trigger.globalIndex == k0.negativeDaughterId) { + continue; + } + const double deltaPhi = computeDeltaPhi(trigger.phi, k0.phi); + inClass[PairLossPVQualityHasPair] = true; + if (std::abs(deltaPhi) < PVQualitySideHalfWidth) { + inClass[PairLossPVQualityNearPair] = true; + } + if (std::abs(deltaPhi - PI) < PVQualitySideHalfWidth) { + inClass[PairLossPVQualityAwayPair] = true; + } + } + } + + auto fillEventStage = [&](int stage) { + for (int eventClass = 0; eventClass < PairLossPVQualityNClasses; ++eventClass) { + if (inClass[eventClass]) { + histos.fill(HIST("PairLossK0/PVQuality/hEventStage"), eventClass, stage, nChGen); + } + } + }; + fillEventStage(PairLossPVQualityStageAll); + if (recCollisions.size() == 0) { + return; + } + + // Best collision: the one with the most PV contributors, as in the Rec comparison + int largestNContributors = -1; + int64_t bestCollisionId = -1; + float bestCollisionVtxX = 0.0f; + float bestCollisionVtxY = 0.0f; + float bestCollisionVtxZ = 0.0f; + int bestCollisionNTracksPVeta1 = -1; + bool bestCollisionSelected = false; + for (auto const& collision : recCollisions) { + if (collision.numContrib() <= largestNContributors) { + continue; + } + largestNContributors = collision.numContrib(); + bestCollisionId = collision.globalIndex(); + bestCollisionVtxX = collision.posX(); + bestCollisionVtxY = collision.posY(); + bestCollisionVtxZ = collision.posZ(); + bestCollisionNTracksPVeta1 = collision.multNTracksPVeta1(); + // same event selection as pairLossEventSelected in runRecComparison + bestCollisionSelected = (!pairLossK0Configurations.applyRecoEventSelection || + (masterConfigurations.doPPAnalysis ? isCollisionSelected(collision, !applyGenInel) && (!applyGenInel || isGenInelGt0(nChGen)) + : isCollisionSelectedPbPb(collision, false))) && + (!applyBandCut || isInNchResponseBand(nChGen, collision.multNTracksPVeta1())); + } + if (bestCollisionId < 0) { + return; + } + + // Direct check of the K0 side of the hypothesis: a daughter of a truth K0 above + // was used in the vertex fit of the best collision + std::unordered_set pvK0DaughterIds; + for (auto const& k0 : pvK0s) { + for (const int64_t daughterId : {k0.positiveDaughterId, k0.negativeDaughterId}) { + if (daughterId >= 0) { + pvK0DaughterIds.insert(daughterId); + } + } + } + if (!pvK0DaughterIds.empty()) { + const auto trackSlice = tracks.sliceBy(pairLossTracksPerCollision, bestCollisionId); + for (auto const& track : trackSlice) { + if (!track.isPVContributor()) { + continue; + } + const auto trackLabel = trackLabels.iteratorAt(track.globalIndex()); + if (trackLabel.has_mcParticle() && pvK0DaughterIds.contains(trackLabel.mcParticleId())) { + inClass[PairLossPVQualityK0DaughterPVContributor] = true; + break; + } + } + } + + fillEventStage(PairLossPVQualityStageHasRecoColl); + if (bestCollisionSelected) { + fillEventStage(PairLossPVQualityStageSelected); + } + + const float residualX = bestCollisionVtxX - mcCollision.posX(); + const float residualY = bestCollisionVtxY - mcCollision.posY(); + const float residualZ = bestCollisionVtxZ - mcCollision.posZ(); + auto fillBestCollision = [&](int selectionBin) { + for (int eventClass = 0; eventClass < PairLossPVQualityNClasses; ++eventClass) { + if (!inClass[eventClass]) { + continue; + } + histos.fill(HIST("PairLossK0/PVQuality/hPosX"), eventClass, selectionBin, bestCollisionNTracksPVeta1, bestCollisionVtxX); + histos.fill(HIST("PairLossK0/PVQuality/hPosY"), eventClass, selectionBin, bestCollisionNTracksPVeta1, bestCollisionVtxY); + histos.fill(HIST("PairLossK0/PVQuality/hResX"), eventClass, selectionBin, bestCollisionNTracksPVeta1, residualX); + histos.fill(HIST("PairLossK0/PVQuality/hResY"), eventClass, selectionBin, bestCollisionNTracksPVeta1, residualY); + histos.fill(HIST("PairLossK0/PVQuality/hResZ"), eventClass, selectionBin, bestCollisionNTracksPVeta1, residualZ); + } + }; + fillBestCollision(0); + if (bestCollisionSelected) { + fillBestCollision(1); + } + }; + + if (pairLossK0Configurations.doEventRecoEfficiencyStudy) { + runEventRecoEfficiencyStudy(); + } + if (pairLossK0Configurations.doPVQualityStudy) { + runPVQualityStudy(); + } if (pairLossK0Configurations.doGenLevelStudy) { runGenLevelStudy(); } @@ -5130,7 +5860,7 @@ struct HStrangeCorrelation { continue; } v0sAnyCollision[v0MC.particleIdMC()].push_back(PairLossV0Info{ - .globalIndex = static_cast(v0.globalIndex()), + .globalIndex = v0.globalIndex(), .positiveTrackId = static_cast(v0.posTrackId()), .negativeTrackId = static_cast(v0.negTrackId()), .pt = v0.pt(), @@ -5144,7 +5874,7 @@ struct HStrangeCorrelation { } for (auto const& collision : recCollisions) { - if (static_cast(collision.globalIndex()) != bestCollisionId) { + if (collision.globalIndex() != bestCollisionId) { continue; } @@ -5178,7 +5908,7 @@ struct HStrangeCorrelation { continue; } v0sBestCollision[v0MC.particleIdMC()].push_back(PairLossV0Info{ - .globalIndex = static_cast(v0.globalIndex()), + .globalIndex = v0.globalIndex(), .positiveTrackId = static_cast(v0.posTrackId()), .negativeTrackId = static_cast(v0.negTrackId()), .pt = v0.pt(), @@ -5195,7 +5925,7 @@ struct HStrangeCorrelation { PairLossV0Map v0sInTable; PairLossV0Map v0sFinal; for (auto const& finalCollision : recCollisions) { - const int64_t finalCollisionId = static_cast(finalCollision.globalIndex()); + const int64_t finalCollisionId = finalCollision.globalIndex(); const bool isBestCollision = finalCollisionId == bestCollisionId; if (!isBestCollision) { if (pairLossK0Configurations.finalPairUseBestCollisionOnly) { @@ -5242,9 +5972,9 @@ struct HStrangeCorrelation { auto positiveTrack = v0.posTrack_as(); auto negativeTrack = v0.negTrack_as(); const PairLossV0Info v0Info{ - .globalIndex = static_cast(v0.globalIndex()), - .positiveTrackId = static_cast(positiveTrack.globalIndex()), - .negativeTrackId = static_cast(negativeTrack.globalIndex()), + .globalIndex = v0.globalIndex(), + .positiveTrackId = positiveTrack.globalIndex(), + .negativeTrackId = negativeTrack.globalIndex(), .pt = v0.pt(), .eta = v0.eta(), .phi = v0.phi(), @@ -5268,8 +5998,8 @@ struct HStrangeCorrelation { const bool passesDaughterDcaAndArmenteros = std::abs(v0.dcapostopv()) > dcaCut && std::abs(v0.dcanegtopv()) > dcaCut && v0.qtarm() * v0Selection.armPodCut > std::abs(v0.alpha()); passesFinalSelection = v0SelectedPbPb(v0) && passesLifetime && passesDaughterDcaAndArmenteros; } - passesFinalSelection = passesFinalSelection && positiveTrack.tpcNClsCrossedRows() >= trackSelection.minTPCNCrossedRowsAssociated && - negativeTrack.tpcNClsCrossedRows() >= trackSelection.minTPCNCrossedRowsAssociated; + passesFinalSelection = passesFinalSelection && positiveTrack.hasTPC() && positiveTrack.tpcNClsCrossedRows() >= trackSelection.minTPCNCrossedRowsAssociated && + negativeTrack.hasTPC() && negativeTrack.tpcNClsCrossedRows() >= trackSelection.minTPCNCrossedRowsAssociated; if (trackSelection.checksRequireTPCChi2) { passesFinalSelection = passesFinalSelection && positiveTrack.tpcChi2NCl() <= trackSelection.maxTPCChi2PerClusterAssociated && negativeTrack.tpcChi2NCl() <= trackSelection.maxTPCChi2PerClusterAssociated; @@ -5279,7 +6009,7 @@ struct HStrangeCorrelation { negativeTrack.itsNCls() >= trackSelection.minITSClustersForDaughterTracks; } passesFinalSelection = passesFinalSelection && assocEntry.compatible(IndexK0, trackSelection.dEdxCompatibility) && - (!doAssocPhysicalPrimary || assocEntry.mcPhysicalPrimary()) && + (!masterConfigurations.doAssocPhysicalPrimary || assocEntry.mcPhysicalPrimary()) && assocEntry.invMassNSigma(IndexK0) > -massWindowConfigurations.maxPeakNSigma && assocEntry.invMassNSigma(IndexK0) < massWindowConfigurations.maxPeakNSigma && v0.pt() >= axisRanges[2][0] && v0.pt() <= axisRanges[2][1]; @@ -5302,7 +6032,7 @@ struct HStrangeCorrelation { continue; } truthTriggers.push_back(PairLossTruthTrackInfo{ - .globalIndex = static_cast(mcParticle.globalIndex()), + .globalIndex = mcParticle.globalIndex(), .pt = mcParticle.pt(), .eta = mcParticle.eta(), .phi = mcParticle.phi(), @@ -5311,11 +6041,11 @@ struct HStrangeCorrelation { if (mcParticle.pdgCode() != PDG_t::kK0Short || std::abs(mcParticle.eta()) > etaSel || mcParticle.pt() < axisRanges[2][0] || mcParticle.pt() > axisRanges[2][1] || - (doAssocPhysicalPrimary && !mcParticle.isPhysicalPrimary())) { + (masterConfigurations.doAssocPhysicalPrimary && !mcParticle.isPhysicalPrimary())) { continue; } PairLossTruthK0Info truthK0{ - .globalIndex = static_cast(mcParticle.globalIndex()), + .globalIndex = mcParticle.globalIndex(), .pt = mcParticle.pt(), .eta = mcParticle.eta(), .phi = mcParticle.phi(), @@ -5326,7 +6056,7 @@ struct HStrangeCorrelation { const auto daughters = mcParticle.daughters_as(); for (auto const& daughter : daughters) { PairLossTruthTrackInfo daughterInfo{ - .globalIndex = static_cast(daughter.globalIndex()), + .globalIndex = daughter.globalIndex(), .pt = daughter.pt(), .eta = daughter.eta(), .phi = daughter.phi(), @@ -5406,7 +6136,7 @@ struct HStrangeCorrelation { true, // V0 in AssocV0s: K0 side only true, // V0 final selection: K0 side only contains(triggersFinal, truthTrigger.globalIndex)}; // Final reconstructed pair - std::array triggerStageFills; + std::array triggerStageFills{}; triggerStageFills.fill(1); if (!pairLossK0Configurations.fillFinalPairOnce) { triggerStageFills[PairLossTriggerPureReco] = static_cast(numberOfMatches(tracksBestCollision, truthTrigger.globalIndex)); @@ -5762,7 +6492,7 @@ struct HStrangeCorrelation { PairLossTrackMap pairLossFinalTriggers; PairLossV0Map pairLossFinalV0s; for (auto const& collision : recCollisions) { - if (static_cast(collision.globalIndex()) != pairLossBestCollisionId) { + if (collision.globalIndex() != pairLossBestCollisionId) { continue; } @@ -5807,8 +6537,8 @@ struct HStrangeCorrelation { v0.qtarm() * v0Selection.armPodCut > std::abs(v0.alpha()); passesFinalSelection = v0SelectedPbPb(v0) && passesLifetime && passesDaughterDcaAndArmenteros; } - passesFinalSelection = passesFinalSelection && positiveTrack.tpcNClsCrossedRows() >= trackSelection.minTPCNCrossedRowsAssociated && - negativeTrack.tpcNClsCrossedRows() >= trackSelection.minTPCNCrossedRowsAssociated; + passesFinalSelection = passesFinalSelection && positiveTrack.hasTPC() && positiveTrack.tpcNClsCrossedRows() >= trackSelection.minTPCNCrossedRowsAssociated && + negativeTrack.hasTPC() && negativeTrack.tpcNClsCrossedRows() >= trackSelection.minTPCNCrossedRowsAssociated; if (trackSelection.checksRequireTPCChi2) { passesFinalSelection = passesFinalSelection && positiveTrack.tpcChi2NCl() <= trackSelection.maxTPCChi2PerClusterAssociated && negativeTrack.tpcChi2NCl() <= trackSelection.maxTPCChi2PerClusterAssociated; @@ -5818,7 +6548,7 @@ struct HStrangeCorrelation { negativeTrack.itsNCls() >= trackSelection.minITSClustersForDaughterTracks; } passesFinalSelection = passesFinalSelection && assocEntry.compatible(IndexK0, trackSelection.dEdxCompatibility) && - (!doAssocPhysicalPrimary || assocEntry.mcPhysicalPrimary()) && + (!masterConfigurations.doAssocPhysicalPrimary || assocEntry.mcPhysicalPrimary()) && assocEntry.invMassNSigma(IndexK0) > -massWindowConfigurations.maxPeakNSigma && assocEntry.invMassNSigma(IndexK0) < massWindowConfigurations.maxPeakNSigma && v0.pt() >= axisRanges[2][0] && v0.pt() <= axisRanges[2][1]; @@ -5826,9 +6556,9 @@ struct HStrangeCorrelation { continue; } pairLossFinalV0s[v0MC.particleIdMC()].push_back(PairLossV0Info{ - .globalIndex = static_cast(v0.globalIndex()), - .positiveTrackId = static_cast(positiveTrack.globalIndex()), - .negativeTrackId = static_cast(negativeTrack.globalIndex()), + .globalIndex = v0.globalIndex(), + .positiveTrackId = positiveTrack.globalIndex(), + .negativeTrackId = negativeTrack.globalIndex(), .pt = v0.pt(), .eta = v0.eta(), .phi = v0.phi(), @@ -5845,10 +6575,10 @@ struct HStrangeCorrelation { // requires the reconstructed pair itself to fall in the reconstructed angular // range and to survive the reconstructed autocorrelation rejection. auto pairLossHasFinalTrigger = [&](int64_t mcId) { - return pairLossFinalTriggers.find(mcId) != pairLossFinalTriggers.end(); + return pairLossFinalTriggers.contains(mcId); }; auto pairLossHasFinalK0 = [&](int64_t mcId) { - return pairLossFinalV0s.find(mcId) != pairLossFinalV0s.end(); + return pairLossFinalV0s.contains(mcId); }; auto pairLossHasFinalPair = [&](int64_t triggerMcId, int64_t k0McId) { const auto triggerMatches = pairLossFinalTriggers.find(triggerMcId); @@ -5880,7 +6610,7 @@ struct HStrangeCorrelation { }; for (auto const& collision : recCollisions) { - if (static_cast(collision.globalIndex()) != pairLossBestCollisionId) { + if (collision.globalIndex() != pairLossBestCollisionId) { continue; } @@ -5929,7 +6659,7 @@ struct HStrangeCorrelation { double sumPt = 0., q2x = 0., q2y = 0.; for (auto const& particle : mcParticles) { currentMcIds.insert(particle.globalIndex()); - nAnyMatched += pairLossAnyTrackMcParticleIds.count(particle.globalIndex()) != 0; + nAnyMatched += pairLossAnyTrackMcParticleIds.contains(particle.globalIndex()); if (!particle.isPhysicalPrimary() || !std::isfinite(particle.eta()) || std::abs(particle.eta()) >= GenEtaMax) { continue; } @@ -5951,7 +6681,7 @@ struct HStrangeCorrelation { std::unordered_set bestMatchedMcIds; const auto bestTracks = tracks.sliceBy(pairLossTracksPerCollision, collision.globalIndex()); for (auto const& track : bestTracks) { - const bool sameMc = track.has_mcParticle() && currentMcIds.count(track.mcParticleId()) != 0; + const bool sameMc = track.has_mcParticle() && currentMcIds.contains(track.mcParticleId()); if (sameMc) { bestMatchedMcIds.insert(track.mcParticleId()); } @@ -6012,9 +6742,9 @@ struct HStrangeCorrelation { auto const* pdgParticle = pdgDB->GetParticle(mcParticle.pdgCode()); const double charge = pdgParticle != nullptr ? pdgParticle->Charge() : 0.0; const int sign = charge > 0.0 ? 1 : (charge < 0.0 ? -1 : 0); - if (!((triggerParticleCharge > 0 && sign < 0) || (triggerParticleCharge < 0 && sign > 0) || sign == 0)) { + if ((triggerParticleCharge <= 0 || sign >= 0) && (triggerParticleCharge >= 0 || sign <= 0) && sign != 0) { pairLossTruthTriggers.push_back(PairLossTruthTrackInfo{ - .globalIndex = static_cast(mcParticle.globalIndex()), + .globalIndex = mcParticle.globalIndex(), .pt = mcParticle.pt(), .eta = mcParticle.eta(), .phi = mcParticle.phi(), @@ -6024,12 +6754,12 @@ struct HStrangeCorrelation { if (mcParticle.pdgCode() != PDG_t::kK0Short || std::abs(mcParticle.eta()) > etaSel || mcParticle.pt() < axisRanges[2][0] || mcParticle.pt() > axisRanges[2][1] || - (doAssocPhysicalPrimary && !mcParticle.isPhysicalPrimary())) { + (masterConfigurations.doAssocPhysicalPrimary && !mcParticle.isPhysicalPrimary())) { continue; } PairLossTruthK0Info truthK0{ - .globalIndex = static_cast(mcParticle.globalIndex()), + .globalIndex = mcParticle.globalIndex(), .pt = mcParticle.pt(), .eta = mcParticle.eta(), .phi = mcParticle.phi(), @@ -6039,7 +6769,7 @@ struct HStrangeCorrelation { .negativeDaughter = {}}; for (auto const& daughter : mcParticle.daughters_as()) { PairLossTruthTrackInfo daughterInfo{ - .globalIndex = static_cast(daughter.globalIndex()), + .globalIndex = daughter.globalIndex(), .pt = daughter.pt(), .eta = daughter.eta(), .phi = daughter.phi(), @@ -6067,11 +6797,11 @@ struct HStrangeCorrelation { // AnyTrackBoth/, at fully-selected level in Final/. for (auto const& truthTrigger : pairLossTruthTriggers) { if (pairLossK0Configurations.doEventQualityStudy) { - fillEventQuality(1, countDeltaPhi, countDeltaEta, axisRanges[2][0], truthTrigger.pt, pairLossAnyTrackMcParticleIds.count(truthTrigger.globalIndex) != 0, pairLossHasFinalTrigger(truthTrigger.globalIndex)); + fillEventQuality(1, countDeltaPhi, countDeltaEta, axisRanges[2][0], truthTrigger.pt, pairLossAnyTrackMcParticleIds.contains(truthTrigger.globalIndex), pairLossHasFinalTrigger(truthTrigger.globalIndex)); } histos.fill(HIST("ClosureTest/PairLossK0/Truth/hTrigger"), truthTrigger.pt, truthTrigger.eta, truthTrigger.phi); histos.fill(HIST("ClosureTest/PairLossK0/AnyTrackK0/hTrigger"), truthTrigger.pt, truthTrigger.eta, truthTrigger.phi); - if (pairLossAnyTrackMcParticleIds.find(truthTrigger.globalIndex) != pairLossAnyTrackMcParticleIds.end()) { + if (pairLossAnyTrackMcParticleIds.contains(truthTrigger.globalIndex)) { histos.fill(HIST("ClosureTest/PairLossK0/AnyTrack/hTrigger"), truthTrigger.pt, truthTrigger.eta, truthTrigger.phi); histos.fill(HIST("ClosureTest/PairLossK0/AnyTrackBoth/hTrigger"), truthTrigger.pt, truthTrigger.eta, truthTrigger.phi); } @@ -6082,7 +6812,7 @@ struct HStrangeCorrelation { for (auto const& truthK0 : pairLossTruthK0s) { histos.fill(HIST("ClosureTest/PairLossK0/Truth/hK0Short"), truthK0.pt, truthK0.eta, truthK0.phi); histos.fill(HIST("ClosureTest/PairLossK0/AnyTrack/hK0Short"), truthK0.pt, truthK0.eta, truthK0.phi); - if (pairLossAnyV0McParticleIds.find(truthK0.globalIndex) != pairLossAnyV0McParticleIds.end()) { + if (pairLossAnyV0McParticleIds.contains(truthK0.globalIndex)) { histos.fill(HIST("ClosureTest/PairLossK0/AnyTrackK0/hK0Short"), truthK0.pt, truthK0.eta, truthK0.phi); histos.fill(HIST("ClosureTest/PairLossK0/AnyTrackBoth/hK0Short"), truthK0.pt, truthK0.eta, truthK0.phi); } @@ -6092,7 +6822,7 @@ struct HStrangeCorrelation { } for (auto const& truthTrigger : pairLossTruthTriggers) { - const bool triggerHasAnyTrack = pairLossAnyTrackMcParticleIds.find(truthTrigger.globalIndex) != pairLossAnyTrackMcParticleIds.end(); + const bool triggerHasAnyTrack = pairLossAnyTrackMcParticleIds.contains(truthTrigger.globalIndex); for (auto const& truthK0 : pairLossTruthK0s) { if (truthTrigger.globalIndex == truthK0.positiveDaughter.globalIndex || truthTrigger.globalIndex == truthK0.negativeDaughter.globalIndex) { continue; @@ -6107,7 +6837,7 @@ struct HStrangeCorrelation { continue; } - const bool k0HasAnyV0 = pairLossAnyV0McParticleIds.find(truthK0.globalIndex) != pairLossAnyV0McParticleIds.end(); + const bool k0HasAnyV0 = pairLossAnyV0McParticleIds.contains(truthK0.globalIndex); histos.fill(HIST("ClosureTest/PairLossK0/Truth/sameEvent/K0Short"), truthDeltaPhi, truthDeltaEta, truthK0.pt, truthTrigger.pt, pairLossBestCollisionVtxZ, pairLossBestCollisionMultiplicity); if (triggerHasAnyTrack) { @@ -6171,7 +6901,7 @@ struct HStrangeCorrelation { } } - if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (!masterConfigurations.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { if (mcParticle.pdgCode() == PDG_t::kK0Short && masterConfigurations.doCorrelationK0Short) { histos.fill(HIST("hClosureQAPtAssociatedK0"), gpt, 0.0f); // step 1: no event selection whatsoever } @@ -6192,7 +6922,7 @@ struct HStrangeCorrelation { } else { bool bestCollisionSel8 = false; bool bestCollisionINELgtZERO = false; - bool bestCollisionINELgtONE = false; + bool bestCollisionINELgtN = false; bool bestCollisionNoSameBunchPileup = false; bool bestCollisionGoodTriggerTVX = false; bool bestCollisionGoodZvtxFT0vsPV = false; @@ -6212,19 +6942,19 @@ struct HStrangeCorrelation { } else { bestCollisionSel8 = recCollision.sel8(); bestCollisionINELgtZERO = recCollision.isInelGt0(); - bestCollisionINELgtONE = recCollision.isInelGt1(); + bestCollisionINELgtN = isInelGtNReco(recCollision, masterConfigurations.inelGtNThreshold); bestCollisionNoSameBunchPileup = recCollision.selection_bit(o2::aod::evsel::kNoSameBunchPileup); bestCollisionGoodTriggerTVX = recCollision.selection_bit(aod::evsel::kIsTriggerTVX); bestCollisionGoodZvtxFT0vsPV = recCollision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV); } - if (triggerPresenceMap.size() > 0) { + if (!triggerPresenceMap.empty()) { bestCollisionTriggerPresenceMap = triggerPresenceMap[recCollision.globalIndex()]; } } } // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(bestCollisionTriggerPresenceMap, triggerBinToSelect)) { + if (!triggerPresenceMap.empty() && !TESTBIT(bestCollisionTriggerPresenceMap, triggerBinToSelect)) { return; } @@ -6243,7 +6973,7 @@ struct HStrangeCorrelation { if (!bestCollisionINELgtZERO) { return; } - if (masterConfigurations.selectINELgtONE && !bestCollisionINELgtONE) { + if (masterConfigurations.selectINELgtN && !bestCollisionINELgtN) { return; } if (masterConfigurations.rejectSameBunchPileup && !bestCollisionNoSameBunchPileup) { @@ -6262,7 +6992,8 @@ struct HStrangeCorrelation { } } histos.fill(HIST("hClosureTestEventCounter"), 1.5f); - + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; + int binnumb = colBinning.getBin({bestCollisionVtxZ, bestCollisionCentpercentile}); for (auto const& mcParticle : mcParticles) { double geta = mcParticle.eta(); if (std::abs(geta) > etaSel) { @@ -6275,7 +7006,7 @@ struct HStrangeCorrelation { } } - if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (!masterConfigurations.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { if (mcParticle.pdgCode() == PDG_t::kK0Short && masterConfigurations.doCorrelationK0Short) { histos.fill(HIST("hClosureQAPtAssociatedK0"), gpt, 1.0f); // step 2: after event selection } @@ -6283,6 +7014,9 @@ struct HStrangeCorrelation { } int iteratorNum = -1; + if (masterConfigurations.mixingEventForClosureTest) { + genLevelCollisions[binnumb].emplace_back(); + } for (auto const& mcParticle : mcParticles) { iteratorNum = iteratorNum + 1; double geta = mcParticle.eta(); @@ -6294,19 +7028,27 @@ struct HStrangeCorrelation { if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kProton || std::abs(mcParticle.pdgCode()) == PDG_t::kElectron || std::abs(mcParticle.pdgCode()) == PDG_t::kMuonMinus) { if (!masterConfigurations.doTriggPhysicalPrimary || mcParticle.isPhysicalPrimary()) { triggerIndices.emplace_back(iteratorNum); - histos.fill(HIST("ClosureTest/hTrigger"), gpt, geta, bestCollisionCentpercentile); - if (masterConfigurations.doClosureTestTriggerWithRecoTrackMatch && mcParticleIdsWithRecoTrackMatch.find(mcParticle.globalIndex()) != mcParticleIdsWithRecoTrackMatch.end()) { + histos.fill(HIST("ClosureTest/hTrigger"), gpt, geta, bestCollisionCentpercentile, gphi); + if (masterConfigurations.mixingEventForClosureTest) { + ValidTriggerInGen TriggerInGen; + TriggerInGen.pt = gpt; + TriggerInGen.eta = geta; + TriggerInGen.phi = gphi; + if (gpt <= axisRanges[3][1] && gpt >= axisRanges[3][0]) + genLevelCollisions[binnumb].back().emplace_back(TriggerInGen); + } + if (masterConfigurations.doClosureTestTriggerWithRecoTrackMatch && mcParticleIdsWithRecoTrackMatch.contains(mcParticle.globalIndex())) { histos.fill(HIST("ClosureTest/TriggerWithRecoTrackMatch/hTrigger"), gpt, geta, bestCollisionCentpercentile); } } if (masterConfigurations.doCorrelationHadron) { - if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (!masterConfigurations.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { assocHadronIndices.emplace_back(iteratorNum); histos.fill(HIST("ClosureTest/hHadron"), gpt, geta, gphi); } } } - if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (!masterConfigurations.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus && masterConfigurations.doCorrelationPion) { piIndices.emplace_back(iteratorNum); histos.fill(HIST("ClosureTest/hPion"), gpt, geta, gphi); @@ -6361,7 +7103,7 @@ struct HStrangeCorrelation { double getatrigger = triggerParticle.eta(); double gphitrigger = triggerParticle.phi(); double pttrigger = triggerParticle.pt(); - const bool triggerHasRecoTrackMatch = masterConfigurations.doClosureTestTriggerWithRecoTrackMatch && mcParticleIdsWithRecoTrackMatch.find(triggerParticle.globalIndex()) != mcParticleIdsWithRecoTrackMatch.end(); + const bool triggerHasRecoTrackMatch = masterConfigurations.doClosureTestTriggerWithRecoTrackMatch && mcParticleIdsWithRecoTrackMatch.contains(triggerParticle.globalIndex()); auto const* triggerPdg = pdgDB->GetParticle(triggerParticle.pdgCode()); const double triggerCharge = triggerPdg ? triggerPdg->Charge() : 0.; auto const& mother = triggerParticle.mothers_first_as(); @@ -6419,6 +7161,33 @@ struct HStrangeCorrelation { } }); } + if (!masterConfigurations.mixingEventForClosureTest) { + return; + } + for (std::size_t iCollision = 0; iCollision < genLevelCollisions[binnumb].size(); iCollision++) { + for (std::size_t iTrigger = 0; iTrigger < genLevelCollisions[binnumb][iCollision].size(); iTrigger++) { + auto triggerParticle = genLevelCollisions[binnumb][iCollision][iTrigger]; + double getatrigger = triggerParticle.eta; + double gphitrigger = triggerParticle.phi; + double pttrigger = triggerParticle.pt; + static_for<0, 8>([&](auto i) { // associated loop + constexpr int Index = i.value; + for (std::size_t iassoc = 0; iassoc < associatedIndices[Index].size(); iassoc++) { + auto assocParticle = mcParticles.iteratorAt(associatedIndices[Index][iassoc]); + double getaassoc = assocParticle.eta(); + double gphiassoc = assocParticle.phi(); + double ptassoc = assocParticle.pt(); + double deltaeta = getatrigger - getaassoc; + if (TESTBIT(doCorrelation, i)) { + histos.fill(HIST("ClosureTest/mixedEvent/") + HIST(Particlenames[Index]), computeDeltaPhi(gphitrigger, gphiassoc), deltaeta, ptassoc, pttrigger, bestCollisionVtxZ, bestCollisionCentpercentile); + } + } + }); + } + } + if (genLevelCollisions[binnumb].size() >= static_cast(masterConfigurations.mixingParameter) + 1) { + genLevelCollisions[binnumb].erase(genLevelCollisions[binnumb].begin()); + } } void processFeedDown(soa::Join::iterator const& collision, aod::AssocV0s const& associatedV0s, aod::McParticles const&, V0DatasWithoutTrackXMC const&, TracksComplete const&, aod::BCsWithTimestamps const&) @@ -6436,7 +7205,7 @@ struct HStrangeCorrelation { //---] track quality check [--- auto postrack = v0Data.posTrack_as(); auto negtrack = v0Data.negTrack_as(); - if (postrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || negtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { + if (!postrack.hasTPC() || postrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || !negtrack.hasTPC() || negtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { continue; } if (trackSelection.requireClusterInITS && (postrack.itsNCls() < trackSelection.minITSClustersForDaughterTracks || negtrack.itsNCls() < trackSelection.minITSClustersForDaughterTracks)) { @@ -6519,7 +7288,7 @@ struct HStrangeCorrelation { } // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + if (!triggerPresenceMap.empty() && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { return; } @@ -6549,7 +7318,7 @@ struct HStrangeCorrelation { } // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + if (!triggerPresenceMap.empty() && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { return; } // Perform basic event selection on both collisions @@ -6587,9 +7356,9 @@ struct HStrangeCorrelation { float multEta08 = -1; float multEta05 = -1; histos.fill(HIST("Prediction/hEventSelection"), 0.5); - // INEL>1 implies INEL>0, so only the tighter enabled selection has to be evaluated - if (masterConfigurations.selectINELgtONE) { - if (!o2::pwglf::isINELgt1mc(mcParticles, pdgDB)) { + // INEL>N (N >= 0) implies INEL>0, so only the tighter enabled selection has to be evaluated + if (masterConfigurations.selectINELgtN) { + if (!isInelGtNGen(mcParticles, masterConfigurations.inelGtNThreshold)) { return; } } else if (masterConfigurations.selectINELgtZERO) { @@ -6650,13 +7419,13 @@ struct HStrangeCorrelation { } } if (masterConfigurations.doCorrelationHadron) { - if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (!masterConfigurations.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { assocHadronIndices.emplace_back(iteratorNum); histos.fill(HIST("Prediction/hHadron"), gpt, geta, gphi); } } } - if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (!masterConfigurations.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus && masterConfigurations.doCorrelationPion) { piIndices.emplace_back(iteratorNum); histos.fill(HIST("Prediction/hPion"), gpt, geta, gphi); diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 72a6465ced8..3eb74ce6662 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -1487,8 +1487,7 @@ struct lambdajetpolarizationionsderived { using LeadPBinningType = FlexibleBinningPolicy, o2::aod::lambdajetpol::Zvtx, decltype(getMixLeadPPt), decltype(getMixCentrality)>; LeadPBinningType leadPBinning{{getMixLeadPPt, getMixCentrality}, - {axisConfigurations.axisPVz, axisConfigurations.axisJetPt, axisConfigurations.axisCentrality}, - true}; // Ignore overflows true + {axisConfigurations.axisPVz, axisConfigurations.axisJetPt, axisConfigurations.axisCentrality}}; // Ignore overflows true // SameKindPair defaults to CombinationsBlockStrictlyUpperSameIndexPolicy, so no same-event mixing should happen: //(Already filtered by Zvtx even though we call by aod::RingCollisions, so no need to access the filtered table) @@ -1532,8 +1531,7 @@ struct lambdajetpolarizationionsderived { using LeadJetBinningType = FlexibleBinningPolicy, o2::aod::lambdajetpol::Zvtx, decltype(getMixLeadJetPt), decltype(getMixCentrality)>; LeadJetBinningType leadJetBinning{{getMixLeadJetPt, getMixCentrality}, - {axisConfigurations.axisPVz, axisConfigurations.axisJetPt, axisConfigurations.axisCentrality}, - true}; + {axisConfigurations.axisPVz, axisConfigurations.axisJetPt, axisConfigurations.axisCentrality}}; // RingJets is still the associated table (SameKindPair requires one), but its sliced content goes unused here: // the borrowed direction comes from jetProxyByCollision, which already knows which jet is the leading one. @@ -1568,8 +1566,7 @@ struct lambdajetpolarizationionsderived { using SubJetBinningType = FlexibleBinningPolicy, o2::aod::lambdajetpol::Zvtx, decltype(getMixSubJetPt), decltype(getMixCentrality)>; SubJetBinningType subJetBinning{{getMixSubJetPt, getMixCentrality}, - {axisConfigurations.axisPVz, axisConfigurations.axisJetPt, axisConfigurations.axisCentrality}, - true}; + {axisConfigurations.axisPVz, axisConfigurations.axisJetPt, axisConfigurations.axisCentrality}}; SameKindPair subJetPair{ subJetBinning, fakePolSwitches.mixedEventWindowSize, -1, collisions, std::make_tuple(jets), &mixCache}; diff --git a/PWGLF/Tasks/Strangeness/lambdaTwoPartPolarization.cxx b/PWGLF/Tasks/Strangeness/lambdaTwoPartPolarization.cxx index 1859c3f0782..2eeee1ccae9 100644 --- a/PWGLF/Tasks/Strangeness/lambdaTwoPartPolarization.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaTwoPartPolarization.cxx @@ -601,7 +601,7 @@ struct LfLambdaTwoPartPolarization { Preslice tracksPerCollisionV0 = aod::v0data::collisionId; using BinningTypeT0C = ColumnBinningPolicy; - BinningTypeT0C colBinningT0C{{vertexAxis, centAxis}, true}; + BinningTypeT0C colBinningT0C{{vertexAxis, centAxis}}; void processDataMixedT0C(EventCandidates const& collisions, TrackCandidates const& /*tracks*/, aod::V0Datas const& V0s, aod::BCsWithTimestamps const&) @@ -633,7 +633,7 @@ struct LfLambdaTwoPartPolarization { PROCESS_SWITCH(LfLambdaTwoPartPolarization, processDataMixedT0C, "Process event for mixed data in PbPb", false); using BinningTypeT0M = ColumnBinningPolicy; - BinningTypeT0M colBinningT0M{{vertexAxis, centAxis}, true}; + BinningTypeT0M colBinningT0M{{vertexAxis, centAxis}}; void processDataMixedT0M(EventCandidates const& collisions, TrackCandidates const& /*tracks*/, aod::V0Datas const& V0s, aod::BCsWithTimestamps const&) diff --git a/PWGLF/Tasks/Strangeness/lambdalambda.cxx b/PWGLF/Tasks/Strangeness/lambdalambda.cxx index d3659397339..91ddb9c66ae 100644 --- a/PWGLF/Tasks/Strangeness/lambdalambda.cxx +++ b/PWGLF/Tasks/Strangeness/lambdalambda.cxx @@ -585,7 +585,7 @@ struct lambdalambda { SliceCache cache; using BinningType = ColumnBinningPolicy; - BinningType colBinning{{vertexAxis, centAxis}, true}; + BinningType colBinning{{vertexAxis, centAxis}}; Preslice tracksPerCollisionV0 = aod::v0data::collisionId; void processDataMixed(EventCandidates const& collisions, diff --git a/PWGLF/Tasks/Strangeness/lambdapolsp.cxx b/PWGLF/Tasks/Strangeness/lambdapolsp.cxx index d939c77b84c..45d7fd49b01 100644 --- a/PWGLF/Tasks/Strangeness/lambdapolsp.cxx +++ b/PWGLF/Tasks/Strangeness/lambdapolsp.cxx @@ -2445,7 +2445,7 @@ struct lambdapolsp { // Processing Event Mixing /* using BinningType = ColumnBinningPolicy; - BinningType colBinning{{meGrp.axisVertex, meGrp.axisMultiplicityClass}, true}; + BinningType colBinning{{meGrp.axisVertex, meGrp.axisMultiplicityClass}}; Preslice tracksPerCollisionV0Mixed = o2::aod::v0data::straCollisionId; // for derived data only void processDerivedDataMixed(soa::Join const& collisions, v0Candidates const& V0s, dauTracks const&) diff --git a/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx b/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx index b1546805464..1a3b71e02f8 100644 --- a/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx @@ -59,6 +59,66 @@ using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::soa; +// Compute the competing mass under the K0s (pi+, pi-) hypothesis. +template +static double getK0sCompetingMass(T const& v0) +{ + const ROOT::Math::PtEtaPhiMVector lambda( + v0.lambdaPt(), v0.lambdaEta(), v0.lambdaPhi(), v0.lambdaMass()); + + const ROOT::Math::PtEtaPhiMVector posDaugh( + v0.protonPt(), v0.protonEta(), v0.protonPhi(), + o2::constants::physics::MassProton); + + const auto negDaugh = lambda - posDaugh; + + const ROOT::Math::PtEtaPhiMVector pi1( + posDaugh.Pt(), posDaugh.Eta(), posDaugh.Phi(), + o2::constants::physics::MassPionCharged); + + const ROOT::Math::PtEtaPhiMVector pi2( + negDaugh.Pt(), negDaugh.Eta(), negDaugh.Phi(), + o2::constants::physics::MassPionCharged); + + return (pi1 + pi2).M(); +} + +template +static std::vector getCleanCandidateIndices( + const Container& candidates, + SelectionFunc&& passSelection) +{ + std::vector selected; + selected.reserve(candidates.size()); + + for (auto const& c : candidates) { + if (passSelection(c)) { + selected.push_back(c.globalIndex()); + } + } + + std::vector dirty(selected.size(), false); + for (size_t i = 0; i < selected.size(); ++i) { + for (size_t j = i + 1; j < selected.size(); ++j) { + auto a = candidates.iteratorAt(selected[i]); + auto b = candidates.iteratorAt(selected[j]); + if (hasSharedDaughters(a, b)) { + dirty[i] = true; + dirty[j] = true; + } + } + } + + std::vector clean; + clean.reserve(selected.size()); + for (size_t i = 0; i < selected.size(); ++i) { + if (!dirty[i]) { + clean.push_back(selected[i]); + } + } + return clean; +} + static inline double getKStar( const ROOT::Math::PtEtaPhiMVector& p1, const ROOT::Math::PtEtaPhiMVector& p2) @@ -350,7 +410,19 @@ struct lambdaspincorrderived { Configurable dcaPion{"dcaPion", 0.2, "DCA Pion"}; Configurable dcaDaughters{"dcaDaughters", 1.0, "DCA between daughters"}; Configurable dcaV0ToPV{"dcaV0ToPV", 1.2, "DCA V0 to PV cut on lambda"}; - Configurable v0etaMixBuffer{"v0etaMixBuffer", 0.8, "Eta cut on mix event buffer"}; + Configurable v0etaMixBuffer{"v0etaMixBuffer", 0.5, "Eta cut on mix event buffer"}; + + Configurable useK0sRejection{"useK0sRejection", true, "Apply K0s competing mass rejection"}; + Configurable confK0sMassWindow{"confK0sMassWindow", 0.01f, "K0s competing mass rejection window (GeV/c^2)"}; + + Configurable useAdditionalCut{"useAdditionalCut", false, "Apply additional cut"}; + + // tpcnclustercorssedrows () + Configurable minTpcNClsCrossedRows{"minTpcNClsCrossedRows", 70.f, "Minimum TPC crossed rows for daughter"}; + // ctau (lifetime) + Configurable maxCtau{"maxCtau", 30.f, "Maximum ctau of Lambda candidate (cm)"}; + // TPC nsigma + Configurable maxTpcNsigma{"maxTpcNsigma", 4.f, "Maximum |TPC nsigma| for Lambda daughters"}; } v0Configurations; @@ -543,6 +615,8 @@ struct lambdaspincorrderived { histos.add("deltaPhiMix", "deltaPhiMix", HistType::kTH1D, {{72, -TMath::Pi(), TMath::Pi()}}, true); histos.add("ptCent", "ptCent", HistType::kTH2D, {{100, 0.0, 10.0}, {8, 0.0, 80.0}}, true); histos.add("etaCent", "etaCent", HistType::kTH2D, {{32, -0.8, 0.8}, {8, 0.0, 80.0}}, true); + histos.add("hNMEperSE_leg1", "ME replacements per SE pair, leg1", HistType::kTH1D, {{12, -0.5, 11.5}}, true); + histos.add("hNMEperSE_leg2", "ME replacements per SE pair, leg2", HistType::kTH1D, {{12, -0.5, 11.5}}, true); } if (fillHistConfig.fillWeightQAHistos) { @@ -858,6 +932,35 @@ struct lambdaspincorrderived { if (candidate.lambdaPt() > ptMax) { return false; } + + if (v0Configurations.useAdditionalCut) { + // additional cuts + if (candidate.v0Status() == 0 && (std::abs(candidate.tpcNsigmaPos()) > v0Configurations.maxTpcNsigma || std::abs(candidate.tpcNsigmaNeg()) > v0Configurations.maxTpcNsigma)) { + return false; + } + if (candidate.v0Status() == 1 && (std::abs(candidate.tpcNsigmaNeg()) > v0Configurations.maxTpcNsigma || std::abs(candidate.tpcNsigmaPos()) > v0Configurations.maxTpcNsigma)) { + return false; + } + + if (candidate.tpcPosNClsCrossedRows() < v0Configurations.minTpcNClsCrossedRows) { + return false; + } + + if (candidate.tpcNegNClsCrossedRows() < v0Configurations.minTpcNClsCrossedRows) { + return false; + } + + if (candidate.ctau() > v0Configurations.maxCtau) { + return false; + } + + if (v0Configurations.useK0sRejection) { + if (std::abs(candidate.mK0Short() - o2::constants::physics::MassK0Short) < v0Configurations.confK0sMassWindow) { + return false; + } + } + } + return true; } @@ -3190,6 +3293,8 @@ struct lambdaspincorrderived { ++pendingAdded; } } + histos.fill(HIST("hNMEperSE_leg2"), nFill2); + histos.fill(HIST("hNMEperSE_leg1"), nFill1); if (nFill1 <= 0 && nFill2 <= 0) { continue; diff --git a/PWGLF/Tasks/Strangeness/nonPromptCascade.cxx b/PWGLF/Tasks/Strangeness/nonPromptCascade.cxx index 5ae932534dd..3e0a451e21b 100644 --- a/PWGLF/Tasks/Strangeness/nonPromptCascade.cxx +++ b/PWGLF/Tasks/Strangeness/nonPromptCascade.cxx @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -110,6 +111,8 @@ struct NPCascCandidate { float pionDCAz; float bachDCAxy; float bachDCAz; + float cascDCADaughters; + float v0DCADaughters; float casccosPA; float v0cosPA; double massXi; @@ -233,6 +236,7 @@ struct NonPromptCascadeTask { Configurable cfgMaxMult{"cfgMaxMult", 8000.f, "Upper range of multiplicty histo"}; Configurable cfgMaxMultFV0{"cfgMaxMultFV0", 10000.f, "Upper range of multiplicty FV0 histo"}; Configurable cfgPtEdgesdNdeta{"ptEdges", "0,0.2,0.4,0.6,0.8,1,1.2,1.6,2.0,2.4,2.8,3.2,3.6,4,4.5,5,5.5,6,7,8,10", "Pt bin edges (comma-separated)"}; + Configurable cfgDownscaleCascTF{"cfgDownscaleCascTF", 1, "Keep one in every N input timeframes for cascade reco and generator output (1 keeps all)"}; Configurable cfgDownscaleMB{"cfgDownscaleMB", 1, "Downscaling for pile up study sample"}; Configurable cfgEtaCutdNdeta{"cfgEtaCutdNdeta", 0.8, "Eta cut for charged tracks"}; @@ -241,6 +245,8 @@ struct NonPromptCascadeTask { SliceCache cache; Service mCCDB; + uint64_t mTimeframeCount = 0; + bool mKeepTimeframe = true; int mRunNumber = 0; float mBz = 0.f; o2::vertexing::DCAFitterN<2> mDCAFitter; @@ -272,8 +278,21 @@ struct NonPromptCascadeTask { } } + // The framework calls run once before all enabled process functions for a timeframe. + void run(ProcessingContext&) + { + mKeepTimeframe = (mTimeframeCount++ % static_cast(cfgDownscaleCascTF.value)) == 0; + mRegistry.fill(HIST("hTimeframeSelection"), mKeepTimeframe ? 1.f : 0.f); + } + void init(InitContext& context) { + if (cfgDownscaleCascTF.value < 1) { + LOG(fatal) << "cfgDownscaleCascTF must be at least 1"; + } + auto timeframeSelection = mRegistry.add("hTimeframeSelection", ";Timeframe selection;Timeframes", HistType::kTH1D, {{2, -0.5, 1.5}}); + timeframeSelection->GetXaxis()->SetBinLabel(1, "Skipped"); + timeframeSelection->GetXaxis()->SetBinLabel(2, "Kept"); mZorroSummary.setObject(mZorro.getZorroSummary()); mCCDB->setURL(ccdbUrl); mCCDB->setFatalWhenNull(true); @@ -529,11 +548,14 @@ struct NonPromptCascadeTask { o2::math_utils::SVector cascadePos, v0Pos; float cascCpa = -1, v0Cpa = -1; + float cascDCADaughters = -1.f, v0DCADaughters = -1.f; o2::track::TrackParCov ntCascadeTrack; if (mDCAFitter.process(pionTrkParCov, protonTrkParCov)) { + v0DCADaughters = std::sqrt(mDCAFitter.getChi2AtPCACandidate()); auto trackParCovV0 = mDCAFitter.createParentTrackParCov(0); // V0 track retrieved from p and pi daughters v0Pos = mDCAFitter.getPCACandidate(); if (mDCAFitter.process(trackParCovV0, bachTrkParCov)) { + cascDCADaughters = std::sqrt(mDCAFitter.getChi2AtPCACandidate()); mDCAFitter.getTrackParamAtPCA(0).getPxPyPzGlo(momenta[0]); mDCAFitter.getTrackParamAtPCA(1).getPxPyPzGlo(momenta[1]); ntCascadeTrack = mDCAFitter.createParentTrackParCov(); @@ -655,7 +677,7 @@ struct NonPromptCascadeTask { cascadeLvector.pt(), cascadeLvector.eta(), cascadeLvector.phi(), protonTrack.pt(), protonTrack.eta(), pionTrack.pt(), pionTrack.eta(), bachelor.pt(), bachelor.eta(), motherDCA.getY(), motherDCA.getZ(), protonDCA.getY(), protonDCA.getZ(), pionDCA.getY(), pionDCA.getZ(), bachDCA.getY(), bachDCA.getZ(), - cascCpa, v0Cpa, massXi, massOmega, v0mass, + cascDCADaughters, v0DCADaughters, cascCpa, v0Cpa, massXi, massOmega, v0mass, static_cast(std::hypot(cascadePos[0], cascadePos[1])), static_cast(std::hypot(v0Pos[0], v0Pos[1])), static_cast(std::hypot(cascadePos[0], cascadePos[1], cascadePos[2])), static_cast(std::hypot(v0Pos[0], v0Pos[1], v0Pos[2])), cascITSclusters, protonTrack.itsNCls(), pionTrack.itsNCls(), bachelor.itsNCls(), protonTrack.tpcNClsFound(), pionTrack.tpcNClsFound(), bachelor.tpcNClsFound(), protonTrack.tpcNSigmaPr(), pionTrack.tpcNSigmaPi(), bachelor.tpcNSigmaKa(), bachelor.tpcNSigmaPi(), @@ -673,7 +695,7 @@ struct NonPromptCascadeTask { c.cascPt, c.cascEta, c.cascPhi, c.protonPt, c.protonEta, c.pionPt, c.pionEta, c.bachPt, c.bachEta, c.cascDCAxy, c.cascDCAz, c.protonDCAxy, c.protonDCAz, c.pionDCAxy, c.pionDCAz, c.bachDCAxy, c.bachDCAz, - c.casccosPA, c.v0cosPA, + c.cascDCADaughters, c.v0DCADaughters, c.casccosPA, c.v0cosPA, c.massXi, c.massOmega, c.massV0, c.cascRadius, c.v0radius, c.cascLength, c.v0length, c.cascNClusITS, c.protonNClusITS, c.pionNClusITS, c.bachNClusITS, c.protonNClusTPC, c.pionNClusTPC, c.bachNClusTPC, @@ -712,7 +734,7 @@ struct NonPromptCascadeTask { c.pvContributors, c.cascPVContribs, c.pvTimeResolution, c.pvX, c.pvY, c.pvZ, c.cascPt, c.cascEta, c.cascPhi, c.protonPt, c.protonEta, c.pionPt, c.pionEta, c.bachPt, c.bachEta, c.cascDCAxy, c.cascDCAz, c.protonDCAxy, c.protonDCAz, c.pionDCAxy, c.pionDCAz, c.bachDCAxy, c.bachDCAz, - c.casccosPA, c.v0cosPA, c.massXi, c.massOmega, c.massV0, c.cascRadius, c.v0radius, c.cascLength, c.v0length, + c.cascDCADaughters, c.v0DCADaughters, c.casccosPA, c.v0cosPA, c.massXi, c.massOmega, c.massV0, c.cascRadius, c.v0radius, c.cascLength, c.v0length, c.cascNClusITS, c.protonNClusITS, c.pionNClusITS, c.bachNClusITS, c.protonNClusTPC, c.pionNClusTPC, c.bachNClusTPC, c.protonTPCNSigma, c.pionTPCNSigma, c.bachKaonTPCNSigma, c.bachPionTPCNSigma, c.protonHasTOF, c.pionHasTOF, c.bachHasTOF, c.protonTOFNSigma, c.pionTOFNSigma, c.bachKaonTOFNSigma, c.bachPionTOFNSigma, @@ -748,6 +770,9 @@ struct NonPromptCascadeTask { aod::V0s const& /*v0s*/, TracksExtMC const& tracks, aod::McParticles const& mcParticles, aod::McCollisions const&, aod::BCsWithTimestamps const&) { + if (!mKeepTimeframe) { + return; + } fillCandidatesVector(collisions, tracks, trackedCascades, gCandidates); fillMCtable(mcParticles, collisions, gCandidates); } @@ -757,6 +782,9 @@ struct NonPromptCascadeTask { aod::V0s const& /*v0s*/, TracksExtMC const& tracks, aod::McParticles const& mcParticles, aod::McCollisions const&, aod::BCsWithTimestamps const&) { + if (!mKeepTimeframe) { + return; + } fillCandidatesVector(collisions, tracks, cascades, gCandidatesNT); fillMCtable(mcParticles, collisions, gCandidatesNT); // fillMultHistos(collisions); @@ -765,6 +793,9 @@ struct NonPromptCascadeTask { void processGenParticles(aod::McParticles const& mcParticles, aod::McCollisions const&) { + if (!mKeepTimeframe) { + return; + } for (const auto& p : mcParticles) { auto absCode = std::abs(p.pdgCode()); if (absCode != 3312 && absCode != 3334) { @@ -795,6 +826,9 @@ struct NonPromptCascadeTask { aod::V0s const& /*v0s*/, TracksExtData const& tracks, aod::BCsWithTimestamps const&) { + if (!mKeepTimeframe) { + return; + } mProcessCounter[0]++; zorroAccounting(collisions); fillCandidatesVector(collisions, tracks, trackedCascades, gCandidates); @@ -806,6 +840,9 @@ struct NonPromptCascadeTask { aod::V0s const& /*v0s*/, TracksExtData const& tracks, aod::BCsWithTimestamps const&) { + if (!mKeepTimeframe) { + return; + } mProcessCounter[1]++; zorroAccounting(collisions); fillCandidatesVector(collisions, tracks, cascades, gCandidatesNT); diff --git a/PWGLF/Tasks/Strangeness/phiStrangeCorrelation.cxx b/PWGLF/Tasks/Strangeness/phiStrangeCorrelation.cxx index 81b6ec46eb1..4f95c222ce5 100644 --- a/PWGLF/Tasks/Strangeness/phiStrangeCorrelation.cxx +++ b/PWGLF/Tasks/Strangeness/phiStrangeCorrelation.cxx @@ -361,7 +361,7 @@ struct PhiStrangeCorrelation { ConfigurableAxis axisCentralityMixing{"axisCentralityMixing", {VARIABLE_WIDTH, 0.0f, 1.0f, 5.0f, 10.0f, 15.0f, 20.0f, 30.0f, 40.0f, 50.0f, 70.0f, 100.0f}, "Multiplicity percentage binning for mixing"}; using BinningTypeVertexCent = ColumnBinningPolicy; - BinningTypeVertexCent binningOnVertexAndCent{{axisVertexMixing, axisCentralityMixing}, true}; + BinningTypeVertexCent binningOnVertexAndCent{{axisVertexMixing, axisCentralityMixing}}; static constexpr std::array PhiMassRegionLabels{"Signal", "Sideband"}; static constexpr std::array AssocParticleLabels{"K0S", "Lambda", "AntiLambda", "Xi", "Omega", "Pi"}; @@ -1303,7 +1303,7 @@ struct PhiStrangeCorrelation { histos.fill(histoKey, collision.posZ(), mcCollision.centFT0M(), assoc.pt(), assoc.y()); } - }; + }; // NOLINT(readability/braces) fillRecoAssocSpecies(makeAssocInput(k0sReduced), HIST("k0s/h4K0SMCReco")); fillRecoAssocSpecies(makeAssocInput(lambdaReduced), HIST("lambda/h4LambdaMCReco")); @@ -1337,7 +1337,7 @@ struct PhiStrangeCorrelation { for (const auto& mcParticle : mcParticlesThisMcColl) { if (std::abs(mcParticle.y()) > yConfigs.cfgYAcceptance) { continue; - }; + } auto fillGenHistos = [&](auto h3Key, auto h4Key) { histos.fill(h3Key, mcCollision.centFT0M(), mcParticle.pt(), mcParticle.y()); @@ -1352,7 +1352,7 @@ struct PhiStrangeCorrelation { } fillGenHistos(h3Key, h4Key); - }; + }; // NOLINT(readability/braces) switch (std::abs(mcParticle.pdgCode())) { case o2::constants::physics::Pdg::kPhi: @@ -1404,13 +1404,13 @@ struct PhiStrangeCorrelation { for (const auto& mcParticle : mcParticles) { if (std::abs(mcParticle.y()) > yConfigs.cfgYAcceptance) { continue; - }; + } auto fillPartCollection = [&]() { if (activeCorrelationTypes->at(PartType) && isGenSpeciesValid(mcParticle, minPtMcGenConfigs.bypassPtCut.value)) { assocParticles[PartType].emplace_back(mcParticle.pt(), mcParticle.y(), mcParticle.phi()); } - }; + }; // NOLINT(readability/braces) switch (std::abs(mcParticle.pdgCode())) { case o2::constants::physics::Pdg::kPhi: diff --git a/PWGLF/Tasks/Strangeness/phik0shortanalysis.cxx b/PWGLF/Tasks/Strangeness/phik0shortanalysis.cxx index 51975c776f2..40b9cc698cb 100644 --- a/PWGLF/Tasks/Strangeness/phik0shortanalysis.cxx +++ b/PWGLF/Tasks/Strangeness/phik0shortanalysis.cxx @@ -290,7 +290,7 @@ struct Phik0shortanalysis { ConfigurableAxis axisCentralityMixing{"axisCentralityMixing", {20, 0, 100}, "Multiplicity percentil binning for mixing"}; using BinningTypeVertexCent = ColumnBinningPolicy; - BinningTypeVertexCent binningOnVertexAndCent{{axisVertexMixing, axisCentralityMixing}, true}; + BinningTypeVertexCent binningOnVertexAndCent{{axisVertexMixing, axisCentralityMixing}}; // Cache for manual slicing SliceCache cache; diff --git a/PWGLF/Tasks/Strangeness/sigmaanalysis.cxx b/PWGLF/Tasks/Strangeness/sigmaanalysis.cxx index fae9617f013..b7205446f78 100644 --- a/PWGLF/Tasks/Strangeness/sigmaanalysis.cxx +++ b/PWGLF/Tasks/Strangeness/sigmaanalysis.cxx @@ -21,17 +21,21 @@ #include "PWGLF/DataModel/LFSigmaTables.h" #include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGLF/Utils/ResonanceMlResponse.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/ctpRateFetcher.h" +#include "Tools/ML/MlResponse.h" #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -47,6 +51,7 @@ #include #include +#include #include #include #include @@ -54,6 +59,7 @@ #include using namespace o2; +using namespace o2::ml; using namespace o2::framework; using namespace o2::framework::expressions; @@ -84,7 +90,9 @@ enum CentEstimator { struct sigmaanalysis { Service ccdb; + o2::ccdb::CcdbApi ccdbApi; ctpRateFetcher rateFetcher; + o2::analysis::ResonanceMlResponse mlResponse; //__________________________________________________ HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -100,6 +108,24 @@ struct sigmaanalysis { Configurable fIRCrashOnNull{"fIRCrashOnNull", false, "Flag to avoid CTP RateFetcher crash."}; Configurable irSource{"irSource", "T0VTX", "Estimator of the interaction rate (Recommended: pp --> T0VTX, Pb-Pb --> ZNC hadronic)"}; + struct : ConfigurableGroup { + std::string prefix = "bdt"; // JSON group name + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable> onnxFileNames{"onnxFileNames", std::vector{"BDTModel.onnx"}, "Local .onnx file names, one per pT bin"}; + Configurable> modelPathsCCDB{"modelPathsCCDB", std::vector{"Users/o/obenchik/MLModels/BDT"}, "Model paths on CCDB, one per pT bin (each model needs its own folder)"}; + Configurable timestampCCDB{"timestampCCDB", 1695750420200, "timestamp of the ONNX file for ML model used to query in CCDB. Please use 1695750420200"}; + Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; + Configurable enableOptimizations{"enableOptimizations", false, "Enables the ONNX extended model-optimization: sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED)"}; + Configurable numThreads{"numThreads", 1, "ONNX intra-op threads. 0 lets ONNX Runtime default to one thread per physical core"}; + Configurable enableML{"enableML", false, "Enables bdt model"}; + Configurable> ptBinEdges{"ptBinEdges", {0., 30.}, "Candidate-pT bin edges of the BDT models, one model per bin (candidates outside are rejected)"}; + Configurable> scoreCuts{"scoreCuts", {std::array{0., 0.}.data(), 1, 2, {"pT bin 0"}, {"Background score", "Signal score"}}, "BDT score cuts, one row per pT bin"}; + Configurable> cutDir{"cutDir", std::vector{o2::cuts_ml::CutNot, o2::cuts_ml::CutNot}, "Cut direction per class: 0 = keep score < cut, 1 = keep score >= cut, 2 = no cut"}; + // Configurable mlCorrThreshold{"mlCorrThreshold", 0.5, "Threshold for correlated K* score"}; + Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"lambdaDCADau", "lambdaAlpha", "lambdaDCANegPV", "lambdaDCAPosPV", "lambdaQt", "photonAlpha", "photonCosPA", "photonDCADau", "photonDCANegPV", "photonDCAPosPV", "photonQt", "photonRadius", "opAngle"}, "Names and order of the BDT input features (see ResonanceMlResponse.h): must match FeaturesToTrain"}; + + } bdt; + struct : ConfigurableGroup { std::string prefix = "eventSelections"; // JSON group name Configurable requireSel8{"requireSel8", true, "require sel8 event selection"}; @@ -287,6 +313,8 @@ struct sigmaanalysis { ConfigurableAxis axisV0Radius{"axisV0Radius", {240, 0.0f, 120.0f}, "V0 radius (cm)"}; ConfigurableAxis axisV0PairRadius{"axisV0PairRadius", {200, 0.0f, 20.0f}, "V0Pair radius (cm)"}; ConfigurableAxis axisDCAtoPV{"axisDCAtoPV", {500, 0.0f, 50.0f}, "DCA (cm)"}; + ConfigurableAxis axisSignedDCAtoPV{"axisSignedDCAtoPV", {1000, -50.0f, 50.0f}, "signed DCA (cm)"}; + ConfigurableAxis axisSignedDCAtoPVLambda{"axisSignedDCAtoPVLambda", {500, -10.0f, 10.0f}, "signed DCA (cm)"}; ConfigurableAxis axisDCAdau{"axisDCAdau", {50, 0.0f, 5.0f}, "DCA (cm)"}; ConfigurableAxis axisCosPA{"axisCosPA", {200, 0.5f, 1.0f}, "Cosine of pointing angle"}; ConfigurableAxis axisPA{"axisPA", {100, 0.0f, 1}, "Pointing angle"}; @@ -305,7 +333,7 @@ struct sigmaanalysis { ConfigurableAxis axisCandSel{"axisCandSel", {20, 0.5f, +20.5f}, "Candidate Selection"}; // ML - ConfigurableAxis MLProb{"MLOutput", {100, 0.0f, 1.0f}, ""}; + ConfigurableAxis mlProb{"MLOutput", {100, 0.0f, 1.0f}, ""}; int NSigma0Cand = 0; void init(InitContext const&) @@ -321,6 +349,67 @@ struct sigmaanalysis { ccdb->setCaching(true); ccdb->setFatalWhenNull(false); + if (bdt.enableML) { + ccdb->setURL(bdt.ccdbUrl.value); + + // One model per candidate-pT bin + constexpr uint8_t NClassesML = 2; // background, signal + if (bdt.scoreCuts.value.rows() != bdt.ptBinEdges.value.size() - 1 || bdt.scoreCuts.value.cols() != NClassesML) { + LOG(fatal) << "bdt.scoreCuts needs one row per pT bin and " << static_cast(NClassesML) << " columns"; + } + mlResponse.configure(bdt.ptBinEdges.value, bdt.scoreCuts.value, bdt.cutDir.value, NClassesML); + mlResponse.cacheInputFeaturesIndices(bdt.namesInputFeatures); + + if (bdt.loadModelsFromCCDB) { + ccdbApi.init(bdt.ccdbUrl); + LOG(info) << "Fetching models for timestamp: " << bdt.timestampCCDB.value; + mlResponse.setModelPathsCCDB(bdt.onnxFileNames.value, ccdbApi, bdt.modelPathsCCDB.value, bdt.timestampCCDB.value); + } else { + mlResponse.setModelPathsLocal(bdt.onnxFileNames.value); + } + mlResponse.init(bdt.enableOptimizations.value, bdt.numThreads.value); + + // The model is trained on PCM (V0) photon features, which EMCal clusters do not have + if (doprocessRealDataWithEMCal || doprocessMonteCarloWithEMCal) { + LOG(fatal) << "BDT selection is only available for PCM photons: disable bdt.enableML or use processRealData/processMonteCarlo."; + } + + // BDT performance QA + histos.add("BDT/hScoreSignal", "hScoreSignal", kTH1D, {mlProb}); + histos.add("BDT/hScoreBackground", "hScoreBackground", kTH1D, {mlProb}); + histos.add("BDT/h2dScoreVsMassSignal", "h2dScoreVsMassSignal", kTH2D, {axisSigmaMass, mlProb}); + histos.add("BDT/h2dScoreVsPtSignal", "h2dScoreVsPtSignal", kTH2D, {axisPt, mlProb}); + histos.add("BDT/h3dScoreSignal", "h3dScoreSignal", kTH3D, {axisPt, axisSigmaMass, mlProb}); + histos.add("BDT/h2dScoreVsMassBackground", "h2dScoreVsMassBackground", kTH2D, {axisSigmaMass, mlProb}); + histos.add("BDT/h2dScoreVsPtBackground", "h2dScoreVsPtBackground", kTH2D, {axisPt, mlProb}); + histos.add("BDT/h3dScoreBackground", "h3dScoreBackground", kTH3D, {axisPt, axisSigmaMass, mlProb}); + + // Signal score vs the main topological variables + histos.add("BDT/h2dDCADaughters", "h2dDCADaughters", kTH2D, {mlProb, axisDCAdau}); + histos.add("BDT/h2dLambdaAlpha", "h2dLambdaAlpha", kTH2D, {mlProb, axisAPAlpha}); + histos.add("BDT/h2dLambdaDCANegPV", "h2dLambdaDCANegPV", kTH2D, {mlProb, axisSignedDCAtoPVLambda}); + histos.add("BDT/h2dLambdaDCAPosPV", "h2dLambdaDCAPosPV", kTH2D, {mlProb, axisSignedDCAtoPVLambda}); + histos.add("BDT/h2dLambdaQt", "h2dLambdaQt", kTH2D, {mlProb, axisAPQt}); + histos.add("BDT/h2dPhotonAlpha", "h2dPhotonAlpha", kTH2D, {mlProb, axisAPAlpha}); + histos.add("BDT/h2dPhotonCosPA", "h2dPhotonCosPA", kTH2D, {mlProb, axisCosPA}); + histos.add("BDT/h2dPhotonDCADau", "h2dPhotonDCADau", kTH2D, {mlProb, axisDCAdau}); + histos.add("BDT/h2dPhotonDCANegPV", "h2dPhotonDCANegPV", kTH2D, {mlProb, axisSignedDCAtoPV}); + histos.add("BDT/h2dPhotonDCAPosPV", "h2dPhotonDCAPosPV", kTH2D, {mlProb, axisSignedDCAtoPV}); + histos.add("BDT/h2dPhotonQt", "h2dPhotonQt", kTH2D, {mlProb, axisAPQt}); + histos.add("BDT/h2dPhotonRadius", "h2dPhotonRadius", kTH2D, {mlProb, axisV0Radius}); + histos.add("BDT/h2dOPAngle", "h2dOPAngle", kTH2D, {mlProb, axisOPAngle}); + histos.add("BDT/h2dAPAlpha", "h2dAPAlpha", kTH2D, {mlProb, axisAPAlpha}); + histos.add("BDT/h2dAPQt", "h2dAPQt", kTH2D, {mlProb, axisAPQt}); + + // MC-truth-based score + if (doprocessMonteCarlo) { + histos.add("BDT/hScoreTrueSignal", "hScoreTrueSignal", kTH1D, {mlProb}); + histos.add("BDT/hScoreTrueBackground", "hScoreTrueBackground", kTH1D, {mlProb}); + histos.add("BDT/h2dScoreVsPtTrueSignal", "h2dScoreVsPtTrueSignal", kTH2D, {axisPt, mlProb}); + histos.add("BDT/h2dScoreVsPtTrueBackground", "h2dScoreVsPtTrueBackground", kTH2D, {axisPt, mlProb}); + } + } + // Event Counters histos.add("hEventCentrality", "hEventCentrality", kTH1D, {axisCentrality}); histos.add("hCentralityVsNch", "hCentralityVsNch", kTH2D, {{101, 0.0f, 101.0f}, axisNch}); @@ -1652,10 +1741,84 @@ struct sigmaanalysis { return true; } + // Fill BDT performance QA + template + void fillBDTPerformance(TSigma0Object const& cand, float score) + { + float pt = cand.pt(); + float mass = cand.sigma0Mass(); + float bkgScore = 1.0f - score; + + // Signal-probability output + histos.fill(HIST("BDT/hScoreSignal"), score); + histos.fill(HIST("BDT/h2dScoreVsMassSignal"), mass, score); + histos.fill(HIST("BDT/h2dScoreVsPtSignal"), pt, score); + histos.fill(HIST("BDT/h3dScoreSignal"), pt, mass, score); + + // Background-probability output + histos.fill(HIST("BDT/hScoreBackground"), bkgScore); + histos.fill(HIST("BDT/h2dScoreVsMassBackground"), mass, bkgScore); + histos.fill(HIST("BDT/h2dScoreVsPtBackground"), pt, bkgScore); + histos.fill(HIST("BDT/h3dScoreBackground"), pt, mass, bkgScore); + + // Signal score vs the main topological variables + histos.fill(HIST("BDT/h2dDCADaughters"), score, cand.lambdaDCADau()); + histos.fill(HIST("BDT/h2dLambdaAlpha"), score, cand.lambdaAlpha()); + histos.fill(HIST("BDT/h2dLambdaDCANegPV"), score, cand.lambdaDCANegPV()); + histos.fill(HIST("BDT/h2dLambdaDCAPosPV"), score, cand.lambdaDCAPosPV()); + histos.fill(HIST("BDT/h2dLambdaQt"), score, cand.lambdaQt()); + histos.fill(HIST("BDT/h2dPhotonAlpha"), score, cand.photonAlpha()); + histos.fill(HIST("BDT/h2dPhotonCosPA"), score, cand.photonCosPA()); + histos.fill(HIST("BDT/h2dPhotonDCADau"), score, cand.photonDCADau()); + histos.fill(HIST("BDT/h2dPhotonDCANegPV"), score, cand.photonDCANegPV()); + histos.fill(HIST("BDT/h2dPhotonDCAPosPV"), score, cand.photonDCAPosPV()); + histos.fill(HIST("BDT/h2dPhotonQt"), score, cand.photonQt()); + histos.fill(HIST("BDT/h2dPhotonRadius"), score, cand.photonRadius()); + histos.fill(HIST("BDT/h2dOPAngle"), score, cand.opAngle()); + histos.fill(HIST("BDT/h2dAPAlpha"), score, cand.lStarAlpha()); + histos.fill(HIST("BDT/h2dAPQt"), score, cand.lStarQtarm()); + + // MC-truth-based separation (signal = particle + antiparticle) + if constexpr (requires { cand.isSigma0(); cand.isLambdaStar(); }) { + bool isTrueSignal = doLambdaStar ? (cand.isLambdaStar() || cand.isAntiLambdaStar()) : (cand.isSigma0() || cand.isAntiSigma0()); + if (isTrueSignal) { + histos.fill(HIST("BDT/hScoreTrueSignal"), score); + histos.fill(HIST("BDT/h2dScoreVsPtTrueSignal"), pt, score); + } else { + histos.fill(HIST("BDT/hScoreTrueBackground"), score); + histos.fill(HIST("BDT/h2dScoreVsPtTrueBackground"), pt, score); + } + } + } + + template + bool selectML(TSigma0Object const& cand) + { + // No model outside the bdt.ptBinEdges range + const float pt = cand.pt(); + if (pt < bdt.ptBinEdges.value.front() || pt >= bdt.ptBinEdges.value.back()) + return false; + + // Features in the order of bdt.namesInputFeatures + auto inputFeatures = mlResponse.getInputFeatures(cand, cand, cand.opAngle(), cand.lStarAlpha(), cand.lStarQtarm()); + std::vector outputMl; // [background, signal] + const bool isSelected = mlResponse.isSelectedMl(inputFeatures, pt, outputMl); // model and cut of the pT bin + + fillBDTPerformance(cand, outputMl[1]); + + return isSelected; + } + // Apply selections in sigma0 candidates template bool processSigma0Candidate(TSigma0Object const& cand) { + // BDT selection: model trained on PCM photon features only + if constexpr (requires { cand.photonV0Type(); }) { + if (bdt.enableML && !selectML(cand)) + return false; + } + // Photon specific selections if constexpr (requires { cand.photonV0Type(); }) { // Processing PCM photon if (!selectPhoton(cand)) diff --git a/PWGLF/Tasks/Strangeness/strangenessInJets.cxx b/PWGLF/Tasks/Strangeness/strangenessInJets.cxx index 245ce30e294..54b8fc923cf 100644 --- a/PWGLF/Tasks/Strangeness/strangenessInJets.cxx +++ b/PWGLF/Tasks/Strangeness/strangenessInJets.cxx @@ -444,6 +444,15 @@ struct StrangenessInJets { registryMC.add("OmegaPos_reconstructed_ue", "OmegaPos_reconstructed_ue", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("OmegaNeg_reconstructed_jet", "OmegaNeg_reconstructed_jet", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("OmegaNeg_reconstructed_ue", "OmegaNeg_reconstructed_ue", HistType::kTH2F, {multAxis, ptAxis}); + // Generated cascades associated to reconstructed jet / UE axes. + registryMC.add("XiPos_generated_recojet_jet", "XiPos_generated_recojet_jet", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiPos_generated_recojet_ue", "XiPos_generated_recojet_ue", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiNeg_generated_recojet_jet", "XiNeg_generated_recojet_jet", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiNeg_generated_recojet_ue", "XiNeg_generated_recojet_ue", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaPos_generated_recojet_jet", "OmegaPos_generated_recojet_jet", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaPos_generated_recojet_ue", "OmegaPos_generated_recojet_ue", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaNeg_generated_recojet_jet", "OmegaNeg_generated_recojet_jet", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaNeg_generated_recojet_ue", "OmegaNeg_generated_recojet_ue", HistType::kTH2F, {multAxis, ptAxis}); // Histograms to calculate probability of hyperons to be found within jets registryMC.add("XiPos_reconstructed_fullevent", "XiPos_reconstructed_fullevent", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("XiNeg_reconstructed_fullevent", "XiNeg_reconstructed_fullevent", HistType::kTH2F, {multAxis, ptAxis}); @@ -1891,7 +1900,6 @@ struct StrangenessInJets { continue; if (!motherPos.isPhysicalPrimary()) continue; - double maxPseudorap = 0.8; if (std::abs(motherPos.eta()) > maxPseudorap) continue; @@ -1911,7 +1919,6 @@ struct StrangenessInJets { } for (auto const& particle : mcParticlesPerColl) { - double maxPseudorap = 0.8; if (particle.isPhysicalPrimary() && std::abs(particle.eta()) <= maxPseudorap) { switch (particle.pdgCode()) { case kK0Short: @@ -1974,6 +1981,50 @@ struct StrangenessInJets { } } break; + + // Cascades: generated particles classified with respect to the + // reconstructed jet and perpendicular-cone axes, exactly as for V0s. + case kXiPlusBar: + if (enabledSignals.value[ParticleOfInterest::kCascades]) { + if (positionMC.isInJet()) { + registryMC.fill(HIST("XiPos_generated_recojet_jet"), multiplicity, particle.pt()); + } + if (positionMC.isInUE1() || positionMC.isInUE2()) { + registryMC.fill(HIST("XiPos_generated_recojet_ue"), multiplicity, particle.pt()); + } + } + break; + case kXiMinus: + if (enabledSignals.value[ParticleOfInterest::kCascades]) { + if (positionMC.isInJet()) { + registryMC.fill(HIST("XiNeg_generated_recojet_jet"), multiplicity, particle.pt()); + } + if (positionMC.isInUE1() || positionMC.isInUE2()) { + registryMC.fill(HIST("XiNeg_generated_recojet_ue"), multiplicity, particle.pt()); + } + } + break; + case kOmegaPlusBar: + if (enabledSignals.value[ParticleOfInterest::kCascades]) { + if (positionMC.isInJet()) { + registryMC.fill(HIST("OmegaPos_generated_recojet_jet"), multiplicity, particle.pt()); + } + if (positionMC.isInUE1() || positionMC.isInUE2()) { + registryMC.fill(HIST("OmegaPos_generated_recojet_ue"), multiplicity, particle.pt()); + } + } + break; + case kOmegaMinus: + if (enabledSignals.value[ParticleOfInterest::kCascades]) { + if (positionMC.isInJet()) { + registryMC.fill(HIST("OmegaNeg_generated_recojet_jet"), multiplicity, particle.pt()); + } + if (positionMC.isInUE1() || positionMC.isInUE2()) { + registryMC.fill(HIST("OmegaNeg_generated_recojet_ue"), multiplicity, particle.pt()); + } + } + break; + default: break; } diff --git a/PWGLF/Tasks/Strangeness/strangenessInJetsIons.cxx b/PWGLF/Tasks/Strangeness/strangenessInJetsIons.cxx index 47769cced1e..974350c21dd 100644 --- a/PWGLF/Tasks/Strangeness/strangenessInJetsIons.cxx +++ b/PWGLF/Tasks/Strangeness/strangenessInJetsIons.cxx @@ -69,6 +69,7 @@ #include #include #include +#include #include #include #include @@ -2049,7 +2050,7 @@ struct StrangenessInJetsIons { bool isV0 = isK0S || isLambda || isAntiLambda; if (!isV0) continue; - for (long unsigned int i = 0; i < fjTracks.size(); ++i) { + for (uint64_t i = 0; i < fjTracks.size(); ++i) { if (isV0DaughterTrack(fjTracks[i], v0)) { // LOG(info) << "[AddV0sForJetReconstructionData] V0 daughter track found in fjTracks."; isTrackReplaced[i] = true; @@ -2059,7 +2060,7 @@ struct StrangenessInJetsIons { std::vector cleanFjInput; cleanFjInput.reserve(fjInput.size()); - for (long unsigned int i = 0; i < fjInput.size(); ++i) { + for (uint64_t i = 0; i < fjInput.size(); ++i) { if (!isTrackReplaced[i]) cleanFjInput.push_back(fjInput[i]); } @@ -2147,7 +2148,7 @@ struct StrangenessInJetsIons { bool isV0 = isK0S || isLambda || isAntiLambda; if (!isV0) continue; - for (long unsigned int i = 0; i < fjTracks.size(); ++i) { + for (uint64_t i = 0; i < fjTracks.size(); ++i) { if (isV0DaughterTrack(fjTracks[i], v0)) { // LOG(info) << "[AddV0sForJetReconstructionMCD] V0 daughter track found in fjTracks."; isTrackReplaced[i] = true; @@ -2157,7 +2158,7 @@ struct StrangenessInJetsIons { std::vector cleanFjInput; cleanFjInput.reserve(fjInput.size()); - for (long unsigned int i = 0; i < fjInput.size(); ++i) { + for (uint64_t i = 0; i < fjInput.size(); ++i) { if (!isTrackReplaced[i]) cleanFjInput.push_back(fjInput[i]); } @@ -2205,7 +2206,7 @@ struct StrangenessInJetsIons { } // Remove V0 daughter particles if already in the input list for the jet finder - for (long unsigned int i = 0; i < fjParticleObj.size(); ++i) { + for (uint64_t i = 0; i < fjParticleObj.size(); ++i) { const auto& mcPart = fjParticleObj[i]; if (!mcPart.has_mothers()) continue; @@ -2220,7 +2221,7 @@ struct StrangenessInJetsIons { std::vector cleanFjInput; cleanFjInput.reserve(fjInput.size()); - for (long unsigned int i = 0; i < fjInput.size(); ++i) { + for (uint64_t i = 0; i < fjInput.size(); ++i) { if (!isTrackReplaced[i]) cleanFjInput.push_back(fjInput[i]); } @@ -2341,7 +2342,7 @@ struct StrangenessInJetsIons { double minDeltaR = maxDeltaR; // Search closest jet RECO in (eta,phi) space - for (long unsigned int iReco = 0; iReco < jetsReco.size(); ++iReco) { + for (uint64_t iReco = 0; iReco < jetsReco.size(); ++iReco) { if (isRecoJetMatched[iReco]) continue; diff --git a/PWGLF/Tasks/Strangeness/taskLambdaSpinCorr.cxx b/PWGLF/Tasks/Strangeness/taskLambdaSpinCorr.cxx index 9fcac3ab845..e0c175fae1e 100644 --- a/PWGLF/Tasks/Strangeness/taskLambdaSpinCorr.cxx +++ b/PWGLF/Tasks/Strangeness/taskLambdaSpinCorr.cxx @@ -575,7 +575,7 @@ struct LfTaskLambdaSpinCorr { // Processing Event Mixing SliceCache cache; using BinningType = ColumnBinningPolicy; - BinningType colBinning{{axisVertex, axisMultiplicityClass}, true}; + BinningType colBinning{{axisVertex, axisMultiplicityClass}}; Preslice tracksPerCollisionV0 = aod::v0data::collisionId; void processME(EventCandidates const& collisions, AllTrackCandidates const&, ResoV0s const& V0s) { diff --git a/PWGLF/Tasks/Strangeness/v0ptinvmassplots.cxx b/PWGLF/Tasks/Strangeness/v0ptinvmassplots.cxx index e8f028b5f3c..de414884670 100644 --- a/PWGLF/Tasks/Strangeness/v0ptinvmassplots.cxx +++ b/PWGLF/Tasks/Strangeness/v0ptinvmassplots.cxx @@ -68,6 +68,9 @@ static std::vector antilambdaPtBins; std::vector> kaonSplit; std::vector> lambdaSplit; std::vector> antilambdaSplit; +std::vector> kaonSplitFeeddown; +std::vector> lambdaSplitFeeddown; +std::vector> antilambdaSplitFeeddown; } // namespace pthistos using namespace o2; using namespace o2::framework; @@ -104,57 +107,75 @@ struct V0PtInvMassPlots { Configurable itsMinHits{"itsMinHits", 1.0, "Minimum Hits of Daughter Tracks in the ITS"}; // Configurables switches for event selection - Configurable dosel8{"dosel8", true, "Enable sel8 event selection"}; - Configurable doNoTimeFrameBorder{"doNoTimeFrameBorder", true, "Enable NoTimeFrameBorder event selection"}; - Configurable doNoITSROFrameBorder{"doNoITSROFrameBorder", true, "Enable NoITSROFrameBorder event selection"}; - Configurable doIsTriggerTVX{"doIsTriggerTVX", true, "Enable IsTriggerTVX event selection"}; - Configurable docutZVertex{"docutZVertex", true, "Enable cutZVertex event selection"}; - Configurable doIsVertexTOFmatched{"doIsVertexTOFmatched", true, "Enable IsVertexTOFmatched event selection"}; - Configurable doNoSameBunchPileup{"doNoSameBunchPileup", true, "Enable NoSameBunchPileup event selection"}; - Configurable doIsVertexITSTPC{"doIsVertexITSTPC", true, "Enable IsVertexITSTPC event selection"}; - Configurable doisInelGt0{"doisInelGt0", true, "Enable isInelGt0 event selection"}; + struct EventSelectionConfig : ConfigurableGroup { + std::string prefix = "eventSelections"; // JSON group name + Configurable dosel8{"dosel8", true, "Enable sel8 event selection"}; + Configurable doNoTimeFrameBorder{"doNoTimeFrameBorder", true, "Enable NoTimeFrameBorder event selection"}; + Configurable doNoITSROFrameBorder{"doNoITSROFrameBorder", true, "Enable NoITSROFrameBorder event selection"}; + Configurable doIsTriggerTVX{"doIsTriggerTVX", true, "Enable IsTriggerTVX event selection"}; + Configurable docutZVertex{"docutZVertex", true, "Enable cutZVertex event selection"}; + Configurable doIsVertexTOFmatched{"doIsVertexTOFmatched", true, "Enable IsVertexTOFmatched event selection"}; + Configurable doNoSameBunchPileup{"doNoSameBunchPileup", true, "Enable NoSameBunchPileup event selection"}; + Configurable doIsVertexITSTPC{"doIsVertexITSTPC", true, "Enable IsVertexITSTPC event selection"}; + Configurable doisInelGt0{"doisInelGt0", true, "Enable isInelGt0 event selection"}; + Configurable doZvertexGenCut{"doZvertexGenCut", false, "Enable Generated Z vertex cut"}; + }; // Configurables switches for v0 selection - Configurable doRapidityCut{"doRapidityCut", true, "Enable rapidity v0 selection"}; - Configurable doDaughterPseudorapidityCut{"doDaughterPseudorapidityCut", true, "Enable Daughter pseudorapidity v0 selection"}; - Configurable doisNotITSAfterburner{"doisNotITSAfterburner", true, "Enable Tracks do not come from Afterburner"}; - Configurable doitsMinHits{"doitsMinHits", true, "Enable ITS Minimum hits"}; + struct V0SelectionConfig : ConfigurableGroup { + std::string prefix = "v0Selections"; // JSON group name + Configurable doDaughterPseudorapidityCut{"doDaughterPseudorapidityCut", true, "Enable Daughter pseudorapidity v0 selection"}; + Configurable doisNotITSAfterburner{"doisNotITSAfterburner", true, "Enable Tracks do not come from Afterburner"}; + Configurable doitsMinHits{"doitsMinHits", true, "Enable ITS Minimum hits"}; + }; // Configurables switches for K0sh selection - Configurable dotruthK0sh{"dotruthK0sh", true, "Enable K0sh MC Matching"}; - Configurable doK0shTPCPID{"doK0shTPCPID", true, "Enable K0sh TPC PID"}; - Configurable doK0shcomptmasscut{"doK0shcomptmasscut", true, "Enable K0sh Competitive V0 Mass Cut"}; - Configurable doK0shMaxct{"doK0shMaxct", true, "Enable K0sh Max ct Cut"}; - Configurable doK0shArmenterosCut{"doK0shArmenterosCut", true, "Enable K0sh Armenteros Cut"}; - Configurable doK0shcosPACut{"doK0shcosPACut", true, "Enable K0sh cosPA Topological Cut"}; - Configurable doK0shDCAdauCut{"doK0shDCAdauCut", true, "Enable K0sh DCA daughters Topological Cut"}; - Configurable doK0shv0radiusCut{"doK0shv0radiusCut", true, "Enable K0sh v0radius Topological Cut"}; - Configurable doK0shdcaposdautopv{"doK0shdcaposdautopv", true, "Enable K0sh DCA pos daughter to PV Topological Cut"}; - Configurable doK0shdcanegdautopv{"doK0shdcanegdautopv", true, "Enable K0sh DCA neg daughter to PV Topological Cut"}; + struct K0shSelectionConfig : ConfigurableGroup { + std::string prefix = "k0shSelections"; // JSON group name + Configurable doK0shRapidityCut{"doK0shRapidityCut", true, "Enable rapidity K0sh selection"}; + Configurable dotruthK0sh{"dotruthK0sh", true, "Enable K0sh MC Matching"}; + Configurable doK0shTPCPID{"doK0shTPCPID", true, "Enable K0sh TPC PID"}; + Configurable doK0shcomptmasscut{"doK0shcomptmasscut", true, "Enable K0sh Competitive V0 Mass Cut"}; + Configurable doK0shMaxct{"doK0shMaxct", true, "Enable K0sh Max ct Cut"}; + Configurable doK0shArmenterosCut{"doK0shArmenterosCut", true, "Enable K0sh Armenteros Cut"}; + Configurable doK0shcosPACut{"doK0shcosPACut", true, "Enable K0sh cosPA Topological Cut"}; + Configurable doK0shDCAdauCut{"doK0shDCAdauCut", true, "Enable K0sh DCA daughters Topological Cut"}; + Configurable doK0shv0radiusCut{"doK0shv0radiusCut", true, "Enable K0sh v0radius Topological Cut"}; + Configurable doK0shdcaposdautopv{"doK0shdcaposdautopv", true, "Enable K0sh DCA pos daughter to PV Topological Cut"}; + Configurable doK0shdcanegdautopv{"doK0shdcanegdautopv", true, "Enable K0sh DCA neg daughter to PV Topological Cut"}; + }; // Configurables switches for Lambda selection - Configurable dotruthLambda{"dotruthLambda", true, "Enable Lambda MC Matching"}; - Configurable doLambdaTPCPID{"doLambdaTPCPID", true, "Enable Lambda TPC PID"}; - Configurable doLambdacomptmasscut{"doLambdacomptmasscut", true, "Enable Lambda Competitive V0 Mass Cut"}; - Configurable doLambdaMaxct{"doLambdaMaxct", true, "Enable Lambda Max ct Cut"}; - Configurable doLambdaArmenterosCut{"doLambdaArmenterosCut", true, "Enable Lambda Armenteros Cut"}; - Configurable doLambdacosPACut{"doLambdacosPACut", true, "Enable Lambda cosPA Topological Cut"}; - Configurable doLambdaDCAdauCut{"doLambdaDCAdauCut", true, "Enable Lambda DCA daughters Topological Cut"}; - Configurable doLambdav0radiusCut{"doLambdav0radiusCut", true, "Enable Lambda v0radius Topological Cut"}; - Configurable doLambdadcaposdautopv{"doLambdadcaposdautopv", true, "Enable Lambda DCA pos daughter to PV Topological Cut"}; - Configurable doLambdadcanegdautopv{"doLambdadcanegdautopv", true, "Enable Lambda DCA neg daughter to PV Topological Cut"}; + struct LambdaSelectionConfig : ConfigurableGroup { + std::string prefix = "lambdaSelections"; // JSON group name + Configurable doLambdaRapidityCut{"doLambdaRapidityCut", true, "Enable rapidity Lambda selection"}; + Configurable dotruthLambda{"dotruthLambda", true, "Enable Lambda MC Matching"}; + Configurable doLambdaTPCPID{"doLambdaTPCPID", true, "Enable Lambda TPC PID"}; + Configurable doLambdacomptmasscut{"doLambdacomptmasscut", true, "Enable Lambda Competitive V0 Mass Cut"}; + Configurable doLambdaMaxct{"doLambdaMaxct", true, "Enable Lambda Max ct Cut"}; + Configurable doLambdaArmenterosCut{"doLambdaArmenterosCut", true, "Enable Lambda Armenteros Cut"}; + Configurable doLambdacosPACut{"doLambdacosPACut", true, "Enable Lambda cosPA Topological Cut"}; + Configurable doLambdaDCAdauCut{"doLambdaDCAdauCut", true, "Enable Lambda DCA daughters Topological Cut"}; + Configurable doLambdav0radiusCut{"doLambdav0radiusCut", true, "Enable Lambda v0radius Topological Cut"}; + Configurable doLambdadcaposdautopv{"doLambdadcaposdautopv", true, "Enable Lambda DCA pos daughter to PV Topological Cut"}; + Configurable doLambdadcanegdautopv{"doLambdadcanegdautopv", true, "Enable Lambda DCA neg daughter to PV Topological Cut"}; + }; // Configurables switches for AntiLambda selection - Configurable dotruthAntiLambda{"dotruthAntiLambda", true, "Enable AntiLambda MC Matching"}; - Configurable doAntilambdaTPCPID{"doAntilambdaTPCPID", true, "Enable AntiLambda TPC PID"}; - Configurable doAntilambdacomptmasscut{"doAntilambdacomptmasscut", true, "Enable AntiLambda Competitive V0 Mass Cut"}; - Configurable doAntilambdaMaxct{"doAntilambdaMaxct", true, "Enable AntiLambda Max ct Cut"}; - Configurable doAntilambdaArmenterosCut{"doAntilambdaArmenterosCut", true, "Enable AntiLambda Armenteros Cut"}; - Configurable doAntilambdacosPACut{"doAntilambdacosPACut", true, "Enable AntiLambda cosPA Topological Cut"}; - Configurable doAntilambdaDCAdauCut{"doAntilambdaDCAdauCut", true, "Enable AntiLambda DCA daughters Topological Cut"}; - Configurable doAntilambdav0radiusCut{"doAntilambdav0radiusCut", true, "Enable AntiLambda v0radius Topological Cut"}; - Configurable doAntilambdadcaposdautopv{"doAntilambdadcaposdautopv", true, "Enable AntiLambda DCA pos daughter to PV Topological Cut"}; - Configurable doAntilambdadcanegdautopv{"doAntilambdadcanegdautopv", true, "Enable AntiLambda DCA neg daughter to PV Topological Cut"}; + struct AntiLambdaSelectionConfig : ConfigurableGroup { + std::string prefix = "antilambdaSelections"; // JSON group name + Configurable doAntiLambdaRapidityCut{"doAntiLambdaRapidityCut", true, "Enable rapidity AntiLambda selection"}; + Configurable dotruthAntiLambda{"dotruthAntiLambda", true, "Enable AntiLambda MC Matching"}; + Configurable doAntilambdaTPCPID{"doAntilambdaTPCPID", true, "Enable AntiLambda TPC PID"}; + Configurable doAntilambdacomptmasscut{"doAntilambdacomptmasscut", true, "Enable AntiLambda Competitive V0 Mass Cut"}; + Configurable doAntilambdaMaxct{"doAntilambdaMaxct", true, "Enable AntiLambda Max ct Cut"}; + Configurable doAntilambdaArmenterosCut{"doAntilambdaArmenterosCut", true, "Enable AntiLambda Armenteros Cut"}; + Configurable doAntilambdacosPACut{"doAntilambdacosPACut", true, "Enable AntiLambda cosPA Topological Cut"}; + Configurable doAntilambdaDCAdauCut{"doAntilambdaDCAdauCut", true, "Enable AntiLambda DCA daughters Topological Cut"}; + Configurable doAntilambdav0radiusCut{"doAntilambdav0radiusCut", true, "Enable AntiLambda v0radius Topological Cut"}; + Configurable doAntilambdadcaposdautopv{"doAntilambdadcaposdautopv", true, "Enable AntiLambda DCA pos daughter to PV Topological Cut"}; + Configurable doAntilambdadcanegdautopv{"doAntilambdadcanegdautopv", true, "Enable AntiLambda DCA neg daughter to PV Topological Cut"}; + }; // Configurable K0sh Cuts (best cuts determined by v0topologicalcuts task) Configurable k0shSettingdcav0dau{"k0shSettingdcav0dau", 0.3, "DCA V0 Daughters"}; @@ -206,12 +227,15 @@ struct V0PtInvMassPlots { int nLambdaHistograms = pthistos::lambdaPtBins.size() - 1; int nAntilambdaHistograms = pthistos::antilambdaPtBins.size() - 1; - pthistos::kaonPt.resize(nKaonHistograms); // number of Kaon Pt histograms to expect - pthistos::lambdaPt.resize(nLambdaHistograms); // number of Lambda histograms to expect - pthistos::antilambdaPt.resize(nAntilambdaHistograms); // number of AntiLambda histograms to expect - pthistos::kaonSplit.resize(nKaonHistograms); // number of Kaon Split Pt histograms to expect - pthistos::lambdaSplit.resize(nLambdaHistograms); // number of Lambda Split Pt histograms to expect - pthistos::antilambdaSplit.resize(nAntilambdaHistograms); // number of AntiLambda Split Pt histograms to expect + pthistos::kaonPt.resize(nKaonHistograms); // number of Kaon Pt histograms to expect + pthistos::lambdaPt.resize(nLambdaHistograms); // number of Lambda histograms to expect + pthistos::antilambdaPt.resize(nAntilambdaHistograms); // number of AntiLambda histograms to expect + pthistos::kaonSplit.resize(nKaonHistograms); // number of Kaon Split Pt histograms to expect + pthistos::lambdaSplit.resize(nLambdaHistograms); // number of Lambda Split Pt histograms to expect + pthistos::antilambdaSplit.resize(nAntilambdaHistograms); // number of AntiLambda Split Pt histograms to expect + pthistos::kaonSplitFeeddown.resize(nKaonHistograms); // number of Kaon Split Pt histograms to expect + pthistos::lambdaSplitFeeddown.resize(nLambdaHistograms); // number of Lambda Split Pt histograms to expect + pthistos::antilambdaSplitFeeddown.resize(nAntilambdaHistograms); // number of AntiLambda Split Pt histograms to expect // initialize and convert tokenized strings into vector of doubles for AxisSpec std::vector kaonptedgevalues(pthistos::kaonPtBins.size()); @@ -292,6 +316,7 @@ struct V0PtInvMassPlots { for (int i = 0; i < nKaonHistograms; i++) { pthistos::kaonPt[i] = rK0shMassPlotsPerPtBin.add(fmt::format("hK0shPt_vs_Cent_from_{0}_to_{1}", kaonhistvalue[i], kaonhistvalue[i + 1]).c_str(), fmt::format("K0s mass vs centrality, pT from {0} to {1}", kaonhistvalue[i], kaonhistvalue[i + 1]).c_str(), {HistType::kTH2D, {{k0ShortMassAxis}, {centAxis}}}); pthistos::kaonSplit[i] = rK0shSplitMassPlotsPerPtBin.add(fmt::format("hK0shSplitPt_vs_Cent_from_{0}_to_{1}", kaonhistvalue[i], kaonhistvalue[i + 1]).c_str(), fmt::format("Split K0s mass vs centrality, pT from {0} to {1}", kaonhistvalue[i], kaonhistvalue[i + 1]).c_str(), {HistType::kTH2D, {{k0ShortMassAxis}, {centAxis}}}); + pthistos::kaonSplitFeeddown[i] = rK0shSplitMassPlotsPerPtBin.add(fmt::format("hK0shSplitFeeddownPt_vs_Cent_from_{0}_to_{1}", kaonhistvalue[i], kaonhistvalue[i + 1]).c_str(), fmt::format("Feeddown Split K0s mass vs centrality, pT from {0} to {1}", kaonhistvalue[i], kaonhistvalue[i + 1]).c_str(), {HistType::kTH2D, {{k0ShortMassAxis}, {centAxis}}}); } rFeeddownMatrices.add("hK0shFeeddownMatrix", "hK0shFeeddownMatrix", {HistType::kTH3D, {k0ShortPtAxis, k0ShortPtAxis, centAxis}}); rFeeddownMatrices.add("hK0shPhiFeeddownMatrix", "hK0shPhiFeeddownMatrix", {HistType::kTH3D, {k0ShortPtAxis, k0ShortPtAxis, centAxis}}); @@ -299,7 +324,7 @@ struct V0PtInvMassPlots { // Adding Lambda Histograms if (lambdaAnalysis == true) { // same method as in K0sh above - rPtAnalysis.add("hMassLambdavsCuts", "hMassLambdavsCuts", {HistType::kTH2F, {{partCutsAxis}, {k0ShortMassAxis}}}); + rPtAnalysis.add("hMassLambdavsCuts", "hMassLambdavsCuts", {HistType::kTH2F, {{partCutsAxis}, {lambdaMassAxis}}}); rPtAnalysis.add("hArmenterosPodolanskiPlotLambda", "hArmenterosPodolanskiPlotLambda", {HistType::kTH2F, {{armenterosasymAxis}, {armenterosQtAxis}}}); rPtAnalysis.add("hNSigmaPosProtonFromLambdas", "hNSigmaPosProtonFromLambdas", {HistType::kTH2F, {{100, -5.f, 5.f}, {lambdaPtAxis}}}); rPtAnalysis.add("hNSigmaNegPionFromLambdas", "hNSigmaNegPionFromLambdas", {HistType::kTH2F, {{100, -5.f, 5.f}, {lambdaPtAxis}}}); @@ -312,6 +337,7 @@ struct V0PtInvMassPlots { for (int i = 0; i < nLambdaHistograms; i++) { pthistos::lambdaPt[i] = rLambdaMassPlotsPerPtBin.add(fmt::format("hLambdaPt_vs_Cent_from_{0}_to_{1}", lambdahistvalue[i], lambdahistvalue[i + 1]).c_str(), fmt::format("Lambda mass vs centrality, pT from {0} to {1}", lambdahistvalue[i], lambdahistvalue[i + 1]).c_str(), {HistType::kTH2D, {{lambdaMassAxis}, {centAxis}}}); pthistos::lambdaSplit[i] = rLambdaSplitMassPlotsPerPtBin.add(fmt::format("hLambdaSplitPt_vs_Cent_from_{0}_to_{1}", lambdahistvalue[i], lambdahistvalue[i + 1]).c_str(), fmt::format("Split Lambda mass vs centrality, pT from {0} to {1}", lambdahistvalue[i], lambdahistvalue[i + 1]).c_str(), {HistType::kTH2D, {{lambdaMassAxis}, {centAxis}}}); + pthistos::lambdaSplitFeeddown[i] = rLambdaSplitMassPlotsPerPtBin.add(fmt::format("hLambdaSplitFeeddownPt_vs_Cent_from_{0}_to_{1}", lambdahistvalue[i], lambdahistvalue[i + 1]).c_str(), fmt::format("Feeddown Split Lambda mass vs centrality, pT from {0} to {1}", lambdahistvalue[i], lambdahistvalue[i + 1]).c_str(), {HistType::kTH2D, {{lambdaMassAxis}, {centAxis}}}); } // lambdafeeddown matrices rFeeddownMatrices.add("hLambdaFeeddownMatrix", "hLambdaFeeddownMatrix", {HistType::kTH3D, {lambdaPtAxis, lambdaPtAxis, centAxis}}); @@ -322,7 +348,7 @@ struct V0PtInvMassPlots { // Adding AntiLambda Histograms if (antiLambdaAnalysis == true) { // same method as in Lambda and K0sh above - rPtAnalysis.add("hMassAntiLambdavsCuts", "hMassAntiLambdavsCuts", {HistType::kTH2F, {{partCutsAxis}, {k0ShortMassAxis}}}); + rPtAnalysis.add("hMassAntiLambdavsCuts", "hMassAntiLambdavsCuts", {HistType::kTH2F, {{partCutsAxis}, {antiLambdaMassAxis}}}); rPtAnalysis.add("hArmenterosPodolanskiPlotAntiLambda", "hArmenterosPodolanskiPlotAntiLambda", {HistType::kTH2F, {{armenterosasymAxis}, {armenterosQtAxis}}}); rPtAnalysis.add("hNSigmaPosPionFromAntiLambdas", "hNSigmaPosPionFromAntiLambdas", {HistType::kTH2F, {{100, -5.f, 5.f}, {antilambdaPtAxis}}}); rPtAnalysis.add("hNSigmaNegProtonFromAntiLambdas", "hNSigmaNegProtonFromAntiLambdas", {HistType::kTH2F, {{100, -5.f, 5.f}, {antilambdaPtAxis}}}); @@ -334,6 +360,7 @@ struct V0PtInvMassPlots { for (int i = 0; i < nAntilambdaHistograms; i++) { pthistos::antilambdaPt[i] = rAntilambdaMassPlotsPerPtBin.add(fmt::format("hAntiLambdaPt_vs_Cent_from_{0}_to_{1}", antilambdahistvalue[i], antilambdahistvalue[i + 1]).c_str(), fmt::format("AntiLambda mass vs centrality, pT from {0} to {1}", antilambdahistvalue[i], antilambdahistvalue[i + 1]).c_str(), {HistType::kTH2D, {{antiLambdaMassAxis}, {centAxis}}}); pthistos::antilambdaSplit[i] = rAntilambdaSplitMassPlotsPerPtBin.add(fmt::format("hAntiLambdaSplitPt_vs_Cent_from_{0}_to_{1}", antilambdahistvalue[i], antilambdahistvalue[i + 1]).c_str(), fmt::format("Split AntiLambda mass vs centrality, pT from {0} to {1}", antilambdahistvalue[i], antilambdahistvalue[i + 1]).c_str(), {HistType::kTH2D, {{antiLambdaMassAxis}, {centAxis}}}); + pthistos::antilambdaSplitFeeddown[i] = rAntilambdaSplitMassPlotsPerPtBin.add(fmt::format("hAntiLambdaSplitFeeddownPt_vs_Cent_from_{0}_to_{1}", antilambdahistvalue[i], antilambdahistvalue[i + 1]).c_str(), fmt::format("Feeddown Split AntiLambda mass vs centrality, pT from {0} to {1}", antilambdahistvalue[i], antilambdahistvalue[i + 1]).c_str(), {HistType::kTH2D, {{antiLambdaMassAxis}, {centAxis}}}); } // antilambdafeeddown matrices rFeeddownMatrices.add("hAntiLambdaFeeddownMatrix", "hAntiLambdaFeeddownMatrix", {HistType::kTH3D, {antilambdaPtAxis, antilambdaPtAxis, centAxis}}); @@ -346,9 +373,6 @@ struct V0PtInvMassPlots { rMCCorrections.add("hK0shBeforeEventSelectionPtSpectrum", "hK0shBeforeEventSelectionPtSpectrum", {HistType::kTH2D, {k0ShortPtAxis, centAxis}}); // not filled rMCCorrections.add("hLambdaBeforeEventSelectionPtSpectrum", "hLambdaBeforeEventSelectionPtSpectrum", {HistType::kTH2D, {lambdaPtAxis, centAxis}}); rMCCorrections.add("hAntiLambdaBeforeEventSelectionPtSpectrum", "hAntiLambdaBeforeEventSelectionPtSpectrum", {HistType::kTH2D, {antilambdaPtAxis, centAxis}}); - rMCCorrections.add("hK0shAfterEventSelectionPtSpectrum", "hK0shAfterEventSelectionPtSpectrum", {HistType::kTH2D, {k0ShortPtAxis, centAxis}}); - rMCCorrections.add("hLambdaAfterEventSelectionPtSpectrum", "hLambdaAfterEventSelectionPtSpectrum", {HistType::kTH2D, {lambdaPtAxis, centAxis}}); - rMCCorrections.add("hAntiLambdaAfterEventSelectionPtSpectrum", "hAntiLambdaAfterEventSelectionPtSpectrum", {HistType::kTH2D, {antilambdaPtAxis, centAxis}}); // Event and V0s Corrections rMCCorrections.add("hNEvents_Corrections", "hNEvents_Corrections", {HistType::kTH2D, {{10, 0.f, 10.f}, centAxis}}); @@ -356,8 +380,11 @@ struct V0PtInvMassPlots { // Generated Level Pt Spectrums (with rapidity cut) rMCCorrections.add("GenParticleRapidity", "GenParticleRapidity", {HistType::kTH1F, {{nBins, -10.0f, 10.0f}}}); rMCCorrections.add("hK0shGeneratedPtSpectrum", "hK0shGeneratedPtSpectrum", {HistType::kTH2D, {k0ShortPtAxis, centAxis}}); + rMCCorrections.add("hK0shGeneratedPtSpectrumMult", "hK0shGeneratedPtSpectrumMult", {HistType::kTH2D, {k0ShortPtAxis, nchAxis}}); rMCCorrections.add("hLambdaGeneratedPtSpectrum", "hLambdaGeneratedPtSpectrum", {HistType::kTH2D, {lambdaPtAxis, centAxis}}); + rMCCorrections.add("hLambdaGeneratedPtSpectrumMult", "hLambdaGeneratedPtSpectrumMult", {HistType::kTH2D, {lambdaPtAxis, nchAxis}}); rMCCorrections.add("hAntiLambdaGeneratedPtSpectrum", "hAntiLambdaGeneratedPtSpectrum", {HistType::kTH2D, {antilambdaPtAxis, centAxis}}); + rMCCorrections.add("hAntiLambdaGeneratedPtSpectrumMult", "hAntiLambdaGeneratedPtSpectrumMult", {HistType::kTH2D, {antilambdaPtAxis, nchAxis}}); rMCCorrections.add("hXiMinusGeneratedPtSpectrum", "hXiMinusGeneratedPtSpectrum", {HistType::kTH2D, {lambdaPtAxis, centAxis}}); rMCCorrections.add("hXiZeroGeneratedPtSpectrum", "hXiZeroGeneratedPtSpectrum", {HistType::kTH2D, {lambdaPtAxis, centAxis}}); rMCCorrections.add("hOmegaGeneratedPtSpectrum", "hOmegaGeneratedPtSpectrum", {HistType::kTH2D, {lambdaPtAxis, centAxis}}); @@ -383,42 +410,50 @@ struct V0PtInvMassPlots { rNchAnalysis.add("hNchCentralityGenerated", "hNchCentralityGenerated", {HistType::kTH2D, {centAxis, nchAxis}}); // Nch vs Centrality Generated rNchAnalysis.add("hNchCentralityGeneratedAfterEventSelection", "hNchCentralityGeneratedAfterEventSelection", {HistType::kTH2D, {centAxis, nchAxis}}); // Nch vs Centrality Generated After Event Selection rNchAnalysis.add("hNchCentrality", "hNchCentrality", {HistType::kTH2D, {centAxis, nchAxis}}); - rNchAnalysis.add("hNchCentralityEtaHalf", "hNchCentralityEtaHalf", {HistType::kTH2D, {centAxis, nchAxis}}); // Nch vs Centrality EtaHalfCut + rNchAnalysis.add("hNchCentralityEtaHalf", "hNchCentralityEtaHalf", {HistType::kTH2D, {centAxis, nchAxis}}); // Nch vs Centrality EtaHalfCut + rNchAnalysis.add("hNchCentralityEtaHalfGen", "hNchCentralityEtaHalfGen", {HistType::kTH2D, {nchAxis, centAxis}}); // Nch vs Centrality EtaHalfCut } + // Grouped Configurables + EventSelectionConfig eventSelection; + V0SelectionConfig v0Selection; + K0shSelectionConfig k0shSelection; + LambdaSelectionConfig lambdaSelection; + AntiLambdaSelectionConfig antilambdaSelection; + // Event selection function template bool acceptEvent(TCollision const& collision) { rPtAnalysis.fill(HIST("hNEvents"), 0.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(1, "All"); - if (dosel8 && !collision.sel8()) { + if (eventSelection.dosel8 && !collision.sel8()) { return false; } rPtAnalysis.fill(HIST("hNEvents"), 1.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(2, "sel 8"); - if (doNoTimeFrameBorder && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + if (eventSelection.doNoTimeFrameBorder && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { return false; } rPtAnalysis.fill(HIST("hNEvents"), 2.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(3, "NoTimeFrameBorder"); - if (doNoITSROFrameBorder && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + if (eventSelection.doNoITSROFrameBorder && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { return false; } rPtAnalysis.fill(HIST("hNEvents"), 3.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(4, "NoITSROFrameBorder"); - if (doIsTriggerTVX && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) { + if (eventSelection.doIsTriggerTVX && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) { return false; } rPtAnalysis.fill(HIST("hNEvents"), 4.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(5, "IsTriggerTVX"); - if (docutZVertex && !(std::abs(collision.posZ()) < cutZVertex)) { + if (eventSelection.docutZVertex && !(std::abs(collision.posZ()) < cutZVertex)) { return false; } rPtAnalysis.fill(HIST("hNEvents"), 5.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(6, "cutZVertex"); - if (doisInelGt0 && !(collision.multNTracksPVeta1() > 0)) { - // if (doisInelGt0 && !(collision.multMCNParticlesEta10() > 0)) { //CHANGE TO THIS + if (eventSelection.doisInelGt0 && !(collision.multNTracksPVeta1() > 0)) { + // if (eventSelection.doisInelGt0 && !(collision.multMCNParticlesEta10() > 0)) { //CHANGE TO THIS return false; } rPtAnalysis.fill(HIST("hNEvents"), 6.5, collision.centFT0M()); @@ -476,17 +511,17 @@ struct V0PtInvMassPlots { { rPtAnalysis.fill(HIST("hNV0s"), 0.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNV0s"))->GetXaxis()->SetBinLabel(1, "All V0s"); - if (doDaughterPseudorapidityCut && !(std::abs(posDaughterTrack.eta()) < etadau && std::abs(negDaughterTrack.eta()) < etadau)) { // Daughters Pseudorapidity Cut + if (v0Selection.doDaughterPseudorapidityCut && !(std::abs(posDaughterTrack.eta()) < etadau && std::abs(negDaughterTrack.eta()) < etadau)) { // Daughters Pseudorapidity Cut return false; } rPtAnalysis.fill(HIST("hNV0s"), 1.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNV0s"))->GetXaxis()->SetBinLabel(2, "Dau Pseudorapidity"); - if (doisNotITSAfterburner && (posDaughterTrack.isITSAfterburner() || negDaughterTrack.isITSAfterburner())) { // ITS After Burner on daughter tracks + if (v0Selection.doisNotITSAfterburner && (posDaughterTrack.isITSAfterburner() || negDaughterTrack.isITSAfterburner())) { // ITS After Burner on daughter tracks return false; } rPtAnalysis.fill(HIST("hNV0s"), 2.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNV0s"))->GetXaxis()->SetBinLabel(3, "ITS Afterburner"); - if (doitsMinHits && !(posDaughterTrack.itsNCls() >= itsMinHits && negDaughterTrack.itsNCls() >= itsMinHits)) { // Minimum hits in the ITS + if (v0Selection.doitsMinHits && !(posDaughterTrack.itsNCls() >= itsMinHits && negDaughterTrack.itsNCls() >= itsMinHits)) { // Minimum hits in the ITS return false; } rPtAnalysis.fill(HIST("hNV0s"), 3.5, collision.centFT0M()); @@ -506,61 +541,61 @@ struct V0PtInvMassPlots { rPtAnalysis.get(HIST("hNK0sh"))->GetXaxis()->SetBinLabel(1, "All"); rPtAnalysis.fill(HIST("hMassK0ShortvsCuts"), 0.5, v0.mK0Short()); - if (doRapidityCut && (std::abs(v0.rapidity(0)) > rapidityCut)) { // V0 Rapidity Cut + if (k0shSelection.doK0shRapidityCut && (std::abs(v0.rapidity(0)) > rapidityCut)) { // V0 Rapidity Cut return false; } rPtAnalysis.fill(HIST("hNK0sh"), 1.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNK0sh"))->GetXaxis()->SetBinLabel(2, "Rapidity"); rPtAnalysis.fill(HIST("hMassK0ShortvsCuts"), 1.5, v0.mK0Short()); - if (doK0shTPCPID && (std::abs(posDaughterTrack.tpcNSigmaPi()) > nSigmaTPCPion || std::abs(negDaughterTrack.tpcNSigmaPi()) > nSigmaTPCPion)) { // TPC PID for two pions + if (k0shSelection.doK0shTPCPID && (std::abs(posDaughterTrack.tpcNSigmaPi()) > nSigmaTPCPion || std::abs(negDaughterTrack.tpcNSigmaPi()) > nSigmaTPCPion)) { // TPC PID for two pions return false; } rPtAnalysis.fill(HIST("hNK0sh"), 2.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNK0sh"))->GetXaxis()->SetBinLabel(3, "TPC_PID"); rPtAnalysis.fill(HIST("hMassK0ShortvsCuts"), 2.5, v0.mK0Short()); - if (doK0shcomptmasscut && ((std::abs(v0.mLambda() - o2::constants::physics::MassLambda0) < compv0masscut) || (std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda0) < compv0masscut))) { // K0sh competitive v0 mass cut (cut out Lambdas and Anti-Lambdas) + if (k0shSelection.doK0shcomptmasscut && ((std::abs(v0.mLambda() - o2::constants::physics::MassLambda0) < compv0masscut) || (std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda0) < compv0masscut))) { // K0sh competitive v0 mass cut (cut out Lambdas and Anti-Lambdas) return false; } rPtAnalysis.fill(HIST("hNK0sh"), 3.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNK0sh"))->GetXaxis()->SetBinLabel(4, "Compt_Mass"); rPtAnalysis.fill(HIST("hMassK0ShortvsCuts"), 3.5, v0.mK0Short()); - if (doK0shMaxct && (v0.v0radius() > k0shmaxct)) { // K0sh max ct + if (k0shSelection.doK0shMaxct && (v0.v0radius() > k0shmaxct)) { // K0sh max ct return false; } rPtAnalysis.fill(HIST("hNK0sh"), 4.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNK0sh"))->GetXaxis()->SetBinLabel(5, "Max_ct"); rPtAnalysis.fill(HIST("hMassK0ShortvsCuts"), 4.5, v0.mK0Short()); - if (doK0shArmenterosCut && (v0.qtarm() < (k0shparamArmenterosCut * std::abs(v0.alpha())))) { // K0sh Armenteros Cut + if (k0shSelection.doK0shArmenterosCut && (v0.qtarm() < (k0shparamArmenterosCut * std::abs(v0.alpha())))) { // K0sh Armenteros Cut return false; } rPtAnalysis.fill(HIST("hNK0sh"), 5.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNK0sh"))->GetXaxis()->SetBinLabel(6, "Armenteros"); rPtAnalysis.fill(HIST("hMassK0ShortvsCuts"), 5.5, v0.mK0Short()); - if (doK0shcosPACut && (v0.v0cosPA() < k0shSettingcosPA)) { // K0sh cosPA Topological Cut + if (k0shSelection.doK0shcosPACut && (v0.v0cosPA() < k0shSettingcosPA)) { // K0sh cosPA Topological Cut return false; } rPtAnalysis.fill(HIST("hNK0sh"), 6.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNK0sh"))->GetXaxis()->SetBinLabel(7, "cosPA"); rPtAnalysis.fill(HIST("hMassK0ShortvsCuts"), 6.5, v0.mK0Short()); - if (doK0shDCAdauCut && (v0.dcaV0daughters() > k0shSettingdcav0dau)) { // K0sh DCAdaughters Topological Cut + if (k0shSelection.doK0shDCAdauCut && (v0.dcaV0daughters() > k0shSettingdcav0dau)) { // K0sh DCAdaughters Topological Cut return false; } rPtAnalysis.fill(HIST("hNK0sh"), 7.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNK0sh"))->GetXaxis()->SetBinLabel(8, "DCAdau"); rPtAnalysis.fill(HIST("hMassK0ShortvsCuts"), 7.5, v0.mK0Short()); - if (doK0shv0radiusCut && (v0.v0radius() < k0shSettingradius)) { // K0sh v0radius Topological Cut + if (k0shSelection.doK0shv0radiusCut && (v0.v0radius() < k0shSettingradius)) { // K0sh v0radius Topological Cut return false; } rPtAnalysis.fill(HIST("hNK0sh"), 8.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNK0sh"))->GetXaxis()->SetBinLabel(9, "v0radius"); rPtAnalysis.fill(HIST("hMassK0ShortvsCuts"), 8.5, v0.mK0Short()); - if (doK0shdcaposdautopv && (std::abs(v0.dcapostopv()) < k0shSettingdcapostopv)) { // K0sh DCAPosDaughterToPV Topological Cut + if (k0shSelection.doK0shdcaposdautopv && (std::abs(v0.dcapostopv()) < k0shSettingdcapostopv)) { // K0sh DCAPosDaughterToPV Topological Cut return false; } rPtAnalysis.fill(HIST("hNK0sh"), 9.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNK0sh"))->GetXaxis()->SetBinLabel(10, "DCAPosDautoPV"); rPtAnalysis.fill(HIST("hMassK0ShortvsCuts"), 9.5, v0.mK0Short()); - if (doK0shdcanegdautopv && (std::abs(v0.dcanegtopv()) < k0shSettingdcanegtopv)) { // K0sh DCANegDaughterToPV Topological Cut + if (k0shSelection.doK0shdcanegdautopv && (std::abs(v0.dcanegtopv()) < k0shSettingdcanegtopv)) { // K0sh DCANegDaughterToPV Topological Cut return false; } rPtAnalysis.fill(HIST("hNK0sh"), 10.5, collision.centFT0M()); @@ -588,61 +623,61 @@ struct V0PtInvMassPlots { rPtAnalysis.get(HIST("hNLambda"))->GetXaxis()->SetBinLabel(1, "All"); rPtAnalysis.fill(HIST("hMassLambdavsCuts"), 0.5, v0.mLambda()); - if (doRapidityCut && (std::abs(v0.rapidity(1)) > rapidityCut)) { // V0 Rapidity Cut + if (lambdaSelection.doLambdaRapidityCut && (std::abs(v0.rapidity(1)) > rapidityCut)) { // V0 Rapidity Cut return false; } rPtAnalysis.fill(HIST("hNLambda"), 1.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNLambda"))->GetXaxis()->SetBinLabel(2, "Rapidity"); rPtAnalysis.fill(HIST("hMassLambdavsCuts"), 1.5, v0.mLambda()); - if (doLambdaTPCPID && ((std::abs(posDaughterTrack.tpcNSigmaPr()) > nSigmaTPCProton) || (std::abs(negDaughterTrack.tpcNSigmaPi()) > nSigmaTPCPion))) { // TPC PID on daughter pion and proton for Lambda + if (lambdaSelection.doLambdaTPCPID && ((std::abs(posDaughterTrack.tpcNSigmaPr()) > nSigmaTPCProton) || (std::abs(negDaughterTrack.tpcNSigmaPi()) > nSigmaTPCPion))) { // TPC PID on daughter pion and proton for Lambda return false; } rPtAnalysis.fill(HIST("hNLambda"), 2.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNLambda"))->GetXaxis()->SetBinLabel(3, "TPC_PID"); rPtAnalysis.fill(HIST("hMassLambdavsCuts"), 2.5, v0.mLambda()); - if (doLambdacomptmasscut && ((std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) < compv0masscut) || (std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda0) < compv0masscut))) { // Lambda competitive v0 mass cut (cut out Kaons) + if (lambdaSelection.doLambdacomptmasscut && ((std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) < compv0masscut) || (std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda0) < compv0masscut))) { // Lambda competitive v0 mass cut (cut out Kaons) return false; } rPtAnalysis.fill(HIST("hNLambda"), 3.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNLambda"))->GetXaxis()->SetBinLabel(4, "Compt_Mass"); rPtAnalysis.fill(HIST("hMassLambdavsCuts"), 3.5, v0.mLambda()); - if (doLambdaMaxct && (v0.v0radius() > lambdamaxct)) { // Lambda max ct + if (lambdaSelection.doLambdaMaxct && (v0.v0radius() > lambdamaxct)) { // Lambda max ct return false; } rPtAnalysis.fill(HIST("hNLambda"), 4.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNLambda"))->GetXaxis()->SetBinLabel(5, "Max_ct"); rPtAnalysis.fill(HIST("hMassLambdavsCuts"), 4.5, v0.mLambda()); - if (doLambdaArmenterosCut && (v0.alpha() <= 0 || v0.qtarm() > lambdaparamArmenterosCut * v0.alpha())) { // Lambda Armenteros Cut + if (lambdaSelection.doLambdaArmenterosCut && (v0.alpha() <= 0 || v0.qtarm() > lambdaparamArmenterosCut * v0.alpha())) { // Lambda Armenteros Cut return false; } rPtAnalysis.fill(HIST("hNLambda"), 5.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNLambda"))->GetXaxis()->SetBinLabel(6, "Armenteros"); rPtAnalysis.fill(HIST("hMassLambdavsCuts"), 5.5, v0.mLambda()); - if (doLambdacosPACut && (v0.v0cosPA() < lambdaSettingcosPA)) { // Lambda cosPA Topological Cut + if (lambdaSelection.doLambdacosPACut && (v0.v0cosPA() < lambdaSettingcosPA)) { // Lambda cosPA Topological Cut return false; } rPtAnalysis.fill(HIST("hNLambda"), 6.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNLambda"))->GetXaxis()->SetBinLabel(7, "cosPA"); rPtAnalysis.fill(HIST("hMassLambdavsCuts"), 6.5, v0.mLambda()); - if (doLambdaDCAdauCut && (v0.dcaV0daughters() > lambdaSettingdcav0dau)) { // Lambda DCAdaughters Topological Cut + if (lambdaSelection.doLambdaDCAdauCut && (v0.dcaV0daughters() > lambdaSettingdcav0dau)) { // Lambda DCAdaughters Topological Cut return false; } rPtAnalysis.fill(HIST("hNLambda"), 7.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNLambda"))->GetXaxis()->SetBinLabel(8, "DCAdau"); rPtAnalysis.fill(HIST("hMassLambdavsCuts"), 7.5, v0.mLambda()); - if (doLambdav0radiusCut && (v0.v0radius() < lambdaSettingradius)) { // Lambda v0radius Topological Cut + if (lambdaSelection.doLambdav0radiusCut && (v0.v0radius() < lambdaSettingradius)) { // Lambda v0radius Topological Cut return false; } rPtAnalysis.fill(HIST("hNLambda"), 8.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNLambda"))->GetXaxis()->SetBinLabel(9, "v0radius"); rPtAnalysis.fill(HIST("hMassLambdavsCuts"), 8.5, v0.mLambda()); - if (doLambdadcaposdautopv && (std::abs(v0.dcapostopv()) < lambdaSettingdcapostopv)) { // Lambda DCAPosDaughterToPV Topological Cut + if (lambdaSelection.doLambdadcaposdautopv && (std::abs(v0.dcapostopv()) < lambdaSettingdcapostopv)) { // Lambda DCAPosDaughterToPV Topological Cut return false; } rPtAnalysis.fill(HIST("hNLambda"), 9.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNLambda"))->GetXaxis()->SetBinLabel(10, "DCAPosDautoPV"); rPtAnalysis.fill(HIST("hMassLambdavsCuts"), 9.5, v0.mLambda()); - if (doLambdadcanegdautopv && (std::abs(v0.dcanegtopv()) < lambdaSettingdcanegtopv)) { // Lambda DCANegDaughterToPV Topological Cut + if (lambdaSelection.doLambdadcanegdautopv && (std::abs(v0.dcanegtopv()) < lambdaSettingdcanegtopv)) { // Lambda DCANegDaughterToPV Topological Cut return false; } rPtAnalysis.fill(HIST("hNLambda"), 10.5, collision.centFT0M()); @@ -670,61 +705,61 @@ struct V0PtInvMassPlots { rPtAnalysis.get(HIST("hNAntiLambda"))->GetXaxis()->SetBinLabel(1, "All"); rPtAnalysis.fill(HIST("hMassAntiLambdavsCuts"), 0.5, v0.mAntiLambda()); - if (doRapidityCut && (std::abs(v0.rapidity(2)) > rapidityCut)) { // V0 Rapidity Cut + if (antilambdaSelection.doAntiLambdaRapidityCut && (std::abs(v0.rapidity(2)) > rapidityCut)) { // V0 Rapidity Cut return false; } rPtAnalysis.fill(HIST("hNAntiLambda"), 1.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNAntiLambda"))->GetXaxis()->SetBinLabel(2, "Rapidity"); rPtAnalysis.fill(HIST("hMassAntiLambdavsCuts"), 1.5, v0.mAntiLambda()); - if (doAntilambdaTPCPID && (std::abs(negDaughterTrack.tpcNSigmaPr()) > nSigmaTPCProton || std::abs(posDaughterTrack.tpcNSigmaPi()) > nSigmaTPCPion)) { // TPC PID on daughter pion and proton for AntiLambda + if (antilambdaSelection.doAntilambdaTPCPID && (std::abs(negDaughterTrack.tpcNSigmaPr()) > nSigmaTPCProton || std::abs(posDaughterTrack.tpcNSigmaPi()) > nSigmaTPCPion)) { // TPC PID on daughter pion and proton for AntiLambda return false; } rPtAnalysis.fill(HIST("hNAntiLambda"), 2.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNAntiLambda"))->GetXaxis()->SetBinLabel(3, "TPC_PID"); rPtAnalysis.fill(HIST("hMassAntiLambdavsCuts"), 2.5, v0.mAntiLambda()); - if (doAntilambdacomptmasscut && ((std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) < compv0masscut) || (std::abs(v0.mLambda() - o2::constants::physics::MassLambda0) < compv0masscut))) { // AntiLambda competitive v0 mass cut (cut out Kaons) + if (antilambdaSelection.doAntilambdacomptmasscut && ((std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) < compv0masscut) || (std::abs(v0.mLambda() - o2::constants::physics::MassLambda0) < compv0masscut))) { // AntiLambda competitive v0 mass cut (cut out Kaons) return false; } rPtAnalysis.fill(HIST("hNAntiLambda"), 3.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNAntiLambda"))->GetXaxis()->SetBinLabel(4, "Compt_Mass"); rPtAnalysis.fill(HIST("hMassAntiLambdavsCuts"), 3.5, v0.mAntiLambda()); - if (doAntilambdaMaxct && (v0.v0radius() > antilambdamaxct)) { // AntiLambda max ct + if (antilambdaSelection.doAntilambdaMaxct && (v0.v0radius() > antilambdamaxct)) { // AntiLambda max ct return false; } rPtAnalysis.fill(HIST("hNAntiLambda"), 4.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNAntiLambda"))->GetXaxis()->SetBinLabel(5, "Max_ct"); rPtAnalysis.fill(HIST("hMassAntiLambdavsCuts"), 4.5, v0.mAntiLambda()); - if (doAntilambdaArmenterosCut && (v0.alpha() > 0 || v0.qtarm() > antilambdaparamArmenterosCut * std::abs(v0.alpha()))) { // AntiLambda Armenteros Cut + if (antilambdaSelection.doAntilambdaArmenterosCut && (v0.alpha() > 0 || v0.qtarm() > antilambdaparamArmenterosCut * std::abs(v0.alpha()))) { // AntiLambda Armenteros Cut return false; } rPtAnalysis.fill(HIST("hNAntiLambda"), 5.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNAntiLambda"))->GetXaxis()->SetBinLabel(6, "Armenteros"); rPtAnalysis.fill(HIST("hMassAntiLambdavsCuts"), 5.5, v0.mAntiLambda()); - if (doAntilambdacosPACut && (v0.v0cosPA() < antilambdaSettingcosPA)) { // AntiLambda cosPA Topological Cut + if (antilambdaSelection.doAntilambdacosPACut && (v0.v0cosPA() < antilambdaSettingcosPA)) { // AntiLambda cosPA Topological Cut return false; } rPtAnalysis.fill(HIST("hNAntiLambda"), 6.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNAntiLambda"))->GetXaxis()->SetBinLabel(7, "cosPA"); rPtAnalysis.fill(HIST("hMassAntiLambdavsCuts"), 6.5, v0.mAntiLambda()); - if (doAntilambdaDCAdauCut && (v0.dcaV0daughters() > antilambdaSettingdcav0dau)) { // AntiLambda DCAdaughters Topological Cut + if (antilambdaSelection.doAntilambdaDCAdauCut && (v0.dcaV0daughters() > antilambdaSettingdcav0dau)) { // AntiLambda DCAdaughters Topological Cut return false; } rPtAnalysis.fill(HIST("hNAntiLambda"), 7.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNAntiLambda"))->GetXaxis()->SetBinLabel(8, "DCAdau"); rPtAnalysis.fill(HIST("hMassAntiLambdavsCuts"), 7.5, v0.mAntiLambda()); - if (doAntilambdav0radiusCut && (v0.v0radius() < antilambdaSettingradius)) { // AntiLambda v0radius Topological Cut + if (antilambdaSelection.doAntilambdav0radiusCut && (v0.v0radius() < antilambdaSettingradius)) { // AntiLambda v0radius Topological Cut return false; } rPtAnalysis.fill(HIST("hNAntiLambda"), 8.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNAntiLambda"))->GetXaxis()->SetBinLabel(9, "v0radius"); rPtAnalysis.fill(HIST("hMassAntiLambdavsCuts"), 8.5, v0.mAntiLambda()); - if (doAntilambdadcaposdautopv && (std::abs(v0.dcapostopv()) < antilambdaSettingdcapostopv)) { // AntiLambda DCAPosDaughterToPV Topological Cut + if (antilambdaSelection.doAntilambdadcaposdautopv && (std::abs(v0.dcapostopv()) < antilambdaSettingdcapostopv)) { // AntiLambda DCAPosDaughterToPV Topological Cut return false; } rPtAnalysis.fill(HIST("hNAntiLambda"), 9.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNAntiLambda"))->GetXaxis()->SetBinLabel(10, "DCAPosDautoPV"); rPtAnalysis.fill(HIST("hMassAntiLambdavsCuts"), 9.5, v0.mAntiLambda()); - if (doAntilambdadcanegdautopv && (std::abs(v0.dcanegtopv()) < antilambdaSettingdcanegtopv)) { // AntiLambda DCANegDaughterToPV Topological Cut + if (antilambdaSelection.doAntilambdadcanegdautopv && (std::abs(v0.dcanegtopv()) < antilambdaSettingdcanegtopv)) { // AntiLambda DCANegDaughterToPV Topological Cut return false; } rPtAnalysis.fill(HIST("hNAntiLambda"), 10.5, collision.centFT0M()); @@ -750,17 +785,17 @@ struct V0PtInvMassPlots { { rPtAnalysis.fill(HIST("hNV0s"), 0.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNV0s"))->GetXaxis()->SetBinLabel(1, "All V0s"); - if (doDaughterPseudorapidityCut && !(std::abs(v0.positiveeta()) < etadau && std::abs(v0.negativeeta()) < etadau)) { // Daughters Pseudorapidity Cut + if (v0Selection.doDaughterPseudorapidityCut && !(std::abs(v0.positiveeta()) < etadau && std::abs(v0.negativeeta()) < etadau)) { // Daughters Pseudorapidity Cut return false; } rPtAnalysis.fill(HIST("hNV0s"), 1.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNV0s"))->GetXaxis()->SetBinLabel(2, "Dau Pseudorapidity"); - if (doisNotITSAfterburner && (posDaughterTrack.isITSAfterburner() || negDaughterTrack.isITSAfterburner())) { // ITS After Burner on daughter tracks + if (v0Selection.doisNotITSAfterburner && (posDaughterTrack.isITSAfterburner() || negDaughterTrack.isITSAfterburner())) { // ITS After Burner on daughter tracks return false; } rPtAnalysis.fill(HIST("hNV0s"), 2.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNV0s"))->GetXaxis()->SetBinLabel(3, "ITS Afterburner"); - if (doitsMinHits && !(posDaughterTrack.itsNCls() >= itsMinHits && negDaughterTrack.itsNCls() >= itsMinHits)) { // Minimum hits in the ITS + if (v0Selection.doitsMinHits && !(posDaughterTrack.itsNCls() >= itsMinHits && negDaughterTrack.itsNCls() >= itsMinHits)) { // Minimum hits in the ITS return false; } rPtAnalysis.fill(HIST("hNV0s"), 3.5, collision.centFT0M()); @@ -782,8 +817,8 @@ struct V0PtInvMassPlots { soa::SmallGroups> const& collisions, aod::McParticles const& mcParticles) { - rMCCorrections.fill(HIST("hNEvents_Corrections"), 0.5, mcCollision.centFT0M()); // All Events - if (std::abs(mcCollision.posZ()) > cutZVertexGen) { // Generated Z vertex cut + rMCCorrections.fill(HIST("hNEvents_Corrections"), 0.5, mcCollision.centFT0M()); // All Events + if (eventSelection.doZvertexGenCut && std::abs(mcCollision.posZ()) > cutZVertexGen) { // Generated Z vertex cut return; } // if (!(mcCollision.multMCNParticlesEta10() > 0)) { // TRY TO CHANGE TO THIS @@ -846,10 +881,10 @@ struct V0PtInvMassPlots { nParticlesPerCollision++; } } - } // End of MCParticle Loop + } // End of MCParticle Loop For Signal Loss Denominator rNchAnalysis.fill(HIST("hNchCentralityGenerated"), mcCollision.centFT0M(), nParticlesPerCollision); - // Signal Loss Numenator Loop + // Signal Loss Start, Make sure we have INEL Collisions int recoCollINEL = 0; int recoCollINELgt0 = 0; @@ -938,7 +973,6 @@ struct V0PtInvMassPlots { pthistos::kaonPtBins = o2::utils::Str::tokenize(kzeroSettingPtBinsString, ','); pthistos::lambdaPtBins = o2::utils::Str::tokenize(lambdaSettingPtBinsString, ','); pthistos::antilambdaPtBins = o2::utils::Str::tokenize(antilambdaSettingPtBinsString, ','); - pthistos::kaonPtBins = o2::utils::Str::tokenize(kzeroSettingPtBinsString, ','); // Calculate number of histograms for each particle type int nKaonHistograms = pthistos::kaonPtBins.size() - 1; @@ -965,6 +999,9 @@ struct V0PtInvMassPlots { if (!acceptEvent(collision)) { // Event Selection return; } + if (!collision.has_mcCollision()) { + return; + } rPtAnalysis.fill(HIST("hNRecEvents"), 0.5, mcCollision.centFT0M()); // Event Split Numenator rNchAnalysis.fill(HIST("hNchCentrality"), mcCollision.centFT0M(), collision.multNTracksGlobal()); // Nch vs Centrality rNchAnalysis.fill(HIST("hNchCentralityEtaHalf"), mcCollision.centFT0M(), collision.multNGlobalTracksPVetaHalf()); // Nch vs Centrality EtaHalfCut @@ -984,10 +1021,10 @@ struct V0PtInvMassPlots { pthistos::kaonSplit[i]->Fill(v0.mK0Short(), mcCollision.centFT0M()); // filling the k0s namespace histograms for K0sh Splitting Numerator } } - // K0sh Signla Split Numerator End + // K0sh Signal Split Numerator End if (v0.has_mcParticle()) { auto v0mcParticle = v0.mcParticle(); - if (dotruthK0sh && (v0mcParticle.pdgCode() == kK0Short)) { // kzero matched + if (k0shSelection.dotruthK0sh && (v0mcParticle.pdgCode() == kK0Short)) { // kzero matched if (v0mcParticle.isPhysicalPrimary()) { for (int i = 0; i < nKaonHistograms; i++) { if (kaonptedgevalues[i] <= v0.pt() && v0.pt() < kaonptedgevalues[i + 1]) { // finding v0s with pt within the range of our bin edges @@ -996,6 +1033,13 @@ struct V0PtInvMassPlots { } } if (!v0mcParticle.isPhysicalPrimary()) { + // Split Feeddown Start + for (int i = 0; i < nKaonHistograms; i++) { + if (kaonptedgevalues[i] <= v0.pt() && v0.pt() < kaonptedgevalues[i + 1]) { + pthistos::kaonSplitFeeddown[i]->Fill(v0.mK0Short(), mcCollision.centFT0M()); + } + } + // Split Feeddown End auto v0mothers = v0mcParticle.mothers_as(); // Get mothers if (!v0mothers.empty()) { auto& v0mcParticleMother = v0mothers.front(); // First mother @@ -1021,7 +1065,7 @@ struct V0PtInvMassPlots { // Lambda Signal Split Numerator End if (v0.has_mcParticle()) { auto v0mcParticle = v0.mcParticle(); - if (dotruthLambda && (v0mcParticle.pdgCode() == kLambda0)) { // lambda matched + if (lambdaSelection.dotruthLambda && (v0mcParticle.pdgCode() == kLambda0)) { // lambda matched if (v0mcParticle.isPhysicalPrimary()) { for (int i = 0; i < nLambdaHistograms; i++) { if (lambdaptedgevalues[i] <= v0.pt() && v0.pt() < lambdaptedgevalues[i + 1]) { @@ -1030,6 +1074,13 @@ struct V0PtInvMassPlots { } } if (!v0mcParticle.isPhysicalPrimary()) { + // Split Feeddown Start + for (int i = 0; i < nLambdaHistograms; i++) { + if (lambdaptedgevalues[i] <= v0.pt() && v0.pt() < lambdaptedgevalues[i + 1]) { + pthistos::lambdaSplitFeeddown[i]->Fill(v0.mLambda(), mcCollision.centFT0M()); + } + } + // Split Feeddown End auto v0mothers = v0mcParticle.mothers_as(); // Get mothers if (!v0mothers.empty()) { auto& v0mcParticleMother = v0mothers.front(); // First mother @@ -1052,7 +1103,7 @@ struct V0PtInvMassPlots { // antilambda analysis if (antiLambdaAnalysis == true) { if (acceptAntilambda(v0, posDaughterTrack, negDaughterTrack, mcCollision)) { // AntiLambda Selections - // AntiLambda Signal Split Numerator End + // AntiLambda Signal Split Numerator Start for (int i = 0; i < nAntilambdaHistograms; i++) { if (antilambdaptedgevalues[i] <= v0.pt() && v0.pt() < antilambdaptedgevalues[i + 1]) { pthistos::antilambdaSplit[i]->Fill(v0.mAntiLambda(), mcCollision.centFT0M()); @@ -1061,7 +1112,7 @@ struct V0PtInvMassPlots { // AntiLambda Signal Split Numerator End if (v0.has_mcParticle()) { auto v0mcParticle = v0.mcParticle(); - if (dotruthAntiLambda && (v0mcParticle.pdgCode() == kLambda0Bar)) { // antilambda matched + if (antilambdaSelection.dotruthAntiLambda && (v0mcParticle.pdgCode() == kLambda0Bar)) { // antilambda matched if (v0mcParticle.isPhysicalPrimary()) { for (int i = 0; i < nAntilambdaHistograms; i++) { if (antilambdaptedgevalues[i] <= v0.pt() && v0.pt() < antilambdaptedgevalues[i + 1]) { @@ -1070,6 +1121,13 @@ struct V0PtInvMassPlots { } } if (!v0mcParticle.isPhysicalPrimary()) { + // Split Feeddown Start + for (int i = 0; i < nAntilambdaHistograms; i++) { + if (antilambdaptedgevalues[i] <= v0.pt() && v0.pt() < antilambdaptedgevalues[i + 1]) { + pthistos::antilambdaSplitFeeddown[i]->Fill(v0.mAntiLambda(), mcCollision.centFT0M()); + } + } + // Split Feeddown End auto v0mothers = v0mcParticle.mothers_as(); // Get mothers if (!v0mothers.empty()) { auto& v0mcParticleMother = v0mothers.front(); // First mother @@ -1100,7 +1158,6 @@ struct V0PtInvMassPlots { pthistos::kaonPtBins = o2::utils::Str::tokenize(kzeroSettingPtBinsString, ','); pthistos::lambdaPtBins = o2::utils::Str::tokenize(lambdaSettingPtBinsString, ','); pthistos::antilambdaPtBins = o2::utils::Str::tokenize(antilambdaSettingPtBinsString, ','); - pthistos::kaonPtBins = o2::utils::Str::tokenize(kzeroSettingPtBinsString, ','); // Calculate number of histograms for each particle type int nKaonHistograms = pthistos::kaonPtBins.size() - 1; @@ -1121,6 +1178,7 @@ struct V0PtInvMassPlots { for (int i = 0; i < nAntilambdaHistograms + 1; i++) { antilambdaptedgevalues[i] = std::stod(pthistos::antilambdaPtBins[i]); } + if (!acceptEvent(collision)) { // Event Selection return; } @@ -1158,7 +1216,7 @@ struct V0PtInvMassPlots { if (antiLambdaAnalysis == true) { if (acceptAntilambda(v0, posDaughterTrack, negDaughterTrack, collision)) { // AntiLambda Selection for (int i = 0; i < nAntilambdaHistograms; i++) { - if (lambdaptedgevalues[i] <= v0.pt() && v0.pt() < lambdaptedgevalues[i + 1]) { + if (antilambdaptedgevalues[i] <= v0.pt() && v0.pt() < antilambdaptedgevalues[i + 1]) { pthistos::antilambdaPt[i]->Fill(v0.mAntiLambda(), collision.centFT0M()); } } @@ -1175,7 +1233,6 @@ struct V0PtInvMassPlots { pthistos::kaonPtBins = o2::utils::Str::tokenize(kzeroSettingPtBinsString, ','); pthistos::lambdaPtBins = o2::utils::Str::tokenize(lambdaSettingPtBinsString, ','); pthistos::antilambdaPtBins = o2::utils::Str::tokenize(antilambdaSettingPtBinsString, ','); - pthistos::kaonPtBins = o2::utils::Str::tokenize(kzeroSettingPtBinsString, ','); // Calculate number of histograms for each particle type int nKaonHistograms = pthistos::kaonPtBins.size() - 1; @@ -1197,10 +1254,12 @@ struct V0PtInvMassPlots { antilambdaptedgevalues[i] = std::stod(pthistos::antilambdaPtBins[i]); } - // if (!acceptEvent(collision)) { // Event Selection - // return; - // } - rPtAnalysis.fill(HIST("hNRecEvents"), 0.5, collision.centFT0M()); // Number of recorded events + if (!acceptEvent(collision)) { // Event Selection + return; + } + rPtAnalysis.fill(HIST("hNRecEvents"), 0.5, collision.centFT0M()); // Number of recorded events + rNchAnalysis.fill(HIST("hNchCentrality"), collision.centFT0M(), collision.multNTracksGlobal()); // Nch vs Centrality + // rNchAnalysis.fill(HIST("hNchCentralityEtaHalf"), collision.centFT0M(), collision.multNGlobalTracksPVetaHalf()); // Nch vs Centrality EtaHalfCut for (const auto& v0 : V0s) { // Checking that the V0 is a true K0s/Lambdas/Antilambdas and then filling the parameter histograms and the invariant mass plots for different cuts (which are taken from namespace) const auto& posDaughterTrack = v0.template posTrackExtra_as(); // Positive Daughter track @@ -1241,15 +1300,15 @@ struct V0PtInvMassPlots { } } void recMCProcessDerived(soa::Join::iterator const& collision, - // To add McCentFT0Ms - soa::Join const& V0s, + soa::Join const& /*mcCollisions*/, + soa::Join const& V0s, + aod::V0MCCores const&, DaughterTracksDerived const&) { // tokenise strings into individual values pthistos::kaonPtBins = o2::utils::Str::tokenize(kzeroSettingPtBinsString, ','); pthistos::lambdaPtBins = o2::utils::Str::tokenize(lambdaSettingPtBinsString, ','); pthistos::antilambdaPtBins = o2::utils::Str::tokenize(antilambdaSettingPtBinsString, ','); - pthistos::kaonPtBins = o2::utils::Str::tokenize(kzeroSettingPtBinsString, ','); // Calculate number of histograms for each particle type int nKaonHistograms = pthistos::kaonPtBins.size() - 1; @@ -1270,42 +1329,55 @@ struct V0PtInvMassPlots { for (int i = 0; i < nAntilambdaHistograms + 1; i++) { antilambdaptedgevalues[i] = std::stod(pthistos::antilambdaPtBins[i]); } - // if (!acceptEvent(collision)) { // Event Selection - // return; - // } - rPtAnalysis.fill(HIST("hNRecEvents"), 0.5, collision.centFT0M()); // Event Split Numenator + + // For centrality estimation + const auto& mcCollision = collision.straMCCollision_as>(); + + if (!acceptEvent(collision)) { // Event Selection + return; + } + rPtAnalysis.fill(HIST("hNRecEvents"), 0.5, mcCollision.centFT0M()); // Event Split Numenator + rNchAnalysis.fill(HIST("hNchCentrality"), mcCollision.centFT0M(), collision.multNTracksGlobal()); // Nch vs Centrality + // rNchAnalysis.fill(HIST("hNchCentralityEtaHalf"), mcCollision.centFT0M(), collision.multNGlobalTracksPVetaHalf()); // Nch vs Centrality EtaHalfCut for (const auto& v0 : V0s) { // Checking that the V0 is a true K0s/Lambdas/Antilambdas and then filling the parameter histograms and the invariant mass plots for different cuts (which are taken from namespace) const auto& posDaughterTrack = v0.template posTrackExtra_as(); // Positive Daughter track const auto& negDaughterTrack = v0.template negTrackExtra_as(); // Negative Daughter track - if (!acceptV0Derived(v0, posDaughterTrack, negDaughterTrack, collision)) { // V0 Selections + if (!acceptV0Derived(v0, posDaughterTrack, negDaughterTrack, mcCollision)) { // V0 Selections continue; } // K0sh Analysis if (kzeroAnalysis == true) { - if (acceptK0sh(v0, posDaughterTrack, negDaughterTrack, collision)) { // K0sh Selection + if (acceptK0sh(v0, posDaughterTrack, negDaughterTrack, mcCollision)) { // K0sh Selection // K0sh Signal Split Numerator Start for (int i = 0; i < nKaonHistograms; i++) { - if (kaonptedgevalues[i] <= v0.ptMC() && v0.ptMC() < kaonptedgevalues[i + 1]) { // finding v0s with pt within the range of our bin edges for K0sh Splitting Numerator - pthistos::kaonSplit[i]->Fill(v0.mK0Short(), collision.centFT0M()); // filling the k0s namespace histograms for K0sh Splitting Numerator + if (kaonptedgevalues[i] <= v0.pt() && v0.pt() < kaonptedgevalues[i + 1]) { // finding v0s with pt within the range of our bin edges for K0sh Splitting Numerator + pthistos::kaonSplit[i]->Fill(v0.mK0Short(), mcCollision.centFT0M()); // filling the k0s namespace histograms for K0sh Splitting Numerator } } // K0sh SignaL Split Numerator End if (v0.has_v0MCCore()) { auto v0mcParticle = v0.v0MCCore_as(); - if (dotruthK0sh && (v0mcParticle.pdgCode() == kK0Short)) { // kzero matched + if (k0shSelection.dotruthK0sh && (v0mcParticle.pdgCode() == kK0Short)) { // kzero matched if (v0mcParticle.isPhysicalPrimary()) { for (int i = 0; i < nKaonHistograms; i++) { - if (kaonptedgevalues[i] <= v0.ptMC() && v0.ptMC() < kaonptedgevalues[i + 1]) { // finding v0s with pt within the range of our bin edges - pthistos::kaonPt[i]->Fill(v0.mK0Short(), collision.centFT0M()); // filling the k0s namespace histograms + if (kaonptedgevalues[i] <= v0.pt() && v0.pt() < kaonptedgevalues[i + 1]) { // finding v0s with pt within the range of our bin edges + pthistos::kaonPt[i]->Fill(v0.mK0Short(), mcCollision.centFT0M()); // filling the k0s namespace histograms } } } if (!v0mcParticle.isPhysicalPrimary()) { + // Split Feeddown Start + for (int i = 0; i < nKaonHistograms; i++) { + if (kaonptedgevalues[i] <= v0.pt() && v0.pt() < kaonptedgevalues[i + 1]) { + pthistos::kaonSplitFeeddown[i]->Fill(v0.mK0Short(), mcCollision.centFT0M()); + } + } + // Split Feeddown End auto v0mother = v0.motherMCPart(); // Get mothers - rFeeddownMatrices.fill(HIST("hK0shFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), collision.centFT0M()); + rFeeddownMatrices.fill(HIST("hK0shPhiFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); if (v0mother.pdgCode() == kPhi) { // Phi Mother Matched - rFeeddownMatrices.fill(HIST("hK0shFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), collision.centFT0M()); + rFeeddownMatrices.fill(HIST("hK0shFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); } } } @@ -1314,35 +1386,42 @@ struct V0PtInvMassPlots { } // Lambda analysis if (lambdaAnalysis == true) { - if (acceptLambda(v0, posDaughterTrack, negDaughterTrack, collision)) { // Lambda Selections + if (acceptLambda(v0, posDaughterTrack, negDaughterTrack, mcCollision)) { // Lambda Selections // Lambda Signal Split Numerator Start for (int i = 0; i < nLambdaHistograms; i++) { - if (lambdaptedgevalues[i] <= v0.ptMC() && v0.ptMC() < lambdaptedgevalues[i + 1]) { - pthistos::lambdaSplit[i]->Fill(v0.mLambda(), collision.centFT0M()); + if (lambdaptedgevalues[i] <= v0.pt() && v0.pt() < lambdaptedgevalues[i + 1]) { + pthistos::lambdaSplit[i]->Fill(v0.mLambda(), mcCollision.centFT0M()); } } // Lambda Signal Split Numerator End if (v0.has_v0MCCore()) { auto v0mcParticle = v0.v0MCCore_as(); - if (dotruthLambda && (v0mcParticle.pdgCode() == kLambda0)) { // lambda matched + if (lambdaSelection.dotruthLambda && (v0mcParticle.pdgCode() == kLambda0)) { // lambda matched if (v0mcParticle.isPhysicalPrimary()) { for (int i = 0; i < nLambdaHistograms; i++) { - if (lambdaptedgevalues[i] <= v0.ptMC() && v0.ptMC() < lambdaptedgevalues[i + 1]) { - pthistos::lambdaPt[i]->Fill(v0.mLambda(), collision.centFT0M()); + if (lambdaptedgevalues[i] <= v0.pt() && v0.pt() < lambdaptedgevalues[i + 1]) { + pthistos::lambdaPt[i]->Fill(v0.mLambda(), mcCollision.centFT0M()); } } } if (!v0mcParticle.isPhysicalPrimary()) { + // Split Feeddown Start + for (int i = 0; i < nLambdaHistograms; i++) { + if (lambdaptedgevalues[i] <= v0.pt() && v0.pt() < lambdaptedgevalues[i + 1]) { + pthistos::lambdaSplitFeeddown[i]->Fill(v0.mLambda(), mcCollision.centFT0M()); + } + } + // Split Feeddown End auto v0mother = v0.motherMCPart(); // Get mothers - rFeeddownMatrices.fill(HIST("hLambdaFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), collision.centFT0M()); + rFeeddownMatrices.fill(HIST("hLambdaFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); if (v0mother.pdgCode() == kXiMinus) { // Xi Minus Mother Matched - rFeeddownMatrices.fill(HIST("hLambdaXiMinusFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), collision.centFT0M()); + rFeeddownMatrices.fill(HIST("hLambdaXiMinusFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); } if (v0mother.pdgCode() == kXi0) { // Xi Zero Mother Matched - rFeeddownMatrices.fill(HIST("hLambdaXiZeroFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), collision.centFT0M()); + rFeeddownMatrices.fill(HIST("hLambdaXiZeroFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); } if (v0mother.pdgCode() == kOmegaMinus) { // Omega Mother Matched - rFeeddownMatrices.fill(HIST("hLambdaOmegaFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), collision.centFT0M()); + rFeeddownMatrices.fill(HIST("hLambdaOmegaFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); } } } @@ -1351,35 +1430,42 @@ struct V0PtInvMassPlots { } // AntiLambda analysis if (antiLambdaAnalysis == true) { - if (acceptAntilambda(v0, posDaughterTrack, negDaughterTrack, collision)) { // AntiLambda Selections - // AntiLambda Signal Split Numerator End + if (acceptAntilambda(v0, posDaughterTrack, negDaughterTrack, mcCollision)) { // AntiLambda Selections + // AntiLambda Signal Split Numerator Start for (int i = 0; i < nAntilambdaHistograms; i++) { - if (antilambdaptedgevalues[i] <= v0.ptMC() && v0.ptMC() < antilambdaptedgevalues[i + 1]) { - pthistos::antilambdaSplit[i]->Fill(v0.mAntiLambda(), collision.centFT0M()); + if (antilambdaptedgevalues[i] <= v0.pt() && v0.pt() < antilambdaptedgevalues[i + 1]) { + pthistos::antilambdaSplit[i]->Fill(v0.mAntiLambda(), mcCollision.centFT0M()); } } // AntiLambda Signal Split Numerator End if (v0.has_v0MCCore()) { auto v0mcParticle = v0.v0MCCore_as(); - if (dotruthAntiLambda && (v0mcParticle.pdgCode() == kLambda0Bar)) { // antilambda matched + if (antilambdaSelection.dotruthAntiLambda && (v0mcParticle.pdgCode() == kLambda0Bar)) { // antilambda matched if (v0mcParticle.isPhysicalPrimary()) { for (int i = 0; i < nAntilambdaHistograms; i++) { - if (antilambdaptedgevalues[i] <= v0.ptMC() && v0.ptMC() < antilambdaptedgevalues[i + 1]) { - pthistos::antilambdaPt[i]->Fill(v0.mAntiLambda(), collision.centFT0M()); + if (antilambdaptedgevalues[i] <= v0.pt() && v0.pt() < antilambdaptedgevalues[i + 1]) { + pthistos::antilambdaPt[i]->Fill(v0.mAntiLambda(), mcCollision.centFT0M()); } } } if (!v0mcParticle.isPhysicalPrimary()) { + // Split Feeddown Start + for (int i = 0; i < nAntilambdaHistograms; i++) { + if (antilambdaptedgevalues[i] <= v0.pt() && v0.pt() < antilambdaptedgevalues[i + 1]) { + pthistos::antilambdaSplitFeeddown[i]->Fill(v0.mAntiLambda(), mcCollision.centFT0M()); + } + } + // Split Feeddown End auto v0mother = v0.motherMCPart(); // Get mothers - rFeeddownMatrices.fill(HIST("hAntiLambdaFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), collision.centFT0M()); + rFeeddownMatrices.fill(HIST("hAntiLambdaFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); if (v0mother.pdgCode() == kXiPlusBar) { // Xi Plus Mother Matched - rFeeddownMatrices.fill(HIST("hAntiLambdaXiPlusFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), collision.centFT0M()); + rFeeddownMatrices.fill(HIST("hAntiLambdaXiPlusFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); } if (v0mother.pdgCode() == -kXi0) { // Anti-Xi Zero Mother Matched - rFeeddownMatrices.fill(HIST("hAntiLambdaAntiXiZeroFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), collision.centFT0M()); + rFeeddownMatrices.fill(HIST("hAntiLambdaAntiXiZeroFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); } if (v0mother.pdgCode() == kOmegaPlusBar) { // Anti-Omega (minus) Mother Matched - rFeeddownMatrices.fill(HIST("hAntiLambdaAntiOmegaFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), collision.centFT0M()); + rFeeddownMatrices.fill(HIST("hAntiLambdaAntiOmegaFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); } } } @@ -1388,12 +1474,302 @@ struct V0PtInvMassPlots { } } } + // This ii the process for the MC generated derived data + void genMCProcessDerived( + soa::Join::iterator const& mcCollision, + soa::SmallGroups> const& collisions, + soa::SmallGroups> const& V0s, + soa::SmallGroups> const& cascs) + { + // Event Efficiency, Event Split and V0 Signal Loss Corrections + rMCCorrections.fill(HIST("hNEvents_Corrections"), 0.5, mcCollision.centFT0M()); // All Events + if (std::abs(mcCollision.posZ()) > cutZVertex) { + return; + } + rMCCorrections.fill(HIST("hNEvents_Corrections"), 1.5, mcCollision.centFT0M()); // Event Efficiency Denominator + // rNchAnalysis.fill(HIST("hNchCentralityEtaHalfGen"), collisions.multNGlobalTracksPVetaHalf(), mcCollision.centFT0M()); // Nch vs Centrality EtaHalfCut + // Particles (of interest) Generated Pt Spectrum and Signal Loss Denominator Loop + for (const auto& v0 : V0s) { + if (v0.isPhysicalPrimary()) { + if (v0.pdgCode() == kK0Short) // kzero matched + { + if (std::abs(v0.rapidityMC(0)) < rapidityCut) { + rMCCorrections.fill(HIST("hK0shGeneratedPtSpectrum"), v0.ptMC(), mcCollision.centFT0M()); + // rMCCorrections.fill(HIST("hK0shGeneratedPtSpectrumMult"), v0.ptMC(), collisions.multMCNParticlesEta05()); // K0sh vs Centrality EtaHalfCut + } + } + if (v0.pdgCode() == kLambda0) // lambda matched + { + if (std::abs(v0.rapidityMC(1)) < rapidityCut) { + rMCCorrections.fill(HIST("hLambdaGeneratedPtSpectrum"), v0.ptMC(), mcCollision.centFT0M()); + // rMCCorrections.fill(HIST("hLambdaGeneratedPtSpectrumMult"), v0.ptMC(), collisions.multNGlobalTracksPVetaHalf()); // Lambda vs Centrality EtaHalfCut + } + } + if (v0.pdgCode() == kLambda0Bar) // antilambda matched + { + if (std::abs(v0.rapidityMC(2)) < rapidityCut) { + rMCCorrections.fill(HIST("hAntiLambdaGeneratedPtSpectrum"), v0.ptMC(), mcCollision.centFT0M()); + // rMCCorrections.fill(HIST("hAntiLambdaGeneratedPtSpectrumMult"), v0.ptMC(), collisions.multNGlobalTracksPVetaHalf()); // Anti-Lambda vs Centrality EtaHalfCut + } + } + } + } + for (const auto& casc : cascs) { + if (casc.isPhysicalPrimary()) { + // Make rapidity cuts, from O2's documentation: rapidity (0, 1: Xi; 2, 3: Omega) + if (casc.pdgCode() == kXiMinus) // Xi Minus matched + { + if (std::abs(casc.rapidityMC(0)) < rapidityCut) { + rMCCorrections.fill(HIST("hXiMinusGeneratedPtSpectrum"), casc.ptMC(), mcCollision.centFT0M()); + } + } + if (casc.pdgCode() == kXi0) // Xi Zero matched + { + if (std::abs(casc.rapidityMC(0)) < rapidityCut) { // Using the Xi mass assumption + rMCCorrections.fill(HIST("hXiZeroGeneratedPtSpectrum"), casc.ptMC(), mcCollision.centFT0M()); + } + } + if (casc.pdgCode() == kOmegaMinus) // Omega matched + { + if (std::abs(casc.rapidityMC(2)) < rapidityCut) { + rMCCorrections.fill(HIST("hOmegaGeneratedPtSpectrum"), casc.ptMC(), mcCollision.centFT0M()); + } + } + if (casc.pdgCode() == kXiPlusBar) // Xi Plus matched + { + if (std::abs(casc.rapidityMC(1)) < rapidityCut) { + rMCCorrections.fill(HIST("hXiPlusGeneratedPtSpectrum"), casc.ptMC(), mcCollision.centFT0M()); + } + } + if (casc.pdgCode() == -kXi0) // Anti-Xi Zero matched + { + if (std::abs(casc.rapidityMC(1)) < rapidityCut) { // Using the Xi mass assumption + rMCCorrections.fill(HIST("hAntiXiZeroGeneratedPtSpectrum"), casc.ptMC(), mcCollision.centFT0M()); + } + } + if (casc.pdgCode() == kOmegaPlusBar) // Anti-Omega matched + { + if (std::abs(casc.rapidityMC(2)) < rapidityCut) { + rMCCorrections.fill(HIST("hAntiOmegaGeneratedPtSpectrum"), casc.ptMC(), mcCollision.centFT0M()); + } + } + } + } // End of MCParticle Loop For Signal Loss Denominator + + bool hasSelectedInelGt0Collision = false; + for (const auto& collision : collisions) { + rMCCorrections.fill(HIST("hNEvents_Corrections"), 2.5, mcCollision.centFT0M()); // Number of Events Reconsctructed + if (!acceptEvent(collision)) { // Event Selection + continue; + } + rMCCorrections.fill(HIST("hNEvents_Corrections"), 3.5, mcCollision.centFT0M()); // Selected reconstructed collisions + if (collision.multNTracksPVeta1() > 0) { + hasSelectedInelGt0Collision = true; + } + } + + if (!hasSelectedInelGt0Collision) { + return; + } + + // Fill the signal-loss numerator once per MC collision. + for (const auto& v0 : V0s) { + if (!v0.isPhysicalPrimary()) { + continue; + } + if (v0.pdgCode() == kK0Short) // kzero matched + { + if (std::abs(v0.rapidityMC(0)) < rapidityCut) { + rMCCorrections.fill(HIST("hK0shGeneratedRecoPtSpectrum"), v0.ptMC(), mcCollision.centFT0M()); + } + } + if (v0.pdgCode() == kLambda0) // lambda matched + { + if (std::abs(v0.rapidityMC(1)) < rapidityCut) { + rMCCorrections.fill(HIST("hLambdaGeneratedRecoPtSpectrum"), v0.ptMC(), mcCollision.centFT0M()); + } + } + if (v0.pdgCode() == kLambda0Bar) // antilambda matched + { + if (std::abs(v0.rapidityMC(2)) < rapidityCut) { + rMCCorrections.fill(HIST("hAntiLambdaGeneratedRecoPtSpectrum"), v0.ptMC(), mcCollision.centFT0M()); + } + } + } + } + // This is the process for Real Data MC Closure Test + void dataProcessClosureTest(soa::Join::iterator const& collision, + soa::Join const& /*mcCollisions*/, + aod::V0Datas const& V0s, + DaughterTracks const&) + { + // tokenise strings into individual values + pthistos::kaonPtBins = o2::utils::Str::tokenize(kzeroSettingPtBinsString, ','); + pthistos::lambdaPtBins = o2::utils::Str::tokenize(lambdaSettingPtBinsString, ','); + pthistos::antilambdaPtBins = o2::utils::Str::tokenize(antilambdaSettingPtBinsString, ','); + + // Calculate number of histograms for each particle type + int nKaonHistograms = pthistos::kaonPtBins.size() - 1; + int nLambdaHistograms = pthistos::lambdaPtBins.size() - 1; + int nAntilambdaHistograms = pthistos::antilambdaPtBins.size() - 1; + + // initialize and convert tokenized strings into vector of doubles for Pt Bin Edges + std::vector kaonptedgevalues(nKaonHistograms + 1); + std::vector lambdaptedgevalues(nLambdaHistograms + 1); + std::vector antilambdaptedgevalues(nAntilambdaHistograms + 1); + + for (int i = 0; i < nKaonHistograms + 1; i++) { + kaonptedgevalues[i] = std::stod(pthistos::kaonPtBins[i]); + } + for (int i = 0; i < nLambdaHistograms + 1; i++) { + lambdaptedgevalues[i] = std::stod(pthistos::lambdaPtBins[i]); + } + for (int i = 0; i < nAntilambdaHistograms + 1; i++) { + antilambdaptedgevalues[i] = std::stod(pthistos::antilambdaPtBins[i]); + } + + // For centrality estimation + const auto& mcCollision = collision.mcCollision_as>(); + + if (!acceptEvent(collision)) { // Event Selection + return; + } + rPtAnalysis.fill(HIST("hNRecEvents"), 0.5, mcCollision.centFT0M()); // Number of recorded events + rNchAnalysis.fill(HIST("hNchCentrality"), mcCollision.centFT0M(), collision.multNTracksGlobal()); // Nch vs Centrality + rNchAnalysis.fill(HIST("hNchCentralityEtaHalf"), mcCollision.centFT0M(), collision.multNGlobalTracksPVetaHalf()); // Nch vs Centrality EtaHalfCut + for (const auto& v0 : V0s) { + // Checking that the V0 is a true K0s/Lambdas/Antilambdas and then filling the parameter histograms and the invariant mass plots for different cuts (which are taken from namespace) + const auto& posDaughterTrack = v0.template posTrack_as(); + const auto& negDaughterTrack = v0.template negTrack_as(); + if (!acceptV0(v0, posDaughterTrack, negDaughterTrack, collision)) { // V0 Selection + continue; + } + // K0sh analysis + if (kzeroAnalysis == true) { + if (acceptK0sh(v0, posDaughterTrack, negDaughterTrack, collision)) { // K0sh Selection + for (int i = 0; i < nKaonHistograms; i++) { + if (kaonptedgevalues[i] <= v0.pt() && v0.pt() < kaonptedgevalues[i + 1]) { // finding v0s with pt within the range of our bin edges + pthistos::kaonPt[i]->Fill(v0.mK0Short(), mcCollision.centFT0M()); // filling the k0s namespace histograms + } + } + } + } + // Lambda analysis + if (lambdaAnalysis == true) { + if (acceptLambda(v0, posDaughterTrack, negDaughterTrack, collision)) { // Lambda Selection + for (int i = 0; i < nLambdaHistograms; i++) { + if (lambdaptedgevalues[i] <= v0.pt() && v0.pt() < lambdaptedgevalues[i + 1]) { + pthistos::lambdaPt[i]->Fill(v0.mLambda(), mcCollision.centFT0M()); + } + } + } + } + // Anti-Lambda analysis + if (antiLambdaAnalysis == true) { + if (acceptAntilambda(v0, posDaughterTrack, negDaughterTrack, collision)) { // AntiLambda Selection + for (int i = 0; i < nAntilambdaHistograms; i++) { + if (antilambdaptedgevalues[i] <= v0.pt() && v0.pt() < antilambdaptedgevalues[i + 1]) { + pthistos::antilambdaPt[i]->Fill(v0.mAntiLambda(), mcCollision.centFT0M()); + } + } + } + } + } + } + // This is the process for Real Derived Data MC Closure Test + void dataProcessDerivedClosureTest( + soa::Join::iterator const& collision, + soa::Join const& /*mcCollisions*/, + soa::Join const& V0s, + DaughterTracksDerived const&) + { + // Tokenise strings into individual values + pthistos::kaonPtBins = o2::utils::Str::tokenize(kzeroSettingPtBinsString, ','); + pthistos::lambdaPtBins = o2::utils::Str::tokenize(lambdaSettingPtBinsString, ','); + pthistos::antilambdaPtBins = o2::utils::Str::tokenize(antilambdaSettingPtBinsString, ','); + + // Calculate number of histograms for each particle type + int nKaonHistograms = pthistos::kaonPtBins.size() - 1; + int nLambdaHistograms = pthistos::lambdaPtBins.size() - 1; + int nAntilambdaHistograms = pthistos::antilambdaPtBins.size() - 1; + + // Initialize and convert tokenized strings into vector of doubles for Pt Bin Edges + std::vector kaonptedgevalues(nKaonHistograms + 1); + std::vector lambdaptedgevalues(nLambdaHistograms + 1); + std::vector antilambdaptedgevalues(nAntilambdaHistograms + 1); + + for (int i = 0; i < nKaonHistograms + 1; i++) { + kaonptedgevalues[i] = std::stod(pthistos::kaonPtBins[i]); + } + for (int i = 0; i < nLambdaHistograms + 1; i++) { + lambdaptedgevalues[i] = std::stod(pthistos::lambdaPtBins[i]); + } + for (int i = 0; i < nAntilambdaHistograms + 1; i++) { + antilambdaptedgevalues[i] = std::stod(pthistos::antilambdaPtBins[i]); + } + + // For MC centrality estimation + const auto& mcCollision = collision.straMCCollision_as>(); + + if (!acceptEvent(collision)) { // Event Selection + return; + } + + rPtAnalysis.fill(HIST("hNRecEvents"), 0.5, mcCollision.centFT0M()); // Number of recorded events + rNchAnalysis.fill(HIST("hNchCentrality"), mcCollision.centFT0M(), collision.multNTracksGlobal()); // Nch vs Centrality + // rNchAnalysis.fill(HIST("hNchCentralityEtaHalf"), mcCollision.centFT0M(), collision.multNGlobalTracksPVetaHalf()); // Nch vs Centrality EtaHalfCut + + for (const auto& v0 : V0s) { + // Checking the V0 candidates and filling the parameter histograms and invariant mass plots + const auto& posDaughterTrack = v0.template posTrackExtra_as(); // Positive Daughter track + const auto& negDaughterTrack = v0.template negTrackExtra_as(); // Negative Daughter track + + if (!acceptV0Derived(v0, posDaughterTrack, negDaughterTrack, collision)) { // V0 Selection + continue; + } + + // K0sh analysis + if (kzeroAnalysis == true) { + if (acceptK0sh(v0, posDaughterTrack, negDaughterTrack, collision)) { // K0sh Selection + for (int i = 0; i < nKaonHistograms; i++) { + if (kaonptedgevalues[i] <= v0.pt() && v0.pt() < kaonptedgevalues[i + 1]) { + pthistos::kaonPt[i]->Fill(v0.mK0Short(), mcCollision.centFT0M()); + } + } + } + } + + // Lambda analysis + if (lambdaAnalysis == true) { + if (acceptLambda(v0, posDaughterTrack, negDaughterTrack, collision)) { // Lambda Selection + for (int i = 0; i < nLambdaHistograms; i++) { + if (lambdaptedgevalues[i] <= v0.pt() && v0.pt() < lambdaptedgevalues[i + 1]) { + pthistos::lambdaPt[i]->Fill(v0.mLambda(), mcCollision.centFT0M()); + } + } + } + } + + // Anti-Lambda analysis + if (antiLambdaAnalysis == true) { + if (acceptAntilambda(v0, posDaughterTrack, negDaughterTrack, collision)) { // AntiLambda Selection + for (int i = 0; i < nAntilambdaHistograms; i++) { + if (antilambdaptedgevalues[i] <= v0.pt() && v0.pt() < antilambdaptedgevalues[i + 1]) { + pthistos::antilambdaPt[i]->Fill(v0.mAntiLambda(), mcCollision.centFT0M()); + } + } + } + } + } + } PROCESS_SWITCH(V0PtInvMassPlots, genMCProcess, "Process Run 3 MC Generated", false); PROCESS_SWITCH(V0PtInvMassPlots, recMCProcess, "Process Run 3 MC Reconstructed", false); - PROCESS_SWITCH(V0PtInvMassPlots, dataProcess, "Process Run 3 Data,", false); - // PROCESS_SWITCH(V0PtInvMassPlots, genMCProcessDerived, "Process Run 3 MC Generated", false); - PROCESS_SWITCH(V0PtInvMassPlots, recMCProcessDerived, "Process Run 3 MC Reconstructed", false); - PROCESS_SWITCH(V0PtInvMassPlots, dataProcessDerived, "Process Run 3 Data,", false); + PROCESS_SWITCH(V0PtInvMassPlots, dataProcess, "Process Run 3 Data", false); + PROCESS_SWITCH(V0PtInvMassPlots, genMCProcessDerived, "Process Run 3 MC Generated Derived", false); + PROCESS_SWITCH(V0PtInvMassPlots, recMCProcessDerived, "Process Run 3 MC Reconstructed Derived", false); + PROCESS_SWITCH(V0PtInvMassPlots, dataProcessDerived, "Process Run 3 Data Derived", false); + PROCESS_SWITCH(V0PtInvMassPlots, dataProcessClosureTest, "Process Run 3 Data Closure Test", false); + PROCESS_SWITCH(V0PtInvMassPlots, dataProcessDerivedClosureTest, "Process Run 3 Data Derived Closure Test", false) }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGLF/Utils/CMakeLists.txt b/PWGLF/Utils/CMakeLists.txt index b81a6eb7198..c0996bebe49 100644 --- a/PWGLF/Utils/CMakeLists.txt +++ b/PWGLF/Utils/CMakeLists.txt @@ -37,6 +37,45 @@ o2physics_add_library(LfStrangenessBuilderModule SOURCES strangenessBuilderModule.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore KFParticle::KFParticle) -o2physics_add_library(LfCascadeMlResponse - SOURCES cascadeMlResponse.cxx +o2physics_add_library(LfMlResponse + SOURCES CascadeMlResponse.cxx + ResonanceMlResponse.cxx PUBLIC_LINK_LIBRARIES O2Physics::MLCore) + +# Precompiled header shared across the PWGLF strangeness workflows. Percentages +# are the share of the group's workflow translation units that include the header, +# measured over the tree. LFStrangenessPIDTables.h is deliberately left out: it +# reaches only 26% of them, and it includes LFStrangenessTables.h anyway. +# +# runDataProcessing.h is deliberately NOT precompiled: it requires each workflow's +# customize() to be declared before it is included, so precompiling it silently +# unregisters every option (the binary aborts at --dump-workflow, but links fine). +# +# Skipped under recc, which caches compilations remotely instead. +add_library(LfStrangenessPCH OBJECT strangenessPCH.cxx) +# The PCH compile is one of the heaviest translation units in the tree -- it +# instantiates the whole data model up front -- but add_library() does not put +# it in the 'analysis' job pool that o2physics_add_dpl_workflow() uses for the +# workflows. Left out, it runs unthrottled alongside the pooled compilations +# and defeats the per-job memory budget the pool exists to enforce. +set_property(TARGET LfStrangenessPCH PROPERTY JOB_POOL_COMPILE analysis) +target_link_libraries(LfStrangenessPCH PUBLIC O2::Framework O2Physics::AnalysisCore) +if(NOT DEFINED ENV{USE_RECC}) + target_precompile_headers(LfStrangenessPCH PRIVATE + # Framework baseline -- same set AnalysisPCH precompiles. A target with + # REUSE_FROM uses ONLY this PCH, so anything left out here is parsed from + # scratch by every consumer: without this block these workflows precompile + # LESS than the default carrier they opted out of. + + + + + + + + + + + [["PWGLF/DataModel/LFStrangenessTables.h"]] # 100%, 2045 lines + ) +endif() diff --git a/PWGLF/Utils/cascadeMlResponse.cxx b/PWGLF/Utils/CascadeMlResponse.cxx similarity index 96% rename from PWGLF/Utils/cascadeMlResponse.cxx rename to PWGLF/Utils/CascadeMlResponse.cxx index 43cb3236f9b..3be1d105e50 100644 --- a/PWGLF/Utils/cascadeMlResponse.cxx +++ b/PWGLF/Utils/CascadeMlResponse.cxx @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file cascadeMlResponse.cxx +/// \file CascadeMlResponse.cxx /// \brief Helper file providing the compilation command for the CascadeMlResponse header. /// /// \author ALICE Collaboration diff --git a/PWGLF/Utils/ResonanceMlResponse.cxx b/PWGLF/Utils/ResonanceMlResponse.cxx new file mode 100644 index 00000000000..71dc6dc9d7b --- /dev/null +++ b/PWGLF/Utils/ResonanceMlResponse.cxx @@ -0,0 +1,18 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file ResonanceMlResponse.cxx +/// \brief Helper file providing the compilation command for the ResonanceMlResponse header. +/// +/// \author ALICE Collaboration + +// o2-linter: disable=name/workflow-file (not a workflow file) +#include "ResonanceMlResponse.h" // IWYU pragma: keep diff --git a/PWGLF/Utils/ResonanceMlResponse.h b/PWGLF/Utils/ResonanceMlResponse.h new file mode 100644 index 00000000000..e3051ef81c6 --- /dev/null +++ b/PWGLF/Utils/ResonanceMlResponse.h @@ -0,0 +1,328 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file ResonanceMlResponse.h +/// \brief Class to compute the ML response for Lambda + photon resonance selections (Sigma0, Lambda(1520)), +/// either from sigma0builder candidates or from a Lambda and a photon V0 of the strangeness derived data +/// \author Oussama Benchikhi + +#ifndef PWGLF_UTILS_RESONANCEMLRESPONSE_H_ +#define PWGLF_UTILS_RESONANCEMLRESPONSE_H_ + +#include "PWGLF/DataModel/LFStrangenessPIDTables.h" +#include "PWGLF/DataModel/LFStrangenessTables.h" + +#include "Tools/ML/MlResponse.h" + +#include + +#include +#include + +namespace o2::analysis +{ +// list of input features that can be requested via the namesInputFeatures configurable (sigma0builder getter names) +enum class InputFeaturesResonance : uint8_t { + // Lambda + lambdaQt = 0, + lambdaAlpha, + lambdaRadius, + lambdaCosPA, + lambdaDCADau, + lambdaDCANegPV, + lambdaDCAPosPV, + lambdaPosEta, + lambdaNegEta, + lambdaPosPrTPCNSigma, + lambdaPosPiTPCNSigma, + lambdaNegPrTPCNSigma, + lambdaNegPiTPCNSigma, + // Photon + photonQt, + photonAlpha, + photonCosPA, + photonDCADau, + photonDCANegPV, + photonDCAPosPV, + photonRadius, + photonZconv, + photonPsiPair, + photonPosEta, + photonNegEta, + photonPosTPCNSigmaEl, + photonNegTPCNSigmaEl, + // Photon-Lambda pair + opAngle, + apAlpha, + apQt +}; + +template +class ResonanceMlResponse : public MlResponse +{ + public: + ResonanceMlResponse() = default; + ~ResonanceMlResponse() override = default; + + /// Input features of a photon-Lambda pair + template , typename TLambda, typename TPhoton> + std::vector getInputFeatures(TLambda const& lambda, TPhoton const& photon, float opAngle, float apAlpha, float apQt) + { + // sigma0builder candidates carry prefixed columns (lambdaQt(), photonQt(), ...), derived V0s the plain V0 getters + constexpr bool LambdaFromSigma0 = requires(TLambda const& cand) { cand.lambdaQt(); }; + constexpr bool PhotonFromSigma0 = requires(TPhoton const& cand) { cand.photonQt(); }; + + std::vector inputFeatures; + inputFeatures.reserve(MlResponse::mCachedIndices.size()); + + for (const auto& idx : MlResponse::mCachedIndices) { + switch (static_cast(idx)) { + // Lambda + case InputFeaturesResonance::lambdaQt: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaQt()); + } else { + inputFeatures.emplace_back(lambda.qtarm()); + } + break; + case InputFeaturesResonance::lambdaAlpha: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaAlpha()); + } else { + inputFeatures.emplace_back(lambda.alpha()); + } + break; + case InputFeaturesResonance::lambdaRadius: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaRadius()); + } else { + inputFeatures.emplace_back(lambda.v0radius()); + } + break; + case InputFeaturesResonance::lambdaCosPA: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaCosPA()); + } else { + inputFeatures.emplace_back(lambda.v0cosPA()); + } + break; + case InputFeaturesResonance::lambdaDCADau: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaDCADau()); + } else { + inputFeatures.emplace_back(lambda.dcaV0daughters()); + } + break; + case InputFeaturesResonance::lambdaDCANegPV: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaDCANegPV()); + } else { + inputFeatures.emplace_back(lambda.dcanegtopv()); + } + break; + case InputFeaturesResonance::lambdaDCAPosPV: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaDCAPosPV()); + } else { + inputFeatures.emplace_back(lambda.dcapostopv()); + } + break; + case InputFeaturesResonance::lambdaPosEta: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaPosEta()); + } else { + inputFeatures.emplace_back(lambda.positiveeta()); + } + break; + case InputFeaturesResonance::lambdaNegEta: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaNegEta()); + } else { + inputFeatures.emplace_back(lambda.negativeeta()); + } + break; + case InputFeaturesResonance::lambdaPosPrTPCNSigma: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaPosPrTPCNSigma()); + } else { + inputFeatures.emplace_back(lambda.template posTrackExtra_as().tpcNSigmaPr()); + } + break; + case InputFeaturesResonance::lambdaPosPiTPCNSigma: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaPosPiTPCNSigma()); + } else { + inputFeatures.emplace_back(lambda.template posTrackExtra_as().tpcNSigmaPi()); + } + break; + case InputFeaturesResonance::lambdaNegPrTPCNSigma: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaNegPrTPCNSigma()); + } else { + inputFeatures.emplace_back(lambda.template negTrackExtra_as().tpcNSigmaPr()); + } + break; + case InputFeaturesResonance::lambdaNegPiTPCNSigma: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaNegPiTPCNSigma()); + } else { + inputFeatures.emplace_back(lambda.template negTrackExtra_as().tpcNSigmaPi()); + } + break; + // Photon + case InputFeaturesResonance::photonQt: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonQt()); + } else { + inputFeatures.emplace_back(photon.qtarm()); + } + break; + case InputFeaturesResonance::photonAlpha: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonAlpha()); + } else { + inputFeatures.emplace_back(photon.alpha()); + } + break; + case InputFeaturesResonance::photonCosPA: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonCosPA()); + } else { + inputFeatures.emplace_back(photon.v0cosPA()); + } + break; + case InputFeaturesResonance::photonDCADau: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonDCADau()); + } else { + inputFeatures.emplace_back(photon.dcaV0daughters()); + } + break; + case InputFeaturesResonance::photonDCANegPV: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonDCANegPV()); + } else { + inputFeatures.emplace_back(photon.dcanegtopv()); + } + break; + case InputFeaturesResonance::photonDCAPosPV: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonDCAPosPV()); + } else { + inputFeatures.emplace_back(photon.dcapostopv()); + } + break; + case InputFeaturesResonance::photonRadius: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonRadius()); + } else { + inputFeatures.emplace_back(photon.v0radius()); + } + break; + case InputFeaturesResonance::photonZconv: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonZconv()); + } else { + inputFeatures.emplace_back(photon.z()); + } + break; + case InputFeaturesResonance::photonPsiPair: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonPsiPair()); + } else { + inputFeatures.emplace_back(photon.psipair()); + } + break; + case InputFeaturesResonance::photonPosEta: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonPosEta()); + } else { + inputFeatures.emplace_back(photon.positiveeta()); + } + break; + case InputFeaturesResonance::photonNegEta: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonNegEta()); + } else { + inputFeatures.emplace_back(photon.negativeeta()); + } + break; + case InputFeaturesResonance::photonPosTPCNSigmaEl: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonPosTPCNSigmaEl()); + } else { + inputFeatures.emplace_back(photon.template posTrackExtra_as().tpcNSigmaEl()); + } + break; + case InputFeaturesResonance::photonNegTPCNSigmaEl: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonNegTPCNSigmaEl()); + } else { + inputFeatures.emplace_back(photon.template negTrackExtra_as().tpcNSigmaEl()); + } + break; + // Photon-Lambda pair + case InputFeaturesResonance::opAngle: + inputFeatures.emplace_back(opAngle); + break; + + case InputFeaturesResonance::apAlpha: + inputFeatures.emplace_back(apAlpha); + break; + + case InputFeaturesResonance::apQt: + inputFeatures.emplace_back(apQt); + break; + } + } + return inputFeatures; + } + + protected: + /// Method to fill the map of available input features + void setAvailableInputFeatures() override + { + MlResponse::mAvailableInputFeatures = { + {"lambdaQt", static_cast(InputFeaturesResonance::lambdaQt)}, + {"lambdaAlpha", static_cast(InputFeaturesResonance::lambdaAlpha)}, + {"lambdaRadius", static_cast(InputFeaturesResonance::lambdaRadius)}, + {"lambdaCosPA", static_cast(InputFeaturesResonance::lambdaCosPA)}, + {"lambdaDCADau", static_cast(InputFeaturesResonance::lambdaDCADau)}, + {"lambdaDCANegPV", static_cast(InputFeaturesResonance::lambdaDCANegPV)}, + {"lambdaDCAPosPV", static_cast(InputFeaturesResonance::lambdaDCAPosPV)}, + {"lambdaPosEta", static_cast(InputFeaturesResonance::lambdaPosEta)}, + {"lambdaNegEta", static_cast(InputFeaturesResonance::lambdaNegEta)}, + {"lambdaPosPrTPCNSigma", static_cast(InputFeaturesResonance::lambdaPosPrTPCNSigma)}, + {"lambdaPosPiTPCNSigma", static_cast(InputFeaturesResonance::lambdaPosPiTPCNSigma)}, + {"lambdaNegPrTPCNSigma", static_cast(InputFeaturesResonance::lambdaNegPrTPCNSigma)}, + {"lambdaNegPiTPCNSigma", static_cast(InputFeaturesResonance::lambdaNegPiTPCNSigma)}, + {"photonQt", static_cast(InputFeaturesResonance::photonQt)}, + {"photonAlpha", static_cast(InputFeaturesResonance::photonAlpha)}, + {"photonCosPA", static_cast(InputFeaturesResonance::photonCosPA)}, + {"photonDCADau", static_cast(InputFeaturesResonance::photonDCADau)}, + {"photonDCANegPV", static_cast(InputFeaturesResonance::photonDCANegPV)}, + {"photonDCAPosPV", static_cast(InputFeaturesResonance::photonDCAPosPV)}, + {"photonRadius", static_cast(InputFeaturesResonance::photonRadius)}, + {"photonZconv", static_cast(InputFeaturesResonance::photonZconv)}, + {"photonPsiPair", static_cast(InputFeaturesResonance::photonPsiPair)}, + {"photonPosEta", static_cast(InputFeaturesResonance::photonPosEta)}, + {"photonNegEta", static_cast(InputFeaturesResonance::photonNegEta)}, + {"photonPosTPCNSigmaEl", static_cast(InputFeaturesResonance::photonPosTPCNSigmaEl)}, + {"photonNegTPCNSigmaEl", static_cast(InputFeaturesResonance::photonNegTPCNSigmaEl)}, + {"opAngle", static_cast(InputFeaturesResonance::opAngle)}, + {"apAlpha", static_cast(InputFeaturesResonance::apAlpha)}, + {"apQt", static_cast(InputFeaturesResonance::apQt)}}; + } +}; + +} // namespace o2::analysis + +#endif // PWGLF_UTILS_RESONANCEMLRESPONSE_H_ diff --git a/PWGLF/Utils/collisionCutsGroupLinkDef.h b/PWGLF/Utils/collisionCutsGroupLinkDef.h index e327556fbd2..a6aa000a66e 100644 --- a/PWGLF/Utils/collisionCutsGroupLinkDef.h +++ b/PWGLF/Utils/collisionCutsGroupLinkDef.h @@ -17,8 +17,7 @@ /// /// \author Bong-Hwi Lim -#ifndef PWGLF_UTILS_COLLISIONCUTSGROUPLINKDEF_H_ -#define PWGLF_UTILS_COLLISIONCUTSGROUPLINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -30,5 +29,3 @@ #pragma link C++ class std::vector < int> + ; #pragma link C++ class std::vector < float> + ; #pragma link C++ class std::vector < bool> + ; - -#endif // PWGLF_UTILS_COLLISIONCUTSGROUPLINKDEF_H_ diff --git a/PWGLF/Utils/doubleOmegaBuilderHelper.h b/PWGLF/Utils/doubleOmegaBuilderHelper.h new file mode 100644 index 00000000000..ac071658516 --- /dev/null +++ b/PWGLF/Utils/doubleOmegaBuilderHelper.h @@ -0,0 +1,628 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file doubleOmegaBuilderHelper.h +/// \brief Building blocks of the double-Omega (Omega + Lambda + kaon) reconstruction, shared by the +/// candidate builder (derived data producer) and the tree creator. + +#ifndef PWGLF_UTILS_DOUBLEOMEGABUILDERHELPER_H_ +#define PWGLF_UTILS_DOUBLEOMEGABUILDERHELPER_H_ + +#include "Common/Core/RecoDecay.h" +#include "Common/Core/trackUtilities.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace o2::pwglf::doubleomega +{ + +struct DoubleOmegaCandidate { + float pt = -999.f; + float eta = -999.f; + float phi = -999.f; + float x = -999.f; + float y = -999.f; + float z = -999.f; + float pvX = -999.f; + float pvY = -999.f; + float pvZ = -999.f; + float cosPAOmega = -999.f; + float cosPADirectLambda = -999.f; + float cosPADoubleOmega = -999.f; + float cosPAOmegaToPV = -999.f; + float cosPADirectLambdaToPV = -999.f; + float dcaXYOmegaToPV = -999.f; + float dcaXYDirectLambdaToPV = -999.f; + float dcaXYDirectKaonToPV = -999.f; + float dcaXYOmegaKaonToPV = -999.f; + float dcaXYOmegaProtonToPV = -999.f; + float dcaXYOmegaPionToPV = -999.f; + float dcaXYDirectProtonToPV = -999.f; + float dcaXYDirectPionToPV = -999.f; + float tpcNSigmaDirectKaon = -999.f; + float tofNSigmaDirectKaon = -999.f; + uint32_t daughterDetectorMap = 0; + float mass = -999.f; + float massOmega = -999.f; + float massXi = -999.f; + int8_t sign = 0; +}; + +struct LambdaCandidate { + float px = 0.f; + float py = 0.f; + float pz = 0.f; + float mass = -999.f; +}; + +struct BuiltLambdaCandidate { + LambdaCandidate candidate; + o2::track::TrackParCov parentTrack; + std::array decayVertex{}; + int64_t posTrackId = -1; + int64_t negTrackId = -1; + uint32_t daughterDetectorMap = 0; // Proton, pion; three detector bits each. + float dcaXYProtonToPV = -999.f; + float dcaXYPionToPV = -999.f; + float dcaDaughters = -999.f; +}; + +struct BuiltOmegaCandidate { + float px = 0.f; + float py = 0.f; + float pz = 0.f; + float x = 0.f; + float y = 0.f; + float z = 0.f; + float massOmega = -999.f; + float massXi = -999.f; + int8_t sign = 0; + int64_t posTrackId = -1; + int64_t negTrackId = -1; + int64_t bachelorId = -1; + o2::track::TrackParCov parentTrack; + uint32_t daughterDetectorMap = 0; // Kaon, proton, pion; three detector bits each. + float dcaXYKaonToPV = -999.f; + float dcaXYProtonToPV = -999.f; + float dcaXYPionToPV = -999.f; + float dcaDaughters = -999.f; +}; + +struct BuiltKaonCandidate { + o2::track::TrackParCov track; + int64_t trackId = -1; + int8_t sign = 0; + float tpcNSigma = -999.f; // -999 when TPC is unavailable. + float tofNSigma = -999.f; // -999 when TOF is unavailable. + uint32_t detectorMap = 0; +}; + +// Omega, Lambda and kaon candidates of one collision, built once and paired afterwards. +// Lambdas and kaons are stored per charge of the Omega they can be paired with (see chargeIndex). +struct CollisionCandidates { + std::array primaryVertex{}; + std::vector omegas; + std::array, 2> lambdas; + std::array, 2> kaons; +}; + +struct DoubleOmegaSelections { + float etaMax = 0.9f; + float minNITSClus = 0.f; + float minNTPCClus = 80.f; + float minNCrossedRows = 100.f; + float maxNSharedTPCClus = 5.f; + float nSigmaTPCCut = 3.f; + float dcaBachToPV = 0.05f; + float dcaKaonToPV = 0.05f; + float dcaOmegaToPV = 0.f; + float dcaDirectLambdaToPV = 0.f; + float dcaV0DauToPV = 0.05f; + float dcaV0Bach = 1.f; + float dcaLambdaDaughters = 1.f; + float mOmegaWindow = 0.01f; + float mLambdaWindow = 0.01f; + float maxDoubleOmegaMass = 3.6f; + float minCosPAOmega = -1.f; + float minCosPADirectLambda = -1.f; + float minCosPADoubleOmega = -1.f; + float minDoubleOmegaDecayRadius = 1.f; +}; + +class DoubleOmegaBuilderHelper +{ + public: + DoubleOmegaSelections selections; + o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; + o2::vertexing::DCAFitterN<2> fitter; + o2::vertexing::DCAFitterN<3> fitter3Body; + + void initFitters() + { + configureFitter(fitter); + configureFitter(fitter3Body); + } + + void setBz(float bz) + { + fitter.setBz(bz); + fitter3Body.setBz(bz); + } + + void setMatCorrType(o2::base::Propagator::MatCorrType matCorrType) + { + matCorr = matCorrType; + fitter.setMatCorrType(matCorrType); + fitter3Body.setMatCorrType(matCorrType); + } + + static int chargeIndex(int8_t charge) + { + return charge < 0 ? 0 : 1; + } + + template + static uint32_t detectorMap(Track const& track) + { + return static_cast(track.hasITS()) | + (static_cast(track.hasTPC()) << 1) | + (static_cast(track.hasTOF()) << 2); + } + + template + static BuiltKaonCandidate buildKaon(Track const& track) + { + return {getTrackParCov(track), + track.globalIndex(), + static_cast(track.sign()), + track.hasTPC() ? track.tpcNSigmaKa() : -999.f, + track.hasTOF() ? track.tofNSigmaKa() : -999.f, + detectorMap(track)}; + } + + static bool sharesTracks(BuiltOmegaCandidate const& omega, BuiltLambdaCandidate const& lambda) + { + const std::array omegaTrackIds{omega.posTrackId, omega.negTrackId, omega.bachelorId}; + return std::find(omegaTrackIds.begin(), omegaTrackIds.end(), lambda.posTrackId) != omegaTrackIds.end() || + std::find(omegaTrackIds.begin(), omegaTrackIds.end(), lambda.negTrackId) != omegaTrackIds.end(); + } + + static bool sharesTracks(BuiltOmegaCandidate const& omega, BuiltKaonCandidate const& kaon) + { + return kaon.trackId == omega.posTrackId || kaon.trackId == omega.negTrackId || kaon.trackId == omega.bachelorId; + } + + static bool sharesTracks(BuiltOmegaCandidate const& omega, BuiltLambdaCandidate const& lambda, BuiltKaonCandidate const& kaon) + { + return sharesTracks(omega, lambda) || sharesTracks(omega, kaon) || + kaon.trackId == lambda.posTrackId || kaon.trackId == lambda.negTrackId; + } + + template + bool selectTrack(T const& track) const + { + if (std::abs(track.eta()) > selections.etaMax) { + return false; + } + if (track.itsNCls() < selections.minNITSClus || + track.tpcNClsFound() < selections.minNTPCClus || + track.tpcNClsCrossedRows() < selections.minNCrossedRows || + track.tpcNClsCrossedRows() < 0.8f * track.tpcNClsFindable() || + track.tpcNClsShared() > selections.maxNSharedTPCClus) { + LOG(debug) << "Track failed selection: itsNCls=" << static_cast(track.itsNCls()) << ", tpcNClsFound=" << track.tpcNClsFound() << ", tpcNClsCrossedRows=" << static_cast(track.tpcNClsCrossedRows()) << ", tpcNClsFindable=" << static_cast(track.tpcNClsFindable()) << ", tpcNClsShared=" << static_cast(track.tpcNClsShared()); + return false; + } + return true; + } + + template + bool isSelectedKaon(Track const& track, int8_t charge) const + { + return track.sign() == charge && isSelectedKaon(track); + } + + template + bool isSelectedKaon(Track const& track) const + { + return selectTrack(track) && + std::abs(track.tpcNSigmaKa()) <= selections.nSigmaTPCCut; + } + + // Transverse DCA of a track to the collision primary vertex, computed by propagating + // a copy of the track parameters. Returns false if the propagation fails. + template + bool dcaXYToPV(C const& collision, Track const& track, float& dcaXY) const + { + auto trackParCov = getTrackParCov(track); + const o2::math_utils::Point3D primaryVertex{collision.posX(), collision.posY(), collision.posZ()}; + std::array dca{}; + if (!o2::base::Propagator::Instance()->propagateToDCABxByBz(primaryVertex, trackParCov, 2.f, matCorr, &dca)) { + return false; + } + dcaXY = dca[0]; + return true; + } + + template + bool buildLambda(C const& collision, Track const& posTrack, Track const& negTrack, int8_t charge, BuiltLambdaCandidate& builtLambda) + { + if (posTrack.sign() <= 0 || negTrack.sign() >= 0 || + !selectTrack(posTrack) || !selectTrack(negTrack)) { + return false; + } + + const bool isMatter = charge < 0; + const float protonNSigma = isMatter ? posTrack.tpcNSigmaPr() : negTrack.tpcNSigmaPr(); + const float pionNSigma = isMatter ? negTrack.tpcNSigmaPi() : posTrack.tpcNSigmaPi(); + if (std::abs(protonNSigma) > selections.nSigmaTPCCut || std::abs(pionNSigma) > selections.nSigmaTPCCut) { + return false; + } + float dcaXYPos = -999.f; + float dcaXYNeg = -999.f; + if (!dcaXYToPV(collision, posTrack, dcaXYPos) || !dcaXYToPV(collision, negTrack, dcaXYNeg)) { + LOG(debug) << "Propagation to the PV failed for Lambda daughters " << posTrack.globalIndex() << ", " << negTrack.globalIndex(); + return false; + } + if (std::abs(dcaXYPos) < selections.dcaV0DauToPV || std::abs(dcaXYNeg) < selections.dcaV0DauToPV) { + LOG(debug) << "Lambda dau don't pass selections: " << "nsigmaPR: " << protonNSigma << ", pionNSigma: " << pionNSigma << ", dcaXYPos: " << dcaXYPos << ", dcaXYNeg: " << dcaXYNeg; + return false; + } + + auto posTrackParCov = getTrackParCov(posTrack); + auto negTrackParCov = getTrackParCov(negTrack); + int nCandidates = 0; + try { + nCandidates = fitter.process(posTrackParCov, negTrackParCov); + } catch (...) { + LOG(error) << "Exception while fitting Lambda daughter tracks " << posTrack.globalIndex() << ", " << negTrack.globalIndex(); + return false; + } + if (nCandidates == 0) { + return false; + } + + std::array posMomentum{}; + std::array negMomentum{}; + fitter.getTrack(0).getPxPyPzGlo(posMomentum); + fitter.getTrack(1).getPxPyPzGlo(negMomentum); + const std::array lambdaMomentum{ + posMomentum[0] + negMomentum[0], + posMomentum[1] + negMomentum[1], + posMomentum[2] + negMomentum[2]}; + const auto& fittedVertex = fitter.getPCACandidate(); + const std::array decayVertex{ + static_cast(fittedVertex[0]), static_cast(fittedVertex[1]), static_cast(fittedVertex[2])}; + const float mass = isMatter ? RecoDecay::m(std::array{posMomentum, negMomentum}, + std::array{o2::constants::physics::MassProton, o2::constants::physics::MassPionCharged}) + : RecoDecay::m(std::array{posMomentum, negMomentum}, + std::array{o2::constants::physics::MassPionCharged, o2::constants::physics::MassProton}); + const float dcaDaughters = std::sqrt(std::abs(fitter.getChi2AtPCACandidate())); + const float eta = RecoDecay::eta(lambdaMomentum); + if (std::abs(eta) > selections.etaMax || + dcaDaughters > selections.dcaLambdaDaughters || + std::abs(mass - o2::constants::physics::MassLambda0) > selections.mLambdaWindow) { + return false; + } + + builtLambda.candidate = { + .px = lambdaMomentum[0], + .py = lambdaMomentum[1], + .pz = lambdaMomentum[2], + .mass = mass}; + builtLambda.parentTrack = fitter.createParentTrackParCov(0); + builtLambda.decayVertex = decayVertex; + builtLambda.posTrackId = posTrack.globalIndex(); + builtLambda.negTrackId = negTrack.globalIndex(); + // Keep particle-role ordering for matter and antimatter alike. + builtLambda.daughterDetectorMap = isMatter ? detectorMap(posTrack) | (detectorMap(negTrack) << 3) + : detectorMap(negTrack) | (detectorMap(posTrack) << 3); + builtLambda.dcaXYProtonToPV = isMatter ? dcaXYPos : dcaXYNeg; + builtLambda.dcaXYPionToPV = isMatter ? dcaXYNeg : dcaXYPos; + builtLambda.dcaDaughters = dcaDaughters; + return true; + } + + template + bool buildOmega(C const& collision, Track const& posTrack, Track const& negTrack, Track const& bachelor, BuiltOmegaCandidate& builtOmega) + { + if (bachelor.sign() == 0 || + !selectTrack(bachelor) || + std::abs(bachelor.tpcNSigmaKa()) > selections.nSigmaTPCCut) { + return false; + } + float dcaXYBachelor = -999.f; + if (!dcaXYToPV(collision, bachelor, dcaXYBachelor) || std::abs(dcaXYBachelor) < selections.dcaBachToPV) { + return false; + } + + BuiltLambdaCandidate lambda; + if (!buildLambda(collision, posTrack, negTrack, bachelor.sign(), lambda)) { + return false; + } + + auto v0TrackParCov = lambda.parentTrack; + auto bachelorTrackParCov = getTrackParCov(bachelor); + int nCandidates = 0; + try { + nCandidates = fitter.process(v0TrackParCov, bachelorTrackParCov); + } catch (...) { + LOG(error) << "Exception while fitting Omega daughter tracks " << posTrack.globalIndex() << ", " << negTrack.globalIndex() << ", " << bachelor.globalIndex(); + return false; + } + if (nCandidates == 0) { + return false; + } + + std::array lambdaMomentum{}; + std::array bachelorMomentum{}; + fitter.getTrack(0).getPxPyPzGlo(lambdaMomentum); + fitter.getTrack(1).getPxPyPzGlo(bachelorMomentum); + const std::array omegaMomentum{ + lambdaMomentum[0] + bachelorMomentum[0], + lambdaMomentum[1] + bachelorMomentum[1], + lambdaMomentum[2] + bachelorMomentum[2]}; + const auto& fittedVertex = fitter.getPCACandidate(); + const std::array decayVertex{ + static_cast(fittedVertex[0]), static_cast(fittedVertex[1]), static_cast(fittedVertex[2])}; + const float massOmega = RecoDecay::m(std::array{lambdaMomentum, bachelorMomentum}, + std::array{o2::constants::physics::MassLambda0, o2::constants::physics::MassKaonCharged}); + const float massXi = RecoDecay::m(std::array{lambdaMomentum, bachelorMomentum}, + std::array{o2::constants::physics::MassLambda0, o2::constants::physics::MassPionCharged}); + const float dcaDaughters = std::sqrt(std::abs(fitter.getChi2AtPCACandidate())); + const float eta = RecoDecay::eta(omegaMomentum); + if (dcaDaughters > selections.dcaV0Bach || + std::abs(eta) > selections.etaMax || + std::abs(massOmega - o2::constants::physics::MassOmegaMinus) > selections.mOmegaWindow) { + return false; + } + + builtOmega = { + omegaMomentum[0], + omegaMomentum[1], + omegaMomentum[2], + decayVertex[0], + decayVertex[1], + decayVertex[2], + massOmega, + massXi, + static_cast(bachelor.sign()), + posTrack.globalIndex(), + negTrack.globalIndex(), + bachelor.globalIndex(), + fitter.createParentTrackParCov(), + detectorMap(bachelor) | (lambda.daughterDetectorMap << 3), + dcaXYBachelor, + lambda.dcaXYProtonToPV, + lambda.dcaXYPionToPV, + dcaDaughters}; + return true; + } + + // Selections of the Omega, Lambda and kaon candidates that can be applied again on their stored + // properties (e.g. when reading them back from derived data), to tighten the production selections. + bool isSelectedOmega(BuiltOmegaCandidate const& omega) const + { + return std::abs(RecoDecay::eta(std::array{omega.px, omega.py, omega.pz})) <= selections.etaMax && + std::abs(omega.massOmega - o2::constants::physics::MassOmegaMinus) <= selections.mOmegaWindow && + omega.dcaDaughters <= selections.dcaV0Bach && + std::abs(omega.dcaXYKaonToPV) >= selections.dcaBachToPV && + std::abs(omega.dcaXYProtonToPV) >= selections.dcaV0DauToPV && + std::abs(omega.dcaXYPionToPV) >= selections.dcaV0DauToPV; + } + + bool isSelectedLambda(BuiltLambdaCandidate const& lambda) const + { + return std::abs(RecoDecay::eta(std::array{lambda.candidate.px, lambda.candidate.py, lambda.candidate.pz})) <= selections.etaMax && + std::abs(lambda.candidate.mass - o2::constants::physics::MassLambda0) <= selections.mLambdaWindow && + lambda.dcaDaughters <= selections.dcaLambdaDaughters && + std::abs(lambda.dcaXYProtonToPV) >= selections.dcaV0DauToPV && + std::abs(lambda.dcaXYPionToPV) >= selections.dcaV0DauToPV; + } + + bool isSelectedKaon(BuiltKaonCandidate const& kaon) const + { + return kaon.tpcNSigma > -999.f && std::abs(kaon.tpcNSigma) <= selections.nSigmaTPCCut; + } + + bool buildDoubleOmega(std::array const& primaryVertex, BuiltOmegaCandidate const& omega, BuiltLambdaCandidate const& directLambda, BuiltKaonCandidate const& kaon, DoubleOmegaCandidate& cand) + { + auto omegaTrackParCov = omega.parentTrack; + auto lambdaTrackParCov = directLambda.parentTrack; + auto kaonTrackParCov = kaon.track; + int nCandidates = 0; + try { + nCandidates = fitter3Body.process(omegaTrackParCov, lambdaTrackParCov, kaonTrackParCov); + } catch (...) { + LOG(error) << "Exception while fitting the double-Omega candidate"; + return false; + } + if (nCandidates == 0) { + return false; + } + + auto omegaTrackAtVertex = fitter3Body.getTrack(0); + auto lambdaTrackAtVertex = fitter3Body.getTrack(1); + auto kaonTrackAtVertex = fitter3Body.getTrack(2); + std::array omegaMomentum{}; + std::array lambdaMomentum{}; + std::array kaonMomentum{}; + omegaTrackAtVertex.getPxPyPzGlo(omegaMomentum); + lambdaTrackAtVertex.getPxPyPzGlo(lambdaMomentum); + kaonTrackAtVertex.getPxPyPzGlo(kaonMomentum); + + const std::array totalMomentum{ + omegaMomentum[0] + lambdaMomentum[0] + kaonMomentum[0], + omegaMomentum[1] + lambdaMomentum[1] + kaonMomentum[1], + omegaMomentum[2] + lambdaMomentum[2] + kaonMomentum[2]}; + const auto& fittedVertex = fitter3Body.getPCACandidate(); + const std::array decayVertex{ + static_cast(fittedVertex[0]), static_cast(fittedVertex[1]), static_cast(fittedVertex[2])}; + const o2::math_utils::Point3D primaryVertexPoint{primaryVertex[0], primaryVertex[1], primaryVertex[2]}; + const std::array omegaDecayVertex{omega.x, omega.y, omega.z}; + + std::array dcaOmega{}; + std::array dcaDirectLambda{}; + std::array dcaDirectKaon{}; + if (!o2::base::Propagator::Instance()->propagateToDCABxByBz(primaryVertexPoint, omegaTrackAtVertex, 2.f, matCorr, &dcaOmega) || + !o2::base::Propagator::Instance()->propagateToDCABxByBz(primaryVertexPoint, lambdaTrackAtVertex, 2.f, matCorr, &dcaDirectLambda) || + !o2::base::Propagator::Instance()->propagateToDCABxByBz(primaryVertexPoint, kaonTrackAtVertex, 2.f, matCorr, &dcaDirectKaon)) { + return false; + } + + const float massSquared = RecoDecay::m2(std::array{omegaMomentum, lambdaMomentum, kaonMomentum}, + std::array{o2::constants::physics::MassOmegaMinus, o2::constants::physics::MassLambda0, o2::constants::physics::MassKaonCharged}); + + cand.pt = std::hypot(totalMomentum[0], totalMomentum[1]); + cand.eta = RecoDecay::eta(totalMomentum); + cand.phi = std::atan2(totalMomentum[1], totalMomentum[0]); + cand.x = decayVertex[0]; + cand.y = decayVertex[1]; + cand.z = decayVertex[2]; + cand.pvX = primaryVertex[0]; + cand.pvY = primaryVertex[1]; + cand.pvZ = primaryVertex[2]; + cand.cosPAOmega = RecoDecay::cpa(decayVertex, omegaDecayVertex, omegaMomentum); + cand.cosPADirectLambda = RecoDecay::cpa(decayVertex, directLambda.decayVertex, lambdaMomentum); + cand.cosPADoubleOmega = RecoDecay::cpa(primaryVertex, decayVertex, totalMomentum); + // PV pointing angles use the daughter momenta at their own decay vertices. + cand.cosPAOmegaToPV = RecoDecay::cpa(primaryVertex, omegaDecayVertex, std::array{omega.px, omega.py, omega.pz}); + cand.cosPADirectLambdaToPV = RecoDecay::cpa(primaryVertex, directLambda.decayVertex, std::array{directLambda.candidate.px, directLambda.candidate.py, directLambda.candidate.pz}); + cand.dcaXYOmegaToPV = dcaOmega[0]; + cand.dcaXYDirectLambdaToPV = dcaDirectLambda[0]; + cand.dcaXYDirectKaonToPV = dcaDirectKaon[0]; + cand.dcaXYOmegaKaonToPV = omega.dcaXYKaonToPV; + cand.dcaXYOmegaProtonToPV = omega.dcaXYProtonToPV; + cand.dcaXYOmegaPionToPV = omega.dcaXYPionToPV; + cand.dcaXYDirectProtonToPV = directLambda.dcaXYProtonToPV; + cand.dcaXYDirectPionToPV = directLambda.dcaXYPionToPV; + cand.tpcNSigmaDirectKaon = kaon.tpcNSigma; + cand.tofNSigmaDirectKaon = kaon.tofNSigma; + cand.daughterDetectorMap = omega.daughterDetectorMap | + (directLambda.daughterDetectorMap << 9) | + (kaon.detectorMap << 15); + cand.mass = std::sqrt(std::max(0.f, massSquared)); + if (cand.cosPAOmega < selections.minCosPAOmega || + cand.cosPADirectLambda < selections.minCosPADirectLambda || + cand.cosPADoubleOmega < selections.minCosPADoubleOmega || + std::hypot(decayVertex[0], decayVertex[1]) < selections.minDoubleOmegaDecayRadius || + std::abs(cand.dcaXYOmegaToPV) < selections.dcaOmegaToPV || + std::abs(cand.dcaXYDirectLambdaToPV) < selections.dcaDirectLambdaToPV || + std::abs(cand.dcaXYDirectKaonToPV) < selections.dcaKaonToPV || + cand.mass > selections.maxDoubleOmegaMass) { + return false; + } + cand.massOmega = omega.massOmega; + cand.massXi = omega.massXi; + cand.sign = omega.sign; + return true; + } + + // Builds the Omega, Lambda and kaon candidates of a collision. If requireOmega is set, Lambdas and + // kaons are built only if the collision has at least one Omega. + template + CollisionCandidates buildCollisionCandidates(C const& collision, T const& tracks, Cascades const& cascades, V0s const& v0s, bool requireOmega) + { + CollisionCandidates candidates; + candidates.primaryVertex = {collision.posX(), collision.posY(), collision.posZ()}; + for (const auto& rawCascade : cascades) { + const auto omegaV0 = rawCascade.template v0_as(); + BuiltOmegaCandidate omega; + if (buildOmega(collision, omegaV0.template posTrack_as(), omegaV0.template negTrack_as(), rawCascade.template bachelor_as(), omega)) { + candidates.omegas.push_back(omega); + } + } + if (requireOmega && candidates.omegas.empty()) { + return candidates; + } + + for (const int8_t charge : {int8_t{-1}, int8_t{1}}) { + for (const auto& rawV0 : v0s) { + BuiltLambdaCandidate lambda; + if (buildLambda(collision, rawV0.template posTrack_as(), rawV0.template negTrack_as(), charge, lambda)) { + candidates.lambdas[chargeIndex(charge)].push_back(lambda); + } + } + for (const auto& track : tracks) { + if (isSelectedKaon(track, charge)) { + candidates.kaons[chargeIndex(charge)].push_back(buildKaon(track)); + } + } + } + return candidates; + } + + // Pairs the Omegas, Lambdas and kaons of (possibly different) collisions. The double-Omega candidate + // is built with respect to the primary vertex of the reference collision. + template + void combine(CollisionCandidates const& reference, CollisionCandidates const& omegaSource, + CollisionCandidates const& lambdaSource, CollisionCandidates const& kaonSource, F&& onCandidate) + { + for (const auto& omega : omegaSource.omegas) { + for (const auto& lambda : lambdaSource.lambdas[chargeIndex(omega.sign)]) { + for (const auto& kaon : kaonSource.kaons[chargeIndex(omega.sign)]) { + // Tracks can also be shared by V0s and cascades associated to different collisions. + if (sharesTracks(omega, lambda, kaon)) { + continue; + } + DoubleOmegaCandidate cand; + if (!buildDoubleOmega(reference.primaryVertex, omega, lambda, kaon, cand)) { + continue; + } + onCandidate(cand, omega, lambda, kaon); + } + } + } + } + + // True if at least one Omega has a Lambda and a kaon of compatible charge that do not share its tracks. + static bool hasOmegaLambdaKaon(CollisionCandidates const& candidates) + { + for (const auto& omega : candidates.omegas) { + const auto& lambdas = candidates.lambdas[chargeIndex(omega.sign)]; + const auto& kaons = candidates.kaons[chargeIndex(omega.sign)]; + const bool hasLambda = std::any_of(lambdas.begin(), lambdas.end(), [&](BuiltLambdaCandidate const& lambda) { return !sharesTracks(omega, lambda); }); + const bool hasKaon = std::any_of(kaons.begin(), kaons.end(), [&](BuiltKaonCandidate const& kaon) { return !sharesTracks(omega, kaon); }); + if (hasLambda && hasKaon) { + return true; + } + } + return false; + } + + private: + template + static void configureFitter(Fitter& dcaFitter) + { + dcaFitter.setPropagateToPCA(true); + dcaFitter.setMaxR(200.); + dcaFitter.setMaxDZIni(4.); + dcaFitter.setMinParamChange(1.e-3); + dcaFitter.setMinRelChi2Change(0.9); + dcaFitter.setUseAbsDCA(true); + } +}; + +} // namespace o2::pwglf::doubleomega + +#endif // PWGLF_UTILS_DOUBLEOMEGABUILDERHELPER_H_ diff --git a/PWGLF/Utils/mcCentralityModule.h b/PWGLF/Utils/mcCentralityModule.h index 0a7871609aa..dd53bbb2ac8 100644 --- a/PWGLF/Utils/mcCentralityModule.h +++ b/PWGLF/Utils/mcCentralityModule.h @@ -40,6 +40,7 @@ #include +#include #include #include #include @@ -257,7 +258,7 @@ struct BuilderModule { nEnabledTables = 0; - TString listOfRequestors[nTablesConst]; + std::array listOfRequestors{}; for (int i = 0; i < nTablesConst; i++) { int f = baseOpts.enabledTables->get(tableNames[i].c_str(), "enable"); if (f == 1) { diff --git a/PWGLF/Utils/strangenessPCH.cxx b/PWGLF/Utils/strangenessPCH.cxx new file mode 100644 index 00000000000..e9bcd249723 --- /dev/null +++ b/PWGLF/Utils/strangenessPCH.cxx @@ -0,0 +1,13 @@ +// Copyright 2019-2026 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. + +// Carrier for the LfStrangenessPCH precompiled header; see this directory's +// CMakeLists.txt. It exists only to own the PCH, so it has no code of its own. diff --git a/PWGLF/Utils/v0SelectionGroupLinkDef.h b/PWGLF/Utils/v0SelectionGroupLinkDef.h index 3c81add2fc2..c40840a0625 100644 --- a/PWGLF/Utils/v0SelectionGroupLinkDef.h +++ b/PWGLF/Utils/v0SelectionGroupLinkDef.h @@ -9,8 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef PWGLF_UTILS_V0SELECTIONGROUPLINKDEF_H_ -#define PWGLF_UTILS_V0SELECTIONGROUPLINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -21,5 +20,3 @@ #pragma link C++ class std::vector < float> + ; #pragma link C++ class std::vector < double> + ; #pragma link C++ class std::vector < bool> + ; - -#endif // PWGLF_UTILS_V0SELECTIONGROUPLINKDEF_H_ diff --git a/PWGMM/Mult/TableProducer/percentiles.cxx b/PWGMM/Mult/TableProducer/percentiles.cxx index d34802ab2ae..bba44ad0243 100644 --- a/PWGMM/Mult/TableProducer/percentiles.cxx +++ b/PWGMM/Mult/TableProducer/percentiles.cxx @@ -32,6 +32,7 @@ #include #include +#include using namespace o2; using namespace o2::framework; diff --git a/PWGMM/Mult/Tasks/dndeta.cxx b/PWGMM/Mult/Tasks/dndeta.cxx index ae99e5f8205..6f69f60e42c 100644 --- a/PWGMM/Mult/Tasks/dndeta.cxx +++ b/PWGMM/Mult/Tasks/dndeta.cxx @@ -42,6 +42,7 @@ #include #include +#include #include using namespace o2; diff --git a/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx b/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx index 7b8ec127ee5..8eaef0ae1c8 100644 --- a/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx +++ b/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx @@ -26,6 +26,7 @@ #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/McCollisionExtra.h" #include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" #include #include @@ -43,12 +44,14 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -80,12 +83,7 @@ using namespace o2::aod::rctsel; auto static constexpr CminCharge = 3.f; auto static constexpr CintZero = 0; auto static constexpr CintOne = 1; -auto static constexpr CfloatFive = 5.f; auto static constexpr CInvalid = -999.f; -auto static constexpr CminAccFT0A = 3.5f; -auto static constexpr CmaxAccFT0A = 4.9f; -auto static constexpr CminAccFT0C = -3.3f; -auto static constexpr CmaxAccFT0C = -2.1f; enum class EvtSel { evtAll = 0, @@ -112,6 +110,7 @@ enum class TrkSel { trkSelNCls, trkSelChi2Ncl, trkSelEta, + trkSelPhi, trkSelPhiCut, trkSelPt, trkSelCA, @@ -139,7 +138,7 @@ enum class AmbTrkType { nAmbTrkType }; -enum class GenTrkType { +enum class McGenTrkType { kGenAll = 0, kGenRecEvt, nGenTrkType @@ -152,6 +151,11 @@ enum class GenIdxTrkType { nGenIdxTrkType }; +enum class GenIdxTrkLightType { + kGenIdxAll = 0, + nGenIdxTrkType +}; + enum class RecTrkType { kRecAll = 0, kRecFake, @@ -165,6 +169,12 @@ enum class RecIdxTrkType { nRecIdxTrkType }; +enum class RecIdxTrkLightType { + kRecIdxPrim = 0, + kRecIdxSec, + nRecIdxTrkType +}; + enum class EvtLossType { kGenAll = 0, kGenSel, @@ -173,21 +183,37 @@ enum class EvtLossType { nEvtLossType }; -enum class McEffStatus { - kMcEffAll = 0, - kMcEffSel, - kMcEffHasMcColl, - kMcEffNoSplitVtx, - nMcEfftStatus +enum class DataEvtStatus { + kDataAll = 0, + kDataSel, + kDataRej, + nDataEvtStatus }; -enum class McStatus { +enum class McEvtStatus { kMcRecAll = 0, kMcRecSel, kMcRecHasMcColl, kMcRecNoSplitVtx, kMcGenAll, - nMcStatus + nMcEvtStatus +}; + +enum class McEvtLossStatus { + kMcEvtAll = 0, + kMcEvtVtxZ, + kMcEvtTVX, + kMcEvtInelGt0, + kMcEvtInelGt0wMft, + kMcEvtStatus +}; + +enum class McEffStatus { + kMcEffAll = 0, + kMcEffSel, + kMcEffHasMcColl, + kMcEffNoSplitVtx, + nMcEfftStatus }; enum class McTrackStatus { @@ -196,6 +222,7 @@ enum class McTrackStatus { kTrkAsBestSel, kTrkHasColl, kTrkReassignedRemoved, + kTrkFake, nMcTrackStatus }; @@ -204,6 +231,35 @@ enum class OccupancyEst { Ft0C }; +enum class McRecTrkType { + kPrimary = 0, + kSecondary, + kStrangeDecay, + kTrkSel, + kTrkFake, + nMcRecTrkStatus +}; + +struct MultE { + static constexpr int CnoMult = 0; + static constexpr int CmultFT0C = 1; + static constexpr int CmultFT0M = 2; + static constexpr int CmultTPC = 3; + static constexpr int CmultMFT = 4; +}; + +static constexpr TrackSelectionFlags::flagtype TrackSelectionITS = + TrackSelectionFlags::kITSNCls | TrackSelectionFlags::kITSChi2NDF | + TrackSelectionFlags::kITSHits; +static constexpr TrackSelectionFlags::flagtype TrackSelectionTPC = + TrackSelectionFlags::kTPCNCls | + TrackSelectionFlags::kTPCCrossedRowsOverNCls | + TrackSelectionFlags::kTPCChi2NDF; +static constexpr TrackSelectionFlags::flagtype TrackSelectionDCA = + TrackSelectionFlags::kDCAz | TrackSelectionFlags::kDCAxy; +static constexpr TrackSelectionFlags::flagtype TrackSelectionDCAXYonly = + TrackSelectionFlags::kDCAxy; + struct DndetaMFTPbPb { SliceCache cache; @@ -215,19 +271,25 @@ struct DndetaMFTPbPb { HistogramRegistry registryQC{"registryQC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; struct : ConfigurableGroup { - Configurable cfgDoIR{"cfgDoIR", false, "Flag to retrieve Interaction rate from CCDB"}; - Configurable cfgUseIRCut{"cfgUseIRCut", false, "Flag to cut on IR rate"}; - Configurable cfgIRCrashOnNull{"cfgIRCrashOnNull", false, "Flag to avoid CTP RateFetcher crash"}; - Configurable cfgIRSource{"cfgIRSource", "ZNC hadronic", "Estimator of the interaction rate (Pb-Pb: ZNC hadronic)"}; - Configurable cfgUseTrackSel{"cfgUseTrackSel", false, "Flag to apply track selection"}; - Configurable cfgUseParticleSel{"cfgUseParticleSel", true, "Flag to apply particle selection"}; - Configurable cfgUsePrimaries{"cfgUsePrimaries", true, "Select primary particles"}; - Configurable cfgUseSecondaries{"cfgUseSecondaries", false, "Select secondary particles"}; - Configurable cfgRemoveReassigned{"cfgRemoveReassigned", false, "Remove reassgined tracks"}; - Configurable cfgRemoveSplitVertex{"cfgRemoveSplitVertex", true, "Remove split vertices"}; - Configurable cfgUseTrackParExtra{"cfgUseTrackParExtra", false, "Use table with refitted track parameters"}; - Configurable cfgUseInelgt0wMFT{"cfgUseInelgt0wMFT", false, "Use INEL > 0 condition with MFT acceptance"}; + Configurable dcaZmidrap{"dcaZmidrap", 0.2f, "Custom DCA Z cut (ignored if negative)"}; + Configurable useEvtSel{"useEvtSel", true, "Use event selection"}; + Configurable useIRFromCCDB{"useIRFromCCDB", false, "Flag to retrieve Interaction rate from CCDB"}; + Configurable useIRCut{"useIRCut", false, "Flag to cut on IR rate"}; + Configurable intRateCrashOnNull{"intRateCrashOnNull", false, "Flag to avoid CTP RateFetcher crash"}; + Configurable intRateSource{"intRateSource", "ZNC hadronic", "Estimator of the interaction rate (Pb-Pb: ZNC hadronic)"}; + Configurable useTrackSel{"useTrackSel", false, "Flag to apply track selection"}; + Configurable useParticleSel{"useParticleSel", true, "Flag to apply particle selection"}; + Configurable usePrimaries{"usePrimaries", true, "Select primary particles"}; + Configurable useSecondaries{"useSecondaries", false, "Select secondary particles"}; + Configurable rmReassigned{"rmReassigned", false, "Remove reassgined tracks"}; + Configurable rmSplitVertex{"rmSplitVertex", true, "Remove split vertices"}; + Configurable useInelgt0{"useInelgt0", false, "Use INEL > 0 condition"}; + Configurable useInelgt0wMFT{"useInelgt0wMFT", false, "Use INEL > 0 condition with MFT acceptance"}; Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + Configurable fillTrackQaHist{"fillTrackQaHist", false, "fill track QA histograms"}; + Configurable multEstimator{"multEstimator", 1, "0: without multiplicity; 1: MultFT0C; 2: MultFT0M; 3: MultTPC, 4: MultMFT"}; + Configurable useMultMCatMidRapidity{"useMultMCatMidRapidity", true, "use generated Nch in ∣eta∣ < 0.8"}; + Configurable useMultMCatMFTacc{"useMultMCatMFTacc", false, "use generated Nch in MFT acceptance"}; } gConf; struct : ConfigurableGroup { @@ -241,14 +303,15 @@ struct DndetaMFTPbPb { ConfigurableAxis fv0aMultBins{"fv0aMultBins", {501, -0.5, 500.5}, ""}; ConfigurableAxis ft0aMultBins{"ft0aMultBins", {501, -0.5, 500.5}, ""}; ConfigurableAxis ft0cMultBins{"ft0cMultBins", {501, -0.5, 500.5}, ""}; - ConfigurableAxis ptBins{"ptBins", {101, -0.5, 10.5}, "pT binning (GeV/c)"}; + ConfigurableAxis ptBins{"ptBins", {11, -0.5, 10.5}, "pT binning (GeV/c)"}; ConfigurableAxis multBins{"multBins", {1001, -0.5, 1000.5}, "Multiplicity binning"}; ConfigurableAxis zvtxBins{"zvtxBins", {60, -30., 30.}, "Z-vtx binning (cm)"}; - ConfigurableAxis deltaZBins{"deltaZBins", {800, -10., 10.}, "Delta Z-vtx binning (cm)"}; - ConfigurableAxis dcaXYBins{"dcaXYBins", {800, -2., 2.}, "DCAxy binning (cm)"}; - ConfigurableAxis dcaZBins{"dcaZBins", {800, -2., 2.}, "DCAz binning (cm)"}; + ConfigurableAxis deltaZBins{"deltaZBins", {200, -1., 1.}, "Delta Z-vtx binning (cm)"}; + ConfigurableAxis dcaXYBins{"dcaXYBins", {100, -0.5, 0.5}, "DCAxy binning (cm)"}; + ConfigurableAxis dcaZBins{"dcaZBins", {100, -0.5, 0.5}, "DCAz binning (cm)"}; ConfigurableAxis phiBins{"phiBins", {629, 0., TwoPI}, "#varphi binning (rad)"}; ConfigurableAxis etaBins{"etaBins", {20, -4., -2.}, "#eta binning"}; + ConfigurableAxis etaMidrapBins{"etaMidrapBins", {40, -2., 2.}, "#eta midrapidity binning"}; ConfigurableAxis chiSqPerNclBins{"chiSqPerNclBins", {100, 0, 100}, "#chi^{2} binning"}; ConfigurableAxis nClBins{"nClBins", {10, 0.5, 10.5}, "number of clusters binning"}; ConfigurableAxis tanLambdaBins{"tanLambdaBins", {100, -25, 0}, "binning for tan(lambda)"}; @@ -264,6 +327,7 @@ struct DndetaMFTPbPb { } rctCuts; struct : ConfigurableGroup { + Configurable etaRangeMidrap{"etaRangeMidrap", 1.0f, "Eta range for midrapidity tracks"}; Configurable usephiCut{"usephiCut", false, "use azimuthal angle cut"}; Configurable phiCut{"phiCut", 0.1f, "Cut on azimuthal angle of MFT tracks"}; Configurable minPhi{"minPhi", 0.f, ""}; @@ -281,6 +345,7 @@ struct DndetaMFTPbPb { Configurable maxDCAxy{"maxDCAxy", 2.0f, "Cut on dca XY"}; Configurable useDCAzCut{"useDCAzCut", true, "use dca Z cut"}; Configurable maxDCAz{"maxDCAz", 2.0f, "Cut on dca Z"}; + Configurable removeFakeByMcMask{"removeFakeByMcMask", 1, "Remove fakes based on MC mask"}; Configurable selMcMask{"selMcMask", 0, "McMask for correct match"}; } trackCuts; @@ -291,7 +356,7 @@ struct DndetaMFTPbPb { Configurable minZvtx{"minZvtx", -10.0f, "minimum cut on z-vtx (cm)"}; Configurable useZDiffCut{"useZDiffCut", false, "use Zvtx reco-mc diff. cut"}; Configurable maxZvtxDiff{"maxZvtxDiff", 1.0f, "max allowed Z vtx difference for reconstruced collisions (cm)"}; - Configurable useZVtxCutMC{"useZVtxCutMC", false, "use Zvtx cut in MC"}; + Configurable useZVtxCutMC{"useZVtxCutMC", true, "use Zvtx cut in MC"}; Configurable requireIsGoodZvtxFT0VsPV{"requireIsGoodZvtxFT0VsPV", true, "require events with PV position along z consistent (within 1 cm) between PV reconstructed using tracks and PV using FT0 A-C time difference"}; Configurable requireRejectSameBunchPileup{"requireRejectSameBunchPileup", true, "reject collisions in case of pileup with another collision in the same foundBC"}; Configurable requireNoCollInTimeRangeStrict{"requireNoCollInTimeRangeStrict", false, " requireNoCollInTimeRangeStrict"}; @@ -306,8 +371,9 @@ struct DndetaMFTPbPb { Configurable maxOccupancy{"maxOccupancy", -1, "maximum occupancy from neighbouring collisions"}; Configurable minIR{"minIR", -1, "minimum IR (kHz) collisions"}; Configurable maxIR{"maxIR", -1, "maximum IR (kHz) collisions"}; - Configurable useInelgt0wTVX{"useInelgt0wTVX", false, "Use INEL > 0 condition with TVX trigger, i.e. FT0A and FT0C acceptance"}; + Configurable requireTVX{"requireTVX", false, "Use TVX trigger, i.e. FT0A and FT0C acceptance"}; Configurable useGenMult{"useGenMult", false, "use MultMC for centrality"}; + Configurable checkCollisionId{"checkCollisionId", false, "check collision ids"}; } eventCuts; Service pdg{}; @@ -328,9 +394,6 @@ struct DndetaMFTPbPb { std::array centerMFT{0, 0, -61.4}; // Field at center of MFT o2::parameters::GRPMagField* grpmag = nullptr; - std::vector ambiguousTrkIds; - std::vector reassignedTrkIds; - /// @brief init function, definition of histograms void init(InitContext&) { @@ -349,14 +412,17 @@ struct DndetaMFTPbPb { const AxisSpec dcazAxis = {binOpt.dcaZBins, "DCA-z axis"}; const AxisSpec phiAxis = {binOpt.phiBins, "#phi axis"}; const AxisSpec etaAxis = {binOpt.etaBins, "#eta axis"}; + const AxisSpec etaMidrapAxis = {binOpt.etaMidrapBins, "#eta midrapidity axis"}; const AxisSpec chiSqAxis = {binOpt.chiSqPerNclBins, "Chi2 axis"}; const AxisSpec nclsAxis = {binOpt.nClBins, "Number of clusters axis"}; const AxisSpec tanLambdaAxis{binOpt.tanLambdaBins, "tan(#lambda)"}; const AxisSpec invQPtAxis{binOpt.invQPtBins, "q/p_{T} (1/GeV)"}; - const AxisSpec genEvtTypeAxis = {static_cast(GenTrkType::nGenTrkType), -0.5, +static_cast(GenTrkType::nGenTrkType) - 0.5, "", "Gen-Trk Type axis"}; - const AxisSpec recEvtTypeAxis = {static_cast(RecTrkType::nRecTrkType), -0.5, +static_cast(RecTrkType::nRecTrkType) - 0.5, "", "Rec-Trk Type axis"}; + const AxisSpec recTrkTypeAxis = {static_cast(McRecTrkType::nMcRecTrkStatus), -0.5, +static_cast(McRecTrkType::nMcRecTrkStatus) - 0.5, "", "MC Rec-Trk Type axis"}; + const AxisSpec genTrkTypeAxis = {static_cast(McGenTrkType::nGenTrkType), -0.5, +static_cast(McGenTrkType::nGenTrkType) - 0.5, "", "MC Gen-Trk Type axis"}; const AxisSpec genEvtIdxTypeAxis = {static_cast(GenIdxTrkType::nGenIdxTrkType), -0.5, +static_cast(GenIdxTrkType::nGenIdxTrkType) - 0.5, "", "Gen-idx-Trk Type axis"}; const AxisSpec recEvtIdxTypeAxis = {static_cast(RecIdxTrkType::nRecIdxTrkType), -0.5, +static_cast(RecIdxTrkType::nRecIdxTrkType) - 0.5, "", "Rec-idx-Trk Type axis"}; + const AxisSpec genEvtIdxLightTypeAxis = {static_cast(GenIdxTrkLightType::nGenIdxTrkType), -0.5, +static_cast(GenIdxTrkLightType::nGenIdxTrkType) - 0.5, "", "Gen-idx-Trk Light Type axis"}; + const AxisSpec recEvtIdxLightTypeAxis = {static_cast(RecIdxTrkLightType::nRecIdxTrkType), -0.5, +static_cast(RecIdxTrkLightType::nRecIdxTrkType) - 0.5, "", "Rec-idx-Trk Light Type axis"}; const AxisSpec evtLossTypeAxis = {static_cast(EvtLossType::nEvtLossType), -0.5, +static_cast(EvtLossType::nEvtLossType) - 0.5, "", "Evt-loss Type axis"}; rctChecker.init(rctCuts.cfgEvtRCTFlagCheckerLabel, rctCuts.cfgEvtRCTFlagCheckerZDCCheck, rctCuts.cfgEvtRCTFlagCheckerLimitAcceptAsBad); @@ -388,15 +454,15 @@ struct DndetaMFTPbPb { if (static_cast(doprocessMcEfficiencyIdxInlusive) + static_cast(doprocessMcEfficiencyIdxBestInlusive) > 1) { LOGP(fatal, "Either doprocessMcEfficiencyIdxInlusive OR doprocessMcEfficiencyIdxBestInlusive should be enabled!"); } + if (static_cast(doprocessMcEfficiencyIdxBestInlusive) + static_cast(doprocessMcEfficiencyIdxBestLightInlusive) > 1) { + LOGP(fatal, "Either doprocessMcEfficiencyIdxBestInlusive OR doprocessMcEfficiencyIdxBestLightInlusive should be enabled!"); + } if (static_cast(doprocessMcEfficiencyIdxCentFT0C) + static_cast(doprocessMcEfficiencyIdxBestCentFT0C) > 1) { LOGP(fatal, "Either doprocessMcEfficiencyIdxCentFT0C OR doprocessMcEfficiencyIdxBestCentFT0C should be enabled!"); } - - // General counters - QC - auto hBcSel = registryQC.add("hBcSel", "hBcSel", HistType::kTH1F, {{3, -0.5f, +2.5f}}); - hBcSel->GetXaxis()->SetBinLabel(1, "Good BCs"); - hBcSel->GetXaxis()->SetBinLabel(2, "BCs with collisions"); - hBcSel->GetXaxis()->SetBinLabel(3, "BCs with pile-up/splitting"); + if (static_cast(doprocessMcEfficiencyIdxBestCentFT0C) + static_cast(doprocessMcEfficiencyIdxBestLightCentFT0C) > 1) { + LOGP(fatal, "Either doprocessMcEfficiencyIdxBestCentFT0C OR doprocessMcEfficiencyIdxBestLightCentFT0C should be enabled!"); + } registryQC.add("Events/hEvtSel", "Number of events; Cut; #Evt Passed Cut", {HistType::kTH1F, {{static_cast(EvtSel::nEvtSel), -0.5, +static_cast(EvtSel::nEvtSel) - 0.5}}}); std::array(EvtSel::nEvtSel)> labelEvtSel{ @@ -421,25 +487,44 @@ struct DndetaMFTPbPb { registryQC.get(HIST("Events/hEvtSel"))->GetXaxis()->SetBinLabel(iBin + 1, labelEvtSel[iBin].data()); } - registryQC.add("Tracks/hTrkSel", "Number of tracks; Cut; #Tracks Passed Cut", {HistType::kTH1F, {{static_cast(TrkSel::nTrkSel), -0.5, +static_cast(TrkSel::nTrkSel) - 0.5}}}); - std::array(TrkSel::nTrkSel)> labelTrkSel{ - "All", - "Ncls", - "Chi2", - "Eta", - "Phi cut", - "Pt", - "CA"}; - registryQC.get(HIST("Tracks/hTrkSel"))->SetMinimum(0.1); - for (int iBin = 0; iBin < static_cast(TrkSel::nTrkSel); iBin++) { - registryQC.get(HIST("Tracks/hTrkSel"))->GetXaxis()->SetBinLabel(iBin + 1, labelTrkSel[iBin].data()); + if (doprocessTagging) { + auto hBcSel = registryQC.add("hBcSel", "hBcSel", HistType::kTH1F, {{3, -0.5f, +2.5f}}); + hBcSel->GetXaxis()->SetBinLabel(1, "Good BCs"); + hBcSel->GetXaxis()->SetBinLabel(2, "BCs with collisions"); + hBcSel->GetXaxis()->SetBinLabel(3, "BCs with pile-up/splitting"); + } + + if (doprocessDataInclusive || doprocessDatawBestTracksInclusive || + doprocessDataCentFT0C || doprocessDatawBestTracksCentFT0C || + doprocessMcInclusive || doprocessMcBestInclusive || + doprocessMcCentFT0C || doprocessMcBestCentFT0C || + doprocessMcEfficiencyInclusive || doprocessMcEfficiencyBestInclusive || + doprocessMcEfficiencyCentFT0C || doprocessMcEfficiencyBestCentFT0C || + doprocessMcEfficiencyIdxInlusive || doprocessMcEfficiencyIdxCentFT0C || + doprocessMcEfficiencyIdxBestLightInlusive || doprocessMcEfficiencyIdxBestLightCentFT0C || + doprocessDataCorrelationwBestTracksInclusive || doprocessMcReassocDCA) { + registryQC.add("Tracks/hTrkSel", "Number of tracks; Cut; #Tracks Passed Cut", {HistType::kTH1F, {{static_cast(TrkSel::nTrkSel), -0.5, +static_cast(TrkSel::nTrkSel) - 0.5}}}); + std::array(TrkSel::nTrkSel)> labelTrkSel{ + "All", + "Ncls", + "Chi2", + "Eta", + "Phi", + "Phi cut", + "Pt", + "CA"}; + registryQC.get(HIST("Tracks/hTrkSel"))->SetMinimum(0.1); + for (int iBin = 0; iBin < static_cast(TrkSel::nTrkSel); iBin++) { + registryQC.get(HIST("Tracks/hTrkSel"))->GetXaxis()->SetBinLabel(iBin + 1, labelTrkSel[iBin].data()); + } } if (doprocessDatawBestTracksInclusive || doprocessDatawBestTracksCentFT0C || doprocessMcBestInclusive || doprocessMcBestCentFT0C || doprocessMcEfficiencyBestInclusive || doprocessMcEfficiencyBestCentFT0C || doprocessMcEfficiencyIdxBestInlusive || doprocessMcEfficiencyIdxBestCentFT0C || - doprocessDataCorrelationwBestTracksInclusive) { + doprocessMcEfficiencyIdxBestLightInlusive || doprocessMcEfficiencyIdxBestLightCentFT0C || + doprocessDataCorrelationwBestTracksInclusive || doprocessMcReassocDCA) { registryQC.add("Tracks/hBestTrkSel", "Number of best tracks; Cut; #Tracks Passed Cut", {HistType::kTH1F, {{static_cast(TrkBestSel::nTrkBestSel), -0.5, +static_cast(TrkBestSel::nTrkBestSel) - 0.5}}}); std::array(TrkBestSel::nTrkBestSel)> labelTrkTrkBestSel{ "All", @@ -454,66 +539,63 @@ struct DndetaMFTPbPb { } } if (doprocessDataInclusive || doprocessDatawBestTracksInclusive) { - registryData.add({"Events/hInteractionRate", "; IR (kHz); occupancy", {HistType::kTH2F, {irAxis, occupancyAxis}}}); - registryData.add({"Events/Selection", ";status; occupancy", {HistType::kTH2F, {{2, 0.5, 2.5}, occupancyAxis}}}); - auto hstat = registryData.get(HIST("Events/Selection")); - auto* x = hstat->GetXaxis(); - x->SetBinLabel(1, "All"); - x->SetBinLabel(2, "Selected"); - - registryData.add({"Tracks/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); + if (gConf.useIRFromCCDB) { + registryData.add({"Events/hInteractionRate", "; IR (kHz); occupancy", {HistType::kTH2F, {irAxis, occupancyAxis}}}); + } + registryData.add("Events/DataEvtStatus", "Number of events; Cut; occupancy", {HistType::kTH2F, {{static_cast(DataEvtStatus::nDataEvtStatus), -0.5, +static_cast(DataEvtStatus::nDataEvtStatus) - 0.5}, occupancyAxis}}); + std::array(DataEvtStatus::nDataEvtStatus)> labelDataEvtStatus{ + "Data all", + "Data sel", + "Data rejected"}; + for (int iBin = 0; iBin < static_cast(DataEvtStatus::nDataEvtStatus); iBin++) { + registryData.get(HIST("Events/DataEvtStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labelDataEvtStatus[iBin].data()); + } + registryData.add({"Tracks/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, dcaxyAxis, dcazAxis, occupancyAxis}}}); registryData.add({"Tracks/PhiEta", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); - registryData.add({"Events/NtrkZvtx", "; N_{trk}; Z_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); - registryData.add({"Tracks/Chi2Eta", "; #chi^{2}; #eta; occupancy", {HistType::kTHnSparseF, {chiSqAxis, etaAxis, occupancyAxis}}}); - registryData.add({"Tracks/NclustersEta", "; nClusters; #eta; occupancy", {HistType::kTHnSparseF, {nclsAxis, etaAxis, occupancyAxis}}}); - registryData.add({"Tracks/TanLambda", "; TanLambda; occupancy", {HistType::kTH2F, {tanLambdaAxis, occupancyAxis}}}); - registryData.add({"Tracks/InvQPt", "; InvQPt; occupancy", {HistType::kTH2F, {invQPtAxis, occupancyAxis}}}); - + if (gConf.fillTrackQaHist) { + registryData.add({"Events/NtrkZvtx", "; N_{trk}; Z_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); + registryData.add({"Tracks/Chi2Eta", "; #chi^{2}; #eta; occupancy", {HistType::kTH2F, {chiSqAxis, occupancyAxis}}}); + registryData.add({"Tracks/NclustersEta", "; nClusters; #eta; occupancy", {HistType::kTH2F, {nclsAxis, occupancyAxis}}}); + } if (doprocessDatawBestTracksInclusive) { - registryData.add({"Tracks/DCA3d", "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, occupancyAxis}}}); - registryData.add({"Tracks/ReTracksEtaZvtx", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); - registryData.add({"Tracks/ReTracksPhiEta", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); - registryData.add({"Tracks/OrigTracksEtaZvtx", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); - registryData.add({"Tracks/OrigTracksPhiEta", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); - registryData.add({"Tracks/RestTracksEtaZvtx", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); - registryData.add({"Tracks/RestTracksPhiEta", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); - registryData.add({"Tracks/TrackAmbDegree", "; N_{coll}^{comp}; occupancy", {HistType::kTH2F, {{51, -0.5, 50.5}, occupancyAxis}}}); - registryData.add({"Tracks/TanLambdaExtra", "; TanLambda; occupancy", {HistType::kTH2F, {tanLambdaAxis, occupancyAxis}}}); - registryData.add({"Tracks/InvQPtExtra", "; InvQPt; occupancy", {HistType::kTH2F, {invQPtAxis, occupancyAxis}}}); - registryData.add({"Tracks/EtaExtra", "; #eta; occupancy", {HistType::kTH2F, {etaAxis, occupancyAxis}}}); - registryData.add({"Tracks/PhiExtra", "; #varphi; occupancy", {HistType::kTH2F, {phiAxis, occupancyAxis}}}); + if (gConf.fillTrackQaHist) { + registryData.add({"Tracks/ReTracksEtaZvtx", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); + registryData.add({"Tracks/ReTracksPhiEta", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); + registryData.add({"Tracks/TrackAmbDegree", "; N_{coll}^{comp}; occupancy", {HistType::kTH2F, {{51, -0.5, 50.5}, occupancyAxis}}}); + } } + LOG(info) << "doprocessData[BestTrack]Inclusive -> Size of the Data histograms:"; + registryData.print(); } if (doprocessDataCentFT0C || doprocessDatawBestTracksCentFT0C) { - registryData.add({"Events/Centrality/hInteractionRate", "; IR (kHz); centrality; occupancy", {HistType::kTHnSparseF, {irAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Events/Centrality/Selection", ";status; centrality; occupancy", {HistType::kTHnSparseF, {{2, 0.5, 2.5}, centralityAxis, occupancyAxis}}}); - auto hstat = registryData.get(HIST("Events/Centrality/Selection")); - hstat->GetAxis(0)->SetBinLabel(1, "All"); - hstat->GetAxis(0)->SetBinLabel(2, "Selected"); - - registryData.add({"Tracks/Centrality/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/PhiEta", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Events/Centrality/NtrkZvtx", "; N_{trk}; Z_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/Chi2Eta", "; #chi^{2}; #eta; centrality; occupancy", {HistType::kTHnSparseF, {chiSqAxis, etaAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/NclustersEta", "; nClusters; #eta; centrality; occupancy", {HistType::kTHnSparseF, {nclsAxis, etaAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/TanLambda", "; TanLambda; centrality; occupancy", {HistType::kTHnSparseF, {tanLambdaAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/InvQPt", "; InvQPt; centrality; occupancy", {HistType::kTHnSparseF, {invQPtAxis, centralityAxis, occupancyAxis}}}); + if (gConf.useIRFromCCDB) { + registryData.add({"Events/Centrality/hInteractionRate", "; IR (kHz); centrality; occupancy", {HistType::kTHnSparseF, {irAxis, centralityAxis, occupancyAxis}}}); + } + registryData.add("Events/Centrality/DataEvtStatus", "Number of events; Cut; centrality; occupancy", {HistType::kTHnSparseF, {{static_cast(DataEvtStatus::nDataEvtStatus), -0.5, +static_cast(DataEvtStatus::nDataEvtStatus) - 0.5}, centralityAxis, occupancyAxis}}); + std::array(DataEvtStatus::nDataEvtStatus)> labelDataEvtStatusCent{ + "Data all", + "Data sel", + "Data rejected"}; + for (int iBin = 0; iBin < static_cast(DataEvtStatus::nDataEvtStatus); iBin++) { + registryData.get(HIST("Events/Centrality/DataEvtStatus"))->GetAxis(0)->SetBinLabel(iBin + 1, labelDataEvtStatusCent[iBin].data()); + } registryData.add({"Events/Centrality/hZvtxCent", "; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); - + registryData.add({"Tracks/Centrality/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/PhiEta", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); + if (gConf.fillTrackQaHist) { + registryData.add({"Events/Centrality/NtrkZvtx", "; N_{trk}; Z_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/Chi2Eta", "; #chi^{2}; centrality; occupancy", {HistType::kTHnSparseF, {chiSqAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/NclustersEta", "; nClusters; #eta; centrality; occupancy", {HistType::kTHnSparseF, {nclsAxis, centralityAxis, occupancyAxis}}}); + } if (doprocessDatawBestTracksCentFT0C) { - registryData.add({"Tracks/Centrality/DCA3d", "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/ReTracksEtaZvtx", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/ReTracksPhiEta", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/OrigTracksEtaZvtx", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/OrigTracksPhiEta", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/RestTracksEtaZvtx", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/RestTracksPhiEta", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/TrackAmbDegree", "; N_{coll}^{comp}; centrality; occupancy", {HistType::kTHnSparseF, {{51, -0.5, 50.5}, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/TanLambdaExtra", "; TanLambda; centrality; occupancy", {HistType::kTHnSparseF, {tanLambdaAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/InvQPtExtra", "; InvQPt; centrality; occupancy", {HistType::kTHnSparseF, {invQPtAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/EtaExtra", "; #eta; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/PhiExtra", "; #varphi; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, centralityAxis, occupancyAxis}}}); + if (gConf.fillTrackQaHist) { + registryData.add({"Tracks/Centrality/ReTracksEtaZvtx", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/ReTracksPhiEta", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/TrackAmbDegree", "; N_{coll}^{comp}; centrality; occupancy", {HistType::kTHnSparseF, {{51, -0.5, 50.5}, centralityAxis, occupancyAxis}}}); + } } + LOG(info) << "doprocessData[BestTrack]CentFT0C -> Size of the Data histograms:"; + registryData.print(); } if (doprocessDataCorrelationwBestTracksInclusive) { registryData.add("Events/hMultMFTvsFT0A", "MultMFT_vs_FT0A", {HistType::kTH2F, {multAxis, multFT0aAxis}}); @@ -521,17 +603,19 @@ struct DndetaMFTPbPb { registryData.add("Events/hNPVtracksVsFT0C", "NPVtracks_vs_FT0C", {HistType::kTH2F, {pvAxis, multFT0cAxis}}); registryData.add("Events/hMultMFTvsFV0A", "MultMFT_vs_FV0A", {HistType::kTH2F, {multAxis, multFV0aAxis}}); registryData.add("Events/hNPVtracksVsMultMFT", "NPVtracks_vs_MultMFT", {HistType::kTH2F, {pvAxis, multAxis}}); + LOG(info) << "doprocessDataCorrelationwBestTracksInclusive -> Size of the Data histograms:"; + registryData.print(); } if (doprocessMcInclusive || doprocessMcBestInclusive) { - registryMC.add("Events/McStatus", "Number of events; Cut; occupancy", {HistType::kTH2F, {{static_cast(McStatus::nMcStatus), -0.5, +static_cast(McStatus::nMcStatus) - 0.5}, occupancyAxis}}); - std::array(McStatus::nMcStatus)> labelMcStatus{ + registryMC.add("Events/McEvtStatus", "Number of events; Cut; occupancy", {HistType::kTH2F, {{static_cast(McEvtStatus::nMcEvtStatus), -0.5, +static_cast(McEvtStatus::nMcEvtStatus) - 0.5}, occupancyAxis}}); + std::array(McEvtStatus::nMcEvtStatus)> labelMcEvtStatus{ "Rec all", "Rec sel", "Rec w/ mc coll", "Rec w/o split vtx", "Gen all"}; - for (int iBin = 0; iBin < static_cast(McStatus::nMcStatus); iBin++) { - registryMC.get(HIST("Events/McStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labelMcStatus[iBin].data()); + for (int iBin = 0; iBin < static_cast(McEvtStatus::nMcEvtStatus); iBin++) { + registryMC.get(HIST("Events/McEvtStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labelMcEvtStatus[iBin].data()); } if (doprocessMcBestInclusive) { registryMC.add({"Tracks/hMcTrackStatus", "Number of tracks; Cut; occupancy", {HistType::kTH2F, {{static_cast(McTrackStatus::nMcTrackStatus), -0.5, +static_cast(McTrackStatus::nMcTrackStatus) - 0.5}, occupancyAxis}}}); @@ -540,60 +624,98 @@ struct DndetaMFTPbPb { "Best sel", "Trk sel", "Has coll", - "Reas rm"}; + "Reas rm", + "Fake"}; for (int iBin = 0; iBin < static_cast(McTrackStatus::nMcTrackStatus); iBin++) { registryMC.get(HIST("Tracks/hMcTrackStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labelMcTrackStatus[iBin].data()); } - registryMC.add({"Tracks/DCA3d", "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, occupancyAxis}}}); - registryMC.add({"Tracks/TrackAmbDegree", "; N_{coll}^{comp}; occupancy", {HistType::kTH2F, {{51, -0.5, 50.5}, occupancyAxis}}}); + if (gConf.fillTrackQaHist) { + registryMC.add({"Tracks/TrackAmbDegree", "; N_{coll}^{comp}; occupancy", {HistType::kTH2F, {{51, -0.5, 50.5}, occupancyAxis}}}); + } } - registryMC.add({"Tracks/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); + if (gConf.fillTrackQaHist) { + registryMC.add({"Tracks/NclustersEta", "; nClusters; #eta; occupancy", {HistType::kTHnSparseF, {nclsAxis, etaAxis, occupancyAxis}}}); + registryMC.add({"Events/NtrkZvtxGen_t", "; N_{trk}; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); + registryMC.add({"Events/NtrkZvtxGenRecEvt", "; N_{trk}; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); + registryMC.add({"Events/NtrkZvtxRec", "; N_{trk}; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); + registryMC.add({"Events/NotFoundEventZvtx", "; #it{z}_{vtx} (cm); occupancy", {HistType::kTH2F, {zAxis, occupancyAxis}}}); + registryMC.add({"Events/ZvtxDiff", "; Z_{rec} - Z_{gen} (cm); occupancy", {HistType::kTH2F, {deltaZAxis, occupancyAxis}}}); + registryMC.add({"Events/NgenZvtxSplit", "; N_{gen}; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); + registryMC.add({"Events/NgenZvtxNoSplit", "; N_{gen}; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); + } + if (doprocessMcBestInclusive) { + registryMC.add({"Tracks/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm); recTrkTypeAxis; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, dcaxyAxis, dcazAxis, recTrkTypeAxis, occupancyAxis}}}); + } else { + registryMC.add({"Tracks/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); recTrkTypeAxis; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, recTrkTypeAxis, occupancyAxis}}}); + } + if (doprocessMcInclusive) { + registryMC.add({"TracksMidrap/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm); recTrkTypeAxis; occupancy", {HistType::kTHnSparseF, {etaMidrapAxis, zAxis, dcaxyAxis, dcazAxis, recTrkTypeAxis, occupancyAxis}}}); + } + registryMC.add({"Tracks/EtaZvtxGen_t", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); registryMC.add({"Tracks/EtaZvtxGen", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); + registryMC.add({"Tracks/EtaZvtxGenRecEvt", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); registryMC.add({"Tracks/PhiEta", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); + registryMC.add({"Tracks/PhiEtaGen_t", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); registryMC.add({"Tracks/PhiEtaGen", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); - registryMC.add({"Events/NtrkZvtxGen_t", "; N_{trk}; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); - registryMC.add({"Events/NtrkZvtxGen", "; N_{trk}; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); - registryMC.add({"Tracks/NclustersEta", "; nClusters; #eta; occupancy", {HistType::kTHnSparseF, {nclsAxis, etaAxis, occupancyAxis}}}); - registryMC.add({"Events/NotFoundEventZvtx", "; #it{z}_{vtx} (cm); occupancy", {HistType::kTH2F, {zAxis, occupancyAxis}}}); - registryMC.add({"Events/ZvtxDiff", "; Z_{rec} - Z_{gen} (cm); occupancy", {HistType::kTH2F, {deltaZAxis, occupancyAxis}}}); + registryMC.add({"Tracks/PhiEtaGenRecEvt", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); + LOG(info) << "doprocessMc[Best]Inclusive -> Size of the MC histograms:"; + registryMC.print(); } if (doprocessMcCentFT0C || doprocessMcBestCentFT0C) { - registryMC.add("Events/Centrality/McStatus", "Number of events; Cut; centrality; occupancy", {HistType::kTHnSparseF, {{static_cast(McStatus::nMcStatus), -0.5, +static_cast(McStatus::nMcStatus) - 0.5}, centralityAxis, occupancyAxis}}); - std::array(McStatus::nMcStatus)> labelMcStatusCent{ + registryMC.add("Events/Centrality/McEvtStatus", "Number of events; Cut; centrality; occupancy", {HistType::kTHnSparseF, {{static_cast(McEvtStatus::nMcEvtStatus), -0.5, +static_cast(McEvtStatus::nMcEvtStatus) - 0.5}, centralityAxis, occupancyAxis}}); + std::array(McEvtStatus::nMcEvtStatus)> labelMcEvtStatusCent{ "Rec all", "Rec sel", "Rec w/ mc coll", "Rec w/o split vtx", "Gen all"}; - for (int iBin = 0; iBin < static_cast(McStatus::nMcStatus); iBin++) { - registryMC.get(HIST("Events/Centrality/McStatus"))->GetAxis(0)->SetBinLabel(iBin + 1, labelMcStatusCent[iBin].data()); + for (int iBin = 0; iBin < static_cast(McEvtStatus::nMcEvtStatus); iBin++) { + registryMC.get(HIST("Events/Centrality/McEvtStatus"))->GetAxis(0)->SetBinLabel(iBin + 1, labelMcEvtStatusCent[iBin].data()); } if (doprocessMcBestCentFT0C) { registryMC.add({"Tracks/Centrality/hMcTrackStatus", "Number of tracks; Cut; centrality; occupancy", {HistType::kTHnSparseF, {{static_cast(McTrackStatus::nMcTrackStatus), -0.5, +static_cast(McTrackStatus::nMcTrackStatus) - 0.5}, centralityAxis, occupancyAxis}}}); - std::array(McTrackStatus::nMcTrackStatus)> labelMcStatusCentBest{ + std::array(McTrackStatus::nMcTrackStatus)> labelMcEvtStatusCentBest{ "Best all", "Best sel", "Trk sel", "Has coll", - "Reas rm"}; + "Reas rm", + "Fake"}; for (int iBin = 0; iBin < static_cast(McTrackStatus::nMcTrackStatus); iBin++) { - registryMC.get(HIST("Tracks/Centrality/hMcTrackStatus"))->GetAxis(0)->SetBinLabel(iBin + 1, labelMcStatusCentBest[iBin].data()); + registryMC.get(HIST("Tracks/Centrality/hMcTrackStatus"))->GetAxis(0)->SetBinLabel(iBin + 1, labelMcEvtStatusCentBest[iBin].data()); + } + if (gConf.fillTrackQaHist) { + registryMC.add({"Tracks/Centrality/TrackAmbDegree", "; N_{coll}^{comp}; centrality; occupancy", {HistType::kTHnSparseF, {{51, -0.5, 50.5}, centralityAxis, occupancyAxis}}}); } - registryMC.add({"Tracks/Centrality/DCA3d", "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Tracks/Centrality/TrackAmbDegree", "; N_{coll}^{comp}; centrality; occupancy", {HistType::kTHnSparseF, {{51, -0.5, 50.5}, centralityAxis, occupancyAxis}}}); } - registryMC.add({"Tracks/Centrality/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Tracks/Centrality/EtaZvtxGen", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); + if (gConf.fillTrackQaHist) { + registryMC.add({"Tracks/Centrality/NclustersEta", "; nClusters; #eta; centrality; occupancy", {HistType::kTHnSparseF, {nclsAxis, etaAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/NtrkZvtxGen_t", "; N_{trk}; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/NtrkZvtxGenRecEvt", "; N_{trk}; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/NtrkZvtxRec", "; N_{trk}; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/NotFoundEventZvtx", "; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/ZvtxDiff", "; Z_{rec} - Z_{gen} (cm); centrality; occupancy", {HistType::kTHnSparseF, {deltaZAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/NgenZvtxSplit", "; N_{gen}; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/NgenZvtxNoSplit", "; N_{gen}; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); + } + if (doprocessMcBestCentFT0C) { + registryMC.add({"Tracks/Centrality/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm); recTrkTypeAxis; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, dcaxyAxis, dcazAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); + } else { + registryMC.add({"Tracks/Centrality/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); recTrkTypeAxis; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); + } + if (doprocessMcCentFT0C) { + registryMC.add({"TracksMidrap/Centrality/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm); recTrkTypeAxis; centrality; occupancy", {HistType::kTHnSparseF, {etaMidrapAxis, zAxis, dcaxyAxis, dcazAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); + } registryMC.add({"Tracks/Centrality/EtaZvtxGen_t", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/EtaZvtxGen", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/EtaZvtxGenRecEvt", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Tracks/Centrality/PhiEta", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Tracks/Centrality/PhiEtaGen", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Tracks/Centrality/PhiEtaGen_t", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Events/Centrality/NtrkZvtxGen_t", "; N_{trk}; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Events/Centrality/NtrkZvtxGen", "; N_{trk}; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Tracks/Centrality/NclustersEta", "; nClusters; #eta; centrality; occupancy", {HistType::kTHnSparseF, {nclsAxis, etaAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Events/Centrality/NotFoundEventZvtx", "; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Events/Centrality/ZvtxDiff", "; Z_{rec} - Z_{gen} (cm); centrality; occupancy", {HistType::kTHnSparseF, {deltaZAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/PhiEtaGen", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/PhiEtaGenRecEvt", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Events/Centrality/hRecZvtxCent", "; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); + LOG(info) << "doprocessMc[Best]CentFT0C -> Size of the MC histograms:"; + registryMC.print(); } if (doprocessMcEfficiencyInclusive || doprocessMcEfficiencyBestInclusive) { registryMC.add("Events/hMcEffStatus", "Number of events; Cut; occupancy", {HistType::kTH2F, {{static_cast(McEffStatus::nMcEfftStatus), -0.5, +static_cast(McEffStatus::nMcEfftStatus) - 0.5}, occupancyAxis}}); @@ -605,11 +727,17 @@ struct DndetaMFTPbPb { for (int iBin = 0; iBin < static_cast(McEffStatus::nMcEfftStatus); iBin++) { registryMC.get(HIST("Events/hMcEffStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labelMcEffStatus[iBin].data()); } - registryMC.add({"Events/hVtxZGen", "; #it{z}_{vtx} (cm); genEvtType; occupancy", {HistType::kTHnSparseF, {zAxis, genEvtTypeAxis, occupancyAxis}}}); - registryMC.add({"Tracks/hEffGen", "; p_{T} (GeV/c); #varphi; #eta; #it{z}_{vtx} (cm); genEvtType; occupancy", {HistType::kTHnSparseF, {ptAxis, phiAxis, etaAxis, zAxis, genEvtTypeAxis, occupancyAxis}}}); + if (doprocessMcEfficiencyBestInclusive) { + registryMC.add({"Tracks/hEffRec", ";#eta; #it{z}_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm); recTrkTypeAxis; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, dcaxyAxis, dcazAxis, recTrkTypeAxis, occupancyAxis}}}); + } else { + registryMC.add({"Tracks/hEffRec", ";#eta; #it{z}_{vtx} (cm); recTrkTypeAxis; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, recTrkTypeAxis, occupancyAxis}}}); + } + registryMC.add({"Events/hVtxZGen", ";#it{z}_{vtx} (cm); genEvtType; occupancy", {HistType::kTHnSparseF, {zAxis, genTrkTypeAxis, occupancyAxis}}}); + registryMC.add({"Tracks/hEffGen", ";#eta; #it{z}_{vtx} (cm); genEvtType; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, genTrkTypeAxis, occupancyAxis}}}); registryMC.add({"Events/hVtxZRec", "#it{z}_{vtx} (cm); occupancy", {HistType::kTH2F, {zAxis, occupancyAxis}}}); - registryMC.add({"Tracks/hEffRec", "; p_{T} (GeV/c); #varphi; #eta; #it{z}_{vtx} (cm); recEvtType; occupancy", {HistType::kTHnSparseF, {ptAxis, phiAxis, etaAxis, zAxis, recEvtTypeAxis, occupancyAxis}}}); registryMC.add({"Tracks/hEtaRes", "#eta resolution;;(#eta_{rec} - #eta_{gen})/#eta_{gen}; occupancy", {HistType::kTHnSparseF, {etaAxis, {100, -1.0, 1.0}, occupancyAxis}}}); + LOG(info) << "doprocessMcEfficiency[Best]Inclusive -> Size of the MC histograms:"; + registryMC.print(); } if (doprocessMcEfficiencyCentFT0C || doprocessMcEfficiencyBestCentFT0C) { registryMC.add("Events/Centrality/hMcEffStatus", "Number of events; Cut; centrality; occupancy", {HistType::kTHnSparseF, {{static_cast(McEffStatus::nMcEfftStatus), -0.5, +static_cast(McEffStatus::nMcEfftStatus) - 0.5}, centralityAxis, occupancyAxis}}); @@ -621,45 +749,72 @@ struct DndetaMFTPbPb { for (int iBin = 0; iBin < static_cast(McEffStatus::nMcEfftStatus); iBin++) { registryMC.get(HIST("Events/Centrality/hMcEffStatus"))->GetAxis(0)->SetBinLabel(iBin + 1, labelMcEffStatusCent[iBin].data()); } - registryMC.add({"Events/Centrality/hVtxZGen", "; #it{z}_{vtx} (cm); genEvtType; centrality; occupancy", {HistType::kTHnSparseF, {zAxis, genEvtTypeAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Tracks/Centrality/hEffGen", "; p_{T} (GeV/c); #varphi; #eta; #it{z}_{vtx} (cm); genEvtType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, phiAxis, etaAxis, zAxis, genEvtTypeAxis, centralityAxis, occupancyAxis}}}); + if (doprocessMcEfficiencyBestCentFT0C) { + registryMC.add({"Tracks/Centrality/hEffRec", ";#eta; #it{z}_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm); recTrkTypeAxis; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, dcaxyAxis, dcazAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); + } else { + registryMC.add({"Tracks/Centrality/hEffRec", ";#eta; #it{z}_{vtx} (cm); recTrkTypeAxis; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); + } + registryMC.add({"Events/Centrality/hVtxZGen", ";#it{z}_{vtx} (cm); genEvtType; centrality; occupancy", {HistType::kTHnSparseF, {zAxis, genTrkTypeAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/hEffGen", ";#eta; #it{z}_{vtx} (cm); genEvtType; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, genTrkTypeAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Events/Centrality/hVtxZRec", "#it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Tracks/Centrality/hEffRec", "; p_{T} (GeV/c); #varphi; #eta; #it{z}_{vtx} (cm); recEvtType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, phiAxis, etaAxis, zAxis, recEvtTypeAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/hEffRecClosure", ";#eta; #it{z}_{vtx} (cm); recTrkTypeAxis; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); + LOG(info) << "doprocessMcEfficiency[Best]CentFT0C -> Size of the MC histograms:"; + registryMC.print(); } if (doprocessMcEfficiencyIdxInlusive || doprocessMcEfficiencyIdxBestInlusive) { - registryMC.add({"Tracks/hEffIdxGen", "; p_{T} (GeV/c); #eta; recEvtIdxType; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, genEvtIdxTypeAxis, occupancyAxis}}}); - registryMC.add({"Tracks/hEffIdxRec", "; p_{T} (GeV/c); #eta; genEvtIdxType; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, recEvtIdxTypeAxis, occupancyAxis}}}); + registryMC.add({"Tracks/hEffIdxGen", "; p_{T} (GeV/c); #eta; genEvtIdxType; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, genEvtIdxTypeAxis, occupancyAxis}}}); + registryMC.add({"Tracks/hEffIdxRec", "; p_{T} (GeV/c); #eta; recEvtIdxType; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, recEvtIdxTypeAxis, occupancyAxis}}}); registryMC.add({"Tracks/NmftTrkPerPart", "; #it{N}_{mft tracks per particle}; occupancy", {HistType::kTH2F, {{10, 0.5, 10.5}, occupancyAxis}}}); + LOG(info) << "doprocessMcEfficiencyIdx[Best]Inlusive -> Size of the MC histograms:"; + registryMC.print(); } if (doprocessMcEfficiencyIdxCentFT0C || doprocessMcEfficiencyIdxBestCentFT0C) { - registryMC.add({"Tracks/Centrality/hEffIdxGen", "; p_{T} (GeV/c); #eta; recEvtIdxType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, genEvtIdxTypeAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Tracks/Centrality/hEffIdxRec", "; p_{T} (GeV/c); #eta; genEvtIdxType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, recEvtIdxTypeAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/hEffIdxGen", "; p_{T} (GeV/c); #eta; genEvtIdxType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, genEvtIdxTypeAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/hEffIdxRec", "; p_{T} (GeV/c); #eta; recEvtIdxType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, recEvtIdxTypeAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Tracks/Centrality/NmftTrkPerPart", "; #it{N}_{mft tracks per particle}; centrality; occupancy", {HistType::kTHnSparseF, {{10, 0.5, 10.5}, centralityAxis, occupancyAxis}}}); + LOG(info) << "doprocessMcEfficiencyIdx[Best]CentFT0C -> Size of the MC histograms:"; + registryMC.print(); + } + if (doprocessMcEfficiencyIdxBestLightInlusive) { + registryMC.add({"Tracks/hEffIdxGen", "; p_{T} (GeV/c); #eta; genEvtIdxLightTypeAxis", {HistType::kTHnSparseF, {ptAxis, etaAxis, genEvtIdxLightTypeAxis}}}); + registryMC.add({"Tracks/hEffIdxRec", "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); recEvtIdxLightTypeAxis", {HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, recEvtIdxLightTypeAxis}}}); + registryMC.add({"Tracks/NmftTrkPerPart", "; #it{N}_{mft tracks per particle}", {HistType::kTH1F, {{10, 0.5, 10.5}}}}); + LOG(info) << "doprocessMcEfficiencyIdx[Best]LightInlusive -> Size of the MC histograms:"; + registryMC.print(); + } + if (doprocessMcEfficiencyIdxBestLightCentFT0C) { + registryMC.add({"Tracks/Centrality/hEffIdxGen", "; p_{T} (GeV/c); #eta; genEvtIdxLightTypeAxis; centrality", {HistType::kTHnSparseF, {ptAxis, etaAxis, genEvtIdxLightTypeAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/hEffIdxRec", "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); recEvtIdxLightTypeAxis; centrality", {HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, recEvtIdxLightTypeAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/NmftTrkPerPart", "; #it{N}_{mft tracks per particle}; centrality", {HistType::kTH1F, {{10, 0.5, 10.5}}}}); + LOG(info) << "doprocessMcEfficiencyIdx[Best]LightCentFT0C -> Size of the MC histograms:"; + registryMC.print(); } if (doprocessMcSgnEvtLossCentFT0C) { - registryMC.add("Events/hEvtMcGen", "Events/hEvtMcGen", {HistType::kTH1F, {{4, 0.f, 4.f}}}); - registryMC.get(HIST("Events/hEvtMcGen"))->GetXaxis()->SetBinLabel(1, "all"); - registryMC.get(HIST("Events/hEvtMcGen"))->GetXaxis()->SetBinLabel(2, "z-vtx"); - registryMC.get(HIST("Events/hEvtMcGen"))->GetXaxis()->SetBinLabel(3, "isInelGt0wMft"); - registryMC.get(HIST("Events/hEvtMcGen"))->GetXaxis()->SetBinLabel(4, "TVX"); - // - registryMC.add("Events/EvtSigLossStatus", ";status;centrality", {HistType::kTH2F, {{3, 0.5, 3.5}, centralityAxis}}); - auto hstat = registryMC.get(HIST("Events/EvtSigLossStatus")); + registryMC.add("Events/hMcEvtLossStatus", "Number of events; Cut", {HistType::kTH1F, {{static_cast(McEvtLossStatus::kMcEvtStatus), -0.5, +static_cast(McEvtLossStatus::kMcEvtStatus) - 0.5}}}); + std::array(McEvtLossStatus::kMcEvtStatus)> labelMcEvtLossStatus{ + "All", + "Z-vtx", + "TVX", + "InelGt0 coll", + "InelGt0 in MFT"}; + for (int iBin = 0; iBin < static_cast(McEvtLossStatus::kMcEvtStatus); iBin++) { + registryMC.get(HIST("Events/hMcEvtLossStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labelMcEvtLossStatus[iBin].data()); + } + registryMC.add("Events/Centrality/hEvtSigLossStatus", ";status;centrality", {HistType::kTH2F, {{3, 0.5, 3.5}, centralityAxis}}); + auto hstat = registryMC.get(HIST("Events/Centrality/hEvtSigLossStatus")); hstat->GetXaxis()->SetBinLabel(1, "All MC gen events"); hstat->GetXaxis()->SetBinLabel(2, "MC gen events with rec event with event selection"); hstat->GetXaxis()->SetBinLabel(3, "MC gen events with no rec events"); - // - registryMC.add({"Events/hNchGen", "Evt loss; Gen Nch FT0C; evtLossType", {HistType::kTH2F, {multFT0cAxis, evtLossTypeAxis}}}); - registryMC.add({"Events/hMultGenVsCentSplit", "Split MC events: Gen Nch vs Rec Cent; rec cent; Gen Nch ", {HistType::kTH2F, {centralityAxis, multFT0cAxis}}}); - registryMC.add({"Events/hNchTVX", "; Nch; status", {HistType::kTH2F, {{2, 0, 2}, multAxis}}}); - registryMC.add({"Events/hMultGenVsCent", "event mult MC gen", {HistType::kTH2F, {centralityAxis, multFT0cAxis}}}); - registryMC.add({"Events/hMultGenVsCentNParticlesEta05", "event mult MC gen", {HistType::kTH2F, {centralityAxis, multAxis}}}); - registryMC.add({"Events/hMultGenVsCentNParticlesEtaMFT", "event mult MC gen", {HistType::kTH2F, {centralityAxis, multAxis}}}); - registryMC.add({"Events/hMultGenVsCentRec", "event mult MC gen vs centrality", {HistType::kTH2F, {centralityAxis, multFT0cAxis}}}); - registryMC.add({"Events/hMultGenVsCentRecNParticlesEta05", "event mult MC gen vs centrality", {HistType::kTH2F, {centralityAxis, multAxis}}}); - registryMC.add({"Events/hMultGenVsCentRecNParticlesEtaMFT", "event mult MC gen vs centrality", {HistType::kTH2F, {centralityAxis, multAxis}}}); - registryMC.add({"Tracks/hEtaVsNchGen", "; #eta; mult gen", {HistType::kTH2F, {etaAxis, multFT0cAxis}}}); - registryMC.add({"Tracks/hEtaVsNchGenRecEvt", "; #eta; mult gen w/ Rec evt", {HistType::kTH2F, {etaAxis, multFT0cAxis}}}); + registryMC.add({"Events/hNchGen", "Evt loss; Gen Nch FT0C; evtLossType", {HistType::kTH2F, {multAxis, evtLossTypeAxis}}}); + registryMC.add({"Events/hMultGenVsCentSplit", "Split MC events: Gen Nch vs Rec Cent; rec cent; Gen Nch ", {HistType::kTH2F, {centralityAxis, multAxis}}}); + registryMC.add({"Events/hMultGenVsCent", "Gen Nch vs rec cent.; Gen Nch; Gen Nch; cent rec.", {HistType::kTHnSparseF, {multAxis, multAxis, centralityAxis}}}); + registryMC.add({"Events/hMultGenVsCentMFTacc", "Gen Nch (MFT acc) vs rec cent.; Gen Nch (MFT acc); cent rec.", {HistType::kTH2F, {multAxis, centralityAxis}}}); + registryMC.add({"Events/hMultGenVsCentRecEvt", "Gen Nch w/ Nrec > 0 vs rec cent.; Gen Nch; Gen Nch; cent rec.", {HistType::kTHnSparseF, {multAxis, multAxis, centralityAxis}}}); + registryMC.add({"Events/hMultGenVsCentRecEvtMFTacc", "Gen Nch (MFT acc) w/ Nrec > 0 vs rec cent.; Gen Nch (MFT acc); cent rec.", {HistType::kTH2F, {multAxis, centralityAxis}}}); + registryMC.add({"Tracks/hNchGenVsEta", "Gen Nch vs #eta; Gen Nch; #eta", {HistType::kTH2F, {multAxis, etaAxis}}}); + registryMC.add({"Tracks/hNchGenVsEtaRecEvt", "Gen Nch w/ Nrec > 0 vs #eta; Gen Nch; #eta", {HistType::kTH2F, {multAxis, etaAxis}}}); + LOG(info) << "doprocessMcSgnEvtLossCentFT0C -> Size of the MC histograms:"; + registryMC.print(); } if (doprocessMcReassocDCA) { registryMC.add({"Events/Centrality/EvtGenRecReassoc", ";status;centrality", {HistType::kTHnSparseF, {{3, 0.5, 3.5}, centralityAxis}}}); @@ -667,12 +822,10 @@ struct DndetaMFTPbPb { heff->GetAxis(0)->SetBinLabel(1, "All reconstructed"); heff->GetAxis(0)->SetBinLabel(2, "Selected reconstructed"); heff->GetAxis(0)->SetBinLabel(3, "Remove split vertices"); - registryMC.add("Events/hCentBest", "; centrality", HistType::kTH1F, {centralityAxis}); registryMC.add("Events/hGenMult", "Sel rec evt. vs generated mult; mult", HistType::kTH1F, {multAxis}); registryMC.add("Events/hCentRecVsGenMult", "Rec cent vs gen mid-rapidity multiplicity; centrality; mult", HistType::kTH2F, {centralityAxis, multAxis}); registryMC.add("Events/hCentRecFromGenMult", "Centrality from generated mult", HistType::kTH1F, {centralityAxis}); - registryMC.add({"Tracks/Centrality/THnDCAxyBestRec", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); registryMC.add({"Tracks/Centrality/THnDCAxyBestRecFake", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); registryMC.add({"Tracks/Centrality/THnDCAxyBestGenPrim", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); @@ -707,11 +860,19 @@ struct DndetaMFTPbPb { registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSecWeakAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSecMatAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSecMatAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + LOG(info) << "doprocessMcReassocDCA -> Size of the MC histograms:"; + registryMC.print(); } } - /// Filters - particles - Filter primaries = ifnode(nsqrt(aod::mcparticle::vx * aod::mcparticle::vx + aod::mcparticle::vy * aod::mcparticle::vy) > CfloatFive, false, ncheckbit(aod::mcparticle_v2::storedFlags, (uint8_t)o2::aod::mcparticle::enums::PhysicalPrimary)); + /// Filter (midrapidity selections) + expressions::Filter fTrackSelectionITS = ncheckbit(aod::track::v001::detectorMap, (uint8_t)o2::aod::track::ITS) && + ncheckbit(aod::track::trackCutFlag, TrackSelectionITS); + expressions::Filter fTrackSelectionTPC = ifnode(ncheckbit(aod::track::v001::detectorMap, (uint8_t)o2::aod::track::TPC), + ncheckbit(aod::track::trackCutFlag, TrackSelectionTPC), true); + expressions::Filter fTrackSelectionDCA = ifnode(gConf.dcaZmidrap.node() > 0.f, nabs(aod::track::dcaZ) <= dcaZ && ncheckbit(aod::track::trackCutFlag, TrackSelectionDCAXYonly), + ncheckbit(aod::track::trackCutFlag, TrackSelectionDCA)); + /// Joined tables using FullBCs = soa::Join; using CollBCs = soa::Join; @@ -720,16 +881,65 @@ struct DndetaMFTPbPb { using Colls = soa::Join; using CollsCentFT0C = soa::Join; using CollsGenCentFT0C = soa::Join; - using CollsGenCentFT0CExtra = soa::Join; using CollsCorr = soa::Join; using CollsMCExtra = soa::Join; using CollsMCExtraMult = soa::Join; /// Tracks using MftTracksLabeled = soa::Join; using MftBestTracksLabeled = soa::Join; + // midrapidity tracks + using MidrapTracksLabeled = soa::Join; + using FiltMidrapTracksLabeled = soa::Filtered; + /// Particles using ParticlesIdx = soa::Join; - using FiltParticlesIdx = soa::Filtered; + + template + float getMult(C const& collision) + { + float val = CInvalid; + switch (gConf.multEstimator.value) { + case MultE::CnoMult: + return val; + case MultE::CmultFT0C: + if constexpr (!isMC) { + return collision.centFT0C(); + } else { + return collision.multMCFT0C(); + } + break; + case MultE::CmultFT0M: + if constexpr (!isMC) { + return collision.centFT0M(); + } else { + return collision.multMCFT0C() + collision.multMCFT0A(); + } + break; + case MultE::CmultTPC: + if constexpr (!isMC) { + return collision.multTPC(); + } else { + return collision.multMCNParticlesEta08(); + } + break; + case MultE::CmultMFT: + if constexpr (!isMC) { + return collision.centMFT(); + } else { + return CInvalid; + // return collision.multMCMFT(); // FIXME: uncomment when MC MFT mult is in available mcCentralityModule.cxx or in aod::MultMCExtras + } + break; + default: + LOGF(fatal, "No valid multiplicity estimator: %s", gConf.multEstimator.value); + return val; + } + } + + float getMultMC(CollsMCExtraMult::iterator const& collision) + { + return getMult(collision); + } template float getDCAz(const T& track) @@ -766,10 +976,10 @@ struct DndetaMFTPbPb { std::unordered_map mapRecToMc; std::unordered_map mapMcToRec; - PresliceUnsorted perCollMcRec = aod::mccollisionlabel::mcCollisionId; + PresliceUnsorted perCollMcRec = aod::mccollisionlabel::mcCollisionId; template - void createMCIds(MC const& mcCollisions, CollsGenCentFT0CExtra const& collisions, P const& particles) + void createMCIds(MC const& mcCollisions, CollsGenCentFT0C const& collisions, P const& particles) { const auto& nMcColls = mcCollisions.size(); LOG(info) << "MC collisions: " << nMcColls; @@ -929,8 +1139,15 @@ struct DndetaMFTPbPb { if constexpr (fillHis) { registryQC.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelEta)); } + float phi = track.phi(); + o2::math_utils::bringTo02Pi(phi); + if (phi < trackCuts.minPhi || trackCuts.maxPhi < phi) { + return false; + } + if constexpr (fillHis) { + registryQC.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelPhi)); + } if (trackCuts.usephiCut) { - float phi = track.phi(); if ((phi < trackCuts.phiCut) || ((phi > PI - trackCuts.phiCut) && (phi < PI + trackCuts.phiCut)) || (phi > TwoPI - trackCuts.phiCut) || @@ -957,34 +1174,30 @@ struct DndetaMFTPbPb { return true; } - template + template int countTracks(T const& tracks, float z, float c, float occ) { auto nTrk = 0; for (auto const& track : tracks) { - if (fillHis) { - if constexpr (has_reco_cent) { - registryData.fill(HIST("Tracks/Centrality/Chi2Eta"), track.chi2(), track.eta(), c, occ); - registryData.fill(HIST("Tracks/Centrality/NclustersEta"), track.nClusters(), track.eta(), c, occ); - } else { - registryData.fill(HIST("Tracks/Chi2Eta"), track.chi2(), track.eta(), occ); - registryData.fill(HIST("Tracks/NclustersEta"), track.nClusters(), track.eta(), occ); - } - } if (!isTrackSelected(track)) { continue; } - if (fillHis) { + float phi = track.phi(); + o2::math_utils::bringTo02Pi(phi); + if constexpr (has_reco_cent) { + registryData.fill(HIST("Tracks/Centrality/EtaZvtx"), track.eta(), z, c, occ); + registryData.fill(HIST("Tracks/Centrality/PhiEta"), phi, track.eta(), c, occ); + } else { + registryData.fill(HIST("Tracks/EtaZvtx"), track.eta(), z, occ); + registryData.fill(HIST("Tracks/PhiEta"), phi, track.eta(), occ); + } + if (gConf.fillTrackQaHist) { if constexpr (has_reco_cent) { - registryData.fill(HIST("Tracks/Centrality/EtaZvtx"), track.eta(), z, c, occ); - registryData.fill(HIST("Tracks/Centrality/PhiEta"), track.phi(), track.eta(), c, occ); - registryData.fill(HIST("Tracks/Centrality/TanLambda"), track.tgl(), c, occ); - registryData.fill(HIST("Tracks/Centrality/InvQPt"), track.signed1Pt(), c, occ); + registryData.fill(HIST("Tracks/Centrality/Chi2Eta"), track.chi2(), c, occ); + registryData.fill(HIST("Tracks/Centrality/NclustersEta"), track.nClusters(), c, occ); } else { - registryData.fill(HIST("Tracks/EtaZvtx"), track.eta(), z, occ); - registryData.fill(HIST("Tracks/PhiEta"), track.phi(), track.eta(), occ); - registryData.fill(HIST("Tracks/TanLambda"), track.tgl(), occ); - registryData.fill(HIST("Tracks/InvQPt"), track.signed1Pt(), occ); + registryData.fill(HIST("Tracks/Chi2Eta"), track.chi2(), occ); + registryData.fill(HIST("Tracks/NclustersEta"), track.nClusters(), occ); } } ++nTrk; @@ -992,126 +1205,51 @@ struct DndetaMFTPbPb { return nTrk; } - template - void countBestTracksExtra(B const& besttracksExtra, float c, float occ) - { - for (auto const& etrack : besttracksExtra) { - if (fillHis) { - if constexpr (has_reco_cent) { - if (gConf.cfgUseTrackParExtra) { - registryData.fill(HIST("Tracks/Centrality/TanLambdaExtra"), etrack.tgl(), c, occ); - registryData.fill(HIST("Tracks/Centrality/InvQPtExtra"), etrack.signed1Pt(), c, occ); - registryData.fill(HIST("Tracks/Centrality/EtaExtra"), etrack.etas(), c, occ); - registryData.fill(HIST("Tracks/Centrality/PhiExtra"), etrack.phis(), c, occ); - } - } else { - if (gConf.cfgUseTrackParExtra) { - registryData.fill(HIST("Tracks/TanLambdaExtra"), etrack.tgl(), occ); - registryData.fill(HIST("Tracks/InvQPtExtra"), etrack.signed1Pt(), occ); - registryData.fill(HIST("Tracks/EtaExtra"), etrack.etas(), occ); - registryData.fill(HIST("Tracks/PhiExtra"), etrack.phis(), occ); - } - } - } - } - } - - template - int countBestTracks(T const& tracks, B const& besttracks, float z, float c, float occ) + template + int countBestTracks(T const& /*tracks*/, B const& besttracks, float z, float c, float occ) { auto nATrk = 0; - ambiguousTrkIds.reserve(besttracks.size()); - reassignedTrkIds.reserve(besttracks.size()); for (auto const& atrack : besttracks) { if (!isBestTrackSelected(atrack)) { continue; } const float bestDcaZ = getDCAz(atrack); auto itrack = atrack.template mfttrack_as(); - if constexpr (has_reco_cent) { - registryData.fill(HIST("Tracks/Centrality/Chi2Eta"), itrack.chi2(), itrack.eta(), c, occ); - registryData.fill(HIST("Tracks/Centrality/NclustersEta"), itrack.nClusters(), itrack.eta(), c, occ); - } else { - registryData.fill(HIST("Tracks/Chi2Eta"), itrack.chi2(), itrack.eta(), occ); - registryData.fill(HIST("Tracks/NclustersEta"), itrack.nClusters(), itrack.eta(), occ); - } if (!isTrackSelected(itrack)) { continue; } - ambiguousTrkIds.emplace_back(atrack.mfttrackId()); - ++nATrk; - if (fillHis) { + float phi = itrack.phi(); + o2::math_utils::bringTo02Pi(phi); + if constexpr (has_reco_cent) { + registryData.fill(HIST("Tracks/Centrality/EtaZvtx"), itrack.eta(), z, atrack.bestDCAXY(), bestDcaZ, c, occ); + registryData.fill(HIST("Tracks/Centrality/PhiEta"), phi, itrack.eta(), c, occ); + } else { + registryData.fill(HIST("Tracks/EtaZvtx"), itrack.eta(), z, atrack.bestDCAXY(), bestDcaZ, occ); + registryData.fill(HIST("Tracks/PhiEta"), phi, itrack.eta(), occ); + } + if (gConf.fillTrackQaHist) { if constexpr (has_reco_cent) { - registryData.fill(HIST("Tracks/Centrality/EtaZvtx"), itrack.eta(), z, c, occ); - registryData.fill(HIST("Tracks/Centrality/PhiEta"), itrack.phi(), itrack.eta(), c, occ); - registryData.fill(HIST("Tracks/Centrality/TanLambda"), itrack.tgl(), c, occ); - registryData.fill(HIST("Tracks/Centrality/InvQPt"), itrack.signed1Pt(), c, occ); - registryData.fill(HIST("Tracks/Centrality/DCA3d"), itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, c, occ); + registryData.fill(HIST("Tracks/Centrality/Chi2Eta"), itrack.chi2(), c, occ); + registryData.fill(HIST("Tracks/Centrality/NclustersEta"), itrack.nClusters(), c, occ); registryData.fill(HIST("Tracks/Centrality/TrackAmbDegree"), atrack.ambDegree(), c, occ); } else { - registryData.fill(HIST("Tracks/EtaZvtx"), itrack.eta(), z, occ); - registryData.fill(HIST("Tracks/PhiEta"), itrack.phi(), itrack.eta(), occ); - registryData.fill(HIST("Tracks/TanLambda"), itrack.tgl(), occ); - registryData.fill(HIST("Tracks/InvQPt"), itrack.signed1Pt(), occ); - registryData.fill(HIST("Tracks/DCA3d"), itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, occ); + registryData.fill(HIST("Tracks/Chi2Eta"), itrack.chi2(), occ); + registryData.fill(HIST("Tracks/NclustersEta"), itrack.nClusters(), occ); registryData.fill(HIST("Tracks/TrackAmbDegree"), atrack.ambDegree(), occ); } - } - if (itrack.has_collision() && itrack.collisionId() != atrack.bestCollisionId()) { - reassignedTrkIds.emplace_back(atrack.mfttrackId()); - if (fillHis) { + if (itrack.has_collision() && itrack.collisionId() != atrack.bestCollisionId()) { registryQC.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkBestSel::trkBestSelNumReassoc)); if constexpr (has_reco_cent) { registryData.fill(HIST("Tracks/Centrality/ReTracksEtaZvtx"), itrack.eta(), itrack.template collision_as().posZ(), c, occ); - registryData.fill(HIST("Tracks/Centrality/ReTracksPhiEta"), itrack.phi(), itrack.eta(), c, occ); + registryData.fill(HIST("Tracks/Centrality/ReTracksPhiEta"), phi, itrack.eta(), c, occ); } else { registryData.fill(HIST("Tracks/ReTracksEtaZvtx"), itrack.eta(), itrack.template collision_as().posZ(), occ); - registryData.fill(HIST("Tracks/ReTracksPhiEta"), itrack.phi(), itrack.eta(), occ); + registryData.fill(HIST("Tracks/ReTracksPhiEta"), phi, itrack.eta(), occ); } } } + ++nATrk; } - - for (auto const& track : tracks) { - if (!isTrackSelected(track)) { - continue; - } - if (fillHis) { - if constexpr (has_reco_cent) { - registryData.fill(HIST("Tracks/Centrality/OrigTracksEtaZvtx"), track.eta(), z, c, occ); - registryData.fill(HIST("Tracks/Centrality/OrigTracksPhiEta"), track.phi(), track.eta(), c, occ); - } else { - registryData.fill(HIST("Tracks/OrigTracksEtaZvtx"), track.eta(), z, occ); - registryData.fill(HIST("Tracks/OrigTracksPhiEta"), track.phi(), track.eta(), occ); - } - } - if (std::find(ambiguousTrkIds.begin(), ambiguousTrkIds.end(), track.globalIndex()) != ambiguousTrkIds.end()) { - continue; - } - if (std::find(reassignedTrkIds.begin(), reassignedTrkIds.end(), track.globalIndex()) != reassignedTrkIds.end()) { - continue; - } - // ++nATrk; // use for testing purposes only! - if (fillHis) { - if constexpr (has_reco_cent) { - registryData.fill(HIST("Tracks/Centrality/RestTracksEtaZvtx"), track.eta(), z, c, occ); - registryData.fill(HIST("Tracks/Centrality/RestTracksPhiEta"), track.phi(), track.eta(), c, occ); - // registryData.fill(HIST("Tracks/Centrality/EtaZvtx"), track.eta(), z, c, occ); - // registryData.fill(HIST("Tracks/Centrality/PhiEta"), phi, track.eta(), c, occ); - // registryData.fill(HIST("Tracks/Centrality/NclustersEta"), track.nClusters(), track.eta(), c, occ); - } else { - registryData.fill(HIST("Tracks/RestTracksEtaZvtx"), track.eta(), z, occ); - registryData.fill(HIST("Tracks/RestTracksPhiEta"), track.phi(), track.eta(), occ); - // registryData.fill(HIST("Tracks/EtaZvtx"), track.eta(), z, occ); - // registryData.fill(HIST("Tracks/PhiEta"), phi, track.eta(), occ); - // registryData.fill(HIST("Tracks/NclustersEta"), track.nClusters(), track.eta(), occ); - } - } - } - ambiguousTrkIds.clear(); - ambiguousTrkIds.shrink_to_fit(); - reassignedTrkIds.clear(); - reassignedTrkIds.shrink_to_fit(); return nATrk; } @@ -1119,34 +1257,15 @@ struct DndetaMFTPbPb { bool isInelGt0wMft(P const& particles) { int nChrgMc = 0; - int nChrgFT0A = 0; - int nChrgFT0C = 0; for (auto const& particle : particles) { if (!isChrgParticle(particle.pdgCode())) { continue; } - if (!particle.isPhysicalPrimary()) { - continue; - } - // trigger TVX - if (particle.eta() > CminAccFT0A && particle.eta() < CmaxAccFT0A) { - nChrgFT0A++; - } - if (particle.eta() > CminAccFT0C && particle.eta() < CmaxAccFT0C) { - nChrgFT0C++; - } - // acceptance MFT - if (particle.eta() < trackCuts.minEta || particle.eta() > trackCuts.maxEta) { + if (gConf.useParticleSel && !isParticleSelected(particle)) { continue; } nChrgMc++; } - if (nChrgFT0A == CintZero || nChrgFT0C == CintZero) { - registryMC.fill(HIST("Events/hNchTVX"), nChrgMc, 0.5); - return false; - } - registryMC.fill(HIST("Events/hNchTVX"), nChrgMc, 1.5); - return nChrgMc != CintZero; } @@ -1192,7 +1311,7 @@ struct DndetaMFTPbPb { if (!isChrgParticle(particle.pdgCode())) { continue; } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + if (gConf.useParticleSel && !isParticleSelected(particle)) { continue; } if (particle.eta() < trackCuts.minEta || particle.eta() > trackCuts.maxEta) { @@ -1206,17 +1325,21 @@ struct DndetaMFTPbPb { template bool isParticleSelected(P const& particle) { - if (gConf.cfgUsePrimaries && !particle.isPhysicalPrimary()) { + if (gConf.usePrimaries && !particle.isPhysicalPrimary()) { return false; } - if (!gConf.cfgUsePrimaries && (gConf.cfgUseSecondaries && particle.isPhysicalPrimary())) { + if (!gConf.usePrimaries && (gConf.useSecondaries && particle.isPhysicalPrimary())) { return false; } if (particle.eta() < trackCuts.minEta || particle.eta() > trackCuts.maxEta) { return false; } + float phi = particle.phi(); + o2::math_utils::bringTo02Pi(phi); + if (phi < trackCuts.minPhi || trackCuts.maxPhi < phi) { + return false; + } if (trackCuts.usephiCut) { - float phi = particle.phi(); if ((phi < trackCuts.phiCut) || ((phi > PI - trackCuts.phiCut) && (phi < PI + trackCuts.phiCut)) || (phi > TwoPI - trackCuts.phiCut) || @@ -1228,16 +1351,6 @@ struct DndetaMFTPbPb { return true; } - template - bool cutInPhi(P const& particle) - { - if (!trackCuts.usephiCut) { - return true; - } - auto phi = particle.phi(); - return (phi < trackCuts.phiCut) || ((phi > PI - trackCuts.phiCut) && (phi < PI + trackCuts.phiCut)) || (phi > TwoPI - trackCuts.phiCut) || ((phi > ((PIHalf - 0.1) * PI) - trackCuts.phiCut) && (phi < ((PIHalf - 0.1) * PI) + trackCuts.phiCut)); - } - template float getOccupancy(C const& collision, uint occEstimator) { @@ -1389,21 +1502,41 @@ struct DndetaMFTPbPb { } template - void fillHistMC(P const& particles, float zvtx, float c, float occ) + void fillHistMC(P const& particles, float zvtx, float c, float occ, bool const gtZeroColl, bool const gtOneRec) { for (auto const& particle : particles) { if (!isChrgParticle(particle.pdgCode())) { continue; } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + if (gConf.useParticleSel && !isParticleSelected(particle)) { continue; } + float phi = particle.phi(); + o2::math_utils::bringTo02Pi(phi); if constexpr (isCent) { - registryMC.fill(HIST("Tracks/Centrality/EtaZvtxGen"), particle.eta(), zvtx, c, occ); - registryMC.fill(HIST("Tracks/Centrality/PhiEtaGen"), particle.phi(), particle.eta(), c, occ); + registryMC.fill(HIST("Tracks/Centrality/EtaZvtxGen_t"), particle.eta(), zvtx, c, occ); + registryMC.fill(HIST("Tracks/Centrality/PhiEtaGen_t"), phi, particle.eta(), c, occ); } else { - registryMC.fill(HIST("Tracks/EtaZvtxGen"), particle.eta(), zvtx, occ); - registryMC.fill(HIST("Tracks/PhiEtaGen"), particle.phi(), particle.eta(), occ); + registryMC.fill(HIST("Tracks/EtaZvtxGen_t"), particle.eta(), zvtx, occ); + registryMC.fill(HIST("Tracks/PhiEtaGen_t"), phi, particle.eta(), occ); + } + if (gtZeroColl) { + if constexpr (isCent) { + registryMC.fill(HIST("Tracks/Centrality/EtaZvtxGen"), particle.eta(), zvtx, c, occ); + registryMC.fill(HIST("Tracks/Centrality/PhiEtaGen"), phi, particle.eta(), c, occ); + } else { + registryMC.fill(HIST("Tracks/EtaZvtxGen"), particle.eta(), zvtx, occ); + registryMC.fill(HIST("Tracks/PhiEtaGen"), phi, particle.eta(), occ); + } + } + if (gtOneRec) { + if constexpr (isCent) { + registryMC.fill(HIST("Tracks/Centrality/EtaZvtxGenRecEvt"), particle.eta(), zvtx, c, occ); + registryMC.fill(HIST("Tracks/Centrality/PhiEtaGenRecEvt"), phi, particle.eta(), c, occ); + } else { + registryMC.fill(HIST("Tracks/EtaZvtxGenRecEvt"), particle.eta(), zvtx, occ); + registryMC.fill(HIST("Tracks/PhiEtaGenRecEvt"), phi, particle.eta(), occ); + } } } } @@ -1439,51 +1572,68 @@ struct DndetaMFTPbPb { PROCESS_SWITCH(DndetaMFTPbPb, processTagging, "Collect event sample stats", true); + std::vector nTrkPerCol; + std::vector centPerCol; + std::vector occPerCol; + /// @brief process function for counting tracks template void processData(typename C::iterator const& collision, aod::MFTTracks const& tracks, CollBCs const& /*bcs*/) { + nTrkPerCol.clear(); + centPerCol.clear(); + occPerCol.clear(); float c = getRecoCent(collision); float occ = getOccupancy(collision, eventCuts.occupancyEstimator); auto bc = collision.template foundBC_as(); if constexpr (has_reco_cent) { - registryData.fill(HIST("Events/Centrality/Selection"), 1., c, occ); + registryData.fill(HIST("Events/Centrality/DataEvtStatus"), static_cast(DataEvtStatus::kDataAll), c, occ); } else { - registryData.fill(HIST("Events/Selection"), 1., occ); + registryData.fill(HIST("Events/DataEvtStatus"), static_cast(DataEvtStatus::kDataAll), occ); } - if (gConf.cfgDoIR) { + if (gConf.useIRFromCCDB) { initHadronicRate(bc); - float ir = !gConf.cfgIRSource.value.empty() ? rateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), gConf.cfgIRSource, gConf.cfgIRCrashOnNull) * 1.e-3 : -1; + float ir = !gConf.intRateSource.value.empty() ? rateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), gConf.intRateSource, gConf.intRateCrashOnNull) * 1.e-3 : -1; if constexpr (has_reco_cent) { registryData.fill(HIST("Events/Centrality/hInteractionRate"), ir, c, occ); } else { registryData.fill(HIST("Events/hInteractionRate"), ir, occ); } float seconds = bc.timestamp() * 1.e-3 - mMinSeconds; - if (gConf.cfgUseIRCut && (ir < eventCuts.minIR || ir > eventCuts.maxIR)) { // cut on hadronic rate + if (gConf.useIRCut && (ir < eventCuts.minIR || ir > eventCuts.maxIR)) { // cut on hadronic rate return; } gCurrentHadronicRate->Fill(seconds, ir); } - if (!isGoodEvent(collision)) { - return; - } - auto z = collision.posZ(); - if constexpr (has_reco_cent) { - registryData.fill(HIST("Events/Centrality/Selection"), 2., c, occ); - registryData.fill(HIST("Events/Centrality/hZvtxCent"), z, c, occ); - } else { - registryData.fill(HIST("Events/Selection"), 2., occ); - } - - auto nTrk = countTracks(tracks, z, c, occ); - - if constexpr (has_reco_cent) { - registryData.fill(HIST("Events/Centrality/NtrkZvtx"), nTrk, z, c, occ); + if (!gConf.useEvtSel || isGoodEvent(collision)) { + auto z = collision.posZ(); + centPerCol.emplace_back(c); + occPerCol.emplace_back(occ); + if constexpr (has_reco_cent) { + registryData.fill(HIST("Events/Centrality/DataEvtStatus"), static_cast(DataEvtStatus::kDataSel), c, occ); + registryData.fill(HIST("Events/Centrality/hZvtxCent"), z, c, occ); + } else { + registryData.fill(HIST("Events/DataEvtStatus"), static_cast(DataEvtStatus::kDataSel), occ); + } + auto nTrk = countTracks(tracks, z, c, occ); + nTrkPerCol.emplace_back(nTrk); + if (gConf.fillTrackQaHist) { + for (auto i = 0U; i < nTrkPerCol.size(); ++i) { + if constexpr (has_reco_cent) { + registryData.fill(HIST("Events/Centrality/NtrkZvtx"), nTrkPerCol[i], z, centPerCol[i], occPerCol[i]); + } else { + registryData.fill(HIST("Events/NtrkZvtx"), nTrkPerCol[i], z, occPerCol[i]); + } + } + } } else { - registryData.fill(HIST("Events/NtrkZvtx"), nTrk, z, occ); + if constexpr (has_reco_cent) { + registryData.fill(HIST("Events/Centrality/DataEvtStatus"), static_cast(DataEvtStatus::kDataRej), c, occ); + } else { + registryData.fill(HIST("Events/DataEvtStatus"), static_cast(DataEvtStatus::kDataRej), occ); + } } } @@ -1492,49 +1642,60 @@ struct DndetaMFTPbPb { void processDatawBestTracks(typename C::iterator const& collision, aod::MFTTracks const& tracks, soa::SmallGroups const& besttracks, - aod::BestCollisionsFwd3dExtra const& besttracksExtra, CollBCs const& /*bcs*/) { + nTrkPerCol.clear(); + centPerCol.clear(); + occPerCol.clear(); float c = getRecoCent(collision); float occ = getOccupancy(collision, eventCuts.occupancyEstimator); auto bc = collision.template foundBC_as(); if constexpr (has_reco_cent) { - registryData.fill(HIST("Events/Centrality/Selection"), 1., c, occ); + registryData.fill(HIST("Events/Centrality/DataEvtStatus"), static_cast(DataEvtStatus::kDataAll), c, occ); } else { - registryData.fill(HIST("Events/Selection"), 1., occ); + registryData.fill(HIST("Events/DataEvtStatus"), static_cast(DataEvtStatus::kDataAll), occ); } - if (gConf.cfgDoIR) { + if (gConf.useIRFromCCDB) { initHadronicRate(bc); - float ir = !gConf.cfgIRSource.value.empty() ? rateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), gConf.cfgIRSource, gConf.cfgIRCrashOnNull) * 1.e-3 : -1; + float ir = !gConf.intRateSource.value.empty() ? rateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), gConf.intRateSource, gConf.intRateCrashOnNull) * 1.e-3 : -1; if constexpr (has_reco_cent) { registryData.fill(HIST("Events/Centrality/hInteractionRate"), ir, c, occ); } else { registryData.fill(HIST("Events/hInteractionRate"), ir, occ); } float seconds = bc.timestamp() * 1.e-3 - mMinSeconds; - if (gConf.cfgUseIRCut && (ir < eventCuts.minIR || ir > eventCuts.maxIR)) { // cut on hadronic rate + if (gConf.useIRCut && (ir < eventCuts.minIR || ir > eventCuts.maxIR)) { // cut on hadronic rate return; } gCurrentHadronicRate->Fill(seconds, ir); } - if (!isGoodEvent(collision)) { - return; - } - auto z = collision.posZ(); - if constexpr (has_reco_cent) { - registryData.fill(HIST("Events/Centrality/Selection"), 2., c, occ); - registryData.fill(HIST("Events/Centrality/hZvtxCent"), z, c, occ); - } else { - registryData.fill(HIST("Events/Selection"), 2., occ); - } - - auto nBestTrks = countBestTracks(tracks, besttracks, z, c, occ); - countBestTracksExtra(besttracksExtra, c, occ); - - if constexpr (has_reco_cent) { - registryData.fill(HIST("Events/Centrality/NtrkZvtx"), nBestTrks, z, c, occ); + if (!gConf.useEvtSel || isGoodEvent(collision)) { + auto z = collision.posZ(); + centPerCol.emplace_back(c); + occPerCol.emplace_back(occ); + if constexpr (has_reco_cent) { + registryData.fill(HIST("Events/Centrality/DataEvtStatus"), static_cast(DataEvtStatus::kDataSel), c, occ); + registryData.fill(HIST("Events/Centrality/hZvtxCent"), z, c, occ); + } else { + registryData.fill(HIST("Events/DataEvtStatus"), static_cast(DataEvtStatus::kDataSel), occ); + } + auto nBestTrks = countBestTracks(tracks, besttracks, z, c, occ); + nTrkPerCol.emplace_back(nBestTrks); + if (gConf.fillTrackQaHist) { + for (auto i = 0U; i < nTrkPerCol.size(); ++i) { + if constexpr (has_reco_cent) { + registryData.fill(HIST("Events/Centrality/NtrkZvtx"), nTrkPerCol[i], z, centPerCol[i], occPerCol[i]); + } else { + registryData.fill(HIST("Events/NtrkZvtx"), nTrkPerCol[i], z, occPerCol[i]); + } + } + } } else { - registryData.fill(HIST("Events/NtrkZvtx"), nBestTrks, z, occ); + if constexpr (has_reco_cent) { + registryData.fill(HIST("Events/Centrality/DataEvtStatus"), static_cast(DataEvtStatus::kDataRej), c, occ); + } else { + registryData.fill(HIST("Events/DataEvtStatus"), static_cast(DataEvtStatus::kDataRej), occ); + } } } @@ -1552,16 +1713,16 @@ struct DndetaMFTPbPb { PROCESS_SWITCH(DndetaMFTPbPb, processDataCentFT0C, "Count tracks in FT0C centrality bins", false); - void processDatawBestTracksInclusive(Colls::iterator const& collision, aod::MFTTracks const& tracks, soa::SmallGroups const& besttracks, aod::BestCollisionsFwd3dExtra const& besttracksExtra, CollBCs const& bcs) + void processDatawBestTracksInclusive(Colls::iterator const& collision, aod::MFTTracks const& tracks, soa::SmallGroups const& besttracks, CollBCs const& bcs) { - processDatawBestTracks(collision, tracks, besttracks, besttracksExtra, bcs); + processDatawBestTracks(collision, tracks, besttracks, bcs); } PROCESS_SWITCH(DndetaMFTPbPb, processDatawBestTracksInclusive, "Count tracks based on BestCollisionsFwd3d table (inclusive)", false); - void processDatawBestTracksCentFT0C(CollsCentFT0C::iterator const& collision, aod::MFTTracks const& tracks, soa::SmallGroups const& besttracks, aod::BestCollisionsFwd3dExtra const& besttracksExtra, CollBCs const& bcs) + void processDatawBestTracksCentFT0C(CollsCentFT0C::iterator const& collision, aod::MFTTracks const& tracks, soa::SmallGroups const& besttracks, CollBCs const& bcs) { - processDatawBestTracks(collision, tracks, besttracks, besttracksExtra, bcs); + processDatawBestTracks(collision, tracks, besttracks, bcs); } PROCESS_SWITCH(DndetaMFTPbPb, processDatawBestTracksCentFT0C, "Count tracks in FT0C centrality bins based on BestCollisionsFwd3d table", false); @@ -1577,14 +1738,14 @@ struct DndetaMFTPbPb { auto nBestTrks = 0; for (auto const& atrack : besttracks) { - if (gConf.cfgUseTrackSel && !isBestTrackSelected(atrack)) { + if (gConf.useTrackSel && !isBestTrackSelected(atrack)) { continue; } auto itrack = atrack.template mfttrack_as(); if (itrack.eta() < trackCuts.minEta || itrack.eta() > trackCuts.maxEta) { continue; } - if (gConf.cfgUseTrackSel && !isTrackSelected(itrack)) { + if (gConf.useTrackSel && !isTrackSelected(itrack)) { continue; } nBestTrks++; @@ -1607,20 +1768,31 @@ struct DndetaMFTPbPb { Partition mcSample = (aod::mcparticle::eta < trackCuts.maxEta) && (aod::mcparticle::eta > trackCuts.minEta); Preslice perColMcFiltTrk = o2::aod::fwdtrack::collisionId; + Preslice perColMcFiltTrkMidrap = aod::track::collisionId; + + std::vector nRecPerCol; + std::vector centRecPerCol; + std::vector occRecPerCol; template void processMc(typename MC::iterator const& mcCollision, soa::SmallGroups> const& collisions, aod::McParticles const& particles, - MftTracksLabeled const& tracks) + MftTracksLabeled const& tracks, + FiltMidrapTracksLabeled const& midtracks) { + nRecPerCol.clear(); + centRecPerCol.clear(); + occRecPerCol.clear(); LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); float occGen = -1.; for (const auto& collision : collisions) { - if (isGoodEvent(collision)) { - float o = getOccupancy(collision, eventCuts.occupancyEstimator); - if (o > occGen) { - occGen = o; + if (!gConf.useEvtSel || isGoodEvent(collision)) { + if (!gConf.rmSplitVertex || (collision.globalIndex() == mcCollision.bestCollisionIndex())) { + float o = getOccupancy(collision, eventCuts.occupancyEstimator); + if (o > occGen) { + occGen = o; + } } } } @@ -1631,10 +1803,12 @@ struct DndetaMFTPbPb { if constexpr (has_reco_cent) { float crecMin = 999.; for (const auto& collision : collisions) { - if (isGoodEvent(collision)) { - float c = getRecoCent(collision); - if (c < crecMin) { - crecMin = c; + if (!gConf.useEvtSel || isGoodEvent(collision)) { + if (!gConf.rmSplitVertex || (collision.globalIndex() == mcCollision.bestCollisionIndex())) { + float c = getRecoCent(collision); + if (c < crecMin) { + crecMin = c; + } } } } @@ -1643,111 +1817,222 @@ struct DndetaMFTPbPb { } } + bool gtZeroColl = false; + bool gtOneRec = false; + int gtOneColl = 0; for (auto const& collision : collisions) { - float occRec = getOccupancy(collision, eventCuts.occupancyEstimator); - float cRec = getRecoCent(collision); - if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/McStatus"), static_cast(McStatus::kMcRecAll), cRec, occRec); - } else { - registryMC.fill(HIST("Events/McStatus"), static_cast(McStatus::kMcRecAll), occRec); - } - if (!isGoodEvent(collision)) { - continue; - } - if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/McStatus"), static_cast(McStatus::kMcRecSel), cRec, occRec); - } else { - registryMC.fill(HIST("Events/McStatus"), static_cast(McStatus::kMcRecSel), occRec); - } - if (!collision.has_mcCollision()) { - continue; - } - if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/McStatus"), static_cast(McStatus::kMcRecHasMcColl), cRec, occRec); - } else { - registryMC.fill(HIST("Events/McStatus"), static_cast(McStatus::kMcRecHasMcColl), occRec); - } - if (gConf.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { - continue; - } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/McStatus"), static_cast(McStatus::kMcRecNoSplitVtx), cRec, occRec); - registryMC.fill(HIST("Events/Centrality/hRecZvtxCent"), collision.posZ(), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecAll), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Events/McStatus"), static_cast(McStatus::kMcRecNoSplitVtx), occRec); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecAll), getOccupancy(collision, eventCuts.occupancyEstimator)); } - - auto nTrkRec = 0; - auto perColSample = tracks.sliceBy(perColMcFiltTrk, collision.globalIndex()); - for (auto const& track : perColSample) { - if (!isTrackSelected(track)) { + if (!gConf.useEvtSel || isGoodEvent(collision)) { + gtZeroColl = true; + ++gtOneColl; + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecSel), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + } else { + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecSel), getOccupancy(collision, eventCuts.occupancyEstimator)); + } + if (!collision.has_mcCollision()) { continue; } - if (track.has_mcParticle() && track.mcMask() == trackCuts.selMcMask) { - const auto& particle = track.template mcParticle_as(); - if (!isChrgParticle(particle.pdgCode())) { - continue; - } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { - continue; - } - if (collision.mcCollisionId() != particle.mcCollisionId()) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecHasMcColl), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + } else { + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecHasMcColl), getOccupancy(collision, eventCuts.occupancyEstimator)); + } + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + continue; + } + gtOneRec = true; + float cRec = getRecoCent(collision); + float occRec = getOccupancy(collision, eventCuts.occupancyEstimator); + centRecPerCol.emplace_back(cRec); + occRecPerCol.emplace_back(occRec); + + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecNoSplitVtx), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hRecZvtxCent"), collision.posZ(), cRec, occRec); + } else { + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecNoSplitVtx), occRec); + } + + // midrapidity tracks for weak deacy correction study + auto midtracksPerCol = midtracks.sliceBy(perColMcFiltTrkMidrap, collision.globalIndex()); + for (auto const& midtrack : midtracksPerCol) { + if (std::abs(midtrack.eta()) >= trackCuts.etaRangeMidrap) { continue; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), track.eta(), collision.posZ(), cRec, occRec); - registryMC.fill(HIST("Tracks/Centrality/PhiEta"), track.phi(), track.eta(), cRec, occRec); - registryMC.fill(HIST("Tracks/Centrality/NclustersEta"), track.nClusters(), track.eta(), cRec, occRec); + registryMC.fill(HIST("TracksMidrap/Centrality/EtaZvtx"), midtrack.eta(), collision.posZ(), midtrack.dcaXY(), midtrack.dcaZ(), static_cast(McRecTrkType::kTrkSel), cRec, occRec); } else { - registryMC.fill(HIST("Tracks/EtaZvtx"), track.eta(), collision.posZ(), occRec); - registryMC.fill(HIST("Tracks/PhiEta"), track.phi(), track.eta(), occRec); - registryMC.fill(HIST("Tracks/NclustersEta"), track.nClusters(), track.eta(), occRec); + registryMC.fill(HIST("TracksMidrap/EtaZvtx"), midtrack.eta(), collision.posZ(), midtrack.dcaXY(), midtrack.dcaZ(), static_cast(McRecTrkType::kTrkSel), occRec); } - ++nTrkRec; - } - } - if (eventCuts.useZDiffCut) { - if (std::abs(collision.posZ() - mcCollision.posZ()) > eventCuts.maxZvtxDiff) { - continue; - } - } - if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/NtrkZvtxGen"), nTrkRec, collision.posZ(), cRec, occRec); - registryMC.fill(HIST("Events/Centrality/ZvtxDiff"), collision.posZ() - mcCollision.posZ(), cRec, occRec); - } else { - registryMC.fill(HIST("Events/NtrkZvtxGen"), nTrkRec, collision.posZ(), occRec); - registryMC.fill(HIST("Events/ZvtxDiff"), collision.posZ() - mcCollision.posZ(), occRec); - } - } + if (midtrack.has_mcParticle() && (!trackCuts.removeFakeByMcMask || (midtrack.mcMask() == trackCuts.selMcMask))) { + const auto& particle = midtrack.template mcParticle_as(); + if (collision.mcCollisionId() != particle.mcCollisionId()) { + continue; + } + bool isPhysicalPrimary = particle.isPhysicalPrimary(); + bool isSecondary = (particle.has_mothers() && particle.getProcess() == TMCProcess::kPDecay); + if (!isPhysicalPrimary && !isSecondary) { + continue; + } + int particleOrigin = -1; + auto particleMother = particle.template mothers_as().front(); + if (particle.isPhysicalPrimary()) { + particleOrigin = static_cast(McRecTrkType::kPrimary); + } else if (particleMother.isPhysicalPrimary() && particle.getProcess() == TMCProcess::kPDecay) { + particleOrigin = static_cast(McRecTrkType::kSecondary); + if (particleMother.pdgCode() == PDG_t::kK0Short || std::abs(particleMother.pdgCode()) == PDG_t::kLambda0) { + particleOrigin = static_cast(McRecTrkType::kStrangeDecay); + } + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("TracksMidrap/Centrality/EtaZvtx"), midtrack.eta(), collision.posZ(), midtrack.dcaXY(), midtrack.dcaZ(), static_cast(particleOrigin), cRec, occRec); + } else { + registryMC.fill(HIST("TracksMidrap/EtaZvtx"), midtrack.eta(), collision.posZ(), midtrack.dcaXY(), midtrack.dcaZ(), static_cast(particleOrigin), occRec); + } + } else { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("TracksMidrap/Centrality/EtaZvtx"), midtrack.eta(), collision.posZ(), midtrack.dcaXY(), midtrack.dcaZ(), static_cast(McRecTrkType::kTrkFake), cRec, occRec); + } else { + registryMC.fill(HIST("TracksMidrap/EtaZvtx"), midtrack.eta(), collision.posZ(), midtrack.dcaXY(), midtrack.dcaZ(), static_cast(McRecTrkType::kTrkFake), occRec); + } + } + } - if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/McStatus"), static_cast(McStatus::kMcGenAll), cGen, occGen); - } else { - registryMC.fill(HIST("Events/McStatus"), static_cast(McStatus::kMcGenAll), occGen); + auto nTrkRec = 0; + auto perColSample = tracks.sliceBy(perColMcFiltTrk, collision.globalIndex()); + for (auto const& track : perColSample) { + if (!isTrackSelected(track)) { + continue; + } + float phi = track.phi(); + o2::math_utils::bringTo02Pi(phi); + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), track.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkSel), cRec, occRec); + } else { + registryMC.fill(HIST("Tracks/EtaZvtx"), track.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkSel), occRec); + } + if (track.has_mcParticle() && (!trackCuts.removeFakeByMcMask || (track.mcMask() == trackCuts.selMcMask))) { + const auto& particle = track.template mcParticle_as(); + if (collision.mcCollisionId() != particle.mcCollisionId()) { + continue; + } + bool isPhysicalPrimary = particle.isPhysicalPrimary(); + bool isSecondary = (particle.has_mothers() && particle.getProcess() == TMCProcess::kPDecay); + if (!isPhysicalPrimary && !isSecondary) { + continue; + } + int particleOrigin = -1; + auto particleMother = particle.template mothers_as().front(); + if (particle.isPhysicalPrimary()) { + particleOrigin = static_cast(McRecTrkType::kPrimary); + } else if (particleMother.isPhysicalPrimary() && particle.getProcess() == TMCProcess::kPDecay) { + particleOrigin = static_cast(McRecTrkType::kSecondary); + if (particleMother.pdgCode() == PDG_t::kK0Short || std::abs(particleMother.pdgCode()) == PDG_t::kLambda0) { + particleOrigin = static_cast(McRecTrkType::kStrangeDecay); + } + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), track.eta(), collision.posZ(), static_cast(particleOrigin), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/PhiEta"), phi, track.eta(), cRec, occRec); + if (gConf.fillTrackQaHist) { + registryMC.fill(HIST("Tracks/Centrality/NclustersEta"), track.nClusters(), track.eta(), cRec, occRec); + } + } else { + registryMC.fill(HIST("Tracks/EtaZvtx"), track.eta(), collision.posZ(), static_cast(particleOrigin), occRec); + registryMC.fill(HIST("Tracks/PhiEta"), phi, track.eta(), occRec); + if (gConf.fillTrackQaHist) { + registryMC.fill(HIST("Tracks/NclustersEta"), track.nClusters(), track.eta(), occRec); + } + } + ++nTrkRec; + } else { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), track.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkFake), cRec, occRec); + } else { + registryMC.fill(HIST("Tracks/EtaZvtx"), track.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkFake), occRec); + } + } + } + nRecPerCol.emplace_back(nTrkRec); + if (eventCuts.useZDiffCut) { + if (std::abs(collision.posZ() - mcCollision.posZ()) > eventCuts.maxZvtxDiff) { + continue; + } + } + if (gConf.fillTrackQaHist) { + for (auto i = 0U; i < nRecPerCol.size(); ++i) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NtrkZvtxRec"), nRecPerCol[i], collision.posZ(), centRecPerCol[i], occRecPerCol[i]); + } else { + registryMC.fill(HIST("Events/NtrkZvtxRec"), nRecPerCol[i], collision.posZ(), occRecPerCol[i]); + } + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/ZvtxDiff"), collision.posZ() - mcCollision.posZ(), cRec, occRec); + } else { + registryMC.fill(HIST("Events/ZvtxDiff"), collision.posZ() - mcCollision.posZ(), occRec); + } + } + } } - - auto nCharged = countPart(particles); if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/NtrkZvtxGen_t"), nCharged, mcCollision.posZ(), cGen, occGen); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcGenAll), cGen, occGen); } else { - registryMC.fill(HIST("Events/NtrkZvtxGen_t"), nCharged, mcCollision.posZ(), occGen); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcGenAll), occGen); } - if (collisions.size() == 0) { + + if (gConf.fillTrackQaHist) { + auto perCollMCsample = mcSample->sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); + auto nchrg = countPart(perCollMCsample); + if (gtOneColl > 1) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NgenZvtxSplit"), nchrg, mcCollision.posZ(), cGen, occGen); + } else { + registryMC.fill(HIST("Events/NgenZvtxSplit"), nchrg, mcCollision.posZ(), occGen); + } + } else { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NgenZvtxNoSplit"), nchrg, mcCollision.posZ(), cGen, occGen); + } else { + registryMC.fill(HIST("Events/NgenZvtxNoSplit"), nchrg, mcCollision.posZ(), occGen); + } + } + auto nCharged = countPart(particles); if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/NotFoundEventZvtx"), mcCollision.posZ(), cGen, occGen); + registryMC.fill(HIST("Events/Centrality/NtrkZvtxGen_t"), nCharged, mcCollision.posZ(), cGen, occGen); } else { - registryMC.fill(HIST("Events/NotFoundEventZvtx"), mcCollision.posZ(), occGen); + registryMC.fill(HIST("Events/NtrkZvtxGen_t"), nCharged, mcCollision.posZ(), occGen); + } + if (gtOneRec) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NtrkZvtxGenRecEvt"), nCharged, mcCollision.posZ(), cGen, occGen); + } else { + registryMC.fill(HIST("Events/NtrkZvtxGenRecEvt"), nCharged, mcCollision.posZ(), occGen); + } + } + if (collisions.size() == 0) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NotFoundEventZvtx"), mcCollision.posZ(), cGen, occGen); + } else { + registryMC.fill(HIST("Events/NotFoundEventZvtx"), mcCollision.posZ(), occGen); + } } } - fillHistMC>(particles, mcCollision.posZ(), cGen, occGen); + fillHistMC>(particles, mcCollision.posZ(), cGen, occGen, gtZeroColl, gtOneRec); } void processMcInclusive(CollsMCExtraMult::iterator const& mccollision, soa::SmallGroups> const& collisions, aod::McParticles const& particles, - MftTracksLabeled const& tracks) + MftTracksLabeled const& tracks, + FiltMidrapTracksLabeled const& midtracks) { - processMc(mccollision, collisions, particles, tracks); + processMc(mccollision, collisions, particles, tracks, midtracks); } PROCESS_SWITCH(DndetaMFTPbPb, processMcInclusive, "Count MC particles (inclusive)", false); @@ -1755,9 +2040,10 @@ struct DndetaMFTPbPb { void processMcCentFT0C(CollsMCExtraMult::iterator const& mccollision, soa::SmallGroups> const& collisions, aod::McParticles const& particles, - MftTracksLabeled const& tracks) + MftTracksLabeled const& tracks, + FiltMidrapTracksLabeled const& midtracks) { - processMc(mccollision, collisions, particles, tracks); + processMc(mccollision, collisions, particles, tracks, midtracks); } PROCESS_SWITCH(DndetaMFTPbPb, processMcCentFT0C, "Count MC particles in FT0C centrality bins", false); @@ -1769,13 +2055,18 @@ struct DndetaMFTPbPb { MftTracksLabeled const& /*tracks*/, MftBestTracksLabeled const& besttracks) { + nRecPerCol.clear(); + centRecPerCol.clear(); + occRecPerCol.clear(); LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); float occGen = -1.; for (const auto& collision : collisions) { - if (isGoodEvent(collision)) { - float o = getOccupancy(collision, eventCuts.occupancyEstimator); - if (o > occGen) { - occGen = o; + if (!gConf.useEvtSel || isGoodEvent(collision)) { + if (!gConf.rmSplitVertex || (collision.globalIndex() == mcCollision.bestCollisionIndex())) { + float o = getOccupancy(collision, eventCuts.occupancyEstimator); + if (o > occGen) { + occGen = o; + } } } } @@ -1786,10 +2077,12 @@ struct DndetaMFTPbPb { if constexpr (has_reco_cent) { float crecMin = 999.; for (const auto& collision : collisions) { - if (isGoodEvent(collision)) { - float c = getRecoCent(collision); - if (c < crecMin) { - crecMin = c; + if (!gConf.useEvtSel || isGoodEvent(collision)) { + if (!gConf.rmSplitVertex || (collision.globalIndex() == mcCollision.bestCollisionIndex())) { + float c = getRecoCent(collision); + if (c < crecMin) { + crecMin = c; + } } } } @@ -1800,158 +2093,217 @@ struct DndetaMFTPbPb { buildLookupTable(collisions); + bool gtZeroColl = false; + bool gtOneRec = false; + int gtOneColl = 0; for (auto const& collision : collisions) { - float occRec = getOccupancy(collision, eventCuts.occupancyEstimator); - float cRec = getRecoCent(collision); - if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/McStatus"), static_cast(McStatus::kMcRecAll), cRec, occRec); - } else { - registryMC.fill(HIST("Events/McStatus"), static_cast(McStatus::kMcRecAll), occRec); - } - if (!isGoodEvent(collision)) { - continue; - } - if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/McStatus"), static_cast(McStatus::kMcRecSel), cRec, occRec); - } else { - registryMC.fill(HIST("Events/McStatus"), static_cast(McStatus::kMcRecSel), occRec); - } - if (!collision.has_mcCollision()) { - continue; - } - if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/McStatus"), static_cast(McStatus::kMcRecHasMcColl), cRec, occRec); - } else { - registryMC.fill(HIST("Events/McStatus"), static_cast(McStatus::kMcRecHasMcColl), occRec); - } - if (gConf.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { - continue; - } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/McStatus"), static_cast(McStatus::kMcRecNoSplitVtx), cRec, occRec); - registryMC.fill(HIST("Events/Centrality/hRecZvtxCent"), collision.posZ(), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecAll), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Events/McStatus"), static_cast(McStatus::kMcRecNoSplitVtx), occRec); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecAll), getOccupancy(collision, eventCuts.occupancyEstimator)); } - - auto nATrk = 0; - auto perCollisionASample = besttracks.sliceBy(perColBestTrks, collision.globalIndex()); - for (auto const& atrack : perCollisionASample) { + if (!gConf.useEvtSel || isGoodEvent(collision)) { + gtZeroColl = true; + ++gtOneColl; if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kBestTrkAll), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecSel), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kBestTrkAll), occRec); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecSel), getOccupancy(collision, eventCuts.occupancyEstimator)); } - if (!isBestTrackSelected(atrack)) { + if (!collision.has_mcCollision()) { continue; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kBesTrktSel), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecHasMcColl), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kBesTrktSel), occRec); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecHasMcColl), getOccupancy(collision, eventCuts.occupancyEstimator)); } - const float bestDcaZ = getDCAz(atrack); - auto itrack = atrack.template mfttrack_as(); - if (!isTrackSelected(itrack)) { + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } + gtOneRec = true; + float cRec = getRecoCent(collision); + float occRec = getOccupancy(collision, eventCuts.occupancyEstimator); + centRecPerCol.emplace_back(cRec); + occRecPerCol.emplace_back(occRec); + if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kTrkAsBestSel), cRec, occRec); - } else { - registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kTrkAsBestSel), occRec); - } - if (!itrack.has_collision()) { - continue; - } - if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kTrkHasColl), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecNoSplitVtx), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hRecZvtxCent"), collision.posZ(), cRec, occRec); } else { - registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kTrkHasColl), occRec); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecNoSplitVtx), occRec); } - if (gConf.cfgRemoveReassigned) { - if (itrack.collisionId() != atrack.bestCollisionId()) { + + auto nATrk = 0; + auto perCollisionASample = besttracks.sliceBy(perColBestTrks, collision.globalIndex()); + for (auto const& atrack : perCollisionASample) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kBestTrkAll), cRec, occRec); + } else { + registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kBestTrkAll), occRec); + } + if (!isBestTrackSelected(atrack)) { continue; } - } - if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kTrkReassignedRemoved), cRec, occRec); - } else { - registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kTrkReassignedRemoved), occRec); - } - if (itrack.collisionId() >= 0 && itrack.has_mcParticle() && itrack.mcMask() == trackCuts.selMcMask) { - const auto& particle = itrack.template mcParticle_as(); - if (!isChrgParticle(particle.pdgCode())) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kBesTrktSel), cRec, occRec); + } else { + registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kBesTrktSel), occRec); + } + const float bestDcaZ = getDCAz(atrack); + auto itrack = atrack.template mfttrack_as(); + if (!isTrackSelected(itrack)) { continue; } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + float phi = itrack.phi(); + o2::math_utils::bringTo02Pi(phi); + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kTrkAsBestSel), cRec, occRec); + } else { + registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kTrkAsBestSel), occRec); + } + if (!itrack.has_collision()) { continue; } - if (eventCuts.useZDiffCut) { - if (std::abs(collision.posZ() - atrack.mcParticle().mcCollision().posZ()) > eventCuts.maxZvtxDiff) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kTrkHasColl), cRec, occRec); + } else { + registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kTrkHasColl), occRec); + } + if (gConf.rmReassigned) { + if (itrack.collisionId() != atrack.bestCollisionId()) { continue; } } - // if (collision.mcCollisionId() != particle.mcCollisionId()) { - // continue; - // } - const int bestRecColl = atrack.bestCollisionId(); - if (!mapMcCollIdPerRecColl.contains(bestRecColl)) { - continue; + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kTrkReassignedRemoved), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(McRecTrkType::kTrkSel), cRec, occRec); + } else { + registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kTrkReassignedRemoved), occRec); + registryMC.fill(HIST("Tracks/EtaZvtx"), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(McRecTrkType::kTrkSel), occRec); } - int64_t mcCollIdRec = mapMcCollIdPerRecColl.find(bestRecColl)->second; - if (mcCollIdRec != particle.mcCollisionId()) { + if (itrack.collisionId() >= 0 && itrack.has_mcParticle() && (!trackCuts.removeFakeByMcMask || (itrack.mcMask() == trackCuts.selMcMask))) { + const auto& particle = itrack.template mcParticle_as(); + if (eventCuts.useZDiffCut) { + if (std::abs(collision.posZ() - atrack.mcParticle().mcCollision().posZ()) > eventCuts.maxZvtxDiff) { + continue; + } + } + if (eventCuts.checkCollisionId) { + if (!mapMcCollIdPerRecColl.contains(atrack.bestCollisionId())) { + continue; + } + if (mapMcCollIdPerRecColl.find(atrack.bestCollisionId())->second != particle.mcCollisionId()) { + continue; + } + } + bool isPhysicalPrimary = particle.isPhysicalPrimary(); + bool isSecondary = (particle.has_mothers() && particle.getProcess() == TMCProcess::kPDecay); + if (!isPhysicalPrimary && !isSecondary) { + continue; + } + int particleOrigin = -1; + auto particleMother = particle.template mothers_as().front(); + if (particle.isPhysicalPrimary()) { + particleOrigin = static_cast(McRecTrkType::kPrimary); + } else if (particleMother.isPhysicalPrimary() && particle.getProcess() == TMCProcess::kPDecay) { + particleOrigin = static_cast(McRecTrkType::kSecondary); + if (particleMother.pdgCode() == PDG_t::kK0Short || std::abs(particleMother.pdgCode()) == PDG_t::kLambda0) { + particleOrigin = static_cast(McRecTrkType::kStrangeDecay); + } + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(particleOrigin), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/PhiEta"), phi, itrack.eta(), cRec, occRec); + if (gConf.fillTrackQaHist) { + registryMC.fill(HIST("Tracks/Centrality/NclustersEta"), itrack.nClusters(), itrack.eta(), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/TrackAmbDegree"), atrack.ambDegree(), cRec, occRec); + } + } else { + registryMC.fill(HIST("Tracks/EtaZvtx"), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(particleOrigin), occRec); + registryMC.fill(HIST("Tracks/PhiEta"), phi, itrack.eta(), occRec); + if (gConf.fillTrackQaHist) { + registryMC.fill(HIST("Tracks/NclustersEta"), itrack.nClusters(), itrack.eta(), occRec); + registryMC.fill(HIST("Tracks/TrackAmbDegree"), atrack.ambDegree(), occRec); + } + } + ++nATrk; + } else { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kTrkFake), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(McRecTrkType::kTrkFake), cRec, occRec); + } else { + registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kTrkFake), occRec); + registryMC.fill(HIST("Tracks/EtaZvtx"), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(McRecTrkType::kTrkFake), occRec); + } + } + } + nRecPerCol.emplace_back(nATrk); + if (eventCuts.useZDiffCut) { + if (std::abs(collision.posZ() - mcCollision.posZ()) > eventCuts.maxZvtxDiff) { continue; } + } + if (gConf.fillTrackQaHist) { + for (auto i = 0U; i < nRecPerCol.size(); ++i) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NtrkZvtxRec"), nRecPerCol[i], collision.posZ(), centRecPerCol[i], occRecPerCol[i]); + } else { + registryMC.fill(HIST("Events/NtrkZvtxRec"), nRecPerCol[i], collision.posZ(), occRecPerCol[i]); + } + } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), itrack.eta(), collision.posZ(), cRec, occRec); - registryMC.fill(HIST("Tracks/Centrality/PhiEta"), itrack.phi(), itrack.eta(), cRec, occRec); - registryMC.fill(HIST("Tracks/Centrality/NclustersEta"), itrack.nClusters(), itrack.eta(), cRec, occRec); - registryMC.fill(HIST("Tracks/Centrality/DCA3d"), itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, cRec, occRec); - registryMC.fill(HIST("Tracks/Centrality/TrackAmbDegree"), atrack.ambDegree(), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/ZvtxDiff"), collision.posZ() - mcCollision.posZ(), cRec, occRec); } else { - registryMC.fill(HIST("Tracks/EtaZvtx"), itrack.eta(), collision.posZ(), occRec); - registryMC.fill(HIST("Tracks/PhiEta"), itrack.phi(), itrack.eta(), occRec); - registryMC.fill(HIST("Tracks/NclustersEta"), itrack.nClusters(), itrack.eta(), occRec); - registryMC.fill(HIST("Tracks/DCA3d"), itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, occRec); - registryMC.fill(HIST("Tracks/TrackAmbDegree"), atrack.ambDegree(), occRec); + registryMC.fill(HIST("Events/ZvtxDiff"), collision.posZ() - mcCollision.posZ(), occRec); } - ++nATrk; - } - } - if (eventCuts.useZDiffCut) { - if (std::abs(collision.posZ() - mcCollision.posZ()) > eventCuts.maxZvtxDiff) { - continue; } } - if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/NtrkZvtxGen"), nATrk, collision.posZ(), cRec, occRec); - registryMC.fill(HIST("Events/Centrality/ZvtxDiff"), collision.posZ() - mcCollision.posZ(), cRec, occRec); - } else { - registryMC.fill(HIST("Events/NtrkZvtxGen"), nATrk, collision.posZ(), occRec); - registryMC.fill(HIST("Events/ZvtxDiff"), collision.posZ() - mcCollision.posZ(), occRec); - } } - if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/McStatus"), static_cast(McStatus::kMcGenAll), cGen, occGen); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcGenAll), cGen, occGen); } else { - registryMC.fill(HIST("Events/McStatus"), static_cast(McStatus::kMcGenAll), occGen); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcGenAll), occGen); } - auto nCharged = countPart(particles); - if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/NtrkZvtxGen_t"), nCharged, mcCollision.posZ(), cGen, occGen); - } else { - registryMC.fill(HIST("Events/NtrkZvtxGen_t"), nCharged, mcCollision.posZ(), occGen); - } - if (collisions.size() == 0) { + if (gConf.fillTrackQaHist) { + auto perCollMCsample = mcSample->sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); + auto nchrg = countPart(perCollMCsample); + if (gtOneColl > 1) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NgenZvtxSplit"), nchrg, mcCollision.posZ(), cGen, occGen); + } else { + registryMC.fill(HIST("Events/NgenZvtxSplit"), nchrg, mcCollision.posZ(), occGen); + } + } else { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NgenZvtxNoSplit"), nchrg, mcCollision.posZ(), cGen, occGen); + } else { + registryMC.fill(HIST("Events/NgenZvtxNoSplit"), nchrg, mcCollision.posZ(), occGen); + } + } + auto nCharged = countPart(particles); if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/NotFoundEventZvtx"), mcCollision.posZ(), cGen, occGen); + registryMC.fill(HIST("Events/Centrality/NtrkZvtxGen_t"), nCharged, mcCollision.posZ(), cGen, occGen); } else { - registryMC.fill(HIST("Events/NotFoundEventZvtx"), mcCollision.posZ(), occGen); + registryMC.fill(HIST("Events/NtrkZvtxGen_t"), nCharged, mcCollision.posZ(), occGen); + } + if (gtOneRec) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NtrkZvtxGenRecEvt"), nCharged, mcCollision.posZ(), cGen, occGen); + } else { + registryMC.fill(HIST("Events/NtrkZvtxGenRecEvt"), nCharged, mcCollision.posZ(), occGen); + } + } + if (collisions.size() == 0) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NotFoundEventZvtx"), mcCollision.posZ(), cGen, occGen); + } else { + registryMC.fill(HIST("Events/NotFoundEventZvtx"), mcCollision.posZ(), occGen); + } } } - fillHistMC>(particles, mcCollision.posZ(), cGen, occGen); + fillHistMC>(particles, mcCollision.posZ(), cGen, occGen, gtZeroColl, gtOneRec); } void processMcBestInclusive(CollsMCExtraMult::iterator const& mccollision, @@ -1987,114 +2339,135 @@ struct DndetaMFTPbPb { auto cRec = CInvalid; auto occRec = CInvalid; bool gtOneRec = false; + float cGen = -1; + if (eventCuts.useGenMult) { + cGen = mcCollision.multMCFT0C(); + } for (const auto& collision : collisions) { if (!isGoodEvent(collision)) { continue; } - if (gConf.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } gtOneRec = true; cRec = getRecoCent(collision); occRec = getOccupancy(collision, eventCuts.occupancyEstimator); } - + if (cGen < 0 && cRec >= 0) { + cGen = cRec; + } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hVtxZGen"), mcCollision.posZ(), static_cast(McGenTrkType::kGenAll), cGen, occRec); if (gtOneRec) { - registryMC.fill(HIST("Events/Centrality/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hVtxZGen"), mcCollision.posZ(), static_cast(McGenTrkType::kGenRecEvt), cGen, occRec); } } else { - registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), occRec); + registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(McGenTrkType::kGenAll), occRec); if (gtOneRec) { - registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), occRec); + registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(McGenTrkType::kGenRecEvt), occRec); } } for (const auto& particle : particles) { if (!isChrgParticle(particle.pdgCode())) { continue; } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + if (gConf.useParticleSel && !isParticleSelected(particle)) { continue; } if constexpr (has_reco_cent) { if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.pt(), particle.phi(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenAll), cGen, occRec); if (gtOneRec) { - registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.pt(), particle.phi(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenRecEvt), cGen, occRec); } } } else { if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - registryMC.fill(HIST("Tracks/hEffGen"), particle.pt(), particle.phi(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), occRec); + registryMC.fill(HIST("Tracks/hEffGen"), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenAll), occRec); if (gtOneRec) { - registryMC.fill(HIST("Tracks/hEffGen"), particle.pt(), particle.phi(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), occRec); + registryMC.fill(HIST("Tracks/hEffGen"), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenRecEvt), occRec); } } } } for (const auto& collision : collisions) { if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffAll), getRecoCent(collision), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffAll), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffAll), occRec); + registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffAll), getRecoCent(collision)); } - if (!isGoodEvent(collision)) { + if (gConf.useEvtSel && !isGoodEvent(collision)) { continue; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffSel), getRecoCent(collision), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffSel), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffSel), occRec); + registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffSel), getRecoCent(collision)); } if (!collision.has_mcCollision()) { continue; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffHasMcColl), getRecoCent(collision), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffHasMcColl), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffHasMcColl), occRec); + registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffHasMcColl), getRecoCent(collision)); } - if (gConf.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffNoSplitVtx), getRecoCent(collision), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffNoSplitVtx), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffNoSplitVtx), occRec); + registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffNoSplitVtx), getRecoCent(collision)); } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hVtxZRec"), collision.posZ(), getRecoCent(collision), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hVtxZRec"), collision.posZ(), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Events/hVtxZRec"), collision.posZ(), occRec); + registryMC.fill(HIST("Events/hVtxZRec"), collision.posZ(), getRecoCent(collision)); } auto perColTrks = tracks.sliceBy(perColMcFiltTrk, collision.globalIndex()); for (auto const& track : perColTrks) { if (!isTrackSelected(track)) { continue; } - if (track.has_mcParticle() && track.mcMask() == trackCuts.selMcMask) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hEffRec"), track.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkSel), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + } else { + registryMC.fill(HIST("Tracks/hEffRec"), track.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkSel), getOccupancy(collision, eventCuts.occupancyEstimator)); + } + if (track.has_mcParticle() && (!trackCuts.removeFakeByMcMask || (track.mcMask() == trackCuts.selMcMask))) { const auto& particle = track.template mcParticle_as(); - if (!isChrgParticle(particle.pdgCode())) { + if (collision.mcCollisionId() != particle.mcCollisionId()) { continue; } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + bool isPhysicalPrimary = particle.isPhysicalPrimary(); + bool isSecondary = (particle.has_mothers() && particle.getProcess() == TMCProcess::kPDecay); + if (!isPhysicalPrimary && !isSecondary) { continue; } - if (collision.mcCollisionId() != particle.mcCollisionId()) { - continue; + int particleOrigin = -1; + auto particleMother = particle.template mothers_as().front(); + if (particle.isPhysicalPrimary()) { + particleOrigin = static_cast(McRecTrkType::kPrimary); + } else if (particleMother.isPhysicalPrimary() && particle.getProcess() == TMCProcess::kPDecay) { + particleOrigin = static_cast(McRecTrkType::kSecondary); + if (particleMother.pdgCode() == PDG_t::kK0Short || std::abs(particleMother.pdgCode()) == PDG_t::kLambda0) { + particleOrigin = static_cast(McRecTrkType::kStrangeDecay); + } } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/hEffRec"), particle.pt(), particle.phi(), particle.eta(), collision.posZ(), static_cast(RecTrkType::kRecAll), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/Centrality/hEffRec"), particle.eta(), collision.posZ(), static_cast(particleOrigin), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/Centrality/hEffRecClosure"), particle.eta(), collision.posZ(), static_cast(particleOrigin), cGen, getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Tracks/hEffRec"), particle.pt(), particle.phi(), particle.eta(), collision.posZ(), static_cast(RecTrkType::kRecAll), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/hEffRec"), particle.eta(), collision.posZ(), static_cast(particleOrigin), getOccupancy(collision, eventCuts.occupancyEstimator)); registryMC.fill(HIST("Tracks/hEtaRes"), particle.eta(), (track.eta() - particle.eta()) / particle.eta(), getOccupancy(collision, eventCuts.occupancyEstimator)); } } else { if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/hEffRec"), track.pt(), track.phi(), track.eta(), collision.posZ(), static_cast(RecTrkType::kRecFake), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/Centrality/hEffRec"), track.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkFake), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Tracks/hEffRec"), track.pt(), track.phi(), track.eta(), collision.posZ(), static_cast(RecTrkType::kRecFake), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/hEffRec"), track.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkFake), getOccupancy(collision, eventCuts.occupancyEstimator)); } } } @@ -2123,10 +2496,10 @@ struct DndetaMFTPbPb { /// @brief process function to calculate tracking efficiency based on BestCollisionsFwd3d in FT0C bins template - void processEfficiencyBest(typename MC::iterator const& mcCollision, - soa::SmallGroups> const& collisions, - aod::McParticles const& particles, - MftBestTracksLabeled const& besttracks) + void processMcEfficiencyBest(typename MC::iterator const& mcCollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& particles, + MftBestTracksLabeled const& besttracks) { LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); @@ -2135,92 +2508,99 @@ struct DndetaMFTPbPb { auto cRec = CInvalid; auto occRec = CInvalid; bool gtOneRec = false; + float cGen = -1; + if (eventCuts.useGenMult) { + cGen = mcCollision.multMCFT0C(); + } for (const auto& collision : collisions) { if (!isGoodEvent(collision)) { continue; } - if (gConf.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } gtOneRec = true; cRec = getRecoCent(collision); occRec = getOccupancy(collision, eventCuts.occupancyEstimator); } + if (cGen < 0 && cRec >= 0) { + cGen = cRec; + } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hVtxZGen"), mcCollision.posZ(), static_cast(McGenTrkType::kGenAll), cGen, occRec); if (gtOneRec) { - registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hVtxZGen"), mcCollision.posZ(), static_cast(McGenTrkType::kGenRecEvt), cGen, occRec); } } else { - registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), occRec); + registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(McGenTrkType::kGenAll), occRec); if (gtOneRec) { - registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), occRec); + registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(McGenTrkType::kGenRecEvt), occRec); } } for (const auto& particle : particles) { if (!isChrgParticle(particle.pdgCode())) { continue; } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + if (gConf.useParticleSel && !isParticleSelected(particle)) { continue; } if constexpr (has_reco_cent) { if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.pt(), particle.phi(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenAll), cGen, occRec); if (gtOneRec) { - registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.pt(), particle.phi(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenRecEvt), cGen, occRec); } } } else { if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - registryMC.fill(HIST("Tracks/hEffGen"), particle.pt(), particle.phi(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), occRec); + registryMC.fill(HIST("Tracks/hEffGen"), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenAll), occRec); if (gtOneRec) { - registryMC.fill(HIST("Tracks/hEffGen"), particle.pt(), particle.phi(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), occRec); + registryMC.fill(HIST("Tracks/hEffGen"), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenRecEvt), occRec); } } } } for (const auto& collision : collisions) { if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffAll), getRecoCent(collision), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffAll), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffAll), occRec); + registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffAll), getOccupancy(collision, eventCuts.occupancyEstimator)); } - if (!isGoodEvent(collision)) { + if (gConf.useEvtSel && !isGoodEvent(collision)) { continue; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffSel), getRecoCent(collision), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffSel), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffSel), occRec); + registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffSel), getOccupancy(collision, eventCuts.occupancyEstimator)); } if (!collision.has_mcCollision()) { continue; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffHasMcColl), getRecoCent(collision), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffHasMcColl), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffHasMcColl), occRec); + registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffHasMcColl), getOccupancy(collision, eventCuts.occupancyEstimator)); } - if (gConf.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffNoSplitVtx), getRecoCent(collision), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffNoSplitVtx), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffNoSplitVtx), occRec); + registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffNoSplitVtx), getOccupancy(collision, eventCuts.occupancyEstimator)); } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hVtxZRec"), collision.posZ(), getRecoCent(collision), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hVtxZRec"), collision.posZ(), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Events/hVtxZRec"), collision.posZ(), occRec); + registryMC.fill(HIST("Events/hVtxZRec"), collision.posZ(), getOccupancy(collision, eventCuts.occupancyEstimator)); } - auto perCollisionASample = besttracks.sliceBy(perColBestTrks, collision.globalIndex()); for (auto const& atrack : perCollisionASample) { if (!isBestTrackSelected(atrack)) { continue; } + const float bestDcaZ = getDCAz(atrack); auto itrack = atrack.template mfttrack_as(); if (!isTrackSelected(itrack)) { continue; @@ -2228,43 +2608,58 @@ struct DndetaMFTPbPb { if (!itrack.has_collision()) { continue; } - if (gConf.cfgRemoveReassigned) { + if (gConf.rmReassigned) { if (itrack.collisionId() != atrack.bestCollisionId()) { continue; } } - if (itrack.collisionId() >= 0 && itrack.has_mcParticle() && itrack.mcMask() == trackCuts.selMcMask) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hEffRec"), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(McRecTrkType::kTrkSel), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + } else { + registryMC.fill(HIST("Tracks/hEffRec"), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(McRecTrkType::kTrkSel), getOccupancy(collision, eventCuts.occupancyEstimator)); + } + if (itrack.collisionId() >= 0 && itrack.has_mcParticle() && (!trackCuts.removeFakeByMcMask || (itrack.mcMask() == trackCuts.selMcMask))) { const auto& particle = itrack.template mcParticle_as(); - if (!isChrgParticle(particle.pdgCode())) { - continue; - } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { - continue; - } if (eventCuts.useZDiffCut) { if (std::abs(collision.posZ() - atrack.mcParticle().mcCollision().posZ()) > eventCuts.maxZvtxDiff) { continue; } } - const int bestRecColl = atrack.bestCollisionId(); - if (!mapMcCollIdPerRecColl.contains(bestRecColl)) { - continue; + if (eventCuts.checkCollisionId) { + if (!mapMcCollIdPerRecColl.contains(atrack.bestCollisionId())) { + continue; + } + if (mapMcCollIdPerRecColl.find(atrack.bestCollisionId())->second != particle.mcCollisionId()) { + continue; + } } - int64_t mcCollIdRec = mapMcCollIdPerRecColl.find(bestRecColl)->second; - if (mcCollIdRec != particle.mcCollisionId()) { + bool isPhysicalPrimary = particle.isPhysicalPrimary(); + bool isSecondary = (particle.has_mothers() && particle.getProcess() == TMCProcess::kPDecay); + if (!isPhysicalPrimary && !isSecondary) { continue; } + int particleOrigin = -1; + auto particleMother = particle.template mothers_as().front(); + if (particle.isPhysicalPrimary()) { + particleOrigin = static_cast(McRecTrkType::kPrimary); + } else if (particleMother.isPhysicalPrimary() && particle.getProcess() == TMCProcess::kPDecay) { + particleOrigin = static_cast(McRecTrkType::kSecondary); + if (particleMother.pdgCode() == PDG_t::kK0Short || std::abs(particleMother.pdgCode()) == PDG_t::kLambda0) { + particleOrigin = static_cast(McRecTrkType::kStrangeDecay); + } + } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/hEffRec"), particle.pt(), particle.phi(), particle.eta(), collision.posZ(), static_cast(RecTrkType::kRecAll), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/Centrality/hEffRec"), particle.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(particleOrigin), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/Centrality/hEffRecClosure"), particle.eta(), collision.posZ(), static_cast(particleOrigin), cGen, getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Tracks/hEffRec"), particle.pt(), particle.phi(), particle.eta(), collision.posZ(), static_cast(RecTrkType::kRecAll), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/hEffRec"), particle.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(particleOrigin), getOccupancy(collision, eventCuts.occupancyEstimator)); registryMC.fill(HIST("Tracks/hEtaRes"), particle.eta(), (itrack.eta() - particle.eta()) / particle.eta(), getOccupancy(collision, eventCuts.occupancyEstimator)); } } else { if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/hEffRec"), itrack.pt(), itrack.phi(), itrack.eta(), collision.posZ(), static_cast(RecTrkType::kRecFake), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/Centrality/hEffRec"), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(McRecTrkType::kTrkFake), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Tracks/hEffRec"), itrack.pt(), itrack.phi(), itrack.eta(), collision.posZ(), static_cast(RecTrkType::kRecFake), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/hEffRec"), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(McRecTrkType::kTrkFake), getOccupancy(collision, eventCuts.occupancyEstimator)); } } } @@ -2276,7 +2671,7 @@ struct DndetaMFTPbPb { aod::McParticles const& particles, MftBestTracksLabeled const& besttracks) { - processEfficiencyBest(mccollision, collisions, particles, besttracks); + processMcEfficiencyBest(mccollision, collisions, particles, besttracks); } PROCESS_SWITCH(DndetaMFTPbPb, processMcEfficiencyBestInclusive, "Process tracking efficiency (inclusive, based on BestCollisionsFwd3d)", false); @@ -2286,38 +2681,43 @@ struct DndetaMFTPbPb { aod::McParticles const& particles, MftBestTracksLabeled const& besttracks) { - processEfficiencyBest(mccollision, collisions, particles, besttracks); + processMcEfficiencyBest(mccollision, collisions, particles, besttracks); } PROCESS_SWITCH(DndetaMFTPbPb, processMcEfficiencyBestCentFT0C, "Process tracking efficiency (in FT0 centrality bins, based on BestCollisionsFwd3d)", false); - // Partition primariesIdx = ifnode(nsqrt(aod::mcparticle::vx * aod::mcparticle::vx + aod::mcparticle::vy * aod::mcparticle::vy) > 5.f, false, ncheckbit(aod::mcparticle_v2::storedFlags, (uint8_t)o2::aod::mcparticle::enums::PhysicalPrimary)) && (aod::mcparticle::eta < trackCuts.maxEta) && (aod::mcparticle::eta > trackCuts.minEta); - /// @brief process template function to calculate tracking efficiency (indexed as particle-to-MFT-tracks) template void processEfficiencyIdx(typename MC::iterator const& mcCollision, soa::SmallGroups> const& collisions, - FiltParticlesIdx const& particles, + ParticlesIdx const& particles, MftTracksLabeled const& /*tracks*/) { LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); auto cRec = CInvalid; auto occRec = CInvalid; + float cGen = -1; + if (eventCuts.useGenMult) { + cGen = mcCollision.multMCFT0C(); + } for (const auto& collision : collisions) { if (!isGoodEvent(collision)) { continue; } - if (gConf.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } cRec = getRecoCent(collision); occRec = getOccupancy(collision, eventCuts.occupancyEstimator); } + if (cGen < 0 && cRec >= 0) { + cGen = cRec; + } for (const auto& collision : collisions) { - if (!isGoodEvent(collision)) { + if (gConf.useEvtSel && !isGoodEvent(collision)) { continue; } - if (gConf.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } auto partsPerCol = particles.sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); @@ -2325,19 +2725,22 @@ struct DndetaMFTPbPb { if (!isChrgParticle(particle.pdgCode())) { continue; } + if (gConf.useParticleSel && !isParticleSelected(particle)) { + continue; + } if (collision.mcCollisionId() != particle.mcCollisionId()) { continue; } // MC gen if constexpr (has_reco_cent) { - if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta && cutInPhi(particle)) { - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { - registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxAll), cRec, occRec); + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxAll), cGen, occRec); } } } else { - if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta && cutInPhi(particle)) { - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { registryMC.fill(HIST("Tracks/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxAll), occRec); } } @@ -2348,32 +2751,32 @@ struct DndetaMFTPbPb { auto ncnt = 0; auto relatedTracks = particle.template mfttracks_as(); for (auto const& track : relatedTracks) { - if (!isTrackSelected(track)) { + if (!isTrackSelected(track)) { continue; } - if (track.mcMask() != trackCuts.selMcMask) { + if (trackCuts.removeFakeByMcMask && (track.mcMask() != trackCuts.selMcMask)) { continue; } ++ncnt; if constexpr (has_reco_cent) { if (track.eta() > trackCuts.minEta && track.eta() < trackCuts.maxEta) { if (!iscounted) { // primaries - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { - registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxPrim), cRec, occRec); + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxPrim), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } iscounted = true; } } - registryMC.fill(HIST("Tracks/Centrality/NmftTrkPerPart"), ncnt, cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/NmftTrkPerPart"), ncnt, getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); if (ncnt > 1) { // secondaries if (track.eta() > trackCuts.minEta && track.eta() < trackCuts.maxEta) { - registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxSec), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxSec), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } } } else { if (track.eta() > trackCuts.minEta && track.eta() < trackCuts.maxEta) { if (!iscounted) { // primaries - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { registryMC.fill(HIST("Tracks/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxPrim), occRec); } iscounted = true; @@ -2389,9 +2792,9 @@ struct DndetaMFTPbPb { } if (relatedTracks.size() > 1) { // duplicates if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxDupl), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxDupl), cGen, occRec); for (auto const& track : relatedTracks) { - registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), track.pt(), track.eta(), static_cast(RecIdxTrkType::kRecIdxDupl), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), track.pt(), track.eta(), static_cast(RecIdxTrkType::kRecIdxDupl), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } } else { registryMC.fill(HIST("Tracks/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxDupl), occRec); @@ -2404,13 +2807,13 @@ struct DndetaMFTPbPb { // MC FAKES if constexpr (has_reco_cent) { if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { - registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxFake), cRec, occRec); + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxFake), cGen, occRec); } } } else { if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { registryMC.fill(HIST("Tracks/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxFake), occRec); } } @@ -2422,7 +2825,7 @@ struct DndetaMFTPbPb { void processMcEfficiencyIdxInlusive(CollsMCExtraMult::iterator const& mccollision, soa::SmallGroups> const& collisions, - FiltParticlesIdx const& particles, + ParticlesIdx const& particles, MftTracksLabeled const& tracks) { processEfficiencyIdx(mccollision, collisions, particles, tracks); @@ -2432,7 +2835,7 @@ struct DndetaMFTPbPb { void processMcEfficiencyIdxCentFT0C(CollsMCExtraMult::iterator const& mccollision, soa::SmallGroups> const& collisions, - FiltParticlesIdx const& particles, + ParticlesIdx const& particles, MftTracksLabeled const& tracks) { processEfficiencyIdx(mccollision, collisions, particles, tracks); @@ -2443,30 +2846,37 @@ struct DndetaMFTPbPb { template void processEfficiencyIdxBest(typename MC::iterator const& mcCollision, soa::SmallGroups> const& collisions, - FiltParticlesIdx const& particles, + ParticlesIdx const& particles, MftBestTracksLabeled const& /*atracks*/) { LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); auto cRec = CInvalid; auto occRec = CInvalid; + float cGen = -1; + if (eventCuts.useGenMult) { + cGen = mcCollision.multMCFT0C(); + } for (const auto& collision : collisions) { if (!isGoodEvent(collision)) { continue; } - if (gConf.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } cRec = getRecoCent(collision); occRec = getOccupancy(collision, eventCuts.occupancyEstimator); } + if (cGen < 0 && cRec >= 0) { + cGen = cRec; + } buildLookupTable(collisions); for (const auto& collision : collisions) { - if (!isGoodEvent(collision)) { + if (gConf.useEvtSel && !isGoodEvent(collision)) { continue; } - if (gConf.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } auto partsPerCol = particles.sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); @@ -2474,19 +2884,22 @@ struct DndetaMFTPbPb { if (!isChrgParticle(particle.pdgCode())) { continue; } + if (gConf.useParticleSel && !isParticleSelected(particle)) { + continue; + } if (collision.mcCollisionId() != particle.mcCollisionId()) { continue; } // MC gen if constexpr (has_reco_cent) { - if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta && cutInPhi(particle)) { - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { - registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxAll), cRec, occRec); + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxAll), cGen, occRec); } } } else { - if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta && cutInPhi(particle)) { - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { registryMC.fill(HIST("Tracks/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxAll), occRec); } } @@ -2497,37 +2910,40 @@ struct DndetaMFTPbPb { auto ncnt = 0; auto relatedTracks = particle.template mfttracks_as(); for (auto const& atrack : relatedTracks) { - if (!isBestTrackSelected(atrack)) { + if (!isBestTrackSelected(atrack)) { continue; } - const int bestRecColl = atrack.bestCollisionId(); - if (!mapMcCollIdPerRecColl.contains(bestRecColl)) { + if (trackCuts.removeFakeByMcMask && (atrack.mcMask() != trackCuts.selMcMask)) { continue; } - int64_t mcCollIdRec = mapMcCollIdPerRecColl.find(bestRecColl)->second; - if (mcCollIdRec != particle.mcCollisionId()) { - continue; + if (eventCuts.checkCollisionId) { + if (!mapMcCollIdPerRecColl.contains(atrack.bestCollisionId())) { + continue; + } + if (mapMcCollIdPerRecColl.find(atrack.bestCollisionId())->second != particle.mcCollisionId()) { + continue; + } } ++ncnt; if constexpr (has_reco_cent) { if (atrack.eta() > trackCuts.minEta && atrack.eta() < trackCuts.maxEta) { if (!iscounted) { // primaries - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { - registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxPrim), cRec, occRec); + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxPrim), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } iscounted = true; } } - registryMC.fill(HIST("Tracks/Centrality/NmftTrkPerPart"), ncnt, cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/NmftTrkPerPart"), ncnt, getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); if (ncnt > 1) { // secondaries if (atrack.eta() > trackCuts.minEta && atrack.eta() < trackCuts.maxEta) { - registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxSec), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxSec), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } } } else { if (atrack.eta() > trackCuts.minEta && atrack.eta() < trackCuts.maxEta) { if (!iscounted) { // primaries - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { registryMC.fill(HIST("Tracks/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxPrim), occRec); } iscounted = true; @@ -2543,9 +2959,9 @@ struct DndetaMFTPbPb { } if (relatedTracks.size() > 1) { // duplicates if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxDupl), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxDupl), cGen, occRec); for (auto const& track : relatedTracks) { - registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), track.pt(), track.eta(), static_cast(RecIdxTrkType::kRecIdxDupl), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), track.pt(), track.eta(), static_cast(RecIdxTrkType::kRecIdxDupl), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } } else { registryMC.fill(HIST("Tracks/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxDupl), occRec); @@ -2558,13 +2974,13 @@ struct DndetaMFTPbPb { // MC FAKES if constexpr (has_reco_cent) { if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { - registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxFake), cRec, occRec); + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxFake), cGen, occRec); } } } else { if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { registryMC.fill(HIST("Tracks/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxFake), occRec); } } @@ -2576,7 +2992,7 @@ struct DndetaMFTPbPb { void processMcEfficiencyIdxBestInlusive(CollsMCExtraMult::iterator const& mccollision, soa::SmallGroups> const& collisions, - FiltParticlesIdx const& particles, + ParticlesIdx const& particles, MftBestTracksLabeled const& atracks) { processEfficiencyIdxBest(mccollision, collisions, particles, atracks); @@ -2586,7 +3002,7 @@ struct DndetaMFTPbPb { void processMcEfficiencyIdxBestCentFT0C(CollsMCExtraMult::iterator const& mccollision, soa::SmallGroups> const& collisions, - FiltParticlesIdx const& particles, + ParticlesIdx const& particles, MftBestTracksLabeled const& atracks) { processEfficiencyIdxBest(mccollision, collisions, particles, atracks); @@ -2594,83 +3010,241 @@ struct DndetaMFTPbPb { PROCESS_SWITCH(DndetaMFTPbPb, processMcEfficiencyIdxBestCentFT0C, "Process tracking efficiency best (in FT0C centrality bins, indexed)", false); - /// @brief process function to calculate signal loss based on MC + template + void processEfficiencyIdxBestLight(typename MC::iterator const& mcCollision, + soa::SmallGroups> const& collisions, + ParticlesIdx const& particles, + MftTracksLabeled const& /*tracks*/, + MftBestTracksLabeled const& /*atracks*/) + { + LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); + auto cRec = CInvalid; + float cGen = -1; + if (eventCuts.useGenMult) { + cGen = mcCollision.multMCFT0C(); + } + for (const auto& collision : collisions) { + if (!isGoodEvent(collision)) { + continue; + } + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + continue; + } + cRec = getRecoCent(collision); + } + if (cGen < 0 && cRec >= 0) { + cGen = cRec; + } + + buildLookupTable(collisions); + + for (const auto& collision : collisions) { + if (gConf.useEvtSel && !isGoodEvent(collision)) { + continue; + } + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + continue; + } + auto partsPerCol = particles.sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); + for (auto const& particle : partsPerCol) { + if (!isChrgParticle(particle.pdgCode())) { + continue; + } + if (gConf.useParticleSel && !isParticleSelected(particle)) { + continue; + } + if (collision.mcCollisionId() != particle.mcCollisionId()) { + continue; + } + // MC gen + if constexpr (has_reco_cent) { + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkLightType::kGenIdxAll), cGen); + } + } + } else { + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkLightType::kGenIdxAll)); + } + } + } + // MC rec + if (particle.has_mfttracks()) { + auto iscounted = false; + auto ncnt = 0; + auto relatedTracks = particle.template mfttracks_as(); + for (auto const& atrack : relatedTracks) { + if (!isBestTrackSelected(atrack)) { + continue; + } + if (eventCuts.checkCollisionId) { + if (!mapMcCollIdPerRecColl.contains(atrack.bestCollisionId())) { + continue; + } + if (mapMcCollIdPerRecColl.find(atrack.bestCollisionId())->second != particle.mcCollisionId()) { + continue; + } + } + const float bestDcaZ = getDCAz(atrack); + auto itrack = atrack.template mfttrack_as(); + if (!isTrackSelected(itrack)) { + continue; + } + if (!itrack.has_collision()) { + continue; + } + if (gConf.rmReassigned) { + if (itrack.collisionId() != atrack.bestCollisionId()) { + continue; + } + } + if (itrack.collisionId() >= 0 && itrack.has_mcParticle() && (!trackCuts.removeFakeByMcMask || (itrack.mcMask() == trackCuts.selMcMask))) { + ++ncnt; + if constexpr (has_reco_cent) { + if (atrack.eta() > trackCuts.minEta && atrack.eta() < trackCuts.maxEta) { + if (!iscounted) { // primaries + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), particle.pt(), particle.eta(), atrack.bestDCAXY(), bestDcaZ, static_cast(RecIdxTrkLightType::kRecIdxPrim), cRec); + } + iscounted = true; + } + } + registryMC.fill(HIST("Tracks/Centrality/NmftTrkPerPart"), ncnt, cRec); + if (ncnt > 1) { // secondaries + if (atrack.eta() > trackCuts.minEta && atrack.eta() < trackCuts.maxEta) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), particle.pt(), particle.eta(), atrack.bestDCAXY(), bestDcaZ, static_cast(RecIdxTrkLightType::kRecIdxSec), cRec); + } + } + } else { + if (atrack.eta() > trackCuts.minEta && atrack.eta() < trackCuts.maxEta) { + if (!iscounted) { // primaries + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/hEffIdxRec"), particle.pt(), particle.eta(), atrack.bestDCAXY(), bestDcaZ, static_cast(RecIdxTrkLightType::kRecIdxPrim)); + } + iscounted = true; + } + } + registryMC.fill(HIST("Tracks/NmftTrkPerPart"), ncnt); + if (ncnt > 1) { // secondaries + if (atrack.eta() > trackCuts.minEta && atrack.eta() < trackCuts.maxEta) { + registryMC.fill(HIST("Tracks/hEffIdxRec"), particle.pt(), particle.eta(), atrack.bestDCAXY(), bestDcaZ, static_cast(RecIdxTrkLightType::kRecIdxSec)); + } + } + } + } + } + } + } + } + } + + void processMcEfficiencyIdxBestLightInlusive(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + ParticlesIdx const& particles, + MftTracksLabeled const& tracks, + MftBestTracksLabeled const& atracks) + { + processEfficiencyIdxBestLight(mccollision, collisions, particles, tracks, atracks); + } + + PROCESS_SWITCH(DndetaMFTPbPb, processMcEfficiencyIdxBestLightInlusive, "Process tracking efficiency best light (inclusive, indexed)", false); + + void processMcEfficiencyIdxBestLightCentFT0C(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + ParticlesIdx const& particles, + MftTracksLabeled const& tracks, + MftBestTracksLabeled const& atracks) + { + processEfficiencyIdxBestLight(mccollision, collisions, particles, tracks, atracks); + } + + PROCESS_SWITCH(DndetaMFTPbPb, processMcEfficiencyIdxBestLightCentFT0C, "Process tracking efficiency best light (in FT0C centrality bins, indexed)", false); + void processMcSgnEvtLossCentFT0C(CollsMCExtraMult::iterator const& mcCollision, - soa::SmallGroups> const& collisions, + soa::SmallGroups const& collisions, aod::McParticles const& particles) { LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); - registryMC.fill(HIST("Events/hNchGen"), mcCollision.multMCFT0C(), static_cast(EvtLossType::kGenAll)); - registryMC.fill(HIST("Events/hEvtMcGen"), 0.5); + float multMC{}; + auto perCollMCsample = mcSample->sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); + auto multMCNParticlesEtaMFT = countPart(perCollMCsample); + if (gConf.useMultMCatMidRapidity) { + multMC = mcCollision.multMCNParticlesEta08(); + } else if (gConf.useMultMCatMFTacc) { + multMC = multMCNParticlesEtaMFT; + } else { + multMC = getMultMC(mcCollision); + } + registryMC.fill(HIST("Events/hNchGen"), multMC, static_cast(EvtLossType::kGenAll)); + registryMC.fill(HIST("Events/hMcEvtLossStatus"), static_cast(McEvtLossStatus::kMcEvtAll)); if (eventCuts.useZVtxCutMC && (std::abs(mcCollision.posZ()) >= eventCuts.maxZvtx)) { return; } - registryMC.fill(HIST("Events/hEvtMcGen"), 1.5); - // At least one generated primary in MFT acceptance + TVX triggered collisions - if (gConf.cfgUseInelgt0wMFT && !isInelGt0wMft(particles)) { + registryMC.fill(HIST("Events/hMcEvtLossStatus"), static_cast(McEvtLossStatus::kMcEvtVtxZ)); + if (eventCuts.requireTVX && !(mcCollision.multMCFT0C() > 0 && mcCollision.multMCFT0A() > 0)) { + return; + } + registryMC.fill(HIST("Events/hMcEvtLossStatus"), static_cast(McEvtLossStatus::kMcEvtTVX)); + if (gConf.useInelgt0 && !mcCollision.isInelGt0()) { return; } - registryMC.fill(HIST("Events/hEvtMcGen"), 2.5); - if (eventCuts.useInelgt0wTVX && !(mcCollision.multMCFT0C() > 0 && mcCollision.multMCFT0A() > 0)) { + registryMC.fill(HIST("Events/hMcEvtLossStatus"), static_cast(McEvtLossStatus::kMcEvtInelGt0)); + if (gConf.useInelgt0wMFT && !isInelGt0wMft(particles)) { return; } - registryMC.fill(HIST("Events/hEvtMcGen"), 3.5); - registryMC.fill(HIST("Events/hNchGen"), mcCollision.multMCFT0C(), static_cast(EvtLossType::kGenSel)); // Evt loss den + registryMC.fill(HIST("Events/hMcEvtLossStatus"), static_cast(McEvtLossStatus::kMcEvtInelGt0wMft)); + registryMC.fill(HIST("Events/hNchGen"), multMC, static_cast(EvtLossType::kGenSel)); // Evt loss den bool gtZeroColl = false; - auto maxNcontributors = -1; float cRec = CInvalid; for (auto const& collision : collisions) { - if (!isGoodEvent(collision)) { + if (gConf.useEvtSel && !isGoodEvent(collision)) { continue; } - if (std::abs(collision.posZ()) >= eventCuts.maxZvtx) { + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } - registryMC.fill(HIST("Events/hNchGen"), mcCollision.multMCFT0C(), static_cast(EvtLossType::kGenSplit)); - registryMC.fill(HIST("Events/hMultGenVsCentSplit"), getRecoCent(collision), mcCollision.multMCFT0C()); - if (maxNcontributors < collision.numContrib()) { - maxNcontributors = collision.numContrib(); - cRec = getRecoCent(collision); - } + registryMC.fill(HIST("Events/hNchGen"), multMC, static_cast(EvtLossType::kGenSplit)); + registryMC.fill(HIST("Events/hMultGenVsCentSplit"), getRecoCent(collision), multMC); + cRec = getRecoCent(collision); gtZeroColl = true; } - auto perCollMCsample = mcSample->sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); - auto multMCNParticlesEtaMFT = countPart(perCollMCsample); - - registryMC.fill(HIST("Events/EvtSigLossStatus"), 1., cRec); // Evt split den - registryMC.fill(HIST("Events/hMultGenVsCent"), cRec, mcCollision.multMCFT0C()); - registryMC.fill(HIST("Events/hMultGenVsCentNParticlesEta05"), cRec, mcCollision.multMCNParticlesEta05()); - registryMC.fill(HIST("Events/hMultGenVsCentNParticlesEtaMFT"), cRec, multMCNParticlesEtaMFT); + registryMC.fill(HIST("Events/Centrality/hEvtSigLossStatus"), 1., cRec); // Evt split den + registryMC.fill(HIST("Events/hMultGenVsCent"), multMC, getMultMC(mcCollision), cRec); + registryMC.fill(HIST("Events/hMultGenVsCentMFTacc"), multMCNParticlesEtaMFT, cRec); if (gtZeroColl) { - registryMC.fill(HIST("Events/hNchGen"), mcCollision.multMCFT0C(), static_cast(EvtLossType::kGenRecEvt)); // Evt loss num - registryMC.fill(HIST("Events/EvtSigLossStatus"), 2., cRec); // Evt split num - registryMC.fill(HIST("Events/hMultGenVsCentRec"), cRec, mcCollision.multMCFT0C()); - registryMC.fill(HIST("Events/hMultGenVsCentRecNParticlesEta05"), cRec, mcCollision.multMCNParticlesEta05()); - registryMC.fill(HIST("Events/hMultGenVsCentRecNParticlesEtaMFT"), cRec, multMCNParticlesEtaMFT); + if (gConf.useInelgt0wMFT && !isInelGt0wMft(particles)) { + return; + } + registryMC.fill(HIST("Events/hNchGen"), multMC, static_cast(EvtLossType::kGenRecEvt)); // Evt loss num + registryMC.fill(HIST("Events/Centrality/hEvtSigLossStatus"), 2., cRec); // Evt split num + registryMC.fill(HIST("Events/hMultGenVsCentRecEvt"), multMC, getMultMC(mcCollision), cRec); + registryMC.fill(HIST("Events/hMultGenVsCentRecEvtMFTacc"), multMCNParticlesEtaMFT, cRec); } if (collisions.size() == 0) { - registryMC.fill(HIST("Events/EvtSigLossStatus"), 3., cRec); + registryMC.fill(HIST("Events/Centrality/hEvtSigLossStatus"), 3., cRec); } for (auto const& particle : particles) { if (!isChrgParticle(particle.pdgCode())) { continue; } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + if (gConf.useParticleSel && !isParticleSelected(particle)) { continue; } - registryMC.fill(HIST("Tracks/hEtaVsNchGen"), particle.eta(), mcCollision.multMCFT0C()); // Sgn loss den + registryMC.fill(HIST("Tracks/hNchGenVsEta"), multMC, particle.eta()); // Sgn loss den if (gtZeroColl) { - registryMC.fill(HIST("Tracks/hEtaVsNchGenRecEvt"), particle.eta(), mcCollision.multMCFT0C()); // Sgn loss num + registryMC.fill(HIST("Tracks/hNchGenVsEtaRecEvt"), multMC, particle.eta()); // Sgn loss num } } } PROCESS_SWITCH(DndetaMFTPbPb, processMcSgnEvtLossCentFT0C, "Process Signal/event loss based on MC (in FT0C centrality bins)", false); - void processMcReassocDCA(CollsGenCentFT0CExtra const& collisions, + void processMcReassocDCA(CollsGenCentFT0C const& collisions, CollsMCExtra const& mcCollisions, aod::McParticles const& particles, MftBestTracksLabeled const& besttracks, @@ -2681,8 +3255,8 @@ struct DndetaMFTPbPb { int nNoMC{0}; for (const auto& collision : collisions) { auto crec = getRecoCent(collision); - registryMC.fill(HIST("Events/Centrality/EvtGenRecReassoc"), 2., crec); - if (!isGoodEvent(collision)) { + registryMC.fill(HIST("Events/Centrality/EvtGenRecReassoc"), 1., crec); + if (gConf.useEvtSel && !isGoodEvent(collision)) { continue; } if (!collision.has_mcCollision()) { @@ -2695,43 +3269,42 @@ struct DndetaMFTPbPb { nNoMC++; continue; } - registryMC.fill(HIST("Events/Centrality/EvtGenRecReassoc"), 3., crec); - if (gConf.cfgRemoveSplitVertex && (!setRecCollSel.contains(collision.globalIndex()))) { + registryMC.fill(HIST("Events/Centrality/EvtGenRecReassoc"), 2., crec); + if (gConf.rmSplitVertex && (!setRecCollSel.contains(collision.globalIndex()))) { continue; } auto mcColl = collision.mcCollision_as(); if (eventCuts.useZVtxCutMC && (std::abs(mcColl.posZ()) >= eventCuts.maxZvtx)) { continue; } - registryMC.fill(HIST("Events/Centrality/EvtGenRecReassoc"), 4., crec); + registryMC.fill(HIST("Events/Centrality/EvtGenRecReassoc"), 3., crec); auto perCollisionASample = besttracks.sliceBy(perColBestTrks, collision.globalIndex()); for (auto const& atrack : perCollisionASample) { - if (!isBestTrackSelected(atrack)) { + if (!isBestTrackSelected(atrack)) { continue; } const float bestDcaZ = getDCAz(atrack); auto itrack = atrack.template mfttrack_as(); - - if (!isTrackSelected(itrack)) { + if (!isTrackSelected(itrack)) { continue; } if (!itrack.has_collision()) { continue; } - if (gConf.cfgRemoveReassigned) { + if (gConf.rmReassigned) { if (itrack.collisionId() != atrack.bestCollisionId()) { continue; } } registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestRec"), itrack.pt(), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), atrack.bestDCAZ(), crec, crec); - if (itrack.collisionId() >= 0 && itrack.has_mcParticle() && itrack.mcMask() == trackCuts.selMcMask) { + if (itrack.collisionId() >= 0 && itrack.has_mcParticle() && (!trackCuts.removeFakeByMcMask || (itrack.mcMask() == trackCuts.selMcMask))) { auto particle = itrack.template mcParticle_as(); if (!isChrgParticle(particle.pdgCode())) { continue; } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + if (gConf.useParticleSel && !isParticleSelected(particle)) { continue; } if (eventCuts.useZDiffCut) { @@ -2749,7 +3322,7 @@ struct DndetaMFTPbPb { const auto dcaXtruth(particle.vx() - mcColl.posX()); const auto dcaYtruth(particle.vy() - mcColl.posY()); const auto dcaZtruth(particle.vz() - mcColl.posZ()); - auto dcaXYtruth = std::sqrt(dcaXtruth * dcaXtruth + dcaYtruth * dcaYtruth); + const auto dcaXYtruth = std::sqrt(dcaXtruth * dcaXtruth + dcaYtruth * dcaYtruth); const auto mcId = particle.mcCollisionId(); if (!mcIds.contains(mcId)) { diff --git a/PWGMM/Mult/Tasks/mftReassociationValidation.cxx b/PWGMM/Mult/Tasks/mftReassociationValidation.cxx index 59b9a20e76b..65cb143aa0a 100644 --- a/PWGMM/Mult/Tasks/mftReassociationValidation.cxx +++ b/PWGMM/Mult/Tasks/mftReassociationValidation.cxx @@ -272,6 +272,7 @@ struct MftReassociationValidation { Configurable fillGeneralHistograms{"fillGeneralHistograms", true, "Fill general reassociation efficiencyhistograms"}; Configurable fillSpecificHistograms{"fillSpecificHistograms", true, "Fill specific histograms (DCA, DeltaPos)"}; Configurable fillParametersHistograms{"fillParametersHistograms", true, "Fill parameters histograms"}; + Configurable fillDcaTemplateFit{"fillDcaTemplateFit", true, "Fill DCA template histograms for primary/secondary fraction fit"}; Configurable keepOnlyPhysicalPrimary{"keepOnlyPhysicalPrimary", true, "Keep only physical primary particles"}; Configurable keepOnlySecondaries{"keepOnlySecondaries", false, "Keep only secondary particles"}; Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; @@ -309,8 +310,6 @@ struct MftReassociationValidation { Configurable cutOrphanTracksExplicitly{"cutOrphanTracksExplicitly", false, "if true, cut orphan tracks explicitly"}; Configurable cutOnDcaXY{"cutOnDcaXY", false, "if true, cut on DCA XY"}; Configurable cutOnDcaZ{"cutOnDcaZ", false, "if true, cut on DCA Z"}; - Configurable dcaXYMax{"dcaXYMax", 2.0f, "Maximum value for DCA XY"}; - Configurable dcaZMax{"dcaZMax", 10.0f, "Maximum value for DCA Z"}; Configurable etaMftTrackMax{"etaMftTrackMax", -2.4f, "Maximum value for the eta of MFT tracks when used in cut function"}; Configurable etaMftTrackMin{"etaMftTrackMin", -3.36f, "Minimum value for the eta of MFT tracks when used in cut function"}; Configurable etaMftTrackMaxFilter{"etaMftTrackMaxFilter", -2.0f, "Maximum value for the eta of MFT tracks when used in filter"}; @@ -406,6 +405,8 @@ struct MftReassociationValidation { ConfigurableAxis axisDcaX{"axisDcaX", {500, -0.1, 2.1}, "DCAx binning (cm)"}; ConfigurableAxis axisDcaY{"axisDcaY", {500, -0.1, 2.1}, "DCAy binning (cm)"}; ConfigurableAxis axisDcaZ{"axisDcaZ", {500, -1., 1.}, "DCAz binning (cm)"}; + ConfigurableAxis axisDcaXYfit{"axisDcaXYfit", {500, 0., 2.}, "DCAxy binning (cm) for the template fit"}; + ConfigurableAxis axisDcaZfit{"axisDcaZfit", {500, -1., 1.}, "DCAz binning (cm) for the template fit"}; ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 100.1}, "multiplicity axis for histograms"}; ConfigurableAxis axisPhi{"axisPhi", {72, 0, TwoPI}, "phi axis for histograms"}; ConfigurableAxis axisPt{"axisPt", {72, 0, 36}, "pt axis for histograms"}; @@ -578,6 +579,11 @@ struct MftReassociationValidation { registry.get(HIST("MC/2D/hIsTrueCollisionAmongCompatibleCollisionsDcaNotReassociated"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMftIsTrueCollisionAmongCompatibleCollisionsStep[iBin].data()); } } + + if (configTask.fillDcaTemplateFit) { + registry.add("MC/2D/DcaFit/hPtVsDcaXY_prim", "Primary DCA_{xy} (2D);p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaXYfit}); + registry.add("MC/2D/DcaFit/hPtVsDcaXY_sec", "Secondary DCA_{xy} (2D);p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaXYfit}); + } } void addMftMonteCarloHistograms3D() @@ -689,6 +695,20 @@ struct MftReassociationValidation { registry.get(HIST("MC/3D/hIsTrueCollisionAmongCompatibleCollisionsDcaNotReassociated"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMftIsTrueCollisionAmongCompatibleCollisionsStep[iBin].data()); } } + + if (configTask.fillDcaTemplateFit) { + // 3D reassociated + registry.add("MC/3D/DcaFit/hPtVsDcaXY_prim", "Primary DCA_{xy} (3D);p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaXYfit}); + registry.add("MC/3D/DcaFit/hPtVsDcaXY_sec", "Secondary DCA_{xy} (3D);p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaXYfit}); + registry.add("MC/3D/DcaFit/hPtVsDcaZ_prim", "Primary DCA_{z} (3D);p_{T} (GeV/c);DCA_{z} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaZfit}); + registry.add("MC/3D/DcaFit/hPtVsDcaZ_sec", "Secondary DCA_{z} (3D);p_{T} (GeV/c);DCA_{z} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaZfit}); + + // Non-ambiguous + registry.add("MC/3D/DcaFit/NonAmb/hPtVsDcaXY_prim", "NonAmb Primary DCA_{xy};p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaXYfit}); + registry.add("MC/3D/DcaFit/NonAmb/hPtVsDcaXY_sec", "NonAmb Secondary DCA_{xy};p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaXYfit}); + registry.add("MC/3D/DcaFit/NonAmb/hPtVsDcaZ_prim", "NonAmb Primary DCA_{z};p_{T} (GeV/c);DCA_{z} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaZfit}); + registry.add("MC/3D/DcaFit/NonAmb/hPtVsDcaZ_sec", "NonAmb Secondary DCA_{z};p_{T} (GeV/c);DCA_{z} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaZfit}); + } } template @@ -833,6 +853,10 @@ struct MftReassociationValidation { if (doprocessData) { addMftHistograms(); + + if (configTask.fillDcaTemplateFit) { + registry.add("Data/2D/DcaFit/hPtVsDcaXY", "Data DCA_{xy} (2D);p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaXYfit}); + } } if (doprocessMcReassociated2d) { @@ -857,6 +881,18 @@ struct MftReassociationValidation { addMftMonteCarloHistograms3D(); } + if (doprocessData3d) { + addMftHistograms(); + + if (configTask.fillDcaTemplateFit) { + registry.add("Data/3D/DcaFit/hPtVsDcaXY", "Data DCA_{xy} (3D);p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaXYfit}); + registry.add("Data/3D/DcaFit/hPtVsDcaZ", "Data DCA_{z} (3D);p_{T} (GeV/c);DCA_{z} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaZfit}); + + registry.add("Data/3D/DcaFit/NonAmb/hPtVsDcaXY", "NonAmb Data DCA_{xy};p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaXYfit}); + registry.add("Data/3D/DcaFit/NonAmb/hPtVsDcaZ", "NonAmb Data DCA_{z};p_{T} (GeV/c);DCA_{z} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaZfit}); + } + } + } // End of init() function // ========================= @@ -1108,11 +1144,11 @@ struct MftReassociationValidation { } } - if (configMft.cutOnDcaXY && std::abs(dcaXY) > configMft.dcaXYMax) { + if (configMft.cutOnDcaXY && std::abs(dcaXY) > configMft.mftMaxDCAxy) { return false; } registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::AfterDcaXYCut); - if (configMft.cutOnDcaZ && std::abs(dcaZ) > configMft.dcaZMax) { + if (configMft.cutOnDcaZ && std::abs(dcaZ) > configMft.mftMaxDCAz) { return false; } registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::AfterDcaZCut); @@ -1199,31 +1235,77 @@ struct MftReassociationValidation { for (const auto& reassociated2dMftTrack : reassociated2dMftTracks) { - registry.fill(HIST("Data/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::AllMftTracks); auto templatedMftTrack = reassociated2dMftTrack.template mfttrack_as(); o2::track::TrackParCovFwd trackPar = o2::aod::fwdtrackutils::getTrackParCovFwdShift(templatedMftTrack, mZShift); std::array dcaInfOrig; trackPar.propagateToDCAhelix(bZ, {collision.posX(), collision.posY(), collision.posZ()}, dcaInfOrig); - auto dcaXYoriginal = 999.f; - dcaXYoriginal = std::sqrt(dcaInfOrig[0] * dcaInfOrig[0] + dcaInfOrig[1] * dcaInfOrig[1]); + // auto dcaXYoriginal = 999.f; + // dcaXYoriginal = std::sqrt(dcaInfOrig[0] * dcaInfOrig[0] + dcaInfOrig[1] * dcaInfOrig[1]); - if (!isAcceptedMftTrack(templatedMftTrack, false, true, dcaXYoriginal, dcaInfOrig[2])) { + if (!isAcceptedMftTrack(templatedMftTrack, false, true, reassociated2dMftTrack.bestDCAXY(), dcaInfOrig[2])) { continue; } - registry.fill(HIST("Data/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::AfterTrackSelection); - - if (isAmbiguousMftTrack(reassociated2dMftTrack, true)) { - registry.fill(HIST("Data/hReassociation2dMftTracks"), Reassociation2dMftTracks::NotReassociated2dMftTracks); + if (configTask.fillDcaTemplateFit) { + registry.fill(HIST("Data/2D/DcaFit/hPtVsDcaXY"), templatedMftTrack.pt(), reassociated2dMftTrack.bestDCAXY()); } } // end of loop over reassociated MFT tracks - - // TO-DO the same for reassociated3d (change the histograms) } PROCESS_SWITCH(MftReassociationValidation, processData, "Process MFT reassociation validation for DATA", false); + void processData3d(FilteredCollisionsWSelMult::iterator const& collision, + FilteredMftTracks const& /*mftTracks*/, + soa::SmallGroups const& reassociated3dMftTracks, + aod::BCsWithTimestamps const&) + { + auto bc = collision.template bc_as(); + if (areCorrectionsLoaded == false) { + loadZVertexShiftCorrection(bc); + areCorrectionsLoaded = true; + } + + if (!isAcceptedCollision(collision, false)) { + return; + } + + for (const auto& reassociated3dMftTrack : reassociated3dMftTracks) { + auto templatedMftTrack = reassociated3dMftTrack.template mfttrack_as(); + + // // Compute DCA at original collision for isAcceptedMftTrack + // o2::track::TrackParCovFwd trackPar = + // o2::aod::fwdtrackutils::getTrackParCovFwdShift(templatedMftTrack, mZShift); + // std::array dcaInfOrig; + // trackPar.propagateToDCAhelix( + // bZ, + // {collision.posX(), collision.posY(), collision.posZ()}, + // dcaInfOrig); + // const float dcaXYoriginal = + // std::sqrt(dcaInfOrig[0] * dcaInfOrig[0] + + // dcaInfOrig[1] * dcaInfOrig[1]); + + // Use existing track selection function (unchanged) + if (!isAcceptedMftTrack(templatedMftTrack, false, true, reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ())) { + continue; + } + + if (configTask.fillDcaTemplateFit) { + registry.fill(HIST("Data/3D/DcaFit/hPtVsDcaXY"), templatedMftTrack.pt(), reassociated3dMftTrack.bestDCAXY()); + registry.fill(HIST("Data/3D/DcaFit/hPtVsDcaZ"), templatedMftTrack.pt(), reassociated3dMftTrack.bestDCAZ()); + } + + // The same for nonambi only + if (reassociated3dMftTrack.ambDegree() == 1) { + if (configTask.fillDcaTemplateFit) { + registry.fill(HIST("Data/3D/DcaFit/NonAmb/hPtVsDcaXY"), templatedMftTrack.pt(), reassociated3dMftTrack.bestDCAXY()); + registry.fill(HIST("Data/3D/DcaFit/NonAmb/hPtVsDcaZ"), templatedMftTrack.pt(), reassociated3dMftTrack.bestDCAZ()); + } + } + } + } + PROCESS_SWITCH(MftReassociationValidation, processData3d, "Process DCA fit histograms for DATA 3D reassociation", false); + void processCreateLookupTable(FilteredCollisionsWSelMultMcLabels const& collisions, soa::Join const& mcCollisions) { recoVtxX.clear(); @@ -1347,6 +1429,15 @@ struct MftReassociationValidation { registry.fill(HIST("MC/2D/hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::HasMcParticle); auto particle = templatedTrack.template mcParticle_as(); + + if (configTask.fillDcaTemplateFit) { + if (particle.isPhysicalPrimary()) { + registry.fill(HIST("MC/2D/DcaFit/hPtVsDcaXY_prim"), templatedTrack.pt(), reassociated2dMftTrack.bestDCAXY()); + } else { + registry.fill(HIST("MC/2D/DcaFit/hPtVsDcaXY_sec"), templatedTrack.pt(), reassociated2dMftTrack.bestDCAXY()); + } + } + float deltaX = -999.f; float deltaY = -999.f; float deltaZ = -999.f; @@ -1734,6 +1825,25 @@ struct MftReassociationValidation { registry.fill(HIST("MC/3D/hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::HasMcParticle); auto particle = templatedTrack.template mcParticle_as(); + + if (configTask.fillDcaTemplateFit) { + if (particle.isPhysicalPrimary()) { + registry.fill(HIST("MC/3D/DcaFit/hPtVsDcaXY_prim"), templatedTrack.pt(), reassociated3dMftTrack.bestDCAXY()); + registry.fill(HIST("MC/3D/DcaFit/hPtVsDcaZ_prim"), templatedTrack.pt(), reassociated3dMftTrack.bestDCAZ()); + if (reassociated3dMftTrack.ambDegree() == 1) { + registry.fill(HIST("MC/3D/DcaFit/NonAmb/hPtVsDcaXY_prim"), templatedTrack.pt(), reassociated3dMftTrack.bestDCAXY()); + registry.fill(HIST("MC/3D/DcaFit/NonAmb/hPtVsDcaZ_prim"), templatedTrack.pt(), reassociated3dMftTrack.bestDCAZ()); + } + } else { + registry.fill(HIST("MC/3D/DcaFit/hPtVsDcaXY_sec"), templatedTrack.pt(), reassociated3dMftTrack.bestDCAXY()); + registry.fill(HIST("MC/3D/DcaFit/hPtVsDcaZ_sec"), templatedTrack.pt(), reassociated3dMftTrack.bestDCAZ()); + if (reassociated3dMftTrack.ambDegree() == 1) { + registry.fill(HIST("MC/3D/DcaFit/NonAmb/hPtVsDcaXY_sec"), templatedTrack.pt(), reassociated3dMftTrack.bestDCAXY()); + registry.fill(HIST("MC/3D/DcaFit/NonAmb/hPtVsDcaZ_sec"), templatedTrack.pt(), reassociated3dMftTrack.bestDCAZ()); + } + } + } + float deltaX = -999.f; float deltaY = -999.f; float deltaZ = -999.f; diff --git a/PWGMM/Mult/Tasks/puremc-dndeta.cxx b/PWGMM/Mult/Tasks/puremc-dndeta.cxx index f634755a3dd..1891459aa4f 100644 --- a/PWGMM/Mult/Tasks/puremc-dndeta.cxx +++ b/PWGMM/Mult/Tasks/puremc-dndeta.cxx @@ -31,6 +31,7 @@ #include #include #include +#include #include using namespace o2; diff --git a/PWGUD/AQC/udQC.cxx b/PWGUD/AQC/udQC.cxx index 44c2f03a2e8..24117f9d6e2 100644 --- a/PWGUD/AQC/udQC.cxx +++ b/PWGUD/AQC/udQC.cxx @@ -97,7 +97,7 @@ struct UDQC { // initialize global variables maxdEdxTPC = 0.; maxdEdxTOF = 0.; - diffCuts = (DGCutparHolder)DGCuts; + diffCuts = DGCutparHolder(DGCuts); // add histograms for the different process functions if (context.mOptions.get("processMain")) { diff --git a/PWGUD/AQC/udQCmidRap.cxx b/PWGUD/AQC/udQCmidRap.cxx index 6a2ea533d4c..1aaff2bb186 100644 --- a/PWGUD/AQC/udQCmidRap.cxx +++ b/PWGUD/AQC/udQCmidRap.cxx @@ -86,7 +86,7 @@ struct UDQCmid { // initialize global variables maxdEdxTPC = 0.; maxdEdxTOF = 0.; - diffCuts = (DGCutparHolder)DGCuts; + diffCuts = DGCutparHolder(DGCuts); // add histograms for the different process functions if (context.mOptions.get("processMain")) { diff --git a/PWGUD/AQC/udQcMuon.cxx b/PWGUD/AQC/udQcMuon.cxx index 9f09ee2d2c9..995fcaa3fec 100644 --- a/PWGUD/AQC/udQcMuon.cxx +++ b/PWGUD/AQC/udQcMuon.cxx @@ -98,7 +98,7 @@ struct UdQcMuon { void init(InitContext& context) { - diffCuts = (DGCutparHolder)dgCuts; + diffCuts = DGCutparHolder(dgCuts); // add histograms for the different process functions if (context.mOptions.get("processMain")) { diff --git a/PWGUD/Core/CMakeLists.txt b/PWGUD/Core/CMakeLists.txt index 3a7fe0950cf..243935064d4 100644 --- a/PWGUD/Core/CMakeLists.txt +++ b/PWGUD/Core/CMakeLists.txt @@ -71,4 +71,40 @@ o2physics_add_library(FITCutParHolder o2physics_target_root_dictionary(FITCutParHolder HEADERS FITCutParHolder.h - LINKDEF FITCutParHolderLinkDef.h) \ No newline at end of file + LINKDEF FITCutParHolderLinkDef.h) +# Precompiled header shared across PWGUD. Percentages are the share of the +# group's workflow translation units that include the header, measured over the tree. +# UDTables.h is the group's single shared data model. +# +# runDataProcessing.h is deliberately NOT precompiled: it requires each workflow's +# customize() to be declared before it is included, so precompiling it silently +# unregisters every option (the binary aborts at --dump-workflow, but links fine). +# +# Skipped under recc, which caches compilations remotely instead. +add_library(UDTablesPCH OBJECT udTablesPCH.cxx) +# The PCH compile is one of the heaviest translation units in the tree -- it +# instantiates the whole data model up front -- but add_library() does not put +# it in the 'analysis' job pool that o2physics_add_dpl_workflow() uses for the +# workflows. Left out, it runs unthrottled alongside the pooled compilations +# and defeats the per-job memory budget the pool exists to enforce. +set_property(TARGET UDTablesPCH PROPERTY JOB_POOL_COMPILE analysis) +target_link_libraries(UDTablesPCH PUBLIC O2::Framework O2Physics::AnalysisCore) +if(NOT DEFINED ENV{USE_RECC}) + target_precompile_headers(UDTablesPCH PRIVATE + # Framework baseline — same set AnalysisPCH precompiles. A target with + # REUSE_FROM uses ONLY this PCH, so anything left out here is parsed from + # scratch by every consumer: without this block these workflows were + # precompiling LESS than the default carrier they opted out of. + + + + + + + + + + + [["PWGUD/DataModel/UDTables.h"]] # 79%, ~650 lines + ) +endif() diff --git a/PWGUD/Core/DGCutparHolderLinkDef.h b/PWGUD/Core/DGCutparHolderLinkDef.h index b6b7c63efca..722796d015f 100644 --- a/PWGUD/Core/DGCutparHolderLinkDef.h +++ b/PWGUD/Core/DGCutparHolderLinkDef.h @@ -8,12 +8,10 @@ // 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. -#ifndef PWGUD_CORE_DGCUTPARHOLDERLINKDEF_H_ -#define PWGUD_CORE_DGCUTPARHOLDERLINKDEF_H_ + +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; #pragma link C++ class DGCutparHolder + ; - -#endif // PWGUD_CORE_DGCUTPARHOLDERLINKDEF_H_ diff --git a/PWGUD/Core/DGPIDSelectorLinkDef.h b/PWGUD/Core/DGPIDSelectorLinkDef.h index 2d0b181ba19..837079914e1 100644 --- a/PWGUD/Core/DGPIDSelectorLinkDef.h +++ b/PWGUD/Core/DGPIDSelectorLinkDef.h @@ -9,6 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +// NOLINT(build/header_guard) + #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; diff --git a/PWGUD/Core/FITCutParHolderLinkDef.h b/PWGUD/Core/FITCutParHolderLinkDef.h index 3194a6754b2..5e0644779cb 100644 --- a/PWGUD/Core/FITCutParHolderLinkDef.h +++ b/PWGUD/Core/FITCutParHolderLinkDef.h @@ -13,12 +13,9 @@ // \author Sandor Lokos, sandor.lokos@cern.ch // \since March 2026 -#ifndef PWGUD_CORE_FITCUTPARHOLDERLINKDEF_H_ -#define PWGUD_CORE_FITCUTPARHOLDERLINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; #pragma link C++ class FITCutParHolder + ; - -#endif // PWGUD_CORE_FITCUTPARHOLDERLINKDEF_H_ diff --git a/PWGUD/Core/SGCutParHolderLinkDef.h b/PWGUD/Core/SGCutParHolderLinkDef.h index 5dcadadc69e..f5d3a6f5a82 100644 --- a/PWGUD/Core/SGCutParHolderLinkDef.h +++ b/PWGUD/Core/SGCutParHolderLinkDef.h @@ -9,12 +9,9 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef PWGUD_CORE_SGCUTPARHOLDERLINKDEF_H_ -#define PWGUD_CORE_SGCUTPARHOLDERLINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; #pragma link C++ class SGCutParHolder + ; - -#endif // PWGUD_CORE_SGCUTPARHOLDERLINKDEF_H_ diff --git a/PWGUD/Core/UDFSParserLinkDef.h b/PWGUD/Core/UDFSParserLinkDef.h index f7caf1a0ab0..8f6e10eabd6 100644 --- a/PWGUD/Core/UDFSParserLinkDef.h +++ b/PWGUD/Core/UDFSParserLinkDef.h @@ -8,12 +8,10 @@ // 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. -#ifndef PWGUD_CORE_UDFSPARSERLINKDEF_H_ -#define PWGUD_CORE_UDFSPARSERLINKDEF_H_ + +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; #pragma link C++ class UDFSParser + ; - -#endif // PWGUD_CORE_UDFSPARSERLINKDEF_H_ diff --git a/PWGUD/Core/UDGoodRunSelectorLinkDef.h b/PWGUD/Core/UDGoodRunSelectorLinkDef.h index c26b4622809..c439e2e1094 100644 --- a/PWGUD/Core/UDGoodRunSelectorLinkDef.h +++ b/PWGUD/Core/UDGoodRunSelectorLinkDef.h @@ -9,6 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +// NOLINT(build/header_guard) + #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; diff --git a/PWGUD/Core/UDHelpers.h b/PWGUD/Core/UDHelpers.h index d014047bcc9..ed94841fda5 100644 --- a/PWGUD/Core/UDHelpers.h +++ b/PWGUD/Core/UDHelpers.h @@ -425,14 +425,14 @@ bool cleanFDD(T& bc, float maxFITtime, float limitA, float limitC) // lims[4]: FDDC template -bool cleanFIT(T& bc, float maxFITtime, std::vector lims) +bool cleanFIT(T& bc, float maxFITtime, std::vector const& lims) { return cleanFV0(bc, maxFITtime, lims[0]) && cleanFT0(bc, maxFITtime, lims[1], lims[2]) && cleanFDD(bc, maxFITtime, lims[3], lims[4]); } template -bool cleanFITCollision(T& col, float maxFITtime, std::vector lims) +bool cleanFITCollision(T& col, float maxFITtime, std::vector const& lims) { bool isCleanFV0 = true; if (col.has_foundFV0()) { @@ -453,7 +453,7 @@ bool cleanFITCollision(T& col, float maxFITtime, std::vector lims) // ----------------------------------------------------------------------------- template -bool cleanFITA(T& bc, float maxFITtime, std::vector lims) +bool cleanFITA(T& bc, float maxFITtime, std::vector const& lims) { return cleanFV0(bc, maxFITtime, lims[0]) && cleanFT0A(bc, maxFITtime, lims[1]) && @@ -462,7 +462,7 @@ bool cleanFITA(T& bc, float maxFITtime, std::vector lims) // ----------------------------------------------------------------------------- template -bool cleanFITC(T& bc, float maxFITtime, std::vector lims) +bool cleanFITC(T& bc, float maxFITtime, std::vector const& lims) { return cleanFT0C(bc, maxFITtime, lims[2]) && cleanFDDC(bc, maxFITtime, lims[4]); @@ -506,7 +506,7 @@ bool TCE(T& bc) // ----------------------------------------------------------------------------- template -bool TOR(T& bc, float maxFITtime, std::vector lims) +bool TOR(T& bc, float maxFITtime, std::vector const& lims) { auto torA = !cleanFT0A(bc, maxFITtime, lims[1]); auto torC = !cleanFT0C(bc, maxFITtime, lims[2]); @@ -565,27 +565,48 @@ inline void buildFT0FV0Words(TFT0 const& ft0, TFV0A const& fv0a, constexpr int kFV0Offset = 208; auto ampsA = ft0.amplitudeA(); - const int nA = std::min(ampsA.size(), 96); + auto chanA = ft0.channelA(); + const int nA = std::min(ampsA.size(), chanA.size()); for (int i = 0; i < nA; ++i) { const auto a = ampsA[i]; - setBit(thr1, kFT0AOffset + i, a >= thr1_FT0A); - setBit(thr2, kFT0AOffset + i, a >= thr2_FT0A); + const int c = chanA[i]; + + if (c < 0 || c >= 96) { + continue; + } + + setBit(thr1, kFT0AOffset + c, a >= thr1_FT0A); + setBit(thr2, kFT0AOffset + c, a >= thr2_FT0A); } auto ampsC = ft0.amplitudeC(); - const int nC = std::min(ampsC.size(), 112); + auto chanC = ft0.channelC(); + const int nC = std::min(ampsC.size(), chanC.size()); for (int i = 0; i < nC; ++i) { const auto a = ampsC[i]; - setBit(thr1, kFT0COffset + i, a >= thr1_FT0C); - setBit(thr2, kFT0COffset + i, a >= thr2_FT0C); + const int c = chanC[i]; + + if (c < 0 || c >= 112) { + continue; + } + + setBit(thr1, kFT0COffset + c, a >= thr1_FT0C); + setBit(thr2, kFT0COffset + c, a >= thr2_FT0C); } auto ampsV = fv0a.amplitude(); - const int nV = std::min(ampsV.size(), 48); + auto chanV = fv0a.channel(); + const int nV = std::min(ampsV.size(), chanV.size()); for (int i = 0; i < nV; ++i) { const auto a = ampsV[i]; - setBit(thr1, kFV0Offset + i, a >= thr1_FV0A); - setBit(thr2, kFV0Offset + i, a >= thr2_FV0A); + const int c = chanV[i]; + + if (c < 0 || c >= 48) { + continue; + } + + setBit(thr1, kFV0Offset + c, a >= thr1_FV0A); + setBit(thr2, kFV0Offset + c, a >= thr2_FV0A); } } @@ -777,24 +798,15 @@ template bool goodCollision(T const& coll, DGCutparHolder const& diffCuts) // Return true if collision is accepted according to user-chosen rules from event selection task { - bool accepted = true; - std::vector sels = diffCuts.collisionSel(); - if (sels[0]) - accepted = accepted && cutNoTimeFrameBorder(coll); - if (sels[1]) - accepted = accepted && cutNoSameBunchPileup(coll); - if (sels[2]) - accepted = accepted && cutNoITSROFrameBorder(coll); - if (sels[3]) - accepted = accepted && cutIsGoodZvtxFT0vsPV(coll); - if (sels[4]) - accepted = accepted && cutIsVertexITSTPC(coll); - if (sels[5]) - accepted = accepted && cutIsVertexTRDmatched(coll); - if (sels[6]) - accepted = accepted && cutIsVertexTOFmatched(coll); - - return accepted; + auto const sels = diffCuts.collisionSel(); + + return (!sels[0] || cutNoTimeFrameBorder(coll)) && + (!sels[1] || cutNoSameBunchPileup(coll)) && + (!sels[2] || cutNoITSROFrameBorder(coll)) && + (!sels[3] || cutIsGoodZvtxFT0vsPV(coll)) && + (!sels[4] || cutIsVertexITSTPC(coll)) && + (!sels[5] || cutIsVertexTRDmatched(coll)) && + (!sels[6] || cutIsVertexTOFmatched(coll)); } // ----------------------------------------------------------------------------- diff --git a/PWGUD/Core/UPCCutparHolderLinkDef.h b/PWGUD/Core/UPCCutparHolderLinkDef.h index 49d4802838d..6391b991669 100644 --- a/PWGUD/Core/UPCCutparHolderLinkDef.h +++ b/PWGUD/Core/UPCCutparHolderLinkDef.h @@ -9,6 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +// NOLINT(build/header_guard) + #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; diff --git a/PWGUD/Core/decayTreeLinkDef.h b/PWGUD/Core/decayTreeLinkDef.h index 92d99ccf1bc..6ed7643f91b 100644 --- a/PWGUD/Core/decayTreeLinkDef.h +++ b/PWGUD/Core/decayTreeLinkDef.h @@ -8,12 +8,10 @@ // 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. -#ifndef PWGUD_CORE_DECAYTREELINKDEF_H_ -#define PWGUD_CORE_DECAYTREELINKDEF_H_ + +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; #pragma link C++ class decayTree + ; - -#endif // PWGUD_CORE_DECAYTREELINKDEF_H_ diff --git a/PWGUD/Core/udTablesPCH.cxx b/PWGUD/Core/udTablesPCH.cxx new file mode 100644 index 00000000000..8faa701be69 --- /dev/null +++ b/PWGUD/Core/udTablesPCH.cxx @@ -0,0 +1,13 @@ +// Copyright 2019-2026 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. + +// Carrier for the UDTablesPCH precompiled header; see this directory's +// CMakeLists.txt. It exists only to own the PCH, so it has no code of its own. diff --git a/PWGUD/DataModel/TauThreeProngEventTables.h b/PWGUD/DataModel/TauThreeProngEventTables.h index ca00bd0be6f..95c8fff2a69 100644 --- a/PWGUD/DataModel/TauThreeProngEventTables.h +++ b/PWGUD/DataModel/TauThreeProngEventTables.h @@ -138,7 +138,7 @@ DECLARE_SOA_COLUMN(True6DaugPx, true6DaugPx, float[6]); DECLARE_SOA_COLUMN(True6DaugPy, true6DaugPy, float[6]); DECLARE_SOA_COLUMN(True6DaugPz, true6DaugPz, float[6]); DECLARE_SOA_COLUMN(True6DaugPdgCode, true6DaugPdgCode, int[6]); -DECLARE_SOA_COLUMN(Problem, problem, int8_t); +DECLARE_SOA_COLUMN(Problem, problem, uint8_t); DECLARE_SOA_COLUMN(IsRec, isRec, int8_t); } // namespace tautree diff --git a/PWGUD/DataModel/TwoFwdTracksEventTables.h b/PWGUD/DataModel/TwoFwdTracksEventTables.h new file mode 100644 index 00000000000..93a0309d7a8 --- /dev/null +++ b/PWGUD/DataModel/TwoFwdTracksEventTables.h @@ -0,0 +1,164 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// \file TwoFwdTracksEventTables.h +/// \author Roman Lavička +/// \since 2026-10-02 +/// \brief A table to store information about forward UPC candidates preselected to have exactly two muon tracks. +/// \brief Forward counterpart of TwoTracksEventTables.h, filled from the output of upcCandProducerGlobalMuon +/// \brief (MCH/MID/MFT tracks, FV0A and ZDC instead of ITS/TPC/TOF tracks, FT0 and event-selection bits). +/// + +#ifndef PWGUD_DATAMODEL_TWOFWDTRACKSEVENTTABLES_H_ +#define PWGUD_DATAMODEL_TWOFWDTRACKSEVENTTABLES_H_ + +#include + +#include +#include + +namespace o2::aod +{ +namespace two_fwd_tracks_tree +{ +// event info +DECLARE_SOA_COLUMN(RunNumber, runNumber, int32_t); +DECLARE_SOA_COLUMN(Bc, bc, uint64_t); +DECLARE_SOA_COLUMN(NumContrib, numContrib, int); +DECLARE_SOA_COLUMN(PosX, posX, float); +DECLARE_SOA_COLUMN(PosY, posY, float); +DECLARE_SOA_COLUMN(PosZ, posZ, float); +// FV0A and ZDC info +DECLARE_SOA_COLUMN(TotalFV0AmplitudeA, totalFV0AmplitudeA, float); +DECLARE_SOA_COLUMN(AmplitudesV0A, amplitudesV0A, std::vector); +DECLARE_SOA_COLUMN(AmpRelBCsV0A, ampRelBCsV0A, std::vector); +DECLARE_SOA_COLUMN(EnergyCommonZNA, energyCommonZNA, float); +DECLARE_SOA_COLUMN(EnergyCommonZNC, energyCommonZNC, float); +DECLARE_SOA_COLUMN(TimeZNA, timeZNA, float); +DECLARE_SOA_COLUMN(TimeZNC, timeZNC, float); +// tracks +DECLARE_SOA_COLUMN(TrkPx, trkPx, float[2]); +DECLARE_SOA_COLUMN(TrkPy, trkPy, float[2]); +DECLARE_SOA_COLUMN(TrkPz, trkPz, float[2]); +DECLARE_SOA_COLUMN(TrkSign, trkSign, int[2]); +DECLARE_SOA_COLUMN(TrkTime, trkTime, float[2]); +DECLARE_SOA_COLUMN(TrkTimeRes, trkTimeRes, float[2]); +DECLARE_SOA_COLUMN(TrkType, trkType, int[2]); +DECLARE_SOA_COLUMN(TrkNClusters, trkNClusters, int[2]); +DECLARE_SOA_COLUMN(TrkPDca, trkPDca, float[2]); +DECLARE_SOA_COLUMN(TrkRAtAbsorberEnd, trkRAtAbsorberEnd, float[2]); +DECLARE_SOA_COLUMN(TrkChi2, trkChi2, float[2]); +DECLARE_SOA_COLUMN(TrkChi2MatchMCHMID, trkChi2MatchMCHMID, float[2]); +DECLARE_SOA_COLUMN(TrkChi2MatchMCHMFT, trkChi2MatchMCHMFT, float[2]); +DECLARE_SOA_COLUMN(TrkMCHBitMap, trkMCHBitMap, int[2]); +DECLARE_SOA_COLUMN(TrkMIDBitMap, trkMIDBitMap, int[2]); +DECLARE_SOA_COLUMN(Trk1MIDBoards, trk1MIDBoards, uint32_t); +DECLARE_SOA_COLUMN(Trk2MIDBoards, trk2MIDBoards, uint32_t); +// truth event +DECLARE_SOA_COLUMN(TrueChannel, trueChannel, int); +DECLARE_SOA_COLUMN(TrueHasRecoColl, trueHasRecoColl, bool); +DECLARE_SOA_COLUMN(TruePosX, truePosX, float); +DECLARE_SOA_COLUMN(TruePosY, truePosY, float); +DECLARE_SOA_COLUMN(TruePosZ, truePosZ, float); +// truth particles +DECLARE_SOA_COLUMN(TrueMotherPx, trueMotherPx, float[2]); +DECLARE_SOA_COLUMN(TrueMotherPy, trueMotherPy, float[2]); +DECLARE_SOA_COLUMN(TrueMotherPz, trueMotherPz, float[2]); +DECLARE_SOA_COLUMN(TrueDaugPx, trueDaugPx, float[2]); +DECLARE_SOA_COLUMN(TrueDaugPy, trueDaugPy, float[2]); +DECLARE_SOA_COLUMN(TrueDaugPz, trueDaugPz, float[2]); +DECLARE_SOA_COLUMN(TrueDaugPdgCode, trueDaugPdgCode, int[2]); +// additional info +DECLARE_SOA_COLUMN(ProblematicEvent, problematicEvent, bool); + +} // namespace two_fwd_tracks_tree + +DECLARE_SOA_TABLE(TwoFwdTracks, "AOD", "TWOFWDTRACK", + two_fwd_tracks_tree::RunNumber, + two_fwd_tracks_tree::Bc, + two_fwd_tracks_tree::NumContrib, + two_fwd_tracks_tree::PosX, + two_fwd_tracks_tree::PosY, + two_fwd_tracks_tree::PosZ, + two_fwd_tracks_tree::TotalFV0AmplitudeA, + two_fwd_tracks_tree::AmplitudesV0A, + two_fwd_tracks_tree::AmpRelBCsV0A, + two_fwd_tracks_tree::EnergyCommonZNA, + two_fwd_tracks_tree::EnergyCommonZNC, + two_fwd_tracks_tree::TimeZNA, + two_fwd_tracks_tree::TimeZNC, + two_fwd_tracks_tree::TrkPx, + two_fwd_tracks_tree::TrkPy, + two_fwd_tracks_tree::TrkPz, + two_fwd_tracks_tree::TrkSign, + two_fwd_tracks_tree::TrkTime, + two_fwd_tracks_tree::TrkTimeRes, + two_fwd_tracks_tree::TrkType, + two_fwd_tracks_tree::TrkNClusters, + two_fwd_tracks_tree::TrkPDca, + two_fwd_tracks_tree::TrkRAtAbsorberEnd, + two_fwd_tracks_tree::TrkChi2, + two_fwd_tracks_tree::TrkChi2MatchMCHMID, + two_fwd_tracks_tree::TrkChi2MatchMCHMFT, + two_fwd_tracks_tree::TrkMCHBitMap, + two_fwd_tracks_tree::TrkMIDBitMap, + two_fwd_tracks_tree::Trk1MIDBoards, + two_fwd_tracks_tree::Trk2MIDBoards); + +DECLARE_SOA_TABLE(TrueTwoFwdTracks, "AOD", "TRUETWOFWDTRACK", + two_fwd_tracks_tree::RunNumber, + two_fwd_tracks_tree::Bc, + two_fwd_tracks_tree::NumContrib, + two_fwd_tracks_tree::PosX, + two_fwd_tracks_tree::PosY, + two_fwd_tracks_tree::PosZ, + two_fwd_tracks_tree::TotalFV0AmplitudeA, + two_fwd_tracks_tree::AmplitudesV0A, + two_fwd_tracks_tree::AmpRelBCsV0A, + two_fwd_tracks_tree::EnergyCommonZNA, + two_fwd_tracks_tree::EnergyCommonZNC, + two_fwd_tracks_tree::TimeZNA, + two_fwd_tracks_tree::TimeZNC, + two_fwd_tracks_tree::TrkPx, + two_fwd_tracks_tree::TrkPy, + two_fwd_tracks_tree::TrkPz, + two_fwd_tracks_tree::TrkSign, + two_fwd_tracks_tree::TrkTime, + two_fwd_tracks_tree::TrkTimeRes, + two_fwd_tracks_tree::TrkType, + two_fwd_tracks_tree::TrkNClusters, + two_fwd_tracks_tree::TrkPDca, + two_fwd_tracks_tree::TrkRAtAbsorberEnd, + two_fwd_tracks_tree::TrkChi2, + two_fwd_tracks_tree::TrkChi2MatchMCHMID, + two_fwd_tracks_tree::TrkChi2MatchMCHMFT, + two_fwd_tracks_tree::TrkMCHBitMap, + two_fwd_tracks_tree::TrkMIDBitMap, + two_fwd_tracks_tree::Trk1MIDBoards, + two_fwd_tracks_tree::Trk2MIDBoards, + two_fwd_tracks_tree::TrueChannel, + two_fwd_tracks_tree::TrueHasRecoColl, + two_fwd_tracks_tree::TruePosX, + two_fwd_tracks_tree::TruePosY, + two_fwd_tracks_tree::TruePosZ, + two_fwd_tracks_tree::TrueMotherPx, + two_fwd_tracks_tree::TrueMotherPy, + two_fwd_tracks_tree::TrueMotherPz, + two_fwd_tracks_tree::TrueDaugPx, + two_fwd_tracks_tree::TrueDaugPy, + two_fwd_tracks_tree::TrueDaugPz, + two_fwd_tracks_tree::TrueDaugPdgCode, + two_fwd_tracks_tree::ProblematicEvent); + +} // namespace o2::aod + +#endif // PWGUD_DATAMODEL_TWOFWDTRACKSEVENTTABLES_H_ diff --git a/PWGUD/DataModel/UDTables.h b/PWGUD/DataModel/UDTables.h index afb7728a5ab..c47f5befaa1 100644 --- a/PWGUD/DataModel/UDTables.h +++ b/PWGUD/DataModel/UDTables.h @@ -410,28 +410,43 @@ DECLARE_SOA_TABLE(UDTracksLabels, "AOD", "UDTRACKLABEL", namespace udcollfitbits { +DECLARE_SOA_INDEX_COLUMN(UDCollision, udCollision); /// Link to the UDCOLLISION table + DECLARE_SOA_COLUMN(Thr1W0, thr1W0, uint64_t); /// 1 MIP thresholds for FT0A ch 0 - ch 63 -DECLARE_SOA_COLUMN(Thr1W1, thr1W1, uint64_t); /// 1 MIP thresholds for FT0A ch 64 - ch 96 & FT0C ch 0 - ch 31 -DECLARE_SOA_COLUMN(Thr1W2, thr1W2, uint64_t); /// 1 MIP thresholds for FT0C ch 32 - ch 96 -DECLARE_SOA_COLUMN(Thr1W3, thr1W3, uint64_t); /// 1 MIP thresholds for FT0C ch 97 - 112 & FV0 0 - 47 +DECLARE_SOA_COLUMN(Thr1W1, thr1W1, uint64_t); /// 1 MIP thresholds for FT0A ch 64 - ch 95 & FT0C ch 0 - ch 31 +DECLARE_SOA_COLUMN(Thr1W2, thr1W2, uint64_t); /// 1 MIP thresholds for FT0C ch 32 - ch 95 +DECLARE_SOA_COLUMN(Thr1W3, thr1W3, uint64_t); /// 1 MIP thresholds for FT0C ch 96 - 111 & FV0 0 - 47 DECLARE_SOA_COLUMN(Thr2W0, thr2W0, uint64_t); /// 2 MIP thresholds for FT0A ch 0 - ch 63 -DECLARE_SOA_COLUMN(Thr2W1, thr2W1, uint64_t); /// 2 MIP thresholds for FT0A ch 63 - ch 96 & FT0C ch 0 - ch 31 -DECLARE_SOA_COLUMN(Thr2W2, thr2W2, uint64_t); /// 2 MIP thresholds for FT0C ch 32 - ch 96 -DECLARE_SOA_COLUMN(Thr2W3, thr2W3, uint64_t); /// 2 MIP thresholds for FT0C ch 97 - 112 & FV0 0 - 47 +DECLARE_SOA_COLUMN(Thr2W1, thr2W1, uint64_t); /// 2 MIP thresholds for FT0A ch 64 - ch 95 & FT0C ch 0 - ch 31 +DECLARE_SOA_COLUMN(Thr2W2, thr2W2, uint64_t); /// 2 MIP thresholds for FT0C ch 32 - ch 95 +DECLARE_SOA_COLUMN(Thr2W3, thr2W3, uint64_t); /// 2 MIP thresholds for FT0C ch 96 - 111 & FV0 0 - 47 } // namespace udcollfitbits -DECLARE_SOA_TABLE(UDCollisionFITBits, "AOD", "UDCOLLFITBITS", +DECLARE_SOA_TABLE(UDCollisionFITBits_000, "AOD", "UDCOLLFITBITS", o2::soa::Index<>, - udcollfitbits::Thr1W0, /// 1 MIP thresholds for FT0A ch 0 - ch 63 - udcollfitbits::Thr1W1, /// 1 MIP thresholds for FT0A ch 63 - ch 96 & FT0C ch 0 - ch 31 - udcollfitbits::Thr1W2, /// 1 MIP thresholds for FT0C ch 32 - ch 96 - udcollfitbits::Thr1W3, /// 1 MIP thresholds for FT0C ch 97 - 112 & FV0 0 - 47 - udcollfitbits::Thr2W0, /// 2 MIP thresholds for FT0A ch 0 - ch 63 - udcollfitbits::Thr2W1, /// 2 MIP thresholds for FT0A ch 63 - ch 96 & FT0C ch 0 - ch 31 - udcollfitbits::Thr2W2, /// 2 MIP thresholds for FT0C ch 32 - ch 96 - udcollfitbits::Thr2W3 /// 2 MIP thresholds for FT0C ch 97 - 112 & FV0 0 - 47 -); + udcollfitbits::Thr1W0, + udcollfitbits::Thr1W1, + udcollfitbits::Thr1W2, + udcollfitbits::Thr1W3, + udcollfitbits::Thr2W0, + udcollfitbits::Thr2W1, + udcollfitbits::Thr2W2, + udcollfitbits::Thr2W3); + +DECLARE_SOA_TABLE_VERSIONED(UDCollisionFITBits_001, "AOD", "UDCOLLFITBITS", 1, + o2::soa::Index<>, + udcollfitbits::UDCollisionId, + udcollfitbits::Thr1W0, + udcollfitbits::Thr1W1, + udcollfitbits::Thr1W2, + udcollfitbits::Thr1W3, + udcollfitbits::Thr2W0, + udcollfitbits::Thr2W1, + udcollfitbits::Thr2W2, + udcollfitbits::Thr2W3); + +using UDCollisionFITBits = UDCollisionFITBits_001; using UDTrack = UDTracks::iterator; using UDTrackCov = UDTracksCov::iterator; diff --git a/PWGUD/TableProducer/CMakeLists.txt b/PWGUD/TableProducer/CMakeLists.txt index ecc22560552..85923b12bc2 100644 --- a/PWGUD/TableProducer/CMakeLists.txt +++ b/PWGUD/TableProducer/CMakeLists.txt @@ -13,6 +13,7 @@ add_subdirectory(Converters) o2physics_add_dpl_workflow(dg-cand-producer SOURCES dgCandProducer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGCutparHolder O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) @@ -33,16 +34,25 @@ o2physics_add_dpl_workflow(upccand-producer # o2-linter: disable=name/o2-workflo o2physics_add_dpl_workflow(tau-event-table-producer SOURCES tauEventTableProducer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(two-tracks-event-table-producer SOURCES twoTracksEventTableProducer.cxx + REUSE_FROM UDTablesPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(two-fwd-tracks-event-table-producer + SOURCES twoFwdTracksEventTableProducer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(fwd-track-propagation SOURCES fwdTrackPropagation.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::GlobalTracking COMPONENT_NAME Analysis) @@ -58,26 +68,31 @@ o2physics_add_dpl_workflow(udmccollisions-to-udcollisions # o2-linter: disable=n o2physics_add_dpl_workflow(tau-three-prong-event-table-producer SOURCES tauThreeProngEventTableProducer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-cand-producer-muon SOURCES upcCandProducerMuon.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::UPCCutparHolder O2::GlobalTracking COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(true-gap-producer SOURCES trueGapProducer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-cand-producer-global-muon SOURCES upcCandProducerGlobalMuon.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::UPCCutparHolder O2::GlobalTracking COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-cand-producer-semi-fwd SOURCES upcCandProducerSemiFwd.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::UPCCutparHolder O2::GlobalTracking COMPONENT_NAME Analysis) @@ -85,3 +100,8 @@ o2physics_add_dpl_workflow(upc-cand-producer-barrel SOURCES upcCandProducerBarrel.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::CCDB O2::DataFormatsParameters O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(muon-efficiency-producer + SOURCES muonEfficiencyProducer.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::GlobalTracking + COMPONENT_NAME Analysis) diff --git a/PWGUD/TableProducer/Converters/CMakeLists.txt b/PWGUD/TableProducer/Converters/CMakeLists.txt index 7d5a6297ba1..8197eb878be 100644 --- a/PWGUD/TableProducer/Converters/CMakeLists.txt +++ b/PWGUD/TableProducer/Converters/CMakeLists.txt @@ -11,26 +11,36 @@ o2physics_add_dpl_workflow(fwd-tracks-extra-converter SOURCES UDFwdTracksExtraConverter.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(collisions-converter SOURCES UDCollisionsConverter.cxx + REUSE_FROM UDTablesPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(collision-fit-bits-converter + SOURCES UDCollisionFITBitsConverter.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(collisionselextras-converter SOURCES UDCollisionSelExtrasConverter.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(collisionselextras-converter-v002 SOURCES UDCollisionSelExtrasV002Converter.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(collisionselextras-converter-v003 SOURCES UDCollisionSelExtrasV003Converter.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) diff --git a/PWGUD/TableProducer/Converters/UDCollisionFITBitsConverter.cxx b/PWGUD/TableProducer/Converters/UDCollisionFITBitsConverter.cxx new file mode 100644 index 00000000000..82be0196959 --- /dev/null +++ b/PWGUD/TableProducer/Converters/UDCollisionFITBitsConverter.cxx @@ -0,0 +1,61 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// + +/// \file UDCollisionFITBitsConverter.cxx +/// \brief Converts UDCollisionFITBits from version 000 to 001 +/// +/// The legacy table was written with exactly one FIT-bit row for every +/// UDCollision, in identical row order. Therefore row i of +/// UDCollisionFITBits_000 belongs to row i of UDCollisions. Because the +/// legacy table does not store this association explicitly, the converter +/// materialises it by writing the FIT-bit row's global index as +/// UDCollisionId. The eight packed FIT-bit words are copied unchanged. +/// +/// Executable: o2-analysis-ud-collision-fit-bits-converter +/// +/// \author Sandor Lokos, sandor.lokos@cern.ch + +#include "PWGUD/DataModel/UDTables.h" + +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +struct UDCollisionFITBitsConverter { + Produces outputFITBits; + + void process(aod::UDCollisionFITBits_000 const& legacyFITBits) + { + outputFITBits.reserve(legacyFITBits.size()); + + for (const auto& bits : legacyFITBits) { + outputFITBits(bits.globalIndex(), + bits.thr1W0(), + bits.thr1W1(), + bits.thr1W2(), + bits.thr1W3(), + bits.thr2W0(), + bits.thr2W1(), + bits.thr2W2(), + bits.thr2W3()); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGUD/TableProducer/DGBCCandProducer.cxx b/PWGUD/TableProducer/DGBCCandProducer.cxx index cf62e50c84d..4719805c3e2 100644 --- a/PWGUD/TableProducer/DGBCCandProducer.cxx +++ b/PWGUD/TableProducer/DGBCCandProducer.cxx @@ -131,8 +131,8 @@ struct tracksWGTInBCs { // compute the BC closest in time auto firstCompatibleBC = ambTracksSlice.begin().bc().begin().globalBC(); LOGF(debug, "First compatible BC %d Track time %f", firstCompatibleBC, track.trackTime()); - closestBC = (uint64_t)(firstCompatibleBC + - (track.trackTime() / o2::constants::lhc::LHCBunchSpacingNS)); + closestBC = static_cast(firstCompatibleBC + + (track.trackTime() / o2::constants::lhc::LHCBunchSpacingNS)); } else { // this track is not ambiguous, has hence a unique association to a collision/BC if (!track.has_collision()) { @@ -147,7 +147,7 @@ struct tracksWGTInBCs { // update tracksInBCList LOGF(debug, "Updating tracksInBCList with %d", closestBC); - tracksInBCList[closestBC].emplace_back((int32_t)track.globalIndex()); + tracksInBCList[closestBC].emplace_back(static_cast(track.globalIndex())); } } @@ -215,8 +215,8 @@ struct tracksWGTInBCs { // compute the BC closest in time auto firstCompatibleBC = ambFwdTracksSlice.begin().bc().begin().globalBC(); - closestBC = (uint64_t)(firstCompatibleBC + - (fwdTrack.trackTime() / o2::constants::lhc::LHCBunchSpacingNS)); + closestBC = static_cast(firstCompatibleBC + + (fwdTrack.trackTime() / o2::constants::lhc::LHCBunchSpacingNS)); } else { // this track is not ambiguous, has hence a unique association to a collision/BC if (!fwdTrack.has_collision()) { @@ -231,7 +231,7 @@ struct tracksWGTInBCs { // update tracksInBCList LOGF(debug, "Updating fwdTracksInBCList with %d", closestBC); - fwdTracksInBCList[closestBC].emplace_back((int32_t)fwdTrack.globalIndex()); + fwdTracksInBCList[closestBC].emplace_back(static_cast(fwdTrack.globalIndex())); } } @@ -385,7 +385,7 @@ struct DGBCCandProducer { void init(InitContext& context) { - diffCuts = (DGCutparHolder)DGCuts; + diffCuts = DGCutparHolder(DGCuts); if (context.mOptions.get("processTinBCs")) { registry.add("table/candCase", "#candCase", {HistType::kTH1F, {{4, -0.5, 3.5}}}); diff --git a/PWGUD/TableProducer/SGCandProducer.cxx b/PWGUD/TableProducer/SGCandProducer.cxx index 8bc472c455f..3e407e3c0b5 100644 --- a/PWGUD/TableProducer/SGCandProducer.cxx +++ b/PWGUD/TableProducer/SGCandProducer.cxx @@ -351,7 +351,8 @@ struct SGCandProducer { fitCuts.thr2_FV0A()); } - outputFITBits(w1[0], w1[1], w1[2], w1[3], + outputFITBits(outputCollisions.lastIndex(), + w1[0], w1[1], w1[2], w1[3], w2[0], w2[1], w2[2], w2[3]); if (newbc.has_zdc()) { @@ -391,8 +392,8 @@ struct SGCandProducer { ccdb->setURL("http://alice-ccdb.cern.ch"); ccdb->setCaching(true); ccdb->setFatalWhenNull(false); - sameCuts = (SGCutParHolder)SGCuts; - fitCuts = (FITCutParHolder)FITCuts; + sameCuts = SGCutParHolder(SGCuts); + fitCuts = FITCutParHolder(FITCuts); // add histograms for the different process functions histPointers.clear(); diff --git a/PWGUD/TableProducer/UPCCandidateProducer.cxx b/PWGUD/TableProducer/UPCCandidateProducer.cxx index f59c92ca55c..670e6b1f158 100644 --- a/PWGUD/TableProducer/UPCCandidateProducer.cxx +++ b/PWGUD/TableProducer/UPCCandidateProducer.cxx @@ -157,7 +157,7 @@ struct UpcCandProducer { fwdSelectors.resize(upchelpers::kNFwdSels - 1, false); barrelSelectors.resize(upchelpers::kNBarrelSels - 1, false); - upcCuts = (UPCCutparHolder)inputCuts; + upcCuts = UPCCutparHolder(inputCuts); // initialize RCT flag checker if (rctLabel.value != "none" && rctLabel.value != "muon" && rctLabel.value != "muon_glo") { @@ -876,7 +876,7 @@ struct UpcCandProducer { ++curit; if (curit == v.end()) break; - curbc = curit->first; + // curbc = curit->first; // FIXME: unused } uint32_t size = curit->second.size(); if (size > 1) // too many tracks per BC -> possibly another event @@ -1990,7 +1990,7 @@ struct UpcCandProducer { fwdTracks, fwdTrkClusters, ambFwdTracks, bcs, collisions, ft0s, fdds, fv0as, - (o2::aod::McTrackLabels*)nullptr, (o2::aod::McFwdTrackLabels*)nullptr); + nullptr, nullptr); } // create candidates for central region @@ -2007,7 +2007,7 @@ struct UpcCandProducer { createCandidatesCentral(barrelTracks, ambBarrelTracks, bcs, collisions, ft0s, fdds, fv0as, zdcs, - (o2::aod::McTrackLabels*)nullptr); + nullptr); } // MC processors @@ -2076,7 +2076,7 @@ struct UpcCandProducer { createCandidatesFwd(fwdTracks, fwdTrkClusters, ambFwdTracks, bcs, collisions, ft0s, fdds, fv0as, zdcs, - (o2::aod::McFwdTrackLabels*)nullptr); + nullptr); } void processForwardMC(ForwardTracks const& fwdTracks, @@ -2116,7 +2116,7 @@ struct UpcCandProducer { createCandidatesFwdGlobal(fwdTracks, fwdTrkClusters, ambFwdTracks, bcs, collisions, ft0s, fdds, fv0as, zdcs, - (o2::aod::McFwdTrackLabels*)nullptr); + nullptr); } void processForwardGlobalMC(ForwardTracks const& fwdTracks, diff --git a/PWGUD/TableProducer/dgCandProducer.cxx b/PWGUD/TableProducer/dgCandProducer.cxx index 97a3c423c3e..284f48f5e8e 100644 --- a/PWGUD/TableProducer/dgCandProducer.cxx +++ b/PWGUD/TableProducer/dgCandProducer.cxx @@ -456,7 +456,7 @@ struct DgCandProducer { ccdb->setFatalWhenNull(false); // DGCuts - diffCuts = (DGCutparHolder)DGCuts; + diffCuts = DGCutparHolder(DGCuts); // add histograms for the different process functions histPointers.clear(); diff --git a/PWGUD/TableProducer/muonEfficiencyProducer.cxx b/PWGUD/TableProducer/muonEfficiencyProducer.cxx new file mode 100644 index 00000000000..267c9bb55fd --- /dev/null +++ b/PWGUD/TableProducer/muonEfficiencyProducer.cxx @@ -0,0 +1,328 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file muonEfficiencyProducer.cxx +/// \brief Compact forward-muon skim for offline MCH and MCH-MID efficiency studies +/// \author Nazar Burmasov (JINR) + +#include "Common/CCDB/EventSelectionParams.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +namespace o2::aod::mcheffskim +{ +DECLARE_SOA_COLUMN(RunNumber, runNumber, int32_t); +DECLARE_SOA_COLUMN(ReferenceBC, referenceBC, int64_t); +DECLARE_SOA_COLUMN(FT0AAmplitude, ft0aAmplitude, float); +DECLARE_SOA_COLUMN(FT0CAmplitude, ft0cAmplitude, float); +DECLARE_SOA_COLUMN(FV0AAmplitude, fv0aAmplitude, float); +DECLARE_SOA_COLUMN(CollisionId, collisionId, int32_t); // -1 for ambiguous tracks +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Sign, sign, int8_t); +DECLARE_SOA_COLUMN(RAbs, rAbs, float); +DECLARE_SOA_COLUMN(PDca, pDca, float); +DECLARE_SOA_COLUMN(Chi2Ndf, chi2Ndf, float); // -1 if propagation failed +DECLARE_SOA_COLUMN(NClusters, nClusters, int8_t); +DECLARE_SOA_COLUMN(MchBitMap, mchBitMap, uint16_t); +DECLARE_SOA_COLUMN(MidBitMap, midBitMap, uint8_t); +DECLARE_SOA_COLUMN(MidBoards, midBoards, uint32_t); +DECLARE_SOA_COLUMN(Chi2MatchMCHMID, chi2MatchMCHMID, float); // -1 for MCH-only tracks +DECLARE_SOA_COLUMN(TrackTime, trackTime, float); // ns relative to referenceBC +DECLARE_SOA_COLUMN(TrackTimeRes, trackTimeRes, float); +} // namespace o2::aod::mcheffskim + +namespace o2::aod +{ +DECLARE_SOA_TABLE(MchEffBCs, "AOD", "MCHEFFBC", + o2::soa::Index<>, + mcheffskim::RunNumber, mcheffskim::ReferenceBC, + mcheffskim::FT0AAmplitude, mcheffskim::FT0CAmplitude, + mcheffskim::FV0AAmplitude); +} // namespace o2::aod + +namespace o2::aod::mcheffskim +{ +DECLARE_SOA_INDEX_COLUMN(MchEffBC, mchEffBC); // links a muon to the compact BC table +} // namespace o2::aod::mcheffskim + +namespace o2::aod +{ +DECLARE_SOA_TABLE(MchEffSkims, "AOD", "MCHEFFSKIM", + o2::soa::Index<>, mcheffskim::MchEffBCId, + mcheffskim::CollisionId, + mcheffskim::Pt, mcheffskim::Eta, mcheffskim::Phi, mcheffskim::Sign, + mcheffskim::RAbs, mcheffskim::PDca, mcheffskim::Chi2Ndf, + mcheffskim::NClusters, mcheffskim::MchBitMap, + mcheffskim::MidBitMap, mcheffskim::MidBoards, + mcheffskim::Chi2MatchMCHMID, + mcheffskim::TrackTime, mcheffskim::TrackTimeRes); +} // namespace o2::aod + +struct MuonEfficiencyProducer { + static constexpr float MinEta = -4.f; + static constexpr float MaxEta = -2.5f; + static constexpr float NoAmplitude = -1.f; + static constexpr float MaxFITTime = 30.f; + static constexpr int BCsPerOrbit = constants::lhc::LHCMaxBunches; + + using Tracks = soa::Join; + using Matrix55 = ROOT::Math::SMatrix>; + using Vector5 = ROOT::Math::SVector; + + Produces outputBCs; + Produces output; + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + Service ccdb{}; + globaltracking::MatchGlobalFwd matcher; + bool conditionsInitialized = false; + std::bitset collidingBCs; + float zncBBLower = 0.f; + float zncBBUpper = 0.f; + + Configurable minPt{"minPt", 0.4f, "Minimum track pT (Gev/c)"}; + Configurable minRAbs{"minRAbs", 17.6f, "Minimum absorber-end radius (cm)"}; + Configurable maxRAbs{"maxRAbs", 89.5f, "Maximum absorber-end radius (cm)"}; + Configurable maxPDca{"maxPDca", 400.f, "Maximum stored pDCA (GeV cm/c)"}; + Configurable maxChi2{"maxChi2", 100.f, "Maximum MCH chi2/ndf"}; + + void init(InitContext&) + { + ccdb->setLocalObjectValidityChecking(); + const AxisSpec runAxis{1, 0.5, 1.5, "Run"}; + const AxisSpec bcAxis{BCsPerOrbit, -0.5, BCsPerOrbit - 0.5, "BC in orbit"}; + histos.add("hCounterTVX", "TVX;Run;BC in orbit;Counts", HistType::kTH2D, {runAxis, bcAxis}); + histos.add("hCounterTCE", "TCE;Run;BC in orbit;Counts", HistType::kTH2D, {runAxis, bcAxis}); + histos.add("hCounterZNC", "ZNC;Run;BC in orbit;Counts", HistType::kTH2D, {runAxis, bcAxis}); + histos.get(HIST("hCounterTVX"))->SetCanExtend(TH1::kXaxis); + histos.get(HIST("hCounterTCE"))->SetCanExtend(TH1::kXaxis); + histos.get(HIST("hCounterZNC"))->SetCanExtend(TH1::kXaxis); + } + + void initRunConditions(int run) + { + if (conditionsInitialized) { + return; + } + const auto runInfo = parameters::AggregatedRunInfo::buildAggregatedRunInfo(ccdb->instance(), run); + const auto ts = (runInfo.sor + runInfo.eor) / 2; + auto* grp = ccdb->getForTimeStamp("GLO/Config/GRPMagField", ts); + if (!grp) { + LOGF(fatal, "GRPMagField not found for run %d", run); + } + base::Propagator::initFieldFromGRP(grp); + if (!base::GeometryManager::isGeometryLoaded()) { + ccdb->get("GLO/Config/GeometryAligned"); + } + mch::TrackExtrap::setField(); + if (!runInfo.grpLHC) { + LOGF(fatal, "GRPLHCIFData not found for run %d", run); + } + collidingBCs = runInfo.grpLHC->getBunchFilling().getBCPattern(); + auto* params = ccdb->getForTimeStamp("EventSelection/EventSelectionParams", ts); + if (!params) { + LOGF(fatal, "EventSelectionParams not found for run %d", run); + } + zncBBLower = params->fZNCBBlower; + zncBBUpper = params->fZNCBBupper; + const auto label = std::to_string(run); + histos.get(HIST("hCounterTVX"))->GetXaxis()->SetBinLabel(1, label.c_str()); + histos.get(HIST("hCounterTCE"))->GetXaxis()->SetBinLabel(1, label.c_str()); + histos.get(HIST("hCounterZNC"))->GetXaxis()->SetBinLabel(1, label.c_str()); + conditionsInitialized = true; + } + + struct Kinematics { + float pt, eta, phi; + }; + + template + bool propagateMCH(const Track& t, const std::array& vtx, const std::array& vtxCov, Kinematics& k) + { + const Vector5 params(t.x(), t.y(), t.phi(), t.tgl(), t.signed1Pt()); + const std::vector covElements{t.cXX(), t.cXY(), t.cYY(), t.cPhiX(), t.cPhiY(), t.cPhiPhi(), + t.cTglX(), t.cTglY(), t.cTglPhi(), t.cTglTgl(), + t.c1PtX(), t.c1PtY(), t.c1PtPhi(), t.c1PtTgl(), t.c1Pt21Pt2()}; + const Matrix55 cov(covElements.begin(), covElements.end()); + dataformats::GlobalFwdTrack source; + source.setParameters(params); + source.setZ(t.z()); + source.setCovariances(cov); + auto mchTrack = matcher.FwdtoMCH(source); + if (!mch::TrackExtrap::extrapToVertex(mchTrack, vtx[0], vtx[1], vtx[2], vtxCov[0], vtxCov[1])) { + return false; + } + const auto propagated = matcher.MCHtoFwd(mchTrack); + k = {.pt = static_cast(propagated.getPt()), .eta = static_cast(propagated.getEta()), .phi = static_cast(propagated.getPhi())}; + return true; + } + + void process(aod::BCs const& bcs, aod::FT0s const& ft0s, aod::FV0As const& fv0as, + aod::Zdcs const& zdcs, + Tracks const& tracks, aod::Collisions const& collisions, + aod::AmbiguousFwdTracks const& ambiguous) + { + if (bcs.size() == 0) { + return; + } + const int run = bcs.begin().runNumber(); + initRunConditions(run); + std::vector ft0TriggersByBC(bcs.size(), 0); + std::vector zncIsBBByBC(bcs.size(), 0); + std::vector ft0aByBC(bcs.size(), NoAmplitude); + std::vector ft0cByBC(bcs.size(), NoAmplitude); + std::vector fv0aByBC(bcs.size(), NoAmplitude); + std::vector skimBCIds(bcs.size(), -1); + for (const auto& ft0 : ft0s) { + ft0TriggersByBC[ft0.bcId()] |= ft0.triggerMask(); + if (ft0.timeA() < MaxFITTime) { + ft0aByBC[ft0.bcId()] = ft0.sumAmpA(); + } + if (ft0.timeC() < MaxFITTime) { + ft0cByBC[ft0.bcId()] = ft0.sumAmpC(); + } + } + for (const auto& fv0 : fv0as) { + if (fv0.time() >= MaxFITTime) { + continue; + } + float amplitude = 0.f; + for (const float a : fv0.amplitude()) { + if (a > 0.f) { + amplitude += a; + } + } + fv0aByBC[fv0.bcId()] = amplitude; + } + + for (const auto& zdc : zdcs) { + if (zdc.timeZNC() > zncBBLower && zdc.timeZNC() < zncBBUpper) { + zncIsBBByBC[zdc.bcId()] = 1; + } + } + + auto hTVX = histos.get(HIST("hCounterTVX")); + auto hTCE = histos.get(HIST("hCounterTCE")); + auto hZNC = histos.get(HIST("hCounterZNC")); + for (const auto& bc : bcs) { + const auto slot = bc.globalBC() % BCsPerOrbit; + if (!collidingBCs[slot]) { + continue; + } + const auto mask = ft0TriggersByBC[bc.globalIndex()]; + const bool tvx = (mask & (1u << o2::ft0::Triggers::bitVertex)) != 0; + if (tvx) { + hTVX->Fill(1., static_cast(slot)); + if ((mask & (1u << o2::ft0::Triggers::bitCen)) != 0) { + hTCE->Fill(1., static_cast(slot)); + } + } + if (zncIsBBByBC[bc.globalIndex()] != 0u) { + hZNC->Fill(1., static_cast(slot)); + } + } + + std::vector ambiguousBCIds(tracks.size(), -1); + for (const auto& entry : ambiguous) { + const auto& ids = entry.bcIds(); + if (ids.empty()) { + continue; + } + ambiguousBCIds[entry.fwdtrackId()] = *ids.begin(); + } + + for (const auto& t : tracks) { + const auto type = t.trackType(); + if (type != aod::fwdtrack::MuonStandaloneTrack && type != aod::fwdtrack::MCHStandaloneTrack) { + continue; + } + const float rAbs = t.rAtAbsorberEnd(); + if (rAbs < minRAbs || rAbs > maxRAbs || t.pDca() > maxPDca || t.chi2() > maxChi2) { + continue; + } + Kinematics k{.pt = t.pt(), .eta = t.eta(), .phi = t.phi()}; + std::array vtx{0.f, 0.f, 0.f}; + std::array vtxCov{0.f, 0.f}; + int32_t referenceBCId = ambiguousBCIds[t.globalIndex()]; + if (t.has_collision()) { + const auto col = collisions.iteratorAt(t.collisionId()); + vtx = {col.posX(), col.posY(), col.posZ()}; + vtxCov = {col.covXX(), col.covYY()}; + referenceBCId = col.bcId(); + } + if (!propagateMCH(t, vtx, vtxCov, k)) { + continue; + } + if (k.eta < MinEta || k.eta > MaxEta) { + continue; + } + if (k.pt < minPt) { + continue; + } + int32_t skimBCId = -1; + if (referenceBCId >= 0) { + skimBCId = skimBCIds[referenceBCId]; + if (skimBCId < 0) { + outputBCs(run, bcs.iteratorAt(referenceBCId).globalBC(), + ft0aByBC[referenceBCId], ft0cByBC[referenceBCId], fv0aByBC[referenceBCId]); + skimBCId = outputBCs.lastIndex(); + skimBCIds[referenceBCId] = skimBCId; + } + } + output(skimBCId, t.collisionId(), k.pt, k.eta, k.phi, static_cast(t.signed1Pt() > 0 ? 1 : -1), + rAbs, t.pDca(), t.chi2(), t.nClusters(), t.mchBitMap(), t.midBitMap(), t.midBoards(), + type == aod::fwdtrack::MuonStandaloneTrack ? t.chi2MatchMCHMID() : -1.f, t.trackTime(), t.trackTimeRes()); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{adaptAnalysisTask(context)}; +} diff --git a/PWGUD/TableProducer/tauEventTableProducer.cxx b/PWGUD/TableProducer/tauEventTableProducer.cxx index 2538415540f..d7e15959b85 100644 --- a/PWGUD/TableProducer/tauEventTableProducer.cxx +++ b/PWGUD/TableProducer/tauEventTableProducer.cxx @@ -513,7 +513,7 @@ struct TauEventTableProducer { if (verboseInfo) printLargeMessage("Truth collision has more than 1 reco collision. Skipping this event."); histos.get(HIST("Truth/hTroubles"))->Fill(1); - problem = true; + // problem = true; // FIXME: unused continue; } // grap reco-level collision diff --git a/PWGUD/TableProducer/tauThreeProngEventTableProducer.cxx b/PWGUD/TableProducer/tauThreeProngEventTableProducer.cxx index e0967520da8..8afc2b486c8 100644 --- a/PWGUD/TableProducer/tauThreeProngEventTableProducer.cxx +++ b/PWGUD/TableProducer/tauThreeProngEventTableProducer.cxx @@ -67,7 +67,10 @@ enum MyRecoProblem { TOO_MANY_DAUGHTERS = 2, // more than 6 daughters from 2 taus TWO_TRACKS = 3, // more than 1 associated track to MC particle (tau daughter) NO_TRACK = 4, // No associated track to MC particle (tau daughter) - MIXED_TRACKS = 5 // 4 (or 6) tracks reconstructed but from 2 collisions + MIXED_TRACKS = 5, // 4 (or 6) tracks reconstructed but from 2 collisions + BAD_GAP = 6, // rapidity gap out of range 0-2 + GAP_NE_2 = 7, // rapidity gap != 2 + FIT_VETO = 8 // FIT not empty (with thresholds) }; enum MyParticle { @@ -171,6 +174,7 @@ struct TauThreeProngEventTableProducer { // ConfigurableAxis minvAxis{"minvAxis", {100, 0.5, 5.0}, "M_{inv} (GeV/#it{c}^{2})"}; // ConfigurableAxis phiAxis{"phiAxis", {120, 0., 3.2}, "#phi"}; Configurable verbose{"verbose", {}, "Additional print outs"}; + Configurable> generatorIds{"generatorIds", std::vector{31}, "MC generatorIds to process"}; // cut selection configurables // Configurable zvertexcut{"zvertexcut", 10., "Z vertex cut"}; @@ -184,7 +188,7 @@ struct TauThreeProngEventTableProducer { // Configurable maxNsigmaPrVetocut{"maxNsigmaPrVetocut", 3., "max Nsigma for Proton veto cut"}; // Configurable maxNsigmaKaVetocut{"maxNsigmaKaVetocut", 3., "max Nsigma for Kaon veto cut"}; // Configurable minPtEtrkcut{"minPtEtrkcut", 0.25, "min Pt for El track cut"}; - Configurable minTrkPtcut{"minTrkPtcut", 0.1, "min Pt for each charged track cut, default=100MeV/c"}; + Configurable minTrkPtcut{"minTrkPtcut", 0.10, "min Pt for each charged track cut, default=100MeV/c"}; Configurable nPVtrackscut{"nPVtrackscut", 4, "number of PV contributors, default=4"}; Configurable mFITvetoFlag{"mFITvetoFlag", true, "To apply FIT veto"}; Configurable mFITvetoWindow{"mFITvetoWindow", 2, "FIT veto window"}; @@ -218,7 +222,7 @@ struct TauThreeProngEventTableProducer { ////using FullSGUDCollisions = soa::Join; ////using FullSGUDCollision = FullSGUDCollisions::iterator; using FullMCUDTracks = soa::Join; - using FullMCSGUDCollisions = soa::Join; + using FullMCSGUDCollisions = soa::Join; // using FullMCSGUDCollision = FullMCSGUDCollisions::iterator; // init @@ -256,27 +260,25 @@ struct TauThreeProngEventTableProducer { registrySkim.add("skim/gapSide", ";Gap;events", {HistType::kTH1D, {{10, -1., 9.}}}); registrySkim.add("skim/trueGapSide", ";TrueGap;events", {HistType::kTH1D, {{10, -1., 9.}}}); - registrySkim.add("skim/etaTrk", ";#eta^{trk};events", {HistType::kTH1F, {{100, -1.5, 1.5}}}); - registrySkim.add("skim/ptTrk", ";p_{T}^{trk};events", {HistType::kTH1F, {{100, 0., 5.}}}); - registrySkim.add("skim/phiTrk", ";#phi^{trk};events", {HistType::kTH1F, {{128, -3.2, 3.2}}}); registrySkim.add("skim/nTof", ";N_{TOFtrk};events", {HistType::kTH1F, {{10, -1., 9.}}}); } if (doprocessMonteCarlo) { - registrySkim.add("skim/efficiencyMC", ";efficiency;events", {HistType::kTH1D, {{15, 0., 15.}}}); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(1, "1: All"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(2, "2: N^{#tau}=2"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(3, "3: |y^{#tau}| <= 0.9"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(4, "4: |#eta^{ch}|<0.9"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(5, "5: 4 or 6 trk"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(6, "e+3#pi"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(7, "#mu+3#pi"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(8, "#pi+3#pi"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(9, "6#pi"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(10, "rec"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(11, "rec e+3#pi"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(12, "rec #mu+3#pi"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(13, "rec #pi+3#pi"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(14, "rec 6#pi"); + registrySkim.add("skim/efficiencyMC", ";efficiency;events", {HistType::kTH1D, {{16, -1., 15.}}}); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(1, "All"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(2, "GenID"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(3, "N^{#tau}=2"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(4, "|y^{#tau}| <= 0.9"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(5, "|#eta^{ch}|<0.9"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(6, "4 or 6 trk"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(7, "e+3#pi"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(8, "#mu+3#pi"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(9, "#pi+3#pi"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(10, "6#pi"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(11, "rec"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(12, "rec e+3#pi"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(13, "rec #mu+3#pi"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(14, "rec #pi+3#pi"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(15, "rec 6#pi"); registrySkim.add("skim/problemMC", ";problem;events", {HistType::kTH1D, {{10, 0., 10.}}}); @@ -293,24 +295,42 @@ struct TauThreeProngEventTableProducer { registrySkim.add("skim/daughterEtaMC", ";#eta^{daughter};events", {HistType::kTH1F, {{100, -4., 4.}}}); registrySkim.add("skim/daughterPtMC", ";p_{T}^{daughter};events", {HistType::kTH1F, {{100, 0, 5.0}}}); } + // common histos for doprocessDoSkim and doprocessMonteCarlo + if (doprocessDoSkim || doprocessMonteCarlo) { + registrySkim.add("rec/zvertex_SGP", ";#it{V}_{z};events", {HistType::kTH1D, {{100, -15., 15.}}}); + registrySkim.add("rec/nPVtrack_SGP", ";#it{N}_{PVtrack};events", {HistType::kTH1D, {{100, 0., 100.}}}); + registrySkim.add("rec/qtot_SGP", ";#it{Q}_{tot};events", {HistType::kTH1D, {{30, -15., 15.}}}); + // registrySkim.add("skim/etaTrk", ";#eta^{trk};events", {HistType::kTH1F, {{100, -1.5, 1.5}}}); + // registrySkim.add("skim/ptTrk", ";p_{T}^{trk};events", {HistType::kTH1F, {{100, 0., 5.}}}); + // registrySkim.add("skim/phiTrk", ";#phi^{trk};events", {HistType::kTH1F, {{128, -3.2, 3.2}}}); + registrySkim.add("rec/phiPVtrack_SGP", ";#phi^{PVtrack};events", {HistType::kTH1F, {{128, -3.2, 3.2}}}); + registrySkim.add("rec/etaPVtrack_SGP", ";#eta^{PVtrack};events", {HistType::kTH1F, {{100, -1.5, 1.5}}}); + registrySkim.add("rec/ptPVtrack_SGP", ";#it{p}_{T}^{PVtrack};events", {HistType::kTH1F, {{100, 0, 5.0}}}); + registrySkim.add("rec/nTOFtrack_SGP", ";#it{N}_{TOFtrack};events", {HistType::kTH1F, {{50, 0, 50}}}); + registrySkim.add("rec/FT0A_SGP", ";#it{E}_{FT0A};events", {HistType::kTH1F, {{100, 0, 500}}}); + registrySkim.add("rec/FT0C_SGP", ";#it{E}_{FT0C};events", {HistType::kTH1F, {{100, 0, 500}}}); + registrySkim.add("rec/FV0A_SGP", ";#it{E}_{FV0A};events", {HistType::kTH1F, {{100, 0, 500}}}); + } + if (doprocessGenerated) { - registrySkim.add("gen/efficiencyMC", ";efficiency;events", {HistType::kTH1D, {{15, 0., 15.}}}); + registrySkim.add("gen/efficiencyMC", ";efficiency;events", {HistType::kTH1D, {{16, -1., 15.}}}); registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(1, "All"); - registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(2, "N^{#tau}=2"); - registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(3, "|y^{#tau}| <=0.9"); - registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(4, "|#eta^{ch}|<=0.9"); - registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(5, "4 or 6 trk"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(2, "GenID"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(3, "N^{#tau}=2"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(4, "|y^{#tau}| <=0.9"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(5, "|#eta^{ch}|<=0.9"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(6, "4 or 6 trk"); // registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(6, "6: 4 trk"); // registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(7, "7: 6 trk"); - registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(6, "e+3#pi"); - registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(7, "#mu+3#pi"); - registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(8, "#pi+3#pi"); - registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(9, "6#pi"); - registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(10, "rec"); - registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(11, "rec e+3#pi"); - registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(12, "rec #mu+3#pi"); - registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(13, "rec #pi+3#pi"); - registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(14, "rec 6#pi"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(7, "e+3#pi"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(8, "#mu+3#pi"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(9, "#pi+3#pi"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(10, "6#pi"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(11, "rec"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(12, "rec e+3#pi"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(13, "rec #mu+3#pi"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(14, "rec #pi+3#pi"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(15, "rec 6#pi"); registrySkim.add("gen/problemMC", ";problem;events", {HistType::kTH1D, {{10, 0., 10.}}}); registrySkim.get(HIST("gen/problemMC"))->GetXaxis()->SetBinLabel(1, "1: NoProblem"); @@ -752,44 +772,19 @@ struct TauThreeProngEventTableProducer { registrySkim.get(HIST("skim/efficiency"))->Fill(0., 1.); - int gapSide = dgcand.gapSide(); - registrySkim.get(HIST("skim/gapSide"))->Fill(gapSide); - // if (gapSide < 0 || gapSide > 2) - if (gapSide < o2::aod::sgselector::SingleGapA || gapSide > o2::aod::sgselector::DoubleGap) - return; - registrySkim.get(HIST("skim/efficiency"))->Fill(1., 1.); - - int truegapSide = sgSelector.trueGap(dgcand, cutFV0, cutFT0A, cutFT0C, cutZDC); - gapSide = truegapSide; - registrySkim.get(HIST("skim/trueGapSide"))->Fill(gapSide); - if (gapSide != mGapSide) - return; - registrySkim.get(HIST("skim/efficiency"))->Fill(2., 1.); - - // zdc information - float energyZNA = dgcand.energyCommonZNA(); - float energyZNC = dgcand.energyCommonZNC(); - if (energyZNA < 0) - energyZNA = -1.; - if (energyZNC < 0) - energyZNC = -1.; - float timeZNA = dgcand.timeZNA(); - float timeZNC = dgcand.timeZNC(); - + // set up PV track features int nTofTrk = 0; int nEtaIn15 = 0; int npT100 = 0; - // // int qtot = 0; - // int8_t qtot = 0; - // TLorentzVector p; + int qtot = 0; ROOT::Math::LorentzVector> p; for (const auto& trk : PVContributors) { - // qtot += trk.sign(); + qtot += trk.sign(); // p.SetXYZM(trk.px(), trk.py(), trk.pz(), MassPiPlus); p.SetXYZT(trk.px(), trk.py(), trk.pz(), RecoDecay::e(trk.px(), trk.py(), trk.pz(), MassPiPlus)); - registrySkim.get(HIST("skim/etaTrk"))->Fill(p.Eta()); - registrySkim.get(HIST("skim/ptTrk"))->Fill(trk.pt()); - registrySkim.get(HIST("skim/phiTrk"))->Fill(p.Phi()); + registrySkim.get(HIST("rec/etaPVtrack_SGP"))->Fill(p.Eta()); + registrySkim.get(HIST("rec/ptPVtrack_SGP"))->Fill(trk.pt()); + registrySkim.get(HIST("rec/phiPVtrack_SGP"))->Fill(p.Phi()); if (std::abs(p.Eta()) < trkEtacut) nEtaIn15++; // 0.9 is a default @@ -799,8 +794,39 @@ struct TauThreeProngEventTableProducer { nTofTrk++; } // end of loop over PV tracks + // + // plots after Single Gap producer without any further cuts + // + registrySkim.get(HIST("rec/zvertex_SGP"))->Fill(dgcand.posZ()); + registrySkim.get(HIST("rec/nPVtrack_SGP"))->Fill(PVContributors.size()); + registrySkim.get(HIST("rec/qtot_SGP"))->Fill(qtot); + registrySkim.get(HIST("rec/nTOFtrack_SGP"))->Fill(nTofTrk); + registrySkim.get(HIST("rec/FT0A_SGP"))->Fill(dgcand.totalFT0AmplitudeA()); + registrySkim.get(HIST("rec/FT0C_SGP"))->Fill(dgcand.totalFT0AmplitudeC()); + registrySkim.get(HIST("rec/FV0A_SGP"))->Fill(dgcand.totalFV0AmplitudeA()); + // + // end of plots after Single Gap producer + // + + // check number of PV tracks if (PVContributors.size() != nPVtrackscut) // 4 or 6 return; + registrySkim.get(HIST("skim/efficiency"))->Fill(1., 1.); + + // check gap side + int gapSide = dgcand.gapSide(); + registrySkim.get(HIST("skim/gapSide"))->Fill(gapSide); + // if (gapSide < 0 || gapSide > 2) + if (gapSide < o2::aod::sgselector::SingleGapA || gapSide > o2::aod::sgselector::DoubleGap) + return; + registrySkim.get(HIST("skim/efficiency"))->Fill(2., 1.); + + // check true gap side + int truegapSide = sgSelector.trueGap(dgcand, cutFV0, cutFT0A, cutFT0C, cutZDC); + gapSide = truegapSide; + registrySkim.get(HIST("skim/trueGapSide"))->Fill(gapSide); + if (gapSide != mGapSide) + return; registrySkim.get(HIST("skim/efficiency"))->Fill(3., 1.); if (nEtaIn15 != nPVtrackscut) // 4 or 6 @@ -840,6 +866,16 @@ struct TauThreeProngEventTableProducer { return; registrySkim.get(HIST("skim/efficiency"))->Fill(7., 1.); + // zdc information + float energyZNA = dgcand.energyCommonZNA(); + float energyZNC = dgcand.energyCommonZNC(); + if (energyZNA < 0) + energyZNA = -1.; + if (energyZNC < 0) + energyZNC = -1.; + float timeZNA = dgcand.timeZNA(); + float timeZNC = dgcand.timeZNC(); + // RCT variable int rct = 0; rct = isGoodRCTflag(dgcand); @@ -1133,6 +1169,11 @@ struct TauThreeProngEventTableProducer { if (verbose) LOGF(info, " UDMcCollision size %d, Collisions size %d, UDtracks %d, UDMcParticles %d", mcCollisions.size(), collisions.size(), tracks.size(), mcParticles.size()); + // temporary variables needed for reconstruction + int gapSide = -10; + int truegapSide = -10; + bool isFITveto = false; + // temporary variables // float tmpRapidity = -999.; float trueTauRapidity[2] = {-999., -999.}; @@ -1149,9 +1190,27 @@ struct TauThreeProngEventTableProducer { // start loop over generated collisions for (const auto& mccoll : mcCollisions) { + // all collisions if (verbose) LOGF(info, "-- McColl GID %d", mccoll.globalIndex()); - registrySkim.get(HIST("skim/efficiencyMC"))->Fill(0., 1.); // all MC collisions + registrySkim.get(HIST("skim/efficiencyMC"))->Fill(-1., 1.); // all MC collisions + + // check Generator ID, for taus(1+3,3+3): GenID=31,32,33,34,35 + if (verbose) { + LOGF(info, "generatorId desired (0) %d, in MC %d", generatorIds->at(0), mccoll.generatorsID()); + } + + if (std::find(generatorIds->begin(), generatorIds->end(), mccoll.generatorsID()) != generatorIds->end()) { + registrySkim.get(HIST("skim/efficiencyMC"))->Fill(0., 1.); // GenID + if (verbose) { + LOGF(info, "Event with good generatorID %d", mccoll.generatorsID()); + } + } else { + if (verbose) { + LOGF(info, "Event with bad generatorID %d", mccoll.generatorsID()); + } + continue; + } // set up default values per colission trueTauX[0] = -999.; @@ -1367,7 +1426,9 @@ struct TauThreeProngEventTableProducer { float trueDaugZ[6] = {-998., -998., -998., -998., -998., -998.}; int trueDaugPdgCode[6] = {-999, -999, -999, -999, -999, -999}; - MyRecoProblem problem = NO_PROBLEM; + bool problemBits[8] = {false, false, false, false, false, false, false, false}; + uint8_t problemBitsFlag = 0; + // MyRecoProblem problem = NO_PROBLEM; registrySkim.get(HIST("skim/problemMC"))->Fill(NO_PROBLEM); // tau tau event type @@ -1424,7 +1485,8 @@ struct TauThreeProngEventTableProducer { // LOGF(info, "Truth collision has more than 1 reco collision. Skipping this event."); // histos.get(HIST("Truth/hTroubles"))->Fill(1); // problem = true; - problem = MANY_RECO; + problemBits[MANY_RECO - 1] = true; + // problem = MANY_RECO; registrySkim.get(HIST("skim/problemMC"))->Fill(MANY_RECO); // continue; } else { @@ -1432,12 +1494,77 @@ struct TauThreeProngEventTableProducer { LOGF(info, "---- Truth collision has 1 reco collision. Record event."); } + // To fill bcFlags and problemFlags + const int offset = 1; + // grab reco-level collision for (const auto& collFromMcColl : collFromMcColls) { if (verbose) { LOGF(info, "---- ###### Collision %d #########", collFromMcColl.index()); LOGF(info, "---- Coll GID %d, Vertex (%f,%f,%f)", collFromMcColl.globalIndex(), collFromMcColl.posX(), collFromMcColl.posY(), collFromMcColl.posZ()); } + + // + // part neede to mimic reconstruction selection criteria + // + gapSide = collFromMcColl.gapSide(); + // registrySkim.get(HIST("skim/gapSide"))->Fill(gapSide); + if (gapSide < o2::aod::sgselector::SingleGapA || gapSide > o2::aod::sgselector::DoubleGap) { // if (gapSide < 0 || gapSide > 2) + problemBits[BAD_GAP - 1] = true; + } + // return; + // registrySkim.get(HIST("skim/efficiency"))->Fill(1., 1.); + + truegapSide = sgSelector.trueGap(collFromMcColl, cutFV0, cutFT0A, cutFT0C, cutZDC); + gapSide = truegapSide; + // registrySkim.get(HIST("skim/trueGapSide"))->Fill(gapSide); + if (gapSide != mGapSide) { + problemBits[GAP_NE_2 - 1] = true; + } + // return; + // registrySkim.get(HIST("skim/efficiency"))->Fill(2., 1.); + + // n PV contributors + // if (PVContributors.size() != nPVtrackscut) // 4 or 6 + // return; + // if (nEtaIn15 != nPVtrackscut) // 4 or 6 + // return; + // registrySkim.get(HIST("skim/efficiency"))->Fill(4., 1.); + // + // if (npT100 != nPVtrackscut) // 4 or 6 + // return; + // registrySkim.get(HIST("skim/efficiency"))->Fill(5., 1.); + // + // registrySkim.get(HIST("skim/nTof"))->Fill(nTofTrk); + // if (nTofTrk < nTofTrkMinCut) + // return; + // registrySkim.get(HIST("skim/efficiency"))->Fill(6., 1.); + + // + // FIT informaton + // + auto bitMin = 16 - mFITvetoWindow; // default is +- 2 bc (2 bit) + auto bitMax = 16 + mFITvetoWindow; + isFITveto = false; + // check FIT information + for (auto bit = bitMin; bit <= bitMax; bit++) { + if (TESTBIT(collFromMcColl.bbFT0Apf(), bit)) + isFITveto = true; + if (TESTBIT(collFromMcColl.bbFT0Cpf(), bit)) + isFITveto = true; + if (useFV0ForVeto && TESTBIT(collFromMcColl.bbFV0Apf(), bit)) + isFITveto = true; + if (useFDDAForVeto && TESTBIT(collFromMcColl.bbFDDApf(), bit)) + isFITveto = true; + if (useFDDCForVeto && TESTBIT(collFromMcColl.bbFDDCpf(), bit)) + isFITveto = true; + } // end of loop over FIT bits + if (mFITvetoFlag && isFITveto) { + problemBits[FIT_VETO - 1] = true; + } + // return; + // registrySkim.get(HIST("skim/efficiency"))->Fill(7., 1.); + // auto const& collFromMcColl = collFromMcColls.iteratorAt(0); // grab tracks from the reco-level collision to get info to match measured data tables (processDataSG function) auto const& trksFromColl = tracks.sliceBy(trackPerCollision, collFromMcColl.globalIndex()); @@ -1477,7 +1604,7 @@ struct TauThreeProngEventTableProducer { bcSels[6] = collFromMcColl.zVtxFT0vPV(); bcSels[7] = collFromMcColl.vtxITSTPC(); - const int offset = 1; + // const int offset = 1; bcSelBits = bcSels[0]; // initialization for (int ibit = 1; ibit < nBitsMax; ibit++) { bcSelBits = (bcSelBits << offset); // shift by 1 position towards left @@ -1498,6 +1625,20 @@ struct TauThreeProngEventTableProducer { // timesFIT[1] = collFromMcColl.timeFT0C(); // timesFIT[2] = collFromMcColl.timeFV0A(); + // + // plots after Single Gap producer without any further cuts + // + registrySkim.get(HIST("rec/zvertex_SGP"))->Fill(zVertex); + registrySkim.get(HIST("rec/nPVtrack_SGP"))->Fill(nPVcontrib); + // registrySkim.get(HIST("rec/qtot_SGP"))->Fill(qtot); + // registrySkim.get(HIST("rec/nTOFtrack_SGP"))->Fill(nTofTrk); + registrySkim.get(HIST("rec/FT0A_SG"))->Fill(amplitudesFIT[0]); + registrySkim.get(HIST("rec/FT0C_SGP"))->Fill(amplitudesFIT[1]); + registrySkim.get(HIST("rec/FV0A_SGP"))->Fill(amplitudesFIT[2]); + // + // end of plots after Single Gap producer + // + // get particles associated to generated collision auto const& partsFromMcColl = mcParticles.sliceBy(partPerMcCollision, mccoll.globalIndex()); if (verbose) @@ -1535,7 +1676,9 @@ struct TauThreeProngEventTableProducer { LOGF(info, "------ Truth collision has more than 6 charged daughters from 2 taus. Breaking the daughter loop."); // histos.get(HIST("Truth/hTroubles"))->Fill(3); // problem = true; - problem = TOO_MANY_DAUGHTERS; + + problemBits[TOO_MANY_DAUGHTERS - 1] = true; + // problem = TOO_MANY_DAUGHTERS; registrySkim.get(HIST("skim/problemMC"))->Fill(TOO_MANY_DAUGHTERS); break; @@ -1581,7 +1724,9 @@ struct TauThreeProngEventTableProducer { if (counter > 1) { if (verbose) LOGF(info, "------ Daughter has more than 1 associated track. Ntracks = %d. Skipping this daughter.", counter); - problem = TWO_TRACKS; + + problemBits[TWO_TRACKS - 1] = true; + // problem = TWO_TRACKS; registrySkim.get(HIST("skim/problemMC"))->Fill(TWO_TRACKS); continue; } @@ -1589,7 +1734,8 @@ struct TauThreeProngEventTableProducer { // what if no track is associated to mc particle??? if (verbose) LOGF(info, "----- Daughter has no associated track. Ntracks = %d. Skipping this daughter.", tracksFromDaughter.size()); - problem = NO_TRACK; + problemBits[NO_TRACK - 1] = true; + // problem = NO_TRACK; registrySkim.get(HIST("skim/problemMC"))->Fill(NO_TRACK); continue; } @@ -1638,6 +1784,13 @@ struct TauThreeProngEventTableProducer { } // end of loop over MC particles } // end of loop over collisions associated to MC collision + // set up problem bits + problemBitsFlag = static_cast(problemBits[0]); // initialization + for (int ibit = 1; ibit < nBitsMax; ibit++) { + problemBitsFlag = (problemBitsFlag << offset); // shift by 1 position towards left + problemBitsFlag += static_cast(problemBits[ibit]); // add next bit to the pool + } + // decide the channel and set the variable. trueChannel = trueChannel + countPi0 * 10 + zerothTau * 100; @@ -1674,7 +1827,7 @@ struct TauThreeProngEventTableProducer { mccoll.posZ(), // trueTauX, trueTauY, trueTauZ, trueDaugX, trueDaugY, trueDaugZ, - trueDaugPdgCode, problem); + trueDaugPdgCode, problemBitsFlag); } else if (nChargedDaughtersTau[0] + nChargedDaughtersTau[1] == sixTracks) { // 6 trueTauSixTracks(runNumber, bc, // is it necessary @@ -1706,7 +1859,7 @@ struct TauThreeProngEventTableProducer { mccoll.posZ(), // trueTauX, trueTauY, trueTauZ, trueDaugX, trueDaugY, trueDaugZ, - trueDaugPdgCode, problem); + trueDaugPdgCode, problemBitsFlag); } // end of saving the output } else { // get only the truth information. The reco-level info is left on default @@ -1719,7 +1872,6 @@ struct TauThreeProngEventTableProducer { PROCESS_SWITCH(TauThreeProngEventTableProducer, processMonteCarlo, "Iterate UD tables with simulated data created by SG-Candidate-Producer.", false); // only MC information and what events are reconstructed - void processGenerated(aod::UDMcCollisions const& mcCollisions, aod::UDMcParticles const& mcParticles, FullMCSGUDCollisions const& collisions) @@ -1750,9 +1902,27 @@ struct TauThreeProngEventTableProducer { // start loop over generated collisions for (const auto& mccoll : mcCollisions) { + // all collisions if (verbose) LOGF(info, "-- McColl GID %d", mccoll.globalIndex()); - registrySkim.get(HIST("gen/efficiencyMC"))->Fill(0., 1.); // all MC collisions + registrySkim.get(HIST("gen/efficiencyMC"))->Fill(-1., 1.); // all MC collisions + + // check Generator ID, for taus(1+3,3+3): GenID=31,32,33,34,35 + if (verbose) { + LOGF(info, "generatorId desired (0) %d, in MC %d", generatorIds->at(0), mccoll.generatorsID()); + } + + if (std::find(generatorIds->begin(), generatorIds->end(), mccoll.generatorsID()) != generatorIds->end()) { + registrySkim.get(HIST("gen/efficiencyMC"))->Fill(0., 1.); // GenID + if (verbose) { + LOGF(info, "Event with good generatorID %d", mccoll.generatorsID()); + } + } else { + if (verbose) { + LOGF(info, "Event with bad generatorID %d", mccoll.generatorsID()); + } + continue; + } // set up default values per colission trueTauX[0] = -999.; diff --git a/PWGUD/TableProducer/twoFwdTracksEventTableProducer.cxx b/PWGUD/TableProducer/twoFwdTracksEventTableProducer.cxx new file mode 100644 index 00000000000..1950842a7cf --- /dev/null +++ b/PWGUD/TableProducer/twoFwdTracksEventTableProducer.cxx @@ -0,0 +1,609 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file twoFwdTracksEventTableProducer.cxx +/// \brief Produces derived table with two forward tracks per event from UD tables created by upcCandProducerGlobalMuon +/// +/// \author Roman Lavicka , Austrian Academy of Sciences & SMI +/// \since 02.10.2026 +// + +#include "PWGUD/Core/UPCHelpers.h" +#include "PWGUD/Core/UPCTauCentralBarrelHelperRL.h" +#include "PWGUD/DataModel/TwoFwdTracksEventTables.h" +#include "PWGUD/DataModel/UDTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::constants::physics; + +namespace +{ +constexpr int NumTracks = 2; // the table stores exactly two forward tracks per event +constexpr int DefaultInt = -999; // value of integer columns without information +constexpr float DefaultFloat = -999.f; // value of float columns without information +constexpr uint32_t DefaultMIDBoards = 0; // value of MID-board columns without information + +enum EventSelection { + EvSelAll = 0, + EvSelFV0Veto, + EvSelNumContrib, + EvSelTwoTracks, + EvSelInvariantMass, + EvSelSystemPt, + EvSelOneTrackMomentum, + EvSelBothTracksMomentum, + NEvSels +}; + +enum FwdTrackSelection { + TrkSelAll = 0, + TrkSelType, + TrkSelPt, + TrkSelEta, + TrkSelRabs, + TrkSelPDca, + TrkSelChi2, + TrkSelChi2MatchMCHMFT, + NTrkSels +}; + +enum TruthTrouble { + TroubleTooManyMothers = 1, + TroubleTooManyDaughters, + TroubleNoTrack, + TroubleMoreTracks, + TroubleDifferentCandidates, + NTroubles +}; + +// reco-level information of one row, shared by the measured and the simulated table +struct RecoInfo { + int32_t runNumber{DefaultInt}; + uint64_t bc{0}; + int numContrib{DefaultInt}; + float posX{DefaultFloat}; + float posY{DefaultFloat}; + float posZ{DefaultFloat}; + float totalFV0AmplitudeA{DefaultFloat}; + std::vector amplitudesV0A; + std::vector ampRelBCsV0A; + float energyCommonZNA{DefaultFloat}; + float energyCommonZNC{DefaultFloat}; + float timeZNA{DefaultFloat}; + float timeZNC{DefaultFloat}; + float px[NumTracks] = {DefaultFloat, DefaultFloat}; + float py[NumTracks] = {DefaultFloat, DefaultFloat}; + float pz[NumTracks] = {DefaultFloat, DefaultFloat}; + int sign[NumTracks] = {DefaultInt, DefaultInt}; + float time[NumTracks] = {DefaultFloat, DefaultFloat}; + float timeRes[NumTracks] = {DefaultFloat, DefaultFloat}; + int type[NumTracks] = {DefaultInt, DefaultInt}; + int nClusters[NumTracks] = {DefaultInt, DefaultInt}; + float pDca[NumTracks] = {DefaultFloat, DefaultFloat}; + float rAbs[NumTracks] = {DefaultFloat, DefaultFloat}; + float chi2[NumTracks] = {DefaultFloat, DefaultFloat}; + float chi2MatchMCHMID[NumTracks] = {DefaultFloat, DefaultFloat}; + float chi2MatchMCHMFT[NumTracks] = {DefaultFloat, DefaultFloat}; + int mchBitMap[NumTracks] = {DefaultInt, DefaultInt}; + int midBitMap[NumTracks] = {DefaultInt, DefaultInt}; + uint32_t midBoards[NumTracks] = {DefaultMIDBoards, DefaultMIDBoards}; +}; + +// truth-level information of one row of the simulated table +struct TruthInfo { + int channel{-1}; + bool hasRecoColl{false}; + float motherPx[NumTracks] = {DefaultFloat, DefaultFloat}; + float motherPy[NumTracks] = {DefaultFloat, DefaultFloat}; + float motherPz[NumTracks] = {DefaultFloat, DefaultFloat}; + float daugPx[NumTracks] = {DefaultFloat, DefaultFloat}; + float daugPy[NumTracks] = {DefaultFloat, DefaultFloat}; + float daugPz[NumTracks] = {DefaultFloat, DefaultFloat}; + int daugPdgCode[NumTracks] = {DefaultInt, DefaultInt}; + bool problem{false}; +}; +} // namespace + +struct TwoFwdTracksEventTableProducer { + Produces twoFwdTracks; + Produces trueTwoFwdTracks; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // declare configurables + Configurable verboseInfo{"verboseInfo", false, {"Print general info to terminal; default it false."}}; + + struct : ConfigurableGroup { + Configurable useFV0Veto{"useFV0Veto", true, {"Reject candidates with FV0A activity"}}; + Configurable cutMaxFV0Amp{"cutMaxFV0Amp", 100.f, {"Maximum FV0A amplitude allowed"}}; + Configurable cutFV0RelBcRange{"cutFV0RelBcRange", 0, {"FV0A veto applied in BCs [-range, +range] around the candidate BC; 0 = candidate BC only"}}; + Configurable useNumContrib{"useNumContrib", false, {"Require given number of forward tracks in the candidate (before track selection)"}}; + Configurable cutNumContrib{"cutNumContrib", 2, {"Number of forward tracks required in the candidate"}}; + } cutSample; + + struct : ConfigurableGroup { + Configurable acceptGlobalMuonTrack{"acceptGlobalMuonTrack", true, {"Accept MCH-MID-MFT tracks"}}; + Configurable acceptGlobalForwardTrack{"acceptGlobalForwardTrack", true, {"Accept MCH-MFT tracks"}}; + Configurable acceptMuonStandaloneTrack{"acceptMuonStandaloneTrack", true, {"Accept MCH-MID tracks"}}; + Configurable acceptMCHStandaloneTrack{"acceptMCHStandaloneTrack", true, {"Accept MCH-only tracks"}}; + Configurable applyFwdTrackSelection{"applyFwdTrackSelection", true, {"Apply kinematic and quality cuts defined below on forward tracks"}}; + Configurable cutMinPt{"cutMinPt", 0.f, {"Forward track cut"}}; + Configurable cutMaxPt{"cutMaxPt", 1e10f, {"Forward track cut"}}; + Configurable cutMinEta{"cutMinEta", -4.f, {"Forward track cut"}}; + Configurable cutMaxEta{"cutMaxEta", -2.5f, {"Forward track cut"}}; + Configurable cutMinRabs{"cutMinRabs", 17.6f, {"Forward track cut on radius at the absorber end (cm)"}}; + Configurable cutMaxRabs{"cutMaxRabs", 89.5f, {"Forward track cut on radius at the absorber end (cm)"}}; + Configurable cutMaxPDcaLowRabs{"cutMaxPDcaLowRabs", 594.f, {"Forward track pDCA cut for rAbs below 26.5 cm"}}; + Configurable cutMaxPDcaHighRabs{"cutMaxPDcaHighRabs", 324.f, {"Forward track pDCA cut for rAbs above 26.5 cm"}}; + Configurable cutMaxChi2{"cutMaxChi2", 1e10f, {"Forward track cut on track chi2"}}; + Configurable cutMaxChi2MatchMCHMFT{"cutMaxChi2MatchMCHMFT", 1e10f, {"Forward track cut on MCH-MFT matching chi2 (MFT tracks only)"}}; + } cutFwdTrack; + + struct : ConfigurableGroup { + Configurable preselUseMinMomentumOnBothTracks{"preselUseMinMomentumOnBothTracks", false, {"Both tracks are required to fill requirement on minimum momentum."}}; + Configurable preselMinTrackMomentum{"preselMinTrackMomentum", 0.1, {"Requirement on minimum momentum of the track."}}; + Configurable preselSystemPtCut{"preselSystemPtCut", 2.0, {"By default, cut on maximum system pT."}}; + Configurable preselUseOppositeSystemPtCut{"preselUseOppositeSystemPtCut", false, {"Negates the system pT cut (cut on minimum system pT)."}}; + Configurable preselMinInvariantMass{"preselMinInvariantMass", 2.0, {"Requirement on minimum system invariant mass."}}; + Configurable preselMaxInvariantMass{"preselMaxInvariantMass", 5.0, {"Requirement on maximum system invariant mass."}}; + } cutPreselect; + + using CandidatesFwd = soa::Join; + using CandidateFwd = CandidatesFwd::iterator; + using FwdTracks = soa::Join; + using FwdMcTracks = soa::Join; + + // init + void init(InitContext&) + { + if (verboseInfo) + printMediumMessage("INIT METHOD"); + + histos.add("Reco/hSelections", "Effect of selections;;Number of events (-)", HistType::kTH1D, {{NEvSels, -0.5, static_cast(NEvSels) - 0.5}}); + auto hSel = histos.get(HIST("Reco/hSelections")); + hSel->GetXaxis()->SetBinLabel(EvSelAll + 1, "All"); + hSel->GetXaxis()->SetBinLabel(EvSelFV0Veto + 1, "FV0A veto"); + hSel->GetXaxis()->SetBinLabel(EvSelNumContrib + 1, "N contrib."); + hSel->GetXaxis()->SetBinLabel(EvSelTwoTracks + 1, "2 sel. tracks"); + hSel->GetXaxis()->SetBinLabel(EvSelInvariantMass + 1, "Inv. mass"); + hSel->GetXaxis()->SetBinLabel(EvSelSystemPt + 1, "System pT"); + hSel->GetXaxis()->SetBinLabel(EvSelOneTrackMomentum + 1, "One trk p"); + hSel->GetXaxis()->SetBinLabel(EvSelBothTracksMomentum + 1, "Both trks p"); + + histos.add("Reco/hFwdTrackSelections", "Effect of forward track selections;;Number of tracks (-)", HistType::kTH1D, {{NTrkSels, -0.5, static_cast(NTrkSels) - 0.5}}); + auto hTrkSel = histos.get(HIST("Reco/hFwdTrackSelections")); + hTrkSel->GetXaxis()->SetBinLabel(TrkSelAll + 1, "All"); + hTrkSel->GetXaxis()->SetBinLabel(TrkSelType + 1, "Track type"); + hTrkSel->GetXaxis()->SetBinLabel(TrkSelPt + 1, "pT"); + hTrkSel->GetXaxis()->SetBinLabel(TrkSelEta + 1, "#eta"); + hTrkSel->GetXaxis()->SetBinLabel(TrkSelRabs + 1, "R_{abs}"); + hTrkSel->GetXaxis()->SetBinLabel(TrkSelPDca + 1, "pDCA"); + hTrkSel->GetXaxis()->SetBinLabel(TrkSelChi2 + 1, "#chi^{2}"); + hTrkSel->GetXaxis()->SetBinLabel(TrkSelChi2MatchMCHMFT + 1, "#chi^{2}_{MCH-MFT}"); + + histos.add("Reco/hNanalyzedPerRun", "N analyzed events per run;Run number (-);Number of analyzed events (-)", HistType::kTH1D, {{1, 0., 1.}}); + histos.add("Reco/hNselectedPerRun", "N selected events per run;Run number (-);Number of selected events (-)", HistType::kTH1D, {{1, 0., 1.}}); + + histos.add("Truth/hTroubles", "Counter of unwanted issues;;Number of troubles (-)", HistType::kTH1D, {{NTroubles - 1, 0.5, static_cast(NTroubles) - 0.5}}); + auto hTroubles = histos.get(HIST("Truth/hTroubles")); + hTroubles->GetXaxis()->SetBinLabel(TroubleTooManyMothers + 1, "> 2 mothers"); + hTroubles->GetXaxis()->SetBinLabel(TroubleTooManyDaughters + 1, "> 2 charged daughters"); + hTroubles->GetXaxis()->SetBinLabel(TroubleNoTrack + 1, "Daughter without track"); + hTroubles->GetXaxis()->SetBinLabel(TroubleMoreTracks + 1, "Daughter in > 1 cand."); + hTroubles->GetXaxis()->SetBinLabel(TroubleDifferentCandidates + 1, "Daughters in diff. cand."); + } // end init + + bool isAcceptedTrackType(uint8_t trackType) + { + using o2::aod::fwdtrack::ForwardTrackTypeEnum; + switch (trackType) { + case ForwardTrackTypeEnum::GlobalMuonTrack: + return cutFwdTrack.acceptGlobalMuonTrack; + case ForwardTrackTypeEnum::GlobalForwardTrack: + return cutFwdTrack.acceptGlobalForwardTrack; + case ForwardTrackTypeEnum::MuonStandaloneTrack: + return cutFwdTrack.acceptMuonStandaloneTrack; + case ForwardTrackTypeEnum::MCHStandaloneTrack: + return cutFwdTrack.acceptMCHStandaloneTrack; + default: + return false; + } + } + + static bool hasMFT(uint8_t trackType) + { + using o2::aod::fwdtrack::ForwardTrackTypeEnum; + return trackType == ForwardTrackTypeEnum::GlobalMuonTrack || trackType == ForwardTrackTypeEnum::GlobalForwardTrack; + } + + template + static ROOT::Math::PxPyPzMVector muonVector(T const& track) + { + return ROOT::Math::PxPyPzMVector(track.px(), track.py(), track.pz(), MassMuon); + } + + template + bool isSelectedFwdTrack(T const& track, bool fillHistos = true) + { + auto countPassed = [&](FwdTrackSelection step) { + if (fillHistos) + histos.fill(HIST("Reco/hFwdTrackSelections"), step); + }; + countPassed(TrkSelAll); + + if (!isAcceptedTrackType(track.trackType())) + return false; + countPassed(TrkSelType); + + if (!cutFwdTrack.applyFwdTrackSelection) + return true; + + const auto vec = muonVector(track); + if (vec.Pt() < cutFwdTrack.cutMinPt || vec.Pt() > cutFwdTrack.cutMaxPt) + return false; + countPassed(TrkSelPt); + + if (vec.Eta() < cutFwdTrack.cutMinEta || vec.Eta() > cutFwdTrack.cutMaxEta) + return false; + countPassed(TrkSelEta); + + if (track.rAtAbsorberEnd() < cutFwdTrack.cutMinRabs || track.rAtAbsorberEnd() > cutFwdTrack.cutMaxRabs) + return false; + countPassed(TrkSelRabs); + + float maxPDca = track.rAtAbsorberEnd() < upchelpers::AbsorberMid ? cutFwdTrack.cutMaxPDcaLowRabs : cutFwdTrack.cutMaxPDcaHighRabs; + if (track.pDca() > maxPDca) + return false; + countPassed(TrkSelPDca); + + if (track.chi2() > cutFwdTrack.cutMaxChi2) + return false; + countPassed(TrkSelChi2); + + if (hasMFT(track.trackType()) && track.chi2MatchMCHMFT() > cutFwdTrack.cutMaxChi2MatchMCHMFT) + return false; + countPassed(TrkSelChi2MatchMCHMFT); + + return true; + } + + // largest FV0A amplitude within [-maxRelBc, +maxRelBc] around the candidate BC + template + static float getMaxFV0Amplitude(C const& cand, int maxRelBc) + { + const auto& amps = cand.amplitudesV0A(); + const auto& relBCs = cand.ampRelBCsV0A(); + float maxAmp = 0.f; + for (std::size_t i = 0; i < amps.size(); ++i) { + if (std::abs(relBCs[i]) <= maxRelBc) + maxAmp = std::max(maxAmp, amps[i]); + } + return maxAmp; + } + + static float finiteOrDefault(float value) + { + return std::isfinite(value) ? value : DefaultFloat; + } + + template + static void fillEventInfo(RecoInfo& info, C const& cand) + { + info.runNumber = cand.runNumber(); + info.bc = cand.globalBC(); + info.numContrib = cand.numContrib(); + info.posX = cand.posX(); + info.posY = cand.posY(); + info.posZ = cand.posZ(); + info.totalFV0AmplitudeA = getMaxFV0Amplitude(cand, 0); + const auto& amps = cand.amplitudesV0A(); + const auto& relBCs = cand.ampRelBCsV0A(); + info.amplitudesV0A.assign(amps.begin(), amps.end()); + info.ampRelBCsV0A.assign(relBCs.begin(), relBCs.end()); + } + + // the ZDC table is filled by the producer only for candidates with ZDC activity, i.e. it holds at most one row per candidate + static void fillZdcInfo(RecoInfo& info, aod::UDZdcsReduced const& zdcs) + { + for (const auto& zdc : zdcs) { + info.energyCommonZNA = finiteOrDefault(zdc.energyCommonZNA()); + info.energyCommonZNC = finiteOrDefault(zdc.energyCommonZNC()); + info.timeZNA = finiteOrDefault(zdc.timeZNA()); + info.timeZNC = finiteOrDefault(zdc.timeZNC()); + } + } + + template + static void fillTrackInfo(RecoInfo& info, int iTrk, T const& track) + { + info.px[iTrk] = track.px(); + info.py[iTrk] = track.py(); + info.pz[iTrk] = track.pz(); + info.sign[iTrk] = track.sign(); + info.time[iTrk] = track.trackTime(); + info.timeRes[iTrk] = track.trackTimeRes(); + info.type[iTrk] = track.trackType(); + info.nClusters[iTrk] = track.nClusters(); + info.pDca[iTrk] = track.pDca(); + info.rAbs[iTrk] = track.rAtAbsorberEnd(); + info.chi2[iTrk] = track.chi2(); + info.chi2MatchMCHMID[iTrk] = track.chi2MatchMCHMID(); + info.chi2MatchMCHMFT[iTrk] = track.chi2MatchMCHMFT(); + info.mchBitMap[iTrk] = track.mchBitMap(); + info.midBitMap[iTrk] = track.midBitMap(); + info.midBoards[iTrk] = track.midBoards(); + } + + // writes reco-level columns, followed by optional truth-level columns of the simulated table + template + static void writeRow(TCursor& cursor, RecoInfo& info, TTruth&&... truth) + { + cursor(info.runNumber, info.bc, info.numContrib, info.posX, info.posY, info.posZ, + info.totalFV0AmplitudeA, info.amplitudesV0A, info.ampRelBCsV0A, + info.energyCommonZNA, info.energyCommonZNC, info.timeZNA, info.timeZNC, + info.px, info.py, info.pz, info.sign, info.time, info.timeRes, + info.type, info.nClusters, info.pDca, info.rAbs, info.chi2, info.chi2MatchMCHMID, info.chi2MatchMCHMFT, + info.mchBitMap, info.midBitMap, info.midBoards[0], info.midBoards[1], + std::forward(truth)...); + } + + static int getTrueChannel(const int pdgCodes[NumTracks]) + { + const int part1 = enumMyParticle(pdgCodes[0]); + const int part2 = enumMyParticle(pdgCodes[1]); + if (part1 == P_ELECTRON && part2 == P_ELECTRON) + return CH_EE; + if (part1 == P_MUON && part2 == P_MUON) + return CH_MUMU; + if ((part1 == P_ELECTRON && (part2 == P_PION || part2 == P_MUON)) || (part2 == P_ELECTRON && (part1 == P_PION || part1 == P_MUON))) + return CH_EMUPI; + return -1; + } + + void processDataFwd(CandidateFwd const& cand, + FwdTracks const& tracks, + aod::UDZdcsReduced const& zdcs) + { + const char* srun = Form("%d", cand.runNumber()); + histos.get(HIST("Reco/hNanalyzedPerRun"))->Fill(srun, 1); + + histos.fill(HIST("Reco/hSelections"), EvSelAll); + + if (cutSample.useFV0Veto && getMaxFV0Amplitude(cand, cutSample.cutFV0RelBcRange) > cutSample.cutMaxFV0Amp) + return; + histos.fill(HIST("Reco/hSelections"), EvSelFV0Veto); + + if (cutSample.useNumContrib && cand.numContrib() != cutSample.cutNumContrib) + return; + histos.fill(HIST("Reco/hSelections"), EvSelNumContrib); + + RecoInfo info; + std::array daug; + int nSelected = 0; + for (const auto& track : tracks) { + if (!isSelectedFwdTrack(track)) + continue; + if (nSelected < NumTracks) { + fillTrackInfo(info, nSelected, track); + daug[nSelected] = muonVector(track); + } + nSelected++; + } + + // Critical selection, without it the rest of the process function will fail + if (nSelected != NumTracks) + return; + histos.fill(HIST("Reco/hSelections"), EvSelTwoTracks); + + const auto mother = daug[0] + daug[1]; + + // Apply system selections + // invariant mass + if (mother.M() < cutPreselect.preselMinInvariantMass || mother.M() > cutPreselect.preselMaxInvariantMass) + return; + histos.fill(HIST("Reco/hSelections"), EvSelInvariantMass); + + // system pt + if (cutPreselect.preselUseOppositeSystemPtCut ? mother.Pt() < cutPreselect.preselSystemPtCut : mother.Pt() > cutPreselect.preselSystemPtCut) + return; + histos.fill(HIST("Reco/hSelections"), EvSelSystemPt); + + // one track momentum + if (daug[0].P() < cutPreselect.preselMinTrackMomentum && daug[1].P() < cutPreselect.preselMinTrackMomentum) + return; + histos.fill(HIST("Reco/hSelections"), EvSelOneTrackMomentum); + + // both tracks momentum + if (cutPreselect.preselUseMinMomentumOnBothTracks && (daug[0].P() < cutPreselect.preselMinTrackMomentum || daug[1].P() < cutPreselect.preselMinTrackMomentum)) + return; + histos.fill(HIST("Reco/hSelections"), EvSelBothTracksMomentum); + + histos.get(HIST("Reco/hNselectedPerRun"))->Fill(srun, 1); + + fillEventInfo(info, cand); + fillZdcInfo(info, zdcs); + writeRow(twoFwdTracks, info); + } + PROCESS_SWITCH(TwoFwdTracksEventTableProducer, processDataFwd, "Iterate UD tables with measured data created by upcCandProducerGlobalMuon.", true); + + PresliceUnsorted partPerMcCollision = aod::udmcparticle::udMcCollisionId; + PresliceUnsorted trackPerMcParticle = aod::udmcfwdtracklabel::udMcParticleId; + + void processMonteCarlo(aod::UDMcCollisions const& mcCollisions, + aod::UDMcParticles const& parts, + CandidatesFwd const& candidates, + FwdMcTracks const& tracks) + { + // start loop over generated collisions + for (const auto& mcColl : mcCollisions) { + RecoInfo info; + TruthInfo truth; + std::vector daughterIds; + + // store a charged particle as truth daughter; returns false if there are already too many of them + auto addTrueDaughter = [&](auto const& daughter) { + // check if it is the charged particle (= no pi0, photon or neutrino) + if (enumMyParticle(daughter.pdgCode()) == -1) + return true; + // check we do not have more than 2 charged daughters in total + if (static_cast(daughterIds.size()) >= NumTracks) { + if (verboseInfo) + printLargeMessage("Truth collision has more than 2 total charged daughters. Breaking the daughter loop."); + histos.fill(HIST("Truth/hTroubles"), TroubleTooManyDaughters); + truth.problem = true; + return false; + } + const auto iDaug = daughterIds.size(); + truth.daugPx[iDaug] = daughter.px(); + truth.daugPy[iDaug] = daughter.py(); + truth.daugPz[iDaug] = daughter.pz(); + truth.daugPdgCode[iDaug] = daughter.pdgCode(); + daughterIds.push_back(daughter.globalIndex()); + return true; + }; + + // get particles associated to generated collision + const auto& partsFromMcColl = parts.sliceBy(partPerMcCollision, mcColl.globalIndex()); + int countMothers = 0; + for (const auto& particle : partsFromMcColl) { + // select only mothers with checking if particle has no mother + if (particle.has_mothers()) + continue; + countMothers++; + // check the generated collision does not have more than 2 mothers + if (countMothers > NumTracks) { + if (verboseInfo) + printLargeMessage("Truth collision has more than 2 no mother particles. Breaking the particle loop."); + histos.fill(HIST("Truth/hTroubles"), TroubleTooManyMothers); + truth.problem = true; + break; + } + // fill info for each mother + truth.motherPx[countMothers - 1] = particle.px(); + truth.motherPy[countMothers - 1] = particle.py(); + truth.motherPz[countMothers - 1] = particle.pz(); + + if (particle.has_daughters()) { + for (const auto& daughter : particle.daughters_as()) { + if (!addTrueDaughter(daughter)) + break; + } + } else { + // motherless final-state particle, e.g. muon from gamma gamma -> mu mu, is its own daughter + addTrueDaughter(particle); + } + if (truth.problem) + break; + } // particles + + // get the reconstructed tracks of each daughter (how well the daughter was reconstructed). + // One muon can be stored in several candidates (as MCH-MID-MFT track in an anchor candidate and as MCH-MID track + // in an MCH-MID candidate), so collect per daughter the selected track in each candidate, preferring the lowest track type. + std::array, NumTracks> trackIdPerCand; // candidate id -> track id, per daughter + int nRecoDaughters = 0; + for (std::size_t iDaug = 0; iDaug < daughterIds.size(); ++iDaug) { + auto& candTracks = trackIdPerCand[iDaug]; + for (const auto& trk : tracks.sliceBy(trackPerMcParticle, daughterIds[iDaug])) { + if (!isSelectedFwdTrack(trk, false)) + continue; + auto it = candTracks.find(trk.udCollisionId()); + if (it == candTracks.end() || trk.trackType() < tracks.iteratorAt(it->second).trackType()) + candTracks[trk.udCollisionId()] = trk.globalIndex(); + } + if (candTracks.empty()) { + if (verboseInfo) + printLargeMessage("Daughter has no associated track. Skipping this daughter."); + histos.fill(HIST("Truth/hTroubles"), TroubleNoTrack); + truth.problem = true; + continue; + } + if (candTracks.size() > 1) + histos.fill(HIST("Truth/hTroubles"), TroubleMoreTracks); + nRecoDaughters++; + } // daughters + + // choose the candidate holding most daughters; on a tie the one with the lowest summed track type (i.e. with MFT) + std::map> candScore; // candidate id -> (number of daughters, summed track type) + for (const auto& candTracks : trackIdPerCand) { + for (const auto& [cand, trkId] : candTracks) { + candScore[cand].first++; + candScore[cand].second += tracks.iteratorAt(trkId).trackType(); + } + } + int32_t candId = -1; + std::pair bestScore{0, 0}; + for (const auto& [cand, score] : candScore) { + if (candId < 0 || score.first > bestScore.first || (score.first == bestScore.first && score.second < bestScore.second)) { + candId = cand; + bestScore = score; + } + } + + // fill info for reconstructed collision and tracks of the chosen candidate + if (candId >= 0) { + if (bestScore.first < nRecoDaughters) { + if (verboseInfo) + printLargeMessage("Daughters are reconstructed in different candidates."); + histos.fill(HIST("Truth/hTroubles"), TroubleDifferentCandidates); + truth.problem = true; + } + truth.hasRecoColl = true; + fillEventInfo(info, candidates.iteratorAt(candId)); + for (int iDaug = 0; iDaug < NumTracks; ++iDaug) { + auto it = trackIdPerCand[iDaug].find(candId); + if (it != trackIdPerCand[iDaug].end()) + fillTrackInfo(info, iDaug, tracks.iteratorAt(it->second)); + } + } + + truth.channel = getTrueChannel(truth.daugPdgCode); + + writeRow(trueTwoFwdTracks, info, // no ZDC info in MC + truth.channel, truth.hasRecoColl, mcColl.posX(), mcColl.posY(), mcColl.posZ(), + truth.motherPx, truth.motherPy, truth.motherPz, truth.daugPx, truth.daugPy, truth.daugPz, truth.daugPdgCode, truth.problem); + } // mccollisions + } + PROCESS_SWITCH(TwoFwdTracksEventTableProducer, processMonteCarlo, "Iterate UD tables with simulated data created by upcCandProducerGlobalMuon.", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGUD/TableProducer/twoTracksEventTableProducer.cxx b/PWGUD/TableProducer/twoTracksEventTableProducer.cxx index cf5f91a34a6..3526401d72c 100644 --- a/PWGUD/TableProducer/twoTracksEventTableProducer.cxx +++ b/PWGUD/TableProducer/twoTracksEventTableProducer.cxx @@ -577,7 +577,7 @@ struct TwoTracksEventTableProducer { if (verboseInfo) printLargeMessage("Truth collision has more than 1 reco collision. Skipping this event."); histos.get(HIST("Truth/hTroubles"))->Fill(1); - problem = true; + // problem = true; // FIXME: unused continue; } // grap reco-level collision diff --git a/PWGUD/TableProducer/upcCandProducerBarrel.cxx b/PWGUD/TableProducer/upcCandProducerBarrel.cxx index 4d2a75be18a..135fdd887ac 100644 --- a/PWGUD/TableProducer/upcCandProducerBarrel.cxx +++ b/PWGUD/TableProducer/upcCandProducerBarrel.cxx @@ -23,7 +23,9 @@ #include #include +#include #include +#include #include #include #include @@ -36,16 +38,22 @@ #include #include #include +#include #include #include +#include + +#include #include #include #include #include #include +#include #include +#include #include #include #include @@ -58,7 +66,6 @@ using namespace o2::aod::rctsel; namespace { constexpr int NBCsPerOrbit = o2::constants::lhc::LHCMaxBunches; -constexpr int MaxStoredDistance = 15; constexpr float MaxFITTime = 30.f; constexpr int NoSignalMCRef = -1; @@ -94,7 +101,9 @@ DistanceMap buildMinimumDistanceMap(std::vector const& tfIDs, // The forward-only cursor makes the scan linear after sorting size_t nextIndex = 0; for (size_t iTF = 0; iTF < tfIDs.size(); ++iTF) { - const int64_t tfStartBC = bcSOR + tfIDs[iTF] * nBCsPerTF; + // Cast before multiplication to prevent 32-bit integer overflow. + const int64_t tfStartBC = bcSOR + static_cast(tfIDs[iTF]) * nBCsPerTF; + // Use int64_t to keep BC arithmetic in 64 bits without additional casts. for (int64_t bcInTF = 0; bcInTF < nBCsPerTF; ++bcInTF) { const int64_t currentBC = tfStartBC + bcInTF; while (nextIndex < activeBCs.size() && activeBCs[nextIndex] < currentBC) { @@ -118,26 +127,13 @@ DistanceMap buildMinimumDistanceMap(std::vector const& tfIDs, return distances; } -void fillVetoHistograms(std::shared_ptr const& hVetoT00, std::shared_ptr const& hVetoTV0, - std::shared_ptr const& hVetoTVD, int bcInOrbit, +void fillVetoHistograms(std::shared_ptr const& hVetoT00, std::shared_ptr const& hVetoTV0, + std::shared_ptr const& hVetoTVD, int bcInOrbit, int tfID, uint32_t distanceFT0, uint32_t distanceFV0, uint32_t distanceFDD) { - hVetoT00->Fill(bcInOrbit, -1); - hVetoTV0->Fill(bcInOrbit, -1); - hVetoTVD->Fill(bcInOrbit, -1); - for (uint32_t threshold = 0; threshold <= MaxStoredDistance; ++threshold) { - if (distanceFT0 <= threshold) { - continue; - } - hVetoT00->Fill(bcInOrbit, threshold); - if (distanceFV0 <= threshold) { - continue; - } - hVetoTV0->Fill(bcInOrbit, threshold); - if (distanceFDD > threshold) { - hVetoTVD->Fill(bcInOrbit, threshold); - } - } + hVetoT00->Fill(bcInOrbit, tfID, distanceFT0); + hVetoTV0->Fill(bcInOrbit, tfID, std::min(distanceFT0, distanceFV0)); + hVetoTVD->Fill(bcInOrbit, tfID, std::min({distanceFT0, distanceFV0, distanceFDD})); } } // namespace @@ -147,6 +143,8 @@ DECLARE_SOA_COLUMN(RunNumber, runNumber, int32_t); DECLARE_SOA_COLUMN(GlobalBC, globalBC, uint64_t); DECLARE_SOA_COLUMN(TfId, tfId, uint32_t); DECLARE_SOA_COLUMN(Timestamp, timestamp, uint64_t); +DECLARE_SOA_COLUMN(Flags, flags, int8_t); +DECLARE_SOA_COLUMN(OccupancyFT0C, occupancyFT0C, float); DECLARE_SOA_COLUMN(PosX, posX, float); DECLARE_SOA_COLUMN(PosY, posY, float); DECLARE_SOA_COLUMN(PosZ, posZ, float); @@ -184,6 +182,8 @@ DECLARE_SOA_TABLE(UPCBarrelCands, "AOD", "UPCBARRELCANDS", upc_cand_prod_bar::GlobalBC, upc_cand_prod_bar::TfId, upc_cand_prod_bar::Timestamp, + upc_cand_prod_bar::Flags, + upc_cand_prod_bar::OccupancyFT0C, upc_cand_prod_bar::PosX, upc_cand_prod_bar::PosY, upc_cand_prod_bar::PosZ, @@ -243,12 +243,19 @@ struct UpcCandProducerBarrel { int cachedRunNumber = -1; int64_t bcSOR = 0; - int64_t nBCsPerTF = 0; + int32_t nBCsPerTF = 0; int configuredOrbitsPerTF = -1; int configuredTFStartBorder = -1; int configuredTFEndBorder = -1; + int configuredITSROFStartBorder = -1; + int configuredITSROFEndBorder = -1; int tfStartBorder = 0; int tfEndBorder = 0; + int rofOffset = 0; + int rofLength = 0; + int nRofPerTF = 0; + int rofStartBorderMargin = 0; + int rofEndBorderMargin = 0; std::bitset collidingBCs; Configurable nTracks{"nTracks", 2, "Required N tracks in selected collisions"}; @@ -258,8 +265,19 @@ struct UpcCandProducerBarrel { Configurable vetoFT0{"vetoFT0", 1, "FT0 veto: 0=off, 1=on"}; Configurable vetoFV0{"vetoFV0", 0, "FV0 veto: 0=off, 1=on"}; Configurable vetoFDD{"vetoFDD", 0, "FDD veto: 0=off, 1=on"}; + Configurable useTCE{"useTCE", 0, "Use TCE for lumi accounting: 0=off, 1=on"}; Configurable signalSourceId{"signalSourceId", 1, "MC source ID stored as embedded signal"}; + Configurable minAmpFV0A{"minAmpFV0A", 150.f, "Minimum amplitude for FV0A veto"}; + Configurable minAmpFT0A{"minAmpFT0A", 150.f, "Minimum amplitude for FT0A veto"}; + Configurable minAmpFT0C{"minAmpFT0C", 50.f, "Minimum amplitude for FT0C veto"}; + Configurable minAmpFDDA{"minAmpFDDA", 1000.f, "Minimum amplitude for FDDA veto"}; + Configurable minAmpFDDC{"minAmpFDDC", 1000.f, "Minimum amplitude for FDDC veto"}; + + Configurable maxStoredDistance{"maxStoredDistance", 32, "Maximum stored veto distance"}; + Configurable nBinsOccupancy{"nBinsOccupancy", 25, "Number of occupancy bins"}; + Configurable nBinsTF{"nBinsTF", 100, "Number of TF bins"}; + using CollisionsWithSels = soa::Join; using BCsWithSels = soa::Join; using TracksWithPID = soa::Join -40 && dt < -5) { // o2-linter: disable=magic-number (collisions in the past) + wOccup = 1. / 1225 * (dt + 40) * (dt + 40); // o2-linter: disable=magic-number (parabolic weight for past collisions) + } else if (dt >= -5 && dt < 15) { // o2-linter: disable=magic-number (collisions near a given one) + wOccup = 1; + } else if (dt >= 15 && dt < 40) { // o2-linter: disable=magic-number (collisions from the future) + wOccup = -0.4 / 25 * dt + 1.24; // o2-linter: disable=magic-number (linear weight for future collisions) + } else if (dt >= 40 && dt < 100) { // o2-linter: disable=magic-number (collisions from the distant future) + wOccup = -0.4 / 60 * dt + 0.6 + 0.8 / 3; // o2-linter: disable=magic-number (linear weight for distant future collisions) + } + return wOccup; + } + + bool noRofBorder(int64_t bc) + { + int bcInRof = (bc + rofLength - rofOffset) % rofLength; + return bcInRof > rofStartBorderMargin && bcInRof < rofLength - rofEndBorderMargin; + } + + int getRofInTF(int64_t bc) + { + return (bc + rofLength - rofOffset) / rofLength % nRofPerTF; + } + void init(InitContext& initContext) { ccdb->setLocalObjectValidityChecking(); @@ -287,15 +331,50 @@ struct UpcCandProducerBarrel { inheritEventSelectionOption("bcselOpts.NumberOfOrbitsPerTF", configuredOrbitsPerTF); inheritEventSelectionOption("bcselOpts.TimeFrameStartBorderMargin", configuredTFStartBorder); inheritEventSelectionOption("bcselOpts.TimeFrameEndBorderMargin", configuredTFEndBorder); - registry.add("hProcessedTFs", "Processed TFs;;TFs", HistType::kTH1D, {{1, 0., 1.}}); - registry.add("hTVX", "TVX counts per run;run;TVX BCs", HistType::kTH1D, {{NBCsPerOrbit, 0., NBCsPerOrbit}}); - registry.add("hTVXRCT", "TVX counts per run;run;TVX BCs", HistType::kTH1D, {{NBCsPerOrbit, 0., NBCsPerOrbit}}); - registry.add("hVetoT00", ";BC in orbit;veto range (BC);colliding BC count, no RCT mask", HistType::kTH2D, {{NBCsPerOrbit, 0., NBCsPerOrbit}, {17, -1.5, 15.5}}); - registry.add("hVetoTV0", ";BC in orbit;veto range (BC);colliding BC count, no RCT mask", HistType::kTH2D, {{NBCsPerOrbit, 0., NBCsPerOrbit}, {17, -1.5, 15.5}}); - registry.add("hVetoTVD", ";BC in orbit;veto range (BC);colliding BC count, no RCT mask", HistType::kTH2D, {{NBCsPerOrbit, 0., NBCsPerOrbit}, {17, -1.5, 15.5}}); - registry.add("hVetoT00RCT", ";BC in orbit;veto range (BC);colliding BC count, RCT mask", HistType::kTH2D, {{NBCsPerOrbit, 0., NBCsPerOrbit}, {17, -1.5, 15.5}}); - registry.add("hVetoTV0RCT", ";BC in orbit;veto range (BC);colliding BC count, RCT mask", HistType::kTH2D, {{NBCsPerOrbit, 0., NBCsPerOrbit}, {17, -1.5, 15.5}}); - registry.add("hVetoTVDRCT", ";BC in orbit;veto range (BC);colliding BC count, RCT mask", HistType::kTH2D, {{NBCsPerOrbit, 0., NBCsPerOrbit}, {17, -1.5, 15.5}}); + inheritEventSelectionOption("bcselOpts.ITSROFrameStartBorderMargin", configuredITSROFStartBorder); + inheritEventSelectionOption("bcselOpts.ITSROFrameEndBorderMargin", configuredITSROFEndBorder); + + const AxisSpec axisBCs{NBCsPerOrbit, 0., NBCsPerOrbit, "BC in orbit"}; + const AxisSpec axisVeto{maxStoredDistance + 1, -0.5, maxStoredDistance + 0.5, "veto range in BCs"}; + const AxisSpec axisOccupancy{nBinsOccupancy, 0., 250000., "FT0-based occupancy"}; + const AxisSpec axisTFs{nBinsTF, 0., 4.e7, "TFs"}; // 100 bins -> 400000 TFs per bin (1 bin ~ 1140 seconds for 32 orbits per TF) + + registry.add("hTF", "TF counts", HistType::kTH1D, {axisTFs}); + registry.add("hTVX", "TVX counts", HistType::kTH2D, {axisBCs, axisTFs}); + registry.add("hTVXRCT", "TVX counts after RCT selection", HistType::kTH2D, {axisBCs, axisTFs}); + registry.add("hTVXRCTnoPileupROF", "TVX counts after RCT and ROF pileup selection", HistType::kTH2D, {axisBCs, axisTFs}); + registry.add("hTVXupc", "TVX counts in upc ROFs", HistType::kTH2D, {axisBCs, axisTFs}); + registry.add("hTVXRCTupc", "TVX counts after RCT selection in upc ROFs", HistType::kTH2D, {axisBCs, axisTFs}); + registry.add("hTVXRCTnoPileupROFupc", "TVX counts after RCT and ROF pileup selection in upc ROFs", HistType::kTH2D, {axisBCs, axisTFs}); + + registry.add("hVetoT00", "no RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTV0", "no RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTVD", "no RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoT00RCT", "after RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTV0RCT", "after RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTVDRCT", "after RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoT00RCTnoPileupROF", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTV0RCTnoPileupROF", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTVDRCTnoPileupROF", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + + registry.add("hOccupancyT00RCTnoPileupROF", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisOccupancy}); + registry.add("hOccupancyTV0RCTnoPileupROF", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisOccupancy}); + registry.add("hOccupancyTVDRCTnoPileupROF", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisOccupancy}); + + registry.add("hVetoT00upc", "no RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTV0upc", "no RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTVDupc", "no RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoT00RCTupc", "after RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTV0RCTupc", "after RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTVDRCTupc", "after RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoT00RCTnoPileupROFupc", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTV0RCTnoPileupROFupc", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTVDRCTnoPileupROFupc", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + + registry.add("hOccupancyT00RCTnoPileupROFupc", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisOccupancy}); + registry.add("hOccupancyTV0RCTnoPileupROFupc", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisOccupancy}); + registry.add("hOccupancyTVDRCTnoPileupROFupc", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisOccupancy}); + LOGF(info, "Veto config: window=%d FT0=%d FV0=%d FDD=%d", vetoBCWindow.value, vetoFT0.value, vetoFV0.value, vetoFDD.value); } @@ -307,7 +386,8 @@ struct UpcCandProducerBarrel { const auto runInfo = o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(ccdb->instance(), runNumber); const int64_t orbitsPerTF = configuredOrbitsPerTF < 0 ? runInfo.orbitsPerTF : configuredOrbitsPerTF; const bool needsEventSelectionParams = configuredTFStartBorder < 0 || configuredTFEndBorder < 0; - const auto* eventSelectionParams = needsEventSelectionParams ? ccdb->getForTimeStamp("EventSelection/EventSelectionParams", runInfo.sor / 2 + runInfo.eor / 2) : nullptr; + const auto ts = (runInfo.sor + runInfo.eor) / 2; + const auto* eventSelectionParams = needsEventSelectionParams ? ccdb->getForTimeStamp("EventSelection/EventSelectionParams", ts) : nullptr; if (orbitsPerTF <= 0 || !runInfo.grpLHC || (needsEventSelectionParams && !eventSelectionParams)) { LOGF(fatal, "Incomplete run information for run %d", runNumber); } @@ -316,6 +396,12 @@ struct UpcCandProducerBarrel { tfStartBorder = configuredTFStartBorder < 0 ? eventSelectionParams->fTimeFrameStartBorderMargin : configuredTFStartBorder; tfEndBorder = configuredTFEndBorder < 0 ? eventSelectionParams->fTimeFrameEndBorderMargin : configuredTFEndBorder; collidingBCs = runInfo.grpLHC->getBunchFilling().getBCPattern(); + + // get ITS ROF parameters + auto alppar = ccdb->getForTimeStamp>("ITS/Config/AlpideParam", ts); + rofOffset = alppar->roFrameBiasInBC; + rofLength = alppar->roFrameLengthInBC; + nRofPerTF = nBCsPerTF / rofLength; cachedRunNumber = runNumber; } @@ -332,15 +418,18 @@ struct UpcCandProducerBarrel { std::vector localTFIndex; localTFIndex.reserve(bcs.size()); // Cache the local TF slot for each BC row for collision and MC lookups + auto hTF = registry.get(HIST("hTF")); for (const auto& bc : bcs) { - const auto tfID = static_cast((static_cast(bc.globalBC()) - bcSOR) / nBCsPerTF); + const int64_t globalBC = bc.globalBC(); + const uint32_t tfID = (globalBC - bcSOR) / nBCsPerTF; if (tfIDs.empty() || tfID != tfIDs.back()) { + hTF->Fill(tfID); tfIDs.push_back(tfID); tfPassesRCT.push_back(static_cast(rctChecker(bc))); } localTFIndex.push_back(tfIDs.size() - 1); } - registry.fill(HIST("hProcessedTFs"), 0.5, static_cast(tfIDs.size())); + LOGP(info, "Starting new DF. Number of TFs = {}", tfIDs.size()); std::unordered_map signalParticleRefs; if constexpr (isMC) { @@ -356,7 +445,7 @@ struct UpcCandProducerBarrel { for (const auto& mcCollision : *mcCollisions) { bcsWithMCCollision.push_back(static_cast(mcCollision.template bc_as().globalBC())); } - const auto minDistanceMC = buildMinimumDistanceMap(tfIDs, bcSOR, nBCsPerTF, bcsWithMCCollision, MaxStoredDistance, true); + const auto minDistanceMC = buildMinimumDistanceMap(tfIDs, bcSOR, nBCsPerTF, bcsWithMCCollision, maxStoredDistance, true); for (const auto& mcCollision : *mcCollisions) { const auto bc = mcCollision.template bc_as(); @@ -416,78 +505,237 @@ struct UpcCandProducerBarrel { } } - auto hTVX = registry.get(HIST("hTVX")); - auto hTVXRCT = registry.get(HIST("hTVXRCT")); + // collect number of all collisions and upc-mode collisions per ROF + LOGP(debug, "Collect number of all collisions and upc-mode collisions per ROF"); + std::vector> vMultTracks(tfIDs.size(), std::map()); + std::vector> vCollisionsPerROF(tfIDs.size(), std::vector(nRofPerTF, 0)); + std::vector> vUpcModePerROF(tfIDs.size(), std::vector(nRofPerTF, 0)); + for (const auto& collision : collisions) { + auto bc = collision.template bc_as(); + const int64_t globalBC = bc.globalBC(); + const int32_t bcInTF = (globalBC - bcSOR) % nBCsPerTF; + const auto iTF = localTFIndex[bc.globalIndex()]; + const int32_t rofInTF = getRofInTF(bcInTF); + vCollisionsPerROF[iTF][rofInTF]++; + const bool upcMode = (collision.flags() & dataformats::Vertex>::Flags::UPCMode) != 0; + if (upcMode) { + vUpcModePerROF[iTF][rofInTF]++; + } + } + + auto hTVX = registry.get(HIST("hTVX")); + auto hTVXRCT = registry.get(HIST("hTVXRCT")); + + auto hTVXupc = registry.get(HIST("hTVXupc")); + auto hTVXRCTupc = registry.get(HIST("hTVXRCTupc")); std::vector bcsWithFT0, bcsWithFV0, bcsWithFDD; bcsWithFT0.reserve(ft0s.size()); bcsWithFV0.reserve(fv0s.size()); bcsWithFDD.reserve(fdds.size()); + LOGP(debug, "Starting lumi accounting"); + // lumi accounting + std::vector> vMultFT0C(tfIDs.size(), std::map()); for (const auto& ft0 : ft0s) { const auto bc = ft0.bc_as(); - const auto gbc = bc.globalBC(); - const auto bcInOrbit = gbc % NBCsPerOrbit; - if (ft0.timeA() < MaxFITTime || ft0.timeC() < MaxFITTime) { - bcsWithFT0.push_back(gbc); + const auto globalBC = bc.globalBC(); + const auto bcInOrbit = globalBC % NBCsPerOrbit; + const int32_t bcInTF = (globalBC - bcSOR) % nBCsPerTF; + const auto iTF = localTFIndex[bc.globalIndex()]; + const auto tfID = tfIDs[iTF]; + const int32_t rofInTF = getRofInTF(bcInTF); + bool upcMode = vCollisionsPerROF[iTF][rofInTF] == 0 || vUpcModePerROF[iTF][rofInTF] > 0; + + // Multiplicity for FT0C-based occupancy estimates + if (ft0.timeC() < MaxFITTime && ft0.sumAmpC() >= minAmpFT0C) { + vMultFT0C[iTF][bcInTF] = ft0.sumAmpC(); } - if (!collidingBCs[bcInOrbit] || !(ft0.triggerMask() & (1U << o2::fit::Triggers::bitVertex))) { + + if (!collidingBCs[bcInOrbit] || !TESTBIT(ft0.triggerMask(), o2::fit::Triggers::bitVertex)) { continue; } - hTVX->Fill(bcInOrbit); + + if (useTCE && !TESTBIT(ft0.triggerMask(), o2::fit::Triggers::bitCen)) { + continue; + } + + hTVX->Fill(bcInOrbit, tfID); + if (upcMode) { + hTVXupc->Fill(bcInOrbit, tfID); + } + if (!bc.selection_bit(aod::evsel::kNoTimeFrameBorder) || !rctChecker(bc)) { continue; } - hTVXRCT->Fill(bcInOrbit); + + hTVXRCT->Fill(bcInOrbit, tfID); + if (upcMode) { + hTVXRCTupc->Fill(bcInOrbit, tfID); + } } + + LOGP(debug, "Filling FT0 activity arrays"); + for (const auto& ft0 : ft0s) { + const float sumAmpFT0A = ft0.sumAmpA(); + const float sumAmpFT0C = ft0.sumAmpC(); + if ((ft0.timeA() < MaxFITTime && sumAmpFT0A >= minAmpFT0A) || (ft0.timeC() < MaxFITTime && sumAmpFT0C >= minAmpFT0C)) { + bcsWithFT0.push_back(ft0.bc_as().globalBC()); + } + } + + LOGP(debug, "Filling FV0 activity arrays"); for (const auto& fv0 : fv0s) { - if (fv0.time() < MaxFITTime) { + const float sumAmpFV0A = std::accumulate(fv0.amplitude().begin(), fv0.amplitude().end(), 0.f); + if (fv0.time() < MaxFITTime && sumAmpFV0A >= minAmpFV0A) { bcsWithFV0.push_back(fv0.bc_as().globalBC()); } } + + LOGP(debug, "Filling FDD activity arrays"); for (const auto& fdd : fdds) { - if (fdd.timeA() < MaxFITTime || fdd.timeC() < MaxFITTime) { + const float sumAmpFDDA = std::accumulate(std::begin(fdd.chargeA()), std::end(fdd.chargeA()), 0.f); + const float sumAmpFDDC = std::accumulate(std::begin(fdd.chargeC()), std::end(fdd.chargeC()), 0.f); + if ((fdd.timeA() < MaxFITTime && sumAmpFDDA >= minAmpFDDA) || (fdd.timeC() < MaxFITTime && sumAmpFDDC >= minAmpFDDC)) { bcsWithFDD.push_back(fdd.bc_as().globalBC()); } } - const uint32_t scanDistance = std::max(MaxStoredDistance, vetoBCWindow.value); + LOGP(debug, "Starting occupancy calculation"); + std::vector> vOccupancyFT0C(tfIDs.size(), std::vector(nBCsPerTF, 0.f)); + int deltaBcPast = 1600; + int deltaBcFuture = 4000; + // TODO: calculate weights only once per task + std::vector weights(deltaBcPast + deltaBcFuture + 1); + for (int d = -deltaBcPast; d <= deltaBcFuture; ++d) { + weights[d + deltaBcPast] = calcWeightForOccupancy(d * o2::constants::lhc::LHCBunchSpacingMUS); + } + + for (size_t iTF = 0; iTF < tfIDs.size(); ++iTF) { + for (const auto& [bc, multFT0C] : vMultFT0C[iTF]) { + LOGP(debug, "iTF={} bc={} mult={}", iTF, bc, multFT0C); + const int bcInTFMin = std::max(bc - deltaBcFuture, 0); + const int bcInTFMax = std::min(bc + deltaBcPast, nBCsPerTF - 1); + for (int bcInTF = bcInTFMin; bcInTF <= bcInTFMax; ++bcInTF) { + vOccupancyFT0C[iTF][bcInTF] += weights[bc - bcInTF + deltaBcPast] * multFT0C; + } + } + } + + LOGP(debug, "Building minimum distance maps"); + const uint32_t scanDistance = std::max(maxStoredDistance.value, vetoBCWindow.value); const DistanceMap minDistanceFT0 = buildMinimumDistanceMap(tfIDs, bcSOR, nBCsPerTF, bcsWithFT0, scanDistance); const DistanceMap minDistanceFV0 = buildMinimumDistanceMap(tfIDs, bcSOR, nBCsPerTF, bcsWithFV0, scanDistance); const DistanceMap minDistanceFDD = buildMinimumDistanceMap(tfIDs, bcSOR, nBCsPerTF, bcsWithFDD, scanDistance); - auto hVetoT00 = registry.get(HIST("hVetoT00")); - auto hVetoTV0 = registry.get(HIST("hVetoTV0")); - auto hVetoTVD = registry.get(HIST("hVetoTVD")); - auto hVetoT00RCT = registry.get(HIST("hVetoT00RCT")); - auto hVetoTV0RCT = registry.get(HIST("hVetoTV0RCT")); - auto hVetoTVDRCT = registry.get(HIST("hVetoTVDRCT")); + auto hVetoT00 = registry.get(HIST("hVetoT00")); + auto hVetoTV0 = registry.get(HIST("hVetoTV0")); + auto hVetoTVD = registry.get(HIST("hVetoTVD")); + auto hVetoT00RCT = registry.get(HIST("hVetoT00RCT")); + auto hVetoTV0RCT = registry.get(HIST("hVetoTV0RCT")); + auto hVetoTVDRCT = registry.get(HIST("hVetoTVDRCT")); + auto hVetoT00RCTnoPileupROF = registry.get(HIST("hVetoT00RCTnoPileupROF")); + auto hVetoTV0RCTnoPileupROF = registry.get(HIST("hVetoTV0RCTnoPileupROF")); + auto hVetoTVDRCTnoPileupROF = registry.get(HIST("hVetoTVDRCTnoPileupROF")); + + auto hVetoT00upc = registry.get(HIST("hVetoT00upc")); + auto hVetoTV0upc = registry.get(HIST("hVetoTV0upc")); + auto hVetoTVDupc = registry.get(HIST("hVetoTVDupc")); + auto hVetoT00RCTupc = registry.get(HIST("hVetoT00RCTupc")); + auto hVetoTV0RCTupc = registry.get(HIST("hVetoTV0RCTupc")); + auto hVetoTVDRCTupc = registry.get(HIST("hVetoTVDRCTupc")); + auto hVetoT00RCTnoPileupROFupc = registry.get(HIST("hVetoT00RCTnoPileupROFupc")); + auto hVetoTV0RCTnoPileupROFupc = registry.get(HIST("hVetoTV0RCTnoPileupROFupc")); + auto hVetoTVDRCTnoPileupROFupc = registry.get(HIST("hVetoTVDRCTnoPileupROFupc")); + + auto hOccupancyT00RCTnoPileupROF = registry.get(HIST("hOccupancyT00RCTnoPileupROF")); + auto hOccupancyTV0RCTnoPileupROF = registry.get(HIST("hOccupancyTV0RCTnoPileupROF")); + auto hOccupancyTVDRCTnoPileupROF = registry.get(HIST("hOccupancyTVDRCTnoPileupROF")); + + auto hOccupancyT00RCTnoPileupROFupc = registry.get(HIST("hOccupancyT00RCTnoPileupROFupc")); + auto hOccupancyTV0RCTnoPileupROFupc = registry.get(HIST("hOccupancyTV0RCTnoPileupROFupc")); + auto hOccupancyTVDRCTnoPileupROFupc = registry.get(HIST("hOccupancyTVDRCTnoPileupROFupc")); + + LOGP(debug, "Filling veto efficiency histograms"); for (size_t iTF = 0; iTF < tfIDs.size(); ++iTF) { const int64_t tfStartBC = bcSOR + static_cast(tfIDs[iTF]) * nBCsPerTF; - for (int64_t bcInTF = tfStartBorder + 1; bcInTF < nBCsPerTF - tfEndBorder; ++bcInTF) { + const auto tfID = tfIDs[iTF]; + for (int64_t bcInTF = tfStartBorder + 1; bcInTF < nBCsPerTF - tfEndBorder; ++bcInTF) { // TODO why int64_t? const int64_t globalBC = tfStartBC + bcInTF; const int bcInOrbit = globalBC % NBCsPerOrbit; - if (!collidingBCs[bcInOrbit]) { - continue; - } + const int32_t rofInTF = getRofInTF(bcInTF); + bool upcMode = vCollisionsPerROF[iTF][rofInTF] == 0 || vUpcModePerROF[iTF][rofInTF] > 0; + bool pileupInROF = vCollisionsPerROF[iTF][rofInTF] >= 1; const auto distanceFT0 = minDistanceFT0[iTF][bcInTF]; const auto distanceFV0 = minDistanceFV0[iTF][bcInTF]; const auto distanceFDD = minDistanceFDD[iTF][bcInTF]; - fillVetoHistograms(hVetoT00, hVetoTV0, hVetoTVD, bcInOrbit, distanceFT0, distanceFV0, distanceFDD); - if (tfPassesRCT[iTF] != 0u) { - fillVetoHistograms(hVetoT00RCT, hVetoTV0RCT, hVetoTVDRCT, bcInOrbit, distanceFT0, distanceFV0, distanceFDD); + + fillVetoHistograms(hVetoT00, hVetoTV0, hVetoTVD, bcInOrbit, tfID, distanceFT0, distanceFV0, distanceFDD); + if (upcMode) { + fillVetoHistograms(hVetoT00upc, hVetoTV0upc, hVetoTVDupc, bcInOrbit, tfID, distanceFT0, distanceFV0, distanceFDD); + } + + if (tfPassesRCT[iTF] == 0u) { + continue; + } + + fillVetoHistograms(hVetoT00RCT, hVetoTV0RCT, hVetoTVDRCT, bcInOrbit, tfID, distanceFT0, distanceFV0, distanceFDD); + if (upcMode) { + fillVetoHistograms(hVetoT00RCTupc, hVetoTV0RCTupc, hVetoTVDRCTupc, bcInOrbit, tfID, distanceFT0, distanceFV0, distanceFDD); + } + + if (pileupInROF) { + continue; + } + + fillVetoHistograms(hVetoT00RCTnoPileupROF, hVetoTV0RCTnoPileupROF, hVetoTVDRCTnoPileupROF, bcInOrbit, tfID, distanceFT0, distanceFV0, distanceFDD); + if (upcMode) { + fillVetoHistograms(hVetoT00RCTnoPileupROFupc, hVetoTV0RCTnoPileupROFupc, hVetoTVDRCTnoPileupROFupc, bcInOrbit, tfID, distanceFT0, distanceFV0, distanceFDD); + } + + if (distanceFT0 == 0) { + continue; + } + float occupancy = vOccupancyFT0C[iTF][bcInTF]; + hOccupancyT00RCTnoPileupROF->Fill(bcInOrbit, tfID, occupancy); + if (upcMode) { + hOccupancyT00RCTnoPileupROFupc->Fill(bcInOrbit, tfID, occupancy); + } + + if (distanceFV0 == 0) { + continue; + } + hOccupancyTV0RCTnoPileupROF->Fill(bcInOrbit, tfID, occupancy); + if (upcMode) { + hOccupancyTV0RCTnoPileupROFupc->Fill(bcInOrbit, tfID, occupancy); + } + + if (distanceFDD == 0) { + continue; + } + hOccupancyTVDRCTnoPileupROF->Fill(bcInOrbit, tfID, occupancy); + if (upcMode) { + hOccupancyTVDRCTnoPileupROFupc->Fill(bcInOrbit, tfID, occupancy); } } } + LOGP(debug, "Filling upc trees"); for (const auto& collision : collisions) { if (collision.numContrib() != nTracks || !collision.selection_bit(aod::evsel::kNoTimeFrameBorder) || !rctChecker(collision)) { continue; } auto bc = collision.template bc_as(); - auto gbc = bc.globalBC(); + int64_t globalBC = bc.globalBC(); const auto iTF = localTFIndex[bc.globalIndex()]; - const int64_t bcInTF = (static_cast(gbc) - bcSOR) % nBCsPerTF; + const int64_t bcInTF = (globalBC - bcSOR) % nBCsPerTF; + const int32_t rofInTF = getRofInTF(bcInTF); + const bool upcMode = (collision.flags() & dataformats::Vertex>::Flags::UPCMode) != 0; + const bool noPileupInRof = vCollisionsPerROF[iTF][rofInTF] <= 1; + uint8_t flags = 0; + flags |= upcMode << 0; + flags |= noPileupInRof << 1; + const auto occupancyFT0C = vOccupancyFT0C[iTF][bcInTF]; const auto distFT0 = minDistanceFT0[iTF][bcInTF]; const auto distFV0 = minDistanceFV0[iTF][bcInTF]; const auto distFDD = minDistanceFDD[iTF][bcInTF]; @@ -537,12 +785,13 @@ struct UpcCandProducerBarrel { if (px.size() != static_cast(nTracks)) { continue; } - selectedCandidates(bc.runNumber(), gbc, tfIDs[iTF], bc.timestamp(), collision.posX(), collision.posY(), collision.posZ(), + selectedCandidates(bc.runNumber(), globalBC, tfIDs[iTF], bc.timestamp(), flags, occupancyFT0C, + collision.posX(), collision.posY(), collision.posZ(), px, py, pz, tpcSignal, tpcNSigmaEl, tpcNSigmaPi, tpcNSigmaKa, tpcNSigmaPr, tofNSigmaEl, tofNSigmaPi, tofNSigmaKa, tofNSigmaPr, itsClusterMap, nClusters, sign, - std::min(distFT0, MaxStoredDistance + 1), - std::min(distFV0, MaxStoredDistance + 1), - std::min(distFDD, MaxStoredDistance + 1)); + std::min(distFT0, maxStoredDistance + 1), + std::min(distFV0, maxStoredDistance + 1), + std::min(distFDD, maxStoredDistance + 1)); if constexpr (isMC) { selectedCandidateMCLabels(newMCCollisionIds, newParticleIds); } diff --git a/PWGUD/TableProducer/upcCandProducerGlobalMuon.cxx b/PWGUD/TableProducer/upcCandProducerGlobalMuon.cxx index c418fd7c9c1..cdd76286241 100644 --- a/PWGUD/TableProducer/upcCandProducerGlobalMuon.cxx +++ b/PWGUD/TableProducer/upcCandProducerGlobalMuon.cxx @@ -124,7 +124,7 @@ struct UpcCandProducerGlobalMuon { void init(InitContext&) { - fUpcCuts = (UPCCutparHolder)fUpcCutsConf; + fUpcCuts = UPCCutparHolder(fUpcCutsConf); if (fUpcCuts.getUseFwdCuts() || fEnableMFT) { fCCDB->setURL("http://alice-ccdb.cern.ch"); fCCDB->setCaching(true); @@ -610,7 +610,7 @@ struct UpcCandProducerGlobalMuon { } // Initialize FwdDCAFitter with magnetic field if (fEnableMFT) { - o2::field::MagneticField* field = static_cast(TGeoGlobalMagField::Instance()->GetField()); + o2::field::MagneticField* field = dynamic_cast(TGeoGlobalMagField::Instance()->GetField()); static constexpr double kCenterMFT[3] = {0, 0, -61.4}; float bz = field->getBz(kCenterMFT); LOG(info) << "FwdDCAFitter magnetic field at MFT center: bZ = " << bz; @@ -922,7 +922,7 @@ struct UpcCandProducerGlobalMuon { o2::aod::Zdcs const& zdcs) { fDoMC = false; - createCandidates(fwdTracks, mftTracks, fwdTrkCls, ambFwdTracks, bcs, collisions, fv0s, zdcs, (o2::aod::McFwdTrackLabels*)nullptr); + createCandidates(fwdTracks, mftTracks, fwdTrkCls, ambFwdTracks, bcs, collisions, fv0s, zdcs, nullptr); } void processFwdMC(ForwardTracks const& fwdTracks, diff --git a/PWGUD/TableProducer/upcCandProducerMuon.cxx b/PWGUD/TableProducer/upcCandProducerMuon.cxx index 0ac8458d60f..b9f5edc7007 100644 --- a/PWGUD/TableProducer/upcCandProducerMuon.cxx +++ b/PWGUD/TableProducer/upcCandProducerMuon.cxx @@ -88,7 +88,7 @@ struct UpcCandProducerMuon { void init(InitContext&) { - fUpcCuts = (UPCCutparHolder)fUpcCutsConf; + fUpcCuts = UPCCutparHolder(fUpcCutsConf); if (fUpcCuts.getUseFwdCuts()) { fCCDB->setURL("http://alice-ccdb.cern.ch"); fCCDB->setCaching(true); @@ -520,7 +520,7 @@ struct UpcCandProducerMuon { o2::aod::Zdcs const& zdcs) { fDoMC = false; - createCandidates(fwdTracks, ambFwdTracks, bcs, collisions, fv0s, zdcs, (o2::aod::McFwdTrackLabels*)nullptr); + createCandidates(fwdTracks, ambFwdTracks, bcs, collisions, fv0s, zdcs, nullptr); } void processFwdMC(ForwardTracks const& fwdTracks, diff --git a/PWGUD/TableProducer/upcCandProducerSemiFwd.cxx b/PWGUD/TableProducer/upcCandProducerSemiFwd.cxx index cb6146aee7b..d99c57f5dc9 100644 --- a/PWGUD/TableProducer/upcCandProducerSemiFwd.cxx +++ b/PWGUD/TableProducer/upcCandProducerSemiFwd.cxx @@ -108,7 +108,7 @@ struct UpcCandProducerSemiFwd { void init(InitContext&) { - fUpcCuts = (UPCCutparHolder)fUpcCutsConf; + fUpcCuts = UPCCutparHolder(fUpcCutsConf); if (fUpcCuts.getUseFwdCuts()) { fCCDB->setURL("http://alice-ccdb.cern.ch"); fCCDB->setCaching(true); @@ -653,7 +653,7 @@ struct UpcCandProducerSemiFwd { { fDoMC = false; createCandidates(fwdTracks, ambFwdTracks, barrelTracks, ambBarrelTracks, bcs, collisions, fv0s, zdcs, - (o2::aod::McFwdTrackLabels*)nullptr, (o2::aod::McTrackLabels*)nullptr); + nullptr, nullptr); } void processSemiFwdMC(ForwardTracks const& fwdTracks, diff --git a/PWGUD/Tasks/CMakeLists.txt b/PWGUD/Tasks/CMakeLists.txt index bfe050bbe04..a2566a51d87 100644 --- a/PWGUD/Tasks/CMakeLists.txt +++ b/PWGUD/Tasks/CMakeLists.txt @@ -11,6 +11,12 @@ o2physics_add_dpl_workflow(upc-polarisation-jpsi-incoh SOURCES upcPolarisationJpsiIncoh.cxx + REUSE_FROM UDTablesPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(upc-track-vertexing-qa + SOURCES upcTrackVertexingQA.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) @@ -21,11 +27,13 @@ o2physics_add_dpl_workflow(upc-analysis o2physics_add_dpl_workflow(sg-spectra SOURCES sgSpectraAnalyzer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-pid-spectra-table SOURCES sgPIDSpectraTable.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) @@ -36,46 +44,55 @@ o2physics_add_dpl_workflow(sg-pid-analyzer o2physics_add_dpl_workflow(sg-pid-spectra SOURCES sgPIDSpectra.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-incl-jpsi SOURCES sgInclJpsi.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-excl-omega SOURCES sgExclOmega.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-excl-universe SOURCES sgExcUniverse.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-fourpi SOURCES sgFourPiAnalyzer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-sixpi SOURCES sgSixPiAnalyzer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-twopi SOURCES sgTwoPiAnalyzer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-d0 SOURCES sgD0Analyzer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-fit SOURCES sgFITAnalyzer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) @@ -101,11 +118,13 @@ o2physics_add_dpl_workflow(diff-qa o2physics_add_dpl_workflow(dg-cand-analyzer SOURCES dgCandAnalyzer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGCutparHolder O2Physics::UDGoodRunSelector O2Physics::DGPIDSelector O2Physics::UDFSParser COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-cand-analyzer SOURCES upcCandidateAnalyzer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) @@ -126,121 +145,145 @@ o2physics_add_dpl_workflow(upc-veto-analysis o2physics_add_dpl_workflow(tautau13topo SOURCES upcTauTau13topo.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-tau-rl SOURCES upcTauRl.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::ReconstructionDataFormats O2::DetectorsBase O2::DetectorsCommonDataFormats COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(polarisation-rho SOURCES polarisationRho.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-jpsi-corr SOURCES upcJpsiCorr.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(exclusive-phi SOURCES exclusivePhi.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-photonuclear-jmg SOURCES upcPhotonuclearAnalysisJMG.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore O2::ReconstructionDataFormats O2::DetectorsBase O2::DetectorsCommonDataFormats COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(exclusive-two-protons SOURCES exclusiveTwoProtons.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(exclusive-two-protons-sg SOURCES exclusiveTwoProtonsSG.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(exclusive-phi-leptons SOURCES exclusivePhiLeptons.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(exclusive-phi-leptons-trees SOURCES exclusivePhiLeptonsTrees.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(exclusive-pentaquark SOURCES exclusivePentaquark.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(inclusive-phikstar-sd SOURCES sginclusivePhiKstarSD.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-exclusive-phi SOURCES sgExclusivePhi.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-exclusive-phi-its SOURCES sgExclusivePhiITSselections.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-pion-analysis SOURCES upcPionAnalysis.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(fwd-muons-upc SOURCES fwdMuonsUpc.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-fwd-jpsi-rl SOURCES upcFwdJpsiRl.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-semi-fwd-jpsi-rl SOURCES upcSemiFwdJpsiRl.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-event-its-rof-counter SOURCES upcEventItsRofCounter.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-rho-analysis SOURCES upcRhoAnalysis.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-rho-prime-analysis SOURCES upcRhoPrimeAnalysis.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(event-by-event SOURCES eventByevent.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(exclusive-rho-to-four-pi SOURCES exclusiveRhoTo4Pi.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-quarkonia-central-barrel SOURCES upcQuarkoniaCentralBarrel.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) @@ -251,16 +294,19 @@ o2physics_add_dpl_workflow(upc-test-mc-std-tabs-rl o2physics_add_dpl_workflow(decaytree-analyzer SOURCES decayTreeAnalyzer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::UDGoodRunSelector O2Physics::decayTree COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(flow-cumulants-upc SOURCES flowCumulantsUpc.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::GFWCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(flow-correlations-upc SOURCES flowCorrelationsUpc.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::GFWCore O2Physics::PWGCFCore O2Physics::TwoPartCorrCore COMPONENT_NAME Analysis) @@ -271,21 +317,25 @@ o2physics_add_dpl_workflow(flow-mc-upc o2physics_add_dpl_workflow(flow-long-range-corr-upc SOURCES flowLongRangeCorrUpc.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::PWGCFCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(analysis-mc-dpm-jet-sg-v3 SOURCES analysisMCDPMJetSGv3.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-test-rct-tables SOURCES upcTestRctTables.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-exclusive-jpsi-midrapidity SOURCES sgExclusiveJpsiMidrapidity.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -296,10 +346,23 @@ o2physics_add_dpl_workflow(upc-vm-rof o2physics_add_dpl_workflow(upc-test-fit-bit-mapping SOURCES upcTestFitBitMapping.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(pt-spectra-inclusive-upc SOURCES ptSpectraInclusiveUpc.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(sg-deuteron-spectra + SOURCES sgDeuteronSpectra.cxx + REUSE_FROM UDTablesPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(upc-pile-up + SOURCES upcPileUp.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB + COMPONENT_NAME Analysis) diff --git a/PWGUD/Tasks/dgCandAnalyzer.cxx b/PWGUD/Tasks/dgCandAnalyzer.cxx index 79b5b6c5f02..60547236485 100644 --- a/PWGUD/Tasks/dgCandAnalyzer.cxx +++ b/PWGUD/Tasks/dgCandAnalyzer.cxx @@ -144,7 +144,7 @@ struct DGCandAnalyzer { // PDG pdg = TDatabasePDG::Instance(); - anaPars = (DGAnaparHolder)DGPars; + anaPars = DGAnaparHolder(DGPars); pidsel.init(anaPars); grsel.init(goodRunsFile); diff --git a/PWGUD/Tasks/diffMCDataScanner.cxx b/PWGUD/Tasks/diffMCDataScanner.cxx index 6b945059121..369eca48921 100644 --- a/PWGUD/Tasks/diffMCDataScanner.cxx +++ b/PWGUD/Tasks/diffMCDataScanner.cxx @@ -360,7 +360,7 @@ struct TrackTypes { } for (auto ii = 0; ii < nTypes; ii++) { if ((fwdtrack.trackType() & (1 << ii)) > 0) { - registry.get(HIST("FwdType"))->Fill((Double_t)(ii + 2), 0., 1.); + registry.get(HIST("FwdType"))->Fill(static_cast(ii + 2), 0., 1.); } } } diff --git a/PWGUD/Tasks/diffMCQA.cxx b/PWGUD/Tasks/diffMCQA.cxx index c6b1f650db2..9c32645a1bd 100644 --- a/PWGUD/Tasks/diffMCQA.cxx +++ b/PWGUD/Tasks/diffMCQA.cxx @@ -88,7 +88,7 @@ struct DiffMCQA { { maxdEdxTPC = 0.; maxdEdxTOF = 0.; - diffCuts = (DGCutparHolder)DGCuts; + diffCuts = DGCutparHolder(DGCuts); // add histograms for the different process functions if (context.mOptions.get("processMCTruth")) { diff --git a/PWGUD/Tasks/diffQA.cxx b/PWGUD/Tasks/diffQA.cxx index edecda89087..b97b7e3eeb6 100644 --- a/PWGUD/Tasks/diffQA.cxx +++ b/PWGUD/Tasks/diffQA.cxx @@ -98,7 +98,7 @@ struct DiffQA { // initialize global variables maxdEdxTPC = 0.; maxdEdxTOF = 0.; - diffCuts = (DGCutparHolder)DGCuts; + diffCuts = DGCutparHolder(DGCuts); // add histograms for the different process functions if (context.mOptions.get("processMain")) { @@ -950,7 +950,7 @@ struct DiffQA { for (const auto& col : collisions) { bc1 = -1; bc2 = -2; - bc3 = -3; + // bc3 = -3; // FIXME: unused if (col.has_foundBC()) { auto bc = col.foundBC_as(); bc1 = bc.globalBC(); diff --git a/PWGUD/Tasks/exclusivePentaquark.cxx b/PWGUD/Tasks/exclusivePentaquark.cxx index ed5b440add0..a37d73f1096 100644 --- a/PWGUD/Tasks/exclusivePentaquark.cxx +++ b/PWGUD/Tasks/exclusivePentaquark.cxx @@ -116,7 +116,7 @@ struct ExclusivePentaquark { int truegapSide = sgSelector.trueGap(collision, FV0_cut, FT0A_cut, FT0C_cut, ZDC_cut); registry.fill(HIST("GapSide"), gapSide); registry.fill(HIST("TrueGapSide"), truegapSide); - gapSide = truegapSide; + // gapSide = truegapSide; // if (gapSide != gap_Side) { // return; // } diff --git a/PWGUD/Tasks/exclusivePhiLeptons.cxx b/PWGUD/Tasks/exclusivePhiLeptons.cxx index 9576ece02b5..383273a34cc 100644 --- a/PWGUD/Tasks/exclusivePhiLeptons.cxx +++ b/PWGUD/Tasks/exclusivePhiLeptons.cxx @@ -259,7 +259,7 @@ struct ExclusivePhiLeptons { registry.fill(HIST("hEta1"), onlyElectronTracks[1].Eta()); resonance += onlyElectronTracks[0]; resonance += onlyElectronTracks[1]; - sigmaTotal = 0; + // sigmaTotal = 0; // FIXME: unused sigmaTotal = onlyElectronSigma[0] * onlyElectronSigma[0] + onlyElectronSigma[1] * onlyElectronSigma[1]; ; registry.fill(HIST("hNsigEvsKa1"), onlyElectronSigma[0], onlyElectronSigma[1]); @@ -285,7 +285,7 @@ struct ExclusivePhiLeptons { registry.fill(HIST("hEta1"), onlyElectronTracksTOF[0].Eta()); resonance += onlyElectronTracks[0]; resonance += onlyElectronTracksTOF[0]; - sigmaTotal = 0; + // sigmaTotal = 0; // FIXME: unused sigmaTotal = onlyElectronSigma[0] * onlyElectronSigma[0] + onlyElectronSigmaTOF[0] * onlyElectronSigmaTOF[0]; ; registry.fill(HIST("hNsigEvsKa1"), onlyElectronSigma[0], onlyElectronSigmaTOF[0]); @@ -311,7 +311,7 @@ struct ExclusivePhiLeptons { registry.fill(HIST("hEta1"), onlyElectronTracksTOF[1].Eta()); resonance += onlyElectronTracksTOF[0]; resonance += onlyElectronTracksTOF[1]; - sigmaTotal = 0; + // sigmaTotal = 0; // FIXME: unused sigmaTotal = onlyElectronSigmaTOF[0] * onlyElectronSigmaTOF[0] + onlyElectronSigmaTOF[1] * onlyElectronSigmaTOF[1]; ; registry.fill(HIST("hNsigEvsKa1"), onlyElectronSigmaTOF[0], onlyElectronSigmaTOF[1]); diff --git a/PWGUD/Tasks/exclusivePhiLeptonsTrees.cxx b/PWGUD/Tasks/exclusivePhiLeptonsTrees.cxx index 5a7ccd7a315..3bf485b55da 100644 --- a/PWGUD/Tasks/exclusivePhiLeptonsTrees.cxx +++ b/PWGUD/Tasks/exclusivePhiLeptonsTrees.cxx @@ -237,7 +237,7 @@ struct ExclusivePhiLeptonsTrees { resonance += onlyElectronTracks[1]; a += onlyElectronTracks[0]; b += onlyElectronTracks[1]; - sigmaTotal = 0; + // sigmaTotal = 0; // FIXME: unused sigmaTotal = onlyElectronSigma[0] * onlyElectronSigma[0] + onlyElectronSigma[1] * onlyElectronSigma[1]; ; registry.fill(HIST("hNsigEvsKa1"), onlyElectronSigma[0], onlyElectronSigma[1]); @@ -254,7 +254,7 @@ struct ExclusivePhiLeptonsTrees { resonance += onlyElectronTracksTOF[0]; a += onlyElectronTracks[0]; b += onlyElectronTracksTOF[0]; - sigmaTotal = 0; + // sigmaTotal = 0; // FIXME: unused sigmaTotal = onlyElectronSigma[0] * onlyElectronSigma[0] + onlyElectronSigmaTOF[0] * onlyElectronSigmaTOF[0]; ; registry.fill(HIST("hNsigEvsKa1"), onlyElectronSigma[0], onlyElectronSigmaTOF[0]); @@ -271,7 +271,7 @@ struct ExclusivePhiLeptonsTrees { resonance += onlyElectronTracksTOF[1]; a += onlyElectronTracksTOF[0]; b += onlyElectronTracksTOF[1]; - sigmaTotal = 0; + // sigmaTotal = 0; // FIXME: unused sigmaTotal = onlyElectronSigmaTOF[0] * onlyElectronSigmaTOF[0] + onlyElectronSigmaTOF[1] * onlyElectronSigmaTOF[1]; ; registry.fill(HIST("hNsigEvsKa1"), onlyElectronSigmaTOF[0], onlyElectronSigmaTOF[1]); diff --git a/PWGUD/Tasks/exclusiveTwoProtons.cxx b/PWGUD/Tasks/exclusiveTwoProtons.cxx index 3b0b081dc86..5fea2cb18e7 100644 --- a/PWGUD/Tasks/exclusiveTwoProtons.cxx +++ b/PWGUD/Tasks/exclusiveTwoProtons.cxx @@ -284,7 +284,7 @@ struct ExclusiveTwoProtons { resonance += onlyProtonTracksTOF[0]; a = onlyProtonTracks[0]; b = onlyProtonTracksTOF[0]; - sigmaTotal = 0; + // sigmaTotal = 0; // FIXME: unused sigmaTotal = onlyProtonSigma[0] * onlyProtonSigma[0] + onlyProtonSigmaTOF[0] * onlyProtonSigmaTOF[0]; ; registry.fill(HIST("hNsigEvsKa1"), onlyProtonSigma[0], onlyProtonSigmaTOF[0]); @@ -327,7 +327,7 @@ struct ExclusiveTwoProtons { resonance += onlyProtonTracksTOF[1]; a = onlyProtonTracksTOF[0]; b = onlyProtonTracksTOF[1]; - sigmaTotal = 0; + // sigmaTotal = 0; // FIXME: unused sigmaTotal = onlyProtonSigmaTOF[0] * onlyProtonSigmaTOF[0] + onlyProtonSigmaTOF[1] * onlyProtonSigmaTOF[1]; ; registry.fill(HIST("hNsigEvsKa1"), onlyProtonSigmaTOF[0], onlyProtonSigmaTOF[1]); diff --git a/PWGUD/Tasks/exclusiveTwoProtonsSG.cxx b/PWGUD/Tasks/exclusiveTwoProtonsSG.cxx index def67ca1bd0..a28473e7c41 100644 --- a/PWGUD/Tasks/exclusiveTwoProtonsSG.cxx +++ b/PWGUD/Tasks/exclusiveTwoProtonsSG.cxx @@ -311,7 +311,7 @@ struct ExclusiveTwoProtonsSG { resonance += onlyProtonTracksTOF[0]; a = onlyProtonTracks[0]; b = onlyProtonTracksTOF[0]; - sigmaTotal = 0; + // sigmaTotal = 0; // FIXME: unused sigmaTotal = onlyProtonSigma[0] * onlyProtonSigma[0] + onlyProtonSigmaTOF[0] * onlyProtonSigmaTOF[0]; ; registry.fill(HIST("hNsigEvsKa1"), onlyProtonSigma[0], onlyProtonSigmaTOF[0]); @@ -355,7 +355,7 @@ struct ExclusiveTwoProtonsSG { resonance += onlyProtonTracksTOF[1]; a = onlyProtonTracksTOF[0]; b = onlyProtonTracksTOF[1]; - sigmaTotal = 0; + // sigmaTotal = 0; // FIXME: unused sigmaTotal = onlyProtonSigmaTOF[0] * onlyProtonSigmaTOF[0] + onlyProtonSigmaTOF[1] * onlyProtonSigmaTOF[1]; ; registry.fill(HIST("hNsigEvsKa1"), onlyProtonSigmaTOF[0], onlyProtonSigmaTOF[1]); diff --git a/PWGUD/Tasks/flowCorrelationsUpc.cxx b/PWGUD/Tasks/flowCorrelationsUpc.cxx index d708c17e5ae..b4d5e1ee93b 100644 --- a/PWGUD/Tasks/flowCorrelationsUpc.cxx +++ b/PWGUD/Tasks/flowCorrelationsUpc.cxx @@ -77,8 +77,7 @@ struct FlowCorrelationsUpc { O2_DEFINE_CONFIGURABLE(cfgDcaz, bool, false, "choose dcaz") O2_DEFINE_CONFIGURABLE(cfgDcazCut, float, 2.0, "dcaz cut") O2_DEFINE_CONFIGURABLE(cfgMaxTPCChi2NCl, int, 4, "tpcchi2") - O2_DEFINE_CONFIGURABLE(cfgGapSide, int, 1, "choose one side 0:A; 1:C") - O2_DEFINE_CONFIGURABLE(cfgGapSideMerge, bool, true, "merge A and C side") + O2_DEFINE_CONFIGURABLE(cfgGapSide, int, 0, "choose one side 0:A; 1:C") O2_DEFINE_CONFIGURABLE(cfgCutTPCCrossedRows, float, 70.0f, "minimum number of crossed TPC Rows") O2_DEFINE_CONFIGURABLE(cfgCutTPCclu, float, 50.0f, "minimum number of found TPC clusters") O2_DEFINE_CONFIGURABLE(cfgCutITSclu, float, 5.0f, "minimum number of ITS clusters") @@ -91,7 +90,7 @@ struct FlowCorrelationsUpc { O2_DEFINE_CONFIGURABLE(cfgRctFlagEnabled, bool, false, "use run condition table flag") O2_DEFINE_CONFIGURABLE(cfgRctFlagIndex, int, 1, "1: isCBTOk; 2:isCBTZdcOk; 3: isCBTHadronOk; 4:isCBTHadronZdcOk ") O2_DEFINE_CONFIGURABLE(cfgIRMaxCut, double, 50, "maximum interaction rate for UPC events") - O2_DEFINE_CONFIGURABLE(cfgZdcTime, bool, false, "choose zdc time cut") + O2_DEFINE_CONFIGURABLE(cfgZdcTime, bool, true, "choose zdc time cut") O2_DEFINE_CONFIGURABLE(cfgZdcTimeCut, float, 2.0, "zdc time cut") O2_DEFINE_CONFIGURABLE(cfgSbp, bool, true, "choose sbp") O2_DEFINE_CONFIGURABLE(cfgvtxITSTPC, bool, true, "choose vtxITSTPC") @@ -122,11 +121,12 @@ struct FlowCorrelationsUpc { // make the filters and cuts. Filter trackFilter = (aod::udtrack::isPVContributor == true); - Filter collisionFilter = cfgGapSideMerge - ? ((aod::udcollision::gapSide == (uint8_t)0 || aod::udcollision::gapSide == (uint8_t)1) && - (aod::upcservice::truegapside == 0 || aod::upcservice::truegapside == 1)) - : ((aod::udcollision::gapSide == (uint8_t)cfgGapSide) && - (aod::upcservice::truegapside == cfgGapSide)); + Filter collisionFilter = ifnode( + cfgGapSide.node() == 0, + ((aod::udcollision::gapSide == static_cast(0)) && + (aod::upcservice::truegapside == 0)), + ((aod::udcollision::gapSide == static_cast(1)) && + (aod::upcservice::truegapside == 1))); // Connect to ccdb Service ccdb{}; @@ -264,13 +264,13 @@ struct FlowCorrelationsUpc { registry.fill(HIST("neutronClass"), 0, 0); } if (std::abs(timeZNA) <= cfgZdcTimeCut && std::abs(timeZNC) > cfgZdcTimeCut) { - neutronClass = 1; - registry.fill(HIST("neutronClass"), 0, 1); - } - if (std::abs(timeZNA) > cfgZdcTimeCut && std::abs(timeZNC) <= cfgZdcTimeCut) { neutronClass = 2; registry.fill(HIST("neutronClass"), 1, 0); } + if (std::abs(timeZNA) > cfgZdcTimeCut && std::abs(timeZNC) <= cfgZdcTimeCut) { + neutronClass = 1; + registry.fill(HIST("neutronClass"), 0, 1); + } if (std::abs(timeZNA) <= cfgZdcTimeCut && std::abs(timeZNC) <= cfgZdcTimeCut) { neutronClass = 3; registry.fill(HIST("neutronClass"), 1, 1); @@ -281,15 +281,11 @@ struct FlowCorrelationsUpc { return false; } // if A or C gap is requested, keep corresponding neutron class - if (cfgGapSideMerge) { - // accepted - } else { - if (cfgGapSide == 0 || cfgGapSide == 1) { - if ((cfgGapSide == 0 && neutronClass == 1) || (cfgGapSide == 1 && neutronClass == 2)) { // o2-linter: disable=magic-number (ZDC time cut) - // accepted - } else { - return false; - } + if (cfgGapSide == 0 || cfgGapSide == 1) { + if ((cfgGapSide == 0 && neutronClass == 1) || (cfgGapSide == 1 && neutronClass == 2)) { // o2-linter: disable=magic-number (ZDC time cut) + // accepted + } else { + return false; } } } @@ -332,10 +328,6 @@ struct FlowCorrelationsUpc { return false; } - if (!zdcTimeCut(collision)) { - return false; - } - return true; } @@ -618,7 +610,7 @@ struct FlowCorrelationsUpc { }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {vtxMix, multMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {vtxMix, multMix}}; auto tracksTuple = std::make_tuple(tracks); SameKindPair pairs{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; diff --git a/PWGUD/Tasks/flowCumulantsUpc.cxx b/PWGUD/Tasks/flowCumulantsUpc.cxx index e7ccfdb51e4..01bf6d76a7c 100644 --- a/PWGUD/Tasks/flowCumulantsUpc.cxx +++ b/PWGUD/Tasks/flowCumulantsUpc.cxx @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -159,6 +160,7 @@ struct FlowCumulantsUpc { TRandom3* fRndmMc = new TRandom3(0); int lastRunNumber = -1; std::vector runNumbers; + std::map neventsIdBins; std::map> th3sPerRun; // map of TH3 histograms for all runs using UdTracks = soa::Join; @@ -320,6 +322,26 @@ struct FlowCumulantsUpc { } } } + + // One Y bin for every FlowContainer IDName already stored in oba. + const int nIdNames = oba->GetEntries(); + + const AxisSpec axisIdName{nIdNames, 0.5, static_cast(nIdNames) + 0.5, "ID name"}; + + registry.add("hNeventsVsNch", "Selected events;N_{ch};ID name", {HistType::kTH2D, {axisIndependent, axisIdName}}); + + auto hNeventsVsNch = registry.get(HIST("hNeventsVsNch")); + + for (int i = 0; i < nIdNames; ++i) { + auto* observable = dynamic_cast(oba->At(i)); + if (!observable) { + continue; + } + const std::string idName = observable->GetName(); + const int yBin = i + 1; + neventsIdBins[idName] = yBin; + hNeventsVsNch->GetYaxis()->SetBinLabel(yBin, idName.c_str()); + } fFC->SetName("FlowContainer"); fFC->SetXAxis(fPtAxis); fFC->Initialize(oba, axisIndependent, cfgNbootstrap); @@ -520,6 +542,16 @@ struct FlowCumulantsUpc { fGFW->CreateRegions(); } + void fillNeventsVsNch(const std::string& idName, double independent) + { + const auto it = neventsIdBins.find(idName); + if (it == neventsIdBins.end()) { + return; + } + + registry.fill(HIST("hNeventsVsNch"), independent, static_cast(it->second)); + } + void createOutputObjectsForRun(int runNumber) { const AxisSpec axisPhi{60, 0.0, constants::math::TwoPI, "#varphi"}; @@ -538,6 +570,7 @@ struct FlowCumulantsUpc { val = fGFW->Calculate(corrconf, 0, kFALSE).real() / dnx; if (std::fabs(val) < 1) { fFC->FillProfile(corrconf.Head.c_str(), cent, val, dnx, rndm); + fillNeventsVsNch(corrconf.Head, cent); } return; } @@ -738,13 +771,13 @@ struct FlowCumulantsUpc { registry.fill(HIST("neutronClass"), 0, 0); } if (std::abs(timeZNA) <= cfgZdcTimeCut && std::abs(timeZNC) > cfgZdcTimeCut) { - neutronClass = 1; - registry.fill(HIST("neutronClass"), 0, 1); - } - if (std::abs(timeZNA) > cfgZdcTimeCut && std::abs(timeZNC) <= cfgZdcTimeCut) { neutronClass = 2; registry.fill(HIST("neutronClass"), 1, 0); } + if (std::abs(timeZNA) > cfgZdcTimeCut && std::abs(timeZNC) <= cfgZdcTimeCut) { + neutronClass = 1; + registry.fill(HIST("neutronClass"), 0, 1); + } if (std::abs(timeZNA) <= cfgZdcTimeCut && std::abs(timeZNC) <= cfgZdcTimeCut) { neutronClass = 3; registry.fill(HIST("neutronClass"), 1, 1); diff --git a/PWGUD/Tasks/flowLongRangeCorrUpc.cxx b/PWGUD/Tasks/flowLongRangeCorrUpc.cxx index 7fbca9da958..e5766de17f2 100644 --- a/PWGUD/Tasks/flowLongRangeCorrUpc.cxx +++ b/PWGUD/Tasks/flowLongRangeCorrUpc.cxx @@ -115,9 +115,9 @@ struct FlowLongRangeCorrUpc { // make the filters and cuts. Filter trackFilter = (aod::udtrack::isPVContributor == true); Filter collisionFilter = (cfgGapSideMerge == true) - ? ((aod::udcollision::gapSide == (uint8_t)0 || aod::udcollision::gapSide == (uint8_t)1) && + ? ((aod::udcollision::gapSide == static_cast(0) || aod::udcollision::gapSide == static_cast(1)) && (aod::upcservice::truegapside == 0 || aod::upcservice::truegapside == 1)) - : ((aod::udcollision::gapSide == (uint8_t)cfgGapSide) && + : ((aod::udcollision::gapSide == static_cast(cfgGapSide)) && (aod::upcservice::truegapside == cfgGapSide)); using UDTracksFull = soa::Filtered>; @@ -584,7 +584,7 @@ struct FlowLongRangeCorrUpc { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -652,7 +652,7 @@ struct FlowLongRangeCorrUpc { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -719,7 +719,7 @@ struct FlowLongRangeCorrUpc { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip diff --git a/PWGUD/Tasks/ptSpectraInclusiveUpc.cxx b/PWGUD/Tasks/ptSpectraInclusiveUpc.cxx index ce4feb743db..0ffc5ee5f6d 100644 --- a/PWGUD/Tasks/ptSpectraInclusiveUpc.cxx +++ b/PWGUD/Tasks/ptSpectraInclusiveUpc.cxx @@ -11,8 +11,10 @@ /// /// \file ptSpectraInclusiveUpc.cxx /// \executable o2-analysis-ud-pt-spectra-inclusive-upc -/// \brief Task for the of pT spectra of pions, kaons and protons in inclusive UPC events. -/// Used to obtain the templates for the DCA_xy fits for the primary fractions +/// \brief Task for pT spectra of pions, kaons and protons in inclusive UPC events. +/// Used to obtain the templates for the DCA_xy fits for the primary fractions. +/// Fractions are obtained separately for TPC and TOF spectra: a particle passing both PID +/// selections will contribute to both histograms. /// /// \author Andrea Giovanni Riffero andrea.giovanni.riffero@cern.ch @@ -50,10 +52,24 @@ struct PtSpectraInclusiveUpc { HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - Configurable nBinsPt{"nBinsPt", 100, "N bins in pT histos"}; - Configurable nBinsDCAxy{"nBinsDCAxy", 100, "N bins in DCA_{xy} histos"}; + ConfigurableAxis ptBinning{"ptBinning", + {VARIABLE_WIDTH, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, + 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, + 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0}, + "#it{p}_{T} (GeV/#it{c})"}; + + ConfigurableAxis dcaXYaxis{"dcaXYaxis", {1000, -0.6, 0.6}, "DCA_{xy} (cm) binning"}; Configurable applyKineCutsInGen{"applyKineCutsInGen", false, "Apply kinematic cuts in the generated level"}; + Configurable etaMax{"etaMax", 0.9, "Maximum track pseudorapidity"}; + Configurable yMax{"yMax", 0.9, "Maximum particle rapidity"}; + Configurable ptMin{"ptMin", 0.1, "Minimum track transverse momentum (GeV/c)"}; + Configurable nFindableMin{"nFindableMin", 70, "Minimum number of findable TPC clusters"}; + Configurable sigmaMax{"sigmaMax", 3., "Maximum absolute PID n-sigma"}; + Configurable dcaZlimit{"dcaZlimit", 2., "Maximum absolute DCA in z (cm)"}; + Configurable maxChi2TPC{"maxChi2TPC", 4., "Maximum TPC chi2 per cluster"}; + Configurable maxChi2ITS{"maxChi2ITS", 36., "Maximum ITS chi2 per cluster"}; + // define abbreviations using CCs = soa::Join; using CC = CCs::iterator; @@ -62,53 +78,172 @@ struct PtSpectraInclusiveUpc { using TCs = soa::Join; using TCMCs = soa::Join; - const double etaMax = 0.9; - const double yMax = 0.9; - const double ptMin = 0.1; - const int nFindableMin = 70; - const double sigmaMax = 3.; - const double dcaZlimit = 2.; - void init(InitContext const&) { // axes - const AxisSpec axisPt{nBinsPt, 0, 5, "#it{p}_{T} GeV/#it{c}"}; - const AxisSpec axisEventCounter{2, 0.5, 2.5, "Event type"}; - const AxisSpec axisDCAxy{nBinsDCAxy, -0.6, 0.6, "DCA_{xy} cm"}; + const AxisSpec axisPt{ + ptBinning, + "#it{p}_{T} (GeV/#it{c})", + "axisPt"}; + + const AxisSpec axisEventCounter{ + 2, + 0.5, + 2.5, + "Event type"}; + + const AxisSpec axisDCAxy{ + dcaXYaxis, + "DCA_{xy} (cm)"}; // histograms - histos.add("ptGeneratedPion", "ptGeneratedPion", kTH1F, {axisPt}); - histos.add("ptGeneratedKaon", "ptGeneratedKaon", kTH1F, {axisPt}); - histos.add("ptGeneratedProton", "ptGeneratedProton", kTH1F, {axisPt}); - - histos.add("ptReconstructedTPCPion", "ptReconstructedTPCPion", kTH1F, {axisPt}); - histos.add("ptReconstructedTPCKaon", "ptReconstructedTPCKaon", kTH1F, {axisPt}); - histos.add("ptReconstructedTPCProton", "ptReconstructedTPCProton", kTH1F, {axisPt}); - - histos.add("ptReconstructedTOFPion", "ptReconstructedTOFPion", kTH1F, {axisPt}); - histos.add("ptReconstructedTOFKaon", "ptReconstructedTOFKaon", kTH1F, {axisPt}); - histos.add("ptReconstructedTOFProton", "ptReconstructedTOFProton", kTH1F, {axisPt}); - - histos.add("ptDataTPCPion", "ptDataTPCPion", kTH1F, {axisPt}); - histos.add("ptDataTPCKaon", "ptDataTPCKaon", kTH1F, {axisPt}); - histos.add("ptDataTPCProton", "ptDataTPCProton", kTH1F, {axisPt}); - - histos.add("ptDataTOFPion", "ptDataTOFPion", kTH1F, {axisPt}); - histos.add("ptDataTOFKaon", "ptDataTOFKaon", kTH1F, {axisPt}); - histos.add("ptDataTOFProton", "ptDataTOFProton", kTH1F, {axisPt}); - - histos.add("myEventCounter", "myEventCounter", kTH1F, {axisEventCounter}); - histos.add("DCAxy_primary_pions", "DCAxy_primary_pions", kTH1F, {axisDCAxy}); - histos.add("DCAxy_secondary_pions", "DCAxy_secondary_pions", kTH1F, {axisDCAxy}); - histos.add("DCAxy_primary_kaons", "DCAxy_primary_kaons", kTH1F, {axisDCAxy}); - histos.add("DCAxy_secondary_kaons", "DCAxy_secondary_kaons", kTH1F, {axisDCAxy}); - histos.add("DCAxy_primary_protons", "DCAxy_primary_protons", kTH1F, {axisDCAxy}); - histos.add("DCAxy_secondary_protons", "DCAxy_secondary_protons", kTH1F, {axisDCAxy}); - histos.add("DCAxy_material_protons", "DCAxy_material_protons", kTH1F, {axisDCAxy}); - histos.add("DCAxy_data_pions", "DCAxy_data_pions", kTH1F, {axisDCAxy}); - histos.add("DCAxy_data_kaons", "DCAxy_data_kaons", kTH1F, {axisDCAxy}); - histos.add("DCAxy_data_protons", "DCAxy_data_protons", kTH1F, {axisDCAxy}); + // generated pT spectra + histos.add("ptGeneratedPion", "Generated pions;#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + histos.add("ptGeneratedKaon", "Generated kaons;#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + histos.add("ptGeneratedProton", "Generated protons;#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + + // reconstructed pT spectra with TPC PID + histos.add("ptReconstructedTPCPion", "Reconstructed pions (TPC);#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + histos.add("ptReconstructedTPCKaon", "Reconstructed kaons (TPC);#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + histos.add("ptReconstructedTPCProton", "Reconstructed protons (TPC);#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + + // reconstructed pT spectra with TOF PID + histos.add("ptReconstructedTOFPion", "Reconstructed TOF pions;#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + histos.add("ptReconstructedTOFKaon", "Reconstructed TOF kaons;#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + histos.add("ptReconstructedTOFProton", "Reconstructed TOF protons;#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + + // data pT spectra with TPC PID + histos.add("ptDataTPCPion", "Data pions (TPC);#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + histos.add("ptDataTPCKaon", "Data kaons (TPC);#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + histos.add("ptDataTPCProton", "Data protons (TPC);#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + + // data pT spectra with TOF PID + histos.add("ptDataTOFPion", "Data pions (TOF);#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + histos.add("ptDataTOFKaon", "Data kaons (TOF);#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + histos.add("ptDataTOFProton", "Data protons (TOF);#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + + histos.add("myEventCounter", "Event counter;Event type;Counts", kTH1F, {axisEventCounter}); + + histos.add( + "DCAxy_TPC_primary_pions", + "Primary pions (TPC);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TPC_secondary_pions", + "Secondary pions (TPC);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TPC_primary_kaons", + "Primary kaons (TPC);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TPC_secondary_kaons", + "Secondary kaons (TPC);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TPC_primary_protons", + "Primary protons (TPC);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TPC_secondary_protons", + "Secondary protons from decays (TPC);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TPC_material_protons", + "Secondary protons from material (TPC);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TPC_data_pions", + "Data pion candidates (TPC);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TPC_data_kaons", + "Data kaon candidates (TPC);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TPC_data_protons", + "Data proton candidates (TPC);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TOF_primary_pions", + "Primary pions (TOF);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TOF_secondary_pions", + "Secondary pions (TOF);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TOF_primary_kaons", + "Primary kaons (TOF);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TOF_secondary_kaons", + "Secondary kaons (TOF);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TOF_primary_protons", + "Primary protons (TOF);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TOF_secondary_protons", + "Secondary protons from decays (TOF);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TOF_material_protons", + "Secondary protons from material (TOF);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TOF_data_pions", + "Data pion candidates (TOF);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TOF_data_kaons", + "Data kaon candidates (TOF);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TOF_data_protons", + "Data proton candidates (TOF);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); } void processSim(aod::UDMcCollision const&, aod::UDMcParticles const& mcParticles) @@ -151,10 +286,12 @@ struct PtSpectraInclusiveUpc { } } - void processReco(CCMC const&, TCMCs const& tracks, aod::UDMcParticles const&) + template + void selectDataAndFillHistos(TCollision const& /*collision*/, TTracks const& tracks) { double dcaXyLimit = 0; + bool passDCAxyCut = true; auto nSigmaPi = -999.; auto nSigmaKa = -999.; @@ -163,11 +300,23 @@ struct PtSpectraInclusiveUpc { std::array trackMomentum; for (const auto& track : tracks) { + passDCAxyCut = true; + if (!track.isPVContributor()) { continue; } - if (track.tpcNClsFindable() < nFindableMin) { + // here TPC selection on findable number of clusters + if (track.hasTPC()) { + if (track.tpcNClsFindable() < nFindableMin) { + continue; + } + if (track.tpcChi2NCl() > maxChi2TPC) { + continue; + } + } + + if (track.itsChi2NCl() > maxChi2ITS) { continue; } @@ -180,121 +329,150 @@ struct PtSpectraInclusiveUpc { } dcaXyLimit = 0.0105 + 0.035 / std::pow(track.pt(), 1.1); - if (!(std::abs(track.dcaXY()) < dcaXyLimit)) { - continue; + if ((std::abs(track.dcaXY()) > dcaXyLimit)) { + passDCAxyCut = false; + } + + bool isPrimary = false; + bool isDecay = false; + if constexpr (isMc) { + if (!track.has_udMcParticle()) { + continue; + } + const auto mcParticle = track.udMcParticle(); + isPrimary = mcParticle.isPhysicalPrimary(); + isDecay = mcParticle.getProcess() == kPDecay; } trackMomentum[0] = track.px(); trackMomentum[1] = track.py(); trackMomentum[2] = track.pz(); - if (!track.has_udMcParticle()) { - continue; - } - auto mcParticle = track.udMcParticle(); - - bool hasTpc = false; + // compute rapidity for each ma // TPC tracks if (track.hasTPC()) { - hasTpc = true; nSigmaPi = track.tpcNSigmaPi(); nSigmaKa = track.tpcNSigmaKa(); nSigmaPr = track.tpcNSigmaPr(); if (std::abs(nSigmaPi) < sigmaMax) { - if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassPionCharged)) > yMax) { - continue; - } - - if (mcParticle.isPhysicalPrimary()) { - histos.fill(HIST("ptReconstructedTPCPion"), track.pt()); - histos.fill(HIST("DCAxy_primary_pions"), track.dcaXY()); - } else { - histos.fill(HIST("DCAxy_secondary_pions"), track.dcaXY()); + if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassPionCharged)) < yMax) { + if constexpr (isMc) { + if (isPrimary) { + if (passDCAxyCut) + histos.fill(HIST("ptReconstructedTPCPion"), track.pt()); + histos.fill(HIST("DCAxy_TPC_primary_pions"), track.pt(), track.dcaXY()); + } else { + histos.fill(HIST("DCAxy_TPC_secondary_pions"), track.pt(), track.dcaXY()); + } + } else { + if (passDCAxyCut) + histos.fill(HIST("ptDataTPCPion"), track.pt()); + histos.fill(HIST("DCAxy_TPC_data_pions"), track.pt(), track.dcaXY()); + } } } if (std::abs(nSigmaKa) < sigmaMax) { - if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassKaonCharged)) > yMax) { - continue; - } - - if (mcParticle.isPhysicalPrimary()) { - histos.fill(HIST("ptReconstructedTPCKaon"), track.pt()); - histos.fill(HIST("DCAxy_primary_kaons"), track.dcaXY()); - } else { - histos.fill(HIST("DCAxy_secondary_kaons"), track.dcaXY()); + if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassKaonCharged)) < yMax) { + if constexpr (isMc) { + if (isPrimary) { + if (passDCAxyCut) + histos.fill(HIST("ptReconstructedTPCKaon"), track.pt()); + histos.fill(HIST("DCAxy_TPC_primary_kaons"), track.pt(), track.dcaXY()); + } else { + histos.fill(HIST("DCAxy_TPC_secondary_kaons"), track.pt(), track.dcaXY()); + } + } else { + if (passDCAxyCut) + histos.fill(HIST("ptDataTPCKaon"), track.pt()); + histos.fill(HIST("DCAxy_TPC_data_kaons"), track.pt(), track.dcaXY()); + } } } if (std::abs(nSigmaPr) < sigmaMax) { - if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassProton)) > yMax) { - continue; - } - - if (mcParticle.isPhysicalPrimary()) { - histos.fill(HIST("ptReconstructedTPCProton"), track.pt()); - histos.fill(HIST("DCAxy_primary_protons"), track.dcaXY()); - } else { - if (mcParticle.getProcess() == kPDecay) { - histos.fill(HIST("DCAxy_secondary_protons"), track.dcaXY()); + if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassProton)) < yMax) { + if constexpr (isMc) { + if (isPrimary) { + if (passDCAxyCut) + histos.fill(HIST("ptReconstructedTPCProton"), track.pt()); + histos.fill(HIST("DCAxy_TPC_primary_protons"), track.pt(), track.dcaXY()); + } else { + if (isDecay) { + histos.fill(HIST("DCAxy_TPC_secondary_protons"), track.pt(), track.dcaXY()); + } else { + histos.fill(HIST("DCAxy_TPC_material_protons"), track.pt(), track.dcaXY()); + } + } } else { - histos.fill(HIST("DCAxy_material_protons"), track.dcaXY()); + if (passDCAxyCut) + histos.fill(HIST("ptDataTPCProton"), track.pt()); + histos.fill(HIST("DCAxy_TPC_data_protons"), track.pt(), track.dcaXY()); } } } } - // TPC tracks + // TOF tracks if (track.hasTOF()) { nSigmaPi = track.tofNSigmaPi(); nSigmaKa = track.tofNSigmaKa(); nSigmaPr = track.tofNSigmaPr(); if (std::abs(nSigmaPi) < sigmaMax) { - if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassPionCharged)) > yMax) { - continue; - } - - if (mcParticle.isPhysicalPrimary()) { - histos.fill(HIST("ptReconstructedTOFPion"), track.pt()); - if (!hasTpc) - histos.fill(HIST("DCAxy_primary_pions"), track.dcaXY()); - } else { - if (!hasTpc) - histos.fill(HIST("DCAxy_secondary_pions"), track.dcaXY()); + if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassPionCharged)) < yMax) { + if constexpr (isMc) { + if (isPrimary) { + if (passDCAxyCut) + histos.fill(HIST("ptReconstructedTOFPion"), track.pt()); + histos.fill(HIST("DCAxy_TOF_primary_pions"), track.pt(), track.dcaXY()); + } else { + histos.fill(HIST("DCAxy_TOF_secondary_pions"), track.pt(), track.dcaXY()); + } + } else { + if (passDCAxyCut) + histos.fill(HIST("ptDataTOFPion"), track.pt()); + histos.fill(HIST("DCAxy_TOF_data_pions"), track.pt(), track.dcaXY()); + } } } - if (std::abs(nSigmaKa) < sigmaMax) { - if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassKaonCharged)) > yMax) { - continue; - } - if (mcParticle.isPhysicalPrimary()) { - histos.fill(HIST("ptReconstructedTOFKaon"), track.pt()); - if (!hasTpc) - histos.fill(HIST("DCAxy_primary_kaons"), track.dcaXY()); - } else { - if (!hasTpc) - histos.fill(HIST("DCAxy_secondary_kaons"), track.dcaXY()); + if (std::abs(nSigmaKa) < sigmaMax) { + if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassKaonCharged)) < yMax) { + if constexpr (isMc) { + if (isPrimary) { + if (passDCAxyCut) + histos.fill(HIST("ptReconstructedTOFKaon"), track.pt()); + histos.fill(HIST("DCAxy_TOF_primary_kaons"), track.pt(), track.dcaXY()); + } else { + histos.fill(HIST("DCAxy_TOF_secondary_kaons"), track.pt(), track.dcaXY()); + } + } else { + if (passDCAxyCut) + histos.fill(HIST("ptDataTOFKaon"), track.pt()); + histos.fill(HIST("DCAxy_TOF_data_kaons"), track.pt(), track.dcaXY()); + } } } if (std::abs(nSigmaPr) < sigmaMax) { - if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassProton)) > yMax) { - continue; - } - - if (mcParticle.isPhysicalPrimary()) { - histos.fill(HIST("ptReconstructedTOFProton"), track.pt()); - if (!hasTpc) - histos.fill(HIST("DCAxy_primary_protons"), track.dcaXY()); - } else { - if (!hasTpc) { - if (mcParticle.getProcess() == kPDecay) { - histos.fill(HIST("DCAxy_secondary_protons"), track.dcaXY()); + if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassProton)) < yMax) { + if constexpr (isMc) { + if (isPrimary) { + if (passDCAxyCut) + histos.fill(HIST("ptReconstructedTOFProton"), track.pt()); + histos.fill(HIST("DCAxy_TOF_primary_protons"), track.pt(), track.dcaXY()); } else { - histos.fill(HIST("DCAxy_material_protons"), track.dcaXY()); + if (isDecay) { + histos.fill(HIST("DCAxy_TOF_secondary_protons"), track.pt(), track.dcaXY()); + } else { + histos.fill(HIST("DCAxy_TOF_material_protons"), track.pt(), track.dcaXY()); + } } + } else { + if (passDCAxyCut) + histos.fill(HIST("ptDataTOFProton"), track.pt()); + histos.fill(HIST("DCAxy_TOF_data_protons"), track.pt(), track.dcaXY()); } } } @@ -302,109 +480,14 @@ struct PtSpectraInclusiveUpc { } } - void processData(CC const&, TCs const& tracks) + void processReco(CCMC const& collision, TCMCs const& tracks, aod::UDMcParticles const&) { + selectDataAndFillHistos(collision, tracks); + } - double dcaXyLimit = 0; - - auto nSigmaPi = -999.; - auto nSigmaKa = -999.; - auto nSigmaPr = -999.; - - std::array trackMomentum; - - for (const auto& track : tracks) { - if (!track.isPVContributor()) { - continue; - } - - if (track.tpcNClsFindable() < nFindableMin) { - continue; - } - - if (track.pt() < ptMin) { - continue; - } - - if (!(std::abs(track.dcaZ()) < dcaZlimit)) { - continue; - } - - dcaXyLimit = 0.0105 + 0.035 / std::pow(track.pt(), 1.1); - if (!(std::abs(track.dcaXY()) < dcaXyLimit)) { - continue; - } - - trackMomentum[0] = track.px(); - trackMomentum[1] = track.py(); - trackMomentum[2] = track.pz(); - - bool hasTpc = false; - // TPC tracks - if (track.hasTPC()) { - hasTpc = true; - nSigmaPi = track.tpcNSigmaPi(); - nSigmaKa = track.tpcNSigmaKa(); - nSigmaPr = track.tpcNSigmaPr(); - - if (std::abs(nSigmaPi) < sigmaMax) { - if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassPionCharged)) > yMax) { - continue; - } - histos.fill(HIST("ptDataTPCPion"), track.pt()); - histos.fill(HIST("DCAxy_data_pions"), track.dcaXY()); - } - - if (std::abs(nSigmaKa) < sigmaMax) { - if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassKaonCharged)) > yMax) { - continue; - } - histos.fill(HIST("ptDataTPCKaon"), track.pt()); - histos.fill(HIST("DCAxy_data_kaons"), track.dcaXY()); - } - - if (std::abs(nSigmaPr) < sigmaMax) { - if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassProton)) > yMax) { - continue; - } - histos.fill(HIST("ptDataTPCProton"), track.pt()); - histos.fill(HIST("DCAxy_data_protons"), track.dcaXY()); - } - } - - // TPC tracks - if (track.hasTOF()) { - nSigmaPi = track.tofNSigmaPi(); - nSigmaKa = track.tofNSigmaKa(); - nSigmaPr = track.tofNSigmaPr(); - - if (std::abs(nSigmaPi) < sigmaMax) { - if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassPionCharged)) > yMax) { - continue; - } - histos.fill(HIST("ptDataTOFPion"), track.pt()); - if (!hasTpc) - histos.fill(HIST("DCAxy_data_pions"), track.dcaXY()); - } - if (std::abs(nSigmaKa) < sigmaMax) { - if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassKaonCharged)) > yMax) { - continue; - } - histos.fill(HIST("ptDataTOFKaon"), track.pt()); - if (!hasTpc) - histos.fill(HIST("DCAxy_data_kaons"), track.dcaXY()); - } - - if (std::abs(nSigmaPr) < sigmaMax) { - if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassProton)) > yMax) { - continue; - } - histos.fill(HIST("ptDataTOFProton"), track.pt()); - if (!hasTpc) - histos.fill(HIST("DCAxy_data_protons"), track.dcaXY()); - } - } - } + void processData(CC const& collision, TCs const& tracks) + { + selectDataAndFillHistos(collision, tracks); } PROCESS_SWITCH(PtSpectraInclusiveUpc, processSim, "processSim", false); diff --git a/PWGUD/Tasks/sgDeuteronSpectra.cxx b/PWGUD/Tasks/sgDeuteronSpectra.cxx new file mode 100644 index 00000000000..109afa51a79 --- /dev/null +++ b/PWGUD/Tasks/sgDeuteronSpectra.cxx @@ -0,0 +1,212 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file sgDeuteronSpectra.cxx +/// \brief Analysis for the (anti)deuteron production in UPC +/// \author Marika Rasa +/// \since September 2026 + +#include "PWGUD/Core/SGSelector.h" +#include "PWGUD/Core/SGTrackSelector.h" +#include "PWGUD/DataModel/UDTables.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +struct SgDeuteronSpectra { + // UPC cuts + SGSelector sgSelector; + Configurable fv0Cut{"fv0Cut", 50., "FV0A threshold"}; + Configurable zdcCut{"zdcCut", 10., "ZDC threshold"}; + Configurable ft0aCut{"ft0aCut", 150., "FT0A threshold"}; + Configurable ft0cCut{"ft0cCut", 50., "FT0C threshold"}; + Configurable fddaCut{"fddaCut", 10000., "FDDA threshold"}; + Configurable fddcCut{"fddcCut", 10000., "FDDC threshold"}; + + // Track cuts + Configurable pvCut{"pvCut", 1.0, "Use Only PV tracks"}; + Configurable dcaZCut{"dcaZCut", 2.0, "dcaZ cut"}; + Configurable dcaXYCut{"dcaXYCut", 0.0, "dcaXY cut (0 for Pt-function)"}; + Configurable tpcChi2Cut{"tpcChi2Cut", 4, "Max tpcChi2NCl"}; + Configurable tpcNClsFindableCut{"tpcNClsFindableCut", 70, "Min tpcNClsFindable"}; + Configurable itsChi2Cut{"itsChi2Cut", 36, "Max itsChi2NCl"}; + Configurable etaCut{"etaCut", 0.9, "Track Pseudorapidity"}; + Configurable ptCut{"ptCut", 0.1, "Track Pt"}; + Configurable nsigmatpcPreselCut{"nsigmatpcPreselCut", 3.0, "nsigma TPC preselection cut for TOF analysis"}; + Configurable occupancyCut{"occupancyCut", 1000, "Occupancy cut"}; + Configurable vertexZCut{"vertexZCut", 10, "VertexZ cut"}; + Configurable maxTracks{"maxTracks", 100, "Maximum number of tracks in the event"}; + Configurable useVtxFT0Cut{"useVtxFT0Cut", true, "Use zVtxFT0vPV cut in event selection"}; + + // Configurable axis for histograms + ConfigurableAxis ptAxis{"ptAxis", {200, 0.0, 10.0}, "p_{T}"}; + ConfigurableAxis nSigmaAxis{"nSigmaAxis", {800, -20.0, 20.0}, "nSigma axis for TPC and TOF"}; + + // Initialize histogram registry + HistogramRegistry registry{"registry", {}}; + + void init(InitContext&) + { + const AxisSpec axispt{ptAxis, "p_{T}"}; + const AxisSpec axistpc{nSigmaAxis, "n#sigma_{TPC}"}; + const AxisSpec axistof{nSigmaAxis, "n#sigma_{TOF}"}; + + // Collision histograms + registry.add("collisions/selectionCuts", "", {HistType::kTH1F, {{8, -0.5, 7.5}}}); + registry.add("collisions/VertexZ", "", {HistType::kTH1F, {{800, -20, 20}}}); + registry.add("collisions/GapSide", "Gap Side: A, C, A+C", {HistType::kTH1F, {{3, -0.5, 2.5}}}); + registry.add("collisions/TrueGapSide", "Gap Side: A, C, A+C", {HistType::kTH1F, {{4, -1.5, 2.5}}}); + registry.add("tracks/Deut_Pt_TPC_GapA", "", {HistType::kTH2F, {axispt, axistpc}}); + registry.add("tracks/Deut_Pt_TOF_GapA", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Deut_Pt_TOF_GapA_TPCpresel", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Antideut_Pt_TPC_GapA", "", {HistType::kTH2F, {axispt, axistpc}}); + registry.add("tracks/Antideut_Pt_TOF_GapA", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Antideut_Pt_TOF_GapA_TPCpresel", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Deut_Pt_TPC_GapC", "", {HistType::kTH2F, {axispt, axistpc}}); + registry.add("tracks/Deut_Pt_TOF_GapC", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Deut_Pt_TOF_GapC_TPCpresel", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Antideut_Pt_TPC_GapC", "", {HistType::kTH2F, {axispt, axistpc}}); + registry.add("tracks/Antideut_Pt_TOF_GapC", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Antideut_Pt_TOF_GapC_TPCpresel", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Deut_Pt_TPC_DoubleGap", "", {HistType::kTH2F, {axispt, axistpc}}); + registry.add("tracks/Deut_Pt_TOF_DoubleGap", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Deut_Pt_TOF_DoubleGap_TPCpresel", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Antideut_Pt_TPC_DoubleGap", "", {HistType::kTH2F, {axispt, axistpc}}); + registry.add("tracks/Antideut_Pt_TOF_DoubleGap", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Antideut_Pt_TOF_DoubleGap_TPCpresel", "", {HistType::kTH2F, {axispt, axistof}}); + } + + // Define data types + using UDCollisionsFull = soa::Join; // UDCollisions + using UDCollisionFull = UDCollisionsFull::iterator; + using UDTracksFull = soa::Join; + + void process(UDCollisionFull const& coll, UDTracksFull const& tracks) + { + registry.fill(HIST("collisions/VertexZ"), coll.posZ()); + registry.fill(HIST("collisions/selectionCuts"), 0); + // applying event cuts + if (!coll.tfb()) { + return; + } + registry.fill(HIST("collisions/selectionCuts"), 1); + if (!coll.itsROFb()) { + return; + } + registry.fill(HIST("collisions/selectionCuts"), 2); + if (!coll.sbp()) { + return; + } + registry.fill(HIST("collisions/selectionCuts"), 3); + if (useVtxFT0Cut) { + if (!coll.zVtxFT0vPV()) { + return; + } + registry.fill(HIST("collisions/selectionCuts"), 4); + } else { + registry.fill(HIST("collisions/selectionCuts"), 4); + } + if (!coll.vtxITSTPC()) { + return; + } + registry.fill(HIST("collisions/selectionCuts"), 5); + if (std::abs(coll.posZ()) > vertexZCut) { + return; + } + registry.fill(HIST("collisions/selectionCuts"), 6); + if (coll.occupancyInTime() > occupancyCut) { + return; + } + registry.fill(HIST("collisions/selectionCuts"), 7); + if (tracks.size() > maxTracks) { + return; + } + registry.fill(HIST("collisions/selectionCuts"), 8); + // fill gapside + registry.fill(HIST("collisions/GapSide"), coll.gapSide(), 1.); + std::vector fitCut = {fv0Cut, ft0aCut, ft0cCut, fddaCut, fddcCut}; + int truegapSide = sgSelector.trueGap(coll, fitCut[0], fitCut[1], fitCut[2], zdcCut); + registry.fill(HIST("collisions/TrueGapSide"), truegapSide, 1.); + + std::vector parameters = {pvCut, dcaZCut, dcaXYCut, tpcChi2Cut, tpcNClsFindableCut, itsChi2Cut, etaCut, ptCut}; + + for (const auto& t : tracks) { + if (trackselector(t, parameters) != 0) { + if (truegapSide == o2::aod::sgselector::SingleGapA) { + if (t.sign() > 0) { + registry.fill(HIST("tracks/Deut_Pt_TPC_GapA"), t.pt(), t.tpcNSigmaDe()); + registry.fill(HIST("tracks/Deut_Pt_TOF_GapA"), t.pt(), t.tofNSigmaDe()); + if (std::abs(t.tpcNSigmaDe()) < nsigmatpcPreselCut) { + registry.fill(HIST("tracks/Deut_Pt_TOF_GapA_TPCpresel"), t.pt(), t.tofNSigmaDe()); + } + } else { + registry.fill(HIST("tracks/Antideut_Pt_TPC_GapA"), t.pt(), t.tpcNSigmaDe()); + registry.fill(HIST("tracks/Antideut_Pt_TOF_GapA"), t.pt(), t.tofNSigmaDe()); + if (std::abs(t.tpcNSigmaDe()) < nsigmatpcPreselCut) { + registry.fill(HIST("tracks/Antideut_Pt_TOF_GapA_TPCpresel"), t.pt(), t.tofNSigmaDe()); + } + } + } + + if (truegapSide == o2::aod::sgselector::SingleGapC) { + if (t.sign() > 0) { + registry.fill(HIST("tracks/Deut_Pt_TPC_GapC"), t.pt(), t.tpcNSigmaDe()); + registry.fill(HIST("tracks/Deut_Pt_TOF_GapC"), t.pt(), t.tofNSigmaDe()); + if (std::abs(t.tpcNSigmaDe()) < nsigmatpcPreselCut) { + registry.fill(HIST("tracks/Deut_Pt_TOF_GapC_TPCpresel"), t.pt(), t.tofNSigmaDe()); + } + } else { + registry.fill(HIST("tracks/Antideut_Pt_TPC_GapC"), t.pt(), t.tpcNSigmaDe()); + registry.fill(HIST("tracks/Antideut_Pt_TOF_GapC"), t.pt(), t.tofNSigmaDe()); + if (std::abs(t.tpcNSigmaDe()) < nsigmatpcPreselCut) { + registry.fill(HIST("tracks/Antideut_Pt_TOF_GapC_TPCpresel"), t.pt(), t.tofNSigmaDe()); + } + } + } + + if (truegapSide == o2::aod::sgselector::DoubleGap) { + if (t.sign() > 0) { + registry.fill(HIST("tracks/Deut_Pt_TPC_DoubleGap"), t.pt(), t.tpcNSigmaDe()); + registry.fill(HIST("tracks/Deut_Pt_TOF_DoubleGap"), t.pt(), t.tofNSigmaDe()); + if (std::abs(t.tpcNSigmaDe()) < nsigmatpcPreselCut) { + registry.fill(HIST("tracks/Deut_Pt_TOF_DoubleGap_TPCpresel"), t.pt(), t.tofNSigmaDe()); + } + } else { + registry.fill(HIST("tracks/Antideut_Pt_TPC_DoubleGap"), t.pt(), t.tpcNSigmaDe()); + registry.fill(HIST("tracks/Antideut_Pt_TOF_DoubleGap"), t.pt(), t.tofNSigmaDe()); + if (std::abs(t.tpcNSigmaDe()) < nsigmatpcPreselCut) { + registry.fill(HIST("tracks/Antideut_Pt_TOF_DoubleGap_TPCpresel"), t.pt(), t.tofNSigmaDe()); + } + } + } + } + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + }; +} diff --git a/PWGUD/Tasks/sgExclOmega.cxx b/PWGUD/Tasks/sgExclOmega.cxx index cb8c298ce0f..14da74f4411 100644 --- a/PWGUD/Tasks/sgExclOmega.cxx +++ b/PWGUD/Tasks/sgExclOmega.cxx @@ -120,7 +120,7 @@ struct SGExclOmega { int truegapSide = sgSelector.trueGap(collision, FIT_cut[0], FIT_cut[1], FIT_cut[2], ZDC_cut); registry.fill(HIST("GapSide"), gapSide); registry.fill(HIST("TrueGapSide"), truegapSide); - gapSide = truegapSide; + // gapSide = truegapSide; // if (gapSide!=2) return; int pvtracks = 0; int esign = 0; diff --git a/PWGUD/Tasks/sgFITAnalyzer.cxx b/PWGUD/Tasks/sgFITAnalyzer.cxx index de44924b0a2..cff17b961f6 100644 --- a/PWGUD/Tasks/sgFITAnalyzer.cxx +++ b/PWGUD/Tasks/sgFITAnalyzer.cxx @@ -37,6 +37,7 @@ #include #include +#include #include using namespace o2; @@ -777,36 +778,36 @@ struct SGFITAnalyzer { avPtPVa += track.pt(); vecEtaPVa.push_back(vtrk.Eta()); registry.get(HIST("tracks/etaApv"))->Fill(vtrk.Eta(), 1.); - if (!an) + if (!an) { registry.get(HIST("tracks/eta2Apv"))->Fill(vtrk.Eta(), 1.); - if (!an) z0pva++; - else if (an == 4) + } else if (an == 4) { z1pva++; + } } if (truegapSide == 1) { pvc++; avPtPVc += track.pt(); vecEtaPVc.push_back(vtrk.Eta()); registry.get(HIST("tracks/etaCpv"))->Fill(vtrk.Eta(), 1.); - if (!cn) + if (!cn) { registry.get(HIST("tracks/eta2Cpv"))->Fill(vtrk.Eta(), 1.); - if (!cn) z0pvc++; - else if (cn == 4) + } else if (cn == 4) { z1pvc++; + } } if (truegapSide == 2) { pvac++; avPtPVac += track.pt(); vecEtaPVac.push_back(vtrk.Eta()); registry.get(HIST("tracks/etaACpv"))->Fill(vtrk.Eta(), 1.); - if (!an && !cn) + if (!an && !cn) { registry.get(HIST("tracks/eta2ACpv"))->Fill(vtrk.Eta(), 1.); - if (!an && !cn) z0pvac++; - else if (an >= 3 && cn >= 3) + } else if (an >= 3 && cn >= 3) { z1pvac++; + } } registry.get(HIST("tracks/TPCSignalvspPVC"))->Fill(vtrk.Mag(), signalTPC, 1.); registry.get(HIST("tracks/TOFSignalvspPVC"))->Fill(vtrk.Mag(), signalTOF, 1.); diff --git a/PWGUD/Tasks/sgSpectraAnalyzer.cxx b/PWGUD/Tasks/sgSpectraAnalyzer.cxx index b17c699970f..bd041e7269b 100644 --- a/PWGUD/Tasks/sgSpectraAnalyzer.cxx +++ b/PWGUD/Tasks/sgSpectraAnalyzer.cxx @@ -33,6 +33,7 @@ #include #include +#include #include #include @@ -233,7 +234,7 @@ struct SGSpectraAnalyzer { int goodtracks = 0; int alltracks = 0; sum.SetXYZM(0, 0, 0, 0); - int gapSide = collision.gapSide(); + int gapSide{}; // = collision.gapSide(); // FIXME: unused float FIT_cut[5] = {FV0_cut, FT0A_cut, FT0C_cut, FDDA_cut, FDDC_cut}; std::vector parameters = {PV_cut, dcaZ_cut, dcaXY_cut, tpcChi2_cut, tpcNClsFindable_cut, itsChi2_cut, eta_cut, pt_cut}; int truegapSide = sgSelector.trueGap(collision, FIT_cut[0], FIT_cut[1], FIT_cut[2], ZDC_cut); diff --git a/PWGUD/Tasks/sginclusivePhiKstarSD.cxx b/PWGUD/Tasks/sginclusivePhiKstarSD.cxx index 29a6fa3a46e..41713b8ca40 100644 --- a/PWGUD/Tasks/sginclusivePhiKstarSD.cxx +++ b/PWGUD/Tasks/sginclusivePhiKstarSD.cxx @@ -46,9 +46,11 @@ #include +#include #include #include #include +#include #include #include #include @@ -155,6 +157,27 @@ struct SginclusivePhiKstarSD { ConfigurableAxis axispt{"axispt", {200, 0.0, 20.0}, ""}; ConfigurableAxis axisrapdity{"axisrapdity", {40, -2.0, 2.0}, ""}; + std::vector runNumbers = { + 544013, 544028, 544032, 544091, 544095, 544098, 544116, 544121, 544122, 544123, + 544124, 544184, 544185, 544389, 544390, 544391, 544392, 544451, 544454, 544474, + 544475, 544476, 544477, 544490, 544491, 544492, 544508, 544510, 544511, 544512, + 544514, 544515, 544518, 544548, 544549, 544550, 544551, 544564, 544565, 544567, + 544568, 544580, 544582, 544583, 544585, 544614, 544640, 544652, 544653, 544672, + 544674, 544692, 544693, 544694, 544696, 544739, 544742, 544754, 544767, 544794, + 544795, 544797, 544813, 544868, 544886, 544887, 544896, 544913, 544914, 544917, + 544931, 544947, 544961, 544963, 544964, 544968, 544992, 545009, 545044, 545047, + 545063, 545064, 545066, 545185, 545210, 545223, 545249, 545291, 545294, 545295, + 545296, 545312}; + + int getRunBin(int runNumber) + { + auto it = std::lower_bound(runNumbers.begin(), runNumbers.end(), runNumber); + if (it == runNumbers.end() || *it != runNumber) { + return -1; + } + return static_cast(std::distance(runNumbers.begin(), it)) + 1; + } + int numTwoTracks = 2; int numFourTracks = 4; @@ -307,6 +330,13 @@ struct SginclusivePhiKstarSD { rQA.add("V0A_2", "V0A amplitude", kTH1F, {{1000, 0.0, 1000.0}}); rQA.add("V0A_0", "V0A amplitude", kTH1F, {{1000, 0.0, 1000.0}}); rQA.add("V0A_1", "V0A amplitude", kTH1F, {{1000, 0.0, 1000.0}}); + + rQA.add("hIntRateVsRun", "Hadronic interaction rate; Run number; Hadronic interaction rate (kHz)", kTH2F, {{92, 0.5, 92.5}, {1000, 0., 50.}}); + auto hIntRateVsRun = rQA.get(HIST("hIntRateVsRun")); + for (size_t i = 0; i < runNumbers.size(); ++i) { + hIntRateVsRun->GetXaxis()->SetBinLabel(i + 1, Form("%d", runNumbers[i])); + } + hIntRateVsRun->GetXaxis()->LabelsOption("v"); } registry.add("gap_mult0", "Mult 0", kTH1F, {{100, 0.0, 100.0}}); registry.add("gap_mult1", "Mult 1", kTH1F, {{100, 0.0, 100.0}}); @@ -785,6 +815,11 @@ struct SginclusivePhiKstarSD { { if (qa) { rQA.fill(HIST("hOcc_before"), collision.occupancyInTime()); + + int runBin = getRunBin(collision.runNumber()); + if (runBin > 0 && collision.hadronicRate() > 0.) { + rQA.fill(HIST("hIntRateVsRun"), runBin, collision.hadronicRate()); + } } ROOT::Math::PxPyPzMVector v0; @@ -1191,7 +1226,7 @@ struct SginclusivePhiKstarSD { std::vector parameters = {pvCut, dcazCut, dcaxyCut, tpcChi2Cut, tpcNClsFindableCut, itsChi2Cut, etaCut, ptCut}; - BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass}, true}; + BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass}}; for (auto const& [collision1, collision2] : o2::soa::selfCombinations(binningOnPositions, cfgNoMixedEvents, -1, collisions, collisions)) { @@ -2334,7 +2369,7 @@ struct SginclusivePhiKstarSD { std::vector parameters = {pvCut, dcazCut, dcaxyCut, tpcChi2Cut, tpcNClsFindableCut, itsChi2Cut, etaCut, ptCut}; - BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass}, true}; + BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass}}; for (auto const& [collision1, collision2] : o2::soa::selfCombinations(binningOnPositions, cfgNoMixedEvents, -1, collisions, collisions)) { diff --git a/PWGUD/Tasks/upcCandidateAnalyzer.cxx b/PWGUD/Tasks/upcCandidateAnalyzer.cxx index dcf23797803..50a80cc11dd 100644 --- a/PWGUD/Tasks/upcCandidateAnalyzer.cxx +++ b/PWGUD/Tasks/upcCandidateAnalyzer.cxx @@ -608,7 +608,7 @@ struct UpcCandAnalyzer { const auto& cand = eventCandidates.iteratorAt(candID); const auto& tr1 = fwdTracks.iteratorAt(trId1); const auto& tr2 = fwdTracks.iteratorAt(trId2); - processCandidate<0>(cand, tr1, tr2, &mcParticles, (o2::aod::UDMcTrackLabels*)nullptr, &mcFwdTrackLabels); + processCandidate<0>(cand, tr1, tr2, &mcParticles, nullptr, &mcFwdTrackLabels); } } @@ -629,7 +629,7 @@ struct UpcCandAnalyzer { const auto& cand = eventCandidates.iteratorAt(candID); const auto& tr1 = fwdTracks.iteratorAt(trId1); const auto& tr2 = fwdTracks.iteratorAt(trId2); - processCandidate<0>(cand, tr1, tr2, (o2::aod::UDMcParticles*)nullptr, (o2::aod::UDMcTrackLabels*)nullptr, (o2::aod::UDMcFwdTrackLabels*)nullptr); + processCandidate<0>(cand, tr1, tr2, nullptr, nullptr, nullptr); } } @@ -687,7 +687,7 @@ struct UpcCandAnalyzer { const auto& cand = eventCandidates.iteratorAt(candID); const auto& tr1 = fwdTracks.iteratorAt(trId1); const auto& tr2 = barTracks.iteratorAt(trId2); - processCandidate<1>(cand, tr1, tr2, (o2::aod::UDMcParticles*)nullptr, (o2::aod::UDMcTrackLabels*)nullptr, (o2::aod::UDMcFwdTrackLabels*)nullptr); + processCandidate<1>(cand, tr1, tr2, nullptr, nullptr, nullptr); } } @@ -713,7 +713,7 @@ struct UpcCandAnalyzer { const auto& cand = eventCandidates.iteratorAt(candID); const auto& tr1 = barTracks.iteratorAt(trId1); const auto& tr2 = barTracks.iteratorAt(trId2); - processCandidate<2>(cand, tr1, tr2, &mcParticles, &mcTrackLabels, (o2::aod::UDMcFwdTrackLabels*)nullptr); + processCandidate<2>(cand, tr1, tr2, &mcParticles, &mcTrackLabels, nullptr); } } @@ -734,7 +734,7 @@ struct UpcCandAnalyzer { const auto& cand = eventCandidates.iteratorAt(candID); const auto& tr1 = barTracks.iteratorAt(trId1); const auto& tr2 = barTracks.iteratorAt(trId2); - processCandidate<2>(cand, tr1, tr2, (o2::aod::UDMcParticles*)nullptr, (o2::aod::UDMcTrackLabels*)nullptr, (o2::aod::UDMcFwdTrackLabels*)nullptr); + processCandidate<2>(cand, tr1, tr2, nullptr, nullptr, nullptr); } } diff --git a/PWGUD/Tasks/upcMft.cxx b/PWGUD/Tasks/upcMft.cxx index 6349589e6b4..18bb62bb7ea 100644 --- a/PWGUD/Tasks/upcMft.cxx +++ b/PWGUD/Tasks/upcMft.cxx @@ -127,7 +127,7 @@ struct UpcMftRec { o2::base::Propagator::initFieldFromGRP(grpmag); // for some reason this is necessary for the next next line runNumber = bc.runNumber(); - o2::field::MagneticField* field = static_cast(TGeoGlobalMagField::Instance()->GetField()); + o2::field::MagneticField* field = dynamic_cast(TGeoGlobalMagField::Instance()->GetField()); Bz = field->getBz(centerMFT); // gives error if the propagator is not initFielded LOG(info) << "The field at the center of the MFT is Bz = " << Bz; diff --git a/PWGUD/Tasks/upcPhotonuclearAnalysisJMG.cxx b/PWGUD/Tasks/upcPhotonuclearAnalysisJMG.cxx index bc6408d3b39..36b8d605947 100644 --- a/PWGUD/Tasks/upcPhotonuclearAnalysisJMG.cxx +++ b/PWGUD/Tasks/upcPhotonuclearAnalysisJMG.cxx @@ -395,7 +395,7 @@ struct UpcPhotonuclearAnalysisJMG { } std::vector vtxBinsEdges{VARIABLE_WIDTH, -10.0f, -7.0f, -5.0f, -2.5f, 0.0f, 2.5f, 5.0f, 7.0f, 10.0f}; - std::vector gapSideBinsEdges{VARIABLE_WIDTH, -0.5, 0.5, 1.5}; + // std::vector gapSideBinsEdges{VARIABLE_WIDTH, -0.5, 0.5, 1.5}; enum EventType { SameEvent = 1, @@ -409,9 +409,9 @@ struct UpcPhotonuclearAnalysisJMG { // Binning only on PosZ without multiplicity // using BinningType = ColumnBinningPolicy; - // BinningType bindingOnVtx{{vtxBinsEdges, gapSideBinsEdges}, true}; + // BinningType bindingOnVtx{{vtxBinsEdges, gapSideBinsEdges}}; using BinningType = ColumnBinningPolicy; - // BinningType bindingOnVtx{{vtxBinsEdges}, true}; + // BinningType bindingOnVtx{{vtxBinsEdges}}; // SameKindPair pairs{bindingOnVtx, nEventsMixed, -1, &cache}; template @@ -447,7 +447,7 @@ struct UpcPhotonuclearAnalysisJMG { if (useEnergyZN && !((collision.energyCommonZNA() < cutGapAMyEnergyZNA) && (collision.energyCommonZNC() >= cutGapAMyEnergyZNC))) { return false; } // 0n - A side && Xn - C Side - if (useGapSideVariable && collision.gapSide() != uint8_t(0)) { + if (useGapSideVariable && collision.gapSide() != static_cast(0)) { return false; } if (useFV0 && collision.totalFV0AmplitudeA() > cutGapAFV0Amplitude) { @@ -464,7 +464,7 @@ struct UpcPhotonuclearAnalysisJMG { if (useEnergyZN && !((collision.energyCommonZNA() >= cutGapCMyEnergyZNA) && (collision.energyCommonZNC() < cutGapCMyEnergyZNC))) { return false; } // Xn - A side && 0n - C Side - if (useGapSideVariable && collision.gapSide() != uint8_t(1)) { + if (useGapSideVariable && collision.gapSide() != static_cast(1)) { return false; } if (useFT0CGapC && (collision.totalFT0AmplitudeC() > cutGapCMyAmplitudeFT0C)) { @@ -706,7 +706,7 @@ struct UpcPhotonuclearAnalysisJMG { nTracksCharged++; sumPt += track.pt(); float phiVal = RecoDecay::constrainAngle(phi(track.px(), track.py()), 0.f); - float pTotal = momentum(track.px(), track.py(), track.pz()) * track.sign(); + float pTotal = momentum(track.px(), track.py(), track.pz()); float etaVal = eta(track.px(), track.py(), track.pz()); histos.fill(HIST("Tracks/SGsideA/hTrackPt"), track.pt()); histos.fill(HIST("Tracks/SGsideA/hTrackPhi"), phiVal); @@ -755,7 +755,6 @@ struct UpcPhotonuclearAnalysisJMG { histos.fill(HIST("Events/SGsideA/hTrackPV"), nchPVGapSideA, nchGapSideA); nTracksChargedSideA = nTracksCharged; multiplicitySideA = reconstructedTracks.size(); - nTracksCharged = sumPt = 0; break; case 1: // gap for side C if (isCollisionCutSG(reconstructedCollision, 1) == false) { @@ -782,7 +781,7 @@ struct UpcPhotonuclearAnalysisJMG { nTracksCharged++; sumPt += track.pt(); float phiVal = RecoDecay::constrainAngle(phi(track.px(), track.py()), 0.f); - float pTotal = momentum(track.px(), track.py(), track.pz()) * track.sign(); + float pTotal = momentum(track.px(), track.py(), track.pz()); float etaVal = eta(track.px(), track.py(), track.pz()); histos.fill(HIST("Tracks/SGsideC/hTrackPt"), track.pt()); histos.fill(HIST("Tracks/SGsideC/hTrackPhi"), phiVal); @@ -831,7 +830,6 @@ struct UpcPhotonuclearAnalysisJMG { histos.fill(HIST("Events/SGsideC/hTrackPV"), nchPVGapSideC, nchGapSideC); nTracksChargedSideC = nTracksCharged; multiplicitySideC = reconstructedTracks.size(); - nTracksCharged = sumPt = 0; break; default: return; @@ -871,16 +869,16 @@ struct UpcPhotonuclearAnalysisJMG { vTrackChargeSideC, vTrackEtaSideC, vTrackPhiSideC, - vTrackTPCSignalSideA, - vTrackTOFSignalSideA, - vTrackTPCNSigmaPiSideA, - vTrackTOFNSigmaPiSideA, - vTrackTPCNSigmaKaSideA, - vTrackTOFNSigmaKaSideA, - vTrackTPCNSigmaProSideA, - vTrackTOFNSigmaProSideA, - vTrackTOFBetaSideA, - vTrackTOFBetaErrorSideA, + vTrackTPCSignalSideC, + vTrackTOFSignalSideC, + vTrackTPCNSigmaPiSideC, + vTrackTOFNSigmaPiSideC, + vTrackTPCNSigmaKaSideC, + vTrackTOFNSigmaKaSideC, + vTrackTPCNSigmaProSideC, + vTrackTOFNSigmaProSideC, + vTrackTOFBetaSideC, + vTrackTOFBetaErrorSideC, nTracksChargedSideA, multiplicitySideA, nTracksChargedSideC, @@ -896,8 +894,8 @@ struct UpcPhotonuclearAnalysisJMG { // int sgSide = reconstructedCollision.gapSide(); // int sgSide = 0; - // BinningType bindingOnVtx{{vtxBinsEdges, gapSideBinsEdges}, true}; - BinningType bindingOnVtx{{vtxBinsEdges}, true}; + // BinningType bindingOnVtx{{vtxBinsEdges, gapSideBinsEdges}}; + BinningType bindingOnVtx{{vtxBinsEdges}}; auto tracksTuple = std::make_tuple(reconstructedTracks); SameKindPair pairs{bindingOnVtx, nEventsMixed, -1, reconstructedCollision, tracksTuple, &cache}; @@ -924,9 +922,10 @@ struct UpcPhotonuclearAnalysisJMG { ++multiplicity; } // multiplicity = tracks1.size(); - if (fillCollisionUD(mixed, multiplicity) == false) { - return; - } + // if (fillCollisionUD(mixed, multiplicity) == false) { + // return; + // } + fillCollisionUD(mixed, multiplicity); histos.fill(HIST("Events/hCountCollisionsMixed"), 2); // histos.fill(HIST("eventcount"), bindingOnVtx.getBin({collision1.posZ()})); // histos.fill(HIST("eventcount"), bindingOnVtx.getBin({collision1.posZ(), collision1.gapSide()})); @@ -1008,8 +1007,8 @@ struct UpcPhotonuclearAnalysisJMG { return; } histos.fill(HIST("Events/hCollisionsFlow"), 11); - bool cutGapSideAVariable = useGapSideVariable && reconstructedCollision.gapSide() != uint8_t(0); - bool cutGapSideCVariable = useGapSideVariable && reconstructedCollision.gapSide() != uint8_t(1); + bool cutGapSideAVariable = useGapSideVariable && reconstructedCollision.gapSide() != static_cast(0); + bool cutGapSideCVariable = useGapSideVariable && reconstructedCollision.gapSide() != static_cast(1); if (cutGapSideAVariable || cutGapSideCVariable) { return; } @@ -1122,9 +1121,10 @@ struct UpcPhotonuclearAnalysisJMG { ++multiplicity; } // multiplicity = reconstructedTracks.size(); - if (fillCollisionUD(same, multiplicity) == false) { - return; - } + // if (fillCollisionUD(same, multiplicity) == false) { + // return; + // } + fillCollisionUD(same, multiplicity); // LOGF(debug, "Filling same events"); histos.fill(HIST("eventcount"), -2); if (minMultiplicity <= multiplicity && multiplicity <= range1Max) { diff --git a/PWGUD/Tasks/upcPileUp.cxx b/PWGUD/Tasks/upcPileUp.cxx new file mode 100644 index 00000000000..2fca0ac06a8 --- /dev/null +++ b/PWGUD/Tasks/upcPileUp.cxx @@ -0,0 +1,533 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file upcPileUp.cxx +/// \brief Pile-up probability in FT0 for UPC analyses, per colliding bunch and time frame +/// +/// For each colliding bunch crossing it counts +/// - denominator A: crossings with no activity on the C side (FT0C, FDDC, ZNC) +/// - numerator A : of those, the ones with a signal in FT0A above threshold +/// and the mirror for the C side (A side empty: FT0A, FV0A, FDDA, ZNA). +/// The ratio numerator / denominator is the pile-up probability. +/// +/// The BCs table only stores crossings with some activity. Completely empty +/// crossings are not in it, but they belong in the denominator. The task +/// therefore computes the number of analysed crossings of every colliding +/// bunch in every time frame, applying the same time-frame and ITS ROF border +/// cuts as the event selection, and the denominator is that number minus the +/// stored crossings with activity on the other side. +/// +/// \author David Arevalos + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/EventSelection.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +using BCsTSsSels = soa::Join; + +struct UpcPileUp { + + // services + Service ccdb{}; + + // histograms, booked per run and kept in the maps by name + HistogramRegistry bcTH1Registry{"bcTH1Registry", {}}; + std::map> bcTH1Pointers; + HistogramRegistry bcTH2Registry{"bcTH2Registry", {}}; + std::map> bcTH2Pointers; + + // histograms of the current run, cached so that the loops do no name lookup + std::shared_ptr hBcSel, hTF, hBorderCheck; + std::shared_ptr hCross, hVtx, hVtxCe, hDenA, hDenC; + std::vector> hNumA, hNumC, hNumAZn, hNumCZn; + + // filling scheme + std::bitset bcPatternB; + std::vector bcbIdx; // index of each colliding bunch, -1 otherwise + std::vector bcbList; // bcID within the orbit of each colliding bunch + int nbcB = 0; + + // ITS ROF info + int rofLength = -1; // number of bcs per ROF + int rofShift = -1; // bc shift of the ITS ROF + + // border margins, as in the event selection + int tfStartMargin = 0; + int tfEndMargin = 0; + int rofStartMargin = 0; + int rofEndMargin = 0; + + // run info + int runNumberBc = 0; // run number used to process BCs + int64_t tsMid = 0; // middle of the run, to access run-wise CCDB entries + int64_t orbitsPerTF = 0; + int64_t bcSOR = 0; // first bc of the first orbit + int64_t nBCsPerTF = 0; // duration of TF in bcs + int64_t nTF = 0; // number of time frames in run + std::set seenTFs; // time frames already counted in this run + + // thresholds used, filled in init from the configurable + std::vector thresholds; + int nThresholds = 0; + + // analysed crossings of each colliding bunch in the current time frame + std::vector nCrossTF; + + // histogram binning constants + static constexpr int NBinsBcSel = 4; + static constexpr int NBinsBorderCheck = 2; + + // FDD channel pairing, as in upcVmRof + static constexpr int NFddPairs = 4; + static constexpr int FddPairOffset = 4; + + // configurables + Configurable tfPerBin{"tfPerBin", 10000, "timeframes per bin, 1e4 means some 28 s"}; + Configurable ft0VtxIdx{"ft0VtxIdx", 2, "index of the FT0VTX input in the CTP input mask"}; + Configurable ft0CeIdx{"ft0CeIdx", 4, "index of the FT0CE input in the CTP input mask"}; + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + + // signal and veto definition + Configurable> ampThresholds{"ampThresholds", {100.0f, 150.0f, 200.0f}, "amplitude thresholds defining a signal in FT0"}; + Configurable maxAbsTimeFT0{"maxAbsTimeFT0", 4.0, "max |time| in a FT0 side (ns)"}; + Configurable maxAbsTimeFV0{"maxAbsTimeFV0", 4.0, "max |time| in FV0A (ns)"}; + Configurable maxAbsTimeFDD{"maxAbsTimeFDD", 4.0, "max |time| in a FDD side (ns)"}; + Configurable maxAbsTimeZN{"maxAbsTimeZN", 2.0, "max |time| in a ZN (ns)"}; + Configurable fillZnVariation{"fillZnVariation", true, "also fill the numerators asking in addition for a signal in ZNA or ZNC"}; + + // border margins, must match the ones used by the event selection; -1 takes them from CCDB + Configurable confTFStartBorderMargin{"confTFStartBorderMargin", -1, "bcs cut at the start of the TF, -1 from CCDB"}; + Configurable confTFEndBorderMargin{"confTFEndBorderMargin", -1, "bcs cut at the end of the TF, -1 from CCDB"}; + Configurable confROFStartBorderMargin{"confROFStartBorderMargin", -1, "bcs cut at the start of the ITS ROF, -1 from CCDB"}; + Configurable confROFEndBorderMargin{"confROFEndBorderMargin", -1, "bcs cut at the end of the ITS ROF, -1 from CCDB"}; + + //-------------------------------------------------------------------------------- + // general run information + void getRunInfo(int run) + { + auto runInfo = o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(ccdb->instance(), run); + tsMid = runInfo.sor / 2 + runInfo.eor / 2; + orbitsPerTF = runInfo.orbitsPerTF; + bcSOR = runInfo.orbitSOR * o2::constants::lhc::LHCMaxBunches; + nBCsPerTF = orbitsPerTF * o2::constants::lhc::LHCMaxBunches; + nTF = (runInfo.orbitEOR - runInfo.orbitSOR + orbitsPerTF - 1) / orbitsPerTF; // rounded up + } // end getRunInfo() + + //-------------------------------------------------------------------------------- + // filling scheme + void getFillingScheme() + { + auto grplhcif = ccdb->getForTimeStamp("GLO/Config/GRPLHCIF", tsMid); + auto beamPatternA = grplhcif->getBunchFilling().getBeamPattern(0); + auto beamPatternC = grplhcif->getBunchFilling().getBeamPattern(1); + bcPatternB = beamPatternA & beamPatternC; + bcbIdx.assign(o2::constants::lhc::LHCMaxBunches, -1); + bcbList.clear(); + nbcB = 0; + for (int i = 0; i < o2::constants::lhc::LHCMaxBunches; i++) { + if (bcPatternB.test(i)) { + bcbIdx[i] = nbcB; + bcbList.push_back(i); + nbcB++; + } + } + nCrossTF.assign(nbcB, 0.); + } // end getFillingScheme() + + //-------------------------------------------------------------------------------- + // ITS ROF info and border margins, taken as in the event selection + void getBorderInfo() + { + auto alppar = ccdb->getForTimeStamp>("ITS/Config/AlpideParam", tsMid); + rofShift = alppar->roFrameBiasInBC; + rofLength = alppar->roFrameLengthInBC; + + auto par = ccdb->getForTimeStamp("EventSelection/EventSelectionParams", tsMid); + tfStartMargin = confTFStartBorderMargin < 0 ? par->fTimeFrameStartBorderMargin : confTFStartBorderMargin.value; + tfEndMargin = confTFEndBorderMargin < 0 ? par->fTimeFrameEndBorderMargin : confTFEndBorderMargin.value; + rofStartMargin = confROFStartBorderMargin < 0 ? par->fITSROFrameStartBorderMargin : confROFStartBorderMargin.value; + rofEndMargin = confROFEndBorderMargin < 0 ? par->fITSROFrameEndBorderMargin : confROFEndBorderMargin.value; + LOGP(info, "border margins: TF {} {}, ITS ROF {} {}", tfStartMargin, tfEndMargin, rofStartMargin, rofEndMargin); + } // end getBorderInfo() + + //-------------------------------------------------------------------------------- + int64_t getBcWithinOrbit(int64_t globalBC) + { + return (globalBC % o2::constants::lhc::LHCMaxBunches); + } + + //-------------------------------------------------------------------------------- + int64_t getTimeFrame(int64_t globalBC) + { + return (globalBC - bcSOR) / nBCsPerTF; + } + + //-------------------------------------------------------------------------------- + // same conditions as kNoTimeFrameBorder and kNoITSROFrameBorder in the event selection + bool passesBorders(int64_t globalBC) + { + int64_t bcInTF = (globalBC - bcSOR) % nBCsPerTF; + if (!(bcInTF > tfStartMargin && bcInTF < nBCsPerTF - tfEndMargin)) { + return false; + } + int64_t bcInRof = (globalBC + o2::constants::lhc::LHCMaxBunches - rofShift) % rofLength; + return (bcInRof > rofStartMargin && bcInRof < rofLength - rofEndMargin); + } + + //-------------------------------------------------------------------------------- + // selection flags of a stored bc + bool checkBcFlags(const auto& bc) + { + hBcSel->Fill(0); + if (!bc.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + hBcSel->Fill(1); + if (!bc.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + hBcSel->Fill(2); + return true; + } // end checkBcFlags() + + //-------------------------------------------------------------------------------- + // analysed crossings of every colliding bunch in a time frame: all of them, + // stored or not, that pass the same border cuts as the stored ones. + // They go into the crossings histogram and, as a start, into both + // denominators; stored crossings with activity on the other side are removed + // later, one by one. + void fillCrossings(int64_t tf) + { + const int64_t tfStartBC = bcSOR + tf * nBCsPerTF; + std::fill(nCrossTF.begin(), nCrossTF.end(), 0.); + for (int64_t orbit = 0; orbit < orbitsPerTF; orbit++) { + for (const auto& bcInOrbit : bcbList) { + int64_t globalBC = tfStartBC + orbit * o2::constants::lhc::LHCMaxBunches + bcInOrbit; + if (passesBorders(globalBC)) { + nCrossTF[bcbIdx[bcInOrbit]] += 1.; + } + } + } + for (int ib = 0; ib < nbcB; ib++) { + if (nCrossTF[ib] > 0.) { + hCross->Fill(tf, ib, nCrossTF[ib]); + hDenA->Fill(tf, ib, nCrossTF[ib]); + hDenC->Fill(tf, ib, nCrossTF[ib]); + } + } + } // end fillCrossings() + + //-------------------------------------------------------------------------------- + // FDD amplitude of a side, using the channel pairing of upcVmRof + // channelPairs = {{0, 4}, {1, 5}, {2, 6}, {3, 7}} + float sumFddAmplitude(const auto& charges) + { + float amp = 0; + for (int i = 0; i < NFddPairs; i++) { + if (charges[i] > 0 && charges[i + FddPairOffset] > 0) { + amp += 0.5 * (charges[i] + charges[i + FddPairOffset]); + } + } + return amp; + } // end sumFddAmplitude() + + //-------------------------------------------------------------------------------- + // book the histograms of a run + void addBcHistos(int run) + { + if (bcTH1Pointers[Form("bc/%d/bcSel_H", run)]) { + return; + } + + const int nBunches = o2::constants::lhc::LHCMaxBunches; + const double maxBunch = static_cast(nBunches) - 0.5; + const int nBinsTF = static_cast(nTF / tfPerBin) + 1; + const double maxTF = static_cast(nBinsTF * tfPerBin - 1) - 0.5; + const AxisSpec axisTF{nBinsTF, -0.5, maxTF, "TF"}; + const AxisSpec axisBcb{nbcB, -0.5, nbcB - 0.5, "bc-B idx"}; + + // filling scheme and bookkeeping + bcTH1Pointers[Form("bc/%d/bcPatternB_H", run)] = bcTH1Registry.add(Form("bc/%d/bcPatternB_H", run), "colliding bunches; bcID;", + {HistType::kTH1D, {{nBunches, -0.5, maxBunch}}}); + bcTH1Pointers[Form("bc/%d/bcSel_H", run)] = bcTH1Registry.add(Form("bc/%d/bcSel_H", run), "bc selection: all, no TF border, no ROF border, colliding; ; Counter", + {HistType::kTH1D, {{NBinsBcSel, -0.5, NBinsBcSel - 0.5}}}); + bcTH1Pointers[Form("bc/%d/tf_H", run)] = bcTH1Registry.add(Form("bc/%d/tf_H", run), "analysed time frames; TF; Counts", + {HistType::kTH1D, {axisTF}}); + // stored bcs where the border cut computed here agrees / disagrees with the event selection + bcTH1Pointers[Form("bc/%d/borderCheck_H", run)] = bcTH1Registry.add(Form("bc/%d/borderCheck_H", run), "border cut vs event selection: agree, disagree; ; Counter", + {HistType::kTH1D, {{NBinsBorderCheck, -0.5, NBinsBorderCheck - 0.5}}}); + + // thresholds, stored as bin labels so that they survive merging + bcTH1Pointers[Form("bc/%d/thresholds_H", run)] = bcTH1Registry.add(Form("bc/%d/thresholds_H", run), "amplitude thresholds; threshold index; ", + {HistType::kTH1D, {{nThresholds, -0.5, nThresholds - 0.5}}}); + for (int i = 0; i < nThresholds; i++) { + bcTH1Pointers[Form("bc/%d/thresholds_H", run)]->GetXaxis()->SetBinLabel(i + 1, Form("%g", thresholds[i])); + bcTH1Pointers[Form("bc/%d/thresholds_H", run)]->SetBinContent(i + 1, 1); + } + for (const auto& bcInOrbit : bcbList) { + bcTH1Pointers[Form("bc/%d/bcPatternB_H", run)]->Fill(bcInOrbit); + } + + // analysed crossings, stored or not + bcTH2Pointers[Form("bc/%d/nCross_bcb_H", run)] = bcTH2Registry.add(Form("bc/%d/nCross_bcb_H", run), "analysed crossings; TF; bc-B idx; Counter", + {HistType::kTH2D, {axisTF, axisBcb}}); + + // triggers from the CTP input mask, to compute mu + bcTH2Pointers[Form("bc/%d/ft0Vtx_bcb_H", run)] = bcTH2Registry.add(Form("bc/%d/ft0Vtx_bcb_H", run), "ft0Vtx triggers; TF; bc-B idx; Counter", + {HistType::kTH2D, {axisTF, axisBcb}}); + bcTH2Pointers[Form("bc/%d/ft0VtxCe_bcb_H", run)] = bcTH2Registry.add(Form("bc/%d/ft0VtxCe_bcb_H", run), "ft0VtxCe triggers; TF; bc-B idx; Counter", + {HistType::kTH2D, {axisTF, axisBcb}}); + + // pile-up probability. The denominators are filled with weights, so their + // bin errors are not meaningful: compute the uncertainty of the ratio + // binomially from the contents. + bcTH2Pointers[Form("bc/%d/puDenA_bcb_H", run)] = bcTH2Registry.add(Form("bc/%d/puDenA_bcb_H", run), "crossings empty on the C side; TF; bc-B idx; Counter", + {HistType::kTH2D, {axisTF, axisBcb}}); + bcTH2Pointers[Form("bc/%d/puDenC_bcb_H", run)] = bcTH2Registry.add(Form("bc/%d/puDenC_bcb_H", run), "crossings empty on the A side; TF; bc-B idx; Counter", + {HistType::kTH2D, {axisTF, axisBcb}}); + for (int i = 0; i < nThresholds; i++) { + bcTH2Pointers[Form("bc/%d/puNumA_t%d_bcb_H", run, i)] = bcTH2Registry.add(Form("bc/%d/puNumA_t%d_bcb_H", run, i), Form("FT0A amplitude > %g, C side empty; TF; bc-B idx; Counter", thresholds[i]), + {HistType::kTH2D, {axisTF, axisBcb}}); + bcTH2Pointers[Form("bc/%d/puNumC_t%d_bcb_H", run, i)] = bcTH2Registry.add(Form("bc/%d/puNumC_t%d_bcb_H", run, i), Form("FT0C amplitude > %g, A side empty; TF; bc-B idx; Counter", thresholds[i]), + {HistType::kTH2D, {axisTF, axisBcb}}); + // variation asking in addition for a signal in ZNA or ZNC. Since the ZN of + // the other side is part of the empty condition, this is in practice the + // ZN of the same side. + if (fillZnVariation) { + bcTH2Pointers[Form("bc/%d/puNumAZn_t%d_bcb_H", run, i)] = bcTH2Registry.add(Form("bc/%d/puNumAZn_t%d_bcb_H", run, i), Form("FT0A amplitude > %g and ZN, C side empty; TF; bc-B idx; Counter", thresholds[i]), + {HistType::kTH2D, {axisTF, axisBcb}}); + bcTH2Pointers[Form("bc/%d/puNumCZn_t%d_bcb_H", run, i)] = bcTH2Registry.add(Form("bc/%d/puNumCZn_t%d_bcb_H", run, i), Form("FT0C amplitude > %g and ZN, A side empty; TF; bc-B idx; Counter", thresholds[i]), + {HistType::kTH2D, {axisTF, axisBcb}}); + } + } + } // end addBcHistos() + + //-------------------------------------------------------------------------------- + // cache the histograms of a run + void setHistoPointers(int run) + { + hBcSel = bcTH1Pointers[Form("bc/%d/bcSel_H", run)]; + hTF = bcTH1Pointers[Form("bc/%d/tf_H", run)]; + hBorderCheck = bcTH1Pointers[Form("bc/%d/borderCheck_H", run)]; + hCross = bcTH2Pointers[Form("bc/%d/nCross_bcb_H", run)]; + hVtx = bcTH2Pointers[Form("bc/%d/ft0Vtx_bcb_H", run)]; + hVtxCe = bcTH2Pointers[Form("bc/%d/ft0VtxCe_bcb_H", run)]; + hDenA = bcTH2Pointers[Form("bc/%d/puDenA_bcb_H", run)]; + hDenC = bcTH2Pointers[Form("bc/%d/puDenC_bcb_H", run)]; + hNumA.assign(nThresholds, nullptr); + hNumC.assign(nThresholds, nullptr); + hNumAZn.assign(nThresholds, nullptr); + hNumCZn.assign(nThresholds, nullptr); + for (int i = 0; i < nThresholds; i++) { + hNumA[i] = bcTH2Pointers[Form("bc/%d/puNumA_t%d_bcb_H", run, i)]; + hNumC[i] = bcTH2Pointers[Form("bc/%d/puNumC_t%d_bcb_H", run, i)]; + if (fillZnVariation) { + hNumAZn[i] = bcTH2Pointers[Form("bc/%d/puNumAZn_t%d_bcb_H", run, i)]; + hNumCZn[i] = bcTH2Pointers[Form("bc/%d/puNumCZn_t%d_bcb_H", run, i)]; + } + } + } // end setHistoPointers() + + //-------------------------------------------------------------------------------- + void init(InitContext&) + { + ccdb->setURL(ccdbUrl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + + thresholds = static_cast>(ampThresholds); + nThresholds = static_cast(thresholds.size()); + } // end init() + + //-------------------------------------------------------------------------------- + void processBCs(BCsTSsSels const& bcs, + aod::FV0As const&, aod::FT0s const&, aod::FDDs const&, + aod::Zdcs const&) + { + if (bcs.size() == 0) { + return; + } + + // new run + const auto& bcAt0 = bcs.iteratorAt(0); + if (runNumberBc != bcAt0.runNumber()) { + runNumberBc = bcAt0.runNumber(); + getRunInfo(runNumberBc); + getFillingScheme(); + getBorderInfo(); + addBcHistos(runNumberBc); + setHistoPointers(runNumberBc); + seenTFs.clear(); + } + + for (const auto& bc : bcs) { + int64_t thisBC = getBcWithinOrbit(bc.globalBC()); + int64_t thisTF = getTimeFrame(bc.globalBC()); + + // first time this time frame is seen: count it and add all its analysed + // crossings. The set protects against a time frame split over two + // dataframes being counted twice. + if (seenTFs.insert(thisTF).second) { + hTF->Fill(thisTF); + fillCrossings(thisTF); + } + + // check that the border cut computed here agrees with the event selection + bool passesFlags = bc.selection_bit(aod::evsel::kNoTimeFrameBorder) && bc.selection_bit(aod::evsel::kNoITSROFrameBorder); + hBorderCheck->Fill(passesFlags == passesBorders(bc.globalBC()) ? 0 : 1); + + if (!checkBcFlags(bc)) { + continue; + } + if (!bcPatternB.test(thisBC)) { + continue; + } + hBcSel->Fill(3); + int thisBcb = bcbIdx[thisBC]; + + // triggers from the CTP input mask + std::bitset<64> mask = bc.inputMask(); + if (mask[ft0VtxIdx]) { + hVtx->Fill(thisTF, thisBcb); + if (mask[ft0CeIdx]) { + hVtxCe->Fill(thisTF, thisBcb); + } + } + + //------------------------------------------------------------------------------ + // detector information; amplitudes are only meaningful when in time + + // FT0 + bool ft0aInTime = false; + bool ft0cInTime = false; + float ampFT0A = 0; + float ampFT0C = 0; + if (bc.has_foundFT0()) { + if (bc.foundFT0().isValidTimeA() && std::abs(bc.foundFT0().timeA()) < maxAbsTimeFT0) { + ft0aInTime = true; + ampFT0A = bc.foundFT0().sumAmpA(); + } + if (bc.foundFT0().isValidTimeC() && std::abs(bc.foundFT0().timeC()) < maxAbsTimeFT0) { + ft0cInTime = true; + ampFT0C = bc.foundFT0().sumAmpC(); + } + } + + // FV0A + bool fv0aSignal = false; + if (bc.has_foundFV0() && std::abs(bc.foundFV0().time()) < maxAbsTimeFV0) { + auto amps = bc.foundFV0().amplitude(); + fv0aSignal = (std::accumulate(amps.begin(), amps.end(), 0.f) > 0); + } + + // FDD + bool fddaSignal = false; + bool fddcSignal = false; + if (bc.has_foundFDD()) { + if (std::abs(bc.foundFDD().timeA()) < maxAbsTimeFDD) { + fddaSignal = (sumFddAmplitude(bc.foundFDD().chargeA()) > 0); + } + if (std::abs(bc.foundFDD().timeC()) < maxAbsTimeFDD) { + fddcSignal = (sumFddAmplitude(bc.foundFDD().chargeC()) > 0); + } + } + + // ZN: an entry in time, no requirement on the energy + bool znaSignal = false; + bool zncSignal = false; + if (bc.has_zdc()) { + float tZNA = bc.zdc().timeZNA(); + float tZNC = bc.zdc().timeZNC(); + znaSignal = std::isfinite(tZNA) && std::abs(tZNA) < maxAbsTimeZN; + zncSignal = std::isfinite(tZNC) && std::abs(tZNC) < maxAbsTimeZN; + } + bool znSignal = znaSignal || zncSignal; + + // a side is empty if no detector has a signal in time, at any amplitude above zero + bool ft0aSignal = ft0aInTime && (ampFT0A > 0); + bool ft0cSignal = ft0cInTime && (ampFT0C > 0); + bool noSignalC = !ft0cSignal && !fddcSignal && !zncSignal; + bool noSignalA = !ft0aSignal && !fv0aSignal && !fddaSignal && !znaSignal; + + //------------------------------------------------------------------------------ + // pile-up counters. The denominators already hold all analysed crossings + // of this bunch; a stored crossing with activity on the other side is + // removed from them. + if (noSignalC) { + for (int i = 0; i < nThresholds; i++) { + if (ft0aInTime && ampFT0A > thresholds[i]) { + hNumA[i]->Fill(thisTF, thisBcb); + if (fillZnVariation && znSignal) { + hNumAZn[i]->Fill(thisTF, thisBcb); + } + } + } + } else { + hDenA->Fill(thisTF, thisBcb, -1.); + } + if (noSignalA) { + for (int i = 0; i < nThresholds; i++) { + if (ft0cInTime && ampFT0C > thresholds[i]) { + hNumC[i]->Fill(thisTF, thisBcb); + if (fillZnVariation && znSignal) { + hNumCZn[i]->Fill(thisTF, thisBcb); + } + } + } + } else { + hDenC->Fill(thisTF, thisBcb, -1.); + } + } // loop over bcs + } // end processBCs() + PROCESS_SWITCH(UpcPileUp, processBCs, "pile-up probability in FT0", true); + +}; // end of struct UpcPileUp + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGUD/Tasks/upcQuarkoniaCentralBarrel.cxx b/PWGUD/Tasks/upcQuarkoniaCentralBarrel.cxx index 7e4996c13ba..7352dafee7c 100644 --- a/PWGUD/Tasks/upcQuarkoniaCentralBarrel.cxx +++ b/PWGUD/Tasks/upcQuarkoniaCentralBarrel.cxx @@ -538,13 +538,13 @@ struct upcQuarkoniaCentralBarrel { // Check the number of Protons and antiProtons // needs at least 1 of each if (!buildSameSignPairs && nProtons >= 1 && nAntiProtons >= 1) { - buildProtonAntiProtonPairs(fullTracks, (TObject*)nullptr, selProtonIndices, selAntiProtonIndices, selGapSide); + buildProtonAntiProtonPairs(fullTracks, nullptr, selProtonIndices, selAntiProtonIndices, selGapSide); } if (buildSameSignPairs && nProtons > 1) { - buildProtonAntiProtonPairs(fullTracks, (TObject*)nullptr, selProtonIndices, selProtonIndices, selGapSide); + buildProtonAntiProtonPairs(fullTracks, nullptr, selProtonIndices, selProtonIndices, selGapSide); } if (buildSameSignPairs && nAntiProtons > 1) { - buildProtonAntiProtonPairs(fullTracks, (TObject*)nullptr, selAntiProtonIndices, selAntiProtonIndices, selGapSide); + buildProtonAntiProtonPairs(fullTracks, nullptr, selAntiProtonIndices, selAntiProtonIndices, selGapSide); } } diff --git a/PWGUD/Tasks/upcRhoAnalysis.cxx b/PWGUD/Tasks/upcRhoAnalysis.cxx index db20d94afb3..adb18292a7b 100644 --- a/PWGUD/Tasks/upcRhoAnalysis.cxx +++ b/PWGUD/Tasks/upcRhoAnalysis.cxx @@ -72,7 +72,11 @@ DECLARE_SOA_COLUMN(PosY, posY, float); DECLARE_SOA_COLUMN(PosZ, posZ, float); DECLARE_SOA_COLUMN(OccupancyInTime, occupancyInTime, float); DECLARE_SOA_COLUMN(HadronicRate, hadronicRate, float); +DECLARE_SOA_COLUMN(GlobalBC, globalBC, uint64_t); DECLARE_SOA_COLUMN(LocalBC, localBC, int); +DECLARE_SOA_COLUMN(KNoCollInTimeRangeStandard, kNoCollInTimeRangeStandard, bool); +DECLARE_SOA_COLUMN(KNoCollInRofStandard, kNoCollInRofStandard, bool); +DECLARE_SOA_COLUMN(KNoHighMultCollInPrevRof, kNoHighMultCollInPrevRof, bool); // FIT info DECLARE_SOA_COLUMN(TotalFT0AmplitudeA, totalFT0AmplitudeA, float); DECLARE_SOA_COLUMN(TotalFT0AmplitudeC, totalFT0AmplitudeC, float); @@ -107,9 +111,12 @@ DECLARE_SOA_COLUMN(LeadingTrackKaPID, leadingTrackKaPID, float); DECLARE_SOA_COLUMN(SubleadingTrackKaPID, subleadingTrackKaPID, float); DECLARE_SOA_COLUMN(LeadingTrackPrPID, leadingTrackPrPID, float); DECLARE_SOA_COLUMN(SubleadingTrackPrPID, subleadingTrackPrPID, float); +DECLARE_SOA_COLUMN(LeadingTrackTofHit, leadingTrackTofHit, int); +DECLARE_SOA_COLUMN(SubleadingTrackTofHit, subleadingTrackTofHit, int); } // namespace reco_tree DECLARE_SOA_TABLE(RecoTree, "AOD", "RECOTREE", - reco_tree::RecoSetting, reco_tree::RunNumber, reco_tree::PosX, reco_tree::PosY, reco_tree::PosZ, reco_tree::OccupancyInTime, reco_tree::HadronicRate, reco_tree::LocalBC, + reco_tree::RecoSetting, reco_tree::RunNumber, reco_tree::PosX, reco_tree::PosY, reco_tree::PosZ, reco_tree::OccupancyInTime, reco_tree::HadronicRate, reco_tree::GlobalBC, reco_tree::LocalBC, + reco_tree::KNoCollInTimeRangeStandard, reco_tree::KNoCollInRofStandard, reco_tree::KNoHighMultCollInPrevRof, reco_tree::TotalFT0AmplitudeA, reco_tree::TotalFT0AmplitudeC, reco_tree::TotalFV0AmplitudeA, reco_tree::TotalFDDAmplitudeA, reco_tree::TotalFDDAmplitudeC, reco_tree::TimeFT0A, reco_tree::TimeFT0C, reco_tree::TimeFV0A, reco_tree::TimeFDDA, reco_tree::TimeFDDC, reco_tree::EnergyCommonZNA, reco_tree::EnergyCommonZNC, reco_tree::TimeZNA, reco_tree::TimeZNC, reco_tree::NeutronClass, @@ -120,8 +127,8 @@ DECLARE_SOA_TABLE(RecoTree, "AOD", "RECOTREE", reco_tree::LeadingTrackPiPID, reco_tree::SubleadingTrackPiPID, reco_tree::LeadingTrackElPID, reco_tree::SubleadingTrackElPID, reco_tree::LeadingTrackKaPID, reco_tree::SubleadingTrackKaPID, - reco_tree::LeadingTrackPrPID, reco_tree::SubleadingTrackPrPID); - + reco_tree::LeadingTrackPrPID, reco_tree::SubleadingTrackPrPID, + reco_tree::LeadingTrackTofHit, reco_tree::SubleadingTrackTofHit); namespace mc_tree { // misc event info @@ -129,6 +136,7 @@ DECLARE_SOA_COLUMN(RunNumber, runNumber, int); DECLARE_SOA_COLUMN(PosX, posX, float); DECLARE_SOA_COLUMN(PosY, posY, float); DECLARE_SOA_COLUMN(PosZ, posZ, float); +DECLARE_SOA_COLUMN(GlobalBC, globalBC, uint64_t); DECLARE_SOA_COLUMN(LocalBC, localBC, int); // tracks DECLARE_SOA_COLUMN(LeadingTrackSign, leadingTrackSign, int); @@ -141,7 +149,7 @@ DECLARE_SOA_COLUMN(LeadingTrackPhi, leadingTrackPhi, float); DECLARE_SOA_COLUMN(SubleadingTrackPhi, subleadingTrackPhi, float); } // namespace mc_tree DECLARE_SOA_TABLE(McTree, "AOD", "MCTREE", - mc_tree::RunNumber, mc_tree::PosX, mc_tree::PosY, mc_tree::PosZ, mc_tree::LocalBC, + mc_tree::RunNumber, mc_tree::PosX, mc_tree::PosY, mc_tree::PosZ, mc_tree::GlobalBC, mc_tree::LocalBC, mc_tree::LeadingTrackSign, mc_tree::SubleadingTrackSign, mc_tree::LeadingTrackPt, mc_tree::SubleadingTrackPt, mc_tree::LeadingTrackEta, mc_tree::SubleadingTrackEta, @@ -149,6 +157,12 @@ DECLARE_SOA_TABLE(McTree, "AOD", "MCTREE", namespace resolution_tree { +// misc event info +DECLARE_SOA_COLUMN(GenGlobalBC, genGlobalBC, uint64_t); +DECLARE_SOA_COLUMN(GenLocalBC, genLocalBC, int); +DECLARE_SOA_COLUMN(RecoRunNumber, recoRunNumber, int); +DECLARE_SOA_COLUMN(RecoGlobalBC, recoGlobalBC, uint64_t); +DECLARE_SOA_COLUMN(RecoLocalBC, recoLocalBC, int); // vertex info DECLARE_SOA_COLUMN(GenPosX, genPosX, float); DECLARE_SOA_COLUMN(GenPosY, genPosY, float); @@ -156,6 +170,17 @@ DECLARE_SOA_COLUMN(GenPosZ, genPosZ, float); DECLARE_SOA_COLUMN(RecoPosX, recoPosX, float); DECLARE_SOA_COLUMN(RecoPosY, recoPosY, float); DECLARE_SOA_COLUMN(RecoPosZ, recoPosZ, float); +// FIT info +DECLARE_SOA_COLUMN(TotalFT0AmplitudeA, totalFT0AmplitudeA, float); +DECLARE_SOA_COLUMN(TotalFT0AmplitudeC, totalFT0AmplitudeC, float); +DECLARE_SOA_COLUMN(TotalFV0AmplitudeA, totalFV0AmplitudeA, float); +DECLARE_SOA_COLUMN(TotalFDDAmplitudeA, totalFDDAmplitudeA, float); +DECLARE_SOA_COLUMN(TotalFDDAmplitudeC, totalFDDAmplitudeC, float); +DECLARE_SOA_COLUMN(TimeFT0A, timeFT0A, float); +DECLARE_SOA_COLUMN(TimeFT0C, timeFT0C, float); +DECLARE_SOA_COLUMN(TimeFV0A, timeFV0A, float); +DECLARE_SOA_COLUMN(TimeFDDA, timeFDDA, float); +DECLARE_SOA_COLUMN(TimeFDDC, timeFDDC, float); // track info DECLARE_SOA_COLUMN(LeadingSign, leadingSign, int); DECLARE_SOA_COLUMN(LeadingGenPt, leadingGenPt, float); @@ -164,6 +189,7 @@ DECLARE_SOA_COLUMN(LeadingGenPhi, leadingGenPhi, float); DECLARE_SOA_COLUMN(LeadingRecoPt, leadingRecoPt, float); DECLARE_SOA_COLUMN(LeadingRecoEta, leadingRecoEta, float); DECLARE_SOA_COLUMN(LeadingRecoPhi, leadingRecoPhi, float); +DECLARE_SOA_COLUMN(LeadingRecoTofHit, leadingRecoTofHit, int); DECLARE_SOA_COLUMN(SubleadingSign, subleadingSign, int); DECLARE_SOA_COLUMN(SubleadingGenPt, subleadingGenPt, float); DECLARE_SOA_COLUMN(SubleadingGenEta, subleadingGenEta, float); @@ -171,14 +197,20 @@ DECLARE_SOA_COLUMN(SubleadingGenPhi, subleadingGenPhi, float); DECLARE_SOA_COLUMN(SubleadingRecoPt, subleadingRecoPt, float); DECLARE_SOA_COLUMN(SubleadingRecoEta, subleadingRecoEta, float); DECLARE_SOA_COLUMN(SubleadingRecoPhi, subleadingRecoPhi, float); +DECLARE_SOA_COLUMN(SubleadingRecoTofHit, subleadingRecoTofHit, int); } // namespace resolution_tree DECLARE_SOA_TABLE(ResolutionTree, "AOD", "RESOLUTIONTREE", + resolution_tree::GenGlobalBC, resolution_tree::GenLocalBC, + resolution_tree::RecoRunNumber, + resolution_tree::RecoGlobalBC, resolution_tree::RecoLocalBC, resolution_tree::GenPosX, resolution_tree::GenPosY, resolution_tree::GenPosZ, resolution_tree::RecoPosX, resolution_tree::RecoPosY, resolution_tree::RecoPosZ, + resolution_tree::TotalFT0AmplitudeA, resolution_tree::TotalFT0AmplitudeC, resolution_tree::TotalFV0AmplitudeA, resolution_tree::TotalFDDAmplitudeA, resolution_tree::TotalFDDAmplitudeC, + resolution_tree::TimeFT0A, resolution_tree::TimeFT0C, resolution_tree::TimeFV0A, resolution_tree::TimeFDDA, resolution_tree::TimeFDDC, resolution_tree::LeadingSign, resolution_tree::LeadingGenPt, resolution_tree::LeadingGenEta, resolution_tree::LeadingGenPhi, - resolution_tree::LeadingRecoPt, resolution_tree::LeadingRecoEta, resolution_tree::LeadingRecoPhi, + resolution_tree::LeadingRecoPt, resolution_tree::LeadingRecoEta, resolution_tree::LeadingRecoPhi, resolution_tree::LeadingRecoTofHit, resolution_tree::SubleadingSign, resolution_tree::SubleadingGenPt, resolution_tree::SubleadingGenEta, resolution_tree::SubleadingGenPhi, - resolution_tree::SubleadingRecoPt, resolution_tree::SubleadingRecoEta, resolution_tree::SubleadingRecoPhi); + resolution_tree::SubleadingRecoPt, resolution_tree::SubleadingRecoEta, resolution_tree::SubleadingRecoPhi, resolution_tree::SubleadingRecoTofHit); } // namespace o2::aod struct UpcRhoAnalysis { @@ -188,9 +220,9 @@ struct UpcRhoAnalysis { SGSelector sgSelector; - const float pcEtaCut = 0.9; // physics coordination recommendation - const int nPions = 2; // only study dipion final states - const std::vector runNumbers = {544013, 544028, 544032, 544091, 544095, 544098, 544116, 544121, 544122, 544123, 544124, 544184, 544185, 544389, 544390, 544391, 544392, 544451, 544454, 544474, 544475, 544476, 544477, 544490, 544491, 544492, 544508, 544510, 544511, 544512, 544514, 544515, 544518, 544548, 544549, 544550, 544551, 544564, 544565, 544567, 544568, 544580, 544582, 544583, 544585, 544614, 544640, 544652, 544653, 544672, 544674, 544692, 544693, 544694, 544696, 544739, 544742, 544754, 544767, 544794, 544795, 544797, 544813, 544868, 544886, 544887, 544896, 544911, 544913, 544914, 544917, 544931, 544947, 544961, 544963, 544964, 544968, 544991, 544992, 545004, 545008, 545009, 545041, 545042, 545044, 545047, 545060, 545062, 545063, 545064, 545066, 545086, 545103, 545117, 545171, 545184, 545185, 545210, 545222, 545223, 545246, 545249, 545262, 545289, 545291, 545294, 545295, 545296, 545311, 545312, 545332, 545345, 545367}; + const float pcEtaCut = 0.9; // physics coordination recommendation + const int nExpectedPions = 2; // only study dipion final states + const std::vector runNumbers = {544013, 544028, 544032, 544091, 544095, 544098, 544116, 544121, 544122, 544123, 544124, 544184, 544185, 544389, 544390, 544391, 544392, 544451, 544454, 544474, 544475, 544476, 544477, 544490, 544491, 544492, 544508, 544510, 544511, 544512, 544514, 544515, 544518, 544548, 544549, 544550, 544551, 544564, 544565, 544567, 544568, 544580, 544582, 544583, 544585, 544614, 544640, 544652, 544653, 544672, 544674, 544692, 544693, 544694, 544696, 544739, 544742, 544754, 544767, 544794, 544795, 544797, 544813, 544868, 544886, 544887, 544896, 544913, 544914, 544917, 544931, 544947, 544961, 544963, 544964, 544968, 544992, 545009, 545044, 545047, 545063, 545064, 545066, 545185, 545210, 545223, 545249, 545291, 545294, 545295, 545296, 545312}; AxisSpec runNumberAxis = {static_cast(runNumbers.size()), 0.5, static_cast(runNumbers.size()) + 0.5, "run number"}; Configurable isPO{"isPO", false, "processing p-O data?"}; @@ -207,7 +239,7 @@ struct UpcRhoAnalysis { Configurable useRecoFlag{"useRecoFlag", false, "use UPC/STD reconstruction flag for event selection?"}; Configurable cutRecoFlag{"cutRecoFlag", 1, "0 = std mode, 1 = upc mode"}; Configurable useRctFlag{"useRctFlag", true, "use RCT flags for event selection?"}; - Configurable cutRctFlag{"cutRctFlag", 1, "0 = off, 1 = CBT, 2 = CBT+ZDC, 3 = CBThadron, 4 = CBThadron+ZDC"}; + Configurable cutRctFlag{"cutRctFlag", 4, "0 = off, 1 = CBT, 2 = CBT+ZDC, 3 = CBThadron, 4 = CBThadron+ZDC"}; Configurable selectRuns{"selectRuns", false, "select runs?"}; Configurable> selectedRuns{"selectedRuns", {544013, 544028, 544032, 544091, 544095, 544098, 544116, 544121, 544122, 544123, 544124, 544184, 544185, 544389, 544390, 544391, 544392, 544451, 544454, 544474, 544475, 544476, 544477, 544490, 544491, 544492, 544508, 544510, 544511, 544512, 544514, 544515, 544518, 544548, 544549, 544550, 544551, 544564, 544565, 544567, 544568, 544580, 544582, 544583, 544585, 544614, 544640, 544652, 544653, 544672, 544674, 544692, 544693, 544694, 544696, 544739, 544742, 544754, 544767, 544794, 544795, 544797, 544813, 544868, 544886, 544887, 544896, 544913, 544914, 544917, 544931, 544947, 544961, 544963, 544964, 544968, 544992, 545009, 545044, 545047, 545063, 545064, 545066, 545185, 545210, 545223, 545249, 545291, 545294, 545295, 545296, 545312}, "list of selected runs"}; @@ -230,11 +262,12 @@ struct UpcRhoAnalysis { Configurable tracksMaxTpcChi2NClCut{"tracksMaxTpcChi2NClCut", 3.0, "max TPC chi2/Ncls cut"}; Configurable tracksMinTpcNClsCrossedOverFindableCut{"tracksMinTpcNClsCrossedOverFindableCut", 1.0, "min TPC crossed rows / findable clusters cut"}; Configurable tracksMinPtCut{"tracksMinPtCut", 0.1, "min pT cut on tracks"}; + Configurable applyPid{"applyPid", true, "apply PID before creating derived data?"}; Configurable systemMassMinCut{"systemMassMinCut", 0.5, "min M cut for reco system"}; Configurable systemMassMaxCut{"systemMassMaxCut", 1.0, "max M cut for reco system"}; Configurable systemPtCut{"systemPtCut", 0.1, "max pT cut for reco system"}; - Configurable systemYCut{"systemYCut", 0.9, "rapiditiy cut for reco system"}; + Configurable systemYCut{"systemYCut", 0.5, "rapiditiy cut for reco system"}; ConfigurableAxis mAxis{"mAxis", {400, 0.0, 4.0}, "#it{m} (GeV/#it{c}^{2})"}; ConfigurableAxis ptAxis{"ptAxis", {400, 0.0, 4.0}, "#it{p}_{T} (GeV/#it{c})"}; @@ -256,13 +289,15 @@ struct UpcRhoAnalysis { void init(o2::framework::InitContext& context) { - if (context.mOptions.get("processSGdata") || context.mOptions.get("processDGdata")) { + if (context.mOptions.get("processSGdata") || context.mOptions.get("processDGdata") || context.mOptions.get("processMcRecoWithTruth")) { // QA // collisions rQC.add("QC/collisions/all/hPosXY", ";vertex #it{x} (cm);vertex #it{y} (cm);counts", kTH2D, {{2000, -0.1, 0.1}, {2000, -0.1, 0.1}}); rQC.add("QC/collisions/all/hPosZ", ";vertex #it{z} (cm);counts", kTH1D, {{400, -20.0, 20.0}}); rQC.add("QC/collisions/all/hNumContrib", ";number of PV contributors;counts", kTH1D, {{36, -0.5, 35.5}}); rQC.add("QC/collisions/all/hZdcCommonEnergy", ";ZNA common energy (TeV);ZNC common energy (TeV);counts", kTH2D, {znCommonEnergyAxis, znCommonEnergyAxis}); + rQC.add("QC/collisions/all/hZnaCommonEnergyPerRun", ";ZNA common energy (TeV);run number;counts", kTH2D, {znCommonEnergyAxis, runNumberAxis}); + rQC.add("QC/collisions/all/hZncCommonEnergyPerRun", ";ZNC common energy (TeV);run number;counts", kTH2D, {znCommonEnergyAxis, runNumberAxis}); rQC.add("QC/collisions/all/hZdcTime", ";ZNA time (ns);ZNC time (ns);counts", kTH2D, {znTimeAxis, znTimeAxis}); rQC.add("QC/collisions/all/hZNTimeVsZNCommonEnergy", ";ZNA/C common energy (TeV);ZNA/C time (ns);counts", kTH2D, {znCommonEnergyAxis, znTimeAxis}); rQC.add("QC/collisions/all/hTotalFT0AmplitudeA", ";FT0A amplitude;counts", kTH1D, {{160, 0.0, 160.0}}); @@ -276,12 +311,17 @@ struct UpcRhoAnalysis { rQC.add("QC/collisions/all/hTimeFDDA", ";FDDA time (ns);counts", kTH1D, {{400, -5.0, 35.0}}); rQC.add("QC/collisions/all/hTimeFDDC", ";FDDC time (ns);counts", kTH1D, {{400, -5.0, 35.0}}); rQC.add("QC/collisions/all/hOccupancyInTime", ";occupancy in time;counts", kTH1D, {{1100, 0.0, 1100.0}}); + rQC.add("QC/collisions/all/hRecoMode", ";reconstruction mode;counts", kTH1D, {{2, -0.5, 1.5}}); rQC.add("QC/collisions/hNumContribVsPVTracks", ";number of track.isPVContributor() per collision;collision.numContrib();counts", kTH2D, {{101, -0.5, 100.5}, {101, -0.5, 100.5}}); + rQC.add("QC/collisions/hLocalBC", ";BC ID;counts", kTH1D, {{o2::constants::lhc::LHCMaxBunches, -0.5, static_cast(o2::constants::lhc::LHCMaxBunches) - 0.5}}); + rQC.add("QC/collisions/all/hkNoCollInTimeRangeStandard", ";kNoCollInTimeRangeStandard;counts", kTH1D, {{2, -0.5, 1.5}}); + rQC.add("QC/collisions/all/hkNoCollInRofStandard", ";kNoCollInRofStandard;counts", kTH1D, {{2, -0.5, 1.5}}); + rQC.add("QC/collisions/all/hkNoHighMultCollInPrevRof", ";kNoHighMultCollInPrevRof;counts", kTH1D, {{2, -0.5, 1.5}}); // events with selected rho candidates rQC.addClone("QC/collisions/all/", "QC/collisions/trackSelections/"); rQC.addClone("QC/collisions/all/", "QC/collisions/systemSelections/"); - std::vector collisionSelectionCounterLabels = {"all collisions", "rapidity gap", "ITS-TPC vertex", "same bunch pile-up", "ITS ROF border", "TF border", "#it{z} position", "number of contributors", "RCT selections", "reco flag selection", "occupancy selection"}; + std::vector collisionSelectionCounterLabels = {"all collisions", "rapidity gap", "ITS-TPC vertex", "same bunch pile-up", "ITS ROF border", "TF border", "vertex #it{z} position", "number of contributors", "RCT selections", "occupancy selection"}; rQC.add("QC/collisions/hSelectionCounter", ";;collisions passing selections", kTH1D, {{static_cast(collisionSelectionCounterLabels.size()), -0.5, static_cast(collisionSelectionCounterLabels.size()) - 0.5}}); rQC.add("QC/collisions/hSelectionCounterPerRun", ";;run number;collisions passing selections", kTH2D, {{static_cast(collisionSelectionCounterLabels.size()), -0.5, static_cast(collisionSelectionCounterLabels.size()) - 0.5}, runNumberAxis}); for (int i = 0; i < static_cast(collisionSelectionCounterLabels.size()); ++i) { @@ -292,13 +332,10 @@ struct UpcRhoAnalysis { rQC.get(HIST("QC/collisions/hSelectionCounterPerRun"))->GetYaxis()->SetBinLabel(i + 1, std::to_string(runNumbers[i]).c_str()); // tracks rQC.add("QC/tracks/all/hTpcNSigmaPi", ";TPC #it{n#sigma}(#pi);counts", kTH1D, {nSigmaAxis}); - rQC.add("QC/tracks/all/hPtVsEtaVsTpcNSigmaPi", ";TPC #it{n#sigma}(#pi);#it{p}_{T} (GeV/#it{c});#it{#eta}", kTH3D, {{100, -10.0, 10.0}, {200, 0.0, 4.0}, {18, -0.9, 0.9}}); rQC.add("QC/tracks/all/hTpcNSigmaEl", ";TPC #it{n#sigma}(e);counts", kTH1D, {nSigmaAxis}); - rQC.add("QC/tracks/all/hPtVsEtaVsTpcNSigmaEl", ";TPC #it{n#sigma}(e);#it{p}_{T} (GeV/#it{c});#it{#eta}", kTH3D, {{100, -10.0, 10.0}, {200, 0.0, 4.0}, {18, -0.9, 0.9}}); rQC.add("QC/tracks/all/hTpcNSigmaKa", ";TPC #it{n#sigma}(K);counts", kTH1D, {nSigmaAxis}); - rQC.add("QC/tracks/all/hPtVsEtaVsTpcNSigmaKa", ";TPC #it{n#sigma}(K);#it{p}_{T} (GeV/#it{c});#it{#eta}", kTH3D, {{100, -10.0, 10.0}, {200, 0.0, 4.0}, {18, -0.9, 0.9}}); rQC.add("QC/tracks/all/hTpcNSigmaPr", ";TPC #it{n#sigma}(p);counts", kTH1D, {nSigmaAxis}); - rQC.add("QC/tracks/all/hPtVsEtaVsTpcNSigmaPr", ";TPC #it{n#sigma}(p);#it{p}_{T} (GeV/#it{c});#it{#eta}", kTH3D, {{100, -10.0, 10.0}, {200, 0.0, 4.0}, {18, -0.9, 0.9}}); + rQC.add("QC/tracks/all/hTpcNSigmaMu", ";TPC #it{n#sigma}(#mu);counts", kTH1D, {nSigmaAxis}); rQC.add("QC/tracks/all/hDcaXYZ", ";track #it{DCA}_{z} (cm);track #it{DCA}_{xy} (cm);counts", kTH2D, {{1000, -5.0, 5.0}, {400, -2.0, 2.0}}); rQC.add("QC/tracks/all/hItsNCls", ";ITS #it{N}_{cls};counts", kTH1D, {{9, -0.5, 8.5}}); rQC.add("QC/tracks/all/hItsChi2NCl", ";ITS #it{#chi}^{2}/#it{N}_{cls};counts", kTH1D, {{150, 0.0, 15.0}}); @@ -311,18 +348,31 @@ struct UpcRhoAnalysis { rQC.add("QC/tracks/all/hPhi", ";#it{#phi} (rad);counts", kTH1D, {phiAxis}); rQC.add("QC/tracks/all/hTpcSignalVsP", ";|#it{p}| (GeV/#it{c});TPC d#it{E}/d#it{x} signal (arb. units);counts", kTH2D, {ptAxis, {500, 0.0, 500.0}}); rQC.add("QC/tracks/all/hTpcSignalVsPt", ";#it{p}_{T} (GeV/#it{c});TPC d#it{E}/d#it{x} signal (arb. units);counts", kTH2D, {ptAxis, {500, 0.0, 500.0}}); + rQC.add("QC/tracks/all/hLength", ";track length (cm);counts", kTH1D, {{1000, 0.0, 1000.0}}); + rQC.add("QC/tracks/all/hTofChi2", ";TOF #it{#chi}^{2};counts", kTH1D, {{100, 0.0, 10.0}}); + rQC.add("QC/tracks/all/hTofSignalVsP", ";|#it{p}| (GeV/#it{c});TOF signal (arb. units);counts", kTH2D, {ptAxis, {500, 0.0, 500.0}}); + rQC.add("QC/tracks/all/hTofSignalVsPt", ";#it{p}_{T} (GeV/#it{c});TOF signal (arb. units);counts", kTH2D, {ptAxis, {500, 0.0, 500.0}}); + rQC.add("QC/tracks/all/hTofExpMom", ";expected momentum from TOF (GeV/#it{c});counts", kTH1D, {ptAxis}); + rQC.add("QC/tracks/all/hBeta", ";TOF #it{#beta};counts", kTH1D, {{1000, 0.0, 1.0}}); + rQC.add("QC/tracks/all/hBetaError", ";TOF #it{#beta} error;counts", kTH1D, {{1000, 0.0, 1.0}}); + rQC.add("QC/tracks/all/hTofNSigmaPi", ";TOF #it{n#sigma}(#pi);counts", kTH1D, {nSigmaAxis}); + rQC.add("QC/tracks/all/hTofNSigmaEl", ";TOF #it{n#sigma}(e);counts", kTH1D, {nSigmaAxis}); + rQC.add("QC/tracks/all/hTofNSigmaKa", ";TOF #it{n#sigma}(K);counts", kTH1D, {nSigmaAxis}); + rQC.add("QC/tracks/all/hTofNSigmaPr", ";TOF #it{n#sigma}(p);counts", kTH1D, {nSigmaAxis}); + rQC.add("QC/tracks/all/hTofNSigmaMu", ";TOF #it{n#sigma}(#mu);counts", kTH1D, {nSigmaAxis}); // tracks passing selections rQC.addClone("QC/tracks/all/", "QC/tracks/trackSelections/"); rQC.addClone("QC/tracks/all/", "QC/tracks/systemSelections/"); rQC.add("QC/tracks/trackSelections/hRemainingTracks", ";remaining tracks;counts", kTH1D, {{21, -0.5, 20.5}}); rQC.add("QC/tracks/trackSelections/hTpcNSigmaPi2D", ";TPC #it{n#sigma}(#pi)_{leading};TPC #it{n#sigma}(#pi)_{subleading};counts", kTH2D, {nSigmaAxis, nSigmaAxis}); + rQC.add("QC/tracks/trackSelections/hTpcNSigmaPi2DAfterCut", ";TPC #it{n#sigma}(#pi)_{leading};TPC #it{n#sigma}(#pi)_{subleading};counts", kTH2D, {nSigmaAxis, nSigmaAxis}); rQC.add("QC/tracks/trackSelections/hTpcNSigmaEl2D", ";TPC #it{n#sigma}(e)_{leading};TPC #it{n#sigma}(e)_{subleading};counts", kTH2D, {nSigmaAxis, nSigmaAxis}); rQC.add("QC/tracks/trackSelections/hTpcNSigmaKa2D", ";TPC #it{n#sigma}(K)_{leading};TPC #it{n#sigma}(K)_{subleading};counts", kTH2D, {nSigmaAxis, nSigmaAxis}); rQC.add("QC/tracks/trackSelections/hTpcNSigmaPr2D", ";TPC #it{n#sigma}(p)_{leading};TPC #it{n#sigma}(p)_{subleading};counts", kTH2D, {nSigmaAxis, nSigmaAxis}); + rQC.add("QC/tracks/trackSelections/hTpcNSigmaMu2D", ";TPC #it{n#sigma}(#mu)_{leading};TPC #it{n#sigma}(#mu)_{subleading};counts", kTH2D, {nSigmaAxis, nSigmaAxis}); // selection counter std::vector trackSelectionCounterLabels = {"all tracks", "PV contributor", "ITS hit", "ITS #it{N}_{cls}", "itsClusterMap check", "ITS #it{#chi}^{2}/#it{N}_{cls}", "TPC hit", "found TPC #it{N}_{cls}", "TPC #it{#chi}^{2}/#it{N}_{cls}", "TPC crossed rows", "TPC crossed rows/#it{N}_{cls}", - "TOF requirement", "#it{p}_{T}", "#it{DCA}", "#it{#eta}", "exactly 2 tracks", "PID"}; rQC.add("QC/tracks/hSelectionCounter", ";;tracks passing selections", kTH1D, {{static_cast(trackSelectionCounterLabels.size()), -0.5, static_cast(trackSelectionCounterLabels.size()) - 0.5}}); rQC.add("QC/tracks/hSelectionCounterPerRun", ";;run number;tracks passing selections", kTH2D, {{static_cast(trackSelectionCounterLabels.size()), -0.5, static_cast(trackSelectionCounterLabels.size()) - 0.5}, runNumberAxis}); @@ -378,7 +428,7 @@ struct UpcRhoAnalysis { rSystem.addClone("system/selected/AnAn/", "system/selected/XnXn/"); } - if (context.mOptions.get("processMCdata") || context.mOptions.get("processMCdataWithBCs")) { + if (context.mOptions.get("processMCdata") || context.mOptions.get("processMCdataWithBCs") || context.mOptions.get("processMcRecoWithTruth")) { // MC // collisions rMC.add("MC/collisions/hPosXY", ";vertex #it{x} (cm);vertex #it{y} (cm);counts", kTH2D, {{2000, -0.1, 0.1}, {2000, -0.1, 0.1}}); @@ -413,6 +463,8 @@ struct UpcRhoAnalysis { rMC.add("MC/system/hPhiCharge", ";#Delta#it{#phi}_{charge} (rad);counts", kTH1D, {deltaPhiAxis}); rMC.add("MC/system/hPhiRandomVsM", ";#it{m} (GeV/#it{c}^{2});#Delta#it{#phi} (rad);counts", kTH2D, {mAxis, deltaPhiAxis}); rMC.add("MC/system/hPhiChargeVsM", ";#it{m} (GeV/#it{c}^{2});#Delta#it{#phi} (rad);counts", kTH2D, {mAxis, deltaPhiAxis}); + rMC.add("MC/system/hPhiRandomVsPt", ";#it{p}_{T} (GeV/#it{c});#Delta#it{#phi} (rad);counts", kTH2D, {ptAxis, deltaPhiAxis}); + rMC.add("MC/system/hPhiChargeVsPt", ";#it{p}_{T} (GeV/#it{c});#Delta#it{#phi} (rad);counts", kTH2D, {ptAxis, deltaPhiAxis}); rMC.addClone("MC/system/", "MC/system/selected/"); } @@ -434,9 +486,11 @@ struct UpcRhoAnalysis { rResolution.add("MC/resolution/system/1D/hM", ";#it{m}_{reco} - #it{m}_{true} (GeV/#it{c}^{2});counts", kTH1D, {resolutionAxis}); rResolution.add("MC/resolution/system/2D/hMVsM", ";#it{m}_{true} (GeV/#it{c}^{2});#it{m}_{reco} (GeV/#it{c}^{2});counts", kTH2D, {mAxis, mAxis}); rResolution.add("MC/resolution/system/1D/hPt", ";1/#it{p}_{T, reco} - 1/#it{p}_{T, true} (1/(GeV/#it{c}));counts", kTH1D, {resolutionAxis}); - rResolution.add("MC/resolution/system/2D/hPtVsPt", ";1/#it{p}_{T, true} (GeV/#it{c});#it{p}_{T, reco} (GeV/#it{c});counts", kTH2D, {ptAxis, ptAxis}); + rResolution.add("MC/resolution/system/2D/hPtVsPt", ";#it{p}_{T, true} (GeV/#it{c});#it{p}_{T, reco} (GeV/#it{c});counts", kTH2D, {ptAxis, ptAxis}); rResolution.add("MC/resolution/system/1D/hY", ";#it{y}_{reco} - #it{y}_{true};counts", kTH1D, {resolutionAxis}); rResolution.add("MC/resolution/system/2D/hYVsY", ";#it{y}_{true};#it{y}_{reco};counts", kTH2D, {yAxis, yAxis}); + rResolution.add("MC/resolution/system/1D/hPhi", ";#it{#phi}_{reco} - #it{#phi}_{true} (rad);counts", kTH1D, {resolutionAxis}); + rResolution.add("MC/resolution/system/2D/hPhiVsPhi", ";#it{#phi}_{true} (rad);#it{#phi}_{reco} (rad);counts", kTH2D, {phiAxis, phiAxis}); rResolution.add("MC/resolution/system/1D/hDeltaPhi", ";#Delta#it{#phi}_{reco} - #Delta#it{#phi}_{true} (rad);counts", kTH1D, {resolutionAxis}); rResolution.add("MC/resolution/system/2D/hDeltaPhiVsDeltaPhi", ";#Delta#it{#phi}_{true} (rad);#Delta#it{#phi}_{reco} (rad);counts", kTH2D, {deltaPhiAxis, deltaPhiAxis}); } @@ -447,11 +501,13 @@ struct UpcRhoAnalysis { static constexpr std::string_view NeutronClass[5] = {"AnAn/", "0n0n/", "Xn0n/", "0nXn/", "XnXn/"}; template - void fillCollisionQcHistos(const C& collision) // fills collision QC histograms before/after cuts + void fillCollisionQcHistos(const C& collision, int runIndex) // fills collision QC histograms before/after cuts { rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hPosXY"), collision.posX(), collision.posY()); rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hPosZ"), collision.posZ()); rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hZdcCommonEnergy"), collision.energyCommonZNA(), collision.energyCommonZNC()); + rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hZnaCommonEnergyPerRun"), collision.energyCommonZNA(), runIndex); + rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hZncCommonEnergyPerRun"), collision.energyCommonZNC(), runIndex); rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hZdcTime"), collision.timeZNA(), collision.timeZNC()); rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hZNTimeVsZNCommonEnergy"), collision.energyCommonZNA(), collision.timeZNA()); rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hZNTimeVsZNCommonEnergy"), collision.energyCommonZNC(), collision.timeZNC()); @@ -467,6 +523,10 @@ struct UpcRhoAnalysis { rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hTimeFDDA"), collision.timeFDDA()); rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hTimeFDDC"), collision.timeFDDC()); rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hOccupancyInTime"), collision.occupancyInTime()); + rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hRecoMode"), collision.flags()); + rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hkNoCollInTimeRangeStandard"), collision.trs()); + rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hkNoCollInRofStandard"), collision.trofs()); + rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hkNoHighMultCollInPrevRof"), collision.hmpr()); } template @@ -476,13 +536,10 @@ struct UpcRhoAnalysis { rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hEta"), eta(track.px(), track.py(), track.pz())); rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hPhi"), phi(track.px(), track.py())); rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTpcNSigmaPi"), track.tpcNSigmaPi()); - rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hPtVsEtaVsTpcNSigmaPi"), track.tpcNSigmaPi(), track.pt(), eta(track.px(), track.py(), track.pz())); rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTpcNSigmaEl"), track.tpcNSigmaEl()); - rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hPtVsEtaVsTpcNSigmaEl"), track.tpcNSigmaEl(), track.pt(), eta(track.px(), track.py(), track.pz())); rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTpcNSigmaKa"), track.tpcNSigmaKa()); - rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hPtVsEtaVsTpcNSigmaKa"), track.tpcNSigmaKa(), track.pt(), eta(track.px(), track.py(), track.pz())); rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTpcNSigmaPr"), track.tpcNSigmaPr()); - rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hPtVsEtaVsTpcNSigmaPr"), track.tpcNSigmaPr(), track.pt(), eta(track.px(), track.py(), track.pz())); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTpcNSigmaMu"), track.tpcNSigmaMu()); rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hDcaXYZ"), track.dcaZ(), track.dcaXY()); rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hItsNCls"), track.itsNCls()); rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hItsChi2NCl"), track.itsChi2NCl()); @@ -492,6 +549,18 @@ struct UpcRhoAnalysis { rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTpcNClsCrossedRowsOverNClsFindable"), (static_cast(track.tpcNClsCrossedRows()) / static_cast(track.tpcNClsFindable()))); rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTpcSignalVsP"), std::abs(momentum(track.px(), track.py(), track.pz())), track.tpcSignal()); rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTpcSignalVsPt"), track.pt(), track.tpcSignal()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hLength"), track.length()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTofChi2"), track.tofChi2()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTofSignalVsP"), std::abs(momentum(track.px(), track.py(), track.pz())), track.tofSignal()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTofSignalVsPt"), track.pt(), track.tofSignal()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTofExpMom"), track.tofExpMom()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hBeta"), track.beta()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hBetaError"), track.betaerror()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTofNSigmaPi"), track.tofNSigmaPi()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTofNSigmaEl"), track.tofNSigmaEl()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTofNSigmaKa"), track.tofNSigmaKa()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTofNSigmaPr"), track.tofNSigmaPr()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTofNSigmaMu"), track.tofNSigmaMu()); } template @@ -612,18 +681,11 @@ struct UpcRhoAnalysis { rQC.fill(HIST("QC/collisions/hSelectionCounterPerRun"), 8, runIndex); } - if (useRecoFlag) { - if (collision.flags() != cutRecoFlag) // check reconstruction mode - return false; - rQC.fill(HIST("QC/collisions/hSelectionCounter"), 9); - rQC.fill(HIST("QC/collisions/hSelectionCounterPerRun"), 9, runIndex); - } - if (cutOccupancy) { if (collision.occupancyInTime() > occupancyCut) return false; - rQC.fill(HIST("QC/collisions/hSelectionCounter"), 10); - rQC.fill(HIST("QC/collisions/hSelectionCounterPerRun"), 10, runIndex); + rQC.fill(HIST("QC/collisions/hSelectionCounter"), 9); + rQC.fill(HIST("QC/collisions/hSelectionCounterPerRun"), 9, runIndex); } // if all selections passed @@ -686,28 +748,21 @@ struct UpcRhoAnalysis { rQC.fill(HIST("QC/tracks/hSelectionCounter"), 10); rQC.fill(HIST("QC/tracks/hSelectionCounterPerRun"), 10, runIndex); - if (requireTof) { - if (!track.hasTOF()) - return false; - rQC.fill(HIST("QC/tracks/hSelectionCounter"), 11); - rQC.fill(HIST("QC/tracks/hSelectionCounterPerRun"), 11, runIndex); - } - if (track.pt() < tracksMinPtCut) return false; + rQC.fill(HIST("QC/tracks/hSelectionCounter"), 11); + rQC.fill(HIST("QC/tracks/hSelectionCounterPerRun"), 11, runIndex); + + if (std::abs(track.dcaZ()) > tracksDcaMaxCut || std::abs(track.dcaXY()) > (0.0105 + 0.0350 / std::pow(track.pt(), 1.1))) + return false; rQC.fill(HIST("QC/tracks/hSelectionCounter"), 12); rQC.fill(HIST("QC/tracks/hSelectionCounterPerRun"), 12, runIndex); - if (std::abs(track.dcaZ()) > tracksDcaMaxCut || std::abs(track.dcaXY()) > (0.0105 + 0.0350 / std::pow(track.pt(), 1.01))) + if (std::abs(eta(track.px(), track.py(), track.pz())) > pcEtaCut) return false; rQC.fill(HIST("QC/tracks/hSelectionCounter"), 13); rQC.fill(HIST("QC/tracks/hSelectionCounterPerRun"), 13, runIndex); - if (std::abs(eta(track.px(), track.py(), track.pz())) > pcEtaCut) - return false; - rQC.fill(HIST("QC/tracks/hSelectionCounter"), 14); - rQC.fill(HIST("QC/tracks/hSelectionCounterPerRun"), 14, runIndex); - // if all selections passed return true; } @@ -812,11 +867,11 @@ struct UpcRhoAnalysis { // function to obtain index of run from the run number vector // search for passed run number in the vector and return its index +1 to use in the filling of a histogram - int getRunIndex(int runNumber, const std::vector& runNumbers) + int getRunIndex(int runNumber, const std::vector& runNumberList) { - auto it = std::find(runNumbers.begin(), runNumbers.end(), runNumber); - if (it != runNumbers.end()) { - return std::distance(runNumbers.begin(), it) + 1; // +1 to avoid 0 bin in histogram + auto it = std::find(runNumberList.begin(), runNumberList.end(), runNumber); + if (it != runNumberList.end()) { + return std::distance(runNumberList.begin(), it) + 1; // +1 to avoid 0 bin in histogram } else { return 0; // return 0 if run number not found } @@ -836,8 +891,9 @@ struct UpcRhoAnalysis { nPVTracks++; } rQC.fill(HIST("QC/collisions/hNumContribVsPVTracks"), nPVTracks, collision.numContrib()); + rQC.fill(HIST("QC/collisions/hLocalBC"), collision.globalBC() % o2::constants::lhc::LHCMaxBunches); - fillCollisionQcHistos<0>(collision); // fill QC histograms before cuts + fillCollisionQcHistos<0>(collision, runIndex); // fill QC histograms before cuts if (!collisionPassesCuts(collision, runIndex)) // apply collision cuts return; @@ -881,16 +937,12 @@ struct UpcRhoAnalysis { } rQC.fill(HIST("QC/tracks/trackSelections/hRemainingTracks"), cutTracks.size()); - if (static_cast(cutTracks.size()) != nPions) // further consider only two pion systems + if (static_cast(cutTracks.size()) != nExpectedPions) // further consider only two pion systems return; - for (int i = 0; i < nPions; i++) { - rQC.fill(HIST("QC/tracks/hSelectionCounter"), 15); - rQC.fill(HIST("QC/tracks/hSelectionCounterPerRun"), 15, runIndex); + for (int i = 0; i < nExpectedPions; i++) { + rQC.fill(HIST("QC/tracks/hSelectionCounter"), 14); + rQC.fill(HIST("QC/tracks/hSelectionCounterPerRun"), 14, runIndex); } - rQC.fill(HIST("QC/tracks/trackSelections/hTpcNSigmaPi2D"), cutTracks[0].tpcNSigmaPi(), cutTracks[1].tpcNSigmaPi()); - rQC.fill(HIST("QC/tracks/trackSelections/hTpcNSigmaEl2D"), cutTracks[0].tpcNSigmaEl(), cutTracks[1].tpcNSigmaEl()); - rQC.fill(HIST("QC/tracks/trackSelections/hTpcNSigmaKa2D"), cutTracks[0].tpcNSigmaKa(), cutTracks[1].tpcNSigmaKa()); - rQC.fill(HIST("QC/tracks/trackSelections/hTpcNSigmaPr2D"), cutTracks[0].tpcNSigmaPr(), cutTracks[1].tpcNSigmaPr()); // create a vector of 4-vectors for selected tracks std::vector cutTracksLVs; @@ -902,6 +954,12 @@ struct UpcRhoAnalysis { auto leadingTrack = momentum(cutTracks[0].px(), cutTracks[0].py(), cutTracks[0].pz()) > momentum(cutTracks[1].px(), cutTracks[1].py(), cutTracks[1].pz()) ? cutTracks[0] : cutTracks[1]; auto subleadingTrack = (leadingTrack == cutTracks[0]) ? cutTracks[1] : cutTracks[0]; + rQC.fill(HIST("QC/tracks/trackSelections/hTpcNSigmaPi2D"), leadingTrack.tpcNSigmaPi(), subleadingTrack.tpcNSigmaPi()); + rQC.fill(HIST("QC/tracks/trackSelections/hTpcNSigmaEl2D"), leadingTrack.tpcNSigmaEl(), subleadingTrack.tpcNSigmaEl()); + rQC.fill(HIST("QC/tracks/trackSelections/hTpcNSigmaKa2D"), leadingTrack.tpcNSigmaKa(), subleadingTrack.tpcNSigmaKa()); + rQC.fill(HIST("QC/tracks/trackSelections/hTpcNSigmaPr2D"), leadingTrack.tpcNSigmaPr(), subleadingTrack.tpcNSigmaPr()); + rQC.fill(HIST("QC/tracks/trackSelections/hTpcNSigmaMu2D"), leadingTrack.tpcNSigmaMu(), subleadingTrack.tpcNSigmaMu()); + float leadingPt = leadingTrack.pt(); float subleadingPt = subleadingTrack.pt(); float leadingEta = eta(leadingTrack.px(), leadingTrack.py(), leadingTrack.pz()); @@ -911,8 +969,12 @@ struct UpcRhoAnalysis { float phiRandom = getPhiRandom(cutTracksLVs); float phiCharge = getPhiCharge(cutTracks, cutTracksLVs); + if (applyPid && !tracksPassPID(cutTracks)) // apply PID cut before creating derived data if desired + return; + // fill recoTree - recoTree(collision.flags(), collision.runNumber(), collision.posX(), collision.posY(), collision.posZ(), collision.occupancyInTime(), collision.hadronicRate(), collision.globalBC() % o2::constants::lhc::LHCMaxBunches, + recoTree(collision.flags(), collision.runNumber(), collision.posX(), collision.posY(), collision.posZ(), collision.occupancyInTime(), collision.hadronicRate(), collision.globalBC(), collision.globalBC() % o2::constants::lhc::LHCMaxBunches, + collision.trs(), collision.trofs(), collision.hmpr(), collision.totalFT0AmplitudeA(), collision.totalFT0AmplitudeC(), collision.totalFV0AmplitudeA(), collision.totalFDDAmplitudeA(), collision.totalFDDAmplitudeC(), collision.timeFT0A(), collision.timeFT0C(), collision.timeFV0A(), collision.timeFDDA(), collision.timeFDDC(), energyCommonZNA, energyCommonZNC, timeZNA, timeZNC, neutronClass, @@ -923,18 +985,20 @@ struct UpcRhoAnalysis { leadingTrack.tpcNSigmaPi(), subleadingTrack.tpcNSigmaPi(), leadingTrack.tpcNSigmaEl(), subleadingTrack.tpcNSigmaEl(), leadingTrack.tpcNSigmaKa(), subleadingTrack.tpcNSigmaKa(), - leadingTrack.tpcNSigmaPr(), subleadingTrack.tpcNSigmaPr()); + leadingTrack.tpcNSigmaPr(), subleadingTrack.tpcNSigmaPr(), + leadingTrack.hasTOF(), subleadingTrack.hasTOF()); if (!tracksPassPID(cutTracks)) // apply PID cut return; + rQC.fill(HIST("QC/tracks/trackSelections/hTpcNSigmaPi2DAfterCut"), leadingTrack.tpcNSigmaPi(), subleadingTrack.tpcNSigmaPi()); for (const auto& cutTrack : cutTracks) { - rQC.fill(HIST("QC/tracks/hSelectionCounter"), 16); - rQC.fill(HIST("QC/tracks/hSelectionCounterPerRun"), 16, runIndex); + rQC.fill(HIST("QC/tracks/hSelectionCounter"), 15); + rQC.fill(HIST("QC/tracks/hSelectionCounterPerRun"), 15, runIndex); fillTrackQcHistos<1>(cutTrack); // fill QC histograms after cuts } rQC.fill(HIST("QC/tracks/hTofHitCheck"), leadingTrack.hasTOF(), subleadingTrack.hasTOF()); - fillCollisionQcHistos<1>(collision); // fill QC histograms after track selections + fillCollisionQcHistos<1>(collision, runIndex); // fill QC histograms after track selections ROOT::Math::PxPyPzMVector system = reconstructSystem(cutTracksLVs); int totalCharge = tracksTotalCharge(cutTracks); @@ -972,7 +1036,7 @@ struct UpcRhoAnalysis { // fill histograms for system passing cuts switch (totalCharge) { case 0: - fillCollisionQcHistos<2>(collision); + fillCollisionQcHistos<2>(collision, runIndex); for (const auto& cutTrack : cutTracks) fillTrackQcHistos<2>(cutTrack); fillTrack2dHistos<2, 0>(leadingPt, subleadingPt, leadingEta, subleadingEta, leadingPhi, subleadingPhi); @@ -1066,7 +1130,7 @@ struct UpcRhoAnalysis { } rMC.fill(HIST("MC/collisions/hNPions"), cutMcParticles.size()); - if (static_cast(cutMcParticles.size()) != 2) + if (static_cast(cutMcParticles.size()) != nExpectedPions) // further consider only two pion systems return; if (mcParticlesLVs.size() != cutMcParticles.size()) // sanity check return; @@ -1098,6 +1162,8 @@ struct UpcRhoAnalysis { rMC.fill(HIST("MC/system/hPhiCharge"), phiCharge); rMC.fill(HIST("MC/system/hPhiRandomVsM"), mass, phiRandom); rMC.fill(HIST("MC/system/hPhiChargeVsM"), mass, phiCharge); + rMC.fill(HIST("MC/system/hPhiRandomVsPt"), pT, phiRandom); + rMC.fill(HIST("MC/system/hPhiChargeVsPt"), pT, phiCharge); if (systemPassesCuts(system)) { rMC.fill(HIST("MC/system/selected/hM"), mass); @@ -1113,7 +1179,7 @@ struct UpcRhoAnalysis { } // fill mcTree - mcTree(runNumber, mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), mcCollision.globalBC() % o2::constants::lhc::LHCMaxBunches, + mcTree(runNumber, mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), mcCollision.globalBC(), mcCollision.globalBC() % o2::constants::lhc::LHCMaxBunches, leadingPion.pdgCode() / std::abs(leadingPion.pdgCode()), subleadingPion.pdgCode() / std::abs(subleadingPion.pdgCode()), pt(leadingPion.px(), leadingPion.py()), pt(subleadingPion.px(), subleadingPion.py()), eta(leadingPion.px(), leadingPion.py(), leadingPion.pz()), eta(subleadingPion.px(), subleadingPion.py(), subleadingPion.pz()), @@ -1146,6 +1212,8 @@ struct UpcRhoAnalysis { void processDGdata(FullUdDgCollision const& collision, FullUdTracks const& tracks) { int runIndex = getRunIndex(collision.runNumber(), runNumbers); + rQC.fill(HIST("QC/collisions/hSelectionCounter"), 0); // all collisions + rQC.fill(HIST("QC/collisions/hSelectionCounterPerRun"), 0, runIndex); rQC.fill(HIST("QC/collisions/hSelectionCounter"), 1); // no single-gap collisions in dataset rQC.fill(HIST("QC/collisions/hSelectionCounterPerRun"), 1, runIndex); @@ -1202,7 +1270,7 @@ struct UpcRhoAnalysis { recoTracks.push_back(track); } - if (truePionLVs.size() != 2 || recoPionLVs.size() != 2) + if (static_cast(truePionLVs.size()) != nExpectedPions || static_cast(recoPionLVs.size()) != nExpectedPions) return; ROOT::Math::PxPyPzMVector trueSystem = reconstructSystem(truePionLVs); @@ -1216,6 +1284,8 @@ struct UpcRhoAnalysis { rResolution.fill(HIST("MC/resolution/system/2D/hPtVsPt"), trueSystem.Pt(), recoSystem.Pt()); rResolution.fill(HIST("MC/resolution/system/1D/hY"), recoSystem.Rapidity() - trueSystem.Rapidity()); rResolution.fill(HIST("MC/resolution/system/2D/hYVsY"), trueSystem.Rapidity(), recoSystem.Rapidity()); + rResolution.fill(HIST("MC/resolution/system/1D/hPhi"), phi(recoSystem.Px(), recoSystem.Py()) - phi(trueSystem.Px(), trueSystem.Py())); + rResolution.fill(HIST("MC/resolution/system/2D/hPhiVsPhi"), phi(trueSystem.Px(), trueSystem.Py()), phi(recoSystem.Px(), recoSystem.Py())); rResolution.fill(HIST("MC/resolution/system/1D/hDeltaPhi"), recoDeltaPhi - trueDeltaPhi); rResolution.fill(HIST("MC/resolution/system/2D/hDeltaPhiVsDeltaPhi"), trueDeltaPhi, recoDeltaPhi); @@ -1224,15 +1294,65 @@ struct UpcRhoAnalysis { auto leadingRecoPion = momentum(recoTracks[0].px(), recoTracks[0].py(), recoTracks[0].pz()) > momentum(recoTracks[1].px(), recoTracks[1].py(), recoTracks[1].pz()) ? recoTracks[0] : recoTracks[1]; auto subleadingRecoPion = (leadingRecoPion == recoTracks[0]) ? recoTracks[1] : recoTracks[0]; - resolutionTree(mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), + resolutionTree(mcCollision.globalBC(), mcCollision.globalBC() % o2::constants::lhc::LHCMaxBunches, + collision.runNumber(), + collision.globalBC(), collision.globalBC() % o2::constants::lhc::LHCMaxBunches, + mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), collision.posX(), collision.posY(), collision.posZ(), + collision.totalFT0AmplitudeA(), collision.totalFT0AmplitudeC(), collision.totalFV0AmplitudeA(), collision.totalFDDAmplitudeA(), collision.totalFDDAmplitudeC(), + collision.timeFT0A(), collision.timeFT0C(), collision.timeFV0A(), collision.timeFDDA(), collision.timeFDDC(), leadingTruePion.pdgCode() / std::abs(leadingTruePion.pdgCode()), pt(leadingTruePion.px(), leadingTruePion.py()), eta(leadingTruePion.px(), leadingTruePion.py(), leadingTruePion.pz()), phi(leadingTruePion.px(), leadingTruePion.py()), - pt(leadingRecoPion.px(), leadingRecoPion.py()), eta(leadingRecoPion.px(), leadingRecoPion.py(), leadingRecoPion.pz()), phi(leadingRecoPion.px(), leadingRecoPion.py()), + pt(leadingRecoPion.px(), leadingRecoPion.py()), eta(leadingRecoPion.px(), leadingRecoPion.py(), leadingRecoPion.pz()), phi(leadingRecoPion.px(), leadingRecoPion.py()), leadingRecoPion.hasTOF(), subleadingTruePion.pdgCode() / std::abs(subleadingTruePion.pdgCode()), pt(subleadingTruePion.px(), subleadingTruePion.py()), eta(subleadingTruePion.px(), subleadingTruePion.py(), subleadingTruePion.pz()), phi(subleadingTruePion.px(), subleadingTruePion.py()), - pt(subleadingRecoPion.px(), subleadingRecoPion.py()), eta(subleadingRecoPion.px(), subleadingRecoPion.py(), subleadingRecoPion.pz()), phi(subleadingRecoPion.px(), subleadingRecoPion.py())); + pt(subleadingRecoPion.px(), subleadingRecoPion.py()), eta(subleadingRecoPion.px(), subleadingRecoPion.py(), subleadingRecoPion.pz()), phi(subleadingRecoPion.px(), subleadingRecoPion.py()), subleadingRecoPion.hasTOF()); } PROCESS_SWITCH(UpcRhoAnalysis, processResolution, "check resolution of kinematic variables", false); + void processMcRecoWithTruth(soa::Join::iterator const& collision, soa::Join const& tracks, aod::UDMcCollisions const&, aod::UDMcParticles const&) + { + // basically just runs the analysis as for normal data but also accesses the MC truth information for the tracks and collisions + auto mcCollision = collision.udMcCollision(); + const int runIndex = -1; // we don't care here + if (cutGapSide && collision.gapSide() != gapSide) + return; + if (!collisionPassesCuts(collision, runIndex)) // apply collision cuts + return; + + std::vector recoTracks; // store selected tracks + std::vector trueTracks; + for (const auto& track : tracks) { + fillTrackQcHistos<0>(track); // fill QC histograms before cuts + + if (!trackPassesCuts(track, runIndex)) // apply track cuts + continue; + recoTracks.push_back(track); + if (track.has_udMcParticle()) + trueTracks.push_back(track.udMcParticle()); + } + if (static_cast(recoTracks.size()) != nExpectedPions || static_cast(trueTracks.size()) != nExpectedPions) // further consider only two pion systems + return; + if (applyPid && !tracksPassPID(recoTracks)) // apply PID cut before creating derived data if desired + return; + + auto leadingTruePion = momentum(trueTracks[0].px(), trueTracks[0].py(), trueTracks[0].pz()) > momentum(trueTracks[1].px(), trueTracks[1].py(), trueTracks[1].pz()) ? trueTracks[0] : trueTracks[1]; + auto subleadingTruePion = (leadingTruePion == trueTracks[0]) ? trueTracks[1] : trueTracks[0]; + auto leadingRecoPion = momentum(recoTracks[0].px(), recoTracks[0].py(), recoTracks[0].pz()) > momentum(recoTracks[1].px(), recoTracks[1].py(), recoTracks[1].pz()) ? recoTracks[0] : recoTracks[1]; + auto subleadingRecoPion = (leadingRecoPion == recoTracks[0]) ? recoTracks[1] : recoTracks[0]; + + resolutionTree(mcCollision.globalBC(), mcCollision.globalBC() % o2::constants::lhc::LHCMaxBunches, + collision.runNumber(), + collision.globalBC(), collision.globalBC() % o2::constants::lhc::LHCMaxBunches, + mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), + collision.posX(), collision.posY(), collision.posZ(), + collision.totalFT0AmplitudeA(), collision.totalFT0AmplitudeC(), collision.totalFV0AmplitudeA(), collision.totalFDDAmplitudeA(), collision.totalFDDAmplitudeC(), + collision.timeFT0A(), collision.timeFT0C(), collision.timeFV0A(), collision.timeFDDA(), collision.timeFDDC(), + leadingTruePion.pdgCode() / std::abs(leadingTruePion.pdgCode()), pt(leadingTruePion.px(), leadingTruePion.py()), eta(leadingTruePion.px(), leadingTruePion.py(), leadingTruePion.pz()), phi(leadingTruePion.px(), leadingTruePion.py()), + pt(leadingRecoPion.px(), leadingRecoPion.py()), eta(leadingRecoPion.px(), leadingRecoPion.py(), leadingRecoPion.pz()), phi(leadingRecoPion.px(), leadingRecoPion.py()), leadingRecoPion.hasTOF(), + subleadingTruePion.pdgCode() / std::abs(subleadingTruePion.pdgCode()), pt(subleadingTruePion.px(), subleadingTruePion.py()), eta(subleadingTruePion.px(), subleadingTruePion.py(), subleadingTruePion.pz()), phi(subleadingTruePion.px(), subleadingTruePion.py()), + pt(subleadingRecoPion.px(), subleadingRecoPion.py()), eta(subleadingRecoPion.px(), subleadingRecoPion.py(), subleadingRecoPion.pz()), phi(subleadingRecoPion.px(), subleadingRecoPion.py()), subleadingRecoPion.hasTOF()); + } + PROCESS_SWITCH(UpcRhoAnalysis, processMcRecoWithTruth, "process MC reco with access to MC truth", false); + void processCollisionRecoCheck(aod::UDMcCollision const& /* mcCollision */, soa::SmallGroups> const& collisions) { checkNumberOfCollisionReconstructions(collisions); diff --git a/PWGUD/Tasks/upcRhoPrimeAnalysis.cxx b/PWGUD/Tasks/upcRhoPrimeAnalysis.cxx index 703f0989283..b8bfdcb6d81 100644 --- a/PWGUD/Tasks/upcRhoPrimeAnalysis.cxx +++ b/PWGUD/Tasks/upcRhoPrimeAnalysis.cxx @@ -9,10 +9,10 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. /// -/// \brief Task for analysis of rho' in UPCs using UD tables (from SG producer). -/// \author Cesar Ramirez, cesar.ramirez@cern.ch +/// \file upcRhoPrimeAnalysis.cxx +/// \brief Task for analysis of rho prime in UPCs using UD tables (from SG producer). +/// \author Cesar Omar Ramirez Alvarez (cesar.ramirez@cern.ch), Autonomous University of Puebla -#include "PWGUD/Core/UPCTauCentralBarrelHelperRL.h" #include "PWGUD/DataModel/UDTables.h" #include @@ -26,62 +26,66 @@ #include #include -#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include // IWYU pragma: keep #include #include +#include #include #include #include -#include -#include #include using namespace o2; using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace ROOT::Math; // Define UD tables using UDtracks = soa::Join; -using UDCollisions = soa::Join; +using UDCollisions = soa::Join; +using UDMcCollisions = aod::UDMcCollisions; +using UDMcParticles = aod::UDMcParticles; +using UDtracksMC = soa::Join; +using UDCollisionsMC = soa::Join; namespace o2::aod { namespace fourpi { -// Declare columns -DECLARE_SOA_COLUMN(RunNumber, runNumber, int32_t); // Run number for event identification -DECLARE_SOA_COLUMN(M, m, double); // Invariant mass of the system -DECLARE_SOA_COLUMN(Pt, pt, double); // Transverse momentum of the system -DECLARE_SOA_COLUMN(Eta, eta, double); // Pseudorapidity of the system -DECLARE_SOA_COLUMN(Phi, phi, double); // Azimuthal angle of the system +// Columns RD / MC reco +DECLARE_SOA_COLUMN(RunNumber, runNumber, int32_t); // Run number +DECLARE_SOA_COLUMN(M, m, double); // System invariant mass +DECLARE_SOA_COLUMN(Pt, pt, double); // System pT +DECLARE_SOA_COLUMN(Eta, eta, double); // System pseudorapidity +DECLARE_SOA_COLUMN(Phi, phi, double); // System azimuthal angle DECLARE_SOA_COLUMN(PosX, posX, double); // Vertex X position DECLARE_SOA_COLUMN(PosY, posY, double); // Vertex Y position DECLARE_SOA_COLUMN(PosZ, posZ, double); // Vertex Z position -DECLARE_SOA_COLUMN(TotalCharge, totalCharge, int); // Total charge of selected tracks +DECLARE_SOA_COLUMN(TotalCharge, totalCharge, int); // Real total charge of the 4 selected tracks DECLARE_SOA_COLUMN(TotalFT0AmplitudeA, totalFT0AmplitudeA, float); // FT0A amplitude DECLARE_SOA_COLUMN(TotalFT0AmplitudeC, totalFT0AmplitudeC, float); // FT0C amplitude DECLARE_SOA_COLUMN(TotalFV0AmplitudeA, totalFV0AmplitudeA, float); // FV0A amplitude -DECLARE_SOA_COLUMN(NumContrib, numContrib, int32_t); // Number of primary vertex contributors +DECLARE_SOA_COLUMN(NumContrib, numContrib, int32_t); // Number of PV contributors DECLARE_SOA_COLUMN(Sign, sign, std::vector); // Track charges -DECLARE_SOA_COLUMN(TrackPt, trackPt, std::vector); // Track pT values -DECLARE_SOA_COLUMN(TrackEta, trackEta, std::vector); // Track eta values -DECLARE_SOA_COLUMN(TrackPhi, trackPhi, std::vector); // Track phi values -DECLARE_SOA_COLUMN(TPCNSigmaEl, tpcNSigmaEl, std::vector); // TPC nσ for electrons -DECLARE_SOA_COLUMN(TPCNSigmaPi, tpcNSigmaPi, std::vector); // TPC nσ for pions -DECLARE_SOA_COLUMN(TPCNSigmaKa, tpcNSigmaKa, std::vector); // TPC nσ for kaons -DECLARE_SOA_COLUMN(TPCNSigmaPr, tpcNSigmaPr, std::vector); // TPC nσ for protons -DECLARE_SOA_COLUMN(TrackID, trackID, std::vector); // Track identifiers -DECLARE_SOA_COLUMN(IsReconstructedWithUPC, isReconstructedWithUPC, bool); // UPC mode reconstruction flag -DECLARE_SOA_COLUMN(TimeZNA, timeZNA, float); // ZNA timing -DECLARE_SOA_COLUMN(TimeZNC, timeZNC, float); // ZNC timing +DECLARE_SOA_COLUMN(TrackPt, trackPt, std::vector); // Track pT +DECLARE_SOA_COLUMN(TrackEta, trackEta, std::vector); // Track eta +DECLARE_SOA_COLUMN(TrackPhi, trackPhi, std::vector); // Track phi +DECLARE_SOA_COLUMN(TPCNSigmaEl, tpcNSigmaEl, std::vector); // TPC nSigma electron +DECLARE_SOA_COLUMN(TPCNSigmaPi, tpcNSigmaPi, std::vector); // TPC nSigma pion +DECLARE_SOA_COLUMN(TPCNSigmaKa, tpcNSigmaKa, std::vector); // TPC nSigma kaon +DECLARE_SOA_COLUMN(TPCNSigmaPr, tpcNSigmaPr, std::vector); // TPC nSigma proton +DECLARE_SOA_COLUMN(TrackID, trackID, std::vector); // Track index within the system +DECLARE_SOA_COLUMN(IsReconstructedWithUPC, isReconstructedWithUPC, bool); // UPC reconstruction mode flag +DECLARE_SOA_COLUMN(TimeZNA, timeZNA, float); // ZNA time +DECLARE_SOA_COLUMN(TimeZNC, timeZNC, float); // ZNC time DECLARE_SOA_COLUMN(EnergyCommonZNA, energyCommonZNA, float); // ZNA energy DECLARE_SOA_COLUMN(EnergyCommonZNC, energyCommonZNC, float); // ZNC energy -DECLARE_SOA_COLUMN(IsChargeZero, isChargeZero, bool); // Neutral system flag -DECLARE_SOA_COLUMN(OccupancyInTime, occupancyInTime, int); // Occupancy in time +DECLARE_SOA_COLUMN(IsChargeZero, isChargeZero, bool); // TotalCharge == 0 +DECLARE_SOA_COLUMN(OccupancyInTime, occupancyInTime, int); // Occupancy DECLARE_SOA_COLUMN(HadronicRate, hadronicRate, double); // Hadronic interaction rate } // namespace fourpi -// Define the output DECLARE_SOA_TABLE(SYSTEMTREE, "AOD", "SystemTree", fourpi::RunNumber, fourpi::M, fourpi::Pt, fourpi::Eta, fourpi::Phi, fourpi::PosX, fourpi::PosY, fourpi::PosZ, fourpi::TotalCharge, @@ -92,10 +96,63 @@ DECLARE_SOA_TABLE(SYSTEMTREE, "AOD", "SystemTree", fourpi::TrackID, fourpi::IsReconstructedWithUPC, fourpi::TimeZNA, fourpi::TimeZNC, fourpi::EnergyCommonZNA, fourpi::EnergyCommonZNC, fourpi::IsChargeZero, fourpi::OccupancyInTime, fourpi::HadronicRate); + +namespace mcgen4pi +{ +// Columns MC gen +DECLARE_SOA_COLUMN(McMotherPdg, mcMotherPdg, int); // Common mother PDG +DECLARE_SOA_COLUMN(McMotherPt, mcMotherPt, float); // Mother pT +DECLARE_SOA_COLUMN(McMotherPhi, mcMotherPhi, float); // Mother phi +DECLARE_SOA_COLUMN(McMotherMass, mcMotherMass, float); // Mother invariant mass +DECLARE_SOA_COLUMN(McMotherRapidity, mcMotherRapidity, float); // Mother rapidity +DECLARE_SOA_COLUMN(McTotalCharge, mcTotalCharge, int); // Real total charge of the 4 generated particles + +// Per-particle info +DECLARE_SOA_COLUMN(McTrackPdg, mcTrackPdg, int[4]); +DECLARE_SOA_COLUMN(McTrackPt, mcTrackPt, float[4]); +DECLARE_SOA_COLUMN(McTrackEta, mcTrackEta, float[4]); +DECLARE_SOA_COLUMN(McTrackPhi, mcTrackPhi, float[4]); +DECLARE_SOA_COLUMN(McTrackSign, mcTrackSign, int[4]); +DECLARE_SOA_COLUMN(McTrackIsPrimary, mcTrackIsPrimary, int[4]); + +// Generated vertex +DECLARE_SOA_COLUMN(McPosX, mcPosX, float); +DECLARE_SOA_COLUMN(McPosY, mcPosY, float); +DECLARE_SOA_COLUMN(McPosZ, mcPosZ, float); + +// Generated collision index +DECLARE_SOA_COLUMN(McCollisionIndex, mcCollisionIndex, int); + +// Real run number for MC gen +DECLARE_SOA_COLUMN(McRunNumber, mcRunNumber, int); + +DECLARE_SOA_COLUMN(RecoIndex, recoIndex, int); +} // namespace mcgen4pi + +// MC reco<->gen match table +DECLARE_SOA_TABLE(FourPiMcMatchTree, "AOD", "FOURPIMCMATCH", + fourpi::IsReconstructedWithUPC, + mcgen4pi::McMotherPdg, mcgen4pi::McMotherPt, mcgen4pi::McMotherPhi, + mcgen4pi::McMotherMass, mcgen4pi::McMotherRapidity, mcgen4pi::McTotalCharge, + mcgen4pi::McTrackPdg, mcgen4pi::McTrackPt, mcgen4pi::McTrackEta, mcgen4pi::McTrackPhi, + mcgen4pi::McTrackSign, mcgen4pi::McTrackIsPrimary, + mcgen4pi::McPosX, mcgen4pi::McPosY, mcgen4pi::McPosZ, mcgen4pi::McCollisionIndex, + mcgen4pi::McRunNumber, mcgen4pi::RecoIndex); + +// MC All generated collisions +DECLARE_SOA_TABLE(FourPiMcGenAllTree, "AOD", "FOURPIMCGALL", + mcgen4pi::McMotherPdg, mcgen4pi::McMotherPt, mcgen4pi::McMotherPhi, + mcgen4pi::McMotherMass, mcgen4pi::McMotherRapidity, mcgen4pi::McTotalCharge, + mcgen4pi::McTrackPdg, mcgen4pi::McTrackPt, mcgen4pi::McTrackEta, mcgen4pi::McTrackPhi, + mcgen4pi::McTrackSign, mcgen4pi::McTrackIsPrimary, + mcgen4pi::McPosX, mcgen4pi::McPosY, mcgen4pi::McPosZ, mcgen4pi::McCollisionIndex, + mcgen4pi::McRunNumber); } // namespace o2::aod struct upcRhoPrimeAnalysis { Produces systemTree; + Produces fourPiMcMatchTree; + Produces fourPiMcGenAllTree; // System selection configuration Configurable systemYCut{"systemYCut", 0.5, "Max Rapidity of rho prime"}; @@ -127,11 +184,16 @@ struct upcRhoPrimeAnalysis { Configurable dcaZcut{"dcaZcut", 2, "dcaZ cut"}; Configurable minTPCFindableClusters{"minTPCFindableClusters", 70, "Minimum number of findable TPC clusters"}; - // Define histogram registry + // Optional generatorId filter + Configurable genId{"genId", -1, "generator ID; -1 = no filter"}; + + // Define histogram registry for RD / MC reco HistogramRegistry registry{ "registry", {// Event flow histograms - {"Events/Flow", "Event flow;Cut;Counts", {HistType::kTH1F, {{9, 0, 9}}}}, + {"Events/Flow", "Event flow;Cut;Counts", {HistType::kTH1D, {{11, 0, 11}}}}, + {"Events/FlowDetailed", "Detailed event flow;Cut;Counts", {HistType::kTH1D, {{13, 0, 13}}}}, + {"Events/hRecoMode", "Reconstruction mode;;Counts", {HistType::kTH1D, {{2, 0, 2}}}}, {"Events/VertexZ", "Vertex Z;z (cm);Counts", {HistType::kTH1F, {{200, -20, 20}}}}, {"Events/NumContrib", "Number of contributors;N_{contrib};Counts", {HistType::kTH1F, {{100, 0, 100}}}}, {"Events/FV0Amplitude", "FV0 amplitude;Amplitude;Counts", {HistType::kTH1F, {{200, 0, 200}}}}, @@ -145,23 +207,148 @@ struct upcRhoPrimeAnalysis { {"Tracks/TPCNSigmaPi", "TPC n#sigma for #pi;n#sigma;Counts", {HistType::kTH1F, {{200, -10, 10}}}}, {"Tracks/TPCChi2NCl", "TPC #chi^{2}/N_{cls};#chi^{2}/N_{cls};Counts", {HistType::kTH1F, {{200, 0, 20}}}}, {"Tracks/ITSChi2NCl", "ITS #chi^{2}/N_{cls};#chi^{2}/N_{cls};Counts", {HistType::kTH1F, {{200, 0, 50}}}}, - {"Tracks/RejectionReasons", "Track rejection reasons;Reason;Counts", {HistType::kTH1F, {{12, 0, 12}}}}, + {"Tracks/RejectionReasons", "Track rejection reasons;Reason;Counts", {HistType::kTH1F, {{15, 0, 15}}}}, {"Tracks/DCASpectrum", "Track DCA spectrum;DCA (cm);Counts", {HistType::kTH1F, {{100, 0, 5}}}}, {"Tracks/ChargeDistribution", "Track charge distribution;Charge;Counts", {HistType::kTH1F, {{3, -1.5, 1.5}}}}, {"Tracks/TPCClusters", "TPC clusters findable;N_{clusters};Counts", {HistType::kTH1F, {{100, 0, 200}}}}, + {"Tracks/NGoodTracksPerEvent", "Good tracks per event (before the ==4 cut);N;Counts", {HistType::kTH1F, {{11, -0.5, 10.5}}}}, // System kinematics histograms {"System/hM", ";m (GeV/#it{c}^{2});counts", {HistType::kTH1F, {{1000, 0.0, 10.0}}}}, - {"System/hPt", ";p_{T} (GeV/#it{c});counts", {HistType::kTH1F, {{1000, 0.0, 10.0}}}}, + {"System/hPt", ";p_{T} (GeV/#it{c});counts", {HistType::kTH1F, {{1000, 0.0, 1.1}}}}, {"System/hEta", ";#eta;counts", {HistType::kTH1F, {{180, -0.9, 0.9}}}}, {"System/hPhi", ";#phi;counts", {HistType::kTH1F, {{180, 0.0, 6.28}}}}, {"System/hY", ";y;counts", {HistType::kTH1F, {{180, -0.9, 0.9}}}}, + {"System/hTotalChargeBefore", "Total charge before M/Pt/Y cuts;Q;counts", {HistType::kTH1F, {{9, -4.5, 4.5}}}}, + // -4=----, -2=---+ (3-,1+), 0=+-+- (2+,2-), +2=+++- (3+,1-), +4=++++ + {"System/hTotalCharge", "System total charge (4 tracks, after M/Pt/Y cuts);Q;counts", {HistType::kTH1F, {{9, -4.5, 4.5}}}}, + {"System/hMVsTotalChargeBefore", "Invariant mass vs charge combination (before M/Pt/Y cuts);m (GeV/#it{c}^{2});Q", {HistType::kTH2F, {{1000, 0.0, 10.0}, {9, -4.5, 4.5}}}}, + {"System/hMVsTotalCharge", "Invariant mass vs charge combination (after M/Pt/Y cuts);m (GeV/#it{c}^{2});Q", {HistType::kTH2F, {{1000, 0.0, 10.0}, {9, -4.5, 4.5}}}}, // Comparison histograms {"Cuts/MBefore", "Mass before cuts;m (GeV/c^{2});Counts", {HistType::kTH1F, {{1000, 0, 10}}}}, {"Cuts/MAfter", "Mass after cuts;m (GeV/c^{2});Counts", {HistType::kTH1F, {{1000, 0, 10}}}}, - {"Cuts/PtBefore", "p_{T} before cuts;p_{T} (GeV/c);Counts", {HistType::kTH1F, {{1000, 0, 1}}}}, - {"Cuts/PtAfter", "p_{T} after cuts;p_{T} (GeV/c);Counts", {HistType::kTH1F, {{1000, 0, 10}}}}}}; + {"Cuts/PtBefore", "p_{T} before cuts;p_{T} (GeV/c);Counts", {HistType::kTH1F, {{1000, 0, 1.1}}}}, + {"Cuts/PtAfter", "p_{T} after cuts;p_{T} (GeV/c);Counts", {HistType::kTH1F, {{1000, 0, 1.1}}}}}}; + + // Define histogram registry for MC analysis + HistogramRegistry mcRegistry{ + "mcRegistry", + {// Event-level MC histograms + {"MC/Events/hAllEvents", "All MC Events", {HistType::kTH1F, {{1, 0, 1}}}}, + {"MC/Events/hAccepted", "Accepted MC Events", {HistType::kTH1F, {{1, 0, 1}}}}, + {"MC/Events/hVertexZ", "MC Vertex Z;z (cm);Counts", {HistType::kTH1F, {{400, -20.0, 20.0}}}}, + {"MC/Events/hNPrimaries", "Number of primary particles;N;Counts", {HistType::kTH1F, {{10, -0.5, 9.5}}}}, + + // Track-level MC histograms + {"MC/Tracks/hPt", "MC Track p_{T};p_{T} (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0, 2.0}}}}, + {"MC/Tracks/hEta", "MC Track #eta;#eta;Counts", {HistType::kTH1F, {{200, -2.0, 2.0}}}}, + {"MC/Tracks/hPhi", "MC Track #phi;#phi;Counts", {HistType::kTH1F, {{200, 0.0, 6.28}}}}, + {"MC/Tracks/hPdgCode", "PDG codes;PDG code;Counts", {HistType::kTH1F, {{2000, -1000, 1000}}}}, + + // System-level (mother) MC histograms + {"MC/System/hM", "MC Invariant Mass;m (GeV/c^{2});Counts", {HistType::kTH1F, {{1000, 0.0, 10.0}}}}, + {"MC/System/hPt", "MC p_{T};p_{T} (GeV/c);Counts", {HistType::kTH1F, {{1000, 0.0, 1.1}}}}, + {"MC/System/hY", "MC Rapidity;y;Counts", {HistType::kTH1F, {{180, -0.9, 0.9}}}}, + {"MC/System/hMvsPt", "MC Mass vs p_{T};m (GeV/c^{2});p_{T} (GeV/c)", {HistType::kTH2F, {{1000, 0.0, 10.0}, {1000, 0.0, 1.1}}}}, + {"MC/System/hMvsY", "MC Mass vs Rapidity;m (GeV/c^{2});y", {HistType::kTH2F, {{1000, 0.0, 10.0}, {200, -2.0, 2.0}}}}, + {"MC/System/hTotalCharge", "Generated total charge;Q;Counts", {HistType::kTH1F, {{9, -4.5, 4.5}}}}, + + // Basic summary + {"MC/Summary/hEventCounter", "Generated vs reconstructed summary;;Counts", {HistType::kTH1D, {{2, 0, 2}}}}, + {"MC/Summary/hMatchStatus", "Reco<->Gen match status;;Counts", {HistType::kTH1D, {{3, 0, 3}}}}, + {"MC/Summary/hRecoMode", "Reconstruction mode;;Counts", {HistType::kTH1D, {{2, 0, 2}}}}, + {"MC/Summary/hRecoTotalCharge", "Reco total charge (MC events, after M/Pt/Y cuts);Q;Counts", {HistType::kTH1D, {{9, -4.5, 4.5}}}}, + + // Rho prime specific histograms + {"MC/RhoPrime/hFound", "Rho Prime Found;Found;Counts", {HistType::kTH1F, {{2, -0.5, 1.5}}}}, + {"MC/RhoPrime/hMass", "Rho Prime Mass;m (GeV/c^{2});Counts", {HistType::kTH1F, {{1000, 0.0, 10.0}}}}, + {"MC/RhoPrime/hMassUPC", "Rho Prime Mass UPC;m (GeV/c^{2});Counts", {HistType::kTH1F, {{1000, 0.0, 10.0}}}}, + {"MC/RhoPrime/hMassSTD", "Rho Prime Mass STD;m (GeV/c^{2});Counts", {HistType::kTH1F, {{1000, 0.0, 10.0}}}}, + {"MC/RhoPrime/hPt", "Rho Prime p_{T};p_{T} (GeV/c);Counts", {HistType::kTH1F, {{1000, 0.0, 1.1}}}}, + + // Control histograms + {"MC/Control/hNDaughtersOfMother", "Number of daughters of the common mother;N;Counts", {HistType::kTH1F, {{10, 0, 10}}}}, + {"MC/Control/hMotherPdg", "PDG of the common mother found;PDG;Counts", {HistType::kTH1F, {{200, 0, 40000}}}}, + {"MC/Control/hTracksWithMcParticle", "Reco tracks with an associated mcParticle (of 4);N;Counts", {HistType::kTH1F, {{5, -0.5, 4.5}}}}, + + // MC reco<->gen match histograms + {"MC/Match/hRecoEvents", "Reco events entering the match check;;Counts", {HistType::kTH1F, {{1, 0, 1}}}}, + {"MC/Match/hMatchedGenM", "Matched candidates - generated M;m_{gen} (GeV/c^{2});Counts", {HistType::kTH1F, {{1000, 0.0, 10.0}}}}, + {"MC/Match/hMatchedGenPt", "Matched candidates - generated p_{T};p_{T,gen} (GeV/c);Counts", {HistType::kTH1F, {{1000, 0.0, 1.1}}}}, + {"MC/Match/hMatchedGenY", "Matched candidates - generated y;y_{gen};Counts", {HistType::kTH1F, {{180, -0.9, 0.9}}}}, + {"MC/Match/hRecoVsGenM", "Reco vs Gen M;m_{gen} (GeV/c^{2});m_{reco} (GeV/c^{2})", {HistType::kTH2F, {{500, 0.0, 5.0}, {500, 0.0, 5.0}}}}}}; + + // Helper functions for kinematic calculations + static float pt(float px, float py) { return std::sqrt(px * px + py * py); } + + static float eta(float px, float py, float pz) + { + if (std::abs(pz) > 1e-10) { + float p = std::sqrt(px * px + py * py + pz * pz); + return 0.5f * std::log((p + pz) / (p - pz)); + } + return 0.0f; + } + + static float phi(float px, float py) + { + if (std::abs(px) > 1e-10 || std::abs(py) > 1e-10) { + return std::atan2(py, px); + } + return 0.0f; + } + + // Generic "common mother" + struct MotherInfo { + bool found = false; + int pdg = 0; + float px = 0, py = 0; + }; + + // 4 particles share the same direct mother + template + MotherInfo findCommonMotherGeneric(const std::vector& daughters) + { + MotherInfo info; + if (daughters.empty() || !daughters[0].has_mothers()) { + return info; + } + auto firstMothers = daughters[0].template mothers_as(); + if (firstMothers.begin() == firstMothers.end()) { + return info; + } + auto motherIt = firstMothers.begin(); + int64_t motherGlobalIndex = motherIt->globalIndex(); + for (size_t i = 1; i < daughters.size(); i++) { + if (!daughters[i].has_mothers()) { + return info; + } + auto iMothers = daughters[i].template mothers_as(); + if (iMothers.begin() == iMothers.end() || iMothers.begin()->globalIndex() != motherGlobalIndex) { + return info; + } + } + const auto& mother = *motherIt; + info.found = true; + info.pdg = mother.pdgCode(); + info.px = mother.px(); + info.py = mother.py(); + return info; + } + + // Charge sign from PDG code + static int signFromPdg(int pdgCode) + { + if (pdgCode == 211) { + return 1; + } + if (pdgCode == -211) { + return -1; + } + return (pdgCode > 0) ? 1 : (pdgCode < 0) ? -1 + : 0; + } void init(InitContext&) { @@ -173,32 +360,135 @@ struct upcRhoPrimeAnalysis { hFlow->GetXaxis()->SetBinLabel(4, "ITS ROFb cut"); hFlow->GetXaxis()->SetBinLabel(5, "TFB cut"); hFlow->GetXaxis()->SetBinLabel(6, "Gap Side cut"); - hFlow->GetXaxis()->SetBinLabel(7, "PV contrib cut"); - hFlow->GetXaxis()->SetBinLabel(8, "Z vtx cut"); - hFlow->GetXaxis()->SetBinLabel(9, "4 tracks cut"); + hFlow->GetXaxis()->SetBinLabel(7, "ZDC energy cut"); + hFlow->GetXaxis()->SetBinLabel(8, "PV contrib cut"); + hFlow->GetXaxis()->SetBinLabel(9, "Z vtx cut"); + hFlow->GetXaxis()->SetBinLabel(10, "4 tracks cut"); + hFlow->GetXaxis()->SetBinLabel(11, "System cuts (M,Pt,Y)"); + + auto hFlowDetailed = registry.get(HIST("Events/FlowDetailed")); + hFlowDetailed->GetXaxis()->SetBinLabel(1, "All events"); + hFlowDetailed->GetXaxis()->SetBinLabel(2, "vtxITSTPC"); + hFlowDetailed->GetXaxis()->SetBinLabel(3, "sbp"); + hFlowDetailed->GetXaxis()->SetBinLabel(4, "itsROFb"); + hFlowDetailed->GetXaxis()->SetBinLabel(5, "tfb"); + hFlowDetailed->GetXaxis()->SetBinLabel(6, "gapSide"); + hFlowDetailed->GetXaxis()->SetBinLabel(7, "FV0 < cut"); + hFlowDetailed->GetXaxis()->SetBinLabel(8, "FT0A < cut"); + hFlowDetailed->GetXaxis()->SetBinLabel(9, "FT0C < cut"); + hFlowDetailed->GetXaxis()->SetBinLabel(10, "ZDC energy < cut"); + hFlowDetailed->GetXaxis()->SetBinLabel(11, "numContrib == 4"); + hFlowDetailed->GetXaxis()->SetBinLabel(12, "posZ < cut"); + hFlowDetailed->GetXaxis()->SetBinLabel(13, "4 tracks"); + + // Readable labels for Events/hRecoMode + auto hRecoMode = registry.get(HIST("Events/hRecoMode")); + hRecoMode->GetXaxis()->SetBinLabel(1, "STD"); + hRecoMode->GetXaxis()->SetBinLabel(2, "UPC"); + + auto hChargeBefore = registry.get(HIST("System/hTotalChargeBefore")); + hChargeBefore->GetXaxis()->SetBinLabel(1, "----"); + hChargeBefore->GetXaxis()->SetBinLabel(3, "---+"); + hChargeBefore->GetXaxis()->SetBinLabel(5, "+-+-"); + hChargeBefore->GetXaxis()->SetBinLabel(7, "+++-"); + hChargeBefore->GetXaxis()->SetBinLabel(9, "++++"); + + auto hCharge = registry.get(HIST("System/hTotalCharge")); + hCharge->GetXaxis()->SetBinLabel(1, "----"); + hCharge->GetXaxis()->SetBinLabel(3, "---+"); + hCharge->GetXaxis()->SetBinLabel(5, "+-+-"); + hCharge->GetXaxis()->SetBinLabel(7, "+++-"); + hCharge->GetXaxis()->SetBinLabel(9, "++++"); + + auto hMChargeBefore = registry.get(HIST("System/hMVsTotalChargeBefore")); + hMChargeBefore->GetYaxis()->SetBinLabel(1, "----"); + hMChargeBefore->GetYaxis()->SetBinLabel(3, "---+"); + hMChargeBefore->GetYaxis()->SetBinLabel(5, "+-+-"); + hMChargeBefore->GetYaxis()->SetBinLabel(7, "+++-"); + hMChargeBefore->GetYaxis()->SetBinLabel(9, "++++"); + + auto hMCharge = registry.get(HIST("System/hMVsTotalCharge")); + hMCharge->GetYaxis()->SetBinLabel(1, "----"); + hMCharge->GetYaxis()->SetBinLabel(3, "---+"); + hMCharge->GetYaxis()->SetBinLabel(5, "+-+-"); + hMCharge->GetYaxis()->SetBinLabel(7, "+++-"); + hMCharge->GetYaxis()->SetBinLabel(9, "++++"); - // Configure track rejection reasons histogram labels auto hReject = registry.get(HIST("Tracks/RejectionReasons")); - hReject->GetXaxis()->SetBinLabel(1, "All Tracks"); - hReject->GetXaxis()->SetBinLabel(2, "PV Contributor"); - hReject->GetXaxis()->SetBinLabel(3, "Has ITS+TPC"); - hReject->GetXaxis()->SetBinLabel(4, "pT > 0.1 GeV/c"); - hReject->GetXaxis()->SetBinLabel(5, "TPC chi2/cluster"); - hReject->GetXaxis()->SetBinLabel(6, "ITS chi2/cluster"); - hReject->GetXaxis()->SetBinLabel(7, "TPC clusters findable"); - hReject->GetXaxis()->SetBinLabel(8, "TPC nSigmaPi"); - hReject->GetXaxis()->SetBinLabel(9, "Eta acceptance"); - hReject->GetXaxis()->SetBinLabel(10, "DCAz cut"); - hReject->GetXaxis()->SetBinLabel(11, "DCAxy cut"); - hReject->GetXaxis()->SetBinLabel(12, "Accepted Tracks"); + hReject->GetXaxis()->SetBinLabel(1, "All tracks"); + hReject->GetXaxis()->SetBinLabel(2, "isPVContributor"); + hReject->GetXaxis()->SetBinLabel(3, "hasITS && hasTPC"); + hReject->GetXaxis()->SetBinLabel(4, "pT > 0.1"); + hReject->GetXaxis()->SetBinLabel(5, "tpcChi2NCl < cut"); + hReject->GetXaxis()->SetBinLabel(6, "itsChi2NCl < cut"); + hReject->GetXaxis()->SetBinLabel(7, "tpcNClsFindable > cut"); + hReject->GetXaxis()->SetBinLabel(8, "|tpcNSigmaPi| < cut"); + hReject->GetXaxis()->SetBinLabel(9, "|eta| < cut"); + hReject->GetXaxis()->SetBinLabel(10, "|dcaZ| < cut"); + hReject->GetXaxis()->SetBinLabel(11, "|dcaXY| < cut"); + hReject->GetXaxis()->SetBinLabel(12, "Accepted tracks"); + + auto hPdg = mcRegistry.get(HIST("MC/Tracks/hPdgCode")); + if (hPdg) { + int bin211 = hPdg->GetXaxis()->FindBin(211); + int binM211 = hPdg->GetXaxis()->FindBin(-211); + int bin30113 = hPdg->GetXaxis()->FindBin(30113); + if (bin211 > 0 && bin211 <= hPdg->GetNbinsX()) + hPdg->GetXaxis()->SetBinLabel(bin211, "#pi^{+}"); + if (binM211 > 0 && binM211 <= hPdg->GetNbinsX()) + hPdg->GetXaxis()->SetBinLabel(binM211, "#pi^{-}"); + if (bin30113 > 0 && bin30113 <= hPdg->GetNbinsX()) + hPdg->GetXaxis()->SetBinLabel(bin30113, "rho'"); + } + + auto hRhoFound = mcRegistry.get(HIST("MC/RhoPrime/hFound")); + hRhoFound->GetXaxis()->SetBinLabel(1, "Not Found"); + hRhoFound->GetXaxis()->SetBinLabel(2, "Found"); + + auto hMcCharge = mcRegistry.get(HIST("MC/System/hTotalCharge")); + hMcCharge->GetXaxis()->SetBinLabel(1, "----"); + hMcCharge->GetXaxis()->SetBinLabel(3, "---+"); + hMcCharge->GetXaxis()->SetBinLabel(5, "+-+-"); + hMcCharge->GetXaxis()->SetBinLabel(7, "+++-"); + hMcCharge->GetXaxis()->SetBinLabel(9, "++++"); + + auto hMcSummary = mcRegistry.get(HIST("MC/Summary/hEventCounter")); + hMcSummary->GetXaxis()->SetBinLabel(1, "Generated (denominator, gen all)"); + hMcSummary->GetXaxis()->SetBinLabel(2, "Reconstructed + matched (numerator)"); + + auto hMatchStatus = mcRegistry.get(HIST("MC/Summary/hMatchStatus")); + hMatchStatus->GetXaxis()->SetBinLabel(1, "No mcParticle on some track"); + hMatchStatus->GetXaxis()->SetBinLabel(2, "Has mcParticle but no common mother"); + hMatchStatus->GetXaxis()->SetBinLabel(3, "Match found"); + + auto hMcRecoMode = mcRegistry.get(HIST("MC/Summary/hRecoMode")); + hMcRecoMode->GetXaxis()->SetBinLabel(1, "STD"); + hMcRecoMode->GetXaxis()->SetBinLabel(2, "UPC"); + + auto hMcRecoCharge = mcRegistry.get(HIST("MC/Summary/hRecoTotalCharge")); + hMcRecoCharge->GetXaxis()->SetBinLabel(1, "----"); + hMcRecoCharge->GetXaxis()->SetBinLabel(3, "---+"); + hMcRecoCharge->GetXaxis()->SetBinLabel(5, "+-+-"); + hMcRecoCharge->GetXaxis()->SetBinLabel(7, "+++-"); + hMcRecoCharge->GetXaxis()->SetBinLabel(9, "++++"); + + if (doprocessDataCols && (doprocessMcCols || doprocessMcGenAll)) { + LOGP(fatal, + "Invalid config: processDataCols must not run together with processMcCols/processMcGenAll " + "(it would duplicate SystemTree rows). Disable one of the two groups in your configuration.json."); + } } - void process(UDCollisions::iterator const& collision, UDtracks const& tracks) + // Collision + track selection + template + std::vector selectAndFillSystemTree(ColType const& collision, TracksType const& tracks, bool& outFilled) { - // Count all processed events + outFilled = false; + std::vector goodTracks; + registry.fill(HIST("Events/Flow"), 0); + registry.fill(HIST("Events/FlowDetailed"), 0); - // Fill basic event diagnostics registry.fill(HIST("Events/VertexZ"), collision.posZ()); registry.fill(HIST("Events/NumContrib"), collision.numContrib()); registry.fill(HIST("Events/FV0Amplitude"), collision.totalFV0AmplitudeA()); @@ -207,64 +497,84 @@ struct upcRhoPrimeAnalysis { registry.fill(HIST("Events/ZDCEnergy"), collision.energyCommonZNA()); registry.fill(HIST("Events/ZDCEnergy"), collision.energyCommonZNC()); - // Apply event selection cuts in sequence - if (collision.vtxITSTPC() != vtxITSTPCcut) - return; + if (collision.vtxITSTPC() != vtxITSTPCcut) { + return goodTracks; + } registry.fill(HIST("Events/Flow"), 1); + registry.fill(HIST("Events/FlowDetailed"), 1); - if (collision.sbp() != sbpCut) - return; + if (collision.sbp() != sbpCut) { + return goodTracks; + } registry.fill(HIST("Events/Flow"), 2); + registry.fill(HIST("Events/FlowDetailed"), 2); - if (collision.itsROFb() != itsROFbCut) - return; + if (collision.itsROFb() != itsROFbCut) { + return goodTracks; + } registry.fill(HIST("Events/Flow"), 3); + registry.fill(HIST("Events/FlowDetailed"), 3); - if (collision.tfb() != tfbCut) - return; + if (collision.tfb() != tfbCut) { + return goodTracks; + } registry.fill(HIST("Events/Flow"), 4); + registry.fill(HIST("Events/FlowDetailed"), 4); + + if (specifyGapSide && collision.gapSide() != gapSide) { + return goodTracks; + } - if (specifyGapSide && collision.gapSide() != gapSide) - return; - if (collision.totalFV0AmplitudeA() > fv0Cut) - return; - if (collision.totalFT0AmplitudeA() > ft0aCut) - return; - if (collision.totalFT0AmplitudeC() > ft0cCut) - return; - if (collision.energyCommonZNA() > zdcCut || collision.energyCommonZNC() > zdcCut) - return; registry.fill(HIST("Events/Flow"), 5); + registry.fill(HIST("Events/FlowDetailed"), 5); - if (collision.numContrib() != numPVContrib) - return; + if (collision.totalFV0AmplitudeA() > fv0Cut) { + return goodTracks; + } + registry.fill(HIST("Events/FlowDetailed"), 6); + + if (collision.totalFT0AmplitudeA() > ft0aCut) { + return goodTracks; + } + registry.fill(HIST("Events/FlowDetailed"), 7); + + if (collision.totalFT0AmplitudeC() > ft0cCut) { + return goodTracks; + } + registry.fill(HIST("Events/FlowDetailed"), 8); + + if (collision.energyCommonZNA() > zdcCut || collision.energyCommonZNC() > zdcCut) { + return goodTracks; + } registry.fill(HIST("Events/Flow"), 6); + registry.fill(HIST("Events/FlowDetailed"), 9); - if (std::abs(collision.posZ()) > vZCut) - return; + if (collision.numContrib() != numPVContrib) { + return goodTracks; + } registry.fill(HIST("Events/Flow"), 7); + registry.fill(HIST("Events/FlowDetailed"), 10); - std::vector posPions; - std::vector negPions; - posPions.reserve(2); - negPions.reserve(2); + if (std::abs(collision.posZ()) > vZCut) { + return goodTracks; + } + registry.fill(HIST("Events/Flow"), 8); + registry.fill(HIST("Events/FlowDetailed"), 11); - // Loop over all tracks in the event + // --- Track selection: up to 4 good tracks, charge combination --- + goodTracks.reserve(4); for (const auto& track : tracks) { - registry.fill(HIST("Tracks/RejectionReasons"), 0); // Count all tracks + registry.fill(HIST("Tracks/RejectionReasons"), 0); - // Track selection criteria applied in sequence: if (useOnlyPVtracks && !track.isPVContributor()) { registry.fill(HIST("Tracks/RejectionReasons"), 1); continue; } - if (!track.hasITS() || !track.hasTPC()) { registry.fill(HIST("Tracks/RejectionReasons"), 2); continue; } - // Fill track spectra registry.fill(HIST("Tracks/Pt"), track.pt()); registry.fill(HIST("Tracks/Eta"), eta(track.px(), track.py(), track.pz())); registry.fill(HIST("Tracks/TPCNSigmaPi"), track.tpcNSigmaPi()); @@ -278,7 +588,6 @@ struct upcRhoPrimeAnalysis { registry.fill(HIST("Tracks/RejectionReasons"), 3); continue; } - if (track.tpcChi2NCl() > tpcChi2NClsCut) { registry.fill(HIST("Tracks/RejectionReasons"), 4); continue; @@ -287,84 +596,71 @@ struct upcRhoPrimeAnalysis { registry.fill(HIST("Tracks/RejectionReasons"), 5); continue; } - if (track.tpcNClsFindable() < minTPCFindableClusters) { registry.fill(HIST("Tracks/RejectionReasons"), 6); continue; } - if (std::abs(track.tpcNSigmaPi()) > nSigmaTPCcut) { registry.fill(HIST("Tracks/RejectionReasons"), 7); continue; } - - float trackEta = eta(track.px(), track.py(), track.pz()); - if (std::abs(trackEta) > etaCut) { + if (std::abs(eta(track.px(), track.py(), track.pz())) > etaCut) { registry.fill(HIST("Tracks/RejectionReasons"), 8); continue; } - if (std::abs(track.dcaZ()) > dcaZcut) { registry.fill(HIST("Tracks/RejectionReasons"), 9); continue; } - float maxDCAxy = 0.0105 + 0.035 / std::pow(track.pt(), 1.1); - if (dcaXYcut == 0 && (std::fabs(track.dcaXY()) > maxDCAxy)) { - registry.fill(HIST("Tracks/RejectionReasons"), 10); - continue; - } else if (dcaXYcut != 0 && (std::fabs(track.dcaXY()) > dcaXYcut)) { + if (dcaXYcut == 0 && std::fabs(track.dcaXY()) > maxDCAxy) { registry.fill(HIST("Tracks/RejectionReasons"), 10); continue; } - // Track passed all selection criteria registry.fill(HIST("Tracks/RejectionReasons"), 11); - - if (track.sign() > 0 && posPions.size() < 2) { - posPions.push_back(track); - } else if (track.sign() < 0 && negPions.size() < 2) { - negPions.push_back(track); - } - - if (posPions.size() == 2 && negPions.size() == 2) + goodTracks.push_back(track); + if (goodTracks.size() == 4) { break; + } } - if (posPions.size() != 2 || negPions.size() != 2) { - return; - } - registry.fill(HIST("Events/Flow"), 8); + // Basic control + registry.fill(HIST("Tracks/NGoodTracksPerEvent"), goodTracks.size()); - std::vector selectedTracks; - selectedTracks.insert(selectedTracks.end(), posPions.begin(), posPions.end()); - selectedTracks.insert(selectedTracks.end(), negPions.begin(), negPions.end()); + if (goodTracks.size() != 4) { + return goodTracks; + } + registry.fill(HIST("Events/Flow"), 9); + registry.fill(HIST("Events/FlowDetailed"), 12); - // Reconstruct the 4-pion system - ROOT::Math::PxPyPzMVector fourPionSystem; - std::vector pionFourVectors; + // Real total charge + int totalCharge = 0; + for (const auto& track : goodTracks) { + totalCharge += track.sign(); + } + bool isChargeZero = (totalCharge == 0); + registry.fill(HIST("System/hTotalChargeBefore"), totalCharge); - for (const auto& track : selectedTracks) { - ROOT::Math::PxPyPzMVector pionVec( - track.px(), track.py(), track.pz(), - o2::constants::physics::MassPionCharged); - fourPionSystem += pionVec; - pionFourVectors.push_back(pionVec); + PxPyPzMVector fourPionSystem; + for (const auto& track : goodTracks) { + fourPionSystem += PxPyPzMVector(track.px(), track.py(), track.pz(), o2::constants::physics::MassPionCharged); } - // Fill pre-cut system histograms registry.fill(HIST("Cuts/MBefore"), fourPionSystem.M()); registry.fill(HIST("Cuts/PtBefore"), fourPionSystem.Pt()); + registry.fill(HIST("System/hMVsTotalChargeBefore"), fourPionSystem.M(), totalCharge); - // Apply system-level kinematic cuts - if (fourPionSystem.M() < systemMassMinCut || fourPionSystem.M() > systemMassMaxCut) - return; - if (fourPionSystem.Pt() > systemPtCut) - return; - if (std::abs(fourPionSystem.Rapidity()) > systemYCut) - return; + if (fourPionSystem.M() < systemMassMinCut || fourPionSystem.M() > systemMassMaxCut) { + return goodTracks; + } + if (fourPionSystem.Pt() > systemPtCut) { + return goodTracks; + } + if (std::abs(fourPionSystem.Rapidity()) > systemYCut) { + return goodTracks; + } - // Fill post-cut system histograms registry.fill(HIST("Cuts/MAfter"), fourPionSystem.M()); registry.fill(HIST("Cuts/PtAfter"), fourPionSystem.Pt()); registry.fill(HIST("System/hM"), fourPionSystem.M()); @@ -372,13 +668,14 @@ struct upcRhoPrimeAnalysis { registry.fill(HIST("System/hEta"), fourPionSystem.Eta()); registry.fill(HIST("System/hPhi"), fourPionSystem.Phi() + o2::constants::math::PI); registry.fill(HIST("System/hY"), fourPionSystem.Rapidity()); + registry.fill(HIST("System/hTotalCharge"), totalCharge); + registry.fill(HIST("System/hMVsTotalCharge"), fourPionSystem.M(), totalCharge); std::vector trackPts, trackEtas, trackPhis; std::vector trackSigns, trackIDs; std::vector tpcNSigmasEl, tpcNSigmasPi, tpcNSigmasKa, tpcNSigmasPr; - - for (size_t i = 0; i < selectedTracks.size(); i++) { - const auto& track = selectedTracks[i]; + for (size_t i = 0; i < goodTracks.size(); i++) { + const auto& track = goodTracks[i]; trackPts.push_back(track.pt()); trackEtas.push_back(eta(track.px(), track.py(), track.pz())); trackPhis.push_back(phi(track.px(), track.py())); @@ -387,48 +684,272 @@ struct upcRhoPrimeAnalysis { tpcNSigmasPi.push_back(track.tpcNSigmaPi()); tpcNSigmasKa.push_back(track.tpcNSigmaKa()); tpcNSigmasPr.push_back(track.tpcNSigmaPr()); - trackIDs.push_back(i); + trackIDs.push_back(static_cast(i)); } bool isReconstructedWithUPC = (collision.flags() == 1); - // Fill the output + registry.fill(HIST("Events/Flow"), 10); + registry.fill(HIST("Events/hRecoMode"), isReconstructedWithUPC ? 1 : 0); + systemTree( collision.runNumber(), - fourPionSystem.M(), - fourPionSystem.Pt(), - fourPionSystem.Rapidity(), - fourPionSystem.Phi(), - collision.posX(), - collision.posY(), - collision.posZ(), - 0, // Total charge = 0 - collision.totalFT0AmplitudeA(), - collision.totalFT0AmplitudeC(), - collision.totalFV0AmplitudeA(), + fourPionSystem.M(), fourPionSystem.Pt(), fourPionSystem.Rapidity(), fourPionSystem.Phi(), + collision.posX(), collision.posY(), collision.posZ(), + totalCharge, + collision.totalFT0AmplitudeA(), collision.totalFT0AmplitudeC(), collision.totalFV0AmplitudeA(), collision.numContrib(), - trackSigns, - trackPts, - trackEtas, - trackPhis, - tpcNSigmasEl, - tpcNSigmasPi, - tpcNSigmasKa, - tpcNSigmasPr, - trackIDs, + trackSigns, trackPts, trackEtas, trackPhis, + tpcNSigmasEl, tpcNSigmasPi, tpcNSigmasKa, tpcNSigmasPr, + trackIDs, isReconstructedWithUPC, + collision.timeZNA(), collision.timeZNC(), collision.energyCommonZNA(), collision.energyCommonZNC(), + isChargeZero, collision.occupancyInTime(), collision.hadronicRate()); + + outFilled = true; + return goodTracks; + } // end selectAndFillSystemTree + + int getMcRunNumber(aod::BCs const& bcs) + { + if (bcs.size() == 0) { + return -1; + } + auto bc = bcs.begin(); + return bc.runNumber(); + } + + // processDataCols: RD or MC reco + void processDataCols(UDCollisions::iterator const& collision, UDtracks const& tracks) + { + bool filled = false; + selectAndFillSystemTree(collision, tracks, filled); + } + PROCESS_SWITCH(upcRhoPrimeAnalysis, processDataCols, "Process real data or MC reco", true); + + // processMcCols: MC only + void processMcCols(UDCollisionsMC::iterator const& collision, UDtracksMC const& tracks, UDMcParticles const&, UDMcCollisions const&, aod::BCs const& bcs) + { + if (genId != -1) { + if (!collision.has_udMcCollision() || collision.template udMcCollision_as().generatorsID() != genId) { + return; + } + } + + bool filled = false; + auto selTrks = selectAndFillSystemTree(collision, tracks, filled); + if (!filled) { + return; + } + + bool isReconstructedWithUPC = (collision.flags() == 1); + + mcRegistry.fill(HIST("MC/Match/hRecoEvents"), 0); + mcRegistry.fill(HIST("MC/Summary/hRecoMode"), isReconstructedWithUPC ? 1 : 0); + + int recoTotalCharge = 0; + for (const auto& track : selTrks) { + recoTotalCharge += track.sign(); + } + mcRegistry.fill(HIST("MC/Summary/hRecoTotalCharge"), recoTotalCharge); + + // Defaults (pdg=0 / kinematics=-999 => "no match") + int motherPdg = 0; + float motherPt = -999, motherPhi = -999, motherMass = -999, motherRap = -999; + int mcTotalCharge = 0; + int trackPdgs[4] = {0, 0, 0, 0}; + float trackPts[4] = {-999, -999, -999, -999}; + float trackEtas[4] = {-999, -999, -999, -999}; + float trackPhis[4] = {-999, -999, -999, -999}; + int trackSigns[4] = {0, 0, 0, 0}; + int isPrimary[4] = {0, 0, 0, 0}; + float mcPosX = -999, mcPosY = -999, mcPosZ = -999; + int mcCollisionIndex = -1; + int mcRunNumber = getMcRunNumber(bcs); + + // Basic control + int nTracksWithMcParticle = 0; + for (const auto& track : selTrks) { + if (track.has_udMcParticle()) { + nTracksWithMcParticle++; + } + } + mcRegistry.fill(HIST("MC/Control/hTracksWithMcParticle"), nTracksWithMcParticle); + + // All 4 reco tracks need an associated mcParticle to look for the mother + std::vector mcParts; + bool allHaveMcParticle = true; + for (const auto& track : selTrks) { + if (!track.has_udMcParticle()) { + allHaveMcParticle = false; + break; + } + mcParts.push_back(track.template udMcParticle_as()); + } + + MotherInfo mi; + if (allHaveMcParticle) { + mi = findCommonMotherGeneric(mcParts); + } + + if (!allHaveMcParticle) { + mcRegistry.fill(HIST("MC/Summary/hMatchStatus"), 0); + } else if (!mi.found) { + mcRegistry.fill(HIST("MC/Summary/hMatchStatus"), 1); + } else { + mcRegistry.fill(HIST("MC/Summary/hMatchStatus"), 2); + mcRegistry.fill(HIST("MC/Summary/hEventCounter"), 1); + } + + if (allHaveMcParticle) { + for (size_t i = 0; i < mcParts.size() && i < 4; i++) { + trackPdgs[i] = mcParts[i].pdgCode(); + trackPts[i] = pt(mcParts[i].px(), mcParts[i].py()); + trackEtas[i] = eta(mcParts[i].px(), mcParts[i].py(), mcParts[i].pz()); + trackPhis[i] = phi(mcParts[i].px(), mcParts[i].py()); + trackSigns[i] = signFromPdg(trackPdgs[i]); + isPrimary[i] = mcParts[i].isPhysicalPrimary() ? 1 : 0; + mcTotalCharge += trackSigns[i]; + } + + auto mcCollision = mcParts[0].template udMcCollision_as(); + mcPosX = mcCollision.posX(); + mcPosY = mcCollision.posY(); + mcPosZ = mcCollision.posZ(); + mcCollisionIndex = static_cast(mcCollision.globalIndex()); + } + + if (mi.found) { + motherPdg = mi.pdg; + motherPt = pt(mi.px, mi.py); + motherPhi = phi(mi.px, mi.py); + PxPyPzMVector genSystem; + for (const auto& p : mcParts) { + genSystem += PxPyPzMVector(p.px(), p.py(), p.pz(), o2::constants::physics::MassPionCharged); + } + motherMass = genSystem.M(); + motherRap = genSystem.Rapidity(); + + mcRegistry.fill(HIST("MC/Control/hMotherPdg"), motherPdg); + mcRegistry.fill(HIST("MC/Match/hMatchedGenM"), motherMass); + mcRegistry.fill(HIST("MC/Match/hMatchedGenPt"), motherPt); + mcRegistry.fill(HIST("MC/Match/hMatchedGenY"), motherRap); + + PxPyPzMVector recoSystem; + for (const auto& track : selTrks) { + recoSystem += PxPyPzMVector(track.px(), track.py(), track.pz(), o2::constants::physics::MassPionCharged); + } + mcRegistry.fill(HIST("MC/Match/hRecoVsGenM"), motherMass, recoSystem.M()); + if (motherPdg == 30113) { // only when the mother found is actually the rho prime + if (isReconstructedWithUPC) { + mcRegistry.fill(HIST("MC/RhoPrime/hMassUPC"), recoSystem.M()); + } else { + mcRegistry.fill(HIST("MC/RhoPrime/hMassSTD"), recoSystem.M()); + } + } + } + + fourPiMcMatchTree( isReconstructedWithUPC, - collision.timeZNA(), - collision.timeZNC(), - collision.energyCommonZNA(), - collision.energyCommonZNC(), - true, // Always charge zero for our selection - collision.occupancyInTime(), - collision.hadronicRate()); + motherPdg, motherPt, motherPhi, motherMass, motherRap, mcTotalCharge, + trackPdgs, trackPts, trackEtas, trackPhis, trackSigns, isPrimary, + mcPosX, mcPosY, mcPosZ, mcCollisionIndex, mcRunNumber, + systemTree.lastIndex()); + } + PROCESS_SWITCH(upcRhoPrimeAnalysis, processMcCols, "Match MC reco<->gen (MC only)", false); + + // processMcGenAll: MC All generated collisions + void processMcGenAll(UDMcCollisions::iterator const& mcCollision, UDMcParticles const& mcParticles, aod::BCs const& bcs) + { + if (genId != -1 && mcCollision.generatorsID() != genId) { + return; + } + + int mcRunNumber = getMcRunNumber(bcs); + + mcRegistry.fill(HIST("MC/Events/hAllEvents"), 0); + mcRegistry.fill(HIST("MC/Events/hVertexZ"), mcCollision.posZ()); + + std::vector primaries; + for (const auto& part : mcParticles) { + if (part.isPhysicalPrimary()) { + primaries.push_back(part); + } + } + mcRegistry.fill(HIST("MC/Events/hNPrimaries"), primaries.size()); + + if (primaries.size() != 4) { + return; + } + mcRegistry.fill(HIST("MC/Summary/hEventCounter"), 0); + + int trackPdgs[4] = {0, 0, 0, 0}; + float trackPts[4] = {0, 0, 0, 0}; + float trackEtas[4] = {0, 0, 0, 0}; + float trackPhis[4] = {0, 0, 0, 0}; + int trackSigns[4] = {0, 0, 0, 0}; + int isPrimary[4] = {1, 1, 1, 1}; + int mcTotalCharge = 0; + + PxPyPzMVector genSystem; + for (size_t i = 0; i < 4; i++) { + const auto& p = primaries[i]; + trackPdgs[i] = p.pdgCode(); + trackPts[i] = pt(p.px(), p.py()); + trackEtas[i] = eta(p.px(), p.py(), p.pz()); + trackPhis[i] = phi(p.px(), p.py()); + trackSigns[i] = signFromPdg(trackPdgs[i]); + mcTotalCharge += trackSigns[i]; + + mcRegistry.fill(HIST("MC/Tracks/hPt"), trackPts[i]); + mcRegistry.fill(HIST("MC/Tracks/hEta"), trackEtas[i]); + mcRegistry.fill(HIST("MC/Tracks/hPhi"), trackPhis[i] + o2::constants::math::PI); // same 0-2pi convention as System/hPhi + mcRegistry.fill(HIST("MC/Tracks/hPdgCode"), trackPdgs[i]); + + genSystem += PxPyPzMVector(p.px(), p.py(), p.pz(), o2::constants::physics::MassPionCharged); + } + + // Generic common-mother search + int motherPdg = 0; + float motherPt = -999, motherPhi = -999, motherMass = -999, motherRap = -999; + MotherInfo mi = findCommonMotherGeneric(primaries); + mcRegistry.fill(HIST("MC/Control/hNDaughtersOfMother"), mi.found ? 4 : 0); + if (mi.found) { + motherPdg = mi.pdg; + motherPt = pt(mi.px, mi.py); + motherPhi = phi(mi.px, mi.py); + motherMass = genSystem.M(); + motherRap = genSystem.Rapidity(); + mcRegistry.fill(HIST("MC/Control/hMotherPdg"), motherPdg); + } + + mcRegistry.fill(HIST("MC/System/hM"), genSystem.M()); + mcRegistry.fill(HIST("MC/System/hPt"), genSystem.Pt()); + mcRegistry.fill(HIST("MC/System/hY"), genSystem.Rapidity()); + mcRegistry.fill(HIST("MC/System/hMvsPt"), genSystem.M(), genSystem.Pt()); + mcRegistry.fill(HIST("MC/System/hMvsY"), genSystem.M(), genSystem.Rapidity()); + mcRegistry.fill(HIST("MC/System/hTotalCharge"), mcTotalCharge); + + if (motherPdg == 30113) { + mcRegistry.fill(HIST("MC/RhoPrime/hFound"), 1); + mcRegistry.fill(HIST("MC/RhoPrime/hMass"), motherMass); + mcRegistry.fill(HIST("MC/RhoPrime/hPt"), motherPt); + } else { + mcRegistry.fill(HIST("MC/RhoPrime/hFound"), 0); + } + + fourPiMcGenAllTree( + motherPdg, motherPt, motherPhi, motherMass, motherRap, mcTotalCharge, + trackPdgs, trackPts, trackEtas, trackPhis, trackSigns, isPrimary, + mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), + static_cast(mcCollision.globalIndex()), mcRunNumber); + + mcRegistry.fill(HIST("MC/Events/hAccepted"), 0); } + PROCESS_SWITCH(upcRhoPrimeAnalysis, processMcGenAll, "All generated collisions (MC only)", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(cfgc, TaskName{"upc-rho-prime-analysis"})}; } diff --git a/PWGUD/Tasks/upcTestFitBitMapping.cxx b/PWGUD/Tasks/upcTestFitBitMapping.cxx index bcee34c60fc..829e1df5f52 100644 --- a/PWGUD/Tasks/upcTestFitBitMapping.cxx +++ b/PWGUD/Tasks/upcTestFitBitMapping.cxx @@ -11,111 +11,218 @@ // /// \file upcTestFitBitMapping.cxx -/// \brief FIT bits to phi, eta mapping +/// \brief Tutorial for accessing and mapping the FIT-bit table /// \author Sandor Lokos, sandor.lokos@cern.ch -/// \since March 2026 +/// \since October 2026 -#include "PWGUD/Core/UDHelpers.h" // udhelpers::Bits256, makeBits256, testBit, getPhiEtaFromFitBit -#include "PWGUD/DataModel/UDTables.h" // aod::UDCollisionFITBits +#include "PWGUD/Core/UDHelpers.h" +#include "PWGUD/DataModel/UDTables.h" -#include // o2::constants::math::TwoPI -#include // o2::ft0::Geometry +#include +#include #include #include #include #include #include +#include #include #include -#include using namespace o2; using namespace o2::framework; struct UpcTestFitBitMapping { - static constexpr int Thr2Selector = 2; // value of whichThr that selects the Thr2 bit set + static constexpr int kThr1Selector = 1; + static constexpr int kThr2Selector = 2; - Configurable whichThr{"whichThr", 1, "Use 1=Thr1 bits or 2=Thr2 bits"}; - Configurable maxEvents{"maxEvents", -1, "Process at most this many rows (-1 = all)"}; + Configurable whichThr{"whichThr", 1, "FIT amplitude threshold to use: 1 for Thr1 or 2 for Thr2"}; - // Minimal offset container compatible with UDHelpers.h expectations: getX/getY/getZ + // Minimal zero-offset container required by getPhiEtaFromFitBit(). + // Detector-alignment corrections are not applied in this tutorial. struct OffsetXYZ { - double x{0}, y{0}, z{0}; + double x{0.}; + double y{0.}; + double z{0.}; + double getX() const { return x; } double getY() const { return y; } double getZ() const { return z; } }; - std::array offsetFT0{}; // iRunOffset = 0 for now - int iRunOffset = 0; + std::array offsetFT0{}; + static constexpr int kRunOffsetIndex = 0; - o2::ft0::Geometry ft0Det{}; + o2::ft0::Geometry ft0Geometry{}; HistogramRegistry registry{ "registry", { - {"hPhiA", "FT0A #varphi;#varphi;counts", {HistType::kTH1F, {{18, 0.0, o2::constants::math::TwoPI}}}}, - {"hEtaA", "FT0A #eta;#eta;counts", {HistType::kTH1F, {{8, 3.5, 5.0}}}}, - {"hEtaPhiA", "FT0A #eta vs #varphi;#eta;#varphi", {HistType::kTH2F, {{8, 3.5, 5.0}, {18, 0.0, o2::constants::math::TwoPI}}}}, - - {"hPhiC", "FT0C #varphi;#varphi;counts", {HistType::kTH1F, {{18, 0.0, o2::constants::math::TwoPI}}}}, - {"hEtaC", "FT0C #eta;#eta;counts", {HistType::kTH1F, {{8, -3.5, -2.0}}}}, - {"hEtaPhiC", "FT0C #eta vs #varphi;#eta;#varphi", {HistType::kTH2F, {{8, -3.5, -2.0}, {18, 0.0, o2::constants::math::TwoPI}}}}, + {"association/hRowsPerCollision", + "FIT-bit rows associated with each collision;rows;collisions", + {HistType::kTH1F, {{4, -0.5, 3.5}}}}, + + {"multiplicity/hFT0A", + "FT0A fired-channel multiplicity;N_{fired}^{FT0A};collisions", + {HistType::kTH1F, {{97, -0.5, 96.5}}}}, + {"multiplicity/hFT0C", + "FT0C fired-channel multiplicity;N_{fired}^{FT0C};collisions", + {HistType::kTH1F, {{113, -0.5, 112.5}}}}, + {"multiplicity/hFV0A", + "FV0A fired-channel multiplicity;N_{fired}^{FV0A};collisions", + {HistType::kTH1F, {{49, -0.5, 48.5}}}}, + {"multiplicity/hFT0AVsFT0C", + "FT0A versus FT0C fired channels;N_{fired}^{FT0A};N_{fired}^{FT0C}", + {HistType::kTH2F, {{97, -0.5, 96.5}, {113, -0.5, 112.5}}}}, + + {"occupancy/hPackedBit", + "Selected FIT-bit occupancy;packed bit;counts", + {HistType::kTH1F, {{256, -0.5, 255.5}}}}, + {"occupancy/hFT0AChannel", + "FT0A channel occupancy;FT0A channel;counts", + {HistType::kTH1F, {{96, -0.5, 95.5}}}}, + {"occupancy/hFT0CChannel", + "FT0C channel occupancy;FT0C channel;counts", + {HistType::kTH1F, {{112, -0.5, 111.5}}}}, + {"occupancy/hFV0AChannel", + "FV0A channel occupancy;FV0A channel;counts", + {HistType::kTH1F, {{48, -0.5, 47.5}}}}, + + {"mapping/hPhiA", + "FT0A #varphi;#varphi;counts", + {HistType::kTH1F, {{18, 0., o2::constants::math::TwoPI}}}}, + {"mapping/hEtaA", + "FT0A #eta;#eta;counts", + {HistType::kTH1F, {{8, 3.5, 5.0}}}}, + {"mapping/hEtaPhiA", + "FT0A #eta versus #varphi;#eta;#varphi", + {HistType::kTH2F, {{8, 3.5, 5.0}, {18, 0., o2::constants::math::TwoPI}}}}, + {"mapping/hXYA", + "FT0A channel positions;x (cm);y (cm)", + {HistType::kTH2F, {{24, -18., 18.}, {24, -18., 18.}}}}, + + {"mapping/hPhiC", + "FT0C #varphi;#varphi;counts", + {HistType::kTH1F, {{18, 0., o2::constants::math::TwoPI}}}}, + {"mapping/hEtaC", + "FT0C #eta;#eta;counts", + {HistType::kTH1F, {{8, -3.5, -2.0}}}}, + {"mapping/hEtaPhiC", + "FT0C #eta versus #varphi;#eta;#varphi", + {HistType::kTH2F, {{8, -3.5, -2.0}, {18, 0., o2::constants::math::TwoPI}}}}, + {"mapping/hXYC", + "FT0C channel positions;x (cm);y (cm)", + {HistType::kTH2F, {{24, -18., 18.}, {24, -18., 18.}}}}, }}; void init(InitContext&) { - // UDHelpers calls calculateChannelCenter() inside, but doing it once here is fine. - ft0Det.calculateChannelCenter(); + if (whichThr.value != kThr1Selector && whichThr.value != kThr2Selector) { + LOGF(fatal, + "Invalid whichThr=%d: allowed values are 1 and 2", + whichThr.value); + } + + ft0Geometry.calculateChannelCenter(); + } + + template + udhelpers::Bits256 getSelectedBits(T const& row) const + { + if (whichThr.value == kThr2Selector) { + return udhelpers::makeBits256( + row.thr2W0(), row.thr2W1(), row.thr2W2(), row.thr2W3()); + } + + return udhelpers::makeBits256( + row.thr1W0(), row.thr1W1(), row.thr1W2(), row.thr1W3()); } - void process(aod::UDCollisionFITBits const& bitsTable) + void process(aod::UDCollisions::iterator const&, + aod::UDCollisionFITBits const& fitBits) { - int64_t nProcessed = 0; + // Because UDCollisionFITBits contains UDCollisionId, the framework + // supplies only the FIT-bit rows associated with the current collision. + registry.fill(HIST("association/hRowsPerCollision"), fitBits.size()); + + // The producer is expected to write exactly one FIT-bit row per collision. + if (fitBits.size() != 1) { + return; + } + + const auto row = fitBits.begin(); + const auto bits = getSelectedBits(row); - for (auto const& row : bitsTable) { - if (maxEvents >= 0 && nProcessed >= maxEvents) { - break; + int nFT0A = 0; + int nFT0C = 0; + int nFV0A = 0; + + // Packed layout: + // 0--95: FT0A + // 96--207: FT0C + // 208--255: FV0A + for (int bit = 0; bit < udhelpers::kTotalBits; ++bit) { + if (!udhelpers::testBit(bits, bit)) { + continue; + } + + registry.fill(HIST("occupancy/hPackedBit"), bit); + + const auto decoded = udhelpers::decodeFitBit(bit); + + if (decoded.det == udhelpers::FitBitRef::Det::FV0) { + ++nFV0A; + registry.fill(HIST("occupancy/hFV0AChannel"), decoded.ch); + continue; + } + + if (decoded.det != udhelpers::FitBitRef::Det::FT0) { + continue; } - ++nProcessed; - // Use udhelpers' canonical packed type + builder - udhelpers::Bits256 w{}; - if (whichThr == Thr2Selector) { - w = udhelpers::makeBits256(row.thr2W0(), row.thr2W1(), row.thr2W2(), row.thr2W3()); + if (decoded.isC) { + ++nFT0C; + registry.fill( + HIST("occupancy/hFT0CChannel"), + decoded.ch - udhelpers::kFT0AChannels); } else { - w = udhelpers::makeBits256(row.thr1W0(), row.thr1W1(), row.thr1W2(), row.thr1W3()); + ++nFT0A; + registry.fill(HIST("occupancy/hFT0AChannel"), decoded.ch); + } + + double phi = 0.; + double eta = 0.; + if (!udhelpers::getPhiEtaFromFitBit( + ft0Geometry, bit, offsetFT0, kRunOffsetIndex, phi, eta)) { + continue; } - // Loop FT0 bits only (0..207). FV0 starts at 208 but ignored here. - for (int bit = 0; bit < udhelpers::kFT0Bits; ++bit) { - if (!udhelpers::testBit(w, bit)) { - continue; - } - - double phi = 0., eta = 0.; - const bool ok = udhelpers::getPhiEtaFromFitBit(ft0Det, bit, offsetFT0, iRunOffset, phi, eta); - if (!ok) { - continue; - } - - if (bit < udhelpers::kFT0AChannels) { - registry.fill(HIST("hPhiA"), phi); - registry.fill(HIST("hEtaA"), eta); - registry.fill(HIST("hEtaPhiA"), eta, phi); - } else { - registry.fill(HIST("hPhiC"), phi); - registry.fill(HIST("hEtaC"), eta); - registry.fill(HIST("hEtaPhiC"), eta, phi); - } + const auto position = ft0Geometry.getChannelCenter(decoded.ch); + const double x = position.X() + offsetFT0[kRunOffsetIndex].getX(); + const double y = position.Y() + offsetFT0[kRunOffsetIndex].getY(); + + if (decoded.isC) { + registry.fill(HIST("mapping/hPhiC"), phi); + registry.fill(HIST("mapping/hEtaC"), eta); + registry.fill(HIST("mapping/hEtaPhiC"), eta, phi); + registry.fill(HIST("mapping/hXYC"), x, y); + } else { + registry.fill(HIST("mapping/hPhiA"), phi); + registry.fill(HIST("mapping/hEtaA"), eta); + registry.fill(HIST("mapping/hEtaPhiA"), eta, phi); + registry.fill(HIST("mapping/hXYA"), x, y); } } + + registry.fill(HIST("multiplicity/hFT0A"), nFT0A); + registry.fill(HIST("multiplicity/hFT0C"), nFT0C); + registry.fill(HIST("multiplicity/hFV0A"), nFV0A); + registry.fill(HIST("multiplicity/hFT0AVsFT0C"), nFT0A, nFT0C); } }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { - return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(cfgc)}; } diff --git a/PWGUD/Tasks/upcTrackVertexingQA.cxx b/PWGUD/Tasks/upcTrackVertexingQA.cxx new file mode 100644 index 00000000000..70900886cee --- /dev/null +++ b/PWGUD/Tasks/upcTrackVertexingQA.cxx @@ -0,0 +1,530 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file upcTrackVertexingQA.cxx +/// \brief task to study the performance of vertexing for low-multiplicity UPC collisions +/// \author Andrea Tavira Garcia a.tavira@cern.ch +/// \author Andrea Giovanni Riffero andrea.giovanni.riffero@cern.ch + +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include + +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +enum class CandSpecies { + kRho = 0, + kJpsi = 1 +}; + +struct UpcTrackVertexingQA { + // Configurables + Configurable yCandMax{"yCandMax", 0.8, "max. cand. rapidity"}; + Configurable ptCandMin{"ptCandMin", 0., "min. cand. pT (GeV/c)"}; + Configurable ptTrackMin{"ptTrackMin", 0.1, "min. track pT (GeV/c)"}; + Configurable etaTrackMax{"etaTrackMax", 0.9, "max. |eta| of tracks"}; + Configurable nMinTpcClusters{"nMinTpcClusters", 60, "min. number of TPC clusters"}; + + // Axes (the mass axis matches the default rho window; override it for the J/psi) + ConfigurableAxis axisMass{"axisMass", {200, 0.5, 1.3}, "m_{#pi#pi} (GeV/#it{c}^{2})"}; + ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.00, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.175, 0.20, 0.25, 0.30, 0.40, 0.50}, "#it{p}_{T} (GeV/#it{c})"}; + ConfigurableAxis axisY{"axisY", {100, -1., 1.}, "Rapidity"}; + ConfigurableAxis axisPtTrk{"axisPtTrk", {80, 0.0, 0.8}, "#it{p}_{T} (GeV/#it{c})"}; + + HistogramConfigSpec hVtxZ{HistType::kTH1F, {{80, -20., 20.}}}; + HistogramConfigSpec hVtxX{HistType::kTH1F, {{50, -0.04, -0.015}}}; + HistogramConfigSpec hVtxY{HistType::kTH1F, {{60, -0.015, 0.015}}}; + HistogramConfigSpec hEtaTrk{HistType::kTH1F, {{100, -1.0, 1.0}}}; + + HistogramRegistry registry{"registry"}; + + // services + Service ccdb{}; // access to database + + // variables to store run info + int runNumberBc = 0; // run number used to process BCs + int runNumberCol = 0; // run number used to process collisions + int64_t sor = 0; // best known timestamp for the start of run + int64_t orbitsPerTF = 0; // number of orbits per TF + int64_t bcSOR = 0; // first bc of the first orbit + int64_t nBCsPerTF = 0; // duration of TF in bcs + int64_t currentTF = -1; // current time frame being looked at + int64_t nTF = 0; // number of time frames in run + + // constants + static constexpr unsigned int TwoTracks = 2; + static constexpr int UnassignedDCA = 999; + + static constexpr float MassPion = o2::constants::physics::MassPionCharged; + static constexpr float MassMuon = o2::constants::physics::MassMuon; + + // folders that contains plots for all tracks and associated tracks + static constexpr std::array TrackFolders = {"Trk/", "TrkColl/", "TrkAmb/"}; + + // Name shortenings + // passed* columns are in TrackSelectionExtension; isGlobalTrack* and trackCutFlag in TrackSelection + using TracksExtraSels = soa::Join; + using TracksExtraWPidPi = soa::Join; + using TracksExtraWPidMu = soa::Join; + using TracksExtraWPidPiMc = soa::Join; + using TracksExtraWPidMuMc = soa::Join; + using CollisionsWithMc = soa::Join; + using TracksExtraIU = soa::Join; + + void init(InitContext&) + { + // collision level + registry.add("Coll/hNContrib", ";N_{PV contributors};entries", {HistType::kTH1F, {{10, -0.5, 9.5}}}); + registry.add("Coll/hBCid", ";BCid;entries", {HistType::kTH1F, {{1000, 0., 100000.}}}); + registry.add("Coll/hVtxZ", ";#it{z}_{vtx} (cm);entries", hVtxZ); + registry.add("Coll/hnTF", ";TF;entries", {HistType::kTH1F, {{2000, 0., 2000}}}); + registry.add("Coll/hVtxX", ";#it{x}_{vtx} (cm);entries", hVtxX); + registry.add("Coll/hVtxY", ";#it{y}_{vtx} (cm);entries", hVtxY); + registry.add("Coll/hVtxChi2", ";#chi^{2} vtx;entries", {HistType::kTH1F, {{100, 0., 10.}}}); + + // track level (all good tracks, before grouping by collision) + registry.add("Trk/hPt", ";#it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPtTrk}}); + registry.add("Trk/hEta", ";#eta;entries", hEtaTrk); + registry.add("Trk/hChi2NCl", ";#chi^{2}/N_{cls} TPC;entries", {HistType::kTH1F, {{50, 0., 5.}}}); + registry.add("Trk/hTpcSignalVsP", ";#it{p} (GeV/#it{c});TPC d#it{E}/d#it{x}", {HistType::kTH2F, {{200, 0., 2.}, {300, 0., 300.}}}); + registry.add("Trk/hNSigmaVsP", ";#it{p} (GeV/#it{c});n#sigma^{TPC}", {HistType::kTH2F, {{200, 0., 2.}, {100, -10., 10.}}}); + registry.add("Trk/hHasIts", ";has ITS;entries", {HistType::kTH1F, {{2, -0.5, 1.5}}}); + registry.add("Trk/hIsPvContrib", ";is PV contributor;entries", {HistType::kTH1F, {{2, -0.5, 1.5}}}); + registry.add("Trk/hTpcNClsFound", ";N_{cls} TPC;entries", {HistType::kTH1F, {{90, 60, 150.}}}); + registry.add("Trk/hItsChi2NCl", ";#chi^{2}/N_{cls} ITS;entries", {HistType::kTH1F, {{100, 0., 10.}}}); + registry.add("Trk/hItsNCls", ";N_{cls} ITS;entries", {HistType::kTH1F, {{8, -0.5, 7.5}}}); + registry.add("Trk/hItsNClsInnerBarrel", ";N_{cls} ITS Inner Barrel;entries", {HistType::kTH1F, {{4, -0.5, 3.5}}}); + registry.add("Trk/hDcaXY", ";DCA_{xy} (cm);entries", {HistType::kTH1F, {{140, -0.035, 0.035}}}); + registry.add("Trk/hDcaZ", ";DCA_{z} (cm);entries", {HistType::kTH1F, {{200, -0.05, 0.05}}}); + + // track level (prongs of the candidate, after collision matching) + registry.addClone("Trk/", "TrkColl/"); + registry.add("TrkColl/hTrackSizePerEvent", ";#it{N}_{tracks} per event;entries", {HistType::kTH1F, {{10, -0.5, 9.5}}}); + + // track level plots for ambiguous tracks + registry.addClone("Trk/", "TrkAmb/"); + + // collision Id counter + registry.add("Coll/hCollIdCounter", ";collision ID;entries", {HistType::kTH1F, {{3, -1.5, 1.5}}}); + + // candidate level plots + registry.add("Cand/hMass", ";m_{#pi#pi} (GeV/#it{c}^{2});entries", {HistType::kTH1F, {axisMass}}); + registry.add("Cand/hPt", ";#it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); + registry.add("Cand/hRapidity", ";#it{y};entries", {HistType::kTH1F, {axisY}}); + registry.add("Cand/hPtVsRapidity", ";#it{p}_{T} (GeV/#it{c});#it{y}", {HistType::kTH2F, {axisPt, axisY}}); + + // MC histograms are only booked if at least one MC process function is enabled + const bool needMc = doprocessRhoTracksBeforeGroupingMcInfo || doprocessJpsiTracksBeforeGroupingMcInfo || + doprocessRhoCandMcInfo || doprocessJpsiCandMcInfo || doprocessMcGenExclusive || doprocessMcGenEmbedded; + if (!needMc) { + return; + } + + // MC reco matched plots + registry.addClone("Cand/", "Matched/Cand/"); + + registry.addClone("Coll/hNContrib", "Matched/Coll/hNContrib"); + registry.addClone("Coll/hVtxZ", "Matched/Coll/hVtxZ"); + registry.addClone("Coll/hVtxX", "Matched/Coll/hVtxX"); + registry.addClone("Coll/hVtxY", "Matched/Coll/hVtxY"); + + registry.add("Matched/Trk/hPt", ";#it{p}_{T}^{MC matched} (GeV/#it{c});entries", {HistType::kTH1F, {axisPtTrk}}); + registry.add("Matched/Trk/hEta", ";#eta^{MC};entries", hEtaTrk); + registry.add("Matched/Trk/hMcIsPhysicalPrimary", ";is physical primary;entries", {HistType::kTH1F, {{2, -0.5, 1.5}}}); + registry.add("Matched/Trk/hMotherPdg", ";mother PDG code;entries", {HistType::kTH1F, {{1001, 0.5, 1000.5}}}); + + registry.addClone("Matched/Trk/", "Matched/TrkColl/"); + + // MC generated plots + registry.addClone("Matched/Coll/", "McGen/Coll/"); + + registry.addClone("Matched/Trk/", "McGen/Part/"); + + registry.add("McGen/MotherPart/hMotherPdg", ";mother PDG code;entries", {HistType::kTH1F, {{1001, 0.5, 1000.5}}}); + registry.add("McGen/MotherPart/hPt", ";#it{p}_{T}^{MC} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); + registry.add("McGen/MotherPart/hRapidity", ";#it{y}^{MC};entries", {HistType::kTH1F, {axisY}}); + registry.add("McGen/MotherPart/hPtVsRapidity", ";#it{p}_{T}^{MC} (GeV/#it{c});#it{y}^{MC}", {HistType::kTH2F, {axisPt, axisY}}); + + registry.addClone("TrkColl/hTrackSizePerEvent", "McGen/Part/hTrackSizePerEvent"); + + } // end init() + + // FUNCTIONS: all functions defined here + + //-------------------------------------------------------------------------------- + // get info on the run, given the run number. Fills info needed to use getTimeFrame() + void getRunInfo(int run) + { + auto runInfo = o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(ccdb->instance(), run); + sor = runInfo.sor; // in ms + auto orbitSOR = runInfo.orbitSOR; + auto orbitEOR = runInfo.orbitEOR; + orbitsPerTF = runInfo.orbitsPerTF; + bcSOR = orbitSOR * o2::constants::lhc::LHCMaxBunches; // first bc of the first orbit + nBCsPerTF = orbitsPerTF * o2::constants::lhc::LHCMaxBunches; // duration of TF in bcs + nTF = std::ceil((orbitEOR - orbitSOR) / orbitsPerTF); + } + + //-------------------------------------------------------------------------------- + // compute and return the TF for the given BC + int64_t getTimeFrame(int64_t globalBC) + { + return (globalBC - bcSOR) / nBCsPerTF; + } + + //-------------------------------------------------------------------------------- + // Basic single-track selection + template + bool isGoodTrack(TTrack const& track) + { + if (!track.hasTPC()) { + return false; + } + if (track.tpcNClsFound() < nMinTpcClusters) { + return false; + } + if (track.pt() < ptTrackMin || std::abs(track.eta()) > etaTrackMax) { + return false; + } + if (std::abs(track.dcaZ()) >= UnassignedDCA || std::abs(track.dcaXY()) >= UnassignedDCA) { + return false; + } + return true; + } + + //-------------------------------------------------------------------------------- + // Select the PID column at compile time + template + float getNSigma(TTrack const& track) + { + if constexpr (species == CandSpecies::kRho) { + return track.tpcNSigmaPi(); + } else { + return track.tpcNSigmaMu(); + } + } + + //-------------------------------------------------------------------------------- + // fills the histograms with info on track properties + // works for both all and associated tracks, and if MC is available or not + template + void fillTrackHistograms(TTrack const& track) + { + // fill the histos only for good tracks + if (!isGoodTrack(track)) { + return; + } + + registry.fill(HIST(TrackFolders[folder]) + HIST("hPt"), track.pt()); + registry.fill(HIST(TrackFolders[folder]) + HIST("hEta"), track.eta()); + registry.fill(HIST(TrackFolders[folder]) + HIST("hChi2NCl"), track.tpcChi2NCl()); + registry.fill(HIST(TrackFolders[folder]) + HIST("hTpcSignalVsP"), track.p(), track.tpcSignal()); + registry.fill(HIST(TrackFolders[folder]) + HIST("hNSigmaVsP"), track.p(), getNSigma(track)); + registry.fill(HIST(TrackFolders[folder]) + HIST("hHasIts"), static_cast(track.hasITS())); + registry.fill(HIST(TrackFolders[folder]) + HIST("hIsPvContrib"), static_cast(track.isPVContributor())); + registry.fill(HIST(TrackFolders[folder]) + HIST("hTpcNClsFound"), track.tpcNClsFound()); + registry.fill(HIST(TrackFolders[folder]) + HIST("hItsChi2NCl"), track.itsChi2NCl()); + registry.fill(HIST(TrackFolders[folder]) + HIST("hItsNCls"), track.itsNCls()); + registry.fill(HIST(TrackFolders[folder]) + HIST("hItsNClsInnerBarrel"), track.itsNClsInnerBarrel()); + registry.fill(HIST(TrackFolders[folder]) + HIST("hDcaXY"), track.dcaXY()); + registry.fill(HIST(TrackFolders[folder]) + HIST("hDcaZ"), track.dcaZ()); + + // if the MC info are available, fill the corresponding histograms + if constexpr (isMc) { + if (!track.has_mcParticle()) { + return; + } + auto mcPart = track.template mcParticle_as(); + registry.fill(HIST("Matched/") + HIST(TrackFolders[folder]) + HIST("hPt"), mcPart.pt()); + registry.fill(HIST("Matched/") + HIST(TrackFolders[folder]) + HIST("hEta"), mcPart.eta()); + registry.fill(HIST("Matched/") + HIST(TrackFolders[folder]) + HIST("hMcIsPhysicalPrimary"), static_cast(mcPart.isPhysicalPrimary())); + if (mcPart.has_mothers()) { + for (auto const& mcMother : mcPart.template mothers_as()) { + registry.fill(HIST("Matched/") + HIST(TrackFolders[folder]) + HIST("hMotherPdg"), std::abs(mcMother.pdgCode())); + } + } + } + } + + //-------------------------------------------------------------------------------- + // loop over tracks and fill the histograms with info on track properties + template + void loopOverTracksBeforeGrouping(TTrack const& tracks) + { + for (auto const& track : tracks) { + int32_t collId = track.collisionId(); + fillTrackHistograms(track); + if (collId < 0) { + fillTrackHistograms(track); // isMc always false here since it fills Matched/.. histos + } + } + } + + //-------------------------------------------------------------------------------- + // fills the histograms with info on candidate properties + template + void fillCandidateHistograms(TCollision const& collision, TTrack const& tracks, float candMass) + { + registry.fill(HIST("Coll/hNContrib"), collision.numContrib()); + registry.fill(HIST("Coll/hBCid"), collision.bcId()); + registry.fill(HIST("Coll/hVtxZ"), collision.posZ()); + registry.fill(HIST("Coll/hVtxX"), collision.posX()); + registry.fill(HIST("Coll/hVtxY"), collision.posY()); + registry.fill(HIST("Coll/hVtxChi2"), collision.chi2()); + if constexpr (isMc) { + if (collision.has_mcCollision()) { + auto mcColl = collision.template mcCollision_as(); + registry.fill(HIST("Matched/Coll/hNContrib"), collision.numContrib()); + registry.fill(HIST("Matched/Coll/hVtxX"), mcColl.posX()); + registry.fill(HIST("Matched/Coll/hVtxY"), mcColl.posY()); + registry.fill(HIST("Matched/Coll/hVtxZ"), mcColl.posZ()); + } + } + + // Select tracks for the candidate + std::vector goodTracks; + for (auto const& track : tracks) { + if (isGoodTrack(track)) { + goodTracks.push_back(track); + // use TrkColl directory + fillTrackHistograms(track); + } + } + + registry.fill(HIST("TrkColl/hTrackSizePerEvent"), goodTracks.size()); + + // build UPC candidates using events with exactly two opposite sign good tracks + if (goodTracks.size() != TwoTracks) { + return; + } + auto const& track0 = goodTracks[0]; + auto const& track1 = goodTracks[1]; + if (track0.sign() * track1.sign() >= 0) { + return; + } + + ROOT::Math::PxPyPzMVector p0(track0.px(), track0.py(), track0.pz(), candMass); + ROOT::Math::PxPyPzMVector p1(track1.px(), track1.py(), track1.pz(), candMass); + auto candidate = p0 + p1; + + registry.fill(HIST("Cand/hMass"), candidate.M()); + registry.fill(HIST("Cand/hPt"), candidate.Pt()); + registry.fill(HIST("Cand/hRapidity"), candidate.Rapidity()); + registry.fill(HIST("Cand/hPtVsRapidity"), candidate.Pt(), candidate.Rapidity()); + + if constexpr (isMc) { + if (track0.has_mcParticle() && track1.has_mcParticle()) { + auto mcPart0 = track0.template mcParticle_as(); + auto mcPart1 = track1.template mcParticle_as(); + ROOT::Math::PxPyPzMVector mcP0(mcPart0.px(), mcPart0.py(), mcPart0.pz(), candMass); + ROOT::Math::PxPyPzMVector mcP1(mcPart1.px(), mcPart1.py(), mcPart1.pz(), candMass); + auto mcCandidate = mcP0 + mcP1; + registry.fill(HIST("Matched/Cand/hMass"), mcCandidate.M()); + registry.fill(HIST("Matched/Cand/hPt"), mcCandidate.Pt()); + registry.fill(HIST("Matched/Cand/hRapidity"), mcCandidate.Rapidity()); + registry.fill(HIST("Matched/Cand/hPtVsRapidity"), mcCandidate.Pt(), mcCandidate.Rapidity()); + } + } + } + + //-------------------------------------------------------------------------------- + // generated (MC truth) level: daughters and their mothers + template + void checkMcGen(aod::McCollision const& mcCollision, aod::McParticles const& mcParticles) + { + + registry.fill(HIST("McGen/Coll/hVtxX"), mcCollision.posX()); + registry.fill(HIST("McGen/Coll/hVtxY"), mcCollision.posY()); + registry.fill(HIST("McGen/Coll/hVtxZ"), mcCollision.posZ()); + + std::vector goodMcGenParticles; + for (auto const& mcPart : mcParticles) { + + if (!mcPart.isPhysicalPrimary() || mcPart.pt() < ptTrackMin || std::abs(mcPart.eta()) > etaTrackMax) { + continue; + } + registry.fill(HIST("McGen/Part/hPt"), mcPart.pt()); + registry.fill(HIST("McGen/Part/hEta"), mcPart.eta()); + + goodMcGenParticles.push_back(mcPart); + } + registry.fill(HIST("McGen/Part/hTrackSizePerEvent"), goodMcGenParticles.size()); + + // consider only j/psi->mumu and rho->pipi events + auto sourceId = mcCollision.getSourceId(); + + if constexpr (isEmbedded) { + if (!sourceId) { + return; + } + } else { + if (sourceId) { + LOGF(fatal, "Source Id: {%d} is not zero for non-embedded MC", sourceId); + return; + } + } + + if (goodMcGenParticles.size() != TwoTracks) { + return; + } + + // we have 2 good tracks, with the same mother, taking it from the first one + auto mothers = goodMcGenParticles[0].template mothers_as(); + + if (mothers.empty()) { + LOGF(fatal, "Selected particle has no mother: pdgCode=%d", goodMcGenParticles[0].pdgCode()); + return; + } + auto const& motherPart = mothers[0]; + + registry.fill(HIST("McGen/MotherPart/hMotherPdg"), std::abs(motherPart.pdgCode())); + registry.fill(HIST("McGen/MotherPart/hPt"), motherPart.pt()); + registry.fill(HIST("McGen/MotherPart/hRapidity"), motherPart.y()); + registry.fill(HIST("McGen/MotherPart/hPtVsRapidity"), motherPart.pt(), motherPart.y()); + } + + //-------------------------------------------------------------------------------- + // PROCESS FUNCTIONS + //-------------------------------------------------------------------------------- + + //-------------------------------------------------------------------------------- + // check collisionId and TF + void processTracksIU(TracksExtraIU const& tracks, aod::Collisions const&, aod::BCs const&) + { + // track loop + for (const auto& track : tracks) { + + if (!isGoodTrack(track)) { + continue; + } + + int32_t collId = track.collisionId(); + + if (collId < 0) { + registry.fill(HIST("Coll/hCollIdCounter"), -1); + } else if (collId > 0) { + registry.fill(HIST("Coll/hCollIdCounter"), 1); + } else { + registry.fill(HIST("Coll/hCollIdCounter"), 0); + } + + if (collId < 0) { + continue; + } + + auto collision = track.collision(); + auto bc = collision.bc(); + + getRunInfo(bc.runNumber()); + + registry.fill(HIST("Coll/hnTF"), getTimeFrame(bc.globalBC())); + } + } + + // look at all tracks + void processRhoTracksBeforeGrouping(TracksExtraWPidPi const& tracks) + { + loopOverTracksBeforeGrouping(tracks); + } + PROCESS_SWITCH(UpcTrackVertexingQA, processRhoTracksBeforeGrouping, "Process rho tracks before asking for collisions", true); + + void processJpsiTracksBeforeGrouping(TracksExtraWPidMu const& tracks) + { + loopOverTracksBeforeGrouping(tracks); + } + PROCESS_SWITCH(UpcTrackVertexingQA, processJpsiTracksBeforeGrouping, "Process J/Psi tracks before asking for collisions", false); + + void processRhoTracksBeforeGroupingMcInfo(TracksExtraWPidPiMc const& tracks, aod::McParticles const&) + { + loopOverTracksBeforeGrouping(tracks); + } + PROCESS_SWITCH(UpcTrackVertexingQA, processRhoTracksBeforeGroupingMcInfo, "Process rho tracks before asking for collisions", false); + + void processJpsiTracksBeforeGroupingMcInfo(TracksExtraWPidMuMc const& tracks, aod::McParticles const&) + { + loopOverTracksBeforeGrouping(tracks); + } + PROCESS_SWITCH(UpcTrackVertexingQA, processJpsiTracksBeforeGroupingMcInfo, "Process J/Psi tracks before asking for collisions", false); + + // look at matched tracks + void processRhoCand(aod::Collision const& collision, TracksExtraWPidPi const& tracks) + { + fillCandidateHistograms(collision, tracks, MassPion); + } + PROCESS_SWITCH(UpcTrackVertexingQA, processRhoCand, "Rho -> pi pi candidates and track QA", true); + + void processJpsiCand(aod::Collision const& collision, TracksExtraWPidMu const& tracks) + { + fillCandidateHistograms(collision, tracks, MassMuon); + } + PROCESS_SWITCH(UpcTrackVertexingQA, processJpsiCand, "J/Psi -> mu mu candidates and track QA", false); + + void processRhoCandMcInfo(CollisionsWithMc::iterator const& collision, TracksExtraWPidPiMc const& tracks, + aod::McParticles const&, aod::McCollisions const&) + { + fillCandidateHistograms(collision, tracks, MassPion); + } + PROCESS_SWITCH(UpcTrackVertexingQA, processRhoCandMcInfo, "Rho -> pi pi candidates and track QA", false); + + void processJpsiCandMcInfo(CollisionsWithMc::iterator const& collision, TracksExtraWPidMuMc const& tracks, + aod::McParticles const&, aod::McCollisions const&) + { + fillCandidateHistograms(collision, tracks, MassMuon); + } + PROCESS_SWITCH(UpcTrackVertexingQA, processJpsiCandMcInfo, "J/Psi -> mu mu candidates and track QA", false); + + // look at gen MC + void processMcGenEmbedded(aod::McCollision const& mcCollision, aod::McParticles const& mcParticles) + { + checkMcGen(mcCollision, mcParticles); + } + PROCESS_SWITCH(UpcTrackVertexingQA, processMcGenEmbedded, "Generated embedded events", false); + + void processMcGenExclusive(aod::McCollision const& mcCollision, aod::McParticles const& mcParticles) + { + checkMcGen(mcCollision, mcParticles); + } + PROCESS_SWITCH(UpcTrackVertexingQA, processMcGenExclusive, "Generated exclusive events", false); + + PROCESS_SWITCH(UpcTrackVertexingQA, processTracksIU, "Track QA for IU tracks", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGUD/Tasks/upcVmRof.cxx b/PWGUD/Tasks/upcVmRof.cxx index 5d112ad9ecb..715c573afb6 100644 --- a/PWGUD/Tasks/upcVmRof.cxx +++ b/PWGUD/Tasks/upcVmRof.cxx @@ -13,6 +13,7 @@ /// \brief analysis of UPC vector meson production ROF by ROF /// /// \author Guillermo Contreras (jesus.guillermo.contreras.nuno@cern.ch), Czech Technical University in Prague +/// \author Cesar Omar Ramirez Alvarez (cesar.ramirez@cern.ch), Autonomous University of Puebla #include "Common/CCDB/EventSelectionParams.h" #include "Common/DataModel/EventSelection.h" @@ -41,6 +42,7 @@ #include #include +#include #include #include #include @@ -48,6 +50,7 @@ #include #include #include +#include #include #include @@ -57,11 +60,14 @@ using namespace o2::framework::expressions; using BCsTSsSels = soa::Join; using ColSels = soa::Join; +using ColSelsMc = soa::Join; using TRKs = soa::Join; +using TRKsMc = soa::Join; using ColSel = ColSels::iterator; +using ColSelMc = ColSelsMc::iterator; namespace o2::aod { @@ -74,6 +80,7 @@ DECLARE_SOA_COLUMN(PosY, posY, float); DECLARE_SOA_COLUMN(PosZ, posZ, float); DECLARE_SOA_COLUMN(Chi2, chi2, float); DECLARE_SOA_COLUMN(LocalBC, localBC, int); +DECLARE_SOA_COLUMN(NearestBCB, nearestBCB, int); DECLARE_SOA_COLUMN(LocalTF, localTF, int); DECLARE_SOA_COLUMN(LocalROF, localROF, int); DECLARE_SOA_COLUMN(UpcFlag, upcFlag, int); @@ -140,9 +147,49 @@ DECLARE_SOA_COLUMN(HasTof3, hasTof3, int); DECLARE_SOA_COLUMN(HasTof4, hasTof4, int); } // namespace datarows +namespace mcgen +{ +DECLARE_SOA_COLUMN(McMotherPdg, mcMotherPdg, int); +DECLARE_SOA_COLUMN(McMotherPt, mcMotherPt, float); +DECLARE_SOA_COLUMN(McMotherPhi, mcMotherPhi, float); +DECLARE_SOA_COLUMN(McMotherMass, mcMotherMass, float); +DECLARE_SOA_COLUMN(McMotherRapidity, mcMotherRapidity, float); + +DECLARE_SOA_COLUMN(McPdg1, mcPdg1, int); +DECLARE_SOA_COLUMN(McPt1, mcPt1, float); +DECLARE_SOA_COLUMN(McEta1, mcEta1, float); +DECLARE_SOA_COLUMN(McPhi1, mcPhi1, float); +DECLARE_SOA_COLUMN(McQ1, mcQ1, int); +DECLARE_SOA_COLUMN(IsPrimary1, isPrimary1, int); +DECLARE_SOA_COLUMN(McPdg2, mcPdg2, int); +DECLARE_SOA_COLUMN(McPt2, mcPt2, float); +DECLARE_SOA_COLUMN(McEta2, mcEta2, float); +DECLARE_SOA_COLUMN(McPhi2, mcPhi2, float); +DECLARE_SOA_COLUMN(McQ2, mcQ2, int); +DECLARE_SOA_COLUMN(IsPrimary2, isPrimary2, int); +DECLARE_SOA_COLUMN(McPdg3, mcPdg3, int); +DECLARE_SOA_COLUMN(McPt3, mcPt3, float); +DECLARE_SOA_COLUMN(McEta3, mcEta3, float); +DECLARE_SOA_COLUMN(McPhi3, mcPhi3, float); +DECLARE_SOA_COLUMN(McQ3, mcQ3, int); +DECLARE_SOA_COLUMN(IsPrimary3, isPrimary3, int); +DECLARE_SOA_COLUMN(McPdg4, mcPdg4, int); +DECLARE_SOA_COLUMN(McPt4, mcPt4, float); +DECLARE_SOA_COLUMN(McEta4, mcEta4, float); +DECLARE_SOA_COLUMN(McPhi4, mcPhi4, float); +DECLARE_SOA_COLUMN(McQ4, mcQ4, int); +DECLARE_SOA_COLUMN(IsPrimary4, isPrimary4, int); + +DECLARE_SOA_COLUMN(McPosX, mcPosX, float); +DECLARE_SOA_COLUMN(McPosY, mcPosY, float); +DECLARE_SOA_COLUMN(McPosZ, mcPosZ, float); +DECLARE_SOA_COLUMN(McLocalBC, mcLocalBC, int); +DECLARE_SOA_COLUMN(RecoIndex, recoIndex, int); +} // namespace mcgen + DECLARE_SOA_TABLE(TwoTrkTable, "AOD", "TWOTRKTABLE", datarows::RunNumber, datarows::PosX, datarows::PosY, datarows::PosZ, datarows::Chi2, - datarows::LocalBC, datarows::LocalTF, datarows::LocalROF, datarows::UpcFlag, + datarows::LocalBC, datarows::NearestBCB, datarows::LocalTF, datarows::LocalROF, datarows::UpcFlag, datarows::AmplitudeFT0A, datarows::AmplitudeFT0C, datarows::AmplitudeFV0A, datarows::AmplitudeFDDA, datarows::AmplitudeFDDC, datarows::TimeFT0A, datarows::TimeFT0C, datarows::TimeFV0A, datarows::TimeFDDA, datarows::TimeFDDC, datarows::ChannelsFT0A, datarows::ChannelsFT0C, datarows::ChannelsFV0A, datarows::ChannelsFDDA, datarows::ChannelsFDDC, @@ -152,7 +199,7 @@ DECLARE_SOA_TABLE(TwoTrkTable, "AOD", "TWOTRKTABLE", datarows::HasTof1, datarows::HasTof2); DECLARE_SOA_TABLE(FourTrkTable, "AOD", "FOURTRKTABLE", datarows::RunNumber, datarows::PosX, datarows::PosY, datarows::PosZ, datarows::Chi2, - datarows::LocalBC, datarows::LocalTF, datarows::LocalROF, datarows::UpcFlag, + datarows::LocalBC, datarows::NearestBCB, datarows::LocalTF, datarows::LocalROF, datarows::UpcFlag, datarows::AmplitudeFT0A, datarows::AmplitudeFT0C, datarows::AmplitudeFV0A, datarows::AmplitudeFDDA, datarows::AmplitudeFDDC, datarows::TimeFT0A, datarows::TimeFT0C, datarows::TimeFV0A, datarows::TimeFDDA, datarows::TimeFDDC, datarows::ChannelsFT0A, datarows::ChannelsFT0C, datarows::ChannelsFV0A, datarows::ChannelsFDDA, datarows::ChannelsFDDC, @@ -162,6 +209,32 @@ DECLARE_SOA_TABLE(FourTrkTable, "AOD", "FOURTRKTABLE", datarows::Pt3, datarows::Eta3, datarows::Phi3, datarows::Q3, datarows::PidPion3, datarows::PidElectron3, datarows::PidKaon3, datarows::PidProton3, datarows::Pt4, datarows::Eta4, datarows::Phi4, datarows::Q4, datarows::PidPion4, datarows::PidElectron4, datarows::PidKaon4, datarows::PidProton4, datarows::HasTof1, datarows::HasTof2, datarows::HasTof3, datarows::HasTof4); + +DECLARE_SOA_TABLE(TwoTrkRecGenTable, "AOD", "TWOTRKRECGEN", + mcgen::McMotherPdg, mcgen::McMotherPt, mcgen::McMotherPhi, mcgen::McMotherMass, mcgen::McMotherRapidity, + mcgen::McPdg1, mcgen::McPt1, mcgen::McEta1, mcgen::McPhi1, mcgen::McQ1, mcgen::IsPrimary1, + mcgen::McPdg2, mcgen::McPt2, mcgen::McEta2, mcgen::McPhi2, mcgen::McQ2, mcgen::IsPrimary2, + mcgen::McPosX, mcgen::McPosY, mcgen::McPosZ, mcgen::McLocalBC, mcgen::RecoIndex); +DECLARE_SOA_TABLE(FourTrkRecGenTable, "AOD", "FOURTRKRECGEN", + mcgen::McMotherPdg, mcgen::McMotherPt, mcgen::McMotherPhi, mcgen::McMotherMass, mcgen::McMotherRapidity, + mcgen::McPdg1, mcgen::McPt1, mcgen::McEta1, mcgen::McPhi1, mcgen::McQ1, mcgen::IsPrimary1, + mcgen::McPdg2, mcgen::McPt2, mcgen::McEta2, mcgen::McPhi2, mcgen::McQ2, mcgen::IsPrimary2, + mcgen::McPdg3, mcgen::McPt3, mcgen::McEta3, mcgen::McPhi3, mcgen::McQ3, mcgen::IsPrimary3, + mcgen::McPdg4, mcgen::McPt4, mcgen::McEta4, mcgen::McPhi4, mcgen::McQ4, mcgen::IsPrimary4, + mcgen::McPosX, mcgen::McPosY, mcgen::McPosZ, mcgen::McLocalBC, mcgen::RecoIndex); + +DECLARE_SOA_TABLE(TwoTrkGenTable, "AOD", "TWOTRKGEN", + mcgen::McMotherPdg, mcgen::McMotherPt, mcgen::McMotherPhi, mcgen::McMotherMass, mcgen::McMotherRapidity, + mcgen::McPdg1, mcgen::McPt1, mcgen::McEta1, mcgen::McPhi1, mcgen::McQ1, mcgen::IsPrimary1, + mcgen::McPdg2, mcgen::McPt2, mcgen::McEta2, mcgen::McPhi2, mcgen::McQ2, mcgen::IsPrimary2, + mcgen::McPosX, mcgen::McPosY, mcgen::McPosZ, mcgen::McLocalBC); +DECLARE_SOA_TABLE(FourTrkGenTable, "AOD", "FOURTRKGEN", + mcgen::McMotherPdg, mcgen::McMotherPt, mcgen::McMotherPhi, mcgen::McMotherMass, mcgen::McMotherRapidity, + mcgen::McPdg1, mcgen::McPt1, mcgen::McEta1, mcgen::McPhi1, mcgen::McQ1, mcgen::IsPrimary1, + mcgen::McPdg2, mcgen::McPt2, mcgen::McEta2, mcgen::McPhi2, mcgen::McQ2, mcgen::IsPrimary2, + mcgen::McPdg3, mcgen::McPt3, mcgen::McEta3, mcgen::McPhi3, mcgen::McQ3, mcgen::IsPrimary3, + mcgen::McPdg4, mcgen::McPt4, mcgen::McEta4, mcgen::McPhi4, mcgen::McQ4, mcgen::IsPrimary4, + mcgen::McPosX, mcgen::McPosY, mcgen::McPosZ, mcgen::McLocalBC); } // namespace o2::aod struct UpcVmRof { @@ -169,6 +242,10 @@ struct UpcVmRof { // output Produces twoTrkTable; Produces fourTrkTable; + Produces twoTrkRecGenTable; + Produces fourTrkRecGenTable; + Produces twoTrkGenTable; + Produces fourTrkGenTable; // services Service ccdb{}; // access to database @@ -188,6 +265,9 @@ struct UpcVmRof { std::bitset bcPatternB; std::bitset bcPatternC; std::vector bcbIdx; + std::vector vecNearestBCB; + std::vector vecDistNearestBCB; + int nbcB = 0; // variables to store ITS ROF info @@ -230,6 +310,9 @@ struct UpcVmRof { Configurable minTrkTpcClusters{"minTrkTpcClusters", 70.0, "minimum number of TPC clusters associated to the track"}; Configurable maxTrkDcaZ{"maxTrkDcaZ", 2.0, "max DCA in z of track to vtx (cm)"}; Configurable tfPerBin{"tfPerBin", 10000, "timeframes per bin 1e4 means some 28 s"}; + Configurable genId{"genId", -1, "generator ID; -1 = no filter"}; + Configurable requireVtxItsTpc{"requireVtxItsTpc", false, "require kIsVertexITSTPC"}; + Configurable requireNoSameBunchPileup{"requireNoSameBunchPileup", false, "require kNoSameBunchPileup"}; //-------------------------------------------------------------------------------- // get ITS ROF info @@ -239,7 +322,64 @@ struct UpcVmRof { auto alppar = ccdb->getForTimeStamp>("ITS/Config/AlpideParam", sor); rofShift = alppar->roFrameBiasInBC; rofLength = alppar->roFrameLengthInBC; - rofPerOrbit = static_cast(o2::constants::lhc::LHCMaxBunches / rofLength); + rofPerOrbit = (o2::constants::lhc::LHCMaxBunches / rofLength); + } + + //-------------------------------------------------------------------------------- + // find nearest bcb for this filling scheme + void setNearestBCB() + { + vecNearestBCB.clear(); + vecDistNearestBCB.clear(); + for (int i = 0; i < o2::constants::lhc::LHCMaxBunches; i++) { + // extend vector + vecNearestBCB.push_back(-1); + vecDistNearestBCB.push_back(-1); + // are we in a bc-b? + if (bcPatternB.test(i)) { + vecNearestBCB[i] = i; + vecDistNearestBCB[i] = 0; + continue; + } + // find nearest previous bc-b + int nearestLeft = -1; + for (int j = 1; j < o2::constants::lhc::LHCMaxBunches; j++) { + int k = i - j; + if (k < 0) + k = o2::constants::lhc::LHCMaxBunches + k; + if (bcPatternB.test(k)) { + nearestLeft = k; + break; + } + } // end nearestLeft loop + + // find nearest next bc-b + int nearesRight = -1; + for (int j = 1; j < o2::constants::lhc::LHCMaxBunches; j++) { + int k = i + j; + if (k > (o2::constants::lhc::LHCMaxBunches - 1)) + k = k - o2::constants::lhc::LHCMaxBunches; + if (bcPatternB.test(k)) { + nearesRight = k; + break; + } + } // end nearesRight loop + + // find nearest bc-b + int dLeft = i - nearestLeft; + // cppcheck-suppress knownConditionTrueFalse + if (dLeft < 0) + dLeft += o2::constants::lhc::LHCMaxBunches; + int dRight = nearesRight - i; + // cppcheck-suppress knownConditionTrueFalse + if (dRight < 0) + dRight += o2::constants::lhc::LHCMaxBunches; + int dMin = std::min(dLeft, dRight); + int nearest = ((dMin == dLeft) ? nearestLeft : nearesRight); + vecNearestBCB[i] = nearest; + int dist = ((dMin == dLeft) ? -dLeft : dRight); + vecDistNearestBCB[i] = dist; + } // end loop over bc } //-------------------------------------------------------------------------------- @@ -354,11 +494,11 @@ struct UpcVmRof { return false; } colTH1Pointers[Form("col/%d/colSel_H", run)]->Fill(3); - if (!col.selection_bit(aod::evsel::kIsVertexITSTPC)) { + if (requireVtxItsTpc && !col.selection_bit(aod::evsel::kIsVertexITSTPC)) { return false; } colTH1Pointers[Form("col/%d/colSel_H", run)]->Fill(4); - if (!col.selection_bit(aod::evsel::kNoSameBunchPileup)) { + if (requireNoSameBunchPileup && !col.selection_bit(aod::evsel::kNoSameBunchPileup)) { return false; } colTH1Pointers[Form("col/%d/colSel_H", run)]->Fill(5); @@ -460,6 +600,109 @@ struct UpcVmRof { ccdb->setFatalWhenNull(false); } // end init() + //-------------------------------------------------------------------------------- + // helper function for MC: pt + static float mcPt(float px, float py) { return std::sqrt(px * px + py * py); } + + //-------------------------------------------------------------------------------- + // helper function for MC: pseudorapidity + static float mcEta(float px, float py, float pz) + { + float p = std::sqrt(px * px + py * py + pz * pz); + if (std::abs(p - std::abs(pz)) > 1e-10) { + return 0.5f * std::log((p + pz) / (p - pz)); + } + return 0.0f; + } + + //-------------------------------------------------------------------------------- + // helper function for MC: azimuth + static float mcPhi(float px, float py) + { + if (std::abs(px) > 1e-10 || std::abs(py) > 1e-10) { + return std::atan2(py, px); + } + return 0.0f; + } + + //-------------------------------------------------------------------------------- + // helper function for MC: rapidity + static float mcRapidity(float px, float py, float pz, float mass) + { + float energy = std::sqrt(px * px + py * py + pz * pz + mass * mass); + if (std::abs(energy - std::abs(pz)) > 1e-10) { + return 0.5f * std::log((energy + pz) / (energy - pz)); + } + return 0.0f; + } + + //-------------------------------------------------------------------------------- + // mother search + struct MotherInfo { + bool found = false; + int pdg = 0; + float px = 0, py = 0, pz = 0, mass = 0; + }; + + //-------------------------------------------------------------------------------- + // checks if all given daughters share the same direct mother: found=false if not + template + MotherInfo findCommonMother(aod::McParticles const& mcParticles, const std::vector& daughters) + { + MotherInfo info; + if (daughters.empty() || !daughters[0].has_mothers()) { + return info; + } + int64_t motherIdx = daughters[0].mothersIds()[0]; + for (size_t i = 1; i < daughters.size(); i++) { + if (!daughters[i].has_mothers() || daughters[i].mothersIds()[0] != motherIdx) { + return info; + } + } + auto mother = mcParticles.rawIteratorAt(motherIdx); + info.found = true; + info.pdg = mother.pdgCode(); + info.px = mother.px(); + info.py = mother.py(); + info.pz = mother.pz(); + info.mass = std::sqrt(mother.e() * mother.e() - mother.px() * mother.px() - mother.py() * mother.py() - mother.pz() * mother.pz()); + return info; + } + + //-------------------------------------------------------------------------------- + // for processMcGenAll: same mother search. + template + MotherInfo findCommonMotherFromIters(/* aod::McParticles const& mcParticles, */ const std::vector& daughters) + { + MotherInfo info; + if (daughters.empty() || !daughters[0].has_mothers()) { + return info; + } + auto firstMothers = daughters[0].template mothers_as(); + if (firstMothers.begin() == firstMothers.end()) { + return info; + } + auto motherIt = firstMothers.begin(); + int64_t motherGlobalIndex = motherIt->globalIndex(); + for (size_t i = 1; i < daughters.size(); i++) { + if (!daughters[i].has_mothers()) { + return info; + } + auto iMothers = daughters[i].template mothers_as(); + if (iMothers.begin() == iMothers.end() || iMothers.begin()->globalIndex() != motherGlobalIndex) { + return info; + } + } + const auto& mother = *motherIt; + info.found = true; + info.pdg = mother.pdgCode(); + info.px = mother.px(); + info.py = mother.py(); + info.pz = mother.pz(); + info.mass = std::sqrt(mother.e() * mother.e() - mother.px() * mother.px() - mother.py() * mother.py() - mother.pz() * mother.pz()); + return info; + } + //-------------------------------------------------------------------------------- // process BCs to get trigger information void processBCs(BCsTSsSels const& bcs) @@ -528,11 +771,17 @@ struct UpcVmRof { PROCESS_SWITCH(UpcVmRof, processBCs, "get BCs and trigger information", true); //-------------------------------------------------------------------------------- - // get collision information - void processCols(ColSel const& col, BCsTSsSels const&, TRKs const& tracks, - aod::FV0As const&, aod::FT0s const&, aod::FDDs const&, - aod::Zdcs const&) + // shared collision+track selection; used by both real data and MC + template + std::vector fillCollisionTables( + ColType const& col, BCsTSsSels const& bcs, TracksType const& tracks, + aod::FV0As const&, aod::FT0s const&, aod::FDDs const&, aod::Zdcs const&, + bool& outIsTwoBody, bool& outIsFourBody) { + outIsTwoBody = false; + outIsFourBody = false; + std::vector selTrks; + // get info for this bc auto bc = col.template foundBC_as(); if (runNumberCol != bc.runNumber()) { // new run @@ -540,6 +789,7 @@ struct UpcVmRof { getRunInfo(runNumberCol); getFillingScheme(); addColHistos(runNumberCol); + setNearestBCB(); } int64_t thisBC = getBcWithinOrbit(bc.globalBC()); int64_t thisTF = getTimeFrame(bc.globalBC()); @@ -547,18 +797,14 @@ struct UpcVmRof { // select collision if (!checkColFlags(col, runNumberCol)) { - return; + return selTrks; } - // accept only -B bcs - if (!bcPatternB.test(thisBC)) { - return; - } colTH1Pointers[Form("col/%d/colSel_H", runNumberCol)]->Fill(10); // select on zVtx if (std::abs(col.posZ()) > maxAbsPosZ) { - return; + return selTrks; } colTH1Pointers[Form("col/%d/colSel_H", runNumberCol)]->Fill(11); @@ -566,7 +812,7 @@ struct UpcVmRof { bool isTwoContributors = (col.numContrib() == NTrksTwoBody); bool isFourContributors = (col.numContrib() == NTrksFourBody); if (!isTwoContributors && !isFourContributors) { - return; + return selTrks; } if (isTwoContributors) { colTH1Pointers[Form("col/%d/colSel_H", runNumberCol)]->Fill(16); @@ -576,7 +822,6 @@ struct UpcVmRof { } // select tracks - std::vector selTrks; colTH1Pointers[Form("col/%d/trkSel_H", runNumberCol)]->Fill(0); for (const auto& track : tracks) { if (!track.isPVContributor()) { @@ -623,7 +868,7 @@ struct UpcVmRof { bool isTwoBody = isTwoContributors && (selTrks.size() == NTrksTwoBody); bool isFourBody = isFourContributors && (selTrks.size() == NTrksFourBody); if (!isTwoBody && !isFourBody) { - return; + return selTrks; } // selected events @@ -634,6 +879,40 @@ struct UpcVmRof { colTH1Pointers[Form("col/%d/colSel_H", runNumberCol)]->Fill(19); } + // find the bc row for the nearest bc if different from the current one + bool foundBCB = true; + auto nearbcb = bcs.iteratorAt(bc.globalIndex()); + if (vecDistNearestBCB[thisBC] > 0) { + foundBCB = false; + auto gidxBCB = bc.globalIndex() + vecDistNearestBCB[thisBC]; + if (gidxBCB <= bcs.iteratorAt(bcs.size() - 1).globalIndex()) { + while (nearbcb != bcs.end()) { + ++nearbcb; + if (nearbcb.globalIndex() > gidxBCB) { + break; + } else if (getBcWithinOrbit(nearbcb.globalBC()) == vecNearestBCB[thisBC]) { + foundBCB = true; + break; + } + } // end while + } + } // end search in future direction + if (vecDistNearestBCB[thisBC] < 0) { + foundBCB = false; + auto gidxBCB = bc.globalIndex() + vecDistNearestBCB[thisBC]; + if (gidxBCB >= bcs.iteratorAt(0).globalIndex()) { + while (nearbcb != bcs.iteratorAt(0)) { + --nearbcb; + if (nearbcb.globalIndex() < gidxBCB) { + break; + } else if (getBcWithinOrbit(nearbcb.globalBC()) == vecNearestBCB[thisBC]) { + foundBCB = true; + break; + } + } // end while + } + } // end search in past direction + // FT0 selection float aFT0A = 0; float aFT0C = 0; @@ -641,34 +920,34 @@ struct UpcVmRof { float tFT0C = 33; // default time to mark events without FT0 info int nFT0A = 0; int nFT0C = 0; - if (bc.has_foundFT0()) { + if (foundBCB && nearbcb.has_foundFT0()) { // a side - if (bc.foundFT0().isValidTimeA()) { // valid time - tFT0A = bc.foundFT0().timeA(); + if (nearbcb.foundFT0().isValidTimeA()) { // valid time + tFT0A = nearbcb.foundFT0().timeA(); if (std::abs(tFT0A) > maxAbsTimeFT0) { - return; + return selTrks; } colTH1Pointers[Form("col/%d/colSel_H", runNumberCol)]->Fill(12); - aFT0A = bc.foundFT0().sumAmpA(); + aFT0A = nearbcb.foundFT0().sumAmpA(); if (aFT0A > maxAmpFT0) { - return; + return selTrks; } colTH1Pointers[Form("col/%d/colSel_H", runNumberCol)]->Fill(13); - nFT0A = (bc.foundFT0().amplitudeA()).size(); + nFT0A = (nearbcb.foundFT0().amplitudeA()).size(); } // a side // c side - if (bc.foundFT0().isValidTimeC()) { // valid time - tFT0C = bc.foundFT0().timeC(); + if (nearbcb.foundFT0().isValidTimeC()) { // valid time + tFT0C = nearbcb.foundFT0().timeC(); if (std::abs(tFT0C) > maxAbsTimeFT0) { - return; + return selTrks; } colTH1Pointers[Form("col/%d/colSel_H", runNumberCol)]->Fill(14); - aFT0C = bc.foundFT0().sumAmpC(); + aFT0C = nearbcb.foundFT0().sumAmpC(); if (aFT0C > maxAmpFT0) { - return; + return selTrks; } colTH1Pointers[Form("col/%d/colSel_H", runNumberCol)]->Fill(15); - nFT0C = (bc.foundFT0().amplitudeC()).size(); + nFT0C = (nearbcb.foundFT0().amplitudeC()).size(); } // c side } // FT0 selection @@ -684,9 +963,9 @@ struct UpcVmRof { float aFV0A = 0; float tFV0A = 33; // default time to mark events without FV0 info int nFV0A = 0; - if (bc.has_foundFV0()) { - tFV0A = bc.foundFV0().time(); - auto v = bc.foundFV0().amplitude(); + if (foundBCB && nearbcb.has_foundFV0()) { + tFV0A = nearbcb.foundFV0().time(); + auto v = nearbcb.foundFV0().amplitude(); aFV0A = std::accumulate(v.begin(), v.end(), 0.f); nFV0A = v.size(); } // FV0A info @@ -698,9 +977,9 @@ struct UpcVmRof { float aFDDC = 0; float tFDDC = 33; // default time to mark events without FDD info int nFDDC = 0; - if (bc.has_foundFDD()) { - tFDDA = bc.foundFDD().timeA(); - auto vA = bc.foundFDD().chargeA(); + if (foundBCB && nearbcb.has_foundFDD()) { + tFDDA = nearbcb.foundFDD().timeA(); + auto vA = nearbcb.foundFDD().chargeA(); // channelPairs = {{0, 4}, {1, 5}, {2, 6}, {3, 7}}; if (vA[0] > 0 && vA[4] > 0) { aFDDA += 0.5 * (vA[0] + vA[4]); @@ -718,8 +997,8 @@ struct UpcVmRof { aFDDA += 0.5 * (vA[3] + vA[7]); nFDDA++; } - tFDDC = bc.foundFDD().timeC(); - auto vC = bc.foundFDD().chargeC(); + tFDDC = nearbcb.foundFDD().timeC(); + auto vC = nearbcb.foundFDD().chargeC(); // channelPairs = {{0, 4}, {1, 5}, {2, 6}, {3, 7}}; if (vC[0] > 0 && vC[4] > 0) { aFDDC += 0.5 * (vC[0] + vC[4]); @@ -744,11 +1023,11 @@ struct UpcVmRof { float tZNC = -999; // default time to mark events without ZN info float eZNA = -999; float eZNC = -999; - if (bc.has_zdc()) { - tZNA = (bc.zdc()).timeZNA(); - tZNC = (bc.zdc()).timeZNC(); - eZNA = (bc.zdc()).energyCommonZNA(); - eZNC = (bc.zdc()).energyCommonZNC(); + if (foundBCB && nearbcb.has_zdc()) { + tZNA = (nearbcb.zdc()).timeZNA(); + tZNC = (nearbcb.zdc()).timeZNC(); + eZNA = (nearbcb.zdc()).energyCommonZNA(); + eZNC = (nearbcb.zdc()).energyCommonZNC(); if (!std::isfinite(tZNA)) { tZNA = -999; } @@ -774,7 +1053,7 @@ struct UpcVmRof { tof[1] = 1; } colTH1Pointers[Form("col/%d/twoTrkTF_H", runNumberCol)]->Fill(thisTF); - twoTrkTable(runNumberCol, col.posX(), col.posY(), col.posZ(), col.chi2(), thisBC, thisTF, thisROF, recoFlag, + twoTrkTable(runNumberCol, col.posX(), col.posY(), col.posZ(), col.chi2(), thisBC, vecNearestBCB[thisBC], thisTF, thisROF, recoFlag, aFT0A, aFT0C, aFV0A, aFDDA, aFDDC, tFT0A, tFT0C, tFV0A, tFDDA, tFDDC, nFT0A, nFT0C, nFV0A, nFDDA, nFDDC, eZNA, eZNC, tZNA, tZNC, selTrks[0].pt(), selTrks[0].eta(), selTrks[0].phi(), selTrks[0].sign(), @@ -797,7 +1076,7 @@ struct UpcVmRof { tof[3] = 1; } colTH1Pointers[Form("col/%d/fourTrkTF_H", runNumberCol)]->Fill(thisTF); - fourTrkTable(runNumberCol, col.posX(), col.posY(), col.posZ(), col.chi2(), thisBC, thisTF, thisROF, recoFlag, + fourTrkTable(runNumberCol, col.posX(), col.posY(), col.posZ(), col.chi2(), thisBC, vecNearestBCB[thisBC], thisTF, thisROF, recoFlag, aFT0A, aFT0C, aFV0A, aFDDA, aFDDC, tFT0A, tFT0C, tFV0A, tFDDA, tFDDC, nFT0A, nFT0C, nFV0A, nFDDA, nFDDC, eZNA, eZNC, tZNA, tZNC, selTrks[0].pt(), selTrks[0].eta(), selTrks[0].phi(), selTrks[0].sign(), @@ -811,12 +1090,287 @@ struct UpcVmRof { tof[0], tof[1], tof[2], tof[3]); } - } // end processCol - PROCESS_SWITCH(UpcVmRof, processCols, "get collisions and track information", true); + outIsTwoBody = isTwoBody; + outIsFourBody = isFourBody; + return selTrks; + } // end fillCollisionTables + + //-------------------------------------------------------------------------------- + // process real data or MC at reconstruction level + void processDataCols(ColSel const& col, BCsTSsSels const& bcs, TRKs const& tracks, + aod::FV0As const& fv0s, aod::FT0s const& ft0s, aod::FDDs const& fdds, aod::Zdcs const& zdcs) + { + bool isTwoBody, isFourBody; + fillCollisionTables(col, bcs, tracks, fv0s, ft0s, fdds, zdcs, isTwoBody, isFourBody); + } // end processDataCols + PROCESS_SWITCH(UpcVmRof, processDataCols, "process real data or mc reco, no mc truth", true); + + //-------------------------------------------------------------------------------- + // MC only: fills the same reconstruction tables and the generated information of the reconstructed event + void processMcCols(ColSelMc const& col, BCsTSsSels const& bcs, TRKsMc const& tracks, + aod::FV0As const& fv0s, aod::FT0s const& ft0s, aod::FDDs const& fdds, aod::Zdcs const& zdcs, + aod::McCollisions const&, aod::McParticles const& mcParticles) + { + if (genId != -1) { + if (!col.has_mcCollision() || col.mcCollision().getGeneratorId() != genId) { + return; + } + } + + bool isTwoBody, isFourBody; + auto selTrks = fillCollisionTables(col, bcs, tracks, fv0s, ft0s, fdds, zdcs, isTwoBody, isFourBody); + if (!isTwoBody && !isFourBody) { + return; // event was not written to the reco tables either + } + + // MC gen + if (isTwoBody) { + auto mcPart1 = selTrks[0].has_mcParticle() ? std::optional(selTrks[0].mcParticle()) : std::nullopt; + auto mcPart2 = selTrks[1].has_mcParticle() ? std::optional(selTrks[1].mcParticle()) : std::nullopt; + + int pdg1 = 0, pdg2 = 0, sign1 = 0, sign2 = 0, isPrim1 = 0, isPrim2 = 0; + float pt1 = -999, eta1 = -999, phi1 = -999, pt2 = -999, eta2 = -999, phi2 = -999; + int motherPdg = 0; + float motherPt = -999, motherPhi = -999, motherMass = -999, motherRap = -999; + float mcPosX = -999, mcPosY = -999, mcPosZ = -999; + int mcLocalBC = -999; + + if (mcPart1 && mcPart2) { + pdg1 = mcPart1->pdgCode(); + pt1 = mcPt(mcPart1->px(), mcPart1->py()); + eta1 = mcEta(mcPart1->px(), mcPart1->py(), mcPart1->pz()); + phi1 = mcPhi(mcPart1->px(), mcPart1->py()); + sign1 = (pdg1 > 0) ? 1 : (pdg1 < 0) ? -1 + : 0; + isPrim1 = mcPart1->isPhysicalPrimary() ? 1 : 0; + + pdg2 = mcPart2->pdgCode(); + pt2 = mcPt(mcPart2->px(), mcPart2->py()); + eta2 = mcEta(mcPart2->px(), mcPart2->py(), mcPart2->pz()); + phi2 = mcPhi(mcPart2->px(), mcPart2->py()); + sign2 = (pdg2 > 0) ? 1 : (pdg2 < 0) ? -1 + : 0; + isPrim2 = mcPart2->isPhysicalPrimary() ? 1 : 0; + + auto mcCollision = mcPart1->mcCollision(); + mcPosX = mcCollision.posX(); + mcPosY = mcCollision.posY(); + mcPosZ = mcCollision.posZ(); + mcLocalBC = static_cast(getBcWithinOrbit(mcCollision.template bc_as().globalBC())); + + float sumPx = mcPart1->px() + mcPart2->px(); + float sumPy = mcPart1->py() + mcPart2->py(); + float sumPz = mcPart1->pz() + mcPart2->pz(); + float sumE = mcPart1->e() + mcPart2->e(); + motherPt = mcPt(sumPx, sumPy); + motherPhi = mcPhi(sumPx, sumPy); + motherMass = std::sqrt(sumE * sumE - sumPx * sumPx - sumPy * sumPy - sumPz * sumPz); + motherRap = mcRapidity(sumPx, sumPy, sumPz, motherMass); + + MotherInfo mi = findCommonMother(mcParticles, std::vector{*mcPart1, *mcPart2}); + if (mi.found) { + motherPdg = mi.pdg; + } + } + + twoTrkRecGenTable( + motherPdg, motherPt, motherPhi, motherMass, motherRap, + pdg1, pt1, eta1, phi1, sign1, isPrim1, + pdg2, pt2, eta2, phi2, sign2, isPrim2, + mcPosX, mcPosY, mcPosZ, mcLocalBC, + twoTrkTable.lastIndex() // row of TwoTrkTable this mc info belongs to + ); + } + if (isFourBody) { + auto mcPart1 = selTrks[0].has_mcParticle() ? std::optional(selTrks[0].mcParticle()) : std::nullopt; + auto mcPart2 = selTrks[1].has_mcParticle() ? std::optional(selTrks[1].mcParticle()) : std::nullopt; + auto mcPart3 = selTrks[2].has_mcParticle() ? std::optional(selTrks[2].mcParticle()) : std::nullopt; + auto mcPart4 = selTrks[3].has_mcParticle() ? std::optional(selTrks[3].mcParticle()) : std::nullopt; + + int pdg1 = 0, pdg2 = 0, pdg3 = 0, pdg4 = 0; + int sign1 = 0, sign2 = 0, sign3 = 0, sign4 = 0; + int isPrim1 = 0, isPrim2 = 0, isPrim3 = 0, isPrim4 = 0; + float pt1 = -999, eta1 = -999, phi1 = -999; + float pt2 = -999, eta2 = -999, phi2 = -999; + float pt3 = -999, eta3 = -999, phi3 = -999; + float pt4 = -999, eta4 = -999, phi4 = -999; + int motherPdg = 0; + float motherPt = -999, motherPhi = -999, motherMass = -999, motherRap = -999; + float mcPosX = -999, mcPosY = -999, mcPosZ = -999; + int mcLocalBC = -999; + + if (mcPart1 && mcPart2 && mcPart3 && mcPart4) { + pdg1 = mcPart1->pdgCode(); + pt1 = mcPt(mcPart1->px(), mcPart1->py()); + eta1 = mcEta(mcPart1->px(), mcPart1->py(), mcPart1->pz()); + phi1 = mcPhi(mcPart1->px(), mcPart1->py()); + sign1 = (pdg1 > 0) ? 1 : (pdg1 < 0) ? -1 + : 0; + isPrim1 = mcPart1->isPhysicalPrimary() ? 1 : 0; + + pdg2 = mcPart2->pdgCode(); + pt2 = mcPt(mcPart2->px(), mcPart2->py()); + eta2 = mcEta(mcPart2->px(), mcPart2->py(), mcPart2->pz()); + phi2 = mcPhi(mcPart2->px(), mcPart2->py()); + sign2 = (pdg2 > 0) ? 1 : (pdg2 < 0) ? -1 + : 0; + isPrim2 = mcPart2->isPhysicalPrimary() ? 1 : 0; + + pdg3 = mcPart3->pdgCode(); + pt3 = mcPt(mcPart3->px(), mcPart3->py()); + eta3 = mcEta(mcPart3->px(), mcPart3->py(), mcPart3->pz()); + phi3 = mcPhi(mcPart3->px(), mcPart3->py()); + sign3 = (pdg3 > 0) ? 1 : (pdg3 < 0) ? -1 + : 0; + isPrim3 = mcPart3->isPhysicalPrimary() ? 1 : 0; + + pdg4 = mcPart4->pdgCode(); + pt4 = mcPt(mcPart4->px(), mcPart4->py()); + eta4 = mcEta(mcPart4->px(), mcPart4->py(), mcPart4->pz()); + phi4 = mcPhi(mcPart4->px(), mcPart4->py()); + sign4 = (pdg4 > 0) ? 1 : (pdg4 < 0) ? -1 + : 0; + isPrim4 = mcPart4->isPhysicalPrimary() ? 1 : 0; + + auto mcCollision = mcPart1->mcCollision(); + mcPosX = mcCollision.posX(); + mcPosY = mcCollision.posY(); + mcPosZ = mcCollision.posZ(); + mcLocalBC = static_cast(getBcWithinOrbit(mcCollision.template bc_as().globalBC())); + + float sumPx = mcPart1->px() + mcPart2->px() + mcPart3->px() + mcPart4->px(); + float sumPy = mcPart1->py() + mcPart2->py() + mcPart3->py() + mcPart4->py(); + float sumPz = mcPart1->pz() + mcPart2->pz() + mcPart3->pz() + mcPart4->pz(); + float sumE = mcPart1->e() + mcPart2->e() + mcPart3->e() + mcPart4->e(); + motherPt = mcPt(sumPx, sumPy); + motherPhi = mcPhi(sumPx, sumPy); + motherMass = std::sqrt(sumE * sumE - sumPx * sumPx - sumPy * sumPy - sumPz * sumPz); + motherRap = mcRapidity(sumPx, sumPy, sumPz, motherMass); + + MotherInfo mi = findCommonMother(mcParticles, std::vector{*mcPart1, *mcPart2, *mcPart3, *mcPart4}); + if (mi.found) { + motherPdg = mi.pdg; + } + } + + fourTrkRecGenTable( + motherPdg, motherPt, motherPhi, motherMass, motherRap, + pdg1, pt1, eta1, phi1, sign1, isPrim1, + pdg2, pt2, eta2, phi2, sign2, isPrim2, + pdg3, pt3, eta3, phi3, sign3, isPrim3, + pdg4, pt4, eta4, phi4, sign4, isPrim4, + mcPosX, mcPosY, mcPosZ, mcLocalBC, + fourTrkTable.lastIndex() // row of FourTrkTable this mc info belongs to + ); + } + } // end processMcCols + PROCESS_SWITCH(UpcVmRof, processMcCols, "process mc truth matched to reco, mc only", false); + + //-------------------------------------------------------------------------------- + // Fill MC information for all generated events + void processMcGen(aod::McCollision const& mcCollision, BCsTSsSels const&, aod::McParticles const& mcParticles) + { + if (genId != -1 && mcCollision.getGeneratorId() != genId) { + return; + } + + // event counter + if (!colTH1Pointers["mcGen/genSel_H"]) { + colTH1Pointers["mcGen/genSel_H"] = colTH1Registry.add("mcGen/genSel_H", + "pure generator event counter; selID; Counter", + {HistType::kTH1D, {{3, -0.5, 2.5}}}); + } + colTH1Pointers["mcGen/genSel_H"]->Fill(0); + + std::vector primaries; + for (const auto& part : mcParticles) { + if (part.isPhysicalPrimary()) { + primaries.push_back(part); + } + } + + // local BC of the generated collision + int localBc = static_cast(getBcWithinOrbit(mcCollision.template bc_as().globalBC())); + + if (primaries.size() == NTrksTwoBody) { + auto p1 = primaries[0]; + auto p2 = primaries[1]; + + int pdg1 = p1.pdgCode(); + int pdg2 = p2.pdgCode(); + int sign1 = (pdg1 > 0) ? 1 : (pdg1 < 0) ? -1 + : 0; + int sign2 = (pdg2 > 0) ? 1 : (pdg2 < 0) ? -1 + : 0; + + // mother mass/momentum always from summed daughters + float sumPx = p1.px() + p2.px(); + float sumPy = p1.py() + p2.py(); + float sumPz = p1.pz() + p2.pz(); + float sumE = p1.e() + p2.e(); + float motherMass = std::sqrt(sumE * sumE - sumPx * sumPx - sumPy * sumPy - sumPz * sumPz); + + int motherPdg = 0; // 0 = no common mother confirmed in the gen tree + MotherInfo mi = findCommonMotherFromIters(/* mcParticles,*/ std::vector{p1, p2}); + if (mi.found) { + motherPdg = mi.pdg; + } + + colTH1Pointers["mcGen/genSel_H"]->Fill(1); + twoTrkGenTable( + motherPdg, mcPt(sumPx, sumPy), mcPhi(sumPx, sumPy), motherMass, mcRapidity(sumPx, sumPy, sumPz, motherMass), + pdg1, mcPt(p1.px(), p1.py()), mcEta(p1.px(), p1.py(), p1.pz()), mcPhi(p1.px(), p1.py()), sign1, 1, + pdg2, mcPt(p2.px(), p2.py()), mcEta(p2.px(), p2.py(), p2.pz()), mcPhi(p2.px(), p2.py()), sign2, 1, + mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), localBc); + } + + if (primaries.size() == NTrksFourBody) { + auto p1 = primaries[0]; + auto p2 = primaries[1]; + auto p3 = primaries[2]; + auto p4 = primaries[3]; + + int pdg1 = p1.pdgCode(); + int pdg2 = p2.pdgCode(); + int pdg3 = p3.pdgCode(); + int pdg4 = p4.pdgCode(); + int sign1 = (pdg1 > 0) ? 1 : (pdg1 < 0) ? -1 + : 0; + int sign2 = (pdg2 > 0) ? 1 : (pdg2 < 0) ? -1 + : 0; + int sign3 = (pdg3 > 0) ? 1 : (pdg3 < 0) ? -1 + : 0; + int sign4 = (pdg4 > 0) ? 1 : (pdg4 < 0) ? -1 + : 0; + + float sumPx = p1.px() + p2.px() + p3.px() + p4.px(); + float sumPy = p1.py() + p2.py() + p3.py() + p4.py(); + float sumPz = p1.pz() + p2.pz() + p3.pz() + p4.pz(); + float sumE = p1.e() + p2.e() + p3.e() + p4.e(); + float motherMass = std::sqrt(sumE * sumE - sumPx * sumPx - sumPy * sumPy - sumPz * sumPz); + + int motherPdg = 0; + MotherInfo mi = findCommonMotherFromIters(/* mcParticles, */ std::vector{p1, p2, p3, p4}); + if (mi.found) { + motherPdg = mi.pdg; + } + + colTH1Pointers["mcGen/genSel_H"]->Fill(2); + fourTrkGenTable( + motherPdg, mcPt(sumPx, sumPy), mcPhi(sumPx, sumPy), motherMass, mcRapidity(sumPx, sumPy, sumPz, motherMass), + pdg1, mcPt(p1.px(), p1.py()), mcEta(p1.px(), p1.py(), p1.pz()), mcPhi(p1.px(), p1.py()), sign1, 1, + pdg2, mcPt(p2.px(), p2.py()), mcEta(p2.px(), p2.py(), p2.pz()), mcPhi(p2.px(), p2.py()), sign2, 1, + pdg3, mcPt(p3.px(), p3.py()), mcEta(p3.px(), p3.py(), p3.pz()), mcPhi(p3.px(), p3.py()), sign3, 1, + pdg4, mcPt(p4.px(), p4.py()), mcEta(p4.px(), p4.py(), p4.pz()), mcPhi(p4.px(), p4.py()), sign4, 1, + mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), localBc); + } + } // end processMcGenAll + PROCESS_SWITCH(UpcVmRof, processMcGen, "process all generated collisions, mc only", false); }; // end of struct UpcVmRof WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) + { return WorkflowSpec{ adaptAnalysisTask(cfgc)}; diff --git a/Scripts/format_includes.awk b/Scripts/format_includes.awk deleted file mode 100644 index 42484d62830..00000000000 --- a/Scripts/format_includes.awk +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/awk -f - -# Fix include style. -# NB: Run before sorting. - -{ - if ($1 ~ /^#include/) { - h = substr($2, 2, length($2) - 2) - if ( h ~ /^(PWG[A-Z]{2}|Common|ALICE3|DPG|EventFiltering|PID|Tools|Tutorials)\/.*\.h/ ) { $2 = "\""h"\"" } # O2Physics - else if ( h ~ /^(Algorithm|CCDB|Common[A-Z]|DataFormats|DCAFitter|Detectors|EMCAL|FDD|Field|Framework|FT0|FV0|GlobalTracking|GPU|ITS|MathUtils|MCH|MFT|MID|PHOS|ReconstructionDataFormats|SimulationDataFormat|TOF|TPC|ZDC).*\/.*\.h/ ) { $2 = "<"h">" } # O2 - else if ( h ~ /^(T[A-Z]|Math\/|Roo[A-Z])[[:alnum:]\/]+\.h/ ) { $2 = "<"h">" } # ROOT - else if ( h ~ /^KF[A-Z][[:alnum:]]+\.h/ ) { $2 = "<"h">" } # KFParticle - else if ( h ~ /^(fastjet\/|onnxruntime)/ ) { $2 = "<"h">" } # FastJet, ONNX - else if ( h ~ /^.*DataModel\// ) { $2 = "\""h"\"" } # incomplete path to DataModel - else if ( h ~ /^([[:alnum:]_]+\/)+[[:alnum:]_]+\.h/ ) { $2 = "<"h">" } # other third-party - else if ( $2 ~ /^".*\./ ) { } # other local-looking file - else if ( h ~ /^[[:lower:]_]+\.h/ ) { $2 = "<"h">" } # C system - else if ( h ~ /^[[:lower:]_\/]+/ ) { $2 = "<"h">" } # C++ system - } - print -} diff --git a/Scripts/format_includes.py b/Scripts/format_includes.py new file mode 100755 index 00000000000..c545441d9af --- /dev/null +++ b/Scripts/format_includes.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 + +# Copyright 2019-2020 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 Fix include style +@author Vít Kučera , Inha University +@date 2026-10-08 + +NB: Run before sorting. + +Usage: format_includes.py FILE [FILE ...] +""" + +import re +import sys + +LOCAL = ('"', '"') +EXTERNAL = ("<", ">") + +INCLUDE = re.compile(r"(\s*#include\s+)(\S+)") + + +def fix_line(line: str) -> str: + m = INCLUDE.match(line) + if not m: + return line + pre, tok = m.groups() + rest = line[m.end() :] + h = tok[1:-1] + + if re.match(r"(PWG[A-Z]{2}|Common|ALICE3|DPG|EventFiltering|PID|Tools|Tutorials)/.*\.h", h): + d = LOCAL # O2Physics + elif re.match( + r"(Algorithm|CCDB|Common[A-Z]|DataFormats|DCAFitter|Detectors|EMCAL|FDD|Field|Framework|FT0|FV0|GlobalTracking|GPU|ITS|MathUtils|MCH|MFT|MID|PHOS|ReconstructionDataFormats|SimulationDataFormat|TOF|TPC|ZDC).*/.*\.h", + h, + ): + d = EXTERNAL # O2 + elif re.match(r"(T[A-Z]|Math/|Roo[A-Z])[A-Za-z0-9/]+\.h", h): + d = EXTERNAL # ROOT + elif re.match(r"KF[A-Z][A-Za-z0-9]+\.h", h): + d = EXTERNAL # KFParticle + elif re.match(r"(fastjet/|onnxruntime)", h): + d = EXTERNAL # FastJet, ONNX + elif re.match(r".*DataModel/", h): + d = LOCAL # incomplete path to DataModel + elif re.match(r"([A-Za-z0-9_]+/)+[A-Za-z0-9_]+\.h", h): + d = EXTERNAL # other third-party + elif re.match(r'".*\.', tok): + return line # other local-looking file + elif re.match(r"[a-z_]+\.h", h): + d = EXTERNAL # C system + elif re.match(r"[a-z_/]+$", h): + d = EXTERNAL # C++ system (whole string) + else: + return line + + return pre + d[0] + h + d[1] + rest + + +def process(path: str): + # newline='' keeps the original line endings untouched. + with open(path, newline="") as f: + lines = f.readlines() + new_lines = [fix_line(line) for line in lines] + if new_lines != lines: + with open(path, "w", newline="") as f: + f.writelines(new_lines) + + +if __name__ == "__main__": + if len(sys.argv) < 2: + sys.exit(__doc__) + for p in sys.argv[1:]: + process(p) diff --git a/Scripts/o2_linter.py b/Scripts/o2_linter.py old mode 100644 new mode 100755 diff --git a/Tools/KFparticle/qaKFEventTrack.cxx b/Tools/KFparticle/qaKFEventTrack.cxx index 7ff14e80c1c..6e026a08f4e 100644 --- a/Tools/KFparticle/qaKFEventTrack.cxx +++ b/Tools/KFparticle/qaKFEventTrack.cxx @@ -247,7 +247,7 @@ struct qaKFEventTrack { template void writeVarTree(const T1& /*kfpTrackPi*/, const T2& KFPion, const T2& KFPV, const T3& track, const int source, const int pVContrib, const int runNumber, const T4& collision) { - const double pseudoRndm = track.pt() * 1000. - (int64_t)(track.pt() * 1000); + const double pseudoRndm = track.pt() * 1000. - static_cast(track.pt() * 1000); if (pseudoRndm < d_DwnSmplFact) { if (writeTree) { /// Filling the tree @@ -629,7 +629,7 @@ struct qaKFEvent { if (!isSelectedCollision(collisionIndex)) { continue; } - timeColl = uint64_t((bc.globalBC() - bc0) * o2::constants::lhc::LHCBunchSpacingNS) + collisionIndex.collisionTime(); + timeColl = static_cast((bc.globalBC() - bc0) * o2::constants::lhc::LHCBunchSpacingNS) + collisionIndex.collisionTime(); writeVarTreeColl(collisionIndex, timeColl, bc.timestamp(), collisionIndex.collisionTime(), tfID); } } diff --git a/Tools/KFparticle/qaKFParticleLc.cxx b/Tools/KFparticle/qaKFParticleLc.cxx index eb7c68eeaa8..f9e5c11e4a6 100644 --- a/Tools/KFparticle/qaKFParticleLc.cxx +++ b/Tools/KFparticle/qaKFParticleLc.cxx @@ -416,7 +416,7 @@ struct qaKFParticleLc { float chi2geo = KFLc.GetChi2() / KFLc.GetNDF(); float normdecayLength = KFLc_PV.GetDecayLength() / KFLc_PV.GetErrDecayLength(); float chi2topo = KFLc_PV.GetChi2() / KFLc_PV.GetNDF(); - const double pseudoRndm = trackKa.pt() * 1000. - (int64_t)(trackKa.pt() * 1000); + const double pseudoRndm = trackKa.pt() * 1000. - static_cast(trackKa.pt() * 1000); if (pseudoRndm < d_DwnSmplFact) { /// Filling the D0 tree rowKFLc(runNumber, diff --git a/Tools/ML/MlResponse.h b/Tools/ML/MlResponse.h index 393eecba293..f7cd001ea96 100644 --- a/Tools/ML/MlResponse.h +++ b/Tools/ML/MlResponse.h @@ -109,10 +109,10 @@ class MlResponse void setModelPathsCCDB(const std::vector& onnxFiles, const o2::ccdb::CcdbApi& ccdbApi, const std::vector& pathsCCDB, int64_t timestampCCDB) { if (onnxFiles.size() != mNModels) { - LOG(fatal) << "Number of expected models (" << mNModels << ") different from the one set (" << onnxFiles.size() << ")! Please check your configurables."; + LOG(fatal) << "Number of expected models (" << static_cast(mNModels) << ") different from the one set (" << onnxFiles.size() << ")! Please check your configurables."; } if (pathsCCDB.size() != mNModels) { - LOG(fatal) << "Number of expected models (" << mNModels << ") different from the number of CCDB paths (" << pathsCCDB.size() << ")! Please check your configurables."; + LOG(fatal) << "Number of expected models (" << static_cast(mNModels) << ") different from the number of CCDB paths (" << pathsCCDB.size() << ")! Please check your configurables."; } // check that the path is unique for each BDT model (otherwise CCDB download does not work as expected) @@ -140,7 +140,7 @@ class MlResponse void setModelPathsLocal(const std::vector& onnxFiles) { if (onnxFiles.size() != mNModels) { - LOG(fatal) << "Number of expected models (" << mNModels << ") different from the one set (" << onnxFiles.size() << ")! Please check your configurables."; + LOG(fatal) << "Number of expected models (" << static_cast(mNModels) << ") different from the one set (" << onnxFiles.size() << ")! Please check your configurables."; } mPaths = onnxFiles; } @@ -190,8 +190,16 @@ class MlResponse LOG(fatal) << "Number of input nodes in the model " << mPaths[nModel] << " is different from the number of input features to be tested (" << numInputNodes << " vs " << numInputFeatures << ")"; } - TypeOutputScore* outputPtr = mModels[nModel].template evalModel(input); - return std::vector{outputPtr, outputPtr + mNClasses}; + // evalModel returns an owning copy of the (last) output tensor of the model + std::vector output = mModels[nModel].template evalModel(input); + if (output.size() < mNClasses) { + LOG(fatal) << "Model " << mPaths[nModel] << " returned " << output.size() << " scores, but " << static_cast(mNClasses) << " classes are expected. Please check your configurables."; + } + if (output.size() > mNClasses) { + // keep only the first mNClasses scores (e.g. single-candidate probabilities of a multi-output model) + output.resize(mNClasses); + } + return output; } /// Get vector with model predictions for a batch of candidates @@ -221,11 +229,16 @@ class MlResponse LOG(fatal) << "Number of input nodes in the model " << mPaths[nModel] << " differs from features per row (" << numInputNodes << " vs " << featuresPerRow << ")"; } - TypeOutputScore* outputPtr = mModels[nModel].template evalModel(input); - if (outputPtr == nullptr) { - LOG(fatal) << "Batched model evaluation failed for model " << mPaths[nModel]; + std::vector output = mModels[nModel].template evalModel(input); + const std::size_t expectedOutputSize = nRows * mNClasses; + if (output.size() < expectedOutputSize) { + LOG(fatal) << "Model " << mPaths[nModel] << " returned " << output.size() << " scores, but " << expectedOutputSize << " scores are expected for " << nRows << " rows and " << static_cast(mNClasses) << " classes. Please check your configurables."; + } + if (output.size() > expectedOutputSize) { + // keep only the first scores (e.g. batched probabilities of a multi-output model) + output.resize(expectedOutputSize); } - return std::vector{outputPtr, outputPtr + nRows * mNClasses}; + return output; } /// ML selections diff --git a/Tools/ML/model.cxx b/Tools/ML/model.cxx index 6d2098068aa..ebf3919a891 100644 --- a/Tools/ML/model.cxx +++ b/Tools/ML/model.cxx @@ -42,6 +42,9 @@ namespace ml std::string OnnxModel::printShape(const std::vector& v) { + if (v.empty()) { + return "[]"; + } std::stringstream ss(""); for (std::size_t i = 0; i < v.size() - 1; i++) ss << v[i] << "x"; @@ -90,7 +93,14 @@ void OnnxModel::initModel(const std::string& localPath, const bool enableOptimiz mEnv = std::make_shared(ORT_LOGGING_LEVEL_WARNING, "onnx-model"); mSession = std::make_shared(*mEnv, modelPath.c_str(), sessionOptions); + mMemInfo = Ort::MemoryInfo::CreateCpu(OrtAllocatorType::OrtArenaAllocator, OrtMemType::OrtMemTypeDefault); + mInputNamesChar.clear(); + mOutputNamesChar.clear(); + mInputNames.clear(); + mInputShapes.clear(); + mOutputNames.clear(); + mOutputShapes.clear(); Ort::AllocatorWithDefaultOptions const tmpAllocator; for (std::size_t i = 0; i < mSession->GetInputCount(); ++i) { mInputNames.push_back(mSession->GetInputNameAllocated(i, tmpAllocator).get()); @@ -104,6 +114,14 @@ void OnnxModel::initModel(const std::string& localPath, const bool enableOptimiz for (std::size_t i = 0; i < mSession->GetOutputCount(); ++i) { mOutputShapes.emplace_back(mSession->GetOutputTypeInfo(i).GetTensorTypeAndShapeInfo().GetShape()); } + mInputNamesChar.reserve(mInputNames.size()); + for (const auto& name : mInputNames) { + mInputNamesChar.push_back(name.c_str()); + } + mOutputNamesChar.reserve(mOutputNames.size()); + for (const auto& name : mOutputNames) { + mOutputNamesChar.push_back(name.c_str()); + } LOG(info) << "Input Nodes:"; for (std::size_t i = 0; i < mInputNames.size(); i++) { LOG(info) << "\t" << mInputNames[i] << " : " << printShape(mInputShapes[i]); @@ -122,6 +140,114 @@ void OnnxModel::initModel(const std::string& localPath, const bool enableOptimiz LOG(info) << "--- Model initialized! ---"; } +std::vector OnnxModel::inferInputShape(const std::size_t iinput, const int64_t size) const +{ + const std::vector& modelShape = mInputShapes[iinput]; + + // Rank-1 input: the whole vector is the tensor + if (modelShape.size() < 2) { + return {size}; + } + + // Product of all non-batch dimensions; dynamic dimensions (< 0) cannot be inferred + int64_t totalSize = 1; + bool hasDynamicDim = false; + for (std::size_t idim = 1; idim < modelShape.size(); idim++) { + if (modelShape[idim] < 0) { + hasDynamicDim = true; + } else { + totalSize *= modelShape[idim]; + } + } + + if (hasDynamicDim) { + if (modelShape.size() == 2) { + // [batch, features] with dynamic feature dimension: interpret the vector as a single sample + return {1, size}; + } + LOG(fatal) << "Input " << iinput << " (" << mInputNames[iinput] << ") has dynamic non-batch dimensions (" << printShape(modelShape) << "), the tensor shape cannot be inferred from a flat vector. Please provide std::vector inputs instead."; + } + + if (totalSize <= 0 || size % totalSize != 0) { + LOG(fatal) << "Size of the input vector (" << size << ") is not a multiple of the model input size (" << totalSize << ") for input " << iinput << " (" << mInputNames[iinput] << ", shape " << printShape(modelShape) << ")"; + } + + std::vector inputShape; + inputShape.reserve(modelShape.size()); + inputShape.push_back(size / totalSize); + for (std::size_t idim = 1; idim < modelShape.size(); idim++) { + inputShape.push_back(modelShape[idim]); + } + return inputShape; +} + +void OnnxModel::checkInput(const std::vector& input) const +{ + if (!mSession) { + LOG(fatal) << "OnnxModel::evalModel called before initModel()"; + } + if (input.size() != mInputNames.size()) { + LOG(fatal) << "Number of input tensors (" << input.size() << ") does not agree with the number of model inputs (" << mInputNames.size() << ")"; + } + for (std::size_t i = 0; i < input.size(); i++) { + LOG(debug) << "Input tensor " << i << " shape: " << printShape(input[i].GetTensorTypeAndShapeInfo().GetShape()); + } +} + +std::vector OnnxModel::evalModelRaw(std::vector& input) +{ + checkInput(input); + std::vector outputTensors; + try { + const Ort::RunOptions runOptions{nullptr}; + outputTensors = mSession->Run(runOptions, mInputNamesChar.data(), input.data(), input.size(), mOutputNamesChar.data(), mOutputNamesChar.size()); + } catch (const Ort::Exception& exception) { + LOG(fatal) << "Error running model inference: " << exception.what(); + } + + LOG(debug) << "Number of output tensors: " << outputTensors.size(); + if (outputTensors.size() != mOutputNames.size()) { + LOG(fatal) << "Number of output tensors: " << outputTensors.size() << " does not agree with the model specified size: " << mOutputNames.size(); + } + for (std::size_t i = 0; i < outputTensors.size(); i++) { + checkOutput(outputTensors[i], i); + } + + return outputTensors; +} + +Ort::Value OnnxModel::evalModelLast(std::vector& input) +{ + checkInput(input); + if (mOutputNamesChar.empty()) { + LOG(fatal) << "Model has no outputs"; + } + Ort::Value output{nullptr}; + try { + // A null RunOptions uses the runtime defaults without allocating options per call. + const Ort::RunOptions runOptions{nullptr}; + mSession->Run(runOptions, mInputNamesChar.data(), input.data(), input.size(), &mOutputNamesChar.back(), &output, 1); + } catch (const Ort::Exception& exception) { + LOG(fatal) << "Error running model inference: " << exception.what(); + } + checkOutput(output, mOutputShapes.size() - 1); + return output; +} + +void OnnxModel::checkOutput(const Ort::Value& tensor, const std::size_t index) const +{ + const std::vector shape = tensor.GetTensorTypeAndShapeInfo().GetShape(); + LOG(debug) << "Output tensor " << index << " shape: " << printShape(shape); + bool shapeOk = (shape.size() == mOutputShapes[index].size()); + for (std::size_t idim = 0; shapeOk && idim < shape.size(); idim++) { + // Dynamic dimensions of the model (< 0) can take any value. + shapeOk = (mOutputShapes[index][idim] < 0) || (shape[idim] == mOutputShapes[index][idim]); + } + if (!shapeOk) { + LOG(fatal) << "Shape of output tensor " << index << " does not agree with model specification! Output: " << printShape(shape) << " model: " << printShape(mOutputShapes[index]); + } +} + void OnnxModel::setActiveThreads(const int threads) { activeThreads = threads; diff --git a/Tools/ML/model.h b/Tools/ML/model.h index e9997c1501e..d94491f8c38 100644 --- a/Tools/ML/model.h +++ b/Tools/ML/model.h @@ -22,14 +22,10 @@ #include -#include #include -#include -#include #include #include -#include #include #include #include @@ -40,91 +36,112 @@ namespace o2 namespace ml { +/// \brief Thin wrapper around an ONNX Runtime session (CPU only) +/// +/// Inference entry points: +/// - evalModelRaw(): runs the session and returns the owning output tensors (one Ort::Value per model output). +/// This is the zero-copy path: the caller owns the tensors and may read them via GetTensorData() +/// for as long as the returned vector is alive. +/// - evalModel(input): convenience wrapper returning a copy of the *last* model output as std::vector. +/// - evalModel(input, output): same as above but writes into a caller-provided vector. The vector is +/// resized as needed and its capacity is reused across calls, which avoids repeated allocations of the result buffer. +/// Only the last model output is requested from ONNX Runtime. +/// +/// Inputs given as std::vector are wrapped in an Ort::Value without copying. The input vector must therefore +/// stay alive until the call returns (which is always the case for the synchronous calls provided here). class OnnxModel { public: OnnxModel() = default; ~OnnxModel() = default; + // The ONNX session options are not copyable, so the model is move-only + OnnxModel(const OnnxModel&) = delete; + OnnxModel& operator=(const OnnxModel&) = delete; + OnnxModel(OnnxModel&&) = default; + OnnxModel& operator=(OnnxModel&&) = default; // Inferencing void initModel(const std::string&, const bool = false, const int = 0, const uint64_t = 0, const uint64_t = 0); - // template methods -- best to define them in header + /// Run the model on already prepared input tensors + /// \param input one Ort::Value per model input + /// \return the output tensors of the model (owning). Access to the data via output[i].GetTensorData() + std::vector evalModelRaw(std::vector& input); + + /// Run the model and copy the last output tensor into a vector + /// \param input one Ort::Value per model input + /// \return flattened content of the last output tensor template - T* evalModel(std::vector& input) + std::vector evalModel(std::vector& input) { - LOG(debug) << "Input tensor shape: " << printShape(input[0].GetTensorTypeAndShapeInfo().GetShape()); - // assert(input[0].GetTensorTypeAndShapeInfo().GetShape() == getNumInputNodes()); --> Fails build in debug mode, TODO: assertion should be checked somehow - - try { - const Ort::RunOptions runOptions; - std::vector inputNamesChar(mInputNames.size(), nullptr); - std::transform(std::begin(mInputNames), std::end(mInputNames), std::begin(inputNamesChar), - [&](const std::string& str) { return str.c_str(); }); - - std::vector outputNamesChar(mOutputNames.size(), nullptr); - std::transform(std::begin(mOutputNames), std::end(mOutputNames), std::begin(outputNamesChar), - [&](const std::string& str) { return str.c_str(); }); - auto outputTensors = mSession->Run(runOptions, inputNamesChar.data(), input.data(), input.size(), outputNamesChar.data(), outputNamesChar.size()); - LOG(debug) << "Number of output tensors: " << outputTensors.size(); - if (outputTensors.size() != mOutputNames.size()) { - LOG(fatal) << "Number of output tensors: " << outputTensors.size() << " does not agree with the model specified size: " << mOutputNames.size(); - } - for (std::size_t i = 0; i < outputTensors.size(); i++) { - LOG(debug) << "Output tensor shape: " << printShape(outputTensors[i].GetTensorTypeAndShapeInfo().GetShape()); - if ((outputTensors[i].GetTensorTypeAndShapeInfo().GetShape() != mOutputShapes[i]) && (mOutputShapes[i][0] != -1)) { - LOG(fatal) << "Shape of tensor " << i << " does not agree with model specification! Output: " << printShape(outputTensors[i].GetTensorTypeAndShapeInfo().GetShape()) << " model: " << printShape(mOutputShapes[i]); - } - } - // TODO: isn't outputTensors freed after exiting this function thus making the returned pointer pointing to garbage memory resulting in undefined beaviour? - T* outputValues = outputTensors.back().GetTensorMutableData(); - return outputValues; - } catch (const Ort::Exception& exception) { - LOG(error) << "Error running model inference: " << exception.what(); - } - return nullptr; + std::vector output; + evalModel(input, output); + return output; } + /// Run the model and copy the last output tensor into the provided vector (allocation is reused between calls) + /// \param input one Ort::Value per model input + /// \param output vector which is filled with the flattened content of the last output tensor template - T* evalModel(std::vector& input) + void evalModel(std::vector& input, std::vector& output) { - const int64_t size = input.size(); - assert(size % mInputShapes[0][1] == 0); - std::vector inputShape{size / mInputShapes[0][1], mInputShapes[0][1]}; - std::vector inputTensors; - Ort::MemoryInfo memInfo = - Ort::MemoryInfo::CreateCpu(OrtAllocatorType::OrtArenaAllocator, OrtMemType::OrtMemTypeDefault); - inputTensors.emplace_back(Ort::Value::CreateTensor(memInfo, input.data(), size, inputShape.data(), inputShape.size())); - LOG(debug) << "Input shape calculated from vector: " << printShape(inputShape); - return evalModel(inputTensors); + const Ort::Value tensor = evalModelLast(input); + copyOutput(tensor, output); } - // For 2D inputs + /// Run a single-input model on a flat vector of features (batches are inferred from the model input shape) + /// \param input flattened input features, size must be a multiple of the number of input nodes + /// \return flattened content of the last output tensor template - T* evalModel(std::vector>& input) + std::vector evalModel(std::vector& input) { + std::vector output; + evalModel(input, output); + return output; + } + + /// Run a single-input model on a flat vector of features (batches are inferred from the model input shape) + /// \param input flattened input features, size must be a multiple of the number of input nodes + /// \param output vector which is filled with the flattened content of the last output tensor + template + void evalModel(std::vector& input, std::vector& output) + { + if (mInputShapes.size() != 1) { + LOG(fatal) << "Model has " << mInputShapes.size() << " inputs but a single input vector was provided. Use the std::vector> or std::vector overload."; + } std::vector inputTensors; + inputTensors.reserve(1); + addInputTensor(inputTensors, input, 0); + evalModel(inputTensors, output); + } - Ort::MemoryInfo memInfo = Ort::MemoryInfo::CreateCpu(OrtAllocatorType::OrtArenaAllocator, OrtMemType::OrtMemTypeDefault); + /// Run a multi-input model: one flat vector of features per model input + /// \param input one flattened vector per model input + /// \return flattened content of the last output tensor + template + std::vector evalModel(std::vector>& input) + { + std::vector output; + evalModel(input, output); + return output; + } + /// Run a multi-input model: one flat vector of features per model input + /// \param input one flattened vector per model input + /// \param output vector which is filled with the flattened content of the last output tensor + template + void evalModel(std::vector>& input, std::vector& output) + { + if (input.size() != mInputShapes.size()) { + LOG(fatal) << "Model has " << mInputShapes.size() << " inputs but " << input.size() << " input vectors were provided."; + } + std::vector inputTensors; + inputTensors.reserve(input.size()); for (std::size_t iinput = 0; iinput < input.size(); iinput++) { - [[maybe_unused]] int totalSize = 1; - int64_t size = input[iinput].size(); - for (std::size_t idim = 1; idim < mInputShapes[iinput].size(); idim++) { - totalSize *= mInputShapes[iinput][idim]; - } - assert(size % totalSize == 0); - - std::vector inputShape{static_cast(size / totalSize)}; - for (std::size_t idim = 1; idim < mInputShapes[iinput].size(); idim++) { - inputShape.push_back(mInputShapes[iinput][idim]); - } - - inputTensors.emplace_back(Ort::Value::CreateTensor(memInfo, input[iinput].data(), size, inputShape.data(), inputShape.size())); + addInputTensor(inputTensors, input[iinput], iinput); } - - return evalModel(inputTensors); + evalModel(inputTensors, output); } // Reset session @@ -142,6 +159,7 @@ class OnnxModel int getNumInputNodes() const { return mInputShapes[0][1]; } std::vector> getInputShapes() const { return mInputShapes; } int getNumOutputNodes() const { return mOutputShapes[0][1]; } + std::vector> getOutputShapes() const { return mOutputShapes; } uint64_t getValidityFrom() const { return validFrom; } uint64_t getValidityUntil() const { return validUntil; } void setActiveThreads(const int); @@ -151,6 +169,7 @@ class OnnxModel std::shared_ptr mEnv = nullptr; std::shared_ptr mSession = nullptr; Ort::SessionOptions sessionOptions; + Ort::MemoryInfo mMemInfo{nullptr}; // CPU memory info, created once in initModel // Input & Output specifications of the loaded network std::vector mInputNames; @@ -158,14 +177,54 @@ class OnnxModel std::vector mOutputNames; std::vector> mOutputShapes; + // Pointers into the name vectors above, populated only after all names are loaded. + // Moving the model transfers the backing vectors without invalidating these pointers. + std::vector mInputNamesChar; + std::vector mOutputNamesChar; + // Environment settings std::string modelPath; int activeThreads = 0; uint64_t validFrom = 0; uint64_t validUntil = 0; + /// Derive the tensor shape for input number iinput from the number of provided values + /// The first dimension is treated as batch dimension; all other dimensions are taken from the model + std::vector inferInputShape(const std::size_t iinput, const int64_t size) const; + + /// Wrap a flat data vector (no copy) into an Ort::Value and append it to tensors + template + void addInputTensor(std::vector& tensors, std::vector& data, const std::size_t iinput) const + { + const std::vector inputShape = inferInputShape(iinput, static_cast(data.size())); + LOG(debug) << "Input shape calculated from vector: " << printShape(inputShape); + tensors.emplace_back(Ort::Value::CreateTensor(mMemInfo, data.data(), data.size(), inputShape.data(), inputShape.size())); + } + + /// Request only the last output, keeping its tensor alive until the caller has copied it + Ort::Value evalModelLast(std::vector& input); + void checkInput(const std::vector& input) const; + void checkOutput(const Ort::Value& tensor, std::size_t index) const; + + /// Copy the content of an output tensor into output (reusing its allocation) + template + void copyOutput(const Ort::Value& tensor, std::vector& output) const + { + const auto info = tensor.GetTensorTypeAndShapeInfo(); + if (info.GetElementType() != Ort::TypeToTensorType::type) { + LOG(fatal) << "Requested output type (ONNX type id " << static_cast(Ort::TypeToTensorType::type) << ") does not match the model output tensor type (ONNX type id " << static_cast(info.GetElementType()) << ")"; + } + const T* data = tensor.GetTensorData(); + const auto size = info.GetElementCount(); + if (size == 0) { + output.clear(); + } else { + output.assign(data, data + size); + } + } + // Internal function for printing the shape of tensors - std::string printShape(const std::vector&); + static std::string printShape(const std::vector&); bool checkHyperloop(const bool = true); }; diff --git a/Tutorials/ML/CMakeLists.txt b/Tutorials/ML/CMakeLists.txt index db28093cd7d..ad8a5477b88 100644 --- a/Tutorials/ML/CMakeLists.txt +++ b/Tutorials/ML/CMakeLists.txt @@ -19,3 +19,14 @@ o2physics_add_dpl_workflow(apply-ml-selection SOURCES applyMlSelection.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME AnalysisTutorial) + +o2physics_add_dpl_workflow(benchmark-onnx-model + SOURCES benchmarkOnnxModel.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::ML O2::GPUCommon ONNXRuntime::ONNXRuntime + TARGETVARNAME benchmarkOnnxModelTarget + COMPONENT_NAME AnalysisTutorial) + +# System libraries are linked directly: the workflow helper requires namespaced targets. +if(CMAKE_DL_LIBS) + target_link_libraries(${benchmarkOnnxModelTarget} PRIVATE ${CMAKE_DL_LIBS}) +endif() diff --git a/Tutorials/ML/README-benchmark.md b/Tutorials/ML/README-benchmark.md new file mode 100644 index 00000000000..65702fc5ede --- /dev/null +++ b/Tutorials/ML/README-benchmark.md @@ -0,0 +1,123 @@ +# ONNX inference benchmark tutorial + +`benchmarkOnnxModel.cxx` registers `o2-analysis-benchmark-onnx-model`. +It runs once with deterministic synthetic data, writes one CSV row, and exits. +It accepts one dense floating-point input of shape `[batch, features]` and one +or more outputs. Static dimensions must match the options; symbolic dimensions +are filled from the options. Output types are left to ONNX Runtime. + +## Build and GPU prerequisites + +Build O2Physics normally after loading its O2 environment. This target links +`O2::ML`, `O2::GPUCommon`, `ONNXRuntime::ONNXRuntime`, and the platform dynamic +loader. No HIP or CUDA headers are required for building the task. The O2 installation +must contain `ML/3rdparty/GPUORTFloat16.h` and its GPUCommon headers. + +For AMD GPU runs, ONNX Runtime must contain **MIGraphXExecutionProvider**, and its +MIGraphX/ROCm shared libraries, including `libamdhip64.so`, must be discoverable +at runtime. The task checks provider availability and disables CPU fallback: +unsupported graphs fail rather than producing a misleading GPU benchmark. +No changes to O2's OrtInterface are needed for this task; the current checked-out +OrtInterface does not attach an AMD execution provider, so this example uses +ORT directly. + +For NVIDIA runs, select `--provider cuda`. ONNX Runtime must contain +**CUDAExecutionProvider**, with matching CUDA/cuDNN libraries and an NVIDIA +driver. Telemetry loads `libcudart.so` (also tries `.so.13`, `.so.12`, `.so.11.0`) +from the library search path. Keep this runtime consistent with the ORT CUDA +build. CUDA provider options require ORT 1.18 or newer: `use_tf32=0` disables +TF32 so the FP32 benchmark does not silently use TF32 matrix multiplication. +Both GPU backends disable CPU fallback and use the same timing/CSV path. + +Check the actual hydra driver, ROCm, MIGraphX and ORT combination first. MI100 +(gfx908) and MI50 (gfx906) do not have identical software support. MI50 is absent +from current supported ROCm hardware lists, so it needs a compatible older +stack or a separately validated build. The old ROCm execution provider was +removed from ONNX Runtime starting with 1.23; this task uses MIGraphX. + +References: +- +- +- +- + +## Run the three comparisons + +Use equivalent FP32 and FP16 exports of the same network. Replace the example +feature count with the actual model input width. Use separate output files; +each invocation overwrites its output CSV. + +```bash +o2-analysis-benchmark-onnx-model -b --model-path model-fp32.onnx \ + --provider cpu --fp16 false --batch-size 1024 --features 5 \ + --cpu-threads 1 --warmup 20 --iterations 200 --output-csv cpu-fp32.csv + +o2-analysis-benchmark-onnx-model -b --model-path model-fp32.onnx \ + --provider migraphx --gpu-id 0 --fp16 false --batch-size 1024 --features 5 \ + --warmup 20 --iterations 200 --output-csv gpu-fp32.csv + +o2-analysis-benchmark-onnx-model -b --model-path model-fp16.onnx \ + --provider migraphx --gpu-id 0 --fp16 true --batch-size 1024 --features 5 \ + --warmup 20 --iterations 200 --output-csv gpu-fp16.csv +``` + +For NVIDIA, replace `--provider migraphx` with `--provider cuda`, for example: + +```bash +o2-analysis-benchmark-onnx-model -b --model-path model-fp32.onnx \ + --provider cuda --gpu-id 0 --fp16 false --batch-size 1024 --features 5 \ + --warmup 20 --iterations 200 --output-csv cuda-fp32.csv + +o2-analysis-benchmark-onnx-model -b --model-path model-fp16.onnx \ + --provider cuda --gpu-id 0 --fp16 true --batch-size 1024 --features 5 \ + --warmup 20 --iterations 200 --output-csv cuda-fp16.csv +``` + +`gpu-id` is the logical ID after `HIP_VISIBLE_DEVICES` / `ROCR_VISIBLE_DEVICES` +masking on AMD or `CUDA_VISIBLE_DEVICES` masking on NVIDIA. Run one benchmark process at a time on an idle +GPU. Repeat with the same batch sizes and CPU thread count, and compare several +runs. Small batches can be dominated by host overhead and transfers. + +`--fp16 true` selects O2's `o2::OrtDataType::Float16_t` input storage and checks +that the model input is FLOAT16. It does not convert model weights or prove that +every internal operation is FP16. Export/inspect the model accordingly. FP16 +models retaining FP32 input interfaces need an adapted input path; this small +tutorial intentionally rejects them. CPU runs require FP32 input. + +## What the CSV measures + +- `samples_per_second = batch_size * iterations / seconds`. +- Timing covers synchronous `Session::Run`, input/output transfers, and output + allocation/destruction. It excludes session creation, MIGraphX compilation, + warmup, input generation, FP16 conversion, telemetry, and CSV I/O. This is + host-to-host inference throughput, not GPU kernel-only throughput. +- GPU memory fields are **device-wide used bytes**, sampled through + `hipMemGetInfo` (AMD) or `cudaMemGetInfo` (NVIDIA) before session creation, after warmup, and after timed runs. + They include other processes and runtime allocations; they are neither + model-only memory nor a transient peak. The baseline follows GPU runtime/context + initialization. CPU runs leave these fields empty. GPU telemetry failures + fail the run rather than reporting zero usage. +- Precision labels describe the requested model/input variant, not measured + arithmetic precision. Strict CPU fallback rejection can reject graphs that + would otherwise run with some CPU operators. + +This benchmark does not check prediction accuracy. Compare real model outputs +between the exports separately before interpreting performance gains. + +## Adding the AO2D input later + +Planned input on hydra: + +```text +/lustre/alice/users/marin/R3A/TPCTrees/TPCTrees_LHC24as_250224/AO2D_merge_LHC24as_updatedBB_250224.root +``` + +The file has not been inspected here. Do not pass `--aod-file` to this standalone +synthetic task yet. First establish the AO2D tables, feature columns and order, +training normalization, selections, and missing-value policy. Then add an +analysis `process(...)` input with the appropriate table definitions, build +contiguous batches from its rows, and reuse the inference path. Keep session +creation in `init`, aggregate timing/sample counts across batches, and write +results at end of stream. Handle the final partial batch explicitly (dynamic +batch, or pad and count only valid samples). Keep data reading/preprocessing +outside inference timing or report a separate full-pipeline metric. diff --git a/Tutorials/ML/applyMlSelection.cxx b/Tutorials/ML/applyMlSelection.cxx index 869abd7a62c..b457dfa61ce 100644 --- a/Tutorials/ML/applyMlSelection.cxx +++ b/Tutorials/ML/applyMlSelection.cxx @@ -36,6 +36,7 @@ #include #include +#include #include using namespace o2; @@ -65,7 +66,6 @@ struct applyMlSelection { Filter filterDsFlag = (o2::aod::hf_track_index::hfflag & static_cast(BIT(aod::hf_cand_3prong::DecayType::DsToKKPi))) != static_cast(0); - HfHelper hfHelper; o2::ccdb::CcdbApi ccdbApi; int nCandidates = 0; @@ -81,7 +81,7 @@ struct applyMlSelection { void init(InitContext const&) { // Add histograms vs pT (only for selected candidates) - auto vbins = (std::vector)binsPtMl; + auto vbins = std::vector(binsPtMl); registry.add("hMassAfterSelVsPt", "Ds candidates;inv. mass (KK#pi) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{100, 1.77, 2.17}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hPromptScoreAfterSelVsPt", "Prompt score after selection;BDT first score;entries", {HistType::kTH2F, {{100, 0., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); @@ -107,22 +107,22 @@ struct applyMlSelection { continue; } // Check that the candidate rapidity is within the analysis range - if (yCandRecoMax >= 0. && std::abs(hfHelper.yDs(candidate)) > yCandRecoMax) { + if (yCandRecoMax >= 0. && std::abs(HfHelper::yDs(candidate)) > yCandRecoMax) { continue; } // Fill mass histogram before ML selections - registry.fill(HIST("hMassBeforeSel"), hfHelper.invMassDsToPiKK(candidate)); - registry.fill(HIST("hMassBeforeSel"), hfHelper.invMassDsToKKPi(candidate)); + registry.fill(HIST("hMassBeforeSel"), HfHelper::invMassDsToPiKK(candidate)); + registry.fill(HIST("hMassBeforeSel"), HfHelper::invMassDsToKKPi(candidate)); // Perform ML selections for one mass hypothesis (Ds -> PhiPi -> PiKK) std::vector inputFeaturesPiKK{candidate.cpa(), candidate.cpaXY(), candidate.decayLength(), candidate.decayLengthXY(), - static_cast(hfHelper.deltaMassPhiDsToPiKK(candidate)), + static_cast(HfHelper::deltaMassPhiDsToPiKK(candidate)), candidate.impactParameterXY(), - static_cast(hfHelper.cos3PiKDsToPiKK(candidate)), + static_cast(HfHelper::cos3PiKDsToPiKK(candidate)), candidate.maxNormalisedDeltaIP()}; // Retrieve model output and selection outcome diff --git a/Tutorials/ML/applyOnnxModel.cxx b/Tutorials/ML/applyOnnxModel.cxx index 16151511df0..560e3f204bd 100644 --- a/Tutorials/ML/applyOnnxModel.cxx +++ b/Tutorials/ML/applyOnnxModel.cxx @@ -24,6 +24,8 @@ #include #include +#include +#include #include #include @@ -51,11 +53,11 @@ struct applyModel { void run(ProcessingContext& pc) { - // Here we evaluate the model - float* modelOutput = network.evalModel(modelInput); + // Here we evaluate the model. The output tensor is copied into a std::vector owned by us + const std::vector modelOutput = network.evalModel(modelInput); // And now we print the output - for (int i = 0; i < 5; i++) { + for (std::size_t i = 0; i < std::min(modelInput.size(), modelOutput.size()); i++) { LOG(info) << "Input: " << modelInput[i] << ", Output: " << modelOutput[i]; } pc.services().get().endOfStream(); diff --git a/Tutorials/ML/benchmarkOnnxModel.cxx b/Tutorials/ML/benchmarkOnnxModel.cxx new file mode 100644 index 00000000000..67677940346 --- /dev/null +++ b/Tutorials/ML/benchmarkOnnxModel.cxx @@ -0,0 +1,266 @@ +// Copyright 2019-2020 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. + +/// Benchmark one dense ONNX input [batch, features] using CPU, AMD MIGraphX or NVIDIA CUDA. +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace +{ +// Load GPU telemetry at runtime so CPU builds need no HIP or CUDA headers. +// Memory is device-wide, not process-local; samples are not a transient peak. +class GpuMemory +{ + public: + explicit GpuMemory(int device, bool cuda) + { + const std::vector libraries = cuda + ? std::vector{"libcudart.so", "libcudart.so.13", "libcudart.so.12", "libcudart.so.11.0"} + : std::vector{"libamdhip64.so"}; + for (auto name : libraries) { + library = dlopen(name, RTLD_LAZY | RTLD_LOCAL); + if (library) { + break; + } + } + if (!library) { + throw std::runtime_error(cuda ? "GPU telemetry requires libcudart.so (CUDA 11/12/13) on LD_LIBRARY_PATH" + : "GPU telemetry requires libamdhip64.so on LD_LIBRARY_PATH"); + } + auto setDevice = reinterpret_cast(dlsym(library, cuda ? "cudaSetDevice" : "hipSetDevice")); + memoryFunction = cuda ? "cudaMemGetInfo" : "hipMemGetInfo"; + getMemory = reinterpret_cast(dlsym(library, memoryFunction)); + if (!setDevice || !getMemory || setDevice(device) != 0) { + dlclose(library); + library = nullptr; + throw std::runtime_error(cuda ? "Cannot select CUDA device or resolve CUDA memory API" + : "Cannot select HIP device or resolve HIP memory API"); + } + } + ~GpuMemory() + { + if (library) { + dlclose(library); + } + } + GpuMemory(GpuMemory const&) = delete; + GpuMemory& operator=(GpuMemory const&) = delete; + uint64_t used() const + { + size_t free = 0, total = 0; + if (getMemory(&free, &total) != 0 || free > total) { + throw std::runtime_error(std::string(memoryFunction) + " failed"); + } + return total - free; + } + + private: + const char* memoryFunction = nullptr; + void* library = nullptr; + int (*getMemory)(size_t*, size_t*) = nullptr; +}; + +std::string csvQuote(std::string const& value) +{ + std::string result = "\""; + for (char c : value) { + result += c; + if (c == '"') { + result += '"'; + } + } + return result + '"'; +} +} // namespace + +using namespace o2::framework; + +struct BenchmarkOnnxModel { + Configurable modelPath{"model-path", "model.onnx", "Local ONNX model"}; + Configurable provider{"provider", "cpu", "cpu, migraphx or cuda"}; + Configurable gpuId{"gpu-id", 0, "HIP/CUDA logical device ID (after visibility masking)"}; + Configurable fp16{"fp16", false, "Require FP16 input; does not convert model weights"}; + Configurable batchSize{"batch-size", 1024, "Samples per inference"}; + Configurable features{"features", 5, "Features per sample, in model order"}; + Configurable warmup{"warmup", 10, "Untimed warm-up calls"}; + Configurable iterations{"iterations", 100, "Timed inference calls"}; + Configurable cpuThreads{"cpu-threads", 1, "ORT intra-op CPU threads"}; + Configurable outputCsv{"output-csv", "onnx-benchmark.csv", "CSV output (overwritten)"}; + + void run(ProcessingContext& pc) + { + benchmark(); + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + } + + void benchmark() + { + if (batchSize.value <= 0 || features.value <= 0 || iterations.value <= 0 || warmup.value < 1 || cpuThreads.value <= 0 || gpuId.value < 0) { + throw std::runtime_error("Require positive batch, features, iterations, warmup, threads and nonnegative GPU ID"); + } + bool cuda = provider.value == "cuda"; + bool gpu = cuda || provider.value == "migraphx"; + if (!gpu && provider.value != "cpu") { + throw std::runtime_error("provider must be cpu, migraphx or cuda"); + } + if (!gpu && fp16.value) { + throw std::runtime_error("Use FP32 for the CPU baseline"); + } + std::unique_ptr memory; + uint64_t before = 0, afterWarmup = 0, afterRun = 0; + Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "tutorial-benchmark"); + Ort::SessionOptions options; + options.SetIntraOpNumThreads(cpuThreads.value); + options.SetInterOpNumThreads(1); + options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL); + if (gpu) { + const std::string providerName = cuda ? "CUDAExecutionProvider" : "MIGraphXExecutionProvider"; + auto providers = Ort::GetAvailableProviders(); + if (std::find(providers.begin(), providers.end(), providerName) == providers.end()) { + throw std::runtime_error("This ONNX Runtime has no " + providerName); + } + memory = std::make_unique(gpuId.value, cuda); + before = memory->used(); + if (cuda) { + // V2 options allow disabling TF32 for the FP32 comparison. + OrtCUDAProviderOptionsV2* raw = nullptr; + Ort::ThrowOnError(Ort::GetApi().CreateCUDAProviderOptions(&raw)); + std::unique_ptr nvidia(raw, Ort::GetApi().ReleaseCUDAProviderOptions); + const std::string device = std::to_string(gpuId.value); + const char* keys[] = {"device_id", "use_tf32"}; + const char* values[] = {device.c_str(), "0"}; + Ort::ThrowOnError(Ort::GetApi().UpdateCUDAProviderOptions(nvidia.get(), keys, values, 2)); + options.AppendExecutionProvider_CUDA_V2(*nvidia); + } else { + OrtMIGraphXProviderOptions amd{}; + amd.device_id = gpuId.value; + amd.migraphx_mem_limit = std::numeric_limits::max(); + // Keep the supplied model precision; do not automatically quantize FP32. + amd.migraphx_fp16_enable = 0; + options.AppendExecutionProvider_MIGraphX(amd); + } + options.AddConfigEntry("session.disable_cpu_ep_fallback", "1"); + } + Ort::Session session(env, modelPath.value.c_str(), options); + if (session.GetInputCount() != 1 || session.GetOutputCount() < 1) { + throw std::runtime_error("Tutorial expects exactly one dense input and at least one output"); + } + auto inputInfo = session.GetInputTypeInfo(0); + auto tensorInfo = inputInfo.GetTensorTypeAndShapeInfo(); + auto shape = tensorInfo.GetShape(); + auto type = fp16.value ? ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT16 : ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT; + if (tensorInfo.GetElementType() != type || shape.size() != 2 || + (shape[0] >= 0 && shape[0] != batchSize.value) || (shape[1] >= 0 && shape[1] != features.value)) { + throw std::runtime_error("Input must match fp16 flag and [batch-size, features]; only dynamic dimensions may be overridden"); + } + shape = {batchSize.value, features.value}; + const size_t count = static_cast(batchSize.value) * features.value; + if (count > std::numeric_limits::max() / sizeof(float)) { + throw std::runtime_error("Input size overflow"); + } + // Replace this feature filling with AO2D extraction and training preprocessing later. + std::vector floats(fp16.value ? 0 : count); + std::vector halves(fp16.value ? count : 0); + static_assert(sizeof(o2::OrtDataType::Float16_t) == 2); + for (size_t i = 0; i < count; ++i) { + float value = static_cast(i % 101) / 100.f - 0.5f; + if (fp16.value) { + halves[i] = o2::OrtDataType::Float16_t(value); + } else { + floats[i] = value; + } + } + auto host = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault); + void* data = fp16.value ? static_cast(halves.data()) : static_cast(floats.data()); + size_t bytes = count * (fp16.value ? sizeof(halves[0]) : sizeof(floats[0])); + auto input = Ort::Value::CreateTensor(host, data, bytes, shape.data(), shape.size(), type); + Ort::AllocatorWithDefaultOptions allocator; + auto inputName = session.GetInputNameAllocated(0, allocator); + const char* inputNames[] = {inputName.get()}; + std::vector names; + names.reserve(session.GetOutputCount()); + for (size_t i = 0; i < session.GetOutputCount(); ++i) { + names.emplace_back(session.GetOutputNameAllocated(i, allocator).get()); + } + std::vector outputNames; + outputNames.reserve(names.size()); + for (auto const& name : names) { + outputNames.push_back(name.c_str()); + } + Ort::RunOptions runOptions; + auto infer = [&]() { + // Default synchronous Run with CPU input/output includes host/device transfers + // and output allocation/destruction. No asynchronous Run option is enabled. + auto outputs = session.Run(runOptions, inputNames, &input, 1, outputNames.data(), outputNames.size()); + }; + for (int i = 0; i < warmup.value; ++i) { + infer(); + } + if (memory) { + afterWarmup = memory->used(); + } + auto start = std::chrono::steady_clock::now(); + for (int i = 0; i < iterations.value; ++i) { + infer(); + } + double seconds = std::chrono::duration(std::chrono::steady_clock::now() - start).count(); + if (memory) { + afterRun = memory->used(); + } + std::ofstream csv(outputCsv.value, std::ios::trunc); + if (!csv) { + throw std::runtime_error("Cannot open output-csv"); + } + csv << "model,ort_version,provider,precision,gpu_id,batch_size,features,warmup,iterations,cpu_threads,seconds,samples_per_second,gpu_used_before_bytes,gpu_used_after_warmup_bytes,gpu_used_after_run_bytes\n"; + csv << csvQuote(modelPath.value) << ',' << csvQuote(Ort::GetVersionString()) << ',' << provider.value << ',' + << (fp16.value ? "fp16" : "fp32") << ',' << (gpu ? gpuId.value : -1) << ',' + << batchSize.value << ',' << features.value << ',' << warmup.value << ',' << iterations.value << ',' << cpuThreads.value << ',' + << std::setprecision(12) << seconds << ',' << (static_cast(batchSize.value) * iterations.value / seconds) << ','; + if (memory) { + csv << before << ',' << afterWarmup << ',' << afterRun; + } else { + csv << ",,"; + } + csv << '\n'; + csv.close(); + if (!csv) { + throw std::runtime_error("Failed writing output-csv"); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/Tutorials/OpenData/flowAnalysis.cxx b/Tutorials/OpenData/flowAnalysis.cxx index 6ab74ebfced..6f71ff609c5 100644 --- a/Tutorials/OpenData/flowAnalysis.cxx +++ b/Tutorials/OpenData/flowAnalysis.cxx @@ -87,8 +87,8 @@ struct flow_base { HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - Filter collisionFilter = (aod::collision::flags & (uint16_t)aod::collision::CollisionFlagsRun2::Run2VertexerTracks) == (uint16_t)aod::collision::CollisionFlagsRun2::Run2VertexerTracks; - Filter trackFilter = ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t) true)); + Filter collisionFilter = (aod::collision::flags & static_cast(aod::collision::CollisionFlagsRun2::Run2VertexerTracks)) == static_cast(aod::collision::CollisionFlagsRun2::Run2VertexerTracks); + Filter trackFilter = ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == static_cast(true))); void fillAPt(double trackpt, double cent, double vn, double sinHarm, double cosHarm) { @@ -286,7 +286,7 @@ struct flow_base { ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); - long now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + int64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); ccdb->setCreatedNotAfter(now); // TODO must become global parameter from the train creation time } diff --git a/Tutorials/PWGCF/EventPlane/src/spectatorPlaneTutorial.cxx b/Tutorials/PWGCF/EventPlane/src/spectatorPlaneTutorial.cxx index d99a16fb996..8e2ef19f665 100644 --- a/Tutorials/PWGCF/EventPlane/src/spectatorPlaneTutorial.cxx +++ b/Tutorials/PWGCF/EventPlane/src/spectatorPlaneTutorial.cxx @@ -77,7 +77,7 @@ struct SpectatorPlaneTutorial { float dcaZMax = 2.0; Filter collisionFilter = nabs(aod::collision::posZ) < vtxZ; - Filter trackFilter = nabs(aod::track::eta) < etaMax && aod::track::pt > ptMin&& aod::track::pt < ptMax && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t) true)) && nabs(aod::track::dcaXY) < dcaXYMax&& nabs(aod::track::dcaZ) < dcaZMax; + Filter trackFilter = nabs(aod::track::eta) < etaMax && aod::track::pt > ptMin&& aod::track::pt < ptMax && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == static_cast(true))) && nabs(aod::track::dcaXY) < dcaXYMax&& nabs(aod::track::dcaZ) < dcaZMax; using GeneralCollisions = soa::Join; using UnfilteredTracks = soa::Join; @@ -182,9 +182,9 @@ struct SpectatorPlaneTutorial { // Get QQ-correlations from CCDB if (cfgCCDBdir_QQ.value.empty() == false) { TList* list = ccdb->getForTimeStamp(cfgCCDBdir_QQ.value, bc.timestamp()); - TProfile* qAqCX = reinterpret_cast(list->FindObject("qAqCX")); - TProfile* qAqCY = reinterpret_cast(list->FindObject("qAqCY")); - TProfile* qAqCXY = reinterpret_cast(list->FindObject("qAqCXY")); + TProfile* qAqCX = dynamic_cast(list->FindObject("qAqCX")); + TProfile* qAqCY = dynamic_cast(list->FindObject("qAqCY")); + TProfile* qAqCXY = dynamic_cast(list->FindObject("qAqCXY")); // The sum is qAqCXY corrQQx = qAqCX->GetBinContent(centrality); corrQQy = qAqCY->GetBinContent(centrality); diff --git a/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask3.cxx b/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask3.cxx index 112b4d053b5..f31e4ad46d0 100644 --- a/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask3.cxx +++ b/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask3.cxx @@ -69,8 +69,8 @@ struct CFTutorialTask3 { // Partitions for particle 1 and particle 2 // add PID selection to partition definition - Partition PartsOne = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartOne) == ConfCutPartOne); - Partition PartsTwo = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartTwo) == ConfCutPartTwo); + Partition PartsOne = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartOne) == ConfCutPartOne); + Partition PartsTwo = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartTwo) == ConfCutPartTwo); HistogramRegistry HistRegistry{"FemtoTutorial", {}, OutputObjHandlingPolicy::AnalysisObject}; diff --git a/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask4.cxx b/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask4.cxx index fdfcf410147..28db4793c05 100644 --- a/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask4.cxx +++ b/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask4.cxx @@ -69,8 +69,8 @@ struct CFTutorialTask4 { Configurable ConfPIDThresholdPartTwo{"ConfPIDThresholdPartTwo", 0.75, "Particle 2 - Momentum threshold for TPC to TPCTOF PID"}; /// Partitions for particle 1 and particle 2 - Partition PartsOne = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartOne) == ConfCutPartOne); - Partition PartsTwo = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartTwo) == ConfCutPartTwo); + Partition PartsOne = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartOne) == ConfCutPartOne); + Partition PartsTwo = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartTwo) == ConfCutPartTwo); HistogramRegistry HistRegistry{"FemtoTutorial", {}, OutputObjHandlingPolicy::AnalysisObject}; diff --git a/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask5.cxx b/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask5.cxx index be71808ecf5..24f5edeaf85 100644 --- a/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask5.cxx +++ b/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask5.cxx @@ -73,9 +73,9 @@ struct CFTutorialTask5 { Configurable ConfPIDThresholdPartTwo{"ConfPIDThresholdPartTwo", 0.75, "Particle 2 - Momentum threshold for TPC to TPCTOF PID"}; /// Partitions for particle 1 and particle 2 - Partition PartsOne = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartOne) == ConfCutPartOne) && ifnode(aod::femtodreamparticle::pt < ConfPIDThresholdPartOne, ncheckbit(aod::femtodreamparticle::pidcut, ConfPIDTPCPartOne), ncheckbit(aod::femtodreamparticle::pidcut, ConfPIDTPCTOFPartOne)); + Partition PartsOne = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartOne) == ConfCutPartOne) && ifnode(aod::femtodreamparticle::pt < ConfPIDThresholdPartOne, ncheckbit(aod::femtodreamparticle::pidcut, ConfPIDTPCPartOne), ncheckbit(aod::femtodreamparticle::pidcut, ConfPIDTPCTOFPartOne)); - Partition PartsTwo = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartTwo) == ConfCutPartTwo) && ifnode(aod::femtodreamparticle::pt < ConfPIDThresholdPartTwo, ncheckbit(aod::femtodreamparticle::pidcut, ConfPIDTPCPartTwo), ncheckbit(aod::femtodreamparticle::pidcut, ConfPIDTPCTOFPartTwo)); + Partition PartsTwo = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartTwo) == ConfCutPartTwo) && ifnode(aod::femtodreamparticle::pt < ConfPIDThresholdPartTwo, ncheckbit(aod::femtodreamparticle::pidcut, ConfPIDTPCPartTwo), ncheckbit(aod::femtodreamparticle::pidcut, ConfPIDTPCTOFPartTwo)); HistogramRegistry HistRegistry{"FemtoTutorial", {}, OutputObjHandlingPolicy::AnalysisObject}; diff --git a/Tutorials/PWGCF/FlowGenericFramework/src/flowGFWTutorial.cxx b/Tutorials/PWGCF/FlowGenericFramework/src/flowGFWTutorial.cxx index 02d8c89d063..49dfc3e4a81 100644 --- a/Tutorials/PWGCF/FlowGenericFramework/src/flowGFWTutorial.cxx +++ b/Tutorials/PWGCF/FlowGenericFramework/src/flowGFWTutorial.cxx @@ -64,7 +64,7 @@ struct GfwTutorial { ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90}, "centrality axis for histograms"}; Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; - Filter trackFilter = (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPtMin) && (aod::track::pt < cfgCutPtMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (aod::track::tpcChi2NCl < cfgCutChi2prTPCcls); + Filter trackFilter = (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPtMin) && (aod::track::pt < cfgCutPtMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == static_cast(true))) && (aod::track::tpcChi2NCl < cfgCutChi2prTPCcls); // Connect to ccdb Service ccdb; diff --git a/Tutorials/PWGEM/Cocktail/plotLFCocktail.C b/Tutorials/PWGEM/Cocktail/plotLFCocktail.C index 31974fe47d0..dcb56b9dfa1 100644 --- a/Tutorials/PWGEM/Cocktail/plotLFCocktail.C +++ b/Tutorials/PWGEM/Cocktail/plotLFCocktail.C @@ -34,8 +34,8 @@ void loadHistos(TFile* file, TH1F* hists[], const TString& name_extra, int rebin int nEvents) { for (int i = 0; i < nHists; i++) { - hists[i] = (TH1F*)file->GetDirectory("em-lmee-lf-cocktail") - ->Get(TString("mee") + name_extra + histNames[i]); + hists[i] = dynamic_cast(file->GetDirectory("em-lmee-lf-cocktail") + ->Get(TString("mee") + name_extra + histNames[i])); hists[i]->Rebin(rebin); hists[i]->Scale(1. / nEvents); hists[i]->SetTitle(histLegends[i]); @@ -48,7 +48,7 @@ void plotLFCocktail(const TString& filename = "AnalysisResults.root", int rebin TFile* file = TFile::Open(filename.Data()); TH1I* hNEvents = - (TH1I*)file->GetDirectory("em-lmee-lf-cocktail")->Get("NEvents"); + dynamic_cast(file->GetDirectory("em-lmee-lf-cocktail")->Get("NEvents")); int nEvents = hNEvents->GetBinContent(1); loadHistos(file, mee, "", rebin, nEvents); diff --git a/Tutorials/PWGLF/Resonance/resonancesCombine.cxx b/Tutorials/PWGLF/Resonance/resonancesCombine.cxx index 29b34cd6e44..6d45fc3dd7a 100644 --- a/Tutorials/PWGLF/Resonance/resonancesCombine.cxx +++ b/Tutorials/PWGLF/Resonance/resonancesCombine.cxx @@ -66,8 +66,8 @@ struct ResonanceCombine { double massKa = o2::constants::physics::MassKPlus; EventPlaneHelper helperEP; - int evtPlRefAId = static_cast(cfgEvtPl / 10000); - int evtPlRefBId = static_cast((cfgEvtPl - evtPlRefAId * 10000) / 100); + int evtPlRefAId = (cfgEvtPl / 10000); + int evtPlRefBId = ((cfgEvtPl - evtPlRefAId * 10000) / 100); int evtPlDetId = cfgEvtPl - evtPlRefAId * 10000 - evtPlRefBId * 100; void init(o2::framework::InitContext&) diff --git a/Tutorials/PWGLF/Resonance/resonances_step2.cxx b/Tutorials/PWGLF/Resonance/resonances_step2.cxx index 3d83bc39bb6..a0c43dd03ba 100644 --- a/Tutorials/PWGLF/Resonance/resonances_step2.cxx +++ b/Tutorials/PWGLF/Resonance/resonances_step2.cxx @@ -202,7 +202,7 @@ struct resonances_tutorial { void processME(o2::aod::ResoCollisions& collisions, aod::ResoTracks const& resotracks) { auto tracksTuple = std::make_tuple(resotracks); - BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}}; SameKindPair pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto& [collision1, tracks1, collision2, tracks2] : pairs) { // loop over all pairs diff --git a/Tutorials/PWGUD/UDTutorial_02b.cxx b/Tutorials/PWGUD/UDTutorial_02b.cxx index 1d060b44f27..f80ccb1ccd1 100644 --- a/Tutorials/PWGUD/UDTutorial_02b.cxx +++ b/Tutorials/PWGUD/UDTutorial_02b.cxx @@ -62,7 +62,7 @@ struct UDTutorial02b { void init(InitContext&) { - anaPars = (DGAnaparHolder)DGPars; + anaPars = DGAnaparHolder(DGPars); pidsel.init(anaPars); if (verbose) { LOGF(info, ""); diff --git a/Tutorials/src/ConfigurableCutLinkDef.h b/Tutorials/src/ConfigurableCutLinkDef.h index f2667e6b6cc..397ee540c36 100644 --- a/Tutorials/src/ConfigurableCutLinkDef.h +++ b/Tutorials/src/ConfigurableCutLinkDef.h @@ -9,6 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +// NOLINT(build/header_guard) + #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; diff --git a/Tutorials/src/associatedExample.cxx b/Tutorials/src/associatedExample.cxx index c8c87debff1..1aba1e01905 100644 --- a/Tutorials/src/associatedExample.cxx +++ b/Tutorials/src/associatedExample.cxx @@ -150,7 +150,7 @@ struct PartitionColExtra { struct BuildHmpidIndex { // build the index table HMPIDTracksIndex Builds idx; - void init(InitContext const&) {}; + void init(InitContext const&) {} }; struct ConsumeHmpidIndex { diff --git a/Tutorials/src/ccdbaccess.cxx b/Tutorials/src/ccdbaccess.cxx index e1ea568f46f..0a987fae790 100644 --- a/Tutorials/src/ccdbaccess.cxx +++ b/Tutorials/src/ccdbaccess.cxx @@ -29,6 +29,7 @@ #include #include +#include #include using namespace o2::framework; @@ -39,7 +40,7 @@ struct TimestampUserTask { Service ccdb; Configurable path{"ccdb-path", "qc/TOF/TOFTaskCompressed/hDiagnostic", "path to the ccdb object"}; Configurable url{"ccdb-url", "http://ccdb-test.cern.ch:8080", "url of the ccdb repository"}; - Configurable nolaterthan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + Configurable nolaterthan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; void init(o2::framework::InitContext&) { diff --git a/Tutorials/src/ccdbtableaccess.cxx b/Tutorials/src/ccdbtableaccess.cxx index 5c703e4d1dd..3cd86168e8f 100644 --- a/Tutorials/src/ccdbtableaccess.cxx +++ b/Tutorials/src/ccdbtableaccess.cxx @@ -21,6 +21,9 @@ #include #include +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) + using namespace o2::framework; using namespace o2::header; using namespace o2; diff --git a/Tutorials/src/configurableObjects.cxx b/Tutorials/src/configurableObjects.cxx index e1a758b1851..ad02482613d 100644 --- a/Tutorials/src/configurableObjects.cxx +++ b/Tutorials/src/configurableObjects.cxx @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -63,10 +64,8 @@ auto printMatrix(Array2D const& m) return ss.str(); } -static constexpr float defaultm[3][4] = {{1.1, 1.2, 1.3, 1.4}, {2.1, 2.2, 2.3, 2.4}, {3.1, 3.2, 3.3, 3.4}}; -static LabeledArray la{&defaultm[0][0], 3, 4, {"r 1", "r 2", "r 3"}, {"c 1", "c 2", "c 3", "c 4"}}; -const std::string defaultmS[3][4] = {{"One.One", "One.Two", "One.Three", "One.Four"}, {"Two.One", "Two.Two", "Two.Three", "Two.Four"}, {"Three.One", "Three.Two", "Three.Three", "Three.Four"}}; -static LabeledArray laS{&defaultmS[0][0], 3, 4, {"rS 1", "rS 2", "rS 3"}, {"cS 1", "cS 2", "cS 3", "cS 4"}}; +static constexpr std::array, 3> defaultm{{{1.1, 1.2, 1.3, 1.4}, {2.1, 2.2, 2.3, 2.4}, {3.1, 3.2, 3.3, 3.4}}}; +static constexpr std::array, 3> defaultmS{{{"One.One", "One.Two", "One.Three", "One.Four"}, {"Two.One", "Two.Two", "Two.Three", "Two.Four"}, {"Three.One", "Three.Two", "Three.Three", "Three.Four"}}}; struct ConfigurableObjectDemo { // Simple type configurables @@ -82,9 +81,9 @@ struct ConfigurableObjectDemo { Configurable> array{"array", {0, 0, 0, 0, 0, 0, 0}, "generic int array"}; Configurable> farray{"farray", {0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1}, "generic float array"}; Configurable> darray{"darray", {0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1}, "generic double array"}; - Configurable> vmatrix{"matrix", {&defaultm[0][0], 3, 4}, "generic matrix"}; - Configurable> vla{"vla", {defaultm[0], 3, 4, {"r 1", "r 2", "r 3"}, {"c 1", "c 2", "c 3", "c 4"}}, "labeled array with float content"}; - Configurable> vlaS{"vlaS", {defaultmS[0], 3, 4, {"rS 1", "rS 2", "rS 3"}, {"cS 1", "cS 2", "cS 3", "cS 4"}}, "labeled array with string content"}; + Configurable> vmatrix{"matrix", {defaultm.front().data(), 3, 4}, "generic matrix"}; + Configurable> vla{"vla", {defaultm.front().data(), 3, 4, {"r 1", "r 2", "r 3"}, {"c 1", "c 2", "c 3", "c 4"}}, "labeled array with float content"}; + Configurable> vlaS{"vlaS", {defaultmS.front().data(), 3, 4, {"rS 1", "rS 2", "rS 3"}, {"cS 1", "cS 2", "cS 3", "cS 4"}}, "labeled array with string content"}; // Configurables can be grouped into `ConfigurableGroup`s. // Their names must be unique. @@ -99,11 +98,11 @@ struct ConfigurableObjectDemo { LOGF(info, "max_eta: %f; min_clusters: %d", (float)trackcuts.max_eta, (int)trackcuts.min_clusters); LOGF(info, "Cut1 bins: %s; Cut2 bins: %s", printArray(cut->getBins()), printArray(mutable_cut->getBins())); LOGF(info, "Cut1 labels: %s; Cut2 labels: %s", printArray(cut->getLabels()), printArray(mutable_cut->getLabels())); - auto vec = (std::vector)array; + auto vec = std::vector(array); LOGF(info, "Array: %s", printArray(vec).c_str()); - auto dvec = (std::vector)darray; + auto dvec = std::vector(darray); LOGF(info, "Double array: %s", printArray(dvec).c_str()); - auto fvec = (std::vector)farray; + auto fvec = std::vector(farray); LOGF(info, "Float array: %s", printArray(fvec).c_str()); LOGF(info, "Matrix: %s", printMatrix((Array2D)vmatrix)); LOGF(info, "Labeled float array:\n %s\n %s\n %s", printArray(vla->getLabelsRows()), printArray(vla->getLabelsCols()), printMatrix(vla->getData())); diff --git a/Tutorials/src/efficiencyGlobal.cxx b/Tutorials/src/efficiencyGlobal.cxx index 13e1fdb9df3..a697162622b 100644 --- a/Tutorials/src/efficiencyGlobal.cxx +++ b/Tutorials/src/efficiencyGlobal.cxx @@ -28,6 +28,7 @@ #include #include +#include #include using namespace o2::framework; @@ -37,7 +38,7 @@ struct EfficiencyGlobal { Service ccdb; Configurable path{"ccdb-path", "Users/j/jgrosseo/tutorial/efficiency/simple", "base path to the ccdb object"}; Configurable url{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable nolaterthan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + Configurable nolaterthan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; OutputObj pt{TH1F("pt", "pt", 20, 0., 10.)}; diff --git a/Tutorials/src/efficiencyPerRun.cxx b/Tutorials/src/efficiencyPerRun.cxx index 7cbebd815ab..944b7fbb105 100644 --- a/Tutorials/src/efficiencyPerRun.cxx +++ b/Tutorials/src/efficiencyPerRun.cxx @@ -31,6 +31,7 @@ #include #include +#include #include using namespace o2::framework; @@ -40,7 +41,7 @@ struct EfficiencyPerRun { Service ccdb; Configurable path{"ccdb-path", "Users/j/jgrosseo/tutorial/efficiency/simple", "base path to the ccdb object"}; Configurable url{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable nolaterthan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + Configurable nolaterthan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; OutputObj pt{TH1F("pt", "pt", 20, 0., 10.)}; diff --git a/Tutorials/src/eventMixing.cxx b/Tutorials/src/eventMixing.cxx index 925cc9f03a7..379532105ab 100644 --- a/Tutorials/src/eventMixing.cxx +++ b/Tutorials/src/eventMixing.cxx @@ -51,7 +51,7 @@ struct MixedEvents { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins}}; SameKindPair pair{binningOnPositions, 5, -1, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored void process(aod::Collisions const& collisions, aod::Tracks const& tracks) { @@ -86,7 +86,7 @@ struct MixedEventsInsideProcess { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins}}; void process(aod::Collisions const& collisions, aod::Tracks const& tracks) @@ -124,7 +124,7 @@ struct MixedEventsFilteredTracks { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins}}; SameKindPair pair{binningOnPositions, 5, -1, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored void process(aod::Collisions const& collisions, MyTracks const& tracks) @@ -157,7 +157,7 @@ struct MixedEventsJoinedCollisions { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins}}; SameKindPair pair{binningOnPositions, 5, -1, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored void process(AODCollisions const& collisions, aod::Tracks const& tracks, aod::BCsWithTimestamps const&) @@ -190,7 +190,7 @@ struct MixedEventsDynamicColumns { std::vector zBins{7, -7, 7}; std::vector multBins{VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 100.1}; using BinningType = ColumnBinningPolicy>; - BinningType corrBinning{{zBins, multBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType corrBinning{{zBins, multBins}}; SameKindPair pair{corrBinning, 5, -1, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored void process(AODCollisions const& collisions, aod::Tracks const& tracks) @@ -223,7 +223,7 @@ struct MixedEventsVariousKinds { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins}}; Pair pair{binningOnPositions, 5, -1, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored void process(aod::Collisions const& collisions, aod::Tracks const& tracks, aod::V0s const& v0s) @@ -258,7 +258,7 @@ struct MixedEventsTriple { std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; std::vector zBins{7, -7, 7}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins, zBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins, zBins}}; SameKindTriple triple{binningOnPositions, 5, -1, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored void process(aod::Collisions const& collisions, aod::Tracks const& tracks) @@ -292,7 +292,7 @@ struct MixedEventsTripleVariousKinds { std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; std::vector zBins{7, -7, 7}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins, zBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins, zBins}}; Triple triple{binningOnPositions, 5, -1, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored void process(aod::Collisions const& collisions, aod::Tracks const& tracks, aod::V0s const& v0s) @@ -401,7 +401,7 @@ struct MixedEventsPartitionedTracks { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins}}; SameKindPair pair{binningOnPositions, 5, -1, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored void process(aod::Collisions const& collisions, MyTracks const& tracks) @@ -453,7 +453,7 @@ struct MixedEventsLambdaBinning { }; using BinningType = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - BinningType binningWithLambda{{getTracksSize}, {axisVertex, axisMultiplicity}, true}; + BinningType binningWithLambda{{getTracksSize}, {axisVertex, axisMultiplicity}}; auto tracksTuple = std::make_tuple(tracks); SameKindPair pair{binningWithLambda, 5, -1, collisions, tracksTuple, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored @@ -490,8 +490,8 @@ struct MixedEventsCounters { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins}, true}; - // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins}}; + SameKindPair pair{binningOnPositions, 5, -1, &cache}; // indicates that 5 events should be mixed and under / overflow(-1) to be ignored void process(aod::Collisions const& collisions, aod::Tracks const& tracks) diff --git a/Tutorials/src/eventMixingValidation.cxx b/Tutorials/src/eventMixingValidation.cxx index f7cf702f5d0..3b026e2c48e 100644 --- a/Tutorials/src/eventMixingValidation.cxx +++ b/Tutorials/src/eventMixingValidation.cxx @@ -40,7 +40,7 @@ struct MixedEventsEmptyTables { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins}}; SameKindPair pair{binningOnPositions, 5, -1, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored void process(aod::Collisions const& collisions, MyTracks const& tracks) @@ -72,7 +72,7 @@ struct MixedEventsJoinedTracks { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins}}; SameKindPair pair{binningOnPositions, 5, -1, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored void process(aod::Collisions const& collisions, aod::FullTracks const& tracks) @@ -104,7 +104,7 @@ struct MixedEventsJoinedTracks { // std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; // std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; // using BinningType = ColumnBinningPolicy; -// BinningType binningOnPositions{{xBins, yBins}, true}; // true is for 'ignore overflows' (true by default) +// BinningType binningOnPositions{{xBins, yBins}}; // SameKindPair pair{binningOnPositions, 5, -1}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored // // void process(aod::Collisions const& collisions, aod::Tracks const& tracks) diff --git a/Tutorials/src/extendedTables.cxx b/Tutorials/src/extendedTables.cxx index 96bf680ff28..574d9edb2d0 100644 --- a/Tutorials/src/extendedTables.cxx +++ b/Tutorials/src/extendedTables.cxx @@ -121,7 +121,7 @@ struct SpawnDynamicColumns { void init(InitContext&) { - cextable.projectors[0] = (float)factor * aod::track::p * aod::track::p; + cextable.projectors[0] = factor.value * aod::track::p * aod::track::p; } void process(aod::Tracks const& tracks) diff --git a/Tutorials/src/preprocess.cxx b/Tutorials/src/preprocess.cxx index 8a3bc43be05..14513311028 100644 --- a/Tutorials/src/preprocess.cxx +++ b/Tutorials/src/preprocess.cxx @@ -28,6 +28,7 @@ #include #include +#include using namespace o2; using namespace o2::framework; diff --git a/Tutorials/src/reweighting.cxx b/Tutorials/src/reweighting.cxx index 48e90a9560d..4319a587858 100644 --- a/Tutorials/src/reweighting.cxx +++ b/Tutorials/src/reweighting.cxx @@ -51,6 +51,7 @@ #include #include #include +#include #include using namespace o2; @@ -106,7 +107,7 @@ struct CreateWeights { void init(InitContext&) { - auto path = (std::string)onnxModel; + auto path = std::string(onnxModel); /// create session onnxSession = std::make_shared(env, path.c_str(), sessionOptions); /// adjust input shape to use row-by-row model application diff --git a/Tutorials/src/tracksCombinations.cxx b/Tutorials/src/tracksCombinations.cxx index a4ecbf1047e..f3a22c03d73 100644 --- a/Tutorials/src/tracksCombinations.cxx +++ b/Tutorials/src/tracksCombinations.cxx @@ -60,7 +60,7 @@ struct TrackCombinations { struct BinnedTrackCombinations { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; - ColumnBinningPolicy trackBinning{{xBins, yBins}, true}; + ColumnBinningPolicy trackBinning{{xBins, yBins}}; void process(aod::Tracks const& tracks) { @@ -83,7 +83,7 @@ struct ConfigurableBinnedCollisionCombinations { void process(soa::Join const& collisions) { using BinningType = ColumnBinningPolicy>; - BinningType colBinning{{axisVertex, axisMultiplicity}, true}; + BinningType colBinning{{axisVertex, axisMultiplicity}}; int count = 0; // Strictly upper tracks binned by x and y position @@ -100,7 +100,7 @@ struct ConfigurableBinnedCollisionCombinations { struct BinnedTrackPartitionsCombinations { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; - ColumnBinningPolicy trackBinning{{xBins, yBins}, true}; + ColumnBinningPolicy trackBinning{{xBins, yBins}}; Configurable philow{"phiLow", 1.0f, "lowest phi"}; void process(aod::Tracks const& tracks) diff --git a/dependencies/O2PhysicsCompileFlags.cmake b/dependencies/O2PhysicsCompileFlags.cmake index 3aff4b73547..a68cf793aa3 100644 --- a/dependencies/O2PhysicsCompileFlags.cmake +++ b/dependencies/O2PhysicsCompileFlags.cmake @@ -14,7 +14,7 @@ include_guard() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") # Enabled warnings supported by Clang and GCC, not treated as errors -set(O2PHYSICS_WARNINGS_COMMON_NO_ERROR "") +set(O2PHYSICS_WARNINGS_COMMON_NO_ERROR "deprecated-declarations") # Enabled warnings supported by Clang only, not treated as errors set(O2PHYSICS_WARNINGS_CLANG_NO_ERROR "vla-extension") @@ -80,12 +80,38 @@ IF (NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE RELWITHDEBINFO) ENDIF (NOT CMAKE_BUILD_TYPE) +# Adjust the per-config flags rather than replacing them. These used to be plain +# set() calls, which create normal variables that shadow the cache for the rest of +# the configure -- so whatever the build recipe passed as +# -DCMAKE_CXX_FLAGS_RELWITHDEBINFO=... was silently discarded. alidist asks for +# "-O2 -g1 -DNDEBUG" (overridable via O2PHYSICS_DEBUG_FLAGS) and got -g anyway; +# full debug info costs ~18% of total compile time across the tree, because the +# register allocator's rewrite pass scales with the number of debug records. +# Here we only enforce what this project actually cares about: -pipe, and whether +# NDEBUG is defined. The optimisation and debug-info levels stay as given. +function(o2physics_adjust_config_flags varname want_ndebug) + set(flags "${${varname}}") + if(want_ndebug) + if(NOT flags MATCHES "(^| )-DNDEBUG($| )") + string(APPEND flags " -DNDEBUG") + endif() + else() + # ENABLE_CASSERT: the CI wants assertions live. + string(REGEX REPLACE "(^| )-DNDEBUG($| )" " " flags "${flags}") + endif() + if(NOT flags MATCHES "(^| )-pipe($| )") + string(APPEND flags " -pipe") + endif() + string(STRIP "${flags}" flags) + set(${varname} "${flags}" PARENT_SCOPE) +endfunction() + IF(ENABLE_CASSERT) #For the CI, we want to have assertions enabled - set(CMAKE_CXX_FLAGS_RELEASE "-O2 -pipe") - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g -pipe") + o2physics_adjust_config_flags(CMAKE_CXX_FLAGS_RELEASE FALSE) + o2physics_adjust_config_flags(CMAKE_CXX_FLAGS_RELWITHDEBINFO FALSE) ELSE() - set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG -pipe") - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g -DNDEBUG -pipe") + o2physics_adjust_config_flags(CMAKE_CXX_FLAGS_RELEASE TRUE) + o2physics_adjust_config_flags(CMAKE_CXX_FLAGS_RELWITHDEBINFO TRUE) if (CMAKE_BUILD_TYPE STREQUAL "RELEASE" OR CMAKE_BUILD_TYPE STREQUAL "RELWITHDEBINFO") set(FAIR_MIN_SEVERITY "info") endif()